@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.
- package/dist/attestation-DnebS4XZ.mjs +64 -0
- package/dist/attestation-DnebS4XZ.mjs.map +1 -0
- package/dist/{constants-DARNL_LD.mjs → constants-BRi0X7B_.mjs} +1 -1
- package/dist/{constants-DARNL_LD.mjs.map → constants-BRi0X7B_.mjs.map} +1 -1
- package/dist/exports/attestation.d.mts +1 -1
- package/dist/exports/attestation.mjs +3 -63
- package/dist/exports/constants.mjs +1 -1
- package/dist/exports/dag.d.mts +1 -1
- package/dist/exports/dag.mjs +1 -1
- package/dist/exports/io.d.mts +14 -2
- package/dist/exports/io.d.mts.map +1 -1
- package/dist/exports/io.mjs +2 -2
- package/dist/exports/migration-ts.d.mts +27 -18
- package/dist/exports/migration-ts.d.mts.map +1 -1
- package/dist/exports/migration-ts.mjs +39 -85
- package/dist/exports/migration-ts.mjs.map +1 -1
- package/dist/exports/migration.d.mts +35 -6
- package/dist/exports/migration.d.mts.map +1 -1
- package/dist/exports/migration.mjs +83 -29
- package/dist/exports/migration.mjs.map +1 -1
- package/dist/exports/types.d.mts +1 -1
- package/dist/{io-BO18-Evu.mjs → io-Cun81AIZ.mjs} +24 -3
- package/dist/io-Cun81AIZ.mjs.map +1 -0
- package/dist/{types-DXjq7Fum.d.mts → types-D2uX4ql7.d.mts} +1 -1
- package/dist/{types-DXjq7Fum.d.mts.map → types-D2uX4ql7.d.mts.map} +1 -1
- package/package.json +4 -4
- package/src/exports/io.ts +1 -0
- package/src/exports/migration-ts.ts +3 -6
- package/src/io.ts +26 -1
- package/src/migration-base.ts +123 -35
- package/src/migration-ts.ts +27 -144
- package/src/runtime-detection.ts +18 -0
- package/dist/exports/attestation.mjs.map +0 -1
- 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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants-
|
|
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,64 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import "../io-Cun81AIZ.mjs";
|
|
2
|
+
import { n as computeMigrationId, r as verifyMigration, t as attestMigration } from "../attestation-DnebS4XZ.mjs";
|
|
3
3
|
|
|
4
|
-
|
|
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 };
|
package/dist/exports/dag.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as MigrationChainEntry, s as MigrationGraph, t as AttestedMigrationBundle } from "../types-
|
|
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;
|
package/dist/exports/dag.mjs
CHANGED
|
@@ -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-
|
|
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
|
package/dist/exports/io.d.mts
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
|
-
import { l as MigrationManifest, r as BaseMigrationBundle, u as MigrationOps } from "../types-
|
|
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;
|
|
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"}
|
package/dist/exports/io.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as
|
|
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
|
-
*
|
|
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
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
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
|
|
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
|
|
26
|
-
* Returns the result of calling the default export (expected
|
|
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
|
-
|
|
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":"
|
|
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,
|
|
2
|
+
import { join, resolve } from "pathe";
|
|
3
3
|
|
|
4
4
|
//#region src/migration-ts.ts
|
|
5
5
|
/**
|
|
6
|
-
* Utilities for
|
|
6
|
+
* Utilities for reading/writing `migration.ts` files.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
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
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
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
|
|
67
|
-
const
|
|
68
|
-
|
|
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
|
|
104
|
-
* Returns the result of calling the default export (expected
|
|
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
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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. `
|
|
14
|
-
* and if so, serializes the migration plan to `ops.json`
|
|
15
|
-
* `migration.json`
|
|
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":"
|
|
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"}
|