@prisma/composer 0.1.0-dev.18 → 0.1.0-dev.3
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-DJdU4ubR-BwioNK8j.d.mts → app-config-5mXxjaEl-D2Q6MU5g.d.mts} +19 -105
- package/dist/assertions.d.mts +1 -1
- package/dist/assertions.mjs.map +1 -1
- package/dist/bin.mjs +266 -81
- package/dist/bin.mjs.map +1 -1
- package/dist/casts-Ci5rYYaR.mjs.map +1 -1
- package/dist/casts.d.mts +1 -1
- package/dist/config-C23NciC8.d.mts +1 -0
- package/dist/config.d.mts +3 -3
- package/dist/config.mjs +1 -2
- package/dist/config.mjs.map +1 -1
- package/dist/deploy-C23NciC8.d.mts +1 -0
- package/dist/deploy.d.mts +2 -2
- package/dist/deploy.mjs +5 -12
- package/dist/deploy.mjs.map +1 -1
- package/dist/dist-B0axxnBf.mjs.map +1 -1
- package/dist/{graph-B7NcPiOr-aSUOCGTH.d.mts → graph-D383dfW2-Cepin3Om.d.mts} +3 -3
- package/dist/{graph-types-BgT9UEdm-Bz-_OcJH.d.mts → graph-types-COu3ss99-CrJcsbDJ.d.mts} +4 -4
- package/dist/{index-B2DJ5CN4.d.mts → index-8wU5wpMV.d.mts} +3 -3
- package/dist/{nextjs-DLyeRR7M-B9ukbB2L.d.mts → index-DIyo-rxT.d.mts} +5 -5
- package/dist/index.d.mts +3 -3
- package/dist/nextjs-control.d.mts +5 -5
- 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 +4 -4
- package/dist/node-control.mjs.map +1 -1
- package/dist/node.d.mts +4 -4
- package/dist/node.mjs.map +1 -1
- package/dist/report.d.mts +3 -3
- package/dist/report.mjs.map +1 -1
- package/dist/{service-rpc.d.mts → rpc.d.mts} +11 -19
- package/dist/rpc.mjs +184 -0
- package/dist/rpc.mjs.map +1 -0
- package/dist/testing.d.mts +2 -2
- package/dist/testing.mjs.map +1 -1
- package/package.json +14 -14
- package/dist/config-ByZICgry.d.mts +0 -1
- package/dist/container-transport-DKmKg5JQ-DKWs0ubK.mjs +0 -45
- package/dist/container-transport-DKmKg5JQ-DKWs0ubK.mjs.map +0 -1
- package/dist/deploy-ByZICgry.d.mts +0 -1
- package/dist/service-rpc.mjs +0 -353
- package/dist/service-rpc.mjs.map +0 -1
|
@@ -1,75 +1,12 @@
|
|
|
1
|
-
import { M as ResourceNode, V as ServiceNode, d as Graph, k as ProvisionNeed, n as Config, t as BuildAdapter, x as NodeId, y as ModuleNode } from "./graph-types-
|
|
2
|
-
import "./graph-
|
|
1
|
+
import { M as ResourceNode, V as ServiceNode, d as Graph, k as ProvisionNeed, n as Config, t as BuildAdapter, x as NodeId, y as ModuleNode } from "./graph-types-COu3ss99-CrJcsbDJ.mjs";
|
|
2
|
+
import "./graph-D383dfW2-Cepin3Om.mjs";
|
|
3
3
|
import * as Alchemy from "alchemy";
|
|
4
4
|
import { Input, StackServices } from "alchemy";
|
|
5
5
|
import * as Effect from "effect/Effect";
|
|
6
6
|
import * as Layer from "effect/Layer";
|
|
7
7
|
import { State } from "alchemy/State/State";
|
|
8
|
-
//#region ../../0-framework/1-core/core/dist/app-config-
|
|
9
|
-
//#region src/
|
|
10
|
-
/**
|
|
11
|
-
* Carries resolved containers from the CLI process into the alchemy process
|
|
12
|
-
* (ADR-0037). A deploy runs as two processes: the CLI resolves each
|
|
13
|
-
* extension's containers, then spawns `alchemy`, which re-imports the config
|
|
14
|
-
* from scratch and needs those containers back — and env vars are the only
|
|
15
|
-
* channel between the two. So the CLI writes each instance's `serialize()`
|
|
16
|
-
* output into one env var per extension, and in the alchemy process
|
|
17
|
-
* `deserializeContainers` reads each var back through the same extension's
|
|
18
|
-
* descriptor. The framework owns the vars; it never reads their contents.
|
|
19
|
-
*/
|
|
20
|
-
/** The key an extension resolves a container from: which app, which stage. */
|
|
21
|
-
interface LocateContainerInput {
|
|
22
|
-
/** The application name (root node's name, or `--name`). */
|
|
23
|
-
readonly appName: string;
|
|
24
|
-
/** The named stage, or `undefined` for the default (production) stage. */
|
|
25
|
-
readonly stage: string | undefined;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* One resolved container. The framework sees only this interface; the
|
|
29
|
-
* extension that produced the instance narrows it back to its own concrete
|
|
30
|
-
* type wherever the framework hands it back (ADR-0037).
|
|
31
|
-
*/
|
|
32
|
-
interface ContainerInstance {
|
|
33
|
-
readonly input: LocateContainerInput;
|
|
34
|
-
/** Serialize to a non-empty string for the process transport above. The format is the extension's own; only its `deserialize` reads it. */
|
|
35
|
-
serialize(): string;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* The platform containers an app deploys into, as one lifecycle. `I` is
|
|
39
|
-
* the extension's own instance type — the same descriptor produces and
|
|
40
|
-
* consumes it, so the extension gets full typing internally while the
|
|
41
|
-
* framework stores the erased form. METHOD SYNTAX REQUIRED on all four
|
|
42
|
-
* members: the erased assignment into ExtensionDescriptor compiles only
|
|
43
|
-
* through method bivariance; property-arrow members are checked
|
|
44
|
-
* contravariantly and the assignment fails (same rule as
|
|
45
|
-
* ServiceLowering<P, S> — ADR-0033).
|
|
46
|
-
*/
|
|
47
|
-
interface ContainerDescriptor<I extends ContainerInstance = ContainerInstance> {
|
|
48
|
-
/** Resolve the container for (appName, stage), creating anything absent. Called by `deploy`. */
|
|
49
|
-
ensure(input: LocateContainerInput): Promise<I>;
|
|
50
|
-
/** Find the container for (appName, stage); `undefined` when nothing exists. Called by `destroy` — never creates. */
|
|
51
|
-
locate(input: LocateContainerInput): Promise<I | undefined>;
|
|
52
|
-
/** Remove the container after a successful destroy, after every extension's `teardown` has run. Failure policy is the extension's. */
|
|
53
|
-
remove(instance: I): Promise<void>;
|
|
54
|
-
/** Reconstruct an instance from its own `serialize()` output — the far end of the framework's parent→child transport. */
|
|
55
|
-
deserialize(serialized: string): I;
|
|
56
|
-
}
|
|
57
|
-
/** '@prisma/composer-prisma-cloud' → 'PRISMA_COMPOSER_CONTAINER_PRISMA_COMPOSER_PRISMA_CLOUD' */
|
|
58
|
-
declare function containerEnvVarName(extensionId: string): string;
|
|
59
|
-
/** The env entries the CLI sets on the alchemy process: `{ [containerEnvVarName(id)]: instance.serialize() }` for every resolved instance. */
|
|
60
|
-
declare function containerEnv(instances: ReadonlyMap<string, ContainerInstance>): Record<string, string>;
|
|
61
|
-
/** The slice of `PrismaAppConfig.extensions` this module needs — kept narrow so this shared-plane module never imports the control-plane `ExtensionDescriptor`/`PrismaAppConfig` types (ADR-0028's plane split). */
|
|
62
|
-
interface ContainerTransportExtension {
|
|
63
|
-
readonly id: string;
|
|
64
|
-
readonly container?: ContainerDescriptor;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* The alchemy-process side: for each extension with a container descriptor
|
|
68
|
-
* whose var is present in `env`, call its deserialize. Absent var → no entry.
|
|
69
|
-
*/
|
|
70
|
-
declare function deserializeContainers(extensions: readonly ContainerTransportExtension[], env: Readonly<Record<string, string | undefined>>): ReadonlyMap<string, ContainerInstance>;
|
|
71
|
-
//#endregion
|
|
72
|
-
//#region src/control/deploy.d.ts
|
|
8
|
+
//#region ../../0-framework/1-core/core/dist/app-config-5mXxjaEl.d.mts
|
|
9
|
+
//#region src/exports/deploy.d.ts
|
|
73
10
|
/** The Layer shape every Alchemy state store must satisfy — what `LowerOptions.state` and `PrismaAppConfig.state` both traffic in. */
|
|
74
11
|
type AlchemyStateLayer = Layer.Layer<State, never, StackServices>;
|
|
75
12
|
/**
|
|
@@ -155,13 +92,6 @@ interface LowerContext {
|
|
|
155
92
|
* it with its own type guard.
|
|
156
93
|
*/
|
|
157
94
|
readonly application: unknown;
|
|
158
|
-
/**
|
|
159
|
-
* The owning extension's resolved container, deserialized from the
|
|
160
|
-
* framework transport; core never reads it — the extension narrows it
|
|
161
|
-
* with its own type guard. `undefined` when the extension declares no
|
|
162
|
-
* container descriptor.
|
|
163
|
-
*/
|
|
164
|
-
readonly container: ContainerInstance | undefined;
|
|
165
95
|
/** Already-lowered deps (topo order). */
|
|
166
96
|
readonly lowered: ReadonlyMap<NodeId, Outputs>;
|
|
167
97
|
/** Every provisioned param value minted this lowering, keyed by edge id (ADR-0031). */
|
|
@@ -289,12 +219,10 @@ declare function joinDeployment(graph: Graph, entries: readonly {
|
|
|
289
219
|
}[]): readonly DeployedNode[];
|
|
290
220
|
/**
|
|
291
221
|
* The state-layer precedence a deploy resolves to: an explicit opts.state
|
|
292
|
-
* always wins; failing that, the config's own (required) state
|
|
293
|
-
*
|
|
294
|
-
* that extension declared none). A pure function so the precedence is
|
|
295
|
-
* testable without booting Alchemy.
|
|
222
|
+
* always wins; failing that, the config's own (required) state. A pure
|
|
223
|
+
* function so the precedence is testable without booting Alchemy.
|
|
296
224
|
*/
|
|
297
|
-
declare function resolveStateLayer(opts: LowerOptions, config: PrismaAppConfig
|
|
225
|
+
declare function resolveStateLayer(opts: LowerOptions, config: PrismaAppConfig): AlchemyStateLayer;
|
|
298
226
|
/**
|
|
299
227
|
* All configured extensions' providers merged, config array order — an
|
|
300
228
|
* extension without `providers` is skipped; no used-extensions-only
|
|
@@ -313,7 +241,7 @@ declare function lowering(root: ModuleNode, config: PrismaAppConfig, opts: Lower
|
|
|
313
241
|
*/
|
|
314
242
|
declare function lower(root: ModuleNode, config: PrismaAppConfig, opts: LowerOptions): Effect.Effect<Alchemy.CompiledStack<undefined, any>, import("effect/Config").ConfigError, never>;
|
|
315
243
|
//#endregion
|
|
316
|
-
//#region src/
|
|
244
|
+
//#region src/exports/app-config.d.ts
|
|
317
245
|
/**
|
|
318
246
|
* One extension's control-plane registry: everything the deploy pipeline may
|
|
319
247
|
* look up for a node whose `extension` field names this package. `nodes` is
|
|
@@ -347,38 +275,24 @@ interface ExtensionDescriptor {
|
|
|
347
275
|
* fail the command handles that itself. Async: it talks to the platform.
|
|
348
276
|
*/
|
|
349
277
|
readonly teardown?: (input: TeardownInput) => Promise<void>;
|
|
350
|
-
/**
|
|
351
|
-
* The extension's container lifecycle, when its platform has containers
|
|
352
|
-
* (ADR-0038). The CLI resolves containers after assembly and before any
|
|
353
|
-
* stack file or Alchemy run (deploy ensures, destroy locates); the
|
|
354
|
-
* resolved instance reaches the alchemy process through the env transport
|
|
355
|
-
* in container-transport.ts.
|
|
356
|
-
*/
|
|
357
|
-
readonly container?: ContainerDescriptor;
|
|
358
|
-
}
|
|
359
|
-
/**
|
|
360
|
-
* The deploy's one state store. It names its owning extension so core knows
|
|
361
|
-
* whose resolved container to pass into `create` (ADR-0038).
|
|
362
|
-
*/
|
|
363
|
-
interface StateDescriptor {
|
|
364
|
-
/** The owning extension's id — matched against `ExtensionDescriptor.id`. */
|
|
365
|
-
readonly extension: string;
|
|
366
|
-
/** Build the state layer. `container` is the owning extension's resolved instance; `undefined` when it declared no container descriptor. */
|
|
367
|
-
create(container: ContainerInstance | undefined): AlchemyStateLayer;
|
|
368
278
|
}
|
|
369
279
|
/** The resolved deploy context handed to an extension's `preflight` hook. */
|
|
370
280
|
interface PreflightInput {
|
|
371
281
|
/** The loaded application graph — the manifest of prerequisites is read from it (`provisionManifest`). */
|
|
372
282
|
readonly graph: Graph;
|
|
373
|
-
/** The
|
|
374
|
-
readonly
|
|
283
|
+
/** The resolved Prisma Cloud Project id. */
|
|
284
|
+
readonly projectId: string;
|
|
285
|
+
/** The resolved Branch id for a named stage; `undefined` for the default (production) stage. */
|
|
286
|
+
readonly branchId: string | undefined;
|
|
375
287
|
/** The stage name (`--stage`), or `undefined` for the default stage — for diagnostics/scope. */
|
|
376
288
|
readonly stage: string | undefined;
|
|
377
289
|
}
|
|
378
290
|
/** The resolved destroy context handed to an extension's `teardown` hook. */
|
|
379
291
|
interface TeardownInput {
|
|
380
|
-
/** The
|
|
381
|
-
readonly
|
|
292
|
+
/** The resolved Prisma Cloud Project id. */
|
|
293
|
+
readonly projectId: string;
|
|
294
|
+
/** The resolved Branch id for a named stage; `undefined` for the default (production) stage. */
|
|
295
|
+
readonly branchId: string | undefined;
|
|
382
296
|
/** The stage name (`--stage`), or `undefined` for the default stage — for diagnostics/scope. */
|
|
383
297
|
readonly stage: string | undefined;
|
|
384
298
|
}
|
|
@@ -402,10 +316,10 @@ type NodeDescriptor = ({
|
|
|
402
316
|
*/
|
|
403
317
|
interface PrismaAppConfig {
|
|
404
318
|
readonly extensions: ExtensionDescriptor[];
|
|
405
|
-
readonly state:
|
|
319
|
+
readonly state: () => AlchemyStateLayer;
|
|
406
320
|
}
|
|
407
321
|
/** Typed identity — exists so `prisma-composer.config.ts` gets checked against PrismaAppConfig where it is written. */
|
|
408
322
|
declare function defineConfig(config: PrismaAppConfig): PrismaAppConfig;
|
|
409
323
|
//#endregion
|
|
410
|
-
export {
|
|
411
|
-
//# sourceMappingURL=app-config-
|
|
324
|
+
export { resolveStateLayer as A, TeardownInput as C, lower as D, joinDeployment as E, lowering as O, ServiceLowering as S, defineConfig as T, PackageInput as _, Bundle as a, ProvisionEdge as b, DeploymentResult as c, LowerError as d, LowerOptions as f, Outputs as g, NodeDescriptor as h, AssembleInput as i, mergedProviders as k, ExtensionDescriptor as l, Lowering as m, ApplicationDescriptor as n, DeployedEntity as o, LoweredResult as p, Artifact as r, DeployedNode as s, AlchemyStateLayer as t, LowerContext as u, PreflightInput as v, buildConfig as w, ProvisionerDescriptor as x, PrismaAppConfig as y };
|
|
325
|
+
//# sourceMappingURL=app-config-5mXxjaEl-D2Q6MU5g.d.mts.map
|
package/dist/assertions.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region ../../0-framework/0-foundation/foundation/dist/assertions.d.mts
|
|
2
|
-
//#region src/assertions.d.ts
|
|
2
|
+
//#region src/exports/assertions.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Asserts that a value is defined (not null or undefined).
|
|
5
5
|
* Use for invariants where the value should always exist at runtime.
|
package/dist/assertions.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assertions.mjs","names":[],"sources":["../../../0-framework/0-foundation/foundation/dist/assertions.mjs"],"sourcesContent":["//#region src/assertions.ts\n/**\n* Asserts that a value is defined (not null or undefined).\n* Use for invariants where the value should always exist at runtime.\n*\n* @throws Error if value is null or undefined\n*\n* @example\n* ```typescript\n* const port = config.ports[name];\n* assertDefined(port, `Port \"${name}\" not found`);\n* // port is now narrowed to non-nullable\n* ```\n*/\nfunction assertDefined(value, message) {\n\tif (value === null || value === void 0) throw new Error(message);\n}\n/**\n* Asserts that a condition is true.\n* Use for invariants that should always hold at runtime.\n*\n* @throws Error if condition is false\n*\n* @example\n* ```typescript\n* invariant(edges.length > 0, 'A wired module must have at least one connection');\n* ```\n*/\nfunction invariant(condition, message) {\n\tif (!condition) throw new Error(message);\n}\n//#endregion\nexport { assertDefined, invariant };\n\n//# sourceMappingURL=assertions.mjs.map"],"mappings":";;;;;;;;;;;;;;AAcA,SAAS,cAAc,OAAO,SAAS;CACtC,IAAI,UAAU,QAAQ,UAAU,KAAK,GAAG,MAAM,IAAI,MAAM,OAAO;AAChE;;;;;;;;;;;;AAYA,SAAS,UAAU,WAAW,SAAS;CACtC,IAAI,CAAC,WAAW,MAAM,IAAI,MAAM,OAAO;AACxC"}
|
|
1
|
+
{"version":3,"file":"assertions.mjs","names":[],"sources":["../../../0-framework/0-foundation/foundation/dist/assertions.mjs"],"sourcesContent":["//#region src/exports/assertions.ts\n/**\n* Asserts that a value is defined (not null or undefined).\n* Use for invariants where the value should always exist at runtime.\n*\n* @throws Error if value is null or undefined\n*\n* @example\n* ```typescript\n* const port = config.ports[name];\n* assertDefined(port, `Port \"${name}\" not found`);\n* // port is now narrowed to non-nullable\n* ```\n*/\nfunction assertDefined(value, message) {\n\tif (value === null || value === void 0) throw new Error(message);\n}\n/**\n* Asserts that a condition is true.\n* Use for invariants that should always hold at runtime.\n*\n* @throws Error if condition is false\n*\n* @example\n* ```typescript\n* invariant(edges.length > 0, 'A wired module must have at least one connection');\n* ```\n*/\nfunction invariant(condition, message) {\n\tif (!condition) throw new Error(message);\n}\n//#endregion\nexport { assertDefined, invariant };\n\n//# sourceMappingURL=assertions.mjs.map"],"mappings":";;;;;;;;;;;;;;AAcA,SAAS,cAAc,OAAO,SAAS;CACtC,IAAI,UAAU,QAAQ,UAAU,KAAK,GAAG,MAAM,IAAI,MAAM,OAAO;AAChE;;;;;;;;;;;;AAYA,SAAS,UAAU,WAAW,SAAS;CACtC,IAAI,CAAC,WAAW,MAAM,IAAI,MAAM,OAAO;AACxC"}
|
package/dist/bin.mjs
CHANGED
|
@@ -2,9 +2,19 @@
|
|
|
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";
|
|
5
16
|
import * as c12 from "c12";
|
|
6
17
|
import { pathToFileURL } from "node:url";
|
|
7
|
-
import { spawnSync } from "node:child_process";
|
|
8
18
|
//#region ../../0-framework/3-tooling/assemble/dist/index.mjs
|
|
9
19
|
/**
|
|
10
20
|
* A user-facing assembly failure with a message that already names the fix
|
|
@@ -575,34 +585,155 @@ function Load(root, opts) {
|
|
|
575
585
|
throw new LoadError("Load expects a service or module root (received another node kind).");
|
|
576
586
|
}
|
|
577
587
|
//#endregion
|
|
578
|
-
//#region ../../
|
|
579
|
-
/**
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
588
|
+
//#region ../../1-prisma-cloud/0-lowering/lowering/dist/http-CxGdfSAP.mjs
|
|
589
|
+
/**
|
|
590
|
+
* The Prisma service token used to authenticate Management API calls. Kept
|
|
591
|
+
* as a Redacted value so it never lands in logs or error output.
|
|
592
|
+
*/
|
|
593
|
+
var PrismaCredentials = class extends Context.Service()("PrismaCredentials") {};
|
|
594
|
+
/** Resolve the token from the `PRISMA_SERVICE_TOKEN` environment variable. */
|
|
595
|
+
const fromEnv = () => Layer.effect(PrismaCredentials, Effect.gen(function* () {
|
|
596
|
+
return { token: yield* Config.redacted("PRISMA_SERVICE_TOKEN") };
|
|
597
|
+
}));
|
|
598
|
+
/**
|
|
599
|
+
* The typed Prisma Management API client, built once from the resolved
|
|
600
|
+
* credentials. Providers yield this in their outer Effect and call it inside
|
|
601
|
+
* `reconcile` / `delete`.
|
|
602
|
+
*/
|
|
603
|
+
var ManagementClient = class extends Context.Service()("PrismaManagementClient") {};
|
|
604
|
+
const layer = () => Layer.effect(ManagementClient, Effect.gen(function* () {
|
|
605
|
+
const { token } = yield* PrismaCredentials;
|
|
606
|
+
return createManagementApiClient({ token: Redacted.value(token) });
|
|
607
|
+
}));
|
|
608
|
+
/** A non-2xx response from the Management API (or a transport failure). */
|
|
609
|
+
var PrismaApiError = class extends Data.TaggedError("PrismaApiError") {};
|
|
610
|
+
const attempt = (f) => Effect.tryPromise({
|
|
611
|
+
try: f,
|
|
612
|
+
catch: (cause) => new PrismaApiError({
|
|
613
|
+
status: 0,
|
|
614
|
+
message: String(cause)
|
|
615
|
+
})
|
|
616
|
+
});
|
|
617
|
+
const fail = (r) => Effect.fail(new PrismaApiError({
|
|
618
|
+
status: r.response.status,
|
|
619
|
+
message: JSON.stringify(r.error)
|
|
620
|
+
}));
|
|
621
|
+
/** Unwrap `data`, failing on any API error. Preserves the SDK's response type. */
|
|
622
|
+
const call = (f) => attempt(f).pipe(Effect.flatMap((r) => r.error !== void 0 || r.data === void 0 ? fail(r) : Effect.succeed(r.data)));
|
|
623
|
+
/** Fire-and-forget a call, tolerating a 404 (already deleted). */
|
|
624
|
+
const callVoid = (f) => attempt(f).pipe(Effect.flatMap((r) => r.response.status === 404 || r.error === void 0 ? Effect.void : fail(r)));
|
|
625
|
+
Schedule.both(Schedule.exponential("2 seconds", 2), Schedule.during("5 minutes"));
|
|
626
|
+
Resource("Prisma.ComputeService");
|
|
627
|
+
Resource("Prisma.Deployment");
|
|
628
|
+
Resource("Prisma.EnvironmentVariable");
|
|
629
|
+
Resource("PrismaCloud.ServiceKey");
|
|
630
|
+
Resource("Prisma.Connection");
|
|
631
|
+
Resource("Prisma.Database");
|
|
632
|
+
Resource("Prisma.Project");
|
|
633
|
+
//#endregion
|
|
634
|
+
//#region ../../1-prisma-cloud/0-lowering/lowering/dist/index.mjs
|
|
635
|
+
/** Raised with `ensure: false` when the app's Project (or a named stage's Branch) doesn't exist. */
|
|
636
|
+
var ContainerNotFoundError = class extends Data.TaggedError("ContainerNotFoundError") {};
|
|
637
|
+
const listAllProjects = (client) => Effect.gen(function* () {
|
|
638
|
+
const projects = [];
|
|
639
|
+
let cursor;
|
|
640
|
+
for (;;) {
|
|
641
|
+
const query = cursor === void 0 ? {} : { cursor };
|
|
642
|
+
const page = yield* call(() => client.GET("/v1/projects", { params: { query } }));
|
|
643
|
+
projects.push(...page.data);
|
|
644
|
+
if (!page.pagination.hasMore || page.pagination.nextCursor === null) break;
|
|
645
|
+
cursor = page.pagination.nextCursor;
|
|
601
646
|
}
|
|
602
|
-
return
|
|
603
|
-
}
|
|
647
|
+
return projects;
|
|
648
|
+
});
|
|
649
|
+
/**
|
|
650
|
+
* Workspace ids circulate in two shapes: `wksp_`-prefixed and bare. Compare
|
|
651
|
+
* bare-to-bare so a `wksp_`-prefixed API id still matches a bare configured
|
|
652
|
+
* one (the same normalization `state/bootstrap.ts` applies to the same
|
|
653
|
+
* `/v1/projects` listing).
|
|
654
|
+
*/
|
|
655
|
+
const bareWorkspaceId = (id) => id.startsWith("wksp_") ? id.slice(5) : id;
|
|
656
|
+
/**
|
|
657
|
+
* Finds the app's Project by name in the workspace — PDP allows duplicate
|
|
658
|
+
* project names, so more than one can match; the oldest wins. Creates one
|
|
659
|
+
* if none match, unless `ensure` is `false` (find-only — `destroy`), in
|
|
660
|
+
* which case an absent Project fails with `ContainerNotFoundError`. No
|
|
661
|
+
* ownership marker and no `--project` override (both deferred — see
|
|
662
|
+
* ADR-0019).
|
|
663
|
+
*/
|
|
664
|
+
const resolveProject = (client, workspaceId, appName, ensure) => Effect.gen(function* () {
|
|
665
|
+
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];
|
|
666
|
+
if (oldest !== void 0) return oldest.id;
|
|
667
|
+
if (!ensure) return yield* Effect.fail(new ContainerNotFoundError({ appName }));
|
|
668
|
+
return (yield* call(() => client.POST("/v1/projects", { body: {
|
|
669
|
+
name: appName,
|
|
670
|
+
workspaceId
|
|
671
|
+
} }))).data.id;
|
|
672
|
+
});
|
|
673
|
+
const findBranchId = (client, projectId, gitName) => call(() => client.GET("/v1/projects/{projectId}/branches", { params: {
|
|
674
|
+
path: { projectId },
|
|
675
|
+
query: { gitName }
|
|
676
|
+
} })).pipe(Effect.map((page) => page.data[0]?.id));
|
|
677
|
+
/**
|
|
678
|
+
* Finds the stage's Branch by its exact `gitName`, creating it if absent
|
|
679
|
+
* unless `ensure` is `false` (find-only — `destroy`), in which case an
|
|
680
|
+
* absent Branch fails with `ContainerNotFoundError`. The Management API has
|
|
681
|
+
* no server-side "create-or-return" idempotency (`POST
|
|
682
|
+
* /v1/projects/:id/branches` 409s on a duplicate `gitName`, with no request
|
|
683
|
+
* field to make that a no-op), so idempotency is client-side: observe
|
|
684
|
+
* first, and on a racing 409 from create, re-observe rather than fail.
|
|
685
|
+
*/
|
|
686
|
+
const resolveBranch = (client, projectId, gitName, appName, ensure) => Effect.gen(function* () {
|
|
687
|
+
const existing = yield* findBranchId(client, projectId, gitName);
|
|
688
|
+
if (existing !== void 0) return existing;
|
|
689
|
+
if (!ensure) return yield* Effect.fail(new ContainerNotFoundError({
|
|
690
|
+
appName,
|
|
691
|
+
stage: gitName
|
|
692
|
+
}));
|
|
693
|
+
return yield* call(() => client.POST("/v1/projects/{projectId}/branches", {
|
|
694
|
+
params: { path: { projectId } },
|
|
695
|
+
body: { gitName }
|
|
696
|
+
})).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)));
|
|
697
|
+
});
|
|
698
|
+
/**
|
|
699
|
+
* Resolves the two containers a stage's deploy runs into (ADR-0019): the
|
|
700
|
+
* app's **Project**, found-or-created by name, and — for a named stage
|
|
701
|
+
* only — its **Branch**, found-or-created by `gitName`. The default stage
|
|
702
|
+
* (no `stage`) creates no Branch; `branchId` is omitted. With `ensure:
|
|
703
|
+
* false` (`destroy`), nothing is created — an absent Project or Branch
|
|
704
|
+
* fails with `ContainerNotFoundError` instead.
|
|
705
|
+
*/
|
|
706
|
+
const resolveContainer = (opts) => Effect.gen(function* () {
|
|
707
|
+
const client = yield* ManagementClient;
|
|
708
|
+
const ensure = opts.ensure ?? true;
|
|
709
|
+
const projectId = yield* resolveProject(client, opts.workspaceId, opts.appName, ensure);
|
|
710
|
+
if (opts.stage === void 0) return { projectId };
|
|
711
|
+
return {
|
|
712
|
+
projectId,
|
|
713
|
+
branchId: yield* resolveBranch(client, projectId, opts.stage, opts.appName, ensure)
|
|
714
|
+
};
|
|
715
|
+
});
|
|
716
|
+
/**
|
|
717
|
+
* Soft-deletes a Branch. Tolerates a 404 (already gone). The API refuses if
|
|
718
|
+
* the Branch still has live members or is the production/default Branch —
|
|
719
|
+
* that surfaces as a `PrismaApiError`.
|
|
720
|
+
*/
|
|
721
|
+
const deleteBranch = (branchId) => Effect.gen(function* () {
|
|
722
|
+
const client = yield* ManagementClient;
|
|
723
|
+
yield* callVoid(() => client.DELETE("/v1/branches/{branchId}", { params: { path: { branchId } } }));
|
|
724
|
+
});
|
|
725
|
+
/**
|
|
726
|
+
* Deletes a Project. Tolerates a 404 (already gone). The API refuses with a
|
|
727
|
+
* 400 if the Project still has live dependencies (e.g. another stage's
|
|
728
|
+
* Branch/resources) — that surfaces as a `PrismaApiError`.
|
|
729
|
+
*/
|
|
730
|
+
const deleteProject = (projectId) => Effect.gen(function* () {
|
|
731
|
+
const client = yield* ManagementClient;
|
|
732
|
+
yield* callVoid(() => client.DELETE("/v1/projects/{id}", { params: { path: { id: projectId } } }));
|
|
733
|
+
});
|
|
734
|
+
Provider.ProviderCollection()("Prisma");
|
|
604
735
|
//#endregion
|
|
605
|
-
//#region ../../0-framework/3-tooling/cli/dist/cli-
|
|
736
|
+
//#region ../../0-framework/3-tooling/cli/dist/cli-BeT-XTbX.mjs
|
|
606
737
|
/**
|
|
607
738
|
* A user-facing failure with a message that already names the fix (deploy-cli.md
|
|
608
739
|
* § Error surface). `bin.ts` catches this — and any other Error, including
|
|
@@ -614,6 +745,89 @@ var CliError = class extends Error {
|
|
|
614
745
|
this.name = "CliError";
|
|
615
746
|
}
|
|
616
747
|
};
|
|
748
|
+
/**
|
|
749
|
+
* Pipeline pre-stack step: resolves the app's Project + (named stage) Branch
|
|
750
|
+
* via `@internal/lowering`'s `resolveContainer`, before the generated stack file
|
|
751
|
+
* runs — `deploy` creates-if-absent, `destroy` finds only.
|
|
752
|
+
*/
|
|
753
|
+
/** Validates `stage` as a git ref name via `git check-ref-format` — no silent normalization. */
|
|
754
|
+
function validateStageName(stage) {
|
|
755
|
+
const result = spawnSync("git", ["check-ref-format", `refs/heads/${stage}`], { stdio: "ignore" });
|
|
756
|
+
if (result.error) throw new CliError(`git is required to validate --stage "${stage}" (git check-ref-format): ${result.error.message}.`);
|
|
757
|
+
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}").`);
|
|
758
|
+
}
|
|
759
|
+
async function ensureContainers(input, deps) {
|
|
760
|
+
const env = input.env ?? process.env;
|
|
761
|
+
const workspaceId = env["PRISMA_WORKSPACE_ID"];
|
|
762
|
+
if (workspaceId === void 0 || workspaceId.length === 0) throw new CliError("environment variable PRISMA_WORKSPACE_ID is required.");
|
|
763
|
+
if (deps?.client === void 0 && (env["PRISMA_SERVICE_TOKEN"] ?? "").length === 0) throw new CliError("environment variable PRISMA_SERVICE_TOKEN is required.");
|
|
764
|
+
if (input.stage !== void 0) validateStageName(input.stage);
|
|
765
|
+
const program = resolveContainer({
|
|
766
|
+
workspaceId,
|
|
767
|
+
appName: input.appName,
|
|
768
|
+
...input.stage !== void 0 ? { stage: input.stage } : {},
|
|
769
|
+
ensure: input.command === "deploy"
|
|
770
|
+
}).pipe(Effect.map((c) => ({
|
|
771
|
+
ok: true,
|
|
772
|
+
container: c
|
|
773
|
+
})), Effect.catchTag("ContainerNotFoundError", (e) => Effect.succeed({
|
|
774
|
+
ok: false,
|
|
775
|
+
message: `Nothing deployed for ${e.appName}${e.stage ? `/${e.stage}` : ""} — deploy it first.`
|
|
776
|
+
})), Effect.catchTag("PrismaApiError", (e) => Effect.succeed({
|
|
777
|
+
ok: false,
|
|
778
|
+
message: `Prisma Management API error resolving containers: ${e.message}.`
|
|
779
|
+
})));
|
|
780
|
+
const provided = deps?.client !== void 0 ? program.pipe(Effect.provideService(ManagementClient, deps.client)) : program.pipe(Effect.provide(layer().pipe(Layer.provide(fromEnv()))));
|
|
781
|
+
const outcome = await Effect.runPromise(provided);
|
|
782
|
+
if (!outcome.ok) throw new CliError(outcome.message);
|
|
783
|
+
return outcome.container;
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* Soft-deletes a named stage's Branch after a successful `alchemy destroy`
|
|
787
|
+
* has removed its members (spec §10) — the Management API refuses to delete
|
|
788
|
+
* a Branch that still has live members.
|
|
789
|
+
*/
|
|
790
|
+
async function deleteStageBranch(input, deps) {
|
|
791
|
+
const env = input.env ?? process.env;
|
|
792
|
+
if (deps?.client === void 0 && (env["PRISMA_SERVICE_TOKEN"] ?? "").length === 0) throw new CliError("environment variable PRISMA_SERVICE_TOKEN is required.");
|
|
793
|
+
const program = deleteBranch(input.branchId).pipe(Effect.map(() => ({ ok: true })), Effect.catchTag("PrismaApiError", (e) => Effect.succeed({
|
|
794
|
+
ok: false,
|
|
795
|
+
message: `Failed to delete the stage Branch: ${e.message}.`
|
|
796
|
+
})));
|
|
797
|
+
const provided = deps?.client !== void 0 ? program.pipe(Effect.provideService(ManagementClient, deps.client)) : program.pipe(Effect.provide(layer().pipe(Layer.provide(fromEnv()))));
|
|
798
|
+
const outcome = await Effect.runPromise(provided);
|
|
799
|
+
if (!outcome.ok) throw new CliError(outcome.message);
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* Best-effort cleanup after a successful `--production` destroy: removes
|
|
803
|
+
* the app's Project so hand-run stacks don't accumulate as empty Projects
|
|
804
|
+
* (they eventually hit the workspace's plan limit). Unlike `deleteStageBranch`,
|
|
805
|
+
* this never throws: the destroy itself already succeeded, and the API's own
|
|
806
|
+
* 400 ("still has dependencies") is the only check that matters — failing
|
|
807
|
+
* the command over a cleanup step would be worse than leaving a Project shell.
|
|
808
|
+
*/
|
|
809
|
+
async function deleteAppProject(input, deps) {
|
|
810
|
+
const env = input.env ?? process.env;
|
|
811
|
+
if (deps?.client === void 0 && (env["PRISMA_SERVICE_TOKEN"] ?? "").length === 0) {
|
|
812
|
+
console.warn(`Skipped removing the Project (${input.projectId}): PRISMA_SERVICE_TOKEN is not set.`);
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
const program = deleteProject(input.projectId).pipe(Effect.map(() => ({ ok: true })), Effect.catchTag("PrismaApiError", (e) => Effect.succeed({
|
|
816
|
+
ok: false,
|
|
817
|
+
error: e
|
|
818
|
+
})));
|
|
819
|
+
const provided = deps?.client !== void 0 ? program.pipe(Effect.provideService(ManagementClient, deps.client)) : program.pipe(Effect.provide(layer().pipe(Layer.provide(fromEnv()))));
|
|
820
|
+
const outcome = await Effect.runPromise(provided);
|
|
821
|
+
if (outcome.ok) {
|
|
822
|
+
console.log(`Removed the Project (${input.projectId}) — nothing was left in it.`);
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
if (outcome.error.status === 400) {
|
|
826
|
+
console.log(`Kept the Project (${input.projectId}) — it still has another stage's resources.`);
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
console.warn(`Could not remove the Project (${input.projectId}) after destroy: ${outcome.error.message}.`);
|
|
830
|
+
}
|
|
617
831
|
/** Pipeline step 6: writes a regenerated-every-run, independently runnable stack module at `.prisma-composer/alchemy.run.ts`. */
|
|
618
832
|
const GENERATED_DIR = ".prisma-composer";
|
|
619
833
|
const GENERATED_FILE = "alchemy.run.ts";
|
|
@@ -716,8 +930,7 @@ function validateConfigShape(loaded, configPath) {
|
|
|
716
930
|
if (seen.has(id)) throw new CliError(`${CONFIG_FILENAME}: extension "${id}" is listed more than once in \`extensions\`.`);
|
|
717
931
|
seen.add(id);
|
|
718
932
|
}
|
|
719
|
-
|
|
720
|
-
if (!isRecord(state) || typeof state["extension"] !== "string" || typeof state["create"] !== "function") throw fieldError("state", "must be a state descriptor (e.g. prismaState())");
|
|
933
|
+
if (typeof loaded["state"] !== "function") throw fieldError("state", "must be a function returning the deploy state layer (e.g. () => prismaState())");
|
|
721
934
|
return blindCast(loaded);
|
|
722
935
|
}
|
|
723
936
|
/**
|
|
@@ -776,7 +989,7 @@ function resolveAlchemyBin(startDir) {
|
|
|
776
989
|
dir = parent;
|
|
777
990
|
}
|
|
778
991
|
}
|
|
779
|
-
/** Runs `alchemy deploy|destroy <stack file> --yes [--stage <stage>]`, inheriting stdio + env, plus
|
|
992
|
+
/** Runs `alchemy deploy|destroy <stack file> --yes [--stage <stage>]`, inheriting stdio + env, plus the resolved Project/Branch ids. */
|
|
780
993
|
function runAlchemy(input) {
|
|
781
994
|
const bin = resolveAlchemyBin(input.cwd);
|
|
782
995
|
const args = [
|
|
@@ -790,7 +1003,8 @@ function runAlchemy(input) {
|
|
|
790
1003
|
stdio: "inherit",
|
|
791
1004
|
env: {
|
|
792
1005
|
...input.env ?? process.env,
|
|
793
|
-
|
|
1006
|
+
PRISMA_PROJECT_ID: input.projectId,
|
|
1007
|
+
...input.branchId !== void 0 ? { PRISMA_BRANCH_ID: input.branchId } : {}
|
|
794
1008
|
}
|
|
795
1009
|
});
|
|
796
1010
|
if (result.error !== void 0) throw result.error;
|
|
@@ -816,12 +1030,6 @@ function validateRegistryCoverage(graph, config) {
|
|
|
816
1030
|
lookup(extensions, node.build.extension, node.build.type, "build", `service node "${id}"'s build descriptor`);
|
|
817
1031
|
}
|
|
818
1032
|
}
|
|
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
|
-
}
|
|
825
1033
|
/**
|
|
826
1034
|
* Argument parsing (clipanion — prisma-next's CLI idiom, see
|
|
827
1035
|
* prisma-next/packages/1-framework/3-tooling/cli/src/migration-cli.ts) +
|
|
@@ -917,7 +1125,6 @@ async function run(argv, deps = {}) {
|
|
|
917
1125
|
throw error;
|
|
918
1126
|
}
|
|
919
1127
|
const stage = effectiveStage(args);
|
|
920
|
-
if (stage !== void 0) validateStageName(stage);
|
|
921
1128
|
const cwd = process.cwd();
|
|
922
1129
|
if (args.command === "destroy") warnIfNoLocalDeployState(cwd);
|
|
923
1130
|
const resolvedEntryPath = path.resolve(cwd, args.entry);
|
|
@@ -937,32 +1144,18 @@ async function run(argv, deps = {}) {
|
|
|
937
1144
|
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.`);
|
|
938
1145
|
throw error;
|
|
939
1146
|
}
|
|
940
|
-
const
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
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
|
-
}
|
|
1147
|
+
const { projectId, branchId } = await (deps.ensureContainers ?? ensureContainers)({
|
|
1148
|
+
command: args.command,
|
|
1149
|
+
appName: name,
|
|
1150
|
+
stage
|
|
1151
|
+
});
|
|
960
1152
|
if (args.command === "deploy") for (const extension of config.extensions) {
|
|
961
1153
|
if (extension.preflight === void 0) continue;
|
|
962
1154
|
try {
|
|
963
1155
|
await extension.preflight({
|
|
964
1156
|
graph,
|
|
965
|
-
|
|
1157
|
+
projectId,
|
|
1158
|
+
branchId,
|
|
966
1159
|
stage
|
|
967
1160
|
});
|
|
968
1161
|
} catch (error) {
|
|
@@ -982,36 +1175,28 @@ async function run(argv, deps = {}) {
|
|
|
982
1175
|
stackFileRelativePath: GENERATED_STACK_RELATIVE_PATH,
|
|
983
1176
|
cwd,
|
|
984
1177
|
stage,
|
|
985
|
-
|
|
1178
|
+
projectId,
|
|
1179
|
+
...branchId !== void 0 ? { branchId } : {}
|
|
986
1180
|
});
|
|
987
1181
|
if (status !== 0) {
|
|
988
1182
|
console.error(`\nGenerated stack file: ${stackPath}`);
|
|
989
1183
|
console.error(`Run \`alchemy ${args.command} ${GENERATED_STACK_RELATIVE_PATH} --yes\` from ${cwd} to reproduce this directly.`);
|
|
990
1184
|
return status;
|
|
991
1185
|
}
|
|
992
|
-
if (args.command === "destroy") {
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
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
|
-
}
|
|
1186
|
+
if (args.command === "destroy") for (const extension of config.extensions) {
|
|
1187
|
+
if (extension.teardown === void 0) continue;
|
|
1188
|
+
try {
|
|
1189
|
+
await extension.teardown({
|
|
1190
|
+
projectId,
|
|
1191
|
+
branchId,
|
|
1192
|
+
stage
|
|
1193
|
+
});
|
|
1194
|
+
} catch (error) {
|
|
1195
|
+
throw error instanceof CliError ? error : new CliError(error instanceof Error ? error.message : String(error));
|
|
1013
1196
|
}
|
|
1014
1197
|
}
|
|
1198
|
+
if (args.command === "destroy" && branchId !== void 0) await (deps.deleteBranch ?? ((input) => deleteStageBranch(input)))({ branchId });
|
|
1199
|
+
else if (args.command === "destroy") await (deps.deleteProject ?? ((input) => deleteAppProject(input)))({ projectId });
|
|
1015
1200
|
return status;
|
|
1016
1201
|
} catch (error) {
|
|
1017
1202
|
console.error(`\nGenerated stack file: ${stackPath}`);
|