@prisma/composer 0.25.0 → 0.26.0-dev.1

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.
Files changed (58) hide show
  1. package/dist/{app-config-joXc-BKm-CdcXqNMD.mjs → app-config-CGJHcFb--CIEFCG4V.mjs} +3 -3
  2. package/dist/{app-config-joXc-BKm-CdcXqNMD.mjs.map → app-config-CGJHcFb--CIEFCG4V.mjs.map} +1 -1
  3. package/dist/{app-config-jGcCcxK4-DnzGGbDS.d.mts → app-config-DyCMnkDr-CguUhanl.d.mts} +4 -4
  4. package/dist/{app-config-jGcCcxK4-DnzGGbDS.d.mts.map → app-config-DyCMnkDr-CguUhanl.d.mts.map} +1 -1
  5. package/dist/config-CdxvLFzg.mjs.map +1 -1
  6. package/dist/config-i9yR7YWi.d.mts +1 -0
  7. package/dist/config.d.mts +2 -2
  8. package/dist/config.mjs +1 -1
  9. package/dist/control.d.mts +63 -44
  10. package/dist/control.d.mts.map +1 -1
  11. package/dist/control.mjs +10 -42
  12. package/dist/control.mjs.map +1 -1
  13. package/dist/deploy-i9yR7YWi.d.mts +1 -0
  14. package/dist/deploy.d.mts +2 -2
  15. package/dist/deploy.mjs +1 -1
  16. package/dist/deploy.mjs.map +1 -1
  17. package/dist/deployment-summary-37e6WEuh-BOTJynIi.mjs +168 -0
  18. package/dist/deployment-summary-37e6WEuh-BOTJynIi.mjs.map +1 -0
  19. package/dist/deployment-summary-CU-eVvJM-c3AqO52N.d.mts +26 -0
  20. package/dist/deployment-summary-CU-eVvJM-c3AqO52N.d.mts.map +1 -0
  21. package/dist/dev-Bmq-eLnv-DsenQNJM.mjs +154 -0
  22. package/dist/dev-Bmq-eLnv-DsenQNJM.mjs.map +1 -0
  23. package/dist/{execute-deploy-destroy-CvKCwVkD-Di3Ooeip.mjs → execute-deploy-destroy-c0eko3AO-BoK0-r1U.mjs} +43 -32
  24. package/dist/execute-deploy-destroy-c0eko3AO-BoK0-r1U.mjs.map +1 -0
  25. package/dist/{execute-dev-BLCK5wF7-CJvkj5wl.mjs → execute-dev-BICPaq1g-DQccw8kr.mjs} +180 -131
  26. package/dist/{execute-dev-BLCK5wF7-CJvkj5wl.mjs.map → execute-dev-BICPaq1g-DQccw8kr.mjs.map} +1 -1
  27. package/dist/{execute-log-B_zCJ17b-CE954g1a.mjs → execute-log-DivzSDBL-CBdSN2Vv.mjs} +7 -10
  28. package/dist/execute-log-DivzSDBL-CBdSN2Vv.mjs.map +1 -0
  29. package/dist/{local-target-DnA2E8IN.mjs → local-target-B4xoiDAV.mjs} +2 -2
  30. package/dist/{local-target-DnA2E8IN.mjs.map → local-target-B4xoiDAV.mjs.map} +1 -1
  31. package/dist/local-target.d.mts +1 -1
  32. package/dist/local-target.mjs +2 -2
  33. package/dist/nextjs-control.d.mts +4 -4
  34. package/dist/nextjs-control.mjs +2 -2
  35. package/dist/nextjs-control.mjs.map +1 -1
  36. package/dist/node-control.d.mts +4 -4
  37. package/dist/node-control.mjs +2 -2
  38. package/dist/node-control.mjs.map +1 -1
  39. package/dist/{pipeline-C7gNjMzJ-CzM8DNS_.mjs → pipeline-DYvl561J-DDtnkFPh.mjs} +278 -311
  40. package/dist/pipeline-DYvl561J-DDtnkFPh.mjs.map +1 -0
  41. package/dist/report.d.mts +4 -3
  42. package/dist/report.d.mts.map +1 -1
  43. package/dist/report.mjs +2 -2
  44. package/dist/report.mjs.map +1 -1
  45. package/dist/{run-alchemy-pANK8XNX-CgCpXLkG.mjs → run-alchemy-BEvB2sYQ-Cy2fhbcu.mjs} +2 -2
  46. package/dist/{run-alchemy-pANK8XNX-CgCpXLkG.mjs.map → run-alchemy-BEvB2sYQ-Cy2fhbcu.mjs.map} +1 -1
  47. package/package.json +11 -12
  48. package/skills/prisma-composer-core-concepts/SKILL.md +106 -45
  49. package/src/exports/testing.ts +1 -1
  50. package/dist/config-BGtKERz-.d.mts +0 -1
  51. package/dist/deploy-BGtKERz-.d.mts +0 -1
  52. package/dist/deployment-summary-DswOl_9E-B9MhYYDt.mjs +0 -70
  53. package/dist/deployment-summary-DswOl_9E-B9MhYYDt.mjs.map +0 -1
  54. package/dist/execute-deploy-destroy-CvKCwVkD-Di3Ooeip.mjs.map +0 -1
  55. package/dist/execute-log-B_zCJ17b-CE954g1a.mjs.map +0 -1
  56. package/dist/pipeline-C7gNjMzJ-CzM8DNS_.mjs.map +0 -1
  57. package/dist/result-CQIOBgdW.mjs +0 -247
  58. package/dist/result-CQIOBgdW.mjs.map +0 -1
@@ -2,14 +2,255 @@ import { t as blindCast } from "./casts-DpaahrlC-C5XMnP9S.mjs";
2
2
  import { t as CliStructuredError } from "./errors-0e8IVwzi.mjs";
3
3
  import { s as isNode, t as Load } from "./graph-BfKAg2Ap-LY3OAuK2.mjs";
4
4
  import "./dist-CR0-3j9o.mjs";
5
- import { i as effectResolutionDiagnostic } from "./result-CQIOBgdW.mjs";
6
5
  import * as mod from "node:module";
7
6
  import * as fs$1 from "node:fs";
8
7
  import { existsSync } from "node:fs";
9
8
  import * as path$1 from "node:path";
10
9
  import { fileURLToPath, pathToFileURL } from "node:url";
11
- import * as c12 from "c12";
12
- //#region ../../0-framework/3-tooling/cli/dist/load-entry-BjRb7YB3.mjs
10
+ //#region ../../0-framework/3-tooling/cli/dist/composer-config-CY9fET8H.mjs
11
+ /**
12
+ * What makes a `composer` section valid, shared by the CLI's section validator and the programmatic operations.
13
+ * It imports nothing from the CLI engine, because `@prisma/composer` must not depend on it.
14
+ */
15
+ const SECTION_FIX = "Write `composer: composer({ extensions: [...], state: ... })` in prisma.config.ts, with `import { defineConfig as composer } from '@prisma/composer/config'`.";
16
+ const MOVE_TO_SECTION_FIX = `${SECTION_FIX} If the project has a prisma-composer.config.ts, move its extensions and state into that section and delete the file.`;
17
+ const SECTION_IS_THE_ONLY_SOURCE = "Composer reads its configuration only from the `composer` section of prisma.config.ts.";
18
+ const DESCRIPTOR_FIX = "Use the descriptor the extension factory returns, unchanged, instead of building one by hand.";
19
+ const RETIRED_FILE_NAMES = [
20
+ "ts",
21
+ "mts",
22
+ "mjs",
23
+ "js"
24
+ ].map((extension) => `prisma-composer.config.${extension}`);
25
+ /** What the generated stack file throws when the file it imports has no `composer` export. */
26
+ function noComposerExportMessage(configFile) {
27
+ return `${configFile} declares no \`composer\` section. Pass the prisma.config.ts that declares it.`;
28
+ }
29
+ function configFileMissing(file) {
30
+ return {
31
+ code: "CONFIG.FILE_MISSING",
32
+ summary: `${file} does not exist.`,
33
+ fix: "Pass the path of the prisma.config.ts that declares the `composer` section.",
34
+ where: file
35
+ };
36
+ }
37
+ /** A prisma-composer.config.* beside the declaring file would otherwise be silently ignored. */
38
+ /** The path with symlinks resolved; the path itself when it cannot be resolved, so the check never throws. */
39
+ function realPath(file) {
40
+ try {
41
+ return fs$1.realpathSync(file);
42
+ } catch {
43
+ return file;
44
+ }
45
+ }
46
+ function retiredFileFinding(configFile) {
47
+ const directory = path$1.dirname(realPath(configFile));
48
+ const found = RETIRED_FILE_NAMES.map((name) => path$1.join(directory, name)).find((file) => fs$1.existsSync(file));
49
+ if (found === void 0) return void 0;
50
+ return {
51
+ code: "CONFIG.FILE_RETIRED",
52
+ summary: `${found} is no longer read.`,
53
+ why: SECTION_IS_THE_ONLY_SOURCE,
54
+ fix: MOVE_TO_SECTION_FIX,
55
+ where: found
56
+ };
57
+ }
58
+ function isObject(value) {
59
+ return typeof value === "object" && value !== null && !Array.isArray(value);
60
+ }
61
+ function isExtensionDescriptor(value) {
62
+ return isObject(value) && typeof value["id"] === "string" && value["id"].length > 0 && isObject(value["nodes"]);
63
+ }
64
+ function isStateDescriptor(value) {
65
+ return isObject(value) && typeof value["extension"] === "string" && typeof value["create"] === "function";
66
+ }
67
+ const stateInvalid = {
68
+ code: "CONFIG.FIELD_INVALID",
69
+ summary: "`composer.state` must be a state descriptor with a string `extension` and a `create` function.",
70
+ fix: "Set `state` to the state descriptor an extension provides, e.g. `state: prismaState()`.",
71
+ field: "state"
72
+ };
73
+ function unknownField(key) {
74
+ return {
75
+ code: "CONFIG.FIELD_UNKNOWN",
76
+ summary: `The \`composer\` section has no field \`${key}\`.`,
77
+ fix: key === "composer" ? "Pass the `composer` property of the prisma.config.ts export, not the whole export. The section takes only `extensions` and `state`." : `Remove \`${key}\`. The section takes only \`extensions\` and \`state\`.`,
78
+ field: key
79
+ };
80
+ }
81
+ function checkExtensions(value) {
82
+ if (!Array.isArray(value)) return {
83
+ extensions: [],
84
+ findings: [{
85
+ code: "CONFIG.FIELD_INVALID",
86
+ summary: "`composer.extensions` must be an array of extension descriptors.",
87
+ fix: "Set `extensions` to the descriptors your extensions provide, e.g. `extensions: [nodeBuild()]`.",
88
+ field: "extensions"
89
+ }]
90
+ };
91
+ const findings = [];
92
+ const extensions = [];
93
+ const seen = /* @__PURE__ */ new Set();
94
+ for (const [index, entry] of value.entries()) {
95
+ const field = `extensions[${index}]`;
96
+ if (!isObject(entry)) {
97
+ findings.push({
98
+ code: "CONFIG.FIELD_INVALID",
99
+ summary: `\`composer.${field}\` must be an extension descriptor object.`,
100
+ fix: "Put the descriptor an extension factory returns here, e.g. `nodeBuild()`, not the factory itself.",
101
+ field
102
+ });
103
+ continue;
104
+ }
105
+ const id = entry["id"];
106
+ if (typeof id !== "string" || id.length === 0) findings.push({
107
+ code: "CONFIG.FIELD_INVALID",
108
+ summary: `\`composer.${field}.id\` must be a non-empty string (the extension package name).`,
109
+ fix: DESCRIPTOR_FIX,
110
+ field: `${field}.id`
111
+ });
112
+ else if (seen.has(id)) findings.push({
113
+ code: "CONFIG.EXTENSION_DUPLICATE",
114
+ summary: `Extension "${id}" is listed more than once in \`composer.extensions\`.`,
115
+ fix: `Remove the repeated "${id}" entry from \`extensions\`.`,
116
+ field: `${field}.id`
117
+ });
118
+ else seen.add(id);
119
+ if (!isObject(entry["nodes"])) findings.push({
120
+ code: "CONFIG.FIELD_INVALID",
121
+ summary: `\`composer.${field}.nodes\` must be an object (the node-ID → control registry).`,
122
+ fix: DESCRIPTOR_FIX,
123
+ field: `${field}.nodes`
124
+ });
125
+ if (isExtensionDescriptor(entry)) extensions.push(entry);
126
+ }
127
+ return {
128
+ findings,
129
+ extensions
130
+ };
131
+ }
132
+ function checkFields(section) {
133
+ if (Object.hasOwn(section, "configPath")) return {
134
+ ok: false,
135
+ findings: [{
136
+ code: "CONFIG.FIELD_RETIRED",
137
+ summary: "`composer.configPath` is no longer supported: prisma-composer.config.ts is no longer read.",
138
+ why: SECTION_IS_THE_ONLY_SOURCE,
139
+ fix: `Replace \`configPath\` with the section itself. ${MOVE_TO_SECTION_FIX}`,
140
+ field: "configPath"
141
+ }]
142
+ };
143
+ if (Object.hasOwn(section, "composer")) return {
144
+ ok: false,
145
+ findings: [unknownField("composer")]
146
+ };
147
+ const findings = Object.keys(section).filter((key) => key !== "extensions" && key !== "state").map(unknownField);
148
+ const checked = checkExtensions(section["extensions"]);
149
+ findings.push(...checked.findings);
150
+ const state = section["state"];
151
+ if (!isStateDescriptor(state)) findings.push(stateInvalid);
152
+ const [first, ...rest] = findings;
153
+ if (first === void 0 && isStateDescriptor(state)) return {
154
+ ok: true,
155
+ value: {
156
+ extensions: checked.extensions,
157
+ state
158
+ }
159
+ };
160
+ return {
161
+ ok: false,
162
+ findings: [first ?? stateInvalid, ...rest]
163
+ };
164
+ }
165
+ /**
166
+ * Checks the fields that identify each descriptor and returns the descriptors as the config file's own objects.
167
+ * Never throws: a section's values are user code, and a throwing getter or Proxy trap becomes a finding.
168
+ */
169
+ function checkComposerSection(raw) {
170
+ if (!isObject(raw)) return {
171
+ ok: false,
172
+ findings: [{
173
+ code: "CONFIG.FIELD_INVALID",
174
+ summary: "The `composer` section of prisma.config.ts must be an object.",
175
+ fix: SECTION_FIX
176
+ }]
177
+ };
178
+ try {
179
+ return checkFields({ ...raw });
180
+ } catch (cause) {
181
+ return {
182
+ ok: false,
183
+ findings: [{
184
+ code: "CONFIG.FIELD_INVALID",
185
+ summary: "The `composer` section of prisma.config.ts could not be read.",
186
+ why: `Reading it threw: ${cause instanceof Error ? cause.message : String(cause)}`,
187
+ fix: SECTION_FIX
188
+ }]
189
+ };
190
+ }
191
+ }
192
+ /**
193
+ * The whole check, shared by the section validator and the programmatic operations so both report the same findings in the same order: the retired file beside the declaring `configFile` (resolved through symlinks), then every field finding.
194
+ */
195
+ function checkComposerConfig(raw, configFile) {
196
+ const retired = retiredFileFinding(configFile);
197
+ const checked = checkComposerSection(raw);
198
+ if (retired === void 0) return checked;
199
+ return {
200
+ ok: false,
201
+ findings: checked.ok ? [retired] : [retired, ...checked.findings]
202
+ };
203
+ }
204
+ //#endregion
205
+ //#region ../../0-framework/3-tooling/assemble/dist/index.mjs
206
+ /**
207
+ * A user-facing assembly failure, structured at origin (base-type rule 6):
208
+ * a `CliStructuredError` whose code names which way assembly went wrong.
209
+ * This package's second consumer is the programmatic deploy API, not just
210
+ * the CLI — hosts branch on `error.code`, the CLI renders the envelope.
211
+ */
212
+ var AssembleError = class extends CliStructuredError {
213
+ constructor(code, summary, options) {
214
+ super(code, summary, options);
215
+ }
216
+ };
217
+ /**
218
+ * The registry route for one service's build: extension by
219
+ * `build.extension`, node descriptor by `build.type`, kind must be "build".
220
+ * The CLI's coverage validation reports the same misses earlier with the
221
+ * config fix; these errors are the backstop for programmatic callers.
222
+ */
223
+ function buildDescriptorAssemble(config, node, address, cwd) {
224
+ const { extension, type } = node.build;
225
+ const extensionDescriptor = config.extensions.find((candidate) => candidate.id === extension);
226
+ if (extensionDescriptor === void 0) throw new AssembleError("ASSEMBLE.EXTENSION_MISSING", `No extension "${extension}" is configured (needed by service "${node.name}"'s build).`, { fix: "Add it to `extensions` in the `composer` section of prisma.config.ts." });
227
+ const nodeDescriptor = extensionDescriptor.nodes[type];
228
+ if (nodeDescriptor === void 0) throw new AssembleError("ASSEMBLE.DESCRIPTOR_MISSING", `Extension "${extension}" has no descriptor for build type "${type}".`, { why: `Known types: ${Object.keys(extensionDescriptor.nodes).join(", ")}.` });
229
+ if (nodeDescriptor.kind !== "build") throw new AssembleError("ASSEMBLE.DESCRIPTOR_KIND_MISMATCH", `Extension "${extension}"'s descriptor for type "${type}" is a "${nodeDescriptor.kind}" descriptor.`, { why: "Assembling a service build needs a \"build\" descriptor." });
230
+ return nodeDescriptor.assemble({
231
+ build: node.build,
232
+ address,
233
+ cwd
234
+ });
235
+ }
236
+ async function assembleServices(graph, config, cwd, run) {
237
+ const runAssembler = run ?? ((node, address, nodeCwd) => buildDescriptorAssemble(config, node, address, nodeCwd));
238
+ const serviceNodes = graph.nodes.filter((n) => n.node.kind === "service");
239
+ if (serviceNodes.length === 0) throw new AssembleError("ASSEMBLE.SERVICE_MISSING", "The loaded graph has no service to assemble.");
240
+ const bundles = {};
241
+ for (const { id, node } of serviceNodes) try {
242
+ bundles[id] = await runAssembler(node, id, cwd);
243
+ } catch (error) {
244
+ if (CliStructuredError.is(error)) throw error;
245
+ throw new AssembleError("ASSEMBLE.BUILD_FAILED", error instanceof Error ? error.message : String(error), {
246
+ meta: { address: id },
247
+ cause: error
248
+ });
249
+ }
250
+ return { bundles };
251
+ }
252
+ //#endregion
253
+ //#region ../../0-framework/3-tooling/cli/dist/pipeline-DYvl561J.mjs
13
254
  /**
14
255
  * Synchronous Node resolve hook so that entry modules may import relative
15
256
  * files using the specifier forms TypeScript accepts by default —
@@ -153,291 +394,10 @@ async function loadEntry(entryArg, cwd) {
153
394
  root: blindCast(root)
154
395
  };
155
396
  }
156
- //#endregion
157
- //#region ../../0-framework/3-tooling/assemble/dist/index.mjs
158
- /**
159
- * A user-facing assembly failure, structured at origin (base-type rule 6):
160
- * a `CliStructuredError` whose code names which way assembly went wrong.
161
- * This package's second consumer is the programmatic deploy API, not just
162
- * the CLI — hosts branch on `error.code`, the CLI renders the envelope.
163
- */
164
- var AssembleError = class extends CliStructuredError {
165
- constructor(code, summary, options) {
166
- super(code, summary, options);
167
- }
168
- };
169
- /**
170
- * The registry route for one service's build: extension by
171
- * `build.extension`, node descriptor by `build.type`, kind must be "build".
172
- * The CLI's coverage validation reports the same misses earlier with the
173
- * config fix; these errors are the backstop for programmatic callers.
174
- */
175
- function buildDescriptorAssemble(config, node, address, cwd) {
176
- const { extension, type } = node.build;
177
- const extensionDescriptor = config.extensions.find((candidate) => candidate.id === extension);
178
- if (extensionDescriptor === void 0) throw new AssembleError("ASSEMBLE.EXTENSION_MISSING", `No extension "${extension}" is configured (needed by service "${node.name}"'s build).`, { fix: "Add it to prisma-composer.config.ts's `extensions`." });
179
- const nodeDescriptor = extensionDescriptor.nodes[type];
180
- if (nodeDescriptor === void 0) throw new AssembleError("ASSEMBLE.DESCRIPTOR_MISSING", `Extension "${extension}" has no descriptor for build type "${type}".`, { why: `Known types: ${Object.keys(extensionDescriptor.nodes).join(", ")}.` });
181
- if (nodeDescriptor.kind !== "build") throw new AssembleError("ASSEMBLE.DESCRIPTOR_KIND_MISMATCH", `Extension "${extension}"'s descriptor for type "${type}" is a "${nodeDescriptor.kind}" descriptor.`, { why: "Assembling a service build needs a \"build\" descriptor." });
182
- return nodeDescriptor.assemble({
183
- build: node.build,
184
- address,
185
- cwd
186
- });
187
- }
188
- async function assembleServices(graph, config, cwd, run) {
189
- const runAssembler = run ?? ((node, address, nodeCwd) => buildDescriptorAssemble(config, node, address, nodeCwd));
190
- const serviceNodes = graph.nodes.filter((n) => n.node.kind === "service");
191
- if (serviceNodes.length === 0) throw new AssembleError("ASSEMBLE.SERVICE_MISSING", "The loaded graph has no service to assemble.");
192
- const bundles = {};
193
- for (const { id, node } of serviceNodes) try {
194
- bundles[id] = await runAssembler(node, id, cwd);
195
- } catch (error) {
196
- if (CliStructuredError.is(error)) throw error;
197
- throw new AssembleError("ASSEMBLE.BUILD_FAILED", error instanceof Error ? error.message : String(error), {
198
- meta: { address: id },
199
- cause: error
200
- });
201
- }
202
- return { bundles };
203
- }
204
- //#endregion
205
- //#region ../../0-framework/3-tooling/cli/dist/pipeline-C7gNjMzJ.mjs
206
- /**
207
- * Pipeline step: find and load `prisma-composer.config.ts` (ADR-0017) — the ONE
208
- * file that imports control-plane code. Discovery is the standard walk-up
209
- * from the deploy entry's directory (mirrors prisma/orm's config-loader);
210
- * loading is c12 with that explicit path (rc/global/package.json lookups
211
- * disabled), so the config file's own static imports resolve from the app
212
- * root by whatever package manager runs — no specifier construction, no
213
- * anchoring.
214
- *
215
- * Two shapes of the same machinery:
216
- *
217
- * loadAppConfigDiagnostics — returns the evaluated value together with
218
- * EVERY problem found, and never throws. This is the shape the engine
219
- * command family consumes: the engine renders diagnostics itself and
220
- * decides per command whether a broken section is fatal, so a loader that
221
- * threw on the first bad field would both hide the rest and bypass that
222
- * rendering.
223
- *
224
- * loadAppConfig — the same load, throwing its first diagnostic. The
225
- * clipanion CLI still runs on it; it goes when clipanion does.
226
- */
227
- /**
228
- * Accepted config file names, in precedence order: when several sit in one
229
- * directory the first one found wins. The JavaScript spellings exist so a
230
- * project whose own TypeScript build would otherwise compile the config file
231
- * (no `include`, a `rootDir`, `module: commonjs`) can keep it out of that
232
- * build's sight.
233
- */
234
- const CONFIG_FILENAMES = [
235
- "prisma-composer.config.ts",
236
- "prisma-composer.config.mts",
237
- "prisma-composer.config.mjs",
238
- "prisma-composer.config.js"
239
- ];
240
- /** The canonical spelling, used in messages and docs. */
241
- const CONFIG_FILENAME = CONFIG_FILENAMES[0];
242
- /** The accepted spellings as one glob-like string for messages. */
243
- const CONFIG_FILENAME_PATTERN = "prisma-composer.config.{ts,mts,mjs,js}";
244
- /** Walks UP from the entry file's directory looking for one of CONFIG_FILENAMES; undefined when the walk hits the filesystem root. */
245
- function findConfigPathForEntry(entryPath) {
246
- let current = path$1.dirname(path$1.resolve(entryPath));
247
- while (true) {
248
- for (const filename of CONFIG_FILENAMES) {
249
- const candidate = path$1.join(current, filename);
250
- if (fs$1.existsSync(candidate)) return candidate;
251
- }
252
- const parent = path$1.dirname(current);
253
- if (parent === current) return void 0;
254
- current = parent;
255
- }
256
- }
257
- function missingConfigError(entryPath) {
258
- const searchedFrom = path$1.dirname(path$1.resolve(entryPath));
259
- return new CliStructuredError("CONFIG.FILE_MISSING", `No ${CONFIG_FILENAME_PATTERN} found walking up from "${searchedFrom}".`, {
260
- why: "The deploy needs the app's config file.",
261
- fix: "Create one next to (or above) the entry, default-exporting defineConfig({ extensions: [...], state: ... }) from '@prisma/composer/config'.",
262
- where: { path: searchedFrom }
263
- });
264
- }
265
- function fieldError(configPath, field, requirement) {
266
- return new CliStructuredError("CONFIG.FIELD_INVALID", `${path$1.basename(configPath)}: \`${field}\` ${requirement}.`, {
267
- fix: "See defineConfig() in '@prisma/composer/config'.",
268
- meta: { field }
269
- });
270
- }
271
- function isRecord(value) {
272
- return typeof value === "object" && value !== null;
273
- }
274
- /**
275
- * Field-by-field validation of the loaded default export — deliberately no
276
- * schema library: each check is a structured error naming the offending field.
277
- *
278
- * Collects rather than throws, so one bad config reports every bad field
279
- * instead of only the first. The `extensions` and `state` checks are
280
- * independent, and within `extensions` each entry is judged on its own; a
281
- * field whose own shape check already failed is not probed further, because
282
- * anything derived from it would be noise rather than a second finding.
283
- */
284
- function configShapeDiagnostics(loaded, configPath) {
285
- if (!isRecord(loaded) || Object.keys(loaded).length === 0) return [new CliStructuredError("CONFIG.EXPORT_INVALID", `"${configPath}" exported no config.`, {
286
- fix: "It must default-export defineConfig({ extensions: [...], state: ... }) from '@prisma/composer/config'.",
287
- where: { path: configPath }
288
- })];
289
- const diagnostics = [];
290
- const extensions = loaded["extensions"];
291
- if (!Array.isArray(extensions)) diagnostics.push(fieldError(configPath, "extensions", "must be an array"));
292
- else {
293
- const seen = /* @__PURE__ */ new Set();
294
- for (const [index, entry] of extensions.entries()) {
295
- if (!isRecord(entry)) {
296
- diagnostics.push(fieldError(configPath, `extensions[${index}]`, "must be an extension descriptor object"));
297
- continue;
298
- }
299
- const id = entry["id"];
300
- if (typeof id !== "string" || id.length === 0) diagnostics.push(fieldError(configPath, `extensions[${index}].id`, "must be a non-empty string (the extension package name)"));
301
- else if (seen.has(id)) diagnostics.push(new CliStructuredError("CONFIG.EXTENSION_DUPLICATE", `${path$1.basename(configPath)}: extension "${id}" is listed more than once in \`extensions\`.`));
302
- else seen.add(id);
303
- if (!isRecord(entry["nodes"])) diagnostics.push(fieldError(configPath, `extensions[${index}].nodes`, "must be an object (the node-ID → control registry)"));
304
- }
305
- }
306
- const state = loaded["state"];
307
- if (!isRecord(state) || typeof state["extension"] !== "string" || typeof state["create"] !== "function") diagnostics.push(fieldError(configPath, "state", "must be a state descriptor (e.g. prismaState())"));
308
- return diagnostics;
309
- }
310
- /** The throwing form of configShapeDiagnostics: raises the first finding, returns the same object typed. Used by the clipanion pipeline. */
311
- function validateConfigShape(loaded, configPath) {
312
- const [first] = configShapeDiagnostics(loaded, configPath);
313
- if (first !== void 0) throw first;
314
- return blindCast(loaded);
315
- }
316
- /**
317
- * Evaluates `configPath` with c12 (explicit file; rc / global-rc /
318
- * package.json lookups disabled). `verifySamePath` runs the same-file check
319
- * against the discovered path — see loadAppConfigDiagnostics for when it does
320
- * not apply.
321
- */
322
- async function evaluateConfig(configPath, verifySamePath) {
323
- let result;
324
- try {
325
- result = await c12.loadConfig({
326
- name: "prisma-composer",
327
- configFile: configPath,
328
- cwd: path$1.dirname(configPath),
329
- rcFile: false,
330
- globalRc: false,
331
- packageJson: false
332
- });
333
- } catch (error) {
334
- return {
335
- value: void 0,
336
- diagnostics: [new CliStructuredError("CONFIG.EVALUATION_FAILED", `Evaluating "${configPath}" failed: ${error instanceof Error ? error.message : String(error)}`, {
337
- where: { path: configPath },
338
- cause: error
339
- })]
340
- };
341
- }
342
- const loadedFile = result.configFile;
343
- if (verifySamePath && (typeof loadedFile !== "string" || fs$1.realpathSync(loadedFile) !== fs$1.realpathSync(configPath))) return {
344
- value: result.config,
345
- diagnostics: [new CliStructuredError("CONFIG.PATH_MISMATCH", `Config loading resolved "${String(loadedFile)}" instead of the discovered "${configPath}".`, {
346
- why: "Refusing to deploy against a different file.",
347
- where: { path: configPath }
348
- })]
349
- };
350
- return {
351
- value: result.config,
352
- diagnostics: configShapeDiagnostics(result.config, configPath)
353
- };
354
- }
355
- /**
356
- * The front of every config load, shared by the throwing and the
357
- * diagnostics-list shapes: verify the installed tree can evaluate a config at
358
- * all, then settle which file to load — the section's path, or the
359
- * entry-anchored walk.
360
- *
361
- * The effect-resolution check comes first because evaluating a config imports
362
- * alchemy's provider tree: in a tree where alchemy resolves an `effect` we did
363
- * not pin, nothing downstream can load, so the dependency conflict is the
364
- * finding with the fix in it and every later failure is its symptom.
365
- */
366
- function configSource(request) {
367
- const resolvedEntry = path$1.resolve(request.entryPath);
368
- const effectConflict = effectResolutionDiagnostic(request.cwd ?? path$1.dirname(resolvedEntry));
369
- if (effectConflict !== void 0) return {
370
- ok: false,
371
- path: void 0,
372
- diagnostic: effectConflict
373
- };
374
- if (request.configPath === void 0) {
375
- const discovered = findConfigPathForEntry(resolvedEntry);
376
- return discovered === void 0 ? {
377
- ok: false,
378
- path: void 0,
379
- diagnostic: missingConfigError(resolvedEntry)
380
- } : {
381
- ok: true,
382
- path: discovered,
383
- explicit: false
384
- };
385
- }
386
- const configPath = request.configPath;
387
- if (!path$1.isAbsolute(configPath)) return {
388
- ok: false,
389
- path: configPath,
390
- diagnostic: new CliStructuredError("CONFIG.PATH_NOT_ABSOLUTE", `The \`composer\` config section's configPath must arrive absolute, and "${configPath}" is relative.`, {
391
- why: "The section validator resolves a relative configPath against the config file that declared it; a relative value here means that resolution was skipped.",
392
- fix: "Run the configPath through the composer section validator, or resolve it against its declaring file before constructing the load request.",
393
- where: { path: configPath }
394
- })
395
- };
396
- if (!fs$1.existsSync(configPath)) return {
397
- ok: false,
398
- path: configPath,
399
- diagnostic: new CliStructuredError("CONFIG.FILE_MISSING", `The \`composer\` config section points at "${configPath}", which does not exist.`, {
400
- why: "An explicit configPath is used as given — there is no walk to fall back on.",
401
- fix: "Correct `configPath` in the `composer` section of prisma.config.ts, or remove it to search upward from the entry.",
402
- where: { path: configPath }
403
- })
404
- };
405
- return {
406
- ok: true,
407
- path: configPath,
408
- explicit: true
409
- };
410
- }
411
- /**
412
- * The throwing form of that front, for the pipeline: the file the load will
413
- * use, or the finding raised. `explicit` says the section named the file,
414
- * which is what retires the same-file check for that case.
415
- */
416
- function resolveConfigFile(request) {
417
- const source = configSource(request);
418
- if (!source.ok) throw source.diagnostic;
419
- return {
420
- path: source.path,
421
- explicit: source.explicit
422
- };
423
- }
424
- /**
425
- * Loads + validates the config at `configPath`, throwing its first diagnostic.
426
- * The clipanion CLI's pipeline runs on this; it goes when clipanion does.
427
- */
428
- async function loadAppConfig(configPath, verifySamePath = true) {
429
- const { value, diagnostics } = await evaluateConfig(configPath, verifySamePath);
430
- const [first] = diagnostics;
431
- if (first !== void 0) throw first;
432
- return {
433
- path: configPath,
434
- config: validateConfigShape(value, configPath)
435
- };
436
- }
437
397
  function lookup(extensions, extension, type, expectedKind, what) {
438
398
  const ext = extensions.get(extension);
439
399
  if (ext === void 0) throw new CliStructuredError("CONFIG.EXTENSION_MISSING", `No extension "${extension}" is configured (needed by ${what}).`, {
440
- fix: `Add it to ${CONFIG_FILENAME}'s \`extensions\` (import its /control entry and list its descriptor).`,
400
+ fix: "Add it to `extensions` in the `composer` section of prisma.config.ts (import its /control entry and list its descriptor).",
441
401
  meta: {
442
402
  extension,
443
403
  type
@@ -472,7 +432,7 @@ function validateRegistryCoverage(graph, config) {
472
432
  }
473
433
  /**
474
434
  * The shared prefix of `deploy`/`destroy`/`dev` (deploy-cli.md § The
475
- * pipeline; local-dev spec § 6): config discovery/load, entry load, Load,
435
+ * pipeline; local-dev spec § 6): entry load, Load,
476
436
  * registry coverage validation, name resolution, assemble. Deploy and dev
477
437
  * diverge after this — deploy resolves containers/preflight/stack file
478
438
  * against the hosted providers, dev against the local ones — so everything
@@ -480,50 +440,58 @@ function validateRegistryCoverage(graph, config) {
480
440
  * `run()` (main.ts) and `runDev()` (dev/run-dev.ts), so the two pipelines
481
441
  * cannot drift.
482
442
  */
443
+ function configError(findings, file) {
444
+ const [first] = findings;
445
+ return new CliStructuredError(first.code, first.summary, {
446
+ ...first.why === void 0 ? {} : { why: first.why },
447
+ fix: first.fix,
448
+ where: { path: first.where ?? file },
449
+ meta: {
450
+ ...first.field === void 0 ? {} : { field: first.field },
451
+ findings
452
+ }
453
+ });
454
+ }
483
455
  /**
484
- * The config step both pipelines share: the effect-resolution check and the
485
- * choice of config file (load-config.ts's shared front), then evaluation —
486
- * unless the caller substituted a config, which replaces the evaluation and
487
- * nothing before it.
456
+ * Refuses a caller's config with the same findings, in the same order, as the CLI's section validator.
457
+ * The error is the first finding and carries all of them in `meta.findings`. Returns the config with `file` resolved against `cwd`.
488
458
  */
489
- async function loadConfigStep(entryPath, cwd, deps) {
490
- const { path: configPath, explicit } = resolveConfigFile({
491
- entryPath,
492
- configPath: deps.configPath,
493
- cwd
494
- });
459
+ function checkConfigSource(source, cwd) {
460
+ const file = path$1.resolve(cwd, source.file);
461
+ if (!fs$1.existsSync(file)) throw configError([configFileMissing(file)], file);
462
+ const checked = checkComposerConfig(source.value, file);
463
+ if (!checked.ok) throw configError(checked.findings, file);
495
464
  return {
496
- configPath,
497
- config: deps.config ?? (await loadAppConfig(configPath, !explicit)).config
465
+ value: checked.value,
466
+ file
498
467
  };
499
468
  }
500
469
  /**
501
- * The pipeline's front — config discovery/load, entry load, name resolution —
470
+ * The pipeline's front — entry load and name resolution —
502
471
  * WITHOUT Load, coverage, or assemble. `log` needs only who the app is (its
503
472
  * config and resolved name) to reach the already-running local instance; it
504
473
  * neither builds nor provisions, so it must not require the user's built
505
474
  * output the way the full pipeline does.
506
475
  */
507
- async function resolveAppIdentity(entry, overrideName, cwd, deps = {}) {
508
- const { configPath, config } = await loadConfigStep(path$1.resolve(cwd, entry), cwd, deps);
476
+ async function resolveAppIdentity(entry, overrideName, cwd, config) {
477
+ const { value } = checkConfigSource(config, cwd);
509
478
  const entryModule = await loadEntry(entry, cwd);
510
479
  const name = overrideName ?? entryModule.root.name;
511
480
  if (name.length === 0) throw new CliStructuredError("COMPOSE.NAME_MISSING", "The root node has no name.", { fix: "Name it at authoring, or pass --name." });
512
481
  return {
513
- configPath,
514
- config,
482
+ config: value,
515
483
  name
516
484
  };
517
485
  }
518
486
  /**
519
- * Runs config discovery/load, entry load, Load, registry coverage, name
520
- * resolution, and assemble — steps 1–6 of `run()`. `onAssembleError`, when
487
+ * Runs entry load, Load, registry coverage, name resolution, and assemble — steps 1–5 of `run()`. `onAssembleError`, when
521
488
  * given, lets a caller decorate an assemble failure with command-specific
522
489
  * guidance (destroy's "build first" hint) without this shared step knowing
523
490
  * about any one command.
524
491
  */
525
- async function runPipeline(entry, overrideName, cwd, deps = {}, onAssembleError) {
526
- const { configPath, config } = await loadConfigStep(path$1.resolve(cwd, entry), cwd, deps);
492
+ async function runPipeline(entry, overrideName, cwd, source, deps = {}, onAssembleError) {
493
+ const configSource = checkConfigSource(source, cwd);
494
+ const config = configSource.value;
527
495
  const entryModule = await loadEntry(entry, cwd);
528
496
  const graph = Load(entryModule.root);
529
497
  if (graph.root.node.kind !== "module") throw new CliStructuredError("COMPOSE.ROOT_NOT_MODULE", "The deploy root must be a module.", { fix: "Wrap your service, e.g. export default module('name', ({ provision }) => { provision(service); })." });
@@ -538,8 +506,7 @@ async function runPipeline(entry, overrideName, cwd, deps = {}, onAssembleError)
538
506
  throw error;
539
507
  }
540
508
  return {
541
- configPath,
542
- config,
509
+ configSource,
543
510
  entryModule,
544
511
  graph,
545
512
  name,
@@ -547,6 +514,6 @@ async function runPipeline(entry, overrideName, cwd, deps = {}, onAssembleError)
547
514
  };
548
515
  }
549
516
  //#endregion
550
- export { runPipeline as n, resolveAppIdentity as t };
517
+ export { runPipeline as n, noComposerExportMessage as r, resolveAppIdentity as t };
551
518
 
552
- //# sourceMappingURL=pipeline-C7gNjMzJ-CzM8DNS_.mjs.map
519
+ //# sourceMappingURL=pipeline-DYvl561J-DDtnkFPh.mjs.map