@forgehive/task 0.2.3 → 0.2.5

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 (73) hide show
  1. package/dist/index.d.ts +9 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +38 -9
  4. package/dist/index.js.map +1 -1
  5. package/dist/test/add-listener-with-boundaries.test.js +78 -7
  6. package/dist/test/add-listener-with-boundaries.test.js.map +1 -1
  7. package/dist/test/add-listener.test.js +36 -0
  8. package/dist/test/add-listener.test.js.map +1 -1
  9. package/dist/test/boundary-modes.test.js +45 -5
  10. package/dist/test/boundary-modes.test.js.map +1 -1
  11. package/dist/test/execution-record-boundaries.test.js +12 -2
  12. package/dist/test/execution-record-boundaries.test.js.map +1 -1
  13. package/dist/test/integration-enhanced-records.test.d.ts +2 -0
  14. package/dist/test/integration-enhanced-records.test.d.ts.map +1 -0
  15. package/dist/test/integration-enhanced-records.test.js +467 -0
  16. package/dist/test/integration-enhanced-records.test.js.map +1 -0
  17. package/dist/test/metrics-collection.test.d.ts +2 -0
  18. package/dist/test/metrics-collection.test.d.ts.map +1 -0
  19. package/dist/test/metrics-collection.test.js +409 -0
  20. package/dist/test/metrics-collection.test.js.map +1 -0
  21. package/dist/test/performance-edge-cases.test.d.ts +2 -0
  22. package/dist/test/performance-edge-cases.test.d.ts.map +1 -0
  23. package/dist/test/performance-edge-cases.test.js +502 -0
  24. package/dist/test/performance-edge-cases.test.js.map +1 -0
  25. package/dist/test/run-boundary.test.js +27 -3
  26. package/dist/test/run-boundary.test.js.map +1 -1
  27. package/dist/test/safe-replay-complex-boundary.test.js +110 -9
  28. package/dist/test/safe-replay-complex-boundary.test.js.map +1 -1
  29. package/dist/test/safe-replay.test.js +35 -5
  30. package/dist/test/safe-replay.test.js.map +1 -1
  31. package/dist/test/safe-run.test.js +46 -4
  32. package/dist/test/safe-run.test.js.map +1 -1
  33. package/dist/test/setmetrics-boundary.test.d.ts +2 -0
  34. package/dist/test/setmetrics-boundary.test.d.ts.map +1 -0
  35. package/dist/test/setmetrics-boundary.test.js +195 -0
  36. package/dist/test/setmetrics-boundary.test.js.map +1 -0
  37. package/dist/test/task-with-boundaries.test.js +63 -2
  38. package/dist/test/task-with-boundaries.test.js.map +1 -1
  39. package/dist/test/timing-capture.test.d.ts +2 -0
  40. package/dist/test/timing-capture.test.d.ts.map +1 -0
  41. package/dist/test/timing-capture.test.js +304 -0
  42. package/dist/test/timing-capture.test.js.map +1 -0
  43. package/dist/test/timing-utilities.test.d.ts +2 -0
  44. package/dist/test/timing-utilities.test.d.ts.map +1 -0
  45. package/dist/test/timing-utilities.test.js +127 -0
  46. package/dist/test/timing-utilities.test.js.map +1 -0
  47. package/dist/types.d.ts +93 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +78 -0
  50. package/dist/types.js.map +1 -0
  51. package/dist/utils/boundary.d.ts +3 -0
  52. package/dist/utils/boundary.d.ts.map +1 -1
  53. package/dist/utils/boundary.js +11 -2
  54. package/dist/utils/boundary.js.map +1 -1
  55. package/package.json +3 -2
  56. package/src/index.ts +63 -8
  57. package/src/test/ToDos.md +79 -0
  58. package/src/test/add-listener-with-boundaries.test.ts +78 -7
  59. package/src/test/add-listener.test.ts +36 -0
  60. package/src/test/boundary-modes.test.ts +45 -5
  61. package/src/test/execution-record-boundaries.test.ts +12 -2
  62. package/src/test/metrics-collection.test.ts +476 -0
  63. package/src/test/performance-edge-cases.test.ts +596 -0
  64. package/src/test/run-boundary.test.ts +27 -3
  65. package/src/test/safe-replay-complex-boundary.test.ts +115 -10
  66. package/src/test/safe-replay.test.ts +35 -5
  67. package/src/test/safe-run.test.ts +46 -4
  68. package/src/test/setmetrics-boundary.test.ts +223 -0
  69. package/src/test/task-with-boundaries.test.ts +71 -5
  70. package/src/test/timing-capture.test.ts +348 -0
  71. package/src/test/timing-utilities.test.ts +145 -0
  72. package/src/types.ts +139 -0
  73. package/src/utils/boundary.ts +15 -2
package/src/types.ts ADDED
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Shared types for execution timing and metrics
3
+ */
4
+
5
+ /**
6
+ * Timing information for tracking start/end times and duration
7
+ */
8
+ export interface TimingInfo {
9
+ /** Unix timestamp in milliseconds when execution started */
10
+ startTime: number;
11
+ /** Unix timestamp in milliseconds when execution ended */
12
+ endTime: number;
13
+ /** Computed duration in milliseconds (endTime - startTime) */
14
+ duration?: number;
15
+ }
16
+
17
+ /**
18
+ * Metric data structure for collecting custom measurements
19
+ */
20
+ export interface Metric {
21
+ /** Category of metric (e.g., "performance", "business", "error") */
22
+ type: string;
23
+ /** Specific metric name (e.g., "response_time", "items_processed") */
24
+ name: string;
25
+ /** Numeric value of the metric */
26
+ value: number;
27
+ }
28
+
29
+ /**
30
+ * Utility class for capturing timing information
31
+ */
32
+ export class TimingTracker {
33
+ private startTime: number | null = null
34
+
35
+ /**
36
+ * Start timing capture
37
+ */
38
+ start(): void {
39
+ this.startTime = Date.now()
40
+ }
41
+
42
+ /**
43
+ * End timing capture and return timing information
44
+ * @returns TimingInfo object or null if start() was not called
45
+ */
46
+ end(): TimingInfo | null {
47
+ if (this.startTime === null) {return null}
48
+
49
+ const endTime = Date.now()
50
+ const result = {
51
+ startTime: this.startTime,
52
+ endTime,
53
+ duration: endTime - this.startTime
54
+ }
55
+
56
+ // Reset startTime so subsequent calls return null
57
+ this.startTime = null
58
+ return result
59
+ }
60
+
61
+ /**
62
+ * Create a new TimingTracker instance
63
+ * @returns New TimingTracker instance
64
+ */
65
+ static create(): TimingTracker {
66
+ return new TimingTracker()
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Enhanced boundary record with timing information
72
+ */
73
+ export interface BoundaryTimingRecord<TInput = unknown[], TOutput = unknown> {
74
+ input: TInput;
75
+ output?: TOutput;
76
+ error?: string;
77
+ timing: TimingInfo;
78
+ }
79
+
80
+ /**
81
+ * Base execution record interface with timing and metrics support
82
+ */
83
+ export interface BaseExecutionRecord<InputType = unknown, OutputType = unknown, B = unknown> {
84
+ /** The input arguments passed to the task */
85
+ input: InputType;
86
+ /** The output returned by the task (if successful) */
87
+ output?: OutputType | null;
88
+ /** The error message if the task failed */
89
+ error?: string;
90
+ /** Boundary execution data */
91
+ boundaries?: B;
92
+ /** The name of the task (if set) */
93
+ taskName?: string;
94
+ /** Additional context metadata */
95
+ metadata?: Record<string, string>;
96
+ /** Array of collected metrics */
97
+ metrics?: Metric[];
98
+ /** Main function execution timing */
99
+ timing?: TimingInfo;
100
+ /** The type of execution record - computed from output/error state */
101
+ type: 'success' | 'error' | 'pending';
102
+ }
103
+
104
+ /**
105
+ * Validates a metric object to ensure it conforms to the Metric interface
106
+ * @param metric - The metric object to validate
107
+ * @returns true if the metric is valid, false otherwise
108
+ */
109
+ export function validateMetric(metric: unknown): metric is Metric {
110
+ if (typeof metric !== 'object' || metric === null) {
111
+ return false
112
+ }
113
+
114
+ const m = metric as Record<string, unknown>
115
+
116
+ return (
117
+ typeof m.type === 'string' && m.type.length > 0 &&
118
+ typeof m.name === 'string' && m.name.length > 0 &&
119
+ typeof m.value === 'number' && !isNaN(m.value) && isFinite(m.value)
120
+ )
121
+ }
122
+
123
+ /**
124
+ * Creates a validated metric object
125
+ * @param type - The metric type/category
126
+ * @param name - The metric name
127
+ * @param value - The metric value
128
+ * @returns A valid Metric object
129
+ * @throws Error if the metric data is invalid
130
+ */
131
+ export function createMetric(type: string, name: string, value: number): Metric {
132
+ const metric: Metric = { type, name, value }
133
+
134
+ if (!validateMetric(metric)) {
135
+ throw new Error(`Invalid metric: type="${type}", name="${name}", value=${value}`)
136
+ }
137
+
138
+ return metric
139
+ }
@@ -1,4 +1,5 @@
1
1
  import * as assert from 'assert'
2
+ import { TimingInfo, TimingTracker } from '../types'
2
3
 
3
4
  // Define generic types for input and output
4
5
  type BaseBoundary = (...args: unknown[]) => unknown
@@ -22,6 +23,7 @@ export type BoundarySuccessRecord<TInput = unknown[], TOutput = unknown> = {
22
23
  input: TInput;
23
24
  output: TOutput;
24
25
  error?: null;
26
+ timing: TimingInfo;
25
27
  }
26
28
 
27
29
  /**
@@ -32,6 +34,7 @@ export type BoundaryErrorRecord<TInput = unknown[]> = {
32
34
  input: TInput;
33
35
  output?: null;
34
36
  error: string;
37
+ timing: TimingInfo;
35
38
  }
36
39
 
37
40
  /**
@@ -129,6 +132,9 @@ export const createBoundary = <Func extends BaseBoundary>(fn: Func): WrappedBoun
129
132
  }
130
133
 
131
134
  return await (async (): Promise<ReturnType<Func>> => {
135
+ const timer = TimingTracker.create()
136
+ timer.start()
137
+
132
138
  let result, error: Error | undefined
133
139
  try {
134
140
  result = await fn(...args)
@@ -136,6 +142,11 @@ export const createBoundary = <Func extends BaseBoundary>(fn: Func): WrappedBoun
136
142
  error = e as Error
137
143
  }
138
144
 
145
+ const timing = timer.end()
146
+ if (!timing) {
147
+ throw new Error('Failed to capture timing information')
148
+ }
149
+
139
150
  if (typeof error !== 'undefined') {
140
151
  const prevRecord: RecordType | undefined = findRecord(args, cacheTape)
141
152
  if (mode === 'proxy-catch' && typeof prevRecord !== 'undefined') {
@@ -146,7 +157,8 @@ export const createBoundary = <Func extends BaseBoundary>(fn: Func): WrappedBoun
146
157
  // Create an error record
147
158
  const errorRecord: BoundaryErrorRecord<FuncInput> = {
148
159
  input: args,
149
- error: error.message
160
+ error: error.message,
161
+ timing
150
162
  }
151
163
 
152
164
  if (hasRun) { runLog.push(errorRecord) }
@@ -158,7 +170,8 @@ export const createBoundary = <Func extends BaseBoundary>(fn: Func): WrappedBoun
158
170
  // Create a success record
159
171
  const successRecord: BoundarySuccessRecord<FuncInput, FuncOutput> = {
160
172
  input: args,
161
- output: result as FuncOutput
173
+ output: result as FuncOutput,
174
+ timing
162
175
  }
163
176
 
164
177
  if (hasRun) { runLog.push(successRecord) }