@dudousxd/nestjs-telescope-ui 1.0.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/CHANGELOG.md +349 -0
- package/LICENSE +21 -0
- package/README.md +51 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +2 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/telescope-client.d.ts +63 -0
- package/dist/client/telescope-client.d.ts.map +1 -0
- package/dist/client/telescope-client.js +87 -0
- package/dist/client/telescope-client.js.map +1 -0
- package/dist/client/types.d.ts +267 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +2 -0
- package/dist/client/types.js.map +1 -0
- package/dist/react/components/batch-timeline.d.ts +7 -0
- package/dist/react/components/batch-timeline.d.ts.map +1 -0
- package/dist/react/components/batch-timeline.js +10 -0
- package/dist/react/components/batch-timeline.js.map +1 -0
- package/dist/react/components/cache-badge.d.ts +20 -0
- package/dist/react/components/cache-badge.d.ts.map +1 -0
- package/dist/react/components/cache-badge.js +42 -0
- package/dist/react/components/cache-badge.js.map +1 -0
- package/dist/react/components/charts/area-chart-card.d.ts +16 -0
- package/dist/react/components/charts/area-chart-card.d.ts.map +1 -0
- package/dist/react/components/charts/area-chart-card.js +12 -0
- package/dist/react/components/charts/area-chart-card.js.map +1 -0
- package/dist/react/components/charts/bar-chart-card.d.ts +26 -0
- package/dist/react/components/charts/bar-chart-card.d.ts.map +1 -0
- package/dist/react/components/charts/bar-chart-card.js +27 -0
- package/dist/react/components/charts/bar-chart-card.js.map +1 -0
- package/dist/react/components/charts/chart-card.d.ts +11 -0
- package/dist/react/components/charts/chart-card.d.ts.map +1 -0
- package/dist/react/components/charts/chart-card.js +9 -0
- package/dist/react/components/charts/chart-card.js.map +1 -0
- package/dist/react/components/charts/chart-theme.d.ts +41 -0
- package/dist/react/components/charts/chart-theme.d.ts.map +1 -0
- package/dist/react/components/charts/chart-theme.js +53 -0
- package/dist/react/components/charts/chart-theme.js.map +1 -0
- package/dist/react/components/charts/index.d.ts +6 -0
- package/dist/react/components/charts/index.d.ts.map +1 -0
- package/dist/react/components/charts/index.js +6 -0
- package/dist/react/components/charts/index.js.map +1 -0
- package/dist/react/components/charts/stacked-area-chart-card.d.ts +14 -0
- package/dist/react/components/charts/stacked-area-chart-card.d.ts.map +1 -0
- package/dist/react/components/charts/stacked-area-chart-card.js +13 -0
- package/dist/react/components/charts/stacked-area-chart-card.js.map +1 -0
- package/dist/react/components/entries-table.d.ts +11 -0
- package/dist/react/components/entries-table.d.ts.map +1 -0
- package/dist/react/components/entries-table.js +68 -0
- package/dist/react/components/entries-table.js.map +1 -0
- package/dist/react/components/entry-detail.d.ts +6 -0
- package/dist/react/components/entry-detail.d.ts.map +1 -0
- package/dist/react/components/entry-detail.js +110 -0
- package/dist/react/components/entry-detail.js.map +1 -0
- package/dist/react/components/entry-insights.d.ts +5 -0
- package/dist/react/components/entry-insights.d.ts.map +1 -0
- package/dist/react/components/entry-insights.js +107 -0
- package/dist/react/components/entry-insights.js.map +1 -0
- package/dist/react/components/entry-types.d.ts +40 -0
- package/dist/react/components/entry-types.d.ts.map +1 -0
- package/dist/react/components/entry-types.js +44 -0
- package/dist/react/components/entry-types.js.map +1 -0
- package/dist/react/components/export-json-toolbar.d.ts +9 -0
- package/dist/react/components/export-json-toolbar.d.ts.map +1 -0
- package/dist/react/components/export-json-toolbar.js +21 -0
- package/dist/react/components/export-json-toolbar.js.map +1 -0
- package/dist/react/components/export-json.d.ts +14 -0
- package/dist/react/components/export-json.d.ts.map +1 -0
- package/dist/react/components/export-json.js +44 -0
- package/dist/react/components/export-json.js.map +1 -0
- package/dist/react/components/pulse-panel.d.ts +9 -0
- package/dist/react/components/pulse-panel.d.ts.map +1 -0
- package/dist/react/components/pulse-panel.js +20 -0
- package/dist/react/components/pulse-panel.js.map +1 -0
- package/dist/react/components/queue-sparkline.d.ts +5 -0
- package/dist/react/components/queue-sparkline.d.ts.map +1 -0
- package/dist/react/components/queue-sparkline.js +8 -0
- package/dist/react/components/queue-sparkline.js.map +1 -0
- package/dist/react/components/queues/EnqueueJobForm.d.ts +14 -0
- package/dist/react/components/queues/EnqueueJobForm.d.ts.map +1 -0
- package/dist/react/components/queues/EnqueueJobForm.js +61 -0
- package/dist/react/components/queues/EnqueueJobForm.js.map +1 -0
- package/dist/react/components/queues/JobActions.d.ts +38 -0
- package/dist/react/components/queues/JobActions.d.ts.map +1 -0
- package/dist/react/components/queues/JobActions.js +106 -0
- package/dist/react/components/queues/JobActions.js.map +1 -0
- package/dist/react/components/queues/JobDetailDrawer.d.ts +11 -0
- package/dist/react/components/queues/JobDetailDrawer.d.ts.map +1 -0
- package/dist/react/components/queues/JobDetailDrawer.js +45 -0
- package/dist/react/components/queues/JobDetailDrawer.js.map +1 -0
- package/dist/react/components/queues/JobTable.d.ts +13 -0
- package/dist/react/components/queues/JobTable.d.ts.map +1 -0
- package/dist/react/components/queues/JobTable.js +0 -0
- package/dist/react/components/queues/JobTable.js.map +1 -0
- package/dist/react/components/queues/QueueList.d.ts +8 -0
- package/dist/react/components/queues/QueueList.d.ts.map +1 -0
- package/dist/react/components/queues/QueueList.js +27 -0
- package/dist/react/components/queues/QueueList.js.map +1 -0
- package/dist/react/components/queues/QueueStateTabs.d.ts +7 -0
- package/dist/react/components/queues/QueueStateTabs.d.ts.map +1 -0
- package/dist/react/components/queues/QueueStateTabs.js +12 -0
- package/dist/react/components/queues/QueueStateTabs.js.map +1 -0
- package/dist/react/components/queues/index.d.ts +8 -0
- package/dist/react/components/queues/index.d.ts.map +1 -0
- package/dist/react/components/queues/index.js +8 -0
- package/dist/react/components/queues/index.js.map +1 -0
- package/dist/react/components/queues/queue-format.d.ts +8 -0
- package/dist/react/components/queues/queue-format.d.ts.map +1 -0
- package/dist/react/components/queues/queue-format.js +51 -0
- package/dist/react/components/queues/queue-format.js.map +1 -0
- package/dist/react/components/queues-panel.d.ts +6 -0
- package/dist/react/components/queues-panel.d.ts.map +1 -0
- package/dist/react/components/queues-panel.js +8 -0
- package/dist/react/components/queues-panel.js.map +1 -0
- package/dist/react/components/request-timeline.d.ts +14 -0
- package/dist/react/components/request-timeline.d.ts.map +1 -0
- package/dist/react/components/request-timeline.js +30 -0
- package/dist/react/components/request-timeline.js.map +1 -0
- package/dist/react/components/retention-indicator.d.ts +10 -0
- package/dist/react/components/retention-indicator.d.ts.map +1 -0
- package/dist/react/components/retention-indicator.js +38 -0
- package/dist/react/components/retention-indicator.js.map +1 -0
- package/dist/react/components/sparkline.d.ts +7 -0
- package/dist/react/components/sparkline.d.ts.map +1 -0
- package/dist/react/components/sparkline.js +20 -0
- package/dist/react/components/sparkline.js.map +1 -0
- package/dist/react/components/stacked-bars.d.ts +21 -0
- package/dist/react/components/stacked-bars.d.ts.map +1 -0
- package/dist/react/components/stacked-bars.js +51 -0
- package/dist/react/components/stacked-bars.js.map +1 -0
- package/dist/react/components/tag-autocomplete.d.ts +12 -0
- package/dist/react/components/tag-autocomplete.d.ts.map +1 -0
- package/dist/react/components/tag-autocomplete.js +90 -0
- package/dist/react/components/tag-autocomplete.js.map +1 -0
- package/dist/react/components/trace-link.d.ts +6 -0
- package/dist/react/components/trace-link.d.ts.map +1 -0
- package/dist/react/components/trace-link.js +10 -0
- package/dist/react/components/trace-link.js.map +1 -0
- package/dist/react/components/type-tabs.d.ts +5 -0
- package/dist/react/components/type-tabs.d.ts.map +1 -0
- package/dist/react/components/type-tabs.js +8 -0
- package/dist/react/components/type-tabs.js.map +1 -0
- package/dist/react/components/window-select.d.ts +5 -0
- package/dist/react/components/window-select.d.ts.map +1 -0
- package/dist/react/components/window-select.js +6 -0
- package/dist/react/components/window-select.js.map +1 -0
- package/dist/react/index.d.ts +23 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +23 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/telescope-context.d.ts +14 -0
- package/dist/react/telescope-context.d.ts.map +1 -0
- package/dist/react/telescope-context.js +27 -0
- package/dist/react/telescope-context.js.map +1 -0
- package/dist/react/use-telescope-queries.d.ts +244 -0
- package/dist/react/use-telescope-queries.d.ts.map +1 -0
- package/dist/react/use-telescope-queries.js +216 -0
- package/dist/react/use-telescope-queries.js.map +1 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +2 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/telescope-ui.controller.d.ts +10 -0
- package/dist/server/telescope-ui.controller.d.ts.map +1 -0
- package/dist/server/telescope-ui.controller.js +105 -0
- package/dist/server/telescope-ui.controller.js.map +1 -0
- package/dist/server/telescope-ui.module.d.ts +6 -0
- package/dist/server/telescope-ui.module.d.ts.map +1 -0
- package/dist/server/telescope-ui.module.js +26 -0
- package/dist/server/telescope-ui.module.js.map +1 -0
- package/dist/server/telescope-ui.options.d.ts +12 -0
- package/dist/server/telescope-ui.options.d.ts.map +1 -0
- package/dist/server/telescope-ui.options.js +2 -0
- package/dist/server/telescope-ui.options.js.map +1 -0
- package/dist/spa/assets/index-BHS361H_.js +135 -0
- package/dist/spa/assets/index-DsBEgxHC.css +1 -0
- package/dist/spa/index.html +13 -0
- package/package.json +97 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
# @dudousxd/nestjs-telescope-ui
|
|
2
|
+
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`1f00e62`](https://github.com/DavideCarvalho/nestjs-telescope/commit/1f00e62c8e60482b64251813680a5f866ef1619a) - Make the telescope mount path configurable. `TelescopeModule.forRoot({ path:
|
|
8
|
+
'observability' })` and `TelescopeUiModule.forRoot({ path: 'observability' })`
|
|
9
|
+
now serve the dashboard at `/observability` and the API at `/observability/api`.
|
|
10
|
+
The `path` option (normalized — leading/trailing slashes stripped) defaults to
|
|
11
|
+
`'telescope'`, so behavior is byte-identical when unset.
|
|
12
|
+
|
|
13
|
+
Core binds the API controller route at module-build time via a per-`forRoot`
|
|
14
|
+
subclass of `TelescopeController` (`dynamicController(...)`), preserving all
|
|
15
|
+
route/DI metadata. `forRootAsync` accepts a static `path` field (the route must
|
|
16
|
+
be known synchronously). The request-capture middleware now skips the configured
|
|
17
|
+
path instead of a hardcoded `/telescope`, and the resolved core config exposes
|
|
18
|
+
`path`.
|
|
19
|
+
|
|
20
|
+
The UI is **not** rebuilt per-path: the SPA keeps the `/telescope/` Vite base
|
|
21
|
+
placeholder and the UI controller rewrites the asset base to `/<path>/` at serve
|
|
22
|
+
time, injecting `window.__TELESCOPE_BASE__` so the client derives its API base
|
|
23
|
+
(`createTelescopeClient` falls back to `/telescope/api` when the global is
|
|
24
|
+
absent; an explicit `baseUrl` still wins). Asset traversal safety is unchanged.
|
|
25
|
+
|
|
26
|
+
- [`090cd1f`](https://github.com/DavideCarvalho/nestjs-telescope/commit/090cd1ff871dbe46c1c877a26f90496550b5304c) - Dashboard insights, charts, and trace grouping. Core gains a `StatsService` +
|
|
27
|
+
`GET /stats` endpoint with per-type analytics (latency p50/p95/p99, query-family
|
|
28
|
+
breakdown, cache hit/miss ratio, request status breakdown) and an `EntryQuery.traceId`
|
|
29
|
+
filter across all storages. The schedule watcher now tags scheduled runs with a stable
|
|
30
|
+
`schedule` tag. The dashboard adds a per-type insights header with Recharts cards, cache
|
|
31
|
+
hit/miss badges, a working Schedule view (job entries tagged `schedule`), and a
|
|
32
|
+
`#/traces/:traceId` page that groups every entry sharing an OpenTelemetry trace.
|
|
33
|
+
|
|
34
|
+
- [`b7326b3`](https://github.com/DavideCarvalho/nestjs-telescope/commit/b7326b33d8d55b5f1ac5de4256f5e1980278699e) - Add a **Dumps** dev tool. Call `telescopeDump(value, 'label')` anywhere in your
|
|
35
|
+
code (no dependency injection at the call site) and the value shows up in a new
|
|
36
|
+
"Dumps" tab in the dashboard — a request-correlated alternative to
|
|
37
|
+
`console.log`. `telescopeDump` forwards to a module-level sink that
|
|
38
|
+
`TelescopeService` wires on construction and detaches on shutdown (it is a
|
|
39
|
+
no-op until wired, so importing it in shared code never crashes outside a
|
|
40
|
+
Telescope-enabled app). `TelescopeService.dump(value, label?)` records a `dump`
|
|
41
|
+
entry whose `value` is redacted by the Recorder like any other content and
|
|
42
|
+
correlated to the active batch. Core exports `telescopeDump`, `setTelescopeDump`,
|
|
43
|
+
`DumpContent`, and `EntryType.Dump`. The UI gains a `dump` entry type, a detail
|
|
44
|
+
view that renders the label and pretty-printed JSON value (guarding
|
|
45
|
+
non-serializable values), and a table summary showing the label or a value
|
|
46
|
+
preview.
|
|
47
|
+
|
|
48
|
+
- [`bfc0e26`](https://github.com/DavideCarvalho/nestjs-telescope/commit/bfc0e268388b5563d05c24e4de6ff99c74d1201a) - Add free-text search over entries. `EntryQuery` gains a `search?: string` field —
|
|
49
|
+
a case-insensitive substring matched against the entry's serialized `content`, so
|
|
50
|
+
you can find a request by uri, a query by sql, a cache op by key, or an exception
|
|
51
|
+
by message. It is applied as an extra AND predicate, so it composes with every
|
|
52
|
+
other filter and with keyset pagination, and it is independent of `omitContent`
|
|
53
|
+
(the match runs over the stored content before any projection).
|
|
54
|
+
|
|
55
|
+
Every storage provider implements it: sqlite (`content LIKE '%term%'`), mikro-orm
|
|
56
|
+
(`raw('content')` `$like` so the JSON column is matched as text, not JSON-encoded),
|
|
57
|
+
in-memory and redis (case-insensitive substring over `JSON.stringify(content)` in
|
|
58
|
+
the predicate / in-scan post-filter). The `GET /entries` endpoint reads a non-empty
|
|
59
|
+
`search` query param, and the dashboard entries filter bar gains a "Search content"
|
|
60
|
+
input (commits on Enter/blur, never on keystroke) shown as a clearable
|
|
61
|
+
`search:"term"` chip that "Clear filters" drops.
|
|
62
|
+
|
|
63
|
+
- [`6817fe6`](https://github.com/DavideCarvalho/nestjs-telescope/commit/6817fe62775b1ff847fdb1038d3298e7709569e0) - Add exception grouping to the per-type stats insight. `summarizeStats` now
|
|
64
|
+
returns `exceptions?: ExceptionGroupStats[]` for the `exception` type: groups
|
|
65
|
+
the window's exception entries by family key (the entry's `familyHash`, or a
|
|
66
|
+
`${class}: ${message}` fallback when null) with `count`, `lastAt`, `class`,
|
|
67
|
+
`message`, and a per-bucket `overTime` series aligned to the report's buckets,
|
|
68
|
+
sorted by count desc then last-seen desc (top 8). The Exceptions view renders an
|
|
69
|
+
"Exception groups" list — class (mono), truncated message, count, relative
|
|
70
|
+
last-seen, and an inline over-time sparkline — each row drilling through to that
|
|
71
|
+
group's occurrences via `?familyHash=`.
|
|
72
|
+
|
|
73
|
+
- [`20ceb87`](https://github.com/DavideCarvalho/nestjs-telescope/commit/20ceb878cd4495dfbc7a3c71d882ae216a633757) - Surface JSON export and retention/sampling in the dashboard.
|
|
74
|
+
|
|
75
|
+
Core: `TelescopeMeta` (`GET /meta`) now reports the resolved `retention`
|
|
76
|
+
(`{ afterMs, keepLast }` from `prune`, or `null` when unbounded) and the
|
|
77
|
+
resolved per-type `sampling` rates, sourced from the resolved config.
|
|
78
|
+
|
|
79
|
+
UI: the entry-detail page gains a "Copy JSON" / "Download JSON" toolbar that
|
|
80
|
+
exports the full `EntryWithBatch` (`telescope-entry-<id>.json`); the trace page
|
|
81
|
+
gains a "Download JSON" button exporting all trace entries as a JSON array
|
|
82
|
+
(`telescope-trace-<traceId>.json`). The dashboard header shows a subtle
|
|
83
|
+
retention indicator (e.g. "retention: 5m", or "none" when unbounded) with a
|
|
84
|
+
sampling tooltip when any type is down-sampled below 1. New `toPrettyJson`,
|
|
85
|
+
`copyJson`, and `downloadJson` export utilities back the buttons; clipboard
|
|
86
|
+
absence degrades gracefully.
|
|
87
|
+
|
|
88
|
+
- [`d3ccf7d`](https://github.com/DavideCarvalho/nestjs-telescope/commit/d3ccf7dfdbe515b7efe134ca0d7d0cfb901bc8a2) - Add a "Telescope health" card to the Overview page rendering `GET /health`:
|
|
89
|
+
per-capture cost (µs), buffer pressure + high-water, flush durations, and dropped
|
|
90
|
+
count (green when keeping up, red with an overflow/store breakdown when shedding).
|
|
91
|
+
Makes Telescope's own overhead visible at a glance.
|
|
92
|
+
|
|
93
|
+
- [`9d8eb65`](https://github.com/DavideCarvalho/nestjs-telescope/commit/9d8eb6562a7584801d5aa8b74491091f0fade5f9) - Let operators **enqueue (send) a new job/message** onto a queue from the dashboard.
|
|
94
|
+
|
|
95
|
+
Core adds an optional `enqueue?(queue, payload, opts, ctx)` method to the
|
|
96
|
+
`QueueManager` SPI and a new `POST /telescope/api/queues/live/:driver/:queue/enqueue`
|
|
97
|
+
route. Unlike the other mutations it carries a JSON body (`{ name?, payload }`),
|
|
98
|
+
so it lives on its own path rather than under `:action` — but it flows through the
|
|
99
|
+
same default-deny `TelescopeActionGuard` as `retry` / `remove` / `redrive`: the
|
|
100
|
+
guard recovers the `enqueue` action from the request path, so without an
|
|
101
|
+
`authorizeAction` callback it returns `403`. The route returns `400` when the
|
|
102
|
+
payload is absent and `404` when the driver is unknown or doesn't implement
|
|
103
|
+
`enqueue`. `enqueue` is added to `QUEUE_ACTIONS` and advertised in the
|
|
104
|
+
`/queues/live` `actionsByDriver` capabilities when a manager implements it.
|
|
105
|
+
|
|
106
|
+
`BullMqQueueManager` implements `enqueue` via the real `Queue.add(name, data)`
|
|
107
|
+
(defaulting the name to `manual`), returning the new job id.
|
|
108
|
+
|
|
109
|
+
The UI gains an "Send message" form on the queue console — shown only when the
|
|
110
|
+
selected driver advertises `enqueue` and mutations are enabled. It parses the
|
|
111
|
+
payload textarea as JSON (invalid JSON surfaces inline without calling the API),
|
|
112
|
+
posts via a new `queueEnqueue` client method, and on success confirms and
|
|
113
|
+
refreshes the queue counts/jobs. A `403` surfaces inline as "Not authorized".
|
|
114
|
+
|
|
115
|
+
- [`9b6f12f`](https://github.com/DavideCarvalho/nestjs-telescope/commit/9b6f12f8b00f847f52c1adaa043c1e32d0b64598) - Add the live queue management console. The Queues area now leads with a
|
|
116
|
+
master-detail manager (`#/queues`): a live queue list with per-state count
|
|
117
|
+
badges and paused indicators, state tabs, a dense job table with cursor-based
|
|
118
|
+
"Load more", and a slide-over job detail drawer (pretty-printed payload/opts,
|
|
119
|
+
timestamps, attempts, and stacktrace for failures). Retry / remove / promote
|
|
120
|
+
and "Retry all failed" actions render only when the driver advertises the
|
|
121
|
+
action and `mutationsEnabled` is true (server-side default-deny still enforced;
|
|
122
|
+
403s surface a friendly "Not authorized" hint). The Phase-3 throughput/runtime
|
|
123
|
+
metrics now live under a `Metrics` sub-tab (`#/queues/metrics`) so there is a
|
|
124
|
+
single, coherent "Queues" nav entry.
|
|
125
|
+
|
|
126
|
+
- [`ba7eeae`](https://github.com/DavideCarvalho/nestjs-telescope/commit/ba7eeaeb23da9d054f5ef40b769f7738607305e6) - Add a gated "Redrive DLQ" action to the queue management UI. On the failed tab /
|
|
127
|
+
queue header, `RedriveDlqButton` is shown only when the driver advertises
|
|
128
|
+
`'redrive'` (`capabilities.actionsByDriver[driver]`) and `mutationsEnabled` is
|
|
129
|
+
true — so it surfaces for SQS-style DLQ drivers and stays hidden for BullMQ. It
|
|
130
|
+
confirms before firing (it moves messages back to the source queue) via the
|
|
131
|
+
existing `useQueueAction()` with `action: 'redrive'`, and a 403 surfaces inline as
|
|
132
|
+
"Not authorized".
|
|
133
|
+
|
|
134
|
+
- [`3778c8d`](https://github.com/DavideCarvalho/nestjs-telescope/commit/3778c8d9fbbfeb96e0e32b5ebed63751a9debcd7) - Add Laravel-Telescope-style per-type navigation and entry filtering to the
|
|
135
|
+
dashboard UI. The chrome is now a left sidebar with the existing top-level nav
|
|
136
|
+
(Overview, Entries, Pulse, Queues) plus a grouped "Watchers" section linking to
|
|
137
|
+
each entry type (Requests, Queries, Exceptions, Jobs, Mail, Cache, Schedule,
|
|
138
|
+
HTTP Client), each with a per-type color dot and routing to the entries list
|
|
139
|
+
pre-filtered to that type via `#/entries/<type>`. The entries list gains a
|
|
140
|
+
compact filter bar: an active-type chip with a clear affordance, a tag text
|
|
141
|
+
filter wired into the backend-supported `tag` query param, a "Clear filters"
|
|
142
|
+
control, and type/tag-aware empty states. Entry detail moves to
|
|
143
|
+
`#/entries/view/:id` to disambiguate from the per-type list route.
|
|
144
|
+
|
|
145
|
+
- [`25fcff4`](https://github.com/DavideCarvalho/nestjs-telescope/commit/25fcff41ea9783d8a8085f5a2b7358464d3d74be) - Add a Horizon-style Overview landing page (now the index route `#/`) with a stat
|
|
146
|
+
cards row (entries, requests, exceptions, jobs, jobs/min, failed jobs), a
|
|
147
|
+
throughput area chart, a by-type stacked-area chart, and recent-failures /
|
|
148
|
+
slowest lists linking to entry detail. Introduce dark-themed Recharts chart
|
|
149
|
+
primitives (`AreaChartCard`, `StackedAreaChartCard`, `BarChartCard` + shared
|
|
150
|
+
`chart-theme`), exported from `/react`, with all Recharts imports isolated to
|
|
151
|
+
`src/react/components/charts/*`. Re-skin the Pulse throughput-by-type and the
|
|
152
|
+
Queues failure-rate visualizations onto the new charts.
|
|
153
|
+
|
|
154
|
+
- [`c0804b9`](https://github.com/DavideCarvalho/nestjs-telescope/commit/c0804b96523deeb92762a03aa6b1ccee16598cef) - Add a global live-tail pause/resume toggle to the dashboard. A `Live`/`Paused`
|
|
155
|
+
control in the header freezes all polling so you can inspect a moment in time.
|
|
156
|
+
`TelescopeProvider` now exposes a live-tail flag via `useLiveTail()`/`usePaused()`,
|
|
157
|
+
and every polling query hook (entries, meta, pulse, stats, queues, timeseries,
|
|
158
|
+
traces, live queues, queue jobs) sets `refetchInterval: false` while paused and
|
|
159
|
+
resumes at the normal interval when live. Default is live (unchanged behavior).
|
|
160
|
+
Manual navigation/clicks still fetch — only the interval is gated.
|
|
161
|
+
|
|
162
|
+
- [`6fa0946`](https://github.com/DavideCarvalho/nestjs-telescope/commit/6fa0946f5543868704864af2e32793eb448ac827) - Add the `model` and `redis` entry types (`EntryType.Model` / `EntryType.Redis`)
|
|
163
|
+
and their content shapes `ModelContent { action, entity, id, changes }` and
|
|
164
|
+
`RedisContent { command, args, durationMs }`, consumed by the new
|
|
165
|
+
`@dudousxd/nestjs-telescope-mikro-orm-watcher` and
|
|
166
|
+
`@dudousxd/nestjs-telescope-redis-watcher`. The UI gains Models/Redis nav types,
|
|
167
|
+
`ModelBody` / `RedisBody` detail renderers, and table summaries
|
|
168
|
+
(`<action> <entity>#<id>` for model, `<COMMAND> <args-preview>` for redis).
|
|
169
|
+
|
|
170
|
+
- [`d507547`](https://github.com/DavideCarvalho/nestjs-telescope/commit/d507547df3c13e76e90b8a97c4e3e1d8aef25bd1) - Add OpenTelemetry trace linking. The new `@dudousxd/nestjs-telescope-otel`
|
|
171
|
+
package implements core's `TraceContextProvider` SPI by reading the active
|
|
172
|
+
OpenTelemetry span via `@opentelemetry/api` (`OtelTraceContextProvider`,
|
|
173
|
+
optional peer, read-only — never creates or exports spans, degrades to `null`
|
|
174
|
+
when no span/API is present). Core gains the `TraceContextProvider` SPI,
|
|
175
|
+
`traceId`/`spanId` on each `Entry`, and a `meta.traceLink` template so the UI
|
|
176
|
+
can deep-link entries to a trace backend; the MikroORM storage provider adds the
|
|
177
|
+
corresponding trace columns so the ids survive persistence. The Redis storage
|
|
178
|
+
provider now reconstructs `traceId`/`spanId` when reading entries back, and the
|
|
179
|
+
dashboard shows a Trace row in the entry detail with a clickable deep-link when
|
|
180
|
+
`traceLink` is configured.
|
|
181
|
+
|
|
182
|
+
- [`cad6dae`](https://github.com/DavideCarvalho/nestjs-telescope/commit/cad6dae0dba4f22e476d78c23ce2f74f7f6848e4) - Two pulse/dashboard UX improvements.
|
|
183
|
+
|
|
184
|
+
N+1 query hotspots are now aggregated by query family instead of listed per
|
|
185
|
+
request. `summarizePulse` returns `NPlusOneHotspot[]` — one row per family with
|
|
186
|
+
`perRequest` (worst single-request repetition), `requests` (how many requests
|
|
187
|
+
tripped the threshold), `total` (sum of repetitions), and `sampleBatchId` (the
|
|
188
|
+
worst batch). Identical queries that previously appeared as several identical
|
|
189
|
+
rows now collapse into a single hotspot, sorted by total repetitions. The Pulse
|
|
190
|
+
panel renders `×{perRequest} per request · {requests} requests · {total} total`
|
|
191
|
+
and links each hotspot to its query family (`#/entries/query?familyHash=…`).
|
|
192
|
+
|
|
193
|
+
The entries table now surfaces a subtle `trace:<id>` chip on any row that
|
|
194
|
+
belongs to a trace, linking straight to `#/traces/:traceId` so traces are
|
|
195
|
+
discoverable from the list without opening an entry's detail. Rows without a
|
|
196
|
+
trace show nothing extra.
|
|
197
|
+
|
|
198
|
+
- [`c8596c8`](https://github.com/DavideCarvalho/nestjs-telescope/commit/c8596c85712880cb235e8cce059a1d93d339e9bd) - Add slow-request hotspots to the pulse panel — like the N+1 hotspot, but for
|
|
199
|
+
endpoints: which routes are consistently slow, aggregated by route with `p99`,
|
|
200
|
+
`p50`, and request `count`.
|
|
201
|
+
|
|
202
|
+
Request entries now carry a readable, normalized **route family** as their
|
|
203
|
+
`familyHash` (e.g. `"GET /api/base/:id/mel"`), via a new pure
|
|
204
|
+
`normalizeRoute(method, url)` helper that strips the query string and replaces
|
|
205
|
+
id-like path segments (UUID / all-digits / long hex) with `:id`. The route
|
|
206
|
+
family doubles as the human label and indexes on the existing `family_hash`
|
|
207
|
+
column, so the new `slowRoutes` aggregation runs entirely over content-less
|
|
208
|
+
columns in the existing two-pass pulse scan — no `content` reads, no hydration.
|
|
209
|
+
|
|
210
|
+
The pulse panel renders a "Slow request hotspots" section; each row links
|
|
211
|
+
through to that route's request entries (`familyHash` filter).
|
|
212
|
+
|
|
213
|
+
- [`abde392`](https://github.com/DavideCarvalho/nestjs-telescope/commit/abde39264effb31b0524cc4fa89a335276c8dccb) - Capture requests under a global prefix, and make query-shape insights actionable.
|
|
214
|
+
|
|
215
|
+
Core: the request-capture middleware no longer relies on NestJS module routing,
|
|
216
|
+
which silently scoped the catch-all to `/` whenever the host called
|
|
217
|
+
`setGlobalPrefix(...)`. New `telescopeRequestCapture(service)` factory lets hosts
|
|
218
|
+
register the capture globally (`app.use(...)` in bootstrap), and a
|
|
219
|
+
`registerRequestMiddleware: false` option disables the built-in registration.
|
|
220
|
+
Telescope's own dashboard routes are skipped inside the middleware instead of via
|
|
221
|
+
`.exclude()` (which breaks with catch-all routes). The query-family label now
|
|
222
|
+
tolerates content shapes that omit `slow`/`connection` (e.g. the MikroORM logger).
|
|
223
|
+
|
|
224
|
+
UI: the "Slowest query shapes" chart uses a horizontal, truncated layout (full
|
|
225
|
+
SQL on hover) so long labels no longer overlap, and each bar is clickable —
|
|
226
|
+
drilling into the entries list filtered to that query family.
|
|
227
|
+
|
|
228
|
+
- [`8ff32a2`](https://github.com/DavideCarvalho/nestjs-telescope/commit/8ff32a2cc95775224eea3377460d91674dfda47f) - Capture richer request detail. The request middleware now records the parsed
|
|
229
|
+
request body as `payload` and the authenticated user as the new
|
|
230
|
+
`RequestContent.user` field — read in the `res.on('finish')` callback (after the
|
|
231
|
+
host body-parser and guards have run). The user defaults to the raw request's
|
|
232
|
+
`user` (the Passport/guard convention) and is customizable via the new
|
|
233
|
+
`TelescopeModuleOptions.resolveUser(request)` hook. Both flow through the
|
|
234
|
+
Recorder's redaction (passwords/tokens masked). The request detail UI gains
|
|
235
|
+
collapsible Headers (with count), a pretty-printed Payload section, and a User
|
|
236
|
+
section ("anonymous" when none).
|
|
237
|
+
|
|
238
|
+
- [`b14a201`](https://github.com/DavideCarvalho/nestjs-telescope/commit/b14a20175eae3d3017e8cbc068d367a03f634175) - Add a live Schedule console for `@nestjs/schedule` scheduled tasks, mirroring the
|
|
239
|
+
Queues console.
|
|
240
|
+
|
|
241
|
+
Core gains a `ScheduleManager` SPI (`ScheduledTask` = name, kind, schedule,
|
|
242
|
+
nextRunAt, lastRunAt, lastDurationMs, lastStatus), a `ScheduleManagerRegistry`
|
|
243
|
+
that boots the configured managers, a `scheduleManagers?` option, and a
|
|
244
|
+
read-guarded `GET /telescope/api/schedules/live` returning `{ tasks }`.
|
|
245
|
+
|
|
246
|
+
The `@nestjs/schedule` watcher now also implements `ScheduleManager`: its
|
|
247
|
+
`listTasks()` reads `SchedulerRegistry` (via `moduleRef`) for the registered
|
|
248
|
+
cron/interval/timeout names, cron expression and next fire time, and merges its
|
|
249
|
+
own in-memory last-run map (additive to the existing entry-recording path). It
|
|
250
|
+
degrades gracefully — a missing/partial registry yields a null-filled or empty
|
|
251
|
+
list and never throws.
|
|
252
|
+
|
|
253
|
+
The UI adds a read-only `#/schedules` page (a table of name, kind, schedule,
|
|
254
|
+
next run, last run, duration, status) that polls like the queues console and
|
|
255
|
+
honors the live-tail pause, a `schedulesLive()` client method + `useSchedulesLive()`
|
|
256
|
+
hook, and a "Schedules" sidebar console link (distinct from the per-type
|
|
257
|
+
Schedule entries tab).
|
|
258
|
+
|
|
259
|
+
- [`a90ef56`](https://github.com/DavideCarvalho/nestjs-telescope/commit/a90ef569ba12484bece07d8de2045e13ff2ff528) - Two dashboard additions:
|
|
260
|
+
|
|
261
|
+
- **Slow outgoing HTTP hotspots.** The `http_client` watcher now records a
|
|
262
|
+
grouping `familyHash` of `${method} ${host}${normalizedPath}` (via a new
|
|
263
|
+
`normalizeHttpTarget` helper that keeps the host and `:id`-normalizes the
|
|
264
|
+
path), so outbound calls to the same external endpoint aggregate regardless of
|
|
265
|
+
ids. Pulse gains `slowOutgoing: SlowRouteHotspot[]` — top-N external targets by
|
|
266
|
+
p99 (count + p99/p50 over `durationMs`), computed entirely from content-less
|
|
267
|
+
columns (no hydration). The UI renders a "Slow outgoing HTTP" section in the
|
|
268
|
+
Pulse panel, each row deep-linking to `#/entries/http_client?familyHash=…`.
|
|
269
|
+
|
|
270
|
+
- **Server stats card.** A read-guarded `GET /telescope/api/server-stats`
|
|
271
|
+
endpoint returns a point-in-time Node process-health snapshot
|
|
272
|
+
(`uptimeSec`, `memory` RSS/heap MB, `cpu` user/system ms, `eventLoopDelayMs`,
|
|
273
|
+
`instanceId`). Event-loop delay is read from a `perf_hooks.monitorEventLoopDelay`
|
|
274
|
+
histogram started once at service init and degrades to `null` when unavailable.
|
|
275
|
+
The UI Overview page shows a polled "Server" card (honors pause) via the new
|
|
276
|
+
`serverStats()` client method and `useServerStats()` hook.
|
|
277
|
+
|
|
278
|
+
- [`f144584`](https://github.com/DavideCarvalho/nestjs-telescope/commit/f144584f7be7bda2a1189ddfd0b9fd461d2e1e86) - Turn the entries tag filter into an accessible autocomplete. Typing now queries
|
|
279
|
+
`GET /telescope/api/tags?prefix=` and surfaces matching tags with their entry
|
|
280
|
+
counts (top 10, sorted by count desc); picking one applies it as the active tag
|
|
281
|
+
filter and shows a clearable chip. Adds a `tags(prefix?)` client method and a
|
|
282
|
+
`useTags(prefix)` hook (non-polling reference data) plus the `TagCount` type.
|
|
283
|
+
|
|
284
|
+
- [`7b6636b`](https://github.com/DavideCarvalho/nestjs-telescope/commit/7b6636b54438427cd53ea0cbedd186b77d807169) - Add `@dudousxd/nestjs-telescope-ui`: a bundled React dashboard served by
|
|
285
|
+
`TelescopeUiModule` (no host frontend deps), with an Entries view (type tabs,
|
|
286
|
+
polling) and correlated entry detail. The same building blocks are exported at
|
|
287
|
+
`/react` (components + hooks) and `/client` (typed API client) so consumers can
|
|
288
|
+
compose their own dashboard. Core's request middleware now also excludes the
|
|
289
|
+
`/telescope` UI routes from capture.
|
|
290
|
+
|
|
291
|
+
- [`e64f35a`](https://github.com/DavideCarvalho/nestjs-telescope/commit/e64f35a1bb7cae15b2ef24404888463d04f81eef) - Add throughput time-series. `GET /telescope/api/timeseries?window=1h&buckets=60`
|
|
292
|
+
returns bucketed entry counts (total + per type) computed from stored entries
|
|
293
|
+
(`TimeseriesService` + `bucketTimeseries`), with optional `type`/`tag` filters.
|
|
294
|
+
The dashboard renders it as a zero-dependency SVG `Sparkline` on the Pulse page
|
|
295
|
+
(also exported at `/react`).
|
|
296
|
+
|
|
297
|
+
- [`4892ef6`](https://github.com/DavideCarvalho/nestjs-telescope/commit/4892ef61e45e5486d34c7ec82764e6767fe8233d) - Add a **Traces list** — browse recent distinct OTel traces instead of only
|
|
298
|
+
drilling in from a single entry. Core gains a pure `summarizeTraces` that groups
|
|
299
|
+
windowed entries by `traceId` (skipping null trace ids) into per-trace summaries
|
|
300
|
+
(`entryCount`, distinct sorted `types`, `firstAt`/`lastAt`, summed
|
|
301
|
+
`totalDurationMs`, optional `rootLabel`), a `TracesService` that scans the store
|
|
302
|
+
content-less over a window, and a `GET /telescope/api/traces?window=&limit=`
|
|
303
|
+
endpoint (read-guarded, mirrors `/timeseries`). The dashboard adds a **Traces**
|
|
304
|
+
nav item and a `#/traces` page listing recent traces — each row shows the root
|
|
305
|
+
label (or traceId), the type dots present, entry count, total duration, and
|
|
306
|
+
relative time, and links to the existing `#/traces/:traceId` grouping page.
|
|
307
|
+
|
|
308
|
+
- [`205eabe`](https://github.com/DavideCarvalho/nestjs-telescope/commit/205eabe2c3a34496f2dea608e89887407f4997df) - Add a Cmd+K / Ctrl+K command palette and a light/dark theme toggle to the
|
|
309
|
+
dashboard. The palette is a centered modal (backdrop/Escape close, focus-trapped
|
|
310
|
+
input, ArrowUp/Down wrapping highlight, Enter to navigate via the hash router)
|
|
311
|
+
whose action list is built from `ENTRY_TYPES` plus the static page targets so it
|
|
312
|
+
stays in sync. A header "⌘K" badge also opens it. The theme toggle (dark default)
|
|
313
|
+
persists to `localStorage`, restores on load, and applies a `light`/`dark` root
|
|
314
|
+
class; a scoped `.light` stylesheet flips the most common dark surfaces, text, and
|
|
315
|
+
borders for a usable light mode (full per-component theming is a follow-up).
|
|
316
|
+
|
|
317
|
+
- [`7797a2a`](https://github.com/DavideCarvalho/nestjs-telescope/commit/7797a2a1554aff49bb59f5ca1b204974a7e04a41) - Render the new `event` and `log` entry types: nav entries (Events/Logs), an event
|
|
318
|
+
detail (name + pretty payload + listener count) and a log detail (level badge +
|
|
319
|
+
message + context), plus table summaries (`event` name / `[level] message`).
|
|
320
|
+
|
|
321
|
+
- [`8dcded2`](https://github.com/DavideCarvalho/nestjs-telescope/commit/8dcded2a46582799e8176a83136a56cd1ac1f7fb) - Richer dashboard charts: per-type stacked-bar throughput (with a legend) on the
|
|
322
|
+
Pulse page, and a per-queue throughput sparkline column on the Queues page (via
|
|
323
|
+
a `tag`-filtered timeseries). New `StackedBars` / `QueueSparkline` components +
|
|
324
|
+
`stackedBarLayout`/`deriveTypes`/`colorForType` helpers exported at `/react`;
|
|
325
|
+
`QueuesPanel` gains an optional `sparkline` render-prop.
|
|
326
|
+
|
|
327
|
+
- [`d4c3986`](https://github.com/DavideCarvalho/nestjs-telescope/commit/d4c398631c2a77867b26a4b9bc86db073d9dd8c3) - Complete the dashboard with Pulse (per-type counts, slowest entries, top
|
|
328
|
+
exceptions, N+1 hotspots) and Queues (throughput, failure rate, runtime/wait
|
|
329
|
+
percentiles) views, each with a window selector. Typed the `pulse()`/`queues()`
|
|
330
|
+
client responses and exported `PulsePanel`/`QueuesPanel`/`WindowSelect` at
|
|
331
|
+
`/react`. Added an e2e that serves the real built bundle.
|
|
332
|
+
|
|
333
|
+
- [`e995e26`](https://github.com/DavideCarvalho/nestjs-telescope/commit/e995e266c87673f865164e76c7c930eb5170f6ac) - Show a request's batch as a compact timeline/waterfall in the entry detail. When
|
|
334
|
+
viewing a `request` entry that captured children (queries, cache, jobs, …), a new
|
|
335
|
+
`RequestTimeline` renders one row per batch entry ordered by `sequence`: a type
|
|
336
|
+
dot, a short label, a horizontal duration bar scaled to the slowest entry in the
|
|
337
|
+
batch, and the `durationMs`. Each row links to that child's detail. This makes
|
|
338
|
+
"where did the time go in this request" obvious without leaving for a trace
|
|
339
|
+
viewer. Null-duration children render with a minimal bar, and large batches cap
|
|
340
|
+
at 50 rows with a "+N more" note.
|
|
341
|
+
|
|
342
|
+
### Patch Changes
|
|
343
|
+
|
|
344
|
+
- [`da0aa24`](https://github.com/DavideCarvalho/nestjs-telescope/commit/da0aa245b4efac6b95037339b0b81b50305bf9d9) - Serve the dashboard `index.html` with `Cache-Control: no-store` and the hashed
|
|
345
|
+
asset bundles with a long immutable cache. Previously index.html had no cache
|
|
346
|
+
headers, so browsers kept loading a stale bundle across upgrades — the classic
|
|
347
|
+
"a widget is stuck loading / shows old labels after deploying a new version".
|
|
348
|
+
- Updated dependencies [[`73b50ad`](https://github.com/DavideCarvalho/nestjs-telescope/commit/73b50ad00193127271fdec36ad080d2858045922), [`9126bb0`](https://github.com/DavideCarvalho/nestjs-telescope/commit/9126bb04777cdaec6af3b0a1c5fe6f91d055ce82), [`1f00e62`](https://github.com/DavideCarvalho/nestjs-telescope/commit/1f00e62c8e60482b64251813680a5f866ef1619a), [`090cd1f`](https://github.com/DavideCarvalho/nestjs-telescope/commit/090cd1ff871dbe46c1c877a26f90496550b5304c), [`b7326b3`](https://github.com/DavideCarvalho/nestjs-telescope/commit/b7326b33d8d55b5f1ac5de4256f5e1980278699e), [`bfc0e26`](https://github.com/DavideCarvalho/nestjs-telescope/commit/bfc0e268388b5563d05c24e4de6ff99c74d1201a), [`7797a2a`](https://github.com/DavideCarvalho/nestjs-telescope/commit/7797a2a1554aff49bb59f5ca1b204974a7e04a41), [`6817fe6`](https://github.com/DavideCarvalho/nestjs-telescope/commit/6817fe62775b1ff847fdb1038d3298e7709569e0), [`20ceb87`](https://github.com/DavideCarvalho/nestjs-telescope/commit/20ceb878cd4495dfbc7a3c71d882ae216a633757), [`1dd4db0`](https://github.com/DavideCarvalho/nestjs-telescope/commit/1dd4db0f3cd46d04b35ac112343cfb424c2d3190), [`a9f517c`](https://github.com/DavideCarvalho/nestjs-telescope/commit/a9f517c076461aef55cfb90d072ec38427ace91b), [`9d8eb65`](https://github.com/DavideCarvalho/nestjs-telescope/commit/9d8eb6562a7584801d5aa8b74491091f0fade5f9), [`418f1f0`](https://github.com/DavideCarvalho/nestjs-telescope/commit/418f1f0421948b40b25f845441a716fa4c6655c2), [`e76980d`](https://github.com/DavideCarvalho/nestjs-telescope/commit/e76980ddd7ee740f1b337a422a98ca98d97a007e), [`80c8f97`](https://github.com/DavideCarvalho/nestjs-telescope/commit/80c8f9769c8ab9ee724635086740910bc4d44ea3), [`e14ac60`](https://github.com/DavideCarvalho/nestjs-telescope/commit/e14ac603551372fc3767c63a349c509582b5e6ab), [`6fa0946`](https://github.com/DavideCarvalho/nestjs-telescope/commit/6fa0946f5543868704864af2e32793eb448ac827), [`d507547`](https://github.com/DavideCarvalho/nestjs-telescope/commit/d507547df3c13e76e90b8a97c4e3e1d8aef25bd1), [`affd07e`](https://github.com/DavideCarvalho/nestjs-telescope/commit/affd07e4cb9ee85cfabaabed424833e8c638d04a), [`6a4d8d5`](https://github.com/DavideCarvalho/nestjs-telescope/commit/6a4d8d56321d3840fe64e646130ccfdafcfb1bdd), [`c1f1ec9`](https://github.com/DavideCarvalho/nestjs-telescope/commit/c1f1ec903d470d6b884924e2713de305b61b7481), [`cad6dae`](https://github.com/DavideCarvalho/nestjs-telescope/commit/cad6dae0dba4f22e476d78c23ce2f74f7f6848e4), [`c8596c8`](https://github.com/DavideCarvalho/nestjs-telescope/commit/c8596c85712880cb235e8cce059a1d93d339e9bd), [`c4222b1`](https://github.com/DavideCarvalho/nestjs-telescope/commit/c4222b16ef4c0fc9c61694eb67033f03369ff24e), [`de29d2f`](https://github.com/DavideCarvalho/nestjs-telescope/commit/de29d2f519b1f25bc702c9dcc737d99b4751c8c9), [`10a3bc2`](https://github.com/DavideCarvalho/nestjs-telescope/commit/10a3bc224f6e0b1a237e1e7631acad70493b4c12), [`abde392`](https://github.com/DavideCarvalho/nestjs-telescope/commit/abde39264effb31b0524cc4fa89a335276c8dccb), [`8ff32a2`](https://github.com/DavideCarvalho/nestjs-telescope/commit/8ff32a2cc95775224eea3377460d91674dfda47f), [`5f2eddd`](https://github.com/DavideCarvalho/nestjs-telescope/commit/5f2eddd0ed5d72bf1de323b45870d7ddcaf64349), [`b14a201`](https://github.com/DavideCarvalho/nestjs-telescope/commit/b14a20175eae3d3017e8cbc068d367a03f634175), [`a90ef56`](https://github.com/DavideCarvalho/nestjs-telescope/commit/a90ef569ba12484bece07d8de2045e13ff2ff528), [`593bcc8`](https://github.com/DavideCarvalho/nestjs-telescope/commit/593bcc85ad6558040c62ba66bd1e5e0cbe5a6ac7), [`7b6636b`](https://github.com/DavideCarvalho/nestjs-telescope/commit/7b6636b54438427cd53ea0cbedd186b77d807169), [`e64f35a`](https://github.com/DavideCarvalho/nestjs-telescope/commit/e64f35a1bb7cae15b2ef24404888463d04f81eef), [`4892ef6`](https://github.com/DavideCarvalho/nestjs-telescope/commit/4892ef61e45e5486d34c7ec82764e6767fe8233d)]:
|
|
349
|
+
- @dudousxd/nestjs-telescope@1.0.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Davi Carvalho
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# @dudousxd/nestjs-telescope-ui
|
|
2
|
+
|
|
3
|
+
A visual dashboard for [`@dudousxd/nestjs-telescope`](../../README.md) — a bundled
|
|
4
|
+
React SPA served by a NestJS module. **No frontend dependency is imposed on your
|
|
5
|
+
app:** React + Tailwind are bundled into static assets at publish time; your app
|
|
6
|
+
just serves them. Works on Express and Fastify.
|
|
7
|
+
|
|
8
|
+
## Turnkey dashboard
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { Module } from '@nestjs/common';
|
|
12
|
+
import { TelescopeModule } from '@dudousxd/nestjs-telescope';
|
|
13
|
+
import { TelescopeUiModule } from '@dudousxd/nestjs-telescope-ui';
|
|
14
|
+
|
|
15
|
+
@Module({
|
|
16
|
+
imports: [TelescopeModule.forRoot(), TelescopeUiModule.forRoot()],
|
|
17
|
+
})
|
|
18
|
+
export class AppModule {}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Open `/telescope`. The dashboard polls `/telescope/api/*` and shows captured
|
|
22
|
+
entries (type tabs + live polling) and, on an entry, the correlated batch — the
|
|
23
|
+
request and everything it caused.
|
|
24
|
+
|
|
25
|
+
## Build your own (composable)
|
|
26
|
+
|
|
27
|
+
The same pieces are exported so you can drop them into your own admin:
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { TelescopeProvider, useEntries, EntriesTable } from '@dudousxd/nestjs-telescope-ui/react';
|
|
31
|
+
|
|
32
|
+
function MyEntries() {
|
|
33
|
+
const { data } = useEntries({ type: 'query' });
|
|
34
|
+
return <EntriesTable entries={data?.data ?? []} />;
|
|
35
|
+
}
|
|
36
|
+
// wrap your tree once in <TelescopeProvider> (defaults to the /telescope/api base)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Or just the typed client (no React):
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { createTelescopeClient } from '@dudousxd/nestjs-telescope-ui/client';
|
|
43
|
+
const telescope = createTelescopeClient({ baseUrl: '/telescope/api' });
|
|
44
|
+
const page = await telescope.entries({ type: 'job' });
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Peer dependencies
|
|
48
|
+
|
|
49
|
+
`.` (the module) needs `@nestjs/common` / `@nestjs/core` (>= 11). `/react` needs
|
|
50
|
+
`react`, `react-dom`, `@tanstack/react-query` (only if you import it — they're
|
|
51
|
+
optional peers). The turnkey dashboard bundles its own copies.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,YAAY,EACV,cAAc,EACd,aAAa,EACb,eAAe,EACf,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAC/B,mBAAmB,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { EntriesQuery, Entry, EntryWithBatch, JobPage, Page, PulseReport, QueueCapabilities, QueueCounts, QueueJobDetail, QueueMetricsReport, QueueState, QueueSummary, ScheduledTask, ServerStats, StatsResult, TagCount, TelescopeHealth, TelescopeMeta, TimeseriesQuery, TimeseriesReport, TracesResult } from './types.js';
|
|
2
|
+
declare global {
|
|
3
|
+
interface Window {
|
|
4
|
+
/**
|
|
5
|
+
* Mount base (e.g. `/observability`) injected by the UI controller's
|
|
6
|
+
* index.html when the dashboard is served under a custom path. Absent on the
|
|
7
|
+
* default mount and in SSR/tests, where the API base falls back to
|
|
8
|
+
* `/telescope/api`.
|
|
9
|
+
*/
|
|
10
|
+
__TELESCOPE_BASE__?: string;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export interface TelescopeClientOptions {
|
|
14
|
+
/** Base URL of the telescope API. Default '/telescope/api' (or `${window.__TELESCOPE_BASE__}/api`). */
|
|
15
|
+
baseUrl?: string;
|
|
16
|
+
/** Fetch implementation (injectable for tests). Default global fetch. */
|
|
17
|
+
fetch?: typeof globalThis.fetch;
|
|
18
|
+
}
|
|
19
|
+
export type JobActionName = 'retry' | 'remove' | 'promote';
|
|
20
|
+
export type BulkActionName = 'retry-all' | 'redrive';
|
|
21
|
+
export interface TelescopeClient {
|
|
22
|
+
entries(query?: EntriesQuery): Promise<Page<Entry>>;
|
|
23
|
+
entry(id: string): Promise<EntryWithBatch>;
|
|
24
|
+
pulse(window?: string): Promise<PulseReport>;
|
|
25
|
+
queues(window?: string): Promise<QueueMetricsReport>;
|
|
26
|
+
timeseries(query?: TimeseriesQuery): Promise<TimeseriesReport>;
|
|
27
|
+
traces(window?: string, limit?: number): Promise<TracesResult>;
|
|
28
|
+
stats(type: string, window: string): Promise<StatsResult>;
|
|
29
|
+
tags(prefix?: string): Promise<TagCount[]>;
|
|
30
|
+
meta(): Promise<TelescopeMeta>;
|
|
31
|
+
serverStats(): Promise<ServerStats>;
|
|
32
|
+
health(): Promise<TelescopeHealth>;
|
|
33
|
+
liveQueues(): Promise<{
|
|
34
|
+
queues: QueueSummary[];
|
|
35
|
+
capabilities: QueueCapabilities;
|
|
36
|
+
}>;
|
|
37
|
+
schedulesLive(): Promise<{
|
|
38
|
+
tasks: ScheduledTask[];
|
|
39
|
+
}>;
|
|
40
|
+
queueCounts(driver: string, queue: string): Promise<QueueCounts>;
|
|
41
|
+
queueJobs(driver: string, queue: string, state: QueueState, page?: {
|
|
42
|
+
cursor?: string;
|
|
43
|
+
limit?: number;
|
|
44
|
+
}): Promise<JobPage>;
|
|
45
|
+
queueJob(driver: string, queue: string, id: string): Promise<QueueJobDetail | null>;
|
|
46
|
+
queueJobAction(driver: string, queue: string, id: string, action: JobActionName): Promise<{
|
|
47
|
+
ok: true;
|
|
48
|
+
}>;
|
|
49
|
+
queueAction(driver: string, queue: string, action: BulkActionName, opts?: {
|
|
50
|
+
state?: QueueState;
|
|
51
|
+
}): Promise<{
|
|
52
|
+
ok: true;
|
|
53
|
+
count?: number;
|
|
54
|
+
}>;
|
|
55
|
+
queueEnqueue(driver: string, queue: string, body: {
|
|
56
|
+
name?: string;
|
|
57
|
+
payload: unknown;
|
|
58
|
+
}): Promise<{
|
|
59
|
+
id: string | null;
|
|
60
|
+
}>;
|
|
61
|
+
}
|
|
62
|
+
export declare function createTelescopeClient(options?: TelescopeClientOptions): TelescopeClient;
|
|
63
|
+
//# sourceMappingURL=telescope-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telescope-client.d.ts","sourceRoot":"","sources":["../../src/client/telescope-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** Derives the API base from the server-injected mount base, falling back to the default. */
|
|
2
|
+
function defaultBaseUrl() {
|
|
3
|
+
const base = (typeof window !== 'undefined' && window.__TELESCOPE_BASE__) || '/telescope';
|
|
4
|
+
return `${base}/api`;
|
|
5
|
+
}
|
|
6
|
+
export function createTelescopeClient(options = {}) {
|
|
7
|
+
const baseUrl = (options.baseUrl ?? defaultBaseUrl()).replace(/\/$/, '');
|
|
8
|
+
const doFetch = options.fetch ?? globalThis.fetch;
|
|
9
|
+
async function get(path, params) {
|
|
10
|
+
let url = `${baseUrl}${path}`;
|
|
11
|
+
if (params) {
|
|
12
|
+
const search = new URLSearchParams();
|
|
13
|
+
for (const [key, value] of Object.entries(params)) {
|
|
14
|
+
if (value !== undefined)
|
|
15
|
+
search.set(key, String(value));
|
|
16
|
+
}
|
|
17
|
+
const qs = search.toString();
|
|
18
|
+
if (qs)
|
|
19
|
+
url += `?${qs}`;
|
|
20
|
+
}
|
|
21
|
+
const response = await doFetch(url);
|
|
22
|
+
if (!response.ok)
|
|
23
|
+
throw new Error(`Telescope API ${path} failed: ${response.status}`);
|
|
24
|
+
return (await response.json());
|
|
25
|
+
}
|
|
26
|
+
async function post(path, params, body) {
|
|
27
|
+
let url = `${baseUrl}${path}`;
|
|
28
|
+
if (params) {
|
|
29
|
+
const search = new URLSearchParams();
|
|
30
|
+
for (const [key, value] of Object.entries(params)) {
|
|
31
|
+
if (value !== undefined)
|
|
32
|
+
search.set(key, String(value));
|
|
33
|
+
}
|
|
34
|
+
const qs = search.toString();
|
|
35
|
+
if (qs)
|
|
36
|
+
url += `?${qs}`;
|
|
37
|
+
}
|
|
38
|
+
const response = await doFetch(url, {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
headers: { 'Content-Type': 'application/json' },
|
|
41
|
+
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
42
|
+
});
|
|
43
|
+
if (!response.ok)
|
|
44
|
+
throw new Error(`Telescope API ${path} failed: ${response.status}`);
|
|
45
|
+
return (await response.json());
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
entries: (query = {}) => get('/entries', {
|
|
49
|
+
type: query.type,
|
|
50
|
+
tag: query.tag,
|
|
51
|
+
traceId: query.traceId,
|
|
52
|
+
batchId: query.batchId,
|
|
53
|
+
familyHash: query.familyHash,
|
|
54
|
+
search: query.search,
|
|
55
|
+
cursor: query.cursor,
|
|
56
|
+
limit: query.limit,
|
|
57
|
+
}),
|
|
58
|
+
entry: (id) => get(`/entries/${encodeURIComponent(id)}`),
|
|
59
|
+
pulse: (window) => get('/pulse', { window }),
|
|
60
|
+
queues: (window) => get('/queues', { window }),
|
|
61
|
+
timeseries: (query = {}) => get('/timeseries', {
|
|
62
|
+
window: query.window,
|
|
63
|
+
buckets: query.buckets,
|
|
64
|
+
type: query.type,
|
|
65
|
+
tag: query.tag,
|
|
66
|
+
}),
|
|
67
|
+
traces: (window, limit) => get('/traces', { window, limit }),
|
|
68
|
+
stats: (type, window) => get('/stats', { type, window }),
|
|
69
|
+
tags: (prefix) => get('/tags', { prefix }),
|
|
70
|
+
meta: () => get('/meta'),
|
|
71
|
+
serverStats: () => get('/server-stats'),
|
|
72
|
+
health: () => get('/health'),
|
|
73
|
+
liveQueues: () => get('/queues/live'),
|
|
74
|
+
schedulesLive: () => get('/schedules/live'),
|
|
75
|
+
queueCounts: (driver, queue) => get(`/queues/live/${encodeURIComponent(driver)}/${encodeURIComponent(queue)}/counts`),
|
|
76
|
+
queueJobs: (driver, queue, state, page = {}) => get(`/queues/live/${encodeURIComponent(driver)}/${encodeURIComponent(queue)}/jobs`, {
|
|
77
|
+
state,
|
|
78
|
+
cursor: page.cursor,
|
|
79
|
+
limit: page.limit,
|
|
80
|
+
}),
|
|
81
|
+
queueJob: (driver, queue, id) => get(`/queues/live/${encodeURIComponent(driver)}/${encodeURIComponent(queue)}/jobs/${encodeURIComponent(id)}`),
|
|
82
|
+
queueJobAction: (driver, queue, id, action) => post(`/queues/live/${encodeURIComponent(driver)}/${encodeURIComponent(queue)}/jobs/${encodeURIComponent(id)}/${encodeURIComponent(action)}`),
|
|
83
|
+
queueAction: (driver, queue, action, opts = {}) => post(`/queues/live/${encodeURIComponent(driver)}/${encodeURIComponent(queue)}/actions/${encodeURIComponent(action)}`, { state: opts.state }),
|
|
84
|
+
queueEnqueue: (driver, queue, body) => post(`/queues/live/${encodeURIComponent(driver)}/${encodeURIComponent(queue)}/enqueue`, undefined, body),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=telescope-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telescope-client.js","sourceRoot":"","sources":["../../src/client/telescope-client.ts"],"names":[],"mappings":"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"}
|