@prisma/composer 0.1.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 (51) hide show
  1. package/LICENSE +201 -0
  2. package/dist/app-config-CpWN1ZfP-CZ1c6Eqk.d.mts +325 -0
  3. package/dist/assertions.d.mts +31 -0
  4. package/dist/assertions.mjs +35 -0
  5. package/dist/assertions.mjs.map +1 -0
  6. package/dist/bin.mjs +1229 -0
  7. package/dist/bin.mjs.map +1 -0
  8. package/dist/casts-Ci5rYYaR.mjs +82 -0
  9. package/dist/casts-Ci5rYYaR.mjs.map +1 -0
  10. package/dist/casts.d.mts +78 -0
  11. package/dist/casts.mjs +2 -0
  12. package/dist/config-D-h0FACe.d.mts +1 -0
  13. package/dist/config-ad92ubCB-D0-dZUgA.d.mts +568 -0
  14. package/dist/config.d.mts +3 -0
  15. package/dist/config.mjs +9 -0
  16. package/dist/config.mjs.map +1 -0
  17. package/dist/deploy-D-h0FACe.d.mts +1 -0
  18. package/dist/deploy.d.mts +3 -0
  19. package/dist/deploy.mjs +305 -0
  20. package/dist/deploy.mjs.map +1 -0
  21. package/dist/dist-B0axxnBf.mjs +193 -0
  22. package/dist/dist-B0axxnBf.mjs.map +1 -0
  23. package/dist/graph-BmrUEdo9-6Oq1hSmS.mjs +688 -0
  24. package/dist/graph-BmrUEdo9-6Oq1hSmS.mjs.map +1 -0
  25. package/dist/graph-DXuL5tN6-BeAHOb0u.d.mts +18 -0
  26. package/dist/index-C1f1Aot7.d.mts +16 -0
  27. package/dist/index-Cy4C23u1.d.mts +32 -0
  28. package/dist/index.d.mts +4 -0
  29. package/dist/index.mjs +3 -0
  30. package/dist/nextjs-control.d.mts +14 -0
  31. package/dist/nextjs-control.mjs +105 -0
  32. package/dist/nextjs-control.mjs.map +1 -0
  33. package/dist/nextjs.d.mts +2 -0
  34. package/dist/nextjs.mjs +12 -0
  35. package/dist/nextjs.mjs.map +1 -0
  36. package/dist/node-control.d.mts +11 -0
  37. package/dist/node-control.mjs +142 -0
  38. package/dist/node-control.mjs.map +1 -0
  39. package/dist/node.d.mts +23 -0
  40. package/dist/node.mjs +12 -0
  41. package/dist/node.mjs.map +1 -0
  42. package/dist/report.d.mts +17 -0
  43. package/dist/report.mjs +79 -0
  44. package/dist/report.mjs.map +1 -0
  45. package/dist/rpc.d.mts +53 -0
  46. package/dist/rpc.mjs +184 -0
  47. package/dist/rpc.mjs.map +1 -0
  48. package/dist/testing.d.mts +23 -0
  49. package/dist/testing.mjs +45 -0
  50. package/dist/testing.mjs.map +1 -0
  51. package/package.json +69 -0
@@ -0,0 +1,688 @@
1
+ import { t as blindCast } from "./casts-Ci5rYYaR.mjs";
2
+ //#region ../../0-framework/1-core/core/dist/graph-BmrUEdo9.mjs
3
+ /** Thrown by Load when the graph is malformed. */
4
+ var LoadError = class extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = "LoadError";
8
+ }
9
+ };
10
+ /**
11
+ * Core model: node types and the factories that construct them, plain frozen
12
+ * data objects. A node's `extension` + `type` form its deploy-time registry key (ADR-0017).
13
+ */
14
+ const NODE = Symbol.for("prisma:node");
15
+ const SECRET_NEED = blindCast(Symbol.for("prisma:secret-need"));
16
+ const SECRET_SOURCE = blindCast(Symbol.for("prisma:secret-source"));
17
+ /** Declares a secret NEED. Nameless — the platform name is bound at the root via `envSecret`. */
18
+ function secret() {
19
+ return Object.freeze({
20
+ [SECRET_NEED]: true,
21
+ kind: "secret"
22
+ });
23
+ }
24
+ /** Builds an opaque secret source from a target-defined payload — the SPI a deploy target's own source constructor (e.g. `envSecret`) calls. Core forwards the source and never inspects the payload. */
25
+ function secretSource(payload) {
26
+ return Object.freeze({
27
+ [SECRET_SOURCE]: true,
28
+ payload
29
+ });
30
+ }
31
+ /** True if `value` is a secret source (an `envSecret` result or a forwarded ctx.secrets ref). */
32
+ function isSecretSource(value) {
33
+ return typeof value === "object" && value !== null && blindCast(value)[SECRET_SOURCE] === true;
34
+ }
35
+ const PROVISION_NEED = blindCast(Symbol.for("prisma:provision-need"));
36
+ /** Builds an opaque provisioning need — the declaring package's own brand plus whatever payload its provisioner reads back. */
37
+ function provisionNeed(brand, payload) {
38
+ return blindCast(Object.freeze({
39
+ [PROVISION_NEED]: true,
40
+ brand,
41
+ payload
42
+ }));
43
+ }
44
+ /** True if `value` is a provisioning need (a `provisionNeed()` result). */
45
+ function isProvisionNeed(value) {
46
+ return typeof value === "object" && value !== null && blindCast(value)[PROVISION_NEED] === true;
47
+ }
48
+ const PARAM_SOURCE = blindCast(Symbol.for("prisma:param-source"));
49
+ const PARAM_NEED = blindCast(Symbol.for("prisma:param-need"));
50
+ /** Builds an opaque param source from a target-defined payload — the SPI a deploy target's own source constructor (e.g. `envParam`) calls. Core forwards the source and never inspects the payload. */
51
+ function paramSource(payload) {
52
+ return Object.freeze({
53
+ [PARAM_SOURCE]: true,
54
+ payload
55
+ });
56
+ }
57
+ /** True if `value` is a param source (an `envParam` result or a forwarded ctx.params ref). */
58
+ function isParamSource(value) {
59
+ return typeof value === "object" && value !== null && blindCast(value)[PARAM_SOURCE] === true;
60
+ }
61
+ /** Declares a module param-forwarding NEED. Nameless — bound to a `ParamSource` by an enclosing scope and forwarded into a child's real param. */
62
+ function paramNeed() {
63
+ return Object.freeze({
64
+ [PARAM_NEED]: true,
65
+ kind: "param"
66
+ });
67
+ }
68
+ function requireType(type, factory) {
69
+ if (typeof type !== "string" || type.length === 0) throw new Error(`${factory}() requires a non-empty node type.`);
70
+ }
71
+ function requireName(name, factory) {
72
+ if (typeof name !== "string" || name.length === 0) throw new Error(`${factory}() requires a non-empty name.`);
73
+ }
74
+ function requireExtension(extension, factory) {
75
+ if (typeof extension !== "string" || extension.length === 0) throw new Error(`${factory}() requires a non-empty extension (the authoring extension's package name).`);
76
+ }
77
+ /**
78
+ * Config keys join address/input/param names with "_" and uppercase — an
79
+ * underscore inside a name would collide with that separator (e.g. param
80
+ * "db_url" vs input "db"'s param "url" both hitting env key "DB_URL").
81
+ */
82
+ function requireNoUnderscoreName(name, kind, factory) {
83
+ if (name.includes("_")) throw new Error(`${factory}() ${kind} name "${name}" may not contain "_" — config keys join names with "_" as the separator (e.g. an input "db"'s param "url" becomes env key "DB_URL"), so an underscore inside a name would collide with that separator.`);
84
+ }
85
+ function requireNoUnderscoreNames(names, kind, factory) {
86
+ for (const name of names) requireNoUnderscoreName(name, kind, factory);
87
+ }
88
+ function freezeParams(params) {
89
+ const frozen = {};
90
+ for (const [name, param] of Object.entries(params)) frozen[name] = Object.freeze({ ...param });
91
+ return Object.freeze(frozen);
92
+ }
93
+ function freezeSecrets(secrets) {
94
+ const frozen = {};
95
+ for (const [name, need] of Object.entries(secrets)) frozen[name] = Object.freeze({ ...need });
96
+ return blindCast(Object.freeze(frozen));
97
+ }
98
+ /** A frozen shallow copy that keeps the caller's declared type. */
99
+ function frozenShallowCopy(obj) {
100
+ return blindCast(Object.freeze({ ...obj }));
101
+ }
102
+ /**
103
+ * Seals a node instance after its constructor has assigned all fields — the
104
+ * last statement of a concrete node class's constructor. A free function, not
105
+ * a base-class method, so an instance stays structurally a plain frozen node.
106
+ */
107
+ function freezeNode(node) {
108
+ Object.freeze(node);
109
+ return node;
110
+ }
111
+ /**
112
+ * Everything `resource()` establishes, minus the freeze — an extension
113
+ * whose resource node carries extra fields extends this, assigns them, and
114
+ * calls `freezeNode(this)` as its constructor's last statement.
115
+ */
116
+ var ResourceNodeBase = class {
117
+ [NODE] = true;
118
+ kind = "resource";
119
+ name;
120
+ extension;
121
+ type;
122
+ provides;
123
+ constructor(def) {
124
+ requireName(def.name, "resource");
125
+ requireExtension(def.extension, "resource");
126
+ const provides = def.provides;
127
+ if (typeof provides !== "object" || provides === null || typeof provides.kind !== "string" || provides.kind.length === 0 || typeof provides.satisfies !== "function") throw new Error("resource() requires `provides` — the Contract this resource offers (a non-empty `kind` plus its `satisfies()`).");
128
+ this.name = def.name;
129
+ this.extension = def.extension;
130
+ this.type = provides.kind;
131
+ this.provides = provides;
132
+ }
133
+ };
134
+ /** The core leaf: exactly the base, frozen. */
135
+ var FrozenResourceNode = class extends ResourceNodeBase {
136
+ constructor(def) {
137
+ super(def);
138
+ freezeNode(this);
139
+ }
140
+ };
141
+ /**
142
+ * Constructs a branded, frozen Resource node — an identity plus the Contract
143
+ * it provides; the routing `type` is the contract's `kind`. Pure — nothing
144
+ * is provisioned until a module provisions it.
145
+ */
146
+ function resource(def) {
147
+ return new FrozenResourceNode(def);
148
+ }
149
+ /**
150
+ * Constructs a branded, frozen Service node — declarations only (inputs,
151
+ * params, build adapter, and the ports it exposes). Pure; carries no runtime behavior.
152
+ */
153
+ function service(def) {
154
+ requireName(def.name, "service");
155
+ requireExtension(def.extension, "service");
156
+ requireType(def.type, "service");
157
+ requireNoUnderscoreNames(Object.keys(def.inputs), "input", "service");
158
+ requireNoUnderscoreNames(Object.keys(def.params), "param", "service");
159
+ requireNoUnderscoreNames(Object.keys(def.secrets ?? {}), "secret", "service");
160
+ for (const slot of Object.keys(def.secrets ?? {})) if (Object.hasOwn(def.params, slot)) throw new Error(`service() secret slot "${slot}" collides with a param of the same name — a secret slot and a service param derive the same config key (COMPOSER_<addr>_${slot.toUpperCase()}); rename one.`);
161
+ return Object.freeze({
162
+ [NODE]: true,
163
+ kind: "service",
164
+ name: def.name,
165
+ extension: def.extension,
166
+ type: def.type,
167
+ inputs: frozenShallowCopy(def.inputs),
168
+ params: freezeParams(def.params),
169
+ secretSlots: freezeSecrets(def.secrets ?? blindCast({})),
170
+ build: Object.freeze({ ...def.build }),
171
+ expose: def.expose !== void 0 ? frozenShallowCopy(def.expose) : void 0
172
+ });
173
+ }
174
+ /**
175
+ * Constructs a branded, frozen DependencyEnd. `required` (if given) is the
176
+ * contract Load compares a wired ref against via `satisfies()`; an unnamed
177
+ * end's diagnostic `name` falls back to its `type`.
178
+ */
179
+ function dependency(def) {
180
+ requireType(def.type, "dependency");
181
+ requireNoUnderscoreNames(Object.keys(def.connection.params), "param", "dependency");
182
+ const connection = Object.freeze({
183
+ params: freezeParams(def.connection.params),
184
+ hydrate: def.connection.hydrate
185
+ });
186
+ return Object.freeze({
187
+ [NODE]: true,
188
+ kind: "dependency",
189
+ name: def.name !== void 0 && def.name.length > 0 ? def.name : def.type,
190
+ type: def.type,
191
+ connection,
192
+ required: def.required
193
+ });
194
+ }
195
+ /**
196
+ * Constructs a branded, frozen Module node. Construction is INERT — the body is
197
+ * wiring, not user code, and runs only when the module is Loaded.
198
+ */
199
+ function module(name, boundaryOrBody, maybeBody) {
200
+ requireName(name, "module");
201
+ const closedRoot = typeof boundaryOrBody === "function";
202
+ const boundary = closedRoot ? {} : boundaryOrBody;
203
+ const deps = frozenShallowCopy(boundary.deps ?? {});
204
+ const secretSlots = frozenShallowCopy(boundary.secrets ?? {});
205
+ const paramSlots = frozenShallowCopy(boundary.params ?? {});
206
+ const expose = frozenShallowCopy(boundary.expose ?? {});
207
+ const body = closedRoot ? (ctx) => {
208
+ boundaryOrBody(ctx);
209
+ return {};
210
+ } : maybeBody;
211
+ return Object.freeze({
212
+ [NODE]: true,
213
+ kind: "module",
214
+ name,
215
+ deps,
216
+ secretSlots,
217
+ paramSlots,
218
+ expose,
219
+ body
220
+ });
221
+ }
222
+ /**
223
+ * True if `value` was constructed by this module's factories. Checks the
224
+ * brand only, never a prototype — a graph may mix nodes from a different
225
+ * installed copy of core (dual-package hazard).
226
+ */
227
+ function isNode(value) {
228
+ return typeof value === "object" && value !== null && value[NODE] === true;
229
+ }
230
+ /**
231
+ * Stable topological sort: every edge's `from` precedes its `to` in the
232
+ * result. Ties (nodes with no ordering constraint between them) keep their
233
+ * relative order from `nodes` — so a graph already authored producer-first
234
+ * comes out byte-identical to its pre-sort layout; only a graph that
235
+ * genuinely needs reordering (e.g. a module wired via a forged ref pointing at a
236
+ * not-yet-provisioned producer) actually moves. A Kahn's-algorithm variant
237
+ * that always picks the ready node with the smallest original index. Edges
238
+ * whose endpoint falls outside `nodes` (e.g. a service-root's input edges
239
+ * targeting the root, which is appended separately) are ignored. Cycles
240
+ * cannot reach here: `assertDependencyDag` already rejects them for
241
+ * dependency edges, and input edges never cycle.
242
+ */
243
+ function topoSort(nodes, edges) {
244
+ const byId = new Map(nodes.map((n) => [n.id, n]));
245
+ const indexOf = new Map(nodes.map((n, i) => [n.id, i]));
246
+ const indegree = new Map(nodes.map((n) => [n.id, 0]));
247
+ const successors = /* @__PURE__ */ new Map();
248
+ for (const edge of edges) {
249
+ if (!byId.has(edge.from) || !byId.has(edge.to)) continue;
250
+ const targets = successors.get(edge.from) ?? [];
251
+ targets.push(edge.to);
252
+ successors.set(edge.from, targets);
253
+ indegree.set(edge.to, (indegree.get(edge.to) ?? 0) + 1);
254
+ }
255
+ const ready = new Set(nodes.filter((n) => indegree.get(n.id) === 0).map((n) => n.id));
256
+ const order = [];
257
+ while (ready.size > 0) {
258
+ let next;
259
+ let bestIndex = Number.POSITIVE_INFINITY;
260
+ for (const id of ready) {
261
+ const index = indexOf.get(id) ?? Number.POSITIVE_INFINITY;
262
+ if (index < bestIndex) {
263
+ bestIndex = index;
264
+ next = id;
265
+ }
266
+ }
267
+ if (next === void 0) break;
268
+ ready.delete(next);
269
+ order.push(next);
270
+ for (const target of successors.get(next) ?? []) {
271
+ const remaining = (indegree.get(target) ?? 0) - 1;
272
+ indegree.set(target, remaining);
273
+ if (remaining === 0) ready.add(target);
274
+ }
275
+ }
276
+ if (order.length !== nodes.length) throw new LoadError(`topological sort processed ${order.length} of ${nodes.length} nodes — the graph contains a cycle that slipped past the DAG validation.`);
277
+ return order.map((id) => byId.get(id)).filter((n) => n !== void 0);
278
+ }
279
+ function serviceInputs(service, serviceId) {
280
+ if (typeof service.inputs !== "object" || service.inputs === null) throw new LoadError(`Service "${serviceId}" has no inputs map.`);
281
+ const nodes = [];
282
+ const edges = [];
283
+ for (const [input, value] of Object.entries(service.inputs)) {
284
+ const kind = isNode(value) ? value.kind : void 0;
285
+ 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.`);
286
+ if (kind !== "dependency") throw new LoadError(`Input "${input}" of "${serviceId}" is not a branded dependency end (construct it with the dependency() factory).`);
287
+ if (value.type.length === 0) throw new LoadError(`Input "${input}" of "${serviceId}" has an empty node type.`);
288
+ const id = `${serviceId}.${input}`;
289
+ nodes.push({
290
+ id,
291
+ node: value
292
+ });
293
+ edges.push({
294
+ from: id,
295
+ to: serviceId,
296
+ input,
297
+ kind: "input"
298
+ });
299
+ }
300
+ return {
301
+ nodes,
302
+ edges
303
+ };
304
+ }
305
+ function loadService(root, rootId) {
306
+ 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.`);
307
+ const secretSlots = Object.keys(root.secretSlots);
308
+ if (secretSlots.length > 0) {
309
+ const names = secretSlots.map((k) => `"${k}"`).join(", ");
310
+ throw new LoadError(`Service "${rootId}" declares secret slot${secretSlots.length > 1 ? "s" : ""} ${names} but is being loaded directly — a lone service has no enclosing scope to bind them. Compose it inside a module that binds each with envSecret('NAME').`);
311
+ }
312
+ const rootGraphNode = {
313
+ id: rootId,
314
+ node: root
315
+ };
316
+ const { nodes, edges } = serviceInputs(root, rootId);
317
+ return {
318
+ root: rootGraphNode,
319
+ nodes: [...topoSort(nodes, edges), rootGraphNode],
320
+ edges,
321
+ secrets: [],
322
+ params: []
323
+ };
324
+ }
325
+ /**
326
+ * Builds the ref a provision() call hands back: the id (so a producer with no
327
+ * exposed ports — or an untyped slot — can still be wired wholesale) plus one
328
+ * ref-port per exposed contract, each the contract's own runtime value (so
329
+ * its `satisfies()` still works) tagged with the provider's id.
330
+ */
331
+ function refFor(id, service) {
332
+ const ports = {};
333
+ for (const [port, contract] of Object.entries(service.expose ?? {})) ports[port] = {
334
+ ...contract,
335
+ __providerId: id
336
+ };
337
+ return blindCast({
338
+ id,
339
+ ...ports
340
+ });
341
+ }
342
+ /**
343
+ * The resource variant of refFor: a resource has exactly one port — the
344
+ * contract it provides — flattened onto the ref itself, tagged with the
345
+ * provider id. `id` is written last so a hostile contract value cannot
346
+ * clobber it.
347
+ */
348
+ function refForResource(id, resource) {
349
+ return blindCast({
350
+ ...resource.provides,
351
+ __providerId: id,
352
+ id
353
+ });
354
+ }
355
+ /** A wired value's producer id: a ref-port's `__providerId`, or a bare ref's `id`. */
356
+ function producerIdOf(ref) {
357
+ if (typeof ref !== "object" || ref === null) return void 0;
358
+ if ("__providerId" in ref && typeof ref.__providerId === "string") return ref.__providerId;
359
+ if ("id" in ref && typeof ref.id === "string") return ref.id;
360
+ }
361
+ /**
362
+ * Brands each `ctx.inputs` entry with the input key it stands for (see
363
+ * flatten): diagnostic only — usage attribution relies on the per-key object
364
+ * identity the branding copy creates, never on reading this back.
365
+ */
366
+ const MODULE_INPUT_KEY = Symbol("prisma:module-input-key");
367
+ /** Same per-key identity trick as MODULE_INPUT_KEY, for the parallel `ctx.secrets` forwarding channel. */
368
+ const MODULE_SECRET_KEY = Symbol("prisma:module-secret-key");
369
+ /** Same per-key identity trick as MODULE_INPUT_KEY, for the parallel `ctx.params` forwarding channel. */
370
+ const MODULE_PARAM_KEY = Symbol("prisma:module-param-key");
371
+ /** Whether `ref` carries a callable `satisfies` that accepts `required` truthily. */
372
+ function satisfiesRequired(ref, required) {
373
+ return typeof ref === "object" && ref !== null && "satisfies" in ref && typeof ref.satisfies === "function" && ref.satisfies(required);
374
+ }
375
+ /**
376
+ * Checks one recorded `wiring` object against the Deps it was wired against:
377
+ * every named input exists and is a dependency slot, every referenced
378
+ * producer is a real (by-now-provisioned) address, and a wired ref whose
379
+ * slot declares a required contract must satisfy() it — no producer-kind
380
+ * branching, the contract alone determines validity, whether the producer is
381
+ * a service port or a resource. Shared by both provisioned kinds so a
382
+ * module-as-child gets exactly the checks a service gets, and run once per
383
+ * entry against the one `byId` shared by the whole recursive flatten, so a
384
+ * forwarded ref resolves through to its real producer address regardless of
385
+ * which ancestor scope provisioned it.
386
+ */
387
+ function validateWiring(pending, byId) {
388
+ const { deps, wiring, targetId, targetKind, enclosingModuleName } = pending;
389
+ for (const [input, ref] of Object.entries(wiring)) {
390
+ const declared = blindCast(deps[input]);
391
+ 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}.`);
392
+ const producerId = producerIdOf(ref);
393
+ const producer = producerId !== void 0 ? byId.get(producerId) : void 0;
394
+ 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}".`);
395
+ const required = declared.required;
396
+ if (required !== void 0 && !satisfiesRequired(ref, required)) throw new LoadError(`The deps for "${targetId}.${input}" do not satisfy the slot's required contract.`);
397
+ }
398
+ for (const [input, rawValue] of Object.entries(deps)) {
399
+ const value = blindCast(rawValue);
400
+ if (!isNode(value) || wiring[input] !== void 0) continue;
401
+ if (value.kind === "dependency") throw new LoadError(`Dependency input "${input}" of provisioned ${targetKind} "${targetId}" is not wired to a producer (module "${enclosingModuleName}").`);
402
+ }
403
+ }
404
+ /** The (unvalidated) edges a `wiring` object implies — one dependency edge per entry; `validateWiring` does the real checking. */
405
+ function wiringEdges(wiring, targetId) {
406
+ return Object.entries(wiring).map(([input, ref]) => ({
407
+ from: producerIdOf(ref) ?? "",
408
+ to: targetId,
409
+ input,
410
+ kind: "dependency"
411
+ }));
412
+ }
413
+ /**
414
+ * Checks the secrets wired into one provisioned child: every declared slot is
415
+ * bound to a real secret source (an `envSecret` or a forwarded ctx.secrets
416
+ * ref), and no wired key names a slot the child doesn't declare — the secret
417
+ * analog of `validateWiring`'s per-input checks, but resolved inline (a source
418
+ * carries its own name, so no whole-graph pass is needed).
419
+ */
420
+ function validateSecretBinding(child, id, secretWiring, enclosingModuleName) {
421
+ const { kind } = child;
422
+ for (const slot of Object.keys(child.secretSlots)) {
423
+ const bound = secretWiring[slot];
424
+ 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.`);
425
+ 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.`);
426
+ }
427
+ 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}").`);
428
+ }
429
+ /**
430
+ * Checks the params wired into a provisioned SERVICE: unlike a secret slot, a
431
+ * declared param is never required to be bound here — it may fall back to its
432
+ * own `default` (checked later, at `buildConfig`) — so this only rejects a
433
+ * wired key that names something other than a real declared param.
434
+ */
435
+ function validateServiceParamBinding(child, id, paramWiring, enclosingModuleName) {
436
+ 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}").`);
437
+ }
438
+ /**
439
+ * Checks the params wired into a provisioned MODULE: every declared
440
+ * param-forwarding slot must be bound to a real `ParamSource` — a slot has no
441
+ * schema of its own, so (unlike a service param) it cannot fall back to a
442
+ * literal or a default; the param analog of `validateSecretBinding`.
443
+ */
444
+ function validateParamNeedBinding(child, id, paramWiring, enclosingModuleName) {
445
+ for (const slot of Object.keys(child.paramSlots)) {
446
+ const bound = paramWiring[slot];
447
+ 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.`);
448
+ 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.`);
449
+ }
450
+ 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}").`);
451
+ }
452
+ /**
453
+ * Recursively flattens one module's body into the shared graph state and
454
+ * returns its resolved ModuleOutputs (one ref-port per expose key) for the
455
+ * caller (the enclosing provision() call, or Load itself for the root) to
456
+ * use. `address` is this module's OWN full address, or `undefined` for the root
457
+ * scope — its direct children then get bare (unprefixed) addresses, keeping
458
+ * a single-level module identical to before nesting existed. `wiring` supplies a
459
+ * resolved producer ref-port for each of this module's OWN declared deps (empty
460
+ * for the root, which may not declare any — see the root non-empty-deps
461
+ * check in loadModule). `nodes`, `edges`, `pending`, and `byId` are shared
462
+ * across the ENTIRE recursive flatten, not per scope — a nested module may
463
+ * forward in a producer provisioned by an ancestor scope, and it is the
464
+ * shared `byId` (keyed by full address) that lets that resolve.
465
+ */
466
+ function flatten(moduleNode, address, wiring, secretWiring, paramWiring, nodes, edges, pending, secretBindings, paramBindings, byId) {
467
+ const localIds = /* @__PURE__ */ new Set();
468
+ const used = /* @__PURE__ */ new Set();
469
+ const usedSecrets = /* @__PURE__ */ new Set();
470
+ const usedParams = /* @__PURE__ */ new Set();
471
+ const ctxInputs = {};
472
+ for (const key of Object.keys(moduleNode.deps)) {
473
+ const wired = wiring[key];
474
+ ctxInputs[key] = typeof wired === "object" && wired !== null ? {
475
+ ...wired,
476
+ [MODULE_INPUT_KEY]: key
477
+ } : wired;
478
+ }
479
+ const markUsed = (values) => {
480
+ for (const value of Object.values(values)) for (const key of Object.keys(ctxInputs)) if (value === ctxInputs[key]) used.add(key);
481
+ };
482
+ const ctxSecrets = {};
483
+ for (const key of Object.keys(moduleNode.secretSlots)) {
484
+ const bound = secretWiring[key];
485
+ ctxSecrets[key] = typeof bound === "object" && bound !== null ? {
486
+ ...bound,
487
+ [MODULE_SECRET_KEY]: key
488
+ } : bound;
489
+ }
490
+ const markSecretsUsed = (values) => {
491
+ for (const value of Object.values(values)) for (const key of Object.keys(ctxSecrets)) if (value === ctxSecrets[key]) usedSecrets.add(key);
492
+ };
493
+ const ctxParams = {};
494
+ for (const key of Object.keys(moduleNode.paramSlots)) {
495
+ const bound = paramWiring[key];
496
+ ctxParams[key] = typeof bound === "object" && bound !== null ? {
497
+ ...bound,
498
+ [MODULE_PARAM_KEY]: key
499
+ } : bound;
500
+ }
501
+ const markParamsUsed = (values) => {
502
+ for (const value of Object.values(values)) for (const key of Object.keys(ctxParams)) if (value === ctxParams[key]) usedParams.add(key);
503
+ };
504
+ const provision = (child, opts) => {
505
+ const id = opts?.id ?? child.name;
506
+ const provisionWiring = opts?.deps;
507
+ const provisionSecrets = opts?.secrets;
508
+ const provisionParams = opts?.params;
509
+ if (typeof id !== "string" || id.length === 0) throw new LoadError(`provision() requires a non-empty id (module "${moduleNode.name}").`);
510
+ if (id.includes("_") || id.includes(".")) throw new LoadError(`provision() id "${id}" (module "${moduleNode.name}") may not contain "_" or "." — "_" is the config-key separator and "." the node-id path separator; either inside an id collides with the joined form of other names.`);
511
+ if (localIds.has(id)) throw new LoadError(`Duplicate provision id "${id}" in module "${moduleNode.name}".`);
512
+ const untrusted = child;
513
+ 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).`);
514
+ localIds.add(id);
515
+ const fullAddress = address === void 0 ? id : `${address}.${id}`;
516
+ if (child.kind === "resource") {
517
+ if (provisionWiring !== void 0) throw new LoadError(`provision("${id}") received deps for a resource — a resource has no dependency slots to satisfy.`);
518
+ if (provisionSecrets !== void 0) throw new LoadError(`provision("${id}") received secrets for a resource — a resource has no secret slots to satisfy.`);
519
+ if (provisionParams !== void 0) throw new LoadError(`provision("${id}") received params for a resource — a resource has no params to satisfy.`);
520
+ if (child.type.length === 0) throw new LoadError(`provision("${id}") received a resource with an empty node type.`);
521
+ byId.set(fullAddress, child);
522
+ nodes.push({
523
+ id: fullAddress,
524
+ node: child
525
+ });
526
+ return refForResource(fullAddress, child);
527
+ }
528
+ const localWiring = { ...provisionWiring ?? {} };
529
+ markUsed(localWiring);
530
+ const localSecrets = { ...provisionSecrets ?? {} };
531
+ markSecretsUsed(localSecrets);
532
+ validateSecretBinding(child, id, localSecrets, moduleNode.name);
533
+ const localParams = { ...provisionParams ?? {} };
534
+ markParamsUsed(localParams);
535
+ if (child.kind === "service") {
536
+ for (const slot of Object.keys(child.secretSlots)) {
537
+ const bound = localSecrets[slot];
538
+ if (isSecretSource(bound)) secretBindings.push({
539
+ serviceAddress: fullAddress,
540
+ slot,
541
+ source: bound
542
+ });
543
+ }
544
+ validateServiceParamBinding(child, id, localParams, moduleNode.name);
545
+ for (const [slot, binding] of Object.entries(localParams)) paramBindings.push({
546
+ serviceAddress: fullAddress,
547
+ slot,
548
+ binding
549
+ });
550
+ const inputs = serviceInputs(child, fullAddress);
551
+ nodes.push(...inputs.nodes, {
552
+ id: fullAddress,
553
+ node: child
554
+ });
555
+ edges.push(...inputs.edges, ...wiringEdges(localWiring, fullAddress));
556
+ pending.push({
557
+ deps: child.inputs,
558
+ wiring: localWiring,
559
+ targetId: fullAddress,
560
+ targetKind: "service",
561
+ enclosingModuleName: moduleNode.name
562
+ });
563
+ byId.set(fullAddress, child);
564
+ return refFor(fullAddress, child);
565
+ }
566
+ validateParamNeedBinding(child, id, localParams, moduleNode.name);
567
+ edges.push(...wiringEdges(localWiring, fullAddress));
568
+ pending.push({
569
+ deps: child.deps,
570
+ wiring: localWiring,
571
+ targetId: fullAddress,
572
+ targetKind: "module",
573
+ enclosingModuleName: moduleNode.name
574
+ });
575
+ const childOutputs = flatten(child, fullAddress, localWiring, localSecrets, localParams, nodes, edges, pending, secretBindings, paramBindings, byId);
576
+ nodes.push({
577
+ id: fullAddress,
578
+ node: child
579
+ });
580
+ byId.set(fullAddress, child);
581
+ return blindCast({
582
+ id: fullAddress,
583
+ ...childOutputs
584
+ });
585
+ };
586
+ const ctx = blindCast({
587
+ inputs: ctxInputs,
588
+ secrets: ctxSecrets,
589
+ params: ctxParams,
590
+ provision: blindCast(provision)
591
+ });
592
+ const outputs = blindCast(moduleNode.body(ctx) ?? {});
593
+ markUsed(outputs);
594
+ 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.`);
595
+ 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.`);
596
+ 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.`);
597
+ for (const [key, contract] of Object.entries(moduleNode.expose)) {
598
+ const port = outputs[key];
599
+ if (port === void 0) throw new LoadError(`Module "${moduleNode.name}" declares expose "${key}" but its body did not return a port for it.`);
600
+ if (!satisfiesRequired(port, contract)) throw new LoadError(`Module "${moduleNode.name}"'s returned port for expose "${key}" does not satisfy its declared contract.`);
601
+ }
602
+ return outputs;
603
+ }
604
+ function loadModule(root, opts) {
605
+ const rootId = opts?.id ?? root.name;
606
+ const rootDepKeys = Object.keys(root.deps);
607
+ if (rootDepKeys.length > 0) {
608
+ const names = rootDepKeys.map((k) => `"${k}"`).join(", ");
609
+ 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.`);
610
+ }
611
+ const rootSecretKeys = Object.keys(root.secretSlots);
612
+ if (rootSecretKeys.length > 0) {
613
+ const names = rootSecretKeys.map((k) => `"${k}"`).join(", ");
614
+ 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.`);
615
+ }
616
+ const rootParamKeys = Object.keys(root.paramSlots);
617
+ if (rootParamKeys.length > 0) {
618
+ const names = rootParamKeys.map((k) => `"${k}"`).join(", ");
619
+ 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.`);
620
+ }
621
+ const nodes = [];
622
+ const edges = [];
623
+ const pending = [];
624
+ const secretBindings = [];
625
+ const paramBindings = [];
626
+ const byId = /* @__PURE__ */ new Map();
627
+ flatten(root, void 0, {}, {}, {}, nodes, edges, pending, secretBindings, paramBindings, byId);
628
+ for (const entry of pending) validateWiring(entry, byId);
629
+ assertDependencyDag(edges);
630
+ const rootGraphNode = {
631
+ id: rootId,
632
+ node: root
633
+ };
634
+ return {
635
+ root: rootGraphNode,
636
+ nodes: [...topoSort(nodes, edges), rootGraphNode],
637
+ edges,
638
+ secrets: secretBindings,
639
+ params: paramBindings
640
+ };
641
+ }
642
+ /**
643
+ * The dependency edges must form a DAG — a cycle means neither producer can
644
+ * deploy first. Resources take no wiring, so only service/module-to-service/module
645
+ * edges can ever participate in a cycle; no special-casing needed.
646
+ */
647
+ function assertDependencyDag(edges) {
648
+ const adjacency = /* @__PURE__ */ new Map();
649
+ for (const edge of edges) {
650
+ if (edge.kind !== "dependency") continue;
651
+ const targets = adjacency.get(edge.from) ?? [];
652
+ targets.push(edge.to);
653
+ adjacency.set(edge.from, targets);
654
+ }
655
+ const visiting = /* @__PURE__ */ new Set();
656
+ const done = /* @__PURE__ */ new Set();
657
+ const stack = [];
658
+ const visit = (id) => {
659
+ if (done.has(id)) return;
660
+ if (visiting.has(id)) throw new LoadError(`Dependency cycle: ${[...stack.slice(stack.indexOf(id)), id].join(" → ")} — no deploy order exists.`);
661
+ visiting.add(id);
662
+ stack.push(id);
663
+ for (const next of adjacency.get(id) ?? []) visit(next);
664
+ stack.pop();
665
+ visiting.delete(id);
666
+ done.add(id);
667
+ };
668
+ for (const id of adjacency.keys()) visit(id);
669
+ }
670
+ /**
671
+ * Builds the in-memory graph from a root node. A service root walks its own
672
+ * `inputs`; a module root executes its body (wiring, not user code — the
673
+ * designed exception to imports-run-nothing) and recursively flattens every
674
+ * module it provisions into one graph of hierarchical addresses. A malformed
675
+ * graph is a `LoadError` that names its fix; the individual validation rules
676
+ * live with `loadService` / `loadModule` and are covered by name in the Load
677
+ * tests. Executes nothing of the user's own code beyond module bodies.
678
+ */
679
+ function Load(root, opts) {
680
+ if (!isNode(root)) throw new LoadError("Load expects a branded service or module node (construct it with the service()/module() factories).");
681
+ if (root.kind === "module") return loadModule(root, opts);
682
+ if (root.kind === "service") return loadService(root, opts?.id ?? "root");
683
+ throw new LoadError("Load expects a service or module root (received another node kind).");
684
+ }
685
+ //#endregion
686
+ export { service as _, freezeNode as a, isProvisionNeed as c, paramNeed as d, paramSource as f, secretSource as g, secret as h, dependency as i, isSecretSource as l, resource as m, LoadError as n, isNode as o, provisionNeed as p, ResourceNodeBase as r, isParamSource as s, Load as t, module as u };
687
+
688
+ //# sourceMappingURL=graph-BmrUEdo9-6Oq1hSmS.mjs.map