@output.ai/core 0.1.11 → 0.1.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@output.ai/core",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "The core module of the output framework",
5
5
  "type": "module",
6
6
  "exports": {
package/src/index.d.ts CHANGED
@@ -21,18 +21,28 @@ export { z } from 'zod';
21
21
  type AnyZodSchema = z.ZodType<any, any, any>;
22
22
 
23
23
  /**
24
- * Activity retry options accepted by step/evaluator/workflow definitions.
25
- * Uses Temporal's ActivityOptions['retry'] type.
24
+ * Activity options accepted by step/evaluator/workflow definitions.
25
+ * Exposes supported Temporal ActivityOptions.
26
+ *
27
+ * @description Exposes supported Temporal ActivityOptions
26
28
  */
27
- export type Options = { retry?: ActivityOptions['retry'] };
29
+ export type Options = Omit<ActivityOptions, 'versioningIntent' | 'taskQueue' | 'allowEagerDispatch'>;
28
30
 
29
31
  /**
30
- * @typedef {object} Options
31
- * @property {import('@temporalio/workflow').ActivityOptions['retry']} [retry]
32
+ * The last argument when calling workflows is used to configure it
32
33
  */
33
-
34
34
  export type WorkflowCallConfig = {
35
- options?: ActivityOptions
35
+
36
+ /**
37
+ * Temporal Activity Options
38
+ */
39
+ options?: Options,
40
+
41
+ /**
42
+ * Whether this workflow will run detached or not.
43
+ * Detached workflows called without explicitly awaiting for the result will be "fire-an-forget", outliving the parent.
44
+ */
45
+ detached?: boolean
36
46
  };
37
47
 
38
48
  /*
@@ -17,10 +17,16 @@ const refineSchema = ( value, ctx ) => {
17
17
  } );
18
18
  };
19
19
 
20
- export const durationStringSchema = z.string().regex(
20
+ export const durationSchema = z.union( [ z.string().regex(
21
21
  /^(\d+)(ms|s|m|h|d)$/,
22
22
  'Expected duration like "500ms", "10s", "5m", "2h", or "1d"'
23
- );
23
+ ), z.number() ] );
24
+
25
+ export const prioritySchema = z.object( {
26
+ fairnessKey: z.string().optional(),
27
+ fairnessWeight: z.number().min( 0.0001 ).max( 1000 ).optional(),
28
+ priorityKey: z.number().min( 1 ).optional()
29
+ } );
24
30
 
25
31
  const stepAndWorkflowSchema = z.strictObject( {
26
32
  name: z.string().regex( /^[a-z_][a-z0-9_]*$/i ),
@@ -29,13 +35,21 @@ const stepAndWorkflowSchema = z.strictObject( {
29
35
  outputSchema: z.any().optional().superRefine( refineSchema ),
30
36
  fn: z.function(),
31
37
  options: z.strictObject( {
38
+ activityId: z.string().optional(),
39
+ cancellationType: z.enum( [ 'TRY_CANCEL', 'WAIT_CANCELLATION_COMPLETED', 'ABANDON' ] ).optional(),
40
+ heartbeatTimeout: durationSchema.optional(),
41
+ priority: prioritySchema.optional(),
32
42
  retry: z.strictObject( {
33
- initialInterval: durationStringSchema.optional(),
43
+ initialInterval: durationSchema.optional(),
34
44
  backoffCoefficient: z.number().gte( 1 ).optional(),
35
- maximumInterval: durationStringSchema.optional(),
45
+ maximumInterval: durationSchema.optional(),
36
46
  maximumAttempts: z.number().gte( 1 ).int().optional(),
37
47
  nonRetryableErrorTypes: z.array( z.string() ).optional()
38
- } ).optional()
48
+ } ).optional(),
49
+ scheduleToCloseTimeout: durationSchema.optional(),
50
+ scheduleToStartTimeout: durationSchema.optional(),
51
+ startToCloseTimeout: durationSchema.optional(),
52
+ summary: z.string().optional()
39
53
  } ).optional()
40
54
  } );
41
55
 
@@ -82,6 +82,71 @@ describe( 'interface/validator', () => {
82
82
  expect( () => validateStep( args ) ).not.toThrow();
83
83
  } );
84
84
 
85
+ it( 'passes with options.activityId as string', () => {
86
+ expect( () => validateStep( { ...validArgs, options: { activityId: 'act-123' } } ) ).not.toThrow();
87
+ } );
88
+
89
+ it( 'rejects non-string options.activityId', () => {
90
+ expect( () => validateStep( { ...validArgs, options: { activityId: 123 } } ) ).toThrow( StaticValidationError );
91
+ } );
92
+
93
+ it( 'passes with valid options.cancellationType values', () => {
94
+ for ( const v of [ 'TRY_CANCEL', 'WAIT_CANCELLATION_COMPLETED', 'ABANDON' ] ) {
95
+ expect( () => validateStep( { ...validArgs, options: { cancellationType: v } } ) ).not.toThrow();
96
+ }
97
+ } );
98
+
99
+ it( 'rejects invalid options.cancellationType', () => {
100
+ expect( () => validateStep( { ...validArgs, options: { cancellationType: 'INVALID' } } ) ).toThrow( StaticValidationError );
101
+ } );
102
+
103
+ it( 'accepts duration fields in options', () => {
104
+ const options = {
105
+ heartbeatTimeout: '1s',
106
+ scheduleToCloseTimeout: '2m',
107
+ scheduleToStartTimeout: '3m',
108
+ startToCloseTimeout: '4m'
109
+ };
110
+ expect( () => validateStep( { ...validArgs, options } ) ).not.toThrow();
111
+ } );
112
+
113
+ it( 'rejects invalid duration string in heartbeatTimeout', () => {
114
+ expect( () => validateStep( { ...validArgs, options: { heartbeatTimeout: '5x' } } ) ).toThrow( StaticValidationError );
115
+ } );
116
+
117
+ it( 'passes with options.summary string', () => {
118
+ expect( () => validateStep( { ...validArgs, options: { summary: 'brief' } } ) ).not.toThrow();
119
+ } );
120
+
121
+ it( 'rejects non-string options.summary', () => {
122
+ expect( () => validateStep( { ...validArgs, options: { summary: 42 } } ) ).toThrow( StaticValidationError );
123
+ } );
124
+
125
+ it( 'passes with options.priority valid payload', () => {
126
+ const options = {
127
+ priority: {
128
+ fairnessKey: 'user-1',
129
+ fairnessWeight: 1.5,
130
+ priorityKey: 10
131
+ }
132
+ };
133
+ expect( () => validateStep( { ...validArgs, options } ) ).not.toThrow();
134
+ } );
135
+
136
+ it( 'rejects invalid options.priority values', () => {
137
+ const options = { priority: { fairnessWeight: 0, priorityKey: 0 } };
138
+ expect( () => validateStep( { ...validArgs, options } ) ).toThrow( StaticValidationError );
139
+ } );
140
+
141
+ it( 'rejects invalid options.retry values', () => {
142
+ const options = { retry: { backoffCoefficient: 0.5, maximumAttempts: 0, nonRetryableErrorTypes: [ 1 ] } };
143
+ expect( () => validateStep( { ...validArgs, options } ) ).toThrow( StaticValidationError );
144
+ } );
145
+
146
+ it( 'rejects unknown keys inside options due to strictObject', () => {
147
+ expect( () => validateStep( { ...validArgs, options: { unknownKey: true } } ) ).toThrow( StaticValidationError );
148
+ } );
149
+
85
150
  it( 'rejects unknown top-level keys due to strictObject', () => {
86
151
  expect( () => validateStep( { ...validArgs, extra: 123 } ) ).toThrow( StaticValidationError );
87
152
  } );
@@ -1,5 +1,5 @@
1
1
  // THIS RUNS IN THE TEMPORAL'S SANDBOX ENVIRONMENT
2
- import { proxyActivities, inWorkflowContext, executeChild, workflowInfo } from '@temporalio/workflow';
2
+ import { proxyActivities, inWorkflowContext, executeChild, workflowInfo, ParentClosePolicy } from '@temporalio/workflow';
3
3
  import { validateWorkflow } from './validations/static.js';
4
4
  import { validateWithSchema } from './validations/runtime.js';
5
5
  import { SHARED_STEP_PREFIX, ACTIVITY_GET_TRACE_DESTINATIONS } from '#consts';
@@ -59,18 +59,28 @@ export function workflow( { name, description, inputSchema, outputSchema, fn, op
59
59
  invokeSharedStep: async ( stepName, input, options ) => steps[`${SHARED_STEP_PREFIX}#${stepName}`]( input, options ),
60
60
  invokeEvaluator: async ( evaluatorName, input, options ) => steps[`${workflowPath}#${evaluatorName}`]( input, options ),
61
61
 
62
- startWorkflow: async ( childName, input, extra = {} ) => {
63
- return executeChild( childName, {
62
+ /**
63
+ * Start a child workflow
64
+ *
65
+ * @param {string} childName
66
+ * @param {unknown} input
67
+ * @param {object} extra
68
+ * @param {boolean} extra.detached
69
+ * @param {import('@temporalio/workflow').ActivityOptions} extra.options
70
+ * @returns
71
+ */
72
+ startWorkflow: async ( childName, input, extra = {} ) =>
73
+ executeChild( childName, {
64
74
  args: input ? [ input ] : [],
65
75
  workflowId: `${workflowId}-${childName}-${Date.now()}`,
76
+ parentClosePolicy: ParentClosePolicy[extra?.detached ? 'ABANDON' : 'TERMINATE'],
66
77
  memo: {
67
78
  executionContext,
68
79
  parentId: workflowId,
69
80
  // new configuration for activities of the child workflow, this will be omitted so it will use what that workflow have defined
70
81
  ...( extra?.options && { activityOptions: mergeActivityOptions( activityOptions, extra.options ) } )
71
82
  }
72
- } );
73
- }
83
+ } )
74
84
  }, input );
75
85
 
76
86
  validateWithSchema( outputSchema, output, `Workflow ${name} output` );