@prisma-next/cli 0.3.0-dev.33 → 0.3.0-dev.36
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 +14 -13
- package/dist/cli.d.mts +1 -0
- package/dist/cli.js +1 -2910
- package/dist/cli.mjs +169 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/client-Lm9Q6aQM.mjs +694 -0
- package/dist/client-Lm9Q6aQM.mjs.map +1 -0
- package/dist/commands/contract-emit.d.mts +7 -0
- package/dist/commands/contract-emit.d.mts.map +1 -0
- package/dist/commands/contract-emit.mjs +140 -0
- package/dist/commands/contract-emit.mjs.map +1 -0
- package/dist/commands/db-init.d.mts +7 -0
- package/dist/commands/db-init.d.mts.map +1 -0
- package/dist/commands/db-init.mjs +179 -0
- package/dist/commands/db-init.mjs.map +1 -0
- package/dist/commands/db-introspect.d.mts +7 -0
- package/dist/commands/db-introspect.d.mts.map +1 -0
- package/dist/commands/db-introspect.mjs +120 -0
- package/dist/commands/db-introspect.mjs.map +1 -0
- package/dist/commands/db-schema-verify.d.mts +7 -0
- package/dist/commands/db-schema-verify.d.mts.map +1 -0
- package/dist/commands/db-schema-verify.mjs +116 -0
- package/dist/commands/db-schema-verify.mjs.map +1 -0
- package/dist/commands/db-sign.d.mts +7 -0
- package/dist/commands/db-sign.d.mts.map +1 -0
- package/dist/commands/db-sign.mjs +138 -0
- package/dist/commands/db-sign.mjs.map +1 -0
- package/dist/commands/db-verify.d.mts +7 -0
- package/dist/commands/db-verify.d.mts.map +1 -0
- package/dist/commands/db-verify.mjs +129 -0
- package/dist/commands/db-verify.mjs.map +1 -0
- package/dist/config-loader-CnnWuluc.mjs +42 -0
- package/dist/config-loader-CnnWuluc.mjs.map +1 -0
- package/dist/{config-loader.d.ts → config-loader.d.mts} +8 -3
- package/dist/config-loader.d.mts.map +1 -0
- package/dist/config-loader.mjs +3 -0
- package/dist/exports/config-types.d.mts +2 -0
- package/dist/exports/config-types.mjs +3 -0
- package/dist/exports/control-api.d.mts +451 -0
- package/dist/exports/control-api.d.mts.map +1 -0
- package/dist/exports/control-api.mjs +59 -0
- package/dist/exports/control-api.mjs.map +1 -0
- package/dist/{load-ts-contract.d.ts → exports/index.d.mts} +10 -5
- package/dist/exports/index.d.mts.map +1 -0
- package/dist/exports/index.mjs +132 -0
- package/dist/exports/index.mjs.map +1 -0
- package/dist/result-handler-BZPY7HX4.mjs +1029 -0
- package/dist/result-handler-BZPY7HX4.mjs.map +1 -0
- package/package.json +41 -34
- package/src/commands/contract-emit.ts +13 -5
- package/src/commands/db-init.ts +13 -18
- package/src/commands/db-verify.ts +3 -2
- package/src/control-api/client.ts +4 -2
- package/src/control-api/operations/contract-emit.ts +103 -0
- package/src/control-api/operations/db-init.ts +6 -6
- package/src/control-api/types.ts +44 -12
- package/src/exports/control-api.ts +5 -0
- package/src/load-ts-contract.ts +16 -11
- package/src/utils/output.ts +16 -10
- package/dist/chunk-AGOTG4L3.js +0 -965
- package/dist/chunk-AGOTG4L3.js.map +0 -1
- package/dist/chunk-HLLI4YL7.js +0 -180
- package/dist/chunk-HLLI4YL7.js.map +0 -1
- package/dist/chunk-HWYQOCAJ.js +0 -47
- package/dist/chunk-HWYQOCAJ.js.map +0 -1
- package/dist/chunk-VG2R7DGF.js +0 -735
- package/dist/chunk-VG2R7DGF.js.map +0 -1
- package/dist/cli.d.ts +0 -2
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/commands/contract-emit.d.ts +0 -3
- package/dist/commands/contract-emit.d.ts.map +0 -1
- package/dist/commands/contract-emit.js +0 -10
- package/dist/commands/contract-emit.js.map +0 -1
- package/dist/commands/db-init.d.ts +0 -3
- package/dist/commands/db-init.d.ts.map +0 -1
- package/dist/commands/db-init.js +0 -257
- package/dist/commands/db-init.js.map +0 -1
- package/dist/commands/db-introspect.d.ts +0 -3
- package/dist/commands/db-introspect.d.ts.map +0 -1
- package/dist/commands/db-introspect.js +0 -155
- package/dist/commands/db-introspect.js.map +0 -1
- package/dist/commands/db-schema-verify.d.ts +0 -3
- package/dist/commands/db-schema-verify.d.ts.map +0 -1
- package/dist/commands/db-schema-verify.js +0 -171
- package/dist/commands/db-schema-verify.js.map +0 -1
- package/dist/commands/db-sign.d.ts +0 -3
- package/dist/commands/db-sign.d.ts.map +0 -1
- package/dist/commands/db-sign.js +0 -195
- package/dist/commands/db-sign.js.map +0 -1
- package/dist/commands/db-verify.d.ts +0 -3
- package/dist/commands/db-verify.d.ts.map +0 -1
- package/dist/commands/db-verify.js +0 -193
- package/dist/commands/db-verify.js.map +0 -1
- package/dist/config-loader.d.ts.map +0 -1
- package/dist/config-loader.js +0 -7
- package/dist/config-loader.js.map +0 -1
- package/dist/control-api/client.d.ts +0 -13
- package/dist/control-api/client.d.ts.map +0 -1
- package/dist/control-api/operations/db-init.d.ts +0 -29
- package/dist/control-api/operations/db-init.d.ts.map +0 -1
- package/dist/control-api/types.d.ts +0 -387
- package/dist/control-api/types.d.ts.map +0 -1
- package/dist/exports/config-types.d.ts +0 -3
- package/dist/exports/config-types.d.ts.map +0 -1
- package/dist/exports/config-types.js +0 -6
- package/dist/exports/config-types.js.map +0 -1
- package/dist/exports/control-api.d.ts +0 -13
- package/dist/exports/control-api.d.ts.map +0 -1
- package/dist/exports/control-api.js +0 -7
- package/dist/exports/control-api.js.map +0 -1
- package/dist/exports/index.d.ts +0 -4
- package/dist/exports/index.d.ts.map +0 -1
- package/dist/exports/index.js +0 -176
- package/dist/exports/index.js.map +0 -1
- package/dist/load-ts-contract.d.ts.map +0 -1
- package/dist/utils/cli-errors.d.ts +0 -7
- package/dist/utils/cli-errors.d.ts.map +0 -1
- package/dist/utils/command-helpers.d.ts +0 -12
- package/dist/utils/command-helpers.d.ts.map +0 -1
- package/dist/utils/framework-components.d.ts +0 -70
- package/dist/utils/framework-components.d.ts.map +0 -1
- package/dist/utils/global-flags.d.ts +0 -25
- package/dist/utils/global-flags.d.ts.map +0 -1
- package/dist/utils/output.d.ts +0 -142
- package/dist/utils/output.d.ts.map +0 -1
- package/dist/utils/progress-adapter.d.ts +0 -26
- package/dist/utils/progress-adapter.d.ts.map +0 -1
- package/dist/utils/result-handler.d.ts +0 -15
- package/dist/utils/result-handler.d.ts.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,2911 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/cli.ts
|
|
4
|
-
import { Command as Command7 } from "commander";
|
|
5
|
-
|
|
6
|
-
// src/commands/contract-emit.ts
|
|
7
|
-
import { mkdirSync, writeFileSync } from "fs";
|
|
8
|
-
import { dirname as dirname2, relative as relative2, resolve as resolve2 } from "path";
|
|
9
|
-
import { errorContractConfigMissing as errorContractConfigMissing2 } from "@prisma-next/core-control-plane/errors";
|
|
10
|
-
import { notOk as notOk3, ok as ok3 } from "@prisma-next/utils/result";
|
|
11
|
-
import { Command } from "commander";
|
|
12
|
-
|
|
13
|
-
// src/config-loader.ts
|
|
14
|
-
import { dirname, resolve } from "path";
|
|
15
|
-
import { validateConfig } from "@prisma-next/core-control-plane/config-validation";
|
|
16
|
-
import { errorConfigFileNotFound, errorUnexpected } from "@prisma-next/core-control-plane/errors";
|
|
17
|
-
import { loadConfig as loadConfigC12 } from "c12";
|
|
18
|
-
async function loadConfig(configPath) {
|
|
19
|
-
try {
|
|
20
|
-
const cwd = process.cwd();
|
|
21
|
-
const resolvedConfigPath = configPath ? resolve(cwd, configPath) : void 0;
|
|
22
|
-
const configCwd = resolvedConfigPath ? dirname(resolvedConfigPath) : cwd;
|
|
23
|
-
const result = await loadConfigC12({
|
|
24
|
-
name: "prisma-next",
|
|
25
|
-
...resolvedConfigPath ? { configFile: resolvedConfigPath } : {},
|
|
26
|
-
cwd: configCwd
|
|
27
|
-
});
|
|
28
|
-
if (resolvedConfigPath && result.configFile !== resolvedConfigPath) {
|
|
29
|
-
throw errorConfigFileNotFound(resolvedConfigPath);
|
|
30
|
-
}
|
|
31
|
-
if (!result.config || Object.keys(result.config).length === 0) {
|
|
32
|
-
const displayPath = result.configFile || resolvedConfigPath || configPath;
|
|
33
|
-
throw errorConfigFileNotFound(displayPath);
|
|
34
|
-
}
|
|
35
|
-
validateConfig(result.config);
|
|
36
|
-
return result.config;
|
|
37
|
-
} catch (error) {
|
|
38
|
-
if (error instanceof Error && "code" in error && typeof error.code === "string") {
|
|
39
|
-
throw error;
|
|
40
|
-
}
|
|
41
|
-
if (error instanceof Error) {
|
|
42
|
-
if (error.message.includes("not found") || error.message.includes("Cannot find") || error.message.includes("ENOENT")) {
|
|
43
|
-
const displayPath = configPath ? resolve(process.cwd(), configPath) : void 0;
|
|
44
|
-
throw errorConfigFileNotFound(displayPath, {
|
|
45
|
-
why: error.message
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
throw errorUnexpected(error.message, {
|
|
49
|
-
why: `Failed to load config: ${error.message}`
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
throw errorUnexpected(String(error));
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// src/control-api/client.ts
|
|
57
|
-
import { createControlPlaneStack } from "@prisma-next/core-control-plane/stack";
|
|
58
|
-
import { notOk as notOk2, ok as ok2 } from "@prisma-next/utils/result";
|
|
59
|
-
|
|
60
|
-
// src/utils/framework-components.ts
|
|
61
|
-
import {
|
|
62
|
-
checkContractComponentRequirements
|
|
63
|
-
} from "@prisma-next/contract/framework-components";
|
|
64
|
-
|
|
65
|
-
// src/utils/cli-errors.ts
|
|
66
|
-
import {
|
|
67
|
-
CliStructuredError,
|
|
68
|
-
errorConfigFileNotFound as errorConfigFileNotFound2,
|
|
69
|
-
errorConfigValidation,
|
|
70
|
-
errorContractConfigMissing,
|
|
71
|
-
errorContractMissingExtensionPacks,
|
|
72
|
-
errorContractValidationFailed,
|
|
73
|
-
errorDatabaseConnectionRequired,
|
|
74
|
-
errorDriverRequired,
|
|
75
|
-
errorFamilyReadMarkerSqlRequired,
|
|
76
|
-
errorFileNotFound,
|
|
77
|
-
errorHashMismatch,
|
|
78
|
-
errorJsonFormatNotSupported,
|
|
79
|
-
errorMarkerMissing,
|
|
80
|
-
errorMigrationPlanningFailed,
|
|
81
|
-
errorQueryRunnerFactoryRequired,
|
|
82
|
-
errorRuntime,
|
|
83
|
-
errorTargetMigrationNotSupported,
|
|
84
|
-
errorTargetMismatch,
|
|
85
|
-
errorUnexpected as errorUnexpected2
|
|
86
|
-
} from "@prisma-next/core-control-plane/errors";
|
|
87
|
-
|
|
88
|
-
// src/utils/framework-components.ts
|
|
89
|
-
function assertFrameworkComponentsCompatible(expectedFamilyId, expectedTargetId, frameworkComponents) {
|
|
90
|
-
for (let i = 0; i < frameworkComponents.length; i++) {
|
|
91
|
-
const component = frameworkComponents[i];
|
|
92
|
-
if (typeof component !== "object" || component === null) {
|
|
93
|
-
throw errorConfigValidation("frameworkComponents[]", {
|
|
94
|
-
why: `Framework component at index ${i} must be an object`
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
const record = component;
|
|
98
|
-
if (!Object.hasOwn(record, "kind")) {
|
|
99
|
-
throw errorConfigValidation("frameworkComponents[].kind", {
|
|
100
|
-
why: `Framework component at index ${i} must have 'kind' property`
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
const kind = record["kind"];
|
|
104
|
-
if (kind !== "target" && kind !== "adapter" && kind !== "extension" && kind !== "driver") {
|
|
105
|
-
throw errorConfigValidation("frameworkComponents[].kind", {
|
|
106
|
-
why: `Framework component at index ${i} has invalid kind '${String(kind)}' (must be 'target', 'adapter', 'extension', or 'driver')`
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
if (!Object.hasOwn(record, "familyId")) {
|
|
110
|
-
throw errorConfigValidation("frameworkComponents[].familyId", {
|
|
111
|
-
why: `Framework component at index ${i} (kind: ${String(kind)}) must have 'familyId' property`
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
const familyId = record["familyId"];
|
|
115
|
-
if (familyId !== expectedFamilyId) {
|
|
116
|
-
throw errorConfigValidation("frameworkComponents[].familyId", {
|
|
117
|
-
why: `Framework component at index ${i} (kind: ${String(kind)}) has familyId '${String(familyId)}' but expected '${expectedFamilyId}'`
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
if (!Object.hasOwn(record, "targetId")) {
|
|
121
|
-
throw errorConfigValidation("frameworkComponents[].targetId", {
|
|
122
|
-
why: `Framework component at index ${i} (kind: ${String(kind)}) must have 'targetId' property`
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
const targetId = record["targetId"];
|
|
126
|
-
if (targetId !== expectedTargetId) {
|
|
127
|
-
throw errorConfigValidation("frameworkComponents[].targetId", {
|
|
128
|
-
why: `Framework component at index ${i} (kind: ${String(kind)}) has targetId '${String(targetId)}' but expected '${expectedTargetId}'`
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
return frameworkComponents;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// src/control-api/operations/db-init.ts
|
|
136
|
-
import { notOk, ok } from "@prisma-next/utils/result";
|
|
137
|
-
async function executeDbInit(options) {
|
|
138
|
-
const { driver, familyInstance, contractIR, mode, migrations, frameworkComponents, onProgress } = options;
|
|
139
|
-
const planner = migrations.createPlanner(familyInstance);
|
|
140
|
-
const runner = migrations.createRunner(familyInstance);
|
|
141
|
-
const introspectSpanId = "introspect";
|
|
142
|
-
onProgress?.({
|
|
143
|
-
action: "dbInit",
|
|
144
|
-
kind: "spanStart",
|
|
145
|
-
spanId: introspectSpanId,
|
|
146
|
-
label: "Introspecting database schema"
|
|
147
|
-
});
|
|
148
|
-
const schemaIR = await familyInstance.introspect({ driver });
|
|
149
|
-
onProgress?.({
|
|
150
|
-
action: "dbInit",
|
|
151
|
-
kind: "spanEnd",
|
|
152
|
-
spanId: introspectSpanId,
|
|
153
|
-
outcome: "ok"
|
|
154
|
-
});
|
|
155
|
-
const policy = { allowedOperationClasses: ["additive"] };
|
|
156
|
-
const planSpanId = "plan";
|
|
157
|
-
onProgress?.({
|
|
158
|
-
action: "dbInit",
|
|
159
|
-
kind: "spanStart",
|
|
160
|
-
spanId: planSpanId,
|
|
161
|
-
label: "Planning migration"
|
|
162
|
-
});
|
|
163
|
-
const plannerResult = await planner.plan({
|
|
164
|
-
contract: contractIR,
|
|
165
|
-
schema: schemaIR,
|
|
166
|
-
policy,
|
|
167
|
-
frameworkComponents
|
|
168
|
-
});
|
|
169
|
-
if (plannerResult.kind === "failure") {
|
|
170
|
-
onProgress?.({
|
|
171
|
-
action: "dbInit",
|
|
172
|
-
kind: "spanEnd",
|
|
173
|
-
spanId: planSpanId,
|
|
174
|
-
outcome: "error"
|
|
175
|
-
});
|
|
176
|
-
return notOk({
|
|
177
|
-
code: "PLANNING_FAILED",
|
|
178
|
-
summary: "Migration planning failed due to conflicts",
|
|
179
|
-
conflicts: plannerResult.conflicts,
|
|
180
|
-
why: void 0,
|
|
181
|
-
meta: void 0
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
const migrationPlan = plannerResult.plan;
|
|
185
|
-
onProgress?.({
|
|
186
|
-
action: "dbInit",
|
|
187
|
-
kind: "spanEnd",
|
|
188
|
-
spanId: planSpanId,
|
|
189
|
-
outcome: "ok"
|
|
190
|
-
});
|
|
191
|
-
const checkMarkerSpanId = "checkMarker";
|
|
192
|
-
onProgress?.({
|
|
193
|
-
action: "dbInit",
|
|
194
|
-
kind: "spanStart",
|
|
195
|
-
spanId: checkMarkerSpanId,
|
|
196
|
-
label: "Checking contract marker"
|
|
197
|
-
});
|
|
198
|
-
const existingMarker = await familyInstance.readMarker({ driver });
|
|
199
|
-
if (existingMarker) {
|
|
200
|
-
const markerMatchesDestination = existingMarker.coreHash === migrationPlan.destination.coreHash && (!migrationPlan.destination.profileHash || existingMarker.profileHash === migrationPlan.destination.profileHash);
|
|
201
|
-
if (markerMatchesDestination) {
|
|
202
|
-
onProgress?.({
|
|
203
|
-
action: "dbInit",
|
|
204
|
-
kind: "spanEnd",
|
|
205
|
-
spanId: checkMarkerSpanId,
|
|
206
|
-
outcome: "skipped"
|
|
207
|
-
});
|
|
208
|
-
const result2 = {
|
|
209
|
-
mode,
|
|
210
|
-
plan: { operations: [] },
|
|
211
|
-
...mode === "apply" ? {
|
|
212
|
-
execution: { operationsPlanned: 0, operationsExecuted: 0 },
|
|
213
|
-
marker: {
|
|
214
|
-
coreHash: existingMarker.coreHash,
|
|
215
|
-
profileHash: existingMarker.profileHash
|
|
216
|
-
}
|
|
217
|
-
} : {},
|
|
218
|
-
summary: "Database already at target contract state"
|
|
219
|
-
};
|
|
220
|
-
return ok(result2);
|
|
221
|
-
}
|
|
222
|
-
onProgress?.({
|
|
223
|
-
action: "dbInit",
|
|
224
|
-
kind: "spanEnd",
|
|
225
|
-
spanId: checkMarkerSpanId,
|
|
226
|
-
outcome: "error"
|
|
227
|
-
});
|
|
228
|
-
return notOk({
|
|
229
|
-
code: "MARKER_ORIGIN_MISMATCH",
|
|
230
|
-
summary: "Existing contract marker does not match plan destination",
|
|
231
|
-
marker: {
|
|
232
|
-
coreHash: existingMarker.coreHash,
|
|
233
|
-
profileHash: existingMarker.profileHash
|
|
234
|
-
},
|
|
235
|
-
destination: {
|
|
236
|
-
coreHash: migrationPlan.destination.coreHash,
|
|
237
|
-
profileHash: migrationPlan.destination.profileHash
|
|
238
|
-
},
|
|
239
|
-
why: void 0,
|
|
240
|
-
conflicts: void 0,
|
|
241
|
-
meta: void 0
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
onProgress?.({
|
|
245
|
-
action: "dbInit",
|
|
246
|
-
kind: "spanEnd",
|
|
247
|
-
spanId: checkMarkerSpanId,
|
|
248
|
-
outcome: "ok"
|
|
249
|
-
});
|
|
250
|
-
if (mode === "plan") {
|
|
251
|
-
const result2 = {
|
|
252
|
-
mode: "plan",
|
|
253
|
-
plan: { operations: migrationPlan.operations },
|
|
254
|
-
summary: `Planned ${migrationPlan.operations.length} operation(s)`
|
|
255
|
-
};
|
|
256
|
-
return ok(result2);
|
|
257
|
-
}
|
|
258
|
-
const applySpanId = "apply";
|
|
259
|
-
onProgress?.({
|
|
260
|
-
action: "dbInit",
|
|
261
|
-
kind: "spanStart",
|
|
262
|
-
spanId: applySpanId,
|
|
263
|
-
label: "Applying migration plan"
|
|
264
|
-
});
|
|
265
|
-
const callbacks = onProgress ? {
|
|
266
|
-
onOperationStart: (op) => {
|
|
267
|
-
onProgress({
|
|
268
|
-
action: "dbInit",
|
|
269
|
-
kind: "spanStart",
|
|
270
|
-
spanId: `operation:${op.id}`,
|
|
271
|
-
parentSpanId: applySpanId,
|
|
272
|
-
label: op.label
|
|
273
|
-
});
|
|
274
|
-
},
|
|
275
|
-
onOperationComplete: (op) => {
|
|
276
|
-
onProgress({
|
|
277
|
-
action: "dbInit",
|
|
278
|
-
kind: "spanEnd",
|
|
279
|
-
spanId: `operation:${op.id}`,
|
|
280
|
-
outcome: "ok"
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
} : void 0;
|
|
284
|
-
const runnerResult = await runner.execute({
|
|
285
|
-
plan: migrationPlan,
|
|
286
|
-
driver,
|
|
287
|
-
destinationContract: contractIR,
|
|
288
|
-
policy,
|
|
289
|
-
...callbacks ? { callbacks } : {},
|
|
290
|
-
// db init plans and applies back-to-back from a fresh introspection, so per-operation
|
|
291
|
-
// pre/postchecks and the idempotency probe are usually redundant overhead. We still
|
|
292
|
-
// enforce marker/origin compatibility and a full schema verification after apply.
|
|
293
|
-
executionChecks: {
|
|
294
|
-
prechecks: false,
|
|
295
|
-
postchecks: false,
|
|
296
|
-
idempotencyChecks: false
|
|
297
|
-
},
|
|
298
|
-
frameworkComponents
|
|
299
|
-
});
|
|
300
|
-
if (!runnerResult.ok) {
|
|
301
|
-
onProgress?.({
|
|
302
|
-
action: "dbInit",
|
|
303
|
-
kind: "spanEnd",
|
|
304
|
-
spanId: applySpanId,
|
|
305
|
-
outcome: "error"
|
|
306
|
-
});
|
|
307
|
-
return notOk({
|
|
308
|
-
code: "RUNNER_FAILED",
|
|
309
|
-
summary: runnerResult.failure.summary,
|
|
310
|
-
why: runnerResult.failure.why,
|
|
311
|
-
meta: runnerResult.failure.meta,
|
|
312
|
-
conflicts: void 0
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
const execution = runnerResult.value;
|
|
316
|
-
onProgress?.({
|
|
317
|
-
action: "dbInit",
|
|
318
|
-
kind: "spanEnd",
|
|
319
|
-
spanId: applySpanId,
|
|
320
|
-
outcome: "ok"
|
|
321
|
-
});
|
|
322
|
-
const result = {
|
|
323
|
-
mode: "apply",
|
|
324
|
-
plan: { operations: migrationPlan.operations },
|
|
325
|
-
execution: {
|
|
326
|
-
operationsPlanned: execution.operationsPlanned,
|
|
327
|
-
operationsExecuted: execution.operationsExecuted
|
|
328
|
-
},
|
|
329
|
-
marker: migrationPlan.destination.profileHash ? {
|
|
330
|
-
coreHash: migrationPlan.destination.coreHash,
|
|
331
|
-
profileHash: migrationPlan.destination.profileHash
|
|
332
|
-
} : { coreHash: migrationPlan.destination.coreHash },
|
|
333
|
-
summary: `Applied ${execution.operationsExecuted} operation(s), marker written`
|
|
334
|
-
};
|
|
335
|
-
return ok(result);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// src/control-api/client.ts
|
|
339
|
-
function createControlClient(options) {
|
|
340
|
-
return new ControlClientImpl(options);
|
|
341
|
-
}
|
|
342
|
-
var ControlClientImpl = class {
|
|
343
|
-
options;
|
|
344
|
-
stack = null;
|
|
345
|
-
driver = null;
|
|
346
|
-
familyInstance = null;
|
|
347
|
-
frameworkComponents = null;
|
|
348
|
-
initialized = false;
|
|
349
|
-
defaultConnection;
|
|
350
|
-
constructor(options) {
|
|
351
|
-
this.options = options;
|
|
352
|
-
this.defaultConnection = options.connection;
|
|
353
|
-
}
|
|
354
|
-
init() {
|
|
355
|
-
if (this.initialized) {
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
this.stack = createControlPlaneStack({
|
|
359
|
-
target: this.options.target,
|
|
360
|
-
adapter: this.options.adapter,
|
|
361
|
-
driver: this.options.driver,
|
|
362
|
-
extensionPacks: this.options.extensionPacks
|
|
363
|
-
});
|
|
364
|
-
this.familyInstance = this.options.family.create(this.stack);
|
|
365
|
-
const rawComponents = [
|
|
366
|
-
this.options.target,
|
|
367
|
-
this.options.adapter,
|
|
368
|
-
...this.options.extensionPacks ?? []
|
|
369
|
-
];
|
|
370
|
-
this.frameworkComponents = assertFrameworkComponentsCompatible(
|
|
371
|
-
this.options.family.familyId,
|
|
372
|
-
this.options.target.targetId,
|
|
373
|
-
rawComponents
|
|
374
|
-
);
|
|
375
|
-
this.initialized = true;
|
|
376
|
-
}
|
|
377
|
-
async connect(connection) {
|
|
378
|
-
this.init();
|
|
379
|
-
if (this.driver) {
|
|
380
|
-
throw new Error("Already connected. Call close() before reconnecting.");
|
|
381
|
-
}
|
|
382
|
-
const resolvedConnection = connection ?? this.defaultConnection;
|
|
383
|
-
if (resolvedConnection === void 0) {
|
|
384
|
-
throw new Error(
|
|
385
|
-
"No connection provided. Pass a connection to connect() or provide a default connection when creating the client."
|
|
386
|
-
);
|
|
387
|
-
}
|
|
388
|
-
if (!this.stack?.driver) {
|
|
389
|
-
throw new Error(
|
|
390
|
-
"Driver is not configured. Pass a driver descriptor when creating the control client to enable database operations."
|
|
391
|
-
);
|
|
392
|
-
}
|
|
393
|
-
this.driver = await this.stack?.driver.create(resolvedConnection);
|
|
394
|
-
}
|
|
395
|
-
async close() {
|
|
396
|
-
if (this.driver) {
|
|
397
|
-
await this.driver.close();
|
|
398
|
-
this.driver = null;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
async ensureConnected() {
|
|
402
|
-
this.init();
|
|
403
|
-
if (!this.driver && this.defaultConnection !== void 0) {
|
|
404
|
-
await this.connect(this.defaultConnection);
|
|
405
|
-
}
|
|
406
|
-
if (!this.driver || !this.familyInstance || !this.frameworkComponents) {
|
|
407
|
-
throw new Error("Not connected. Call connect(connection) first.");
|
|
408
|
-
}
|
|
409
|
-
return {
|
|
410
|
-
driver: this.driver,
|
|
411
|
-
familyInstance: this.familyInstance,
|
|
412
|
-
frameworkComponents: this.frameworkComponents
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
async verify(options) {
|
|
416
|
-
const { onProgress } = options;
|
|
417
|
-
if (options.connection !== void 0) {
|
|
418
|
-
onProgress?.({
|
|
419
|
-
action: "verify",
|
|
420
|
-
kind: "spanStart",
|
|
421
|
-
spanId: "connect",
|
|
422
|
-
label: "Connecting to database..."
|
|
423
|
-
});
|
|
424
|
-
try {
|
|
425
|
-
await this.connect(options.connection);
|
|
426
|
-
onProgress?.({
|
|
427
|
-
action: "verify",
|
|
428
|
-
kind: "spanEnd",
|
|
429
|
-
spanId: "connect",
|
|
430
|
-
outcome: "ok"
|
|
431
|
-
});
|
|
432
|
-
} catch (error) {
|
|
433
|
-
onProgress?.({
|
|
434
|
-
action: "verify",
|
|
435
|
-
kind: "spanEnd",
|
|
436
|
-
spanId: "connect",
|
|
437
|
-
outcome: "error"
|
|
438
|
-
});
|
|
439
|
-
throw error;
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
const { driver, familyInstance } = await this.ensureConnected();
|
|
443
|
-
const contractIR = familyInstance.validateContractIR(options.contractIR);
|
|
444
|
-
onProgress?.({
|
|
445
|
-
action: "verify",
|
|
446
|
-
kind: "spanStart",
|
|
447
|
-
spanId: "verify",
|
|
448
|
-
label: "Verifying contract marker..."
|
|
449
|
-
});
|
|
450
|
-
try {
|
|
451
|
-
const result = await familyInstance.verify({
|
|
452
|
-
driver,
|
|
453
|
-
contractIR,
|
|
454
|
-
expectedTargetId: this.options.target.targetId,
|
|
455
|
-
contractPath: ""
|
|
456
|
-
});
|
|
457
|
-
onProgress?.({
|
|
458
|
-
action: "verify",
|
|
459
|
-
kind: "spanEnd",
|
|
460
|
-
spanId: "verify",
|
|
461
|
-
outcome: result.ok ? "ok" : "error"
|
|
462
|
-
});
|
|
463
|
-
return result;
|
|
464
|
-
} catch (error) {
|
|
465
|
-
onProgress?.({
|
|
466
|
-
action: "verify",
|
|
467
|
-
kind: "spanEnd",
|
|
468
|
-
spanId: "verify",
|
|
469
|
-
outcome: "error"
|
|
470
|
-
});
|
|
471
|
-
throw error;
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
async schemaVerify(options) {
|
|
475
|
-
const { onProgress } = options;
|
|
476
|
-
if (options.connection !== void 0) {
|
|
477
|
-
onProgress?.({
|
|
478
|
-
action: "schemaVerify",
|
|
479
|
-
kind: "spanStart",
|
|
480
|
-
spanId: "connect",
|
|
481
|
-
label: "Connecting to database..."
|
|
482
|
-
});
|
|
483
|
-
try {
|
|
484
|
-
await this.connect(options.connection);
|
|
485
|
-
onProgress?.({
|
|
486
|
-
action: "schemaVerify",
|
|
487
|
-
kind: "spanEnd",
|
|
488
|
-
spanId: "connect",
|
|
489
|
-
outcome: "ok"
|
|
490
|
-
});
|
|
491
|
-
} catch (error) {
|
|
492
|
-
onProgress?.({
|
|
493
|
-
action: "schemaVerify",
|
|
494
|
-
kind: "spanEnd",
|
|
495
|
-
spanId: "connect",
|
|
496
|
-
outcome: "error"
|
|
497
|
-
});
|
|
498
|
-
throw error;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
|
|
502
|
-
const contractIR = familyInstance.validateContractIR(options.contractIR);
|
|
503
|
-
onProgress?.({
|
|
504
|
-
action: "schemaVerify",
|
|
505
|
-
kind: "spanStart",
|
|
506
|
-
spanId: "schemaVerify",
|
|
507
|
-
label: "Verifying database schema..."
|
|
508
|
-
});
|
|
509
|
-
try {
|
|
510
|
-
const result = await familyInstance.schemaVerify({
|
|
511
|
-
driver,
|
|
512
|
-
contractIR,
|
|
513
|
-
strict: options.strict ?? false,
|
|
514
|
-
contractPath: "",
|
|
515
|
-
frameworkComponents
|
|
516
|
-
});
|
|
517
|
-
onProgress?.({
|
|
518
|
-
action: "schemaVerify",
|
|
519
|
-
kind: "spanEnd",
|
|
520
|
-
spanId: "schemaVerify",
|
|
521
|
-
outcome: result.ok ? "ok" : "error"
|
|
522
|
-
});
|
|
523
|
-
return result;
|
|
524
|
-
} catch (error) {
|
|
525
|
-
onProgress?.({
|
|
526
|
-
action: "schemaVerify",
|
|
527
|
-
kind: "spanEnd",
|
|
528
|
-
spanId: "schemaVerify",
|
|
529
|
-
outcome: "error"
|
|
530
|
-
});
|
|
531
|
-
throw error;
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
async sign(options) {
|
|
535
|
-
const { onProgress } = options;
|
|
536
|
-
if (options.connection !== void 0) {
|
|
537
|
-
onProgress?.({
|
|
538
|
-
action: "sign",
|
|
539
|
-
kind: "spanStart",
|
|
540
|
-
spanId: "connect",
|
|
541
|
-
label: "Connecting to database..."
|
|
542
|
-
});
|
|
543
|
-
try {
|
|
544
|
-
await this.connect(options.connection);
|
|
545
|
-
onProgress?.({
|
|
546
|
-
action: "sign",
|
|
547
|
-
kind: "spanEnd",
|
|
548
|
-
spanId: "connect",
|
|
549
|
-
outcome: "ok"
|
|
550
|
-
});
|
|
551
|
-
} catch (error) {
|
|
552
|
-
onProgress?.({
|
|
553
|
-
action: "sign",
|
|
554
|
-
kind: "spanEnd",
|
|
555
|
-
spanId: "connect",
|
|
556
|
-
outcome: "error"
|
|
557
|
-
});
|
|
558
|
-
throw error;
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
const { driver, familyInstance } = await this.ensureConnected();
|
|
562
|
-
const contractIR = familyInstance.validateContractIR(options.contractIR);
|
|
563
|
-
onProgress?.({
|
|
564
|
-
action: "sign",
|
|
565
|
-
kind: "spanStart",
|
|
566
|
-
spanId: "sign",
|
|
567
|
-
label: "Signing database..."
|
|
568
|
-
});
|
|
569
|
-
try {
|
|
570
|
-
const result = await familyInstance.sign({
|
|
571
|
-
driver,
|
|
572
|
-
contractIR,
|
|
573
|
-
contractPath: options.contractPath ?? "",
|
|
574
|
-
...options.configPath ? { configPath: options.configPath } : {}
|
|
575
|
-
});
|
|
576
|
-
onProgress?.({
|
|
577
|
-
action: "sign",
|
|
578
|
-
kind: "spanEnd",
|
|
579
|
-
spanId: "sign",
|
|
580
|
-
outcome: "ok"
|
|
581
|
-
});
|
|
582
|
-
return result;
|
|
583
|
-
} catch (error) {
|
|
584
|
-
onProgress?.({
|
|
585
|
-
action: "sign",
|
|
586
|
-
kind: "spanEnd",
|
|
587
|
-
spanId: "sign",
|
|
588
|
-
outcome: "error"
|
|
589
|
-
});
|
|
590
|
-
throw error;
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
async dbInit(options) {
|
|
594
|
-
const { onProgress } = options;
|
|
595
|
-
if (options.connection !== void 0) {
|
|
596
|
-
onProgress?.({
|
|
597
|
-
action: "dbInit",
|
|
598
|
-
kind: "spanStart",
|
|
599
|
-
spanId: "connect",
|
|
600
|
-
label: "Connecting to database..."
|
|
601
|
-
});
|
|
602
|
-
try {
|
|
603
|
-
await this.connect(options.connection);
|
|
604
|
-
onProgress?.({
|
|
605
|
-
action: "dbInit",
|
|
606
|
-
kind: "spanEnd",
|
|
607
|
-
spanId: "connect",
|
|
608
|
-
outcome: "ok"
|
|
609
|
-
});
|
|
610
|
-
} catch (error) {
|
|
611
|
-
onProgress?.({
|
|
612
|
-
action: "dbInit",
|
|
613
|
-
kind: "spanEnd",
|
|
614
|
-
spanId: "connect",
|
|
615
|
-
outcome: "error"
|
|
616
|
-
});
|
|
617
|
-
throw error;
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
|
|
621
|
-
if (!this.options.target.migrations) {
|
|
622
|
-
throw new Error(`Target "${this.options.target.targetId}" does not support migrations`);
|
|
623
|
-
}
|
|
624
|
-
const contractIR = familyInstance.validateContractIR(options.contractIR);
|
|
625
|
-
return executeDbInit({
|
|
626
|
-
driver,
|
|
627
|
-
familyInstance,
|
|
628
|
-
contractIR,
|
|
629
|
-
mode: options.mode,
|
|
630
|
-
migrations: this.options.target.migrations,
|
|
631
|
-
frameworkComponents,
|
|
632
|
-
...onProgress ? { onProgress } : {}
|
|
633
|
-
});
|
|
634
|
-
}
|
|
635
|
-
async introspect(options) {
|
|
636
|
-
const onProgress = options?.onProgress;
|
|
637
|
-
if (options?.connection !== void 0) {
|
|
638
|
-
onProgress?.({
|
|
639
|
-
action: "introspect",
|
|
640
|
-
kind: "spanStart",
|
|
641
|
-
spanId: "connect",
|
|
642
|
-
label: "Connecting to database..."
|
|
643
|
-
});
|
|
644
|
-
try {
|
|
645
|
-
await this.connect(options.connection);
|
|
646
|
-
onProgress?.({
|
|
647
|
-
action: "introspect",
|
|
648
|
-
kind: "spanEnd",
|
|
649
|
-
spanId: "connect",
|
|
650
|
-
outcome: "ok"
|
|
651
|
-
});
|
|
652
|
-
} catch (error) {
|
|
653
|
-
onProgress?.({
|
|
654
|
-
action: "introspect",
|
|
655
|
-
kind: "spanEnd",
|
|
656
|
-
spanId: "connect",
|
|
657
|
-
outcome: "error"
|
|
658
|
-
});
|
|
659
|
-
throw error;
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
const { driver, familyInstance } = await this.ensureConnected();
|
|
663
|
-
const _schema = options?.schema;
|
|
664
|
-
void _schema;
|
|
665
|
-
onProgress?.({
|
|
666
|
-
action: "introspect",
|
|
667
|
-
kind: "spanStart",
|
|
668
|
-
spanId: "introspect",
|
|
669
|
-
label: "Introspecting database schema..."
|
|
670
|
-
});
|
|
671
|
-
try {
|
|
672
|
-
const result = await familyInstance.introspect({ driver });
|
|
673
|
-
onProgress?.({
|
|
674
|
-
action: "introspect",
|
|
675
|
-
kind: "spanEnd",
|
|
676
|
-
spanId: "introspect",
|
|
677
|
-
outcome: "ok"
|
|
678
|
-
});
|
|
679
|
-
return result;
|
|
680
|
-
} catch (error) {
|
|
681
|
-
onProgress?.({
|
|
682
|
-
action: "introspect",
|
|
683
|
-
kind: "spanEnd",
|
|
684
|
-
spanId: "introspect",
|
|
685
|
-
outcome: "error"
|
|
686
|
-
});
|
|
687
|
-
throw error;
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
toSchemaView(schemaIR) {
|
|
691
|
-
this.init();
|
|
692
|
-
if (this.familyInstance?.toSchemaView) {
|
|
693
|
-
return this.familyInstance.toSchemaView(schemaIR);
|
|
694
|
-
}
|
|
695
|
-
return void 0;
|
|
696
|
-
}
|
|
697
|
-
async emit(options) {
|
|
698
|
-
const { onProgress, contractConfig } = options;
|
|
699
|
-
this.init();
|
|
700
|
-
if (!this.familyInstance) {
|
|
701
|
-
throw new Error("Family instance was not initialized. This is a bug.");
|
|
702
|
-
}
|
|
703
|
-
let contractRaw;
|
|
704
|
-
onProgress?.({
|
|
705
|
-
action: "emit",
|
|
706
|
-
kind: "spanStart",
|
|
707
|
-
spanId: "resolveSource",
|
|
708
|
-
label: "Resolving contract source..."
|
|
709
|
-
});
|
|
710
|
-
try {
|
|
711
|
-
switch (contractConfig.source.kind) {
|
|
712
|
-
case "loader":
|
|
713
|
-
contractRaw = await contractConfig.source.load();
|
|
714
|
-
break;
|
|
715
|
-
case "value":
|
|
716
|
-
contractRaw = contractConfig.source.value;
|
|
717
|
-
break;
|
|
718
|
-
}
|
|
719
|
-
onProgress?.({
|
|
720
|
-
action: "emit",
|
|
721
|
-
kind: "spanEnd",
|
|
722
|
-
spanId: "resolveSource",
|
|
723
|
-
outcome: "ok"
|
|
724
|
-
});
|
|
725
|
-
} catch (error) {
|
|
726
|
-
onProgress?.({
|
|
727
|
-
action: "emit",
|
|
728
|
-
kind: "spanEnd",
|
|
729
|
-
spanId: "resolveSource",
|
|
730
|
-
outcome: "error"
|
|
731
|
-
});
|
|
732
|
-
return notOk2({
|
|
733
|
-
code: "CONTRACT_SOURCE_INVALID",
|
|
734
|
-
summary: "Failed to resolve contract source",
|
|
735
|
-
why: error instanceof Error ? error.message : String(error),
|
|
736
|
-
meta: void 0
|
|
737
|
-
});
|
|
738
|
-
}
|
|
739
|
-
onProgress?.({
|
|
740
|
-
action: "emit",
|
|
741
|
-
kind: "spanStart",
|
|
742
|
-
spanId: "emit",
|
|
743
|
-
label: "Emitting contract..."
|
|
744
|
-
});
|
|
745
|
-
try {
|
|
746
|
-
const emitResult = await this.familyInstance.emitContract({ contractIR: contractRaw });
|
|
747
|
-
onProgress?.({
|
|
748
|
-
action: "emit",
|
|
749
|
-
kind: "spanEnd",
|
|
750
|
-
spanId: "emit",
|
|
751
|
-
outcome: "ok"
|
|
752
|
-
});
|
|
753
|
-
return ok2({
|
|
754
|
-
coreHash: emitResult.coreHash,
|
|
755
|
-
profileHash: emitResult.profileHash,
|
|
756
|
-
contractJson: emitResult.contractJson,
|
|
757
|
-
contractDts: emitResult.contractDts
|
|
758
|
-
});
|
|
759
|
-
} catch (error) {
|
|
760
|
-
onProgress?.({
|
|
761
|
-
action: "emit",
|
|
762
|
-
kind: "spanEnd",
|
|
763
|
-
spanId: "emit",
|
|
764
|
-
outcome: "error"
|
|
765
|
-
});
|
|
766
|
-
return notOk2({
|
|
767
|
-
code: "EMIT_FAILED",
|
|
768
|
-
summary: "Failed to emit contract",
|
|
769
|
-
why: error instanceof Error ? error.message : String(error),
|
|
770
|
-
meta: void 0
|
|
771
|
-
});
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
};
|
|
775
|
-
|
|
776
|
-
// src/utils/command-helpers.ts
|
|
777
|
-
function setCommandDescriptions(command, shortDescription, longDescription) {
|
|
778
|
-
command.description(shortDescription);
|
|
779
|
-
if (longDescription) {
|
|
780
|
-
command._longDescription = longDescription;
|
|
781
|
-
}
|
|
782
|
-
return command;
|
|
783
|
-
}
|
|
784
|
-
function getLongDescription(command) {
|
|
785
|
-
return command._longDescription;
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
// src/utils/global-flags.ts
|
|
789
|
-
function parseGlobalFlags(options) {
|
|
790
|
-
const flags = {};
|
|
791
|
-
if (options.json === true || options.json === "object") {
|
|
792
|
-
flags.json = "object";
|
|
793
|
-
} else if (options.json === "ndjson") {
|
|
794
|
-
flags.json = "ndjson";
|
|
795
|
-
}
|
|
796
|
-
if (options.quiet || options.q) {
|
|
797
|
-
flags.quiet = true;
|
|
798
|
-
}
|
|
799
|
-
if (options.vv || options.trace) {
|
|
800
|
-
flags.verbose = 2;
|
|
801
|
-
} else if (options.verbose || options.v) {
|
|
802
|
-
flags.verbose = 1;
|
|
803
|
-
} else {
|
|
804
|
-
flags.verbose = 0;
|
|
805
|
-
}
|
|
806
|
-
if (options.timestamps) {
|
|
807
|
-
flags.timestamps = true;
|
|
808
|
-
}
|
|
809
|
-
if (process.env["NO_COLOR"] || flags.json) {
|
|
810
|
-
flags.color = false;
|
|
811
|
-
} else if (options["no-color"]) {
|
|
812
|
-
flags.color = false;
|
|
813
|
-
} else if (options.color !== void 0) {
|
|
814
|
-
flags.color = options.color;
|
|
815
|
-
} else {
|
|
816
|
-
flags.color = process.stdout.isTTY && !process.env["CI"];
|
|
817
|
-
}
|
|
818
|
-
return flags;
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
// src/utils/output.ts
|
|
822
|
-
import { relative } from "path";
|
|
823
|
-
import { bgGreen, blue, bold, cyan, dim, green, magenta, red, yellow } from "colorette";
|
|
824
|
-
import stringWidth from "string-width";
|
|
825
|
-
import stripAnsi from "strip-ansi";
|
|
826
|
-
import wrapAnsi from "wrap-ansi";
|
|
827
|
-
function formatTimestamp() {
|
|
828
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
829
|
-
}
|
|
830
|
-
function createPrefix(flags) {
|
|
831
|
-
return flags.timestamps ? `[${formatTimestamp()}] ` : "";
|
|
832
|
-
}
|
|
833
|
-
function isVerbose(flags, level) {
|
|
834
|
-
return (flags.verbose ?? 0) >= level;
|
|
835
|
-
}
|
|
836
|
-
function createColorFormatter(useColor, colorFn) {
|
|
837
|
-
return useColor ? colorFn : (text) => text;
|
|
838
|
-
}
|
|
839
|
-
function formatDim(useColor, text) {
|
|
840
|
-
return useColor ? dim(text) : text;
|
|
841
|
-
}
|
|
842
|
-
function formatEmitOutput(result, flags) {
|
|
843
|
-
if (flags.quiet) {
|
|
844
|
-
return "";
|
|
845
|
-
}
|
|
846
|
-
const lines = [];
|
|
847
|
-
const prefix = createPrefix(flags);
|
|
848
|
-
const jsonPath = relative(process.cwd(), result.files.json);
|
|
849
|
-
const dtsPath = relative(process.cwd(), result.files.dts);
|
|
850
|
-
lines.push(`${prefix}\u2714 Emitted contract.json \u2192 ${jsonPath}`);
|
|
851
|
-
lines.push(`${prefix}\u2714 Emitted contract.d.ts \u2192 ${dtsPath}`);
|
|
852
|
-
lines.push(`${prefix} coreHash: ${result.coreHash}`);
|
|
853
|
-
if (result.profileHash) {
|
|
854
|
-
lines.push(`${prefix} profileHash: ${result.profileHash}`);
|
|
855
|
-
}
|
|
856
|
-
if (isVerbose(flags, 1)) {
|
|
857
|
-
lines.push(`${prefix} Total time: ${result.timings.total}ms`);
|
|
858
|
-
}
|
|
859
|
-
return lines.join("\n");
|
|
860
|
-
}
|
|
861
|
-
function formatEmitJson(result) {
|
|
862
|
-
const output = {
|
|
863
|
-
ok: true,
|
|
864
|
-
coreHash: result.coreHash,
|
|
865
|
-
...result.profileHash ? { profileHash: result.profileHash } : {},
|
|
866
|
-
outDir: result.outDir,
|
|
867
|
-
files: result.files,
|
|
868
|
-
timings: result.timings
|
|
869
|
-
};
|
|
870
|
-
return JSON.stringify(output, null, 2);
|
|
871
|
-
}
|
|
872
|
-
function formatErrorOutput(error, flags) {
|
|
873
|
-
const lines = [];
|
|
874
|
-
const prefix = createPrefix(flags);
|
|
875
|
-
const useColor = flags.color !== false;
|
|
876
|
-
const formatRed = createColorFormatter(useColor, red);
|
|
877
|
-
const formatDimText = (text) => formatDim(useColor, text);
|
|
878
|
-
lines.push(`${prefix}${formatRed("\u2716")} ${error.summary} (${error.code})`);
|
|
879
|
-
if (error.why) {
|
|
880
|
-
lines.push(`${prefix}${formatDimText(` Why: ${error.why}`)}`);
|
|
881
|
-
}
|
|
882
|
-
if (error.fix) {
|
|
883
|
-
lines.push(`${prefix}${formatDimText(` Fix: ${error.fix}`)}`);
|
|
884
|
-
}
|
|
885
|
-
if (error.where?.path) {
|
|
886
|
-
const whereLine = error.where.line ? `${error.where.path}:${error.where.line}` : error.where.path;
|
|
887
|
-
lines.push(`${prefix}${formatDimText(` Where: ${whereLine}`)}`);
|
|
888
|
-
}
|
|
889
|
-
if (error.meta?.["conflicts"]) {
|
|
890
|
-
const conflicts = error.meta["conflicts"];
|
|
891
|
-
if (conflicts.length > 0) {
|
|
892
|
-
const maxToShow = isVerbose(flags, 1) ? conflicts.length : Math.min(3, conflicts.length);
|
|
893
|
-
const header = isVerbose(flags, 1) ? " Conflicts:" : ` Conflicts (showing ${maxToShow} of ${conflicts.length}):`;
|
|
894
|
-
lines.push(`${prefix}${formatDimText(header)}`);
|
|
895
|
-
for (const conflict of conflicts.slice(0, maxToShow)) {
|
|
896
|
-
lines.push(`${prefix}${formatDimText(` - [${conflict.kind}] ${conflict.summary}`)}`);
|
|
897
|
-
}
|
|
898
|
-
if (!isVerbose(flags, 1) && conflicts.length > maxToShow) {
|
|
899
|
-
lines.push(`${prefix}${formatDimText(" Re-run with -v/--verbose to see all conflicts")}`);
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
if (error.meta?.["issues"]) {
|
|
904
|
-
const issues = error.meta["issues"];
|
|
905
|
-
if (issues.length > 0) {
|
|
906
|
-
const maxToShow = isVerbose(flags, 1) ? issues.length : Math.min(3, issues.length);
|
|
907
|
-
const header = isVerbose(flags, 1) ? " Issues:" : ` Issues (showing ${maxToShow} of ${issues.length}):`;
|
|
908
|
-
lines.push(`${prefix}${formatDimText(header)}`);
|
|
909
|
-
for (const issue of issues.slice(0, maxToShow)) {
|
|
910
|
-
const kind = issue.kind ?? "issue";
|
|
911
|
-
const message = issue.message ?? "";
|
|
912
|
-
lines.push(`${prefix}${formatDimText(` - [${kind}] ${message}`)}`);
|
|
913
|
-
}
|
|
914
|
-
if (!isVerbose(flags, 1) && issues.length > maxToShow) {
|
|
915
|
-
lines.push(`${prefix}${formatDimText(" Re-run with -v/--verbose to see all issues")}`);
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
if (error.docsUrl && isVerbose(flags, 1)) {
|
|
920
|
-
lines.push(formatDimText(error.docsUrl));
|
|
921
|
-
}
|
|
922
|
-
if (isVerbose(flags, 2) && error.meta) {
|
|
923
|
-
lines.push(`${prefix}${formatDimText(` Meta: ${JSON.stringify(error.meta, null, 2)}`)}`);
|
|
924
|
-
}
|
|
925
|
-
return lines.join("\n");
|
|
926
|
-
}
|
|
927
|
-
function formatErrorJson(error) {
|
|
928
|
-
return JSON.stringify(error, null, 2);
|
|
929
|
-
}
|
|
930
|
-
function formatVerifyOutput(result, flags) {
|
|
931
|
-
if (flags.quiet) {
|
|
932
|
-
return "";
|
|
933
|
-
}
|
|
934
|
-
const lines = [];
|
|
935
|
-
const prefix = createPrefix(flags);
|
|
936
|
-
const useColor = flags.color !== false;
|
|
937
|
-
const formatGreen = createColorFormatter(useColor, green);
|
|
938
|
-
const formatRed = createColorFormatter(useColor, red);
|
|
939
|
-
const formatDimText = (text) => formatDim(useColor, text);
|
|
940
|
-
if (result.ok) {
|
|
941
|
-
lines.push(`${prefix}${formatGreen("\u2714")} ${result.summary}`);
|
|
942
|
-
lines.push(`${prefix}${formatDimText(` coreHash: ${result.contract.coreHash}`)}`);
|
|
943
|
-
if (result.contract.profileHash) {
|
|
944
|
-
lines.push(`${prefix}${formatDimText(` profileHash: ${result.contract.profileHash}`)}`);
|
|
945
|
-
}
|
|
946
|
-
} else {
|
|
947
|
-
lines.push(`${prefix}${formatRed("\u2716")} ${result.summary} (${result.code})`);
|
|
948
|
-
}
|
|
949
|
-
if (isVerbose(flags, 1)) {
|
|
950
|
-
if (result.codecCoverageSkipped) {
|
|
951
|
-
lines.push(
|
|
952
|
-
`${prefix}${formatDimText(" Codec coverage check skipped (helper returned no supported types)")}`
|
|
953
|
-
);
|
|
954
|
-
}
|
|
955
|
-
lines.push(`${prefix}${formatDimText(` Total time: ${result.timings.total}ms`)}`);
|
|
956
|
-
}
|
|
957
|
-
return lines.join("\n");
|
|
958
|
-
}
|
|
959
|
-
function formatVerifyJson(result) {
|
|
960
|
-
const output = {
|
|
961
|
-
ok: result.ok,
|
|
962
|
-
...result.code ? { code: result.code } : {},
|
|
963
|
-
summary: result.summary,
|
|
964
|
-
contract: result.contract,
|
|
965
|
-
...result.marker ? { marker: result.marker } : {},
|
|
966
|
-
target: result.target,
|
|
967
|
-
...result.missingCodecs ? { missingCodecs: result.missingCodecs } : {},
|
|
968
|
-
...result.meta ? { meta: result.meta } : {},
|
|
969
|
-
timings: result.timings
|
|
970
|
-
};
|
|
971
|
-
return JSON.stringify(output, null, 2);
|
|
972
|
-
}
|
|
973
|
-
function formatIntrospectJson(result) {
|
|
974
|
-
return JSON.stringify(result, null, 2);
|
|
975
|
-
}
|
|
976
|
-
function renderSchemaTree(node, flags, options) {
|
|
977
|
-
const { isLast, prefix, useColor, formatDimText, isRoot = false } = options;
|
|
978
|
-
const lines = [];
|
|
979
|
-
let formattedLabel = node.label;
|
|
980
|
-
if (useColor) {
|
|
981
|
-
switch (node.kind) {
|
|
982
|
-
case "root":
|
|
983
|
-
formattedLabel = bold(node.label);
|
|
984
|
-
break;
|
|
985
|
-
case "entity": {
|
|
986
|
-
const tableMatch = node.label.match(/^table\s+(.+)$/);
|
|
987
|
-
if (tableMatch?.[1]) {
|
|
988
|
-
const tableName = tableMatch[1];
|
|
989
|
-
formattedLabel = `${dim("table")} ${cyan(tableName)}`;
|
|
990
|
-
} else {
|
|
991
|
-
formattedLabel = cyan(node.label);
|
|
992
|
-
}
|
|
993
|
-
break;
|
|
994
|
-
}
|
|
995
|
-
case "collection": {
|
|
996
|
-
formattedLabel = dim(node.label);
|
|
997
|
-
break;
|
|
998
|
-
}
|
|
999
|
-
case "field": {
|
|
1000
|
-
const columnMatch = node.label.match(/^([^:]+):\s*(.+)$/);
|
|
1001
|
-
if (columnMatch?.[1] && columnMatch[2]) {
|
|
1002
|
-
const columnName = columnMatch[1];
|
|
1003
|
-
const rest = columnMatch[2];
|
|
1004
|
-
const typeMatch = rest.match(/^([^\s(]+)\s*(\([^)]+\))$/);
|
|
1005
|
-
if (typeMatch?.[1] && typeMatch[2]) {
|
|
1006
|
-
const typeDisplay = typeMatch[1];
|
|
1007
|
-
const nullability = typeMatch[2];
|
|
1008
|
-
formattedLabel = `${cyan(columnName)}: ${typeDisplay} ${dim(nullability)}`;
|
|
1009
|
-
} else {
|
|
1010
|
-
formattedLabel = `${cyan(columnName)}: ${rest}`;
|
|
1011
|
-
}
|
|
1012
|
-
} else {
|
|
1013
|
-
formattedLabel = node.label;
|
|
1014
|
-
}
|
|
1015
|
-
break;
|
|
1016
|
-
}
|
|
1017
|
-
case "index": {
|
|
1018
|
-
const pkMatch = node.label.match(/^primary key:\s*(.+)$/);
|
|
1019
|
-
if (pkMatch?.[1]) {
|
|
1020
|
-
const columnNames = pkMatch[1];
|
|
1021
|
-
formattedLabel = `${dim("primary key")}: ${cyan(columnNames)}`;
|
|
1022
|
-
} else {
|
|
1023
|
-
const uniqueMatch = node.label.match(/^unique\s+(.+)$/);
|
|
1024
|
-
if (uniqueMatch?.[1]) {
|
|
1025
|
-
const name = uniqueMatch[1];
|
|
1026
|
-
formattedLabel = `${dim("unique")} ${cyan(name)}`;
|
|
1027
|
-
} else {
|
|
1028
|
-
const indexMatch = node.label.match(/^(unique\s+)?index\s+(.+)$/);
|
|
1029
|
-
if (indexMatch?.[2]) {
|
|
1030
|
-
const prefix2 = indexMatch[1] ? `${dim("unique")} ` : "";
|
|
1031
|
-
const name = indexMatch[2];
|
|
1032
|
-
formattedLabel = `${prefix2}${dim("index")} ${cyan(name)}`;
|
|
1033
|
-
} else {
|
|
1034
|
-
formattedLabel = dim(node.label);
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
break;
|
|
1039
|
-
}
|
|
1040
|
-
case "extension": {
|
|
1041
|
-
const extMatch = node.label.match(/^([^\s]+)\s+(extension is enabled)$/);
|
|
1042
|
-
if (extMatch?.[1] && extMatch[2]) {
|
|
1043
|
-
const extName = extMatch[1];
|
|
1044
|
-
const rest = extMatch[2];
|
|
1045
|
-
formattedLabel = `${cyan(extName)} ${dim(rest)}`;
|
|
1046
|
-
} else {
|
|
1047
|
-
formattedLabel = magenta(node.label);
|
|
1048
|
-
}
|
|
1049
|
-
break;
|
|
1050
|
-
}
|
|
1051
|
-
default:
|
|
1052
|
-
formattedLabel = node.label;
|
|
1053
|
-
break;
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
if (isRoot) {
|
|
1057
|
-
lines.push(formattedLabel);
|
|
1058
|
-
} else {
|
|
1059
|
-
const treeChar = isLast ? "\u2514" : "\u251C";
|
|
1060
|
-
const treePrefix = `${prefix}${formatDimText(treeChar)}\u2500 `;
|
|
1061
|
-
const isRootChild = prefix === "";
|
|
1062
|
-
const prefixWithoutAnsi = stripAnsi(prefix);
|
|
1063
|
-
const prefixHasVerticalBar = prefixWithoutAnsi.includes("\u2502");
|
|
1064
|
-
if (isRootChild) {
|
|
1065
|
-
lines.push(`${treePrefix}${formattedLabel}`);
|
|
1066
|
-
} else if (prefixHasVerticalBar) {
|
|
1067
|
-
lines.push(`${treePrefix}${formattedLabel}`);
|
|
1068
|
-
} else {
|
|
1069
|
-
lines.push(`${formatDimText("\u2502")} ${treePrefix}${formattedLabel}`);
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
if (node.children && node.children.length > 0) {
|
|
1073
|
-
const childPrefix = isRoot ? "" : isLast ? `${prefix} ` : `${prefix}${formatDimText("\u2502")} `;
|
|
1074
|
-
for (let i = 0; i < node.children.length; i++) {
|
|
1075
|
-
const child = node.children[i];
|
|
1076
|
-
if (!child) continue;
|
|
1077
|
-
const isLastChild = i === node.children.length - 1;
|
|
1078
|
-
const childLines = renderSchemaTree(child, flags, {
|
|
1079
|
-
isLast: isLastChild,
|
|
1080
|
-
prefix: childPrefix,
|
|
1081
|
-
useColor,
|
|
1082
|
-
formatDimText,
|
|
1083
|
-
isRoot: false
|
|
1084
|
-
});
|
|
1085
|
-
lines.push(...childLines);
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
return lines;
|
|
1089
|
-
}
|
|
1090
|
-
function formatIntrospectOutput(result, schemaView, flags) {
|
|
1091
|
-
if (flags.quiet) {
|
|
1092
|
-
return "";
|
|
1093
|
-
}
|
|
1094
|
-
const lines = [];
|
|
1095
|
-
const prefix = createPrefix(flags);
|
|
1096
|
-
const useColor = flags.color !== false;
|
|
1097
|
-
const formatDimText = (text) => formatDim(useColor, text);
|
|
1098
|
-
if (schemaView) {
|
|
1099
|
-
const treeLines = renderSchemaTree(schemaView.root, flags, {
|
|
1100
|
-
isLast: true,
|
|
1101
|
-
prefix: "",
|
|
1102
|
-
useColor,
|
|
1103
|
-
formatDimText,
|
|
1104
|
-
isRoot: true
|
|
1105
|
-
});
|
|
1106
|
-
const prefixedTreeLines = treeLines.map((line) => `${prefix}${line}`);
|
|
1107
|
-
lines.push(...prefixedTreeLines);
|
|
1108
|
-
} else {
|
|
1109
|
-
lines.push(`${prefix}\u2714 ${result.summary}`);
|
|
1110
|
-
if (isVerbose(flags, 1)) {
|
|
1111
|
-
lines.push(`${prefix} Target: ${result.target.familyId}/${result.target.id}`);
|
|
1112
|
-
if (result.meta?.dbUrl) {
|
|
1113
|
-
lines.push(`${prefix} Database: ${result.meta.dbUrl}`);
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
if (isVerbose(flags, 1)) {
|
|
1118
|
-
lines.push(`${prefix}${formatDimText(` Total time: ${result.timings.total}ms`)}`);
|
|
1119
|
-
}
|
|
1120
|
-
return lines.join("\n");
|
|
1121
|
-
}
|
|
1122
|
-
function renderSchemaVerificationTree(node, flags, options) {
|
|
1123
|
-
const { isLast, prefix, useColor, formatDimText, isRoot = false } = options;
|
|
1124
|
-
const lines = [];
|
|
1125
|
-
let statusGlyph = "";
|
|
1126
|
-
let statusColor = (text) => text;
|
|
1127
|
-
if (useColor) {
|
|
1128
|
-
switch (node.status) {
|
|
1129
|
-
case "pass":
|
|
1130
|
-
statusGlyph = "\u2714";
|
|
1131
|
-
statusColor = green;
|
|
1132
|
-
break;
|
|
1133
|
-
case "warn":
|
|
1134
|
-
statusGlyph = "\u26A0";
|
|
1135
|
-
statusColor = (text) => useColor ? yellow(text) : text;
|
|
1136
|
-
break;
|
|
1137
|
-
case "fail":
|
|
1138
|
-
statusGlyph = "\u2716";
|
|
1139
|
-
statusColor = red;
|
|
1140
|
-
break;
|
|
1141
|
-
}
|
|
1142
|
-
} else {
|
|
1143
|
-
switch (node.status) {
|
|
1144
|
-
case "pass":
|
|
1145
|
-
statusGlyph = "\u2714";
|
|
1146
|
-
break;
|
|
1147
|
-
case "warn":
|
|
1148
|
-
statusGlyph = "\u26A0";
|
|
1149
|
-
break;
|
|
1150
|
-
case "fail":
|
|
1151
|
-
statusGlyph = "\u2716";
|
|
1152
|
-
break;
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
let labelColor = (text) => text;
|
|
1156
|
-
let formattedLabel = node.name;
|
|
1157
|
-
if (useColor) {
|
|
1158
|
-
switch (node.kind) {
|
|
1159
|
-
case "contract":
|
|
1160
|
-
case "schema":
|
|
1161
|
-
labelColor = bold;
|
|
1162
|
-
formattedLabel = labelColor(node.name);
|
|
1163
|
-
break;
|
|
1164
|
-
case "table": {
|
|
1165
|
-
const tableMatch = node.name.match(/^table\s+(.+)$/);
|
|
1166
|
-
if (tableMatch?.[1]) {
|
|
1167
|
-
const tableName = tableMatch[1];
|
|
1168
|
-
formattedLabel = `${dim("table")} ${cyan(tableName)}`;
|
|
1169
|
-
} else {
|
|
1170
|
-
formattedLabel = dim(node.name);
|
|
1171
|
-
}
|
|
1172
|
-
break;
|
|
1173
|
-
}
|
|
1174
|
-
case "columns":
|
|
1175
|
-
labelColor = dim;
|
|
1176
|
-
formattedLabel = labelColor(node.name);
|
|
1177
|
-
break;
|
|
1178
|
-
case "column": {
|
|
1179
|
-
const columnMatch = node.name.match(/^([^:]+):\s*(.+)$/);
|
|
1180
|
-
if (columnMatch?.[1] && columnMatch[2]) {
|
|
1181
|
-
const columnName = columnMatch[1];
|
|
1182
|
-
const rest = columnMatch[2];
|
|
1183
|
-
const typeMatch = rest.match(/^([^\s→]+)\s*→\s*([^\s(]+)\s*(\([^)]+\))$/);
|
|
1184
|
-
if (typeMatch?.[1] && typeMatch[2] && typeMatch[3]) {
|
|
1185
|
-
const contractType = typeMatch[1];
|
|
1186
|
-
const nativeType = typeMatch[2];
|
|
1187
|
-
const nullability = typeMatch[3];
|
|
1188
|
-
formattedLabel = `${cyan(columnName)}: ${contractType} \u2192 ${dim(nativeType)} ${dim(nullability)}`;
|
|
1189
|
-
} else {
|
|
1190
|
-
formattedLabel = `${cyan(columnName)}: ${rest}`;
|
|
1191
|
-
}
|
|
1192
|
-
} else {
|
|
1193
|
-
formattedLabel = node.name;
|
|
1194
|
-
}
|
|
1195
|
-
break;
|
|
1196
|
-
}
|
|
1197
|
-
case "type":
|
|
1198
|
-
case "nullability":
|
|
1199
|
-
labelColor = (text) => text;
|
|
1200
|
-
formattedLabel = labelColor(node.name);
|
|
1201
|
-
break;
|
|
1202
|
-
case "primaryKey": {
|
|
1203
|
-
const pkMatch = node.name.match(/^primary key:\s*(.+)$/);
|
|
1204
|
-
if (pkMatch?.[1]) {
|
|
1205
|
-
const columnNames = pkMatch[1];
|
|
1206
|
-
formattedLabel = `${dim("primary key")}: ${cyan(columnNames)}`;
|
|
1207
|
-
} else {
|
|
1208
|
-
formattedLabel = dim(node.name);
|
|
1209
|
-
}
|
|
1210
|
-
break;
|
|
1211
|
-
}
|
|
1212
|
-
case "foreignKey":
|
|
1213
|
-
case "unique":
|
|
1214
|
-
case "index":
|
|
1215
|
-
labelColor = dim;
|
|
1216
|
-
formattedLabel = labelColor(node.name);
|
|
1217
|
-
break;
|
|
1218
|
-
case "extension": {
|
|
1219
|
-
const dbMatch = node.name.match(/^database is\s+(.+)$/);
|
|
1220
|
-
if (dbMatch?.[1]) {
|
|
1221
|
-
const dbName = dbMatch[1];
|
|
1222
|
-
formattedLabel = `${dim("database is")} ${cyan(dbName)}`;
|
|
1223
|
-
} else {
|
|
1224
|
-
const extMatch = node.name.match(/^([^\s]+)\s+(extension is enabled)$/);
|
|
1225
|
-
if (extMatch?.[1] && extMatch[2]) {
|
|
1226
|
-
const extName = extMatch[1];
|
|
1227
|
-
const rest = extMatch[2];
|
|
1228
|
-
formattedLabel = `${cyan(extName)} ${dim(rest)}`;
|
|
1229
|
-
} else {
|
|
1230
|
-
labelColor = magenta;
|
|
1231
|
-
formattedLabel = labelColor(node.name);
|
|
1232
|
-
}
|
|
1233
|
-
}
|
|
1234
|
-
break;
|
|
1235
|
-
}
|
|
1236
|
-
default:
|
|
1237
|
-
formattedLabel = node.name;
|
|
1238
|
-
break;
|
|
1239
|
-
}
|
|
1240
|
-
} else {
|
|
1241
|
-
formattedLabel = node.name;
|
|
1242
|
-
}
|
|
1243
|
-
const statusGlyphColored = statusColor(statusGlyph);
|
|
1244
|
-
let nodeLabel = formattedLabel;
|
|
1245
|
-
if ((node.status === "fail" || node.status === "warn") && node.message && node.message.length > 0) {
|
|
1246
|
-
const messageText = formatDimText(`(${node.message})`);
|
|
1247
|
-
nodeLabel = `${formattedLabel} ${messageText}`;
|
|
1248
|
-
}
|
|
1249
|
-
if (isRoot) {
|
|
1250
|
-
lines.push(`${statusGlyphColored} ${nodeLabel}`);
|
|
1251
|
-
} else {
|
|
1252
|
-
const treeChar = isLast ? "\u2514" : "\u251C";
|
|
1253
|
-
const treePrefix = `${prefix}${formatDimText(treeChar)}\u2500 `;
|
|
1254
|
-
const isRootChild = prefix === "";
|
|
1255
|
-
const prefixWithoutAnsi = stripAnsi(prefix);
|
|
1256
|
-
const prefixHasVerticalBar = prefixWithoutAnsi.includes("\u2502");
|
|
1257
|
-
if (isRootChild) {
|
|
1258
|
-
lines.push(`${treePrefix}${statusGlyphColored} ${nodeLabel}`);
|
|
1259
|
-
} else if (prefixHasVerticalBar) {
|
|
1260
|
-
lines.push(`${treePrefix}${statusGlyphColored} ${nodeLabel}`);
|
|
1261
|
-
} else {
|
|
1262
|
-
lines.push(`${formatDimText("\u2502")} ${treePrefix}${statusGlyphColored} ${nodeLabel}`);
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
if (node.children && node.children.length > 0) {
|
|
1266
|
-
const childPrefix = isRoot ? "" : isLast ? `${prefix} ` : `${prefix}${formatDimText("\u2502")} `;
|
|
1267
|
-
for (let i = 0; i < node.children.length; i++) {
|
|
1268
|
-
const child = node.children[i];
|
|
1269
|
-
if (!child) continue;
|
|
1270
|
-
const isLastChild = i === node.children.length - 1;
|
|
1271
|
-
const childLines = renderSchemaVerificationTree(child, flags, {
|
|
1272
|
-
isLast: isLastChild,
|
|
1273
|
-
prefix: childPrefix,
|
|
1274
|
-
useColor,
|
|
1275
|
-
formatDimText,
|
|
1276
|
-
isRoot: false
|
|
1277
|
-
});
|
|
1278
|
-
lines.push(...childLines);
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
return lines;
|
|
1282
|
-
}
|
|
1283
|
-
function formatSchemaVerifyOutput(result, flags) {
|
|
1284
|
-
if (flags.quiet) {
|
|
1285
|
-
return "";
|
|
1286
|
-
}
|
|
1287
|
-
const lines = [];
|
|
1288
|
-
const prefix = createPrefix(flags);
|
|
1289
|
-
const useColor = flags.color !== false;
|
|
1290
|
-
const formatGreen = createColorFormatter(useColor, green);
|
|
1291
|
-
const formatRed = createColorFormatter(useColor, red);
|
|
1292
|
-
const formatDimText = (text) => formatDim(useColor, text);
|
|
1293
|
-
const treeLines = renderSchemaVerificationTree(result.schema.root, flags, {
|
|
1294
|
-
isLast: true,
|
|
1295
|
-
prefix: "",
|
|
1296
|
-
useColor,
|
|
1297
|
-
formatDimText,
|
|
1298
|
-
isRoot: true
|
|
1299
|
-
});
|
|
1300
|
-
const prefixedTreeLines = treeLines.map((line) => `${prefix}${line}`);
|
|
1301
|
-
lines.push(...prefixedTreeLines);
|
|
1302
|
-
if (isVerbose(flags, 1)) {
|
|
1303
|
-
lines.push(`${prefix}${formatDimText(` Total time: ${result.timings.total}ms`)}`);
|
|
1304
|
-
lines.push(
|
|
1305
|
-
`${prefix}${formatDimText(` pass=${result.schema.counts.pass} warn=${result.schema.counts.warn} fail=${result.schema.counts.fail}`)}`
|
|
1306
|
-
);
|
|
1307
|
-
}
|
|
1308
|
-
lines.push("");
|
|
1309
|
-
if (result.ok) {
|
|
1310
|
-
lines.push(`${prefix}${formatGreen("\u2714")} ${result.summary}`);
|
|
1311
|
-
} else {
|
|
1312
|
-
const codeText = result.code ? ` (${result.code})` : "";
|
|
1313
|
-
lines.push(`${prefix}${formatRed("\u2716")} ${result.summary}${codeText}`);
|
|
1314
|
-
}
|
|
1315
|
-
return lines.join("\n");
|
|
1316
|
-
}
|
|
1317
|
-
function formatSchemaVerifyJson(result) {
|
|
1318
|
-
return JSON.stringify(result, null, 2);
|
|
1319
|
-
}
|
|
1320
|
-
function formatSignOutput(result, flags) {
|
|
1321
|
-
if (flags.quiet) {
|
|
1322
|
-
return "";
|
|
1323
|
-
}
|
|
1324
|
-
const lines = [];
|
|
1325
|
-
const prefix = createPrefix(flags);
|
|
1326
|
-
const useColor = flags.color !== false;
|
|
1327
|
-
const formatGreen = createColorFormatter(useColor, green);
|
|
1328
|
-
const formatDimText = (text) => formatDim(useColor, text);
|
|
1329
|
-
if (result.ok) {
|
|
1330
|
-
lines.push(`${prefix}${formatGreen("\u2714")} Database signed`);
|
|
1331
|
-
const previousHash = result.marker.previous?.coreHash ?? "none";
|
|
1332
|
-
const currentHash = result.contract.coreHash;
|
|
1333
|
-
lines.push(`${prefix}${formatDimText(` from: ${previousHash}`)}`);
|
|
1334
|
-
lines.push(`${prefix}${formatDimText(` to: ${currentHash}`)}`);
|
|
1335
|
-
if (isVerbose(flags, 1)) {
|
|
1336
|
-
if (result.contract.profileHash) {
|
|
1337
|
-
lines.push(`${prefix}${formatDimText(` profileHash: ${result.contract.profileHash}`)}`);
|
|
1338
|
-
}
|
|
1339
|
-
if (result.marker.previous?.profileHash) {
|
|
1340
|
-
lines.push(
|
|
1341
|
-
`${prefix}${formatDimText(` previous profileHash: ${result.marker.previous.profileHash}`)}`
|
|
1342
|
-
);
|
|
1343
|
-
}
|
|
1344
|
-
lines.push(`${prefix}${formatDimText(` Total time: ${result.timings.total}ms`)}`);
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
return lines.join("\n");
|
|
1348
|
-
}
|
|
1349
|
-
function formatSignJson(result) {
|
|
1350
|
-
return JSON.stringify(result, null, 2);
|
|
1351
|
-
}
|
|
1352
|
-
function formatDbInitPlanOutput(result, flags) {
|
|
1353
|
-
if (flags.quiet) {
|
|
1354
|
-
return "";
|
|
1355
|
-
}
|
|
1356
|
-
const lines = [];
|
|
1357
|
-
const prefix = createPrefix(flags);
|
|
1358
|
-
const useColor = flags.color !== false;
|
|
1359
|
-
const formatGreen = createColorFormatter(useColor, green);
|
|
1360
|
-
const formatDimText = (text) => formatDim(useColor, text);
|
|
1361
|
-
const operationCount = result.plan?.operations.length ?? 0;
|
|
1362
|
-
lines.push(`${prefix}${formatGreen("\u2714")} Planned ${operationCount} operation(s)`);
|
|
1363
|
-
if (result.plan?.operations && result.plan.operations.length > 0) {
|
|
1364
|
-
lines.push(`${prefix}${formatDimText("\u2502")}`);
|
|
1365
|
-
for (let i = 0; i < result.plan.operations.length; i++) {
|
|
1366
|
-
const op = result.plan.operations[i];
|
|
1367
|
-
if (!op) continue;
|
|
1368
|
-
const isLast = i === result.plan.operations.length - 1;
|
|
1369
|
-
const treeChar = isLast ? "\u2514" : "\u251C";
|
|
1370
|
-
const opClass = formatDimText(`[${op.operationClass}]`);
|
|
1371
|
-
lines.push(`${prefix}${formatDimText(treeChar)}\u2500 ${op.label} ${opClass}`);
|
|
1372
|
-
}
|
|
1373
|
-
}
|
|
1374
|
-
if (result.plan?.destination) {
|
|
1375
|
-
lines.push(`${prefix}`);
|
|
1376
|
-
lines.push(
|
|
1377
|
-
`${prefix}${formatDimText(`Destination hash: ${result.plan.destination.coreHash}`)}`
|
|
1378
|
-
);
|
|
1379
|
-
}
|
|
1380
|
-
if (isVerbose(flags, 1)) {
|
|
1381
|
-
lines.push(`${prefix}${formatDimText(`Total time: ${result.timings.total}ms`)}`);
|
|
1382
|
-
}
|
|
1383
|
-
lines.push(`${prefix}`);
|
|
1384
|
-
lines.push(`${prefix}${formatDimText("This is a dry run. No changes were applied.")}`);
|
|
1385
|
-
lines.push(`${prefix}${formatDimText("Run without --plan to apply changes.")}`);
|
|
1386
|
-
return lines.join("\n");
|
|
1387
|
-
}
|
|
1388
|
-
function formatDbInitApplyOutput(result, flags) {
|
|
1389
|
-
if (flags.quiet) {
|
|
1390
|
-
return "";
|
|
1391
|
-
}
|
|
1392
|
-
const lines = [];
|
|
1393
|
-
const prefix = createPrefix(flags);
|
|
1394
|
-
const useColor = flags.color !== false;
|
|
1395
|
-
const formatGreen = createColorFormatter(useColor, green);
|
|
1396
|
-
const formatDimText = (text) => formatDim(useColor, text);
|
|
1397
|
-
if (result.ok) {
|
|
1398
|
-
const executed = result.execution?.operationsExecuted ?? 0;
|
|
1399
|
-
lines.push(`${prefix}${formatGreen("\u2714")} Applied ${executed} operation(s)`);
|
|
1400
|
-
if (result.marker) {
|
|
1401
|
-
lines.push(`${prefix}${formatDimText(` Marker written: ${result.marker.coreHash}`)}`);
|
|
1402
|
-
if (result.marker.profileHash) {
|
|
1403
|
-
lines.push(`${prefix}${formatDimText(` Profile hash: ${result.marker.profileHash}`)}`);
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
if (isVerbose(flags, 1)) {
|
|
1407
|
-
lines.push(`${prefix}${formatDimText(` Total time: ${result.timings.total}ms`)}`);
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
return lines.join("\n");
|
|
1411
|
-
}
|
|
1412
|
-
function formatDbInitJson(result) {
|
|
1413
|
-
return JSON.stringify(result, null, 2);
|
|
1414
|
-
}
|
|
1415
|
-
var LEFT_COLUMN_WIDTH = 20;
|
|
1416
|
-
var RIGHT_COLUMN_MIN_WIDTH = 40;
|
|
1417
|
-
var RIGHT_COLUMN_MAX_WIDTH = 90;
|
|
1418
|
-
function getTerminalWidth() {
|
|
1419
|
-
const terminalWidth = process.stdout.columns;
|
|
1420
|
-
const defaultWidth = Number.parseInt(process.env["CLI_WIDTH"] || "80", 10);
|
|
1421
|
-
return terminalWidth || defaultWidth;
|
|
1422
|
-
}
|
|
1423
|
-
function calculateRightColumnWidth() {
|
|
1424
|
-
const terminalWidth = getTerminalWidth();
|
|
1425
|
-
const availableWidth = terminalWidth - 2 - LEFT_COLUMN_WIDTH - 2;
|
|
1426
|
-
return Math.max(RIGHT_COLUMN_MIN_WIDTH, Math.min(availableWidth, RIGHT_COLUMN_MAX_WIDTH));
|
|
1427
|
-
}
|
|
1428
|
-
function createPrismaNextBadge(useColor) {
|
|
1429
|
-
if (!useColor) {
|
|
1430
|
-
return "prisma-next";
|
|
1431
|
-
}
|
|
1432
|
-
const text = " prisma-next ";
|
|
1433
|
-
const body = bgGreen(bold(text));
|
|
1434
|
-
const separator = "\uE0B0";
|
|
1435
|
-
const tip = green(separator);
|
|
1436
|
-
return `${body}${tip}`;
|
|
1437
|
-
}
|
|
1438
|
-
function createPadFunction() {
|
|
1439
|
-
return (s, w) => s + " ".repeat(Math.max(0, w - s.length));
|
|
1440
|
-
}
|
|
1441
|
-
function formatHeaderLine(options) {
|
|
1442
|
-
if (options.operation) {
|
|
1443
|
-
return `${options.brand} ${options.operation} \u2192 ${options.intent}`;
|
|
1444
|
-
}
|
|
1445
|
-
return `${options.brand} ${options.intent}`;
|
|
1446
|
-
}
|
|
1447
|
-
function formatReadMoreLine(options) {
|
|
1448
|
-
const pad = createPadFunction();
|
|
1449
|
-
const labelPadded = pad("Read more", options.maxLabelWidth);
|
|
1450
|
-
const valueColored = options.useColor ? blue(options.url) : options.url;
|
|
1451
|
-
return `${options.formatDimText("\u2502")} ${labelPadded} ${valueColored}`;
|
|
1452
|
-
}
|
|
1453
|
-
function padToFixedWidth(text, width) {
|
|
1454
|
-
const actualWidth = stringWidth(text);
|
|
1455
|
-
const padding = Math.max(0, width - actualWidth);
|
|
1456
|
-
return text + " ".repeat(padding);
|
|
1457
|
-
}
|
|
1458
|
-
function wrapTextAnsi(text, width) {
|
|
1459
|
-
const wrapped = wrapAnsi(text, width, { hard: false, trim: true });
|
|
1460
|
-
return wrapped.split("\n");
|
|
1461
|
-
}
|
|
1462
|
-
function formatDefaultValue(value, useColor) {
|
|
1463
|
-
const valueStr = String(value);
|
|
1464
|
-
const defaultText = `default: ${valueStr}`;
|
|
1465
|
-
return useColor ? dim(defaultText) : defaultText;
|
|
1466
|
-
}
|
|
1467
|
-
function renderCommandTree(options) {
|
|
1468
|
-
const { commands, useColor, formatDimText, hasItemsAfter, continuationPrefix } = options;
|
|
1469
|
-
const lines = [];
|
|
1470
|
-
if (commands.length === 0) {
|
|
1471
|
-
return lines;
|
|
1472
|
-
}
|
|
1473
|
-
for (let i = 0; i < commands.length; i++) {
|
|
1474
|
-
const cmd = commands[i];
|
|
1475
|
-
if (!cmd) continue;
|
|
1476
|
-
const subcommands = cmd.commands.filter((subcmd) => !subcmd.name().startsWith("_"));
|
|
1477
|
-
const isLastCommand = i === commands.length - 1;
|
|
1478
|
-
if (subcommands.length > 0) {
|
|
1479
|
-
const prefix = isLastCommand && !hasItemsAfter ? formatDimText("\u2514") : formatDimText("\u251C");
|
|
1480
|
-
const treePrefix = `${prefix}\u2500 `;
|
|
1481
|
-
const treePrefixWidth = stringWidth(stripAnsi(treePrefix));
|
|
1482
|
-
const remainingWidth = LEFT_COLUMN_WIDTH - treePrefixWidth;
|
|
1483
|
-
const commandNamePadded = padToFixedWidth(cmd.name(), remainingWidth);
|
|
1484
|
-
const commandNameColored = useColor ? cyan(commandNamePadded) : commandNamePadded;
|
|
1485
|
-
lines.push(`${formatDimText("\u2502")} ${treePrefix}${commandNameColored}`);
|
|
1486
|
-
for (let j = 0; j < subcommands.length; j++) {
|
|
1487
|
-
const subcmd = subcommands[j];
|
|
1488
|
-
if (!subcmd) continue;
|
|
1489
|
-
const isLastSubcommand = j === subcommands.length - 1;
|
|
1490
|
-
const shortDescription = subcmd.description() || "";
|
|
1491
|
-
const treeChar = isLastSubcommand ? "\u2514" : "\u251C";
|
|
1492
|
-
const continuation = continuationPrefix ?? (isLastCommand && isLastSubcommand && !hasItemsAfter ? " " : formatDimText("\u2502"));
|
|
1493
|
-
const continuationStr = continuation === " " ? " " : continuation;
|
|
1494
|
-
const subTreePrefix = `${continuationStr} ${formatDimText(treeChar)}\u2500 `;
|
|
1495
|
-
const subTreePrefixWidth = stringWidth(stripAnsi(subTreePrefix));
|
|
1496
|
-
const subRemainingWidth = LEFT_COLUMN_WIDTH - subTreePrefixWidth;
|
|
1497
|
-
const subcommandNamePadded = padToFixedWidth(subcmd.name(), subRemainingWidth);
|
|
1498
|
-
const subcommandNameColored = useColor ? cyan(subcommandNamePadded) : subcommandNamePadded;
|
|
1499
|
-
lines.push(
|
|
1500
|
-
`${formatDimText("\u2502")} ${subTreePrefix}${subcommandNameColored} ${shortDescription}`
|
|
1501
|
-
);
|
|
1502
|
-
}
|
|
1503
|
-
} else {
|
|
1504
|
-
const prefix = isLastCommand && !hasItemsAfter ? formatDimText("\u2514") : formatDimText("\u251C");
|
|
1505
|
-
const treePrefix = `${prefix}\u2500 `;
|
|
1506
|
-
const treePrefixWidth = stringWidth(stripAnsi(treePrefix));
|
|
1507
|
-
const remainingWidth = LEFT_COLUMN_WIDTH - treePrefixWidth;
|
|
1508
|
-
const commandNamePadded = padToFixedWidth(cmd.name(), remainingWidth);
|
|
1509
|
-
const commandNameColored = useColor ? cyan(commandNamePadded) : commandNamePadded;
|
|
1510
|
-
const shortDescription = cmd.description() || "";
|
|
1511
|
-
lines.push(`${formatDimText("\u2502")} ${treePrefix}${commandNameColored} ${shortDescription}`);
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1514
|
-
return lines;
|
|
1515
|
-
}
|
|
1516
|
-
function formatMultilineDescription(options) {
|
|
1517
|
-
const lines = [];
|
|
1518
|
-
const formatGreen = (text) => options.useColor ? green(text) : text;
|
|
1519
|
-
const rightColumnWidth = calculateRightColumnWidth();
|
|
1520
|
-
const totalWidth = 2 + LEFT_COLUMN_WIDTH + 2 + rightColumnWidth;
|
|
1521
|
-
const wrapWidth = totalWidth - 2;
|
|
1522
|
-
for (const descLine of options.descriptionLines) {
|
|
1523
|
-
const formattedLine = descLine.replace(/Prisma Next/g, (match) => formatGreen(match));
|
|
1524
|
-
const wrappedLines = wrapTextAnsi(formattedLine, wrapWidth);
|
|
1525
|
-
for (const wrappedLine of wrappedLines) {
|
|
1526
|
-
lines.push(`${options.formatDimText("\u2502")} ${wrappedLine}`);
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
return lines;
|
|
1530
|
-
}
|
|
1531
|
-
function formatStyledHeader(options) {
|
|
1532
|
-
const lines = [];
|
|
1533
|
-
const useColor = options.flags.color !== false;
|
|
1534
|
-
const formatDimText = (text) => formatDim(useColor, text);
|
|
1535
|
-
const brand = createPrismaNextBadge(useColor);
|
|
1536
|
-
const operation = useColor ? bold(options.command) : options.command;
|
|
1537
|
-
const intent = formatDimText(options.description);
|
|
1538
|
-
lines.push(formatHeaderLine({ brand, operation, intent }));
|
|
1539
|
-
lines.push(formatDimText("\u2502"));
|
|
1540
|
-
for (const detail of options.details) {
|
|
1541
|
-
const labelWithColon = `${detail.label}:`;
|
|
1542
|
-
const labelPadded = padToFixedWidth(labelWithColon, LEFT_COLUMN_WIDTH);
|
|
1543
|
-
const labelColored = useColor ? cyan(labelPadded) : labelPadded;
|
|
1544
|
-
lines.push(`${formatDimText("\u2502")} ${labelColored} ${detail.value}`);
|
|
1545
|
-
}
|
|
1546
|
-
if (options.url) {
|
|
1547
|
-
lines.push(formatDimText("\u2502"));
|
|
1548
|
-
lines.push(
|
|
1549
|
-
formatReadMoreLine({
|
|
1550
|
-
url: options.url,
|
|
1551
|
-
maxLabelWidth: LEFT_COLUMN_WIDTH,
|
|
1552
|
-
useColor,
|
|
1553
|
-
formatDimText
|
|
1554
|
-
})
|
|
1555
|
-
);
|
|
1556
|
-
}
|
|
1557
|
-
lines.push(formatDimText("\u2514"));
|
|
1558
|
-
return `${lines.join("\n")}
|
|
1559
|
-
`;
|
|
1560
|
-
}
|
|
1561
|
-
function formatSuccessMessage(flags) {
|
|
1562
|
-
const useColor = flags.color !== false;
|
|
1563
|
-
const formatGreen = createColorFormatter(useColor, green);
|
|
1564
|
-
return `${formatGreen("\u2714")} Success`;
|
|
1565
|
-
}
|
|
1566
|
-
function getCommandDocsUrl(commandPath) {
|
|
1567
|
-
const docsMap = {
|
|
1568
|
-
"contract emit": "https://pris.ly/contract-emit",
|
|
1569
|
-
"db verify": "https://pris.ly/db-verify"
|
|
1570
|
-
};
|
|
1571
|
-
return docsMap[commandPath];
|
|
1572
|
-
}
|
|
1573
|
-
function buildCommandPath(command) {
|
|
1574
|
-
const parts = [];
|
|
1575
|
-
let current = command;
|
|
1576
|
-
while (current && current.name() !== "prisma-next") {
|
|
1577
|
-
parts.unshift(current.name());
|
|
1578
|
-
current = current.parent ?? void 0;
|
|
1579
|
-
}
|
|
1580
|
-
return parts.join(" ");
|
|
1581
|
-
}
|
|
1582
|
-
function formatCommandHelp(options) {
|
|
1583
|
-
const { command, flags } = options;
|
|
1584
|
-
const lines = [];
|
|
1585
|
-
const useColor = flags.color !== false;
|
|
1586
|
-
const formatDimText = (text) => formatDim(useColor, text);
|
|
1587
|
-
const commandPath = buildCommandPath(command);
|
|
1588
|
-
const shortDescription = command.description() || "";
|
|
1589
|
-
const longDescription = getLongDescription(command);
|
|
1590
|
-
const brand = createPrismaNextBadge(useColor);
|
|
1591
|
-
const operation = useColor ? bold(commandPath) : commandPath;
|
|
1592
|
-
const intent = formatDimText(shortDescription);
|
|
1593
|
-
lines.push(formatHeaderLine({ brand, operation, intent }));
|
|
1594
|
-
lines.push(formatDimText("\u2502"));
|
|
1595
|
-
const optionsList = command.options.map((opt) => {
|
|
1596
|
-
const flags2 = opt.flags;
|
|
1597
|
-
const description = opt.description || "";
|
|
1598
|
-
const defaultValue = opt.defaultValue;
|
|
1599
|
-
return { flags: flags2, description, defaultValue };
|
|
1600
|
-
});
|
|
1601
|
-
const subcommands = command.commands.filter((cmd) => !cmd.name().startsWith("_"));
|
|
1602
|
-
if (subcommands.length > 0) {
|
|
1603
|
-
const hasItemsAfter = optionsList.length > 0;
|
|
1604
|
-
const treeLines = renderCommandTree({
|
|
1605
|
-
commands: subcommands,
|
|
1606
|
-
useColor,
|
|
1607
|
-
formatDimText,
|
|
1608
|
-
hasItemsAfter
|
|
1609
|
-
});
|
|
1610
|
-
lines.push(...treeLines);
|
|
1611
|
-
}
|
|
1612
|
-
if (subcommands.length > 0 && optionsList.length > 0) {
|
|
1613
|
-
lines.push(formatDimText("\u2502"));
|
|
1614
|
-
}
|
|
1615
|
-
if (optionsList.length > 0) {
|
|
1616
|
-
for (const opt of optionsList) {
|
|
1617
|
-
const flagsPadded = padToFixedWidth(opt.flags, LEFT_COLUMN_WIDTH);
|
|
1618
|
-
let flagsColored = flagsPadded;
|
|
1619
|
-
if (useColor) {
|
|
1620
|
-
flagsColored = flagsPadded.replace(/(<[^>]+>)/g, (match) => magenta(match));
|
|
1621
|
-
flagsColored = cyan(flagsColored);
|
|
1622
|
-
}
|
|
1623
|
-
const rightColumnWidth = calculateRightColumnWidth();
|
|
1624
|
-
const wrappedDescription = wrapTextAnsi(opt.description, rightColumnWidth);
|
|
1625
|
-
lines.push(`${formatDimText("\u2502")} ${flagsColored} ${wrappedDescription[0] || ""}`);
|
|
1626
|
-
for (let i = 1; i < wrappedDescription.length; i++) {
|
|
1627
|
-
const emptyLabel = " ".repeat(LEFT_COLUMN_WIDTH);
|
|
1628
|
-
lines.push(`${formatDimText("\u2502")} ${emptyLabel} ${wrappedDescription[i] || ""}`);
|
|
1629
|
-
}
|
|
1630
|
-
if (opt.defaultValue !== void 0) {
|
|
1631
|
-
const emptyLabel = " ".repeat(LEFT_COLUMN_WIDTH);
|
|
1632
|
-
const defaultText = formatDefaultValue(opt.defaultValue, useColor);
|
|
1633
|
-
lines.push(`${formatDimText("\u2502")} ${emptyLabel} ${defaultText}`);
|
|
1634
|
-
}
|
|
1635
|
-
}
|
|
1636
|
-
}
|
|
1637
|
-
const docsUrl = getCommandDocsUrl(commandPath);
|
|
1638
|
-
if (docsUrl) {
|
|
1639
|
-
lines.push(formatDimText("\u2502"));
|
|
1640
|
-
lines.push(
|
|
1641
|
-
formatReadMoreLine({
|
|
1642
|
-
url: docsUrl,
|
|
1643
|
-
maxLabelWidth: LEFT_COLUMN_WIDTH,
|
|
1644
|
-
useColor,
|
|
1645
|
-
formatDimText
|
|
1646
|
-
})
|
|
1647
|
-
);
|
|
1648
|
-
}
|
|
1649
|
-
if (longDescription) {
|
|
1650
|
-
lines.push(formatDimText("\u2502"));
|
|
1651
|
-
const descriptionLines = longDescription.split("\n").filter((line) => line.trim().length > 0);
|
|
1652
|
-
lines.push(...formatMultilineDescription({ descriptionLines, useColor, formatDimText }));
|
|
1653
|
-
}
|
|
1654
|
-
lines.push(formatDimText("\u2514"));
|
|
1655
|
-
return `${lines.join("\n")}
|
|
1656
|
-
`;
|
|
1657
|
-
}
|
|
1658
|
-
function formatRootHelp(options) {
|
|
1659
|
-
const { program: program2, flags } = options;
|
|
1660
|
-
const lines = [];
|
|
1661
|
-
const useColor = flags.color !== false;
|
|
1662
|
-
const formatDimText = (text) => formatDim(useColor, text);
|
|
1663
|
-
const brand = createPrismaNextBadge(useColor);
|
|
1664
|
-
const shortDescription = "Manage your data layer";
|
|
1665
|
-
const intent = formatDimText(shortDescription);
|
|
1666
|
-
lines.push(formatHeaderLine({ brand, operation: "", intent }));
|
|
1667
|
-
lines.push(formatDimText("\u2502"));
|
|
1668
|
-
const topLevelCommands = program2.commands.filter(
|
|
1669
|
-
(cmd) => !cmd.name().startsWith("_") && cmd.name() !== "help"
|
|
1670
|
-
);
|
|
1671
|
-
const globalOptions = program2.options.map((opt) => {
|
|
1672
|
-
const flags2 = opt.flags;
|
|
1673
|
-
const description = opt.description || "";
|
|
1674
|
-
const defaultValue = opt.defaultValue;
|
|
1675
|
-
return { flags: flags2, description, defaultValue };
|
|
1676
|
-
});
|
|
1677
|
-
if (topLevelCommands.length > 0) {
|
|
1678
|
-
const hasItemsAfter = globalOptions.length > 0;
|
|
1679
|
-
const treeLines = renderCommandTree({
|
|
1680
|
-
commands: topLevelCommands,
|
|
1681
|
-
useColor,
|
|
1682
|
-
formatDimText,
|
|
1683
|
-
hasItemsAfter
|
|
1684
|
-
});
|
|
1685
|
-
lines.push(...treeLines);
|
|
1686
|
-
}
|
|
1687
|
-
if (topLevelCommands.length > 0 && globalOptions.length > 0) {
|
|
1688
|
-
lines.push(formatDimText("\u2502"));
|
|
1689
|
-
}
|
|
1690
|
-
if (globalOptions.length > 0) {
|
|
1691
|
-
for (const opt of globalOptions) {
|
|
1692
|
-
const flagsPadded = padToFixedWidth(opt.flags, LEFT_COLUMN_WIDTH);
|
|
1693
|
-
let flagsColored = flagsPadded;
|
|
1694
|
-
if (useColor) {
|
|
1695
|
-
flagsColored = flagsPadded.replace(/(<[^>]+>)/g, (match) => magenta(match));
|
|
1696
|
-
flagsColored = cyan(flagsColored);
|
|
1697
|
-
}
|
|
1698
|
-
const rightColumnWidth = calculateRightColumnWidth();
|
|
1699
|
-
const wrappedDescription = wrapTextAnsi(opt.description, rightColumnWidth);
|
|
1700
|
-
lines.push(`${formatDimText("\u2502")} ${flagsColored} ${wrappedDescription[0] || ""}`);
|
|
1701
|
-
for (let i = 1; i < wrappedDescription.length; i++) {
|
|
1702
|
-
const emptyLabel = " ".repeat(LEFT_COLUMN_WIDTH);
|
|
1703
|
-
lines.push(`${formatDimText("\u2502")} ${emptyLabel} ${wrappedDescription[i] || ""}`);
|
|
1704
|
-
}
|
|
1705
|
-
if (opt.defaultValue !== void 0) {
|
|
1706
|
-
const emptyLabel = " ".repeat(LEFT_COLUMN_WIDTH);
|
|
1707
|
-
const defaultText = formatDefaultValue(opt.defaultValue, useColor);
|
|
1708
|
-
lines.push(`${formatDimText("\u2502")} ${emptyLabel} ${defaultText}`);
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
|
-
}
|
|
1712
|
-
const formatGreen = (text) => useColor ? green(text) : text;
|
|
1713
|
-
const descriptionLines = [
|
|
1714
|
-
`Use ${formatGreen("Prisma Next")} to define your data layer as a contract. Sign your database and application with the same contract to guarantee compatibility. Plan and apply migrations to safely evolve your schema.`
|
|
1715
|
-
];
|
|
1716
|
-
if (descriptionLines.length > 0) {
|
|
1717
|
-
lines.push(formatDimText("\u2502"));
|
|
1718
|
-
lines.push(...formatMultilineDescription({ descriptionLines, useColor, formatDimText }));
|
|
1719
|
-
}
|
|
1720
|
-
lines.push(formatDimText("\u2514"));
|
|
1721
|
-
return `${lines.join("\n")}
|
|
1722
|
-
`;
|
|
1723
|
-
}
|
|
1724
|
-
|
|
1725
|
-
// src/utils/progress-adapter.ts
|
|
1726
|
-
import ora from "ora";
|
|
1727
|
-
function createProgressAdapter(options) {
|
|
1728
|
-
const { flags } = options;
|
|
1729
|
-
const shouldShowProgress = !flags.quiet && flags.json !== "object" && process.stdout.isTTY;
|
|
1730
|
-
if (!shouldShowProgress) {
|
|
1731
|
-
return () => {
|
|
1732
|
-
};
|
|
1733
|
-
}
|
|
1734
|
-
const activeSpans = /* @__PURE__ */ new Map();
|
|
1735
|
-
return (event) => {
|
|
1736
|
-
if (event.kind === "spanStart") {
|
|
1737
|
-
if (event.parentSpanId) {
|
|
1738
|
-
console.log(` \u2192 ${event.label}...`);
|
|
1739
|
-
return;
|
|
1740
|
-
}
|
|
1741
|
-
const spinner = ora({
|
|
1742
|
-
text: event.label,
|
|
1743
|
-
color: flags.color !== false ? "cyan" : false
|
|
1744
|
-
}).start();
|
|
1745
|
-
activeSpans.set(event.spanId, {
|
|
1746
|
-
spinner,
|
|
1747
|
-
startTime: Date.now()
|
|
1748
|
-
});
|
|
1749
|
-
} else if (event.kind === "spanEnd") {
|
|
1750
|
-
const spanState = activeSpans.get(event.spanId);
|
|
1751
|
-
if (spanState) {
|
|
1752
|
-
const elapsed = Date.now() - spanState.startTime;
|
|
1753
|
-
if (event.outcome === "error") {
|
|
1754
|
-
spanState.spinner.fail(`${spanState.spinner.text} (failed)`);
|
|
1755
|
-
} else if (event.outcome === "skipped") {
|
|
1756
|
-
spanState.spinner.info(`${spanState.spinner.text} (skipped)`);
|
|
1757
|
-
} else {
|
|
1758
|
-
spanState.spinner.succeed(`${spanState.spinner.text} (${elapsed}ms)`);
|
|
1759
|
-
}
|
|
1760
|
-
activeSpans.delete(event.spanId);
|
|
1761
|
-
}
|
|
1762
|
-
}
|
|
1763
|
-
};
|
|
1764
|
-
}
|
|
1765
|
-
|
|
1766
|
-
// src/utils/result-handler.ts
|
|
1767
|
-
function handleResult(result, flags, onSuccess) {
|
|
1768
|
-
if (result.ok) {
|
|
1769
|
-
if (onSuccess) {
|
|
1770
|
-
onSuccess(result.value);
|
|
1771
|
-
}
|
|
1772
|
-
return 0;
|
|
1773
|
-
}
|
|
1774
|
-
const envelope = result.failure.toEnvelope();
|
|
1775
|
-
if (flags.json) {
|
|
1776
|
-
console.error(formatErrorJson(envelope));
|
|
1777
|
-
} else {
|
|
1778
|
-
console.error(formatErrorOutput(envelope, flags));
|
|
1779
|
-
}
|
|
1780
|
-
const exitCode = result.failure.domain === "CLI" ? 2 : 1;
|
|
1781
|
-
return exitCode;
|
|
1782
|
-
}
|
|
1783
|
-
|
|
1784
|
-
// src/commands/contract-emit.ts
|
|
1785
|
-
function mapEmitFailure(failure) {
|
|
1786
|
-
if (failure.code === "CONTRACT_SOURCE_INVALID") {
|
|
1787
|
-
return errorRuntime(failure.summary, {
|
|
1788
|
-
why: failure.why ?? "Contract source is invalid",
|
|
1789
|
-
fix: "Check your contract source configuration in prisma-next.config.ts"
|
|
1790
|
-
});
|
|
1791
|
-
}
|
|
1792
|
-
if (failure.code === "EMIT_FAILED") {
|
|
1793
|
-
return errorRuntime(failure.summary, {
|
|
1794
|
-
why: failure.why ?? "Failed to emit contract",
|
|
1795
|
-
fix: "Check your contract configuration and ensure the source is valid"
|
|
1796
|
-
});
|
|
1797
|
-
}
|
|
1798
|
-
const exhaustive = failure.code;
|
|
1799
|
-
throw new Error(`Unhandled EmitFailure code: ${exhaustive}`);
|
|
1800
|
-
}
|
|
1801
|
-
async function executeContractEmitCommand(options, flags, startTime) {
|
|
1802
|
-
let config;
|
|
1803
|
-
try {
|
|
1804
|
-
config = await loadConfig(options.config);
|
|
1805
|
-
} catch (error) {
|
|
1806
|
-
if (error instanceof CliStructuredError) {
|
|
1807
|
-
return notOk3(error);
|
|
1808
|
-
}
|
|
1809
|
-
return notOk3(
|
|
1810
|
-
errorUnexpected2(error instanceof Error ? error.message : String(error), {
|
|
1811
|
-
why: "Failed to load config"
|
|
1812
|
-
})
|
|
1813
|
-
);
|
|
1814
|
-
}
|
|
1815
|
-
if (!config.contract) {
|
|
1816
|
-
return notOk3(
|
|
1817
|
-
errorContractConfigMissing2({
|
|
1818
|
-
why: "Config.contract is required for emit. Define it in your config: contract: { source: ..., output: ..., types: ... }"
|
|
1819
|
-
})
|
|
1820
|
-
);
|
|
1821
|
-
}
|
|
1822
|
-
const contractConfig = config.contract;
|
|
1823
|
-
if (!contractConfig.output || !contractConfig.types) {
|
|
1824
|
-
return notOk3(
|
|
1825
|
-
errorContractConfigMissing2({
|
|
1826
|
-
why: "Contract config must have output and types paths. This should not happen if defineConfig() was used."
|
|
1827
|
-
})
|
|
1828
|
-
);
|
|
1829
|
-
}
|
|
1830
|
-
const outputJsonPath = resolve2(contractConfig.output);
|
|
1831
|
-
const outputDtsPath = resolve2(contractConfig.types);
|
|
1832
|
-
if (flags.json !== "object" && !flags.quiet) {
|
|
1833
|
-
const configPath = options.config ? relative2(process.cwd(), resolve2(options.config)) : "prisma-next.config.ts";
|
|
1834
|
-
const contractPath = relative2(process.cwd(), outputJsonPath);
|
|
1835
|
-
const typesPath = relative2(process.cwd(), outputDtsPath);
|
|
1836
|
-
const header = formatStyledHeader({
|
|
1837
|
-
command: "contract emit",
|
|
1838
|
-
description: "Write your contract to JSON and sign it",
|
|
1839
|
-
url: "https://pris.ly/contract-emit",
|
|
1840
|
-
details: [
|
|
1841
|
-
{ label: "config", value: configPath },
|
|
1842
|
-
{ label: "contract", value: contractPath },
|
|
1843
|
-
{ label: "types", value: typesPath }
|
|
1844
|
-
],
|
|
1845
|
-
flags
|
|
1846
|
-
});
|
|
1847
|
-
console.log(header);
|
|
1848
|
-
}
|
|
1849
|
-
const client = createControlClient({
|
|
1850
|
-
family: config.family,
|
|
1851
|
-
target: config.target,
|
|
1852
|
-
adapter: config.adapter,
|
|
1853
|
-
extensionPacks: config.extensionPacks ?? []
|
|
1854
|
-
});
|
|
1855
|
-
const onProgress = createProgressAdapter({ flags });
|
|
1856
|
-
try {
|
|
1857
|
-
const source = typeof contractConfig.source === "function" ? { kind: "loader", load: contractConfig.source } : { kind: "value", value: contractConfig.source };
|
|
1858
|
-
const result = await client.emit({
|
|
1859
|
-
contractConfig: {
|
|
1860
|
-
source,
|
|
1861
|
-
output: outputJsonPath,
|
|
1862
|
-
types: outputDtsPath
|
|
1863
|
-
},
|
|
1864
|
-
onProgress
|
|
1865
|
-
});
|
|
1866
|
-
if (!result.ok) {
|
|
1867
|
-
return notOk3(mapEmitFailure(result.failure));
|
|
1868
|
-
}
|
|
1869
|
-
mkdirSync(dirname2(outputJsonPath), { recursive: true });
|
|
1870
|
-
mkdirSync(dirname2(outputDtsPath), { recursive: true });
|
|
1871
|
-
writeFileSync(outputJsonPath, result.value.contractJson, "utf-8");
|
|
1872
|
-
writeFileSync(outputDtsPath, result.value.contractDts, "utf-8");
|
|
1873
|
-
if (!flags.quiet && flags.json !== "object" && process.stdout.isTTY) {
|
|
1874
|
-
console.log("");
|
|
1875
|
-
}
|
|
1876
|
-
const emitResult = {
|
|
1877
|
-
coreHash: result.value.coreHash,
|
|
1878
|
-
profileHash: result.value.profileHash,
|
|
1879
|
-
outDir: dirname2(outputJsonPath),
|
|
1880
|
-
files: {
|
|
1881
|
-
json: outputJsonPath,
|
|
1882
|
-
dts: outputDtsPath
|
|
1883
|
-
},
|
|
1884
|
-
timings: { total: Date.now() - startTime }
|
|
1885
|
-
};
|
|
1886
|
-
return ok3(emitResult);
|
|
1887
|
-
} catch (error) {
|
|
1888
|
-
if (CliStructuredError.is(error)) {
|
|
1889
|
-
return notOk3(error);
|
|
1890
|
-
}
|
|
1891
|
-
return notOk3(
|
|
1892
|
-
errorUnexpected2("Unexpected error during contract emit", {
|
|
1893
|
-
why: error instanceof Error ? error.message : String(error)
|
|
1894
|
-
})
|
|
1895
|
-
);
|
|
1896
|
-
} finally {
|
|
1897
|
-
await client.close();
|
|
1898
|
-
}
|
|
1899
|
-
}
|
|
1900
|
-
function createContractEmitCommand() {
|
|
1901
|
-
const command = new Command("emit");
|
|
1902
|
-
setCommandDescriptions(
|
|
1903
|
-
command,
|
|
1904
|
-
"Write your contract to JSON and sign it",
|
|
1905
|
-
"Reads your contract source (TypeScript or Prisma schema) and emits contract.json and\ncontract.d.ts. The contract.json contains the canonical contract structure, and\ncontract.d.ts provides TypeScript types for type-safe query building."
|
|
1906
|
-
);
|
|
1907
|
-
command.configureHelp({
|
|
1908
|
-
formatHelp: (cmd) => {
|
|
1909
|
-
const flags = parseGlobalFlags({});
|
|
1910
|
-
return formatCommandHelp({ command: cmd, flags });
|
|
1911
|
-
}
|
|
1912
|
-
}).option("--config <path>", "Path to prisma-next.config.ts").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) => {
|
|
1913
|
-
const flags = parseGlobalFlags(options);
|
|
1914
|
-
const startTime = Date.now();
|
|
1915
|
-
const result = await executeContractEmitCommand(options, flags, startTime);
|
|
1916
|
-
const exitCode = handleResult(result, flags, (emitResult) => {
|
|
1917
|
-
if (flags.json === "object") {
|
|
1918
|
-
console.log(formatEmitJson(emitResult));
|
|
1919
|
-
} else {
|
|
1920
|
-
const output = formatEmitOutput(emitResult, flags);
|
|
1921
|
-
if (output) {
|
|
1922
|
-
console.log(output);
|
|
1923
|
-
}
|
|
1924
|
-
if (!flags.quiet) {
|
|
1925
|
-
console.log(formatSuccessMessage(flags));
|
|
1926
|
-
}
|
|
1927
|
-
}
|
|
1928
|
-
});
|
|
1929
|
-
process.exit(exitCode);
|
|
1930
|
-
});
|
|
1931
|
-
return command;
|
|
1932
|
-
}
|
|
1933
|
-
|
|
1934
|
-
// src/commands/db-init.ts
|
|
1935
|
-
import { readFile } from "fs/promises";
|
|
1936
|
-
import { relative as relative3, resolve as resolve3 } from "path";
|
|
1937
|
-
import { notOk as notOk4, ok as ok4 } from "@prisma-next/utils/result";
|
|
1938
|
-
import { Command as Command2 } from "commander";
|
|
1939
|
-
function mapDbInitFailure(failure) {
|
|
1940
|
-
if (failure.code === "PLANNING_FAILED") {
|
|
1941
|
-
return errorMigrationPlanningFailed({ conflicts: failure.conflicts ?? [] });
|
|
1942
|
-
}
|
|
1943
|
-
if (failure.code === "MARKER_ORIGIN_MISMATCH") {
|
|
1944
|
-
const mismatchParts = [];
|
|
1945
|
-
if (failure.marker?.coreHash !== failure.destination?.coreHash && failure.marker?.coreHash && failure.destination?.coreHash) {
|
|
1946
|
-
mismatchParts.push(
|
|
1947
|
-
`coreHash (marker: ${failure.marker.coreHash}, destination: ${failure.destination.coreHash})`
|
|
1948
|
-
);
|
|
1949
|
-
}
|
|
1950
|
-
if (failure.marker?.profileHash !== failure.destination?.profileHash && failure.marker?.profileHash && failure.destination?.profileHash) {
|
|
1951
|
-
mismatchParts.push(
|
|
1952
|
-
`profileHash (marker: ${failure.marker.profileHash}, destination: ${failure.destination.profileHash})`
|
|
1953
|
-
);
|
|
1954
|
-
}
|
|
1955
|
-
return errorRuntime(
|
|
1956
|
-
`Existing contract marker does not match plan destination.${mismatchParts.length > 0 ? ` Mismatch in ${mismatchParts.join(" and ")}.` : ""}`,
|
|
1957
|
-
{
|
|
1958
|
-
why: "Database has an existing contract marker that does not match the target contract",
|
|
1959
|
-
fix: "If bootstrapping, drop/reset the database then re-run `prisma-next db init`; otherwise reconcile schema/marker using your migration workflow",
|
|
1960
|
-
meta: {
|
|
1961
|
-
code: "MARKER_ORIGIN_MISMATCH",
|
|
1962
|
-
...failure.marker?.coreHash ? { markerCoreHash: failure.marker.coreHash } : {},
|
|
1963
|
-
...failure.destination?.coreHash ? { destinationCoreHash: failure.destination.coreHash } : {},
|
|
1964
|
-
...failure.marker?.profileHash ? { markerProfileHash: failure.marker.profileHash } : {},
|
|
1965
|
-
...failure.destination?.profileHash ? { destinationProfileHash: failure.destination.profileHash } : {}
|
|
1966
|
-
}
|
|
1967
|
-
}
|
|
1968
|
-
);
|
|
1969
|
-
}
|
|
1970
|
-
if (failure.code === "RUNNER_FAILED") {
|
|
1971
|
-
return errorRuntime(failure.summary, {
|
|
1972
|
-
why: failure.why ?? "Migration runner failed",
|
|
1973
|
-
fix: "Fix the schema mismatch (db init is additive-only), or drop/reset the database and re-run `prisma-next db init`",
|
|
1974
|
-
meta: {
|
|
1975
|
-
code: "RUNNER_FAILED",
|
|
1976
|
-
...failure.meta ?? {}
|
|
1977
|
-
}
|
|
1978
|
-
});
|
|
1979
|
-
}
|
|
1980
|
-
const exhaustive = failure.code;
|
|
1981
|
-
throw new Error(`Unhandled DbInitFailure code: ${exhaustive}`);
|
|
1982
|
-
}
|
|
1983
|
-
async function executeDbInitCommand(options, flags, startTime) {
|
|
1984
|
-
const config = await loadConfig(options.config);
|
|
1985
|
-
const configPath = options.config ? relative3(process.cwd(), resolve3(options.config)) : "prisma-next.config.ts";
|
|
1986
|
-
const contractPathAbsolute = config.contract?.output ? resolve3(config.contract.output) : resolve3("src/prisma/contract.json");
|
|
1987
|
-
const contractPath = relative3(process.cwd(), contractPathAbsolute);
|
|
1988
|
-
if (flags.json !== "object" && !flags.quiet) {
|
|
1989
|
-
const details = [
|
|
1990
|
-
{ label: "config", value: configPath },
|
|
1991
|
-
{ label: "contract", value: contractPath }
|
|
1992
|
-
];
|
|
1993
|
-
if (options.db) {
|
|
1994
|
-
details.push({ label: "database", value: options.db });
|
|
1995
|
-
}
|
|
1996
|
-
if (options.plan) {
|
|
1997
|
-
details.push({ label: "mode", value: "plan (dry run)" });
|
|
1998
|
-
}
|
|
1999
|
-
const header = formatStyledHeader({
|
|
2000
|
-
command: "db init",
|
|
2001
|
-
description: "Bootstrap a database to match the current contract",
|
|
2002
|
-
url: "https://pris.ly/db-init",
|
|
2003
|
-
details,
|
|
2004
|
-
flags
|
|
2005
|
-
});
|
|
2006
|
-
console.log(header);
|
|
2007
|
-
}
|
|
2008
|
-
let contractJsonContent;
|
|
2009
|
-
try {
|
|
2010
|
-
contractJsonContent = await readFile(contractPathAbsolute, "utf-8");
|
|
2011
|
-
} catch (error) {
|
|
2012
|
-
if (error instanceof Error && error.code === "ENOENT") {
|
|
2013
|
-
return notOk4(
|
|
2014
|
-
errorFileNotFound(contractPathAbsolute, {
|
|
2015
|
-
why: `Contract file not found at ${contractPathAbsolute}`,
|
|
2016
|
-
fix: `Run \`prisma-next contract emit\` to generate ${contractPath}, or update \`config.contract.output\` in ${configPath}`
|
|
2017
|
-
})
|
|
2018
|
-
);
|
|
2019
|
-
}
|
|
2020
|
-
return notOk4(
|
|
2021
|
-
errorUnexpected2(error instanceof Error ? error.message : String(error), {
|
|
2022
|
-
why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`
|
|
2023
|
-
})
|
|
2024
|
-
);
|
|
2025
|
-
}
|
|
2026
|
-
let contractJson;
|
|
2027
|
-
try {
|
|
2028
|
-
contractJson = JSON.parse(contractJsonContent);
|
|
2029
|
-
} catch (error) {
|
|
2030
|
-
return notOk4(
|
|
2031
|
-
errorContractValidationFailed(
|
|
2032
|
-
`Contract JSON is invalid: ${error instanceof Error ? error.message : String(error)}`,
|
|
2033
|
-
{ where: { path: contractPathAbsolute } }
|
|
2034
|
-
)
|
|
2035
|
-
);
|
|
2036
|
-
}
|
|
2037
|
-
const dbConnection = options.db ?? config.db?.connection;
|
|
2038
|
-
if (!dbConnection) {
|
|
2039
|
-
return notOk4(
|
|
2040
|
-
errorDatabaseConnectionRequired({
|
|
2041
|
-
why: `Database connection is required for db init (set db.connection in ${configPath}, or pass --db <url>)`
|
|
2042
|
-
})
|
|
2043
|
-
);
|
|
2044
|
-
}
|
|
2045
|
-
if (!config.driver) {
|
|
2046
|
-
return notOk4(errorDriverRequired({ why: "Config.driver is required for db init" }));
|
|
2047
|
-
}
|
|
2048
|
-
if (!config.target.migrations) {
|
|
2049
|
-
return notOk4(
|
|
2050
|
-
errorTargetMigrationNotSupported({
|
|
2051
|
-
why: `Target "${config.target.id}" does not support migrations`
|
|
2052
|
-
})
|
|
2053
|
-
);
|
|
2054
|
-
}
|
|
2055
|
-
const client = createControlClient({
|
|
2056
|
-
family: config.family,
|
|
2057
|
-
target: config.target,
|
|
2058
|
-
adapter: config.adapter,
|
|
2059
|
-
driver: config.driver,
|
|
2060
|
-
extensionPacks: config.extensionPacks ?? []
|
|
2061
|
-
});
|
|
2062
|
-
const onProgress = createProgressAdapter({ flags });
|
|
2063
|
-
try {
|
|
2064
|
-
const result = await client.dbInit({
|
|
2065
|
-
contractIR: contractJson,
|
|
2066
|
-
mode: options.plan ? "plan" : "apply",
|
|
2067
|
-
connection: dbConnection,
|
|
2068
|
-
onProgress
|
|
2069
|
-
});
|
|
2070
|
-
if (!result.ok) {
|
|
2071
|
-
return notOk4(mapDbInitFailure(result.failure));
|
|
2072
|
-
}
|
|
2073
|
-
const profileHash = result.value.marker?.profileHash;
|
|
2074
|
-
const dbInitResult = {
|
|
2075
|
-
ok: true,
|
|
2076
|
-
mode: result.value.mode,
|
|
2077
|
-
plan: {
|
|
2078
|
-
targetId: config.target.targetId,
|
|
2079
|
-
destination: {
|
|
2080
|
-
coreHash: result.value.marker?.coreHash ?? "",
|
|
2081
|
-
...profileHash ? { profileHash } : {}
|
|
2082
|
-
},
|
|
2083
|
-
operations: result.value.plan.operations.map((op) => ({
|
|
2084
|
-
id: op.id,
|
|
2085
|
-
label: op.label,
|
|
2086
|
-
operationClass: op.operationClass
|
|
2087
|
-
}))
|
|
2088
|
-
},
|
|
2089
|
-
...result.value.execution ? {
|
|
2090
|
-
execution: {
|
|
2091
|
-
operationsPlanned: result.value.execution.operationsPlanned,
|
|
2092
|
-
operationsExecuted: result.value.execution.operationsExecuted
|
|
2093
|
-
}
|
|
2094
|
-
} : {},
|
|
2095
|
-
...result.value.marker ? {
|
|
2096
|
-
marker: {
|
|
2097
|
-
coreHash: result.value.marker.coreHash,
|
|
2098
|
-
...result.value.marker.profileHash ? { profileHash: result.value.marker.profileHash } : {}
|
|
2099
|
-
}
|
|
2100
|
-
} : {},
|
|
2101
|
-
summary: result.value.summary,
|
|
2102
|
-
timings: { total: Date.now() - startTime }
|
|
2103
|
-
};
|
|
2104
|
-
return ok4(dbInitResult);
|
|
2105
|
-
} catch (error) {
|
|
2106
|
-
if (CliStructuredError.is(error)) {
|
|
2107
|
-
return notOk4(error);
|
|
2108
|
-
}
|
|
2109
|
-
return notOk4(
|
|
2110
|
-
errorUnexpected2(error instanceof Error ? error.message : String(error), {
|
|
2111
|
-
why: `Unexpected error during db init: ${error instanceof Error ? error.message : String(error)}`
|
|
2112
|
-
})
|
|
2113
|
-
);
|
|
2114
|
-
} finally {
|
|
2115
|
-
await client.close();
|
|
2116
|
-
}
|
|
2117
|
-
}
|
|
2118
|
-
function createDbInitCommand() {
|
|
2119
|
-
const command = new Command2("init");
|
|
2120
|
-
setCommandDescriptions(
|
|
2121
|
-
command,
|
|
2122
|
-
"Bootstrap a database to match the current contract and write the contract marker",
|
|
2123
|
-
"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."
|
|
2124
|
-
);
|
|
2125
|
-
command.configureHelp({
|
|
2126
|
-
formatHelp: (cmd) => {
|
|
2127
|
-
const flags = parseGlobalFlags({});
|
|
2128
|
-
return formatCommandHelp({ command: cmd, flags });
|
|
2129
|
-
}
|
|
2130
|
-
}).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) => {
|
|
2131
|
-
const flags = parseGlobalFlags(options);
|
|
2132
|
-
const startTime = Date.now();
|
|
2133
|
-
if (flags.json === "ndjson") {
|
|
2134
|
-
const result2 = notOk4(
|
|
2135
|
-
errorJsonFormatNotSupported({
|
|
2136
|
-
command: "db init",
|
|
2137
|
-
format: "ndjson",
|
|
2138
|
-
supportedFormats: ["object"]
|
|
2139
|
-
})
|
|
2140
|
-
);
|
|
2141
|
-
const exitCode2 = handleResult(result2, flags);
|
|
2142
|
-
process.exit(exitCode2);
|
|
2143
|
-
}
|
|
2144
|
-
const result = await executeDbInitCommand(options, flags, startTime);
|
|
2145
|
-
const exitCode = handleResult(result, flags, (dbInitResult) => {
|
|
2146
|
-
if (flags.json === "object") {
|
|
2147
|
-
console.log(formatDbInitJson(dbInitResult));
|
|
2148
|
-
} else {
|
|
2149
|
-
const output = dbInitResult.mode === "plan" ? formatDbInitPlanOutput(dbInitResult, flags) : formatDbInitApplyOutput(dbInitResult, flags);
|
|
2150
|
-
if (output) {
|
|
2151
|
-
console.log(output);
|
|
2152
|
-
}
|
|
2153
|
-
}
|
|
2154
|
-
});
|
|
2155
|
-
process.exit(exitCode);
|
|
2156
|
-
});
|
|
2157
|
-
return command;
|
|
2158
|
-
}
|
|
2159
|
-
|
|
2160
|
-
// src/commands/db-introspect.ts
|
|
2161
|
-
import { relative as relative4, resolve as resolve4 } from "path";
|
|
2162
|
-
import { notOk as notOk5, ok as ok5 } from "@prisma-next/utils/result";
|
|
2163
|
-
import { Command as Command3 } from "commander";
|
|
2164
|
-
async function executeDbIntrospectCommand(options, flags, startTime) {
|
|
2165
|
-
const config = await loadConfig(options.config);
|
|
2166
|
-
const configPath = options.config ? relative4(process.cwd(), resolve4(options.config)) : "prisma-next.config.ts";
|
|
2167
|
-
if (flags.json !== "object" && !flags.quiet) {
|
|
2168
|
-
const details = [
|
|
2169
|
-
{ label: "config", value: configPath }
|
|
2170
|
-
];
|
|
2171
|
-
if (options.db) {
|
|
2172
|
-
const maskedUrl = options.db.replace(/:([^:@]+)@/, ":****@");
|
|
2173
|
-
details.push({ label: "database", value: maskedUrl });
|
|
2174
|
-
} else if (config.db?.connection && typeof config.db.connection === "string") {
|
|
2175
|
-
const maskedUrl = config.db.connection.replace(/:([^:@]+)@/, ":****@");
|
|
2176
|
-
details.push({ label: "database", value: maskedUrl });
|
|
2177
|
-
}
|
|
2178
|
-
const header = formatStyledHeader({
|
|
2179
|
-
command: "db introspect",
|
|
2180
|
-
description: "Inspect the database schema",
|
|
2181
|
-
url: "https://pris.ly/db-introspect",
|
|
2182
|
-
details,
|
|
2183
|
-
flags
|
|
2184
|
-
});
|
|
2185
|
-
console.log(header);
|
|
2186
|
-
}
|
|
2187
|
-
const dbConnection = options.db ?? config.db?.connection;
|
|
2188
|
-
if (!dbConnection) {
|
|
2189
|
-
return notOk5(
|
|
2190
|
-
errorDatabaseConnectionRequired({
|
|
2191
|
-
why: `Database connection is required for db introspect (set db.connection in ${configPath}, or pass --db <url>)`
|
|
2192
|
-
})
|
|
2193
|
-
);
|
|
2194
|
-
}
|
|
2195
|
-
if (!config.driver) {
|
|
2196
|
-
return notOk5(errorDriverRequired({ why: "Config.driver is required for db introspect" }));
|
|
2197
|
-
}
|
|
2198
|
-
const client = createControlClient({
|
|
2199
|
-
family: config.family,
|
|
2200
|
-
target: config.target,
|
|
2201
|
-
adapter: config.adapter,
|
|
2202
|
-
driver: config.driver,
|
|
2203
|
-
extensionPacks: config.extensionPacks ?? []
|
|
2204
|
-
});
|
|
2205
|
-
const onProgress = createProgressAdapter({ flags });
|
|
2206
|
-
try {
|
|
2207
|
-
const schemaIR = await client.introspect({
|
|
2208
|
-
connection: dbConnection,
|
|
2209
|
-
onProgress
|
|
2210
|
-
});
|
|
2211
|
-
if (!flags.quiet && flags.json !== "object" && process.stdout.isTTY) {
|
|
2212
|
-
console.log("");
|
|
2213
|
-
}
|
|
2214
|
-
const schemaView = client.toSchemaView(schemaIR);
|
|
2215
|
-
const totalTime = Date.now() - startTime;
|
|
2216
|
-
const connectionForMeta = typeof dbConnection === "string" ? dbConnection.replace(/:([^:@]+)@/, ":****@") : void 0;
|
|
2217
|
-
const introspectResult = {
|
|
2218
|
-
ok: true,
|
|
2219
|
-
summary: "Schema introspected successfully",
|
|
2220
|
-
target: {
|
|
2221
|
-
familyId: config.family.familyId,
|
|
2222
|
-
id: config.target.targetId
|
|
2223
|
-
},
|
|
2224
|
-
schema: schemaIR,
|
|
2225
|
-
meta: {
|
|
2226
|
-
...configPath ? { configPath } : {},
|
|
2227
|
-
...connectionForMeta ? { dbUrl: connectionForMeta } : {}
|
|
2228
|
-
},
|
|
2229
|
-
timings: {
|
|
2230
|
-
total: totalTime
|
|
2231
|
-
}
|
|
2232
|
-
};
|
|
2233
|
-
return ok5({ introspectResult, schemaView });
|
|
2234
|
-
} catch (error) {
|
|
2235
|
-
if (error instanceof CliStructuredError) {
|
|
2236
|
-
return notOk5(error);
|
|
2237
|
-
}
|
|
2238
|
-
return notOk5(
|
|
2239
|
-
errorUnexpected2(error instanceof Error ? error.message : String(error), {
|
|
2240
|
-
why: `Unexpected error during db introspect: ${error instanceof Error ? error.message : String(error)}`
|
|
2241
|
-
})
|
|
2242
|
-
);
|
|
2243
|
-
} finally {
|
|
2244
|
-
await client.close();
|
|
2245
|
-
}
|
|
2246
|
-
}
|
|
2247
|
-
function createDbIntrospectCommand() {
|
|
2248
|
-
const command = new Command3("introspect");
|
|
2249
|
-
setCommandDescriptions(
|
|
2250
|
-
command,
|
|
2251
|
-
"Inspect the database schema",
|
|
2252
|
-
"Reads the live database schema and displays it as a tree structure. This command\ndoes not check the schema against your contract - it only shows what exists in\nthe database. Use `db verify` or `db schema-verify` to compare against your contract."
|
|
2253
|
-
);
|
|
2254
|
-
command.configureHelp({
|
|
2255
|
-
formatHelp: (cmd) => {
|
|
2256
|
-
const flags = parseGlobalFlags({});
|
|
2257
|
-
return formatCommandHelp({ command: cmd, flags });
|
|
2258
|
-
}
|
|
2259
|
-
}).option("--db <url>", "Database connection string").option("--config <path>", "Path to prisma-next.config.ts").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) => {
|
|
2260
|
-
const flags = parseGlobalFlags(options);
|
|
2261
|
-
const startTime = Date.now();
|
|
2262
|
-
if (flags.json === "ndjson") {
|
|
2263
|
-
const result2 = notOk5(
|
|
2264
|
-
errorJsonFormatNotSupported({
|
|
2265
|
-
command: "db introspect",
|
|
2266
|
-
format: "ndjson",
|
|
2267
|
-
supportedFormats: ["object"]
|
|
2268
|
-
})
|
|
2269
|
-
);
|
|
2270
|
-
const exitCode2 = handleResult(result2, flags);
|
|
2271
|
-
process.exit(exitCode2);
|
|
2272
|
-
}
|
|
2273
|
-
const result = await executeDbIntrospectCommand(options, flags, startTime);
|
|
2274
|
-
const exitCode = handleResult(result, flags, (value) => {
|
|
2275
|
-
const { introspectResult, schemaView } = value;
|
|
2276
|
-
if (flags.json === "object") {
|
|
2277
|
-
console.log(formatIntrospectJson(introspectResult));
|
|
2278
|
-
} else {
|
|
2279
|
-
const output = formatIntrospectOutput(introspectResult, schemaView, flags);
|
|
2280
|
-
if (output) {
|
|
2281
|
-
console.log(output);
|
|
2282
|
-
}
|
|
2283
|
-
}
|
|
2284
|
-
});
|
|
2285
|
-
process.exit(exitCode);
|
|
2286
|
-
});
|
|
2287
|
-
return command;
|
|
2288
|
-
}
|
|
2289
|
-
|
|
2290
|
-
// src/commands/db-schema-verify.ts
|
|
2291
|
-
import { readFile as readFile2 } from "fs/promises";
|
|
2292
|
-
import { relative as relative5, resolve as resolve5 } from "path";
|
|
2293
|
-
import { notOk as notOk6, ok as ok6 } from "@prisma-next/utils/result";
|
|
2294
|
-
import { Command as Command4 } from "commander";
|
|
2295
|
-
async function executeDbSchemaVerifyCommand(options, flags) {
|
|
2296
|
-
const config = await loadConfig(options.config);
|
|
2297
|
-
const configPath = options.config ? relative5(process.cwd(), resolve5(options.config)) : "prisma-next.config.ts";
|
|
2298
|
-
const contractPathAbsolute = config.contract?.output ? resolve5(config.contract.output) : resolve5("src/prisma/contract.json");
|
|
2299
|
-
const contractPath = relative5(process.cwd(), contractPathAbsolute);
|
|
2300
|
-
if (flags.json !== "object" && !flags.quiet) {
|
|
2301
|
-
const details = [
|
|
2302
|
-
{ label: "config", value: configPath },
|
|
2303
|
-
{ label: "contract", value: contractPath }
|
|
2304
|
-
];
|
|
2305
|
-
if (options.db) {
|
|
2306
|
-
details.push({ label: "database", value: options.db });
|
|
2307
|
-
}
|
|
2308
|
-
const header = formatStyledHeader({
|
|
2309
|
-
command: "db schema-verify",
|
|
2310
|
-
description: "Check whether the database schema satisfies your contract",
|
|
2311
|
-
url: "https://pris.ly/db-schema-verify",
|
|
2312
|
-
details,
|
|
2313
|
-
flags
|
|
2314
|
-
});
|
|
2315
|
-
console.log(header);
|
|
2316
|
-
}
|
|
2317
|
-
let contractJsonContent;
|
|
2318
|
-
try {
|
|
2319
|
-
contractJsonContent = await readFile2(contractPathAbsolute, "utf-8");
|
|
2320
|
-
} catch (error) {
|
|
2321
|
-
if (error instanceof Error && error.code === "ENOENT") {
|
|
2322
|
-
return notOk6(
|
|
2323
|
-
errorFileNotFound(contractPathAbsolute, {
|
|
2324
|
-
why: `Contract file not found at ${contractPathAbsolute}`,
|
|
2325
|
-
fix: `Run \`prisma-next contract emit\` to generate ${contractPath}, or update \`config.contract.output\` in ${configPath}`
|
|
2326
|
-
})
|
|
2327
|
-
);
|
|
2328
|
-
}
|
|
2329
|
-
return notOk6(
|
|
2330
|
-
errorUnexpected2(error instanceof Error ? error.message : String(error), {
|
|
2331
|
-
why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`
|
|
2332
|
-
})
|
|
2333
|
-
);
|
|
2334
|
-
}
|
|
2335
|
-
let contractJson;
|
|
2336
|
-
try {
|
|
2337
|
-
contractJson = JSON.parse(contractJsonContent);
|
|
2338
|
-
} catch (error) {
|
|
2339
|
-
return notOk6(
|
|
2340
|
-
errorContractValidationFailed(
|
|
2341
|
-
`Contract JSON is invalid: ${error instanceof Error ? error.message : String(error)}`,
|
|
2342
|
-
{ where: { path: contractPathAbsolute } }
|
|
2343
|
-
)
|
|
2344
|
-
);
|
|
2345
|
-
}
|
|
2346
|
-
const dbConnection = options.db ?? config.db?.connection;
|
|
2347
|
-
if (!dbConnection) {
|
|
2348
|
-
return notOk6(
|
|
2349
|
-
errorDatabaseConnectionRequired({
|
|
2350
|
-
why: `Database connection is required for db schema-verify (set db.connection in ${configPath}, or pass --db <url>)`
|
|
2351
|
-
})
|
|
2352
|
-
);
|
|
2353
|
-
}
|
|
2354
|
-
if (!config.driver) {
|
|
2355
|
-
return notOk6(errorDriverRequired({ why: "Config.driver is required for db schema-verify" }));
|
|
2356
|
-
}
|
|
2357
|
-
const client = createControlClient({
|
|
2358
|
-
family: config.family,
|
|
2359
|
-
target: config.target,
|
|
2360
|
-
adapter: config.adapter,
|
|
2361
|
-
driver: config.driver,
|
|
2362
|
-
extensionPacks: config.extensionPacks ?? []
|
|
2363
|
-
});
|
|
2364
|
-
const onProgress = createProgressAdapter({ flags });
|
|
2365
|
-
try {
|
|
2366
|
-
const schemaVerifyResult = await client.schemaVerify({
|
|
2367
|
-
contractIR: contractJson,
|
|
2368
|
-
strict: options.strict ?? false,
|
|
2369
|
-
connection: dbConnection,
|
|
2370
|
-
onProgress
|
|
2371
|
-
});
|
|
2372
|
-
if (!flags.quiet && flags.json !== "object" && process.stdout.isTTY) {
|
|
2373
|
-
console.log("");
|
|
2374
|
-
}
|
|
2375
|
-
return ok6(schemaVerifyResult);
|
|
2376
|
-
} catch (error) {
|
|
2377
|
-
if (error instanceof CliStructuredError) {
|
|
2378
|
-
return notOk6(error);
|
|
2379
|
-
}
|
|
2380
|
-
return notOk6(
|
|
2381
|
-
errorUnexpected2(error instanceof Error ? error.message : String(error), {
|
|
2382
|
-
why: `Unexpected error during db schema-verify: ${error instanceof Error ? error.message : String(error)}`
|
|
2383
|
-
})
|
|
2384
|
-
);
|
|
2385
|
-
} finally {
|
|
2386
|
-
await client.close();
|
|
2387
|
-
}
|
|
2388
|
-
}
|
|
2389
|
-
function createDbSchemaVerifyCommand() {
|
|
2390
|
-
const command = new Command4("schema-verify");
|
|
2391
|
-
setCommandDescriptions(
|
|
2392
|
-
command,
|
|
2393
|
-
"Check whether the database schema satisfies your contract",
|
|
2394
|
-
"Verifies that your database schema satisfies the emitted contract. Compares table structures,\ncolumn types, constraints, and extensions. Reports any mismatches via a contract-shaped\nverification tree. This is a read-only operation that does not modify the database."
|
|
2395
|
-
);
|
|
2396
|
-
command.configureHelp({
|
|
2397
|
-
formatHelp: (cmd) => {
|
|
2398
|
-
const flags = parseGlobalFlags({});
|
|
2399
|
-
return formatCommandHelp({ command: cmd, flags });
|
|
2400
|
-
}
|
|
2401
|
-
}).option("--db <url>", "Database connection string").option("--config <path>", "Path to prisma-next.config.ts").option("--json [format]", "Output as JSON (object)", false).option("--strict", "Strict mode: extra schema elements cause failures", 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) => {
|
|
2402
|
-
const flags = parseGlobalFlags(options);
|
|
2403
|
-
if (flags.json === "ndjson") {
|
|
2404
|
-
const result2 = notOk6(
|
|
2405
|
-
errorJsonFormatNotSupported({
|
|
2406
|
-
command: "db schema-verify",
|
|
2407
|
-
format: "ndjson",
|
|
2408
|
-
supportedFormats: ["object"]
|
|
2409
|
-
})
|
|
2410
|
-
);
|
|
2411
|
-
const exitCode2 = handleResult(result2, flags);
|
|
2412
|
-
process.exit(exitCode2);
|
|
2413
|
-
}
|
|
2414
|
-
const result = await executeDbSchemaVerifyCommand(options, flags);
|
|
2415
|
-
const exitCode = handleResult(result, flags, (schemaVerifyResult) => {
|
|
2416
|
-
if (flags.json === "object") {
|
|
2417
|
-
console.log(formatSchemaVerifyJson(schemaVerifyResult));
|
|
2418
|
-
} else {
|
|
2419
|
-
const output = formatSchemaVerifyOutput(schemaVerifyResult, flags);
|
|
2420
|
-
if (output) {
|
|
2421
|
-
console.log(output);
|
|
2422
|
-
}
|
|
2423
|
-
}
|
|
2424
|
-
});
|
|
2425
|
-
if (result.ok && !result.value.ok) {
|
|
2426
|
-
process.exit(1);
|
|
2427
|
-
} else {
|
|
2428
|
-
process.exit(exitCode);
|
|
2429
|
-
}
|
|
2430
|
-
});
|
|
2431
|
-
return command;
|
|
2432
|
-
}
|
|
2433
|
-
|
|
2434
|
-
// src/commands/db-sign.ts
|
|
2435
|
-
import { readFile as readFile3 } from "fs/promises";
|
|
2436
|
-
import { relative as relative6, resolve as resolve6 } from "path";
|
|
2437
|
-
import { notOk as notOk7, ok as ok7 } from "@prisma-next/utils/result";
|
|
2438
|
-
import { Command as Command5 } from "commander";
|
|
2439
|
-
async function executeDbSignCommand(options, flags) {
|
|
2440
|
-
const config = await loadConfig(options.config);
|
|
2441
|
-
const configPath = options.config ? relative6(process.cwd(), resolve6(options.config)) : "prisma-next.config.ts";
|
|
2442
|
-
const contractPathAbsolute = config.contract?.output ? resolve6(config.contract.output) : resolve6("src/prisma/contract.json");
|
|
2443
|
-
const contractPath = relative6(process.cwd(), contractPathAbsolute);
|
|
2444
|
-
if (flags.json !== "object" && !flags.quiet) {
|
|
2445
|
-
const details = [
|
|
2446
|
-
{ label: "config", value: configPath },
|
|
2447
|
-
{ label: "contract", value: contractPath }
|
|
2448
|
-
];
|
|
2449
|
-
if (options.db) {
|
|
2450
|
-
details.push({ label: "database", value: options.db });
|
|
2451
|
-
}
|
|
2452
|
-
const header = formatStyledHeader({
|
|
2453
|
-
command: "db sign",
|
|
2454
|
-
description: "Sign the database with your contract so you can safely run queries",
|
|
2455
|
-
url: "https://pris.ly/db-sign",
|
|
2456
|
-
details,
|
|
2457
|
-
flags
|
|
2458
|
-
});
|
|
2459
|
-
console.log(header);
|
|
2460
|
-
}
|
|
2461
|
-
let contractJsonContent;
|
|
2462
|
-
try {
|
|
2463
|
-
contractJsonContent = await readFile3(contractPathAbsolute, "utf-8");
|
|
2464
|
-
} catch (error) {
|
|
2465
|
-
if (error instanceof Error && error.code === "ENOENT") {
|
|
2466
|
-
return notOk7(
|
|
2467
|
-
errorFileNotFound(contractPathAbsolute, {
|
|
2468
|
-
why: `Contract file not found at ${contractPathAbsolute}`,
|
|
2469
|
-
fix: `Run \`prisma-next contract emit\` to generate ${contractPath}, or update \`config.contract.output\` in ${configPath}`
|
|
2470
|
-
})
|
|
2471
|
-
);
|
|
2472
|
-
}
|
|
2473
|
-
return notOk7(
|
|
2474
|
-
errorUnexpected2(error instanceof Error ? error.message : String(error), {
|
|
2475
|
-
why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`
|
|
2476
|
-
})
|
|
2477
|
-
);
|
|
2478
|
-
}
|
|
2479
|
-
let contractJson;
|
|
2480
|
-
try {
|
|
2481
|
-
contractJson = JSON.parse(contractJsonContent);
|
|
2482
|
-
} catch (error) {
|
|
2483
|
-
return notOk7(
|
|
2484
|
-
errorContractValidationFailed(
|
|
2485
|
-
`Contract JSON is invalid: ${error instanceof Error ? error.message : String(error)}`,
|
|
2486
|
-
{ where: { path: contractPathAbsolute } }
|
|
2487
|
-
)
|
|
2488
|
-
);
|
|
2489
|
-
}
|
|
2490
|
-
const dbConnection = options.db ?? config.db?.connection;
|
|
2491
|
-
if (!dbConnection) {
|
|
2492
|
-
return notOk7(
|
|
2493
|
-
errorDatabaseConnectionRequired({
|
|
2494
|
-
why: `Database connection is required for db sign (set db.connection in ${configPath}, or pass --db <url>)`
|
|
2495
|
-
})
|
|
2496
|
-
);
|
|
2497
|
-
}
|
|
2498
|
-
if (!config.driver) {
|
|
2499
|
-
return notOk7(errorDriverRequired({ why: "Config.driver is required for db sign" }));
|
|
2500
|
-
}
|
|
2501
|
-
const client = createControlClient({
|
|
2502
|
-
family: config.family,
|
|
2503
|
-
target: config.target,
|
|
2504
|
-
adapter: config.adapter,
|
|
2505
|
-
driver: config.driver,
|
|
2506
|
-
extensionPacks: config.extensionPacks ?? []
|
|
2507
|
-
});
|
|
2508
|
-
const onProgress = createProgressAdapter({ flags });
|
|
2509
|
-
try {
|
|
2510
|
-
const schemaVerifyResult = await client.schemaVerify({
|
|
2511
|
-
contractIR: contractJson,
|
|
2512
|
-
strict: false,
|
|
2513
|
-
connection: dbConnection,
|
|
2514
|
-
onProgress
|
|
2515
|
-
});
|
|
2516
|
-
if (!schemaVerifyResult.ok) {
|
|
2517
|
-
if (!flags.quiet && flags.json !== "object" && process.stdout.isTTY) {
|
|
2518
|
-
console.log("");
|
|
2519
|
-
}
|
|
2520
|
-
return notOk7(schemaVerifyResult);
|
|
2521
|
-
}
|
|
2522
|
-
const signResult = await client.sign({
|
|
2523
|
-
contractIR: contractJson,
|
|
2524
|
-
contractPath,
|
|
2525
|
-
configPath,
|
|
2526
|
-
onProgress
|
|
2527
|
-
});
|
|
2528
|
-
if (!flags.quiet && flags.json !== "object" && process.stdout.isTTY) {
|
|
2529
|
-
console.log("");
|
|
2530
|
-
}
|
|
2531
|
-
return ok7(signResult);
|
|
2532
|
-
} catch (error) {
|
|
2533
|
-
if (error instanceof CliStructuredError) {
|
|
2534
|
-
return notOk7(error);
|
|
2535
|
-
}
|
|
2536
|
-
return notOk7(
|
|
2537
|
-
errorUnexpected2(error instanceof Error ? error.message : String(error), {
|
|
2538
|
-
why: `Unexpected error during db sign: ${error instanceof Error ? error.message : String(error)}`
|
|
2539
|
-
})
|
|
2540
|
-
);
|
|
2541
|
-
} finally {
|
|
2542
|
-
await client.close();
|
|
2543
|
-
}
|
|
2544
|
-
}
|
|
2545
|
-
function createDbSignCommand() {
|
|
2546
|
-
const command = new Command5("sign");
|
|
2547
|
-
setCommandDescriptions(
|
|
2548
|
-
command,
|
|
2549
|
-
"Sign the database with your contract so you can safely run queries",
|
|
2550
|
-
"Verifies that your database schema satisfies the emitted contract, and if so, writes or\nupdates the contract marker in the database. This command is idempotent and safe to run\nin CI/deployment pipelines. The marker records that this database instance is aligned\nwith a specific contract version."
|
|
2551
|
-
);
|
|
2552
|
-
command.configureHelp({
|
|
2553
|
-
formatHelp: (cmd) => {
|
|
2554
|
-
const flags = parseGlobalFlags({});
|
|
2555
|
-
return formatCommandHelp({ command: cmd, flags });
|
|
2556
|
-
}
|
|
2557
|
-
}).option("--db <url>", "Database connection string").option("--config <path>", "Path to prisma-next.config.ts").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) => {
|
|
2558
|
-
const flags = parseGlobalFlags(options);
|
|
2559
|
-
if (flags.json === "ndjson") {
|
|
2560
|
-
const result2 = notOk7(
|
|
2561
|
-
errorJsonFormatNotSupported({
|
|
2562
|
-
command: "db sign",
|
|
2563
|
-
format: "ndjson",
|
|
2564
|
-
supportedFormats: ["object"]
|
|
2565
|
-
})
|
|
2566
|
-
);
|
|
2567
|
-
const exitCode = handleResult(result2, flags);
|
|
2568
|
-
process.exit(exitCode);
|
|
2569
|
-
}
|
|
2570
|
-
const result = await executeDbSignCommand(options, flags);
|
|
2571
|
-
if (result.ok) {
|
|
2572
|
-
if (flags.json === "object") {
|
|
2573
|
-
console.log(formatSignJson(result.value));
|
|
2574
|
-
} else {
|
|
2575
|
-
const output = formatSignOutput(result.value, flags);
|
|
2576
|
-
if (output) {
|
|
2577
|
-
console.log(output);
|
|
2578
|
-
}
|
|
2579
|
-
}
|
|
2580
|
-
process.exit(0);
|
|
2581
|
-
}
|
|
2582
|
-
const failure = result.failure;
|
|
2583
|
-
if (failure instanceof CliStructuredError) {
|
|
2584
|
-
const exitCode = handleResult(result, flags);
|
|
2585
|
-
process.exit(exitCode);
|
|
2586
|
-
}
|
|
2587
|
-
if (flags.json === "object") {
|
|
2588
|
-
console.log(formatSchemaVerifyJson(failure));
|
|
2589
|
-
} else {
|
|
2590
|
-
const output = formatSchemaVerifyOutput(failure, flags);
|
|
2591
|
-
if (output) {
|
|
2592
|
-
console.log(output);
|
|
2593
|
-
}
|
|
2594
|
-
}
|
|
2595
|
-
process.exit(1);
|
|
2596
|
-
});
|
|
2597
|
-
return command;
|
|
2598
|
-
}
|
|
2599
|
-
|
|
2600
|
-
// src/commands/db-verify.ts
|
|
2601
|
-
import { readFile as readFile4 } from "fs/promises";
|
|
2602
|
-
import { relative as relative7, resolve as resolve7 } from "path";
|
|
2603
|
-
import { notOk as notOk8, ok as ok8 } from "@prisma-next/utils/result";
|
|
2604
|
-
import { Command as Command6 } from "commander";
|
|
2605
|
-
function mapVerifyFailure(verifyResult) {
|
|
2606
|
-
if (!verifyResult.ok && verifyResult.code) {
|
|
2607
|
-
if (verifyResult.code === "PN-RTM-3001") {
|
|
2608
|
-
return errorMarkerMissing();
|
|
2609
|
-
}
|
|
2610
|
-
if (verifyResult.code === "PN-RTM-3002") {
|
|
2611
|
-
return errorHashMismatch({
|
|
2612
|
-
expected: verifyResult.contract.coreHash,
|
|
2613
|
-
...verifyResult.marker?.coreHash ? { actual: verifyResult.marker.coreHash } : {}
|
|
2614
|
-
});
|
|
2615
|
-
}
|
|
2616
|
-
if (verifyResult.code === "PN-RTM-3003") {
|
|
2617
|
-
return errorTargetMismatch(
|
|
2618
|
-
verifyResult.target.expected,
|
|
2619
|
-
verifyResult.target.actual ?? "unknown"
|
|
2620
|
-
);
|
|
2621
|
-
}
|
|
2622
|
-
}
|
|
2623
|
-
return errorRuntime(verifyResult.summary);
|
|
2624
|
-
}
|
|
2625
|
-
async function executeDbVerifyCommand(options, flags) {
|
|
2626
|
-
const config = await loadConfig(options.config);
|
|
2627
|
-
const configPath = options.config ? relative7(process.cwd(), resolve7(options.config)) : "prisma-next.config.ts";
|
|
2628
|
-
const contractPathAbsolute = config.contract?.output ? resolve7(config.contract.output) : resolve7("src/prisma/contract.json");
|
|
2629
|
-
const contractPath = relative7(process.cwd(), contractPathAbsolute);
|
|
2630
|
-
if (flags.json !== "object" && !flags.quiet) {
|
|
2631
|
-
const details = [
|
|
2632
|
-
{ label: "config", value: configPath },
|
|
2633
|
-
{ label: "contract", value: contractPath }
|
|
2634
|
-
];
|
|
2635
|
-
if (options.db) {
|
|
2636
|
-
details.push({ label: "database", value: options.db });
|
|
2637
|
-
}
|
|
2638
|
-
const header = formatStyledHeader({
|
|
2639
|
-
command: "db verify",
|
|
2640
|
-
description: "Check whether the database has been signed with your contract",
|
|
2641
|
-
url: "https://pris.ly/db-verify",
|
|
2642
|
-
details,
|
|
2643
|
-
flags
|
|
2644
|
-
});
|
|
2645
|
-
console.log(header);
|
|
2646
|
-
}
|
|
2647
|
-
let contractJsonContent;
|
|
2648
|
-
try {
|
|
2649
|
-
contractJsonContent = await readFile4(contractPathAbsolute, "utf-8");
|
|
2650
|
-
} catch (error) {
|
|
2651
|
-
if (error instanceof Error && error.code === "ENOENT") {
|
|
2652
|
-
return notOk8(
|
|
2653
|
-
errorFileNotFound(contractPathAbsolute, {
|
|
2654
|
-
why: `Contract file not found at ${contractPathAbsolute}`,
|
|
2655
|
-
fix: `Run \`prisma-next contract emit\` to generate ${contractPath}, or update \`config.contract.output\` in ${configPath}`
|
|
2656
|
-
})
|
|
2657
|
-
);
|
|
2658
|
-
}
|
|
2659
|
-
return notOk8(
|
|
2660
|
-
errorUnexpected2(error instanceof Error ? error.message : String(error), {
|
|
2661
|
-
why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`
|
|
2662
|
-
})
|
|
2663
|
-
);
|
|
2664
|
-
}
|
|
2665
|
-
let contractJson;
|
|
2666
|
-
try {
|
|
2667
|
-
contractJson = JSON.parse(contractJsonContent);
|
|
2668
|
-
} catch (error) {
|
|
2669
|
-
return notOk8(
|
|
2670
|
-
errorContractValidationFailed(
|
|
2671
|
-
`Contract JSON is invalid: ${error instanceof Error ? error.message : String(error)}`,
|
|
2672
|
-
{ where: { path: contractPathAbsolute } }
|
|
2673
|
-
)
|
|
2674
|
-
);
|
|
2675
|
-
}
|
|
2676
|
-
const dbConnection = options.db ?? config.db?.connection;
|
|
2677
|
-
if (!dbConnection) {
|
|
2678
|
-
return notOk8(
|
|
2679
|
-
errorDatabaseConnectionRequired({
|
|
2680
|
-
why: `Database connection is required for db verify (set db.connection in ${configPath}, or pass --db <url>)`
|
|
2681
|
-
})
|
|
2682
|
-
);
|
|
2683
|
-
}
|
|
2684
|
-
if (!config.driver) {
|
|
2685
|
-
return notOk8(errorDriverRequired({ why: "Config.driver is required for db verify" }));
|
|
2686
|
-
}
|
|
2687
|
-
const client = createControlClient({
|
|
2688
|
-
family: config.family,
|
|
2689
|
-
target: config.target,
|
|
2690
|
-
adapter: config.adapter,
|
|
2691
|
-
driver: config.driver,
|
|
2692
|
-
extensionPacks: config.extensionPacks ?? []
|
|
2693
|
-
});
|
|
2694
|
-
const onProgress = createProgressAdapter({ flags });
|
|
2695
|
-
try {
|
|
2696
|
-
const verifyResult = await client.verify({
|
|
2697
|
-
contractIR: contractJson,
|
|
2698
|
-
connection: dbConnection,
|
|
2699
|
-
onProgress
|
|
2700
|
-
});
|
|
2701
|
-
if (!flags.quiet && flags.json !== "object" && process.stdout.isTTY) {
|
|
2702
|
-
console.log("");
|
|
2703
|
-
}
|
|
2704
|
-
if (!verifyResult.ok) {
|
|
2705
|
-
return notOk8(mapVerifyFailure(verifyResult));
|
|
2706
|
-
}
|
|
2707
|
-
return ok8(verifyResult);
|
|
2708
|
-
} catch (error) {
|
|
2709
|
-
if (error instanceof CliStructuredError) {
|
|
2710
|
-
return notOk8(error);
|
|
2711
|
-
}
|
|
2712
|
-
return notOk8(
|
|
2713
|
-
errorUnexpected2(error instanceof Error ? error.message : String(error), {
|
|
2714
|
-
why: `Unexpected error during db verify: ${error instanceof Error ? error.message : String(error)}`
|
|
2715
|
-
})
|
|
2716
|
-
);
|
|
2717
|
-
} finally {
|
|
2718
|
-
await client.close();
|
|
2719
|
-
}
|
|
2720
|
-
}
|
|
2721
|
-
function createDbVerifyCommand() {
|
|
2722
|
-
const command = new Command6("verify");
|
|
2723
|
-
setCommandDescriptions(
|
|
2724
|
-
command,
|
|
2725
|
-
"Check whether the database has been signed with your contract",
|
|
2726
|
-
"Verifies that your database schema matches the emitted contract. Checks table structures,\ncolumn types, constraints, and codec coverage. Reports any mismatches or missing codecs."
|
|
2727
|
-
);
|
|
2728
|
-
command.configureHelp({
|
|
2729
|
-
formatHelp: (cmd) => {
|
|
2730
|
-
const flags = parseGlobalFlags({});
|
|
2731
|
-
return formatCommandHelp({ command: cmd, flags });
|
|
2732
|
-
}
|
|
2733
|
-
}).option("--db <url>", "Database connection string").option("--config <path>", "Path to prisma-next.config.ts").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) => {
|
|
2734
|
-
const flags = parseGlobalFlags(options);
|
|
2735
|
-
if (flags.json === "ndjson") {
|
|
2736
|
-
const result2 = notOk8(
|
|
2737
|
-
errorJsonFormatNotSupported({
|
|
2738
|
-
command: "db verify",
|
|
2739
|
-
format: "ndjson",
|
|
2740
|
-
supportedFormats: ["object"]
|
|
2741
|
-
})
|
|
2742
|
-
);
|
|
2743
|
-
const exitCode2 = handleResult(result2, flags);
|
|
2744
|
-
process.exit(exitCode2);
|
|
2745
|
-
}
|
|
2746
|
-
const result = await executeDbVerifyCommand(options, flags);
|
|
2747
|
-
const exitCode = handleResult(result, flags, (verifyResult) => {
|
|
2748
|
-
if (flags.json === "object") {
|
|
2749
|
-
console.log(formatVerifyJson(verifyResult));
|
|
2750
|
-
} else {
|
|
2751
|
-
const output = formatVerifyOutput(verifyResult, flags);
|
|
2752
|
-
if (output) {
|
|
2753
|
-
console.log(output);
|
|
2754
|
-
}
|
|
2755
|
-
}
|
|
2756
|
-
});
|
|
2757
|
-
process.exit(exitCode);
|
|
2758
|
-
});
|
|
2759
|
-
return command;
|
|
2760
|
-
}
|
|
2761
|
-
|
|
2762
|
-
// src/cli.ts
|
|
2763
|
-
var program = new Command7();
|
|
2764
|
-
program.name("prisma-next").description("Prisma Next CLI").version("0.0.1");
|
|
2765
|
-
var versionOption = program.options.find((opt) => opt.flags.includes("--version"));
|
|
2766
|
-
if (versionOption) {
|
|
2767
|
-
versionOption.description = "Output the version number";
|
|
2768
|
-
}
|
|
2769
|
-
program.configureOutput({
|
|
2770
|
-
writeErr: () => {
|
|
2771
|
-
},
|
|
2772
|
-
writeOut: () => {
|
|
2773
|
-
}
|
|
2774
|
-
});
|
|
2775
|
-
var rootHelpFormatter = (cmd) => {
|
|
2776
|
-
const flags = parseGlobalFlags({});
|
|
2777
|
-
return formatRootHelp({ program: cmd, flags });
|
|
2778
|
-
};
|
|
2779
|
-
program.configureHelp({
|
|
2780
|
-
formatHelp: rootHelpFormatter,
|
|
2781
|
-
subcommandDescription: () => ""
|
|
2782
|
-
});
|
|
2783
|
-
program.exitOverride((err) => {
|
|
2784
|
-
if (err) {
|
|
2785
|
-
const errorCode = err.code;
|
|
2786
|
-
const errorMessage = String(err.message ?? "");
|
|
2787
|
-
const errorName = err.name ?? "";
|
|
2788
|
-
const isUnknownCommandError = errorCode === "commander.unknownCommand" || errorCode === "commander.unknownArgument" || errorName === "CommanderError" && (errorMessage.includes("unknown command") || errorMessage.includes("unknown argument"));
|
|
2789
|
-
if (isUnknownCommandError) {
|
|
2790
|
-
const flags = parseGlobalFlags({});
|
|
2791
|
-
const match = errorMessage.match(/unknown command ['"]([^'"]+)['"]/);
|
|
2792
|
-
const commandName = match ? match[1] : process.argv[3] || process.argv[2] || "unknown";
|
|
2793
|
-
const firstArg = process.argv[2];
|
|
2794
|
-
const parentCommand = firstArg ? program.commands.find((cmd) => cmd.name() === firstArg) : void 0;
|
|
2795
|
-
if (parentCommand && commandName !== firstArg) {
|
|
2796
|
-
console.error(`Unknown command: ${commandName}`);
|
|
2797
|
-
console.error("");
|
|
2798
|
-
const helpText = formatCommandHelp({ command: parentCommand, flags });
|
|
2799
|
-
console.log(helpText);
|
|
2800
|
-
} else {
|
|
2801
|
-
console.error(`Unknown command: ${commandName}`);
|
|
2802
|
-
console.error("");
|
|
2803
|
-
const helpText = formatRootHelp({ program, flags });
|
|
2804
|
-
console.log(helpText);
|
|
2805
|
-
}
|
|
2806
|
-
process.exit(1);
|
|
2807
|
-
return;
|
|
2808
|
-
}
|
|
2809
|
-
const isHelpError = errorCode === "commander.help" || errorCode === "commander.helpDisplayed" || errorCode === "outputHelp" || errorMessage === "(outputHelp)" || errorMessage.includes("outputHelp") || errorName === "CommanderError" && errorMessage.includes("outputHelp");
|
|
2810
|
-
if (isHelpError) {
|
|
2811
|
-
process.exit(0);
|
|
2812
|
-
return;
|
|
2813
|
-
}
|
|
2814
|
-
const isMissingArgumentError = errorCode === "commander.missingArgument" || errorCode === "commander.missingMandatoryOptionValue" || errorName === "CommanderError" && (errorMessage.includes("missing") || errorMessage.includes("required"));
|
|
2815
|
-
if (isMissingArgumentError) {
|
|
2816
|
-
process.exit(0);
|
|
2817
|
-
return;
|
|
2818
|
-
}
|
|
2819
|
-
console.error(`Unhandled error: ${err.message}`);
|
|
2820
|
-
if (err.stack) {
|
|
2821
|
-
console.error(err.stack);
|
|
2822
|
-
}
|
|
2823
|
-
process.exit(1);
|
|
2824
|
-
}
|
|
2825
|
-
process.exit(0);
|
|
2826
|
-
});
|
|
2827
|
-
var contractCommand = new Command7("contract");
|
|
2828
|
-
setCommandDescriptions(
|
|
2829
|
-
contractCommand,
|
|
2830
|
-
"Contract management commands",
|
|
2831
|
-
"Define and emit your application data contract. The contract describes your schema as a\ndeclarative data structure that can be signed and verified against your database."
|
|
2832
|
-
);
|
|
2833
|
-
contractCommand.configureHelp({
|
|
2834
|
-
formatHelp: (cmd) => {
|
|
2835
|
-
const flags = parseGlobalFlags({});
|
|
2836
|
-
return formatCommandHelp({ command: cmd, flags });
|
|
2837
|
-
},
|
|
2838
|
-
subcommandDescription: () => ""
|
|
2839
|
-
});
|
|
2840
|
-
var contractEmitCommand = createContractEmitCommand();
|
|
2841
|
-
contractCommand.addCommand(contractEmitCommand);
|
|
2842
|
-
program.addCommand(contractCommand);
|
|
2843
|
-
var dbCommand = new Command7("db");
|
|
2844
|
-
setCommandDescriptions(
|
|
2845
|
-
dbCommand,
|
|
2846
|
-
"Database management commands",
|
|
2847
|
-
"Verify and sign your database with your contract. Ensure your database schema matches\nyour contract, and sign it to record the contract hash for future verification."
|
|
2848
|
-
);
|
|
2849
|
-
dbCommand.configureHelp({
|
|
2850
|
-
formatHelp: (cmd) => {
|
|
2851
|
-
const flags = parseGlobalFlags({});
|
|
2852
|
-
return formatCommandHelp({ command: cmd, flags });
|
|
2853
|
-
},
|
|
2854
|
-
subcommandDescription: () => ""
|
|
2855
|
-
});
|
|
2856
|
-
var dbVerifyCommand = createDbVerifyCommand();
|
|
2857
|
-
dbCommand.addCommand(dbVerifyCommand);
|
|
2858
|
-
var dbInitCommand = createDbInitCommand();
|
|
2859
|
-
dbCommand.addCommand(dbInitCommand);
|
|
2860
|
-
var dbIntrospectCommand = createDbIntrospectCommand();
|
|
2861
|
-
dbCommand.addCommand(dbIntrospectCommand);
|
|
2862
|
-
var dbSchemaVerifyCommand = createDbSchemaVerifyCommand();
|
|
2863
|
-
dbCommand.addCommand(dbSchemaVerifyCommand);
|
|
2864
|
-
var dbSignCommand = createDbSignCommand();
|
|
2865
|
-
dbCommand.addCommand(dbSignCommand);
|
|
2866
|
-
program.addCommand(dbCommand);
|
|
2867
|
-
var helpCommand = new Command7("help").description("Show usage instructions").configureHelp({
|
|
2868
|
-
formatHelp: (cmd) => {
|
|
2869
|
-
const flags = parseGlobalFlags({});
|
|
2870
|
-
return formatCommandHelp({ command: cmd, flags });
|
|
2871
|
-
}
|
|
2872
|
-
}).action(() => {
|
|
2873
|
-
const flags = parseGlobalFlags({});
|
|
2874
|
-
const helpText = formatRootHelp({ program, flags });
|
|
2875
|
-
console.log(helpText);
|
|
2876
|
-
process.exit(0);
|
|
2877
|
-
});
|
|
2878
|
-
program.addCommand(helpCommand);
|
|
2879
|
-
program.action(() => {
|
|
2880
|
-
const flags = parseGlobalFlags({});
|
|
2881
|
-
const helpText = formatRootHelp({ program, flags });
|
|
2882
|
-
console.log(helpText);
|
|
2883
|
-
process.exit(0);
|
|
2884
|
-
});
|
|
2885
|
-
var args = process.argv.slice(2);
|
|
2886
|
-
if (args.length > 0) {
|
|
2887
|
-
const commandName = args[0];
|
|
2888
|
-
if (commandName === "--version" || commandName === "-V") {
|
|
2889
|
-
console.log(program.version());
|
|
2890
|
-
process.exit(0);
|
|
2891
|
-
}
|
|
2892
|
-
const isGlobalOption = commandName === "--help" || commandName === "-h";
|
|
2893
|
-
if (!isGlobalOption) {
|
|
2894
|
-
const command = program.commands.find((cmd) => cmd.name() === commandName);
|
|
2895
|
-
if (!command) {
|
|
2896
|
-
const flags = parseGlobalFlags({});
|
|
2897
|
-
console.error(`Unknown command: ${commandName}`);
|
|
2898
|
-
console.error("");
|
|
2899
|
-
const helpText = formatRootHelp({ program, flags });
|
|
2900
|
-
console.log(helpText);
|
|
2901
|
-
process.exit(1);
|
|
2902
|
-
} else if (command.commands.length > 0 && args.length === 1) {
|
|
2903
|
-
const flags = parseGlobalFlags({});
|
|
2904
|
-
const helpText = formatCommandHelp({ command, flags });
|
|
2905
|
-
console.log(helpText);
|
|
2906
|
-
process.exit(0);
|
|
2907
|
-
}
|
|
2908
|
-
}
|
|
2909
|
-
}
|
|
2910
|
-
program.parse();
|
|
2911
|
-
//# sourceMappingURL=cli.js.map
|
|
2
|
+
import './cli.mjs';
|