@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.
- package/LICENSE +176 -0
- package/README.md +23 -4
- package/dist/approval-store.d.ts +52 -0
- package/dist/approval-store.js +79 -0
- package/dist/asset-resolution.d.ts +52 -0
- package/dist/asset-resolution.js +66 -0
- package/dist/catalog-resolution.d.ts +76 -0
- package/dist/catalog-resolution.js +151 -0
- package/dist/client.d.ts +19 -0
- package/dist/client.js +18 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.js +16 -0
- package/dist/connection.d.ts +3 -0
- package/dist/connection.js +26 -0
- package/dist/credential-resolution.d.ts +222 -0
- package/dist/credential-resolution.js +255 -0
- package/dist/grant-store.d.ts +3 -0
- package/dist/grant-store.js +59 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +20 -0
- package/dist/migrate.d.ts +36 -0
- package/dist/migrate.js +135 -0
- package/dist/model-source-resolution.d.ts +115 -0
- package/dist/model-source-resolution.js +291 -0
- package/dist/parse-row.d.ts +373 -0
- package/dist/parse-row.js +232 -0
- package/dist/pg-error.d.ts +3 -0
- package/dist/pg-error.js +19 -0
- package/dist/pricing.d.ts +15 -0
- package/dist/pricing.js +24 -0
- package/dist/rekey-credential-secrets.d.ts +13 -0
- package/dist/rekey-credential-secrets.js +61 -0
- package/dist/schema/approvals.d.ts +245 -0
- package/dist/schema/approvals.js +52 -0
- package/dist/schema/assets.d.ts +143 -0
- package/{src/schema/assets.ts → dist/schema/assets.js} +7 -16
- package/dist/schema/auth.d.ts +606 -0
- package/dist/schema/auth.js +47 -0
- package/dist/schema/catalog.d.ts +811 -0
- package/dist/schema/catalog.js +108 -0
- package/dist/schema/column-types.d.ts +26 -0
- package/dist/schema/column-types.js +15 -0
- package/dist/schema/credentials.d.ts +305 -0
- package/{src/schema/credentials.ts → dist/schema/credentials.js} +16 -22
- package/dist/schema/git-tokens.d.ts +256 -0
- package/{src/schema/git-tokens.ts → dist/schema/git-tokens.js} +16 -39
- package/dist/schema/grants.d.ts +211 -0
- package/dist/schema/grants.js +30 -0
- package/dist/schema/index.d.ts +25 -0
- package/dist/schema/index.js +25 -0
- package/dist/schema/messages.d.ts +414 -0
- package/dist/schema/messages.js +82 -0
- package/dist/schema/oauth-clients.d.ts +246 -0
- package/{src/schema/oauth-clients.ts → dist/schema/oauth-clients.js} +8 -14
- package/dist/schema/offerings.d.ts +160 -0
- package/dist/schema/offerings.js +24 -0
- package/dist/schema/principals.d.ts +128 -0
- package/dist/schema/principals.js +23 -0
- package/dist/schema/providers.d.ts +237 -0
- package/dist/schema/providers.js +22 -0
- package/dist/schema/roles.d.ts +242 -0
- package/dist/schema/roles.js +37 -0
- package/dist/schema/session-assets.d.ts +92 -0
- package/dist/schema/session-assets.js +45 -0
- package/dist/schema/sessions.d.ts +143 -0
- package/dist/schema/sessions.js +29 -0
- package/dist/schema/sidecar-allocation.d.ts +406 -0
- package/dist/schema/sidecar-allocation.js +57 -0
- package/dist/schema/sidecar.d.ts +145 -0
- package/dist/schema/sidecar.js +28 -0
- package/dist/schema/signal-correlations.d.ts +177 -0
- package/dist/schema/signal-correlations.js +31 -0
- package/dist/schema/tenants.d.ts +235 -0
- package/{src/schema/tenants.ts → dist/schema/tenants.js} +10 -27
- package/dist/schema/wallets.d.ts +354 -0
- package/dist/schema/wallets.js +43 -0
- package/dist/schema/workflow-definitions.d.ts +371 -0
- package/dist/schema/workflow-definitions.js +97 -0
- package/dist/schema/workflow-run-dispatch.d.ts +328 -0
- package/dist/schema/workflow-run-dispatch.js +56 -0
- package/dist/schema/workflow-run-execution.d.ts +126 -0
- package/dist/schema/workflow-run-execution.js +48 -0
- package/dist/schema/workflow-run-launch-spec.d.ts +200 -0
- package/dist/schema/workflow-run-launch-spec.js +26 -0
- package/dist/schema/workflow-run.d.ts +230 -0
- package/dist/schema/workflow-run.js +102 -0
- package/dist/sidecar-allocation-store.d.ts +171 -0
- package/dist/sidecar-allocation-store.js +512 -0
- package/dist/signal-correlation-store.d.ts +36 -0
- package/dist/signal-correlation-store.js +63 -0
- package/dist/tenant-hierarchy.d.ts +23 -0
- package/dist/tenant-hierarchy.js +60 -0
- package/dist/workflow-definition-store.d.ts +55 -0
- package/dist/workflow-definition-store.js +93 -0
- package/dist/workflow-run-dispatch-store.d.ts +80 -0
- package/dist/workflow-run-dispatch-store.js +298 -0
- package/dist/workflow-run-launch-spec-store.d.ts +13 -0
- package/dist/workflow-run-launch-spec-store.js +30 -0
- package/dist/workflow-run-store.d.ts +54 -0
- package/dist/workflow-run-store.js +85 -0
- package/migrations/0029_loose_warlock.sql +1 -0
- package/migrations/0030_session_asset_audit_split.sql +5 -0
- package/migrations/0031_gigantic_nicolaos.sql +68 -0
- package/migrations/0032_lethal_misty_knight.sql +1 -0
- package/migrations/0033_old_payback.sql +1 -0
- package/migrations/0034_sleepy_songbird.sql +11 -0
- package/migrations/0035_violet_giant_man.sql +3 -0
- package/migrations/0036_sharp_the_executioner.sql +2 -0
- package/migrations/0037_credential_use_backfill.sql +39 -0
- package/migrations/0038_chilly_blacklash.sql +37 -0
- package/migrations/0039_quick_carnage.sql +2 -0
- package/migrations/0040_reshape_approval_origin.sql +24 -0
- package/migrations/0041_make_approval_timeout_at_nullable.sql +1 -0
- package/migrations/0042_youthful_mantis.sql +2 -0
- package/migrations/0043_signal_correlation_deployment_fk.sql +1 -0
- package/migrations/0044_model_offering_quirks.sql +1 -0
- package/migrations/0045_create_workflow_run.sql +13 -0
- package/migrations/0046_approval_signal_correlation_run_fk.sql +2 -0
- package/migrations/0047_create_workflow_definition.sql +30 -0
- package/migrations/0048_workflow_definition_origin_agent_id.sql +2 -0
- package/migrations/0049_curly_omega_flight.sql +2 -0
- package/migrations/0050_late_crystal.sql +10 -0
- package/migrations/0051_funny_madelyne_pryor.sql +1 -0
- package/migrations/0052_drop_inference_turn_instance_fk.sql +1 -0
- package/migrations/0053_session_mail_session_id_index.sql +1 -0
- package/migrations/0054_rekey_instance_grants_to_workflow_run.sql +10 -0
- package/migrations/0055_backfill_anchor_workflow_runs.sql +69 -0
- package/migrations/0056_repoint_deployment_fks_to_anchor_run.sql +32 -0
- package/migrations/0057_drop_workflow_deployment_projection.sql +22 -0
- package/migrations/0058_repoint_offering_fk_to_definition.sql +41 -0
- package/migrations/0059_repoint_transaction_fk_to_run.sql +10 -0
- package/migrations/0060_drop_session_mail_instance_fk.sql +6 -0
- package/migrations/0061_drop_session_asset_instance_fk.sql +7 -0
- package/migrations/0062_drop_agent_asset_table.sql +11 -0
- package/migrations/0063_repoint_agent_role_fk_to_definition.sql +39 -0
- package/migrations/0064_repoint_agent_session_fk_to_definition.sql +47 -0
- package/migrations/0065_add_workflow_definition_model_requirements.sql +37 -0
- package/migrations/0066_rekey_agent_definition_principals_to_workflow.sql +27 -0
- package/migrations/0067_add_workflow_definition_kind.sql +10 -0
- package/migrations/0068_drop_agent_tables_and_origin_agent_id.sql +31 -0
- package/migrations/0069_drop_workflow_definition_kind.sql +10 -0
- package/migrations/0070_flashy_proteus.sql +13 -0
- package/migrations/0072_add_workflow_definition_credential_bindings.sql +1 -0
- package/migrations/0073_grant_target_exactly_one_check.sql +13 -0
- package/migrations/0074_add_provider_api_base_url.sql +1 -0
- package/migrations/0075_workflow_run_launch_spec.sql +17 -0
- package/migrations/0076_sidecar_allocation.sql +40 -0
- package/migrations/0077_workflow_run_dispatch.sql +29 -0
- package/migrations/0078_workflow_run_dispatch_kind.sql +2 -0
- package/migrations/0079_rename_workflow_run_deployment_id_to_anchor_run_id.sql +4 -0
- package/migrations/0080_rename_correlation_approval_deployment_id_to_anchor_run_id.sql +9 -0
- package/migrations/0081_workflow_definition_content_hash_and_approved_wire_hash.sql +4 -0
- package/migrations/0082_blue_black_queen.sql +1 -0
- package/migrations/0083_replace_launch_spec_snapshot_with_frozen_bundle.sql +3 -0
- package/migrations/0084_delete_orphaned_credential_grants.sql +22 -0
- package/migrations/meta/0029_snapshot.json +2664 -0
- package/migrations/meta/0030_snapshot.json +2670 -0
- package/migrations/meta/0031_snapshot.json +3100 -0
- package/migrations/meta/0032_snapshot.json +3106 -0
- package/migrations/meta/0033_snapshot.json +3112 -0
- package/migrations/meta/0034_snapshot.json +3198 -0
- package/migrations/meta/0035_snapshot.json +3225 -0
- package/migrations/meta/0036_snapshot.json +3237 -0
- package/migrations/meta/0037_snapshot.json +3237 -0
- package/migrations/meta/0038_snapshot.json +3470 -0
- package/migrations/meta/0039_snapshot.json +3470 -0
- package/migrations/meta/0040_snapshot.json +3489 -0
- package/migrations/meta/0041_snapshot.json +3489 -0
- package/migrations/meta/0042_snapshot.json +3489 -0
- package/migrations/meta/0043_snapshot.json +3498 -0
- package/migrations/meta/0044_snapshot.json +3504 -0
- package/migrations/meta/0045_snapshot.json +3589 -0
- package/migrations/meta/0046_snapshot.json +3607 -0
- package/migrations/meta/0047_snapshot.json +3832 -0
- package/migrations/meta/0048_snapshot.json +3853 -0
- package/migrations/meta/0049_snapshot.json +3854 -0
- package/migrations/meta/0050_snapshot.json +3924 -0
- package/migrations/meta/0051_snapshot.json +3940 -0
- package/migrations/meta/0052_snapshot.json +3931 -0
- package/migrations/meta/0053_snapshot.json +3952 -0
- package/migrations/meta/0054_snapshot.json +3952 -0
- package/migrations/meta/0055_snapshot.json +3952 -0
- package/migrations/meta/0056_snapshot.json +3952 -0
- package/migrations/meta/0057_snapshot.json +3839 -0
- package/migrations/meta/0058_snapshot.json +3839 -0
- package/migrations/meta/0059_snapshot.json +3839 -0
- package/migrations/meta/0060_snapshot.json +3830 -0
- package/migrations/meta/0061_snapshot.json +3821 -0
- package/migrations/meta/0062_snapshot.json +3723 -0
- package/migrations/meta/0063_snapshot.json +3723 -0
- package/migrations/meta/0064_snapshot.json +3723 -0
- package/migrations/meta/0065_snapshot.json +3729 -0
- package/migrations/meta/0066_snapshot.json +3729 -0
- package/migrations/meta/0067_snapshot.json +3736 -0
- package/migrations/meta/0068_snapshot.json +3347 -0
- package/migrations/meta/0069_snapshot.json +3340 -0
- package/migrations/meta/0070_snapshot.json +3444 -0
- package/migrations/meta/0072_snapshot.json +3450 -0
- package/migrations/meta/0073_snapshot.json +3455 -0
- package/migrations/meta/0074_snapshot.json +3461 -0
- package/migrations/meta/0075_snapshot.json +3567 -0
- package/migrations/meta/0076_snapshot.json +3851 -0
- package/migrations/meta/0077_snapshot.json +4067 -0
- package/migrations/meta/0078_snapshot.json +4078 -0
- package/migrations/meta/0079_snapshot.json +4078 -0
- package/migrations/meta/0080_snapshot.json +4078 -0
- package/migrations/meta/0081_snapshot.json +4096 -0
- package/migrations/meta/0082_snapshot.json +4102 -0
- package/migrations/meta/0083_snapshot.json +4096 -0
- package/migrations/meta/0084_snapshot.json +4096 -0
- package/migrations/meta/_journal.json +385 -0
- package/package.json +25 -11
- package/drizzle.config.ts +0 -23
- package/src/client.ts +0 -24
- package/src/config.ts +0 -19
- package/src/connection.ts +0 -27
- package/src/credential-resolution.ts +0 -378
- package/src/grant-store.ts +0 -51
- package/src/index.ts +0 -32
- package/src/migrate.test.ts +0 -35
- package/src/migrate.ts +0 -168
- package/src/parse-row.test.ts +0 -113
- package/src/parse-row.ts +0 -185
- package/src/schema/agent-assets.ts +0 -21
- package/src/schema/agents.ts +0 -49
- package/src/schema/auth.ts +0 -51
- package/src/schema/grants.ts +0 -26
- package/src/schema/index.ts +0 -19
- package/src/schema/instances.ts +0 -36
- package/src/schema/messages.ts +0 -108
- package/src/schema/offerings.ts +0 -20
- package/src/schema/principals.ts +0 -28
- package/src/schema/providers.ts +0 -23
- package/src/schema/roles.ts +0 -51
- package/src/schema/session-assets.ts +0 -49
- package/src/schema/sessions.ts +0 -26
- package/src/schema/sidecar.ts +0 -14
- package/src/schema/wallets.ts +0 -44
- package/src/tenant-hierarchy.ts +0 -34
- package/tsconfig.json +0 -4
- 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 {};
|