@contrail/util 1.1.15-alpha-6 → 1.1.15-alpha-8
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.
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
2
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
12
|
const performance_util_1 = require("./performance-util");
|
|
4
13
|
function demonstrateStartEndSpan() {
|
|
@@ -71,5 +80,219 @@ function demonstrateWarningBehavior() {
|
|
|
71
80
|
console.log('Attempted to end "Non-existent Span" (should see warning above)');
|
|
72
81
|
(0, performance_util_1.displayTimingTree)();
|
|
73
82
|
}
|
|
74
|
-
|
|
75
|
-
|
|
83
|
+
function demonstrateParentNodeId() {
|
|
84
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
85
|
+
console.log('\n--- Demonstrating parentNodeId Functionality ---\n');
|
|
86
|
+
(0, performance_util_1.clearDefaultTimingProfile)();
|
|
87
|
+
console.log('Example 5: Concurrent async operations with explicit parent');
|
|
88
|
+
yield (0, performance_util_1.withTimingAsync)('Main Operation', () => __awaiter(this, void 0, void 0, function* () {
|
|
89
|
+
const mainOpId = (0, performance_util_1.getCurrentParentNodeId)();
|
|
90
|
+
console.log(`Main Operation ID: ${mainOpId}`);
|
|
91
|
+
console.log('Starting concurrent operations...');
|
|
92
|
+
const results = yield Promise.all([
|
|
93
|
+
(0, performance_util_1.withTimingAsync)('Fetch User Data', () => __awaiter(this, void 0, void 0, function* () {
|
|
94
|
+
yield new Promise((resolve) => setTimeout(resolve, 80));
|
|
95
|
+
console.log(' ✓ User data fetched');
|
|
96
|
+
return 'user-data';
|
|
97
|
+
}), { parentNodeId: mainOpId }),
|
|
98
|
+
(0, performance_util_1.withTimingAsync)('Fetch Permissions', () => __awaiter(this, void 0, void 0, function* () {
|
|
99
|
+
yield new Promise((resolve) => setTimeout(resolve, 60));
|
|
100
|
+
console.log(' ✓ Permissions fetched');
|
|
101
|
+
return 'permissions-data';
|
|
102
|
+
}), { parentNodeId: mainOpId }),
|
|
103
|
+
(0, performance_util_1.withTimingAsync)('Fetch Preferences', () => __awaiter(this, void 0, void 0, function* () {
|
|
104
|
+
yield new Promise((resolve) => setTimeout(resolve, 100));
|
|
105
|
+
console.log(' ✓ Preferences fetched');
|
|
106
|
+
return 'preferences-data';
|
|
107
|
+
}), { parentNodeId: mainOpId }),
|
|
108
|
+
]);
|
|
109
|
+
console.log('All concurrent operations completed:', results);
|
|
110
|
+
yield (0, performance_util_1.withTimingAsync)('Process Results', () => __awaiter(this, void 0, void 0, function* () {
|
|
111
|
+
yield new Promise((resolve) => setTimeout(resolve, 30));
|
|
112
|
+
console.log(' ✓ Results processed');
|
|
113
|
+
}));
|
|
114
|
+
}));
|
|
115
|
+
(0, performance_util_1.displayTimingTree)();
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
function demonstrateMixedNesting() {
|
|
119
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
120
|
+
console.log('\n--- Demonstrating Mixed Natural and Explicit Nesting ---\n');
|
|
121
|
+
(0, performance_util_1.clearDefaultTimingProfile)();
|
|
122
|
+
console.log('Example 6: Mixed natural and explicit nesting');
|
|
123
|
+
yield (0, performance_util_1.withTimingAsync)('Application Startup', () => __awaiter(this, void 0, void 0, function* () {
|
|
124
|
+
yield (0, performance_util_1.withTimingAsync)('Load Configuration', () => __awaiter(this, void 0, void 0, function* () {
|
|
125
|
+
yield new Promise((resolve) => setTimeout(resolve, 50));
|
|
126
|
+
console.log(' ✓ Configuration loaded');
|
|
127
|
+
yield (0, performance_util_1.withTimingAsync)('Validate Config', () => __awaiter(this, void 0, void 0, function* () {
|
|
128
|
+
yield new Promise((resolve) => setTimeout(resolve, 20));
|
|
129
|
+
console.log(' ✓ Configuration validated');
|
|
130
|
+
}));
|
|
131
|
+
}));
|
|
132
|
+
console.log('Starting parallel initialization...');
|
|
133
|
+
(0, performance_util_1.startSpan)('Initialization');
|
|
134
|
+
const ininitializeId = (0, performance_util_1.getCurrentParentNodeId)();
|
|
135
|
+
(0, performance_util_1.withTimingSync)('Do something before initialization', () => {
|
|
136
|
+
const start = performance.now();
|
|
137
|
+
while (performance.now() < start + 20) {
|
|
138
|
+
}
|
|
139
|
+
console.log(' ✓ Pre-initialization task completed');
|
|
140
|
+
});
|
|
141
|
+
yield Promise.all([
|
|
142
|
+
(0, performance_util_1.withTimingAsync)('Initialize Database', () => __awaiter(this, void 0, void 0, function* () {
|
|
143
|
+
yield new Promise((resolve) => setTimeout(resolve, 120));
|
|
144
|
+
console.log(' ✓ Database initialized');
|
|
145
|
+
}), { parentNodeId: ininitializeId }),
|
|
146
|
+
(0, performance_util_1.withTimingAsync)('Initialize Cache', () => __awaiter(this, void 0, void 0, function* () {
|
|
147
|
+
yield new Promise((resolve) => setTimeout(resolve, 90));
|
|
148
|
+
console.log(' ✓ Cache initialized');
|
|
149
|
+
}), { parentNodeId: ininitializeId }),
|
|
150
|
+
(0, performance_util_1.withTimingAsync)('Initialize Logging', () => __awaiter(this, void 0, void 0, function* () {
|
|
151
|
+
yield new Promise((resolve) => setTimeout(resolve, 40));
|
|
152
|
+
console.log(' ✓ Logging initialized');
|
|
153
|
+
}), { parentNodeId: ininitializeId }),
|
|
154
|
+
]);
|
|
155
|
+
(0, performance_util_1.withTimingSync)('Do something after initialization', () => {
|
|
156
|
+
const start = performance.now();
|
|
157
|
+
while (performance.now() < start + 20) {
|
|
158
|
+
}
|
|
159
|
+
console.log(' ✓ Post-initialization task completed');
|
|
160
|
+
});
|
|
161
|
+
(0, performance_util_1.endSpan)('Initialization');
|
|
162
|
+
yield (0, performance_util_1.withTimingAsync)('Final Setup', () => __awaiter(this, void 0, void 0, function* () {
|
|
163
|
+
yield new Promise((resolve) => setTimeout(resolve, 30));
|
|
164
|
+
console.log(' ✓ Final setup completed');
|
|
165
|
+
}));
|
|
166
|
+
}));
|
|
167
|
+
(0, performance_util_1.displayTimingTree)();
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
function demonstrateSyncWithParentId() {
|
|
171
|
+
console.log('\n--- Demonstrating Sync Functions with parentNodeId ---\n');
|
|
172
|
+
(0, performance_util_1.clearDefaultTimingProfile)();
|
|
173
|
+
console.log('Example 7: Sync functions with explicit parent');
|
|
174
|
+
(0, performance_util_1.withTimingSync)('Data Processing Pipeline', () => {
|
|
175
|
+
const pipelineId = (0, performance_util_1.getCurrentParentNodeId)();
|
|
176
|
+
console.log(`Pipeline ID: ${pipelineId}`);
|
|
177
|
+
(0, performance_util_1.withTimingSync)('Load Input Data', () => {
|
|
178
|
+
const start = performance.now();
|
|
179
|
+
while (performance.now() < start + 50) {
|
|
180
|
+
}
|
|
181
|
+
console.log(' ✓ Input data loaded');
|
|
182
|
+
});
|
|
183
|
+
(0, performance_util_1.withTimingSync)('Process Batch 1', () => {
|
|
184
|
+
const start = performance.now();
|
|
185
|
+
while (performance.now() < start + 80) {
|
|
186
|
+
}
|
|
187
|
+
console.log(' ✓ Batch 1 processed');
|
|
188
|
+
}, { parentNodeId: pipelineId });
|
|
189
|
+
(0, performance_util_1.withTimingSync)('Process Batch 2', () => {
|
|
190
|
+
const start = performance.now();
|
|
191
|
+
while (performance.now() < start + 70) {
|
|
192
|
+
}
|
|
193
|
+
console.log(' ✓ Batch 2 processed');
|
|
194
|
+
}, { parentNodeId: pipelineId });
|
|
195
|
+
(0, performance_util_1.withTimingSync)('Process Batch 3', () => {
|
|
196
|
+
const start = performance.now();
|
|
197
|
+
while (performance.now() < start + 90) {
|
|
198
|
+
}
|
|
199
|
+
console.log(' ✓ Batch 3 processed');
|
|
200
|
+
}, { parentNodeId: pipelineId });
|
|
201
|
+
(0, performance_util_1.withTimingSync)('Save Results', () => {
|
|
202
|
+
const start = performance.now();
|
|
203
|
+
while (performance.now() < start + 40) {
|
|
204
|
+
}
|
|
205
|
+
console.log(' ✓ Results saved');
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
(0, performance_util_1.displayTimingTree)();
|
|
209
|
+
}
|
|
210
|
+
function demonstrateManualSpansWithParentId() {
|
|
211
|
+
console.log('\n--- Demonstrating Manual Spans with parentNodeId ---\n');
|
|
212
|
+
(0, performance_util_1.clearDefaultTimingProfile)();
|
|
213
|
+
console.log('Example 8: Manual spans with explicit parent');
|
|
214
|
+
(0, performance_util_1.startSpan)('Service Request');
|
|
215
|
+
const serviceId = (0, performance_util_1.getCurrentParentNodeId)();
|
|
216
|
+
console.log(`Service Request ID: ${serviceId}`);
|
|
217
|
+
(0, performance_util_1.startSpan)('Authenticate User');
|
|
218
|
+
const start1 = performance.now();
|
|
219
|
+
while (performance.now() < start1 + 30) {
|
|
220
|
+
}
|
|
221
|
+
(0, performance_util_1.endSpan)('Authenticate User');
|
|
222
|
+
console.log(' ✓ User authenticated');
|
|
223
|
+
(0, performance_util_1.startSpan)('Validate Input', { parentNodeId: serviceId });
|
|
224
|
+
const start2 = performance.now();
|
|
225
|
+
while (performance.now() < start2 + 20) {
|
|
226
|
+
}
|
|
227
|
+
(0, performance_util_1.endSpan)('Validate Input');
|
|
228
|
+
console.log(' ✓ Input validated');
|
|
229
|
+
(0, performance_util_1.startSpan)('Check Rate Limits', { parentNodeId: serviceId });
|
|
230
|
+
const start3 = performance.now();
|
|
231
|
+
while (performance.now() < start3 + 15) {
|
|
232
|
+
}
|
|
233
|
+
(0, performance_util_1.endSpan)('Check Rate Limits');
|
|
234
|
+
console.log(' ✓ Rate limits checked');
|
|
235
|
+
(0, performance_util_1.startSpan)('Log Request', { parentNodeId: serviceId });
|
|
236
|
+
const start4 = performance.now();
|
|
237
|
+
while (performance.now() < start4 + 10) {
|
|
238
|
+
}
|
|
239
|
+
(0, performance_util_1.endSpan)('Log Request');
|
|
240
|
+
console.log(' ✓ Request logged');
|
|
241
|
+
(0, performance_util_1.startSpan)('Process Request');
|
|
242
|
+
const start5 = performance.now();
|
|
243
|
+
while (performance.now() < start5 + 60) {
|
|
244
|
+
}
|
|
245
|
+
(0, performance_util_1.endSpan)('Process Request');
|
|
246
|
+
console.log(' ✓ Request processed');
|
|
247
|
+
(0, performance_util_1.endSpan)('Service Request');
|
|
248
|
+
(0, performance_util_1.displayTimingTree)();
|
|
249
|
+
}
|
|
250
|
+
function demonstrateNestingComparison() {
|
|
251
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
252
|
+
console.log('\n--- Demonstrating Natural vs Explicit Nesting ---\n');
|
|
253
|
+
console.log('Natural Nesting (call stack based):');
|
|
254
|
+
(0, performance_util_1.clearDefaultTimingProfile)();
|
|
255
|
+
yield (0, performance_util_1.withTimingAsync)('Sequential Operations', () => __awaiter(this, void 0, void 0, function* () {
|
|
256
|
+
yield (0, performance_util_1.withTimingAsync)('Operation 1', () => __awaiter(this, void 0, void 0, function* () {
|
|
257
|
+
yield new Promise((resolve) => setTimeout(resolve, 30));
|
|
258
|
+
}));
|
|
259
|
+
yield (0, performance_util_1.withTimingAsync)('Operation 2', () => __awaiter(this, void 0, void 0, function* () {
|
|
260
|
+
yield new Promise((resolve) => setTimeout(resolve, 40));
|
|
261
|
+
}));
|
|
262
|
+
yield (0, performance_util_1.withTimingAsync)('Operation 3', () => __awaiter(this, void 0, void 0, function* () {
|
|
263
|
+
yield new Promise((resolve) => setTimeout(resolve, 35));
|
|
264
|
+
}));
|
|
265
|
+
}));
|
|
266
|
+
(0, performance_util_1.displayTimingTree)();
|
|
267
|
+
console.log('\nExplicit Parent Assignment (logical grouping):');
|
|
268
|
+
(0, performance_util_1.clearDefaultTimingProfile)();
|
|
269
|
+
yield (0, performance_util_1.withTimingAsync)('Parallel Operations', () => __awaiter(this, void 0, void 0, function* () {
|
|
270
|
+
const parentId = (0, performance_util_1.getCurrentParentNodeId)();
|
|
271
|
+
yield Promise.all([
|
|
272
|
+
(0, performance_util_1.withTimingAsync)('Operation 1', () => __awaiter(this, void 0, void 0, function* () {
|
|
273
|
+
yield new Promise((resolve) => setTimeout(resolve, 30));
|
|
274
|
+
}), { parentNodeId: parentId }),
|
|
275
|
+
(0, performance_util_1.withTimingAsync)('Operation 2', () => __awaiter(this, void 0, void 0, function* () {
|
|
276
|
+
yield new Promise((resolve) => setTimeout(resolve, 40));
|
|
277
|
+
}), { parentNodeId: parentId }),
|
|
278
|
+
(0, performance_util_1.withTimingAsync)('Operation 3', () => __awaiter(this, void 0, void 0, function* () {
|
|
279
|
+
yield new Promise((resolve) => setTimeout(resolve, 35));
|
|
280
|
+
}), { parentNodeId: parentId }),
|
|
281
|
+
]);
|
|
282
|
+
}));
|
|
283
|
+
(0, performance_util_1.displayTimingTree)();
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
function runAllExamples() {
|
|
287
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
288
|
+
demonstrateStartEndSpan();
|
|
289
|
+
demonstrateWarningBehavior();
|
|
290
|
+
yield demonstrateParentNodeId();
|
|
291
|
+
yield demonstrateMixedNesting();
|
|
292
|
+
demonstrateSyncWithParentId();
|
|
293
|
+
demonstrateManualSpansWithParentId();
|
|
294
|
+
yield demonstrateNestingComparison();
|
|
295
|
+
console.log('\nAll examples completed. ');
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
runAllExamples().catch(console.error);
|
|
@@ -2,23 +2,34 @@ type TimingSpans = TimingNode[];
|
|
|
2
2
|
type TimingStack = TimingNode[];
|
|
3
3
|
type TimingNode = {
|
|
4
4
|
label: string;
|
|
5
|
+
id: string;
|
|
5
6
|
durationMs: number;
|
|
6
7
|
children: TimingNode[];
|
|
8
|
+
startTime: number;
|
|
7
9
|
};
|
|
8
10
|
type ActiveSpan = {
|
|
9
11
|
node: TimingNode;
|
|
10
12
|
startTime: number;
|
|
13
|
+
wasAddedToStack: boolean;
|
|
11
14
|
};
|
|
12
15
|
type TimingProfile = {
|
|
16
|
+
activeSpans: Map<string, ActiveSpan>;
|
|
17
|
+
spansById: Map<string, TimingNode>;
|
|
13
18
|
timingSpans: TimingSpans;
|
|
14
19
|
timingStack: TimingStack;
|
|
15
|
-
activeSpans: Map<string, ActiveSpan>;
|
|
16
20
|
};
|
|
17
21
|
export declare function getDefaultTimingProfile(): TimingProfile;
|
|
18
22
|
export declare function clearDefaultTimingProfile(): void;
|
|
19
|
-
export declare function
|
|
20
|
-
export declare function
|
|
23
|
+
export declare function getCurrentParentNodeId(): string | null;
|
|
24
|
+
export declare function withTimingAsync<T>(label: string, fn: () => Promise<T>, options?: {
|
|
25
|
+
parentNodeId?: string | null;
|
|
26
|
+
}): Promise<T>;
|
|
27
|
+
export declare function withTimingSync<T>(label: string, fn: () => T, options?: {
|
|
28
|
+
parentNodeId?: string | null;
|
|
29
|
+
}): T;
|
|
21
30
|
export declare function displayTimingTree(nodes?: TimingSpans, depth?: number): string;
|
|
22
|
-
export declare function startSpan(spanName: string
|
|
31
|
+
export declare function startSpan(spanName: string, options?: {
|
|
32
|
+
parentNodeId?: string | null;
|
|
33
|
+
}): void;
|
|
23
34
|
export declare function endSpan(spanName: string): void;
|
|
24
35
|
export {};
|
|
@@ -11,15 +11,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.getDefaultTimingProfile = getDefaultTimingProfile;
|
|
13
13
|
exports.clearDefaultTimingProfile = clearDefaultTimingProfile;
|
|
14
|
+
exports.getCurrentParentNodeId = getCurrentParentNodeId;
|
|
14
15
|
exports.withTimingAsync = withTimingAsync;
|
|
15
16
|
exports.withTimingSync = withTimingSync;
|
|
16
17
|
exports.displayTimingTree = displayTimingTree;
|
|
17
18
|
exports.startSpan = startSpan;
|
|
18
19
|
exports.endSpan = endSpan;
|
|
20
|
+
const crypto_1 = require("crypto");
|
|
19
21
|
const DEFAULT_TIMING_PROFILE = {
|
|
22
|
+
activeSpans: new Map(),
|
|
23
|
+
spansById: new Map(),
|
|
20
24
|
timingSpans: [],
|
|
21
25
|
timingStack: [],
|
|
22
|
-
activeSpans: new Map(),
|
|
23
26
|
};
|
|
24
27
|
function getDefaultTimingProfile() {
|
|
25
28
|
return DEFAULT_TIMING_PROFILE;
|
|
@@ -30,48 +33,91 @@ function getTimingStack() {
|
|
|
30
33
|
function getTimingSpans() {
|
|
31
34
|
return DEFAULT_TIMING_PROFILE.timingSpans;
|
|
32
35
|
}
|
|
36
|
+
function getNodeById(id) {
|
|
37
|
+
return DEFAULT_TIMING_PROFILE.spansById.get(id);
|
|
38
|
+
}
|
|
39
|
+
function setSpanId(id, node) {
|
|
40
|
+
DEFAULT_TIMING_PROFILE.spansById.set(id, node);
|
|
41
|
+
}
|
|
33
42
|
function clearDefaultTimingProfile() {
|
|
34
43
|
DEFAULT_TIMING_PROFILE.timingSpans = [];
|
|
35
44
|
DEFAULT_TIMING_PROFILE.timingStack = [];
|
|
36
45
|
DEFAULT_TIMING_PROFILE.activeSpans.clear();
|
|
46
|
+
DEFAULT_TIMING_PROFILE.spansById.clear();
|
|
37
47
|
}
|
|
38
|
-
function
|
|
48
|
+
function getParentNode() {
|
|
49
|
+
const stack = getTimingStack();
|
|
50
|
+
return stack.length > 0 ? stack[stack.length - 1] : null;
|
|
51
|
+
}
|
|
52
|
+
function getCurrentParentNodeId() {
|
|
53
|
+
const parentNode = getParentNode();
|
|
54
|
+
return parentNode ? parentNode.id : null;
|
|
55
|
+
}
|
|
56
|
+
function withTimingAsync(label, fn, options) {
|
|
39
57
|
return __awaiter(this, void 0, void 0, function* () {
|
|
40
58
|
const start = performance.now();
|
|
41
|
-
const
|
|
42
|
-
const
|
|
59
|
+
const chosenId = options === null || options === void 0 ? void 0 : options.parentNodeId;
|
|
60
|
+
const uniqueId = (0, crypto_1.randomUUID)();
|
|
61
|
+
const timingNode = {
|
|
62
|
+
label,
|
|
63
|
+
durationMs: 0,
|
|
64
|
+
children: [],
|
|
65
|
+
id: uniqueId,
|
|
66
|
+
startTime: start,
|
|
67
|
+
};
|
|
68
|
+
setSpanId(uniqueId, timingNode);
|
|
69
|
+
const parent = chosenId ? getNodeById(chosenId) : getParentNode();
|
|
43
70
|
if (parent) {
|
|
44
71
|
parent.children.push(timingNode);
|
|
45
72
|
}
|
|
46
73
|
else {
|
|
47
74
|
getTimingSpans().push(timingNode);
|
|
48
75
|
}
|
|
49
|
-
|
|
76
|
+
const isRunningConcurrently = !!chosenId;
|
|
77
|
+
if (!isRunningConcurrently) {
|
|
78
|
+
getTimingStack().push(timingNode);
|
|
79
|
+
}
|
|
50
80
|
return fn().finally(() => {
|
|
51
81
|
const end = performance.now();
|
|
52
82
|
timingNode.durationMs = end - start;
|
|
53
|
-
|
|
83
|
+
if (!isRunningConcurrently) {
|
|
84
|
+
getTimingStack().pop();
|
|
85
|
+
}
|
|
54
86
|
});
|
|
55
87
|
});
|
|
56
88
|
}
|
|
57
|
-
function withTimingSync(label, fn) {
|
|
89
|
+
function withTimingSync(label, fn, options) {
|
|
58
90
|
const start = performance.now();
|
|
59
|
-
const
|
|
60
|
-
const
|
|
91
|
+
const chosenId = options === null || options === void 0 ? void 0 : options.parentNodeId;
|
|
92
|
+
const uniqueId = (0, crypto_1.randomUUID)();
|
|
93
|
+
const timingNode = {
|
|
94
|
+
label,
|
|
95
|
+
durationMs: 0,
|
|
96
|
+
children: [],
|
|
97
|
+
id: uniqueId,
|
|
98
|
+
startTime: start,
|
|
99
|
+
};
|
|
100
|
+
setSpanId(uniqueId, timingNode);
|
|
101
|
+
const parent = chosenId ? getNodeById(chosenId) : getParentNode();
|
|
61
102
|
if (parent) {
|
|
62
103
|
parent.children.push(timingNode);
|
|
63
104
|
}
|
|
64
105
|
else {
|
|
65
106
|
getTimingSpans().push(timingNode);
|
|
66
107
|
}
|
|
67
|
-
|
|
108
|
+
const isRunningConcurrently = !!chosenId;
|
|
109
|
+
if (!isRunningConcurrently) {
|
|
110
|
+
getTimingStack().push(timingNode);
|
|
111
|
+
}
|
|
68
112
|
try {
|
|
69
113
|
return fn();
|
|
70
114
|
}
|
|
71
115
|
finally {
|
|
72
116
|
const end = performance.now();
|
|
73
117
|
timingNode.durationMs = end - start;
|
|
74
|
-
|
|
118
|
+
if (!isRunningConcurrently) {
|
|
119
|
+
getTimingStack().pop();
|
|
120
|
+
}
|
|
75
121
|
}
|
|
76
122
|
}
|
|
77
123
|
function displayTimingTree(nodes = DEFAULT_TIMING_PROFILE.timingSpans, depth = 0) {
|
|
@@ -79,14 +125,58 @@ function displayTimingTree(nodes = DEFAULT_TIMING_PROFILE.timingSpans, depth = 0
|
|
|
79
125
|
const indent = ' '.repeat(depth);
|
|
80
126
|
const totalWidth = 120;
|
|
81
127
|
const timeWidth = 10;
|
|
82
|
-
nodes.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
128
|
+
const sortedNodes = [...nodes].sort((a, b) => a.startTime - b.startTime);
|
|
129
|
+
const groups = [];
|
|
130
|
+
let currentGroup = [];
|
|
131
|
+
for (let i = 0; i < sortedNodes.length; i++) {
|
|
132
|
+
const node = sortedNodes[i];
|
|
133
|
+
if (currentGroup.length === 0) {
|
|
134
|
+
currentGroup = [node];
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
const hasOverlap = currentGroup.some((groupNode) => {
|
|
138
|
+
const nodeEnd = node.startTime + node.durationMs;
|
|
139
|
+
const groupNodeEnd = groupNode.startTime + groupNode.durationMs;
|
|
140
|
+
return node.startTime < groupNodeEnd && nodeEnd > groupNode.startTime;
|
|
141
|
+
});
|
|
142
|
+
if (hasOverlap) {
|
|
143
|
+
currentGroup.push(node);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
groups.push(currentGroup);
|
|
147
|
+
currentGroup = [node];
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (currentGroup.length > 0) {
|
|
152
|
+
groups.push(currentGroup);
|
|
153
|
+
}
|
|
154
|
+
groups.forEach((group) => {
|
|
155
|
+
if (group.length === 1) {
|
|
156
|
+
const node = group[0];
|
|
157
|
+
const timeStr = `${node.durationMs.toFixed(2)}ms`;
|
|
158
|
+
const labelWithIndent = `${indent}${node.label}`;
|
|
159
|
+
const availableWidth = totalWidth - timeWidth - labelWithIndent.length;
|
|
160
|
+
const dots = '.'.repeat(Math.max(2, availableWidth));
|
|
161
|
+
lines.push(`${labelWithIndent}${dots}${timeStr.padStart(timeWidth)}`);
|
|
162
|
+
if (node.children && node.children.length > 0) {
|
|
163
|
+
lines.push(displayTimingTree(node.children, depth + 1));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
const parallelHeader = `${indent}∥ Parallel Operations (${group.length})`;
|
|
168
|
+
lines.push(parallelHeader);
|
|
169
|
+
group.forEach((node) => {
|
|
170
|
+
const timeStr = `${node.durationMs.toFixed(2)}ms`;
|
|
171
|
+
const parallelIndicator = '∥ ';
|
|
172
|
+
const labelWithIndent = `${indent}${parallelIndicator}${node.label}`;
|
|
173
|
+
const availableWidth = totalWidth - timeWidth - labelWithIndent.length;
|
|
174
|
+
const dots = '.'.repeat(Math.max(2, availableWidth));
|
|
175
|
+
lines.push(`${labelWithIndent}${dots}${timeStr.padStart(timeWidth)}`);
|
|
176
|
+
if (node.children && node.children.length > 0) {
|
|
177
|
+
lines.push(displayTimingTree(node.children, depth + 1));
|
|
178
|
+
}
|
|
179
|
+
});
|
|
90
180
|
}
|
|
91
181
|
});
|
|
92
182
|
const result = lines.join('\n');
|
|
@@ -97,24 +187,37 @@ function displayTimingTree(nodes = DEFAULT_TIMING_PROFILE.timingSpans, depth = 0
|
|
|
97
187
|
}
|
|
98
188
|
return result;
|
|
99
189
|
}
|
|
100
|
-
function startSpan(spanName) {
|
|
190
|
+
function startSpan(spanName, options) {
|
|
101
191
|
if (DEFAULT_TIMING_PROFILE.activeSpans.has(spanName)) {
|
|
102
192
|
console.warn(`Span "${spanName}" is already active. Please end it before starting a new one with the same name.`);
|
|
103
193
|
return;
|
|
104
194
|
}
|
|
105
195
|
const startTime = performance.now();
|
|
106
|
-
const
|
|
107
|
-
const
|
|
196
|
+
const chosenId = options === null || options === void 0 ? void 0 : options.parentNodeId;
|
|
197
|
+
const uniqueId = (0, crypto_1.randomUUID)();
|
|
198
|
+
const timingNode = {
|
|
199
|
+
label: spanName,
|
|
200
|
+
durationMs: 0,
|
|
201
|
+
children: [],
|
|
202
|
+
id: uniqueId,
|
|
203
|
+
startTime: startTime,
|
|
204
|
+
};
|
|
205
|
+
setSpanId(uniqueId, timingNode);
|
|
206
|
+
const parent = chosenId ? getNodeById(chosenId) : getParentNode();
|
|
108
207
|
if (parent) {
|
|
109
208
|
parent.children.push(timingNode);
|
|
110
209
|
}
|
|
111
210
|
else {
|
|
112
211
|
getTimingSpans().push(timingNode);
|
|
113
212
|
}
|
|
114
|
-
|
|
213
|
+
const isRunningConcurrently = !!chosenId;
|
|
214
|
+
if (!isRunningConcurrently) {
|
|
215
|
+
getTimingStack().push(timingNode);
|
|
216
|
+
}
|
|
115
217
|
DEFAULT_TIMING_PROFILE.activeSpans.set(spanName, {
|
|
116
218
|
node: timingNode,
|
|
117
219
|
startTime: startTime,
|
|
220
|
+
wasAddedToStack: !isRunningConcurrently,
|
|
118
221
|
});
|
|
119
222
|
}
|
|
120
223
|
function endSpan(spanName) {
|
|
@@ -125,14 +228,16 @@ function endSpan(spanName) {
|
|
|
125
228
|
}
|
|
126
229
|
const endTime = performance.now();
|
|
127
230
|
activeSpan.node.durationMs = endTime - activeSpan.startTime;
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
231
|
+
if (activeSpan.wasAddedToStack) {
|
|
232
|
+
const currentTop = getTimingStack()[getTimingStack().length - 1];
|
|
233
|
+
if (currentTop === activeSpan.node) {
|
|
234
|
+
getTimingStack().pop();
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
const stackIndex = getTimingStack().findIndex((node) => node === activeSpan.node);
|
|
238
|
+
if (stackIndex !== -1) {
|
|
239
|
+
getTimingStack().splice(stackIndex, 1);
|
|
240
|
+
}
|
|
136
241
|
}
|
|
137
242
|
}
|
|
138
243
|
DEFAULT_TIMING_PROFILE.activeSpans.delete(spanName);
|