@routier/core 0.0.6 → 0.1.0-rc.0

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 (57) hide show
  1. package/dist/capabilities/Capability.d.ts +6 -16
  2. package/dist/capabilities/PerformanceCapability.d.ts +4 -6
  3. package/dist/capabilities/TracingCapability.d.ts +3 -6
  4. package/dist/capabilities/index.js +399 -343
  5. package/dist/capabilities/index.js.map +1 -1
  6. package/dist/capabilities/types.d.ts +10 -19
  7. package/dist/codegen/blocks.d.ts +8 -0
  8. package/dist/codegen/handlers/CompareIdsHandlerBuilder.d.ts +4 -0
  9. package/dist/codegen/handlers/compare/CompareArrayHandler.d.ts +6 -0
  10. package/dist/codegen/handlers/compare/CompareDateHandler.d.ts +6 -0
  11. package/dist/codegen/handlers/compareIds/CompareIdsKeyHandler.d.ts +6 -0
  12. package/dist/codegen/handlers/index.d.ts +1 -0
  13. package/dist/codegen/handlers/serialize/SerializeDateHandler.d.ts +2 -1
  14. package/dist/codegen/handlers/serialize/SerializeFunctionHandler.d.ts +6 -0
  15. package/dist/codegen/index.js +39 -0
  16. package/dist/codegen/index.js.map +1 -1
  17. package/dist/collections/index.js +1 -1
  18. package/dist/collections/index.js.map +1 -1
  19. package/dist/expressions/index.js +203 -29
  20. package/dist/expressions/index.js.map +1 -1
  21. package/dist/index.js +1147 -507
  22. package/dist/index.js.map +1 -1
  23. package/dist/pipeline/TrampolinePipeline.d.ts +1 -0
  24. package/dist/pipeline/index.js +71 -47
  25. package/dist/pipeline/index.js.map +1 -1
  26. package/dist/plugins/EphemeralDataPlugin.d.ts +2 -2
  27. package/dist/plugins/index.js +252 -62
  28. package/dist/plugins/index.js.map +1 -1
  29. package/dist/plugins/query/types.d.ts +5 -0
  30. package/dist/plugins/replication/OptimisticReplicationDbPlugin.d.ts +2 -1
  31. package/dist/plugins/replication/ReplicationDbPlugin.d.ts +2 -1
  32. package/dist/plugins/translators/DataTranslator.d.ts +3 -1
  33. package/dist/plugins/translators/JsonTranslator.d.ts +1 -0
  34. package/dist/plugins/translators/SqlTranslator.d.ts +1 -0
  35. package/dist/plugins/translators/TranslatedArrayValue.d.ts +6 -0
  36. package/dist/plugins/translators/TranslatedGroupValue.d.ts +6 -0
  37. package/dist/plugins/translators/TranslatedSingleValue.d.ts +6 -0
  38. package/dist/plugins/translators/index.d.ts +4 -0
  39. package/dist/plugins/translators/types.d.ts +10 -0
  40. package/dist/plugins/types.d.ts +2 -1
  41. package/dist/schema/PropertyInfo.d.ts +7 -2
  42. package/dist/schema/SchemaDefinition.d.ts +1 -1
  43. package/dist/schema/communication/broadcast.d.ts +3 -3
  44. package/dist/schema/index.js +504 -73
  45. package/dist/schema/index.js.map +1 -1
  46. package/dist/schema/property/modifiers/SchemaTracked.d.ts +3 -1
  47. package/dist/schema/table/SchemaComputed.d.ts +1 -1
  48. package/dist/schema/testSchemas.test.d.ts +60 -36
  49. package/dist/schema/types.d.ts +16 -1
  50. package/dist/utilities/index.d.ts +1 -0
  51. package/dist/utilities/index.js +164 -2
  52. package/dist/utilities/index.js.map +1 -1
  53. package/dist/utilities/strings.d.ts +34 -0
  54. package/dist/utilities/strings.test.d.ts +1 -0
  55. package/dist/utilities/unsafeCast.d.ts +1 -0
  56. package/package.json +1 -1
  57. package/readme.md +1 -1
@@ -9,214 +9,103 @@ __webpack_require__.d(__webpack_exports__, {
9
9
  Capability: () => (Capability)
10
10
  });
11
11
  class Capability {
12
+ excludedNames = new Set([
13
+ "Array",
14
+ "Set",
15
+ "Map",
16
+ "AbortController",
17
+ "AbortSignal",
18
+ "SchemaString",
19
+ "SchemaNumber",
20
+ "SchemaArray",
21
+ "SchemaBoolean",
22
+ "SchemaDate",
23
+ "SchemaObject",
24
+ "SchemaDefault",
25
+ "SchemaDeserialize",
26
+ "SchemaDistinct",
27
+ "SchemaFrom",
28
+ "SchemaIdentity",
29
+ "SchemaIndex",
30
+ "SchemaKey",
31
+ "SchemaNullable",
32
+ "SchemaOptional",
33
+ "SchemaReadonly",
34
+ "SchemaSerialize",
35
+ "SchemaTracked",
36
+ "SchemaComputed",
37
+ "SchemaFunction",
38
+ "SchemaBase",
39
+ "SchemaDefinition"
40
+ ]);
12
41
  isValidObject(obj) {
13
42
  return typeof obj === "object" && obj !== null;
14
43
  }
15
- getObjectName(obj) {
16
- return obj?.constructor?.name || 'root';
17
- }
18
- exploreObjectMethods(obj, callback, options = {}) {
19
- if (!this.isValidObject(obj)) {
20
- return;
21
- }
22
- const { maxDepth = 10, includeNonEnumerable = false, filter = () => true } = options;
23
- const rootName = this.getObjectName(obj);
24
- const visited = new Set();
25
- // Explore root methods
26
- this.exploreRootMethods(obj, callback, filter);
27
- // Explore nested methods
28
- this.exploreNestedMethods(obj, callback, filter, [rootName], visited, maxDepth, includeNonEnumerable);
29
- }
30
- exploreRootMethods(obj, callback, filter) {
31
- const methodNames = this.extractMethodNames(obj);
32
- for (const methodName of methodNames) {
33
- const methodInfo = {
34
- methodName,
35
- instance: obj,
36
- methodPath: [String(methodName)],
37
- parent: null
38
- };
39
- if (filter(methodInfo)) {
40
- callback(methodInfo);
41
- }
42
- }
43
- }
44
- extractMethodNames(obj) {
45
- const methodNames = new Set();
46
- let prototype = obj;
47
- while (prototype && prototype !== Object.prototype) {
48
- const allKeys = [
49
- ...Object.getOwnPropertyNames(prototype),
50
- ...Object.getOwnPropertySymbols(prototype),
51
- ];
52
- for (const key of allKeys) {
53
- const descriptor = Object.getOwnPropertyDescriptor(prototype, key);
54
- if (this.isCallableMethod(descriptor, key)) {
55
- methodNames.add(key);
56
- }
57
- }
58
- prototype = Object.getPrototypeOf(prototype);
59
- }
60
- return Array.from(methodNames);
61
- }
62
44
  isCallableMethod(descriptor, key) {
63
45
  return (descriptor?.value &&
64
46
  typeof descriptor.value === 'function' &&
65
47
  key !== 'constructor' &&
66
48
  key !== 'undefined');
67
49
  }
68
- isCustomClassInstance(value) {
69
- if (value === null || (typeof value !== "object" && typeof value !== "function")) {
50
+ canExplore(descriptor) {
51
+ if (typeof descriptor.value !== "object") {
70
52
  return false;
71
53
  }
72
- const objectTag = Object.prototype.toString.call(value);
73
- // Most built-ins have distinct tags; user classes default to "[object Object]"
74
- // Caveat: Symbol.toStringTag can spoof this.
75
- return objectTag === "[object Object]";
76
- }
77
- shouldProcessProperty(property) {
78
- return (this.isCustomClassInstance(property) &&
79
- property?.constructor.name !== "Object");
80
- }
81
- exploreNestedMethods(obj, callback, filter, initialPath, visited, maxDepth, includeNonEnumerable) {
82
- if (visited.has(obj) || initialPath.length > maxDepth) {
83
- return;
84
- }
85
- visited.add(obj);
86
- if (!this.isValidObject(obj)) {
87
- return;
54
+ if (descriptor.value == null) {
55
+ return false;
88
56
  }
89
- const properties = includeNonEnumerable
90
- ? Object.getOwnPropertyNames(obj)
91
- : Object.keys(obj);
92
- for (const propertyName of properties) {
93
- const property = obj[propertyName];
94
- if (!this.shouldProcessProperty(property)) {
95
- continue;
96
- }
97
- const newPath = [...initialPath, propertyName];
98
- // Explore methods on this property
99
- this.explorePropertyMethods(property, callback, filter, newPath, obj);
100
- // Recursively explore nested objects
101
- this.exploreNestedMethods(property, callback, filter, newPath, visited, maxDepth, includeNonEnumerable);
57
+ const name = this.getName(descriptor.value);
58
+ if (name == null) {
59
+ return true;
102
60
  }
61
+ return this.excludedNames.has(name) === false;
103
62
  }
104
- explorePropertyMethods(property, callback, filter, methodPath, parent) {
105
- const methodNames = this.extractMethodNames(property);
106
- for (const methodName of methodNames) {
107
- const fullMethodPath = [...methodPath, String(methodName)];
108
- const methodInfo = {
109
- methodName,
110
- instance: property,
111
- methodPath: fullMethodPath,
112
- parent
113
- };
114
- if (filter(methodInfo)) {
115
- callback(methodInfo);
116
- }
63
+ getName(value) {
64
+ if (value.constructor != null) {
65
+ return value.constructor.name;
117
66
  }
67
+ return null;
118
68
  }
119
- stringifyValue(value, maxDepth = 3, currentDepth = 0) {
120
- if (value === null)
121
- return 'null';
122
- if (value === undefined)
123
- return 'undefined';
124
- const type = typeof value;
125
- switch (type) {
126
- case 'string':
127
- return `"${value}"`;
128
- case 'number':
129
- case 'boolean':
130
- return String(value);
131
- case 'function':
132
- return `[Function: ${this.getFunctionName(value)}]`;
133
- case 'object':
134
- if (currentDepth >= maxDepth) {
135
- return '[Max Depth Reached]';
136
- }
137
- return this.stringifyObject(value, maxDepth, currentDepth);
138
- default:
139
- return `[${type}]`;
69
+ getPath(info, propertyName) {
70
+ let parent = info.parent;
71
+ const path = [info.propertyName, propertyName];
72
+ while (parent != null) {
73
+ path.unshift(parent.propertyName);
74
+ parent = parent.parent;
140
75
  }
76
+ return path.join(".");
141
77
  }
142
- getFunctionName(fn) {
143
- const name = fn.name;
144
- return name || 'anonymous';
145
- }
146
- stringifyObject(obj, maxDepth, currentDepth) {
147
- if (obj === null)
148
- return 'null';
149
- // Handle special object types
150
- if (obj instanceof Date) {
151
- return `Date(${obj.toISOString()})`;
152
- }
153
- if (obj instanceof Error) {
154
- return `Error(${obj.message})`;
155
- }
156
- if (obj instanceof RegExp) {
157
- return obj.toString();
158
- }
159
- if (Array.isArray(obj)) {
160
- return this.stringifyArray(obj, maxDepth, currentDepth);
161
- }
162
- if (obj.constructor && obj.constructor.name !== 'Object') {
163
- return this.stringifyClassInstance(obj, maxDepth, currentDepth);
164
- }
165
- return this.stringifyPlainObject(obj, maxDepth, currentDepth);
166
- }
167
- stringifyArray(arr, maxDepth, currentDepth) {
168
- if (arr.length === 0)
169
- return '[]';
170
- const items = arr.slice(0, 5).map(item => this.stringifyValue(item, maxDepth, currentDepth + 1));
171
- const suffix = arr.length > 5 ? `... (+${arr.length - 5} more)` : '';
172
- return `[${items.join(', ')}${suffix}]`;
173
- }
174
- stringifyClassInstance(obj, maxDepth, currentDepth) {
175
- const className = obj.constructor.name;
176
- const properties = this.getObjectProperties(obj);
177
- if (Object.keys(properties).length === 0) {
178
- return `${className} {}`;
179
- }
180
- const props = Object.entries(properties)
181
- .slice(0, 5)
182
- .map(([key, value]) => {
183
- // For primitive values, don't increase depth
184
- const isPrimitive = value === null || value === undefined ||
185
- (typeof value !== 'object' && typeof value !== 'function');
186
- const depth = isPrimitive ? currentDepth : currentDepth + 1;
187
- return `${key}: ${this.stringifyValue(value, maxDepth, depth)}`;
188
- });
189
- const suffix = Object.keys(properties).length > 5 ?
190
- `... (+${Object.keys(properties).length - 5} more)` : '';
191
- return `${className} { ${props.join(', ')}${suffix} }`;
192
- }
193
- stringifyPlainObject(obj, maxDepth, currentDepth) {
194
- const properties = this.getObjectProperties(obj);
195
- if (Object.keys(properties).length === 0) {
196
- return '{}';
78
+ explore(instance, onDiscover) {
79
+ if (!this.isValidObject(instance)) {
80
+ return;
197
81
  }
198
- const props = Object.entries(properties)
199
- .slice(0, 5)
200
- .map(([key, value]) => {
201
- // For primitive values, don't increase depth
202
- const isPrimitive = value === null || value === undefined ||
203
- (typeof value !== 'object' && typeof value !== 'function');
204
- const depth = isPrimitive ? currentDepth : currentDepth + 1;
205
- return `${key}: ${this.stringifyValue(value, maxDepth, depth)}`;
206
- });
207
- const suffix = Object.keys(properties).length > 5 ?
208
- `... (+${Object.keys(properties).length - 5} more)` : '';
209
- return `{ ${props.join(', ')}${suffix} }`;
210
- }
211
- getObjectProperties(obj) {
212
- const properties = {};
213
- // Get enumerable properties
214
- for (const key in obj) {
215
- if (obj.hasOwnProperty(key)) {
216
- properties[key] = obj[key];
82
+ const explore = [{ instance, propertyName: this.getName(instance) }];
83
+ const visited = new Set();
84
+ for (let i = 0; i < explore.length; i++) {
85
+ const info = explore[i];
86
+ const item = info.instance;
87
+ if (visited.has(item)) {
88
+ continue;
217
89
  }
90
+ const allKeys = [
91
+ ...Object.getOwnPropertyNames(item),
92
+ ...Object.getOwnPropertySymbols(item),
93
+ ];
94
+ for (const key of allKeys) {
95
+ const descriptor = Object.getOwnPropertyDescriptor(item, key);
96
+ const isCallable = this.isCallableMethod(descriptor, key);
97
+ onDiscover(info, {
98
+ name: key,
99
+ isCallable
100
+ });
101
+ if (this.canExplore(descriptor) === false) {
102
+ continue;
103
+ }
104
+ const path = this.getPath(info, key);
105
+ explore.push({ instance: descriptor.value, parent: info, propertyName: key, path });
106
+ }
107
+ visited.add(item);
218
108
  }
219
- return properties;
220
109
  }
221
110
  }
222
111
 
@@ -231,99 +120,134 @@ __webpack_require__.r(__webpack_exports__);
231
120
  __webpack_require__.d(__webpack_exports__, {
232
121
  PerformanceCapability: () => (PerformanceCapability)
233
122
  });
123
+ /* ESM import */var _utilities__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../utilities */ "./src/utilities/strings.ts");
234
124
  /* ESM import */var _Capability__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Capability */ "./src/capabilities/Capability.ts");
235
125
  /* ESM import */var _performance_PerformanceTracker__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./performance/PerformanceTracker */ "./src/capabilities/performance/PerformanceTracker.ts");
236
126
  /* ESM import */var _tracing_CallTraceManager__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./tracing/CallTraceManager */ "./src/capabilities/tracing/CallTraceManager.ts");
237
127
 
238
128
 
239
129
 
130
+
240
131
  class PerformanceCapability extends _Capability__WEBPACK_IMPORTED_MODULE_0__.Capability {
241
132
  callTraceManager;
242
133
  performanceTracker;
243
- log;
244
- shouldLog;
134
+ filter;
135
+ childDurations = new Map();
245
136
  constructor(options) {
246
137
  super();
247
- this.log = options?.log ?? ((type, operationId, methodName, methodPath, performanceMetrics, isCompleted) => {
248
- if (isCompleted) {
249
- if (!performanceMetrics.duration)
250
- return;
251
- const deltaFromStart = this.performanceTracker.getDeltaFromOperationStart(operationId, performanceMetrics.endTime || performanceMetrics.startTime);
252
- console.log(`[${type} ${operationId}] ${methodName} COMPLETED`, {
253
- methodPath,
254
- performance: {
255
- deltaFromStart: this.performanceTracker.formatDuration(deltaFromStart),
256
- executionTime: this.performanceTracker.formatDuration(performanceMetrics.duration),
257
- timeToNextCall: performanceMetrics.timeToNextCall ?
258
- this.performanceTracker.formatDuration(performanceMetrics.timeToNextCall) : 'N/A'
259
- }
260
- });
261
- return;
262
- }
263
- const deltaFromStart = this.performanceTracker.getDeltaFromOperationStart(operationId, performanceMetrics.startTime);
264
- console.log(`[${type} ${operationId}] ${methodName}`, {
265
- methodPath,
266
- performance: {
267
- deltaFromStart: this.performanceTracker.formatDuration(deltaFromStart)
268
- }
269
- });
270
- });
271
- this.shouldLog = options?.shouldLog ?? (() => true);
138
+ this.filter = options?.filter ?? (() => true);
272
139
  this.callTraceManager = new _tracing_CallTraceManager__WEBPACK_IMPORTED_MODULE_1__.CallTraceManager();
273
140
  this.performanceTracker = new _performance_PerformanceTracker__WEBPACK_IMPORTED_MODULE_2__.PerformanceTracker();
274
141
  }
275
- createPerformanceInterceptor(originalMethod, methodName, methodPath, instance) {
276
- return (...args) => {
277
- const isNewOperation = this.callTraceManager.isNewOperation();
278
- let operationId;
279
- if (isNewOperation) {
280
- operationId = this.callTraceManager.startNewOperation();
281
- const startTime = this.performanceTracker.startMethodTiming(operationId, methodPath);
282
- this.log('ORIGIN', operationId, methodName, methodPath, { startTime }, false);
283
- }
284
- else {
285
- operationId = this.callTraceManager.getActiveOperationId();
286
- // Record that the previous method is about to call this one
287
- const callTrace = this.callTraceManager.getCurrentTrace();
288
- const previousMethodPath = callTrace[callTrace.length - 2];
289
- if (previousMethodPath) {
290
- this.performanceTracker.recordNextMethodStart(operationId, previousMethodPath);
291
- }
292
- const startTime = this.performanceTracker.startMethodTiming(operationId, methodPath);
293
- this.log('CHILD', operationId, methodName, methodPath, { startTime }, false);
294
- }
295
- try {
296
- const result = originalMethod.apply(instance, args);
297
- return result;
298
- }
299
- finally {
300
- // End performance tracking
301
- const performanceMetrics = this.performanceTracker.endMethodTiming(operationId, methodPath);
302
- // Log completion with performance metrics
303
- this.log(isNewOperation ? 'ORIGIN' : 'CHILD', operationId, methodName, methodPath, performanceMetrics, true);
304
- if (isNewOperation) {
305
- this.callTraceManager.endOperation();
306
- this.performanceTracker.cleanupOperation(operationId);
307
- }
308
- }
309
- };
310
- }
311
142
  apply(instance) {
312
- if (!this.isValidObject(instance)) {
313
- return;
314
- }
315
- // Create a performance wrapper
316
- const wrapper = {
317
- wrapMethod: (originalMethod, methodInfo) => {
318
- return this.createPerformanceInterceptor(originalMethod, String(methodInfo.methodName), methodInfo.methodPath.join(' → '), methodInfo.instance);
143
+ this.explore(instance, (meta, info) => {
144
+ if (info.isCallable) {
145
+ const originalMethod = meta.instance[info.name].bind(meta.instance);
146
+ meta.instance[info.name] = (...args) => {
147
+ const path = `${meta.path}.${String(info.name)}()`;
148
+ if (this.filter(path, info, meta) === false) {
149
+ return originalMethod(...args);
150
+ }
151
+ const isNewOperation = this.callTraceManager.isNewOperation();
152
+ let operationId;
153
+ let callTrace;
154
+ let depth;
155
+ if (isNewOperation) {
156
+ operationId = this.callTraceManager.startNewOperation();
157
+ this.childDurations.set(operationId, []);
158
+ callTrace = this.callTraceManager.addMethodToTrace(path);
159
+ depth = callTrace.length - 1;
160
+ const formattedCallTrace = this.callTraceManager.formatMethodPaths(callTrace);
161
+ this.performanceTracker.startMethodTiming(operationId, path);
162
+ console.log(`\n${'═'.repeat(60)}`);
163
+ console.log(`▶ ORIGIN [${operationId}] ${path}`);
164
+ if (args.length > 0) {
165
+ console.log(` Args:`, (0,_utilities__WEBPACK_IMPORTED_MODULE_3__.stringifyObject)(args, 4, 0));
166
+ }
167
+ console.log(` Call Stack: ${formattedCallTrace.join(' → ')}`);
168
+ }
169
+ else {
170
+ operationId = this.callTraceManager.getActiveOperationId();
171
+ callTrace = this.callTraceManager.addMethodToTrace(path);
172
+ depth = callTrace.length - 1;
173
+ const indent = ' '.repeat(Math.min(depth, 4));
174
+ // Track children for this child method too
175
+ const childMethodKey = `${operationId}:${path}`;
176
+ this.childDurations.set(childMethodKey, []);
177
+ this.performanceTracker.startMethodTiming(operationId, path);
178
+ console.log(`${indent}└─ CHILD [${operationId}] ${path}`);
179
+ if (args.length > 0) {
180
+ console.log(`${indent} Args:`, (0,_utilities__WEBPACK_IMPORTED_MODULE_3__.stringifyObject)(args, 4, 0));
181
+ }
182
+ }
183
+ try {
184
+ return originalMethod(...args);
185
+ }
186
+ finally {
187
+ const metrics = this.performanceTracker.endMethodTiming(operationId, path);
188
+ const duration = metrics.duration ?? 0;
189
+ const formattedDuration = this.performanceTracker.formatDuration(duration);
190
+ if (isNewOperation) {
191
+ const childDurations = this.childDurations.get(operationId) ?? [];
192
+ const totalChildTime = childDurations.reduce((sum, d) => sum + d, 0);
193
+ const formattedTotalChildTime = this.performanceTracker.formatDuration(totalChildTime);
194
+ const overhead = duration - totalChildTime;
195
+ const formattedOverhead = this.performanceTracker.formatDuration(Math.max(0, overhead));
196
+ console.log(`\n${'═'.repeat(60)}`);
197
+ console.log(`◀ COMPLETE [${operationId}] ${path}`);
198
+ console.log(` Total Duration: ${formattedDuration}`);
199
+ if (childDurations.length > 0) {
200
+ console.log(` Children Duration: ${formattedTotalChildTime} (${childDurations.length} calls)`);
201
+ console.log(` Overhead: ${formattedOverhead}`);
202
+ }
203
+ console.log(`${'═'.repeat(60)}\n`);
204
+ this.childDurations.delete(operationId);
205
+ this.performanceTracker.cleanupOperation(operationId);
206
+ this.callTraceManager.endOperation();
207
+ }
208
+ else {
209
+ const indent = ' '.repeat(Math.min(depth, 4));
210
+ const childMethodKey = `${operationId}:${path}`;
211
+ const childDurations = this.childDurations.get(childMethodKey) ?? [];
212
+ const totalChildTime = childDurations.reduce((sum, d) => sum + d, 0);
213
+ const formattedTotalChildTime = this.performanceTracker.formatDuration(totalChildTime);
214
+ const overhead = duration - totalChildTime;
215
+ const formattedOverhead = this.performanceTracker.formatDuration(Math.max(0, overhead));
216
+ console.log(`${indent} ✓ ${formattedDuration}`);
217
+ if (childDurations.length > 0) {
218
+ console.log(`${indent} Children: ${formattedTotalChildTime} (${childDurations.length} calls), Overhead: ${formattedOverhead}`);
219
+ }
220
+ // Clean up child method tracking
221
+ this.childDurations.delete(childMethodKey);
222
+ // Find the parent method and add this duration to its children list
223
+ // The parent is the method one level up in the call trace
224
+ const currentTrace = this.callTraceManager.getCurrentTrace();
225
+ if (currentTrace.length > 1) {
226
+ // Parent is the second-to-last item in the trace (before we remove current)
227
+ const parentPath = currentTrace[currentTrace.length - 2];
228
+ // Check if parent is the root operation (trace length 2 means root + this child)
229
+ if (currentTrace.length === 2) {
230
+ // Direct child of root - add to root's children list
231
+ const rootChildDurations = this.childDurations.get(operationId);
232
+ if (rootChildDurations) {
233
+ rootChildDurations.push(duration);
234
+ }
235
+ }
236
+ else {
237
+ // Nested child - add to parent method's children list
238
+ const parentMethodKey = `${operationId}:${parentPath}`;
239
+ const parentChildDurations = this.childDurations.get(parentMethodKey);
240
+ if (parentChildDurations) {
241
+ parentChildDurations.push(duration);
242
+ }
243
+ }
244
+ }
245
+ }
246
+ this.callTraceManager.removeMethodFromTrace();
247
+ }
248
+ };
319
249
  }
320
- };
321
- // Use the generic interception utility
322
- this.exploreObjectMethods(instance, (methodInfo) => {
323
- const originalMethod = methodInfo.instance[methodInfo.methodName].bind(methodInfo.instance);
324
- const wrappedMethod = wrapper.wrapMethod(originalMethod, methodInfo);
325
- methodInfo.instance[methodInfo.methodName] = wrappedMethod;
326
- }, {});
250
+ });
327
251
  }
328
252
  }
329
253
 
@@ -340,95 +264,61 @@ __webpack_require__.d(__webpack_exports__, {
340
264
  });
341
265
  /* ESM import */var _Capability__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Capability */ "./src/capabilities/Capability.ts");
342
266
  /* ESM import */var _tracing_CallTraceManager__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./tracing/CallTraceManager */ "./src/capabilities/tracing/CallTraceManager.ts");
267
+ /* ESM import */var _utilities_strings__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../utilities/strings */ "./src/utilities/strings.ts");
268
+
343
269
 
344
270
 
345
271
  class TracingCapability extends _Capability__WEBPACK_IMPORTED_MODULE_0__.Capability {
346
272
  callTraceManager;
347
- log;
348
- shouldLogMethod;
273
+ filter;
349
274
  constructor(options) {
350
275
  super();
351
- this.log = options?.log ?? ((type, operationId, methodName, methodPath, _callTrace, formattedCallTrace, args) => {
352
- const stringifiedArgs = args.map(arg => this.stringifyValue(arg, 4));
353
- const logData = {
354
- methodPath,
355
- callStack: formattedCallTrace,
356
- args: stringifiedArgs
357
- };
358
- console.log(`[${type} ${operationId}] ${methodName}`, logData);
359
- });
360
- this.shouldLogMethod = options?.shouldLog ?? (() => true);
276
+ this.filter = options?.filter ?? (() => true);
361
277
  this.callTraceManager = new _tracing_CallTraceManager__WEBPACK_IMPORTED_MODULE_1__.CallTraceManager();
362
278
  }
363
- createTracingInterceptor(originalMethod, methodName, methodPath, instance) {
364
- return (...args) => {
365
- const isNewOperation = this.callTraceManager.isNewOperation();
366
- let operationId;
367
- let callTrace;
368
- if (isNewOperation) {
369
- operationId = this.callTraceManager.startNewOperation();
370
- callTrace = this.callTraceManager.addMethodToTrace(methodPath);
371
- const formattedCallTrace = this.callTraceManager.formatMethodPaths(callTrace);
372
- this.log('ORIGIN', operationId, methodName, methodPath, callTrace, formattedCallTrace, args);
373
- }
374
- else {
375
- operationId = this.callTraceManager.getActiveOperationId();
376
- callTrace = this.callTraceManager.addMethodToTrace(methodPath);
377
- const formattedCallTrace = this.callTraceManager.formatMethodPaths(callTrace);
378
- this.log('CHILD', operationId, methodName, methodPath, callTrace, formattedCallTrace, args);
379
- }
380
- try {
381
- const result = originalMethod.apply(instance, args);
382
- return result;
383
- }
384
- finally {
385
- if (isNewOperation) {
386
- this.callTraceManager.endOperation();
387
- }
388
- else {
389
- this.callTraceManager.removeMethodFromTrace();
390
- }
391
- }
392
- };
393
- }
394
279
  apply(instance) {
395
- if (!this.isValidObject(instance)) {
396
- return;
397
- }
398
- const logMethodCall = (type, operationId, methodName, methodPath, callTrace, formattedCallTrace, args) => {
399
- if (this.log) {
400
- this.log(type, operationId, methodName, methodPath, callTrace, formattedCallTrace, args);
401
- return;
402
- }
403
- const stringifiedArgs = args.map(arg => this.stringifyValue(arg, 4));
404
- const logData = {
405
- methodPath,
406
- callStack: formattedCallTrace,
407
- args: stringifiedArgs
408
- };
409
- console.log(`[${type} ${operationId}] ${methodName}`, logData);
410
- };
411
- // Create a tracing wrapper
412
- const wrapper = {
413
- wrapMethod: (originalMethod, methodInfo) => {
414
- const metadata = {
415
- parent: methodInfo.parent,
416
- instance: methodInfo.instance,
417
- methodPath: methodInfo.methodPath
280
+ this.explore(instance, (meta, info) => {
281
+ if (info.isCallable) {
282
+ const originalMethod = meta.instance[info.name].bind(meta.instance);
283
+ meta.instance[info.name] = (...args) => {
284
+ const path = `${meta.path}.${String(info.name)}()`;
285
+ if (this.filter(path, info, meta) === false) {
286
+ return originalMethod(...args);
287
+ }
288
+ const isNewOperation = this.callTraceManager.isNewOperation();
289
+ let operationId;
290
+ if (isNewOperation) {
291
+ operationId = this.callTraceManager.startNewOperation();
292
+ const callTrace = this.callTraceManager.addMethodToTrace(path);
293
+ const formattedCallTrace = this.callTraceManager.formatMethodPaths(callTrace);
294
+ console.log(`\n${'═'.repeat(60)}`);
295
+ console.log(`▶ ORIGIN [${operationId}] ${path}`);
296
+ if (args.length > 0) {
297
+ console.log(` Args:`, (0,_utilities_strings__WEBPACK_IMPORTED_MODULE_2__.stringifyObject)(args, 4, 0));
298
+ }
299
+ console.log(` Call Stack: ${formattedCallTrace.join(' → ')}`);
300
+ }
301
+ else {
302
+ operationId = this.callTraceManager.getActiveOperationId();
303
+ const callTrace = this.callTraceManager.addMethodToTrace(path);
304
+ const indent = ' '.repeat(Math.min(callTrace.length - 1, 4));
305
+ console.log(`${indent}└─ CHILD [${operationId}] ${path}`);
306
+ if (args.length > 0) {
307
+ console.log(`${indent} Args:`, (0,_utilities_strings__WEBPACK_IMPORTED_MODULE_2__.stringifyObject)(args, 4, 0));
308
+ }
309
+ }
310
+ try {
311
+ return originalMethod(...args);
312
+ }
313
+ finally {
314
+ this.callTraceManager.removeMethodFromTrace();
315
+ if (isNewOperation) {
316
+ this.callTraceManager.endOperation();
317
+ }
318
+ }
418
319
  };
419
- const shouldLog = this.shouldLogMethod(methodInfo.methodName, metadata);
420
- if (!shouldLog) {
421
- return originalMethod; // Return unwrapped method if not logging
422
- }
423
- return this.createTracingInterceptor(originalMethod, String(methodInfo.methodName), methodInfo.methodPath.join(' → '), methodInfo.instance);
424
320
  }
425
- };
426
- // Use the generic interception utility
427
- this.exploreObjectMethods(instance, (methodInfo) => {
428
- const originalMethod = methodInfo.instance[methodInfo.methodName].bind(methodInfo.instance);
429
- const wrappedMethod = wrapper.wrapMethod(originalMethod, methodInfo);
430
- methodInfo.instance[methodInfo.methodName] = wrappedMethod;
431
- }, {});
321
+ });
432
322
  }
433
323
  }
434
324
 
@@ -520,7 +410,7 @@ class CallTraceManager {
520
410
  activeOperationId = null;
521
411
  activeCallStack = [];
522
412
  startNewOperation() {
523
- const operationId = (0,_utilities__WEBPACK_IMPORTED_MODULE_0__.uuid)();
413
+ const operationId = (0,_utilities__WEBPACK_IMPORTED_MODULE_0__.uuid)(8);
524
414
  this.activeOperationId = operationId;
525
415
  this.activeCallStack = [];
526
416
  return operationId;
@@ -561,6 +451,172 @@ class CallTraceManager {
561
451
  }
562
452
 
563
453
 
454
+ }),
455
+ "./src/utilities/strings.ts":
456
+ /*!**********************************!*\
457
+ !*** ./src/utilities/strings.ts ***!
458
+ \**********************************/
459
+ (function (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
460
+ __webpack_require__.r(__webpack_exports__);
461
+ __webpack_require__.d(__webpack_exports__, {
462
+ fastHash: () => (fastHash),
463
+ hash: () => (hash),
464
+ stringifyObject: () => (stringifyObject)
465
+ });
466
+ const hash = (value, seed = 0) => {
467
+ // From Stack Overflow
468
+ // https://stackoverflow.com/a/52171480/3329760
469
+ let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
470
+ for (let i = 0, ch; i < value.length; i++) {
471
+ ch = value.charCodeAt(i);
472
+ h1 = Math.imul(h1 ^ ch, 2654435761);
473
+ h2 = Math.imul(h2 ^ ch, 1597334677);
474
+ }
475
+ h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
476
+ h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
477
+ h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
478
+ h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
479
+ return 4294967296 * (2097151 & h2) + (h1 >>> 0);
480
+ };
481
+ /**
482
+ * Fast string hash optimized for comparisons.
483
+ * Uses djb2 algorithm - very fast and good distribution for short to medium strings.
484
+ * Same input always produces same output (deterministic).
485
+ *
486
+ * @param value - The string to hash
487
+ * @param seed - Optional seed value (default: 5381)
488
+ * @returns A positive 32-bit integer hash value
489
+ *
490
+ * @example
491
+ * ```ts
492
+ * fastHash("test") === fastHash("test") // true
493
+ * fastHash("test") !== fastHash("test2") // true
494
+ * ```
495
+ */
496
+ const fastHash = (value, seed = 5381) => {
497
+ let hash = seed;
498
+ for (let i = 0; i < value.length; i++) {
499
+ hash = ((hash << 5) + hash) + value.charCodeAt(i);
500
+ }
501
+ return hash >>> 0; // Convert to unsigned 32-bit integer
502
+ };
503
+ /**
504
+ * Converts any value to a readable string representation.
505
+ * Handles primitives, objects, arrays, classes, dates, errors, and functions.
506
+ * Supports depth limiting to prevent infinite recursion on circular references.
507
+ *
508
+ * @param obj - The value to stringify
509
+ * @param maxDepth - Maximum depth for nested objects (default: 3)
510
+ * @param currentDepth - Current recursion depth (default: 0)
511
+ * @returns String representation of the value
512
+ *
513
+ * @example
514
+ * ```ts
515
+ * stringifyObject({ name: "test", count: 5 }) // '{ name: "test", count: 5 }'
516
+ * stringifyObject([1, 2, 3]) // '[1, 2, 3]'
517
+ * stringifyObject(new Date()) // 'Date(2024-01-01T00:00:00.000Z)'
518
+ * ```
519
+ */
520
+ function stringifyObject(obj, maxDepth = 3, currentDepth = 0) {
521
+ if (obj === null)
522
+ return 'null';
523
+ if (obj === undefined)
524
+ return 'undefined';
525
+ const type = typeof obj;
526
+ switch (type) {
527
+ case 'string':
528
+ return `"${obj}"`;
529
+ case 'number':
530
+ case 'boolean':
531
+ return String(obj);
532
+ case 'function':
533
+ return `[Function: ${getFunctionName(obj)}]`;
534
+ case 'object':
535
+ if (currentDepth >= maxDepth) {
536
+ return '[Max Depth Reached]';
537
+ }
538
+ return stringifyObjectValue(obj, maxDepth, currentDepth);
539
+ default:
540
+ return `[${type}]`;
541
+ }
542
+ }
543
+ function getFunctionName(fn) {
544
+ const name = fn.name;
545
+ return name || 'anonymous';
546
+ }
547
+ function getObjectProperties(obj) {
548
+ const properties = {};
549
+ for (const key in obj) {
550
+ if (obj.hasOwnProperty(key)) {
551
+ properties[key] = obj[key];
552
+ }
553
+ }
554
+ return properties;
555
+ }
556
+ function stringifyObjectValue(obj, maxDepth, currentDepth) {
557
+ if (obj === null)
558
+ return 'null';
559
+ if (obj instanceof Date) {
560
+ return `Date(${obj.toISOString()})`;
561
+ }
562
+ if (obj instanceof Error) {
563
+ return `Error(${obj.message})`;
564
+ }
565
+ if (obj instanceof RegExp) {
566
+ return obj.toString();
567
+ }
568
+ if (Array.isArray(obj)) {
569
+ return stringifyArray(obj, maxDepth, currentDepth);
570
+ }
571
+ if (obj.constructor && obj.constructor.name !== 'Object') {
572
+ return stringifyClassInstance(obj, maxDepth, currentDepth);
573
+ }
574
+ return stringifyPlainObject(obj, maxDepth, currentDepth);
575
+ }
576
+ function stringifyArray(arr, maxDepth, currentDepth) {
577
+ if (arr.length === 0)
578
+ return '[]';
579
+ const items = arr.slice(0, 5).map(item => stringifyObject(item, maxDepth, currentDepth + 1));
580
+ const suffix = arr.length > 5 ? `... (+${arr.length - 5} more)` : '';
581
+ return `[${items.join(', ')}${suffix}]`;
582
+ }
583
+ function stringifyClassInstance(obj, maxDepth, currentDepth) {
584
+ const className = obj.constructor.name;
585
+ const properties = getObjectProperties(obj);
586
+ if (Object.keys(properties).length === 0) {
587
+ return `${className} {}`;
588
+ }
589
+ const props = Object.entries(properties)
590
+ .slice(0, 5)
591
+ .map(([key, value]) => {
592
+ const isPrimitive = value === null || value === undefined ||
593
+ (typeof value !== 'object' && typeof value !== 'function');
594
+ const depth = isPrimitive ? currentDepth : currentDepth + 1;
595
+ return `${key}: ${stringifyObject(value, maxDepth, depth)}`;
596
+ });
597
+ const suffix = Object.keys(properties).length > 5 ?
598
+ `... (+${Object.keys(properties).length - 5} more)` : '';
599
+ return `${className} { ${props.join(', ')}${suffix} }`;
600
+ }
601
+ function stringifyPlainObject(obj, maxDepth, currentDepth) {
602
+ const properties = getObjectProperties(obj);
603
+ if (Object.keys(properties).length === 0) {
604
+ return '{}';
605
+ }
606
+ const props = Object.entries(properties)
607
+ .slice(0, 5)
608
+ .map(([key, value]) => {
609
+ const isPrimitive = value === null || value === undefined ||
610
+ (typeof value !== 'object' && typeof value !== 'function');
611
+ const depth = isPrimitive ? currentDepth : currentDepth + 1;
612
+ return `${key}: ${stringifyObject(value, maxDepth, depth)}`;
613
+ });
614
+ const suffix = Object.keys(properties).length > 5 ?
615
+ `... (+${Object.keys(properties).length - 5} more)` : '';
616
+ return `{ ${props.join(', ')}${suffix} }`;
617
+ }
618
+
619
+
564
620
  }),
565
621
  "./src/utilities/uuid.ts":
566
622
  /*!*******************************!*\