@vercel/factory 0.0.3 → 0.0.5

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.
Files changed (73) hide show
  1. package/dist/approval-h5aj8HYN.d.mts +510 -0
  2. package/dist/blob/index.d.mts +32 -0
  3. package/dist/blob/index.mjs +141 -0
  4. package/dist/blob/index.mjs.map +1 -0
  5. package/dist/connectors/index.d.mts +2 -0
  6. package/dist/connectors/index.mjs +49 -0
  7. package/dist/connectors/index.mjs.map +1 -0
  8. package/dist/connectors/slack.d.mts +43 -0
  9. package/dist/connectors/slack.mjs +50 -0
  10. package/dist/connectors/slack.mjs.map +1 -0
  11. package/dist/driver-Bbd25rNM.mjs +23 -0
  12. package/dist/driver-Bbd25rNM.mjs.map +1 -0
  13. package/dist/driver-CrZsy7sm.d.mts +34 -0
  14. package/dist/eve/index.d.mts +1101 -0
  15. package/dist/eve/index.mjs +853 -0
  16. package/dist/eve/index.mjs.map +1 -0
  17. package/dist/github-3sbHe-fo.d.mts +149 -0
  18. package/dist/github.d.mts +2 -0
  19. package/dist/github.mjs +332 -0
  20. package/dist/github.mjs.map +1 -0
  21. package/dist/id-DwCihQr4.mjs +31 -0
  22. package/dist/id-DwCihQr4.mjs.map +1 -0
  23. package/dist/index-WNGZJRRH.d.mts +67 -0
  24. package/dist/index.d.mts +187 -0
  25. package/dist/index.mjs +907 -0
  26. package/dist/index.mjs.map +1 -0
  27. package/dist/next-page.d.mts +5 -0
  28. package/dist/next-page.mjs +71 -0
  29. package/dist/next-page.mjs.map +1 -0
  30. package/dist/next.d.mts +12 -0
  31. package/dist/{next.js → next.mjs} +13 -14
  32. package/dist/next.mjs.map +1 -0
  33. package/dist/postgres/index.d.mts +137 -0
  34. package/dist/postgres/index.mjs +96 -0
  35. package/dist/postgres/index.mjs.map +1 -0
  36. package/dist/signal-CHVEmzvI.d.mts +59 -0
  37. package/dist/task-Cj7Ei9Nh.mjs +336 -0
  38. package/dist/task-Cj7Ei9Nh.mjs.map +1 -0
  39. package/package.json +29 -22
  40. package/dist/approval-Z7iaWibT.d.ts +0 -541
  41. package/dist/blob/index.d.ts +0 -26
  42. package/dist/blob/index.js +0 -127
  43. package/dist/blob/index.js.map +0 -1
  44. package/dist/chunk-2JFES6BA.js +0 -41
  45. package/dist/chunk-2JFES6BA.js.map +0 -1
  46. package/dist/chunk-CEF6VJZC.js +0 -28
  47. package/dist/chunk-CEF6VJZC.js.map +0 -1
  48. package/dist/chunk-D2XMABG4.js +0 -311
  49. package/dist/chunk-D2XMABG4.js.map +0 -1
  50. package/dist/chunk-EFL4PDDE.js +0 -325
  51. package/dist/chunk-EFL4PDDE.js.map +0 -1
  52. package/dist/common-CaHaEOAS.d.ts +0 -18
  53. package/dist/connectors/index.d.ts +0 -53
  54. package/dist/connectors/index.js +0 -64
  55. package/dist/connectors/index.js.map +0 -1
  56. package/dist/driver-tRNggCCJ.d.ts +0 -32
  57. package/dist/eve/index.d.ts +0 -1053
  58. package/dist/eve/index.js +0 -752
  59. package/dist/eve/index.js.map +0 -1
  60. package/dist/github.d.ts +0 -139
  61. package/dist/github.js +0 -27
  62. package/dist/github.js.map +0 -1
  63. package/dist/index.d.ts +0 -164
  64. package/dist/index.js +0 -940
  65. package/dist/index.js.map +0 -1
  66. package/dist/next-page.d.ts +0 -5
  67. package/dist/next-page.js +0 -75
  68. package/dist/next-page.js.map +0 -1
  69. package/dist/next.d.ts +0 -12
  70. package/dist/next.js.map +0 -1
  71. package/dist/postgres/index.d.ts +0 -137
  72. package/dist/postgres/index.js +0 -118
  73. package/dist/postgres/index.js.map +0 -1
@@ -0,0 +1,332 @@
1
+ import { z } from "zod";
2
+ import { createHmac, timingSafeEqual } from "node:crypto";
3
+ //#region src/github.ts
4
+ const gitHubApiUrl = "https://api.github.com";
5
+ const repositorySlugPattern = /^[^/\s]+\/[^/\s]+$/;
6
+ const pullRequestSchema = z.object({
7
+ number: z.number().int().positive(),
8
+ html_url: z.url()
9
+ });
10
+ const pullRequestHeadSchema = z.object({ head: z.object({
11
+ ref: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._/-]*$/).refine((value) => !value.includes("..")),
12
+ sha: z.string().regex(/^[0-9a-f]{40}$/),
13
+ repo: z.object({ full_name: z.string().min(1) })
14
+ }) });
15
+ const reviewCommentSchema = z.object({
16
+ id: z.number().int().positive(),
17
+ body: z.string(),
18
+ html_url: z.url(),
19
+ user: z.object({ login: z.string().min(1) }),
20
+ in_reply_to_id: z.number().int().positive().nullish()
21
+ });
22
+ const labelsSchema = z.array(z.object({ name: z.string().min(1) }));
23
+ const pullRequestCandidateSchema = z.object({
24
+ number: z.number().int().positive(),
25
+ user: z.object({ type: z.string().min(1) }),
26
+ head: z.object({
27
+ ref: z.string().min(1),
28
+ repo: z.object({ full_name: z.string().min(1) })
29
+ })
30
+ });
31
+ const openPullRequestsSchema = z.array(pullRequestCandidateSchema);
32
+ const pullRequestDetailSchema = pullRequestCandidateSchema.extend({
33
+ html_url: z.url(),
34
+ mergeable: z.boolean().nullable(),
35
+ mergeable_state: z.string().min(1),
36
+ head: pullRequestCandidateSchema.shape.head.extend({ sha: z.string().min(1) }),
37
+ base: z.object({
38
+ ref: z.string().min(1),
39
+ sha: z.string().min(1)
40
+ })
41
+ });
42
+ const collaboratorPermissionSchema = z.object({ permission: z.enum([
43
+ "admin",
44
+ "maintain",
45
+ "write",
46
+ "triage",
47
+ "read",
48
+ "none"
49
+ ]) });
50
+ const writePermissions = /* @__PURE__ */ new Set([
51
+ "admin",
52
+ "maintain",
53
+ "write"
54
+ ]);
55
+ var GitHubReviewCommentAuthorError = class extends Error {
56
+ author;
57
+ constructor(author) {
58
+ super(`GitHub user ${author} does not have write access to the repository`);
59
+ this.name = "GitHubReviewCommentAuthorError";
60
+ this.author = author;
61
+ }
62
+ };
63
+ /** Create a pull request from an existing head branch. */
64
+ async function createPullRequest(input) {
65
+ const pull = pullRequestSchema.parse(await gitHubRequest(input, "/pulls", {
66
+ method: "POST",
67
+ body: JSON.stringify({
68
+ title: input.title,
69
+ head: input.head,
70
+ base: input.base,
71
+ ...input.body === void 0 ? {} : { body: input.body },
72
+ ...input.draft === void 0 ? {} : { draft: input.draft }
73
+ })
74
+ }));
75
+ return {
76
+ prNumber: pull.number,
77
+ prUrl: pull.html_url
78
+ };
79
+ }
80
+ /** Read one inline review comment after applying the author policy, which requires write access by default. */
81
+ async function readReviewComment(input) {
82
+ const comment = parseReviewComment(await gitHubRequest(input, `/pulls/comments/${positiveInteger(input.commentId, "commentId")}`));
83
+ await assertReviewCommentAuthor(input, comment.author);
84
+ return comment;
85
+ }
86
+ /** Reply to an inline review comment after applying the author policy, which requires write access by default. */
87
+ async function answerReviewComment(input) {
88
+ await readReviewComment(input);
89
+ return parseReviewComment(await gitHubRequest(input, `/pulls/${positiveInteger(input.pullRequestNumber, "pullRequestNumber")}/comments/${positiveInteger(input.commentId, "commentId")}/replies`, {
90
+ method: "POST",
91
+ body: JSON.stringify({ body: input.body })
92
+ }));
93
+ }
94
+ /** Read the current head branch and commit of a pull request. */
95
+ async function readPullRequestHead(input) {
96
+ const pull = pullRequestHeadSchema.parse(await gitHubRequest(input, `/pulls/${positiveInteger(input.pullRequestNumber, "pullRequestNumber")}`));
97
+ return {
98
+ branch: pull.head.ref,
99
+ sha: pull.head.sha,
100
+ repository: pull.head.repo.full_name.toLowerCase()
101
+ };
102
+ }
103
+ /** List open, same-repository pull requests by one author that GitHub reports as conflicting. */
104
+ async function listConflictedPullRequests(input) {
105
+ const candidates = openPullRequestsSchema.parse(await gitHubRequest(input, `/pulls?state=open&per_page=100`)).filter((pull) => pull.user.type === "Bot" && pull.head.ref.startsWith("factory/") && pull.head.repo.full_name.toLowerCase() === input.repository.toLowerCase());
106
+ const conflicted = [];
107
+ for (const candidate of candidates) {
108
+ const pull = pullRequestDetailSchema.parse(await gitHubRequest(input, `/pulls/${candidate.number}`));
109
+ if (pull.mergeable === false || pull.mergeable_state === "dirty") conflicted.push({
110
+ prNumber: pull.number,
111
+ prUrl: pull.html_url,
112
+ headBranch: pull.head.ref,
113
+ headSha: pull.head.sha,
114
+ baseBranch: pull.base.ref,
115
+ baseSha: pull.base.sha
116
+ });
117
+ }
118
+ return conflicted;
119
+ }
120
+ /** Add one label to a pull request. */
121
+ async function addLabel(input) {
122
+ return parseLabels(await gitHubRequest(input, `/issues/${positiveInteger(input.pullRequestNumber, "pullRequestNumber")}/labels`, {
123
+ method: "POST",
124
+ body: JSON.stringify({ labels: [input.label] })
125
+ }));
126
+ }
127
+ /** Remove one label from a pull request. */
128
+ async function removeLabel(input) {
129
+ return parseLabels(await gitHubRequest(input, `/issues/${positiveInteger(input.pullRequestNumber, "pullRequestNumber")}/labels/${encodeURIComponent(input.label)}`, { method: "DELETE" }));
130
+ }
131
+ async function gitHubRequest(input, path, init = {}) {
132
+ if (!repositorySlugPattern.test(input.repository)) throw new Error(`invalid GitHub repository slug: ${input.repository}`);
133
+ const response = await (input.fetch ?? globalThis.fetch)(`${gitHubApiUrl}/repos/${input.repository}${path}`, {
134
+ ...init,
135
+ headers: {
136
+ Accept: "application/vnd.github+json",
137
+ Authorization: `Bearer ${input.token}`,
138
+ "Content-Type": "application/json",
139
+ "X-GitHub-Api-Version": "2022-11-28",
140
+ ...init.headers
141
+ }
142
+ });
143
+ const body = await response.json();
144
+ if (!response.ok) {
145
+ const message = z.object({ message: z.string() }).safeParse(body);
146
+ throw new Error(`GitHub API request failed (${response.status}): ${message.success ? message.data.message : response.statusText}`);
147
+ }
148
+ return body;
149
+ }
150
+ function parseReviewComment(value) {
151
+ const comment = reviewCommentSchema.parse(value);
152
+ return {
153
+ id: comment.id,
154
+ body: comment.body,
155
+ url: comment.html_url,
156
+ author: comment.user.login,
157
+ inReplyToId: comment.in_reply_to_id ?? null
158
+ };
159
+ }
160
+ function parseLabels(value) {
161
+ return labelsSchema.parse(value).map(({ name }) => name);
162
+ }
163
+ async function assertReviewCommentAuthor(input, author) {
164
+ if (input.authorPolicy === "any") return;
165
+ const { permission } = collaboratorPermissionSchema.parse(await gitHubRequest(input, `/collaborators/${encodeURIComponent(author)}/permission`));
166
+ if (!writePermissions.has(permission)) throw new GitHubReviewCommentAuthorError(author);
167
+ }
168
+ function positiveInteger(value, name) {
169
+ if (!Number.isInteger(value) || value < 1) throw new Error(`${name} must be a positive integer`);
170
+ return value;
171
+ }
172
+ /**
173
+ * Verify a GitHub webhook HMAC signature (the x-hub-signature-256 header)
174
+ * against the raw request body. Returns false for missing or malformed
175
+ * signatures instead of throwing.
176
+ */
177
+ function verifyGitHubSignature(options) {
178
+ const { secret, payload, signature } = options;
179
+ if (signature === null || !signature.startsWith("sha256=")) return false;
180
+ const expected = createHmac("sha256", secret).update(payload).digest("hex");
181
+ const provided = signature.slice(7);
182
+ if (provided.length !== expected.length) return false;
183
+ return timingSafeEqual(Buffer.from(provided, "hex"), Buffer.from(expected, "hex"));
184
+ }
185
+ const pushEventSchema = z.looseObject({
186
+ ref: z.string().min(1),
187
+ repository: z.looseObject({
188
+ default_branch: z.string().min(1),
189
+ full_name: z.string().min(1)
190
+ })
191
+ });
192
+ /** Normalize a push to a repository's default branch for deterministic reconciliation. */
193
+ function normalizeGitHubDefaultBranchPush(options) {
194
+ if (options.event !== "push") return null;
195
+ const parsed = pushEventSchema.safeParse(options.payload);
196
+ if (!parsed.success || parsed.data.ref !== `refs/heads/${parsed.data.repository.default_branch}`) return null;
197
+ return {
198
+ source: "github_default_branch_push",
199
+ deliveryId: options.deliveryId,
200
+ repositorySlug: parsed.data.repository.full_name.toLowerCase(),
201
+ defaultBranch: parsed.data.repository.default_branch
202
+ };
203
+ }
204
+ const issueEventSchema = z.looseObject({
205
+ action: z.string(),
206
+ issue: z.looseObject({
207
+ number: z.number().int().min(1),
208
+ title: z.string(),
209
+ body: z.string().nullable()
210
+ }),
211
+ repository: z.looseObject({ full_name: z.string().min(1) })
212
+ });
213
+ const failedWorkflowConclusions = /* @__PURE__ */ new Set([
214
+ "action_required",
215
+ "failure",
216
+ "stale",
217
+ "startup_failure",
218
+ "timed_out"
219
+ ]);
220
+ const workflowRunEventSchema = z.looseObject({
221
+ action: z.string(),
222
+ repository: z.looseObject({
223
+ default_branch: z.string().min(1),
224
+ full_name: z.string().min(1)
225
+ }),
226
+ workflow_run: z.looseObject({
227
+ id: z.number().int().min(1),
228
+ name: z.string().min(1),
229
+ event: z.string().min(1),
230
+ head_branch: z.string().min(1).nullable(),
231
+ head_repository: z.looseObject({ full_name: z.string().min(1) }).nullable(),
232
+ head_sha: z.string().min(1),
233
+ html_url: z.url(),
234
+ conclusion: z.string().nullable(),
235
+ run_attempt: z.number().int().min(1)
236
+ })
237
+ });
238
+ /**
239
+ * Normalize a GitHub issues webhook into the factory's signal shape. Returns
240
+ * null for payloads that are not an opened issue. The caller supplies the
241
+ * repository id; the payload stays untrusted data.
242
+ */
243
+ function normalizeGitHubIssueOpened(options) {
244
+ if (options.event !== "issues") return null;
245
+ const parsed = issueEventSchema.safeParse(options.payload);
246
+ if (!parsed.success || parsed.data.action !== "opened") return null;
247
+ const slug = parsed.data.repository.full_name.toLowerCase();
248
+ const { number, title, body } = parsed.data.issue;
249
+ return {
250
+ source: "github_issue",
251
+ deliveryId: options.deliveryId,
252
+ repositorySlug: slug,
253
+ replyTo: {
254
+ channel: "github",
255
+ address: `${slug}#${number}`
256
+ },
257
+ issue: {
258
+ number,
259
+ title,
260
+ body
261
+ },
262
+ payload: options.payload
263
+ };
264
+ }
265
+ const pullRequestClosedSchema = z.looseObject({
266
+ action: z.string(),
267
+ pull_request: z.looseObject({
268
+ number: z.number().int().min(1),
269
+ html_url: z.url(),
270
+ merged: z.boolean(),
271
+ merged_by: z.looseObject({ login: z.string().min(1) }).nullable().optional()
272
+ }),
273
+ repository: z.looseObject({ full_name: z.string().min(1) }),
274
+ sender: z.looseObject({ login: z.string().min(1) })
275
+ });
276
+ /**
277
+ * Normalize a closed pull_request webhook so the factory can observe what
278
+ * happened to its change: merged records merged_by (the sender when GitHub
279
+ * omits it), a close without merge records the sender.
280
+ */
281
+ function normalizeGitHubPullRequestClosed(options) {
282
+ if (options.event !== "pull_request") return null;
283
+ const parsed = pullRequestClosedSchema.safeParse(options.payload);
284
+ if (!parsed.success || parsed.data.action !== "closed") return null;
285
+ const pr = parsed.data.pull_request;
286
+ const sender = parsed.data.sender.login;
287
+ return {
288
+ source: "github_pull_request_closed",
289
+ deliveryId: options.deliveryId,
290
+ repositorySlug: parsed.data.repository.full_name.toLowerCase(),
291
+ prNumber: pr.number,
292
+ prUrl: pr.html_url,
293
+ merged: pr.merged,
294
+ decidedBy: pr.merged ? pr.merged_by?.login ?? sender : sender
295
+ };
296
+ }
297
+ /** Normalize a completed, unsuccessful GitHub Actions run into a CI failure signal. */
298
+ function normalizeGitHubWorkflowRunFailure(options) {
299
+ if (options.event !== "workflow_run") return null;
300
+ const parsed = workflowRunEventSchema.safeParse(options.payload);
301
+ if (!parsed.success || parsed.data.action !== "completed") return null;
302
+ const run = parsed.data.workflow_run;
303
+ const conclusion = run.conclusion;
304
+ if (conclusion === null || !failedWorkflowConclusions.has(conclusion)) return null;
305
+ const slug = parsed.data.repository.full_name.toLowerCase();
306
+ return {
307
+ source: "github_ci_failure",
308
+ deliveryId: options.deliveryId,
309
+ repositoryDefaultBranch: parsed.data.repository.default_branch,
310
+ repositorySlug: slug,
311
+ replyTo: {
312
+ channel: "github",
313
+ address: `${slug}/actions/runs/${run.id}`
314
+ },
315
+ workflowRun: {
316
+ id: run.id,
317
+ name: run.name,
318
+ trigger: run.event,
319
+ branch: run.head_branch,
320
+ headRepositorySlug: run.head_repository?.full_name.toLowerCase() ?? null,
321
+ headSha: run.head_sha,
322
+ url: run.html_url,
323
+ conclusion,
324
+ attempt: run.run_attempt
325
+ },
326
+ payload: options.payload
327
+ };
328
+ }
329
+ //#endregion
330
+ export { GitHubReviewCommentAuthorError, addLabel, answerReviewComment, createPullRequest, listConflictedPullRequests, normalizeGitHubDefaultBranchPush, normalizeGitHubIssueOpened, normalizeGitHubPullRequestClosed, normalizeGitHubWorkflowRunFailure, readPullRequestHead, readReviewComment, removeLabel, verifyGitHubSignature };
331
+
332
+ //# sourceMappingURL=github.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.mjs","names":[],"sources":["../src/github.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { z } from \"zod\";\nimport type { NormalizedSignal, WebhookEvent } from \"./connectors/index\";\n\nconst gitHubApiUrl = \"https://api.github.com\";\nconst repositorySlugPattern = /^[^/\\s]+\\/[^/\\s]+$/;\n\nexport interface GitHubRepositoryRequest {\n repository: string;\n token: string;\n fetch?: typeof globalThis.fetch;\n}\n\nexport interface CreatePullRequestInput extends GitHubRepositoryRequest {\n title: string;\n head: string;\n base: string;\n body?: string;\n draft?: boolean;\n}\n\nexport interface CreatedPullRequest {\n prNumber: number;\n prUrl: string;\n}\n\nexport type ReviewCommentAuthorPolicy = \"write\" | \"any\";\n\ninterface ReviewCommentTrustInput {\n /** Defaults to `write`. Use `any` only when untrusted review text is handled as data. */\n authorPolicy?: ReviewCommentAuthorPolicy;\n}\n\nexport interface ReadReviewCommentInput extends GitHubRepositoryRequest, ReviewCommentTrustInput {\n commentId: number;\n}\n\nexport interface GitHubReviewComment {\n id: number;\n body: string;\n url: string;\n author: string;\n inReplyToId: number | null;\n}\n\nexport interface AnswerReviewCommentInput\n extends GitHubRepositoryRequest,\n ReviewCommentTrustInput {\n pullRequestNumber: number;\n commentId: number;\n body: string;\n}\n\nexport interface ReadPullRequestHeadInput extends GitHubRepositoryRequest {\n pullRequestNumber: number;\n}\n\nexport interface GitHubPullRequestHead {\n branch: string;\n sha: string;\n repository: string;\n}\n\nexport interface PullRequestLabelInput extends GitHubRepositoryRequest {\n pullRequestNumber: number;\n label: string;\n}\n\nexport interface ConflictedPullRequest {\n prNumber: number;\n prUrl: string;\n headBranch: string;\n headSha: string;\n baseBranch: string;\n baseSha: string;\n}\n\nexport type ListConflictedPullRequestsInput = GitHubRepositoryRequest;\n\nconst pullRequestSchema = z.object({\n number: z.number().int().positive(),\n html_url: z.url(),\n});\n\nconst pullRequestHeadSchema = z.object({\n head: z.object({\n ref: z\n .string()\n .regex(/^[A-Za-z0-9][A-Za-z0-9._/-]*$/)\n .refine((value) => !value.includes(\"..\")),\n sha: z.string().regex(/^[0-9a-f]{40}$/),\n repo: z.object({ full_name: z.string().min(1) }),\n }),\n});\n\nconst reviewCommentSchema = z.object({\n id: z.number().int().positive(),\n body: z.string(),\n html_url: z.url(),\n user: z.object({ login: z.string().min(1) }),\n in_reply_to_id: z.number().int().positive().nullish(),\n});\n\nconst labelsSchema = z.array(z.object({ name: z.string().min(1) }));\nconst pullRequestCandidateSchema = z.object({\n number: z.number().int().positive(),\n user: z.object({ type: z.string().min(1) }),\n head: z.object({ ref: z.string().min(1), repo: z.object({ full_name: z.string().min(1) }) }),\n});\nconst openPullRequestsSchema = z.array(pullRequestCandidateSchema);\nconst pullRequestDetailSchema = pullRequestCandidateSchema.extend({\n html_url: z.url(),\n mergeable: z.boolean().nullable(),\n mergeable_state: z.string().min(1),\n head: pullRequestCandidateSchema.shape.head.extend({ sha: z.string().min(1) }),\n base: z.object({ ref: z.string().min(1), sha: z.string().min(1) }),\n});\nconst collaboratorPermissionSchema = z.object({\n permission: z.enum([\"admin\", \"maintain\", \"write\", \"triage\", \"read\", \"none\"]),\n});\nconst writePermissions = new Set([\"admin\", \"maintain\", \"write\"]);\n\nexport class GitHubReviewCommentAuthorError extends Error {\n readonly author: string;\n\n constructor(author: string) {\n super(`GitHub user ${author} does not have write access to the repository`);\n this.name = \"GitHubReviewCommentAuthorError\";\n this.author = author;\n }\n}\n\n/** Create a pull request from an existing head branch. */\nexport async function createPullRequest(input: CreatePullRequestInput): Promise<CreatedPullRequest> {\n const pull = pullRequestSchema.parse(\n await gitHubRequest(input, \"/pulls\", {\n method: \"POST\",\n body: JSON.stringify({\n title: input.title,\n head: input.head,\n base: input.base,\n ...(input.body === undefined ? {} : { body: input.body }),\n ...(input.draft === undefined ? {} : { draft: input.draft }),\n }),\n }),\n );\n return { prNumber: pull.number, prUrl: pull.html_url };\n}\n\n/** Read one inline review comment after applying the author policy, which requires write access by default. */\nexport async function readReviewComment(\n input: ReadReviewCommentInput,\n): Promise<GitHubReviewComment> {\n const comment = parseReviewComment(\n await gitHubRequest(input, `/pulls/comments/${positiveInteger(input.commentId, \"commentId\")}`),\n );\n await assertReviewCommentAuthor(input, comment.author);\n return comment;\n}\n\n/** Reply to an inline review comment after applying the author policy, which requires write access by default. */\nexport async function answerReviewComment(\n input: AnswerReviewCommentInput,\n): Promise<GitHubReviewComment> {\n await readReviewComment(input);\n return parseReviewComment(\n await gitHubRequest(\n input,\n `/pulls/${positiveInteger(input.pullRequestNumber, \"pullRequestNumber\")}/comments/${positiveInteger(input.commentId, \"commentId\")}/replies`,\n { method: \"POST\", body: JSON.stringify({ body: input.body }) },\n ),\n );\n}\n\n/** Read the current head branch and commit of a pull request. */\nexport async function readPullRequestHead(\n input: ReadPullRequestHeadInput,\n): Promise<GitHubPullRequestHead> {\n const pull = pullRequestHeadSchema.parse(\n await gitHubRequest(input, `/pulls/${positiveInteger(input.pullRequestNumber, \"pullRequestNumber\")}`),\n );\n return {\n branch: pull.head.ref,\n sha: pull.head.sha,\n repository: pull.head.repo.full_name.toLowerCase(),\n };\n}\n\n/** List open, same-repository pull requests by one author that GitHub reports as conflicting. */\nexport async function listConflictedPullRequests(\n input: ListConflictedPullRequestsInput,\n): Promise<ConflictedPullRequest[]> {\n const pulls = openPullRequestsSchema.parse(\n await gitHubRequest(input, `/pulls?state=open&per_page=100`),\n );\n const candidates = pulls.filter(\n (pull) =>\n pull.user.type === \"Bot\" &&\n pull.head.ref.startsWith(\"factory/\") &&\n pull.head.repo.full_name.toLowerCase() === input.repository.toLowerCase(),\n );\n const conflicted: ConflictedPullRequest[] = [];\n for (const candidate of candidates) {\n const pull = pullRequestDetailSchema.parse(\n await gitHubRequest(input, `/pulls/${candidate.number}`),\n );\n if (pull.mergeable === false || pull.mergeable_state === \"dirty\") {\n conflicted.push({\n prNumber: pull.number,\n prUrl: pull.html_url,\n headBranch: pull.head.ref,\n headSha: pull.head.sha,\n baseBranch: pull.base.ref,\n baseSha: pull.base.sha,\n });\n }\n }\n return conflicted;\n}\n\n/** Add one label to a pull request. */\nexport async function addLabel(input: PullRequestLabelInput): Promise<string[]> {\n return parseLabels(\n await gitHubRequest(\n input,\n `/issues/${positiveInteger(input.pullRequestNumber, \"pullRequestNumber\")}/labels`,\n { method: \"POST\", body: JSON.stringify({ labels: [input.label] }) },\n ),\n );\n}\n\n/** Remove one label from a pull request. */\nexport async function removeLabel(input: PullRequestLabelInput): Promise<string[]> {\n return parseLabels(\n await gitHubRequest(\n input,\n `/issues/${positiveInteger(input.pullRequestNumber, \"pullRequestNumber\")}/labels/${encodeURIComponent(input.label)}`,\n { method: \"DELETE\" },\n ),\n );\n}\n\nasync function gitHubRequest(\n input: GitHubRepositoryRequest,\n path: string,\n init: RequestInit = {},\n): Promise<unknown> {\n if (!repositorySlugPattern.test(input.repository)) {\n throw new Error(`invalid GitHub repository slug: ${input.repository}`);\n }\n const response = await (input.fetch ?? globalThis.fetch)(\n `${gitHubApiUrl}/repos/${input.repository}${path}`,\n {\n ...init,\n headers: {\n Accept: \"application/vnd.github+json\",\n Authorization: `Bearer ${input.token}`,\n \"Content-Type\": \"application/json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n ...init.headers,\n },\n },\n );\n const body: unknown = await response.json();\n if (!response.ok) {\n const message = z.object({ message: z.string() }).safeParse(body);\n throw new Error(\n `GitHub API request failed (${response.status}): ${message.success ? message.data.message : response.statusText}`,\n );\n }\n return body;\n}\n\nfunction parseReviewComment(value: unknown): GitHubReviewComment {\n const comment = reviewCommentSchema.parse(value);\n return {\n id: comment.id,\n body: comment.body,\n url: comment.html_url,\n author: comment.user.login,\n inReplyToId: comment.in_reply_to_id ?? null,\n };\n}\n\nfunction parseLabels(value: unknown): string[] {\n return labelsSchema.parse(value).map(({ name }) => name);\n}\n\nasync function assertReviewCommentAuthor(\n input: GitHubRepositoryRequest & ReviewCommentTrustInput,\n author: string,\n): Promise<void> {\n if (input.authorPolicy === \"any\") {\n return;\n }\n const { permission } = collaboratorPermissionSchema.parse(\n await gitHubRequest(input, `/collaborators/${encodeURIComponent(author)}/permission`),\n );\n if (!writePermissions.has(permission)) {\n throw new GitHubReviewCommentAuthorError(author);\n }\n}\n\nfunction positiveInteger(value: number, name: string): number {\n if (!Number.isInteger(value) || value < 1) {\n throw new Error(`${name} must be a positive integer`);\n }\n return value;\n}\n\n/**\n * Verify a GitHub webhook HMAC signature (the x-hub-signature-256 header)\n * against the raw request body. Returns false for missing or malformed\n * signatures instead of throwing.\n */\nexport function verifyGitHubSignature(options: {\n secret: string;\n payload: string;\n signature: string | null;\n}): boolean {\n const { secret, payload, signature } = options;\n if (signature === null || !signature.startsWith(\"sha256=\")) {\n return false;\n }\n const expected = createHmac(\"sha256\", secret).update(payload).digest(\"hex\");\n const provided = signature.slice(\"sha256=\".length);\n if (provided.length !== expected.length) {\n return false;\n }\n return timingSafeEqual(Buffer.from(provided, \"hex\"), Buffer.from(expected, \"hex\"));\n}\n\nconst pushEventSchema = z.looseObject({\n ref: z.string().min(1),\n repository: z.looseObject({\n default_branch: z.string().min(1),\n full_name: z.string().min(1),\n }),\n});\n\nexport interface GitHubDefaultBranchPush {\n source: \"github_default_branch_push\";\n deliveryId: string;\n repositorySlug: string;\n defaultBranch: string;\n}\n\n/** Normalize a push to a repository's default branch for deterministic reconciliation. */\nexport function normalizeGitHubDefaultBranchPush(options: {\n deliveryId: string;\n event: string;\n payload: unknown;\n}): GitHubDefaultBranchPush | null {\n if (options.event !== \"push\") return null;\n const parsed = pushEventSchema.safeParse(options.payload);\n if (!parsed.success || parsed.data.ref !== `refs/heads/${parsed.data.repository.default_branch}`) {\n return null;\n }\n return {\n source: \"github_default_branch_push\",\n deliveryId: options.deliveryId,\n repositorySlug: parsed.data.repository.full_name.toLowerCase(),\n defaultBranch: parsed.data.repository.default_branch,\n };\n}\n\nconst issueEventSchema = z.looseObject({\n action: z.string(),\n issue: z.looseObject({\n number: z.number().int().min(1),\n title: z.string(),\n body: z.string().nullable(),\n }),\n repository: z.looseObject({\n full_name: z.string().min(1),\n }),\n});\n\nexport interface GitHubIssueSignal extends NormalizedSignal<\"github_issue\"> {\n repositorySlug: string;\n issue: { number: number; title: string; body: string | null };\n}\n\nconst failedWorkflowConclusions = new Set([\n \"action_required\",\n \"failure\",\n \"stale\",\n \"startup_failure\",\n \"timed_out\",\n]);\n\nconst workflowRunEventSchema = z.looseObject({\n action: z.string(),\n repository: z.looseObject({\n default_branch: z.string().min(1),\n full_name: z.string().min(1),\n }),\n workflow_run: z.looseObject({\n id: z.number().int().min(1),\n name: z.string().min(1),\n event: z.string().min(1),\n head_branch: z.string().min(1).nullable(),\n head_repository: z\n .looseObject({\n full_name: z.string().min(1),\n })\n .nullable(),\n head_sha: z.string().min(1),\n html_url: z.url(),\n conclusion: z.string().nullable(),\n run_attempt: z.number().int().min(1),\n }),\n});\n\nexport interface GitHubCIFailureSignal extends NormalizedSignal<\"github_ci_failure\"> {\n repositoryDefaultBranch: string;\n repositorySlug: string;\n workflowRun: {\n id: number;\n name: string;\n trigger: string;\n branch: string | null;\n headRepositorySlug: string | null;\n headSha: string;\n url: string;\n conclusion: string;\n attempt: number;\n };\n}\n\n/**\n * Normalize a GitHub issues webhook into the factory's signal shape. Returns\n * null for payloads that are not an opened issue. The caller supplies the\n * repository id; the payload stays untrusted data.\n */\nexport function normalizeGitHubIssueOpened(options: WebhookEvent): GitHubIssueSignal | null {\n if (options.event !== \"issues\") {\n return null;\n }\n const parsed = issueEventSchema.safeParse(options.payload);\n if (!parsed.success || parsed.data.action !== \"opened\") {\n return null;\n }\n const slug = parsed.data.repository.full_name.toLowerCase();\n const { number, title, body } = parsed.data.issue;\n return {\n source: \"github_issue\",\n deliveryId: options.deliveryId,\n repositorySlug: slug,\n replyTo: { channel: \"github\", address: `${slug}#${number}` },\n issue: { number, title, body },\n payload: options.payload,\n };\n}\n\nconst pullRequestClosedSchema = z.looseObject({\n action: z.string(),\n pull_request: z.looseObject({\n number: z.number().int().min(1),\n html_url: z.url(),\n merged: z.boolean(),\n merged_by: z.looseObject({ login: z.string().min(1) }).nullable().optional(),\n }),\n repository: z.looseObject({\n full_name: z.string().min(1),\n }),\n sender: z.looseObject({ login: z.string().min(1) }),\n});\n\nexport interface GitHubPullRequestClosed {\n source: \"github_pull_request_closed\";\n deliveryId: string;\n repositorySlug: string;\n prNumber: number;\n prUrl: string;\n merged: boolean;\n /** The GitHub login that merged the pull request, or closed it without merging. */\n decidedBy: string;\n}\n\n/**\n * Normalize a closed pull_request webhook so the factory can observe what\n * happened to its change: merged records merged_by (the sender when GitHub\n * omits it), a close without merge records the sender.\n */\nexport function normalizeGitHubPullRequestClosed(\n options: WebhookEvent,\n): GitHubPullRequestClosed | null {\n if (options.event !== \"pull_request\") {\n return null;\n }\n const parsed = pullRequestClosedSchema.safeParse(options.payload);\n if (!parsed.success || parsed.data.action !== \"closed\") {\n return null;\n }\n const pr = parsed.data.pull_request;\n const sender = parsed.data.sender.login;\n return {\n source: \"github_pull_request_closed\",\n deliveryId: options.deliveryId,\n repositorySlug: parsed.data.repository.full_name.toLowerCase(),\n prNumber: pr.number,\n prUrl: pr.html_url,\n merged: pr.merged,\n decidedBy: pr.merged ? (pr.merged_by?.login ?? sender) : sender,\n };\n}\n\n/** Normalize a completed, unsuccessful GitHub Actions run into a CI failure signal. */\nexport function normalizeGitHubWorkflowRunFailure(\n options: WebhookEvent,\n): GitHubCIFailureSignal | null {\n if (options.event !== \"workflow_run\") {\n return null;\n }\n const parsed = workflowRunEventSchema.safeParse(options.payload);\n if (!parsed.success || parsed.data.action !== \"completed\") {\n return null;\n }\n\n const run = parsed.data.workflow_run;\n const conclusion = run.conclusion;\n if (conclusion === null || !failedWorkflowConclusions.has(conclusion)) {\n return null;\n }\n const slug = parsed.data.repository.full_name.toLowerCase();\n return {\n source: \"github_ci_failure\",\n deliveryId: options.deliveryId,\n repositoryDefaultBranch: parsed.data.repository.default_branch,\n repositorySlug: slug,\n replyTo: { channel: \"github\", address: `${slug}/actions/runs/${run.id}` },\n workflowRun: {\n id: run.id,\n name: run.name,\n trigger: run.event,\n branch: run.head_branch,\n headRepositorySlug: run.head_repository?.full_name.toLowerCase() ?? null,\n headSha: run.head_sha,\n url: run.html_url,\n conclusion,\n attempt: run.run_attempt,\n },\n payload: options.payload,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import { z } from "zod";
2
+ //#region src/schema/common.ts
3
+ const SCHEMA_VERSION = 1;
4
+ const schemaVersionSchema = z.literal(1).describe("Version of the entity schema this row was written with.");
5
+ const isoDateTimeSchema = z.iso.datetime().describe("UTC timestamp in ISO 8601 format, e.g. 2026-08-26T12:00:00Z.");
6
+ const usdSchema = z.number().finite().nonnegative().describe("Amount in US dollars.");
7
+ /**
8
+ * Open vocabulary value. Standard values ship as exported constants; custom
9
+ * values matching the pattern are equally valid.
10
+ */
11
+ const vocabularySchema = z.string().regex(/^[a-z][a-z0-9_]{0,63}$/, "must be lowercase snake_case starting with a letter").describe("Lowercase snake_case name, e.g. github_issue or sentry_alert.");
12
+ const replyToSchema = z.strictObject({
13
+ channel: z.string().min(1).describe("Connector that should carry replies, e.g. github or slack."),
14
+ address: z.string().min(1).describe("Connector-specific reply address, e.g. acme/web#41 or a Slack thread id.")
15
+ });
16
+ //#endregion
17
+ //#region src/schema/id.ts
18
+ function idSchema(prefix) {
19
+ return z.string().regex(new RegExp(`^${prefix}_[a-z0-9]{6,64}$`));
20
+ }
21
+ const factoryIdSchema = idSchema("fac");
22
+ const repositoryIdSchema = idSchema("repo");
23
+ const signalIdSchema = idSchema("sig");
24
+ const taskIdSchema = idSchema("task");
25
+ const changeIdSchema = idSchema("chg");
26
+ const receiptIdSchema = idSchema("rcpt");
27
+ const repositoryIdsSchema = z.array(repositoryIdSchema).min(1);
28
+ //#endregion
29
+ export { repositoryIdsSchema as a, SCHEMA_VERSION as c, schemaVersionSchema as d, usdSchema as f, repositoryIdSchema as i, isoDateTimeSchema as l, factoryIdSchema as n, signalIdSchema as o, vocabularySchema as p, receiptIdSchema as r, taskIdSchema as s, changeIdSchema as t, replyToSchema as u };
30
+
31
+ //# sourceMappingURL=id-DwCihQr4.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id-DwCihQr4.mjs","names":[],"sources":["../src/schema/common.ts","../src/schema/id.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const SCHEMA_VERSION = 1;\n\nexport const schemaVersionSchema = z\n .literal(SCHEMA_VERSION)\n .describe(\"Version of the entity schema this row was written with.\");\n\nexport const isoDateTimeSchema = z.iso\n .datetime()\n .describe(\"UTC timestamp in ISO 8601 format, e.g. 2026-08-26T12:00:00Z.\");\n\nexport const usdSchema = z.number().finite().nonnegative().describe(\"Amount in US dollars.\");\n\n/**\n * Open vocabulary value. Standard values ship as exported constants; custom\n * values matching the pattern are equally valid.\n */\nexport const vocabularySchema = z\n .string()\n .regex(/^[a-z][a-z0-9_]{0,63}$/, \"must be lowercase snake_case starting with a letter\")\n .describe(\"Lowercase snake_case name, e.g. github_issue or sentry_alert.\");\n\nexport const replyToSchema = z.strictObject({\n channel: z\n .string()\n .min(1)\n .describe(\"Connector that should carry replies, e.g. github or slack.\"),\n address: z\n .string()\n .min(1)\n .describe(\"Connector-specific reply address, e.g. acme/web#41 or a Slack thread id.\"),\n});\n\nexport type ReplyTo = z.infer<typeof replyToSchema>;\n","import { z } from \"zod\";\n\nfunction idSchema(prefix: string) {\n return z.string().regex(new RegExp(`^${prefix}_[a-z0-9]{6,64}$`));\n}\n\nexport const factoryIdSchema = idSchema(\"fac\");\nexport const repositoryIdSchema = idSchema(\"repo\");\nexport const signalIdSchema = idSchema(\"sig\");\nexport const taskIdSchema = idSchema(\"task\");\nexport const changeIdSchema = idSchema(\"chg\");\nexport const receiptIdSchema = idSchema(\"rcpt\");\n\nexport const repositoryIdsSchema = z.array(repositoryIdSchema).min(1);\n\nexport type FactoryId = z.infer<typeof factoryIdSchema>;\nexport type RepositoryId = z.infer<typeof repositoryIdSchema>;\nexport type SignalId = z.infer<typeof signalIdSchema>;\nexport type TaskId = z.infer<typeof taskIdSchema>;\nexport type ChangeId = z.infer<typeof changeIdSchema>;\nexport type ReceiptId = z.infer<typeof receiptIdSchema>;\n"],"mappings":";;AAEA,MAAa,iBAAiB;AAE9B,MAAa,sBAAsB,EAChC,QAAA,CAAsB,CAAC,CACvB,SAAS,yDAAyD;AAErE,MAAa,oBAAoB,EAAE,IAChC,SAAS,CAAC,CACV,SAAS,8DAA8D;AAE1E,MAAa,YAAY,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,SAAS,uBAAuB;;;;;AAM3F,MAAa,mBAAmB,EAC7B,OAAO,CAAC,CACR,MAAM,0BAA0B,qDAAqD,CAAC,CACtF,SAAS,+DAA+D;AAE3E,MAAa,gBAAgB,EAAE,aAAa;CAC1C,SAAS,EACN,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,SAAS,4DAA4D;CACxE,SAAS,EACN,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,SAAS,0EAA0E;AACxF,CAAC;;;AC9BD,SAAS,SAAS,QAAgB;CAChC,OAAO,EAAE,OAAO,CAAC,CAAC,MAAM,IAAI,OAAO,IAAI,OAAO,iBAAiB,CAAC;AAClE;AAEA,MAAa,kBAAkB,SAAS,KAAK;AAC7C,MAAa,qBAAqB,SAAS,MAAM;AACjD,MAAa,iBAAiB,SAAS,KAAK;AAC5C,MAAa,eAAe,SAAS,MAAM;AAC3C,MAAa,iBAAiB,SAAS,KAAK;AAC5C,MAAa,kBAAkB,SAAS,MAAM;AAE9C,MAAa,sBAAsB,EAAE,MAAM,kBAAkB,CAAC,CAAC,IAAI,CAAC"}
@@ -0,0 +1,67 @@
1
+ import { c as ReplyTo, r as SignalSource } from "./signal-CHVEmzvI.mjs";
2
+ import { z } from "zod";
3
+ //#region src/connectors/index.d.ts
4
+ interface WebhookEvent {
5
+ deliveryId: string;
6
+ event: string;
7
+ payload: unknown;
8
+ }
9
+ interface NormalizedSignal<TSource extends SignalSource = SignalSource> {
10
+ source: TSource;
11
+ deliveryId: string;
12
+ replyTo: ReplyTo;
13
+ payload: unknown;
14
+ }
15
+ type SignalNormalizer<TSignal extends NormalizedSignal = NormalizedSignal> = (event: WebhookEvent) => TSignal | null;
16
+ declare const communicationOutcomes: readonly ["accepted", "started", "succeeded", "failed", "needs_human", "question", "digest", "rolled_back", "budget_warning", "budget_exhausted", "message"];
17
+ declare const communicationOutcomeSchema: z.ZodEnum<{
18
+ message: "message";
19
+ succeeded: "succeeded";
20
+ failed: "failed";
21
+ needs_human: "needs_human";
22
+ question: "question";
23
+ accepted: "accepted";
24
+ rolled_back: "rolled_back";
25
+ started: "started";
26
+ digest: "digest";
27
+ budget_warning: "budget_warning";
28
+ budget_exhausted: "budget_exhausted";
29
+ }>;
30
+ declare const communicationEventSchema: z.ZodObject<{
31
+ eventId: z.ZodString;
32
+ repositoryId: z.ZodString;
33
+ taskId: z.ZodOptional<z.ZodString>;
34
+ changeId: z.ZodOptional<z.ZodString>;
35
+ replyTo: z.ZodObject<{
36
+ channel: z.ZodString;
37
+ address: z.ZodString;
38
+ }, z.core.$strict>;
39
+ outcome: z.ZodEnum<{
40
+ message: "message";
41
+ succeeded: "succeeded";
42
+ failed: "failed";
43
+ needs_human: "needs_human";
44
+ question: "question";
45
+ accepted: "accepted";
46
+ rolled_back: "rolled_back";
47
+ started: "started";
48
+ digest: "digest";
49
+ budget_warning: "budget_warning";
50
+ budget_exhausted: "budget_exhausted";
51
+ }>;
52
+ data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
53
+ }, z.core.$strict>;
54
+ type CommunicationOutcome = z.infer<typeof communicationOutcomeSchema>;
55
+ type CommunicationEvent = z.infer<typeof communicationEventSchema>;
56
+ interface CommunicationAdapter {
57
+ readonly channel: string;
58
+ deliver(event: CommunicationEvent): Promise<void>;
59
+ }
60
+ interface CommunicationRouter {
61
+ readonly channels: readonly string[];
62
+ deliver(event: CommunicationEvent): Promise<void>;
63
+ }
64
+ declare function createCommunicationRouter(adapters: readonly CommunicationAdapter[]): CommunicationRouter;
65
+ //#endregion
66
+ export { NormalizedSignal as a, communicationEventSchema as c, createCommunicationRouter as d, CommunicationRouter as i, communicationOutcomeSchema as l, CommunicationEvent as n, SignalNormalizer as o, CommunicationOutcome as r, WebhookEvent as s, CommunicationAdapter as t, communicationOutcomes as u };
67
+ //# sourceMappingURL=index-WNGZJRRH.d.mts.map