@prisma-next/cli 0.3.0-dev.15 → 0.3.0-dev.16
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/chunk-5MPKZYVI.js +47 -0
- package/dist/chunk-5MPKZYVI.js.map +1 -0
- package/dist/chunk-74IELXRA.js +371 -0
- package/dist/chunk-74IELXRA.js.map +1 -0
- package/dist/{chunk-MG7PBERL.js → chunk-U6QI3AZ3.js} +7 -5
- package/dist/{chunk-MG7PBERL.js.map → chunk-U6QI3AZ3.js.map} +1 -1
- package/dist/{chunk-DIJPT5TZ.js → chunk-ZG5T6OB5.js} +2 -46
- package/dist/chunk-ZG5T6OB5.js.map +1 -0
- package/dist/cli.js +593 -268
- package/dist/cli.js.map +1 -1
- package/dist/commands/contract-emit.js +3 -2
- package/dist/commands/db-init.d.ts.map +1 -1
- package/dist/commands/db-init.js +238 -275
- package/dist/commands/db-init.js.map +1 -1
- package/dist/commands/db-introspect.js +6 -4
- package/dist/commands/db-introspect.js.map +1 -1
- package/dist/commands/db-schema-verify.js +6 -4
- package/dist/commands/db-schema-verify.js.map +1 -1
- package/dist/commands/db-sign.js +6 -4
- package/dist/commands/db-sign.js.map +1 -1
- package/dist/commands/db-verify.js +6 -4
- package/dist/commands/db-verify.js.map +1 -1
- package/dist/control-api/operations/db-init.d.ts +3 -1
- package/dist/control-api/operations/db-init.d.ts.map +1 -1
- package/dist/control-api/types.d.ts +54 -1
- package/dist/control-api/types.d.ts.map +1 -1
- package/dist/exports/control-api.d.ts +1 -1
- package/dist/exports/control-api.d.ts.map +1 -1
- package/dist/exports/control-api.js +3 -234
- package/dist/exports/control-api.js.map +1 -1
- package/dist/exports/index.js +3 -2
- package/dist/exports/index.js.map +1 -1
- package/dist/utils/progress-adapter.d.ts +26 -0
- package/dist/utils/progress-adapter.d.ts.map +1 -0
- package/package.json +11 -11
- package/src/commands/db-init.ts +262 -355
- package/src/control-api/client.ts +30 -0
- package/src/control-api/operations/db-init.ts +116 -2
- package/src/control-api/types.ts +63 -1
- package/src/exports/control-api.ts +3 -0
- package/src/utils/progress-adapter.ts +86 -0
- package/dist/chunk-DIJPT5TZ.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1192,10 +1192,12 @@ function createContractEmitCommand() {
|
|
|
1192
1192
|
// src/commands/db-init.ts
|
|
1193
1193
|
import { readFile } from "fs/promises";
|
|
1194
1194
|
import { relative as relative3, resolve as resolve3 } from "path";
|
|
1195
|
-
import {
|
|
1196
|
-
import { redactDatabaseUrl } from "@prisma-next/utils/redact-db-url";
|
|
1195
|
+
import { notOk as notOk3, ok as ok3 } from "@prisma-next/utils/result";
|
|
1197
1196
|
import { Command as Command2 } from "commander";
|
|
1198
1197
|
|
|
1198
|
+
// src/control-api/client.ts
|
|
1199
|
+
import { createControlPlaneStack as createControlPlaneStack2 } from "@prisma-next/core-control-plane/stack";
|
|
1200
|
+
|
|
1199
1201
|
// src/utils/framework-components.ts
|
|
1200
1202
|
import {
|
|
1201
1203
|
checkContractComponentRequirements
|
|
@@ -1277,7 +1279,587 @@ function assertContractRequirementsSatisfied({
|
|
|
1277
1279
|
}
|
|
1278
1280
|
}
|
|
1279
1281
|
|
|
1282
|
+
// src/control-api/operations/db-init.ts
|
|
1283
|
+
import { notOk as notOk2, ok as ok2 } from "@prisma-next/utils/result";
|
|
1284
|
+
async function executeDbInit(options) {
|
|
1285
|
+
const { driver, familyInstance, contractIR, mode, migrations, frameworkComponents, onProgress } = options;
|
|
1286
|
+
const planner = migrations.createPlanner(familyInstance);
|
|
1287
|
+
const runner = migrations.createRunner(familyInstance);
|
|
1288
|
+
const introspectSpanId = "introspect";
|
|
1289
|
+
onProgress?.({
|
|
1290
|
+
action: "dbInit",
|
|
1291
|
+
kind: "spanStart",
|
|
1292
|
+
spanId: introspectSpanId,
|
|
1293
|
+
label: "Introspecting database schema"
|
|
1294
|
+
});
|
|
1295
|
+
const schemaIR = await familyInstance.introspect({ driver });
|
|
1296
|
+
onProgress?.({
|
|
1297
|
+
action: "dbInit",
|
|
1298
|
+
kind: "spanEnd",
|
|
1299
|
+
spanId: introspectSpanId,
|
|
1300
|
+
outcome: "ok"
|
|
1301
|
+
});
|
|
1302
|
+
const policy = { allowedOperationClasses: ["additive"] };
|
|
1303
|
+
const planSpanId = "plan";
|
|
1304
|
+
onProgress?.({
|
|
1305
|
+
action: "dbInit",
|
|
1306
|
+
kind: "spanStart",
|
|
1307
|
+
spanId: planSpanId,
|
|
1308
|
+
label: "Planning migration"
|
|
1309
|
+
});
|
|
1310
|
+
const plannerResult = await planner.plan({
|
|
1311
|
+
contract: contractIR,
|
|
1312
|
+
schema: schemaIR,
|
|
1313
|
+
policy,
|
|
1314
|
+
frameworkComponents
|
|
1315
|
+
});
|
|
1316
|
+
if (plannerResult.kind === "failure") {
|
|
1317
|
+
onProgress?.({
|
|
1318
|
+
action: "dbInit",
|
|
1319
|
+
kind: "spanEnd",
|
|
1320
|
+
spanId: planSpanId,
|
|
1321
|
+
outcome: "error"
|
|
1322
|
+
});
|
|
1323
|
+
return notOk2({
|
|
1324
|
+
code: "PLANNING_FAILED",
|
|
1325
|
+
summary: "Migration planning failed due to conflicts",
|
|
1326
|
+
conflicts: plannerResult.conflicts,
|
|
1327
|
+
why: void 0,
|
|
1328
|
+
meta: void 0
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
const migrationPlan = plannerResult.plan;
|
|
1332
|
+
onProgress?.({
|
|
1333
|
+
action: "dbInit",
|
|
1334
|
+
kind: "spanEnd",
|
|
1335
|
+
spanId: planSpanId,
|
|
1336
|
+
outcome: "ok"
|
|
1337
|
+
});
|
|
1338
|
+
const checkMarkerSpanId = "checkMarker";
|
|
1339
|
+
onProgress?.({
|
|
1340
|
+
action: "dbInit",
|
|
1341
|
+
kind: "spanStart",
|
|
1342
|
+
spanId: checkMarkerSpanId,
|
|
1343
|
+
label: "Checking contract marker"
|
|
1344
|
+
});
|
|
1345
|
+
const existingMarker = await familyInstance.readMarker({ driver });
|
|
1346
|
+
if (existingMarker) {
|
|
1347
|
+
const markerMatchesDestination = existingMarker.coreHash === migrationPlan.destination.coreHash && (!migrationPlan.destination.profileHash || existingMarker.profileHash === migrationPlan.destination.profileHash);
|
|
1348
|
+
if (markerMatchesDestination) {
|
|
1349
|
+
onProgress?.({
|
|
1350
|
+
action: "dbInit",
|
|
1351
|
+
kind: "spanEnd",
|
|
1352
|
+
spanId: checkMarkerSpanId,
|
|
1353
|
+
outcome: "skipped"
|
|
1354
|
+
});
|
|
1355
|
+
const result2 = {
|
|
1356
|
+
mode,
|
|
1357
|
+
plan: { operations: [] },
|
|
1358
|
+
...mode === "apply" ? {
|
|
1359
|
+
execution: { operationsPlanned: 0, operationsExecuted: 0 },
|
|
1360
|
+
marker: {
|
|
1361
|
+
coreHash: existingMarker.coreHash,
|
|
1362
|
+
profileHash: existingMarker.profileHash
|
|
1363
|
+
}
|
|
1364
|
+
} : {},
|
|
1365
|
+
summary: "Database already at target contract state"
|
|
1366
|
+
};
|
|
1367
|
+
return ok2(result2);
|
|
1368
|
+
}
|
|
1369
|
+
onProgress?.({
|
|
1370
|
+
action: "dbInit",
|
|
1371
|
+
kind: "spanEnd",
|
|
1372
|
+
spanId: checkMarkerSpanId,
|
|
1373
|
+
outcome: "error"
|
|
1374
|
+
});
|
|
1375
|
+
return notOk2({
|
|
1376
|
+
code: "MARKER_ORIGIN_MISMATCH",
|
|
1377
|
+
summary: "Existing contract marker does not match plan destination",
|
|
1378
|
+
marker: {
|
|
1379
|
+
coreHash: existingMarker.coreHash,
|
|
1380
|
+
profileHash: existingMarker.profileHash
|
|
1381
|
+
},
|
|
1382
|
+
destination: {
|
|
1383
|
+
coreHash: migrationPlan.destination.coreHash,
|
|
1384
|
+
profileHash: migrationPlan.destination.profileHash
|
|
1385
|
+
},
|
|
1386
|
+
why: void 0,
|
|
1387
|
+
conflicts: void 0,
|
|
1388
|
+
meta: void 0
|
|
1389
|
+
});
|
|
1390
|
+
}
|
|
1391
|
+
onProgress?.({
|
|
1392
|
+
action: "dbInit",
|
|
1393
|
+
kind: "spanEnd",
|
|
1394
|
+
spanId: checkMarkerSpanId,
|
|
1395
|
+
outcome: "ok"
|
|
1396
|
+
});
|
|
1397
|
+
if (mode === "plan") {
|
|
1398
|
+
const result2 = {
|
|
1399
|
+
mode: "plan",
|
|
1400
|
+
plan: { operations: migrationPlan.operations },
|
|
1401
|
+
summary: `Planned ${migrationPlan.operations.length} operation(s)`
|
|
1402
|
+
};
|
|
1403
|
+
return ok2(result2);
|
|
1404
|
+
}
|
|
1405
|
+
const applySpanId = "apply";
|
|
1406
|
+
onProgress?.({
|
|
1407
|
+
action: "dbInit",
|
|
1408
|
+
kind: "spanStart",
|
|
1409
|
+
spanId: applySpanId,
|
|
1410
|
+
label: "Applying migration plan"
|
|
1411
|
+
});
|
|
1412
|
+
const callbacks = onProgress ? {
|
|
1413
|
+
onOperationStart: (op) => {
|
|
1414
|
+
onProgress({
|
|
1415
|
+
action: "dbInit",
|
|
1416
|
+
kind: "spanStart",
|
|
1417
|
+
spanId: `operation:${op.id}`,
|
|
1418
|
+
parentSpanId: applySpanId,
|
|
1419
|
+
label: op.label
|
|
1420
|
+
});
|
|
1421
|
+
},
|
|
1422
|
+
onOperationComplete: (op) => {
|
|
1423
|
+
onProgress({
|
|
1424
|
+
action: "dbInit",
|
|
1425
|
+
kind: "spanEnd",
|
|
1426
|
+
spanId: `operation:${op.id}`,
|
|
1427
|
+
outcome: "ok"
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1430
|
+
} : void 0;
|
|
1431
|
+
const runnerResult = await runner.execute({
|
|
1432
|
+
plan: migrationPlan,
|
|
1433
|
+
driver,
|
|
1434
|
+
destinationContract: contractIR,
|
|
1435
|
+
policy,
|
|
1436
|
+
...callbacks ? { callbacks } : {},
|
|
1437
|
+
// db init plans and applies back-to-back from a fresh introspection, so per-operation
|
|
1438
|
+
// pre/postchecks and the idempotency probe are usually redundant overhead. We still
|
|
1439
|
+
// enforce marker/origin compatibility and a full schema verification after apply.
|
|
1440
|
+
executionChecks: {
|
|
1441
|
+
prechecks: false,
|
|
1442
|
+
postchecks: false,
|
|
1443
|
+
idempotencyChecks: false
|
|
1444
|
+
},
|
|
1445
|
+
frameworkComponents
|
|
1446
|
+
});
|
|
1447
|
+
if (!runnerResult.ok) {
|
|
1448
|
+
onProgress?.({
|
|
1449
|
+
action: "dbInit",
|
|
1450
|
+
kind: "spanEnd",
|
|
1451
|
+
spanId: applySpanId,
|
|
1452
|
+
outcome: "error"
|
|
1453
|
+
});
|
|
1454
|
+
return notOk2({
|
|
1455
|
+
code: "RUNNER_FAILED",
|
|
1456
|
+
summary: runnerResult.failure.summary,
|
|
1457
|
+
why: runnerResult.failure.why,
|
|
1458
|
+
meta: runnerResult.failure.meta,
|
|
1459
|
+
conflicts: void 0
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
const execution = runnerResult.value;
|
|
1463
|
+
onProgress?.({
|
|
1464
|
+
action: "dbInit",
|
|
1465
|
+
kind: "spanEnd",
|
|
1466
|
+
spanId: applySpanId,
|
|
1467
|
+
outcome: "ok"
|
|
1468
|
+
});
|
|
1469
|
+
const result = {
|
|
1470
|
+
mode: "apply",
|
|
1471
|
+
plan: { operations: migrationPlan.operations },
|
|
1472
|
+
execution: {
|
|
1473
|
+
operationsPlanned: execution.operationsPlanned,
|
|
1474
|
+
operationsExecuted: execution.operationsExecuted
|
|
1475
|
+
},
|
|
1476
|
+
marker: migrationPlan.destination.profileHash ? {
|
|
1477
|
+
coreHash: migrationPlan.destination.coreHash,
|
|
1478
|
+
profileHash: migrationPlan.destination.profileHash
|
|
1479
|
+
} : { coreHash: migrationPlan.destination.coreHash },
|
|
1480
|
+
summary: `Applied ${execution.operationsExecuted} operation(s), marker written`
|
|
1481
|
+
};
|
|
1482
|
+
return ok2(result);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
// src/control-api/client.ts
|
|
1486
|
+
function createControlClient(options) {
|
|
1487
|
+
return new ControlClientImpl(options);
|
|
1488
|
+
}
|
|
1489
|
+
var ControlClientImpl = class {
|
|
1490
|
+
options;
|
|
1491
|
+
stack = null;
|
|
1492
|
+
driver = null;
|
|
1493
|
+
familyInstance = null;
|
|
1494
|
+
frameworkComponents = null;
|
|
1495
|
+
initialized = false;
|
|
1496
|
+
defaultConnection;
|
|
1497
|
+
constructor(options) {
|
|
1498
|
+
this.options = options;
|
|
1499
|
+
this.defaultConnection = options.connection;
|
|
1500
|
+
}
|
|
1501
|
+
init() {
|
|
1502
|
+
if (this.initialized) {
|
|
1503
|
+
return;
|
|
1504
|
+
}
|
|
1505
|
+
this.stack = createControlPlaneStack2({
|
|
1506
|
+
target: this.options.target,
|
|
1507
|
+
adapter: this.options.adapter,
|
|
1508
|
+
driver: this.options.driver,
|
|
1509
|
+
extensionPacks: this.options.extensionPacks
|
|
1510
|
+
});
|
|
1511
|
+
this.familyInstance = this.options.family.create(this.stack);
|
|
1512
|
+
const rawComponents = [
|
|
1513
|
+
this.options.target,
|
|
1514
|
+
this.options.adapter,
|
|
1515
|
+
...this.options.extensionPacks ?? []
|
|
1516
|
+
];
|
|
1517
|
+
this.frameworkComponents = assertFrameworkComponentsCompatible(
|
|
1518
|
+
this.options.family.familyId,
|
|
1519
|
+
this.options.target.targetId,
|
|
1520
|
+
rawComponents
|
|
1521
|
+
);
|
|
1522
|
+
this.initialized = true;
|
|
1523
|
+
}
|
|
1524
|
+
async connect(connection) {
|
|
1525
|
+
this.init();
|
|
1526
|
+
if (this.driver) {
|
|
1527
|
+
throw new Error("Already connected. Call close() before reconnecting.");
|
|
1528
|
+
}
|
|
1529
|
+
const resolvedConnection = connection ?? this.defaultConnection;
|
|
1530
|
+
if (resolvedConnection === void 0) {
|
|
1531
|
+
throw new Error(
|
|
1532
|
+
"No connection provided. Pass a connection to connect() or provide a default connection when creating the client."
|
|
1533
|
+
);
|
|
1534
|
+
}
|
|
1535
|
+
if (!this.stack?.driver) {
|
|
1536
|
+
throw new Error(
|
|
1537
|
+
"Driver is not configured. Pass a driver descriptor when creating the control client to enable database operations."
|
|
1538
|
+
);
|
|
1539
|
+
}
|
|
1540
|
+
this.driver = await this.stack?.driver.create(resolvedConnection);
|
|
1541
|
+
}
|
|
1542
|
+
async close() {
|
|
1543
|
+
if (this.driver) {
|
|
1544
|
+
await this.driver.close();
|
|
1545
|
+
this.driver = null;
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
async ensureConnected() {
|
|
1549
|
+
this.init();
|
|
1550
|
+
if (!this.driver && this.defaultConnection !== void 0) {
|
|
1551
|
+
await this.connect(this.defaultConnection);
|
|
1552
|
+
}
|
|
1553
|
+
if (!this.driver || !this.familyInstance || !this.frameworkComponents) {
|
|
1554
|
+
throw new Error("Not connected. Call connect(connection) first.");
|
|
1555
|
+
}
|
|
1556
|
+
return {
|
|
1557
|
+
driver: this.driver,
|
|
1558
|
+
familyInstance: this.familyInstance,
|
|
1559
|
+
frameworkComponents: this.frameworkComponents
|
|
1560
|
+
};
|
|
1561
|
+
}
|
|
1562
|
+
async verify(options) {
|
|
1563
|
+
const { driver, familyInstance } = await this.ensureConnected();
|
|
1564
|
+
const contractIR = familyInstance.validateContractIR(options.contractIR);
|
|
1565
|
+
return familyInstance.verify({
|
|
1566
|
+
driver,
|
|
1567
|
+
contractIR,
|
|
1568
|
+
expectedTargetId: this.options.target.targetId,
|
|
1569
|
+
contractPath: ""
|
|
1570
|
+
});
|
|
1571
|
+
}
|
|
1572
|
+
async schemaVerify(options) {
|
|
1573
|
+
const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
|
|
1574
|
+
const contractIR = familyInstance.validateContractIR(options.contractIR);
|
|
1575
|
+
return familyInstance.schemaVerify({
|
|
1576
|
+
driver,
|
|
1577
|
+
contractIR,
|
|
1578
|
+
strict: options.strict ?? false,
|
|
1579
|
+
contractPath: "",
|
|
1580
|
+
frameworkComponents
|
|
1581
|
+
});
|
|
1582
|
+
}
|
|
1583
|
+
async sign(options) {
|
|
1584
|
+
const { driver, familyInstance } = await this.ensureConnected();
|
|
1585
|
+
const contractIR = familyInstance.validateContractIR(options.contractIR);
|
|
1586
|
+
return familyInstance.sign({
|
|
1587
|
+
driver,
|
|
1588
|
+
contractIR,
|
|
1589
|
+
contractPath: ""
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
async dbInit(options) {
|
|
1593
|
+
const { onProgress } = options;
|
|
1594
|
+
if (options.connection !== void 0) {
|
|
1595
|
+
onProgress?.({
|
|
1596
|
+
action: "dbInit",
|
|
1597
|
+
kind: "spanStart",
|
|
1598
|
+
spanId: "connect",
|
|
1599
|
+
label: "Connecting to database..."
|
|
1600
|
+
});
|
|
1601
|
+
try {
|
|
1602
|
+
await this.connect(options.connection);
|
|
1603
|
+
onProgress?.({
|
|
1604
|
+
action: "dbInit",
|
|
1605
|
+
kind: "spanEnd",
|
|
1606
|
+
spanId: "connect",
|
|
1607
|
+
outcome: "ok"
|
|
1608
|
+
});
|
|
1609
|
+
} catch (error) {
|
|
1610
|
+
onProgress?.({
|
|
1611
|
+
action: "dbInit",
|
|
1612
|
+
kind: "spanEnd",
|
|
1613
|
+
spanId: "connect",
|
|
1614
|
+
outcome: "error"
|
|
1615
|
+
});
|
|
1616
|
+
throw error;
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
|
|
1620
|
+
if (!this.options.target.migrations) {
|
|
1621
|
+
throw new Error(`Target "${this.options.target.targetId}" does not support migrations`);
|
|
1622
|
+
}
|
|
1623
|
+
const contractIR = familyInstance.validateContractIR(options.contractIR);
|
|
1624
|
+
return executeDbInit({
|
|
1625
|
+
driver,
|
|
1626
|
+
familyInstance,
|
|
1627
|
+
contractIR,
|
|
1628
|
+
mode: options.mode,
|
|
1629
|
+
migrations: this.options.target.migrations,
|
|
1630
|
+
frameworkComponents,
|
|
1631
|
+
...onProgress ? { onProgress } : {}
|
|
1632
|
+
});
|
|
1633
|
+
}
|
|
1634
|
+
async introspect(options) {
|
|
1635
|
+
const { driver, familyInstance } = await this.ensureConnected();
|
|
1636
|
+
const _schema = options?.schema;
|
|
1637
|
+
void _schema;
|
|
1638
|
+
return familyInstance.introspect({ driver });
|
|
1639
|
+
}
|
|
1640
|
+
};
|
|
1641
|
+
|
|
1642
|
+
// src/utils/progress-adapter.ts
|
|
1643
|
+
import ora2 from "ora";
|
|
1644
|
+
function createProgressAdapter(options) {
|
|
1645
|
+
const { flags } = options;
|
|
1646
|
+
const shouldShowProgress = !flags.quiet && flags.json !== "object" && process.stdout.isTTY;
|
|
1647
|
+
if (!shouldShowProgress) {
|
|
1648
|
+
return () => {
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
const activeSpans = /* @__PURE__ */ new Map();
|
|
1652
|
+
return (event) => {
|
|
1653
|
+
if (event.kind === "spanStart") {
|
|
1654
|
+
if (event.parentSpanId) {
|
|
1655
|
+
console.log(` \u2192 ${event.label}...`);
|
|
1656
|
+
return;
|
|
1657
|
+
}
|
|
1658
|
+
const spinner = ora2({
|
|
1659
|
+
text: event.label,
|
|
1660
|
+
color: flags.color !== false ? "cyan" : false
|
|
1661
|
+
}).start();
|
|
1662
|
+
activeSpans.set(event.spanId, {
|
|
1663
|
+
spinner,
|
|
1664
|
+
startTime: Date.now()
|
|
1665
|
+
});
|
|
1666
|
+
} else if (event.kind === "spanEnd") {
|
|
1667
|
+
const spanState = activeSpans.get(event.spanId);
|
|
1668
|
+
if (spanState) {
|
|
1669
|
+
const elapsed = Date.now() - spanState.startTime;
|
|
1670
|
+
if (event.outcome === "error") {
|
|
1671
|
+
spanState.spinner.fail(`${spanState.spinner.text} (failed)`);
|
|
1672
|
+
} else if (event.outcome === "skipped") {
|
|
1673
|
+
spanState.spinner.info(`${spanState.spinner.text} (skipped)`);
|
|
1674
|
+
} else {
|
|
1675
|
+
spanState.spinner.succeed(`${spanState.spinner.text} (${elapsed}ms)`);
|
|
1676
|
+
}
|
|
1677
|
+
activeSpans.delete(event.spanId);
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1280
1683
|
// src/commands/db-init.ts
|
|
1684
|
+
function mapDbInitFailure(failure) {
|
|
1685
|
+
if (failure.code === "PLANNING_FAILED") {
|
|
1686
|
+
return errorMigrationPlanningFailed({ conflicts: failure.conflicts ?? [] });
|
|
1687
|
+
}
|
|
1688
|
+
if (failure.code === "MARKER_ORIGIN_MISMATCH") {
|
|
1689
|
+
const mismatchParts = [];
|
|
1690
|
+
if (failure.marker?.coreHash !== failure.destination?.coreHash && failure.marker?.coreHash && failure.destination?.coreHash) {
|
|
1691
|
+
mismatchParts.push(
|
|
1692
|
+
`coreHash (marker: ${failure.marker.coreHash}, destination: ${failure.destination.coreHash})`
|
|
1693
|
+
);
|
|
1694
|
+
}
|
|
1695
|
+
if (failure.marker?.profileHash !== failure.destination?.profileHash && failure.marker?.profileHash && failure.destination?.profileHash) {
|
|
1696
|
+
mismatchParts.push(
|
|
1697
|
+
`profileHash (marker: ${failure.marker.profileHash}, destination: ${failure.destination.profileHash})`
|
|
1698
|
+
);
|
|
1699
|
+
}
|
|
1700
|
+
return errorRuntime(
|
|
1701
|
+
`Existing contract marker does not match plan destination.${mismatchParts.length > 0 ? ` Mismatch in ${mismatchParts.join(" and ")}.` : ""}`,
|
|
1702
|
+
{
|
|
1703
|
+
why: "Database has an existing contract marker that does not match the target contract",
|
|
1704
|
+
fix: "If bootstrapping, drop/reset the database then re-run `prisma-next db init`; otherwise reconcile schema/marker using your migration workflow",
|
|
1705
|
+
meta: {
|
|
1706
|
+
code: "MARKER_ORIGIN_MISMATCH",
|
|
1707
|
+
...failure.marker?.coreHash ? { markerCoreHash: failure.marker.coreHash } : {},
|
|
1708
|
+
...failure.destination?.coreHash ? { destinationCoreHash: failure.destination.coreHash } : {},
|
|
1709
|
+
...failure.marker?.profileHash ? { markerProfileHash: failure.marker.profileHash } : {},
|
|
1710
|
+
...failure.destination?.profileHash ? { destinationProfileHash: failure.destination.profileHash } : {}
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
);
|
|
1714
|
+
}
|
|
1715
|
+
if (failure.code === "RUNNER_FAILED") {
|
|
1716
|
+
return errorRuntime(failure.summary, {
|
|
1717
|
+
why: failure.why ?? "Migration runner failed",
|
|
1718
|
+
fix: "Fix the schema mismatch (db init is additive-only), or drop/reset the database and re-run `prisma-next db init`",
|
|
1719
|
+
meta: {
|
|
1720
|
+
code: "RUNNER_FAILED",
|
|
1721
|
+
...failure.meta ?? {}
|
|
1722
|
+
}
|
|
1723
|
+
});
|
|
1724
|
+
}
|
|
1725
|
+
const exhaustive = failure.code;
|
|
1726
|
+
throw new Error(`Unhandled DbInitFailure code: ${exhaustive}`);
|
|
1727
|
+
}
|
|
1728
|
+
async function executeDbInitCommand(options, flags, startTime) {
|
|
1729
|
+
const config = await loadConfig(options.config);
|
|
1730
|
+
const configPath = options.config ? relative3(process.cwd(), resolve3(options.config)) : "prisma-next.config.ts";
|
|
1731
|
+
const contractPathAbsolute = config.contract?.output ? resolve3(config.contract.output) : resolve3("src/prisma/contract.json");
|
|
1732
|
+
const contractPath = relative3(process.cwd(), contractPathAbsolute);
|
|
1733
|
+
if (flags.json !== "object" && !flags.quiet) {
|
|
1734
|
+
const details = [
|
|
1735
|
+
{ label: "config", value: configPath },
|
|
1736
|
+
{ label: "contract", value: contractPath }
|
|
1737
|
+
];
|
|
1738
|
+
if (options.db) {
|
|
1739
|
+
details.push({ label: "database", value: options.db });
|
|
1740
|
+
}
|
|
1741
|
+
if (options.plan) {
|
|
1742
|
+
details.push({ label: "mode", value: "plan (dry run)" });
|
|
1743
|
+
}
|
|
1744
|
+
const header = formatStyledHeader({
|
|
1745
|
+
command: "db init",
|
|
1746
|
+
description: "Bootstrap a database to match the current contract",
|
|
1747
|
+
url: "https://pris.ly/db-init",
|
|
1748
|
+
details,
|
|
1749
|
+
flags
|
|
1750
|
+
});
|
|
1751
|
+
console.log(header);
|
|
1752
|
+
}
|
|
1753
|
+
let contractJsonContent;
|
|
1754
|
+
try {
|
|
1755
|
+
contractJsonContent = await readFile(contractPathAbsolute, "utf-8");
|
|
1756
|
+
} catch (error) {
|
|
1757
|
+
if (error instanceof Error && error.code === "ENOENT") {
|
|
1758
|
+
return notOk3(
|
|
1759
|
+
errorFileNotFound(contractPathAbsolute, {
|
|
1760
|
+
why: `Contract file not found at ${contractPathAbsolute}`,
|
|
1761
|
+
fix: `Run \`prisma-next contract emit\` to generate ${contractPath}, or update \`config.contract.output\` in ${configPath}`
|
|
1762
|
+
})
|
|
1763
|
+
);
|
|
1764
|
+
}
|
|
1765
|
+
return notOk3(
|
|
1766
|
+
errorUnexpected2(error instanceof Error ? error.message : String(error), {
|
|
1767
|
+
why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`
|
|
1768
|
+
})
|
|
1769
|
+
);
|
|
1770
|
+
}
|
|
1771
|
+
let contractJson;
|
|
1772
|
+
try {
|
|
1773
|
+
contractJson = JSON.parse(contractJsonContent);
|
|
1774
|
+
} catch (error) {
|
|
1775
|
+
return notOk3(
|
|
1776
|
+
errorContractValidationFailed(
|
|
1777
|
+
`Contract JSON is invalid: ${error instanceof Error ? error.message : String(error)}`,
|
|
1778
|
+
{ where: { path: contractPathAbsolute } }
|
|
1779
|
+
)
|
|
1780
|
+
);
|
|
1781
|
+
}
|
|
1782
|
+
const dbConnection = options.db ?? config.db?.connection;
|
|
1783
|
+
if (!dbConnection) {
|
|
1784
|
+
return notOk3(
|
|
1785
|
+
errorDatabaseConnectionRequired({
|
|
1786
|
+
why: `Database connection is required for db init (set db.connection in ${configPath}, or pass --db <url>)`
|
|
1787
|
+
})
|
|
1788
|
+
);
|
|
1789
|
+
}
|
|
1790
|
+
if (!config.driver) {
|
|
1791
|
+
return notOk3(errorDriverRequired({ why: "Config.driver is required for db init" }));
|
|
1792
|
+
}
|
|
1793
|
+
if (!config.target.migrations) {
|
|
1794
|
+
return notOk3(
|
|
1795
|
+
errorTargetMigrationNotSupported({
|
|
1796
|
+
why: `Target "${config.target.id}" does not support migrations`
|
|
1797
|
+
})
|
|
1798
|
+
);
|
|
1799
|
+
}
|
|
1800
|
+
const client = createControlClient({
|
|
1801
|
+
family: config.family,
|
|
1802
|
+
target: config.target,
|
|
1803
|
+
adapter: config.adapter,
|
|
1804
|
+
driver: config.driver,
|
|
1805
|
+
extensionPacks: config.extensionPacks ?? []
|
|
1806
|
+
});
|
|
1807
|
+
const onProgress = createProgressAdapter({ flags });
|
|
1808
|
+
try {
|
|
1809
|
+
const result = await client.dbInit({
|
|
1810
|
+
contractIR: contractJson,
|
|
1811
|
+
mode: options.plan ? "plan" : "apply",
|
|
1812
|
+
connection: dbConnection,
|
|
1813
|
+
onProgress
|
|
1814
|
+
});
|
|
1815
|
+
if (!result.ok) {
|
|
1816
|
+
return notOk3(mapDbInitFailure(result.failure));
|
|
1817
|
+
}
|
|
1818
|
+
const profileHash = result.value.marker?.profileHash;
|
|
1819
|
+
const dbInitResult = {
|
|
1820
|
+
ok: true,
|
|
1821
|
+
mode: result.value.mode,
|
|
1822
|
+
plan: {
|
|
1823
|
+
targetId: config.target.targetId,
|
|
1824
|
+
destination: {
|
|
1825
|
+
coreHash: result.value.marker?.coreHash ?? "",
|
|
1826
|
+
...profileHash ? { profileHash } : {}
|
|
1827
|
+
},
|
|
1828
|
+
operations: result.value.plan.operations.map((op) => ({
|
|
1829
|
+
id: op.id,
|
|
1830
|
+
label: op.label,
|
|
1831
|
+
operationClass: op.operationClass
|
|
1832
|
+
}))
|
|
1833
|
+
},
|
|
1834
|
+
...result.value.execution ? {
|
|
1835
|
+
execution: {
|
|
1836
|
+
operationsPlanned: result.value.execution.operationsPlanned,
|
|
1837
|
+
operationsExecuted: result.value.execution.operationsExecuted
|
|
1838
|
+
}
|
|
1839
|
+
} : {},
|
|
1840
|
+
...result.value.marker ? {
|
|
1841
|
+
marker: {
|
|
1842
|
+
coreHash: result.value.marker.coreHash,
|
|
1843
|
+
...result.value.marker.profileHash ? { profileHash: result.value.marker.profileHash } : {}
|
|
1844
|
+
}
|
|
1845
|
+
} : {},
|
|
1846
|
+
summary: result.value.summary,
|
|
1847
|
+
timings: { total: Date.now() - startTime }
|
|
1848
|
+
};
|
|
1849
|
+
return ok3(dbInitResult);
|
|
1850
|
+
} catch (error) {
|
|
1851
|
+
if (CliStructuredError.is(error)) {
|
|
1852
|
+
return notOk3(error);
|
|
1853
|
+
}
|
|
1854
|
+
return notOk3(
|
|
1855
|
+
errorUnexpected2(error instanceof Error ? error.message : String(error), {
|
|
1856
|
+
why: `Unexpected error during db init: ${error instanceof Error ? error.message : String(error)}`
|
|
1857
|
+
})
|
|
1858
|
+
);
|
|
1859
|
+
} finally {
|
|
1860
|
+
await client.close();
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1281
1863
|
function createDbInitCommand() {
|
|
1282
1864
|
const command = new Command2("init");
|
|
1283
1865
|
setCommandDescriptions(
|
|
@@ -1293,275 +1875,18 @@ function createDbInitCommand() {
|
|
|
1293
1875
|
}).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)", 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) => {
|
|
1294
1876
|
const flags = parseGlobalFlags(options);
|
|
1295
1877
|
const startTime = Date.now();
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1878
|
+
if (flags.json === "ndjson") {
|
|
1879
|
+
const result2 = notOk3(
|
|
1880
|
+
errorJsonFormatNotSupported({
|
|
1299
1881
|
command: "db init",
|
|
1300
1882
|
format: "ndjson",
|
|
1301
1883
|
supportedFormats: ["object"]
|
|
1302
|
-
})
|
|
1303
|
-
|
|
1304
|
-
const
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
if (flags.json !== "object" && !flags.quiet) {
|
|
1309
|
-
const details = [
|
|
1310
|
-
{ label: "config", value: configPath },
|
|
1311
|
-
{ label: "contract", value: contractPath }
|
|
1312
|
-
];
|
|
1313
|
-
if (options.db) {
|
|
1314
|
-
details.push({ label: "database", value: options.db });
|
|
1315
|
-
}
|
|
1316
|
-
if (options.plan) {
|
|
1317
|
-
details.push({ label: "mode", value: "plan (dry run)" });
|
|
1318
|
-
}
|
|
1319
|
-
const header = formatStyledHeader({
|
|
1320
|
-
command: "db init",
|
|
1321
|
-
description: "Bootstrap a database to match the current contract",
|
|
1322
|
-
url: "https://pris.ly/db-init",
|
|
1323
|
-
details,
|
|
1324
|
-
flags
|
|
1325
|
-
});
|
|
1326
|
-
console.log(header);
|
|
1327
|
-
}
|
|
1328
|
-
let contractJsonContent;
|
|
1329
|
-
try {
|
|
1330
|
-
contractJsonContent = await readFile(contractPathAbsolute, "utf-8");
|
|
1331
|
-
} catch (error) {
|
|
1332
|
-
if (error instanceof Error && error.code === "ENOENT") {
|
|
1333
|
-
throw errorFileNotFound(contractPathAbsolute, {
|
|
1334
|
-
why: `Contract file not found at ${contractPathAbsolute}`,
|
|
1335
|
-
fix: `Run \`prisma-next contract emit\` to generate ${contractPath}, or update \`config.contract.output\` in ${configPath}`
|
|
1336
|
-
});
|
|
1337
|
-
}
|
|
1338
|
-
throw errorUnexpected2(error instanceof Error ? error.message : String(error), {
|
|
1339
|
-
why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`
|
|
1340
|
-
});
|
|
1341
|
-
}
|
|
1342
|
-
let contractJson;
|
|
1343
|
-
try {
|
|
1344
|
-
contractJson = JSON.parse(contractJsonContent);
|
|
1345
|
-
} catch (error) {
|
|
1346
|
-
throw errorContractValidationFailed(
|
|
1347
|
-
`Contract JSON is invalid: ${error instanceof Error ? error.message : String(error)}`,
|
|
1348
|
-
{ where: { path: contractPathAbsolute } }
|
|
1349
|
-
);
|
|
1350
|
-
}
|
|
1351
|
-
const dbConnection = options.db ?? config.db?.connection;
|
|
1352
|
-
if (!dbConnection) {
|
|
1353
|
-
throw errorDatabaseConnectionRequired({
|
|
1354
|
-
why: `Database connection is required for db init (set db.connection in ${configPath}, or pass --db <url>)`
|
|
1355
|
-
});
|
|
1356
|
-
}
|
|
1357
|
-
if (!config.driver) {
|
|
1358
|
-
throw errorDriverRequired({ why: "Config.driver is required for db init" });
|
|
1359
|
-
}
|
|
1360
|
-
const driverDescriptor = config.driver;
|
|
1361
|
-
if (!config.target.migrations) {
|
|
1362
|
-
throw errorTargetMigrationNotSupported({
|
|
1363
|
-
why: `Target "${config.target.id}" does not support migrations`
|
|
1364
|
-
});
|
|
1365
|
-
}
|
|
1366
|
-
const migrations = config.target.migrations;
|
|
1367
|
-
let driver;
|
|
1368
|
-
try {
|
|
1369
|
-
driver = await withSpinner(() => driverDescriptor.create(dbConnection), {
|
|
1370
|
-
message: "Connecting to database...",
|
|
1371
|
-
flags
|
|
1372
|
-
});
|
|
1373
|
-
} catch (error) {
|
|
1374
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1375
|
-
const code = error.code;
|
|
1376
|
-
const redacted = typeof dbConnection === "string" ? redactDatabaseUrl(dbConnection) : void 0;
|
|
1377
|
-
throw errorRuntime("Database connection failed", {
|
|
1378
|
-
why: message,
|
|
1379
|
-
fix: "Verify the database connection, ensure the database is reachable, and confirm credentials/permissions",
|
|
1380
|
-
meta: {
|
|
1381
|
-
...typeof code !== "undefined" ? { code } : {},
|
|
1382
|
-
...redacted ?? {}
|
|
1383
|
-
}
|
|
1384
|
-
});
|
|
1385
|
-
}
|
|
1386
|
-
try {
|
|
1387
|
-
const stack = createControlPlaneStack2({
|
|
1388
|
-
target: config.target,
|
|
1389
|
-
adapter: config.adapter,
|
|
1390
|
-
driver: driverDescriptor,
|
|
1391
|
-
extensionPacks: config.extensionPacks
|
|
1392
|
-
});
|
|
1393
|
-
const familyInstance = config.family.create(stack);
|
|
1394
|
-
const rawComponents = [config.target, config.adapter, ...config.extensionPacks ?? []];
|
|
1395
|
-
const frameworkComponents = assertFrameworkComponentsCompatible(
|
|
1396
|
-
config.family.familyId,
|
|
1397
|
-
config.target.targetId,
|
|
1398
|
-
rawComponents
|
|
1399
|
-
);
|
|
1400
|
-
const contractIR = familyInstance.validateContractIR(contractJson);
|
|
1401
|
-
assertContractRequirementsSatisfied({ contract: contractIR, stack });
|
|
1402
|
-
const planner = migrations.createPlanner(familyInstance);
|
|
1403
|
-
const runner = migrations.createRunner(familyInstance);
|
|
1404
|
-
const schemaIR = await withSpinner(() => familyInstance.introspect({ driver }), {
|
|
1405
|
-
message: "Introspecting database schema...",
|
|
1406
|
-
flags
|
|
1407
|
-
});
|
|
1408
|
-
const policy = { allowedOperationClasses: ["additive"] };
|
|
1409
|
-
const plannerResult = await withSpinner(
|
|
1410
|
-
async () => planner.plan({
|
|
1411
|
-
contract: contractIR,
|
|
1412
|
-
schema: schemaIR,
|
|
1413
|
-
policy,
|
|
1414
|
-
frameworkComponents
|
|
1415
|
-
}),
|
|
1416
|
-
{
|
|
1417
|
-
message: "Planning migration...",
|
|
1418
|
-
flags
|
|
1419
|
-
}
|
|
1420
|
-
);
|
|
1421
|
-
if (plannerResult.kind === "failure") {
|
|
1422
|
-
throw errorMigrationPlanningFailed({ conflicts: plannerResult.conflicts });
|
|
1423
|
-
}
|
|
1424
|
-
const migrationPlan = plannerResult.plan;
|
|
1425
|
-
const existingMarker = await familyInstance.readMarker({ driver });
|
|
1426
|
-
if (existingMarker) {
|
|
1427
|
-
const markerMatchesDestination = existingMarker.coreHash === migrationPlan.destination.coreHash && (!migrationPlan.destination.profileHash || existingMarker.profileHash === migrationPlan.destination.profileHash);
|
|
1428
|
-
if (markerMatchesDestination) {
|
|
1429
|
-
const dbInitResult2 = {
|
|
1430
|
-
ok: true,
|
|
1431
|
-
mode: options.plan ? "plan" : "apply",
|
|
1432
|
-
plan: {
|
|
1433
|
-
targetId: migrationPlan.targetId,
|
|
1434
|
-
destination: migrationPlan.destination,
|
|
1435
|
-
operations: []
|
|
1436
|
-
},
|
|
1437
|
-
...options.plan ? {} : {
|
|
1438
|
-
execution: { operationsPlanned: 0, operationsExecuted: 0 },
|
|
1439
|
-
marker: {
|
|
1440
|
-
coreHash: existingMarker.coreHash,
|
|
1441
|
-
profileHash: existingMarker.profileHash
|
|
1442
|
-
}
|
|
1443
|
-
},
|
|
1444
|
-
summary: "Database already at target contract state",
|
|
1445
|
-
timings: { total: Date.now() - startTime }
|
|
1446
|
-
};
|
|
1447
|
-
return dbInitResult2;
|
|
1448
|
-
}
|
|
1449
|
-
const coreHashMismatch = existingMarker.coreHash !== migrationPlan.destination.coreHash;
|
|
1450
|
-
const profileHashMismatch = migrationPlan.destination.profileHash && existingMarker.profileHash !== migrationPlan.destination.profileHash;
|
|
1451
|
-
const mismatchParts = [];
|
|
1452
|
-
if (coreHashMismatch) {
|
|
1453
|
-
mismatchParts.push(
|
|
1454
|
-
`coreHash (marker: ${existingMarker.coreHash}, destination: ${migrationPlan.destination.coreHash})`
|
|
1455
|
-
);
|
|
1456
|
-
}
|
|
1457
|
-
if (profileHashMismatch) {
|
|
1458
|
-
mismatchParts.push(
|
|
1459
|
-
`profileHash (marker: ${existingMarker.profileHash}, destination: ${migrationPlan.destination.profileHash})`
|
|
1460
|
-
);
|
|
1461
|
-
}
|
|
1462
|
-
throw errorRuntime(
|
|
1463
|
-
`Existing contract marker does not match plan destination. Mismatch in ${mismatchParts.join(" and ")}.`,
|
|
1464
|
-
{
|
|
1465
|
-
why: "Database has an existing contract marker that does not match the target contract",
|
|
1466
|
-
fix: "If bootstrapping, drop/reset the database then re-run `prisma-next db init`; otherwise reconcile schema/marker using your migration workflow",
|
|
1467
|
-
meta: {
|
|
1468
|
-
code: "MARKER_ORIGIN_MISMATCH",
|
|
1469
|
-
markerCoreHash: existingMarker.coreHash,
|
|
1470
|
-
destinationCoreHash: migrationPlan.destination.coreHash,
|
|
1471
|
-
...existingMarker.profileHash ? { markerProfileHash: existingMarker.profileHash } : {},
|
|
1472
|
-
...migrationPlan.destination.profileHash ? { destinationProfileHash: migrationPlan.destination.profileHash } : {}
|
|
1473
|
-
}
|
|
1474
|
-
}
|
|
1475
|
-
);
|
|
1476
|
-
}
|
|
1477
|
-
if (options.plan) {
|
|
1478
|
-
const dbInitResult2 = {
|
|
1479
|
-
ok: true,
|
|
1480
|
-
mode: "plan",
|
|
1481
|
-
plan: {
|
|
1482
|
-
targetId: migrationPlan.targetId,
|
|
1483
|
-
destination: migrationPlan.destination,
|
|
1484
|
-
operations: migrationPlan.operations.map((op) => ({
|
|
1485
|
-
id: op.id,
|
|
1486
|
-
label: op.label,
|
|
1487
|
-
operationClass: op.operationClass
|
|
1488
|
-
}))
|
|
1489
|
-
},
|
|
1490
|
-
summary: `Planned ${migrationPlan.operations.length} operation(s)`,
|
|
1491
|
-
timings: { total: Date.now() - startTime }
|
|
1492
|
-
};
|
|
1493
|
-
return dbInitResult2;
|
|
1494
|
-
}
|
|
1495
|
-
if (!flags.quiet && flags.json !== "object") {
|
|
1496
|
-
console.log("Applying migration plan and verifying schema...");
|
|
1497
|
-
}
|
|
1498
|
-
const callbacks = {
|
|
1499
|
-
onOperationStart: (op) => {
|
|
1500
|
-
if (!flags.quiet && flags.json !== "object") {
|
|
1501
|
-
console.log(` \u2192 ${op.label}...`);
|
|
1502
|
-
}
|
|
1503
|
-
},
|
|
1504
|
-
onOperationComplete: (_op) => {
|
|
1505
|
-
}
|
|
1506
|
-
};
|
|
1507
|
-
const runnerResult = await runner.execute({
|
|
1508
|
-
plan: migrationPlan,
|
|
1509
|
-
driver,
|
|
1510
|
-
destinationContract: contractIR,
|
|
1511
|
-
policy,
|
|
1512
|
-
callbacks,
|
|
1513
|
-
// db init plans and applies back-to-back from a fresh introspection, so per-operation
|
|
1514
|
-
// pre/postchecks and the idempotency probe are usually redundant overhead. We still
|
|
1515
|
-
// enforce marker/origin compatibility and a full schema verification after apply.
|
|
1516
|
-
executionChecks: {
|
|
1517
|
-
prechecks: false,
|
|
1518
|
-
postchecks: false,
|
|
1519
|
-
idempotencyChecks: false
|
|
1520
|
-
},
|
|
1521
|
-
frameworkComponents
|
|
1522
|
-
});
|
|
1523
|
-
if (!runnerResult.ok) {
|
|
1524
|
-
const meta = {
|
|
1525
|
-
code: runnerResult.failure.code,
|
|
1526
|
-
...runnerResult.failure.meta ?? {}
|
|
1527
|
-
};
|
|
1528
|
-
const sqlState = typeof meta["sqlState"] === "string" ? meta["sqlState"] : void 0;
|
|
1529
|
-
const fix = sqlState === "42501" ? "Grant the database user sufficient privileges (insufficient_privilege), or run db init as a more privileged role" : runnerResult.failure.code === "SCHEMA_VERIFY_FAILED" ? "Fix the schema mismatch (db init is additive-only), or drop/reset the database and re-run `prisma-next db init`" : void 0;
|
|
1530
|
-
throw errorRuntime(runnerResult.failure.summary, {
|
|
1531
|
-
why: runnerResult.failure.why ?? `Migration runner failed: ${runnerResult.failure.code}`,
|
|
1532
|
-
...fix ? { fix } : {},
|
|
1533
|
-
meta
|
|
1534
|
-
});
|
|
1535
|
-
}
|
|
1536
|
-
const execution = runnerResult.value;
|
|
1537
|
-
const dbInitResult = {
|
|
1538
|
-
ok: true,
|
|
1539
|
-
mode: "apply",
|
|
1540
|
-
plan: {
|
|
1541
|
-
targetId: migrationPlan.targetId,
|
|
1542
|
-
destination: migrationPlan.destination,
|
|
1543
|
-
operations: migrationPlan.operations.map((op) => ({
|
|
1544
|
-
id: op.id,
|
|
1545
|
-
label: op.label,
|
|
1546
|
-
operationClass: op.operationClass
|
|
1547
|
-
}))
|
|
1548
|
-
},
|
|
1549
|
-
execution: {
|
|
1550
|
-
operationsPlanned: execution.operationsPlanned,
|
|
1551
|
-
operationsExecuted: execution.operationsExecuted
|
|
1552
|
-
},
|
|
1553
|
-
marker: migrationPlan.destination.profileHash ? {
|
|
1554
|
-
coreHash: migrationPlan.destination.coreHash,
|
|
1555
|
-
profileHash: migrationPlan.destination.profileHash
|
|
1556
|
-
} : { coreHash: migrationPlan.destination.coreHash },
|
|
1557
|
-
summary: `Applied ${execution.operationsExecuted} operation(s), marker written`,
|
|
1558
|
-
timings: { total: Date.now() - startTime }
|
|
1559
|
-
};
|
|
1560
|
-
return dbInitResult;
|
|
1561
|
-
} finally {
|
|
1562
|
-
await driver.close();
|
|
1563
|
-
}
|
|
1564
|
-
});
|
|
1884
|
+
})
|
|
1885
|
+
);
|
|
1886
|
+
const exitCode2 = handleResult(result2, flags);
|
|
1887
|
+
process.exit(exitCode2);
|
|
1888
|
+
}
|
|
1889
|
+
const result = await executeDbInitCommand(options, flags, startTime);
|
|
1565
1890
|
const exitCode = handleResult(result, flags, (dbInitResult) => {
|
|
1566
1891
|
if (flags.json === "object") {
|
|
1567
1892
|
console.log(formatDbInitJson(dbInitResult));
|