@outputai/core 0.14.0 → 0.14.1-next.2947341.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/dist/credentials/credentials.d.ts +6 -0
- package/dist/credentials/credentials.js +87 -0
- package/dist/credentials/encrypted_yaml_provider.d.ts +2 -0
- package/dist/credentials/encrypted_yaml_provider.js +77 -0
- package/dist/credentials/encryption.d.ts +3 -0
- package/dist/credentials/encryption.js +6 -0
- package/dist/credentials/errors.d.ts +13 -0
- package/dist/credentials/errors.js +39 -0
- package/dist/credentials/index.d.ts +6 -0
- package/dist/credentials/index.js +5 -0
- package/dist/credentials/init.d.ts +1 -0
- package/dist/credentials/init.js +7 -0
- package/dist/credentials/paths.d.ts +7 -0
- package/dist/credentials/paths.js +14 -0
- package/dist/credentials/provider_registry.d.ts +3 -0
- package/dist/credentials/provider_registry.js +6 -0
- package/dist/credentials/types.d.ts +12 -0
- package/dist/credentials/types.js +1 -0
- package/package.json +25 -14
- package/src/async_storage.d.ts +4 -0
- package/src/consts.js +0 -2
- package/src/helpers/object.d.ts +2 -0
- package/src/hooks/index.d.ts +0 -8
- package/src/hooks/index.js +0 -3
- package/src/logger/index.d.ts +4 -0
- package/src/worker/index.js +3 -1
- package/bin/healthcheck.spec.js +0 -90
- package/src/bus.spec.js +0 -196
- package/src/errors.spec.js +0 -19
- package/src/helpers/activity_options.spec.js +0 -55
- package/src/helpers/component.spec.js +0 -54
- package/src/helpers/error_serializer.spec.js +0 -479
- package/src/helpers/errors.spec.js +0 -189
- package/src/helpers/fetch.spec.js +0 -292
- package/src/helpers/function.spec.js +0 -24
- package/src/helpers/object.spec.js +0 -425
- package/src/helpers/promise.spec.js +0 -62
- package/src/helpers/redact.spec.js +0 -66
- package/src/helpers/string.spec.js +0 -99
- package/src/helpers/temporal_context.spec.ts +0 -83
- package/src/helpers/trace_info.spec.js +0 -45
- package/src/helpers/workflow_context.spec.js +0 -46
- package/src/hooks/index.spec.js +0 -265
- package/src/hooks/pending_hooks.spec.js +0 -62
- package/src/interface/evaluator.spec.js +0 -685
- package/src/interface/logger.spec.js +0 -172
- package/src/interface/step.spec.js +0 -128
- package/src/interface/validations/index.spec.js +0 -197
- package/src/interface/validations/schemas.spec.js +0 -229
- package/src/interface/webhook.spec.js +0 -170
- package/src/interface/workflow.spec.js +0 -710
- package/src/interface/workflow_utils.spec.js +0 -271
- package/src/interface/zod_integration.spec.js +0 -646
- package/src/internal_activities/index.spec.js +0 -186
- package/src/logger/development.spec.js +0 -87
- package/src/logger/index.spec.js +0 -27
- package/src/logger/production.spec.js +0 -71
- package/src/sdk/helpers/component_metadata.spec.js +0 -30
- package/src/sdk/helpers/objects.spec.js +0 -16
- package/src/sdk/helpers/path.spec.js +0 -102
- package/src/sdk/runtime/context.spec.js +0 -37
- package/src/sdk/runtime/events.spec.js +0 -40
- package/src/temporal/client.spec.js +0 -124
- package/src/tracing/processors/local/index.spec.js +0 -204
- package/src/tracing/processors/s3/configs.spec.js +0 -64
- package/src/tracing/processors/s3/index.spec.js +0 -185
- package/src/tracing/processors/s3/redis_client.spec.js +0 -179
- package/src/tracing/processors/s3/s3_client.spec.js +0 -68
- package/src/tracing/tools/build_trace_tree.spec.js +0 -216
- package/src/tracing/trace_engine.spec.js +0 -299
- package/src/worker/bundle.spec.js +0 -53
- package/src/worker/bundler_options.spec.js +0 -62
- package/src/worker/catalog_workflow/catalog_publisher.spec.js +0 -317
- package/src/worker/catalog_workflow/index.spec.js +0 -222
- package/src/worker/configs.spec.js +0 -213
- package/src/worker/configs_tuner_schema.spec.js +0 -127
- package/src/worker/connection_monitor.spec.js +0 -328
- package/src/worker/global_functions.spec.js +0 -55
- package/src/worker/index.spec.js +0 -526
- package/src/worker/interceptors/activity.spec.js +0 -369
- package/src/worker/interceptors/headers.spec.js +0 -73
- package/src/worker/interceptors/workflow.spec.js +0 -357
- package/src/worker/interruption.spec.js +0 -177
- package/src/worker/loader/activities.spec.js +0 -212
- package/src/worker/loader/hooks.spec.js +0 -64
- package/src/worker/loader/matchers.spec.js +0 -140
- package/src/worker/loader/tools.spec.js +0 -595
- package/src/worker/loader/workflows.spec.js +0 -287
- package/src/worker/log_hooks.spec.js +0 -303
- package/src/worker/shutdown.spec.js +0 -197
- package/src/worker/sinks.spec.js +0 -220
- package/src/worker/telemetry.spec.js +0 -80
- package/src/worker/temporal_logger.spec.js +0 -90
- package/src/worker/webpack_loaders/tools.spec.js +0 -314
- package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.spec.js +0 -134
- package/src/worker/webpack_loaders/workflow_rewriter/index.spec.js +0 -154
- package/src/worker/webpack_loaders/workflow_rewriter/rewrite_activity_calls.spec.js +0 -165
- package/src/worker/webpack_loaders/workflow_validator/index.spec.js +0 -202
- package/src/worker/webpack_loaders/workflow_validator/npm_workflow_export_resolve.spec.js +0 -374
- package/src/worker/worker_runner.spec.js +0 -220
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { WorkflowNotFoundError } from '@temporalio/client';
|
|
3
|
-
import { WorkflowExecutionAlreadyStartedError, WorkflowIdConflictPolicy } from '@temporalio/common';
|
|
4
|
-
import { CatalogPublisher } from './catalog_publisher.js';
|
|
5
|
-
|
|
6
|
-
const {
|
|
7
|
-
catalogId,
|
|
8
|
-
describeMock,
|
|
9
|
-
getHandleMock,
|
|
10
|
-
mockLog,
|
|
11
|
-
serializeErrorMock,
|
|
12
|
-
sleepMock,
|
|
13
|
-
startMock,
|
|
14
|
-
taskQueue,
|
|
15
|
-
terminateMock
|
|
16
|
-
} = vi.hoisted( () => ( {
|
|
17
|
-
catalogId: 'test-catalog',
|
|
18
|
-
describeMock: vi.fn(),
|
|
19
|
-
getHandleMock: vi.fn(),
|
|
20
|
-
mockLog: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
|
|
21
|
-
serializeErrorMock: vi.fn( error => ( { message: error.message } ) ),
|
|
22
|
-
sleepMock: vi.fn(),
|
|
23
|
-
startMock: vi.fn(),
|
|
24
|
-
taskQueue: 'test-queue',
|
|
25
|
-
terminateMock: vi.fn()
|
|
26
|
-
} ) );
|
|
27
|
-
|
|
28
|
-
vi.mock( '#logger', () => ( { createChildLogger: () => mockLog } ) );
|
|
29
|
-
vi.mock( '#helpers/error_serializer', () => ( { serializeError: serializeErrorMock } ) );
|
|
30
|
-
vi.mock( '#consts', () => ( { WORKFLOW_CATALOG: 'catalog' } ) );
|
|
31
|
-
vi.mock( '#helpers/promise', () => ( { sleepCancellable: sleepMock } ) );
|
|
32
|
-
vi.mock( '../configs.js', () => ( { catalogId, taskQueue } ) );
|
|
33
|
-
vi.mock( '@temporalio/client', async importOriginal => {
|
|
34
|
-
const actual = await importOriginal();
|
|
35
|
-
return {
|
|
36
|
-
...actual,
|
|
37
|
-
Client: vi.fn().mockImplementation( function () {
|
|
38
|
-
return { workflow: { start: startMock, getHandle: getHandleMock } };
|
|
39
|
-
} )
|
|
40
|
-
};
|
|
41
|
-
} );
|
|
42
|
-
|
|
43
|
-
const RETRY_DELAY = 10_000;
|
|
44
|
-
const namespace = 'default';
|
|
45
|
-
const catalogHash = 'catalog-hash';
|
|
46
|
-
const catalog = { workflows: [], workflowNames: { workflow: 'workflow' }, activities: {} };
|
|
47
|
-
|
|
48
|
-
const startArguments = {
|
|
49
|
-
taskQueue,
|
|
50
|
-
workflowId: catalogId,
|
|
51
|
-
workflowIdConflictPolicy: WorkflowIdConflictPolicy.FAIL,
|
|
52
|
-
workflowTaskTimeout: 30_000,
|
|
53
|
-
args: [ catalog ],
|
|
54
|
-
memo: {
|
|
55
|
-
workflowNames: catalog.workflowNames,
|
|
56
|
-
hash: catalogHash
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const compactError = message => ( { error: { message } } );
|
|
61
|
-
|
|
62
|
-
const running = hash => ( { closeTime: undefined, runId: 'run-1', memo: { hash } } );
|
|
63
|
-
const notFound = () => new WorkflowNotFoundError( 'not found' );
|
|
64
|
-
const alreadyStarted = () => new WorkflowExecutionAlreadyStartedError( 'already started', catalogId, 'catalog' );
|
|
65
|
-
|
|
66
|
-
const createPublisher = ( signal = new AbortController().signal ) =>
|
|
67
|
-
new CatalogPublisher( { connection: {}, namespace, catalog, catalogHash, signal } );
|
|
68
|
-
|
|
69
|
-
const flushPromises = async () => Array
|
|
70
|
-
.from( { length: 10 } )
|
|
71
|
-
.reduce( promise => promise.then( () => Promise.resolve() ), Promise.resolve() );
|
|
72
|
-
|
|
73
|
-
describe( 'CatalogPublisher', () => {
|
|
74
|
-
beforeEach( () => {
|
|
75
|
-
vi.clearAllMocks();
|
|
76
|
-
getHandleMock.mockReturnValue( { describe: describeMock, terminate: terminateMock } );
|
|
77
|
-
describeMock.mockRejectedValue( notFound() );
|
|
78
|
-
terminateMock.mockResolvedValue( undefined );
|
|
79
|
-
startMock.mockResolvedValue( undefined );
|
|
80
|
-
sleepMock.mockResolvedValue( undefined );
|
|
81
|
-
} );
|
|
82
|
-
|
|
83
|
-
describe( 'publish paths', () => {
|
|
84
|
-
it( 'starts the catalog when nothing is running', async () => {
|
|
85
|
-
const publisher = createPublisher();
|
|
86
|
-
|
|
87
|
-
await publisher.run();
|
|
88
|
-
|
|
89
|
-
expect( terminateMock ).not.toHaveBeenCalled();
|
|
90
|
-
expect( startMock ).toHaveBeenCalledWith( 'catalog', startArguments );
|
|
91
|
-
expect( mockLog.info ).toHaveBeenCalledWith( 'Catalog workflow started' );
|
|
92
|
-
expect( mockLog.warn ).not.toHaveBeenCalled();
|
|
93
|
-
expect( publisher.running ).toBe( false );
|
|
94
|
-
} );
|
|
95
|
-
|
|
96
|
-
it( 'starts the catalog when the previous one is closed', async () => {
|
|
97
|
-
describeMock.mockResolvedValue( { closeTime: '2024-01-01T00:00:00Z' } );
|
|
98
|
-
|
|
99
|
-
await createPublisher().run();
|
|
100
|
-
|
|
101
|
-
expect( terminateMock ).not.toHaveBeenCalled();
|
|
102
|
-
expect( startMock ).toHaveBeenCalledWith( 'catalog', startArguments );
|
|
103
|
-
} );
|
|
104
|
-
|
|
105
|
-
it( 'skips the restart when the running hash matches', async () => {
|
|
106
|
-
describeMock.mockResolvedValue( running( catalogHash ) );
|
|
107
|
-
|
|
108
|
-
await createPublisher().run();
|
|
109
|
-
|
|
110
|
-
expect( terminateMock ).not.toHaveBeenCalled();
|
|
111
|
-
expect( startMock ).not.toHaveBeenCalled();
|
|
112
|
-
expect( mockLog.info ).toHaveBeenCalledWith( 'Current catalog workflow hash matches worker, restart skipped' );
|
|
113
|
-
} );
|
|
114
|
-
|
|
115
|
-
it( 'terminates and starts when the running catalog carries no memo', async () => {
|
|
116
|
-
describeMock.mockResolvedValue( { closeTime: undefined, runId: 'run-1', memo: undefined } );
|
|
117
|
-
|
|
118
|
-
await createPublisher().run();
|
|
119
|
-
|
|
120
|
-
expect( mockLog.warn ).not.toHaveBeenCalled();
|
|
121
|
-
expect( terminateMock ).toHaveBeenCalledOnce();
|
|
122
|
-
expect( startMock ).toHaveBeenCalledWith( 'catalog', startArguments );
|
|
123
|
-
} );
|
|
124
|
-
|
|
125
|
-
it( 'terminates the pinned run then starts when the running hash differs', async () => {
|
|
126
|
-
describeMock.mockResolvedValue( running( 'old-hash' ) );
|
|
127
|
-
|
|
128
|
-
await createPublisher().run();
|
|
129
|
-
|
|
130
|
-
expect( getHandleMock ).toHaveBeenCalledWith( catalogId, 'run-1' );
|
|
131
|
-
expect( terminateMock ).toHaveBeenCalledWith( `Systematic terminating. Superseded by catalog ${catalogHash}` );
|
|
132
|
-
expect( startMock ).toHaveBeenCalledWith( 'catalog', startArguments );
|
|
133
|
-
} );
|
|
134
|
-
} );
|
|
135
|
-
|
|
136
|
-
describe( 'race paths', () => {
|
|
137
|
-
it( 'ignores an already started error when the published hash matches', async () => {
|
|
138
|
-
describeMock
|
|
139
|
-
.mockRejectedValueOnce( notFound() )
|
|
140
|
-
.mockResolvedValueOnce( running( catalogHash ) );
|
|
141
|
-
startMock.mockRejectedValue( alreadyStarted() );
|
|
142
|
-
|
|
143
|
-
await createPublisher().run();
|
|
144
|
-
|
|
145
|
-
expect( startMock ).toHaveBeenCalledOnce();
|
|
146
|
-
expect( terminateMock ).not.toHaveBeenCalled();
|
|
147
|
-
expect( mockLog.info ).toHaveBeenCalledWith( 'Found a catalog workflow with matching hashes, start skipped' );
|
|
148
|
-
expect( mockLog.info ).not.toHaveBeenCalledWith( 'Catalog workflow started' );
|
|
149
|
-
} );
|
|
150
|
-
|
|
151
|
-
it( 'retires the winner on retry when an already started error exposes a different hash', async () => {
|
|
152
|
-
describeMock
|
|
153
|
-
.mockRejectedValueOnce( notFound() )
|
|
154
|
-
.mockResolvedValue( running( 'other-hash' ) );
|
|
155
|
-
startMock
|
|
156
|
-
.mockRejectedValueOnce( alreadyStarted() )
|
|
157
|
-
.mockResolvedValue( undefined );
|
|
158
|
-
|
|
159
|
-
await createPublisher().run();
|
|
160
|
-
|
|
161
|
-
expect( sleepMock ).toHaveBeenCalledOnce();
|
|
162
|
-
expect( terminateMock ).toHaveBeenCalledOnce();
|
|
163
|
-
expect( startMock ).toHaveBeenCalledTimes( 2 );
|
|
164
|
-
expect( mockLog.warn ).toHaveBeenCalledWith(
|
|
165
|
-
'Error while starting catalog workflow, retrying...',
|
|
166
|
-
compactError( 'already started' )
|
|
167
|
-
);
|
|
168
|
-
} );
|
|
169
|
-
|
|
170
|
-
it( 'warns and attempts a start when describing fails', async () => {
|
|
171
|
-
const error = new Error( 'describe failed' );
|
|
172
|
-
describeMock.mockRejectedValue( error );
|
|
173
|
-
|
|
174
|
-
await createPublisher().run();
|
|
175
|
-
|
|
176
|
-
expect( serializeErrorMock ).toHaveBeenCalledWith( error, { dropKeys: [ 'stack' ] } );
|
|
177
|
-
expect( mockLog.warn ).toHaveBeenCalledWith(
|
|
178
|
-
'Ignoring error while describing catalog workflow',
|
|
179
|
-
compactError( 'describe failed' )
|
|
180
|
-
);
|
|
181
|
-
expect( startMock ).toHaveBeenCalledWith( 'catalog', startArguments );
|
|
182
|
-
} );
|
|
183
|
-
|
|
184
|
-
it( 'warns and still starts when terminating the previous catalog fails', async () => {
|
|
185
|
-
describeMock.mockResolvedValue( running( 'old-hash' ) );
|
|
186
|
-
terminateMock.mockRejectedValue( new Error( 'terminate failed' ) );
|
|
187
|
-
|
|
188
|
-
await createPublisher().run();
|
|
189
|
-
|
|
190
|
-
expect( mockLog.warn ).toHaveBeenCalledWith(
|
|
191
|
-
'Ignoring error while terminating previous catalog workflow',
|
|
192
|
-
compactError( 'terminate failed' )
|
|
193
|
-
);
|
|
194
|
-
expect( startMock ).toHaveBeenCalledWith( 'catalog', startArguments );
|
|
195
|
-
} );
|
|
196
|
-
} );
|
|
197
|
-
|
|
198
|
-
describe( 'failure', () => {
|
|
199
|
-
it( 'rejects with the original error after exhausting the retries', async () => {
|
|
200
|
-
const error = new Error( 'start failed' );
|
|
201
|
-
startMock.mockRejectedValue( error );
|
|
202
|
-
const publisher = createPublisher();
|
|
203
|
-
|
|
204
|
-
await expect( publisher.run() ).rejects.toBe( error );
|
|
205
|
-
|
|
206
|
-
expect( startMock ).toHaveBeenCalledTimes( 3 );
|
|
207
|
-
expect( sleepMock ).toHaveBeenCalledTimes( 2 );
|
|
208
|
-
expect( publisher.running ).toBe( false );
|
|
209
|
-
} );
|
|
210
|
-
|
|
211
|
-
it( 'waits the retry delay with the abort signal between attempts', async () => {
|
|
212
|
-
startMock.mockRejectedValue( new Error( 'start failed' ) );
|
|
213
|
-
|
|
214
|
-
await expect( createPublisher().run() ).rejects.toThrow( 'start failed' );
|
|
215
|
-
|
|
216
|
-
expect( sleepMock ).toHaveBeenCalledWith( RETRY_DELAY, expect.any( AbortSignal ) );
|
|
217
|
-
} );
|
|
218
|
-
} );
|
|
219
|
-
|
|
220
|
-
describe( 'abort paths', () => {
|
|
221
|
-
it( 'publishes nothing when already aborted', async () => {
|
|
222
|
-
const controller = new AbortController();
|
|
223
|
-
controller.abort();
|
|
224
|
-
|
|
225
|
-
await expect( createPublisher( controller.signal ).run() ).resolves.toBeUndefined();
|
|
226
|
-
|
|
227
|
-
expect( describeMock ).not.toHaveBeenCalled();
|
|
228
|
-
expect( startMock ).not.toHaveBeenCalled();
|
|
229
|
-
} );
|
|
230
|
-
|
|
231
|
-
it( 'stops retrying when the signal aborts during the backoff', async () => {
|
|
232
|
-
const controller = new AbortController();
|
|
233
|
-
startMock.mockRejectedValue( new Error( 'start failed' ) );
|
|
234
|
-
sleepMock.mockImplementation( async () => controller.abort() );
|
|
235
|
-
|
|
236
|
-
await expect( createPublisher( controller.signal ).run() ).resolves.toBeUndefined();
|
|
237
|
-
|
|
238
|
-
expect( startMock ).toHaveBeenCalledOnce();
|
|
239
|
-
expect( sleepMock ).toHaveBeenCalledOnce();
|
|
240
|
-
} );
|
|
241
|
-
|
|
242
|
-
it( 'stops retrying when interrupted during the backoff', async () => {
|
|
243
|
-
startMock.mockRejectedValue( new Error( 'start failed' ) );
|
|
244
|
-
const publisher = createPublisher();
|
|
245
|
-
sleepMock.mockImplementation( async () => {
|
|
246
|
-
publisher.interrupt();
|
|
247
|
-
} );
|
|
248
|
-
|
|
249
|
-
await expect( publisher.run() ).resolves.toBeUndefined();
|
|
250
|
-
|
|
251
|
-
expect( startMock ).toHaveBeenCalledOnce();
|
|
252
|
-
} );
|
|
253
|
-
|
|
254
|
-
it( 'still starts when the abort lands between terminating and starting', async () => {
|
|
255
|
-
const controller = new AbortController();
|
|
256
|
-
describeMock.mockResolvedValue( running( 'old-hash' ) );
|
|
257
|
-
terminateMock.mockImplementation( async () => controller.abort() );
|
|
258
|
-
|
|
259
|
-
await createPublisher( controller.signal ).run();
|
|
260
|
-
|
|
261
|
-
expect( terminateMock ).toHaveBeenCalledOnce();
|
|
262
|
-
expect( startMock ).toHaveBeenCalledWith( 'catalog', startArguments );
|
|
263
|
-
} );
|
|
264
|
-
} );
|
|
265
|
-
|
|
266
|
-
describe( 'lifecycle', () => {
|
|
267
|
-
it( 'reports running while the sequence is pending', async () => {
|
|
268
|
-
const deferred = {};
|
|
269
|
-
describeMock.mockReturnValue( new Promise( resolve => {
|
|
270
|
-
deferred.resolve = resolve;
|
|
271
|
-
} ) );
|
|
272
|
-
const publisher = createPublisher();
|
|
273
|
-
|
|
274
|
-
const run = publisher.run();
|
|
275
|
-
await flushPromises();
|
|
276
|
-
|
|
277
|
-
expect( publisher.running ).toBe( true );
|
|
278
|
-
|
|
279
|
-
deferred.resolve( running( catalogHash ) );
|
|
280
|
-
await run;
|
|
281
|
-
|
|
282
|
-
expect( publisher.running ).toBe( false );
|
|
283
|
-
} );
|
|
284
|
-
|
|
285
|
-
it( 'returns the same execution and publishes once when run more than once', async () => {
|
|
286
|
-
const publisher = createPublisher();
|
|
287
|
-
|
|
288
|
-
const first = publisher.run();
|
|
289
|
-
const second = publisher.run();
|
|
290
|
-
|
|
291
|
-
expect( second ).toBe( first );
|
|
292
|
-
|
|
293
|
-
await first;
|
|
294
|
-
|
|
295
|
-
expect( describeMock ).toHaveBeenCalledOnce();
|
|
296
|
-
expect( startMock ).toHaveBeenCalledOnce();
|
|
297
|
-
} );
|
|
298
|
-
|
|
299
|
-
it( 'resolves when interrupted before running', async () => {
|
|
300
|
-
const publisher = createPublisher();
|
|
301
|
-
|
|
302
|
-
await expect( publisher.interrupt() ).resolves.toBeUndefined();
|
|
303
|
-
|
|
304
|
-
expect( publisher.running ).toBe( false );
|
|
305
|
-
expect( describeMock ).not.toHaveBeenCalled();
|
|
306
|
-
} );
|
|
307
|
-
|
|
308
|
-
it( 'reports the failure when interrupting after a rejected run', async () => {
|
|
309
|
-
startMock.mockRejectedValue( new Error( 'start failed' ) );
|
|
310
|
-
const publisher = createPublisher();
|
|
311
|
-
|
|
312
|
-
await expect( publisher.run() ).rejects.toThrow( 'start failed' );
|
|
313
|
-
|
|
314
|
-
await expect( publisher.interrupt() ).rejects.toThrow( 'start failed' );
|
|
315
|
-
} );
|
|
316
|
-
} );
|
|
317
|
-
} );
|
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
// Provide the same symbol to the module under test and to the test
|
|
5
|
-
const METADATA_ACCESS_SYMBOL = Symbol( '__metadata' );
|
|
6
|
-
vi.mock( '#consts', () => ( {
|
|
7
|
-
METADATA_ACCESS_SYMBOL
|
|
8
|
-
} ) );
|
|
9
|
-
|
|
10
|
-
describe( 'createCatalog', () => {
|
|
11
|
-
it( 'builds catalog with activities grouped by workflow path and returns Catalog with CatalogWorkflow entries', async () => {
|
|
12
|
-
const { createCatalog } = await import( './index.js' );
|
|
13
|
-
|
|
14
|
-
const workflows = [
|
|
15
|
-
{
|
|
16
|
-
name: 'flow1',
|
|
17
|
-
path: '/flows/flow1/workflow.js',
|
|
18
|
-
description: 'desc-flow1',
|
|
19
|
-
inputSchema: z.object( { in: z.literal( 'f1' ) } ),
|
|
20
|
-
outputSchema: z.object( { out: z.literal( 'f1' ) } )
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
name: 'flow2',
|
|
24
|
-
path: '/flows/flow2/workflow.js',
|
|
25
|
-
description: 'desc-flow2',
|
|
26
|
-
inputSchema: z.object( { in: z.literal( 'f2' ) } ),
|
|
27
|
-
outputSchema: z.object( { out: z.literal( 'f2' ) } )
|
|
28
|
-
}
|
|
29
|
-
];
|
|
30
|
-
|
|
31
|
-
const activity1 = () => {};
|
|
32
|
-
activity1[METADATA_ACCESS_SYMBOL] = {
|
|
33
|
-
name: 'A1',
|
|
34
|
-
path: '/flows/flow1#A1',
|
|
35
|
-
description: 'desc-a1',
|
|
36
|
-
inputSchema: z.object( { in: z.literal( 'a1' ) } ),
|
|
37
|
-
outputSchema: z.object( { out: z.literal( 'a1' ) } )
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const activity2 = () => {};
|
|
41
|
-
activity2[METADATA_ACCESS_SYMBOL] = {
|
|
42
|
-
name: 'A2',
|
|
43
|
-
path: '/flows/flow1#A2',
|
|
44
|
-
description: 'desc-a2',
|
|
45
|
-
inputSchema: z.object( { in: z.literal( 'a2' ) } ),
|
|
46
|
-
outputSchema: z.object( { out: z.literal( 'a2' ) } )
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const activity3 = () => {};
|
|
50
|
-
activity3[METADATA_ACCESS_SYMBOL] = {
|
|
51
|
-
name: 'B1',
|
|
52
|
-
path: '/flows/flow2#B1',
|
|
53
|
-
description: 'desc-b1',
|
|
54
|
-
inputSchema: z.object( { in: z.literal( 'b1' ) } ),
|
|
55
|
-
outputSchema: z.object( { out: z.literal( 'b1' ) } )
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const activity4 = () => {};
|
|
59
|
-
activity4[METADATA_ACCESS_SYMBOL] = {
|
|
60
|
-
name: 'X',
|
|
61
|
-
path: '/other#X',
|
|
62
|
-
description: 'desc-x',
|
|
63
|
-
inputSchema: z.object( { in: z.literal( 'x' ) } ),
|
|
64
|
-
outputSchema: z.object( { out: z.literal( 'x' ) } )
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
const activities = {
|
|
68
|
-
'/flows/flow1#A1': activity1,
|
|
69
|
-
'/flows/flow1#A2': activity2,
|
|
70
|
-
'/flows/flow2#B1': activity3,
|
|
71
|
-
'/other#X': activity4
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
const catalog = createCatalog( { workflows, activities } );
|
|
75
|
-
|
|
76
|
-
const mapped = catalog.workflows.map( w => ( {
|
|
77
|
-
name: w.name,
|
|
78
|
-
path: w.path,
|
|
79
|
-
description: w.description,
|
|
80
|
-
inputSchema: w.inputSchema,
|
|
81
|
-
outputSchema: w.outputSchema,
|
|
82
|
-
activities: w.activities.map( a => ( {
|
|
83
|
-
name: a.name,
|
|
84
|
-
description: a.description,
|
|
85
|
-
inputSchema: a.inputSchema,
|
|
86
|
-
outputSchema: a.outputSchema
|
|
87
|
-
} ) )
|
|
88
|
-
} ) );
|
|
89
|
-
|
|
90
|
-
expect( mapped ).toEqual( [
|
|
91
|
-
{
|
|
92
|
-
name: 'flow1',
|
|
93
|
-
path: '/flows/flow1/workflow.js',
|
|
94
|
-
description: 'desc-flow1',
|
|
95
|
-
inputSchema: {
|
|
96
|
-
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
97
|
-
type: 'object',
|
|
98
|
-
properties: { in: { type: 'string', const: 'f1' } },
|
|
99
|
-
required: [ 'in' ],
|
|
100
|
-
additionalProperties: false
|
|
101
|
-
},
|
|
102
|
-
outputSchema: {
|
|
103
|
-
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
104
|
-
type: 'object',
|
|
105
|
-
properties: { out: { type: 'string', const: 'f1' } },
|
|
106
|
-
required: [ 'out' ],
|
|
107
|
-
additionalProperties: false
|
|
108
|
-
},
|
|
109
|
-
activities: [
|
|
110
|
-
{
|
|
111
|
-
name: 'A1',
|
|
112
|
-
description: 'desc-a1',
|
|
113
|
-
inputSchema: {
|
|
114
|
-
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
115
|
-
type: 'object',
|
|
116
|
-
properties: { in: { type: 'string', const: 'a1' } },
|
|
117
|
-
required: [ 'in' ],
|
|
118
|
-
additionalProperties: false
|
|
119
|
-
},
|
|
120
|
-
outputSchema: {
|
|
121
|
-
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
122
|
-
type: 'object',
|
|
123
|
-
properties: { out: { type: 'string', const: 'a1' } },
|
|
124
|
-
required: [ 'out' ],
|
|
125
|
-
additionalProperties: false
|
|
126
|
-
}
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
name: 'A2',
|
|
130
|
-
description: 'desc-a2',
|
|
131
|
-
inputSchema: {
|
|
132
|
-
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
133
|
-
type: 'object',
|
|
134
|
-
properties: { in: { type: 'string', const: 'a2' } },
|
|
135
|
-
required: [ 'in' ],
|
|
136
|
-
additionalProperties: false
|
|
137
|
-
},
|
|
138
|
-
outputSchema: {
|
|
139
|
-
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
140
|
-
type: 'object',
|
|
141
|
-
properties: { out: { type: 'string', const: 'a2' } },
|
|
142
|
-
required: [ 'out' ],
|
|
143
|
-
additionalProperties: false
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
]
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
name: 'flow2',
|
|
150
|
-
path: '/flows/flow2/workflow.js',
|
|
151
|
-
description: 'desc-flow2',
|
|
152
|
-
inputSchema: {
|
|
153
|
-
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
154
|
-
type: 'object',
|
|
155
|
-
properties: { in: { type: 'string', const: 'f2' } },
|
|
156
|
-
required: [ 'in' ],
|
|
157
|
-
additionalProperties: false
|
|
158
|
-
},
|
|
159
|
-
outputSchema: {
|
|
160
|
-
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
161
|
-
type: 'object',
|
|
162
|
-
properties: { out: { type: 'string', const: 'f2' } },
|
|
163
|
-
required: [ 'out' ],
|
|
164
|
-
additionalProperties: false
|
|
165
|
-
},
|
|
166
|
-
activities: [
|
|
167
|
-
{
|
|
168
|
-
name: 'B1',
|
|
169
|
-
description: 'desc-b1',
|
|
170
|
-
inputSchema: {
|
|
171
|
-
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
172
|
-
type: 'object',
|
|
173
|
-
properties: { in: { type: 'string', const: 'b1' } },
|
|
174
|
-
required: [ 'in' ],
|
|
175
|
-
additionalProperties: false
|
|
176
|
-
},
|
|
177
|
-
outputSchema: {
|
|
178
|
-
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
179
|
-
type: 'object',
|
|
180
|
-
properties: { out: { type: 'string', const: 'b1' } },
|
|
181
|
-
required: [ 'out' ],
|
|
182
|
-
additionalProperties: false
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
]
|
|
186
|
-
}
|
|
187
|
-
] );
|
|
188
|
-
|
|
189
|
-
// Original inputs are not mutated
|
|
190
|
-
expect( workflows[0].path ).toBe( '/flows/flow1/workflow.js' );
|
|
191
|
-
expect( workflows[1].path ).toBe( '/flows/flow2/workflow.js' );
|
|
192
|
-
} );
|
|
193
|
-
|
|
194
|
-
it( 'includes aliases in catalog workflow entries', async () => {
|
|
195
|
-
const { createCatalog } = await import( './index.js' );
|
|
196
|
-
|
|
197
|
-
const workflows = [
|
|
198
|
-
{
|
|
199
|
-
name: 'flow1',
|
|
200
|
-
path: '/flows/flow1/workflow.js',
|
|
201
|
-
description: 'desc-flow1',
|
|
202
|
-
aliases: [ 'flow1_old', 'flow1_legacy' ]
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
name: 'flow2',
|
|
206
|
-
path: '/flows/flow2/workflow.js',
|
|
207
|
-
description: 'desc-flow2'
|
|
208
|
-
}
|
|
209
|
-
];
|
|
210
|
-
|
|
211
|
-
const catalog = createCatalog( { workflows, activities: {} } );
|
|
212
|
-
|
|
213
|
-
expect( catalog.workflows[0].aliases ).toEqual( [ 'flow1_old', 'flow1_legacy' ] );
|
|
214
|
-
expect( catalog.workflows[1].aliases ).toEqual( [] );
|
|
215
|
-
expect( catalog.workflowNames ).toEqual( {
|
|
216
|
-
flow1: 'flow1',
|
|
217
|
-
flow1_old: 'flow1',
|
|
218
|
-
flow1_legacy: 'flow1',
|
|
219
|
-
flow2: 'flow2'
|
|
220
|
-
} );
|
|
221
|
-
} );
|
|
222
|
-
} );
|