@adonis-agora/durable 0.21.1 → 0.23.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.
Files changed (56) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/assets/spa/assets/index-BxZp72PC.js +67 -0
  3. package/dist/assets/spa/assets/index-BxZp72PC.js.map +1 -0
  4. package/dist/assets/spa/assets/index-CTsYsi6p.css +1 -0
  5. package/dist/assets/spa/index.html +19 -0
  6. package/dist/providers/dashboard_provider.d.ts +51 -52
  7. package/dist/providers/dashboard_provider.d.ts.map +1 -1
  8. package/dist/providers/dashboard_provider.js +234 -121
  9. package/dist/providers/dashboard_provider.js.map +1 -1
  10. package/dist/src/dashboard/attr-filter.d.ts +8 -0
  11. package/dist/src/dashboard/attr-filter.d.ts.map +1 -0
  12. package/dist/src/dashboard/attr-filter.js +35 -0
  13. package/dist/src/dashboard/attr-filter.js.map +1 -0
  14. package/dist/src/dashboard/auth.d.ts +55 -10
  15. package/dist/src/dashboard/auth.d.ts.map +1 -1
  16. package/dist/src/dashboard/auth.js +50 -11
  17. package/dist/src/dashboard/auth.js.map +1 -1
  18. package/dist/src/dashboard/gateway-adapter.d.ts +55 -5
  19. package/dist/src/dashboard/gateway-adapter.d.ts.map +1 -1
  20. package/dist/src/dashboard/gateway-adapter.js +53 -6
  21. package/dist/src/dashboard/gateway-adapter.js.map +1 -1
  22. package/dist/src/dashboard/handlers.d.ts +66 -8
  23. package/dist/src/dashboard/handlers.d.ts.map +1 -1
  24. package/dist/src/dashboard/handlers.js +132 -18
  25. package/dist/src/dashboard/handlers.js.map +1 -1
  26. package/dist/src/dashboard/index.d.ts +4 -4
  27. package/dist/src/dashboard/index.d.ts.map +1 -1
  28. package/dist/src/dashboard/index.js +3 -3
  29. package/dist/src/dashboard/index.js.map +1 -1
  30. package/dist/src/dashboard/spa.d.ts +22 -0
  31. package/dist/src/dashboard/spa.d.ts.map +1 -0
  32. package/dist/src/dashboard/spa.js +43 -0
  33. package/dist/src/dashboard/spa.js.map +1 -0
  34. package/dist/src/engine.d.ts +10 -1
  35. package/dist/src/engine.d.ts.map +1 -1
  36. package/dist/src/engine.js +11 -0
  37. package/dist/src/engine.js.map +1 -1
  38. package/dist/src/index.d.ts +2 -1
  39. package/dist/src/index.d.ts.map +1 -1
  40. package/dist/src/index.js +2 -1
  41. package/dist/src/index.js.map +1 -1
  42. package/dist/src/interfaces.d.ts +15 -0
  43. package/dist/src/interfaces.d.ts.map +1 -1
  44. package/dist/src/run-waiting.d.ts +25 -0
  45. package/dist/src/run-waiting.d.ts.map +1 -0
  46. package/dist/src/run-waiting.js +59 -0
  47. package/dist/src/run-waiting.js.map +1 -0
  48. package/dist/src/telescope/extension.d.ts.map +1 -1
  49. package/dist/src/telescope/extension.js +1 -0
  50. package/dist/src/telescope/extension.js.map +1 -1
  51. package/dist/src/transports/queue-mock-adapter.d.ts +1 -0
  52. package/dist/src/transports/queue-mock-adapter.d.ts.map +1 -1
  53. package/dist/src/transports/queue-mock-adapter.js +1 -0
  54. package/dist/src/transports/queue-mock-adapter.js.map +1 -1
  55. package/dist/stubs/config/durable_dashboard.stub +19 -0
  56. package/package.json +23 -22
@@ -1,34 +1,60 @@
1
- import { SESSION_COOKIE_NAME, performLogin, readSession, } from '../src/dashboard/auth.js';
1
+ import { readFile } from 'node:fs/promises';
2
+ import { basename, resolve as resolvePath } from 'node:path';
3
+ import { PassThrough } from 'node:stream';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { SESSION_COOKIE_NAME, performLogin, performSession, readSession, } from '../src/dashboard/auth.js';
2
6
  import { compat, enumerateLiveFleet, mergeFleets, } from '../src/dashboard/compat.js';
3
7
  import { resolveConfig, } from '../src/dashboard/define_config.js';
4
8
  import { BlockedDiagnosticsRecorder } from '../src/dashboard/diagnostics-recorder.js';
5
9
  import { dashboardEngineForRole } from '../src/dashboard/gateway-adapter.js';
6
- import { cancelRun, getRun, health, listRuns, redispatchPendingRun, retryRun, } from '../src/dashboard/handlers.js';
10
+ import { bulkAction, cancelRun, continueRun, getRun, health, listRuns, redispatchPendingRun, retryRun, retryWithInputRun, topology, workers, } from '../src/dashboard/handlers.js';
7
11
  import { renderDashboard } from '../src/dashboard/html.js';
8
12
  import { renderLoginPage } from '../src/dashboard/login_page.js';
13
+ import { contentTypeFor, renderIndexHtml } from '../src/dashboard/spa.js';
9
14
  import { WorkflowEngine } from '../src/index.js';
10
15
  import { DURABLE_TRANSPORT } from '../src/role_bindings.js';
11
16
  /**
12
- * Mounts the durable dashboard into an AdonisJS app: a JSON API over the
13
- * {@link WorkflowEngine}'s read surface, and a single self-contained HTML page
14
- * that consumes it. All routes sit behind the configurable `authorize` guard from
15
- * `config/durable_dashboard.ts`.
17
+ * Directory of the built SPA (`dist/spa`, copied in by `copy:stubs` — see `package.json`), relative to
18
+ * this compiled provider (`dist/providers`). Resolved lazily (not at module scope) for the same reason
19
+ * `@adonis-agora/media-dashboard`'s provider resolves its SPA directory lazily: `fileURLToPath` throws
20
+ * on any non-`file:` `import.meta.url`, which would make this provider unimportable under a test
21
+ * runner that doesn't use real file URLs.
22
+ */
23
+ let spaDir;
24
+ function spaDirectory() {
25
+ spaDir ??= fileURLToPath(new URL('../assets/spa/', import.meta.url));
26
+ return spaDir;
27
+ }
28
+ /**
29
+ * Mounts the durable dashboard into an AdonisJS app: the `@adonis-agora/durable-dashboard` React SPA
30
+ * (a JSON API over the {@link WorkflowEngine}'s read/control surface, served under a configurable
31
+ * path) plus, for backward compatibility, the original hand-rolled `assets/dashboard.html` at
32
+ * `<path>/legacy`.
16
33
  *
17
34
  * Routes (relative to the configured `path`, default `/durable`):
18
- * - `GET /` → the dashboard HTML
19
- * - `GET /api/runs` → list runs (status/workflow/tag filters, paged)
20
- * - `GET /api/runs/:id` → run detail (run + step timeline + children)
21
- * - `POST /api/runs/:id/retry` → re-enqueue the run
22
- * - `POST /api/runs/:id/redispatch` → re-dispatch a run's lost pending remote steps
23
- * - `POST /api/runs/:id/cancel` → cancel the run
24
- * - `GET /api/health` → worker-group health
35
+ * - `GET /` -> the dashboard SPA's `index.html`
36
+ * - `GET /assets/:file` -> the SPA's hashed JS/CSS bundle
37
+ * - `GET /legacy` -> the original static dashboard (compat fallback)
38
+ * - `GET /api/runs` -> list runs (status/workflow/tag/namespace/attr filters, paged)
39
+ * - `GET /api/runs/:id` -> run detail (run + step timeline + children)
40
+ * - `GET /api/runs/:id/stream` -> SSE live-tail of one run's lifecycle events
41
+ * - `POST /api/runs/:id/retry` -> re-enqueue the run
42
+ * - `POST /api/runs/:id/retry-with-input` -> fix-and-replay: start a corrected linked run
43
+ * - `POST /api/runs/:id/redispatch` -> re-dispatch a run's lost pending remote steps
44
+ * - `POST /api/runs/:id/cancel` -> cancel the run
45
+ * - `POST /api/runs/:id/continue` -> resume a run paused at a breakpoint
46
+ * - `POST /api/bulk/:action` -> bulk retry/cancel every run matching a filter
47
+ * - `GET /api/health` -> worker-group health (compact; legacy HTML)
48
+ * - `GET /api/workers` -> worker-group health (full heartbeats; SPA)
49
+ * - `GET /api/topology` -> this deployment's durable role
50
+ * - `GET /api/compat` -> fleet health / protocol-compatibility panel
25
51
  */
26
52
  export default class DashboardProvider {
27
53
  app;
28
54
  constructor(app) {
29
55
  this.app = app;
30
56
  }
31
- /** Warn once so a throwing `login` hook doesn't spam the logs on every failed attempt. */
57
+ /** Warn once so a throwing `login`/`session` hook doesn't spam the logs on every failed attempt. */
32
58
  warnedOnHookThrow = false;
33
59
  /**
34
60
  * Captures the engine's `capability.unavailable` / `protocol.incompatible` diagnostics events so the
@@ -47,34 +73,15 @@ export default class DashboardProvider {
47
73
  if (!config.enabled)
48
74
  return;
49
75
  const role = this.durableRole();
50
- // Route registration can't happen synchronously in `boot()`: at this point in the AdonisJS
51
- // lifecycle the HTTP server/router binding may not be resolvable yet (bindings from other
52
- // providers' `boot()` methods can still be pending), and — critically — the *documented*
53
- // `@adonisjs/core/services/router` singleton is only assigned once the app's "booted" hooks run
54
- // (`await app.booted(async () => { router = ... })` inside that service module itself), which
55
- // fire strictly AFTER every provider's own `boot()`. A provider that imports that singleton and
56
- // calls `router.get(...)` directly inside `boot()` crashes every entrypoint (serve/ace/tests)
57
- // that registers it, because `router` is still `undefined` at that point.
58
- //
59
- // Deferring to `app.booted(...)` runs our route registration as another "booted" hook — the
60
- // same mechanism the router service uses to become available in the first place — which is
61
- // guaranteed to fire BEFORE `app.start()`'s callback boots the HTTP server and commits the
62
- // router (the last point at which routes can still be added). Resolving `router` fresh from the
63
- // container here (rather than depending on that service singleton) is also the same pattern
64
- // `@adonisjs/core`'s own `AppServiceProvider` uses internally.
76
+ // Route registration can't happen synchronously in `boot()` — see the long-standing note this
77
+ // provider always carried: the router singleton isn't committed until the app's "booted" hooks
78
+ // run, which fire strictly AFTER every provider's own `boot()`.
65
79
  await this.app.booted(async () => {
66
80
  const router = await this.app.container.make('router');
67
81
  this.registerRoutes(router, config, role);
68
82
  await this.attachDiagnostics(role);
69
83
  });
70
84
  }
71
- /**
72
- * Subscribe the {@link diagnostics} recorder to the store-backed engine's lifecycle events so the
73
- * `/compat` panel is fed live (design §10). Store-role only: a `tenant` pod owns no engine (structural
74
- * isolation), so its panel shows blocked runs reason-only. Defensive: if the engine can't be resolved
75
- * (the dashboard mounted without `@adonis-agora/durable`'s provider), the recorder simply stays empty
76
- * rather than crashing boot.
77
- */
78
85
  async attachDiagnostics(role) {
79
86
  if (role === 'tenant')
80
87
  return;
@@ -89,27 +96,17 @@ export default class DashboardProvider {
89
96
  registerRoutes(router, config, role) {
90
97
  const apiBase = `${config.path}/api`;
91
98
  // Resolve the dashboard's read/control port lazily per request, branched by role (design §5/§8):
92
- // a store role resolves the engine (built by @adonis-agora/durable's provider); a store-less `tenant`
99
+ // a store role adapts the engine (built by @adonis-agora/durable's provider); a store-less `tenant`
93
100
  // pod resolves the DURABLE_RUN_GATEWAY token (a ProxyRunGateway) and adapts it — NO engine, NO store.
94
101
  const resolveEngine = () => dashboardEngineForRole(role, this.app.container, WorkflowEngine);
95
102
  const deps = async () => ({ engine: await resolveEngine() });
96
- // Built-in `dashboardAuth` login screen (opt-in). Registered ONLY when configured, so omitting
97
- // `dashboardAuth` leaves route registration byte-for-byte as it was. These endpoints are public
98
- // (behind NEITHER guard): they MINT the session the guard checks for, and the login page must
99
- // stay reachable while unauthenticated — including in production, where the default `authorize`
100
- // hook would otherwise deny it.
103
+ // Built-in `dashboardAuth` (Mode A `session` and/or Mode B `login`, opt-in). Registered ONLY when
104
+ // configured, so omitting `dashboardAuth` leaves route registration byte-for-byte as it was. These
105
+ // endpoints are public (behind NEITHER guard): they MINT the session the guard checks for.
101
106
  if (config.dashboardAuth) {
102
107
  this.registerAuthRoutes(router, config, config.dashboardAuth);
103
108
  }
104
- // The HTML page.
105
- router
106
- .get(config.path === '' ? '/' : config.path, async (ctx) => {
107
- if (!(await this.enforce(config, ctx, 'page')))
108
- return;
109
- ctx.response.header('content-type', 'text/html; charset=utf-8');
110
- return ctx.response.send(renderDashboard(apiBase));
111
- })
112
- .as('durable_dashboard.index');
109
+ this.registerSpaRoutes(router, config, apiBase);
113
110
  // JSON API.
114
111
  const json = (handler) => {
115
112
  return async (ctx) => {
@@ -129,13 +126,46 @@ export default class DashboardProvider {
129
126
  router.get(`${apiBase}/runs`, json(listRuns)).as('durable_dashboard.runs.index');
130
127
  router.get(`${apiBase}/runs/:id`, json(getRun)).as('durable_dashboard.runs.show');
131
128
  router.post(`${apiBase}/runs/:id/retry`, json(retryRun)).as('durable_dashboard.runs.retry');
129
+ router
130
+ .post(`${apiBase}/runs/:id/retry-with-input`, json(retryWithInputRun))
131
+ .as('durable_dashboard.runs.retry_with_input');
132
132
  router
133
133
  .post(`${apiBase}/runs/:id/redispatch`, json(redispatchPendingRun))
134
134
  .as('durable_dashboard.runs.redispatch');
135
135
  router.post(`${apiBase}/runs/:id/cancel`, json(cancelRun)).as('durable_dashboard.runs.cancel');
136
+ router
137
+ .post(`${apiBase}/runs/:id/continue`, json(continueRun))
138
+ .as('durable_dashboard.runs.continue');
139
+ router.post(`${apiBase}/bulk/:action`, json(bulkAction)).as('durable_dashboard.bulk');
136
140
  router
137
141
  .get(`${apiBase}/health`, json((d) => health(d)))
138
142
  .as('durable_dashboard.health');
143
+ router
144
+ .get(`${apiBase}/workers`, json((d) => workers(d)))
145
+ .as('durable_dashboard.workers');
146
+ router
147
+ .get(`${apiBase}/topology`, async (ctx) => {
148
+ if (!(await this.enforce(config, ctx, 'api')))
149
+ return;
150
+ // Only a `TenantConfig` carries a `partition` (its `<name>@<tenant>` routing suffix, spec
151
+ // §6.1) — the field this dashboard's `DurableTopology.tenant` badge actually names.
152
+ const durableConfig = this.app.config.get('durable', {});
153
+ const tenant = durableConfig.role === 'tenant' ? durableConfig.partition : undefined;
154
+ const result = topology(role, tenant);
155
+ return ctx.response.status(result.status).json(result.body);
156
+ })
157
+ .as('durable_dashboard.topology');
158
+ // Live-tail one run's lifecycle events over SSE — needs the raw HTTP response, so it's wired
159
+ // directly here rather than through the JSON `ApiResponse` convention the other routes share.
160
+ router
161
+ .get(`${apiBase}/runs/:id/stream`, async (ctx) => {
162
+ if (!(await this.enforce(config, ctx, 'api')))
163
+ return;
164
+ const id = ctx.params.id;
165
+ const engine = await resolveEngine();
166
+ await this.streamRun(ctx, engine, id);
167
+ })
168
+ .as('durable_dashboard.runs.stream');
139
169
  // Fleet health / protocol-compatibility panel (design §7.6, §10): per queue/group/pod protocol +
140
170
  // negotiated level + red-flag reason on incompatibility, plus blocked runs with their structured delta.
141
171
  router
@@ -155,17 +185,85 @@ export default class DashboardProvider {
155
185
  .as('durable_dashboard.compat');
156
186
  }
157
187
  /**
158
- * Assemble the {@link CompatSource} the `/compat` handler reads (design §10). The fleet view is the LIVE
159
- * green fleet enumerated off the transport (every advertising worker — compatible OR incompatible, even
160
- * one that never triggered a blocked dispatch) merged with the descriptors the {@link diagnostics}
161
- * recorder reconstructed from past blocks; the live descriptors win per instance (fresher). Captured
162
- * diagnostics + blocked runs stay as they were.
163
- *
164
- * The live enumeration is snapshotted ONCE per request (an `async` fetch off the transport), then handed
165
- * to the handler as a synchronous `fleet()` closure — so {@link CompatSource} keeps its cheap sync shape.
166
- * A `tenant` pod (or a transport without the fleet capability) has no {@link DURABLE_TRANSPORT} binding →
167
- * live fleet is empty → the panel degrades to the diagnostics-only view exactly as before.
188
+ * Mount the React SPA at `config.path` (default) plus a `<path>/legacy` fallback serving the
189
+ * original hand-rolled `assets/dashboard.html` — kept for any bookmark/integration built against it,
190
+ * but no longer the default (per the SPA rollout, the built React console is now `GET <path>`).
191
+ */
192
+ registerSpaRoutes(router, config, apiBase) {
193
+ const indexPath = config.path === '' ? '/' : config.path;
194
+ router
195
+ .get(indexPath, async (ctx) => {
196
+ if (!(await this.enforce(config, ctx, 'page')))
197
+ return;
198
+ let html;
199
+ try {
200
+ html = await readFile(resolvePath(spaDirectory(), 'index.html'), 'utf8');
201
+ }
202
+ catch {
203
+ return ctx.response
204
+ .status(404)
205
+ .send('The durable dashboard SPA is not built. Run `pnpm --filter @adonis-agora/durable-dashboard build` (or the package build) to emit dist/spa.');
206
+ }
207
+ return ctx.response
208
+ .header('content-type', 'text/html; charset=utf-8')
209
+ .header('cache-control', 'no-store, must-revalidate')
210
+ .send(renderIndexHtml(html, config.path, apiBase));
211
+ })
212
+ .as('durable_dashboard.index');
213
+ router
214
+ .get(`${config.path}/assets/:file`, async (ctx) => {
215
+ if (!(await this.enforce(config, ctx, 'page')))
216
+ return;
217
+ const file = basename(String(ctx.params.file));
218
+ const root = resolvePath(spaDirectory(), 'assets');
219
+ const assetPath = resolvePath(root, file);
220
+ if (!assetPath.startsWith(root))
221
+ return ctx.response.status(404).send('');
222
+ let bytes;
223
+ try {
224
+ bytes = await readFile(assetPath);
225
+ }
226
+ catch {
227
+ return ctx.response.status(404).send('');
228
+ }
229
+ return ctx.response
230
+ .header('content-type', contentTypeFor(file))
231
+ .header('cache-control', 'public, max-age=31536000, immutable')
232
+ .send(bytes);
233
+ })
234
+ .as('durable_dashboard.assets');
235
+ router
236
+ .get(`${config.path}/legacy`, async (ctx) => {
237
+ if (!(await this.enforce(config, ctx, 'page')))
238
+ return;
239
+ ctx.response.header('content-type', 'text/html; charset=utf-8');
240
+ return ctx.response.send(renderDashboard(apiBase));
241
+ })
242
+ .as('durable_dashboard.legacy');
243
+ }
244
+ /**
245
+ * Stream one run's lifecycle events as `text/event-stream`, via AdonisJS's `response.stream()` (a
246
+ * managed pipe with graceful error handling — no raw `ServerResponse` writes). The stream stays
247
+ * open until the client disconnects, at which point the engine subscription is torn down and the
248
+ * stream ended, mirroring the NestJS sibling's `@Sse` route (`gateway.subscribe` unsubscribed on
249
+ * teardown).
168
250
  */
251
+ async streamRun(ctx, engine, runId) {
252
+ ctx.response.header('content-type', 'text/event-stream');
253
+ ctx.response.header('cache-control', 'no-cache');
254
+ ctx.response.header('connection', 'keep-alive');
255
+ ctx.response.header('x-accel-buffering', 'no');
256
+ const stream = new PassThrough();
257
+ const unsubscribe = engine.subscribe(runId, (event) => {
258
+ stream.write(`data: ${JSON.stringify(event)}\n\n`);
259
+ });
260
+ const cleanup = () => {
261
+ unsubscribe();
262
+ stream.end();
263
+ };
264
+ ctx.request.request.on('close', cleanup);
265
+ ctx.response.stream(stream);
266
+ }
169
267
  async compatSource(engine) {
170
268
  const live = await enumerateLiveFleet(await this.fleetTransport());
171
269
  const fleet = mergeFleets(this.diagnostics.fleet(), live);
@@ -176,9 +274,6 @@ export default class DashboardProvider {
176
274
  diagnosticsFor: (runId) => this.diagnostics.diagnosticsFor(runId),
177
275
  };
178
276
  }
179
- /** Resolve the store role's task transport (published under {@link DURABLE_TRANSPORT}) for LIVE fleet
180
- * enumeration, or `null` when unavailable — a `tenant` pod never binds it, and a mis-mounted dashboard
181
- * degrades cleanly to the diagnostics-only view rather than crashing the panel. */
182
277
  async fleetTransport() {
183
278
  try {
184
279
  return (await this.app.container.make(DURABLE_TRANSPORT));
@@ -188,64 +283,84 @@ export default class DashboardProvider {
188
283
  }
189
284
  }
190
285
  /**
191
- * Mount the built-in `dashboardAuth` endpoints under `basePath`. All three are public (no guard):
192
- * they create/destroy the session the {@link enforce} guard checks for.
286
+ * Mount the built-in `dashboardAuth` endpoints under `basePath`. All are public (no guard): they
287
+ * create/destroy the session the {@link enforce} guard checks for.
193
288
  *
194
- * - `GET <base>/login` → the server-rendered login page (never varies per request beyond the
195
- * developer-controlled `basePath`; `returnTo`/`error` are read client-side from the query).
196
- * - `POST <base>/login` → verifies credentials via the host `login` hook and mints the cookie.
197
- * Called by the login page's own `fetch` (JSON body). Uniform `401` on ANY failure — unknown
198
- * user, wrong password, or a throwing hook — so there is no user-enumeration.
199
- * - `GET <base>/logout` → clears the cookie and redirects back to the login page. A plain `GET`
200
- * (idempotent, only ever destroys the caller's own session) so a simple `<a href>` works.
289
+ * - `GET <base>/login` -> the server-rendered login page (Mode B only).
290
+ * - `POST <base>/login` -> verifies credentials via the host `login` hook and mints the cookie.
291
+ * - `POST <base>/session` -> Mode A: verifies the HOST APP's own auth (off the raw request) via the
292
+ * `session` hook and mints the cookie. No body — the hook reads whatever it needs off `request`.
293
+ * `204` on success, uniform `401` on denial. Mirrors `@dudousxd/nestjs-durable-dashboard`'s
294
+ * `DurableAuthController.session` and `client/console-session.ts`'s `mintDurableConsoleSession`.
295
+ * - `GET <base>/logout` -> clears the cookie and redirects to the login page (Mode B) or the
296
+ * dashboard root (Mode A only). A plain `GET` (idempotent, only ever destroys the caller's own
297
+ * session) so a simple `<a href>` works.
201
298
  */
202
299
  registerAuthRoutes(router, config, auth) {
203
300
  const loginPath = `${config.path}/login`;
301
+ const sessionPath = `${config.path}/session`;
204
302
  const logoutPath = `${config.path}/logout`;
205
- router
206
- .get(loginPath, async (ctx) => {
207
- ctx.response.header('content-type', 'text/html; charset=utf-8');
208
- ctx.response.header('cache-control', 'no-store, must-revalidate');
209
- return ctx.response.send(renderLoginPage(config.path));
210
- })
211
- .as('durable_dashboard.login.page');
212
- router
213
- .post(loginPath, async (ctx) => {
214
- const outcome = await performLogin(auth, ctx.request.body(), config.path);
215
- if (outcome.kind === 'bad-request') {
216
- return ctx.response.status(400).json({ error: outcome.message });
217
- }
218
- if (outcome.kind === 'unauthorized') {
219
- // A throwing hook is a denial (never a 500), warn-logged once so a buggy hook doesn't
220
- // flood the logs. The client sees the same uniform 401 either way.
221
- if (outcome.hookError !== undefined && !this.warnedOnHookThrow) {
222
- this.warnedOnHookThrow = true;
223
- const message = outcome.hookError instanceof Error
224
- ? outcome.hookError.message
225
- : String(outcome.hookError);
226
- const logger = await this.app.container.make('logger');
227
- logger.warn(`dashboardAuth login hook threw; treating as denial. ${message}`);
303
+ if (auth.login) {
304
+ router
305
+ .get(loginPath, async (ctx) => {
306
+ ctx.response.header('content-type', 'text/html; charset=utf-8');
307
+ ctx.response.header('cache-control', 'no-store, must-revalidate');
308
+ return ctx.response.send(renderLoginPage(config.path));
309
+ })
310
+ .as('durable_dashboard.login.page');
311
+ router
312
+ .post(loginPath, async (ctx) => {
313
+ const outcome = await performLogin(auth, ctx.request.body(), config.path);
314
+ if (outcome.kind === 'bad-request') {
315
+ return ctx.response.status(400).json({ error: outcome.message });
228
316
  }
229
- return ctx.response.status(401).json({ error: outcome.message });
230
- }
231
- this.writeSessionCookie(ctx, auth, outcome.cookieValue);
232
- return ctx.response.status(200).json({ redirectTo: outcome.redirectTo });
233
- })
234
- .as('durable_dashboard.login.submit');
317
+ if (outcome.kind === 'unauthorized') {
318
+ this.warnHookThrow(outcome.hookError);
319
+ return ctx.response.status(401).json({ error: outcome.message });
320
+ }
321
+ this.writeSessionCookie(ctx, auth, outcome.cookieValue);
322
+ return ctx.response.status(200).json({ redirectTo: outcome.redirectTo });
323
+ })
324
+ .as('durable_dashboard.login.submit');
325
+ }
326
+ if (auth.session) {
327
+ router
328
+ .post(sessionPath, async (ctx) => {
329
+ const outcome = await performSession(auth, ctx.request.request);
330
+ if (outcome.kind === 'unauthorized') {
331
+ this.warnHookThrow(outcome.hookError);
332
+ return ctx.response.status(401).json({ error: 'unauthorized' });
333
+ }
334
+ this.writeSessionCookie(ctx, auth, outcome.cookieValue);
335
+ return ctx.response.status(204).send('');
336
+ })
337
+ .as('durable_dashboard.session.mint');
338
+ }
235
339
  router
236
340
  .get(logoutPath, async (ctx) => {
237
341
  ctx.response.clearCookie(SESSION_COOKIE_NAME, { path: '/' });
238
- return ctx.response.redirect().toPath(loginPath);
342
+ return ctx.response.redirect().toPath(auth.login ? loginPath : config.path || '/');
239
343
  })
240
344
  .as('durable_dashboard.logout');
241
345
  }
346
+ async warnHookThrow(hookError) {
347
+ if (hookError === undefined || this.warnedOnHookThrow)
348
+ return;
349
+ this.warnedOnHookThrow = true;
350
+ const message = hookError instanceof Error ? hookError.message : String(hookError);
351
+ const logger = await this.app.container.make('logger');
352
+ logger.warn(`dashboardAuth login/session hook threw; treating as denial. ${message}`);
353
+ }
242
354
  /**
243
355
  * Run the guards for a dashboard resource. Composes the existing `authorize` hook (bearer
244
356
  * token/custom) with the optional `dashboardAuth` session guard — BOTH must pass:
245
357
  *
246
- * 1. `authorize` fails → `403` (unchanged behavior).
247
- * 2. `dashboardAuth` configured AND no valid session → for a `page` request, redirect `302` to
248
- * the login page carrying a sanitized `returnTo`; for an `api` request, a plain `401`.
358
+ * 1. `authorize` fails -> `403` (unchanged behavior).
359
+ * 2. `dashboardAuth` configured AND no valid session -> for a `page` request, redirect `302` to
360
+ * the login page (Mode B) carrying a sanitized `returnTo`, or a `401` "session required" notice
361
+ * (Mode A only, no login page to redirect to); for an `api` request, a plain
362
+ * `401 { error: 'unauthorized', auth: { modes } }` — the `modes` list lets the SPA's client
363
+ * (`durable-client.ts`) decide whether to offer the login page.
249
364
  *
250
365
  * When `dashboardAuth` is unconfigured only step 1 runs, so behavior is byte-for-byte unchanged.
251
366
  * Returns `false` (and has already written the response) when the request must short-circuit.
@@ -253,10 +368,6 @@ export default class DashboardProvider {
253
368
  async enforce(config, ctx, mode) {
254
369
  const allowed = await config.authorize(ctx);
255
370
  if (!allowed) {
256
- // A custom hook may have WRITTEN its own denial before returning false — typically a redirect
257
- // to the host app's login page (`ctx.response.redirect('/login')`), the natural UX when the
258
- // dashboard is guarded by the app's own session instead of a bearer token. Respect it: only
259
- // fall back to the uniform 403 when the hook left the response untouched (no Location header).
260
371
  if (!ctx.response.getHeader('location')) {
261
372
  ctx.response.status(403).json({ error: 'forbidden' });
262
373
  }
@@ -269,24 +380,27 @@ export default class DashboardProvider {
269
380
  if (session)
270
381
  return true;
271
382
  if (mode === 'page') {
272
- const returnTo = ctx.request.url(true);
273
- ctx.response.redirect().withQs('returnTo', returnTo).toPath(`${config.path}/login`);
383
+ if (auth.login) {
384
+ const returnTo = ctx.request.url(true);
385
+ ctx.response.redirect().withQs('returnTo', returnTo).toPath(`${config.path}/login`);
386
+ return false;
387
+ }
388
+ // Mode A only: there's no login page to send the browser to — this deployment expects the
389
+ // host app to mint a session via `POST <path>/session` before ever navigating here.
390
+ ctx.response
391
+ .status(401)
392
+ .header('content-type', 'text/html; charset=utf-8')
393
+ .header('cache-control', 'no-store, must-revalidate')
394
+ .send('<!doctype html><html><body style="font-family:ui-monospace,monospace;background:#09090b;color:#e7e7ea;display:flex;align-items:center;justify-content:center;height:100vh;margin:0"><p>Open this console from your application.</p></body></html>');
274
395
  return false;
275
396
  }
276
- ctx.response.status(401).json({ error: 'unauthorized' });
397
+ ctx.response.status(401).json({ error: 'unauthorized', auth: { modes: auth.modes } });
277
398
  return false;
278
399
  }
279
- /** Read the raw (unencoded) session cookie value, or `undefined` when absent. */
280
400
  readSessionCookie(ctx) {
281
401
  const value = ctx.request.plainCookie(SESSION_COOKIE_NAME, undefined, false);
282
402
  return typeof value === 'string' && value !== '' ? value : undefined;
283
403
  }
284
- /**
285
- * Write the signed session as an unsigned (`plainCookie`, `encode: false`) cookie — the value
286
- * carries its own HMAC signature, so AdonisJS's cookie signing would be redundant double-wrapping.
287
- * `HttpOnly` + `SameSite=Lax` (blocks cross-site POSTs carrying the cookie — CSRF coverage) +
288
- * `Secure` on https. `Path=/` so it reaches both the UI and API mounts regardless of prefix.
289
- */
290
404
  writeSessionCookie(ctx, auth, value) {
291
405
  ctx.response.plainCookie(SESSION_COOKIE_NAME, value, {
292
406
  httpOnly: true,
@@ -297,7 +411,6 @@ export default class DashboardProvider {
297
411
  encode: false,
298
412
  });
299
413
  }
300
- /** Detach the diagnostics subscription so the recorder stops listening on a clean shutdown/redeploy. */
301
414
  async shutdown() {
302
415
  this.detachDiagnostics?.();
303
416
  this.detachDiagnostics = null;
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard_provider.js","sourceRoot":"","sources":["../../providers/dashboard_provider.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,mBAAmB,EACnB,YAAY,EACZ,WAAW,GACZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAGL,MAAM,EACN,kBAAkB,EAClB,WAAW,GACZ,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAGL,aAAa,GACd,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,0BAA0B,EAAE,MAAM,0CAA0C,CAAC;AACtF,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAKL,SAAS,EACT,MAAM,EACN,MAAM,EACN,QAAQ,EACR,oBAAoB,EACpB,QAAQ,GACT,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEjE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,OAAO,OAAO,iBAAiB;IACd;IAAtB,YAAsB,GAAuB;QAAvB,QAAG,GAAH,GAAG,CAAoB;IAAG,CAAC;IAEjD,0FAA0F;IAClF,iBAAiB,GAAG,KAAK,CAAC;IAElC;;;;OAIG;IACc,WAAW,GAAG,IAAI,0BAA0B,EAAE,CAAC;IAChE,oFAAoF;IAC5E,iBAAiB,GAAwB,IAAI,CAAC;IAEtD,oGAAoG;IAC5F,WAAW;QACjB,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAgB,SAAS,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,YAAY,CAAC;IAChF,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,MAAM,GAAG,aAAa,CAC1B,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAyB,mBAAmB,EAAE,EAAE,CAAC,CACrE,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,OAAO;QAE5B,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAEhC,2FAA2F;QAC3F,0FAA0F;QAC1F,yFAAyF;QACzF,gGAAgG;QAChG,8FAA8F;QAC9F,gGAAgG;QAChG,8FAA8F;QAC9F,0EAA0E;QAC1E,EAAE;QACF,4FAA4F;QAC5F,2FAA2F;QAC3F,2FAA2F;QAC3F,gGAAgG;QAChG,4FAA4F;QAC5F,+DAA+D;QAC/D,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE;YAC/B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvD,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;YAC1C,MAAM,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,iBAAiB,CAAC,IAA+C;QAC7E,IAAI,IAAI,KAAK,QAAQ;YAAE,OAAO;QAC9B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAC7D,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACP,4FAA4F;QAC9F,CAAC;IACH,CAAC;IAEO,cAAc,CACpB,MAAyB,EACzB,MAAsC,EACtC,IAA+C;QAE/C,MAAM,OAAO,GAAG,GAAG,MAAM,CAAC,IAAI,MAAM,CAAC;QAErC,iGAAiG;QACjG,sGAAsG;QACtG,sGAAsG;QACtG,MAAM,aAAa,GAAG,GAA6B,EAAE,CACnD,sBAAsB,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;QACnE,MAAM,IAAI,GAAG,KAAK,IAAmB,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,aAAa,EAAE,EAAE,CAAC,CAAC;QAE5E,+FAA+F;QAC/F,gGAAgG;QAChG,8FAA8F;QAC9F,gGAAgG;QAChG,gCAAgC;QAChC,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;YACzB,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;QAChE,CAAC;QAED,iBAAiB;QACjB,MAAM;aACH,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACzD,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;gBAAE,OAAO;YACvD,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,cAAc,EAAE,0BAA0B,CAAC,CAAC;YAChE,OAAO,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;QACrD,CAAC,CAAC;aACD,EAAE,CAAC,yBAAyB,CAAC,CAAC;QAEjC,YAAY;QACZ,MAAM,IAAI,GAAG,CAAC,OAA2D,EAAE,EAAE;YAC3E,OAAO,KAAK,EAAE,GAAgB,EAAE,EAAE;gBAChC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;oBAAE,OAAO;gBACtD,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;oBAC9D,OAAO,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBAC9D,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,GAAG,CAAC,QAAQ;yBAChB,MAAM,CAAC,GAAG,CAAC;yBACX,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC,CAAC;gBAChF,CAAC;YACH,CAAC,CAAC;QACJ,CAAC,CAAC;QAEF,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,8BAA8B,CAAC,CAAC;QACjF,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,6BAA6B,CAAC,CAAC;QAClF,MAAM,CAAC,IAAI,CAAC,GAAG,OAAO,iBAAiB,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,8BAA8B,CAAC,CAAC;QAC5F,MAAM;aACH,IAAI,CAAC,GAAG,OAAO,sBAAsB,EAAE,IAAI,CAAC,oBAAoB,CAAC,CAAC;aAClE,EAAE,CAAC,mCAAmC,CAAC,CAAC;QAC3C,MAAM,CAAC,IAAI,CAAC,GAAG,OAAO,kBAAkB,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,+BAA+B,CAAC,CAAC;QAC/F,MAAM;aACH,GAAG,CACF,GAAG,OAAO,SAAS,EACnB,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CACvB;aACA,EAAE,CAAC,0BAA0B,CAAC,CAAC;QAElC,iGAAiG;QACjG,wGAAwG;QACxG,MAAM;aACH,GAAG,CAAC,GAAG,OAAO,SAAS,EAAE,KAAK,EAAE,GAAgB,EAAE,EAAE;YACnD,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;gBAAE,OAAO;YACtD,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,aAAa,EAAE,CAAC,CAAC,CAAC;gBAC5E,OAAO,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC9D,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,GAAG,CAAC,QAAQ;qBAChB,MAAM,CAAC,GAAG,CAAC;qBACX,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC,CAAC;YAChF,CAAC;QACH,CAAC,CAAC;aACD,EAAE,CAAC,0BAA0B,CAAC,CAAC;IACpC,CAAC;IAED;;;;;;;;;;;OAWG;IACK,KAAK,CAAC,YAAY,CAAC,MAAuB;QAChD,MAAM,IAAI,GAAG,MAAM,kBAAkB,CAAC,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;QACnE,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC;QAC1D,OAAO;YACL,sBAAsB,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,sBAAsB,EAAE;YACvE,KAAK,EAAE,GAAG,EAAE,CAAC,KAAK;YAClB,WAAW,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;YAC7D,cAAc,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,KAAK,CAAC;SAClE,CAAC;IACJ,CAAC;IAED;;wFAEoF;IAC5E,KAAK,CAAC,cAAc;QAC1B,IAAI,CAAC;YACH,OAAO,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAmB,CAAC;QAC9E,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;;;;;;;;;OAWG;IACK,kBAAkB,CACxB,MAAyB,EACzB,MAAsC,EACtC,IAA2B;QAE3B,MAAM,SAAS,GAAG,GAAG,MAAM,CAAC,IAAI,QAAQ,CAAC;QACzC,MAAM,UAAU,GAAG,GAAG,MAAM,CAAC,IAAI,SAAS,CAAC;QAE3C,MAAM;aACH,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAC5B,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,cAAc,EAAE,0BAA0B,CAAC,CAAC;YAChE,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,eAAe,EAAE,2BAA2B,CAAC,CAAC;YAClE,OAAO,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QACzD,CAAC,CAAC;aACD,EAAE,CAAC,8BAA8B,CAAC,CAAC;QAEtC,MAAM;aACH,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAC7B,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;YAC1E,IAAI,OAAO,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;gBACnC,OAAO,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;YACnE,CAAC;YACD,IAAI,OAAO,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;gBACpC,sFAAsF;gBACtF,mEAAmE;gBACnE,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC;oBAC/D,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;oBAC9B,MAAM,OAAO,GACX,OAAO,CAAC,SAAS,YAAY,KAAK;wBAChC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO;wBAC3B,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;oBAChC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;oBACvD,MAAM,CAAC,IAAI,CAAC,uDAAuD,OAAO,EAAE,CAAC,CAAC;gBAChF,CAAC;gBACD,OAAO,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;YACnE,CAAC;YACD,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;YACxD,OAAO,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;QAC3E,CAAC,CAAC;aACD,EAAE,CAAC,gCAAgC,CAAC,CAAC;QAExC,MAAM;aACH,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAC7B,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,mBAAmB,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;YAC7D,OAAO,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACnD,CAAC,CAAC;aACD,EAAE,CAAC,0BAA0B,CAAC,CAAC;IACpC,CAAC;IAED;;;;;;;;;;OAUG;IACK,KAAK,CAAC,OAAO,CACnB,MAAsC,EACtC,GAAgB,EAChB,IAAoB;QAEpB,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAC5C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,8FAA8F;YAC9F,4FAA4F;YAC5F,4FAA4F;YAC5F,+FAA+F;YAC/F,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC;gBACxC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;YACxD,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,CAAC,aAAa,CAAC;QAClC,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QAEvB,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/D,IAAI,OAAO;YAAE,OAAO,IAAI,CAAC;QAEzB,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACpB,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACvC,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,QAAQ,CAAC,CAAC;YACpF,OAAO,KAAK,CAAC;QACf,CAAC;QACD,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;QACzD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,iFAAiF;IACzE,iBAAiB,CAAC,GAAgB;QACxC,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,mBAAmB,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;QAC7E,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IACvE,CAAC;IAED;;;;;OAKG;IACK,kBAAkB,CAAC,GAAgB,EAAE,IAA2B,EAAE,KAAa;QACrF,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,mBAAmB,EAAE,KAAK,EAAE;YACnD,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,KAAK;YACf,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE;YAC5B,IAAI,EAAE,GAAG;YACT,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YACrC,MAAM,EAAE,KAAK;SACd,CAAC,CAAC;IACL,CAAC;IAED,wGAAwG;IACxG,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC,iBAAiB,EAAE,EAAE,CAAC;QAC3B,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAChC,CAAC;CACF;AAED,iFAAiF;AACjF,SAAS,YAAY,CAAC,GAAgB;IACpC,OAAO;QACL,MAAM,EAAE,GAAG,CAAC,MAA4C;QACxD,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,EAAmD;QACxE,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE;KACzB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"dashboard_provider.js","sourceRoot":"","sources":["../../providers/dashboard_provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { AttributeFilter } from '../interfaces.js';
2
+ /**
3
+ * Parse `attr=key:op:value` query params (repeatable, ANDed) into {@link AttributeFilter}s — e.g.
4
+ * `?attr=amount:gte:200&attr=tier:eq:pro`. `op` must be a known operator; a colon in the value is
5
+ * preserved (only the first two colons delimit). Malformed entries are skipped.
6
+ */
7
+ export declare function parseAttrFilters(attr?: string | string[]): AttributeFilter[] | undefined;
8
+ //# sourceMappingURL=attr-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attr-filter.d.ts","sourceRoot":"","sources":["../../../src/dashboard/attr-filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAe,MAAM,kBAAkB,CAAC;AAkBrE;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,eAAe,EAAE,GAAG,SAAS,CAUxF"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Ported from `@dudousxd/nestjs-durable-dashboard`'s `attr-filter.ts` — parses the same
3
+ * `attr=key:op:value` query convention so the dashboard's search-attribute filtering behaves
4
+ * identically across both ecosystems.
5
+ */
6
+ const ATTR_OPS = new Set(['eq', 'ne', 'gt', 'gte', 'lt', 'lte']);
7
+ /** Coerce a query-string value: `true`/`false` -> boolean, numeric -> number, else the raw string. */
8
+ function coerce(v) {
9
+ if (v === 'true')
10
+ return true;
11
+ if (v === 'false')
12
+ return false;
13
+ if (v !== '' && !Number.isNaN(Number(v)))
14
+ return Number(v);
15
+ return v;
16
+ }
17
+ /**
18
+ * Parse `attr=key:op:value` query params (repeatable, ANDed) into {@link AttributeFilter}s — e.g.
19
+ * `?attr=amount:gte:200&attr=tier:eq:pro`. `op` must be a known operator; a colon in the value is
20
+ * preserved (only the first two colons delimit). Malformed entries are skipped.
21
+ */
22
+ export function parseAttrFilters(attr) {
23
+ if (!attr)
24
+ return undefined;
25
+ const raw = Array.isArray(attr) ? attr : [attr];
26
+ const filters = [];
27
+ for (const entry of raw) {
28
+ const [key, op, ...rest] = entry.split(':');
29
+ if (!key || !ATTR_OPS.has(op) || rest.length === 0)
30
+ continue;
31
+ filters.push({ key, op: op, value: coerce(rest.join(':')) });
32
+ }
33
+ return filters.length ? filters : undefined;
34
+ }
35
+ //# sourceMappingURL=attr-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attr-filter.js","sourceRoot":"","sources":["../../../src/dashboard/attr-filter.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAc,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;AAE9E,sGAAsG;AACtG,SAAS,MAAM,CAAC,CAAS;IACvB,IAAI,CAAC,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IAC9B,IAAI,CAAC,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IAC3D,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAwB;IACvD,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,OAAO,GAAsB,EAAE,CAAC;IACtC,KAAK,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;QACxB,MAAM,CAAC,GAAG,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5C,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAiB,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAC5E,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,EAAiB,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9C,CAAC"}