diff --git a/src/cloudflare/internal/test/workflows/workflows-api-test.js b/src/cloudflare/internal/test/workflows/workflows-api-test.js index 77d67e58f59..6c3370553ce 100644 --- a/src/cloudflare/internal/test/workflows/workflows-api-test.js +++ b/src/cloudflare/internal/test/workflows/workflows-api-test.js @@ -58,6 +58,34 @@ export const workflowsApi = { assert.deepStrictEqual(instances[1].id, 'bar'); } + { + const result = await env.workflow.createBatch({ + count: 2, + params: { bar: 'baz' }, + }); + assert.deepStrictEqual( + result.created.map(({ id }) => id), + ['generated-0', 'generated-1'] + ); + assert.deepStrictEqual(result.errors, []); + } + + { + const result = await env.workflow.createBatch({ + instances: [{ id: 'batch-ok' }, { id: 'batch-error' }], + }); + assert.deepStrictEqual(result.created[0].id, 'batch-ok'); + assert.strictEqual(typeof result.created[0].status, 'function'); + assert.deepStrictEqual(result.errors, [ + { + index: 1, + id: 'batch-error', + code: 10405, + message: 'Provided instance ID already exists', + }, + ]); + } + { const result = await env.workflow.deleteBatch([ 'delete-1', diff --git a/src/cloudflare/internal/test/workflows/workflows-mock.js b/src/cloudflare/internal/test/workflows/workflows-mock.js index b0729a9e12b..fec54ab47e0 100644 --- a/src/cloudflare/internal/test/workflows/workflows-mock.js +++ b/src/cloudflare/internal/test/workflows/workflows-mock.js @@ -8,6 +8,7 @@ const restartBodies = new Map(); const subscribeOptions = new Map(); const THROW_ID = 'throw'; +const BATCH_ERROR_ID = 'batch-error'; const MISSING_DELETE_ID = 'missing-delete'; class SubscriptionMock extends RpcTarget { @@ -41,7 +42,32 @@ export default class WorkflowsMock extends WorkerEntrypoint { } async createBatch(options) { - return options.map((val) => ({ id: val.id })); + if (Array.isArray(options)) { + return options.map((val) => ({ id: val.id })); + } + + const instances = + options.instances ?? + Array.from({ length: options.count }, (_, index) => ({ + id: `generated-${index}`, + })); + const created = []; + const errors = []; + + for (const [index, instance] of instances.entries()) { + if (instance.id === BATCH_ERROR_ID) { + errors.push({ + index, + id: instance.id, + code: 10405, + message: 'Provided instance ID already exists', + }); + } else { + created.push({ id: instance.id }); + } + } + + return { created, errors }; } async deleteBatch(options) { diff --git a/src/cloudflare/internal/workflows-api.ts b/src/cloudflare/internal/workflows-api.ts index 075e522a16f..7cd4a800be7 100644 --- a/src/cloudflare/internal/workflows-api.ts +++ b/src/cloudflare/internal/workflows-api.ts @@ -23,6 +23,10 @@ interface Fetcher { createBatch( options: WorkflowInstanceCreateOptions[] ): Promise<{ id: string }[]>; + createBatch(options: WorkflowBatchCreateOptions): Promise<{ + created: { id: string }[]; + errors: WorkflowBatchCreateResult['errors']; + }>; deleteBatch(options: { instances: string[]; }): Promise; @@ -123,10 +127,29 @@ class WorkflowImpl extends wrappedBinding.WrappedBinding { async createBatch( options: WorkflowInstanceCreateOptions[] - ): Promise { - const results = await this.#fetcher.createBatch(options); - - return results.map((result) => new InstanceImpl(result.id, this.#fetcher)); + ): Promise; + async createBatch( + options: WorkflowBatchCreateOptions + ): Promise; + async createBatch( + options: WorkflowInstanceCreateOptions[] | WorkflowBatchCreateOptions + ): Promise { + if (Array.isArray(options)) { + const results = await this.#fetcher.createBatch(options); + + return results.map( + (result) => new InstanceImpl(result.id, this.#fetcher) + ); + } + + const result = await this.#fetcher.createBatch(options); + + return { + created: result.created.map( + ({ id }) => new InstanceImpl(id, this.#fetcher) + ), + errors: result.errors, + }; } async deleteBatch(instanceIds: string[]): Promise { diff --git a/src/cloudflare/internal/workflows.d.ts b/src/cloudflare/internal/workflows.d.ts index c02b7a2812d..5b16a25bf31 100644 --- a/src/cloudflare/internal/workflows.d.ts +++ b/src/cloudflare/internal/workflows.d.ts @@ -40,16 +40,53 @@ declare abstract class Workflow { ): Promise; /** - * Create a batch of instances and return handle for all of them. If a provided id exists, an error will be thrown. + * Create a batch of instances and return handles for the created instances and any per-instance errors. * `createBatch` is limited at 100 instances at a time or when the RPC limit (1MiB) is reached. - * @param batch List of Options when creating an instance including name and params - * @returns A promise that resolves with a list of handles for the created instances. + * @param options Options for creating instances by count or from a list of instance options + * @returns A promise that resolves with the created instance handles and any per-instance errors. + */ + createBatch( + options: WorkflowBatchCreateOptions + ): Promise; + + /** + * Create a batch of instances and return handles for all of them. + * @deprecated Use the object form of `createBatch` instead of the array form. */ createBatch( batch: WorkflowInstanceCreateOptions[] ): Promise; } +type WorkflowBatchCreateOptions = + | { + count: number; + params?: PARAMS; + retention?: { + successRetention?: WorkflowRetentionDuration; + errorRetention?: WorkflowRetentionDuration; + }; + locationHint?: WorkflowInstanceLocationHint; + instances?: never; + } + | { + instances: WorkflowInstanceCreateOptions[]; + count?: never; + params?: never; + retention?: never; + locationHint?: never; + }; + +type WorkflowBatchCreateResult = { + created: WorkflowInstance[]; + errors: { + index: number; + id?: string; + code: number; + message: string; + }[]; +}; + type WorkflowDurationLabel = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year'; diff --git a/types/defines/workflows.d.ts b/types/defines/workflows.d.ts index 8b575ed1fa4..7883aa15c69 100644 --- a/types/defines/workflows.d.ts +++ b/types/defines/workflows.d.ts @@ -26,10 +26,18 @@ declare abstract class Workflow { ): Promise; /** - * Create a batch of instances and return handle for all of them. If a provided id exists, an error will be thrown. + * Create a batch of instances and return handles for the created instances and any per-instance errors. * `createBatch` is limited at 100 instances at a time or when the RPC limit for the batch (1MiB) is reached. - * @param batch List of Options when creating an instance including name and params - * @returns A promise that resolves with a list of handles for the created instances. + * @param options Options for creating instances by count or from a list of instance options + * @returns A promise that resolves with the created instance handles and any per-instance errors. + */ + public createBatch( + options: WorkflowBatchCreateOptions + ): Promise; + + /** + * Create a batch of instances and return handles for all of them. + * @deprecated Use the object form of `createBatch` instead of the array form. */ public createBatch( batch: WorkflowInstanceCreateOptions[] @@ -45,6 +53,35 @@ declare abstract class Workflow { public deleteBatch(instanceIds: string[]): Promise; } +type WorkflowBatchCreateOptions = + | { + count: number; + params?: PARAMS; + retention?: { + successRetention?: WorkflowRetentionDuration; + errorRetention?: WorkflowRetentionDuration; + }; + locationHint?: WorkflowInstanceLocationHint; + instances?: never; + } + | { + instances: WorkflowInstanceCreateOptions[]; + count?: never; + params?: never; + retention?: never; + locationHint?: never; + }; + +type WorkflowBatchCreateResult = { + created: WorkflowInstance[]; + errors: { + index: number; + id?: string; + code: number; + message: string; + }[]; +}; + type WorkflowBatchDeleteResult = { deleted: { id: string }[]; errors: { diff --git a/types/generated-snapshot/experimental/index.d.ts b/types/generated-snapshot/experimental/index.d.ts index 231a08c0c42..b29f2b2d8f8 100755 --- a/types/generated-snapshot/experimental/index.d.ts +++ b/types/generated-snapshot/experimental/index.d.ts @@ -17667,10 +17667,17 @@ declare abstract class Workflow { options?: WorkflowInstanceCreateOptions, ): Promise; /** - * Create a batch of instances and return handle for all of them. If a provided id exists, an error will be thrown. + * Create a batch of instances and return handles for the created instances and any per-instance errors. * `createBatch` is limited at 100 instances at a time or when the RPC limit for the batch (1MiB) is reached. - * @param batch List of Options when creating an instance including name and params - * @returns A promise that resolves with a list of handles for the created instances. + * @param options Options for creating instances by count or from a list of instance options + * @returns A promise that resolves with the created instance handles and any per-instance errors. + */ + public createBatch( + options: WorkflowBatchCreateOptions, + ): Promise; + /** + * Create a batch of instances and return handles for all of them. + * @deprecated Use the object form of `createBatch` instead of the array form. */ public createBatch( batch: WorkflowInstanceCreateOptions[], @@ -17684,6 +17691,33 @@ declare abstract class Workflow { */ public deleteBatch(instanceIds: string[]): Promise; } +type WorkflowBatchCreateOptions = + | { + count: number; + params?: PARAMS; + retention?: { + successRetention?: WorkflowRetentionDuration; + errorRetention?: WorkflowRetentionDuration; + }; + locationHint?: WorkflowInstanceLocationHint; + instances?: never; + } + | { + instances: WorkflowInstanceCreateOptions[]; + count?: never; + params?: never; + retention?: never; + locationHint?: never; + }; +type WorkflowBatchCreateResult = { + created: WorkflowInstance[]; + errors: { + index: number; + id?: string; + code: number; + message: string; + }[]; +}; type WorkflowBatchDeleteResult = { deleted: { id: string; diff --git a/types/generated-snapshot/experimental/index.ts b/types/generated-snapshot/experimental/index.ts index 075d7a4de07..c96b86e19c9 100755 --- a/types/generated-snapshot/experimental/index.ts +++ b/types/generated-snapshot/experimental/index.ts @@ -17616,10 +17616,17 @@ export declare abstract class Workflow { options?: WorkflowInstanceCreateOptions, ): Promise; /** - * Create a batch of instances and return handle for all of them. If a provided id exists, an error will be thrown. + * Create a batch of instances and return handles for the created instances and any per-instance errors. * `createBatch` is limited at 100 instances at a time or when the RPC limit for the batch (1MiB) is reached. - * @param batch List of Options when creating an instance including name and params - * @returns A promise that resolves with a list of handles for the created instances. + * @param options Options for creating instances by count or from a list of instance options + * @returns A promise that resolves with the created instance handles and any per-instance errors. + */ + public createBatch( + options: WorkflowBatchCreateOptions, + ): Promise; + /** + * Create a batch of instances and return handles for all of them. + * @deprecated Use the object form of `createBatch` instead of the array form. */ public createBatch( batch: WorkflowInstanceCreateOptions[], @@ -17633,6 +17640,33 @@ export declare abstract class Workflow { */ public deleteBatch(instanceIds: string[]): Promise; } +export type WorkflowBatchCreateOptions = + | { + count: number; + params?: PARAMS; + retention?: { + successRetention?: WorkflowRetentionDuration; + errorRetention?: WorkflowRetentionDuration; + }; + locationHint?: WorkflowInstanceLocationHint; + instances?: never; + } + | { + instances: WorkflowInstanceCreateOptions[]; + count?: never; + params?: never; + retention?: never; + locationHint?: never; + }; +export type WorkflowBatchCreateResult = { + created: WorkflowInstance[]; + errors: { + index: number; + id?: string; + code: number; + message: string; + }[]; +}; export type WorkflowBatchDeleteResult = { deleted: { id: string; diff --git a/types/generated-snapshot/index.d.ts b/types/generated-snapshot/index.d.ts index ae78aa33079..d57a01a9382 100755 --- a/types/generated-snapshot/index.d.ts +++ b/types/generated-snapshot/index.d.ts @@ -17390,10 +17390,17 @@ declare abstract class Workflow { options?: WorkflowInstanceCreateOptions, ): Promise; /** - * Create a batch of instances and return handle for all of them. If a provided id exists, an error will be thrown. + * Create a batch of instances and return handles for the created instances and any per-instance errors. * `createBatch` is limited at 100 instances at a time or when the RPC limit for the batch (1MiB) is reached. - * @param batch List of Options when creating an instance including name and params - * @returns A promise that resolves with a list of handles for the created instances. + * @param options Options for creating instances by count or from a list of instance options + * @returns A promise that resolves with the created instance handles and any per-instance errors. + */ + public createBatch( + options: WorkflowBatchCreateOptions, + ): Promise; + /** + * Create a batch of instances and return handles for all of them. + * @deprecated Use the object form of `createBatch` instead of the array form. */ public createBatch( batch: WorkflowInstanceCreateOptions[], @@ -17407,6 +17414,33 @@ declare abstract class Workflow { */ public deleteBatch(instanceIds: string[]): Promise; } +type WorkflowBatchCreateOptions = + | { + count: number; + params?: PARAMS; + retention?: { + successRetention?: WorkflowRetentionDuration; + errorRetention?: WorkflowRetentionDuration; + }; + locationHint?: WorkflowInstanceLocationHint; + instances?: never; + } + | { + instances: WorkflowInstanceCreateOptions[]; + count?: never; + params?: never; + retention?: never; + locationHint?: never; + }; +type WorkflowBatchCreateResult = { + created: WorkflowInstance[]; + errors: { + index: number; + id?: string; + code: number; + message: string; + }[]; +}; type WorkflowBatchDeleteResult = { deleted: { id: string; diff --git a/types/generated-snapshot/index.ts b/types/generated-snapshot/index.ts index c8263d6f166..29005ddc684 100755 --- a/types/generated-snapshot/index.ts +++ b/types/generated-snapshot/index.ts @@ -17339,10 +17339,17 @@ export declare abstract class Workflow { options?: WorkflowInstanceCreateOptions, ): Promise; /** - * Create a batch of instances and return handle for all of them. If a provided id exists, an error will be thrown. + * Create a batch of instances and return handles for the created instances and any per-instance errors. * `createBatch` is limited at 100 instances at a time or when the RPC limit for the batch (1MiB) is reached. - * @param batch List of Options when creating an instance including name and params - * @returns A promise that resolves with a list of handles for the created instances. + * @param options Options for creating instances by count or from a list of instance options + * @returns A promise that resolves with the created instance handles and any per-instance errors. + */ + public createBatch( + options: WorkflowBatchCreateOptions, + ): Promise; + /** + * Create a batch of instances and return handles for all of them. + * @deprecated Use the object form of `createBatch` instead of the array form. */ public createBatch( batch: WorkflowInstanceCreateOptions[], @@ -17356,6 +17363,33 @@ export declare abstract class Workflow { */ public deleteBatch(instanceIds: string[]): Promise; } +export type WorkflowBatchCreateOptions = + | { + count: number; + params?: PARAMS; + retention?: { + successRetention?: WorkflowRetentionDuration; + errorRetention?: WorkflowRetentionDuration; + }; + locationHint?: WorkflowInstanceLocationHint; + instances?: never; + } + | { + instances: WorkflowInstanceCreateOptions[]; + count?: never; + params?: never; + retention?: never; + locationHint?: never; + }; +export type WorkflowBatchCreateResult = { + created: WorkflowInstance[]; + errors: { + index: number; + id?: string; + code: number; + message: string; + }[]; +}; export type WorkflowBatchDeleteResult = { deleted: { id: string; diff --git a/types/test/types/rpc.ts b/types/test/types/rpc.ts index 0e50de33087..f0bb8627cad 100644 --- a/types/test/types/rpc.ts +++ b/types/test/types/rpc.ts @@ -1318,9 +1318,39 @@ expectTypeOf(withoutSchedule.schedule).toEqualTypeOf< WorkflowCronSchedule | undefined >(); -declare const workflow: Workflow; +declare const workflow: Workflow; declare const workflowInstance: WorkflowInstance; expectTypeOf(workflowInstance.delete()).toEqualTypeOf>(); +expectTypeOf( + workflow.createBatch({ + count: 2, + params: {foo: 'bar'}, + retention: {successRetention: '1 day'}, + locationHint: 'weur', + }) +).toEqualTypeOf>(); +expectTypeOf( + workflow.createBatch({instances: [{id: 'one', params: {foo: 'bar'}}]}) +).toEqualTypeOf>(); +expectTypeOf( + workflow.createBatch([{id: 'one', params: {foo: 'bar'}}]) +).toEqualTypeOf>(); +expectTypeOf().toEqualTypeOf< + WorkflowInstance +>(); +expectTypeOf().toEqualTypeOf<{ + index: number; + id?: string; + code: number; + message: string; +}>(); + +// @ts-expect-error count and instances are mutually exclusive +workflow.createBatch({count: 1, instances: []}); + +// @ts-expect-error batch params must match the Workflow generic +workflow.createBatch({count: 1, params: {foo: 1}}); + expectTypeOf(workflow.deleteBatch(['one', 'two'])).toEqualTypeOf< Promise >();