@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/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: TaskRecord<I, O>) => void) => void
104
+ addListener: <I = Parameters<Func>[0], O = ReturnType<Func>>(fn: (record: ExecutionRecord<I, O, B>) => void) => void
117
105
  removeListener: () => void
118
- emit: (data: Partial<TaskRecord>) => void
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: TaskRecord<Parameters<Func>[0], ReturnType<Func>>) => void) | undefined
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: TaskRecord<I, O>) => void): void {
300
- this._listener = fn as (record: TaskRecord<Parameters<Func>[0], ReturnType<Func>>) => void
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: Partial<TaskRecord>): void {
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 as TaskRecord<Parameters<Func>[0], ReturnType<Func>>)
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 executionBoundaries) {
448
- const boundary = executionBoundaries[name]
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
- executionBoundaries as unknown as Parameters<Func>[1]
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 executionBoundaries) {
583
- const boundary = executionBoundaries[name]
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
- executionBoundaries as unknown as Parameters<Func>[1]
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
  }