@prisma-next/cli 0.5.0-dev.9 → 0.5.1
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 +61 -26
- package/dist/cli-errors-B9OBbled.d.mts +3 -0
- package/dist/cli-errors-D3_sMh2K.mjs +33 -0
- package/dist/cli-errors-D3_sMh2K.mjs.map +1 -0
- package/dist/cli.mjs +16 -78
- package/dist/cli.mjs.map +1 -1
- package/dist/client-BCnP7cHo.mjs +1485 -0
- package/dist/client-BCnP7cHo.mjs.map +1 -0
- package/dist/{result-handler-Ba3zWQsI.mjs → command-helpers-BeZHkxV8.mjs} +70 -47
- package/dist/command-helpers-BeZHkxV8.mjs.map +1 -0
- package/dist/commands/contract-emit.d.mts.map +1 -1
- package/dist/commands/contract-emit.mjs +2 -4
- package/dist/commands/contract-infer.d.mts.map +1 -1
- package/dist/commands/contract-infer.mjs +2 -4
- package/dist/commands/db-init.d.mts.map +1 -1
- package/dist/commands/db-init.mjs +16 -13
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.d.mts.map +1 -1
- package/dist/commands/db-schema.mjs +6 -7
- package/dist/commands/db-schema.mjs.map +1 -1
- package/dist/commands/db-sign.d.mts.map +1 -1
- package/dist/commands/db-sign.mjs +9 -9
- package/dist/commands/db-sign.mjs.map +1 -1
- package/dist/commands/db-update.d.mts.map +1 -1
- package/dist/commands/db-update.mjs +15 -13
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.d.mts.map +1 -1
- package/dist/commands/db-verify.mjs +1 -321
- package/dist/commands/migration-apply.d.mts +28 -13
- package/dist/commands/migration-apply.d.mts.map +1 -1
- package/dist/commands/migration-apply.mjs +55 -151
- package/dist/commands/migration-apply.mjs.map +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 +34 -40
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +33 -6
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +2 -348
- 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 +8 -12
- package/dist/commands/migration-ref.mjs.map +1 -1
- package/dist/commands/migration-show.d.mts +64 -10
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +166 -60
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +126 -5
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +2 -4
- package/dist/{config-loader-C25b63rJ.mjs → config-loader-B6sJjXTv.mjs} +3 -5
- package/dist/config-loader-B6sJjXTv.mjs.map +1 -0
- package/dist/config-loader.d.mts +0 -1
- package/dist/config-loader.d.mts.map +1 -1
- package/dist/config-loader.mjs +2 -3
- package/dist/contract-emit-9DBda5Ou.mjs +150 -0
- package/dist/contract-emit-9DBda5Ou.mjs.map +1 -0
- package/dist/contract-emit-B77TsJqf.mjs +327 -0
- package/dist/contract-emit-B77TsJqf.mjs.map +1 -0
- package/dist/{contract-enrichment-CAOELa-H.mjs → contract-enrichment-Dani0mMW.mjs} +4 -6
- package/dist/contract-enrichment-Dani0mMW.mjs.map +1 -0
- package/dist/{contract-infer-D9cC3rJm.mjs → contract-infer-ByxhPjpW.mjs} +13 -22
- package/dist/contract-infer-ByxhPjpW.mjs.map +1 -0
- package/dist/contract-space-aggregate-loader-BrwKK6Q6.mjs +160 -0
- package/dist/contract-space-aggregate-loader-BrwKK6Q6.mjs.map +1 -0
- package/dist/db-verify-Czm5T-J4.mjs +404 -0
- package/dist/db-verify-Czm5T-J4.mjs.map +1 -0
- package/dist/exports/config-types.mjs +1 -2
- package/dist/exports/control-api.d.mts +101 -586
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +4 -6
- package/dist/exports/index.d.mts.map +1 -1
- package/dist/exports/index.mjs +28 -30
- package/dist/exports/index.mjs.map +1 -1
- package/dist/exports/init-output.d.mts +2 -4
- package/dist/exports/init-output.d.mts.map +1 -1
- package/dist/exports/init-output.mjs +2 -3
- package/dist/{framework-components-Cr--XBKy.mjs → framework-components-ChqVUxR-.mjs} +3 -4
- package/dist/{framework-components-Cr--XBKy.mjs.map → framework-components-ChqVUxR-.mjs.map} +1 -1
- package/dist/global-flags-Icqpxk23.d.mts +12 -0
- package/dist/global-flags-Icqpxk23.d.mts.map +1 -0
- package/dist/helpers-eqdN8tH6.mjs +25 -0
- package/dist/helpers-eqdN8tH6.mjs.map +1 -0
- package/dist/{init-C5220SY9.mjs → init-DETSgw3h.mjs} +40 -49
- package/dist/init-DETSgw3h.mjs.map +1 -0
- package/dist/{inspect-live-schema-yrHAvG71.mjs → inspect-live-schema-DxdBd4Er.mjs} +10 -11
- package/dist/inspect-live-schema-DxdBd4Er.mjs.map +1 -0
- package/dist/migration-cli.d.mts +41 -12
- package/dist/migration-cli.d.mts.map +1 -1
- package/dist/migration-cli.mjs +309 -86
- package/dist/migration-cli.mjs.map +1 -1
- package/dist/{migration-command-scaffold-B3B09et6.mjs → migration-command-scaffold-BdV8JYXV.mjs} +8 -9
- package/dist/migration-command-scaffold-BdV8JYXV.mjs.map +1 -0
- package/dist/migration-plan-mRu5K81L.mjs +494 -0
- package/dist/migration-plan-mRu5K81L.mjs.map +1 -0
- package/dist/{migration-status-DUMiH8_G.mjs → migration-status-By9G5p2H.mjs} +270 -65
- package/dist/migration-status-By9G5p2H.mjs.map +1 -0
- package/dist/migrations-CTsyBXCA.mjs +229 -0
- package/dist/migrations-CTsyBXCA.mjs.map +1 -0
- package/dist/{output-BpcQrnnq.mjs → output-B16Kefzx.mjs} +9 -3
- package/dist/output-B16Kefzx.mjs.map +1 -0
- package/dist/{progress-adapter-DvQWB1nK.mjs → progress-adapter-DFfvZcYL.mjs} +2 -2
- package/dist/{progress-adapter-DvQWB1nK.mjs.map → progress-adapter-DFfvZcYL.mjs.map} +1 -1
- package/dist/result-handler-rmPVKIP2.mjs +25 -0
- package/dist/result-handler-rmPVKIP2.mjs.map +1 -0
- package/dist/rolldown-runtime-twds-ZHy.mjs +14 -0
- package/dist/{terminal-ui-C3ZLwQxK.mjs → terminal-ui-C_hFNbAn.mjs} +4 -28
- package/dist/terminal-ui-C_hFNbAn.mjs.map +1 -0
- package/dist/types-LItU7E4l.d.mts +856 -0
- package/dist/types-LItU7E4l.d.mts.map +1 -0
- package/dist/{verify-Bkycc-Tf.mjs → verify-CiwNWM9N.mjs} +3 -4
- package/dist/verify-CiwNWM9N.mjs.map +1 -0
- package/package.json +28 -26
- 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 +15 -3
- package/src/commands/db-update.ts +9 -4
- package/src/commands/db-verify.ts +47 -15
- package/src/commands/init/index.ts +1 -1
- package/src/commands/init/init.ts +2 -2
- package/src/commands/init/templates/code-templates.ts +26 -18
- package/src/commands/inspect-live-schema.ts +10 -5
- package/src/commands/migration-apply.ts +114 -212
- package/src/commands/migration-new.ts +42 -45
- package/src/commands/migration-plan.ts +213 -75
- package/src/commands/migration-ref.ts +8 -7
- package/src/commands/migration-show.ts +274 -70
- package/src/commands/migration-status.ts +491 -64
- package/src/config-path-validation.ts +0 -1
- package/src/control-api/client.ts +85 -5
- package/src/control-api/contract-enrichment.ts +6 -4
- package/src/control-api/operations/apply-aggregate.ts +290 -0
- package/src/control-api/operations/contract-emit.ts +198 -115
- package/src/control-api/operations/db-apply-aggregate.ts +399 -0
- package/src/control-api/operations/db-init.ts +51 -253
- package/src/control-api/operations/db-update.ts +66 -183
- package/src/control-api/operations/db-verify.ts +342 -0
- package/src/control-api/operations/migration-apply.ts +430 -131
- package/src/control-api/types.ts +278 -29
- package/src/exports/control-api.ts +15 -3
- package/src/load-ts-contract.ts +28 -26
- package/src/migration-cli.ts +445 -122
- package/src/utils/cli-errors.ts +49 -2
- package/src/utils/combine-schema-results.ts +84 -0
- package/src/utils/command-helpers.ts +69 -25
- package/src/utils/contract-space-aggregate-loader.ts +177 -0
- package/src/utils/contract-space-seed-phase.ts +201 -0
- package/src/utils/emit-queue.ts +26 -0
- package/src/utils/extension-pack-inputs.ts +162 -0
- package/src/utils/formatters/graph-migration-mapper.ts +7 -3
- package/src/utils/formatters/migrations.ts +255 -77
- package/src/utils/publish-contract-artifact-pair.ts +134 -0
- package/dist/cli-errors-BFYgBH3L.d.mts +0 -4
- package/dist/cli-errors-Cd79vmTH.mjs +0 -5
- package/dist/client-CrsnY58k.mjs +0 -997
- package/dist/client-CrsnY58k.mjs.map +0 -1
- package/dist/commands/db-verify.mjs.map +0 -1
- package/dist/commands/migration-plan.mjs.map +0 -1
- package/dist/config-loader-C25b63rJ.mjs.map +0 -1
- package/dist/contract-emit--feXyNd7.mjs +0 -4
- 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-enrichment-CAOELa-H.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/init-C5220SY9.mjs.map +0 -1
- package/dist/inspect-live-schema-yrHAvG71.mjs.map +0 -1
- package/dist/migration-command-scaffold-B3B09et6.mjs.map +0 -1
- package/dist/migration-status-DUMiH8_G.mjs.map +0 -1
- package/dist/migrations-Bo5WtTla.mjs +0 -153
- package/dist/migrations-Bo5WtTla.mjs.map +0 -1
- package/dist/output-BpcQrnnq.mjs.map +0 -1
- package/dist/result-handler-Ba3zWQsI.mjs.map +0 -1
- package/dist/terminal-ui-C3ZLwQxK.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/dist/verify-Bkycc-Tf.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
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { materialiseExtensionMigrationPackageIfMissing } from '@prisma-next/migration-tools/io';
|
|
2
|
+
import type { MigrationMetadata } from '@prisma-next/migration-tools/metadata';
|
|
3
|
+
import type { MigrationOps } from '@prisma-next/migration-tools/package';
|
|
4
|
+
import {
|
|
5
|
+
emitContractSpaceArtefacts,
|
|
6
|
+
planAllSpaces,
|
|
7
|
+
readContractSpaceHeadRef,
|
|
8
|
+
type SpacePlanOutput,
|
|
9
|
+
spaceMigrationDirectory,
|
|
10
|
+
} from '@prisma-next/migration-tools/spaces';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* In-memory authored migration package shipped by an extension descriptor.
|
|
14
|
+
* Mirrors `MigrationPackage` from `@prisma-next/migration-tools/io` (the
|
|
15
|
+
* on-disk shape minus `dirPath`); redeclared structurally here so the
|
|
16
|
+
* CLI helper does not couple to any family's `ExtensionMigrationPackage`
|
|
17
|
+
* type — any family that ships pre-built migration packages can pass
|
|
18
|
+
* them through unchanged.
|
|
19
|
+
*/
|
|
20
|
+
export interface DescriptorMigrationPackage {
|
|
21
|
+
readonly dirName: string;
|
|
22
|
+
readonly metadata: MigrationMetadata;
|
|
23
|
+
readonly ops: MigrationOps;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Minimal descriptor view consumed by the seed phase. Mirrors the shape
|
|
28
|
+
* the SQL family ships on each declared extension entry; only the fields
|
|
29
|
+
* the seed phase needs are surfaced.
|
|
30
|
+
*/
|
|
31
|
+
export interface SeedPhaseExtensionInput {
|
|
32
|
+
readonly id: string;
|
|
33
|
+
readonly contractSpace?: {
|
|
34
|
+
readonly contractJson: unknown;
|
|
35
|
+
readonly headRef: { readonly hash: string; readonly invariants: readonly string[] };
|
|
36
|
+
readonly migrations: readonly DescriptorMigrationPackage[];
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ContractSpaceSeedPhaseInputs {
|
|
41
|
+
readonly migrationsDir: string;
|
|
42
|
+
readonly extensionPacks: ReadonlyArray<SeedPhaseExtensionInput>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* One per-space record describing what the seed phase did for an
|
|
47
|
+
* extension contract space. Surfaced verbatim by the caller (typically
|
|
48
|
+
* `migration plan`) so users see a single line per touched extension.
|
|
49
|
+
*
|
|
50
|
+
* - `action: 'updated'` — either the on-disk head pointer changed, or
|
|
51
|
+
* one or more new descriptor-shipped migration packages were
|
|
52
|
+
* materialised into `migrations/<spaceId>/<dirName>/`.
|
|
53
|
+
* - `action: 'unchanged'` — the on-disk head already matched the
|
|
54
|
+
* descriptor and no new migration packages needed to be written.
|
|
55
|
+
*
|
|
56
|
+
* Either way, the artefacts (`contract.json`, `contract.d.ts`,
|
|
57
|
+
* `refs/head.json`) are re-emitted: the framework owns those files and
|
|
58
|
+
* makes the re-emit observably idempotent at the byte level.
|
|
59
|
+
*/
|
|
60
|
+
export interface ContractSpaceSeedPhaseRecord {
|
|
61
|
+
readonly spaceId: string;
|
|
62
|
+
readonly action: 'updated' | 'unchanged';
|
|
63
|
+
readonly priorHash: string | null;
|
|
64
|
+
readonly newHash: string;
|
|
65
|
+
readonly newMigrationDirs: readonly string[];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface ContractSpaceSeedPhaseResult {
|
|
69
|
+
readonly seeded: readonly ContractSpaceSeedPhaseRecord[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Phase-1 of the two-phase `migration plan` pipeline (sub-spec § 4).
|
|
74
|
+
*
|
|
75
|
+
* For every extension that exposes a `contractSpace`:
|
|
76
|
+
*
|
|
77
|
+
* 1. Read the on-disk head ref (returns `null` on first emit).
|
|
78
|
+
* 2. Re-emit `contract.json` / `contract.d.ts` / `refs/head.json`
|
|
79
|
+
* unconditionally via {@link emitContractSpaceArtefacts}. The
|
|
80
|
+
* framework owns these files; re-emit is the contract.
|
|
81
|
+
* 3. Materialise any descriptor-shipped migration packages not yet on
|
|
82
|
+
* disk via {@link materialiseExtensionMigrationPackageIfMissing}.
|
|
83
|
+
* Existing packages are left untouched (by-existence skip).
|
|
84
|
+
*
|
|
85
|
+
* The return value lets the caller render a per-space status line and
|
|
86
|
+
* lets the phase-2 aggregate loader run on a now-consistent disk state
|
|
87
|
+
* (every loaded extension is guaranteed to have its head ref pinned
|
|
88
|
+
* to the descriptor's hash and to ship every package the descriptor
|
|
89
|
+
* declares).
|
|
90
|
+
*
|
|
91
|
+
* Output ordering is deterministic and alphabetical by spaceId (via
|
|
92
|
+
* {@link planAllSpaces}, which also detects duplicate spaceIds). This
|
|
93
|
+
* matches the canonical sort order used by every other aggregate
|
|
94
|
+
* surface (`migration apply`, `migration status`, the runner).
|
|
95
|
+
*/
|
|
96
|
+
export async function runContractSpaceSeedPhase(
|
|
97
|
+
inputs: ContractSpaceSeedPhaseInputs,
|
|
98
|
+
): Promise<ContractSpaceSeedPhaseResult> {
|
|
99
|
+
const planInputs = inputs.extensionPacks
|
|
100
|
+
.filter(
|
|
101
|
+
(
|
|
102
|
+
pack,
|
|
103
|
+
): pack is SeedPhaseExtensionInput & {
|
|
104
|
+
contractSpace: NonNullable<SeedPhaseExtensionInput['contractSpace']>;
|
|
105
|
+
} => pack.contractSpace !== undefined,
|
|
106
|
+
)
|
|
107
|
+
.map((pack) => ({
|
|
108
|
+
spaceId: pack.id,
|
|
109
|
+
priorContract: null,
|
|
110
|
+
newContract: pack.contractSpace.contractJson,
|
|
111
|
+
__pack: pack.contractSpace,
|
|
112
|
+
}));
|
|
113
|
+
|
|
114
|
+
// `planAllSpaces` brings deterministic alphabetical ordering and
|
|
115
|
+
// duplicate-spaceId detection. The "planner" callback is a no-op
|
|
116
|
+
// pass-through that simply returns the descriptor's pre-built
|
|
117
|
+
// migration packages.
|
|
118
|
+
const planned: readonly SpacePlanOutput<DescriptorMigrationPackage>[] = planAllSpaces(
|
|
119
|
+
planInputs,
|
|
120
|
+
(input) =>
|
|
121
|
+
(
|
|
122
|
+
input as typeof input & {
|
|
123
|
+
readonly __pack: NonNullable<SeedPhaseExtensionInput['contractSpace']>;
|
|
124
|
+
}
|
|
125
|
+
).__pack.migrations,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Reassemble a spaceId → descriptor lookup so the loop below can read
|
|
129
|
+
// the contractJson / headRef without leaking the typed-cast back into
|
|
130
|
+
// `planAllSpaces`'s output shape.
|
|
131
|
+
const descriptorBySpace = new Map<
|
|
132
|
+
string,
|
|
133
|
+
NonNullable<SeedPhaseExtensionInput['contractSpace']>
|
|
134
|
+
>();
|
|
135
|
+
for (const pack of inputs.extensionPacks) {
|
|
136
|
+
if (pack.contractSpace !== undefined) descriptorBySpace.set(pack.id, pack.contractSpace);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const seeded: ContractSpaceSeedPhaseRecord[] = [];
|
|
140
|
+
for (const space of planned) {
|
|
141
|
+
const descriptor = descriptorBySpace.get(space.spaceId);
|
|
142
|
+
if (descriptor === undefined) continue;
|
|
143
|
+
|
|
144
|
+
const onDiskHeadRef = await readContractSpaceHeadRef(inputs.migrationsDir, space.spaceId);
|
|
145
|
+
const priorHash = onDiskHeadRef?.hash ?? null;
|
|
146
|
+
|
|
147
|
+
await emitContractSpaceArtefacts(inputs.migrationsDir, space.spaceId, {
|
|
148
|
+
contract: descriptor.contractJson,
|
|
149
|
+
contractDts: buildPlaceholderContractDts(space.spaceId),
|
|
150
|
+
headRef: { hash: descriptor.headRef.hash, invariants: descriptor.headRef.invariants },
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const spaceDir = spaceMigrationDirectory(inputs.migrationsDir, space.spaceId);
|
|
154
|
+
const newMigrationDirs: string[] = [];
|
|
155
|
+
for (const pkg of space.migrationPackages) {
|
|
156
|
+
const { written } = await materialiseExtensionMigrationPackageIfMissing(spaceDir, pkg);
|
|
157
|
+
if (written) newMigrationDirs.push(pkg.dirName);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const action: ContractSpaceSeedPhaseRecord['action'] =
|
|
161
|
+
priorHash !== descriptor.headRef.hash || newMigrationDirs.length > 0
|
|
162
|
+
? 'updated'
|
|
163
|
+
: 'unchanged';
|
|
164
|
+
|
|
165
|
+
seeded.push({
|
|
166
|
+
spaceId: space.spaceId,
|
|
167
|
+
action,
|
|
168
|
+
priorHash,
|
|
169
|
+
newHash: descriptor.headRef.hash,
|
|
170
|
+
newMigrationDirs,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return { seeded };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Placeholder `.d.ts` content for an extension space's on-disk mirror.
|
|
179
|
+
*
|
|
180
|
+
* Rendering a fully-typed `.d.ts` for an extension contract requires
|
|
181
|
+
* the SQL-family renderer with the codec / typemap registry threaded
|
|
182
|
+
* through; until that integration ships, the on-disk `.d.ts` is a
|
|
183
|
+
* stub `export {};` module that documents how consumers should
|
|
184
|
+
* validate the sibling `contract.json`. The stub typechecks on its
|
|
185
|
+
* own and does not need any TypeScript suppressions.
|
|
186
|
+
*/
|
|
187
|
+
function buildPlaceholderContractDts(spaceId: string): string {
|
|
188
|
+
return [
|
|
189
|
+
'/**',
|
|
190
|
+
` * Placeholder \`.d.ts\` for extension space "${spaceId}".`,
|
|
191
|
+
' *',
|
|
192
|
+
' * The framework re-emits this file on every `migration plan` run',
|
|
193
|
+
' * alongside `contract.json` and `refs/head.json`. A typed `.d.ts`',
|
|
194
|
+
' * rendering pass for extension contracts is tracked separately;',
|
|
195
|
+
' * until that ships, consumers should import `contract.json`',
|
|
196
|
+
' * directly with `validateContract<…>(…)`.',
|
|
197
|
+
' */',
|
|
198
|
+
'export {};',
|
|
199
|
+
'',
|
|
200
|
+
].join('\n');
|
|
201
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-output FIFO queue for `executeContractEmit`.
|
|
3
|
+
*
|
|
4
|
+
* Ensures that at most one emit (load → resolve source → emit bytes → publish)
|
|
5
|
+
* runs per output JSON path at a time. Concurrent calls for the same path
|
|
6
|
+
* line up behind the in-flight one and run in submission order; the user-visible
|
|
7
|
+
* outcome is "last submission wins on disk" without any supersession bookkeeping.
|
|
8
|
+
*
|
|
9
|
+
* Long-lived hosts (Vite dev server, watch CLIs) must call `disposeEmitQueue`
|
|
10
|
+
* when they stop publishing to a path, otherwise the module-global `Map`
|
|
11
|
+
* accumulates one entry per unique output path for the lifetime of the process.
|
|
12
|
+
*/
|
|
13
|
+
const emitQueues = new Map<string, Promise<unknown>>();
|
|
14
|
+
|
|
15
|
+
export function queueEmitByOutput<T>(outputJsonPath: string, action: () => Promise<T>): Promise<T> {
|
|
16
|
+
const previous = emitQueues.get(outputJsonPath) ?? Promise.resolve();
|
|
17
|
+
// Continue regardless of the previous task's outcome — a failed emit must not
|
|
18
|
+
// block subsequent ones. The current task's outcome propagates via `next`.
|
|
19
|
+
const next = previous.then(action, action);
|
|
20
|
+
emitQueues.set(outputJsonPath, next);
|
|
21
|
+
return next;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function disposeEmitQueue(outputJsonPath: string): void {
|
|
25
|
+
emitQueues.delete(outputJsonPath);
|
|
26
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single descriptor-import boundary for CLI consumers of `Config.extensionPacks`.
|
|
3
|
+
*
|
|
4
|
+
* Every CLI command / utility that reads an extension descriptor's
|
|
5
|
+
* `contractSpace` projection (loader, migrate-pass, extension-migrations
|
|
6
|
+
* pass, migration commands) goes through {@link toExtensionInputs}. The
|
|
7
|
+
* structural cast `pack as { contractSpace?: ... }` lives **only** here —
|
|
8
|
+
* downstream code consumes the canonical shape and maps it to its own
|
|
9
|
+
* narrower shape via the per-consumer adapters below.
|
|
10
|
+
*
|
|
11
|
+
* The CLI receives extension descriptors typed against the SQL family
|
|
12
|
+
* (or any other family in the future); this helper only depends on the
|
|
13
|
+
* structural shape of `contractSpace`. SQL-family callers pass the same
|
|
14
|
+
* `contractJson` / `headRef.hash` value through unchanged.
|
|
15
|
+
*/
|
|
16
|
+
import type { DeclaredExtensionEntry } from '@prisma-next/migration-tools/aggregate';
|
|
17
|
+
import type { MigrationMetadata } from '@prisma-next/migration-tools/metadata';
|
|
18
|
+
import type { MigrationOps } from '@prisma-next/migration-tools/package';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* In-memory authored migration package shipped by an extension descriptor.
|
|
22
|
+
* Mirrors the `MigrationPackage` shape from
|
|
23
|
+
* `@prisma-next/framework-components/control` minus `dirPath`; redeclared
|
|
24
|
+
* structurally here so the helper does not couple to the SQL family's
|
|
25
|
+
* `ExtensionMigrationPackage` type.
|
|
26
|
+
*/
|
|
27
|
+
export interface DescriptorMigrationPackage {
|
|
28
|
+
readonly dirName: string;
|
|
29
|
+
readonly metadata: MigrationMetadata;
|
|
30
|
+
readonly ops: MigrationOps;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The most-general projection of a single declared extension pack
|
|
35
|
+
* needed by the CLI's descriptor-import boundary.
|
|
36
|
+
*
|
|
37
|
+
* - `id` / `targetId` are always present.
|
|
38
|
+
* - `contractSpace` is present only when the extension declares one.
|
|
39
|
+
* When present, it carries the canonical inputs every downstream
|
|
40
|
+
* consumer needs — `contractJson`, `headRef`, and the descriptor's
|
|
41
|
+
* pre-built migration packages.
|
|
42
|
+
*/
|
|
43
|
+
export interface ExtensionPackInput {
|
|
44
|
+
readonly id: string;
|
|
45
|
+
readonly targetId: string;
|
|
46
|
+
readonly contractSpace?: {
|
|
47
|
+
readonly contractJson: unknown;
|
|
48
|
+
readonly headRef: {
|
|
49
|
+
readonly hash: string;
|
|
50
|
+
readonly invariants: readonly string[];
|
|
51
|
+
};
|
|
52
|
+
readonly migrations: readonly DescriptorMigrationPackage[];
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Structural shape we read off each `Config.extensionPacks` entry.
|
|
58
|
+
*
|
|
59
|
+
* The CLI is the descriptor-import boundary; `extensionPacks` is the only
|
|
60
|
+
* surface where the SQL-family-typed `ControlExtensionDescriptor` flows
|
|
61
|
+
* into framework-neutral helpers. The structural cast lives here, and
|
|
62
|
+
* here alone — every other CLI consumer reads the canonical
|
|
63
|
+
* {@link ExtensionPackInput} shape produced by {@link toExtensionInputs}.
|
|
64
|
+
*/
|
|
65
|
+
type ExtensionPackLike = {
|
|
66
|
+
readonly id: string;
|
|
67
|
+
readonly targetId: string;
|
|
68
|
+
readonly contractSpace?: {
|
|
69
|
+
readonly contractJson: unknown;
|
|
70
|
+
readonly headRef: {
|
|
71
|
+
readonly hash: string;
|
|
72
|
+
readonly invariants: readonly string[];
|
|
73
|
+
};
|
|
74
|
+
readonly migrations?: readonly DescriptorMigrationPackage[];
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Project the CLI's `Config.extensionPacks` array into the canonical
|
|
80
|
+
* {@link ExtensionPackInput} shape. The single `as ExtensionPackLike`
|
|
81
|
+
* structural cast in the CLI lives inside this function.
|
|
82
|
+
*/
|
|
83
|
+
export function toExtensionInputs(
|
|
84
|
+
extensionPacks: ReadonlyArray<unknown>,
|
|
85
|
+
): readonly ExtensionPackInput[] {
|
|
86
|
+
return extensionPacks.map((raw) => {
|
|
87
|
+
const pack = raw as ExtensionPackLike;
|
|
88
|
+
if (pack.contractSpace === undefined) {
|
|
89
|
+
return { id: pack.id, targetId: pack.targetId };
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
id: pack.id,
|
|
93
|
+
targetId: pack.targetId,
|
|
94
|
+
contractSpace: {
|
|
95
|
+
contractJson: pack.contractSpace.contractJson,
|
|
96
|
+
headRef: pack.contractSpace.headRef,
|
|
97
|
+
migrations: pack.contractSpace.migrations ?? [],
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Per-consumer adapters: take the canonical `ExtensionPackInput[]` and
|
|
105
|
+
// project to whatever narrower shape the downstream primitive needs.
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Aggregate-loader projection. Surfaces `id` + `targetId` per
|
|
110
|
+
* contract-space-bearing extension to
|
|
111
|
+
* {@link import('./contract-space-aggregate-loader').buildContractSpaceAggregate}.
|
|
112
|
+
*
|
|
113
|
+
* Codec-only extensions (no `contractSpace` declaration) are filtered
|
|
114
|
+
* out: they are not contract-space members, so the aggregate loader
|
|
115
|
+
* has nothing to do with them. Filtering happens at this descriptor-
|
|
116
|
+
* import boundary so the loader stays oblivious to that distinction —
|
|
117
|
+
* every entry it sees expects an on-disk `migrations/<id>/` directory.
|
|
118
|
+
*/
|
|
119
|
+
export function toDeclaredExtensions(
|
|
120
|
+
inputs: ReadonlyArray<ExtensionPackInput>,
|
|
121
|
+
): readonly DeclaredExtensionEntry[] {
|
|
122
|
+
const entries: DeclaredExtensionEntry[] = [];
|
|
123
|
+
for (const pack of inputs) {
|
|
124
|
+
if (pack.contractSpace === undefined) continue;
|
|
125
|
+
entries.push({ id: pack.id, targetId: pack.targetId });
|
|
126
|
+
}
|
|
127
|
+
return entries;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Minimal aggregate-loader projection that extracts `id` + `targetId`
|
|
132
|
+
* from raw extension pack descriptors **without invoking any
|
|
133
|
+
* `contractSpace` accessor**. Inspects the own-property descriptor so
|
|
134
|
+
* that getter-backed `contractSpace` declarations are detected but
|
|
135
|
+
* never called.
|
|
136
|
+
*
|
|
137
|
+
* Inclusion semantics match {@link toDeclaredExtensions}: a data
|
|
138
|
+
* property whose value is explicitly `undefined` is treated as "no
|
|
139
|
+
* contract-space declaration" and skipped, mirroring the
|
|
140
|
+
* `pack.contractSpace === undefined` check used on canonicalised
|
|
141
|
+
* inputs. Prototype-chain `contractSpace` properties (no own
|
|
142
|
+
* descriptor) are also skipped.
|
|
143
|
+
*
|
|
144
|
+
* This variant must be used by `buildContractSpaceAggregate` so that
|
|
145
|
+
* the aggregate path (including `db verify`) never reads
|
|
146
|
+
* `contractSpace.contractJson` from extension descriptors — the loader
|
|
147
|
+
* always reads the contract from on-disk artefacts instead.
|
|
148
|
+
*/
|
|
149
|
+
export function toDeclaredExtensionsFromRaw(
|
|
150
|
+
extensionPacks: ReadonlyArray<unknown>,
|
|
151
|
+
): readonly DeclaredExtensionEntry[] {
|
|
152
|
+
const entries: DeclaredExtensionEntry[] = [];
|
|
153
|
+
for (const raw of extensionPacks) {
|
|
154
|
+
if (typeof raw !== 'object' || raw === null) continue;
|
|
155
|
+
const descriptor = Object.getOwnPropertyDescriptor(raw, 'contractSpace');
|
|
156
|
+
if (descriptor === undefined) continue;
|
|
157
|
+
if ('value' in descriptor && descriptor.value === undefined) continue;
|
|
158
|
+
const pack = raw as { readonly id: string; readonly targetId: string };
|
|
159
|
+
entries.push({ id: pack.id, targetId: pack.targetId });
|
|
160
|
+
}
|
|
161
|
+
return entries;
|
|
162
|
+
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Maps MigrationGraph + status info to the generic graph renderer types.
|
|
3
3
|
*/
|
|
4
4
|
import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
5
|
+
import type { MigrationGraph } from '@prisma-next/migration-tools/graph';
|
|
6
|
+
import { findPath } from '@prisma-next/migration-tools/migration-graph';
|
|
7
7
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
8
8
|
|
|
9
9
|
import type { StatusRef } from '../migration-types';
|
|
@@ -106,7 +106,11 @@ export function migrationGraphToRenderInput(input: MigrationGraphInput): Migrati
|
|
|
106
106
|
for (const entry of entries) {
|
|
107
107
|
const status = statusByDirName.get(entry.dirName);
|
|
108
108
|
const icon = status ? STATUS_ICON[status] : '';
|
|
109
|
-
const
|
|
109
|
+
const invariantsSuffix =
|
|
110
|
+
entry.invariants.length > 0
|
|
111
|
+
? ` provides [${entry.invariants.map((id) => JSON.stringify(id)).join(', ')}]`
|
|
112
|
+
: '';
|
|
113
|
+
const label = `${entry.dirName}${icon}${invariantsSuffix}`;
|
|
110
114
|
|
|
111
115
|
edgeList.push({
|
|
112
116
|
from: toShortId(entry.from),
|