gencow 0.1.209 → 0.1.211

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.
@@ -36,6 +36,10 @@ function projectSchemaSourcePaths({
36
36
  const missing = declared.filter((pathValue) => !existsSyncImpl(pathValue));
37
37
  if (missing.length > 0) throw new Error("Configured schema source is missing from the project");
38
38
  }
39
+ // An explicit schema list is the customer's immutable migration contract.
40
+ // Deploy codegen may materialize a compatibility auth schema later in the
41
+ // same workspace; it must not silently widen that declared contract.
42
+ if (config?.schema) return declared.filter((pathValue) => existsSyncImpl(pathValue));
39
43
  return resolveDrizzleSchemaPaths(config ?? { rootDir: backendDir }, {
40
44
  cwd,
41
45
  existsSyncImpl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gencow",
3
- "version": "0.1.209",
3
+ "version": "0.1.211",
4
4
  "description": "Gencow — AI Backend Engine",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,9 +33,9 @@
33
33
  "devDependencies": {
34
34
  "@types/node": "^25.9.5",
35
35
  "better-auth": "^1.6.23",
36
- "@gencow/client": "0.2.6",
37
36
  "@gencow/core": "0.1.43",
38
- "@gencow/react": "0.2.6"
37
+ "@gencow/react": "0.2.6",
38
+ "@gencow/client": "0.2.6"
39
39
  },
40
40
  "scripts": {
41
41
  "prebuild": "pnpm --filter @gencow/migration-contract run build && pnpm --filter @gencow/server run build",
@@ -9660,7 +9660,13 @@ var init_deployment_executor_authority_contract = __esm({
9660
9660
  objectAuthority("app_runtime_activity_state", ["SELECT", "INSERT", "UPDATE"]),
9661
9661
  objectAuthority("app_runtime_activity_observers", ["SELECT", "INSERT", "UPDATE", "DELETE"]),
9662
9662
  objectAuthority("app_runtime_activity_leases", ["SELECT", "INSERT", "UPDATE", "DELETE"]),
9663
- objectAuthority("runtime_drain_requests", ["SELECT"]),
9663
+ // runtime_stop_intents is owned by the public runtime role and references a
9664
+ // drain request. PostgreSQL's FK check takes a KEY SHARE lock, which needs
9665
+ // UPDATE authority on one referenced column. Constrain that capability to
9666
+ // the immutable drain-request identity.
9667
+ objectAuthority("runtime_drain_requests", ["SELECT"], {
9668
+ columnPrivileges: [{ columns: ["id"], privileges: ["UPDATE"] }]
9669
+ }),
9664
9670
  // The public Platform owns node heartbeat, readiness, capacity admission,
9665
9671
  // and authenticated node administration. These are system-plane data
9666
9672
  // mutations, not lifecycle execution authority.
@@ -110044,32 +110050,6 @@ var require_output = __commonJS({
110044
110050
  }
110045
110051
  });
110046
110052
 
110047
- // ../../node_modules/.pnpm/@img+sharp-wasm32@0.34.5/node_modules/@img/sharp-wasm32/versions.json
110048
- var require_versions = __commonJS({
110049
- "../../node_modules/.pnpm/@img+sharp-wasm32@0.34.5/node_modules/@img/sharp-wasm32/versions.json"(exports2, module2) {
110050
- module2.exports = {
110051
- aom: "3.13.1",
110052
- cgif: "0.5.0",
110053
- emscripten: "4.0.18",
110054
- exif: "0.6.25",
110055
- expat: "2.7.3",
110056
- ffi: "3.5.2",
110057
- glib: "2.86.1",
110058
- heif: "1.20.2",
110059
- highway: "1.3.0",
110060
- imagequant: "2.4.1",
110061
- lcms: "2.17",
110062
- mozjpeg: "0826579",
110063
- resvg: "0.45.1",
110064
- spng: "0.7.4",
110065
- tiff: "4.7.1",
110066
- vips: "8.17.3",
110067
- webp: "1.6.0",
110068
- "zlib-ng": "2.2.5"
110069
- };
110070
- }
110071
- });
110072
-
110073
110053
  // ../../node_modules/.pnpm/sharp@0.34.5/node_modules/sharp/lib/utility.js
110074
110054
  var require_utility = __commonJS({
110075
110055
  "../../node_modules/.pnpm/sharp@0.34.5/node_modules/sharp/lib/utility.js"(exports2, module2) {
@@ -110114,7 +110094,7 @@ var require_utility = __commonJS({
110114
110094
  }
110115
110095
  } else {
110116
110096
  try {
110117
- versions = require_versions();
110097
+ versions = __require("@img/sharp-wasm32/versions");
110118
110098
  } catch (_4) {
110119
110099
  }
110120
110100
  }
@@ -185398,7 +185378,8 @@ function fleetSelection() {
185398
185378
  return {
185399
185379
  ...bindingSelection(),
185400
185380
  authorityMode: appRuntimeDesiredState.authorityMode,
185401
- desiredRuntimeState: appRuntimeDesiredState.desiredRuntimeState
185381
+ desiredRuntimeState: appRuntimeDesiredState.desiredRuntimeState,
185382
+ hasBackend: apps.hasBackend
185402
185383
  };
185403
185384
  }
185404
185385
  function queryBindingRows(db) {
@@ -185450,6 +185431,7 @@ function fleetAuthorityDigest(rows4) {
185450
185431
  row.appId,
185451
185432
  row.appName,
185452
185433
  row.authorityMode,
185434
+ row.hasBackend,
185453
185435
  row.desiredActivationId,
185454
185436
  row.desiredRuntimeState,
185455
185437
  row.desiredActivationEpoch,
@@ -185509,7 +185491,7 @@ async function loadCanonicalTenantRouteFleetSnapshot(input) {
185509
185491
  const removalTargets = [];
185510
185492
  for (const appRows of grouped.values()) {
185511
185493
  const desired = appRows[0];
185512
- if (desired.desiredRuntimeState === "serving" && desired.desiredNodeId === input.nodeId) {
185494
+ if (desired.hasBackend && desired.desiredRuntimeState === "serving" && desired.desiredNodeId === input.nodeId) {
185513
185495
  const joinedRows = appRows.filter((row) => row.instanceId !== null);
185514
185496
  try {
185515
185497
  bindings.push(bindingFromRows(joinedRows));
@@ -401317,7 +401299,7 @@ function requestLocalHealth(url2, init) {
401317
401299
  },
401318
401300
  (response) => {
401319
401301
  const status = response.statusCode ?? 0;
401320
- response.once("end", () => resolve115({ ok: status >= 200 && status < 300 }));
401302
+ resolve115({ ok: status >= 200 && status < 300 });
401321
401303
  response.resume();
401322
401304
  }
401323
401305
  );
@@ -415089,7 +415071,7 @@ async function persistDecision(tx, input, current, requiredTargets) {
415089
415071
  updated_at = NOW()
415090
415072
  WHERE app_id = ${input.appId}
415091
415073
  AND app_instance_id = ${input.appInstanceId}
415092
- AND route_epoch = ${input.expectedRouteEpoch}
415074
+ AND route_epoch = ${Number(current.route_epoch)}
415093
415075
  RETURNING app_id
415094
415076
  `) : await tx.execute(sql79`
415095
415077
  INSERT INTO app_serving_route_authorities (
@@ -415212,8 +415194,31 @@ async function persistPreparedDecision(tx, input, current, requiredTargets) {
415212
415194
  }
415213
415195
  return outcome(input, routeEpoch, predecessorHash, eventId, publicationSequence, false);
415214
415196
  }
415215
- async function assertAuthorityEpoch(current, input) {
415216
- if (current === null && input.expectedRouteEpoch !== 0 || current !== null && (current.environment !== input.environment || current.app_instance_id !== input.appInstanceId || Number(current.route_epoch) !== input.expectedRouteEpoch)) {
415197
+ async function loadLatestReservedEpoch(tx, input) {
415198
+ const result2 = await tx.execute(sql79`
415199
+ SELECT COALESCE(MAX(route_epoch), 0) AS route_epoch
415200
+ FROM app_serving_route_outbox
415201
+ WHERE app_id = ${input.appId}
415202
+ AND app_instance_id = ${input.appInstanceId}
415203
+ AND environment = ${input.environment}
415204
+ `);
415205
+ const routeEpoch = Number(
415206
+ rowsFromResult31(result2)[0]?.route_epoch ?? 0
415207
+ );
415208
+ if (!Number.isSafeInteger(routeEpoch) || routeEpoch < 0) fail13("APP_SERVING_ROUTE_EPOCH_CONFLICT");
415209
+ return routeEpoch;
415210
+ }
415211
+ async function assertPrepareAuthorityEpoch(tx, current, input) {
415212
+ const authorityEpoch = current ? Number(current.route_epoch) : 0;
415213
+ const reservedEpoch = await loadLatestReservedEpoch(tx, input);
415214
+ if (current !== null && (current.environment !== input.environment || current.app_instance_id !== input.appInstanceId) || authorityEpoch > input.expectedRouteEpoch || Math.max(authorityEpoch, reservedEpoch) !== input.expectedRouteEpoch) {
415215
+ fail13("APP_SERVING_ROUTE_EPOCH_CONFLICT");
415216
+ }
415217
+ }
415218
+ async function assertCommitPreparedAuthorityEpoch(tx, current, input) {
415219
+ const authorityEpoch = current ? Number(current.route_epoch) : 0;
415220
+ const reservedEpoch = await loadLatestReservedEpoch(tx, input);
415221
+ if (current !== null && (current.environment !== input.environment || current.app_instance_id !== input.appInstanceId) || authorityEpoch > input.expectedRouteEpoch || reservedEpoch !== input.expectedRouteEpoch + 1) {
415217
415222
  fail13("APP_SERVING_ROUTE_EPOCH_CONFLICT");
415218
415223
  }
415219
415224
  }
@@ -415221,7 +415226,6 @@ async function prepareNormalizedDecision(tx, input, acquireLock) {
415221
415226
  if (input.phase !== "prepare") fail13("APP_SERVING_ROUTE_DECISION_INVALID");
415222
415227
  await acquireLock(tx, `app-serving-route:${input.appId}`);
415223
415228
  const current = await loadAuthority(tx, input.appId);
415224
- await assertAuthorityEpoch(current, input);
415225
415229
  const requiredTargets = await resolveAppServingRouteTargetSet({
415226
415230
  db: tx,
415227
415231
  environment: input.environment,
@@ -415231,13 +415235,14 @@ async function prepareNormalizedDecision(tx, input, acquireLock) {
415231
415235
  });
415232
415236
  const replay = await inspectPreparedReplay(tx, input, current, requiredTargets);
415233
415237
  if (replay) return replay;
415238
+ await assertPrepareAuthorityEpoch(tx, current, input);
415234
415239
  return persistPreparedDecision(tx, input, current, requiredTargets);
415235
415240
  }
415236
415241
  async function commitPreparedNormalizedDecision(tx, input, acquireLock) {
415237
415242
  if (input.phase !== "commit") fail13("APP_SERVING_ROUTE_DECISION_INVALID");
415238
415243
  await acquireLock(tx, `app-serving-route:${input.appId}`);
415239
415244
  const current = await loadAuthority(tx, input.appId);
415240
- await assertAuthorityEpoch(current, input);
415245
+ await assertCommitPreparedAuthorityEpoch(tx, current, input);
415241
415246
  const preparedInput = { ...input, phase: "prepare" };
415242
415247
  const requiredTargets = await resolveAppServingRouteTargetSet({
415243
415248
  db: tx,
@@ -415532,8 +415537,42 @@ async function commitAppServingRouteSourceInTransaction(input) {
415532
415537
  if (authority && (authority.environment !== input.environment || authority.app_instance_id !== input.source.appInstanceId)) {
415533
415538
  fail15("APP_SERVING_ROUTE_SOURCE_DECISION_CONFLICT");
415534
415539
  }
415535
- const expectedRouteEpoch = authority ? Number(authority.route_epoch) : 0;
415536
- if (!Number.isSafeInteger(expectedRouteEpoch) || expectedRouteEpoch < 0) {
415540
+ const reservedResult = await input.tx.execute(sql80`
415541
+ SELECT COALESCE(MAX(route_epoch), 0) AS route_epoch
415542
+ FROM app_serving_route_outbox
415543
+ WHERE app_id = ${input.source.appId}
415544
+ AND app_instance_id = ${input.source.appInstanceId}
415545
+ AND environment = ${input.environment}
415546
+ `);
415547
+ const authorityEpoch = authority ? Number(authority.route_epoch) : 0;
415548
+ const reservedEpoch = Number(
415549
+ rowsFromResult32(reservedResult)[0]?.route_epoch ?? 0
415550
+ );
415551
+ let expectedRouteEpoch = Math.max(authorityEpoch, reservedEpoch);
415552
+ if (input.phase === "commit") {
415553
+ const preparedResult = await input.tx.execute(sql80`
415554
+ SELECT event_id, publication_sequence, environment, app_id, app_instance_id, route_epoch,
415555
+ phase, disposition, mutation_kind, entry_hash, predecessor_hash, topology_revision,
415556
+ target_set_id, source_revision
415557
+ FROM app_serving_route_outbox
415558
+ WHERE app_id = ${input.source.appId}
415559
+ AND app_instance_id = ${input.source.appInstanceId}
415560
+ AND mutation_kind = ${input.mutationKind}
415561
+ AND source_revision = ${input.sourceRevision}
415562
+ AND phase = 'prepare'
415563
+ FOR UPDATE
415564
+ `);
415565
+ const prepared = rowsFromResult32(preparedResult)[0];
415566
+ const preparedEpoch = Number(prepared?.route_epoch);
415567
+ const preparedEntryHash = Number.isSafeInteger(preparedEpoch) ? hashServingRouteManifestEntry(
415568
+ compileAppServingRouteEntry({ ...input.source, routeEpoch: preparedEpoch })
415569
+ ) : "";
415570
+ if (!prepared || prepared.environment !== input.environment || prepared.disposition !== input.source.disposition || prepared.topology_revision !== input.topologyRevision || prepared.target_set_id !== input.targetSetId || prepared.entry_hash !== preparedEntryHash || preparedEpoch < 1) {
415571
+ fail15("APP_SERVING_ROUTE_SOURCE_DECISION_CONFLICT");
415572
+ }
415573
+ expectedRouteEpoch = preparedEpoch - 1;
415574
+ }
415575
+ if (!Number.isSafeInteger(authorityEpoch) || authorityEpoch < 0 || !Number.isSafeInteger(reservedEpoch) || !Number.isSafeInteger(expectedRouteEpoch)) {
415537
415576
  fail15("APP_SERVING_ROUTE_SOURCE_DECISION_CONFLICT");
415538
415577
  }
415539
415578
  const entry = compileAppServingRouteEntry({
@@ -415579,9 +415618,6 @@ function fail16(code) {
415579
415618
  }
415580
415619
  async function recordActivationServingRoute(input) {
415581
415620
  if (!input.publication) return void 0;
415582
- if (input.deploymentEnvironment !== input.publication.environment) {
415583
- return fail16("ACTIVATION_SERVING_ROUTE_ENVIRONMENT_MISMATCH");
415584
- }
415585
415621
  const app2 = (await input.tx.select({
415586
415622
  instanceId: apps.instanceId,
415587
415623
  name: apps.name,
@@ -415707,7 +415743,6 @@ async function prepareCandidateActivationServingRoute(input) {
415707
415743
  publication: input.servingRoutePublication,
415708
415744
  acquireLock,
415709
415745
  appId: input.appId,
415710
- deploymentEnvironment: deployment.env,
415711
415746
  activationId: input.activationId,
415712
415747
  instanceId: input.instanceId,
415713
415748
  nodeId: activation.nodeId,
@@ -415871,7 +415906,6 @@ async function commitCandidateActivation(input) {
415871
415906
  publication: input.servingRoutePublication,
415872
415907
  acquireLock,
415873
415908
  appId: input.appId,
415874
- deploymentEnvironment: deploymentRows2[0].env,
415875
415909
  activationId: input.activationId,
415876
415910
  instanceId: input.instanceId,
415877
415911
  nodeId: activation.nodeId,
@@ -415988,7 +416022,6 @@ async function commitCandidateActivation(input) {
415988
416022
  publication: input.servingRoutePublication,
415989
416023
  acquireLock,
415990
416024
  appId: input.appId,
415991
- deploymentEnvironment: deploymentRows[0].env,
415992
416025
  activationId: input.activationId,
415993
416026
  instanceId: input.instanceId,
415994
416027
  nodeId: activation.nodeId,
@@ -419033,6 +419066,7 @@ async function runImmutableDeploymentCandidate(input) {
419033
419066
  () => input.prepareSchema({ workspacePath: generation.workspacePath }),
419034
419067
  preparation
419035
419068
  );
419069
+ await input.enterRuntimePreparation?.();
419036
419070
  if (!existingReady) {
419037
419071
  await runWithCandidateFailurePhase(
419038
419072
  "workspace_build",
@@ -421259,10 +421293,8 @@ async function deployWithRuntimeControlPlane(input) {
421259
421293
  verifyPreparedSchema: async (revisionId) => {
421260
421294
  await assertSchemaRevisionAllowsCandidate({ db: input.db, revisionId });
421261
421295
  },
421262
- build: async ({ workspacePath }) => {
421263
- await input.onExecutorPhase?.("runtime_preparing");
421264
- return provisioner.prepareWorkspace(input.app.name, workspacePath);
421265
- },
421296
+ enterRuntimePreparation: () => input.onExecutorPhase?.("runtime_preparing") ?? Promise.resolve(),
421297
+ build: ({ workspacePath }) => provisioner.prepareWorkspace(input.app.name, workspacePath),
421266
421298
  prepareIdentity,
421267
421299
  bootAndAttest: async (context2) => {
421268
421300
  await input.onExecutorPhase?.("candidate_attesting");
@@ -424053,7 +424085,7 @@ function loadAppServingRouteRolloutConfig(input) {
424053
424085
  const mode = route.publicationMode;
424054
424086
  if (mode === "off") return null;
424055
424087
  if (mode !== "enforce" || !Number.isSafeInteger(input.appId) || input.appId < 1) return fail30();
424056
- const environment = input.deploymentEnvironment;
424088
+ const deploymentEnvironment = input.deploymentEnvironment;
424057
424089
  const configuredEnvironment = route.environment ?? void 0;
424058
424090
  const topologyRevision = route.topologyRevision ?? "";
424059
424091
  const upsertTargetSetId = route.upsertTargetSetId ?? "";
@@ -424065,24 +424097,24 @@ function loadAppServingRouteRolloutConfig(input) {
424065
424097
  } catch {
424066
424098
  return fail30();
424067
424099
  }
424068
- if (!ENVIRONMENTS3.has(environment) || !ENVIRONMENTS3.has(configuredEnvironment)) {
424100
+ if (!ENVIRONMENTS3.has(deploymentEnvironment) || !ENVIRONMENTS3.has(configuredEnvironment)) {
424069
424101
  return fail30();
424070
424102
  }
424071
- if (configuredEnvironment !== environment) return null;
424103
+ const servingEnvironment = configuredEnvironment;
424072
424104
  if (!REFERENCE4.test(topologyRevision) || !REFERENCE4.test(upsertTargetSetId) || !REFERENCE4.test(tombstoneTargetSetId)) {
424073
424105
  return fail30();
424074
424106
  }
424075
424107
  if (allowlist3 !== "all" && !allowlist3.has(input.appId)) return null;
424076
424108
  return {
424077
424109
  activation: {
424078
- environment,
424110
+ environment: servingEnvironment,
424079
424111
  baseDomain,
424080
424112
  topologyRevision,
424081
424113
  targetSetId: upsertTargetSetId,
424082
424114
  backendRoutePrefixes: MANAGED_BACKEND_ROUTE_PREFIXES
424083
424115
  },
424084
424116
  deletion: {
424085
- environment,
424117
+ environment: servingEnvironment,
424086
424118
  baseDomain,
424087
424119
  topologyRevision,
424088
424120
  tombstoneTargetSetId
@@ -427993,10 +428025,15 @@ async function assertAppDeleteTreeMutationReady(input) {
427993
428025
  if (childRows.some((child) => child.userId !== input.ownerId)) {
427994
428026
  throw new AppDeletePreAdmissionError(input.correlationId);
427995
428027
  }
427996
- const targetNodeIds = /* @__PURE__ */ new Set([
427997
- input.app.nodeId,
427998
- ...childRows.map((child) => child.nodeId)
427999
- ]);
428028
+ const currentNodeId = input.currentNodeId?.trim() || process.env.GENCOW_NODE_ID?.trim() || null;
428029
+ const targetNodeIds = new Set(
428030
+ [input.app.nodeId ?? currentNodeId, ...childRows.map((child) => child.nodeId)].map((nodeId) => nodeId?.trim() || null).filter((nodeId) => nodeId !== null)
428031
+ );
428032
+ if (targetNodeIds.size === 0) {
428033
+ throw new PlatformMutationAdmissionError("PLATFORM_MUTATION_IDENTITY_INVALID", {
428034
+ correlationId: input.correlationId
428035
+ });
428036
+ }
428000
428037
  const assertTargetReady = input.assertTargetReadyImpl ?? assertTargetPlatformMutationReady;
428001
428038
  for (const nodeId of targetNodeIds) {
428002
428039
  await assertTargetReady({
@@ -428014,7 +428051,7 @@ async function isAppDeleteExecutorAvailable(input) {
428014
428051
  if (!input.operation.appInstanceId) return false;
428015
428052
  try {
428016
428053
  const rows4 = await input.db.select({ nodeId: apps.nodeId }).from(apps).where(and69(eq75(apps.id, input.operation.appId), eq75(apps.instanceId, input.operation.appInstanceId))).limit(1);
428017
- nodeId = rows4[0]?.nodeId?.trim() || null;
428054
+ nodeId = rows4[0]?.nodeId?.trim() || input.currentNodeId?.trim() || process.env.GENCOW_NODE_ID?.trim() || null;
428018
428055
  } catch {
428019
428056
  return false;
428020
428057
  }