@prisma-next/cli 0.1.0-dev.2 → 0.1.0-dev.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { Command as Command6 } from "commander";
4
+ import { Command as Command7 } from "commander";
5
5
 
6
6
  // src/commands/contract-emit.ts
7
7
  import { mkdirSync, writeFileSync } from "fs";
@@ -165,6 +165,15 @@ function formatErrorOutput(error, flags) {
165
165
  const whereLine = error.where.line ? `${error.where.path}:${error.where.line}` : error.where.path;
166
166
  lines.push(`${prefix}${formatDimText(` Where: ${whereLine}`)}`);
167
167
  }
168
+ if (isVerbose(flags, 1) && error.meta?.["conflicts"]) {
169
+ const conflicts = error.meta["conflicts"];
170
+ if (conflicts.length > 0) {
171
+ lines.push(`${prefix}${formatDimText(" Conflicts:")}`);
172
+ for (const conflict of conflicts) {
173
+ lines.push(`${prefix}${formatDimText(` - [${conflict.kind}] ${conflict.summary}`)}`);
174
+ }
175
+ }
176
+ }
168
177
  if (error.docsUrl && isVerbose(flags, 1)) {
169
178
  lines.push(formatDimText(error.docsUrl));
170
179
  }
@@ -598,6 +607,69 @@ function formatSignOutput(result, flags) {
598
607
  function formatSignJson(result) {
599
608
  return JSON.stringify(result, null, 2);
600
609
  }
610
+ function formatDbInitPlanOutput(result, flags) {
611
+ if (flags.quiet) {
612
+ return "";
613
+ }
614
+ const lines = [];
615
+ const prefix = createPrefix(flags);
616
+ const useColor = flags.color !== false;
617
+ const formatGreen = createColorFormatter(useColor, green);
618
+ const formatDimText = (text) => formatDim(useColor, text);
619
+ const operationCount = result.plan?.operations.length ?? 0;
620
+ lines.push(`${prefix}${formatGreen("\u2714")} Planned ${operationCount} operation(s)`);
621
+ if (result.plan?.operations && result.plan.operations.length > 0) {
622
+ lines.push(`${prefix}${formatDimText("\u2502")}`);
623
+ for (let i = 0; i < result.plan.operations.length; i++) {
624
+ const op = result.plan.operations[i];
625
+ if (!op) continue;
626
+ const isLast = i === result.plan.operations.length - 1;
627
+ const treeChar = isLast ? "\u2514" : "\u251C";
628
+ const opClass = formatDimText(`[${op.operationClass}]`);
629
+ lines.push(`${prefix}${formatDimText(treeChar)}\u2500 ${op.label} ${opClass}`);
630
+ }
631
+ }
632
+ if (result.plan?.destination) {
633
+ lines.push(`${prefix}`);
634
+ lines.push(
635
+ `${prefix}${formatDimText(`Destination hash: ${result.plan.destination.coreHash}`)}`
636
+ );
637
+ }
638
+ if (isVerbose(flags, 1)) {
639
+ lines.push(`${prefix}${formatDimText(`Total time: ${result.timings.total}ms`)}`);
640
+ }
641
+ lines.push(`${prefix}`);
642
+ lines.push(`${prefix}${formatDimText("This is a dry run. No changes were applied.")}`);
643
+ lines.push(`${prefix}${formatDimText("Run without --plan to apply changes.")}`);
644
+ return lines.join("\n");
645
+ }
646
+ function formatDbInitApplyOutput(result, flags) {
647
+ if (flags.quiet) {
648
+ return "";
649
+ }
650
+ const lines = [];
651
+ const prefix = createPrefix(flags);
652
+ const useColor = flags.color !== false;
653
+ const formatGreen = createColorFormatter(useColor, green);
654
+ const formatDimText = (text) => formatDim(useColor, text);
655
+ if (result.ok) {
656
+ const executed = result.execution?.operationsExecuted ?? 0;
657
+ lines.push(`${prefix}${formatGreen("\u2714")} Applied ${executed} operation(s)`);
658
+ if (result.marker) {
659
+ lines.push(`${prefix}${formatDimText(` Marker written: ${result.marker.coreHash}`)}`);
660
+ if (result.marker.profileHash) {
661
+ lines.push(`${prefix}${formatDimText(` Profile hash: ${result.marker.profileHash}`)}`);
662
+ }
663
+ }
664
+ if (isVerbose(flags, 1)) {
665
+ lines.push(`${prefix}${formatDimText(` Total time: ${result.timings.total}ms`)}`);
666
+ }
667
+ }
668
+ return lines.join("\n");
669
+ }
670
+ function formatDbInitJson(result) {
671
+ return JSON.stringify(result, null, 2);
672
+ }
601
673
  var LEFT_COLUMN_WIDTH = 20;
602
674
  var RIGHT_COLUMN_MIN_WIDTH = 40;
603
675
  var RIGHT_COLUMN_MAX_WIDTH = 90;
@@ -921,8 +993,10 @@ import {
921
993
  errorFileNotFound,
922
994
  errorHashMismatch,
923
995
  errorMarkerMissing,
996
+ errorMigrationPlanningFailed,
924
997
  errorQueryRunnerFactoryRequired,
925
998
  errorRuntime,
999
+ errorTargetMigrationNotSupported,
926
1000
  errorTargetMismatch,
927
1001
  errorUnexpected as errorUnexpected2
928
1002
  } from "@prisma-next/core-control-plane/errors";
@@ -967,46 +1041,23 @@ function handleResult(result, flags, onSuccess) {
967
1041
  // src/utils/spinner.ts
968
1042
  import ora from "ora";
969
1043
  async function withSpinner(operation, options) {
970
- const { message, flags, delayThreshold = 100 } = options;
1044
+ const { message, flags } = options;
971
1045
  const shouldShowSpinner = !flags.quiet && flags.json !== "object" && process.stdout.isTTY;
972
1046
  if (!shouldShowSpinner) {
973
1047
  return operation();
974
1048
  }
975
1049
  const startTime = Date.now();
976
- let spinner = null;
977
- let timeoutId = null;
978
- let operationCompleted = false;
979
- timeoutId = setTimeout(() => {
980
- if (!operationCompleted) {
981
- spinner = ora({
982
- text: message,
983
- color: flags.color !== false ? "cyan" : false
984
- }).start();
985
- }
986
- }, delayThreshold);
1050
+ const spinner = ora({
1051
+ text: message,
1052
+ color: flags.color !== false ? "cyan" : false
1053
+ }).start();
987
1054
  try {
988
1055
  const result = await operation();
989
- operationCompleted = true;
990
- if (timeoutId) {
991
- clearTimeout(timeoutId);
992
- timeoutId = null;
993
- }
994
- if (spinner !== null) {
995
- const elapsed = Date.now() - startTime;
996
- spinner.succeed(`${message} (${elapsed}ms)`);
997
- }
1056
+ const elapsed = Date.now() - startTime;
1057
+ spinner.succeed(`${message} (${elapsed}ms)`);
998
1058
  return result;
999
1059
  } catch (error) {
1000
- operationCompleted = true;
1001
- if (timeoutId) {
1002
- clearTimeout(timeoutId);
1003
- timeoutId = null;
1004
- }
1005
- if (spinner !== null) {
1006
- spinner.fail(
1007
- `${message} failed: ${error instanceof Error ? error.message : String(error)}`
1008
- );
1009
- }
1060
+ spinner.fail(`${message} failed: ${error instanceof Error ? error.message : String(error)}`);
1010
1061
  throw error;
1011
1062
  }
1012
1063
  }
@@ -1121,18 +1172,330 @@ function createContractEmitCommand() {
1121
1172
  return command;
1122
1173
  }
1123
1174
 
1124
- // src/commands/db-introspect.ts
1175
+ // src/commands/db-init.ts
1125
1176
  import { readFile } from "fs/promises";
1126
1177
  import { relative as relative3, resolve as resolve3 } from "path";
1178
+ import { Command as Command2 } from "commander";
1179
+
1180
+ // src/utils/framework-components.ts
1181
+ function assertFrameworkComponentsCompatible(expectedFamilyId, expectedTargetId, frameworkComponents) {
1182
+ for (let i = 0; i < frameworkComponents.length; i++) {
1183
+ const component = frameworkComponents[i];
1184
+ if (typeof component !== "object" || component === null) {
1185
+ throw errorConfigValidation("frameworkComponents[]", {
1186
+ why: `Framework component at index ${i} must be an object`
1187
+ });
1188
+ }
1189
+ const record = component;
1190
+ if (!Object.hasOwn(record, "kind")) {
1191
+ throw errorConfigValidation("frameworkComponents[].kind", {
1192
+ why: `Framework component at index ${i} must have 'kind' property`
1193
+ });
1194
+ }
1195
+ const kind = record["kind"];
1196
+ if (kind !== "target" && kind !== "adapter" && kind !== "extension" && kind !== "driver") {
1197
+ throw errorConfigValidation("frameworkComponents[].kind", {
1198
+ why: `Framework component at index ${i} has invalid kind '${String(kind)}' (must be 'target', 'adapter', 'extension', or 'driver')`
1199
+ });
1200
+ }
1201
+ if (!Object.hasOwn(record, "familyId")) {
1202
+ throw errorConfigValidation("frameworkComponents[].familyId", {
1203
+ why: `Framework component at index ${i} (kind: ${String(kind)}) must have 'familyId' property`
1204
+ });
1205
+ }
1206
+ const familyId = record["familyId"];
1207
+ if (familyId !== expectedFamilyId) {
1208
+ throw errorConfigValidation("frameworkComponents[].familyId", {
1209
+ why: `Framework component at index ${i} (kind: ${String(kind)}) has familyId '${String(familyId)}' but expected '${expectedFamilyId}'`
1210
+ });
1211
+ }
1212
+ if (!Object.hasOwn(record, "targetId")) {
1213
+ throw errorConfigValidation("frameworkComponents[].targetId", {
1214
+ why: `Framework component at index ${i} (kind: ${String(kind)}) must have 'targetId' property`
1215
+ });
1216
+ }
1217
+ const targetId = record["targetId"];
1218
+ if (targetId !== expectedTargetId) {
1219
+ throw errorConfigValidation("frameworkComponents[].targetId", {
1220
+ why: `Framework component at index ${i} (kind: ${String(kind)}) has targetId '${String(targetId)}' but expected '${expectedTargetId}'`
1221
+ });
1222
+ }
1223
+ }
1224
+ return frameworkComponents;
1225
+ }
1226
+
1227
+ // src/commands/db-init.ts
1228
+ function createDbInitCommand() {
1229
+ const command = new Command2("init");
1230
+ setCommandDescriptions(
1231
+ command,
1232
+ "Bootstrap a database to match the current contract and write the contract marker",
1233
+ "Initializes a database to match your emitted contract using additive-only operations.\nCreates any missing tables, columns, indexes, and constraints defined in your contract.\nLeaves existing compatible structures in place, surfaces conflicts when destructive changes\nwould be required, and writes a contract marker to track the database state. Use --plan to\npreview changes without applying."
1234
+ );
1235
+ command.configureHelp({
1236
+ formatHelp: (cmd) => {
1237
+ const flags = parseGlobalFlags({});
1238
+ return formatCommandHelp({ command: cmd, flags });
1239
+ }
1240
+ }).option("--db <url>", "Database connection string").option("--config <path>", "Path to prisma-next.config.ts").option("--plan", "Preview planned operations without applying", false).option("--json [format]", "Output as JSON (object or ndjson)", false).option("-q, --quiet", "Quiet mode: errors only").option("-v, --verbose", "Verbose output: debug info, timings").option("-vv, --trace", "Trace output: deep internals, stack traces").option("--timestamps", "Add timestamps to output").option("--color", "Force color output").option("--no-color", "Disable color output").action(async (options) => {
1241
+ const flags = parseGlobalFlags(options);
1242
+ const startTime = Date.now();
1243
+ const result = await performAction(async () => {
1244
+ const config = await loadConfig(options.config);
1245
+ const configPath = options.config ? relative3(process.cwd(), resolve3(options.config)) : "prisma-next.config.ts";
1246
+ const contractPathAbsolute = config.contract?.output ? resolve3(config.contract.output) : resolve3("src/prisma/contract.json");
1247
+ const contractPath = relative3(process.cwd(), contractPathAbsolute);
1248
+ if (flags.json !== "object" && !flags.quiet) {
1249
+ const details = [
1250
+ { label: "config", value: configPath },
1251
+ { label: "contract", value: contractPath }
1252
+ ];
1253
+ if (options.db) {
1254
+ details.push({ label: "database", value: options.db });
1255
+ }
1256
+ if (options.plan) {
1257
+ details.push({ label: "mode", value: "plan (dry run)" });
1258
+ }
1259
+ const header = formatStyledHeader({
1260
+ command: "db init",
1261
+ description: "Bootstrap a database to match the current contract",
1262
+ url: "https://pris.ly/db-init",
1263
+ details,
1264
+ flags
1265
+ });
1266
+ console.log(header);
1267
+ }
1268
+ let contractJsonContent;
1269
+ try {
1270
+ contractJsonContent = await readFile(contractPathAbsolute, "utf-8");
1271
+ } catch (error) {
1272
+ if (error instanceof Error && error.code === "ENOENT") {
1273
+ throw errorFileNotFound(contractPathAbsolute, {
1274
+ why: `Contract file not found at ${contractPathAbsolute}`
1275
+ });
1276
+ }
1277
+ throw errorUnexpected2(error instanceof Error ? error.message : String(error), {
1278
+ why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`
1279
+ });
1280
+ }
1281
+ let contractJson;
1282
+ try {
1283
+ contractJson = JSON.parse(contractJsonContent);
1284
+ } catch (error) {
1285
+ throw errorUnexpected2(error instanceof Error ? error.message : String(error), {
1286
+ why: `Failed to parse contract JSON at ${contractPathAbsolute}: ${error instanceof Error ? error.message : String(error)}`
1287
+ });
1288
+ }
1289
+ const dbUrl = options.db ?? config.db?.url;
1290
+ if (!dbUrl) {
1291
+ throw errorDatabaseUrlRequired({ why: "Database URL is required for db init" });
1292
+ }
1293
+ if (!config.driver) {
1294
+ throw errorDriverRequired({ why: "Config.driver is required for db init" });
1295
+ }
1296
+ const driverDescriptor = config.driver;
1297
+ if (!config.target.migrations) {
1298
+ throw errorTargetMigrationNotSupported({
1299
+ why: `Target "${config.target.id}" does not support migrations`
1300
+ });
1301
+ }
1302
+ const migrations = config.target.migrations;
1303
+ const driver = await withSpinner(() => driverDescriptor.create(dbUrl), {
1304
+ message: "Connecting to database...",
1305
+ flags
1306
+ });
1307
+ try {
1308
+ const familyInstance = config.family.create({
1309
+ target: config.target,
1310
+ adapter: config.adapter,
1311
+ driver: driverDescriptor,
1312
+ extensions: config.extensions ?? []
1313
+ });
1314
+ const rawComponents = [config.target, config.adapter, ...config.extensions ?? []];
1315
+ const frameworkComponents = assertFrameworkComponentsCompatible(
1316
+ config.family.familyId,
1317
+ config.target.targetId,
1318
+ rawComponents
1319
+ );
1320
+ const contractIR = familyInstance.validateContractIR(contractJson);
1321
+ const planner = migrations.createPlanner(familyInstance);
1322
+ const runner = migrations.createRunner(familyInstance);
1323
+ const schemaIR = await withSpinner(() => familyInstance.introspect({ driver }), {
1324
+ message: "Introspecting database schema...",
1325
+ flags
1326
+ });
1327
+ const policy = { allowedOperationClasses: ["additive"] };
1328
+ const plannerResult = await withSpinner(
1329
+ async () => planner.plan({
1330
+ contract: contractIR,
1331
+ schema: schemaIR,
1332
+ policy,
1333
+ frameworkComponents
1334
+ }),
1335
+ {
1336
+ message: "Planning migration...",
1337
+ flags
1338
+ }
1339
+ );
1340
+ if (plannerResult.kind === "failure") {
1341
+ throw errorMigrationPlanningFailed({ conflicts: plannerResult.conflicts });
1342
+ }
1343
+ const migrationPlan = plannerResult.plan;
1344
+ const existingMarker = await familyInstance.readMarker({ driver });
1345
+ if (existingMarker) {
1346
+ const markerMatchesDestination = existingMarker.coreHash === migrationPlan.destination.coreHash && (!migrationPlan.destination.profileHash || existingMarker.profileHash === migrationPlan.destination.profileHash);
1347
+ if (markerMatchesDestination) {
1348
+ const dbInitResult2 = {
1349
+ ok: true,
1350
+ mode: options.plan ? "plan" : "apply",
1351
+ plan: {
1352
+ targetId: migrationPlan.targetId,
1353
+ destination: migrationPlan.destination,
1354
+ operations: []
1355
+ },
1356
+ ...options.plan ? {} : {
1357
+ execution: { operationsPlanned: 0, operationsExecuted: 0 },
1358
+ marker: {
1359
+ coreHash: existingMarker.coreHash,
1360
+ profileHash: existingMarker.profileHash
1361
+ }
1362
+ },
1363
+ summary: "Database already at target contract state",
1364
+ timings: { total: Date.now() - startTime }
1365
+ };
1366
+ return dbInitResult2;
1367
+ }
1368
+ const coreHashMismatch = existingMarker.coreHash !== migrationPlan.destination.coreHash;
1369
+ const profileHashMismatch = migrationPlan.destination.profileHash && existingMarker.profileHash !== migrationPlan.destination.profileHash;
1370
+ const mismatchParts = [];
1371
+ if (coreHashMismatch) {
1372
+ mismatchParts.push(
1373
+ `coreHash (marker: ${existingMarker.coreHash}, destination: ${migrationPlan.destination.coreHash})`
1374
+ );
1375
+ }
1376
+ if (profileHashMismatch) {
1377
+ mismatchParts.push(
1378
+ `profileHash (marker: ${existingMarker.profileHash}, destination: ${migrationPlan.destination.profileHash})`
1379
+ );
1380
+ }
1381
+ throw errorRuntime(
1382
+ `Existing contract marker does not match plan destination. Mismatch in ${mismatchParts.join(" and ")}.`,
1383
+ {
1384
+ why: "Database has an existing contract marker that does not match the target contract",
1385
+ fix: "Use `prisma-next db migrate` to migrate from the existing contract, or drop the database and re-run `db init`",
1386
+ meta: {
1387
+ code: "MARKER_ORIGIN_MISMATCH",
1388
+ markerCoreHash: existingMarker.coreHash,
1389
+ destinationCoreHash: migrationPlan.destination.coreHash,
1390
+ ...existingMarker.profileHash ? { markerProfileHash: existingMarker.profileHash } : {},
1391
+ ...migrationPlan.destination.profileHash ? { destinationProfileHash: migrationPlan.destination.profileHash } : {}
1392
+ }
1393
+ }
1394
+ );
1395
+ }
1396
+ if (options.plan) {
1397
+ const dbInitResult2 = {
1398
+ ok: true,
1399
+ mode: "plan",
1400
+ plan: {
1401
+ targetId: migrationPlan.targetId,
1402
+ destination: migrationPlan.destination,
1403
+ operations: migrationPlan.operations.map((op) => ({
1404
+ id: op.id,
1405
+ label: op.label,
1406
+ operationClass: op.operationClass
1407
+ }))
1408
+ },
1409
+ summary: `Planned ${migrationPlan.operations.length} operation(s)`,
1410
+ timings: { total: Date.now() - startTime }
1411
+ };
1412
+ return dbInitResult2;
1413
+ }
1414
+ const callbacks = {
1415
+ onOperationStart: (op) => {
1416
+ if (!flags.quiet && flags.json !== "object") {
1417
+ console.log(` \u2192 ${op.label}...`);
1418
+ }
1419
+ },
1420
+ onOperationComplete: (_op) => {
1421
+ }
1422
+ };
1423
+ const runnerResult = await withSpinner(
1424
+ () => runner.execute({
1425
+ plan: migrationPlan,
1426
+ driver,
1427
+ destinationContract: contractIR,
1428
+ policy,
1429
+ callbacks,
1430
+ frameworkComponents
1431
+ }),
1432
+ {
1433
+ message: "Applying migration plan...",
1434
+ flags
1435
+ }
1436
+ );
1437
+ if (!runnerResult.ok) {
1438
+ throw errorRuntime(runnerResult.failure.summary, {
1439
+ why: runnerResult.failure.why ?? `Migration runner failed: ${runnerResult.failure.code}`,
1440
+ meta: { code: runnerResult.failure.code }
1441
+ });
1442
+ }
1443
+ const execution = runnerResult.value;
1444
+ const dbInitResult = {
1445
+ ok: true,
1446
+ mode: "apply",
1447
+ plan: {
1448
+ targetId: migrationPlan.targetId,
1449
+ destination: migrationPlan.destination,
1450
+ operations: migrationPlan.operations.map((op) => ({
1451
+ id: op.id,
1452
+ label: op.label,
1453
+ operationClass: op.operationClass
1454
+ }))
1455
+ },
1456
+ execution: {
1457
+ operationsPlanned: execution.operationsPlanned,
1458
+ operationsExecuted: execution.operationsExecuted
1459
+ },
1460
+ marker: migrationPlan.destination.profileHash ? {
1461
+ coreHash: migrationPlan.destination.coreHash,
1462
+ profileHash: migrationPlan.destination.profileHash
1463
+ } : { coreHash: migrationPlan.destination.coreHash },
1464
+ summary: `Applied ${execution.operationsExecuted} operation(s), marker written`,
1465
+ timings: { total: Date.now() - startTime }
1466
+ };
1467
+ return dbInitResult;
1468
+ } finally {
1469
+ await driver.close();
1470
+ }
1471
+ });
1472
+ const exitCode = handleResult(result, flags, (dbInitResult) => {
1473
+ if (flags.json === "object") {
1474
+ console.log(formatDbInitJson(dbInitResult));
1475
+ } else {
1476
+ const output = dbInitResult.mode === "plan" ? formatDbInitPlanOutput(dbInitResult, flags) : formatDbInitApplyOutput(dbInitResult, flags);
1477
+ if (output) {
1478
+ console.log(output);
1479
+ }
1480
+ }
1481
+ });
1482
+ process.exit(exitCode);
1483
+ });
1484
+ return command;
1485
+ }
1486
+
1487
+ // src/commands/db-introspect.ts
1488
+ import { readFile as readFile2 } from "fs/promises";
1489
+ import { relative as relative4, resolve as resolve4 } from "path";
1127
1490
  import {
1128
1491
  errorDatabaseUrlRequired as errorDatabaseUrlRequired2,
1129
1492
  errorDriverRequired as errorDriverRequired2,
1130
1493
  errorRuntime as errorRuntime2,
1131
1494
  errorUnexpected as errorUnexpected3
1132
1495
  } from "@prisma-next/core-control-plane/errors";
1133
- import { Command as Command2 } from "commander";
1496
+ import { Command as Command3 } from "commander";
1134
1497
  function createDbIntrospectCommand() {
1135
- const command = new Command2("introspect");
1498
+ const command = new Command3("introspect");
1136
1499
  setCommandDescriptions(
1137
1500
  command,
1138
1501
  "Inspect the database schema",
@@ -1148,14 +1511,13 @@ function createDbIntrospectCommand() {
1148
1511
  const result = await performAction(async () => {
1149
1512
  const startTime = Date.now();
1150
1513
  const config = await loadConfig(options.config);
1151
- const configPath = options.config ? relative3(process.cwd(), resolve3(options.config)) : "prisma-next.config.ts";
1514
+ const configPath = options.config ? relative4(process.cwd(), resolve4(options.config)) : "prisma-next.config.ts";
1152
1515
  let contractIR;
1153
1516
  if (config.contract?.output) {
1154
- const contractPath = resolve3(config.contract.output);
1517
+ const contractPath = resolve4(config.contract.output);
1155
1518
  try {
1156
- const contractJsonContent = await readFile(contractPath, "utf-8");
1157
- const contractJson = JSON.parse(contractJsonContent);
1158
- contractIR = contractJson;
1519
+ const contractJsonContent = await readFile2(contractPath, "utf-8");
1520
+ contractIR = JSON.parse(contractJsonContent);
1159
1521
  } catch (error) {
1160
1522
  if (error instanceof Error && error.code !== "ENOENT") {
1161
1523
  throw errorUnexpected3(error.message, {
@@ -1203,14 +1565,13 @@ function createDbIntrospectCommand() {
1203
1565
  driver: driverDescriptor,
1204
1566
  extensions: config.extensions ?? []
1205
1567
  });
1206
- const typedFamilyInstance = familyInstance;
1207
1568
  if (contractIR) {
1208
- contractIR = typedFamilyInstance.validateContractIR(contractIR);
1569
+ contractIR = familyInstance.validateContractIR(contractIR);
1209
1570
  }
1210
1571
  let schemaIR;
1211
1572
  try {
1212
1573
  schemaIR = await withSpinner(
1213
- () => typedFamilyInstance.introspect({
1574
+ () => familyInstance.introspect({
1214
1575
  driver,
1215
1576
  contractIR
1216
1577
  }),
@@ -1225,9 +1586,9 @@ function createDbIntrospectCommand() {
1225
1586
  });
1226
1587
  }
1227
1588
  let schemaView;
1228
- if (typedFamilyInstance.toSchemaView) {
1589
+ if (familyInstance.toSchemaView) {
1229
1590
  try {
1230
- schemaView = typedFamilyInstance.toSchemaView(schemaIR);
1591
+ schemaView = familyInstance.toSchemaView(schemaIR);
1231
1592
  } catch (error) {
1232
1593
  if (flags.verbose) {
1233
1594
  console.error(
@@ -1285,8 +1646,8 @@ function createDbIntrospectCommand() {
1285
1646
  }
1286
1647
 
1287
1648
  // src/commands/db-schema-verify.ts
1288
- import { readFile as readFile2 } from "fs/promises";
1289
- import { relative as relative4, resolve as resolve4 } from "path";
1649
+ import { readFile as readFile3 } from "fs/promises";
1650
+ import { relative as relative5, resolve as resolve5 } from "path";
1290
1651
  import {
1291
1652
  errorDatabaseUrlRequired as errorDatabaseUrlRequired3,
1292
1653
  errorDriverRequired as errorDriverRequired3,
@@ -1294,9 +1655,9 @@ import {
1294
1655
  errorRuntime as errorRuntime3,
1295
1656
  errorUnexpected as errorUnexpected4
1296
1657
  } from "@prisma-next/core-control-plane/errors";
1297
- import { Command as Command3 } from "commander";
1658
+ import { Command as Command4 } from "commander";
1298
1659
  function createDbSchemaVerifyCommand() {
1299
- const command = new Command3("schema-verify");
1660
+ const command = new Command4("schema-verify");
1300
1661
  setCommandDescriptions(
1301
1662
  command,
1302
1663
  "Check whether the database schema satisfies your contract",
@@ -1311,9 +1672,9 @@ function createDbSchemaVerifyCommand() {
1311
1672
  const flags = parseGlobalFlags(options);
1312
1673
  const result = await performAction(async () => {
1313
1674
  const config = await loadConfig(options.config);
1314
- const configPath = options.config ? relative4(process.cwd(), resolve4(options.config)) : "prisma-next.config.ts";
1315
- const contractPathAbsolute = config.contract?.output ? resolve4(config.contract.output) : resolve4("src/prisma/contract.json");
1316
- const contractPath = relative4(process.cwd(), contractPathAbsolute);
1675
+ const configPath = options.config ? relative5(process.cwd(), resolve5(options.config)) : "prisma-next.config.ts";
1676
+ const contractPathAbsolute = config.contract?.output ? resolve5(config.contract.output) : resolve5("src/prisma/contract.json");
1677
+ const contractPath = relative5(process.cwd(), contractPathAbsolute);
1317
1678
  if (flags.json !== "object" && !flags.quiet) {
1318
1679
  const details = [
1319
1680
  { label: "config", value: configPath },
@@ -1333,7 +1694,7 @@ function createDbSchemaVerifyCommand() {
1333
1694
  }
1334
1695
  let contractJsonContent;
1335
1696
  try {
1336
- contractJsonContent = await readFile2(contractPathAbsolute, "utf-8");
1697
+ contractJsonContent = await readFile3(contractPathAbsolute, "utf-8");
1337
1698
  } catch (error) {
1338
1699
  if (error instanceof Error && error.code === "ENOENT") {
1339
1700
  throw errorFileNotFound2(contractPathAbsolute, {
@@ -1364,17 +1725,23 @@ function createDbSchemaVerifyCommand() {
1364
1725
  driver: driverDescriptor,
1365
1726
  extensions: config.extensions ?? []
1366
1727
  });
1367
- const typedFamilyInstance = familyInstance;
1368
- const contractIR = typedFamilyInstance.validateContractIR(contractJson);
1728
+ const rawComponents = [config.target, config.adapter, ...config.extensions ?? []];
1729
+ const frameworkComponents = assertFrameworkComponentsCompatible(
1730
+ config.family.familyId,
1731
+ config.target.targetId,
1732
+ rawComponents
1733
+ );
1734
+ const contractIR = familyInstance.validateContractIR(contractJson);
1369
1735
  let schemaVerifyResult;
1370
1736
  try {
1371
1737
  schemaVerifyResult = await withSpinner(
1372
- () => typedFamilyInstance.schemaVerify({
1738
+ () => familyInstance.schemaVerify({
1373
1739
  driver,
1374
1740
  contractIR,
1375
1741
  strict: options.strict ?? false,
1376
1742
  contractPath: contractPathAbsolute,
1377
- configPath
1743
+ configPath,
1744
+ frameworkComponents
1378
1745
  }),
1379
1746
  {
1380
1747
  message: "Verifying database schema...",
@@ -1414,8 +1781,8 @@ function createDbSchemaVerifyCommand() {
1414
1781
  }
1415
1782
 
1416
1783
  // src/commands/db-sign.ts
1417
- import { readFile as readFile3 } from "fs/promises";
1418
- import { relative as relative5, resolve as resolve5 } from "path";
1784
+ import { readFile as readFile4 } from "fs/promises";
1785
+ import { relative as relative6, resolve as resolve6 } from "path";
1419
1786
  import {
1420
1787
  errorDatabaseUrlRequired as errorDatabaseUrlRequired4,
1421
1788
  errorDriverRequired as errorDriverRequired4,
@@ -1423,9 +1790,9 @@ import {
1423
1790
  errorRuntime as errorRuntime4,
1424
1791
  errorUnexpected as errorUnexpected5
1425
1792
  } from "@prisma-next/core-control-plane/errors";
1426
- import { Command as Command4 } from "commander";
1793
+ import { Command as Command5 } from "commander";
1427
1794
  function createDbSignCommand() {
1428
- const command = new Command4("sign");
1795
+ const command = new Command5("sign");
1429
1796
  setCommandDescriptions(
1430
1797
  command,
1431
1798
  "Sign the database with your contract so you can safely run queries",
@@ -1440,9 +1807,9 @@ function createDbSignCommand() {
1440
1807
  const flags = parseGlobalFlags(options);
1441
1808
  const result = await performAction(async () => {
1442
1809
  const config = await loadConfig(options.config);
1443
- const configPath = options.config ? relative5(process.cwd(), resolve5(options.config)) : "prisma-next.config.ts";
1444
- const contractPathAbsolute = config.contract?.output ? resolve5(config.contract.output) : resolve5("src/prisma/contract.json");
1445
- const contractPath = relative5(process.cwd(), contractPathAbsolute);
1810
+ const configPath = options.config ? relative6(process.cwd(), resolve6(options.config)) : "prisma-next.config.ts";
1811
+ const contractPathAbsolute = config.contract?.output ? resolve6(config.contract.output) : resolve6("src/prisma/contract.json");
1812
+ const contractPath = relative6(process.cwd(), contractPathAbsolute);
1446
1813
  if (flags.json !== "object" && !flags.quiet) {
1447
1814
  const details = [
1448
1815
  { label: "config", value: configPath },
@@ -1462,7 +1829,7 @@ function createDbSignCommand() {
1462
1829
  }
1463
1830
  let contractJsonContent;
1464
1831
  try {
1465
- contractJsonContent = await readFile3(contractPathAbsolute, "utf-8");
1832
+ contractJsonContent = await readFile4(contractPathAbsolute, "utf-8");
1466
1833
  } catch (error) {
1467
1834
  if (error instanceof Error && error.code === "ENOENT") {
1468
1835
  throw errorFileNotFound3(contractPathAbsolute, {
@@ -1490,20 +1857,24 @@ function createDbSignCommand() {
1490
1857
  driver: driverDescriptor,
1491
1858
  extensions: config.extensions ?? []
1492
1859
  });
1493
- const typedFamilyInstance = familyInstance;
1494
- const contractIR = typedFamilyInstance.validateContractIR(contractJson);
1860
+ const rawComponents = [config.target, config.adapter, ...config.extensions ?? []];
1861
+ const frameworkComponents = assertFrameworkComponentsCompatible(
1862
+ config.family.familyId,
1863
+ config.target.targetId,
1864
+ rawComponents
1865
+ );
1866
+ const contractIR = familyInstance.validateContractIR(contractJson);
1495
1867
  let schemaVerifyResult;
1496
1868
  try {
1497
1869
  schemaVerifyResult = await withSpinner(
1498
- async () => {
1499
- return await typedFamilyInstance.schemaVerify({
1500
- driver,
1501
- contractIR,
1502
- strict: false,
1503
- contractPath: contractPathAbsolute,
1504
- configPath
1505
- });
1506
- },
1870
+ () => familyInstance.schemaVerify({
1871
+ driver,
1872
+ contractIR,
1873
+ strict: false,
1874
+ contractPath: contractPathAbsolute,
1875
+ configPath,
1876
+ frameworkComponents
1877
+ }),
1507
1878
  {
1508
1879
  message: "Verifying database satisfies contract",
1509
1880
  flags
@@ -1520,14 +1891,12 @@ function createDbSignCommand() {
1520
1891
  let signResult;
1521
1892
  try {
1522
1893
  signResult = await withSpinner(
1523
- async () => {
1524
- return await typedFamilyInstance.sign({
1525
- driver,
1526
- contractIR,
1527
- contractPath: contractPathAbsolute,
1528
- configPath
1529
- });
1530
- },
1894
+ () => familyInstance.sign({
1895
+ driver,
1896
+ contractIR,
1897
+ contractPath: contractPathAbsolute,
1898
+ configPath
1899
+ }),
1531
1900
  {
1532
1901
  message: "Signing database...",
1533
1902
  flags
@@ -1580,8 +1949,8 @@ function createDbSignCommand() {
1580
1949
  }
1581
1950
 
1582
1951
  // src/commands/db-verify.ts
1583
- import { readFile as readFile4 } from "fs/promises";
1584
- import { relative as relative6, resolve as resolve6 } from "path";
1952
+ import { readFile as readFile5 } from "fs/promises";
1953
+ import { relative as relative7, resolve as resolve7 } from "path";
1585
1954
  import {
1586
1955
  errorDatabaseUrlRequired as errorDatabaseUrlRequired5,
1587
1956
  errorDriverRequired as errorDriverRequired5,
@@ -1592,9 +1961,9 @@ import {
1592
1961
  errorTargetMismatch as errorTargetMismatch2,
1593
1962
  errorUnexpected as errorUnexpected6
1594
1963
  } from "@prisma-next/core-control-plane/errors";
1595
- import { Command as Command5 } from "commander";
1964
+ import { Command as Command6 } from "commander";
1596
1965
  function createDbVerifyCommand() {
1597
- const command = new Command5("verify");
1966
+ const command = new Command6("verify");
1598
1967
  setCommandDescriptions(
1599
1968
  command,
1600
1969
  "Check whether the database has been signed with your contract",
@@ -1609,9 +1978,9 @@ function createDbVerifyCommand() {
1609
1978
  const flags = parseGlobalFlags(options);
1610
1979
  const result = await performAction(async () => {
1611
1980
  const config = await loadConfig(options.config);
1612
- const configPath = options.config ? relative6(process.cwd(), resolve6(options.config)) : "prisma-next.config.ts";
1613
- const contractPathAbsolute = config.contract?.output ? resolve6(config.contract.output) : resolve6("src/prisma/contract.json");
1614
- const contractPath = relative6(process.cwd(), contractPathAbsolute);
1981
+ const configPath = options.config ? relative7(process.cwd(), resolve7(options.config)) : "prisma-next.config.ts";
1982
+ const contractPathAbsolute = config.contract?.output ? resolve7(config.contract.output) : resolve7("src/prisma/contract.json");
1983
+ const contractPath = relative7(process.cwd(), contractPathAbsolute);
1615
1984
  if (flags.json !== "object" && !flags.quiet) {
1616
1985
  const details = [
1617
1986
  { label: "config", value: configPath },
@@ -1631,7 +2000,7 @@ function createDbVerifyCommand() {
1631
2000
  }
1632
2001
  let contractJsonContent;
1633
2002
  try {
1634
- contractJsonContent = await readFile4(contractPathAbsolute, "utf-8");
2003
+ contractJsonContent = await readFile5(contractPathAbsolute, "utf-8");
1635
2004
  } catch (error) {
1636
2005
  if (error instanceof Error && error.code === "ENOENT") {
1637
2006
  throw errorFileNotFound4(contractPathAbsolute, {
@@ -1662,12 +2031,11 @@ function createDbVerifyCommand() {
1662
2031
  driver: driverDescriptor,
1663
2032
  extensions: config.extensions ?? []
1664
2033
  });
1665
- const typedFamilyInstance = familyInstance;
1666
- const contractIR = typedFamilyInstance.validateContractIR(contractJson);
2034
+ const contractIR = familyInstance.validateContractIR(contractJson);
1667
2035
  let verifyResult;
1668
2036
  try {
1669
2037
  verifyResult = await withSpinner(
1670
- () => typedFamilyInstance.verify({
2038
+ () => familyInstance.verify({
1671
2039
  driver,
1672
2040
  contractIR,
1673
2041
  expectedTargetId: config.target.targetId,
@@ -1726,7 +2094,7 @@ function createDbVerifyCommand() {
1726
2094
  }
1727
2095
 
1728
2096
  // src/cli.ts
1729
- var program = new Command6();
2097
+ var program = new Command7();
1730
2098
  program.name("prisma-next").description("Prisma Next CLI").version("0.0.1");
1731
2099
  var versionOption = program.options.find((opt) => opt.flags.includes("--version"));
1732
2100
  if (versionOption) {
@@ -1790,7 +2158,7 @@ program.exitOverride((err2) => {
1790
2158
  }
1791
2159
  process.exit(0);
1792
2160
  });
1793
- var contractCommand = new Command6("contract");
2161
+ var contractCommand = new Command7("contract");
1794
2162
  setCommandDescriptions(
1795
2163
  contractCommand,
1796
2164
  "Contract management commands",
@@ -1806,7 +2174,7 @@ contractCommand.configureHelp({
1806
2174
  var contractEmitCommand = createContractEmitCommand();
1807
2175
  contractCommand.addCommand(contractEmitCommand);
1808
2176
  program.addCommand(contractCommand);
1809
- var dbCommand = new Command6("db");
2177
+ var dbCommand = new Command7("db");
1810
2178
  setCommandDescriptions(
1811
2179
  dbCommand,
1812
2180
  "Database management commands",
@@ -1821,6 +2189,8 @@ dbCommand.configureHelp({
1821
2189
  });
1822
2190
  var dbVerifyCommand = createDbVerifyCommand();
1823
2191
  dbCommand.addCommand(dbVerifyCommand);
2192
+ var dbInitCommand = createDbInitCommand();
2193
+ dbCommand.addCommand(dbInitCommand);
1824
2194
  var dbIntrospectCommand = createDbIntrospectCommand();
1825
2195
  dbCommand.addCommand(dbIntrospectCommand);
1826
2196
  var dbSchemaVerifyCommand = createDbSchemaVerifyCommand();
@@ -1828,7 +2198,7 @@ dbCommand.addCommand(dbSchemaVerifyCommand);
1828
2198
  var dbSignCommand = createDbSignCommand();
1829
2199
  dbCommand.addCommand(dbSignCommand);
1830
2200
  program.addCommand(dbCommand);
1831
- var helpCommand = new Command6("help").description("Show usage instructions").configureHelp({
2201
+ var helpCommand = new Command7("help").description("Show usage instructions").configureHelp({
1832
2202
  formatHelp: (cmd) => {
1833
2203
  const flags = parseGlobalFlags({});
1834
2204
  return formatCommandHelp({ command: cmd, flags });