@outputai/core 0.7.0 → 0.7.1-next.0e958f3.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.
Files changed (66) hide show
  1. package/bin/worker.sh +6 -0
  2. package/package.json +1 -1
  3. package/src/consts.js +0 -4
  4. package/src/errors.js +6 -2
  5. package/src/hooks/index.d.ts +10 -0
  6. package/src/interface/evaluator.js +7 -20
  7. package/src/interface/evaluator.spec.js +117 -1
  8. package/src/interface/step.js +8 -9
  9. package/src/interface/step.spec.js +124 -0
  10. package/src/interface/validations/index.js +108 -0
  11. package/src/interface/validations/index.spec.js +182 -0
  12. package/src/interface/validations/schemas.js +113 -0
  13. package/src/interface/validations/schemas.spec.js +209 -0
  14. package/src/interface/webhook.js +1 -1
  15. package/src/interface/webhook.spec.js +1 -1
  16. package/src/interface/workflow.d.ts +10 -9
  17. package/src/interface/workflow.js +76 -164
  18. package/src/interface/workflow.spec.js +637 -521
  19. package/src/interface/workflow_activity_options.js +16 -0
  20. package/src/interface/workflow_utils.js +1 -1
  21. package/src/interface/zod_integration.spec.js +2 -2
  22. package/src/internal_utils/aggregations.js +0 -10
  23. package/src/internal_utils/aggregations.spec.js +1 -48
  24. package/src/internal_utils/errors.js +14 -8
  25. package/src/internal_utils/errors.spec.js +73 -27
  26. package/src/utils/index.d.ts +19 -0
  27. package/src/utils/utils.js +46 -0
  28. package/src/utils/utils.spec.js +82 -1
  29. package/src/worker/bundle.js +26 -0
  30. package/src/worker/bundle.spec.js +52 -0
  31. package/src/worker/catalog_workflow/catalog_job.js +148 -0
  32. package/src/worker/catalog_workflow/catalog_job.spec.js +232 -0
  33. package/src/worker/check.js +24 -0
  34. package/src/worker/connection_monitor.js +112 -0
  35. package/src/worker/connection_monitor.spec.js +199 -0
  36. package/src/worker/index.js +140 -34
  37. package/src/worker/index.spec.js +280 -108
  38. package/src/worker/interceptors/activity.js +7 -24
  39. package/src/worker/interceptors/activity.spec.js +97 -66
  40. package/src/worker/interceptors/index.js +4 -7
  41. package/src/worker/interceptors/modules.js +15 -0
  42. package/src/worker/interceptors/workflow.js +4 -7
  43. package/src/worker/interceptors/workflow.spec.js +49 -42
  44. package/src/worker/interruption.js +33 -0
  45. package/src/worker/interruption.spec.js +86 -0
  46. package/src/worker/loader_tools.js +1 -1
  47. package/src/worker/loader_tools.spec.js +36 -0
  48. package/src/worker/{setup_telemetry.js → telemetry.js} +9 -4
  49. package/src/worker/{setup_telemetry.spec.js → telemetry.spec.js} +3 -3
  50. package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.js +5 -109
  51. package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.spec.js +31 -103
  52. package/src/worker/webpack_loaders/workflow_rewriter/index.mjs +5 -6
  53. package/src/worker/webpack_loaders/workflow_rewriter/index.spec.js +11 -83
  54. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.js +8 -11
  55. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.spec.js +9 -9
  56. package/src/interface/validations/runtime.js +0 -20
  57. package/src/interface/validations/runtime.spec.js +0 -29
  58. package/src/interface/validations/schema_utils.js +0 -8
  59. package/src/interface/validations/schema_utils.spec.js +0 -67
  60. package/src/interface/validations/static.js +0 -137
  61. package/src/interface/validations/static.spec.js +0 -397
  62. package/src/interface/workflow.replay_compatibility.spec.js +0 -254
  63. package/src/worker/shutdown.js +0 -26
  64. package/src/worker/shutdown.spec.js +0 -82
  65. package/src/worker/start_catalog.js +0 -96
  66. package/src/worker/start_catalog.spec.js +0 -179
@@ -0,0 +1,182 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { z } from 'zod';
3
+ import { ValidationError } from '#errors';
4
+ import { EvaluationResult } from '../evaluation_result.js';
5
+ import {
6
+ EvaluatorValidator,
7
+ StepValidator,
8
+ WorkflowValidator,
9
+ validateExecuteInParallel,
10
+ validateRequestPayload
11
+ } from './index.js';
12
+
13
+ const fn = async () => {};
14
+
15
+ const workflowArgs = {
16
+ name: 'valid_workflow',
17
+ description: 'Valid workflow',
18
+ inputSchema: z.object( { value: z.string() } ),
19
+ outputSchema: z.object( { result: z.string() } ),
20
+ fn,
21
+ options: {
22
+ activityOptions: {
23
+ startToCloseTimeout: '5m',
24
+ retry: { maximumAttempts: 2 }
25
+ },
26
+ disableTrace: true
27
+ },
28
+ aliases: [ 'old_workflow' ]
29
+ };
30
+
31
+ const stepArgs = {
32
+ name: 'valid_step',
33
+ description: 'Valid step',
34
+ inputSchema: z.object( { value: z.string() } ),
35
+ outputSchema: z.object( { result: z.string() } ),
36
+ fn,
37
+ options: {
38
+ activityOptions: {
39
+ heartbeatTimeout: '30s'
40
+ }
41
+ }
42
+ };
43
+
44
+ const evaluatorArgs = {
45
+ name: 'valid_evaluator',
46
+ description: 'Valid evaluator',
47
+ inputSchema: z.object( { value: z.string() } ),
48
+ fn
49
+ };
50
+
51
+ describe( 'interface validators', () => {
52
+ describe( 'WorkflowValidator', () => {
53
+ it( 'validates workflow definitions, input, output, and invocation options', () => {
54
+ expect( () => WorkflowValidator.validateDefinition( workflowArgs ) ).not.toThrow();
55
+ const validator = new WorkflowValidator( workflowArgs );
56
+
57
+ expect( () => validator.validateInput( { value: 'ok' } ) ).not.toThrow();
58
+ expect( () => validator.validateOutput( { result: 'ok' } ) ).not.toThrow();
59
+ expect( () => validator.validateInvocationOptions( {
60
+ detached: true,
61
+ activityOptions: { retry: { maximumAttempts: 1 } },
62
+ context: { info: { workflowId: 'test-workflow' } }
63
+ } ) ).not.toThrow();
64
+ } );
65
+
66
+ it( 'throws ValidationError with useful prefixes for invalid workflow data', () => {
67
+ expect( () => WorkflowValidator.validateDefinition( { ...workflowArgs, name: 'bad-name' } ) ).toThrow( ValidationError );
68
+ expect( () => WorkflowValidator.validateDefinition( { ...workflowArgs, name: 'bad-name' } ) ).toThrow(
69
+ /Workflow validation failed/
70
+ );
71
+
72
+ const validator = new WorkflowValidator( workflowArgs );
73
+
74
+ expect( () => validator.validateInput( { value: 1 } ) ).toThrow( ValidationError );
75
+ expect( () => validator.validateInput( { value: 1 } ) ).toThrow( /Workflow "valid_workflow" input validation failed/ );
76
+ expect( () => validator.validateOutput( { result: 1 } ) ).toThrow( /Workflow "valid_workflow" output validation failed/ );
77
+ expect( () => validator.validateInvocationOptions( {
78
+ options: { activityOptions: { retry: { maximumAttempts: 1 } } }
79
+ } ) ).toThrow( /Workflow "valid_workflow" invocation options validation failed/ );
80
+ } );
81
+
82
+ it( 'skips input and output validation when schemas are omitted', () => {
83
+ const validator = new WorkflowValidator( {
84
+ name: 'schema_less_workflow',
85
+ fn
86
+ } );
87
+
88
+ expect( () => validator.validateInput( { anything: true } ) ).not.toThrow();
89
+ expect( () => validator.validateOutput( { anything: true } ) ).not.toThrow();
90
+ } );
91
+ } );
92
+
93
+ describe( 'StepValidator', () => {
94
+ it( 'validates step definitions, input, and output', () => {
95
+ expect( () => StepValidator.validateDefinition( stepArgs ) ).not.toThrow();
96
+ const validator = new StepValidator( stepArgs );
97
+
98
+ expect( () => validator.validateInput( { value: 'ok' } ) ).not.toThrow();
99
+ expect( () => validator.validateOutput( { result: 'ok' } ) ).not.toThrow();
100
+ } );
101
+
102
+ it( 'throws ValidationError with useful prefixes for invalid step data', () => {
103
+ expect( () => StepValidator.validateDefinition( { ...stepArgs, fn: 'not-a-function' } ) ).toThrow(
104
+ /Step validation failed/
105
+ );
106
+
107
+ const validator = new StepValidator( stepArgs );
108
+
109
+ expect( () => validator.validateInput( { value: 1 } ) ).toThrow( /Step "valid_step" input validation failed/ );
110
+ expect( () => validator.validateOutput( { result: 1 } ) ).toThrow( /Step "valid_step" output validation failed/ );
111
+ } );
112
+ } );
113
+
114
+ describe( 'EvaluatorValidator', () => {
115
+ it( 'validates evaluator definitions, input, and EvaluationResult output', () => {
116
+ expect( () => EvaluatorValidator.validateDefinition( evaluatorArgs ) ).not.toThrow();
117
+ const validator = new EvaluatorValidator( evaluatorArgs );
118
+
119
+ expect( () => validator.validateInput( { value: 'ok' } ) ).not.toThrow();
120
+ expect( () => validator.validateOutput( new EvaluationResult( { value: 'pass', confidence: 1 } ) ) ).not.toThrow();
121
+ } );
122
+
123
+ it( 'throws ValidationError for output schemas and invalid evaluator output', () => {
124
+ expect( () => EvaluatorValidator.validateDefinition( {
125
+ ...evaluatorArgs,
126
+ outputSchema: z.string()
127
+ } ) ).toThrow( /Evaluator validation failed/ );
128
+
129
+ const validator = new EvaluatorValidator( evaluatorArgs );
130
+
131
+ expect( () => validator.validateInput( { value: 1 } ) ).toThrow( /Evaluator "valid_evaluator" input validation failed/ );
132
+ expect( () => validator.validateOutput( { value: 'pass', confidence: 1 } ) ).toThrow(
133
+ /Evaluator "valid_evaluator" output validation failed/
134
+ );
135
+ } );
136
+ } );
137
+
138
+ describe( 'validateRequestPayload()', () => {
139
+ it( 'accepts valid request payloads', () => {
140
+ expect( () => validateRequestPayload( {
141
+ url: 'https://example.com',
142
+ method: 'POST',
143
+ payload: { ok: true },
144
+ headers: { authorization: 'Bearer token' }
145
+ } ) ).not.toThrow();
146
+ } );
147
+
148
+ it( 'throws ValidationError for invalid request payloads', () => {
149
+ expect( () => validateRequestPayload( {
150
+ url: 'ftp://example.com',
151
+ method: 'POST'
152
+ } ) ).toThrow( /Request payload validation failed/ );
153
+
154
+ expect( () => validateRequestPayload( {
155
+ url: 'https://example.com',
156
+ method: 'OPTIONS'
157
+ } ) ).toThrow( ValidationError );
158
+ } );
159
+ } );
160
+
161
+ describe( 'validateExecuteInParallel()', () => {
162
+ it( 'accepts valid parallel execution configs', () => {
163
+ expect( () => validateExecuteInParallel( {
164
+ jobs: [ () => 'ok' ],
165
+ concurrency: Infinity,
166
+ onJobCompleted: () => {}
167
+ } ) ).not.toThrow();
168
+ } );
169
+
170
+ it( 'throws ValidationError for invalid parallel execution configs', () => {
171
+ expect( () => validateExecuteInParallel( {
172
+ jobs: [ () => 'ok' ],
173
+ concurrency: 0
174
+ } ) ).toThrow( /ExecuteInParallel validation failed/ );
175
+
176
+ expect( () => validateExecuteInParallel( {
177
+ jobs: [ 'not-a-function' ],
178
+ concurrency: 1
179
+ } ) ).toThrow( ValidationError );
180
+ } );
181
+ } );
182
+ } );
@@ -0,0 +1,113 @@
1
+ import * as z from 'zod';
2
+ import { EvaluationResult } from '../evaluation_result.js';
3
+
4
+ /**
5
+ * Detects if a value behaves like a Zod v4 Classic/Mini schema.
6
+ *
7
+ * Zod v4 schemas from different package instances do not share the same
8
+ * prototype, so `instanceof z.ZodType` is too fragile here. The `_zod`
9
+ * property is the documented v4 runtime marker, and `safeParse` is required
10
+ * because the validators call it directly.
11
+ *
12
+ * @param {unknown} schema - The schema to check
13
+ * @returns {boolean} True if the schema is a Zod schema
14
+ */
15
+ export const isZodSchema = schema =>
16
+ Boolean(
17
+ schema &&
18
+ typeof schema === 'object' &&
19
+ typeof schema._zod?.def?.type === 'string' &&
20
+ typeof schema.safeParse === 'function'
21
+ );
22
+
23
+ const refineSchema = ( value, ctx ) => {
24
+ if ( !value || isZodSchema( value ) ) {
25
+ return;
26
+ }
27
+
28
+ ctx.addIssue( {
29
+ code: 'invalid_type',
30
+ message: 'Schema must be a Zod schema'
31
+ } );
32
+ };
33
+
34
+ const durationSchema = z.union( [ z.string().regex(
35
+ /^(\d+)(ms|s|m|h|d)$/,
36
+ 'Expected duration like "500ms", "10s", "5m", "2h", or "1d"'
37
+ ), z.number() ] );
38
+
39
+ const prioritySchema = z.object( {
40
+ fairnessKey: z.string().optional(),
41
+ fairnessWeight: z.number().min( 0.0001 ).max( 1000 ).optional(),
42
+ priorityKey: z.number().min( 1 ).optional()
43
+ } );
44
+
45
+ const activityOptionsSchema = z.strictObject( {
46
+ activityId: z.string().optional(),
47
+ cancellationType: z.enum( [ 'TRY_CANCEL', 'WAIT_CANCELLATION_COMPLETED', 'ABANDON' ] ).optional(),
48
+ heartbeatTimeout: durationSchema.optional(),
49
+ priority: prioritySchema.optional(),
50
+ retry: z.strictObject( {
51
+ initialInterval: durationSchema.optional(),
52
+ backoffCoefficient: z.number().gte( 1 ).optional(),
53
+ maximumInterval: durationSchema.optional(),
54
+ maximumAttempts: z.number().gte( 1 ).int().optional(),
55
+ nonRetryableErrorTypes: z.array( z.string() ).optional()
56
+ } ).optional(),
57
+ scheduleToCloseTimeout: durationSchema.optional(),
58
+ scheduleToStartTimeout: durationSchema.optional(),
59
+ startToCloseTimeout: durationSchema.optional(),
60
+ summary: z.string().optional()
61
+ } );
62
+
63
+ const baseSchema = z.strictObject( {
64
+ name: z.string().regex( /^[a-z_][a-z0-9_]*$/i ),
65
+ description: z.string().optional(),
66
+ inputSchema: z.any().optional().superRefine( refineSchema ),
67
+ outputSchema: z.any().optional().superRefine( refineSchema ),
68
+ fn: z.function(),
69
+ options: z.object( {
70
+ activityOptions: activityOptionsSchema.optional()
71
+ } ).optional()
72
+ } );
73
+
74
+ export const stepSchema = baseSchema;
75
+
76
+ export const workflowSchema = baseSchema.extend( {
77
+ aliases: z.array( z.string().regex( /^[a-z_][a-z0-9_]*$/i ) ).optional().default( [] ),
78
+ options: baseSchema.shape.options.unwrap().extend( {
79
+ disableTrace: z.boolean().optional().default( false )
80
+ } ).optional()
81
+ } );
82
+
83
+ export const evaluatorSchema = baseSchema.omit( { outputSchema: true } );
84
+
85
+ export const evaluatorOutputSchema = z.instanceof( EvaluationResult );
86
+
87
+ export const httpRequestSchema = z.object( {
88
+ url: z.url( { protocol: /^https?$/ } ),
89
+ method: z.enum( [ 'GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE' ] ),
90
+ payload: z.any().optional(),
91
+ headers: z.record( z.string(), z.string() ).optional()
92
+ } );
93
+
94
+ export const workflowInvocationOptionsSchema = z.strictObject( {
95
+ detached: z.boolean().optional(),
96
+ activityOptions: activityOptionsSchema.optional(),
97
+ context: z.object( {
98
+ control: z.object( {
99
+ continueAsNew: z.function().optional(),
100
+ isContinueAsNewSuggested: z.function().optional()
101
+ } ).loose().optional(),
102
+ info: z.object( {
103
+ workflowId: z.string().optional(),
104
+ runId: z.string().optional()
105
+ } ).loose().optional()
106
+ } ).loose().optional()
107
+ } ).optional();
108
+
109
+ export const executeInParallelSchema = z.object( {
110
+ jobs: z.array( z.function() ),
111
+ concurrency: z.number().min( 1 ).or( z.literal( Infinity ) ),
112
+ onJobCompleted: z.function().optional()
113
+ } );
@@ -0,0 +1,209 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { z } from 'zod';
3
+ import { EvaluationResult } from '../evaluation_result.js';
4
+ import {
5
+ evaluatorOutputSchema,
6
+ evaluatorSchema,
7
+ executeInParallelSchema,
8
+ httpRequestSchema,
9
+ isZodSchema,
10
+ stepSchema,
11
+ workflowInvocationOptionsSchema,
12
+ workflowSchema
13
+ } from './schemas.js';
14
+
15
+ const fn = () => {};
16
+ const validBase = {
17
+ name: 'valid_name',
18
+ description: 'Valid description',
19
+ inputSchema: z.object( { value: z.string() } ),
20
+ outputSchema: z.object( { result: z.string() } ),
21
+ fn,
22
+ options: {
23
+ activityOptions: {
24
+ startToCloseTimeout: '5m',
25
+ heartbeatTimeout: 1000,
26
+ retry: {
27
+ initialInterval: '10s',
28
+ backoffCoefficient: 2,
29
+ maximumInterval: '1m',
30
+ maximumAttempts: 3,
31
+ nonRetryableErrorTypes: [ 'FatalError' ]
32
+ },
33
+ priority: {
34
+ fairnessKey: 'tenant',
35
+ fairnessWeight: 1,
36
+ priorityKey: 2
37
+ },
38
+ summary: 'Short summary'
39
+ }
40
+ }
41
+ };
42
+
43
+ describe( 'validation schemas', () => {
44
+ describe( 'isZodSchema()', () => {
45
+ it( 'identifies Zod schemas', () => {
46
+ expect( isZodSchema( z.object( { name: z.string() } ) ) ).toBe( true );
47
+ expect( isZodSchema( z.string() ) ).toBe( true );
48
+ expect( isZodSchema( z.array( z.number() ) ) ).toBe( true );
49
+ expect( isZodSchema( z.union( [ z.string(), z.number() ] ) ) ).toBe( true );
50
+ } );
51
+
52
+ it( 'uses the Zod v4 runtime shape instead of instanceof', () => {
53
+ const schemaLikeFromAnotherPackageInstance = {
54
+ _zod: {
55
+ def: {
56
+ type: 'object'
57
+ }
58
+ },
59
+ safeParse: () => ( { success: true, data: {} } )
60
+ };
61
+
62
+ expect( isZodSchema( schemaLikeFromAnotherPackageInstance ) ).toBe( true );
63
+ } );
64
+
65
+ it( 'rejects non-Zod values', () => {
66
+ expect( isZodSchema( { type: 'object' } ) ).toBe( false );
67
+ expect( isZodSchema( {} ) ).toBe( false );
68
+ expect( isZodSchema( [] ) ).toBe( false );
69
+ expect( isZodSchema( null ) ).toBe( false );
70
+ expect( isZodSchema( undefined ) ).toBe( false );
71
+ expect( isZodSchema( 'string' ) ).toBe( false );
72
+ } );
73
+
74
+ it( 'rejects objects that only partially match the Zod shape', () => {
75
+ expect( isZodSchema( { safeParse: () => ( { success: true } ) } ) ).toBe( false );
76
+ expect( isZodSchema( { _zod: { def: { type: 'string' } } } ) ).toBe( false );
77
+ expect( isZodSchema( { _zod: { def: {} }, safeParse: () => ( { success: true } ) } ) ).toBe( false );
78
+ expect( isZodSchema( { _zod: null, safeParse: () => ( { success: true } ) } ) ).toBe( false );
79
+ } );
80
+ } );
81
+
82
+ describe( 'stepSchema', () => {
83
+ it( 'accepts a valid step definition', () => {
84
+ expect( stepSchema.safeParse( validBase ).success ).toBe( true );
85
+ } );
86
+
87
+ it( 'rejects invalid names, non-Zod schemas, invalid activity options, and unknown top-level keys', () => {
88
+ expect( stepSchema.safeParse( { ...validBase, name: 'invalid-name' } ).success ).toBe( false );
89
+ expect( stepSchema.safeParse( { ...validBase, inputSchema: { type: 'object' } } ).success ).toBe( false );
90
+ expect( stepSchema.safeParse( {
91
+ ...validBase,
92
+ options: { activityOptions: { retry: { maximumAttempts: 0 } } }
93
+ } ).success ).toBe( false );
94
+ expect( stepSchema.safeParse( { ...validBase, unexpected: true } ).success ).toBe( false );
95
+ } );
96
+ } );
97
+
98
+ describe( 'workflowSchema', () => {
99
+ it( 'accepts workflow-specific options and aliases', () => {
100
+ const result = workflowSchema.safeParse( {
101
+ ...validBase,
102
+ aliases: [ 'old_name' ],
103
+ options: {
104
+ ...validBase.options,
105
+ disableTrace: true
106
+ }
107
+ } );
108
+
109
+ expect( result.success ).toBe( true );
110
+ } );
111
+
112
+ it( 'defaults aliases and rejects invalid workflow fields', () => {
113
+ const result = workflowSchema.safeParse( validBase );
114
+ expect( result.success && result.data.aliases ).toEqual( [] );
115
+
116
+ expect( workflowSchema.safeParse( { ...validBase, aliases: [ 'bad-alias' ] } ).success ).toBe( false );
117
+ expect( workflowSchema.safeParse( {
118
+ ...validBase,
119
+ options: { ...validBase.options, disableTrace: 'yes' }
120
+ } ).success ).toBe( false );
121
+ } );
122
+ } );
123
+
124
+ describe( 'evaluatorSchema and evaluatorOutputSchema', () => {
125
+ it( 'accepts evaluator definitions without outputSchema', () => {
126
+ const { outputSchema: _outputSchema, ...validEvaluator } = validBase;
127
+ expect( evaluatorSchema.safeParse( validEvaluator ).success ).toBe( true );
128
+ } );
129
+
130
+ it( 'rejects evaluator definitions with outputSchema', () => {
131
+ expect( evaluatorSchema.safeParse( validBase ).success ).toBe( false );
132
+ } );
133
+
134
+ it( 'accepts only EvaluationResult instances as evaluator output', () => {
135
+ expect( evaluatorOutputSchema.safeParse( new EvaluationResult( { value: 'ok', confidence: 1 } ) ).success ).toBe( true );
136
+ expect( evaluatorOutputSchema.safeParse( { value: 'ok', confidence: 1 } ).success ).toBe( false );
137
+ } );
138
+ } );
139
+
140
+ describe( 'httpRequestSchema', () => {
141
+ it( 'accepts valid HTTP request payloads', () => {
142
+ expect( httpRequestSchema.safeParse( {
143
+ url: 'https://example.com',
144
+ method: 'POST',
145
+ payload: { ok: true },
146
+ headers: { authorization: 'Bearer token' }
147
+ } ).success ).toBe( true );
148
+ } );
149
+
150
+ it( 'rejects invalid URL protocols, methods, and header values', () => {
151
+ expect( httpRequestSchema.safeParse( { url: 'ftp://example.com', method: 'GET' } ).success ).toBe( false );
152
+ expect( httpRequestSchema.safeParse( { url: 'https://example.com', method: 'OPTIONS' } ).success ).toBe( false );
153
+ expect( httpRequestSchema.safeParse( {
154
+ url: 'https://example.com',
155
+ method: 'GET',
156
+ headers: { count: 1 }
157
+ } ).success ).toBe( false );
158
+ } );
159
+ } );
160
+
161
+ describe( 'workflowInvocationOptionsSchema', () => {
162
+ it( 'accepts omitted options and valid invocation configuration', () => {
163
+ expect( workflowInvocationOptionsSchema.safeParse( undefined ).success ).toBe( true );
164
+ expect( workflowInvocationOptionsSchema.safeParse( {
165
+ detached: true,
166
+ activityOptions: { retry: { maximumAttempts: 1 } },
167
+ context: {
168
+ control: {
169
+ continueAsNew: fn,
170
+ isContinueAsNewSuggested: fn,
171
+ extraControl: true
172
+ },
173
+ info: {
174
+ workflowId: 'wf',
175
+ runId: 'run',
176
+ extraInfo: true
177
+ },
178
+ extraContext: true
179
+ }
180
+ } ).success ).toBe( true );
181
+ } );
182
+
183
+ it( 'rejects stale option shapes and invalid invocation values', () => {
184
+ expect( workflowInvocationOptionsSchema.safeParse( {
185
+ options: { activityOptions: { retry: { maximumAttempts: 1 } } }
186
+ } ).success ).toBe( false );
187
+ expect( workflowInvocationOptionsSchema.safeParse( { detached: 'true' } ).success ).toBe( false );
188
+ expect( workflowInvocationOptionsSchema.safeParse( {
189
+ activityOptions: { retry: { maximumAttempts: 0 } }
190
+ } ).success ).toBe( false );
191
+ expect( workflowInvocationOptionsSchema.safeParse( {
192
+ context: { control: { continueAsNew: 'nope' } }
193
+ } ).success ).toBe( false );
194
+ } );
195
+ } );
196
+
197
+ describe( 'executeInParallelSchema', () => {
198
+ it( 'accepts valid execution configs', () => {
199
+ expect( executeInParallelSchema.safeParse( { jobs: [ fn ], concurrency: 1 } ).success ).toBe( true );
200
+ expect( executeInParallelSchema.safeParse( { jobs: [ fn ], concurrency: Infinity, onJobCompleted: fn } ).success ).toBe( true );
201
+ } );
202
+
203
+ it( 'rejects invalid execution configs', () => {
204
+ expect( executeInParallelSchema.safeParse( { jobs: [], concurrency: 0 } ).success ).toBe( false );
205
+ expect( executeInParallelSchema.safeParse( { jobs: [ 'not-a-function' ], concurrency: 1 } ).success ).toBe( false );
206
+ expect( executeInParallelSchema.safeParse( { jobs: [ fn ], concurrency: 1, onJobCompleted: 'nope' } ).success ).toBe( false );
207
+ } );
208
+ } );
209
+ } );
@@ -2,7 +2,7 @@
2
2
  import { defineSignal, setHandler, proxyActivities, workflowInfo, proxySinks, uuid4, Trigger } from '@temporalio/workflow';
3
3
  import { ACTIVITY_SEND_HTTP_REQUEST } from '#consts';
4
4
  import { FatalError } from '#errors';
5
- import { validateRequestPayload } from './validations/static.js';
5
+ import { validateRequestPayload } from './validations/index.js';
6
6
 
7
7
  /**
8
8
  * Call the internal activity to make a HTTP request and returns its response.
@@ -6,7 +6,7 @@ vi.mock( '#consts', () => ( {
6
6
  } ) );
7
7
 
8
8
  const validateRequestPayloadMock = vi.fn();
9
- vi.mock( './validations/static.js', () => ( {
9
+ vi.mock( './validations/index.js', () => ( {
10
10
  validateRequestPayload: validateRequestPayloadMock
11
11
  } ) );
12
12
 
@@ -79,20 +79,21 @@ export type WorkflowContext<
79
79
  };
80
80
 
81
81
  /**
82
- * Configuration for workflow invocations.
82
+ * Options for workflow invocations.
83
83
  *
84
84
  * Allows overriding Temporal Activity options for this workflow.
85
85
  */
86
- export type WorkflowInvocationConfiguration<Context extends WorkflowContext = WorkflowContext> = {
86
+ export type WorkflowInvocationOptions<Context extends WorkflowContext = WorkflowContext> = {
87
87
 
88
88
  /**
89
89
  * Temporal activity options for this invocation (overrides the workflow's default activity options).
90
90
  */
91
- options?: TemporalActivityOptions,
91
+ activityOptions?: TemporalActivityOptions,
92
92
 
93
93
  /**
94
- * Configures whether this workflow runs detached.
95
- * Detached workflows called without explicitly awaiting the result are "fire-and-forget" and may outlive the parent.
94
+ * Configures whether this workflow runs detached:
95
+ * - `detached=true` maps to `ParentClosePolicy.ABANDON`: if parent closes before child, the child keeps executing.
96
+ * - `detached=false` maps to `ParentClosePolicy.TERMINATE`: if parent closes before child, the child is terminated.
96
97
  */
97
98
  detached?: boolean,
98
99
 
@@ -140,19 +141,19 @@ export type WorkflowFunction<
140
141
  *
141
142
  * It accepts the same input and returns the same value, calling the user function inside.
142
143
  *
143
- * The second argument is a WorkflowInvocationConfiguration object, allowing workflows configuration overwrite.
144
+ * The second argument is a WorkflowInvocationOptions object, allowing workflows configuration overwrite.
144
145
  *
145
146
  * It adds input and output validation based on the `inputSchema`, `outputSchema`.
146
147
  *
147
148
  * @param input - The workflow input; it matches the schema defined by `inputSchema`.
148
- * @param config - Additional configuration for the invocation.
149
+ * @param options - Additional options for the invocation.
149
150
  * @returns A value matching the schema defined by `outputSchema`.
150
151
  */
151
152
  export type WorkflowFunctionWrapper<WorkflowFunction> =
152
153
  [Parameters<WorkflowFunction>[0]] extends [undefined | null] ?
153
- ( input?: undefined | null, config?: WorkflowInvocationConfiguration ) =>
154
+ ( input?: undefined | null, options?: WorkflowInvocationOptions ) =>
154
155
  ReturnType<WorkflowFunction> :
155
- ( input: Parameters<WorkflowFunction>[0], config?: WorkflowInvocationConfiguration ) =>
156
+ ( input: Parameters<WorkflowFunction>[0], options?: WorkflowInvocationOptions ) =>
156
157
  ReturnType<WorkflowFunction>;
157
158
 
158
159
  /**