alchemy 2.0.0-beta.43 → 2.0.0-beta.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/bin/cli.js +18 -5
  2. package/bin/exec.js +315 -233
  3. package/bin/exec.js.map +1 -1
  4. package/lib/Auth/Lock.js +2 -2
  5. package/lib/Auth/Lock.js.map +1 -1
  6. package/lib/Bundle/Bundle.d.ts +11 -0
  7. package/lib/Bundle/Bundle.d.ts.map +1 -1
  8. package/lib/Bundle/Bundle.js +19 -12
  9. package/lib/Bundle/Bundle.js.map +1 -1
  10. package/lib/Bundle/BundleAnalyzerPlugin.d.ts +25 -0
  11. package/lib/Bundle/BundleAnalyzerPlugin.d.ts.map +1 -0
  12. package/lib/Bundle/BundleAnalyzerPlugin.js +16 -0
  13. package/lib/Bundle/BundleAnalyzerPlugin.js.map +1 -0
  14. package/lib/Bundle/RawPlugin.d.ts +36 -0
  15. package/lib/Bundle/RawPlugin.d.ts.map +1 -0
  16. package/lib/Bundle/RawPlugin.js +80 -0
  17. package/lib/Bundle/RawPlugin.js.map +1 -0
  18. package/lib/Bundle/index.d.ts +2 -0
  19. package/lib/Bundle/index.d.ts.map +1 -1
  20. package/lib/Bundle/index.js +2 -0
  21. package/lib/Bundle/index.js.map +1 -1
  22. package/lib/Cli/commands/dev.d.ts +3 -2
  23. package/lib/Cli/commands/dev.d.ts.map +1 -1
  24. package/lib/Cli/commands/dev.js +9 -1
  25. package/lib/Cli/commands/dev.js.map +1 -1
  26. package/lib/Cli/exec.d.ts.map +1 -1
  27. package/lib/Cli/exec.js +2 -1
  28. package/lib/Cli/exec.js.map +1 -1
  29. package/lib/Cloudflare/Local.d.ts +2 -0
  30. package/lib/Cloudflare/Local.d.ts.map +1 -0
  31. package/lib/Cloudflare/Local.js +9 -0
  32. package/lib/Cloudflare/Local.js.map +1 -0
  33. package/lib/Cloudflare/Providers.d.ts.map +1 -1
  34. package/lib/Cloudflare/Providers.js +3 -1
  35. package/lib/Cloudflare/Providers.js.map +1 -1
  36. package/lib/Cloudflare/Workers/DurableObjectBridge.d.ts.map +1 -1
  37. package/lib/Cloudflare/Workers/DurableObjectBridge.js +0 -1
  38. package/lib/Cloudflare/Workers/DurableObjectBridge.js.map +1 -1
  39. package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts +22 -3
  40. package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts.map +1 -1
  41. package/lib/Cloudflare/Workers/LocalWorkerProvider.js +413 -21
  42. package/lib/Cloudflare/Workers/LocalWorkerProvider.js.map +1 -1
  43. package/lib/Cloudflare/Workers/Worker.d.ts +6 -19
  44. package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
  45. package/lib/Cloudflare/Workers/Worker.js +3 -4
  46. package/lib/Cloudflare/Workers/Worker.js.map +1 -1
  47. package/lib/Cloudflare/Workers/WorkerBridge.d.ts.map +1 -1
  48. package/lib/Cloudflare/Workers/WorkerBridge.js +3 -5
  49. package/lib/Cloudflare/Workers/WorkerBridge.js.map +1 -1
  50. package/lib/Cloudflare/Workers/WorkerBundle.d.ts +1 -2
  51. package/lib/Cloudflare/Workers/WorkerBundle.d.ts.map +1 -1
  52. package/lib/Cloudflare/Workers/WorkerBundle.js +3 -6
  53. package/lib/Cloudflare/Workers/WorkerBundle.js.map +1 -1
  54. package/lib/Cloudflare/Zaraz/ZarazConfig.d.ts +184 -0
  55. package/lib/Cloudflare/Zaraz/ZarazConfig.d.ts.map +1 -0
  56. package/lib/Cloudflare/Zaraz/ZarazConfig.js +216 -0
  57. package/lib/Cloudflare/Zaraz/ZarazConfig.js.map +1 -0
  58. package/lib/Cloudflare/Zaraz/ZarazEventTypes.d.ts +188 -0
  59. package/lib/Cloudflare/Zaraz/ZarazEventTypes.d.ts.map +1 -0
  60. package/lib/Cloudflare/Zaraz/ZarazEventTypes.js +4 -0
  61. package/lib/Cloudflare/Zaraz/ZarazEventTypes.js.map +1 -0
  62. package/lib/Cloudflare/Zaraz/index.d.ts +3 -0
  63. package/lib/Cloudflare/Zaraz/index.d.ts.map +1 -0
  64. package/lib/Cloudflare/Zaraz/index.js +2 -0
  65. package/lib/Cloudflare/Zaraz/index.js.map +1 -0
  66. package/lib/Cloudflare/index.d.ts +1 -0
  67. package/lib/Cloudflare/index.d.ts.map +1 -1
  68. package/lib/Cloudflare/index.js +1 -0
  69. package/lib/Cloudflare/index.js.map +1 -1
  70. package/lib/Local/RpcProvider.d.ts +54 -0
  71. package/lib/Local/RpcProvider.d.ts.map +1 -0
  72. package/lib/Local/RpcProvider.js +57 -0
  73. package/lib/Local/RpcProvider.js.map +1 -0
  74. package/lib/Local/RpcProviderProxy.d.ts +19 -0
  75. package/lib/Local/RpcProviderProxy.d.ts.map +1 -0
  76. package/lib/Local/RpcProviderProxy.js +48 -0
  77. package/lib/Local/RpcProviderProxy.js.map +1 -0
  78. package/lib/Local/RpcSerialization.d.ts +34 -0
  79. package/lib/Local/RpcSerialization.d.ts.map +1 -0
  80. package/lib/Local/RpcSerialization.js +120 -0
  81. package/lib/Local/RpcSerialization.js.map +1 -0
  82. package/lib/Local/RpcServer.d.ts +63 -0
  83. package/lib/Local/RpcServer.d.ts.map +1 -0
  84. package/lib/Local/RpcServer.js +72 -0
  85. package/lib/Local/RpcServer.js.map +1 -0
  86. package/lib/Local/RpcServerBun.d.ts +3 -0
  87. package/lib/Local/RpcServerBun.d.ts.map +1 -0
  88. package/lib/Local/RpcServerBun.js +47 -0
  89. package/lib/Local/RpcServerBun.js.map +1 -0
  90. package/lib/Local/RpcServerEnvironment.d.ts +20 -0
  91. package/lib/Local/RpcServerEnvironment.d.ts.map +1 -0
  92. package/lib/Local/RpcServerEnvironment.js +22 -0
  93. package/lib/Local/RpcServerEnvironment.js.map +1 -0
  94. package/lib/Local/RpcServerNode.d.ts +3 -0
  95. package/lib/Local/RpcServerNode.d.ts.map +1 -0
  96. package/lib/Local/RpcServerNode.js +43 -0
  97. package/lib/Local/RpcServerNode.js.map +1 -0
  98. package/lib/Local/RpcServerSession.d.ts +27 -0
  99. package/lib/Local/RpcServerSession.d.ts.map +1 -0
  100. package/lib/Local/RpcServerSession.js +68 -0
  101. package/lib/Local/RpcServerSession.js.map +1 -0
  102. package/lib/Local/RpcSpawner.d.ts +21 -0
  103. package/lib/Local/RpcSpawner.d.ts.map +1 -0
  104. package/lib/Local/RpcSpawner.js +132 -0
  105. package/lib/Local/RpcSpawner.js.map +1 -0
  106. package/lib/Util/PlatformServices.d.ts +7 -0
  107. package/lib/Util/PlatformServices.d.ts.map +1 -1
  108. package/lib/Util/PlatformServices.js +22 -11
  109. package/lib/Util/PlatformServices.js.map +1 -1
  110. package/lib/Util/camel.d.ts.map +1 -1
  111. package/lib/Util/camel.js +2 -3
  112. package/lib/Util/camel.js.map +1 -1
  113. package/lib/Util/data.d.ts +5 -0
  114. package/lib/Util/data.d.ts.map +1 -1
  115. package/lib/Util/data.js +29 -0
  116. package/lib/Util/data.js.map +1 -1
  117. package/lib/Util/stable.d.ts.map +1 -1
  118. package/lib/Util/stable.js +3 -6
  119. package/lib/Util/stable.js.map +1 -1
  120. package/lib/tsconfig.test.tsbuildinfo +1 -1
  121. package/package.json +12 -14
  122. package/src/Auth/Lock.ts +2 -2
  123. package/src/Bundle/Bundle.ts +35 -13
  124. package/src/Bundle/BundleAnalyzerPlugin.ts +33 -0
  125. package/src/Bundle/RawPlugin.ts +82 -0
  126. package/src/Bundle/index.ts +2 -0
  127. package/src/Cli/commands/dev.ts +51 -37
  128. package/src/Cli/exec.ts +12 -7
  129. package/src/Cloudflare/Local.ts +23 -0
  130. package/src/Cloudflare/Providers.ts +3 -0
  131. package/src/Cloudflare/Workers/DurableObjectBridge.ts +0 -1
  132. package/src/Cloudflare/Workers/LocalWorkerProvider.ts +547 -21
  133. package/src/Cloudflare/Workers/Worker.ts +11 -22
  134. package/src/Cloudflare/Workers/WorkerBridge.ts +3 -5
  135. package/src/Cloudflare/Workers/WorkerBundle.ts +4 -8
  136. package/src/Cloudflare/Zaraz/ZarazConfig.ts +403 -0
  137. package/src/Cloudflare/Zaraz/ZarazEventTypes.ts +257 -0
  138. package/src/Cloudflare/Zaraz/index.ts +25 -0
  139. package/src/Cloudflare/index.ts +1 -0
  140. package/src/Local/RpcProvider.ts +102 -0
  141. package/src/Local/RpcProviderProxy.ts +86 -0
  142. package/src/Local/RpcSerialization.ts +225 -0
  143. package/src/Local/RpcServer.ts +138 -0
  144. package/src/Local/RpcServerBun.ts +59 -0
  145. package/src/Local/RpcServerEnvironment.ts +55 -0
  146. package/src/Local/RpcServerNode.ts +58 -0
  147. package/src/Local/RpcServerSession.ts +87 -0
  148. package/src/Local/RpcSpawner.ts +196 -0
  149. package/src/Util/PlatformServices.ts +41 -27
  150. package/src/Util/camel.ts +3 -5
  151. package/src/Util/data.ts +38 -0
  152. package/src/Util/stable.ts +4 -10
  153. package/lib/Cloudflare/Local/Sidecar.d.ts +0 -63
  154. package/lib/Cloudflare/Local/Sidecar.d.ts.map +0 -1
  155. package/lib/Cloudflare/Local/Sidecar.js +0 -50
  156. package/lib/Cloudflare/Local/Sidecar.js.map +0 -1
  157. package/lib/Cloudflare/Local/SidecarHandlers.d.ts +0 -8
  158. package/lib/Cloudflare/Local/SidecarHandlers.d.ts.map +0 -1
  159. package/lib/Cloudflare/Local/SidecarHandlers.js +0 -381
  160. package/lib/Cloudflare/Local/SidecarHandlers.js.map +0 -1
  161. package/lib/Cloudflare/Local/SidecarServer.d.ts +0 -2
  162. package/lib/Cloudflare/Local/SidecarServer.d.ts.map +0 -1
  163. package/lib/Cloudflare/Local/SidecarServer.js +0 -34
  164. package/lib/Cloudflare/Local/SidecarServer.js.map +0 -1
  165. package/lib/Sidecar/Lock.d.ts +0 -23
  166. package/lib/Sidecar/Lock.d.ts.map +0 -1
  167. package/lib/Sidecar/Lock.js +0 -39
  168. package/lib/Sidecar/Lock.js.map +0 -1
  169. package/lib/Sidecar/RpcClient.d.ts +0 -15
  170. package/lib/Sidecar/RpcClient.d.ts.map +0 -1
  171. package/lib/Sidecar/RpcClient.js +0 -78
  172. package/lib/Sidecar/RpcClient.js.map +0 -1
  173. package/lib/Sidecar/RpcHandler.d.ts +0 -51
  174. package/lib/Sidecar/RpcHandler.d.ts.map +0 -1
  175. package/lib/Sidecar/RpcHandler.js +0 -87
  176. package/lib/Sidecar/RpcHandler.js.map +0 -1
  177. package/lib/Sidecar/RpcPaths.d.ts +0 -19
  178. package/lib/Sidecar/RpcPaths.d.ts.map +0 -1
  179. package/lib/Sidecar/RpcPaths.js +0 -31
  180. package/lib/Sidecar/RpcPaths.js.map +0 -1
  181. package/lib/Sidecar/RpcServer.d.ts +0 -30
  182. package/lib/Sidecar/RpcServer.d.ts.map +0 -1
  183. package/lib/Sidecar/RpcServer.js +0 -105
  184. package/lib/Sidecar/RpcServer.js.map +0 -1
  185. package/lib/Sidecar/RpcServerBun.d.ts +0 -4
  186. package/lib/Sidecar/RpcServerBun.d.ts.map +0 -1
  187. package/lib/Sidecar/RpcServerBun.js +0 -28
  188. package/lib/Sidecar/RpcServerBun.js.map +0 -1
  189. package/lib/Sidecar/RpcServerNode.d.ts +0 -4
  190. package/lib/Sidecar/RpcServerNode.d.ts.map +0 -1
  191. package/lib/Sidecar/RpcServerNode.js +0 -39
  192. package/lib/Sidecar/RpcServerNode.js.map +0 -1
  193. package/lib/Sidecar/RpcServices.d.ts +0 -6
  194. package/lib/Sidecar/RpcServices.d.ts.map +0 -1
  195. package/lib/Sidecar/RpcServices.js +0 -29
  196. package/lib/Sidecar/RpcServices.js.map +0 -1
  197. package/src/Cloudflare/Local/Sidecar.ts +0 -81
  198. package/src/Cloudflare/Local/SidecarHandlers.ts +0 -491
  199. package/src/Cloudflare/Local/SidecarServer.ts +0 -70
  200. package/src/Sidecar/Lock.ts +0 -66
  201. package/src/Sidecar/RpcClient.ts +0 -142
  202. package/src/Sidecar/RpcHandler.ts +0 -205
  203. package/src/Sidecar/RpcPaths.ts +0 -39
  204. package/src/Sidecar/RpcServer.ts +0 -153
  205. package/src/Sidecar/RpcServerBun.ts +0 -41
  206. package/src/Sidecar/RpcServerNode.ts +0 -51
  207. package/src/Sidecar/RpcServices.ts +0 -48
@@ -1,37 +1,563 @@
1
+ import {
2
+ layerLocalProxy,
3
+ layerRuntime,
4
+ Runtime,
5
+ RuntimeError,
6
+ type BindingHook,
7
+ type BindingServices,
8
+ type HyperdriveOrigin,
9
+ type Module,
10
+ type Assets as RuntimeAssets,
11
+ type DurableObjectNamespace as RuntimeDurableObjectNamespace,
12
+ type RuntimeServices,
13
+ } from "@distilled.cloud/cloudflare-runtime";
14
+ import {
15
+ Ai,
16
+ Artifacts,
17
+ Assets,
18
+ Browser,
19
+ D1,
20
+ Data,
21
+ DurableObjectNamespace,
22
+ Hyperdrive,
23
+ Images,
24
+ Json,
25
+ KvNamespace,
26
+ R2Bucket,
27
+ Service,
28
+ Text,
29
+ VersionMetadata,
30
+ WasmModule,
31
+ WorkerLoader,
32
+ } from "@distilled.cloud/cloudflare-runtime/bindings";
33
+ import * as LocalProxy from "@distilled.cloud/cloudflare-runtime/proxy/LocalProxy";
34
+ import * as Cause from "effect/Cause";
1
35
  import * as Effect from "effect/Effect";
2
- import * as Provider from "../../Provider.ts";
36
+ import * as Exit from "effect/Exit";
37
+ import * as Fiber from "effect/Fiber";
38
+ import * as Hash from "effect/Hash";
39
+ import * as Layer from "effect/Layer";
40
+ import * as Path from "effect/Path";
41
+ import * as Redacted from "effect/Redacted";
42
+ import * as Result from "effect/Result";
43
+ import * as Schema from "effect/Schema";
44
+ import * as Scope from "effect/Scope";
45
+ import * as Stream from "effect/Stream";
46
+ import { AlchemyContext } from "../../AlchemyContext.ts";
47
+ import type * as Bundle from "../../Bundle/Bundle.ts";
48
+ import { InstanceId } from "../../InstanceId.ts";
49
+ import * as RpcProvider from "../../Local/RpcProvider.ts";
50
+ import type { ResourceBinding } from "../../Resource.ts";
3
51
  import { Stack } from "../../Stack.ts";
4
- import { Sidecar } from "../Local/Sidecar.ts";
52
+ import { Stage } from "../../Stage.ts";
53
+ import { CloudflareEnvironment } from "../CloudflareEnvironment.ts";
54
+ import type { WorkerAssetsConfig, WorkerProps } from "../Workers/Worker.ts";
55
+ import { getCompatibility } from "./Compatibility.ts";
56
+ import * as Vite from "./Vite.ts";
5
57
  import { Worker } from "./Worker.ts";
58
+ import { getCronBindings } from "./WorkerAsyncBindings.ts";
59
+ import type { WorkerBinding } from "./WorkerBinding.ts";
60
+ import { WorkerBundle, type WorkerBundleOptions } from "./WorkerBundle.ts";
61
+ import { createWorkerName } from "./WorkerName.ts";
62
+
63
+ export class WorkerValidationError extends Schema.TaggedErrorClass<WorkerValidationError>()(
64
+ "WorkerValidationError",
65
+ {
66
+ message: Schema.String,
67
+ hint: Schema.optional(Schema.String),
68
+ value: Schema.Unknown,
69
+ },
70
+ ) {}
71
+
72
+ export const localRuntimeServices = (options: { port?: number } = {}) =>
73
+ RpcProvider.providerServicesEffect(
74
+ Effect.gen(function* () {
75
+ const { accountId } = yield* CloudflareEnvironment;
76
+ const { dotAlchemy } = yield* AlchemyContext;
77
+ const path = yield* Path.Path;
78
+ return Layer.merge(
79
+ layerRuntime({
80
+ api: {
81
+ accountId,
82
+ },
83
+ storage: {
84
+ directory: path.join(dotAlchemy, "local"),
85
+ },
86
+ }),
87
+ layerLocalProxy(options.port ?? 0),
88
+ );
89
+ }),
90
+ );
6
91
 
7
92
  export const LocalWorkerProvider = () =>
8
- Provider.effect(
93
+ RpcProvider.effect(
9
94
  Worker,
95
+ import.meta.resolve(
96
+ // `import.meta.resolve(<string>)` is a runtime API — TypeScript's
97
+ // `rewriteRelativeImportExtensions` does NOT touch the string literal, so
98
+ // we have to pick the right extension ourselves. `import.meta.url` reflects
99
+ // the actual on-disk extension of *this* file (`.ts` when loaded from
100
+ // `src/` under Bun or vitest, `.js` when loaded from the compiled `lib/`
101
+ // under Node), which is exactly the signal we need.
102
+ import.meta.url.endsWith(".ts") ? "../Local.ts" : "../Local.js",
103
+ import.meta.url,
104
+ ),
10
105
  Effect.gen(function* () {
106
+ const { accountId } = yield* CloudflareEnvironment;
107
+ const bundler = yield* WorkerBundle;
108
+ const runtime = yield* Runtime;
11
109
  const stack = yield* Stack;
12
- const sidecar = yield* Sidecar;
13
- return {
14
- diff: ({ id, news, newBindings, instanceId }) =>
15
- sidecar.diff({
110
+ const path = yield* Path.Path;
111
+ const localProxy = yield* LocalProxy.LocalProxy;
112
+
113
+ const toRuntimeModules = Effect.fn(function* (
114
+ bundle: Bundle.BundleOutput,
115
+ ) {
116
+ const modules: Module[] = [];
117
+ for (const file of bundle.files) {
118
+ const ext = path.extname(file.path);
119
+ const type = moduleTypeFromExtension(ext);
120
+ if (type === "SourceMap") continue;
121
+ if (type === "Data" || type === "Wasm") {
122
+ if (!(file.content instanceof Uint8Array)) {
123
+ return yield* new WorkerValidationError({
124
+ message: `Expected Uint8Array for ${file.path} (${type})`,
125
+ value: file.content,
126
+ });
127
+ }
128
+ modules.push({
129
+ name: file.path,
130
+ type,
131
+ content: file.content,
132
+ });
133
+ } else {
134
+ if (typeof file.content !== "string") {
135
+ return yield* new WorkerValidationError({
136
+ message: `Expected string for ${file.path} (${type})`,
137
+ value: file.content,
138
+ });
139
+ }
140
+ modules.push({
141
+ name: file.path,
142
+ type,
143
+ content: file.content,
144
+ });
145
+ }
146
+ }
147
+ return modules;
148
+ });
149
+
150
+ const serveScoped = Effect.fnUntraced(function* (
151
+ worker: WorkerConfig,
152
+ bundle: Bundle.BundleOutput,
153
+ ) {
154
+ const scope = yield* Effect.scope.pipe(Effect.flatMap(Scope.fork));
155
+ const address = yield* runtime
156
+ .start({
157
+ name: worker.name,
158
+ compatibilityDate: worker.compatibility.date,
159
+ compatibilityFlags: worker.compatibility.flags,
160
+ bindings: worker.workerBindings as never,
161
+ hyperdrives: worker.hyperdrives,
162
+ durableObjectNamespaces: toRuntimeDurableObjectNamespaces(
163
+ worker.durableObjectNamespaces,
164
+ ),
165
+ modules: yield* toRuntimeModules(bundle),
166
+ assets: toRuntimeAssets(worker.assets),
167
+ })
168
+ .pipe(Scope.provide(scope));
169
+ const previous = workerdScopes.get(worker.id);
170
+ if (previous) {
171
+ yield* Effect.forkDetach(Scope.close(previous, Exit.void));
172
+ }
173
+ workerdScopes.set(worker.id, scope);
174
+ yield* localProxy.setLocalAddress(worker.id, address);
175
+ return address;
176
+ });
177
+
178
+ const buildConfig = Effect.fn(function* ({
179
+ id,
180
+ props,
181
+ bindings,
182
+ instanceId,
183
+ }: {
184
+ id: string;
185
+ props: WorkerProps;
186
+ bindings: ResourceBinding<Worker["Binding"]>[];
187
+ instanceId: string;
188
+ }) {
189
+ const name = yield* createWorkerName(id, props.name).pipe(
190
+ Effect.provideService(Stack, stack),
191
+ Effect.provideService(Stage, stack.stage),
192
+ Effect.provideService(InstanceId, instanceId),
193
+ );
194
+ const compatibility = getCompatibility(props);
195
+ const workerBindings: BindingHook<BindingServices>[] = [];
196
+ const durableObjectNamespaces: Record<string, string> = {};
197
+ const hyperdrives: Record<string, Required<HyperdriveOrigin>> = {};
198
+ for (const { data } of bindings) {
199
+ for (const binding of data.bindings ?? []) {
200
+ if (
201
+ binding.type === "durable_object_namespace" &&
202
+ // The `durableObjectNamespaces` property is only used to declare DOs in this worker.
203
+ // Otherwise, it's a cross-worker durable object binding, which cloudflare-runtime handles automatically.
204
+ (!binding.scriptName || binding.scriptName === name)
205
+ ) {
206
+ // Reuse the existing namespace id if it was provided, otherwise generate a new one.
207
+ // `workerd` uses this for the object's storage path, so it must be safe to use as a file name.
208
+ durableObjectNamespaces[binding.className] =
209
+ binding.namespaceId ??
210
+ encodeURIComponent(`${id}-${binding.className}`);
211
+ }
212
+ workerBindings.push(yield* toRuntimeBinding(binding));
213
+ }
214
+ if (data.hyperdrives) {
215
+ for (const [id, origin] of Object.entries(data.hyperdrives)) {
216
+ hyperdrives[id] = {
217
+ scheme: origin.scheme,
218
+ host: origin.host,
219
+ port: origin.port,
220
+ user: origin.user,
221
+ database: origin.database,
222
+ password: Redacted.isRedacted(origin.password)
223
+ ? Redacted.value(origin.password)
224
+ : origin.password,
225
+ sslmode: origin.sslmode,
226
+ };
227
+ }
228
+ }
229
+ }
230
+ for (const [key, value] of Object.entries(props.env ?? {})) {
231
+ if (value === undefined) continue;
232
+ if (Redacted.isRedacted(value)) {
233
+ workerBindings.push(Text.binding(key, Redacted.value(value)));
234
+ } else if (typeof value === "string") {
235
+ workerBindings.push(Text.binding(key, value));
236
+ } else {
237
+ workerBindings.push(Json.binding(key, value));
238
+ }
239
+ }
240
+ return {
241
+ id,
242
+ name,
243
+ compatibility,
244
+ workerBindings,
245
+ durableObjectNamespaces,
246
+ hyperdrives,
247
+ bundleOptions: {
16
248
  id,
17
- props: news as any,
18
- bindings: newBindings as any,
19
- stack,
20
- instanceId,
249
+ main: props.main,
250
+ compatibility,
251
+ entry: props.isExternal
252
+ ? { kind: "external" }
253
+ : { kind: "effect", exports: (props.exports ?? {}) as any },
254
+ stack: { name: stack.name, stage: stack.stage },
255
+ extraOptions: props.build,
256
+ } satisfies WorkerBundleOptions,
257
+ assets: props.assets,
258
+ };
259
+ });
260
+
261
+ type WorkerConfig = Effect.Success<ReturnType<typeof buildConfig>>;
262
+
263
+ const runServer = Effect.fnUntraced(function* (worker: WorkerConfig) {
264
+ let start = Date.now();
265
+ let status: "start" | "update" = "start";
266
+ yield* bundler.watch(worker.bundleOptions).pipe(
267
+ Stream.tap((event) => {
268
+ if (event._tag === "Start") {
269
+ start = Date.now();
270
+ if (status === "update") {
271
+ return Effect.log(`[${worker.id}] Rebuilding`);
272
+ }
273
+ } else if (event._tag === "Error") {
274
+ return Effect.logError(
275
+ `[${worker.id}] Bundle error`,
276
+ event.error,
277
+ );
278
+ }
279
+ return Effect.void;
21
280
  }),
22
- // The local sidecar `serve` operation is itself a true upsert:
23
- // it tears down any existing process for the worker name and
24
- // starts a fresh one with the latest bindings, so observe and
25
- // sync collapse into a single sidecar call.
26
- reconcile: ({ id, news, bindings, instanceId }) =>
27
- sidecar.reconcile({
281
+ Stream.filterMap((event) =>
282
+ event._tag === "Success"
283
+ ? Result.succeed(event.output)
284
+ : Result.failVoid,
285
+ ),
286
+ Stream.mapEffect((bundle) =>
287
+ serveScoped(worker, bundle).pipe(
288
+ Effect.exit,
289
+ Effect.tap((exit) => {
290
+ if (exit._tag === "Success") {
291
+ const message = Effect.log(
292
+ `[${worker.id}] ${status === "update" ? "Updated" : "Started"} in ${Math.round(Date.now() - start)}ms`,
293
+ );
294
+ status = "update";
295
+ return message;
296
+ } else {
297
+ return Effect.logError(
298
+ `[${worker.id}] Error`,
299
+ Cause.squash(exit.cause),
300
+ );
301
+ }
302
+ }),
303
+ ),
304
+ ),
305
+ Stream.runDrain,
306
+ Effect.forkScoped,
307
+ );
308
+ });
309
+
310
+ const rootScope = yield* Effect.scope;
311
+ const workerdScopes = new Map<string, Scope.Closeable>();
312
+
313
+ const context = yield* Effect.context<RuntimeServices>();
314
+ const instances = new Map<
315
+ string,
316
+ {
317
+ hash: number;
318
+ fiber: Fiber.Fiber<
319
+ Worker["Attributes"],
320
+ Bundle.BundleError | WorkerValidationError | RuntimeError
321
+ >;
322
+ scope: Scope.Closeable;
323
+ }
324
+ >();
325
+
326
+ const runInstance = Effect.fn(function* (options: {
327
+ id: string;
328
+ props: WorkerProps;
329
+ bindings: ResourceBinding<Worker["Binding"]>[];
330
+ instanceId: string;
331
+ }) {
332
+ const { id, props, bindings } = options;
333
+ const config = yield* buildConfig(options);
334
+ const url = yield* localProxy.registerWorker(id);
335
+ if (props.vite) {
336
+ const devServer = yield* Vite.viteDev(
337
+ props.vite.rootDir,
338
+ props.env ?? {},
339
+ {
340
+ compatibilityDate: config.compatibility.date,
341
+ compatibilityFlags: config.compatibility.flags,
342
+ worker: {
343
+ name: config.name,
344
+ bindings: config.workerBindings,
345
+ durableObjectNamespaces: toRuntimeDurableObjectNamespaces(
346
+ config.durableObjectNamespaces,
347
+ ),
348
+ hyperdrives: config.hyperdrives,
349
+ assets: toRuntimeAssets(config.assets),
350
+ },
351
+ context,
352
+ },
353
+ );
354
+ const localAddress = devServer.resolvedUrls!.local[0].slice(0, -1);
355
+ yield* localProxy.setLocalAddress(id, localAddress);
356
+ } else {
357
+ yield* runServer(config);
358
+ }
359
+ return {
360
+ workerId: config.name,
361
+ workerName: config.name,
362
+ logpush: undefined,
363
+ url,
364
+ tags: [],
365
+ durableObjectNamespaces: config.durableObjectNamespaces,
366
+ domains: [],
367
+ crons: Array.from(
368
+ new Set([...getCronBindings(bindings), ...(props.crons ?? [])]),
369
+ ),
370
+ accountId,
371
+ } satisfies Worker["Attributes"];
372
+ });
373
+
374
+ return {
375
+ diff: Effect.fn(function* ({ id, news, newBindings, instanceId }) {
376
+ const options = {
28
377
  id,
29
378
  props: news,
30
- bindings,
31
- stack,
379
+ bindings: newBindings,
32
380
  instanceId,
33
- }),
34
- delete: ({ id }) => sidecar.delete(id),
381
+ };
382
+ const hash = Hash.structure(options);
383
+ return {
384
+ action:
385
+ instances.get(options.id)?.hash === hash ? "noop" : "update",
386
+ };
387
+ }),
388
+ reconcile: Effect.fn(function* ({ id, news, bindings, instanceId }) {
389
+ const options = { id, props: news, bindings, instanceId };
390
+ const hash = Hash.structure(options);
391
+ const existing = instances.get(options.id);
392
+ if (existing) {
393
+ if (existing.hash === hash) {
394
+ yield* Effect.log(
395
+ `[${options.id}] No changes, using existing instance`,
396
+ );
397
+ return yield* Fiber.join(existing.fiber);
398
+ }
399
+ yield* Effect.log(
400
+ `[${options.id}] Changes detected, interrupting existing instance`,
401
+ );
402
+ yield* Fiber.interrupt(existing.fiber);
403
+ yield* Scope.close(existing.scope, Exit.void);
404
+ instances.delete(options.id);
405
+ }
406
+ const scope = yield* Scope.fork(rootScope);
407
+ const fiber = yield* runInstance(options).pipe(
408
+ Effect.forkDetach,
409
+ Scope.provide(scope),
410
+ );
411
+ instances.set(options.id, { hash, fiber, scope });
412
+ return yield* Fiber.join(fiber).pipe(
413
+ Effect.onExit((exit) =>
414
+ Effect.sync(() => {
415
+ if (exit._tag === "Failure") {
416
+ instances.delete(options.id);
417
+ }
418
+ }),
419
+ ),
420
+ );
421
+ }),
422
+ delete: Effect.fn(function* ({ id }) {
423
+ const existing = instances.get(id);
424
+ if (existing) {
425
+ yield* Fiber.interrupt(existing.fiber);
426
+ yield* Scope.close(existing.scope, Exit.void);
427
+ instances.delete(id);
428
+ }
429
+ }),
35
430
  };
36
431
  }),
37
432
  );
433
+
434
+ const toRuntimeBinding = Effect.fnUntraced(function* (b: WorkerBinding) {
435
+ const unsupported = () =>
436
+ new WorkerValidationError({
437
+ message: `${b.type} bindings are not supported in local mode`,
438
+ value: b,
439
+ });
440
+ switch (b.type) {
441
+ case "ai":
442
+ return Ai.remote(b.name);
443
+ case "analytics_engine":
444
+ return yield* unsupported();
445
+ case "artifacts":
446
+ return Artifacts.binding(b.name, b.namespace);
447
+ case "assets":
448
+ return Assets.binding(b.name);
449
+ case "browser":
450
+ return Browser.binding(b.name);
451
+ case "d1":
452
+ return D1.remote(b.name, b.id);
453
+ case "data_blob":
454
+ return Data.binding(b.name, Buffer.from(b.part));
455
+ case "dispatch_namespace":
456
+ return yield* unsupported();
457
+ case "durable_object_namespace":
458
+ return DurableObjectNamespace.local({
459
+ name: b.name,
460
+ className: b.className!,
461
+ scriptName: b.scriptName,
462
+ });
463
+ case "hyperdrive":
464
+ return Hyperdrive.binding(b.name, b.id);
465
+ case "images":
466
+ return Images.remote(b.name);
467
+ case "inherit":
468
+ return yield* unsupported();
469
+ case "json":
470
+ return Json.binding(b.name, b.json);
471
+ case "kv_namespace":
472
+ return KvNamespace.remote(b.name, b.namespaceId);
473
+ case "mtls_certificate":
474
+ return yield* unsupported();
475
+ case "pipelines":
476
+ return yield* unsupported();
477
+ case "plain_text":
478
+ return Text.binding(b.name, b.text);
479
+ case "queue":
480
+ return yield* unsupported();
481
+ case "r2_bucket":
482
+ return R2Bucket.remote(b.name, b.bucketName, b.jurisdiction);
483
+ case "ratelimit":
484
+ return yield* unsupported();
485
+ case "secret_key":
486
+ return yield* unsupported();
487
+ case "secret_text":
488
+ return Text.binding(b.name, b.text);
489
+ case "secrets_store_secret":
490
+ return yield* unsupported();
491
+ case "send_email":
492
+ return yield* unsupported();
493
+ case "service":
494
+ return Service.local({ name: b.name, scriptName: b.service });
495
+ case "text_blob":
496
+ return Data.binding(b.name, Buffer.from(b.part));
497
+ case "vectorize":
498
+ return yield* unsupported();
499
+ case "version_metadata":
500
+ return VersionMetadata.binding(b.name);
501
+ case "wasm_module":
502
+ return WasmModule.binding(b.name, Buffer.from(b.part));
503
+ case "worker_loader":
504
+ return WorkerLoader.binding(b.name);
505
+ case "workflow":
506
+ return yield* unsupported();
507
+ default:
508
+ return yield* unsupported();
509
+ }
510
+ });
511
+
512
+ const toRuntimeAssets = (
513
+ assets: WorkerAssetsConfig | undefined,
514
+ ): RuntimeAssets | undefined => {
515
+ if (!assets) return undefined;
516
+ if (typeof assets === "string") {
517
+ return {
518
+ directory: assets,
519
+ };
520
+ }
521
+ return {
522
+ directory: "directory" in assets ? assets.directory : assets.path,
523
+ headers: assets.config?.headers,
524
+ redirects: assets.config?.redirects,
525
+ htmlHandling: assets.config?.htmlHandling,
526
+ notFoundHandling: assets.config?.notFoundHandling,
527
+ runWorkerFirst: assets.config?.runWorkerFirst,
528
+ serveDirectly: assets.config?.serveDirectly,
529
+ };
530
+ };
531
+
532
+ const toRuntimeDurableObjectNamespaces = (
533
+ namespaces: Record<string, string>,
534
+ ): RuntimeDurableObjectNamespace[] => {
535
+ return Object.entries(namespaces).map(([className, namespaceId]) => ({
536
+ className,
537
+ uniqueKey: namespaceId,
538
+ sql: true,
539
+ }));
540
+ };
541
+
542
+ const moduleTypeFromExtension = (ext: string): Module["type"] | "SourceMap" => {
543
+ switch (ext) {
544
+ case ".wasm":
545
+ return "Wasm";
546
+ case ".txt":
547
+ case ".html":
548
+ case ".sql":
549
+ case ".custom":
550
+ return "Text";
551
+ case ".bin":
552
+ return "Data";
553
+ case ".mjs":
554
+ case ".js":
555
+ return "ESModule";
556
+ case ".cjs":
557
+ return "CommonJsModule";
558
+ case ".map":
559
+ return "SourceMap";
560
+ default:
561
+ return "Text";
562
+ }
563
+ };
@@ -22,7 +22,6 @@ import { Resource, type ResourceBinding } from "../../Resource.ts";
22
22
  import { Stack } from "../../Stack.ts";
23
23
  import { CloudflareEnvironment } from "../CloudflareEnvironment.ts";
24
24
  import type { HyperdriveDevOrigin } from "../Hyperdrive/Hyperdrive.ts";
25
- import { SidecarLive } from "../Local/Sidecar.ts";
26
25
  import { CloudflareLogs } from "../Logs.ts";
27
26
  import type { Providers } from "../Providers.ts";
28
27
  import {
@@ -34,7 +33,10 @@ import {
34
33
  } from "./Assets.ts";
35
34
  import { getCompatibility } from "./Compatibility.ts";
36
35
  import { isDurableObjectExport } from "./DurableObjectNamespace.ts";
37
- import { LocalWorkerProvider } from "./LocalWorkerProvider.ts";
36
+ import {
37
+ LocalWorkerProvider,
38
+ localRuntimeServices,
39
+ } from "./LocalWorkerProvider.ts";
38
40
  import { Request } from "./Request.ts";
39
41
  import * as Vite from "./Vite.ts";
40
42
  import {
@@ -243,24 +245,11 @@ export interface WorkerProps<
243
245
  * already exist in the account.
244
246
  */
245
247
  domain?: string | string[];
246
- build?: {
247
- /**
248
- * Whether to generate a metafile for the worker bundle.
249
- * @default false
250
- */
251
- metafile?: boolean;
252
- /**
253
- * Configures the {@link Bundle.purePlugin} which annotates top-level
254
- * call/new expressions in matching packages with `/*#__PURE__*\/`
255
- * so rolldown can tree-shake them.
256
- *
257
- * - `undefined` (default): plugin is enabled with default packages
258
- * (`effect`, `@effect/*`).
259
- * - `PurePluginOptions`: plugin is enabled with the provided options.
260
- * - `false`: plugin is disabled.
261
- */
262
- pure?: Bundle.BundleExtraOptions["pure"];
263
- };
248
+ /**
249
+ * Extra bundler options applied on top of the standard rolldown input/output
250
+ * options used to build this Worker. See {@link Bundle.BundleExtraOptions}.
251
+ */
252
+ build?: Bundle.BundleExtraOptions;
264
253
  }
265
254
 
266
255
  export type Worker<Bindings extends WorkerBindings = any> = Resource<
@@ -724,7 +713,7 @@ const selectLayer = <
724
713
  export const WorkerProvider = () =>
725
714
  selectLayer({
726
715
  live: LiveWorkerProvider,
727
- dev: () => Layer.provide(LocalWorkerProvider(), SidecarLive),
716
+ dev: () => Layer.provide(LocalWorkerProvider(), localRuntimeServices()),
728
717
  });
729
718
 
730
719
  export const LiveWorkerProvider = () =>
@@ -1121,7 +1110,7 @@ export const LiveWorkerProvider = () =>
1121
1110
  exports: (props.exports ?? {}) as any,
1122
1111
  },
1123
1112
  stack: { name: stack.name, stage: stack.stage },
1124
- userOptions: props.build,
1113
+ extraOptions: props.build,
1125
1114
  })
1126
1115
  .pipe(Artifacts.cached("build"));
1127
1116
 
@@ -1,7 +1,6 @@
1
1
  import type * as cf from "@cloudflare/workers-types";
2
- import { NodeServices } from "@effect/platform-node";
2
+ import * as NodeServices from "@effect/platform-node/NodeServices";
3
3
  import type { DurableObject, WorkerEntrypoint } from "cloudflare:workers";
4
- import { Stream } from "effect";
5
4
  import * as Cause from "effect/Cause";
6
5
  import * as ConfigProvider from "effect/ConfigProvider";
7
6
  import * as Context from "effect/Context";
@@ -12,7 +11,8 @@ import * as Layer from "effect/Layer";
12
11
  import * as Logger from "effect/Logger";
13
12
  import { MinimumLogLevel } from "effect/References";
14
13
  import * as Scope from "effect/Scope";
15
- import { FetchHttpClient } from "effect/unstable/http";
14
+ import * as Stream from "effect/Stream";
15
+ import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
16
16
  import { ExecutionContext } from "../../ExecutionContext.ts";
17
17
  import { makeEntrypointLayer } from "../../Runtime.ts";
18
18
  import { Self } from "../../Self.ts";
@@ -138,7 +138,6 @@ export const makeWorkerBridge = (
138
138
  ),
139
139
  );
140
140
  }
141
- console.log("dispatcher", dispatcher.toString());
142
141
  return Effect.succeed([
143
142
  dispatcher(...args) as Effect.Effect<any>,
144
143
  Context.empty(),
@@ -272,7 +271,6 @@ export const makeRpcProxy = (
272
271
  ),
273
272
  );
274
273
  }
275
- console.log("dispatcher", dispatcher.toString());
276
274
  return dispatcher(...args) as Effect.Effect<any>;
277
275
  }),
278
276
  processEvent,