@outputai/core 0.1.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 (114) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +11 -0
  3. package/bin/healthcheck.mjs +36 -0
  4. package/bin/healthcheck.spec.js +90 -0
  5. package/bin/worker.sh +26 -0
  6. package/package.json +67 -0
  7. package/src/activity_integration/context.d.ts +27 -0
  8. package/src/activity_integration/context.js +17 -0
  9. package/src/activity_integration/context.spec.js +42 -0
  10. package/src/activity_integration/events.d.ts +7 -0
  11. package/src/activity_integration/events.js +10 -0
  12. package/src/activity_integration/index.d.ts +9 -0
  13. package/src/activity_integration/index.js +3 -0
  14. package/src/activity_integration/tracing.d.ts +32 -0
  15. package/src/activity_integration/tracing.js +37 -0
  16. package/src/async_storage.js +19 -0
  17. package/src/bus.js +3 -0
  18. package/src/consts.js +32 -0
  19. package/src/errors.d.ts +15 -0
  20. package/src/errors.js +14 -0
  21. package/src/hooks/index.d.ts +28 -0
  22. package/src/hooks/index.js +32 -0
  23. package/src/index.d.ts +49 -0
  24. package/src/index.js +4 -0
  25. package/src/interface/evaluation_result.d.ts +173 -0
  26. package/src/interface/evaluation_result.js +215 -0
  27. package/src/interface/evaluator.d.ts +70 -0
  28. package/src/interface/evaluator.js +34 -0
  29. package/src/interface/evaluator.spec.js +565 -0
  30. package/src/interface/index.d.ts +9 -0
  31. package/src/interface/index.js +26 -0
  32. package/src/interface/step.d.ts +138 -0
  33. package/src/interface/step.js +22 -0
  34. package/src/interface/types.d.ts +27 -0
  35. package/src/interface/validations/runtime.js +20 -0
  36. package/src/interface/validations/runtime.spec.js +29 -0
  37. package/src/interface/validations/schema_utils.js +8 -0
  38. package/src/interface/validations/schema_utils.spec.js +67 -0
  39. package/src/interface/validations/static.js +136 -0
  40. package/src/interface/validations/static.spec.js +366 -0
  41. package/src/interface/webhook.d.ts +84 -0
  42. package/src/interface/webhook.js +64 -0
  43. package/src/interface/webhook.spec.js +122 -0
  44. package/src/interface/workflow.d.ts +273 -0
  45. package/src/interface/workflow.js +128 -0
  46. package/src/interface/workflow.spec.js +467 -0
  47. package/src/interface/workflow_context.js +31 -0
  48. package/src/interface/workflow_utils.d.ts +76 -0
  49. package/src/interface/workflow_utils.js +50 -0
  50. package/src/interface/workflow_utils.spec.js +190 -0
  51. package/src/interface/zod_integration.spec.js +646 -0
  52. package/src/internal_activities/index.js +66 -0
  53. package/src/internal_activities/index.spec.js +102 -0
  54. package/src/logger.js +73 -0
  55. package/src/tracing/internal_interface.js +71 -0
  56. package/src/tracing/processors/local/index.js +111 -0
  57. package/src/tracing/processors/local/index.spec.js +149 -0
  58. package/src/tracing/processors/s3/configs.js +31 -0
  59. package/src/tracing/processors/s3/configs.spec.js +64 -0
  60. package/src/tracing/processors/s3/index.js +114 -0
  61. package/src/tracing/processors/s3/index.spec.js +153 -0
  62. package/src/tracing/processors/s3/redis_client.js +62 -0
  63. package/src/tracing/processors/s3/redis_client.spec.js +185 -0
  64. package/src/tracing/processors/s3/s3_client.js +27 -0
  65. package/src/tracing/processors/s3/s3_client.spec.js +62 -0
  66. package/src/tracing/tools/build_trace_tree.js +83 -0
  67. package/src/tracing/tools/build_trace_tree.spec.js +135 -0
  68. package/src/tracing/tools/utils.js +21 -0
  69. package/src/tracing/tools/utils.spec.js +14 -0
  70. package/src/tracing/trace_engine.js +97 -0
  71. package/src/tracing/trace_engine.spec.js +199 -0
  72. package/src/utils/index.d.ts +134 -0
  73. package/src/utils/index.js +2 -0
  74. package/src/utils/resolve_invocation_dir.js +34 -0
  75. package/src/utils/resolve_invocation_dir.spec.js +102 -0
  76. package/src/utils/utils.js +211 -0
  77. package/src/utils/utils.spec.js +448 -0
  78. package/src/worker/bundler_options.js +43 -0
  79. package/src/worker/catalog_workflow/catalog.js +114 -0
  80. package/src/worker/catalog_workflow/index.js +54 -0
  81. package/src/worker/catalog_workflow/index.spec.js +196 -0
  82. package/src/worker/catalog_workflow/workflow.js +24 -0
  83. package/src/worker/configs.js +49 -0
  84. package/src/worker/configs.spec.js +130 -0
  85. package/src/worker/index.js +89 -0
  86. package/src/worker/index.spec.js +177 -0
  87. package/src/worker/interceptors/activity.js +62 -0
  88. package/src/worker/interceptors/activity.spec.js +212 -0
  89. package/src/worker/interceptors/workflow.js +70 -0
  90. package/src/worker/interceptors/workflow.spec.js +167 -0
  91. package/src/worker/interceptors.js +10 -0
  92. package/src/worker/loader.js +151 -0
  93. package/src/worker/loader.spec.js +236 -0
  94. package/src/worker/loader_tools.js +132 -0
  95. package/src/worker/loader_tools.spec.js +156 -0
  96. package/src/worker/log_hooks.js +95 -0
  97. package/src/worker/log_hooks.spec.js +217 -0
  98. package/src/worker/sandboxed_utils.js +18 -0
  99. package/src/worker/shutdown.js +26 -0
  100. package/src/worker/shutdown.spec.js +82 -0
  101. package/src/worker/sinks.js +74 -0
  102. package/src/worker/start_catalog.js +36 -0
  103. package/src/worker/start_catalog.spec.js +118 -0
  104. package/src/worker/webpack_loaders/consts.js +9 -0
  105. package/src/worker/webpack_loaders/tools.js +548 -0
  106. package/src/worker/webpack_loaders/tools.spec.js +330 -0
  107. package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.js +221 -0
  108. package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.spec.js +336 -0
  109. package/src/worker/webpack_loaders/workflow_rewriter/index.mjs +61 -0
  110. package/src/worker/webpack_loaders/workflow_rewriter/index.spec.js +216 -0
  111. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.js +196 -0
  112. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.spec.js +123 -0
  113. package/src/worker/webpack_loaders/workflow_validator/index.mjs +205 -0
  114. package/src/worker/webpack_loaders/workflow_validator/index.spec.js +613 -0
@@ -0,0 +1,102 @@
1
+ import { afterEach, describe, expect, it } from 'vitest';
2
+ import resolveInvocationDir from './resolve_invocation_dir';
3
+
4
+ const OriginalError = Error;
5
+
6
+ describe( 'Resolve Invocation Dir', () => {
7
+ afterEach( () => {
8
+ Error = OriginalError;
9
+ } );
10
+
11
+ it( 'Should detect the invocation dir from the tests workflow', () => {
12
+ const stack = `Error
13
+ at resolveInvocationDir (file:///app/sdk/core/src/utils/resolve_invocation_dir.js)
14
+ at fn (file:///app/test_workflows/dist/simple/steps.js:8:21)
15
+ at wrapper (file:///app/sdk/core/src/interface/step.js:12:26)
16
+ at executeNextHandler (/app/node_modules/@temporalio/worker/lib/activity.js:99:54)
17
+ at Storage.runWithContext.parentId (file:///app/sdk/core/src/worker/interceptors/activity.js:31:63)
18
+ at AsyncLocalStorage.run (node:internal/async_local_storage/async_context_frame:63:14)
19
+ at Object.runWithContext (file:///app/sdk/core/src/async_storage.js:12:44)
20
+ at ActivityExecutionInterceptor.execute (file:///app/sdk/core/src/worker/interceptors/activity.js:31:36)
21
+ at next (/app/node_modules/@temporalio/common/lib/interceptors.js:22:51)
22
+ at Activity.execute (/app/node_modules/@temporalio/worker/lib/activity.js:101:26)
23
+ at /app/node_modules/@temporalio/worker/lib/activity.js:149:29`;
24
+
25
+ Error = class extends OriginalError {
26
+ constructor( ...args ) {
27
+ super( ...args );
28
+ this.stack = stack;
29
+ }
30
+ };
31
+
32
+ expect( resolveInvocationDir() ).toBe( '/app/test_workflows/dist/simple' );
33
+ } );
34
+
35
+ it( 'Should detect the invocation dir from the sandbox environment at sdk/core', () => {
36
+ const stack = `Error
37
+ at resolveInvocationDir (file:///app/sdk/core/src/utils/resolve_invocation_dir.js)
38
+ at workflow (file:///app/sdk/core/src/interface/workflow.js:25:16)
39
+ at file:///app/test_workflows/dist/nested/workflow.js:4:16
40
+ at ModuleJob.run (node:internal/modules/esm/module_job:365:25)
41
+ at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:665:26)
42
+ at async importComponents (file:///app/sdk/core/src/worker/loader_tools.js:54:22)
43
+ at async loadWorkflows (file:///app/sdk/core/src/worker/loader.js:64:38)
44
+ at async file:///app/sdk/core/src/worker/index.js:21:21`;
45
+
46
+ Error = class extends OriginalError {
47
+ constructor( ...args ) {
48
+ super( ...args );
49
+ this.stack = stack;
50
+ }
51
+ };
52
+
53
+ expect( resolveInvocationDir() ).toBe( '/app/test_workflows/dist/nested' );
54
+ } );
55
+
56
+ it( 'Should detect the invocation dir from workflow loading at core', () => {
57
+ const stack = `Error
58
+ at __WEBPACK_DEFAULT_EXPORT__ (/app/sdk/core/src/utils/resolve_invocation_dir.js:13:0)
59
+ at workflow (/app/sdk/core/src/interface/workflow.js:22:43)
60
+ at ../../test_workflows/dist/nested/workflow.js (/app/test_workflows/dist/nested/workflow.js:4:24)
61
+ at __webpack_require__ (webpack/bootstrap:19:0)
62
+ at ./src/worker/temp/__workflows_entrypoint.js (null:null:null)
63
+ at __webpack_require__ (webpack/bootstrap:19:0)
64
+ at importWorkflows (/app/sdk/core/src/worker/temp/__workflows_entrypoint-autogenerated-entrypoint.cjs:9:9)
65
+ at Object.initRuntime (/app/node_modules/@temporalio/workflow/src/worker-interface.ts:78:16)
66
+ at __TEMPORAL_CALL_INTO_SCOPE (evalmachine.<anonymous>:30:40)
67
+ at evalmachine.<anonymous>:1:1`;
68
+
69
+ Error = class extends OriginalError {
70
+ constructor( ...args ) {
71
+ super( ...args );
72
+ this.stack = stack;
73
+ }
74
+ };
75
+
76
+ expect( resolveInvocationDir() ).toBe( '/app/test_workflows/dist/nested' );
77
+ } );
78
+
79
+ it( 'Should detect the invocation dir from a workflow using core installed via NPM', () => {
80
+ const stack = `Error
81
+ at resolveInvocationDir (file:///app/node_modules/@outputai/core/src/utils/resolve_invocation_dir.js)
82
+ at fn (file:///app/dist/simple/steps.js:8:21)
83
+ at wrapper (file:///app/node_modules/@outputai/core/src/interface/step.js:12:26)
84
+ at executeNextHandler (/app/node_modules/@temporalio/worker/lib/activity.js:99:54)
85
+ at Storage.runWithContext.parentId (file:///app/node_modules/@outputai/core/src/worker/interceptors/activity.js:31:63)
86
+ at AsyncLocalStorage.run (node:internal/async_local_storage/async_context_frame:63:14)
87
+ at Object.runWithContext (file:///app/node_modules/@outputai/core/src/async_storage.js:12:44)
88
+ at ActivityExecutionInterceptor.execute (file:///app/node_modules/@outputai/core/src/worker/interceptors/activity.js:31:36)
89
+ at next (/app/node_modules/@temporalio/common/lib/interceptors.js:22:51)
90
+ at Activity.execute (/app/node_modules/@temporalio/worker/lib/activity.js:101:26)
91
+ at /app/node_modules/@temporalio/worker/lib/activity.js:149:29`;
92
+
93
+ Error = class extends OriginalError {
94
+ constructor( ...args ) {
95
+ super( ...args );
96
+ this.stack = stack;
97
+ }
98
+ };
99
+
100
+ expect( resolveInvocationDir() ).toBe( '/app/dist/simple' );
101
+ } );
102
+ } );
@@ -0,0 +1,211 @@
1
+ import { METADATA_ACCESS_SYMBOL } from '#consts';
2
+
3
+ /**
4
+ * Node safe clone implementation that doesn't use global structuredClone()
5
+ * @param {object} v
6
+ * @returns {object}
7
+ */
8
+ export const clone = v => JSON.parse( JSON.stringify( v ) );
9
+
10
+ /**
11
+ * Detect a JS plain object.
12
+ *
13
+ * @param {unknown} v
14
+ * @returns {boolean}
15
+ */
16
+ export const isPlainObject = v =>
17
+ typeof v === 'object' &&
18
+ !Array.isArray( v ) &&
19
+ v !== null &&
20
+ [ Object.prototype, null ].includes( Object.getPrototypeOf( v ) );
21
+
22
+ /**
23
+ * Throw given error
24
+ * @param {Error} e
25
+ * @throws {e}
26
+ */
27
+ export const throws = e => {
28
+ throw e;
29
+ };
30
+
31
+ /**
32
+ * Add metadata "values" property to a given object
33
+ * @param {object} target
34
+ * @param {object} values
35
+ * @returns
36
+ */
37
+ export const setMetadata = ( target, values ) =>
38
+ Object.defineProperty( target, METADATA_ACCESS_SYMBOL, { value: values, writable: false, enumerable: false, configurable: false } );
39
+
40
+ /**
41
+ * Read metadata previously attached via setMetadata
42
+ * @param {Function} target
43
+ * @returns {object|null}
44
+ */
45
+ export const getMetadata = target => target[METADATA_ACCESS_SYMBOL] ?? null;
46
+
47
+ /**
48
+ * Returns true if string value is stringbool and true
49
+ * @param {string} v
50
+ * @returns
51
+ */
52
+ export const isStringboolTrue = v => [ '1', 'true', 'on' ].includes( v );
53
+
54
+ /**
55
+ * Consume Fetch's HTTP Response and return a serialized version of it;
56
+ *
57
+ * @param {Response} response
58
+ * @returns {object} Serialized response
59
+ */
60
+ export const serializeFetchResponse = async response => {
61
+ const headers = Object.fromEntries( response.headers );
62
+ const contentType = headers['content-type'] || '';
63
+
64
+ const body = await ( async () => {
65
+ if ( contentType.includes( 'application/json' ) ) {
66
+ return response.json();
67
+ }
68
+ if ( contentType.startsWith( 'text/' ) ) {
69
+ return response.text();
70
+ }
71
+ return response.arrayBuffer().then( buf => Buffer.from( buf ).toString( 'base64' ) );
72
+ } )();
73
+
74
+ return {
75
+ url: response.url,
76
+ status: response.status,
77
+ statusText: response.statusText,
78
+ ok: response.ok,
79
+ headers,
80
+ body
81
+ };
82
+ };
83
+
84
+ /**
85
+ * Duck-typing to detect a Node Readable (Stream) without importing anything
86
+ *
87
+ * @param {unknown} v
88
+ * @returns {boolean}
89
+ */
90
+ const isReadable = v =>
91
+ typeof v === 'object' &&
92
+ typeof v?.read === 'function' &&
93
+ typeof v?.on === 'function' &&
94
+ typeof v?.pipe === 'function' &&
95
+ v?.readable !== false;
96
+
97
+ /**
98
+ * Based on the type of a payload, serialized it to be send as the body of a fetch POST request and also infer its Content Type.
99
+ *
100
+ * Non serializable types versus Content-Type reference (for Node)
101
+ *
102
+ * |Type|Is self-describing)|Inferred type by fetch|Defined mime type|
103
+ * |-|-|-|-}
104
+ * |Blob|yes|`blob.type`||
105
+ * |File|yes|`file.type`||
106
+ * |FormData|yes|"multipart/form-data; boundary=..."||
107
+ * |URLSearchParams|yes|"application/x-www-form-urlencoded;charset=UTF-8"||
108
+ * |ArrayBuffer|no||"application/octet-stream"|
109
+ * |TypedArray (Uint8Array,Uint16Array)||"application/octet-stream"||
110
+ * |DataView|no||"application/octet-stream"|
111
+ * |ReadableStream, Readable, AsyncIterator|no||Can't, because stream must be read|
112
+ *
113
+ * If payload is none of the above types, test it:
114
+ * If the it is an object, serialize using JSON.stringify and set content-type to `application/json`;
115
+ * Else, it is a JS primitive, serialize using JSON.stringify and set content-type to `text/plain`;
116
+ *
117
+ * This implementation is overkill for temporal workflows since the only types available there will be:
118
+ * - URLSearchParams
119
+ * - ArrayBuffer
120
+ * - TypedArrays
121
+ * - DataView
122
+ * - asyncGenerator
123
+ * The others are non deterministic and are not available at runtime, but this function was build to be flexible
124
+ *
125
+ * @see {@link https://fetch.spec.whatwg.org/#bodyinit}
126
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch}
127
+ *
128
+ * @param {unknown} payload
129
+ * @returns {object} An object with the serialized body and inferred content-type
130
+ */
131
+ export const serializeBodyAndInferContentType = payload => {
132
+ const dataTypes = [ Blob, File, URLSearchParams, FormData ];
133
+
134
+ // empty body
135
+ if ( [ null, undefined ].includes( payload ) ) {
136
+ return { body: undefined, contentType: undefined };
137
+ }
138
+
139
+ // Buffer types, covers ArrayBuffer, TypedArrays and DataView
140
+ if ( payload instanceof ArrayBuffer || ArrayBuffer.isView( payload ) ) {
141
+ return { body: payload, contentType: 'application/octet-stream' };
142
+ }
143
+
144
+ // These data types auto assigned mime types
145
+ if ( dataTypes.some( t => payload instanceof t ) ) {
146
+ return { body: payload, contentType: undefined };
147
+ }
148
+
149
+ // ReadableStream, Readable and Async Iterator mimes cant be determined without reading it
150
+ if ( payload instanceof ReadableStream || typeof payload[Symbol.asyncIterator] === 'function' || isReadable( payload ) ) {
151
+ return { body: payload, contentType: undefined };
152
+ }
153
+
154
+ if ( typeof payload === 'object' ) {
155
+ return { body: JSON.stringify( payload ), contentType: 'application/json; charset=UTF-8' };
156
+ }
157
+
158
+ return { body: String( payload ), contentType: 'text/plain; charset=UTF-8' };
159
+ };
160
+
161
+ /**
162
+ * Receives an array and returns a copy of it with the elements shuffled
163
+ *
164
+ * @param {array} arr
165
+ * @returns {array}
166
+ */
167
+ export const shuffleArray = arr => arr
168
+ .map( v => ( { v, sort: Math.random() } ) )
169
+ .sort( ( a, b ) => a.sort - b.sort )
170
+ .map( ( { v } ) => v );
171
+
172
+ /**
173
+ * Creates a new object merging object "b" onto object "a" biased to "b":
174
+ * - Object "b" will overwrite fields on object "a";
175
+ * - Object "b" fields that don't exist on object "a" will be created;
176
+ * - Object "a" fields that don't exist on object "b" will not be touched;
177
+ *
178
+ * If "b" isn't an object, a new object equal to "a" is returned
179
+ *
180
+ * @param {object} a - The base object
181
+ * @param {object} b - The target object
182
+ * @returns {object} A new object
183
+ */
184
+ export const deepMerge = ( a, b ) => {
185
+ if ( !isPlainObject( a ) ) {
186
+ throw new Error( 'Parameter "a" is not an object.' );
187
+ }
188
+ if ( !isPlainObject( b ) ) {
189
+ return clone( a );
190
+ }
191
+ return Object.entries( b ).reduce( ( obj, [ k, v ] ) =>
192
+ Object.assign( obj, { [k]: isPlainObject( v ) && isPlainObject( a[k] ) ? deepMerge( a[k], v ) : v } )
193
+ , clone( a ) );
194
+ };
195
+
196
+ /**
197
+ * Shortens a UUID by re-encoding it to base62.
198
+ *
199
+ * This is a Temporal friendly, without crypto or Buffer.
200
+ * @param {string} uuid
201
+ * @returns {string}
202
+ */
203
+ export const toUrlSafeBase64 = uuid => {
204
+ const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-';
205
+ const alphabetLen = alphabet.length;
206
+ const base = BigInt( alphabetLen );
207
+ const hex = uuid.replace( /-/g, '' );
208
+
209
+ const toDigits = n => n <= 0n ? [] : toDigits( n / base ).concat( alphabet[Number( n % base )] );
210
+ return toDigits( BigInt( '0x' + hex ) ).join( '' );
211
+ };