@prisma-next/cli 0.15.0 → 0.16.0-dev.10
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/README.md +3 -4
- package/dist/cli.d.mts +1 -1
- package/dist/cli.mjs +13 -13
- package/dist/{client-DigVI8zX.mjs → client-2kwLMBSf.mjs} +67 -36
- package/dist/client-2kwLMBSf.mjs.map +1 -0
- package/dist/{command-helpers-CUoKYTlG.mjs → command-helpers-CVn3U9Uz.mjs} +32 -8
- package/dist/command-helpers-CVn3U9Uz.mjs.map +1 -0
- package/dist/commands/contract-emit.d.mts +0 -1
- package/dist/commands/contract-emit.d.mts.map +1 -1
- package/dist/commands/contract-emit.mjs +1 -1
- package/dist/commands/contract-infer.d.mts +0 -1
- package/dist/commands/contract-infer.d.mts.map +1 -1
- package/dist/commands/contract-infer.mjs +1 -1
- package/dist/commands/db-init.d.mts +0 -1
- package/dist/commands/db-init.d.mts.map +1 -1
- package/dist/commands/db-init.mjs +8 -7
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.d.mts +0 -1
- package/dist/commands/db-schema.d.mts.map +1 -1
- package/dist/commands/db-schema.mjs +3 -3
- package/dist/commands/db-sign.d.mts +0 -1
- package/dist/commands/db-sign.d.mts.map +1 -1
- package/dist/commands/db-sign.mjs +8 -10
- package/dist/commands/db-sign.mjs.map +1 -1
- package/dist/commands/db-update.d.mts +0 -1
- package/dist/commands/db-update.d.mts.map +1 -1
- package/dist/commands/db-update.mjs +12 -14
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.d.mts +0 -1
- package/dist/commands/db-verify.d.mts.map +1 -1
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migrate.d.mts +1 -2
- package/dist/commands/migrate.d.mts.map +1 -1
- package/dist/commands/migrate.mjs +7 -7
- package/dist/commands/migrate.mjs.map +1 -1
- package/dist/commands/migration-check.d.mts +4 -3
- package/dist/commands/migration-check.d.mts.map +1 -1
- package/dist/commands/migration-check.mjs +1 -1
- package/dist/commands/migration-graph.d.mts +2 -3
- package/dist/commands/migration-graph.d.mts.map +1 -1
- package/dist/commands/migration-graph.mjs +3 -3
- package/dist/commands/migration-list.d.mts +2 -3
- package/dist/commands/migration-list.d.mts.map +1 -1
- package/dist/commands/migration-list.mjs +1 -1
- package/dist/commands/migration-log.d.mts +2 -3
- package/dist/commands/migration-log.d.mts.map +1 -1
- package/dist/commands/migration-log.mjs +1 -1
- package/dist/commands/migration-new.d.mts +0 -1
- package/dist/commands/migration-new.d.mts.map +1 -1
- package/dist/commands/migration-new.mjs +14 -38
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +0 -1
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +1 -1
- package/dist/commands/migration-show.d.mts +0 -1
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +3 -3
- package/dist/commands/migration-status.d.mts +2 -3
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +1 -1
- package/dist/commands/ref.d.mts +1 -2
- package/dist/commands/ref.d.mts.map +1 -1
- package/dist/commands/ref.mjs +13 -18
- package/dist/commands/ref.mjs.map +1 -1
- package/dist/commands/telemetry/index.d.mts +0 -1
- package/dist/commands/telemetry/index.d.mts.map +1 -1
- package/dist/commands/telemetry/index.mjs +1 -1
- package/dist/contract-at-errors-C-l4-JEY.mjs +36 -0
- package/dist/contract-at-errors-C-l4-JEY.mjs.map +1 -0
- package/dist/{contract-emit-wgqrOXUI.mjs → contract-emit-DzOkTs11.mjs} +3 -3
- package/dist/{contract-emit-wgqrOXUI.mjs.map → contract-emit-DzOkTs11.mjs.map} +1 -1
- package/dist/{contract-emit-sYrqzsdN.mjs → contract-emit-iSMJVs26.mjs} +3 -3
- package/dist/{contract-emit-sYrqzsdN.mjs.map → contract-emit-iSMJVs26.mjs.map} +1 -1
- package/dist/{contract-infer-6SQnnVj7.mjs → contract-infer-BVqworBB.mjs} +3 -3
- package/dist/{contract-infer-6SQnnVj7.mjs.map → contract-infer-BVqworBB.mjs.map} +1 -1
- package/dist/{contract-space-aggregate-loader-DpB4x97a.mjs → contract-space-aggregate-loader-hPVymNLw.mjs} +14 -15
- package/dist/contract-space-aggregate-loader-hPVymNLw.mjs.map +1 -0
- package/dist/{db-verify-CEsG5Gek.mjs → db-verify-BpwCMyWJ.mjs} +6 -7
- package/dist/db-verify-BpwCMyWJ.mjs.map +1 -0
- package/dist/exports/control-api.d.mts +1 -2
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +2 -2
- package/dist/exports/index.d.mts +0 -1
- package/dist/exports/index.d.mts.map +1 -1
- package/dist/exports/index.mjs +2 -2
- package/dist/exports/init-output.d.mts +1 -0
- package/dist/exports/init-output.d.mts.map +1 -1
- package/dist/exports/init-output.mjs +1 -1
- package/dist/{format-CjongNb1.mjs → format-CFLZv_zU.mjs} +6 -5
- package/dist/format-CFLZv_zU.mjs.map +1 -0
- package/dist/{framework-components-BbnM1RYm.mjs → framework-components-VMQJbAwl.mjs} +2 -2
- package/dist/{framework-components-BbnM1RYm.mjs.map → framework-components-VMQJbAwl.mjs.map} +1 -1
- package/dist/global-flags-BpoOYtNZ.d.mts.map +1 -1
- package/dist/{init-D8YFfaD6.mjs → init-BSgPxVXA.mjs} +85 -90
- package/dist/init-BSgPxVXA.mjs.map +1 -0
- package/dist/{inspect-live-schema-CEt_ztBJ.mjs → inspect-live-schema-DxsD_a4d.mjs} +3 -3
- package/dist/{inspect-live-schema-CEt_ztBJ.mjs.map → inspect-live-schema-DxsD_a4d.mjs.map} +1 -1
- package/dist/{migration-check-ByeQHOhK.mjs → migration-check-DMxPzbHl.mjs} +70 -44
- package/dist/migration-check-DMxPzbHl.mjs.map +1 -0
- package/dist/migration-cli.d.mts +0 -1
- package/dist/migration-cli.d.mts.map +1 -1
- package/dist/migration-cli.mjs +3 -3
- package/dist/migration-cli.mjs.map +1 -1
- package/dist/{migration-command-scaffold-CJO3tjiR.mjs → migration-command-scaffold-DB_i9nBC.mjs} +3 -3
- package/dist/{migration-command-scaffold-CJO3tjiR.mjs.map → migration-command-scaffold-DB_i9nBC.mjs.map} +1 -1
- package/dist/{migration-list-DTYM8xvm.mjs → migration-list-cnSKilYH.mjs} +5 -5
- package/dist/migration-list-cnSKilYH.mjs.map +1 -0
- package/dist/{migration-log-qZ8LpQIl.mjs → migration-log-CEqFiez9.mjs} +3 -3
- package/dist/{migration-log-qZ8LpQIl.mjs.map → migration-log-CEqFiez9.mjs.map} +1 -1
- package/dist/{migration-path-target-DmNwsDgs.mjs → migration-path-target-C0TQuV5n.mjs} +2 -2
- package/dist/{migration-path-target-DmNwsDgs.mjs.map → migration-path-target-C0TQuV5n.mjs.map} +1 -1
- package/dist/{migration-plan-CfPrB80t.mjs → migration-plan-BJHulFZC.mjs} +54 -62
- package/dist/migration-plan-BJHulFZC.mjs.map +1 -0
- package/dist/{migration-status-DRuS-rP5.mjs → migration-status-ugWwGk1V.mjs} +5 -5
- package/dist/{migration-status-DRuS-rP5.mjs.map → migration-status-ugWwGk1V.mjs.map} +1 -1
- package/dist/{output-mEQ74_nd.mjs → output-DWNYqZGc.mjs} +5 -6
- package/dist/output-DWNYqZGc.mjs.map +1 -0
- package/dist/{ref-advancement-BkXlikCA.mjs → ref-advancement-BCz7X7qJ.mjs} +13 -6
- package/dist/ref-advancement-BCz7X7qJ.mjs.map +1 -0
- package/dist/schemas-CeGMYFYX.d.mts.map +1 -1
- package/dist/{telemetry-DHBBq5wT.mjs → telemetry-BEyNMfco.mjs} +2 -2
- package/dist/{telemetry-DHBBq5wT.mjs.map → telemetry-BEyNMfco.mjs.map} +1 -1
- package/dist/{terminal-ui-DGRNFWna.d.mts → terminal-ui-BN4QOsm5.d.mts} +1 -2
- package/dist/terminal-ui-BN4QOsm5.d.mts.map +1 -0
- package/dist/{types-DFoqNIcQ.d.mts → types-D4r_HI9-.d.mts} +8 -4
- package/dist/types-D4r_HI9-.d.mts.map +1 -0
- package/dist/{verify-DDCb0R2r.mjs → verify-C4RazDE1.mjs} +9 -8
- package/dist/verify-C4RazDE1.mjs.map +1 -0
- package/package.json +25 -25
- package/src/commands/db-init.ts +4 -3
- package/src/commands/db-sign.ts +6 -4
- package/src/commands/db-update.ts +15 -7
- package/src/commands/db-verify.ts +2 -3
- package/src/commands/init/errors.ts +62 -58
- package/src/commands/init/hygiene-gitattributes.ts +37 -21
- package/src/commands/init/init.ts +35 -32
- package/src/commands/init/output.ts +4 -5
- package/src/commands/init/reinit-cleanup.ts +16 -17
- package/src/commands/migrate.ts +1 -0
- package/src/commands/migration-check.ts +75 -38
- package/src/commands/migration-list.ts +1 -5
- package/src/commands/migration-new.ts +15 -39
- package/src/commands/migration-plan.ts +56 -78
- package/src/commands/ref.ts +19 -14
- package/src/control-api/operations/db-run.ts +4 -4
- package/src/control-api/operations/db-verify.ts +12 -9
- package/src/control-api/operations/format.ts +4 -3
- package/src/control-api/operations/migrate.ts +70 -26
- package/src/control-api/types.ts +4 -1
- package/src/migration-cli.ts +5 -5
- package/src/utils/cli-errors.ts +28 -7
- package/src/utils/combine-verify-results.ts +1 -1
- package/src/utils/contract-at-errors.ts +13 -22
- package/src/utils/contract-space-aggregate-loader.ts +12 -13
- package/src/utils/contract-space-seed-phase.ts +8 -6
- package/src/utils/formatters/errors.ts +24 -3
- package/src/utils/formatters/verify.ts +7 -6
- package/src/utils/integrity-violation-to-check-failure.ts +14 -14
- package/src/utils/plan-resolution.ts +6 -9
- package/src/utils/ref-advancement.ts +22 -3
- package/src/utils/result-handler.ts +4 -2
- package/dist/client-DigVI8zX.mjs.map +0 -1
- package/dist/command-helpers-CUoKYTlG.mjs.map +0 -1
- package/dist/contract-at-errors-D8CNDD7g.mjs +0 -42
- package/dist/contract-at-errors-D8CNDD7g.mjs.map +0 -1
- package/dist/contract-space-aggregate-loader-DpB4x97a.mjs.map +0 -1
- package/dist/db-verify-CEsG5Gek.mjs.map +0 -1
- package/dist/format-CjongNb1.mjs.map +0 -1
- package/dist/init-D8YFfaD6.mjs.map +0 -1
- package/dist/migration-check-ByeQHOhK.mjs.map +0 -1
- package/dist/migration-list-DTYM8xvm.mjs.map +0 -1
- package/dist/migration-plan-CfPrB80t.mjs.map +0 -1
- package/dist/output-mEQ74_nd.mjs.map +0 -1
- package/dist/ref-advancement-BkXlikCA.mjs.map +0 -1
- package/dist/terminal-ui-DGRNFWna.d.mts.map +0 -1
- package/dist/types-DFoqNIcQ.d.mts.map +0 -1
- package/dist/verify-DDCb0R2r.mjs.map +0 -1
|
@@ -1,47 +1,60 @@
|
|
|
1
1
|
import type { TargetId } from './templates/code-templates';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* The `.gitattributes` entries written for a freshly initialised project
|
|
5
|
+
* (FR3.4). Mirrors the relevant subset of the repo-root
|
|
6
|
+
* [`.gitattributes`](../../../../../../../../.gitattributes):
|
|
7
7
|
*
|
|
8
8
|
* - **Today**: `contract.json`, `contract.d.ts` are emitted on every
|
|
9
9
|
* `prisma-next contract emit`. Marking them `linguist-generated`
|
|
10
10
|
* keeps GitHub's diff stats honest and collapses the file in code
|
|
11
11
|
* review by default.
|
|
12
|
-
* - **Forward-looking**: `
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
12
|
+
* - **Forward-looking**: `ops.json`, `migration.json` are not yet emitted
|
|
13
|
+
* by `init` flows but will be produced by adjacent commands (lower /
|
|
14
|
+
* migration tooling). Adding them now matches Decision 5
|
|
15
|
+
* (forward-looking subset) so the file does not need to be amended
|
|
16
|
+
* every time a new artifact lands.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
* who runs `init --schema-path db/contract.prisma`
|
|
20
|
-
* `db/contract.json linguist-generated` — not the workspace-glob
|
|
21
|
-
* `<glob>/contract.json` (which would over-match any unrelated
|
|
18
|
+
* `ARTIFACT_FILENAMES` entries are written relative to the schema
|
|
19
|
+
* directory so a user who runs `init --schema-path db/contract.prisma`
|
|
20
|
+
* gets `db/contract.json linguist-generated` — not the workspace-glob
|
|
21
|
+
* form `<glob>/contract.json` (which would over-match any unrelated
|
|
22
22
|
* `contract.json` the user has elsewhere) and not the absolute
|
|
23
|
-
* `DEFAULT_CONTRACT_SOURCE_DIR/contract.json` (which would silently
|
|
24
|
-
* schema path).
|
|
23
|
+
* `DEFAULT_CONTRACT_SOURCE_DIR/contract.json` (which would silently
|
|
24
|
+
* break for a non-default schema path).
|
|
25
|
+
*
|
|
26
|
+
* The migration contract snapshot store (`migrations/snapshots/<hex>/`)
|
|
27
|
+
* is anchored to the migrations root instead, not the schema directory:
|
|
28
|
+
* migration package depth under `migrations/` varies (`app/<pkg>`,
|
|
29
|
+
* `<space>/<pkg>`, or a bare `<pkg>` in extension source repos), so no
|
|
30
|
+
* single schema-dir-relative pattern can reach every snapshot. See
|
|
31
|
+
* `STORE_GITATTRIBUTES_LINES` below.
|
|
25
32
|
*/
|
|
26
|
-
const
|
|
33
|
+
const ARTIFACT_FILENAMES: readonly string[] = [
|
|
27
34
|
'contract.json',
|
|
28
35
|
'contract.d.ts',
|
|
29
|
-
'end-contract.json',
|
|
30
|
-
'end-contract.d.ts',
|
|
31
|
-
'start-contract.json',
|
|
32
|
-
'start-contract.d.ts',
|
|
33
36
|
'ops.json',
|
|
34
37
|
'migration.json',
|
|
35
38
|
];
|
|
36
39
|
|
|
37
40
|
const ATTRIBUTE = 'linguist-generated';
|
|
38
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Full `.gitattributes` lines for the migration contract snapshot store,
|
|
44
|
+
* already anchored to the migrations root — unlike `ARTIFACT_FILENAMES`,
|
|
45
|
+
* these are not combined with the schema-relative prefix.
|
|
46
|
+
*/
|
|
47
|
+
const STORE_GITATTRIBUTES_LINES: readonly string[] = [
|
|
48
|
+
`migrations/snapshots/**/contract.json ${ATTRIBUTE}`,
|
|
49
|
+
`migrations/snapshots/**/contract.d.ts ${ATTRIBUTE}`,
|
|
50
|
+
];
|
|
51
|
+
|
|
39
52
|
/**
|
|
40
53
|
* Computes the `.gitattributes` lines this scaffold expects to own. Each
|
|
41
54
|
* line has the shape `<path> linguist-generated`. The `target` parameter
|
|
42
55
|
* is currently unused but accepted for symmetry with the other hygiene
|
|
43
56
|
* helpers and to leave room for target-specific entries (e.g. a future
|
|
44
|
-
*
|
|
57
|
+
* family-specific artifact) without a signature break.
|
|
45
58
|
*/
|
|
46
59
|
export function requiredGitattributesLines(
|
|
47
60
|
schemaDir: string,
|
|
@@ -49,7 +62,10 @@ export function requiredGitattributesLines(
|
|
|
49
62
|
): readonly string[] {
|
|
50
63
|
const dir = schemaDir === '.' ? '' : schemaDir.replace(/\/+$/, '');
|
|
51
64
|
const prefix = dir === '' ? '' : `${dir}/`;
|
|
52
|
-
return
|
|
65
|
+
return [
|
|
66
|
+
...ARTIFACT_FILENAMES.map((file) => `${prefix}${file} ${ATTRIBUTE}`),
|
|
67
|
+
...STORE_GITATTRIBUTES_LINES,
|
|
68
|
+
];
|
|
53
69
|
}
|
|
54
70
|
|
|
55
71
|
/**
|
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
renderInitOutro,
|
|
49
49
|
} from './output';
|
|
50
50
|
import { type ProbeOutcome, type ProbeOverrides, probeServerVersion } from './probe-db';
|
|
51
|
-
import {
|
|
51
|
+
import { findStaleArtifacts, removeDependency } from './reinit-cleanup';
|
|
52
52
|
import {
|
|
53
53
|
DEFAULT_SKILL_SOURCES,
|
|
54
54
|
formatSkillInstallCommand,
|
|
@@ -175,14 +175,14 @@ export async function runInit(
|
|
|
175
175
|
{ path: '.env.example', content: envExampleContent(inputs.target) },
|
|
176
176
|
];
|
|
177
177
|
|
|
178
|
-
// FR9.1 — on re-init, queue the previously-emitted contract
|
|
178
|
+
// FR9.1 — on re-init, queue the previously-emitted contract artifacts
|
|
179
179
|
// for deletion so a target switch (or schema-shape change) does not
|
|
180
180
|
// leave a stale `contract.json` / `contract.d.ts` next to the new
|
|
181
181
|
// schema source. Detection is filesystem-only (no parsing of the
|
|
182
182
|
// previous config) so the cleanup is safe to run before the write
|
|
183
183
|
// phase: each path is checked for existence in the precondition,
|
|
184
184
|
// and missing-on-disk-at-write-time is tolerated.
|
|
185
|
-
const filesToDelete: string[] = inputs.reinit ? [...
|
|
185
|
+
const filesToDelete: string[] = inputs.reinit ? [...findStaleArtifacts(baseDir, schemaDir)] : [];
|
|
186
186
|
|
|
187
187
|
// `init` delegates the skill to `npx skills add prisma/prisma-next#v<version>`,
|
|
188
188
|
// so a hand-rolled `.agents/skills/prisma-next/SKILL.md` in the project
|
|
@@ -245,7 +245,7 @@ export async function runInit(
|
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
// FR3.4: idempotent .gitattributes — linguist-generated entries for
|
|
248
|
-
// the emitted
|
|
248
|
+
// the emitted artifacts so GitHub diff stats / code review collapse
|
|
249
249
|
// them by default.
|
|
250
250
|
const gitattributesPath = join(baseDir, '.gitattributes');
|
|
251
251
|
const existingGitattributes = existsSync(gitattributesPath)
|
|
@@ -373,7 +373,7 @@ export async function runInit(
|
|
|
373
373
|
}
|
|
374
374
|
}
|
|
375
375
|
|
|
376
|
-
// FR9.1 — delete stale
|
|
376
|
+
// FR9.1 — delete stale artifacts after the new templates are written.
|
|
377
377
|
// Order is intentional: the names do not collide with `filesToWrite`
|
|
378
378
|
// (we never write `contract.json` from this command — that's `contract
|
|
379
379
|
// emit`'s job), so deletion *after* the writes guarantees we never
|
|
@@ -534,18 +534,21 @@ export async function runInit(
|
|
|
534
534
|
if (validated instanceof Error || (validated as { problems?: unknown }).problems !== undefined) {
|
|
535
535
|
// Route through `emitError` rather than throwing: the bare throw
|
|
536
536
|
// bypassed `--json` envelope formatting and `exitCodeForError`, so a
|
|
537
|
-
//
|
|
538
|
-
// commander instead of the documented
|
|
539
|
-
// the right channel.
|
|
537
|
+
// CLI.INIT_INVALID_OUTPUT_DOCUMENT regression would surface as an
|
|
538
|
+
// uncaught exception in commander instead of the documented
|
|
539
|
+
// `INTERNAL_ERROR` envelope on the right channel.
|
|
540
540
|
return emitError(
|
|
541
541
|
ui,
|
|
542
542
|
flags,
|
|
543
|
-
new CliStructuredError(
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
543
|
+
new CliStructuredError(
|
|
544
|
+
'CLI.INIT_INVALID_OUTPUT_DOCUMENT',
|
|
545
|
+
'Init produced an invalid output document',
|
|
546
|
+
{
|
|
547
|
+
why: `The success document failed schema validation: ${String(validated)}`,
|
|
548
|
+
fix: 'This is a bug in prisma-next. Please report it with the full `-v` output.',
|
|
549
|
+
docsUrl: 'https://prisma-next.dev/docs/cli/init',
|
|
550
|
+
},
|
|
551
|
+
),
|
|
549
552
|
);
|
|
550
553
|
}
|
|
551
554
|
|
|
@@ -582,10 +585,10 @@ function emitError(ui: TerminalUI, flags: GlobalFlags, error: CliStructuredError
|
|
|
582
585
|
* Maps a structured init error to its documented exit code. Centralised so
|
|
583
586
|
* the error → exit-code contract lives next to the codes themselves.
|
|
584
587
|
*
|
|
585
|
-
* `
|
|
586
|
-
* `INIT_EXIT_INTERNAL_ERROR` because those represent prisma-next
|
|
587
|
-
* user did not cause — surfacing them as `PRECONDITION` would
|
|
588
|
-
* automation into thinking the caller mis-invoked the CLI.
|
|
588
|
+
* `CLI.INIT_INVALID_OUTPUT_DOCUMENT` (and the unknown-code default branch)
|
|
589
|
+
* routes to `INIT_EXIT_INTERNAL_ERROR` because those represent prisma-next
|
|
590
|
+
* bugs the user did not cause — surfacing them as `PRECONDITION` would
|
|
591
|
+
* mislead automation into thinking the caller mis-invoked the CLI.
|
|
589
592
|
*
|
|
590
593
|
* See [exit-codes.ts](./exit-codes.ts) for the canonical list and
|
|
591
594
|
* [Style Guide § Exit Codes](../../../../../../../docs/CLI%20Style%20Guide.md#exit-codes)
|
|
@@ -596,24 +599,24 @@ function emitError(ui: TerminalUI, flags: GlobalFlags, error: CliStructuredError
|
|
|
596
599
|
*/
|
|
597
600
|
export function exitCodeForError(error: { readonly code: string }): number {
|
|
598
601
|
switch (error.code) {
|
|
599
|
-
case '
|
|
600
|
-
case '
|
|
601
|
-
case '
|
|
602
|
-
case '
|
|
603
|
-
case '
|
|
604
|
-
case '
|
|
605
|
-
case '
|
|
606
|
-
case '
|
|
602
|
+
case 'CLI.INIT_REINIT_NEEDS_FORCE': // re-init needs --force — precondition
|
|
603
|
+
case 'CLI.INIT_MISSING_FLAGS': // missing flags — precondition
|
|
604
|
+
case 'CLI.INIT_INVALID_FLAG_VALUE': // invalid flag value — precondition
|
|
605
|
+
case 'CLI.INIT_STRICT_PROBE_WITHOUT_PROBE': // --strict-probe without --probe-db — precondition
|
|
606
|
+
case 'CLI.INIT_INVALID_MANIFEST': // invalid manifest (malformed package.json) — precondition
|
|
607
|
+
case 'CLI.INIT_INVALID_TSCONFIG': // invalid tsconfig (unparseable JSONC) — precondition
|
|
608
|
+
case 'CLI.INIT_PROBE_FAILED': // probe failed under --strict-probe — precondition
|
|
609
|
+
case 'CLI.INIT_AUTHORING_SCHEMA_PATH_MISMATCH': // --authoring / --schema-path extension mismatch — precondition
|
|
607
610
|
return INIT_EXIT_PRECONDITION;
|
|
608
|
-
case '
|
|
611
|
+
case 'CLI.INIT_USER_ABORTED': // user aborted interactive prompt
|
|
609
612
|
return INIT_EXIT_USER_ABORTED;
|
|
610
|
-
case '
|
|
613
|
+
case 'CLI.INIT_INSTALL_FAILED': // install failed
|
|
611
614
|
return INIT_EXIT_INSTALL_FAILED;
|
|
612
|
-
case '
|
|
615
|
+
case 'CLI.INIT_EMIT_FAILED': // emit failed
|
|
613
616
|
return INIT_EXIT_EMIT_FAILED;
|
|
614
|
-
case '
|
|
617
|
+
case 'CLI.INIT_INVALID_OUTPUT_DOCUMENT': // invalid output document — internal bug in prisma-next
|
|
615
618
|
return INIT_EXIT_INTERNAL_ERROR;
|
|
616
|
-
case '
|
|
619
|
+
case 'CLI.INIT_SKILL_INSTALL_FAILED': // skill install failed
|
|
617
620
|
return INIT_EXIT_SKILL_INSTALL_FAILED;
|
|
618
621
|
default:
|
|
619
622
|
// Any unexpected code is treated as an internal bug rather than
|
|
@@ -844,7 +847,7 @@ function formatCatalogWarning(
|
|
|
844
847
|
/**
|
|
845
848
|
* Recognised pnpm error signatures that justify a fallback to npm.
|
|
846
849
|
*
|
|
847
|
-
* These patterns indicate the published
|
|
850
|
+
* These patterns indicate the published artifact itself is at fault
|
|
848
851
|
* (a leaked `workspace:*` or `catalog:` specifier), not the user's
|
|
849
852
|
* environment — pnpm is faithfully reporting "I cannot resolve this
|
|
850
853
|
* registry version", and npm is willing to install it because npm
|
|
@@ -24,10 +24,9 @@ export const InitOutputSchema = type({
|
|
|
24
24
|
filesWritten: 'string[]',
|
|
25
25
|
/**
|
|
26
26
|
* FR9.1 — files removed from disk during this run. Populated only on
|
|
27
|
-
* re-init when previously-emitted contract
|
|
28
|
-
* `contract.d.ts`, `
|
|
29
|
-
*
|
|
30
|
-
* green-field init.
|
|
27
|
+
* re-init when previously-emitted contract artifacts (`contract.json`,
|
|
28
|
+
* `contract.d.ts`, `ops.json`, `migration.json`) were left behind by an
|
|
29
|
+
* earlier run. Empty on a green-field init.
|
|
31
30
|
*/
|
|
32
31
|
filesDeleted: 'string[]',
|
|
33
32
|
packagesInstalled: {
|
|
@@ -80,7 +79,7 @@ export function renderInitOutro(ui: TerminalUI, output: InitOutput, flags: Globa
|
|
|
80
79
|
|
|
81
80
|
if (output.filesDeleted.length > 0) {
|
|
82
81
|
lines.push('');
|
|
83
|
-
lines.push('Files deleted (stale contract
|
|
82
|
+
lines.push('Files deleted (stale contract artifacts):');
|
|
84
83
|
for (const file of output.filesDeleted) {
|
|
85
84
|
lines.push(` • ${file}`);
|
|
86
85
|
}
|
|
@@ -4,41 +4,40 @@ import { join } from 'pathe';
|
|
|
4
4
|
/**
|
|
5
5
|
* Filenames the contract pipeline emits next to the user's schema source
|
|
6
6
|
* (`<schemaDir>/contract.json`, `<schemaDir>/contract.d.ts`, …). Mirrors
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* the schema-dir-relative `ARTIFACT_FILENAMES` in `hygiene-gitattributes.ts`
|
|
8
|
+
* (not that file's migrations-root-anchored store lines, which are outside
|
|
9
|
+
* this command's scope — reinit only ever touches `schemaDir`); kept as a
|
|
10
|
+
* separate constant here because the cleanup contract is target-agnostic
|
|
11
|
+
* and we deliberately do not want a stale artifact from a previous target
|
|
12
|
+
* lingering after a re-init.
|
|
11
13
|
*
|
|
12
|
-
* If a future emit pipeline produces an additional
|
|
13
|
-
* **and** to
|
|
14
|
-
*
|
|
15
|
-
* `init` is willing to delete on
|
|
14
|
+
* If a future emit pipeline produces an additional schema-dir artifact,
|
|
15
|
+
* add it here **and** to `ARTIFACT_FILENAMES` in `hygiene-gitattributes.ts`
|
|
16
|
+
* — the two stay in lockstep so the file `init` advertises as
|
|
17
|
+
* `linguist-generated` is exactly the file `init` is willing to delete on
|
|
18
|
+
* re-init.
|
|
16
19
|
*/
|
|
17
|
-
const
|
|
20
|
+
const ARTIFACT_FILENAMES: readonly string[] = [
|
|
18
21
|
'contract.json',
|
|
19
22
|
'contract.d.ts',
|
|
20
|
-
'end-contract.json',
|
|
21
|
-
'end-contract.d.ts',
|
|
22
|
-
'start-contract.json',
|
|
23
|
-
'start-contract.d.ts',
|
|
24
23
|
'ops.json',
|
|
25
24
|
'migration.json',
|
|
26
25
|
];
|
|
27
26
|
|
|
28
27
|
/**
|
|
29
|
-
* Returns the schema-relative paths of stale contract
|
|
28
|
+
* Returns the schema-relative paths of stale contract artifacts the
|
|
30
29
|
* previous `init` run (or a `contract emit`) left behind in `schemaDir`.
|
|
31
30
|
* Paths are returned relative to `baseDir` so the caller can plumb them
|
|
32
31
|
* into `filesWritten`-style logging without re-deriving the path.
|
|
33
32
|
*
|
|
34
33
|
* Pure function: no filesystem mutation. Used by `runInit`'s precondition
|
|
35
34
|
* phase (FR6.2 / NFR3 atomicity) so a downstream parse failure leaves
|
|
36
|
-
* the
|
|
35
|
+
* the artifacts on disk and the project byte-identical to its pre-init
|
|
37
36
|
* state.
|
|
38
37
|
*/
|
|
39
|
-
export function
|
|
38
|
+
export function findStaleArtifacts(baseDir: string, schemaDir: string): readonly string[] {
|
|
40
39
|
const result: string[] = [];
|
|
41
|
-
for (const filename of
|
|
40
|
+
for (const filename of ARTIFACT_FILENAMES) {
|
|
42
41
|
const rel = join(schemaDir, filename);
|
|
43
42
|
if (existsSync(join(baseDir, rel))) {
|
|
44
43
|
result.push(rel);
|
package/src/commands/migrate.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readdirSync,
|
|
1
|
+
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import { loadConfig } from '@prisma-next/config-loader';
|
|
4
4
|
import { createControlStack } from '@prisma-next/framework-components/control';
|
|
@@ -7,6 +7,11 @@ import type {
|
|
|
7
7
|
IntegrityViolation,
|
|
8
8
|
} from '@prisma-next/migration-tools/aggregate';
|
|
9
9
|
import { loadContractSpaceAggregate } from '@prisma-next/migration-tools/aggregate';
|
|
10
|
+
import {
|
|
11
|
+
contractSnapshotDir,
|
|
12
|
+
readContractSnapshotJson,
|
|
13
|
+
} from '@prisma-next/migration-tools/contract-snapshot-store';
|
|
14
|
+
import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
10
15
|
import type { MigrationGraph } from '@prisma-next/migration-tools/graph';
|
|
11
16
|
import { verifyMigrationHash } from '@prisma-next/migration-tools/hash';
|
|
12
17
|
import type { OnDiskMigrationPackage } from '@prisma-next/migration-tools/package';
|
|
@@ -18,7 +23,6 @@ import type { Refs } from '@prisma-next/migration-tools/refs';
|
|
|
18
23
|
import {
|
|
19
24
|
isValidSpaceId,
|
|
20
25
|
listContractSpaceDirectories,
|
|
21
|
-
RESERVED_SPACE_SUBDIR_NAMES,
|
|
22
26
|
spaceMigrationDirectory,
|
|
23
27
|
spaceRefsDirectory,
|
|
24
28
|
} from '@prisma-next/migration-tools/spaces';
|
|
@@ -83,7 +87,7 @@ function checkFileExists(
|
|
|
83
87
|
if (!existsSync(join(dirPath, fileName))) {
|
|
84
88
|
return {
|
|
85
89
|
space: spaceId,
|
|
86
|
-
code: '
|
|
90
|
+
code: 'MIGRATION.CHECK_FILE_MISSING',
|
|
87
91
|
where: migrationFileRelative(dirPath, fileName),
|
|
88
92
|
why: `${fileName} is missing from ${dirName}`,
|
|
89
93
|
fix: 'Re-emit the migration package or restore from version control.',
|
|
@@ -92,32 +96,57 @@ function checkFileExists(
|
|
|
92
96
|
return null;
|
|
93
97
|
}
|
|
94
98
|
|
|
95
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Snapshot-store consistency check for one migration package (D6.5):
|
|
101
|
+
* absent store entry is not an issue (runner independence, ADR 199 — a
|
|
102
|
+
* package dir with only `migration.json` + `ops.json` is legitimate); a
|
|
103
|
+
* present entry whose inner `storage.storageHash` disagrees with
|
|
104
|
+
* `pkg.metadata.to` is `MIGRATION.CHECK_SNAPSHOT_HASH_MISMATCH`; an unparseable store
|
|
105
|
+
* entry (or a malformed `to`) is `MIGRATION.CHECK_SNAPSHOT_UNPARSEABLE`.
|
|
106
|
+
*/
|
|
107
|
+
async function checkSnapshotConsistency(
|
|
96
108
|
spaceId: string,
|
|
97
109
|
pkg: OnDiskMigrationPackage,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
110
|
+
migrationsDir: string,
|
|
111
|
+
): Promise<CheckFailure | null> {
|
|
112
|
+
let snapshotDir: string;
|
|
101
113
|
try {
|
|
102
|
-
|
|
103
|
-
const storage = raw['storage'] as Record<string, unknown> | undefined;
|
|
104
|
-
const snapshotHash = storage?.['storageHash'];
|
|
105
|
-
if (typeof snapshotHash === 'string' && snapshotHash !== pkg.metadata.to) {
|
|
106
|
-
return {
|
|
107
|
-
space: spaceId,
|
|
108
|
-
code: 'PN-MIG-CHECK-005',
|
|
109
|
-
where: migrationPathRelative(pkg.dirPath),
|
|
110
|
-
why: `Migration "${pkg.dirName}" declares to=${pkg.metadata.to} but end-contract.json has storageHash=${snapshotHash}`,
|
|
111
|
-
fix: 'Re-emit the migration package so migration.json and end-contract.json agree.',
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
+
snapshotDir = contractSnapshotDir(migrationsDir, pkg.metadata.to);
|
|
114
115
|
} catch {
|
|
115
116
|
return {
|
|
116
117
|
space: spaceId,
|
|
117
|
-
code: '
|
|
118
|
+
code: 'MIGRATION.CHECK_SNAPSHOT_UNPARSEABLE',
|
|
118
119
|
where: migrationPathRelative(pkg.dirPath),
|
|
119
|
-
why: `Migration "${pkg.dirName}"
|
|
120
|
-
fix: 'Re-emit the migration package
|
|
120
|
+
why: `Migration "${pkg.dirName}" declares to="${pkg.metadata.to}", which is not a well-formed contract snapshot hash.`,
|
|
121
|
+
fix: 'Re-emit the migration package so migration.json declares a valid `sha256:<64hex>` to-hash.',
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let raw: unknown;
|
|
126
|
+
try {
|
|
127
|
+
raw = await readContractSnapshotJson(migrationsDir, pkg.metadata.to);
|
|
128
|
+
} catch (error) {
|
|
129
|
+
if (MigrationToolsError.is(error) && error.code === 'MIGRATION.CONTRACT_SNAPSHOT_MISSING') {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
space: spaceId,
|
|
134
|
+
code: 'MIGRATION.CHECK_SNAPSHOT_UNPARSEABLE',
|
|
135
|
+
where: migrationPathRelative(pkg.dirPath),
|
|
136
|
+
why: `Migration "${pkg.dirName}" has an unparseable contract snapshot at ${snapshotDir}/contract.json.`,
|
|
137
|
+
fix: 'Restore migrations/snapshots/ from version control, or re-run the command that produced this migration to regenerate its snapshot.',
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
const record = raw !== null && typeof raw === 'object' ? (raw as Record<string, unknown>) : {};
|
|
141
|
+
const storage = record['storage'] as Record<string, unknown> | undefined;
|
|
142
|
+
const snapshotHash = storage?.['storageHash'];
|
|
143
|
+
if (typeof snapshotHash === 'string' && snapshotHash !== pkg.metadata.to) {
|
|
144
|
+
return {
|
|
145
|
+
space: spaceId,
|
|
146
|
+
code: 'MIGRATION.CHECK_SNAPSHOT_HASH_MISMATCH',
|
|
147
|
+
where: migrationPathRelative(pkg.dirPath),
|
|
148
|
+
why: `Migration "${pkg.dirName}" declares to=${pkg.metadata.to} but its contract snapshot has storageHash=${snapshotHash}`,
|
|
149
|
+
fix: 'Re-emit the migration package so migration.json and its contract snapshot agree.',
|
|
121
150
|
};
|
|
122
151
|
}
|
|
123
152
|
return null;
|
|
@@ -137,6 +166,8 @@ export interface CheckSpace {
|
|
|
137
166
|
readonly graph: MigrationGraph;
|
|
138
167
|
readonly migrationsDir: string;
|
|
139
168
|
readonly refsDir: string;
|
|
169
|
+
/** Migrations root the contract-snapshot store lives under — shared by every space. */
|
|
170
|
+
readonly projectMigrationsDir: string;
|
|
140
171
|
}
|
|
141
172
|
|
|
142
173
|
/**
|
|
@@ -152,9 +183,7 @@ export async function enumerateCheckSpaces(
|
|
|
152
183
|
projectMigrationsDir: string,
|
|
153
184
|
): Promise<readonly CheckSpace[]> {
|
|
154
185
|
const candidateDirs = await listContractSpaceDirectories(projectMigrationsDir);
|
|
155
|
-
const onDiskSpaceIds = new Set(
|
|
156
|
-
candidateDirs.filter((name) => !RESERVED_SPACE_SUBDIR_NAMES.has(name)).filter(isValidSpaceId),
|
|
157
|
-
);
|
|
186
|
+
const onDiskSpaceIds = new Set(candidateDirs.filter(isValidSpaceId));
|
|
158
187
|
const spaces: CheckSpace[] = [];
|
|
159
188
|
for (const space of aggregate.spaces()) {
|
|
160
189
|
const spaceId = space.spaceId;
|
|
@@ -168,6 +197,7 @@ export async function enumerateCheckSpaces(
|
|
|
168
197
|
graph: space.graph(),
|
|
169
198
|
migrationsDir,
|
|
170
199
|
refsDir: spaceRefsDirectory(migrationsDir),
|
|
200
|
+
projectMigrationsDir,
|
|
171
201
|
});
|
|
172
202
|
}
|
|
173
203
|
return spaces;
|
|
@@ -212,7 +242,7 @@ function checkReachability(space: CheckSpace): readonly CheckFailure[] {
|
|
|
212
242
|
if (!isReachable) {
|
|
213
243
|
failures.push({
|
|
214
244
|
space: space.spaceId,
|
|
215
|
-
code: '
|
|
245
|
+
code: 'MIGRATION.CHECK_UNREACHABLE_MIGRATION',
|
|
216
246
|
where: migrationPathRelative(pkg.dirPath),
|
|
217
247
|
why: `Migration "${pkg.dirName}" starts from ${pkg.metadata.from} which no other migration produces`,
|
|
218
248
|
fix: 'This migration is unreachable in the graph. Delete it or re-emit a connecting migration.',
|
|
@@ -228,7 +258,7 @@ function checkDanglingRefs(space: CheckSpace): readonly CheckFailure[] {
|
|
|
228
258
|
if (!space.graph.nodes.has(entry.hash)) {
|
|
229
259
|
failures.push({
|
|
230
260
|
space: space.spaceId,
|
|
231
|
-
code: '
|
|
261
|
+
code: 'MIGRATION.CHECK_DANGLING_REF',
|
|
232
262
|
where: relative(process.cwd(), join(space.refsDir, `${name}.json`)),
|
|
233
263
|
why: `Ref "${name}" points at ${entry.hash} which does not exist in the migration graph`,
|
|
234
264
|
fix: `Update the ref with \`prisma-next ref set ${name} <valid-hash>\` or delete it.`,
|
|
@@ -238,12 +268,15 @@ function checkDanglingRefs(space: CheckSpace): readonly CheckFailure[] {
|
|
|
238
268
|
return failures;
|
|
239
269
|
}
|
|
240
270
|
|
|
241
|
-
function checkSpace(space: CheckSpace): readonly CheckFailure[] {
|
|
271
|
+
async function checkSpace(space: CheckSpace): Promise<readonly CheckFailure[]> {
|
|
272
|
+
const snapshotFailures = await Promise.all(
|
|
273
|
+
space.packages.map((pkg) =>
|
|
274
|
+
checkSnapshotConsistency(space.spaceId, pkg, space.projectMigrationsDir),
|
|
275
|
+
),
|
|
276
|
+
);
|
|
242
277
|
return [
|
|
243
278
|
...checkManifestFilesPresent(space),
|
|
244
|
-
...
|
|
245
|
-
.map((pkg) => checkSnapshotConsistency(space.spaceId, pkg))
|
|
246
|
-
.filter((f): f is CheckFailure => f !== null),
|
|
279
|
+
...snapshotFailures.filter((f): f is CheckFailure => f !== null),
|
|
247
280
|
...checkReachability(space),
|
|
248
281
|
...checkDanglingRefs(space),
|
|
249
282
|
];
|
|
@@ -272,9 +305,9 @@ export interface RunMigrationCheckInputs {
|
|
|
272
305
|
* Aggregate-integrity violations (which already span every space) are folded
|
|
273
306
|
* in by the caller, not here.
|
|
274
307
|
*/
|
|
275
|
-
export function runMigrationCheck(
|
|
308
|
+
export async function runMigrationCheck(
|
|
276
309
|
inputs: RunMigrationCheckInputs,
|
|
277
|
-
): Result<MigrationCheckResult, CliStructuredError
|
|
310
|
+
): Promise<Result<MigrationCheckResult, CliStructuredError>> {
|
|
278
311
|
const { spaces, spaceFilter } = inputs;
|
|
279
312
|
|
|
280
313
|
if (spaceFilter !== undefined && !isValidSpaceId(spaceFilter)) {
|
|
@@ -287,7 +320,7 @@ export function runMigrationCheck(
|
|
|
287
320
|
const scopedSpaces =
|
|
288
321
|
spaceFilter !== undefined ? spaces.filter((s) => s.spaceId === spaceFilter) : spaces;
|
|
289
322
|
|
|
290
|
-
const failures = scopedSpaces.
|
|
323
|
+
const failures = (await Promise.all(scopedSpaces.map(checkSpace))).flat();
|
|
291
324
|
if (failures.length === 0) {
|
|
292
325
|
return ok({ ok: true, failures: [], summary: 'All checks passed' });
|
|
293
326
|
}
|
|
@@ -365,7 +398,7 @@ async function executeMigrationCheckCommand(
|
|
|
365
398
|
});
|
|
366
399
|
}
|
|
367
400
|
|
|
368
|
-
const checkResult = runMigrationCheck({
|
|
401
|
+
const checkResult = await runMigrationCheck({
|
|
369
402
|
spaces,
|
|
370
403
|
...(options.space !== undefined ? { spaceFilter: options.space } : {}),
|
|
371
404
|
});
|
|
@@ -522,7 +555,7 @@ async function checkSingleTarget(
|
|
|
522
555
|
matchedPkg = hits[0]!.pkg;
|
|
523
556
|
} else if (bestParseFailure !== undefined) {
|
|
524
557
|
// The ref didn't resolve in any in-scope space — surface the most informative
|
|
525
|
-
// parse failure through the shared ref-resolution envelope (
|
|
558
|
+
// parse failure through the shared ref-resolution envelope (CONTRACT.VERIFY_FAILED) the
|
|
526
559
|
// earlier work established, rather than a bespoke string. (Ref-resolved-but-
|
|
527
560
|
// no-package falls through to the "not found on disk" result below.)
|
|
528
561
|
return { error: mapRefResolutionError(bestParseFailure), exitCode: PRECONDITION };
|
|
@@ -551,14 +584,18 @@ async function checkSingleTarget(
|
|
|
551
584
|
if (!verification.ok) {
|
|
552
585
|
failures.push({
|
|
553
586
|
space: matchedSpace.spaceId,
|
|
554
|
-
code: '
|
|
587
|
+
code: 'MIGRATION.CHECK_HASH_MISMATCH',
|
|
555
588
|
where: migrationFileRelative(matchedPkg.dirPath, 'migration.json'),
|
|
556
589
|
why: `Stored hash ${verification.storedHash} does not match recomputed hash ${verification.computedHash}`,
|
|
557
590
|
fix: 'Re-emit the migration package or restore from version control.',
|
|
558
591
|
});
|
|
559
592
|
}
|
|
560
593
|
|
|
561
|
-
const snapshotFailure = checkSnapshotConsistency(
|
|
594
|
+
const snapshotFailure = await checkSnapshotConsistency(
|
|
595
|
+
matchedSpace.spaceId,
|
|
596
|
+
matchedPkg,
|
|
597
|
+
matchedSpace.projectMigrationsDir,
|
|
598
|
+
);
|
|
562
599
|
if (snapshotFailure) failures.push(snapshotFailure);
|
|
563
600
|
|
|
564
601
|
const resolvedSpaceId = matchedSpace.spaceId !== 'app' ? matchedSpace.spaceId : undefined;
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
APP_SPACE_ID,
|
|
10
10
|
isValidSpaceId,
|
|
11
11
|
listContractSpaceDirectories,
|
|
12
|
-
RESERVED_SPACE_SUBDIR_NAMES,
|
|
13
12
|
} from '@prisma-next/migration-tools/spaces';
|
|
14
13
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
15
14
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
@@ -81,10 +80,7 @@ export function listRefsByContractHash(
|
|
|
81
80
|
|
|
82
81
|
async function orderedOnDiskSpaceIds(projectMigrationsDir: string): Promise<readonly string[]> {
|
|
83
82
|
const candidateDirs = await listContractSpaceDirectories(projectMigrationsDir);
|
|
84
|
-
return candidateDirs
|
|
85
|
-
.filter((name) => !RESERVED_SPACE_SUBDIR_NAMES.has(name))
|
|
86
|
-
.filter(isValidSpaceId)
|
|
87
|
-
.sort(compareSpaceIds);
|
|
83
|
+
return candidateDirs.filter(isValidSpaceId).sort(compareSpaceIds);
|
|
88
84
|
}
|
|
89
85
|
|
|
90
86
|
/**
|