alchemy 2.0.0-beta.43 → 2.0.0-beta.44
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/bin/cli.js +18 -5
- package/bin/exec.js +315 -233
- package/bin/exec.js.map +1 -1
- package/lib/Auth/Lock.js +2 -2
- package/lib/Auth/Lock.js.map +1 -1
- package/lib/Bundle/Bundle.d.ts +11 -0
- package/lib/Bundle/Bundle.d.ts.map +1 -1
- package/lib/Bundle/Bundle.js +19 -12
- package/lib/Bundle/Bundle.js.map +1 -1
- package/lib/Bundle/BundleAnalyzerPlugin.d.ts +25 -0
- package/lib/Bundle/BundleAnalyzerPlugin.d.ts.map +1 -0
- package/lib/Bundle/BundleAnalyzerPlugin.js +16 -0
- package/lib/Bundle/BundleAnalyzerPlugin.js.map +1 -0
- package/lib/Bundle/RawPlugin.d.ts +36 -0
- package/lib/Bundle/RawPlugin.d.ts.map +1 -0
- package/lib/Bundle/RawPlugin.js +80 -0
- package/lib/Bundle/RawPlugin.js.map +1 -0
- package/lib/Bundle/index.d.ts +2 -0
- package/lib/Bundle/index.d.ts.map +1 -1
- package/lib/Bundle/index.js +2 -0
- package/lib/Bundle/index.js.map +1 -1
- package/lib/Cli/commands/dev.d.ts +3 -2
- package/lib/Cli/commands/dev.d.ts.map +1 -1
- package/lib/Cli/commands/dev.js +9 -1
- package/lib/Cli/commands/dev.js.map +1 -1
- package/lib/Cli/exec.d.ts.map +1 -1
- package/lib/Cli/exec.js +2 -1
- package/lib/Cli/exec.js.map +1 -1
- package/lib/Cloudflare/Local.d.ts +2 -0
- package/lib/Cloudflare/Local.d.ts.map +1 -0
- package/lib/Cloudflare/Local.js +9 -0
- package/lib/Cloudflare/Local.js.map +1 -0
- package/lib/Cloudflare/Providers.d.ts.map +1 -1
- package/lib/Cloudflare/Providers.js +3 -1
- package/lib/Cloudflare/Providers.js.map +1 -1
- package/lib/Cloudflare/Workers/DurableObjectBridge.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/DurableObjectBridge.js +0 -1
- package/lib/Cloudflare/Workers/DurableObjectBridge.js.map +1 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts +22 -3
- package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.js +413 -21
- package/lib/Cloudflare/Workers/LocalWorkerProvider.js.map +1 -1
- package/lib/Cloudflare/Workers/Worker.d.ts +6 -19
- package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Worker.js +3 -4
- package/lib/Cloudflare/Workers/Worker.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBridge.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBridge.js +3 -5
- package/lib/Cloudflare/Workers/WorkerBridge.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBundle.d.ts +1 -2
- package/lib/Cloudflare/Workers/WorkerBundle.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBundle.js +3 -6
- package/lib/Cloudflare/Workers/WorkerBundle.js.map +1 -1
- package/lib/Cloudflare/Zaraz/ZarazConfig.d.ts +184 -0
- package/lib/Cloudflare/Zaraz/ZarazConfig.d.ts.map +1 -0
- package/lib/Cloudflare/Zaraz/ZarazConfig.js +216 -0
- package/lib/Cloudflare/Zaraz/ZarazConfig.js.map +1 -0
- package/lib/Cloudflare/Zaraz/ZarazEventTypes.d.ts +188 -0
- package/lib/Cloudflare/Zaraz/ZarazEventTypes.d.ts.map +1 -0
- package/lib/Cloudflare/Zaraz/ZarazEventTypes.js +4 -0
- package/lib/Cloudflare/Zaraz/ZarazEventTypes.js.map +1 -0
- package/lib/Cloudflare/Zaraz/index.d.ts +3 -0
- package/lib/Cloudflare/Zaraz/index.d.ts.map +1 -0
- package/lib/Cloudflare/Zaraz/index.js +2 -0
- package/lib/Cloudflare/Zaraz/index.js.map +1 -0
- package/lib/Cloudflare/index.d.ts +1 -0
- package/lib/Cloudflare/index.d.ts.map +1 -1
- package/lib/Cloudflare/index.js +1 -0
- package/lib/Cloudflare/index.js.map +1 -1
- package/lib/Local/RpcProvider.d.ts +54 -0
- package/lib/Local/RpcProvider.d.ts.map +1 -0
- package/lib/Local/RpcProvider.js +57 -0
- package/lib/Local/RpcProvider.js.map +1 -0
- package/lib/Local/RpcProviderProxy.d.ts +19 -0
- package/lib/Local/RpcProviderProxy.d.ts.map +1 -0
- package/lib/Local/RpcProviderProxy.js +48 -0
- package/lib/Local/RpcProviderProxy.js.map +1 -0
- package/lib/Local/RpcSerialization.d.ts +34 -0
- package/lib/Local/RpcSerialization.d.ts.map +1 -0
- package/lib/Local/RpcSerialization.js +120 -0
- package/lib/Local/RpcSerialization.js.map +1 -0
- package/lib/Local/RpcServer.d.ts +63 -0
- package/lib/Local/RpcServer.d.ts.map +1 -0
- package/lib/Local/RpcServer.js +72 -0
- package/lib/Local/RpcServer.js.map +1 -0
- package/lib/Local/RpcServerBun.d.ts +3 -0
- package/lib/Local/RpcServerBun.d.ts.map +1 -0
- package/lib/Local/RpcServerBun.js +47 -0
- package/lib/Local/RpcServerBun.js.map +1 -0
- package/lib/Local/RpcServerEnvironment.d.ts +20 -0
- package/lib/Local/RpcServerEnvironment.d.ts.map +1 -0
- package/lib/Local/RpcServerEnvironment.js +22 -0
- package/lib/Local/RpcServerEnvironment.js.map +1 -0
- package/lib/Local/RpcServerNode.d.ts +3 -0
- package/lib/Local/RpcServerNode.d.ts.map +1 -0
- package/lib/Local/RpcServerNode.js +43 -0
- package/lib/Local/RpcServerNode.js.map +1 -0
- package/lib/Local/RpcServerSession.d.ts +27 -0
- package/lib/Local/RpcServerSession.d.ts.map +1 -0
- package/lib/Local/RpcServerSession.js +68 -0
- package/lib/Local/RpcServerSession.js.map +1 -0
- package/lib/Local/RpcSpawner.d.ts +21 -0
- package/lib/Local/RpcSpawner.d.ts.map +1 -0
- package/lib/Local/RpcSpawner.js +132 -0
- package/lib/Local/RpcSpawner.js.map +1 -0
- package/lib/Util/PlatformServices.d.ts +7 -0
- package/lib/Util/PlatformServices.d.ts.map +1 -1
- package/lib/Util/PlatformServices.js +22 -11
- package/lib/Util/PlatformServices.js.map +1 -1
- package/lib/Util/camel.d.ts.map +1 -1
- package/lib/Util/camel.js +2 -3
- package/lib/Util/camel.js.map +1 -1
- package/lib/Util/data.d.ts +5 -0
- package/lib/Util/data.d.ts.map +1 -1
- package/lib/Util/data.js +29 -0
- package/lib/Util/data.js.map +1 -1
- package/lib/Util/stable.d.ts.map +1 -1
- package/lib/Util/stable.js +3 -6
- package/lib/Util/stable.js.map +1 -1
- package/lib/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +12 -14
- package/src/Auth/Lock.ts +2 -2
- package/src/Bundle/Bundle.ts +35 -13
- package/src/Bundle/BundleAnalyzerPlugin.ts +33 -0
- package/src/Bundle/RawPlugin.ts +82 -0
- package/src/Bundle/index.ts +2 -0
- package/src/Cli/commands/dev.ts +51 -37
- package/src/Cli/exec.ts +12 -7
- package/src/Cloudflare/Local.ts +23 -0
- package/src/Cloudflare/Providers.ts +3 -0
- package/src/Cloudflare/Workers/DurableObjectBridge.ts +0 -1
- package/src/Cloudflare/Workers/LocalWorkerProvider.ts +547 -21
- package/src/Cloudflare/Workers/Worker.ts +11 -22
- package/src/Cloudflare/Workers/WorkerBridge.ts +3 -5
- package/src/Cloudflare/Workers/WorkerBundle.ts +4 -8
- package/src/Cloudflare/Zaraz/ZarazConfig.ts +403 -0
- package/src/Cloudflare/Zaraz/ZarazEventTypes.ts +257 -0
- package/src/Cloudflare/Zaraz/index.ts +25 -0
- package/src/Cloudflare/index.ts +1 -0
- package/src/Local/RpcProvider.ts +102 -0
- package/src/Local/RpcProviderProxy.ts +86 -0
- package/src/Local/RpcSerialization.ts +225 -0
- package/src/Local/RpcServer.ts +138 -0
- package/src/Local/RpcServerBun.ts +59 -0
- package/src/Local/RpcServerEnvironment.ts +55 -0
- package/src/Local/RpcServerNode.ts +58 -0
- package/src/Local/RpcServerSession.ts +87 -0
- package/src/Local/RpcSpawner.ts +196 -0
- package/src/Util/PlatformServices.ts +41 -27
- package/src/Util/camel.ts +3 -5
- package/src/Util/data.ts +38 -0
- package/src/Util/stable.ts +4 -10
- package/lib/Cloudflare/Local/Sidecar.d.ts +0 -63
- package/lib/Cloudflare/Local/Sidecar.d.ts.map +0 -1
- package/lib/Cloudflare/Local/Sidecar.js +0 -50
- package/lib/Cloudflare/Local/Sidecar.js.map +0 -1
- package/lib/Cloudflare/Local/SidecarHandlers.d.ts +0 -8
- package/lib/Cloudflare/Local/SidecarHandlers.d.ts.map +0 -1
- package/lib/Cloudflare/Local/SidecarHandlers.js +0 -381
- package/lib/Cloudflare/Local/SidecarHandlers.js.map +0 -1
- package/lib/Cloudflare/Local/SidecarServer.d.ts +0 -2
- package/lib/Cloudflare/Local/SidecarServer.d.ts.map +0 -1
- package/lib/Cloudflare/Local/SidecarServer.js +0 -34
- package/lib/Cloudflare/Local/SidecarServer.js.map +0 -1
- package/lib/Sidecar/Lock.d.ts +0 -23
- package/lib/Sidecar/Lock.d.ts.map +0 -1
- package/lib/Sidecar/Lock.js +0 -39
- package/lib/Sidecar/Lock.js.map +0 -1
- package/lib/Sidecar/RpcClient.d.ts +0 -15
- package/lib/Sidecar/RpcClient.d.ts.map +0 -1
- package/lib/Sidecar/RpcClient.js +0 -78
- package/lib/Sidecar/RpcClient.js.map +0 -1
- package/lib/Sidecar/RpcHandler.d.ts +0 -51
- package/lib/Sidecar/RpcHandler.d.ts.map +0 -1
- package/lib/Sidecar/RpcHandler.js +0 -87
- package/lib/Sidecar/RpcHandler.js.map +0 -1
- package/lib/Sidecar/RpcPaths.d.ts +0 -19
- package/lib/Sidecar/RpcPaths.d.ts.map +0 -1
- package/lib/Sidecar/RpcPaths.js +0 -31
- package/lib/Sidecar/RpcPaths.js.map +0 -1
- package/lib/Sidecar/RpcServer.d.ts +0 -30
- package/lib/Sidecar/RpcServer.d.ts.map +0 -1
- package/lib/Sidecar/RpcServer.js +0 -105
- package/lib/Sidecar/RpcServer.js.map +0 -1
- package/lib/Sidecar/RpcServerBun.d.ts +0 -4
- package/lib/Sidecar/RpcServerBun.d.ts.map +0 -1
- package/lib/Sidecar/RpcServerBun.js +0 -28
- package/lib/Sidecar/RpcServerBun.js.map +0 -1
- package/lib/Sidecar/RpcServerNode.d.ts +0 -4
- package/lib/Sidecar/RpcServerNode.d.ts.map +0 -1
- package/lib/Sidecar/RpcServerNode.js +0 -39
- package/lib/Sidecar/RpcServerNode.js.map +0 -1
- package/lib/Sidecar/RpcServices.d.ts +0 -6
- package/lib/Sidecar/RpcServices.d.ts.map +0 -1
- package/lib/Sidecar/RpcServices.js +0 -29
- package/lib/Sidecar/RpcServices.js.map +0 -1
- package/src/Cloudflare/Local/Sidecar.ts +0 -81
- package/src/Cloudflare/Local/SidecarHandlers.ts +0 -491
- package/src/Cloudflare/Local/SidecarServer.ts +0 -70
- package/src/Sidecar/Lock.ts +0 -66
- package/src/Sidecar/RpcClient.ts +0 -142
- package/src/Sidecar/RpcHandler.ts +0 -205
- package/src/Sidecar/RpcPaths.ts +0 -39
- package/src/Sidecar/RpcServer.ts +0 -153
- package/src/Sidecar/RpcServerBun.ts +0 -41
- package/src/Sidecar/RpcServerNode.ts +0 -51
- package/src/Sidecar/RpcServices.ts +0 -48
package/bin/cli.js
CHANGED
|
@@ -19,11 +19,16 @@
|
|
|
19
19
|
// exports point at .ts source), but consumers install into `node_modules/`,
|
|
20
20
|
// so the path check sends them to the bundled `alchemy.js` regardless.
|
|
21
21
|
//
|
|
22
|
-
// foreground-child
|
|
23
|
-
//
|
|
24
|
-
|
|
22
|
+
// We use @alchemy.run/node-utils' foreground-child so we can pipe the
|
|
23
|
+
// child's stderr through a filter — upstream hardcodes stdio = [0, 1, 2],
|
|
24
|
+
// so the only way to drop bun's hard-coded watcher warning ("warn: File
|
|
25
|
+
// <path> is not in the project directory and will not be watched", which is
|
|
26
|
+
// noise in our monorepo and has no bun flag to silence) is to own the spawn
|
|
27
|
+
// call. Everything else — signal proxying, watchdog, IPC bridging, exit-code
|
|
28
|
+
// forwarding — matches upstream.
|
|
25
29
|
import { fileURLToPath } from "node:url";
|
|
26
30
|
import path from "pathe";
|
|
31
|
+
import { foregroundChild } from "@alchemy.run/node-utils";
|
|
27
32
|
|
|
28
33
|
const execpath = (process.env.npm_execpath ?? "").toLowerCase();
|
|
29
34
|
const userAgent = (process.env.npm_config_user_agent ?? "").toLowerCase();
|
|
@@ -62,6 +67,9 @@ if (runtime === "bun" && isDev) {
|
|
|
62
67
|
args.push(runtime === "bun" ? tsEntry : jsEntry, ...process.argv.slice(2));
|
|
63
68
|
|
|
64
69
|
process.on("uncaughtException", (error) => {
|
|
70
|
+
// STATUS_CONTROL_C_EXIT on Windows — the watchdog inherits Ctrl-C from the
|
|
71
|
+
// console and exits with this code instead of a SIGINT signal.
|
|
72
|
+
const WIN_CTRL_C = 3221225786;
|
|
65
73
|
if (
|
|
66
74
|
error.message.includes(
|
|
67
75
|
"foreground-child watchdog process died unexpectedly!",
|
|
@@ -71,7 +79,7 @@ process.on("uncaughtException", (error) => {
|
|
|
71
79
|
"watchedProcess" in error.cause &&
|
|
72
80
|
error.cause.watchedProcess !== null &&
|
|
73
81
|
"cmd" in error.cause.watchedProcess &&
|
|
74
|
-
error.cause.signal === "SIGINT"
|
|
82
|
+
(error.cause.signal === "SIGINT" || error.cause.code === WIN_CTRL_C)
|
|
75
83
|
) {
|
|
76
84
|
console.log("Interrupted.");
|
|
77
85
|
process.exit(0);
|
|
@@ -79,4 +87,9 @@ process.on("uncaughtException", (error) => {
|
|
|
79
87
|
console.error(error);
|
|
80
88
|
});
|
|
81
89
|
|
|
82
|
-
|
|
90
|
+
// Substring match (not regex) — bun may wrap the line in ANSI color codes
|
|
91
|
+
// when stderr is piped to a TTY-aware parent, so anchored regex is fragile.
|
|
92
|
+
foregroundChild(runtime, args, {
|
|
93
|
+
stderrFilter: (line) =>
|
|
94
|
+
!line.includes("is not in the project directory and will not be watched"),
|
|
95
|
+
});
|
package/bin/exec.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as ConfigProvider from "effect/ConfigProvider";
|
|
2
2
|
import * as Effect from "effect/Effect";
|
|
3
3
|
import * as Layer from "effect/Layer";
|
|
4
|
-
import * as
|
|
4
|
+
import * as Schema from "effect/Schema";
|
|
5
5
|
import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
|
|
6
6
|
import * as EffectContext from "effect/Context";
|
|
7
7
|
import * as FileSystem from "effect/FileSystem";
|
|
@@ -11,20 +11,28 @@ import * as Redacted from "effect/Redacted";
|
|
|
11
11
|
import path from "pathe";
|
|
12
12
|
import * as Config from "effect/Config";
|
|
13
13
|
import os from "node:os";
|
|
14
|
-
import
|
|
14
|
+
import { newWebSocketRpcSession } from "capnweb";
|
|
15
|
+
import * as Cache from "effect/Cache";
|
|
16
|
+
import * as HttpBody from "effect/unstable/http/HttpBody";
|
|
17
|
+
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
15
18
|
import * as Logger from "effect/Logger";
|
|
16
19
|
import * as Option from "effect/Option";
|
|
17
|
-
import
|
|
18
|
-
import * as Flag from "effect/unstable/cli/Flag";
|
|
19
|
-
import * as Data from "effect/Data";
|
|
20
|
-
import * as Cause from "effect/Cause";
|
|
20
|
+
import "effect/Scope";
|
|
21
21
|
import * as Deferred from "effect/Deferred";
|
|
22
|
-
import
|
|
22
|
+
import * as Data from "effect/Data";
|
|
23
|
+
import { flow, pipe } from "effect/Function";
|
|
23
24
|
import { SingleShotGen } from "effect/Utils";
|
|
24
25
|
import { pipeArguments } from "effect/Pipeable";
|
|
25
|
-
import "effect/
|
|
26
|
-
import
|
|
26
|
+
import "effect/Predicate";
|
|
27
|
+
import "effect/Record";
|
|
28
|
+
import * as Cause from "effect/Cause";
|
|
27
29
|
import * as Exit from "effect/Exit";
|
|
30
|
+
import * as Stream from "effect/Stream";
|
|
31
|
+
import * as NodeUtil from "node:util";
|
|
32
|
+
import * as Console from "effect/Console";
|
|
33
|
+
import * as Command from "effect/unstable/cli/Command";
|
|
34
|
+
import * as Flag from "effect/unstable/cli/Flag";
|
|
35
|
+
import * as Duration from "effect/Duration";
|
|
28
36
|
import * as Metric from "effect/Metric";
|
|
29
37
|
import { createHash } from "node:crypto";
|
|
30
38
|
import * as Argument from "effect/unstable/cli/Argument";
|
|
@@ -146,125 +154,6 @@ const CredentialsStoreLive = Layer.effect(CredentialsStore, Effect.gen(function*
|
|
|
146
154
|
};
|
|
147
155
|
}));
|
|
148
156
|
|
|
149
|
-
//#endregion
|
|
150
|
-
//#region lib/Util/PlatformServices.js
|
|
151
|
-
const isBun = typeof Bun !== "undefined";
|
|
152
|
-
const PlatformServices = Effect.promise(async () => {
|
|
153
|
-
if (isBun) {
|
|
154
|
-
const [BunServices, BunSocket] = await Promise.all([import("@effect/platform-bun/BunServices"), import("@effect/platform-bun/BunSocket")]);
|
|
155
|
-
return Layer.merge(BunServices.layer, BunSocket.layerWebSocketConstructor);
|
|
156
|
-
} else {
|
|
157
|
-
const [NodeServices, NodeSocket] = await Promise.all([import("@effect/platform-node/NodeServices"), import("@effect/platform-node/NodeSocket")]);
|
|
158
|
-
return Layer.merge(NodeServices.layer, NodeSocket.layerWebSocketConstructor);
|
|
159
|
-
}
|
|
160
|
-
}).pipe(Layer.unwrap);
|
|
161
|
-
const runMain = (effect, options) => {
|
|
162
|
-
if (isBun) import("@effect/platform-bun/BunRuntime").then((BunRuntime) => BunRuntime.runMain(effect, options));
|
|
163
|
-
else import("@effect/platform-node/NodeRuntime").then((NodeRuntime) => NodeRuntime.runMain(effect, options));
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
//#endregion
|
|
167
|
-
//#region lib/AdoptPolicy.js
|
|
168
|
-
/**
|
|
169
|
-
* AdoptPolicy controls whether the engine should "adopt" pre-existing cloud
|
|
170
|
-
* resources that conflict with this stack instead of failing.
|
|
171
|
-
*
|
|
172
|
-
* The engine always calls a provider's `read` lifecycle operation when there
|
|
173
|
-
* is no prior state and `read` is implemented. The provider's job is to
|
|
174
|
-
* report two things:
|
|
175
|
-
*
|
|
176
|
-
* 1. Whether the resource exists (returns attributes vs. `undefined`).
|
|
177
|
-
* 2. Whether *we* own it (plain attributes vs. {@link Unowned}-branded).
|
|
178
|
-
*
|
|
179
|
-
* The engine then routes:
|
|
180
|
-
*
|
|
181
|
-
* | `read` outcome | `adopt: false` | `adopt: true` |
|
|
182
|
-
* | ------------------- | -------------- | ----------------- |
|
|
183
|
-
* | undefined | create | create |
|
|
184
|
-
* | owned (plain attrs) | silent adopt | silent adopt |
|
|
185
|
-
* | {@link Unowned} | fail | takeover (adopt) |
|
|
186
|
-
*
|
|
187
|
-
* "Owned" means a provider has affirmatively determined the resource was
|
|
188
|
-
* created by this stack/stage/logical-id (typically by inspecting tags or
|
|
189
|
-
* naming conventions). Resources without ownership semantics simply always
|
|
190
|
-
* return plain attrs — they are treated as owned by default, so silent
|
|
191
|
-
* adoption is the norm and `--adopt` is unnecessary.
|
|
192
|
-
*
|
|
193
|
-
* The policy can be overridden per-effect via {@link adopt} — most commonly
|
|
194
|
-
* applied at the resource or stack scope.
|
|
195
|
-
*/
|
|
196
|
-
var AdoptPolicy = class extends EffectContext.Service()("AdoptPolicy") {};
|
|
197
|
-
/**
|
|
198
|
-
* Engine-raised failure produced when `read` reports an existing resource
|
|
199
|
-
* marked as {@link Unowned} and `adopt` is `false`. Surface this to the user
|
|
200
|
-
* to indicate they should either re-tag the cloud resource, pick a different
|
|
201
|
-
* physical name, or re-run with `--adopt` (or `adopt(true)`) to force a
|
|
202
|
-
* takeover.
|
|
203
|
-
*/
|
|
204
|
-
var OwnedBySomeoneElse = class extends Data.TaggedError("OwnedBySomeoneElse") {};
|
|
205
|
-
/**
|
|
206
|
-
* Private symbol used to brand attribute objects returned by `read` as
|
|
207
|
-
* "exists in cloud, but not owned by this stack/stage/logical-id".
|
|
208
|
-
*
|
|
209
|
-
* Deliberately *not* registered via `Symbol.for` — the brand is an
|
|
210
|
-
* engine-internal routing hint and must not be observable or
|
|
211
|
-
* forgeable from outside this module. The engine strips it before any
|
|
212
|
-
* state persistence (see {@link stripUnowned}) so it never leaks into
|
|
213
|
-
* the state store, JSON serialization, or downstream `Output`s.
|
|
214
|
-
*/
|
|
215
|
-
const UnownedTag = Symbol("alchemy/Unowned");
|
|
216
|
-
/**
|
|
217
|
-
* Brand a `read` return value as belonging to a different owner.
|
|
218
|
-
*
|
|
219
|
-
* Use this in a provider's `read` implementation when the resource exists in
|
|
220
|
-
* the cloud but a positive ownership check (e.g. tag inspection) shows it
|
|
221
|
-
* was *not* created by this stack/stage/logical-id:
|
|
222
|
-
*
|
|
223
|
-
* ```ts
|
|
224
|
-
* read: Effect.fnUntraced(function* ({ id, output, olds }) {
|
|
225
|
-
* const settings = yield* getScriptSettings({ ... }).pipe(
|
|
226
|
-
* Effect.catchTag("WorkerNotFound", () => Effect.succeed(undefined)),
|
|
227
|
-
* );
|
|
228
|
-
* if (!settings) return undefined;
|
|
229
|
-
*
|
|
230
|
-
* const attrs = { workerName, ...buildAttrs(settings) };
|
|
231
|
-
* return hasAlchemyWorkerTags(id, settings.tags) ? attrs : Unowned(attrs);
|
|
232
|
-
* }),
|
|
233
|
-
* ```
|
|
234
|
-
*
|
|
235
|
-
* The returned value is structurally still `T` — there is no wrapper to
|
|
236
|
-
* unwrap. The brand is a non-enumerable symbol that the engine inspects
|
|
237
|
-
* during planning to decide whether to take over (`adopt: true`) or fail
|
|
238
|
-
* loudly (`adopt: false`).
|
|
239
|
-
*
|
|
240
|
-
* Resources without ownership semantics should simply return plain attrs —
|
|
241
|
-
* the engine treats them as owned and silent adoption is the default.
|
|
242
|
-
*/
|
|
243
|
-
const Unowned = Object.assign((attrs) => {
|
|
244
|
-
const cloned = { ...attrs };
|
|
245
|
-
Object.defineProperty(cloned, UnownedTag, {
|
|
246
|
-
value: true,
|
|
247
|
-
enumerable: false,
|
|
248
|
-
writable: false,
|
|
249
|
-
configurable: false
|
|
250
|
-
});
|
|
251
|
-
return cloned;
|
|
252
|
-
}, { is: (value) => typeof value === "object" && value !== null && value[UnownedTag] === true });
|
|
253
|
-
/**
|
|
254
|
-
* Strip the {@link Unowned} brand from an attributes object before persisting
|
|
255
|
-
* it — state should not carry per-deploy ownership-routing metadata.
|
|
256
|
-
*
|
|
257
|
-
* The brand is intentionally non-enumerable, so a plain spread already
|
|
258
|
-
* drops it; the additional `delete` is defense-in-depth in case a future
|
|
259
|
-
* change makes the descriptor enumerable. Always returns a fresh object
|
|
260
|
-
* (never mutates the input).
|
|
261
|
-
*/
|
|
262
|
-
const stripUnowned = (attrs) => {
|
|
263
|
-
const out = { ...attrs };
|
|
264
|
-
if (UnownedTag in out) delete out[UnownedTag];
|
|
265
|
-
return out;
|
|
266
|
-
};
|
|
267
|
-
|
|
268
157
|
//#endregion
|
|
269
158
|
//#region lib/Artifacts.js
|
|
270
159
|
/**
|
|
@@ -325,6 +214,10 @@ const makeScopedArtifacts = (store, fqn) => {
|
|
|
325
214
|
*/
|
|
326
215
|
const ensureArtifactStore = (effect) => Effect.serviceOption(ArtifactStore).pipe(Effect.map(Option.getOrUndefined), Effect.flatMap((existing) => effect.pipe(Effect.provideService(ArtifactStore, existing ?? createArtifactStore()))));
|
|
327
216
|
|
|
217
|
+
//#endregion
|
|
218
|
+
//#region lib/Auth/AuthProvider.js
|
|
219
|
+
var AuthProviders = class extends EffectContext.Service()("AuthProviders") {};
|
|
220
|
+
|
|
328
221
|
//#endregion
|
|
329
222
|
//#region lib/Cli/Cli.js
|
|
330
223
|
var Cli = class extends EffectContext.Service()("CLI") {};
|
|
@@ -415,97 +308,6 @@ const tryFindProviderByType = Effect.fnUntraced(function* (resourceType) {
|
|
|
415
308
|
return Option.none();
|
|
416
309
|
});
|
|
417
310
|
|
|
418
|
-
//#endregion
|
|
419
|
-
//#region lib/Auth/AuthProvider.js
|
|
420
|
-
var AuthProviders = class extends EffectContext.Service()("AuthProviders") {};
|
|
421
|
-
|
|
422
|
-
//#endregion
|
|
423
|
-
//#region lib/Stage.js
|
|
424
|
-
var Stage = class extends EffectContext.Service()("Stage") {};
|
|
425
|
-
|
|
426
|
-
//#endregion
|
|
427
|
-
//#region lib/Util/ConfigProvider.js
|
|
428
|
-
const loadConfigProvider = (envFile$1) => {
|
|
429
|
-
if (Option.isSome(envFile$1)) return ConfigProvider.fromDotEnv({ path: envFile$1.value }).pipe(Effect.map((dotEnv) => ConfigProvider.orElse(dotEnv, ConfigProvider.fromEnv())));
|
|
430
|
-
return Effect.gen(function* () {
|
|
431
|
-
if (!(yield* (yield* FileSystem.FileSystem).exists(".env"))) return ConfigProvider.fromEnv();
|
|
432
|
-
return ConfigProvider.orElse(yield* ConfigProvider.fromDotEnv({ path: ".env" }), ConfigProvider.fromEnv());
|
|
433
|
-
});
|
|
434
|
-
};
|
|
435
|
-
|
|
436
|
-
//#endregion
|
|
437
|
-
//#region lib/Util/effect.js
|
|
438
|
-
const effectClass = ((impl) => impl === void 0 ? (innerImpl) => effectClass(innerImpl) : Object.assign(class {
|
|
439
|
-
static asEffect() {
|
|
440
|
-
return impl;
|
|
441
|
-
}
|
|
442
|
-
static [Symbol.iterator]() {
|
|
443
|
-
return new SingleShotGen(this.asEffect());
|
|
444
|
-
}
|
|
445
|
-
static pipe(...fns) {
|
|
446
|
-
return pipeArguments(this, fns);
|
|
447
|
-
}
|
|
448
|
-
}, impl));
|
|
449
|
-
const taggedFunction = (tag$1, fn) => {
|
|
450
|
-
const overrides = {
|
|
451
|
-
asEffect: () => tag$1,
|
|
452
|
-
[Symbol.iterator]: () => tag$1[Symbol.iterator](),
|
|
453
|
-
pipe: (...fns) => pipeArguments(tag$1, fns),
|
|
454
|
-
toString: () => `${tag$1.toString()}.${fn.name}`
|
|
455
|
-
};
|
|
456
|
-
return new Proxy(fn, {
|
|
457
|
-
get: (target, prop, receiver) => Reflect.has(overrides, prop) ? Reflect.get(overrides, prop, receiver) : Reflect.has(target, prop) ? Reflect.get(target, prop, receiver) : Reflect.get(tag$1, prop, tag$1),
|
|
458
|
-
has: (target, prop) => Reflect.has(overrides, prop) || Reflect.has(target, prop) || Reflect.has(tag$1, prop)
|
|
459
|
-
});
|
|
460
|
-
};
|
|
461
|
-
|
|
462
|
-
//#endregion
|
|
463
|
-
//#region lib/Util/FileLogger.js
|
|
464
|
-
const fileLogger = Effect.fnUntraced(function* (...segments) {
|
|
465
|
-
const { dotAlchemy } = yield* AlchemyContext;
|
|
466
|
-
const fs = yield* FileSystem.FileSystem;
|
|
467
|
-
const path$1 = yield* Path;
|
|
468
|
-
const logFile = path$1.join(dotAlchemy, "log", ...segments);
|
|
469
|
-
yield* fs.makeDirectory(path$1.dirname(logFile), { recursive: true });
|
|
470
|
-
return yield* Logger.formatLogFmt.pipe(Logger.toFile(logFile, { flag: "a" }));
|
|
471
|
-
});
|
|
472
|
-
|
|
473
|
-
//#endregion
|
|
474
|
-
//#region lib/Stack.js
|
|
475
|
-
const Stack = Object.assign(taggedFunction(EffectContext.Service()("Stack"), (stackName, options, eff) => {
|
|
476
|
-
if (!stackName) return (stackName$1) => Object.assign(stackRef(stackName$1).pipe(effectClass), {
|
|
477
|
-
stage: createStageProxy(stackName$1),
|
|
478
|
-
make: (options$1, eff$1) => Stack(stackName$1, options$1, eff$1)
|
|
479
|
-
});
|
|
480
|
-
return eff.pipe(make$1({
|
|
481
|
-
name: stackName,
|
|
482
|
-
...options
|
|
483
|
-
}), (eff$1) => Object.assign(eff$1, { stage: createStageProxy(stackName) }));
|
|
484
|
-
}));
|
|
485
|
-
const createStageProxy = (stackName) => new Proxy({}, { get: (_, stage$1) => stackRef(stackName, { stage: stage$1 }) });
|
|
486
|
-
const StackName = Stack.use((stack) => Effect.succeed(stack.name));
|
|
487
|
-
const make$1 = (options) => (effect) => Effect.scope.pipe(Effect.flatMap((scope) => {
|
|
488
|
-
if (options.state == null) return Effect.die(/* @__PURE__ */ new Error(`Stack "${options.name}" is missing a state store. Add a \`state\` layer to the stack options, e.g.:\n Alchemy.Stack("${options.name}", {\n providers: Cloudflare.providers(),\n state: Cloudflare.state(), // <-- required\n }, ...)\nSee https://v2.alchemy.run/concepts/state-store for available state stores.`));
|
|
489
|
-
if (options.providers == null) return Effect.die(/* @__PURE__ */ new Error(`Stack "${options.name}" is missing a providers layer. Add a \`providers\` layer to the stack options, e.g.:\n Alchemy.Stack("${options.name}", {\n providers: Cloudflare.providers(), // <-- required\n state: Cloudflare.state(),\n }, ...)`));
|
|
490
|
-
return options.providers.pipe(Layer.provideMerge(options.state), Layer.provideMerge(Layer.effect(Stack, Stage.pipe(Effect.map((stage$1) => options.stack ?? {
|
|
491
|
-
name: options.name,
|
|
492
|
-
stage: stage$1,
|
|
493
|
-
resources: {},
|
|
494
|
-
bindings: {},
|
|
495
|
-
actions: {}
|
|
496
|
-
})))), Layer.buildWithScope(scope));
|
|
497
|
-
}), Effect.flatMap((context) => Effect.all([
|
|
498
|
-
effect,
|
|
499
|
-
Stack,
|
|
500
|
-
Effect.context()
|
|
501
|
-
]).pipe(Effect.map(([output, stack, services$1]) => ({
|
|
502
|
-
...stack,
|
|
503
|
-
output,
|
|
504
|
-
services: EffectContext.merge(services$1, context)
|
|
505
|
-
})), Effect.provideContext(context))));
|
|
506
|
-
const CurrentStack = Effect.serviceOption(Stack).pipe(Effect.map(Option.getOrUndefined));
|
|
507
|
-
const platform = Layer.mergeAll(PlatformServices, FetchHttpClient.layer, Logger.layer([fileLogger("out")], { mergeWithExisting: true }), Layer.provide(ProfileLive, PlatformServices), Layer.provide(CredentialsStoreLive, PlatformServices));
|
|
508
|
-
|
|
509
311
|
//#endregion
|
|
510
312
|
//#region lib/Resource.js
|
|
511
313
|
const isResource = (value) => {
|
|
@@ -522,6 +324,10 @@ const isResource = (value) => {
|
|
|
522
324
|
var RuntimeContext = class extends EffectContext.Service()("RuntimeContext") {};
|
|
523
325
|
const CurrentRuntimeContext = Effect.serviceOption(RuntimeContext).pipe(Effect.map(Option.getOrUndefined));
|
|
524
326
|
|
|
327
|
+
//#endregion
|
|
328
|
+
//#region lib/Stage.js
|
|
329
|
+
var Stage = class extends EffectContext.Service()("Stage") {};
|
|
330
|
+
|
|
525
331
|
//#endregion
|
|
526
332
|
//#region lib/State/State.js
|
|
527
333
|
const isActionState = (s) => !!s && s.kind === "action";
|
|
@@ -839,6 +645,282 @@ const toObject = (acc, v) => ({
|
|
|
839
645
|
...v
|
|
840
646
|
});
|
|
841
647
|
|
|
648
|
+
//#endregion
|
|
649
|
+
//#region lib/Util/ConfigProvider.js
|
|
650
|
+
const loadConfigProvider = (envFile$1) => {
|
|
651
|
+
if (Option.isSome(envFile$1)) return ConfigProvider.fromDotEnv({ path: envFile$1.value }).pipe(Effect.map((dotEnv) => ConfigProvider.orElse(dotEnv, ConfigProvider.fromEnv())));
|
|
652
|
+
return Effect.gen(function* () {
|
|
653
|
+
if (!(yield* (yield* FileSystem.FileSystem).exists(".env"))) return ConfigProvider.fromEnv();
|
|
654
|
+
return ConfigProvider.orElse(yield* ConfigProvider.fromDotEnv({ path: ".env" }), ConfigProvider.fromEnv());
|
|
655
|
+
});
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
//#endregion
|
|
659
|
+
//#region lib/Util/effect.js
|
|
660
|
+
const effectClass = ((impl) => impl === void 0 ? (innerImpl) => effectClass(innerImpl) : Object.assign(class {
|
|
661
|
+
static asEffect() {
|
|
662
|
+
return impl;
|
|
663
|
+
}
|
|
664
|
+
static [Symbol.iterator]() {
|
|
665
|
+
return new SingleShotGen(this.asEffect());
|
|
666
|
+
}
|
|
667
|
+
static pipe(...fns) {
|
|
668
|
+
return pipeArguments(this, fns);
|
|
669
|
+
}
|
|
670
|
+
}, impl));
|
|
671
|
+
const taggedFunction = (tag$1, fn) => {
|
|
672
|
+
const overrides = {
|
|
673
|
+
asEffect: () => tag$1,
|
|
674
|
+
[Symbol.iterator]: () => tag$1[Symbol.iterator](),
|
|
675
|
+
pipe: (...fns) => pipeArguments(tag$1, fns),
|
|
676
|
+
toString: () => `${tag$1.toString()}.${fn.name}`
|
|
677
|
+
};
|
|
678
|
+
return new Proxy(fn, {
|
|
679
|
+
get: (target, prop, receiver) => Reflect.has(overrides, prop) ? Reflect.get(overrides, prop, receiver) : Reflect.has(target, prop) ? Reflect.get(target, prop, receiver) : Reflect.get(tag$1, prop, tag$1),
|
|
680
|
+
has: (target, prop) => Reflect.has(overrides, prop) || Reflect.has(target, prop) || Reflect.has(tag$1, prop)
|
|
681
|
+
});
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
//#endregion
|
|
685
|
+
//#region lib/Util/FileLogger.js
|
|
686
|
+
const fileLogger = Effect.fnUntraced(function* (...segments) {
|
|
687
|
+
const { dotAlchemy } = yield* AlchemyContext;
|
|
688
|
+
const fs = yield* FileSystem.FileSystem;
|
|
689
|
+
const path$1 = yield* Path;
|
|
690
|
+
const logFile = path$1.join(dotAlchemy, "log", ...segments);
|
|
691
|
+
yield* fs.makeDirectory(path$1.dirname(logFile), { recursive: true });
|
|
692
|
+
return yield* Logger.formatLogFmt.pipe(Logger.toFile(logFile, { flag: "a" }));
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
//#endregion
|
|
696
|
+
//#region lib/Util/PlatformServices.js
|
|
697
|
+
const isBun = typeof Bun !== "undefined";
|
|
698
|
+
/**
|
|
699
|
+
* Constructs a layer with different implementations for Bun and Node.
|
|
700
|
+
*/
|
|
701
|
+
const platformLayer = (constructors) => Layer.unwrap(Effect.promise(async () => {
|
|
702
|
+
if (isBun) return await constructors.bun();
|
|
703
|
+
else return await constructors.node();
|
|
704
|
+
}));
|
|
705
|
+
const PlatformServices = platformLayer({
|
|
706
|
+
bun: async () => {
|
|
707
|
+
const [BunServices, BunSocket] = await Promise.all([import("@effect/platform-bun/BunServices"), import("@effect/platform-bun/BunSocket")]);
|
|
708
|
+
return Layer.merge(BunServices.layer, BunSocket.layerWebSocketConstructor);
|
|
709
|
+
},
|
|
710
|
+
node: async () => {
|
|
711
|
+
const [NodeServices, NodeSocket] = await Promise.all([import("@effect/platform-node/NodeServices"), import("@effect/platform-node/NodeSocket")]);
|
|
712
|
+
return Layer.merge(NodeServices.layer, NodeSocket.layerWebSocketConstructor);
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
const runMain = (effect, options) => {
|
|
716
|
+
if (isBun) import("@effect/platform-bun/BunRuntime").then((BunRuntime) => BunRuntime.runMain(effect, options));
|
|
717
|
+
else import("@effect/platform-node/NodeRuntime").then((NodeRuntime) => NodeRuntime.runMain(effect, options));
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
//#endregion
|
|
721
|
+
//#region lib/Stack.js
|
|
722
|
+
const Stack = Object.assign(taggedFunction(EffectContext.Service()("Stack"), (stackName, options, eff) => {
|
|
723
|
+
if (!stackName) return (stackName$1) => Object.assign(stackRef(stackName$1).pipe(effectClass), {
|
|
724
|
+
stage: createStageProxy(stackName$1),
|
|
725
|
+
make: (options$1, eff$1) => Stack(stackName$1, options$1, eff$1)
|
|
726
|
+
});
|
|
727
|
+
return eff.pipe(make$2({
|
|
728
|
+
name: stackName,
|
|
729
|
+
...options
|
|
730
|
+
}), (eff$1) => Object.assign(eff$1, { stage: createStageProxy(stackName) }));
|
|
731
|
+
}));
|
|
732
|
+
const createStageProxy = (stackName) => new Proxy({}, { get: (_, stage$1) => stackRef(stackName, { stage: stage$1 }) });
|
|
733
|
+
const StackName = Stack.use((stack) => Effect.succeed(stack.name));
|
|
734
|
+
const make$2 = (options) => (effect) => Effect.scope.pipe(Effect.flatMap((scope) => {
|
|
735
|
+
if (options.state == null) return Effect.die(/* @__PURE__ */ new Error(`Stack "${options.name}" is missing a state store. Add a \`state\` layer to the stack options, e.g.:\n Alchemy.Stack("${options.name}", {\n providers: Cloudflare.providers(),\n state: Cloudflare.state(), // <-- required\n }, ...)\nSee https://v2.alchemy.run/concepts/state-store for available state stores.`));
|
|
736
|
+
if (options.providers == null) return Effect.die(/* @__PURE__ */ new Error(`Stack "${options.name}" is missing a providers layer. Add a \`providers\` layer to the stack options, e.g.:\n Alchemy.Stack("${options.name}", {\n providers: Cloudflare.providers(), // <-- required\n state: Cloudflare.state(),\n }, ...)`));
|
|
737
|
+
return options.providers.pipe(Layer.provideMerge(options.state), Layer.provideMerge(Layer.effect(Stack, Stage.pipe(Effect.map((stage$1) => options.stack ?? {
|
|
738
|
+
name: options.name,
|
|
739
|
+
stage: stage$1,
|
|
740
|
+
resources: {},
|
|
741
|
+
bindings: {},
|
|
742
|
+
actions: {}
|
|
743
|
+
})))), Layer.buildWithScope(scope));
|
|
744
|
+
}), Effect.flatMap((context) => Effect.all([
|
|
745
|
+
effect,
|
|
746
|
+
Stack,
|
|
747
|
+
Effect.context()
|
|
748
|
+
]).pipe(Effect.map(([output, stack, services$1]) => ({
|
|
749
|
+
...stack,
|
|
750
|
+
output,
|
|
751
|
+
services: EffectContext.merge(services$1, context)
|
|
752
|
+
})), Effect.provideContext(context))));
|
|
753
|
+
const CurrentStack = Effect.serviceOption(Stack).pipe(Effect.map(Option.getOrUndefined));
|
|
754
|
+
const platform = Layer.mergeAll(PlatformServices, FetchHttpClient.layer, Logger.layer([fileLogger("out")], { mergeWithExisting: true }), Layer.provide(ProfileLive, PlatformServices), Layer.provide(CredentialsStoreLive, PlatformServices));
|
|
755
|
+
|
|
756
|
+
//#endregion
|
|
757
|
+
//#region lib/Local/RpcSerialization.js
|
|
758
|
+
const unwrapRpcHandlers = (handlers, streamKeys) => {
|
|
759
|
+
return Object.fromEntries(Object.entries(handlers).map(([key, value]) => [key, typeof value === "function" ? streamKeys?.includes(key) ? unwrapRpcStreamHandler(value) : unwrapRpcEffectHandler(value) : typeof value === "object" && value !== null ? unwrapRpcHandlers(value) : value]));
|
|
760
|
+
};
|
|
761
|
+
const serializeError = Schema.encodeSync(Schema.Defect);
|
|
762
|
+
const unwrapRpcEffectHandler = (handler) => flow((...args) => serializeRpcArgs(args), (args) => Effect.promise(() => handler(args)), Effect.flatMap((exit) => {
|
|
763
|
+
if (exit._tag === "Success") return Exit.succeed(exit.value);
|
|
764
|
+
return Exit.failCause(Cause.fromReasons(exit.cause.map((reason) => {
|
|
765
|
+
switch (reason._tag) {
|
|
766
|
+
case "Fail": return Cause.makeFailReason(reason.error);
|
|
767
|
+
case "Die": return Cause.makeDieReason(reason.defect);
|
|
768
|
+
case "Interrupt": return Cause.makeInterruptReason(reason.fiberId);
|
|
769
|
+
}
|
|
770
|
+
})));
|
|
771
|
+
}));
|
|
772
|
+
const unwrapRpcStreamHandler = (handler) => flow((...args) => serializeRpcArgs(args), (args) => handler(args), (stream) => Stream.fromReadableStream({
|
|
773
|
+
evaluate: () => stream,
|
|
774
|
+
onError: (error) => error
|
|
775
|
+
}));
|
|
776
|
+
const serializeRpcArgs = (value) => {
|
|
777
|
+
if (Redacted.isRedacted(value)) return {
|
|
778
|
+
_tag: "Redacted",
|
|
779
|
+
value: Redacted.value(value)
|
|
780
|
+
};
|
|
781
|
+
if (isOutput(value)) return {
|
|
782
|
+
_tag: "Output",
|
|
783
|
+
description: NodeUtil.inspect(value)
|
|
784
|
+
};
|
|
785
|
+
if (typeof value === "function") return null;
|
|
786
|
+
if (Array.isArray(value)) return value.map(serializeRpcArgs);
|
|
787
|
+
if (value && typeof value === "object" && !("toJSON" in value)) return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, serializeRpcArgs(child)]));
|
|
788
|
+
return value;
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
//#endregion
|
|
792
|
+
//#region lib/Local/RpcProviderProxy.js
|
|
793
|
+
var RpcProviderProxy = class extends EffectContext.Service()("alchemy/Local/RpcProviderProxy") {};
|
|
794
|
+
const SPAWNER_URL_ENV_KEY = "ALCHEMY_RPC_SPAWNER_URL";
|
|
795
|
+
const make$1 = Effect.fnUntraced(function* (spawnerUrl) {
|
|
796
|
+
const client = yield* HttpClient.HttpClient;
|
|
797
|
+
const getSession = Effect.fnUntraced(function* (serverEntryUrl) {
|
|
798
|
+
const alchemyContext = yield* AlchemyContext;
|
|
799
|
+
const stack = yield* Stack;
|
|
800
|
+
const payload = {
|
|
801
|
+
serverEntryUrl,
|
|
802
|
+
alchemyContext,
|
|
803
|
+
stack: {
|
|
804
|
+
name: stack.name,
|
|
805
|
+
stage: stack.stage
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
return newWebSocketRpcSession(yield* (yield* client.post(spawnerUrl, { body: yield* HttpBody.json(payload) })).text);
|
|
809
|
+
}, (effect, serverEntryUrl) => Effect.catch(effect, (error) => Effect.die(new Error(`Failed to create provider RPC session for "${serverEntryUrl}"`, { cause: error }))));
|
|
810
|
+
const cache = yield* Cache.make({
|
|
811
|
+
lookup: getSession,
|
|
812
|
+
capacity: Infinity,
|
|
813
|
+
requireServicesAt: "lookup"
|
|
814
|
+
});
|
|
815
|
+
return RpcProviderProxy.of({ get: Effect.fnUntraced(function* (mainUrl, providerName) {
|
|
816
|
+
const session = yield* cache.lookup(mainUrl);
|
|
817
|
+
return unwrapRpcHandlers(yield* Effect.promise(() => session.getProvider(providerName)), ["tail"]);
|
|
818
|
+
}) });
|
|
819
|
+
});
|
|
820
|
+
const fromEnv = () => Layer.effect(RpcProviderProxy, Config.string(SPAWNER_URL_ENV_KEY).pipe(Effect.flatMap(make$1)));
|
|
821
|
+
|
|
822
|
+
//#endregion
|
|
823
|
+
//#region lib/AdoptPolicy.js
|
|
824
|
+
/**
|
|
825
|
+
* AdoptPolicy controls whether the engine should "adopt" pre-existing cloud
|
|
826
|
+
* resources that conflict with this stack instead of failing.
|
|
827
|
+
*
|
|
828
|
+
* The engine always calls a provider's `read` lifecycle operation when there
|
|
829
|
+
* is no prior state and `read` is implemented. The provider's job is to
|
|
830
|
+
* report two things:
|
|
831
|
+
*
|
|
832
|
+
* 1. Whether the resource exists (returns attributes vs. `undefined`).
|
|
833
|
+
* 2. Whether *we* own it (plain attributes vs. {@link Unowned}-branded).
|
|
834
|
+
*
|
|
835
|
+
* The engine then routes:
|
|
836
|
+
*
|
|
837
|
+
* | `read` outcome | `adopt: false` | `adopt: true` |
|
|
838
|
+
* | ------------------- | -------------- | ----------------- |
|
|
839
|
+
* | undefined | create | create |
|
|
840
|
+
* | owned (plain attrs) | silent adopt | silent adopt |
|
|
841
|
+
* | {@link Unowned} | fail | takeover (adopt) |
|
|
842
|
+
*
|
|
843
|
+
* "Owned" means a provider has affirmatively determined the resource was
|
|
844
|
+
* created by this stack/stage/logical-id (typically by inspecting tags or
|
|
845
|
+
* naming conventions). Resources without ownership semantics simply always
|
|
846
|
+
* return plain attrs — they are treated as owned by default, so silent
|
|
847
|
+
* adoption is the norm and `--adopt` is unnecessary.
|
|
848
|
+
*
|
|
849
|
+
* The policy can be overridden per-effect via {@link adopt} — most commonly
|
|
850
|
+
* applied at the resource or stack scope.
|
|
851
|
+
*/
|
|
852
|
+
var AdoptPolicy = class extends EffectContext.Service()("AdoptPolicy") {};
|
|
853
|
+
/**
|
|
854
|
+
* Engine-raised failure produced when `read` reports an existing resource
|
|
855
|
+
* marked as {@link Unowned} and `adopt` is `false`. Surface this to the user
|
|
856
|
+
* to indicate they should either re-tag the cloud resource, pick a different
|
|
857
|
+
* physical name, or re-run with `--adopt` (or `adopt(true)`) to force a
|
|
858
|
+
* takeover.
|
|
859
|
+
*/
|
|
860
|
+
var OwnedBySomeoneElse = class extends Data.TaggedError("OwnedBySomeoneElse") {};
|
|
861
|
+
/**
|
|
862
|
+
* Private symbol used to brand attribute objects returned by `read` as
|
|
863
|
+
* "exists in cloud, but not owned by this stack/stage/logical-id".
|
|
864
|
+
*
|
|
865
|
+
* Deliberately *not* registered via `Symbol.for` — the brand is an
|
|
866
|
+
* engine-internal routing hint and must not be observable or
|
|
867
|
+
* forgeable from outside this module. The engine strips it before any
|
|
868
|
+
* state persistence (see {@link stripUnowned}) so it never leaks into
|
|
869
|
+
* the state store, JSON serialization, or downstream `Output`s.
|
|
870
|
+
*/
|
|
871
|
+
const UnownedTag = Symbol("alchemy/Unowned");
|
|
872
|
+
/**
|
|
873
|
+
* Brand a `read` return value as belonging to a different owner.
|
|
874
|
+
*
|
|
875
|
+
* Use this in a provider's `read` implementation when the resource exists in
|
|
876
|
+
* the cloud but a positive ownership check (e.g. tag inspection) shows it
|
|
877
|
+
* was *not* created by this stack/stage/logical-id:
|
|
878
|
+
*
|
|
879
|
+
* ```ts
|
|
880
|
+
* read: Effect.fnUntraced(function* ({ id, output, olds }) {
|
|
881
|
+
* const settings = yield* getScriptSettings({ ... }).pipe(
|
|
882
|
+
* Effect.catchTag("WorkerNotFound", () => Effect.succeed(undefined)),
|
|
883
|
+
* );
|
|
884
|
+
* if (!settings) return undefined;
|
|
885
|
+
*
|
|
886
|
+
* const attrs = { workerName, ...buildAttrs(settings) };
|
|
887
|
+
* return hasAlchemyWorkerTags(id, settings.tags) ? attrs : Unowned(attrs);
|
|
888
|
+
* }),
|
|
889
|
+
* ```
|
|
890
|
+
*
|
|
891
|
+
* The returned value is structurally still `T` — there is no wrapper to
|
|
892
|
+
* unwrap. The brand is a non-enumerable symbol that the engine inspects
|
|
893
|
+
* during planning to decide whether to take over (`adopt: true`) or fail
|
|
894
|
+
* loudly (`adopt: false`).
|
|
895
|
+
*
|
|
896
|
+
* Resources without ownership semantics should simply return plain attrs —
|
|
897
|
+
* the engine treats them as owned and silent adoption is the default.
|
|
898
|
+
*/
|
|
899
|
+
const Unowned = Object.assign((attrs) => {
|
|
900
|
+
const cloned = { ...attrs };
|
|
901
|
+
Object.defineProperty(cloned, UnownedTag, {
|
|
902
|
+
value: true,
|
|
903
|
+
enumerable: false,
|
|
904
|
+
writable: false,
|
|
905
|
+
configurable: false
|
|
906
|
+
});
|
|
907
|
+
return cloned;
|
|
908
|
+
}, { is: (value) => typeof value === "object" && value !== null && value[UnownedTag] === true });
|
|
909
|
+
/**
|
|
910
|
+
* Strip the {@link Unowned} brand from an attributes object before persisting
|
|
911
|
+
* it — state should not carry per-deploy ownership-routing metadata.
|
|
912
|
+
*
|
|
913
|
+
* The brand is intentionally non-enumerable, so a plain spread already
|
|
914
|
+
* drops it; the additional `delete` is defense-in-depth in case a future
|
|
915
|
+
* change makes the descriptor enumerable. Always returns a fresh object
|
|
916
|
+
* (never mutates the input).
|
|
917
|
+
*/
|
|
918
|
+
const stripUnowned = (attrs) => {
|
|
919
|
+
const out = { ...attrs };
|
|
920
|
+
if (UnownedTag in out) delete out[UnownedTag];
|
|
921
|
+
return out;
|
|
922
|
+
};
|
|
923
|
+
|
|
842
924
|
//#endregion
|
|
843
925
|
//#region lib/Diff.js
|
|
844
926
|
const isResolved = (value) => !_hasUnresolved(value);
|
|
@@ -2420,7 +2502,7 @@ var __rewriteRelativeImportExtension = void 0 && (void 0).__rewriteRelativeImpor
|
|
|
2420
2502
|
};
|
|
2421
2503
|
const USER = Config.string("USER").pipe(Config.orElse(() => Config.string("USERNAME")), Config.withDefault("unknown"));
|
|
2422
2504
|
const STAGE = Config.string("stage").pipe(Config.option, (a) => a, Effect.map(Option.getOrUndefined));
|
|
2423
|
-
const stage = Flag.string("stage").pipe(Flag.withSchema(
|
|
2505
|
+
const stage = Flag.string("stage").pipe(Flag.withSchema(Schema.String.check(Schema.isPattern(/^[a-z0-9]+([-_a-z0-9]+)*$/gi))), Flag.withDescription("Stage to deploy to, defaults to dev_${USER}"), Flag.optional, Flag.map(Option.getOrUndefined), Flag.mapEffect(Effect.fn(function* (stage$1) {
|
|
2424
2506
|
if (stage$1) return stage$1;
|
|
2425
2507
|
return yield* STAGE.pipe(Effect.catch(() => Effect.fail(new CliError.MissingOption({ option: "stage" }))), Effect.flatMap((s) => s === void 0 ? USER.pipe(Effect.map((user) => `dev_${user}`), Effect.catch(() => Effect.succeed("unknown"))) : Effect.succeed(s)));
|
|
2426
2508
|
})));
|
|
@@ -2460,17 +2542,17 @@ const importStack = Effect.fn(function* (main) {
|
|
|
2460
2542
|
|
|
2461
2543
|
//#endregion
|
|
2462
2544
|
//#region lib/Cli/commands/deploy.js
|
|
2463
|
-
const ExecStackOptions =
|
|
2464
|
-
main:
|
|
2465
|
-
stage:
|
|
2466
|
-
envFile:
|
|
2467
|
-
profile:
|
|
2468
|
-
dryRun:
|
|
2469
|
-
force:
|
|
2470
|
-
yes:
|
|
2471
|
-
destroy:
|
|
2472
|
-
dev:
|
|
2473
|
-
adopt:
|
|
2545
|
+
const ExecStackOptions = Schema.Struct({
|
|
2546
|
+
main: Schema.String,
|
|
2547
|
+
stage: Schema.String,
|
|
2548
|
+
envFile: Schema.OptionFromOptional(Schema.String),
|
|
2549
|
+
profile: Schema.optional(Schema.String),
|
|
2550
|
+
dryRun: Schema.optional(Schema.Boolean),
|
|
2551
|
+
force: Schema.optional(Schema.Boolean),
|
|
2552
|
+
yes: Schema.optional(Schema.Boolean),
|
|
2553
|
+
destroy: Schema.optional(Schema.Boolean),
|
|
2554
|
+
dev: Schema.optional(Schema.Boolean),
|
|
2555
|
+
adopt: Schema.optional(Schema.Boolean)
|
|
2474
2556
|
});
|
|
2475
2557
|
const stackSpanAttrs = (args) => ({
|
|
2476
2558
|
"alchemy.stage": args.stage,
|
|
@@ -3341,8 +3423,8 @@ const selectCli = () => isNonInteractive() ? LoggingCli : inkCLI();
|
|
|
3341
3423
|
|
|
3342
3424
|
//#endregion
|
|
3343
3425
|
//#region lib/Cli/exec.js
|
|
3344
|
-
const services = Layer.
|
|
3345
|
-
const exec = () => execStack(
|
|
3426
|
+
const services = Layer.merge(selectCli(), fromEnv()).pipe(Layer.provideMerge(Layer.mergeAll(AlchemyContextLive, ProfileLive, CredentialsStoreLive)), Layer.provideMerge(Layer.mergeAll(PlatformServices, FetchHttpClient.layer, ConfigProvider.layer(ConfigProvider.fromEnv()))));
|
|
3427
|
+
const exec = () => execStack(Schema.decodeSync(ExecStackOptions)(JSON.parse(process.env.ALCHEMY_EXEC_OPTIONS))).pipe(Effect.provide(services), Effect.scoped);
|
|
3346
3428
|
|
|
3347
3429
|
//#endregion
|
|
3348
3430
|
//#region bin/exec.ts
|