@prisma-next/migration-tools 0.3.0-dev.84 → 0.3.0-dev.86
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/{errors-DdSjGRqx.mjs → errors-CqLiJwqA.mjs} +47 -9
- package/dist/errors-CqLiJwqA.mjs.map +1 -0
- package/dist/exports/attestation.d.mts +1 -1
- package/dist/exports/attestation.mjs +1 -1
- package/dist/exports/dag.d.mts +36 -17
- package/dist/exports/dag.d.mts.map +1 -1
- package/dist/exports/dag.mjs +206 -76
- package/dist/exports/dag.mjs.map +1 -1
- package/dist/exports/io.d.mts +3 -3
- package/dist/exports/io.d.mts.map +1 -1
- package/dist/exports/io.mjs +1 -1
- package/dist/exports/refs.d.mts +10 -0
- package/dist/exports/refs.d.mts.map +1 -0
- package/dist/exports/refs.mjs +73 -0
- package/dist/exports/refs.mjs.map +1 -0
- package/dist/exports/types.d.mts +2 -2
- package/dist/exports/types.mjs +13 -2
- package/dist/exports/types.mjs.map +1 -0
- package/dist/{io-Dx98-h0p.mjs → io-afog-e8J.mjs} +2 -3
- package/dist/io-afog-e8J.mjs.map +1 -0
- package/dist/types-9YQfIg6N.d.mts +96 -0
- package/dist/types-9YQfIg6N.d.mts.map +1 -0
- package/package.json +8 -4
- package/src/dag.ts +267 -107
- package/src/errors.ts +58 -7
- package/src/exports/dag.ts +3 -0
- package/src/exports/refs.ts +2 -0
- package/src/exports/types.ts +6 -1
- package/src/io.ts +4 -5
- package/src/refs.ts +102 -0
- package/src/types.ts +54 -7
- package/dist/errors-DdSjGRqx.mjs.map +0 -1
- package/dist/io-Dx98-h0p.mjs.map +0 -1
- package/dist/types-CUnzoaLY.d.mts +0 -56
- package/dist/types-CUnzoaLY.d.mts.map +0 -1
package/src/refs.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { type } from 'arktype';
|
|
3
|
+
import { dirname, join } from 'pathe';
|
|
4
|
+
import {
|
|
5
|
+
errorInvalidRefName,
|
|
6
|
+
errorInvalidRefs,
|
|
7
|
+
errorInvalidRefValue,
|
|
8
|
+
MigrationToolsError,
|
|
9
|
+
} from './errors';
|
|
10
|
+
|
|
11
|
+
export type Refs = Readonly<Record<string, string>>;
|
|
12
|
+
|
|
13
|
+
const REF_NAME_PATTERN = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\/[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/;
|
|
14
|
+
const REF_VALUE_PATTERN = /^sha256:(empty|[0-9a-f]{64})$/;
|
|
15
|
+
|
|
16
|
+
export function validateRefName(name: string): boolean {
|
|
17
|
+
if (name.length === 0) return false;
|
|
18
|
+
if (name.includes('..')) return false;
|
|
19
|
+
if (name.includes('//')) return false;
|
|
20
|
+
if (name.startsWith('.')) return false;
|
|
21
|
+
return REF_NAME_PATTERN.test(name);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function validateRefValue(value: string): boolean {
|
|
25
|
+
return REF_VALUE_PATTERN.test(value);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const RefsSchema = type('Record<string, string>').narrow((refs, ctx) => {
|
|
29
|
+
for (const [key, value] of Object.entries(refs)) {
|
|
30
|
+
if (!validateRefName(key)) return ctx.mustBe(`valid ref names (invalid: "${key}")`);
|
|
31
|
+
if (!validateRefValue(value))
|
|
32
|
+
return ctx.mustBe(`valid contract hashes (invalid value for "${key}": "${value}")`);
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export async function readRefs(refsPath: string): Promise<Refs> {
|
|
38
|
+
let raw: string;
|
|
39
|
+
try {
|
|
40
|
+
raw = await readFile(refsPath, 'utf-8');
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let parsed: unknown;
|
|
49
|
+
try {
|
|
50
|
+
parsed = JSON.parse(raw);
|
|
51
|
+
} catch {
|
|
52
|
+
throw errorInvalidRefs(refsPath, 'Failed to parse as JSON');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const result = RefsSchema(parsed);
|
|
56
|
+
if (result instanceof type.errors) {
|
|
57
|
+
throw errorInvalidRefs(refsPath, result.summary);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function writeRefs(refsPath: string, refs: Refs): Promise<void> {
|
|
64
|
+
for (const [key, value] of Object.entries(refs)) {
|
|
65
|
+
if (!validateRefName(key)) {
|
|
66
|
+
throw errorInvalidRefName(key);
|
|
67
|
+
}
|
|
68
|
+
if (!validateRefValue(value)) {
|
|
69
|
+
throw errorInvalidRefValue(value);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const sorted = Object.fromEntries(Object.entries(refs).sort(([a], [b]) => a.localeCompare(b)));
|
|
74
|
+
|
|
75
|
+
const dir = dirname(refsPath);
|
|
76
|
+
await mkdir(dir, { recursive: true });
|
|
77
|
+
|
|
78
|
+
const tmpPath = join(dir, `.refs.json.${Date.now()}.tmp`);
|
|
79
|
+
await writeFile(tmpPath, `${JSON.stringify(sorted, null, 2)}\n`);
|
|
80
|
+
await rename(tmpPath, refsPath);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function resolveRef(refs: Refs, name: string): string {
|
|
84
|
+
if (!validateRefName(name)) {
|
|
85
|
+
throw errorInvalidRefName(name);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const hash = refs[name];
|
|
89
|
+
if (hash === undefined) {
|
|
90
|
+
throw new MigrationToolsError('MIGRATION.UNKNOWN_REF', `Unknown ref "${name}"`, {
|
|
91
|
+
why: `No ref named "${name}" exists in refs.json.`,
|
|
92
|
+
fix: `Available refs: ${Object.keys(refs).join(', ') || '(none)'}. Create a ref with: set the "${name}" key in migrations/refs.json.`,
|
|
93
|
+
details: { refName: name, availableRefs: Object.keys(refs) },
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!validateRefValue(hash)) {
|
|
98
|
+
throw errorInvalidRefValue(hash);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return hash;
|
|
102
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -8,11 +8,12 @@ export interface MigrationHints {
|
|
|
8
8
|
readonly planningStrategy: string;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Shared fields for all migration manifests (draft and attested).
|
|
13
|
+
*/
|
|
14
|
+
interface MigrationManifestBase {
|
|
12
15
|
readonly from: string;
|
|
13
16
|
readonly to: string;
|
|
14
|
-
readonly migrationId: string | null;
|
|
15
|
-
readonly parentMigrationId: string | null;
|
|
16
17
|
readonly kind: 'regular' | 'baseline';
|
|
17
18
|
readonly fromContract: ContractIR | null;
|
|
18
19
|
readonly toContract: ContractIR;
|
|
@@ -23,20 +24,59 @@ export interface MigrationManifest {
|
|
|
23
24
|
readonly createdAt: string;
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
/**
|
|
28
|
+
* A draft migration that has been planned but not yet attested.
|
|
29
|
+
* Draft migrations have `migrationId: null` and are excluded from
|
|
30
|
+
* graph reconstruction and apply.
|
|
31
|
+
*/
|
|
32
|
+
export interface DraftMigrationManifest extends MigrationManifestBase {
|
|
33
|
+
readonly migrationId: null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* An attested migration with a content-addressed migrationId.
|
|
38
|
+
* Only attested migrations participate in the migration graph.
|
|
39
|
+
*/
|
|
40
|
+
export interface AttestedMigrationManifest extends MigrationManifestBase {
|
|
41
|
+
readonly migrationId: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Union of draft and attested manifests. This is what the on-disk
|
|
46
|
+
* format represents — `migrationId` is `null` for drafts, a string
|
|
47
|
+
* for attested migrations.
|
|
48
|
+
*/
|
|
49
|
+
export type MigrationManifest = DraftMigrationManifest | AttestedMigrationManifest;
|
|
50
|
+
|
|
26
51
|
export type MigrationOps = readonly MigrationPlanOperation[];
|
|
27
52
|
|
|
28
|
-
|
|
53
|
+
/**
|
|
54
|
+
* An on-disk migration directory containing a manifest and operations.
|
|
55
|
+
* The manifest may be draft or attested.
|
|
56
|
+
*/
|
|
57
|
+
export interface MigrationBundle {
|
|
29
58
|
readonly dirName: string;
|
|
30
59
|
readonly dirPath: string;
|
|
31
60
|
readonly manifest: MigrationManifest;
|
|
32
61
|
readonly ops: MigrationOps;
|
|
33
62
|
}
|
|
34
63
|
|
|
64
|
+
/**
|
|
65
|
+
* A bundle known to be attested (migrationId is a string).
|
|
66
|
+
* Use this after filtering bundles to attested-only.
|
|
67
|
+
*/
|
|
68
|
+
export interface AttestedMigrationBundle extends MigrationBundle {
|
|
69
|
+
readonly manifest: AttestedMigrationManifest;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* An entry in the migration graph. Only attested migrations appear in the
|
|
74
|
+
* graph, so `migrationId` is always a string.
|
|
75
|
+
*/
|
|
35
76
|
export interface MigrationChainEntry {
|
|
36
77
|
readonly from: string;
|
|
37
78
|
readonly to: string;
|
|
38
|
-
readonly migrationId: string
|
|
39
|
-
readonly parentMigrationId: string | null;
|
|
79
|
+
readonly migrationId: string;
|
|
40
80
|
readonly dirName: string;
|
|
41
81
|
readonly createdAt: string;
|
|
42
82
|
readonly labels: readonly string[];
|
|
@@ -47,5 +87,12 @@ export interface MigrationGraph {
|
|
|
47
87
|
readonly forwardChain: ReadonlyMap<string, readonly MigrationChainEntry[]>;
|
|
48
88
|
readonly reverseChain: ReadonlyMap<string, readonly MigrationChainEntry[]>;
|
|
49
89
|
readonly migrationById: ReadonlyMap<string, MigrationChainEntry>;
|
|
50
|
-
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Type guard that narrows a MigrationBundle to an AttestedMigrationBundle.
|
|
94
|
+
* Use with `.filter(isAttested)` to get a typed array of attested bundles.
|
|
95
|
+
*/
|
|
96
|
+
export function isAttested(bundle: MigrationBundle): bundle is AttestedMigrationBundle {
|
|
97
|
+
return typeof bundle.manifest.migrationId === 'string';
|
|
51
98
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"errors-DdSjGRqx.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Structured error for migration tooling operations.\n *\n * Follows the NAMESPACE.SUBCODE convention from ADR 027. All codes live under\n * the MIGRATION namespace. These are tooling-time errors (file I/O, attestation,\n * migration-chain reconstruction), distinct from the runtime MIGRATION.* codes for apply-time\n * failures (PRECHECK_FAILED, POSTCHECK_FAILED, etc.).\n *\n * Fields:\n * - code: Stable machine-readable code (MIGRATION.SUBCODE)\n * - category: Always 'MIGRATION'\n * - why: Explains the cause in plain language\n * - fix: Actionable remediation step\n * - details: Machine-readable structured data for agents\n */\nexport class MigrationToolsError extends Error {\n readonly code: string;\n readonly category = 'MIGRATION' as const;\n readonly why: string;\n readonly fix: string;\n readonly details: Record<string, unknown> | undefined;\n\n constructor(\n code: string,\n summary: string,\n options: {\n readonly why: string;\n readonly fix: string;\n readonly details?: Record<string, unknown>;\n },\n ) {\n super(summary);\n this.name = 'MigrationToolsError';\n this.code = code;\n this.why = options.why;\n this.fix = options.fix;\n this.details = options.details;\n }\n\n static is(error: unknown): error is MigrationToolsError {\n if (!(error instanceof Error)) return false;\n const candidate = error as MigrationToolsError;\n return candidate.name === 'MigrationToolsError' && typeof candidate.code === 'string';\n }\n}\n\nexport function errorDirectoryExists(dir: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.DIR_EXISTS', 'Migration directory already exists', {\n why: `The directory \"${dir}\" already exists. Each migration must have a unique directory.`,\n fix: 'Use --name to pick a different name, or delete the existing directory and re-run.',\n details: { dir },\n });\n}\n\nexport function errorMissingFile(file: string, dir: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.FILE_MISSING', `Missing ${file}`, {\n why: `Expected \"${file}\" in \"${dir}\" but the file does not exist.`,\n fix: 'Ensure the migration directory contains both migration.json and ops.json. If the directory is corrupt, delete it and re-run migration plan.',\n details: { file, dir },\n });\n}\n\nexport function errorInvalidJson(filePath: string, parseError: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_JSON', 'Invalid JSON in migration file', {\n why: `Failed to parse \"${filePath}\": ${parseError}`,\n fix: 'Fix the JSON syntax error, or delete the migration directory and re-run migration plan.',\n details: { filePath, parseError },\n });\n}\n\nexport function errorInvalidManifest(filePath: string, reason: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_MANIFEST', 'Invalid migration manifest', {\n why: `Manifest at \"${filePath}\" is invalid: ${reason}`,\n fix: 'Ensure the manifest has all required fields (from, to, kind, toContract). If corrupt, delete and re-plan.',\n details: { filePath, reason },\n });\n}\n\nexport function errorInvalidSlug(slug: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_NAME', 'Invalid migration name', {\n why: `The slug \"${slug}\" contains no valid characters after sanitization (only a-z, 0-9 are kept).`,\n fix: 'Provide a name with at least one alphanumeric character, e.g. --name add_users.',\n details: { slug },\n });\n}\n\nexport function errorSelfLoop(dirName: string, hash: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.SELF_LOOP', 'Self-loop in migration graph', {\n why: `Migration \"${dirName}\" has from === to === \"${hash}\". A migration must transition between two different contract states.`,\n fix: 'Delete the invalid migration directory and re-run migration plan.',\n details: { dirName, hash },\n });\n}\n\nexport function errorAmbiguousLeaf(leaves: readonly string[]): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.AMBIGUOUS_LEAF', 'Ambiguous migration graph', {\n why: `Multiple leaf nodes found: ${leaves.join(', ')}. The migration graph has diverged — this typically happens when two developers plan migrations from the same starting point.`,\n fix: 'Delete one of the conflicting migration directories, then re-run `migration plan` to re-plan it from the remaining branch. Or use --from <hash> to explicitly select a starting point.',\n details: { leaves },\n });\n}\n\nexport function errorNoRoot(nodes: readonly string[]): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.NO_ROOT', 'Migration graph has no root', {\n why: `No root migration found in the migration graph (nodes: ${nodes.join(', ')}). Every migration references a parentMigrationId that does not exist, or the graph contains a cycle in parent pointers.`,\n fix: 'Inspect the migrations directory for corrupted migration.json files. Exactly one migration must have parentMigrationId set to null (the first migration).',\n details: { nodes },\n });\n}\n\nexport function errorDuplicateMigrationId(migrationId: string): MigrationToolsError {\n return new MigrationToolsError(\n 'MIGRATION.DUPLICATE_MIGRATION_ID',\n 'Duplicate migrationId in migration graph',\n {\n why: `Multiple migrations share migrationId \"${migrationId}\". This makes parent-chain reconstruction ambiguous and unsafe.`,\n fix: 'Regenerate one of the conflicting migrations so each migrationId is unique, then re-run migration commands.',\n details: { migrationId },\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAeA,IAAa,sBAAb,cAAyC,MAAM;CAC7C,AAAS;CACT,AAAS,WAAW;CACpB,AAAS;CACT,AAAS;CACT,AAAS;CAET,YACE,MACA,SACA,SAKA;AACA,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,OAAO;AACZ,OAAK,MAAM,QAAQ;AACnB,OAAK,MAAM,QAAQ;AACnB,OAAK,UAAU,QAAQ;;CAGzB,OAAO,GAAG,OAA8C;AACtD,MAAI,EAAE,iBAAiB,OAAQ,QAAO;EACtC,MAAM,YAAY;AAClB,SAAO,UAAU,SAAS,yBAAyB,OAAO,UAAU,SAAS;;;AAIjF,SAAgB,qBAAqB,KAAkC;AACrE,QAAO,IAAI,oBAAoB,wBAAwB,sCAAsC;EAC3F,KAAK,kBAAkB,IAAI;EAC3B,KAAK;EACL,SAAS,EAAE,KAAK;EACjB,CAAC;;AAGJ,SAAgB,iBAAiB,MAAc,KAAkC;AAC/E,QAAO,IAAI,oBAAoB,0BAA0B,WAAW,QAAQ;EAC1E,KAAK,aAAa,KAAK,QAAQ,IAAI;EACnC,KAAK;EACL,SAAS;GAAE;GAAM;GAAK;EACvB,CAAC;;AAGJ,SAAgB,iBAAiB,UAAkB,YAAyC;AAC1F,QAAO,IAAI,oBAAoB,0BAA0B,kCAAkC;EACzF,KAAK,oBAAoB,SAAS,KAAK;EACvC,KAAK;EACL,SAAS;GAAE;GAAU;GAAY;EAClC,CAAC;;AAGJ,SAAgB,qBAAqB,UAAkB,QAAqC;AAC1F,QAAO,IAAI,oBAAoB,8BAA8B,8BAA8B;EACzF,KAAK,gBAAgB,SAAS,gBAAgB;EAC9C,KAAK;EACL,SAAS;GAAE;GAAU;GAAQ;EAC9B,CAAC;;AAGJ,SAAgB,iBAAiB,MAAmC;AAClE,QAAO,IAAI,oBAAoB,0BAA0B,0BAA0B;EACjF,KAAK,aAAa,KAAK;EACvB,KAAK;EACL,SAAS,EAAE,MAAM;EAClB,CAAC;;AAGJ,SAAgB,cAAc,SAAiB,MAAmC;AAChF,QAAO,IAAI,oBAAoB,uBAAuB,gCAAgC;EACpF,KAAK,cAAc,QAAQ,yBAAyB,KAAK;EACzD,KAAK;EACL,SAAS;GAAE;GAAS;GAAM;EAC3B,CAAC;;AAGJ,SAAgB,mBAAmB,QAAgD;AACjF,QAAO,IAAI,oBAAoB,4BAA4B,6BAA6B;EACtF,KAAK,8BAA8B,OAAO,KAAK,KAAK,CAAC;EACrD,KAAK;EACL,SAAS,EAAE,QAAQ;EACpB,CAAC;;AAGJ,SAAgB,YAAY,OAA+C;AACzE,QAAO,IAAI,oBAAoB,qBAAqB,+BAA+B;EACjF,KAAK,0DAA0D,MAAM,KAAK,KAAK,CAAC;EAChF,KAAK;EACL,SAAS,EAAE,OAAO;EACnB,CAAC;;AAGJ,SAAgB,0BAA0B,aAA0C;AAClF,QAAO,IAAI,oBACT,oCACA,4CACA;EACE,KAAK,0CAA0C,YAAY;EAC3D,KAAK;EACL,SAAS,EAAE,aAAa;EACzB,CACF"}
|
package/dist/io-Dx98-h0p.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"io-Dx98-h0p.mjs","names":["manifestRaw: string","opsRaw: string","manifest: MigrationManifest","ops: MigrationOps","entries: string[]","packages: MigrationPackage[]"],"sources":["../src/io.ts"],"sourcesContent":["import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';\nimport { type } from 'arktype';\nimport { basename, dirname, join } from 'pathe';\nimport {\n errorDirectoryExists,\n errorInvalidJson,\n errorInvalidManifest,\n errorInvalidSlug,\n errorMissingFile,\n} from './errors';\nimport type { MigrationManifest, MigrationOps, MigrationPackage } from './types';\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 planningStrategy: 'string',\n});\n\nconst MigrationManifestSchema = type({\n from: 'string',\n to: 'string',\n migrationId: 'string | null',\n parentMigrationId: 'string | null',\n kind: \"'regular' | 'baseline'\",\n fromContract: 'object | null',\n toContract: 'object',\n hints: MigrationHintsSchema,\n labels: '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\nconst MigrationOpSchema = type({\n id: 'string',\n label: 'string',\n operationClass: \"'additive' | 'widening' | 'destructive'\",\n});\n\n// Intentionally shallow: operation-specific payload validation is owned by planner/runner layers.\nconst MigrationOpsSchema = MigrationOpSchema.array();\n\nexport async function writeMigrationPackage(\n dir: string,\n manifest: MigrationManifest,\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(manifest, null, 2), { flag: 'wx' });\n await writeFile(join(dir, OPS_FILE), JSON.stringify(ops, null, 2), { flag: 'wx' });\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 manifest: MigrationManifest;\n try {\n manifest = 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 validateManifest(manifest, manifestPath);\n validateOps(ops, opsPath);\n\n return {\n dirName: basename(dir),\n dirPath: dir,\n manifest,\n ops,\n };\n}\n\nfunction validateManifest(\n manifest: unknown,\n filePath: string,\n): asserts manifest is MigrationManifest {\n const result = MigrationManifestSchema(manifest);\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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"}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { ContractIR } from "@prisma-next/contract/ir";
|
|
2
|
-
import { MigrationPlanOperation } from "@prisma-next/core-control-plane/types";
|
|
3
|
-
|
|
4
|
-
//#region src/types.d.ts
|
|
5
|
-
interface MigrationHints {
|
|
6
|
-
readonly used: readonly string[];
|
|
7
|
-
readonly applied: readonly string[];
|
|
8
|
-
readonly plannerVersion: string;
|
|
9
|
-
readonly planningStrategy: string;
|
|
10
|
-
}
|
|
11
|
-
interface MigrationManifest {
|
|
12
|
-
readonly from: string;
|
|
13
|
-
readonly to: string;
|
|
14
|
-
readonly migrationId: string | null;
|
|
15
|
-
readonly parentMigrationId: string | null;
|
|
16
|
-
readonly kind: 'regular' | 'baseline';
|
|
17
|
-
readonly fromContract: ContractIR | null;
|
|
18
|
-
readonly toContract: ContractIR;
|
|
19
|
-
readonly hints: MigrationHints;
|
|
20
|
-
readonly labels: readonly string[];
|
|
21
|
-
readonly authorship?: {
|
|
22
|
-
readonly author?: string;
|
|
23
|
-
readonly email?: string;
|
|
24
|
-
};
|
|
25
|
-
readonly signature?: {
|
|
26
|
-
readonly keyId: string;
|
|
27
|
-
readonly value: string;
|
|
28
|
-
} | null;
|
|
29
|
-
readonly createdAt: string;
|
|
30
|
-
}
|
|
31
|
-
type MigrationOps = readonly MigrationPlanOperation[];
|
|
32
|
-
interface MigrationPackage {
|
|
33
|
-
readonly dirName: string;
|
|
34
|
-
readonly dirPath: string;
|
|
35
|
-
readonly manifest: MigrationManifest;
|
|
36
|
-
readonly ops: MigrationOps;
|
|
37
|
-
}
|
|
38
|
-
interface MigrationChainEntry {
|
|
39
|
-
readonly from: string;
|
|
40
|
-
readonly to: string;
|
|
41
|
-
readonly migrationId: string | null;
|
|
42
|
-
readonly parentMigrationId: string | null;
|
|
43
|
-
readonly dirName: string;
|
|
44
|
-
readonly createdAt: string;
|
|
45
|
-
readonly labels: readonly string[];
|
|
46
|
-
}
|
|
47
|
-
interface MigrationGraph {
|
|
48
|
-
readonly nodes: ReadonlySet<string>;
|
|
49
|
-
readonly forwardChain: ReadonlyMap<string, readonly MigrationChainEntry[]>;
|
|
50
|
-
readonly reverseChain: ReadonlyMap<string, readonly MigrationChainEntry[]>;
|
|
51
|
-
readonly migrationById: ReadonlyMap<string, MigrationChainEntry>;
|
|
52
|
-
readonly childrenByParentId: ReadonlyMap<string | null, readonly MigrationChainEntry[]>;
|
|
53
|
-
}
|
|
54
|
-
//#endregion
|
|
55
|
-
export { MigrationOps as a, MigrationManifest as i, MigrationGraph as n, MigrationPackage as o, MigrationHints as r, MigrationChainEntry as t };
|
|
56
|
-
//# sourceMappingURL=types-CUnzoaLY.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types-CUnzoaLY.d.mts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":";;;;UAGiB,cAAA;;EAAA,SAAA,OAAA,EAAc,SAAA,MAAA,EAAA;EAOd,SAAA,cAAiB,EAAA,MAAA;EAMT,SAAA,gBAAA,EAAA,MAAA;;AAEP,UARD,iBAAA,CAQC;EAAc,SAAA,IAAA,EAAA,MAAA;EAOpB,SAAA,EAAA,EAAA,MAAY;EAEP,SAAA,WAAgB,EAAA,MAAA,GAGZ,IAAA;EAIJ,SAAA,iBAAmB,EAAA,MAAA,GAAA,IAAA;EAUnB,SAAA,IAAA,EAAA,SAAc,GAAA,UAAA;EACb,SAAA,YAAA,EA7BO,UA6BP,GAAA,IAAA;EACoC,SAAA,UAAA,EA7B/B,UA6B+B;EAA7B,SAAA,KAAA,EA5BP,cA4BO;EAC6B,SAAA,MAAA,EAAA,SAAA,MAAA,EAAA;EAA7B,SAAA,UAAA,CAAA,EAAA;IACqB,SAAA,MAAA,CAAA,EAAA,MAAA;IAApB,SAAA,KAAA,CAAA,EAAA,MAAA;EACyC,CAAA;EAApC,SAAA,SAAA,CAAA,EAAA;IAAW,SAAA,KAAA,EAAA,MAAA;;;;;KAxB9B,YAAA,YAAwB;UAEnB,gBAAA;;;qBAGI;gBACL;;UAGC,mBAAA;;;;;;;;;UAUA,cAAA;kBACC;yBACO,6BAA6B;yBAC7B,6BAA6B;0BAC5B,oBAAoB;+BACf,oCAAoC"}
|