@supacloud/compiler 0.58.0 → 0.60.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/README.md +12 -6
- package/dist/cli.js +178 -71
- package/dist/contract-manifest.d.ts +1 -0
- package/dist/database-contracts.d.ts +4 -0
- package/dist/database-governance-runtime.d.ts +1 -1
- package/dist/database-governance-worker.js +29 -6
- package/dist/database-governance.d.ts +1 -1
- package/dist/delivery-bundle-protocol.d.ts +1 -0
- package/dist/delivery-bundle-worker.js +29 -2
- package/dist/execution-snapshot.d.ts +1 -0
- package/dist/index.js +178 -71
- package/dist/inspect.d.ts +2 -0
- package/dist/migration-app-version.d.ts +1 -1
- package/dist/migration-elysia-version.d.ts +1 -1
- package/dist/types.d.ts +11 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -86,7 +86,8 @@ attempts. The default policy requires `requireRouteEffects`,
|
|
|
86
86
|
## Unified Database Contracts
|
|
87
87
|
|
|
88
88
|
Database contracts are protocol-aware: configure only the database sources the
|
|
89
|
-
application uses (`postgrestTypes`, `modelSchema`,
|
|
89
|
+
application uses (`postgrestTypes`, `modelSchema`, `graphql`, `rpcRegistry` or
|
|
90
|
+
`rpcCatalog`). The generated
|
|
90
91
|
`database.manifest.json` is a TypeBox-validated, fingerprinted build boundary;
|
|
91
92
|
consumers must reject it when its schema or migration fingerprint is stale.
|
|
92
93
|
|
|
@@ -99,6 +100,8 @@ type entry point while preserving PostgREST, ZModel and GraphQL native types:
|
|
|
99
100
|
"outDir": "generated",
|
|
100
101
|
"postgrestTypes": "database.types.ts",
|
|
101
102
|
"modelSchema": "zenstack/generated/schema.ts",
|
|
103
|
+
"rpcRegistry": "db/rpc-registry.json",
|
|
104
|
+
"rpcCatalog": "db/contracts/rpc-catalog.json",
|
|
102
105
|
"role": "authenticated",
|
|
103
106
|
"graphql": {
|
|
104
107
|
"schema": "graphql/schema.graphql",
|
|
@@ -114,11 +117,14 @@ snapshot and a role-scoped `pg_graphql` schema snapshot. The generated barrel
|
|
|
114
117
|
exports `Database`, `QueryData`, ZModel schema types and GraphQL contracts.
|
|
115
118
|
Consumers need `@supabase/supabase-js` and their ZenStack dependencies.
|
|
116
119
|
|
|
117
|
-
`--check` detects drift in supplied snapshots, local ZModel imports,
|
|
118
|
-
content/order and generated artifacts without
|
|
119
|
-
not proof of live database parity or
|
|
120
|
-
intended database and role before
|
|
121
|
-
boundaries; protocol result shapes
|
|
120
|
+
`--check` detects drift in supplied snapshots, local ZModel imports, RPC
|
|
121
|
+
registry/catalog inputs, migration content/order and generated artifacts without
|
|
122
|
+
writing. This is an offline check, not proof of live database parity or
|
|
123
|
+
authorization. Refresh snapshots from the intended database and role before
|
|
124
|
+
running it. Keep runtime decoders at untrusted boundaries; protocol result shapes
|
|
125
|
+
are not interchangeable. When `databaseGovernance.rpcBoundary` is configured,
|
|
126
|
+
the compiler also fails if a named Command RPC capability has no registry/catalog
|
|
127
|
+
source or explicit historical entry.
|
|
122
128
|
|
|
123
129
|
Architecture references: Micronaut for compile-time AOP, NestJS for feature modules,
|
|
124
130
|
and Angular for DI semantics. Compiled DI requires constructor injection and generated scope factories.
|
package/dist/cli.js
CHANGED
|
@@ -1090,7 +1090,13 @@ function validateGraph(graph, options = false) {
|
|
|
1090
1090
|
if (command.rpc && command.branches) {
|
|
1091
1091
|
error("command-rpc-branches-conflict", `Command ${command.name} cannot declare both rpc and branches.`, module.file, module.line);
|
|
1092
1092
|
}
|
|
1093
|
+
if (command.branchesFallback !== undefined && command.branchesFallback !== "reject" && command.branchesFallback !== "command") {
|
|
1094
|
+
error("command-branches-fallback-invalid", `Command ${command.name} has an invalid branches fallback.`, module.file, module.line);
|
|
1095
|
+
}
|
|
1093
1096
|
const branchAdapters = command.branches ?? [];
|
|
1097
|
+
if (command.branchesFallback === "command" && branchAdapters.length === 0) {
|
|
1098
|
+
error("command-branches-fallback-without-branches", `Command ${command.name} declares a fallback without branches.`, module.file, module.line);
|
|
1099
|
+
}
|
|
1094
1100
|
for (const branch of branchAdapters) {
|
|
1095
1101
|
if (typeof options !== "object" || !Object.hasOwn(options.commandCapabilities?.rpc ?? {}, branch.rpc)) {
|
|
1096
1102
|
error("command-rpc-unavailable", `Command ${command.name} requires configured RPC adapter '${branch.rpc}'.`, module.file, module.line);
|
|
@@ -1120,7 +1126,7 @@ function validateGraph(graph, options = false) {
|
|
|
1120
1126
|
error("command-transaction-unsupported", `Command ${command.name} branch '${branch.when}' declares transaction, but transaction support is unavailable.`, module.file, module.line);
|
|
1121
1127
|
}
|
|
1122
1128
|
}
|
|
1123
|
-
if (hostCaps.requirePersistentAdapters && (command.rpc !== undefined && (!rpcCaps?.boundary || rpcCaps.audit !== true || rpcCaps.idempotency !== true) || branchCaps.length > 0 && branchCaps.some(({ branch, capabilities }) => !capabilities?.boundary || capabilities.audit !== true || capabilities.idempotency !== true || capabilities.boundary === "database" && (capabilities.transaction !== true || branch.transaction !== "required")) || hostCaps.permission !== true || !command.permission || branchCaps.length === 0 && (!command.audit || command.idempotency !== "required") || branchCaps.length > 0 && branchCaps.some(({ branch }) => !branch.audit || branch.idempotency !== "required") || branchCaps.length > 0 && branchCaps.some(({ capabilities }) => capabilities?.boundary === "external" && capabilities.reconcile !== true) || command.rpc !== undefined && rpcCaps?.boundary === "database" && (rpcCaps.transaction !== true || command.transaction !== "required") || command.rpc === undefined && branchCaps.length === 0 && (hostCaps.audit !== true || hostCaps.idempotency !== true || hostCaps.transaction !== true || command.transaction !== "required") || command.rpc !== undefined && rpcCaps?.boundary === "external" && rpcCaps.reconcile !== true)) {
|
|
1129
|
+
if (hostCaps.requirePersistentAdapters && (command.rpc !== undefined && (!rpcCaps?.boundary || rpcCaps.audit !== true || rpcCaps.idempotency !== true) || branchCaps.length > 0 && branchCaps.some(({ branch, capabilities }) => !capabilities?.boundary || capabilities.audit !== true || capabilities.idempotency !== true || capabilities.boundary === "database" && (capabilities.transaction !== true || branch.transaction !== "required")) || hostCaps.permission !== true || !command.permission || branchCaps.length === 0 && (!command.audit || command.idempotency !== "required") || branchCaps.length > 0 && branchCaps.some(({ branch }) => !branch.audit || branch.idempotency !== "required") || branchCaps.length > 0 && branchCaps.some(({ capabilities }) => capabilities?.boundary === "external" && capabilities.reconcile !== true) || command.branchesFallback === "command" && (!command.audit || command.idempotency !== "required" || command.transaction !== "required" || hostCaps.audit !== true || hostCaps.idempotency !== true || hostCaps.transaction !== true) || command.rpc !== undefined && rpcCaps?.boundary === "database" && (rpcCaps.transaction !== true || command.transaction !== "required") || command.rpc === undefined && branchCaps.length === 0 && (hostCaps.audit !== true || hostCaps.idempotency !== true || hostCaps.transaction !== true || command.transaction !== "required") || command.rpc !== undefined && rpcCaps?.boundary === "external" && rpcCaps.reconcile !== true)) {
|
|
1124
1130
|
error("command-persistence-required", `Command ${command.name} requires an explicit persistent adapter, permission, audit and idempotency policy.`, module.file, module.line, "Register a named database/external adapter, enable permission checks and declare permission, audit and required idempotency on the command.");
|
|
1125
1131
|
}
|
|
1126
1132
|
if (rpcCaps?.boundary === "external" && (command.transaction === "required" || rpcCaps.transaction === true)) {
|
|
@@ -1673,6 +1679,7 @@ function buildContractManifest(graph, artifacts) {
|
|
|
1673
1679
|
module: module.name,
|
|
1674
1680
|
...command.permission === undefined ? {} : { permission: command.permission },
|
|
1675
1681
|
...command.rpc === undefined ? {} : { rpc: command.rpc },
|
|
1682
|
+
...command.branchesFallback === undefined ? {} : { branchesFallback: command.branchesFallback },
|
|
1676
1683
|
...command.branches === undefined ? {} : {
|
|
1677
1684
|
branches: command.branches.map((branch) => ({
|
|
1678
1685
|
when: branch.when,
|
|
@@ -2090,6 +2097,47 @@ function allAspects(module) {
|
|
|
2090
2097
|
...(module.jobs ?? []).flatMap((job) => job.aspects ?? [])
|
|
2091
2098
|
];
|
|
2092
2099
|
}
|
|
2100
|
+
function uniqueStages(paths) {
|
|
2101
|
+
return [...new Set(paths.flat())];
|
|
2102
|
+
}
|
|
2103
|
+
function commandStagePaths(command, aspects) {
|
|
2104
|
+
const prefix = ["commandExecutor", "authorize"];
|
|
2105
|
+
if (command.branches?.length) {
|
|
2106
|
+
const branchPaths = command.branches.map((branch) => [
|
|
2107
|
+
...prefix,
|
|
2108
|
+
`rpc:${branch.rpc}`,
|
|
2109
|
+
...aspects,
|
|
2110
|
+
"handler"
|
|
2111
|
+
]);
|
|
2112
|
+
if (command.branchesFallback === "command") {
|
|
2113
|
+
branchPaths.push([
|
|
2114
|
+
...prefix,
|
|
2115
|
+
...command.idempotency === "required" ? ["idempotency"] : [],
|
|
2116
|
+
...command.transaction === "required" ? ["transaction"] : [],
|
|
2117
|
+
...aspects,
|
|
2118
|
+
"handler",
|
|
2119
|
+
...command.audit ? ["audit"] : []
|
|
2120
|
+
]);
|
|
2121
|
+
}
|
|
2122
|
+
return branchPaths;
|
|
2123
|
+
}
|
|
2124
|
+
return [[
|
|
2125
|
+
...prefix,
|
|
2126
|
+
...command.rpc ? [`rpc:${command.rpc}`] : [
|
|
2127
|
+
...command.idempotency === "required" ? ["idempotency"] : [],
|
|
2128
|
+
...command.transaction === "required" ? ["transaction"] : []
|
|
2129
|
+
],
|
|
2130
|
+
...aspects,
|
|
2131
|
+
"handler",
|
|
2132
|
+
...command.audit && !command.rpc ? ["audit"] : []
|
|
2133
|
+
]];
|
|
2134
|
+
}
|
|
2135
|
+
function planStages(paths, branches) {
|
|
2136
|
+
const stages = uniqueStages(paths);
|
|
2137
|
+
if (branches && !stages.includes("rpc:selected-branch"))
|
|
2138
|
+
stages.splice(2, 0, "rpc:selected-branch");
|
|
2139
|
+
return paths.length > 1 ? { stages, stagePaths: paths } : { stages };
|
|
2140
|
+
}
|
|
2093
2141
|
function createExecutionPlans(graph) {
|
|
2094
2142
|
const aspects = (boundary, refs = []) => refs.map((ref, index) => `${boundary}.aspect[${index}]:${ref.name}`);
|
|
2095
2143
|
return graph.modules.flatMap((module) => [
|
|
@@ -2099,46 +2147,34 @@ function createExecutionPlans(graph) {
|
|
|
2099
2147
|
const command = module.commands.find((item) => item.className === commandRef || item.name === commandRef);
|
|
2100
2148
|
const boundCommand = route.command === undefined ? undefined : command;
|
|
2101
2149
|
const path = `${controller.path}/${route.path}`.replace(/\/+/g, "/");
|
|
2150
|
+
const routeAspects = [
|
|
2151
|
+
...aspects(`module:${module.name}`, module.aspects),
|
|
2152
|
+
...aspects("route", route.aspects),
|
|
2153
|
+
...aspects("command", boundCommand?.aspects)
|
|
2154
|
+
];
|
|
2155
|
+
const stagePaths = boundCommand === undefined ? undefined : commandStagePaths(boundCommand, routeAspects);
|
|
2156
|
+
const plannedStages = stagePaths === undefined ? { stages: [...routeAspects, "handler"] } : planStages(stagePaths, Boolean(boundCommand?.branches?.length));
|
|
2102
2157
|
return {
|
|
2103
2158
|
module: module.name,
|
|
2104
2159
|
kind: "route",
|
|
2105
2160
|
name: `${route.method} ${path.length > 1 ? path.replace(/\/+$/, "") : path}`,
|
|
2106
2161
|
...command ? { command: command.name } : {},
|
|
2107
|
-
|
|
2108
|
-
...boundCommand ? [
|
|
2109
|
-
"commandExecutor",
|
|
2110
|
-
"authorize",
|
|
2111
|
-
...boundCommand.branches?.length ? ["rpc:selected-branch"] : boundCommand.rpc ? [`rpc:${boundCommand.rpc}`] : [
|
|
2112
|
-
...boundCommand.idempotency === "required" ? ["idempotency"] : [],
|
|
2113
|
-
...boundCommand.transaction === "required" ? ["transaction"] : []
|
|
2114
|
-
]
|
|
2115
|
-
] : [],
|
|
2116
|
-
...aspects(`module:${module.name}`, module.aspects),
|
|
2117
|
-
...aspects("route", route.aspects),
|
|
2118
|
-
...aspects("command", boundCommand?.aspects),
|
|
2119
|
-
"handler",
|
|
2120
|
-
...boundCommand?.audit && !boundCommand.rpc && !boundCommand.branches?.length ? ["audit"] : []
|
|
2121
|
-
]
|
|
2162
|
+
...plannedStages
|
|
2122
2163
|
};
|
|
2123
2164
|
})),
|
|
2124
|
-
...module.commands.map((command) =>
|
|
2125
|
-
|
|
2126
|
-
kind: "command",
|
|
2127
|
-
name: command.name,
|
|
2128
|
-
command: command.name,
|
|
2129
|
-
stages: [
|
|
2130
|
-
"commandExecutor",
|
|
2131
|
-
"authorize",
|
|
2132
|
-
...command.branches?.length ? ["rpc:selected-branch"] : command.rpc ? [`rpc:${command.rpc}`] : [
|
|
2133
|
-
...command.idempotency === "required" ? ["idempotency"] : [],
|
|
2134
|
-
...command.transaction === "required" ? ["transaction"] : []
|
|
2135
|
-
],
|
|
2165
|
+
...module.commands.map((command) => {
|
|
2166
|
+
const stagePaths = commandStagePaths(command, [
|
|
2136
2167
|
...aspects(`module:${module.name}`, module.aspects),
|
|
2137
|
-
...aspects("command", command.aspects)
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2168
|
+
...aspects("command", command.aspects)
|
|
2169
|
+
]);
|
|
2170
|
+
return {
|
|
2171
|
+
module: module.name,
|
|
2172
|
+
kind: "command",
|
|
2173
|
+
name: command.name,
|
|
2174
|
+
command: command.name,
|
|
2175
|
+
...planStages(stagePaths, Boolean(command.branches?.length))
|
|
2176
|
+
};
|
|
2177
|
+
}),
|
|
2142
2178
|
...(module.jobs ?? []).map((job) => ({
|
|
2143
2179
|
module: module.name,
|
|
2144
2180
|
kind: "job",
|
|
@@ -2404,6 +2440,7 @@ function renderApplication(graph, options) {
|
|
|
2404
2440
|
permission: command.permission ?? null,
|
|
2405
2441
|
rpc: command.rpc ?? null,
|
|
2406
2442
|
branches: command.branches ?? [],
|
|
2443
|
+
branchesFallback: command.branchesFallback ?? "reject",
|
|
2407
2444
|
transaction: command.transaction ?? null,
|
|
2408
2445
|
audit: command.audit ?? null,
|
|
2409
2446
|
idempotency: command.idempotency ?? null
|
|
@@ -2472,25 +2509,29 @@ function renderApplication(graph, options) {
|
|
|
2472
2509
|
}
|
|
2473
2510
|
};
|
|
2474
2511
|
}));
|
|
2475
|
-
const commands = module.commands.map((command) =>
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2512
|
+
const commands = module.commands.map((command) => {
|
|
2513
|
+
const fallbackCommand = command.branchesFallback === "command";
|
|
2514
|
+
return {
|
|
2515
|
+
name: command.name,
|
|
2516
|
+
className: command.className,
|
|
2517
|
+
executor: "commandExecutor",
|
|
2518
|
+
adapter: command.rpc ?? null,
|
|
2519
|
+
permission: command.permission ?? null,
|
|
2520
|
+
audit: command.audit ?? null,
|
|
2521
|
+
transaction: command.transaction,
|
|
2522
|
+
idempotency: command.idempotency,
|
|
2523
|
+
branches: command.branches ?? [],
|
|
2524
|
+
branchesFallback: command.branchesFallback ?? "reject",
|
|
2525
|
+
...command.tenantScope === undefined ? {} : { tenantScope: command.tenantScope },
|
|
2526
|
+
requiredCapabilities: {
|
|
2527
|
+
permission: true,
|
|
2528
|
+
audit: Boolean(command.audit) && fallbackCommand || (command.branches?.length ? command.branches.some((branch) => Boolean(branch.audit)) : Boolean(command.audit)),
|
|
2529
|
+
idempotency: command.idempotency === "required" && fallbackCommand || (command.branches?.length ? command.branches.some((branch) => branch.idempotency === "required") : command.idempotency === "required"),
|
|
2530
|
+
transaction: command.transaction === "required" && fallbackCommand || (command.branches?.length ? command.branches.some((branch) => branch.transaction === "required") : command.transaction === "required")
|
|
2531
|
+
},
|
|
2532
|
+
aspects: (command.aspects ?? []).map((aspect) => aspect.name)
|
|
2533
|
+
};
|
|
2534
|
+
}).sort((left, right) => left.name.localeCompare(right.name));
|
|
2494
2535
|
const jobs = (module.jobs ?? []).map((job) => ({
|
|
2495
2536
|
name: job.name,
|
|
2496
2537
|
className: job.className,
|
|
@@ -2974,6 +3015,7 @@ ${indent(item, 2)}`).join(",")}
|
|
|
2974
3015
|
`idempotency: ${JSON.stringify(command.idempotency)}`,
|
|
2975
3016
|
...command.tenantScope ? [`tenantScope: ${JSON.stringify(command.tenantScope)}`] : [],
|
|
2976
3017
|
...command.rpc ? [`rpc: ${JSON.stringify(command.rpc)}`] : [],
|
|
3018
|
+
...command.branchesFallback ? [`branchesFallback: ${JSON.stringify(command.branchesFallback)}`] : [],
|
|
2977
3019
|
...command.branches && command.branches.length > 0 ? [`branches: ${JSON.stringify(command.branches)}`] : [],
|
|
2978
3020
|
...command.standalone ? ["standalone: true"] : [],
|
|
2979
3021
|
...command.aspects && command.aspects.length > 0 ? [`aspects: ${this.renderAspects(command.aspects)}`, `aspectPipeline: ${this.renderAspectPipeline(command.aspects)}`] : []
|
|
@@ -4733,6 +4775,7 @@ var HEADER = "// GENERATED BY @supacloud/compiler — do not edit", INTERFACES =
|
|
|
4733
4775
|
export interface CompiledCommand {
|
|
4734
4776
|
rpc?: string;
|
|
4735
4777
|
branches?: readonly CompiledCommandBranch[];
|
|
4778
|
+
branchesFallback?: "reject" | "command";
|
|
4736
4779
|
className: string;
|
|
4737
4780
|
name: string;
|
|
4738
4781
|
permission: string;
|
|
@@ -4865,7 +4908,7 @@ async function compiledAspectStep(context: CompiledAspectContext, handler: () =>
|
|
|
4865
4908
|
}
|
|
4866
4909
|
called = true;
|
|
4867
4910
|
pending = Promise.resolve().then(handler).then(result => {
|
|
4868
|
-
if (strict) freezeCompiledAspectData(result);
|
|
4911
|
+
if (strict && !(result instanceof Response)) freezeCompiledAspectData(result);
|
|
4869
4912
|
value = result;
|
|
4870
4913
|
return result;
|
|
4871
4914
|
});
|
|
@@ -15812,7 +15855,8 @@ var init_execution_snapshot = __esm(() => {
|
|
|
15812
15855
|
kind,
|
|
15813
15856
|
name: text,
|
|
15814
15857
|
command: Optional(text),
|
|
15815
|
-
stages: _Array_(text)
|
|
15858
|
+
stages: _Array_(text),
|
|
15859
|
+
stagePaths: Optional(_Array_(_Array_(text)))
|
|
15816
15860
|
}, closed)),
|
|
15817
15861
|
commandAliases: _Array_(_Object_({ module: text, name: text, alias: text }, closed)),
|
|
15818
15862
|
contexts: _Array_(_Object_({
|
|
@@ -15981,7 +16025,8 @@ function buildTimeline(events, matchedPlans) {
|
|
|
15981
16025
|
attempts: [],
|
|
15982
16026
|
omittedAttempts: 0
|
|
15983
16027
|
},
|
|
15984
|
-
attempts: new Map
|
|
16028
|
+
attempts: new Map,
|
|
16029
|
+
...plan === undefined ? {} : { plan }
|
|
15985
16030
|
};
|
|
15986
16031
|
groups.set(key, group);
|
|
15987
16032
|
}
|
|
@@ -16009,13 +16054,16 @@ function buildTimeline(events, matchedPlans) {
|
|
|
16009
16054
|
const declared = group.entry.declaredStages;
|
|
16010
16055
|
const attempts = [...group.attempts.values()].sort((left, right) => Number(hasFailure(right)) - Number(hasFailure(left)) || left.attempt - right.attempt).slice(0, EXECUTION_CONTEXT_LIMITS.timelineAttempts).sort((left, right) => left.attempt - right.attempt).map((attempt) => {
|
|
16011
16056
|
const stages = [...attempt.stages].sort((left, right) => Number(right.phase === "failed") - Number(left.phase === "failed") || left.index - right.index).slice(0, EXECUTION_CONTEXT_LIMITS.timelineStages).sort((left, right) => left.index - right.index);
|
|
16012
|
-
const
|
|
16057
|
+
const paths = group.plan?.stagePaths && group.plan.stagePaths.length > 0 ? group.plan.stagePaths : [declared];
|
|
16058
|
+
const bestPath = [...paths].sort((left, right) => right.filter((stage) => attempt.observed.has(stage)).length - left.filter((stage) => attempt.observed.has(stage)).length)[0] ?? declared;
|
|
16059
|
+
const terminal = bestPath[bestPath.length - 1];
|
|
16060
|
+
const complete = terminal !== undefined && attempt.stages.some((stage) => stage.stage === terminal && stage.phase === "succeeded");
|
|
16013
16061
|
return {
|
|
16014
16062
|
attempt: attempt.attempt,
|
|
16015
16063
|
stages,
|
|
16016
16064
|
failed: hasFailure(attempt),
|
|
16017
|
-
complete
|
|
16018
|
-
missingStages:
|
|
16065
|
+
complete,
|
|
16066
|
+
missingStages: bestPath.filter((stage) => !attempt.observed.has(stage)),
|
|
16019
16067
|
unexpectedStages: [],
|
|
16020
16068
|
traceIds: [...attempt.traceIds].sort(),
|
|
16021
16069
|
omittedStages: attempt.stages.length - stages.length
|
|
@@ -16494,23 +16542,49 @@ var init_application_development2 = __esm(() => {
|
|
|
16494
16542
|
});
|
|
16495
16543
|
|
|
16496
16544
|
// node_modules/@supacloud/delivery/src/worker-execution.ts
|
|
16545
|
+
function workerShardCount(group) {
|
|
16546
|
+
return group.shardCount ?? 1;
|
|
16547
|
+
}
|
|
16548
|
+
function shardSuffix(shard) {
|
|
16549
|
+
if (!Number.isSafeInteger(shard) || shard < 0 || shard >= 64) {
|
|
16550
|
+
throw new Error("WORKER_SHARD_INVALID");
|
|
16551
|
+
}
|
|
16552
|
+
return `s${shard.toString(36).padStart(2, "0")}`;
|
|
16553
|
+
}
|
|
16554
|
+
function workerShardQueue(group, shard) {
|
|
16555
|
+
const count = workerShardCount(group);
|
|
16556
|
+
if (!Number.isSafeInteger(shard) || shard < 0 || shard >= count) {
|
|
16557
|
+
throw new Error("WORKER_SHARD_INVALID");
|
|
16558
|
+
}
|
|
16559
|
+
if (count === 1)
|
|
16560
|
+
return group.queue;
|
|
16561
|
+
const queue = `${group.queue}_${shardSuffix(shard)}`;
|
|
16562
|
+
if (!/^scw_[a-z0-9_]{1,40}$/.test(queue))
|
|
16563
|
+
throw new Error("WORKER_QUEUE_INVALID");
|
|
16564
|
+
return queue;
|
|
16565
|
+
}
|
|
16497
16566
|
function parseWorkerExecutionGroup(value) {
|
|
16498
16567
|
if (!Check2(WorkerExecutionGroupSchema, value) || value.replicas > value.maxReplicas || !value.queue.endsWith(`_v${value.definitionVersion}`) || value.lifecycle.visibilityTimeoutSeconds < value.lifecycle.executionTimeoutSeconds + value.lifecycle.shutdownGraceSeconds + 30) {
|
|
16499
16568
|
throw new Error("WORKER_EXECUTION_INVALID");
|
|
16500
16569
|
}
|
|
16501
|
-
|
|
16570
|
+
const group = structuredClone(value);
|
|
16571
|
+
for (let shard = 0;shard < workerShardCount(group); shard += 1)
|
|
16572
|
+
workerShardQueue(group, shard);
|
|
16573
|
+
return group;
|
|
16502
16574
|
}
|
|
16503
16575
|
function validateWorkerExecution(groups) {
|
|
16504
16576
|
const names = new Set, targets = new Set, queues = new Set, routes = new Set;
|
|
16505
16577
|
for (const candidate of groups) {
|
|
16506
16578
|
const group = parseWorkerExecutionGroup(candidate);
|
|
16507
16579
|
const route = `${group.taskKey}:${group.definitionVersion}`;
|
|
16508
|
-
|
|
16580
|
+
const physicalQueues = Array.from({ length: workerShardCount(group) }, (_, shard) => workerShardQueue(group, shard));
|
|
16581
|
+
if (names.has(group.name) || targets.has(group.target) || queues.has(group.queue) || physicalQueues.some((queue) => queues.has(queue)) || routes.has(route)) {
|
|
16509
16582
|
throw new Error("WORKER_EXECUTION_CONFLICT");
|
|
16510
16583
|
}
|
|
16511
16584
|
names.add(group.name);
|
|
16512
16585
|
targets.add(group.target);
|
|
16513
16586
|
queues.add(group.queue);
|
|
16587
|
+
physicalQueues.forEach((queue) => queues.add(queue));
|
|
16514
16588
|
routes.add(route);
|
|
16515
16589
|
}
|
|
16516
16590
|
}
|
|
@@ -16534,6 +16608,7 @@ var init_worker_execution = __esm(() => {
|
|
|
16534
16608
|
definitionVersion: String2({ pattern: "^[1-9][0-9]{0,8}$" }),
|
|
16535
16609
|
replicas: positive(16),
|
|
16536
16610
|
maxReplicas: positive(16),
|
|
16611
|
+
shardCount: Optional(positive(64)),
|
|
16537
16612
|
concurrencyPerReplica: positive(32),
|
|
16538
16613
|
resources: ComputeResourcesSchema,
|
|
16539
16614
|
database: _Object_({
|
|
@@ -16943,7 +17018,7 @@ function parseDeliveryBuildManifest(value) {
|
|
|
16943
17018
|
if (!Check2(DeliveryBuildManifestSchema, value))
|
|
16944
17019
|
throw new Error("Invalid delivery build manifest.");
|
|
16945
17020
|
validateWorkerExecution(value.plan.targets.flatMap((target) => target.execution ? [target.execution] : []));
|
|
16946
|
-
if (value.plan.targets.reduce((count, target) => count + (target.execution
|
|
17021
|
+
if (value.plan.targets.reduce((count, target) => count + (target.execution ? (target.execution.replicas ?? 1) * workerShardCount(target.execution) : 1), 0) > 256) {
|
|
16947
17022
|
throw new Error("Invalid delivery replica count.");
|
|
16948
17023
|
}
|
|
16949
17024
|
for (const target of value.plan.targets) {
|
|
@@ -18515,7 +18590,7 @@ function importPath(out, path) {
|
|
|
18515
18590
|
function parseDatabaseContractsOptions(value, directory) {
|
|
18516
18591
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
18517
18592
|
throw new TypeError("Expected database contracts configuration");
|
|
18518
|
-
const allowed = ["rootDir", "outDir", "postgrestTypes", "modelSchema", "role", "graphql", "migrations"];
|
|
18593
|
+
const allowed = ["rootDir", "outDir", "postgrestTypes", "modelSchema", "role", "graphql", "rpcRegistry", "rpcCatalog", "migrations"];
|
|
18519
18594
|
if (Object.keys(value).some((name) => !allowed.includes(name)))
|
|
18520
18595
|
throw new TypeError("Unknown database contracts option");
|
|
18521
18596
|
const field = (name) => {
|
|
@@ -18531,6 +18606,8 @@ function parseDatabaseContractsOptions(value, directory) {
|
|
|
18531
18606
|
};
|
|
18532
18607
|
const postgrestTypes = optionalPath("postgrestTypes");
|
|
18533
18608
|
const modelSchema = optionalPath("modelSchema");
|
|
18609
|
+
const rpcRegistry = optionalPath("rpcRegistry");
|
|
18610
|
+
const rpcCatalog = optionalPath("rpcCatalog");
|
|
18534
18611
|
const graphqlPresent = Object.hasOwn(value, "graphql");
|
|
18535
18612
|
if (!postgrestTypes && !modelSchema && !graphqlPresent) {
|
|
18536
18613
|
throw new TypeError("Database contracts require postgrestTypes, modelSchema, or graphql");
|
|
@@ -18552,6 +18629,8 @@ function parseDatabaseContractsOptions(value, directory) {
|
|
|
18552
18629
|
...modelSchema ? { modelSchema: resolve25(directory, modelSchema) } : {},
|
|
18553
18630
|
role: field("role"),
|
|
18554
18631
|
...graphql ? { graphql } : {},
|
|
18632
|
+
...rpcRegistry ? { rpcRegistry: resolve25(directory, rpcRegistry) } : {},
|
|
18633
|
+
...rpcCatalog ? { rpcCatalog: resolve25(directory, rpcCatalog) } : {},
|
|
18555
18634
|
migrations: migrations.map((file) => resolve25(directory, file))
|
|
18556
18635
|
};
|
|
18557
18636
|
}
|
|
@@ -18627,6 +18706,10 @@ async function generateDatabaseContracts(options, check = false) {
|
|
|
18627
18706
|
}
|
|
18628
18707
|
if (options.graphql)
|
|
18629
18708
|
await addInput(resolve25(rootDir, options.graphql.schema));
|
|
18709
|
+
if (options.rpcRegistry)
|
|
18710
|
+
await addInput(options.rpcRegistry);
|
|
18711
|
+
if (options.rpcCatalog)
|
|
18712
|
+
await addInput(options.rpcCatalog);
|
|
18630
18713
|
if (new Set(options.migrations.map((path) => resolve25(rootDir, path))).size !== options.migrations.length) {
|
|
18631
18714
|
throw new Error("Duplicate migration in database contracts configuration");
|
|
18632
18715
|
}
|
|
@@ -19879,6 +19962,7 @@ async function analyzeProject(rootDir, include, cache, changedPaths, effectOptio
|
|
|
19879
19962
|
const permission = stringLiteralProp(meta, "permission");
|
|
19880
19963
|
const rpc = checkedRpc(meta, ctx);
|
|
19881
19964
|
const branches = checkedBranches(meta, ctx);
|
|
19965
|
+
const branchesFallback = checkedBranchesFallback(meta, ctx);
|
|
19882
19966
|
const audit = stringLiteralProp(meta, "audit");
|
|
19883
19967
|
const standaloneName = stringLiteralProp(meta, "name") ?? classInfo.decl.name?.text ?? name;
|
|
19884
19968
|
const standaloneUses = await parseInfraResourceUseRefs(infraResourceProp(meta, "uses", ctx), ctx, `command ${standaloneName}`, sourcePath(ctx.rootDir, classInfo.file), lineOf(classInfo.decl));
|
|
@@ -19889,6 +19973,7 @@ async function analyzeProject(rootDir, include, cache, changedPaths, effectOptio
|
|
|
19889
19973
|
...permission === undefined ? {} : { permission },
|
|
19890
19974
|
...rpc === undefined ? {} : { rpc },
|
|
19891
19975
|
...branches === undefined ? {} : { branches },
|
|
19976
|
+
...branchesFallback === undefined ? {} : { branchesFallback },
|
|
19892
19977
|
transaction: checkedCommandMode(meta, "transaction", ctx, classInfo.decl.name?.text ?? name) ?? "none",
|
|
19893
19978
|
...audit === undefined ? {} : { audit },
|
|
19894
19979
|
idempotency: checkedCommandMode(meta, "idempotency", ctx, classInfo.decl.name?.text ?? name) ?? "none",
|
|
@@ -20263,6 +20348,7 @@ async function parseModule(candidate, nameByNode, ctx) {
|
|
|
20263
20348
|
const permission = stringLiteralProp(meta, "permission");
|
|
20264
20349
|
const rpc = checkedRpc(meta, ctx);
|
|
20265
20350
|
const branches = checkedBranches(meta, ctx);
|
|
20351
|
+
const branchesFallback = checkedBranchesFallback(meta, ctx);
|
|
20266
20352
|
const audit = stringLiteralProp(meta, "audit");
|
|
20267
20353
|
const commandName = stringLiteralProp(meta, "name") ?? cls.name?.text ?? "<anonymous>";
|
|
20268
20354
|
const uses = await parseInfraResourceUseRefs(infraResourceProp(meta, "uses", ctx), ctx, `command ${commandName}`, sourcePath(ctx.rootDir, cls.getSourceFile().fileName), lineOf(cls));
|
|
@@ -20274,6 +20360,7 @@ async function parseModule(candidate, nameByNode, ctx) {
|
|
|
20274
20360
|
...permission === undefined ? {} : { permission },
|
|
20275
20361
|
...rpc === undefined ? {} : { rpc },
|
|
20276
20362
|
...branches === undefined ? {} : { branches },
|
|
20363
|
+
...branchesFallback === undefined ? {} : { branchesFallback },
|
|
20277
20364
|
transaction: checkedCommandMode(meta, "transaction", ctx, cls.name?.text ?? "<anonymous>") ?? "none",
|
|
20278
20365
|
...audit === undefined ? {} : { audit },
|
|
20279
20366
|
idempotency: checkedCommandMode(meta, "idempotency", ctx, cls.name?.text ?? "<anonymous>") ?? "none",
|
|
@@ -22112,6 +22199,23 @@ function checkedBranches(meta, ctx) {
|
|
|
22112
22199
|
}
|
|
22113
22200
|
return branches;
|
|
22114
22201
|
}
|
|
22202
|
+
function checkedBranchesFallback(meta, ctx) {
|
|
22203
|
+
const expression = getProp(meta, "branchesFallback");
|
|
22204
|
+
if (!expression)
|
|
22205
|
+
return;
|
|
22206
|
+
const value = stringLiteralProp(meta, "branchesFallback");
|
|
22207
|
+
if (value === "reject" || value === "command")
|
|
22208
|
+
return value;
|
|
22209
|
+
ctx.diagnostics.push({
|
|
22210
|
+
severity: "error",
|
|
22211
|
+
code: "invalid-command-branches-fallback",
|
|
22212
|
+
file: sourcePath(ctx.rootDir, expression.getSourceFile().fileName),
|
|
22213
|
+
line: lineOf(expression),
|
|
22214
|
+
message: "Command branchesFallback must be 'reject' or 'command'.",
|
|
22215
|
+
suggestion: "Use branchesFallback: 'command' only when the command handler owns the unmatched-input boundary."
|
|
22216
|
+
});
|
|
22217
|
+
return;
|
|
22218
|
+
}
|
|
22115
22219
|
async function classDeps(cls, ctx) {
|
|
22116
22220
|
const injectable = await parseInjectableOptions(cls, ctx);
|
|
22117
22221
|
const ctor = cls.members.find(ts8.isConstructorDeclaration);
|
|
@@ -23457,11 +23561,13 @@ function assertDatabaseGovernance(value) {
|
|
|
23457
23561
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
23458
23562
|
throw new Error("Invalid databaseGovernance configuration.");
|
|
23459
23563
|
const entry = value;
|
|
23460
|
-
|
|
23564
|
+
const rpcBoundary = entry["rpcBoundary"];
|
|
23565
|
+
const validRpcBoundary = rpcBoundary === undefined || rpcBoundary !== null && typeof rpcBoundary === "object" && !Array.isArray(rpcBoundary) && Object.keys(rpcBoundary).every((key) => ["registry", "catalog", "legacy"].includes(key)) && Object.values(rpcBoundary).length > 0 && Object.values(rpcBoundary).every((path) => typeof path === "string" && path.trim() !== "" && !path.includes("\\") && !path.startsWith("/"));
|
|
23566
|
+
if (entry["mode"] !== "off" && entry["mode"] !== "check" && entry["mode"] !== "enforce" || Object.keys(entry).some((key) => !["mode", "projectRoot", "moduleFile", "databaseUrl", "databaseUrlEnv", "schemas", "rpcBoundary"].includes(key)) || ["projectRoot", "moduleFile", "databaseUrl", "databaseUrlEnv"].some((key) => entry[key] !== undefined && (typeof entry[key] !== "string" || !entry[key].trim())) || entry["databaseUrlEnv"] !== undefined && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(String(entry["databaseUrlEnv"])) || entry["schemas"] !== undefined && (!Array.isArray(entry["schemas"]) || !entry["schemas"].length || !entry["schemas"].every((schema) => typeof schema === "string" && schema.trim())) || !validRpcBoundary) {
|
|
23461
23567
|
throw new Error("Invalid databaseGovernance mode, paths, connection or schemas.");
|
|
23462
23568
|
}
|
|
23463
23569
|
}
|
|
23464
|
-
async function checkDatabaseGovernance(rootDir, value) {
|
|
23570
|
+
async function checkDatabaseGovernance(rootDir, value, rpcCapabilities = []) {
|
|
23465
23571
|
if (value === undefined)
|
|
23466
23572
|
return [];
|
|
23467
23573
|
const failure = () => [{
|
|
@@ -23478,7 +23584,7 @@ async function checkDatabaseGovernance(rootDir, value) {
|
|
|
23478
23584
|
const output = await new Promise((accept, reject) => {
|
|
23479
23585
|
const child = execFile(process.versions.bun ? process.execPath : "bun", ["--no-env-file", worker], { timeout: 30000, killSignal: "SIGKILL", maxBuffer: 1024 * 1024 }, (error, stdout) => error ? reject(error) : accept(stdout));
|
|
23480
23586
|
child.stdin?.on("error", reject);
|
|
23481
|
-
child.stdin?.end(JSON.stringify(options));
|
|
23587
|
+
child.stdin?.end(JSON.stringify({ options, rpcCapabilities }));
|
|
23482
23588
|
});
|
|
23483
23589
|
const report = JSON.parse(output);
|
|
23484
23590
|
if (!report || typeof report !== "object" || !("diagnostics" in report) || !Array.isArray(report.diagnostics))
|
|
@@ -23524,7 +23630,7 @@ async function compileProject(options) {
|
|
|
23524
23630
|
...validateEffectPolicies(graph, options.effect),
|
|
23525
23631
|
...validateJobPolicies(graph)
|
|
23526
23632
|
];
|
|
23527
|
-
const databaseDiagnostics = await checkDatabaseGovernance(options.rootDir, options.databaseGovernance);
|
|
23633
|
+
const databaseDiagnostics = await checkDatabaseGovernance(options.rootDir, options.databaseGovernance, Object.keys(options.commandCapabilities?.rpc ?? {}));
|
|
23528
23634
|
diagnostics.push(...databaseDiagnostics);
|
|
23529
23635
|
if (databaseDiagnostics.length)
|
|
23530
23636
|
return { diagnostics, graph, written: [] };
|
|
@@ -23594,7 +23700,7 @@ async function checkProject(options) {
|
|
|
23594
23700
|
...validateEffectPolicies(graph, options.effect),
|
|
23595
23701
|
...validateJobPolicies(graph)
|
|
23596
23702
|
];
|
|
23597
|
-
const databaseDiagnostics = await checkDatabaseGovernance(options.rootDir, options.databaseGovernance);
|
|
23703
|
+
const databaseDiagnostics = await checkDatabaseGovernance(options.rootDir, options.databaseGovernance, Object.keys(options.commandCapabilities?.rpc ?? {}));
|
|
23598
23704
|
diagnostics.push(...databaseDiagnostics);
|
|
23599
23705
|
if (databaseDiagnostics.length) {
|
|
23600
23706
|
return { diagnostics, graph, upToDate: false, mismatches: ["Database governance verification failed."] };
|
|
@@ -25292,8 +25398,8 @@ function createDeliveryPlan(graph, input, validation) {
|
|
|
25292
25398
|
target.execution = group;
|
|
25293
25399
|
}
|
|
25294
25400
|
}
|
|
25295
|
-
if ([...targets.values()].reduce((count, target) => count + (target.execution
|
|
25296
|
-
fail("delivery-execution-replica-limit", "A release supports at most
|
|
25401
|
+
if ([...targets.values()].reduce((count, target) => count + (target.execution ? (target.execution.replicas ?? 1) * (target.execution.shardCount ?? 1) : 1), 0) > 256) {
|
|
25402
|
+
fail("delivery-execution-replica-limit", "A release supports at most 256 runtime targets including replicas and queue shards.", "Reduce the replica or shard count, or split independent applications.");
|
|
25297
25403
|
}
|
|
25298
25404
|
const canonicalDiagnostics = [...new Map(diagnostics.map((item) => [JSON.stringify(item), item])).values()].sort((a, b) => compare2(JSON.stringify(a), JSON.stringify(b)));
|
|
25299
25405
|
if (canonicalDiagnostics.some((item) => item.severity === "error")) {
|
|
@@ -25384,6 +25490,7 @@ function validateExecutionManifest(plan, capabilities = {}) {
|
|
|
25384
25490
|
...command.adapter === null ? {} : { rpc: command.adapter },
|
|
25385
25491
|
...command.audit === null ? {} : { audit: command.audit },
|
|
25386
25492
|
...command.branches.length === 0 ? {} : { branches: command.branches },
|
|
25493
|
+
...command.branchesFallback === undefined ? {} : { branchesFallback: command.branchesFallback },
|
|
25387
25494
|
transaction: command.transaction,
|
|
25388
25495
|
idempotency: command.idempotency
|
|
25389
25496
|
}))
|
|
@@ -26450,10 +26557,10 @@ import { readFileSync as readFileSync4 } from "node:fs";
|
|
|
26450
26557
|
import { resolve as resolve19 } from "node:path";
|
|
26451
26558
|
|
|
26452
26559
|
// src/migration-app-version.ts
|
|
26453
|
-
var MIGRATION_APP_VERSION = "0.39.
|
|
26560
|
+
var MIGRATION_APP_VERSION = "0.39.1";
|
|
26454
26561
|
|
|
26455
26562
|
// src/migration-elysia-version.ts
|
|
26456
|
-
var MIGRATION_ELYSIA_VERSION = "0.
|
|
26563
|
+
var MIGRATION_ELYSIA_VERSION = "0.42.1";
|
|
26457
26564
|
|
|
26458
26565
|
// src/migration-policy.ts
|
|
26459
26566
|
var cachedCompilerVersion;
|
|
@@ -11,6 +11,10 @@ export interface DatabaseContractsOptions {
|
|
|
11
11
|
role: string;
|
|
12
12
|
/** Role-scoped pg_graphql snapshot exported by graphql-schema. Omit when GraphQL is unused. */
|
|
13
13
|
graphql?: GraphqlOptions;
|
|
14
|
+
/** Registry emitted by application or database code generation. Tracked for contract freshness. */
|
|
15
|
+
rpcRegistry?: string;
|
|
16
|
+
/** Generated or exported RPC catalog. Tracked for contract freshness. */
|
|
17
|
+
rpcCatalog?: string;
|
|
14
18
|
/** Ordered complete migration history. */
|
|
15
19
|
migrations: readonly string[];
|
|
16
20
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { DatabaseGovernanceOptions, Diagnostic } from "./types";
|
|
2
|
-
export declare function enforceDatabaseGovernance(options: DatabaseGovernanceOptions): Promise<{
|
|
2
|
+
export declare function enforceDatabaseGovernance(options: DatabaseGovernanceOptions, rpcCapabilities?: readonly string[]): Promise<{
|
|
3
3
|
diagnostics: Diagnostic[];
|
|
4
4
|
}>;
|