@zintrust/trace 0.5.4 → 0.5.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.
@@ -22,58 +22,6 @@ const describeValueType = (value) => {
22
22
  return 'null';
23
23
  return typeof value;
24
24
  };
25
- const chooseLargerCandidate = (left, right) => {
26
- if (left === null)
27
- return right;
28
- if (right === null)
29
- return left;
30
- return right.size > left.size ? right : left;
31
- };
32
- const fallbackCandidate = (value, path) => {
33
- return path.length === 0 ? null : { path, size: serializedSize(value) };
34
- };
35
- const findLargestDroppablePathInArray = (value, path) => {
36
- let best = null;
37
- for (const [index, item] of value.entries()) {
38
- best = chooseLargerCandidate(best, findLargestDroppablePath(item, [...path, index]));
39
- }
40
- return best ?? fallbackCandidate(value, path);
41
- };
42
- const findLargestDroppablePathInObject = (value, path) => {
43
- let best = null;
44
- for (const [key, entryValue] of Object.entries(value)) {
45
- if (key === '__traceNotice')
46
- continue;
47
- best = chooseLargerCandidate(best, findLargestDroppablePath(entryValue, [...path, key]));
48
- }
49
- return best ?? fallbackCandidate(value, path);
50
- };
51
- const findLargestDroppablePath = (value, path = []) => {
52
- if (Array.isArray(value))
53
- return findLargestDroppablePathInArray(value, path);
54
- if (typeof value === 'object' && value !== null) {
55
- return findLargestDroppablePathInObject(value, path);
56
- }
57
- return fallbackCandidate(value, path);
58
- };
59
- const replaceAtPath = (value, path, replacement) => {
60
- if (path.length === 0)
61
- return replacement;
62
- const [segment, ...rest] = path;
63
- if (Array.isArray(value) && typeof segment === 'number') {
64
- const next = value.slice();
65
- next[segment] = replaceAtPath(next[segment], rest, replacement);
66
- return next;
67
- }
68
- if (typeof value === 'object' && value !== null && typeof segment === 'string') {
69
- const current = value;
70
- return {
71
- ...current,
72
- [segment]: replaceAtPath(current[segment], rest, replacement),
73
- };
74
- }
75
- return value;
76
- };
77
25
  const compactValue = (value, depth) => {
78
26
  if (depth >= DEFAULT_MAX_DEPTH) {
79
27
  return DROPPED_FIELD_MESSAGE;
@@ -106,17 +54,28 @@ const compactValue = (value, depth) => {
106
54
  return Object.fromEntries(compactedEntries);
107
55
  };
108
56
  const compactStructuredValueToBudget = (value) => {
109
- let compacted = typeof value === 'object' && value !== null && !Array.isArray(value)
110
- ? {
111
- ...value,
112
- __traceNotice: COMPACTED_CONTENT_MESSAGE,
113
- }
114
- : value;
115
- while (serializedSize(compacted) > DEFAULT_MAX_ENTRY_BYTES) {
116
- const candidate = findLargestDroppablePath(compacted);
117
- if (candidate === null)
57
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
58
+ return value;
59
+ }
60
+ const compacted = {
61
+ ...value,
62
+ __traceNotice: COMPACTED_CONTENT_MESSAGE,
63
+ };
64
+ const topLevelCandidates = Object.entries(compacted)
65
+ .filter(([key]) => key !== '__traceNotice')
66
+ .map(([key, entryValue]) => ({ key, size: serializedSize(entryValue) }))
67
+ .sort((left, right) => right.size - left.size);
68
+ let droppedCount = 0;
69
+ for (const candidate of topLevelCandidates) {
70
+ if (serializedSize(compacted) <= DEFAULT_MAX_ENTRY_BYTES) {
118
71
  break;
119
- compacted = replaceAtPath(compacted, candidate.path, DROPPED_FIELD_MESSAGE);
72
+ }
73
+ compacted[candidate.key] = DROPPED_FIELD_MESSAGE;
74
+ droppedCount += 1;
75
+ }
76
+ if (droppedCount > 0) {
77
+ compacted['__traceNotice'] =
78
+ `${COMPACTED_CONTENT_MESSAGE} ${String(droppedCount)} top-level field(s) were dropped.`;
120
79
  }
121
80
  return compacted;
122
81
  };
@@ -158,23 +117,24 @@ const closePort = (port) => {
158
117
  port.close();
159
118
  }
160
119
  };
161
- const scheduleTask = (task) => {
162
- if (typeof MessageChannel === 'function') {
163
- const channel = new MessageChannel();
164
- channel.port1.onmessage = () => {
165
- channel.port1.onmessage = null;
166
- closePort(channel.port1);
167
- closePort(channel.port2);
168
- void task().catch(() => undefined);
120
+ const scheduleTask = async (task) => {
121
+ return await new Promise((resolve, reject) => {
122
+ const runTask = () => {
123
+ void task().then(resolve).catch(reject);
169
124
  };
170
- channel.port2.postMessage(undefined);
171
- return;
172
- }
173
- Promise.resolve()
174
- .then(() => {
175
- void task().catch(() => undefined);
176
- })
177
- .catch(() => undefined);
125
+ if (typeof MessageChannel === 'function') {
126
+ const channel = new MessageChannel();
127
+ channel.port1.onmessage = () => {
128
+ channel.port1.onmessage = null;
129
+ closePort(channel.port1);
130
+ closePort(channel.port2);
131
+ runTask();
132
+ };
133
+ channel.port2.postMessage(undefined);
134
+ return;
135
+ }
136
+ Promise.resolve().then(runTask).catch(reject);
137
+ });
178
138
  };
179
139
  const getReplacementContent = (content) => {
180
140
  return {
@@ -283,7 +243,7 @@ const startInternalDispatchWorker = (storage, config, runtime) => {
283
243
  if (startedWorkerKeys.has(key))
284
244
  return;
285
245
  startedWorkerKeys.add(key);
286
- scheduleTask(async () => {
246
+ void scheduleTask(async () => {
287
247
  const workersApi = runtime?.queueWorkerApi ?? (await getQueueWorkerApi());
288
248
  if (workersApi === null) {
289
249
  startedWorkerKeys.delete(key);
@@ -323,10 +283,12 @@ const startInternalDispatchWorker = (storage, config, runtime) => {
323
283
  ensureWorkerTimer(key, setInterval(() => {
324
284
  void runWorker();
325
285
  }, intervalMs));
286
+ }).catch(() => {
287
+ startedWorkerKeys.delete(key);
326
288
  });
327
289
  };
328
- const dispatchWrite = (storage, config, entry, runtime) => {
329
- scheduleTask(async () => {
290
+ const dispatchWrite = async (storage, config, entry, runtime) => {
291
+ await scheduleTask(async () => {
330
292
  if (hasQueueDispatch(config)) {
331
293
  const enqueued = await enqueueTraceDispatch(config, { operation: 'write', entry }, runtime);
332
294
  if (enqueued)
@@ -335,8 +297,8 @@ const dispatchWrite = (storage, config, entry, runtime) => {
335
297
  await persistWriteFallback(storage, entry);
336
298
  });
337
299
  };
338
- const dispatchUpdate = (storage, config, uuid, patch, runtime) => {
339
- scheduleTask(async () => {
300
+ const dispatchUpdate = async (storage, config, uuid, patch, runtime) => {
301
+ await scheduleTask(async () => {
340
302
  if (hasQueueDispatch(config)) {
341
303
  const enqueued = await enqueueTraceDispatch(config, { operation: 'update', uuid, patch }, runtime);
342
304
  if (enqueued)
@@ -351,10 +313,10 @@ export const TraceContentBudget = Object.freeze({
351
313
  return Object.freeze({
352
314
  ...storage,
353
315
  writeEntry: async (entry) => {
354
- dispatchWrite(storage, config, entry, runtime);
316
+ await dispatchWrite(storage, config, entry, runtime);
355
317
  },
356
318
  updateEntry: async (uuid, patch) => {
357
- dispatchUpdate(storage, config, uuid, patch, runtime);
319
+ await dispatchUpdate(storage, config, uuid, patch, runtime);
358
320
  },
359
321
  });
360
322
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zintrust/trace",
3
- "version": "0.5.4",
3
+ "version": "0.5.5",
4
4
  "description": "Trace assistant for ZinTrust: logs requests, queries, exceptions, jobs, and more.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -28,87 +28,6 @@ const describeValueType = (value: unknown): string => {
28
28
  return typeof value;
29
29
  };
30
30
 
31
- type TracePathSegment = string | number;
32
-
33
- type TracePathCandidate = {
34
- path: TracePathSegment[];
35
- size: number;
36
- };
37
-
38
- const chooseLargerCandidate = (
39
- left: TracePathCandidate | null,
40
- right: TracePathCandidate | null
41
- ): TracePathCandidate | null => {
42
- if (left === null) return right;
43
- if (right === null) return left;
44
- return right.size > left.size ? right : left;
45
- };
46
-
47
- const fallbackCandidate = (value: unknown, path: TracePathSegment[]): TracePathCandidate | null => {
48
- return path.length === 0 ? null : { path, size: serializedSize(value) };
49
- };
50
-
51
- const findLargestDroppablePathInArray = (
52
- value: unknown[],
53
- path: TracePathSegment[]
54
- ): TracePathCandidate | null => {
55
- let best: TracePathCandidate | null = null;
56
-
57
- for (const [index, item] of value.entries()) {
58
- best = chooseLargerCandidate(best, findLargestDroppablePath(item, [...path, index]));
59
- }
60
-
61
- return best ?? fallbackCandidate(value, path);
62
- };
63
-
64
- const findLargestDroppablePathInObject = (
65
- value: Record<string, unknown>,
66
- path: TracePathSegment[]
67
- ): TracePathCandidate | null => {
68
- let best: TracePathCandidate | null = null;
69
-
70
- for (const [key, entryValue] of Object.entries(value)) {
71
- if (key === '__traceNotice') continue;
72
- best = chooseLargerCandidate(best, findLargestDroppablePath(entryValue, [...path, key]));
73
- }
74
-
75
- return best ?? fallbackCandidate(value, path);
76
- };
77
-
78
- const findLargestDroppablePath = (
79
- value: unknown,
80
- path: TracePathSegment[] = []
81
- ): TracePathCandidate | null => {
82
- if (Array.isArray(value)) return findLargestDroppablePathInArray(value, path);
83
- if (typeof value === 'object' && value !== null) {
84
- return findLargestDroppablePathInObject(value as Record<string, unknown>, path);
85
- }
86
-
87
- return fallbackCandidate(value, path);
88
- };
89
-
90
- const replaceAtPath = (value: unknown, path: TracePathSegment[], replacement: unknown): unknown => {
91
- if (path.length === 0) return replacement;
92
-
93
- const [segment, ...rest] = path;
94
-
95
- if (Array.isArray(value) && typeof segment === 'number') {
96
- const next = value.slice();
97
- next[segment] = replaceAtPath(next[segment], rest, replacement);
98
- return next;
99
- }
100
-
101
- if (typeof value === 'object' && value !== null && typeof segment === 'string') {
102
- const current = value as Record<string, unknown>;
103
- return {
104
- ...current,
105
- [segment]: replaceAtPath(current[segment], rest, replacement),
106
- };
107
- }
108
-
109
- return value;
110
- };
111
-
112
31
  const compactValue = (value: unknown, depth: number): unknown => {
113
32
  if (depth >= DEFAULT_MAX_DEPTH) {
114
33
  return DROPPED_FIELD_MESSAGE;
@@ -152,18 +71,34 @@ const compactValue = (value: unknown, depth: number): unknown => {
152
71
  };
153
72
 
154
73
  const compactStructuredValueToBudget = (value: unknown): unknown => {
155
- let compacted: unknown =
156
- typeof value === 'object' && value !== null && !Array.isArray(value)
157
- ? {
158
- ...(value as Record<string, unknown>),
159
- __traceNotice: COMPACTED_CONTENT_MESSAGE,
160
- }
161
- : value;
74
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
75
+ return value;
76
+ }
77
+
78
+ const compacted: Record<string, unknown> = {
79
+ ...(value as Record<string, unknown>),
80
+ __traceNotice: COMPACTED_CONTENT_MESSAGE,
81
+ };
82
+
83
+ const topLevelCandidates = Object.entries(compacted)
84
+ .filter(([key]) => key !== '__traceNotice')
85
+ .map(([key, entryValue]) => ({ key, size: serializedSize(entryValue) }))
86
+ .sort((left, right) => right.size - left.size);
87
+
88
+ let droppedCount = 0;
89
+
90
+ for (const candidate of topLevelCandidates) {
91
+ if (serializedSize(compacted) <= DEFAULT_MAX_ENTRY_BYTES) {
92
+ break;
93
+ }
162
94
 
163
- while (serializedSize(compacted) > DEFAULT_MAX_ENTRY_BYTES) {
164
- const candidate = findLargestDroppablePath(compacted);
165
- if (candidate === null) break;
166
- compacted = replaceAtPath(compacted, candidate.path, DROPPED_FIELD_MESSAGE);
95
+ compacted[candidate.key] = DROPPED_FIELD_MESSAGE;
96
+ droppedCount += 1;
97
+ }
98
+
99
+ if (droppedCount > 0) {
100
+ compacted['__traceNotice'] =
101
+ `${COMPACTED_CONTENT_MESSAGE} ${String(droppedCount)} top-level field(s) were dropped.`;
167
102
  }
168
103
 
169
104
  return compacted;
@@ -270,26 +205,28 @@ const closePort = (port: MessagePort): void => {
270
205
  }
271
206
  };
272
207
 
273
- const scheduleTask = (task: () => Promise<void>): void => {
274
- if (typeof MessageChannel === 'function') {
275
- const channel = new MessageChannel();
276
-
277
- channel.port1.onmessage = (): void => {
278
- channel.port1.onmessage = null;
279
- closePort(channel.port1);
280
- closePort(channel.port2);
281
- void task().catch(() => undefined);
208
+ const scheduleTask = async (task: () => Promise<void>): Promise<void> => {
209
+ return await new Promise<void>((resolve, reject) => {
210
+ const runTask = (): void => {
211
+ void task().then(resolve).catch(reject);
282
212
  };
283
213
 
284
- channel.port2.postMessage(undefined);
285
- return;
286
- }
214
+ if (typeof MessageChannel === 'function') {
215
+ const channel = new MessageChannel();
216
+
217
+ channel.port1.onmessage = (): void => {
218
+ channel.port1.onmessage = null;
219
+ closePort(channel.port1);
220
+ closePort(channel.port2);
221
+ runTask();
222
+ };
223
+
224
+ channel.port2.postMessage(undefined);
225
+ return;
226
+ }
287
227
 
288
- Promise.resolve()
289
- .then(() => {
290
- void task().catch(() => undefined);
291
- })
292
- .catch(() => undefined);
228
+ Promise.resolve().then(runTask).catch(reject);
229
+ });
293
230
  };
294
231
 
295
232
  const getReplacementContent = (content: unknown): Record<string, unknown> => {
@@ -442,7 +379,7 @@ const startInternalDispatchWorker = (
442
379
  if (startedWorkerKeys.has(key)) return;
443
380
  startedWorkerKeys.add(key);
444
381
 
445
- scheduleTask(async () => {
382
+ void scheduleTask(async () => {
446
383
  const workersApi = runtime?.queueWorkerApi ?? (await getQueueWorkerApi());
447
384
  if (workersApi === null) {
448
385
  startedWorkerKeys.delete(key);
@@ -487,16 +424,18 @@ const startInternalDispatchWorker = (
487
424
  void runWorker();
488
425
  }, intervalMs)
489
426
  );
427
+ }).catch(() => {
428
+ startedWorkerKeys.delete(key);
490
429
  });
491
430
  };
492
431
 
493
- const dispatchWrite = (
432
+ const dispatchWrite = async (
494
433
  storage: ITraceStorage,
495
434
  config: ITraceConfig,
496
435
  entry: ITraceEntry,
497
436
  runtime?: TraceContentBudgetRuntime
498
- ): void => {
499
- scheduleTask(async () => {
437
+ ): Promise<void> => {
438
+ await scheduleTask(async () => {
500
439
  if (hasQueueDispatch(config)) {
501
440
  const enqueued = await enqueueTraceDispatch(config, { operation: 'write', entry }, runtime);
502
441
  if (enqueued) return;
@@ -506,14 +445,14 @@ const dispatchWrite = (
506
445
  });
507
446
  };
508
447
 
509
- const dispatchUpdate = (
448
+ const dispatchUpdate = async (
510
449
  storage: ITraceStorage,
511
450
  config: ITraceConfig,
512
451
  uuid: string,
513
452
  patch: Partial<Pick<ITraceEntry, 'content' | 'isLatest'>>,
514
453
  runtime?: TraceContentBudgetRuntime
515
- ): void => {
516
- scheduleTask(async () => {
454
+ ): Promise<void> => {
455
+ await scheduleTask(async () => {
517
456
  if (hasQueueDispatch(config)) {
518
457
  const enqueued = await enqueueTraceDispatch(
519
458
  config,
@@ -538,13 +477,13 @@ export const TraceContentBudget = Object.freeze({
538
477
  return Object.freeze({
539
478
  ...storage,
540
479
  writeEntry: async (entry: ITraceEntry): Promise<void> => {
541
- dispatchWrite(storage, config, entry, runtime);
480
+ await dispatchWrite(storage, config, entry, runtime);
542
481
  },
543
482
  updateEntry: async (
544
483
  uuid: string,
545
484
  patch: Partial<Pick<ITraceEntry, 'content' | 'isLatest'>>
546
485
  ): Promise<void> => {
547
- dispatchUpdate(storage, config, uuid, patch, runtime);
486
+ await dispatchUpdate(storage, config, uuid, patch, runtime);
548
487
  },
549
488
  });
550
489
  },