@noodleseed/one 0.17.0 → 0.18.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 (180) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +3 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/alerts-ops.d.ts +2 -0
  5. package/dist/commands/alerts-ops.d.ts.map +1 -0
  6. package/dist/commands/alerts-ops.js +249 -0
  7. package/dist/commands/alerts-ops.js.map +1 -0
  8. package/dist/commands/analytics-ops.d.ts +13 -0
  9. package/dist/commands/analytics-ops.d.ts.map +1 -1
  10. package/dist/commands/analytics-ops.js +5 -2
  11. package/dist/commands/analytics-ops.js.map +1 -1
  12. package/dist/commands/catalog-data-resources.d.ts.map +1 -1
  13. package/dist/commands/catalog-data-resources.js +45 -0
  14. package/dist/commands/catalog-data-resources.js.map +1 -1
  15. package/dist/commands/github-ops.d.ts.map +1 -1
  16. package/dist/commands/github-ops.js +24 -4
  17. package/dist/commands/github-ops.js.map +1 -1
  18. package/dist/commands/github-runs-ops.d.ts +32 -0
  19. package/dist/commands/github-runs-ops.d.ts.map +1 -0
  20. package/dist/commands/github-runs-ops.js +132 -0
  21. package/dist/commands/github-runs-ops.js.map +1 -0
  22. package/dist/commands/mcp-apps.js +27 -0
  23. package/dist/commands/mcp-apps.js.map +1 -1
  24. package/dist/prompts.d.ts.map +1 -1
  25. package/dist/prompts.js +1 -3
  26. package/dist/prompts.js.map +1 -1
  27. package/node_modules/@noodle-borg/agent-kit/dist/curated/command-groups.d.ts.map +1 -1
  28. package/node_modules/@noodle-borg/agent-kit/dist/curated/command-groups.js +5 -0
  29. package/node_modules/@noodle-borg/agent-kit/dist/curated/command-groups.js.map +1 -1
  30. package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.d.ts.map +1 -1
  31. package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js +1 -0
  32. package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js.map +1 -1
  33. package/node_modules/@noodle-borg/agent-kit/dist/skill-content.d.ts.map +1 -1
  34. package/node_modules/@noodle-borg/agent-kit/dist/skill-content.js +3 -1
  35. package/node_modules/@noodle-borg/agent-kit/dist/skill-content.js.map +1 -1
  36. package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
  37. package/node_modules/@noodle-borg/authoring/dist/recording.js +18 -23
  38. package/node_modules/@noodle-borg/authoring/dist/recording.js.map +1 -1
  39. package/node_modules/@noodle-borg/authoring/dist/server.d.ts +4 -1
  40. package/node_modules/@noodle-borg/authoring/dist/server.d.ts.map +1 -1
  41. package/node_modules/@noodle-borg/authoring/dist/server.js +3 -1
  42. package/node_modules/@noodle-borg/authoring/dist/server.js.map +1 -1
  43. package/node_modules/@noodle-borg/compiler/dist/artifact/types.d.ts +2 -0
  44. package/node_modules/@noodle-borg/compiler/dist/artifact/types.d.ts.map +1 -1
  45. package/node_modules/@noodle-borg/compiler/dist/compile.js +1 -1
  46. package/node_modules/@noodle-borg/compiler/dist/compile.js.map +1 -1
  47. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +1 -0
  48. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts.map +1 -1
  49. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +6 -0
  50. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js.map +1 -1
  51. package/node_modules/@noodle-borg/compiler/dist/widget-compile-helpers.d.ts +3 -1
  52. package/node_modules/@noodle-borg/compiler/dist/widget-compile-helpers.d.ts.map +1 -1
  53. package/node_modules/@noodle-borg/compiler/dist/widget-compile-helpers.js +7 -1
  54. package/node_modules/@noodle-borg/compiler/dist/widget-compile-helpers.js.map +1 -1
  55. package/node_modules/@noodle-borg/compiler/dist/widget-emit.d.ts.map +1 -1
  56. package/node_modules/@noodle-borg/compiler/dist/widget-emit.js +3 -1
  57. package/node_modules/@noodle-borg/compiler/dist/widget-emit.js.map +1 -1
  58. package/node_modules/@noodle-borg/protocol/dist/mapping.d.ts +3 -1
  59. package/node_modules/@noodle-borg/protocol/dist/mapping.d.ts.map +1 -1
  60. package/node_modules/@noodle-borg/protocol/dist/mapping.js +9 -3
  61. package/node_modules/@noodle-borg/protocol/dist/mapping.js.map +1 -1
  62. package/node_modules/@noodle-borg/protocol/dist/sdk-server.js +1 -1
  63. package/node_modules/@noodle-borg/protocol/dist/sdk-server.js.map +1 -1
  64. package/node_modules/@noodle-borg/protocol/dist/widget/react-bridge-bootstrap.d.ts +1 -1
  65. package/node_modules/@noodle-borg/protocol/dist/widget/react-bridge-bootstrap.d.ts.map +1 -1
  66. package/node_modules/@noodle-borg/protocol/dist/widget/react-bridge-bootstrap.js +1 -1
  67. package/node_modules/@noodle-borg/service/dist/alert-evaluator.d.ts +57 -0
  68. package/node_modules/@noodle-borg/service/dist/alert-evaluator.d.ts.map +1 -0
  69. package/node_modules/@noodle-borg/service/dist/alert-evaluator.js +158 -0
  70. package/node_modules/@noodle-borg/service/dist/alert-evaluator.js.map +1 -0
  71. package/node_modules/@noodle-borg/service/dist/alert-webhook.d.ts +72 -0
  72. package/node_modules/@noodle-borg/service/dist/alert-webhook.d.ts.map +1 -0
  73. package/node_modules/@noodle-borg/service/dist/alert-webhook.js +138 -0
  74. package/node_modules/@noodle-borg/service/dist/alert-webhook.js.map +1 -0
  75. package/node_modules/@noodle-borg/service/dist/github/pending-installations.d.ts +15 -8
  76. package/node_modules/@noodle-borg/service/dist/github/pending-installations.d.ts.map +1 -1
  77. package/node_modules/@noodle-borg/service/dist/github/pending-installations.js +16 -9
  78. package/node_modules/@noodle-borg/service/dist/github/pending-installations.js.map +1 -1
  79. package/node_modules/@noodle-borg/service/dist/index.d.ts +10 -2
  80. package/node_modules/@noodle-borg/service/dist/index.d.ts.map +1 -1
  81. package/node_modules/@noodle-borg/service/dist/index.js +9 -1
  82. package/node_modules/@noodle-borg/service/dist/index.js.map +1 -1
  83. package/node_modules/@noodle-borg/service/dist/options.d.ts +20 -1
  84. package/node_modules/@noodle-borg/service/dist/options.d.ts.map +1 -1
  85. package/node_modules/@noodle-borg/service/dist/routes/alerts-dispatch.d.ts +28 -0
  86. package/node_modules/@noodle-borg/service/dist/routes/alerts-dispatch.d.ts.map +1 -0
  87. package/node_modules/@noodle-borg/service/dist/routes/alerts-dispatch.js +50 -0
  88. package/node_modules/@noodle-borg/service/dist/routes/alerts-dispatch.js.map +1 -0
  89. package/node_modules/@noodle-borg/service/dist/routes/alerts.d.ts +39 -0
  90. package/node_modules/@noodle-borg/service/dist/routes/alerts.d.ts.map +1 -0
  91. package/node_modules/@noodle-borg/service/dist/routes/alerts.js +204 -0
  92. package/node_modules/@noodle-borg/service/dist/routes/alerts.js.map +1 -0
  93. package/node_modules/@noodle-borg/service/dist/routes/contracts.d.ts +149 -0
  94. package/node_modules/@noodle-borg/service/dist/routes/contracts.d.ts.map +1 -1
  95. package/node_modules/@noodle-borg/service/dist/routes/contracts.js +81 -0
  96. package/node_modules/@noodle-borg/service/dist/routes/contracts.js.map +1 -1
  97. package/node_modules/@noodle-borg/service/dist/routes/github-connection.js +1 -1
  98. package/node_modules/@noodle-borg/service/dist/routes/github-connection.js.map +1 -1
  99. package/node_modules/@noodle-borg/service/dist/routes/github-dispatch.d.ts +2 -1
  100. package/node_modules/@noodle-borg/service/dist/routes/github-dispatch.d.ts.map +1 -1
  101. package/node_modules/@noodle-borg/service/dist/routes/github-dispatch.js +37 -2
  102. package/node_modules/@noodle-borg/service/dist/routes/github-dispatch.js.map +1 -1
  103. package/node_modules/@noodle-borg/service/dist/routes/github-runs.d.ts +8 -0
  104. package/node_modules/@noodle-borg/service/dist/routes/github-runs.d.ts.map +1 -0
  105. package/node_modules/@noodle-borg/service/dist/routes/github-runs.js +33 -0
  106. package/node_modules/@noodle-borg/service/dist/routes/github-runs.js.map +1 -0
  107. package/node_modules/@noodle-borg/service/dist/routes/github-setup.d.ts +1 -1
  108. package/node_modules/@noodle-borg/service/dist/routes/github-setup.d.ts.map +1 -1
  109. package/node_modules/@noodle-borg/service/dist/routes/github-setup.js +3 -3
  110. package/node_modules/@noodle-borg/service/dist/routes/github-setup.js.map +1 -1
  111. package/node_modules/@noodle-borg/service/dist/routes/github-webhook.d.ts +29 -0
  112. package/node_modules/@noodle-borg/service/dist/routes/github-webhook.d.ts.map +1 -0
  113. package/node_modules/@noodle-borg/service/dist/routes/github-webhook.js +355 -0
  114. package/node_modules/@noodle-borg/service/dist/routes/github-webhook.js.map +1 -0
  115. package/node_modules/@noodle-borg/service/dist/routes/org-admin-dispatch.d.ts +27 -0
  116. package/node_modules/@noodle-borg/service/dist/routes/org-admin-dispatch.d.ts.map +1 -0
  117. package/node_modules/@noodle-borg/service/dist/routes/org-admin-dispatch.js +61 -0
  118. package/node_modules/@noodle-borg/service/dist/routes/org-admin-dispatch.js.map +1 -0
  119. package/node_modules/@noodle-borg/service/dist/routes/paths.d.ts +20 -1
  120. package/node_modules/@noodle-borg/service/dist/routes/paths.d.ts.map +1 -1
  121. package/node_modules/@noodle-borg/service/dist/routes/paths.js +54 -0
  122. package/node_modules/@noodle-borg/service/dist/routes/paths.js.map +1 -1
  123. package/node_modules/@noodle-borg/service/dist/serve.d.ts.map +1 -1
  124. package/node_modules/@noodle-borg/service/dist/serve.js +42 -1
  125. package/node_modules/@noodle-borg/service/dist/serve.js.map +1 -1
  126. package/node_modules/@noodle-borg/service/dist/service.d.ts.map +1 -1
  127. package/node_modules/@noodle-borg/service/dist/service.js +37 -53
  128. package/node_modules/@noodle-borg/service/dist/service.js.map +1 -1
  129. package/node_modules/@noodle-borg/service/dist/store/alert-rules-json-file.d.ts +21 -0
  130. package/node_modules/@noodle-borg/service/dist/store/alert-rules-json-file.d.ts.map +1 -0
  131. package/node_modules/@noodle-borg/service/dist/store/alert-rules-json-file.js +115 -0
  132. package/node_modules/@noodle-borg/service/dist/store/alert-rules-json-file.js.map +1 -0
  133. package/node_modules/@noodle-borg/service/dist/store/alert-rules.d.ts +108 -0
  134. package/node_modules/@noodle-borg/service/dist/store/alert-rules.d.ts.map +1 -0
  135. package/node_modules/@noodle-borg/service/dist/store/alert-rules.js +181 -0
  136. package/node_modules/@noodle-borg/service/dist/store/alert-rules.js.map +1 -0
  137. package/node_modules/@noodle-borg/service/dist/store/github-connections-json-file.d.ts +1 -0
  138. package/node_modules/@noodle-borg/service/dist/store/github-connections-json-file.d.ts.map +1 -1
  139. package/node_modules/@noodle-borg/service/dist/store/github-connections-json-file.js +4 -0
  140. package/node_modules/@noodle-borg/service/dist/store/github-connections-json-file.js.map +1 -1
  141. package/node_modules/@noodle-borg/service/dist/store/github-connections.d.ts +1 -0
  142. package/node_modules/@noodle-borg/service/dist/store/github-connections.d.ts.map +1 -1
  143. package/node_modules/@noodle-borg/service/dist/store/github-connections.js +4 -0
  144. package/node_modules/@noodle-borg/service/dist/store/github-connections.js.map +1 -1
  145. package/node_modules/@noodle-borg/service/dist/store/github-runs-json-file.d.ts +22 -0
  146. package/node_modules/@noodle-borg/service/dist/store/github-runs-json-file.d.ts.map +1 -0
  147. package/node_modules/@noodle-borg/service/dist/store/github-runs-json-file.js +116 -0
  148. package/node_modules/@noodle-borg/service/dist/store/github-runs-json-file.js.map +1 -0
  149. package/node_modules/@noodle-borg/service/dist/store/github-runs.d.ts +51 -0
  150. package/node_modules/@noodle-borg/service/dist/store/github-runs.d.ts.map +1 -0
  151. package/node_modules/@noodle-borg/service/dist/store/github-runs.js +158 -0
  152. package/node_modules/@noodle-borg/service/dist/store/github-runs.js.map +1 -0
  153. package/node_modules/@noodle-borg/service/dist/store/pending-installations-postgres.d.ts +19 -0
  154. package/node_modules/@noodle-borg/service/dist/store/pending-installations-postgres.d.ts.map +1 -0
  155. package/node_modules/@noodle-borg/service/dist/store/pending-installations-postgres.js +46 -0
  156. package/node_modules/@noodle-borg/service/dist/store/pending-installations-postgres.js.map +1 -0
  157. package/node_modules/@noodle-borg/service/dist/store/postgres-alerts.d.ts +26 -0
  158. package/node_modules/@noodle-borg/service/dist/store/postgres-alerts.d.ts.map +1 -0
  159. package/node_modules/@noodle-borg/service/dist/store/postgres-alerts.js +142 -0
  160. package/node_modules/@noodle-borg/service/dist/store/postgres-alerts.js.map +1 -0
  161. package/node_modules/@noodle-borg/service/dist/store/postgres-github-runs.d.ts +9 -0
  162. package/node_modules/@noodle-borg/service/dist/store/postgres-github-runs.d.ts.map +1 -0
  163. package/node_modules/@noodle-borg/service/dist/store/postgres-github-runs.js +113 -0
  164. package/node_modules/@noodle-borg/service/dist/store/postgres-github-runs.js.map +1 -0
  165. package/node_modules/@noodle-borg/service/dist/store/postgres-github.d.ts +1 -0
  166. package/node_modules/@noodle-borg/service/dist/store/postgres-github.d.ts.map +1 -1
  167. package/node_modules/@noodle-borg/service/dist/store/postgres-github.js +7 -2
  168. package/node_modules/@noodle-borg/service/dist/store/postgres-github.js.map +1 -1
  169. package/node_modules/@noodle-borg/service/dist/store/postgres-schema.d.ts.map +1 -1
  170. package/node_modules/@noodle-borg/service/dist/store/postgres-schema.js +49 -0
  171. package/node_modules/@noodle-borg/service/dist/store/postgres-schema.js.map +1 -1
  172. package/node_modules/@noodle-borg/service/dist/store/postgres.d.ts +9 -2
  173. package/node_modules/@noodle-borg/service/dist/store/postgres.d.ts.map +1 -1
  174. package/node_modules/@noodle-borg/service/dist/store/postgres.js +17 -1
  175. package/node_modules/@noodle-borg/service/dist/store/postgres.js.map +1 -1
  176. package/node_modules/@noodle-borg/service/dist/store.d.ts +104 -0
  177. package/node_modules/@noodle-borg/service/dist/store.d.ts.map +1 -1
  178. package/node_modules/@noodle-borg/service/dist/store.js +2 -0
  179. package/node_modules/@noodle-borg/service/dist/store.js.map +1 -1
  180. package/package.json +2 -2
@@ -0,0 +1,158 @@
1
+ import { validatePositiveInteger, validateSlug } from './validate.js';
2
+ /**
3
+ * In-memory {@link GithubDeployRunStore} plus the shared pure helpers every backend reuses
4
+ * (validation, duplicate detection, the pinned state machine, list shaping). Mirrors
5
+ * `github-connections.ts`: the Map backend is the non-persistent default and the test double; the
6
+ * exported helpers keep the json-file backend byte-equivalent in semantics.
7
+ */
8
+ /** Newest-first list cap when the caller doesn't pass one — mirrors the runs-feed page size. */
9
+ export const DEFAULT_DEPLOY_RUN_LIST_LIMIT = 50;
10
+ /**
11
+ * The pinned state machine as data (see `DeployRunStatus` in `../store.ts`). The
12
+ * `failed|canceled|superseded → queued` retry edge is deliberately ABSENT — it is reserved for
13
+ * `runs retry` (GHD-4) and must not be walkable until that slice adds it.
14
+ */
15
+ export const DEPLOY_RUN_TRANSITIONS = {
16
+ queued: ['building', 'canceled', 'superseded'],
17
+ building: ['deploying', 'failed', 'canceled'],
18
+ deploying: ['deployed', 'failed'],
19
+ deployed: [],
20
+ failed: [],
21
+ canceled: [],
22
+ superseded: [],
23
+ };
24
+ export function canTransitionDeployRun(from, to) {
25
+ return DEPLOY_RUN_TRANSITIONS[from].includes(to);
26
+ }
27
+ /** Shared input validation, reused by the json-file backend. */
28
+ export function validateCreateDeployRunInput(input) {
29
+ if (input.runId.length === 0)
30
+ throw new Error('runId must not be empty');
31
+ if (input.deliveryId.length === 0)
32
+ throw new Error('deliveryId must not be empty');
33
+ if (input.commitSha.length === 0)
34
+ throw new Error('commitSha must not be empty');
35
+ return {
36
+ runId: input.runId,
37
+ orgSlug: validateSlug('org', input.orgSlug),
38
+ appSlug: validateSlug('app', input.appSlug),
39
+ envName: input.envName,
40
+ sourceEvent: input.sourceEvent,
41
+ deliveryId: input.deliveryId,
42
+ githubRepositoryId: validatePositiveInteger('githubRepositoryId', input.githubRepositoryId),
43
+ commitSha: input.commitSha,
44
+ ref: input.ref,
45
+ ...(input.prNumber !== undefined
46
+ ? { prNumber: validatePositiveInteger('prNumber', input.prNumber) }
47
+ : {}),
48
+ actorLogin: input.actorLogin,
49
+ ...(input.blockedReason !== undefined ? { blockedReason: input.blockedReason } : {}),
50
+ };
51
+ }
52
+ /**
53
+ * Shared duplicate detection over an already-loaded record set (json-file + in-memory; Postgres does
54
+ * the same checks inside its transaction). The idempotency contract lives on the interface doc:
55
+ * delivery ids are globally unique, and only a CANCELED run frees its `{repo, sha, env}` slot.
56
+ */
57
+ export function findCreateDuplicate(records, input) {
58
+ const sameDelivery = records.find((record) => record.deliveryId === input.deliveryId);
59
+ if (sameDelivery !== undefined)
60
+ return { reason: 'duplicate_delivery', existing: sameDelivery };
61
+ const sameTarget = records.find((record) => record.status !== 'canceled' &&
62
+ record.githubRepositoryId === input.githubRepositoryId &&
63
+ record.commitSha === input.commitSha &&
64
+ record.envName === input.envName);
65
+ if (sameTarget !== undefined)
66
+ return { reason: 'duplicate_target', existing: sameTarget };
67
+ return undefined;
68
+ }
69
+ /**
70
+ * Every backend refuses an unfiltered cancel: no webhook outcome ever means "cancel everything
71
+ * queued for the app" — a missing filter is a caller bug, not a mass cancelation.
72
+ */
73
+ export function validateCancelFilter(filter) {
74
+ if (filter.envName === undefined && filter.ref === undefined) {
75
+ throw new Error('cancelQueuedDeployRuns requires envName or ref');
76
+ }
77
+ return {
78
+ orgSlug: validateSlug('org', filter.orgSlug),
79
+ appSlug: validateSlug('app', filter.appSlug),
80
+ ...(filter.envName !== undefined ? { envName: filter.envName } : {}),
81
+ ...(filter.ref !== undefined ? { ref: filter.ref } : {}),
82
+ };
83
+ }
84
+ /** A queued run for the same `(orgSlug, appSlug, envName)` that a new creation supersedes. */
85
+ export function supersedesQueued(record, input) {
86
+ return (record.status === 'queued' &&
87
+ record.orgSlug === input.orgSlug &&
88
+ record.appSlug === input.appSlug &&
89
+ record.envName === input.envName);
90
+ }
91
+ /** A queued run the cancel filter targets — org+app always, env and/or ref narrow it further. */
92
+ export function matchesCancelFilter(record, filter) {
93
+ if (record.status !== 'queued')
94
+ return false;
95
+ if (record.orgSlug !== filter.orgSlug || record.appSlug !== filter.appSlug)
96
+ return false;
97
+ if (filter.envName !== undefined && record.envName !== filter.envName)
98
+ return false;
99
+ if (filter.ref !== undefined && record.ref !== filter.ref)
100
+ return false;
101
+ return true;
102
+ }
103
+ /** Newest-first (createdAt desc, runId desc tiebreak for same-millisecond rows), cap + flag. */
104
+ export function shapeDeployRunList(records, limit) {
105
+ const sorted = [...records].sort((a, b) => b.createdAt.localeCompare(a.createdAt) || b.runId.localeCompare(a.runId));
106
+ return { runs: sorted.slice(0, limit), truncated: sorted.length > limit };
107
+ }
108
+ export class InMemoryGithubDeployRunStore {
109
+ #records = new Map();
110
+ #now;
111
+ constructor(options = {}) {
112
+ this.#now = options.now ?? (() => new Date());
113
+ }
114
+ // All four methods are `async` so validation failures reject (matching the other backends)
115
+ // instead of throwing synchronously out of a Promise-returning signature.
116
+ async createDeployRun(input) {
117
+ const safe = validateCreateDeployRunInput(input);
118
+ const all = [...this.#records.values()];
119
+ const duplicate = findCreateDuplicate(all, safe);
120
+ if (duplicate !== undefined)
121
+ return { ok: false, ...duplicate };
122
+ const nowIso = this.#now().toISOString();
123
+ const superseded = [];
124
+ for (const record of all) {
125
+ if (!supersedesQueued(record, safe))
126
+ continue;
127
+ this.#records.set(record.runId, { ...record, status: 'superseded', finishedAt: nowIso });
128
+ superseded.push(record.runId);
129
+ }
130
+ const record = { ...safe, status: 'queued', createdAt: nowIso };
131
+ this.#records.set(record.runId, record);
132
+ return { ok: true, record, superseded };
133
+ }
134
+ async getDeployRun(org, runId) {
135
+ const safeOrg = validateSlug('org', org);
136
+ const record = this.#records.get(runId);
137
+ return record?.orgSlug === safeOrg ? record : undefined;
138
+ }
139
+ async listDeployRuns(org, app, opts = {}) {
140
+ const safeOrg = validateSlug('org', org);
141
+ const safeApp = validateSlug('app', app);
142
+ const matching = [...this.#records.values()].filter((record) => record.orgSlug === safeOrg && record.appSlug === safeApp);
143
+ return shapeDeployRunList(matching, opts.limit ?? DEFAULT_DEPLOY_RUN_LIST_LIMIT);
144
+ }
145
+ async cancelQueuedDeployRuns(filter) {
146
+ const safe = validateCancelFilter(filter);
147
+ const nowIso = this.#now().toISOString();
148
+ const canceled = [];
149
+ for (const record of this.#records.values()) {
150
+ if (!matchesCancelFilter(record, safe))
151
+ continue;
152
+ this.#records.set(record.runId, { ...record, status: 'canceled', finishedAt: nowIso });
153
+ canceled.push(record.runId);
154
+ }
155
+ return canceled;
156
+ }
157
+ }
158
+ //# sourceMappingURL=github-runs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-runs.js","sourceRoot":"","sources":["../../src/store/github-runs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { Pool } from 'pg';
2
+ import { type PendingGithubInstallationStore } from '../github/pending-installations.js';
3
+ /**
4
+ * Postgres {@link PendingGithubInstallationStore} (GHD-2): the multi-instance backing for the GitHub
5
+ * install hand-off — the setup redirect and the CLI poll can land on different instances, so the
6
+ * correlation must live in the shared store (closes the gap documented in GHD-1 / ADR 0111). A tiny,
7
+ * self-pruning table: `ON CONFLICT DO NOTHING` IS the first-write-wins rule, expired rows are dropped
8
+ * opportunistically on every write and treated as absent on read. Mirrors `PostgresStateHandleStore`'s
9
+ * shape: pool injected, schema ensured by `ensureArtifactSchema`.
10
+ */
11
+ export declare class PostgresPendingGithubInstallationStore implements PendingGithubInstallationStore {
12
+ #private;
13
+ constructor(pool: Pool, options?: {
14
+ now?: () => Date;
15
+ });
16
+ put(state: string, installationId: number): Promise<void>;
17
+ get(state: string): Promise<number | undefined>;
18
+ }
19
+ //# sourceMappingURL=pending-installations-postgres.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending-installations-postgres.d.ts","sourceRoot":"","sources":["../../src/store/pending-installations-postgres.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,EAEL,KAAK,8BAA8B,EACpC,MAAM,oCAAoC,CAAC;AAE5C;;;;;;;GAOG;AACH,qBAAa,sCAAuC,YAAW,8BAA8B;;gBAI/E,IAAI,EAAE,IAAI,EAAE,OAAO,GAAE;QAAE,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;KAAO;IASpD,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAazD,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;CAqBtD"}
@@ -0,0 +1,46 @@
1
+ import { PENDING_GITHUB_INSTALLATION_TTL_MS, } from '../github/pending-installations.js';
2
+ /**
3
+ * Postgres {@link PendingGithubInstallationStore} (GHD-2): the multi-instance backing for the GitHub
4
+ * install hand-off — the setup redirect and the CLI poll can land on different instances, so the
5
+ * correlation must live in the shared store (closes the gap documented in GHD-1 / ADR 0111). A tiny,
6
+ * self-pruning table: `ON CONFLICT DO NOTHING` IS the first-write-wins rule, expired rows are dropped
7
+ * opportunistically on every write and treated as absent on read. Mirrors `PostgresStateHandleStore`'s
8
+ * shape: pool injected, schema ensured by `ensureArtifactSchema`.
9
+ */
10
+ export class PostgresPendingGithubInstallationStore {
11
+ #pool;
12
+ #now;
13
+ constructor(pool, options = {}) {
14
+ this.#pool = pool;
15
+ this.#now = options.now ?? (() => new Date());
16
+ }
17
+ #expiryCutoff() {
18
+ return new Date(this.#now().getTime() - PENDING_GITHUB_INSTALLATION_TTL_MS);
19
+ }
20
+ async put(state, installationId) {
21
+ // Opportunistic hygiene first: never let an expired row satisfy the conflict check below.
22
+ await this.#pool.query('DELETE FROM pending_github_installations WHERE created_at < $1', [
23
+ this.#expiryCutoff(),
24
+ ]);
25
+ await this.#pool.query(`INSERT INTO pending_github_installations (state, installation_id, created_at)
26
+ VALUES ($1, $2, $3)
27
+ ON CONFLICT (state) DO NOTHING`, [state, installationId, this.#now()]);
28
+ }
29
+ async get(state) {
30
+ const { rows } = await this.#pool.query('SELECT installation_id, created_at FROM pending_github_installations WHERE state = $1', [
31
+ state,
32
+ ]);
33
+ const row = rows[0];
34
+ if (row === undefined)
35
+ return undefined;
36
+ const cutoff = this.#expiryCutoff();
37
+ if (new Date(row.created_at).getTime() < cutoff.getTime()) {
38
+ // Scope the cleanup to the expired row: another instance may have pruned it and landed a
39
+ // FRESH row for the same state between our SELECT and this DELETE.
40
+ await this.#pool.query('DELETE FROM pending_github_installations WHERE state = $1 AND created_at < $2', [state, cutoff]);
41
+ return undefined;
42
+ }
43
+ return Number(row.installation_id);
44
+ }
45
+ }
46
+ //# sourceMappingURL=pending-installations-postgres.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending-installations-postgres.js","sourceRoot":"","sources":["../../src/store/pending-installations-postgres.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kCAAkC,GAEnC,MAAM,oCAAoC,CAAC;AAE5C;;;;;;;GAOG;AACH,MAAM,OAAO,sCAAsC;IACxC,KAAK,CAAO;IACZ,IAAI,CAAa;IAE1B,YAAY,IAAU,EAAE,UAAgC,EAAE;QACxD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,aAAa;QACX,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,kCAAkC,CAAC,CAAC;IAC9E,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAAa,EAAE,cAAsB;QAC7C,0FAA0F;QAC1F,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,gEAAgE,EAAE;YACvF,IAAI,CAAC,aAAa,EAAE;SACrB,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CACpB;;sCAEgC,EAChC,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CACrC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAAa;QACrB,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAGpC,uFAAuF,EAAE;YAC1F,KAAK;SACN,CAAC,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACpC,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;YAC1D,yFAAyF;YACzF,mEAAmE;YACnE,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CACpB,+EAA+E,EAC/E,CAAC,KAAK,EAAE,MAAM,CAAC,CAChB,CAAC;YACF,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACrC,CAAC;CACF"}
@@ -0,0 +1,26 @@
1
+ import type { Pool } from 'pg';
2
+ import type { TenantRef } from '../store.js';
3
+ import { type AlertFiringState, type AlertRuleRecord, type AlertRuleStore, type CreateAlertRuleInput } from './alert-rules.js';
4
+ /**
5
+ * Relational {@link AlertRuleStore} backend (analytics alerting E2), a standalone pool-injected
6
+ * class mirroring `PostgresRequestEventStore` (the analytics-family precedent) rather than another
7
+ * facet of `PostgresArtifactStore` — alert rules belong to the analytics surface and this keeps
8
+ * `postgres.ts` under the size gate. Pure SQL; unit-testable against any local Postgres
9
+ * (`DATABASE_URL_TEST`).
10
+ */
11
+ /** Create the `alert_rules` table if absent (idempotent; run once at startup). */
12
+ export declare function ensureAlertRuleSchema(pool: Pool): Promise<void>;
13
+ export declare class PostgresAlertRuleStore implements AlertRuleStore {
14
+ #private;
15
+ constructor(pool: Pool, options?: {
16
+ now?: () => Date;
17
+ });
18
+ ensureSchema(): Promise<void>;
19
+ createAlertRule(input: CreateAlertRuleInput): Promise<AlertRuleRecord>;
20
+ listAlertRules(ref: TenantRef): Promise<readonly AlertRuleRecord[]>;
21
+ getAlertRule(ref: TenantRef, id: string): Promise<AlertRuleRecord | undefined>;
22
+ deleteAlertRule(ref: TenantRef, id: string): Promise<boolean>;
23
+ listEnabledAlertRules(): Promise<readonly AlertRuleRecord[]>;
24
+ updateAlertFiringState(id: string, state: AlertFiringState): Promise<AlertRuleRecord | undefined>;
25
+ }
26
+ //# sourceMappingURL=postgres-alerts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres-alerts.d.ts","sourceRoot":"","sources":["../../src/store/postgres-alerts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EACL,KAAK,gBAAgB,EAErB,KAAK,eAAe,EACpB,KAAK,cAAc,EAEnB,KAAK,oBAAoB,EAG1B,MAAM,kBAAkB,CAAC;AAG1B;;;;;;GAMG;AAEH,kFAAkF;AAClF,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAgCrE;AAgDD,qBAAa,sBAAuB,YAAW,cAAc;;gBAI/C,IAAI,EAAE,IAAI,EAAE,OAAO,GAAE;QAAE,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;KAAO;IAKpD,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAI7B,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC;IA4BtE,cAAc,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC;IAUnE,YAAY,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAc9E,eAAe,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAc7D,qBAAqB,IAAI,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC;IAO5D,sBAAsB,CAC1B,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,gBAAgB,GACtB,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;CAYxC"}
@@ -0,0 +1,142 @@
1
+ import { newAlertRuleRecord, validateAlertRuleId, } from './alert-rules.js';
2
+ import { validateSlug } from './validate.js';
3
+ /**
4
+ * Relational {@link AlertRuleStore} backend (analytics alerting E2), a standalone pool-injected
5
+ * class mirroring `PostgresRequestEventStore` (the analytics-family precedent) rather than another
6
+ * facet of `PostgresArtifactStore` — alert rules belong to the analytics surface and this keeps
7
+ * `postgres.ts` under the size gate. Pure SQL; unit-testable against any local Postgres
8
+ * (`DATABASE_URL_TEST`).
9
+ */
10
+ /** Create the `alert_rules` table if absent (idempotent; run once at startup). */
11
+ export async function ensureAlertRuleSchema(pool) {
12
+ await pool.query(`
13
+ CREATE TABLE IF NOT EXISTS alert_rules (
14
+ id uuid PRIMARY KEY,
15
+ org_slug text NOT NULL,
16
+ app_slug text NOT NULL,
17
+ environment text NOT NULL,
18
+ name text,
19
+ metric text NOT NULL,
20
+ threshold double precision NOT NULL,
21
+ window_minutes int NOT NULL,
22
+ comparison text NOT NULL DEFAULT '>=',
23
+ webhook_url text NOT NULL,
24
+ enabled boolean NOT NULL DEFAULT true,
25
+ cooldown_minutes int NOT NULL,
26
+ breaching boolean NOT NULL DEFAULT false,
27
+ last_observed double precision,
28
+ last_fired_at timestamptz,
29
+ created_by_subject text,
30
+ created_at timestamptz NOT NULL,
31
+ updated_at timestamptz NOT NULL
32
+ )
33
+ `);
34
+ await pool.query(`
35
+ CREATE INDEX IF NOT EXISTS alert_rules_tenant_idx
36
+ ON alert_rules (org_slug, app_slug, environment)
37
+ `);
38
+ await pool.query(`
39
+ CREATE INDEX IF NOT EXISTS alert_rules_enabled_idx
40
+ ON alert_rules (enabled)
41
+ WHERE enabled
42
+ `);
43
+ }
44
+ function rowToRecord(row) {
45
+ return {
46
+ id: row.id,
47
+ orgSlug: row.org_slug,
48
+ appSlug: row.app_slug,
49
+ environment: row.environment,
50
+ ...(row.name !== null ? { name: row.name } : {}),
51
+ metric: row.metric,
52
+ threshold: Number(row.threshold),
53
+ windowMinutes: Number(row.window_minutes),
54
+ comparison: '>=',
55
+ webhookUrl: row.webhook_url,
56
+ enabled: row.enabled,
57
+ cooldownMinutes: Number(row.cooldown_minutes),
58
+ breaching: row.breaching,
59
+ ...(row.last_observed !== null ? { lastObserved: Number(row.last_observed) } : {}),
60
+ ...(row.last_fired_at !== null
61
+ ? { lastFiredAt: new Date(row.last_fired_at).toISOString() }
62
+ : {}),
63
+ ...(row.created_by_subject !== null ? { createdBySubject: row.created_by_subject } : {}),
64
+ createdAt: new Date(row.created_at).toISOString(),
65
+ updatedAt: new Date(row.updated_at).toISOString(),
66
+ };
67
+ }
68
+ export class PostgresAlertRuleStore {
69
+ #pool;
70
+ #now;
71
+ constructor(pool, options = {}) {
72
+ this.#pool = pool;
73
+ this.#now = options.now ?? (() => new Date());
74
+ }
75
+ async ensureSchema() {
76
+ await ensureAlertRuleSchema(this.#pool);
77
+ }
78
+ async createAlertRule(input) {
79
+ const record = newAlertRuleRecord(input, this.#now);
80
+ await this.#pool.query(`INSERT INTO alert_rules
81
+ (id, org_slug, app_slug, environment, name, metric, threshold, window_minutes,
82
+ comparison, webhook_url, enabled, cooldown_minutes, breaching,
83
+ created_by_subject, created_at, updated_at)
84
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, false, $13, $14, $14)`, [
85
+ record.id,
86
+ record.orgSlug,
87
+ record.appSlug,
88
+ record.environment,
89
+ record.name ?? null,
90
+ record.metric,
91
+ record.threshold,
92
+ record.windowMinutes,
93
+ record.comparison,
94
+ record.webhookUrl,
95
+ record.enabled,
96
+ record.cooldownMinutes,
97
+ record.createdBySubject ?? null,
98
+ record.createdAt,
99
+ ]);
100
+ return record;
101
+ }
102
+ async listAlertRules(ref) {
103
+ const { rows } = await this.#pool.query(`SELECT * FROM alert_rules
104
+ WHERE org_slug = $1 AND app_slug = $2 AND environment = $3
105
+ ORDER BY created_at ASC, id ASC`, [validateSlug('org', ref.org), validateSlug('app', ref.app), validateSlug('env', ref.env)]);
106
+ return rows.map(rowToRecord);
107
+ }
108
+ async getAlertRule(ref, id) {
109
+ const { rows } = await this.#pool.query(`SELECT * FROM alert_rules
110
+ WHERE id = $1 AND org_slug = $2 AND app_slug = $3 AND environment = $4`, [
111
+ validateAlertRuleId(id),
112
+ validateSlug('org', ref.org),
113
+ validateSlug('app', ref.app),
114
+ validateSlug('env', ref.env),
115
+ ]);
116
+ return rows[0] ? rowToRecord(rows[0]) : undefined;
117
+ }
118
+ async deleteAlertRule(ref, id) {
119
+ const { rowCount } = await this.#pool.query(`DELETE FROM alert_rules
120
+ WHERE id = $1 AND org_slug = $2 AND app_slug = $3 AND environment = $4`, [
121
+ validateAlertRuleId(id),
122
+ validateSlug('org', ref.org),
123
+ validateSlug('app', ref.app),
124
+ validateSlug('env', ref.env),
125
+ ]);
126
+ return (rowCount ?? 0) > 0;
127
+ }
128
+ async listEnabledAlertRules() {
129
+ const { rows } = await this.#pool.query('SELECT * FROM alert_rules WHERE enabled ORDER BY created_at ASC, id ASC');
130
+ return rows.map(rowToRecord);
131
+ }
132
+ async updateAlertFiringState(id, state) {
133
+ const { rows } = await this.#pool.query(`UPDATE alert_rules
134
+ SET breaching = $2,
135
+ last_observed = $3,
136
+ last_fired_at = COALESCE($4, last_fired_at)
137
+ WHERE id = $1
138
+ RETURNING *`, [validateAlertRuleId(id), state.breaching, state.lastObserved, state.lastFiredAt ?? null]);
139
+ return rows[0] ? rowToRecord(rows[0]) : undefined;
140
+ }
141
+ }
142
+ //# sourceMappingURL=postgres-alerts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres-alerts.js","sourceRoot":"","sources":["../../src/store/postgres-alerts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { Pool } from 'pg';
2
+ import type { CancelQueuedDeployRunsFilter, CreateDeployRunInput, CreateDeployRunResult, DeployRunRecord, ListDeployRunsResult } from '../store.js';
3
+ export declare function createDeployRunRow(pool: Pool, now: () => Date, input: CreateDeployRunInput): Promise<CreateDeployRunResult>;
4
+ export declare function getDeployRunRow(pool: Pool, org: string, runId: string): Promise<DeployRunRecord | undefined>;
5
+ export declare function listDeployRunRows(pool: Pool, org: string, app: string, opts?: {
6
+ readonly limit?: number;
7
+ }): Promise<ListDeployRunsResult>;
8
+ export declare function cancelQueuedDeployRunRows(pool: Pool, now: () => Date, filter: CancelQueuedDeployRunsFilter): Promise<readonly string[]>;
9
+ //# sourceMappingURL=postgres-github-runs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres-github-runs.d.ts","sourceRoot":"","sources":["../../src/store/postgres-github-runs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,IAAI,CAAC;AAC3C,OAAO,KAAK,EACV,4BAA4B,EAC5B,oBAAoB,EACpB,qBAAqB,EACrB,eAAe,EACf,oBAAoB,EACrB,MAAM,aAAa,CAAC;AA6DrB,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,MAAM,IAAI,EACf,KAAK,EAAE,oBAAoB,GAC1B,OAAO,CAAC,qBAAqB,CAAC,CAkEhC;AAED,wBAAsB,eAAe,CACnC,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAMtC;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,IAAI,GAAE;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GACrC,OAAO,CAAC,oBAAoB,CAAC,CAW/B;AAED,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,MAAM,IAAI,EACf,MAAM,EAAE,4BAA4B,GACnC,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAoB5B"}
@@ -0,0 +1,113 @@
1
+ import { DEFAULT_DEPLOY_RUN_LIST_LIMIT, validateCancelFilter, validateCreateDeployRunInput, } from './github-runs.js';
2
+ import { validateSlug } from './validate.js';
3
+ function rowToRecord(row) {
4
+ return {
5
+ runId: row.run_id,
6
+ orgSlug: row.org_slug,
7
+ appSlug: row.app_slug,
8
+ envName: row.env_name,
9
+ sourceEvent: row.source_event,
10
+ deliveryId: row.delivery_id,
11
+ githubRepositoryId: Number(row.github_repository_id),
12
+ commitSha: row.commit_sha,
13
+ ref: row.ref,
14
+ ...(row.pr_number !== null ? { prNumber: Number(row.pr_number) } : {}),
15
+ status: row.status,
16
+ ...(row.blocked_reason !== null ? { blockedReason: row.blocked_reason } : {}),
17
+ ...(row.deployment_id !== null ? { deploymentId: row.deployment_id } : {}),
18
+ actorLogin: row.actor_login,
19
+ ...(row.error_summary !== null ? { errorSummary: row.error_summary } : {}),
20
+ createdAt: new Date(row.created_at).toISOString(),
21
+ ...(row.started_at !== null ? { startedAt: new Date(row.started_at).toISOString() } : {}),
22
+ ...(row.finished_at !== null ? { finishedAt: new Date(row.finished_at).toISOString() } : {}),
23
+ };
24
+ }
25
+ export async function createDeployRunRow(pool, now, input) {
26
+ const safe = validateCreateDeployRunInput(input);
27
+ const client = await pool.connect();
28
+ try {
29
+ // BEGIN inside the try: a transient failure here must still release the checked-out client.
30
+ await client.query('BEGIN');
31
+ await client.query('LOCK TABLE deploy_runs IN SHARE ROW EXCLUSIVE MODE');
32
+ const { rows: sameDelivery } = await client.query('SELECT * FROM deploy_runs WHERE delivery_id = $1', [safe.deliveryId]);
33
+ if (sameDelivery[0] !== undefined) {
34
+ await client.query('ROLLBACK');
35
+ return { ok: false, reason: 'duplicate_delivery', existing: rowToRecord(sameDelivery[0]) };
36
+ }
37
+ const { rows: sameTarget } = await client.query(`SELECT * FROM deploy_runs
38
+ WHERE github_repository_id = $1 AND commit_sha = $2 AND env_name = $3
39
+ AND status != 'canceled'`, [safe.githubRepositoryId, safe.commitSha, safe.envName]);
40
+ if (sameTarget[0] !== undefined) {
41
+ await client.query('ROLLBACK');
42
+ return { ok: false, reason: 'duplicate_target', existing: rowToRecord(sameTarget[0]) };
43
+ }
44
+ const nowTs = now();
45
+ const { rows: supersededRows } = await client.query(`UPDATE deploy_runs SET status = 'superseded', finished_at = $4
46
+ WHERE org_slug = $1 AND app_slug = $2 AND env_name = $3 AND status = 'queued'
47
+ RETURNING run_id`, [safe.orgSlug, safe.appSlug, safe.envName, nowTs]);
48
+ const { rows } = await client.query(`INSERT INTO deploy_runs
49
+ (run_id, org_slug, app_slug, env_name, source_event, delivery_id, github_repository_id,
50
+ commit_sha, ref, pr_number, status, blocked_reason, actor_login, created_at)
51
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, 'queued', $11, $12, $13)
52
+ RETURNING *`, [
53
+ safe.runId,
54
+ safe.orgSlug,
55
+ safe.appSlug,
56
+ safe.envName,
57
+ safe.sourceEvent,
58
+ safe.deliveryId,
59
+ safe.githubRepositoryId,
60
+ safe.commitSha,
61
+ safe.ref,
62
+ safe.prNumber ?? null,
63
+ safe.blockedReason ?? null,
64
+ safe.actorLogin,
65
+ nowTs,
66
+ ]);
67
+ await client.query('COMMIT');
68
+ return {
69
+ ok: true,
70
+ record: rowToRecord(rows[0]),
71
+ superseded: supersededRows.map((row) => row.run_id),
72
+ };
73
+ }
74
+ catch (error) {
75
+ await client.query('ROLLBACK').catch(() => undefined); // best-effort — preserve the original error
76
+ throw error;
77
+ }
78
+ finally {
79
+ client.release();
80
+ }
81
+ }
82
+ export async function getDeployRunRow(pool, org, runId) {
83
+ const { rows } = await pool.query('SELECT * FROM deploy_runs WHERE org_slug = $1 AND run_id = $2', [validateSlug('org', org), runId]);
84
+ return rows[0] ? rowToRecord(rows[0]) : undefined;
85
+ }
86
+ export async function listDeployRunRows(pool, org, app, opts = {}) {
87
+ const limit = opts.limit ?? DEFAULT_DEPLOY_RUN_LIST_LIMIT;
88
+ // Fetch limit+1 so `truncated` is exact without a second count query.
89
+ const { rows } = await pool.query(`SELECT * FROM deploy_runs
90
+ WHERE org_slug = $1 AND app_slug = $2
91
+ ORDER BY created_at DESC, run_id DESC
92
+ LIMIT $3`, [validateSlug('org', org), validateSlug('app', app), limit + 1]);
93
+ return { runs: rows.slice(0, limit).map(rowToRecord), truncated: rows.length > limit };
94
+ }
95
+ export async function cancelQueuedDeployRunRows(pool, now, filter) {
96
+ const safe = validateCancelFilter(filter);
97
+ const conditions = ['org_slug = $1', 'app_slug = $2', "status = 'queued'"];
98
+ const params = [safe.orgSlug, safe.appSlug];
99
+ if (safe.envName !== undefined) {
100
+ params.push(safe.envName);
101
+ conditions.push(`env_name = $${params.length}`);
102
+ }
103
+ if (safe.ref !== undefined) {
104
+ params.push(safe.ref);
105
+ conditions.push(`ref = $${params.length}`);
106
+ }
107
+ params.push(now());
108
+ const { rows } = await pool.query(`UPDATE deploy_runs SET status = 'canceled', finished_at = $${params.length}
109
+ WHERE ${conditions.join(' AND ')}
110
+ RETURNING run_id`, params);
111
+ return rows.map((row) => row.run_id);
112
+ }
113
+ //# sourceMappingURL=postgres-github-runs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres-github-runs.js","sourceRoot":"","sources":["../../src/store/postgres-github-runs.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@ import type { RepoConnectionRecord, UpsertRepoConnectionInput, UpsertRepoConnect
3
3
  export declare function upsertRepoConnectionRow(pool: Pool, now: () => Date, input: UpsertRepoConnectionInput): Promise<UpsertRepoConnectionResult>;
4
4
  export declare function getRepoConnectionRow(pool: Pool, org: string, app: string): Promise<RepoConnectionRecord | undefined>;
5
5
  export declare function getRepoConnectionByRepositoryRow(pool: Pool, githubRepositoryId: number): Promise<RepoConnectionRecord | undefined>;
6
+ export declare function listRepoConnectionsByInstallationRow(pool: Pool, installationId: number): Promise<readonly RepoConnectionRecord[]>;
6
7
  export declare function disableRepoConnectionRow(pool: Pool, now: () => Date, org: string, app: string): Promise<boolean>;
7
8
  export declare function refreshRepoMetadataRow(pool: Pool, now: () => Date, githubRepositoryId: number, updates: {
8
9
  readonly ownerLogin?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-github.d.ts","sourceRoot":"","sources":["../../src/store/postgres-github.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,IAAI,CAAC;AAC3C,OAAO,KAAK,EACV,oBAAoB,EACpB,yBAAyB,EACzB,0BAA0B,EAC3B,MAAM,aAAa,CAAC;AA4CrB,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,MAAM,IAAI,EACf,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,0BAA0B,CAAC,CAsFrC;AAED,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAM3C;AAED,wBAAsB,gCAAgC,CACpD,IAAI,EAAE,IAAI,EACV,kBAAkB,EAAE,MAAM,GACzB,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAM3C;AAED,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,MAAM,IAAI,EACf,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,MAAM,IAAI,EACf,kBAAkB,EAAE,MAAM,EAC1B,OAAO,EAAE;IACP,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC,GACA,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAmB3C"}
1
+ {"version":3,"file":"postgres-github.d.ts","sourceRoot":"","sources":["../../src/store/postgres-github.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,IAAI,CAAC;AAC3C,OAAO,KAAK,EACV,oBAAoB,EACpB,yBAAyB,EACzB,0BAA0B,EAC3B,MAAM,aAAa,CAAC;AA4CrB,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,MAAM,IAAI,EACf,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,0BAA0B,CAAC,CAuFrC;AAED,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAM3C;AAED,wBAAsB,gCAAgC,CACpD,IAAI,EAAE,IAAI,EACV,kBAAkB,EAAE,MAAM,GACzB,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAM3C;AAED,wBAAsB,oCAAoC,CACxD,IAAI,EAAE,IAAI,EACV,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,SAAS,oBAAoB,EAAE,CAAC,CAM1C;AAED,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,MAAM,IAAI,EACf,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,MAAM,IAAI,EACf,kBAAkB,EAAE,MAAM,EAC1B,OAAO,EAAE;IACP,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC,GACA,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAmB3C"}
@@ -21,8 +21,9 @@ export async function upsertRepoConnectionRow(pool, now, input) {
21
21
  const orgSlug = validateSlug('org', input.orgSlug);
22
22
  const appSlug = validateSlug('app', input.appSlug);
23
23
  const client = await pool.connect();
24
- await client.query('BEGIN');
25
24
  try {
25
+ // BEGIN inside the try: a transient failure here must still release the checked-out client.
26
+ await client.query('BEGIN');
26
27
  // FOR UPDATE only locks rows that exist, so two concurrent FIRST-TIME connects would both pass
27
28
  // the conflict checks and race the insert (second one either rewrites the enabled row via
28
29
  // ON CONFLICT or surfaces a raw partial-index violation). Connects are rare, owner-gated admin
@@ -85,7 +86,7 @@ export async function upsertRepoConnectionRow(pool, now, input) {
85
86
  return { ok: true, record: rowToRecord(rows[0]) };
86
87
  }
87
88
  catch (error) {
88
- await client.query('ROLLBACK');
89
+ await client.query('ROLLBACK').catch(() => undefined); // best-effort — preserve the original error
89
90
  throw error;
90
91
  }
91
92
  finally {
@@ -100,6 +101,10 @@ export async function getRepoConnectionByRepositoryRow(pool, githubRepositoryId)
100
101
  const { rows } = await pool.query('SELECT * FROM repo_connections WHERE github_repository_id = $1 AND enabled = true', [validatePositiveInteger('githubRepositoryId', githubRepositoryId)]);
101
102
  return rows[0] ? rowToRecord(rows[0]) : undefined;
102
103
  }
104
+ export async function listRepoConnectionsByInstallationRow(pool, installationId) {
105
+ const { rows } = await pool.query('SELECT * FROM repo_connections WHERE installation_id = $1 AND enabled = true', [validatePositiveInteger('installationId', installationId)]);
106
+ return rows.map(rowToRecord);
107
+ }
103
108
  export async function disableRepoConnectionRow(pool, now, org, app) {
104
109
  const { rowCount } = await pool.query(`UPDATE repo_connections SET enabled = false, updated_at = $3
105
110
  WHERE org_slug = $1 AND app_slug = $2 AND enabled = true`, [validateSlug('org', org), validateSlug('app', app), now()]);