@prisma-next/migration-tools 0.5.0-dev.62 → 0.5.0-dev.64
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/{constants-BQEHsaEx.mjs → constants-B87kJAGj.mjs} +1 -1
- package/dist/{constants-BQEHsaEx.mjs.map → constants-B87kJAGj.mjs.map} +1 -1
- package/dist/{errors-CfmjBeK0.mjs → errors-DQsXvidG.mjs} +22 -2
- package/dist/errors-DQsXvidG.mjs.map +1 -0
- package/dist/exports/constants.mjs +1 -1
- package/dist/exports/errors.d.mts.map +1 -1
- package/dist/exports/errors.mjs +1 -1
- package/dist/exports/graph.d.mts +1 -1
- package/dist/exports/hash.d.mts +3 -3
- package/dist/exports/hash.d.mts.map +1 -1
- package/dist/exports/hash.mjs +1 -1
- package/dist/exports/invariants.d.mts +1 -1
- package/dist/exports/invariants.mjs +2 -2
- package/dist/exports/io.d.mts +40 -5
- package/dist/exports/io.d.mts.map +1 -1
- package/dist/exports/io.mjs +4 -162
- package/dist/exports/metadata.d.mts +1 -1
- package/dist/exports/migration-graph.d.mts +3 -3
- package/dist/exports/migration-graph.d.mts.map +1 -1
- package/dist/exports/migration-graph.mjs +2 -2
- package/dist/exports/migration-graph.mjs.map +1 -1
- package/dist/exports/migration.d.mts +3 -3
- package/dist/exports/migration.d.mts.map +1 -1
- package/dist/exports/migration.mjs +4 -4
- package/dist/exports/package.d.mts +3 -2
- package/dist/exports/refs.mjs +1 -1
- package/dist/exports/spaces.d.mts +447 -0
- package/dist/exports/spaces.d.mts.map +1 -0
- package/dist/exports/spaces.mjs +433 -0
- package/dist/exports/spaces.mjs.map +1 -0
- package/dist/{graph-BHPv-9Gl.d.mts → graph-Czaj8O2q.d.mts} +1 -1
- package/dist/{graph-BHPv-9Gl.d.mts.map → graph-Czaj8O2q.d.mts.map} +1 -1
- package/dist/{hash-BARZdVgW.mjs → hash-G0bAfIGh.mjs} +2 -2
- package/dist/hash-G0bAfIGh.mjs.map +1 -0
- package/dist/{invariants-30VA65sB.mjs → invariants-4Avb_Yhy.mjs} +2 -2
- package/dist/{invariants-30VA65sB.mjs.map → invariants-4Avb_Yhy.mjs.map} +1 -1
- package/dist/io-CDJaWGbt.mjs +207 -0
- package/dist/io-CDJaWGbt.mjs.map +1 -0
- package/dist/metadata-CSjwljJx.d.mts +2 -0
- package/dist/{op-schema-DZKFua46.mjs → op-schema-BiF1ZYqH.mjs} +1 -1
- package/dist/{op-schema-DZKFua46.mjs.map → op-schema-BiF1ZYqH.mjs.map} +1 -1
- package/dist/package-B3Yl6DTr.d.mts +21 -0
- package/dist/package-B3Yl6DTr.d.mts.map +1 -0
- package/package.json +8 -4
- package/src/concatenate-space-apply-inputs.ts +90 -0
- package/src/detect-space-contract-drift.ts +95 -0
- package/src/emit-pinned-space-artefacts.ts +89 -0
- package/src/errors.ts +35 -0
- package/src/exports/io.ts +1 -0
- package/src/exports/package.ts +2 -1
- package/src/exports/spaces.ts +36 -0
- package/src/hash.ts +2 -2
- package/src/io.ts +71 -16
- package/src/metadata.ts +1 -41
- package/src/migration-graph.ts +2 -2
- package/src/package.ts +14 -11
- package/src/plan-all-spaces.ts +80 -0
- package/src/read-pinned-contract-hash.ts +77 -0
- package/src/space-layout.ts +55 -0
- package/src/verify-contract-spaces.ts +276 -0
- package/dist/errors-CfmjBeK0.mjs.map +0 -1
- package/dist/exports/io.mjs.map +0 -1
- package/dist/hash-BARZdVgW.mjs.map +0 -1
- package/dist/metadata-BP1cmU7Z.d.mts +0 -50
- package/dist/metadata-BP1cmU7Z.d.mts.map +0 -1
- package/dist/package-5HCCg0z-.d.mts +0 -21
- package/dist/package-5HCCg0z-.d.mts.map +0 -1
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { readdir, stat } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'pathe';
|
|
3
|
+
import { MANIFEST_FILE } from './io';
|
|
4
|
+
import { APP_SPACE_ID } from './space-layout';
|
|
5
|
+
|
|
6
|
+
function hasErrnoCode(error: unknown, code: string): boolean {
|
|
7
|
+
return error instanceof Error && (error as { code?: string }).code === code;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* List the per-space pinned subdirectories under
|
|
12
|
+
* `<projectRoot>/migrations/`. Returns space-id directory names (sorted
|
|
13
|
+
* alphabetically) — i.e. any non-dot-prefixed subdirectory whose root
|
|
14
|
+
* does **not** contain a `migration.json` manifest. The manifest is the
|
|
15
|
+
* structural marker of a user-authored migration directory (see
|
|
16
|
+
* `readMigrationsDir` in `./io`); directory names themselves belong to
|
|
17
|
+
* the user and are not part of the contract.
|
|
18
|
+
*
|
|
19
|
+
* Returns `[]` if the migrations directory does not exist (greenfield
|
|
20
|
+
* project).
|
|
21
|
+
*
|
|
22
|
+
* Reads only the user's repo. **No descriptor import.** The caller
|
|
23
|
+
* (verifier) feeds the result into {@link verifyContractSpaces} alongside
|
|
24
|
+
* the loaded-space set and the marker rows.
|
|
25
|
+
*
|
|
26
|
+
* @see specs/framework-mechanism.spec.md § 4 — Verifier (steps 5–6).
|
|
27
|
+
*/
|
|
28
|
+
export async function listPinnedSpaceDirectories(
|
|
29
|
+
projectMigrationsDir: string,
|
|
30
|
+
): Promise<readonly string[]> {
|
|
31
|
+
let entries: { readonly name: string; readonly isDirectory: boolean }[];
|
|
32
|
+
try {
|
|
33
|
+
const dirents = await readdir(projectMigrationsDir, { withFileTypes: true });
|
|
34
|
+
entries = dirents.map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (hasErrnoCode(error, 'ENOENT')) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const namedCandidates = entries
|
|
43
|
+
.filter((e) => e.isDirectory)
|
|
44
|
+
.map((e) => e.name)
|
|
45
|
+
.filter((name) => !name.startsWith('.'))
|
|
46
|
+
.sort();
|
|
47
|
+
|
|
48
|
+
const manifestChecks = await Promise.all(
|
|
49
|
+
namedCandidates.map(async (name) => {
|
|
50
|
+
try {
|
|
51
|
+
await stat(join(projectMigrationsDir, name, MANIFEST_FILE));
|
|
52
|
+
return { name, isMigrationDir: true };
|
|
53
|
+
} catch (error) {
|
|
54
|
+
if (hasErrnoCode(error, 'ENOENT')) {
|
|
55
|
+
return { name, isMigrationDir: false };
|
|
56
|
+
}
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
return manifestChecks.filter((c) => !c.isMigrationDir).map((c) => c.name);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Pinned head value (`(hash, invariants)`) for one contract space.
|
|
67
|
+
* The verifier compares this against the marker row for the same space
|
|
68
|
+
* to detect drift between the user-emitted artefacts and the live DB
|
|
69
|
+
* marker.
|
|
70
|
+
*/
|
|
71
|
+
export interface SpacePinnedHashRecord {
|
|
72
|
+
readonly hash: string;
|
|
73
|
+
readonly invariants: readonly string[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Marker row read from `prisma_contract.marker` (one per `space`).
|
|
78
|
+
* Caller resolves these via the family runtime's marker reader (T1.1)
|
|
79
|
+
* before invoking {@link verifyContractSpaces}.
|
|
80
|
+
*/
|
|
81
|
+
export interface SpaceMarkerRecord {
|
|
82
|
+
readonly hash: string;
|
|
83
|
+
readonly invariants: readonly string[];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface VerifyContractSpacesInputs {
|
|
87
|
+
/**
|
|
88
|
+
* Set of contract spaces the project declares: `'app'` plus each
|
|
89
|
+
* extension space in `extensionPacks`. The caller's discovery path
|
|
90
|
+
* never reads the extension descriptor module — it walks the
|
|
91
|
+
* `extensionPacks` configuration in `prisma-next.config.ts` for the
|
|
92
|
+
* space ids.
|
|
93
|
+
*/
|
|
94
|
+
readonly loadedSpaces: ReadonlySet<string>;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Pinned per-space subdirectories observed under
|
|
98
|
+
* `<projectRoot>/migrations/`. Resolved via
|
|
99
|
+
* {@link listPinnedSpaceDirectories}.
|
|
100
|
+
*/
|
|
101
|
+
readonly pinnedDirsOnDisk: readonly string[];
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Pinned head ref per space, keyed by space id. Caller reads
|
|
105
|
+
* `<projectRoot>/migrations/<space-id>/contract.json` and
|
|
106
|
+
* `refs/head.json` (or, for app-space if its pinned shape ever moves
|
|
107
|
+
* under `migrations/`, the equivalent files) to construct this map.
|
|
108
|
+
* Spaces with no pinned dir on disk simply omit a map entry.
|
|
109
|
+
*/
|
|
110
|
+
readonly pinnedHashesBySpace: ReadonlyMap<string, SpacePinnedHashRecord>;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Marker rows keyed by `space`. Caller reads them from the
|
|
114
|
+
* `prisma_contract.marker` table.
|
|
115
|
+
*/
|
|
116
|
+
readonly markerRowsBySpace: ReadonlyMap<string, SpaceMarkerRecord>;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export type SpaceVerifierViolation =
|
|
120
|
+
| {
|
|
121
|
+
readonly kind: 'declaredButUnmigrated';
|
|
122
|
+
readonly spaceId: string;
|
|
123
|
+
readonly remediation: string;
|
|
124
|
+
}
|
|
125
|
+
| {
|
|
126
|
+
readonly kind: 'orphanMarker';
|
|
127
|
+
readonly spaceId: string;
|
|
128
|
+
readonly remediation: string;
|
|
129
|
+
}
|
|
130
|
+
| {
|
|
131
|
+
readonly kind: 'orphanPinnedDir';
|
|
132
|
+
readonly spaceId: string;
|
|
133
|
+
readonly remediation: string;
|
|
134
|
+
}
|
|
135
|
+
| {
|
|
136
|
+
readonly kind: 'hashMismatch';
|
|
137
|
+
readonly spaceId: string;
|
|
138
|
+
readonly pinnedHash: string;
|
|
139
|
+
readonly markerHash: string;
|
|
140
|
+
readonly remediation: string;
|
|
141
|
+
}
|
|
142
|
+
| {
|
|
143
|
+
readonly kind: 'invariantsMismatch';
|
|
144
|
+
readonly spaceId: string;
|
|
145
|
+
readonly pinnedInvariants: readonly string[];
|
|
146
|
+
readonly markerInvariants: readonly string[];
|
|
147
|
+
readonly remediation: string;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export type VerifyContractSpacesResult =
|
|
151
|
+
| { readonly ok: true }
|
|
152
|
+
| { readonly ok: false; readonly violations: readonly SpaceVerifierViolation[] };
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Pure structural verifier for the per-space mechanism. Aggregates the
|
|
156
|
+
* three orphan / missing checks (FR6 cases a–c) plus per-space hash and
|
|
157
|
+
* invariant comparison.
|
|
158
|
+
*
|
|
159
|
+
* Algorithm (sub-spec § 4):
|
|
160
|
+
*
|
|
161
|
+
* - For every extension space declared in `loadedSpaces` (`'app'`
|
|
162
|
+
* excluded — its pinned `contract.json` lives at the project root):
|
|
163
|
+
* - If no pinned dir on disk → `declaredButUnmigrated`.
|
|
164
|
+
* - Else if `markerRowsBySpace` lacks an entry → no violation here;
|
|
165
|
+
* the live-DB compare in step 8 (out of scope of this helper) is
|
|
166
|
+
* where the absence shows up.
|
|
167
|
+
* - Else compare marker hash / invariants vs. pinned hash /
|
|
168
|
+
* invariants → `hashMismatch` / `invariantsMismatch` on drift.
|
|
169
|
+
* - For every pinned dir on disk that is not in `loadedSpaces` →
|
|
170
|
+
* `orphanPinnedDir`.
|
|
171
|
+
* - For every marker row whose `space` is not in `loadedSpaces` →
|
|
172
|
+
* `orphanMarker`. The app-space marker is always loaded (`'app'` is
|
|
173
|
+
* in `loadedSpaces` by definition).
|
|
174
|
+
*
|
|
175
|
+
* Output is deterministic (NFR6): violations are sorted first by `kind`
|
|
176
|
+
* (`declaredButUnmigrated` → `orphanMarker` → `orphanPinnedDir` →
|
|
177
|
+
* `hashMismatch` → `invariantsMismatch`) then by `spaceId`. Two callers
|
|
178
|
+
* passing equivalent inputs see byte-identical violation lists.
|
|
179
|
+
*
|
|
180
|
+
* Synchronous, pure, no I/O. **Does not import the extension descriptor**
|
|
181
|
+
* (the inputs are pre-resolved by the caller). This is the property
|
|
182
|
+
* AC-15 / AC-26 ("verifier reads only the user repo, not
|
|
183
|
+
* `node_modules`") locks in.
|
|
184
|
+
*
|
|
185
|
+
* @see specs/framework-mechanism.spec.md § 4 — Verifier (T1.5).
|
|
186
|
+
*/
|
|
187
|
+
export function verifyContractSpaces(
|
|
188
|
+
inputs: VerifyContractSpacesInputs,
|
|
189
|
+
): VerifyContractSpacesResult {
|
|
190
|
+
const violations: SpaceVerifierViolation[] = [];
|
|
191
|
+
|
|
192
|
+
for (const spaceId of [...inputs.loadedSpaces].sort()) {
|
|
193
|
+
if (spaceId === APP_SPACE_ID) continue;
|
|
194
|
+
|
|
195
|
+
if (!inputs.pinnedDirsOnDisk.includes(spaceId)) {
|
|
196
|
+
violations.push({
|
|
197
|
+
kind: 'declaredButUnmigrated',
|
|
198
|
+
spaceId,
|
|
199
|
+
remediation: `Extension '${spaceId}' is declared in extensionPacks but has not been emitted; run \`prisma-next migrate\`.`,
|
|
200
|
+
});
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const pinned = inputs.pinnedHashesBySpace.get(spaceId);
|
|
205
|
+
const marker = inputs.markerRowsBySpace.get(spaceId);
|
|
206
|
+
if (!pinned || !marker) {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (pinned.hash !== marker.hash) {
|
|
211
|
+
violations.push({
|
|
212
|
+
kind: 'hashMismatch',
|
|
213
|
+
spaceId,
|
|
214
|
+
pinnedHash: pinned.hash,
|
|
215
|
+
markerHash: marker.hash,
|
|
216
|
+
remediation: `Marker row for space '${spaceId}' is keyed at ${marker.hash}, but the pinned ${join('migrations', spaceId, 'contract.json')} resolves to ${pinned.hash}. Run \`prisma-next db update\` to advance the database, or \`prisma-next migrate\` if the descriptor was bumped without re-emitting.`,
|
|
217
|
+
});
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const pinnedInvariants = [...pinned.invariants].sort();
|
|
222
|
+
const markerInvariants = new Set(marker.invariants);
|
|
223
|
+
const missing = pinnedInvariants.filter((id) => !markerInvariants.has(id));
|
|
224
|
+
if (missing.length > 0) {
|
|
225
|
+
violations.push({
|
|
226
|
+
kind: 'invariantsMismatch',
|
|
227
|
+
spaceId,
|
|
228
|
+
pinnedInvariants,
|
|
229
|
+
markerInvariants: [...marker.invariants].sort(),
|
|
230
|
+
remediation: `Marker row for space '${spaceId}' is missing invariants [${missing.map((s) => JSON.stringify(s)).join(', ')}]. Run \`prisma-next db update\` to apply the corresponding data-transform migrations.`,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
for (const dir of [...inputs.pinnedDirsOnDisk].sort()) {
|
|
236
|
+
if (!inputs.loadedSpaces.has(dir)) {
|
|
237
|
+
violations.push({
|
|
238
|
+
kind: 'orphanPinnedDir',
|
|
239
|
+
spaceId: dir,
|
|
240
|
+
remediation: `Orphan pinned directory \`${join('migrations', dir)}/\` for an extension not in extensionPacks; remove the directory or re-add the extension.`,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
for (const space of [...inputs.markerRowsBySpace.keys()].sort()) {
|
|
246
|
+
if (!inputs.loadedSpaces.has(space)) {
|
|
247
|
+
violations.push({
|
|
248
|
+
kind: 'orphanMarker',
|
|
249
|
+
spaceId: space,
|
|
250
|
+
remediation: `Orphan marker row for space '${space}' (no longer in extensionPacks); remediation: manually delete the row from \`prisma_contract.marker\`.`,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (violations.length === 0) {
|
|
256
|
+
return { ok: true };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const kindOrder: Record<SpaceVerifierViolation['kind'], number> = {
|
|
260
|
+
declaredButUnmigrated: 0,
|
|
261
|
+
orphanMarker: 1,
|
|
262
|
+
orphanPinnedDir: 2,
|
|
263
|
+
hashMismatch: 3,
|
|
264
|
+
invariantsMismatch: 4,
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
violations.sort((a, b) => {
|
|
268
|
+
const k = kindOrder[a.kind] - kindOrder[b.kind];
|
|
269
|
+
if (k !== 0) return k;
|
|
270
|
+
if (a.spaceId < b.spaceId) return -1;
|
|
271
|
+
if (a.spaceId > b.spaceId) return 1;
|
|
272
|
+
return 0;
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
return { ok: false, violations };
|
|
276
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"errors-CfmjBeK0.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["import { ifDefined } from '@prisma-next/utils/defined';\nimport { basename, dirname, relative } from 'pathe';\n\n/**\n * Build the canonical \"re-emit this package\" remediation hint.\n *\n * Every on-disk migration package ships its own `migration.ts` author-time\n * file. Running it regenerates `migration.json` and `ops.json` with the\n * correct hash + metadata, so it is the right primitive whenever a single\n * package's on-disk artifacts are missing, malformed, or otherwise corrupt.\n * Pointing users at `migration plan` would emit a *new* package rather than\n * heal the broken one.\n */\nfunction reemitHint(dir: string, fallback?: string): string {\n const relativeDir = relative(process.cwd(), dir);\n const reemit = `Re-emit the package by running \\`node \"${relativeDir}/migration.ts\"\\``;\n return fallback ? `${reemit}, ${fallback}` : `${reemit}.`;\n}\n\n/**\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, hash\n * verification, migration history reconstruction), distinct from the runtime\n * MIGRATION.* codes for apply-time failures (PRECHECK_FAILED, POSTCHECK_FAILED,\n * 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: reemitHint(\n dir,\n 'or delete the directory if the migration is unwanted and the source TypeScript is gone.',\n ),\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: reemitHint(dirname(filePath), 'or restore the directory from version control.'),\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: `Migration manifest at \"${filePath}\" is invalid: ${reason}`,\n fix: reemitHint(dirname(filePath), 'or restore the directory from version control.'),\n details: { filePath, reason },\n });\n}\n\nexport function errorInvalidOperationEntry(index: number, reason: string): MigrationToolsError {\n return new MigrationToolsError(\n 'MIGRATION.INVALID_OPERATION_ENTRY',\n 'Migration operation entry is malformed',\n {\n why: `Operation at index ${index} returned by the migration class failed schema validation: ${reason}.`,\n fix: \"Update the migration class so each entry of `operations` carries `id` (string), `label` (string), and `operationClass` (one of 'additive' | 'widening' | 'destructive' | 'data').\",\n details: { index, reason },\n },\n );\n}\n\nexport function errorStaleContractBookends(args: {\n readonly side: 'from' | 'to';\n readonly metaHash: string | null;\n readonly contractHash: string;\n}): MigrationToolsError {\n const { side, metaHash, contractHash } = args;\n // `meta.from` is `string | null` (null = baseline). Render `null` as a\n // human-readable token in the diagnostic so the message stays clear when\n // the mismatch is a baseline-vs-non-baseline disagreement.\n const renderedMetaHash = metaHash === null ? 'null (baseline)' : `\"${metaHash}\"`;\n return new MigrationToolsError(\n 'MIGRATION.STALE_CONTRACT_BOOKENDS',\n 'Migration manifest contract bookends disagree with describe()',\n {\n why: `migration.json stores ${side}Contract.storage.storageHash \"${contractHash}\", but describe() returned meta.${side} = ${renderedMetaHash}. The bookend is stale — most likely the migration's describe() was edited after the package was scaffolded by \\`migration plan\\`.`,\n fix: 'Re-run `migration plan` to regenerate the package with fresh contract bookends, or restore the directory from version control.',\n details: { side, metaHash, contractHash },\n },\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 errorInvalidDestName(destName: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_DEST_NAME', 'Invalid copy destination name', {\n why: `The destination name \"${destName}\" must be a single path segment (no \"..\" or directory separators).`,\n fix: 'Use a simple file name such as \"contract.json\" for each destination in the copy list.',\n details: { destName },\n });\n}\n\nexport function errorSameSourceAndTarget(dir: string, hash: string): MigrationToolsError {\n const dirName = basename(dir);\n return new MigrationToolsError(\n 'MIGRATION.SAME_SOURCE_AND_TARGET',\n 'Migration without data-transform operations has same source and target',\n {\n why: `Migration \"${dirName}\" has from === to === \"${hash}\" and declares no data-transform operations. Self-edges are only allowed when the migration runs at least one dataTransform — otherwise the migration is a no-op.`,\n fix: reemitHint(\n dir,\n 'and either change the contract so from ≠ to, add a dataTransform op, or delete the directory if the migration is unwanted.',\n ),\n details: { dirName, hash },\n },\n );\n}\n\nexport function errorAmbiguousTarget(\n branchTips: readonly string[],\n context?: {\n divergencePoint: string;\n branches: readonly {\n tip: string;\n edges: readonly { dirName: string; from: string; to: string }[];\n }[];\n },\n): MigrationToolsError {\n const divergenceInfo = context\n ? `\\nDivergence point: ${context.divergencePoint}\\nBranches:\\n${context.branches.map((b) => ` → ${b.tip} (${b.edges.length} edge(s): ${b.edges.map((e) => e.dirName).join(' → ') || 'direct'})`).join('\\n')}`\n : '';\n return new MigrationToolsError('MIGRATION.AMBIGUOUS_TARGET', 'Ambiguous migration target', {\n why: `The migration history has diverged into multiple branches: ${branchTips.join(', ')}. This typically happens when two developers plan migrations from the same starting point.${divergenceInfo}`,\n fix: 'Use `migration ref set <name> <hash>` to target a specific branch, delete one of the conflicting migration directories and re-run `migration plan`, or use --from <hash> to explicitly select a starting point.',\n details: {\n branchTips,\n ...(context ? { divergencePoint: context.divergencePoint, branches: context.branches } : {}),\n },\n });\n}\n\nexport function errorNoInitialMigration(nodes: readonly string[]): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.NO_INITIAL_MIGRATION', 'No initial migration found', {\n why: `No migration starts from the empty contract state (known hashes: ${nodes.join(', ')}). At least one migration must originate from the empty state.`,\n fix: 'Inspect the migrations directory for corrupted migration.json files. At least one migration must start from the empty contract hash.',\n details: { nodes },\n });\n}\n\nexport function errorInvalidRefs(refsPath: string, reason: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_REFS', 'Invalid refs.json', {\n why: `refs.json at \"${refsPath}\" is invalid: ${reason}`,\n fix: 'Ensure refs.json is a flat object mapping valid ref names to contract hash strings.',\n details: { path: refsPath, reason },\n });\n}\n\nexport function errorInvalidRefFile(filePath: string, reason: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_REF_FILE', 'Invalid ref file', {\n why: `Ref file at \"${filePath}\" is invalid: ${reason}`,\n fix: 'Ensure the ref file contains valid JSON with { \"hash\": \"sha256:<64 hex chars>\", \"invariants\": [\"...\"] }.',\n details: { path: filePath, reason },\n });\n}\n\nexport function errorInvalidRefName(refName: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_REF_NAME', 'Invalid ref name', {\n why: `Ref name \"${refName}\" is invalid. Names must be lowercase alphanumeric with hyphens or forward slashes (no \".\" or \"..\" segments).`,\n fix: `Use a valid ref name (e.g., \"staging\", \"envs/production\").`,\n details: { refName },\n });\n}\n\nexport function errorNoTarget(reachableHashes: readonly string[]): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.NO_TARGET', 'No migration target could be resolved', {\n why: `The migration history contains cycles and no target can be resolved automatically (reachable hashes: ${reachableHashes.join(', ')}). This typically happens after rollback migrations (e.g., C1→C2→C1).`,\n fix: 'Use --from <hash> to specify the planning origin explicitly.',\n details: { reachableHashes },\n });\n}\n\nexport function errorInvalidRefValue(value: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_REF_VALUE', 'Invalid ref value', {\n why: `Ref value \"${value}\" is not a valid contract hash. Values must be in the format \"sha256:<64 hex chars>\" or \"sha256:empty\".`,\n fix: 'Use a valid storage hash from `prisma-next contract emit` output or an existing migration.',\n details: { value },\n });\n}\n\nexport function errorDuplicateMigrationHash(migrationHash: string): MigrationToolsError {\n return new MigrationToolsError(\n 'MIGRATION.DUPLICATE_MIGRATION_HASH',\n 'Duplicate migrationHash in migration graph',\n {\n why: `Multiple migrations share migrationHash \"${migrationHash}\". Each migration must have a unique content-addressed identity.`,\n fix: 'Regenerate one of the conflicting migrations so each migrationHash is unique, then re-run migration commands.',\n details: { migrationHash },\n },\n );\n}\n\nexport function errorInvalidInvariantId(invariantId: string): MigrationToolsError {\n return new MigrationToolsError('MIGRATION.INVALID_INVARIANT_ID', 'Invalid invariantId', {\n why: `invariantId ${JSON.stringify(invariantId)} is invalid. Ids must be non-empty and contain no whitespace or control characters (including Unicode whitespace like NBSP); other content (kebab-case, camelCase, namespaced, Unicode letters) is allowed.`,\n fix: 'Pick an invariantId without spaces, tabs, newlines, or control characters — e.g. \"backfill-user-phone\", \"users/backfill-phone\", or \"BackfillUserPhone\".',\n details: { invariantId },\n });\n}\n\nexport function errorDuplicateInvariantInEdge(invariantId: string): MigrationToolsError {\n return new MigrationToolsError(\n 'MIGRATION.DUPLICATE_INVARIANT_IN_EDGE',\n 'Duplicate invariantId on a single migration',\n {\n why: `invariantId \"${invariantId}\" is declared by more than one dataTransform on the same migration. The marker stores invariants as a set and the routing layer treats them as edge-level, so two ops cannot share a routing identity.`,\n fix: 'Rename one of the conflicting dataTransform invariantIds, or drop invariantId on the op that does not need to be routing-visible.',\n details: { invariantId },\n },\n );\n}\n\nexport function errorProvidedInvariantsMismatch(\n filePath: string,\n stored: readonly string[],\n derived: readonly string[],\n): MigrationToolsError {\n const storedSet = new Set(stored);\n const derivedSet = new Set(derived);\n const missing = [...derivedSet].filter((id) => !storedSet.has(id));\n const extra = [...storedSet].filter((id) => !derivedSet.has(id));\n // When sets agree but arrays don't, the only difference is ordering — call\n // it out so the reader doesn't stare at two visually-identical arrays.\n // Canonical providedInvariants is sorted ascending; a manifest with the\n // same ids in a different order is still a mismatch (the hash check would\n // also fail), but the human-readable diagnostic is otherwise unhelpful.\n const orderingOnly = missing.length === 0 && extra.length === 0;\n const why = orderingOnly\n ? `migration.json at \"${filePath}\" stores providedInvariants ${JSON.stringify(stored)}, but the canonical value derived from ops.json is ${JSON.stringify(derived)} — same ids, different order. Canonical providedInvariants is sorted ascending.`\n : `migration.json at \"${filePath}\" stores providedInvariants ${JSON.stringify(stored)}, but the value derived from ops.json is ${JSON.stringify(derived)}. The manifest copy was likely hand-edited without re-emitting.`;\n return new MigrationToolsError(\n 'MIGRATION.PROVIDED_INVARIANTS_MISMATCH',\n 'providedInvariants on migration.json disagrees with ops.json',\n {\n why,\n fix: reemitHint(dirname(filePath), 'or restore the directory from version control.'),\n details: { filePath, stored, derived, difference: { missing, extra } },\n },\n );\n}\n\n/**\n * Wire-shape edge surfaced through the JSON envelope's\n * `meta.structuralPath` of `MIGRATION.NO_INVARIANT_PATH`. Slim by design —\n * authoring metadata (`createdAt`, `labels`) lives on `MigrationEdge` but\n * is intentionally dropped here so the envelope stays stable across\n * graph-internal refactors.\n *\n * Stability: any field added here is part of the public CLI JSON contract.\n * Callers (CLI consumers, agents) must be able to treat\n * `(dirName, migrationHash, from, to, invariants)` as the canonical shape.\n */\nexport interface NoInvariantPathStructuralEdge {\n readonly dirName: string;\n readonly migrationHash: string;\n readonly from: string;\n readonly to: string;\n readonly invariants: readonly string[];\n}\n\nexport function errorNoInvariantPath(args: {\n readonly refName?: string;\n readonly required: readonly string[];\n readonly missing: readonly string[];\n readonly structuralPath: readonly NoInvariantPathStructuralEdge[];\n}): MigrationToolsError {\n const { refName, required, missing, structuralPath } = args;\n const refClause = refName ? `Ref \"${refName}\"` : 'Target';\n const missingList = missing.map((id) => JSON.stringify(id)).join(', ');\n const requiredList = required.map((id) => JSON.stringify(id)).join(', ');\n return new MigrationToolsError(\n 'MIGRATION.NO_INVARIANT_PATH',\n 'No path covers the required invariants',\n {\n why: `${refClause} requires invariants the reachable path doesn't cover. required=[${requiredList}], missing=[${missingList}].`,\n fix: 'Add a migration on the path that runs `dataTransform({ invariantId: \"<id>\", … })` for each missing invariant, or retarget the ref to a hash whose path already provides them.',\n details: {\n required,\n missing,\n structuralPath,\n ...ifDefined('refName', refName),\n },\n },\n );\n}\n\nexport function errorUnknownInvariant(args: {\n readonly refName?: string;\n readonly unknown: readonly string[];\n readonly declared: readonly string[];\n}): MigrationToolsError {\n const { refName, unknown, declared } = args;\n const refClause = refName ? `Ref \"${refName}\" declares` : 'Declares';\n const unknownList = unknown.map((id) => JSON.stringify(id)).join(', ');\n return new MigrationToolsError(\n 'MIGRATION.UNKNOWN_INVARIANT',\n 'Ref declares invariants no migration in the graph provides',\n {\n why: `${refClause} invariants no migration in the graph provides. unknown=[${unknownList}].`,\n fix: 'Either the ref has a typo, or the declaring migration has not been authored/attested yet. Re-check the ref file and the migrations directory.',\n details: {\n unknown,\n declared,\n ...ifDefined('refName', refName),\n },\n },\n );\n}\n\nexport function errorMigrationHashMismatch(\n dir: string,\n storedHash: string,\n computedHash: string,\n): MigrationToolsError {\n // Render a cwd-relative path in the human-readable diagnostic so users\n // running CLI commands from the project root see a familiar short path.\n // Keep the absolute path in `details.dir` for machine consumers.\n const relativeDir = relative(process.cwd(), dir);\n return new MigrationToolsError('MIGRATION.HASH_MISMATCH', 'Migration package is corrupt', {\n why: `Stored migrationHash \"${storedHash}\" does not match the recomputed hash \"${computedHash}\" for \"${relativeDir}\". The migration.json or ops.json has been edited or partially written since emit.`,\n fix: reemitHint(dir, 'or restore the directory from version control.'),\n details: { dir, storedHash, computedHash },\n });\n}\n"],"mappings":"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"}
|
package/dist/exports/io.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hash-BARZdVgW.mjs","names":["sorted: Record<string, unknown>"],"sources":["../src/canonicalize-json.ts","../src/hash.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 type { MigrationMetadata } from './metadata';\nimport type { MigrationOps, MigrationPackage } from './package';\n\nexport interface VerifyResult {\n readonly ok: boolean;\n readonly reason?: 'mismatch';\n readonly storedHash: string;\n readonly computedHash: string;\n}\n\nfunction sha256Hex(input: string): string {\n return createHash('sha256').update(input).digest('hex');\n}\n\n/**\n * Content-addressed migration hash over (metadata envelope sans\n * contracts/hints/signature, ops). See ADR 199 — Storage-only migration\n * identity 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 *\n * The integrity check is purely structural, not semantic. The function\n * canonicalizes its inputs via `sortKeys` (recursive) + `JSON.stringify`\n * and hashes the result. Target-specific operation payloads (`step.sql`,\n * Mongo's pipeline AST, …) are hashed verbatim — no per-target\n * normalization is required, because what's being verified is \"do the\n * on-disk bytes still produce their recorded hash\", not \"do two\n * semantically-equivalent migrations hash the same\". The latter is an\n * emit-drift concern (ADR 192 step 2).\n *\n * The symmetry across write and read holds because `JSON.parse(\n * JSON.stringify(x))` round-trips JSON-safe values losslessly and\n * `sortKeys` is idempotent and deterministic — write-time and read-time\n * canonicalization produce the same canonical bytes regardless of\n * source-side key ordering or whitespace.\n *\n * The `migrationHash` field on the metadata is stripped before hashing\n * so the function can be used both at write time (when no hash exists\n * yet) and at verify time (rehashing an already-attested record).\n */\nexport function computeMigrationHash(\n metadata: Omit<MigrationMetadata, 'migrationHash'> & { readonly migrationHash?: string },\n ops: MigrationOps,\n): string {\n const {\n migrationHash: _migrationHash,\n signature: _signature,\n fromContract: _fromContract,\n toContract: _toContract,\n hints: _hints,\n ...strippedMeta\n } = metadata;\n\n const canonicalMetadata = canonicalizeJson(strippedMeta);\n const canonicalOps = canonicalizeJson(ops);\n\n const partHashes = [canonicalMetadata, canonicalOps].map(sha256Hex);\n const hash = sha256Hex(canonicalizeJson(partHashes));\n\n return `sha256:${hash}`;\n}\n\n/**\n * Re-hash an in-memory migration package and compare against the stored\n * `migrationHash`. See `computeMigrationHash` for the canonicalization rules.\n *\n * Returns `{ ok: true }` when the package is internally consistent, or\n * `{ ok: false, reason: 'mismatch', storedHash, computedHash }` when it is\n * not — typically a sign of FS corruption, partial writes, or a post-emit\n * hand edit.\n */\nexport function verifyMigrationHash(pkg: MigrationPackage): VerifyResult {\n const computed = computeMigrationHash(pkg.metadata, pkg.ops);\n\n if (pkg.metadata.migrationHash === computed) {\n return {\n ok: true,\n storedHash: pkg.metadata.migrationHash,\n computedHash: computed,\n };\n }\n\n return {\n ok: false,\n reason: 'mismatch',\n storedHash: pkg.metadata.migrationHash,\n computedHash: 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;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BzD,SAAgB,qBACd,UACA,KACQ;CACR,MAAM,EACJ,eAAe,gBACf,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;;;;;;;;;;;AActD,SAAgB,oBAAoB,KAAqC;CACvE,MAAM,WAAW,qBAAqB,IAAI,UAAU,IAAI,IAAI;AAE5D,KAAI,IAAI,SAAS,kBAAkB,SACjC,QAAO;EACL,IAAI;EACJ,YAAY,IAAI,SAAS;EACzB,cAAc;EACf;AAGH,QAAO;EACL,IAAI;EACJ,QAAQ;EACR,YAAY,IAAI,SAAS;EACzB,cAAc;EACf"}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { Contract } from "@prisma-next/contract/types";
|
|
2
|
-
|
|
3
|
-
//#region src/metadata.d.ts
|
|
4
|
-
interface MigrationHints {
|
|
5
|
-
readonly used: readonly string[];
|
|
6
|
-
readonly applied: readonly string[];
|
|
7
|
-
readonly plannerVersion: string;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* In-memory migration metadata envelope. Every migration is content-addressed:
|
|
11
|
-
* the `migrationHash` is a hash over the metadata envelope plus the operations
|
|
12
|
-
* list, computed at write time. There is no draft state — a migration
|
|
13
|
-
* directory either exists with fully attested metadata or it does not.
|
|
14
|
-
*
|
|
15
|
-
* When the planner cannot lower an operation because of an unfilled
|
|
16
|
-
* `placeholder(...)` slot, the migration is still written with `migrationHash`
|
|
17
|
-
* hashed over `ops: []`. Re-running self-emit after the user fills the
|
|
18
|
-
* placeholder produces a *different* `migrationHash` (committed to the real
|
|
19
|
-
* ops); this is intentional.
|
|
20
|
-
*
|
|
21
|
-
* The on-disk JSON shape in `migration.json` matches this type field-for-field
|
|
22
|
-
* — `JSON.stringify(metadata, null, 2)` is the canonical writer output.
|
|
23
|
-
*/
|
|
24
|
-
interface MigrationMetadata {
|
|
25
|
-
readonly migrationHash: string;
|
|
26
|
-
readonly from: string | null;
|
|
27
|
-
readonly to: string;
|
|
28
|
-
readonly fromContract: Contract | null;
|
|
29
|
-
readonly toContract: Contract;
|
|
30
|
-
readonly hints: MigrationHints;
|
|
31
|
-
readonly labels: readonly string[];
|
|
32
|
-
/**
|
|
33
|
-
* Sorted, deduplicated list of `invariantId`s declared by the
|
|
34
|
-
* migration's data-transform ops. Always present; an empty array
|
|
35
|
-
* means the migration has no routing-visible data transforms.
|
|
36
|
-
*/
|
|
37
|
-
readonly providedInvariants: readonly string[];
|
|
38
|
-
readonly authorship?: {
|
|
39
|
-
readonly author?: string;
|
|
40
|
-
readonly email?: string;
|
|
41
|
-
};
|
|
42
|
-
readonly signature?: {
|
|
43
|
-
readonly keyId: string;
|
|
44
|
-
readonly value: string;
|
|
45
|
-
} | null;
|
|
46
|
-
readonly createdAt: string;
|
|
47
|
-
}
|
|
48
|
-
//#endregion
|
|
49
|
-
export { MigrationMetadata as n, MigrationHints as t };
|
|
50
|
-
//# sourceMappingURL=metadata-BP1cmU7Z.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"metadata-BP1cmU7Z.d.mts","names":[],"sources":["../src/metadata.ts"],"sourcesContent":[],"mappings":";;;UAEiB,cAAA;;EAAA,SAAA,OAAA,EAAc,SAAA,MAAA,EAAA;EAqBd,SAAA,cAAiB,EAAA,MAAA;;;;;;;;;;;;;;;;;UAAjB,iBAAA;;;;yBAIQ;uBACF;kBACL"}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { n as MigrationMetadata } from "./metadata-BP1cmU7Z.mjs";
|
|
2
|
-
import { MigrationPlanOperation } from "@prisma-next/framework-components/control";
|
|
3
|
-
|
|
4
|
-
//#region src/package.d.ts
|
|
5
|
-
type MigrationOps = readonly MigrationPlanOperation[];
|
|
6
|
-
/**
|
|
7
|
-
* An on-disk migration directory (a "package") with its parsed metadata and
|
|
8
|
-
* operations. Returned from `readMigrationPackage` / `readMigrationsDir` only
|
|
9
|
-
* after the loader has verified the package's integrity (hash recomputation
|
|
10
|
-
* against the stored `migrationHash`); holding a `MigrationPackage` value
|
|
11
|
-
* therefore implies the package is internally consistent.
|
|
12
|
-
*/
|
|
13
|
-
interface MigrationPackage {
|
|
14
|
-
readonly dirName: string;
|
|
15
|
-
readonly dirPath: string;
|
|
16
|
-
readonly metadata: MigrationMetadata;
|
|
17
|
-
readonly ops: MigrationOps;
|
|
18
|
-
}
|
|
19
|
-
//#endregion
|
|
20
|
-
export { MigrationPackage as n, MigrationOps as t };
|
|
21
|
-
//# sourceMappingURL=package-5HCCg0z-.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"package-5HCCg0z-.d.mts","names":[],"sources":["../src/package.ts"],"sourcesContent":[],"mappings":";;;;KAGY,YAAA,YAAwB;;AAApC;AASA;;;;;UAAiB,gBAAA;;;qBAGI;gBACL"}
|