@vercel/factory 0.0.3 → 0.0.4

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 (70) 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/driver-Bbd25rNM.mjs +23 -0
  9. package/dist/driver-Bbd25rNM.mjs.map +1 -0
  10. package/dist/driver-CrZsy7sm.d.mts +34 -0
  11. package/dist/eve/index.d.mts +1057 -0
  12. package/dist/eve/index.mjs +777 -0
  13. package/dist/eve/index.mjs.map +1 -0
  14. package/dist/github-5qQPCaXj.d.mts +123 -0
  15. package/dist/github.d.mts +2 -0
  16. package/dist/github.mjs +286 -0
  17. package/dist/github.mjs.map +1 -0
  18. package/dist/id-DwCihQr4.mjs +31 -0
  19. package/dist/id-DwCihQr4.mjs.map +1 -0
  20. package/dist/index-WNGZJRRH.d.mts +67 -0
  21. package/dist/index.d.mts +175 -0
  22. package/dist/index.mjs +882 -0
  23. package/dist/index.mjs.map +1 -0
  24. package/dist/next-page.d.mts +5 -0
  25. package/dist/next-page.mjs +71 -0
  26. package/dist/next-page.mjs.map +1 -0
  27. package/dist/next.d.mts +12 -0
  28. package/dist/{next.js → next.mjs} +13 -14
  29. package/dist/next.mjs.map +1 -0
  30. package/dist/postgres/index.d.mts +137 -0
  31. package/dist/postgres/index.mjs +96 -0
  32. package/dist/postgres/index.mjs.map +1 -0
  33. package/dist/signal-CHVEmzvI.d.mts +59 -0
  34. package/dist/task-Cj7Ei9Nh.mjs +336 -0
  35. package/dist/task-Cj7Ei9Nh.mjs.map +1 -0
  36. package/package.json +25 -22
  37. package/dist/approval-Z7iaWibT.d.ts +0 -541
  38. package/dist/blob/index.d.ts +0 -26
  39. package/dist/blob/index.js +0 -127
  40. package/dist/blob/index.js.map +0 -1
  41. package/dist/chunk-2JFES6BA.js +0 -41
  42. package/dist/chunk-2JFES6BA.js.map +0 -1
  43. package/dist/chunk-CEF6VJZC.js +0 -28
  44. package/dist/chunk-CEF6VJZC.js.map +0 -1
  45. package/dist/chunk-D2XMABG4.js +0 -311
  46. package/dist/chunk-D2XMABG4.js.map +0 -1
  47. package/dist/chunk-EFL4PDDE.js +0 -325
  48. package/dist/chunk-EFL4PDDE.js.map +0 -1
  49. package/dist/common-CaHaEOAS.d.ts +0 -18
  50. package/dist/connectors/index.d.ts +0 -53
  51. package/dist/connectors/index.js +0 -64
  52. package/dist/connectors/index.js.map +0 -1
  53. package/dist/driver-tRNggCCJ.d.ts +0 -32
  54. package/dist/eve/index.d.ts +0 -1053
  55. package/dist/eve/index.js +0 -752
  56. package/dist/eve/index.js.map +0 -1
  57. package/dist/github.d.ts +0 -139
  58. package/dist/github.js +0 -27
  59. package/dist/github.js.map +0 -1
  60. package/dist/index.d.ts +0 -164
  61. package/dist/index.js +0 -940
  62. package/dist/index.js.map +0 -1
  63. package/dist/next-page.d.ts +0 -5
  64. package/dist/next-page.js +0 -75
  65. package/dist/next-page.js.map +0 -1
  66. package/dist/next.d.ts +0 -12
  67. package/dist/next.js.map +0 -1
  68. package/dist/postgres/index.d.ts +0 -137
  69. package/dist/postgres/index.js +0 -118
  70. package/dist/postgres/index.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/blob/index.ts"],"sourcesContent":["import { get, head, list, put } from \"@vercel/blob\";\nimport {\n RecordExistsError,\n RecordNotFoundError,\n VersionConflictError,\n type CollectionName,\n type StoredEntry,\n type StoreDriver,\n} from \"../store/driver\";\n\n/** The narrow blob operations the driver needs; injectable for tests. */\nexport interface BlobClient {\n read(pathname: string): Promise<string | null>;\n /** Must reject with an already-exists error when overwrite is false and the path exists. */\n write(pathname: string, body: string, overwrite: boolean): Promise<void>;\n listPaths(prefix: string): Promise<string[]>;\n}\n\n// @vercel/blob throws BlobNotFoundError with the message\n// \"Vercel Blob: The requested blob does not exist\".\nfunction isNotFoundError(error: unknown): boolean {\n return (\n error instanceof Error &&\n (error.name === \"BlobNotFoundError\" ||\n /does not exist|not.?found|404/iu.test(error.message))\n );\n}\n\nfunction isAlreadyExistsError(error: unknown): boolean {\n return (\n error instanceof Error &&\n /already exists|not allowed to overwrite|overwrite/iu.test(error.message)\n );\n}\n\nexport const vercelBlobClient: BlobClient = {\n async read(pathname) {\n let url: string;\n try {\n url = (await head(pathname)).url;\n } catch (error) {\n if (isNotFoundError(error)) {\n return null;\n }\n throw error;\n }\n const result = await get(url, { access: \"private\" });\n if (result === null) {\n return null;\n }\n return new Response(result.stream).text();\n },\n\n async write(pathname, body, overwrite) {\n await put(pathname, body, {\n access: \"private\",\n allowOverwrite: overwrite,\n contentType: \"application/json\",\n });\n },\n\n async listPaths(prefix) {\n const paths: string[] = [];\n let cursor: string | undefined;\n do {\n const page = await list({ prefix, cursor, limit: 1000 });\n paths.push(...page.blobs.map((blob) => blob.pathname));\n cursor = page.hasMore ? page.cursor : undefined;\n } while (cursor !== undefined);\n return paths;\n },\n};\n\nexport interface BlobDriverOptions {\n client?: BlobClient;\n /** Path prefix inside the blob store; defaults to \"factory\". */\n prefix?: string;\n}\n\n/**\n * Store driver on Vercel Blob: durable and dependency-free (one Blob store on\n * the project, no database). Inserts and receipts are exactly as safe as on\n * Postgres (create-if-absent is atomic). The compare-and-swap update is\n * BEST-EFFORT: the version check reads then writes without a lock, so two\n * simultaneous writers can race. Acceptable for low-volume factories; use the\n * Postgres driver when concurrent dispatchers are expected.\n */\nexport function createBlobDriver(options: BlobDriverOptions = {}): StoreDriver {\n const client = options.client ?? vercelBlobClient;\n const prefix = options.prefix ?? \"factory\";\n\n function pathFor(collection: CollectionName, id: string): string {\n return `${prefix}/${collection}/${id}.json`;\n }\n\n async function readRecord(\n collection: CollectionName,\n id: string,\n ): Promise<{ value: unknown; version: number } | null> {\n let text: string | null;\n try {\n text = await client.read(pathFor(collection, id));\n } catch (error) {\n if (isNotFoundError(error)) {\n return null;\n }\n throw error;\n }\n if (text === null) {\n return null;\n }\n const parsed = JSON.parse(text) as { value: unknown; version: number };\n if (typeof parsed.version !== \"number\") {\n throw new Error(`malformed stored record at ${pathFor(collection, id)}`);\n }\n return parsed;\n }\n\n return {\n name: \"blob\",\n\n get(collection, id) {\n return readRecord(collection, id);\n },\n\n async insert(collection, id, value) {\n try {\n await client.write(\n pathFor(collection, id),\n JSON.stringify({ value, version: 1 }),\n false,\n );\n } catch (error) {\n if (isAlreadyExistsError(error)) {\n throw new RecordExistsError(collection, id);\n }\n throw error;\n }\n },\n\n async update(collection, id, value, expectedVersion) {\n const existing = await readRecord(collection, id);\n if (existing === null) {\n throw new RecordNotFoundError(collection, id);\n }\n if (existing.version !== expectedVersion) {\n throw new VersionConflictError(collection, id, expectedVersion);\n }\n await client.write(\n pathFor(collection, id),\n JSON.stringify({ value, version: expectedVersion + 1 }),\n true,\n );\n },\n\n async list(collection) {\n const dir = `${prefix}/${collection}/`;\n const paths = await client.listPaths(dir);\n const entries: StoredEntry[] = [];\n for (const pathname of paths) {\n const id = pathname.slice(dir.length).replace(/\\.json$/u, \"\");\n const record = await readRecord(collection, id);\n if (record !== null) {\n entries.push({ id, record });\n }\n }\n return entries;\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -1,41 +0,0 @@
1
- // src/schema/common.ts
2
- import { z } from "zod";
3
- var SCHEMA_VERSION = 1;
4
- var schemaVersionSchema = z.literal(SCHEMA_VERSION).describe("Version of the entity schema this row was written with.");
5
- var isoDateTimeSchema = z.iso.datetime().describe("UTC timestamp in ISO 8601 format, e.g. 2026-08-26T12:00:00Z.");
6
- var usdSchema = z.number().finite().nonnegative().describe("Amount in US dollars.");
7
- var 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.");
8
- var replyToSchema = z.strictObject({
9
- channel: z.string().min(1).describe("Connector that should carry replies, e.g. github or slack."),
10
- address: z.string().min(1).describe("Connector-specific reply address, e.g. acme/web#41 or a Slack thread id.")
11
- });
12
-
13
- // src/schema/id.ts
14
- import { z as z2 } from "zod";
15
- function idSchema(prefix) {
16
- return z2.string().regex(new RegExp(`^${prefix}_[a-z0-9]{6,64}$`));
17
- }
18
- var factoryIdSchema = idSchema("fac");
19
- var repositoryIdSchema = idSchema("repo");
20
- var signalIdSchema = idSchema("sig");
21
- var taskIdSchema = idSchema("task");
22
- var changeIdSchema = idSchema("chg");
23
- var receiptIdSchema = idSchema("rcpt");
24
- var repositoryIdsSchema = z2.array(repositoryIdSchema).min(1);
25
-
26
- export {
27
- SCHEMA_VERSION,
28
- schemaVersionSchema,
29
- isoDateTimeSchema,
30
- usdSchema,
31
- vocabularySchema,
32
- replyToSchema,
33
- factoryIdSchema,
34
- repositoryIdSchema,
35
- signalIdSchema,
36
- taskIdSchema,
37
- changeIdSchema,
38
- receiptIdSchema,
39
- repositoryIdsSchema
40
- };
41
- //# sourceMappingURL=chunk-2JFES6BA.js.map
@@ -1 +0,0 @@
1
- {"version":3,"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":";AAAA,SAAS,SAAS;AAEX,IAAM,iBAAiB;AAEvB,IAAM,sBAAsB,EAChC,QAAQ,cAAc,EACtB,SAAS,yDAAyD;AAE9D,IAAM,oBAAoB,EAAE,IAChC,SAAS,EACT,SAAS,8DAA8D;AAEnE,IAAM,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,uBAAuB;AAMpF,IAAM,mBAAmB,EAC7B,OAAO,EACP,MAAM,0BAA0B,qDAAqD,EACrF,SAAS,+DAA+D;AAEpE,IAAM,gBAAgB,EAAE,aAAa;AAAA,EAC1C,SAAS,EACN,OAAO,EACP,IAAI,CAAC,EACL,SAAS,4DAA4D;AAAA,EACxE,SAAS,EACN,OAAO,EACP,IAAI,CAAC,EACL,SAAS,0EAA0E;AACxF,CAAC;;;AChCD,SAAS,KAAAA,UAAS;AAElB,SAAS,SAAS,QAAgB;AAChC,SAAOA,GAAE,OAAO,EAAE,MAAM,IAAI,OAAO,IAAI,MAAM,kBAAkB,CAAC;AAClE;AAEO,IAAM,kBAAkB,SAAS,KAAK;AACtC,IAAM,qBAAqB,SAAS,MAAM;AAC1C,IAAM,iBAAiB,SAAS,KAAK;AACrC,IAAM,eAAe,SAAS,MAAM;AACpC,IAAM,iBAAiB,SAAS,KAAK;AACrC,IAAM,kBAAkB,SAAS,MAAM;AAEvC,IAAM,sBAAsBA,GAAE,MAAM,kBAAkB,EAAE,IAAI,CAAC;","names":["z"]}
@@ -1,28 +0,0 @@
1
- // src/store/driver.ts
2
- var RecordExistsError = class extends Error {
3
- constructor(collection, id) {
4
- super(`record "${id}" already exists in "${collection}"`);
5
- this.name = "RecordExistsError";
6
- }
7
- };
8
- var RecordNotFoundError = class extends Error {
9
- constructor(collection, id) {
10
- super(`record "${id}" not found in "${collection}"`);
11
- this.name = "RecordNotFoundError";
12
- }
13
- };
14
- var VersionConflictError = class extends Error {
15
- constructor(collection, id, expectedVersion) {
16
- super(
17
- `record "${id}" in "${collection}" changed since version ${expectedVersion} was read`
18
- );
19
- this.name = "VersionConflictError";
20
- }
21
- };
22
-
23
- export {
24
- RecordExistsError,
25
- RecordNotFoundError,
26
- VersionConflictError
27
- };
28
- //# sourceMappingURL=chunk-CEF6VJZC.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/store/driver.ts"],"sourcesContent":["export type CollectionName = \"repositories\" | \"signals\" | \"tasks\" | \"changes\" | \"receipts\";\n\nexport interface StoredRecord {\n readonly value: unknown;\n readonly version: number;\n}\n\nexport interface StoredEntry {\n readonly id: string;\n readonly record: StoredRecord;\n}\n\nexport class RecordExistsError extends Error {\n constructor(collection: CollectionName, id: string) {\n super(`record \"${id}\" already exists in \"${collection}\"`);\n this.name = \"RecordExistsError\";\n }\n}\n\nexport class RecordNotFoundError extends Error {\n constructor(collection: CollectionName, id: string) {\n super(`record \"${id}\" not found in \"${collection}\"`);\n this.name = \"RecordNotFoundError\";\n }\n}\n\nexport class VersionConflictError extends Error {\n constructor(collection: CollectionName, id: string, expectedVersion: number) {\n super(\n `record \"${id}\" in \"${collection}\" changed since version ${expectedVersion} was read`,\n );\n this.name = \"VersionConflictError\";\n }\n}\n\n/**\n * Raw persistence contract. Drivers store opaque values and enforce nothing\n * about their content; every invariant lives in the engine above. `update`\n * must be an atomic compare-and-swap on `expectedVersion`.\n */\nexport interface StoreDriver {\n readonly name: string;\n get(collection: CollectionName, id: string): Promise<StoredRecord | null>;\n insert(collection: CollectionName, id: string, value: unknown): Promise<void>;\n update(\n collection: CollectionName,\n id: string,\n value: unknown,\n expectedVersion: number,\n ): Promise<void>;\n list(collection: CollectionName): Promise<readonly StoredEntry[]>;\n}\n"],"mappings":";AAYO,IAAM,oBAAN,cAAgC,MAAM;AAAA,EAC3C,YAAY,YAA4B,IAAY;AAClD,UAAM,WAAW,EAAE,wBAAwB,UAAU,GAAG;AACxD,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAC7C,YAAY,YAA4B,IAAY;AAClD,UAAM,WAAW,EAAE,mBAAmB,UAAU,GAAG;AACnD,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,uBAAN,cAAmC,MAAM;AAAA,EAC9C,YAAY,YAA4B,IAAY,iBAAyB;AAC3E;AAAA,MACE,WAAW,EAAE,SAAS,UAAU,2BAA2B,eAAe;AAAA,IAC5E;AACA,SAAK,OAAO;AAAA,EACd;AACF;","names":[]}
@@ -1,311 +0,0 @@
1
- // src/github.ts
2
- import { createHmac, timingSafeEqual } from "crypto";
3
- import { z } from "zod";
4
- var gitHubApiUrl = "https://api.github.com";
5
- var repositorySlugPattern = /^[^/\s]+\/[^/\s]+$/;
6
- var pullRequestSchema = z.object({
7
- number: z.number().int().positive(),
8
- html_url: z.url()
9
- });
10
- var reviewCommentSchema = z.object({
11
- id: z.number().int().positive(),
12
- body: z.string(),
13
- html_url: z.url(),
14
- user: z.object({ login: z.string().min(1) }),
15
- in_reply_to_id: z.number().int().positive().nullish()
16
- });
17
- var labelsSchema = z.array(z.object({ name: z.string().min(1) }));
18
- var pullRequestCandidateSchema = z.object({
19
- number: z.number().int().positive(),
20
- user: z.object({ type: z.string().min(1) }),
21
- head: z.object({ ref: z.string().min(1), repo: z.object({ full_name: z.string().min(1) }) })
22
- });
23
- var openPullRequestsSchema = z.array(pullRequestCandidateSchema);
24
- var pullRequestDetailSchema = pullRequestCandidateSchema.extend({
25
- html_url: z.url(),
26
- mergeable: z.boolean().nullable(),
27
- mergeable_state: z.string().min(1),
28
- head: pullRequestCandidateSchema.shape.head.extend({ sha: z.string().min(1) }),
29
- base: z.object({ ref: z.string().min(1), sha: z.string().min(1) })
30
- });
31
- var collaboratorPermissionSchema = z.object({
32
- permission: z.enum(["admin", "maintain", "write", "triage", "read", "none"])
33
- });
34
- var writePermissions = /* @__PURE__ */ new Set(["admin", "maintain", "write"]);
35
- var GitHubReviewCommentAuthorError = class extends Error {
36
- author;
37
- constructor(author) {
38
- super(`GitHub user ${author} does not have write access to the repository`);
39
- this.name = "GitHubReviewCommentAuthorError";
40
- this.author = author;
41
- }
42
- };
43
- async function createPullRequest(input) {
44
- const pull = pullRequestSchema.parse(
45
- await gitHubRequest(input, "/pulls", {
46
- method: "POST",
47
- body: JSON.stringify({
48
- title: input.title,
49
- head: input.head,
50
- base: input.base,
51
- ...input.body === void 0 ? {} : { body: input.body },
52
- ...input.draft === void 0 ? {} : { draft: input.draft }
53
- })
54
- })
55
- );
56
- return { prNumber: pull.number, prUrl: pull.html_url };
57
- }
58
- async function readReviewComment(input) {
59
- const comment = parseReviewComment(
60
- await gitHubRequest(input, `/pulls/comments/${positiveInteger(input.commentId, "commentId")}`)
61
- );
62
- await assertReviewCommentAuthor(input, comment.author);
63
- return comment;
64
- }
65
- async function answerReviewComment(input) {
66
- await readReviewComment(input);
67
- return parseReviewComment(
68
- await gitHubRequest(
69
- input,
70
- `/pulls/${positiveInteger(input.pullRequestNumber, "pullRequestNumber")}/comments/${positiveInteger(input.commentId, "commentId")}/replies`,
71
- { method: "POST", body: JSON.stringify({ body: input.body }) }
72
- )
73
- );
74
- }
75
- async function listConflictedPullRequests(input) {
76
- const pulls = openPullRequestsSchema.parse(
77
- await gitHubRequest(input, `/pulls?state=open&per_page=100`)
78
- );
79
- const candidates = pulls.filter(
80
- (pull) => pull.user.type === "Bot" && pull.head.ref.startsWith("factory/") && pull.head.repo.full_name.toLowerCase() === input.repository.toLowerCase()
81
- );
82
- const conflicted = [];
83
- for (const candidate of candidates) {
84
- const pull = pullRequestDetailSchema.parse(
85
- await gitHubRequest(input, `/pulls/${candidate.number}`)
86
- );
87
- if (pull.mergeable === false || pull.mergeable_state === "dirty") {
88
- conflicted.push({
89
- prNumber: pull.number,
90
- prUrl: pull.html_url,
91
- headBranch: pull.head.ref,
92
- headSha: pull.head.sha,
93
- baseBranch: pull.base.ref,
94
- baseSha: pull.base.sha
95
- });
96
- }
97
- }
98
- return conflicted;
99
- }
100
- async function addLabel(input) {
101
- return parseLabels(
102
- await gitHubRequest(
103
- input,
104
- `/issues/${positiveInteger(input.pullRequestNumber, "pullRequestNumber")}/labels`,
105
- { method: "POST", body: JSON.stringify({ labels: [input.label] }) }
106
- )
107
- );
108
- }
109
- async function removeLabel(input) {
110
- return parseLabels(
111
- await gitHubRequest(
112
- input,
113
- `/issues/${positiveInteger(input.pullRequestNumber, "pullRequestNumber")}/labels/${encodeURIComponent(input.label)}`,
114
- { method: "DELETE" }
115
- )
116
- );
117
- }
118
- async function gitHubRequest(input, path, init = {}) {
119
- if (!repositorySlugPattern.test(input.repository)) {
120
- throw new Error(`invalid GitHub repository slug: ${input.repository}`);
121
- }
122
- const response = await (input.fetch ?? globalThis.fetch)(
123
- `${gitHubApiUrl}/repos/${input.repository}${path}`,
124
- {
125
- ...init,
126
- headers: {
127
- Accept: "application/vnd.github+json",
128
- Authorization: `Bearer ${input.token}`,
129
- "Content-Type": "application/json",
130
- "X-GitHub-Api-Version": "2022-11-28",
131
- ...init.headers
132
- }
133
- }
134
- );
135
- const body = await response.json();
136
- if (!response.ok) {
137
- const message = z.object({ message: z.string() }).safeParse(body);
138
- throw new Error(
139
- `GitHub API request failed (${response.status}): ${message.success ? message.data.message : response.statusText}`
140
- );
141
- }
142
- return body;
143
- }
144
- function parseReviewComment(value) {
145
- const comment = reviewCommentSchema.parse(value);
146
- return {
147
- id: comment.id,
148
- body: comment.body,
149
- url: comment.html_url,
150
- author: comment.user.login,
151
- inReplyToId: comment.in_reply_to_id ?? null
152
- };
153
- }
154
- function parseLabels(value) {
155
- return labelsSchema.parse(value).map(({ name }) => name);
156
- }
157
- async function assertReviewCommentAuthor(input, author) {
158
- if (input.authorPolicy === "any") {
159
- return;
160
- }
161
- const { permission } = collaboratorPermissionSchema.parse(
162
- await gitHubRequest(input, `/collaborators/${encodeURIComponent(author)}/permission`)
163
- );
164
- if (!writePermissions.has(permission)) {
165
- throw new GitHubReviewCommentAuthorError(author);
166
- }
167
- }
168
- function positiveInteger(value, name) {
169
- if (!Number.isInteger(value) || value < 1) {
170
- throw new Error(`${name} must be a positive integer`);
171
- }
172
- return value;
173
- }
174
- function verifyGitHubSignature(options) {
175
- const { secret, payload, signature } = options;
176
- if (signature === null || !signature.startsWith("sha256=")) {
177
- return false;
178
- }
179
- const expected = createHmac("sha256", secret).update(payload).digest("hex");
180
- const provided = signature.slice("sha256=".length);
181
- if (provided.length !== expected.length) {
182
- return false;
183
- }
184
- return timingSafeEqual(Buffer.from(provided, "hex"), Buffer.from(expected, "hex"));
185
- }
186
- var pushEventSchema = z.looseObject({
187
- ref: z.string().min(1),
188
- repository: z.looseObject({
189
- default_branch: z.string().min(1),
190
- full_name: z.string().min(1)
191
- })
192
- });
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}`) {
197
- return null;
198
- }
199
- return {
200
- source: "github_default_branch_push",
201
- deliveryId: options.deliveryId,
202
- repositorySlug: parsed.data.repository.full_name.toLowerCase(),
203
- defaultBranch: parsed.data.repository.default_branch
204
- };
205
- }
206
- var issueEventSchema = z.looseObject({
207
- action: z.string(),
208
- issue: z.looseObject({
209
- number: z.number().int().min(1),
210
- title: z.string(),
211
- body: z.string().nullable()
212
- }),
213
- repository: z.looseObject({
214
- full_name: z.string().min(1)
215
- })
216
- });
217
- var failedWorkflowConclusions = /* @__PURE__ */ new Set([
218
- "action_required",
219
- "failure",
220
- "stale",
221
- "startup_failure",
222
- "timed_out"
223
- ]);
224
- var workflowRunEventSchema = z.looseObject({
225
- action: z.string(),
226
- repository: z.looseObject({
227
- default_branch: z.string().min(1),
228
- full_name: z.string().min(1)
229
- }),
230
- workflow_run: z.looseObject({
231
- id: z.number().int().min(1),
232
- name: z.string().min(1),
233
- event: z.string().min(1),
234
- head_branch: z.string().min(1).nullable(),
235
- head_repository: z.looseObject({
236
- full_name: z.string().min(1)
237
- }).nullable(),
238
- head_sha: z.string().min(1),
239
- html_url: z.url(),
240
- conclusion: z.string().nullable(),
241
- run_attempt: z.number().int().min(1)
242
- })
243
- });
244
- function normalizeGitHubIssueOpened(options) {
245
- if (options.event !== "issues") {
246
- return null;
247
- }
248
- const parsed = issueEventSchema.safeParse(options.payload);
249
- if (!parsed.success || parsed.data.action !== "opened") {
250
- return null;
251
- }
252
- const slug = parsed.data.repository.full_name.toLowerCase();
253
- const { number, title, body } = parsed.data.issue;
254
- return {
255
- source: "github_issue",
256
- deliveryId: options.deliveryId,
257
- repositorySlug: slug,
258
- replyTo: { channel: "github", address: `${slug}#${number}` },
259
- issue: { number, title, body },
260
- payload: options.payload
261
- };
262
- }
263
- function normalizeGitHubWorkflowRunFailure(options) {
264
- if (options.event !== "workflow_run") {
265
- return null;
266
- }
267
- const parsed = workflowRunEventSchema.safeParse(options.payload);
268
- if (!parsed.success || parsed.data.action !== "completed") {
269
- return null;
270
- }
271
- const run = parsed.data.workflow_run;
272
- const conclusion = run.conclusion;
273
- if (conclusion === null || !failedWorkflowConclusions.has(conclusion)) {
274
- return null;
275
- }
276
- const slug = parsed.data.repository.full_name.toLowerCase();
277
- return {
278
- source: "github_ci_failure",
279
- deliveryId: options.deliveryId,
280
- repositoryDefaultBranch: parsed.data.repository.default_branch,
281
- repositorySlug: slug,
282
- replyTo: { channel: "github", address: `${slug}/actions/runs/${run.id}` },
283
- workflowRun: {
284
- id: run.id,
285
- name: run.name,
286
- trigger: run.event,
287
- branch: run.head_branch,
288
- headRepositorySlug: run.head_repository?.full_name.toLowerCase() ?? null,
289
- headSha: run.head_sha,
290
- url: run.html_url,
291
- conclusion,
292
- attempt: run.run_attempt
293
- },
294
- payload: options.payload
295
- };
296
- }
297
-
298
- export {
299
- GitHubReviewCommentAuthorError,
300
- createPullRequest,
301
- readReviewComment,
302
- answerReviewComment,
303
- listConflictedPullRequests,
304
- addLabel,
305
- removeLabel,
306
- verifyGitHubSignature,
307
- normalizeGitHubDefaultBranchPush,
308
- normalizeGitHubIssueOpened,
309
- normalizeGitHubWorkflowRunFailure
310
- };
311
- //# sourceMappingURL=chunk-D2XMABG4.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/github.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { z } from \"zod\";\nimport type { ReplyTo } from \"./schema/common\";\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 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 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/** 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 {\n source: \"github_issue\";\n deliveryId: string;\n repositorySlug: string;\n replyTo: ReplyTo;\n issue: { number: number; title: string; body: string | null };\n payload: unknown;\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 {\n source: \"github_ci_failure\";\n deliveryId: string;\n repositoryDefaultBranch: string;\n repositorySlug: string;\n replyTo: ReplyTo;\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 payload: unknown;\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: {\n deliveryId: string;\n event: string;\n payload: unknown;\n}): 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\n/** Normalize a completed, unsuccessful GitHub Actions run into a CI failure signal. */\nexport function normalizeGitHubWorkflowRunFailure(options: {\n deliveryId: string;\n event: string;\n payload: unknown;\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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","names":[]}