@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/dist/index.js
CHANGED
|
@@ -1089,7 +1089,13 @@ function validateGraph(graph, options = false) {
|
|
|
1089
1089
|
if (command.rpc && command.branches) {
|
|
1090
1090
|
error("command-rpc-branches-conflict", `Command ${command.name} cannot declare both rpc and branches.`, module.file, module.line);
|
|
1091
1091
|
}
|
|
1092
|
+
if (command.branchesFallback !== undefined && command.branchesFallback !== "reject" && command.branchesFallback !== "command") {
|
|
1093
|
+
error("command-branches-fallback-invalid", `Command ${command.name} has an invalid branches fallback.`, module.file, module.line);
|
|
1094
|
+
}
|
|
1092
1095
|
const branchAdapters = command.branches ?? [];
|
|
1096
|
+
if (command.branchesFallback === "command" && branchAdapters.length === 0) {
|
|
1097
|
+
error("command-branches-fallback-without-branches", `Command ${command.name} declares a fallback without branches.`, module.file, module.line);
|
|
1098
|
+
}
|
|
1093
1099
|
for (const branch of branchAdapters) {
|
|
1094
1100
|
if (typeof options !== "object" || !Object.hasOwn(options.commandCapabilities?.rpc ?? {}, branch.rpc)) {
|
|
1095
1101
|
error("command-rpc-unavailable", `Command ${command.name} requires configured RPC adapter '${branch.rpc}'.`, module.file, module.line);
|
|
@@ -1119,7 +1125,7 @@ function validateGraph(graph, options = false) {
|
|
|
1119
1125
|
error("command-transaction-unsupported", `Command ${command.name} branch '${branch.when}' declares transaction, but transaction support is unavailable.`, module.file, module.line);
|
|
1120
1126
|
}
|
|
1121
1127
|
}
|
|
1122
|
-
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)) {
|
|
1128
|
+
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)) {
|
|
1123
1129
|
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.");
|
|
1124
1130
|
}
|
|
1125
1131
|
if (rpcCaps?.boundary === "external" && (command.transaction === "required" || rpcCaps.transaction === true)) {
|
|
@@ -1672,6 +1678,7 @@ function buildContractManifest(graph, artifacts) {
|
|
|
1672
1678
|
module: module.name,
|
|
1673
1679
|
...command.permission === undefined ? {} : { permission: command.permission },
|
|
1674
1680
|
...command.rpc === undefined ? {} : { rpc: command.rpc },
|
|
1681
|
+
...command.branchesFallback === undefined ? {} : { branchesFallback: command.branchesFallback },
|
|
1675
1682
|
...command.branches === undefined ? {} : {
|
|
1676
1683
|
branches: command.branches.map((branch) => ({
|
|
1677
1684
|
when: branch.when,
|
|
@@ -2089,6 +2096,47 @@ function allAspects(module) {
|
|
|
2089
2096
|
...(module.jobs ?? []).flatMap((job) => job.aspects ?? [])
|
|
2090
2097
|
];
|
|
2091
2098
|
}
|
|
2099
|
+
function uniqueStages(paths) {
|
|
2100
|
+
return [...new Set(paths.flat())];
|
|
2101
|
+
}
|
|
2102
|
+
function commandStagePaths(command, aspects) {
|
|
2103
|
+
const prefix = ["commandExecutor", "authorize"];
|
|
2104
|
+
if (command.branches?.length) {
|
|
2105
|
+
const branchPaths = command.branches.map((branch) => [
|
|
2106
|
+
...prefix,
|
|
2107
|
+
`rpc:${branch.rpc}`,
|
|
2108
|
+
...aspects,
|
|
2109
|
+
"handler"
|
|
2110
|
+
]);
|
|
2111
|
+
if (command.branchesFallback === "command") {
|
|
2112
|
+
branchPaths.push([
|
|
2113
|
+
...prefix,
|
|
2114
|
+
...command.idempotency === "required" ? ["idempotency"] : [],
|
|
2115
|
+
...command.transaction === "required" ? ["transaction"] : [],
|
|
2116
|
+
...aspects,
|
|
2117
|
+
"handler",
|
|
2118
|
+
...command.audit ? ["audit"] : []
|
|
2119
|
+
]);
|
|
2120
|
+
}
|
|
2121
|
+
return branchPaths;
|
|
2122
|
+
}
|
|
2123
|
+
return [[
|
|
2124
|
+
...prefix,
|
|
2125
|
+
...command.rpc ? [`rpc:${command.rpc}`] : [
|
|
2126
|
+
...command.idempotency === "required" ? ["idempotency"] : [],
|
|
2127
|
+
...command.transaction === "required" ? ["transaction"] : []
|
|
2128
|
+
],
|
|
2129
|
+
...aspects,
|
|
2130
|
+
"handler",
|
|
2131
|
+
...command.audit && !command.rpc ? ["audit"] : []
|
|
2132
|
+
]];
|
|
2133
|
+
}
|
|
2134
|
+
function planStages(paths, branches) {
|
|
2135
|
+
const stages = uniqueStages(paths);
|
|
2136
|
+
if (branches && !stages.includes("rpc:selected-branch"))
|
|
2137
|
+
stages.splice(2, 0, "rpc:selected-branch");
|
|
2138
|
+
return paths.length > 1 ? { stages, stagePaths: paths } : { stages };
|
|
2139
|
+
}
|
|
2092
2140
|
function createExecutionPlans(graph) {
|
|
2093
2141
|
const aspects = (boundary, refs = []) => refs.map((ref, index) => `${boundary}.aspect[${index}]:${ref.name}`);
|
|
2094
2142
|
return graph.modules.flatMap((module) => [
|
|
@@ -2098,46 +2146,34 @@ function createExecutionPlans(graph) {
|
|
|
2098
2146
|
const command = module.commands.find((item) => item.className === commandRef || item.name === commandRef);
|
|
2099
2147
|
const boundCommand = route.command === undefined ? undefined : command;
|
|
2100
2148
|
const path = `${controller.path}/${route.path}`.replace(/\/+/g, "/");
|
|
2149
|
+
const routeAspects = [
|
|
2150
|
+
...aspects(`module:${module.name}`, module.aspects),
|
|
2151
|
+
...aspects("route", route.aspects),
|
|
2152
|
+
...aspects("command", boundCommand?.aspects)
|
|
2153
|
+
];
|
|
2154
|
+
const stagePaths = boundCommand === undefined ? undefined : commandStagePaths(boundCommand, routeAspects);
|
|
2155
|
+
const plannedStages = stagePaths === undefined ? { stages: [...routeAspects, "handler"] } : planStages(stagePaths, Boolean(boundCommand?.branches?.length));
|
|
2101
2156
|
return {
|
|
2102
2157
|
module: module.name,
|
|
2103
2158
|
kind: "route",
|
|
2104
2159
|
name: `${route.method} ${path.length > 1 ? path.replace(/\/+$/, "") : path}`,
|
|
2105
2160
|
...command ? { command: command.name } : {},
|
|
2106
|
-
|
|
2107
|
-
...boundCommand ? [
|
|
2108
|
-
"commandExecutor",
|
|
2109
|
-
"authorize",
|
|
2110
|
-
...boundCommand.branches?.length ? ["rpc:selected-branch"] : boundCommand.rpc ? [`rpc:${boundCommand.rpc}`] : [
|
|
2111
|
-
...boundCommand.idempotency === "required" ? ["idempotency"] : [],
|
|
2112
|
-
...boundCommand.transaction === "required" ? ["transaction"] : []
|
|
2113
|
-
]
|
|
2114
|
-
] : [],
|
|
2115
|
-
...aspects(`module:${module.name}`, module.aspects),
|
|
2116
|
-
...aspects("route", route.aspects),
|
|
2117
|
-
...aspects("command", boundCommand?.aspects),
|
|
2118
|
-
"handler",
|
|
2119
|
-
...boundCommand?.audit && !boundCommand.rpc && !boundCommand.branches?.length ? ["audit"] : []
|
|
2120
|
-
]
|
|
2161
|
+
...plannedStages
|
|
2121
2162
|
};
|
|
2122
2163
|
})),
|
|
2123
|
-
...module.commands.map((command) =>
|
|
2124
|
-
|
|
2125
|
-
kind: "command",
|
|
2126
|
-
name: command.name,
|
|
2127
|
-
command: command.name,
|
|
2128
|
-
stages: [
|
|
2129
|
-
"commandExecutor",
|
|
2130
|
-
"authorize",
|
|
2131
|
-
...command.branches?.length ? ["rpc:selected-branch"] : command.rpc ? [`rpc:${command.rpc}`] : [
|
|
2132
|
-
...command.idempotency === "required" ? ["idempotency"] : [],
|
|
2133
|
-
...command.transaction === "required" ? ["transaction"] : []
|
|
2134
|
-
],
|
|
2164
|
+
...module.commands.map((command) => {
|
|
2165
|
+
const stagePaths = commandStagePaths(command, [
|
|
2135
2166
|
...aspects(`module:${module.name}`, module.aspects),
|
|
2136
|
-
...aspects("command", command.aspects)
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2167
|
+
...aspects("command", command.aspects)
|
|
2168
|
+
]);
|
|
2169
|
+
return {
|
|
2170
|
+
module: module.name,
|
|
2171
|
+
kind: "command",
|
|
2172
|
+
name: command.name,
|
|
2173
|
+
command: command.name,
|
|
2174
|
+
...planStages(stagePaths, Boolean(command.branches?.length))
|
|
2175
|
+
};
|
|
2176
|
+
}),
|
|
2141
2177
|
...(module.jobs ?? []).map((job) => ({
|
|
2142
2178
|
module: module.name,
|
|
2143
2179
|
kind: "job",
|
|
@@ -2403,6 +2439,7 @@ function renderApplication(graph, options) {
|
|
|
2403
2439
|
permission: command.permission ?? null,
|
|
2404
2440
|
rpc: command.rpc ?? null,
|
|
2405
2441
|
branches: command.branches ?? [],
|
|
2442
|
+
branchesFallback: command.branchesFallback ?? "reject",
|
|
2406
2443
|
transaction: command.transaction ?? null,
|
|
2407
2444
|
audit: command.audit ?? null,
|
|
2408
2445
|
idempotency: command.idempotency ?? null
|
|
@@ -2471,25 +2508,29 @@ function renderApplication(graph, options) {
|
|
|
2471
2508
|
}
|
|
2472
2509
|
};
|
|
2473
2510
|
}));
|
|
2474
|
-
const commands = module.commands.map((command) =>
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2511
|
+
const commands = module.commands.map((command) => {
|
|
2512
|
+
const fallbackCommand = command.branchesFallback === "command";
|
|
2513
|
+
return {
|
|
2514
|
+
name: command.name,
|
|
2515
|
+
className: command.className,
|
|
2516
|
+
executor: "commandExecutor",
|
|
2517
|
+
adapter: command.rpc ?? null,
|
|
2518
|
+
permission: command.permission ?? null,
|
|
2519
|
+
audit: command.audit ?? null,
|
|
2520
|
+
transaction: command.transaction,
|
|
2521
|
+
idempotency: command.idempotency,
|
|
2522
|
+
branches: command.branches ?? [],
|
|
2523
|
+
branchesFallback: command.branchesFallback ?? "reject",
|
|
2524
|
+
...command.tenantScope === undefined ? {} : { tenantScope: command.tenantScope },
|
|
2525
|
+
requiredCapabilities: {
|
|
2526
|
+
permission: true,
|
|
2527
|
+
audit: Boolean(command.audit) && fallbackCommand || (command.branches?.length ? command.branches.some((branch) => Boolean(branch.audit)) : Boolean(command.audit)),
|
|
2528
|
+
idempotency: command.idempotency === "required" && fallbackCommand || (command.branches?.length ? command.branches.some((branch) => branch.idempotency === "required") : command.idempotency === "required"),
|
|
2529
|
+
transaction: command.transaction === "required" && fallbackCommand || (command.branches?.length ? command.branches.some((branch) => branch.transaction === "required") : command.transaction === "required")
|
|
2530
|
+
},
|
|
2531
|
+
aspects: (command.aspects ?? []).map((aspect) => aspect.name)
|
|
2532
|
+
};
|
|
2533
|
+
}).sort((left, right) => left.name.localeCompare(right.name));
|
|
2493
2534
|
const jobs = (module.jobs ?? []).map((job) => ({
|
|
2494
2535
|
name: job.name,
|
|
2495
2536
|
className: job.className,
|
|
@@ -2973,6 +3014,7 @@ ${indent(item, 2)}`).join(",")}
|
|
|
2973
3014
|
`idempotency: ${JSON.stringify(command.idempotency)}`,
|
|
2974
3015
|
...command.tenantScope ? [`tenantScope: ${JSON.stringify(command.tenantScope)}`] : [],
|
|
2975
3016
|
...command.rpc ? [`rpc: ${JSON.stringify(command.rpc)}`] : [],
|
|
3017
|
+
...command.branchesFallback ? [`branchesFallback: ${JSON.stringify(command.branchesFallback)}`] : [],
|
|
2976
3018
|
...command.branches && command.branches.length > 0 ? [`branches: ${JSON.stringify(command.branches)}`] : [],
|
|
2977
3019
|
...command.standalone ? ["standalone: true"] : [],
|
|
2978
3020
|
...command.aspects && command.aspects.length > 0 ? [`aspects: ${this.renderAspects(command.aspects)}`, `aspectPipeline: ${this.renderAspectPipeline(command.aspects)}`] : []
|
|
@@ -4732,6 +4774,7 @@ var HEADER = "// GENERATED BY @supacloud/compiler — do not edit", INTERFACES =
|
|
|
4732
4774
|
export interface CompiledCommand {
|
|
4733
4775
|
rpc?: string;
|
|
4734
4776
|
branches?: readonly CompiledCommandBranch[];
|
|
4777
|
+
branchesFallback?: "reject" | "command";
|
|
4735
4778
|
className: string;
|
|
4736
4779
|
name: string;
|
|
4737
4780
|
permission: string;
|
|
@@ -4864,7 +4907,7 @@ async function compiledAspectStep(context: CompiledAspectContext, handler: () =>
|
|
|
4864
4907
|
}
|
|
4865
4908
|
called = true;
|
|
4866
4909
|
pending = Promise.resolve().then(handler).then(result => {
|
|
4867
|
-
if (strict) freezeCompiledAspectData(result);
|
|
4910
|
+
if (strict && !(result instanceof Response)) freezeCompiledAspectData(result);
|
|
4868
4911
|
value = result;
|
|
4869
4912
|
return result;
|
|
4870
4913
|
});
|
|
@@ -15630,23 +15673,49 @@ var init_value2 = __esm(() => {
|
|
|
15630
15673
|
});
|
|
15631
15674
|
|
|
15632
15675
|
// node_modules/@supacloud/delivery/src/worker-execution.ts
|
|
15676
|
+
function workerShardCount(group) {
|
|
15677
|
+
return group.shardCount ?? 1;
|
|
15678
|
+
}
|
|
15679
|
+
function shardSuffix(shard) {
|
|
15680
|
+
if (!Number.isSafeInteger(shard) || shard < 0 || shard >= 64) {
|
|
15681
|
+
throw new Error("WORKER_SHARD_INVALID");
|
|
15682
|
+
}
|
|
15683
|
+
return `s${shard.toString(36).padStart(2, "0")}`;
|
|
15684
|
+
}
|
|
15685
|
+
function workerShardQueue(group, shard) {
|
|
15686
|
+
const count = workerShardCount(group);
|
|
15687
|
+
if (!Number.isSafeInteger(shard) || shard < 0 || shard >= count) {
|
|
15688
|
+
throw new Error("WORKER_SHARD_INVALID");
|
|
15689
|
+
}
|
|
15690
|
+
if (count === 1)
|
|
15691
|
+
return group.queue;
|
|
15692
|
+
const queue = `${group.queue}_${shardSuffix(shard)}`;
|
|
15693
|
+
if (!/^scw_[a-z0-9_]{1,40}$/.test(queue))
|
|
15694
|
+
throw new Error("WORKER_QUEUE_INVALID");
|
|
15695
|
+
return queue;
|
|
15696
|
+
}
|
|
15633
15697
|
function parseWorkerExecutionGroup(value) {
|
|
15634
15698
|
if (!Check2(WorkerExecutionGroupSchema, value) || value.replicas > value.maxReplicas || !value.queue.endsWith(`_v${value.definitionVersion}`) || value.lifecycle.visibilityTimeoutSeconds < value.lifecycle.executionTimeoutSeconds + value.lifecycle.shutdownGraceSeconds + 30) {
|
|
15635
15699
|
throw new Error("WORKER_EXECUTION_INVALID");
|
|
15636
15700
|
}
|
|
15637
|
-
|
|
15701
|
+
const group = structuredClone(value);
|
|
15702
|
+
for (let shard = 0;shard < workerShardCount(group); shard += 1)
|
|
15703
|
+
workerShardQueue(group, shard);
|
|
15704
|
+
return group;
|
|
15638
15705
|
}
|
|
15639
15706
|
function validateWorkerExecution(groups) {
|
|
15640
15707
|
const names = new Set, targets = new Set, queues = new Set, routes = new Set;
|
|
15641
15708
|
for (const candidate of groups) {
|
|
15642
15709
|
const group = parseWorkerExecutionGroup(candidate);
|
|
15643
15710
|
const route = `${group.taskKey}:${group.definitionVersion}`;
|
|
15644
|
-
|
|
15711
|
+
const physicalQueues = Array.from({ length: workerShardCount(group) }, (_, shard) => workerShardQueue(group, shard));
|
|
15712
|
+
if (names.has(group.name) || targets.has(group.target) || queues.has(group.queue) || physicalQueues.some((queue) => queues.has(queue)) || routes.has(route)) {
|
|
15645
15713
|
throw new Error("WORKER_EXECUTION_CONFLICT");
|
|
15646
15714
|
}
|
|
15647
15715
|
names.add(group.name);
|
|
15648
15716
|
targets.add(group.target);
|
|
15649
15717
|
queues.add(group.queue);
|
|
15718
|
+
physicalQueues.forEach((queue) => queues.add(queue));
|
|
15650
15719
|
routes.add(route);
|
|
15651
15720
|
}
|
|
15652
15721
|
}
|
|
@@ -15670,6 +15739,7 @@ var init_worker_execution = __esm(() => {
|
|
|
15670
15739
|
definitionVersion: String2({ pattern: "^[1-9][0-9]{0,8}$" }),
|
|
15671
15740
|
replicas: positive(16),
|
|
15672
15741
|
maxReplicas: positive(16),
|
|
15742
|
+
shardCount: Optional(positive(64)),
|
|
15673
15743
|
concurrencyPerReplica: positive(32),
|
|
15674
15744
|
resources: ComputeResourcesSchema,
|
|
15675
15745
|
database: _Object_({
|
|
@@ -16079,7 +16149,7 @@ function parseDeliveryBuildManifest(value) {
|
|
|
16079
16149
|
if (!Check2(DeliveryBuildManifestSchema, value))
|
|
16080
16150
|
throw new Error("Invalid delivery build manifest.");
|
|
16081
16151
|
validateWorkerExecution(value.plan.targets.flatMap((target) => target.execution ? [target.execution] : []));
|
|
16082
|
-
if (value.plan.targets.reduce((count, target) => count + (target.execution
|
|
16152
|
+
if (value.plan.targets.reduce((count, target) => count + (target.execution ? (target.execution.replicas ?? 1) * workerShardCount(target.execution) : 1), 0) > 256) {
|
|
16083
16153
|
throw new Error("Invalid delivery replica count.");
|
|
16084
16154
|
}
|
|
16085
16155
|
for (const target of value.plan.targets) {
|
|
@@ -16372,7 +16442,8 @@ var init_execution_snapshot = __esm(() => {
|
|
|
16372
16442
|
kind: kind2,
|
|
16373
16443
|
name: text,
|
|
16374
16444
|
command: Optional(text),
|
|
16375
|
-
stages: _Array_(text)
|
|
16445
|
+
stages: _Array_(text),
|
|
16446
|
+
stagePaths: Optional(_Array_(_Array_(text)))
|
|
16376
16447
|
}, closed3)),
|
|
16377
16448
|
commandAliases: _Array_(_Object_({ module: text, name: text, alias: text }, closed3)),
|
|
16378
16449
|
contexts: _Array_(_Object_({
|
|
@@ -18091,7 +18162,8 @@ function buildTimeline(events, matchedPlans) {
|
|
|
18091
18162
|
attempts: [],
|
|
18092
18163
|
omittedAttempts: 0
|
|
18093
18164
|
},
|
|
18094
|
-
attempts: new Map
|
|
18165
|
+
attempts: new Map,
|
|
18166
|
+
...plan === undefined ? {} : { plan }
|
|
18095
18167
|
};
|
|
18096
18168
|
groups.set(key, group);
|
|
18097
18169
|
}
|
|
@@ -18119,13 +18191,16 @@ function buildTimeline(events, matchedPlans) {
|
|
|
18119
18191
|
const declared = group.entry.declaredStages;
|
|
18120
18192
|
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) => {
|
|
18121
18193
|
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);
|
|
18122
|
-
const
|
|
18194
|
+
const paths = group.plan?.stagePaths && group.plan.stagePaths.length > 0 ? group.plan.stagePaths : [declared];
|
|
18195
|
+
const bestPath = [...paths].sort((left, right) => right.filter((stage) => attempt.observed.has(stage)).length - left.filter((stage) => attempt.observed.has(stage)).length)[0] ?? declared;
|
|
18196
|
+
const terminal = bestPath[bestPath.length - 1];
|
|
18197
|
+
const complete = terminal !== undefined && attempt.stages.some((stage) => stage.stage === terminal && stage.phase === "succeeded");
|
|
18123
18198
|
return {
|
|
18124
18199
|
attempt: attempt.attempt,
|
|
18125
18200
|
stages,
|
|
18126
18201
|
failed: hasFailure(attempt),
|
|
18127
|
-
complete
|
|
18128
|
-
missingStages:
|
|
18202
|
+
complete,
|
|
18203
|
+
missingStages: bestPath.filter((stage) => !attempt.observed.has(stage)),
|
|
18129
18204
|
unexpectedStages: [],
|
|
18130
18205
|
traceIds: [...attempt.traceIds].sort(),
|
|
18131
18206
|
omittedStages: attempt.stages.length - stages.length
|
|
@@ -18547,7 +18622,7 @@ function importPath(out, path) {
|
|
|
18547
18622
|
function parseDatabaseContractsOptions(value, directory) {
|
|
18548
18623
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
18549
18624
|
throw new TypeError("Expected database contracts configuration");
|
|
18550
|
-
const allowed = ["rootDir", "outDir", "postgrestTypes", "modelSchema", "role", "graphql", "migrations"];
|
|
18625
|
+
const allowed = ["rootDir", "outDir", "postgrestTypes", "modelSchema", "role", "graphql", "rpcRegistry", "rpcCatalog", "migrations"];
|
|
18551
18626
|
if (Object.keys(value).some((name) => !allowed.includes(name)))
|
|
18552
18627
|
throw new TypeError("Unknown database contracts option");
|
|
18553
18628
|
const field = (name) => {
|
|
@@ -18563,6 +18638,8 @@ function parseDatabaseContractsOptions(value, directory) {
|
|
|
18563
18638
|
};
|
|
18564
18639
|
const postgrestTypes = optionalPath("postgrestTypes");
|
|
18565
18640
|
const modelSchema = optionalPath("modelSchema");
|
|
18641
|
+
const rpcRegistry = optionalPath("rpcRegistry");
|
|
18642
|
+
const rpcCatalog = optionalPath("rpcCatalog");
|
|
18566
18643
|
const graphqlPresent = Object.hasOwn(value, "graphql");
|
|
18567
18644
|
if (!postgrestTypes && !modelSchema && !graphqlPresent) {
|
|
18568
18645
|
throw new TypeError("Database contracts require postgrestTypes, modelSchema, or graphql");
|
|
@@ -18584,6 +18661,8 @@ function parseDatabaseContractsOptions(value, directory) {
|
|
|
18584
18661
|
...modelSchema ? { modelSchema: resolve24(directory, modelSchema) } : {},
|
|
18585
18662
|
role: field("role"),
|
|
18586
18663
|
...graphql ? { graphql } : {},
|
|
18664
|
+
...rpcRegistry ? { rpcRegistry: resolve24(directory, rpcRegistry) } : {},
|
|
18665
|
+
...rpcCatalog ? { rpcCatalog: resolve24(directory, rpcCatalog) } : {},
|
|
18587
18666
|
migrations: migrations.map((file) => resolve24(directory, file))
|
|
18588
18667
|
};
|
|
18589
18668
|
}
|
|
@@ -18659,6 +18738,10 @@ async function generateDatabaseContracts(options, check = false) {
|
|
|
18659
18738
|
}
|
|
18660
18739
|
if (options.graphql)
|
|
18661
18740
|
await addInput(resolve24(rootDir, options.graphql.schema));
|
|
18741
|
+
if (options.rpcRegistry)
|
|
18742
|
+
await addInput(options.rpcRegistry);
|
|
18743
|
+
if (options.rpcCatalog)
|
|
18744
|
+
await addInput(options.rpcCatalog);
|
|
18662
18745
|
if (new Set(options.migrations.map((path) => resolve24(rootDir, path))).size !== options.migrations.length) {
|
|
18663
18746
|
throw new Error("Duplicate migration in database contracts configuration");
|
|
18664
18747
|
}
|
|
@@ -19838,6 +19921,7 @@ async function analyzeProject(rootDir, include, cache, changedPaths, effectOptio
|
|
|
19838
19921
|
const permission = stringLiteralProp(meta, "permission");
|
|
19839
19922
|
const rpc = checkedRpc(meta, ctx);
|
|
19840
19923
|
const branches = checkedBranches(meta, ctx);
|
|
19924
|
+
const branchesFallback = checkedBranchesFallback(meta, ctx);
|
|
19841
19925
|
const audit = stringLiteralProp(meta, "audit");
|
|
19842
19926
|
const standaloneName = stringLiteralProp(meta, "name") ?? classInfo.decl.name?.text ?? name;
|
|
19843
19927
|
const standaloneUses = await parseInfraResourceUseRefs(infraResourceProp(meta, "uses", ctx), ctx, `command ${standaloneName}`, sourcePath(ctx.rootDir, classInfo.file), lineOf(classInfo.decl));
|
|
@@ -19848,6 +19932,7 @@ async function analyzeProject(rootDir, include, cache, changedPaths, effectOptio
|
|
|
19848
19932
|
...permission === undefined ? {} : { permission },
|
|
19849
19933
|
...rpc === undefined ? {} : { rpc },
|
|
19850
19934
|
...branches === undefined ? {} : { branches },
|
|
19935
|
+
...branchesFallback === undefined ? {} : { branchesFallback },
|
|
19851
19936
|
transaction: checkedCommandMode(meta, "transaction", ctx, classInfo.decl.name?.text ?? name) ?? "none",
|
|
19852
19937
|
...audit === undefined ? {} : { audit },
|
|
19853
19938
|
idempotency: checkedCommandMode(meta, "idempotency", ctx, classInfo.decl.name?.text ?? name) ?? "none",
|
|
@@ -20222,6 +20307,7 @@ async function parseModule(candidate, nameByNode, ctx) {
|
|
|
20222
20307
|
const permission = stringLiteralProp(meta, "permission");
|
|
20223
20308
|
const rpc = checkedRpc(meta, ctx);
|
|
20224
20309
|
const branches = checkedBranches(meta, ctx);
|
|
20310
|
+
const branchesFallback = checkedBranchesFallback(meta, ctx);
|
|
20225
20311
|
const audit = stringLiteralProp(meta, "audit");
|
|
20226
20312
|
const commandName = stringLiteralProp(meta, "name") ?? cls.name?.text ?? "<anonymous>";
|
|
20227
20313
|
const uses = await parseInfraResourceUseRefs(infraResourceProp(meta, "uses", ctx), ctx, `command ${commandName}`, sourcePath(ctx.rootDir, cls.getSourceFile().fileName), lineOf(cls));
|
|
@@ -20233,6 +20319,7 @@ async function parseModule(candidate, nameByNode, ctx) {
|
|
|
20233
20319
|
...permission === undefined ? {} : { permission },
|
|
20234
20320
|
...rpc === undefined ? {} : { rpc },
|
|
20235
20321
|
...branches === undefined ? {} : { branches },
|
|
20322
|
+
...branchesFallback === undefined ? {} : { branchesFallback },
|
|
20236
20323
|
transaction: checkedCommandMode(meta, "transaction", ctx, cls.name?.text ?? "<anonymous>") ?? "none",
|
|
20237
20324
|
...audit === undefined ? {} : { audit },
|
|
20238
20325
|
idempotency: checkedCommandMode(meta, "idempotency", ctx, cls.name?.text ?? "<anonymous>") ?? "none",
|
|
@@ -22071,6 +22158,23 @@ function checkedBranches(meta, ctx) {
|
|
|
22071
22158
|
}
|
|
22072
22159
|
return branches;
|
|
22073
22160
|
}
|
|
22161
|
+
function checkedBranchesFallback(meta, ctx) {
|
|
22162
|
+
const expression = getProp(meta, "branchesFallback");
|
|
22163
|
+
if (!expression)
|
|
22164
|
+
return;
|
|
22165
|
+
const value = stringLiteralProp(meta, "branchesFallback");
|
|
22166
|
+
if (value === "reject" || value === "command")
|
|
22167
|
+
return value;
|
|
22168
|
+
ctx.diagnostics.push({
|
|
22169
|
+
severity: "error",
|
|
22170
|
+
code: "invalid-command-branches-fallback",
|
|
22171
|
+
file: sourcePath(ctx.rootDir, expression.getSourceFile().fileName),
|
|
22172
|
+
line: lineOf(expression),
|
|
22173
|
+
message: "Command branchesFallback must be 'reject' or 'command'.",
|
|
22174
|
+
suggestion: "Use branchesFallback: 'command' only when the command handler owns the unmatched-input boundary."
|
|
22175
|
+
});
|
|
22176
|
+
return;
|
|
22177
|
+
}
|
|
22074
22178
|
async function classDeps(cls, ctx) {
|
|
22075
22179
|
const injectable = await parseInjectableOptions(cls, ctx);
|
|
22076
22180
|
const ctor = cls.members.find(ts8.isConstructorDeclaration);
|
|
@@ -23418,11 +23522,13 @@ function assertDatabaseGovernance(value) {
|
|
|
23418
23522
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
23419
23523
|
throw new Error("Invalid databaseGovernance configuration.");
|
|
23420
23524
|
const entry = value;
|
|
23421
|
-
|
|
23525
|
+
const rpcBoundary = entry["rpcBoundary"];
|
|
23526
|
+
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("/"));
|
|
23527
|
+
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) {
|
|
23422
23528
|
throw new Error("Invalid databaseGovernance mode, paths, connection or schemas.");
|
|
23423
23529
|
}
|
|
23424
23530
|
}
|
|
23425
|
-
async function checkDatabaseGovernance(rootDir, value) {
|
|
23531
|
+
async function checkDatabaseGovernance(rootDir, value, rpcCapabilities = []) {
|
|
23426
23532
|
if (value === undefined)
|
|
23427
23533
|
return [];
|
|
23428
23534
|
const failure = () => [{
|
|
@@ -23439,7 +23545,7 @@ async function checkDatabaseGovernance(rootDir, value) {
|
|
|
23439
23545
|
const output = await new Promise((accept, reject) => {
|
|
23440
23546
|
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));
|
|
23441
23547
|
child.stdin?.on("error", reject);
|
|
23442
|
-
child.stdin?.end(JSON.stringify(options));
|
|
23548
|
+
child.stdin?.end(JSON.stringify({ options, rpcCapabilities }));
|
|
23443
23549
|
});
|
|
23444
23550
|
const report = JSON.parse(output);
|
|
23445
23551
|
if (!report || typeof report !== "object" || !("diagnostics" in report) || !Array.isArray(report.diagnostics))
|
|
@@ -23485,7 +23591,7 @@ async function compileProject(options) {
|
|
|
23485
23591
|
...validateEffectPolicies(graph, options.effect),
|
|
23486
23592
|
...validateJobPolicies(graph)
|
|
23487
23593
|
];
|
|
23488
|
-
const databaseDiagnostics = await checkDatabaseGovernance(options.rootDir, options.databaseGovernance);
|
|
23594
|
+
const databaseDiagnostics = await checkDatabaseGovernance(options.rootDir, options.databaseGovernance, Object.keys(options.commandCapabilities?.rpc ?? {}));
|
|
23489
23595
|
diagnostics.push(...databaseDiagnostics);
|
|
23490
23596
|
if (databaseDiagnostics.length)
|
|
23491
23597
|
return { diagnostics, graph, written: [] };
|
|
@@ -23555,7 +23661,7 @@ async function checkProject(options) {
|
|
|
23555
23661
|
...validateEffectPolicies(graph, options.effect),
|
|
23556
23662
|
...validateJobPolicies(graph)
|
|
23557
23663
|
];
|
|
23558
|
-
const databaseDiagnostics = await checkDatabaseGovernance(options.rootDir, options.databaseGovernance);
|
|
23664
|
+
const databaseDiagnostics = await checkDatabaseGovernance(options.rootDir, options.databaseGovernance, Object.keys(options.commandCapabilities?.rpc ?? {}));
|
|
23559
23665
|
diagnostics.push(...databaseDiagnostics);
|
|
23560
23666
|
if (databaseDiagnostics.length) {
|
|
23561
23667
|
return { diagnostics, graph, upToDate: false, mismatches: ["Database governance verification failed."] };
|
|
@@ -23860,8 +23966,8 @@ function createDeliveryPlan(graph, input, validation) {
|
|
|
23860
23966
|
target.execution = group;
|
|
23861
23967
|
}
|
|
23862
23968
|
}
|
|
23863
|
-
if ([...targets.values()].reduce((count, target) => count + (target.execution
|
|
23864
|
-
fail("delivery-execution-replica-limit", "A release supports at most
|
|
23969
|
+
if ([...targets.values()].reduce((count, target) => count + (target.execution ? (target.execution.replicas ?? 1) * (target.execution.shardCount ?? 1) : 1), 0) > 256) {
|
|
23970
|
+
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.");
|
|
23865
23971
|
}
|
|
23866
23972
|
const canonicalDiagnostics = [...new Map(diagnostics.map((item) => [JSON.stringify(item), item])).values()].sort((a, b) => compare2(JSON.stringify(a), JSON.stringify(b)));
|
|
23867
23973
|
if (canonicalDiagnostics.some((item) => item.severity === "error")) {
|
|
@@ -23951,6 +24057,7 @@ function validateExecutionManifest(plan, capabilities = {}) {
|
|
|
23951
24057
|
...command.adapter === null ? {} : { rpc: command.adapter },
|
|
23952
24058
|
...command.audit === null ? {} : { audit: command.audit },
|
|
23953
24059
|
...command.branches.length === 0 ? {} : { branches: command.branches },
|
|
24060
|
+
...command.branchesFallback === undefined ? {} : { branchesFallback: command.branchesFallback },
|
|
23954
24061
|
transaction: command.transaction,
|
|
23955
24062
|
idempotency: command.idempotency
|
|
23956
24063
|
}))
|
|
@@ -25702,10 +25809,10 @@ import { readFileSync as readFileSync4 } from "node:fs";
|
|
|
25702
25809
|
import { resolve as resolve17 } from "node:path";
|
|
25703
25810
|
|
|
25704
25811
|
// src/migration-app-version.ts
|
|
25705
|
-
var MIGRATION_APP_VERSION = "0.39.
|
|
25812
|
+
var MIGRATION_APP_VERSION = "0.39.1";
|
|
25706
25813
|
|
|
25707
25814
|
// src/migration-elysia-version.ts
|
|
25708
|
-
var MIGRATION_ELYSIA_VERSION = "0.
|
|
25815
|
+
var MIGRATION_ELYSIA_VERSION = "0.42.1";
|
|
25709
25816
|
|
|
25710
25817
|
// src/migration-policy.ts
|
|
25711
25818
|
var cachedCompilerVersion;
|
package/dist/inspect.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export interface ExecutionPlan {
|
|
|
7
7
|
command?: string;
|
|
8
8
|
/** Standard governance contract. Audit success follows the handler; custom executors may short-circuit. */
|
|
9
9
|
stages: string[];
|
|
10
|
+
/** Alternative stage sequences for commands with conditional execution paths. */
|
|
11
|
+
stagePaths?: string[][];
|
|
10
12
|
}
|
|
11
13
|
export interface ContextPack {
|
|
12
14
|
version: 1;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const MIGRATION_APP_VERSION = "0.39.
|
|
1
|
+
export declare const MIGRATION_APP_VERSION = "0.39.1";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const MIGRATION_ELYSIA_VERSION = "0.
|
|
1
|
+
export declare const MIGRATION_ELYSIA_VERSION = "0.42.1";
|
package/dist/types.d.ts
CHANGED
|
@@ -324,6 +324,7 @@ export interface ControllerNode {
|
|
|
324
324
|
export interface CommandNode {
|
|
325
325
|
rpc?: string;
|
|
326
326
|
branches?: CommandBranchNode[];
|
|
327
|
+
branchesFallback?: "reject" | "command";
|
|
327
328
|
className: string;
|
|
328
329
|
name: string;
|
|
329
330
|
permission?: string;
|
|
@@ -432,6 +433,7 @@ export interface ExecutionPlanCommand {
|
|
|
432
433
|
transaction: CommandNode["transaction"];
|
|
433
434
|
idempotency: CommandNode["idempotency"];
|
|
434
435
|
branches: CommandBranchNode[];
|
|
436
|
+
branchesFallback: "reject" | "command";
|
|
435
437
|
tenantScope?: CommandNode["tenantScope"];
|
|
436
438
|
requiredCapabilities: {
|
|
437
439
|
permission: boolean;
|
|
@@ -773,6 +775,15 @@ export interface DatabaseGovernanceOptions {
|
|
|
773
775
|
databaseUrlEnv?: string;
|
|
774
776
|
/** Schemas included in the live Catalog query. */
|
|
775
777
|
schemas?: string[];
|
|
778
|
+
/** Optional offline gate connecting named RPC capabilities to maintained artifacts. */
|
|
779
|
+
rpcBoundary?: {
|
|
780
|
+
/** JSON registry produced by application or database code generation. */
|
|
781
|
+
registry?: string;
|
|
782
|
+
/** Generated or exported RPC catalog JSON. */
|
|
783
|
+
catalog?: string;
|
|
784
|
+
/** JSON list/object containing explicitly historical RPC capability names. */
|
|
785
|
+
legacy?: string;
|
|
786
|
+
};
|
|
776
787
|
}
|
|
777
788
|
export interface ModuleBoundaryRule {
|
|
778
789
|
/** Source module tag pattern or tag (for example, 'type:ui', 'scope:case', or '*'). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supacloud/compiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.60.0",
|
|
4
4
|
"description": "Static compiler for @supacloud/app metadata: builds the application graph from AST, validates it, and generates reflection-free factory code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
"directory": "packages/compiler"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@supacloud/db": "0.
|
|
61
|
-
"@supacloud/delivery": "0.
|
|
60
|
+
"@supacloud/db": "0.24.0",
|
|
61
|
+
"@supacloud/delivery": "0.26.0",
|
|
62
62
|
"typebox": "1.4.3",
|
|
63
63
|
"@graphql-codegen/core": "^6.2.2",
|
|
64
64
|
"@graphql-codegen/typed-document-node": "^7.2.0",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"typescript": "7.1.0-beta"
|
|
69
69
|
},
|
|
70
70
|
"overrides": {
|
|
71
|
-
"@supacloud/contracts": "0.16.
|
|
71
|
+
"@supacloud/contracts": "0.16.1",
|
|
72
72
|
"@graphql-tools/utils": "^12.0.4",
|
|
73
73
|
"@whatwg-node/promise-helpers": "2.0.0",
|
|
74
74
|
"deepmerge-ts": "8.0.2",
|