@forgehive/task 0.2.0 → 0.2.2
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 +164 -10
- package/dist/index.d.ts +20 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +42 -6
- package/dist/index.js.map +1 -1
- package/dist/test/add-listener-with-boundaries.test.js +175 -200
- package/dist/test/add-listener-with-boundaries.test.js.map +1 -1
- package/dist/test/add-listener.test.js +80 -52
- package/dist/test/add-listener.test.js.map +1 -1
- package/dist/test/execution-record-boundaries.test.d.ts +2 -0
- package/dist/test/execution-record-boundaries.test.d.ts.map +1 -0
- package/dist/test/execution-record-boundaries.test.js +220 -0
- package/dist/test/execution-record-boundaries.test.js.map +1 -0
- package/dist/test/task-with-boundaries.test.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +78 -28
- package/src/test/add-listener-with-boundaries.test.ts +186 -242
- package/src/test/add-listener.test.ts +90 -64
- package/src/test/execution-record-boundaries.test.ts +266 -0
- package/src/test/task-with-boundaries.test.ts +3 -3
package/src/index.ts
CHANGED
|
@@ -51,19 +51,7 @@ export interface ReplayConfig<B extends Boundaries = Boundaries> {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
// ToDo: Add a type for the boundaries data
|
|
54
|
-
|
|
55
|
-
* Represents the record passed to task listeners
|
|
56
|
-
*/
|
|
57
|
-
export interface TaskRecord<InputType = unknown, OutputType = unknown> {
|
|
58
|
-
/** The input arguments passed to the task */
|
|
59
|
-
input: InputType;
|
|
60
|
-
/** The output returned by the task (if successful) */
|
|
61
|
-
output?: OutputType | null;
|
|
62
|
-
/** The error message if the task failed */
|
|
63
|
-
error?: string;
|
|
64
|
-
/** Boundary execution data */
|
|
65
|
-
boundaries?: Record<string, unknown>;
|
|
66
|
-
}
|
|
54
|
+
|
|
67
55
|
|
|
68
56
|
// Make BoundaryLog generic
|
|
69
57
|
export type BoundaryLog<I extends unknown[] = unknown[], O = unknown> = BoundaryRecord<I, O>;
|
|
@@ -113,9 +101,9 @@ export interface TaskInstanceType<Func extends BaseFunction = BaseFunction, B ex
|
|
|
113
101
|
isValid: <T extends Record<string, unknown> = Parameters<Func>[0]>(argv?: T) => boolean
|
|
114
102
|
|
|
115
103
|
// Listener methods
|
|
116
|
-
addListener: <I = Parameters<Func>[0], O = ReturnType<Func>>(fn: (record:
|
|
104
|
+
addListener: <I = Parameters<Func>[0], O = ReturnType<Func>>(fn: (record: ExecutionRecord<I, O, B>) => void) => void
|
|
117
105
|
removeListener: () => void
|
|
118
|
-
emit: (data:
|
|
106
|
+
emit: (data: ExecutionRecord<Parameters<Func>[0], ReturnType<Func>, B>) => void
|
|
119
107
|
|
|
120
108
|
// Boundary methods
|
|
121
109
|
asBoundary: () => (args: Parameters<Func>[0]) => Promise<ReturnType<Func>>
|
|
@@ -151,10 +139,19 @@ type BoundaryData = Array<{input: unknown[], output?: unknown}>
|
|
|
151
139
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
152
140
|
export type InferSchemaType<S> = S extends Schema<any> ? InferSchema<S> : Record<string, unknown>;
|
|
153
141
|
|
|
142
|
+
// Type for execution record boundaries that are automatically injected
|
|
143
|
+
// When adding new execution boundaries, add their types here
|
|
144
|
+
export type ExecutionRecordBoundaries = {
|
|
145
|
+
setMetadata: (key: string, value: string) => Promise<void>
|
|
146
|
+
// Future execution boundaries can be added here:
|
|
147
|
+
// setContext: (context: Record<string, unknown>) => Promise<void>
|
|
148
|
+
// addTag: (tag: string) => Promise<void>
|
|
149
|
+
}
|
|
150
|
+
|
|
154
151
|
// Helper type for task function with proper typing
|
|
155
152
|
export type TaskFunction<S, B extends Boundaries> =
|
|
156
153
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
157
|
-
(argv: InferSchemaType<S>, boundaries: WrappedBoundaries<B>) => Promise<any>;
|
|
154
|
+
(argv: InferSchemaType<S>, boundaries: WrappedBoundaries<B> & ExecutionRecordBoundaries) => Promise<any>;
|
|
158
155
|
|
|
159
156
|
/**
|
|
160
157
|
* Utility function to compute the execution record type based on output and error state
|
|
@@ -191,7 +188,7 @@ export const Task = class Task<
|
|
|
191
188
|
_boundaryMocks: Record<string, WrappedBoundaryFunction> = {}
|
|
192
189
|
|
|
193
190
|
_schema: Schema<Record<string, SchemaType>> | undefined
|
|
194
|
-
_listener?: ((record:
|
|
191
|
+
_listener?: ((record: ExecutionRecord<Parameters<Func>[0], ReturnType<Func>, B>) => void) | undefined
|
|
195
192
|
|
|
196
193
|
constructor (fn: Func, conf: TaskConfig<B> = {
|
|
197
194
|
name: undefined,
|
|
@@ -296,8 +293,8 @@ export const Task = class Task<
|
|
|
296
293
|
}
|
|
297
294
|
|
|
298
295
|
// Posible improvement to handle multiple listeners, but so far its not needed
|
|
299
|
-
addListener<I = Parameters<Func>[0], O = ReturnType<Func>>(fn: (record:
|
|
300
|
-
this._listener = fn as (record:
|
|
296
|
+
addListener<I = Parameters<Func>[0], O = ReturnType<Func>>(fn: (record: ExecutionRecord<I, O, B>) => void): void {
|
|
297
|
+
this._listener = fn as (record: ExecutionRecord<Parameters<Func>[0], ReturnType<Func>, B>) => void
|
|
301
298
|
}
|
|
302
299
|
|
|
303
300
|
removeListener (): void {
|
|
@@ -308,10 +305,10 @@ export const Task = class Task<
|
|
|
308
305
|
The listener get the input/outout of the call
|
|
309
306
|
Plus all the boundary data
|
|
310
307
|
*/
|
|
311
|
-
emit (data:
|
|
308
|
+
emit (data: ExecutionRecord<Parameters<Func>[0], ReturnType<Func>, B>): void {
|
|
312
309
|
if (typeof this._listener === 'undefined') { return }
|
|
313
310
|
|
|
314
|
-
this._listener(data
|
|
311
|
+
this._listener(data)
|
|
315
312
|
}
|
|
316
313
|
|
|
317
314
|
getBoundaries (): WrappedBoundaries<B> {
|
|
@@ -367,6 +364,28 @@ export const Task = class Task<
|
|
|
367
364
|
this._boundaryMocks = {}
|
|
368
365
|
}
|
|
369
366
|
|
|
367
|
+
/**
|
|
368
|
+
* Creates execution record boundaries that modify execution metadata and logging
|
|
369
|
+
* These boundaries are automatically injected into all task executions
|
|
370
|
+
*
|
|
371
|
+
* To add a new execution boundary:
|
|
372
|
+
* 1. Add the boundary function here
|
|
373
|
+
* 2. Update the ExecutionRecordBoundaries type to include the new boundary
|
|
374
|
+
* 3. That's it! The boundary will be available in all tasks automatically
|
|
375
|
+
*/
|
|
376
|
+
_createExecutionBoundaries(metadata: Record<string, string>): Record<string, WrappedBoundaryFunction> {
|
|
377
|
+
return {
|
|
378
|
+
// Allows setting metadata key-value pairs from within task execution
|
|
379
|
+
setMetadata: createBoundary(async (...args: unknown[]): Promise<void> => {
|
|
380
|
+
const [key, value] = args as [string, string]
|
|
381
|
+
metadata[key] = value
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
// Future execution boundaries can be added here:
|
|
385
|
+
// addMetrics: createBoundary(async (...args: unknown[]): Promise<void> => { ... }),
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
370
389
|
_createBounderies ({
|
|
371
390
|
definition,
|
|
372
391
|
baseData,
|
|
@@ -443,9 +462,16 @@ export const Task = class Task<
|
|
|
443
462
|
mode: this._mode
|
|
444
463
|
})
|
|
445
464
|
|
|
465
|
+
// Create and inject execution record boundaries (setMetadata, etc.)
|
|
466
|
+
const executionRecordBoundaries = this._createExecutionBoundaries(metadata)
|
|
467
|
+
const allBoundaries = {
|
|
468
|
+
...executionBoundaries,
|
|
469
|
+
...executionRecordBoundaries
|
|
470
|
+
}
|
|
471
|
+
|
|
446
472
|
// Start run for each boundary
|
|
447
|
-
for (const name in
|
|
448
|
-
const boundary =
|
|
473
|
+
for (const name in allBoundaries) {
|
|
474
|
+
const boundary = allBoundaries[name]
|
|
449
475
|
boundary.startRun()
|
|
450
476
|
}
|
|
451
477
|
|
|
@@ -482,7 +508,7 @@ export const Task = class Task<
|
|
|
482
508
|
// Execute the task function
|
|
483
509
|
output = await this._fn(
|
|
484
510
|
argv as Parameters<Func>[0],
|
|
485
|
-
|
|
511
|
+
allBoundaries as unknown as Parameters<Func>[1]
|
|
486
512
|
)
|
|
487
513
|
|
|
488
514
|
logItem.output = output
|
|
@@ -519,6 +545,9 @@ export const Task = class Task<
|
|
|
519
545
|
}
|
|
520
546
|
}
|
|
521
547
|
|
|
548
|
+
// Filter out setMetadata boundary calls from the logs (they should not appear in execution record)
|
|
549
|
+
// Note: setMetadata is not part of the original boundaries definition, so it won't be in boundariesRunLog anyway
|
|
550
|
+
|
|
522
551
|
// Set boundaries in log item before emitting
|
|
523
552
|
logItem.boundaries = boundariesRunLog
|
|
524
553
|
|
|
@@ -578,9 +607,18 @@ export const Task = class Task<
|
|
|
578
607
|
boundaryModes: config.boundaries
|
|
579
608
|
})
|
|
580
609
|
|
|
610
|
+
// Create and inject execution record boundaries (setMetadata, etc.)
|
|
611
|
+
// Clone the metadata to avoid mutating the original metadata
|
|
612
|
+
const replayMetadata = { ...(logItem.metadata || {}) }
|
|
613
|
+
const executionRecordBoundaries = this._createExecutionBoundaries(replayMetadata)
|
|
614
|
+
const allBoundaries = {
|
|
615
|
+
...executionBoundaries,
|
|
616
|
+
...executionRecordBoundaries
|
|
617
|
+
}
|
|
618
|
+
|
|
581
619
|
// Start run for each boundary
|
|
582
|
-
for (const name in
|
|
583
|
-
const boundary =
|
|
620
|
+
for (const name in allBoundaries) {
|
|
621
|
+
const boundary = allBoundaries[name]
|
|
584
622
|
boundary.startRun()
|
|
585
623
|
}
|
|
586
624
|
|
|
@@ -615,7 +653,7 @@ export const Task = class Task<
|
|
|
615
653
|
// Execute the task function with replay boundaries
|
|
616
654
|
output = await this._fn(
|
|
617
655
|
argv,
|
|
618
|
-
|
|
656
|
+
allBoundaries as unknown as Parameters<Func>[1]
|
|
619
657
|
)
|
|
620
658
|
|
|
621
659
|
logItem.output = output
|
|
@@ -644,9 +682,15 @@ export const Task = class Task<
|
|
|
644
682
|
}
|
|
645
683
|
}
|
|
646
684
|
|
|
685
|
+
// Filter out setMetadata boundary calls from the logs (they should not appear in execution record)
|
|
686
|
+
// Note: setMetadata is not part of the original boundaries definition, so it won't be in boundariesRunLog anyway
|
|
687
|
+
|
|
647
688
|
// Set boundaries in log item before emitting
|
|
648
689
|
logItem.boundaries = boundariesRunLog
|
|
649
690
|
|
|
691
|
+
// Update the log item metadata with changes made during replay
|
|
692
|
+
logItem.metadata = replayMetadata
|
|
693
|
+
|
|
650
694
|
// Emit the log item
|
|
651
695
|
this.emit(logItem)
|
|
652
696
|
|
|
@@ -696,6 +740,12 @@ export const Task = class Task<
|
|
|
696
740
|
// Call the task's safeRun method
|
|
697
741
|
const [outcome, error, log] = await this.safeRun(eventArgs)
|
|
698
742
|
|
|
743
|
+
// Extend log metadata with environment info
|
|
744
|
+
log.metadata = {
|
|
745
|
+
...log.metadata,
|
|
746
|
+
environment: 'hive-lambda'
|
|
747
|
+
}
|
|
748
|
+
|
|
699
749
|
// Send log to Hive if environment variables are present
|
|
700
750
|
await this._sendToHive(log)
|
|
701
751
|
|
|
@@ -835,7 +885,7 @@ export interface CreateTaskConfig<
|
|
|
835
885
|
description?: string
|
|
836
886
|
schema: S
|
|
837
887
|
boundaries: B
|
|
838
|
-
fn: (argv: InferSchemaType<S>, boundaries: WrappedBoundaries<B>) => Promise<R>
|
|
888
|
+
fn: (argv: InferSchemaType<S>, boundaries: WrappedBoundaries<B> & ExecutionRecordBoundaries) => Promise<R>
|
|
839
889
|
mode?: Mode
|
|
840
890
|
boundariesData?: BoundaryTapeData
|
|
841
891
|
}
|