@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.
- 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 +53 -0
- package/src/utils/utils.spec.js +105 -1
- package/src/worker/bundle.js +26 -0
- package/src/worker/bundle.spec.js +53 -0
- package/src/worker/bundler_options.js +1 -1
- package/src/worker/bundler_options.spec.js +1 -1
- 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 +146 -41
- package/src/worker/index.spec.js +281 -109
- 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 +6 -8
- package/src/worker/interceptors/workflow.spec.js +49 -42
- package/src/worker/interruption.js +33 -0
- package/src/worker/interruption.spec.js +98 -0
- package/src/worker/loader/activities.js +75 -0
- package/src/worker/loader/activities.spec.js +213 -0
- package/src/worker/loader/hooks.js +28 -0
- package/src/worker/loader/hooks.spec.js +64 -0
- package/src/worker/loader/matchers.js +46 -0
- package/src/worker/loader/matchers.spec.js +140 -0
- package/src/worker/{loader_tools.js → loader/tools.js} +19 -67
- package/src/worker/{loader_tools.spec.js → loader/tools.spec.js} +53 -85
- package/src/worker/loader/workflows.js +82 -0
- package/src/worker/loader/workflows.spec.js +256 -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/loader.js +0 -202
- package/src/worker/loader.spec.js +0 -498
- 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
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { Client, WorkflowNotFoundError } from '@temporalio/client';
|
|
2
|
-
import { WorkflowExecutionAlreadyStartedError, WorkflowIdConflictPolicy } from '@temporalio/common';
|
|
3
|
-
import { WORKFLOW_CATALOG } from '#consts';
|
|
4
|
-
import { catalogId, taskQueue } from './configs.js';
|
|
5
|
-
import { createChildLogger } from '#logger';
|
|
6
|
-
|
|
7
|
-
const log = createChildLogger( 'Catalog' );
|
|
8
|
-
|
|
9
|
-
// Note, functions don't log on "WorkflowNotFound" errors,
|
|
10
|
-
// because they happen when the catalog is not running at all.
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Check if the currently running catalog has the same hash as the passed argument.
|
|
14
|
-
* @param {import('@temporalio/client').WorkflowHandle} handle
|
|
15
|
-
* @param {string} hash
|
|
16
|
-
* @returns {boolean}
|
|
17
|
-
*/
|
|
18
|
-
const checkCatalogIsTheSame = async ( handle, hash ) => {
|
|
19
|
-
try {
|
|
20
|
-
log.info( 'Checking running catalog hash against worker hash....' );
|
|
21
|
-
const runningHash = await handle.query( 'get_hash' );
|
|
22
|
-
return runningHash === hash;
|
|
23
|
-
} catch ( error ) {
|
|
24
|
-
if ( !( error instanceof WorkflowNotFoundError ) ) {
|
|
25
|
-
log.warn( 'Error retrieving catalog hash', { error } );
|
|
26
|
-
}
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Check if the catalog workflow is running.
|
|
33
|
-
* @param {import('@temporalio/client').WorkflowHandle} handle
|
|
34
|
-
* @returns
|
|
35
|
-
*/
|
|
36
|
-
const checkCatalogRunning = async handle => {
|
|
37
|
-
try {
|
|
38
|
-
log.info( 'Checking if the catalog workflow is running....' );
|
|
39
|
-
const description = await handle.describe();
|
|
40
|
-
return !description.closeTime;
|
|
41
|
-
} catch ( error ) {
|
|
42
|
-
if ( !( error instanceof WorkflowNotFoundError ) ) {
|
|
43
|
-
log.warn( 'Error describing catalog workflow', { error } );
|
|
44
|
-
}
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Complete previous running catalog workflow.
|
|
51
|
-
* @param {import('@temporalio/client').WorkflowHandle} handle
|
|
52
|
-
*/
|
|
53
|
-
const completePreviousCatalog = async handle => {
|
|
54
|
-
try {
|
|
55
|
-
log.info( 'Completing previous catalog workflow...' );
|
|
56
|
-
await handle.executeUpdate( 'complete', { args: [] } );
|
|
57
|
-
} catch ( error ) {
|
|
58
|
-
if ( !( error instanceof WorkflowNotFoundError ) ) {
|
|
59
|
-
log.warn( 'Error completing previous catalog workflow', { error } );
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
export const startCatalog = async ( { connection, namespace, catalog, catalogHash } ) => {
|
|
65
|
-
const client = new Client( { connection, namespace } );
|
|
66
|
-
const handle = client.workflow.getHandle( catalogId );
|
|
67
|
-
|
|
68
|
-
if ( await checkCatalogRunning( handle ) ) {
|
|
69
|
-
if ( await checkCatalogIsTheSame( handle, catalogHash ) ) {
|
|
70
|
-
log.info( 'Current catalog workflow hash matches worker, restart skipped' );
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
await completePreviousCatalog( handle );
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const startArguments = {
|
|
77
|
-
taskQueue,
|
|
78
|
-
workflowId: catalogId,
|
|
79
|
-
workflowIdConflictPolicy: WorkflowIdConflictPolicy.FAIL,
|
|
80
|
-
args: [ catalog, catalogHash ]
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
log.info( 'Starting catalog workflow...' );
|
|
84
|
-
try {
|
|
85
|
-
await client.workflow.start( WORKFLOW_CATALOG, startArguments );
|
|
86
|
-
} catch ( error ) {
|
|
87
|
-
// if the error was caused by the catalog existing and its hash is the same as the one from the worker, just ignore the error
|
|
88
|
-
if ( error instanceof WorkflowExecutionAlreadyStartedError && await checkCatalogIsTheSame( handle, catalogHash ) ) {
|
|
89
|
-
log.info( 'Ignoring start error: it failed because execution already started but catalog hash matches worker' );
|
|
90
|
-
} else {
|
|
91
|
-
throw error;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
log.info( 'Startup completed' );
|
|
96
|
-
};
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { WorkflowNotFoundError } from '@temporalio/client';
|
|
3
|
-
import { WorkflowExecutionAlreadyStartedError } from '@temporalio/common';
|
|
4
|
-
|
|
5
|
-
const mockLog = { info: vi.fn(), warn: vi.fn(), error: vi.fn() };
|
|
6
|
-
vi.mock( '#logger', () => ( { createChildLogger: () => mockLog } ) );
|
|
7
|
-
|
|
8
|
-
vi.mock( '#consts', () => ( { WORKFLOW_CATALOG: 'catalog' } ) );
|
|
9
|
-
|
|
10
|
-
const catalogId = 'test-catalog';
|
|
11
|
-
const taskQueue = 'test-queue';
|
|
12
|
-
vi.mock( './configs.js', () => ( { catalogId, taskQueue } ) );
|
|
13
|
-
|
|
14
|
-
const describeMock = vi.fn();
|
|
15
|
-
const queryMock = vi.fn();
|
|
16
|
-
const executeUpdateMock = vi.fn();
|
|
17
|
-
const workflowStartMock = vi.fn().mockResolvedValue( undefined );
|
|
18
|
-
vi.mock( '@temporalio/client', async importOriginal => {
|
|
19
|
-
const actual = await importOriginal();
|
|
20
|
-
return {
|
|
21
|
-
...actual,
|
|
22
|
-
Client: vi.fn().mockImplementation( function () {
|
|
23
|
-
return {
|
|
24
|
-
workflow: {
|
|
25
|
-
start: workflowStartMock,
|
|
26
|
-
getHandle: () => ( { describe: describeMock, query: queryMock, executeUpdate: executeUpdateMock } )
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
} )
|
|
30
|
-
};
|
|
31
|
-
} );
|
|
32
|
-
|
|
33
|
-
describe( 'worker/start_catalog', () => {
|
|
34
|
-
const mockConnection = {};
|
|
35
|
-
const namespace = 'default';
|
|
36
|
-
const catalog = { workflows: [], activities: {} };
|
|
37
|
-
const catalogHash = 'catalog-hash';
|
|
38
|
-
|
|
39
|
-
beforeEach( () => {
|
|
40
|
-
mockLog.info.mockClear();
|
|
41
|
-
mockLog.warn.mockClear();
|
|
42
|
-
mockLog.error.mockClear();
|
|
43
|
-
describeMock.mockReset();
|
|
44
|
-
queryMock.mockReset();
|
|
45
|
-
executeUpdateMock.mockReset();
|
|
46
|
-
workflowStartMock.mockReset();
|
|
47
|
-
workflowStartMock.mockResolvedValue( undefined );
|
|
48
|
-
} );
|
|
49
|
-
|
|
50
|
-
it( 'when previous catalog still running with different hash: completes it then starts catalog workflow', async () => {
|
|
51
|
-
describeMock.mockResolvedValue( { closeTime: undefined } );
|
|
52
|
-
queryMock.mockResolvedValue( 'old-catalog-hash' );
|
|
53
|
-
executeUpdateMock.mockResolvedValue( undefined );
|
|
54
|
-
|
|
55
|
-
const { startCatalog } = await import( './start_catalog.js' );
|
|
56
|
-
await startCatalog( { connection: mockConnection, namespace, catalog, catalogHash } );
|
|
57
|
-
|
|
58
|
-
expect( describeMock ).toHaveBeenCalled();
|
|
59
|
-
expect( queryMock ).toHaveBeenCalledWith( 'get_hash' );
|
|
60
|
-
expect( mockLog.info ).toHaveBeenCalledWith( 'Completing previous catalog workflow...' );
|
|
61
|
-
expect( executeUpdateMock ).toHaveBeenCalledWith( 'complete', { args: [] } );
|
|
62
|
-
expect( mockLog.info ).toHaveBeenCalledWith( 'Starting catalog workflow...' );
|
|
63
|
-
expect( workflowStartMock ).toHaveBeenCalledWith( 'catalog', {
|
|
64
|
-
taskQueue,
|
|
65
|
-
workflowId: catalogId,
|
|
66
|
-
workflowIdConflictPolicy: 'FAIL',
|
|
67
|
-
args: [ catalog, catalogHash ]
|
|
68
|
-
} );
|
|
69
|
-
} );
|
|
70
|
-
|
|
71
|
-
it( 'when previous catalog still running with same hash: keeps existing catalog workflow', async () => {
|
|
72
|
-
describeMock.mockResolvedValue( { closeTime: undefined } );
|
|
73
|
-
queryMock.mockResolvedValue( catalogHash );
|
|
74
|
-
|
|
75
|
-
const { startCatalog } = await import( './start_catalog.js' );
|
|
76
|
-
await startCatalog( { connection: mockConnection, namespace, catalog, catalogHash } );
|
|
77
|
-
|
|
78
|
-
expect( describeMock ).toHaveBeenCalled();
|
|
79
|
-
expect( queryMock ).toHaveBeenCalledWith( 'get_hash' );
|
|
80
|
-
expect( mockLog.info ).toHaveBeenCalledWith( 'Current catalog workflow hash matches worker, restart skipped' );
|
|
81
|
-
expect( executeUpdateMock ).not.toHaveBeenCalled();
|
|
82
|
-
expect( workflowStartMock ).not.toHaveBeenCalled();
|
|
83
|
-
} );
|
|
84
|
-
|
|
85
|
-
it( 'when no previous catalog: ignores and starts catalog workflow', async () => {
|
|
86
|
-
describeMock.mockRejectedValue( new WorkflowNotFoundError( 'not found' ) );
|
|
87
|
-
|
|
88
|
-
const { startCatalog } = await import( './start_catalog.js' );
|
|
89
|
-
await startCatalog( { connection: mockConnection, namespace, catalog, catalogHash } );
|
|
90
|
-
|
|
91
|
-
expect( describeMock ).toHaveBeenCalled();
|
|
92
|
-
expect( queryMock ).not.toHaveBeenCalled();
|
|
93
|
-
expect( mockLog.warn ).not.toHaveBeenCalled();
|
|
94
|
-
expect( mockLog.info ).toHaveBeenCalledWith( 'Starting catalog workflow...' );
|
|
95
|
-
expect( executeUpdateMock ).not.toHaveBeenCalled();
|
|
96
|
-
expect( workflowStartMock ).toHaveBeenCalledWith( 'catalog', {
|
|
97
|
-
taskQueue,
|
|
98
|
-
workflowId: catalogId,
|
|
99
|
-
workflowIdConflictPolicy: 'FAIL',
|
|
100
|
-
args: [ catalog, catalogHash ]
|
|
101
|
-
} );
|
|
102
|
-
} );
|
|
103
|
-
|
|
104
|
-
it( 'when previous catalog already closed: skips complete and starts catalog workflow', async () => {
|
|
105
|
-
describeMock.mockResolvedValue( { closeTime: '2024-01-01T00:00:00Z' } );
|
|
106
|
-
|
|
107
|
-
const { startCatalog } = await import( './start_catalog.js' );
|
|
108
|
-
await startCatalog( { connection: mockConnection, namespace, catalog, catalogHash } );
|
|
109
|
-
|
|
110
|
-
expect( describeMock ).toHaveBeenCalled();
|
|
111
|
-
expect( queryMock ).not.toHaveBeenCalled();
|
|
112
|
-
expect( mockLog.info ).not.toHaveBeenCalledWith( 'Completing previous catalog workflow...' );
|
|
113
|
-
expect( executeUpdateMock ).not.toHaveBeenCalled();
|
|
114
|
-
expect( mockLog.info ).toHaveBeenCalledWith( 'Starting catalog workflow...' );
|
|
115
|
-
expect( workflowStartMock ).toHaveBeenCalledWith( 'catalog', {
|
|
116
|
-
taskQueue,
|
|
117
|
-
workflowId: catalogId,
|
|
118
|
-
workflowIdConflictPolicy: 'FAIL',
|
|
119
|
-
args: [ catalog, catalogHash ]
|
|
120
|
-
} );
|
|
121
|
-
} );
|
|
122
|
-
|
|
123
|
-
it( 'when describe or complete fails with other error: logs warn and still starts catalog workflow', async () => {
|
|
124
|
-
describeMock.mockResolvedValue( { closeTime: undefined } );
|
|
125
|
-
queryMock.mockResolvedValue( 'old-catalog-hash' );
|
|
126
|
-
executeUpdateMock.mockRejectedValue( new Error( 'Connection refused' ) );
|
|
127
|
-
|
|
128
|
-
const { startCatalog } = await import( './start_catalog.js' );
|
|
129
|
-
await startCatalog( { connection: mockConnection, namespace, catalog, catalogHash } );
|
|
130
|
-
|
|
131
|
-
expect( describeMock ).toHaveBeenCalled();
|
|
132
|
-
expect( queryMock ).toHaveBeenCalledWith( 'get_hash' );
|
|
133
|
-
expect( executeUpdateMock ).toHaveBeenCalledWith( 'complete', { args: [] } );
|
|
134
|
-
expect( mockLog.warn ).toHaveBeenCalledWith( 'Error completing previous catalog workflow', {
|
|
135
|
-
error: expect.any( Error )
|
|
136
|
-
} );
|
|
137
|
-
expect( mockLog.info ).toHaveBeenCalledWith( 'Starting catalog workflow...' );
|
|
138
|
-
expect( workflowStartMock ).toHaveBeenCalledWith( 'catalog', {
|
|
139
|
-
taskQueue,
|
|
140
|
-
workflowId: catalogId,
|
|
141
|
-
workflowIdConflictPolicy: 'FAIL',
|
|
142
|
-
args: [ catalog, catalogHash ]
|
|
143
|
-
} );
|
|
144
|
-
} );
|
|
145
|
-
|
|
146
|
-
it( 'when another worker starts matching catalog concurrently: ignores already-started error', async () => {
|
|
147
|
-
const alreadyStartedError = new WorkflowExecutionAlreadyStartedError( 'already started', catalogId, 'catalog' );
|
|
148
|
-
describeMock.mockRejectedValue( new WorkflowNotFoundError( 'not found' ) );
|
|
149
|
-
workflowStartMock.mockRejectedValue( alreadyStartedError );
|
|
150
|
-
queryMock.mockResolvedValue( catalogHash );
|
|
151
|
-
|
|
152
|
-
const { startCatalog } = await import( './start_catalog.js' );
|
|
153
|
-
await startCatalog( { connection: mockConnection, namespace, catalog, catalogHash } );
|
|
154
|
-
|
|
155
|
-
expect( workflowStartMock ).toHaveBeenCalledWith( 'catalog', {
|
|
156
|
-
taskQueue,
|
|
157
|
-
workflowId: catalogId,
|
|
158
|
-
workflowIdConflictPolicy: 'FAIL',
|
|
159
|
-
args: [ catalog, catalogHash ]
|
|
160
|
-
} );
|
|
161
|
-
expect( queryMock ).toHaveBeenCalledWith( 'get_hash' );
|
|
162
|
-
expect( mockLog.info ).toHaveBeenCalledWith(
|
|
163
|
-
'Ignoring start error: it failed because execution already started but catalog hash matches worker'
|
|
164
|
-
);
|
|
165
|
-
} );
|
|
166
|
-
|
|
167
|
-
it( 'when another worker starts stale catalog concurrently: rethrows already-started error', async () => {
|
|
168
|
-
const alreadyStartedError = new WorkflowExecutionAlreadyStartedError( 'already started', catalogId, 'catalog' );
|
|
169
|
-
describeMock.mockRejectedValue( new WorkflowNotFoundError( 'not found' ) );
|
|
170
|
-
workflowStartMock.mockRejectedValue( alreadyStartedError );
|
|
171
|
-
queryMock.mockResolvedValue( 'old-catalog-hash' );
|
|
172
|
-
|
|
173
|
-
const { startCatalog } = await import( './start_catalog.js' );
|
|
174
|
-
await expect( startCatalog( { connection: mockConnection, namespace, catalog, catalogHash } ) )
|
|
175
|
-
.rejects.toBe( alreadyStartedError );
|
|
176
|
-
|
|
177
|
-
expect( queryMock ).toHaveBeenCalledWith( 'get_hash' );
|
|
178
|
-
} );
|
|
179
|
-
} );
|