@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.
- package/bin/worker.sh +6 -0
- package/package.json +1 -1
- package/src/consts.js +0 -4
- package/src/errors.js +6 -2
- package/src/hooks/index.d.ts +10 -0
- package/src/interface/evaluator.js +7 -20
- package/src/interface/evaluator.spec.js +117 -1
- package/src/interface/step.js +8 -9
- package/src/interface/step.spec.js +124 -0
- package/src/interface/validations/index.js +108 -0
- package/src/interface/validations/index.spec.js +182 -0
- package/src/interface/validations/schemas.js +113 -0
- package/src/interface/validations/schemas.spec.js +209 -0
- package/src/interface/webhook.js +1 -1
- package/src/interface/webhook.spec.js +1 -1
- package/src/interface/workflow.d.ts +10 -9
- package/src/interface/workflow.js +76 -164
- package/src/interface/workflow.spec.js +637 -521
- package/src/interface/workflow_activity_options.js +16 -0
- package/src/interface/workflow_utils.js +1 -1
- package/src/interface/zod_integration.spec.js +2 -2
- package/src/internal_utils/aggregations.js +0 -10
- package/src/internal_utils/aggregations.spec.js +1 -48
- package/src/internal_utils/errors.js +14 -8
- package/src/internal_utils/errors.spec.js +73 -27
- package/src/utils/index.d.ts +19 -0
- package/src/utils/utils.js +46 -0
- package/src/utils/utils.spec.js +82 -1
- package/src/worker/bundle.js +26 -0
- package/src/worker/bundle.spec.js +52 -0
- package/src/worker/catalog_workflow/catalog_job.js +148 -0
- package/src/worker/catalog_workflow/catalog_job.spec.js +232 -0
- package/src/worker/check.js +24 -0
- package/src/worker/connection_monitor.js +112 -0
- package/src/worker/connection_monitor.spec.js +199 -0
- package/src/worker/index.js +140 -34
- package/src/worker/index.spec.js +280 -108
- package/src/worker/interceptors/activity.js +7 -24
- package/src/worker/interceptors/activity.spec.js +97 -66
- package/src/worker/interceptors/index.js +4 -7
- package/src/worker/interceptors/modules.js +15 -0
- package/src/worker/interceptors/workflow.js +4 -7
- package/src/worker/interceptors/workflow.spec.js +49 -42
- package/src/worker/interruption.js +33 -0
- package/src/worker/interruption.spec.js +86 -0
- package/src/worker/loader_tools.js +1 -1
- package/src/worker/loader_tools.spec.js +36 -0
- package/src/worker/{setup_telemetry.js → telemetry.js} +9 -4
- package/src/worker/{setup_telemetry.spec.js → telemetry.spec.js} +3 -3
- package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.js +5 -109
- package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.spec.js +31 -103
- package/src/worker/webpack_loaders/workflow_rewriter/index.mjs +5 -6
- package/src/worker/webpack_loaders/workflow_rewriter/index.spec.js +11 -83
- package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.js +8 -11
- package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.spec.js +9 -9
- package/src/interface/validations/runtime.js +0 -20
- package/src/interface/validations/runtime.spec.js +0 -29
- package/src/interface/validations/schema_utils.js +0 -8
- package/src/interface/validations/schema_utils.spec.js +0 -67
- package/src/interface/validations/static.js +0 -137
- package/src/interface/validations/static.spec.js +0 -397
- package/src/interface/workflow.replay_compatibility.spec.js +0 -254
- package/src/worker/shutdown.js +0 -26
- package/src/worker/shutdown.spec.js +0 -82
- package/src/worker/start_catalog.js +0 -96
- 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
|
+
} );
|
package/src/interface/webhook.js
CHANGED
|
@@ -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/
|
|
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.
|
|
@@ -79,20 +79,21 @@ export type WorkflowContext<
|
|
|
79
79
|
};
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
|
-
*
|
|
82
|
+
* Options for workflow invocations.
|
|
83
83
|
*
|
|
84
84
|
* Allows overriding Temporal Activity options for this workflow.
|
|
85
85
|
*/
|
|
86
|
-
export type
|
|
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
|
-
|
|
91
|
+
activityOptions?: TemporalActivityOptions,
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
|
-
* Configures whether this workflow runs detached
|
|
95
|
-
*
|
|
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
|
|
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
|
|
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,
|
|
154
|
+
( input?: undefined | null, options?: WorkflowInvocationOptions ) =>
|
|
154
155
|
ReturnType<WorkflowFunction> :
|
|
155
|
-
( input: Parameters<WorkflowFunction>[0],
|
|
156
|
+
( input: Parameters<WorkflowFunction>[0], options?: WorkflowInvocationOptions ) =>
|
|
156
157
|
ReturnType<WorkflowFunction>;
|
|
157
158
|
|
|
158
159
|
/**
|