alchemy 2.0.0-beta.13 → 2.0.0-beta.15
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/alchemy.js +9477 -9290
- package/bin/alchemy.js.map +1 -1
- package/bin/alchemy.ts +6 -2
- package/bin/cli.js +61 -0
- package/bin/commands/deploy.ts +6 -4
- package/bin/commands/login.ts +1 -1
- package/bin/commands/logs.ts +1 -1
- package/bin/commands/state.ts +233 -0
- package/bin/commands/tail.ts +1 -1
- package/lib/AWS/AutoScaling/LaunchTemplate.d.ts +1 -1
- package/lib/AWS/EC2/Instance.d.ts +1 -1
- package/lib/AWS/EC2/hosted.d.ts +2 -2
- package/lib/AWS/ECS/Task.d.ts +4 -4
- package/lib/AWS/ECS/Task.d.ts.map +1 -1
- package/lib/AWS/ECS/Task.js +2 -2
- package/lib/AWS/ECS/Task.js.map +1 -1
- package/lib/AWS/Environment.d.ts +2 -2
- package/lib/AWS/Lambda/BucketEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/BucketEventSource.d.ts.map +1 -1
- package/lib/AWS/Lambda/EventBridgeEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/EventBridgeEventSource.d.ts.map +1 -1
- package/lib/AWS/Lambda/Function.d.ts +2 -2
- package/lib/AWS/Lambda/Function.d.ts.map +1 -1
- package/lib/AWS/Lambda/Function.js +1 -2
- package/lib/AWS/Lambda/Function.js.map +1 -1
- package/lib/AWS/Lambda/QueueEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/QueueEventSource.d.ts.map +1 -1
- package/lib/AWS/Lambda/StreamEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/StreamEventSource.d.ts.map +1 -1
- package/lib/AWS/Lambda/TableEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/TableEventSource.d.ts.map +1 -1
- package/lib/AWS/Lambda/TopicEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/TopicEventSource.d.ts.map +1 -1
- package/lib/AWS/SQS/QueueSink.d.ts.map +1 -1
- package/lib/AWS/SQS/QueueSink.js +1 -2
- package/lib/AWS/SQS/QueueSink.js.map +1 -1
- package/lib/Agent/coding/bash.d.ts +2 -6
- package/lib/Agent/coding/bash.d.ts.map +1 -1
- package/lib/Agent/coding/bash.js +2 -3
- package/lib/Agent/coding/bash.js.map +1 -1
- package/lib/Agent/coding/edit.d.ts +1 -1
- package/lib/Agent/coding/glob.d.ts +2 -4
- package/lib/Agent/coding/glob.d.ts.map +1 -1
- package/lib/Agent/coding/glob.js +1 -2
- package/lib/Agent/coding/glob.js.map +1 -1
- package/lib/Agent/coding/grep.d.ts +2 -4
- package/lib/Agent/coding/grep.d.ts.map +1 -1
- package/lib/Agent/coding/grep.js +1 -2
- package/lib/Agent/coding/grep.js.map +1 -1
- package/lib/Agent/coding/read.d.ts +1 -1
- package/lib/Agent/coding/write.d.ts +1 -1
- package/lib/Agent/lsp/client.d.ts +2 -2
- package/lib/Agent/util/ripgrep.d.ts +2 -2
- package/lib/AlchemyContext.d.ts +13 -0
- package/lib/AlchemyContext.d.ts.map +1 -0
- package/lib/AlchemyContext.js +18 -0
- package/lib/AlchemyContext.js.map +1 -0
- package/lib/Auth/AuthProvider.js +1 -1
- package/lib/Auth/AuthProvider.js.map +1 -1
- package/lib/Auth/Profile.d.ts +1 -1
- package/lib/Auth/index.d.ts +3 -1
- package/lib/Auth/index.d.ts.map +1 -1
- package/lib/Auth/index.js +3 -0
- package/lib/Auth/index.js.map +1 -1
- package/lib/Build/Command.d.ts +1 -1
- package/lib/Build/Memo.d.ts +1 -1
- package/lib/Bundle/Docker.d.ts +3 -3
- package/lib/Bundle/TempRoot.d.ts +3 -3
- package/lib/Cli/LoggingCli.d.ts +4 -0
- package/lib/Cli/LoggingCli.d.ts.map +1 -0
- package/lib/{Test/TestCli.js → Cli/LoggingCli.js} +3 -3
- package/lib/Cli/LoggingCli.js.map +1 -0
- package/lib/Cli/index.d.ts +0 -1
- package/lib/Cli/index.d.ts.map +1 -1
- package/lib/Cli/index.js +4 -1
- package/lib/Cli/index.js.map +1 -1
- package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts +58 -0
- package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts.map +1 -0
- package/lib/Cloudflare/ApiToken/AccountApiToken.js +137 -0
- package/lib/Cloudflare/ApiToken/AccountApiToken.js.map +1 -0
- package/lib/Cloudflare/ApiToken/Common.d.ts +91 -0
- package/lib/Cloudflare/ApiToken/Common.d.ts.map +1 -0
- package/lib/Cloudflare/ApiToken/Common.js +61 -0
- package/lib/Cloudflare/ApiToken/Common.js.map +1 -0
- package/lib/Cloudflare/ApiToken/PermissionGroups.d.ts +1549 -0
- package/lib/Cloudflare/ApiToken/PermissionGroups.d.ts.map +1 -0
- package/lib/Cloudflare/ApiToken/PermissionGroups.js +1856 -0
- package/lib/Cloudflare/ApiToken/PermissionGroups.js.map +1 -0
- package/lib/Cloudflare/ApiToken/UserApiToken.d.ts +49 -0
- package/lib/Cloudflare/ApiToken/UserApiToken.d.ts.map +1 -0
- package/lib/Cloudflare/ApiToken/UserApiToken.js +113 -0
- package/lib/Cloudflare/ApiToken/UserApiToken.js.map +1 -0
- package/lib/Cloudflare/ApiToken/index.d.ts +5 -0
- package/lib/Cloudflare/ApiToken/index.d.ts.map +1 -0
- package/lib/Cloudflare/ApiToken/index.js +5 -0
- package/lib/Cloudflare/ApiToken/index.js.map +1 -0
- package/lib/Cloudflare/Auth/AuthProvider.js +2 -2
- package/lib/Cloudflare/Auth/AuthProvider.js.map +1 -1
- package/lib/Cloudflare/CloudflareEnvironment.d.ts +1 -1
- package/lib/Cloudflare/Container/ContainerApplication.d.ts +2 -2
- package/lib/Cloudflare/Container/ContainerApplication.d.ts.map +1 -1
- package/lib/Cloudflare/Container/ContainerApplication.js +7 -2
- package/lib/Cloudflare/Container/ContainerApplication.js.map +1 -1
- package/lib/Cloudflare/EdgeSession.d.ts +53 -0
- package/lib/Cloudflare/EdgeSession.d.ts.map +1 -0
- package/lib/Cloudflare/EdgeSession.js +78 -0
- package/lib/Cloudflare/EdgeSession.js.map +1 -0
- package/lib/Cloudflare/Providers.d.ts +1 -1
- package/lib/Cloudflare/Providers.d.ts.map +1 -1
- package/lib/Cloudflare/Providers.js +4 -1
- package/lib/Cloudflare/Providers.js.map +1 -1
- package/lib/Cloudflare/StateStore/Api.d.ts +61 -0
- package/lib/Cloudflare/StateStore/Api.d.ts.map +1 -0
- package/lib/Cloudflare/StateStore/Api.js +103 -0
- package/lib/Cloudflare/StateStore/Api.js.map +1 -0
- package/lib/Cloudflare/StateStore/State.d.ts +39 -0
- package/lib/Cloudflare/StateStore/State.d.ts.map +1 -0
- package/lib/Cloudflare/StateStore/State.js +217 -0
- package/lib/Cloudflare/StateStore/State.js.map +1 -0
- package/lib/Cloudflare/StateStore/Store.d.ts +82 -0
- package/lib/Cloudflare/StateStore/Store.d.ts.map +1 -0
- package/lib/Cloudflare/StateStore/Store.js +191 -0
- package/lib/Cloudflare/StateStore/Store.js.map +1 -0
- package/lib/Cloudflare/StateStore/Token.d.ts +43 -0
- package/lib/Cloudflare/StateStore/Token.d.ts.map +1 -0
- package/lib/Cloudflare/StateStore/Token.js +61 -0
- package/lib/Cloudflare/StateStore/Token.js.map +1 -0
- package/lib/Cloudflare/StateStore/index.d.ts +2 -0
- package/lib/Cloudflare/StateStore/index.d.ts.map +1 -0
- package/lib/Cloudflare/StateStore/index.js +2 -0
- package/lib/Cloudflare/StateStore/index.js.map +1 -0
- package/lib/Cloudflare/Workers/Assets.d.ts +2 -2
- package/lib/Cloudflare/Workers/DurableObjectNamespace.d.ts +2 -2
- package/lib/Cloudflare/Workers/DurableObjectNamespace.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/DurableObjectNamespace.js +0 -1
- package/lib/Cloudflare/Workers/DurableObjectNamespace.js.map +1 -1
- package/lib/Cloudflare/Workers/HttpServer.d.ts +1 -1
- package/lib/Cloudflare/Workers/HttpServer.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/HttpServer.js.map +1 -1
- package/lib/Cloudflare/Workers/Worker.d.ts +5 -5
- package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Worker.js +0 -1
- package/lib/Cloudflare/Workers/Worker.js.map +1 -1
- package/lib/Cloudflare/index.d.ts +4 -1
- package/lib/Cloudflare/index.d.ts.map +1 -1
- package/lib/Cloudflare/index.js +4 -1
- package/lib/Cloudflare/index.js.map +1 -1
- package/lib/Deploy.d.ts +10 -0
- package/lib/Deploy.d.ts.map +1 -0
- package/lib/Deploy.js +12 -0
- package/lib/Deploy.js.map +1 -0
- package/lib/Destroy.d.ts +8 -3
- package/lib/Destroy.d.ts.map +1 -1
- package/lib/Destroy.js +8 -8
- package/lib/Destroy.js.map +1 -1
- package/lib/GitHub/Providers.d.ts +3 -0
- package/lib/GitHub/Providers.d.ts.map +1 -0
- package/lib/GitHub/Providers.js +5 -0
- package/lib/GitHub/Providers.js.map +1 -0
- package/lib/GitHub/index.d.ts +1 -0
- package/lib/GitHub/index.d.ts.map +1 -1
- package/lib/GitHub/index.js +1 -0
- package/lib/GitHub/index.js.map +1 -1
- package/lib/Http.d.ts +1 -1
- package/lib/Http.d.ts.map +1 -1
- package/lib/Http.js +7 -3
- package/lib/Http.js.map +1 -1
- package/lib/Kubernetes/client.d.ts +10 -10
- package/lib/Output.d.ts.map +1 -1
- package/lib/Output.js +4 -0
- package/lib/Output.js.map +1 -1
- package/lib/Plan.d.ts.map +1 -1
- package/lib/Plan.js +4 -0
- package/lib/Plan.js.map +1 -1
- package/lib/Platform.d.ts +2 -2
- package/lib/Platform.d.ts.map +1 -1
- package/lib/Platform.js.map +1 -1
- package/lib/Provider.d.ts.map +1 -1
- package/lib/Provider.js +0 -1
- package/lib/Provider.js.map +1 -1
- package/lib/Stack.d.ts +24 -12
- package/lib/Stack.d.ts.map +1 -1
- package/lib/Stack.js +47 -10
- package/lib/Stack.js.map +1 -1
- package/lib/State/HttpStateApi.d.ts +96 -0
- package/lib/State/HttpStateApi.d.ts.map +1 -0
- package/lib/State/HttpStateApi.js +96 -0
- package/lib/State/HttpStateApi.js.map +1 -0
- package/lib/State/HttpStateStore.d.ts +10 -0
- package/lib/State/HttpStateStore.d.ts.map +1 -0
- package/lib/State/HttpStateStore.js +102 -0
- package/lib/State/HttpStateStore.js.map +1 -0
- package/lib/State/InMemoryState.d.ts +2 -30
- package/lib/State/InMemoryState.d.ts.map +1 -1
- package/lib/State/InMemoryState.js +23 -18
- package/lib/State/InMemoryState.js.map +1 -1
- package/lib/State/LocalState.d.ts +4 -2
- package/lib/State/LocalState.d.ts.map +1 -1
- package/lib/State/LocalState.js +9 -41
- package/lib/State/LocalState.js.map +1 -1
- package/lib/State/State.d.ts +11 -4
- package/lib/State/State.d.ts.map +1 -1
- package/lib/State/State.js +1 -1
- package/lib/State/State.js.map +1 -1
- package/lib/State/StateEncoding.d.ts +31 -0
- package/lib/State/StateEncoding.d.ts.map +1 -0
- package/lib/State/StateEncoding.js +82 -0
- package/lib/State/StateEncoding.js.map +1 -0
- package/lib/State/Sync.d.ts +23 -0
- package/lib/State/Sync.d.ts.map +1 -0
- package/lib/State/Sync.js +49 -0
- package/lib/State/Sync.js.map +1 -0
- package/lib/State/index.d.ts +4 -0
- package/lib/State/index.d.ts.map +1 -1
- package/lib/State/index.js +4 -0
- package/lib/State/index.js.map +1 -1
- package/lib/Test/Bun.d.ts +13 -17
- package/lib/Test/Bun.d.ts.map +1 -1
- package/lib/Test/Bun.js +17 -36
- package/lib/Test/Bun.js.map +1 -1
- package/lib/Test/TestState.d.ts +1 -2
- package/lib/Test/TestState.d.ts.map +1 -1
- package/lib/Test/TestState.js +5 -9
- package/lib/Test/TestState.js.map +1 -1
- package/lib/Test/Vitest.d.ts +3 -2
- package/lib/Test/Vitest.d.ts.map +1 -1
- package/lib/Test/Vitest.js +20 -11
- package/lib/Test/Vitest.js.map +1 -1
- package/lib/Test/index.d.ts +0 -1
- package/lib/Test/index.d.ts.map +1 -1
- package/lib/Test/index.js +0 -1
- package/lib/Test/index.js.map +1 -1
- package/lib/Util/FileLogger.d.ts +2 -2
- package/lib/Util/FileLogger.d.ts.map +1 -1
- package/lib/Util/FileLogger.js +2 -2
- package/lib/Util/FileLogger.js.map +1 -1
- package/lib/cli/index.d.ts +10 -5
- package/lib/cli/index.d.ts.map +1 -1
- package/lib/cli/index.js +2 -43045
- package/lib/cli/index.js.map +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/package.json +30 -15
- package/src/AWS/ECS/Task.ts +4 -4
- package/src/AWS/Lambda/Function.ts +2 -3
- package/src/AWS/SQS/QueueSink.ts +1 -2
- package/src/Agent/coding/bash.ts +2 -3
- package/src/Agent/coding/glob.ts +1 -2
- package/src/Agent/coding/grep.ts +1 -2
- package/src/AlchemyContext.ts +27 -0
- package/src/Auth/AuthProvider.ts +1 -1
- package/src/Auth/index.ts +3 -1
- package/src/{Test/TestCli.ts → Cli/LoggingCli.ts} +2 -2
- package/src/Cli/index.ts +4 -1
- package/src/Cloudflare/ApiToken/AccountApiToken.ts +197 -0
- package/src/Cloudflare/ApiToken/Common.ts +148 -0
- package/src/Cloudflare/ApiToken/PermissionGroups.ts +1910 -0
- package/src/Cloudflare/ApiToken/UserApiToken.ts +168 -0
- package/src/Cloudflare/ApiToken/index.ts +4 -0
- package/src/Cloudflare/Auth/AuthProvider.ts +2 -2
- package/src/Cloudflare/Container/ContainerApplication.ts +8 -2
- package/src/Cloudflare/EdgeSession.ts +144 -0
- package/src/Cloudflare/Providers.ts +5 -0
- package/src/Cloudflare/StateStore/Api.ts +159 -0
- package/src/Cloudflare/StateStore/State.ts +318 -0
- package/src/Cloudflare/StateStore/Store.ts +290 -0
- package/src/Cloudflare/StateStore/Token.ts +68 -0
- package/src/Cloudflare/StateStore/index.ts +1 -0
- package/src/Cloudflare/Workers/DurableObjectNamespace.ts +1 -6
- package/src/Cloudflare/Workers/HttpServer.ts +1 -1
- package/src/Cloudflare/Workers/Worker.ts +7 -7
- package/src/Cloudflare/index.ts +4 -1
- package/src/Deploy.ts +25 -0
- package/src/Destroy.ts +25 -13
- package/src/GitHub/Providers.ts +6 -0
- package/src/GitHub/index.ts +1 -0
- package/src/Http.ts +31 -16
- package/src/Output.ts +3 -2
- package/src/Plan.ts +3 -0
- package/src/Platform.ts +8 -2
- package/src/Provider.ts +0 -2
- package/src/Stack.ts +146 -45
- package/src/State/HttpStateApi.ts +155 -0
- package/src/State/HttpStateStore.ts +156 -0
- package/src/State/InMemoryState.ts +15 -17
- package/src/State/LocalState.ts +13 -45
- package/src/State/State.ts +13 -4
- package/src/State/StateEncoding.ts +83 -0
- package/src/State/Sync.ts +89 -0
- package/src/State/index.ts +4 -0
- package/src/Test/Bun.ts +31 -89
- package/src/Test/TestState.ts +6 -8
- package/src/Test/Vitest.ts +32 -19
- package/src/Test/index.ts +0 -1
- package/src/Util/FileLogger.ts +2 -2
- package/src/index.ts +2 -1
- package/lib/Config.d.ts +0 -20
- package/lib/Config.d.ts.map +0 -1
- package/lib/Config.js +0 -22
- package/lib/Config.js.map +0 -1
- package/lib/Observability.d.ts +0 -2
- package/lib/Observability.d.ts.map +0 -1
- package/lib/Observability.js +0 -1
- package/lib/Observability.js.map +0 -1
- package/lib/Test/TestCli.d.ts +0 -4
- package/lib/Test/TestCli.d.ts.map +0 -1
- package/lib/Test/TestCli.js.map +0 -1
- package/src/Config.ts +0 -31
- package/src/Observability.ts +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Credentials } from "@distilled.cloud/cloudflare/Credentials";
|
|
2
|
+
import * as workers from "@distilled.cloud/cloudflare/workers";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
5
|
+
import { CloudflareEnvironment } from "./CloudflareEnvironment.ts";
|
|
6
|
+
/**
|
|
7
|
+
* A "remote binding" as understood by the Cloudflare Workers
|
|
8
|
+
* edge-preview API. We reuse the distilled request schema so the
|
|
9
|
+
* shape tracks Cloudflare's surface.
|
|
10
|
+
*/
|
|
11
|
+
type EdgeMetadata = NonNullable<NonNullable<workers.CreateScriptEdgePreviewRequest["metadata"]>>;
|
|
12
|
+
export type EdgeBinding = NonNullable<NonNullable<EdgeMetadata["bindings"]>>[number];
|
|
13
|
+
declare const EdgeSessionError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
14
|
+
readonly _tag: "EdgeSessionError";
|
|
15
|
+
} & Readonly<A>;
|
|
16
|
+
export declare class EdgeSessionError extends EdgeSessionError_base<{
|
|
17
|
+
readonly message: string;
|
|
18
|
+
readonly cause?: unknown;
|
|
19
|
+
}> {
|
|
20
|
+
}
|
|
21
|
+
export interface EdgeSessionOptions {
|
|
22
|
+
/**
|
|
23
|
+
* Name used as the preview script identifier on Cloudflare's side.
|
|
24
|
+
* Shows up as the hostname: `{scriptName}.{subdomain}.workers.dev`.
|
|
25
|
+
*/
|
|
26
|
+
readonly scriptName: string;
|
|
27
|
+
/** ES-module worker script(s). The first file is the entrypoint. */
|
|
28
|
+
readonly files: ReadonlyArray<File>;
|
|
29
|
+
/** Remote bindings attached to the preview worker. */
|
|
30
|
+
readonly bindings: ReadonlyArray<EdgeBinding>;
|
|
31
|
+
/** @default "2025-04-28" */
|
|
32
|
+
readonly compatibilityDate?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Information needed to call a running edge-preview worker. Attach
|
|
36
|
+
* `headers` (including the `cf-workers-preview-token`) and fetch
|
|
37
|
+
* `url` or a path under it.
|
|
38
|
+
*/
|
|
39
|
+
export interface EdgeSessionHandle {
|
|
40
|
+
readonly url: string;
|
|
41
|
+
readonly headers: Record<string, string>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Upload a short-lived preview worker to Cloudflare's edge and
|
|
45
|
+
* return its URL plus the headers needed to reach it.
|
|
46
|
+
*
|
|
47
|
+
* Preview workers are ephemeral: they live only while a client keeps
|
|
48
|
+
* sending requests with the returned preview token and expire soon
|
|
49
|
+
* after. Nothing is deployed or billed.
|
|
50
|
+
*/
|
|
51
|
+
export declare const createEdgeSession: (options: EdgeSessionOptions) => Effect.Effect<EdgeSessionHandle, EdgeSessionError, CloudflareEnvironment | HttpClient.HttpClient | Credentials>;
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=EdgeSession.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EdgeSession.d.ts","sourceRoot":"","sources":["../../src/Cloudflare/EdgeSession.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AACtE,OAAO,KAAK,OAAO,MAAM,qCAAqC,CAAC;AAE/D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,UAAU,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE;;;;GAIG;AACH,KAAK,YAAY,GAAG,WAAW,CAC7B,WAAW,CAAC,OAAO,CAAC,8BAA8B,CAAC,UAAU,CAAC,CAAC,CAChE,CAAC;AACF,MAAM,MAAM,WAAW,GAAG,WAAW,CACnC,WAAW,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,CACtC,CAAC,MAAM,CAAC,CAAC;;;;AAEV,qBAAa,gBAAiB,SAAQ,sBAAqC;IACzE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;CAAG;AAEL,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;IACpC,sDAAsD;IACtD,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAC9C,4BAA4B;IAC5B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACrC;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAsED;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAC5B,SAAS,kBAAkB,KAC1B,MAAM,CAAC,MAAM,CACd,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,GAAG,UAAU,CAAC,UAAU,GAAG,WAAW,CAcK,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Credentials } from "@distilled.cloud/cloudflare/Credentials";
|
|
2
|
+
import * as workers from "@distilled.cloud/cloudflare/workers";
|
|
3
|
+
import * as Data from "effect/Data";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
6
|
+
import { CloudflareEnvironment } from "./CloudflareEnvironment.js";
|
|
7
|
+
export class EdgeSessionError extends Data.TaggedError("EdgeSessionError") {
|
|
8
|
+
}
|
|
9
|
+
const DEFAULT_COMPATIBILITY_DATE = "2025-04-28";
|
|
10
|
+
const wrap = (effect, message) => effect.pipe(Effect.mapError((cause) => new EdgeSessionError({ message, cause })));
|
|
11
|
+
/**
|
|
12
|
+
* Ask Cloudflare for a preview upload token. Some accounts return an
|
|
13
|
+
* `exchangeUrl` that must be GET'd to swap the session token for a
|
|
14
|
+
* short-lived upload token; when present we do the swap, falling back
|
|
15
|
+
* to the original if the exchange fails.
|
|
16
|
+
*/
|
|
17
|
+
const createUploadToken = Effect.gen(function* () {
|
|
18
|
+
const env = yield* CloudflareEnvironment;
|
|
19
|
+
const http = yield* HttpClient.HttpClient;
|
|
20
|
+
const createSubdomainEdgePreviewSession = yield* workers.createSubdomainEdgePreviewSession;
|
|
21
|
+
const { token, exchangeUrl } = yield* createSubdomainEdgePreviewSession({
|
|
22
|
+
accountId: env.accountId,
|
|
23
|
+
});
|
|
24
|
+
if (!exchangeUrl)
|
|
25
|
+
return token;
|
|
26
|
+
const json = yield* http.get(exchangeUrl).pipe(Effect.flatMap((r) => r.json), Effect.timeout(30_000), Effect.catch(() => Effect.succeed(null)));
|
|
27
|
+
if (typeof json === "object" &&
|
|
28
|
+
json !== null &&
|
|
29
|
+
"token" in json &&
|
|
30
|
+
typeof json.token === "string") {
|
|
31
|
+
return json.token;
|
|
32
|
+
}
|
|
33
|
+
return token;
|
|
34
|
+
});
|
|
35
|
+
const uploadScript = (options, uploadToken) => Effect.gen(function* () {
|
|
36
|
+
const env = yield* CloudflareEnvironment;
|
|
37
|
+
const createScriptEdgePreview = yield* workers.createScriptEdgePreview;
|
|
38
|
+
return yield* createScriptEdgePreview({
|
|
39
|
+
accountId: env.accountId,
|
|
40
|
+
scriptName: options.scriptName,
|
|
41
|
+
cfPreviewUploadConfigToken: uploadToken,
|
|
42
|
+
wranglerSessionConfig: { workersDev: true, minimalMode: true },
|
|
43
|
+
metadata: {
|
|
44
|
+
compatibilityDate: options.compatibilityDate ?? DEFAULT_COMPATIBILITY_DATE,
|
|
45
|
+
bindings: options.bindings,
|
|
46
|
+
mainModule: options.files[0].name,
|
|
47
|
+
},
|
|
48
|
+
// Distilled's generated schema expects a mutable array; our
|
|
49
|
+
// public surface keeps `readonly` so callers can pass a frozen
|
|
50
|
+
// tuple without a cast on their side.
|
|
51
|
+
files: options.files.slice(),
|
|
52
|
+
}).pipe(Effect.timeout(30_000));
|
|
53
|
+
});
|
|
54
|
+
const workerHost = (scriptName) => Effect.gen(function* () {
|
|
55
|
+
const env = yield* CloudflareEnvironment;
|
|
56
|
+
const getSubdomain = yield* workers.getSubdomain;
|
|
57
|
+
const { subdomain } = yield* getSubdomain({ accountId: env.accountId });
|
|
58
|
+
return `${scriptName}.${subdomain}.workers.dev`;
|
|
59
|
+
});
|
|
60
|
+
/**
|
|
61
|
+
* Upload a short-lived preview worker to Cloudflare's edge and
|
|
62
|
+
* return its URL plus the headers needed to reach it.
|
|
63
|
+
*
|
|
64
|
+
* Preview workers are ephemeral: they live only while a client keeps
|
|
65
|
+
* sending requests with the returned preview token and expire soon
|
|
66
|
+
* after. Nothing is deployed or billed.
|
|
67
|
+
*/
|
|
68
|
+
export const createEdgeSession = (options) => Effect.gen(function* () {
|
|
69
|
+
const [{ previewToken }, host] = yield* Effect.all([
|
|
70
|
+
createUploadToken.pipe(Effect.flatMap((t) => uploadScript(options, t))),
|
|
71
|
+
workerHost(options.scriptName),
|
|
72
|
+
], { concurrency: "unbounded" });
|
|
73
|
+
return {
|
|
74
|
+
url: `https://${host}`,
|
|
75
|
+
headers: { "cf-workers-preview-token": previewToken },
|
|
76
|
+
};
|
|
77
|
+
}).pipe((e) => wrap(e, "Failed to create edge preview session"));
|
|
78
|
+
//# sourceMappingURL=EdgeSession.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EdgeSession.js","sourceRoot":"","sources":["../../src/Cloudflare/EdgeSession.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,5 +12,5 @@ export type ProviderRequirements = Layer.Services<ReturnType<typeof providers>>;
|
|
|
12
12
|
/**
|
|
13
13
|
* Cloudflare providers, bindings, and credentials for Worker-based stacks.
|
|
14
14
|
*/
|
|
15
|
-
export declare const providers: () => Layer.Layer<Provider.Provider<Random> | Provider.Provider<Command> | Containers.Credentials | CloudflareEnvironment.CloudflareEnvironment | Providers, never, import("
|
|
15
|
+
export declare const providers: () => Layer.Layer<Provider.Provider<Random> | Provider.Provider<Command> | Containers.Credentials | CloudflareEnvironment.CloudflareEnvironment | Providers, never, import("../AlchemyContext.ts").AlchemyContext | import("effect/Path").Path | import("effect/FileSystem").FileSystem | import("effect/Scope").Scope | import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner | import("effect/unstable/http/HttpClient").HttpClient | import("../Auth/AuthProvider.ts").AuthProviders | import("../Stage.ts").Stage | import("../Stack.ts").Stack>;
|
|
16
16
|
//# sourceMappingURL=Providers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Providers.d.ts","sourceRoot":"","sources":["../../src/Cloudflare/Providers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAE9C,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAkB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"Providers.d.ts","sourceRoot":"","sources":["../../src/Cloudflare/Providers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAE9C,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAkB,MAAM,cAAc,CAAC;AAGtD,OAAO,KAAK,qBAAqB,MAAM,4BAA4B,CAAC;AACpE,OAAO,KAAK,UAAU,MAAM,sBAAsB,CAAC;AAUnD,OAAO,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;;AAEtE,qBAAa,SAAU,SAAQ,cAE9B;CAAG;AAEJ,MAAM,MAAM,oBAAoB,GAAG,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC;AAEhF;;GAEG;AACH,eAAO,MAAM,SAAS,0iBAyDnB,CAAC"}
|
|
@@ -3,6 +3,7 @@ import { Command } from "../Build/Command.js";
|
|
|
3
3
|
import * as Build from "../Build/index.js";
|
|
4
4
|
import * as Provider from "../Provider.js";
|
|
5
5
|
import { Random, RandomProvider } from "../Random.js";
|
|
6
|
+
import * as ApiToken from "./ApiToken/index.js";
|
|
6
7
|
import { CloudflareAuth } from "./Auth/AuthProvider.js";
|
|
7
8
|
import * as CloudflareEnvironment from "./CloudflareEnvironment.js";
|
|
8
9
|
import * as Containers from "./Container/index.js";
|
|
@@ -21,6 +22,8 @@ export class Providers extends Provider.ProviderCollection()("Cloudflare") {
|
|
|
21
22
|
* Cloudflare providers, bindings, and credentials for Worker-based stacks.
|
|
22
23
|
*/
|
|
23
24
|
export const providers = () => Layer.effect(Providers, Provider.collection([
|
|
25
|
+
ApiToken.AccountApiToken,
|
|
26
|
+
ApiToken.UserApiToken,
|
|
24
27
|
Command,
|
|
25
28
|
Containers.Container,
|
|
26
29
|
D1.D1ConnectionPolicy,
|
|
@@ -40,5 +43,5 @@ export const providers = () => Layer.effect(Providers, Provider.collection([
|
|
|
40
43
|
Workers.FetchPolicy,
|
|
41
44
|
Workers.Worker,
|
|
42
45
|
Workflows.WorkflowResource,
|
|
43
|
-
])).pipe(Layer.provide(Layer.mergeAll(Containers.ContainerProvider(), D1.D1ConnectionPolicyLive, D1.DatabaseProvider(), KV.KVNamespaceBindingPolicyLive, KV.KVNamespaceProvider(), Queue.QueueBindingPolicyLive, Queue.QueueProvider(), Queue.QueueConsumerProvider(), R2.R2BucketBindingPolicyLive, R2.R2BucketProvider(), SecretsStore.SecretBindingPolicyLive, SecretsStore.SecretsStoreProvider(), SecretsStore.StoreSecretProvider(), Workers.BindWorkerPolicyLive, Workers.FetchPolicyLive, Workers.WorkerProvider(), Workflows.WorkflowProvider())), Layer.provideMerge(Layer.mergeAll(Build.CommandProvider(), RandomProvider())), Layer.provideMerge(Credentials.fromAuthProvider()), Layer.provideMerge(CloudflareEnvironment.fromProfile()), Layer.provideMerge(CloudflareAuth), Layer.orDie);
|
|
46
|
+
])).pipe(Layer.provide(Layer.mergeAll(ApiToken.AccountApiTokenProvider(), ApiToken.UserApiTokenProvider(), Containers.ContainerProvider(), D1.D1ConnectionPolicyLive, D1.DatabaseProvider(), KV.KVNamespaceBindingPolicyLive, KV.KVNamespaceProvider(), Queue.QueueBindingPolicyLive, Queue.QueueProvider(), Queue.QueueConsumerProvider(), R2.R2BucketBindingPolicyLive, R2.R2BucketProvider(), SecretsStore.SecretBindingPolicyLive, SecretsStore.SecretsStoreProvider(), SecretsStore.StoreSecretProvider(), Workers.BindWorkerPolicyLive, Workers.FetchPolicyLive, Workers.WorkerProvider(), Workflows.WorkflowProvider())), Layer.provideMerge(Layer.mergeAll(Build.CommandProvider(), RandomProvider())), Layer.provideMerge(Credentials.fromAuthProvider()), Layer.provideMerge(CloudflareEnvironment.fromProfile()), Layer.provideMerge(CloudflareAuth), Layer.orDie);
|
|
44
47
|
//# sourceMappingURL=Providers.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Providers.js","sourceRoot":"","sources":["../../src/Cloudflare/Providers.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Providers.js","sourceRoot":"","sources":["../../src/Cloudflare/Providers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
export declare const STATE_STORE_SCRIPT_NAME: "alchemy-state-store";
|
|
3
|
+
declare const _default: Effect.Effect<import("effect/Pipeable").Pipeable & import("../../Resource.ts").ResourceLike<"Cloudflare.Worker", import("../index.ts").WorkerProps<any, import("../index.ts").WorkerAssetsConfig | undefined>, {
|
|
4
|
+
workerId: string;
|
|
5
|
+
workerName: string;
|
|
6
|
+
logpush: boolean | undefined;
|
|
7
|
+
url: string | undefined;
|
|
8
|
+
tags: string[] | undefined;
|
|
9
|
+
durableObjectNamespaces: Record<string, string>;
|
|
10
|
+
accountId: string;
|
|
11
|
+
domains: {
|
|
12
|
+
hostname: string;
|
|
13
|
+
id: string;
|
|
14
|
+
zoneId: string;
|
|
15
|
+
}[];
|
|
16
|
+
hash?: {
|
|
17
|
+
assets: string | undefined;
|
|
18
|
+
bundle: string | undefined;
|
|
19
|
+
input: string | undefined;
|
|
20
|
+
};
|
|
21
|
+
}, {
|
|
22
|
+
bindings?: import("../index.ts").WorkerBinding[];
|
|
23
|
+
containers?: {
|
|
24
|
+
className: string;
|
|
25
|
+
}[];
|
|
26
|
+
}, import("../Providers.ts").Providers> & {
|
|
27
|
+
bind(sid: string, binding: import("../../Input.ts").Input<{
|
|
28
|
+
bindings?: import("../index.ts").WorkerBinding[];
|
|
29
|
+
containers?: {
|
|
30
|
+
className: string;
|
|
31
|
+
}[];
|
|
32
|
+
}>): Effect.Effect<void>;
|
|
33
|
+
bind(template: TemplateStringsArray, ...args: any[]): (binding: import("../../Input.ts").Input<{
|
|
34
|
+
bindings?: import("../index.ts").WorkerBinding[];
|
|
35
|
+
containers?: {
|
|
36
|
+
className: string;
|
|
37
|
+
}[];
|
|
38
|
+
}>) => Effect.Effect<void>;
|
|
39
|
+
} & {
|
|
40
|
+
workerId: import("../../Output.ts").Output<string, never>;
|
|
41
|
+
workerName: import("../../Output.ts").Output<string, never>;
|
|
42
|
+
logpush: import("../../Output.ts").Output<boolean | undefined, never>;
|
|
43
|
+
url: import("../../Output.ts").Output<string | undefined, never>;
|
|
44
|
+
tags: import("../../Output.ts").Output<string[] | undefined, never>;
|
|
45
|
+
durableObjectNamespaces: import("../../Output.ts").Output<Record<string, string>, never>;
|
|
46
|
+
accountId: import("../../Output.ts").Output<string, never>;
|
|
47
|
+
domains: import("../../Output.ts").Output<{
|
|
48
|
+
hostname: string;
|
|
49
|
+
id: string;
|
|
50
|
+
zoneId: string;
|
|
51
|
+
}[], never>;
|
|
52
|
+
hash: import("../../Output.ts").Output<{
|
|
53
|
+
assets: string | undefined;
|
|
54
|
+
bundle: string | undefined;
|
|
55
|
+
input: string | undefined;
|
|
56
|
+
} | undefined, never>;
|
|
57
|
+
} & import("../../Platform.ts").Rpc<{
|
|
58
|
+
fetch: Effect.Effect<Effect.Effect<import("effect/unstable/http/HttpServerResponse").HttpServerResponse, import("effect/unstable/http/HttpServerError").HttpServerError, import("effect/Scope").Scope | import("effect/unstable/http/HttpServerRequest").HttpServerRequest>, never, import("effect/FileSystem").FileSystem | import("effect/Scope").Scope | import("../index.ts").WorkerEnvironment>;
|
|
59
|
+
}>, never, import("../Providers.ts").Providers>;
|
|
60
|
+
export default _default;
|
|
61
|
+
//# sourceMappingURL=Api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Api.d.ts","sourceRoot":"","sources":["../../../src/Cloudflare/StateStore/Api.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAexC,eAAO,MAAM,uBAAuB,EAAG,qBAA8B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEtE,wBA6GE"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { BearerTokenValidator, StateApi, StateAuthLive, } from "alchemy/State/HttpStateApi";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Layer from "effect/Layer";
|
|
4
|
+
import * as Path from "effect/Path";
|
|
5
|
+
import * as Etag from "effect/unstable/http/Etag";
|
|
6
|
+
import * as HttpPlatform from "effect/unstable/http/HttpPlatform";
|
|
7
|
+
import * as HttpRouter from "effect/unstable/http/HttpRouter";
|
|
8
|
+
import * as HttpApiBuilder from "effect/unstable/httpapi/HttpApiBuilder";
|
|
9
|
+
import * as HttpApiError from "effect/unstable/httpapi/HttpApiError";
|
|
10
|
+
import crypto from "node:crypto";
|
|
11
|
+
import { SecretBindingLive } from "../SecretsStore/SecretBinding.js";
|
|
12
|
+
import * as Secret from "../SecretsStore/Secret.js";
|
|
13
|
+
import { Worker } from "../Workers/Worker.js";
|
|
14
|
+
import Store from "./Store.js";
|
|
15
|
+
import { AuthToken } from "./Token.js";
|
|
16
|
+
export const STATE_STORE_SCRIPT_NAME = "alchemy-state-store";
|
|
17
|
+
export default Worker("Api", {
|
|
18
|
+
name: STATE_STORE_SCRIPT_NAME,
|
|
19
|
+
main: import.meta.path,
|
|
20
|
+
url: true,
|
|
21
|
+
compatibility: {
|
|
22
|
+
flags: ["nodejs_compat"],
|
|
23
|
+
date: "2026-03-17",
|
|
24
|
+
},
|
|
25
|
+
}, Effect.gen(function* () {
|
|
26
|
+
const secret = yield* Secret.Secret.bind(AuthToken);
|
|
27
|
+
const store = yield* Store;
|
|
28
|
+
const bearerTokenValidator = Layer.effect(BearerTokenValidator, secret.get().pipe(Effect.map((expected) => BearerTokenValidator.of({
|
|
29
|
+
validate: (token) => !!expected && timingSafeEqual(token, expected)
|
|
30
|
+
? Effect.void
|
|
31
|
+
: Effect.fail(new HttpApiError.Unauthorized()),
|
|
32
|
+
})), Effect.orDie));
|
|
33
|
+
const stateApi = HttpApiBuilder.group(StateApi, "state", (handlers) => handlers
|
|
34
|
+
.handle("listStacks", () => store.getByName(Store.ROOT_DO_NAME).listStacks())
|
|
35
|
+
.handle("listStages", ({ params }) => store.getByName(params.stack).listStages())
|
|
36
|
+
.handle("listResources", ({ params }) => store.getByName(params.stack).listResources({ stage: params.stage }))
|
|
37
|
+
.handle("getState", ({ params }) => store
|
|
38
|
+
.getByName(params.stack)
|
|
39
|
+
.get({ stage: params.stage, fqn: decodeURIComponent(params.fqn) }))
|
|
40
|
+
.handle("setState", ({ params, payload }) => store
|
|
41
|
+
.getByName(params.stack)
|
|
42
|
+
.set({
|
|
43
|
+
stage: params.stage,
|
|
44
|
+
fqn: decodeURIComponent(params.fqn),
|
|
45
|
+
value: payload,
|
|
46
|
+
})
|
|
47
|
+
.pipe(Effect.tap(() => store
|
|
48
|
+
.getByName(Store.ROOT_DO_NAME)
|
|
49
|
+
.registerStack({ stack: params.stack }))))
|
|
50
|
+
.handle("deleteState", ({ params }) =>
|
|
51
|
+
// The DO method is `remove`, not `delete` — `delete` is
|
|
52
|
+
// reserved by Cloudflare's RPC stub proxy.
|
|
53
|
+
store
|
|
54
|
+
.getByName(params.stack)
|
|
55
|
+
.remove({
|
|
56
|
+
stage: params.stage,
|
|
57
|
+
fqn: decodeURIComponent(params.fqn),
|
|
58
|
+
})
|
|
59
|
+
.pipe(Effect.asVoid))
|
|
60
|
+
.handle("getReplacedResources", ({ params }) => store
|
|
61
|
+
.getByName(params.stack)
|
|
62
|
+
.getReplacedResources({ stage: params.stage }))
|
|
63
|
+
.handle("deleteStack", ({ params, query }) => store
|
|
64
|
+
.getByName(params.stack)
|
|
65
|
+
.deleteStack(query.stage === undefined ? {} : { stage: query.stage })
|
|
66
|
+
.pipe(Effect.flatMap(() => query.stage === undefined
|
|
67
|
+
? store
|
|
68
|
+
.getByName(Store.ROOT_DO_NAME)
|
|
69
|
+
.unregisterStack({ stack: params.stack })
|
|
70
|
+
: Effect.void), Effect.asVoid)));
|
|
71
|
+
return {
|
|
72
|
+
fetch: HttpApiBuilder.layer(StateApi).pipe(Layer.provide(stateApi), Layer.provide(StateAuthLive), Layer.provide(bearerTokenValidator),
|
|
73
|
+
// The state-store worker never serves files, so HttpPlatform's
|
|
74
|
+
// file-response surface is stubbed.
|
|
75
|
+
Layer.provide([Etag.layer, HttpPlatformStub, Path.layer]), HttpRouter.toHttpEffect),
|
|
76
|
+
};
|
|
77
|
+
}).pipe(Effect.provide(Layer.mergeAll(SecretBindingLive))));
|
|
78
|
+
/**
|
|
79
|
+
* Stub `HttpPlatform` for the worker. The state-store API never
|
|
80
|
+
* issues file responses, so both surface methods die if invoked. Lets
|
|
81
|
+
* us avoid pulling in a `FileSystem` dependency that workers don't
|
|
82
|
+
* have.
|
|
83
|
+
*/
|
|
84
|
+
const HttpPlatformStub = Layer.succeed(HttpPlatform.HttpPlatform, {
|
|
85
|
+
fileResponse: () => Effect.die("HttpPlatform.fileResponse not supported"),
|
|
86
|
+
fileWebResponse: () => Effect.die("HttpPlatform.fileWebResponse not supported"),
|
|
87
|
+
});
|
|
88
|
+
/**
|
|
89
|
+
* Timing-safe string comparison using the Workers runtime's built-in
|
|
90
|
+
* `crypto.subtle.timingSafeEqual`.
|
|
91
|
+
*
|
|
92
|
+
* @see https://developers.cloudflare.com/workers/examples/protect-against-timing-attacks/
|
|
93
|
+
*/
|
|
94
|
+
const timingSafeEqual = (a, b) => {
|
|
95
|
+
const encoder = new TextEncoder();
|
|
96
|
+
const aBytes = encoder.encode(a);
|
|
97
|
+
const bBytes = encoder.encode(b);
|
|
98
|
+
if (aBytes.byteLength !== bBytes.byteLength)
|
|
99
|
+
return false;
|
|
100
|
+
// @ts-expect-error - TODO(sam)
|
|
101
|
+
return crypto.subtle.timingSafeEqual(aBytes, bBytes);
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=Api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Api.js","sourceRoot":"","sources":["../../../src/Cloudflare/StateStore/Api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as SecretsStore from "@distilled.cloud/cloudflare/secrets-store";
|
|
2
|
+
import * as workers from "@distilled.cloud/cloudflare/workers";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Layer from "effect/Layer";
|
|
5
|
+
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
6
|
+
import * as Config from "effect/Config";
|
|
7
|
+
import { AuthError } from "../../Auth/AuthProvider.ts";
|
|
8
|
+
import * as Cloudflare from "../../Cloudflare/Providers.ts";
|
|
9
|
+
import * as CloudflareEnvironment from "../CloudflareEnvironment.ts";
|
|
10
|
+
import { AlchemyContext } from "../../AlchemyContext.ts";
|
|
11
|
+
import { State } from "../../State/State.ts";
|
|
12
|
+
export declare const state: (props?: {
|
|
13
|
+
/**
|
|
14
|
+
* The name of the script to use for the state store.
|
|
15
|
+
* @default "alchemy-state-store"
|
|
16
|
+
*/
|
|
17
|
+
workerName?: string;
|
|
18
|
+
}) => Layer.Layer<State | Cloudflare.Credentials | CloudflareEnvironment.CloudflareEnvironment, never, AlchemyContext | import("effect/Path").Path | import("effect/FileSystem").FileSystem | HttpClient.HttpClient | import("../../Auth/AuthProvider.ts").AuthProviders | import("../../Cli/Cli.ts").Cli>;
|
|
19
|
+
/**
|
|
20
|
+
* Log in to a Cloudflare-deployed HTTP state-store.
|
|
21
|
+
*
|
|
22
|
+
* 1. Find the single account-wide Secrets Store.
|
|
23
|
+
* 2. Upload a short-lived edge-preview worker that binds the
|
|
24
|
+
* auth-token secret and returns its value.
|
|
25
|
+
* 3. Derive the state-store worker URL from
|
|
26
|
+
* {@link STATE_STORE_SCRIPT_NAME} and the account's workers.dev
|
|
27
|
+
* subdomain.
|
|
28
|
+
* 4. Persist `{ url, token }` under the `http-state-store`
|
|
29
|
+
* credentials file.
|
|
30
|
+
*
|
|
31
|
+
* Requirements are covered by the Cloudflare provider stack —
|
|
32
|
+
* `CloudflareEnvironment`, `Credentials`, `HttpClient`, and
|
|
33
|
+
* `FileSystem`.
|
|
34
|
+
*/
|
|
35
|
+
export declare const loginWithCloudflare: () => Effect.Effect<{
|
|
36
|
+
url: string;
|
|
37
|
+
authToken: string;
|
|
38
|
+
}, Config.ConfigError | AuthError | import("@distilled.cloud/cloudflare").Unauthorized | import("@distilled.cloud/cloudflare").TooManyRequests | import("@distilled.cloud/cloudflare").InternalServerError | import("@distilled.cloud/cloudflare").BadGateway | import("@distilled.cloud/cloudflare").ServiceUnavailable | import("@distilled.cloud/cloudflare").GatewayTimeout | import("@distilled.cloud/cloudflare").CloudflareHttpError | import("@distilled.cloud/cloudflare").CloudflareParseError | import("@distilled.cloud/cloudflare").UnknownCloudflareError | workers.InvalidRoute | SecretsStore.InvalidAccountId, import("effect/FileSystem").FileSystem | HttpClient.HttpClient | Cloudflare.Credentials | CloudflareEnvironment.CloudflareEnvironment>;
|
|
39
|
+
//# sourceMappingURL=State.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"State.d.ts","sourceRoot":"","sources":["../../../src/Cloudflare/StateStore/State.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,2CAA2C,CAAC;AAC1E,OAAO,KAAK,OAAO,MAAM,qCAAqC,CAAC;AAE/D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,OAAO,KAAK,UAAU,MAAM,iCAAiC,CAAC;AAE9D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAGvD,OAAO,KAAK,UAAU,MAAM,+BAA+B,CAAC;AAO5D,OAAO,KAAK,qBAAqB,MAAM,6BAA6B,CAAC;AAGrE,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,OAAO,EAAE,KAAK,EAAqB,MAAM,sBAAsB,CAAC;AAWhE,eAAO,MAAM,KAAK,GAAI,QAAQ;IAC5B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,ySAuGE,CAAC;AAoCJ;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,mBAAmB;;;suBA6D7B,CAAC"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import * as SecretsStore from "@distilled.cloud/cloudflare/secrets-store";
|
|
2
|
+
import * as workers from "@distilled.cloud/cloudflare/workers";
|
|
3
|
+
import * as Output from "alchemy/Output";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as Layer from "effect/Layer";
|
|
6
|
+
import * as Redacted from "effect/Redacted";
|
|
7
|
+
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
8
|
+
import * as Config from "effect/Config";
|
|
9
|
+
import { AuthError } from "../../Auth/AuthProvider.js";
|
|
10
|
+
import { readCredentials, writeCredentials } from "../../Auth/Credentials.js";
|
|
11
|
+
import { ALCHEMY_PROFILE } from "../../Auth/Profile.js";
|
|
12
|
+
import * as Cloudflare from "../../Cloudflare/Providers.js";
|
|
13
|
+
import { deploy } from "../../Deploy.js";
|
|
14
|
+
import * as Alchemy from "../../Stack.js";
|
|
15
|
+
import { makeHttpStateStore, } from "../../State/HttpStateStore.js";
|
|
16
|
+
import * as CloudflareEnvironment from "../CloudflareEnvironment.js";
|
|
17
|
+
import * as Credentials from "../Credentials.js";
|
|
18
|
+
import { AlchemyContext } from "../../AlchemyContext.js";
|
|
19
|
+
import { makeLocalState } from "../../State/LocalState.js";
|
|
20
|
+
import { State } from "../../State/State.js";
|
|
21
|
+
import { syncState } from "../../State/Sync.js";
|
|
22
|
+
import * as Clank from "../../Util/Clank.js";
|
|
23
|
+
import { CloudflareAuth } from "../Auth/AuthProvider.js";
|
|
24
|
+
import { EdgeSessionError, createEdgeSession } from "../EdgeSession.js";
|
|
25
|
+
import Api, { STATE_STORE_SCRIPT_NAME } from "./Api.js";
|
|
26
|
+
import { AuthTokenSecretName, TokenValue } from "./Token.js";
|
|
27
|
+
/** Filename used for stored credentials under the profile directory. */
|
|
28
|
+
const CREDENTIALS_FILE = "cloudflare-state-store";
|
|
29
|
+
export const state = (props) => Layer.effect(State, Effect.gen(function* () {
|
|
30
|
+
const isCI = yield* Config.boolean("CI").pipe(Config.withDefault(false));
|
|
31
|
+
const alchemyContext = yield* AlchemyContext;
|
|
32
|
+
const profileName = yield* ALCHEMY_PROFILE;
|
|
33
|
+
const credentials = yield* readCredentials(profileName, CREDENTIALS_FILE);
|
|
34
|
+
const scriptName = props?.workerName ?? STATE_STORE_SCRIPT_NAME;
|
|
35
|
+
// TODO(sam): support upgrading state store, right now we only deploy once
|
|
36
|
+
if (credentials) {
|
|
37
|
+
// it's in the profile, let's go
|
|
38
|
+
const httpState = yield* makeHttpStateStore(credentials);
|
|
39
|
+
if (alchemyContext.updateStateStore) {
|
|
40
|
+
yield* deployStateStore(scriptName, httpState);
|
|
41
|
+
}
|
|
42
|
+
return httpState;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
// our profile does not contain a reference to the state store, let's try and resolve it
|
|
46
|
+
const { accountId } = yield* CloudflareEnvironment.CloudflareEnvironment;
|
|
47
|
+
const workerExists = yield* workers
|
|
48
|
+
.getScriptSetting({
|
|
49
|
+
accountId,
|
|
50
|
+
scriptName,
|
|
51
|
+
})
|
|
52
|
+
.pipe(Effect.map((setting) => setting !== undefined), Effect.catchTag("WorkerNotFound", () => Effect.succeed(false)));
|
|
53
|
+
if (workerExists) {
|
|
54
|
+
// it exists, so fetch the secret token
|
|
55
|
+
const credentials = yield* loginWithCloudflare();
|
|
56
|
+
if (!isCI) {
|
|
57
|
+
yield* writeCredentials(profileName, CREDENTIALS_FILE, credentials);
|
|
58
|
+
}
|
|
59
|
+
const httpState = yield* makeHttpStateStore(credentials);
|
|
60
|
+
if (alchemyContext.updateStateStore) {
|
|
61
|
+
yield* deployStateStore(scriptName, httpState);
|
|
62
|
+
}
|
|
63
|
+
return httpState;
|
|
64
|
+
}
|
|
65
|
+
else if (isCI) {
|
|
66
|
+
// TODO(sam): do we want to support bootstrapping the state store from CI?
|
|
67
|
+
// for now - just die here
|
|
68
|
+
return yield* Effect.die(new AuthError({
|
|
69
|
+
message: `State store not found for script ${scriptName}. Deploy the state store first.`,
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
const shouldDeploy = yield* Clank.confirm({
|
|
73
|
+
message: "Cloudflare State Store not found. Do you want to deploy it?",
|
|
74
|
+
});
|
|
75
|
+
if (!shouldDeploy) {
|
|
76
|
+
return yield* Effect.die(new Clank.PromptCancelled());
|
|
77
|
+
}
|
|
78
|
+
const { url, authToken, localState } = yield* deployStateStore(scriptName);
|
|
79
|
+
const httpState = yield* makeHttpStateStore({ url, authToken });
|
|
80
|
+
yield* syncState(localState, httpState);
|
|
81
|
+
yield* writeCredentials(profileName, CREDENTIALS_FILE, {
|
|
82
|
+
url,
|
|
83
|
+
authToken,
|
|
84
|
+
});
|
|
85
|
+
yield* localState.deleteStack({
|
|
86
|
+
stack: "CloudflareStateStore",
|
|
87
|
+
stage: scriptName,
|
|
88
|
+
});
|
|
89
|
+
return httpState;
|
|
90
|
+
}
|
|
91
|
+
}).pipe(Effect.orDie)).pipe(Layer.provideMerge(Credentials.fromAuthProvider()), Layer.provideMerge(CloudflareEnvironment.fromProfile()), Layer.provideMerge(CloudflareAuth), Layer.orDie);
|
|
92
|
+
const deployStateStore = (scriptName, state) => Effect.gen(function* () {
|
|
93
|
+
const localState = state ?? (yield* makeLocalState());
|
|
94
|
+
// deploy it with local state (which we will then hoist into the Cloudflare state store)
|
|
95
|
+
const stateLayer = Layer.succeed(State, localState);
|
|
96
|
+
const { url, authToken } = yield* deploy({
|
|
97
|
+
// use the script name as the stage name (so the user can have multiple state stores)
|
|
98
|
+
stage: scriptName,
|
|
99
|
+
stack: Alchemy.Stack("CloudflareStateStore", {
|
|
100
|
+
providers: Cloudflare.providers(),
|
|
101
|
+
state: stateLayer,
|
|
102
|
+
}, Effect.gen(function* () {
|
|
103
|
+
const token = yield* TokenValue;
|
|
104
|
+
const api = yield* Api;
|
|
105
|
+
// Surface the bearer token so tests and clients can authenticate
|
|
106
|
+
// after deploy. The underlying value lives in the Cloudflare
|
|
107
|
+
// Secrets Store; this output carries the same generated string.
|
|
108
|
+
return {
|
|
109
|
+
url: api.url.as(),
|
|
110
|
+
authToken: token.text.pipe(Output.map(Redacted.value)),
|
|
111
|
+
};
|
|
112
|
+
})),
|
|
113
|
+
}).pipe(
|
|
114
|
+
// TODO(sam): we should not need to do this, but types do complain. fix deploy
|
|
115
|
+
Effect.provide(stateLayer));
|
|
116
|
+
return { url, authToken, localState };
|
|
117
|
+
});
|
|
118
|
+
/**
|
|
119
|
+
* Log in to a Cloudflare-deployed HTTP state-store.
|
|
120
|
+
*
|
|
121
|
+
* 1. Find the single account-wide Secrets Store.
|
|
122
|
+
* 2. Upload a short-lived edge-preview worker that binds the
|
|
123
|
+
* auth-token secret and returns its value.
|
|
124
|
+
* 3. Derive the state-store worker URL from
|
|
125
|
+
* {@link STATE_STORE_SCRIPT_NAME} and the account's workers.dev
|
|
126
|
+
* subdomain.
|
|
127
|
+
* 4. Persist `{ url, token }` under the `http-state-store`
|
|
128
|
+
* credentials file.
|
|
129
|
+
*
|
|
130
|
+
* Requirements are covered by the Cloudflare provider stack —
|
|
131
|
+
* `CloudflareEnvironment`, `Credentials`, `HttpClient`, and
|
|
132
|
+
* `FileSystem`.
|
|
133
|
+
*/
|
|
134
|
+
export const loginWithCloudflare = () => Effect.gen(function* () {
|
|
135
|
+
const isCI = yield* Config.boolean("CI").pipe(Config.withDefault(false));
|
|
136
|
+
const profileName = yield* ALCHEMY_PROFILE;
|
|
137
|
+
const { accountId } = yield* CloudflareEnvironment.CloudflareEnvironment;
|
|
138
|
+
// 1. Locate the single Secrets Store on the account.
|
|
139
|
+
const stores = yield* SecretsStore.listStores({ accountId });
|
|
140
|
+
const store = stores.result[0];
|
|
141
|
+
if (!store) {
|
|
142
|
+
return yield* Effect.fail(new AuthError({
|
|
143
|
+
message: "No Secrets Store found on this account. Deploy the state store first.",
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
146
|
+
// 2. Fetch the auth-token value via an edge-preview worker.
|
|
147
|
+
const authToken = yield* readSecretViaEdge(store.id, AuthTokenSecretName);
|
|
148
|
+
// 3. Derive the deployed worker URL.
|
|
149
|
+
const { subdomain } = yield* workers.getSubdomain({ accountId });
|
|
150
|
+
const url = `https://${STATE_STORE_SCRIPT_NAME}.${subdomain}.workers.dev`;
|
|
151
|
+
if (!isCI) {
|
|
152
|
+
// 4. Persist credentials. The profile entry is managed by
|
|
153
|
+
// `loadOrConfigure` when this is invoked through `configure`.
|
|
154
|
+
yield* writeCredentials(profileName, CREDENTIALS_FILE, {
|
|
155
|
+
url,
|
|
156
|
+
authToken: authToken.trim(),
|
|
157
|
+
}).pipe(Effect.mapError((e) => new AuthError({ message: "Failed to write credentials", cause: e })));
|
|
158
|
+
yield* Clank.success(`HTTP state store credentials saved for '${profileName}'.`);
|
|
159
|
+
yield* Clank.info(` url: ${url}`);
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
url,
|
|
163
|
+
authToken: authToken.trim(),
|
|
164
|
+
};
|
|
165
|
+
}).pipe(Effect.catchTag("EdgeSessionError", (e) => Effect.fail(new AuthError({
|
|
166
|
+
message: `Edge-preview secret read failed: ${e.message}`,
|
|
167
|
+
cause: e.cause,
|
|
168
|
+
}))));
|
|
169
|
+
/** Preview script name used by the edge-probe worker. */
|
|
170
|
+
const PROBE_SCRIPT_NAME = "alchemy-state-store-probe";
|
|
171
|
+
/**
|
|
172
|
+
* Tiny ES-module worker that reads `env.SECRET.get()` and echoes it
|
|
173
|
+
* back. Uploaded as an ephemeral edge-preview, called once, then
|
|
174
|
+
* discarded — see {@link readSecretViaEdge}.
|
|
175
|
+
*/
|
|
176
|
+
const SECRET_PROBE_SOURCE = `export default {
|
|
177
|
+
async fetch(_request, env) {
|
|
178
|
+
try {
|
|
179
|
+
const value = await env.SECRET.get();
|
|
180
|
+
return new Response(value ?? "", { status: 200, headers: { "content-type": "text/plain" } });
|
|
181
|
+
} catch (e) {
|
|
182
|
+
return new Response("Error: " + (e && e.message ? e.message : String(e)), { status: 500 });
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
};`;
|
|
186
|
+
/**
|
|
187
|
+
* Upload a tiny edge-preview worker that binds the given Secrets
|
|
188
|
+
* Store secret, call it once, and return the decoded value. The
|
|
189
|
+
* Cloudflare REST API deliberately hides secret values; only worker
|
|
190
|
+
* bindings can resolve them, so this is the out-of-band path.
|
|
191
|
+
*/
|
|
192
|
+
const readSecretViaEdge = (storeId, secretName) => Effect.gen(function* () {
|
|
193
|
+
const http = yield* HttpClient.HttpClient;
|
|
194
|
+
const file = new File([SECRET_PROBE_SOURCE], "worker.js", {
|
|
195
|
+
type: "application/javascript+module",
|
|
196
|
+
});
|
|
197
|
+
const session = yield* createEdgeSession({
|
|
198
|
+
scriptName: PROBE_SCRIPT_NAME,
|
|
199
|
+
files: [file],
|
|
200
|
+
bindings: [
|
|
201
|
+
{ type: "secrets_store_secret", name: "SECRET", secretName, storeId },
|
|
202
|
+
],
|
|
203
|
+
});
|
|
204
|
+
const response = yield* http.get(session.url, {
|
|
205
|
+
headers: session.headers,
|
|
206
|
+
});
|
|
207
|
+
if (response.status !== 200) {
|
|
208
|
+
const body = yield* response.text.pipe(Effect.catch(() => Effect.succeed("")));
|
|
209
|
+
return yield* Effect.fail(new EdgeSessionError({
|
|
210
|
+
message: `Secret probe returned ${response.status}: ${body.slice(0, 200)}`,
|
|
211
|
+
}));
|
|
212
|
+
}
|
|
213
|
+
return yield* response.text;
|
|
214
|
+
}).pipe(Effect.mapError((cause) => cause instanceof EdgeSessionError
|
|
215
|
+
? cause
|
|
216
|
+
: new EdgeSessionError({ message: "Failed to read secret", cause })));
|
|
217
|
+
//# sourceMappingURL=State.js.map
|