Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/app/app/assets/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,7 @@ details {
summary {
@apply cursor-pointer;
}

code > pre {
@apply rounded-lg overflow-x-scroll bg-transparent! text-sm;
}
54 changes: 45 additions & 9 deletions packages/app/app/components/Commits.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
<script lang="ts" setup>
import type { RendererObject } from "marked";
import bash from "@shikijs/langs/bash";
import githubDark from "@shikijs/themes/github-dark";
import githubLight from "@shikijs/themes/github-light";
import { marked } from "marked";
import { createHighlighterCoreSync, type HighlighterCore } from "shiki/core";
import { createJavaScriptRegexEngine } from "shiki/engine/javascript";

const props = defineProps<{
owner: string;
Expand Down Expand Up @@ -42,16 +47,47 @@ const selectedCommit = shallowRef<
// Markdown

// Add target to links
const renderer: RendererObject = {
link(originalLink) {
const link = marked.Renderer.prototype.link.call(this, originalLink);
return link.replace("<a", "<a target='_blank' rel='noreferrer' ");
},
};
marked.use({ renderer });

// Pagination
const colorMode = useColorMode();
let shiki: HighlighterCore;

onBeforeMount(async () => {
// if (typeof window === 'undefined') {
// const { loadWasm } = await import('shiki')
// // @ts-expect-error ignore error
// await loadWasm(import(/* @vite-ignore */ 'shiki/onig.wasm'))
// }

shiki = createHighlighterCoreSync({
themes: [githubDark, githubLight],
langs: [bash],
engine: createJavaScriptRegexEngine(),
});

const renderer: RendererObject = {
link(originalLink) {
const link = marked.Renderer.prototype.link.call(this, originalLink);
return link.replace(
"<a",
"<a target='_blank' rel='noreferrer' class='text-primary underline'",
);
},
code({ text }) {
return `<code class="language-bash">${shiki.codeToHtml(text, {
theme: colorMode.preference === "dark" ? "github-dark" : "github-light",
lang: "bash",
})}</code>`;
},
};

marked.use({ renderer });
});

onBeforeUnmount(() => {
shiki?.dispose();
});

// Pagination
const fetching = ref(false);
const fetchMoreForceDisabled = ref(!commitsWithRelease.value.length);

Expand Down Expand Up @@ -218,7 +254,7 @@ async function fetchMore() {
</div>

<div
class="max-w-full p-4 border border-gray-100 dark:border-gray-800 rounded-lg prose dark:prose-invert"
class="max-w-full p-4 overflow-x-scroll border border-gray-100 dark:border-gray-800 rounded-lg prose dark:prose-invert flex flex-col gap-2"
v-html="marked(selectedCommit.release.text)"
/>
</div>
Expand Down
14 changes: 2 additions & 12 deletions packages/app/app/layouts/default.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,12 @@ const route = useRoute();
<ClientOnly>
<UButton
aria-label="Toggle theme"
:icon="
colorMode.preference === 'dark'
? 'ph-moon'
: colorMode.preference === 'light'
? 'ph-sun'
: 'ph-moon-stars'
"
:icon="colorMode.preference === 'light' ? 'ph-sun' : 'ph-moon-stars'"
color="neutral"
variant="link"
@click="
colorMode.preference =
colorMode.preference === 'dark'
? 'system'
: colorMode.preference === 'system'
? 'light'
: 'dark'
colorMode.preference === 'dark' ? 'light' : 'dark'
"
/>
<template #fallback>
Expand Down
4 changes: 2 additions & 2 deletions packages/app/e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ beforeAll(async () => {
server = await app.listen(3300);

await ezSpawn.async(
"pnpm cross-env TEST=true pnpm --filter=app run build",
"pnpm cross-env TEST=true NITRO_GH_BASE_URL=http://localhost:3300 pnpm --filter=app run build",
[],
{
stdio: "inherit",
Expand Down Expand Up @@ -203,7 +203,7 @@ describe.sequential.each([
}, 10_000);
});

describe("URL redirects", () => {
describe("uRL redirects", () => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo here ig

describe("standard packages", () => {
it("redirects full URLs correctly", async () => {
const response = await fetchWithRedirect("/tinylibs/tinybench@a832a55");
Expand Down
26 changes: 13 additions & 13 deletions packages/app/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import process from "node:process";
import ncb from "nitro-cloudflare-dev";
// import ncb from "nitro-cloudflare-dev";
import { resolve } from "pathe";

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
sourcemap: true,
compatibilityDate: "2024-07-30",

// https://nuxt.com/docs/getting-started/upgrade#testing-nuxt-4
future: { compatibilityVersion: 4 },

// https://nuxt.com/modules
modules: ["@nuxt/eslint", "@nuxt/ui", "@vueuse/nuxt"],
modules: ["@nuxt/eslint", "@nuxt/ui", "@vueuse/nuxt", "nitro-cloudflare-dev"],

css: ["~/assets/css/main.css"],

Expand All @@ -28,7 +28,6 @@ export default defineNuxtConfig({
preset: "cloudflare-pages",
sourceMap: "inline",
compatibilityDate: "2024-09-19",
modules: [ncb],
externals: {
inline: [
"@octokit",
Expand All @@ -47,14 +46,15 @@ export default defineNuxtConfig({
},

runtimeConfig: {
appId: process.env.NITRO_APP_ID || "",
webhookSecret: process.env.NITRO_WEBHOOK_SECRET || "",
privateKey: process.env.NITRO_PRIVATE_KEY || "",
rmStaleKey: process.env.NITRO_RM_STALE_KEY || "",
githubToken:
process.env.GITHUB_TOKEN || process.env.NITRO_GITHUB_TOKEN || "",
ghBaseUrl: process.env.NITRO_GH_BASE_URL || "https://api.github.com",
test: process.env.NITRO_TEST || "",
nitro: {
envPrefix: "NITRO_",
},
appId: "",
webhookSecret: "",
privateKey: "",
rmStaleKey: "",
ghBaseUrl: "https://api.github.com",
test: "",
},

hooks: {
Expand All @@ -68,7 +68,7 @@ export default defineNuxtConfig({
handler: clientRenderer,
});
nitro.options.handlers.unshift({
route: "/view/**",
route: "/~/**",
handler: clientRenderer,
});
},
Expand Down
3 changes: 3 additions & 0 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"dependencies": {
"@iconify-json/ph": "^1.1.14",
"@nuxt/ui": "^3.0.0-alpha.10",
"@nuxtjs/mdc": "0.17.0",
"@octokit/app": "^15.1.1",
"@octokit/graphql": "^8.1.1",
"@octokit/plugin-paginate-rest": "^11.3.6",
Expand All @@ -26,8 +27,10 @@
"@vueuse/nuxt": "^12.2.0",
"marked": "^15.0.4",
"nuxt": "^3.15.0",
"nuxt-shiki": "0.3.0",
"octokit": "^4.0.2",
"query-registry": "^3.0.1",
"unstorage": "^1.16.0",
"vue": "^3.5.13",
"vue-router": "^4.4.3",
"zod": "^3.23.8"
Expand Down
22 changes: 14 additions & 8 deletions packages/app/server/api/repo/commits.get.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { z } from "zod";
import { useGithubREST } from "../../../server/utils/octokit";

const querySchema = z.object({
owner: z.string(),
Expand All @@ -14,12 +13,19 @@
const query = await getValidatedQuery(event, (data) =>
querySchema.parse(data),
);
const octokit = useGithubREST(event);
const installation = await useOctokitInstallation(
event,
query.owner,
query.repo,
);

const { data: repo } = await octokit.request("GET /repos/{owner}/{repo}", {
owner: query.owner,
repo: query.repo,
});
const { data: repo } = await installation.request(
"GET /repos/{owner}/{repo}",
{
owner: query.owner,
repo: query.repo,
},
);

const defaultBranch = repo.default_branch;

Expand All @@ -30,7 +36,7 @@
: 1;
const per_page = Number.parseInt(query.per_page);

const { data: commits } = await octokit.request(
const { data: commits } = await installation.request(
"GET /repos/{owner}/{repo}/commits",
{
owner: query.owner,
Expand All @@ -44,7 +50,7 @@
const commitsWithStatuses = await Promise.all(
commits.map(async (commit) => {
try {
const { data: checkRuns } = await octokit.request(
const { data: checkRuns } = await installation.request(
"GET /repos/{owner}/{repo}/commits/{ref}/check-runs",
{
owner: query.owner,
Expand Down Expand Up @@ -89,7 +95,7 @@
: null,
};
} catch (error) {
console.warn(

Check warning on line 98 in packages/app/server/api/repo/commits.get.ts

View workflow job for this annotation

GitHub Actions / Run Linting

Unexpected console statement
`Could not fetch check runs for commit ${commit.sha}:`,
error,
);
Expand Down Expand Up @@ -126,7 +132,7 @@
},
};
} catch (error) {
console.error("Error fetching repository commits:", error);

Check warning on line 135 in packages/app/server/api/repo/commits.get.ts

View workflow job for this annotation

GitHub Actions / Run Linting

Unexpected console statement

return {
id: "error",
Expand Down
7 changes: 3 additions & 4 deletions packages/app/server/api/repo/index.get.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import type { H3Event } from "h3";
import { z } from "zod";
import { useGithubREST } from "../../../server/utils/octokit";

const querySchema = z.object({
owner: z.string(),
repo: z.string(),
});

const getRepoInfo = defineCachedFunction(
async (owner: string, repo: string, event?: H3Event) => {
async (owner: string, repo: string, event: H3Event) => {
try {
const octokit = useGithubREST(event);
const installation = await useOctokitInstallation(event, owner, repo);

const { data } = await octokit.request("GET /repos/{owner}/{repo}", {
const { data } = await installation.request("GET /repos/{owner}/{repo}", {
owner,
repo,
});
Expand All @@ -30,7 +29,7 @@
description: data.description || "",
};
} catch (error) {
console.error(

Check warning on line 32 in packages/app/server/api/repo/index.get.ts

View workflow job for this annotation

GitHub Actions / Run Linting

Unexpected console statement
`Error fetching repository info for ${owner}/${repo}:`,
error,
);
Expand All @@ -38,7 +37,7 @@
}
},
{
getKey: (owner: string, repo: string, _event?: H3Event) =>

Check warning on line 40 in packages/app/server/api/repo/index.get.ts

View workflow job for this annotation

GitHub Actions / Run Linting

'_event' is defined but never used
`${owner}/${repo}`,
maxAge: 60 * 30, // 30 minutes
swr: true,
Expand All @@ -52,7 +51,7 @@
);
return getRepoInfo(query.owner, query.repo, event);
} catch (error) {
console.error("Error in repo info endpoint:", error);

Check warning on line 54 in packages/app/server/api/repo/index.get.ts

View workflow job for this annotation

GitHub Actions / Run Linting

Unexpected console statement
return {
error: true,
message: (error as Error).message,
Expand Down
84 changes: 62 additions & 22 deletions packages/app/server/api/repo/search.get.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import type { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods";
import { z } from "zod";
import { useGithubREST } from "../../../server/utils/octokit";

const querySchema = z.object({
text: z.string(),
});

export default defineEventHandler(async (event) => {
const r2Binding = useBinding(event);
const request = toWebRequest(event);
const signal = request.signal;

try {
const query = await getValidatedQuery(event, (data) =>
querySchema.parse(data),
Expand All @@ -16,32 +18,59 @@
return { nodes: [] };
}

const octokit = useGithubREST(event);
const searchText = query.text.toLowerCase();

// Internal pagination: iterate until uniqueNodes is filled or no more objects
let cursor: string | undefined;
const seen = new Set<string>();
const uniqueNodes = [];
const maxNodes = 10;
let keepGoing = true;

while (uniqueNodes.length < maxNodes && keepGoing && !signal.aborted) {
const listResult = await r2Binding.list({
prefix: usePackagesBucket.base,
limit: 1000,
cursor,
});
const { objects, truncated } = listResult;
cursor = truncated ? listResult.cursor : undefined;

const parsedObjects = objects.map((obj) => parseKey(obj.key));
const filtered = parsedObjects.filter((obj) => {
const orgRepo = `${obj.org}/${obj.repo}`.toLowerCase();
return (
obj.org.toLowerCase().includes(searchText) ||
obj.repo.toLowerCase().includes(searchText) ||
orgRepo.includes(searchText)
);
});

const { data } = await octokit.request("GET /search/repositories", {
q: query.text,
per_page: 10,
});
for (const obj of filtered) {
const key = `${obj.org}/${obj.repo}`;
if (!seen.has(key)) {
seen.add(key);
uniqueNodes.push({
name: obj.repo,
owner: {
login: obj.org,
avatarUrl: `https://github.com/${obj.org}.png`,
},
});
if (uniqueNodes.length >= maxNodes) break;
}
}

if (!truncated || uniqueNodes.length >= maxNodes) {
keepGoing = false;
}
}

return {
nodes: data.items.map(
(
repo: RestEndpointMethodTypes["search"]["repos"]["response"]["data"]["items"][0],
) => ({
id: repo.id.toString(),
name: repo.name,
owner: repo.owner
? {
id: repo.owner.id.toString(),
login: repo.owner.login,
avatarUrl: repo.owner.avatar_url,
}
: null,
}),
),
nodes: uniqueNodes,
};
} catch (error) {
console.error("Error in repository search:", error);

Check warning on line 73 in packages/app/server/api/repo/search.get.ts

View workflow job for this annotation

GitHub Actions / Run Linting

Unexpected console statement
return {
nodes: [],
error: true,
Expand All @@ -49,3 +78,14 @@
};
}
});

function parseKey(key: string) {
const parts = key.split(":");
return {
org: parts[2],
repo: parts[3],
hash: parts[4],
suffix: parts[5],
key,
};
}
Loading
Loading