@dudousxd/nestjs-durable-dashboard 0.51.0 → 0.52.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.
@@ -5,7 +5,7 @@ import * as rxjs from 'rxjs';
5
5
  import { Observable } from 'rxjs';
6
6
  import * as _dudousxd_nestjs_filter from '@dudousxd/nestjs-filter';
7
7
  import { FilterRunner } from '@dudousxd/nestjs-filter';
8
- import { DynamicModule, Type, CanActivate, Provider, InjectionToken, OptionalFactoryDependency, ExecutionContext, StreamableFile } from '@nestjs/common';
8
+ import { CanActivate, ExecutionContext, Provider, Logger, DynamicModule, Type, InjectionToken, OptionalFactoryDependency, StreamableFile } from '@nestjs/common';
9
9
 
10
10
  /** The session user the host `login` hook returns to mint a cookie. */
11
11
  interface DashboardSessionUser {
@@ -199,8 +199,12 @@ declare class DashboardService {
199
199
  }) | null>;
200
200
  /**
201
201
  * Deliver a `ctx.webhook()` callback: turn an inbound POST (token + body) into the signal the
202
- * waiting run is parked on. Returns the run result, or `null` if no run waits on that token (a
203
- * stale/duplicate callback) — a safe no-op the controller maps to 404. Control-plane-only.
202
+ * run is (or will be) parked on. A callback that arrives BEFORE the run reaches `wait()` (the url
203
+ * was handed out and the run is still busy or sleeping) is buffered, and `wait()` consumes it, so
204
+ * the result is the run's current status. Returns `null` — mapped to 404 by the controller — for a
205
+ * token that is not a webhook token, whose run doesn't exist or is terminal, or whose wait already
206
+ * resolved (a duplicate): nothing is buffered for those, so a public endpoint can't leave junk
207
+ * rows behind or signal non-webhook tokens. Control-plane-only.
204
208
  */
205
209
  deliverWebhook(token: string, body: unknown): Promise<RunResult | null>;
206
210
  /** Side-effect-free read of a value a run published via `ctx.setEvent` (a live query). Control-plane-only. */
@@ -412,6 +416,68 @@ declare class DurableAuthController {
412
416
  private runHook;
413
417
  }
414
418
 
419
+ /** The env var the token gate reads when no `token` option is given. */
420
+ declare const DURABLE_DASHBOARD_TOKEN_ENV = "DURABLE_DASHBOARD_TOKEN";
421
+ /** DI token carrying the resolved {@link DashboardGate} (one per dashboard mount). */
422
+ declare const DASHBOARD_GATE: unique symbol;
423
+ /**
424
+ * What the module knows about how the console is protected, captured at `forRoot`/`forRootAsync`
425
+ * time. Everything that can change between module definition and boot (env vars, the async
426
+ * `dashboardAuth` factory) is read later, in {@link resolveDashboardGate}.
427
+ */
428
+ interface DashboardGateOptions {
429
+ /** Host `guards` were passed (non-empty). */
430
+ hasGuards: boolean;
431
+ /** `allowUnauthenticated` as passed. */
432
+ allowUnauthenticated?: boolean;
433
+ /** `token` as passed (else `DURABLE_DASHBOARD_TOKEN` is read at boot). */
434
+ token?: string;
435
+ }
436
+ /**
437
+ * How the gate treats every console request:
438
+ *
439
+ * - `delegated`: `guards` or `dashboardAuth` protect the console; the gate passes everything.
440
+ * - `open`: `allowUnauthenticated: true`; the gate passes everything (warned at boot).
441
+ * - `token`: a request must carry the token (`Authorization: Bearer`, `x-durable-token`, or
442
+ * `?token=` on GET/HEAD).
443
+ * - `unconfigured`: nothing was configured outside production; the gate passes everything
444
+ * (warned at boot, because production will deny).
445
+ * - `deny`: nothing was configured and `NODE_ENV=production`; every request is refused.
446
+ */
447
+ type DashboardGateMode = 'delegated' | 'open' | 'token' | 'unconfigured' | 'deny';
448
+ interface DashboardGate {
449
+ mode: DashboardGateMode;
450
+ /** Set only when `mode === 'token'`. */
451
+ token?: string;
452
+ }
453
+ /**
454
+ * Decide the gate mode. Precedence: `guards`/`dashboardAuth` (they are the gate) > a token > the
455
+ * `allowUnauthenticated` opt-out > the environment default. A token beats `allowUnauthenticated`
456
+ * because a configured secret is never silently ignored.
457
+ */
458
+ declare function resolveDashboardGate(options: DashboardGateOptions, auth: ResolvedDashboardAuth | null): DashboardGate;
459
+ /** Log the one boot line the resolved gate calls for (nothing for a properly configured console). */
460
+ declare function reportDashboardGate(gate: DashboardGate, options: DashboardGateOptions, logger?: Pick<Logger, 'warn' | 'error'>): void;
461
+ /** The `DASHBOARD_GATE` provider: resolves once, after `DASHBOARD_AUTH`, and logs once. */
462
+ declare function dashboardGateProvider(options: DashboardGateOptions): Provider;
463
+ /**
464
+ * Read the token from `Authorization: Bearer <token>`, `x-durable-token`, or (GET/HEAD only) a
465
+ * `token` query param. The query channel is read-only on purpose: a `?token=` URL ends up in access
466
+ * logs, `Referer` headers and browser history, and must never authorize a mutation.
467
+ */
468
+ declare function readDashboardToken(request: unknown): string | undefined;
469
+ /**
470
+ * The fail-closed default gate, stamped on `DurableUiController` and `DurableApiController` (after
471
+ * the built-in session guard, before host `guards`) whenever the module can't prove at definition
472
+ * time that something else protects the console. It never gates `DurableAuthController`, so the
473
+ * login/session/logout routes stay reachable. See {@link DashboardGateMode} for what each mode does.
474
+ */
475
+ declare class DurableDashboardGateGuard implements CanActivate {
476
+ private readonly gate;
477
+ constructor(gate: DashboardGate);
478
+ canActivate(context: ExecutionContext): boolean;
479
+ }
480
+
415
481
  interface DurableDashboardOptions {
416
482
  /**
417
483
  * Where the SPA (UI) is served. Default `/durable`. This is a page route — keep it out of an
@@ -428,8 +494,10 @@ interface DurableDashboardOptions {
428
494
  * Guard classes fronting BOTH dashboard controllers (the SPA at `basePath` and its JSON API at
429
495
  * `apiBasePath`). Stamped onto each controller via `@nestjs/common`'s own `@UseGuards` metadata key
430
496
  * — REPLACE semantics, so a second `forRoot(...)` call overwrites (not appends to) whatever a prior
431
- * call stamped, same as re-applying `@UseGuards` by hand. Omit to leave the routes unguarded (the
432
- * host fronts them another way, e.g. a global guard or reverse-proxy auth).
497
+ * call stamped, same as re-applying `@UseGuards` by hand. A non-empty `guards` counts as the
498
+ * console's authentication, so the fail-closed default gate steps aside (see
499
+ * {@link allowUnauthenticated}). If the host fronts the routes another way (a global guard,
500
+ * reverse-proxy auth), say so with `allowUnauthenticated: true` instead.
433
501
  *
434
502
  * A guard's own DEPENDENCIES resolve from this module's `imports` (see {@link imports}) — the
435
503
  * dashboard module has no application context of its own to pull them from otherwise.
@@ -490,15 +558,36 @@ interface DurableDashboardOptions {
490
558
  *
491
559
  * Composes with {@link guards}: when both are set, a request must pass the built-in session check
492
560
  * AND every guard in `guards` — the SAME logical-AND semantics `@UseGuards` already gives multiple
493
- * guards, since the built-in guard is simply prepended to whatever `guards` you pass. Omit to leave
494
- * the console open (today's behavior, unchanged byte-for-byte) — front it with `guards`, a global
495
- * guard, or a reverse proxy instead.
561
+ * guards, since the built-in guard is simply prepended to whatever `guards` you pass. Setting it
562
+ * counts as the console's authentication (the fail-closed default gate steps aside). Omit it and
563
+ * configure `guards`, a {@link token}, or {@link allowUnauthenticated} instead.
496
564
  *
497
565
  * See {@link DashboardAuthOptions} for the `secret`/`ttl`/`session`/`login`/`revalidate` shape,
498
566
  * and `forRootAsync` / {@link DurableDashboardAsyncOptions.useDashboardAuth} when a hook needs DI
499
567
  * (e.g. an `EntityManager` to look up an admin user).
500
568
  */
501
569
  dashboardAuth?: DashboardAuthOptions;
570
+ /**
571
+ * Explicitly serve the console with NO authentication from this module. Use it only when
572
+ * something outside the module already protects it: a global guard, a reverse proxy / SSO in
573
+ * front of the mount, a private network. Logs a boot warning.
574
+ *
575
+ * Without it, an unconfigured console (no `guards`, no `dashboardAuth`, no token) is DENIED when
576
+ * `NODE_ENV=production` (every UI and API request answers `403`, and one boot error explains how
577
+ * to configure it). Outside production it is still served open, with a boot warning. Ignored when
578
+ * `guards`, `dashboardAuth` or a token is configured.
579
+ */
580
+ allowUnauthenticated?: boolean;
581
+ /**
582
+ * Bearer-token gate for the console. Defaults to `process.env.DURABLE_DASHBOARD_TOKEN` (read at
583
+ * boot). Only applies when neither `guards` nor `dashboardAuth` is configured: every UI and API
584
+ * request must then carry it as `Authorization: Bearer <token>`, an `x-durable-token` header, or
585
+ * (GET/HEAD only) a `?token=` query param, compared in constant time; anything else gets `401`.
586
+ *
587
+ * Meant for scripts and reverse proxies that inject the header: the bundled SPA does not attach
588
+ * the token to its own asset and API requests. Browser operators want `dashboardAuth` or `guards`.
589
+ */
590
+ token?: string;
502
591
  }
503
592
  /**
504
593
  * Async variant of {@link DurableDashboardOptions}. The mount paths and `guards` stay static (the
@@ -506,7 +595,9 @@ interface DurableDashboardOptions {
506
595
  * already has), but `dashboardAuth` is built by an injected factory, so its `session`/`login` hooks
507
596
  * can reach your DB/services — e.g. Mode A's `session` hook calling out to the same auth service
508
597
  * your host app already uses to validate the raw request. Returning `undefined` from
509
- * `useDashboardAuth` leaves the console open, exactly like omitting `dashboardAuth` from `forRoot`.
598
+ * `useDashboardAuth` is exactly like omitting `dashboardAuth` from `forRoot`: the console then
599
+ * falls back to `guards` / the token / `allowUnauthenticated`, and is DENIED in production when
600
+ * none of those is configured.
510
601
  */
511
602
  interface DurableDashboardAsyncOptions {
512
603
  basePath?: string;
@@ -515,8 +606,12 @@ interface DurableDashboardAsyncOptions {
515
606
  imports?: DynamicModule['imports'];
516
607
  /** Providers injected into `useDashboardAuth`, in order. Shared with a `guards` class's own deps. */
517
608
  inject?: Array<InjectionToken | OptionalFactoryDependency>;
518
- /** Build the `dashboardAuth` config from injected deps (or `undefined` to leave the console open). */
609
+ /** Build the `dashboardAuth` config from injected deps (or `undefined` for no session auth). */
519
610
  useDashboardAuth: (...deps: any[]) => DashboardAuthOptions | undefined | Promise<DashboardAuthOptions | undefined>;
611
+ /** See {@link DurableDashboardOptions.allowUnauthenticated}. */
612
+ allowUnauthenticated?: boolean;
613
+ /** See {@link DurableDashboardOptions.token}. */
614
+ token?: string;
520
615
  }
521
616
  /**
522
617
  * Holds the JSON API controller + its read service, mounted on its own path by `forRoot`. Dynamic:
@@ -536,6 +631,10 @@ declare class DurableApiModule {
536
631
  * (true whenever `dashboardAuth` was set, or unconditionally for `forRootAsync`, where it's
537
632
  * resolved at runtime and is a no-op if it ends up unconfigured). */
538
633
  authMayBeConfigured: boolean;
634
+ /** What the fail-closed gate needs; resolved into `DASHBOARD_GATE` at boot (logged once). */
635
+ gate: DashboardGateOptions;
636
+ /** Whether `DurableDashboardGateGuard` is stamped (false when guards/dashboardAuth are static). */
637
+ stampGate: boolean;
539
638
  }): DynamicModule;
540
639
  }
541
640
  /**
@@ -545,6 +644,8 @@ declare class DurableApiModule {
545
644
  * so it resolves the engine and store. Front the routes with the first-class `guards` option
546
645
  * (plus `imports` for the guards' own dependencies) — see {@link DurableDashboardOptions.guards} —
547
646
  * or with the built-in `dashboardAuth` cookie login, or both.
647
+ * With none of those (and no `token` / `allowUnauthenticated`), the console fails closed when
648
+ * `NODE_ENV=production` — see {@link DurableDashboardOptions.allowUnauthenticated}.
548
649
  */
549
650
  declare class DurableDashboardModule {
550
651
  static forRoot(options?: DurableDashboardOptions): DynamicModule;
@@ -631,4 +732,4 @@ declare class DurableUiController {
631
732
  asset(file: string): StreamableFile;
632
733
  }
633
734
 
634
- export { DASHBOARD_API_PATH, DASHBOARD_BASE_PATH, type DashboardAuthOptions, DashboardService, type DashboardSessionUser, DurableApiController, DurableApiModule, DurableApiSessionGuard, DurableAuthController, type DurableDashboardAsyncOptions, DurableDashboardModule, type DurableDashboardOptions, DurableUiController, DurableUiSessionGuard, type UnauthenticatedPageContext, type UnauthenticatedPageHook };
735
+ export { DASHBOARD_API_PATH, DASHBOARD_BASE_PATH, DASHBOARD_GATE, DURABLE_DASHBOARD_TOKEN_ENV, type DashboardAuthOptions, type DashboardGate, type DashboardGateMode, type DashboardGateOptions, DashboardService, type DashboardSessionUser, DurableApiController, DurableApiModule, DurableApiSessionGuard, DurableAuthController, type DurableDashboardAsyncOptions, DurableDashboardGateGuard, DurableDashboardModule, type DurableDashboardOptions, DurableUiController, DurableUiSessionGuard, type UnauthenticatedPageContext, type UnauthenticatedPageHook, dashboardGateProvider, readDashboardToken, reportDashboardGate, resolveDashboardGate };
@@ -5,7 +5,7 @@ import * as rxjs from 'rxjs';
5
5
  import { Observable } from 'rxjs';
6
6
  import * as _dudousxd_nestjs_filter from '@dudousxd/nestjs-filter';
7
7
  import { FilterRunner } from '@dudousxd/nestjs-filter';
8
- import { DynamicModule, Type, CanActivate, Provider, InjectionToken, OptionalFactoryDependency, ExecutionContext, StreamableFile } from '@nestjs/common';
8
+ import { CanActivate, ExecutionContext, Provider, Logger, DynamicModule, Type, InjectionToken, OptionalFactoryDependency, StreamableFile } from '@nestjs/common';
9
9
 
10
10
  /** The session user the host `login` hook returns to mint a cookie. */
11
11
  interface DashboardSessionUser {
@@ -199,8 +199,12 @@ declare class DashboardService {
199
199
  }) | null>;
200
200
  /**
201
201
  * Deliver a `ctx.webhook()` callback: turn an inbound POST (token + body) into the signal the
202
- * waiting run is parked on. Returns the run result, or `null` if no run waits on that token (a
203
- * stale/duplicate callback) — a safe no-op the controller maps to 404. Control-plane-only.
202
+ * run is (or will be) parked on. A callback that arrives BEFORE the run reaches `wait()` (the url
203
+ * was handed out and the run is still busy or sleeping) is buffered, and `wait()` consumes it, so
204
+ * the result is the run's current status. Returns `null` — mapped to 404 by the controller — for a
205
+ * token that is not a webhook token, whose run doesn't exist or is terminal, or whose wait already
206
+ * resolved (a duplicate): nothing is buffered for those, so a public endpoint can't leave junk
207
+ * rows behind or signal non-webhook tokens. Control-plane-only.
204
208
  */
205
209
  deliverWebhook(token: string, body: unknown): Promise<RunResult | null>;
206
210
  /** Side-effect-free read of a value a run published via `ctx.setEvent` (a live query). Control-plane-only. */
@@ -412,6 +416,68 @@ declare class DurableAuthController {
412
416
  private runHook;
413
417
  }
414
418
 
419
+ /** The env var the token gate reads when no `token` option is given. */
420
+ declare const DURABLE_DASHBOARD_TOKEN_ENV = "DURABLE_DASHBOARD_TOKEN";
421
+ /** DI token carrying the resolved {@link DashboardGate} (one per dashboard mount). */
422
+ declare const DASHBOARD_GATE: unique symbol;
423
+ /**
424
+ * What the module knows about how the console is protected, captured at `forRoot`/`forRootAsync`
425
+ * time. Everything that can change between module definition and boot (env vars, the async
426
+ * `dashboardAuth` factory) is read later, in {@link resolveDashboardGate}.
427
+ */
428
+ interface DashboardGateOptions {
429
+ /** Host `guards` were passed (non-empty). */
430
+ hasGuards: boolean;
431
+ /** `allowUnauthenticated` as passed. */
432
+ allowUnauthenticated?: boolean;
433
+ /** `token` as passed (else `DURABLE_DASHBOARD_TOKEN` is read at boot). */
434
+ token?: string;
435
+ }
436
+ /**
437
+ * How the gate treats every console request:
438
+ *
439
+ * - `delegated`: `guards` or `dashboardAuth` protect the console; the gate passes everything.
440
+ * - `open`: `allowUnauthenticated: true`; the gate passes everything (warned at boot).
441
+ * - `token`: a request must carry the token (`Authorization: Bearer`, `x-durable-token`, or
442
+ * `?token=` on GET/HEAD).
443
+ * - `unconfigured`: nothing was configured outside production; the gate passes everything
444
+ * (warned at boot, because production will deny).
445
+ * - `deny`: nothing was configured and `NODE_ENV=production`; every request is refused.
446
+ */
447
+ type DashboardGateMode = 'delegated' | 'open' | 'token' | 'unconfigured' | 'deny';
448
+ interface DashboardGate {
449
+ mode: DashboardGateMode;
450
+ /** Set only when `mode === 'token'`. */
451
+ token?: string;
452
+ }
453
+ /**
454
+ * Decide the gate mode. Precedence: `guards`/`dashboardAuth` (they are the gate) > a token > the
455
+ * `allowUnauthenticated` opt-out > the environment default. A token beats `allowUnauthenticated`
456
+ * because a configured secret is never silently ignored.
457
+ */
458
+ declare function resolveDashboardGate(options: DashboardGateOptions, auth: ResolvedDashboardAuth | null): DashboardGate;
459
+ /** Log the one boot line the resolved gate calls for (nothing for a properly configured console). */
460
+ declare function reportDashboardGate(gate: DashboardGate, options: DashboardGateOptions, logger?: Pick<Logger, 'warn' | 'error'>): void;
461
+ /** The `DASHBOARD_GATE` provider: resolves once, after `DASHBOARD_AUTH`, and logs once. */
462
+ declare function dashboardGateProvider(options: DashboardGateOptions): Provider;
463
+ /**
464
+ * Read the token from `Authorization: Bearer <token>`, `x-durable-token`, or (GET/HEAD only) a
465
+ * `token` query param. The query channel is read-only on purpose: a `?token=` URL ends up in access
466
+ * logs, `Referer` headers and browser history, and must never authorize a mutation.
467
+ */
468
+ declare function readDashboardToken(request: unknown): string | undefined;
469
+ /**
470
+ * The fail-closed default gate, stamped on `DurableUiController` and `DurableApiController` (after
471
+ * the built-in session guard, before host `guards`) whenever the module can't prove at definition
472
+ * time that something else protects the console. It never gates `DurableAuthController`, so the
473
+ * login/session/logout routes stay reachable. See {@link DashboardGateMode} for what each mode does.
474
+ */
475
+ declare class DurableDashboardGateGuard implements CanActivate {
476
+ private readonly gate;
477
+ constructor(gate: DashboardGate);
478
+ canActivate(context: ExecutionContext): boolean;
479
+ }
480
+
415
481
  interface DurableDashboardOptions {
416
482
  /**
417
483
  * Where the SPA (UI) is served. Default `/durable`. This is a page route — keep it out of an
@@ -428,8 +494,10 @@ interface DurableDashboardOptions {
428
494
  * Guard classes fronting BOTH dashboard controllers (the SPA at `basePath` and its JSON API at
429
495
  * `apiBasePath`). Stamped onto each controller via `@nestjs/common`'s own `@UseGuards` metadata key
430
496
  * — REPLACE semantics, so a second `forRoot(...)` call overwrites (not appends to) whatever a prior
431
- * call stamped, same as re-applying `@UseGuards` by hand. Omit to leave the routes unguarded (the
432
- * host fronts them another way, e.g. a global guard or reverse-proxy auth).
497
+ * call stamped, same as re-applying `@UseGuards` by hand. A non-empty `guards` counts as the
498
+ * console's authentication, so the fail-closed default gate steps aside (see
499
+ * {@link allowUnauthenticated}). If the host fronts the routes another way (a global guard,
500
+ * reverse-proxy auth), say so with `allowUnauthenticated: true` instead.
433
501
  *
434
502
  * A guard's own DEPENDENCIES resolve from this module's `imports` (see {@link imports}) — the
435
503
  * dashboard module has no application context of its own to pull them from otherwise.
@@ -490,15 +558,36 @@ interface DurableDashboardOptions {
490
558
  *
491
559
  * Composes with {@link guards}: when both are set, a request must pass the built-in session check
492
560
  * AND every guard in `guards` — the SAME logical-AND semantics `@UseGuards` already gives multiple
493
- * guards, since the built-in guard is simply prepended to whatever `guards` you pass. Omit to leave
494
- * the console open (today's behavior, unchanged byte-for-byte) — front it with `guards`, a global
495
- * guard, or a reverse proxy instead.
561
+ * guards, since the built-in guard is simply prepended to whatever `guards` you pass. Setting it
562
+ * counts as the console's authentication (the fail-closed default gate steps aside). Omit it and
563
+ * configure `guards`, a {@link token}, or {@link allowUnauthenticated} instead.
496
564
  *
497
565
  * See {@link DashboardAuthOptions} for the `secret`/`ttl`/`session`/`login`/`revalidate` shape,
498
566
  * and `forRootAsync` / {@link DurableDashboardAsyncOptions.useDashboardAuth} when a hook needs DI
499
567
  * (e.g. an `EntityManager` to look up an admin user).
500
568
  */
501
569
  dashboardAuth?: DashboardAuthOptions;
570
+ /**
571
+ * Explicitly serve the console with NO authentication from this module. Use it only when
572
+ * something outside the module already protects it: a global guard, a reverse proxy / SSO in
573
+ * front of the mount, a private network. Logs a boot warning.
574
+ *
575
+ * Without it, an unconfigured console (no `guards`, no `dashboardAuth`, no token) is DENIED when
576
+ * `NODE_ENV=production` (every UI and API request answers `403`, and one boot error explains how
577
+ * to configure it). Outside production it is still served open, with a boot warning. Ignored when
578
+ * `guards`, `dashboardAuth` or a token is configured.
579
+ */
580
+ allowUnauthenticated?: boolean;
581
+ /**
582
+ * Bearer-token gate for the console. Defaults to `process.env.DURABLE_DASHBOARD_TOKEN` (read at
583
+ * boot). Only applies when neither `guards` nor `dashboardAuth` is configured: every UI and API
584
+ * request must then carry it as `Authorization: Bearer <token>`, an `x-durable-token` header, or
585
+ * (GET/HEAD only) a `?token=` query param, compared in constant time; anything else gets `401`.
586
+ *
587
+ * Meant for scripts and reverse proxies that inject the header: the bundled SPA does not attach
588
+ * the token to its own asset and API requests. Browser operators want `dashboardAuth` or `guards`.
589
+ */
590
+ token?: string;
502
591
  }
503
592
  /**
504
593
  * Async variant of {@link DurableDashboardOptions}. The mount paths and `guards` stay static (the
@@ -506,7 +595,9 @@ interface DurableDashboardOptions {
506
595
  * already has), but `dashboardAuth` is built by an injected factory, so its `session`/`login` hooks
507
596
  * can reach your DB/services — e.g. Mode A's `session` hook calling out to the same auth service
508
597
  * your host app already uses to validate the raw request. Returning `undefined` from
509
- * `useDashboardAuth` leaves the console open, exactly like omitting `dashboardAuth` from `forRoot`.
598
+ * `useDashboardAuth` is exactly like omitting `dashboardAuth` from `forRoot`: the console then
599
+ * falls back to `guards` / the token / `allowUnauthenticated`, and is DENIED in production when
600
+ * none of those is configured.
510
601
  */
511
602
  interface DurableDashboardAsyncOptions {
512
603
  basePath?: string;
@@ -515,8 +606,12 @@ interface DurableDashboardAsyncOptions {
515
606
  imports?: DynamicModule['imports'];
516
607
  /** Providers injected into `useDashboardAuth`, in order. Shared with a `guards` class's own deps. */
517
608
  inject?: Array<InjectionToken | OptionalFactoryDependency>;
518
- /** Build the `dashboardAuth` config from injected deps (or `undefined` to leave the console open). */
609
+ /** Build the `dashboardAuth` config from injected deps (or `undefined` for no session auth). */
519
610
  useDashboardAuth: (...deps: any[]) => DashboardAuthOptions | undefined | Promise<DashboardAuthOptions | undefined>;
611
+ /** See {@link DurableDashboardOptions.allowUnauthenticated}. */
612
+ allowUnauthenticated?: boolean;
613
+ /** See {@link DurableDashboardOptions.token}. */
614
+ token?: string;
520
615
  }
521
616
  /**
522
617
  * Holds the JSON API controller + its read service, mounted on its own path by `forRoot`. Dynamic:
@@ -536,6 +631,10 @@ declare class DurableApiModule {
536
631
  * (true whenever `dashboardAuth` was set, or unconditionally for `forRootAsync`, where it's
537
632
  * resolved at runtime and is a no-op if it ends up unconfigured). */
538
633
  authMayBeConfigured: boolean;
634
+ /** What the fail-closed gate needs; resolved into `DASHBOARD_GATE` at boot (logged once). */
635
+ gate: DashboardGateOptions;
636
+ /** Whether `DurableDashboardGateGuard` is stamped (false when guards/dashboardAuth are static). */
637
+ stampGate: boolean;
539
638
  }): DynamicModule;
540
639
  }
541
640
  /**
@@ -545,6 +644,8 @@ declare class DurableApiModule {
545
644
  * so it resolves the engine and store. Front the routes with the first-class `guards` option
546
645
  * (plus `imports` for the guards' own dependencies) — see {@link DurableDashboardOptions.guards} —
547
646
  * or with the built-in `dashboardAuth` cookie login, or both.
647
+ * With none of those (and no `token` / `allowUnauthenticated`), the console fails closed when
648
+ * `NODE_ENV=production` — see {@link DurableDashboardOptions.allowUnauthenticated}.
548
649
  */
549
650
  declare class DurableDashboardModule {
550
651
  static forRoot(options?: DurableDashboardOptions): DynamicModule;
@@ -631,4 +732,4 @@ declare class DurableUiController {
631
732
  asset(file: string): StreamableFile;
632
733
  }
633
734
 
634
- export { DASHBOARD_API_PATH, DASHBOARD_BASE_PATH, type DashboardAuthOptions, DashboardService, type DashboardSessionUser, DurableApiController, DurableApiModule, DurableApiSessionGuard, DurableAuthController, type DurableDashboardAsyncOptions, DurableDashboardModule, type DurableDashboardOptions, DurableUiController, DurableUiSessionGuard, type UnauthenticatedPageContext, type UnauthenticatedPageHook };
735
+ export { DASHBOARD_API_PATH, DASHBOARD_BASE_PATH, DASHBOARD_GATE, DURABLE_DASHBOARD_TOKEN_ENV, type DashboardAuthOptions, type DashboardGate, type DashboardGateMode, type DashboardGateOptions, DashboardService, type DashboardSessionUser, DurableApiController, DurableApiModule, DurableApiSessionGuard, DurableAuthController, type DurableDashboardAsyncOptions, DurableDashboardGateGuard, DurableDashboardModule, type DurableDashboardOptions, DurableUiController, DurableUiSessionGuard, type UnauthenticatedPageContext, type UnauthenticatedPageHook, dashboardGateProvider, readDashboardToken, reportDashboardGate, resolveDashboardGate };