@beignet/devtools 0.0.3 → 0.0.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 (64) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/README.md +116 -71
  3. package/dist/events.d.ts +15 -3
  4. package/dist/events.d.ts.map +1 -1
  5. package/dist/events.js +1 -0
  6. package/dist/events.js.map +1 -1
  7. package/dist/index.d.ts +13 -15
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +11 -13
  10. package/dist/index.js.map +1 -1
  11. package/dist/persistence.d.ts +1 -1
  12. package/dist/persistence.d.ts.map +1 -1
  13. package/dist/provider-instrumentation.d.ts +1 -1
  14. package/dist/provider-instrumentation.d.ts.map +1 -1
  15. package/dist/provider.d.ts +5 -5
  16. package/dist/provider.d.ts.map +1 -1
  17. package/dist/provider.js +10 -5
  18. package/dist/provider.js.map +1 -1
  19. package/dist/redaction.d.ts +1 -1
  20. package/dist/redaction.d.ts.map +1 -1
  21. package/dist/redaction.js +1 -0
  22. package/dist/redaction.js.map +1 -1
  23. package/dist/routes.d.ts +2 -2
  24. package/dist/routes.d.ts.map +1 -1
  25. package/dist/routes.js +3 -3
  26. package/dist/routes.js.map +1 -1
  27. package/dist/ui-model.d.ts +38 -0
  28. package/dist/ui-model.d.ts.map +1 -0
  29. package/dist/ui-model.js +1039 -0
  30. package/dist/ui-model.js.map +1 -0
  31. package/dist/ui.d.ts +1 -1
  32. package/dist/ui.d.ts.map +1 -1
  33. package/dist/ui.js +310 -164
  34. package/dist/ui.js.map +1 -1
  35. package/dist/watchers.d.ts +2 -2
  36. package/dist/watchers.d.ts.map +1 -1
  37. package/dist/watchers.js +10 -2
  38. package/dist/watchers.js.map +1 -1
  39. package/package.json +22 -2
  40. package/src/events.ts +25 -1
  41. package/src/index.ts +13 -15
  42. package/src/persistence.ts +1 -1
  43. package/src/provider-instrumentation.ts +1 -1
  44. package/src/provider.ts +12 -7
  45. package/src/redaction.ts +2 -1
  46. package/src/routes.ts +4 -4
  47. package/src/ui-model.ts +1233 -0
  48. package/src/ui.ts +310 -164
  49. package/src/watchers.ts +12 -3
  50. package/dist/audit.d.ts +0 -31
  51. package/dist/audit.d.ts.map +0 -1
  52. package/dist/audit.js +0 -55
  53. package/dist/audit.js.map +0 -1
  54. package/dist/instrumentation.d.ts +0 -114
  55. package/dist/instrumentation.d.ts.map +0 -1
  56. package/dist/instrumentation.js +0 -303
  57. package/dist/instrumentation.js.map +0 -1
  58. package/dist/trace-context.d.ts +0 -80
  59. package/dist/trace-context.d.ts.map +0 -1
  60. package/dist/trace-context.js +0 -92
  61. package/dist/trace-context.js.map +0 -1
  62. package/src/audit.ts +0 -92
  63. package/src/instrumentation.ts +0 -491
  64. package/src/trace-context.ts +0 -166
@@ -0,0 +1,1039 @@
1
+ function isRecord(value) {
2
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3
+ }
4
+ function detailsObject(event) {
5
+ return isRecord(event.details) ? event.details : {};
6
+ }
7
+ function detailValue(event, key) {
8
+ return detailsObject(event)[key];
9
+ }
10
+ function detailString(event, key) {
11
+ const value = detailValue(event, key);
12
+ return typeof value === "string" ? value : "";
13
+ }
14
+ function detailNumber(event, key) {
15
+ const value = detailValue(event, key);
16
+ return typeof value === "number" && Number.isFinite(value)
17
+ ? value
18
+ : undefined;
19
+ }
20
+ function detailBoolean(event, key) {
21
+ const value = detailValue(event, key);
22
+ return typeof value === "boolean" ? value : undefined;
23
+ }
24
+ function valueText(value) {
25
+ if (value === undefined || value === null || value === "")
26
+ return "";
27
+ if (typeof value === "string")
28
+ return value;
29
+ if (typeof value === "number" || typeof value === "boolean") {
30
+ return String(value);
31
+ }
32
+ try {
33
+ return JSON.stringify(value);
34
+ }
35
+ catch {
36
+ return String(value);
37
+ }
38
+ }
39
+ function jsonSearch(value) {
40
+ return valueText(value);
41
+ }
42
+ function containsText(event, text) {
43
+ return jsonSearch(event).toLowerCase().includes(text.toLowerCase());
44
+ }
45
+ export function inferDevtoolsWatcher(event) {
46
+ if (event.watcher)
47
+ return event.watcher;
48
+ switch (event.type) {
49
+ case "request":
50
+ return "requests";
51
+ case "error":
52
+ return "errors";
53
+ case "usecase":
54
+ return "useCases";
55
+ case "eventBus":
56
+ return "eventBus";
57
+ case "job":
58
+ return "jobs";
59
+ case "outbox":
60
+ return "outbox";
61
+ case "schedule":
62
+ return "schedules";
63
+ case "provider":
64
+ return "providers";
65
+ case "custom":
66
+ return "custom";
67
+ default:
68
+ return "";
69
+ }
70
+ }
71
+ export function devtoolsEventBadgeLabel(event) {
72
+ if (event.type === "custom") {
73
+ switch (inferDevtoolsWatcher(event)) {
74
+ case "auth":
75
+ return "auth";
76
+ case "audit":
77
+ return "audit";
78
+ case "cache":
79
+ return "cache";
80
+ case "db":
81
+ return "database";
82
+ case "mail":
83
+ return "mail";
84
+ case "notifications":
85
+ return "notifications";
86
+ case "rateLimit":
87
+ return "rate limit";
88
+ case "storage":
89
+ return "storage";
90
+ case "uploads":
91
+ return "uploads";
92
+ default:
93
+ return "custom";
94
+ }
95
+ }
96
+ return event.type;
97
+ }
98
+ export function devtoolsEventBadgeClass(event) {
99
+ if (event.type === "custom") {
100
+ switch (inferDevtoolsWatcher(event)) {
101
+ case "auth":
102
+ return "auth";
103
+ case "audit":
104
+ return "audit";
105
+ case "cache":
106
+ return "cache";
107
+ case "db":
108
+ return "db";
109
+ case "mail":
110
+ return "mail";
111
+ case "notifications":
112
+ return "notifications";
113
+ case "rateLimit":
114
+ return "rateLimit";
115
+ case "storage":
116
+ return "storage";
117
+ case "uploads":
118
+ return "uploads";
119
+ default:
120
+ return "custom";
121
+ }
122
+ }
123
+ return event.type;
124
+ }
125
+ export function summarizeDevtoolsEvent(event) {
126
+ switch (event.type) {
127
+ case "request":
128
+ return `${event.method || ""} ${event.path || ""}${event.status ? ` -> ${event.status}` : ""}${event.durationMs !== undefined ? ` (${event.durationMs}ms)` : ""}`;
129
+ case "error":
130
+ return event.message || "Unknown error";
131
+ case "usecase":
132
+ return `${event.name} (${event.phase})${event.durationMs !== undefined ? ` ${event.durationMs}ms` : ""}`;
133
+ case "eventBus":
134
+ return event.eventName || "Domain event";
135
+ case "job":
136
+ return `${event.jobName} -> ${event.status}`;
137
+ case "outbox":
138
+ return `${event.messageName} (${event.messageKind}) -> ${event.status}`;
139
+ case "schedule":
140
+ return `${event.scheduleName} -> ${event.status}`;
141
+ case "provider":
142
+ return `${event.providerName} -> ${event.action}`;
143
+ case "custom":
144
+ return `${event.label || event.name || "Custom event"}${event.summary ? ` - ${event.summary}` : ""}`;
145
+ default:
146
+ return "";
147
+ }
148
+ }
149
+ export function devtoolsEventDuration(event) {
150
+ const detailDuration = detailNumber(event, "durationMs");
151
+ const eventDuration = "durationMs" in event && typeof event.durationMs === "number"
152
+ ? event.durationMs
153
+ : undefined;
154
+ const duration = eventDuration !== undefined ? eventDuration : detailDuration;
155
+ return typeof duration === "number" && Number.isFinite(duration)
156
+ ? duration
157
+ : undefined;
158
+ }
159
+ export function devtoolsEventProvider(event) {
160
+ if (event.type === "provider")
161
+ return event.providerName;
162
+ const details = detailsObject(event);
163
+ return valueText(details.providerName ?? details.provider ?? details.portName ?? "");
164
+ }
165
+ export function devtoolsEventFailure(event) {
166
+ const details = detailsObject(event);
167
+ if (event.type === "error")
168
+ return true;
169
+ if ("status" in event && typeof event.status === "string") {
170
+ return (event.status === "failed" ||
171
+ event.status === "deadLettered" ||
172
+ event.status === "retryScheduled");
173
+ }
174
+ if (event.type === "custom" && event.name.includes("failed"))
175
+ return true;
176
+ return Boolean(details.error) || details.allowed === false;
177
+ }
178
+ export function hasDevtoolsRedactedValue(value) {
179
+ if (value === "[redacted]")
180
+ return true;
181
+ if (!isRecord(value) && !Array.isArray(value))
182
+ return false;
183
+ return Object.values(value).some(hasDevtoolsRedactedValue);
184
+ }
185
+ function averageDuration(events) {
186
+ const durations = events
187
+ .map(devtoolsEventDuration)
188
+ .filter((value) => value !== undefined);
189
+ if (!durations.length)
190
+ return "n/a";
191
+ const total = durations.reduce((sum, value) => sum + value, 0);
192
+ return `${Math.round(total / durations.length)}ms`;
193
+ }
194
+ function averageNumber(events, selector) {
195
+ const values = events
196
+ .map(selector)
197
+ .filter((value) => value !== undefined);
198
+ if (!values.length)
199
+ return "n/a";
200
+ const total = values.reduce((sum, value) => sum + value, 0);
201
+ return Math.round((total / values.length) * 10) / 10;
202
+ }
203
+ function sumNumber(events, selector) {
204
+ return events.reduce((sum, event) => sum + (selector(event) ?? 0), 0);
205
+ }
206
+ function distinctCount(events, selector) {
207
+ const values = new Set();
208
+ for (const event of events) {
209
+ const value = selector(event);
210
+ if (value !== undefined && value !== null && String(value) !== "") {
211
+ values.add(String(value));
212
+ }
213
+ }
214
+ return values.size;
215
+ }
216
+ function operationFor(event) {
217
+ const operation = detailString(event, "operation");
218
+ if (operation)
219
+ return operation;
220
+ if (event.type === "custom") {
221
+ const parts = event.name.split(".");
222
+ return parts.length > 1 ? parts.slice(1).join(".") : event.name;
223
+ }
224
+ if (event.type === "provider")
225
+ return event.action;
226
+ if ("status" in event && typeof event.status === "string") {
227
+ return event.status;
228
+ }
229
+ return inferDevtoolsWatcher(event);
230
+ }
231
+ function operationBase(event) {
232
+ return operationFor(event).replace(/\.failed$/, "");
233
+ }
234
+ function errorTextFor(event) {
235
+ const detailError = detailValue(event, "error");
236
+ if (typeof detailError === "string" && detailError)
237
+ return detailError;
238
+ if (isRecord(detailError)) {
239
+ if (typeof detailError.message === "string")
240
+ return detailError.message;
241
+ return jsonSearch(detailError);
242
+ }
243
+ if ("error" in event && typeof event.error === "string")
244
+ return event.error;
245
+ return "";
246
+ }
247
+ function queryFor(event) {
248
+ const query = detailString(event, "query");
249
+ return query || ("summary" in event && event.summary ? event.summary : "");
250
+ }
251
+ function dbOperationFor(event) {
252
+ const query = queryFor(event).trim();
253
+ const match = query.match(/^([a-z]+)/i);
254
+ return match ? match[1].toLowerCase() : "query";
255
+ }
256
+ function dbTargetFor(event) {
257
+ const query = queryFor(event);
258
+ const patterns = [
259
+ /\bfrom\s+"?([a-zA-Z0-9_]+)"?/i,
260
+ /\binto\s+"?([a-zA-Z0-9_]+)"?/i,
261
+ /\bupdate\s+"?([a-zA-Z0-9_]+)"?/i,
262
+ /\bdelete\s+from\s+"?([a-zA-Z0-9_]+)"?/i,
263
+ ];
264
+ for (const pattern of patterns) {
265
+ const match = query.match(pattern);
266
+ if (match)
267
+ return match[1] ?? "";
268
+ }
269
+ return "";
270
+ }
271
+ function attemptLabel(event) {
272
+ const attempt = detailValue(event, "attempt");
273
+ const max = detailValue(event, "maxAttempts");
274
+ if (attempt !== undefined && max !== undefined)
275
+ return `${attempt}/${max}`;
276
+ if (attempt !== undefined)
277
+ return `attempt ${attempt}`;
278
+ return "";
279
+ }
280
+ function retryFor(event) {
281
+ const retryAt = detailValue(event, "retryAt") || detailValue(event, "availableAt");
282
+ const retryDelayMs = detailValue(event, "retryDelayMs");
283
+ if (retryAt && retryDelayMs !== undefined)
284
+ return `${retryAt} (${retryDelayMs}ms)`;
285
+ return valueText(retryAt);
286
+ }
287
+ function channelList(event) {
288
+ const channels = notificationChannels(event);
289
+ if (channels.length)
290
+ return channels.join(", ");
291
+ return "";
292
+ }
293
+ function notificationChannels(event) {
294
+ const channels = detailValue(event, "channels");
295
+ if (Array.isArray(channels))
296
+ return channels.map(String);
297
+ const channel = detailValue(event, "channel");
298
+ if (channel)
299
+ return [String(channel)];
300
+ const results = detailValue(event, "results");
301
+ if (Array.isArray(results)) {
302
+ return results
303
+ .map((result) => (isRecord(result) ? valueText(result.channel) : ""))
304
+ .filter(Boolean);
305
+ }
306
+ return [];
307
+ }
308
+ function distinctNotificationChannels(events) {
309
+ const values = new Set();
310
+ for (const event of events) {
311
+ for (const channel of notificationChannels(event)) {
312
+ values.add(channel);
313
+ }
314
+ }
315
+ return values.size;
316
+ }
317
+ function recipientSummary(event) {
318
+ const to = detailValue(event, "to");
319
+ if (Array.isArray(to))
320
+ return `${to.length} recipient${to.length === 1 ? "" : "s"}`;
321
+ return valueText(to);
322
+ }
323
+ function actorId(event) {
324
+ const actor = detailValue(event, "actor");
325
+ return isRecord(actor) ? valueText(actor.id) : "";
326
+ }
327
+ function resourceId(event) {
328
+ const resource = detailValue(event, "resource");
329
+ return isRecord(resource) ? valueText(resource.id ?? resource.type) : "";
330
+ }
331
+ function scheduleStatus(event, status) {
332
+ return event.type === "schedule" && event.status === status;
333
+ }
334
+ function notificationFailed(event) {
335
+ const results = detailValue(event, "results");
336
+ if (Array.isArray(results)) {
337
+ return results.some((result) => isRecord(result) && valueText(result.status) === "failed");
338
+ }
339
+ return devtoolsEventFailure(event);
340
+ }
341
+ function storageMissing(event) {
342
+ return (detailBoolean(event, "exists") === false ||
343
+ detailValue(event, "object") === null ||
344
+ containsText(event, "missing"));
345
+ }
346
+ export function devtoolsPanelMeta(tabId, label = "Subsystem") {
347
+ switch (tabId) {
348
+ case "events":
349
+ return {
350
+ title: "Domain events",
351
+ subtitle: "Published facts and event bus activity",
352
+ };
353
+ case "jobs":
354
+ return { title: "Jobs", subtitle: "Background job lifecycle" };
355
+ case "outbox":
356
+ return {
357
+ title: "Outbox",
358
+ subtitle: "Durable message delivery, retries, and dead letters",
359
+ };
360
+ case "schedules":
361
+ return { title: "Schedules", subtitle: "Cron and schedule runs" };
362
+ case "db":
363
+ return {
364
+ title: "Database",
365
+ subtitle: "Queries and database provider diagnostics",
366
+ };
367
+ case "cache":
368
+ return {
369
+ title: "Cache",
370
+ subtitle: "Reads, writes, deletes, and hit rates",
371
+ };
372
+ case "storage":
373
+ return {
374
+ title: "Storage",
375
+ subtitle: "Object reads, writes, deletes, and public URL resolution",
376
+ };
377
+ case "uploads":
378
+ return {
379
+ title: "Uploads",
380
+ subtitle: "Upload preparation, signing, server upload, and completion",
381
+ };
382
+ case "mail":
383
+ return {
384
+ title: "Mail",
385
+ subtitle: "Delivery attempts, recipients, and provider results",
386
+ };
387
+ case "notifications":
388
+ return {
389
+ title: "Notifications",
390
+ subtitle: "Notification intent and per-channel delivery",
391
+ };
392
+ case "auth":
393
+ return { title: "Auth", subtitle: "Session and authentication activity" };
394
+ case "audit":
395
+ return {
396
+ title: "Audit",
397
+ subtitle: "Durable activity records emitted by application code",
398
+ };
399
+ case "rateLimit":
400
+ return {
401
+ title: "Rate limits",
402
+ subtitle: "Allowed, blocked, and failed checks",
403
+ };
404
+ default:
405
+ return { title: label, subtitle: "Subsystem activity" };
406
+ }
407
+ }
408
+ export function devtoolsPanelMetrics(tabId, events) {
409
+ switch (tabId) {
410
+ case "events":
411
+ return [
412
+ { label: "Events", value: events.length },
413
+ {
414
+ label: "Names",
415
+ value: distinctCount(events, (event) => event.type === "eventBus" ? event.eventName : ""),
416
+ },
417
+ {
418
+ label: "Traces",
419
+ value: distinctCount(events, (event) => event.traceId),
420
+ },
421
+ {
422
+ label: "Requests",
423
+ value: distinctCount(events, (event) => event.requestId),
424
+ },
425
+ ];
426
+ case "jobs":
427
+ return [
428
+ { label: "Jobs", value: events.length },
429
+ {
430
+ label: "Scheduled",
431
+ value: events.filter((event) => event.type === "job" && event.status === "scheduled").length,
432
+ },
433
+ {
434
+ label: "Failed",
435
+ value: events.filter((event) => event.type === "job" && event.status === "failed").length,
436
+ },
437
+ {
438
+ label: "Retried",
439
+ value: events.filter((event) => event.type === "job" && event.status === "retryScheduled").length,
440
+ },
441
+ {
442
+ label: "Dead letters",
443
+ value: events.filter((event) => event.type === "job" && event.status === "deadLettered").length,
444
+ },
445
+ ];
446
+ case "outbox":
447
+ return [
448
+ {
449
+ label: "Delivered",
450
+ value: events.filter((event) => event.type === "outbox" && event.status === "delivered").length,
451
+ },
452
+ {
453
+ label: "Retried",
454
+ value: events.filter((event) => event.type === "outbox" && event.status === "retryScheduled").length,
455
+ },
456
+ {
457
+ label: "Dead letters",
458
+ value: events.filter((event) => event.type === "outbox" && event.status === "deadLettered").length,
459
+ },
460
+ {
461
+ label: "Avg attempt",
462
+ value: averageNumber(events, (event) => detailNumber(event, "attempt")),
463
+ },
464
+ ];
465
+ case "schedules":
466
+ return [
467
+ { label: "Runs", value: events.length },
468
+ {
469
+ label: "Completed",
470
+ value: events.filter((event) => scheduleStatus(event, "completed"))
471
+ .length,
472
+ },
473
+ {
474
+ label: "Failed",
475
+ value: events.filter((event) => scheduleStatus(event, "failed"))
476
+ .length,
477
+ },
478
+ {
479
+ label: "Names",
480
+ value: distinctCount(events, (event) => event.type === "schedule" ? event.scheduleName : ""),
481
+ },
482
+ ];
483
+ case "cache":
484
+ return [
485
+ { label: "Operations", value: events.length },
486
+ {
487
+ label: "Hits",
488
+ value: events.filter((event) => detailBoolean(event, "hit") === true ||
489
+ detailBoolean(event, "exists") === true).length,
490
+ },
491
+ {
492
+ label: "Misses",
493
+ value: events.filter((event) => detailBoolean(event, "hit") === false ||
494
+ detailBoolean(event, "exists") === false).length,
495
+ },
496
+ {
497
+ label: "Failures",
498
+ value: events.filter(devtoolsEventFailure).length,
499
+ },
500
+ ];
501
+ case "db":
502
+ return [
503
+ { label: "Queries", value: events.length },
504
+ {
505
+ label: "Failures",
506
+ value: events.filter(devtoolsEventFailure).length,
507
+ },
508
+ { label: "Tables", value: distinctCount(events, dbTargetFor) },
509
+ {
510
+ label: "Redacted",
511
+ value: events.filter(hasDevtoolsRedactedValue).length,
512
+ },
513
+ ];
514
+ case "storage":
515
+ return [
516
+ { label: "Operations", value: events.length },
517
+ {
518
+ label: "Writes",
519
+ value: events.filter((event) => operationBase(event) === "put")
520
+ .length,
521
+ },
522
+ { label: "Missing", value: events.filter(storageMissing).length },
523
+ {
524
+ label: "Failures",
525
+ value: events.filter(devtoolsEventFailure).length,
526
+ },
527
+ ];
528
+ case "uploads":
529
+ return [
530
+ { label: "Uploads", value: events.length },
531
+ {
532
+ label: "Completed",
533
+ value: events.filter((event) => event.type === "custom" && event.name.endsWith(".completed")).length,
534
+ },
535
+ { label: "Failed", value: events.filter(devtoolsEventFailure).length },
536
+ {
537
+ label: "Files",
538
+ value: sumNumber(events, (event) => detailNumber(event, "fileCount")),
539
+ },
540
+ ];
541
+ case "mail":
542
+ return [
543
+ {
544
+ label: "Attempts",
545
+ value: events.filter((event) => event.type === "custom" && event.name === "mail.send").length,
546
+ },
547
+ {
548
+ label: "Sent",
549
+ value: events.filter((event) => event.type === "custom" && event.name === "mail.sent").length,
550
+ },
551
+ { label: "Failed", value: events.filter(devtoolsEventFailure).length },
552
+ {
553
+ label: "Providers",
554
+ value: distinctCount(events, devtoolsEventProvider),
555
+ },
556
+ ];
557
+ case "notifications":
558
+ return [
559
+ { label: "Notifications", value: events.length },
560
+ {
561
+ label: "Completed",
562
+ value: events.filter((event) => event.type === "custom" && event.name.endsWith(".completed")).length,
563
+ },
564
+ { label: "Failed", value: events.filter(notificationFailed).length },
565
+ { label: "Channels", value: distinctNotificationChannels(events) },
566
+ ];
567
+ case "auth":
568
+ return [
569
+ { label: "Checks", value: events.length },
570
+ {
571
+ label: "Signed in",
572
+ value: events.filter((event) => detailBoolean(event, "authenticated") === true).length,
573
+ },
574
+ {
575
+ label: "Guest",
576
+ value: events.filter((event) => detailBoolean(event, "authenticated") === false &&
577
+ !devtoolsEventFailure(event)).length,
578
+ },
579
+ {
580
+ label: "Failures",
581
+ value: events.filter(devtoolsEventFailure).length,
582
+ },
583
+ ];
584
+ case "audit":
585
+ return [
586
+ { label: "Entries", value: events.length },
587
+ {
588
+ label: "Failures",
589
+ value: events.filter((event) => detailValue(event, "outcome") === "failure").length,
590
+ },
591
+ { label: "Actors", value: distinctCount(events, actorId) },
592
+ { label: "Resources", value: distinctCount(events, resourceId) },
593
+ ];
594
+ case "rateLimit":
595
+ return [
596
+ { label: "Checks", value: events.length },
597
+ {
598
+ label: "Allowed",
599
+ value: events.filter((event) => detailBoolean(event, "allowed") === true).length,
600
+ },
601
+ {
602
+ label: "Blocked",
603
+ value: events.filter((event) => detailBoolean(event, "allowed") === false).length,
604
+ },
605
+ { label: "Avg", value: averageDuration(events) },
606
+ ];
607
+ default:
608
+ return [
609
+ { label: "Events", value: events.length },
610
+ {
611
+ label: "Failures",
612
+ value: events.filter(devtoolsEventFailure).length,
613
+ },
614
+ {
615
+ label: "Requests",
616
+ value: distinctCount(events, (event) => event.requestId),
617
+ },
618
+ { label: "Avg", value: averageDuration(events) },
619
+ ];
620
+ }
621
+ }
622
+ function field(label, value) {
623
+ return { label, value: valueText(value) };
624
+ }
625
+ function genericDetail(event) {
626
+ return {
627
+ fields: [
628
+ field("Provider", devtoolsEventProvider(event)),
629
+ field("Operation", operationFor(event)),
630
+ field("Duration", devtoolsEventDuration(event) !== undefined
631
+ ? `${devtoolsEventDuration(event)}ms`
632
+ : ""),
633
+ field("Request", event.requestId),
634
+ field("Trace", event.traceId ? event.traceId.slice(0, 12) : ""),
635
+ field("Error", errorTextFor(event)),
636
+ ],
637
+ note: summarizeDevtoolsEvent(event),
638
+ };
639
+ }
640
+ export function devtoolsPanelDetail(tabId, event) {
641
+ const details = detailsObject(event);
642
+ switch (tabId) {
643
+ case "db":
644
+ return {
645
+ fields: [
646
+ field("Provider", devtoolsEventProvider(event)),
647
+ field("Operation", dbOperationFor(event)),
648
+ field("Table", dbTargetFor(event)),
649
+ field("Port", details.portName),
650
+ field("Params", details.paramsCount),
651
+ field("Duration", devtoolsEventDuration(event) !== undefined
652
+ ? `${devtoolsEventDuration(event)}ms`
653
+ : ""),
654
+ field("Request", event.requestId),
655
+ field("Trace", event.traceId ? event.traceId.slice(0, 12) : ""),
656
+ ],
657
+ note: "summary" in event && event.summary
658
+ ? event.summary
659
+ : "Database provider activity",
660
+ };
661
+ case "outbox":
662
+ return {
663
+ fields: [
664
+ field("Message", event.type === "outbox" ? event.messageName : ""),
665
+ field("Kind", event.type === "outbox" ? event.messageKind : ""),
666
+ field("Status", "status" in event ? event.status : ""),
667
+ field("Attempt", attemptLabel(event)),
668
+ field("Retry", retryFor(event)),
669
+ field("Error", errorTextFor(event)),
670
+ field("Request", event.requestId),
671
+ field("Trace", event.traceId ? event.traceId.slice(0, 12) : ""),
672
+ ],
673
+ note: summarizeDevtoolsEvent(event),
674
+ };
675
+ case "jobs":
676
+ return {
677
+ fields: [
678
+ field("Job", event.type === "job" ? event.jobName : ""),
679
+ field("Status", "status" in event ? event.status : ""),
680
+ field("Phase", details.phase),
681
+ field("Provider", devtoolsEventProvider(event)),
682
+ field("Attempt", attemptLabel(event)),
683
+ field("Retry", retryFor(event)),
684
+ field("Error", errorTextFor(event)),
685
+ field("Request", event.requestId),
686
+ ],
687
+ note: summarizeDevtoolsEvent(event),
688
+ };
689
+ case "schedules":
690
+ return {
691
+ fields: [
692
+ field("Schedule", event.type === "schedule" ? event.scheduleName : ""),
693
+ field("Status", "status" in event ? event.status : ""),
694
+ field("Cron", event.type === "schedule" ? event.cron : details.cron),
695
+ field("Timezone", event.type === "schedule" ? event.timezone : details.timezone),
696
+ field("Provider", devtoolsEventProvider(event)),
697
+ field("Duration", devtoolsEventDuration(event) !== undefined
698
+ ? `${devtoolsEventDuration(event)}ms`
699
+ : ""),
700
+ field("Error", errorTextFor(event)),
701
+ field("Request", event.requestId),
702
+ ],
703
+ note: summarizeDevtoolsEvent(event),
704
+ };
705
+ case "cache":
706
+ return {
707
+ fields: [
708
+ field("Operation", operationFor(event)),
709
+ field("Key", details.key),
710
+ field("Result", devtoolsPanelResult(event)),
711
+ field("TTL", details.ttlSeconds),
712
+ field("Provider", devtoolsEventProvider(event)),
713
+ field("Duration", devtoolsEventDuration(event) !== undefined
714
+ ? `${devtoolsEventDuration(event)}ms`
715
+ : ""),
716
+ field("Error", errorTextFor(event)),
717
+ field("Request", event.requestId),
718
+ ],
719
+ note: summarizeDevtoolsEvent(event),
720
+ };
721
+ case "storage":
722
+ return {
723
+ fields: [
724
+ field("Operation", operationFor(event)),
725
+ field("Key", details.key),
726
+ field("Bucket", details.bucket),
727
+ field("Visibility", details.visibility),
728
+ field("Provider", devtoolsEventProvider(event)),
729
+ field("Duration", devtoolsEventDuration(event) !== undefined
730
+ ? `${devtoolsEventDuration(event)}ms`
731
+ : ""),
732
+ field("Error", errorTextFor(event)),
733
+ field("Request", event.requestId),
734
+ ],
735
+ note: summarizeDevtoolsEvent(event),
736
+ };
737
+ case "uploads":
738
+ return {
739
+ fields: [
740
+ field("Upload", details.uploadName || ("summary" in event ? event.summary : "")),
741
+ field("Operation", operationFor(event)),
742
+ field("Mode", details.mode),
743
+ field("Files", details.fileCount),
744
+ field("Key", details.key),
745
+ field("Provider", devtoolsEventProvider(event)),
746
+ field("Duration", devtoolsEventDuration(event) !== undefined
747
+ ? `${devtoolsEventDuration(event)}ms`
748
+ : ""),
749
+ field("Error", errorTextFor(event)),
750
+ ],
751
+ note: summarizeDevtoolsEvent(event),
752
+ };
753
+ case "mail":
754
+ return {
755
+ fields: [
756
+ field("Subject", details.subject),
757
+ field("Provider", devtoolsEventProvider(event)),
758
+ field("Recipients", recipientSummary(event)),
759
+ field("Message ID", details.id),
760
+ field("Operation", operationFor(event)),
761
+ field("Error", errorTextFor(event)),
762
+ field("Request", event.requestId),
763
+ field("Trace", event.traceId ? event.traceId.slice(0, 12) : ""),
764
+ ],
765
+ note: summarizeDevtoolsEvent(event),
766
+ };
767
+ case "notifications":
768
+ return {
769
+ fields: [
770
+ field("Notification", details.notificationName ||
771
+ ("summary" in event ? event.summary : "")),
772
+ field("Channels", channelList(event)),
773
+ field("Status", devtoolsPanelResult(event)),
774
+ field("Provider", devtoolsEventProvider(event)),
775
+ field("Error", errorTextFor(event)),
776
+ field("Request", event.requestId),
777
+ field("Trace", event.traceId ? event.traceId.slice(0, 12) : ""),
778
+ field("Metadata", details.metadata ? "present" : ""),
779
+ ],
780
+ note: summarizeDevtoolsEvent(event),
781
+ };
782
+ case "auth":
783
+ return {
784
+ fields: [
785
+ field("Operation", operationFor(event)),
786
+ field("Authenticated", detailBoolean(event, "authenticated")),
787
+ field("Provider", devtoolsEventProvider(event)),
788
+ field("Duration", devtoolsEventDuration(event) !== undefined
789
+ ? `${devtoolsEventDuration(event)}ms`
790
+ : ""),
791
+ field("Error", errorTextFor(event)),
792
+ field("Request", event.requestId),
793
+ field("Trace", event.traceId ? event.traceId.slice(0, 12) : ""),
794
+ field("Result", devtoolsPanelResult(event)),
795
+ ],
796
+ note: summarizeDevtoolsEvent(event),
797
+ };
798
+ case "audit":
799
+ return {
800
+ fields: [
801
+ field("Action", details.action || ("name" in event ? event.name : "")),
802
+ field("Outcome", details.outcome),
803
+ field("Actor", actorId(event)),
804
+ field("Resource", resourceId(event)),
805
+ field("Tenant", isRecord(details.tenant) ? details.tenant.id : ""),
806
+ field("Occurred", details.occurredAt),
807
+ field("Request", event.requestId),
808
+ field("Trace", event.traceId ? event.traceId.slice(0, 12) : ""),
809
+ ],
810
+ note: valueText(details.message) || summarizeDevtoolsEvent(event),
811
+ };
812
+ case "rateLimit":
813
+ return {
814
+ fields: [
815
+ field("Key", details.key),
816
+ field("Allowed", detailBoolean(event, "allowed")),
817
+ field("Remaining", details.remaining),
818
+ field("Limit", details.limit),
819
+ field("Window", details.windowSec ? `${details.windowSec}s` : ""),
820
+ field("Retry after", details.retryAfterSeconds),
821
+ field("Provider", devtoolsEventProvider(event)),
822
+ field("Error", errorTextFor(event)),
823
+ ],
824
+ note: summarizeDevtoolsEvent(event),
825
+ };
826
+ default:
827
+ return genericDetail(event);
828
+ }
829
+ }
830
+ export function devtoolsPanelPrimary(tabId, event) {
831
+ const details = detailsObject(event);
832
+ switch (tabId) {
833
+ case "db":
834
+ return (dbTargetFor(event) ||
835
+ ("summary" in event ? event.summary : "") ||
836
+ "Database query");
837
+ case "cache":
838
+ return valueText(details.key) || "Cache operation";
839
+ case "storage":
840
+ return valueText(details.key) || "Storage operation";
841
+ case "uploads":
842
+ return (valueText(details.uploadName || ("summary" in event ? event.summary : "")) || "Upload");
843
+ case "mail":
844
+ return valueText(details.subject) || "Mail";
845
+ case "notifications":
846
+ return (valueText(details.notificationName || ("summary" in event ? event.summary : "")) || "Notification");
847
+ case "auth":
848
+ return operationFor(event) || "Auth";
849
+ case "audit":
850
+ return (valueText(details.action || ("name" in event ? event.name : "")) ||
851
+ "Audit entry");
852
+ case "rateLimit":
853
+ return valueText(details.key) || "Rate limit";
854
+ default:
855
+ switch (event.type) {
856
+ case "eventBus":
857
+ return event.eventName || "Domain event";
858
+ case "job":
859
+ return event.jobName || "Job";
860
+ case "outbox":
861
+ return event.messageName || "Outbox message";
862
+ case "schedule":
863
+ return event.scheduleName || "Schedule";
864
+ case "custom":
865
+ return event.label || event.name || "Custom event";
866
+ default:
867
+ return summarizeDevtoolsEvent(event);
868
+ }
869
+ }
870
+ }
871
+ export function devtoolsPanelSecondary(tabId, event) {
872
+ const details = detailsObject(event);
873
+ switch (tabId) {
874
+ case "db":
875
+ return ([dbOperationFor(event), details.portName].filter(Boolean).join(" / ") ||
876
+ "query");
877
+ case "cache":
878
+ case "storage":
879
+ return [operationFor(event), devtoolsEventProvider(event)]
880
+ .filter(Boolean)
881
+ .join(" / ");
882
+ case "uploads":
883
+ return [
884
+ operationFor(event),
885
+ details.mode,
886
+ details.fileCount ? `${details.fileCount} files` : "",
887
+ ]
888
+ .filter(Boolean)
889
+ .join(" / ");
890
+ case "mail":
891
+ return [devtoolsEventProvider(event), recipientSummary(event)]
892
+ .filter(Boolean)
893
+ .join(" / ");
894
+ case "notifications":
895
+ return [channelList(event), devtoolsPanelResult(event)]
896
+ .filter(Boolean)
897
+ .join(" / ");
898
+ case "auth":
899
+ return [
900
+ detailBoolean(event, "authenticated") === true
901
+ ? "authenticated"
902
+ : detailBoolean(event, "authenticated") === false
903
+ ? "guest"
904
+ : "",
905
+ devtoolsEventProvider(event),
906
+ ]
907
+ .filter(Boolean)
908
+ .join(" / ");
909
+ case "audit":
910
+ return [details.outcome, actorId(event), resourceId(event)]
911
+ .filter(Boolean)
912
+ .join(" / ");
913
+ case "rateLimit":
914
+ return [
915
+ detailBoolean(event, "allowed") === true
916
+ ? "allowed"
917
+ : detailBoolean(event, "allowed") === false
918
+ ? "blocked"
919
+ : "",
920
+ details.remaining !== undefined ? `${details.remaining} remaining` : "",
921
+ ]
922
+ .filter(Boolean)
923
+ .join(" / ");
924
+ default:
925
+ if (event.type === "provider")
926
+ return event.action;
927
+ if (event.type === "outbox")
928
+ return [event.messageKind, attemptLabel(event)]
929
+ .filter(Boolean)
930
+ .join(" / ");
931
+ if (event.type === "job")
932
+ return [event.status, details.phase, attemptLabel(event)]
933
+ .filter(Boolean)
934
+ .join(" / ");
935
+ if (event.type === "schedule")
936
+ return [event.status, event.cron].filter(Boolean).join(" / ");
937
+ if (event.type === "eventBus")
938
+ return event.requestId || event.traceId || "";
939
+ if (details.operation)
940
+ return valueText(details.operation);
941
+ if (event.type === "custom")
942
+ return event.name;
943
+ return inferDevtoolsWatcher(event);
944
+ }
945
+ }
946
+ export function devtoolsPanelResult(event) {
947
+ const details = detailsObject(event);
948
+ if (details.allowed === true)
949
+ return "allowed";
950
+ if (details.allowed === false)
951
+ return "blocked";
952
+ if (details.hit === true)
953
+ return "hit";
954
+ if (details.hit === false)
955
+ return "miss";
956
+ if (details.exists === true)
957
+ return "exists";
958
+ if (details.exists === false)
959
+ return "missing";
960
+ if (details.authenticated === true)
961
+ return "signed in";
962
+ if (details.authenticated === false)
963
+ return "guest";
964
+ if (event.type === "custom" && event.name.endsWith(".completed"))
965
+ return "completed";
966
+ if (event.type === "custom" && event.name.endsWith(".sent"))
967
+ return "sent";
968
+ if ("status" in event && typeof event.status === "string")
969
+ return event.status;
970
+ if (details.outcome)
971
+ return valueText(details.outcome);
972
+ if (details.error)
973
+ return "failed";
974
+ return devtoolsEventProvider(event) || inferDevtoolsWatcher(event);
975
+ }
976
+ export function createDevtoolsPanelRow(tabId, event) {
977
+ return {
978
+ primary: devtoolsPanelPrimary(tabId, event),
979
+ secondary: devtoolsPanelSecondary(tabId, event),
980
+ summary: summarizeDevtoolsEvent(event),
981
+ durationMs: devtoolsEventDuration(event),
982
+ result: devtoolsPanelResult(event),
983
+ detail: devtoolsPanelDetail(tabId, event),
984
+ };
985
+ }
986
+ export function devtoolsPanelModelBrowserSource() {
987
+ return [
988
+ isRecord,
989
+ detailsObject,
990
+ detailValue,
991
+ detailString,
992
+ detailNumber,
993
+ detailBoolean,
994
+ valueText,
995
+ jsonSearch,
996
+ containsText,
997
+ inferDevtoolsWatcher,
998
+ devtoolsEventBadgeLabel,
999
+ devtoolsEventBadgeClass,
1000
+ summarizeDevtoolsEvent,
1001
+ devtoolsEventDuration,
1002
+ devtoolsEventProvider,
1003
+ devtoolsEventFailure,
1004
+ hasDevtoolsRedactedValue,
1005
+ averageDuration,
1006
+ averageNumber,
1007
+ sumNumber,
1008
+ distinctCount,
1009
+ operationFor,
1010
+ operationBase,
1011
+ errorTextFor,
1012
+ queryFor,
1013
+ dbOperationFor,
1014
+ dbTargetFor,
1015
+ attemptLabel,
1016
+ retryFor,
1017
+ channelList,
1018
+ notificationChannels,
1019
+ distinctNotificationChannels,
1020
+ recipientSummary,
1021
+ actorId,
1022
+ resourceId,
1023
+ scheduleStatus,
1024
+ notificationFailed,
1025
+ storageMissing,
1026
+ devtoolsPanelMeta,
1027
+ devtoolsPanelMetrics,
1028
+ field,
1029
+ genericDetail,
1030
+ devtoolsPanelDetail,
1031
+ devtoolsPanelPrimary,
1032
+ devtoolsPanelSecondary,
1033
+ devtoolsPanelResult,
1034
+ createDevtoolsPanelRow,
1035
+ ]
1036
+ .map((fn) => fn.toString())
1037
+ .join("\n");
1038
+ }
1039
+ //# sourceMappingURL=ui-model.js.map