@outputai/core 0.7.1-next.de30052.0 → 0.8.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 (77) 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/interface/evaluator.js +7 -20
  6. package/src/interface/evaluator.spec.js +117 -1
  7. package/src/interface/step.js +8 -9
  8. package/src/interface/step.spec.js +124 -0
  9. package/src/interface/validations/index.js +108 -0
  10. package/src/interface/validations/index.spec.js +182 -0
  11. package/src/interface/validations/schemas.js +113 -0
  12. package/src/interface/validations/schemas.spec.js +209 -0
  13. package/src/interface/webhook.js +1 -1
  14. package/src/interface/webhook.spec.js +1 -1
  15. package/src/interface/workflow.d.ts +10 -9
  16. package/src/interface/workflow.js +76 -164
  17. package/src/interface/workflow.spec.js +637 -521
  18. package/src/interface/workflow_activity_options.js +16 -0
  19. package/src/interface/workflow_utils.js +1 -1
  20. package/src/interface/zod_integration.spec.js +2 -2
  21. package/src/internal_utils/aggregations.js +0 -10
  22. package/src/internal_utils/aggregations.spec.js +1 -48
  23. package/src/internal_utils/errors.js +14 -8
  24. package/src/internal_utils/errors.spec.js +73 -27
  25. package/src/utils/index.d.ts +19 -0
  26. package/src/utils/utils.js +53 -0
  27. package/src/utils/utils.spec.js +105 -1
  28. package/src/worker/bundle.js +26 -0
  29. package/src/worker/bundle.spec.js +53 -0
  30. package/src/worker/bundler_options.js +1 -1
  31. package/src/worker/bundler_options.spec.js +1 -1
  32. package/src/worker/catalog_workflow/catalog_job.js +148 -0
  33. package/src/worker/catalog_workflow/catalog_job.spec.js +232 -0
  34. package/src/worker/check.js +24 -0
  35. package/src/worker/connection_monitor.js +112 -0
  36. package/src/worker/connection_monitor.spec.js +199 -0
  37. package/src/worker/index.js +146 -41
  38. package/src/worker/index.spec.js +281 -109
  39. package/src/worker/interceptors/activity.js +7 -24
  40. package/src/worker/interceptors/activity.spec.js +97 -66
  41. package/src/worker/interceptors/index.js +4 -7
  42. package/src/worker/interceptors/modules.js +15 -0
  43. package/src/worker/interceptors/workflow.js +6 -8
  44. package/src/worker/interceptors/workflow.spec.js +49 -42
  45. package/src/worker/interruption.js +33 -0
  46. package/src/worker/interruption.spec.js +98 -0
  47. package/src/worker/loader/activities.js +75 -0
  48. package/src/worker/loader/activities.spec.js +213 -0
  49. package/src/worker/loader/hooks.js +28 -0
  50. package/src/worker/loader/hooks.spec.js +64 -0
  51. package/src/worker/loader/matchers.js +46 -0
  52. package/src/worker/loader/matchers.spec.js +140 -0
  53. package/src/worker/{loader_tools.js → loader/tools.js} +19 -67
  54. package/src/worker/{loader_tools.spec.js → loader/tools.spec.js} +53 -85
  55. package/src/worker/loader/workflows.js +82 -0
  56. package/src/worker/loader/workflows.spec.js +256 -0
  57. package/src/worker/{setup_telemetry.js → telemetry.js} +9 -4
  58. package/src/worker/{setup_telemetry.spec.js → telemetry.spec.js} +3 -3
  59. package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.js +5 -109
  60. package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.spec.js +31 -103
  61. package/src/worker/webpack_loaders/workflow_rewriter/index.mjs +5 -6
  62. package/src/worker/webpack_loaders/workflow_rewriter/index.spec.js +11 -83
  63. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.js +8 -11
  64. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.spec.js +9 -9
  65. package/src/interface/validations/runtime.js +0 -20
  66. package/src/interface/validations/runtime.spec.js +0 -29
  67. package/src/interface/validations/schema_utils.js +0 -8
  68. package/src/interface/validations/schema_utils.spec.js +0 -67
  69. package/src/interface/validations/static.js +0 -137
  70. package/src/interface/validations/static.spec.js +0 -397
  71. package/src/interface/workflow.replay_compatibility.spec.js +0 -254
  72. package/src/worker/loader.js +0 -202
  73. package/src/worker/loader.spec.js +0 -498
  74. package/src/worker/shutdown.js +0 -26
  75. package/src/worker/shutdown.spec.js +0 -82
  76. package/src/worker/start_catalog.js +0 -96
  77. 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
- } );