@forgehive/task 0.1.13 → 0.2.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.
Files changed (32) hide show
  1. package/dist/index.d.ts +37 -24
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +58 -13
  4. package/dist/index.js.map +1 -1
  5. package/dist/test/add-listener-with-boundaries.test.js +186 -181
  6. package/dist/test/add-listener-with-boundaries.test.js.map +1 -1
  7. package/dist/test/add-listener.test.js +80 -52
  8. package/dist/test/add-listener.test.js.map +1 -1
  9. package/dist/test/safe-replay-complex-boundary.test.js +36 -27
  10. package/dist/test/safe-replay-complex-boundary.test.js.map +1 -1
  11. package/dist/test/safe-replay.test.js +23 -14
  12. package/dist/test/safe-replay.test.js.map +1 -1
  13. package/dist/test/safe-run.test.js +41 -16
  14. package/dist/test/safe-run.test.js.map +1 -1
  15. package/dist/test/task-boundary-mocking.test.js +17 -7
  16. package/dist/test/task-boundary-mocking.test.js.map +1 -1
  17. package/dist/test/task-execution-log.test.d.ts +2 -0
  18. package/dist/test/task-execution-log.test.d.ts.map +1 -0
  19. package/dist/test/task-execution-log.test.js +207 -0
  20. package/dist/test/task-execution-log.test.js.map +1 -0
  21. package/dist/test/task-with-boundaries.test.js +56 -24
  22. package/dist/test/task-with-boundaries.test.js.map +1 -1
  23. package/package.json +1 -1
  24. package/src/index.ts +109 -40
  25. package/src/test/add-listener-with-boundaries.test.ts +202 -252
  26. package/src/test/add-listener.test.ts +90 -64
  27. package/src/test/safe-replay-complex-boundary.test.ts +18 -10
  28. package/src/test/safe-replay.test.ts +21 -12
  29. package/src/test/safe-run.test.ts +20 -15
  30. package/src/test/task-boundary-mocking.test.ts +8 -6
  31. package/src/test/task-execution-log.test.ts +246 -0
  32. package/src/test/task-with-boundaries.test.ts +45 -44
package/src/index.ts CHANGED
@@ -35,6 +35,8 @@ export type {
35
35
  export { Schema }
36
36
 
37
37
  export interface TaskConfig<B extends Boundaries = Boundaries> {
38
+ name?: string
39
+ description?: string
38
40
  schema?: Schema<Record<string, SchemaType>>
39
41
  mode?: Mode
40
42
  boundaries?: B
@@ -49,19 +51,7 @@ export interface ReplayConfig<B extends Boundaries = Boundaries> {
49
51
  }
50
52
 
51
53
  // ToDo: Add a type for the boundaries data
52
- /**
53
- * Represents the record passed to task listeners
54
- */
55
- export interface TaskRecord<InputType = unknown, OutputType = unknown> {
56
- /** The input arguments passed to the task */
57
- input: InputType;
58
- /** The output returned by the task (if successful) */
59
- output?: OutputType | null;
60
- /** The error message if the task failed */
61
- error?: string;
62
- /** Boundary execution data */
63
- boundaries?: Record<string, unknown>;
64
- }
54
+
65
55
 
66
56
  // Make BoundaryLog generic
67
57
  export type BoundaryLog<I extends unknown[] = unknown[], O = unknown> = BoundaryRecord<I, O>;
@@ -85,11 +75,19 @@ export interface ExecutionRecord<InputType = unknown, OutputType = unknown, B ex
85
75
  error?: string
86
76
  /** Boundary execution data */
87
77
  boundaries: BoundaryLogsFor<B>
78
+ /** The name of the task (if set) */
79
+ taskName?: string
80
+ /** Additional context metadata */
81
+ metadata?: Record<string, string>
82
+ /** The type of execution record - computed from output/error state */
83
+ type: 'success' | 'error' | 'pending'
88
84
  }
89
85
 
90
86
  export interface TaskInstanceType<Func extends BaseFunction = BaseFunction, B extends Boundaries = Boundaries> {
91
87
  version: string
92
88
 
89
+ getName: () => string | undefined
90
+ setName: (name: string) => void
93
91
  getMode: () => Mode
94
92
  setMode: (mode: Mode) => void
95
93
  setSchema: (base: Schema<Record<string, SchemaType>>) => void
@@ -103,9 +101,9 @@ export interface TaskInstanceType<Func extends BaseFunction = BaseFunction, B ex
103
101
  isValid: <T extends Record<string, unknown> = Parameters<Func>[0]>(argv?: T) => boolean
104
102
 
105
103
  // Listener methods
106
- 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
107
105
  removeListener: () => void
108
- emit: (data: Partial<TaskRecord>) => void
106
+ emit: (data: ExecutionRecord<Parameters<Func>[0], ReturnType<Func>, B>) => void
109
107
 
110
108
  // Boundary methods
111
109
  asBoundary: () => (args: Parameters<Func>[0]) => Promise<ReturnType<Func>>
@@ -119,7 +117,7 @@ export interface TaskInstanceType<Func extends BaseFunction = BaseFunction, B ex
119
117
  resetMocks: () => void
120
118
 
121
119
  run: (argv?: Parameters<Func>[0]) => Promise<ReturnType<Func>>
122
- safeRun: (argv?: Parameters<Func>[0]) => Promise<[Awaited<ReturnType<Func>> | null, Error | null, ExecutionRecord<Parameters<Func>[0], ReturnType<Func>, B>]>
120
+ safeRun: (argv?: Parameters<Func>[0], context?: Record<string, string>) => Promise<[Awaited<ReturnType<Func>> | null, Error | null, ExecutionRecord<Parameters<Func>[0], ReturnType<Func>, B>]>
123
121
 
124
122
  // Method for replaying task execution
125
123
  safeReplay: (
@@ -146,6 +144,21 @@ export type TaskFunction<S, B extends Boundaries> =
146
144
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
147
145
  (argv: InferSchemaType<S>, boundaries: WrappedBoundaries<B>) => Promise<any>;
148
146
 
147
+ /**
148
+ * Utility function to compute the execution record type based on output and error state
149
+ */
150
+ export function getExecutionRecordType<InputType = unknown, OutputType = unknown, B extends Boundaries = Boundaries>(
151
+ record: Omit<ExecutionRecord<InputType, OutputType, B>, 'type'>
152
+ ): 'success' | 'error' | 'pending' {
153
+ if (record.error !== undefined && record.error !== null) {
154
+ return 'error'
155
+ }
156
+ if (record.output !== undefined && record.output !== null) {
157
+ return 'success'
158
+ }
159
+ return 'pending'
160
+ }
161
+
149
162
  export const Task = class Task<
150
163
  B extends Boundaries = Boundaries,
151
164
  Func extends BaseFunction = BaseFunction
@@ -155,6 +168,7 @@ export const Task = class Task<
155
168
  _fn: Func
156
169
  _mode: Mode
157
170
  _coolDown: number
171
+ _name?: string
158
172
  _description?: string
159
173
 
160
174
  _boundariesDefinition: B
@@ -165,9 +179,11 @@ export const Task = class Task<
165
179
  _boundaryMocks: Record<string, WrappedBoundaryFunction> = {}
166
180
 
167
181
  _schema: Schema<Record<string, SchemaType>> | undefined
168
- _listener?: ((record: TaskRecord<Parameters<Func>[0], ReturnType<Func>>) => void) | undefined
182
+ _listener?: ((record: ExecutionRecord<Parameters<Func>[0], ReturnType<Func>, B>) => void) | undefined
169
183
 
170
184
  constructor (fn: Func, conf: TaskConfig<B> = {
185
+ name: undefined,
186
+ description: undefined,
171
187
  schema: undefined,
172
188
  mode: 'proxy',
173
189
  boundaries: undefined,
@@ -182,6 +198,10 @@ export const Task = class Task<
182
198
  this._mode = conf.mode ?? 'proxy'
183
199
  this._boundariesDefinition = conf.boundaries ?? {} as B
184
200
 
201
+ // Set name and description from config
202
+ this._name = conf.name
203
+ this._description = conf.description
204
+
185
205
  this._listener = undefined
186
206
 
187
207
  // Cool down time before killing the process on cli runner
@@ -208,6 +228,14 @@ export const Task = class Task<
208
228
  }
209
229
  }
210
230
 
231
+ getName(): string | undefined {
232
+ return this._name
233
+ }
234
+
235
+ setName(name: string): void {
236
+ this._name = name
237
+ }
238
+
211
239
  getMode (): Mode {
212
240
  return this._mode
213
241
  }
@@ -256,8 +284,8 @@ export const Task = class Task<
256
284
  }
257
285
 
258
286
  // Posible improvement to handle multiple listeners, but so far its not needed
259
- addListener<I = Parameters<Func>[0], O = ReturnType<Func>>(fn: (record: TaskRecord<I, O>) => void): void {
260
- this._listener = fn as (record: TaskRecord<Parameters<Func>[0], ReturnType<Func>>) => void
287
+ addListener<I = Parameters<Func>[0], O = ReturnType<Func>>(fn: (record: ExecutionRecord<I, O, B>) => void): void {
288
+ this._listener = fn as (record: ExecutionRecord<Parameters<Func>[0], ReturnType<Func>, B>) => void
261
289
  }
262
290
 
263
291
  removeListener (): void {
@@ -268,10 +296,10 @@ export const Task = class Task<
268
296
  The listener get the input/outout of the call
269
297
  Plus all the boundary data
270
298
  */
271
- emit (data: Partial<TaskRecord>): void {
299
+ emit (data: ExecutionRecord<Parameters<Func>[0], ReturnType<Func>, B>): void {
272
300
  if (typeof this._listener === 'undefined') { return }
273
301
 
274
- this._listener(data as TaskRecord<Parameters<Func>[0], ReturnType<Func>>)
302
+ this._listener(data)
275
303
  }
276
304
 
277
305
  getBoundaries (): WrappedBoundaries<B> {
@@ -378,10 +406,22 @@ export const Task = class Task<
378
406
  Error | null,
379
407
  ExecutionRecord<Parameters<Func>[0], ReturnType<Func>, B>
380
408
  ]> {
381
- // Initialize log item
382
- const logItem: ExecutionRecord<Parameters<Func>[0], ReturnType<Func>, B> = {
409
+ // Metadata is empty at start. Then will be populated on the task execution
410
+ // Need to implement that task have a ctx and setMetadata({key, value}) boundary
411
+ const metadata = {} as Record<string, string>
412
+
413
+ // Initialize log item (without type initially)
414
+ const logItemBase = {
383
415
  input: argv as Parameters<Func>[0],
384
- boundaries: {} as BoundaryLogsFor<B>
416
+ boundaries: {} as BoundaryLogsFor<B>,
417
+ taskName: this._name,
418
+ metadata: metadata || {}
419
+ }
420
+
421
+ // Create the log item with computed type
422
+ const logItem: ExecutionRecord<Parameters<Func>[0], ReturnType<Func>, B> = {
423
+ ...logItemBase,
424
+ type: getExecutionRecordType(logItemBase)
385
425
  }
386
426
 
387
427
  // Create fresh boundaries for this execution
@@ -410,6 +450,7 @@ export const Task = class Task<
410
450
  : 'Invalid input'
411
451
 
412
452
  logItem.error = errorMessage
453
+ logItem.type = 'error'
413
454
  logItem.boundaries = {} as BoundaryLogsFor<B>
414
455
 
415
456
  // Add boundary elements empty
@@ -433,9 +474,11 @@ export const Task = class Task<
433
474
  )
434
475
 
435
476
  logItem.output = output
477
+ logItem.type = 'success'
436
478
  } catch (caughtError) {
437
479
  const errorMessage = caughtError instanceof Error ? caughtError.message : String(caughtError)
438
480
  logItem.error = errorMessage
481
+ logItem.type = 'error'
439
482
  error = new Error(errorMessage)
440
483
  }
441
484
 
@@ -487,10 +530,18 @@ export const Task = class Task<
487
530
  // Extract the input from the execution log
488
531
  const argv = executionLog.input
489
532
 
490
- // Initialize log item for this replay
491
- const logItem: ExecutionRecord<Parameters<Func>[0], ReturnType<Func>, B> = {
533
+ // Initialize log item for this replay (without type initially)
534
+ const logItemBase = {
492
535
  input: argv,
493
- boundaries: {} as BoundaryLogsFor<B>
536
+ boundaries: {} as BoundaryLogsFor<B>,
537
+ taskName: this._name,
538
+ metadata: executionLog.metadata || {}
539
+ }
540
+
541
+ // Create the log item with computed type
542
+ const logItem: ExecutionRecord<Parameters<Func>[0], ReturnType<Func>, B> = {
543
+ ...logItemBase,
544
+ type: getExecutionRecordType(logItemBase)
494
545
  }
495
546
 
496
547
  // Create boundaries for this replay execution with custom modes based on config
@@ -534,6 +585,7 @@ export const Task = class Task<
534
585
  : 'Invalid input'
535
586
 
536
587
  logItem.error = errorMessage
588
+ logItem.type = 'error'
537
589
  logItem.output = executionLog.output // Keep the original output
538
590
 
539
591
  // Copy the boundary data from the execution log
@@ -555,9 +607,11 @@ export const Task = class Task<
555
607
  )
556
608
 
557
609
  logItem.output = output
610
+ logItem.type = 'success'
558
611
  } catch (caughtError) {
559
612
  const errorMessage = caughtError instanceof Error ? caughtError.message : String(caughtError)
560
613
  logItem.error = errorMessage
614
+ logItem.type = 'error'
561
615
  error = new Error(errorMessage)
562
616
  }
563
617
 
@@ -757,12 +811,26 @@ export const Task = class Task<
757
811
  }
758
812
  }
759
813
 
814
+ /**
815
+ * Configuration object for creating a task
816
+ */
817
+ export interface CreateTaskConfig<
818
+ S extends Schema<Record<string, SchemaType>>,
819
+ B extends Boundaries,
820
+ R
821
+ > {
822
+ name?: string
823
+ description?: string
824
+ schema: S
825
+ boundaries: B
826
+ fn: (argv: InferSchemaType<S>, boundaries: WrappedBoundaries<B>) => Promise<R>
827
+ mode?: Mode
828
+ boundariesData?: BoundaryTapeData
829
+ }
830
+
760
831
  /**
761
832
  * Helper function to create a task with proper type inference
762
- * @param schema The schema to validate input against
763
- * @param boundaries The boundaries to use
764
- * @param fn The task function
765
- * @param config Additional task configuration
833
+ * @param config Configuration object containing schema, boundaries, and function
766
834
  * @returns A new Task instance with proper type inference
767
835
  */
768
836
  export function createTask<
@@ -770,17 +838,18 @@ export function createTask<
770
838
  B extends Boundaries,
771
839
  R
772
840
  >(
773
- schema: S,
774
- boundaries: B,
775
- fn: (argv: InferSchemaType<S>, boundaries: WrappedBoundaries<B>) => Promise<R>,
776
- config?: Omit<TaskConfig<B>, 'schema' | 'boundaries'>
841
+ config: CreateTaskConfig<S, B, R>
777
842
  ): TaskInstanceType<(argv: InferSchemaType<S>, boundaries: WrappedBoundaries<B>) => Promise<R>, B> {
778
- return new Task(
779
- fn,
843
+ const task = new Task(
844
+ config.fn,
780
845
  {
781
- schema,
782
- boundaries,
783
- ...config
846
+ name: config.name,
847
+ description: config.description,
848
+ schema: config.schema,
849
+ boundaries: config.boundaries,
850
+ mode: config.mode,
851
+ boundariesData: config.boundariesData
784
852
  }
785
853
  )
854
+ return task
786
855
  }