@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 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`, or `graphql`). The generated
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, migration
118
- content/order and generated artifacts without writing. This is an offline check,
119
- not proof of live database parity or authorization. Refresh snapshots from the
120
- intended database and role before running it. Keep runtime decoders at untrusted
121
- boundaries; protocol result shapes are not interchangeable.
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
- stages: [
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
- module: module.name,
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
- "handler",
2139
- ...command.audit && !command.rpc && !command.branches?.length ? ["audit"] : []
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
- name: command.name,
2477
- className: command.className,
2478
- executor: "commandExecutor",
2479
- adapter: command.rpc ?? null,
2480
- permission: command.permission ?? null,
2481
- audit: command.audit ?? null,
2482
- transaction: command.transaction,
2483
- idempotency: command.idempotency,
2484
- branches: command.branches ?? [],
2485
- ...command.tenantScope === undefined ? {} : { tenantScope: command.tenantScope },
2486
- requiredCapabilities: {
2487
- permission: true,
2488
- audit: command.branches?.length ? command.branches.some((branch) => Boolean(branch.audit)) : Boolean(command.audit),
2489
- idempotency: command.branches?.length ? command.branches.some((branch) => branch.idempotency === "required") : command.idempotency === "required",
2490
- transaction: command.branches?.length ? command.branches.some((branch) => branch.transaction === "required") : command.transaction === "required"
2491
- },
2492
- aspects: (command.aspects ?? []).map((aspect) => aspect.name)
2493
- })).sort((left, right) => left.name.localeCompare(right.name));
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 terminal = declared[declared.length - 1];
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: terminal !== undefined && attempt.stages.some((stage) => stage.stage === terminal && stage.phase === "succeeded"),
16018
- missingStages: declared.filter((stage) => !attempt.observed.has(stage)),
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
- return structuredClone(value);
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
- if (names.has(group.name) || targets.has(group.target) || queues.has(group.queue) || routes.has(route)) {
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?.replicas ?? 1), 0) > 32) {
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
- if (entry["mode"] !== "off" && entry["mode"] !== "check" && entry["mode"] !== "enforce" || Object.keys(entry).some((key) => !["mode", "projectRoot", "moduleFile", "databaseUrl", "databaseUrlEnv", "schemas"].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()))) {
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?.replicas ?? 1), 0) > 32) {
25296
- fail("delivery-execution-replica-limit", "A release supports at most 32 runtime targets including replicas.", "Reduce the replica count or split independent applications.");
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.0";
26560
+ var MIGRATION_APP_VERSION = "0.39.1";
26454
26561
 
26455
26562
  // src/migration-elysia-version.ts
26456
- var MIGRATION_ELYSIA_VERSION = "0.41.0";
26563
+ var MIGRATION_ELYSIA_VERSION = "0.42.1";
26457
26564
 
26458
26565
  // src/migration-policy.ts
26459
26566
  var cachedCompilerVersion;
@@ -7,6 +7,7 @@ export interface ContractManifest {
7
7
  module: string;
8
8
  permission?: string;
9
9
  rpc?: string;
10
+ branchesFallback?: "reject" | "command";
10
11
  branches?: Array<{
11
12
  when: string;
12
13
  adapter: string;
@@ -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
  }>;