@dunx/dashboard 2.5.0 → 3.0.1
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/README.md +46 -209
- package/dist/api/bounded.d.ts +6 -10
- package/dist/api/types.d.ts +1 -1
- package/dist/board.d.ts +3 -6
- package/dist/{chunk-xps6r8jv.js → chunk-66xm1dzm.js} +1 -1
- package/dist/contracts.d.ts +26 -72
- package/dist/index.js +3 -7
- package/dist/middleware.d.ts +8 -19
- package/dist/module.d.ts +5 -10
- package/dist/options.d.ts +29 -83
- package/dist/ui.d.ts +6 -12
- package/dist/ui.js +2 -2
- package/package.json +3 -3
package/dist/options.d.ts
CHANGED
|
@@ -1,131 +1,80 @@
|
|
|
1
1
|
import type { BunRequest } from 'bun';
|
|
2
2
|
import type { ConfigValues, DashboardProbe, QueueSource, RedisProbe } from './contracts.js';
|
|
3
3
|
/**
|
|
4
|
-
* Decides whether a request may see the dashboard at all.
|
|
4
|
+
* Decides whether a request may see the dashboard at all. It receives the raw
|
|
5
|
+
* `Request`: the middleware must be registered ahead of any session guard, so
|
|
6
|
+
* there is nothing upstream to have written a context and this has to ask the
|
|
7
|
+
* auth library itself.
|
|
5
8
|
*
|
|
6
|
-
*
|
|
7
|
-
* wrote, and that is load bearing rather than incidental. The dashboard middleware
|
|
8
|
-
* has to be registered ahead of any session guard - measured in `dunx-template`,
|
|
9
|
-
* where a guard running first answered every dashboard request `401` before
|
|
10
|
-
* `authorize` was reached, which defeats the 404 contract below entirely. Running
|
|
11
|
-
* first means there is nothing upstream to have written a context, so this has to
|
|
12
|
-
* be able to ask the auth library itself.
|
|
13
|
-
*
|
|
14
|
-
* A rejected request gets **404, not 403**. A dashboard that announces itself to
|
|
15
|
-
* an unauthenticated caller has told them where to keep knocking.
|
|
9
|
+
* A rejected request gets 404, not 403.
|
|
16
10
|
*/
|
|
17
11
|
export type Authorize = (req: BunRequest) => boolean | Promise<boolean>;
|
|
18
12
|
/**
|
|
19
|
-
* Whether a config value may be shown.
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* design left. `ConfigService` holds whatever the app's `validate` returned, which
|
|
23
|
-
* includes every secret it has; a deny-list of the usual suspects - `SECRET`,
|
|
24
|
-
* `PASSWORD`, `TOKEN` - looks careful and leaks the first key nobody thought of.
|
|
25
|
-
* A deny-list that quietly misses one is worse than no config panel at all.
|
|
26
|
-
*
|
|
27
|
-
* So the panel shows **keys and types** by default, which is most of what it was
|
|
28
|
-
* wanted for ("is FEATURE_X actually set here"), and a value appears only when this
|
|
29
|
-
* predicate says so:
|
|
13
|
+
* Whether a config value may be shown. The default reveals nothing: a deny-list
|
|
14
|
+
* of the usual suspects leaks the first key nobody thought of. The panel shows
|
|
15
|
+
* keys and types, and a value appears only when this says so:
|
|
30
16
|
*
|
|
31
17
|
* ```ts
|
|
32
18
|
* reveal: (key) => key.startsWith('PUBLIC_') || key === 'NODE_ENV'
|
|
33
19
|
* ```
|
|
34
20
|
*
|
|
35
|
-
* There is no
|
|
36
|
-
* app, not per click by whoever reached the page.
|
|
21
|
+
* There is no reveal affordance on the page; redaction is decided at boot.
|
|
37
22
|
*/
|
|
38
23
|
export type Reveal = (key: string, value: unknown) => boolean;
|
|
39
24
|
export interface DashboardOptionsInit {
|
|
40
25
|
/**
|
|
41
|
-
* Where the page is mounted
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* **`app.setGlobalPrefix('api')` does not move it.** That prefixes the routes
|
|
46
|
-
* discovered from controllers, and this is not one of those - it is a middleware
|
|
47
|
-
* matching a path, which is the whole reason the dashboard needs no controllers
|
|
48
|
-
* for a table handed over at runtime. An app with a global prefix that wants the
|
|
49
|
-
* page beside its routes writes `path: '/api/_dunx'` here.
|
|
26
|
+
* Where the page is mounted, `/_dunx` by default. `app.setGlobalPrefix('api')`
|
|
27
|
+
* does not move it: that prefixes discovered routes, and this is a middleware
|
|
28
|
+
* matching a path. Write `path: '/api/_dunx'` to put it beside them.
|
|
50
29
|
*/
|
|
51
30
|
readonly path?: string;
|
|
52
31
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* else, so it is stated either way rather than guessed: omitting it logs a
|
|
56
|
-
* warning naming the mount path at boot.
|
|
32
|
+
* No default: leaving it out serves the page to anyone who can reach the port,
|
|
33
|
+
* and logs a warning naming the mount path at boot.
|
|
57
34
|
*/
|
|
58
35
|
readonly authorize?: Authorize;
|
|
59
36
|
/** Shown in the header and the `<title>`. @default 'dunx' */
|
|
60
37
|
readonly title?: string;
|
|
61
|
-
/**
|
|
62
|
-
* `JobPublisher` goes here. Absent means the queues panel says this process has
|
|
63
|
-
* no queue source rather than that it has no queues.
|
|
64
|
-
*/
|
|
38
|
+
/** `JobPublisher` goes here. Absent means the panel reports no queue source. */
|
|
65
39
|
readonly queues?: QueueSource;
|
|
66
|
-
/**
|
|
67
|
-
*
|
|
68
|
-
* **consumes** a queue never publishes to it, so the publisher has never opened
|
|
69
|
-
* it and it would otherwise be invisible on the page that exists to show it.
|
|
70
|
-
*/
|
|
40
|
+
/** Queues beyond the ones the source has opened. A consume-only process never
|
|
41
|
+
* publishes, so its queues would otherwise be invisible. */
|
|
71
42
|
readonly queueNames?: readonly string[];
|
|
72
43
|
/** `RedisConnection` goes here; it drives the Redis panel and one probe. */
|
|
73
44
|
readonly redis?: RedisProbe;
|
|
74
45
|
/** Anything else worth a light: a database, an upstream, a leader lease. */
|
|
75
46
|
readonly probes?: readonly DashboardProbe[];
|
|
76
|
-
/**
|
|
77
|
-
* `ConfigService` goes here, and the panel is absent without it - showing an
|
|
78
|
-
* app's configuration is something the app says yes to, not something this
|
|
79
|
-
* package reaches into the container for.
|
|
80
|
-
*/
|
|
47
|
+
/** `ConfigService` goes here; the panel is absent without it. */
|
|
81
48
|
readonly config?: ConfigValues;
|
|
82
49
|
/** See {@link Reveal}. The default reveals nothing, even with `config` set. */
|
|
83
50
|
readonly reveal?: Reveal;
|
|
84
|
-
/**
|
|
85
|
-
*
|
|
86
|
-
* to the operation that documents it. A string, not a dependency: the two
|
|
87
|
-
* packages describe the same routes for different audiences and a link is free,
|
|
88
|
-
* where importing one into the other is not.
|
|
89
|
-
*/
|
|
51
|
+
/** Where `@dunx/openapi` serves its explorer, so a routes row can link to the
|
|
52
|
+
* operation documenting it. A string rather than a dependency. */
|
|
90
53
|
readonly openApiPath?: string;
|
|
91
54
|
/**
|
|
92
|
-
* How often the live panels re-fetch, in milliseconds. `0` turns polling off
|
|
93
|
-
* leaves the refresh button.
|
|
94
|
-
*
|
|
95
|
-
* Polling rather than a websocket, deliberately: the page is stateless, a gateway
|
|
96
|
-
* would put the dashboard in the app's own upgrade table, and 5 s is well inside
|
|
97
|
-
* what "how many jobs are failing" needs.
|
|
55
|
+
* How often the live panels re-fetch, in milliseconds. `0` turns polling off
|
|
56
|
+
* and leaves the refresh button. Polling rather than a websocket, which would
|
|
57
|
+
* put the dashboard in the app's own upgrade table.
|
|
98
58
|
*
|
|
99
59
|
* @default 5000
|
|
100
60
|
*/
|
|
101
61
|
readonly pollMs?: number;
|
|
102
62
|
/**
|
|
103
|
-
* How long a probe may take before it is reported `unknown`.
|
|
104
|
-
* cost one light, not the page.
|
|
63
|
+
* How long a probe may take before it is reported `unknown`.
|
|
105
64
|
*
|
|
106
65
|
* @default 2000
|
|
107
66
|
*/
|
|
108
67
|
readonly probeTimeoutMs?: number;
|
|
109
68
|
/**
|
|
110
|
-
* Whether the queue board may change anything
|
|
111
|
-
*
|
|
112
|
-
* Passed through to **bull-board's own `readOnlyMode`** rather than enforced
|
|
113
|
-
* here: it already has the switch, and a second implementation would disagree
|
|
114
|
-
* with it the moment bull-board grew an operation dunx had not heard of.
|
|
115
|
-
*
|
|
116
|
-
* The rest of the dashboard is read-only regardless - it reports on the process
|
|
117
|
-
* and never acts on it - so this is entirely about the queues page. `authorize`
|
|
118
|
-
* gates who reaches the mount; this gates what they can do once there.
|
|
69
|
+
* Whether the queue board may change anything, passed through to bull-board's
|
|
70
|
+
* own `readOnlyMode`. The rest of the dashboard is read-only regardless.
|
|
119
71
|
*
|
|
120
72
|
* @default true
|
|
121
73
|
*/
|
|
122
74
|
readonly commands?: boolean;
|
|
123
75
|
}
|
|
124
|
-
/**
|
|
125
|
-
*
|
|
126
|
-
* constructor parameter type that `@dunx/transform` records - the same reason
|
|
127
|
-
* `QueueOptions` and `RedisOptions` are classes.
|
|
128
|
-
*/
|
|
76
|
+
/** A class rather than an interface, so it is a runtime value the transform can
|
|
77
|
+
* record as a constructor parameter type. */
|
|
129
78
|
export declare class DashboardOptions {
|
|
130
79
|
readonly path: string;
|
|
131
80
|
readonly authorize: Authorize | undefined;
|
|
@@ -144,9 +93,6 @@ export declare class DashboardOptions {
|
|
|
144
93
|
}
|
|
145
94
|
/**
|
|
146
95
|
* A leading slash and no trailing one, so `${path}/api/...` is never `//api`.
|
|
147
|
-
*
|
|
148
|
-
* `/` itself is rejected: mounting the dashboard at the root would swallow every
|
|
149
|
-
* unmatched path in the app, and the middleware's whole contract is that anything
|
|
150
|
-
* outside its mount falls through untouched.
|
|
96
|
+
* `/` is rejected: mounting at the root would swallow every unmatched path.
|
|
151
97
|
*/
|
|
152
98
|
export declare const normalizeMount: (path: string) => string;
|
package/dist/ui.d.ts
CHANGED
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
import type { DashboardOptions } from './options.js';
|
|
2
2
|
import { FAVICON, UI } from './ui-bundle.js';
|
|
3
3
|
/**
|
|
4
|
-
* The page, behind its own entrypoint.
|
|
4
|
+
* The page, behind its own entrypoint. `ui-bundle.ts` is the inlined Vite output,
|
|
5
|
+
* reached with `await import('./ui.js')` on the first request for the page, so an
|
|
6
|
+
* app that mounts the module and never opens it pays nothing at boot.
|
|
5
7
|
*
|
|
6
|
-
* `ui-bundle.ts
|
|
7
|
-
*
|
|
8
|
-
* this with `await import('./ui.js')` on the **first request for the page**, so an
|
|
9
|
-
* app that mounts the module and never opens it - a worker process, a service
|
|
10
|
-
* whose dashboard nobody visits this week - pays nothing at boot.
|
|
8
|
+
* `html.ts` must not import `ui-bundle.ts`, or the split silently reverts; it
|
|
9
|
+
* takes the bundle as an argument for that reason.
|
|
11
10
|
*
|
|
12
|
-
* `
|
|
13
|
-
* reverts. It takes the bundle as an argument for exactly that reason.
|
|
14
|
-
*
|
|
15
|
-
* This is `@dunx/dashboard/ui` in the manifest, and it is an entrypoint rather than
|
|
16
|
-
* a plain module so the build emits it as its own file - see
|
|
17
|
-
* `scripts/build-package.ts`, which derives entrypoints from `exports`.
|
|
11
|
+
* `@dunx/dashboard/ui` in the manifest, so the build emits it as its own file.
|
|
18
12
|
*/
|
|
19
13
|
export declare const renderPage: (options: DashboardOptions) => string;
|
|
20
14
|
export { FAVICON, UI };
|