diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..a18586ba --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +root = true + +[*] +end_of_line = lf +indent_size = 2 +indent_style = space +trim_trailing_whitespace = true diff --git a/.github/workflows/stats.yml b/.github/workflows/stats.yml index 7a661343..d2ce8f14 100644 --- a/.github/workflows/stats.yml +++ b/.github/workflows/stats.yml @@ -1,85 +1,85 @@ -name: pkg.pr.new stats - -on: - push: - -jobs: - stats: - runs-on: ubuntu-latest - steps: - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - node-version: '16' - - - name: Fetch and Count Data Iteratively - uses: actions/github-script@v6 - with: - script: | - async function fetchAndCountData(url) { - const counts = { - templates: 0, - packages: 0, - orgs: new Set(), - repos: new Set(), - commits: 0, - prsAndBranches: 0, - }; - let cursor = null; - - do { - try { - // Construct the URL with cursor if available - const fetchUrl = cursor ? `${url}?cursor=${encodeURIComponent(cursor)}` : url; - - // Fetch the data from the API - const response = await fetch(fetchUrl); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const responseData = await response.json(); - - // Process the current page of data - if (responseData.data) { - for (const item of responseData.data) { - if (item.type === "template") { - counts.templates += 1; - } else if (item.type === "package") { - counts.packages += 1; - counts.orgs.add(item.org); - counts.repos.add(item.repo); - counts.commits += 1; // Count one commit per package - } else if (item.type === "cursor") { - counts.orgs.add(item.org); - counts.repos.add(item.repo); - - // Count PR numbers and branch names combined - counts.prsAndBranches += 1; - } - } - } - - // Update the cursor for the next page - cursor = responseData.nextCursor || null; - } catch (error) { - console.error(`Error fetching data: ${error.message}`); - cursor = null; // Exit the loop on error - } - } while (cursor); - - return { - templates: counts.templates, - packages: counts.packages, - orgs: counts.orgs.size, - repos: counts.repos.size, - commits: counts.commits, - prsAndBranches: counts.prsAndBranches, - }; - } - - const url = "https://pkg.pr.new/stats"; - const counts = await fetchAndCountData(url); - - console.log("Counts:", counts); +name: pkg.pr.new stats + +on: + push: + +jobs: + stats: + runs-on: ubuntu-latest + steps: + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: "16" + + - name: Fetch and Count Data Iteratively + uses: actions/github-script@v6 + with: + script: | + async function fetchAndCountData(url) { + const counts = { + templates: 0, + packages: 0, + orgs: new Set(), + repos: new Set(), + commits: 0, + prsAndBranches: 0, + }; + let cursor = null; + + do { + try { + // Construct the URL with cursor if available + const fetchUrl = cursor ? `${url}?cursor=${encodeURIComponent(cursor)}` : url; + + // Fetch the data from the API + const response = await fetch(fetchUrl); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const responseData = await response.json(); + + // Process the current page of data + if (responseData.data) { + for (const item of responseData.data) { + if (item.type === "template") { + counts.templates += 1; + } else if (item.type === "package") { + counts.packages += 1; + counts.orgs.add(item.org); + counts.repos.add(item.repo); + counts.commits += 1; // Count one commit per package + } else if (item.type === "cursor") { + counts.orgs.add(item.org); + counts.repos.add(item.repo); + + // Count PR numbers and branch names combined + counts.prsAndBranches += 1; + } + } + } + + // Update the cursor for the next page + cursor = responseData.nextCursor || null; + } catch (error) { + console.error(`Error fetching data: ${error.message}`); + cursor = null; // Exit the loop on error + } + } while (cursor); + + return { + templates: counts.templates, + packages: counts.packages, + orgs: counts.orgs.size, + repos: counts.repos.size, + commits: counts.commits, + prsAndBranches: counts.prsAndBranches, + }; + } + + const url = "https://pkg.pr.new/stats"; + const counts = await fetchAndCountData(url); + + console.log("Counts:", counts); diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 48accbeb..59b10062 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,7 +6,6 @@ on: - release pull_request: - jobs: ci: runs-on: [ubuntu-latest] diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..bd5535a6 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +pnpm-lock.yaml diff --git a/packages/backend/script/update-webhook-url.ts b/packages/backend/script/update-webhook-url.ts index 6fb5ad80..407b592f 100644 --- a/packages/backend/script/update-webhook-url.ts +++ b/packages/backend/script/update-webhook-url.ts @@ -7,7 +7,7 @@ config({ path: ".dev.vars" }); const nitroSigner = createSigner({ algorithm: "RS256", key: process.env.NITRO_PRIVATE_KEY!, -}) +}); function generateJWT() { const payload = { diff --git a/packages/backend/server/routes/[owner]/[repo]/[packageAndRefOrSha].get.ts b/packages/backend/server/routes/[owner]/[repo]/[packageAndRefOrSha].get.ts index a42f009c..b081cb4b 100644 --- a/packages/backend/server/routes/[owner]/[repo]/[packageAndRefOrSha].get.ts +++ b/packages/backend/server/routes/[owner]/[repo]/[packageAndRefOrSha].get.ts @@ -1,6 +1,6 @@ import { WorkflowData } from "../../../types"; import { abbreviateCommitHash } from "@pkg-pr-new/utils"; -import { normalizeKey } from 'unstorage' +import { normalizeKey } from "unstorage"; type Params = Omit & { packageAndRefOrSha: string; @@ -8,11 +8,14 @@ type Params = Omit & { export default eventHandler(async (event) => { const params = getRouterParams(event) as Params; - let [encodedPackageName, longerRefOrSha] = params.packageAndRefOrSha.split("@"); + let [encodedPackageName, longerRefOrSha] = + params.packageAndRefOrSha.split("@"); const packageName = decodeURIComponent(encodedPackageName); - longerRefOrSha = longerRefOrSha.split('.tgz')[0] // yarn support + longerRefOrSha = longerRefOrSha.split(".tgz")[0]; // yarn support const isSha = isValidGitHash(longerRefOrSha); - const refOrSha = isSha ? abbreviateCommitHash(longerRefOrSha) : longerRefOrSha; + const refOrSha = isSha + ? abbreviateCommitHash(longerRefOrSha) + : longerRefOrSha; let base = `${params.owner}:${params.repo}:${refOrSha}`; let packageKey = `${base}:${packageName}`; @@ -35,7 +38,9 @@ export default eventHandler(async (event) => { // longer sha support with precision const binding = useBinding(event); - const { objects } = await binding.list({ prefix: `${usePackagesBucket.base}:${base}` }) + const { objects } = await binding.list({ + prefix: `${usePackagesBucket.base}:${base}`, + }); for (const { key } of objects) { // bucket:package:stackblitz-labs:pkg.pr.new:ded05e838c418096e5dd77a29101c8af9e73daea:playground-b const trimmedKey = key.slice(usePackagesBucket.base.length + 1); diff --git a/packages/backend/server/routes/multipart/create.post.ts b/packages/backend/server/routes/multipart/create.post.ts index d0a30dfe..cfaed746 100644 --- a/packages/backend/server/routes/multipart/create.post.ts +++ b/packages/backend/server/routes/multipart/create.post.ts @@ -11,7 +11,7 @@ export default eventHandler(async (event) => { }); } const workflowsBucket = useWorkflowsBucket(event); - + if (!(await workflowsBucket.hasItem(workflowKey))) { throw createError({ statusCode: 401, @@ -37,7 +37,7 @@ export default eventHandler(async (event) => { const binding = useBinding(event); - const base = `${workflowData.owner}:${workflowData.repo}:${workflowData.sha}` + const base = `${workflowData.owner}:${workflowData.repo}:${workflowData.sha}`; const packageKey = `${base}:${packageName}`; const key = joinKeys(usePackagesBucket.base, packageKey); diff --git a/packages/backend/server/routes/publish.post.ts b/packages/backend/server/routes/publish.post.ts index 4dd87d75..f6a9f795 100644 --- a/packages/backend/server/routes/publish.post.ts +++ b/packages/backend/server/routes/publish.post.ts @@ -1,8 +1,4 @@ -import { - Comment, - isPullRequest, - isWhitelisted, -} from "@pkg-pr-new/utils"; +import { Comment, isPullRequest, isWhitelisted } from "@pkg-pr-new/utils"; import { randomUUID } from "uncrypto"; import { setItemStream, useTemplatesBucket } from "~/utils/bucket"; import { useOctokitInstallation } from "~/utils/octokit"; diff --git a/packages/backend/server/routes/stats.get.ts b/packages/backend/server/routes/stats.get.ts index 6054475f..c4856473 100644 --- a/packages/backend/server/routes/stats.get.ts +++ b/packages/backend/server/routes/stats.get.ts @@ -1,73 +1,73 @@ -import { sha256 } from "ohash"; - -export default eventHandler(async (event) => { - try { - const binding = useBinding(event); - const query = getQuery(event); - - let cursor = query.cursor as string || undefined; - let objectCount = 0; - - const packagesPrefix = `${usePackagesBucket.base}:`; - const cursorsPrefix = `${useCursorsBucket.base}:`; - const templatesPrefix = `${useTemplatesBucket.base}:`; - - const results = []; - - const response = await binding.list({ cursor, limit: 500 }); - objectCount += response.objects.length; - - for (const { key } of response.objects) { - let result = null; - - if (key.startsWith(packagesPrefix)) { - const trimmedKey = key.slice(packagesPrefix.length); - const [org, repo, commit, ...packageNameParts] = trimmedKey.split(":"); - const packageName = packageNameParts.join(":"); - - result = { - type: "package", - org: sha256(org), - repo: sha256(repo), - commit: sha256(commit), - packageName: sha256(packageName), - }; - } else if (key.startsWith(cursorsPrefix)) { - const trimmedKey = key.slice(cursorsPrefix.length); - const parts = trimmedKey.split(":"); - const ref = parts.slice(2).join(":"); - - result = { - type: "cursor", - org: sha256(parts[0]), - repo: sha256(parts[1]), - ref: sha256(ref), - }; - } else if (key.startsWith(templatesPrefix)) { - const trimmedKey = key.slice(templatesPrefix.length); - const template = trimmedKey; - - result = { - type: "template", - template: sha256(template), - }; - } - - if (result) { - results.push(result); - } - } - - const nextCursor = response.truncated ? response.cursor : null; - - return { - data: results, - nextCursor: nextCursor, - }; - } catch (error) { - throw createError({ - statusCode: 500, - message: error instanceof Error ? error.message : String(error), - }); - } -}); +import { sha256 } from "ohash"; + +export default eventHandler(async (event) => { + try { + const binding = useBinding(event); + const query = getQuery(event); + + let cursor = (query.cursor as string) || undefined; + let objectCount = 0; + + const packagesPrefix = `${usePackagesBucket.base}:`; + const cursorsPrefix = `${useCursorsBucket.base}:`; + const templatesPrefix = `${useTemplatesBucket.base}:`; + + const results = []; + + const response = await binding.list({ cursor, limit: 500 }); + objectCount += response.objects.length; + + for (const { key } of response.objects) { + let result = null; + + if (key.startsWith(packagesPrefix)) { + const trimmedKey = key.slice(packagesPrefix.length); + const [org, repo, commit, ...packageNameParts] = trimmedKey.split(":"); + const packageName = packageNameParts.join(":"); + + result = { + type: "package", + org: sha256(org), + repo: sha256(repo), + commit: sha256(commit), + packageName: sha256(packageName), + }; + } else if (key.startsWith(cursorsPrefix)) { + const trimmedKey = key.slice(cursorsPrefix.length); + const parts = trimmedKey.split(":"); + const ref = parts.slice(2).join(":"); + + result = { + type: "cursor", + org: sha256(parts[0]), + repo: sha256(parts[1]), + ref: sha256(ref), + }; + } else if (key.startsWith(templatesPrefix)) { + const trimmedKey = key.slice(templatesPrefix.length); + const template = trimmedKey; + + result = { + type: "template", + template: sha256(template), + }; + } + + if (result) { + results.push(result); + } + } + + const nextCursor = response.truncated ? response.cursor : null; + + return { + data: results, + nextCursor: nextCursor, + }; + } catch (error) { + throw createError({ + statusCode: 500, + message: error instanceof Error ? error.message : String(error), + }); + } +}); diff --git a/packages/backend/server/routes/webhook.post.ts b/packages/backend/server/routes/webhook.post.ts index 01df2f6d..d80ad308 100644 --- a/packages/backend/server/routes/webhook.post.ts +++ b/packages/backend/server/routes/webhook.post.ts @@ -47,9 +47,8 @@ export default eventHandler(async (event) => { // old: the old of hashing the prData started to hit collision, so we need to use the new one (e.g. https://github.com/element-plus/element-plus/actions/runs/12351113750/job/34465376908) const oldPrDataHash = hash(prData); - const isOldPullRequest = await pullRequestNumbersBucket.hasItem( - oldPrDataHash, - ); + const isOldPullRequest = + await pullRequestNumbersBucket.hasItem(oldPrDataHash); const isPullRequest = isNewPullRequest || isOldPullRequest; const prNumber = await pullRequestNumbersBucket.getItem( diff --git a/packages/backend/server/types.ts b/packages/backend/server/types.ts index 1dc26021..7afa2fa6 100644 --- a/packages/backend/server/types.ts +++ b/packages/backend/server/types.ts @@ -6,9 +6,9 @@ export type WorkflowData = { }; export type PullRequestData = { - full_name: string, - ref: string -} + full_name: string; + ref: string; +}; export type Cursor = { timestamp: number; diff --git a/packages/backend/server/utils/bucket.ts b/packages/backend/server/utils/bucket.ts index 17e9420c..e733b447 100644 --- a/packages/backend/server/utils/bucket.ts +++ b/packages/backend/server/utils/bucket.ts @@ -38,7 +38,7 @@ export async function getItemStream( const binding = useBinding(event); key = joinKeys(base, key); - const value = await binding.get(key, opts) + const value = await binding.get(key, opts); return value?.body; } diff --git a/packages/backend/server/utils/markdown.ts b/packages/backend/server/utils/markdown.ts index edc5adf4..a2a92afd 100644 --- a/packages/backend/server/utils/markdown.ts +++ b/packages/backend/server/utils/markdown.ts @@ -27,8 +27,8 @@ export function generateCommitPublishMessage( compact, ); - if (packageManager === 'yarn') { - shaUrl = shaUrl + '.tgz' + if (packageManager === "yarn") { + shaUrl = shaUrl + ".tgz"; } return ` @@ -38,7 +38,9 @@ ${packageManager} ${packageCommands[packageManager]} ${shaUrl} `; }) .map((message, i) => - isMoreThanFour ? createCollapsibleBlock(`${packages[i]}`, message) : message, + isMoreThanFour + ? createCollapsibleBlock(`${packages[i]}`, message) + : message, ) .join("\n"); @@ -73,8 +75,8 @@ export function generatePullRequestPublishMessage( compact, ); - if (packageManager === 'yarn') { - refUrl = refUrl + '.tgz' + if (packageManager === "yarn") { + refUrl = refUrl + ".tgz"; } return ` @@ -84,7 +86,9 @@ ${packageManager} ${packageCommands[packageManager]} ${refUrl} `; }) .map((message, i) => - isMoreThanFour ? createCollapsibleBlock(`${packages[i]}`, message) : message, + isMoreThanFour + ? createCollapsibleBlock(`${packages[i]}`, message) + : message, ) .join("\n"); @@ -101,10 +105,14 @@ _commit: ${abbreviateCommitHash(workflowData.sha) function generateTemplatesStr(templates: Record) { const entries = Object.entries(templates).filter(([k]) => k !== "default"); - let str = templates["default"] ? `[Open in Stackblitz](${templates["default"]})` : ''; + let str = templates["default"] + ? `[Open in Stackblitz](${templates["default"]})` + : ""; if (entries.length && entries.length <= 2) { - str = [str, ...entries.map(([k, v]) => `[${k}](${v})`)].filter(Boolean).join(" • "); + str = [str, ...entries.map(([k, v]) => `[${k}](${v})`)] + .filter(Boolean) + .join(" • "); } else if (entries.length > 2) { str += createCollapsibleBlock( "More templates", diff --git a/packages/backend/server/utils/octokit.ts b/packages/backend/server/utils/octokit.ts index 1bf8ae4e..a1038ecb 100644 --- a/packages/backend/server/utils/octokit.ts +++ b/packages/backend/server/utils/octokit.ts @@ -12,7 +12,11 @@ export function useOctokitApp(event: H3Event): AppType { }) as unknown as AppType; } -export async function useOctokitInstallation(event: H3Event, owner: string, repo: string) { +export async function useOctokitInstallation( + event: H3Event, + owner: string, + repo: string, +) { const app = useOctokitApp(event); const { data: installationData } = await app.octokit.request( "GET /repos/{owner}/{repo}/installation", diff --git a/packages/cli/index.ts b/packages/cli/index.ts index c0489b99..5f964014 100644 --- a/packages/cli/index.ts +++ b/packages/cli/index.ts @@ -89,7 +89,8 @@ const main = defineCommand({ }, packageManager: { type: "string", - description: "Specify the package manager to use (npm, bun, pnpm, yarn)", + description: + "Specify the package manager to use (npm, bun, pnpm, yarn)", enum: ["npm", "bun", "pnpm", "yarn"], default: "npm", }, @@ -118,11 +119,17 @@ const main = defineCommand({ const isOnlyTemplates = !!args["only-templates"]; const comment: Comment = args.comment as Comment; - const selectedPackageManager = args.packageManager as "npm" | "bun" | "pnpm" | "yarn"; - - if (!["npm", "bun", "pnpm", "yarn"].includes(selectedPackageManager)) { + const selectedPackageManager = args.packageManager as + | "npm" + | "bun" + | "pnpm" + | "yarn"; + + if ( + !["npm", "bun", "pnpm", "yarn"].includes(selectedPackageManager) + ) { console.error( - `Unsupported package manager: ${selectedPackageManager}. Supported managers are npm, bun, pnpm, yarn.` + `Unsupported package manager: ${selectedPackageManager}. Supported managers are npm, bun, pnpm, yarn.`, ); process.exit(1); } @@ -491,8 +498,11 @@ const main = defineCommand({ .filter((k) => k.startsWith("package:")) .map((name, i) => { const packageName = name.slice("package:".length); - const url = new URL(laterRes.urls[i]) - const publintUrl = new URL(`/pkg.pr.new${url.pathname}`, "https://publint.dev") + const url = new URL(laterRes.urls[i]); + const publintUrl = new URL( + `/pkg.pr.new${url.pathname}`, + "https://publint.dev", + ); return `${packageName}: - sha: ${shasums[packageName]} - publint: ${publintUrl} diff --git a/packages/utils/index.ts b/packages/utils/index.ts index 918d7a44..cc573e0f 100644 --- a/packages/utils/index.ts +++ b/packages/utils/index.ts @@ -37,8 +37,6 @@ export function isPullRequest(ref: string) { export type Comment = "off" | "create" | "update"; export type PackageManager = "npm" | "pnpm" | "yarn" | "bun"; - - const whitelist = "https://raw.githubusercontent.com/stackblitz-labs/pkg.pr.new/main/.whitelist"; diff --git a/templates/example-1/index.js b/templates/example-1/index.js index afd4d357..c5845d94 100644 --- a/templates/example-1/index.js +++ b/templates/example-1/index.js @@ -1 +1 @@ -console.log('template') +console.log("template");