@prisma/composer 0.6.0-dev.17 → 0.6.0-dev.19
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/{app-config-aIrriqVU-D6lJHzh2.d.mts → app-config-aIrriqVU-Dpj1RqnY.d.mts} +3 -2
- package/dist/app-config-aIrriqVU-Dpj1RqnY.d.mts.map +1 -0
- package/dist/config-BeBRB-6m.d.mts +1 -0
- package/dist/config.d.mts +2 -2
- package/dist/control.d.mts +219 -3
- package/dist/control.d.mts.map +1 -0
- package/dist/control.mjs +69 -1
- package/dist/control.mjs.map +1 -0
- package/dist/deploy-BeBRB-6m.d.mts +1 -0
- package/dist/deploy.d.mts +2 -2
- package/dist/deploy.mjs +1 -1
- package/dist/{dist-C0dNEgS-.mjs → dist-C5rZnr4n.mjs} +2 -2
- package/dist/{dist-C0dNEgS-.mjs.map → dist-C5rZnr4n.mjs.map} +1 -1
- package/dist/{errors-DkXcnjPx.mjs → errors-0e8IVwzi.mjs} +2 -3
- package/dist/{errors-DkXcnjPx.mjs.map → errors-0e8IVwzi.mjs.map} +1 -1
- package/dist/{execute-deploy-destroy-DfVJUICu-AMM8CwgV.mjs → execute-deploy-destroy-DfVJUICu-Dbgn1GR_.mjs} +5 -6
- package/dist/{execute-deploy-destroy-DfVJUICu-AMM8CwgV.mjs.map → execute-deploy-destroy-DfVJUICu-Dbgn1GR_.mjs.map} +1 -1
- package/dist/{execute-dev-BMTFWfFc-BoKbcFVF.mjs → execute-dev-BMTFWfFc-CGDFqR4w.mjs} +6 -7
- package/dist/{execute-dev-BMTFWfFc-BoKbcFVF.mjs.map → execute-dev-BMTFWfFc-CGDFqR4w.mjs.map} +1 -1
- package/dist/{execute-log-Cay9hlKW-B0tmRtrY.mjs → execute-log-Cay9hlKW-ChlIv192.mjs} +5 -6
- package/dist/{execute-log-Cay9hlKW-DrMeVOXq.mjs.map → execute-log-Cay9hlKW-ChlIv192.mjs.map} +1 -1
- package/dist/{graph-BJgUfGB0-7fmubS_y.mjs → graph-BJgUfGB0-kvgFzT4q.mjs} +2 -2
- package/dist/{graph-BJgUfGB0-7fmubS_y.mjs.map → graph-BJgUfGB0-kvgFzT4q.mjs.map} +1 -1
- package/dist/graph-DqklX5Sf-BNS2aO1j.d.mts +18 -0
- package/dist/graph-DqklX5Sf-BNS2aO1j.d.mts.map +1 -0
- package/dist/{graph-DqklX5Sf-CdNH3lA7.d.mts → graph-types-N6brq1zY-1VMNwtkJ.d.mts} +2 -17
- package/dist/graph-types-N6brq1zY-1VMNwtkJ.d.mts.map +1 -0
- package/dist/{index-DuF5iI3i.d.mts → index-DryPyF9U.d.mts} +3 -2
- package/dist/index-DryPyF9U.d.mts.map +1 -0
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +2 -2
- package/dist/{local-target-D3u_wuNF.mjs → local-target-DnA2E8IN.mjs} +2 -2
- package/dist/{local-target-D3u_wuNF.mjs.map → local-target-DnA2E8IN.mjs.map} +1 -1
- package/dist/local-target.d.mts +1 -1
- package/dist/local-target.mjs +1 -1
- package/dist/{nextjs-DLyeRR7M-BfUbLXwr.d.mts → nextjs-DLyeRR7M-DZODs9tk.d.mts} +3 -3
- package/dist/{nextjs-DLyeRR7M-BfUbLXwr.d.mts.map → nextjs-DLyeRR7M-DZODs9tk.d.mts.map} +1 -1
- package/dist/nextjs-control.d.mts +4 -4
- package/dist/nextjs.d.mts +1 -1
- package/dist/node-control.d.mts +3 -3
- package/dist/node.d.mts +2 -2
- package/dist/{pipeline-AoW8zq4I-DM74ygtH.mjs → pipeline-AoW8zq4I-CUon5KZH.mjs} +5 -5
- package/dist/{pipeline-AoW8zq4I-DM74ygtH.mjs.map → pipeline-AoW8zq4I-CUon5KZH.mjs.map} +1 -1
- package/dist/report.d.mts +2 -2
- package/dist/{log-DfT2hHqi-CDp9nfI5.mjs → result-CY_TB7bn.mjs} +84 -63
- package/dist/result-CY_TB7bn.mjs.map +1 -0
- package/dist/{run-alchemy-D44OZlyB-DA7s331N.mjs → run-alchemy-D44OZlyB-gVq_0gf4.mjs} +3 -3
- package/dist/{run-alchemy-D44OZlyB-DA7s331N.mjs.map → run-alchemy-D44OZlyB-gVq_0gf4.mjs.map} +1 -1
- package/dist/service-rpc.d.mts +2 -2
- package/dist/service-rpc.mjs +2 -2
- package/dist/testing.d.mts +2 -54
- package/dist/testing.d.mts.map +1 -1
- package/dist/testing.mjs +1 -150
- package/dist/testing.mjs.map +1 -1
- package/package.json +11 -16
- package/src/exports/testing.ts +5 -1
- package/dist/app-config-aIrriqVU-D6lJHzh2.d.mts.map +0 -1
- package/dist/bin.mjs +0 -1344
- package/dist/bin.mjs.map +0 -1
- package/dist/config-CL7Jbve2.d.mts +0 -1
- package/dist/container-transport-DKmKg5JQ-CoY4XPma.mjs +0 -30
- package/dist/container-transport-DKmKg5JQ-CoY4XPma.mjs.map +0 -1
- package/dist/deploy-CL7Jbve2.d.mts +0 -1
- package/dist/execute-deploy-destroy-DfVJUICu-quUQ08T8.mjs +0 -316
- package/dist/execute-deploy-destroy-DfVJUICu-quUQ08T8.mjs.map +0 -1
- package/dist/execute-dev-BMTFWfFc-B1whS-Rr.mjs +0 -2011
- package/dist/execute-dev-BMTFWfFc-B1whS-Rr.mjs.map +0 -1
- package/dist/execute-log-Cay9hlKW-B0tmRtrY.mjs.map +0 -1
- package/dist/execute-log-Cay9hlKW-DrMeVOXq.mjs +0 -157
- package/dist/family-DkH0si4D-Uov_MuHV.d.mts +0 -27
- package/dist/family-DkH0si4D-Uov_MuHV.d.mts.map +0 -1
- package/dist/family.d.mts +0 -53
- package/dist/family.d.mts.map +0 -1
- package/dist/family.mjs +0 -1226
- package/dist/family.mjs.map +0 -1
- package/dist/graph-DqklX5Sf-CdNH3lA7.d.mts.map +0 -1
- package/dist/index-DuF5iI3i.d.mts.map +0 -1
- package/dist/local-target-H0IWu_FM.mjs +0 -68
- package/dist/local-target-H0IWu_FM.mjs.map +0 -1
- package/dist/log-DRnnWupy-9jbzL64M.d.mts +0 -324
- package/dist/log-DRnnWupy-9jbzL64M.d.mts.map +0 -1
- package/dist/log-DfT2hHqi-CDp9nfI5.mjs.map +0 -1
- package/dist/pipeline-AoW8zq4I-C6Qe0VaZ.mjs +0 -976
- package/dist/pipeline-AoW8zq4I-C6Qe0VaZ.mjs.map +0 -1
- package/dist/result-B4XjxVK6.mjs +0 -384
- package/dist/result-B4XjxVK6.mjs.map +0 -1
- package/dist/result-wonf1deB.mjs +0 -92
- package/dist/result-wonf1deB.mjs.map +0 -1
- package/dist/run-alchemy-D44OZlyB-DeDTFsB6.mjs +0 -93
- package/dist/run-alchemy-D44OZlyB-DeDTFsB6.mjs.map +0 -1
- package/src/exports/family.ts +0 -14
|
@@ -1,976 +0,0 @@
|
|
|
1
|
-
import { c as CliStructuredError, d as blindCast, i as effectResolutionDiagnostic, u as InternalError } from "./result-B4XjxVK6.mjs";
|
|
2
|
-
import * as fs from "node:fs";
|
|
3
|
-
import * as path from "node:path";
|
|
4
|
-
import { pathToFileURL } from "node:url";
|
|
5
|
-
import * as c12 from "c12";
|
|
6
|
-
//#region ../../0-framework/1-core/core/dist/graph-BJgUfGB0.mjs
|
|
7
|
-
/**
|
|
8
|
-
* Thrown by Load when the graph is malformed. Structured at origin
|
|
9
|
-
* (base-type rule 6): one type-level code covers every raise site until a
|
|
10
|
-
* finer per-site taxonomy is carved out. The `name` stays
|
|
11
|
-
* `CliStructuredError` — structural recognition depends on it.
|
|
12
|
-
*/
|
|
13
|
-
var LoadError = class extends CliStructuredError {
|
|
14
|
-
constructor(message) {
|
|
15
|
-
super("COMPOSE.GRAPH_INVALID", message);
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
/**
|
|
19
|
-
* Core model: node types and the factories that construct them, plain frozen
|
|
20
|
-
* data objects. A node's `extension` + `type` form its deploy-time registry key (ADR-0017).
|
|
21
|
-
*/
|
|
22
|
-
const NODE = Symbol.for("prisma:node");
|
|
23
|
-
blindCast(Symbol.for("prisma:secret-need"));
|
|
24
|
-
const SECRET_SOURCE = blindCast(Symbol.for("prisma:secret-source"));
|
|
25
|
-
/** True if `value` is a secret source (an `envSecret` result or a forwarded ctx.secrets ref). */
|
|
26
|
-
function isSecretSource(value) {
|
|
27
|
-
return typeof value === "object" && value !== null && blindCast(value)[SECRET_SOURCE] === true;
|
|
28
|
-
}
|
|
29
|
-
blindCast(Symbol.for("prisma:provision-need"));
|
|
30
|
-
const PARAM_SOURCE = blindCast(Symbol.for("prisma:param-source"));
|
|
31
|
-
blindCast(Symbol.for("prisma:param-need"));
|
|
32
|
-
/** True if `value` is a param source (an `envParam` result or a forwarded ctx.params ref). */
|
|
33
|
-
function isParamSource(value) {
|
|
34
|
-
return typeof value === "object" && value !== null && blindCast(value)[PARAM_SOURCE] === true;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Core's grammar for every name that becomes a config-key segment —
|
|
38
|
-
* addresses, input/param/secret names: ASCII letters and digits only.
|
|
39
|
-
* Conservative by design so any target medium — POSIX env-var keys
|
|
40
|
-
* included — can uppercase and "_"-join segments without escaping.
|
|
41
|
-
*/
|
|
42
|
-
function isConfigKeySegment(name) {
|
|
43
|
-
return /^[A-Za-z0-9]+$/.test(name);
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* True if `value` was constructed by this module's factories. Checks the
|
|
47
|
-
* brand only, never a prototype — a graph may mix nodes from a different
|
|
48
|
-
* installed copy of core (dual-package hazard).
|
|
49
|
-
*/
|
|
50
|
-
function isNode(value) {
|
|
51
|
-
return typeof value === "object" && value !== null && value[NODE] === true;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Stable topological sort: every edge's `from` precedes its `to` in the
|
|
55
|
-
* result. Ties (nodes with no ordering constraint between them) keep their
|
|
56
|
-
* relative order from `nodes` — so a graph already authored producer-first
|
|
57
|
-
* comes out byte-identical to its pre-sort layout; only a graph that
|
|
58
|
-
* genuinely needs reordering (e.g. a module wired via a forged ref pointing at a
|
|
59
|
-
* not-yet-provisioned producer) actually moves. A Kahn's-algorithm variant
|
|
60
|
-
* that always picks the ready node with the smallest original index. Edges
|
|
61
|
-
* whose endpoint falls outside `nodes` (e.g. a service-root's input edges
|
|
62
|
-
* targeting the root, which is appended separately) are ignored. Cycles
|
|
63
|
-
* cannot reach here: `assertDependencyDag` already rejects them for
|
|
64
|
-
* dependency edges, and input edges never cycle.
|
|
65
|
-
*/
|
|
66
|
-
function topoSort(nodes, edges) {
|
|
67
|
-
const byId = new Map(nodes.map((n) => [n.id, n]));
|
|
68
|
-
const indexOf = new Map(nodes.map((n, i) => [n.id, i]));
|
|
69
|
-
const indegree = new Map(nodes.map((n) => [n.id, 0]));
|
|
70
|
-
const successors = /* @__PURE__ */ new Map();
|
|
71
|
-
for (const edge of edges) {
|
|
72
|
-
if (!byId.has(edge.from) || !byId.has(edge.to)) continue;
|
|
73
|
-
const targets = successors.get(edge.from) ?? [];
|
|
74
|
-
targets.push(edge.to);
|
|
75
|
-
successors.set(edge.from, targets);
|
|
76
|
-
indegree.set(edge.to, (indegree.get(edge.to) ?? 0) + 1);
|
|
77
|
-
}
|
|
78
|
-
const ready = new Set(nodes.filter((n) => indegree.get(n.id) === 0).map((n) => n.id));
|
|
79
|
-
const order = [];
|
|
80
|
-
while (ready.size > 0) {
|
|
81
|
-
let next;
|
|
82
|
-
let bestIndex = Number.POSITIVE_INFINITY;
|
|
83
|
-
for (const id of ready) {
|
|
84
|
-
const index = indexOf.get(id) ?? Number.POSITIVE_INFINITY;
|
|
85
|
-
if (index < bestIndex) {
|
|
86
|
-
bestIndex = index;
|
|
87
|
-
next = id;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
if (next === void 0) break;
|
|
91
|
-
ready.delete(next);
|
|
92
|
-
order.push(next);
|
|
93
|
-
for (const target of successors.get(next) ?? []) {
|
|
94
|
-
const remaining = (indegree.get(target) ?? 0) - 1;
|
|
95
|
-
indegree.set(target, remaining);
|
|
96
|
-
if (remaining === 0) ready.add(target);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
if (order.length !== nodes.length) throw new InternalError(`topological sort processed ${order.length} of ${nodes.length} nodes — the graph contains a cycle that slipped past the DAG validation.`);
|
|
100
|
-
return order.map((id) => byId.get(id)).filter((n) => n !== void 0);
|
|
101
|
-
}
|
|
102
|
-
function serviceInputs(service, serviceId) {
|
|
103
|
-
if (typeof service.inputs !== "object" || service.inputs === null) throw new LoadError(`Service "${serviceId}" has no inputs map.`);
|
|
104
|
-
const nodes = [];
|
|
105
|
-
const edges = [];
|
|
106
|
-
for (const [input, value] of Object.entries(service.inputs)) {
|
|
107
|
-
const kind = isNode(value) ? value.kind : void 0;
|
|
108
|
-
if (kind === "resource") throw new LoadError(`Input "${input}" of "${serviceId}" is a resource node — a resource is provisioned by the composing module, never created for a service that mentions it. Declare the input as a dependency (the pack's dependency factory) and wire the module-provisioned resource's ref into it.`);
|
|
109
|
-
if (kind !== "dependency") throw new LoadError(`Input "${input}" of "${serviceId}" is not a branded dependency end (construct it with the dependency() factory).`);
|
|
110
|
-
if (value.type.length === 0) throw new LoadError(`Input "${input}" of "${serviceId}" has an empty node type.`);
|
|
111
|
-
const id = `${serviceId}.${input}`;
|
|
112
|
-
nodes.push({
|
|
113
|
-
id,
|
|
114
|
-
node: value
|
|
115
|
-
});
|
|
116
|
-
edges.push({
|
|
117
|
-
from: id,
|
|
118
|
-
to: serviceId,
|
|
119
|
-
input,
|
|
120
|
-
kind: "input"
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
return {
|
|
124
|
-
nodes,
|
|
125
|
-
edges
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
function loadService(root, rootId) {
|
|
129
|
-
for (const [input, value] of Object.entries(root.inputs)) if (isNode(value) && value.kind === "dependency") throw new LoadError(`Service "${rootId}" has an unwired dependency input "${input}" — this service is composed by a module; deploy the module instead of loading "${rootId}" directly.`);
|
|
130
|
-
if (root.inputSchema !== void 0) throw new LoadError(`Service "${rootId}" declares an input schema but is being loaded directly — a lone service has no enclosing scope to bind its input. Compose it inside a module whose provision() call binds \`input: { … }\` (ADR-0042).`);
|
|
131
|
-
const rootGraphNode = {
|
|
132
|
-
id: rootId,
|
|
133
|
-
node: root
|
|
134
|
-
};
|
|
135
|
-
const { nodes, edges } = serviceInputs(root, rootId);
|
|
136
|
-
return {
|
|
137
|
-
root: rootGraphNode,
|
|
138
|
-
nodes: [...topoSort(nodes, edges), rootGraphNode],
|
|
139
|
-
edges,
|
|
140
|
-
inputBindings: [],
|
|
141
|
-
params: []
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Builds the ref a provision() call hands back: the id (so a producer with no
|
|
146
|
-
* exposed ports — or an untyped slot — can still be wired wholesale) plus one
|
|
147
|
-
* ref-port per exposed contract, each the contract's own runtime value (so
|
|
148
|
-
* its `satisfies()` still works) tagged with the provider's id.
|
|
149
|
-
*/
|
|
150
|
-
function refFor(id, service) {
|
|
151
|
-
const ports = {};
|
|
152
|
-
for (const [port, contract] of Object.entries(service.expose ?? {})) ports[port] = {
|
|
153
|
-
...contract,
|
|
154
|
-
__providerId: id
|
|
155
|
-
};
|
|
156
|
-
return blindCast({
|
|
157
|
-
id,
|
|
158
|
-
...ports
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* The resource variant of refFor: a resource has exactly one port — the
|
|
163
|
-
* contract it provides — flattened onto the ref itself, tagged with the
|
|
164
|
-
* provider id. `id` is written last so a hostile contract value cannot
|
|
165
|
-
* clobber it.
|
|
166
|
-
*/
|
|
167
|
-
function refForResource(id, resource) {
|
|
168
|
-
return blindCast({
|
|
169
|
-
...resource.provides,
|
|
170
|
-
__providerId: id,
|
|
171
|
-
id
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
/** A wired value's producer id: a ref-port's `__providerId`, or a bare ref's `id`. */
|
|
175
|
-
function producerIdOf(ref) {
|
|
176
|
-
if (typeof ref !== "object" || ref === null) return void 0;
|
|
177
|
-
if ("__providerId" in ref && typeof ref.__providerId === "string") return ref.__providerId;
|
|
178
|
-
if ("id" in ref && typeof ref.id === "string") return ref.id;
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* Brands each `ctx.inputs` entry with the input key it stands for (see
|
|
182
|
-
* flatten): diagnostic only — usage attribution relies on the per-key object
|
|
183
|
-
* identity the branding copy creates, never on reading this back.
|
|
184
|
-
*/
|
|
185
|
-
const MODULE_INPUT_KEY = Symbol("prisma:module-input-key");
|
|
186
|
-
/** Same per-key identity trick as MODULE_INPUT_KEY, for the parallel `ctx.secrets` forwarding channel. */
|
|
187
|
-
const MODULE_SECRET_KEY = Symbol("prisma:module-secret-key");
|
|
188
|
-
/** Same per-key identity trick as MODULE_INPUT_KEY, for the parallel `ctx.params` forwarding channel. */
|
|
189
|
-
const MODULE_PARAM_KEY = Symbol("prisma:module-param-key");
|
|
190
|
-
/** Whether `ref` carries a callable `satisfies` that accepts `required` truthily. */
|
|
191
|
-
function satisfiesRequired(ref, required) {
|
|
192
|
-
return typeof ref === "object" && ref !== null && "satisfies" in ref && typeof ref.satisfies === "function" && ref.satisfies(required);
|
|
193
|
-
}
|
|
194
|
-
/**
|
|
195
|
-
* Checks one recorded `wiring` object against the Deps it was wired against:
|
|
196
|
-
* every named input exists and is a dependency slot, every referenced
|
|
197
|
-
* producer is a real (by-now-provisioned) address, and a wired ref whose
|
|
198
|
-
* slot declares a required contract must satisfy() it — no producer-kind
|
|
199
|
-
* branching, the contract alone determines validity, whether the producer is
|
|
200
|
-
* a service port or a resource. Shared by both provisioned kinds so a
|
|
201
|
-
* module-as-child gets exactly the checks a service gets, and run once per
|
|
202
|
-
* entry against the one `byId` shared by the whole recursive flatten, so a
|
|
203
|
-
* forwarded ref resolves through to its real producer address regardless of
|
|
204
|
-
* which ancestor scope provisioned it.
|
|
205
|
-
*/
|
|
206
|
-
function validateWiring(pending, byId) {
|
|
207
|
-
const { deps, wiring, targetId, targetKind, enclosingModuleName } = pending;
|
|
208
|
-
for (const [input, ref] of Object.entries(wiring)) {
|
|
209
|
-
const declared = blindCast(deps[input]);
|
|
210
|
-
if (declared === void 0 || !isNode(declared) || declared.kind !== "dependency") throw new LoadError(`The deps for "${targetId}" name "${input}", which is not a dependency slot of that ${targetKind}.`);
|
|
211
|
-
const producerId = producerIdOf(ref);
|
|
212
|
-
const producer = producerId !== void 0 ? byId.get(producerId) : void 0;
|
|
213
|
-
if (producerId === void 0 || producer === void 0) throw new LoadError(`The deps for "${targetId}.${input}" reference "${String(producerId)}", which is not provisioned in module "${enclosingModuleName}".`);
|
|
214
|
-
const required = declared.required;
|
|
215
|
-
if (required !== void 0 && !satisfiesRequired(ref, required)) throw new LoadError(`The deps for "${targetId}.${input}" do not satisfy the slot's required contract.`);
|
|
216
|
-
}
|
|
217
|
-
for (const [input, rawValue] of Object.entries(deps)) {
|
|
218
|
-
const value = blindCast(rawValue);
|
|
219
|
-
if (!isNode(value) || wiring[input] !== void 0) continue;
|
|
220
|
-
if (value.kind === "dependency") throw new LoadError(`Dependency input "${input}" of provisioned ${targetKind} "${targetId}" is not wired to a producer (module "${enclosingModuleName}").`);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Flattens a value into itself plus every nested object/array member value —
|
|
225
|
-
* identity fodder for the ctx.secrets/ctx.params usage marking, which must
|
|
226
|
-
* see a forwarded source wherever it sits inside an input binding (ADR-0042).
|
|
227
|
-
* A secret/param source is an opaque leaf: it is pushed (a forwarded ref IS
|
|
228
|
-
* such a source, so identity-matching still needs it) but never descended
|
|
229
|
-
* into, so a ref buried in an unrelated source's payload is not falsely seen
|
|
230
|
-
* as used. `seen` guards object cycles so a self-referential binding cannot
|
|
231
|
-
* recurse forever.
|
|
232
|
-
*/
|
|
233
|
-
function deepValues(value, out, seen = /* @__PURE__ */ new WeakSet()) {
|
|
234
|
-
out.push(value);
|
|
235
|
-
if (typeof value !== "object" || value === null) return;
|
|
236
|
-
if (isSecretSource(value) || isParamSource(value)) return;
|
|
237
|
-
if (seen.has(value)) return;
|
|
238
|
-
seen.add(value);
|
|
239
|
-
const members = Array.isArray(value) ? value : Object.values(value);
|
|
240
|
-
for (const member of members) deepValues(member, out, seen);
|
|
241
|
-
}
|
|
242
|
-
/** The (unvalidated) edges a `wiring` object implies — one dependency edge per entry; `validateWiring` does the real checking. */
|
|
243
|
-
function wiringEdges(wiring, targetId) {
|
|
244
|
-
return Object.entries(wiring).map(([input, ref]) => ({
|
|
245
|
-
from: producerIdOf(ref) ?? "",
|
|
246
|
-
to: targetId,
|
|
247
|
-
input,
|
|
248
|
-
kind: "dependency"
|
|
249
|
-
}));
|
|
250
|
-
}
|
|
251
|
-
/**
|
|
252
|
-
* Checks the secrets wired into one provisioned child MODULE: every declared
|
|
253
|
-
* forwarding slot is bound to a real secret source (an `envSecret` or a
|
|
254
|
-
* forwarded ctx.secrets ref), and no wired key names a slot the module
|
|
255
|
-
* doesn't declare — the secret analog of `validateWiring`'s per-input checks,
|
|
256
|
-
* but resolved inline (a source carries its own name, so no whole-graph pass
|
|
257
|
-
* is needed).
|
|
258
|
-
*/
|
|
259
|
-
function validateSecretBinding(child, id, secretWiring, enclosingModuleName) {
|
|
260
|
-
const { kind } = child;
|
|
261
|
-
for (const slot of Object.keys(child.secretSlots)) {
|
|
262
|
-
const bound = secretWiring[slot];
|
|
263
|
-
if (bound === void 0) throw new LoadError(`Secret slot "${slot}" of provisioned ${kind} "${id}" is not bound (module "${enclosingModuleName}") — bind it with envSecret('NAME') or forward ctx.secrets.`);
|
|
264
|
-
if (!isSecretSource(bound)) throw new LoadError(`Secret slot "${slot}" of "${id}" (module "${enclosingModuleName}") was wired with a non-secret value — use envSecret('NAME') or a forwarded ctx.secrets ref.`);
|
|
265
|
-
}
|
|
266
|
-
for (const slot of Object.keys(secretWiring)) if (!Object.hasOwn(child.secretSlots, slot)) throw new LoadError(`The secrets for "${id}" name "${slot}", which is not a secret slot of that ${kind} (module "${enclosingModuleName}").`);
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* Checks the params wired into a provisioned SERVICE: unlike a secret slot, a
|
|
270
|
-
* declared param is never required to be bound here — it may fall back to its
|
|
271
|
-
* own `default` (checked later, at `buildConfig`) — so this only rejects a
|
|
272
|
-
* wired key that names something other than a real declared param.
|
|
273
|
-
*/
|
|
274
|
-
function validateServiceParamBinding(child, id, paramWiring, enclosingModuleName) {
|
|
275
|
-
for (const slot of Object.keys(paramWiring)) if (!Object.hasOwn(child.params, slot)) throw new LoadError(`The params for "${id}" name "${slot}", which is not a param of that service (module "${enclosingModuleName}").`);
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* Checks the params wired into a provisioned MODULE: every declared
|
|
279
|
-
* param-forwarding slot must be bound to a real `ParamSource` — a slot has no
|
|
280
|
-
* schema of its own, so (unlike a service param) it cannot fall back to a
|
|
281
|
-
* literal or a default; the param analog of `validateSecretBinding`.
|
|
282
|
-
*/
|
|
283
|
-
function validateParamNeedBinding(child, id, paramWiring, enclosingModuleName) {
|
|
284
|
-
for (const slot of Object.keys(child.paramSlots)) {
|
|
285
|
-
const bound = paramWiring[slot];
|
|
286
|
-
if (bound === void 0) throw new LoadError(`Param slot "${slot}" of provisioned module "${id}" is not bound (module "${enclosingModuleName}") — bind it with a param source (e.g. envParam('NAME')) or forward ctx.params.`);
|
|
287
|
-
if (!isParamSource(bound)) throw new LoadError(`Param slot "${slot}" of "${id}" (module "${enclosingModuleName}") was wired with a non-source value — a module param-forwarding slot carries no schema to validate a literal against; use envParam('NAME') or a forwarded ctx.params ref.`);
|
|
288
|
-
}
|
|
289
|
-
for (const slot of Object.keys(paramWiring)) if (!Object.hasOwn(child.paramSlots, slot)) throw new LoadError(`The params for "${id}" name "${slot}", which is not a param slot of that module (module "${enclosingModuleName}").`);
|
|
290
|
-
}
|
|
291
|
-
/**
|
|
292
|
-
* Recursively flattens one module's body into the shared graph state and
|
|
293
|
-
* returns its resolved ModuleOutputs (one ref-port per expose key) for the
|
|
294
|
-
* caller (the enclosing provision() call, or Load itself for the root) to
|
|
295
|
-
* use. `address` is this module's OWN full address, or `undefined` for the root
|
|
296
|
-
* scope — its direct children then get bare (unprefixed) addresses, keeping
|
|
297
|
-
* a single-level module identical to before nesting existed. `wiring` supplies a
|
|
298
|
-
* resolved producer ref-port for each of this module's OWN declared deps (empty
|
|
299
|
-
* for the root, which may not declare any — see the root non-empty-deps
|
|
300
|
-
* check in loadModule). `nodes`, `edges`, `pending`, and `byId` are shared
|
|
301
|
-
* across the ENTIRE recursive flatten, not per scope — a nested module may
|
|
302
|
-
* forward in a producer provisioned by an ancestor scope, and it is the
|
|
303
|
-
* shared `byId` (keyed by full address) that lets that resolve.
|
|
304
|
-
*/
|
|
305
|
-
function flatten(moduleNode, address, wiring, secretWiring, paramWiring, nodes, edges, pending, inputBindings, paramBindings, byId) {
|
|
306
|
-
const localIds = /* @__PURE__ */ new Set();
|
|
307
|
-
const used = /* @__PURE__ */ new Set();
|
|
308
|
-
const usedSecrets = /* @__PURE__ */ new Set();
|
|
309
|
-
const usedParams = /* @__PURE__ */ new Set();
|
|
310
|
-
const ctxInputs = {};
|
|
311
|
-
for (const key of Object.keys(moduleNode.deps)) {
|
|
312
|
-
const wired = wiring[key];
|
|
313
|
-
ctxInputs[key] = typeof wired === "object" && wired !== null ? {
|
|
314
|
-
...wired,
|
|
315
|
-
[MODULE_INPUT_KEY]: key
|
|
316
|
-
} : wired;
|
|
317
|
-
}
|
|
318
|
-
const markUsed = (values) => {
|
|
319
|
-
for (const value of values) for (const key of Object.keys(ctxInputs)) if (value === ctxInputs[key]) used.add(key);
|
|
320
|
-
};
|
|
321
|
-
const ctxSecrets = {};
|
|
322
|
-
for (const key of Object.keys(moduleNode.secretSlots)) {
|
|
323
|
-
const bound = secretWiring[key];
|
|
324
|
-
ctxSecrets[key] = typeof bound === "object" && bound !== null ? {
|
|
325
|
-
...bound,
|
|
326
|
-
[MODULE_SECRET_KEY]: key
|
|
327
|
-
} : bound;
|
|
328
|
-
}
|
|
329
|
-
const markSecretsUsed = (values) => {
|
|
330
|
-
for (const value of values) for (const key of Object.keys(ctxSecrets)) if (value === ctxSecrets[key]) usedSecrets.add(key);
|
|
331
|
-
};
|
|
332
|
-
const ctxParams = {};
|
|
333
|
-
for (const key of Object.keys(moduleNode.paramSlots)) {
|
|
334
|
-
const bound = paramWiring[key];
|
|
335
|
-
ctxParams[key] = typeof bound === "object" && bound !== null ? {
|
|
336
|
-
...bound,
|
|
337
|
-
[MODULE_PARAM_KEY]: key
|
|
338
|
-
} : bound;
|
|
339
|
-
}
|
|
340
|
-
const markParamsUsed = (values) => {
|
|
341
|
-
for (const value of values) for (const key of Object.keys(ctxParams)) if (value === ctxParams[key]) usedParams.add(key);
|
|
342
|
-
};
|
|
343
|
-
const provision = (child, opts) => {
|
|
344
|
-
const id = opts?.id ?? child.name;
|
|
345
|
-
const provisionWiring = opts?.deps;
|
|
346
|
-
const provisionSecrets = opts?.secrets;
|
|
347
|
-
const provisionInput = opts?.input;
|
|
348
|
-
const provisionParams = opts?.params;
|
|
349
|
-
if (typeof id !== "string" || id.length === 0) throw new LoadError(`provision() requires a non-empty id (module "${moduleNode.name}").`);
|
|
350
|
-
if (!isConfigKeySegment(id)) throw new LoadError(`provision() id "${id}" (module "${moduleNode.name}") must contain only ASCII letters and digits ([A-Za-z0-9]) — the id becomes an address segment, and addresses derive deterministic config keys (segments uppercased and joined with "_"). "${id}" would put "${id.toUpperCase()}" inside the derived key. ("_" collides with the key separator and "." with the address separator.)`);
|
|
351
|
-
if (localIds.has(id)) throw new LoadError(`Duplicate provision id "${id}" in module "${moduleNode.name}".`);
|
|
352
|
-
const untrusted = child;
|
|
353
|
-
if (!isNode(untrusted) || untrusted.kind !== "service" && untrusted.kind !== "resource" && untrusted.kind !== "module") throw new LoadError(`provision("${id}") expects a branded service, resource, or module node (construct it with the service()/resource()/module() factories or a pack's own).`);
|
|
354
|
-
localIds.add(id);
|
|
355
|
-
const fullAddress = address === void 0 ? id : `${address}.${id}`;
|
|
356
|
-
if (child.kind === "resource") {
|
|
357
|
-
if (provisionWiring !== void 0) throw new LoadError(`provision("${id}") received deps for a resource — a resource has no dependency slots to satisfy.`);
|
|
358
|
-
if (provisionSecrets !== void 0) throw new LoadError(`provision("${id}") received secrets for a resource — a resource has no secret slots to satisfy.`);
|
|
359
|
-
if (provisionInput !== void 0) throw new LoadError(`provision("${id}") received an input binding for a resource — a resource declares no input schema.`);
|
|
360
|
-
if (provisionParams !== void 0) throw new LoadError(`provision("${id}") received params for a resource — a resource has no params to satisfy.`);
|
|
361
|
-
if (child.type.length === 0) throw new LoadError(`provision("${id}") received a resource with an empty node type.`);
|
|
362
|
-
byId.set(fullAddress, child);
|
|
363
|
-
nodes.push({
|
|
364
|
-
id: fullAddress,
|
|
365
|
-
node: child
|
|
366
|
-
});
|
|
367
|
-
return refForResource(fullAddress, child);
|
|
368
|
-
}
|
|
369
|
-
const localWiring = { ...provisionWiring ?? {} };
|
|
370
|
-
markUsed(Object.values(localWiring));
|
|
371
|
-
const localSecrets = { ...provisionSecrets ?? {} };
|
|
372
|
-
markSecretsUsed(Object.values(localSecrets));
|
|
373
|
-
const localParams = { ...provisionParams ?? {} };
|
|
374
|
-
markParamsUsed(Object.values(localParams));
|
|
375
|
-
if (child.kind === "service") {
|
|
376
|
-
if (provisionSecrets !== void 0) throw new LoadError(`provision("${id}") received secrets for a service — a service takes its secrets as envSecret(...) leaves of its \`input\` binding (ADR-0042), not a secrets map.`);
|
|
377
|
-
if (child.inputSchema !== void 0 && provisionInput === void 0) throw new LoadError(`Input of provisioned service "${id}" is not bound (module "${moduleNode.name}") — the service declares an input schema; bind it with \`input: { … }\` on its provision() call.`);
|
|
378
|
-
if (child.inputSchema === void 0 && provisionInput !== void 0) throw new LoadError(`provision("${id}") received an input binding, but the service declares no input schema (module "${moduleNode.name}").`);
|
|
379
|
-
if (provisionInput !== void 0) {
|
|
380
|
-
const leaves = [];
|
|
381
|
-
deepValues(provisionInput, leaves);
|
|
382
|
-
markSecretsUsed(leaves);
|
|
383
|
-
markParamsUsed(leaves);
|
|
384
|
-
inputBindings.push({
|
|
385
|
-
serviceAddress: fullAddress,
|
|
386
|
-
binding: blindCast(provisionInput)
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
validateServiceParamBinding(child, id, localParams, moduleNode.name);
|
|
390
|
-
for (const [slot, binding] of Object.entries(localParams)) paramBindings.push({
|
|
391
|
-
serviceAddress: fullAddress,
|
|
392
|
-
slot,
|
|
393
|
-
binding
|
|
394
|
-
});
|
|
395
|
-
const inputs = serviceInputs(child, fullAddress);
|
|
396
|
-
nodes.push(...inputs.nodes, {
|
|
397
|
-
id: fullAddress,
|
|
398
|
-
node: child
|
|
399
|
-
});
|
|
400
|
-
edges.push(...inputs.edges, ...wiringEdges(localWiring, fullAddress));
|
|
401
|
-
pending.push({
|
|
402
|
-
deps: child.inputs,
|
|
403
|
-
wiring: localWiring,
|
|
404
|
-
targetId: fullAddress,
|
|
405
|
-
targetKind: "service",
|
|
406
|
-
enclosingModuleName: moduleNode.name
|
|
407
|
-
});
|
|
408
|
-
byId.set(fullAddress, child);
|
|
409
|
-
return refFor(fullAddress, child);
|
|
410
|
-
}
|
|
411
|
-
if (provisionInput !== void 0) throw new LoadError(`provision("${id}") received an input binding for a module — a module has no input schema; bind the input on the service that declares it.`);
|
|
412
|
-
validateSecretBinding(child, id, localSecrets, moduleNode.name);
|
|
413
|
-
validateParamNeedBinding(child, id, localParams, moduleNode.name);
|
|
414
|
-
edges.push(...wiringEdges(localWiring, fullAddress));
|
|
415
|
-
pending.push({
|
|
416
|
-
deps: child.deps,
|
|
417
|
-
wiring: localWiring,
|
|
418
|
-
targetId: fullAddress,
|
|
419
|
-
targetKind: "module",
|
|
420
|
-
enclosingModuleName: moduleNode.name
|
|
421
|
-
});
|
|
422
|
-
const childOutputs = flatten(child, fullAddress, localWiring, localSecrets, localParams, nodes, edges, pending, inputBindings, paramBindings, byId);
|
|
423
|
-
nodes.push({
|
|
424
|
-
id: fullAddress,
|
|
425
|
-
node: child
|
|
426
|
-
});
|
|
427
|
-
byId.set(fullAddress, child);
|
|
428
|
-
return blindCast({
|
|
429
|
-
id: fullAddress,
|
|
430
|
-
...childOutputs
|
|
431
|
-
});
|
|
432
|
-
};
|
|
433
|
-
const ctx = blindCast({
|
|
434
|
-
inputs: ctxInputs,
|
|
435
|
-
secrets: ctxSecrets,
|
|
436
|
-
params: ctxParams,
|
|
437
|
-
provision: blindCast(provision)
|
|
438
|
-
});
|
|
439
|
-
const outputs = blindCast(moduleNode.body(ctx) ?? {});
|
|
440
|
-
markUsed(Object.values(outputs));
|
|
441
|
-
for (const key of Object.keys(moduleNode.deps)) if (!used.has(key)) throw new LoadError(`Module "${moduleNode.name}" declares input "${key}" but never forwards it into a provision nor returns it as an output.`);
|
|
442
|
-
for (const key of Object.keys(moduleNode.secretSlots)) if (!usedSecrets.has(key)) throw new LoadError(`Module "${moduleNode.name}" declares secret "${key}" but never forwards it into a provision.`);
|
|
443
|
-
for (const key of Object.keys(moduleNode.paramSlots)) if (!usedParams.has(key)) throw new LoadError(`Module "${moduleNode.name}" declares param "${key}" but never forwards it into a provision.`);
|
|
444
|
-
for (const [key, contract] of Object.entries(moduleNode.expose)) {
|
|
445
|
-
const port = outputs[key];
|
|
446
|
-
if (port === void 0) throw new LoadError(`Module "${moduleNode.name}" declares expose "${key}" but its body did not return a port for it.`);
|
|
447
|
-
if (!satisfiesRequired(port, contract)) throw new LoadError(`Module "${moduleNode.name}"'s returned port for expose "${key}" does not satisfy its declared contract.`);
|
|
448
|
-
}
|
|
449
|
-
return outputs;
|
|
450
|
-
}
|
|
451
|
-
function loadModule(root, opts) {
|
|
452
|
-
const rootId = opts?.id ?? root.name;
|
|
453
|
-
const rootDepKeys = Object.keys(root.deps);
|
|
454
|
-
if (rootDepKeys.length > 0) {
|
|
455
|
-
const names = rootDepKeys.map((k) => `"${k}"`).join(", ");
|
|
456
|
-
throw new LoadError(`Module "${root.name}" declares input${rootDepKeys.length > 1 ? "s" : ""} ${names} but is being deployed as the root — a root has no enclosing scope to wire them; compose "${root.name}" from another module that provisions and wires it instead.`);
|
|
457
|
-
}
|
|
458
|
-
const rootSecretKeys = Object.keys(root.secretSlots);
|
|
459
|
-
if (rootSecretKeys.length > 0) {
|
|
460
|
-
const names = rootSecretKeys.map((k) => `"${k}"`).join(", ");
|
|
461
|
-
throw new LoadError(`Module "${root.name}" declares secret${rootSecretKeys.length > 1 ? "s" : ""} ${names} but is being deployed as the root — a root has no enclosing scope to bind them; the root binds secrets with envSecret('NAME'), it does not declare secret slots of its own.`);
|
|
462
|
-
}
|
|
463
|
-
const rootParamKeys = Object.keys(root.paramSlots);
|
|
464
|
-
if (rootParamKeys.length > 0) {
|
|
465
|
-
const names = rootParamKeys.map((k) => `"${k}"`).join(", ");
|
|
466
|
-
throw new LoadError(`Module "${root.name}" declares param${rootParamKeys.length > 1 ? "s" : ""} ${names} but is being deployed as the root — a root has no enclosing scope to bind them; the root binds params directly on each provision() call, it does not declare param-forwarding slots of its own.`);
|
|
467
|
-
}
|
|
468
|
-
const nodes = [];
|
|
469
|
-
const edges = [];
|
|
470
|
-
const pending = [];
|
|
471
|
-
const inputBindings = [];
|
|
472
|
-
const paramBindings = [];
|
|
473
|
-
const byId = /* @__PURE__ */ new Map();
|
|
474
|
-
flatten(root, void 0, {}, {}, {}, nodes, edges, pending, inputBindings, paramBindings, byId);
|
|
475
|
-
for (const entry of pending) validateWiring(entry, byId);
|
|
476
|
-
assertDependencyDag(edges);
|
|
477
|
-
const rootGraphNode = {
|
|
478
|
-
id: rootId,
|
|
479
|
-
node: root
|
|
480
|
-
};
|
|
481
|
-
return {
|
|
482
|
-
root: rootGraphNode,
|
|
483
|
-
nodes: [...topoSort(nodes, edges), rootGraphNode],
|
|
484
|
-
edges,
|
|
485
|
-
inputBindings,
|
|
486
|
-
params: paramBindings
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
/**
|
|
490
|
-
* The dependency edges must form a DAG — a cycle means neither producer can
|
|
491
|
-
* deploy first. Resources take no wiring, so only service/module-to-service/module
|
|
492
|
-
* edges can ever participate in a cycle; no special-casing needed.
|
|
493
|
-
*/
|
|
494
|
-
function assertDependencyDag(edges) {
|
|
495
|
-
const adjacency = /* @__PURE__ */ new Map();
|
|
496
|
-
for (const edge of edges) {
|
|
497
|
-
if (edge.kind !== "dependency") continue;
|
|
498
|
-
const targets = adjacency.get(edge.from) ?? [];
|
|
499
|
-
targets.push(edge.to);
|
|
500
|
-
adjacency.set(edge.from, targets);
|
|
501
|
-
}
|
|
502
|
-
const visiting = /* @__PURE__ */ new Set();
|
|
503
|
-
const done = /* @__PURE__ */ new Set();
|
|
504
|
-
const stack = [];
|
|
505
|
-
const visit = (id) => {
|
|
506
|
-
if (done.has(id)) return;
|
|
507
|
-
if (visiting.has(id)) throw new LoadError(`Dependency cycle: ${[...stack.slice(stack.indexOf(id)), id].join(" → ")} — no deploy order exists.`);
|
|
508
|
-
visiting.add(id);
|
|
509
|
-
stack.push(id);
|
|
510
|
-
for (const next of adjacency.get(id) ?? []) visit(next);
|
|
511
|
-
stack.pop();
|
|
512
|
-
visiting.delete(id);
|
|
513
|
-
done.add(id);
|
|
514
|
-
};
|
|
515
|
-
for (const id of adjacency.keys()) visit(id);
|
|
516
|
-
}
|
|
517
|
-
/**
|
|
518
|
-
* Builds the in-memory graph from a root node. A service root walks its own
|
|
519
|
-
* `inputs`; a module root executes its body (wiring, not user code — the
|
|
520
|
-
* designed exception to imports-run-nothing) and recursively flattens every
|
|
521
|
-
* module it provisions into one graph of hierarchical addresses. A malformed
|
|
522
|
-
* graph is a `LoadError` that names its fix; the individual validation rules
|
|
523
|
-
* live with `loadService` / `loadModule` and are covered by name in the Load
|
|
524
|
-
* tests. Executes nothing of the user's own code beyond module bodies.
|
|
525
|
-
*/
|
|
526
|
-
function Load(root, opts) {
|
|
527
|
-
if (!isNode(root)) throw new LoadError("Load expects a branded service or module node (construct it with the service()/module() factories).");
|
|
528
|
-
if (root.kind === "module") return loadModule(root, opts);
|
|
529
|
-
if (root.kind === "service") return loadService(root, opts?.id ?? "root");
|
|
530
|
-
throw new LoadError("Load expects a service or module root (received another node kind).");
|
|
531
|
-
}
|
|
532
|
-
blindCast(Symbol.for("prisma:secret-box"));
|
|
533
|
-
//#endregion
|
|
534
|
-
//#region ../../0-framework/3-tooling/cli/dist/load-entry-BYBSCuWg.mjs
|
|
535
|
-
/**
|
|
536
|
-
* `loadEntry` imports a user's topology with the host runtime's own module
|
|
537
|
-
* loader — under node (the CLI's shebang runtime), that loader strips
|
|
538
|
-
* TypeScript types but has no JSX transform (ADR-0005: the framework
|
|
539
|
-
* doesn't bundle or transform the app's code; deploy loading is no
|
|
540
|
-
* exception). A `.tsx`/`.jsx` file anywhere in the topology's import graph
|
|
541
|
-
* dies with node's raw `ERR_UNKNOWN_FILE_EXTENSION`, which names the file
|
|
542
|
-
* but not the cause or the fix. This turns that one case into structured
|
|
543
|
-
* error parts (summary/why/fix) that do — every other load failure (syntax
|
|
544
|
-
* errors, missing files, non-JSX unknown extensions) is untouched.
|
|
545
|
-
*/
|
|
546
|
-
const UNKNOWN_EXTENSION_CODE = "ERR_UNKNOWN_FILE_EXTENSION";
|
|
547
|
-
const JSX_EXTENSIONS = [".tsx", ".jsx"];
|
|
548
|
-
function errorCode(error) {
|
|
549
|
-
return "code" in error && typeof error.code === "string" ? error.code : void 0;
|
|
550
|
-
}
|
|
551
|
-
function unknownExtensionPath(error) {
|
|
552
|
-
if (!(error instanceof Error)) return void 0;
|
|
553
|
-
if (errorCode(error) !== UNKNOWN_EXTENSION_CODE) return void 0;
|
|
554
|
-
return /Unknown file extension "\.\w+" for (.+)$/.exec(error.message)?.[1];
|
|
555
|
-
}
|
|
556
|
-
/**
|
|
557
|
-
* Returns structured error parts when `error` is node's
|
|
558
|
-
* `ERR_UNKNOWN_FILE_EXTENSION` for a `.tsx`/`.jsx` file — `undefined` for
|
|
559
|
-
* every other error (including unknown-extension failures for some other
|
|
560
|
-
* extension), so the caller can fall through to its normal rethrow.
|
|
561
|
-
*/
|
|
562
|
-
function explainJsxLoadError(error, entryPath) {
|
|
563
|
-
const offendingPath = unknownExtensionPath(error);
|
|
564
|
-
if (offendingPath === void 0) return void 0;
|
|
565
|
-
if (!JSX_EXTENSIONS.some((ext) => offendingPath.endsWith(ext))) return void 0;
|
|
566
|
-
return {
|
|
567
|
-
summary: `"${offendingPath}" can't be loaded.`,
|
|
568
|
-
why: `deploy loads "${entryPath}"'s topology with node's own module loader, which strips TypeScript types but has no JSX transform — so a .tsx/.jsx file can't sit anywhere in that import graph.`,
|
|
569
|
-
fix: "Precompile that file in your own build (transform the JSX away, keep the real npm packages external) and import the compiled output instead of the raw .tsx/.jsx — see examples/email/scripts/build.ts in the prisma/composer repo for a worked example."
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
|
-
/**
|
|
573
|
-
* Pipeline step 1 (deploy-cli.md § The pipeline): import the entry module
|
|
574
|
-
* (resolved against cwd) and require its default export to be a node — a
|
|
575
|
-
* service or module, branded by core's factories. Whatever this module exports
|
|
576
|
-
* IS the application; nothing else marks a root (ADR-0003).
|
|
577
|
-
*/
|
|
578
|
-
async function loadEntry(entryArg, cwd) {
|
|
579
|
-
const resolvedPath = path.resolve(cwd, entryArg);
|
|
580
|
-
let mod;
|
|
581
|
-
try {
|
|
582
|
-
mod = await import(pathToFileURL(resolvedPath).href);
|
|
583
|
-
} catch (error) {
|
|
584
|
-
const explained = explainJsxLoadError(error, resolvedPath);
|
|
585
|
-
if (explained !== void 0) throw new CliStructuredError("COMPOSE.ENTRY_UNLOADABLE", explained.summary, {
|
|
586
|
-
why: explained.why,
|
|
587
|
-
fix: explained.fix,
|
|
588
|
-
where: { path: resolvedPath },
|
|
589
|
-
cause: error
|
|
590
|
-
});
|
|
591
|
-
throw new CliStructuredError("COMPOSE.ENTRY_UNLOADABLE", `Failed to import entry module "${resolvedPath}": ${error instanceof Error ? error.message : String(error)}`, {
|
|
592
|
-
where: { path: resolvedPath },
|
|
593
|
-
cause: error
|
|
594
|
-
});
|
|
595
|
-
}
|
|
596
|
-
const root = blindCast(mod).default;
|
|
597
|
-
if (!isNode(root) || root.kind === "dependency" || root.kind === "resource") throw new CliStructuredError("COMPOSE.ENTRY_EXPORT_INVALID", `Entry module "${resolvedPath}" must default-export a node (a service or a module).`, {
|
|
598
|
-
fix: "Construct it with service() or module() from @prisma/composer.",
|
|
599
|
-
where: { path: resolvedPath }
|
|
600
|
-
});
|
|
601
|
-
return {
|
|
602
|
-
path: resolvedPath,
|
|
603
|
-
root: blindCast(root)
|
|
604
|
-
};
|
|
605
|
-
}
|
|
606
|
-
//#endregion
|
|
607
|
-
//#region ../../0-framework/3-tooling/assemble/dist/index.mjs
|
|
608
|
-
/**
|
|
609
|
-
* A user-facing assembly failure, structured at origin (base-type rule 6):
|
|
610
|
-
* a `CliStructuredError` whose code names which way assembly went wrong.
|
|
611
|
-
* This package's second consumer is the programmatic deploy API, not just
|
|
612
|
-
* the CLI — hosts branch on `error.code`, the CLI renders the envelope.
|
|
613
|
-
*/
|
|
614
|
-
var AssembleError = class extends CliStructuredError {
|
|
615
|
-
constructor(code, summary, options) {
|
|
616
|
-
super(code, summary, options);
|
|
617
|
-
}
|
|
618
|
-
};
|
|
619
|
-
/**
|
|
620
|
-
* The registry route for one service's build: extension by
|
|
621
|
-
* `build.extension`, node descriptor by `build.type`, kind must be "build".
|
|
622
|
-
* The CLI's coverage validation reports the same misses earlier with the
|
|
623
|
-
* config fix; these errors are the backstop for programmatic callers.
|
|
624
|
-
*/
|
|
625
|
-
function buildDescriptorAssemble(config, node, address, cwd) {
|
|
626
|
-
const { extension, type } = node.build;
|
|
627
|
-
const extensionDescriptor = config.extensions.find((candidate) => candidate.id === extension);
|
|
628
|
-
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`." });
|
|
629
|
-
const nodeDescriptor = extensionDescriptor.nodes[type];
|
|
630
|
-
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(", ")}.` });
|
|
631
|
-
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." });
|
|
632
|
-
return nodeDescriptor.assemble({
|
|
633
|
-
build: node.build,
|
|
634
|
-
address,
|
|
635
|
-
cwd
|
|
636
|
-
});
|
|
637
|
-
}
|
|
638
|
-
async function assembleServices(graph, config, cwd, run) {
|
|
639
|
-
const runAssembler = run ?? ((node, address, nodeCwd) => buildDescriptorAssemble(config, node, address, nodeCwd));
|
|
640
|
-
const serviceNodes = graph.nodes.filter((n) => n.node.kind === "service");
|
|
641
|
-
if (serviceNodes.length === 0) throw new AssembleError("ASSEMBLE.SERVICE_MISSING", "The loaded graph has no service to assemble.");
|
|
642
|
-
const bundles = {};
|
|
643
|
-
for (const { id, node } of serviceNodes) try {
|
|
644
|
-
bundles[id] = await runAssembler(node, id, cwd);
|
|
645
|
-
} catch (error) {
|
|
646
|
-
if (CliStructuredError.is(error)) throw error;
|
|
647
|
-
throw new AssembleError("ASSEMBLE.BUILD_FAILED", error instanceof Error ? error.message : String(error), {
|
|
648
|
-
meta: { address: id },
|
|
649
|
-
cause: error
|
|
650
|
-
});
|
|
651
|
-
}
|
|
652
|
-
return { bundles };
|
|
653
|
-
}
|
|
654
|
-
//#endregion
|
|
655
|
-
//#region ../../0-framework/3-tooling/cli/dist/pipeline-AoW8zq4I.mjs
|
|
656
|
-
/**
|
|
657
|
-
* Pipeline step: find and load `prisma-composer.config.ts` (ADR-0017) — the ONE
|
|
658
|
-
* file that imports control-plane code. Discovery is the standard walk-up
|
|
659
|
-
* from the deploy entry's directory (mirrors prisma-next's config-loader);
|
|
660
|
-
* loading is c12 with that explicit path (rc/global/package.json lookups
|
|
661
|
-
* disabled), so the config file's own static imports resolve from the app
|
|
662
|
-
* root by whatever package manager runs — no specifier construction, no
|
|
663
|
-
* anchoring.
|
|
664
|
-
*
|
|
665
|
-
* Two shapes of the same machinery:
|
|
666
|
-
*
|
|
667
|
-
* loadAppConfigDiagnostics — returns the evaluated value together with
|
|
668
|
-
* EVERY problem found, and never throws. This is the shape the engine
|
|
669
|
-
* command family consumes: the engine renders diagnostics itself and
|
|
670
|
-
* decides per command whether a broken section is fatal, so a loader that
|
|
671
|
-
* threw on the first bad field would both hide the rest and bypass that
|
|
672
|
-
* rendering.
|
|
673
|
-
*
|
|
674
|
-
* loadAppConfig — the same load, throwing its first diagnostic. The
|
|
675
|
-
* clipanion CLI still runs on it; it goes when clipanion does.
|
|
676
|
-
*/
|
|
677
|
-
const CONFIG_FILENAME = "prisma-composer.config.ts";
|
|
678
|
-
/** Walks UP from the entry file's directory looking for the literal CONFIG_FILENAME; undefined when the walk hits the filesystem root. */
|
|
679
|
-
function findConfigPathForEntry(entryPath) {
|
|
680
|
-
let current = path.dirname(path.resolve(entryPath));
|
|
681
|
-
while (true) {
|
|
682
|
-
const candidate = path.join(current, CONFIG_FILENAME);
|
|
683
|
-
if (fs.existsSync(candidate)) return candidate;
|
|
684
|
-
const parent = path.dirname(current);
|
|
685
|
-
if (parent === current) return void 0;
|
|
686
|
-
current = parent;
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
function missingConfigError(entryPath) {
|
|
690
|
-
const searchedFrom = path.dirname(path.resolve(entryPath));
|
|
691
|
-
return new CliStructuredError("CONFIG.FILE_MISSING", `No ${CONFIG_FILENAME} found walking up from "${searchedFrom}".`, {
|
|
692
|
-
why: "The deploy needs the app's config file.",
|
|
693
|
-
fix: "Create one next to (or above) the entry, default-exporting defineConfig({ extensions: [...], state: ... }) from '@prisma/composer/config'.",
|
|
694
|
-
where: { path: searchedFrom }
|
|
695
|
-
});
|
|
696
|
-
}
|
|
697
|
-
function fieldError(field, requirement) {
|
|
698
|
-
return new CliStructuredError("CONFIG.FIELD_INVALID", `${CONFIG_FILENAME}: \`${field}\` ${requirement}.`, {
|
|
699
|
-
fix: "See defineConfig() in '@prisma/composer/config'.",
|
|
700
|
-
meta: { field }
|
|
701
|
-
});
|
|
702
|
-
}
|
|
703
|
-
function isRecord(value) {
|
|
704
|
-
return typeof value === "object" && value !== null;
|
|
705
|
-
}
|
|
706
|
-
/**
|
|
707
|
-
* Field-by-field validation of the loaded default export — deliberately no
|
|
708
|
-
* schema library: each check is a structured error naming the offending field.
|
|
709
|
-
*
|
|
710
|
-
* Collects rather than throws, so one bad config reports every bad field
|
|
711
|
-
* instead of only the first. The `extensions` and `state` checks are
|
|
712
|
-
* independent, and within `extensions` each entry is judged on its own; a
|
|
713
|
-
* field whose own shape check already failed is not probed further, because
|
|
714
|
-
* anything derived from it would be noise rather than a second finding.
|
|
715
|
-
*/
|
|
716
|
-
function configShapeDiagnostics(loaded, configPath) {
|
|
717
|
-
if (!isRecord(loaded) || Object.keys(loaded).length === 0) return [new CliStructuredError("CONFIG.EXPORT_INVALID", `"${configPath}" exported no config.`, {
|
|
718
|
-
fix: "It must default-export defineConfig({ extensions: [...], state: ... }) from '@prisma/composer/config'.",
|
|
719
|
-
where: { path: configPath }
|
|
720
|
-
})];
|
|
721
|
-
const diagnostics = [];
|
|
722
|
-
const extensions = loaded["extensions"];
|
|
723
|
-
if (!Array.isArray(extensions)) diagnostics.push(fieldError("extensions", "must be an array"));
|
|
724
|
-
else {
|
|
725
|
-
const seen = /* @__PURE__ */ new Set();
|
|
726
|
-
for (const [index, entry] of extensions.entries()) {
|
|
727
|
-
if (!isRecord(entry)) {
|
|
728
|
-
diagnostics.push(fieldError(`extensions[${index}]`, "must be an extension descriptor object"));
|
|
729
|
-
continue;
|
|
730
|
-
}
|
|
731
|
-
const id = entry["id"];
|
|
732
|
-
if (typeof id !== "string" || id.length === 0) diagnostics.push(fieldError(`extensions[${index}].id`, "must be a non-empty string (the extension package name)"));
|
|
733
|
-
else if (seen.has(id)) diagnostics.push(new CliStructuredError("CONFIG.EXTENSION_DUPLICATE", `${CONFIG_FILENAME}: extension "${id}" is listed more than once in \`extensions\`.`));
|
|
734
|
-
else seen.add(id);
|
|
735
|
-
if (!isRecord(entry["nodes"])) diagnostics.push(fieldError(`extensions[${index}].nodes`, "must be an object (the node-ID → control registry)"));
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
const state = loaded["state"];
|
|
739
|
-
if (!isRecord(state) || typeof state["extension"] !== "string" || typeof state["create"] !== "function") diagnostics.push(fieldError("state", "must be a state descriptor (e.g. prismaState())"));
|
|
740
|
-
return diagnostics;
|
|
741
|
-
}
|
|
742
|
-
/** The throwing form of configShapeDiagnostics: raises the first finding, returns the same object typed. Used by the clipanion pipeline. */
|
|
743
|
-
function validateConfigShape(loaded, configPath) {
|
|
744
|
-
const [first] = configShapeDiagnostics(loaded, configPath);
|
|
745
|
-
if (first !== void 0) throw first;
|
|
746
|
-
return blindCast(loaded);
|
|
747
|
-
}
|
|
748
|
-
/**
|
|
749
|
-
* Evaluates `configPath` with c12 (explicit file; rc / global-rc /
|
|
750
|
-
* package.json lookups disabled). `verifySamePath` runs the same-file check
|
|
751
|
-
* against the discovered path — see loadAppConfigDiagnostics for when it does
|
|
752
|
-
* not apply.
|
|
753
|
-
*/
|
|
754
|
-
async function evaluateConfig(configPath, verifySamePath) {
|
|
755
|
-
let result;
|
|
756
|
-
try {
|
|
757
|
-
result = await c12.loadConfig({
|
|
758
|
-
name: "prisma-composer",
|
|
759
|
-
configFile: configPath,
|
|
760
|
-
cwd: path.dirname(configPath),
|
|
761
|
-
rcFile: false,
|
|
762
|
-
globalRc: false,
|
|
763
|
-
packageJson: false
|
|
764
|
-
});
|
|
765
|
-
} catch (error) {
|
|
766
|
-
return {
|
|
767
|
-
value: void 0,
|
|
768
|
-
diagnostics: [new CliStructuredError("CONFIG.EVALUATION_FAILED", `Evaluating "${configPath}" failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
769
|
-
where: { path: configPath },
|
|
770
|
-
cause: error
|
|
771
|
-
})]
|
|
772
|
-
};
|
|
773
|
-
}
|
|
774
|
-
const loadedFile = result.configFile;
|
|
775
|
-
if (verifySamePath && (typeof loadedFile !== "string" || fs.realpathSync(loadedFile) !== fs.realpathSync(configPath))) return {
|
|
776
|
-
value: result.config,
|
|
777
|
-
diagnostics: [new CliStructuredError("CONFIG.PATH_MISMATCH", `Config loading resolved "${String(loadedFile)}" instead of the discovered "${configPath}".`, {
|
|
778
|
-
why: "Refusing to deploy against a different file.",
|
|
779
|
-
where: { path: configPath }
|
|
780
|
-
})]
|
|
781
|
-
};
|
|
782
|
-
return {
|
|
783
|
-
value: result.config,
|
|
784
|
-
diagnostics: configShapeDiagnostics(result.config, configPath)
|
|
785
|
-
};
|
|
786
|
-
}
|
|
787
|
-
/**
|
|
788
|
-
* The front of every config load, shared by the throwing and the
|
|
789
|
-
* diagnostics-list shapes: verify the installed tree can evaluate a config at
|
|
790
|
-
* all, then settle which file to load — the section's path, or the
|
|
791
|
-
* entry-anchored walk.
|
|
792
|
-
*
|
|
793
|
-
* The effect-resolution check comes first because evaluating a config imports
|
|
794
|
-
* alchemy's provider tree: in a tree where alchemy resolves an `effect` we did
|
|
795
|
-
* not pin, nothing downstream can load, so the dependency conflict is the
|
|
796
|
-
* finding with the fix in it and every later failure is its symptom.
|
|
797
|
-
*/
|
|
798
|
-
function configSource(request) {
|
|
799
|
-
const resolvedEntry = path.resolve(request.entryPath);
|
|
800
|
-
const cwd = request.cwd ?? path.dirname(resolvedEntry);
|
|
801
|
-
const effectConflict = effectResolutionDiagnostic(cwd);
|
|
802
|
-
if (effectConflict !== void 0) return {
|
|
803
|
-
ok: false,
|
|
804
|
-
path: void 0,
|
|
805
|
-
diagnostic: effectConflict
|
|
806
|
-
};
|
|
807
|
-
if (request.configPath === void 0) {
|
|
808
|
-
const discovered = findConfigPathForEntry(resolvedEntry);
|
|
809
|
-
return discovered === void 0 ? {
|
|
810
|
-
ok: false,
|
|
811
|
-
path: void 0,
|
|
812
|
-
diagnostic: missingConfigError(resolvedEntry)
|
|
813
|
-
} : {
|
|
814
|
-
ok: true,
|
|
815
|
-
path: discovered,
|
|
816
|
-
explicit: false
|
|
817
|
-
};
|
|
818
|
-
}
|
|
819
|
-
const configPath = path.resolve(cwd, request.configPath);
|
|
820
|
-
if (!fs.existsSync(configPath)) return {
|
|
821
|
-
ok: false,
|
|
822
|
-
path: configPath,
|
|
823
|
-
diagnostic: new CliStructuredError("CONFIG.FILE_MISSING", `The \`composer\` config section points at "${configPath}", which does not exist.`, {
|
|
824
|
-
why: "An explicit configPath is used as given — there is no walk to fall back on.",
|
|
825
|
-
fix: "Correct `configPath` in the `composer` section of prisma.config.ts, or remove it to search upward from the entry.",
|
|
826
|
-
where: { path: configPath }
|
|
827
|
-
})
|
|
828
|
-
};
|
|
829
|
-
return {
|
|
830
|
-
ok: true,
|
|
831
|
-
path: configPath,
|
|
832
|
-
explicit: true
|
|
833
|
-
};
|
|
834
|
-
}
|
|
835
|
-
/**
|
|
836
|
-
* The throwing form of that front, for the pipeline: the file the load will
|
|
837
|
-
* use, or the finding raised. `explicit` says the section named the file,
|
|
838
|
-
* which is what retires the same-file check for that case.
|
|
839
|
-
*/
|
|
840
|
-
function resolveConfigFile(request) {
|
|
841
|
-
const source = configSource(request);
|
|
842
|
-
if (!source.ok) throw source.diagnostic;
|
|
843
|
-
return {
|
|
844
|
-
path: source.path,
|
|
845
|
-
explicit: source.explicit
|
|
846
|
-
};
|
|
847
|
-
}
|
|
848
|
-
/**
|
|
849
|
-
* Loads + validates the config at `configPath`, throwing its first diagnostic.
|
|
850
|
-
* The clipanion CLI's pipeline runs on this; it goes when clipanion does.
|
|
851
|
-
*/
|
|
852
|
-
async function loadAppConfig(configPath, verifySamePath = true) {
|
|
853
|
-
const { value, diagnostics } = await evaluateConfig(configPath, verifySamePath);
|
|
854
|
-
const [first] = diagnostics;
|
|
855
|
-
if (first !== void 0) throw first;
|
|
856
|
-
return {
|
|
857
|
-
path: configPath,
|
|
858
|
-
config: validateConfigShape(value, configPath)
|
|
859
|
-
};
|
|
860
|
-
}
|
|
861
|
-
function lookup(extensions, extension, type, expectedKind, what) {
|
|
862
|
-
const ext = extensions.get(extension);
|
|
863
|
-
if (ext === void 0) throw new CliStructuredError("CONFIG.EXTENSION_MISSING", `No extension "${extension}" is configured (needed by ${what}).`, {
|
|
864
|
-
fix: `Add it to ${CONFIG_FILENAME}'s \`extensions\` (import its /control entry and list its descriptor).`,
|
|
865
|
-
meta: {
|
|
866
|
-
extension,
|
|
867
|
-
type
|
|
868
|
-
}
|
|
869
|
-
});
|
|
870
|
-
const descriptor = ext.nodes[type];
|
|
871
|
-
if (descriptor === void 0) throw new CliStructuredError("CONFIG.DESCRIPTOR_MISSING", `Extension "${extension}" has no descriptor for node type "${type}" (needed by ${what}).`, {
|
|
872
|
-
why: `Known types: ${Object.keys(ext.nodes).join(", ")}.`,
|
|
873
|
-
meta: {
|
|
874
|
-
extension,
|
|
875
|
-
type
|
|
876
|
-
}
|
|
877
|
-
});
|
|
878
|
-
if (descriptor.kind !== expectedKind) throw new CliStructuredError("CONFIG.DESCRIPTOR_KIND_MISMATCH", `Extension "${extension}"'s descriptor for node type "${type}" is a "${descriptor.kind}" descriptor — ${what} needs a "${expectedKind}" descriptor.`, { meta: {
|
|
879
|
-
extension,
|
|
880
|
-
type,
|
|
881
|
-
kind: descriptor.kind
|
|
882
|
-
} });
|
|
883
|
-
}
|
|
884
|
-
/** Throws a structured CONFIG.* error on the first uncovered `(extension, type)`; silent when the config covers the whole graph. */
|
|
885
|
-
function validateRegistryCoverage(graph, config) {
|
|
886
|
-
const extensions = new Map(config.extensions.map((descriptor) => [descriptor.id, descriptor]));
|
|
887
|
-
for (const { id, node } of graph.nodes) {
|
|
888
|
-
if (node.kind === "resource") {
|
|
889
|
-
lookup(extensions, node.extension, node.type, "resource", `resource node "${id}"`);
|
|
890
|
-
continue;
|
|
891
|
-
}
|
|
892
|
-
if (node.kind !== "service") continue;
|
|
893
|
-
lookup(extensions, node.extension, node.type, "service", `service node "${id}"`);
|
|
894
|
-
lookup(extensions, node.build.extension, node.build.type, "build", `service node "${id}"'s build descriptor`);
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
/**
|
|
898
|
-
* The shared prefix of `deploy`/`destroy`/`dev` (deploy-cli.md § The
|
|
899
|
-
* pipeline; local-dev spec § 6): config discovery/load, entry load, Load,
|
|
900
|
-
* registry coverage validation, name resolution, assemble. Deploy and dev
|
|
901
|
-
* diverge after this — deploy resolves containers/preflight/stack file
|
|
902
|
-
* against the hosted providers, dev against the local ones — so everything
|
|
903
|
-
* up to and including assemble lives here once, consumed verbatim by both
|
|
904
|
-
* `run()` (main.ts) and `runDev()` (dev/run-dev.ts), so the two pipelines
|
|
905
|
-
* cannot drift.
|
|
906
|
-
*/
|
|
907
|
-
/**
|
|
908
|
-
* The config step both pipelines share: the effect-resolution check and the
|
|
909
|
-
* choice of config file (load-config.ts's shared front), then evaluation —
|
|
910
|
-
* unless the caller substituted a config, which replaces the evaluation and
|
|
911
|
-
* nothing before it.
|
|
912
|
-
*/
|
|
913
|
-
async function loadConfigStep(entryPath, cwd, deps) {
|
|
914
|
-
const { path: configPath, explicit } = resolveConfigFile({
|
|
915
|
-
entryPath,
|
|
916
|
-
configPath: deps.configPath,
|
|
917
|
-
cwd
|
|
918
|
-
});
|
|
919
|
-
return {
|
|
920
|
-
configPath,
|
|
921
|
-
config: deps.config ?? (await loadAppConfig(configPath, !explicit)).config
|
|
922
|
-
};
|
|
923
|
-
}
|
|
924
|
-
/**
|
|
925
|
-
* The pipeline's front — config discovery/load, entry load, name resolution —
|
|
926
|
-
* WITHOUT Load, coverage, or assemble. `log` needs only who the app is (its
|
|
927
|
-
* config and resolved name) to reach the already-running local instance; it
|
|
928
|
-
* neither builds nor provisions, so it must not require the user's built
|
|
929
|
-
* output the way the full pipeline does.
|
|
930
|
-
*/
|
|
931
|
-
async function resolveAppIdentity(entry, overrideName, cwd, deps = {}) {
|
|
932
|
-
const { configPath, config } = await loadConfigStep(path.resolve(cwd, entry), cwd, deps);
|
|
933
|
-
const entryModule = await loadEntry(entry, cwd);
|
|
934
|
-
const name = overrideName ?? entryModule.root.name;
|
|
935
|
-
if (name.length === 0) throw new CliStructuredError("COMPOSE.NAME_MISSING", "The root node has no name.", { fix: "Name it at authoring, or pass --name." });
|
|
936
|
-
return {
|
|
937
|
-
configPath,
|
|
938
|
-
config,
|
|
939
|
-
name
|
|
940
|
-
};
|
|
941
|
-
}
|
|
942
|
-
/**
|
|
943
|
-
* Runs config discovery/load, entry load, Load, registry coverage, name
|
|
944
|
-
* resolution, and assemble — steps 1–6 of `run()`. `onAssembleError`, when
|
|
945
|
-
* given, lets a caller decorate an assemble failure with command-specific
|
|
946
|
-
* guidance (destroy's "build first" hint) without this shared step knowing
|
|
947
|
-
* about any one command.
|
|
948
|
-
*/
|
|
949
|
-
async function runPipeline(entry, overrideName, cwd, deps = {}, onAssembleError) {
|
|
950
|
-
const { configPath, config } = await loadConfigStep(path.resolve(cwd, entry), cwd, deps);
|
|
951
|
-
const entryModule = await loadEntry(entry, cwd);
|
|
952
|
-
const graph = Load(entryModule.root);
|
|
953
|
-
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); })." });
|
|
954
|
-
validateRegistryCoverage(graph, config);
|
|
955
|
-
const name = overrideName ?? entryModule.root.name;
|
|
956
|
-
if (name.length === 0) throw new CliStructuredError("COMPOSE.NAME_MISSING", "The root node has no name.", { fix: "Name it at authoring, or pass --name." });
|
|
957
|
-
let assembled;
|
|
958
|
-
try {
|
|
959
|
-
assembled = await assembleServices(graph, config, cwd, deps.runAssembler);
|
|
960
|
-
} catch (error) {
|
|
961
|
-
if (onAssembleError !== void 0 && error instanceof Error) throw onAssembleError(error);
|
|
962
|
-
throw error;
|
|
963
|
-
}
|
|
964
|
-
return {
|
|
965
|
-
configPath,
|
|
966
|
-
config,
|
|
967
|
-
entryModule,
|
|
968
|
-
graph,
|
|
969
|
-
name,
|
|
970
|
-
assembled
|
|
971
|
-
};
|
|
972
|
-
}
|
|
973
|
-
//#endregion
|
|
974
|
-
export { runPipeline as n, resolveAppIdentity as t };
|
|
975
|
-
|
|
976
|
-
//# sourceMappingURL=pipeline-AoW8zq4I-C6Qe0VaZ.mjs.map
|