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.
Files changed (179) hide show
  1. package/bin/exec.js +16 -3
  2. package/bin/exec.js.map +1 -1
  3. package/lib/AWS/EC2/hosted.js +1 -1
  4. package/lib/AWS/EC2/hosted.js.map +1 -1
  5. package/lib/AWS/ECS/Task.js +1 -1
  6. package/lib/AWS/ECS/Task.js.map +1 -1
  7. package/lib/AWS/Lambda/BucketEventSource.d.ts +1 -1
  8. package/lib/AWS/Lambda/BucketEventSource.d.ts.map +1 -1
  9. package/lib/AWS/Lambda/EventBridgeEventSource.d.ts +1 -1
  10. package/lib/AWS/Lambda/EventBridgeEventSource.d.ts.map +1 -1
  11. package/lib/AWS/Lambda/Function.js +1 -1
  12. package/lib/AWS/Lambda/Function.js.map +1 -1
  13. package/lib/AWS/Lambda/QueueEventSource.d.ts +1 -1
  14. package/lib/AWS/Lambda/QueueEventSource.d.ts.map +1 -1
  15. package/lib/AWS/Lambda/StreamEventSource.d.ts +1 -1
  16. package/lib/AWS/Lambda/StreamEventSource.d.ts.map +1 -1
  17. package/lib/AWS/Lambda/TableEventSource.d.ts +1 -1
  18. package/lib/AWS/Lambda/TableEventSource.d.ts.map +1 -1
  19. package/lib/AWS/Lambda/TopicEventSource.d.ts +1 -1
  20. package/lib/AWS/Lambda/TopicEventSource.d.ts.map +1 -1
  21. package/lib/Apply.d.ts.map +1 -1
  22. package/lib/Apply.js +41 -7
  23. package/lib/Apply.js.map +1 -1
  24. package/lib/Cloudflare/Bridge.d.ts +1 -0
  25. package/lib/Cloudflare/Bridge.d.ts.map +1 -1
  26. package/lib/Cloudflare/Bridge.js +1 -0
  27. package/lib/Cloudflare/Bridge.js.map +1 -1
  28. package/lib/Cloudflare/Container/ContainerApplication.js +1 -1
  29. package/lib/Cloudflare/Container/ContainerApplication.js.map +1 -1
  30. package/lib/Cloudflare/Container/ContainerBinding.d.ts +1 -1
  31. package/lib/Cloudflare/Container/ContainerBinding.d.ts.map +1 -1
  32. package/lib/Cloudflare/Container/ContainerBinding.js +1 -1
  33. package/lib/Cloudflare/Container/ContainerBinding.js.map +1 -1
  34. package/lib/Cloudflare/Fetcher.d.ts +9 -0
  35. package/lib/Cloudflare/Fetcher.d.ts.map +1 -1
  36. package/lib/Cloudflare/Fetcher.js +20 -1
  37. package/lib/Cloudflare/Fetcher.js.map +1 -1
  38. package/lib/Cloudflare/Local/SidecarHandlers.d.ts.map +1 -1
  39. package/lib/Cloudflare/Local/SidecarHandlers.js +1 -1
  40. package/lib/Cloudflare/Local/SidecarHandlers.js.map +1 -1
  41. package/lib/Cloudflare/StateStore/Api.d.ts +2 -2
  42. package/lib/Cloudflare/Website/StaticSite.d.ts +5 -4
  43. package/lib/Cloudflare/Website/StaticSite.d.ts.map +1 -1
  44. package/lib/Cloudflare/Website/StaticSite.js.map +1 -1
  45. package/lib/Cloudflare/Website/Vite.d.ts +4 -4
  46. package/lib/Cloudflare/Website/Vite.d.ts.map +1 -1
  47. package/lib/Cloudflare/Website/Vite.js.map +1 -1
  48. package/lib/Cloudflare/Workers/DurableObjectBridge.d.ts +17 -0
  49. package/lib/Cloudflare/Workers/DurableObjectBridge.d.ts.map +1 -0
  50. package/lib/Cloudflare/Workers/DurableObjectBridge.js +86 -0
  51. package/lib/Cloudflare/Workers/DurableObjectBridge.js.map +1 -0
  52. package/lib/Cloudflare/Workers/DurableObjectNamespace.d.ts +2 -1
  53. package/lib/Cloudflare/Workers/DurableObjectNamespace.d.ts.map +1 -1
  54. package/lib/Cloudflare/Workers/DurableObjectNamespace.js +10 -39
  55. package/lib/Cloudflare/Workers/DurableObjectNamespace.js.map +1 -1
  56. package/lib/Cloudflare/Workers/HttpServer.d.ts +2 -8
  57. package/lib/Cloudflare/Workers/HttpServer.d.ts.map +1 -1
  58. package/lib/Cloudflare/Workers/HttpServer.js +29 -33
  59. package/lib/Cloudflare/Workers/HttpServer.js.map +1 -1
  60. package/lib/Cloudflare/Workers/InferEnv.d.ts +5 -24
  61. package/lib/Cloudflare/Workers/InferEnv.d.ts.map +1 -1
  62. package/lib/Cloudflare/Workers/Rpc.d.ts +0 -101
  63. package/lib/Cloudflare/Workers/Rpc.d.ts.map +1 -1
  64. package/lib/Cloudflare/Workers/Rpc.js +9 -286
  65. package/lib/Cloudflare/Workers/Rpc.js.map +1 -1
  66. package/lib/Cloudflare/Workers/RpcAsync.d.ts +8 -0
  67. package/lib/Cloudflare/Workers/RpcAsync.d.ts.map +1 -0
  68. package/lib/Cloudflare/Workers/RpcAsync.js +45 -0
  69. package/lib/Cloudflare/Workers/RpcAsync.js.map +1 -0
  70. package/lib/Cloudflare/Workers/Worker.d.ts +16 -162
  71. package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
  72. package/lib/Cloudflare/Workers/Worker.js +49 -354
  73. package/lib/Cloudflare/Workers/Worker.js.map +1 -1
  74. package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts +8 -0
  75. package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts.map +1 -0
  76. package/lib/Cloudflare/Workers/WorkerAsyncBindings.js +129 -0
  77. package/lib/Cloudflare/Workers/WorkerAsyncBindings.js.map +1 -0
  78. package/lib/Cloudflare/Workers/WorkerBinding.d.ts +154 -0
  79. package/lib/Cloudflare/Workers/WorkerBinding.d.ts.map +1 -0
  80. package/lib/Cloudflare/Workers/WorkerBinding.js +51 -0
  81. package/lib/Cloudflare/Workers/WorkerBinding.js.map +1 -0
  82. package/lib/Cloudflare/Workers/WorkerBridge.d.ts +35 -0
  83. package/lib/Cloudflare/Workers/WorkerBridge.d.ts.map +1 -0
  84. package/lib/Cloudflare/Workers/WorkerBridge.js +149 -0
  85. package/lib/Cloudflare/Workers/WorkerBridge.js.map +1 -0
  86. package/lib/Cloudflare/Workers/WorkerBundle.d.ts.map +1 -1
  87. package/lib/Cloudflare/Workers/WorkerBundle.js +18 -84
  88. package/lib/Cloudflare/Workers/WorkerBundle.js.map +1 -1
  89. package/lib/Cloudflare/Workers/WorkerRuntimeContext.d.ts +8 -0
  90. package/lib/Cloudflare/Workers/WorkerRuntimeContext.d.ts.map +1 -0
  91. package/lib/Cloudflare/Workers/WorkerRuntimeContext.js +113 -0
  92. package/lib/Cloudflare/Workers/WorkerRuntimeContext.js.map +1 -0
  93. package/lib/Cloudflare/Workers/WorkflowBridge.d.ts +26 -0
  94. package/lib/Cloudflare/Workers/WorkflowBridge.d.ts.map +1 -0
  95. package/lib/Cloudflare/Workers/WorkflowBridge.js +41 -0
  96. package/lib/Cloudflare/Workers/WorkflowBridge.js.map +1 -0
  97. package/lib/Cloudflare/Workers/index.d.ts +4 -0
  98. package/lib/Cloudflare/Workers/index.d.ts.map +1 -1
  99. package/lib/Cloudflare/Workers/index.js +4 -0
  100. package/lib/Cloudflare/Workers/index.js.map +1 -1
  101. package/lib/Cloudflare/index.d.ts +4 -2
  102. package/lib/Cloudflare/index.d.ts.map +1 -1
  103. package/lib/Cloudflare/index.js +4 -2
  104. package/lib/Cloudflare/index.js.map +1 -1
  105. package/lib/Neon/Project.d.ts.map +1 -1
  106. package/lib/Neon/Project.js +2 -1
  107. package/lib/Neon/Project.js.map +1 -1
  108. package/lib/Platform.d.ts +3 -4
  109. package/lib/Platform.d.ts.map +1 -1
  110. package/lib/Platform.js.map +1 -1
  111. package/lib/Rpc.d.ts +17 -0
  112. package/lib/Rpc.d.ts.map +1 -0
  113. package/lib/Rpc.js +1 -0
  114. package/lib/Rpc.js.map +1 -0
  115. package/lib/RuntimeContext.d.ts +1 -0
  116. package/lib/RuntimeContext.d.ts.map +1 -1
  117. package/lib/RuntimeContext.js.map +1 -1
  118. package/lib/Sidecar/RpcHandler.d.ts.map +1 -1
  119. package/lib/Sidecar/RpcHandler.js +13 -9
  120. package/lib/Sidecar/RpcHandler.js.map +1 -1
  121. package/lib/State/HttpStateApi.d.ts +10 -0
  122. package/lib/State/HttpStateApi.d.ts.map +1 -1
  123. package/lib/State/HttpStateApi.js +10 -0
  124. package/lib/State/HttpStateApi.js.map +1 -1
  125. package/lib/State/HttpStateStore.d.ts.map +1 -1
  126. package/lib/State/HttpStateStore.js +2 -1
  127. package/lib/State/HttpStateStore.js.map +1 -1
  128. package/lib/State/InMemoryState.d.ts.map +1 -1
  129. package/lib/State/InMemoryState.js +2 -0
  130. package/lib/State/InMemoryState.js.map +1 -1
  131. package/lib/State/LocalState.d.ts.map +1 -1
  132. package/lib/State/LocalState.js +2 -0
  133. package/lib/State/LocalState.js.map +1 -1
  134. package/lib/State/State.d.ts +8 -0
  135. package/lib/State/State.d.ts.map +1 -1
  136. package/lib/State/StateEncoding.d.ts.map +1 -1
  137. package/lib/State/StateEncoding.js +5 -1
  138. package/lib/State/StateEncoding.js.map +1 -1
  139. package/lib/index.d.ts +2 -1
  140. package/lib/index.d.ts.map +1 -1
  141. package/lib/index.js +1 -1
  142. package/lib/index.js.map +1 -1
  143. package/lib/tsconfig.test.tsbuildinfo +1 -1
  144. package/package.json +1 -1
  145. package/src/Apply.ts +44 -10
  146. package/src/Cli/Event.ts +1 -1
  147. package/src/Cloudflare/Bridge.ts +1 -0
  148. package/src/Cloudflare/Container/ContainerBinding.ts +1 -1
  149. package/src/Cloudflare/Fetcher.ts +38 -1
  150. package/src/Cloudflare/Local/SidecarHandlers.ts +3 -6
  151. package/src/Cloudflare/Website/StaticSite.ts +10 -5
  152. package/src/Cloudflare/Website/Vite.ts +7 -4
  153. package/src/Cloudflare/Workers/DurableObjectBridge.ts +180 -0
  154. package/src/Cloudflare/Workers/DurableObjectNamespace.ts +22 -70
  155. package/src/Cloudflare/Workers/HttpServer.ts +25 -39
  156. package/src/Cloudflare/Workers/InferEnv.ts +42 -78
  157. package/src/Cloudflare/Workers/Rpc.ts +18 -417
  158. package/src/Cloudflare/Workers/RpcAsync.ts +63 -0
  159. package/src/Cloudflare/Workers/Worker.ts +107 -548
  160. package/src/Cloudflare/Workers/WorkerAsyncBindings.ts +149 -0
  161. package/src/Cloudflare/Workers/WorkerBinding.ts +103 -0
  162. package/src/Cloudflare/Workers/WorkerBridge.ts +304 -0
  163. package/src/Cloudflare/Workers/WorkerBundle.ts +17 -83
  164. package/src/Cloudflare/Workers/WorkerRuntimeContext.ts +164 -0
  165. package/src/Cloudflare/Workers/WorkflowBridge.ts +92 -0
  166. package/src/Cloudflare/Workers/index.ts +4 -0
  167. package/src/Cloudflare/index.ts +4 -2
  168. package/src/Neon/Project.ts +3 -1
  169. package/src/Platform.ts +3 -4
  170. package/src/Rpc.ts +25 -0
  171. package/src/RuntimeContext.ts +1 -0
  172. package/src/Sidecar/RpcHandler.ts +18 -9
  173. package/src/State/HttpStateApi.ts +11 -0
  174. package/src/State/HttpStateStore.ts +7 -2
  175. package/src/State/InMemoryState.ts +2 -0
  176. package/src/State/LocalState.ts +2 -0
  177. package/src/State/State.ts +8 -0
  178. package/src/State/StateEncoding.ts +8 -1
  179. package/src/index.ts +7 -6
@@ -1,69 +1,31 @@
1
1
  import type * as cf from "@cloudflare/workers-types";
2
2
  import * as workers from "@distilled.cloud/cloudflare/workers";
3
3
  import * as zones from "@distilled.cloud/cloudflare/zones";
4
- import * as Cause from "effect/Cause";
5
4
  import * as Context from "effect/Context";
6
5
  import * as Data from "effect/Data";
7
6
  import * as Effect from "effect/Effect";
8
- import * as Exit from "effect/Exit";
9
7
  import * as Layer from "effect/Layer";
10
- import * as Option from "effect/Option";
11
8
  import * as Path from "effect/Path";
12
9
  import * as Redacted from "effect/Redacted";
13
10
  import * as Schedule from "effect/Schedule";
14
- import * as Scope from "effect/Scope";
15
- import * as Stream from "effect/Stream";
16
11
  import { Unowned } from "../../AdoptPolicy.ts";
17
12
  import { AlchemyContext } from "../../AlchemyContext.ts";
18
13
  import * as Artifacts from "../../Artifacts.ts";
19
- import * as Binding from "../../Binding.ts";
20
14
  import { hashDirectory, type MemoOptions } from "../../Build/Memo.ts";
21
15
  import * as Bundle from "../../Bundle/Bundle.ts";
22
16
  import type { ScopedPlanStatusSession } from "../../Cli/Cli.ts";
23
17
  import { isResolved } from "../../Diff.ts";
24
- import { ExecutionContext } from "../../ExecutionContext.ts";
25
- import type { HttpEffect } from "../../Http.ts";
26
18
  import type { InputProps } from "../../Input.ts";
27
- import * as Output from "../../Output.ts";
28
- import {
29
- Platform,
30
- type Main,
31
- type PlatformProps,
32
- type Rpc,
33
- } from "../../Platform.ts";
19
+ import { Platform, type Main, type PlatformProps } from "../../Platform.ts";
34
20
  import * as Provider from "../../Provider.ts";
35
21
  import { Resource, type ResourceBinding } from "../../Resource.ts";
36
- import * as Serverless from "../../Serverless/index.ts";
37
22
  import { Stack } from "../../Stack.ts";
38
- import { isYieldableEffectLike } from "../../Util/effect.ts";
39
- import type { AiGateway } from "../AiGateway/AiGateway.ts";
40
- import {
41
- isAnalyticsEngineDataset,
42
- type AnalyticsEngineDataset,
43
- } from "../AnalyticsEngine/AnalyticsEngineDataset.ts";
44
- import {
45
- isArtifacts as isArtifactsBinding,
46
- type Artifacts as ArtifactsBinding,
47
- } from "../Artifacts/Artifacts.ts";
48
23
  import { CloudflareEnvironment } from "../CloudflareEnvironment.ts";
49
- import { D1Database } from "../D1/D1Database.ts";
50
- import { isSendEmail, type SendEmail } from "../Email/SendEmail.ts";
51
- import type {
52
- Hyperdrive,
53
- HyperdriveDevOrigin,
54
- } from "../Hyperdrive/Hyperdrive.ts";
55
- import {
56
- isImages as isImagesBinding,
57
- type Images as ImagesBinding,
58
- } from "../Images/Images.ts";
59
- import type { KVNamespace } from "../KV/KVNamespace.ts";
24
+ import type { HyperdriveDevOrigin } from "../Hyperdrive/Hyperdrive.ts";
60
25
  import { SidecarLive } from "../Local/Sidecar.ts";
61
26
  import { CloudflareLogs } from "../Logs.ts";
62
27
  import type { Providers } from "../Providers.ts";
63
- import type { Queue as CloudflareQueue } from "../Queue/Queue.ts";
64
- import type { R2Bucket } from "../R2/R2Bucket.ts";
65
28
  import {
66
- isAssets,
67
29
  readAssets,
68
30
  uploadAssets,
69
31
  type Assets,
@@ -71,28 +33,29 @@ import {
71
33
  type AssetsProps,
72
34
  } from "./Assets.ts";
73
35
  import { getCompatibility } from "./Compatibility.ts";
74
- import {
75
- isDurableObjectExport,
76
- isDurableObjectNamespaceLike,
77
- type DurableObjectNamespaceLike,
78
- } from "./DurableObjectNamespace.ts";
79
- import { workersHttpHandler } from "./HttpServer.ts";
36
+ import { isDurableObjectExport } from "./DurableObjectNamespace.ts";
80
37
  import { LocalWorkerProvider } from "./LocalWorkerProvider.ts";
81
38
  import { Request } from "./Request.ts";
82
- import {
83
- encodeRpcError,
84
- ErrorTag,
85
- makeRpcStub,
86
- toRpcStream,
87
- type RpcErrorEnvelope,
88
- type RpcStreamEnvelope,
89
- } from "./Rpc.ts";
90
39
  import * as Vite from "./Vite.ts";
40
+ import {
41
+ bindWorkerAsyncBindings,
42
+ getCronBindings,
43
+ } from "./WorkerAsyncBindings.ts";
44
+ import type {
45
+ WorkerBinding,
46
+ WorkerBindingResource,
47
+ WorkerBindings,
48
+ WorkerSettingsBinding,
49
+ } from "./WorkerBinding.ts";
91
50
  import { WorkerBundle } from "./WorkerBundle.ts";
92
51
  import { createWorkerName } from "./WorkerName.ts";
52
+ import {
53
+ makeWorkerRuntimeContext,
54
+ type WorkerRuntimeContext,
55
+ } from "./WorkerRuntimeContext.ts";
93
56
 
94
- const WorkerTypeId = "Cloudflare.Worker";
95
- type WorkerTypeId = typeof WorkerTypeId;
57
+ export const WorkerTypeId = "Cloudflare.Worker";
58
+ export type WorkerTypeId = typeof WorkerTypeId;
96
59
 
97
60
  export const isWorker = <T>(value: T): value is T & Worker =>
98
61
  typeof value === "object" &&
@@ -162,16 +125,6 @@ export type WorkerPlacement = Exclude<
162
125
  undefined
163
126
  >;
164
127
 
165
- export type WorkerBinding = Exclude<
166
- workers.PutScriptRequest["metadata"]["bindings"],
167
- undefined
168
- >[number];
169
-
170
- type WorkerSettingsBinding = Exclude<
171
- workers.GetScriptScriptAndVersionSettingResponse["bindings"],
172
- null | undefined
173
- >[number];
174
-
175
128
  export const ExportedHandlerMethods = [
176
129
  "fetch",
177
130
  "tail",
@@ -183,32 +136,20 @@ export const ExportedHandlerMethods = [
183
136
  "queue",
184
137
  ] as const satisfies (keyof cf.ExportedHandler)[];
185
138
 
186
- export interface WorkerRuntimeContext extends Serverless.FunctionContext {
187
- export(name: string, value: any): Effect.Effect<void>;
188
- }
189
-
190
- export type WorkerServices = Worker | Request;
139
+ export type WorkerServices = Worker | Request | WorkerExecutionContext;
191
140
 
192
141
  export type WorkerShape = Main<WorkerServices | WorkerEnvironment>;
193
142
 
194
- export type WorkerBindingResource =
195
- | Assets
196
- | R2Bucket
197
- | D1Database
198
- | KVNamespace
199
- | CloudflareQueue
200
- | AiGateway
201
- | AnalyticsEngineDataset
202
- | SendEmail
203
- | ArtifactsBinding
204
- | ImagesBinding
205
- | Hyperdrive
206
- | Worker
207
- | DurableObjectNamespaceLike<any>;
208
-
209
- export type WorkerBindings = {
210
- [bindingName in string]: WorkerBindingResource;
211
- };
143
+ export type WorkerEnv = Record<
144
+ string,
145
+ | string
146
+ | number
147
+ | boolean
148
+ | null
149
+ | readonly unknown[]
150
+ | { readonly [key: string]: unknown }
151
+ | Redacted.Redacted<string>
152
+ >;
212
153
 
213
154
  export type WorkerBindingProps = {
214
155
  [bindingName in string]:
@@ -233,6 +174,7 @@ export type WorkerAssetsConfig = string | AssetsProps | AssetsWithHash;
233
174
 
234
175
  export interface WorkerProps<
235
176
  Bindings extends WorkerBindingProps = any,
177
+ Env extends WorkerEnv = WorkerEnv,
236
178
  Assets extends WorkerAssetsConfig | undefined =
237
179
  | WorkerAssetsConfig
238
180
  | undefined,
@@ -282,16 +224,7 @@ export interface WorkerProps<
282
224
  };
283
225
  limits?: WorkerLimits;
284
226
  placement?: WorkerPlacement;
285
- env?: Record<
286
- string,
287
- | string
288
- | number
289
- | boolean
290
- | null
291
- | readonly unknown[]
292
- | { readonly [key: string]: unknown }
293
- | Redacted.Redacted<string>
294
- >;
227
+ env?: Env;
295
228
  exports?: string[];
296
229
  bindings?: Bindings;
297
230
  /**
@@ -718,414 +651,40 @@ export const Worker: Platform<
718
651
  WorkerRuntimeContext
719
652
  > & {
720
653
  <
721
- const Bindings extends WorkerBindingProps,
654
+ const Bindings extends WorkerBindingProps = {},
655
+ const Env extends WorkerEnv = {},
722
656
  const Assets extends WorkerAssetsConfig | undefined = undefined,
723
657
  Req = never,
724
658
  >(
725
659
  id: string,
726
660
  props:
727
- | InputProps<WorkerProps<Bindings, Assets>>
728
- | Effect.Effect<InputProps<WorkerProps<Bindings, Assets>>, never, Req>,
661
+ | WorkerProps<Bindings, Env, Assets>
662
+ | Effect.Effect<
663
+ InputProps<WorkerProps<Bindings, Env, Assets>>,
664
+ never,
665
+ Req
666
+ >,
729
667
  ): Effect.Effect<
730
- Worker<{
731
- [binding in keyof NormalizedBindings<
732
- Bindings,
733
- Assets
734
- >]: NormalizedBindings<Bindings, Assets>[binding];
735
- }>,
668
+ Worker<
669
+ {
670
+ [binding in keyof NormalizedBindings<
671
+ Bindings,
672
+ Assets
673
+ >]: NormalizedBindings<Bindings, Assets>[binding];
674
+ } & {
675
+ [K in keyof Env]: Env[K] extends Redacted.Redacted<infer T>
676
+ ? T
677
+ : Env[K];
678
+ }
679
+ >,
736
680
  never,
737
681
  Req | Providers
738
682
  >;
739
683
  } = Platform(WorkerTypeId, {
740
- onCreate: Effect.fnUntraced(function* (
741
- resource: Worker,
742
- props: InputProps<WorkerProps<WorkerBindingProps>>,
743
- ) {
744
- if (props.bindings) {
745
- for (const bindingName in props.bindings) {
746
- // @ts-expect-error
747
- const bindingEff = props.bindings?.[bindingName] as
748
- | WorkerBindingResource
749
- | Effect.Effect<WorkerBindingResource>;
750
- // Bindings can be passed as a plain resource value, an Effect that
751
- // yields a resource, or an effect-class (e.g. a `Cloudflare.Worker`
752
- // class). Resolve the yieldable forms before deriving binding metadata.
753
- const binding = isYieldableEffectLike(bindingEff)
754
- ? ((yield* bindingEff as Effect.Effect<unknown>) as WorkerBindingResource)
755
- : bindingEff;
756
-
757
- const bindingMeta: InputProps<WorkerBinding> | undefined = isAssets(
758
- binding,
759
- )
760
- ? {
761
- type: "assets",
762
- name: bindingName,
763
- }
764
- : isArtifactsBinding(binding)
765
- ? ({
766
- type: "artifacts",
767
- name: bindingName,
768
- namespace: binding.namespace,
769
- } as any)
770
- : isImagesBinding(binding)
771
- ? {
772
- type: "images",
773
- name: bindingName,
774
- }
775
- : isAnalyticsEngineDataset(binding)
776
- ? {
777
- type: "analytics_engine",
778
- name: bindingName,
779
- dataset: binding.dataset,
780
- }
781
- : isSendEmail(binding)
782
- ? {
783
- type: "send_email",
784
- name: bindingName,
785
- destinationAddress: binding.destinationAddress,
786
- allowedDestinationAddresses:
787
- binding.allowedDestinationAddresses,
788
- allowedSenderAddresses: binding.allowedSenderAddresses,
789
- }
790
- : isDurableObjectNamespaceLike(binding)
791
- ? {
792
- type: "durable_object_namespace",
793
- name: bindingName,
794
- className: binding.className ?? binding.name,
795
- }
796
- : binding.Type === "Cloudflare.D1Database"
797
- ? {
798
- type: "d1",
799
- id: binding.databaseId,
800
- name: bindingName,
801
- }
802
- : binding.Type === "Cloudflare.R2Bucket"
803
- ? {
804
- type: "r2_bucket",
805
- name: bindingName,
806
- bucketName: binding.bucketName,
807
- jurisdiction: binding.jurisdiction.pipe(
808
- Output.map((jurisdiction) =>
809
- jurisdiction === "default"
810
- ? undefined
811
- : jurisdiction,
812
- ),
813
- ),
814
- }
815
- : binding.Type === "Cloudflare.KVNamespace"
816
- ? {
817
- type: "kv_namespace",
818
- name: bindingName,
819
- namespaceId: binding.namespaceId,
820
- }
821
- : binding.Type === "Cloudflare.Queue"
822
- ? {
823
- type: "queue",
824
- name: bindingName,
825
- queueName: binding.queueName,
826
- }
827
- : binding.Type === "Cloudflare.AiGateway"
828
- ? {
829
- type: "ai",
830
- name: bindingName,
831
- }
832
- : binding.Type === "Cloudflare.Hyperdrive"
833
- ? {
834
- type: "hyperdrive",
835
- name: bindingName,
836
- id: binding.hyperdriveId,
837
- }
838
- : isWorker(binding)
839
- ? {
840
- type: "service",
841
- name: bindingName,
842
- service: binding.workerName,
843
- }
844
- : // TODO(sam): handle others
845
- undefined;
846
-
847
- if (bindingMeta) {
848
- yield* resource.bind`${bindingName}`({
849
- bindings: [bindingMeta],
850
- });
851
- } else {
852
- return yield* Effect.die(`Unknown binding type: ${bindingName}`);
853
- }
854
- }
855
- }
856
- }),
857
- createRuntimeContext: (id: string): WorkerRuntimeContext => {
858
- const listeners: Effect.Effect<Serverless.FunctionListener>[] = [];
859
- const exports: Record<string, any> = {};
860
- const env: Record<string, any> = {};
861
- let userShape: Record<string, unknown> | undefined;
862
-
863
- const ctx = {
864
- Type: WorkerTypeId,
865
- id,
866
- env,
867
- get: (key: string) =>
868
- Effect.serviceOption(WorkerEnvironment).pipe(
869
- Effect.map(Option.getOrUndefined),
870
- Effect.flatMap((env) =>
871
- env
872
- ? Effect.succeed(env[key])
873
- : Effect.die("WorkerEnvironment not found"),
874
- ),
875
- Effect.flatMap((value) =>
876
- value
877
- ? Effect.succeed(value)
878
- : Effect.die(`Environment variable '${key}' not found`),
879
- ),
880
- Effect.map((json) => {
881
- try {
882
- const value = JSON.parse(json);
883
- // The `set` path serializes Redacted values as
884
- // `{_tag: "Redacted", value: ...}`. After JSON.parse the
885
- // result is a plain object — `Redacted.isRedacted` would
886
- // always return `false` on it — so detect the marker shape
887
- // and rebuild the Redacted wrapper. Plain values pass
888
- // through unchanged.
889
- if (
890
- value !== null &&
891
- typeof value === "object" &&
892
- (value as { _tag?: unknown })._tag === "Redacted" &&
893
- "value" in (value as object)
894
- ) {
895
- return Redacted.make((value as { value: unknown }).value);
896
- }
897
- return value;
898
- } catch {
899
- return json;
900
- }
901
- }),
902
- ) as any,
903
- set: (id: string, output: Output.Output) =>
904
- Effect.sync(() => {
905
- const key = id.replaceAll(/[^a-zA-Z0-9]/g, "_");
906
- // Preserve `Redacted`-ness across the Output → env → Cloudflare
907
- // binding boundary so the put-worker loop can deploy secrets via
908
- // `secret_text` instead of leaking them as `plain_text`. The JSON
909
- // payload still carries the `{_tag: "Redacted", …}` marker so the
910
- // runtime `get` accessor can rebuild the wrapper after Cloudflare
911
- // hands the binding back as a plain string.
912
- env[key] = output.pipe(
913
- Output.map((value) =>
914
- Redacted.isRedacted(value)
915
- ? Redacted.make(
916
- JSON.stringify({
917
- _tag: "Redacted",
918
- value: Redacted.value(value),
919
- }),
920
- )
921
- : JSON.stringify(value),
922
- ),
923
- );
924
- return key;
925
- }),
926
- serve: <Req = never>(
927
- handler: HttpEffect<Req> | Effect.Effect<HttpEffect<Req>>,
928
- options?: { shape?: Record<string, unknown> },
929
- ) => {
930
- // Capture the user's full default-export shape so `exports` can
931
- // expose any non-handler methods on it as RPC methods on the
932
- // deployed `WorkerEntrypoint` subclass — see `__rpc__` below.
933
- if (options?.shape) userShape = options.shape;
934
- return ctx.listen(workersHttpHandler(handler));
935
- },
936
- listen: ((
937
- handler:
938
- | Serverless.FunctionListener
939
- | Effect.Effect<Serverless.FunctionListener>,
940
- ) =>
941
- Effect.sync(() =>
942
- Effect.isEffect(handler)
943
- ? listeners.push(handler)
944
- : listeners.push(Effect.succeed(handler)),
945
- )) as any as Serverless.FunctionContext["listen"],
946
- export: (name: string, value: any) =>
947
- Effect.sync(() => {
948
- exports[name] = value;
949
- }),
950
- exports: Effect.gen(function* () {
951
- const handlers = yield* Effect.all(listeners, {
952
- concurrency: "unbounded",
953
- });
954
- const services = yield* Effect.context();
955
-
956
- // Provide the per-request runtime layer (services + `WorkerExecutionContext`
957
- // + a fresh `ExecutionContext`/`Scope`) to a user effect, then run it.
958
- // This is the bridge between Cloudflare's request-time callback and
959
- // the user's Effect-native handler. Used by both the standard
960
- // `handle()` dispatch (fetch/scheduled/…) and the per-RPC dispatchers
961
- // below — keeping them on a single layer-provision path.
962
- const runUserEffect = <A, E>(
963
- eff: Effect.Effect<A, E>,
964
- context: cf.ExecutionContext,
965
- ): Promise<Exit.Exit<A, E>> => {
966
- const scope = Scope.makeUnsafe();
967
- return eff
968
- .pipe(
969
- Effect.provideContext(services),
970
- Scope.provide(scope),
971
- Effect.provide(Layer.succeed(WorkerExecutionContext, context)),
972
- Effect.provide(
973
- Layer.succeed(ExecutionContext, { scope, cache: {} }),
974
- ),
975
- Effect.runPromiseExit,
976
- )
977
- .finally(() =>
978
- context.waitUntil(
979
- Effect.runPromise(Scope.close(scope, Exit.void)),
980
- ),
981
- );
982
- };
983
-
984
- const handle =
985
- (type: WorkerEvent["type"]) =>
986
- (request: any, env: unknown, context: cf.ExecutionContext) => {
987
- const event: WorkerEvent = {
988
- kind: "Cloudflare.Workers.WorkerEvent",
989
- type,
990
- input: request,
991
- env,
992
- context,
993
- };
994
- for (const handler of handlers) {
995
- const eff = handler(event);
996
- if (Effect.isEffect(eff)) {
997
- return runUserEffect(
998
- eff as Effect.Effect<unknown, unknown>,
999
- context,
1000
- ).then((exit) => {
1001
- if (exit._tag === "Success") return exit.value;
1002
- throw Cause.squash(exit.cause);
1003
- });
1004
- }
1005
- }
1006
- return Promise.reject(new Error("No event handler found"));
1007
- };
1008
-
1009
- // RPC method dispatchers — one per non-handler method on the user's
1010
- // shape. Each dispatcher is invoked by the WorkerEntrypoint bridge
1011
- // as `dispatcher(args, ctx)`: `args` are the user-facing call args,
1012
- // `ctx` is the `this.ctx` that Cloudflare hands the bridge per RPC
1013
- // request. The dispatcher runs the user effect with the same runtime
1014
- // layer the fetch path uses, then envelope-encodes the result so
1015
- // `Effect.fail` round-trips as `RpcErrorEnvelope` and `Stream` as
1016
- // `RpcStreamEnvelope` (consumers wrap the binding with
1017
- // `toPromiseApi`/`bindWorker` to decode).
1018
- const exportedHandlerSet = new Set<string>(ExportedHandlerMethods);
1019
- const rpcDispatchers: Record<
1020
- string,
1021
- (args: unknown[], ctx: cf.ExecutionContext) => Promise<unknown>
1022
- > = {};
1023
- if (userShape) {
1024
- for (const [name, value] of Object.entries(userShape)) {
1025
- if (exportedHandlerSet.has(name)) continue;
1026
- if (typeof value !== "function") continue;
1027
- rpcDispatchers[name] = async (
1028
- args: unknown[],
1029
- context: cf.ExecutionContext,
1030
- ) => {
1031
- const result = (value as (...a: unknown[]) => unknown)(...args);
1032
- if (!Effect.isEffect(result)) return result;
1033
- const exit = await runUserEffect(
1034
- result as Effect.Effect<unknown, unknown>,
1035
- context,
1036
- );
1037
- if (exit._tag === "Success") {
1038
- if (Stream.isStream(exit.value)) {
1039
- return await Effect.runPromise(
1040
- toRpcStream(exit.value) as Effect.Effect<RpcStreamEnvelope>,
1041
- );
1042
- }
1043
- return exit.value;
1044
- }
1045
- const failReason = exit.cause.reasons.find(Cause.isFailReason);
1046
- if (failReason) {
1047
- return {
1048
- _tag: ErrorTag,
1049
- error: encodeRpcError(failReason.error),
1050
- } satisfies RpcErrorEnvelope;
1051
- }
1052
- const dieReason = exit.cause.reasons.find(Cause.isDieReason);
1053
- throw (
1054
- dieReason?.defect ??
1055
- new Error("RPC method failed with an unexpected cause")
1056
- );
1057
- };
1058
- }
1059
- }
1060
-
1061
- return {
1062
- ...exports,
1063
- default: Object.fromEntries(
1064
- ExportedHandlerMethods.map((method) => [method, handle(method)]),
1065
- ),
1066
- __rpc__: rpcDispatchers,
1067
- };
1068
- }),
1069
- };
1070
- return ctx;
1071
- },
1072
- });
1073
-
1074
- export const bindWorker = Effect.fnUntraced(function* <Shape, Req = never>(
1075
- workerEff:
1076
- | (Worker & Rpc<Shape>)
1077
- | Effect.Effect<Worker & Rpc<Shape>, never, Req>,
1078
- ) {
1079
- // Worker classes and regular Effects are both yieldable here.
1080
- const worker = isYieldableEffectLike(workerEff)
1081
- ? yield* workerEff as Effect.Effect<Worker & Rpc<Shape>, never, Req>
1082
- : workerEff;
1083
- const self = yield* Worker;
1084
- yield* self.bind`${worker}`({
1085
- bindings: [
1086
- {
1087
- type: "service",
1088
- name: worker.LogicalId,
1089
- service: worker.workerName,
1090
- },
1091
- ],
1092
- });
1093
-
1094
- // `bindWorker` runs at *init* phase (both at plantime and at runtime
1095
- // cold-start). `WorkerEnvironment` only exists at exec phase on the
1096
- // deployed worker, so we hand `makeRpcStub` an `Effect<stub>` that
1097
- // resolves the binding lazily on each method call.
1098
- const stubEff = WorkerEnvironment.pipe(
1099
- Effect.map((env) => (env as Record<string, unknown>)[worker.LogicalId]),
1100
- );
1101
- return makeRpcStub<Shape>(stubEff);
684
+ onCreate: bindWorkerAsyncBindings,
685
+ createRuntimeContext: makeWorkerRuntimeContext,
1102
686
  });
1103
687
 
1104
- export class BindWorkerPolicy extends Binding.Policy<
1105
- BindWorkerPolicy,
1106
- (worker: Worker) => Effect.Effect<void>
1107
- >()("Cloudflare.Worker.Bind") {}
1108
-
1109
- export const BindWorkerPolicyLive = BindWorkerPolicy.layer.succeed(
1110
- Effect.fn(function* (host, worker: Worker) {
1111
- if (isWorker(host)) {
1112
- yield* host.bind`${worker}`({
1113
- bindings: [
1114
- {
1115
- type: "service",
1116
- name: worker.LogicalId,
1117
- service: worker.workerName,
1118
- },
1119
- ],
1120
- });
1121
- } else {
1122
- return yield* Effect.die(
1123
- new Error(`BindWorkerPolicy does not support runtime '${host.Type}'`),
1124
- );
1125
- }
1126
- }),
1127
- );
1128
-
1129
688
  class MissingDurableObjectNamespaces extends Data.TaggedError(
1130
689
  "MissingDurableObjectNamespaces",
1131
690
  )<{
@@ -1133,59 +692,6 @@ class MissingDurableObjectNamespaces extends Data.TaggedError(
1133
692
  expected: string[];
1134
693
  }> {}
1135
694
 
1136
- function bumpMigrationTagVersion(
1137
- oldTag: string | undefined,
1138
- ): string | undefined {
1139
- if (!oldTag) return undefined;
1140
- const version = oldTag.match(/^(alchemy:)?v(\d+)$/)?.[2];
1141
- if (!version) return "alchemy:v1";
1142
- return `alchemy:v${parseInt(version, 10) + 1}`;
1143
- }
1144
-
1145
- function getDurableObjectBindings(
1146
- bindings: ReadonlyArray<ResourceBinding>,
1147
- workerName: string,
1148
- ) {
1149
- return bindings.flatMap((binding) =>
1150
- (binding.data.bindings ?? []).flatMap((item: WorkerBinding) =>
1151
- item.type === "durable_object_namespace" &&
1152
- "className" in item &&
1153
- item.className &&
1154
- (!("scriptName" in item) ||
1155
- !item.scriptName ||
1156
- item.scriptName === workerName)
1157
- ? [
1158
- {
1159
- logicalId: binding.sid,
1160
- bindingName: item.name,
1161
- className: item.className,
1162
- },
1163
- ]
1164
- : [],
1165
- ),
1166
- );
1167
- }
1168
-
1169
- export function getCronBindings(
1170
- bindings: ReadonlyArray<ResourceBinding<Worker["Binding"]>>,
1171
- ) {
1172
- return Array.from(new Set(bindings.flatMap((b) => b.data.crons ?? [])));
1173
- }
1174
-
1175
- function getDurableObjectTagMap(tags: ReadonlyArray<string>) {
1176
- return Object.fromEntries(
1177
- tags.flatMap((tag) => {
1178
- if (!tag.startsWith("alchemy:do:")) {
1179
- return [];
1180
- }
1181
- const parts = tag.split(":");
1182
- const logicalId = parts[2];
1183
- const className = parts.slice(3).join(":");
1184
- return logicalId && className ? [[logicalId, className]] : [];
1185
- }),
1186
- );
1187
- }
1188
-
1189
695
  const selectLayer = <
1190
696
  LayerLive extends Layer.Layer<any, any, any>,
1191
697
  LayerDev extends Layer.Layer<any, any, any>,
@@ -2517,6 +2023,12 @@ export const LiveWorkerProvider = () =>
2517
2023
  yield* deleteScript({
2518
2024
  accountId: output.accountId,
2519
2025
  scriptName: output.workerName,
2026
+ // Force teardown of queue consumers, durable object classes, and
2027
+ // service bindings hanging off this worker. Without `force`, those
2028
+ // conditions raise QueueConsumerConflict / ServiceBindingConflict
2029
+ // and leave the script in CF. Alchemy is the source of truth for
2030
+ // the worker, so we want a hard delete on teardown.
2031
+ force: true,
2520
2032
  }).pipe(Effect.catchTag("WorkerNotFound", () => Effect.void));
2521
2033
  }),
2522
2034
  tail: ({ output }) =>
@@ -2563,3 +2075,50 @@ const contentTypeFromExtension = (extension: string) => {
2563
2075
  return "application/octet-stream";
2564
2076
  }
2565
2077
  };
2078
+
2079
+ function bumpMigrationTagVersion(
2080
+ oldTag: string | undefined,
2081
+ ): string | undefined {
2082
+ if (!oldTag) return undefined;
2083
+ const version = oldTag.match(/^(alchemy:)?v(\d+)$/)?.[2];
2084
+ if (!version) return "alchemy:v1";
2085
+ return `alchemy:v${parseInt(version, 10) + 1}`;
2086
+ }
2087
+
2088
+ function getDurableObjectBindings(
2089
+ bindings: ReadonlyArray<ResourceBinding>,
2090
+ workerName: string,
2091
+ ) {
2092
+ return bindings.flatMap((binding) =>
2093
+ (binding.data.bindings ?? []).flatMap((item: WorkerBinding) =>
2094
+ item.type === "durable_object_namespace" &&
2095
+ "className" in item &&
2096
+ item.className &&
2097
+ (!("scriptName" in item) ||
2098
+ !item.scriptName ||
2099
+ item.scriptName === workerName)
2100
+ ? [
2101
+ {
2102
+ logicalId: binding.sid,
2103
+ bindingName: item.name,
2104
+ className: item.className,
2105
+ },
2106
+ ]
2107
+ : [],
2108
+ ),
2109
+ );
2110
+ }
2111
+
2112
+ function getDurableObjectTagMap(tags: ReadonlyArray<string>) {
2113
+ return Object.fromEntries(
2114
+ tags.flatMap((tag) => {
2115
+ if (!tag.startsWith("alchemy:do:")) {
2116
+ return [];
2117
+ }
2118
+ const parts = tag.split(":");
2119
+ const logicalId = parts[2];
2120
+ const className = parts.slice(3).join(":");
2121
+ return logicalId && className ? [[logicalId, className]] : [];
2122
+ }),
2123
+ );
2124
+ }