@output.ai/core 0.0.16 → 0.1.1
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/README.md +16 -22
- package/package.json +19 -7
- package/src/consts.js +2 -11
- package/src/interface/evaluator.js +8 -4
- package/src/interface/step.js +1 -1
- package/src/interface/webhook.js +16 -4
- package/src/interface/workflow.js +28 -48
- package/src/internal_activities/index.js +3 -37
- package/src/tracing/index.d.ts +47 -0
- package/src/tracing/index.js +45 -0
- package/src/tracing/internal_interface.js +66 -0
- package/src/tracing/processors/local/index.js +50 -0
- package/src/tracing/processors/local/index.spec.js +67 -0
- package/src/tracing/processors/s3/index.js +51 -0
- package/src/tracing/processors/s3/index.spec.js +64 -0
- package/src/tracing/processors/s3/redis_client.js +19 -0
- package/src/tracing/processors/s3/redis_client.spec.js +50 -0
- package/src/tracing/processors/s3/s3_client.js +33 -0
- package/src/tracing/processors/s3/s3_client.spec.js +67 -0
- package/src/tracing/tools/build_trace_tree.js +76 -0
- package/src/tracing/tools/build_trace_tree.spec.js +99 -0
- package/src/tracing/tools/utils.js +28 -0
- package/src/tracing/tools/utils.spec.js +14 -0
- package/src/tracing/trace_engine.js +63 -0
- package/src/tracing/trace_engine.spec.js +91 -0
- package/src/utils.js +8 -0
- package/src/worker/catalog_workflow/index.js +2 -1
- package/src/worker/catalog_workflow/index.spec.js +6 -10
- package/src/worker/configs.js +24 -0
- package/src/worker/index.js +7 -8
- package/src/worker/interceptors/activity.js +15 -17
- package/src/worker/interceptors/workflow.js +18 -1
- package/src/worker/loader.js +40 -31
- package/src/worker/loader.spec.js +22 -29
- package/src/worker/loader_tools.js +63 -0
- package/src/worker/loader_tools.spec.js +85 -0
- package/src/worker/sinks.js +60 -10
- package/src/configs.js +0 -36
- package/src/configs.spec.js +0 -379
- package/src/worker/internal_utils.js +0 -60
- package/src/worker/internal_utils.spec.js +0 -134
- package/src/worker/tracer/index.js +0 -75
- package/src/worker/tracer/index.test.js +0 -102
- package/src/worker/tracer/tracer_tree.js +0 -85
- package/src/worker/tracer/tracer_tree.test.js +0 -115
- /package/src/{worker/async_storage.js → async_storage.js} +0 -0
package/src/configs.spec.js
DELETED
|
@@ -1,379 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
-
|
|
3
|
-
describe( 'configs', () => {
|
|
4
|
-
const originalEnv = process.env;
|
|
5
|
-
|
|
6
|
-
beforeEach( () => {
|
|
7
|
-
vi.resetModules();
|
|
8
|
-
process.env = { ...originalEnv };
|
|
9
|
-
} );
|
|
10
|
-
|
|
11
|
-
afterEach( () => {
|
|
12
|
-
process.env = originalEnv;
|
|
13
|
-
} );
|
|
14
|
-
|
|
15
|
-
describe( 'Environment Variable Validation', () => {
|
|
16
|
-
describe( 'TEMPORAL_ADDRESS', () => {
|
|
17
|
-
it( 'should accept string addresses', async () => {
|
|
18
|
-
process.env = {
|
|
19
|
-
TEMPORAL_ADDRESS: 'localhost:7233',
|
|
20
|
-
CATALOG_ID: 'test-catalog'
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const { worker } = await import( './configs.js' );
|
|
24
|
-
expect( worker.address ).toBe( 'localhost:7233' );
|
|
25
|
-
} );
|
|
26
|
-
|
|
27
|
-
it( 'should use default when omitted', async () => {
|
|
28
|
-
process.env = {
|
|
29
|
-
CATALOG_ID: 'test-catalog'
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const { worker } = await import( './configs.js' );
|
|
33
|
-
expect( worker.address ).toBe( 'localhost:7233' );
|
|
34
|
-
} );
|
|
35
|
-
|
|
36
|
-
it( 'should reject non-string values', async () => {
|
|
37
|
-
process.env = {
|
|
38
|
-
TEMPORAL_ADDRESS: '123',
|
|
39
|
-
CATALOG_ID: 'test-catalog'
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
// Convert to number to test non-string
|
|
43
|
-
const originalEnv = process.env.TEMPORAL_ADDRESS;
|
|
44
|
-
process.env.TEMPORAL_ADDRESS = 123;
|
|
45
|
-
|
|
46
|
-
await expect( import( './configs.js' ) ).rejects.toThrow();
|
|
47
|
-
|
|
48
|
-
process.env.TEMPORAL_ADDRESS = originalEnv;
|
|
49
|
-
} );
|
|
50
|
-
} );
|
|
51
|
-
|
|
52
|
-
describe( 'CATALOG_ID', () => {
|
|
53
|
-
it( 'should accept valid catalog IDs with letters and numbers', async () => {
|
|
54
|
-
process.env = {
|
|
55
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
56
|
-
CATALOG_ID: 'catalog123'
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const { worker } = await import( './configs.js' );
|
|
60
|
-
expect( worker.catalogId ).toBe( 'catalog123' );
|
|
61
|
-
expect( worker.taskQueue ).toBe( 'catalog123' );
|
|
62
|
-
} );
|
|
63
|
-
|
|
64
|
-
it( 'should accept catalog IDs with dots and hyphens', async () => {
|
|
65
|
-
process.env = {
|
|
66
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
67
|
-
CATALOG_ID: 'my.catalog-id'
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const { worker } = await import( './configs.js' );
|
|
71
|
-
expect( worker.catalogId ).toBe( 'my.catalog-id' );
|
|
72
|
-
} );
|
|
73
|
-
|
|
74
|
-
it( 'should accept catalog IDs with underscores', async () => {
|
|
75
|
-
process.env = {
|
|
76
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
77
|
-
CATALOG_ID: 'my_catalog_id'
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const { worker } = await import( './configs.js' );
|
|
81
|
-
expect( worker.catalogId ).toBe( 'my_catalog_id' );
|
|
82
|
-
} );
|
|
83
|
-
|
|
84
|
-
it( 'should accept catalog IDs with @ symbol', async () => {
|
|
85
|
-
process.env = {
|
|
86
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
87
|
-
CATALOG_ID: '@my-catalog'
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const { worker } = await import( './configs.js' );
|
|
91
|
-
expect( worker.catalogId ).toBe( '@my-catalog' );
|
|
92
|
-
} );
|
|
93
|
-
|
|
94
|
-
it( 'should reject catalog IDs with special characters', async () => {
|
|
95
|
-
process.env = {
|
|
96
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
97
|
-
CATALOG_ID: 'catalog!@#$'
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
await expect( import( './configs.js' ) ).rejects.toThrow();
|
|
101
|
-
} );
|
|
102
|
-
|
|
103
|
-
it( 'should reject when CATALOG_ID is missing', async () => {
|
|
104
|
-
process.env = {
|
|
105
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233'
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
await expect( import( './configs.js' ) ).rejects.toThrow();
|
|
109
|
-
} );
|
|
110
|
-
|
|
111
|
-
it( 'should reject empty CATALOG_ID', async () => {
|
|
112
|
-
process.env = {
|
|
113
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
114
|
-
CATALOG_ID: ''
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
await expect( import( './configs.js' ) ).rejects.toThrow();
|
|
118
|
-
} );
|
|
119
|
-
} );
|
|
120
|
-
|
|
121
|
-
describe( 'Optional Fields', () => {
|
|
122
|
-
it( 'should use default namespace when TEMPORAL_NAMESPACE is not provided', async () => {
|
|
123
|
-
process.env = {
|
|
124
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
125
|
-
CATALOG_ID: 'test-catalog'
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
const { worker } = await import( './configs.js' );
|
|
129
|
-
expect( worker.namespace ).toBe( 'default' );
|
|
130
|
-
} );
|
|
131
|
-
|
|
132
|
-
it( 'should use custom namespace when provided', async () => {
|
|
133
|
-
process.env = {
|
|
134
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
135
|
-
CATALOG_ID: 'test-catalog',
|
|
136
|
-
TEMPORAL_NAMESPACE: 'custom-namespace'
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
const { worker } = await import( './configs.js' );
|
|
140
|
-
expect( worker.namespace ).toBe( 'custom-namespace' );
|
|
141
|
-
} );
|
|
142
|
-
|
|
143
|
-
it( 'should handle TEMPORAL_API_KEY when provided', async () => {
|
|
144
|
-
process.env = {
|
|
145
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
146
|
-
CATALOG_ID: 'test-catalog',
|
|
147
|
-
TEMPORAL_API_KEY: 'secret-api-key'
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
const { worker } = await import( './configs.js' );
|
|
151
|
-
expect( worker.apiKey ).toBe( 'secret-api-key' );
|
|
152
|
-
} );
|
|
153
|
-
|
|
154
|
-
it( 'should handle missing TEMPORAL_API_KEY', async () => {
|
|
155
|
-
process.env = {
|
|
156
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
157
|
-
CATALOG_ID: 'test-catalog'
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
const { worker } = await import( './configs.js' );
|
|
161
|
-
expect( worker.apiKey ).toBeUndefined();
|
|
162
|
-
} );
|
|
163
|
-
|
|
164
|
-
it( 'should handle API_AUTH_KEY when provided', async () => {
|
|
165
|
-
process.env = {
|
|
166
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
167
|
-
CATALOG_ID: 'test-catalog',
|
|
168
|
-
API_AUTH_KEY: 'api-secret-key'
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
const { api } = await import( './configs.js' );
|
|
172
|
-
expect( api.authKey ).toBe( 'api-secret-key' );
|
|
173
|
-
} );
|
|
174
|
-
|
|
175
|
-
it( 'should handle missing API_AUTH_KEY', async () => {
|
|
176
|
-
process.env = {
|
|
177
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
178
|
-
CATALOG_ID: 'test-catalog'
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
const { api } = await import( './configs.js' );
|
|
182
|
-
expect( api.authKey ).toBeUndefined();
|
|
183
|
-
} );
|
|
184
|
-
|
|
185
|
-
it( 'should handle TRACING_ENABLED when true', async () => {
|
|
186
|
-
process.env = {
|
|
187
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
188
|
-
CATALOG_ID: 'test-catalog',
|
|
189
|
-
TRACING_ENABLED: 'true'
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
const { tracing } = await import( './configs.js' );
|
|
193
|
-
expect( tracing.enabled ).toBe( true );
|
|
194
|
-
} );
|
|
195
|
-
|
|
196
|
-
it( 'should handle TRACING_ENABLED when false', async () => {
|
|
197
|
-
process.env = {
|
|
198
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
199
|
-
CATALOG_ID: 'test-catalog',
|
|
200
|
-
TRACING_ENABLED: 'false'
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
const { tracing } = await import( './configs.js' );
|
|
204
|
-
expect( tracing.enabled ).toBe( false );
|
|
205
|
-
} );
|
|
206
|
-
|
|
207
|
-
it( 'should handle missing TRACING_ENABLED', async () => {
|
|
208
|
-
process.env = {
|
|
209
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
210
|
-
CATALOG_ID: 'test-catalog'
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
const { tracing } = await import( './configs.js' );
|
|
214
|
-
expect( tracing.enabled ).toBeUndefined();
|
|
215
|
-
} );
|
|
216
|
-
} );
|
|
217
|
-
} );
|
|
218
|
-
|
|
219
|
-
describe( 'Exported Config Objects', () => {
|
|
220
|
-
it( 'should export worker config with all properties', async () => {
|
|
221
|
-
process.env = {
|
|
222
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
223
|
-
CATALOG_ID: 'test-catalog',
|
|
224
|
-
TEMPORAL_NAMESPACE: 'test-namespace',
|
|
225
|
-
TEMPORAL_API_KEY: 'test-api-key'
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
const { worker } = await import( './configs.js' );
|
|
229
|
-
|
|
230
|
-
expect( worker ).toEqual( {
|
|
231
|
-
address: 'http://localhost:7233',
|
|
232
|
-
apiKey: 'test-api-key',
|
|
233
|
-
executionTimeout: '1m',
|
|
234
|
-
maxActivities: 100,
|
|
235
|
-
maxWorkflows: 100,
|
|
236
|
-
namespace: 'test-namespace',
|
|
237
|
-
taskQueue: 'test-catalog',
|
|
238
|
-
catalogId: 'test-catalog'
|
|
239
|
-
} );
|
|
240
|
-
} );
|
|
241
|
-
|
|
242
|
-
it( 'should export api config', async () => {
|
|
243
|
-
process.env = {
|
|
244
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
245
|
-
CATALOG_ID: 'test-catalog',
|
|
246
|
-
API_AUTH_KEY: 'test-auth-key'
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
const { api } = await import( './configs.js' );
|
|
250
|
-
|
|
251
|
-
expect( api ).toEqual( {
|
|
252
|
-
authKey: 'test-auth-key'
|
|
253
|
-
} );
|
|
254
|
-
} );
|
|
255
|
-
|
|
256
|
-
it( 'should export tracing config', async () => {
|
|
257
|
-
process.env = {
|
|
258
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
259
|
-
CATALOG_ID: 'test-catalog',
|
|
260
|
-
TRACING_ENABLED: 'true'
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
const { tracing } = await import( './configs.js' );
|
|
264
|
-
|
|
265
|
-
expect( tracing ).toEqual( {
|
|
266
|
-
enabled: true
|
|
267
|
-
} );
|
|
268
|
-
} );
|
|
269
|
-
|
|
270
|
-
it( 'should have correct static worker config values', async () => {
|
|
271
|
-
process.env = {
|
|
272
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
273
|
-
CATALOG_ID: 'test-catalog'
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
const { worker } = await import( './configs.js' );
|
|
277
|
-
|
|
278
|
-
expect( worker.executionTimeout ).toBe( '1m' );
|
|
279
|
-
expect( worker.maxActivities ).toBe( 100 );
|
|
280
|
-
expect( worker.maxWorkflows ).toBe( 100 );
|
|
281
|
-
} );
|
|
282
|
-
} );
|
|
283
|
-
|
|
284
|
-
describe( 'Error Handling', () => {
|
|
285
|
-
it( 'should throw InvalidEnvVarsErrors for invalid configuration', async () => {
|
|
286
|
-
process.env = {
|
|
287
|
-
TEMPORAL_ADDRESS: 'localhost:7233',
|
|
288
|
-
CATALOG_ID: 'invalid!@#'
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
await expect( import( './configs.js' ) ).rejects.toThrow();
|
|
292
|
-
} );
|
|
293
|
-
|
|
294
|
-
it( 'should handle multiple validation errors', async () => {
|
|
295
|
-
process.env = {
|
|
296
|
-
TEMPORAL_ADDRESS: 'localhost:7233',
|
|
297
|
-
CATALOG_ID: 'invalid!@#'
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
await expect( import( './configs.js' ) ).rejects.toThrow();
|
|
301
|
-
} );
|
|
302
|
-
} );
|
|
303
|
-
|
|
304
|
-
describe( 'Edge Cases', () => {
|
|
305
|
-
it( 'should handle environment variables with spaces', async () => {
|
|
306
|
-
process.env = {
|
|
307
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
308
|
-
CATALOG_ID: 'test-catalog',
|
|
309
|
-
TEMPORAL_NAMESPACE: ' custom-namespace '
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
const { worker } = await import( './configs.js' );
|
|
313
|
-
expect( worker.namespace ).toBe( ' custom-namespace ' );
|
|
314
|
-
} );
|
|
315
|
-
|
|
316
|
-
it( 'should handle very long catalog IDs', async () => {
|
|
317
|
-
process.env = {
|
|
318
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
319
|
-
CATALOG_ID: 'a'.repeat( 100 )
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
const { worker } = await import( './configs.js' );
|
|
323
|
-
expect( worker.catalogId ).toBe( 'a'.repeat( 100 ) );
|
|
324
|
-
} );
|
|
325
|
-
|
|
326
|
-
it( 'should handle URLs with ports outside typical range', async () => {
|
|
327
|
-
process.env = {
|
|
328
|
-
TEMPORAL_ADDRESS: 'http://localhost:65535',
|
|
329
|
-
CATALOG_ID: 'test-catalog'
|
|
330
|
-
};
|
|
331
|
-
|
|
332
|
-
const { worker } = await import( './configs.js' );
|
|
333
|
-
expect( worker.address ).toBe( 'http://localhost:65535' );
|
|
334
|
-
} );
|
|
335
|
-
|
|
336
|
-
it( 'should handle container names with numbers', async () => {
|
|
337
|
-
process.env = {
|
|
338
|
-
TEMPORAL_ADDRESS: 'temporal123:7233',
|
|
339
|
-
CATALOG_ID: 'test-catalog'
|
|
340
|
-
};
|
|
341
|
-
|
|
342
|
-
const { worker } = await import( './configs.js' );
|
|
343
|
-
expect( worker.address ).toBe( 'temporal123:7233' );
|
|
344
|
-
} );
|
|
345
|
-
|
|
346
|
-
it( 'should handle mixed case in catalog ID', async () => {
|
|
347
|
-
process.env = {
|
|
348
|
-
TEMPORAL_ADDRESS: 'http://localhost:7233',
|
|
349
|
-
CATALOG_ID: 'Test.Catalog-ID_123'
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
const { worker } = await import( './configs.js' );
|
|
353
|
-
expect( worker.catalogId ).toBe( 'Test.Catalog-ID_123' );
|
|
354
|
-
} );
|
|
355
|
-
} );
|
|
356
|
-
|
|
357
|
-
describe( 'Complete Valid Configuration', () => {
|
|
358
|
-
it( 'should handle a complete valid configuration with all optional fields', async () => {
|
|
359
|
-
process.env = {
|
|
360
|
-
TEMPORAL_ADDRESS: 'https://temporal.cloud.example.com',
|
|
361
|
-
TEMPORAL_NAMESPACE: 'production',
|
|
362
|
-
TEMPORAL_API_KEY: 'prod-api-key-123',
|
|
363
|
-
CATALOG_ID: 'prod.catalog@v1',
|
|
364
|
-
API_AUTH_KEY: 'secure-auth-key',
|
|
365
|
-
TRACING_ENABLED: 'true'
|
|
366
|
-
};
|
|
367
|
-
|
|
368
|
-
const { worker, api, tracing } = await import( './configs.js' );
|
|
369
|
-
|
|
370
|
-
expect( worker.address ).toBe( 'https://temporal.cloud.example.com' );
|
|
371
|
-
expect( worker.namespace ).toBe( 'production' );
|
|
372
|
-
expect( worker.apiKey ).toBe( 'prod-api-key-123' );
|
|
373
|
-
expect( worker.catalogId ).toBe( 'prod.catalog@v1' );
|
|
374
|
-
expect( worker.taskQueue ).toBe( 'prod.catalog@v1' );
|
|
375
|
-
expect( api.authKey ).toBe( 'secure-auth-key' );
|
|
376
|
-
expect( tracing.enabled ).toBe( true );
|
|
377
|
-
} );
|
|
378
|
-
} );
|
|
379
|
-
} );
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* These tools cant be used in sandbox environment!!!
|
|
3
|
-
*/
|
|
4
|
-
import { resolve } from 'path';
|
|
5
|
-
import { pathToFileURL } from 'url';
|
|
6
|
-
import { METADATA_ACCESS_SYMBOL } from '#consts';
|
|
7
|
-
import { writeFileSync, existsSync, readdirSync, mkdirSync } from 'fs';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Recursive traverse directories looking for files with given name,
|
|
11
|
-
* For each found file, return its path, pathname and URI
|
|
12
|
-
*
|
|
13
|
-
* @param {string} path - The path to scan
|
|
14
|
-
* @param {string[]} filenames - The filenames to look for
|
|
15
|
-
* @returns {string[{}]} An array containing an object with path, pathname and URI for each file found
|
|
16
|
-
* */
|
|
17
|
-
export function recursiveNavigateWhileCollecting( path, filenames, collection = [], ignoreDirNames = [ 'vendor', 'node_modules' ] ) {
|
|
18
|
-
for ( const entry of readdirSync( path, { withFileTypes: true } ) ) {
|
|
19
|
-
if ( ignoreDirNames.includes( entry.name ) ) {
|
|
20
|
-
continue;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const pathname = resolve( path, entry.name );
|
|
24
|
-
if ( entry.isDirectory() ) {
|
|
25
|
-
recursiveNavigateWhileCollecting( pathname, filenames, collection );
|
|
26
|
-
} else if ( filenames.includes( entry.name ) ) {
|
|
27
|
-
collection.push( { pathname, path, url: pathToFileURL( pathname ).href } );
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return collection;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* For each path, dynamic import it, and for each exported component with metadata (step, workflow), yields it.
|
|
36
|
-
* @param {string[]} paths - Paths of the files to import
|
|
37
|
-
*/
|
|
38
|
-
export async function *iteratorOverImportedComponents( paths ) {
|
|
39
|
-
for ( const { url, path, pathname } of paths ) {
|
|
40
|
-
const imported = await import( url );
|
|
41
|
-
for ( const component of Object.values( imported ) ) {
|
|
42
|
-
const metadata = component[METADATA_ACCESS_SYMBOL];
|
|
43
|
-
if ( !metadata ) {
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
yield { component, metadata, path, pathname };
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Write a file using the same signature as Node's FS writeFileSync, but recursively creates the necessary directories in the path.
|
|
53
|
-
*/
|
|
54
|
-
export function writeFileOnLocationSync( path, content ) {
|
|
55
|
-
const targetDir = path.split( '/' ).slice( 0, -1 ).join( '/' );
|
|
56
|
-
if ( targetDir && !existsSync( targetDir ) ) {
|
|
57
|
-
mkdirSync( targetDir, { recursive: true } );
|
|
58
|
-
}
|
|
59
|
-
writeFileSync( path, content, 'utf-8' );
|
|
60
|
-
};
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, readFileSync, existsSync } from 'node:fs';
|
|
3
|
-
import { join, resolve } from 'node:path';
|
|
4
|
-
import { tmpdir } from 'node:os';
|
|
5
|
-
import { pathToFileURL } from 'node:url';
|
|
6
|
-
import { recursiveNavigateWhileCollecting, iteratorOverImportedComponents, writeFileOnLocationSync } from './internal_utils.js';
|
|
7
|
-
|
|
8
|
-
function makeTmpRoot( prefix ) {
|
|
9
|
-
return mkdtempSync( join( tmpdir(), prefix ) );
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
describe( '.recursiveNavigateWhileCollecting', () => {
|
|
13
|
-
it( 'collects matching files recursively (happy path)', () => {
|
|
14
|
-
const root = makeTmpRoot( 'nsu-happy-' );
|
|
15
|
-
const target = 'target.txt';
|
|
16
|
-
|
|
17
|
-
// layout:
|
|
18
|
-
// root/target.txt
|
|
19
|
-
// root/a/target.txt
|
|
20
|
-
// root/b/c/target.txt
|
|
21
|
-
mkdirSync( join( root, 'a' ), { recursive: true } );
|
|
22
|
-
mkdirSync( join( root, 'b', 'c' ), { recursive: true } );
|
|
23
|
-
writeFileSync( join( root, target ), 'root' );
|
|
24
|
-
writeFileSync( join( root, 'a', target ), 'a' );
|
|
25
|
-
writeFileSync( join( root, 'b', 'c', target ), 'bc' );
|
|
26
|
-
|
|
27
|
-
const results = recursiveNavigateWhileCollecting( root, [ target ] );
|
|
28
|
-
|
|
29
|
-
expect( results.length ).toBe( 3 );
|
|
30
|
-
for ( const { pathname, path, url } of results ) {
|
|
31
|
-
expect( url ).toBe( pathToFileURL( pathname ).href );
|
|
32
|
-
expect( resolve( path, target ) ).toBe( pathname );
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
rmSync( root, { recursive: true, force: true } );
|
|
36
|
-
} );
|
|
37
|
-
|
|
38
|
-
it( 'skips files inside ignored directories (ignoreDirNames)', () => {
|
|
39
|
-
const root = makeTmpRoot( 'nsu-ignore-' );
|
|
40
|
-
const target = 'target.txt';
|
|
41
|
-
|
|
42
|
-
// layout:
|
|
43
|
-
// root/node_modules/target.txt (ignored)
|
|
44
|
-
// root/vendor/target.txt (ignored)
|
|
45
|
-
// root/ok/target.txt (collected)
|
|
46
|
-
mkdirSync( join( root, 'node_modules' ), { recursive: true } );
|
|
47
|
-
mkdirSync( join( root, 'vendor' ), { recursive: true } );
|
|
48
|
-
mkdirSync( join( root, 'ok' ), { recursive: true } );
|
|
49
|
-
writeFileSync( join( root, 'node_modules', target ), 'nm' );
|
|
50
|
-
writeFileSync( join( root, 'vendor', target ), 'v' );
|
|
51
|
-
writeFileSync( join( root, 'ok', target ), 'ok' );
|
|
52
|
-
|
|
53
|
-
const results = recursiveNavigateWhileCollecting( root, [ target ] );
|
|
54
|
-
|
|
55
|
-
expect( results.length ).toBe( 1 );
|
|
56
|
-
expect( results[0].pathname ).toBe( join( root, 'ok', target ) );
|
|
57
|
-
expect( results[0].path ).toBe( join( root, 'ok' ) );
|
|
58
|
-
expect( results[0].url ).toBe( pathToFileURL( results[0].pathname ).href );
|
|
59
|
-
|
|
60
|
-
rmSync( root, { recursive: true, force: true } );
|
|
61
|
-
} );
|
|
62
|
-
} );
|
|
63
|
-
|
|
64
|
-
describe( '.iteratorOverImportedComponents', () => {
|
|
65
|
-
it( 'imports modules and yields metadata from exports tagged with METADATA_ACCESS_SYMBOL', async () => {
|
|
66
|
-
const root = join( process.cwd(), 'sdk/core/temp_test_modules', `meta-${Date.now()}` );
|
|
67
|
-
mkdirSync( root, { recursive: true } );
|
|
68
|
-
const file = join( root, 'meta.module.js' );
|
|
69
|
-
writeFileSync( file, [
|
|
70
|
-
'import { METADATA_ACCESS_SYMBOL } from \"#consts\";',
|
|
71
|
-
'export const StepA = () => {};',
|
|
72
|
-
'StepA[METADATA_ACCESS_SYMBOL] = { kind: \"step\", name: \"a\" };',
|
|
73
|
-
'export const FlowB = () => {};',
|
|
74
|
-
'FlowB[METADATA_ACCESS_SYMBOL] = { kind: \"workflow\", name: \"b\" };'
|
|
75
|
-
].join( '\n' ) );
|
|
76
|
-
|
|
77
|
-
const paths = recursiveNavigateWhileCollecting( root, [ 'meta.module.js' ] );
|
|
78
|
-
const collected = [];
|
|
79
|
-
for await ( const m of iteratorOverImportedComponents( paths ) ) {
|
|
80
|
-
collected.push( m );
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
expect( collected.length ).toBe( 2 );
|
|
84
|
-
expect( collected.map( m => m.metadata.name ).sort() ).toEqual( [ 'a', 'b' ] );
|
|
85
|
-
expect( collected.map( m => m.metadata.kind ).sort() ).toEqual( [ 'step', 'workflow' ] );
|
|
86
|
-
for ( const m of collected ) {
|
|
87
|
-
expect( m.pathname ).toBe( file );
|
|
88
|
-
expect( m.path ).toBe( root );
|
|
89
|
-
expect( typeof m.component ).toBe( 'function' );
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
rmSync( root, { recursive: true, force: true } );
|
|
93
|
-
} );
|
|
94
|
-
|
|
95
|
-
it( 'ignores exports without metadata symbol', async () => {
|
|
96
|
-
const root = join( process.cwd(), 'sdk/core/temp_test_modules', `meta-${Date.now()}-nometa` );
|
|
97
|
-
mkdirSync( root, { recursive: true } );
|
|
98
|
-
const file = join( root, 'meta.module.js' );
|
|
99
|
-
writeFileSync( file, [
|
|
100
|
-
'export const Plain = () => {};',
|
|
101
|
-
'export const AlsoPlain = {}'
|
|
102
|
-
].join( '\n' ) );
|
|
103
|
-
|
|
104
|
-
const paths = recursiveNavigateWhileCollecting( root, [ 'meta.module.js' ] );
|
|
105
|
-
const collected = [];
|
|
106
|
-
for await ( const m of iteratorOverImportedComponents( paths ) ) {
|
|
107
|
-
collected.push( m );
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
expect( collected.length ).toBe( 0 );
|
|
111
|
-
rmSync( root, { recursive: true, force: true } );
|
|
112
|
-
} );
|
|
113
|
-
} );
|
|
114
|
-
|
|
115
|
-
describe( '.writeFileOnLocationSync', () => {
|
|
116
|
-
it( 'creates missing directories and writes file', () => {
|
|
117
|
-
const root = makeTmpRoot( 'nsu-write-' );
|
|
118
|
-
const nested = join( root, 'a', 'b', 'c.txt' );
|
|
119
|
-
writeFileOnLocationSync( nested, 'hello' );
|
|
120
|
-
expect( existsSync( join( root, 'a', 'b' ) ) ).toBe( true );
|
|
121
|
-
expect( readFileSync( nested, 'utf-8' ) ).toBe( 'hello' );
|
|
122
|
-
rmSync( root, { recursive: true, force: true } );
|
|
123
|
-
} );
|
|
124
|
-
|
|
125
|
-
it( 'overwrites existing content', () => {
|
|
126
|
-
const root = makeTmpRoot( 'nsu-write2-' );
|
|
127
|
-
const file = join( root, 'x', 'y.txt' );
|
|
128
|
-
mkdirSync( join( root, 'x' ), { recursive: true } );
|
|
129
|
-
writeFileSync( file, 'old' );
|
|
130
|
-
writeFileOnLocationSync( file, 'new' );
|
|
131
|
-
expect( readFileSync( file, 'utf-8' ) ).toBe( 'new' );
|
|
132
|
-
rmSync( root, { recursive: true, force: true } );
|
|
133
|
-
} );
|
|
134
|
-
} );
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { Storage } from '../async_storage.js';
|
|
2
|
-
import { mkdirSync, existsSync, readdirSync, appendFileSync } from 'node:fs';
|
|
3
|
-
import { join } from 'path';
|
|
4
|
-
import { EOL } from 'os';
|
|
5
|
-
import { buildLogTree } from './tracer_tree.js';
|
|
6
|
-
import { tracing as tracingConfig } from '#configs';
|
|
7
|
-
|
|
8
|
-
const callerDir = process.argv[2];
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Appends new information to a file
|
|
12
|
-
*
|
|
13
|
-
* Information has to be a JSON
|
|
14
|
-
*
|
|
15
|
-
* File is encoded in utf-8
|
|
16
|
-
*
|
|
17
|
-
* @param {string} path - The full filename
|
|
18
|
-
* @param {object} json - The content
|
|
19
|
-
*/
|
|
20
|
-
const flushEntry = ( path, json ) => appendFileSync( path, JSON.stringify( json ) + EOL, 'utf-8' );
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Add an event to the execution trace file.
|
|
24
|
-
*
|
|
25
|
-
* Events normally are the result of an operation, either a function call or an IO.
|
|
26
|
-
*
|
|
27
|
-
* @param {object} options
|
|
28
|
-
* @param {string} options.lib - The macro part of the platform that triggered the event
|
|
29
|
-
* @param {string} options.event - The name of the event
|
|
30
|
-
* @param {any} [options.input] - The input of the operation
|
|
31
|
-
* @param {any} [options.output] - The output of the operation
|
|
32
|
-
*/
|
|
33
|
-
export function trace( { lib, event, input = undefined, output = undefined } ) {
|
|
34
|
-
const now = Date.now();
|
|
35
|
-
|
|
36
|
-
if ( !tracingConfig.enabled ) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const {
|
|
41
|
-
activityId: stepId,
|
|
42
|
-
activityType: stepName,
|
|
43
|
-
workflowId,
|
|
44
|
-
workflowType,
|
|
45
|
-
workflowPath,
|
|
46
|
-
parentWorkflowId,
|
|
47
|
-
rootWorkflowId,
|
|
48
|
-
rootWorkflowType
|
|
49
|
-
} = Storage.load();
|
|
50
|
-
|
|
51
|
-
const entry = { event, input, lib, output, parentWorkflowId, stepId, stepName, timestamp: now, workflowId, workflowPath, workflowType };
|
|
52
|
-
|
|
53
|
-
// test for rootWorkflow to append to the same file as the parent/grandparent
|
|
54
|
-
const outputDir = join( callerDir, 'logs', 'runs', rootWorkflowType ?? workflowType );
|
|
55
|
-
if ( !existsSync( outputDir ) ) {
|
|
56
|
-
mkdirSync( outputDir, { recursive: true } );
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const suffix = `-${rootWorkflowId ?? workflowId}.raw`;
|
|
60
|
-
const logFile = readdirSync( outputDir ).find( f => f.endsWith( suffix ) ) ?? `${new Date( now ).toISOString()}-${suffix}`;
|
|
61
|
-
const logPath = join( outputDir, logFile );
|
|
62
|
-
|
|
63
|
-
flushEntry( logPath, entry );
|
|
64
|
-
buildLogTree( logPath );
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Setup the global tracer function, so it is available to be used by other libraries
|
|
69
|
-
*
|
|
70
|
-
* It will be situated in the global object, under Symbol.for('__trace')
|
|
71
|
-
*
|
|
72
|
-
* @returns {object} The assigned globalThis
|
|
73
|
-
*/
|
|
74
|
-
export const setupGlobalTracer = () =>
|
|
75
|
-
Object.defineProperty( globalThis, Symbol.for( '__trace' ), { value: trace, writable: false, enumerable: false, configurable: false } );
|