@prisma-next/migration-tools 0.4.0-dev.6 → 0.4.0-dev.8

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 (34) hide show
  1. package/dist/attestation-DnebS4XZ.mjs +64 -0
  2. package/dist/attestation-DnebS4XZ.mjs.map +1 -0
  3. package/dist/{constants-DARNL_LD.mjs → constants-BRi0X7B_.mjs} +1 -1
  4. package/dist/{constants-DARNL_LD.mjs.map → constants-BRi0X7B_.mjs.map} +1 -1
  5. package/dist/exports/attestation.d.mts +1 -1
  6. package/dist/exports/attestation.mjs +3 -63
  7. package/dist/exports/constants.mjs +1 -1
  8. package/dist/exports/dag.d.mts +1 -1
  9. package/dist/exports/dag.mjs +1 -1
  10. package/dist/exports/io.d.mts +14 -2
  11. package/dist/exports/io.d.mts.map +1 -1
  12. package/dist/exports/io.mjs +2 -2
  13. package/dist/exports/migration-ts.d.mts +27 -18
  14. package/dist/exports/migration-ts.d.mts.map +1 -1
  15. package/dist/exports/migration-ts.mjs +39 -85
  16. package/dist/exports/migration-ts.mjs.map +1 -1
  17. package/dist/exports/migration.d.mts +35 -6
  18. package/dist/exports/migration.d.mts.map +1 -1
  19. package/dist/exports/migration.mjs +83 -29
  20. package/dist/exports/migration.mjs.map +1 -1
  21. package/dist/exports/types.d.mts +1 -1
  22. package/dist/{io-BO18-Evu.mjs → io-Cun81AIZ.mjs} +24 -3
  23. package/dist/io-Cun81AIZ.mjs.map +1 -0
  24. package/dist/{types-DXjq7Fum.d.mts → types-D2uX4ql7.d.mts} +1 -1
  25. package/dist/{types-DXjq7Fum.d.mts.map → types-D2uX4ql7.d.mts.map} +1 -1
  26. package/package.json +4 -4
  27. package/src/exports/io.ts +1 -0
  28. package/src/exports/migration-ts.ts +3 -6
  29. package/src/io.ts +26 -1
  30. package/src/migration-base.ts +123 -35
  31. package/src/migration-ts.ts +27 -144
  32. package/src/runtime-detection.ts +18 -0
  33. package/dist/exports/attestation.mjs.map +0 -1
  34. package/dist/io-BO18-Evu.mjs.map +0 -1
@@ -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-DARNL_LD.mjs.map
10
+ //# sourceMappingURL=constants-BRi0X7B_.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants-DARNL_LD.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"}
@@ -1,4 +1,4 @@
1
- import { l as MigrationManifest, u as MigrationOps } from "../types-DXjq7Fum.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 {
@@ -1,64 +1,4 @@
1
- import { i as writeMigrationManifest, n as readMigrationPackage } from "../io-BO18-Evu.mjs";
2
- import { createHash } from "node:crypto";
1
+ import "../io-Cun81AIZ.mjs";
2
+ import { n as computeMigrationId, r as verifyMigration, t as attestMigration } from "../attestation-DnebS4XZ.mjs";
3
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 { attestMigration, computeMigrationId, verifyMigration };
64
- //# sourceMappingURL=attestation.mjs.map
4
+ export { attestMigration, computeMigrationId, verifyMigration };
@@ -1,3 +1,3 @@
1
- import { t as EMPTY_CONTRACT_HASH } from "../constants-DARNL_LD.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-DXjq7Fum.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
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-DARNL_LD.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-DXjq7Fum.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-BO18-Evu.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 emit, 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 emit (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 emit, 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 emit (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 emit, 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":"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"}
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"}
@@ -1,3 +1,5 @@
1
+ import { MigrationPlan, MigrationPlanOperation } from "@prisma-next/framework-components/control";
2
+
1
3
  //#region src/migration-base.d.ts
2
4
  interface MigrationMeta {
3
5
  readonly from: string;
@@ -5,14 +7,41 @@ interface MigrationMeta {
5
7
  readonly kind?: 'regular' | 'baseline';
6
8
  readonly labels?: readonly string[];
7
9
  }
8
- declare abstract class Migration<TOperation = unknown> {
9
- abstract plan(): TOperation[];
10
- describe(): MigrationMeta | undefined;
10
+ /**
11
+ * Base class for class-flow migrations.
12
+ *
13
+ * A `Migration` subclass is itself a `MigrationPlan`: CLI commands and the
14
+ * runner can consume it directly via `targetId`, `operations`, `origin`, and
15
+ * `destination`. The manifest-shaped inputs come from `describe()`, which
16
+ * every migration must implement — `migration.json` is required for a
17
+ * migration to be valid.
18
+ */
19
+ declare abstract class Migration<TOperation extends MigrationPlanOperation = MigrationPlanOperation> implements MigrationPlan {
20
+ abstract readonly targetId: string;
21
+ /**
22
+ * Ordered list of operations this migration performs.
23
+ *
24
+ * Implemented as a getter so that subclasses can either precompute the list
25
+ * in their constructor or build it lazily per access.
26
+ */
27
+ abstract get operations(): readonly TOperation[];
28
+ /**
29
+ * Metadata inputs used to build `migration.json` and to derive the plan's
30
+ * origin/destination identities. Every migration must provide this —
31
+ * omitting it would produce an invalid on-disk migration package.
32
+ */
33
+ abstract describe(): MigrationMeta;
34
+ get origin(): {
35
+ readonly storageHash: string;
36
+ } | null;
37
+ get destination(): {
38
+ readonly storageHash: string;
39
+ };
11
40
  /**
12
41
  * Entrypoint guard for migration files. When called at module scope,
13
- * detects whether the file is being run directly (e.g. `tsx migration.ts`)
14
- * and if so, serializes the migration plan to `ops.json` (and optionally
15
- * `migration.json`) in the same directory. When the file is imported by
42
+ * detects whether the file is being run directly (e.g. `node migration.ts`)
43
+ * and if so, serializes the migration plan to `ops.json` and
44
+ * `migration.json` in the same directory. When the file is imported by
16
45
  * another module, this is a no-op.
17
46
  *
18
47
  * Usage (at module scope, after the class definition):
@@ -1 +1 @@
1
- {"version":3,"file":"migration.d.mts","names":[],"sources":["../../src/migration-base.ts"],"sourcesContent":[],"mappings":";UAKiB,aAAA;EAAA,SAAA,IAAA,EAAA,MAAa;EAcR,SAAA,EAAA,EAAS,MAAA;EACZ,SAAA,IAAA,CAAA,EAAA,SAAA,GAAA,UAAA;EAEL,SAAA,MAAA,CAAA,EAAA,SAAA,MAAA,EAAA;;AAiByD,uBApBjD,SAoBiD,CAAA,aAAA,OAAA,CAAA,CAAA;mBAnBpD;cAEL;;;;;;;;;;;;;;8DAiBgD"}
1
+ {"version":3,"file":"migration.d.mts","names":[],"sources":["../../src/migration-base.ts"],"sourcesContent":[],"mappings":";;;UAaiB,aAAA;;EAAA,SAAA,EAAA,EAAA,MAAa;EAuBR,SAAA,IAAS,CAAA,EAAA,SAAA,GAAA,UAAA;EAAoB,SAAA,MAAA,CAAA,EAAA,SAAA,MAAA,EAAA;;;;;;;;;;;uBAA7B,6BAA6B,yBAAyB,mCAC/D;;;;;;;;sCAUyB;;;;;;uBAOf;;;;;;;;;;;;;;;;;;;;8DA4BuC"}