@dudousxd/nestjs-durable-telescope 0.3.0 → 0.5.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,763 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ DurableTelescopeWatcher: () => DurableTelescopeWatcher,
25
+ durableDashboard: () => durableDashboard,
26
+ durableDurationProvider: () => durableDurationProvider,
27
+ durableRecentFailuresProvider: () => durableRecentFailuresProvider,
28
+ durableRunsOverTimeProvider: () => durableRunsOverTimeProvider,
29
+ durableStateBreakdownProvider: () => durableStateBreakdownProvider,
30
+ durableStateProvider: () => durableStateProvider,
31
+ durableSuccessRateProvider: () => durableSuccessRateProvider,
32
+ durableTelescopeExtension: () => durableTelescopeExtension,
33
+ durableThroughputProvider: () => durableThroughputProvider,
34
+ durableTimeseriesProvider: () => durableTimeseriesProvider,
35
+ durableWorkerHealthProvider: () => durableWorkerHealthProvider
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+
39
+ // src/durable-telescope.watcher.ts
40
+ var import_nestjs_durable_core = require("@dudousxd/nestjs-durable-core");
41
+ var import_api = require("@opentelemetry/api");
42
+ var DurableTelescopeWatcher = class {
43
+ static {
44
+ __name(this, "DurableTelescopeWatcher");
45
+ }
46
+ type = "durable";
47
+ /** Open root span per run, so step entries nest under it and share its traceId. */
48
+ roots = /* @__PURE__ */ new Map();
49
+ register(ctx) {
50
+ const engine = ctx.moduleRef.get(import_nestjs_durable_core.WorkflowEngine, {
51
+ strict: false
52
+ });
53
+ const tracer = import_api.trace.getTracer("@dudousxd/nestjs-durable-telescope");
54
+ engine.subscribe((event) => {
55
+ const record = /* @__PURE__ */ __name(() => ctx.record({
56
+ type: this.type,
57
+ content: {
58
+ event: event.type,
59
+ workflow: event.workflow,
60
+ runId: event.runId,
61
+ seq: event.seq,
62
+ name: event.name,
63
+ kind: event.kind,
64
+ output: event.output,
65
+ error: event.error,
66
+ durationMs: event.durationMs
67
+ },
68
+ tags: this.tags(event)
69
+ }), "record");
70
+ if (event.type === "run.started") {
71
+ this.roots.set(event.runId, tracer.startSpan(`workflow ${event.workflow ?? "?"}`, {
72
+ attributes: {
73
+ "durable.run_id": event.runId,
74
+ "durable.workflow": event.workflow
75
+ }
76
+ }));
77
+ }
78
+ const root = this.roots.get(event.runId);
79
+ const base = root ? import_api.trace.setSpan(import_api.context.active(), root) : import_api.context.active();
80
+ if (event.name != null && event.seq != null) {
81
+ const step = tracer.startSpan(`step ${event.name}`, {
82
+ attributes: {
83
+ "durable.run_id": event.runId,
84
+ "durable.step.seq": event.seq,
85
+ "durable.step.kind": event.kind
86
+ }
87
+ }, base);
88
+ import_api.context.with(import_api.trace.setSpan(base, step), record);
89
+ step.end();
90
+ } else {
91
+ import_api.context.with(base, record);
92
+ }
93
+ if (event.type === "run.completed" || event.type === "run.suspended") {
94
+ root?.end();
95
+ this.roots.delete(event.runId);
96
+ } else if (event.type === "run.failed") {
97
+ const message = event.error?.message;
98
+ root?.setStatus(message !== void 0 ? {
99
+ code: import_api.SpanStatusCode.ERROR,
100
+ message
101
+ } : {
102
+ code: import_api.SpanStatusCode.ERROR
103
+ });
104
+ root?.end();
105
+ this.roots.delete(event.runId);
106
+ }
107
+ });
108
+ }
109
+ tags(event) {
110
+ const tags = [
111
+ "durable",
112
+ event.type,
113
+ `run:${event.runId}`
114
+ ];
115
+ if (event.workflow) tags.push(`workflow:${event.workflow}`);
116
+ if (event.kind) tags.push(`kind:${event.kind}`);
117
+ if (event.type === "run.failed") tags.push("failed");
118
+ return tags;
119
+ }
120
+ };
121
+
122
+ // src/durable-telescope.extension.ts
123
+ var import_nestjs_telescope2 = require("@dudousxd/nestjs-telescope");
124
+
125
+ // src/durable-dashboard.spec-data.ts
126
+ function durableDashboard(opts = {}) {
127
+ const runHref = opts.runHref ?? "/durable/runs/{runId}";
128
+ const windowMs = opts.recentFailuresWindowMs ?? 24 * 60 * 60 * 1e3;
129
+ return {
130
+ id: "durable.workflows",
131
+ label: "Workflows",
132
+ panels: [],
133
+ sections: [
134
+ {
135
+ title: "Health",
136
+ cols: 4,
137
+ panels: [
138
+ {
139
+ kind: "gauge",
140
+ title: "Success rate",
141
+ data: {
142
+ provider: "durable.successRate"
143
+ },
144
+ max: 1,
145
+ format: "percent",
146
+ thresholds: {
147
+ warn: 0.98,
148
+ bad: 0.95,
149
+ direction: "down-bad"
150
+ }
151
+ },
152
+ {
153
+ kind: "stat",
154
+ title: "Duration p95",
155
+ data: {
156
+ provider: "durable.duration",
157
+ query: {
158
+ metric: "p95"
159
+ }
160
+ },
161
+ format: "duration",
162
+ spark: false,
163
+ thresholds: {
164
+ warn: 2e3,
165
+ bad: 5e3,
166
+ direction: "up-bad"
167
+ }
168
+ },
169
+ {
170
+ kind: "stat",
171
+ title: "Backlog",
172
+ data: {
173
+ provider: "durable.state",
174
+ query: {
175
+ status: "pending"
176
+ }
177
+ },
178
+ spark: false,
179
+ thresholds: {
180
+ warn: 50,
181
+ bad: 200,
182
+ direction: "up-bad"
183
+ }
184
+ },
185
+ {
186
+ kind: "stat",
187
+ title: "Throughput",
188
+ data: {
189
+ provider: "durable.throughput"
190
+ },
191
+ format: "rate",
192
+ spark: true
193
+ }
194
+ ]
195
+ },
196
+ {
197
+ title: "Needs attention",
198
+ cols: 3,
199
+ panels: [
200
+ {
201
+ kind: "topN",
202
+ title: "Top failing workflows",
203
+ data: {
204
+ provider: "durable.timeseries",
205
+ query: {
206
+ metric: "topFailures"
207
+ }
208
+ },
209
+ limit: 8
210
+ },
211
+ {
212
+ kind: "table",
213
+ title: "Stuck runs",
214
+ data: {
215
+ provider: "durable.recentFailures",
216
+ query: {
217
+ windowMs
218
+ }
219
+ },
220
+ columns: [
221
+ {
222
+ key: "updatedAt",
223
+ label: "Updated"
224
+ },
225
+ {
226
+ key: "workflow",
227
+ label: "Workflow"
228
+ },
229
+ {
230
+ key: "runId",
231
+ label: "Run",
232
+ link: {
233
+ href: runHref
234
+ }
235
+ },
236
+ {
237
+ key: "error",
238
+ label: "Error"
239
+ }
240
+ ]
241
+ },
242
+ {
243
+ kind: "table",
244
+ title: "Starved worker groups",
245
+ data: {
246
+ provider: "durable.workerHealth"
247
+ },
248
+ columns: [
249
+ {
250
+ key: "group",
251
+ label: "Group"
252
+ },
253
+ {
254
+ key: "queued",
255
+ label: "Queued"
256
+ },
257
+ {
258
+ key: "liveWorkers",
259
+ label: "Workers"
260
+ },
261
+ {
262
+ key: "status",
263
+ label: "Status"
264
+ }
265
+ ]
266
+ }
267
+ ]
268
+ },
269
+ {
270
+ title: "Trends",
271
+ cols: 3,
272
+ panels: [
273
+ {
274
+ kind: "timeseries",
275
+ title: "Runs over time",
276
+ data: {
277
+ provider: "durable.runsOverTime"
278
+ },
279
+ series: [
280
+ "done",
281
+ "failed"
282
+ ],
283
+ style: "stacked"
284
+ },
285
+ {
286
+ kind: "distribution",
287
+ title: "Duration distribution",
288
+ data: {
289
+ provider: "durable.duration"
290
+ },
291
+ markers: [
292
+ "p50",
293
+ "p95",
294
+ "p99"
295
+ ],
296
+ format: "duration"
297
+ },
298
+ {
299
+ kind: "breakdown",
300
+ title: "Runs by state",
301
+ data: {
302
+ provider: "durable.stateBreakdown"
303
+ },
304
+ style: "donut"
305
+ }
306
+ ]
307
+ }
308
+ ]
309
+ };
310
+ }
311
+ __name(durableDashboard, "durableDashboard");
312
+
313
+ // src/durable-data-providers.ts
314
+ var import_nestjs_durable_core2 = require("@dudousxd/nestjs-durable-core");
315
+ var import_nestjs_telescope = require("@dudousxd/nestjs-telescope");
316
+ function percentile(sorted, p) {
317
+ if (sorted.length === 0) return 0;
318
+ const idx = Math.min(sorted.length - 1, Math.ceil(p / 100 * sorted.length) - 1);
319
+ return sorted[Math.max(0, idx)] ?? 0;
320
+ }
321
+ __name(percentile, "percentile");
322
+ async function fetchEntries(ctx) {
323
+ const storage = ctx.moduleRef.get(import_nestjs_telescope.TELESCOPE_STORAGE, {
324
+ strict: false
325
+ });
326
+ const page = await storage.get({
327
+ type: "durable",
328
+ limit: 5e3
329
+ });
330
+ return page.data;
331
+ }
332
+ __name(fetchEntries, "fetchEntries");
333
+ function splitWindows(entries, windowMs, now) {
334
+ const start = now - windowMs;
335
+ const prevStart = start - windowMs;
336
+ return {
337
+ current: entries.filter((e) => {
338
+ const t = e.createdAt ? +new Date(e.createdAt) : 0;
339
+ return t > start && t <= now;
340
+ }),
341
+ previous: entries.filter((e) => {
342
+ const t = e.createdAt ? +new Date(e.createdAt) : 0;
343
+ return t > prevStart && t <= start;
344
+ })
345
+ };
346
+ }
347
+ __name(splitWindows, "splitWindows");
348
+ function successRateOf(entries) {
349
+ let completed = 0;
350
+ let failed = 0;
351
+ for (const e of entries) {
352
+ const c = e.content ?? {};
353
+ if (c.event === "run.completed") completed += 1;
354
+ else if (c.event === "run.failed") failed += 1;
355
+ }
356
+ const total = completed + failed;
357
+ return total === 0 ? 1 : completed / total;
358
+ }
359
+ __name(successRateOf, "successRateOf");
360
+ var STATE_CAP = 1e4;
361
+ function durableStateProvider() {
362
+ return {
363
+ name: "durable.state",
364
+ async resolve(query, ctx) {
365
+ const store = ctx.moduleRef.get(import_nestjs_durable_core2.STATE_STORE, {
366
+ strict: false
367
+ });
368
+ const status = query?.status ?? "dead";
369
+ const runs = await store.listRuns({
370
+ status,
371
+ limit: STATE_CAP
372
+ });
373
+ return {
374
+ value: runs.length
375
+ };
376
+ }
377
+ };
378
+ }
379
+ __name(durableStateProvider, "durableStateProvider");
380
+ function durableTimeseriesProvider() {
381
+ return {
382
+ name: "durable.timeseries",
383
+ async resolve(query, ctx) {
384
+ const storage = ctx.moduleRef.get(import_nestjs_telescope.TELESCOPE_STORAGE, {
385
+ strict: false
386
+ });
387
+ const limit = Math.min(5e3, Math.max(100, Number(query?.limit ?? 2e3)));
388
+ const page = await storage.get({
389
+ type: "durable",
390
+ limit
391
+ });
392
+ let completed = 0;
393
+ let failed = 0;
394
+ const failByWorkflow = /* @__PURE__ */ new Map();
395
+ for (const e of page.data) {
396
+ const c = e.content ?? {};
397
+ if (c.event === "run.completed") completed += 1;
398
+ else if (c.event === "run.failed") {
399
+ failed += 1;
400
+ const wf = c.workflow ?? "unknown";
401
+ failByWorkflow.set(wf, (failByWorkflow.get(wf) ?? 0) + 1);
402
+ }
403
+ }
404
+ const total = completed + failed;
405
+ const metric = query?.metric ?? "successRate";
406
+ if (metric === "successRate") return {
407
+ value: total === 0 ? 1 : completed / total
408
+ };
409
+ if (metric === "failed") return {
410
+ value: failed
411
+ };
412
+ if (metric === "total") return {
413
+ value: total
414
+ };
415
+ if (metric === "topFailures") {
416
+ const items = [
417
+ ...failByWorkflow.entries()
418
+ ].map(([label, value]) => ({
419
+ label,
420
+ value
421
+ })).sort((a, b) => b.value - a.value);
422
+ return {
423
+ items
424
+ };
425
+ }
426
+ return {
427
+ value: total
428
+ };
429
+ }
430
+ };
431
+ }
432
+ __name(durableTimeseriesProvider, "durableTimeseriesProvider");
433
+ function durableRecentFailuresProvider() {
434
+ return {
435
+ name: "durable.recentFailures",
436
+ async resolve(query, ctx) {
437
+ const store = ctx.moduleRef.get(import_nestjs_durable_core2.STATE_STORE, {
438
+ strict: false
439
+ });
440
+ const limit = Math.min(200, Math.max(10, Number(query?.limit ?? 50)));
441
+ const windowMs = query?.windowMs === void 0 ? 24 * 60 * 60 * 1e3 : Number(query.windowMs);
442
+ const cutoff = windowMs > 0 ? Date.now() - windowMs : 0;
443
+ const [failed, dead] = await Promise.all([
444
+ store.listRuns({
445
+ status: "failed",
446
+ limit
447
+ }),
448
+ store.listRuns({
449
+ status: "dead",
450
+ limit
451
+ })
452
+ ]);
453
+ const rows = [
454
+ ...failed,
455
+ ...dead
456
+ ].filter((r) => +new Date(r.updatedAt) >= cutoff).sort((a, b) => +new Date(b.updatedAt) - +new Date(a.updatedAt)).slice(0, limit).map((r) => ({
457
+ // Compact UTC stamp ("YYYY-MM-DD HH:mm Z"); the generic table renderer
458
+ // prints column values as-is, so we format here.
459
+ updatedAt: `${new Date(r.updatedAt).toISOString().replace("T", " ").slice(0, 16)}Z`,
460
+ workflow: r.workflow,
461
+ runId: r.id,
462
+ error: r.error?.message ?? ""
463
+ }));
464
+ return {
465
+ rows
466
+ };
467
+ }
468
+ };
469
+ }
470
+ __name(durableRecentFailuresProvider, "durableRecentFailuresProvider");
471
+ function durableWorkerHealthProvider() {
472
+ return {
473
+ name: "durable.workerHealth",
474
+ async resolve(query, ctx) {
475
+ const engine = ctx.moduleRef.get(import_nestjs_durable_core2.WorkflowEngine, {
476
+ strict: false
477
+ });
478
+ const health = await engine.workerHealth();
479
+ const isStarved = /* @__PURE__ */ __name((g) => g.depth > 0 && g.liveWorkers.length === 0, "isStarved");
480
+ if (query?.metric === "starvedCount") {
481
+ return {
482
+ value: health.filter(isStarved).length
483
+ };
484
+ }
485
+ const rows = health.slice().sort((a, b) => Number(isStarved(b)) - Number(isStarved(a)) || b.depth - a.depth).map((g) => ({
486
+ group: g.group,
487
+ queued: g.depth,
488
+ liveWorkers: g.liveWorkers.length,
489
+ status: isStarved(g) ? "STARVED" : "ok"
490
+ }));
491
+ return {
492
+ rows
493
+ };
494
+ }
495
+ };
496
+ }
497
+ __name(durableWorkerHealthProvider, "durableWorkerHealthProvider");
498
+ function durableDurationProvider() {
499
+ return {
500
+ name: "durable.duration",
501
+ async resolve(query, ctx) {
502
+ const entries = await fetchEntries(ctx);
503
+ const startedAt = /* @__PURE__ */ new Map();
504
+ for (const e of entries) {
505
+ const c = e.content ?? {};
506
+ if (c.event === "run.started" && c.runId && e.createdAt) {
507
+ startedAt.set(c.runId, +new Date(e.createdAt));
508
+ }
509
+ }
510
+ const durs = [];
511
+ for (const e of entries) {
512
+ const c = e.content ?? {};
513
+ if (c.event === "run.completed" || c.event === "run.failed") {
514
+ if (typeof c.durationMs === "number") {
515
+ durs.push(c.durationMs);
516
+ } else if (c.runId) {
517
+ const start = startedAt.get(c.runId);
518
+ const end = e.createdAt ? +new Date(e.createdAt) : void 0;
519
+ if (start !== void 0 && end !== void 0 && end >= start) {
520
+ durs.push(end - start);
521
+ }
522
+ }
523
+ }
524
+ }
525
+ durs.sort((a, b) => a - b);
526
+ const p50 = percentile(durs, 50);
527
+ const p95 = percentile(durs, 95);
528
+ const p99 = percentile(durs, 99);
529
+ const metric = query?.metric;
530
+ if (metric === "p50") return {
531
+ value: p50
532
+ };
533
+ if (metric === "p95") return {
534
+ value: p95
535
+ };
536
+ if (metric === "p99") return {
537
+ value: p99
538
+ };
539
+ const max = durs.at(-1) ?? 0;
540
+ const bucketCount = 8;
541
+ const size = Math.max(1, Math.ceil((max + 1) / bucketCount));
542
+ const buckets = Array.from({
543
+ length: bucketCount
544
+ }, (_, i) => ({
545
+ label: `${Math.round(i * size / 100) / 10}s`,
546
+ count: 0
547
+ }));
548
+ for (const d of durs) {
549
+ const bi = Math.min(bucketCount - 1, Math.floor(d / size));
550
+ const bucket = buckets[bi];
551
+ if (bucket) bucket.count += 1;
552
+ }
553
+ return {
554
+ buckets,
555
+ p50,
556
+ p95,
557
+ p99
558
+ };
559
+ }
560
+ };
561
+ }
562
+ __name(durableDurationProvider, "durableDurationProvider");
563
+ function durableRunsOverTimeProvider() {
564
+ return {
565
+ name: "durable.runsOverTime",
566
+ async resolve(query, ctx) {
567
+ const entries = await fetchEntries(ctx);
568
+ const n = Number(query?.buckets ?? 24);
569
+ const now = Date.now();
570
+ let minT = now;
571
+ for (const e of entries) {
572
+ if (e.createdAt) {
573
+ const t = +new Date(e.createdAt);
574
+ if (t < minT) minT = t;
575
+ }
576
+ }
577
+ const span = Math.max(now - minT, 1);
578
+ const bucketSize = span / n;
579
+ const rows = Array.from({
580
+ length: n
581
+ }, (_, i) => {
582
+ const bucketStart = new Date(minT + i * bucketSize);
583
+ const label = bucketStart.toISOString().slice(11, 16);
584
+ return {
585
+ label,
586
+ done: 0,
587
+ failed: 0
588
+ };
589
+ });
590
+ for (const e of entries) {
591
+ const c = e.content ?? {};
592
+ if (c.event !== "run.completed" && c.event !== "run.failed") continue;
593
+ const t = e.createdAt ? +new Date(e.createdAt) : 0;
594
+ const idx = Math.min(n - 1, Math.floor((t - minT) / bucketSize));
595
+ const row = rows[idx];
596
+ if (row) {
597
+ if (c.event === "run.completed") row.done += 1;
598
+ else row.failed += 1;
599
+ }
600
+ }
601
+ return {
602
+ rows
603
+ };
604
+ }
605
+ };
606
+ }
607
+ __name(durableRunsOverTimeProvider, "durableRunsOverTimeProvider");
608
+ function durableSuccessRateProvider() {
609
+ return {
610
+ name: "durable.successRate",
611
+ async resolve(query, ctx) {
612
+ const entries = await fetchEntries(ctx);
613
+ const windowMs = Number(query?.windowMs ?? 24 * 60 * 60 * 1e3);
614
+ const now = Date.now();
615
+ const { current, previous } = splitWindows(entries, windowMs, now);
616
+ const value = successRateOf(current);
617
+ const prevRate = successRateOf(previous);
618
+ const delta = previous.length > 0 ? value - prevRate : void 0;
619
+ const sparkBuckets = 8;
620
+ const bucketSize = windowMs / sparkBuckets;
621
+ const sparkStart = now - windowMs;
622
+ const spark = Array.from({
623
+ length: sparkBuckets
624
+ }, (_, i) => {
625
+ const bStart = sparkStart + i * bucketSize;
626
+ const bEnd = bStart + bucketSize;
627
+ const bEntries = current.filter((e) => {
628
+ const t = e.createdAt ? +new Date(e.createdAt) : 0;
629
+ return t > bStart && t <= bEnd;
630
+ });
631
+ return successRateOf(bEntries);
632
+ });
633
+ return {
634
+ value,
635
+ delta,
636
+ spark
637
+ };
638
+ }
639
+ };
640
+ }
641
+ __name(durableSuccessRateProvider, "durableSuccessRateProvider");
642
+ function durableThroughputProvider() {
643
+ return {
644
+ name: "durable.throughput",
645
+ async resolve(query, ctx) {
646
+ const entries = await fetchEntries(ctx);
647
+ const windowMs = Number(query?.windowMs ?? 24 * 60 * 60 * 1e3);
648
+ const now = Date.now();
649
+ const { current, previous } = splitWindows(entries, windowMs, now);
650
+ const countCompleted = /* @__PURE__ */ __name((es) => es.filter((e) => (e.content ?? {}).event === "run.completed").length, "countCompleted");
651
+ const windowHours = windowMs / (60 * 60 * 1e3);
652
+ const value = countCompleted(current) / windowHours;
653
+ const prevValue = countCompleted(previous) / windowHours;
654
+ const delta = previous.length > 0 ? value - prevValue : void 0;
655
+ const sparkBuckets = 8;
656
+ const bucketSize = windowMs / sparkBuckets;
657
+ const bucketHours = bucketSize / (60 * 60 * 1e3);
658
+ const sparkStart = now - windowMs;
659
+ const spark = Array.from({
660
+ length: sparkBuckets
661
+ }, (_, i) => {
662
+ const bStart = sparkStart + i * bucketSize;
663
+ const bEnd = bStart + bucketSize;
664
+ const bEntries = current.filter((e) => {
665
+ const t = e.createdAt ? +new Date(e.createdAt) : 0;
666
+ return t > bStart && t <= bEnd;
667
+ });
668
+ return countCompleted(bEntries) / bucketHours;
669
+ });
670
+ return {
671
+ value,
672
+ delta,
673
+ spark
674
+ };
675
+ }
676
+ };
677
+ }
678
+ __name(durableThroughputProvider, "durableThroughputProvider");
679
+ var STATE_BREAKDOWN_PALETTE = [
680
+ "#34d399",
681
+ "#fbbf24",
682
+ "#f87171",
683
+ "#38bdf8",
684
+ "#a78bfa"
685
+ ];
686
+ var STATE_BREAKDOWN_STATUSES = [
687
+ "running",
688
+ "pending",
689
+ "completed",
690
+ "failed",
691
+ "dead"
692
+ ];
693
+ function durableStateBreakdownProvider() {
694
+ return {
695
+ name: "durable.stateBreakdown",
696
+ async resolve(_query, ctx) {
697
+ const store = ctx.moduleRef.get(import_nestjs_durable_core2.STATE_STORE, {
698
+ strict: false
699
+ });
700
+ const counts = await Promise.all(STATE_BREAKDOWN_STATUSES.map((status) => store.listRuns({
701
+ status,
702
+ limit: STATE_CAP
703
+ }).then((runs) => runs.length)));
704
+ const segments = STATE_BREAKDOWN_STATUSES.map((label, i) => ({
705
+ label,
706
+ value: counts[i],
707
+ color: STATE_BREAKDOWN_PALETTE[i]
708
+ }));
709
+ return {
710
+ segments
711
+ };
712
+ }
713
+ };
714
+ }
715
+ __name(durableStateBreakdownProvider, "durableStateBreakdownProvider");
716
+
717
+ // src/durable-telescope.extension.ts
718
+ function durableTelescopeExtension(opts = {}) {
719
+ return (0, import_nestjs_telescope2.defineTelescopeExtension)({
720
+ name: "durable",
721
+ watchers: /* @__PURE__ */ __name(() => [
722
+ new DurableTelescopeWatcher()
723
+ ], "watchers"),
724
+ entryTypes: /* @__PURE__ */ __name(() => [
725
+ {
726
+ id: "durable",
727
+ label: "Workflows",
728
+ dot: "bg-amber-400"
729
+ }
730
+ ], "entryTypes"),
731
+ dashboards: /* @__PURE__ */ __name(() => [
732
+ durableDashboard(opts)
733
+ ], "dashboards"),
734
+ dataProviders: /* @__PURE__ */ __name(() => [
735
+ durableStateProvider(),
736
+ durableTimeseriesProvider(),
737
+ durableRecentFailuresProvider(),
738
+ durableWorkerHealthProvider(),
739
+ durableDurationProvider(),
740
+ durableRunsOverTimeProvider(),
741
+ durableSuccessRateProvider(),
742
+ durableThroughputProvider(),
743
+ durableStateBreakdownProvider()
744
+ ], "dataProviders")
745
+ });
746
+ }
747
+ __name(durableTelescopeExtension, "durableTelescopeExtension");
748
+ // Annotate the CommonJS export names for ESM import in node:
749
+ 0 && (module.exports = {
750
+ DurableTelescopeWatcher,
751
+ durableDashboard,
752
+ durableDurationProvider,
753
+ durableRecentFailuresProvider,
754
+ durableRunsOverTimeProvider,
755
+ durableStateBreakdownProvider,
756
+ durableStateProvider,
757
+ durableSuccessRateProvider,
758
+ durableTelescopeExtension,
759
+ durableThroughputProvider,
760
+ durableTimeseriesProvider,
761
+ durableWorkerHealthProvider
762
+ });
763
+ //# sourceMappingURL=index.cjs.map