alchemy 2.0.0-beta.40 → 2.0.0-beta.41
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/exec.js +16 -3
- package/bin/exec.js.map +1 -1
- package/lib/AWS/EC2/hosted.js +1 -1
- package/lib/AWS/EC2/hosted.js.map +1 -1
- package/lib/AWS/ECS/Task.js +1 -1
- package/lib/AWS/ECS/Task.js.map +1 -1
- 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.js +1 -1
- 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/Apply.d.ts.map +1 -1
- package/lib/Apply.js +41 -7
- package/lib/Apply.js.map +1 -1
- package/lib/Cloudflare/Bridge.d.ts +1 -0
- package/lib/Cloudflare/Bridge.d.ts.map +1 -1
- package/lib/Cloudflare/Bridge.js +1 -0
- package/lib/Cloudflare/Bridge.js.map +1 -1
- package/lib/Cloudflare/Container/ContainerApplication.js +1 -1
- package/lib/Cloudflare/Container/ContainerApplication.js.map +1 -1
- package/lib/Cloudflare/Container/ContainerBinding.d.ts +1 -1
- package/lib/Cloudflare/Container/ContainerBinding.d.ts.map +1 -1
- package/lib/Cloudflare/Container/ContainerBinding.js +1 -1
- package/lib/Cloudflare/Container/ContainerBinding.js.map +1 -1
- package/lib/Cloudflare/Fetcher.d.ts +9 -0
- package/lib/Cloudflare/Fetcher.d.ts.map +1 -1
- package/lib/Cloudflare/Fetcher.js +20 -1
- package/lib/Cloudflare/Fetcher.js.map +1 -1
- package/lib/Cloudflare/Local/SidecarHandlers.d.ts.map +1 -1
- package/lib/Cloudflare/Local/SidecarHandlers.js +1 -1
- package/lib/Cloudflare/Local/SidecarHandlers.js.map +1 -1
- package/lib/Cloudflare/StateStore/Api.d.ts +2 -2
- package/lib/Cloudflare/Website/StaticSite.d.ts +5 -4
- package/lib/Cloudflare/Website/StaticSite.d.ts.map +1 -1
- package/lib/Cloudflare/Website/StaticSite.js.map +1 -1
- package/lib/Cloudflare/Website/Vite.d.ts +4 -4
- package/lib/Cloudflare/Website/Vite.d.ts.map +1 -1
- package/lib/Cloudflare/Website/Vite.js.map +1 -1
- package/lib/Cloudflare/Workers/DurableObjectBridge.d.ts +17 -0
- package/lib/Cloudflare/Workers/DurableObjectBridge.d.ts.map +1 -0
- package/lib/Cloudflare/Workers/DurableObjectBridge.js +86 -0
- package/lib/Cloudflare/Workers/DurableObjectBridge.js.map +1 -0
- package/lib/Cloudflare/Workers/DurableObjectNamespace.d.ts +2 -1
- package/lib/Cloudflare/Workers/DurableObjectNamespace.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/DurableObjectNamespace.js +10 -39
- package/lib/Cloudflare/Workers/DurableObjectNamespace.js.map +1 -1
- package/lib/Cloudflare/Workers/HttpServer.d.ts +2 -8
- package/lib/Cloudflare/Workers/HttpServer.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/HttpServer.js +29 -33
- package/lib/Cloudflare/Workers/HttpServer.js.map +1 -1
- package/lib/Cloudflare/Workers/InferEnv.d.ts +5 -24
- package/lib/Cloudflare/Workers/InferEnv.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Rpc.d.ts +0 -101
- package/lib/Cloudflare/Workers/Rpc.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Rpc.js +9 -286
- package/lib/Cloudflare/Workers/Rpc.js.map +1 -1
- package/lib/Cloudflare/Workers/RpcAsync.d.ts +8 -0
- package/lib/Cloudflare/Workers/RpcAsync.d.ts.map +1 -0
- package/lib/Cloudflare/Workers/RpcAsync.js +45 -0
- package/lib/Cloudflare/Workers/RpcAsync.js.map +1 -0
- package/lib/Cloudflare/Workers/Worker.d.ts +16 -162
- package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Worker.js +49 -354
- package/lib/Cloudflare/Workers/Worker.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts +8 -0
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts.map +1 -0
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.js +129 -0
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.js.map +1 -0
- package/lib/Cloudflare/Workers/WorkerBinding.d.ts +154 -0
- package/lib/Cloudflare/Workers/WorkerBinding.d.ts.map +1 -0
- package/lib/Cloudflare/Workers/WorkerBinding.js +51 -0
- package/lib/Cloudflare/Workers/WorkerBinding.js.map +1 -0
- package/lib/Cloudflare/Workers/WorkerBridge.d.ts +35 -0
- package/lib/Cloudflare/Workers/WorkerBridge.d.ts.map +1 -0
- package/lib/Cloudflare/Workers/WorkerBridge.js +149 -0
- package/lib/Cloudflare/Workers/WorkerBridge.js.map +1 -0
- package/lib/Cloudflare/Workers/WorkerBundle.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBundle.js +18 -84
- package/lib/Cloudflare/Workers/WorkerBundle.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerRuntimeContext.d.ts +8 -0
- package/lib/Cloudflare/Workers/WorkerRuntimeContext.d.ts.map +1 -0
- package/lib/Cloudflare/Workers/WorkerRuntimeContext.js +113 -0
- package/lib/Cloudflare/Workers/WorkerRuntimeContext.js.map +1 -0
- package/lib/Cloudflare/Workers/WorkflowBridge.d.ts +26 -0
- package/lib/Cloudflare/Workers/WorkflowBridge.d.ts.map +1 -0
- package/lib/Cloudflare/Workers/WorkflowBridge.js +41 -0
- package/lib/Cloudflare/Workers/WorkflowBridge.js.map +1 -0
- package/lib/Cloudflare/Workers/index.d.ts +4 -0
- package/lib/Cloudflare/Workers/index.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/index.js +4 -0
- package/lib/Cloudflare/Workers/index.js.map +1 -1
- package/lib/Cloudflare/index.d.ts +4 -2
- package/lib/Cloudflare/index.d.ts.map +1 -1
- package/lib/Cloudflare/index.js +4 -2
- package/lib/Cloudflare/index.js.map +1 -1
- package/lib/Neon/Project.d.ts.map +1 -1
- package/lib/Neon/Project.js +2 -1
- package/lib/Neon/Project.js.map +1 -1
- package/lib/Platform.d.ts +3 -4
- package/lib/Platform.d.ts.map +1 -1
- package/lib/Platform.js.map +1 -1
- package/lib/Rpc.d.ts +17 -0
- package/lib/Rpc.d.ts.map +1 -0
- package/lib/Rpc.js +1 -0
- package/lib/Rpc.js.map +1 -0
- package/lib/RuntimeContext.d.ts +1 -0
- package/lib/RuntimeContext.d.ts.map +1 -1
- package/lib/RuntimeContext.js.map +1 -1
- package/lib/Sidecar/RpcHandler.d.ts.map +1 -1
- package/lib/Sidecar/RpcHandler.js +13 -9
- package/lib/Sidecar/RpcHandler.js.map +1 -1
- package/lib/State/HttpStateApi.d.ts +10 -0
- package/lib/State/HttpStateApi.d.ts.map +1 -1
- package/lib/State/HttpStateApi.js +10 -0
- package/lib/State/HttpStateApi.js.map +1 -1
- package/lib/State/HttpStateStore.d.ts.map +1 -1
- package/lib/State/HttpStateStore.js +2 -1
- package/lib/State/HttpStateStore.js.map +1 -1
- package/lib/State/InMemoryState.d.ts.map +1 -1
- package/lib/State/InMemoryState.js +2 -0
- package/lib/State/InMemoryState.js.map +1 -1
- package/lib/State/LocalState.d.ts.map +1 -1
- package/lib/State/LocalState.js +2 -0
- package/lib/State/LocalState.js.map +1 -1
- package/lib/State/State.d.ts +8 -0
- package/lib/State/State.d.ts.map +1 -1
- package/lib/State/StateEncoding.d.ts.map +1 -1
- package/lib/State/StateEncoding.js +5 -1
- package/lib/State/StateEncoding.js.map +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/Apply.ts +44 -10
- package/src/Cli/Event.ts +1 -1
- package/src/Cloudflare/Bridge.ts +1 -0
- package/src/Cloudflare/Container/ContainerBinding.ts +1 -1
- package/src/Cloudflare/Fetcher.ts +38 -1
- package/src/Cloudflare/Local/SidecarHandlers.ts +3 -6
- package/src/Cloudflare/Website/StaticSite.ts +10 -5
- package/src/Cloudflare/Website/Vite.ts +7 -4
- package/src/Cloudflare/Workers/DurableObjectBridge.ts +180 -0
- package/src/Cloudflare/Workers/DurableObjectNamespace.ts +22 -70
- package/src/Cloudflare/Workers/HttpServer.ts +25 -39
- package/src/Cloudflare/Workers/InferEnv.ts +42 -78
- package/src/Cloudflare/Workers/Rpc.ts +18 -417
- package/src/Cloudflare/Workers/RpcAsync.ts +63 -0
- package/src/Cloudflare/Workers/Worker.ts +107 -548
- package/src/Cloudflare/Workers/WorkerAsyncBindings.ts +149 -0
- package/src/Cloudflare/Workers/WorkerBinding.ts +103 -0
- package/src/Cloudflare/Workers/WorkerBridge.ts +304 -0
- package/src/Cloudflare/Workers/WorkerBundle.ts +17 -83
- package/src/Cloudflare/Workers/WorkerRuntimeContext.ts +164 -0
- package/src/Cloudflare/Workers/WorkflowBridge.ts +92 -0
- package/src/Cloudflare/Workers/index.ts +4 -0
- package/src/Cloudflare/index.ts +4 -2
- package/src/Neon/Project.ts +3 -1
- package/src/Platform.ts +3 -4
- package/src/Rpc.ts +25 -0
- package/src/RuntimeContext.ts +1 -0
- package/src/Sidecar/RpcHandler.ts +18 -9
- package/src/State/HttpStateApi.ts +11 -0
- package/src/State/HttpStateStore.ts +7 -2
- package/src/State/InMemoryState.ts +2 -0
- package/src/State/LocalState.ts +2 -0
- package/src/State/State.ts +8 -0
- package/src/State/StateEncoding.ts +8 -1
- package/src/index.ts +7 -6
package/package.json
CHANGED
package/src/Apply.ts
CHANGED
|
@@ -22,18 +22,17 @@ import type { Input } from "./Input.ts";
|
|
|
22
22
|
import { generateInstanceId, InstanceId } from "./InstanceId.ts";
|
|
23
23
|
import * as Output from "./Output.ts";
|
|
24
24
|
import {
|
|
25
|
+
type ActionApply,
|
|
25
26
|
type Apply,
|
|
26
27
|
type Delete,
|
|
27
28
|
type Plan,
|
|
28
|
-
type ActionApply,
|
|
29
|
-
type ActionDelete,
|
|
30
29
|
} from "./Plan.ts";
|
|
31
30
|
import { findProviderByType } from "./Provider.ts";
|
|
32
31
|
import type { ResourceBinding } from "./Resource.ts";
|
|
33
32
|
import { Stack } from "./Stack.ts";
|
|
34
33
|
import { Stage } from "./Stage.ts";
|
|
35
|
-
import { recordResourceOp, type ResourceOp } from "./Telemetry/Metrics.ts";
|
|
36
34
|
import {
|
|
35
|
+
type ActionState,
|
|
37
36
|
type CreatedResourceState,
|
|
38
37
|
type CreatingResourceState,
|
|
39
38
|
type DeletingResourceState,
|
|
@@ -43,12 +42,12 @@ import {
|
|
|
43
42
|
type ReplacingResourceState,
|
|
44
43
|
type ResourceState,
|
|
45
44
|
type RunningActionState,
|
|
46
|
-
type ActionState,
|
|
47
45
|
type UpdatedResourceState,
|
|
48
46
|
type UpdatingReourceState,
|
|
49
47
|
State,
|
|
50
48
|
StateStoreError,
|
|
51
49
|
} from "./State/index.ts";
|
|
50
|
+
import { type ResourceOp, recordResourceOp } from "./Telemetry/Metrics.ts";
|
|
52
51
|
import { hashInput } from "./Util/hash.ts";
|
|
53
52
|
|
|
54
53
|
export type ApplyEffect<
|
|
@@ -405,12 +404,31 @@ const executeNode = (
|
|
|
405
404
|
});
|
|
406
405
|
|
|
407
406
|
const markTerminal = (status: "created" | "updated") =>
|
|
408
|
-
Effect.
|
|
407
|
+
Effect.gen(function* () {
|
|
409
408
|
terminalStatuses.set(fqn, {
|
|
410
409
|
id: logicalId,
|
|
411
410
|
type: node.resource.Type,
|
|
412
411
|
status,
|
|
413
412
|
});
|
|
413
|
+
// Emit immediately so the CLI surfaces the terminal status as soon
|
|
414
|
+
// as the resource is actually done — instead of batching every
|
|
415
|
+
// resource's "created"/"updated" event to the end of apply(), which
|
|
416
|
+
// makes long-running siblings appear stuck in "creating" until the
|
|
417
|
+
// entire deploy finishes.
|
|
418
|
+
//
|
|
419
|
+
// Cycle members are exempt: their initial pass produces an
|
|
420
|
+
// intermediate result that Phase 3 (`converge`) will overwrite once
|
|
421
|
+
// the SCC reaches a fixed point. Emitting "created"/"updated" here
|
|
422
|
+
// would surface that intermediate state to the CLI before the real
|
|
423
|
+
// terminal status is known. Their final status is flushed from
|
|
424
|
+
// `terminalStatuses` after `converge` completes.
|
|
425
|
+
if (inCycle) return;
|
|
426
|
+
yield* session.emit({
|
|
427
|
+
kind: "status-change",
|
|
428
|
+
id: logicalId,
|
|
429
|
+
type: node.resource.Type,
|
|
430
|
+
status,
|
|
431
|
+
});
|
|
414
432
|
});
|
|
415
433
|
|
|
416
434
|
const signalReady = Deferred.succeed(ready[fqn], void 0);
|
|
@@ -576,11 +594,16 @@ const executeNode = (
|
|
|
576
594
|
});
|
|
577
595
|
}
|
|
578
596
|
|
|
579
|
-
|
|
597
|
+
// While we're waiting on upstream outputs the resource isn't actually
|
|
598
|
+
// creating anything yet — surface that as "pending" so the CLI doesn't
|
|
599
|
+
// look stuck in "creating" for slow-upstream deploys.
|
|
600
|
+
yield* report("pending");
|
|
580
601
|
|
|
581
602
|
// Create runs against fully resolved upstream outputs and bindings, not the
|
|
582
603
|
// raw Output expressions stored in the plan.
|
|
583
604
|
yield* waitForDeps(allUpstreamFqns());
|
|
605
|
+
|
|
606
|
+
yield* report("creating");
|
|
584
607
|
const outputs = getOutputs();
|
|
585
608
|
|
|
586
609
|
const news = (yield* Output.evaluate(node.props, outputs)) as Record<
|
|
@@ -662,6 +685,9 @@ const executeNode = (
|
|
|
662
685
|
});
|
|
663
686
|
}
|
|
664
687
|
|
|
688
|
+
// See create-flow note: while we're waiting on upstream outputs
|
|
689
|
+
// this resource isn't actually updating yet.
|
|
690
|
+
yield* report("pending");
|
|
665
691
|
yield* waitForDeps(allUpstreamFqns());
|
|
666
692
|
const outputs = getOutputs();
|
|
667
693
|
|
|
@@ -867,11 +893,15 @@ const executeNode = (
|
|
|
867
893
|
});
|
|
868
894
|
}
|
|
869
895
|
|
|
870
|
-
|
|
896
|
+
// See create-flow note: while we're waiting on upstream outputs
|
|
897
|
+
// the replacement isn't actually being created yet.
|
|
898
|
+
yield* report("pending");
|
|
871
899
|
|
|
872
900
|
// Replacement create is evaluated exactly like create, but against the new
|
|
873
901
|
// generation's instance id and with the previous generations preserved in `old`.
|
|
874
902
|
yield* waitForDeps(allUpstreamFqns());
|
|
903
|
+
|
|
904
|
+
yield* report("creating replacement");
|
|
875
905
|
const outputs = getOutputs();
|
|
876
906
|
|
|
877
907
|
const news = (yield* Output.evaluate(node.props, outputs)) as Record<
|
|
@@ -1052,13 +1082,16 @@ const executeActionNode = (
|
|
|
1052
1082
|
type: task.Type,
|
|
1053
1083
|
status: "skipped",
|
|
1054
1084
|
});
|
|
1085
|
+
yield* report("skipped");
|
|
1055
1086
|
return;
|
|
1056
1087
|
}
|
|
1057
1088
|
|
|
1058
1089
|
// ── run ──
|
|
1059
|
-
//
|
|
1060
|
-
//
|
|
1061
|
-
//
|
|
1090
|
+
// Tasks wait on `waitForStableDeps` (post-reconcile attrs) for their
|
|
1091
|
+
// upstreams, which is often the slowest dep chain in the deploy.
|
|
1092
|
+
// Surface that as "pending" instead of having the task show no status
|
|
1093
|
+
// until its run actually starts.
|
|
1094
|
+
yield* report("pending");
|
|
1062
1095
|
yield* waitForDeps(
|
|
1063
1096
|
Object.keys(Output.resolveUpstream(node.input)).filter(
|
|
1064
1097
|
(f) => f in readyStable,
|
|
@@ -1108,6 +1141,7 @@ const executeActionNode = (
|
|
|
1108
1141
|
type: task.Type,
|
|
1109
1142
|
status: "ran",
|
|
1110
1143
|
});
|
|
1144
|
+
yield* report("ran");
|
|
1111
1145
|
}).pipe(
|
|
1112
1146
|
Effect.catchCause((cause) =>
|
|
1113
1147
|
Effect.gen(function* () {
|
package/src/Cli/Event.ts
CHANGED
package/src/Cloudflare/Bridge.ts
CHANGED
|
@@ -6,7 +6,11 @@ import { pipe } from "effect/Function";
|
|
|
6
6
|
import * as Latch from "effect/Latch";
|
|
7
7
|
import * as Scope from "effect/Scope";
|
|
8
8
|
import * as HttpBody from "effect/unstable/http/HttpBody";
|
|
9
|
-
import
|
|
9
|
+
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
10
|
+
import {
|
|
11
|
+
HttpClientError,
|
|
12
|
+
TransportError,
|
|
13
|
+
} from "effect/unstable/http/HttpClientError";
|
|
10
14
|
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
|
|
11
15
|
import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse";
|
|
12
16
|
import type { HttpServerError } from "effect/unstable/http/HttpServerError";
|
|
@@ -118,6 +122,39 @@ export const fromCloudflareFetcher = (fetcher: cf.Fetcher): Fetcher => {
|
|
|
118
122
|
};
|
|
119
123
|
};
|
|
120
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Adapt anything that exposes a server-shaped `fetch` (e.g. a Durable Object
|
|
127
|
+
* stub, a Worker service binding) into an Effect `HttpClient`. Lets HttpApi
|
|
128
|
+
* clients address bindings without a base URL via `transformClient`.
|
|
129
|
+
*/
|
|
130
|
+
export const toHttpClient = (fetcher: {
|
|
131
|
+
fetch: (
|
|
132
|
+
request: HttpServerRequest.HttpServerRequest,
|
|
133
|
+
) => Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError>;
|
|
134
|
+
}) =>
|
|
135
|
+
HttpClient.make((request) => {
|
|
136
|
+
console.log("toHttpClient: request", request.url);
|
|
137
|
+
return fetcher.fetch(HttpServerRequest.fromClientRequest(request)).pipe(
|
|
138
|
+
Effect.map((response) => {
|
|
139
|
+
console.log("toHttpClient: response", response);
|
|
140
|
+
return HttpClientResponse.fromWeb(
|
|
141
|
+
request,
|
|
142
|
+
HttpServerResponse.toWeb(response),
|
|
143
|
+
);
|
|
144
|
+
}),
|
|
145
|
+
Effect.mapError(
|
|
146
|
+
(cause) =>
|
|
147
|
+
new HttpClientError({
|
|
148
|
+
reason: new TransportError({
|
|
149
|
+
request,
|
|
150
|
+
cause,
|
|
151
|
+
description: "Fetcher-backed HttpClient request failed",
|
|
152
|
+
}),
|
|
153
|
+
}),
|
|
154
|
+
),
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
|
|
121
158
|
export const fromCloudflareSocket = (cfSocket: cf.Socket): Socket.Socket => {
|
|
122
159
|
const latch = Latch.makeUnsafe(false);
|
|
123
160
|
let currentFiberSet: FiberSet.FiberSet<any, any> | undefined;
|
|
@@ -44,13 +44,10 @@ import { Stack } from "../../Stack.ts";
|
|
|
44
44
|
import { Stage } from "../../Stage.ts";
|
|
45
45
|
import { CloudflareEnvironment } from "../CloudflareEnvironment.ts";
|
|
46
46
|
import { getCompatibility } from "../Workers/Compatibility.ts";
|
|
47
|
-
import { getCronBindings } from "../Workers/index.ts";
|
|
48
47
|
import * as Vite from "../Workers/Vite.ts";
|
|
49
|
-
import type {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
WorkerBinding,
|
|
53
|
-
} from "../Workers/Worker.ts";
|
|
48
|
+
import type { Worker, WorkerAssetsConfig } from "../Workers/Worker.ts";
|
|
49
|
+
import { getCronBindings } from "../Workers/WorkerAsyncBindings.ts";
|
|
50
|
+
import type { WorkerBinding } from "../Workers/WorkerBinding.ts";
|
|
54
51
|
import {
|
|
55
52
|
WorkerBundle,
|
|
56
53
|
type WorkerBundleOptions,
|
|
@@ -8,12 +8,16 @@ import {
|
|
|
8
8
|
Worker,
|
|
9
9
|
type WorkerAssetsConfig,
|
|
10
10
|
type WorkerBindingProps,
|
|
11
|
+
type WorkerEnv,
|
|
11
12
|
type WorkerProps,
|
|
12
13
|
} from "../Workers/Worker.ts";
|
|
13
14
|
|
|
14
|
-
export interface StaticSiteProps<
|
|
15
|
+
export interface StaticSiteProps<
|
|
16
|
+
Bindings extends WorkerBindingProps = {},
|
|
17
|
+
Env extends WorkerEnv = WorkerEnv,
|
|
18
|
+
>
|
|
15
19
|
extends
|
|
16
|
-
Omit<WorkerProps<Bindings, WorkerAssetsConfig>, "assets">,
|
|
20
|
+
Omit<WorkerProps<Bindings, Env, WorkerAssetsConfig>, "assets">,
|
|
17
21
|
Omit<CommandProps, "env"> {
|
|
18
22
|
/**
|
|
19
23
|
* Optional configuration for static asset routing behavior.
|
|
@@ -116,12 +120,13 @@ export type StaticSite = ReturnType<typeof StaticSite>;
|
|
|
116
120
|
*/
|
|
117
121
|
export const StaticSite = <
|
|
118
122
|
const Bindings extends WorkerBindingProps = {},
|
|
123
|
+
const Env extends WorkerEnv = WorkerEnv,
|
|
119
124
|
Req = never,
|
|
120
125
|
>(
|
|
121
126
|
id: string,
|
|
122
127
|
propsEff:
|
|
123
|
-
| InputProps<StaticSiteProps<Bindings>>
|
|
124
|
-
| Effect.Effect<InputProps<StaticSiteProps<Bindings>>, never, Req>,
|
|
128
|
+
| InputProps<StaticSiteProps<Bindings, Env>>
|
|
129
|
+
| Effect.Effect<InputProps<StaticSiteProps<Bindings, Env>>, never, Req>,
|
|
125
130
|
) =>
|
|
126
131
|
Effect.gen(function* () {
|
|
127
132
|
const props = Effect.isEffect(propsEff)
|
|
@@ -149,7 +154,7 @@ export const StaticSite = <
|
|
|
149
154
|
})),
|
|
150
155
|
);
|
|
151
156
|
|
|
152
|
-
return yield* Worker<Bindings, WorkerAssetsConfig, Req>(
|
|
157
|
+
return yield* Worker<Bindings, Env, WorkerAssetsConfig, Req>(
|
|
153
158
|
"Worker",
|
|
154
159
|
Effect.map(props, (props) => ({
|
|
155
160
|
...props,
|
|
@@ -5,12 +5,14 @@ import {
|
|
|
5
5
|
Worker,
|
|
6
6
|
type WorkerAssetsConfig,
|
|
7
7
|
type WorkerBindingProps,
|
|
8
|
+
type WorkerEnv,
|
|
8
9
|
type WorkerProps,
|
|
9
10
|
} from "../Workers/Worker.ts";
|
|
10
11
|
|
|
11
12
|
export interface ViteProps<
|
|
12
13
|
Bindings extends WorkerBindingProps = {},
|
|
13
|
-
|
|
14
|
+
Env extends WorkerEnv = {},
|
|
15
|
+
> extends Omit<WorkerProps<Bindings, Env>, "vite" | "main"> {
|
|
14
16
|
/**
|
|
15
17
|
* Root directory passed to Vite's `root` option.
|
|
16
18
|
* Defaults to the current working directory (`process.cwd()`).
|
|
@@ -109,14 +111,15 @@ export interface ViteProps<
|
|
|
109
111
|
*/
|
|
110
112
|
export const Vite = <
|
|
111
113
|
const Bindings extends WorkerBindingProps = {},
|
|
114
|
+
const Env extends WorkerEnv = {},
|
|
112
115
|
Req = never,
|
|
113
116
|
>(
|
|
114
117
|
id: string,
|
|
115
118
|
propsEff?:
|
|
116
|
-
| InputProps<ViteProps<Bindings>>
|
|
117
|
-
| Effect.Effect<InputProps<ViteProps<Bindings>>, never, Req>,
|
|
119
|
+
| InputProps<ViteProps<Bindings, Env>>
|
|
120
|
+
| Effect.Effect<InputProps<ViteProps<Bindings, Env>>, never, Req>,
|
|
118
121
|
) =>
|
|
119
|
-
Worker<Bindings, WorkerAssetsConfig, Req>(
|
|
122
|
+
Worker<Bindings, Env, WorkerAssetsConfig, Req>(
|
|
120
123
|
id,
|
|
121
124
|
Effect.map(
|
|
122
125
|
Effect.isEffect(propsEff) ? propsEff : Effect.succeed(propsEff),
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import type * as cf from "@cloudflare/workers-types";
|
|
2
|
+
import type { DurableObject as DurableObjectClass } from "cloudflare:workers";
|
|
3
|
+
|
|
4
|
+
import * as Cause from "effect/Cause";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import * as Exit from "effect/Exit";
|
|
7
|
+
import * as Layer from "effect/Layer";
|
|
8
|
+
import * as Scope from "effect/Scope";
|
|
9
|
+
|
|
10
|
+
import { HttpServerResponse } from "effect/unstable/http";
|
|
11
|
+
import type {
|
|
12
|
+
DurableObjectExport,
|
|
13
|
+
DurableObjectShape,
|
|
14
|
+
} from "./DurableObjectNamespace.ts";
|
|
15
|
+
import {
|
|
16
|
+
DurableObjectState,
|
|
17
|
+
fromDurableObjectState,
|
|
18
|
+
} from "./DurableObjectState.ts";
|
|
19
|
+
import { makeRequestEffect } from "./HttpServer.ts";
|
|
20
|
+
import { fromWebSocket } from "./WebSocket.ts";
|
|
21
|
+
import { getWorkerExport, handleRpcExit } from "./WorkerBridge.ts";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Create a DurableObjectBridge class that proxies RPC method calls through
|
|
25
|
+
* the Effect runtime, encoding success/fail/stream results as RPC envelopes.
|
|
26
|
+
*
|
|
27
|
+
* Accepts the `DurableObject` base class and a `getExport` resolver so the
|
|
28
|
+
* implementation lives in real TypeScript instead of a generated string template.
|
|
29
|
+
*/
|
|
30
|
+
export const makeDurableObjectBridge =
|
|
31
|
+
(
|
|
32
|
+
DurableObject: typeof DurableObjectClass,
|
|
33
|
+
{
|
|
34
|
+
entrypoint,
|
|
35
|
+
stack,
|
|
36
|
+
}: {
|
|
37
|
+
entrypoint: Effect.Effect<Record<string, any>>;
|
|
38
|
+
stack: {
|
|
39
|
+
name: string;
|
|
40
|
+
stage: string;
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
) =>
|
|
44
|
+
(className: string) =>
|
|
45
|
+
class DurableObjectBridge extends DurableObject {
|
|
46
|
+
#state;
|
|
47
|
+
#globalContext;
|
|
48
|
+
#exported;
|
|
49
|
+
#instance;
|
|
50
|
+
constructor(state: cf.DurableObjectState, env: any) {
|
|
51
|
+
super(state as any, env);
|
|
52
|
+
this.#state = state;
|
|
53
|
+
|
|
54
|
+
const { globalContext, exported } =
|
|
55
|
+
getWorkerExport<DurableObjectExport>({
|
|
56
|
+
entrypoint,
|
|
57
|
+
stack,
|
|
58
|
+
exportName: className,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
this.#globalContext = globalContext;
|
|
62
|
+
this.#exported = exported;
|
|
63
|
+
|
|
64
|
+
this.#instance = state.blockConcurrencyWhile(() =>
|
|
65
|
+
this.#exported.pipe(
|
|
66
|
+
Effect.flatMap(({ constructor, services }) =>
|
|
67
|
+
constructor.pipe(
|
|
68
|
+
Effect.provide(
|
|
69
|
+
Layer.succeed(
|
|
70
|
+
DurableObjectState,
|
|
71
|
+
fromDurableObjectState(this.#state),
|
|
72
|
+
).pipe(Layer.provideMerge(Layer.succeedContext(services))),
|
|
73
|
+
),
|
|
74
|
+
Effect.map((instance) => ({ instance, services })),
|
|
75
|
+
),
|
|
76
|
+
),
|
|
77
|
+
Effect.provide(this.#globalContext),
|
|
78
|
+
Effect.runPromise,
|
|
79
|
+
),
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return new Proxy(this, {
|
|
83
|
+
get: (target, prop) => {
|
|
84
|
+
console.log(target, prop, prop in target);
|
|
85
|
+
const bind = (f: any) =>
|
|
86
|
+
typeof f === "function" ? f.bind(target) : f;
|
|
87
|
+
if (typeof prop !== "string") return bind((target as any)[prop]);
|
|
88
|
+
if (prop in target) return bind((target as any)[prop]);
|
|
89
|
+
return async (...args: any[]) =>
|
|
90
|
+
this.#execute((instance) => {
|
|
91
|
+
const method = instance[prop as keyof DurableObjectShape];
|
|
92
|
+
if (typeof method === "function") {
|
|
93
|
+
return (method as any)(...args);
|
|
94
|
+
} else if (Effect.isEffect(method)) {
|
|
95
|
+
return method;
|
|
96
|
+
} else {
|
|
97
|
+
return Effect.succeed(method);
|
|
98
|
+
}
|
|
99
|
+
}, handleRpcExit);
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async #execute(
|
|
105
|
+
fn: (instance: DurableObjectShape) => Effect.Effect<any, any, any>,
|
|
106
|
+
onExit?: (exit: Exit.Exit<any, any>) => Promise<any>,
|
|
107
|
+
) {
|
|
108
|
+
const scope = Scope.makeUnsafe();
|
|
109
|
+
|
|
110
|
+
const { instance, services } = await this.#instance;
|
|
111
|
+
|
|
112
|
+
return fn(instance)
|
|
113
|
+
.pipe(
|
|
114
|
+
Effect.provide(
|
|
115
|
+
Layer.succeed(
|
|
116
|
+
DurableObjectState,
|
|
117
|
+
fromDurableObjectState(this.#state),
|
|
118
|
+
).pipe(
|
|
119
|
+
Layer.provideMerge(Layer.succeed(Scope.Scope, scope)),
|
|
120
|
+
Layer.provideMerge(Layer.succeedContext(services)),
|
|
121
|
+
Layer.provideMerge(this.#globalContext),
|
|
122
|
+
),
|
|
123
|
+
),
|
|
124
|
+
Effect.runPromiseExit,
|
|
125
|
+
)
|
|
126
|
+
.then(
|
|
127
|
+
onExit ??
|
|
128
|
+
((exit) =>
|
|
129
|
+
exit._tag === "Success"
|
|
130
|
+
? Promise.resolve(exit.value)
|
|
131
|
+
: Promise.reject(Cause.squash(exit.cause))),
|
|
132
|
+
)
|
|
133
|
+
.finally(() =>
|
|
134
|
+
Scope.close(scope, Exit.void).pipe(Effect.runPromise, (promise) =>
|
|
135
|
+
this.#state.waitUntil(promise),
|
|
136
|
+
),
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async fetch(request: Request): Promise<any> {
|
|
141
|
+
return this.#execute((instance) =>
|
|
142
|
+
instance.fetch
|
|
143
|
+
? makeRequestEffect(request as any, instance.fetch)
|
|
144
|
+
: Effect.succeed(
|
|
145
|
+
HttpServerResponse.text("Not implemented", {
|
|
146
|
+
status: 404,
|
|
147
|
+
}),
|
|
148
|
+
),
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async alarm(alarmInfo?: cf.AlarmInvocationInfo) {
|
|
153
|
+
return this.#execute((instance) => instance.alarm!(alarmInfo));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async webSocketMessage(ws: WebSocket, message: string | ArrayBuffer) {
|
|
157
|
+
return this.#execute(
|
|
158
|
+
(instance) =>
|
|
159
|
+
instance.webSocketMessage?.(fromWebSocket(ws as any), message) ??
|
|
160
|
+
Effect.void,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async webSocketClose(
|
|
165
|
+
ws: WebSocket,
|
|
166
|
+
code: number,
|
|
167
|
+
reason: string,
|
|
168
|
+
wasClean: boolean,
|
|
169
|
+
) {
|
|
170
|
+
return this.#execute(
|
|
171
|
+
(instance) =>
|
|
172
|
+
instance.webSocketClose?.(
|
|
173
|
+
fromWebSocket(ws as any),
|
|
174
|
+
code,
|
|
175
|
+
reason,
|
|
176
|
+
wasClean,
|
|
177
|
+
) ?? Effect.void,
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
} as any;
|
|
@@ -9,29 +9,25 @@ import type { HttpEffect } from "../../Http.ts";
|
|
|
9
9
|
import * as Output from "../../Output.ts";
|
|
10
10
|
import type { PlatformServices } from "../../Platform.ts";
|
|
11
11
|
import { effectClass, taggedFunction } from "../../Util/effect.ts";
|
|
12
|
-
import {
|
|
13
|
-
DurableObjectState,
|
|
14
|
-
fromDurableObjectState,
|
|
15
|
-
} from "./DurableObjectState.ts";
|
|
12
|
+
import { DurableObjectState } from "./DurableObjectState.ts";
|
|
16
13
|
import { makeRpcStub } from "./Rpc.ts";
|
|
17
14
|
import { type DurableWebSocket } from "./WebSocket.ts";
|
|
18
15
|
import { Worker, WorkerEnvironment, type WorkerServices } from "./Worker.ts";
|
|
19
16
|
|
|
20
17
|
export interface DurableObjectExport {
|
|
21
18
|
readonly kind: "durableObject";
|
|
22
|
-
readonly
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
readonly constructor: Effect.Effect<
|
|
20
|
+
DurableObjectShape,
|
|
21
|
+
never,
|
|
22
|
+
DurableObjectState
|
|
23
|
+
>;
|
|
24
|
+
readonly services: Context.Context<never>;
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
export const isDurableObjectExport = (
|
|
29
28
|
value: unknown,
|
|
30
29
|
): value is DurableObjectExport =>
|
|
31
|
-
typeof value === "object" &&
|
|
32
|
-
value !== null &&
|
|
33
|
-
"kind" in value &&
|
|
34
|
-
(value as any).kind === "durableObject";
|
|
30
|
+
typeof value === "object" && (value as any)?.kind === "durableObject";
|
|
35
31
|
|
|
36
32
|
export type DurableObjectId = cf.DurableObjectId;
|
|
37
33
|
export type DurableObjectJurisdiction = cf.DurableObjectJurisdiction;
|
|
@@ -46,10 +42,7 @@ const TypeId = "Cloudflare.DurableObjectNamespace";
|
|
|
46
42
|
export const isDurableObjectNamespaceLike = (
|
|
47
43
|
value: unknown,
|
|
48
44
|
): value is DurableObjectNamespaceLike =>
|
|
49
|
-
typeof value === "object" &&
|
|
50
|
-
value !== null &&
|
|
51
|
-
"kind" in value &&
|
|
52
|
-
value.kind === TypeId;
|
|
45
|
+
typeof value === "object" && (value as any)?.kind === TypeId;
|
|
53
46
|
|
|
54
47
|
export interface DurableObjectNamespaceLike<Shape = any> {
|
|
55
48
|
kind: TypeId;
|
|
@@ -574,11 +567,7 @@ export const DurableObjectNamespace: DurableObjectNamespaceClass =
|
|
|
574
567
|
const [namespace, impl] = args as any as [
|
|
575
568
|
name: string,
|
|
576
569
|
impl: Effect.Effect<
|
|
577
|
-
Effect.Effect<
|
|
578
|
-
DurableObjectNamespace<any>,
|
|
579
|
-
never,
|
|
580
|
-
DurableObjectState
|
|
581
|
-
>
|
|
570
|
+
Effect.Effect<DurableObjectShape, never, DurableObjectState>
|
|
582
571
|
>,
|
|
583
572
|
];
|
|
584
573
|
const worker = yield* Worker;
|
|
@@ -600,52 +589,6 @@ export const DurableObjectNamespace: DurableObjectNamespaceClass =
|
|
|
600
589
|
],
|
|
601
590
|
});
|
|
602
591
|
|
|
603
|
-
const services =
|
|
604
|
-
yield* Effect.context<Effect.Services<typeof impl>>();
|
|
605
|
-
|
|
606
|
-
yield* worker.export(namespace, {
|
|
607
|
-
kind: "durableObject",
|
|
608
|
-
make: (state: cf.DurableObjectState, env: any) => {
|
|
609
|
-
const doState = fromDurableObjectState(state);
|
|
610
|
-
return constructor.pipe(
|
|
611
|
-
Effect.provideContext(services),
|
|
612
|
-
Effect.provideService(DurableObjectState, doState),
|
|
613
|
-
Effect.provideService(WorkerEnvironment, env),
|
|
614
|
-
Effect.map((methods: any) => {
|
|
615
|
-
const wrapped: Record<string, unknown> = {};
|
|
616
|
-
for (const key of Object.getOwnPropertyNames(methods)) {
|
|
617
|
-
const value = methods[key];
|
|
618
|
-
if (Effect.isEffect(value)) {
|
|
619
|
-
wrapped[key] = (
|
|
620
|
-
value as Effect.Effect<any, any, any>
|
|
621
|
-
).pipe(
|
|
622
|
-
Effect.provideService(DurableObjectState, doState),
|
|
623
|
-
);
|
|
624
|
-
} else if (typeof value === "function") {
|
|
625
|
-
wrapped[key] = (...args: unknown[]) => {
|
|
626
|
-
const result = (value as Function)(...args);
|
|
627
|
-
if (Effect.isEffect(result)) {
|
|
628
|
-
return (
|
|
629
|
-
result as Effect.Effect<any, any, any>
|
|
630
|
-
).pipe(
|
|
631
|
-
Effect.provideService(
|
|
632
|
-
DurableObjectState,
|
|
633
|
-
doState,
|
|
634
|
-
),
|
|
635
|
-
);
|
|
636
|
-
}
|
|
637
|
-
return result;
|
|
638
|
-
};
|
|
639
|
-
} else {
|
|
640
|
-
wrapped[key] = value;
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
return wrapped;
|
|
644
|
-
}),
|
|
645
|
-
);
|
|
646
|
-
},
|
|
647
|
-
} satisfies DurableObjectExport);
|
|
648
|
-
|
|
649
592
|
const binding = yield* Effect.serviceOption(
|
|
650
593
|
WorkerEnvironment,
|
|
651
594
|
).pipe(
|
|
@@ -699,9 +642,18 @@ export const DurableObjectNamespace: DurableObjectNamespaceClass =
|
|
|
699
642
|
// use((ns) => ns.jurisdiction(jurisdiction) as any),
|
|
700
643
|
};
|
|
701
644
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
645
|
+
yield* worker.export(namespace, {
|
|
646
|
+
kind: "durableObject",
|
|
647
|
+
// initialize the object's constructor (apply infra dependencies)
|
|
648
|
+
constructor: yield* impl.pipe(
|
|
649
|
+
Effect.provideService(
|
|
650
|
+
DurableObjectNamespaceScope,
|
|
651
|
+
self as any,
|
|
652
|
+
),
|
|
653
|
+
),
|
|
654
|
+
// grab the object's infra dependencies so we can apply them when calling the instance's methods
|
|
655
|
+
services: yield* Effect.context<Effect.Services<typeof impl>>(),
|
|
656
|
+
} satisfies DurableObjectExport);
|
|
705
657
|
|
|
706
658
|
return self;
|
|
707
659
|
}),
|