gencow 0.1.180 → 0.1.181

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.
@@ -0,0 +1,67 @@
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, lstatSync, readFileSync, readdirSync } from "node:fs";
3
+ import { relative, resolve } from "node:path";
4
+
5
+ import { shouldIncludeTarEntry } from "./tar-archive.mjs";
6
+
7
+ function sha256(value) {
8
+ return createHash("sha256").update(value).digest("hex");
9
+ }
10
+
11
+ function canonicalPath(value) {
12
+ return value.replaceAll("\\", "/").normalize("NFC");
13
+ }
14
+
15
+ function collectFiles(backendRoot, archiveRoot, excludes, directory = backendRoot) {
16
+ const files = [];
17
+ for (const entry of readdirSync(directory).sort()) {
18
+ const path = resolve(directory, entry);
19
+ const stats = lstatSync(path);
20
+ if (stats.isDirectory()) {
21
+ files.push(...collectFiles(backendRoot, archiveRoot, excludes, path));
22
+ continue;
23
+ }
24
+ if (!stats.isFile() || stats.isSymbolicLink()) {
25
+ throw new Error(`Bundle entry ${JSON.stringify(entry)} must be a regular file`);
26
+ }
27
+ const relativePath = canonicalPath(relative(backendRoot, path));
28
+ const archivePath = canonicalPath(`${archiveRoot}/${relativePath}`);
29
+ if (!shouldIncludeTarEntry(archivePath, excludes)) continue;
30
+ const bytes = readFileSync(path);
31
+ files.push({ relativePath, archivePath, size: bytes.length, sha256: sha256(bytes) });
32
+ }
33
+ return files;
34
+ }
35
+
36
+ function assertUniqueCanonicalPaths(files) {
37
+ const exact = new Set();
38
+ const folded = new Set();
39
+ for (const file of files) {
40
+ const caseFolded = file.relativePath.toLocaleLowerCase("en-US");
41
+ if (exact.has(file.relativePath) || folded.has(caseFolded)) {
42
+ throw new Error("Backend bundle paths must be unique without Unicode or case-fold collisions");
43
+ }
44
+ exact.add(file.relativePath);
45
+ folded.add(caseFolded);
46
+ }
47
+ }
48
+
49
+ export function buildBackendBundleFilePlan({ backendRoot, archiveRoot = "gencow", excludes = [] }) {
50
+ const normalizedArchiveRoot = canonicalPath(archiveRoot).replace(/^\.\/+|\/+$/gu, "");
51
+ if (!normalizedArchiveRoot || normalizedArchiveRoot.includes("..")) {
52
+ throw new Error("Backend bundle archive root is invalid");
53
+ }
54
+ const files = collectFiles(backendRoot, normalizedArchiveRoot, excludes);
55
+ assertUniqueCanonicalPaths(files);
56
+ const bundleFileRows = files.map((file) => [file.relativePath, file.size, file.sha256]);
57
+ const archiveEntries = files.map((file) => file.archivePath);
58
+ const migrationsRoot = resolve(backendRoot, "migrations");
59
+ if (existsSync(migrationsRoot) && !files.some((file) => file.relativePath.startsWith("migrations/"))) {
60
+ archiveEntries.push(`${normalizedArchiveRoot}/migrations/`);
61
+ }
62
+ return {
63
+ archiveEntries,
64
+ bundleFileRows,
65
+ bundleSha256: sha256(JSON.stringify(bundleFileRows)),
66
+ };
67
+ }
@@ -173,6 +173,7 @@ export function createDbCommands({
173
173
  statSyncImpl = statSync,
174
174
  successImpl = success,
175
175
  tarCreateImpl,
176
+ verifyMigrationBundleArchiveRoundTripImpl,
176
177
  unlinkSyncImpl = unlinkSync,
177
178
  cpSyncImpl = cpSync,
178
179
  dirnameImpl = dirname,
@@ -210,6 +211,7 @@ export function createDbCommands({
210
211
  runInServer,
211
212
  successImpl,
212
213
  tarCreateImpl,
214
+ verifyMigrationBundleArchiveRoundTripImpl,
213
215
  tmpdirImpl,
214
216
  unlinkSyncImpl,
215
217
  warnImpl,
@@ -17,6 +17,7 @@ import {
17
17
  import {
18
18
  buildAuthSchemaProvenanceDiagnostic,
19
19
  buildClientBundleFailureDiagnostic,
20
+ buildLocalBundleIntegrityFailureDiagnostic,
20
21
  buildClientPreflightFailureDiagnostic,
21
22
  buildClientUserFixableDiagnostic,
22
23
  } from "./migration-client-diagnostic.mjs";
@@ -350,11 +351,16 @@ function createDbMigrationCommand(deps, command) {
350
351
  resolvePathImpl: deps.resolvePathImpl,
351
352
  rmSyncImpl: deps.rmSyncImpl,
352
353
  tarCreateImpl: deps.tarCreateImpl,
354
+ verifyArchiveImpl: deps.verifyMigrationBundleArchiveRoundTripImpl,
353
355
  tmpdirImpl: deps.tmpdirImpl,
354
356
  writeFileSyncImpl: deps.writeFileSyncImpl,
355
357
  });
356
358
  } catch (error) {
357
- emitFailure(buildClientBundleFailureDiagnostic(clientCorrelationId(), retryCommand));
359
+ emitFailure(
360
+ error?.code === "MIGRATION_BUNDLE_LOCAL_INTEGRITY_FAILED"
361
+ ? buildLocalBundleIntegrityFailureDiagnostic(clientCorrelationId())
362
+ : buildClientBundleFailureDiagnostic(clientCorrelationId(), retryCommand),
363
+ );
358
364
  return;
359
365
  }
360
366
 
@@ -22,6 +22,7 @@ import {
22
22
  } from "./project-migration-manifest.mjs";
23
23
  import { hasWorkspaceSourceRuntime } from "./runtime-mode.mjs";
24
24
  import { createTarGzipArchive } from "./tar-archive.mjs";
25
+ import { verifyMigrationBundleArchiveRoundTrip } from "./migration-bundle-roundtrip.mjs";
25
26
  import { assertToolingRuntimeCapabilities, loadToolingRuntime } from "./tooling-runtime.mjs";
26
27
  import {
27
28
  extractActionableMigrationDiagnostic,
@@ -179,12 +180,14 @@ export function createDeployPackageRuntime({
179
180
  const cwd = cwdImpl();
180
181
  const { genEnv } = await loadMigrationGenerationContext(cwd);
181
182
 
182
- if (!hasProjectDatabaseSchemaSource({
183
- cwd,
184
- config: migrationProjectConfig,
185
- existsSyncImpl,
186
- resolvePathImpl,
187
- })) {
183
+ if (
184
+ !hasProjectDatabaseSchemaSource({
185
+ cwd,
186
+ config: migrationProjectConfig,
187
+ existsSyncImpl,
188
+ resolvePathImpl,
189
+ })
190
+ ) {
188
191
  logImpl(`${DIM} ok No schema source — migration generation not required${RESET}`);
189
192
  return SUPPORTED_DRIZZLE_KIT_GENERATOR_VERSION;
190
193
  }
@@ -412,12 +415,13 @@ export function createDeployPackageRuntime({
412
415
  /* ignore and keep safe defaults */
413
416
  }
414
417
  }
415
- const filesToPack = ["gencow/", "package.json"];
418
+ const filesToPack = ["package.json"];
416
419
  let migrationManifestStage;
417
420
  try {
418
421
  migrationManifestStage = stageProjectMigrationManifest({
419
422
  cwd,
420
423
  config: migrationProjectConfig,
424
+ backendExcludes: envExcludes,
421
425
  cliVersion,
422
426
  generatorVersion,
423
427
  existsSyncImpl,
@@ -427,6 +431,7 @@ export function createDeployPackageRuntime({
427
431
  rmSyncImpl,
428
432
  writeFileSyncImpl,
429
433
  });
434
+ filesToPack.push(...migrationManifestStage.backendArchiveEntries);
430
435
  if (migrationManifestStage.relativePath) filesToPack.push(migrationManifestStage.relativePath);
431
436
  } catch (caught) {
432
437
  errorImpl(`Migration bundle manifest generation failed: ${caught.message}`);
@@ -515,6 +520,13 @@ export function createDeployPackageRuntime({
515
520
 
516
521
  const bundleBuffer = readFileSyncImpl(tmpBundle);
517
522
  const bundleSize = statSyncImpl(tmpBundle).size;
523
+ try {
524
+ await verifyMigrationBundleArchiveRoundTrip({ bundleBuffer });
525
+ } catch (caught) {
526
+ errorImpl(`Migration bundle round-trip verification failed: ${caught.message}`);
527
+ exitImpl(1);
528
+ return null;
529
+ }
518
530
  let deployClaim;
519
531
  try {
520
532
  deployClaim = await buildDeployClaimFromArchiveImpl({
@@ -13,6 +13,7 @@ import {
13
13
  formatDependencyVersionAuditError,
14
14
  } from "./deploy-auditor.mjs";
15
15
  import { shouldIncludeTarEntry } from "./tar-archive.mjs";
16
+ import { verifyMigrationBundleArchiveRoundTrip } from "./migration-bundle-roundtrip.mjs";
16
17
 
17
18
  const OPTIONAL_BUNDLE_FILES = [
18
19
  "gencow.config.js",
@@ -119,8 +120,7 @@ export async function createDevCloudDeployBundle({
119
120
  writeFileSyncImpl = writeFileSync,
120
121
  } = {}) {
121
122
  const bundleRootDir = rootDir ?? functionsDir;
122
- if (!cwd || !bundleRootDir)
123
- throw new Error("cwd and rootDir are required to create a dev cloud bundle");
123
+ if (!cwd || !bundleRootDir) throw new Error("cwd and rootDir are required to create a dev cloud bundle");
124
124
 
125
125
  const functionsSource = resolvePathImpl(cwd, bundleRootDir);
126
126
  if (!existsSyncImpl(functionsSource)) throw new Error(`Backend root dir not found: ${functionsSource}`);
@@ -143,7 +143,7 @@ export async function createDevCloudDeployBundle({
143
143
  mkdirSyncImpl(stagingDir, { recursive: true });
144
144
 
145
145
  try {
146
- const filesToPack = [bundleRootDir, "package.json", DEPLOY_DEPENDENCY_AUDIT_MANIFEST];
146
+ const filesToPack = ["package.json", DEPLOY_DEPENDENCY_AUDIT_MANIFEST];
147
147
  const envExcludes = buildBackendEnvExcludePatterns({
148
148
  rootDir: `./${bundleRootDir.replace(/^\.\/+/, "").replace(/[\\/]+$/, "")}`,
149
149
  ...(envFile ? { envFile } : {}),
@@ -156,7 +156,10 @@ export async function createDevCloudDeployBundle({
156
156
  mkdirSyncImpl(dirname(manifestPath), { recursive: true });
157
157
  writeFileSyncImpl(manifestPath, JSON.stringify(dependencyVersionResult.manifest, null, 2));
158
158
 
159
- const cronManifest = await writeCronManifestForProjectImpl({ cwd: stagingDir, functionsDir: bundleRootDir });
159
+ const cronManifest = await writeCronManifestForProjectImpl({
160
+ cwd: stagingDir,
161
+ functionsDir: bundleRootDir,
162
+ });
160
163
  if (cronManifest) filesToPack.push(CRON_MANIFEST_RELATIVE_PATH);
161
164
 
162
165
  const filteredPackage = buildFilteredPackageJson(projectPkg, auditResult?.runtimeDeps);
@@ -174,6 +177,7 @@ export async function createDevCloudDeployBundle({
174
177
  const migrationManifestStage = stageProjectMigrationManifest({
175
178
  cwd: stagingDir,
176
179
  backendDir: bundleRootDir,
180
+ backendExcludes: envExcludes,
177
181
  config,
178
182
  cliVersion,
179
183
  generatorVersion,
@@ -184,6 +188,7 @@ export async function createDevCloudDeployBundle({
184
188
  rmSyncImpl,
185
189
  writeFileSyncImpl,
186
190
  });
191
+ filesToPack.push(...migrationManifestStage.backendArchiveEntries);
187
192
  if (migrationManifestStage.relativePath) filesToPack.push(migrationManifestStage.relativePath);
188
193
 
189
194
  try {
@@ -193,6 +198,7 @@ export async function createDevCloudDeployBundle({
193
198
  tarCreateImpl,
194
199
  excludes: envExcludes,
195
200
  });
201
+ await verifyMigrationBundleArchiveRoundTrip({ bundleBuffer: bundle, backendDir: bundleRootDir });
196
202
  return {
197
203
  bundle,
198
204
  auditMessage,
@@ -179,5 +179,6 @@ export function unsignedMigrationBundleManifest({
179
179
 
180
180
  export function buildMigrationBundleManifest(params) {
181
181
  const unsigned = unsignedMigrationBundleManifest(params);
182
- return { ...unsigned, bundleSha256: sha256(JSON.stringify(bundleFileRows(params.backendRoot))) };
182
+ const rows = params.bundleFilePlan?.bundleFileRows ?? bundleFileRows(params.backendRoot);
183
+ return { ...unsigned, bundleSha256: sha256(JSON.stringify(rows)) };
183
184
  }
@@ -0,0 +1,55 @@
1
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join, resolve } from "node:path";
4
+
5
+ import {
6
+ MIGRATION_BUNDLE_MANIFEST_PATH,
7
+ buildMigrationBundleManifest,
8
+ } from "./migration-bundle-manifest.mjs";
9
+
10
+ export const MIGRATION_BUNDLE_LOCAL_INTEGRITY_FAILED = "MIGRATION_BUNDLE_LOCAL_INTEGRITY_FAILED";
11
+
12
+ function localIntegrityError(cause) {
13
+ const error = new Error(MIGRATION_BUNDLE_LOCAL_INTEGRITY_FAILED, cause ? { cause } : undefined);
14
+ error.code = MIGRATION_BUNDLE_LOCAL_INTEGRITY_FAILED;
15
+ return error;
16
+ }
17
+
18
+ export async function verifyMigrationBundleArchiveRoundTrip({
19
+ archivePath,
20
+ bundleBuffer,
21
+ backendDir = "gencow",
22
+ }) {
23
+ const verificationRoot = mkdtempSync(join(tmpdir(), "gencow-migration-roundtrip-"));
24
+ try {
25
+ mkdirSync(verificationRoot, { recursive: true });
26
+ const verificationArchive = archivePath ?? resolve(verificationRoot, "bundle.tar.gz");
27
+ if (!archivePath) writeFileSync(verificationArchive, bundleBuffer, { mode: 0o600 });
28
+ const { extract } = await import("tar");
29
+ await extract({ cwd: verificationRoot, file: verificationArchive });
30
+ const manifestPath = resolve(verificationRoot, MIGRATION_BUNDLE_MANIFEST_PATH);
31
+ const backendRoot = resolve(verificationRoot, backendDir);
32
+ if (!existsSync(manifestPath) || !existsSync(backendRoot)) {
33
+ throw localIntegrityError();
34
+ }
35
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
36
+ const recomputed = buildMigrationBundleManifest({
37
+ backendRoot,
38
+ cliVersion: manifest.cliVersion,
39
+ generatorVersion: manifest.generatorVersion,
40
+ sourceMode: manifest.sourceMode,
41
+ schemaSourcePaths: manifest.schemaSources?.map((source) => resolve(backendRoot, source.path)),
42
+ });
43
+ if (JSON.stringify(recomputed) !== JSON.stringify(manifest)) {
44
+ throw localIntegrityError();
45
+ }
46
+ return manifest;
47
+ } catch (error) {
48
+ if (error?.code === MIGRATION_BUNDLE_LOCAL_INTEGRITY_FAILED) {
49
+ throw error;
50
+ }
51
+ throw localIntegrityError(error);
52
+ } finally {
53
+ rmSync(verificationRoot, { recursive: true, force: true });
54
+ }
55
+ }
@@ -6,7 +6,9 @@ import {
6
6
  buildMigrationBundleManifest,
7
7
  } from "./migration-bundle-manifest.mjs";
8
8
  import { buildBackendEnvExcludePatterns } from "./backend-env-resolver.mjs";
9
+ import { buildBackendBundleFilePlan } from "./backend-bundle-file-plan.mjs";
9
10
  import { createTarGzipArchive } from "./tar-archive.mjs";
11
+ import { verifyMigrationBundleArchiveRoundTrip } from "./migration-bundle-roundtrip.mjs";
10
12
 
11
13
  export async function stageMigrationBundleArchive({
12
14
  backendRoot,
@@ -21,6 +23,7 @@ export async function stageMigrationBundleArchive({
21
23
  resolvePathImpl = resolve,
22
24
  rmSyncImpl = rmSync,
23
25
  tarCreateImpl,
26
+ verifyArchiveImpl = verifyMigrationBundleArchiveRoundTrip,
24
27
  tmpdirImpl = tmpdir,
25
28
  writeFileSyncImpl = writeFileSync,
26
29
  }) {
@@ -34,6 +37,12 @@ export async function stageMigrationBundleArchive({
34
37
  cpSyncImpl(backendRoot, stagedBackendRoot, { recursive: true, errorOnExist: true });
35
38
  mkdirSyncImpl(resolvePathImpl(stagedBackendRoot, "migrations"), { recursive: true });
36
39
  mkdirSyncImpl(metadataRoot, { recursive: true });
40
+ const excludes = buildBackendEnvExcludePatterns(config);
41
+ const bundleFilePlan = buildBackendBundleFilePlan({
42
+ backendRoot: stagedBackendRoot,
43
+ archiveRoot: "gencow",
44
+ excludes,
45
+ });
37
46
  const manifest = buildMigrationBundleManifest({
38
47
  backendRoot: stagedBackendRoot,
39
48
  cliVersion,
@@ -42,15 +51,17 @@ export async function stageMigrationBundleArchive({
42
51
  schemaSourcePaths: schemaSourcePaths?.map((sourcePath) =>
43
52
  resolvePathImpl(stagedBackendRoot, relative(backendRoot, sourcePath)),
44
53
  ),
54
+ bundleFilePlan,
45
55
  });
46
56
  writeFileSyncImpl(manifestPath, `${JSON.stringify(manifest)}\n`, { mode: 0o600 });
47
57
  await createTarGzipArchive({
48
58
  cwd: workspaceRoot,
49
59
  file: archivePath,
50
- entries: ["gencow/", MIGRATION_BUNDLE_MANIFEST_PATH],
51
- excludes: buildBackendEnvExcludePatterns(config),
60
+ entries: [...bundleFilePlan.archiveEntries, MIGRATION_BUNDLE_MANIFEST_PATH],
61
+ excludes,
52
62
  tarCreateImpl,
53
63
  });
64
+ await verifyArchiveImpl({ archivePath });
54
65
  return { archivePath, cleanup, manifest, workspaceRoot };
55
66
  } catch (error) {
56
67
  cleanup();
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  MIGRATION_CAPABILITY_MANIFEST_V1,
3
+ platformActionDiagnostic,
3
4
  retryableInfraDiagnostic,
4
5
  userFixableDiagnostic,
5
6
  } from "@gencow/migration-contract";
@@ -139,6 +140,27 @@ export function buildClientBundleFailureDiagnostic(correlationId, retryCommand)
139
140
  };
140
141
  }
141
142
 
143
+ export function buildLocalBundleIntegrityFailureDiagnostic(correlationId) {
144
+ return platformActionDiagnostic({
145
+ code: "MIGRATION_BUNDLE_LOCAL_INTEGRITY_FAILED",
146
+ phase: "PREFLIGHT",
147
+ databaseState: "UNCHANGED",
148
+ correlationId,
149
+ retryCondition: "Install a patched CLI, then create a fresh migration bundle.",
150
+ reason: {
151
+ code: "migration_bundle_local_integrity_failed",
152
+ summary: "The local archive did not match its migration manifest.",
153
+ detail: "No remote request was sent and the cloud database was not changed.",
154
+ },
155
+ suggestedFixes: [],
156
+ nextAction: "UPDATE_CLI",
157
+ docsUrl: MIGRATION_CAPABILITY_MANIFEST_V1.docsUrl,
158
+ supportedCliRange: MIGRATION_CAPABILITY_MANIFEST_V1.supportedCliRange,
159
+ forbiddenActions: [...FORBIDDEN_ACTIONS],
160
+ redaction: { serverReturnedRawSql: false, localExcerpt: "NONE" },
161
+ });
162
+ }
163
+
142
164
  export function buildClientPreflightFailureDiagnostic(correlationId) {
143
165
  return {
144
166
  ...buildClientBundleFailureDiagnostic(correlationId),
@@ -5,6 +5,8 @@ import {
5
5
  MIGRATION_BUNDLE_MANIFEST_PATH,
6
6
  buildMigrationBundleManifest,
7
7
  } from "./migration-bundle-manifest.mjs";
8
+ import { buildBackendEnvExcludePatterns } from "./backend-env-resolver.mjs";
9
+ import { buildBackendBundleFilePlan } from "./backend-bundle-file-plan.mjs";
8
10
  import { resolveConfiguredSchemaPaths, resolveDrizzleSchemaPaths } from "./cli-schema-paths.mjs";
9
11
 
10
12
  export const PROJECT_AUXILIARY_SCHEMA_PATHS = [
@@ -18,11 +20,7 @@ export const PROJECT_AUXILIARY_SCHEMA_PATHS = [
18
20
 
19
21
  function declaredConfiguredSchemaPaths({ cwd, backendDir, config, resolvePathImpl }) {
20
22
  if (!config?.schema) return [];
21
- return resolveConfiguredSchemaPaths(
22
- cwd,
23
- resolvePathImpl(cwd, config.rootDir ?? backendDir),
24
- config.schema,
25
- );
23
+ return resolveConfiguredSchemaPaths(cwd, resolvePathImpl(cwd, config.rootDir ?? backendDir), config.schema);
26
24
  }
27
25
 
28
26
  function projectSchemaSourcePaths({
@@ -38,10 +36,11 @@ function projectSchemaSourcePaths({
38
36
  const missing = declared.filter((pathValue) => !existsSyncImpl(pathValue));
39
37
  if (missing.length > 0) throw new Error("Configured schema source is missing from the project");
40
38
  }
41
- return resolveDrizzleSchemaPaths(
42
- config ?? { rootDir: backendDir },
43
- { cwd, existsSyncImpl, resolvePathImpl },
44
- ).filter((pathValue) => existsSyncImpl(pathValue));
39
+ return resolveDrizzleSchemaPaths(config ?? { rootDir: backendDir }, {
40
+ cwd,
41
+ existsSyncImpl,
42
+ resolvePathImpl,
43
+ }).filter((pathValue) => existsSyncImpl(pathValue));
45
44
  }
46
45
 
47
46
  export function hasProjectDatabaseSchemaSource({
@@ -52,14 +51,16 @@ export function hasProjectDatabaseSchemaSource({
52
51
  resolvePathImpl = resolve,
53
52
  }) {
54
53
  if (declaredConfiguredSchemaPaths({ cwd, backendDir, config, resolvePathImpl }).length > 0) return true;
55
- return projectSchemaSourcePaths({
56
- cwd,
57
- backendDir,
58
- config,
59
- existsSyncImpl,
60
- resolvePathImpl,
61
- requireDeclaredSources: false,
62
- }).length > 0;
54
+ return (
55
+ projectSchemaSourcePaths({
56
+ cwd,
57
+ backendDir,
58
+ config,
59
+ existsSyncImpl,
60
+ resolvePathImpl,
61
+ requireDeclaredSources: false,
62
+ }).length > 0
63
+ );
63
64
  }
64
65
 
65
66
  export function stageProjectMigrationManifest({
@@ -67,6 +68,7 @@ export function stageProjectMigrationManifest({
67
68
  cliVersion,
68
69
  generatorVersion,
69
70
  backendDir = "gencow",
71
+ backendExcludes,
70
72
  config,
71
73
  existsSyncImpl = existsSync,
72
74
  mkdirSyncImpl = mkdirSync,
@@ -84,6 +86,11 @@ export function stageProjectMigrationManifest({
84
86
  resolvePathImpl,
85
87
  requireDeclaredSources: true,
86
88
  });
89
+ const bundleFilePlan = buildBackendBundleFilePlan({
90
+ backendRoot,
91
+ archiveRoot: backendDir,
92
+ excludes: backendExcludes ?? buildBackendEnvExcludePatterns(config),
93
+ });
87
94
 
88
95
  const manifestPath = resolvePathImpl(cwd, MIGRATION_BUNDLE_MANIFEST_PATH);
89
96
  const previous = existsSyncImpl(manifestPath) ? readFileSyncImpl(manifestPath) : null;
@@ -93,6 +100,7 @@ export function stageProjectMigrationManifest({
93
100
  generatorVersion,
94
101
  sourceMode: "GENERATED",
95
102
  schemaSourcePaths,
103
+ bundleFilePlan,
96
104
  });
97
105
  mkdirSyncImpl(dirname(manifestPath), { recursive: true });
98
106
  writeFileSyncImpl(manifestPath, `${JSON.stringify(manifest)}\n`, { mode: 0o600 });
@@ -101,6 +109,7 @@ export function stageProjectMigrationManifest({
101
109
  return {
102
110
  relativePath: MIGRATION_BUNDLE_MANIFEST_PATH,
103
111
  manifest,
112
+ backendArchiveEntries: bundleFilePlan.archiveEntries,
104
113
  cleanup() {
105
114
  if (cleaned) return;
106
115
  cleaned = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gencow",
3
- "version": "0.1.180",
3
+ "version": "0.1.181",
4
4
  "description": "Gencow — AI Backend Engine",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,7 +19,6 @@
19
19
  "dashboard/"
20
20
  ],
21
21
  "dependencies": {
22
- "@gencow/migration-contract": "0.1.1",
23
22
  "@modelcontextprotocol/sdk": "^1.29.0",
24
23
  "drizzle-kit": "1.0.0-rc.4",
25
24
  "drizzle-orm": "1.0.0-rc.4",
@@ -27,14 +26,15 @@
27
26
  "open": "^10.2.0",
28
27
  "tar": "^7.5.21",
29
28
  "ws": "^8.21.1",
30
- "zod": "^4.4.3"
29
+ "zod": "^4.4.3",
30
+ "@gencow/migration-contract": "0.1.2"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^25.9.5",
34
34
  "better-auth": "^1.6.23",
35
35
  "@gencow/client": "0.2.6",
36
- "@gencow/core": "0.1.42",
37
- "@gencow/react": "0.2.6"
36
+ "@gencow/react": "0.2.6",
37
+ "@gencow/core": "0.1.42"
38
38
  },
39
39
  "scripts": {
40
40
  "prebuild": "pnpm --filter @gencow/migration-contract run build && pnpm --filter @gencow/server run build",