alchemy 2.0.0-beta.35 → 2.0.0-beta.37
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 +176 -26
- package/bin/exec.js.map +1 -1
- package/lib/AWS/Lambda/Function.d.ts.map +1 -1
- package/lib/AWS/Lambda/Function.js +22 -2
- package/lib/AWS/Lambda/Function.js.map +1 -1
- package/lib/Apply.d.ts.map +1 -1
- package/lib/Apply.js +6 -1
- package/lib/Apply.js.map +1 -1
- package/lib/Cli/main.d.ts.map +1 -1
- package/lib/Cli/main.js +2 -0
- package/lib/Cli/main.js.map +1 -1
- package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.d.ts +54 -0
- package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.d.ts.map +1 -0
- package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.js +17 -0
- package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.js.map +1 -0
- package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDatasetBinding.d.ts +37 -0
- package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDatasetBinding.d.ts.map +1 -0
- package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDatasetBinding.js +45 -0
- package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDatasetBinding.js.map +1 -0
- package/lib/Cloudflare/AnalyticsEngine/index.d.ts +3 -0
- package/lib/Cloudflare/AnalyticsEngine/index.d.ts.map +1 -0
- package/lib/Cloudflare/AnalyticsEngine/index.js +3 -0
- package/lib/Cloudflare/AnalyticsEngine/index.js.map +1 -0
- package/lib/Cloudflare/D1/D1Connection.d.ts +47 -3
- package/lib/Cloudflare/D1/D1Connection.d.ts.map +1 -1
- package/lib/Cloudflare/D1/D1Connection.js +62 -5
- package/lib/Cloudflare/D1/D1Connection.js.map +1 -1
- package/lib/Cloudflare/Images/Images.d.ts +103 -0
- package/lib/Cloudflare/Images/Images.d.ts.map +1 -0
- package/lib/Cloudflare/Images/Images.js +82 -0
- package/lib/Cloudflare/Images/Images.js.map +1 -0
- package/lib/Cloudflare/Images/ImagesBinding.d.ts +73 -0
- package/lib/Cloudflare/Images/ImagesBinding.d.ts.map +1 -0
- package/lib/Cloudflare/Images/ImagesBinding.js +83 -0
- package/lib/Cloudflare/Images/ImagesBinding.js.map +1 -0
- package/lib/Cloudflare/Images/index.d.ts +3 -0
- package/lib/Cloudflare/Images/index.d.ts.map +1 -0
- package/lib/Cloudflare/Images/index.js +3 -0
- package/lib/Cloudflare/Images/index.js.map +1 -0
- package/lib/Cloudflare/Local/SidecarHandlers.js +9 -1
- package/lib/Cloudflare/Local/SidecarHandlers.js.map +1 -1
- package/lib/Cloudflare/Providers.d.ts.map +1 -1
- package/lib/Cloudflare/Providers.js +6 -1
- package/lib/Cloudflare/Providers.js.map +1 -1
- package/lib/Cloudflare/R2/R2Bucket.d.ts +135 -0
- package/lib/Cloudflare/R2/R2Bucket.d.ts.map +1 -1
- package/lib/Cloudflare/R2/R2Bucket.js +146 -7
- package/lib/Cloudflare/R2/R2Bucket.js.map +1 -1
- package/lib/Cloudflare/StateStore/Api.d.ts +6 -1
- package/lib/Cloudflare/StateStore/Api.d.ts.map +1 -1
- package/lib/Cloudflare/StateStore/Api.js +23 -1
- package/lib/Cloudflare/StateStore/Api.js.map +1 -1
- package/lib/Cloudflare/StateStore/Store.d.ts +16 -1
- package/lib/Cloudflare/StateStore/Store.d.ts.map +1 -1
- package/lib/Cloudflare/StateStore/Store.js +22 -5
- package/lib/Cloudflare/StateStore/Store.js.map +1 -1
- package/lib/Cloudflare/Workers/CronEventSource.d.ts +35 -0
- package/lib/Cloudflare/Workers/CronEventSource.d.ts.map +1 -0
- package/lib/Cloudflare/Workers/CronEventSource.js +55 -0
- package/lib/Cloudflare/Workers/CronEventSource.js.map +1 -0
- package/lib/Cloudflare/Workers/InferEnv.d.ts +37 -2
- package/lib/Cloudflare/Workers/InferEnv.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.js +2 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.js.map +1 -1
- package/lib/Cloudflare/Workers/Rpc.d.ts +76 -3
- package/lib/Cloudflare/Workers/Rpc.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Rpc.js +197 -13
- package/lib/Cloudflare/Workers/Rpc.js.map +1 -1
- package/lib/Cloudflare/Workers/Worker.d.ts +26 -6
- package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Worker.js +183 -41
- package/lib/Cloudflare/Workers/Worker.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBundle.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBundle.js +15 -5
- package/lib/Cloudflare/Workers/WorkerBundle.js.map +1 -1
- package/lib/Cloudflare/Workers/Workflow.d.ts +50 -47
- package/lib/Cloudflare/Workers/Workflow.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Workflow.js +41 -35
- package/lib/Cloudflare/Workers/Workflow.js.map +1 -1
- package/lib/Cloudflare/Workers/index.d.ts +1 -0
- package/lib/Cloudflare/Workers/index.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/index.js +1 -0
- package/lib/Cloudflare/Workers/index.js.map +1 -1
- package/lib/Cloudflare/index.d.ts +2 -0
- package/lib/Cloudflare/index.d.ts.map +1 -1
- package/lib/Cloudflare/index.js +2 -0
- package/lib/Cloudflare/index.js.map +1 -1
- package/lib/Deploy.d.ts +4 -1
- package/lib/Deploy.d.ts.map +1 -1
- package/lib/Deploy.js +2 -2
- package/lib/Deploy.js.map +1 -1
- package/lib/Destroy.d.ts +4 -1
- package/lib/Destroy.d.ts.map +1 -1
- package/lib/Destroy.js +2 -2
- package/lib/Destroy.js.map +1 -1
- package/lib/Output.d.ts +45 -5
- package/lib/Output.d.ts.map +1 -1
- package/lib/Output.js +100 -4
- package/lib/Output.js.map +1 -1
- package/lib/Plan.d.ts.map +1 -1
- package/lib/Plan.js +93 -12
- package/lib/Plan.js.map +1 -1
- package/lib/Platform.d.ts.map +1 -1
- package/lib/Platform.js +16 -3
- package/lib/Platform.js.map +1 -1
- package/lib/Ref.d.ts +4 -1
- package/lib/Ref.d.ts.map +1 -1
- package/lib/Ref.js +1 -1
- package/lib/Ref.js.map +1 -1
- package/lib/Resource.d.ts +8 -0
- package/lib/Resource.d.ts.map +1 -1
- package/lib/Resource.js +10 -0
- package/lib/Resource.js.map +1 -1
- package/lib/RuntimeContext.d.ts +3 -1
- package/lib/RuntimeContext.d.ts.map +1 -1
- package/lib/RuntimeContext.js.map +1 -1
- package/lib/Secret.d.ts +50 -0
- package/lib/Secret.d.ts.map +1 -0
- package/lib/Secret.js +23 -0
- package/lib/Secret.js.map +1 -0
- package/lib/Serverless/Function.d.ts +9 -1
- package/lib/Serverless/Function.d.ts.map +1 -1
- package/lib/Stack.d.ts +14 -5
- package/lib/Stack.d.ts.map +1 -1
- package/lib/Stack.js +31 -19
- package/lib/Stack.js.map +1 -1
- package/lib/State/HttpStateApi.d.ts +15 -1
- package/lib/State/HttpStateApi.d.ts.map +1 -1
- package/lib/State/HttpStateApi.js +11 -0
- package/lib/State/HttpStateApi.js.map +1 -1
- package/lib/State/HttpStateStore.d.ts.map +1 -1
- package/lib/State/HttpStateStore.js +11 -0
- package/lib/State/HttpStateStore.js.map +1 -1
- package/lib/State/InMemoryState.d.ts +2 -2
- package/lib/State/InMemoryState.d.ts.map +1 -1
- package/lib/State/InMemoryState.js +12 -6
- package/lib/State/InMemoryState.js.map +1 -1
- package/lib/State/LocalState.d.ts.map +1 -1
- package/lib/State/LocalState.js +11 -2
- package/lib/State/LocalState.js.map +1 -1
- package/lib/State/State.d.ts +21 -0
- package/lib/State/State.d.ts.map +1 -1
- package/lib/Test/Bun.d.ts.map +1 -1
- package/lib/Test/Bun.js +30 -7
- package/lib/Test/Bun.js.map +1 -1
- package/lib/Test/Core.d.ts +17 -2
- package/lib/Test/Core.d.ts.map +1 -1
- package/lib/Test/Core.js +29 -11
- package/lib/Test/Core.js.map +1 -1
- package/lib/Test/Vitest.d.ts.map +1 -1
- package/lib/Test/Vitest.js +26 -5
- package/lib/Test/Vitest.js.map +1 -1
- package/lib/Util/LocalhostDns.d.ts +23 -0
- package/lib/Util/LocalhostDns.d.ts.map +1 -0
- package/lib/Util/LocalhostDns.js +46 -0
- package/lib/Util/LocalhostDns.js.map +1 -0
- package/lib/Util/PlatformServices.d.ts.map +1 -1
- package/lib/Util/PlatformServices.js +2 -0
- package/lib/Util/PlatformServices.js.map +1 -1
- package/lib/Util/effect.d.ts +7 -0
- package/lib/Util/effect.d.ts.map +1 -1
- package/lib/Util/effect.js +6 -0
- package/lib/Util/effect.js.map +1 -1
- package/lib/Variable.d.ts +29 -0
- package/lib/Variable.d.ts.map +1 -0
- package/lib/Variable.js +9 -0
- package/lib/Variable.js.map +1 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -0
- package/lib/index.js.map +1 -1
- package/lib/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/src/AWS/Lambda/Function.ts +30 -2
- package/src/Apply.ts +8 -1
- package/src/Cli/main.ts +3 -0
- package/src/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.ts +82 -0
- package/src/Cloudflare/AnalyticsEngine/AnalyticsEngineDatasetBinding.ts +106 -0
- package/src/Cloudflare/AnalyticsEngine/index.ts +2 -0
- package/src/Cloudflare/D1/D1Connection.ts +95 -12
- package/src/Cloudflare/Images/Images.ts +121 -0
- package/src/Cloudflare/Images/ImagesBinding.ts +192 -0
- package/src/Cloudflare/Images/index.ts +2 -0
- package/src/Cloudflare/Local/SidecarHandlers.ts +9 -1
- package/src/Cloudflare/Providers.ts +8 -0
- package/src/Cloudflare/R2/R2Bucket.ts +271 -7
- package/src/Cloudflare/StateStore/Api.ts +34 -1
- package/src/Cloudflare/StateStore/Store.ts +40 -10
- package/src/Cloudflare/Workers/CronEventSource.ts +89 -0
- package/src/Cloudflare/Workers/InferEnv.ts +94 -16
- package/src/Cloudflare/Workers/LocalWorkerProvider.ts +9 -1
- package/src/Cloudflare/Workers/Rpc.ts +227 -18
- package/src/Cloudflare/Workers/Worker.ts +295 -62
- package/src/Cloudflare/Workers/WorkerBundle.ts +15 -5
- package/src/Cloudflare/Workers/Workflow.ts +74 -70
- package/src/Cloudflare/Workers/index.ts +1 -0
- package/src/Cloudflare/index.ts +2 -0
- package/src/Deploy.ts +5 -1
- package/src/Destroy.ts +5 -1
- package/src/Output.ts +121 -12
- package/src/Plan.ts +101 -12
- package/src/Platform.ts +19 -4
- package/src/Ref.ts +10 -5
- package/src/Resource.ts +22 -0
- package/src/RuntimeContext.ts +1 -0
- package/src/Secret.ts +87 -0
- package/src/Serverless/Function.ts +10 -1
- package/src/Stack.ts +52 -29
- package/src/State/HttpStateApi.ts +21 -0
- package/src/State/HttpStateStore.ts +21 -0
- package/src/State/InMemoryState.ts +29 -8
- package/src/State/LocalState.ts +28 -4
- package/src/State/State.ts +21 -0
- package/src/Test/Bun.ts +43 -7
- package/src/Test/Core.ts +29 -8
- package/src/Test/Vitest.ts +36 -4
- package/src/Util/LocalhostDns.ts +52 -0
- package/src/Util/PlatformServices.ts +2 -0
- package/src/Util/effect.ts +25 -0
- package/src/Variable.ts +44 -0
- package/src/index.ts +3 -0
|
@@ -50,12 +50,21 @@ export class WorkflowStep extends Context.Service<
|
|
|
50
50
|
* Execute a named, durable workflow step. The effect is run inside the
|
|
51
51
|
* Cloudflare step transaction so its result is automatically persisted
|
|
52
52
|
* and replayed on retries.
|
|
53
|
+
*
|
|
54
|
+
* Any services the inner effect requires (e.g. `WorkerEnvironment` from a
|
|
55
|
+
* binding like `kv.put` / `kv.get`) are threaded through automatically by
|
|
56
|
+
* capturing the surrounding workflow body's context and providing it to
|
|
57
|
+
* the inner effect before it runs inside `step.do`.
|
|
53
58
|
*/
|
|
54
|
-
export const task = <T>(
|
|
59
|
+
export const task = <T, R = never>(
|
|
55
60
|
name: string,
|
|
56
|
-
effect: Effect.Effect<T>,
|
|
57
|
-
): Effect.Effect<T, never, WorkflowStep> =>
|
|
58
|
-
|
|
61
|
+
effect: Effect.Effect<T, never, R>,
|
|
62
|
+
): Effect.Effect<T, never, WorkflowStep | R> =>
|
|
63
|
+
Effect.gen(function* () {
|
|
64
|
+
const step = yield* WorkflowStep;
|
|
65
|
+
const context = yield* Effect.context<R>();
|
|
66
|
+
return yield* step.do(name, effect.pipe(Effect.provide(context)));
|
|
67
|
+
});
|
|
59
68
|
|
|
60
69
|
/**
|
|
61
70
|
* Pause the workflow for the given duration.
|
|
@@ -103,20 +112,17 @@ export type WorkflowServices = WorkerServices | PlatformServices;
|
|
|
103
112
|
*/
|
|
104
113
|
export interface WorkflowExport {
|
|
105
114
|
readonly kind: "workflow";
|
|
106
|
-
readonly make: (
|
|
107
|
-
env: unknown,
|
|
108
|
-
) => Effect.Effect<Effect.Effect<unknown, never, WorkflowRunServices>>;
|
|
115
|
+
readonly make: (env: unknown) => Effect.Effect<WorkflowImpl<any, any>>;
|
|
109
116
|
}
|
|
110
117
|
|
|
111
118
|
/**
|
|
112
|
-
* A workflow
|
|
113
|
-
*
|
|
119
|
+
* A workflow implementation is a function from a typed `Input` payload to
|
|
120
|
+
* an Effect that produces the workflow's `Result`. The Effect requires
|
|
121
|
+
* `WorkflowRunServices` (event + step + env) to execute.
|
|
114
122
|
*/
|
|
115
|
-
export type
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
WorkflowRunServices
|
|
119
|
-
>;
|
|
123
|
+
export type WorkflowImpl<Input = unknown, Result = unknown> = (
|
|
124
|
+
input: Input,
|
|
125
|
+
) => Effect.Effect<Result, never, WorkflowRunServices>;
|
|
120
126
|
|
|
121
127
|
export const isWorkflowExport = (value: unknown): value is WorkflowExport =>
|
|
122
128
|
typeof value === "object" &&
|
|
@@ -141,24 +147,24 @@ export const isWorkflowBinding = (binding: {
|
|
|
141
147
|
* Handle returned to the caller at deploy/bind time. Allows starting
|
|
142
148
|
* workflow instances and checking their status from the Api layer.
|
|
143
149
|
*/
|
|
144
|
-
export interface WorkflowHandle<
|
|
150
|
+
export interface WorkflowHandle<Input = unknown, Result = unknown> {
|
|
145
151
|
Type: WorkflowTypeId;
|
|
146
152
|
name: string;
|
|
147
|
-
create(
|
|
148
|
-
get(instanceId: string): Effect.Effect<WorkflowInstance
|
|
153
|
+
create(input: Input): Effect.Effect<WorkflowInstance<Result>>;
|
|
154
|
+
get(instanceId: string): Effect.Effect<WorkflowInstance<Result>>;
|
|
149
155
|
}
|
|
150
156
|
|
|
151
|
-
export interface WorkflowInstance {
|
|
157
|
+
export interface WorkflowInstance<Result = unknown> {
|
|
152
158
|
id: string;
|
|
153
|
-
status(): Effect.Effect<WorkflowInstanceStatus
|
|
159
|
+
status(): Effect.Effect<WorkflowInstanceStatus<Result>>;
|
|
154
160
|
pause(): Effect.Effect<void>;
|
|
155
161
|
resume(): Effect.Effect<void>;
|
|
156
162
|
terminate(): Effect.Effect<void>;
|
|
157
163
|
}
|
|
158
164
|
|
|
159
|
-
export interface WorkflowInstanceStatus {
|
|
165
|
+
export interface WorkflowInstanceStatus<Result = unknown> {
|
|
160
166
|
status: string;
|
|
161
|
-
output?:
|
|
167
|
+
output?: Result;
|
|
162
168
|
error?: { name: string; message: string } | null;
|
|
163
169
|
}
|
|
164
170
|
|
|
@@ -168,22 +174,22 @@ export interface WorkflowClass extends Effect.Effect<
|
|
|
168
174
|
WorkflowHandle
|
|
169
175
|
> {
|
|
170
176
|
<_Self>(): {
|
|
171
|
-
<Result = unknown, InitReq = never>(
|
|
177
|
+
<Input = unknown, Result = unknown, InitReq = never>(
|
|
172
178
|
name: string,
|
|
173
|
-
impl: Effect.Effect<
|
|
179
|
+
impl: Effect.Effect<WorkflowImpl<Input, Result>, never, InitReq>,
|
|
174
180
|
): Effect.Effect<
|
|
175
|
-
WorkflowHandle,
|
|
181
|
+
WorkflowHandle<Input, Result>,
|
|
176
182
|
never,
|
|
177
183
|
Worker | Exclude<InitReq, WorkflowServices>
|
|
178
184
|
> & {
|
|
179
|
-
new (_: never):
|
|
185
|
+
new (_: never): WorkflowImpl<Input, Result>;
|
|
180
186
|
};
|
|
181
187
|
};
|
|
182
|
-
<Result = unknown, InitReq = never>(
|
|
188
|
+
<Input = unknown, Result = unknown, InitReq = never>(
|
|
183
189
|
name: string,
|
|
184
|
-
impl: Effect.Effect<
|
|
190
|
+
impl: Effect.Effect<WorkflowImpl<Input, Result>, never, InitReq>,
|
|
185
191
|
): Effect.Effect<
|
|
186
|
-
WorkflowHandle,
|
|
192
|
+
WorkflowHandle<Input, Result>,
|
|
187
193
|
never,
|
|
188
194
|
Worker | Exclude<InitReq, WorkflowServices>
|
|
189
195
|
>;
|
|
@@ -200,18 +206,18 @@ export class WorkflowScope extends Context.Service<
|
|
|
200
206
|
*
|
|
201
207
|
* A Workflow follows the same two-phase pattern as Workers and Durable
|
|
202
208
|
* Objects. The outer `Effect.gen` resolves shared dependencies. The inner
|
|
203
|
-
* `Effect.
|
|
204
|
-
* runs steps using `task`, `sleep`, and
|
|
209
|
+
* `Effect.fn` is the workflow body — a function from a typed `input`
|
|
210
|
+
* payload to an Effect that runs steps using `task`, `sleep`, and
|
|
211
|
+
* `sleepUntil`.
|
|
205
212
|
*
|
|
206
213
|
* ```typescript
|
|
207
214
|
* Effect.gen(function* () {
|
|
208
215
|
* // Phase 1: resolve dependencies
|
|
209
216
|
* const notifier = yield* NotificationService;
|
|
210
217
|
*
|
|
211
|
-
* return Effect.
|
|
218
|
+
* return Effect.fn(function* (input: { orderId: string }) {
|
|
212
219
|
* // Phase 2: workflow body (durable steps)
|
|
213
|
-
* const
|
|
214
|
-
* const result = yield* Cloudflare.task("process", doWork(event.payload));
|
|
220
|
+
* const result = yield* Cloudflare.task("process", doWork(input.orderId));
|
|
215
221
|
* yield* Cloudflare.sleep("cooldown", "10 seconds");
|
|
216
222
|
* return result;
|
|
217
223
|
* });
|
|
@@ -226,9 +232,8 @@ export class WorkflowScope extends Context.Service<
|
|
|
226
232
|
* export default class MyWorkflow extends Cloudflare.Workflow<MyWorkflow>()(
|
|
227
233
|
* "MyWorkflow",
|
|
228
234
|
* Effect.gen(function* () {
|
|
229
|
-
* return Effect.
|
|
230
|
-
*
|
|
231
|
-
* return { received: event.payload };
|
|
235
|
+
* return Effect.fn(function* (input: { name: string }) {
|
|
236
|
+
* return { received: input.name };
|
|
232
237
|
* });
|
|
233
238
|
* }),
|
|
234
239
|
* ) {}
|
|
@@ -248,33 +253,31 @@ export class WorkflowScope extends Context.Service<
|
|
|
248
253
|
* yield* Cloudflare.sleep("cooldown", "30 seconds");
|
|
249
254
|
* ```
|
|
250
255
|
*
|
|
251
|
-
* @example Accessing env bindings inside a
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
+
* @example Accessing env bindings inside a task
|
|
257
|
+
* Bind a resource (e.g. `KVNamespace`, `R2Bucket`) in the workflow's
|
|
258
|
+
* outer init phase to get a typed Effect-native client, then use it
|
|
259
|
+
* directly inside `task`. `task` threads the binding's service
|
|
260
|
+
* requirement (`WorkerEnvironment`) through automatically so the inner
|
|
261
|
+
* Effect needs no extra plumbing.
|
|
256
262
|
*
|
|
257
263
|
* ```typescript
|
|
258
264
|
* Effect.gen(function* () {
|
|
259
|
-
* const
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
* roomId
|
|
263
|
-
* message: string;
|
|
264
|
-
* };
|
|
265
|
+
* const kv = yield* Cloudflare.KVNamespace.bind(KV);
|
|
266
|
+
*
|
|
267
|
+
* return Effect.fn(function* (input: { roomId: string; message: string }) {
|
|
268
|
+
* const { roomId, message } = input;
|
|
265
269
|
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
* return
|
|
272
|
-
* },
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
* );
|
|
277
|
-
* return stored;
|
|
270
|
+
* const stored = yield* Cloudflare.task(
|
|
271
|
+
* "kv-roundtrip",
|
|
272
|
+
* Effect.gen(function* () {
|
|
273
|
+
* const key = `workflow:${roomId}`;
|
|
274
|
+
* yield* kv.put(key, message);
|
|
275
|
+
* return yield* kv.get(key);
|
|
276
|
+
* }).pipe(Effect.orDie),
|
|
277
|
+
* );
|
|
278
|
+
*
|
|
279
|
+
* return stored;
|
|
280
|
+
* });
|
|
278
281
|
* });
|
|
279
282
|
* ```
|
|
280
283
|
*
|
|
@@ -357,7 +360,7 @@ export class WorkflowScope extends Context.Service<
|
|
|
357
360
|
* ```
|
|
358
361
|
*/
|
|
359
362
|
export const Workflow: WorkflowClass = taggedFunction(WorkflowScope, ((
|
|
360
|
-
...args: [] | [name: string, impl: Effect.Effect<
|
|
363
|
+
...args: [] | [name: string, impl: Effect.Effect<WorkflowImpl<any, any>>]
|
|
361
364
|
) =>
|
|
362
365
|
args.length === 0
|
|
363
366
|
? Workflow
|
|
@@ -404,11 +407,11 @@ export const Workflow: WorkflowClass = taggedFunction(WorkflowScope, ((
|
|
|
404
407
|
}),
|
|
405
408
|
);
|
|
406
409
|
|
|
407
|
-
const self: WorkflowHandle = {
|
|
410
|
+
const self: WorkflowHandle<any, any> = {
|
|
408
411
|
Type: WorkflowTypeId,
|
|
409
412
|
name,
|
|
410
|
-
create: (
|
|
411
|
-
Effect.tryPromise(() => binding.create({ params })).pipe(
|
|
413
|
+
create: (input: unknown) =>
|
|
414
|
+
Effect.tryPromise(() => binding.create({ params: input })).pipe(
|
|
412
415
|
Effect.map(wrapInstance),
|
|
413
416
|
Effect.orDie,
|
|
414
417
|
),
|
|
@@ -419,21 +422,22 @@ export const Workflow: WorkflowClass = taggedFunction(WorkflowScope, ((
|
|
|
419
422
|
),
|
|
420
423
|
};
|
|
421
424
|
|
|
422
|
-
const
|
|
425
|
+
const fn = yield* impl.pipe(
|
|
423
426
|
Effect.provideService(WorkflowScope, self as any),
|
|
424
427
|
);
|
|
425
428
|
|
|
426
429
|
yield* worker.export(name, {
|
|
427
430
|
kind: "workflow",
|
|
428
431
|
make: (env: unknown) =>
|
|
429
|
-
Effect.succeed(
|
|
430
|
-
|
|
432
|
+
Effect.succeed(((input: unknown) =>
|
|
433
|
+
fn(input).pipe(
|
|
431
434
|
Effect.provideService(
|
|
432
435
|
WorkerEnvironment,
|
|
433
436
|
env as Record<string, any>,
|
|
434
437
|
),
|
|
435
|
-
),
|
|
436
|
-
|
|
438
|
+
)) as WorkflowImpl<any, any>).pipe(
|
|
439
|
+
Effect.provideContext(services),
|
|
440
|
+
),
|
|
437
441
|
} satisfies WorkflowExport);
|
|
438
442
|
|
|
439
443
|
return self;
|
|
@@ -520,13 +524,13 @@ export const WorkflowProvider = () =>
|
|
|
520
524
|
// Helpers
|
|
521
525
|
// ---------------------------------------------------------------------------
|
|
522
526
|
|
|
523
|
-
const wrapInstance = (raw: any): WorkflowInstance => ({
|
|
527
|
+
const wrapInstance = <Result>(raw: any): WorkflowInstance<Result> => ({
|
|
524
528
|
id: raw.id,
|
|
525
529
|
status: () =>
|
|
526
530
|
Effect.tryPromise(() => raw.status()).pipe(
|
|
527
531
|
Effect.map((s: any) => ({
|
|
528
532
|
status: s.status as string,
|
|
529
|
-
output: s.output,
|
|
533
|
+
output: s.output as Result,
|
|
530
534
|
error: s.error,
|
|
531
535
|
})),
|
|
532
536
|
Effect.orDie,
|
package/src/Cloudflare/index.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
export * from "./ApiToken/index.ts";
|
|
2
2
|
export * from "./Artifacts/index.ts";
|
|
3
3
|
export * from "./AiGateway/index.ts";
|
|
4
|
+
export * from "./AnalyticsEngine/index.ts";
|
|
4
5
|
export * from "./CloudflareEnvironment.ts";
|
|
5
6
|
export * from "./Container/index.ts";
|
|
6
7
|
export * from "./D1/index.ts";
|
|
7
8
|
export * from "./EdgeSession.ts";
|
|
8
9
|
export * from "./Hyperdrive/index.ts";
|
|
10
|
+
export * from "./Images/index.ts";
|
|
9
11
|
export * from "./KV/index.ts";
|
|
10
12
|
export * from "./Providers.ts";
|
|
11
13
|
export * from "./Queue/index.ts";
|
package/src/Deploy.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
|
+
import type * as Scope from "effect/Scope";
|
|
2
3
|
import { AlchemyContext } from "./AlchemyContext.ts";
|
|
3
4
|
import * as Apply from "./Apply.ts";
|
|
4
5
|
import type { Input } from "./Input.ts";
|
|
@@ -10,10 +11,13 @@ export const deploy = <A>({
|
|
|
10
11
|
stack,
|
|
11
12
|
stage,
|
|
12
13
|
dev,
|
|
14
|
+
scope,
|
|
13
15
|
}: {
|
|
14
16
|
stack: StackEffect<CompiledStack<A>, Stage | AlchemyContext>;
|
|
15
17
|
stage: string;
|
|
16
18
|
dev?: boolean;
|
|
19
|
+
/** See {@link evalStack} — when set, scoped resources outlive `deploy`. */
|
|
20
|
+
scope?: Scope.Scope;
|
|
17
21
|
}) =>
|
|
18
22
|
evalStack(
|
|
19
23
|
stack,
|
|
@@ -23,5 +27,5 @@ export const deploy = <A>({
|
|
|
23
27
|
const output = yield* Apply.apply(plan);
|
|
24
28
|
return output as Input.Resolve<A>;
|
|
25
29
|
}),
|
|
26
|
-
{ stage, dev },
|
|
30
|
+
{ stage, dev, scope },
|
|
27
31
|
);
|
package/src/Destroy.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
+
import type * as Scope from "effect/Scope";
|
|
2
3
|
import type { AlchemyContext } from "./AlchemyContext.ts";
|
|
3
4
|
import * as Apply from "./Apply.ts";
|
|
4
5
|
import * as Plan from "./Plan.ts";
|
|
@@ -10,10 +11,13 @@ export const destroy = ({
|
|
|
10
11
|
stack,
|
|
11
12
|
stage,
|
|
12
13
|
dev,
|
|
14
|
+
scope,
|
|
13
15
|
}: {
|
|
14
16
|
stack: StackEffect<CompiledStack, Stage | AlchemyContext>;
|
|
15
17
|
stage: string;
|
|
16
18
|
dev?: boolean;
|
|
19
|
+
/** See {@link evalStack} — when set, scoped resources outlive `destroy`. */
|
|
20
|
+
scope?: Scope.Scope;
|
|
17
21
|
}) =>
|
|
18
22
|
evalStack(
|
|
19
23
|
stack,
|
|
@@ -26,5 +30,5 @@ export const destroy = ({
|
|
|
26
30
|
bindings: {},
|
|
27
31
|
output: {},
|
|
28
32
|
}).pipe(Effect.flatMap(Apply.apply)),
|
|
29
|
-
{ stage, dev },
|
|
33
|
+
{ stage, dev, scope },
|
|
30
34
|
);
|
package/src/Output.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { pipe } from "effect/Function";
|
|
|
5
5
|
import type { Pipeable } from "effect/Pipeable";
|
|
6
6
|
import * as Redacted from "effect/Redacted";
|
|
7
7
|
import { SingleShotGen } from "effect/Utils";
|
|
8
|
-
import { getRefMetadata, isRef,
|
|
8
|
+
import { getRefMetadata, isRef, type Ref } from "./Ref.ts";
|
|
9
9
|
import { isResource, type Resource, type ResourceLike } from "./Resource.ts";
|
|
10
10
|
import { RuntimeContext } from "./RuntimeContext.ts";
|
|
11
11
|
import { Stack } from "./Stack.ts";
|
|
@@ -82,9 +82,11 @@ export type Expr<A = any, Req = any> =
|
|
|
82
82
|
| ApplyExpr<any, A, Req>
|
|
83
83
|
| EffectExpr<any, A, Req>
|
|
84
84
|
| LiteralExpr<A>
|
|
85
|
+
| NamedExpr<A, Req>
|
|
85
86
|
| PropExpr<A, keyof A, Req>
|
|
86
87
|
| ResourceExpr<A, Req>
|
|
87
|
-
| RefExpr<A
|
|
88
|
+
| RefExpr<A>
|
|
89
|
+
| StackRefExpr<A>;
|
|
88
90
|
|
|
89
91
|
export abstract class BaseExpr<A = any, Req = any> implements Output<A, Req> {
|
|
90
92
|
declare readonly kind: any;
|
|
@@ -255,6 +257,36 @@ export class EffectExpr<A, B, Req = never, Req2 = never> extends BaseExpr<
|
|
|
255
257
|
}
|
|
256
258
|
}
|
|
257
259
|
|
|
260
|
+
export const isNamedExpr = <A = any, Req = any>(
|
|
261
|
+
node: any,
|
|
262
|
+
): node is NamedExpr<A, Req> => node?.kind === "NamedExpr";
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Wraps another `Expr` and overrides its `toString()` / inspect output.
|
|
266
|
+
*
|
|
267
|
+
* `BaseExpr.asEffect()` derives the binding id from `this.toString()`, so
|
|
268
|
+
* wrapping an expression in `NamedExpr` makes that derived id stable and
|
|
269
|
+
* caller-controlled (e.g. an env var name like `"API_KEY"`).
|
|
270
|
+
*/
|
|
271
|
+
export class NamedExpr<A, Req = never> extends BaseExpr<A, Req> {
|
|
272
|
+
readonly kind = "NamedExpr";
|
|
273
|
+
constructor(
|
|
274
|
+
public readonly expr: Expr<A, Req>,
|
|
275
|
+
public readonly bindingName: string,
|
|
276
|
+
) {
|
|
277
|
+
super();
|
|
278
|
+
return proxy(this);
|
|
279
|
+
}
|
|
280
|
+
[inspect](): string {
|
|
281
|
+
return this.bindingName;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export const named = <A, Req>(
|
|
286
|
+
expr: Output<A, Req>,
|
|
287
|
+
name: string,
|
|
288
|
+
): Output<A, Req> => new NamedExpr(expr as Expr<A, Req>, name) as any;
|
|
289
|
+
|
|
258
290
|
export const all = <Outs extends (Output | Expr)[]>(...outs: Outs) =>
|
|
259
291
|
new AllExpr(outs as any) as unknown as All<Outs>;
|
|
260
292
|
|
|
@@ -309,6 +341,48 @@ export class RefExpr<A> extends BaseExpr<A, never> {
|
|
|
309
341
|
}
|
|
310
342
|
}
|
|
311
343
|
|
|
344
|
+
export const isStackRefExpr = <A = any>(node: any): node is StackRefExpr<A> =>
|
|
345
|
+
node?.kind === "StackRefExpr";
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* A reference to the persisted output of a Stack at `(stack, stage)`.
|
|
349
|
+
*
|
|
350
|
+
* Resolved at evaluation time by reading `state.getOutput({ stack,
|
|
351
|
+
* stage })`. Distinct from {@link RefExpr}, which references a
|
|
352
|
+
* single resource's attributes within a stack/stage. `stage` may be
|
|
353
|
+
* `undefined`, in which case it falls back to the current stage.
|
|
354
|
+
*/
|
|
355
|
+
export class StackRefExpr<A> extends BaseExpr<A, never> {
|
|
356
|
+
readonly kind = "StackRefExpr";
|
|
357
|
+
constructor(
|
|
358
|
+
public readonly stack: string,
|
|
359
|
+
public readonly stage: string | undefined,
|
|
360
|
+
) {
|
|
361
|
+
super();
|
|
362
|
+
return proxy(this);
|
|
363
|
+
}
|
|
364
|
+
[inspect](): string {
|
|
365
|
+
return `stackRef(${this.stack}${
|
|
366
|
+
this.stage ? `, { stage: ${this.stage} }` : ""
|
|
367
|
+
})`;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Build an `Output<A>` referencing the persisted output of another
|
|
373
|
+
* Stack. The returned Effect resolves to a lazy `Output<A>` whose
|
|
374
|
+
* value is read from the state store at plan/apply time.
|
|
375
|
+
*
|
|
376
|
+
* Returns `Effect<Output<A>>` (not `Output<A>` directly) so that
|
|
377
|
+
* `yield* Output.stackRef(...)` reads ergonomically inside an Effect
|
|
378
|
+
* generator and lines up with `Resource.ref` and `Stack.stage.<name>`.
|
|
379
|
+
*/
|
|
380
|
+
export const stackRef = <A>(
|
|
381
|
+
stack: string,
|
|
382
|
+
options: { stage?: string } = {},
|
|
383
|
+
): Effect.Effect<Output<A, never>> =>
|
|
384
|
+
Effect.succeed(new StackRefExpr<A>(stack, options.stage) as any);
|
|
385
|
+
|
|
312
386
|
export const filter = <Outs extends any[]>(...outs: Outs) =>
|
|
313
387
|
outs.filter(isOutput) as unknown as Filter<Outs>;
|
|
314
388
|
|
|
@@ -353,7 +427,32 @@ function proxy(self: any): any {
|
|
|
353
427
|
prop === ExprSymbol || prop === inspect ? true : prop in self,
|
|
354
428
|
get: (target, prop) =>
|
|
355
429
|
prop === Symbol.toPrimitive
|
|
356
|
-
? (hint: string) =>
|
|
430
|
+
? (hint: string) => {
|
|
431
|
+
// Any JS-level coercion of an unresolved Output produces a
|
|
432
|
+
// placeholder that *looks* like a real value but isn't:
|
|
433
|
+
//
|
|
434
|
+
// - `string` / `default` hints (`${output}`, `output + ""`,
|
|
435
|
+
// `==` against a primitive) previously fell through to
|
|
436
|
+
// `self.toString()` and returned the inspect form
|
|
437
|
+
// (e.g. "tunnel.tunnelId"). The bogus string flowed
|
|
438
|
+
// into resource props and into the cloud — only
|
|
439
|
+
// surfacing as an opaque downstream error (see PR
|
|
440
|
+
// description for a real Cloudflare DNS landing).
|
|
441
|
+
//
|
|
442
|
+
// - `number` hint (`+output`, `output * 2`,
|
|
443
|
+
// `Math.max(0, output)`) previously returned NaN, which
|
|
444
|
+
// propagates silently through arithmetic and lands as
|
|
445
|
+
// "the API rejected a NaN field" much later.
|
|
446
|
+
//
|
|
447
|
+
// All three hints fail loud at the coercion site with a
|
|
448
|
+
// pointer to the right composition API.
|
|
449
|
+
throw new Error(
|
|
450
|
+
`Cannot coerce Output<${self[inspect]()}> to a ` +
|
|
451
|
+
`${hint === "number" ? "number" : "string"} via JS coercion. ` +
|
|
452
|
+
`Use Output.interpolate\`...\` or Output.map(output, fn) ` +
|
|
453
|
+
`to compose Outputs — the value isn't known until deploy time.`,
|
|
454
|
+
);
|
|
455
|
+
}
|
|
357
456
|
: prop === ExprSymbol
|
|
358
457
|
? self
|
|
359
458
|
: prop === inspect
|
|
@@ -445,6 +544,8 @@ export const evaluate: <A, Req = never>(
|
|
|
445
544
|
);
|
|
446
545
|
} else if (isPropExpr(expr)) {
|
|
447
546
|
return (yield* evaluate(expr.expr, upstream))?.[expr.identifier];
|
|
547
|
+
} else if (isNamedExpr(expr)) {
|
|
548
|
+
return yield* evaluate(expr.expr, upstream);
|
|
448
549
|
} else if (isRefExpr(expr)) {
|
|
449
550
|
const state = yield* State.State;
|
|
450
551
|
const stack = expr.stack ?? (yield* Stack).name;
|
|
@@ -465,6 +566,22 @@ export const evaluate: <A, Req = never>(
|
|
|
465
566
|
);
|
|
466
567
|
}
|
|
467
568
|
return resource.attr;
|
|
569
|
+
} else if (isStackRefExpr(expr)) {
|
|
570
|
+
const state = yield* State.State;
|
|
571
|
+
const stack = expr.stack;
|
|
572
|
+
const stage = expr.stage ?? (yield* Stage);
|
|
573
|
+
const output = yield* state.getOutput({ stack, stage });
|
|
574
|
+
if (output == null) {
|
|
575
|
+
return yield* Effect.fail(
|
|
576
|
+
new InvalidReferenceError({
|
|
577
|
+
message: `Reference to stack '${stack}' at stage '${stage}' not found. Have you deployed stage '${stage}' of '${stack}'?`,
|
|
578
|
+
stack,
|
|
579
|
+
stage,
|
|
580
|
+
resourceId: stack,
|
|
581
|
+
}),
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
return output;
|
|
468
585
|
}
|
|
469
586
|
}
|
|
470
587
|
if (Array.isArray(expr)) {
|
|
@@ -483,14 +600,6 @@ export const evaluate: <A, Req = never>(
|
|
|
483
600
|
return expr;
|
|
484
601
|
}) as Effect.Effect<any>;
|
|
485
602
|
|
|
486
|
-
export const ref = <R extends ResourceLike>(
|
|
487
|
-
resourceId: R["LogicalId"],
|
|
488
|
-
options?: {
|
|
489
|
-
stage?: string;
|
|
490
|
-
stack?: string;
|
|
491
|
-
},
|
|
492
|
-
) => of(stageRef({ id: resourceId, ...options }));
|
|
493
|
-
|
|
494
603
|
export const hasOutputs = (value: any): value is Output<any, any> =>
|
|
495
604
|
Object.keys(upstreamAny(value)).length > 0;
|
|
496
605
|
|
|
@@ -531,7 +640,7 @@ export const upstream = <E extends Output<any, any>>(expr: E): any => {
|
|
|
531
640
|
return upstream(expr.expr);
|
|
532
641
|
} else if (isAllExpr(expr)) {
|
|
533
642
|
return Object.assign({}, ...expr.outs.map((out) => upstream(out)));
|
|
534
|
-
} else if (isEffectExpr(expr) || isApplyExpr(expr)) {
|
|
643
|
+
} else if (isEffectExpr(expr) || isApplyExpr(expr) || isNamedExpr(expr)) {
|
|
535
644
|
return upstream(expr.expr);
|
|
536
645
|
} else if (Array.isArray(expr)) {
|
|
537
646
|
return expr.map(upstream).reduce(toObject, {});
|