effect-cf 0.30.0 → 0.32.0

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/src/Images.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import type {
2
2
  HostedImagesBinding as CloudflareHostedImagesBinding,
3
+ ImageDirectUploadOptions as CloudflareImageDirectUploadOptions,
4
+ ImageDirectUploadResult as CloudflareImageDirectUploadResult,
3
5
  ImageDrawOptions as CloudflareImageDrawOptions,
4
6
  ImageHandle as CloudflareImageHandle,
5
7
  ImageInfoResponse as CloudflareImageInfoResponse,
@@ -8,13 +10,16 @@ import type {
8
10
  ImageListOptions as CloudflareImageListOptions,
9
11
  ImageMetadata as CloudflareImageMetadata,
10
12
  ImageOutputOptions as CloudflareImageOutputOptions,
13
+ ImageSignedUrlOptions as CloudflareImageSignedUrlOptions,
11
14
  ImageTransform as CloudflareImageTransform,
12
15
  ImageTransformationOutputOptions as CloudflareImageTransformationOutputOptions,
16
+ ImageTransformationResponseOptions as CloudflareImageTransformationResponseOptions,
13
17
  ImageTransformationResult as CloudflareImageTransformationResult,
14
18
  ImageTransformer as CloudflareImageTransformer,
15
19
  ImageUpdateOptions as CloudflareImageUpdateOptions,
16
20
  ImageUploadOptions as CloudflareImageUploadOptions,
17
21
  Response as CloudflareResponse,
22
+ TextOptions as CloudflareTextOptions,
18
23
  } from "@cloudflare/workers-types";
19
24
  import { Context, Data, Effect, Function, Option, Predicate, type Layer } from "effect";
20
25
 
@@ -51,12 +56,17 @@ export type ImageDrawOptions = CloudflareImageDrawOptions;
51
56
  export type ImageInputOptions = CloudflareImageInputOptions;
52
57
  export type ImageOutputOptions = CloudflareImageOutputOptions;
53
58
  export type ImageTransformationOutputOptions = CloudflareImageTransformationOutputOptions;
59
+ export type ImageTransformationResponseOptions = CloudflareImageTransformationResponseOptions;
54
60
  export type ImageTransformationResult = CloudflareImageTransformationResult;
55
61
  export type ImageUploadOptions = CloudflareImageUploadOptions;
56
62
  export type ImageUpdateOptions = CloudflareImageUpdateOptions;
57
63
  export type ImageListOptions = CloudflareImageListOptions;
58
64
  export type ImageList = CloudflareImageList;
59
65
  export type ImageMetadata = CloudflareImageMetadata;
66
+ export type ImageSignedUrlOptions = CloudflareImageSignedUrlOptions;
67
+ export type ImageDirectUploadOptions = CloudflareImageDirectUploadOptions;
68
+ export type ImageDirectUploadResult = CloudflareImageDirectUploadResult;
69
+ export type TextOptions = CloudflareTextOptions;
60
70
  export type ImageInputValue = ReadableStream<Uint8Array> | ArrayBuffer;
61
71
  export type ImageUploadValue = ReadableStream<Uint8Array> | ArrayBuffer;
62
72
 
@@ -79,15 +89,23 @@ export interface Steps {
79
89
  readonly steps: ReadonlyArray<Step>;
80
90
  }
81
91
 
82
- export interface ProcessOptions {
83
- readonly stream: ImageInputValue;
84
- readonly inputOptions?: ImageInputOptions;
85
- readonly outputOptions: ImageOutputOptions;
86
- }
92
+ export type ProcessOptions =
93
+ | {
94
+ readonly stream: ImageInputValue;
95
+ readonly inputOptions?: ImageInputOptions;
96
+ readonly outputOptions: ImageOutputOptions;
97
+ }
98
+ | {
99
+ readonly text: string;
100
+ readonly textOptions: TextOptions;
101
+ readonly outputOptions: ImageOutputOptions;
102
+ };
87
103
 
88
104
  export interface ImagesTransformationResultClient {
89
105
  readonly raw: CloudflareImageTransformationResult;
90
- readonly response: Effect.Effect<CloudflareResponse, ImagesOperationError>;
106
+ readonly response: (
107
+ options?: ImageTransformationResponseOptions,
108
+ ) => Effect.Effect<CloudflareResponse, ImagesOperationError>;
91
109
  readonly contentType: Effect.Effect<string, ImagesOperationError>;
92
110
  readonly image: (
93
111
  options?: ImageTransformationOutputOptions,
@@ -98,6 +116,9 @@ export interface ImageHandleClient {
98
116
  readonly raw: CloudflareImageHandle;
99
117
  readonly details: Effect.Effect<Option.Option<ImageMetadata>, ImagesOperationError>;
100
118
  readonly bytes: Effect.Effect<Option.Option<ReadableStream<Uint8Array>>, ImagesOperationError>;
119
+ readonly signedUrl: (
120
+ options: ImageSignedUrlOptions,
121
+ ) => Effect.Effect<string, ImagesOperationError>;
101
122
  readonly update: (
102
123
  options: ImageUpdateOptions,
103
124
  ) => Effect.Effect<ImageMetadata, ImagesOperationError>;
@@ -111,6 +132,9 @@ export interface HostedImagesClient {
111
132
  options?: ImageUploadOptions,
112
133
  ) => Effect.Effect<ImageMetadata, ImagesOperationError>;
113
134
  readonly list: (options?: ImageListOptions) => Effect.Effect<ImageList, ImagesOperationError>;
135
+ readonly createDirectUpload: (
136
+ options?: ImageDirectUploadOptions,
137
+ ) => Effect.Effect<ImageDirectUploadResult, ImagesOperationError>;
114
138
  readonly rawUnsafe: Effect.Effect<CloudflareHostedImagesBinding>;
115
139
  }
116
140
 
@@ -123,6 +147,7 @@ export interface ImagesRuntimeBinding {
123
147
  image: ImageInputValue,
124
148
  options?: ImageInputOptions,
125
149
  ) => CloudflareImageTransformer;
150
+ readonly text?: (content: string, options: TextOptions) => CloudflareImageTransformer;
126
151
  readonly hosted?: CloudflareHostedImagesBinding;
127
152
  }
128
153
 
@@ -135,6 +160,10 @@ export interface ImagesClient {
135
160
  image: ImageInputValue,
136
161
  options?: ImageInputOptions,
137
162
  ) => Effect.Effect<CloudflareImageTransformer, ImagesOperationError>;
163
+ readonly text: (
164
+ content: string,
165
+ options: TextOptions,
166
+ ) => Effect.Effect<CloudflareImageTransformer, ImagesOperationError>;
138
167
  readonly process: (
139
168
  steps: Steps,
140
169
  options: ProcessOptions,
@@ -230,7 +259,10 @@ const hasFunction = <Candidate>(value: Candidate, key: string): boolean =>
230
259
  const isHostedImagesBinding = <Candidate>(
231
260
  value: Candidate,
232
261
  ): value is Candidate & CloudflareHostedImagesBinding =>
233
- hasFunction(value, "image") && hasFunction(value, "upload") && hasFunction(value, "list");
262
+ hasFunction(value, "image") &&
263
+ hasFunction(value, "upload") &&
264
+ hasFunction(value, "list") &&
265
+ hasFunction(value, "createDirectUpload");
234
266
 
235
267
  export const isImagesBinding = <Candidate>(
236
268
  value: Candidate,
@@ -242,7 +274,7 @@ const wrapResult = (
242
274
  result: CloudflareImageTransformationResult,
243
275
  ): ImagesTransformationResultClient => ({
244
276
  raw: result,
245
- response: tryImagesSync(binding, "response", () => result.response()),
277
+ response: (options) => tryImagesSync(binding, "response", () => result.response(options)),
246
278
  contentType: tryImagesSync(binding, "contentType", () => result.contentType()),
247
279
  image: (options) => tryImagesSync(binding, "image", () => result.image(options)),
248
280
  });
@@ -257,6 +289,9 @@ const wrapHandle = (binding: string, handle: CloudflareImageHandle): ImageHandle
257
289
  Effect.map(maybe),
258
290
  Effect.withSpan("Images.bytes"),
259
291
  ),
292
+ signedUrl: Effect.fn("Images.signedUrl")((options: ImageSignedUrlOptions) =>
293
+ tryImagesPromise(binding, "signedUrl", () => handle.signedUrl(options)),
294
+ ),
260
295
  update: Effect.fn("Images.update")((options: ImageUpdateOptions) =>
261
296
  tryImagesPromise(binding, "update", () => handle.update(options)),
262
297
  ),
@@ -276,6 +311,9 @@ const wrapHosted = (
276
311
  list: Effect.fn("Images.list")((options?: ImageListOptions) =>
277
312
  tryImagesPromise(binding, "list", () => hosted.list(options)),
278
313
  ),
314
+ createDirectUpload: Effect.fn("Images.createDirectUpload")((options?: ImageDirectUploadOptions) =>
315
+ tryImagesPromise(binding, "createDirectUpload", () => hosted.createDirectUpload(options)),
316
+ ),
279
317
  rawUnsafe: Effect.succeed(hosted),
280
318
  });
281
319
 
@@ -285,8 +323,20 @@ export const makeClient =
285
323
  const input = (image: ImageInputValue, options?: ImageInputOptions) =>
286
324
  tryImagesSync(definition.binding, "input", () => images.input(image, options));
287
325
 
326
+ const text = (content: string, options: TextOptions) =>
327
+ tryImagesSync(definition.binding, "text", () => {
328
+ if (!hasFunction(images, "text")) {
329
+ throw new Error("Images binding is missing text()");
330
+ }
331
+
332
+ return images.text!(content, options);
333
+ });
334
+
288
335
  const process = Effect.fn("Images.process")(function* (steps: Steps, options: ProcessOptions) {
289
- let transformer = yield* input(options.stream, options.inputOptions);
336
+ let transformer =
337
+ "text" in options
338
+ ? yield* text(options.text, options.textOptions)
339
+ : yield* input(options.stream, options.inputOptions);
290
340
 
291
341
  for (const step of steps.steps) {
292
342
  transformer = yield* Step.$match(step, {
@@ -314,6 +364,7 @@ export const makeClient =
314
364
  tryImagesPromise(definition.binding, "info", () => images.info(image, options)),
315
365
  ),
316
366
  input,
367
+ text,
317
368
  process,
318
369
  hosted: isHostedImagesBinding(images.hosted)
319
370
  ? Option.some(wrapHosted(definition.binding, images.hosted))
package/src/Vitest.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Effect-native helpers for tests running in Cloudflare's Vitest Workers pool.
2
+ * Effect-native helpers for tests running in Cloudflare's Workers Vitest plugin.
3
3
  *
4
4
  * Import this module through the `effect-cf/vitest` subpath. It is intended to
5
5
  * run inside the Workers test runtime, where `cloudflare:test` is available.
@@ -37,7 +37,7 @@ import type { WorkflowInstanceStatusName } from "./WorkflowBinding";
37
37
  const currentEnv: WorkerEnv = env;
38
38
 
39
39
  /**
40
- * Provides the Workers pool environment as both {@link WorkerEnvironment} and
40
+ * Provides the Workers test environment as both {@link WorkerEnvironment} and
41
41
  * the active Effect `ConfigProvider`.
42
42
  */
43
43
  export const layer: Layer.Layer<WorkerEnvironment> = Layer.mergeAll(
@@ -134,7 +134,7 @@ type PagesEventContextInitData<Context> =
134
134
  : { readonly data: Data }
135
135
  : never;
136
136
 
137
- /** Initialization accepted by Pool Workers for a Pages Function. */
137
+ /** Initialization accepted by the Workers Vitest plugin for a Pages Function. */
138
138
  export type PagesEventContextInit<Function extends AnyPagesFunction> = PagesEventContextInitBase &
139
139
  PagesEventContextInitParams<Parameters<Function>[0]> &
140
140
  PagesEventContextInitData<Parameters<Function>[0]>;
@@ -162,14 +162,14 @@ interface QueueWorker {
162
162
  queue(batch: globalThis.MessageBatch): Promise<void>;
163
163
  }
164
164
 
165
- /** Input message accepted by Pool Workers' `createMessageBatch` helper. */
165
+ /** Input message accepted by the Workers Vitest plugin's `createMessageBatch` helper. */
166
166
  export type QueueMessage<Body> = {
167
167
  readonly id: string;
168
168
  readonly timestamp: Date;
169
169
  readonly attempts: number;
170
170
  } & ({ readonly body: Body } | { readonly serializedBody: ArrayBuffer | ArrayBufferView });
171
171
 
172
- /** Acknowledgement and retry result returned by Pool Workers. */
172
+ /** Acknowledgement and retry result returned by the Workers Vitest plugin. */
173
173
  export type QueueResult = Awaited<ReturnType<typeof getQueueResult>>;
174
174
 
175
175
  /** Result of invoking a queue consumer through {@link queue}. */
@@ -180,7 +180,7 @@ export interface QueueRunResult<Body> {
180
180
 
181
181
  /**
182
182
  * Invokes an Effect-backed queue consumer, drains its `waitUntil` work, and
183
- * returns Pool Workers' acknowledgement and retry result.
183
+ * returns the Workers Vitest plugin's acknowledgement and retry result.
184
184
  */
185
185
  export const queue = Effect.fn("effect-cf/vitest/queue")(function* <
186
186
  Body,
@@ -280,7 +280,7 @@ export const evictAllDurableObjects = Effect.fn("effect-cf/vitest/evictAllDurabl
280
280
  },
281
281
  );
282
282
 
283
- /** A D1 migration accepted by Pool Workers. */
283
+ /** A D1 migration accepted by the Workers Vitest plugin. */
284
284
  export interface D1Migration {
285
285
  readonly name: string;
286
286
  readonly queries: ReadonlyArray<string>;
@@ -310,7 +310,7 @@ export interface SecretsStoreSecret {
310
310
  readonly metadata?: { readonly uuid: string };
311
311
  }
312
312
 
313
- /** Effect-native wrapper around Pool Workers' Secrets Store admin API. */
313
+ /** Effect-native wrapper around the Workers Vitest plugin's Secrets Store admin API. */
314
314
  export interface SecretsStoreAdmin {
315
315
  readonly raw: NativeSecretsStoreAdmin;
316
316
  readonly create: (value: string) => Effect.Effect<string>;
@@ -445,7 +445,7 @@ const runWorkflowModifier = Effect.fnUntraced(function* <A, E, R>(
445
445
  }
446
446
 
447
447
  if (callbackExit === undefined) {
448
- throw new Error("Pool Workers did not invoke the Workflow modifier callback");
448
+ throw new Error("The Workers Vitest plugin did not invoke the Workflow modifier callback");
449
449
  }
450
450
 
451
451
  return callbackExit;
package/src/Worker.ts CHANGED
@@ -46,6 +46,8 @@ export interface WorkerContextService {
46
46
  options?: Omit<WorkerContextWaitUntilOptions<E, R2>, "mode">,
47
47
  ): Effect.Effect<void, never, R | R2>;
48
48
  readonly passThroughOnException: Effect.Effect<void>;
49
+ /** Abort the current stateless Worker invocation (`ExecutionContext.abort`). */
50
+ abort(reason?: string): Effect.Effect<void>;
49
51
  }
50
52
 
51
53
  /**
@@ -3,7 +3,9 @@ import type {
3
3
  Workflow as CloudflareWorkflow,
4
4
  WorkflowInstance as CloudflareWorkflowInstance,
5
5
  WorkflowInstanceCreateOptions as CloudflareWorkflowInstanceCreateOptions,
6
+ WorkflowInstanceLocationHint as CloudflareWorkflowInstanceLocationHint,
6
7
  WorkflowInstanceRestartOptions as CloudflareWorkflowInstanceRestartOptions,
8
+ WorkflowInstanceTerminateOptions as CloudflareWorkflowInstanceTerminateOptions,
7
9
  } from "@cloudflare/workers-types";
8
10
  import { type Context, Data, Effect, Option, Predicate, Schema as S } from "effect";
9
11
 
@@ -23,6 +25,8 @@ export type WorkflowInstanceCreateBatchOptions<Payload, EncodedPayload = unknown
23
25
  >;
24
26
 
25
27
  export type WorkflowInstanceRestartOptions = CloudflareWorkflowInstanceRestartOptions;
28
+ export type WorkflowInstanceTerminateOptions = CloudflareWorkflowInstanceTerminateOptions;
29
+ export type WorkflowInstanceLocationHint = CloudflareWorkflowInstanceLocationHint;
26
30
 
27
31
  export type WorkflowInstanceStatusName = CloudflareInstanceStatus["status"];
28
32
 
@@ -40,10 +44,13 @@ export interface WorkflowInstance<Result> {
40
44
  readonly id: string;
41
45
  readonly pause: Effect.Effect<void, WorkflowOperationError>;
42
46
  readonly resume: Effect.Effect<void, WorkflowOperationError>;
43
- readonly terminate: Effect.Effect<void, WorkflowOperationError>;
47
+ readonly terminate: (
48
+ options?: WorkflowInstanceTerminateOptions,
49
+ ) => Effect.Effect<void, WorkflowOperationError>;
44
50
  readonly restart: (
45
51
  options?: WorkflowInstanceRestartOptions,
46
52
  ) => Effect.Effect<void, WorkflowOperationError>;
53
+ readonly delete: Effect.Effect<void, WorkflowOperationError>;
47
54
  readonly status: Effect.Effect<
48
55
  WorkflowInstanceStatus<Result>,
49
56
  WorkflowOperationError | WorkflowResultDecodeError
@@ -165,8 +172,9 @@ export const makeClient = <
165
172
  id: raw.id,
166
173
  pause: operation("pause", () => raw.pause()),
167
174
  resume: operation("resume", () => raw.resume()),
168
- terminate: operation("terminate", () => raw.terminate()),
175
+ terminate: (options) => operation("terminate", () => raw.terminate(options)),
169
176
  restart: (options) => operation("restart", () => raw.restart(options)),
177
+ delete: operation("delete", () => raw.delete()),
170
178
  status: operation("status", () => raw.status()).pipe(
171
179
  Effect.flatMap((status) =>
172
180
  Effect.gen(function* () {
@@ -101,5 +101,6 @@ export const fromExecutionContext = (
101
101
  options?: Omit<WorkerContextWaitUntilOptions<E, R2>, "mode">,
102
102
  ) => schedule(effect, options, "propagate"),
103
103
  passThroughOnException: Effect.sync(() => ctx.passThroughOnException()),
104
+ abort: (reason?: string) => Effect.sync(() => ctx.abort(reason)),
104
105
  };
105
106
  };