@prisma-next/cli 0.1.0-dev.2 → 0.1.0-dev.20
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 +172 -20
- package/dist/{chunk-4Q3MO4TK.js → chunk-VWAVGWUP.js} +2 -2
- package/dist/chunk-VWAVGWUP.js.map +1 -0
- package/dist/chunk-YDE4ILKH.js +55 -0
- package/dist/chunk-YDE4ILKH.js.map +1 -0
- package/dist/{chunk-3EODSNGS.js → chunk-ZKEJZ3NU.js} +94 -32
- package/dist/chunk-ZKEJZ3NU.js.map +1 -0
- package/dist/cli.js +417 -99
- package/dist/cli.js.map +1 -1
- package/dist/commands/contract-emit.js +2 -2
- package/dist/commands/db-init.d.ts +5 -0
- package/dist/commands/db-init.js +240 -0
- package/dist/commands/db-init.js.map +1 -0
- package/dist/commands/db-introspect.js +6 -8
- package/dist/commands/db-introspect.js.map +1 -1
- package/dist/commands/db-schema-verify.js +14 -5
- package/dist/commands/db-schema-verify.js.map +1 -1
- package/dist/commands/db-sign.js +25 -20
- package/dist/commands/db-sign.js.map +1 -1
- package/dist/commands/db-verify.js +3 -4
- package/dist/commands/db-verify.js.map +1 -1
- package/dist/index.js +4 -4
- package/package.json +17 -10
- package/dist/chunk-3EODSNGS.js.map +0 -1
- package/dist/chunk-4Q3MO4TK.js.map +0 -1
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
|
|
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
|
|
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
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
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
|
-
|
|
990
|
-
|
|
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
|
-
|
|
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,278 @@ function createContractEmitCommand() {
|
|
|
1121
1172
|
return command;
|
|
1122
1173
|
}
|
|
1123
1174
|
|
|
1124
|
-
// src/commands/db-
|
|
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 tables, columns, indexes, and constraints defined in your contract.\nWrites a contract marker to track the database state. This operation is idempotent.\n\nCurrently supports empty databases only. Use --plan to preview 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
|
+
if (options.plan) {
|
|
1345
|
+
const dbInitResult2 = {
|
|
1346
|
+
ok: true,
|
|
1347
|
+
mode: "plan",
|
|
1348
|
+
plan: {
|
|
1349
|
+
targetId: migrationPlan.targetId,
|
|
1350
|
+
destination: migrationPlan.destination,
|
|
1351
|
+
operations: migrationPlan.operations.map((op) => ({
|
|
1352
|
+
id: op.id,
|
|
1353
|
+
label: op.label,
|
|
1354
|
+
operationClass: op.operationClass
|
|
1355
|
+
}))
|
|
1356
|
+
},
|
|
1357
|
+
summary: `Planned ${migrationPlan.operations.length} operation(s)`,
|
|
1358
|
+
timings: { total: Date.now() - startTime }
|
|
1359
|
+
};
|
|
1360
|
+
return dbInitResult2;
|
|
1361
|
+
}
|
|
1362
|
+
const callbacks = {
|
|
1363
|
+
onOperationStart: (op) => {
|
|
1364
|
+
if (!flags.quiet && flags.json !== "object") {
|
|
1365
|
+
console.log(` \u2192 ${op.label}...`);
|
|
1366
|
+
}
|
|
1367
|
+
},
|
|
1368
|
+
onOperationComplete: (_op) => {
|
|
1369
|
+
}
|
|
1370
|
+
};
|
|
1371
|
+
const runnerResult = await withSpinner(
|
|
1372
|
+
() => runner.execute({
|
|
1373
|
+
plan: migrationPlan,
|
|
1374
|
+
driver,
|
|
1375
|
+
destinationContract: contractIR,
|
|
1376
|
+
policy,
|
|
1377
|
+
callbacks,
|
|
1378
|
+
frameworkComponents
|
|
1379
|
+
}),
|
|
1380
|
+
{
|
|
1381
|
+
message: "Applying migration plan...",
|
|
1382
|
+
flags
|
|
1383
|
+
}
|
|
1384
|
+
);
|
|
1385
|
+
if (!runnerResult.ok) {
|
|
1386
|
+
throw errorRuntime(runnerResult.failure.summary, {
|
|
1387
|
+
why: runnerResult.failure.why ?? `Migration runner failed: ${runnerResult.failure.code}`,
|
|
1388
|
+
meta: { code: runnerResult.failure.code }
|
|
1389
|
+
});
|
|
1390
|
+
}
|
|
1391
|
+
const execution = runnerResult.value;
|
|
1392
|
+
const dbInitResult = {
|
|
1393
|
+
ok: true,
|
|
1394
|
+
mode: "apply",
|
|
1395
|
+
plan: {
|
|
1396
|
+
targetId: migrationPlan.targetId,
|
|
1397
|
+
destination: migrationPlan.destination,
|
|
1398
|
+
operations: migrationPlan.operations.map((op) => ({
|
|
1399
|
+
id: op.id,
|
|
1400
|
+
label: op.label,
|
|
1401
|
+
operationClass: op.operationClass
|
|
1402
|
+
}))
|
|
1403
|
+
},
|
|
1404
|
+
execution: {
|
|
1405
|
+
operationsPlanned: execution.operationsPlanned,
|
|
1406
|
+
operationsExecuted: execution.operationsExecuted
|
|
1407
|
+
},
|
|
1408
|
+
marker: migrationPlan.destination.profileHash ? {
|
|
1409
|
+
coreHash: migrationPlan.destination.coreHash,
|
|
1410
|
+
profileHash: migrationPlan.destination.profileHash
|
|
1411
|
+
} : { coreHash: migrationPlan.destination.coreHash },
|
|
1412
|
+
summary: `Applied ${execution.operationsExecuted} operation(s), marker written`,
|
|
1413
|
+
timings: { total: Date.now() - startTime }
|
|
1414
|
+
};
|
|
1415
|
+
return dbInitResult;
|
|
1416
|
+
} finally {
|
|
1417
|
+
await driver.close();
|
|
1418
|
+
}
|
|
1419
|
+
});
|
|
1420
|
+
const exitCode = handleResult(result, flags, (dbInitResult) => {
|
|
1421
|
+
if (flags.json === "object") {
|
|
1422
|
+
console.log(formatDbInitJson(dbInitResult));
|
|
1423
|
+
} else {
|
|
1424
|
+
const output = dbInitResult.mode === "plan" ? formatDbInitPlanOutput(dbInitResult, flags) : formatDbInitApplyOutput(dbInitResult, flags);
|
|
1425
|
+
if (output) {
|
|
1426
|
+
console.log(output);
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
});
|
|
1430
|
+
process.exit(exitCode);
|
|
1431
|
+
});
|
|
1432
|
+
return command;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
// src/commands/db-introspect.ts
|
|
1436
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
1437
|
+
import { relative as relative4, resolve as resolve4 } from "path";
|
|
1127
1438
|
import {
|
|
1128
1439
|
errorDatabaseUrlRequired as errorDatabaseUrlRequired2,
|
|
1129
1440
|
errorDriverRequired as errorDriverRequired2,
|
|
1130
1441
|
errorRuntime as errorRuntime2,
|
|
1131
1442
|
errorUnexpected as errorUnexpected3
|
|
1132
1443
|
} from "@prisma-next/core-control-plane/errors";
|
|
1133
|
-
import { Command as
|
|
1444
|
+
import { Command as Command3 } from "commander";
|
|
1134
1445
|
function createDbIntrospectCommand() {
|
|
1135
|
-
const command = new
|
|
1446
|
+
const command = new Command3("introspect");
|
|
1136
1447
|
setCommandDescriptions(
|
|
1137
1448
|
command,
|
|
1138
1449
|
"Inspect the database schema",
|
|
@@ -1148,14 +1459,13 @@ function createDbIntrospectCommand() {
|
|
|
1148
1459
|
const result = await performAction(async () => {
|
|
1149
1460
|
const startTime = Date.now();
|
|
1150
1461
|
const config = await loadConfig(options.config);
|
|
1151
|
-
const configPath = options.config ?
|
|
1462
|
+
const configPath = options.config ? relative4(process.cwd(), resolve4(options.config)) : "prisma-next.config.ts";
|
|
1152
1463
|
let contractIR;
|
|
1153
1464
|
if (config.contract?.output) {
|
|
1154
|
-
const contractPath =
|
|
1465
|
+
const contractPath = resolve4(config.contract.output);
|
|
1155
1466
|
try {
|
|
1156
|
-
const contractJsonContent = await
|
|
1157
|
-
|
|
1158
|
-
contractIR = contractJson;
|
|
1467
|
+
const contractJsonContent = await readFile2(contractPath, "utf-8");
|
|
1468
|
+
contractIR = JSON.parse(contractJsonContent);
|
|
1159
1469
|
} catch (error) {
|
|
1160
1470
|
if (error instanceof Error && error.code !== "ENOENT") {
|
|
1161
1471
|
throw errorUnexpected3(error.message, {
|
|
@@ -1203,14 +1513,13 @@ function createDbIntrospectCommand() {
|
|
|
1203
1513
|
driver: driverDescriptor,
|
|
1204
1514
|
extensions: config.extensions ?? []
|
|
1205
1515
|
});
|
|
1206
|
-
const typedFamilyInstance = familyInstance;
|
|
1207
1516
|
if (contractIR) {
|
|
1208
|
-
contractIR =
|
|
1517
|
+
contractIR = familyInstance.validateContractIR(contractIR);
|
|
1209
1518
|
}
|
|
1210
1519
|
let schemaIR;
|
|
1211
1520
|
try {
|
|
1212
1521
|
schemaIR = await withSpinner(
|
|
1213
|
-
() =>
|
|
1522
|
+
() => familyInstance.introspect({
|
|
1214
1523
|
driver,
|
|
1215
1524
|
contractIR
|
|
1216
1525
|
}),
|
|
@@ -1225,9 +1534,9 @@ function createDbIntrospectCommand() {
|
|
|
1225
1534
|
});
|
|
1226
1535
|
}
|
|
1227
1536
|
let schemaView;
|
|
1228
|
-
if (
|
|
1537
|
+
if (familyInstance.toSchemaView) {
|
|
1229
1538
|
try {
|
|
1230
|
-
schemaView =
|
|
1539
|
+
schemaView = familyInstance.toSchemaView(schemaIR);
|
|
1231
1540
|
} catch (error) {
|
|
1232
1541
|
if (flags.verbose) {
|
|
1233
1542
|
console.error(
|
|
@@ -1285,8 +1594,8 @@ function createDbIntrospectCommand() {
|
|
|
1285
1594
|
}
|
|
1286
1595
|
|
|
1287
1596
|
// src/commands/db-schema-verify.ts
|
|
1288
|
-
import { readFile as
|
|
1289
|
-
import { relative as
|
|
1597
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
1598
|
+
import { relative as relative5, resolve as resolve5 } from "path";
|
|
1290
1599
|
import {
|
|
1291
1600
|
errorDatabaseUrlRequired as errorDatabaseUrlRequired3,
|
|
1292
1601
|
errorDriverRequired as errorDriverRequired3,
|
|
@@ -1294,9 +1603,9 @@ import {
|
|
|
1294
1603
|
errorRuntime as errorRuntime3,
|
|
1295
1604
|
errorUnexpected as errorUnexpected4
|
|
1296
1605
|
} from "@prisma-next/core-control-plane/errors";
|
|
1297
|
-
import { Command as
|
|
1606
|
+
import { Command as Command4 } from "commander";
|
|
1298
1607
|
function createDbSchemaVerifyCommand() {
|
|
1299
|
-
const command = new
|
|
1608
|
+
const command = new Command4("schema-verify");
|
|
1300
1609
|
setCommandDescriptions(
|
|
1301
1610
|
command,
|
|
1302
1611
|
"Check whether the database schema satisfies your contract",
|
|
@@ -1311,9 +1620,9 @@ function createDbSchemaVerifyCommand() {
|
|
|
1311
1620
|
const flags = parseGlobalFlags(options);
|
|
1312
1621
|
const result = await performAction(async () => {
|
|
1313
1622
|
const config = await loadConfig(options.config);
|
|
1314
|
-
const configPath = options.config ?
|
|
1315
|
-
const contractPathAbsolute = config.contract?.output ?
|
|
1316
|
-
const contractPath =
|
|
1623
|
+
const configPath = options.config ? relative5(process.cwd(), resolve5(options.config)) : "prisma-next.config.ts";
|
|
1624
|
+
const contractPathAbsolute = config.contract?.output ? resolve5(config.contract.output) : resolve5("src/prisma/contract.json");
|
|
1625
|
+
const contractPath = relative5(process.cwd(), contractPathAbsolute);
|
|
1317
1626
|
if (flags.json !== "object" && !flags.quiet) {
|
|
1318
1627
|
const details = [
|
|
1319
1628
|
{ label: "config", value: configPath },
|
|
@@ -1333,7 +1642,7 @@ function createDbSchemaVerifyCommand() {
|
|
|
1333
1642
|
}
|
|
1334
1643
|
let contractJsonContent;
|
|
1335
1644
|
try {
|
|
1336
|
-
contractJsonContent = await
|
|
1645
|
+
contractJsonContent = await readFile3(contractPathAbsolute, "utf-8");
|
|
1337
1646
|
} catch (error) {
|
|
1338
1647
|
if (error instanceof Error && error.code === "ENOENT") {
|
|
1339
1648
|
throw errorFileNotFound2(contractPathAbsolute, {
|
|
@@ -1364,17 +1673,23 @@ function createDbSchemaVerifyCommand() {
|
|
|
1364
1673
|
driver: driverDescriptor,
|
|
1365
1674
|
extensions: config.extensions ?? []
|
|
1366
1675
|
});
|
|
1367
|
-
const
|
|
1368
|
-
const
|
|
1676
|
+
const rawComponents = [config.target, config.adapter, ...config.extensions ?? []];
|
|
1677
|
+
const frameworkComponents = assertFrameworkComponentsCompatible(
|
|
1678
|
+
config.family.familyId,
|
|
1679
|
+
config.target.targetId,
|
|
1680
|
+
rawComponents
|
|
1681
|
+
);
|
|
1682
|
+
const contractIR = familyInstance.validateContractIR(contractJson);
|
|
1369
1683
|
let schemaVerifyResult;
|
|
1370
1684
|
try {
|
|
1371
1685
|
schemaVerifyResult = await withSpinner(
|
|
1372
|
-
() =>
|
|
1686
|
+
() => familyInstance.schemaVerify({
|
|
1373
1687
|
driver,
|
|
1374
1688
|
contractIR,
|
|
1375
1689
|
strict: options.strict ?? false,
|
|
1376
1690
|
contractPath: contractPathAbsolute,
|
|
1377
|
-
configPath
|
|
1691
|
+
configPath,
|
|
1692
|
+
frameworkComponents
|
|
1378
1693
|
}),
|
|
1379
1694
|
{
|
|
1380
1695
|
message: "Verifying database schema...",
|
|
@@ -1414,8 +1729,8 @@ function createDbSchemaVerifyCommand() {
|
|
|
1414
1729
|
}
|
|
1415
1730
|
|
|
1416
1731
|
// src/commands/db-sign.ts
|
|
1417
|
-
import { readFile as
|
|
1418
|
-
import { relative as
|
|
1732
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
1733
|
+
import { relative as relative6, resolve as resolve6 } from "path";
|
|
1419
1734
|
import {
|
|
1420
1735
|
errorDatabaseUrlRequired as errorDatabaseUrlRequired4,
|
|
1421
1736
|
errorDriverRequired as errorDriverRequired4,
|
|
@@ -1423,9 +1738,9 @@ import {
|
|
|
1423
1738
|
errorRuntime as errorRuntime4,
|
|
1424
1739
|
errorUnexpected as errorUnexpected5
|
|
1425
1740
|
} from "@prisma-next/core-control-plane/errors";
|
|
1426
|
-
import { Command as
|
|
1741
|
+
import { Command as Command5 } from "commander";
|
|
1427
1742
|
function createDbSignCommand() {
|
|
1428
|
-
const command = new
|
|
1743
|
+
const command = new Command5("sign");
|
|
1429
1744
|
setCommandDescriptions(
|
|
1430
1745
|
command,
|
|
1431
1746
|
"Sign the database with your contract so you can safely run queries",
|
|
@@ -1440,9 +1755,9 @@ function createDbSignCommand() {
|
|
|
1440
1755
|
const flags = parseGlobalFlags(options);
|
|
1441
1756
|
const result = await performAction(async () => {
|
|
1442
1757
|
const config = await loadConfig(options.config);
|
|
1443
|
-
const configPath = options.config ?
|
|
1444
|
-
const contractPathAbsolute = config.contract?.output ?
|
|
1445
|
-
const contractPath =
|
|
1758
|
+
const configPath = options.config ? relative6(process.cwd(), resolve6(options.config)) : "prisma-next.config.ts";
|
|
1759
|
+
const contractPathAbsolute = config.contract?.output ? resolve6(config.contract.output) : resolve6("src/prisma/contract.json");
|
|
1760
|
+
const contractPath = relative6(process.cwd(), contractPathAbsolute);
|
|
1446
1761
|
if (flags.json !== "object" && !flags.quiet) {
|
|
1447
1762
|
const details = [
|
|
1448
1763
|
{ label: "config", value: configPath },
|
|
@@ -1462,7 +1777,7 @@ function createDbSignCommand() {
|
|
|
1462
1777
|
}
|
|
1463
1778
|
let contractJsonContent;
|
|
1464
1779
|
try {
|
|
1465
|
-
contractJsonContent = await
|
|
1780
|
+
contractJsonContent = await readFile4(contractPathAbsolute, "utf-8");
|
|
1466
1781
|
} catch (error) {
|
|
1467
1782
|
if (error instanceof Error && error.code === "ENOENT") {
|
|
1468
1783
|
throw errorFileNotFound3(contractPathAbsolute, {
|
|
@@ -1490,20 +1805,24 @@ function createDbSignCommand() {
|
|
|
1490
1805
|
driver: driverDescriptor,
|
|
1491
1806
|
extensions: config.extensions ?? []
|
|
1492
1807
|
});
|
|
1493
|
-
const
|
|
1494
|
-
const
|
|
1808
|
+
const rawComponents = [config.target, config.adapter, ...config.extensions ?? []];
|
|
1809
|
+
const frameworkComponents = assertFrameworkComponentsCompatible(
|
|
1810
|
+
config.family.familyId,
|
|
1811
|
+
config.target.targetId,
|
|
1812
|
+
rawComponents
|
|
1813
|
+
);
|
|
1814
|
+
const contractIR = familyInstance.validateContractIR(contractJson);
|
|
1495
1815
|
let schemaVerifyResult;
|
|
1496
1816
|
try {
|
|
1497
1817
|
schemaVerifyResult = await withSpinner(
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
},
|
|
1818
|
+
() => familyInstance.schemaVerify({
|
|
1819
|
+
driver,
|
|
1820
|
+
contractIR,
|
|
1821
|
+
strict: false,
|
|
1822
|
+
contractPath: contractPathAbsolute,
|
|
1823
|
+
configPath,
|
|
1824
|
+
frameworkComponents
|
|
1825
|
+
}),
|
|
1507
1826
|
{
|
|
1508
1827
|
message: "Verifying database satisfies contract",
|
|
1509
1828
|
flags
|
|
@@ -1520,14 +1839,12 @@ function createDbSignCommand() {
|
|
|
1520
1839
|
let signResult;
|
|
1521
1840
|
try {
|
|
1522
1841
|
signResult = await withSpinner(
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
});
|
|
1530
|
-
},
|
|
1842
|
+
() => familyInstance.sign({
|
|
1843
|
+
driver,
|
|
1844
|
+
contractIR,
|
|
1845
|
+
contractPath: contractPathAbsolute,
|
|
1846
|
+
configPath
|
|
1847
|
+
}),
|
|
1531
1848
|
{
|
|
1532
1849
|
message: "Signing database...",
|
|
1533
1850
|
flags
|
|
@@ -1580,8 +1897,8 @@ function createDbSignCommand() {
|
|
|
1580
1897
|
}
|
|
1581
1898
|
|
|
1582
1899
|
// src/commands/db-verify.ts
|
|
1583
|
-
import { readFile as
|
|
1584
|
-
import { relative as
|
|
1900
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
1901
|
+
import { relative as relative7, resolve as resolve7 } from "path";
|
|
1585
1902
|
import {
|
|
1586
1903
|
errorDatabaseUrlRequired as errorDatabaseUrlRequired5,
|
|
1587
1904
|
errorDriverRequired as errorDriverRequired5,
|
|
@@ -1592,9 +1909,9 @@ import {
|
|
|
1592
1909
|
errorTargetMismatch as errorTargetMismatch2,
|
|
1593
1910
|
errorUnexpected as errorUnexpected6
|
|
1594
1911
|
} from "@prisma-next/core-control-plane/errors";
|
|
1595
|
-
import { Command as
|
|
1912
|
+
import { Command as Command6 } from "commander";
|
|
1596
1913
|
function createDbVerifyCommand() {
|
|
1597
|
-
const command = new
|
|
1914
|
+
const command = new Command6("verify");
|
|
1598
1915
|
setCommandDescriptions(
|
|
1599
1916
|
command,
|
|
1600
1917
|
"Check whether the database has been signed with your contract",
|
|
@@ -1609,9 +1926,9 @@ function createDbVerifyCommand() {
|
|
|
1609
1926
|
const flags = parseGlobalFlags(options);
|
|
1610
1927
|
const result = await performAction(async () => {
|
|
1611
1928
|
const config = await loadConfig(options.config);
|
|
1612
|
-
const configPath = options.config ?
|
|
1613
|
-
const contractPathAbsolute = config.contract?.output ?
|
|
1614
|
-
const contractPath =
|
|
1929
|
+
const configPath = options.config ? relative7(process.cwd(), resolve7(options.config)) : "prisma-next.config.ts";
|
|
1930
|
+
const contractPathAbsolute = config.contract?.output ? resolve7(config.contract.output) : resolve7("src/prisma/contract.json");
|
|
1931
|
+
const contractPath = relative7(process.cwd(), contractPathAbsolute);
|
|
1615
1932
|
if (flags.json !== "object" && !flags.quiet) {
|
|
1616
1933
|
const details = [
|
|
1617
1934
|
{ label: "config", value: configPath },
|
|
@@ -1631,7 +1948,7 @@ function createDbVerifyCommand() {
|
|
|
1631
1948
|
}
|
|
1632
1949
|
let contractJsonContent;
|
|
1633
1950
|
try {
|
|
1634
|
-
contractJsonContent = await
|
|
1951
|
+
contractJsonContent = await readFile5(contractPathAbsolute, "utf-8");
|
|
1635
1952
|
} catch (error) {
|
|
1636
1953
|
if (error instanceof Error && error.code === "ENOENT") {
|
|
1637
1954
|
throw errorFileNotFound4(contractPathAbsolute, {
|
|
@@ -1662,12 +1979,11 @@ function createDbVerifyCommand() {
|
|
|
1662
1979
|
driver: driverDescriptor,
|
|
1663
1980
|
extensions: config.extensions ?? []
|
|
1664
1981
|
});
|
|
1665
|
-
const
|
|
1666
|
-
const contractIR = typedFamilyInstance.validateContractIR(contractJson);
|
|
1982
|
+
const contractIR = familyInstance.validateContractIR(contractJson);
|
|
1667
1983
|
let verifyResult;
|
|
1668
1984
|
try {
|
|
1669
1985
|
verifyResult = await withSpinner(
|
|
1670
|
-
() =>
|
|
1986
|
+
() => familyInstance.verify({
|
|
1671
1987
|
driver,
|
|
1672
1988
|
contractIR,
|
|
1673
1989
|
expectedTargetId: config.target.targetId,
|
|
@@ -1726,7 +2042,7 @@ function createDbVerifyCommand() {
|
|
|
1726
2042
|
}
|
|
1727
2043
|
|
|
1728
2044
|
// src/cli.ts
|
|
1729
|
-
var program = new
|
|
2045
|
+
var program = new Command7();
|
|
1730
2046
|
program.name("prisma-next").description("Prisma Next CLI").version("0.0.1");
|
|
1731
2047
|
var versionOption = program.options.find((opt) => opt.flags.includes("--version"));
|
|
1732
2048
|
if (versionOption) {
|
|
@@ -1790,7 +2106,7 @@ program.exitOverride((err2) => {
|
|
|
1790
2106
|
}
|
|
1791
2107
|
process.exit(0);
|
|
1792
2108
|
});
|
|
1793
|
-
var contractCommand = new
|
|
2109
|
+
var contractCommand = new Command7("contract");
|
|
1794
2110
|
setCommandDescriptions(
|
|
1795
2111
|
contractCommand,
|
|
1796
2112
|
"Contract management commands",
|
|
@@ -1806,7 +2122,7 @@ contractCommand.configureHelp({
|
|
|
1806
2122
|
var contractEmitCommand = createContractEmitCommand();
|
|
1807
2123
|
contractCommand.addCommand(contractEmitCommand);
|
|
1808
2124
|
program.addCommand(contractCommand);
|
|
1809
|
-
var dbCommand = new
|
|
2125
|
+
var dbCommand = new Command7("db");
|
|
1810
2126
|
setCommandDescriptions(
|
|
1811
2127
|
dbCommand,
|
|
1812
2128
|
"Database management commands",
|
|
@@ -1821,6 +2137,8 @@ dbCommand.configureHelp({
|
|
|
1821
2137
|
});
|
|
1822
2138
|
var dbVerifyCommand = createDbVerifyCommand();
|
|
1823
2139
|
dbCommand.addCommand(dbVerifyCommand);
|
|
2140
|
+
var dbInitCommand = createDbInitCommand();
|
|
2141
|
+
dbCommand.addCommand(dbInitCommand);
|
|
1824
2142
|
var dbIntrospectCommand = createDbIntrospectCommand();
|
|
1825
2143
|
dbCommand.addCommand(dbIntrospectCommand);
|
|
1826
2144
|
var dbSchemaVerifyCommand = createDbSchemaVerifyCommand();
|
|
@@ -1828,7 +2146,7 @@ dbCommand.addCommand(dbSchemaVerifyCommand);
|
|
|
1828
2146
|
var dbSignCommand = createDbSignCommand();
|
|
1829
2147
|
dbCommand.addCommand(dbSignCommand);
|
|
1830
2148
|
program.addCommand(dbCommand);
|
|
1831
|
-
var helpCommand = new
|
|
2149
|
+
var helpCommand = new Command7("help").description("Show usage instructions").configureHelp({
|
|
1832
2150
|
formatHelp: (cmd) => {
|
|
1833
2151
|
const flags = parseGlobalFlags({});
|
|
1834
2152
|
return formatCommandHelp({ command: cmd, flags });
|