@outputai/core 0.7.1-next.ae5bab4.0 → 0.7.1-next.ba2fb0b.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 (78) 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 +53 -0
  28. package/src/utils/utils.spec.js +105 -1
  29. package/src/worker/bundle.js +26 -0
  30. package/src/worker/bundle.spec.js +53 -0
  31. package/src/worker/bundler_options.js +1 -1
  32. package/src/worker/bundler_options.spec.js +1 -1
  33. package/src/worker/catalog_workflow/catalog_job.js +148 -0
  34. package/src/worker/catalog_workflow/catalog_job.spec.js +232 -0
  35. package/src/worker/check.js +24 -0
  36. package/src/worker/connection_monitor.js +112 -0
  37. package/src/worker/connection_monitor.spec.js +199 -0
  38. package/src/worker/index.js +146 -41
  39. package/src/worker/index.spec.js +281 -109
  40. package/src/worker/interceptors/activity.js +7 -24
  41. package/src/worker/interceptors/activity.spec.js +97 -66
  42. package/src/worker/interceptors/index.js +4 -7
  43. package/src/worker/interceptors/modules.js +15 -0
  44. package/src/worker/interceptors/workflow.js +6 -8
  45. package/src/worker/interceptors/workflow.spec.js +49 -42
  46. package/src/worker/interruption.js +33 -0
  47. package/src/worker/interruption.spec.js +98 -0
  48. package/src/worker/loader/activities.js +75 -0
  49. package/src/worker/loader/activities.spec.js +213 -0
  50. package/src/worker/loader/hooks.js +28 -0
  51. package/src/worker/loader/hooks.spec.js +64 -0
  52. package/src/worker/loader/matchers.js +46 -0
  53. package/src/worker/loader/matchers.spec.js +140 -0
  54. package/src/worker/{loader_tools.js → loader/tools.js} +19 -67
  55. package/src/worker/{loader_tools.spec.js → loader/tools.spec.js} +53 -85
  56. package/src/worker/loader/workflows.js +82 -0
  57. package/src/worker/loader/workflows.spec.js +256 -0
  58. package/src/worker/{setup_telemetry.js → telemetry.js} +9 -4
  59. package/src/worker/{setup_telemetry.spec.js → telemetry.spec.js} +3 -3
  60. package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.js +5 -109
  61. package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.spec.js +31 -103
  62. package/src/worker/webpack_loaders/workflow_rewriter/index.mjs +5 -6
  63. package/src/worker/webpack_loaders/workflow_rewriter/index.spec.js +11 -83
  64. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.js +8 -11
  65. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.spec.js +9 -9
  66. package/src/interface/validations/runtime.js +0 -20
  67. package/src/interface/validations/runtime.spec.js +0 -29
  68. package/src/interface/validations/schema_utils.js +0 -8
  69. package/src/interface/validations/schema_utils.spec.js +0 -67
  70. package/src/interface/validations/static.js +0 -137
  71. package/src/interface/validations/static.spec.js +0 -397
  72. package/src/interface/workflow.replay_compatibility.spec.js +0 -254
  73. package/src/worker/loader.js +0 -202
  74. package/src/worker/loader.spec.js +0 -498
  75. package/src/worker/shutdown.js +0 -26
  76. package/src/worker/shutdown.spec.js +0 -82
  77. package/src/worker/start_catalog.js +0 -96
  78. package/src/worker/start_catalog.spec.js +0 -179
@@ -1,202 +1,114 @@
1
1
  // THIS RUNS IN THE TEMPORAL'S SANDBOX ENVIRONMENT
2
2
  import { proxyActivities, inWorkflowContext, executeChild, workflowInfo, uuid4, ParentClosePolicy } from '@temporalio/workflow';
3
- import { defineSignal, setHandler } from '@temporalio/workflow';
4
- import { validateWorkflow } from './validations/static.js';
5
- import { validateWithSchema } from './validations/runtime.js';
3
+ import { WorkflowValidator } from './validations/index.js';
6
4
  import { deepMerge, setMetadata, toUrlSafeBase64 } from '#utils';
7
- import { FatalError, ValidationError } from '#errors';
8
5
  import { WorkflowContext } from '#internal_utils/workflow_context';
9
- import { aggregateAttributes, mergeAggregations } from '#internal_utils/aggregations';
10
- import { extractErrorDetail } from '#internal_utils/errors';
11
6
  import { TraceInfo } from '#internal_utils/trace_info';
7
+ import { defaultOptions } from './workflow_activity_options.js';
12
8
  import {
13
- ACTIVITY_GET_TRACE_DESTINATIONS,
14
9
  ACTIVITY_WRAPPER_VERSION_FIELD,
10
+ ACTIVITY_GET_TRACE_DESTINATIONS,
15
11
  METADATA_ACCESS_SYMBOL,
16
12
  SHARED_STEP_PREFIX,
17
- Signal,
18
13
  WORKFLOW_WRAPPER_VERSION_FIELD
19
14
  } from '#consts';
20
15
 
21
- const defaultOptions = {
22
- activityOptions: {
23
- startToCloseTimeout: '20m',
24
- heartbeatTimeout: '5m',
25
- retry: {
26
- initialInterval: '10s',
27
- backoffCoefficient: 2.0,
28
- maximumInterval: '2m',
29
- maximumAttempts: 3,
30
- nonRetryableErrorTypes: [ ValidationError.name, FatalError.name ]
31
- }
32
- },
33
- disableTrace: false
34
- };
16
+ /**
17
+ * @temp
18
+ * This is to keep backwards compatibility [OUT-468]
19
+ */
20
+ const parseActivityOutput = p => Object.hasOwn( p ?? {}, ACTIVITY_WRAPPER_VERSION_FIELD ) ? p.output : p;
35
21
 
36
22
  /**
37
- * Checks if the activity result uses the internal wrapper
23
+ * @temp This is a TEMP fallback method to allow workflow child checks on replays without memo. [OUT-468]
24
+ * This workflows for most scenarios, only does not supports recursion with the same name.
38
25
  */
39
- const isActivityResultWrapped = result => result?.[ACTIVITY_WRAPPER_VERSION_FIELD] > 0;
26
+ const checkChildFallback = ( { workflowType, name, aliases } ) => workflowType !== name && !aliases.includes( workflowType );
40
27
 
28
+ /**
29
+ * Create a new workflow and return a wrapper function around its fn handler
30
+ */
41
31
  export function workflow( { name, description, inputSchema, outputSchema, fn, options = {}, aliases = [] } ) {
42
- validateWorkflow( { name, description, inputSchema, outputSchema, fn, options, aliases } );
32
+ WorkflowValidator.validateDefinition( { name, description, inputSchema, outputSchema, fn, options, aliases } );
43
33
 
44
34
  const { disableTrace, activityOptions } = deepMerge( defaultOptions, options );
45
- const steps = proxyActivities( activityOptions );
46
-
47
- /**
48
- * Wraps the `fn` function of the workflow
49
- *
50
- * @param {unknown} input - The input, must match the inputSchema
51
- * @param {object} extra - Workflow configurations (received directly only in unit tests)
52
- * @returns {unknown} The result, will match the outputSchema
53
- */
35
+ const validator = new WorkflowValidator( { name, inputSchema, outputSchema } );
36
+
54
37
  const wrapper = async ( input, extra = {} ) => {
38
+ validator.validateInvocationOptions( extra );
39
+
55
40
  // this returns a plain function, for example, in unit tests
56
41
  if ( !inWorkflowContext() ) {
57
- validateWithSchema( inputSchema, input, `Workflow ${name} input` );
58
- const output = await fn( input, deepMerge( WorkflowContext.build(), extra.context ) );
59
- validateWithSchema( outputSchema, output, `Workflow ${name} output` );
42
+ validator.validateInput( input );
43
+ const output = await fn( input, deepMerge( WorkflowContext.build(), extra?.context ) );
44
+ validator.validateOutput( output );
60
45
  return output;
61
46
  }
62
47
 
63
- const { workflowId, memo, root } = workflowInfo();
64
- const context = WorkflowContext.build();
48
+ const { workflowId, workflowType, memo, root } = workflowInfo();
49
+
50
+ // if the stack already includes this workflowId, means the workflow() function was called
51
+ // from within a running workflow, meaning it is suppose to start a child workflow
52
+ const isChild = Array.isArray( memo.stack ) ? memo.stack.includes( workflowId ) :
53
+ checkChildFallback( { workflowType, aliases, name } );
54
+
55
+ if ( isChild ) {
56
+ const result = await executeChild( name, {
57
+ args: undefined === input ? [] : [ input ],
58
+ workflowId: `${workflowId}-${toUrlSafeBase64( uuid4() )}`,
59
+ parentClosePolicy: ParentClosePolicy[extra?.detached ? 'ABANDON' : 'TERMINATE'],
60
+ memo: {
61
+ ...memo, // Preserve memo and mix activityOptions, if provided
62
+ ...( extra?.activityOptions && {
63
+ activityOptions: deepMerge( memo?.activityOptions ?? {}, extra?.activityOptions )
64
+ } )
65
+ }
66
+ } );
67
+ return result.output;
68
+ }
65
69
 
66
70
  const isRoot = !root;
67
71
 
68
- // Creates the immutable memo that will be used by all nested workflows/activities
69
- // Preserves all info that already exists in the memo object, so new child workflows inherit this
70
- Object.assign( memo, {
71
- traceInfo: memo.traceInfo ?? TraceInfo.build( { disableTrace } ),
72
- activityOptions: memo.activityOptions ?? activityOptions
73
- } );
74
-
75
- /**
76
- * Run the internal activity to retrieve the workflow trace destinations
77
- * This only happens at the root workflow because nested share the same trace file
78
- * @IMPORTANT Keep support for deprecated non-wrapped activity result to allow for Temporal replays.
79
- * @TODO [OUT-468]
80
- */
81
- const getTraceDestinations = async () => {
82
- const result = await steps[ACTIVITY_GET_TRACE_DESTINATIONS]( memo.traceInfo );
83
- return isActivityResultWrapped( result ) ? result.output : result;
84
- };
85
-
86
- // Creates the result wrapper with information about the workflow
87
- const workflowResult = {
88
- [WORKFLOW_WRAPPER_VERSION_FIELD]: 1,
89
- aggregations: null,
90
- ...( isRoot && {
91
- trace: {
92
- destinations: await getTraceDestinations()
93
- }
94
- } )
95
- };
96
-
97
- // Combine aggregations in the workflow result aggregations, mutating it
98
- const mergeAggregationsInWorkflowResult = aggregations => {
99
- workflowResult.aggregations = mergeAggregations( workflowResult.aggregations, aggregations );
100
- };
101
-
102
- setHandler( defineSignal( Signal.SEND_AGGREGATIONS ), aggregations => {
103
- mergeAggregationsInWorkflowResult( aggregations );
104
- } );
105
-
106
- /**
107
- * @IMPORTANT Keep support for deprecated add_attribute Signal to allow for Temporal replays.
108
- * @TODO This can be removed 30days after this release
109
- */
110
- setHandler( defineSignal( 'add_attribute' ), attribute => {
111
- mergeAggregationsInWorkflowResult( aggregateAttributes( [ attribute ] ) );
112
- } );
113
-
114
- /**
115
- * Invoke a step and unwraps the result to extract and merge "aggregations" and return only the output.
116
- *
117
- * @IMPORTANT Keep support for deprecated non-wrapped activity result to allow for Temporal replays.
118
- * @TODO [OUT-468]
119
- * @param {Function} step
120
- * @param {...any} args
121
- * @returns {any} The step "output"
122
- */
123
- const callStepAndUnwrapResult = async ( step, ...args ) => {
124
- const result = await step( ...args );
125
- if ( !isActivityResultWrapped( result ) ) {
126
- return result;
127
- }
128
- const { output, aggregations } = result;
129
- if ( aggregations ) {
130
- mergeAggregationsInWorkflowResult( aggregations );
131
- }
132
- return output;
133
- };
72
+ memo.stack = [ ...memo.stack ?? [], workflowId ];
73
+ // Parent options have prevalence on nested calls, child will be overwritten
74
+ memo.activityOptions = deepMerge( activityOptions, memo.activityOptions );
75
+ // Trace info is only added in the root workflow
76
+ if ( isRoot ) {
77
+ memo.traceInfo = TraceInfo.build( { disableTrace } );
78
+ }
79
+
80
+ const steps = proxyActivities( memo.activityOptions );
81
+ const traceDest = isRoot && parseActivityOutput( await steps[ACTIVITY_GET_TRACE_DESTINATIONS]( memo.traceInfo ) );
134
82
 
135
83
  try {
136
- // validation comes after setting memo to have that info already set for interceptor even if validations fail
137
- validateWithSchema( inputSchema, input, `Workflow ${name} input` );
84
+ validator.validateInput( input );
138
85
 
86
+ // Creates an activity caller based on a prefix
87
+ const createCaller = prefix => async ( t, ...args ) => parseActivityOutput( await steps[`${prefix}#${t}`]( ...args ) );
88
+
89
+ // This are functions used by the AST to replace direct activity (step/evaluator) calls
139
90
  const dispatchers = {
140
- /* This are shortcuts to invoke activities as steps/evaluators both shared and non shared */
141
- invokeStep: async ( stepName, input, options ) =>
142
- callStepAndUnwrapResult( steps[`${name}#${stepName}`], input, options ),
143
- invokeSharedStep: async ( stepName, input, options ) =>
144
- callStepAndUnwrapResult( steps[`${SHARED_STEP_PREFIX}#${stepName}`], input, options ),
145
- invokeEvaluator: async ( evaluatorName, input, options ) =>
146
- callStepAndUnwrapResult( steps[`${name}#${evaluatorName}`], input, options ),
147
- invokeSharedEvaluator: async ( evaluatorName, input, options ) =>
148
- callStepAndUnwrapResult( steps[`${SHARED_STEP_PREFIX}#${evaluatorName}`], input, options ),
149
-
150
- // Start a new child workflow
151
- startWorkflow: async ( childName, input, extra = {} ) => {
152
- try {
153
- const result = await executeChild( childName, {
154
- args: undefined === input ? [] : [ input ],
155
- workflowId: `${workflowId}-${toUrlSafeBase64( uuid4() )}`,
156
- parentClosePolicy: ParentClosePolicy[extra?.detached ? 'ABANDON' : 'TERMINATE'],
157
- memo: {
158
- ...memo,
159
- ...( extra?.options?.activityOptions && { activityOptions: deepMerge( activityOptions, extra.options.activityOptions ) } )
160
- }
161
- } );
162
- /**
163
- * @IMPORTANT Keep support for deprecated ".attributes" from workflow results to allow for Temporal replays.
164
- * @TODO [OUT-468]
165
- */
166
- if ( result?.attributes ) {
167
- mergeAggregationsInWorkflowResult( aggregateAttributes( result.attributes ) );
168
- }
169
- if ( result?.aggregations ) {
170
- mergeAggregationsInWorkflowResult( result.aggregations );
171
- }
172
- return result.output;
173
- } catch ( error ) {
174
- /**
175
- * @IMPORTANT Keep support for deprecated ".attributes" from workflow errors to allow for Temporal replays.
176
- * @TODO [OUT-468]
177
- */
178
- const attributesFromError = extractErrorDetail( error, 'attributes' );
179
- if ( attributesFromError ) {
180
- mergeAggregationsInWorkflowResult( aggregateAttributes( attributesFromError ) );
181
- }
182
- const aggregationsFromError = extractErrorDetail( error, 'aggregations' );
183
- if ( aggregationsFromError ) {
184
- mergeAggregationsInWorkflowResult( aggregationsFromError );
185
- }
186
- throw error;
187
- }
188
- }
91
+ invokeStep: createCaller( name ),
92
+ invokeSharedStep: createCaller( SHARED_STEP_PREFIX ),
93
+ invokeEvaluator: createCaller( name ),
94
+ invokeSharedEvaluator: createCaller( SHARED_STEP_PREFIX )
189
95
  };
190
96
 
191
- workflowResult.output = await fn.call( dispatchers, input, context );
192
-
193
- validateWithSchema( outputSchema, workflowResult.output, `Workflow ${name} output` );
97
+ // The workflow function execution with "this" set with the dispatchers
98
+ const output = await fn.call( dispatchers, input, WorkflowContext.build() );
99
+ validator.validateOutput( output );
194
100
 
195
- return workflowResult;
196
- } catch ( e ) {
197
- // Append the result as metadata of the error, so it can be read by the interceptor.
198
- e[METADATA_ACCESS_SYMBOL] = { ...( e[METADATA_ACCESS_SYMBOL] ?? {} ), ...workflowResult };
199
- throw e;
101
+ return {
102
+ [WORKFLOW_WRAPPER_VERSION_FIELD]: 1,
103
+ output,
104
+ ...( traceDest && { trace: { destinations: traceDest } } )
105
+ };
106
+ } catch ( error ) {
107
+ if ( isRoot && traceDest ) {
108
+ // Append the trace destinations so it is carried to interceptor
109
+ error[METADATA_ACCESS_SYMBOL] = { trace: { destinations: traceDest } };
110
+ }
111
+ throw error;
200
112
  }
201
113
  };
202
114