@prisma-next/migration-tools 0.5.0-dev.6 → 0.5.0-dev.61

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 (91) hide show
  1. package/README.md +34 -22
  2. package/dist/{constants-BRi0X7B_.mjs → constants-BQEHsaEx.mjs} +1 -1
  3. package/dist/{constants-BRi0X7B_.mjs.map → constants-BQEHsaEx.mjs.map} +1 -1
  4. package/dist/errors-CfmjBeK0.mjs +272 -0
  5. package/dist/errors-CfmjBeK0.mjs.map +1 -0
  6. package/dist/exports/constants.mjs +1 -1
  7. package/dist/exports/errors.d.mts +63 -0
  8. package/dist/exports/errors.d.mts.map +1 -0
  9. package/dist/exports/errors.mjs +3 -0
  10. package/dist/exports/graph.d.mts +2 -0
  11. package/dist/exports/graph.mjs +1 -0
  12. package/dist/exports/hash.d.mts +52 -0
  13. package/dist/exports/hash.d.mts.map +1 -0
  14. package/dist/exports/hash.mjs +3 -0
  15. package/dist/exports/invariants.d.mts +24 -0
  16. package/dist/exports/invariants.d.mts.map +1 -0
  17. package/dist/exports/invariants.mjs +4 -0
  18. package/dist/exports/io.d.mts +7 -6
  19. package/dist/exports/io.d.mts.map +1 -1
  20. package/dist/exports/io.mjs +162 -2
  21. package/dist/exports/io.mjs.map +1 -0
  22. package/dist/exports/metadata.d.mts +2 -0
  23. package/dist/exports/metadata.mjs +1 -0
  24. package/dist/exports/migration-graph.d.mts +124 -0
  25. package/dist/exports/migration-graph.d.mts.map +1 -0
  26. package/dist/exports/migration-graph.mjs +526 -0
  27. package/dist/exports/migration-graph.mjs.map +1 -0
  28. package/dist/exports/migration-ts.mjs +1 -1
  29. package/dist/exports/migration.d.mts +15 -14
  30. package/dist/exports/migration.d.mts.map +1 -1
  31. package/dist/exports/migration.mjs +69 -41
  32. package/dist/exports/migration.mjs.map +1 -1
  33. package/dist/exports/package.d.mts +2 -0
  34. package/dist/exports/package.mjs +1 -0
  35. package/dist/exports/refs.mjs +2 -2
  36. package/dist/graph-BHPv-9Gl.d.mts +28 -0
  37. package/dist/graph-BHPv-9Gl.d.mts.map +1 -0
  38. package/dist/hash-BARZdVgW.mjs +76 -0
  39. package/dist/hash-BARZdVgW.mjs.map +1 -0
  40. package/dist/invariants-30VA65sB.mjs +42 -0
  41. package/dist/invariants-30VA65sB.mjs.map +1 -0
  42. package/dist/metadata-BP1cmU7Z.d.mts +50 -0
  43. package/dist/metadata-BP1cmU7Z.d.mts.map +1 -0
  44. package/dist/op-schema-DZKFua46.mjs +14 -0
  45. package/dist/op-schema-DZKFua46.mjs.map +1 -0
  46. package/dist/package-5HCCg0z-.d.mts +21 -0
  47. package/dist/package-5HCCg0z-.d.mts.map +1 -0
  48. package/package.json +31 -14
  49. package/src/errors.ts +210 -17
  50. package/src/exports/errors.ts +7 -0
  51. package/src/exports/graph.ts +1 -0
  52. package/src/exports/hash.ts +2 -0
  53. package/src/exports/invariants.ts +1 -0
  54. package/src/exports/io.ts +1 -1
  55. package/src/exports/metadata.ts +1 -0
  56. package/src/exports/{dag.ts → migration-graph.ts} +3 -2
  57. package/src/exports/migration.ts +0 -1
  58. package/src/exports/package.ts +1 -0
  59. package/src/graph-ops.ts +57 -30
  60. package/src/graph.ts +25 -0
  61. package/src/hash.ts +91 -0
  62. package/src/invariants.ts +45 -0
  63. package/src/io.ts +57 -31
  64. package/src/metadata.ts +41 -0
  65. package/src/migration-base.ts +97 -56
  66. package/src/migration-graph.ts +676 -0
  67. package/src/op-schema.ts +11 -0
  68. package/src/package.ts +18 -0
  69. package/dist/attestation-BnzTb0Qp.mjs +0 -65
  70. package/dist/attestation-BnzTb0Qp.mjs.map +0 -1
  71. package/dist/errors-BmiSgz1j.mjs +0 -160
  72. package/dist/errors-BmiSgz1j.mjs.map +0 -1
  73. package/dist/exports/attestation.d.mts +0 -37
  74. package/dist/exports/attestation.d.mts.map +0 -1
  75. package/dist/exports/attestation.mjs +0 -4
  76. package/dist/exports/dag.d.mts +0 -51
  77. package/dist/exports/dag.d.mts.map +0 -1
  78. package/dist/exports/dag.mjs +0 -386
  79. package/dist/exports/dag.mjs.map +0 -1
  80. package/dist/exports/types.d.mts +0 -35
  81. package/dist/exports/types.d.mts.map +0 -1
  82. package/dist/exports/types.mjs +0 -3
  83. package/dist/io-Cd6GLyjK.mjs +0 -153
  84. package/dist/io-Cd6GLyjK.mjs.map +0 -1
  85. package/dist/types-DyGXcWWp.d.mts +0 -71
  86. package/dist/types-DyGXcWWp.d.mts.map +0 -1
  87. package/src/attestation.ts +0 -81
  88. package/src/dag.ts +0 -426
  89. package/src/exports/attestation.ts +0 -2
  90. package/src/exports/types.ts +0 -10
  91. package/src/types.ts +0 -66
@@ -1,7 +1,8 @@
1
- import { a as MigrationManifest, o as MigrationOps, t as MigrationBundle } from "../types-DyGXcWWp.mjs";
1
+ import { n as MigrationMetadata } from "../metadata-BP1cmU7Z.mjs";
2
+ import { n as MigrationPackage, t as MigrationOps } from "../package-5HCCg0z-.mjs";
2
3
 
3
4
  //#region src/io.d.ts
4
- declare function writeMigrationPackage(dir: string, manifest: MigrationManifest, ops: MigrationOps): Promise<void>;
5
+ declare function writeMigrationPackage(dir: string, metadata: MigrationMetadata, ops: MigrationOps): Promise<void>;
5
6
  /**
6
7
  * Copy a list of files into `destDir`, optionally renaming each one.
7
8
  *
@@ -17,11 +18,11 @@ declare function copyFilesWithRename(destDir: string, files: readonly {
17
18
  readonly sourcePath: string;
18
19
  readonly destName: string;
19
20
  }[]): Promise<void>;
20
- declare function writeMigrationManifest(dir: string, manifest: MigrationManifest): Promise<void>;
21
+ declare function writeMigrationMetadata(dir: string, metadata: MigrationMetadata): Promise<void>;
21
22
  declare function writeMigrationOps(dir: string, ops: MigrationOps): Promise<void>;
22
- declare function readMigrationPackage(dir: string): Promise<MigrationBundle>;
23
- declare function readMigrationsDir(migrationsRoot: string): Promise<readonly MigrationBundle[]>;
23
+ declare function readMigrationPackage(dir: string): Promise<MigrationPackage>;
24
+ declare function readMigrationsDir(migrationsRoot: string): Promise<readonly MigrationPackage[]>;
24
25
  declare function formatMigrationDirName(timestamp: Date, slug: string): string;
25
26
  //#endregion
26
- export { copyFilesWithRename, formatMigrationDirName, readMigrationPackage, readMigrationsDir, writeMigrationManifest, writeMigrationOps, writeMigrationPackage };
27
+ export { copyFilesWithRename, formatMigrationDirName, readMigrationPackage, readMigrationsDir, writeMigrationMetadata, writeMigrationOps, writeMigrationPackage };
27
28
  //# 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;;AAJH;;;;;AA+BA;AAaA;AAOA;AAIA;AAkEA;AAiCgB,iBA3HM,mBAAA,CA2H4B,OAAI,EAAA,MAAA,EAAA,KAAA,EAAA,SAAA;;;MAxHnD;iBAUmB,sBAAA,wBAEV,oBACT;iBAImB,iBAAA,mBAAoC,eAAe;iBAInD,oBAAA,eAAmC,QAAQ;iBAkE3C,iBAAA,0BAEnB,iBAAiB;iBA+BJ,sBAAA,YAAkC"}
1
+ {"version":3,"file":"io.d.mts","names":[],"sources":["../../src/io.ts"],"sourcesContent":[],"mappings":";;;;iBAsDsB,qBAAA,wBAEV,wBACL,eACJ;;AAJH;;;;;AAiCA;AAaA;AAOA;AAIA;AA4FA;AAiCgB,iBArJM,mBAAA,CAqJ4B,OAAI,EAAA,MAAA,EAAA,KAAA,EAAA,SAAA;;;MAlJnD;iBAUmB,sBAAA,wBAEV,oBACT;iBAImB,iBAAA,mBAAoC,eAAe;iBAInD,oBAAA,eAAmC,QAAQ;iBA4F3C,iBAAA,0BAEnB,iBAAiB;iBA+BJ,sBAAA,YAAkC"}
@@ -1,3 +1,163 @@
1
- import { a as writeMigrationManifest, i as readMigrationsDir, n as formatMigrationDirName, o as writeMigrationOps, r as readMigrationPackage, s as writeMigrationPackage, t as copyFilesWithRename } from "../io-Cd6GLyjK.mjs";
1
+ import { b as errorProvidedInvariantsMismatch, c as errorInvalidJson, g as errorMissingFile, h as errorMigrationHashMismatch, l as errorInvalidManifest, m as errorInvalidSlug, o as errorInvalidDestName, r as errorDirectoryExists } from "../errors-CfmjBeK0.mjs";
2
+ import { n as verifyMigrationHash } from "../hash-BARZdVgW.mjs";
3
+ import { t as deriveProvidedInvariants } from "../invariants-30VA65sB.mjs";
4
+ import { n as MigrationOpsSchema } from "../op-schema-DZKFua46.mjs";
5
+ import { basename, dirname, join } from "pathe";
6
+ import { copyFile, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
7
+ import { type } from "arktype";
2
8
 
3
- export { copyFilesWithRename, formatMigrationDirName, readMigrationPackage, readMigrationsDir, writeMigrationManifest, writeMigrationOps, writeMigrationPackage };
9
+ //#region src/io.ts
10
+ const MANIFEST_FILE = "migration.json";
11
+ const OPS_FILE = "ops.json";
12
+ const MAX_SLUG_LENGTH = 64;
13
+ function hasErrnoCode(error, code) {
14
+ return error instanceof Error && error.code === code;
15
+ }
16
+ const MigrationHintsSchema = type({
17
+ used: "string[]",
18
+ applied: "string[]",
19
+ plannerVersion: "string"
20
+ });
21
+ const MigrationMetadataSchema = type({
22
+ "+": "reject",
23
+ from: "string > 0 | null",
24
+ to: "string",
25
+ migrationHash: "string",
26
+ fromContract: "object | null",
27
+ toContract: "object",
28
+ hints: MigrationHintsSchema,
29
+ labels: "string[]",
30
+ providedInvariants: "string[]",
31
+ "authorship?": type({
32
+ "author?": "string",
33
+ "email?": "string"
34
+ }),
35
+ "signature?": type({
36
+ keyId: "string",
37
+ value: "string"
38
+ }).or("null"),
39
+ createdAt: "string"
40
+ });
41
+ async function writeMigrationPackage(dir, metadata, ops) {
42
+ await mkdir(dirname(dir), { recursive: true });
43
+ try {
44
+ await mkdir(dir);
45
+ } catch (error) {
46
+ if (hasErrnoCode(error, "EEXIST")) throw errorDirectoryExists(dir);
47
+ throw error;
48
+ }
49
+ await writeFile(join(dir, MANIFEST_FILE), JSON.stringify(metadata, null, 2), { flag: "wx" });
50
+ await writeFile(join(dir, OPS_FILE), JSON.stringify(ops, null, 2), { flag: "wx" });
51
+ }
52
+ /**
53
+ * Copy a list of files into `destDir`, optionally renaming each one.
54
+ *
55
+ * The destination directory is created (with `recursive: true`) if it
56
+ * does not already exist. Each source path is copied byte-for-byte into
57
+ * `destDir/<destName>`; missing sources throw `ENOENT`. The helper is
58
+ * intentionally generic: callers own the list of files (e.g. a contract
59
+ * emitter's emitted output) and the naming convention (e.g. renaming
60
+ * the destination contract to `end-contract.*` and the source contract
61
+ * to `start-contract.*`).
62
+ */
63
+ async function copyFilesWithRename(destDir, files) {
64
+ await mkdir(destDir, { recursive: true });
65
+ for (const file of files) {
66
+ if (basename(file.destName) !== file.destName) throw errorInvalidDestName(file.destName);
67
+ await copyFile(file.sourcePath, join(destDir, file.destName));
68
+ }
69
+ }
70
+ async function writeMigrationMetadata(dir, metadata) {
71
+ await writeFile(join(dir, MANIFEST_FILE), `${JSON.stringify(metadata, null, 2)}\n`);
72
+ }
73
+ async function writeMigrationOps(dir, ops) {
74
+ await writeFile(join(dir, OPS_FILE), `${JSON.stringify(ops, null, 2)}\n`);
75
+ }
76
+ async function readMigrationPackage(dir) {
77
+ const manifestPath = join(dir, MANIFEST_FILE);
78
+ const opsPath = join(dir, OPS_FILE);
79
+ let manifestRaw;
80
+ try {
81
+ manifestRaw = await readFile(manifestPath, "utf-8");
82
+ } catch (error) {
83
+ if (hasErrnoCode(error, "ENOENT")) throw errorMissingFile(MANIFEST_FILE, dir);
84
+ throw error;
85
+ }
86
+ let opsRaw;
87
+ try {
88
+ opsRaw = await readFile(opsPath, "utf-8");
89
+ } catch (error) {
90
+ if (hasErrnoCode(error, "ENOENT")) throw errorMissingFile(OPS_FILE, dir);
91
+ throw error;
92
+ }
93
+ let metadata;
94
+ try {
95
+ metadata = JSON.parse(manifestRaw);
96
+ } catch (e) {
97
+ throw errorInvalidJson(manifestPath, e instanceof Error ? e.message : String(e));
98
+ }
99
+ let ops;
100
+ try {
101
+ ops = JSON.parse(opsRaw);
102
+ } catch (e) {
103
+ throw errorInvalidJson(opsPath, e instanceof Error ? e.message : String(e));
104
+ }
105
+ validateMetadata(metadata, manifestPath);
106
+ validateOps(ops, opsPath);
107
+ const derivedInvariants = deriveProvidedInvariants(ops);
108
+ if (!arraysEqual(metadata.providedInvariants, derivedInvariants)) throw errorProvidedInvariantsMismatch(manifestPath, metadata.providedInvariants, derivedInvariants);
109
+ const pkg = {
110
+ dirName: basename(dir),
111
+ dirPath: dir,
112
+ metadata,
113
+ ops
114
+ };
115
+ const verification = verifyMigrationHash(pkg);
116
+ if (!verification.ok) throw errorMigrationHashMismatch(dir, verification.storedHash, verification.computedHash);
117
+ return pkg;
118
+ }
119
+ function arraysEqual(a, b) {
120
+ if (a.length !== b.length) return false;
121
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
122
+ return true;
123
+ }
124
+ function validateMetadata(metadata, filePath) {
125
+ const result = MigrationMetadataSchema(metadata);
126
+ if (result instanceof type.errors) throw errorInvalidManifest(filePath, result.summary);
127
+ }
128
+ function validateOps(ops, filePath) {
129
+ const result = MigrationOpsSchema(ops);
130
+ if (result instanceof type.errors) throw errorInvalidManifest(filePath, result.summary);
131
+ }
132
+ async function readMigrationsDir(migrationsRoot) {
133
+ let entries;
134
+ try {
135
+ entries = await readdir(migrationsRoot);
136
+ } catch (error) {
137
+ if (hasErrnoCode(error, "ENOENT")) return [];
138
+ throw error;
139
+ }
140
+ const packages = [];
141
+ for (const entry of entries.sort()) {
142
+ const entryPath = join(migrationsRoot, entry);
143
+ if (!(await stat(entryPath)).isDirectory()) continue;
144
+ const manifestPath = join(entryPath, MANIFEST_FILE);
145
+ try {
146
+ await stat(manifestPath);
147
+ } catch {
148
+ continue;
149
+ }
150
+ packages.push(await readMigrationPackage(entryPath));
151
+ }
152
+ return packages;
153
+ }
154
+ function formatMigrationDirName(timestamp, slug) {
155
+ const sanitized = slug.toLowerCase().replace(/[^a-z0-9]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
156
+ if (sanitized.length === 0) throw errorInvalidSlug(slug);
157
+ const truncated = sanitized.slice(0, MAX_SLUG_LENGTH);
158
+ return `${timestamp.getUTCFullYear()}${String(timestamp.getUTCMonth() + 1).padStart(2, "0")}${String(timestamp.getUTCDate()).padStart(2, "0")}T${String(timestamp.getUTCHours()).padStart(2, "0")}${String(timestamp.getUTCMinutes()).padStart(2, "0")}_${truncated}`;
159
+ }
160
+
161
+ //#endregion
162
+ export { copyFilesWithRename, formatMigrationDirName, readMigrationPackage, readMigrationsDir, writeMigrationMetadata, writeMigrationOps, writeMigrationPackage };
163
+ //# sourceMappingURL=io.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"io.mjs","names":["manifestRaw: string","opsRaw: string","metadata: MigrationMetadata","ops: MigrationOps","pkg: MigrationPackage","entries: string[]","packages: MigrationPackage[]"],"sources":["../../src/io.ts"],"sourcesContent":["import { copyFile, mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';\nimport { type } from 'arktype';\nimport { basename, dirname, join } from 'pathe';\nimport {\n errorDirectoryExists,\n errorInvalidDestName,\n errorInvalidJson,\n errorInvalidManifest,\n errorInvalidSlug,\n errorMigrationHashMismatch,\n errorMissingFile,\n errorProvidedInvariantsMismatch,\n} from './errors';\nimport { verifyMigrationHash } from './hash';\nimport { deriveProvidedInvariants } from './invariants';\nimport type { MigrationMetadata } from './metadata';\nimport { MigrationOpsSchema } from './op-schema';\nimport type { MigrationOps, MigrationPackage } from './package';\n\nconst MANIFEST_FILE = 'migration.json';\nconst OPS_FILE = 'ops.json';\nconst MAX_SLUG_LENGTH = 64;\n\nfunction hasErrnoCode(error: unknown, code: string): boolean {\n return error instanceof Error && (error as { code?: string }).code === code;\n}\n\nconst MigrationHintsSchema = type({\n used: 'string[]',\n applied: 'string[]',\n plannerVersion: 'string',\n});\n\nconst MigrationMetadataSchema = type({\n '+': 'reject',\n from: 'string > 0 | null',\n to: 'string',\n migrationHash: 'string',\n fromContract: 'object | null',\n toContract: 'object',\n hints: MigrationHintsSchema,\n labels: 'string[]',\n providedInvariants: 'string[]',\n 'authorship?': type({\n 'author?': 'string',\n 'email?': 'string',\n }),\n 'signature?': type({\n keyId: 'string',\n value: 'string',\n }).or('null'),\n createdAt: 'string',\n});\n\nexport async function writeMigrationPackage(\n dir: string,\n metadata: MigrationMetadata,\n ops: MigrationOps,\n): Promise<void> {\n await mkdir(dirname(dir), { recursive: true });\n\n try {\n await mkdir(dir);\n } catch (error) {\n if (hasErrnoCode(error, 'EEXIST')) {\n throw errorDirectoryExists(dir);\n }\n throw error;\n }\n\n await writeFile(join(dir, MANIFEST_FILE), JSON.stringify(metadata, null, 2), {\n flag: 'wx',\n });\n await writeFile(join(dir, OPS_FILE), JSON.stringify(ops, null, 2), { flag: 'wx' });\n}\n\n/**\n * Copy a list of files into `destDir`, optionally renaming each one.\n *\n * The destination directory is created (with `recursive: true`) if it\n * does not already exist. Each source path is copied byte-for-byte into\n * `destDir/<destName>`; missing sources throw `ENOENT`. The helper is\n * intentionally generic: callers own the list of files (e.g. a contract\n * emitter's emitted output) and the naming convention (e.g. renaming\n * the destination contract to `end-contract.*` and the source contract\n * to `start-contract.*`).\n */\nexport async function copyFilesWithRename(\n destDir: string,\n files: readonly { readonly sourcePath: string; readonly destName: string }[],\n): Promise<void> {\n await mkdir(destDir, { recursive: true });\n for (const file of files) {\n if (basename(file.destName) !== file.destName) {\n throw errorInvalidDestName(file.destName);\n }\n await copyFile(file.sourcePath, join(destDir, file.destName));\n }\n}\n\nexport async function writeMigrationMetadata(\n dir: string,\n metadata: MigrationMetadata,\n): Promise<void> {\n await writeFile(join(dir, MANIFEST_FILE), `${JSON.stringify(metadata, null, 2)}\\n`);\n}\n\nexport async function writeMigrationOps(dir: string, ops: MigrationOps): Promise<void> {\n await writeFile(join(dir, OPS_FILE), `${JSON.stringify(ops, null, 2)}\\n`);\n}\n\nexport async function readMigrationPackage(dir: string): Promise<MigrationPackage> {\n const manifestPath = join(dir, MANIFEST_FILE);\n const opsPath = join(dir, OPS_FILE);\n\n let manifestRaw: string;\n try {\n manifestRaw = await readFile(manifestPath, 'utf-8');\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n throw errorMissingFile(MANIFEST_FILE, dir);\n }\n throw error;\n }\n\n let opsRaw: string;\n try {\n opsRaw = await readFile(opsPath, 'utf-8');\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n throw errorMissingFile(OPS_FILE, dir);\n }\n throw error;\n }\n\n let metadata: MigrationMetadata;\n try {\n metadata = JSON.parse(manifestRaw);\n } catch (e) {\n throw errorInvalidJson(manifestPath, e instanceof Error ? e.message : String(e));\n }\n\n let ops: MigrationOps;\n try {\n ops = JSON.parse(opsRaw);\n } catch (e) {\n throw errorInvalidJson(opsPath, e instanceof Error ? e.message : String(e));\n }\n\n validateMetadata(metadata, manifestPath);\n validateOps(ops, opsPath);\n\n // Re-derive before the hash check so format/duplicate diagnostics\n // fire with their dedicated codes rather than as a generic hash mismatch.\n const derivedInvariants = deriveProvidedInvariants(ops);\n if (!arraysEqual(metadata.providedInvariants, derivedInvariants)) {\n throw errorProvidedInvariantsMismatch(\n manifestPath,\n metadata.providedInvariants,\n derivedInvariants,\n );\n }\n\n const pkg: MigrationPackage = {\n dirName: basename(dir),\n dirPath: dir,\n metadata,\n ops,\n };\n\n const verification = verifyMigrationHash(pkg);\n if (!verification.ok) {\n throw errorMigrationHashMismatch(dir, verification.storedHash, verification.computedHash);\n }\n\n return pkg;\n}\n\nfunction arraysEqual(a: readonly string[], b: readonly string[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\nfunction validateMetadata(\n metadata: unknown,\n filePath: string,\n): asserts metadata is MigrationMetadata {\n const result = MigrationMetadataSchema(metadata);\n if (result instanceof type.errors) {\n throw errorInvalidManifest(filePath, result.summary);\n }\n}\n\nfunction validateOps(ops: unknown, filePath: string): asserts ops is MigrationOps {\n const result = MigrationOpsSchema(ops);\n if (result instanceof type.errors) {\n throw errorInvalidManifest(filePath, result.summary);\n }\n}\n\nexport async function readMigrationsDir(\n migrationsRoot: string,\n): Promise<readonly MigrationPackage[]> {\n let entries: string[];\n try {\n entries = await readdir(migrationsRoot);\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n return [];\n }\n throw error;\n }\n\n const packages: MigrationPackage[] = [];\n\n for (const entry of entries.sort()) {\n const entryPath = join(migrationsRoot, entry);\n const entryStat = await stat(entryPath);\n if (!entryStat.isDirectory()) continue;\n\n const manifestPath = join(entryPath, MANIFEST_FILE);\n try {\n await stat(manifestPath);\n } catch {\n continue; // skip non-migration directories\n }\n\n packages.push(await readMigrationPackage(entryPath));\n }\n\n return packages;\n}\n\nexport function formatMigrationDirName(timestamp: Date, slug: string): string {\n const sanitized = slug\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '_')\n .replace(/_+/g, '_')\n .replace(/^_|_$/g, '');\n\n if (sanitized.length === 0) {\n throw errorInvalidSlug(slug);\n }\n\n const truncated = sanitized.slice(0, MAX_SLUG_LENGTH);\n\n const y = timestamp.getUTCFullYear();\n const mo = String(timestamp.getUTCMonth() + 1).padStart(2, '0');\n const d = String(timestamp.getUTCDate()).padStart(2, '0');\n const h = String(timestamp.getUTCHours()).padStart(2, '0');\n const mi = String(timestamp.getUTCMinutes()).padStart(2, '0');\n\n return `${y}${mo}${d}T${h}${mi}_${truncated}`;\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { n as MigrationMetadata, t as MigrationHints } from "../metadata-BP1cmU7Z.mjs";
2
+ export { type MigrationHints, type MigrationMetadata };
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,124 @@
1
+ import { n as MigrationGraph, t as MigrationEdge } from "../graph-BHPv-9Gl.mjs";
2
+ import { n as MigrationPackage } from "../package-5HCCg0z-.mjs";
3
+
4
+ //#region src/migration-graph.d.ts
5
+ declare function reconstructGraph(packages: readonly MigrationPackage[]): MigrationGraph;
6
+ /**
7
+ * Find the shortest path from `fromHash` to `toHash` using BFS over the
8
+ * contract-hash graph. Returns the ordered list of edges, or null if no path
9
+ * exists. Returns an empty array when `fromHash === toHash` (no-op).
10
+ *
11
+ * Neighbor ordering is deterministic via the tie-break sort key:
12
+ * label priority → createdAt → to → migrationHash.
13
+ */
14
+ declare function findPath(graph: MigrationGraph, fromHash: string, toHash: string): readonly MigrationEdge[] | null;
15
+ /**
16
+ * Find the shortest path from `fromHash` to `toHash` whose edges collectively
17
+ * cover every invariant in `required`. Returns `null` when no such path exists
18
+ * (either `fromHash`→`toHash` is structurally unreachable, or every reachable
19
+ * path leaves at least one required invariant uncovered). When `required` is
20
+ * empty, delegates to `findPath` so the result is byte-identical for that case.
21
+ *
22
+ * Algorithm: BFS over `(node, coveredSubset)` states with state-level dedup.
23
+ * The covered subset is a `Set<string>` of invariant ids; the state's dedup
24
+ * key is `${node}\0${[...covered].sort().join('\0')}`. State keys distinguish
25
+ * distinct `(node, covered)` tuples regardless of node-name length because
26
+ * `\0` cannot appear in any invariant id (validation rejects whitespace and
27
+ * control chars at authoring time).
28
+ *
29
+ * Neighbour ordering when `required ≠ ∅`: edges covering ≥1 still-needed
30
+ * invariant come first, with `labelPriority → createdAt → to → migrationHash`
31
+ * as the secondary key. The heuristic steers BFS toward the satisfying path;
32
+ * correctness (shortest, deterministic) does not depend on it.
33
+ */
34
+ declare function findPathWithInvariants(graph: MigrationGraph, fromHash: string, toHash: string, required: ReadonlySet<string>): readonly MigrationEdge[] | null;
35
+ interface PathDecision {
36
+ readonly selectedPath: readonly MigrationEdge[];
37
+ readonly fromHash: string;
38
+ readonly toHash: string;
39
+ readonly alternativeCount: number;
40
+ readonly tieBreakReasons: readonly string[];
41
+ readonly refName?: string;
42
+ /** The caller-supplied required invariant set, sorted ascending. */
43
+ readonly requiredInvariants: readonly string[];
44
+ /**
45
+ * The subset of `requiredInvariants` actually covered by edges on
46
+ * `selectedPath`. Always a subset of `requiredInvariants` (when the path
47
+ * is satisfying, equal to it); always derived from `selectedPath`.
48
+ */
49
+ readonly satisfiedInvariants: readonly string[];
50
+ }
51
+ /**
52
+ * Outcome of {@link findPathWithDecision}. The pathfinder distinguishes
53
+ * three cases up front so callers don't re-derive structural reachability:
54
+ *
55
+ * - `ok` — a path covering `required` exists; `decision` carries the
56
+ * selection metadata and per-edge invariants.
57
+ * - `unreachable` — `from`→`to` has no structural path. Mapped by callers
58
+ * to the existing no-path / `NO_TARGET` diagnostic.
59
+ * - `unsatisfiable` — `from`→`to` is structurally reachable but no path
60
+ * covers every required invariant. `structuralPath` is the
61
+ * `findPath(graph, from, to)` result, included so callers don't have to
62
+ * recompute it when raising `MIGRATION.NO_INVARIANT_PATH`. `missing` is
63
+ * the subset of `required` that the structural path does *not* cover —
64
+ * correctly accounts for partial coverage when some required invariants
65
+ * are met by the fallback path. Only emitted when `required` is
66
+ * non-empty.
67
+ */
68
+ type FindPathOutcome = {
69
+ readonly kind: 'ok';
70
+ readonly decision: PathDecision;
71
+ } | {
72
+ readonly kind: 'unreachable';
73
+ } | {
74
+ readonly kind: 'unsatisfiable';
75
+ readonly structuralPath: readonly MigrationEdge[];
76
+ readonly missing: readonly string[];
77
+ };
78
+ /**
79
+ * Routing context for {@link findPathWithDecision}. Both fields are optional;
80
+ * `refName` is only used to decorate the resulting `PathDecision` for the
81
+ * JSON envelope, and `required` defaults to an empty set (purely structural
82
+ * routing). They are passed via a single options object so the call sites
83
+ * cannot silently swap two adjacent string parameters.
84
+ */
85
+ interface FindPathWithDecisionOptions {
86
+ readonly refName?: string;
87
+ readonly required?: ReadonlySet<string>;
88
+ }
89
+ /**
90
+ * Find the shortest path from `fromHash` to `toHash` and return structured
91
+ * path-decision metadata for machine-readable output. When `required` is
92
+ * non-empty, the returned path is the shortest one whose edges collectively
93
+ * cover every required invariant.
94
+ *
95
+ * The discriminated return type tells the caller *why* a path could not be
96
+ * found, so the CLI can pick the right structured error without re-running
97
+ * a structural BFS.
98
+ */
99
+ declare function findPathWithDecision(graph: MigrationGraph, fromHash: string, toHash: string, options?: FindPathWithDecisionOptions): FindPathOutcome;
100
+ /**
101
+ * Find all branch tips (nodes with no outgoing edges) reachable from
102
+ * `fromHash` via forward edges.
103
+ */
104
+ declare function findReachableLeaves(graph: MigrationGraph, fromHash: string): readonly string[];
105
+ /**
106
+ * Find the target contract hash of the migration graph reachable from
107
+ * EMPTY_CONTRACT_HASH. Returns `null` for a graph that has no target
108
+ * state (either empty, or containing only the root with no outgoing
109
+ * edges). Throws NO_INITIAL_MIGRATION if the graph has nodes but none
110
+ * originate from the empty hash, and AMBIGUOUS_TARGET if multiple
111
+ * branch tips exist.
112
+ */
113
+ declare function findLeaf(graph: MigrationGraph): string | null;
114
+ /**
115
+ * Find the latest migration entry by traversing from EMPTY_CONTRACT_HASH
116
+ * to the single target. Returns null for an empty graph.
117
+ * Throws AMBIGUOUS_TARGET if the graph has multiple branch tips.
118
+ */
119
+ declare function findLatestMigration(graph: MigrationGraph): MigrationEdge | null;
120
+ declare function detectCycles(graph: MigrationGraph): readonly string[][];
121
+ declare function detectOrphans(graph: MigrationGraph): readonly MigrationEdge[];
122
+ //#endregion
123
+ export { type PathDecision, detectCycles, detectOrphans, findLatestMigration, findLeaf, findPath, findPathWithDecision, findPathWithInvariants, findReachableLeaves, reconstructGraph };
124
+ //# sourceMappingURL=migration-graph.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration-graph.d.mts","names":[],"sources":["../../src/migration-graph.ts"],"sourcesContent":[],"mappings":";;;;iBAsCgB,gBAAA,oBAAoC,qBAAqB;;AAAzE;AAqFA;AAgDA;;;;;AA2FiB,iBA3ID,QAAA,CA4IkB,KAAA,EA3IzB,cA2IsC,EAAA,QAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAAA,SAxInC,aAwImC,EAAA,GAAA,IAAA;AAiC/C;AAgBA;AAeA;;;;;AA0LA;AAkBA;AAwCA;AAQA;AA8DA;;;;;;;;iBAtdgB,sBAAA,QACP,4DAGG,+BACA;UAsFK,YAAA;kCACiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAiCtB,eAAA;;qBACkC;;;;;oCAIN;;;;;;;;;;UAWvB,2BAAA;;sBAEK;;;;;;;;;;;;iBAaN,oBAAA,QACP,4DAGE,8BACR;;;;;iBAqLa,mBAAA,QAA2B;;;;;;;;;iBAkB3B,QAAA,QAAgB;;;;;;iBAwChB,mBAAA,QAA2B,iBAAiB;iBAQ5C,YAAA,QAAoB;iBA8DpB,aAAA,QAAqB,0BAA0B"}