@prisma-next/cli 0.5.0-dev.67 → 0.5.0-dev.68

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.
Files changed (86) hide show
  1. package/dist/cli.mjs +8 -8
  2. package/dist/cli.mjs.map +1 -1
  3. package/dist/client-0ZX24FXF.mjs +1398 -0
  4. package/dist/client-0ZX24FXF.mjs.map +1 -0
  5. package/dist/commands/contract-emit.mjs +1 -1
  6. package/dist/commands/contract-infer.mjs +1 -1
  7. package/dist/commands/db-init.d.mts.map +1 -1
  8. package/dist/commands/db-init.mjs +7 -5
  9. package/dist/commands/db-init.mjs.map +1 -1
  10. package/dist/commands/db-schema.mjs +2 -2
  11. package/dist/commands/db-sign.mjs +2 -2
  12. package/dist/commands/db-update.d.mts.map +1 -1
  13. package/dist/commands/db-update.mjs +7 -5
  14. package/dist/commands/db-update.mjs.map +1 -1
  15. package/dist/commands/db-verify.d.mts.map +1 -1
  16. package/dist/commands/db-verify.mjs +1 -320
  17. package/dist/commands/migration-apply.mjs +11 -11
  18. package/dist/commands/migration-apply.mjs.map +1 -1
  19. package/dist/commands/migration-new.mjs +5 -5
  20. package/dist/commands/migration-new.mjs.map +1 -1
  21. package/dist/commands/migration-plan.d.mts.map +1 -1
  22. package/dist/commands/migration-plan.mjs +1 -344
  23. package/dist/commands/migration-ref.d.mts +1 -1
  24. package/dist/commands/migration-ref.mjs +1 -1
  25. package/dist/commands/migration-show.d.mts +1 -1
  26. package/dist/commands/migration-show.d.mts.map +1 -1
  27. package/dist/commands/migration-show.mjs +8 -7
  28. package/dist/commands/migration-show.mjs.map +1 -1
  29. package/dist/commands/migration-status.mjs +1 -1
  30. package/dist/{contract-emit-B0nGrDtk.mjs → contract-emit-DkMqO7f2.mjs} +2 -2
  31. package/dist/{contract-emit-B0nGrDtk.mjs.map → contract-emit-DkMqO7f2.mjs.map} +1 -1
  32. package/dist/{contract-infer-BjMJaOOa.mjs → contract-infer-BDKAE0B0.mjs} +3 -3
  33. package/dist/{contract-infer-BjMJaOOa.mjs.map → contract-infer-BDKAE0B0.mjs.map} +1 -1
  34. package/dist/db-verify-B4TdDKOI.mjs +403 -0
  35. package/dist/db-verify-B4TdDKOI.mjs.map +1 -0
  36. package/dist/exports/control-api.d.mts +201 -3
  37. package/dist/exports/control-api.d.mts.map +1 -1
  38. package/dist/exports/control-api.mjs +2 -2
  39. package/dist/exports/index.d.mts.map +1 -1
  40. package/dist/exports/index.mjs +17 -17
  41. package/dist/exports/index.mjs.map +1 -1
  42. package/dist/exports/init-output.mjs +1 -1
  43. package/dist/{init-C3qdc0Sh.mjs → init-Deo7U8_U.mjs} +2 -2
  44. package/dist/{init-C3qdc0Sh.mjs.map → init-Deo7U8_U.mjs.map} +1 -1
  45. package/dist/{inspect-live-schema-33rxnu0K.mjs → inspect-live-schema-BAgQMYpD.mjs} +3 -3
  46. package/dist/{inspect-live-schema-33rxnu0K.mjs.map → inspect-live-schema-BAgQMYpD.mjs.map} +1 -1
  47. package/dist/{migration-command-scaffold-DQo4R0XT.mjs → migration-command-scaffold-B8J702Uh.mjs} +3 -3
  48. package/dist/{migration-command-scaffold-DQo4R0XT.mjs.map → migration-command-scaffold-B8J702Uh.mjs.map} +1 -1
  49. package/dist/migration-plan-BcKNnTM7.mjs +530 -0
  50. package/dist/migration-plan-BcKNnTM7.mjs.map +1 -0
  51. package/dist/{migration-status-C_2FSkbf.mjs → migration-status-CjwB2of-.mjs} +6 -6
  52. package/dist/{migration-status-C_2FSkbf.mjs.map → migration-status-CjwB2of-.mjs.map} +1 -1
  53. package/dist/{output-BTgnZ5c_.mjs → output-DnjfCC_u.mjs} +1 -1
  54. package/dist/{output-BTgnZ5c_.mjs.map → output-DnjfCC_u.mjs.map} +1 -1
  55. package/dist/{result-handler-C0QeiqKO.mjs → result-handler-DWb1rFS-.mjs} +18 -3
  56. package/dist/result-handler-DWb1rFS-.mjs.map +1 -0
  57. package/package.json +15 -15
  58. package/src/commands/db-init.ts +13 -3
  59. package/src/commands/db-update.ts +7 -3
  60. package/src/commands/db-verify.ts +47 -15
  61. package/src/commands/init/index.ts +1 -1
  62. package/src/commands/init/init.ts +2 -2
  63. package/src/commands/migration-apply.ts +9 -9
  64. package/src/commands/migration-new.ts +4 -4
  65. package/src/commands/migration-plan.ts +66 -9
  66. package/src/commands/migration-show.ts +7 -5
  67. package/src/commands/migration-status.ts +3 -3
  68. package/src/control-api/client.ts +42 -0
  69. package/src/control-api/operations/db-apply-aggregate.ts +446 -0
  70. package/src/control-api/operations/db-init.ts +51 -258
  71. package/src/control-api/operations/db-update.ts +66 -188
  72. package/src/control-api/operations/db-verify.ts +342 -0
  73. package/src/control-api/types.ts +56 -0
  74. package/src/exports/control-api.ts +13 -2
  75. package/src/load-ts-contract.ts +28 -26
  76. package/src/utils/combine-schema-results.ts +84 -0
  77. package/src/utils/command-helpers.ts +24 -2
  78. package/src/utils/contract-space-aggregate-loader.ts +236 -0
  79. package/src/utils/contract-space-extension-migrations-pass.ts +120 -0
  80. package/src/utils/contract-space-migrate-pass.ts +156 -0
  81. package/dist/client-CW1hcUtM.mjs +0 -1025
  82. package/dist/client-CW1hcUtM.mjs.map +0 -1
  83. package/dist/commands/db-verify.mjs.map +0 -1
  84. package/dist/commands/migration-plan.mjs.map +0 -1
  85. package/dist/result-handler-C0QeiqKO.mjs.map +0 -1
  86. /package/dist/{cli-errors-BWn943z2.d.mts → cli-errors-QH8kf-C2.d.mts} +0 -0
@@ -43,6 +43,15 @@ import {
43
43
  setCommandDescriptions,
44
44
  setCommandExamples,
45
45
  } from '../utils/command-helpers';
46
+ import {
47
+ type ExtensionMigrationsExtensionInput,
48
+ runContractSpaceExtensionMigrationsPass,
49
+ } from '../utils/contract-space-extension-migrations-pass';
50
+ import {
51
+ formatContractSpaceDriftWarning,
52
+ type MigrateExtensionInput,
53
+ runContractSpaceMigratePass,
54
+ } from '../utils/contract-space-migrate-pass';
46
55
  import { formatStyledHeader } from '../utils/formatters/styled';
47
56
  import { assertFrameworkComponentsCompatible } from '../utils/framework-components';
48
57
  import type { CommonCommandOptions } from '../utils/global-flags';
@@ -92,10 +101,8 @@ async function executeMigrationPlanCommand(
92
101
  startTime: number,
93
102
  ): Promise<Result<MigrationPlanResult, CliStructuredError>> {
94
103
  const config = await loadConfig(options.config);
95
- const { configPath, migrationsDir, migrationsRelative } = resolveMigrationPaths(
96
- options.config,
97
- config,
98
- );
104
+ const { configPath, migrationsDir, appMigrationsDir, appMigrationsRelative } =
105
+ resolveMigrationPaths(options.config, config);
99
106
 
100
107
  const contractPathAbsolute = resolveContractPath(config);
101
108
  const contractPath = relative(process.cwd(), contractPathAbsolute);
@@ -104,7 +111,7 @@ async function executeMigrationPlanCommand(
104
111
  const details: Array<{ label: string; value: string }> = [
105
112
  { label: 'config', value: configPath },
106
113
  { label: 'contract', value: contractPath },
107
- { label: 'migrations', value: migrationsRelative },
114
+ { label: 'migrations', value: appMigrationsRelative },
108
115
  ];
109
116
  if (options.from) {
110
117
  details.push({ label: 'from', value: options.from });
@@ -170,7 +177,7 @@ async function executeMigrationPlanCommand(
170
177
  let fromContractSourceDir: string | null = null;
171
178
 
172
179
  try {
173
- const { bundles, graph } = await loadMigrationPackages(migrationsDir);
180
+ const { bundles, graph } = await loadMigrationPackages(appMigrationsDir);
174
181
 
175
182
  if (options.from) {
176
183
  const resolved = resolveBundleByPrefix(bundles, options.from);
@@ -179,11 +186,11 @@ async function executeMigrationPlanCommand(
179
186
  return notOk(
180
187
  f.reason === 'ambiguous'
181
188
  ? errorRuntime('Multiple matching migrations found', {
182
- why: `Prefix "${options.from}" matches ${f.count} migrations in ${migrationsRelative}`,
189
+ why: `Prefix "${options.from}" matches ${f.count} migrations in ${appMigrationsRelative}`,
183
190
  fix: 'Provide a longer prefix to disambiguate, or omit --from to use the latest migration target.',
184
191
  })
185
192
  : errorRuntime('Starting contract not found', {
186
- why: `No migration with to hash matching "${options.from}" exists in ${migrationsRelative}`,
193
+ why: `No migration with to hash matching "${options.from}" exists in ${appMigrationsRelative}`,
187
194
  fix: 'Check that the --from hash matches a known migration target hash, or omit --from to use the latest migration target.',
188
195
  }),
189
196
  );
@@ -220,6 +227,56 @@ async function executeMigrationPlanCommand(
220
227
  );
221
228
  }
222
229
 
230
+ // Per-space migrate pass: drift detection + on-disk artefact emission for
231
+ // every loaded extension that exposes a `contractSpace`. Runs *before*
232
+ // the app-space no-op check so that an extension bump alone (with no
233
+ // structural app-space change) still re-pins extension artefacts on
234
+ // disk. Drift warnings are non-fatal — the on-disk artefacts are refreshed
235
+ // and the user is notified that the bump is being captured.
236
+ const extensionInputs: readonly MigrateExtensionInput[] = (config.extensionPacks ?? []).map(
237
+ (pack) => {
238
+ const cs = (pack as { readonly contractSpace?: MigrateExtensionInput['contractSpace'] })
239
+ .contractSpace;
240
+ return cs !== undefined ? { id: pack.id, contractSpace: cs } : { id: pack.id };
241
+ },
242
+ );
243
+ const migratePass = await runContractSpaceMigratePass({
244
+ migrationsDir,
245
+ extensionPacks: extensionInputs,
246
+ });
247
+ if (!flags.json && !flags.quiet) {
248
+ for (const drift of migratePass.drifts) {
249
+ if (drift.kind === 'drift') {
250
+ ui.stderr(formatContractSpaceDriftWarning(drift));
251
+ }
252
+ }
253
+ }
254
+
255
+ // Materialise descriptor-shipped migration packages onto disk under
256
+ // `migrations/<spaceId>/<dirName>/` for any package not yet present.
257
+ // Idempotent (existing dirs are left untouched).
258
+ // Uses `planAllSpaces` for deterministic ordering + duplicate-spaceId
259
+ // detection.
260
+ const extensionMigrationsInputs: readonly ExtensionMigrationsExtensionInput[] = (
261
+ config.extensionPacks ?? []
262
+ ).map((pack) => {
263
+ const cs = (
264
+ pack as {
265
+ readonly contractSpace?: ExtensionMigrationsExtensionInput['contractSpace'];
266
+ }
267
+ ).contractSpace;
268
+ return cs !== undefined ? { id: pack.id, contractSpace: cs } : { id: pack.id };
269
+ });
270
+ const extensionMigrationsResult = await runContractSpaceExtensionMigrationsPass({
271
+ migrationsDir,
272
+ extensionPacks: extensionMigrationsInputs,
273
+ });
274
+ if (!flags.json && !flags.quiet) {
275
+ for (const entry of extensionMigrationsResult.emitted) {
276
+ ui.step(`Emitted ${entry.spaceId}/${entry.dirName}`);
277
+ }
278
+ }
279
+
223
280
  // Check for no-op (same hash means no changes)
224
281
  if (fromHash === toStorageHash) {
225
282
  const result: MigrationPlanResult = {
@@ -253,7 +310,7 @@ async function executeMigrationPlanCommand(
253
310
  const timestamp = new Date();
254
311
  const slug = options.name ?? 'migration';
255
312
  const dirName = formatMigrationDirName(timestamp, slug);
256
- const packageDir = join(migrationsDir, dirName);
313
+ const packageDir = join(appMigrationsDir, dirName);
257
314
 
258
315
  const baseMetadata: Omit<MigrationMetadata, 'migrationHash' | 'providedInvariants'> = {
259
316
  from: fromHash,
@@ -9,6 +9,7 @@ import {
9
9
  reconstructGraph,
10
10
  } from '@prisma-next/migration-tools/migration-graph';
11
11
  import type { OnDiskMigrationPackage } from '@prisma-next/migration-tools/package';
12
+ import { APP_SPACE_ID, spaceMigrationDirectory } from '@prisma-next/migration-tools/spaces';
12
13
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
13
14
  import { Command } from 'commander';
14
15
  import { relative, resolve } from 'pathe';
@@ -103,16 +104,17 @@ async function executeMigrationShowCommand(
103
104
  ? relative(process.cwd(), resolve(options.config))
104
105
  : 'prisma-next.config.ts';
105
106
 
106
- const migrationsDir = resolve(
107
+ const migrationsDirRoot = resolve(
107
108
  options.config ? resolve(options.config, '..') : process.cwd(),
108
109
  config.migrations?.dir ?? 'migrations',
109
110
  );
110
- const migrationsRelative = relative(process.cwd(), migrationsDir);
111
+ const appMigrationsDir = spaceMigrationDirectory(migrationsDirRoot, APP_SPACE_ID);
112
+ const appMigrationsRelative = relative(process.cwd(), appMigrationsDir);
111
113
 
112
114
  if (!flags.json && !flags.quiet) {
113
115
  const details: Array<{ label: string; value: string }> = [
114
116
  { label: 'config', value: configPath },
115
- { label: 'migrations', value: migrationsRelative },
117
+ { label: 'migrations', value: appMigrationsRelative },
116
118
  ];
117
119
  if (target) {
118
120
  details.push({ label: 'target', value: target });
@@ -132,11 +134,11 @@ async function executeMigrationShowCommand(
132
134
  if (target && looksLikePath(target)) {
133
135
  pkg = await readMigrationPackage(resolve(target));
134
136
  } else {
135
- const allPackages = await readMigrationsDir(migrationsDir);
137
+ const allPackages = await readMigrationsDir(appMigrationsDir);
136
138
  if (allPackages.length === 0) {
137
139
  return notOk(
138
140
  errorRuntime('No migrations found', {
139
- why: `No migration packages found in ${migrationsRelative}`,
141
+ why: `No migration packages found in ${appMigrationsRelative}`,
140
142
  fix: 'Run `prisma-next migration plan` to create a migration first.',
141
143
  }),
142
144
  );
@@ -369,7 +369,7 @@ async function executeMigrationStatusCommand(
369
369
  ui: TerminalUI,
370
370
  ): Promise<Result<MigrationStatusResult, CliStructuredError>> {
371
371
  const config = await loadConfig(options.config);
372
- const { configPath, migrationsDir, migrationsRelative, refsDir } = resolveMigrationPaths(
372
+ const { configPath, appMigrationsDir, appMigrationsRelative, refsDir } = resolveMigrationPaths(
373
373
  options.config,
374
374
  config,
375
375
  );
@@ -414,7 +414,7 @@ async function executeMigrationStatusCommand(
414
414
  if (!flags.json && !flags.quiet) {
415
415
  const details: Array<{ label: string; value: string }> = [
416
416
  { label: 'config', value: configPath },
417
- { label: 'migrations', value: migrationsRelative },
417
+ { label: 'migrations', value: appMigrationsRelative },
418
418
  ];
419
419
  if (dbConnection && hasDriver) {
420
420
  details.push({ label: 'database', value: maskConnectionUrl(String(dbConnection)) });
@@ -454,7 +454,7 @@ async function executeMigrationStatusCommand(
454
454
  let bundles: readonly OnDiskMigrationPackage[];
455
455
  let graph: MigrationGraph;
456
456
  try {
457
- ({ bundles, graph } = await loadMigrationPackages(migrationsDir));
457
+ ({ bundles, graph } = await loadMigrationPackages(appMigrationsDir));
458
458
  } catch (error) {
459
459
  if (MigrationToolsError.is(error)) {
460
460
  return notOk(mapMigrationToolsError(error));
@@ -28,7 +28,9 @@ import { enrichContract } from './contract-enrichment';
28
28
  import { ContractValidationError } from './errors';
29
29
  import { executeDbInit } from './operations/db-init';
30
30
  import { executeDbUpdate } from './operations/db-update';
31
+ import { type ExecuteDbVerifyResult, executeDbVerify } from './operations/db-verify';
31
32
  import { executeMigrationApply } from './operations/migration-apply';
33
+
32
34
  import type {
33
35
  ControlActionName,
34
36
  ControlClient,
@@ -37,6 +39,7 @@ import type {
37
39
  DbInitResult,
38
40
  DbUpdateOptions,
39
41
  DbUpdateResult,
42
+ DbVerifyOptions,
40
43
  EmitOptions,
41
44
  EmitResult,
42
45
  IntrospectOptions,
@@ -368,6 +371,9 @@ class ControlClientImpl implements ControlClient {
368
371
  mode: options.mode,
369
372
  migrations: this.options.target.migrations,
370
373
  frameworkComponents,
374
+ migrationsDir: options.migrationsDir,
375
+ targetId: this.options.target.targetId,
376
+ extensionPacks: this.options.extensionPacks ?? [],
371
377
  ...ifDefined('onProgress', onProgress),
372
378
  });
373
379
  }
@@ -396,11 +402,42 @@ class ControlClientImpl implements ControlClient {
396
402
  mode: options.mode,
397
403
  migrations: this.options.target.migrations,
398
404
  frameworkComponents,
405
+ migrationsDir: options.migrationsDir,
406
+ targetId: this.options.target.targetId,
407
+ extensionPacks: this.options.extensionPacks ?? [],
399
408
  ...ifDefined('acceptDataLoss', options.acceptDataLoss),
400
409
  ...ifDefined('onProgress', onProgress),
401
410
  });
402
411
  }
403
412
 
413
+ async dbVerify(options: DbVerifyOptions): Promise<ExecuteDbVerifyResult> {
414
+ const { onProgress } = options;
415
+ await this.connectWithProgress(options.connection, 'dbVerify', onProgress);
416
+ const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
417
+
418
+ let contract: Contract;
419
+ try {
420
+ contract = familyInstance.validateContract(options.contract);
421
+ } catch (error) {
422
+ const message = error instanceof Error ? error.message : String(error);
423
+ throw new ContractValidationError(message, error);
424
+ }
425
+
426
+ return executeDbVerify({
427
+ driver,
428
+ familyInstance,
429
+ contract,
430
+ migrationsDir: options.migrationsDir,
431
+ targetId: this.options.target.targetId,
432
+ extensionPacks: this.options.extensionPacks ?? [],
433
+ frameworkComponents,
434
+ mode: options.strict ? 'strict' : 'lenient',
435
+ skipSchema: options.skipSchema,
436
+ skipMarker: options.skipMarker,
437
+ ...ifDefined('onProgress', onProgress),
438
+ });
439
+ }
440
+
404
441
  async readMarker(): Promise<ContractMarkerRecord | null> {
405
442
  const { driver, familyInstance } = await this.ensureConnected();
406
443
  // The CLI client's readMarker reads the app's marker. Per-extension
@@ -410,6 +447,11 @@ class ControlClientImpl implements ControlClient {
410
447
  return familyInstance.readMarker({ driver, space: APP_SPACE_ID });
411
448
  }
412
449
 
450
+ async readAllMarkers(): Promise<ReadonlyMap<string, ContractMarkerRecord>> {
451
+ const { driver, familyInstance } = await this.ensureConnected();
452
+ return familyInstance.readAllMarkers({ driver });
453
+ }
454
+
413
455
  async migrationApply(options: MigrationApplyOptions): Promise<MigrationApplyResult> {
414
456
  const { onProgress } = options;
415
457
  await this.connectWithProgress(options.connection, 'migrationApply', onProgress);