@prisma/composer 0.1.0 → 0.2.0-dev.2
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-BPb3LCdH-CH7b8Khi.d.mts +411 -0
- package/dist/app-config-BPb3LCdH-CH7b8Khi.d.mts.map +1 -0
- package/dist/assertions.d.mts +1 -1
- package/dist/assertions.d.mts.map +1 -0
- package/dist/bin.mjs +166 -256
- package/dist/bin.mjs.map +1 -1
- package/dist/casts.d.mts +1 -1
- package/dist/casts.d.mts.map +1 -0
- package/dist/config-Cqu_-Hna.d.mts +1 -0
- package/dist/config.d.mts +3 -2
- package/dist/config.mjs +2 -1
- package/dist/config.mjs.map +1 -1
- package/dist/container-transport-DKmKg5JQ-DKWs0ubK.mjs +45 -0
- package/dist/container-transport-DKmKg5JQ-DKWs0ubK.mjs.map +1 -0
- package/dist/deploy-Cqu_-Hna.d.mts +1 -0
- package/dist/deploy.d.mts +3 -2
- package/dist/deploy.mjs +181 -23
- package/dist/deploy.mjs.map +1 -1
- package/dist/{dist-zBU8ASQW.mjs → dist-B0axxnBf.mjs} +16 -4
- package/dist/dist-B0axxnBf.mjs.map +1 -0
- package/dist/{graph-BYdCQKya-BI0njTow.mjs → graph-BmrUEdo9-6Oq1hSmS.mjs} +102 -9
- package/dist/graph-BmrUEdo9-6Oq1hSmS.mjs.map +1 -0
- package/dist/graph-DUSgjmzA-DhtkTqoz.d.mts +18 -0
- package/dist/graph-DUSgjmzA-DhtkTqoz.d.mts.map +1 -0
- package/dist/{config-ob5OhCSP-sP3GW3uu.d.mts → graph-types-BcbVhojU-C3fhxjPK.d.mts} +205 -114
- package/dist/graph-types-BcbVhojU-C3fhxjPK.d.mts.map +1 -0
- package/dist/{index-CZSc9drz.d.mts → index-BFBNz7Aw.d.mts} +5 -20
- package/dist/index-BFBNz7Aw.d.mts.map +1 -0
- package/dist/index.d.mts +4 -3
- package/dist/index.mjs +3 -3
- package/dist/{index-Dh4Zro0y.d.mts → nextjs-DLyeRR7M-DwnBjryZ.d.mts} +6 -5
- package/dist/nextjs-DLyeRR7M-DwnBjryZ.d.mts.map +1 -0
- package/dist/nextjs-control.d.mts +6 -5
- package/dist/nextjs-control.d.mts.map +1 -0
- package/dist/nextjs-control.mjs +7 -10
- package/dist/nextjs-control.mjs.map +1 -1
- package/dist/nextjs.d.mts +2 -2
- package/dist/nextjs.mjs.map +1 -1
- package/dist/node-control.d.mts +5 -3
- package/dist/node-control.d.mts.map +1 -0
- package/dist/node-control.mjs +96 -24
- package/dist/node-control.mjs.map +1 -1
- package/dist/node.d.mts +18 -5
- package/dist/node.d.mts.map +1 -0
- package/dist/node.mjs +2 -1
- package/dist/node.mjs.map +1 -1
- package/dist/report.d.mts +17 -0
- package/dist/report.d.mts.map +1 -0
- package/dist/report.mjs +79 -0
- package/dist/report.mjs.map +1 -0
- package/dist/service-rpc.d.mts +61 -0
- package/dist/service-rpc.d.mts.map +1 -0
- package/dist/service-rpc.mjs +353 -0
- package/dist/service-rpc.mjs.map +1 -0
- package/dist/testing.d.mts +2 -3
- package/dist/testing.d.mts.map +1 -0
- package/package.json +20 -18
- package/src/exports/assertions.ts +1 -0
- package/src/exports/casts.ts +1 -0
- package/src/exports/config.ts +1 -0
- package/src/exports/deploy.ts +1 -0
- package/src/exports/index.ts +1 -0
- package/src/exports/nextjs-control.ts +1 -0
- package/src/exports/nextjs.ts +2 -0
- package/src/exports/node-control.ts +1 -0
- package/src/exports/node.ts +2 -0
- package/src/exports/report.ts +1 -0
- package/src/exports/service-rpc.ts +1 -0
- package/src/exports/testing.ts +1 -0
- package/dist/app-config-BUqyK6N6-CVq3uvHF.d.mts +0 -188
- package/dist/config-BVVgDSdq.d.mts +0 -1
- package/dist/deploy-BVVgDSdq.d.mts +0 -1
- package/dist/dist-zBU8ASQW.mjs.map +0 -1
- package/dist/graph-BYdCQKya-BI0njTow.mjs.map +0 -1
- package/dist/rpc.d.mts +0 -43
- package/dist/rpc.mjs +0 -132
- package/dist/rpc.mjs.map +0 -1
- package/dist/tsdown.d.mts +0 -9
- package/dist/tsdown.mjs +0 -35
- package/dist/tsdown.mjs.map +0 -1
package/dist/bin.mjs
CHANGED
|
@@ -2,19 +2,9 @@
|
|
|
2
2
|
import { Cli, Command, Option, UsageError } from "clipanion";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path from "node:path";
|
|
5
|
-
import { spawnSync } from "node:child_process";
|
|
6
|
-
import * as Layer from "effect/Layer";
|
|
7
|
-
import { createManagementApiClient } from "@prisma/management-api-sdk";
|
|
8
|
-
import * as Context from "effect/Context";
|
|
9
|
-
import * as Effect from "effect/Effect";
|
|
10
|
-
import * as Redacted from "effect/Redacted";
|
|
11
|
-
import * as Config from "effect/Config";
|
|
12
|
-
import * as Data from "effect/Data";
|
|
13
|
-
import * as Provider from "alchemy/Provider";
|
|
14
|
-
import { Resource } from "alchemy";
|
|
15
|
-
import * as Schedule from "effect/Schedule";
|
|
16
5
|
import * as c12 from "c12";
|
|
17
6
|
import { pathToFileURL } from "node:url";
|
|
7
|
+
import { spawnSync } from "node:child_process";
|
|
18
8
|
//#region ../../0-framework/3-tooling/assemble/dist/index.mjs
|
|
19
9
|
/**
|
|
20
10
|
* A user-facing assembly failure with a message that already names the fix
|
|
@@ -31,49 +21,26 @@ var AssembleError = class extends Error {
|
|
|
31
21
|
}
|
|
32
22
|
};
|
|
33
23
|
/**
|
|
34
|
-
* The
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* patterns — it needs one general rule for what an assembler inlines into
|
|
39
|
-
* the wrapper build besides `@prisma/*` (already the assemblers' own
|
|
40
|
-
* default).
|
|
41
|
-
*
|
|
42
|
-
* The rule: inline everything except the runtime's own modules — `bun`,
|
|
43
|
-
* `bun:*` (e.g. bun:sqlite), and `node:*` builtins — which resolve inside
|
|
44
|
-
* the deploy VM at runtime and must never be bundled. Verified (both
|
|
45
|
-
* `@prisma/composer/node/control` and `@prisma/composer/nextjs/control` set
|
|
46
|
-
* `external: ['bun']` ahead of `noExternal` in their tsdown call, and tsdown/
|
|
47
|
-
* rolldown's explicit `external` wins over a `noExternal` match — proven by
|
|
48
|
-
* building an app's wrapper with this exact pattern: the
|
|
49
|
-
* `bun` import stays external while everything else, including the app's own
|
|
50
|
-
* workspace deps, gets inlined) — see this slice's final report for the
|
|
51
|
-
* storefront-auth wrapper proof (arktype + `@storefront-auth/auth/contract`
|
|
52
|
-
* inlined with no other file changes there).
|
|
53
|
-
*/
|
|
54
|
-
const INLINE_EVERYTHING_EXCEPT_RUNTIME_BUILTINS = [/^(?!bun$)(?!bun:)(?!node:).+/];
|
|
55
|
-
/**
|
|
56
|
-
* The registry route for one service's build: descriptor by
|
|
57
|
-
* `build.extension`, control by `build.type`, kind must be "build". The CLI's
|
|
58
|
-
* coverage validation reports the same misses earlier with the config fix;
|
|
59
|
-
* these errors are the backstop for programmatic callers.
|
|
24
|
+
* The registry route for one service's build: extension by
|
|
25
|
+
* `build.extension`, node descriptor by `build.type`, kind must be "build".
|
|
26
|
+
* The CLI's coverage validation reports the same misses earlier with the
|
|
27
|
+
* config fix; these errors are the backstop for programmatic callers.
|
|
60
28
|
*/
|
|
61
|
-
function
|
|
29
|
+
function buildDescriptorAssemble(config, node, address, cwd) {
|
|
62
30
|
const { extension, type } = node.build;
|
|
63
|
-
const
|
|
64
|
-
if (
|
|
65
|
-
const
|
|
66
|
-
if (
|
|
67
|
-
if (
|
|
68
|
-
return
|
|
31
|
+
const extensionDescriptor = config.extensions.find((candidate) => candidate.id === extension);
|
|
32
|
+
if (extensionDescriptor === void 0) throw new AssembleError(`No extension "${extension}" is configured (needed by service "${node.name}"'s build) — add it to prisma-composer.config.ts's \`extensions\`.`);
|
|
33
|
+
const nodeDescriptor = extensionDescriptor.nodes[type];
|
|
34
|
+
if (nodeDescriptor === void 0) throw new AssembleError(`Extension "${extension}" has no descriptor for build type "${type}" (known: ${Object.keys(extensionDescriptor.nodes).join(", ")}).`);
|
|
35
|
+
if (nodeDescriptor.kind !== "build") throw new AssembleError(`Extension "${extension}"'s descriptor for type "${type}" is a "${nodeDescriptor.kind}" descriptor — assembling a service build needs a "build" descriptor.`);
|
|
36
|
+
return nodeDescriptor.assemble({
|
|
69
37
|
build: node.build,
|
|
70
|
-
wrapperNoExternal: INLINE_EVERYTHING_EXCEPT_RUNTIME_BUILTINS,
|
|
71
38
|
address,
|
|
72
39
|
cwd
|
|
73
40
|
});
|
|
74
41
|
}
|
|
75
42
|
async function assembleServices(graph, config, cwd, run) {
|
|
76
|
-
const runAssembler = run ?? ((node, address, nodeCwd) =>
|
|
43
|
+
const runAssembler = run ?? ((node, address, nodeCwd) => buildDescriptorAssemble(config, node, address, nodeCwd));
|
|
77
44
|
const serviceNodes = graph.nodes.filter((n) => n.node.kind === "service");
|
|
78
45
|
if (serviceNodes.length === 0) throw new AssembleError("The loaded graph has no service to assemble.");
|
|
79
46
|
const bundles = {};
|
|
@@ -121,7 +88,7 @@ function blindCast(input) {
|
|
|
121
88
|
return input;
|
|
122
89
|
}
|
|
123
90
|
//#endregion
|
|
124
|
-
//#region ../../0-framework/1-core/core/dist/graph-
|
|
91
|
+
//#region ../../0-framework/1-core/core/dist/graph-BmrUEdo9.mjs
|
|
125
92
|
/** Thrown by Load when the graph is malformed. */
|
|
126
93
|
var LoadError = class extends Error {
|
|
127
94
|
constructor(message) {
|
|
@@ -139,6 +106,11 @@ const SECRET_SOURCE = blindCast(Symbol.for("prisma:secret-source"));
|
|
|
139
106
|
function isSecretSource(value) {
|
|
140
107
|
return typeof value === "object" && value !== null && blindCast(value)[SECRET_SOURCE] === true;
|
|
141
108
|
}
|
|
109
|
+
const PARAM_SOURCE = blindCast(Symbol.for("prisma:param-source"));
|
|
110
|
+
/** True if `value` is a param source (an `envParam` result or a forwarded ctx.params ref). */
|
|
111
|
+
function isParamSource(value) {
|
|
112
|
+
return typeof value === "object" && value !== null && blindCast(value)[PARAM_SOURCE] === true;
|
|
113
|
+
}
|
|
142
114
|
/**
|
|
143
115
|
* True if `value` was constructed by this module's factories. Checks the
|
|
144
116
|
* brand only, never a prototype — a graph may mix nodes from a different
|
|
@@ -238,7 +210,8 @@ function loadService(root, rootId) {
|
|
|
238
210
|
root: rootGraphNode,
|
|
239
211
|
nodes: [...topoSort(nodes, edges), rootGraphNode],
|
|
240
212
|
edges,
|
|
241
|
-
secrets: []
|
|
213
|
+
secrets: [],
|
|
214
|
+
params: []
|
|
242
215
|
};
|
|
243
216
|
}
|
|
244
217
|
/**
|
|
@@ -285,6 +258,8 @@ function producerIdOf(ref) {
|
|
|
285
258
|
const MODULE_INPUT_KEY = Symbol("prisma:module-input-key");
|
|
286
259
|
/** Same per-key identity trick as MODULE_INPUT_KEY, for the parallel `ctx.secrets` forwarding channel. */
|
|
287
260
|
const MODULE_SECRET_KEY = Symbol("prisma:module-secret-key");
|
|
261
|
+
/** Same per-key identity trick as MODULE_INPUT_KEY, for the parallel `ctx.params` forwarding channel. */
|
|
262
|
+
const MODULE_PARAM_KEY = Symbol("prisma:module-param-key");
|
|
288
263
|
/** Whether `ref` carries a callable `satisfies` that accepts `required` truthily. */
|
|
289
264
|
function satisfiesRequired(ref, required) {
|
|
290
265
|
return typeof ref === "object" && ref !== null && "satisfies" in ref && typeof ref.satisfies === "function" && ref.satisfies(required);
|
|
@@ -344,6 +319,29 @@ function validateSecretBinding(child, id, secretWiring, enclosingModuleName) {
|
|
|
344
319
|
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}").`);
|
|
345
320
|
}
|
|
346
321
|
/**
|
|
322
|
+
* Checks the params wired into a provisioned SERVICE: unlike a secret slot, a
|
|
323
|
+
* declared param is never required to be bound here — it may fall back to its
|
|
324
|
+
* own `default` (checked later, at `buildConfig`) — so this only rejects a
|
|
325
|
+
* wired key that names something other than a real declared param.
|
|
326
|
+
*/
|
|
327
|
+
function validateServiceParamBinding(child, id, paramWiring, enclosingModuleName) {
|
|
328
|
+
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}").`);
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Checks the params wired into a provisioned MODULE: every declared
|
|
332
|
+
* param-forwarding slot must be bound to a real `ParamSource` — a slot has no
|
|
333
|
+
* schema of its own, so (unlike a service param) it cannot fall back to a
|
|
334
|
+
* literal or a default; the param analog of `validateSecretBinding`.
|
|
335
|
+
*/
|
|
336
|
+
function validateParamNeedBinding(child, id, paramWiring, enclosingModuleName) {
|
|
337
|
+
for (const slot of Object.keys(child.paramSlots)) {
|
|
338
|
+
const bound = paramWiring[slot];
|
|
339
|
+
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.`);
|
|
340
|
+
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.`);
|
|
341
|
+
}
|
|
342
|
+
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}").`);
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
347
345
|
* Recursively flattens one module's body into the shared graph state and
|
|
348
346
|
* returns its resolved ModuleOutputs (one ref-port per expose key) for the
|
|
349
347
|
* caller (the enclosing provision() call, or Load itself for the root) to
|
|
@@ -357,10 +355,11 @@ function validateSecretBinding(child, id, secretWiring, enclosingModuleName) {
|
|
|
357
355
|
* forward in a producer provisioned by an ancestor scope, and it is the
|
|
358
356
|
* shared `byId` (keyed by full address) that lets that resolve.
|
|
359
357
|
*/
|
|
360
|
-
function flatten(moduleNode, address, wiring, secretWiring, nodes, edges, pending, secretBindings, byId) {
|
|
358
|
+
function flatten(moduleNode, address, wiring, secretWiring, paramWiring, nodes, edges, pending, secretBindings, paramBindings, byId) {
|
|
361
359
|
const localIds = /* @__PURE__ */ new Set();
|
|
362
360
|
const used = /* @__PURE__ */ new Set();
|
|
363
361
|
const usedSecrets = /* @__PURE__ */ new Set();
|
|
362
|
+
const usedParams = /* @__PURE__ */ new Set();
|
|
364
363
|
const ctxInputs = {};
|
|
365
364
|
for (const key of Object.keys(moduleNode.deps)) {
|
|
366
365
|
const wired = wiring[key];
|
|
@@ -383,10 +382,22 @@ function flatten(moduleNode, address, wiring, secretWiring, nodes, edges, pendin
|
|
|
383
382
|
const markSecretsUsed = (values) => {
|
|
384
383
|
for (const value of Object.values(values)) for (const key of Object.keys(ctxSecrets)) if (value === ctxSecrets[key]) usedSecrets.add(key);
|
|
385
384
|
};
|
|
385
|
+
const ctxParams = {};
|
|
386
|
+
for (const key of Object.keys(moduleNode.paramSlots)) {
|
|
387
|
+
const bound = paramWiring[key];
|
|
388
|
+
ctxParams[key] = typeof bound === "object" && bound !== null ? {
|
|
389
|
+
...bound,
|
|
390
|
+
[MODULE_PARAM_KEY]: key
|
|
391
|
+
} : bound;
|
|
392
|
+
}
|
|
393
|
+
const markParamsUsed = (values) => {
|
|
394
|
+
for (const value of Object.values(values)) for (const key of Object.keys(ctxParams)) if (value === ctxParams[key]) usedParams.add(key);
|
|
395
|
+
};
|
|
386
396
|
const provision = (child, opts) => {
|
|
387
397
|
const id = opts?.id ?? child.name;
|
|
388
398
|
const provisionWiring = opts?.deps;
|
|
389
399
|
const provisionSecrets = opts?.secrets;
|
|
400
|
+
const provisionParams = opts?.params;
|
|
390
401
|
if (typeof id !== "string" || id.length === 0) throw new LoadError(`provision() requires a non-empty id (module "${moduleNode.name}").`);
|
|
391
402
|
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.`);
|
|
392
403
|
if (localIds.has(id)) throw new LoadError(`Duplicate provision id "${id}" in module "${moduleNode.name}".`);
|
|
@@ -397,6 +408,7 @@ function flatten(moduleNode, address, wiring, secretWiring, nodes, edges, pendin
|
|
|
397
408
|
if (child.kind === "resource") {
|
|
398
409
|
if (provisionWiring !== void 0) throw new LoadError(`provision("${id}") received deps for a resource — a resource has no dependency slots to satisfy.`);
|
|
399
410
|
if (provisionSecrets !== void 0) throw new LoadError(`provision("${id}") received secrets for a resource — a resource has no secret slots to satisfy.`);
|
|
411
|
+
if (provisionParams !== void 0) throw new LoadError(`provision("${id}") received params for a resource — a resource has no params to satisfy.`);
|
|
400
412
|
if (child.type.length === 0) throw new LoadError(`provision("${id}") received a resource with an empty node type.`);
|
|
401
413
|
byId.set(fullAddress, child);
|
|
402
414
|
nodes.push({
|
|
@@ -410,6 +422,8 @@ function flatten(moduleNode, address, wiring, secretWiring, nodes, edges, pendin
|
|
|
410
422
|
const localSecrets = { ...provisionSecrets ?? {} };
|
|
411
423
|
markSecretsUsed(localSecrets);
|
|
412
424
|
validateSecretBinding(child, id, localSecrets, moduleNode.name);
|
|
425
|
+
const localParams = { ...provisionParams ?? {} };
|
|
426
|
+
markParamsUsed(localParams);
|
|
413
427
|
if (child.kind === "service") {
|
|
414
428
|
for (const slot of Object.keys(child.secretSlots)) {
|
|
415
429
|
const bound = localSecrets[slot];
|
|
@@ -419,6 +433,12 @@ function flatten(moduleNode, address, wiring, secretWiring, nodes, edges, pendin
|
|
|
419
433
|
source: bound
|
|
420
434
|
});
|
|
421
435
|
}
|
|
436
|
+
validateServiceParamBinding(child, id, localParams, moduleNode.name);
|
|
437
|
+
for (const [slot, binding] of Object.entries(localParams)) paramBindings.push({
|
|
438
|
+
serviceAddress: fullAddress,
|
|
439
|
+
slot,
|
|
440
|
+
binding
|
|
441
|
+
});
|
|
422
442
|
const inputs = serviceInputs(child, fullAddress);
|
|
423
443
|
nodes.push(...inputs.nodes, {
|
|
424
444
|
id: fullAddress,
|
|
@@ -435,6 +455,7 @@ function flatten(moduleNode, address, wiring, secretWiring, nodes, edges, pendin
|
|
|
435
455
|
byId.set(fullAddress, child);
|
|
436
456
|
return refFor(fullAddress, child);
|
|
437
457
|
}
|
|
458
|
+
validateParamNeedBinding(child, id, localParams, moduleNode.name);
|
|
438
459
|
edges.push(...wiringEdges(localWiring, fullAddress));
|
|
439
460
|
pending.push({
|
|
440
461
|
deps: child.deps,
|
|
@@ -443,7 +464,7 @@ function flatten(moduleNode, address, wiring, secretWiring, nodes, edges, pendin
|
|
|
443
464
|
targetKind: "module",
|
|
444
465
|
enclosingModuleName: moduleNode.name
|
|
445
466
|
});
|
|
446
|
-
const childOutputs = flatten(child, fullAddress, localWiring, localSecrets, nodes, edges, pending, secretBindings, byId);
|
|
467
|
+
const childOutputs = flatten(child, fullAddress, localWiring, localSecrets, localParams, nodes, edges, pending, secretBindings, paramBindings, byId);
|
|
447
468
|
nodes.push({
|
|
448
469
|
id: fullAddress,
|
|
449
470
|
node: child
|
|
@@ -457,12 +478,14 @@ function flatten(moduleNode, address, wiring, secretWiring, nodes, edges, pendin
|
|
|
457
478
|
const ctx = blindCast({
|
|
458
479
|
inputs: ctxInputs,
|
|
459
480
|
secrets: ctxSecrets,
|
|
481
|
+
params: ctxParams,
|
|
460
482
|
provision: blindCast(provision)
|
|
461
483
|
});
|
|
462
484
|
const outputs = blindCast(moduleNode.body(ctx) ?? {});
|
|
463
485
|
markUsed(outputs);
|
|
464
486
|
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.`);
|
|
465
487
|
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.`);
|
|
488
|
+
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.`);
|
|
466
489
|
for (const [key, contract] of Object.entries(moduleNode.expose)) {
|
|
467
490
|
const port = outputs[key];
|
|
468
491
|
if (port === void 0) throw new LoadError(`Module "${moduleNode.name}" declares expose "${key}" but its body did not return a port for it.`);
|
|
@@ -482,12 +505,18 @@ function loadModule(root, opts) {
|
|
|
482
505
|
const names = rootSecretKeys.map((k) => `"${k}"`).join(", ");
|
|
483
506
|
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.`);
|
|
484
507
|
}
|
|
508
|
+
const rootParamKeys = Object.keys(root.paramSlots);
|
|
509
|
+
if (rootParamKeys.length > 0) {
|
|
510
|
+
const names = rootParamKeys.map((k) => `"${k}"`).join(", ");
|
|
511
|
+
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.`);
|
|
512
|
+
}
|
|
485
513
|
const nodes = [];
|
|
486
514
|
const edges = [];
|
|
487
515
|
const pending = [];
|
|
488
516
|
const secretBindings = [];
|
|
517
|
+
const paramBindings = [];
|
|
489
518
|
const byId = /* @__PURE__ */ new Map();
|
|
490
|
-
flatten(root, void 0, {}, {}, nodes, edges, pending, secretBindings, byId);
|
|
519
|
+
flatten(root, void 0, {}, {}, {}, nodes, edges, pending, secretBindings, paramBindings, byId);
|
|
491
520
|
for (const entry of pending) validateWiring(entry, byId);
|
|
492
521
|
assertDependencyDag(edges);
|
|
493
522
|
const rootGraphNode = {
|
|
@@ -498,7 +527,8 @@ function loadModule(root, opts) {
|
|
|
498
527
|
root: rootGraphNode,
|
|
499
528
|
nodes: [...topoSort(nodes, edges), rootGraphNode],
|
|
500
529
|
edges,
|
|
501
|
-
secrets: secretBindings
|
|
530
|
+
secrets: secretBindings,
|
|
531
|
+
params: paramBindings
|
|
502
532
|
};
|
|
503
533
|
}
|
|
504
534
|
/**
|
|
@@ -545,145 +575,34 @@ function Load(root, opts) {
|
|
|
545
575
|
throw new LoadError("Load expects a service or module root (received another node kind).");
|
|
546
576
|
}
|
|
547
577
|
//#endregion
|
|
548
|
-
//#region ../../
|
|
549
|
-
/**
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
}));
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
*/
|
|
563
|
-
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
try: f,
|
|
572
|
-
catch: (cause) => new PrismaApiError({
|
|
573
|
-
status: 0,
|
|
574
|
-
message: String(cause)
|
|
575
|
-
})
|
|
576
|
-
});
|
|
577
|
-
const fail = (r) => Effect.fail(new PrismaApiError({
|
|
578
|
-
status: r.response.status,
|
|
579
|
-
message: JSON.stringify(r.error)
|
|
580
|
-
}));
|
|
581
|
-
/** Unwrap `data`, failing on any API error. Preserves the SDK's response type. */
|
|
582
|
-
const call = (f) => attempt(f).pipe(Effect.flatMap((r) => r.error !== void 0 || r.data === void 0 ? fail(r) : Effect.succeed(r.data)));
|
|
583
|
-
/** Fire-and-forget a call, tolerating a 404 (already deleted). */
|
|
584
|
-
const callVoid = (f) => attempt(f).pipe(Effect.flatMap((r) => r.response.status === 404 || r.error === void 0 ? Effect.void : fail(r)));
|
|
585
|
-
Schedule.both(Schedule.exponential("2 seconds", 2), Schedule.during("5 minutes"));
|
|
586
|
-
Resource("Prisma.ComputeService");
|
|
587
|
-
Resource("Prisma.Deployment");
|
|
588
|
-
Resource("Prisma.EnvironmentVariable");
|
|
589
|
-
Resource("Prisma.Connection");
|
|
590
|
-
Resource("Prisma.Database");
|
|
591
|
-
Resource("Prisma.Project");
|
|
592
|
-
//#endregion
|
|
593
|
-
//#region ../../1-prisma-cloud/0-lowering/lowering/dist/index.mjs
|
|
594
|
-
/** Raised with `ensure: false` when the app's Project (or a named stage's Branch) doesn't exist. */
|
|
595
|
-
var ContainerNotFoundError = class extends Data.TaggedError("ContainerNotFoundError") {};
|
|
596
|
-
const listAllProjects = (client) => Effect.gen(function* () {
|
|
597
|
-
const projects = [];
|
|
598
|
-
let cursor;
|
|
599
|
-
for (;;) {
|
|
600
|
-
const query = cursor === void 0 ? {} : { cursor };
|
|
601
|
-
const page = yield* call(() => client.GET("/v1/projects", { params: { query } }));
|
|
602
|
-
projects.push(...page.data);
|
|
603
|
-
if (!page.pagination.hasMore || page.pagination.nextCursor === null) break;
|
|
604
|
-
cursor = page.pagination.nextCursor;
|
|
578
|
+
//#region ../../0-framework/1-core/core/dist/container-transport-DKmKg5JQ.mjs
|
|
579
|
+
/** '@prisma/composer-prisma-cloud' → 'PRISMA_COMPOSER_CONTAINER_PRISMA_COMPOSER_PRISMA_CLOUD' */
|
|
580
|
+
function containerEnvVarName(extensionId) {
|
|
581
|
+
return `PRISMA_COMPOSER_CONTAINER_${extensionId.toUpperCase().replace(/[^A-Z0-9]+/g, "_").replace(/^_+|_+$/g, "")}`;
|
|
582
|
+
}
|
|
583
|
+
function collisionError(a, b, varName) {
|
|
584
|
+
return /* @__PURE__ */ new Error(`Extension ids "${a}" and "${b}" both mangle to the container transport variable "${varName}" — rename one of the extensions.`);
|
|
585
|
+
}
|
|
586
|
+
function emptySerializeError(extensionId) {
|
|
587
|
+
return /* @__PURE__ */ new Error(`Extension "${extensionId}"'s container instance serialized to an empty string — ContainerInstance.serialize() must return a non-empty string.`);
|
|
588
|
+
}
|
|
589
|
+
/** The env entries the CLI sets on the alchemy process: `{ [containerEnvVarName(id)]: instance.serialize() }` for every resolved instance. */
|
|
590
|
+
function containerEnv(instances) {
|
|
591
|
+
const env = {};
|
|
592
|
+
const ownerByVarName = /* @__PURE__ */ new Map();
|
|
593
|
+
for (const [extensionId, instance] of instances) {
|
|
594
|
+
const varName = containerEnvVarName(extensionId);
|
|
595
|
+
const owner = ownerByVarName.get(varName);
|
|
596
|
+
if (owner !== void 0) throw collisionError(owner, extensionId, varName);
|
|
597
|
+
ownerByVarName.set(varName, extensionId);
|
|
598
|
+
const serialized = instance.serialize();
|
|
599
|
+
if (serialized.length === 0) throw emptySerializeError(extensionId);
|
|
600
|
+
env[varName] = serialized;
|
|
605
601
|
}
|
|
606
|
-
return
|
|
607
|
-
}
|
|
608
|
-
/**
|
|
609
|
-
* Workspace ids circulate in two shapes: `wksp_`-prefixed and bare. Compare
|
|
610
|
-
* bare-to-bare so a `wksp_`-prefixed API id still matches a bare configured
|
|
611
|
-
* one (the same normalization `state/bootstrap.ts` applies to the same
|
|
612
|
-
* `/v1/projects` listing).
|
|
613
|
-
*/
|
|
614
|
-
const bareWorkspaceId = (id) => id.startsWith("wksp_") ? id.slice(5) : id;
|
|
615
|
-
/**
|
|
616
|
-
* Finds the app's Project by name in the workspace — PDP allows duplicate
|
|
617
|
-
* project names, so more than one can match; the oldest wins. Creates one
|
|
618
|
-
* if none match, unless `ensure` is `false` (find-only — `destroy`), in
|
|
619
|
-
* which case an absent Project fails with `ContainerNotFoundError`. No
|
|
620
|
-
* ownership marker and no `--project` override (both deferred — see
|
|
621
|
-
* ADR-0019).
|
|
622
|
-
*/
|
|
623
|
-
const resolveProject = (client, workspaceId, appName, ensure) => Effect.gen(function* () {
|
|
624
|
-
const oldest = (yield* listAllProjects(client)).filter((p) => bareWorkspaceId(p.workspace.id) === bareWorkspaceId(workspaceId) && p.name === appName).sort((a, b) => a.createdAt.localeCompare(b.createdAt))[0];
|
|
625
|
-
if (oldest !== void 0) return oldest.id;
|
|
626
|
-
if (!ensure) return yield* Effect.fail(new ContainerNotFoundError({ appName }));
|
|
627
|
-
return (yield* call(() => client.POST("/v1/projects", { body: {
|
|
628
|
-
name: appName,
|
|
629
|
-
workspaceId
|
|
630
|
-
} }))).data.id;
|
|
631
|
-
});
|
|
632
|
-
const findBranchId = (client, projectId, gitName) => call(() => client.GET("/v1/projects/{projectId}/branches", { params: {
|
|
633
|
-
path: { projectId },
|
|
634
|
-
query: { gitName }
|
|
635
|
-
} })).pipe(Effect.map((page) => page.data[0]?.id));
|
|
636
|
-
/**
|
|
637
|
-
* Finds the stage's Branch by its exact `gitName`, creating it if absent
|
|
638
|
-
* unless `ensure` is `false` (find-only — `destroy`), in which case an
|
|
639
|
-
* absent Branch fails with `ContainerNotFoundError`. The Management API has
|
|
640
|
-
* no server-side "create-or-return" idempotency (`POST
|
|
641
|
-
* /v1/projects/:id/branches` 409s on a duplicate `gitName`, with no request
|
|
642
|
-
* field to make that a no-op), so idempotency is client-side: observe
|
|
643
|
-
* first, and on a racing 409 from create, re-observe rather than fail.
|
|
644
|
-
*/
|
|
645
|
-
const resolveBranch = (client, projectId, gitName, appName, ensure) => Effect.gen(function* () {
|
|
646
|
-
const existing = yield* findBranchId(client, projectId, gitName);
|
|
647
|
-
if (existing !== void 0) return existing;
|
|
648
|
-
if (!ensure) return yield* Effect.fail(new ContainerNotFoundError({
|
|
649
|
-
appName,
|
|
650
|
-
stage: gitName
|
|
651
|
-
}));
|
|
652
|
-
return yield* call(() => client.POST("/v1/projects/{projectId}/branches", {
|
|
653
|
-
params: { path: { projectId } },
|
|
654
|
-
body: { gitName }
|
|
655
|
-
})).pipe(Effect.map((r) => r.data.id), Effect.catch((err) => err.status === 409 ? findBranchId(client, projectId, gitName).pipe(Effect.flatMap((id) => id === void 0 ? Effect.fail(err) : Effect.succeed(id))) : Effect.fail(err)));
|
|
656
|
-
});
|
|
657
|
-
/**
|
|
658
|
-
* Resolves the two containers a stage's deploy runs into (ADR-0019): the
|
|
659
|
-
* app's **Project**, found-or-created by name, and — for a named stage
|
|
660
|
-
* only — its **Branch**, found-or-created by `gitName`. The default stage
|
|
661
|
-
* (no `stage`) creates no Branch; `branchId` is omitted. With `ensure:
|
|
662
|
-
* false` (`destroy`), nothing is created — an absent Project or Branch
|
|
663
|
-
* fails with `ContainerNotFoundError` instead.
|
|
664
|
-
*/
|
|
665
|
-
const resolveContainer = (opts) => Effect.gen(function* () {
|
|
666
|
-
const client = yield* ManagementClient;
|
|
667
|
-
const ensure = opts.ensure ?? true;
|
|
668
|
-
const projectId = yield* resolveProject(client, opts.workspaceId, opts.appName, ensure);
|
|
669
|
-
if (opts.stage === void 0) return { projectId };
|
|
670
|
-
return {
|
|
671
|
-
projectId,
|
|
672
|
-
branchId: yield* resolveBranch(client, projectId, opts.stage, opts.appName, ensure)
|
|
673
|
-
};
|
|
674
|
-
});
|
|
675
|
-
/**
|
|
676
|
-
* Soft-deletes a Branch. Tolerates a 404 (already gone). The API refuses if
|
|
677
|
-
* the Branch still has live members or is the production/default Branch —
|
|
678
|
-
* that surfaces as a `PrismaApiError`.
|
|
679
|
-
*/
|
|
680
|
-
const deleteBranch = (branchId) => Effect.gen(function* () {
|
|
681
|
-
const client = yield* ManagementClient;
|
|
682
|
-
yield* callVoid(() => client.DELETE("/v1/branches/{branchId}", { params: { path: { branchId } } }));
|
|
683
|
-
});
|
|
684
|
-
Provider.ProviderCollection()("Prisma");
|
|
602
|
+
return env;
|
|
603
|
+
}
|
|
685
604
|
//#endregion
|
|
686
|
-
//#region ../../0-framework/3-tooling/cli/dist/cli-
|
|
605
|
+
//#region ../../0-framework/3-tooling/cli/dist/cli-Bc6_i0ls.mjs
|
|
687
606
|
/**
|
|
688
607
|
* A user-facing failure with a message that already names the fix (deploy-cli.md
|
|
689
608
|
* § Error surface). `bin.ts` catches this — and any other Error, including
|
|
@@ -695,59 +614,6 @@ var CliError = class extends Error {
|
|
|
695
614
|
this.name = "CliError";
|
|
696
615
|
}
|
|
697
616
|
};
|
|
698
|
-
/**
|
|
699
|
-
* Pipeline pre-stack step: resolves the app's Project + (named stage) Branch
|
|
700
|
-
* via `@internal/lowering`'s `resolveContainer`, before the generated stack file
|
|
701
|
-
* runs — `deploy` creates-if-absent, `destroy` finds only.
|
|
702
|
-
*/
|
|
703
|
-
/** Validates `stage` as a git ref name via `git check-ref-format` — no silent normalization. */
|
|
704
|
-
function validateStageName(stage) {
|
|
705
|
-
const result = spawnSync("git", ["check-ref-format", `refs/heads/${stage}`], { stdio: "ignore" });
|
|
706
|
-
if (result.error) throw new CliError(`git is required to validate --stage "${stage}" (git check-ref-format): ${result.error.message}.`);
|
|
707
|
-
if (result.status !== 0) throw new CliError(`Invalid --stage "${stage}": must be a valid git ref name (git check-ref-format rejected "refs/heads/${stage}").`);
|
|
708
|
-
}
|
|
709
|
-
async function ensureContainers(input, deps) {
|
|
710
|
-
const env = input.env ?? process.env;
|
|
711
|
-
const workspaceId = env["PRISMA_WORKSPACE_ID"];
|
|
712
|
-
if (workspaceId === void 0 || workspaceId.length === 0) throw new CliError("environment variable PRISMA_WORKSPACE_ID is required.");
|
|
713
|
-
if (deps?.client === void 0 && (env["PRISMA_SERVICE_TOKEN"] ?? "").length === 0) throw new CliError("environment variable PRISMA_SERVICE_TOKEN is required.");
|
|
714
|
-
if (input.stage !== void 0) validateStageName(input.stage);
|
|
715
|
-
const program = resolveContainer({
|
|
716
|
-
workspaceId,
|
|
717
|
-
appName: input.appName,
|
|
718
|
-
...input.stage !== void 0 ? { stage: input.stage } : {},
|
|
719
|
-
ensure: input.command === "deploy"
|
|
720
|
-
}).pipe(Effect.map((c) => ({
|
|
721
|
-
ok: true,
|
|
722
|
-
container: c
|
|
723
|
-
})), Effect.catchTag("ContainerNotFoundError", (e) => Effect.succeed({
|
|
724
|
-
ok: false,
|
|
725
|
-
message: `Nothing deployed for ${e.appName}${e.stage ? `/${e.stage}` : ""} — deploy it first.`
|
|
726
|
-
})), Effect.catchTag("PrismaApiError", (e) => Effect.succeed({
|
|
727
|
-
ok: false,
|
|
728
|
-
message: `Prisma Management API error resolving containers: ${e.message}.`
|
|
729
|
-
})));
|
|
730
|
-
const provided = deps?.client !== void 0 ? program.pipe(Effect.provideService(ManagementClient, deps.client)) : program.pipe(Effect.provide(layer().pipe(Layer.provide(fromEnv()))));
|
|
731
|
-
const outcome = await Effect.runPromise(provided);
|
|
732
|
-
if (!outcome.ok) throw new CliError(outcome.message);
|
|
733
|
-
return outcome.container;
|
|
734
|
-
}
|
|
735
|
-
/**
|
|
736
|
-
* Soft-deletes a named stage's Branch after a successful `alchemy destroy`
|
|
737
|
-
* has removed its members (spec §10) — the Management API refuses to delete
|
|
738
|
-
* a Branch that still has live members.
|
|
739
|
-
*/
|
|
740
|
-
async function deleteStageBranch(input, deps) {
|
|
741
|
-
const env = input.env ?? process.env;
|
|
742
|
-
if (deps?.client === void 0 && (env["PRISMA_SERVICE_TOKEN"] ?? "").length === 0) throw new CliError("environment variable PRISMA_SERVICE_TOKEN is required.");
|
|
743
|
-
const program = deleteBranch(input.branchId).pipe(Effect.map(() => ({ ok: true })), Effect.catchTag("PrismaApiError", (e) => Effect.succeed({
|
|
744
|
-
ok: false,
|
|
745
|
-
message: `Failed to delete the stage Branch: ${e.message}.`
|
|
746
|
-
})));
|
|
747
|
-
const provided = deps?.client !== void 0 ? program.pipe(Effect.provideService(ManagementClient, deps.client)) : program.pipe(Effect.provide(layer().pipe(Layer.provide(fromEnv()))));
|
|
748
|
-
const outcome = await Effect.runPromise(provided);
|
|
749
|
-
if (!outcome.ok) throw new CliError(outcome.message);
|
|
750
|
-
}
|
|
751
617
|
/** Pipeline step 6: writes a regenerated-every-run, independently runnable stack module at `.prisma-composer/alchemy.run.ts`. */
|
|
752
618
|
const GENERATED_DIR = ".prisma-composer";
|
|
753
619
|
const GENERATED_FILE = "alchemy.run.ts";
|
|
@@ -768,6 +634,7 @@ function renderOptions(input) {
|
|
|
768
634
|
lines.push(" bundles: {");
|
|
769
635
|
for (const [id, bundle] of Object.entries(input.assembled.bundles)) lines.push(` ${quote(id)}: ${renderBundle(bundle)},`);
|
|
770
636
|
lines.push(" },");
|
|
637
|
+
lines.push(" report: deploymentReport,");
|
|
771
638
|
return lines.join("\n");
|
|
772
639
|
}
|
|
773
640
|
/** Renders the stack module's source (tests assert on it without touching disk) — uses `//` headers, not a block comment, since a cwd path with a star-slash could close one early. */
|
|
@@ -782,6 +649,7 @@ function renderStackFile(input) {
|
|
|
782
649
|
//
|
|
783
650
|
// bisects a CLI bug from an Alchemy bug (deploy-cli.md § Implementation decisions).
|
|
784
651
|
import { lower } from '@prisma/composer/deploy';
|
|
652
|
+
import { deploymentReport } from '@prisma/composer/report';
|
|
785
653
|
import config from ${quote(configImport)};
|
|
786
654
|
import app from ${quote(appImport)};
|
|
787
655
|
|
|
@@ -848,7 +716,8 @@ function validateConfigShape(loaded, configPath) {
|
|
|
848
716
|
if (seen.has(id)) throw new CliError(`${CONFIG_FILENAME}: extension "${id}" is listed more than once in \`extensions\`.`);
|
|
849
717
|
seen.add(id);
|
|
850
718
|
}
|
|
851
|
-
|
|
719
|
+
const state = loaded["state"];
|
|
720
|
+
if (!isRecord(state) || typeof state["extension"] !== "string" || typeof state["create"] !== "function") throw fieldError("state", "must be a state descriptor (e.g. prismaState())");
|
|
852
721
|
return blindCast(loaded);
|
|
853
722
|
}
|
|
854
723
|
/**
|
|
@@ -907,7 +776,7 @@ function resolveAlchemyBin(startDir) {
|
|
|
907
776
|
dir = parent;
|
|
908
777
|
}
|
|
909
778
|
}
|
|
910
|
-
/** Runs `alchemy deploy|destroy <stack file> --yes [--stage <stage>]`, inheriting stdio + env, plus
|
|
779
|
+
/** Runs `alchemy deploy|destroy <stack file> --yes [--stage <stage>]`, inheriting stdio + env, plus every extension's resolved container. */
|
|
911
780
|
function runAlchemy(input) {
|
|
912
781
|
const bin = resolveAlchemyBin(input.cwd);
|
|
913
782
|
const args = [
|
|
@@ -921,8 +790,7 @@ function runAlchemy(input) {
|
|
|
921
790
|
stdio: "inherit",
|
|
922
791
|
env: {
|
|
923
792
|
...input.env ?? process.env,
|
|
924
|
-
|
|
925
|
-
...input.branchId !== void 0 ? { PRISMA_BRANCH_ID: input.branchId } : {}
|
|
793
|
+
...input.containerEnv
|
|
926
794
|
}
|
|
927
795
|
});
|
|
928
796
|
if (result.error !== void 0) throw result.error;
|
|
@@ -948,6 +816,12 @@ function validateRegistryCoverage(graph, config) {
|
|
|
948
816
|
lookup(extensions, node.build.extension, node.build.type, "build", `service node "${id}"'s build descriptor`);
|
|
949
817
|
}
|
|
950
818
|
}
|
|
819
|
+
/** A stage name must be a valid git ref (deploy-cli.md) — checked via `git check-ref-format`, never silently normalized. Runs before anything platform-specific. */
|
|
820
|
+
function validateStageName(stage) {
|
|
821
|
+
const result = spawnSync("git", ["check-ref-format", `refs/heads/${stage}`], { stdio: "ignore" });
|
|
822
|
+
if (result.error) throw new CliError(`git is required to validate --stage "${stage}" (git check-ref-format): ${result.error.message}.`);
|
|
823
|
+
if (result.status !== 0) throw new CliError(`Invalid --stage "${stage}": must be a valid git ref name (git check-ref-format rejected "refs/heads/${stage}").`);
|
|
824
|
+
}
|
|
951
825
|
/**
|
|
952
826
|
* Argument parsing (clipanion — prisma-next's CLI idiom, see
|
|
953
827
|
* prisma-next/packages/1-framework/3-tooling/cli/src/migration-cli.ts) +
|
|
@@ -1043,6 +917,7 @@ async function run(argv, deps = {}) {
|
|
|
1043
917
|
throw error;
|
|
1044
918
|
}
|
|
1045
919
|
const stage = effectiveStage(args);
|
|
920
|
+
if (stage !== void 0) validateStageName(stage);
|
|
1046
921
|
const cwd = process.cwd();
|
|
1047
922
|
if (args.command === "destroy") warnIfNoLocalDeployState(cwd);
|
|
1048
923
|
const resolvedEntryPath = path.resolve(cwd, args.entry);
|
|
@@ -1062,18 +937,32 @@ async function run(argv, deps = {}) {
|
|
|
1062
937
|
if (args.command === "destroy" && error instanceof Error) throw new CliError(`${error.message}\n\ndestroy evaluates the same stack program as deploy, which packages the built artifacts — so the app must be built first. Run the build, then retry the destroy.`);
|
|
1063
938
|
throw error;
|
|
1064
939
|
}
|
|
1065
|
-
const
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
940
|
+
const containers = /* @__PURE__ */ new Map();
|
|
941
|
+
for (const extension of config.extensions) {
|
|
942
|
+
if (extension.container === void 0) continue;
|
|
943
|
+
try {
|
|
944
|
+
if (args.command === "deploy") containers.set(extension.id, await extension.container.ensure({
|
|
945
|
+
appName: name,
|
|
946
|
+
stage
|
|
947
|
+
}));
|
|
948
|
+
else {
|
|
949
|
+
const instance = await extension.container.locate({
|
|
950
|
+
appName: name,
|
|
951
|
+
stage
|
|
952
|
+
});
|
|
953
|
+
if (instance === void 0) throw new CliError(`Nothing deployed for ${name}${stage !== void 0 ? `/${stage}` : ""} — deploy it first.`);
|
|
954
|
+
containers.set(extension.id, instance);
|
|
955
|
+
}
|
|
956
|
+
} catch (error) {
|
|
957
|
+
throw error instanceof CliError ? error : new CliError(error instanceof Error ? error.message : String(error));
|
|
958
|
+
}
|
|
959
|
+
}
|
|
1070
960
|
if (args.command === "deploy") for (const extension of config.extensions) {
|
|
1071
961
|
if (extension.preflight === void 0) continue;
|
|
1072
962
|
try {
|
|
1073
963
|
await extension.preflight({
|
|
1074
964
|
graph,
|
|
1075
|
-
|
|
1076
|
-
branchId,
|
|
965
|
+
container: containers.get(extension.id),
|
|
1077
966
|
stage
|
|
1078
967
|
});
|
|
1079
968
|
} catch (error) {
|
|
@@ -1093,15 +982,36 @@ async function run(argv, deps = {}) {
|
|
|
1093
982
|
stackFileRelativePath: GENERATED_STACK_RELATIVE_PATH,
|
|
1094
983
|
cwd,
|
|
1095
984
|
stage,
|
|
1096
|
-
|
|
1097
|
-
...branchId !== void 0 ? { branchId } : {}
|
|
985
|
+
containerEnv: containerEnv(containers)
|
|
1098
986
|
});
|
|
1099
987
|
if (status !== 0) {
|
|
1100
988
|
console.error(`\nGenerated stack file: ${stackPath}`);
|
|
1101
989
|
console.error(`Run \`alchemy ${args.command} ${GENERATED_STACK_RELATIVE_PATH} --yes\` from ${cwd} to reproduce this directly.`);
|
|
1102
990
|
return status;
|
|
1103
991
|
}
|
|
1104
|
-
if (args.command === "destroy"
|
|
992
|
+
if (args.command === "destroy") {
|
|
993
|
+
for (const extension of config.extensions) {
|
|
994
|
+
if (extension.teardown === void 0) continue;
|
|
995
|
+
try {
|
|
996
|
+
await extension.teardown({
|
|
997
|
+
container: containers.get(extension.id),
|
|
998
|
+
stage
|
|
999
|
+
});
|
|
1000
|
+
} catch (error) {
|
|
1001
|
+
throw error instanceof CliError ? error : new CliError(error instanceof Error ? error.message : String(error));
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
for (const extension of config.extensions) {
|
|
1005
|
+
if (extension.container === void 0) continue;
|
|
1006
|
+
const instance = containers.get(extension.id);
|
|
1007
|
+
if (instance === void 0) continue;
|
|
1008
|
+
try {
|
|
1009
|
+
await extension.container.remove(instance);
|
|
1010
|
+
} catch (error) {
|
|
1011
|
+
throw error instanceof CliError ? error : new CliError(error instanceof Error ? error.message : String(error));
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1105
1015
|
return status;
|
|
1106
1016
|
} catch (error) {
|
|
1107
1017
|
console.error(`\nGenerated stack file: ${stackPath}`);
|