@fragno-dev/github-app-fragment 0.0.1

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 (104) hide show
  1. package/LICENSE.md +16 -0
  2. package/README.md +163 -0
  3. package/bin/run.js +5 -0
  4. package/dist/browser/client/react.d.ts +37 -0
  5. package/dist/browser/client/react.d.ts.map +1 -0
  6. package/dist/browser/client/react.js +166 -0
  7. package/dist/browser/client/react.js.map +1 -0
  8. package/dist/browser/client/solid.d.ts +35 -0
  9. package/dist/browser/client/solid.d.ts.map +1 -0
  10. package/dist/browser/client/solid.js +136 -0
  11. package/dist/browser/client/solid.js.map +1 -0
  12. package/dist/browser/client/svelte.d.ts +30 -0
  13. package/dist/browser/client/svelte.d.ts.map +1 -0
  14. package/dist/browser/client/svelte.js +134 -0
  15. package/dist/browser/client/svelte.js.map +1 -0
  16. package/dist/browser/client/vanilla.d.ts +16 -0
  17. package/dist/browser/client/vanilla.d.ts.map +1 -0
  18. package/dist/browser/client/vanilla.js +11 -0
  19. package/dist/browser/client/vanilla.js.map +1 -0
  20. package/dist/browser/client/vue.d.ts +33 -0
  21. package/dist/browser/client/vue.d.ts.map +1 -0
  22. package/dist/browser/client/vue.js +133 -0
  23. package/dist/browser/client/vue.js.map +1 -0
  24. package/dist/browser/factory-BIj4C6PD.js +2210 -0
  25. package/dist/browser/factory-BIj4C6PD.js.map +1 -0
  26. package/dist/browser/index.d.ts +343 -0
  27. package/dist/browser/index.d.ts.map +1 -0
  28. package/dist/browser/index.js +3 -0
  29. package/dist/browser/types-BzeSSOQU.d.ts +660 -0
  30. package/dist/browser/types-BzeSSOQU.d.ts.map +1 -0
  31. package/dist/cli/commands/installations.js +92 -0
  32. package/dist/cli/commands/installations.js.map +1 -0
  33. package/dist/cli/commands/pulls.js +123 -0
  34. package/dist/cli/commands/pulls.js.map +1 -0
  35. package/dist/cli/commands/repositories.js +105 -0
  36. package/dist/cli/commands/repositories.js.map +1 -0
  37. package/dist/cli/commands/serve.js +187 -0
  38. package/dist/cli/commands/serve.js.map +1 -0
  39. package/dist/cli/commands/webhooks.js +122 -0
  40. package/dist/cli/commands/webhooks.js.map +1 -0
  41. package/dist/cli/github/api.js +94 -0
  42. package/dist/cli/github/api.js.map +1 -0
  43. package/dist/cli/github/definition.js +15 -0
  44. package/dist/cli/github/definition.js.map +1 -0
  45. package/dist/cli/github/factory.js +12 -0
  46. package/dist/cli/github/factory.js.map +1 -0
  47. package/dist/cli/github/repo-sync.js +33 -0
  48. package/dist/cli/github/repo-sync.js.map +1 -0
  49. package/dist/cli/github/utils.js +23 -0
  50. package/dist/cli/github/utils.js.map +1 -0
  51. package/dist/cli/github/webhook-processing.js +247 -0
  52. package/dist/cli/github/webhook-processing.js.map +1 -0
  53. package/dist/cli/index.d.ts +5 -0
  54. package/dist/cli/index.d.ts.map +1 -0
  55. package/dist/cli/index.js +263 -0
  56. package/dist/cli/index.js.map +1 -0
  57. package/dist/cli/routes.js +718 -0
  58. package/dist/cli/routes.js.map +1 -0
  59. package/dist/cli/schema.js +47 -0
  60. package/dist/cli/schema.js.map +1 -0
  61. package/dist/cli/utils/client.js +120 -0
  62. package/dist/cli/utils/client.js.map +1 -0
  63. package/dist/cli/utils/config.js +113 -0
  64. package/dist/cli/utils/config.js.map +1 -0
  65. package/dist/cli/utils/options.js +90 -0
  66. package/dist/cli/utils/options.js.map +1 -0
  67. package/dist/cli/utils/output.js +12 -0
  68. package/dist/cli/utils/output.js.map +1 -0
  69. package/dist/node/github/api.d.ts +52 -0
  70. package/dist/node/github/api.d.ts.map +1 -0
  71. package/dist/node/github/api.js +94 -0
  72. package/dist/node/github/api.js.map +1 -0
  73. package/dist/node/github/clients.d.ts +19 -0
  74. package/dist/node/github/clients.d.ts.map +1 -0
  75. package/dist/node/github/clients.js +12 -0
  76. package/dist/node/github/clients.js.map +1 -0
  77. package/dist/node/github/definition.d.ts +33 -0
  78. package/dist/node/github/definition.d.ts.map +1 -0
  79. package/dist/node/github/definition.js +15 -0
  80. package/dist/node/github/definition.js.map +1 -0
  81. package/dist/node/github/factory.d.ts +139 -0
  82. package/dist/node/github/factory.d.ts.map +1 -0
  83. package/dist/node/github/factory.js +18 -0
  84. package/dist/node/github/factory.js.map +1 -0
  85. package/dist/node/github/repo-sync.js +33 -0
  86. package/dist/node/github/repo-sync.js.map +1 -0
  87. package/dist/node/github/types.d.ts +24 -0
  88. package/dist/node/github/types.d.ts.map +1 -0
  89. package/dist/node/github/utils.js +23 -0
  90. package/dist/node/github/utils.js.map +1 -0
  91. package/dist/node/github/webhook-processing.d.ts +15 -0
  92. package/dist/node/github/webhook-processing.d.ts.map +1 -0
  93. package/dist/node/github/webhook-processing.js +247 -0
  94. package/dist/node/github/webhook-processing.js.map +1 -0
  95. package/dist/node/index.d.ts +7 -0
  96. package/dist/node/index.js +6 -0
  97. package/dist/node/routes.d.ts +127 -0
  98. package/dist/node/routes.d.ts.map +1 -0
  99. package/dist/node/routes.js +718 -0
  100. package/dist/node/routes.js.map +1 -0
  101. package/dist/node/schema.js +47 -0
  102. package/dist/node/schema.js.map +1 -0
  103. package/dist/tsconfig.tsbuildinfo +1 -0
  104. package/package.json +114 -0
@@ -0,0 +1,247 @@
1
+ import { githubAppSchema } from "../schema.js";
2
+ import { hasRepoChanges, toRepoCreateRecord, toRepoRecord } from "./repo-sync.js";
3
+ import { normalizeJoinedLinks, toExternalId } from "./utils.js";
4
+
5
+ //#region src/github/webhook-processing.ts
6
+ const SUPPORTED_EVENTS = ["installation", "installation_repositories"];
7
+ function asSupportedWebhook(data) {
8
+ if (!SUPPORTED_EVENTS.includes(data.event)) return null;
9
+ return data;
10
+ }
11
+ const toEmitterWebhookEvent = (data) => {
12
+ return {
13
+ id: data.deliveryId,
14
+ name: data.event,
15
+ payload: data.payload
16
+ };
17
+ };
18
+ const createWebhookEventDispatcher = (configureOn) => {
19
+ const handlersByEvent = /* @__PURE__ */ new Map();
20
+ const registerOn = (event, handler) => {
21
+ const events = Array.isArray(event) ? event : [event];
22
+ for (const eventName of events) {
23
+ const handlers = handlersByEvent.get(eventName);
24
+ if (handlers) handlers.push(handler);
25
+ else handlersByEvent.set(eventName, [handler]);
26
+ }
27
+ };
28
+ configureOn?.(registerOn);
29
+ return async (event, idempotencyKey) => {
30
+ if (handlersByEvent.size === 0) return;
31
+ const action = "action" in event.payload && typeof event.payload.action === "string" ? event.payload.action : null;
32
+ const dispatchEvents = action ? [`${event.name}.${action}`, event.name] : [event.name];
33
+ const handlers = [];
34
+ for (const eventName of dispatchEvents) {
35
+ const registered = handlersByEvent.get(eventName);
36
+ if (registered && registered.length > 0) handlers.push(...registered);
37
+ }
38
+ if (handlers.length === 0) return;
39
+ const failures = (await Promise.allSettled(handlers.map(async (handler) => await handler(event, idempotencyKey)))).filter((result) => result.status === "rejected").map((result) => result.reason);
40
+ if (failures.length === 1) throw failures[0];
41
+ if (failures.length > 1) throw new AggregateError(failures, "One or more configured webhook handlers failed.");
42
+ };
43
+ };
44
+ const normalizeInstallationStatus = (action) => {
45
+ switch (action) {
46
+ case "created":
47
+ case "new_permissions_accepted":
48
+ case "unsuspend": return "active";
49
+ case "suspend": return "suspended";
50
+ case "deleted": return "deleted";
51
+ default: return null;
52
+ }
53
+ };
54
+ const resolveReceivedAt = (receivedAt) => {
55
+ if (!receivedAt) return /* @__PURE__ */ new Date();
56
+ const parsed = new Date(receivedAt);
57
+ return Number.isNaN(parsed.getTime()) ? /* @__PURE__ */ new Date() : parsed;
58
+ };
59
+ const parseOwnerLoginFromFullName = (fullName) => {
60
+ const separatorIndex = fullName.indexOf("/");
61
+ if (separatorIndex <= 0) return "";
62
+ return fullName.slice(0, separatorIndex);
63
+ };
64
+ const toInstallationRepoFromWebhookRepository = (repo) => {
65
+ const ownerLogin = parseOwnerLoginFromFullName(repo.full_name);
66
+ const mapped = {
67
+ id: repo.id,
68
+ name: repo.name,
69
+ full_name: repo.full_name,
70
+ private: repo.private,
71
+ owner: { login: ownerLogin }
72
+ };
73
+ if ("fork" in repo && typeof repo.fork === "boolean") mapped.fork = repo.fork;
74
+ if ("default_branch" in repo) {
75
+ const defaultBranch = repo.default_branch;
76
+ if (typeof defaultBranch === "string" || defaultBranch === null) mapped.default_branch = defaultBranch;
77
+ }
78
+ return mapped;
79
+ };
80
+ function extractAccountFromInstallation(installation) {
81
+ const account = installation.account;
82
+ if (!account) throw new Error("Webhook payload installation missing account");
83
+ const accountId = `${account.id}`;
84
+ if ("login" in account) return {
85
+ accountId,
86
+ accountLogin: account.login,
87
+ accountType: account.type ?? ""
88
+ };
89
+ return {
90
+ accountId,
91
+ accountLogin: account.slug,
92
+ accountType: "Enterprise"
93
+ };
94
+ }
95
+ const isRepoSyncAction = (action) => {
96
+ return action === "created" || action === "new_permissions_accepted";
97
+ };
98
+ const toRepoUpdateData = (record, now) => ({
99
+ installationId: record.installationId,
100
+ ownerLogin: record.ownerLogin,
101
+ name: record.name,
102
+ fullName: record.fullName,
103
+ isPrivate: record.isPrivate,
104
+ ...record.isFork !== void 0 ? { isFork: record.isFork } : {},
105
+ ...record.defaultBranch !== void 0 ? { defaultBranch: record.defaultBranch } : {},
106
+ removedAt: null,
107
+ updatedAt: now
108
+ });
109
+ const createWebhookProcessor = (config = {}) => {
110
+ const dispatchWebhookEvent = createWebhookEventDispatcher(config.webhook);
111
+ return async function processWebhook(data) {
112
+ const emitterEvent = toEmitterWebhookEvent(data);
113
+ const webhook = asSupportedWebhook(data);
114
+ if (!webhook) {
115
+ await dispatchWebhookEvent(emitterEvent, this.idempotencyKey);
116
+ return;
117
+ }
118
+ const { event, action, payload } = webhook;
119
+ const now = resolveReceivedAt(data.receivedAt);
120
+ const installationId = data.installationId;
121
+ const installation = payload.installation;
122
+ if (!installation) throw new Error("Webhook payload missing installation (required for installation events)");
123
+ const { accountId, accountLogin, accountType } = extractAccountFromInstallation(installation);
124
+ const statusOverride = normalizeInstallationStatus(action);
125
+ const needsRepoSync = event === "installation_repositories" || event === "installation" && isRepoSyncAction(action);
126
+ let existingInstallation = null;
127
+ let existingRepos = [];
128
+ if (needsRepoSync) [existingInstallation, existingRepos] = await this.handlerTx().retrieve(({ forSchema }) => {
129
+ return forSchema(githubAppSchema).findFirst("installation", (b) => b.whereIndex("uniq_installation_id", (eb) => eb("id", "=", installationId))).find("installation_repo", (b) => b.whereIndex("idx_installation_repo_installation", (eb) => eb("installationId", "=", installationId)).join((jb) => jb.links()));
130
+ }).execute();
131
+ else [existingInstallation] = await this.handlerTx().retrieve(({ forSchema }) => {
132
+ return forSchema(githubAppSchema).findFirst("installation", (b) => b.whereIndex("uniq_installation_id", (eb) => eb("id", "=", installationId)));
133
+ }).execute();
134
+ const existingStatus = existingInstallation && typeof existingInstallation.status === "string" ? existingInstallation.status : null;
135
+ const installationUpdate = {
136
+ accountId,
137
+ accountLogin,
138
+ accountType,
139
+ status: statusOverride || existingStatus || "active",
140
+ permissions: installation.permissions ?? existingInstallation?.permissions ?? null,
141
+ events: installation.events ?? existingInstallation?.events ?? null,
142
+ updatedAt: now,
143
+ lastWebhookAt: now
144
+ };
145
+ const creates = [];
146
+ const updates = [];
147
+ const removals = [];
148
+ const linksToDelete = [];
149
+ if (needsRepoSync) {
150
+ const existingById = /* @__PURE__ */ new Map();
151
+ for (const repo of existingRepos) {
152
+ const id = toExternalId(repo.id);
153
+ if (id) existingById.set(id, repo);
154
+ }
155
+ const repoLinksByRepoId = /* @__PURE__ */ new Map();
156
+ for (const repo of existingRepos) {
157
+ const repoId = toExternalId(repo.id);
158
+ if (!repoId) continue;
159
+ const linkEntries = normalizeJoinedLinks(repo.links);
160
+ if (linkEntries.length === 0) continue;
161
+ repoLinksByRepoId.set(repoId, linkEntries);
162
+ }
163
+ if (event === "installation") {
164
+ const repos = (payload.repositories ?? []).map(toInstallationRepoFromWebhookRepository);
165
+ const seen = /* @__PURE__ */ new Set();
166
+ for (const repo of repos) {
167
+ const record = toRepoRecord(installationId, repo, now);
168
+ seen.add(record.id);
169
+ const existing = existingById.get(record.id);
170
+ if (!existing) {
171
+ creates.push(record);
172
+ continue;
173
+ }
174
+ if (hasRepoChanges(existing, record)) updates.push({
175
+ id: existing.id,
176
+ data: toRepoUpdateData(record, now)
177
+ });
178
+ }
179
+ for (const [repoId, repo] of existingById.entries()) {
180
+ if (seen.has(repoId)) continue;
181
+ if (repo.removedAt === null) {
182
+ removals.push(repo.id);
183
+ const links = repoLinksByRepoId.get(repoId);
184
+ if (links) for (const link of links) linksToDelete.push(link.id);
185
+ }
186
+ }
187
+ }
188
+ if (event === "installation_repositories") {
189
+ const added = payload.repositories_added;
190
+ const removed = payload.repositories_removed;
191
+ for (const repo of added) {
192
+ const record = toRepoRecord(installationId, toInstallationRepoFromWebhookRepository(repo), now);
193
+ const existing = existingById.get(record.id);
194
+ if (!existing) {
195
+ creates.push(record);
196
+ continue;
197
+ }
198
+ if (hasRepoChanges(existing, record)) updates.push({
199
+ id: existing.id,
200
+ data: toRepoUpdateData(record, now)
201
+ });
202
+ }
203
+ for (const repo of removed) {
204
+ const repoId = toExternalId(repo.id) || null;
205
+ if (!repoId) continue;
206
+ const existing = existingById.get(repoId);
207
+ if (existing && existing.removedAt === null) {
208
+ removals.push(existing.id);
209
+ const links = repoLinksByRepoId.get(repoId);
210
+ if (links) for (const link of links) linksToDelete.push(link.id);
211
+ }
212
+ }
213
+ }
214
+ }
215
+ const hasRepoMutations = creates.length > 0 || updates.length > 0 || removals.length > 0 || linksToDelete.length > 0;
216
+ await this.handlerTx().mutate(({ forSchema }) => {
217
+ const uow = forSchema(githubAppSchema);
218
+ if (existingInstallation) uow.update("installation", installationId, (b) => b.set(installationUpdate));
219
+ else uow.create("installation", {
220
+ id: installationId,
221
+ accountId: installationUpdate.accountId,
222
+ accountLogin: installationUpdate.accountLogin,
223
+ accountType: installationUpdate.accountType,
224
+ status: installationUpdate.status,
225
+ permissions: installationUpdate.permissions,
226
+ events: installationUpdate.events,
227
+ createdAt: now,
228
+ updatedAt: now,
229
+ lastWebhookAt: now
230
+ });
231
+ if (hasRepoMutations) {
232
+ for (const record of creates) uow.create("installation_repo", toRepoCreateRecord(record));
233
+ for (const update of updates) uow.update("installation_repo", update.id, (b) => b.set(update.data));
234
+ for (const id of removals) uow.update("installation_repo", id, (b) => b.set({
235
+ removedAt: now,
236
+ updatedAt: now
237
+ }));
238
+ for (const id of linksToDelete) uow.delete("repo_link", id);
239
+ }
240
+ }).execute();
241
+ await dispatchWebhookEvent(emitterEvent, this.idempotencyKey);
242
+ };
243
+ };
244
+
245
+ //#endregion
246
+ export { createWebhookProcessor };
247
+ //# sourceMappingURL=webhook-processing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-processing.js","names":[],"sources":["../../../src/github/webhook-processing.ts"],"sourcesContent":["import type { TableToColumnValues } from \"@fragno-dev/db/query\";\n\nimport type { HookContext } from \"@fragno-dev/db\";\n\nimport type { EmitterWebhookEvent } from \"@octokit/webhooks\";\n\nimport { githubAppSchema } from \"../schema\";\nimport type { GitHubInstallationRepository } from \"./api\";\nimport {\n hasRepoChanges,\n type InstallationRepoRow,\n toRepoCreateRecord,\n toRepoRecord,\n} from \"./repo-sync\";\nimport type { GitHubAppFragmentConfig, GitHubAppWebhookConfig, GitHubAppWebhookOn } from \"./types\";\nimport { normalizeJoinedLinks, toExternalId } from \"./utils\";\n\ntype InstallationRow = TableToColumnValues<(typeof githubAppSchema)[\"tables\"][\"installation\"]>;\ntype RepoLinkRow = TableToColumnValues<(typeof githubAppSchema)[\"tables\"][\"repo_link\"]>;\ntype InstallationRepoId = InstallationRepoRow[\"id\"];\ntype RepoLinkId = RepoLinkRow[\"id\"];\n\ntype GitHubWebhookPayload<TEventName extends EmitterWebhookEvent[\"name\"]> = Extract<\n EmitterWebhookEvent,\n { name: TEventName }\n>[\"payload\"];\n\ntype InstallationPayload = GitHubWebhookPayload<\"installation\">;\ntype InstallationRepositoriesPayload = GitHubWebhookPayload<\"installation_repositories\">;\n\n/** Single discriminated union for supported events. One cast at entry derives all typed data. */\ntype SupportedWebhook =\n | { event: \"installation\"; action: string | null; payload: InstallationPayload }\n | {\n event: \"installation_repositories\";\n action: string | null;\n payload: InstallationRepositoriesPayload;\n };\n\nexport type WebhookProcessingPayload = {\n deliveryId: string;\n event: string;\n action: string | null;\n installationId: string;\n payload: Record<string, unknown>;\n receivedAt?: string | null;\n};\n\nconst SUPPORTED_EVENTS = [\"installation\", \"installation_repositories\"] as const;\n\nfunction asSupportedWebhook(data: WebhookProcessingPayload): SupportedWebhook | null {\n if (!SUPPORTED_EVENTS.includes(data.event as (typeof SUPPORTED_EVENTS)[number])) {\n return null;\n }\n return data as unknown as SupportedWebhook;\n}\n\ntype InternalWebhookHandler = (\n event: EmitterWebhookEvent,\n idempotencyKey: string,\n) => void | Promise<void>;\n\nconst toEmitterWebhookEvent = (data: WebhookProcessingPayload): EmitterWebhookEvent => {\n return {\n id: data.deliveryId,\n name: data.event as EmitterWebhookEvent[\"name\"],\n payload: data.payload as EmitterWebhookEvent[\"payload\"],\n } as EmitterWebhookEvent;\n};\n\nconst createWebhookEventDispatcher = (configureOn?: GitHubAppWebhookConfig) => {\n const handlersByEvent = new Map<string, InternalWebhookHandler[]>();\n\n const registerOn: GitHubAppWebhookOn = (event, handler) => {\n const events = Array.isArray(event) ? event : [event];\n for (const eventName of events) {\n const handlers = handlersByEvent.get(eventName);\n if (handlers) {\n handlers.push(handler as InternalWebhookHandler);\n } else {\n handlersByEvent.set(eventName, [handler as InternalWebhookHandler]);\n }\n }\n };\n\n configureOn?.(registerOn);\n\n return async (event: EmitterWebhookEvent, idempotencyKey: string) => {\n if (handlersByEvent.size === 0) {\n return;\n }\n\n const action =\n \"action\" in event.payload && typeof event.payload.action === \"string\"\n ? event.payload.action\n : null;\n const dispatchEvents = action ? [`${event.name}.${action}`, event.name] : [event.name];\n const handlers: InternalWebhookHandler[] = [];\n\n for (const eventName of dispatchEvents) {\n const registered = handlersByEvent.get(eventName);\n if (registered && registered.length > 0) {\n handlers.push(...registered);\n }\n }\n\n if (handlers.length === 0) {\n return;\n }\n\n const settled = await Promise.allSettled(\n handlers.map(async (handler) => await handler(event, idempotencyKey)),\n );\n const failures = settled\n .filter((result): result is PromiseRejectedResult => result.status === \"rejected\")\n .map((result) => result.reason);\n\n if (failures.length === 1) {\n throw failures[0];\n }\n if (failures.length > 1) {\n throw new AggregateError(failures, \"One or more configured webhook handlers failed.\");\n }\n };\n};\n\ntype WebhookRepoSnapshot =\n | NonNullable<InstallationPayload[\"repositories\"]>[number]\n | InstallationRepositoriesPayload[\"repositories_added\"][number];\n\nconst normalizeInstallationStatus = (action: string | null) => {\n switch (action) {\n case \"created\":\n case \"new_permissions_accepted\":\n case \"unsuspend\":\n return \"active\";\n case \"suspend\":\n return \"suspended\";\n case \"deleted\":\n return \"deleted\";\n default:\n return null;\n }\n};\n\nconst resolveReceivedAt = (receivedAt?: string | null) => {\n if (!receivedAt) {\n return new Date();\n }\n const parsed = new Date(receivedAt);\n return Number.isNaN(parsed.getTime()) ? new Date() : parsed;\n};\n\nconst parseOwnerLoginFromFullName = (fullName: string) => {\n const separatorIndex = fullName.indexOf(\"/\");\n if (separatorIndex <= 0) {\n return \"\";\n }\n return fullName.slice(0, separatorIndex);\n};\n\n// Webhook repo snapshots can omit `fork` and `default_branch`.\n// Leave them unset so updates do not overwrite previously synced values.\nconst toInstallationRepoFromWebhookRepository = (\n repo: WebhookRepoSnapshot,\n): GitHubInstallationRepository => {\n const ownerLogin = parseOwnerLoginFromFullName(repo.full_name);\n const mapped: GitHubInstallationRepository = {\n id: repo.id,\n name: repo.name,\n full_name: repo.full_name,\n private: repo.private,\n owner: { login: ownerLogin },\n };\n if (\"fork\" in repo && typeof repo.fork === \"boolean\") {\n mapped.fork = repo.fork;\n }\n if (\"default_branch\" in repo) {\n const defaultBranch = repo.default_branch;\n if (typeof defaultBranch === \"string\" || defaultBranch === null) {\n mapped.default_branch = defaultBranch;\n }\n }\n return mapped;\n};\n\nfunction extractAccountFromInstallation(\n installation: NonNullable<SupportedWebhook[\"payload\"][\"installation\"]>,\n) {\n const account = installation.account;\n if (!account) {\n throw new Error(\"Webhook payload installation missing account\");\n }\n const accountId = `${account.id}`;\n if (\"login\" in account) {\n return { accountId, accountLogin: account.login, accountType: account.type ?? \"\" };\n }\n return { accountId, accountLogin: account.slug, accountType: \"Enterprise\" };\n}\n\nconst isRepoSyncAction = (action: string | null) => {\n return action === \"created\" || action === \"new_permissions_accepted\";\n};\n\ntype RepoUpdateData = Omit<ReturnType<typeof toRepoRecord>, \"id\">;\n\nconst toRepoUpdateData = (\n record: ReturnType<typeof toRepoRecord>,\n now: Date,\n): Partial<RepoUpdateData> => ({\n installationId: record.installationId,\n ownerLogin: record.ownerLogin,\n name: record.name,\n fullName: record.fullName,\n isPrivate: record.isPrivate,\n ...(record.isFork !== undefined ? { isFork: record.isFork } : {}),\n ...(record.defaultBranch !== undefined ? { defaultBranch: record.defaultBranch } : {}),\n removedAt: null,\n updatedAt: now,\n});\n\nexport const createWebhookProcessor = (config: Pick<GitHubAppFragmentConfig, \"webhook\"> = {}) => {\n const dispatchWebhookEvent = createWebhookEventDispatcher(config.webhook);\n\n return async function processWebhook(this: HookContext, data: WebhookProcessingPayload) {\n const emitterEvent = toEmitterWebhookEvent(data);\n const webhook = asSupportedWebhook(data);\n if (!webhook) {\n await dispatchWebhookEvent(emitterEvent, this.idempotencyKey);\n return;\n }\n\n const { event, action, payload } = webhook;\n const now = resolveReceivedAt(data.receivedAt);\n const installationId = data.installationId;\n\n const installation = payload.installation;\n if (!installation) {\n throw new Error(\"Webhook payload missing installation (required for installation events)\");\n }\n const { accountId, accountLogin, accountType } = extractAccountFromInstallation(installation);\n\n const statusOverride = normalizeInstallationStatus(action);\n const needsRepoSync =\n event === \"installation_repositories\" ||\n (event === \"installation\" && isRepoSyncAction(action));\n\n let existingInstallation: InstallationRow | null = null;\n let existingRepos: Array<InstallationRepoRow & { links?: RepoLinkRow | RepoLinkRow[] }> = [];\n\n if (needsRepoSync) {\n [existingInstallation, existingRepos] = await this.handlerTx()\n .retrieve(({ forSchema }) => {\n const uow = forSchema(githubAppSchema);\n return uow\n .findFirst(\"installation\", (b) =>\n b.whereIndex(\"uniq_installation_id\", (eb) => eb(\"id\", \"=\", installationId)),\n )\n .find(\"installation_repo\", (b) =>\n b\n .whereIndex(\"idx_installation_repo_installation\", (eb) =>\n eb(\"installationId\", \"=\", installationId),\n )\n .join((jb) => jb.links()),\n );\n })\n .execute();\n } else {\n [existingInstallation] = await this.handlerTx()\n .retrieve(({ forSchema }) => {\n const uow = forSchema(githubAppSchema);\n return uow.findFirst(\"installation\", (b) =>\n b.whereIndex(\"uniq_installation_id\", (eb) => eb(\"id\", \"=\", installationId)),\n );\n })\n .execute();\n }\n\n const existingStatus =\n existingInstallation && typeof existingInstallation.status === \"string\"\n ? existingInstallation.status\n : null;\n const installationUpdate = {\n accountId,\n accountLogin,\n accountType,\n status: statusOverride || existingStatus || \"active\",\n permissions: installation.permissions ?? existingInstallation?.permissions ?? null,\n events: installation.events ?? existingInstallation?.events ?? null,\n updatedAt: now,\n lastWebhookAt: now,\n };\n\n const creates: Array<ReturnType<typeof toRepoRecord>> = [];\n const updates: Array<{ id: InstallationRepoId; data: Partial<RepoUpdateData> }> = [];\n const removals: Array<InstallationRepoId> = [];\n const linksToDelete: Array<RepoLinkId> = [];\n\n if (needsRepoSync) {\n const existingById = new Map<string, InstallationRepoRow>();\n for (const repo of existingRepos) {\n const id = toExternalId(repo.id);\n if (id) {\n existingById.set(id, repo);\n }\n }\n\n const repoLinksByRepoId = new Map<string, RepoLinkRow[]>();\n for (const repo of existingRepos) {\n const repoId = toExternalId(repo.id);\n if (!repoId) {\n continue;\n }\n const linkEntries = normalizeJoinedLinks(repo.links);\n if (linkEntries.length === 0) {\n continue;\n }\n repoLinksByRepoId.set(repoId, linkEntries);\n }\n\n if (event === \"installation\") {\n // `repositories` is optional in the webhook payload — absent means no repos\n // were selected during installation, not that the data was truncated.\n const repos = (payload.repositories ?? []).map(toInstallationRepoFromWebhookRepository);\n const seen = new Set<string>();\n\n for (const repo of repos) {\n const record = toRepoRecord(installationId, repo, now);\n seen.add(record.id);\n const existing = existingById.get(record.id);\n\n if (!existing) {\n creates.push(record);\n continue;\n }\n\n if (hasRepoChanges(existing, record)) {\n updates.push({ id: existing.id, data: toRepoUpdateData(record, now) });\n }\n }\n\n for (const [repoId, repo] of existingById.entries()) {\n if (seen.has(repoId)) {\n continue;\n }\n if (repo.removedAt === null) {\n removals.push(repo.id);\n const links = repoLinksByRepoId.get(repoId);\n if (links) {\n for (const link of links) {\n linksToDelete.push(link.id);\n }\n }\n }\n }\n }\n\n if (event === \"installation_repositories\") {\n const added = payload.repositories_added;\n const removed = payload.repositories_removed;\n\n for (const repo of added) {\n const record = toRepoRecord(\n installationId,\n toInstallationRepoFromWebhookRepository(repo),\n now,\n );\n const existing = existingById.get(record.id);\n\n if (!existing) {\n creates.push(record);\n continue;\n }\n\n if (hasRepoChanges(existing, record)) {\n updates.push({ id: existing.id, data: toRepoUpdateData(record, now) });\n }\n }\n\n for (const repo of removed) {\n const repoId = toExternalId(repo.id) || null;\n if (!repoId) {\n continue;\n }\n const existing = existingById.get(repoId);\n if (existing && existing.removedAt === null) {\n removals.push(existing.id);\n const links = repoLinksByRepoId.get(repoId);\n if (links) {\n for (const link of links) {\n linksToDelete.push(link.id);\n }\n }\n }\n }\n }\n }\n\n const hasRepoMutations =\n creates.length > 0 || updates.length > 0 || removals.length > 0 || linksToDelete.length > 0;\n\n await this.handlerTx()\n .mutate(({ forSchema }) => {\n const uow = forSchema(githubAppSchema);\n\n if (existingInstallation) {\n uow.update(\"installation\", installationId, (b) => b.set(installationUpdate));\n } else {\n uow.create(\"installation\", {\n id: installationId,\n accountId: installationUpdate.accountId,\n accountLogin: installationUpdate.accountLogin,\n accountType: installationUpdate.accountType,\n status: installationUpdate.status,\n permissions: installationUpdate.permissions,\n events: installationUpdate.events,\n createdAt: now,\n updatedAt: now,\n lastWebhookAt: now,\n });\n }\n\n if (hasRepoMutations) {\n for (const record of creates) {\n uow.create(\"installation_repo\", toRepoCreateRecord(record));\n }\n\n for (const update of updates) {\n uow.update(\"installation_repo\", update.id, (b) => b.set(update.data));\n }\n\n for (const id of removals) {\n uow.update(\"installation_repo\", id, (b) => b.set({ removedAt: now, updatedAt: now }));\n }\n\n for (const id of linksToDelete) {\n uow.delete(\"repo_link\", id);\n }\n }\n })\n .execute();\n await dispatchWebhookEvent(emitterEvent, this.idempotencyKey);\n };\n};\n"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { GitHubAppFragmentConfig, GitHubAppFragmentPublicClientConfig, GitHubAppWebhookConfig, GitHubAppWebhookHandler, GitHubAppWebhookOn } from "./github/types.js";
2
+ import { createGitHubAppFragmentClients } from "./github/clients.js";
3
+ import { GitHubAppFragmentDependencies, GitHubAppFragmentServices, githubAppFragmentDefinition } from "./github/definition.js";
4
+ import { createGitHubAppFragment, getGitHubApiClientFromFragment, getGitHubAppFromFragment } from "./github/factory.js";
5
+ import { githubAppRoutesFactory } from "./routes.js";
6
+ import { FragnoRouteConfig } from "@fragno-dev/core";
7
+ export { type FragnoRouteConfig, type GitHubAppFragmentConfig, type GitHubAppFragmentDependencies, type GitHubAppFragmentPublicClientConfig, type GitHubAppFragmentServices, type GitHubAppWebhookConfig, type GitHubAppWebhookHandler, type GitHubAppWebhookOn, createGitHubAppFragment, createGitHubAppFragmentClients, getGitHubApiClientFromFragment, getGitHubAppFromFragment, githubAppFragmentDefinition, githubAppRoutesFactory };
@@ -0,0 +1,6 @@
1
+ import { githubAppFragmentDefinition } from "./github/definition.js";
2
+ import { githubAppRoutesFactory } from "./routes.js";
3
+ import { createGitHubAppFragmentClients } from "./github/clients.js";
4
+ import { createGitHubAppFragment, getGitHubApiClientFromFragment, getGitHubAppFromFragment } from "./github/factory.js";
5
+
6
+ export { createGitHubAppFragment, createGitHubAppFragmentClients, getGitHubApiClientFromFragment, getGitHubAppFromFragment, githubAppFragmentDefinition, githubAppRoutesFactory };
@@ -0,0 +1,127 @@
1
+ import { GitHubAppFragmentConfig } from "./github/types.js";
2
+ import { GitHubApiClient, GitHubAppInstance } from "./github/api.js";
3
+ import { WebhookProcessingPayload } from "./github/webhook-processing.js";
4
+ import "./index.js";
5
+ import * as _fragno_dev_db_schema0 from "@fragno-dev/db/schema";
6
+ import { FragnoId } from "@fragno-dev/db/schema";
7
+ import { z } from "zod";
8
+ import * as _fragno_dev_core0 from "@fragno-dev/core";
9
+ import * as _fragno_dev_db0 from "@fragno-dev/db";
10
+ import * as _standard_schema_spec0 from "@standard-schema/spec";
11
+
12
+ //#region src/routes.d.ts
13
+ declare const githubAppRoutesFactory: _fragno_dev_core0.RouteFactory<GitHubAppFragmentConfig, {
14
+ githubApiClient: GitHubApiClient;
15
+ } & _fragno_dev_db0.ImplicitDatabaseDependencies<_fragno_dev_db_schema0.Schema<{
16
+ installation: _fragno_dev_db_schema0.Table<Record<"id", _fragno_dev_db_schema0.IdColumn<"varchar(128)", string | FragnoId | null, FragnoId>> & Record<"accountId", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"accountLogin", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"accountType", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"status", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"permissions", _fragno_dev_db_schema0.Column<"json", unknown, unknown>> & Record<"events", _fragno_dev_db_schema0.Column<"json", unknown, unknown>> & Record<"createdAt", _fragno_dev_db_schema0.Column<"timestamp", (Date | _fragno_dev_db0.DbNow) | null, Date>> & Record<"updatedAt", _fragno_dev_db_schema0.Column<"timestamp", (Date | _fragno_dev_db0.DbNow) | null, Date>> & Record<"lastWebhookAt", _fragno_dev_db_schema0.Column<"timestamp", (Date | _fragno_dev_db0.DbNow) | null, Date | null>>, Record<string, _fragno_dev_db_schema0.AnyRelation>, Record<string, _fragno_dev_db_schema0.Index<_fragno_dev_db_schema0.AnyColumn[], readonly string[]>> & Record<"idx_installation_account_login", _fragno_dev_db_schema0.Index<readonly [_fragno_dev_db_schema0.Column<"string", string, string>] & _fragno_dev_db_schema0.AnyColumn[], readonly ["accountLogin"]>> & Record<"idx_installation_status", _fragno_dev_db_schema0.Index<readonly [_fragno_dev_db_schema0.Column<"string", string, string>] & _fragno_dev_db_schema0.AnyColumn[], readonly ["status"]>> & Record<"uniq_installation_id", _fragno_dev_db_schema0.Index<readonly [_fragno_dev_db_schema0.IdColumn<"varchar(128)", string | FragnoId | null, FragnoId>] & _fragno_dev_db_schema0.AnyColumn[], readonly ["id"]>>>;
17
+ installation_repo: _fragno_dev_db_schema0.Table<Record<"id", _fragno_dev_db_schema0.IdColumn<"varchar(128)", string | FragnoId | null, FragnoId>> & Record<"installationId", _fragno_dev_db_schema0.Column<"bigint", string | bigint | FragnoId | _fragno_dev_db_schema0.FragnoReference, _fragno_dev_db_schema0.FragnoReference>> & Record<"ownerLogin", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"name", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"fullName", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"isPrivate", _fragno_dev_db_schema0.Column<"bool", boolean, boolean>> & Record<"isFork", _fragno_dev_db_schema0.Column<"bool", boolean | null, boolean | null>> & Record<"defaultBranch", _fragno_dev_db_schema0.Column<"string", string | null, string | null>> & Record<"removedAt", _fragno_dev_db_schema0.Column<"timestamp", (Date | _fragno_dev_db0.DbNow) | null, Date | null>> & Record<"updatedAt", _fragno_dev_db_schema0.Column<"timestamp", (Date | _fragno_dev_db0.DbNow) | null, Date>>, Record<string, _fragno_dev_db_schema0.AnyRelation> & Record<"installation", _fragno_dev_db_schema0.Relation<"one", _fragno_dev_db_schema0.Table<Record<"id", _fragno_dev_db_schema0.IdColumn<"varchar(128)", string | FragnoId | null, FragnoId>> & Record<"accountId", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"accountLogin", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"accountType", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"status", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"permissions", _fragno_dev_db_schema0.Column<"json", unknown, unknown>> & Record<"events", _fragno_dev_db_schema0.Column<"json", unknown, unknown>> & Record<"createdAt", _fragno_dev_db_schema0.Column<"timestamp", (Date | _fragno_dev_db0.DbNow) | null, Date>> & Record<"updatedAt", _fragno_dev_db_schema0.Column<"timestamp", (Date | _fragno_dev_db0.DbNow) | null, Date>> & Record<"lastWebhookAt", _fragno_dev_db_schema0.Column<"timestamp", (Date | _fragno_dev_db0.DbNow) | null, Date | null>>, Record<string, _fragno_dev_db_schema0.AnyRelation>, Record<string, _fragno_dev_db_schema0.Index<_fragno_dev_db_schema0.AnyColumn[], readonly string[]>> & Record<"idx_installation_account_login", _fragno_dev_db_schema0.Index<readonly [_fragno_dev_db_schema0.Column<"string", string, string>] & _fragno_dev_db_schema0.AnyColumn[], readonly ["accountLogin"]>> & Record<"idx_installation_status", _fragno_dev_db_schema0.Index<readonly [_fragno_dev_db_schema0.Column<"string", string, string>] & _fragno_dev_db_schema0.AnyColumn[], readonly ["status"]>> & Record<"uniq_installation_id", _fragno_dev_db_schema0.Index<readonly [_fragno_dev_db_schema0.IdColumn<"varchar(128)", string | FragnoId | null, FragnoId>] & _fragno_dev_db_schema0.AnyColumn[], readonly ["id"]>>>>> & Record<"links", _fragno_dev_db_schema0.Relation<"many", _fragno_dev_db_schema0.Table<Record<"id", _fragno_dev_db_schema0.IdColumn<"varchar(128)", string | FragnoId | null, FragnoId>> & Record<"repoId", _fragno_dev_db_schema0.Column<"bigint", string | bigint | FragnoId | _fragno_dev_db_schema0.FragnoReference, _fragno_dev_db_schema0.FragnoReference>> & Record<"linkKey", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"linkedAt", _fragno_dev_db_schema0.Column<"timestamp", (Date | _fragno_dev_db0.DbNow) | null, Date>>, Record<string, _fragno_dev_db_schema0.AnyRelation>, Record<string, _fragno_dev_db_schema0.Index<_fragno_dev_db_schema0.AnyColumn[], readonly string[]>> & Record<"uniq_repo_link_repo_id_link_key", _fragno_dev_db_schema0.Index<readonly [_fragno_dev_db_schema0.Column<"bigint", string | bigint | FragnoId | _fragno_dev_db_schema0.FragnoReference, _fragno_dev_db_schema0.FragnoReference>, _fragno_dev_db_schema0.Column<"string", string, string>] & _fragno_dev_db_schema0.AnyColumn[], readonly ["repoId", "linkKey"]>>>>>, Record<string, _fragno_dev_db_schema0.Index<_fragno_dev_db_schema0.AnyColumn[], readonly string[]>> & Record<"idx_installation_repo_installation", _fragno_dev_db_schema0.Index<readonly [_fragno_dev_db_schema0.Column<"bigint", string | bigint | FragnoId | _fragno_dev_db_schema0.FragnoReference, _fragno_dev_db_schema0.FragnoReference>] & _fragno_dev_db_schema0.AnyColumn[], readonly ["installationId"]>> & Record<"idx_installation_repo_full_name", _fragno_dev_db_schema0.Index<readonly [_fragno_dev_db_schema0.Column<"string", string, string>] & _fragno_dev_db_schema0.AnyColumn[], readonly ["fullName"]>>>;
18
+ repo_link: _fragno_dev_db_schema0.Table<Record<"id", _fragno_dev_db_schema0.IdColumn<"varchar(128)", string | FragnoId | null, FragnoId>> & Record<"repoId", _fragno_dev_db_schema0.Column<"bigint", string | bigint | FragnoId | _fragno_dev_db_schema0.FragnoReference, _fragno_dev_db_schema0.FragnoReference>> & Record<"linkKey", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"linkedAt", _fragno_dev_db_schema0.Column<"timestamp", (Date | _fragno_dev_db0.DbNow) | null, Date>>, Record<string, _fragno_dev_db_schema0.AnyRelation> & Record<"repo", _fragno_dev_db_schema0.Relation<"one", _fragno_dev_db_schema0.Table<Record<"id", _fragno_dev_db_schema0.IdColumn<"varchar(128)", string | FragnoId | null, FragnoId>> & Record<"installationId", _fragno_dev_db_schema0.Column<"bigint", string | bigint | FragnoId | _fragno_dev_db_schema0.FragnoReference, _fragno_dev_db_schema0.FragnoReference>> & Record<"ownerLogin", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"name", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"fullName", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"isPrivate", _fragno_dev_db_schema0.Column<"bool", boolean, boolean>> & Record<"isFork", _fragno_dev_db_schema0.Column<"bool", boolean | null, boolean | null>> & Record<"defaultBranch", _fragno_dev_db_schema0.Column<"string", string | null, string | null>> & Record<"removedAt", _fragno_dev_db_schema0.Column<"timestamp", (Date | _fragno_dev_db0.DbNow) | null, Date | null>> & Record<"updatedAt", _fragno_dev_db_schema0.Column<"timestamp", (Date | _fragno_dev_db0.DbNow) | null, Date>>, Record<string, _fragno_dev_db_schema0.AnyRelation> & Record<"installation", _fragno_dev_db_schema0.Relation<"one", _fragno_dev_db_schema0.Table<Record<"id", _fragno_dev_db_schema0.IdColumn<"varchar(128)", string | FragnoId | null, FragnoId>> & Record<"accountId", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"accountLogin", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"accountType", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"status", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"permissions", _fragno_dev_db_schema0.Column<"json", unknown, unknown>> & Record<"events", _fragno_dev_db_schema0.Column<"json", unknown, unknown>> & Record<"createdAt", _fragno_dev_db_schema0.Column<"timestamp", (Date | _fragno_dev_db0.DbNow) | null, Date>> & Record<"updatedAt", _fragno_dev_db_schema0.Column<"timestamp", (Date | _fragno_dev_db0.DbNow) | null, Date>> & Record<"lastWebhookAt", _fragno_dev_db_schema0.Column<"timestamp", (Date | _fragno_dev_db0.DbNow) | null, Date | null>>, Record<string, _fragno_dev_db_schema0.AnyRelation>, Record<string, _fragno_dev_db_schema0.Index<_fragno_dev_db_schema0.AnyColumn[], readonly string[]>> & Record<"idx_installation_account_login", _fragno_dev_db_schema0.Index<readonly [_fragno_dev_db_schema0.Column<"string", string, string>] & _fragno_dev_db_schema0.AnyColumn[], readonly ["accountLogin"]>> & Record<"idx_installation_status", _fragno_dev_db_schema0.Index<readonly [_fragno_dev_db_schema0.Column<"string", string, string>] & _fragno_dev_db_schema0.AnyColumn[], readonly ["status"]>> & Record<"uniq_installation_id", _fragno_dev_db_schema0.Index<readonly [_fragno_dev_db_schema0.IdColumn<"varchar(128)", string | FragnoId | null, FragnoId>] & _fragno_dev_db_schema0.AnyColumn[], readonly ["id"]>>>>> & Record<"links", _fragno_dev_db_schema0.Relation<"many", _fragno_dev_db_schema0.Table<Record<"id", _fragno_dev_db_schema0.IdColumn<"varchar(128)", string | FragnoId | null, FragnoId>> & Record<"repoId", _fragno_dev_db_schema0.Column<"bigint", string | bigint | FragnoId | _fragno_dev_db_schema0.FragnoReference, _fragno_dev_db_schema0.FragnoReference>> & Record<"linkKey", _fragno_dev_db_schema0.Column<"string", string, string>> & Record<"linkedAt", _fragno_dev_db_schema0.Column<"timestamp", (Date | _fragno_dev_db0.DbNow) | null, Date>>, Record<string, _fragno_dev_db_schema0.AnyRelation>, Record<string, _fragno_dev_db_schema0.Index<_fragno_dev_db_schema0.AnyColumn[], readonly string[]>> & Record<"uniq_repo_link_repo_id_link_key", _fragno_dev_db_schema0.Index<readonly [_fragno_dev_db_schema0.Column<"bigint", string | bigint | FragnoId | _fragno_dev_db_schema0.FragnoReference, _fragno_dev_db_schema0.FragnoReference>, _fragno_dev_db_schema0.Column<"string", string, string>] & _fragno_dev_db_schema0.AnyColumn[], readonly ["repoId", "linkKey"]>>>>>, Record<string, _fragno_dev_db_schema0.Index<_fragno_dev_db_schema0.AnyColumn[], readonly string[]>> & Record<"idx_installation_repo_installation", _fragno_dev_db_schema0.Index<readonly [_fragno_dev_db_schema0.Column<"bigint", string | bigint | FragnoId | _fragno_dev_db_schema0.FragnoReference, _fragno_dev_db_schema0.FragnoReference>] & _fragno_dev_db_schema0.AnyColumn[], readonly ["installationId"]>> & Record<"idx_installation_repo_full_name", _fragno_dev_db_schema0.Index<readonly [_fragno_dev_db_schema0.Column<"string", string, string>] & _fragno_dev_db_schema0.AnyColumn[], readonly ["fullName"]>>>>>, Record<string, _fragno_dev_db_schema0.Index<_fragno_dev_db_schema0.AnyColumn[], readonly string[]>> & Record<"uniq_repo_link_repo_id_link_key", _fragno_dev_db_schema0.Index<readonly [_fragno_dev_db_schema0.Column<"bigint", string | bigint | FragnoId | _fragno_dev_db_schema0.FragnoReference, _fragno_dev_db_schema0.FragnoReference>, _fragno_dev_db_schema0.Column<"string", string, string>] & _fragno_dev_db_schema0.AnyColumn[], readonly ["repoId", "linkKey"]>>>;
19
+ }>>, _fragno_dev_core0.BoundServices<{
20
+ app: GitHubAppInstance;
21
+ githubApiClient: GitHubApiClient;
22
+ }>, {}, readonly [_fragno_dev_core0.FragnoRouteConfig<"POST", "/webhooks", _standard_schema_spec0.StandardSchemaV1<unknown, unknown> | undefined, _standard_schema_spec0.StandardSchemaV1<unknown, unknown> | undefined, "WEBHOOK_SIGNATURE_INVALID" | "WEBHOOK_DELIVERY_MISSING" | "WEBHOOK_PAYLOAD_INVALID", string, _fragno_dev_db0.DatabaseRequestContext<{
23
+ processWebhook: _fragno_dev_db0.HookFn<WebhookProcessingPayload>;
24
+ }>>, _fragno_dev_core0.FragnoRouteConfig<"GET", "/installations", _standard_schema_spec0.StandardSchemaV1<unknown, unknown> | undefined, z.ZodArray<z.ZodObject<{
25
+ id: z.ZodString;
26
+ accountId: z.ZodString;
27
+ accountLogin: z.ZodString;
28
+ accountType: z.ZodString;
29
+ status: z.ZodString;
30
+ permissions: z.ZodAny;
31
+ events: z.ZodAny;
32
+ createdAt: z.ZodDate;
33
+ updatedAt: z.ZodDate;
34
+ lastWebhookAt: z.ZodNullable<z.ZodDate>;
35
+ }, z.core.$strip>>, "INVALID_STATUS", "status", _fragno_dev_db0.DatabaseRequestContext<{
36
+ processWebhook: _fragno_dev_db0.HookFn<WebhookProcessingPayload>;
37
+ }>>, _fragno_dev_core0.FragnoRouteConfig<"GET", "/installations/:installationId/repos", _standard_schema_spec0.StandardSchemaV1<unknown, unknown> | undefined, z.ZodArray<z.ZodObject<{
38
+ id: z.ZodString;
39
+ installationId: z.ZodString;
40
+ ownerLogin: z.ZodString;
41
+ name: z.ZodString;
42
+ fullName: z.ZodString;
43
+ isPrivate: z.ZodBoolean;
44
+ isFork: z.ZodNullable<z.ZodBoolean>;
45
+ defaultBranch: z.ZodNullable<z.ZodString>;
46
+ removedAt: z.ZodNullable<z.ZodDate>;
47
+ updatedAt: z.ZodDate;
48
+ linkKeys: z.ZodArray<z.ZodString>;
49
+ }, z.core.$strip>>, "INSTALLATION_NOT_FOUND", "linkKey" | "linkedOnly", _fragno_dev_db0.DatabaseRequestContext<{
50
+ processWebhook: _fragno_dev_db0.HookFn<WebhookProcessingPayload>;
51
+ }>>, _fragno_dev_core0.FragnoRouteConfig<"GET", "/repositories/linked", _standard_schema_spec0.StandardSchemaV1<unknown, unknown> | undefined, z.ZodArray<z.ZodObject<{
52
+ id: z.ZodString;
53
+ installationId: z.ZodString;
54
+ ownerLogin: z.ZodString;
55
+ name: z.ZodString;
56
+ fullName: z.ZodString;
57
+ isPrivate: z.ZodBoolean;
58
+ isFork: z.ZodNullable<z.ZodBoolean>;
59
+ defaultBranch: z.ZodNullable<z.ZodString>;
60
+ removedAt: z.ZodNullable<z.ZodDate>;
61
+ updatedAt: z.ZodDate;
62
+ linkKeys: z.ZodArray<z.ZodString>;
63
+ }, z.core.$strip>>, string, "linkKey", _fragno_dev_db0.DatabaseRequestContext<{
64
+ processWebhook: _fragno_dev_db0.HookFn<WebhookProcessingPayload>;
65
+ }>>, _fragno_dev_core0.FragnoRouteConfig<"POST", "/repositories/link", z.ZodObject<{
66
+ installationId: z.ZodString;
67
+ repoId: z.ZodString;
68
+ linkKey: z.ZodOptional<z.ZodString>;
69
+ }, z.core.$strip>, z.ZodObject<{
70
+ link: z.ZodObject<{
71
+ id: z.ZodString;
72
+ repoId: z.ZodString;
73
+ linkKey: z.ZodString;
74
+ linkedAt: z.ZodDate;
75
+ }, z.core.$strip>;
76
+ repo: z.ZodObject<{
77
+ id: z.ZodString;
78
+ installationId: z.ZodString;
79
+ ownerLogin: z.ZodString;
80
+ name: z.ZodString;
81
+ fullName: z.ZodString;
82
+ isPrivate: z.ZodBoolean;
83
+ isFork: z.ZodNullable<z.ZodBoolean>;
84
+ defaultBranch: z.ZodNullable<z.ZodString>;
85
+ removedAt: z.ZodNullable<z.ZodDate>;
86
+ updatedAt: z.ZodDate;
87
+ }, z.core.$strip>;
88
+ }, z.core.$strip>, "INSTALLATION_NOT_FOUND" | "INSTALLATION_INACTIVE" | "REPO_NOT_FOUND" | "REPO_REMOVED", string, _fragno_dev_db0.DatabaseRequestContext<{
89
+ processWebhook: _fragno_dev_db0.HookFn<WebhookProcessingPayload>;
90
+ }>>, _fragno_dev_core0.FragnoRouteConfig<"POST", "/repositories/unlink", z.ZodObject<{
91
+ repoId: z.ZodString;
92
+ linkKey: z.ZodOptional<z.ZodString>;
93
+ }, z.core.$strip>, z.ZodObject<{
94
+ ok: z.ZodLiteral<true>;
95
+ }, z.core.$strip>, "INSTALLATION_NOT_FOUND" | "INSTALLATION_INACTIVE" | "REPO_NOT_FOUND" | "LINK_NOT_FOUND", string, _fragno_dev_db0.DatabaseRequestContext<{
96
+ processWebhook: _fragno_dev_db0.HookFn<WebhookProcessingPayload>;
97
+ }>>, _fragno_dev_core0.FragnoRouteConfig<"GET", "/repositories/:owner/:repo/pulls", _standard_schema_spec0.StandardSchemaV1<unknown, unknown> | undefined, z.ZodObject<{
98
+ pulls: z.ZodArray<z.ZodAny>;
99
+ pageInfo: z.ZodObject<{
100
+ page: z.ZodNumber;
101
+ perPage: z.ZodNumber;
102
+ }, z.core.$strip>;
103
+ }, z.core.$strip>, "INSTALLATION_NOT_FOUND" | "INSTALLATION_INACTIVE" | "REPO_NOT_FOUND" | "REPO_REMOVED" | "INVALID_STATE" | "INVALID_PER_PAGE" | "INVALID_PAGE" | "REPO_NOT_LINKED" | "GITHUB_API_ERROR", "state" | "perPage" | "page", _fragno_dev_db0.DatabaseRequestContext<{
104
+ processWebhook: _fragno_dev_db0.HookFn<WebhookProcessingPayload>;
105
+ }>>, _fragno_dev_core0.FragnoRouteConfig<"POST", "/repositories/:owner/:repo/pulls/:number/reviews", z.ZodObject<{
106
+ event: z.ZodOptional<z.ZodEnum<{
107
+ APPROVE: "APPROVE";
108
+ REQUEST_CHANGES: "REQUEST_CHANGES";
109
+ COMMENT: "COMMENT";
110
+ }>>;
111
+ body: z.ZodOptional<z.ZodString>;
112
+ comments: z.ZodOptional<z.ZodArray<z.ZodAny>>;
113
+ commitId: z.ZodOptional<z.ZodString>;
114
+ }, z.core.$strip>, z.ZodObject<{
115
+ review: z.ZodAny;
116
+ }, z.core.$strip>, "INSTALLATION_NOT_FOUND" | "INSTALLATION_INACTIVE" | "REPO_NOT_FOUND" | "REPO_REMOVED" | "REPO_NOT_LINKED" | "GITHUB_API_ERROR" | "INVALID_PULL_NUMBER", string, _fragno_dev_db0.DatabaseRequestContext<{
117
+ processWebhook: _fragno_dev_db0.HookFn<WebhookProcessingPayload>;
118
+ }>>, _fragno_dev_core0.FragnoRouteConfig<"POST", "/installations/:installationId/sync", _standard_schema_spec0.StandardSchemaV1<unknown, unknown> | undefined, z.ZodObject<{
119
+ added: z.ZodNumber;
120
+ removed: z.ZodNumber;
121
+ updated: z.ZodNumber;
122
+ }, z.core.$strip>, "INSTALLATION_NOT_FOUND" | "GITHUB_API_ERROR", string, _fragno_dev_db0.DatabaseRequestContext<{
123
+ processWebhook: _fragno_dev_db0.HookFn<WebhookProcessingPayload>;
124
+ }>>]>;
125
+ //#endregion
126
+ export { githubAppRoutesFactory };
127
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","names":[],"sources":["../../src/routes.ts"],"mappings":";;;;;;;;;;;;cA6Fa,sBAAA,oBAAsB,YAAA,CAs6BlC,uBAAA;mBAt6BkC,eAAA;AAAA"}