@vercel/factory 0.0.18 → 0.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/integrations/github-issues.d.mts +19 -8
- package/dist/integrations/github-issues.mjs +11 -6
- package/dist/integrations/github-issues.mjs.map +1 -1
- package/dist/integrations/github.d.mts +2 -2
- package/dist/integrations/github.mjs +2 -2
- package/dist/integrations/vercel.d.mts +2 -2
- package/dist/vercel-github-api.d.mts +12 -2
- package/dist/vercel-github-api.mjs +53 -0
- package/dist/vercel-github-api.mjs.map +1 -1
- package/dist/vercel.d.mts +2 -2
- package/dist/vercel.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @vercel/factory
|
|
2
2
|
|
|
3
|
+
## 0.0.20
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 52a1829: Apply originating-flow labels to pull requests published with Vercel-managed GitHub credentials.
|
|
8
|
+
|
|
9
|
+
## 0.0.19
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 2f5561a: Expose confirmed GitHub source-publication metadata through readGitHubIssuePublication and migrate PR label readers in the reference factory and CLI scaffold.
|
|
14
|
+
|
|
3
15
|
## 0.0.18
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -37,17 +37,28 @@ declare function createGitHubIssuePublisher(options: GitHubIssuePublisherOptions
|
|
|
37
37
|
/** Recovers an existing publication by source key, or returns null without reserving or posting. */
|
|
38
38
|
reconcile(key: string): Promise<PublishGitHubIssueResult | null>;
|
|
39
39
|
};
|
|
40
|
-
/**
|
|
41
|
-
|
|
40
|
+
/** Confirmed source-key publication and host-owned metadata for a GitHub issue. */
|
|
41
|
+
interface GitHubIssuePublication {
|
|
42
|
+
repository: RepositorySlug;
|
|
43
|
+
key: string;
|
|
44
|
+
createdAt: string;
|
|
45
|
+
labels: readonly {
|
|
46
|
+
name: string;
|
|
47
|
+
color: string;
|
|
48
|
+
description: string;
|
|
49
|
+
}[];
|
|
50
|
+
issue: {
|
|
51
|
+
number: number;
|
|
52
|
+
html_url: string;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** Reads a confirmed Factory publication matching the repository, source marker, and issue. */
|
|
56
|
+
declare function readGitHubIssuePublication(options: {
|
|
42
57
|
state: Pick<ConditionalStateStore, "read">;
|
|
43
58
|
repository: RepositorySlug;
|
|
44
59
|
number: number;
|
|
45
60
|
body: string;
|
|
46
|
-
}): Promise<
|
|
47
|
-
name: string;
|
|
48
|
-
color: string;
|
|
49
|
-
description: string;
|
|
50
|
-
}[]>;
|
|
61
|
+
}): Promise<GitHubIssuePublication | null>;
|
|
51
62
|
//#endregion
|
|
52
|
-
export { GitHubIssuePublisherOptions, PublishGitHubIssueOptions, PublishGitHubIssueResult, createGitHubIssuePublisher,
|
|
63
|
+
export { GitHubIssuePublication, GitHubIssuePublisherOptions, PublishGitHubIssueOptions, PublishGitHubIssueResult, createGitHubIssuePublisher, readGitHubIssuePublication };
|
|
53
64
|
//# sourceMappingURL=github-issues.d.mts.map
|
|
@@ -155,16 +155,21 @@ function createGitHubIssuePublisher(options) {
|
|
|
155
155
|
}
|
|
156
156
|
};
|
|
157
157
|
}
|
|
158
|
-
/** Reads
|
|
159
|
-
async function
|
|
158
|
+
/** Reads a confirmed Factory publication matching the repository, source marker, and issue. */
|
|
159
|
+
async function readGitHubIssuePublication(options) {
|
|
160
160
|
const digest = /<!-- factory-issue:([a-f0-9]{64}) -->/u.exec(options.body)?.[1];
|
|
161
|
-
if (!digest) return
|
|
161
|
+
if (!digest) return null;
|
|
162
162
|
const stored = await options.state.read({ pathname: pathnameFor(digest) });
|
|
163
|
-
if (!stored) return
|
|
163
|
+
if (!stored) return null;
|
|
164
164
|
const publication = publicationSchema.parse(JSON.parse(stored.body));
|
|
165
|
-
|
|
165
|
+
const { issue } = publication;
|
|
166
|
+
const expectedDigest = createHash("sha256").update(JSON.stringify([publication.repository, publication.key])).digest("hex");
|
|
167
|
+
return publication.repository === options.repository && issue?.number === options.number && digest === expectedDigest ? {
|
|
168
|
+
...publication,
|
|
169
|
+
issue
|
|
170
|
+
} : null;
|
|
166
171
|
}
|
|
167
172
|
//#endregion
|
|
168
|
-
export { createGitHubIssuePublisher,
|
|
173
|
+
export { createGitHubIssuePublisher, readGitHubIssuePublication };
|
|
169
174
|
|
|
170
175
|
//# sourceMappingURL=github-issues.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github-issues.mjs","names":[],"sources":["../../src/integrations/github-issues.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\nimport { repositorySlugSchema, type RepositorySlug } from \"../schema/repository\";\nimport type { ConditionalStateStore } from \"../store/state\";\nimport { ensureGitHubLabel } from \"../github\";\n\nconst issueSchema = z.object({ number: z.number().int().positive(), html_url: z.url() });\nconst labelSchema = z.object({\n name: z.string().min(1).max(50),\n color: z.string().regex(/^[\\da-f]{6}$/iu),\n description: z.string().max(100),\n});\nconst publicationSchema = z.strictObject({\n repository: repositorySlugSchema,\n key: z.string(),\n createdAt: z.iso.datetime(),\n labels: z.array(labelSchema),\n issue: issueSchema.optional(),\n});\ntype State = Pick<ConditionalStateStore, \"read\" | \"write\">;\nconst pathnameFor = (digest: string) => `github-issue-publications/${digest}.json`;\n\n/** Host-owned credentials, repository scope, labels, and durable state for issue publication. */\nexport interface GitHubIssuePublisherOptions {\n repository: RepositorySlug;\n state: State;\n labels: readonly { name: string; color: string; description: string }[];\n authorize(): Promise<void>;\n token(): Promise<string>;\n fetch?: typeof globalThis.fetch;\n}\n\n/** A stable source identity and the sanitized issue the caller wants to publish. */\nexport interface PublishGitHubIssueOptions {\n key: string;\n title: string;\n body: string;\n}\n\n/** A confirmed GitHub issue, or an ambiguous write that requires reconciliation. */\nexport type PublishGitHubIssueResult =\n | { status: \"published\"; number: number; url: string }\n | { status: \"pending\"; reason: string };\n\n/** Creates an authorized publisher and a recovery operation that never creates issues or reservations. */\nexport function createGitHubIssuePublisher(options: GitHubIssuePublisherOptions) {\n repositorySlugSchema.parse(options.repository);\n const labels = z.array(labelSchema).max(10).parse(options.labels);\n async function resolve(\n key: string,\n input?: PublishGitHubIssueOptions,\n ): Promise<PublishGitHubIssueResult | null> {\n z.string().min(1).max(500).parse(key);\n await options.authorize();\n const digest = createHash(\"sha256\")\n .update(JSON.stringify([options.repository, key]))\n .digest(\"hex\");\n const pathname = pathnameFor(digest);\n const marker = `<!-- factory-issue:${digest} -->`;\n const api = `https://api.github.com/repos/${options.repository}`;\n const request: typeof globalThis.fetch = async (url, init) => {\n await options.authorize();\n return (options.fetch ?? globalThis.fetch)(url, {\n ...init,\n redirect: \"error\",\n signal: AbortSignal.timeout(30000),\n headers: {\n Authorization: `Bearer ${await options.token()}`,\n Accept: \"application/vnd.github+json\",\n \"Content-Type\": \"application/json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n },\n });\n };\n const read = () => options.state.read({ pathname });\n let stored = await read();\n let claimed = false;\n if (!stored) {\n if (!input) return null;\n // Label setup has no non-idempotent issue side effect and can be retried before claiming.\n for (const label of labels)\n await ensureGitHubLabel({\n repository: options.repository,\n token: \"\",\n fetch: request,\n ...label,\n });\n try {\n await options.state.write({\n pathname,\n overwrite: false,\n body: JSON.stringify({\n repository: options.repository,\n key,\n createdAt: new Date().toISOString(),\n labels,\n }),\n });\n claimed = true;\n } catch (error) {\n if (!(await read())) throw error;\n }\n stored = await read();\n }\n if (!stored) throw new Error(\"Issue publication reservation is missing\");\n const publication = publicationSchema.parse(JSON.parse(stored.body));\n if (publication.repository !== options.repository || publication.key !== key)\n throw new Error(\"Issue publication identity mismatch\");\n const result = (issue: z.infer<typeof issueSchema>): PublishGitHubIssueResult => ({\n status: \"published\",\n number: issue.number,\n url: issue.html_url,\n });\n if (publication.issue) return result(publication.issue);\n const save = async (issue: z.infer<typeof issueSchema>) => {\n try {\n await options.state.write({\n pathname,\n overwrite: true,\n ifMatch: stored.etag,\n body: JSON.stringify({ ...publication, issue }),\n });\n } catch (error) {\n const winner = await read();\n if (\n !winner ||\n publicationSchema.parse(JSON.parse(winner.body)).issue?.number !== issue.number\n )\n throw error;\n }\n return result(issue);\n };\n if (claimed && input) {\n // GitHub has no issue-creation idempotency key. Any failure after this claim is ambiguous.\n const response = await request(`${api}/issues`, {\n method: \"POST\",\n body: JSON.stringify({\n title: input.title,\n body: `${input.body}\\n\\n${marker}`,\n labels: publication.labels.map(({ name }) => name),\n }),\n });\n if (!response.ok)\n throw new Error(\n `GitHub issue creation returned ${response.status}; retry this source key to reconcile`,\n );\n return save(issueSchema.parse(await response.json()));\n }\n const since = new Date(Date.parse(publication.createdAt) - 60000).toISOString();\n for (let page = 1; page <= 10; page++) {\n const query = new URLSearchParams({\n state: \"all\",\n since,\n sort: \"created\",\n direction: \"asc\",\n per_page: \"100\",\n page: String(page),\n });\n const response = await request(`${api}/issues?${query}`);\n if (!response.ok) throw new Error(`GitHub issue reconciliation returned ${response.status}`);\n const issues = z\n .array(\n issueSchema.extend({ body: z.string().nullable(), pull_request: z.unknown().optional() }),\n )\n .parse(await response.json());\n const matches = issues.filter((issue) => !issue.pull_request && issue.body?.includes(marker));\n if (matches.length > 1)\n throw new Error(\"Multiple GitHub issues have the publication marker; inspect manually\");\n if (matches[0]) return save(matches[0]);\n if (issues.length < 100) break;\n }\n return {\n status: \"pending\",\n reason:\n \"An earlier publication may still be in flight. No second issue was created. Inspect GitHub and the publication record before resetting this source key.\",\n };\n }\n return {\n /** Publishes once per source key, reconciling any existing reservation without another POST. */\n async publish(input: PublishGitHubIssueOptions): Promise<PublishGitHubIssueResult> {\n z.object({\n key: z.string().min(1).max(500),\n title: z.string().min(1).max(256),\n body: z.string().min(1).max(50000),\n }).parse(input);\n if (input.body.includes(\"<!-- factory-issue:\"))\n throw new Error(\"Issue body contains a reserved publication marker\");\n const result = await resolve(input.key, input);\n if (!result) throw new Error(\"Issue publication reservation is missing\");\n return result;\n },\n /** Recovers an existing publication by source key, or returns null without reserving or posting. */\n reconcile(key: string): Promise<PublishGitHubIssueResult | null> {\n return resolve(key);\n },\n };\n}\n\n/** Reads labels only from a confirmed Factory publication matching the source issue. */\nexport async function readGitHubIssuePublicationLabels(options: {\n state: Pick<ConditionalStateStore, \"read\">;\n repository: RepositorySlug;\n number: number;\n body: string;\n}): Promise<readonly { name: string; color: string; description: string }[]> {\n const digest = /<!-- factory-issue:([a-f0-9]{64}) -->/u.exec(options.body)?.[1];\n if (!digest) return [];\n const stored = await options.state.read({ pathname: pathnameFor(digest) });\n if (!stored) return [];\n const publication = publicationSchema.parse(JSON.parse(stored.body));\n return publication.repository === options.repository &&\n publication.issue?.number === options.number\n ? publication.labels\n : [];\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"github-issues.mjs","names":[],"sources":["../../src/integrations/github-issues.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\nimport { repositorySlugSchema, type RepositorySlug } from \"../schema/repository\";\nimport type { ConditionalStateStore } from \"../store/state\";\nimport { ensureGitHubLabel } from \"../github\";\n\nconst issueSchema = z.object({ number: z.number().int().positive(), html_url: z.url() });\nconst labelSchema = z.object({\n name: z.string().min(1).max(50),\n color: z.string().regex(/^[\\da-f]{6}$/iu),\n description: z.string().max(100),\n});\nconst publicationSchema = z.strictObject({\n repository: repositorySlugSchema,\n key: z.string(),\n createdAt: z.iso.datetime(),\n labels: z.array(labelSchema),\n issue: issueSchema.optional(),\n});\ntype State = Pick<ConditionalStateStore, \"read\" | \"write\">;\nconst pathnameFor = (digest: string) => `github-issue-publications/${digest}.json`;\n\n/** Host-owned credentials, repository scope, labels, and durable state for issue publication. */\nexport interface GitHubIssuePublisherOptions {\n repository: RepositorySlug;\n state: State;\n labels: readonly { name: string; color: string; description: string }[];\n authorize(): Promise<void>;\n token(): Promise<string>;\n fetch?: typeof globalThis.fetch;\n}\n\n/** A stable source identity and the sanitized issue the caller wants to publish. */\nexport interface PublishGitHubIssueOptions {\n key: string;\n title: string;\n body: string;\n}\n\n/** A confirmed GitHub issue, or an ambiguous write that requires reconciliation. */\nexport type PublishGitHubIssueResult =\n | { status: \"published\"; number: number; url: string }\n | { status: \"pending\"; reason: string };\n\n/** Creates an authorized publisher and a recovery operation that never creates issues or reservations. */\nexport function createGitHubIssuePublisher(options: GitHubIssuePublisherOptions) {\n repositorySlugSchema.parse(options.repository);\n const labels = z.array(labelSchema).max(10).parse(options.labels);\n async function resolve(\n key: string,\n input?: PublishGitHubIssueOptions,\n ): Promise<PublishGitHubIssueResult | null> {\n z.string().min(1).max(500).parse(key);\n await options.authorize();\n const digest = createHash(\"sha256\")\n .update(JSON.stringify([options.repository, key]))\n .digest(\"hex\");\n const pathname = pathnameFor(digest);\n const marker = `<!-- factory-issue:${digest} -->`;\n const api = `https://api.github.com/repos/${options.repository}`;\n const request: typeof globalThis.fetch = async (url, init) => {\n await options.authorize();\n return (options.fetch ?? globalThis.fetch)(url, {\n ...init,\n redirect: \"error\",\n signal: AbortSignal.timeout(30000),\n headers: {\n Authorization: `Bearer ${await options.token()}`,\n Accept: \"application/vnd.github+json\",\n \"Content-Type\": \"application/json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n },\n });\n };\n const read = () => options.state.read({ pathname });\n let stored = await read();\n let claimed = false;\n if (!stored) {\n if (!input) return null;\n // Label setup has no non-idempotent issue side effect and can be retried before claiming.\n for (const label of labels)\n await ensureGitHubLabel({\n repository: options.repository,\n token: \"\",\n fetch: request,\n ...label,\n });\n try {\n await options.state.write({\n pathname,\n overwrite: false,\n body: JSON.stringify({\n repository: options.repository,\n key,\n createdAt: new Date().toISOString(),\n labels,\n }),\n });\n claimed = true;\n } catch (error) {\n if (!(await read())) throw error;\n }\n stored = await read();\n }\n if (!stored) throw new Error(\"Issue publication reservation is missing\");\n const publication = publicationSchema.parse(JSON.parse(stored.body));\n if (publication.repository !== options.repository || publication.key !== key)\n throw new Error(\"Issue publication identity mismatch\");\n const result = (issue: z.infer<typeof issueSchema>): PublishGitHubIssueResult => ({\n status: \"published\",\n number: issue.number,\n url: issue.html_url,\n });\n if (publication.issue) return result(publication.issue);\n const save = async (issue: z.infer<typeof issueSchema>) => {\n try {\n await options.state.write({\n pathname,\n overwrite: true,\n ifMatch: stored.etag,\n body: JSON.stringify({ ...publication, issue }),\n });\n } catch (error) {\n const winner = await read();\n if (\n !winner ||\n publicationSchema.parse(JSON.parse(winner.body)).issue?.number !== issue.number\n )\n throw error;\n }\n return result(issue);\n };\n if (claimed && input) {\n // GitHub has no issue-creation idempotency key. Any failure after this claim is ambiguous.\n const response = await request(`${api}/issues`, {\n method: \"POST\",\n body: JSON.stringify({\n title: input.title,\n body: `${input.body}\\n\\n${marker}`,\n labels: publication.labels.map(({ name }) => name),\n }),\n });\n if (!response.ok)\n throw new Error(\n `GitHub issue creation returned ${response.status}; retry this source key to reconcile`,\n );\n return save(issueSchema.parse(await response.json()));\n }\n const since = new Date(Date.parse(publication.createdAt) - 60000).toISOString();\n for (let page = 1; page <= 10; page++) {\n const query = new URLSearchParams({\n state: \"all\",\n since,\n sort: \"created\",\n direction: \"asc\",\n per_page: \"100\",\n page: String(page),\n });\n const response = await request(`${api}/issues?${query}`);\n if (!response.ok) throw new Error(`GitHub issue reconciliation returned ${response.status}`);\n const issues = z\n .array(\n issueSchema.extend({ body: z.string().nullable(), pull_request: z.unknown().optional() }),\n )\n .parse(await response.json());\n const matches = issues.filter((issue) => !issue.pull_request && issue.body?.includes(marker));\n if (matches.length > 1)\n throw new Error(\"Multiple GitHub issues have the publication marker; inspect manually\");\n if (matches[0]) return save(matches[0]);\n if (issues.length < 100) break;\n }\n return {\n status: \"pending\",\n reason:\n \"An earlier publication may still be in flight. No second issue was created. Inspect GitHub and the publication record before resetting this source key.\",\n };\n }\n return {\n /** Publishes once per source key, reconciling any existing reservation without another POST. */\n async publish(input: PublishGitHubIssueOptions): Promise<PublishGitHubIssueResult> {\n z.object({\n key: z.string().min(1).max(500),\n title: z.string().min(1).max(256),\n body: z.string().min(1).max(50000),\n }).parse(input);\n if (input.body.includes(\"<!-- factory-issue:\"))\n throw new Error(\"Issue body contains a reserved publication marker\");\n const result = await resolve(input.key, input);\n if (!result) throw new Error(\"Issue publication reservation is missing\");\n return result;\n },\n /** Recovers an existing publication by source key, or returns null without reserving or posting. */\n reconcile(key: string): Promise<PublishGitHubIssueResult | null> {\n return resolve(key);\n },\n };\n}\n\n/** Confirmed source-key publication and host-owned metadata for a GitHub issue. */\nexport interface GitHubIssuePublication {\n repository: RepositorySlug;\n key: string;\n createdAt: string;\n labels: readonly { name: string; color: string; description: string }[];\n issue: { number: number; html_url: string };\n}\n\n/** Reads a confirmed Factory publication matching the repository, source marker, and issue. */\nexport async function readGitHubIssuePublication(options: {\n state: Pick<ConditionalStateStore, \"read\">;\n repository: RepositorySlug;\n number: number;\n body: string;\n}): Promise<GitHubIssuePublication | null> {\n const digest = /<!-- factory-issue:([a-f0-9]{64}) -->/u.exec(options.body)?.[1];\n if (!digest) return null;\n const stored = await options.state.read({ pathname: pathnameFor(digest) });\n if (!stored) return null;\n const publication = publicationSchema.parse(JSON.parse(stored.body));\n const { issue } = publication;\n const expectedDigest = createHash(\"sha256\")\n .update(JSON.stringify([publication.repository, publication.key]))\n .digest(\"hex\");\n return publication.repository === options.repository &&\n issue?.number === options.number &&\n digest === expectedDigest\n ? { ...publication, issue }\n : null;\n}\n"],"mappings":"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"}
|
|
@@ -8,5 +8,5 @@ import { GitHubReviewReporter, GitHubReviewReporterOptions, ReconcileReviewCheck
|
|
|
8
8
|
import { GitHubPullRequestPublicationGate, GitHubPullRequestPublicationGateOptions, GitHubPullRequestPublisher, GitHubPullRequestPublisherOptions, PublishGitHubPullRequestInput, PushGitHubCheckpointInput, RequireTaskPullRequestInput, createGitHubPullRequestPublicationGate, createGitHubPullRequestPublisher } from "../pull-requests/github-publisher.mjs";
|
|
9
9
|
import { GitHubPullRequestLabelManager, GitHubPullRequestLabelManagerOptions, ManageGitHubPullRequestLabelInput, createGitHubPullRequestLabelManager } from "../pull-requests/github-label-manager.mjs";
|
|
10
10
|
import { GitHubOpenPullRequestLister, GitHubOpenPullRequestListerOptions, createGitHubOpenPullRequestLister } from "../work-triage/github.mjs";
|
|
11
|
-
import { GitHubIssuePublisherOptions, PublishGitHubIssueOptions, PublishGitHubIssueResult, createGitHubIssuePublisher,
|
|
12
|
-
export { type AnswerReviewCommentInput, type CheckRunAnnotation, type CheckRunAnnotationLevel, type CheckRunConclusion, type CheckRunFields, type CheckRunOutput, type CheckRunProgressStatus, type CheckoutGitHubRepositoryInput, type CommittedGitChange, type CompletedCheckRunFields, type ConfiguredEventRepositoryInput, type ConflictedGitHubPullRequest, type CreateCheckRunInput, type CreateGitHubBranchInput, type CreateGitHubCommitInput, type CreateGitHubPublicationPullRequestInput, type CreateGitHubTreeInput, type CreatePullRequestCommentInput, type CreatePullRequestInput, type CreatedGitHubPublicationPullRequest, type CreatedGitHubPullRequest, type EnsureGitHubLabelInput, type FactoryMentionOptions, type FactoryQuestionReply, type FactoryReviewComment, type FetchGitHubBundleInput, type FetchGitHubRefsInput, type GitHubAppFactoryInput, type GitHubAppInstallationToken, type GitHubAppLike, type GitHubAppPermissions, type GitHubCheckRun, GitHubCheckoutConflictError, type GitHubCiFailureSignal, type GitHubCommit, type GitHubCommunicationAdapterOptions, type GitHubDefaultBranchPush, type GitHubGitTransport, type GitHubIssueComment, type GitHubIssueCommentRecord, type GitHubIssuePublisherOptions, type GitHubIssueSignal, type GitHubOpenPullRequestLister, type GitHubOpenPullRequestListerOptions, GitHubPublicationApiError, type GitHubPublicationClient, type GitHubPublicationClientOptions, type GitHubPublicationLimits, type GitHubPublicationPullRequest, type GitHubPullRequest, type GitHubPullRequestClosed, type GitHubPullRequestDiscussionComment, type GitHubPullRequestDiscussionPage, type GitHubPullRequestDiscussionReview, type GitHubPullRequestHead, type GitHubPullRequestLabelManager, type GitHubPullRequestLabelManagerOptions, type GitHubPullRequestPublicationGate, type GitHubPullRequestPublicationGateOptions, type GitHubPullRequestPublisher, type GitHubPullRequestPublisherOptions, type GitHubPullRequestReference, type GitHubPullRequestSignal, type GitHubPullRequestTarget, type GitHubRefMapping, type GitHubReplyTarget, type GitHubRepositoryAuthorRequest, type GitHubRepositoryBundle, type GitHubRepositoryRequest, type GitHubReviewComment, GitHubReviewCommentAuthorError, type GitHubReviewReporter, type GitHubReviewReporterOptions, type GitHubTreeDeletion, type GitHubTreeEntry, type GitHubTreeUpsert, type GitTransferSandbox, type IncompleteCheckRunFields, type ListCheckRunsForRefInput, type ListConflictedPullRequestsInput, type ListGitHubPublicationPullRequestsInput, type ListOpenPullRequestsInput, type ListPullRequestCommentsInput, type ListPullRequestDiscussionInput, type ManageGitHubPullRequestLabelInput, type MarkGitHubPullRequestReadyInput, type MintGitHubAppInstallationTokenInput, type NormalizeFactoryCommentInput, type NormalizeFactoryReviewCommentInput, type ObservedCheckRunStatus, type PublishGitHubIssueOptions, type PublishGitHubIssueResult, type PublishGitHubPullRequestInput, type PublishedGitHubReview, type PullRequestLabelInput, type PullRequestReviewComment, type PullRequestReviewEvent, type PushGitHubBundleInput, type PushGitHubCheckpointInput, type PushGitHubHeadInput, type ReadCommittedGitChangeInput, type ReadGitHubBranchHeadInput, type ReadGitHubCommitInput, type ReadGitHubPullRequestInput, type ReadGitTransferFileInput, type ReadReviewCommentInput, type ReconcileReviewChecksInput, type ReconcileReviewChecksOptions, type ReconcileReviewChecksResult, type RequireTaskPullRequestInput, type ReviewCommentAuthorPolicy, type RunSandboxGitInput, type SandboxGit, type SandboxGitCommandInput, type SandboxGitCommandResult, StalePullRequestHeadError, type SubmitPullRequestReviewInput, type SubmittedPullRequestReview, type UpdateCheckRunInput, type UpdateGitHubBranchInput, type UpdatePullRequestCommentInput, type VerifyGitHubSignatureInput, type WithSandboxGitBundleInput, type WriteGitTransferFileInput, addLabel, answerReviewComment, checkoutGitHubRepository, closePullRequest, configuredEventRepositorySlug, createCheckRun, createGitHubCommunicationAdapter, createGitHubIssuePublisher, createGitHubOpenPullRequestLister, createGitHubPublicationClient, createGitHubPullRequestLabelManager, createGitHubPullRequestPublicationGate, createGitHubPullRequestPublisher, createGitHubReviewReporter, createPullRequest, createPullRequestComment, ensureGitHubLabel, factorySessionBranch, factoryTaskBranch, hasGitHubRepositoryWriteAccess, isSafeGitRef, listCheckRunsForRef, listConflictedPullRequests, listOpenPullRequests, listPullRequestComments, listPullRequestDiscussion, mintGitHubAppInstallationToken, normalizeFactoryQuestionReply, normalizeFactoryReviewComment, normalizeGitHubDefaultBranchPush, normalizeGitHubIssueOpened, normalizeGitHubPullRequest, normalizeGitHubPullRequestClosed, normalizeGitHubWorkflowRunFailure, parseGitHubReplyAddress, readCommittedGitChange,
|
|
11
|
+
import { GitHubIssuePublication, GitHubIssuePublisherOptions, PublishGitHubIssueOptions, PublishGitHubIssueResult, createGitHubIssuePublisher, readGitHubIssuePublication } from "./github-issues.mjs";
|
|
12
|
+
export { type AnswerReviewCommentInput, type CheckRunAnnotation, type CheckRunAnnotationLevel, type CheckRunConclusion, type CheckRunFields, type CheckRunOutput, type CheckRunProgressStatus, type CheckoutGitHubRepositoryInput, type CommittedGitChange, type CompletedCheckRunFields, type ConfiguredEventRepositoryInput, type ConflictedGitHubPullRequest, type CreateCheckRunInput, type CreateGitHubBranchInput, type CreateGitHubCommitInput, type CreateGitHubPublicationPullRequestInput, type CreateGitHubTreeInput, type CreatePullRequestCommentInput, type CreatePullRequestInput, type CreatedGitHubPublicationPullRequest, type CreatedGitHubPullRequest, type EnsureGitHubLabelInput, type FactoryMentionOptions, type FactoryQuestionReply, type FactoryReviewComment, type FetchGitHubBundleInput, type FetchGitHubRefsInput, type GitHubAppFactoryInput, type GitHubAppInstallationToken, type GitHubAppLike, type GitHubAppPermissions, type GitHubCheckRun, GitHubCheckoutConflictError, type GitHubCiFailureSignal, type GitHubCommit, type GitHubCommunicationAdapterOptions, type GitHubDefaultBranchPush, type GitHubGitTransport, type GitHubIssueComment, type GitHubIssueCommentRecord, type GitHubIssuePublication, type GitHubIssuePublisherOptions, type GitHubIssueSignal, type GitHubOpenPullRequestLister, type GitHubOpenPullRequestListerOptions, GitHubPublicationApiError, type GitHubPublicationClient, type GitHubPublicationClientOptions, type GitHubPublicationLimits, type GitHubPublicationPullRequest, type GitHubPullRequest, type GitHubPullRequestClosed, type GitHubPullRequestDiscussionComment, type GitHubPullRequestDiscussionPage, type GitHubPullRequestDiscussionReview, type GitHubPullRequestHead, type GitHubPullRequestLabelManager, type GitHubPullRequestLabelManagerOptions, type GitHubPullRequestPublicationGate, type GitHubPullRequestPublicationGateOptions, type GitHubPullRequestPublisher, type GitHubPullRequestPublisherOptions, type GitHubPullRequestReference, type GitHubPullRequestSignal, type GitHubPullRequestTarget, type GitHubRefMapping, type GitHubReplyTarget, type GitHubRepositoryAuthorRequest, type GitHubRepositoryBundle, type GitHubRepositoryRequest, type GitHubReviewComment, GitHubReviewCommentAuthorError, type GitHubReviewReporter, type GitHubReviewReporterOptions, type GitHubTreeDeletion, type GitHubTreeEntry, type GitHubTreeUpsert, type GitTransferSandbox, type IncompleteCheckRunFields, type ListCheckRunsForRefInput, type ListConflictedPullRequestsInput, type ListGitHubPublicationPullRequestsInput, type ListOpenPullRequestsInput, type ListPullRequestCommentsInput, type ListPullRequestDiscussionInput, type ManageGitHubPullRequestLabelInput, type MarkGitHubPullRequestReadyInput, type MintGitHubAppInstallationTokenInput, type NormalizeFactoryCommentInput, type NormalizeFactoryReviewCommentInput, type ObservedCheckRunStatus, type PublishGitHubIssueOptions, type PublishGitHubIssueResult, type PublishGitHubPullRequestInput, type PublishedGitHubReview, type PullRequestLabelInput, type PullRequestReviewComment, type PullRequestReviewEvent, type PushGitHubBundleInput, type PushGitHubCheckpointInput, type PushGitHubHeadInput, type ReadCommittedGitChangeInput, type ReadGitHubBranchHeadInput, type ReadGitHubCommitInput, type ReadGitHubPullRequestInput, type ReadGitTransferFileInput, type ReadReviewCommentInput, type ReconcileReviewChecksInput, type ReconcileReviewChecksOptions, type ReconcileReviewChecksResult, type RequireTaskPullRequestInput, type ReviewCommentAuthorPolicy, type RunSandboxGitInput, type SandboxGit, type SandboxGitCommandInput, type SandboxGitCommandResult, StalePullRequestHeadError, type SubmitPullRequestReviewInput, type SubmittedPullRequestReview, type UpdateCheckRunInput, type UpdateGitHubBranchInput, type UpdatePullRequestCommentInput, type VerifyGitHubSignatureInput, type WithSandboxGitBundleInput, type WriteGitTransferFileInput, addLabel, answerReviewComment, checkoutGitHubRepository, closePullRequest, configuredEventRepositorySlug, createCheckRun, createGitHubCommunicationAdapter, createGitHubIssuePublisher, createGitHubOpenPullRequestLister, createGitHubPublicationClient, createGitHubPullRequestLabelManager, createGitHubPullRequestPublicationGate, createGitHubPullRequestPublisher, createGitHubReviewReporter, createPullRequest, createPullRequestComment, ensureGitHubLabel, factorySessionBranch, factoryTaskBranch, hasGitHubRepositoryWriteAccess, isSafeGitRef, listCheckRunsForRef, listConflictedPullRequests, listOpenPullRequests, listPullRequestComments, listPullRequestDiscussion, mintGitHubAppInstallationToken, normalizeFactoryQuestionReply, normalizeFactoryReviewComment, normalizeGitHubDefaultBranchPush, normalizeGitHubIssueOpened, normalizeGitHubPullRequest, normalizeGitHubPullRequestClosed, normalizeGitHubWorkflowRunFailure, parseGitHubReplyAddress, readCommittedGitChange, readGitHubIssuePublication, readIssueComment, readPullRequest, readPullRequestHead, readReviewComment, removeLabel, renderGitHubCommunicationEvent, runSandboxGit, stripFactoryQuestionMention, submitPullRequestReview, updateCheckRun, updatePullRequestComment, verifyGitHubSignature };
|
|
@@ -6,5 +6,5 @@ import { createGitHubReviewReporter } from "../code-review/github-reporter.mjs";
|
|
|
6
6
|
import { createGitHubPullRequestPublicationGate, createGitHubPullRequestPublisher } from "../pull-requests/github-publisher.mjs";
|
|
7
7
|
import { createGitHubPullRequestLabelManager } from "../pull-requests/github-label-manager.mjs";
|
|
8
8
|
import { createGitHubOpenPullRequestLister } from "../work-triage/github.mjs";
|
|
9
|
-
import { createGitHubIssuePublisher,
|
|
10
|
-
export { GitHubCheckoutConflictError, GitHubPublicationApiError, GitHubReviewCommentAuthorError, StalePullRequestHeadError, addLabel, answerReviewComment, checkoutGitHubRepository, closePullRequest, configuredEventRepositorySlug, createCheckRun, createGitHubCommunicationAdapter, createGitHubIssuePublisher, createGitHubOpenPullRequestLister, createGitHubPublicationClient, createGitHubPullRequestLabelManager, createGitHubPullRequestPublicationGate, createGitHubPullRequestPublisher, createGitHubReviewReporter, createPullRequest, createPullRequestComment, ensureGitHubLabel, factorySessionBranch, factoryTaskBranch, hasGitHubRepositoryWriteAccess, isSafeGitRef, listCheckRunsForRef, listConflictedPullRequests, listOpenPullRequests, listPullRequestComments, listPullRequestDiscussion, mintGitHubAppInstallationToken, normalizeFactoryQuestionReply, normalizeFactoryReviewComment, normalizeGitHubDefaultBranchPush, normalizeGitHubIssueOpened, normalizeGitHubPullRequest, normalizeGitHubPullRequestClosed, normalizeGitHubWorkflowRunFailure, parseGitHubReplyAddress, readCommittedGitChange,
|
|
9
|
+
import { createGitHubIssuePublisher, readGitHubIssuePublication } from "./github-issues.mjs";
|
|
10
|
+
export { GitHubCheckoutConflictError, GitHubPublicationApiError, GitHubReviewCommentAuthorError, StalePullRequestHeadError, addLabel, answerReviewComment, checkoutGitHubRepository, closePullRequest, configuredEventRepositorySlug, createCheckRun, createGitHubCommunicationAdapter, createGitHubIssuePublisher, createGitHubOpenPullRequestLister, createGitHubPublicationClient, createGitHubPullRequestLabelManager, createGitHubPullRequestPublicationGate, createGitHubPullRequestPublisher, createGitHubReviewReporter, createPullRequest, createPullRequestComment, ensureGitHubLabel, factorySessionBranch, factoryTaskBranch, hasGitHubRepositoryWriteAccess, isSafeGitRef, listCheckRunsForRef, listConflictedPullRequests, listOpenPullRequests, listPullRequestComments, listPullRequestDiscussion, mintGitHubAppInstallationToken, normalizeFactoryQuestionReply, normalizeFactoryReviewComment, normalizeGitHubDefaultBranchPush, normalizeGitHubIssueOpened, normalizeGitHubPullRequest, normalizeGitHubPullRequestClosed, normalizeGitHubWorkflowRunFailure, parseGitHubReplyAddress, readCommittedGitChange, readGitHubIssuePublication, readIssueComment, readPullRequest, readPullRequestHead, readReviewComment, removeLabel, renderGitHubCommunicationEvent, runSandboxGit, stripFactoryQuestionMention, submitPullRequestReview, updateCheckRun, updatePullRequestComment, verifyGitHubSignature };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import { VercelManagedGitHubApi, VercelManagedGitHubApiAction, VercelManagedGitHubApiError, VercelManagedGitHubApiOptions, VercelManagedGitHubApiSandboxCreateInput, VercelManagedGitHubIssue, VercelManagedGitHubIssueInput, VercelManagedGitHubPublication, VercelManagedGitHubPublicationInput, VercelManagedGitHubPullRequest, VercelManagedGitHubPullRequestInput, createVercelManagedGitHubApi } from "../vercel-github-api.mjs";
|
|
2
|
+
import { VercelManagedGitHubApi, VercelManagedGitHubApiAction, VercelManagedGitHubApiError, VercelManagedGitHubApiOptions, VercelManagedGitHubApiSandboxCreateInput, VercelManagedGitHubIssue, VercelManagedGitHubIssueInput, VercelManagedGitHubLabelInput, VercelManagedGitHubPublication, VercelManagedGitHubPublicationInput, VercelManagedGitHubPullRequest, VercelManagedGitHubPullRequestInput, createVercelManagedGitHubApi } from "../vercel-github-api.mjs";
|
|
3
3
|
import { VercelGitHubGitTransportOptions, VercelGitHubTokenInput, VercelManagedGitHubGitTransportOptions, VercelManagedGitHubSandboxCreateInput, createVercelGitHubGitTransport, createVercelManagedGitHubGitTransport } from "../vercel-git.mjs";
|
|
4
4
|
import { VercelProductionDeployment, VercelQueueSend, VercelQueueSendOptions, VercelWorkQueue, VercelWorkQueueOptions, authenticateVercelUserToken, createVercelWorkQueue, normalizeVercelProductionDeployment, verifyVercelSignature } from "../vercel.mjs";
|
|
5
|
-
export { type VercelGitHubGitTransportOptions, type VercelGitHubTokenInput, type VercelManagedGitHubApi, type VercelManagedGitHubApiAction, VercelManagedGitHubApiError, type VercelManagedGitHubApiOptions, type VercelManagedGitHubApiSandboxCreateInput, type VercelManagedGitHubGitTransportOptions, type VercelManagedGitHubIssue, type VercelManagedGitHubIssueInput, type VercelManagedGitHubPublication, type VercelManagedGitHubPublicationInput, type VercelManagedGitHubPullRequest, type VercelManagedGitHubPullRequestInput, type VercelManagedGitHubSandboxCreateInput, type VercelProductionDeployment, type VercelQueueSend, type VercelQueueSendOptions, type VercelWorkQueue, type VercelWorkQueueOptions, authenticateVercelUserToken, createVercelGitHubGitTransport, createVercelManagedGitHubApi, createVercelManagedGitHubGitTransport, createVercelWorkQueue, normalizeVercelProductionDeployment, verifyVercelSignature };
|
|
5
|
+
export { type VercelGitHubGitTransportOptions, type VercelGitHubTokenInput, type VercelManagedGitHubApi, type VercelManagedGitHubApiAction, VercelManagedGitHubApiError, type VercelManagedGitHubApiOptions, type VercelManagedGitHubApiSandboxCreateInput, type VercelManagedGitHubGitTransportOptions, type VercelManagedGitHubIssue, type VercelManagedGitHubIssueInput, type VercelManagedGitHubLabelInput, type VercelManagedGitHubPublication, type VercelManagedGitHubPublicationInput, type VercelManagedGitHubPullRequest, type VercelManagedGitHubPullRequestInput, type VercelManagedGitHubSandboxCreateInput, type VercelProductionDeployment, type VercelQueueSend, type VercelQueueSendOptions, type VercelWorkQueue, type VercelWorkQueueOptions, authenticateVercelUserToken, createVercelGitHubGitTransport, createVercelManagedGitHubApi, createVercelManagedGitHubGitTransport, createVercelWorkQueue, normalizeVercelProductionDeployment, verifyVercelSignature };
|
|
@@ -4,7 +4,7 @@ import { NetworkPolicy, Sandbox } from "@vercel/sandbox";
|
|
|
4
4
|
//#region src/vercel-github-api.d.ts
|
|
5
5
|
type TrustedGitHubApiSandbox = Pick<Sandbox, "runCommand" | "stop">;
|
|
6
6
|
/** GitHub API actions supported by Vercel-managed Sandbox credentials. */
|
|
7
|
-
type VercelManagedGitHubApiAction = "github:issues:read" | "github:pull-request:create";
|
|
7
|
+
type VercelManagedGitHubApiAction = "github:issues:read" | "github:pull-request:create" | "github:labels:write";
|
|
8
8
|
/** Exact creation request for one Vercel-managed GitHub API operation. */
|
|
9
9
|
interface VercelManagedGitHubApiSandboxCreateInput {
|
|
10
10
|
source?: {
|
|
@@ -52,6 +52,15 @@ interface VercelManagedGitHubIssue {
|
|
|
52
52
|
url: string;
|
|
53
53
|
pullRequest: boolean;
|
|
54
54
|
}
|
|
55
|
+
/** Authorized request to ensure and attach a repository flow label to a pull request. */
|
|
56
|
+
interface VercelManagedGitHubLabelInput {
|
|
57
|
+
repository: RepositorySlug;
|
|
58
|
+
pullRequestNumber: number;
|
|
59
|
+
name: string;
|
|
60
|
+
color: string;
|
|
61
|
+
description: string;
|
|
62
|
+
authorize(): Promise<void>;
|
|
63
|
+
}
|
|
55
64
|
/** Authorized request to create one draft or ready GitHub pull request. */
|
|
56
65
|
interface VercelManagedGitHubPullRequestInput {
|
|
57
66
|
repository: RepositorySlug;
|
|
@@ -88,6 +97,7 @@ interface VercelManagedGitHubPublication extends VercelManagedGitHubPullRequest
|
|
|
88
97
|
/** Repository-scoped brokered GitHub API operations supplied by Vercel Sandbox. */
|
|
89
98
|
interface VercelManagedGitHubApi {
|
|
90
99
|
readIssue(input: VercelManagedGitHubIssueInput): Promise<VercelManagedGitHubIssue>;
|
|
100
|
+
labelPullRequest(input: VercelManagedGitHubLabelInput): Promise<void>;
|
|
91
101
|
createPullRequest(input: VercelManagedGitHubPullRequestInput): Promise<VercelManagedGitHubPullRequest>;
|
|
92
102
|
publishPullRequest(input: VercelManagedGitHubPublicationInput): Promise<VercelManagedGitHubPublication>;
|
|
93
103
|
}
|
|
@@ -99,5 +109,5 @@ declare class VercelManagedGitHubApiError extends Error {
|
|
|
99
109
|
/** Create repository-scoped GitHub API operations backed by managed Sandbox credentials. */
|
|
100
110
|
declare function createVercelManagedGitHubApi(options?: VercelManagedGitHubApiOptions): VercelManagedGitHubApi;
|
|
101
111
|
//#endregion
|
|
102
|
-
export { VercelManagedGitHubApi, VercelManagedGitHubApiAction, VercelManagedGitHubApiError, VercelManagedGitHubApiOptions, VercelManagedGitHubApiSandboxCreateInput, VercelManagedGitHubIssue, VercelManagedGitHubIssueInput, VercelManagedGitHubPublication, VercelManagedGitHubPublicationInput, VercelManagedGitHubPullRequest, VercelManagedGitHubPullRequestInput, createVercelManagedGitHubApi };
|
|
112
|
+
export { VercelManagedGitHubApi, VercelManagedGitHubApiAction, VercelManagedGitHubApiError, VercelManagedGitHubApiOptions, VercelManagedGitHubApiSandboxCreateInput, VercelManagedGitHubIssue, VercelManagedGitHubIssueInput, VercelManagedGitHubLabelInput, VercelManagedGitHubPublication, VercelManagedGitHubPublicationInput, VercelManagedGitHubPullRequest, VercelManagedGitHubPullRequestInput, createVercelManagedGitHubApi };
|
|
103
113
|
//# sourceMappingURL=vercel-github-api.d.mts.map
|
|
@@ -21,6 +21,15 @@ const issueSchema = z.object({
|
|
|
21
21
|
html_url: z.url(),
|
|
22
22
|
pull_request: z.unknown().optional()
|
|
23
23
|
});
|
|
24
|
+
const labelSchema = z.object({ name: z.string() });
|
|
25
|
+
const labelInputSchema = z.object({
|
|
26
|
+
name: z.string().min(1).max(50).refine((name) => Buffer.byteLength(name, "utf8") <= 50 && name.trim() === name && [...name].every((character) => {
|
|
27
|
+
const code = character.charCodeAt(0);
|
|
28
|
+
return code > 31 && code !== 127 && character !== "\\";
|
|
29
|
+
}) && name.split("/").every((part) => part !== "" && part !== "." && part !== "..")),
|
|
30
|
+
color: z.string().regex(/^[a-fA-F0-9]{6}$/u),
|
|
31
|
+
description: z.string().max(100).refine((description) => Buffer.byteLength(description, "utf8") <= 100)
|
|
32
|
+
});
|
|
24
33
|
const pullRequestSchema = z.object({
|
|
25
34
|
number: z.number().int().positive(),
|
|
26
35
|
html_url: z.url(),
|
|
@@ -206,6 +215,50 @@ function createVercelManagedGitHubApi(options = {}) {
|
|
|
206
215
|
pullRequest: issue.pull_request !== void 0
|
|
207
216
|
};
|
|
208
217
|
},
|
|
218
|
+
async labelPullRequest(input) {
|
|
219
|
+
const repository = repositorySlugSchema.parse(input.repository.toLowerCase());
|
|
220
|
+
if (!Number.isSafeInteger(input.pullRequestNumber) || input.pullRequestNumber < 1) throw new Error("Invalid GitHub pull request number");
|
|
221
|
+
const label = labelInputSchema.parse(input);
|
|
222
|
+
const sandbox = await createSandbox(createInput(repository, "github:labels:write"));
|
|
223
|
+
try {
|
|
224
|
+
const authorizedRequest = async (request) => {
|
|
225
|
+
await input.authorize();
|
|
226
|
+
return sandboxRequest(sandbox, {
|
|
227
|
+
repository,
|
|
228
|
+
...request
|
|
229
|
+
});
|
|
230
|
+
};
|
|
231
|
+
const labelPath = `/labels/${encodeURIComponent(label.name)}`;
|
|
232
|
+
const read = async () => {
|
|
233
|
+
if (labelSchema.parse(await authorizedRequest({
|
|
234
|
+
method: "GET",
|
|
235
|
+
path: labelPath
|
|
236
|
+
})).name.toLowerCase() !== label.name.toLowerCase()) throw new Error("GitHub returned a different repository label");
|
|
237
|
+
};
|
|
238
|
+
try {
|
|
239
|
+
await read();
|
|
240
|
+
} catch (error) {
|
|
241
|
+
if (!(error instanceof VercelManagedGitHubApiError) || error.status !== 404) throw error;
|
|
242
|
+
try {
|
|
243
|
+
await authorizedRequest({
|
|
244
|
+
method: "POST",
|
|
245
|
+
path: "/labels",
|
|
246
|
+
body: label
|
|
247
|
+
});
|
|
248
|
+
} catch (creationError) {
|
|
249
|
+
if (!(creationError instanceof VercelManagedGitHubApiError) || creationError.status !== 422) throw creationError;
|
|
250
|
+
await read();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
await authorizedRequest({
|
|
254
|
+
method: "POST",
|
|
255
|
+
path: `/issues/${input.pullRequestNumber}/labels`,
|
|
256
|
+
body: { labels: [label.name] }
|
|
257
|
+
});
|
|
258
|
+
} finally {
|
|
259
|
+
await sandbox.stop();
|
|
260
|
+
}
|
|
261
|
+
},
|
|
209
262
|
async createPullRequest(input) {
|
|
210
263
|
const repository = repositorySlugSchema.parse(input.repository.toLowerCase());
|
|
211
264
|
const head = publicationBranchSchema.parse(input.head);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vercel-github-api.mjs","names":[],"sources":["../src/vercel-github-api.ts"],"sourcesContent":["import type { Sandbox, NetworkPolicy } from \"@vercel/sandbox\";\nimport { z } from \"zod\";\nimport { isSafeGitRef, type GitHubTreeEntry } from \"./github-publication\";\nimport { repositorySlugSchema, type RepositorySlug } from \"./schema/repository\";\n\ntype TrustedGitHubApiSandbox = Pick<Sandbox, \"runCommand\" | \"stop\">;\n\n/** GitHub API actions supported by Vercel-managed Sandbox credentials. */\nexport type VercelManagedGitHubApiAction = \"github:issues:read\" | \"github:pull-request:create\";\n\n/** Exact creation request for one Vercel-managed GitHub API operation. */\nexport interface VercelManagedGitHubApiSandboxCreateInput {\n source?: {\n type: \"git\";\n url: string;\n };\n __source_credentials?: true;\n __commitAs?: {\n name: string;\n email: string;\n };\n __credentials: {\n github: [\n {\n repositories: [RepositorySlug];\n actions: [VercelManagedGitHubApiAction];\n },\n ];\n };\n networkPolicy: NetworkPolicy;\n persistent: false;\n resources: { vcpus: 6 };\n runtime: \"node24\";\n timeout: 60_000 | 180_000;\n}\n\n/** Configures fresh trusted Sandboxes for brokered GitHub API requests. */\nexport interface VercelManagedGitHubApiOptions {\n /** Attribution metadata supplied when managed publication creates a signed commit. */\n commitIdentity?: { name: string; email: string };\n createSandbox?: (\n input: VercelManagedGitHubApiSandboxCreateInput,\n ) => Promise<TrustedGitHubApiSandbox>;\n}\n\n/** Authorized request to read one exact GitHub issue. */\nexport interface VercelManagedGitHubIssueInput {\n repository: RepositorySlug;\n issueNumber: number;\n authorize(): Promise<void>;\n}\n\n/** Issue fields returned by a Vercel-managed GitHub API request. */\nexport interface VercelManagedGitHubIssue {\n title: string;\n body: string | null;\n url: string;\n pullRequest: boolean;\n}\n\n/** Authorized request to create one draft or ready GitHub pull request. */\nexport interface VercelManagedGitHubPullRequestInput {\n repository: RepositorySlug;\n title: string;\n body: string;\n head: string;\n base: string;\n draft: boolean;\n authorize(): Promise<void>;\n}\n\n/** Pull-request identity returned by a Vercel-managed GitHub API request. */\nexport interface VercelManagedGitHubPullRequest {\n pullRequestNumber: number;\n pullRequestUrl: string;\n draft: boolean;\n}\n\n/** Git Data and pull-request inputs for one idempotent managed publication. */\nexport interface VercelManagedGitHubPublicationInput {\n repository: RepositorySlug;\n baseCommit: string;\n tree: readonly GitHubTreeEntry[];\n commitMessage: string;\n branch: string;\n title: string;\n body: string;\n baseBranch: string;\n draft: boolean;\n authorize(): Promise<void>;\n}\n\n/** Commit and pull-request identities returned after managed publication. */\nexport interface VercelManagedGitHubPublication extends VercelManagedGitHubPullRequest {\n commitSha: string;\n}\n\n/** Repository-scoped brokered GitHub API operations supplied by Vercel Sandbox. */\nexport interface VercelManagedGitHubApi {\n readIssue(input: VercelManagedGitHubIssueInput): Promise<VercelManagedGitHubIssue>;\n createPullRequest(\n input: VercelManagedGitHubPullRequestInput,\n ): Promise<VercelManagedGitHubPullRequest>;\n publishPullRequest(\n input: VercelManagedGitHubPublicationInput,\n ): Promise<VercelManagedGitHubPublication>;\n}\n\n/** GitHub API failure returned through the managed credential Sandbox. */\nexport class VercelManagedGitHubApiError extends Error {\n constructor(\n readonly status: number,\n message: string,\n ) {\n super(`GitHub API request failed (${status}): ${message}`);\n this.name = \"VercelManagedGitHubApiError\";\n }\n}\n\nconst sandboxResponseSchema = z.object({\n status: z.number().int().min(100).max(599),\n body: z.unknown(),\n});\nconst issueSchema = z.object({\n title: z.string(),\n body: z.string().nullable(),\n html_url: z.url(),\n pull_request: z.unknown().optional(),\n});\nconst pullRequestSchema = z.object({\n number: z.number().int().positive(),\n html_url: z.url(),\n draft: z.boolean(),\n});\nconst shaSchema = z.string().regex(/^[a-f0-9]{40}$/u);\nconst commitSchema = z.object({\n sha: shaSchema,\n tree: z.object({ sha: shaSchema }),\n parents: z.array(z.object({ sha: shaSchema })),\n});\nconst treeSchema = z.object({ sha: shaSchema });\nconst refSchema = z.object({ object: z.object({ sha: shaSchema }) });\nconst publicationPullSchema = pullRequestSchema.extend({\n state: z.enum([\"open\", \"closed\"]),\n head: z.object({ ref: z.string(), sha: shaSchema }),\n});\nconst publicationPathSchema = z\n .string()\n .min(1)\n .max(1_024)\n .refine((path) => Buffer.byteLength(path, \"utf8\") <= 1_024, {\n message: \"Managed publication path exceeds byte limit\",\n })\n .refine(\n (path) =>\n /^[A-Za-z0-9.][A-Za-z0-9._/-]*$/u.test(path) &&\n !path.includes(\"..\") &&\n !path.includes(\"//\") &&\n !path.endsWith(\"/\") &&\n !path.split(\"/\").some((part) => part === \".\"),\n { message: \"Invalid managed publication path\" },\n );\nconst publicationBranchSchema = z\n .string()\n .min(1)\n .max(256)\n .refine(\n (branch) =>\n isSafeGitRef(branch) &&\n branch\n .split(\"/\")\n .every(\n (component) =>\n component.length > 0 &&\n !component.startsWith(\".\") &&\n !component.endsWith(\".\") &&\n !component.endsWith(\".lock\"),\n ),\n { message: \"Invalid managed publication branch\" },\n );\nconst publicationTextSchema = (maximum: number, field: string) =>\n z\n .string()\n .max(maximum)\n .refine((value) => Buffer.byteLength(value, \"utf8\") <= maximum, {\n message: `Managed publication ${field} exceeds byte limit`,\n });\nconst publicationCommitMessageSchema = publicationTextSchema(65_536, \"commit message\").min(1);\nconst publicationTitleSchema = publicationTextSchema(256, \"title\").min(1);\nconst publicationBodySchema = publicationTextSchema(65_536, \"body\");\nconst publicationBaseBranchSchema = z\n .string()\n .min(1)\n .max(256)\n .refine((branch) => isSafeGitRef(branch), {\n message: \"Invalid managed publication base branch\",\n });\nconst publicationContentSchema = z\n .string()\n .max(200_000)\n .refine((content) => Buffer.byteLength(content, \"utf8\") <= 200_000, {\n message: \"Managed publication content exceeds byte limit\",\n });\nconst publicationTreeSchema = z\n .array(\n z.union([\n z.strictObject({\n path: publicationPathSchema,\n mode: z.enum([\"100644\", \"100755\"]),\n type: z.literal(\"blob\"),\n content: publicationContentSchema,\n }),\n z.strictObject({\n path: publicationPathSchema,\n mode: z.enum([\"100644\", \"100755\"]),\n type: z.literal(\"blob\"),\n sha: z.null(),\n }),\n ]),\n )\n .min(1)\n .max(25)\n .refine(\n (entries) =>\n new TextEncoder().encode(JSON.stringify({ tree: entries })).byteLength <= 1_000_000,\n \"Managed publication tree exceeds transfer limit\",\n );\nconst githubRequestScript = `\nconst request = JSON.parse(process.argv[1]);\nconst response = await fetch(request.url, {\n method: request.method,\n headers: {\n accept: \"application/vnd.github+json\",\n \"content-type\": \"application/json\",\n \"x-github-api-version\": \"2022-11-28\",\n },\n body: request.body === undefined ? undefined : JSON.stringify(request.body),\n redirect: \"error\",\n});\nconst reader = response.body?.getReader();\nconst chunks = [];\nlet byteLength = 0;\nif (reader) {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n byteLength += value.byteLength;\n if (byteLength > 1_000_000) {\n await reader.cancel();\n throw new Error(\"GitHub API response exceeds transfer limit\");\n }\n chunks.push(value);\n }\n}\nconst bytes = new Uint8Array(byteLength);\nlet offset = 0;\nfor (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.byteLength; }\nconst text = new TextDecoder().decode(bytes);\nlet body = null;\ntry { body = text === \"\" ? null : JSON.parse(text); } catch { body = { message: text }; }\nprocess.stdout.write(JSON.stringify({ status: response.status, body }));\n`;\n\nasync function createManagedSandbox(\n input: VercelManagedGitHubApiSandboxCreateInput,\n): Promise<TrustedGitHubApiSandbox> {\n const { Sandbox } = await import(\"@vercel/sandbox\");\n return Sandbox.create({ ...input });\n}\n\nfunction createInput(\n repository: RepositorySlug,\n action: VercelManagedGitHubApiAction,\n): VercelManagedGitHubApiSandboxCreateInput {\n return {\n __credentials: { github: [{ repositories: [repository], actions: [action] }] },\n networkPolicy: { allow: [\"api.github.com\"] },\n persistent: false,\n resources: { vcpus: 6 },\n runtime: \"node24\",\n timeout: 60_000,\n };\n}\n\nfunction createPublicationInput(\n repository: RepositorySlug,\n commitIdentity: { name: string; email: string },\n): VercelManagedGitHubApiSandboxCreateInput {\n return {\n source: { type: \"git\", url: `https://github.com/${repository}.git` },\n __source_credentials: true,\n __commitAs: commitIdentity,\n __credentials: {\n github: [\n {\n repositories: [repository],\n actions: [\"github:pull-request:create\"],\n },\n ],\n },\n networkPolicy: { allow: [\"github.com\", \"api.github.com\"] },\n persistent: false,\n resources: { vcpus: 6 },\n runtime: \"node24\",\n timeout: 180_000,\n };\n}\n\nasync function request(\n createSandbox: (\n input: VercelManagedGitHubApiSandboxCreateInput,\n ) => Promise<TrustedGitHubApiSandbox>,\n input: {\n repository: RepositorySlug;\n action: VercelManagedGitHubApiAction;\n method: \"GET\" | \"POST\";\n path: string;\n body?: unknown;\n authorize(): Promise<void>;\n },\n): Promise<unknown> {\n const sandbox = await createSandbox(createInput(input.repository, input.action));\n try {\n await input.authorize();\n const result = await sandbox.runCommand({\n cmd: \"node\",\n args: [\n \"--input-type=module\",\n \"--eval\",\n githubRequestScript,\n JSON.stringify({\n method: input.method,\n url: `https://api.github.com/repos/${input.repository}${input.path}`,\n ...(input.body === undefined ? {} : { body: input.body }),\n }),\n ],\n timeoutMs: 30_000,\n });\n const stderr = (await result.stderr()).trim();\n if (result.exitCode !== 0) {\n throw new Error(`Managed GitHub API operation failed (${result.exitCode}): ${stderr}`);\n }\n const response = sandboxResponseSchema.parse(JSON.parse((await result.stdout()).trim()));\n if (response.status < 200 || response.status >= 300) {\n const message = z.object({ message: z.string() }).safeParse(response.body);\n throw new VercelManagedGitHubApiError(\n response.status,\n message.success ? message.data.message : \"Unexpected response\",\n );\n }\n return response.body;\n } finally {\n await sandbox.stop();\n }\n}\n\nasync function sandboxRequest(\n sandbox: TrustedGitHubApiSandbox,\n input: {\n repository: RepositorySlug;\n method: \"GET\" | \"POST\";\n path: string;\n body?: unknown;\n },\n): Promise<unknown> {\n const result = await sandbox.runCommand({\n cmd: \"node\",\n args: [\n \"--input-type=module\",\n \"--eval\",\n githubRequestScript,\n JSON.stringify({\n method: input.method,\n url: `https://api.github.com/repos/${input.repository}${input.path}`,\n ...(input.body === undefined ? {} : { body: input.body }),\n }),\n ],\n timeoutMs: 30_000,\n });\n const stderr = (await result.stderr()).trim();\n if (result.exitCode !== 0) {\n throw new Error(`Managed GitHub API operation failed (${result.exitCode}): ${stderr}`);\n }\n const response = sandboxResponseSchema.parse(JSON.parse((await result.stdout()).trim()));\n if (response.status < 200 || response.status >= 300) {\n const message = z.object({ message: z.string() }).safeParse(response.body);\n throw new VercelManagedGitHubApiError(\n response.status,\n message.success ? message.data.message : \"Unexpected response\",\n );\n }\n return response.body;\n}\n\n/** Create repository-scoped GitHub API operations backed by managed Sandbox credentials. */\nexport function createVercelManagedGitHubApi(\n options: VercelManagedGitHubApiOptions = {},\n): VercelManagedGitHubApi {\n const createSandbox = options.createSandbox ?? createManagedSandbox;\n return {\n async readIssue(input) {\n const repository = repositorySlugSchema.parse(input.repository.toLowerCase());\n if (!Number.isSafeInteger(input.issueNumber) || input.issueNumber < 1) {\n throw new Error(\"Invalid GitHub issue number\");\n }\n const issue = issueSchema.parse(\n await request(createSandbox, {\n repository,\n action: \"github:issues:read\",\n method: \"GET\",\n path: `/issues/${input.issueNumber}`,\n authorize: input.authorize,\n }),\n );\n return {\n title: issue.title,\n body: issue.body,\n url: issue.html_url,\n pullRequest: issue.pull_request !== undefined,\n };\n },\n async createPullRequest(input) {\n const repository = repositorySlugSchema.parse(input.repository.toLowerCase());\n const head = publicationBranchSchema.parse(input.head);\n const base = publicationBaseBranchSchema.parse(input.base);\n const title = publicationTitleSchema.parse(input.title);\n const body = publicationBodySchema.parse(input.body);\n const pull = pullRequestSchema.parse(\n await request(createSandbox, {\n repository,\n action: \"github:pull-request:create\",\n method: \"POST\",\n path: \"/pulls\",\n body: {\n title,\n body,\n head,\n base,\n draft: input.draft,\n },\n authorize: input.authorize,\n }),\n );\n return {\n pullRequestNumber: pull.number,\n pullRequestUrl: pull.html_url,\n draft: pull.draft,\n };\n },\n async publishPullRequest(input) {\n const repository = repositorySlugSchema.parse(input.repository.toLowerCase());\n if (!options.commitIdentity) throw new Error(\"Managed publication requires commit identity\");\n if (!/^[a-f0-9]{40}$/u.test(input.baseCommit) || input.commitMessage.trim() === \"\") {\n throw new Error(\"Invalid managed GitHub publication\");\n }\n const publicationTree = publicationTreeSchema.parse(input.tree);\n const branch = publicationBranchSchema.parse(input.branch);\n const baseBranch = publicationBaseBranchSchema.parse(input.baseBranch);\n const title = publicationTitleSchema.parse(input.title);\n const body = publicationBodySchema.parse(input.body);\n const commitMessage = publicationCommitMessageSchema.parse(\n `${input.commitMessage}\\n\\nCo-authored-by: ${options.commitIdentity.name} <${options.commitIdentity.email}>`,\n );\n const sandbox = await createSandbox(\n createPublicationInput(repository, options.commitIdentity),\n );\n try {\n const authorizedRequest = async (\n request: Omit<Parameters<typeof sandboxRequest>[1], \"repository\">,\n ) => {\n await input.authorize();\n return sandboxRequest(sandbox, { repository, ...request });\n };\n const base = commitSchema.parse(\n await authorizedRequest({\n method: \"GET\",\n path: `/git/commits/${input.baseCommit}`,\n }),\n );\n const tree = treeSchema.parse(\n await authorizedRequest({\n method: \"POST\",\n path: \"/git/trees\",\n body: { base_tree: base.tree.sha, tree: publicationTree },\n }),\n );\n const pullPath = `/pulls?${new URLSearchParams({\n state: \"all\",\n head: `${repository.split(\"/\")[0]}:${branch}`,\n per_page: \"100\",\n })}`;\n const pulls = async () =>\n z\n .array(publicationPullSchema)\n .parse(await authorizedRequest({ method: \"GET\", path: pullPath }));\n const result = (pull: z.infer<typeof pullRequestSchema>, commitSha: string) => ({\n commitSha,\n pullRequestNumber: pull.number,\n pullRequestUrl: pull.html_url,\n draft: pull.draft,\n });\n const refPath = `/git/ref/heads/${branch.split(\"/\").map(encodeURIComponent).join(\"/\")}`;\n let existingRef: z.infer<typeof refSchema> | null;\n try {\n existingRef = refSchema.parse(await authorizedRequest({ method: \"GET\", path: refPath }));\n } catch (error) {\n if (!(error instanceof VercelManagedGitHubApiError) || error.status !== 404) throw error;\n existingRef = null;\n }\n let commitSha = existingRef?.object.sha;\n if (commitSha) {\n const existingPull = (await pulls()).find((pull) => pull.head.ref === branch);\n if (existingPull) {\n if (existingPull.state !== \"open\") {\n throw new Error(\"Managed publication pull request is closed\");\n }\n const existingCommit = commitSchema.parse(\n await authorizedRequest({\n method: \"GET\",\n path: `/git/commits/${existingPull.head.sha}`,\n }),\n );\n if (\n existingCommit.tree.sha !== tree.sha ||\n existingCommit.parents.length !== 1 ||\n existingCommit.parents[0]?.sha !== input.baseCommit\n ) {\n throw new Error(\"Managed publication pull request contains different changes\");\n }\n return result(existingPull, existingPull.head.sha);\n }\n const existingCommit = commitSchema.parse(\n await authorizedRequest({\n method: \"GET\",\n path: `/git/commits/${commitSha}`,\n }),\n );\n if (\n existingCommit.tree.sha !== tree.sha ||\n existingCommit.parents.length !== 1 ||\n existingCommit.parents[0]?.sha !== input.baseCommit\n ) {\n throw new Error(\"Managed publication branch contains different changes\");\n }\n } else {\n const commit = commitSchema.parse(\n await authorizedRequest({\n method: \"POST\",\n path: \"/git/commits\",\n body: {\n message: commitMessage,\n tree: tree.sha,\n parents: [input.baseCommit],\n },\n }),\n );\n commitSha = commit.sha;\n try {\n await authorizedRequest({\n method: \"POST\",\n path: \"/git/refs\",\n body: { ref: `refs/heads/${branch}`, sha: commitSha },\n });\n } catch (error) {\n const reconciled = refSchema.parse(\n await authorizedRequest({ method: \"GET\", path: refPath }),\n );\n if (reconciled.object.sha !== commitSha) throw error;\n }\n }\n let pull: z.infer<typeof pullRequestSchema>;\n try {\n pull = pullRequestSchema.parse(\n await authorizedRequest({\n method: \"POST\",\n path: \"/pulls\",\n body: {\n title,\n body,\n head: branch,\n base: baseBranch,\n draft: input.draft,\n },\n }),\n );\n } catch (error) {\n const reconciled = (await pulls()).find((candidate) => candidate.head.ref === branch);\n if (!reconciled || reconciled.state !== \"open\" || reconciled.head.sha !== commitSha) {\n throw error;\n }\n pull = reconciled;\n }\n return result(pull, commitSha);\n } finally {\n await sandbox.stop();\n }\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vercel-github-api.mjs","names":[],"sources":["../src/vercel-github-api.ts"],"sourcesContent":["import type { Sandbox, NetworkPolicy } from \"@vercel/sandbox\";\nimport { z } from \"zod\";\nimport { isSafeGitRef, type GitHubTreeEntry } from \"./github-publication\";\nimport { repositorySlugSchema, type RepositorySlug } from \"./schema/repository\";\n\ntype TrustedGitHubApiSandbox = Pick<Sandbox, \"runCommand\" | \"stop\">;\n\n/** GitHub API actions supported by Vercel-managed Sandbox credentials. */\nexport type VercelManagedGitHubApiAction =\n | \"github:issues:read\"\n | \"github:pull-request:create\"\n | \"github:labels:write\";\n\n/** Exact creation request for one Vercel-managed GitHub API operation. */\nexport interface VercelManagedGitHubApiSandboxCreateInput {\n source?: {\n type: \"git\";\n url: string;\n };\n __source_credentials?: true;\n __commitAs?: {\n name: string;\n email: string;\n };\n __credentials: {\n github: [\n {\n repositories: [RepositorySlug];\n actions: [VercelManagedGitHubApiAction];\n },\n ];\n };\n networkPolicy: NetworkPolicy;\n persistent: false;\n resources: { vcpus: 6 };\n runtime: \"node24\";\n timeout: 60_000 | 180_000;\n}\n\n/** Configures fresh trusted Sandboxes for brokered GitHub API requests. */\nexport interface VercelManagedGitHubApiOptions {\n /** Attribution metadata supplied when managed publication creates a signed commit. */\n commitIdentity?: { name: string; email: string };\n createSandbox?: (\n input: VercelManagedGitHubApiSandboxCreateInput,\n ) => Promise<TrustedGitHubApiSandbox>;\n}\n\n/** Authorized request to read one exact GitHub issue. */\nexport interface VercelManagedGitHubIssueInput {\n repository: RepositorySlug;\n issueNumber: number;\n authorize(): Promise<void>;\n}\n\n/** Issue fields returned by a Vercel-managed GitHub API request. */\nexport interface VercelManagedGitHubIssue {\n title: string;\n body: string | null;\n url: string;\n pullRequest: boolean;\n}\n\n/** Authorized request to ensure and attach a repository flow label to a pull request. */\nexport interface VercelManagedGitHubLabelInput {\n repository: RepositorySlug;\n pullRequestNumber: number;\n name: string;\n color: string;\n description: string;\n authorize(): Promise<void>;\n}\n\n/** Authorized request to create one draft or ready GitHub pull request. */\nexport interface VercelManagedGitHubPullRequestInput {\n repository: RepositorySlug;\n title: string;\n body: string;\n head: string;\n base: string;\n draft: boolean;\n authorize(): Promise<void>;\n}\n\n/** Pull-request identity returned by a Vercel-managed GitHub API request. */\nexport interface VercelManagedGitHubPullRequest {\n pullRequestNumber: number;\n pullRequestUrl: string;\n draft: boolean;\n}\n\n/** Git Data and pull-request inputs for one idempotent managed publication. */\nexport interface VercelManagedGitHubPublicationInput {\n repository: RepositorySlug;\n baseCommit: string;\n tree: readonly GitHubTreeEntry[];\n commitMessage: string;\n branch: string;\n title: string;\n body: string;\n baseBranch: string;\n draft: boolean;\n authorize(): Promise<void>;\n}\n\n/** Commit and pull-request identities returned after managed publication. */\nexport interface VercelManagedGitHubPublication extends VercelManagedGitHubPullRequest {\n commitSha: string;\n}\n\n/** Repository-scoped brokered GitHub API operations supplied by Vercel Sandbox. */\nexport interface VercelManagedGitHubApi {\n readIssue(input: VercelManagedGitHubIssueInput): Promise<VercelManagedGitHubIssue>;\n labelPullRequest(input: VercelManagedGitHubLabelInput): Promise<void>;\n createPullRequest(\n input: VercelManagedGitHubPullRequestInput,\n ): Promise<VercelManagedGitHubPullRequest>;\n publishPullRequest(\n input: VercelManagedGitHubPublicationInput,\n ): Promise<VercelManagedGitHubPublication>;\n}\n\n/** GitHub API failure returned through the managed credential Sandbox. */\nexport class VercelManagedGitHubApiError extends Error {\n constructor(\n readonly status: number,\n message: string,\n ) {\n super(`GitHub API request failed (${status}): ${message}`);\n this.name = \"VercelManagedGitHubApiError\";\n }\n}\n\nconst sandboxResponseSchema = z.object({\n status: z.number().int().min(100).max(599),\n body: z.unknown(),\n});\nconst issueSchema = z.object({\n title: z.string(),\n body: z.string().nullable(),\n html_url: z.url(),\n pull_request: z.unknown().optional(),\n});\nconst labelSchema = z.object({ name: z.string() });\nconst labelInputSchema = z.object({\n name: z\n .string()\n .min(1)\n .max(50)\n .refine(\n (name) =>\n Buffer.byteLength(name, \"utf8\") <= 50 &&\n name.trim() === name &&\n [...name].every((character) => {\n const code = character.charCodeAt(0);\n return code > 31 && code !== 127 && character !== \"\\\\\";\n }) &&\n name.split(\"/\").every((part) => part !== \"\" && part !== \".\" && part !== \"..\"),\n ),\n color: z.string().regex(/^[a-fA-F0-9]{6}$/u),\n description: z\n .string()\n .max(100)\n .refine((description) => Buffer.byteLength(description, \"utf8\") <= 100),\n});\nconst pullRequestSchema = z.object({\n number: z.number().int().positive(),\n html_url: z.url(),\n draft: z.boolean(),\n});\nconst shaSchema = z.string().regex(/^[a-f0-9]{40}$/u);\nconst commitSchema = z.object({\n sha: shaSchema,\n tree: z.object({ sha: shaSchema }),\n parents: z.array(z.object({ sha: shaSchema })),\n});\nconst treeSchema = z.object({ sha: shaSchema });\nconst refSchema = z.object({ object: z.object({ sha: shaSchema }) });\nconst publicationPullSchema = pullRequestSchema.extend({\n state: z.enum([\"open\", \"closed\"]),\n head: z.object({ ref: z.string(), sha: shaSchema }),\n});\nconst publicationPathSchema = z\n .string()\n .min(1)\n .max(1_024)\n .refine((path) => Buffer.byteLength(path, \"utf8\") <= 1_024, {\n message: \"Managed publication path exceeds byte limit\",\n })\n .refine(\n (path) =>\n /^[A-Za-z0-9.][A-Za-z0-9._/-]*$/u.test(path) &&\n !path.includes(\"..\") &&\n !path.includes(\"//\") &&\n !path.endsWith(\"/\") &&\n !path.split(\"/\").some((part) => part === \".\"),\n { message: \"Invalid managed publication path\" },\n );\nconst publicationBranchSchema = z\n .string()\n .min(1)\n .max(256)\n .refine(\n (branch) =>\n isSafeGitRef(branch) &&\n branch\n .split(\"/\")\n .every(\n (component) =>\n component.length > 0 &&\n !component.startsWith(\".\") &&\n !component.endsWith(\".\") &&\n !component.endsWith(\".lock\"),\n ),\n { message: \"Invalid managed publication branch\" },\n );\nconst publicationTextSchema = (maximum: number, field: string) =>\n z\n .string()\n .max(maximum)\n .refine((value) => Buffer.byteLength(value, \"utf8\") <= maximum, {\n message: `Managed publication ${field} exceeds byte limit`,\n });\nconst publicationCommitMessageSchema = publicationTextSchema(65_536, \"commit message\").min(1);\nconst publicationTitleSchema = publicationTextSchema(256, \"title\").min(1);\nconst publicationBodySchema = publicationTextSchema(65_536, \"body\");\nconst publicationBaseBranchSchema = z\n .string()\n .min(1)\n .max(256)\n .refine((branch) => isSafeGitRef(branch), {\n message: \"Invalid managed publication base branch\",\n });\nconst publicationContentSchema = z\n .string()\n .max(200_000)\n .refine((content) => Buffer.byteLength(content, \"utf8\") <= 200_000, {\n message: \"Managed publication content exceeds byte limit\",\n });\nconst publicationTreeSchema = z\n .array(\n z.union([\n z.strictObject({\n path: publicationPathSchema,\n mode: z.enum([\"100644\", \"100755\"]),\n type: z.literal(\"blob\"),\n content: publicationContentSchema,\n }),\n z.strictObject({\n path: publicationPathSchema,\n mode: z.enum([\"100644\", \"100755\"]),\n type: z.literal(\"blob\"),\n sha: z.null(),\n }),\n ]),\n )\n .min(1)\n .max(25)\n .refine(\n (entries) =>\n new TextEncoder().encode(JSON.stringify({ tree: entries })).byteLength <= 1_000_000,\n \"Managed publication tree exceeds transfer limit\",\n );\nconst githubRequestScript = `\nconst request = JSON.parse(process.argv[1]);\nconst response = await fetch(request.url, {\n method: request.method,\n headers: {\n accept: \"application/vnd.github+json\",\n \"content-type\": \"application/json\",\n \"x-github-api-version\": \"2022-11-28\",\n },\n body: request.body === undefined ? undefined : JSON.stringify(request.body),\n redirect: \"error\",\n});\nconst reader = response.body?.getReader();\nconst chunks = [];\nlet byteLength = 0;\nif (reader) {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n byteLength += value.byteLength;\n if (byteLength > 1_000_000) {\n await reader.cancel();\n throw new Error(\"GitHub API response exceeds transfer limit\");\n }\n chunks.push(value);\n }\n}\nconst bytes = new Uint8Array(byteLength);\nlet offset = 0;\nfor (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.byteLength; }\nconst text = new TextDecoder().decode(bytes);\nlet body = null;\ntry { body = text === \"\" ? null : JSON.parse(text); } catch { body = { message: text }; }\nprocess.stdout.write(JSON.stringify({ status: response.status, body }));\n`;\n\nasync function createManagedSandbox(\n input: VercelManagedGitHubApiSandboxCreateInput,\n): Promise<TrustedGitHubApiSandbox> {\n const { Sandbox } = await import(\"@vercel/sandbox\");\n return Sandbox.create({ ...input });\n}\n\nfunction createInput(\n repository: RepositorySlug,\n action: VercelManagedGitHubApiAction,\n): VercelManagedGitHubApiSandboxCreateInput {\n return {\n __credentials: { github: [{ repositories: [repository], actions: [action] }] },\n networkPolicy: { allow: [\"api.github.com\"] },\n persistent: false,\n resources: { vcpus: 6 },\n runtime: \"node24\",\n timeout: 60_000,\n };\n}\n\nfunction createPublicationInput(\n repository: RepositorySlug,\n commitIdentity: { name: string; email: string },\n): VercelManagedGitHubApiSandboxCreateInput {\n return {\n source: { type: \"git\", url: `https://github.com/${repository}.git` },\n __source_credentials: true,\n __commitAs: commitIdentity,\n __credentials: {\n github: [\n {\n repositories: [repository],\n actions: [\"github:pull-request:create\"],\n },\n ],\n },\n networkPolicy: { allow: [\"github.com\", \"api.github.com\"] },\n persistent: false,\n resources: { vcpus: 6 },\n runtime: \"node24\",\n timeout: 180_000,\n };\n}\n\nasync function request(\n createSandbox: (\n input: VercelManagedGitHubApiSandboxCreateInput,\n ) => Promise<TrustedGitHubApiSandbox>,\n input: {\n repository: RepositorySlug;\n action: VercelManagedGitHubApiAction;\n method: \"GET\" | \"POST\";\n path: string;\n body?: unknown;\n authorize(): Promise<void>;\n },\n): Promise<unknown> {\n const sandbox = await createSandbox(createInput(input.repository, input.action));\n try {\n await input.authorize();\n const result = await sandbox.runCommand({\n cmd: \"node\",\n args: [\n \"--input-type=module\",\n \"--eval\",\n githubRequestScript,\n JSON.stringify({\n method: input.method,\n url: `https://api.github.com/repos/${input.repository}${input.path}`,\n ...(input.body === undefined ? {} : { body: input.body }),\n }),\n ],\n timeoutMs: 30_000,\n });\n const stderr = (await result.stderr()).trim();\n if (result.exitCode !== 0) {\n throw new Error(`Managed GitHub API operation failed (${result.exitCode}): ${stderr}`);\n }\n const response = sandboxResponseSchema.parse(JSON.parse((await result.stdout()).trim()));\n if (response.status < 200 || response.status >= 300) {\n const message = z.object({ message: z.string() }).safeParse(response.body);\n throw new VercelManagedGitHubApiError(\n response.status,\n message.success ? message.data.message : \"Unexpected response\",\n );\n }\n return response.body;\n } finally {\n await sandbox.stop();\n }\n}\n\nasync function sandboxRequest(\n sandbox: TrustedGitHubApiSandbox,\n input: {\n repository: RepositorySlug;\n method: \"GET\" | \"POST\";\n path: string;\n body?: unknown;\n },\n): Promise<unknown> {\n const result = await sandbox.runCommand({\n cmd: \"node\",\n args: [\n \"--input-type=module\",\n \"--eval\",\n githubRequestScript,\n JSON.stringify({\n method: input.method,\n url: `https://api.github.com/repos/${input.repository}${input.path}`,\n ...(input.body === undefined ? {} : { body: input.body }),\n }),\n ],\n timeoutMs: 30_000,\n });\n const stderr = (await result.stderr()).trim();\n if (result.exitCode !== 0) {\n throw new Error(`Managed GitHub API operation failed (${result.exitCode}): ${stderr}`);\n }\n const response = sandboxResponseSchema.parse(JSON.parse((await result.stdout()).trim()));\n if (response.status < 200 || response.status >= 300) {\n const message = z.object({ message: z.string() }).safeParse(response.body);\n throw new VercelManagedGitHubApiError(\n response.status,\n message.success ? message.data.message : \"Unexpected response\",\n );\n }\n return response.body;\n}\n\n/** Create repository-scoped GitHub API operations backed by managed Sandbox credentials. */\nexport function createVercelManagedGitHubApi(\n options: VercelManagedGitHubApiOptions = {},\n): VercelManagedGitHubApi {\n const createSandbox = options.createSandbox ?? createManagedSandbox;\n return {\n async readIssue(input) {\n const repository = repositorySlugSchema.parse(input.repository.toLowerCase());\n if (!Number.isSafeInteger(input.issueNumber) || input.issueNumber < 1) {\n throw new Error(\"Invalid GitHub issue number\");\n }\n const issue = issueSchema.parse(\n await request(createSandbox, {\n repository,\n action: \"github:issues:read\",\n method: \"GET\",\n path: `/issues/${input.issueNumber}`,\n authorize: input.authorize,\n }),\n );\n return {\n title: issue.title,\n body: issue.body,\n url: issue.html_url,\n pullRequest: issue.pull_request !== undefined,\n };\n },\n async labelPullRequest(input) {\n const repository = repositorySlugSchema.parse(input.repository.toLowerCase());\n if (!Number.isSafeInteger(input.pullRequestNumber) || input.pullRequestNumber < 1) {\n throw new Error(\"Invalid GitHub pull request number\");\n }\n const label = labelInputSchema.parse(input);\n const sandbox = await createSandbox(createInput(repository, \"github:labels:write\"));\n try {\n const authorizedRequest = async (\n request: Omit<Parameters<typeof sandboxRequest>[1], \"repository\">,\n ) => {\n await input.authorize();\n return sandboxRequest(sandbox, { repository, ...request });\n };\n const labelPath = `/labels/${encodeURIComponent(label.name)}`;\n const read = async () => {\n const existing = labelSchema.parse(\n await authorizedRequest({ method: \"GET\", path: labelPath }),\n );\n if (existing.name.toLowerCase() !== label.name.toLowerCase()) {\n throw new Error(\"GitHub returned a different repository label\");\n }\n };\n try {\n await read();\n } catch (error) {\n if (!(error instanceof VercelManagedGitHubApiError) || error.status !== 404) throw error;\n try {\n await authorizedRequest({ method: \"POST\", path: \"/labels\", body: label });\n } catch (creationError) {\n if (\n !(creationError instanceof VercelManagedGitHubApiError) ||\n creationError.status !== 422\n ) {\n throw creationError;\n }\n await read();\n }\n }\n await authorizedRequest({\n method: \"POST\",\n path: `/issues/${input.pullRequestNumber}/labels`,\n body: { labels: [label.name] },\n });\n } finally {\n await sandbox.stop();\n }\n },\n async createPullRequest(input) {\n const repository = repositorySlugSchema.parse(input.repository.toLowerCase());\n const head = publicationBranchSchema.parse(input.head);\n const base = publicationBaseBranchSchema.parse(input.base);\n const title = publicationTitleSchema.parse(input.title);\n const body = publicationBodySchema.parse(input.body);\n const pull = pullRequestSchema.parse(\n await request(createSandbox, {\n repository,\n action: \"github:pull-request:create\",\n method: \"POST\",\n path: \"/pulls\",\n body: {\n title,\n body,\n head,\n base,\n draft: input.draft,\n },\n authorize: input.authorize,\n }),\n );\n return {\n pullRequestNumber: pull.number,\n pullRequestUrl: pull.html_url,\n draft: pull.draft,\n };\n },\n async publishPullRequest(input) {\n const repository = repositorySlugSchema.parse(input.repository.toLowerCase());\n if (!options.commitIdentity) throw new Error(\"Managed publication requires commit identity\");\n if (!/^[a-f0-9]{40}$/u.test(input.baseCommit) || input.commitMessage.trim() === \"\") {\n throw new Error(\"Invalid managed GitHub publication\");\n }\n const publicationTree = publicationTreeSchema.parse(input.tree);\n const branch = publicationBranchSchema.parse(input.branch);\n const baseBranch = publicationBaseBranchSchema.parse(input.baseBranch);\n const title = publicationTitleSchema.parse(input.title);\n const body = publicationBodySchema.parse(input.body);\n const commitMessage = publicationCommitMessageSchema.parse(\n `${input.commitMessage}\\n\\nCo-authored-by: ${options.commitIdentity.name} <${options.commitIdentity.email}>`,\n );\n const sandbox = await createSandbox(\n createPublicationInput(repository, options.commitIdentity),\n );\n try {\n const authorizedRequest = async (\n request: Omit<Parameters<typeof sandboxRequest>[1], \"repository\">,\n ) => {\n await input.authorize();\n return sandboxRequest(sandbox, { repository, ...request });\n };\n const base = commitSchema.parse(\n await authorizedRequest({\n method: \"GET\",\n path: `/git/commits/${input.baseCommit}`,\n }),\n );\n const tree = treeSchema.parse(\n await authorizedRequest({\n method: \"POST\",\n path: \"/git/trees\",\n body: { base_tree: base.tree.sha, tree: publicationTree },\n }),\n );\n const pullPath = `/pulls?${new URLSearchParams({\n state: \"all\",\n head: `${repository.split(\"/\")[0]}:${branch}`,\n per_page: \"100\",\n })}`;\n const pulls = async () =>\n z\n .array(publicationPullSchema)\n .parse(await authorizedRequest({ method: \"GET\", path: pullPath }));\n const result = (pull: z.infer<typeof pullRequestSchema>, commitSha: string) => ({\n commitSha,\n pullRequestNumber: pull.number,\n pullRequestUrl: pull.html_url,\n draft: pull.draft,\n });\n const refPath = `/git/ref/heads/${branch.split(\"/\").map(encodeURIComponent).join(\"/\")}`;\n let existingRef: z.infer<typeof refSchema> | null;\n try {\n existingRef = refSchema.parse(await authorizedRequest({ method: \"GET\", path: refPath }));\n } catch (error) {\n if (!(error instanceof VercelManagedGitHubApiError) || error.status !== 404) throw error;\n existingRef = null;\n }\n let commitSha = existingRef?.object.sha;\n if (commitSha) {\n const existingPull = (await pulls()).find((pull) => pull.head.ref === branch);\n if (existingPull) {\n if (existingPull.state !== \"open\") {\n throw new Error(\"Managed publication pull request is closed\");\n }\n const existingCommit = commitSchema.parse(\n await authorizedRequest({\n method: \"GET\",\n path: `/git/commits/${existingPull.head.sha}`,\n }),\n );\n if (\n existingCommit.tree.sha !== tree.sha ||\n existingCommit.parents.length !== 1 ||\n existingCommit.parents[0]?.sha !== input.baseCommit\n ) {\n throw new Error(\"Managed publication pull request contains different changes\");\n }\n return result(existingPull, existingPull.head.sha);\n }\n const existingCommit = commitSchema.parse(\n await authorizedRequest({\n method: \"GET\",\n path: `/git/commits/${commitSha}`,\n }),\n );\n if (\n existingCommit.tree.sha !== tree.sha ||\n existingCommit.parents.length !== 1 ||\n existingCommit.parents[0]?.sha !== input.baseCommit\n ) {\n throw new Error(\"Managed publication branch contains different changes\");\n }\n } else {\n const commit = commitSchema.parse(\n await authorizedRequest({\n method: \"POST\",\n path: \"/git/commits\",\n body: {\n message: commitMessage,\n tree: tree.sha,\n parents: [input.baseCommit],\n },\n }),\n );\n commitSha = commit.sha;\n try {\n await authorizedRequest({\n method: \"POST\",\n path: \"/git/refs\",\n body: { ref: `refs/heads/${branch}`, sha: commitSha },\n });\n } catch (error) {\n const reconciled = refSchema.parse(\n await authorizedRequest({ method: \"GET\", path: refPath }),\n );\n if (reconciled.object.sha !== commitSha) throw error;\n }\n }\n let pull: z.infer<typeof pullRequestSchema>;\n try {\n pull = pullRequestSchema.parse(\n await authorizedRequest({\n method: \"POST\",\n path: \"/pulls\",\n body: {\n title,\n body,\n head: branch,\n base: baseBranch,\n draft: input.draft,\n },\n }),\n );\n } catch (error) {\n const reconciled = (await pulls()).find((candidate) => candidate.head.ref === branch);\n if (!reconciled || reconciled.state !== \"open\" || reconciled.head.sha !== commitSha) {\n throw error;\n }\n pull = reconciled;\n }\n return result(pull, commitSha);\n } finally {\n await sandbox.stop();\n }\n },\n };\n}\n"],"mappings":"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"}
|
package/dist/vercel.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { WorkNotification, WorkNotifications } from "./store/work-notifications.mjs";
|
|
2
2
|
import { RepositorySlug } from "./schema/repository.mjs";
|
|
3
|
-
import { VercelManagedGitHubApi, VercelManagedGitHubApiAction, VercelManagedGitHubApiError, VercelManagedGitHubApiOptions, VercelManagedGitHubApiSandboxCreateInput, VercelManagedGitHubIssue, VercelManagedGitHubIssueInput, VercelManagedGitHubPublication, VercelManagedGitHubPublicationInput, VercelManagedGitHubPullRequest, VercelManagedGitHubPullRequestInput, createVercelManagedGitHubApi } from "./vercel-github-api.mjs";
|
|
3
|
+
import { VercelManagedGitHubApi, VercelManagedGitHubApiAction, VercelManagedGitHubApiError, VercelManagedGitHubApiOptions, VercelManagedGitHubApiSandboxCreateInput, VercelManagedGitHubIssue, VercelManagedGitHubIssueInput, VercelManagedGitHubLabelInput, VercelManagedGitHubPublication, VercelManagedGitHubPublicationInput, VercelManagedGitHubPullRequest, VercelManagedGitHubPullRequestInput, createVercelManagedGitHubApi } from "./vercel-github-api.mjs";
|
|
4
4
|
import { VercelGitHubGitTransportOptions, VercelGitHubTokenInput, VercelManagedGitHubGitTransportOptions, VercelManagedGitHubSandboxCreateInput, createVercelGitHubGitTransport, createVercelManagedGitHubGitTransport } from "./vercel-git.mjs";
|
|
5
5
|
//#region src/vercel.d.ts
|
|
6
6
|
/** Provider delivery settings applied to one Vercel Queue message. */
|
|
@@ -60,5 +60,5 @@ declare function normalizeVercelProductionDeployment(payload: unknown): VercelPr
|
|
|
60
60
|
/** Verify a personal Vercel API token against Vercel; never infer a person from workload OIDC. */
|
|
61
61
|
declare function authenticateVercelUserToken(request: Request, fetcher?: typeof fetch): Promise<string | null>;
|
|
62
62
|
//#endregion
|
|
63
|
-
export { type VercelGitHubGitTransportOptions, type VercelGitHubTokenInput, type VercelManagedGitHubApi, type VercelManagedGitHubApiAction, VercelManagedGitHubApiError, type VercelManagedGitHubApiOptions, type VercelManagedGitHubApiSandboxCreateInput, type VercelManagedGitHubGitTransportOptions, type VercelManagedGitHubIssue, type VercelManagedGitHubIssueInput, type VercelManagedGitHubPublication, type VercelManagedGitHubPublicationInput, type VercelManagedGitHubPullRequest, type VercelManagedGitHubPullRequestInput, type VercelManagedGitHubSandboxCreateInput, VercelProductionDeployment, VercelQueueSend, VercelQueueSendOptions, VercelWorkQueue, VercelWorkQueueOptions, authenticateVercelUserToken, createVercelGitHubGitTransport, createVercelManagedGitHubApi, createVercelManagedGitHubGitTransport, createVercelWorkQueue, normalizeVercelProductionDeployment, verifyVercelSignature };
|
|
63
|
+
export { type VercelGitHubGitTransportOptions, type VercelGitHubTokenInput, type VercelManagedGitHubApi, type VercelManagedGitHubApiAction, VercelManagedGitHubApiError, type VercelManagedGitHubApiOptions, type VercelManagedGitHubApiSandboxCreateInput, type VercelManagedGitHubGitTransportOptions, type VercelManagedGitHubIssue, type VercelManagedGitHubIssueInput, type VercelManagedGitHubLabelInput, type VercelManagedGitHubPublication, type VercelManagedGitHubPublicationInput, type VercelManagedGitHubPullRequest, type VercelManagedGitHubPullRequestInput, type VercelManagedGitHubSandboxCreateInput, VercelProductionDeployment, VercelQueueSend, VercelQueueSendOptions, VercelWorkQueue, VercelWorkQueueOptions, authenticateVercelUserToken, createVercelGitHubGitTransport, createVercelManagedGitHubApi, createVercelManagedGitHubGitTransport, createVercelWorkQueue, normalizeVercelProductionDeployment, verifyVercelSignature };
|
|
64
64
|
//# sourceMappingURL=vercel.d.mts.map
|
package/dist/vercel.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vercel.mjs","names":[],"sources":["../src/vercel.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { z } from \"zod\";\nimport { repositorySlugSchema, type RepositorySlug } from \"./schema/repository\";\nimport {\n workNotificationSchema,\n type WorkNotification,\n type WorkNotifications,\n} from \"./store/work-notifications\";\n\n/** Provider delivery settings applied to one Vercel Queue message. */\nexport interface VercelQueueSendOptions {\n region: string;\n retentionSeconds: number;\n idempotencyKey: string;\n delaySeconds: number;\n}\n\n/** Injectable Vercel Queue send operation for one validated Factory work notification. */\nexport type VercelQueueSend = (\n topic: string,\n message: WorkNotification,\n options: VercelQueueSendOptions,\n) => Promise<unknown>;\n\n/** Configures Vercel Queue transport, failure reporting, retention, and runtime guards. */\nexport interface VercelWorkQueueOptions {\n send: VercelQueueSend;\n onError(error: unknown, event: WorkNotification): void;\n topic?: string;\n region?: string;\n retentionSeconds?: number;\n environment?: Readonly<Record<string, string | undefined>>;\n}\n\n/** Publishes graph wakeups and exposes the hosted-runtime notification adapter. */\nexport interface VercelWorkQueue {\n publish(event: WorkNotification, delaySeconds?: number): Promise<void>;\n hostedNotifications(): WorkNotifications | undefined;\n}\n\nexport {\n createVercelManagedGitHubApi,\n VercelManagedGitHubApiError,\n type VercelManagedGitHubApi,\n type VercelManagedGitHubApiAction,\n type VercelManagedGitHubApiOptions,\n type VercelManagedGitHubApiSandboxCreateInput,\n type VercelManagedGitHubIssue,\n type VercelManagedGitHubIssueInput,\n type VercelManagedGitHubPullRequest,\n type VercelManagedGitHubPullRequestInput,\n type VercelManagedGitHubPublication,\n type VercelManagedGitHubPublicationInput,\n} from \"./vercel-github-api\";\n\nexport {\n createVercelGitHubGitTransport,\n createVercelManagedGitHubGitTransport,\n type VercelGitHubGitTransportOptions,\n type VercelGitHubTokenInput,\n type VercelManagedGitHubGitTransportOptions,\n type VercelManagedGitHubSandboxCreateInput,\n} from \"./vercel-git\";\n\n/** Configure graph wakeups over Vercel Queues without granting queue messages execution authority. */\nexport function createVercelWorkQueue(options: VercelWorkQueueOptions): VercelWorkQueue {\n const environment = options.environment ?? process.env;\n const topic = options.topic ?? \"factory-work\";\n const region = options.region ?? \"iad1\";\n const retentionSeconds = options.retentionSeconds ?? 604_800;\n const hosted = () => environment.VERCEL === \"1\" && environment.FACTORY_LOCAL_TEST !== \"1\";\n const publish = async (event: WorkNotification, delaySeconds = 0): Promise<void> => {\n if (!hosted()) throw new Error(\"Vercel work queues require a hosted, non-fixture runtime\");\n const message = workNotificationSchema.parse(event);\n await options.send(topic, message, {\n region,\n retentionSeconds,\n idempotencyKey: message.key,\n delaySeconds,\n });\n };\n const notifications: WorkNotifications = { publish, onError: options.onError };\n return Object.freeze({\n publish,\n hostedNotifications: () => (hosted() ? notifications : undefined),\n });\n}\n\n/**\n * Verify a Vercel webhook signature (the x-vercel-signature header): an\n * HMAC-SHA1 hex digest of the raw request body. Returns false for missing or\n * malformed signatures instead of throwing.\n */\nexport function verifyVercelSignature(options: {\n secret: string;\n payload: string;\n signature: string | null;\n}): boolean {\n const { secret, payload, signature } = options;\n if (signature === null || !/^[0-9a-f]{40}$/.test(signature)) {\n return false;\n }\n const expected = createHmac(\"sha1\", secret).update(payload).digest(\"hex\");\n return timingSafeEqual(Buffer.from(signature, \"hex\"), Buffer.from(expected, \"hex\"));\n}\n\nconst observedDeploymentEvents = new Set([\"deployment.succeeded\", \"deployment.promoted\"]);\n\nconst deploymentEventSchema = z.looseObject({\n id: z.string().min(1),\n type: z.string(),\n payload: z.looseObject({\n target: z.string().nullable().optional(),\n project: z.looseObject({ id: z.string().min(1) }),\n deployment: z.looseObject({\n id: z.string().min(1),\n url: z.string().min(1),\n meta: z\n .looseObject({\n githubCommitSha: z.string().optional(),\n githubCommitOrg: z.string().optional(),\n githubCommitRepo: z.string().optional(),\n githubOrg: z.string().optional(),\n githubRepo: z.string().optional(),\n })\n .optional(),\n }),\n }),\n});\n\n/** A normalized production deployment observation with optional Git commit identity. */\nexport interface VercelProductionDeployment {\n source: \"vercel_production_deployment\";\n deliveryId: string;\n eventType: string;\n deploymentId: string;\n deploymentUrl: string;\n projectId: string;\n /** owner/name from the deployment's git metadata; null for non-git deployments. */\n repositorySlug: RepositorySlug | null;\n /** Commit the deployment was built from; null for non-git deployments. */\n commitSha: string | null;\n}\n\n/**\n * Normalize a deployment.succeeded or deployment.promoted webhook for a\n * production deployment so the factory can observe a merge reaching\n * production. Returns null for other events and non-production targets.\n */\nexport function normalizeVercelProductionDeployment(\n payload: unknown,\n): VercelProductionDeployment | null {\n const parsed = deploymentEventSchema.safeParse(payload);\n if (!parsed.success || !observedDeploymentEvents.has(parsed.data.type)) {\n return null;\n }\n const event = parsed.data.payload;\n if (event.target !== \"production\") {\n return null;\n }\n const meta = event.deployment.meta;\n const org = meta?.githubCommitOrg ?? meta?.githubOrg;\n const repo = meta?.githubCommitRepo ?? meta?.githubRepo;\n const repositorySlug =\n org === undefined || repo === undefined\n ? null\n : (repositorySlugSchema.safeParse(`${org}/${repo}`.toLowerCase()).data ?? null);\n return {\n source: \"vercel_production_deployment\",\n deliveryId: parsed.data.id,\n eventType: parsed.data.type,\n deploymentId: event.deployment.id,\n deploymentUrl: event.deployment.url,\n projectId: event.project.id,\n repositorySlug,\n commitSha: meta?.githubCommitSha ?? null,\n };\n}\n\n/** Verify a personal Vercel API token against Vercel; never infer a person from workload OIDC. */\nexport async function authenticateVercelUserToken(\n request: Request,\n fetcher: typeof fetch = fetch,\n): Promise<string | null> {\n const authorization = request.headers.get(\"authorization\");\n if (!authorization?.match(/^Bearer \\S+$/u)) return null;\n const response = await fetcher(\"https://api.vercel.com/v2/user\", {\n headers: { authorization },\n cache: \"no-store\",\n redirect: \"error\",\n signal: AbortSignal.timeout(10_000),\n });\n if (response.status === 401 || response.status === 403) return null;\n if (!response.ok) throw new Error(`Vercel identity lookup failed (${response.status})`);\n const result = z\n .object({ user: z.object({ id: z.string().min(1) }) })\n .safeParse(await response.json());\n return result.success ? `vercel:${result.data.user.id}` : null;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vercel.mjs","names":[],"sources":["../src/vercel.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { z } from \"zod\";\nimport { repositorySlugSchema, type RepositorySlug } from \"./schema/repository\";\nimport {\n workNotificationSchema,\n type WorkNotification,\n type WorkNotifications,\n} from \"./store/work-notifications\";\n\n/** Provider delivery settings applied to one Vercel Queue message. */\nexport interface VercelQueueSendOptions {\n region: string;\n retentionSeconds: number;\n idempotencyKey: string;\n delaySeconds: number;\n}\n\n/** Injectable Vercel Queue send operation for one validated Factory work notification. */\nexport type VercelQueueSend = (\n topic: string,\n message: WorkNotification,\n options: VercelQueueSendOptions,\n) => Promise<unknown>;\n\n/** Configures Vercel Queue transport, failure reporting, retention, and runtime guards. */\nexport interface VercelWorkQueueOptions {\n send: VercelQueueSend;\n onError(error: unknown, event: WorkNotification): void;\n topic?: string;\n region?: string;\n retentionSeconds?: number;\n environment?: Readonly<Record<string, string | undefined>>;\n}\n\n/** Publishes graph wakeups and exposes the hosted-runtime notification adapter. */\nexport interface VercelWorkQueue {\n publish(event: WorkNotification, delaySeconds?: number): Promise<void>;\n hostedNotifications(): WorkNotifications | undefined;\n}\n\nexport {\n createVercelManagedGitHubApi,\n VercelManagedGitHubApiError,\n type VercelManagedGitHubApi,\n type VercelManagedGitHubApiAction,\n type VercelManagedGitHubApiOptions,\n type VercelManagedGitHubApiSandboxCreateInput,\n type VercelManagedGitHubIssue,\n type VercelManagedGitHubIssueInput,\n type VercelManagedGitHubLabelInput,\n type VercelManagedGitHubPullRequest,\n type VercelManagedGitHubPullRequestInput,\n type VercelManagedGitHubPublication,\n type VercelManagedGitHubPublicationInput,\n} from \"./vercel-github-api\";\n\nexport {\n createVercelGitHubGitTransport,\n createVercelManagedGitHubGitTransport,\n type VercelGitHubGitTransportOptions,\n type VercelGitHubTokenInput,\n type VercelManagedGitHubGitTransportOptions,\n type VercelManagedGitHubSandboxCreateInput,\n} from \"./vercel-git\";\n\n/** Configure graph wakeups over Vercel Queues without granting queue messages execution authority. */\nexport function createVercelWorkQueue(options: VercelWorkQueueOptions): VercelWorkQueue {\n const environment = options.environment ?? process.env;\n const topic = options.topic ?? \"factory-work\";\n const region = options.region ?? \"iad1\";\n const retentionSeconds = options.retentionSeconds ?? 604_800;\n const hosted = () => environment.VERCEL === \"1\" && environment.FACTORY_LOCAL_TEST !== \"1\";\n const publish = async (event: WorkNotification, delaySeconds = 0): Promise<void> => {\n if (!hosted()) throw new Error(\"Vercel work queues require a hosted, non-fixture runtime\");\n const message = workNotificationSchema.parse(event);\n await options.send(topic, message, {\n region,\n retentionSeconds,\n idempotencyKey: message.key,\n delaySeconds,\n });\n };\n const notifications: WorkNotifications = { publish, onError: options.onError };\n return Object.freeze({\n publish,\n hostedNotifications: () => (hosted() ? notifications : undefined),\n });\n}\n\n/**\n * Verify a Vercel webhook signature (the x-vercel-signature header): an\n * HMAC-SHA1 hex digest of the raw request body. Returns false for missing or\n * malformed signatures instead of throwing.\n */\nexport function verifyVercelSignature(options: {\n secret: string;\n payload: string;\n signature: string | null;\n}): boolean {\n const { secret, payload, signature } = options;\n if (signature === null || !/^[0-9a-f]{40}$/.test(signature)) {\n return false;\n }\n const expected = createHmac(\"sha1\", secret).update(payload).digest(\"hex\");\n return timingSafeEqual(Buffer.from(signature, \"hex\"), Buffer.from(expected, \"hex\"));\n}\n\nconst observedDeploymentEvents = new Set([\"deployment.succeeded\", \"deployment.promoted\"]);\n\nconst deploymentEventSchema = z.looseObject({\n id: z.string().min(1),\n type: z.string(),\n payload: z.looseObject({\n target: z.string().nullable().optional(),\n project: z.looseObject({ id: z.string().min(1) }),\n deployment: z.looseObject({\n id: z.string().min(1),\n url: z.string().min(1),\n meta: z\n .looseObject({\n githubCommitSha: z.string().optional(),\n githubCommitOrg: z.string().optional(),\n githubCommitRepo: z.string().optional(),\n githubOrg: z.string().optional(),\n githubRepo: z.string().optional(),\n })\n .optional(),\n }),\n }),\n});\n\n/** A normalized production deployment observation with optional Git commit identity. */\nexport interface VercelProductionDeployment {\n source: \"vercel_production_deployment\";\n deliveryId: string;\n eventType: string;\n deploymentId: string;\n deploymentUrl: string;\n projectId: string;\n /** owner/name from the deployment's git metadata; null for non-git deployments. */\n repositorySlug: RepositorySlug | null;\n /** Commit the deployment was built from; null for non-git deployments. */\n commitSha: string | null;\n}\n\n/**\n * Normalize a deployment.succeeded or deployment.promoted webhook for a\n * production deployment so the factory can observe a merge reaching\n * production. Returns null for other events and non-production targets.\n */\nexport function normalizeVercelProductionDeployment(\n payload: unknown,\n): VercelProductionDeployment | null {\n const parsed = deploymentEventSchema.safeParse(payload);\n if (!parsed.success || !observedDeploymentEvents.has(parsed.data.type)) {\n return null;\n }\n const event = parsed.data.payload;\n if (event.target !== \"production\") {\n return null;\n }\n const meta = event.deployment.meta;\n const org = meta?.githubCommitOrg ?? meta?.githubOrg;\n const repo = meta?.githubCommitRepo ?? meta?.githubRepo;\n const repositorySlug =\n org === undefined || repo === undefined\n ? null\n : (repositorySlugSchema.safeParse(`${org}/${repo}`.toLowerCase()).data ?? null);\n return {\n source: \"vercel_production_deployment\",\n deliveryId: parsed.data.id,\n eventType: parsed.data.type,\n deploymentId: event.deployment.id,\n deploymentUrl: event.deployment.url,\n projectId: event.project.id,\n repositorySlug,\n commitSha: meta?.githubCommitSha ?? null,\n };\n}\n\n/** Verify a personal Vercel API token against Vercel; never infer a person from workload OIDC. */\nexport async function authenticateVercelUserToken(\n request: Request,\n fetcher: typeof fetch = fetch,\n): Promise<string | null> {\n const authorization = request.headers.get(\"authorization\");\n if (!authorization?.match(/^Bearer \\S+$/u)) return null;\n const response = await fetcher(\"https://api.vercel.com/v2/user\", {\n headers: { authorization },\n cache: \"no-store\",\n redirect: \"error\",\n signal: AbortSignal.timeout(10_000),\n });\n if (response.status === 401 || response.status === 403) return null;\n if (!response.ok) throw new Error(`Vercel identity lookup failed (${response.status})`);\n const result = z\n .object({ user: z.object({ id: z.string().min(1) }) })\n .safeParse(await response.json());\n return result.success ? `vercel:${result.data.user.id}` : null;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/factory",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.20",
|
|
4
4
|
"description": "Schema, state machines, and kernel primitives for Agent Factory.",
|
|
5
5
|
"homepage": "https://github.com/vercel-labs/agent-factory/tree/main/packages/factory",
|
|
6
6
|
"bugs": "https://github.com/vercel-labs/agent-factory/issues",
|