@prisma-next/cli 0.4.2 → 0.4.3
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 +56 -21
- package/dist/cli-errors-By1iVE3z.mjs +34 -0
- package/dist/cli-errors-By1iVE3z.mjs.map +1 -0
- package/dist/{cli-errors-BFYgBH3L.d.mts → cli-errors-DDeVsP2Y.d.mts} +1 -0
- package/dist/cli.mjs +19 -16
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-CrsnY58k.mjs → client-1JqqkiC7.mjs} +45 -20
- package/dist/client-1JqqkiC7.mjs.map +1 -0
- package/dist/commands/contract-emit.d.mts.map +1 -1
- package/dist/commands/contract-emit.mjs +7 -7
- package/dist/commands/contract-infer.d.mts.map +1 -1
- package/dist/commands/contract-infer.mjs +8 -8
- package/dist/commands/db-init.d.mts.map +1 -1
- package/dist/commands/db-init.mjs +11 -10
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.mjs +8 -8
- package/dist/commands/db-sign.mjs +8 -8
- package/dist/commands/db-update.mjs +10 -10
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.mjs +10 -10
- package/dist/commands/migration-apply.d.mts +5 -2
- package/dist/commands/migration-apply.d.mts.map +1 -1
- package/dist/commands/migration-apply.mjs +56 -57
- package/dist/commands/migration-apply.mjs.map +1 -1
- package/dist/commands/migration-new.d.mts.map +1 -1
- package/dist/commands/migration-new.mjs +26 -32
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +14 -5
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +45 -48
- package/dist/commands/migration-plan.mjs.map +1 -1
- package/dist/commands/migration-ref.d.mts +1 -1
- package/dist/commands/migration-ref.d.mts.map +1 -1
- package/dist/commands/migration-ref.mjs +6 -10
- package/dist/commands/migration-ref.mjs.map +1 -1
- package/dist/commands/migration-show.d.mts +13 -7
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +28 -29
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +23 -5
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +8 -8
- package/dist/{config-loader-C25b63rJ.mjs → config-loader-ih8ViDb_.mjs} +2 -2
- package/dist/config-loader-ih8ViDb_.mjs.map +1 -0
- package/dist/config-loader.mjs +1 -1
- package/dist/contract-emit-DS5NzZh2.mjs +6 -0
- package/dist/contract-emit-RZBWzkop.mjs +329 -0
- package/dist/contract-emit-RZBWzkop.mjs.map +1 -0
- package/dist/contract-emit-rt_Nmdwq.mjs +150 -0
- package/dist/contract-emit-rt_Nmdwq.mjs.map +1 -0
- package/dist/{contract-enrichment-CAOELa-H.mjs → contract-enrichment-4Ptgw3Pe.mjs} +1 -1
- package/dist/{contract-enrichment-CAOELa-H.mjs.map → contract-enrichment-4Ptgw3Pe.mjs.map} +1 -1
- package/dist/{contract-infer-D9cC3rJm.mjs → contract-infer-Cf5J2wVg.mjs} +11 -19
- package/dist/contract-infer-Cf5J2wVg.mjs.map +1 -0
- package/dist/exports/control-api.d.mts +86 -21
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +7 -7
- package/dist/exports/index.mjs +8 -8
- package/dist/{framework-components-Cr--XBKy.mjs → framework-components-Bgcre3Z6.mjs} +2 -2
- package/dist/{framework-components-Cr--XBKy.mjs.map → framework-components-Bgcre3Z6.mjs.map} +1 -1
- package/dist/{init-m8x0UoPY.mjs → init-DAbQMxIR.mjs} +5 -5
- package/dist/{init-m8x0UoPY.mjs.map → init-DAbQMxIR.mjs.map} +1 -1
- package/dist/{inspect-live-schema-yrHAvG71.mjs → inspect-live-schema-LWtXfxm_.mjs} +9 -9
- package/dist/inspect-live-schema-LWtXfxm_.mjs.map +1 -0
- package/dist/migration-cli.d.mts +41 -11
- package/dist/migration-cli.d.mts.map +1 -1
- package/dist/migration-cli.mjs +308 -84
- package/dist/migration-cli.mjs.map +1 -1
- package/dist/{migration-command-scaffold-B3B09et6.mjs → migration-command-scaffold-CU452v9h.mjs} +7 -7
- package/dist/{migration-command-scaffold-B3B09et6.mjs.map → migration-command-scaffold-CU452v9h.mjs.map} +1 -1
- package/dist/{migration-status-DUMiH8_G.mjs → migration-status-DoPrFIOQ.mjs} +114 -57
- package/dist/migration-status-DoPrFIOQ.mjs.map +1 -0
- package/dist/{migrations-Bo5WtTla.mjs → migrations-MEoKMiV5.mjs} +42 -21
- package/dist/migrations-MEoKMiV5.mjs.map +1 -0
- package/dist/{progress-adapter-DvQWB1nK.mjs → progress-adapter-DgRGldpT.mjs} +1 -1
- package/dist/{progress-adapter-DvQWB1nK.mjs.map → progress-adapter-DgRGldpT.mjs.map} +1 -1
- package/dist/{result-handler-Ba3zWQsI.mjs → result-handler-Ch6hVnOo.mjs} +34 -19
- package/dist/{result-handler-Ba3zWQsI.mjs.map → result-handler-Ch6hVnOo.mjs.map} +1 -1
- package/dist/{terminal-ui-C3ZLwQxK.mjs → terminal-ui-u2YgKghu.mjs} +1 -1
- package/dist/{terminal-ui-C3ZLwQxK.mjs.map → terminal-ui-u2YgKghu.mjs.map} +1 -1
- package/dist/{verify-Bkycc-Tf.mjs → verify-BT9tgCOH.mjs} +2 -2
- package/dist/{verify-Bkycc-Tf.mjs.map → verify-BT9tgCOH.mjs.map} +1 -1
- package/package.json +16 -15
- package/src/cli.ts +32 -6
- package/src/commands/contract-emit.ts +67 -163
- package/src/commands/contract-infer.ts +7 -20
- package/src/commands/db-init.ts +1 -0
- package/src/commands/db-update.ts +1 -1
- package/src/commands/inspect-live-schema.ts +10 -5
- package/src/commands/migration-apply.ts +84 -63
- package/src/commands/migration-new.ts +28 -34
- package/src/commands/migration-plan.ts +80 -56
- package/src/commands/migration-ref.ts +8 -7
- package/src/commands/migration-show.ts +53 -36
- package/src/commands/migration-status.ts +194 -58
- package/src/config-path-validation.ts +0 -1
- package/src/control-api/client.ts +21 -0
- package/src/control-api/operations/contract-emit.ts +198 -115
- package/src/control-api/operations/db-init.ts +10 -6
- package/src/control-api/operations/db-update.ts +10 -6
- package/src/control-api/operations/migration-apply.ts +30 -9
- package/src/control-api/types.ts +69 -7
- package/src/exports/control-api.ts +2 -1
- package/src/migration-cli.ts +445 -122
- package/src/utils/cli-errors.ts +49 -2
- package/src/utils/command-helpers.ts +45 -23
- package/src/utils/emit-queue.ts +26 -0
- package/src/utils/formatters/graph-migration-mapper.ts +7 -3
- package/src/utils/formatters/migrations.ts +62 -26
- package/src/utils/publish-contract-artifact-pair.ts +134 -0
- package/dist/cli-errors-Cd79vmTH.mjs +0 -5
- package/dist/client-CrsnY58k.mjs.map +0 -1
- package/dist/config-loader-C25b63rJ.mjs.map +0 -1
- package/dist/contract-emit-DxgyXrqV.mjs +0 -6
- package/dist/contract-emit-NJ01hiiv.mjs +0 -195
- package/dist/contract-emit-NJ01hiiv.mjs.map +0 -1
- package/dist/contract-emit-V5SSitUT.mjs +0 -122
- package/dist/contract-emit-V5SSitUT.mjs.map +0 -1
- package/dist/contract-infer-D9cC3rJm.mjs.map +0 -1
- package/dist/extract-operation-statements-DsFfxXVZ.mjs +0 -13
- package/dist/extract-operation-statements-DsFfxXVZ.mjs.map +0 -1
- package/dist/extract-sql-ddl-D9UbZDyz.mjs +0 -26
- package/dist/extract-sql-ddl-D9UbZDyz.mjs.map +0 -1
- package/dist/inspect-live-schema-yrHAvG71.mjs.map +0 -1
- package/dist/migration-status-DUMiH8_G.mjs.map +0 -1
- package/dist/migrations-Bo5WtTla.mjs.map +0 -1
- package/dist/validate-contract-deps-B_Cs29TL.mjs +0 -37
- package/dist/validate-contract-deps-B_Cs29TL.mjs.map +0 -1
- package/src/control-api/operations/extract-operation-statements.ts +0 -14
- package/src/control-api/operations/extract-sql-ddl.ts +0 -47
|
@@ -1,30 +1,19 @@
|
|
|
1
|
-
import { t as loadConfig } from "../config-loader-
|
|
2
|
-
import { _ as errorUnexpected, c as errorDriverRequired, h as errorTargetMigrationNotSupported, m as errorRuntime, o as errorDatabaseConnectionRequired, t as CliStructuredError } from "../cli-errors-
|
|
3
|
-
import "../framework-components-
|
|
4
|
-
import { t as
|
|
5
|
-
import { t as
|
|
6
|
-
import {
|
|
7
|
-
import { t as formatMigrationApplyCommandOutput } from "../migrations-
|
|
1
|
+
import { t as loadConfig } from "../config-loader-ih8ViDb_.mjs";
|
|
2
|
+
import { _ as errorUnexpected, c as errorDriverRequired, h as errorTargetMigrationNotSupported, m as errorRuntime, o as errorDatabaseConnectionRequired, t as CliStructuredError, v as mapMigrationToolsError } from "../cli-errors-By1iVE3z.mjs";
|
|
3
|
+
import "../framework-components-Bgcre3Z6.mjs";
|
|
4
|
+
import { t as TerminalUI } from "../terminal-ui-u2YgKghu.mjs";
|
|
5
|
+
import { a as loadMigrationPackages, d as setCommandDescriptions, f as setCommandExamples, g as parseGlobalFlags, h as toStructuralEdge, l as resolveMigrationPaths, m as toPathDecisionResult, n as addGlobalOptions, o as maskConnectionUrl, p as targetSupportsMigrations, r as collectDeclaredInvariants, s as readContractEnvelope, t as handleResult, y as formatStyledHeader } from "../result-handler-Ch6hVnOo.mjs";
|
|
6
|
+
import { t as createControlClient } from "../client-1JqqkiC7.mjs";
|
|
7
|
+
import { t as formatMigrationApplyCommandOutput } from "../migrations-MEoKMiV5.mjs";
|
|
8
8
|
import { Command } from "commander";
|
|
9
|
+
import { ifDefined } from "@prisma-next/utils/defined";
|
|
9
10
|
import { notOk, ok } from "@prisma-next/utils/result";
|
|
11
|
+
import { findPathWithDecision } from "@prisma-next/migration-tools/migration-graph";
|
|
10
12
|
import { EMPTY_CONTRACT_HASH } from "@prisma-next/migration-tools/constants";
|
|
11
|
-
import {
|
|
12
|
-
import { verifyMigrationBundle } from "@prisma-next/migration-tools/attestation";
|
|
13
|
+
import { MigrationToolsError, errorNoInvariantPath, errorUnknownInvariant } from "@prisma-next/migration-tools/errors";
|
|
13
14
|
import { readRefs, resolveRef } from "@prisma-next/migration-tools/refs";
|
|
14
|
-
import { MigrationToolsError } from "@prisma-next/migration-tools/types";
|
|
15
15
|
|
|
16
16
|
//#region src/commands/migration-apply.ts
|
|
17
|
-
function mapMigrationToolsError(error) {
|
|
18
|
-
if (MigrationToolsError.is(error)) return errorRuntime(error.message, {
|
|
19
|
-
why: error.why,
|
|
20
|
-
fix: error.fix,
|
|
21
|
-
meta: {
|
|
22
|
-
code: error.code,
|
|
23
|
-
...error.details ?? {}
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
return errorUnexpected(error instanceof Error ? error.message : String(error), { why: `Unexpected error during migration apply: ${error instanceof Error ? error.message : String(error)}` });
|
|
27
|
-
}
|
|
28
17
|
function mapApplyFailure(failure) {
|
|
29
18
|
return errorRuntime(failure.summary, {
|
|
30
19
|
why: failure.why ?? "Migration runner failed",
|
|
@@ -35,10 +24,11 @@ function mapApplyFailure(failure) {
|
|
|
35
24
|
function packageToStep(pkg) {
|
|
36
25
|
return {
|
|
37
26
|
dirName: pkg.dirName,
|
|
38
|
-
from: pkg.
|
|
39
|
-
to: pkg.
|
|
40
|
-
toContract: pkg.
|
|
41
|
-
operations: pkg.ops
|
|
27
|
+
from: pkg.metadata.from,
|
|
28
|
+
to: pkg.metadata.to,
|
|
29
|
+
toContract: pkg.metadata.toContract,
|
|
30
|
+
operations: pkg.ops,
|
|
31
|
+
providedInvariants: pkg.metadata.providedInvariants
|
|
42
32
|
};
|
|
43
33
|
}
|
|
44
34
|
async function executeMigrationApplyCommand(options, flags, ui, startTime) {
|
|
@@ -51,24 +41,24 @@ async function executeMigrationApplyCommand(options, flags, ui, startTime) {
|
|
|
51
41
|
}));
|
|
52
42
|
if (!config.driver) return notOk(errorDriverRequired({ why: "Config.driver is required for migration apply" }));
|
|
53
43
|
if (!targetSupportsMigrations(config.target)) return notOk(errorTargetMigrationNotSupported({ why: `Target "${config.target.id}" does not support migrations` }));
|
|
54
|
-
let
|
|
55
|
-
let
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
destinationHash = (await readContractEnvelope(config)).storageHash;
|
|
44
|
+
let refEntry;
|
|
45
|
+
let envelopeHash;
|
|
46
|
+
const refName = options.ref;
|
|
47
|
+
if (refName) try {
|
|
48
|
+
refEntry = resolveRef(await readRefs(refsDir), refName);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
else try {
|
|
54
|
+
envelopeHash = (await readContractEnvelope(config)).storageHash;
|
|
66
55
|
} catch (error) {
|
|
67
56
|
return notOk(errorRuntime("Current contract is unavailable", {
|
|
68
57
|
why: `Failed to read contract: ${error instanceof Error ? error.message : String(error)}`,
|
|
69
58
|
fix: "Run `prisma-next contract emit` to generate a valid contract.json, then retry apply."
|
|
70
59
|
}));
|
|
71
60
|
}
|
|
61
|
+
const destinationHash = refEntry?.hash ?? envelopeHash;
|
|
72
62
|
if (!flags.json && !flags.quiet) {
|
|
73
63
|
const details = [{
|
|
74
64
|
label: "config",
|
|
@@ -96,23 +86,11 @@ async function executeMigrationApplyCommand(options, flags, ui, startTime) {
|
|
|
96
86
|
}
|
|
97
87
|
let migrations;
|
|
98
88
|
try {
|
|
99
|
-
migrations = await
|
|
89
|
+
migrations = await loadMigrationPackages(migrationsDir);
|
|
100
90
|
} catch (error) {
|
|
101
91
|
if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
|
|
102
92
|
throw error;
|
|
103
93
|
}
|
|
104
|
-
for (const bundle of migrations.bundles) {
|
|
105
|
-
const verified = verifyMigrationBundle(bundle);
|
|
106
|
-
if (!verified.ok) return notOk(errorRuntime(`Migration package is corrupt: ${bundle.dirName}`, {
|
|
107
|
-
why: `Stored migrationId "${verified.storedMigrationId}" does not match the recomputed hash "${verified.computedMigrationId}" for ${migrationsRelative}/${bundle.dirName}. The migration.json or ops.json has been edited or partially written since emit.`,
|
|
108
|
-
fix: `Re-emit the package by running \`node "${migrationsRelative}/${bundle.dirName}/migration.ts"\`, or restore the directory from version control.`,
|
|
109
|
-
meta: {
|
|
110
|
-
dirName: bundle.dirName,
|
|
111
|
-
storedMigrationId: verified.storedMigrationId,
|
|
112
|
-
computedMigrationId: verified.computedMigrationId
|
|
113
|
-
}
|
|
114
|
-
}));
|
|
115
|
-
}
|
|
116
94
|
const client = createControlClient({
|
|
117
95
|
family: config.family,
|
|
118
96
|
target: config.target,
|
|
@@ -123,6 +101,17 @@ async function executeMigrationApplyCommand(options, flags, ui, startTime) {
|
|
|
123
101
|
try {
|
|
124
102
|
await client.connect(dbConnection);
|
|
125
103
|
const marker = await client.readMarker();
|
|
104
|
+
if (refEntry && refEntry.invariants.length > 0) {
|
|
105
|
+
const declared = collectDeclaredInvariants(migrations.graph);
|
|
106
|
+
const known = new Set(declared);
|
|
107
|
+
for (const id of marker?.invariants ?? []) known.add(id);
|
|
108
|
+
const unknown = refEntry.invariants.filter((id) => !known.has(id));
|
|
109
|
+
if (unknown.length > 0) return notOk(mapMigrationToolsError(errorUnknownInvariant({
|
|
110
|
+
...ifDefined("refName", refName),
|
|
111
|
+
unknown,
|
|
112
|
+
declared: [...declared].sort()
|
|
113
|
+
})));
|
|
114
|
+
}
|
|
126
115
|
if (migrations.bundles.length === 0) {
|
|
127
116
|
if (marker?.storageHash) return notOk(errorRuntime("Database has state but no migrations exist", {
|
|
128
117
|
why: `The database marker hash "${marker.storageHash}" exists but no migrations were found in ${migrationsRelative}`,
|
|
@@ -173,8 +162,19 @@ async function executeMigrationApplyCommand(options, flags, ui, startTime) {
|
|
|
173
162
|
}
|
|
174
163
|
}));
|
|
175
164
|
const originHash = markerHash ?? EMPTY_CONTRACT_HASH;
|
|
176
|
-
const
|
|
177
|
-
|
|
165
|
+
const appliedInvariants = new Set(marker?.invariants ?? []);
|
|
166
|
+
const effectiveRequired = new Set((refEntry?.invariants ?? []).filter((id) => !appliedInvariants.has(id)));
|
|
167
|
+
const outcome = findPathWithDecision(migrations.graph, originHash, destinationHash, {
|
|
168
|
+
...ifDefined("refName", refName),
|
|
169
|
+
required: effectiveRequired
|
|
170
|
+
});
|
|
171
|
+
if (outcome.kind === "unsatisfiable") return notOk(mapMigrationToolsError(errorNoInvariantPath({
|
|
172
|
+
...ifDefined("refName", refName),
|
|
173
|
+
required: [...effectiveRequired].sort(),
|
|
174
|
+
missing: outcome.missing,
|
|
175
|
+
structuralPath: outcome.structuralPath.map(toStructuralEdge)
|
|
176
|
+
})));
|
|
177
|
+
if (outcome.kind === "unreachable") return notOk(errorRuntime("No migration path from current state to target", {
|
|
178
178
|
why: `Cannot find a path from "${originHash}" to target "${destinationHash}"`,
|
|
179
179
|
fix: "Check the migration history for gaps or inconsistencies.",
|
|
180
180
|
meta: {
|
|
@@ -182,9 +182,8 @@ async function executeMigrationApplyCommand(options, flags, ui, startTime) {
|
|
|
182
182
|
destinationHash
|
|
183
183
|
}
|
|
184
184
|
}));
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
if (pendingPath.length === 0) return ok({
|
|
185
|
+
const pathDecision = toPathDecisionResult(outcome.decision);
|
|
186
|
+
if (outcome.decision.selectedPath.length === 0) return ok({
|
|
188
187
|
ok: true,
|
|
189
188
|
migrationsApplied: 0,
|
|
190
189
|
migrationsTotal: 0,
|
|
@@ -196,7 +195,7 @@ async function executeMigrationApplyCommand(options, flags, ui, startTime) {
|
|
|
196
195
|
});
|
|
197
196
|
const bundleByDir = new Map(migrations.bundles.map((b) => [b.dirName, b]));
|
|
198
197
|
const pendingMigrations = [];
|
|
199
|
-
for (const migration of
|
|
198
|
+
for (const migration of outcome.decision.selectedPath) {
|
|
200
199
|
const pkg = bundleByDir.get(migration.dirName);
|
|
201
200
|
if (!pkg) return notOk(errorRuntime(`Migration package not found: ${migration.dirName}`, {
|
|
202
201
|
why: `The migration directory for path segment ${migration.from} → ${migration.to} was not found`,
|
|
@@ -215,7 +214,7 @@ async function executeMigrationApplyCommand(options, flags, ui, startTime) {
|
|
|
215
214
|
return ok({
|
|
216
215
|
ok: true,
|
|
217
216
|
migrationsApplied: value.migrationsApplied,
|
|
218
|
-
migrationsTotal:
|
|
217
|
+
migrationsTotal: outcome.decision.selectedPath.length,
|
|
219
218
|
markerHash: value.markerHash,
|
|
220
219
|
applied: value.applied,
|
|
221
220
|
summary: value.summary,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration-apply.mjs","names":["destinationHash: string","refName: string | undefined","details: Array<{ label: string; value: string }>","migrations: MigrationBundleSet","pendingMigrations: MigrationApplyStep[]"],"sources":["../../src/commands/migration-apply.ts"],"sourcesContent":["import { verifyMigrationBundle } from '@prisma-next/migration-tools/attestation';\nimport { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';\nimport { findPathWithDecision } from '@prisma-next/migration-tools/dag';\nimport { readRefs, resolveRef } from '@prisma-next/migration-tools/refs';\nimport type { MigrationBundle } from '@prisma-next/migration-tools/types';\nimport { MigrationToolsError } from '@prisma-next/migration-tools/types';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { Command } from 'commander';\n\nimport { loadConfig } from '../config-loader';\nimport { createControlClient } from '../control-api/client';\nimport type { MigrationApplyFailure, MigrationApplyStep } from '../control-api/types';\nimport {\n CliStructuredError,\n type CliStructuredError as CliStructuredErrorType,\n errorDatabaseConnectionRequired,\n errorDriverRequired,\n errorRuntime,\n errorTargetMigrationNotSupported,\n errorUnexpected,\n} from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n loadAllBundles,\n type MigrationBundleSet,\n maskConnectionUrl,\n readContractEnvelope,\n resolveMigrationPaths,\n setCommandDescriptions,\n setCommandExamples,\n targetSupportsMigrations,\n toPathDecisionResult,\n} from '../utils/command-helpers';\nimport { formatMigrationApplyCommandOutput } from '../utils/formatters/migrations';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';\nimport { handleResult } from '../utils/result-handler';\nimport { TerminalUI } from '../utils/terminal-ui';\n\ninterface MigrationApplyCommandOptions extends CommonCommandOptions {\n readonly db?: string;\n readonly config?: string;\n readonly ref?: string;\n}\n\nexport interface MigrationApplyResult {\n readonly ok: boolean;\n readonly migrationsApplied: number;\n readonly migrationsTotal: number;\n readonly markerHash: string;\n readonly applied: readonly {\n readonly dirName: string;\n readonly from: string;\n readonly to: string;\n readonly operationsExecuted: number;\n }[];\n readonly summary: string;\n readonly pathDecision?: {\n readonly fromHash: string;\n readonly toHash: string;\n readonly alternativeCount: number;\n readonly tieBreakReasons: readonly string[];\n readonly refName?: string;\n readonly selectedPath: readonly {\n readonly dirName: string;\n readonly migrationId: string;\n readonly from: string;\n readonly to: string;\n }[];\n };\n readonly timings: {\n readonly total: number;\n };\n}\n\nfunction mapMigrationToolsError(error: unknown): CliStructuredErrorType {\n if (MigrationToolsError.is(error)) {\n return errorRuntime(error.message, {\n why: error.why,\n fix: error.fix,\n meta: { code: error.code, ...(error.details ?? {}) },\n });\n }\n return errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Unexpected error during migration apply: ${error instanceof Error ? error.message : String(error)}`,\n });\n}\n\nfunction mapApplyFailure(failure: MigrationApplyFailure): CliStructuredErrorType {\n return errorRuntime(failure.summary, {\n why: failure.why ?? 'Migration runner failed',\n fix: 'Fix the issue and re-run `prisma-next migration apply` — previously applied migrations are preserved.',\n meta: failure.meta ?? {},\n });\n}\n\nfunction packageToStep(pkg: MigrationBundle): MigrationApplyStep {\n return {\n dirName: pkg.dirName,\n from: pkg.manifest.from,\n to: pkg.manifest.to,\n toContract: pkg.manifest.toContract,\n operations: pkg.ops,\n };\n}\n\nasync function executeMigrationApplyCommand(\n options: MigrationApplyCommandOptions,\n flags: GlobalFlags,\n ui: TerminalUI,\n startTime: number,\n): Promise<Result<MigrationApplyResult, CliStructuredErrorType>> {\n const config = await loadConfig(options.config);\n const { configPath, migrationsDir, migrationsRelative, refsDir } = resolveMigrationPaths(\n options.config,\n config,\n );\n\n const dbConnection = options.db ?? config.db?.connection;\n if (!dbConnection) {\n return notOk(\n errorDatabaseConnectionRequired({\n why: `Database connection is required for migration apply (set db.connection in ${configPath}, or pass --db <url>)`,\n commandName: 'migration apply',\n }),\n );\n }\n\n if (!config.driver) {\n return notOk(\n errorDriverRequired({\n why: 'Config.driver is required for migration apply',\n }),\n );\n }\n\n if (!targetSupportsMigrations(config.target)) {\n return notOk(\n errorTargetMigrationNotSupported({\n why: `Target \"${config.target.id}\" does not support migrations`,\n }),\n );\n }\n\n let destinationHash: string;\n let refName: string | undefined;\n\n if (options.ref) {\n refName = options.ref;\n try {\n const refs = await readRefs(refsDir);\n destinationHash = resolveRef(refs, refName).hash;\n } catch (error) {\n if (MigrationToolsError.is(error)) {\n return notOk(mapMigrationToolsError(error));\n }\n throw error;\n }\n } else {\n try {\n const envelope = await readContractEnvelope(config);\n destinationHash = envelope.storageHash;\n } catch (error) {\n return notOk(\n errorRuntime('Current contract is unavailable', {\n why: `Failed to read contract: ${error instanceof Error ? error.message : String(error)}`,\n fix: 'Run `prisma-next contract emit` to generate a valid contract.json, then retry apply.',\n }),\n );\n }\n }\n\n if (!flags.json && !flags.quiet) {\n const details: Array<{ label: string; value: string }> = [\n { label: 'config', value: configPath },\n { label: 'migrations', value: migrationsRelative },\n ];\n if (typeof dbConnection === 'string') {\n details.push({\n label: 'database',\n value: maskConnectionUrl(dbConnection),\n });\n }\n if (refName) {\n details.push({ label: 'ref', value: refName });\n }\n const header = formatStyledHeader({\n command: 'migration apply',\n description: 'Apply planned migrations to the database',\n url: 'https://pris.ly/migration-apply',\n details,\n flags,\n });\n ui.stderr(header);\n }\n\n // Read migrations and build migration chain model (offline — no DB needed)\n let migrations: MigrationBundleSet;\n try {\n migrations = await loadAllBundles(migrationsDir);\n } catch (error) {\n if (MigrationToolsError.is(error)) {\n return notOk(mapMigrationToolsError(error));\n }\n throw error;\n }\n\n // Defense in depth: re-hash every bundle and confirm the recorded\n // `migrationId` matches the on-disk `(manifest, ops)`. Catches FS\n // corruption, partial writes, and post-emit hand edits before we\n // start touching the database.\n for (const bundle of migrations.bundles) {\n const verified = verifyMigrationBundle(bundle);\n if (!verified.ok) {\n return notOk(\n errorRuntime(`Migration package is corrupt: ${bundle.dirName}`, {\n why: `Stored migrationId \"${verified.storedMigrationId}\" does not match the recomputed hash \"${verified.computedMigrationId}\" for ${migrationsRelative}/${bundle.dirName}. The migration.json or ops.json has been edited or partially written since emit.`,\n fix: `Re-emit the package by running \\`node \"${migrationsRelative}/${bundle.dirName}/migration.ts\"\\`, or restore the directory from version control.`,\n meta: {\n dirName: bundle.dirName,\n storedMigrationId: verified.storedMigrationId,\n computedMigrationId: verified.computedMigrationId,\n },\n }),\n );\n }\n }\n\n const client = createControlClient({\n family: config.family,\n target: config.target,\n adapter: config.adapter,\n driver: config.driver,\n extensionPacks: config.extensionPacks ?? [],\n });\n\n try {\n await client.connect(dbConnection);\n const marker = await client.readMarker();\n\n // --- No migrations on disk ---\n if (migrations.bundles.length === 0) {\n if (marker?.storageHash) {\n return notOk(\n errorRuntime('Database has state but no migrations exist', {\n why: `The database marker hash \"${marker.storageHash}\" exists but no migrations were found in ${migrationsRelative}`,\n fix: 'Ensure the migrations directory is correct. If the database was managed with `db init` or `db update`, run `prisma-next db sign` to update the marker.',\n meta: { markerHash: marker.storageHash, migrationsDir: migrationsRelative },\n }),\n );\n }\n // Non-empty contract + no migrations = user needs to plan first.\n if (destinationHash !== EMPTY_CONTRACT_HASH) {\n return notOk(\n errorRuntime('Current contract has no planned migrations', {\n why: `No migrations were found in ${migrationsRelative}, but current contract hash is \"${destinationHash}\"`,\n fix: 'Run `prisma-next migration plan` to create a migration for the current contract.',\n meta: { destinationHash, migrationsDir: migrationsRelative },\n }),\n );\n }\n // Empty contract + no migrations = nothing to do.\n return ok({\n ok: true,\n migrationsApplied: 0,\n migrationsTotal: 0,\n markerHash: EMPTY_CONTRACT_HASH,\n applied: [],\n summary: 'No migrations found',\n timings: { total: Date.now() - startTime },\n });\n }\n\n // --- Validate marker state ---\n\n // The empty sentinel should never appear in a real marker row — if it does,\n // the marker was corrupted and replaying all migrations would be dangerous.\n if (marker?.storageHash === EMPTY_CONTRACT_HASH) {\n return notOk(\n errorRuntime('Database marker contains the empty sentinel hash', {\n why: `The marker row exists but contains the empty sentinel value \"${EMPTY_CONTRACT_HASH}\". This should never happen — the marker should contain the hash of the last applied contract.`,\n fix: 'The marker is corrupted. Run `prisma-next db sign` to overwrite it with the correct contract hash, or drop and recreate the database.',\n meta: { markerHash: EMPTY_CONTRACT_HASH },\n }),\n );\n }\n\n const markerHash = marker?.storageHash;\n\n if (markerHash !== undefined && !migrations.graph.nodes.has(markerHash)) {\n return notOk(\n errorRuntime('Database marker does not match any known migration', {\n why: `The database marker hash \"${markerHash}\" is not found in the migration history at ${migrationsRelative}`,\n fix: 'Ensure the migrations directory matches this database. If the database was managed with `db init` or `db update`, run `prisma-next db sign` to update the marker.',\n meta: { markerHash, knownNodes: [...migrations.graph.nodes] },\n }),\n );\n }\n\n if (!migrations.graph.nodes.has(destinationHash)) {\n return notOk(\n errorRuntime('Current contract has no planned migration path', {\n why: `Current contract hash \"${destinationHash}\" is not present in the migration history at ${migrationsRelative}`,\n fix: 'Run `prisma-next migration plan` to create a migration for the current contract, then re-run apply.',\n meta: { destinationHash, knownNodes: [...migrations.graph.nodes] },\n }),\n );\n }\n\n // --- Resolve path and apply ---\n\n // \"No marker\" means the database is fresh — start from the empty contract hash.\n const originHash = markerHash ?? EMPTY_CONTRACT_HASH;\n\n const decision = findPathWithDecision(migrations.graph, originHash, destinationHash, refName);\n if (!decision) {\n return notOk(\n errorRuntime('No migration path from current state to target', {\n why: `Cannot find a path from \"${originHash}\" to target \"${destinationHash}\"`,\n fix: 'Check the migration history for gaps or inconsistencies.',\n meta: { markerHash: originHash, destinationHash },\n }),\n );\n }\n\n const pendingPath = decision.selectedPath;\n const pathDecision = toPathDecisionResult(decision);\n\n if (pendingPath.length === 0) {\n return ok({\n ok: true,\n migrationsApplied: 0,\n migrationsTotal: 0,\n markerHash: originHash,\n applied: [],\n summary: 'Already up to date',\n pathDecision,\n timings: { total: Date.now() - startTime },\n });\n }\n\n const bundleByDir = new Map(migrations.bundles.map((b) => [b.dirName, b]));\n const pendingMigrations: MigrationApplyStep[] = [];\n for (const migration of pendingPath) {\n const pkg = bundleByDir.get(migration.dirName);\n if (!pkg) {\n return notOk(\n errorRuntime(`Migration package not found: ${migration.dirName}`, {\n why: `The migration directory for path segment ${migration.from} → ${migration.to} was not found`,\n fix: 'Ensure all migration directories are present and intact.',\n }),\n );\n }\n pendingMigrations.push(packageToStep(pkg));\n }\n\n if (!flags.quiet && !flags.json) {\n for (const migration of pendingMigrations) {\n ui.step(`Pending ${migration.dirName}`);\n }\n }\n\n const applyResult = await client.migrationApply({\n originHash,\n destinationHash,\n pendingMigrations,\n });\n\n if (!applyResult.ok) {\n return notOk(mapApplyFailure(applyResult.failure));\n }\n\n const { value } = applyResult;\n\n return ok({\n ok: true,\n migrationsApplied: value.migrationsApplied,\n migrationsTotal: pendingPath.length,\n markerHash: value.markerHash,\n applied: value.applied,\n summary: value.summary,\n pathDecision,\n timings: { total: Date.now() - startTime },\n });\n } catch (error) {\n if (CliStructuredError.is(error)) {\n return notOk(error);\n }\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Unexpected error during migration apply: ${error instanceof Error ? error.message : String(error)}`,\n }),\n );\n } finally {\n await client.close();\n }\n}\n\nexport function createMigrationApplyCommand(): Command {\n const command = new Command('apply');\n setCommandDescriptions(\n command,\n 'Apply planned migrations to the database',\n 'Applies previously planned migrations (created by `migration plan`) to a live database.\\n' +\n 'Compares the database marker against the migration history to determine which\\n' +\n 'migrations are pending, then executes them sequentially. Each migration runs\\n' +\n 'in its own transaction. Does not plan new migrations — run `migration plan` first.',\n );\n setCommandExamples(command, ['prisma-next migration apply --db $DATABASE_URL']);\n addGlobalOptions(command)\n .option('--db <url>', 'Database connection string')\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .option('--ref <name>', 'Target ref name from migrations/refs/')\n .action(async (options: MigrationApplyCommandOptions) => {\n const flags = parseGlobalFlags(options);\n const startTime = Date.now();\n\n const ui = new TerminalUI({\n color: flags.color,\n interactive: flags.interactive,\n });\n\n const result = await executeMigrationApplyCommand(options, flags, ui, startTime);\n\n const exitCode = handleResult(result, flags, ui, (applyResult) => {\n if (flags.json) {\n ui.output(JSON.stringify(applyResult, null, 2));\n } else if (!flags.quiet) {\n ui.log(formatMigrationApplyCommandOutput(applyResult, flags));\n }\n });\n\n process.exit(exitCode);\n });\n\n return command;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"migration-apply.mjs","names":["refEntry: RefEntry | undefined","envelopeHash: string | undefined","details: Array<{ label: string; value: string }>","migrations: Awaited<ReturnType<typeof loadMigrationPackages>>","pendingMigrations: MigrationApplyStep[]"],"sources":["../../src/commands/migration-apply.ts"],"sourcesContent":["import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';\nimport {\n errorNoInvariantPath,\n errorUnknownInvariant,\n MigrationToolsError,\n} from '@prisma-next/migration-tools/errors';\nimport { findPathWithDecision } from '@prisma-next/migration-tools/migration-graph';\nimport type { MigrationPackage } from '@prisma-next/migration-tools/package';\nimport type { RefEntry } from '@prisma-next/migration-tools/refs';\nimport { readRefs, resolveRef } from '@prisma-next/migration-tools/refs';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { Command } from 'commander';\n\nimport { loadConfig } from '../config-loader';\nimport { createControlClient } from '../control-api/client';\nimport type { MigrationApplyFailure, MigrationApplyStep } from '../control-api/types';\nimport {\n CliStructuredError,\n type CliStructuredError as CliStructuredErrorType,\n errorDatabaseConnectionRequired,\n errorDriverRequired,\n errorRuntime,\n errorTargetMigrationNotSupported,\n errorUnexpected,\n mapMigrationToolsError,\n} from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n collectDeclaredInvariants,\n loadMigrationPackages,\n maskConnectionUrl,\n readContractEnvelope,\n resolveMigrationPaths,\n setCommandDescriptions,\n setCommandExamples,\n targetSupportsMigrations,\n toPathDecisionResult,\n toStructuralEdge,\n} from '../utils/command-helpers';\nimport { formatMigrationApplyCommandOutput } from '../utils/formatters/migrations';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';\nimport { handleResult } from '../utils/result-handler';\nimport { TerminalUI } from '../utils/terminal-ui';\n\ninterface MigrationApplyCommandOptions extends CommonCommandOptions {\n readonly db?: string;\n readonly config?: string;\n readonly ref?: string;\n}\n\nexport interface MigrationApplyResult {\n readonly ok: boolean;\n readonly migrationsApplied: number;\n readonly migrationsTotal: number;\n readonly markerHash: string;\n readonly applied: readonly {\n readonly dirName: string;\n readonly from: string | null;\n readonly to: string;\n readonly operationsExecuted: number;\n }[];\n readonly summary: string;\n readonly pathDecision?: {\n readonly fromHash: string;\n readonly toHash: string;\n readonly alternativeCount: number;\n readonly tieBreakReasons: readonly string[];\n readonly refName?: string;\n readonly requiredInvariants: readonly string[];\n readonly satisfiedInvariants: readonly string[];\n readonly selectedPath: readonly {\n readonly dirName: string;\n readonly migrationHash: string;\n readonly from: string;\n readonly to: string;\n readonly invariants: readonly string[];\n }[];\n };\n readonly timings: {\n readonly total: number;\n };\n}\n\nfunction mapApplyFailure(failure: MigrationApplyFailure): CliStructuredErrorType {\n return errorRuntime(failure.summary, {\n why: failure.why ?? 'Migration runner failed',\n fix: 'Fix the issue and re-run `prisma-next migration apply` — previously applied migrations are preserved.',\n meta: failure.meta ?? {},\n });\n}\n\nfunction packageToStep(pkg: MigrationPackage): MigrationApplyStep {\n return {\n dirName: pkg.dirName,\n from: pkg.metadata.from,\n to: pkg.metadata.to,\n toContract: pkg.metadata.toContract,\n operations: pkg.ops,\n providedInvariants: pkg.metadata.providedInvariants,\n };\n}\n\nasync function executeMigrationApplyCommand(\n options: MigrationApplyCommandOptions,\n flags: GlobalFlags,\n ui: TerminalUI,\n startTime: number,\n): Promise<Result<MigrationApplyResult, CliStructuredErrorType>> {\n const config = await loadConfig(options.config);\n const { configPath, migrationsDir, migrationsRelative, refsDir } = resolveMigrationPaths(\n options.config,\n config,\n );\n\n const dbConnection = options.db ?? config.db?.connection;\n if (!dbConnection) {\n return notOk(\n errorDatabaseConnectionRequired({\n why: `Database connection is required for migration apply (set db.connection in ${configPath}, or pass --db <url>)`,\n commandName: 'migration apply',\n }),\n );\n }\n\n if (!config.driver) {\n return notOk(\n errorDriverRequired({\n why: 'Config.driver is required for migration apply',\n }),\n );\n }\n\n if (!targetSupportsMigrations(config.target)) {\n return notOk(\n errorTargetMigrationNotSupported({\n why: `Target \"${config.target.id}\" does not support migrations`,\n }),\n );\n }\n\n let refEntry: RefEntry | undefined;\n let envelopeHash: string | undefined;\n const refName = options.ref;\n\n if (refName) {\n try {\n const refs = await readRefs(refsDir);\n refEntry = resolveRef(refs, refName);\n } catch (error) {\n if (MigrationToolsError.is(error)) {\n return notOk(mapMigrationToolsError(error));\n }\n throw error;\n }\n } else {\n try {\n const envelope = await readContractEnvelope(config);\n envelopeHash = envelope.storageHash;\n } catch (error) {\n return notOk(\n errorRuntime('Current contract is unavailable', {\n why: `Failed to read contract: ${error instanceof Error ? error.message : String(error)}`,\n fix: 'Run `prisma-next contract emit` to generate a valid contract.json, then retry apply.',\n }),\n );\n }\n }\n const destinationHash = refEntry?.hash ?? envelopeHash!;\n\n if (!flags.json && !flags.quiet) {\n const details: Array<{ label: string; value: string }> = [\n { label: 'config', value: configPath },\n { label: 'migrations', value: migrationsRelative },\n ];\n if (typeof dbConnection === 'string') {\n details.push({\n label: 'database',\n value: maskConnectionUrl(dbConnection),\n });\n }\n if (refName) {\n details.push({ label: 'ref', value: refName });\n }\n const header = formatStyledHeader({\n command: 'migration apply',\n description: 'Apply planned migrations to the database',\n url: 'https://pris.ly/migration-apply',\n details,\n flags,\n });\n ui.stderr(header);\n }\n\n // Read migrations and build migration chain model (offline — no DB needed)\n let migrations: Awaited<ReturnType<typeof loadMigrationPackages>>;\n try {\n migrations = await loadMigrationPackages(migrationsDir);\n } catch (error) {\n if (MigrationToolsError.is(error)) {\n return notOk(mapMigrationToolsError(error));\n }\n throw error;\n }\n\n const client = createControlClient({\n family: config.family,\n target: config.target,\n adapter: config.adapter,\n driver: config.driver,\n extensionPacks: config.extensionPacks ?? [],\n });\n\n try {\n await client.connect(dbConnection);\n const marker = await client.readMarker();\n\n // Pre-check unknown invariants against `(declared by graph) ∪\n // (already on the marker)`. The union catches the edge case where the\n // ref carries an invariant whose declaring migration was retired (e.g.\n // history rewritten) but whose id is recorded on the marker —\n // surfacing that as MIGRATION.UNKNOWN_INVARIANT would be misleading\n // because the database has already satisfied the requirement, so the\n // marker-subtraction below empties `effectiveRequired` and apply\n // short-circuits to \"Already up to date\".\n if (refEntry && refEntry.invariants.length > 0) {\n const declared = collectDeclaredInvariants(migrations.graph);\n const known = new Set<string>(declared);\n for (const id of marker?.invariants ?? []) known.add(id);\n const unknown = refEntry.invariants.filter((id) => !known.has(id));\n if (unknown.length > 0) {\n return notOk(\n mapMigrationToolsError(\n errorUnknownInvariant({\n ...ifDefined('refName', refName),\n unknown,\n declared: [...declared].sort(),\n }),\n ),\n );\n }\n }\n\n // --- No migrations on disk ---\n if (migrations.bundles.length === 0) {\n if (marker?.storageHash) {\n return notOk(\n errorRuntime('Database has state but no migrations exist', {\n why: `The database marker hash \"${marker.storageHash}\" exists but no migrations were found in ${migrationsRelative}`,\n fix: 'Ensure the migrations directory is correct. If the database was managed with `db init` or `db update`, run `prisma-next db sign` to update the marker.',\n meta: { markerHash: marker.storageHash, migrationsDir: migrationsRelative },\n }),\n );\n }\n // Non-empty contract + no migrations = user needs to plan first.\n if (destinationHash !== EMPTY_CONTRACT_HASH) {\n return notOk(\n errorRuntime('Current contract has no planned migrations', {\n why: `No migrations were found in ${migrationsRelative}, but current contract hash is \"${destinationHash}\"`,\n fix: 'Run `prisma-next migration plan` to create a migration for the current contract.',\n meta: { destinationHash, migrationsDir: migrationsRelative },\n }),\n );\n }\n // Empty contract + no migrations = nothing to do.\n return ok({\n ok: true,\n migrationsApplied: 0,\n migrationsTotal: 0,\n markerHash: EMPTY_CONTRACT_HASH,\n applied: [],\n summary: 'No migrations found',\n timings: { total: Date.now() - startTime },\n });\n }\n\n // --- Validate marker state ---\n\n // The empty sentinel should never appear in a real marker row — if it does,\n // the marker was corrupted and replaying all migrations would be dangerous.\n if (marker?.storageHash === EMPTY_CONTRACT_HASH) {\n return notOk(\n errorRuntime('Database marker contains the empty sentinel hash', {\n why: `The marker row exists but contains the empty sentinel value \"${EMPTY_CONTRACT_HASH}\". This should never happen — the marker should contain the hash of the last applied contract.`,\n fix: 'The marker is corrupted. Run `prisma-next db sign` to overwrite it with the correct contract hash, or drop and recreate the database.',\n meta: { markerHash: EMPTY_CONTRACT_HASH },\n }),\n );\n }\n\n const markerHash = marker?.storageHash;\n\n if (markerHash !== undefined && !migrations.graph.nodes.has(markerHash)) {\n return notOk(\n errorRuntime('Database marker does not match any known migration', {\n why: `The database marker hash \"${markerHash}\" is not found in the migration history at ${migrationsRelative}`,\n fix: 'Ensure the migrations directory matches this database. If the database was managed with `db init` or `db update`, run `prisma-next db sign` to update the marker.',\n meta: { markerHash, knownNodes: [...migrations.graph.nodes] },\n }),\n );\n }\n\n if (!migrations.graph.nodes.has(destinationHash)) {\n return notOk(\n errorRuntime('Current contract has no planned migration path', {\n why: `Current contract hash \"${destinationHash}\" is not present in the migration history at ${migrationsRelative}`,\n fix: 'Run `prisma-next migration plan` to create a migration for the current contract, then re-run apply.',\n meta: { destinationHash, knownNodes: [...migrations.graph.nodes] },\n }),\n );\n }\n\n // \"No marker\" means the database is fresh — start from the empty contract hash.\n const originHash = markerHash ?? EMPTY_CONTRACT_HASH;\n\n const appliedInvariants = new Set(marker?.invariants ?? []);\n const effectiveRequired = new Set(\n (refEntry?.invariants ?? []).filter((id) => !appliedInvariants.has(id)),\n );\n\n const outcome = findPathWithDecision(migrations.graph, originHash, destinationHash, {\n ...ifDefined('refName', refName),\n required: effectiveRequired,\n });\n if (outcome.kind === 'unsatisfiable') {\n return notOk(\n mapMigrationToolsError(\n errorNoInvariantPath({\n ...ifDefined('refName', refName),\n required: [...effectiveRequired].sort(),\n missing: outcome.missing,\n structuralPath: outcome.structuralPath.map(toStructuralEdge),\n }),\n ),\n );\n }\n if (outcome.kind === 'unreachable') {\n return notOk(\n errorRuntime('No migration path from current state to target', {\n why: `Cannot find a path from \"${originHash}\" to target \"${destinationHash}\"`,\n fix: 'Check the migration history for gaps or inconsistencies.',\n meta: { markerHash: originHash, destinationHash },\n }),\n );\n }\n\n const pathDecision = toPathDecisionResult(outcome.decision);\n\n if (outcome.decision.selectedPath.length === 0) {\n return ok({\n ok: true,\n migrationsApplied: 0,\n migrationsTotal: 0,\n markerHash: originHash,\n applied: [],\n summary: 'Already up to date',\n pathDecision,\n timings: { total: Date.now() - startTime },\n });\n }\n\n const bundleByDir = new Map(migrations.bundles.map((b) => [b.dirName, b]));\n const pendingMigrations: MigrationApplyStep[] = [];\n for (const migration of outcome.decision.selectedPath) {\n const pkg = bundleByDir.get(migration.dirName);\n if (!pkg) {\n return notOk(\n errorRuntime(`Migration package not found: ${migration.dirName}`, {\n why: `The migration directory for path segment ${migration.from} → ${migration.to} was not found`,\n fix: 'Ensure all migration directories are present and intact.',\n }),\n );\n }\n pendingMigrations.push(packageToStep(pkg));\n }\n\n if (!flags.quiet && !flags.json) {\n for (const migration of pendingMigrations) {\n ui.step(`Pending ${migration.dirName}`);\n }\n }\n\n const applyResult = await client.migrationApply({\n originHash,\n destinationHash,\n pendingMigrations,\n });\n\n if (!applyResult.ok) {\n return notOk(mapApplyFailure(applyResult.failure));\n }\n\n const { value } = applyResult;\n\n return ok({\n ok: true,\n migrationsApplied: value.migrationsApplied,\n migrationsTotal: outcome.decision.selectedPath.length,\n markerHash: value.markerHash,\n applied: value.applied,\n summary: value.summary,\n pathDecision,\n timings: { total: Date.now() - startTime },\n });\n } catch (error) {\n if (CliStructuredError.is(error)) {\n return notOk(error);\n }\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Unexpected error during migration apply: ${error instanceof Error ? error.message : String(error)}`,\n }),\n );\n } finally {\n await client.close();\n }\n}\n\nexport function createMigrationApplyCommand(): Command {\n const command = new Command('apply');\n setCommandDescriptions(\n command,\n 'Apply planned migrations to the database',\n 'Applies previously planned migrations (created by `migration plan`) to a live database.\\n' +\n 'Compares the database marker against the migration history to determine which\\n' +\n 'migrations are pending, then executes them sequentially. Each migration runs\\n' +\n 'in its own transaction. Does not plan new migrations — run `migration plan` first.',\n );\n setCommandExamples(command, ['prisma-next migration apply --db $DATABASE_URL']);\n addGlobalOptions(command)\n .option('--db <url>', 'Database connection string')\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .option('--ref <name>', 'Target ref name from migrations/refs/')\n .action(async (options: MigrationApplyCommandOptions) => {\n const flags = parseGlobalFlags(options);\n const startTime = Date.now();\n\n const ui = new TerminalUI({\n color: flags.color,\n interactive: flags.interactive,\n });\n\n const result = await executeMigrationApplyCommand(options, flags, ui, startTime);\n\n const exitCode = handleResult(result, flags, ui, (applyResult) => {\n if (flags.json) {\n ui.output(JSON.stringify(applyResult, null, 2));\n } else if (!flags.quiet) {\n ui.log(formatMigrationApplyCommandOutput(applyResult, flags));\n }\n });\n\n process.exit(exitCode);\n });\n\n return command;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration-new.d.mts","names":[],"sources":["../../src/commands/migration-new.ts"],"sourcesContent":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"migration-new.d.mts","names":[],"sources":["../../src/commands/migration-new.ts"],"sourcesContent":[],"mappings":";;;;iBAoQgB,yBAAA,CAAA,GAA6B"}
|
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
import { t as loadConfig } from "../config-loader-
|
|
2
|
-
import { _ as errorUnexpected, h as errorTargetMigrationNotSupported, m as errorRuntime, t as CliStructuredError } from "../cli-errors-
|
|
3
|
-
import { t as assertFrameworkComponentsCompatible } from "../framework-components-
|
|
4
|
-
import { t as TerminalUI } from "../terminal-ui-
|
|
5
|
-
import {
|
|
1
|
+
import { t as loadConfig } from "../config-loader-ih8ViDb_.mjs";
|
|
2
|
+
import { _ as errorUnexpected, h as errorTargetMigrationNotSupported, m as errorRuntime, t as CliStructuredError, v as mapMigrationToolsError } from "../cli-errors-By1iVE3z.mjs";
|
|
3
|
+
import { t as assertFrameworkComponentsCompatible } from "../framework-components-Bgcre3Z6.mjs";
|
|
4
|
+
import { t as TerminalUI } from "../terminal-ui-u2YgKghu.mjs";
|
|
5
|
+
import { c as resolveContractPath, d as setCommandDescriptions, f as setCommandExamples, g as parseGlobalFlags, i as getTargetMigrations, l as resolveMigrationPaths, n as addGlobalOptions, t as handleResult, y as formatStyledHeader } from "../result-handler-Ch6hVnOo.mjs";
|
|
6
6
|
import { Command } from "commander";
|
|
7
|
-
import { readFileSync } from "node:fs";
|
|
8
7
|
import { getEmittedArtifactPaths } from "@prisma-next/emitter";
|
|
9
8
|
import { notOk, ok } from "@prisma-next/utils/result";
|
|
10
|
-
import { join, relative
|
|
9
|
+
import { join, relative } from "pathe";
|
|
11
10
|
import { createControlStack } from "@prisma-next/framework-components/control";
|
|
12
|
-
import { EMPTY_CONTRACT_HASH } from "@prisma-next/migration-tools/constants";
|
|
13
|
-
import { findLatestMigration, reconstructGraph } from "@prisma-next/migration-tools/dag";
|
|
14
11
|
import { copyFilesWithRename, formatMigrationDirName, readMigrationsDir, writeMigrationPackage } from "@prisma-next/migration-tools/io";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
12
|
+
import { findLatestMigration, reconstructGraph } from "@prisma-next/migration-tools/migration-graph";
|
|
13
|
+
import { readFileSync } from "node:fs";
|
|
14
|
+
import { MigrationToolsError } from "@prisma-next/migration-tools/errors";
|
|
15
|
+
import { computeMigrationHash } from "@prisma-next/migration-tools/hash";
|
|
17
16
|
import { writeMigrationTs } from "@prisma-next/migration-tools/migration-ts";
|
|
18
17
|
|
|
19
18
|
//#region src/commands/migration-new.ts
|
|
@@ -29,8 +28,7 @@ import { writeMigrationTs } from "@prisma-next/migration-tools/migration-ts";
|
|
|
29
28
|
async function executeMigrationNewCommand(options) {
|
|
30
29
|
const config = await loadConfig(options.config);
|
|
31
30
|
const { migrationsDir, migrationsRelative } = resolveMigrationPaths(options.config, config);
|
|
32
|
-
const
|
|
33
|
-
const contractPathAbsolute = resolve(options.config ? resolve(options.config, "..") : process.cwd(), contractPath);
|
|
31
|
+
const contractPathAbsolute = resolveContractPath(config);
|
|
34
32
|
let contractJsonContent;
|
|
35
33
|
try {
|
|
36
34
|
contractJsonContent = readFileSync(contractPathAbsolute, "utf-8");
|
|
@@ -56,51 +54,46 @@ async function executeMigrationNewCommand(options) {
|
|
|
56
54
|
fix: "Run `prisma-next contract emit` to regenerate the contract"
|
|
57
55
|
}));
|
|
58
56
|
let fromContract = null;
|
|
59
|
-
let fromHash =
|
|
57
|
+
let fromHash = null;
|
|
60
58
|
let fromContractSourceDir = null;
|
|
61
59
|
try {
|
|
62
60
|
const packages = await readMigrationsDir(migrationsDir);
|
|
63
61
|
if (packages.length > 0) {
|
|
64
62
|
const graph = reconstructGraph(packages);
|
|
65
63
|
if (options.from) {
|
|
66
|
-
const match = packages.find((p) => p.
|
|
64
|
+
const match = packages.find((p) => p.metadata.to.startsWith(options.from));
|
|
67
65
|
if (!match) return notOk(errorRuntime("Starting contract not found", {
|
|
68
66
|
why: `No migration with to hash matching "${options.from}" exists in ${migrationsRelative}`,
|
|
69
67
|
fix: "Check that the --from hash matches a known migration target hash."
|
|
70
68
|
}));
|
|
71
|
-
fromHash = match.
|
|
72
|
-
fromContract = match.
|
|
69
|
+
fromHash = match.metadata.to;
|
|
70
|
+
fromContract = match.metadata.toContract;
|
|
73
71
|
fromContractSourceDir = match.dirPath;
|
|
74
72
|
} else {
|
|
75
73
|
const latestMigration = findLatestMigration(graph);
|
|
76
74
|
if (latestMigration) {
|
|
77
75
|
fromHash = latestMigration.to;
|
|
78
|
-
const leafPkg = packages.find((p) => p.
|
|
76
|
+
const leafPkg = packages.find((p) => p.metadata.migrationHash === latestMigration.migrationHash);
|
|
79
77
|
if (leafPkg) {
|
|
80
|
-
fromContract = leafPkg.
|
|
78
|
+
fromContract = leafPkg.metadata.toContract;
|
|
81
79
|
fromContractSourceDir = leafPkg.dirPath;
|
|
82
80
|
}
|
|
83
81
|
}
|
|
84
82
|
}
|
|
85
83
|
}
|
|
86
84
|
} catch (error) {
|
|
87
|
-
if (MigrationToolsError.is(error)) return notOk(
|
|
88
|
-
why: error.why,
|
|
89
|
-
fix: error.fix,
|
|
90
|
-
meta: { code: error.code }
|
|
91
|
-
}));
|
|
85
|
+
if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
|
|
92
86
|
throw error;
|
|
93
87
|
}
|
|
94
|
-
if (fromHash === toStorageHash) return notOk(errorRuntime("No changes detected", {
|
|
88
|
+
if (fromHash === toStorageHash && !options.from) return notOk(errorRuntime("No changes detected", {
|
|
95
89
|
why: "The from and to contract hashes are identical — there is nothing to migrate.",
|
|
96
|
-
fix: "Change the contract and run `prisma-next contract emit` before creating a new migration."
|
|
90
|
+
fix: "Change the contract and run `prisma-next contract emit` before creating a new migration. To author a data-only migration on the current contract hash, pass `--from <hash>` explicitly."
|
|
97
91
|
}));
|
|
98
92
|
const timestamp = /* @__PURE__ */ new Date();
|
|
99
93
|
const packageDir = join(migrationsDir, formatMigrationDirName(timestamp, options.name ?? "migration"));
|
|
100
|
-
const
|
|
94
|
+
const baseMetadata = {
|
|
101
95
|
from: fromHash,
|
|
102
96
|
to: toStorageHash,
|
|
103
|
-
kind: "regular",
|
|
104
97
|
fromContract,
|
|
105
98
|
toContract: toContractJson,
|
|
106
99
|
hints: {
|
|
@@ -109,11 +102,12 @@ async function executeMigrationNewCommand(options) {
|
|
|
109
102
|
plannerVersion: "1.0.0"
|
|
110
103
|
},
|
|
111
104
|
labels: [],
|
|
105
|
+
providedInvariants: [],
|
|
112
106
|
createdAt: timestamp.toISOString()
|
|
113
107
|
};
|
|
114
|
-
const
|
|
115
|
-
...
|
|
116
|
-
|
|
108
|
+
const metadata = {
|
|
109
|
+
...baseMetadata,
|
|
110
|
+
migrationHash: computeMigrationHash(baseMetadata, [])
|
|
117
111
|
};
|
|
118
112
|
const migrations = getTargetMigrations(config.target);
|
|
119
113
|
if (!migrations) return notOk(errorTargetMigrationNotSupported({ why: `Target "${config.target.targetId}" does not support migrations` }));
|
|
@@ -123,7 +117,7 @@ async function executeMigrationNewCommand(options) {
|
|
|
123
117
|
config.adapter,
|
|
124
118
|
...config.extensionPacks ?? []
|
|
125
119
|
]);
|
|
126
|
-
await writeMigrationPackage(packageDir,
|
|
120
|
+
await writeMigrationPackage(packageDir, metadata, []);
|
|
127
121
|
const destinationArtifacts = getEmittedArtifactPaths(contractPathAbsolute);
|
|
128
122
|
await copyFilesWithRename(packageDir, [{
|
|
129
123
|
sourcePath: destinationArtifacts.jsonPath,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration-new.mjs","names":["contractJsonContent: string","toContractJson: Contract","fromContract: Contract | null","fromHash: string","fromContractSourceDir: string | null","baseManifest: Omit<MigrationManifest, 'migrationId'>","manifest: MigrationManifest"],"sources":["../../src/commands/migration-new.ts"],"sourcesContent":["/**\n * `migration new` — scaffolds a migration package with a `migration.ts` file\n * for manual authoring.\n *\n * The planner's `emptyMigration(context)` returns a\n * `MigrationPlanWithAuthoringSurface`, whose `renderTypeScript()` produces\n * the target-appropriate empty stub. The CLI writes the returned source\n * verbatim.\n */\n\nimport { readFileSync } from 'node:fs';\nimport type { Contract } from '@prisma-next/contract/types';\nimport { getEmittedArtifactPaths } from '@prisma-next/emitter';\nimport { createControlStack } from '@prisma-next/framework-components/control';\nimport { computeMigrationId } from '@prisma-next/migration-tools/attestation';\nimport { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';\nimport { findLatestMigration, reconstructGraph } from '@prisma-next/migration-tools/dag';\nimport {\n copyFilesWithRename,\n formatMigrationDirName,\n readMigrationsDir,\n writeMigrationPackage,\n} from '@prisma-next/migration-tools/io';\nimport { writeMigrationTs } from '@prisma-next/migration-tools/migration-ts';\nimport type { MigrationManifest } from '@prisma-next/migration-tools/types';\nimport { MigrationToolsError } from '@prisma-next/migration-tools/types';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { Command } from 'commander';\nimport { join, relative, resolve } from 'pathe';\nimport { loadConfig } from '../config-loader';\nimport {\n CliStructuredError,\n errorRuntime,\n errorTargetMigrationNotSupported,\n errorUnexpected,\n} from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n getTargetMigrations,\n resolveMigrationPaths,\n setCommandDescriptions,\n setCommandExamples,\n} from '../utils/command-helpers';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport { assertFrameworkComponentsCompatible } from '../utils/framework-components';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { parseGlobalFlags } from '../utils/global-flags';\nimport { handleResult } from '../utils/result-handler';\nimport { TerminalUI } from '../utils/terminal-ui';\n\ninterface MigrationNewOptions extends CommonCommandOptions {\n readonly name?: string;\n readonly from?: string;\n readonly config?: string;\n}\n\ninterface MigrationNewResult {\n readonly ok: true;\n readonly dir: string;\n readonly from: string;\n readonly to: string;\n readonly summary: string;\n}\n\nasync function executeMigrationNewCommand(\n options: MigrationNewOptions,\n): Promise<Result<MigrationNewResult, CliStructuredError>> {\n const config = await loadConfig(options.config);\n const { migrationsDir, migrationsRelative } = resolveMigrationPaths(options.config, config);\n\n const contractPath = config.contract?.output ?? 'contract.json';\n const contractPathAbsolute = resolve(\n options.config ? resolve(options.config, '..') : process.cwd(),\n contractPath,\n );\n\n let contractJsonContent: string;\n try {\n contractJsonContent = readFileSync(contractPathAbsolute, 'utf-8');\n } catch (error) {\n if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {\n return notOk(\n errorRuntime(`Contract file not found at ${contractPathAbsolute}`, {\n why: `Contract file not found at ${contractPathAbsolute}`,\n fix: 'Run `prisma-next contract emit` first to generate the contract',\n }),\n );\n }\n throw error;\n }\n\n let toContractJson: Contract;\n try {\n toContractJson = JSON.parse(contractJsonContent) as Contract;\n } catch (error) {\n return notOk(\n errorRuntime('Contract JSON is invalid', {\n why: `Failed to parse ${contractPathAbsolute}: ${error instanceof Error ? error.message : String(error)}`,\n fix: 'Run `prisma-next contract emit` to regenerate the contract',\n }),\n );\n }\n\n const toStorageHash = (\n (toContractJson as unknown as Record<string, unknown>)['storage'] as\n | Record<string, unknown>\n | undefined\n )?.['storageHash'] as string | undefined;\n if (!toStorageHash) {\n return notOk(\n errorRuntime('Contract is missing storageHash', {\n why: `Contract at ${contractPathAbsolute} has no storageHash`,\n fix: 'Run `prisma-next contract emit` to regenerate the contract',\n }),\n );\n }\n\n let fromContract: Contract | null = null;\n let fromHash: string = EMPTY_CONTRACT_HASH;\n let fromContractSourceDir: string | null = null;\n\n try {\n const packages = await readMigrationsDir(migrationsDir);\n\n if (packages.length > 0) {\n const graph = reconstructGraph(packages);\n\n if (options.from) {\n const match = packages.find((p) => p.manifest.to.startsWith(options.from!));\n if (!match) {\n return notOk(\n errorRuntime('Starting contract not found', {\n why: `No migration with to hash matching \"${options.from}\" exists in ${migrationsRelative}`,\n fix: 'Check that the --from hash matches a known migration target hash.',\n }),\n );\n }\n fromHash = match.manifest.to;\n fromContract = match.manifest.toContract;\n fromContractSourceDir = match.dirPath;\n } else {\n const latestMigration = findLatestMigration(graph);\n if (latestMigration) {\n fromHash = latestMigration.to;\n const leafPkg = packages.find(\n (p) => p.manifest.migrationId === latestMigration.migrationId,\n );\n if (leafPkg) {\n fromContract = leafPkg.manifest.toContract;\n fromContractSourceDir = leafPkg.dirPath;\n }\n }\n }\n }\n } catch (error) {\n if (MigrationToolsError.is(error)) {\n return notOk(\n errorRuntime(error.message, {\n why: error.why,\n fix: error.fix,\n meta: { code: error.code },\n }),\n );\n }\n throw error;\n }\n\n if (fromHash === toStorageHash) {\n return notOk(\n errorRuntime('No changes detected', {\n why: 'The from and to contract hashes are identical — there is nothing to migrate.',\n fix: 'Change the contract and run `prisma-next contract emit` before creating a new migration.',\n }),\n );\n }\n\n const timestamp = new Date();\n const slug = options.name ?? 'migration';\n const dirName = formatMigrationDirName(timestamp, slug);\n const packageDir = join(migrationsDir, dirName);\n\n // `migration new` scaffolds an empty `migration.ts` for the user to\n // fill, so we attest over `ops: []`. Re-running self-emit after the\n // user adds operations will produce a different `migrationId` (over\n // the real ops). This is intentional — there is no on-disk draft.\n const baseManifest: Omit<MigrationManifest, 'migrationId'> = {\n from: fromHash,\n to: toStorageHash,\n kind: 'regular',\n fromContract,\n toContract: toContractJson,\n hints: {\n used: [],\n applied: [],\n plannerVersion: '1.0.0',\n },\n labels: [],\n createdAt: timestamp.toISOString(),\n };\n const manifest: MigrationManifest = {\n ...baseManifest,\n migrationId: computeMigrationId(baseManifest, []),\n };\n\n const migrations = getTargetMigrations(config.target);\n if (!migrations) {\n return notOk(\n errorTargetMigrationNotSupported({\n why: `Target \"${config.target.targetId}\" does not support migrations`,\n }),\n );\n }\n\n try {\n assertFrameworkComponentsCompatible(config.family.familyId, config.target.targetId, [\n config.target,\n config.adapter,\n ...(config.extensionPacks ?? []),\n ]);\n\n await writeMigrationPackage(packageDir, manifest, []);\n const destinationArtifacts = getEmittedArtifactPaths(contractPathAbsolute);\n await copyFilesWithRename(packageDir, [\n { sourcePath: destinationArtifacts.jsonPath, destName: 'end-contract.json' },\n { sourcePath: destinationArtifacts.dtsPath, destName: 'end-contract.d.ts' },\n ]);\n if (fromContractSourceDir !== null) {\n const sourceArtifacts = getEmittedArtifactPaths(\n join(fromContractSourceDir, 'end-contract.json'),\n );\n await copyFilesWithRename(packageDir, [\n { sourcePath: sourceArtifacts.jsonPath, destName: 'start-contract.json' },\n { sourcePath: sourceArtifacts.dtsPath, destName: 'start-contract.d.ts' },\n ]);\n }\n\n const stack = createControlStack(config);\n const familyInstance = config.family.create(stack);\n const planner = migrations.createPlanner(familyInstance);\n const emptyPlan = planner.emptyMigration({\n packageDir,\n contractJsonPath: join(packageDir, 'end-contract.json'),\n fromHash,\n toHash: toStorageHash,\n });\n await writeMigrationTs(packageDir, emptyPlan.renderTypeScript());\n\n return ok({\n ok: true as const,\n dir: relative(process.cwd(), packageDir),\n from: fromHash,\n to: toStorageHash,\n summary: `Scaffolded migration at ${relative(process.cwd(), packageDir)}`,\n });\n } catch (error) {\n if (CliStructuredError.is(error)) {\n return notOk(error);\n }\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to scaffold migration: ${error instanceof Error ? error.message : String(error)}`,\n }),\n );\n }\n}\n\nexport function createMigrationNewCommand(): Command {\n const command = new Command('new');\n setCommandDescriptions(\n command,\n 'Scaffold a new migration for manual authoring',\n 'Creates a migration package with a migration.ts file for manual authoring.\\n' +\n 'Write the migration body in migration.ts, then run the file with Node\\n' +\n '(`node migration.ts`) to self-emit ops.json and attest the package.',\n );\n setCommandExamples(command, [\n 'prisma-next migration new --name split-name',\n 'prisma-next migration new --name custom-fk --from sha256:abc...',\n ]);\n addGlobalOptions(command)\n .option('--name <slug>', 'Migration name (used in directory name)')\n .option('--from <hash>', 'Starting contract hash (default: latest migration target)')\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .action(async (options: MigrationNewOptions) => {\n const flags = parseGlobalFlags(options);\n const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });\n\n if (!flags.json && !flags.quiet) {\n const header = formatStyledHeader({\n command: 'migration new',\n description: 'Scaffold a new migration',\n details: [],\n flags,\n });\n ui.stderr(header);\n }\n\n const result = await executeMigrationNewCommand(options);\n\n const exitCode = handleResult(result, flags, ui, (value) => {\n if (flags.json) {\n ui.output(JSON.stringify(value, null, 2));\n } else if (!flags.quiet) {\n ui.output(`\\nScaffolded migration at ${value.dir}`);\n ui.output(` from: ${value.from}`);\n ui.output(` to: ${value.to}`);\n ui.output(\n `\\nEdit migration.ts, then run it directly (\\`node \"${value.dir}/migration.ts\"\\`) to self-emit and attest.`,\n );\n }\n });\n\n process.exit(exitCode);\n });\n\n return command;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgEA,eAAe,2BACb,SACyD;CACzD,MAAM,SAAS,MAAM,WAAW,QAAQ,OAAO;CAC/C,MAAM,EAAE,eAAe,uBAAuB,sBAAsB,QAAQ,QAAQ,OAAO;CAE3F,MAAM,eAAe,OAAO,UAAU,UAAU;CAChD,MAAM,uBAAuB,QAC3B,QAAQ,SAAS,QAAQ,QAAQ,QAAQ,KAAK,GAAG,QAAQ,KAAK,EAC9D,aACD;CAED,IAAIA;AACJ,KAAI;AACF,wBAAsB,aAAa,sBAAsB,QAAQ;UAC1D,OAAO;AACd,MAAI,iBAAiB,SAAU,MAA4B,SAAS,SAClE,QAAO,MACL,aAAa,8BAA8B,wBAAwB;GACjE,KAAK,8BAA8B;GACnC,KAAK;GACN,CAAC,CACH;AAEH,QAAM;;CAGR,IAAIC;AACJ,KAAI;AACF,mBAAiB,KAAK,MAAM,oBAAoB;UACzC,OAAO;AACd,SAAO,MACL,aAAa,4BAA4B;GACvC,KAAK,mBAAmB,qBAAqB,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;GACvG,KAAK;GACN,CAAC,CACH;;CAGH,MAAM,gBACH,eAAsD,aAGrD;AACJ,KAAI,CAAC,cACH,QAAO,MACL,aAAa,mCAAmC;EAC9C,KAAK,eAAe,qBAAqB;EACzC,KAAK;EACN,CAAC,CACH;CAGH,IAAIC,eAAgC;CACpC,IAAIC,WAAmB;CACvB,IAAIC,wBAAuC;AAE3C,KAAI;EACF,MAAM,WAAW,MAAM,kBAAkB,cAAc;AAEvD,MAAI,SAAS,SAAS,GAAG;GACvB,MAAM,QAAQ,iBAAiB,SAAS;AAExC,OAAI,QAAQ,MAAM;IAChB,MAAM,QAAQ,SAAS,MAAM,MAAM,EAAE,SAAS,GAAG,WAAW,QAAQ,KAAM,CAAC;AAC3E,QAAI,CAAC,MACH,QAAO,MACL,aAAa,+BAA+B;KAC1C,KAAK,uCAAuC,QAAQ,KAAK,cAAc;KACvE,KAAK;KACN,CAAC,CACH;AAEH,eAAW,MAAM,SAAS;AAC1B,mBAAe,MAAM,SAAS;AAC9B,4BAAwB,MAAM;UACzB;IACL,MAAM,kBAAkB,oBAAoB,MAAM;AAClD,QAAI,iBAAiB;AACnB,gBAAW,gBAAgB;KAC3B,MAAM,UAAU,SAAS,MACtB,MAAM,EAAE,SAAS,gBAAgB,gBAAgB,YACnD;AACD,SAAI,SAAS;AACX,qBAAe,QAAQ,SAAS;AAChC,8BAAwB,QAAQ;;;;;UAKjC,OAAO;AACd,MAAI,oBAAoB,GAAG,MAAM,CAC/B,QAAO,MACL,aAAa,MAAM,SAAS;GAC1B,KAAK,MAAM;GACX,KAAK,MAAM;GACX,MAAM,EAAE,MAAM,MAAM,MAAM;GAC3B,CAAC,CACH;AAEH,QAAM;;AAGR,KAAI,aAAa,cACf,QAAO,MACL,aAAa,uBAAuB;EAClC,KAAK;EACL,KAAK;EACN,CAAC,CACH;CAGH,MAAM,4BAAY,IAAI,MAAM;CAG5B,MAAM,aAAa,KAAK,eADR,uBAAuB,WAD1B,QAAQ,QAAQ,YAC0B,CACR;CAM/C,MAAMC,eAAuD;EAC3D,MAAM;EACN,IAAI;EACJ,MAAM;EACN;EACA,YAAY;EACZ,OAAO;GACL,MAAM,EAAE;GACR,SAAS,EAAE;GACX,gBAAgB;GACjB;EACD,QAAQ,EAAE;EACV,WAAW,UAAU,aAAa;EACnC;CACD,MAAMC,WAA8B;EAClC,GAAG;EACH,aAAa,mBAAmB,cAAc,EAAE,CAAC;EAClD;CAED,MAAM,aAAa,oBAAoB,OAAO,OAAO;AACrD,KAAI,CAAC,WACH,QAAO,MACL,iCAAiC,EAC/B,KAAK,WAAW,OAAO,OAAO,SAAS,gCACxC,CAAC,CACH;AAGH,KAAI;AACF,sCAAoC,OAAO,OAAO,UAAU,OAAO,OAAO,UAAU;GAClF,OAAO;GACP,OAAO;GACP,GAAI,OAAO,kBAAkB,EAAE;GAChC,CAAC;AAEF,QAAM,sBAAsB,YAAY,UAAU,EAAE,CAAC;EACrD,MAAM,uBAAuB,wBAAwB,qBAAqB;AAC1E,QAAM,oBAAoB,YAAY,CACpC;GAAE,YAAY,qBAAqB;GAAU,UAAU;GAAqB,EAC5E;GAAE,YAAY,qBAAqB;GAAS,UAAU;GAAqB,CAC5E,CAAC;AACF,MAAI,0BAA0B,MAAM;GAClC,MAAM,kBAAkB,wBACtB,KAAK,uBAAuB,oBAAoB,CACjD;AACD,SAAM,oBAAoB,YAAY,CACpC;IAAE,YAAY,gBAAgB;IAAU,UAAU;IAAuB,EACzE;IAAE,YAAY,gBAAgB;IAAS,UAAU;IAAuB,CACzE,CAAC;;EAGJ,MAAM,QAAQ,mBAAmB,OAAO;EACxC,MAAM,iBAAiB,OAAO,OAAO,OAAO,MAAM;AAQlD,QAAM,iBAAiB,YAPP,WAAW,cAAc,eAAe,CAC9B,eAAe;GACvC;GACA,kBAAkB,KAAK,YAAY,oBAAoB;GACvD;GACA,QAAQ;GACT,CAAC,CAC2C,kBAAkB,CAAC;AAEhE,SAAO,GAAG;GACR,IAAI;GACJ,KAAK,SAAS,QAAQ,KAAK,EAAE,WAAW;GACxC,MAAM;GACN,IAAI;GACJ,SAAS,2BAA2B,SAAS,QAAQ,KAAK,EAAE,WAAW;GACxE,CAAC;UACK,OAAO;AACd,MAAI,mBAAmB,GAAG,MAAM,CAC9B,QAAO,MAAM,MAAM;AAErB,SAAO,MACL,gBAAgB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,EAAE,EACtE,KAAK,iCAAiC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IAC7F,CAAC,CACH;;;AAIL,SAAgB,4BAAqC;CACnD,MAAM,UAAU,IAAI,QAAQ,MAAM;AAClC,wBACE,SACA,iDACA,yNAGD;AACD,oBAAmB,SAAS,CAC1B,+CACA,kEACD,CAAC;AACF,kBAAiB,QAAQ,CACtB,OAAO,iBAAiB,0CAA0C,CAClE,OAAO,iBAAiB,4DAA4D,CACpF,OAAO,mBAAmB,gCAAgC,CAC1D,OAAO,OAAO,YAAiC;EAC9C,MAAM,QAAQ,iBAAiB,QAAQ;EACvC,MAAM,KAAK,IAAI,WAAW;GAAE,OAAO,MAAM;GAAO,aAAa,MAAM;GAAa,CAAC;AAEjF,MAAI,CAAC,MAAM,QAAQ,CAAC,MAAM,OAAO;GAC/B,MAAM,SAAS,mBAAmB;IAChC,SAAS;IACT,aAAa;IACb,SAAS,EAAE;IACX;IACD,CAAC;AACF,MAAG,OAAO,OAAO;;EAKnB,MAAM,WAAW,aAFF,MAAM,2BAA2B,QAAQ,EAElB,OAAO,KAAK,UAAU;AAC1D,OAAI,MAAM,KACR,IAAG,OAAO,KAAK,UAAU,OAAO,MAAM,EAAE,CAAC;YAChC,CAAC,MAAM,OAAO;AACvB,OAAG,OAAO,6BAA6B,MAAM,MAAM;AACnD,OAAG,OAAO,WAAW,MAAM,OAAO;AAClC,OAAG,OAAO,WAAW,MAAM,KAAK;AAChC,OAAG,OACD,sDAAsD,MAAM,IAAI,4CACjE;;IAEH;AAEF,UAAQ,KAAK,SAAS;GACtB;AAEJ,QAAO"}
|
|
1
|
+
{"version":3,"file":"migration-new.mjs","names":["contractJsonContent: string","toContractJson: Contract","fromContract: Contract | null","fromHash: string | null","fromContractSourceDir: string | null","baseMetadata: Omit<MigrationMetadata, 'migrationHash'>","metadata: MigrationMetadata"],"sources":["../../src/commands/migration-new.ts"],"sourcesContent":["/**\n * `migration new` — scaffolds a migration package with a `migration.ts` file\n * for manual authoring.\n *\n * The planner's `emptyMigration(context)` returns a\n * `MigrationPlanWithAuthoringSurface`, whose `renderTypeScript()` produces\n * the target-appropriate empty stub. The CLI writes the returned source\n * verbatim.\n */\n\nimport { readFileSync } from 'node:fs';\nimport type { Contract } from '@prisma-next/contract/types';\nimport { getEmittedArtifactPaths } from '@prisma-next/emitter';\nimport { createControlStack } from '@prisma-next/framework-components/control';\nimport { MigrationToolsError } from '@prisma-next/migration-tools/errors';\nimport { computeMigrationHash } from '@prisma-next/migration-tools/hash';\nimport {\n copyFilesWithRename,\n formatMigrationDirName,\n readMigrationsDir,\n writeMigrationPackage,\n} from '@prisma-next/migration-tools/io';\nimport type { MigrationMetadata } from '@prisma-next/migration-tools/metadata';\nimport {\n findLatestMigration,\n reconstructGraph,\n} from '@prisma-next/migration-tools/migration-graph';\nimport { writeMigrationTs } from '@prisma-next/migration-tools/migration-ts';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { Command } from 'commander';\nimport { join, relative } from 'pathe';\nimport { loadConfig } from '../config-loader';\nimport {\n CliStructuredError,\n errorRuntime,\n errorTargetMigrationNotSupported,\n errorUnexpected,\n mapMigrationToolsError,\n} from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n getTargetMigrations,\n resolveContractPath,\n resolveMigrationPaths,\n setCommandDescriptions,\n setCommandExamples,\n} from '../utils/command-helpers';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport { assertFrameworkComponentsCompatible } from '../utils/framework-components';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { parseGlobalFlags } from '../utils/global-flags';\nimport { handleResult } from '../utils/result-handler';\nimport { TerminalUI } from '../utils/terminal-ui';\n\ninterface MigrationNewOptions extends CommonCommandOptions {\n readonly name?: string;\n readonly from?: string;\n readonly config?: string;\n}\n\ninterface MigrationNewResult {\n readonly ok: true;\n readonly dir: string;\n readonly from: string | null;\n readonly to: string;\n readonly summary: string;\n}\n\nasync function executeMigrationNewCommand(\n options: MigrationNewOptions,\n): Promise<Result<MigrationNewResult, CliStructuredError>> {\n const config = await loadConfig(options.config);\n const { migrationsDir, migrationsRelative } = resolveMigrationPaths(options.config, config);\n\n const contractPathAbsolute = resolveContractPath(config);\n\n let contractJsonContent: string;\n try {\n contractJsonContent = readFileSync(contractPathAbsolute, 'utf-8');\n } catch (error) {\n if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {\n return notOk(\n errorRuntime(`Contract file not found at ${contractPathAbsolute}`, {\n why: `Contract file not found at ${contractPathAbsolute}`,\n fix: 'Run `prisma-next contract emit` first to generate the contract',\n }),\n );\n }\n throw error;\n }\n\n let toContractJson: Contract;\n try {\n toContractJson = JSON.parse(contractJsonContent) as Contract;\n } catch (error) {\n return notOk(\n errorRuntime('Contract JSON is invalid', {\n why: `Failed to parse ${contractPathAbsolute}: ${error instanceof Error ? error.message : String(error)}`,\n fix: 'Run `prisma-next contract emit` to regenerate the contract',\n }),\n );\n }\n\n const toStorageHash = (\n (toContractJson as unknown as Record<string, unknown>)['storage'] as\n | Record<string, unknown>\n | undefined\n )?.['storageHash'] as string | undefined;\n if (!toStorageHash) {\n return notOk(\n errorRuntime('Contract is missing storageHash', {\n why: `Contract at ${contractPathAbsolute} has no storageHash`,\n fix: 'Run `prisma-next contract emit` to regenerate the contract',\n }),\n );\n }\n\n let fromContract: Contract | null = null;\n let fromHash: string | null = null;\n let fromContractSourceDir: string | null = null;\n\n try {\n const packages = await readMigrationsDir(migrationsDir);\n\n if (packages.length > 0) {\n const graph = reconstructGraph(packages);\n\n if (options.from) {\n const match = packages.find((p) => p.metadata.to.startsWith(options.from!));\n if (!match) {\n return notOk(\n errorRuntime('Starting contract not found', {\n why: `No migration with to hash matching \"${options.from}\" exists in ${migrationsRelative}`,\n fix: 'Check that the --from hash matches a known migration target hash.',\n }),\n );\n }\n fromHash = match.metadata.to;\n fromContract = match.metadata.toContract;\n fromContractSourceDir = match.dirPath;\n } else {\n const latestMigration = findLatestMigration(graph);\n if (latestMigration) {\n fromHash = latestMigration.to;\n const leafPkg = packages.find(\n (p) => p.metadata.migrationHash === latestMigration.migrationHash,\n );\n if (leafPkg) {\n fromContract = leafPkg.metadata.toContract;\n fromContractSourceDir = leafPkg.dirPath;\n }\n }\n }\n }\n } catch (error) {\n if (MigrationToolsError.is(error)) {\n return notOk(mapMigrationToolsError(error));\n }\n throw error;\n }\n\n if (fromHash === toStorageHash && !options.from) {\n return notOk(\n errorRuntime('No changes detected', {\n why: 'The from and to contract hashes are identical — there is nothing to migrate.',\n fix: 'Change the contract and run `prisma-next contract emit` before creating a new migration. To author a data-only migration on the current contract hash, pass `--from <hash>` explicitly.',\n }),\n );\n }\n\n const timestamp = new Date();\n const slug = options.name ?? 'migration';\n const dirName = formatMigrationDirName(timestamp, slug);\n const packageDir = join(migrationsDir, dirName);\n\n // `migration new` scaffolds an empty `migration.ts` for the user to\n // fill, so we attest over `ops: []`. Re-running self-emit after the\n // user adds operations will produce a different `migrationHash` (over\n // the real ops). This is intentional — there is no on-disk draft.\n const baseMetadata: Omit<MigrationMetadata, 'migrationHash'> = {\n from: fromHash,\n to: toStorageHash,\n fromContract,\n toContract: toContractJson,\n hints: {\n used: [],\n applied: [],\n plannerVersion: '1.0.0',\n },\n labels: [],\n providedInvariants: [],\n createdAt: timestamp.toISOString(),\n };\n const metadata: MigrationMetadata = {\n ...baseMetadata,\n migrationHash: computeMigrationHash(baseMetadata, []),\n };\n\n const migrations = getTargetMigrations(config.target);\n if (!migrations) {\n return notOk(\n errorTargetMigrationNotSupported({\n why: `Target \"${config.target.targetId}\" does not support migrations`,\n }),\n );\n }\n\n try {\n assertFrameworkComponentsCompatible(config.family.familyId, config.target.targetId, [\n config.target,\n config.adapter,\n ...(config.extensionPacks ?? []),\n ]);\n\n await writeMigrationPackage(packageDir, metadata, []);\n const destinationArtifacts = getEmittedArtifactPaths(contractPathAbsolute);\n await copyFilesWithRename(packageDir, [\n { sourcePath: destinationArtifacts.jsonPath, destName: 'end-contract.json' },\n { sourcePath: destinationArtifacts.dtsPath, destName: 'end-contract.d.ts' },\n ]);\n if (fromContractSourceDir !== null) {\n const sourceArtifacts = getEmittedArtifactPaths(\n join(fromContractSourceDir, 'end-contract.json'),\n );\n await copyFilesWithRename(packageDir, [\n { sourcePath: sourceArtifacts.jsonPath, destName: 'start-contract.json' },\n { sourcePath: sourceArtifacts.dtsPath, destName: 'start-contract.d.ts' },\n ]);\n }\n\n const stack = createControlStack(config);\n const familyInstance = config.family.create(stack);\n const planner = migrations.createPlanner(familyInstance);\n const emptyPlan = planner.emptyMigration({\n packageDir,\n contractJsonPath: join(packageDir, 'end-contract.json'),\n fromHash,\n toHash: toStorageHash,\n });\n await writeMigrationTs(packageDir, emptyPlan.renderTypeScript());\n\n return ok({\n ok: true as const,\n dir: relative(process.cwd(), packageDir),\n from: fromHash,\n to: toStorageHash,\n summary: `Scaffolded migration at ${relative(process.cwd(), packageDir)}`,\n });\n } catch (error) {\n if (CliStructuredError.is(error)) {\n return notOk(error);\n }\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to scaffold migration: ${error instanceof Error ? error.message : String(error)}`,\n }),\n );\n }\n}\n\nexport function createMigrationNewCommand(): Command {\n const command = new Command('new');\n setCommandDescriptions(\n command,\n 'Scaffold a new migration for manual authoring',\n 'Creates a migration package with a migration.ts file for manual authoring.\\n' +\n 'Write the migration body in migration.ts, then run the file with Node\\n' +\n '(`node migration.ts`) to self-emit ops.json and attest the package.',\n );\n setCommandExamples(command, [\n 'prisma-next migration new --name split-name',\n 'prisma-next migration new --name custom-fk --from sha256:abc...',\n ]);\n addGlobalOptions(command)\n .option('--name <slug>', 'Migration name (used in directory name)')\n .option('--from <hash>', 'Starting contract hash (default: latest migration target)')\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .action(async (options: MigrationNewOptions) => {\n const flags = parseGlobalFlags(options);\n const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });\n\n if (!flags.json && !flags.quiet) {\n const header = formatStyledHeader({\n command: 'migration new',\n description: 'Scaffold a new migration',\n details: [],\n flags,\n });\n ui.stderr(header);\n }\n\n const result = await executeMigrationNewCommand(options);\n\n const exitCode = handleResult(result, flags, ui, (value) => {\n if (flags.json) {\n ui.output(JSON.stringify(value, null, 2));\n } else if (!flags.quiet) {\n ui.output(`\\nScaffolded migration at ${value.dir}`);\n ui.output(` from: ${value.from}`);\n ui.output(` to: ${value.to}`);\n ui.output(\n `\\nEdit migration.ts, then run it directly (\\`node \"${value.dir}/migration.ts\"\\`) to self-emit and attest.`,\n );\n }\n });\n\n process.exit(exitCode);\n });\n\n return command;\n}\n"],"mappings":"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"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { Result } from "@prisma-next/utils/result";
|
|
3
|
+
import { OperationPreview } from "@prisma-next/framework-components/control";
|
|
3
4
|
|
|
4
5
|
//#region src/commands/migration-plan.d.ts
|
|
5
6
|
interface MigrationPlanResult {
|
|
6
7
|
readonly ok: boolean;
|
|
7
8
|
readonly noOp: boolean;
|
|
8
|
-
readonly from: string;
|
|
9
|
+
readonly from: string | null;
|
|
9
10
|
readonly to: string;
|
|
10
11
|
readonly dir?: string;
|
|
11
12
|
readonly operations: readonly {
|
|
@@ -13,7 +14,12 @@ interface MigrationPlanResult {
|
|
|
13
14
|
readonly label: string;
|
|
14
15
|
readonly operationClass: string;
|
|
15
16
|
}[];
|
|
16
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Family-agnostic textual preview of the migration plan operations.
|
|
19
|
+
* Replaces the previous `sql?: readonly string[]` field; consumers should
|
|
20
|
+
* read `result.preview?.statements`.
|
|
21
|
+
*/
|
|
22
|
+
readonly preview?: OperationPreview;
|
|
17
23
|
readonly summary: string;
|
|
18
24
|
/**
|
|
19
25
|
* When true, `migration.ts` was written but contains unfilled
|
|
@@ -33,17 +39,20 @@ type PrefixResolutionFailure = {
|
|
|
33
39
|
reason: 'not-found';
|
|
34
40
|
};
|
|
35
41
|
/**
|
|
36
|
-
* Resolve a migration
|
|
42
|
+
* Resolve a migration package by **target contract hash** (`metadata.to`)
|
|
43
|
+
* using exact match or prefix match.
|
|
37
44
|
*
|
|
45
|
+
* Note: matches `metadata.to` (the contract hash this migration produces),
|
|
46
|
+
* not `metadata.migrationHash` (the package's content-addressed identity).
|
|
38
47
|
* Tries exact match first, then prefix match (auto-prepending `sha256:` when
|
|
39
|
-
* the needle omits the scheme). Returns the matched
|
|
48
|
+
* the needle omits the scheme). Returns the matched package on success, or a
|
|
40
49
|
* discriminated failure indicating whether the prefix was ambiguous or simply
|
|
41
50
|
* not found.
|
|
42
51
|
*
|
|
43
52
|
* @internal Exported for testing only.
|
|
44
53
|
*/
|
|
45
54
|
declare function resolveBundleByPrefix<T extends {
|
|
46
|
-
|
|
55
|
+
metadata: {
|
|
47
56
|
to: string;
|
|
48
57
|
};
|
|
49
58
|
}>(bundles: readonly T[], needle: string): Result<T, PrefixResolutionFailure>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration-plan.d.mts","names":[],"sources":["../../src/commands/migration-plan.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"migration-plan.d.mts","names":[],"sources":["../../src/commands/migration-plan.ts"],"sourcesContent":[],"mappings":";;;;;UAyDiB,mBAAA;;EAAA,SAAA,IAAA,EAAA,OAAmB;EA6VpB,SAAA,IAAA,EAAA,MAAA,GAAA,IAAA;EAiIJ,SAAA,EAAA,EAAA,MAAA;EAiBI,SAAA,GAAA,CAAA,EAAA,MAAA;EACI,SAAA,UAAA,EAAA,SAAA;IAEV,SAAA,EAAA,EAAA,MAAA;IAAG,SAAA,KAAA,EAAA,MAAA;IAAV,SAAA,cAAA,EAAA,MAAA;EAAM,CAAA,EAAA;;;;;;qBAleY;;;;;;;;;;;;iBA6UL,0BAAA,CAAA,GAA8B;KAiIlC,uBAAA;;;;;;;;;;;;;;;;;;;iBAiBI;;;;qBACI,sBAEjB,OAAO,GAAG"}
|