@prisma-next/cli 0.16.0-dev.30 → 0.16.0-dev.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +13 -13
- package/dist/{client-CMusjGV5.mjs → client-B1UjQc_x.mjs} +30 -37
- package/dist/client-B1UjQc_x.mjs.map +1 -0
- package/dist/{command-helpers-CVn3U9Uz.mjs → command-helpers-D-jBl0Qt.mjs} +5 -5
- package/dist/command-helpers-D-jBl0Qt.mjs.map +1 -0
- package/dist/commands/contract-emit.mjs +1 -1
- package/dist/commands/contract-infer.mjs +1 -1
- package/dist/commands/db-init.d.mts.map +1 -1
- package/dist/commands/db-init.mjs +6 -5
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.mjs +3 -3
- package/dist/commands/db-sign.mjs +6 -5
- package/dist/commands/db-sign.mjs.map +1 -1
- package/dist/commands/db-update.d.mts.map +1 -1
- package/dist/commands/db-update.mjs +7 -6
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migrate.mjs +6 -6
- package/dist/commands/migration-check.mjs +1 -1
- package/dist/commands/migration-graph.mjs +4 -4
- package/dist/commands/migration-list.mjs +1 -1
- package/dist/commands/migration-log.mjs +1 -1
- package/dist/commands/migration-new.mjs +3 -3
- package/dist/commands/migration-plan.mjs +1 -1
- package/dist/commands/migration-show.mjs +3 -3
- package/dist/commands/migration-status.mjs +1 -1
- package/dist/commands/ref.mjs +2 -2
- package/dist/commands/telemetry/index.mjs +1 -1
- package/dist/{contract-at-errors-C-l4-JEY.mjs → contract-at-errors-DvZ_DzJK.mjs} +2 -2
- package/dist/{contract-at-errors-C-l4-JEY.mjs.map → contract-at-errors-DvZ_DzJK.mjs.map} +1 -1
- package/dist/{contract-emit-PaY3qRdN.mjs → contract-emit-BT46Mu8Q.mjs} +3 -3
- package/dist/{contract-emit-PaY3qRdN.mjs.map → contract-emit-BT46Mu8Q.mjs.map} +1 -1
- package/dist/{contract-emit-CfIbTnsR.mjs → contract-emit-hVdlaF2U.mjs} +3 -3
- package/dist/{contract-emit-CfIbTnsR.mjs.map → contract-emit-hVdlaF2U.mjs.map} +1 -1
- package/dist/{contract-infer-DCju2fTk.mjs → contract-infer-DpqTQECL.mjs} +3 -3
- package/dist/{contract-infer-DCju2fTk.mjs.map → contract-infer-DpqTQECL.mjs.map} +1 -1
- package/dist/{contract-space-aggregate-loader-B6E9LGa6.mjs → contract-space-aggregate-loader-CNd-ZB6C.mjs} +2 -2
- package/dist/{contract-space-aggregate-loader-B6E9LGa6.mjs.map → contract-space-aggregate-loader-CNd-ZB6C.mjs.map} +1 -1
- package/dist/{db-verify-B_dKu6Yp.mjs → db-verify-FEN6k1m7.mjs} +9 -7
- package/dist/db-verify-FEN6k1m7.mjs.map +1 -0
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +2 -2
- package/dist/exports/index.d.mts +2 -1
- package/dist/exports/index.d.mts.map +1 -1
- package/dist/exports/index.mjs +61 -21
- package/dist/exports/index.mjs.map +1 -1
- package/dist/{format-cCyDfmRE.mjs → format-DI5qVr22.mjs} +3 -3
- package/dist/{format-cCyDfmRE.mjs.map → format-DI5qVr22.mjs.map} +1 -1
- package/dist/{framework-components-DUN-TYDg.mjs → framework-components-C4XACYNB.mjs} +2 -2
- package/dist/{framework-components-DUN-TYDg.mjs.map → framework-components-C4XACYNB.mjs.map} +1 -1
- package/dist/{init-C9U3ffHH.mjs → init-ClrRkbwd.mjs} +25 -23
- package/dist/init-ClrRkbwd.mjs.map +1 -0
- package/dist/{inspect-live-schema-DHXE7skX.mjs → inspect-live-schema-C0Rgdtw_.mjs} +3 -3
- package/dist/{inspect-live-schema-DHXE7skX.mjs.map → inspect-live-schema-C0Rgdtw_.mjs.map} +1 -1
- package/dist/{migration-check-W5XEx4d-.mjs → migration-check-zEkHpPDT.mjs} +4 -4
- package/dist/{migration-check-W5XEx4d-.mjs.map → migration-check-zEkHpPDT.mjs.map} +1 -1
- package/dist/{migration-command-scaffold-hI5cnoGe.mjs → migration-command-scaffold-CkNsp9g1.mjs} +3 -3
- package/dist/{migration-command-scaffold-hI5cnoGe.mjs.map → migration-command-scaffold-CkNsp9g1.mjs.map} +1 -1
- package/dist/{migration-graph-command-render-DgQg5App.mjs → migration-graph-command-render-DEwFpx-w.mjs} +3 -2
- package/dist/migration-graph-command-render-DEwFpx-w.mjs.map +1 -0
- package/dist/{migration-list-ClkEaoL8.mjs → migration-list-BkafFVXS.mjs} +4 -4
- package/dist/{migration-list-ClkEaoL8.mjs.map → migration-list-BkafFVXS.mjs.map} +1 -1
- package/dist/{migration-log-CjafbZ2Z.mjs → migration-log-BxhAtbhA.mjs} +4 -4
- package/dist/{migration-log-CjafbZ2Z.mjs.map → migration-log-BxhAtbhA.mjs.map} +1 -1
- package/dist/{migration-path-target-C0TQuV5n.mjs → migration-path-target-B5HkjSTO.mjs} +2 -2
- package/dist/{migration-path-target-C0TQuV5n.mjs.map → migration-path-target-B5HkjSTO.mjs.map} +1 -1
- package/dist/{migration-plan-Pc4hCF1H.mjs → migration-plan-Bk2pbPQH.mjs} +5 -5
- package/dist/{migration-plan-Pc4hCF1H.mjs.map → migration-plan-Bk2pbPQH.mjs.map} +1 -1
- package/dist/{migration-status-CqAdRzs8.mjs → migration-status-BzlARseq.mjs} +6 -6
- package/dist/{migration-status-CqAdRzs8.mjs.map → migration-status-BzlARseq.mjs.map} +1 -1
- package/dist/{telemetry-BEyNMfco.mjs → telemetry-BbniBUbP.mjs} +2 -2
- package/dist/{telemetry-BEyNMfco.mjs.map → telemetry-BbniBUbP.mjs.map} +1 -1
- package/dist/{verify-C4RazDE1.mjs → verify-BSaNeaSL.mjs} +2 -2
- package/dist/{verify-C4RazDE1.mjs.map → verify-BSaNeaSL.mjs.map} +1 -1
- package/package.json +21 -21
- package/src/commands/db-init.ts +4 -3
- package/src/commands/db-sign.ts +2 -2
- package/src/commands/db-update.ts +4 -3
- package/src/commands/db-verify.ts +3 -3
- package/src/commands/init/probe-db.ts +9 -2
- package/src/commands/init/templates/render.ts +2 -1
- package/src/control-api/client.ts +36 -17
- package/src/control-api/operations/db-run.ts +2 -1
- package/src/control-api/operations/migrate.ts +3 -2
- package/src/control-api/operations/run-migration.ts +2 -1
- package/src/load-ts-contract.ts +81 -16
- package/src/utils/combine-verify-results.ts +2 -1
- package/src/utils/command-helpers.ts +15 -5
- package/src/utils/formatters/migration-graph-grid-layout.ts +2 -1
- package/dist/client-CMusjGV5.mjs.map +0 -1
- package/dist/command-helpers-CVn3U9Uz.mjs.map +0 -1
- package/dist/db-verify-B_dKu6Yp.mjs.map +0 -1
- package/dist/init-C9U3ffHH.mjs.map +0 -1
- package/dist/migration-graph-command-render-DgQg5App.mjs.map +0 -1
- package/src/control-api/errors.ts +0 -9
package/dist/exports/index.mjs
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
import { t as createContractEmitCommand } from "../contract-emit-
|
|
2
|
-
import { t as createFormatCommand } from "../format-
|
|
1
|
+
import { t as createContractEmitCommand } from "../contract-emit-BT46Mu8Q.mjs";
|
|
2
|
+
import { t as createFormatCommand } from "../format-DI5qVr22.mjs";
|
|
3
|
+
import { CliStructuredError } from "@prisma-next/errors/control";
|
|
3
4
|
import { join, resolve } from "pathe";
|
|
4
5
|
import { existsSync, unlinkSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { InternalError } from "@prisma-next/utils/internal-error";
|
|
5
7
|
import { tmpdir } from "node:os";
|
|
6
8
|
import { pathToFileURL } from "node:url";
|
|
7
9
|
import { build } from "esbuild";
|
|
8
10
|
//#region src/load-ts-contract.ts
|
|
11
|
+
function contractSourceError(code, message, options) {
|
|
12
|
+
const error = new CliStructuredError(code, message, options?.meta !== void 0 ? { meta: options.meta } : void 0);
|
|
13
|
+
if (options?.cause !== void 0) error.cause = options.cause;
|
|
14
|
+
return error;
|
|
15
|
+
}
|
|
9
16
|
const DEFAULT_ALLOWLIST = ["@prisma-next/*", "node:crypto"];
|
|
10
17
|
function isAllowedImport(importPath, allowlist) {
|
|
11
18
|
for (const pattern of allowlist) if (pattern.endsWith("/*")) {
|
|
@@ -17,18 +24,29 @@ function isAllowedImport(importPath, allowlist) {
|
|
|
17
24
|
} else if (importPath === pattern) return true;
|
|
18
25
|
return false;
|
|
19
26
|
}
|
|
20
|
-
function validatePurity(value) {
|
|
27
|
+
function validatePurity(value, entryPath) {
|
|
21
28
|
if (typeof value !== "object" || value === null) return;
|
|
22
29
|
const path = /* @__PURE__ */ new WeakSet();
|
|
23
30
|
function check(value) {
|
|
24
31
|
if (value === null || typeof value !== "object") return;
|
|
25
|
-
if (path.has(value)) throw
|
|
32
|
+
if (path.has(value)) throw contractSourceError("CONTRACT.EXPORT_INVALID", "Contract export contains circular references", { meta: {
|
|
33
|
+
path: entryPath,
|
|
34
|
+
reason: "circular"
|
|
35
|
+
} });
|
|
26
36
|
path.add(value);
|
|
27
37
|
try {
|
|
28
38
|
for (const key in value) {
|
|
29
39
|
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
30
|
-
if (descriptor && (descriptor.get || descriptor.set)) throw
|
|
31
|
-
|
|
40
|
+
if (descriptor && (descriptor.get || descriptor.set)) throw contractSourceError("CONTRACT.EXPORT_INVALID", `Contract export contains getter/setter at key "${key}"`, { meta: {
|
|
41
|
+
path: entryPath,
|
|
42
|
+
reason: "getter",
|
|
43
|
+
key
|
|
44
|
+
} });
|
|
45
|
+
if (descriptor && typeof descriptor.value === "function") throw contractSourceError("CONTRACT.EXPORT_INVALID", `Contract export contains function at key "${key}"`, { meta: {
|
|
46
|
+
path: entryPath,
|
|
47
|
+
reason: "function",
|
|
48
|
+
key
|
|
49
|
+
} });
|
|
32
50
|
check(value[key]);
|
|
33
51
|
}
|
|
34
52
|
} finally {
|
|
@@ -41,9 +59,18 @@ function validatePurity(value) {
|
|
|
41
59
|
} catch (error) {
|
|
42
60
|
if (error instanceof Error) {
|
|
43
61
|
if (error.message.includes("getter") || error.message.includes("circular")) throw error;
|
|
44
|
-
throw
|
|
62
|
+
throw contractSourceError("CONTRACT.EXPORT_INVALID", `Contract export is not JSON-serializable: ${error.message}`, {
|
|
63
|
+
meta: {
|
|
64
|
+
path: entryPath,
|
|
65
|
+
reason: "not-json-serializable"
|
|
66
|
+
},
|
|
67
|
+
cause: error
|
|
68
|
+
});
|
|
45
69
|
}
|
|
46
|
-
throw
|
|
70
|
+
throw contractSourceError("CONTRACT.EXPORT_INVALID", "Contract export is not JSON-serializable", { meta: {
|
|
71
|
+
path: entryPath,
|
|
72
|
+
reason: "not-json-serializable"
|
|
73
|
+
} });
|
|
47
74
|
}
|
|
48
75
|
}
|
|
49
76
|
function createImportAllowlistPlugin(allowlist, entryPath, collected) {
|
|
@@ -81,11 +108,12 @@ function createImportAllowlistPlugin(allowlist, entryPath, collected) {
|
|
|
81
108
|
* @param entryPath - Path to the TypeScript contract file
|
|
82
109
|
* @param options - Optional configuration (import allowlist)
|
|
83
110
|
* @returns The contract as Contract (should already be normalized)
|
|
84
|
-
* @throws
|
|
111
|
+
* @throws structured errors: `CLI.FILE_NOT_FOUND`, `CONTRACT.SOURCE_LOAD_FAILED`,
|
|
112
|
+
* `CONTRACT.SOURCE_IMPORT_DISALLOWED`, `CONTRACT.MODULE_EXPORT_MISSING`, `CONTRACT.EXPORT_INVALID`
|
|
85
113
|
*/
|
|
86
114
|
async function loadContractFromTs(entryPath, options) {
|
|
87
115
|
const allowlist = options?.allowlist ?? DEFAULT_ALLOWLIST;
|
|
88
|
-
if (!existsSync(entryPath)) throw new
|
|
116
|
+
if (!existsSync(entryPath)) throw new CliStructuredError("CLI.FILE_NOT_FOUND", `Contract file not found: ${entryPath}`, { where: { path: entryPath } });
|
|
89
117
|
const tempFile = join(tmpdir(), `prisma-next-contract-${Date.now()}-${Math.random().toString(36).slice(2)}.mjs`);
|
|
90
118
|
const disallowedFromEntry = /* @__PURE__ */ new Set();
|
|
91
119
|
try {
|
|
@@ -101,14 +129,17 @@ async function loadContractFromTs(entryPath, options) {
|
|
|
101
129
|
plugins: [createImportAllowlistPlugin(allowlist, entryPath, disallowedFromEntry)],
|
|
102
130
|
logLevel: "error"
|
|
103
131
|
});
|
|
104
|
-
if (result.errors.length > 0) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
if (!result.outputFiles || result.outputFiles.length === 0) throw new
|
|
109
|
-
if (disallowedFromEntry.size > 0) throw
|
|
132
|
+
if (result.errors.length > 0) throw contractSourceError("CONTRACT.SOURCE_LOAD_FAILED", `Failed to bundle contract file: ${result.errors.map((e) => e.text).join("\n")}`, { meta: {
|
|
133
|
+
path: entryPath,
|
|
134
|
+
stage: "bundle"
|
|
135
|
+
} });
|
|
136
|
+
if (!result.outputFiles || result.outputFiles.length === 0) throw new InternalError("No output files generated from bundling");
|
|
137
|
+
if (disallowedFromEntry.size > 0) throw contractSourceError("CONTRACT.SOURCE_IMPORT_DISALLOWED", `Disallowed imports detected. Only imports matching the allowlist are permitted:\n Allowlist: ${allowlist.join(", ")}\n Disallowed imports: ${[...disallowedFromEntry].join(", ")}`, { meta: {
|
|
138
|
+
allowlist: [...allowlist],
|
|
139
|
+
disallowed: [...disallowedFromEntry]
|
|
140
|
+
} });
|
|
110
141
|
const bundleContent = result.outputFiles[0]?.text;
|
|
111
|
-
if (bundleContent === void 0) throw new
|
|
142
|
+
if (bundleContent === void 0) throw new InternalError("Bundle content is undefined");
|
|
112
143
|
writeFileSync(tempFile, bundleContent, "utf-8");
|
|
113
144
|
const module = await import(
|
|
114
145
|
/* @vite-ignore */
|
|
@@ -118,16 +149,25 @@ async function loadContractFromTs(entryPath, options) {
|
|
|
118
149
|
let contract;
|
|
119
150
|
if (module.default !== void 0) contract = module.default;
|
|
120
151
|
else if (module.contract !== void 0) contract = module.contract;
|
|
121
|
-
else throw new
|
|
122
|
-
if (typeof contract !== "object" || contract === null) throw
|
|
123
|
-
|
|
152
|
+
else throw new CliStructuredError("CONTRACT.MODULE_EXPORT_MISSING", `Contract file must export a contract as default export or named export 'contract'. Found exports: ${Object.keys(module).join(", ") || "none"}`, { where: { path: entryPath } });
|
|
153
|
+
if (typeof contract !== "object" || contract === null) throw contractSourceError("CONTRACT.EXPORT_INVALID", `Contract export must be an object, got ${typeof contract}`, { meta: {
|
|
154
|
+
path: entryPath,
|
|
155
|
+
reason: "not-object"
|
|
156
|
+
} });
|
|
157
|
+
validatePurity(contract, entryPath);
|
|
124
158
|
return contract;
|
|
125
159
|
} catch (error) {
|
|
126
160
|
try {
|
|
127
161
|
if (tempFile) unlinkSync(tempFile);
|
|
128
162
|
} catch {}
|
|
129
163
|
if (error instanceof Error) throw error;
|
|
130
|
-
throw
|
|
164
|
+
throw contractSourceError("CONTRACT.SOURCE_LOAD_FAILED", `Failed to load contract from ${entryPath}: ${String(error)}`, {
|
|
165
|
+
meta: {
|
|
166
|
+
path: entryPath,
|
|
167
|
+
stage: "import"
|
|
168
|
+
},
|
|
169
|
+
cause: error
|
|
170
|
+
});
|
|
131
171
|
}
|
|
132
172
|
}
|
|
133
173
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["resolvePath"],"sources":["../../src/load-ts-contract.ts"],"sourcesContent":["import { existsSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { pathToFileURL } from 'node:url';\nimport type { Contract } from '@prisma-next/contract/types';\nimport type { Plugin } from 'esbuild';\nimport { build } from 'esbuild';\nimport { join, resolve as resolvePath } from 'pathe';\n\nexport interface LoadTsContractOptions {\n readonly allowlist?: ReadonlyArray<string>;\n}\n\nconst DEFAULT_ALLOWLIST = ['@prisma-next/*', 'node:crypto'];\n\nfunction isAllowedImport(importPath: string, allowlist: ReadonlyArray<string>): boolean {\n for (const pattern of allowlist) {\n if (pattern.endsWith('/*')) {\n const prefix = pattern.slice(0, -2);\n if (importPath === prefix || importPath.startsWith(`${prefix}/`)) {\n return true;\n }\n } else if (pattern.endsWith('*')) {\n const prefix = pattern.slice(0, -1);\n if (importPath.startsWith(prefix)) {\n return true;\n }\n } else if (importPath === pattern) {\n return true;\n }\n }\n return false;\n}\n\nfunction validatePurity(value: unknown): void {\n if (typeof value !== 'object' || value === null) {\n return;\n }\n\n const path = new WeakSet();\n\n function check(value: unknown): void {\n if (value === null || typeof value !== 'object') {\n return;\n }\n\n if (path.has(value)) {\n throw new Error('Contract export contains circular references');\n }\n path.add(value);\n\n try {\n for (const key in value) {\n const descriptor = Object.getOwnPropertyDescriptor(value, key);\n if (descriptor && (descriptor.get || descriptor.set)) {\n throw new Error(`Contract export contains getter/setter at key \"${key}\"`);\n }\n if (descriptor && typeof descriptor.value === 'function') {\n throw new Error(`Contract export contains function at key \"${key}\"`);\n }\n check((value as Record<string, unknown>)[key]);\n }\n } finally {\n path.delete(value);\n }\n }\n\n try {\n check(value);\n JSON.stringify(value);\n } catch (error) {\n if (error instanceof Error) {\n if (error.message.includes('getter') || error.message.includes('circular')) {\n throw error;\n }\n throw new Error(`Contract export is not JSON-serializable: ${error.message}`);\n }\n throw new Error('Contract export is not JSON-serializable');\n }\n}\n\nfunction createImportAllowlistPlugin(\n allowlist: ReadonlyArray<string>,\n entryPath: string,\n collected: Set<string>,\n): Plugin {\n // Match against several path forms that esbuild may use as the importer:\n // the absolute resolved entry, the value the caller passed (which may be\n // relative), and the conventional `<stdin>` placeholder. This is more\n // forgiving than `===` against a single form, which broke when esbuild\n // resolved the entry to an absolute path while the caller passed a\n // relative one (or vice versa).\n const entryAbs = resolvePath(entryPath);\n function isFromEntry(importer: string): boolean {\n return importer === entryAbs || importer === entryPath || importer === '<stdin>';\n }\n return {\n name: 'import-allowlist',\n setup(build) {\n build.onResolve({ filter: /.*/ }, (args) => {\n if (args.kind === 'entry-point') {\n return undefined;\n }\n if (args.path.startsWith('.') || args.path.startsWith('/')) {\n return undefined;\n }\n if (isFromEntry(args.importer) && !isAllowedImport(args.path, allowlist)) {\n collected.add(args.path);\n return {\n path: args.path,\n external: true,\n };\n }\n return undefined;\n });\n },\n };\n}\n\n/**\n * Loads a contract from a TypeScript file and returns it as Contract.\n *\n * **Responsibility: Parsing Only**\n * This function loads and parses a TypeScript contract file. It does NOT normalize the contract.\n * The contract should already be normalized if it was built using the contract builder.\n *\n * Normalization must happen in the contract builder when the contract is created.\n * This function only validates that the contract is JSON-serializable and returns it as-is.\n *\n * @param entryPath - Path to the TypeScript contract file\n * @param options - Optional configuration (import allowlist)\n * @returns The contract as Contract (should already be normalized)\n * @throws Error if the contract cannot be loaded or is not JSON-serializable\n */\nexport async function loadContractFromTs(\n entryPath: string,\n options?: LoadTsContractOptions,\n): Promise<Contract> {\n const allowlist = options?.allowlist ?? DEFAULT_ALLOWLIST;\n\n if (!existsSync(entryPath)) {\n throw new Error(`Contract file not found: ${entryPath}`);\n }\n\n const tempFile = join(\n tmpdir(),\n `prisma-next-contract-${Date.now()}-${Math.random().toString(36).slice(2)}.mjs`,\n );\n\n // Disallowed imports are collected by the allowlist resolver plugin itself,\n // which has the `importer` context to distinguish entry-direct imports from\n // transitive imports made inside allowlisted (`@prisma-next/*`) dependencies.\n // The metafile is intentionally not re-walked: it would surface internal\n // `node:*` imports inside framework code as false positives.\n const disallowedFromEntry = new Set<string>();\n\n try {\n const result = await build({\n entryPoints: [entryPath],\n bundle: true,\n format: 'esm',\n platform: 'node',\n target: 'es2022',\n outfile: tempFile,\n write: false,\n metafile: true,\n plugins: [createImportAllowlistPlugin(allowlist, entryPath, disallowedFromEntry)],\n logLevel: 'error',\n });\n\n if (result.errors.length > 0) {\n const errorMessages = result.errors.map((e: { text: string }) => e.text).join('\\n');\n throw new Error(`Failed to bundle contract file: ${errorMessages}`);\n }\n\n if (!result.outputFiles || result.outputFiles.length === 0) {\n throw new Error('No output files generated from bundling');\n }\n\n if (disallowedFromEntry.size > 0) {\n throw new Error(\n `Disallowed imports detected. Only imports matching the allowlist are permitted:\\n Allowlist: ${allowlist.join(', ')}\\n Disallowed imports: ${[...disallowedFromEntry].join(', ')}`,\n );\n }\n\n const bundleContent = result.outputFiles[0]?.text;\n if (bundleContent === undefined) {\n throw new Error('Bundle content is undefined');\n }\n writeFileSync(tempFile, bundleContent, 'utf-8');\n\n const module = (await import(/* @vite-ignore */ pathToFileURL(tempFile).href)) as {\n default?: unknown;\n contract?: unknown;\n };\n unlinkSync(tempFile);\n\n let contract: unknown;\n\n if (module.default !== undefined) {\n contract = module.default;\n } else if (module.contract !== undefined) {\n contract = module.contract;\n } else {\n throw new Error(\n `Contract file must export a contract as default export or named export 'contract'. Found exports: ${Object.keys(module as Record<string, unknown>).join(', ') || 'none'}`,\n );\n }\n\n if (typeof contract !== 'object' || contract === null) {\n throw new Error(`Contract export must be an object, got ${typeof contract}`);\n }\n\n validatePurity(contract);\n\n // Blind cast: the loaded module was authored by user code\n // (typically via `defineContract` / a contract builder) and\n // its runtime shape is structurally a `Contract`, but the\n // dynamic import collapses the source typing. The contract\n // structural validation that asserts the shape happens\n // downstream at the `familyInstance.deserializeContract` seam\n // (e.g. in `executeContractEmit`); this helper only checks\n // purity here.\n return contract as unknown as Contract;\n } catch (error) {\n try {\n if (tempFile) {\n unlinkSync(tempFile);\n }\n } catch {\n // Ignore cleanup errors\n }\n\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Failed to load contract from ${entryPath}: ${String(error)}`);\n }\n}\n"],"mappings":";;;;;;;;AAYA,MAAM,oBAAoB,CAAC,kBAAkB,aAAa;AAE1D,SAAS,gBAAgB,YAAoB,WAA2C;CACtF,KAAK,MAAM,WAAW,WACpB,IAAI,QAAQ,SAAS,IAAI,GAAG;EAC1B,MAAM,SAAS,QAAQ,MAAM,GAAG,EAAE;EAClC,IAAI,eAAe,UAAU,WAAW,WAAW,GAAG,OAAO,EAAE,GAC7D,OAAO;CAEX,OAAO,IAAI,QAAQ,SAAS,GAAG,GAAG;EAChC,MAAM,SAAS,QAAQ,MAAM,GAAG,EAAE;EAClC,IAAI,WAAW,WAAW,MAAM,GAC9B,OAAO;CAEX,OAAO,IAAI,eAAe,SACxB,OAAO;CAGX,OAAO;AACT;AAEA,SAAS,eAAe,OAAsB;CAC5C,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC;CAGF,MAAM,uBAAO,IAAI,QAAQ;CAEzB,SAAS,MAAM,OAAsB;EACnC,IAAI,UAAU,QAAQ,OAAO,UAAU,UACrC;EAGF,IAAI,KAAK,IAAI,KAAK,GAChB,MAAM,IAAI,MAAM,8CAA8C;EAEhE,KAAK,IAAI,KAAK;EAEd,IAAI;GACF,KAAK,MAAM,OAAO,OAAO;IACvB,MAAM,aAAa,OAAO,yBAAyB,OAAO,GAAG;IAC7D,IAAI,eAAe,WAAW,OAAO,WAAW,MAC9C,MAAM,IAAI,MAAM,kDAAkD,IAAI,EAAE;IAE1E,IAAI,cAAc,OAAO,WAAW,UAAU,YAC5C,MAAM,IAAI,MAAM,6CAA6C,IAAI,EAAE;IAErE,MAAO,MAAkC,IAAI;GAC/C;EACF,UAAU;GACR,KAAK,OAAO,KAAK;EACnB;CACF;CAEA,IAAI;EACF,MAAM,KAAK;EACX,KAAK,UAAU,KAAK;CACtB,SAAS,OAAO;EACd,IAAI,iBAAiB,OAAO;GAC1B,IAAI,MAAM,QAAQ,SAAS,QAAQ,KAAK,MAAM,QAAQ,SAAS,UAAU,GACvE,MAAM;GAER,MAAM,IAAI,MAAM,6CAA6C,MAAM,SAAS;EAC9E;EACA,MAAM,IAAI,MAAM,0CAA0C;CAC5D;AACF;AAEA,SAAS,4BACP,WACA,WACA,WACQ;CAOR,MAAM,WAAWA,QAAY,SAAS;CACtC,SAAS,YAAY,UAA2B;EAC9C,OAAO,aAAa,YAAY,aAAa,aAAa,aAAa;CACzE;CACA,OAAO;EACL,MAAM;EACN,MAAM,OAAO;GACX,MAAM,UAAU,EAAE,QAAQ,KAAK,IAAI,SAAS;IAC1C,IAAI,KAAK,SAAS,eAChB;IAEF,IAAI,KAAK,KAAK,WAAW,GAAG,KAAK,KAAK,KAAK,WAAW,GAAG,GACvD;IAEF,IAAI,YAAY,KAAK,QAAQ,KAAK,CAAC,gBAAgB,KAAK,MAAM,SAAS,GAAG;KACxE,UAAU,IAAI,KAAK,IAAI;KACvB,OAAO;MACL,MAAM,KAAK;MACX,UAAU;KACZ;IACF;GAEF,CAAC;EACH;CACF;AACF;;;;;;;;;;;;;;;;AAiBA,eAAsB,mBACpB,WACA,SACmB;CACnB,MAAM,YAAY,SAAS,aAAa;CAExC,IAAI,CAAC,WAAW,SAAS,GACvB,MAAM,IAAI,MAAM,4BAA4B,WAAW;CAGzD,MAAM,WAAW,KACf,OAAO,GACP,wBAAwB,KAAK,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAC5E;CAOA,MAAM,sCAAsB,IAAI,IAAY;CAE5C,IAAI;EACF,MAAM,SAAS,MAAM,MAAM;GACzB,aAAa,CAAC,SAAS;GACvB,QAAQ;GACR,QAAQ;GACR,UAAU;GACV,QAAQ;GACR,SAAS;GACT,OAAO;GACP,UAAU;GACV,SAAS,CAAC,4BAA4B,WAAW,WAAW,mBAAmB,CAAC;GAChF,UAAU;EACZ,CAAC;EAED,IAAI,OAAO,OAAO,SAAS,GAAG;GAC5B,MAAM,gBAAgB,OAAO,OAAO,KAAK,MAAwB,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI;GAClF,MAAM,IAAI,MAAM,mCAAmC,eAAe;EACpE;EAEA,IAAI,CAAC,OAAO,eAAe,OAAO,YAAY,WAAW,GACvD,MAAM,IAAI,MAAM,yCAAyC;EAG3D,IAAI,oBAAoB,OAAO,GAC7B,MAAM,IAAI,MACR,iGAAiG,UAAU,KAAK,IAAI,EAAE,0BAA0B,CAAC,GAAG,mBAAmB,CAAC,CAAC,KAAK,IAAI,GACpL;EAGF,MAAM,gBAAgB,OAAO,YAAY,EAAE,EAAE;EAC7C,IAAI,kBAAkB,KAAA,GACpB,MAAM,IAAI,MAAM,6BAA6B;EAE/C,cAAc,UAAU,eAAe,OAAO;EAE9C,MAAM,SAAU,MAAM;;GAA0B,cAAc,QAAQ,CAAC,CAAC;;EAIxE,WAAW,QAAQ;EAEnB,IAAI;EAEJ,IAAI,OAAO,YAAY,KAAA,GACrB,WAAW,OAAO;OACb,IAAI,OAAO,aAAa,KAAA,GAC7B,WAAW,OAAO;OAElB,MAAM,IAAI,MACR,qGAAqG,OAAO,KAAK,MAAiC,CAAC,CAAC,KAAK,IAAI,KAAK,QACpK;EAGF,IAAI,OAAO,aAAa,YAAY,aAAa,MAC/C,MAAM,IAAI,MAAM,0CAA0C,OAAO,UAAU;EAG7E,eAAe,QAAQ;EAUvB,OAAO;CACT,SAAS,OAAO;EACd,IAAI;GACF,IAAI,UACF,WAAW,QAAQ;EAEvB,QAAQ,CAER;EAEA,IAAI,iBAAiB,OACnB,MAAM;EAER,MAAM,IAAI,MAAM,gCAAgC,UAAU,IAAI,OAAO,KAAK,GAAG;CAC/E;AACF"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["resolvePath"],"sources":["../../src/load-ts-contract.ts"],"sourcesContent":["import { existsSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { pathToFileURL } from 'node:url';\nimport type { Contract } from '@prisma-next/contract/types';\nimport { CliStructuredError } from '@prisma-next/errors/control';\nimport { InternalError } from '@prisma-next/utils/internal-error';\nimport type { Plugin } from 'esbuild';\nimport { build } from 'esbuild';\nimport { join, resolve as resolvePath } from 'pathe';\n\nexport interface LoadTsContractOptions {\n readonly allowlist?: ReadonlyArray<string>;\n}\n\ntype ContractSourceErrorCode =\n | 'CONTRACT.EXPORT_INVALID'\n | 'CONTRACT.SOURCE_LOAD_FAILED'\n | 'CONTRACT.SOURCE_IMPORT_DISALLOWED';\n\nfunction contractSourceError(\n code: ContractSourceErrorCode,\n message: string,\n options?: {\n readonly meta?: Record<string, unknown>;\n readonly cause?: unknown;\n },\n): CliStructuredError {\n const error = new CliStructuredError(\n code,\n message,\n options?.meta !== undefined ? { meta: options.meta } : undefined,\n );\n if (options?.cause !== undefined) {\n error.cause = options.cause;\n }\n return error;\n}\n\nconst DEFAULT_ALLOWLIST = ['@prisma-next/*', 'node:crypto'];\n\nfunction isAllowedImport(importPath: string, allowlist: ReadonlyArray<string>): boolean {\n for (const pattern of allowlist) {\n if (pattern.endsWith('/*')) {\n const prefix = pattern.slice(0, -2);\n if (importPath === prefix || importPath.startsWith(`${prefix}/`)) {\n return true;\n }\n } else if (pattern.endsWith('*')) {\n const prefix = pattern.slice(0, -1);\n if (importPath.startsWith(prefix)) {\n return true;\n }\n } else if (importPath === pattern) {\n return true;\n }\n }\n return false;\n}\n\nfunction validatePurity(value: unknown, entryPath: string): void {\n if (typeof value !== 'object' || value === null) {\n return;\n }\n\n const path = new WeakSet();\n\n function check(value: unknown): void {\n if (value === null || typeof value !== 'object') {\n return;\n }\n\n if (path.has(value)) {\n throw contractSourceError(\n 'CONTRACT.EXPORT_INVALID',\n 'Contract export contains circular references',\n { meta: { path: entryPath, reason: 'circular' } },\n );\n }\n path.add(value);\n\n try {\n for (const key in value) {\n const descriptor = Object.getOwnPropertyDescriptor(value, key);\n if (descriptor && (descriptor.get || descriptor.set)) {\n throw contractSourceError(\n 'CONTRACT.EXPORT_INVALID',\n `Contract export contains getter/setter at key \"${key}\"`,\n { meta: { path: entryPath, reason: 'getter', key } },\n );\n }\n if (descriptor && typeof descriptor.value === 'function') {\n throw contractSourceError(\n 'CONTRACT.EXPORT_INVALID',\n `Contract export contains function at key \"${key}\"`,\n { meta: { path: entryPath, reason: 'function', key } },\n );\n }\n check((value as Record<string, unknown>)[key]);\n }\n } finally {\n path.delete(value);\n }\n }\n\n try {\n check(value);\n JSON.stringify(value);\n } catch (error) {\n if (error instanceof Error) {\n if (error.message.includes('getter') || error.message.includes('circular')) {\n throw error;\n }\n throw contractSourceError(\n 'CONTRACT.EXPORT_INVALID',\n `Contract export is not JSON-serializable: ${error.message}`,\n { meta: { path: entryPath, reason: 'not-json-serializable' }, cause: error },\n );\n }\n throw contractSourceError(\n 'CONTRACT.EXPORT_INVALID',\n 'Contract export is not JSON-serializable',\n { meta: { path: entryPath, reason: 'not-json-serializable' } },\n );\n }\n}\n\nfunction createImportAllowlistPlugin(\n allowlist: ReadonlyArray<string>,\n entryPath: string,\n collected: Set<string>,\n): Plugin {\n // Match against several path forms that esbuild may use as the importer:\n // the absolute resolved entry, the value the caller passed (which may be\n // relative), and the conventional `<stdin>` placeholder. This is more\n // forgiving than `===` against a single form, which broke when esbuild\n // resolved the entry to an absolute path while the caller passed a\n // relative one (or vice versa).\n const entryAbs = resolvePath(entryPath);\n function isFromEntry(importer: string): boolean {\n return importer === entryAbs || importer === entryPath || importer === '<stdin>';\n }\n return {\n name: 'import-allowlist',\n setup(build) {\n build.onResolve({ filter: /.*/ }, (args) => {\n if (args.kind === 'entry-point') {\n return undefined;\n }\n if (args.path.startsWith('.') || args.path.startsWith('/')) {\n return undefined;\n }\n if (isFromEntry(args.importer) && !isAllowedImport(args.path, allowlist)) {\n collected.add(args.path);\n return {\n path: args.path,\n external: true,\n };\n }\n return undefined;\n });\n },\n };\n}\n\n/**\n * Loads a contract from a TypeScript file and returns it as Contract.\n *\n * **Responsibility: Parsing Only**\n * This function loads and parses a TypeScript contract file. It does NOT normalize the contract.\n * The contract should already be normalized if it was built using the contract builder.\n *\n * Normalization must happen in the contract builder when the contract is created.\n * This function only validates that the contract is JSON-serializable and returns it as-is.\n *\n * @param entryPath - Path to the TypeScript contract file\n * @param options - Optional configuration (import allowlist)\n * @returns The contract as Contract (should already be normalized)\n * @throws structured errors: `CLI.FILE_NOT_FOUND`, `CONTRACT.SOURCE_LOAD_FAILED`,\n * `CONTRACT.SOURCE_IMPORT_DISALLOWED`, `CONTRACT.MODULE_EXPORT_MISSING`, `CONTRACT.EXPORT_INVALID`\n */\nexport async function loadContractFromTs(\n entryPath: string,\n options?: LoadTsContractOptions,\n): Promise<Contract> {\n const allowlist = options?.allowlist ?? DEFAULT_ALLOWLIST;\n\n if (!existsSync(entryPath)) {\n throw new CliStructuredError('CLI.FILE_NOT_FOUND', `Contract file not found: ${entryPath}`, {\n where: { path: entryPath },\n });\n }\n\n const tempFile = join(\n tmpdir(),\n `prisma-next-contract-${Date.now()}-${Math.random().toString(36).slice(2)}.mjs`,\n );\n\n // Disallowed imports are collected by the allowlist resolver plugin itself,\n // which has the `importer` context to distinguish entry-direct imports from\n // transitive imports made inside allowlisted (`@prisma-next/*`) dependencies.\n // The metafile is intentionally not re-walked: it would surface internal\n // `node:*` imports inside framework code as false positives.\n const disallowedFromEntry = new Set<string>();\n\n try {\n const result = await build({\n entryPoints: [entryPath],\n bundle: true,\n format: 'esm',\n platform: 'node',\n target: 'es2022',\n outfile: tempFile,\n write: false,\n metafile: true,\n plugins: [createImportAllowlistPlugin(allowlist, entryPath, disallowedFromEntry)],\n logLevel: 'error',\n });\n\n if (result.errors.length > 0) {\n const errorMessages = result.errors.map((e: { text: string }) => e.text).join('\\n');\n throw contractSourceError(\n 'CONTRACT.SOURCE_LOAD_FAILED',\n `Failed to bundle contract file: ${errorMessages}`,\n { meta: { path: entryPath, stage: 'bundle' } },\n );\n }\n\n if (!result.outputFiles || result.outputFiles.length === 0) {\n throw new InternalError('No output files generated from bundling');\n }\n\n if (disallowedFromEntry.size > 0) {\n throw contractSourceError(\n 'CONTRACT.SOURCE_IMPORT_DISALLOWED',\n `Disallowed imports detected. Only imports matching the allowlist are permitted:\\n Allowlist: ${allowlist.join(', ')}\\n Disallowed imports: ${[...disallowedFromEntry].join(', ')}`,\n { meta: { allowlist: [...allowlist], disallowed: [...disallowedFromEntry] } },\n );\n }\n\n const bundleContent = result.outputFiles[0]?.text;\n if (bundleContent === undefined) {\n throw new InternalError('Bundle content is undefined');\n }\n writeFileSync(tempFile, bundleContent, 'utf-8');\n\n const module = (await import(/* @vite-ignore */ pathToFileURL(tempFile).href)) as {\n default?: unknown;\n contract?: unknown;\n };\n unlinkSync(tempFile);\n\n let contract: unknown;\n\n if (module.default !== undefined) {\n contract = module.default;\n } else if (module.contract !== undefined) {\n contract = module.contract;\n } else {\n throw new CliStructuredError(\n 'CONTRACT.MODULE_EXPORT_MISSING',\n `Contract file must export a contract as default export or named export 'contract'. Found exports: ${Object.keys(module as Record<string, unknown>).join(', ') || 'none'}`,\n { where: { path: entryPath } },\n );\n }\n\n if (typeof contract !== 'object' || contract === null) {\n throw contractSourceError(\n 'CONTRACT.EXPORT_INVALID',\n `Contract export must be an object, got ${typeof contract}`,\n { meta: { path: entryPath, reason: 'not-object' } },\n );\n }\n\n validatePurity(contract, entryPath);\n\n // Blind cast: the loaded module was authored by user code\n // (typically via `defineContract` / a contract builder) and\n // its runtime shape is structurally a `Contract`, but the\n // dynamic import collapses the source typing. The contract\n // structural validation that asserts the shape happens\n // downstream at the `familyInstance.deserializeContract` seam\n // (e.g. in `executeContractEmit`); this helper only checks\n // purity here.\n return contract as unknown as Contract;\n } catch (error) {\n try {\n if (tempFile) {\n unlinkSync(tempFile);\n }\n } catch {\n // Ignore cleanup errors\n }\n\n if (error instanceof Error) {\n throw error;\n }\n throw contractSourceError(\n 'CONTRACT.SOURCE_LOAD_FAILED',\n `Failed to load contract from ${entryPath}: ${String(error)}`,\n { meta: { path: entryPath, stage: 'import' }, cause: error },\n );\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { A as formatStyledHeader, F as CliStructuredError, _ as createTerminalUI, ct as errorUnexpected, g as parseGlobalFlagsOrExit, l as setCommandDescriptions, rt as errorRuntime, t as addGlobalOptions, u as setCommandExamples, y as handleResult } from "./command-helpers-
|
|
1
|
+
import { A as formatStyledHeader, F as CliStructuredError, _ as createTerminalUI, ct as errorUnexpected, g as parseGlobalFlagsOrExit, l as setCommandDescriptions, rt as errorRuntime, t as addGlobalOptions, u as setCommandExamples, y as handleResult } from "./command-helpers-D-jBl0Qt.mjs";
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import { loadConfig } from "@prisma-next/config-loader";
|
|
4
4
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
5
5
|
import { notOk, ok } from "@prisma-next/utils/result";
|
|
6
6
|
import { relative, resolve } from "pathe";
|
|
7
7
|
import { readFile, writeFile } from "node:fs/promises";
|
|
8
|
+
import { isStructuredError } from "@prisma-next/utils/structured-error";
|
|
8
9
|
import { EOL } from "node:os";
|
|
9
10
|
import { format } from "@prisma-next/psl-parser/format";
|
|
10
|
-
import { isStructuredError } from "@prisma-next/utils/structured-error";
|
|
11
11
|
//#region src/control-api/operations/format.ts
|
|
12
12
|
function resolveNewline(formatterNewline, eol) {
|
|
13
13
|
if (formatterNewline !== void 0) return formatterNewline;
|
|
@@ -100,4 +100,4 @@ function createFormatCommand() {
|
|
|
100
100
|
//#endregion
|
|
101
101
|
export { createFormatCommand as t };
|
|
102
102
|
|
|
103
|
-
//# sourceMappingURL=format-
|
|
103
|
+
//# sourceMappingURL=format-DI5qVr22.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format-
|
|
1
|
+
{"version":3,"file":"format-DI5qVr22.mjs","names":[],"sources":["../src/control-api/operations/format.ts","../src/commands/format.ts"],"sourcesContent":["import { readFile, writeFile } from 'node:fs/promises';\nimport { EOL } from 'node:os';\nimport { loadConfig } from '@prisma-next/config-loader';\nimport { type FormatOptions, format } from '@prisma-next/psl-parser/format';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { isStructuredError } from '@prisma-next/utils/structured-error';\nimport { CliStructuredError, errorRuntime, errorUnexpected } from '../../utils/cli-errors';\n\nexport interface FormatOperationOptions {\n readonly configPath?: string;\n readonly eol?: string;\n}\n\nexport interface FormatOperationResult {\n readonly formatted: boolean;\n readonly path?: string;\n}\n\nexport function resolveNewline(\n formatterNewline: 'LF' | 'CRLF' | undefined,\n eol: string,\n): 'LF' | 'CRLF' {\n if (formatterNewline !== undefined) {\n return formatterNewline;\n }\n return eol === '\\r\\n' ? 'CRLF' : 'LF';\n}\n\nexport async function executeFormat(\n options: FormatOperationOptions,\n): Promise<Result<FormatOperationResult, CliStructuredError>> {\n const eol = options.eol ?? EOL;\n\n let config: Awaited<ReturnType<typeof loadConfig>>;\n try {\n config = await loadConfig(options.configPath);\n } catch (error) {\n if (CliStructuredError.is(error)) {\n return notOk(error);\n }\n return notOk(errorUnexpected(error instanceof Error ? error.message : String(error)));\n }\n\n const source = config.contract?.source;\n if (source?.format !== 'psl') {\n return ok({ formatted: false });\n }\n\n const inputPath = source.inputs?.[0];\n if (inputPath === undefined) {\n return ok({ formatted: false });\n }\n\n let contents: string;\n try {\n contents = await readFile(inputPath, 'utf-8');\n } catch (error) {\n return notOk(\n errorRuntime('Failed to read contract source file', {\n why: error instanceof Error ? error.message : String(error),\n fix: `Check that ${inputPath} exists and is readable.`,\n }),\n );\n }\n\n const formatOptions: FormatOptions = {\n indent: config.formatter?.indent ?? 2,\n newline: resolveNewline(config.formatter?.newline, eol),\n };\n\n let formatted: string;\n try {\n formatted = format(contents, formatOptions);\n } catch (error) {\n if (isStructuredError(error) && error.code === 'PSL.PARSE_FAILED') {\n return notOk(\n errorRuntime('Cannot format PSL with parse errors', {\n why: error.message,\n fix: 'Fix the parse errors in your schema and try again.',\n meta: { diagnostics: error.meta?.['diagnostics'] },\n }),\n );\n }\n return notOk(errorUnexpected(error instanceof Error ? error.message : String(error)));\n }\n\n try {\n await writeFile(inputPath, formatted, 'utf-8');\n } catch (error) {\n return notOk(\n errorRuntime('Failed to write formatted contract source file', {\n why: error instanceof Error ? error.message : String(error),\n fix: `Check that ${inputPath} is writable.`,\n }),\n );\n }\n\n return ok({ formatted: true, path: inputPath });\n}\n","import { ifDefined } from '@prisma-next/utils/defined';\nimport { Command } from 'commander';\nimport { relative, resolve } from 'pathe';\nimport { executeFormat } from '../control-api/operations/format';\nimport {\n addGlobalOptions,\n setCommandDescriptions,\n setCommandExamples,\n} from '../utils/command-helpers';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { parseGlobalFlagsOrExit } from '../utils/global-flags';\nimport { handleResult } from '../utils/result-handler';\nimport { createTerminalUI } from '../utils/terminal-ui';\n\ninterface FormatCommandOptions extends CommonCommandOptions {\n readonly config?: string;\n}\n\nexport function createFormatCommand(): Command {\n const command = new Command('format');\n setCommandDescriptions(\n command,\n 'Format your PSL contract source',\n 'Formats the Prisma schema (PSL) contract source declared in your config\\n' +\n '(contract.source.inputs[0]) in place. Only runs when contract.source.format\\n' +\n \"is 'psl'; a TypeScript or unset source is left untouched. Indent and newline are\\n\" +\n 'read from the optional formatter config section, defaulting to two spaces and the\\n' +\n 'system newline.',\n );\n setCommandExamples(command, [\n 'prisma-next format',\n 'prisma-next format --config ./custom-config.ts',\n ]);\n addGlobalOptions(command)\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .action(async (options: FormatCommandOptions) => {\n const flags = parseGlobalFlagsOrExit(options);\n const ui = createTerminalUI(flags);\n\n if (!flags.json && !flags.quiet) {\n const displayConfigPath = options.config\n ? relative(process.cwd(), resolve(options.config))\n : 'prisma-next.config.ts';\n ui.stderr(\n formatStyledHeader({\n command: 'format',\n description: 'Format your PSL contract source',\n details: [{ label: 'config', value: displayConfigPath }],\n flags,\n }),\n );\n }\n\n const result = await executeFormat({ ...ifDefined('configPath', options.config) });\n\n const exitCode = handleResult(result, flags, ui, (value) => {\n if (flags.json) {\n ui.output(JSON.stringify(value));\n return;\n }\n if (flags.quiet) {\n return;\n }\n if (value.formatted) {\n ui.success(`Formatted ${relative(process.cwd(), value.path ?? '')}`);\n } else {\n ui.info('Nothing to format (contract source is not PSL).');\n }\n });\n process.exit(exitCode);\n });\n\n return command;\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R as errorConfigValidation } from "./command-helpers-
|
|
1
|
+
import { R as errorConfigValidation } from "./command-helpers-D-jBl0Qt.mjs";
|
|
2
2
|
import "@prisma-next/framework-components/components";
|
|
3
3
|
//#region src/utils/framework-components.ts
|
|
4
4
|
/**
|
|
@@ -55,4 +55,4 @@ function assertFrameworkComponentsCompatible(expectedFamilyId, expectedTargetId,
|
|
|
55
55
|
//#endregion
|
|
56
56
|
export { assertFrameworkComponentsCompatible as t };
|
|
57
57
|
|
|
58
|
-
//# sourceMappingURL=framework-components-
|
|
58
|
+
//# sourceMappingURL=framework-components-C4XACYNB.mjs.map
|
package/dist/{framework-components-DUN-TYDg.mjs.map → framework-components-C4XACYNB.mjs.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"framework-components-
|
|
1
|
+
{"version":3,"file":"framework-components-C4XACYNB.mjs","names":[],"sources":["../src/utils/framework-components.ts"],"sourcesContent":["import type { Contract } from '@prisma-next/contract/types';\nimport {\n checkContractComponentRequirements,\n type TargetBoundComponentDescriptor,\n} from '@prisma-next/framework-components/components';\nimport type { ControlStack } from '@prisma-next/framework-components/control';\nimport { errorConfigValidation, errorContractMissingExtensions } from './cli-errors';\n\n/**\n * Asserts that all framework components are compatible with the expected family and target.\n *\n * This function validates that each component in the framework components array:\n * - Has kind 'target', 'adapter', 'extension', or 'driver'\n * - Has familyId matching expectedFamilyId\n * - Has targetId matching expectedTargetId\n *\n * This validation happens at the CLI composition boundary, before passing components\n * to typed planner/runner instances. It fills the gap between runtime validation\n * (via `validateConfig()`) and compile-time type enforcement.\n *\n * @param expectedFamilyId - The expected family ID (e.g., 'sql')\n * @param expectedTargetId - The expected target ID (e.g., 'postgres')\n * @param frameworkComponents - Array of framework components to validate\n * @returns The same array typed as TargetBoundComponentDescriptor\n * @throws CliStructuredError if any component is incompatible\n *\n * @example\n * ```ts\n * const config = await loadConfig();\n * const frameworkComponents = [config.target, config.adapter, ...(config.extensions ?? [])];\n *\n * // Validate and type-narrow components before passing to planner\n * const typedComponents = assertFrameworkComponentsCompatible(\n * config.family.familyId,\n * config.target.targetId,\n * frameworkComponents\n * );\n *\n * const planner = target.migrations.createPlanner(familyInstance);\n * planner.plan({ contract, schema, policy, frameworkComponents: typedComponents });\n * ```\n */\nexport function assertFrameworkComponentsCompatible<\n TFamilyId extends string,\n TTargetId extends string,\n>(\n expectedFamilyId: TFamilyId,\n expectedTargetId: TTargetId,\n frameworkComponents: ReadonlyArray<unknown>,\n): ReadonlyArray<TargetBoundComponentDescriptor<TFamilyId, TTargetId>> {\n for (let i = 0; i < frameworkComponents.length; i++) {\n const component = frameworkComponents[i];\n\n // Check that component is an object\n if (typeof component !== 'object' || component === null) {\n throw errorConfigValidation('frameworkComponents[]', {\n why: `Framework component at index ${i} must be an object`,\n });\n }\n\n const record = component as Record<string, unknown>;\n\n // Check kind\n if (!Object.hasOwn(record, 'kind')) {\n throw errorConfigValidation('frameworkComponents[].kind', {\n why: `Framework component at index ${i} must have 'kind' property`,\n });\n }\n\n const kind = record['kind'];\n if (kind !== 'target' && kind !== 'adapter' && kind !== 'extension' && kind !== 'driver') {\n throw errorConfigValidation('frameworkComponents[].kind', {\n why: `Framework component at index ${i} has invalid kind '${String(kind)}' (must be 'target', 'adapter', 'extension', or 'driver')`,\n });\n }\n\n // Check familyId\n if (!Object.hasOwn(record, 'familyId')) {\n throw errorConfigValidation('frameworkComponents[].familyId', {\n why: `Framework component at index ${i} (kind: ${String(kind)}) must have 'familyId' property`,\n });\n }\n\n const familyId = record['familyId'];\n if (familyId !== expectedFamilyId) {\n throw errorConfigValidation('frameworkComponents[].familyId', {\n why: `Framework component at index ${i} (kind: ${String(kind)}) has familyId '${String(familyId)}' but expected '${expectedFamilyId}'`,\n });\n }\n\n // Check targetId\n if (!Object.hasOwn(record, 'targetId')) {\n throw errorConfigValidation('frameworkComponents[].targetId', {\n why: `Framework component at index ${i} (kind: ${String(kind)}) must have 'targetId' property`,\n });\n }\n\n const targetId = record['targetId'];\n if (targetId !== expectedTargetId) {\n throw errorConfigValidation('frameworkComponents[].targetId', {\n why: `Framework component at index ${i} (kind: ${String(kind)}) has targetId '${String(targetId)}' but expected '${expectedTargetId}'`,\n });\n }\n }\n\n // Type assertion is safe because we've validated all components above\n return frameworkComponents as ReadonlyArray<TargetBoundComponentDescriptor<TFamilyId, TTargetId>>;\n}\n\n/**\n * Validates that a contract is compatible with the configured target, adapter,\n * and extension packs. Throws on family/target mismatches or missing extension packs.\n *\n * This check ensures the emitted contract matches the CLI config before running\n * commands that depend on the contract (e.g., db verify, db sign).\n *\n * @param contract - The contract to validate (must include targetFamily, target, extensions).\n * @param stack - The control plane stack (target, adapter, driver, extensions).\n *\n * @throws {CliStructuredError} errorConfigValidation when contract.targetFamily or contract.target\n * doesn't match the configured family/target.\n * @throws {CliStructuredError} errorContractMissingExtensions when the contract requires\n * extension packs that are not provided in the config (includes all missing packs in error.meta).\n *\n * @example\n * ```ts\n * import { assertContractRequirementsSatisfied } from './framework-components';\n *\n * const config = await loadConfig();\n * const contract = await loadContractJson(config.contract.output);\n * const stack = createControlStack({ family: config.family, target: config.target, adapter: config.adapter, ... });\n *\n * // Throws if contract is incompatible with config\n * assertContractRequirementsSatisfied({ contract, stack });\n * ```\n */\nexport function assertContractRequirementsSatisfied<\n TFamilyId extends string,\n TTargetId extends string,\n>({\n contract,\n stack,\n}: {\n readonly contract: Pick<Contract, 'targetFamily' | 'target' | 'extensions'>;\n readonly stack: ControlStack<TFamilyId, TTargetId>;\n}): void {\n const providedComponentIds = new Set<string>([\n stack.target.id,\n ...(stack.adapter ? [stack.adapter.id] : []),\n ]);\n for (const extension of stack.extensions) {\n providedComponentIds.add(extension.id);\n }\n\n const result = checkContractComponentRequirements({\n contract,\n expectedTargetFamily: stack.target.familyId,\n expectedTargetId: stack.target.targetId,\n providedComponentIds,\n });\n\n if (result.familyMismatch) {\n throw errorConfigValidation('contract.targetFamily', {\n why: `Contract was emitted for family '${result.familyMismatch.actual}' but CLI config is wired to '${result.familyMismatch.expected}'.`,\n });\n }\n\n if (result.targetMismatch) {\n throw errorConfigValidation('contract.target', {\n why: `Contract target '${result.targetMismatch.actual}' does not match CLI target '${result.targetMismatch.expected}'.`,\n });\n }\n\n if (result.missingExtensionPackIds.length > 0) {\n throw errorContractMissingExtensions({\n missingExtensions: result.missingExtensionPackIds,\n providedComponentIds: [...providedComponentIds],\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,SAAgB,oCAId,kBACA,kBACA,qBACqE;CACrE,KAAK,IAAI,IAAI,GAAG,IAAI,oBAAoB,QAAQ,KAAK;EACnD,MAAM,YAAY,oBAAoB;EAGtC,IAAI,OAAO,cAAc,YAAY,cAAc,MACjD,MAAM,sBAAsB,yBAAyB,EACnD,KAAK,gCAAgC,EAAE,oBACzC,CAAC;EAGH,MAAM,SAAS;EAGf,IAAI,CAAC,OAAO,OAAO,QAAQ,MAAM,GAC/B,MAAM,sBAAsB,8BAA8B,EACxD,KAAK,gCAAgC,EAAE,4BACzC,CAAC;EAGH,MAAM,OAAO,OAAO;EACpB,IAAI,SAAS,YAAY,SAAS,aAAa,SAAS,eAAe,SAAS,UAC9E,MAAM,sBAAsB,8BAA8B,EACxD,KAAK,gCAAgC,EAAE,qBAAqB,OAAO,IAAI,EAAE,2DAC3E,CAAC;EAIH,IAAI,CAAC,OAAO,OAAO,QAAQ,UAAU,GACnC,MAAM,sBAAsB,kCAAkC,EAC5D,KAAK,gCAAgC,EAAE,UAAU,OAAO,IAAI,EAAE,iCAChE,CAAC;EAGH,MAAM,WAAW,OAAO;EACxB,IAAI,aAAa,kBACf,MAAM,sBAAsB,kCAAkC,EAC5D,KAAK,gCAAgC,EAAE,UAAU,OAAO,IAAI,EAAE,kBAAkB,OAAO,QAAQ,EAAE,kBAAkB,iBAAiB,GACtI,CAAC;EAIH,IAAI,CAAC,OAAO,OAAO,QAAQ,UAAU,GACnC,MAAM,sBAAsB,kCAAkC,EAC5D,KAAK,gCAAgC,EAAE,UAAU,OAAO,IAAI,EAAE,iCAChE,CAAC;EAGH,MAAM,WAAW,OAAO;EACxB,IAAI,aAAa,kBACf,MAAM,sBAAsB,kCAAkC,EAC5D,KAAK,gCAAgC,EAAE,UAAU,OAAO,IAAI,EAAE,kBAAkB,OAAO,QAAQ,EAAE,kBAAkB,iBAAiB,GACtI,CAAC;CAEL;CAGA,OAAO;AACT"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { F as CliStructuredError, _ as createTerminalUI, b as formatErrorJson, x as formatErrorOutput } from "./command-helpers-
|
|
1
|
+
import { F as CliStructuredError$1, _ as createTerminalUI, b as formatErrorJson, x as formatErrorOutput } from "./command-helpers-D-jBl0Qt.mjs";
|
|
2
2
|
import { a as starterSchema, c as version, i as schemaSample, n as dbFile, o as targetLabel, r as defaultSchemaPath, s as targetPackageName, t as configFile } from "./cli.mjs";
|
|
3
3
|
import { i as renderInitOutro, n as buildNextSteps, r as formatInitJson, t as InitOutputSchema } from "./output-DWNYqZGc.mjs";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
|
+
import { CliStructuredError } from "@prisma-next/errors/control";
|
|
5
6
|
import { basename, dirname, extname, isAbsolute, join, normalize } from "pathe";
|
|
6
7
|
import { blindCast } from "@prisma-next/utils/casts";
|
|
7
8
|
import * as clack from "@clack/prompts";
|
|
8
9
|
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { InternalError } from "@prisma-next/utils/internal-error";
|
|
9
11
|
import { execFile } from "node:child_process";
|
|
10
12
|
import { promisify } from "node:util";
|
|
11
13
|
import { detect, getUserAgent } from "package-manager-detector/detect";
|
|
@@ -182,7 +184,7 @@ function stripQuotes(value) {
|
|
|
182
184
|
* the user was never given the choice — `--force` is the contract.
|
|
183
185
|
*/
|
|
184
186
|
function errorInitReinitNeedsForce() {
|
|
185
|
-
return new CliStructuredError("CLI.INIT_REINIT_NEEDS_FORCE", "Project is already initialized", {
|
|
187
|
+
return new CliStructuredError$1("CLI.INIT_REINIT_NEEDS_FORCE", "Project is already initialized", {
|
|
186
188
|
why: "A `prisma-next.config.ts` already exists in this directory. Re-running `init` would overwrite the scaffolded files; in non-interactive mode `init` will not do that without `--force`.",
|
|
187
189
|
fix: "Pass `--force` to overwrite the existing scaffold, or run `init` interactively to confirm.",
|
|
188
190
|
docsUrl: "https://prisma-next.dev/docs/cli/init"
|
|
@@ -207,7 +209,7 @@ function errorInitMissingFlags(options) {
|
|
|
207
209
|
default: return `--${flag} <value>`;
|
|
208
210
|
}
|
|
209
211
|
}).join(" ");
|
|
210
|
-
return new CliStructuredError("CLI.INIT_MISSING_FLAGS", "Missing required flags", {
|
|
212
|
+
return new CliStructuredError$1("CLI.INIT_MISSING_FLAGS", "Missing required flags", {
|
|
211
213
|
why: `${options.why} Missing required flag(s): ${flagList}.`,
|
|
212
214
|
fix: `Re-run with the missing flag(s) supplied, e.g. \`prisma-next init --yes ${fixList}\`. Use \`prisma-next init --help\` to see every flag.`,
|
|
213
215
|
docsUrl: "https://prisma-next.dev/docs/cli/init",
|
|
@@ -219,7 +221,7 @@ function errorInitMissingFlags(options) {
|
|
|
219
221
|
* allowed values in `meta` for machine-readable consumption.
|
|
220
222
|
*/
|
|
221
223
|
function errorInitInvalidFlagValue(options) {
|
|
222
|
-
return new CliStructuredError("CLI.INIT_INVALID_FLAG_VALUE", `Invalid value for --${options.flag}`, {
|
|
224
|
+
return new CliStructuredError$1("CLI.INIT_INVALID_FLAG_VALUE", `Invalid value for --${options.flag}`, {
|
|
223
225
|
why: `\`--${options.flag} ${options.value}\` is not one of: ${options.allowed.join(", ")}.`,
|
|
224
226
|
fix: `Use one of: ${options.allowed.map((v) => `--${options.flag} ${v}`).join(", ")}.`,
|
|
225
227
|
docsUrl: "https://prisma-next.dev/docs/cli/init",
|
|
@@ -237,7 +239,7 @@ function errorInitInvalidFlagValue(options) {
|
|
|
237
239
|
*/
|
|
238
240
|
function errorInitAuthoringSchemaPathMismatch(options) {
|
|
239
241
|
const expectedAuthoring = options.expectedExtension === ".ts" ? "typescript" : "psl";
|
|
240
|
-
return new CliStructuredError("CLI.INIT_AUTHORING_SCHEMA_PATH_MISMATCH", "Authoring and schema path do not match", {
|
|
242
|
+
return new CliStructuredError$1("CLI.INIT_AUTHORING_SCHEMA_PATH_MISMATCH", "Authoring and schema path do not match", {
|
|
241
243
|
why: `\`--authoring ${options.authoring}\` requires a schema file ending in ${options.expectedExtension}, but \`--schema-path ${options.schemaPath}\` ends in ${options.actualExtension}.`,
|
|
242
244
|
fix: `Use a matching pair, for example \`--authoring ${expectedAuthoring} --schema-path <path>${options.expectedExtension}\`, or change \`--authoring\` to match the path you supplied. You can also omit \`--schema-path\` to use the default for the chosen authoring.`,
|
|
243
245
|
docsUrl: "https://prisma-next.dev/docs/cli/init",
|
|
@@ -257,7 +259,7 @@ function errorInitAuthoringSchemaPathMismatch(options) {
|
|
|
257
259
|
* 3 (USER_ABORTED).
|
|
258
260
|
*/
|
|
259
261
|
function errorInitUserAborted() {
|
|
260
|
-
return new CliStructuredError("CLI.INIT_USER_ABORTED", "Init cancelled", {
|
|
262
|
+
return new CliStructuredError$1("CLI.INIT_USER_ABORTED", "Init cancelled", {
|
|
261
263
|
why: "The interactive prompt was cancelled before all required inputs were supplied. No files were modified.",
|
|
262
264
|
fix: "Re-run `prisma-next init` and complete the prompts, or pass the required inputs as flags (see `--help`) for a non-interactive run.",
|
|
263
265
|
severity: "info"
|
|
@@ -272,7 +274,7 @@ function errorInitUserAborted() {
|
|
|
272
274
|
* mode is supposed to prevent.
|
|
273
275
|
*/
|
|
274
276
|
function errorInitStrictProbeWithoutProbe() {
|
|
275
|
-
return new CliStructuredError("CLI.INIT_STRICT_PROBE_WITHOUT_PROBE", "`--strict-probe` requires `--probe-db`", {
|
|
277
|
+
return new CliStructuredError$1("CLI.INIT_STRICT_PROBE_WITHOUT_PROBE", "`--strict-probe` requires `--probe-db`", {
|
|
276
278
|
why: "`--strict-probe` only changes how a *failed* probe is reported; without `--probe-db` no probe is attempted in the first place. (`init` is offline-by-default — it never opens a connection to your database without explicit consent.)",
|
|
277
279
|
fix: "Add `--probe-db` to opt in to the probe, or drop `--strict-probe` if you do not need the version check.",
|
|
278
280
|
docsUrl: "https://prisma-next.dev/docs/cli/init"
|
|
@@ -287,7 +289,7 @@ function errorInitStrictProbeWithoutProbe() {
|
|
|
287
289
|
*/
|
|
288
290
|
function errorInitInstallFailed(options) {
|
|
289
291
|
const trimmed = options.stderrLines.map((s) => s.trim()).filter(Boolean);
|
|
290
|
-
return new CliStructuredError("CLI.INIT_INSTALL_FAILED", "Failed to install dependencies", {
|
|
292
|
+
return new CliStructuredError$1("CLI.INIT_INSTALL_FAILED", "Failed to install dependencies", {
|
|
291
293
|
why: trimmed.length === 0 ? "The package manager exited with an error and no recoverable fallback applied." : `The package manager exited with: ${trimmed[0]}`,
|
|
292
294
|
fix: `Install manually:\n ${options.addCommand}\n ${options.addDevCommand}\nThen run \`${options.emitCommand}\` to emit the contract.`,
|
|
293
295
|
docsUrl: "https://prisma-next.dev/docs/cli/init",
|
|
@@ -309,7 +311,7 @@ function errorInitInstallFailed(options) {
|
|
|
309
311
|
* and re-run.
|
|
310
312
|
*/
|
|
311
313
|
function errorInitInvalidManifest(options) {
|
|
312
|
-
return new CliStructuredError("CLI.INIT_INVALID_MANIFEST", `Failed to parse ${options.path}`, {
|
|
314
|
+
return new CliStructuredError$1("CLI.INIT_INVALID_MANIFEST", `Failed to parse ${options.path}`, {
|
|
313
315
|
why: `\`${options.path}\` is not valid JSON: ${options.cause}`,
|
|
314
316
|
fix: `Fix the JSON syntax in \`${options.path}\` (a missing comma or unbalanced brace is the most common cause), then re-run \`prisma-next init\`.`,
|
|
315
317
|
docsUrl: "https://prisma-next.dev/docs/cli/init",
|
|
@@ -334,7 +336,7 @@ function errorInitInvalidManifest(options) {
|
|
|
334
336
|
* re-run.
|
|
335
337
|
*/
|
|
336
338
|
function errorInitInvalidTsconfig(options) {
|
|
337
|
-
return new CliStructuredError("CLI.INIT_INVALID_TSCONFIG", `Failed to parse ${options.path}`, {
|
|
339
|
+
return new CliStructuredError$1("CLI.INIT_INVALID_TSCONFIG", `Failed to parse ${options.path}`, {
|
|
338
340
|
why: `\`${options.path}\` is not valid JSON or JSONC: ${options.cause}`,
|
|
339
341
|
fix: `Fix the syntax in \`${options.path}\` and re-run \`prisma-next init\`. \`init\` accepts JSONC (comments and trailing commas) but cannot recover from unbalanced braces or missing commas.`,
|
|
340
342
|
docsUrl: "https://prisma-next.dev/docs/cli/init",
|
|
@@ -359,7 +361,7 @@ function errorInitInvalidTsconfig(options) {
|
|
|
359
361
|
* carries `filesWritten` so a follow-up agent can resume manually.
|
|
360
362
|
*/
|
|
361
363
|
function errorInitProbeFailed(options) {
|
|
362
|
-
return new CliStructuredError("CLI.INIT_PROBE_FAILED", "Database probe failed", {
|
|
364
|
+
return new CliStructuredError$1("CLI.INIT_PROBE_FAILED", "Database probe failed", {
|
|
363
365
|
why: `\`--probe-db\` could not complete and \`--strict-probe\` was set: ${options.cause}`,
|
|
364
366
|
fix: "Confirm `DATABASE_URL` points at a reachable server, or drop `--strict-probe` to treat probe failures as warnings.",
|
|
365
367
|
docsUrl: "https://prisma-next.dev/docs/cli/init",
|
|
@@ -376,7 +378,7 @@ function errorInitProbeFailed(options) {
|
|
|
376
378
|
* `5 = EMIT_FAILED`.
|
|
377
379
|
*/
|
|
378
380
|
function errorInitEmitFailed(options) {
|
|
379
|
-
return new CliStructuredError("CLI.INIT_EMIT_FAILED", "Failed to emit contract", {
|
|
381
|
+
return new CliStructuredError$1("CLI.INIT_EMIT_FAILED", "Failed to emit contract", {
|
|
380
382
|
why: `\`prisma-next contract emit\` failed: ${options.cause}`,
|
|
381
383
|
fix: `Inspect your contract file, fix the underlying issue, then re-run \`${options.emitCommand}\`. Pass \`-v\` for the full error envelope.`,
|
|
382
384
|
docsUrl: "https://prisma-next.dev/docs/cli/contract-emit",
|
|
@@ -398,7 +400,7 @@ function errorInitEmitFailed(options) {
|
|
|
398
400
|
* semantics. Maps to exit code `6 = SKILL_INSTALL_FAILED`.
|
|
399
401
|
*/
|
|
400
402
|
function errorInitSkillInstallFailed(options) {
|
|
401
|
-
return new CliStructuredError("CLI.INIT_SKILL_INSTALL_FAILED", "Failed to install Prisma Next skills", {
|
|
403
|
+
return new CliStructuredError$1("CLI.INIT_SKILL_INSTALL_FAILED", "Failed to install Prisma Next skills", {
|
|
402
404
|
why: `\`${options.skillInstallCommand}\` exited with an error: ${options.cause}`,
|
|
403
405
|
fix: `Either:
|
|
404
406
|
- Re-run \`prisma-next init --no-skill${options.filesWritten.length > 0 ? " --force" : ""}\` to skip the skill install for this run, or\n - Fix the underlying issue (network, npm registry, \`npx skills\` on PATH) and install manually:\n ${options.skillInstallCommand}`,
|
|
@@ -1008,7 +1010,7 @@ async function defaultProbePostgres(databaseUrl, baseDir, overrides) {
|
|
|
1008
1010
|
*/
|
|
1009
1011
|
function parsePostgresVersion(versionString) {
|
|
1010
1012
|
const match = versionString.match(/PostgreSQL\s+(\d+(?:\.\d+)?)/i);
|
|
1011
|
-
if (match === null || match[1] === void 0) throw new
|
|
1013
|
+
if (match === null || match[1] === void 0) throw new CliStructuredError("CLI.INIT_PROBE_FAILED", `Could not parse PostgreSQL version from \`${versionString}\``);
|
|
1012
1014
|
return match[1];
|
|
1013
1015
|
}
|
|
1014
1016
|
async function defaultProbeMongo(databaseUrl, baseDir, overrides) {
|
|
@@ -1017,7 +1019,7 @@ async function defaultProbeMongo(databaseUrl, baseDir, overrides) {
|
|
|
1017
1019
|
try {
|
|
1018
1020
|
const buildInfo = await client.db().admin().command({ buildInfo: 1 });
|
|
1019
1021
|
const versionString = String(buildInfo.version ?? "");
|
|
1020
|
-
if (versionString.length === 0) throw new
|
|
1022
|
+
if (versionString.length === 0) throw new CliStructuredError("CLI.INIT_PROBE_FAILED", "buildInfo did not include a `version` field");
|
|
1021
1023
|
return { serverVersion: versionString };
|
|
1022
1024
|
} finally {
|
|
1023
1025
|
await client.close().catch(() => void 0);
|
|
@@ -1378,7 +1380,7 @@ function renderTemplate(templateFile, variableNames, vars) {
|
|
|
1378
1380
|
let result = readFileSync(join(import.meta.dirname, templateFile), "utf-8");
|
|
1379
1381
|
for (const key of variableNames) {
|
|
1380
1382
|
const value = vars[key];
|
|
1381
|
-
if (value === void 0) throw new
|
|
1383
|
+
if (value === void 0) throw new InternalError(`Template variable '${key}' is not defined`);
|
|
1382
1384
|
result = result.replaceAll(`{{${key}}}`, value);
|
|
1383
1385
|
}
|
|
1384
1386
|
return result;
|
|
@@ -1648,7 +1650,7 @@ async function runInit(baseDir, runOptions) {
|
|
|
1648
1650
|
canPrompt
|
|
1649
1651
|
});
|
|
1650
1652
|
} catch (error) {
|
|
1651
|
-
if (CliStructuredError.is(error)) return emitError(ui, flags, error);
|
|
1653
|
+
if (CliStructuredError$1.is(error)) return emitError(ui, flags, error);
|
|
1652
1654
|
throw error;
|
|
1653
1655
|
}
|
|
1654
1656
|
const pm = await detectPackageManager(baseDir);
|
|
@@ -1798,7 +1800,7 @@ async function runInit(baseDir, runOptions) {
|
|
|
1798
1800
|
hasTypesNode: parsedPackageJson !== null ? hasDirectDep(parsedPackageJson, "@types/node") : false
|
|
1799
1801
|
});
|
|
1800
1802
|
} catch (error) {
|
|
1801
|
-
if (CliStructuredError.is(error)) return emitError(ui, flags, error);
|
|
1803
|
+
if (CliStructuredError$1.is(error)) return emitError(ui, flags, error);
|
|
1802
1804
|
throw error;
|
|
1803
1805
|
}
|
|
1804
1806
|
warnings.push(...install.warnings);
|
|
@@ -1812,7 +1814,7 @@ async function runInit(baseDir, runOptions) {
|
|
|
1812
1814
|
});
|
|
1813
1815
|
contractEmitted = true;
|
|
1814
1816
|
} catch (error) {
|
|
1815
|
-
if (CliStructuredError.is(error)) return emitError(ui, flags, error);
|
|
1817
|
+
if (CliStructuredError$1.is(error)) return emitError(ui, flags, error);
|
|
1816
1818
|
throw error;
|
|
1817
1819
|
}
|
|
1818
1820
|
if (inputs.probeDb) {
|
|
@@ -1849,7 +1851,7 @@ async function runInit(baseDir, runOptions) {
|
|
|
1849
1851
|
skillRegistered = true;
|
|
1850
1852
|
} catch (error) {
|
|
1851
1853
|
spinner.stop("Agent-skill install failed");
|
|
1852
|
-
if (CliStructuredError.is(error)) return emitError(ui, flags, error);
|
|
1854
|
+
if (CliStructuredError$1.is(error)) return emitError(ui, flags, error);
|
|
1853
1855
|
throw error;
|
|
1854
1856
|
}
|
|
1855
1857
|
}
|
|
@@ -1876,7 +1878,7 @@ async function runInit(baseDir, runOptions) {
|
|
|
1876
1878
|
warnings
|
|
1877
1879
|
};
|
|
1878
1880
|
const validated = InitOutputSchema(output);
|
|
1879
|
-
if (validated instanceof Error || validated.problems !== void 0) return emitError(ui, flags, new CliStructuredError("CLI.INIT_INVALID_OUTPUT_DOCUMENT", "Init produced an invalid output document", {
|
|
1881
|
+
if (validated instanceof Error || validated.problems !== void 0) return emitError(ui, flags, new CliStructuredError$1("CLI.INIT_INVALID_OUTPUT_DOCUMENT", "Init produced an invalid output document", {
|
|
1880
1882
|
why: `The success document failed schema validation: ${String(validated)}`,
|
|
1881
1883
|
fix: "This is a bug in prisma-next. Please report it with the full `-v` output.",
|
|
1882
1884
|
docsUrl: "https://prisma-next.dev/docs/cli/init"
|
|
@@ -2148,7 +2150,7 @@ async function runEmit(ctx) {
|
|
|
2148
2150
|
const spinner = ctx.ui.spinner();
|
|
2149
2151
|
spinner.start("Emitting contract...");
|
|
2150
2152
|
try {
|
|
2151
|
-
const { executeContractEmit } = await import("./contract-emit-
|
|
2153
|
+
const { executeContractEmit } = await import("./contract-emit-hVdlaF2U.mjs").then((n) => n.t);
|
|
2152
2154
|
await executeContractEmit({ configPath: join(ctx.baseDir, "prisma-next.config.ts") });
|
|
2153
2155
|
spinner.stop("Contract emitted");
|
|
2154
2156
|
} catch (err) {
|
|
@@ -2201,4 +2203,4 @@ function sanitisePackageName(raw) {
|
|
|
2201
2203
|
//#endregion
|
|
2202
2204
|
export { runInit };
|
|
2203
2205
|
|
|
2204
|
-
//# sourceMappingURL=init-
|
|
2206
|
+
//# sourceMappingURL=init-ClrRkbwd.mjs.map
|