@prisma-next/migration-tools 0.3.0 → 0.4.0-dev.10

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.
Files changed (42) hide show
  1. package/README.md +4 -0
  2. package/dist/attestation-DnebS4XZ.mjs +64 -0
  3. package/dist/attestation-DnebS4XZ.mjs.map +1 -0
  4. package/dist/{constants-9f-bCq8Y.mjs → constants-BRi0X7B_.mjs} +1 -1
  5. package/dist/{constants-9f-bCq8Y.mjs.map → constants-BRi0X7B_.mjs.map} +1 -1
  6. package/dist/{errors-CSAAto11.mjs → errors-C_XuSbX7.mjs} +1 -1
  7. package/dist/{errors-CSAAto11.mjs.map → errors-C_XuSbX7.mjs.map} +1 -1
  8. package/dist/exports/attestation.d.mts +9 -1
  9. package/dist/exports/attestation.d.mts.map +1 -1
  10. package/dist/exports/attestation.mjs +3 -61
  11. package/dist/exports/constants.mjs +1 -1
  12. package/dist/exports/dag.d.mts +1 -1
  13. package/dist/exports/dag.mjs +2 -2
  14. package/dist/exports/io.d.mts +14 -2
  15. package/dist/exports/io.d.mts.map +1 -1
  16. package/dist/exports/io.mjs +2 -2
  17. package/dist/exports/migration-ts.d.mts +27 -18
  18. package/dist/exports/migration-ts.d.mts.map +1 -1
  19. package/dist/exports/migration-ts.mjs +39 -85
  20. package/dist/exports/migration-ts.mjs.map +1 -1
  21. package/dist/exports/migration.d.mts +57 -0
  22. package/dist/exports/migration.d.mts.map +1 -0
  23. package/dist/exports/migration.mjs +159 -0
  24. package/dist/exports/migration.mjs.map +1 -0
  25. package/dist/exports/refs.mjs +1 -1
  26. package/dist/exports/types.d.mts +1 -1
  27. package/dist/exports/types.mjs +1 -1
  28. package/dist/{io-LsuurzNb.mjs → io-Cun81AIZ.mjs} +25 -4
  29. package/dist/io-Cun81AIZ.mjs.map +1 -0
  30. package/dist/{types-BW_pJEe8.d.mts → types-D2uX4ql7.d.mts} +1 -1
  31. package/dist/{types-BW_pJEe8.d.mts.map → types-D2uX4ql7.d.mts.map} +1 -1
  32. package/package.json +10 -6
  33. package/src/attestation.ts +10 -11
  34. package/src/exports/io.ts +1 -0
  35. package/src/exports/migration-ts.ts +3 -6
  36. package/src/exports/migration.ts +1 -0
  37. package/src/io.ts +26 -1
  38. package/src/migration-base.ts +229 -0
  39. package/src/migration-ts.ts +27 -144
  40. package/src/runtime-detection.ts +18 -0
  41. package/dist/exports/attestation.mjs.map +0 -1
  42. package/dist/io-LsuurzNb.mjs.map +0 -1
package/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @prisma-next/migration-tools
2
2
 
3
+ > **Internal package.** This package is an implementation detail of [`prisma-next`](https://www.npmjs.com/package/prisma-next)
4
+ > and is published only to support its runtime. Its API is unstable and may change
5
+ > without notice. Do not depend on this package directly; install `prisma-next` instead.
6
+
3
7
  On-disk migration persistence, attestation, and history reconstruction for Prisma Next.
4
8
 
5
9
  ## Responsibilities
@@ -0,0 +1,64 @@
1
+ import { a as writeMigrationManifest, r as readMigrationPackage } from "./io-Cun81AIZ.mjs";
2
+ import { createHash } from "node:crypto";
3
+
4
+ //#region src/canonicalize-json.ts
5
+ function sortKeys(value) {
6
+ if (value === null || typeof value !== "object") return value;
7
+ if (Array.isArray(value)) return value.map(sortKeys);
8
+ const sorted = {};
9
+ for (const key of Object.keys(value).sort()) sorted[key] = sortKeys(value[key]);
10
+ return sorted;
11
+ }
12
+ function canonicalizeJson(value) {
13
+ return JSON.stringify(sortKeys(value));
14
+ }
15
+
16
+ //#endregion
17
+ //#region src/attestation.ts
18
+ function sha256Hex(input) {
19
+ return createHash("sha256").update(input).digest("hex");
20
+ }
21
+ /**
22
+ * Content-addressed migration identity over (manifest envelope sans
23
+ * contracts/hints, ops). See ADR 199 "Storage-only migration identity"
24
+ * for the rationale: contracts are anchored separately by the
25
+ * storage-hash bookends inside the envelope; planner hints are advisory
26
+ * and must not affect identity.
27
+ */
28
+ function computeMigrationId(manifest, ops) {
29
+ const { migrationId: _migrationId, signature: _signature, fromContract: _fromContract, toContract: _toContract, hints: _hints, ...strippedMeta } = manifest;
30
+ return `sha256:${sha256Hex(canonicalizeJson([canonicalizeJson(strippedMeta), canonicalizeJson(ops)].map(sha256Hex)))}`;
31
+ }
32
+ /** Compute and persist `migrationId` to `manifest.json`. */
33
+ async function attestMigration(dir) {
34
+ const pkg = await readMigrationPackage(dir);
35
+ const migrationId = computeMigrationId(pkg.manifest, pkg.ops);
36
+ await writeMigrationManifest(dir, {
37
+ ...pkg.manifest,
38
+ migrationId
39
+ });
40
+ return migrationId;
41
+ }
42
+ async function verifyMigration(dir) {
43
+ const pkg = await readMigrationPackage(dir);
44
+ if (pkg.manifest.migrationId === null) return {
45
+ ok: false,
46
+ reason: "draft"
47
+ };
48
+ const computed = computeMigrationId(pkg.manifest, pkg.ops);
49
+ if (pkg.manifest.migrationId === computed) return {
50
+ ok: true,
51
+ storedMigrationId: pkg.manifest.migrationId,
52
+ computedMigrationId: computed
53
+ };
54
+ return {
55
+ ok: false,
56
+ reason: "mismatch",
57
+ storedMigrationId: pkg.manifest.migrationId,
58
+ computedMigrationId: computed
59
+ };
60
+ }
61
+
62
+ //#endregion
63
+ export { computeMigrationId as n, verifyMigration as r, attestMigration as t };
64
+ //# sourceMappingURL=attestation-DnebS4XZ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attestation-DnebS4XZ.mjs","names":["sorted: Record<string, unknown>"],"sources":["../src/canonicalize-json.ts","../src/attestation.ts"],"sourcesContent":["function sortKeys(value: unknown): unknown {\n if (value === null || typeof value !== 'object') {\n return value;\n }\n if (Array.isArray(value)) {\n return value.map(sortKeys);\n }\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(value).sort()) {\n sorted[key] = sortKeys((value as Record<string, unknown>)[key]);\n }\n return sorted;\n}\n\nexport function canonicalizeJson(value: unknown): string {\n return JSON.stringify(sortKeys(value));\n}\n","import { createHash } from 'node:crypto';\nimport { canonicalizeJson } from './canonicalize-json';\nimport { readMigrationPackage, writeMigrationManifest } from './io';\nimport type { MigrationManifest, MigrationOps } from './types';\n\nexport interface VerifyResult {\n readonly ok: boolean;\n readonly reason?: 'draft' | 'mismatch';\n readonly storedMigrationId?: string;\n readonly computedMigrationId?: string;\n}\n\nfunction sha256Hex(input: string): string {\n return createHash('sha256').update(input).digest('hex');\n}\n\n/**\n * Content-addressed migration identity over (manifest envelope sans\n * contracts/hints, ops). See ADR 199 \"Storage-only migration identity\"\n * for the rationale: contracts are anchored separately by the\n * storage-hash bookends inside the envelope; planner hints are advisory\n * and must not affect identity.\n */\nexport function computeMigrationId(manifest: MigrationManifest, ops: MigrationOps): string {\n const {\n migrationId: _migrationId,\n signature: _signature,\n fromContract: _fromContract,\n toContract: _toContract,\n hints: _hints,\n ...strippedMeta\n } = manifest;\n\n const canonicalManifest = canonicalizeJson(strippedMeta);\n const canonicalOps = canonicalizeJson(ops);\n\n const partHashes = [canonicalManifest, canonicalOps].map(sha256Hex);\n const hash = sha256Hex(canonicalizeJson(partHashes));\n\n return `sha256:${hash}`;\n}\n\n/** Compute and persist `migrationId` to `manifest.json`. */\nexport async function attestMigration(dir: string): Promise<string> {\n const pkg = await readMigrationPackage(dir);\n const migrationId = computeMigrationId(pkg.manifest, pkg.ops);\n\n const updated = { ...pkg.manifest, migrationId };\n await writeMigrationManifest(dir, updated);\n\n return migrationId;\n}\n\nexport async function verifyMigration(dir: string): Promise<VerifyResult> {\n const pkg = await readMigrationPackage(dir);\n\n if (pkg.manifest.migrationId === null) {\n return { ok: false, reason: 'draft' };\n }\n\n const computed = computeMigrationId(pkg.manifest, pkg.ops);\n\n if (pkg.manifest.migrationId === computed) {\n return { ok: true, storedMigrationId: pkg.manifest.migrationId, computedMigrationId: computed };\n }\n\n return {\n ok: false,\n reason: 'mismatch',\n storedMigrationId: pkg.manifest.migrationId,\n computedMigrationId: computed,\n };\n}\n"],"mappings":";;;;AAAA,SAAS,SAAS,OAAyB;AACzC,KAAI,UAAU,QAAQ,OAAO,UAAU,SACrC,QAAO;AAET,KAAI,MAAM,QAAQ,MAAM,CACtB,QAAO,MAAM,IAAI,SAAS;CAE5B,MAAMA,SAAkC,EAAE;AAC1C,MAAK,MAAM,OAAO,OAAO,KAAK,MAAM,CAAC,MAAM,CACzC,QAAO,OAAO,SAAU,MAAkC,KAAK;AAEjE,QAAO;;AAGT,SAAgB,iBAAiB,OAAwB;AACvD,QAAO,KAAK,UAAU,SAAS,MAAM,CAAC;;;;;ACHxC,SAAS,UAAU,OAAuB;AACxC,QAAO,WAAW,SAAS,CAAC,OAAO,MAAM,CAAC,OAAO,MAAM;;;;;;;;;AAUzD,SAAgB,mBAAmB,UAA6B,KAA2B;CACzF,MAAM,EACJ,aAAa,cACb,WAAW,YACX,cAAc,eACd,YAAY,aACZ,OAAO,QACP,GAAG,iBACD;AAQJ,QAAO,UAFM,UAAU,iBADJ,CAHO,iBAAiB,aAAa,EACnC,iBAAiB,IAAI,CAEU,CAAC,IAAI,UAAU,CAChB,CAAC;;;AAMtD,eAAsB,gBAAgB,KAA8B;CAClE,MAAM,MAAM,MAAM,qBAAqB,IAAI;CAC3C,MAAM,cAAc,mBAAmB,IAAI,UAAU,IAAI,IAAI;AAG7D,OAAM,uBAAuB,KADb;EAAE,GAAG,IAAI;EAAU;EAAa,CACN;AAE1C,QAAO;;AAGT,eAAsB,gBAAgB,KAAoC;CACxE,MAAM,MAAM,MAAM,qBAAqB,IAAI;AAE3C,KAAI,IAAI,SAAS,gBAAgB,KAC/B,QAAO;EAAE,IAAI;EAAO,QAAQ;EAAS;CAGvC,MAAM,WAAW,mBAAmB,IAAI,UAAU,IAAI,IAAI;AAE1D,KAAI,IAAI,SAAS,gBAAgB,SAC/B,QAAO;EAAE,IAAI;EAAM,mBAAmB,IAAI,SAAS;EAAa,qBAAqB;EAAU;AAGjG,QAAO;EACL,IAAI;EACJ,QAAQ;EACR,mBAAmB,IAAI,SAAS;EAChC,qBAAqB;EACtB"}
@@ -7,4 +7,4 @@ const EMPTY_CONTRACT_HASH = "sha256:empty";
7
7
 
8
8
  //#endregion
9
9
  export { EMPTY_CONTRACT_HASH as t };
10
- //# sourceMappingURL=constants-9f-bCq8Y.mjs.map
10
+ //# sourceMappingURL=constants-BRi0X7B_.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants-9f-bCq8Y.mjs","names":[],"sources":["../src/constants.ts"],"sourcesContent":["/**\n * Sentinel value representing the absence of a contract (empty/new project).\n * This is a human-readable marker, not a real SHA-256 hash.\n */\nexport const EMPTY_CONTRACT_HASH = 'sha256:empty' as const;\n"],"mappings":";;;;;AAIA,MAAa,sBAAsB"}
1
+ {"version":3,"file":"constants-BRi0X7B_.mjs","names":[],"sources":["../src/constants.ts"],"sourcesContent":["/**\n * Sentinel value representing the absence of a contract (empty/new project).\n * This is a human-readable marker, not a real SHA-256 hash.\n */\nexport const EMPTY_CONTRACT_HASH = 'sha256:empty' as const;\n"],"mappings":";;;;;AAIA,MAAa,sBAAsB"}
@@ -150,4 +150,4 @@ function errorDuplicateMigrationId(migrationId) {
150
150
 
151
151
  //#endregion
152
152
  export { errorInvalidJson as a, errorInvalidRefValue as c, errorMissingFile as d, errorNoInitialMigration as f, errorDuplicateMigrationId as i, errorInvalidRefs as l, errorSameSourceAndTarget as m, errorAmbiguousTarget as n, errorInvalidManifest as o, errorNoTarget as p, errorDirectoryExists as r, errorInvalidRefName as s, MigrationToolsError as t, errorInvalidSlug as u };
153
- //# sourceMappingURL=errors-CSAAto11.mjs.map
153
+ //# sourceMappingURL=errors-C_XuSbX7.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors-CSAAto11.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Structured error for migration tooling operations.\n *\n * Follows the NAMESPACE.SUBCODE convention from ADR 027. All codes live under\n * the MIGRATION namespace. These are tooling-time errors (file I/O, attestation,\n * migration history reconstruction), distinct from the runtime MIGRATION.* codes for apply-time\n * failures (PRECHECK_FAILED, POSTCHECK_FAILED, etc.).\n *\n * Fields:\n * - code: Stable machine-readable code (MIGRATION.SUBCODE)\n * - category: Always 'MIGRATION'\n * - why: Explains the cause in plain language\n * - fix: Actionable remediation step\n * - details: Machine-readable structured data for agents\n */\nexport class MigrationToolsError extends Error {\n readonly code: string;\n readonly category = 'MIGRATION' as const;\n readonly why: string;\n readonly fix: string;\n readonly details: Record<string, unknown> | undefined;\n\n constructor(\n code: string,\n summary: string,\n options: {\n readonly why: string;\n readonly fix: string;\n readonly details?: Record<string, unknown>;\n },\n ) {\n super(summary);\n this.name = 'MigrationToolsError';\n this.code = code;\n this.why = options.why;\n this.fix = options.fix;\n this.details = options.details;\n }\n\n static is(error: unknown): error is MigrationToolsError {\n if (!(error instanceof Error)) return false;\n const candidate = error as MigrationToolsError;\n return candidate.name === 'MigrationToolsError' && typeof candidate.code === 'string';\n }\n}\n\nexport function errorDirectoryExists(dir: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.DIR_EXISTS', 'Migration directory already exists', {\n why: `The directory \"${dir}\" already exists. Each migration must have a unique directory.`,\n fix: 'Use --name to pick a different name, or delete the existing directory and re-run.',\n details: { dir },\n });\n}\n\nexport function errorMissingFile(file: string, dir: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.FILE_MISSING', `Missing ${file}`, {\n why: `Expected \"${file}\" in \"${dir}\" but the file does not exist.`,\n fix: 'Ensure the migration directory contains both migration.json and ops.json. If the directory is corrupt, delete it and re-run migration plan.',\n details: { file, dir },\n });\n}\n\nexport function errorInvalidJson(filePath: string, parseError: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_JSON', 'Invalid JSON in migration file', {\n why: `Failed to parse \"${filePath}\": ${parseError}`,\n fix: 'Fix the JSON syntax error, or delete the migration directory and re-run migration plan.',\n details: { filePath, parseError },\n });\n}\n\nexport function errorInvalidManifest(filePath: string, reason: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_MANIFEST', 'Invalid migration manifest', {\n why: `Manifest at \"${filePath}\" is invalid: ${reason}`,\n fix: 'Ensure the manifest has all required fields (from, to, kind, toContract). If corrupt, delete and re-plan.',\n details: { filePath, reason },\n });\n}\n\nexport function errorInvalidSlug(slug: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_NAME', 'Invalid migration name', {\n why: `The slug \"${slug}\" contains no valid characters after sanitization (only a-z, 0-9 are kept).`,\n fix: 'Provide a name with at least one alphanumeric character, e.g. --name add_users.',\n details: { slug },\n });\n}\n\nexport function errorSameSourceAndTarget(dirName: string, hash: string): MigrationToolsError {\n return new MigrationToolsError(\n 'MIGRATION.SAME_SOURCE_AND_TARGET',\n 'Migration has same source and target',\n {\n why: `Migration \"${dirName}\" has from === to === \"${hash}\". A migration must transition between two different contract states.`,\n fix: 'Delete the invalid migration directory and re-run migration plan.',\n details: { dirName, hash },\n },\n );\n}\n\nexport function errorAmbiguousTarget(\n branchTips: readonly string[],\n context?: {\n divergencePoint: string;\n branches: readonly {\n tip: string;\n edges: readonly { dirName: string; from: string; to: string }[];\n }[];\n },\n): MigrationToolsError {\n const divergenceInfo = context\n ? `\\nDivergence point: ${context.divergencePoint}\\nBranches:\\n${context.branches.map((b) => ` → ${b.tip} (${b.edges.length} edge(s): ${b.edges.map((e) => e.dirName).join(' → ') || 'direct'})`).join('\\n')}`\n : '';\n return new MigrationToolsError('MIGRATION.AMBIGUOUS_TARGET', 'Ambiguous migration target', {\n why: `The migration history has diverged into multiple branches: ${branchTips.join(', ')}. This typically happens when two developers plan migrations from the same starting point.${divergenceInfo}`,\n fix: 'Use `migration ref set <name> <hash>` to target a specific branch, delete one of the conflicting migration directories and re-run `migration plan`, or use --from <hash> to explicitly select a starting point.',\n details: {\n branchTips,\n ...(context ? { divergencePoint: context.divergencePoint, branches: context.branches } : {}),\n },\n });\n}\n\nexport function errorNoInitialMigration(nodes: readonly string[]): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.NO_INITIAL_MIGRATION', 'No initial migration found', {\n why: `No migration starts from the empty contract state (known hashes: ${nodes.join(', ')}). At least one migration must originate from the empty state.`,\n fix: 'Inspect the migrations directory for corrupted migration.json files. At least one migration must start from the empty contract hash.',\n details: { nodes },\n });\n}\n\nexport function errorInvalidRefs(refsPath: string, reason: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_REFS', 'Invalid refs.json', {\n why: `refs.json at \"${refsPath}\" is invalid: ${reason}`,\n fix: 'Ensure refs.json is a flat object mapping valid ref names to contract hash strings.',\n details: { path: refsPath, reason },\n });\n}\n\nexport function errorInvalidRefFile(filePath: string, reason: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_REF_FILE', 'Invalid ref file', {\n why: `Ref file at \"${filePath}\" is invalid: ${reason}`,\n fix: 'Ensure the ref file contains valid JSON with { \"hash\": \"sha256:<64 hex chars>\", \"invariants\": [\"...\"] }.',\n details: { path: filePath, reason },\n });\n}\n\nexport function errorInvalidRefName(refName: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_REF_NAME', 'Invalid ref name', {\n why: `Ref name \"${refName}\" is invalid. Names must be lowercase alphanumeric with hyphens or forward slashes (no \".\" or \"..\" segments).`,\n fix: `Use a valid ref name (e.g., \"staging\", \"envs/production\").`,\n details: { refName },\n });\n}\n\nexport function errorNoTarget(reachableHashes: readonly string[]): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.NO_TARGET', 'No migration target could be resolved', {\n why: `The migration history contains cycles and no target can be resolved automatically (reachable hashes: ${reachableHashes.join(', ')}). This typically happens after rollback migrations (e.g., C1→C2→C1).`,\n fix: 'Use --from <hash> to specify the planning origin explicitly.',\n details: { reachableHashes },\n });\n}\n\nexport function errorInvalidRefValue(value: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_REF_VALUE', 'Invalid ref value', {\n why: `Ref value \"${value}\" is not a valid contract hash. Values must be in the format \"sha256:<64 hex chars>\" or \"sha256:empty\".`,\n fix: 'Use a valid storage hash from `prisma-next contract emit` output or an existing migration.',\n details: { value },\n });\n}\n\nexport function errorDuplicateMigrationId(migrationId: string): MigrationToolsError {\n return new MigrationToolsError(\n 'MIGRATION.DUPLICATE_MIGRATION_ID',\n 'Duplicate migrationId in migration graph',\n {\n why: `Multiple migrations share migrationId \"${migrationId}\". Each migration must have a unique content-addressed identity.`,\n fix: 'Regenerate one of the conflicting migrations so each migrationId is unique, then re-run migration commands.',\n details: { migrationId },\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAeA,IAAa,sBAAb,cAAyC,MAAM;CAC7C,AAAS;CACT,AAAS,WAAW;CACpB,AAAS;CACT,AAAS;CACT,AAAS;CAET,YACE,MACA,SACA,SAKA;AACA,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,OAAO;AACZ,OAAK,MAAM,QAAQ;AACnB,OAAK,MAAM,QAAQ;AACnB,OAAK,UAAU,QAAQ;;CAGzB,OAAO,GAAG,OAA8C;AACtD,MAAI,EAAE,iBAAiB,OAAQ,QAAO;EACtC,MAAM,YAAY;AAClB,SAAO,UAAU,SAAS,yBAAyB,OAAO,UAAU,SAAS;;;AAIjF,SAAgB,qBAAqB,KAAkC;AACrE,QAAO,IAAI,oBAAoB,wBAAwB,sCAAsC;EAC3F,KAAK,kBAAkB,IAAI;EAC3B,KAAK;EACL,SAAS,EAAE,KAAK;EACjB,CAAC;;AAGJ,SAAgB,iBAAiB,MAAc,KAAkC;AAC/E,QAAO,IAAI,oBAAoB,0BAA0B,WAAW,QAAQ;EAC1E,KAAK,aAAa,KAAK,QAAQ,IAAI;EACnC,KAAK;EACL,SAAS;GAAE;GAAM;GAAK;EACvB,CAAC;;AAGJ,SAAgB,iBAAiB,UAAkB,YAAyC;AAC1F,QAAO,IAAI,oBAAoB,0BAA0B,kCAAkC;EACzF,KAAK,oBAAoB,SAAS,KAAK;EACvC,KAAK;EACL,SAAS;GAAE;GAAU;GAAY;EAClC,CAAC;;AAGJ,SAAgB,qBAAqB,UAAkB,QAAqC;AAC1F,QAAO,IAAI,oBAAoB,8BAA8B,8BAA8B;EACzF,KAAK,gBAAgB,SAAS,gBAAgB;EAC9C,KAAK;EACL,SAAS;GAAE;GAAU;GAAQ;EAC9B,CAAC;;AAGJ,SAAgB,iBAAiB,MAAmC;AAClE,QAAO,IAAI,oBAAoB,0BAA0B,0BAA0B;EACjF,KAAK,aAAa,KAAK;EACvB,KAAK;EACL,SAAS,EAAE,MAAM;EAClB,CAAC;;AAGJ,SAAgB,yBAAyB,SAAiB,MAAmC;AAC3F,QAAO,IAAI,oBACT,oCACA,wCACA;EACE,KAAK,cAAc,QAAQ,yBAAyB,KAAK;EACzD,KAAK;EACL,SAAS;GAAE;GAAS;GAAM;EAC3B,CACF;;AAGH,SAAgB,qBACd,YACA,SAOqB;CACrB,MAAM,iBAAiB,UACnB,uBAAuB,QAAQ,gBAAgB,eAAe,QAAQ,SAAS,KAAK,MAAM,OAAO,EAAE,IAAI,IAAI,EAAE,MAAM,OAAO,YAAY,EAAE,MAAM,KAAK,MAAM,EAAE,QAAQ,CAAC,KAAK,MAAM,IAAI,SAAS,GAAG,CAAC,KAAK,KAAK,KAC1M;AACJ,QAAO,IAAI,oBAAoB,8BAA8B,8BAA8B;EACzF,KAAK,8DAA8D,WAAW,KAAK,KAAK,CAAC,4FAA4F;EACrL,KAAK;EACL,SAAS;GACP;GACA,GAAI,UAAU;IAAE,iBAAiB,QAAQ;IAAiB,UAAU,QAAQ;IAAU,GAAG,EAAE;GAC5F;EACF,CAAC;;AAGJ,SAAgB,wBAAwB,OAA+C;AACrF,QAAO,IAAI,oBAAoB,kCAAkC,8BAA8B;EAC7F,KAAK,oEAAoE,MAAM,KAAK,KAAK,CAAC;EAC1F,KAAK;EACL,SAAS,EAAE,OAAO;EACnB,CAAC;;AAGJ,SAAgB,iBAAiB,UAAkB,QAAqC;AACtF,QAAO,IAAI,oBAAoB,0BAA0B,qBAAqB;EAC5E,KAAK,iBAAiB,SAAS,gBAAgB;EAC/C,KAAK;EACL,SAAS;GAAE,MAAM;GAAU;GAAQ;EACpC,CAAC;;AAWJ,SAAgB,oBAAoB,SAAsC;AACxE,QAAO,IAAI,oBAAoB,8BAA8B,oBAAoB;EAC/E,KAAK,aAAa,QAAQ;EAC1B,KAAK;EACL,SAAS,EAAE,SAAS;EACrB,CAAC;;AAGJ,SAAgB,cAAc,iBAAyD;AACrF,QAAO,IAAI,oBAAoB,uBAAuB,yCAAyC;EAC7F,KAAK,wGAAwG,gBAAgB,KAAK,KAAK,CAAC;EACxI,KAAK;EACL,SAAS,EAAE,iBAAiB;EAC7B,CAAC;;AAGJ,SAAgB,qBAAqB,OAAoC;AACvE,QAAO,IAAI,oBAAoB,+BAA+B,qBAAqB;EACjF,KAAK,cAAc,MAAM;EACzB,KAAK;EACL,SAAS,EAAE,OAAO;EACnB,CAAC;;AAGJ,SAAgB,0BAA0B,aAA0C;AAClF,QAAO,IAAI,oBACT,oCACA,4CACA;EACE,KAAK,0CAA0C,YAAY;EAC3D,KAAK;EACL,SAAS,EAAE,aAAa;EACzB,CACF"}
1
+ {"version":3,"file":"errors-C_XuSbX7.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Structured error for migration tooling operations.\n *\n * Follows the NAMESPACE.SUBCODE convention from ADR 027. All codes live under\n * the MIGRATION namespace. These are tooling-time errors (file I/O, attestation,\n * migration history reconstruction), distinct from the runtime MIGRATION.* codes for apply-time\n * failures (PRECHECK_FAILED, POSTCHECK_FAILED, etc.).\n *\n * Fields:\n * - code: Stable machine-readable code (MIGRATION.SUBCODE)\n * - category: Always 'MIGRATION'\n * - why: Explains the cause in plain language\n * - fix: Actionable remediation step\n * - details: Machine-readable structured data for agents\n */\nexport class MigrationToolsError extends Error {\n readonly code: string;\n readonly category = 'MIGRATION' as const;\n readonly why: string;\n readonly fix: string;\n readonly details: Record<string, unknown> | undefined;\n\n constructor(\n code: string,\n summary: string,\n options: {\n readonly why: string;\n readonly fix: string;\n readonly details?: Record<string, unknown>;\n },\n ) {\n super(summary);\n this.name = 'MigrationToolsError';\n this.code = code;\n this.why = options.why;\n this.fix = options.fix;\n this.details = options.details;\n }\n\n static is(error: unknown): error is MigrationToolsError {\n if (!(error instanceof Error)) return false;\n const candidate = error as MigrationToolsError;\n return candidate.name === 'MigrationToolsError' && typeof candidate.code === 'string';\n }\n}\n\nexport function errorDirectoryExists(dir: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.DIR_EXISTS', 'Migration directory already exists', {\n why: `The directory \"${dir}\" already exists. Each migration must have a unique directory.`,\n fix: 'Use --name to pick a different name, or delete the existing directory and re-run.',\n details: { dir },\n });\n}\n\nexport function errorMissingFile(file: string, dir: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.FILE_MISSING', `Missing ${file}`, {\n why: `Expected \"${file}\" in \"${dir}\" but the file does not exist.`,\n fix: 'Ensure the migration directory contains both migration.json and ops.json. If the directory is corrupt, delete it and re-run migration plan.',\n details: { file, dir },\n });\n}\n\nexport function errorInvalidJson(filePath: string, parseError: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_JSON', 'Invalid JSON in migration file', {\n why: `Failed to parse \"${filePath}\": ${parseError}`,\n fix: 'Fix the JSON syntax error, or delete the migration directory and re-run migration plan.',\n details: { filePath, parseError },\n });\n}\n\nexport function errorInvalidManifest(filePath: string, reason: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_MANIFEST', 'Invalid migration manifest', {\n why: `Manifest at \"${filePath}\" is invalid: ${reason}`,\n fix: 'Ensure the manifest has all required fields (from, to, kind, toContract). If corrupt, delete and re-plan.',\n details: { filePath, reason },\n });\n}\n\nexport function errorInvalidSlug(slug: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_NAME', 'Invalid migration name', {\n why: `The slug \"${slug}\" contains no valid characters after sanitization (only a-z, 0-9 are kept).`,\n fix: 'Provide a name with at least one alphanumeric character, e.g. --name add_users.',\n details: { slug },\n });\n}\n\nexport function errorSameSourceAndTarget(dirName: string, hash: string): MigrationToolsError {\n return new MigrationToolsError(\n 'MIGRATION.SAME_SOURCE_AND_TARGET',\n 'Migration has same source and target',\n {\n why: `Migration \"${dirName}\" has from === to === \"${hash}\". A migration must transition between two different contract states.`,\n fix: 'Delete the invalid migration directory and re-run migration plan.',\n details: { dirName, hash },\n },\n );\n}\n\nexport function errorAmbiguousTarget(\n branchTips: readonly string[],\n context?: {\n divergencePoint: string;\n branches: readonly {\n tip: string;\n edges: readonly { dirName: string; from: string; to: string }[];\n }[];\n },\n): MigrationToolsError {\n const divergenceInfo = context\n ? `\\nDivergence point: ${context.divergencePoint}\\nBranches:\\n${context.branches.map((b) => ` → ${b.tip} (${b.edges.length} edge(s): ${b.edges.map((e) => e.dirName).join(' → ') || 'direct'})`).join('\\n')}`\n : '';\n return new MigrationToolsError('MIGRATION.AMBIGUOUS_TARGET', 'Ambiguous migration target', {\n why: `The migration history has diverged into multiple branches: ${branchTips.join(', ')}. This typically happens when two developers plan migrations from the same starting point.${divergenceInfo}`,\n fix: 'Use `migration ref set <name> <hash>` to target a specific branch, delete one of the conflicting migration directories and re-run `migration plan`, or use --from <hash> to explicitly select a starting point.',\n details: {\n branchTips,\n ...(context ? { divergencePoint: context.divergencePoint, branches: context.branches } : {}),\n },\n });\n}\n\nexport function errorNoInitialMigration(nodes: readonly string[]): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.NO_INITIAL_MIGRATION', 'No initial migration found', {\n why: `No migration starts from the empty contract state (known hashes: ${nodes.join(', ')}). At least one migration must originate from the empty state.`,\n fix: 'Inspect the migrations directory for corrupted migration.json files. At least one migration must start from the empty contract hash.',\n details: { nodes },\n });\n}\n\nexport function errorInvalidRefs(refsPath: string, reason: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_REFS', 'Invalid refs.json', {\n why: `refs.json at \"${refsPath}\" is invalid: ${reason}`,\n fix: 'Ensure refs.json is a flat object mapping valid ref names to contract hash strings.',\n details: { path: refsPath, reason },\n });\n}\n\nexport function errorInvalidRefFile(filePath: string, reason: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_REF_FILE', 'Invalid ref file', {\n why: `Ref file at \"${filePath}\" is invalid: ${reason}`,\n fix: 'Ensure the ref file contains valid JSON with { \"hash\": \"sha256:<64 hex chars>\", \"invariants\": [\"...\"] }.',\n details: { path: filePath, reason },\n });\n}\n\nexport function errorInvalidRefName(refName: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_REF_NAME', 'Invalid ref name', {\n why: `Ref name \"${refName}\" is invalid. Names must be lowercase alphanumeric with hyphens or forward slashes (no \".\" or \"..\" segments).`,\n fix: `Use a valid ref name (e.g., \"staging\", \"envs/production\").`,\n details: { refName },\n });\n}\n\nexport function errorNoTarget(reachableHashes: readonly string[]): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.NO_TARGET', 'No migration target could be resolved', {\n why: `The migration history contains cycles and no target can be resolved automatically (reachable hashes: ${reachableHashes.join(', ')}). This typically happens after rollback migrations (e.g., C1→C2→C1).`,\n fix: 'Use --from <hash> to specify the planning origin explicitly.',\n details: { reachableHashes },\n });\n}\n\nexport function errorInvalidRefValue(value: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_REF_VALUE', 'Invalid ref value', {\n why: `Ref value \"${value}\" is not a valid contract hash. Values must be in the format \"sha256:<64 hex chars>\" or \"sha256:empty\".`,\n fix: 'Use a valid storage hash from `prisma-next contract emit` output or an existing migration.',\n details: { value },\n });\n}\n\nexport function errorDuplicateMigrationId(migrationId: string): MigrationToolsError {\n return new MigrationToolsError(\n 'MIGRATION.DUPLICATE_MIGRATION_ID',\n 'Duplicate migrationId in migration graph',\n {\n why: `Multiple migrations share migrationId \"${migrationId}\". Each migration must have a unique content-addressed identity.`,\n fix: 'Regenerate one of the conflicting migrations so each migrationId is unique, then re-run migration commands.',\n details: { migrationId },\n },\n );\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { l as MigrationManifest, u as MigrationOps } from "../types-BW_pJEe8.mjs";
1
+ import { l as MigrationManifest, u as MigrationOps } from "../types-D2uX4ql7.mjs";
2
2
 
3
3
  //#region src/attestation.d.ts
4
4
  interface VerifyResult {
@@ -7,7 +7,15 @@ interface VerifyResult {
7
7
  readonly storedMigrationId?: string;
8
8
  readonly computedMigrationId?: string;
9
9
  }
10
+ /**
11
+ * Content-addressed migration identity over (manifest envelope sans
12
+ * contracts/hints, ops). See ADR 199 "Storage-only migration identity"
13
+ * for the rationale: contracts are anchored separately by the
14
+ * storage-hash bookends inside the envelope; planner hints are advisory
15
+ * and must not affect identity.
16
+ */
10
17
  declare function computeMigrationId(manifest: MigrationManifest, ops: MigrationOps): string;
18
+ /** Compute and persist `migrationId` to `manifest.json`. */
11
19
  declare function attestMigration(dir: string): Promise<string>;
12
20
  declare function verifyMigration(dir: string): Promise<VerifyResult>;
13
21
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"attestation.d.mts","names":[],"sources":["../../src/attestation.ts"],"sourcesContent":[],"mappings":";;;UAMiB,YAAA;;EAAA,SAAA,MAAY,CAAA,EAAA,OAAA,GAAA,UAAA;EAWb,SAAA,iBAAkB,CAAA,EAAA,MAAW;EA2BvB,SAAA,mBAAe,CAAA,EAAe,MAAA;AAUpD;iBArCgB,kBAAA,WAA6B,wBAAwB;iBA2B/C,eAAA,eAA8B;iBAU9B,eAAA,eAA8B,QAAQ"}
1
+ {"version":3,"file":"attestation.d.mts","names":[],"sources":["../../src/attestation.ts"],"sourcesContent":[],"mappings":";;;UAKiB,YAAA;;EAAA,SAAA,MAAY,CAAA,EAAA,OAAA,GAAA,UAAA;EAkBb,SAAA,iBAAkB,CAAA,EAAA,MAAW;EAoBvB,SAAA,mBAAe,CAAA,EAAe,MAAA;AAUpD;;;;;;;;iBA9BgB,kBAAA,WAA6B,wBAAwB;;iBAoB/C,eAAA,eAA8B;iBAU9B,eAAA,eAA8B,QAAQ"}
@@ -1,62 +1,4 @@
1
- import { i as writeMigrationManifest, n as readMigrationPackage } from "../io-LsuurzNb.mjs";
2
- import { createHash } from "node:crypto";
3
- import { canonicalizeContract } from "@prisma-next/contract/hashing";
1
+ import "../io-Cun81AIZ.mjs";
2
+ import { n as computeMigrationId, r as verifyMigration, t as attestMigration } from "../attestation-DnebS4XZ.mjs";
4
3
 
5
- //#region src/canonicalize-json.ts
6
- function sortKeys(value) {
7
- if (value === null || typeof value !== "object") return value;
8
- if (Array.isArray(value)) return value.map(sortKeys);
9
- const sorted = {};
10
- for (const key of Object.keys(value).sort()) sorted[key] = sortKeys(value[key]);
11
- return sorted;
12
- }
13
- function canonicalizeJson(value) {
14
- return JSON.stringify(sortKeys(value));
15
- }
16
-
17
- //#endregion
18
- //#region src/attestation.ts
19
- function sha256Hex(input) {
20
- return createHash("sha256").update(input).digest("hex");
21
- }
22
- function computeMigrationId(manifest, ops) {
23
- const { migrationId: _migrationId, signature: _signature, fromContract: _fromContract, toContract: _toContract, ...strippedMeta } = manifest;
24
- return `sha256:${sha256Hex(canonicalizeJson([
25
- canonicalizeJson(strippedMeta),
26
- canonicalizeJson(ops),
27
- manifest.fromContract !== null ? canonicalizeContract(manifest.fromContract) : "null",
28
- canonicalizeContract(manifest.toContract)
29
- ].map(sha256Hex)))}`;
30
- }
31
- async function attestMigration(dir) {
32
- const pkg = await readMigrationPackage(dir);
33
- const migrationId = computeMigrationId(pkg.manifest, pkg.ops);
34
- await writeMigrationManifest(dir, {
35
- ...pkg.manifest,
36
- migrationId
37
- });
38
- return migrationId;
39
- }
40
- async function verifyMigration(dir) {
41
- const pkg = await readMigrationPackage(dir);
42
- if (pkg.manifest.migrationId === null) return {
43
- ok: false,
44
- reason: "draft"
45
- };
46
- const computed = computeMigrationId(pkg.manifest, pkg.ops);
47
- if (pkg.manifest.migrationId === computed) return {
48
- ok: true,
49
- storedMigrationId: pkg.manifest.migrationId,
50
- computedMigrationId: computed
51
- };
52
- return {
53
- ok: false,
54
- reason: "mismatch",
55
- storedMigrationId: pkg.manifest.migrationId,
56
- computedMigrationId: computed
57
- };
58
- }
59
-
60
- //#endregion
61
- export { attestMigration, computeMigrationId, verifyMigration };
62
- //# sourceMappingURL=attestation.mjs.map
4
+ export { attestMigration, computeMigrationId, verifyMigration };
@@ -1,3 +1,3 @@
1
- import { t as EMPTY_CONTRACT_HASH } from "../constants-9f-bCq8Y.mjs";
1
+ import { t as EMPTY_CONTRACT_HASH } from "../constants-BRi0X7B_.mjs";
2
2
 
3
3
  export { EMPTY_CONTRACT_HASH };
@@ -1,4 +1,4 @@
1
- import { o as MigrationChainEntry, s as MigrationGraph, t as AttestedMigrationBundle } from "../types-BW_pJEe8.mjs";
1
+ import { o as MigrationChainEntry, s as MigrationGraph, t as AttestedMigrationBundle } from "../types-D2uX4ql7.mjs";
2
2
 
3
3
  //#region src/dag.d.ts
4
4
  declare function reconstructGraph(packages: readonly AttestedMigrationBundle[]): MigrationGraph;
@@ -1,5 +1,5 @@
1
- import { f as errorNoInitialMigration, i as errorDuplicateMigrationId, m as errorSameSourceAndTarget, n as errorAmbiguousTarget, p as errorNoTarget } from "../errors-CSAAto11.mjs";
2
- import { t as EMPTY_CONTRACT_HASH } from "../constants-9f-bCq8Y.mjs";
1
+ import { f as errorNoInitialMigration, i as errorDuplicateMigrationId, m as errorSameSourceAndTarget, n as errorAmbiguousTarget, p as errorNoTarget } from "../errors-C_XuSbX7.mjs";
2
+ import { t as EMPTY_CONTRACT_HASH } from "../constants-BRi0X7B_.mjs";
3
3
  import { ifDefined } from "@prisma-next/utils/defined";
4
4
 
5
5
  //#region src/dag.ts
@@ -1,12 +1,24 @@
1
- import { l as MigrationManifest, r as BaseMigrationBundle, u as MigrationOps } from "../types-BW_pJEe8.mjs";
1
+ import { l as MigrationManifest, r as BaseMigrationBundle, u as MigrationOps } from "../types-D2uX4ql7.mjs";
2
2
 
3
3
  //#region src/io.d.ts
4
4
  declare function writeMigrationPackage(dir: string, manifest: MigrationManifest, ops: MigrationOps): Promise<void>;
5
+ /**
6
+ * Copy the destination contract artifacts (`contract.json` and the
7
+ * colocated `contract.d.ts`) into the migration package directory so
8
+ * authors of the scaffolded `migration.ts` can import the typed
9
+ * contract relative to the migration directory
10
+ * (`import type { Contract } from './contract'`).
11
+ *
12
+ * A missing `.d.ts` is tolerated (only the `.json` is required) so the
13
+ * helper stays usable in tests that hand-roll a bare `contract.json`.
14
+ * A missing `contract.json` — or any other I/O failure — throws.
15
+ */
16
+ declare function copyContractToMigrationDir(packageDir: string, contractJsonPath: string): Promise<void>;
5
17
  declare function writeMigrationManifest(dir: string, manifest: MigrationManifest): Promise<void>;
6
18
  declare function writeMigrationOps(dir: string, ops: MigrationOps): Promise<void>;
7
19
  declare function readMigrationPackage(dir: string): Promise<BaseMigrationBundle>;
8
20
  declare function readMigrationsDir(migrationsRoot: string): Promise<readonly BaseMigrationBundle[]>;
9
21
  declare function formatMigrationDirName(timestamp: Date, slug: string): string;
10
22
  //#endregion
11
- export { formatMigrationDirName, readMigrationPackage, readMigrationsDir, writeMigrationManifest, writeMigrationOps, writeMigrationPackage };
23
+ export { copyContractToMigrationDir, formatMigrationDirName, readMigrationPackage, readMigrationsDir, writeMigrationManifest, writeMigrationOps, writeMigrationPackage };
12
24
  //# sourceMappingURL=io.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"io.d.mts","names":[],"sources":["../../src/io.ts"],"sourcesContent":[],"mappings":";;;iBAwDsB,qBAAA,wBAEV,wBACL,eACJ;iBAgBmB,sBAAA,wBAEV,oBACT;AAvBmB,iBA2BA,iBAAA,CA3BqB,GAAA,EAAA,MAAA,EAAA,GAAA,EA2Be,YA3Bf,CAAA,EA2B8B,OA3B9B,CAAA,IAAA,CAAA;AAE/B,iBA6BU,oBAAA,CA7BV,GAAA,EAAA,MAAA,CAAA,EA6B6C,OA7B7C,CA6BqD,mBA7BrD,CAAA;AACL,iBA8Fe,iBAAA,CA9Ff,cAAA,EAAA,MAAA,CAAA,EAgGJ,OAhGI,CAAA,SAgGa,mBAhGb,EAAA,CAAA;AACJ,iBA8Ha,sBAAA,CA9Hb,SAAA,EA8H+C,IA9H/C,EAAA,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA"}
1
+ {"version":3,"file":"io.d.mts","names":[],"sources":["../../src/io.ts"],"sourcesContent":[],"mappings":";;;iBAwDsB,qBAAA,wBAEV,wBACL,eACJ;;AAJH;;;;;AA+BA;AAcA;AAOA;AAIA;AAkEA;AAiCgB,iBA5HM,0BAAA,CA4HgC,UAAA,EAAA,MAAA,EAAA,gBAAA,EAAA,MAAA,CAAA,EAzHnD,OAyHmD,CAAA,IAAA,CAAA;iBA9GhC,sBAAA,wBAEV,oBACT;iBAImB,iBAAA,mBAAoC,eAAe;iBAInD,oBAAA,eAAmC,QAAQ;iBAkE3C,iBAAA,0BAEnB,iBAAiB;iBA+BJ,sBAAA,YAAkC"}
@@ -1,3 +1,3 @@
1
- import { a as writeMigrationOps, i as writeMigrationManifest, n as readMigrationPackage, o as writeMigrationPackage, r as readMigrationsDir, t as formatMigrationDirName } from "../io-LsuurzNb.mjs";
1
+ import { a as writeMigrationManifest, i as readMigrationsDir, n as formatMigrationDirName, o as writeMigrationOps, r as readMigrationPackage, s as writeMigrationPackage, t as copyContractToMigrationDir } from "../io-Cun81AIZ.mjs";
2
2
 
3
- export { formatMigrationDirName, readMigrationPackage, readMigrationsDir, writeMigrationManifest, writeMigrationOps, writeMigrationPackage };
3
+ export { copyContractToMigrationDir, formatMigrationDirName, readMigrationPackage, readMigrationsDir, writeMigrationManifest, writeMigrationOps, writeMigrationPackage };
@@ -1,34 +1,43 @@
1
- import { OperationDescriptor } from "@prisma-next/framework-components/control";
2
-
3
1
  //#region src/migration-ts.d.ts
4
-
5
2
  /**
6
- * Options for scaffolding a migration.ts file.
3
+ * Utilities for reading/writing `migration.ts` files.
4
+ *
5
+ * Rendering migration.ts source is now the target's responsibility — the CLI
6
+ * obtains source strings either from a class-flow planner's
7
+ * `plan.renderTypeScript()` or from a descriptor-flow target's
8
+ * `migrations.renderDescriptorTypeScript(descriptors, context)`. The helper
9
+ * here is limited to file I/O: writing the returned source with the right
10
+ * executable bit, probing for existence, and evaluating legacy descriptor-
11
+ * flow files.
7
12
  */
8
- interface ScaffoldOptions {
9
- /** Operation descriptors to serialize as builder calls. */
10
- readonly descriptors?: readonly OperationDescriptor[];
11
- /** Absolute path to contract.json — used to derive contract.d.ts import for typed builders. */
12
- readonly contractJsonPath?: string;
13
- }
14
13
  /**
15
- * Scaffolds a migration.ts file in the given package directory.
16
- * Serializes operation descriptors as builder calls that the user can edit.
17
- * On verify, this file is re-evaluated to produce the final ops.
14
+ * Writes a pre-rendered `migration.ts` source string to the given package
15
+ * directory. If the source begins with a shebang, the file is written with
16
+ * executable permissions (0o755) so it can be run directly via
17
+ * `./migration.ts` by the authoring class's `Migration.run(...)` guard.
18
18
  */
19
- declare function scaffoldMigrationTs(packageDir: string, options?: ScaffoldOptions): Promise<void>;
19
+ declare function writeMigrationTs(packageDir: string, content: string): Promise<void>;
20
20
  /**
21
21
  * Checks whether a migration.ts file exists in the package directory.
22
22
  */
23
23
  declare function hasMigrationTs(packageDir: string): Promise<boolean>;
24
24
  /**
25
- * Evaluates a migration.ts file by loading it via native Node import.
26
- * Returns the result of calling the default export (expected to be a
27
- * function returning an array of operation descriptors).
25
+ * Evaluates a descriptor-flow migration.ts file by loading it via native
26
+ * Node import. Returns the result of calling the default export (expected
27
+ * to be a function returning an array of operation descriptors).
28
+ *
29
+ * Class-flow migration.ts files use a different shape — their default
30
+ * export is a class that extends `Migration` — and are evaluated by the
31
+ * target's `emit` capability, not this helper.
28
32
  *
29
33
  * Requires Node ≥24 for native TypeScript support.
30
34
  */
31
35
  declare function evaluateMigrationTs(packageDir: string): Promise<readonly unknown[]>;
32
36
  //#endregion
33
- export { type ScaffoldOptions, evaluateMigrationTs, hasMigrationTs, scaffoldMigrationTs };
37
+ //#region src/runtime-detection.d.ts
38
+ type ScaffoldRuntime = 'node' | 'bun' | 'deno';
39
+ declare function detectScaffoldRuntime(): ScaffoldRuntime;
40
+ declare function shebangLineFor(runtime: ScaffoldRuntime): string;
41
+ //#endregion
42
+ export { type ScaffoldRuntime, detectScaffoldRuntime, evaluateMigrationTs, hasMigrationTs, shebangLineFor, writeMigrationTs };
34
43
  //# sourceMappingURL=migration-ts.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"migration-ts.d.mts","names":[],"sources":["../../src/migration-ts.ts"],"sourcesContent":[],"mappings":";;;;;;;UAmBiB,eAAA;;kCAEiB;;;;;;;;;iBAsFZ,mBAAA,+BAEX,kBACR;;;;iBA6CmB,cAAA,sBAAoC;;;;;;;;iBAgBpC,mBAAA,sBAAyC"}
1
+ {"version":3,"file":"migration-ts.d.mts","names":[],"sources":["../../src/migration-ts.ts","../../src/runtime-detection.ts"],"sourcesContent":[],"mappings":";;AAuBA;AAYA;AAoBA;;;;ACvDA;AAEA;AAMA;;;;;;;;iBDesB,gBAAA,uCAAuD;;;;iBAYvD,cAAA,sBAAoC;;;;;;;;;;;;iBAoBpC,mBAAA,sBAAyC;;;KCvDnD,eAAA;ADuBU,iBCrBN,qBAAA,CAAA,CDqBoE,ECrB3C,eDqB2C;AAY9D,iBC3BN,cAAA,CD2B0C,OAAO,EC3BzB,eD2ByB,CAAA,EAAA,MAAA"}
@@ -1,93 +1,28 @@
1
1
  import { stat, writeFile } from "node:fs/promises";
2
- import { join, relative, resolve } from "pathe";
2
+ import { join, resolve } from "pathe";
3
3
 
4
4
  //#region src/migration-ts.ts
5
5
  /**
6
- * Utilities for scaffolding and evaluating migration.ts files.
6
+ * Utilities for reading/writing `migration.ts` files.
7
7
  *
8
- * - scaffoldMigrationTs: writes a migration.ts file with boilerplate
9
- * - evaluateMigrationTs: loads migration.ts via native Node import, returns descriptors
10
- *
11
- * Shared by migration plan (scaffold), migration new (scaffold), and
12
- * migration verify (evaluate).
8
+ * Rendering migration.ts source is now the target's responsibility — the CLI
9
+ * obtains source strings either from a class-flow planner's
10
+ * `plan.renderTypeScript()` or from a descriptor-flow target's
11
+ * `migrations.renderDescriptorTypeScript(descriptors, context)`. The helper
12
+ * here is limited to file I/O: writing the returned source with the right
13
+ * executable bit, probing for existence, and evaluating legacy descriptor-
14
+ * flow files.
13
15
  */
14
16
  const MIGRATION_TS_FILE = "migration.ts";
15
- function serializeQueryInput(input) {
16
- if (typeof input === "boolean") return String(input);
17
- if (typeof input === "symbol") return "TODO /* fill in using db.sql.from(...) */";
18
- if (input === null || input === void 0) return "null";
19
- if (Array.isArray(input)) {
20
- if (input.length === 0) return "[]";
21
- if (input.every((item) => typeof item === "symbol")) return "[TODO /* fill in using db.sql.from(...) */]";
22
- return `[${input.map(serializeQueryInput).join(", ")}]`;
23
- }
24
- return JSON.stringify(input);
25
- }
26
- function descriptorToBuilderCall(desc) {
27
- switch (desc.kind) {
28
- case "createTable": return `createTable(${JSON.stringify(desc["table"])})`;
29
- case "dropTable": return `dropTable(${JSON.stringify(desc["table"])})`;
30
- case "addColumn": {
31
- const args = [JSON.stringify(desc["table"]), JSON.stringify(desc["column"])];
32
- if (desc["overrides"]) args.push(JSON.stringify(desc["overrides"]));
33
- return `addColumn(${args.join(", ")})`;
34
- }
35
- case "dropColumn": return `dropColumn(${JSON.stringify(desc["table"])}, ${JSON.stringify(desc["column"])})`;
36
- case "alterColumnType": {
37
- const opts = {};
38
- if (desc["using"]) opts["using"] = desc["using"];
39
- if (desc["toType"]) opts["toType"] = desc["toType"];
40
- return Object.keys(opts).length > 0 ? `alterColumnType(${JSON.stringify(desc["table"])}, ${JSON.stringify(desc["column"])}, ${JSON.stringify(opts)})` : `alterColumnType(${JSON.stringify(desc["table"])}, ${JSON.stringify(desc["column"])})`;
41
- }
42
- case "setNotNull": return `setNotNull(${JSON.stringify(desc["table"])}, ${JSON.stringify(desc["column"])})`;
43
- case "dropNotNull": return `dropNotNull(${JSON.stringify(desc["table"])}, ${JSON.stringify(desc["column"])})`;
44
- case "setDefault": return `setDefault(${JSON.stringify(desc["table"])}, ${JSON.stringify(desc["column"])})`;
45
- case "dropDefault": return `dropDefault(${JSON.stringify(desc["table"])}, ${JSON.stringify(desc["column"])})`;
46
- case "addPrimaryKey": return `addPrimaryKey(${JSON.stringify(desc["table"])})`;
47
- case "addUnique": return `addUnique(${JSON.stringify(desc["table"])}, ${JSON.stringify(desc["columns"])})`;
48
- case "addForeignKey": return `addForeignKey(${JSON.stringify(desc["table"])}, ${JSON.stringify(desc["columns"])})`;
49
- case "dropConstraint": return `dropConstraint(${JSON.stringify(desc["table"])}, ${JSON.stringify(desc["constraintName"])})`;
50
- case "createIndex": return `createIndex(${JSON.stringify(desc["table"])}, ${JSON.stringify(desc["columns"])})`;
51
- case "dropIndex": return `dropIndex(${JSON.stringify(desc["table"])}, ${JSON.stringify(desc["indexName"])})`;
52
- case "createEnumType": return desc["values"] ? `createEnumType(${JSON.stringify(desc["typeName"])}, ${JSON.stringify(desc["values"])})` : `createEnumType(${JSON.stringify(desc["typeName"])})`;
53
- case "addEnumValues": return `addEnumValues(${JSON.stringify(desc["typeName"])}, ${JSON.stringify(desc["values"])})`;
54
- case "dropEnumType": return `dropEnumType(${JSON.stringify(desc["typeName"])})`;
55
- case "renameType": return `renameType(${JSON.stringify(desc["fromName"])}, ${JSON.stringify(desc["toName"])})`;
56
- case "createDependency": return `createDependency(${JSON.stringify(desc["dependencyId"])})`;
57
- case "dataTransform": return `dataTransform(${JSON.stringify(desc["name"])}, {\n check: ${serializeQueryInput(desc["check"])},\n run: ${serializeQueryInput(desc["run"])},\n })`;
58
- default: throw new Error(`Unknown descriptor kind: ${desc.kind}`);
59
- }
60
- }
61
17
  /**
62
- * Scaffolds a migration.ts file in the given package directory.
63
- * Serializes operation descriptors as builder calls that the user can edit.
64
- * On verify, this file is re-evaluated to produce the final ops.
18
+ * Writes a pre-rendered `migration.ts` source string to the given package
19
+ * directory. If the source begins with a shebang, the file is written with
20
+ * executable permissions (0o755) so it can be run directly via
21
+ * `./migration.ts` by the authoring class's `Migration.run(...)` guard.
65
22
  */
66
- async function scaffoldMigrationTs(packageDir, options = {}) {
67
- const filePath = join(packageDir, MIGRATION_TS_FILE);
68
- const descriptors = options.descriptors ?? [];
69
- const hasDataTransform = descriptors.some((d) => d.kind === "dataTransform");
70
- const lines = [];
71
- if (hasDataTransform && options.contractJsonPath) {
72
- const relativeContractDts = relative(packageDir, options.contractJsonPath).replace(/\.json$/, ".d");
73
- lines.push(`import type { Contract } from "${relativeContractDts}"`);
74
- lines.push(`import { createBuilders } from "@prisma-next/target-postgres/migration-builders"`);
75
- lines.push("");
76
- const importList = [...new Set(descriptors.map((d) => d.kind))];
77
- importList.push("TODO");
78
- lines.push(`const { ${importList.join(", ")} } = createBuilders<Contract>()`);
79
- } else {
80
- const importList = [...new Set(descriptors.map((d) => d.kind))];
81
- if (importList.length === 0) importList.push("createTable");
82
- if (hasDataTransform) importList.push("TODO");
83
- lines.push(`import { ${importList.join(", ")} } from "@prisma-next/target-postgres/migration-builders"`);
84
- }
85
- const calls = descriptors.map((d) => ` ${descriptorToBuilderCall(d)},`).join("\n");
86
- const body = calls.length > 0 ? `\n${calls}\n` : "";
87
- lines.push("");
88
- lines.push(`export default () => [${body}]`);
89
- lines.push("");
90
- await writeFile(filePath, lines.join("\n"));
23
+ async function writeMigrationTs(packageDir, content) {
24
+ const isExecutable = content.startsWith("#!");
25
+ await writeFile(join(packageDir, MIGRATION_TS_FILE), content, isExecutable ? { mode: 493 } : void 0);
91
26
  }
92
27
  /**
93
28
  * Checks whether a migration.ts file exists in the package directory.
@@ -100,9 +35,13 @@ async function hasMigrationTs(packageDir) {
100
35
  }
101
36
  }
102
37
  /**
103
- * Evaluates a migration.ts file by loading it via native Node import.
104
- * Returns the result of calling the default export (expected to be a
105
- * function returning an array of operation descriptors).
38
+ * Evaluates a descriptor-flow migration.ts file by loading it via native
39
+ * Node import. Returns the result of calling the default export (expected
40
+ * to be a function returning an array of operation descriptors).
41
+ *
42
+ * Class-flow migration.ts files use a different shape — their default
43
+ * export is a class that extends `Migration` — and are evaluated by the
44
+ * target's `emit` capability, not this helper.
106
45
  *
107
46
  * Requires Node ≥24 for native TypeScript support.
108
47
  */
@@ -121,5 +60,20 @@ async function evaluateMigrationTs(packageDir) {
121
60
  }
122
61
 
123
62
  //#endregion
124
- export { evaluateMigrationTs, hasMigrationTs, scaffoldMigrationTs };
63
+ //#region src/runtime-detection.ts
64
+ function detectScaffoldRuntime() {
65
+ if (typeof globalThis.Bun !== "undefined") return "bun";
66
+ if (typeof globalThis.Deno !== "undefined") return "deno";
67
+ return "node";
68
+ }
69
+ function shebangLineFor(runtime) {
70
+ switch (runtime) {
71
+ case "bun": return "#!/usr/bin/env -S bun";
72
+ case "deno": return "#!/usr/bin/env -S deno run -A";
73
+ case "node": return "#!/usr/bin/env -S node";
74
+ }
75
+ }
76
+
77
+ //#endregion
78
+ export { detectScaffoldRuntime, evaluateMigrationTs, hasMigrationTs, shebangLineFor, writeMigrationTs };
125
79
  //# sourceMappingURL=migration-ts.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"migration-ts.mjs","names":["opts: Record<string, unknown>","lines: string[]","result: unknown"],"sources":["../../src/migration-ts.ts"],"sourcesContent":["/**\n * Utilities for scaffolding and evaluating migration.ts files.\n *\n * - scaffoldMigrationTs: writes a migration.ts file with boilerplate\n * - evaluateMigrationTs: loads migration.ts via native Node import, returns descriptors\n *\n * Shared by migration plan (scaffold), migration new (scaffold), and\n * migration verify (evaluate).\n */\n\nimport { stat, writeFile } from 'node:fs/promises';\nimport type { OperationDescriptor } from '@prisma-next/framework-components/control';\nimport { join, relative, resolve } from 'pathe';\n\nconst MIGRATION_TS_FILE = 'migration.ts';\n\n/**\n * Options for scaffolding a migration.ts file.\n */\nexport interface ScaffoldOptions {\n /** Operation descriptors to serialize as builder calls. */\n readonly descriptors?: readonly OperationDescriptor[];\n /** Absolute path to contract.json — used to derive contract.d.ts import for typed builders. */\n readonly contractJsonPath?: string;\n}\n\nfunction serializeQueryInput(input: unknown): string {\n if (typeof input === 'boolean') return String(input);\n if (typeof input === 'symbol') return 'TODO /* fill in using db.sql.from(...) */';\n if (input === null || input === undefined) return 'null';\n if (Array.isArray(input)) {\n if (input.length === 0) return '[]';\n if (input.every((item) => typeof item === 'symbol'))\n return '[TODO /* fill in using db.sql.from(...) */]';\n return `[${input.map(serializeQueryInput).join(', ')}]`;\n }\n return JSON.stringify(input);\n}\n\nfunction descriptorToBuilderCall(desc: OperationDescriptor): string {\n switch (desc.kind) {\n case 'createTable':\n return `createTable(${JSON.stringify(desc['table'])})`;\n case 'dropTable':\n return `dropTable(${JSON.stringify(desc['table'])})`;\n case 'addColumn': {\n const args = [JSON.stringify(desc['table']), JSON.stringify(desc['column'])];\n if (desc['overrides']) {\n args.push(JSON.stringify(desc['overrides']));\n }\n return `addColumn(${args.join(', ')})`;\n }\n case 'dropColumn':\n return `dropColumn(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])})`;\n case 'alterColumnType': {\n const opts: Record<string, unknown> = {};\n if (desc['using']) opts['using'] = desc['using'];\n if (desc['toType']) opts['toType'] = desc['toType'];\n const hasOpts = Object.keys(opts).length > 0;\n return hasOpts\n ? `alterColumnType(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])}, ${JSON.stringify(opts)})`\n : `alterColumnType(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])})`;\n }\n case 'setNotNull':\n return `setNotNull(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])})`;\n case 'dropNotNull':\n return `dropNotNull(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])})`;\n case 'setDefault':\n return `setDefault(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])})`;\n case 'dropDefault':\n return `dropDefault(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])})`;\n case 'addPrimaryKey':\n return `addPrimaryKey(${JSON.stringify(desc['table'])})`;\n case 'addUnique':\n return `addUnique(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['columns'])})`;\n case 'addForeignKey':\n return `addForeignKey(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['columns'])})`;\n case 'dropConstraint':\n return `dropConstraint(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['constraintName'])})`;\n case 'createIndex':\n return `createIndex(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['columns'])})`;\n case 'dropIndex':\n return `dropIndex(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['indexName'])})`;\n case 'createEnumType':\n return desc['values']\n ? `createEnumType(${JSON.stringify(desc['typeName'])}, ${JSON.stringify(desc['values'])})`\n : `createEnumType(${JSON.stringify(desc['typeName'])})`;\n case 'addEnumValues':\n return `addEnumValues(${JSON.stringify(desc['typeName'])}, ${JSON.stringify(desc['values'])})`;\n case 'dropEnumType':\n return `dropEnumType(${JSON.stringify(desc['typeName'])})`;\n case 'renameType':\n return `renameType(${JSON.stringify(desc['fromName'])}, ${JSON.stringify(desc['toName'])})`;\n case 'createDependency':\n return `createDependency(${JSON.stringify(desc['dependencyId'])})`;\n case 'dataTransform':\n return `dataTransform(${JSON.stringify(desc['name'])}, {\\n check: ${serializeQueryInput(desc['check'])},\\n run: ${serializeQueryInput(desc['run'])},\\n })`;\n default:\n throw new Error(`Unknown descriptor kind: ${desc.kind}`);\n }\n}\n\n/**\n * Scaffolds a migration.ts file in the given package directory.\n * Serializes operation descriptors as builder calls that the user can edit.\n * On verify, this file is re-evaluated to produce the final ops.\n */\nexport async function scaffoldMigrationTs(\n packageDir: string,\n options: ScaffoldOptions = {},\n): Promise<void> {\n const filePath = join(packageDir, MIGRATION_TS_FILE);\n\n const descriptors = options.descriptors ?? [];\n const hasDataTransform = descriptors.some((d) => d.kind === 'dataTransform');\n\n const lines: string[] = [];\n\n if (hasDataTransform && options.contractJsonPath) {\n const relativeContractDts = relative(packageDir, options.contractJsonPath).replace(\n /\\.json$/,\n '.d',\n );\n lines.push(`import type { Contract } from \"${relativeContractDts}\"`);\n lines.push(`import { createBuilders } from \"@prisma-next/target-postgres/migration-builders\"`);\n lines.push('');\n const importList = [...new Set(descriptors.map((d) => d.kind))];\n importList.push('TODO');\n lines.push(`const { ${importList.join(', ')} } = createBuilders<Contract>()`);\n } else {\n const importList = [...new Set(descriptors.map((d) => d.kind))];\n if (importList.length === 0) {\n importList.push('createTable');\n }\n if (hasDataTransform) {\n importList.push('TODO');\n }\n lines.push(\n `import { ${importList.join(', ')} } from \"@prisma-next/target-postgres/migration-builders\"`,\n );\n }\n\n const calls = descriptors.map((d) => ` ${descriptorToBuilderCall(d)},`).join('\\n');\n const body = calls.length > 0 ? `\\n${calls}\\n` : '';\n\n lines.push('');\n lines.push(`export default () => [${body}]`);\n lines.push('');\n\n await writeFile(filePath, lines.join('\\n'));\n}\n\n/**\n * Checks whether a migration.ts file exists in the package directory.\n */\nexport async function hasMigrationTs(packageDir: string): Promise<boolean> {\n try {\n const s = await stat(join(packageDir, MIGRATION_TS_FILE));\n return s.isFile();\n } catch {\n return false;\n }\n}\n\n/**\n * Evaluates a migration.ts file by loading it via native Node import.\n * Returns the result of calling the default export (expected to be a\n * function returning an array of operation descriptors).\n *\n * Requires Node ≥24 for native TypeScript support.\n */\nexport async function evaluateMigrationTs(packageDir: string): Promise<readonly unknown[]> {\n const filePath = resolve(join(packageDir, MIGRATION_TS_FILE));\n\n try {\n await stat(filePath);\n } catch {\n throw new Error(`migration.ts not found at \"${filePath}\"`);\n }\n\n // Use native Node TS import (Node ≥24, stable type stripping)\n const mod = (await import(filePath)) as { default?: unknown };\n\n if (typeof mod.default !== 'function') {\n throw new Error(\n `migration.ts must export a default function returning an operation list. Got: ${typeof mod.default}`,\n );\n }\n\n const result: unknown = mod.default();\n\n if (!Array.isArray(result)) {\n throw new Error(\n `migration.ts default export must return an array of operations. Got: ${typeof result}`,\n );\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;AAcA,MAAM,oBAAoB;AAY1B,SAAS,oBAAoB,OAAwB;AACnD,KAAI,OAAO,UAAU,UAAW,QAAO,OAAO,MAAM;AACpD,KAAI,OAAO,UAAU,SAAU,QAAO;AACtC,KAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,KAAI,MAAM,QAAQ,MAAM,EAAE;AACxB,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,MAAI,MAAM,OAAO,SAAS,OAAO,SAAS,SAAS,CACjD,QAAO;AACT,SAAO,IAAI,MAAM,IAAI,oBAAoB,CAAC,KAAK,KAAK,CAAC;;AAEvD,QAAO,KAAK,UAAU,MAAM;;AAG9B,SAAS,wBAAwB,MAAmC;AAClE,SAAQ,KAAK,MAAb;EACE,KAAK,cACH,QAAO,eAAe,KAAK,UAAU,KAAK,SAAS,CAAC;EACtD,KAAK,YACH,QAAO,aAAa,KAAK,UAAU,KAAK,SAAS,CAAC;EACpD,KAAK,aAAa;GAChB,MAAM,OAAO,CAAC,KAAK,UAAU,KAAK,SAAS,EAAE,KAAK,UAAU,KAAK,UAAU,CAAC;AAC5E,OAAI,KAAK,aACP,MAAK,KAAK,KAAK,UAAU,KAAK,aAAa,CAAC;AAE9C,UAAO,aAAa,KAAK,KAAK,KAAK,CAAC;;EAEtC,KAAK,aACH,QAAO,cAAc,KAAK,UAAU,KAAK,SAAS,CAAC,IAAI,KAAK,UAAU,KAAK,UAAU,CAAC;EACxF,KAAK,mBAAmB;GACtB,MAAMA,OAAgC,EAAE;AACxC,OAAI,KAAK,SAAU,MAAK,WAAW,KAAK;AACxC,OAAI,KAAK,UAAW,MAAK,YAAY,KAAK;AAE1C,UADgB,OAAO,KAAK,KAAK,CAAC,SAAS,IAEvC,mBAAmB,KAAK,UAAU,KAAK,SAAS,CAAC,IAAI,KAAK,UAAU,KAAK,UAAU,CAAC,IAAI,KAAK,UAAU,KAAK,CAAC,KAC7G,mBAAmB,KAAK,UAAU,KAAK,SAAS,CAAC,IAAI,KAAK,UAAU,KAAK,UAAU,CAAC;;EAE1F,KAAK,aACH,QAAO,cAAc,KAAK,UAAU,KAAK,SAAS,CAAC,IAAI,KAAK,UAAU,KAAK,UAAU,CAAC;EACxF,KAAK,cACH,QAAO,eAAe,KAAK,UAAU,KAAK,SAAS,CAAC,IAAI,KAAK,UAAU,KAAK,UAAU,CAAC;EACzF,KAAK,aACH,QAAO,cAAc,KAAK,UAAU,KAAK,SAAS,CAAC,IAAI,KAAK,UAAU,KAAK,UAAU,CAAC;EACxF,KAAK,cACH,QAAO,eAAe,KAAK,UAAU,KAAK,SAAS,CAAC,IAAI,KAAK,UAAU,KAAK,UAAU,CAAC;EACzF,KAAK,gBACH,QAAO,iBAAiB,KAAK,UAAU,KAAK,SAAS,CAAC;EACxD,KAAK,YACH,QAAO,aAAa,KAAK,UAAU,KAAK,SAAS,CAAC,IAAI,KAAK,UAAU,KAAK,WAAW,CAAC;EACxF,KAAK,gBACH,QAAO,iBAAiB,KAAK,UAAU,KAAK,SAAS,CAAC,IAAI,KAAK,UAAU,KAAK,WAAW,CAAC;EAC5F,KAAK,iBACH,QAAO,kBAAkB,KAAK,UAAU,KAAK,SAAS,CAAC,IAAI,KAAK,UAAU,KAAK,kBAAkB,CAAC;EACpG,KAAK,cACH,QAAO,eAAe,KAAK,UAAU,KAAK,SAAS,CAAC,IAAI,KAAK,UAAU,KAAK,WAAW,CAAC;EAC1F,KAAK,YACH,QAAO,aAAa,KAAK,UAAU,KAAK,SAAS,CAAC,IAAI,KAAK,UAAU,KAAK,aAAa,CAAC;EAC1F,KAAK,iBACH,QAAO,KAAK,YACR,kBAAkB,KAAK,UAAU,KAAK,YAAY,CAAC,IAAI,KAAK,UAAU,KAAK,UAAU,CAAC,KACtF,kBAAkB,KAAK,UAAU,KAAK,YAAY,CAAC;EACzD,KAAK,gBACH,QAAO,iBAAiB,KAAK,UAAU,KAAK,YAAY,CAAC,IAAI,KAAK,UAAU,KAAK,UAAU,CAAC;EAC9F,KAAK,eACH,QAAO,gBAAgB,KAAK,UAAU,KAAK,YAAY,CAAC;EAC1D,KAAK,aACH,QAAO,cAAc,KAAK,UAAU,KAAK,YAAY,CAAC,IAAI,KAAK,UAAU,KAAK,UAAU,CAAC;EAC3F,KAAK,mBACH,QAAO,oBAAoB,KAAK,UAAU,KAAK,gBAAgB,CAAC;EAClE,KAAK,gBACH,QAAO,iBAAiB,KAAK,UAAU,KAAK,QAAQ,CAAC,kBAAkB,oBAAoB,KAAK,SAAS,CAAC,cAAc,oBAAoB,KAAK,OAAO,CAAC;EAC3J,QACE,OAAM,IAAI,MAAM,4BAA4B,KAAK,OAAO;;;;;;;;AAS9D,eAAsB,oBACpB,YACA,UAA2B,EAAE,EACd;CACf,MAAM,WAAW,KAAK,YAAY,kBAAkB;CAEpD,MAAM,cAAc,QAAQ,eAAe,EAAE;CAC7C,MAAM,mBAAmB,YAAY,MAAM,MAAM,EAAE,SAAS,gBAAgB;CAE5E,MAAMC,QAAkB,EAAE;AAE1B,KAAI,oBAAoB,QAAQ,kBAAkB;EAChD,MAAM,sBAAsB,SAAS,YAAY,QAAQ,iBAAiB,CAAC,QACzE,WACA,KACD;AACD,QAAM,KAAK,kCAAkC,oBAAoB,GAAG;AACpE,QAAM,KAAK,mFAAmF;AAC9F,QAAM,KAAK,GAAG;EACd,MAAM,aAAa,CAAC,GAAG,IAAI,IAAI,YAAY,KAAK,MAAM,EAAE,KAAK,CAAC,CAAC;AAC/D,aAAW,KAAK,OAAO;AACvB,QAAM,KAAK,WAAW,WAAW,KAAK,KAAK,CAAC,iCAAiC;QACxE;EACL,MAAM,aAAa,CAAC,GAAG,IAAI,IAAI,YAAY,KAAK,MAAM,EAAE,KAAK,CAAC,CAAC;AAC/D,MAAI,WAAW,WAAW,EACxB,YAAW,KAAK,cAAc;AAEhC,MAAI,iBACF,YAAW,KAAK,OAAO;AAEzB,QAAM,KACJ,YAAY,WAAW,KAAK,KAAK,CAAC,2DACnC;;CAGH,MAAM,QAAQ,YAAY,KAAK,MAAM,KAAK,wBAAwB,EAAE,CAAC,GAAG,CAAC,KAAK,KAAK;CACnF,MAAM,OAAO,MAAM,SAAS,IAAI,KAAK,MAAM,MAAM;AAEjD,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,yBAAyB,KAAK,GAAG;AAC5C,OAAM,KAAK,GAAG;AAEd,OAAM,UAAU,UAAU,MAAM,KAAK,KAAK,CAAC;;;;;AAM7C,eAAsB,eAAe,YAAsC;AACzE,KAAI;AAEF,UADU,MAAM,KAAK,KAAK,YAAY,kBAAkB,CAAC,EAChD,QAAQ;SACX;AACN,SAAO;;;;;;;;;;AAWX,eAAsB,oBAAoB,YAAiD;CACzF,MAAM,WAAW,QAAQ,KAAK,YAAY,kBAAkB,CAAC;AAE7D,KAAI;AACF,QAAM,KAAK,SAAS;SACd;AACN,QAAM,IAAI,MAAM,8BAA8B,SAAS,GAAG;;CAI5D,MAAM,MAAO,MAAM,OAAO;AAE1B,KAAI,OAAO,IAAI,YAAY,WACzB,OAAM,IAAI,MACR,iFAAiF,OAAO,IAAI,UAC7F;CAGH,MAAMC,SAAkB,IAAI,SAAS;AAErC,KAAI,CAAC,MAAM,QAAQ,OAAO,CACxB,OAAM,IAAI,MACR,wEAAwE,OAAO,SAChF;AAGH,QAAO"}
1
+ {"version":3,"file":"migration-ts.mjs","names":["result: unknown"],"sources":["../../src/migration-ts.ts","../../src/runtime-detection.ts"],"sourcesContent":["/**\n * Utilities for reading/writing `migration.ts` files.\n *\n * Rendering migration.ts source is now the target's responsibility — the CLI\n * obtains source strings either from a class-flow planner's\n * `plan.renderTypeScript()` or from a descriptor-flow target's\n * `migrations.renderDescriptorTypeScript(descriptors, context)`. The helper\n * here is limited to file I/O: writing the returned source with the right\n * executable bit, probing for existence, and evaluating legacy descriptor-\n * flow files.\n */\n\nimport { stat, writeFile } from 'node:fs/promises';\nimport { join, resolve } from 'pathe';\n\nconst MIGRATION_TS_FILE = 'migration.ts';\n\n/**\n * Writes a pre-rendered `migration.ts` source string to the given package\n * directory. If the source begins with a shebang, the file is written with\n * executable permissions (0o755) so it can be run directly via\n * `./migration.ts` by the authoring class's `Migration.run(...)` guard.\n */\nexport async function writeMigrationTs(packageDir: string, content: string): Promise<void> {\n const isExecutable = content.startsWith('#!');\n await writeFile(\n join(packageDir, MIGRATION_TS_FILE),\n content,\n isExecutable ? { mode: 0o755 } : undefined,\n );\n}\n\n/**\n * Checks whether a migration.ts file exists in the package directory.\n */\nexport async function hasMigrationTs(packageDir: string): Promise<boolean> {\n try {\n const s = await stat(join(packageDir, MIGRATION_TS_FILE));\n return s.isFile();\n } catch {\n return false;\n }\n}\n\n/**\n * Evaluates a descriptor-flow migration.ts file by loading it via native\n * Node import. Returns the result of calling the default export (expected\n * to be a function returning an array of operation descriptors).\n *\n * Class-flow migration.ts files use a different shape — their default\n * export is a class that extends `Migration` — and are evaluated by the\n * target's `emit` capability, not this helper.\n *\n * Requires Node ≥24 for native TypeScript support.\n */\nexport async function evaluateMigrationTs(packageDir: string): Promise<readonly unknown[]> {\n const filePath = resolve(join(packageDir, MIGRATION_TS_FILE));\n\n try {\n await stat(filePath);\n } catch {\n throw new Error(`migration.ts not found at \"${filePath}\"`);\n }\n\n const mod = (await import(filePath)) as { default?: unknown };\n\n if (typeof mod.default !== 'function') {\n throw new Error(\n `migration.ts must export a default function returning an operation list. Got: ${typeof mod.default}`,\n );\n }\n\n const result: unknown = mod.default();\n\n if (!Array.isArray(result)) {\n throw new Error(\n `migration.ts default export must return an array of operations. Got: ${typeof result}`,\n );\n }\n\n return result;\n}\n","export type ScaffoldRuntime = 'node' | 'bun' | 'deno';\n\nexport function detectScaffoldRuntime(): ScaffoldRuntime {\n if (typeof (globalThis as { Bun?: unknown }).Bun !== 'undefined') return 'bun';\n if (typeof (globalThis as { Deno?: unknown }).Deno !== 'undefined') return 'deno';\n return 'node';\n}\n\nexport function shebangLineFor(runtime: ScaffoldRuntime): string {\n switch (runtime) {\n case 'bun':\n return '#!/usr/bin/env -S bun';\n case 'deno':\n return '#!/usr/bin/env -S deno run -A';\n case 'node':\n return '#!/usr/bin/env -S node';\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAeA,MAAM,oBAAoB;;;;;;;AAQ1B,eAAsB,iBAAiB,YAAoB,SAAgC;CACzF,MAAM,eAAe,QAAQ,WAAW,KAAK;AAC7C,OAAM,UACJ,KAAK,YAAY,kBAAkB,EACnC,SACA,eAAe,EAAE,MAAM,KAAO,GAAG,OAClC;;;;;AAMH,eAAsB,eAAe,YAAsC;AACzE,KAAI;AAEF,UADU,MAAM,KAAK,KAAK,YAAY,kBAAkB,CAAC,EAChD,QAAQ;SACX;AACN,SAAO;;;;;;;;;;;;;;AAeX,eAAsB,oBAAoB,YAAiD;CACzF,MAAM,WAAW,QAAQ,KAAK,YAAY,kBAAkB,CAAC;AAE7D,KAAI;AACF,QAAM,KAAK,SAAS;SACd;AACN,QAAM,IAAI,MAAM,8BAA8B,SAAS,GAAG;;CAG5D,MAAM,MAAO,MAAM,OAAO;AAE1B,KAAI,OAAO,IAAI,YAAY,WACzB,OAAM,IAAI,MACR,iFAAiF,OAAO,IAAI,UAC7F;CAGH,MAAMA,SAAkB,IAAI,SAAS;AAErC,KAAI,CAAC,MAAM,QAAQ,OAAO,CACxB,OAAM,IAAI,MACR,wEAAwE,OAAO,SAChF;AAGH,QAAO;;;;;AC9ET,SAAgB,wBAAyC;AACvD,KAAI,OAAQ,WAAiC,QAAQ,YAAa,QAAO;AACzE,KAAI,OAAQ,WAAkC,SAAS,YAAa,QAAO;AAC3E,QAAO;;AAGT,SAAgB,eAAe,SAAkC;AAC/D,SAAQ,SAAR;EACE,KAAK,MACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,KAAK,OACH,QAAO"}