@intx/db 0.1.2 → 0.3.0

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 (241) hide show
  1. package/LICENSE +176 -0
  2. package/README.md +23 -4
  3. package/dist/approval-store.d.ts +52 -0
  4. package/dist/approval-store.js +79 -0
  5. package/dist/asset-resolution.d.ts +52 -0
  6. package/dist/asset-resolution.js +66 -0
  7. package/dist/catalog-resolution.d.ts +76 -0
  8. package/dist/catalog-resolution.js +151 -0
  9. package/dist/client.d.ts +19 -0
  10. package/dist/client.js +18 -0
  11. package/dist/config.d.ts +11 -0
  12. package/dist/config.js +16 -0
  13. package/dist/connection.d.ts +3 -0
  14. package/dist/connection.js +26 -0
  15. package/dist/credential-resolution.d.ts +222 -0
  16. package/dist/credential-resolution.js +255 -0
  17. package/dist/grant-store.d.ts +3 -0
  18. package/dist/grant-store.js +59 -0
  19. package/dist/index.d.ts +22 -0
  20. package/dist/index.js +20 -0
  21. package/dist/migrate.d.ts +36 -0
  22. package/dist/migrate.js +135 -0
  23. package/dist/model-source-resolution.d.ts +115 -0
  24. package/dist/model-source-resolution.js +291 -0
  25. package/dist/parse-row.d.ts +373 -0
  26. package/dist/parse-row.js +232 -0
  27. package/dist/pg-error.d.ts +3 -0
  28. package/dist/pg-error.js +19 -0
  29. package/dist/pricing.d.ts +15 -0
  30. package/dist/pricing.js +24 -0
  31. package/dist/rekey-credential-secrets.d.ts +13 -0
  32. package/dist/rekey-credential-secrets.js +61 -0
  33. package/dist/schema/approvals.d.ts +245 -0
  34. package/dist/schema/approvals.js +52 -0
  35. package/dist/schema/assets.d.ts +143 -0
  36. package/{src/schema/assets.ts → dist/schema/assets.js} +7 -16
  37. package/dist/schema/auth.d.ts +606 -0
  38. package/dist/schema/auth.js +47 -0
  39. package/dist/schema/catalog.d.ts +811 -0
  40. package/dist/schema/catalog.js +108 -0
  41. package/dist/schema/column-types.d.ts +26 -0
  42. package/dist/schema/column-types.js +15 -0
  43. package/dist/schema/credentials.d.ts +305 -0
  44. package/{src/schema/credentials.ts → dist/schema/credentials.js} +16 -22
  45. package/dist/schema/git-tokens.d.ts +256 -0
  46. package/{src/schema/git-tokens.ts → dist/schema/git-tokens.js} +16 -39
  47. package/dist/schema/grants.d.ts +211 -0
  48. package/dist/schema/grants.js +30 -0
  49. package/dist/schema/index.d.ts +25 -0
  50. package/dist/schema/index.js +25 -0
  51. package/dist/schema/messages.d.ts +414 -0
  52. package/dist/schema/messages.js +82 -0
  53. package/dist/schema/oauth-clients.d.ts +246 -0
  54. package/{src/schema/oauth-clients.ts → dist/schema/oauth-clients.js} +8 -14
  55. package/dist/schema/offerings.d.ts +160 -0
  56. package/dist/schema/offerings.js +24 -0
  57. package/dist/schema/principals.d.ts +128 -0
  58. package/dist/schema/principals.js +23 -0
  59. package/dist/schema/providers.d.ts +237 -0
  60. package/dist/schema/providers.js +22 -0
  61. package/dist/schema/roles.d.ts +242 -0
  62. package/dist/schema/roles.js +37 -0
  63. package/dist/schema/session-assets.d.ts +92 -0
  64. package/dist/schema/session-assets.js +45 -0
  65. package/dist/schema/sessions.d.ts +143 -0
  66. package/dist/schema/sessions.js +29 -0
  67. package/dist/schema/sidecar-allocation.d.ts +406 -0
  68. package/dist/schema/sidecar-allocation.js +57 -0
  69. package/dist/schema/sidecar.d.ts +145 -0
  70. package/dist/schema/sidecar.js +28 -0
  71. package/dist/schema/signal-correlations.d.ts +177 -0
  72. package/dist/schema/signal-correlations.js +31 -0
  73. package/dist/schema/tenants.d.ts +235 -0
  74. package/{src/schema/tenants.ts → dist/schema/tenants.js} +10 -27
  75. package/dist/schema/wallets.d.ts +354 -0
  76. package/dist/schema/wallets.js +43 -0
  77. package/dist/schema/workflow-definitions.d.ts +371 -0
  78. package/dist/schema/workflow-definitions.js +97 -0
  79. package/dist/schema/workflow-run-dispatch.d.ts +328 -0
  80. package/dist/schema/workflow-run-dispatch.js +56 -0
  81. package/dist/schema/workflow-run-execution.d.ts +126 -0
  82. package/dist/schema/workflow-run-execution.js +48 -0
  83. package/dist/schema/workflow-run-launch-spec.d.ts +200 -0
  84. package/dist/schema/workflow-run-launch-spec.js +26 -0
  85. package/dist/schema/workflow-run.d.ts +230 -0
  86. package/dist/schema/workflow-run.js +102 -0
  87. package/dist/sidecar-allocation-store.d.ts +171 -0
  88. package/dist/sidecar-allocation-store.js +512 -0
  89. package/dist/signal-correlation-store.d.ts +36 -0
  90. package/dist/signal-correlation-store.js +63 -0
  91. package/dist/tenant-hierarchy.d.ts +23 -0
  92. package/dist/tenant-hierarchy.js +60 -0
  93. package/dist/workflow-definition-store.d.ts +55 -0
  94. package/dist/workflow-definition-store.js +93 -0
  95. package/dist/workflow-run-dispatch-store.d.ts +80 -0
  96. package/dist/workflow-run-dispatch-store.js +298 -0
  97. package/dist/workflow-run-launch-spec-store.d.ts +13 -0
  98. package/dist/workflow-run-launch-spec-store.js +30 -0
  99. package/dist/workflow-run-store.d.ts +54 -0
  100. package/dist/workflow-run-store.js +85 -0
  101. package/migrations/0029_loose_warlock.sql +1 -0
  102. package/migrations/0030_session_asset_audit_split.sql +5 -0
  103. package/migrations/0031_gigantic_nicolaos.sql +68 -0
  104. package/migrations/0032_lethal_misty_knight.sql +1 -0
  105. package/migrations/0033_old_payback.sql +1 -0
  106. package/migrations/0034_sleepy_songbird.sql +11 -0
  107. package/migrations/0035_violet_giant_man.sql +3 -0
  108. package/migrations/0036_sharp_the_executioner.sql +2 -0
  109. package/migrations/0037_credential_use_backfill.sql +39 -0
  110. package/migrations/0038_chilly_blacklash.sql +37 -0
  111. package/migrations/0039_quick_carnage.sql +2 -0
  112. package/migrations/0040_reshape_approval_origin.sql +24 -0
  113. package/migrations/0041_make_approval_timeout_at_nullable.sql +1 -0
  114. package/migrations/0042_youthful_mantis.sql +2 -0
  115. package/migrations/0043_signal_correlation_deployment_fk.sql +1 -0
  116. package/migrations/0044_model_offering_quirks.sql +1 -0
  117. package/migrations/0045_create_workflow_run.sql +13 -0
  118. package/migrations/0046_approval_signal_correlation_run_fk.sql +2 -0
  119. package/migrations/0047_create_workflow_definition.sql +30 -0
  120. package/migrations/0048_workflow_definition_origin_agent_id.sql +2 -0
  121. package/migrations/0049_curly_omega_flight.sql +2 -0
  122. package/migrations/0050_late_crystal.sql +10 -0
  123. package/migrations/0051_funny_madelyne_pryor.sql +1 -0
  124. package/migrations/0052_drop_inference_turn_instance_fk.sql +1 -0
  125. package/migrations/0053_session_mail_session_id_index.sql +1 -0
  126. package/migrations/0054_rekey_instance_grants_to_workflow_run.sql +10 -0
  127. package/migrations/0055_backfill_anchor_workflow_runs.sql +69 -0
  128. package/migrations/0056_repoint_deployment_fks_to_anchor_run.sql +32 -0
  129. package/migrations/0057_drop_workflow_deployment_projection.sql +22 -0
  130. package/migrations/0058_repoint_offering_fk_to_definition.sql +41 -0
  131. package/migrations/0059_repoint_transaction_fk_to_run.sql +10 -0
  132. package/migrations/0060_drop_session_mail_instance_fk.sql +6 -0
  133. package/migrations/0061_drop_session_asset_instance_fk.sql +7 -0
  134. package/migrations/0062_drop_agent_asset_table.sql +11 -0
  135. package/migrations/0063_repoint_agent_role_fk_to_definition.sql +39 -0
  136. package/migrations/0064_repoint_agent_session_fk_to_definition.sql +47 -0
  137. package/migrations/0065_add_workflow_definition_model_requirements.sql +37 -0
  138. package/migrations/0066_rekey_agent_definition_principals_to_workflow.sql +27 -0
  139. package/migrations/0067_add_workflow_definition_kind.sql +10 -0
  140. package/migrations/0068_drop_agent_tables_and_origin_agent_id.sql +31 -0
  141. package/migrations/0069_drop_workflow_definition_kind.sql +10 -0
  142. package/migrations/0070_flashy_proteus.sql +13 -0
  143. package/migrations/0072_add_workflow_definition_credential_bindings.sql +1 -0
  144. package/migrations/0073_grant_target_exactly_one_check.sql +13 -0
  145. package/migrations/0074_add_provider_api_base_url.sql +1 -0
  146. package/migrations/0075_workflow_run_launch_spec.sql +17 -0
  147. package/migrations/0076_sidecar_allocation.sql +40 -0
  148. package/migrations/0077_workflow_run_dispatch.sql +29 -0
  149. package/migrations/0078_workflow_run_dispatch_kind.sql +2 -0
  150. package/migrations/0079_rename_workflow_run_deployment_id_to_anchor_run_id.sql +4 -0
  151. package/migrations/0080_rename_correlation_approval_deployment_id_to_anchor_run_id.sql +9 -0
  152. package/migrations/0081_workflow_definition_content_hash_and_approved_wire_hash.sql +4 -0
  153. package/migrations/0082_blue_black_queen.sql +1 -0
  154. package/migrations/0083_replace_launch_spec_snapshot_with_frozen_bundle.sql +3 -0
  155. package/migrations/0084_delete_orphaned_credential_grants.sql +22 -0
  156. package/migrations/meta/0029_snapshot.json +2664 -0
  157. package/migrations/meta/0030_snapshot.json +2670 -0
  158. package/migrations/meta/0031_snapshot.json +3100 -0
  159. package/migrations/meta/0032_snapshot.json +3106 -0
  160. package/migrations/meta/0033_snapshot.json +3112 -0
  161. package/migrations/meta/0034_snapshot.json +3198 -0
  162. package/migrations/meta/0035_snapshot.json +3225 -0
  163. package/migrations/meta/0036_snapshot.json +3237 -0
  164. package/migrations/meta/0037_snapshot.json +3237 -0
  165. package/migrations/meta/0038_snapshot.json +3470 -0
  166. package/migrations/meta/0039_snapshot.json +3470 -0
  167. package/migrations/meta/0040_snapshot.json +3489 -0
  168. package/migrations/meta/0041_snapshot.json +3489 -0
  169. package/migrations/meta/0042_snapshot.json +3489 -0
  170. package/migrations/meta/0043_snapshot.json +3498 -0
  171. package/migrations/meta/0044_snapshot.json +3504 -0
  172. package/migrations/meta/0045_snapshot.json +3589 -0
  173. package/migrations/meta/0046_snapshot.json +3607 -0
  174. package/migrations/meta/0047_snapshot.json +3832 -0
  175. package/migrations/meta/0048_snapshot.json +3853 -0
  176. package/migrations/meta/0049_snapshot.json +3854 -0
  177. package/migrations/meta/0050_snapshot.json +3924 -0
  178. package/migrations/meta/0051_snapshot.json +3940 -0
  179. package/migrations/meta/0052_snapshot.json +3931 -0
  180. package/migrations/meta/0053_snapshot.json +3952 -0
  181. package/migrations/meta/0054_snapshot.json +3952 -0
  182. package/migrations/meta/0055_snapshot.json +3952 -0
  183. package/migrations/meta/0056_snapshot.json +3952 -0
  184. package/migrations/meta/0057_snapshot.json +3839 -0
  185. package/migrations/meta/0058_snapshot.json +3839 -0
  186. package/migrations/meta/0059_snapshot.json +3839 -0
  187. package/migrations/meta/0060_snapshot.json +3830 -0
  188. package/migrations/meta/0061_snapshot.json +3821 -0
  189. package/migrations/meta/0062_snapshot.json +3723 -0
  190. package/migrations/meta/0063_snapshot.json +3723 -0
  191. package/migrations/meta/0064_snapshot.json +3723 -0
  192. package/migrations/meta/0065_snapshot.json +3729 -0
  193. package/migrations/meta/0066_snapshot.json +3729 -0
  194. package/migrations/meta/0067_snapshot.json +3736 -0
  195. package/migrations/meta/0068_snapshot.json +3347 -0
  196. package/migrations/meta/0069_snapshot.json +3340 -0
  197. package/migrations/meta/0070_snapshot.json +3444 -0
  198. package/migrations/meta/0072_snapshot.json +3450 -0
  199. package/migrations/meta/0073_snapshot.json +3455 -0
  200. package/migrations/meta/0074_snapshot.json +3461 -0
  201. package/migrations/meta/0075_snapshot.json +3567 -0
  202. package/migrations/meta/0076_snapshot.json +3851 -0
  203. package/migrations/meta/0077_snapshot.json +4067 -0
  204. package/migrations/meta/0078_snapshot.json +4078 -0
  205. package/migrations/meta/0079_snapshot.json +4078 -0
  206. package/migrations/meta/0080_snapshot.json +4078 -0
  207. package/migrations/meta/0081_snapshot.json +4096 -0
  208. package/migrations/meta/0082_snapshot.json +4102 -0
  209. package/migrations/meta/0083_snapshot.json +4096 -0
  210. package/migrations/meta/0084_snapshot.json +4096 -0
  211. package/migrations/meta/_journal.json +385 -0
  212. package/package.json +25 -11
  213. package/drizzle.config.ts +0 -23
  214. package/src/client.ts +0 -24
  215. package/src/config.ts +0 -19
  216. package/src/connection.ts +0 -27
  217. package/src/credential-resolution.ts +0 -378
  218. package/src/grant-store.ts +0 -51
  219. package/src/index.ts +0 -32
  220. package/src/migrate.test.ts +0 -35
  221. package/src/migrate.ts +0 -168
  222. package/src/parse-row.test.ts +0 -113
  223. package/src/parse-row.ts +0 -185
  224. package/src/schema/agent-assets.ts +0 -21
  225. package/src/schema/agents.ts +0 -49
  226. package/src/schema/auth.ts +0 -51
  227. package/src/schema/grants.ts +0 -26
  228. package/src/schema/index.ts +0 -19
  229. package/src/schema/instances.ts +0 -36
  230. package/src/schema/messages.ts +0 -108
  231. package/src/schema/offerings.ts +0 -20
  232. package/src/schema/principals.ts +0 -28
  233. package/src/schema/providers.ts +0 -23
  234. package/src/schema/roles.ts +0 -51
  235. package/src/schema/session-assets.ts +0 -49
  236. package/src/schema/sessions.ts +0 -26
  237. package/src/schema/sidecar.ts +0 -14
  238. package/src/schema/wallets.ts +0 -44
  239. package/src/tenant-hierarchy.ts +0 -34
  240. package/tsconfig.json +0 -4
  241. package/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,36 @@
1
+ import type { DB, DBExecutor } from "./client.js";
2
+ import { signalCorrelation } from "./schema/signal-correlations.js";
3
+ import { parseSignalCorrelationRow } from "./parse-row.js";
4
+ type DBHandle = DB["db"];
5
+ type SignalCorrelationInsert = typeof signalCorrelation.$inferInsert;
6
+ type ParsedSignalCorrelation = ReturnType<typeof parseSignalCorrelationRow>;
7
+ /**
8
+ * Store for the `signal_correlation` table, which maps an in-flight signal
9
+ * (correlation id) to the run and address that must be resumed when the signal
10
+ * resolves. Each method accepts an optional transaction handle so the resolver
11
+ * can claim the correlation and flip its approval atomically.
12
+ */
13
+ export declare function createSignalCorrelationStore(db: DBHandle): {
14
+ register(row: SignalCorrelationInsert, tx?: DBExecutor): Promise<ParsedSignalCorrelation>;
15
+ /**
16
+ * Idempotent variant of `register`. On a `correlationId` primary-key
17
+ * conflict the insert is a no-op and this returns `null` rather than
18
+ * throwing, so a redelivered register frame (sidecar reconnect,
19
+ * workflow-log replay, supervisor restart re-emitting) does not fail the
20
+ * co-write. Returns the parsed row only when this call performed the
21
+ * insert.
22
+ */
23
+ registerIfAbsent(row: SignalCorrelationInsert, tx?: DBExecutor): Promise<ParsedSignalCorrelation | null>;
24
+ resolveRoute(correlationId: string, tx?: DBExecutor): Promise<ParsedSignalCorrelation | null>;
25
+ /**
26
+ * Atomically claim a correlation for terminal delivery. The
27
+ * `resolved_at IS NULL` guard makes the claim single-shot: the first caller
28
+ * stamps `resolvedAt` and gets the row back; any second caller matches no
29
+ * row and receives null, so a redelivered signal is not delivered twice.
30
+ * When `signalId` is provided it is persisted on the same update, recording
31
+ * which signal instance won the claim for redelivery idempotency.
32
+ */
33
+ claimTerminal(correlationId: string, resolvedAt: Date, signalId: string | null, tx?: DBExecutor): Promise<ParsedSignalCorrelation | null>;
34
+ };
35
+ export type SignalCorrelationStore = ReturnType<typeof createSignalCorrelationStore>;
36
+ export {};
@@ -0,0 +1,63 @@
1
+ import { and, eq, isNull } from "drizzle-orm";
2
+ import { signalCorrelation } from "./schema/signal-correlations.js";
3
+ import { parseSignalCorrelationRow } from "./parse-row.js";
4
+ /**
5
+ * Store for the `signal_correlation` table, which maps an in-flight signal
6
+ * (correlation id) to the run and address that must be resumed when the signal
7
+ * resolves. Each method accepts an optional transaction handle so the resolver
8
+ * can claim the correlation and flip its approval atomically.
9
+ */
10
+ export function createSignalCorrelationStore(db) {
11
+ return {
12
+ async register(row, tx) {
13
+ const [inserted] = await (tx ?? db)
14
+ .insert(signalCorrelation)
15
+ .values(row)
16
+ .returning();
17
+ if (inserted === undefined) {
18
+ throw new Error(`signalCorrelationStore.register: insert returned no row for ${row.correlationId}`);
19
+ }
20
+ return parseSignalCorrelationRow(inserted);
21
+ },
22
+ /**
23
+ * Idempotent variant of `register`. On a `correlationId` primary-key
24
+ * conflict the insert is a no-op and this returns `null` rather than
25
+ * throwing, so a redelivered register frame (sidecar reconnect,
26
+ * workflow-log replay, supervisor restart re-emitting) does not fail the
27
+ * co-write. Returns the parsed row only when this call performed the
28
+ * insert.
29
+ */
30
+ async registerIfAbsent(row, tx) {
31
+ const [inserted] = await (tx ?? db)
32
+ .insert(signalCorrelation)
33
+ .values(row)
34
+ .onConflictDoNothing({ target: signalCorrelation.correlationId })
35
+ .returning();
36
+ return inserted === undefined
37
+ ? null
38
+ : parseSignalCorrelationRow(inserted);
39
+ },
40
+ async resolveRoute(correlationId, tx) {
41
+ const row = await (tx ?? db).query.signalCorrelation.findFirst({
42
+ where: eq(signalCorrelation.correlationId, correlationId),
43
+ });
44
+ return row === undefined ? null : parseSignalCorrelationRow(row);
45
+ },
46
+ /**
47
+ * Atomically claim a correlation for terminal delivery. The
48
+ * `resolved_at IS NULL` guard makes the claim single-shot: the first caller
49
+ * stamps `resolvedAt` and gets the row back; any second caller matches no
50
+ * row and receives null, so a redelivered signal is not delivered twice.
51
+ * When `signalId` is provided it is persisted on the same update, recording
52
+ * which signal instance won the claim for redelivery idempotency.
53
+ */
54
+ async claimTerminal(correlationId, resolvedAt, signalId, tx) {
55
+ const [claimed] = await (tx ?? db)
56
+ .update(signalCorrelation)
57
+ .set({ resolvedAt, signalId })
58
+ .where(and(eq(signalCorrelation.correlationId, correlationId), isNull(signalCorrelation.resolvedAt)))
59
+ .returning();
60
+ return claimed === undefined ? null : parseSignalCorrelationRow(claimed);
61
+ },
62
+ };
63
+ }
@@ -0,0 +1,23 @@
1
+ import type { DB } from "./client.js";
2
+ /**
3
+ * Walks the tenant parentId chain from the given tenant up to the root.
4
+ * Returns an ordered array of tenant IDs: [tenantId, parentId, grandparentId, ...rootId].
5
+ *
6
+ * Throws if the hierarchy contains a cycle instead of returning a partial
7
+ * chain that could omit inherited policy or configuration.
8
+ */
9
+ export declare function getAncestorChain(db: DB["db"], tenantId: string): Promise<string[]>;
10
+ /**
11
+ * Returns the tenant and every distinct tenant in its subtree (children,
12
+ * their children, and so on), discovered breadth-first. Used to find every
13
+ * tenant whose resolved catalog a change at `tenantId` affects — descendants
14
+ * inherit the ancestor's catalog, so a catalog edit must reach their running
15
+ * agents too.
16
+ *
17
+ * Each level queries children by `parentId` membership rather than walking
18
+ * one tenant at a time. A `visited` set both deduplicates the result and
19
+ * guarantees termination: a tenant enters the frontier at most once, so even
20
+ * a malformed cyclic hierarchy drains the frontier instead of looping. No
21
+ * depth cap is imposed, so an arbitrarily deep tree is returned in full.
22
+ */
23
+ export declare function getDescendantTenants(db: DB["db"], tenantId: string): Promise<string[]>;
@@ -0,0 +1,60 @@
1
+ import { eq, inArray } from "drizzle-orm";
2
+ import { tenant } from "./schema/tenants.js";
3
+ /**
4
+ * Walks the tenant parentId chain from the given tenant up to the root.
5
+ * Returns an ordered array of tenant IDs: [tenantId, parentId, grandparentId, ...rootId].
6
+ *
7
+ * Throws if the hierarchy contains a cycle instead of returning a partial
8
+ * chain that could omit inherited policy or configuration.
9
+ */
10
+ export async function getAncestorChain(db, tenantId) {
11
+ const chain = [];
12
+ const visited = new Set();
13
+ let currentId = tenantId;
14
+ while (true) {
15
+ if (visited.has(currentId)) {
16
+ throw new Error(`Tenant hierarchy contains a cycle at tenant ${currentId}`);
17
+ }
18
+ visited.add(currentId);
19
+ chain.push(currentId);
20
+ const row = await db.query.tenant.findFirst({
21
+ where: eq(tenant.id, currentId),
22
+ columns: { parentId: true },
23
+ });
24
+ if (!row?.parentId)
25
+ break;
26
+ currentId = row.parentId;
27
+ }
28
+ return chain;
29
+ }
30
+ /**
31
+ * Returns the tenant and every distinct tenant in its subtree (children,
32
+ * their children, and so on), discovered breadth-first. Used to find every
33
+ * tenant whose resolved catalog a change at `tenantId` affects — descendants
34
+ * inherit the ancestor's catalog, so a catalog edit must reach their running
35
+ * agents too.
36
+ *
37
+ * Each level queries children by `parentId` membership rather than walking
38
+ * one tenant at a time. A `visited` set both deduplicates the result and
39
+ * guarantees termination: a tenant enters the frontier at most once, so even
40
+ * a malformed cyclic hierarchy drains the frontier instead of looping. No
41
+ * depth cap is imposed, so an arbitrarily deep tree is returned in full.
42
+ */
43
+ export async function getDescendantTenants(db, tenantId) {
44
+ const visited = new Set([tenantId]);
45
+ let frontier = [tenantId];
46
+ while (frontier.length > 0) {
47
+ const children = await db.query.tenant.findMany({
48
+ where: inArray(tenant.parentId, frontier),
49
+ columns: { id: true },
50
+ });
51
+ frontier = [];
52
+ for (const child of children) {
53
+ if (visited.has(child.id))
54
+ continue;
55
+ visited.add(child.id);
56
+ frontier.push(child.id);
57
+ }
58
+ }
59
+ return [...visited];
60
+ }
@@ -0,0 +1,55 @@
1
+ import { GrantWalkSnapshot } from "@intx/types";
2
+ import type { DB, DBExecutor } from "./client.js";
3
+ import { parseWorkflowDefinitionRow } from "./parse-row.js";
4
+ type DBHandle = DB["db"];
5
+ type ParsedWorkflowDefinition = ReturnType<typeof parseWorkflowDefinitionRow>;
6
+ /**
7
+ * The selector that keys a workflow definition's identity: the asset it
8
+ * projects and the content hash of its wire projection. A single asset backs
9
+ * many definitions distinguished by their wire hash, so both fields are
10
+ * required to name exactly one definition.
11
+ */
12
+ export type WorkflowDefinitionSelector = {
13
+ assetId: string;
14
+ wireHash: string;
15
+ };
16
+ /**
17
+ * The definition a selector names, or null when no definition has been folded
18
+ * for that `(assetId, wireHash)` pair. This is the single expression of the
19
+ * deployment -> definition mapping (a deployment names its asset and wire hash);
20
+ * the run backfill and the native-run insert sites both resolve through it.
21
+ * Null on a miss is deliberate, not an error: a deployment whose selector the
22
+ * run-once fold never covered has no definition yet, and its runs anchor on
23
+ * `runId` until that gap closes.
24
+ */
25
+ export declare function resolveDefinitionIdForAsset(db: DBExecutor, selector: WorkflowDefinitionSelector): Promise<string | null>;
26
+ /**
27
+ * Read the deploy-approved grant-walk snapshot frozen onto a definition's
28
+ * version row, validated as a `GrantWalkSnapshot` at this boundary. Returns
29
+ * `null` when the version row is absent or its `grantSnapshot` column is still
30
+ * `null` -- the "not yet approved" state, mirroring `approvedWireHash`. The
31
+ * caller fails closed on `null`; it never substitutes an empty grant set.
32
+ */
33
+ export declare function loadFrozenGrantSnapshot(db: DBExecutor, definitionId: string): Promise<GrantWalkSnapshot | null>;
34
+ export type WorkflowDefinitionRollbackResult = {
35
+ ok: true;
36
+ definition: ParsedWorkflowDefinition;
37
+ } | {
38
+ ok: false;
39
+ reason: "definition_not_found" | "version_not_found";
40
+ };
41
+ /**
42
+ * Store for the first-class `workflow_definition` and its version history.
43
+ */
44
+ export declare function createWorkflowDefinitionStore(db: DBHandle): {
45
+ /**
46
+ * Roll a definition back to a prior version: deactivate the current
47
+ * version, activate the target, and repoint `currentVersion` in one
48
+ * transaction, so a reader never sees two active versions or a
49
+ * `currentVersion` that names an inactive row. Returns a discriminated
50
+ * result rather than throwing for the not-found cases the route reports as
51
+ * 404 / 400.
52
+ */
53
+ rollback(tenantId: string, definitionId: string, targetVersion: string): Promise<WorkflowDefinitionRollbackResult>;
54
+ };
55
+ export {};
@@ -0,0 +1,93 @@
1
+ import { and, eq } from "drizzle-orm";
2
+ import { GrantWalkSnapshot } from "@intx/types";
3
+ import { workflowDefinition, workflowDefinitionVersion, } from "./schema/workflow-definitions.js";
4
+ import { parseWorkflowDefinitionRow } from "./parse-row.js";
5
+ // The version `ensureWorkflowDefinitionForAsset` projects for a fresh
6
+ // definition, and therefore the row the approval freeze stamps the grant
7
+ // snapshot onto. Hand-coupled to the ensure helper's initial version; if that
8
+ // helper ever projects a different version this must follow.
9
+ const FROZEN_VERSION = "1";
10
+ /**
11
+ * The definition a selector names, or null when no definition has been folded
12
+ * for that `(assetId, wireHash)` pair. This is the single expression of the
13
+ * deployment -> definition mapping (a deployment names its asset and wire hash);
14
+ * the run backfill and the native-run insert sites both resolve through it.
15
+ * Null on a miss is deliberate, not an error: a deployment whose selector the
16
+ * run-once fold never covered has no definition yet, and its runs anchor on
17
+ * `runId` until that gap closes.
18
+ */
19
+ export async function resolveDefinitionIdForAsset(db, selector) {
20
+ const row = await db
21
+ .select({ id: workflowDefinition.id })
22
+ .from(workflowDefinition)
23
+ .where(and(eq(workflowDefinition.assetId, selector.assetId), eq(workflowDefinition.wireHash, selector.wireHash)))
24
+ .limit(1)
25
+ .then((rows) => rows[0]);
26
+ return row?.id ?? null;
27
+ }
28
+ /**
29
+ * Read the deploy-approved grant-walk snapshot frozen onto a definition's
30
+ * version row, validated as a `GrantWalkSnapshot` at this boundary. Returns
31
+ * `null` when the version row is absent or its `grantSnapshot` column is still
32
+ * `null` -- the "not yet approved" state, mirroring `approvedWireHash`. The
33
+ * caller fails closed on `null`; it never substitutes an empty grant set.
34
+ */
35
+ export async function loadFrozenGrantSnapshot(db, definitionId) {
36
+ const row = await db
37
+ .select({ grantSnapshot: workflowDefinitionVersion.grantSnapshot })
38
+ .from(workflowDefinitionVersion)
39
+ .where(and(eq(workflowDefinitionVersion.definitionId, definitionId), eq(workflowDefinitionVersion.version, FROZEN_VERSION)))
40
+ .limit(1)
41
+ .then((rows) => rows[0]);
42
+ if (row === undefined || row.grantSnapshot === null)
43
+ return null;
44
+ return GrantWalkSnapshot.assert(row.grantSnapshot);
45
+ }
46
+ /**
47
+ * Store for the first-class `workflow_definition` and its version history.
48
+ */
49
+ export function createWorkflowDefinitionStore(db) {
50
+ return {
51
+ /**
52
+ * Roll a definition back to a prior version: deactivate the current
53
+ * version, activate the target, and repoint `currentVersion` in one
54
+ * transaction, so a reader never sees two active versions or a
55
+ * `currentVersion` that names an inactive row. Returns a discriminated
56
+ * result rather than throwing for the not-found cases the route reports as
57
+ * 404 / 400.
58
+ */
59
+ async rollback(tenantId, definitionId, targetVersion) {
60
+ return db.transaction(async (tx) => {
61
+ const existing = await tx.query.workflowDefinition.findFirst({
62
+ where: and(eq(workflowDefinition.id, definitionId), eq(workflowDefinition.tenantId, tenantId)),
63
+ });
64
+ if (existing === undefined) {
65
+ return { ok: false, reason: "definition_not_found" };
66
+ }
67
+ const target = await tx.query.workflowDefinitionVersion.findFirst({
68
+ where: and(eq(workflowDefinitionVersion.definitionId, definitionId), eq(workflowDefinitionVersion.version, targetVersion)),
69
+ });
70
+ if (target === undefined) {
71
+ return { ok: false, reason: "version_not_found" };
72
+ }
73
+ await tx
74
+ .update(workflowDefinitionVersion)
75
+ .set({ status: "inactive" })
76
+ .where(and(eq(workflowDefinitionVersion.definitionId, definitionId), eq(workflowDefinitionVersion.version, existing.currentVersion)));
77
+ await tx
78
+ .update(workflowDefinitionVersion)
79
+ .set({ status: "active" })
80
+ .where(and(eq(workflowDefinitionVersion.definitionId, definitionId), eq(workflowDefinitionVersion.version, targetVersion)));
81
+ const [updated] = await tx
82
+ .update(workflowDefinition)
83
+ .set({ currentVersion: targetVersion, updatedAt: new Date() })
84
+ .where(eq(workflowDefinition.id, definitionId))
85
+ .returning();
86
+ if (updated === undefined) {
87
+ throw new Error(`workflowDefinitionStore.rollback: update returned no row for ${definitionId}`);
88
+ }
89
+ return { ok: true, definition: parseWorkflowDefinitionRow(updated) };
90
+ });
91
+ },
92
+ };
93
+ }
@@ -0,0 +1,80 @@
1
+ import { type SQL } from "drizzle-orm";
2
+ import { type RunGrantsFrame as RunGrantsFrameType, type SignalDeliverFrame as SignalDeliverFrameType } from "@intx/types/sidecar";
3
+ import type { DB, DBExecutor } from "./client.js";
4
+ import { parseWorkflowRunDispatchRow } from "./parse-row.js";
5
+ type DBHandle = DB["db"];
6
+ type ParsedDispatch = ReturnType<typeof parseWorkflowRunDispatchRow>;
7
+ export type EnqueueWorkflowRunDispatchArgs = {
8
+ readonly id: string;
9
+ readonly anchorRunId: string;
10
+ readonly messageId: string;
11
+ readonly rawMessage: Uint8Array;
12
+ readonly stepGrants: RunGrantsFrameType["stepGrants"];
13
+ readonly now?: Date;
14
+ };
15
+ export type EnqueueWorkflowSignalDispatchArgs = {
16
+ readonly id: string;
17
+ readonly anchorRunId: string;
18
+ readonly signal: Omit<SignalDeliverFrameType, "type">;
19
+ readonly now?: Date;
20
+ };
21
+ export type EnqueueWorkflowRunDispatchResult = {
22
+ readonly dispatch: ParsedDispatch;
23
+ readonly created: boolean;
24
+ };
25
+ type InsertOrReconcileDispatchArgs = {
26
+ readonly operation: "enqueue" | "enqueueSignal";
27
+ readonly id: string;
28
+ readonly anchorRunId: string;
29
+ readonly messageId: string;
30
+ readonly kind: ParsedDispatch["kind"];
31
+ readonly rawMessage: Uint8Array;
32
+ readonly stepGrants: RunGrantsFrameType["stepGrants"];
33
+ readonly now?: Date;
34
+ };
35
+ export declare class WorkflowRunDispatchPayloadConflictError extends Error {
36
+ readonly messageId: string;
37
+ readonly payloadKind: "message" | "signal";
38
+ constructor(messageId: string, payloadKind: "message" | "signal", operation: InsertOrReconcileDispatchArgs["operation"]);
39
+ }
40
+ export type ClaimWorkflowRunDispatchArgs = {
41
+ readonly leaseId: string;
42
+ readonly leaseDurationMs: number;
43
+ };
44
+ export type AcknowledgeWorkflowRunDispatchArgs = {
45
+ readonly allocationId: string;
46
+ readonly anchorRunId: string;
47
+ readonly messageId: string;
48
+ readonly generation: number;
49
+ readonly now?: Date;
50
+ };
51
+ export type RetryWorkflowRunDispatchArgs = {
52
+ readonly dispatchId: string;
53
+ readonly nextAttemptAt: Date;
54
+ readonly code: string;
55
+ readonly message: string;
56
+ readonly expectedLeaseId?: string;
57
+ readonly now?: Date;
58
+ };
59
+ export type FailWorkflowRunDispatchArgs = {
60
+ readonly anchorRunId: string;
61
+ readonly messageId: string;
62
+ readonly code: string;
63
+ readonly message: string;
64
+ readonly now?: Date;
65
+ };
66
+ export declare function createWorkflowRunDispatchStore(db: DBHandle): {
67
+ enqueue(args: EnqueueWorkflowRunDispatchArgs, tx?: DBExecutor): Promise<EnqueueWorkflowRunDispatchResult>;
68
+ enqueueSignal(args: EnqueueWorkflowSignalDispatchArgs, tx?: DBExecutor): Promise<EnqueueWorkflowRunDispatchResult>;
69
+ claimNextPending(args: ClaimWorkflowRunDispatchArgs): Promise<ParsedDispatch | null>;
70
+ acknowledge(args: AcknowledgeWorkflowRunDispatchArgs): Promise<ParsedDispatch | null>;
71
+ scheduleRetry(args: RetryWorkflowRunDispatchArgs): Promise<ParsedDispatch | null>;
72
+ settle(anchorRunId: string, messageId: string, now?: Date): Promise<ParsedDispatch | null>;
73
+ fail(args: FailWorkflowRunDispatchArgs): Promise<ParsedDispatch | null>;
74
+ failUnsettled(anchorRunId: string, code: string, message: string, now?: Date | SQL, tx?: DBExecutor): Promise<number>;
75
+ requeueUnsettled(anchorRunId: string): Promise<number>;
76
+ findById(id: string): Promise<ParsedDispatch | null>;
77
+ listUnsettled(anchorRunId: string): Promise<ParsedDispatch[]>;
78
+ };
79
+ export type WorkflowRunDispatchStore = ReturnType<typeof createWorkflowRunDispatchStore>;
80
+ export {};