@forgehive/task 0.2.2 → 0.2.4
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/dist/index.d.ts +12 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +66 -13
- package/dist/index.js.map +1 -1
- package/dist/test/add-listener-with-boundaries.test.js +78 -7
- package/dist/test/add-listener-with-boundaries.test.js.map +1 -1
- package/dist/test/add-listener.test.js +36 -0
- package/dist/test/add-listener.test.js.map +1 -1
- package/dist/test/boundary-modes.test.js +45 -5
- package/dist/test/boundary-modes.test.js.map +1 -1
- package/dist/test/execution-record-boundaries.test.js +12 -2
- package/dist/test/execution-record-boundaries.test.js.map +1 -1
- package/dist/test/integration-enhanced-records.test.d.ts +2 -0
- package/dist/test/integration-enhanced-records.test.d.ts.map +1 -0
- package/dist/test/integration-enhanced-records.test.js +467 -0
- package/dist/test/integration-enhanced-records.test.js.map +1 -0
- package/dist/test/listen-execution-records.test.d.ts +2 -0
- package/dist/test/listen-execution-records.test.d.ts.map +1 -0
- package/dist/test/listen-execution-records.test.js +223 -0
- package/dist/test/listen-execution-records.test.js.map +1 -0
- package/dist/test/metrics-collection.test.d.ts +2 -0
- package/dist/test/metrics-collection.test.d.ts.map +1 -0
- package/dist/test/metrics-collection.test.js +409 -0
- package/dist/test/metrics-collection.test.js.map +1 -0
- package/dist/test/performance-edge-cases.test.d.ts +2 -0
- package/dist/test/performance-edge-cases.test.d.ts.map +1 -0
- package/dist/test/performance-edge-cases.test.js +502 -0
- package/dist/test/performance-edge-cases.test.js.map +1 -0
- package/dist/test/run-boundary.test.js +27 -3
- package/dist/test/run-boundary.test.js.map +1 -1
- package/dist/test/safe-replay-complex-boundary.test.js +110 -9
- package/dist/test/safe-replay-complex-boundary.test.js.map +1 -1
- package/dist/test/safe-replay.test.js +35 -5
- package/dist/test/safe-replay.test.js.map +1 -1
- package/dist/test/safe-run.test.js +46 -4
- package/dist/test/safe-run.test.js.map +1 -1
- package/dist/test/setmetrics-boundary.test.d.ts +2 -0
- package/dist/test/setmetrics-boundary.test.d.ts.map +1 -0
- package/dist/test/setmetrics-boundary.test.js +195 -0
- package/dist/test/setmetrics-boundary.test.js.map +1 -0
- package/dist/test/task-with-boundaries.test.js +63 -2
- package/dist/test/task-with-boundaries.test.js.map +1 -1
- package/dist/test/timing-capture.test.d.ts +2 -0
- package/dist/test/timing-capture.test.d.ts.map +1 -0
- package/dist/test/timing-capture.test.js +304 -0
- package/dist/test/timing-capture.test.js.map +1 -0
- package/dist/test/timing-utilities.test.d.ts +2 -0
- package/dist/test/timing-utilities.test.d.ts.map +1 -0
- package/dist/test/timing-utilities.test.js +127 -0
- package/dist/test/timing-utilities.test.js.map +1 -0
- package/dist/types.d.ts +93 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +78 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/boundary.d.ts +3 -0
- package/dist/utils/boundary.d.ts.map +1 -1
- package/dist/utils/boundary.js +11 -2
- package/dist/utils/boundary.js.map +1 -1
- package/package.json +3 -2
- package/src/index.ts +97 -14
- package/src/test/add-listener-with-boundaries.test.ts +78 -7
- package/src/test/add-listener.test.ts +36 -0
- package/src/test/boundary-modes.test.ts +45 -5
- package/src/test/execution-record-boundaries.test.ts +12 -2
- package/src/test/listen-execution-records.test.ts +295 -0
- package/src/test/metrics-collection.test.ts +476 -0
- package/src/test/performance-edge-cases.test.ts +596 -0
- package/src/test/run-boundary.test.ts +27 -3
- package/src/test/safe-replay-complex-boundary.test.ts +115 -10
- package/src/test/safe-replay.test.ts +35 -5
- package/src/test/safe-run.test.ts +46 -4
- package/src/test/setmetrics-boundary.test.ts +223 -0
- package/src/test/task-with-boundaries.test.ts +71 -5
- package/src/test/timing-capture.test.ts +348 -0
- package/src/test/timing-utilities.test.ts +145 -0
- package/src/types.ts +139 -0
- 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
|
+
}
|
package/src/utils/boundary.ts
CHANGED
|
@@ -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) }
|