@camstack/system 1.2.147 → 1.2.148

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 (59) hide show
  1. package/dist/addon-runner.js +1 -1
  2. package/dist/addon-runner.mjs +1 -1
  3. package/dist/auth/auth-manager.d.ts +17 -1
  4. package/dist/auth/token-expiry.d.ts +39 -0
  5. package/dist/auth/user-manager.d.ts +2 -1
  6. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  7. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  8. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  9. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  10. package/dist/builtins/alerts/alerts.addon.js +1 -1
  11. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  12. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  13. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  14. package/dist/builtins/console-logging/index.js +1 -1
  15. package/dist/builtins/console-logging/index.mjs +1 -1
  16. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  17. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  18. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  19. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  20. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  21. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  22. package/dist/builtins/hub-forwarder/index.js +1 -1
  23. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  24. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  25. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  26. package/dist/builtins/local-auth/auth-config-reader.d.ts +20 -0
  27. package/dist/builtins/local-auth/local-auth.addon.js +211 -17
  28. package/dist/builtins/local-auth/local-auth.addon.mjs +211 -17
  29. package/dist/builtins/local-network/local-network.addon.js +1 -1
  30. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  31. package/dist/builtins/loki-logging/index.js +1 -1
  32. package/dist/builtins/loki-logging/index.mjs +1 -1
  33. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  34. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  35. package/dist/builtins/platform-probe/index.js +1 -1
  36. package/dist/builtins/platform-probe/index.mjs +1 -1
  37. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  38. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  39. package/dist/builtins/snapshot/index.js +1 -1
  40. package/dist/builtins/snapshot/index.mjs +1 -1
  41. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  42. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  43. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  44. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  45. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  46. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  47. package/dist/builtins/system-config/system-config.addon.js +12 -11
  48. package/dist/builtins/system-config/system-config.addon.mjs +12 -11
  49. package/dist/builtins/winston-logging/index.js +1 -1
  50. package/dist/builtins/winston-logging/index.mjs +1 -1
  51. package/dist/{dist-Dl6MFXPr.js → dist-B-mBrEz9.js} +1 -1
  52. package/dist/{dist-DrBc-7Y3.mjs → dist-BwD83D2f.mjs} +1 -1
  53. package/dist/index.js +2 -2
  54. package/dist/index.mjs +2 -2
  55. package/dist/{manifest-python-deps-FYZBHc3v.js → manifest-python-deps-BV_Cy99l.js} +1 -1
  56. package/dist/{manifest-python-deps-DhT0oFOZ.mjs → manifest-python-deps-DpRX9LtD.mjs} +1 -1
  57. package/dist/{retired-settings-keys-CF5r4YKq.mjs → retired-settings-keys-CRKpAmSn.mjs} +1 -1
  58. package/dist/{retired-settings-keys-6w_JOqBg.js → retired-settings-keys-Davtjo5p.js} +1 -1
  59. package/package.json +6 -4
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
- const require_manifest_python_deps = require("./manifest-python-deps-FYZBHc3v.js");
2
+ const require_manifest_python_deps = require("./manifest-python-deps-BV_Cy99l.js");
3
3
  const require_custom_action_registry = require("./custom-action-registry-jY0NOZK8.js");
4
4
  let node_path = require("node:path");
5
5
  node_path = require_chunk.__toESM(node_path);
@@ -1,4 +1,4 @@
1
- import { F as createUdsLoggerWithControl, Ht as startRunnerHeapWatch, I as LocalChildClient, Nt as RUNNER_RSS_BUDGET_ENV, bt as resolveAddonClass, i as createUdsAddonContext, it as setWorkerNativeCapsChangeListener, nt as getWorkerNativeCapSnapshot, ot as validateProviderRegistrations, t as installManifestPythonDeps, tt as getWorkerNativeCapProvider, yt as installManifestNativeDeps } from "./manifest-python-deps-DhT0oFOZ.mjs";
1
+ import { F as createUdsLoggerWithControl, Ht as startRunnerHeapWatch, I as LocalChildClient, Nt as RUNNER_RSS_BUDGET_ENV, bt as resolveAddonClass, i as createUdsAddonContext, it as setWorkerNativeCapsChangeListener, nt as getWorkerNativeCapSnapshot, ot as validateProviderRegistrations, t as installManifestPythonDeps, tt as getWorkerNativeCapProvider, yt as installManifestNativeDeps } from "./manifest-python-deps-DpRX9LtD.mjs";
2
2
  import { t as CustomActionRegistry } from "./custom-action-registry-F__gp_VX.mjs";
3
3
  import { register } from "node:module";
4
4
  import * as path$1 from "node:path";
@@ -2,13 +2,29 @@ import { IScopedLogger, TokenPayload, SsoBridgeClaimsSchema } from '@camstack/ty
2
2
  import { z } from 'zod';
3
3
  export type { TokenPayload };
4
4
  export type AuthConfigReader = {
5
- get<T>(path: string): T;
5
+ /**
6
+ * `undefined` is part of the contract, not an accident: a reader answers
7
+ * only the paths it can (`local-auth` pre-resolves a fixed set; a
8
+ * `ConfigManager` falls through to RUNTIME_DEFAULTS and then to nothing).
9
+ * Every consumer here must handle the absent case explicitly.
10
+ */
11
+ get<T>(path: string): T | undefined;
6
12
  update(section: string, data: Record<string, unknown>): void;
7
13
  };
8
14
  export declare class AuthManager {
9
15
  private readonly config;
10
16
  private readonly jwtSecret;
11
17
  private readonly logger;
18
+ /**
19
+ * Offending `auth.tokenExpiry` values already reported by this instance.
20
+ *
21
+ * `signToken` re-reads the setting on EVERY mint so an operator's change
22
+ * takes effect without a restart — which also means a bad value would log
23
+ * once per login and once per silent refresh from every viewer. The refusal
24
+ * must be visible, not repeated: the first mint after a bad save logs, the
25
+ * rest resolve silently to the same default until the value changes.
26
+ */
27
+ private readonly reportedInvalidExpiries;
12
28
  constructor(config: AuthConfigReader, logger?: IScopedLogger);
13
29
  signToken(payload: Omit<TokenPayload, 'iat' | 'exp'>): string;
14
30
  verifyToken(token: string): TokenPayload;
@@ -0,0 +1,39 @@
1
+ import { DEFAULT_TOKEN_EXPIRY, IScopedLogger } from '@camstack/types';
2
+ /** Why a candidate value was refused. Feeds the log line, not the UI. */
3
+ export type TokenExpiryRejection = 'not-a-string' | 'malformed' | 'too-short' | 'too-long';
4
+ interface TokenExpiryOutcome {
5
+ /**
6
+ * Lifetime to hand `jwt.sign`, in SECONDS.
7
+ *
8
+ * Deliberately a number rather than the duration string: `expiresIn` accepts
9
+ * both, but the string branch re-parses through `ms` — which is the branch
10
+ * that reads a bare number as milliseconds. Resolving to seconds here means
11
+ * exactly one parser decides what the operator's text meant, and it is this
12
+ * one.
13
+ */
14
+ readonly seconds: number;
15
+ /** The duration string actually applied — for the log line and the UI story. */
16
+ readonly value: string;
17
+ }
18
+ export interface TokenExpiryRejected extends TokenExpiryOutcome {
19
+ readonly ok: false;
20
+ readonly reason: TokenExpiryRejection;
21
+ }
22
+ export interface TokenExpiryAccepted extends TokenExpiryOutcome {
23
+ readonly ok: true;
24
+ readonly reason?: undefined;
25
+ }
26
+ export type TokenExpiryResult = TokenExpiryAccepted | TokenExpiryRejected;
27
+ /**
28
+ * Parse a configured `auth.tokenExpiry`. An absent/blank value is not a
29
+ * rejection — it is "operator never set it" and resolves to the default
30
+ * without a log line.
31
+ */
32
+ export declare function parseTokenExpiry(configured: unknown): TokenExpiryResult;
33
+ /**
34
+ * Report a refused `auth.tokenExpiry`. Separate from {@link parseTokenExpiry}
35
+ * so the caller owns HOW OFTEN it speaks — the signer re-reads the setting on
36
+ * every mint and must not turn one bad save into a line per login.
37
+ */
38
+ export declare function logTokenExpiryRejection(configured: unknown, rejection: TokenExpiryRejected, logger: IScopedLogger): void;
39
+ export { DEFAULT_TOKEN_EXPIRY };
@@ -14,7 +14,8 @@ export interface UserStorageAccess {
14
14
  getStore(): SettingsStoreClient;
15
15
  }
16
16
  export interface UserConfigReader {
17
- get<T>(path: string): T;
17
+ /** See `AuthConfigReader.get` a reader answers only the paths it resolved. */
18
+ get<T>(path: string): T | undefined;
18
19
  }
19
20
  export declare class UserManager {
20
21
  private readonly storageAccess;
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Dl6MFXPr.js");
6
+ const require_dist = require("../../dist-B-mBrEz9.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Ot as errMsg, S as addonPagesCapability, kt as BaseAddon, tn as EventCategory } from "../../dist-DrBc-7Y3.mjs";
1
+ import { Ot as errMsg, S as addonPagesCapability, kt as BaseAddon, tn as EventCategory } from "../../dist-BwD83D2f.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Dl6MFXPr.js");
6
+ const require_dist = require("../../dist-B-mBrEz9.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Ot as errMsg, kt as BaseAddon, tn as EventCategory, w as addonWidgetsCapability } from "../../dist-DrBc-7Y3.mjs";
1
+ import { Ot as errMsg, kt as BaseAddon, tn as EventCategory, w as addonWidgetsCapability } from "../../dist-BwD83D2f.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Dl6MFXPr.js");
6
+ const require_dist = require("../../dist-B-mBrEz9.js");
7
7
  //#region src/builtins/alerts/prune-policy.ts
8
8
  /**
9
9
  * Ids older than `cutoffMs`, oldest first.
@@ -1,4 +1,4 @@
1
- import { Ht as createEvent, Ot as errMsg, T as alertsCapability, kt as BaseAddon, n as AlertSchema, tn as EventCategory } from "../../dist-DrBc-7Y3.mjs";
1
+ import { Ht as createEvent, Ot as errMsg, T as alertsCapability, kt as BaseAddon, n as AlertSchema, tn as EventCategory } from "../../dist-BwD83D2f.mjs";
2
2
  //#region src/builtins/alerts/prune-policy.ts
3
3
  /**
4
4
  * Ids older than `cutoffMs`, oldest first.
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Dl6MFXPr.js");
6
+ const require_dist = require("../../dist-B-mBrEz9.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let buffer = require("buffer");
9
9
  let node_fs_promises = require("node:fs/promises");
@@ -1,4 +1,4 @@
1
- import { D as backupCapability, kt as BaseAddon, tn as EventCategory } from "../../dist-DrBc-7Y3.mjs";
1
+ import { D as backupCapability, kt as BaseAddon, tn as EventCategory } from "../../dist-BwD83D2f.mjs";
2
2
  import { randomBytes, randomUUID } from "node:crypto";
3
3
  import { Buffer as Buffer$1 } from "buffer";
4
4
  import * as fsp from "node:fs/promises";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Dl6MFXPr.js");
6
+ const require_dist = require("../../dist-B-mBrEz9.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  //#region src/builtins/console-logging/console-destination.ts
9
9
  var LEVEL_RANK = {
@@ -1,4 +1,4 @@
1
- import { kt as BaseAddon, rt as logDestinationCapability } from "../../dist-DrBc-7Y3.mjs";
1
+ import { kt as BaseAddon, rt as logDestinationCapability } from "../../dist-BwD83D2f.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  //#region src/builtins/console-logging/console-destination.ts
4
4
  var LEVEL_RANK = {
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-Dl6MFXPr.js");
3
+ const require_dist = require("../../dist-B-mBrEz9.js");
4
4
  let node_crypto = require("node:crypto");
5
5
  let node_fs_promises = require("node:fs/promises");
6
6
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -1,4 +1,4 @@
1
- import { M as coreBlockIdFromAddonId, N as coreBlocksCapability, Ot as errMsg, c as CoreBlockSchema, j as coreBlockAddonId, kt as BaseAddon, o as CORE_BLOCKS_ADDON_ID, s as CORE_BLOCK_ADDON_PREFIX, u as DeclaredDevices } from "../../dist-DrBc-7Y3.mjs";
1
+ import { M as coreBlockIdFromAddonId, N as coreBlocksCapability, Ot as errMsg, c as CoreBlockSchema, j as coreBlockAddonId, kt as BaseAddon, o as CORE_BLOCKS_ADDON_ID, s as CORE_BLOCK_ADDON_PREFIX, u as DeclaredDevices } from "../../dist-BwD83D2f.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import * as fs from "node:fs/promises";
4
4
  import * as path$1 from "node:path";
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Dl6MFXPr.js");
7
- const require_retired_settings_keys = require("../../retired-settings-keys-6w_JOqBg.js");
6
+ const require_dist = require("../../dist-B-mBrEz9.js");
7
+ const require_retired_settings_keys = require("../../retired-settings-keys-Davtjo5p.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let _camstack_types_node = require("@camstack/types/node");
10
10
  //#region src/builtins/device-manager/adoption-job-engine.ts
@@ -1,5 +1,5 @@
1
- import { A as buildStreamParamsConfigSchema, Ft as DeviceType, H as enumerateItemArrayFields, L as deviceManagerCapability, Nt as DeviceFeature, Ot as errMsg, Pt as DeviceRole, R as deviceStateCapability, Rt as WELL_KNOWN_TAB_MAP, U as enumerateSchemaFields, _ as STREAM_PROFILE_META, a as CAP_NAMES_WITH_STATUS, d as DeviceStatusSchema, en as sleep, ht as runtimeStatePolicyFor, kt as BaseAddon, lt as parseStreamParamsFormPatch, qt as isDeviceConfigCap, st as normalizeUnit, t as ALL_CAPABILITY_DEFINITIONS, tn as EventCategory, z as deviceStatusCapability } from "../../dist-DrBc-7Y3.mjs";
2
- import { n as purgeRetiredSettingsRows } from "../../retired-settings-keys-CF5r4YKq.mjs";
1
+ import { A as buildStreamParamsConfigSchema, Ft as DeviceType, H as enumerateItemArrayFields, L as deviceManagerCapability, Nt as DeviceFeature, Ot as errMsg, Pt as DeviceRole, R as deviceStateCapability, Rt as WELL_KNOWN_TAB_MAP, U as enumerateSchemaFields, _ as STREAM_PROFILE_META, a as CAP_NAMES_WITH_STATUS, d as DeviceStatusSchema, en as sleep, ht as runtimeStatePolicyFor, kt as BaseAddon, lt as parseStreamParamsFormPatch, qt as isDeviceConfigCap, st as normalizeUnit, t as ALL_CAPABILITY_DEFINITIONS, tn as EventCategory, z as deviceStatusCapability } from "../../dist-BwD83D2f.mjs";
2
+ import { n as purgeRetiredSettingsRows } from "../../retired-settings-keys-CRKpAmSn.mjs";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { canonicalDeviceFingerprint } from "@camstack/types/node";
5
5
  //#region src/builtins/device-manager/adoption-job-engine.ts
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Dl6MFXPr.js");
6
+ const require_dist = require("../../dist-B-mBrEz9.js");
7
7
  //#region src/builtins/doorbell/trigger-engine.ts
8
8
  /**
9
9
  * Pure trigger logic for the virtual-doorbell builtin — edge detection over
@@ -1,4 +1,4 @@
1
- import { B as doorbellCapability, Ft as DeviceType, Ht as createEvent, O as bareAddonId, Ot as errMsg, Q as isSourceCap, W as evaluateSensorEdge, Z as isSameAddonId, g as SOURCE_DEVICE_TYPES, h as SOURCE_CAPS, kt as BaseAddon, tn as EventCategory } from "../../dist-DrBc-7Y3.mjs";
1
+ import { B as doorbellCapability, Ft as DeviceType, Ht as createEvent, O as bareAddonId, Ot as errMsg, Q as isSourceCap, W as evaluateSensorEdge, Z as isSameAddonId, g as SOURCE_DEVICE_TYPES, h as SOURCE_CAPS, kt as BaseAddon, tn as EventCategory } from "../../dist-BwD83D2f.mjs";
2
2
  //#region src/builtins/doorbell/trigger-engine.ts
3
3
  /**
4
4
  * Pure trigger logic for the virtual-doorbell builtin — edge detection over
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Dl6MFXPr.js");
6
+ const require_dist = require("../../dist-B-mBrEz9.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  //#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
9
9
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
@@ -1,4 +1,4 @@
1
- import { kt as BaseAddon, rt as logDestinationCapability } from "../../dist-DrBc-7Y3.mjs";
1
+ import { kt as BaseAddon, rt as logDestinationCapability } from "../../dist-BwD83D2f.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  //#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
4
4
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-Dl6MFXPr.js");
3
+ const require_dist = require("../../dist-B-mBrEz9.js");
4
4
  //#region src/builtins/liveness-monitor/liveness-checks.ts
5
5
  var NO_DEVICES = "liveness:no-devices";
6
6
  var ALL_OFFLINE = "liveness:all-devices-offline";
@@ -1,4 +1,4 @@
1
- import { Ht as createEvent, Ot as errMsg, kt as BaseAddon, tn as EventCategory } from "../../dist-DrBc-7Y3.mjs";
1
+ import { Ht as createEvent, Ot as errMsg, kt as BaseAddon, tn as EventCategory } from "../../dist-BwD83D2f.mjs";
2
2
  //#region src/builtins/liveness-monitor/liveness-checks.ts
3
3
  var NO_DEVICES = "liveness:no-devices";
4
4
  var ALL_OFFLINE = "liveness:all-devices-offline";
@@ -0,0 +1,20 @@
1
+ import { AuthConfigReader } from '../../auth/auth-manager.js';
2
+ /** The values `local-auth` pre-resolves before constructing its AuthManager. */
3
+ export interface ResolvedAuthConfig {
4
+ readonly jwtSecret: string;
5
+ readonly adminUsername: string;
6
+ readonly adminPassword: string;
7
+ /**
8
+ * Operator-configured session lifetime. `undefined` when the `auth` section
9
+ * carries no value — the signer then applies `DEFAULT_TOKEN_EXPIRY`, which
10
+ * is where the default belongs (one default, one place).
11
+ */
12
+ readonly tokenExpiry: string | undefined;
13
+ }
14
+ /** Persist the generated JWT secret. Called by AuthManager on first boot. */
15
+ export type AuthSectionWriter = (patch: Record<string, unknown>) => void;
16
+ /**
17
+ * Every config path this reader can answer. Anything else resolves to
18
+ * `undefined` by construction — see the module docblock.
19
+ */
20
+ export declare function createAuthConfigReader(resolved: ResolvedAuthConfig, writeAuthSection: AuthSectionWriter): AuthConfigReader;
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Dl6MFXPr.js");
6
+ const require_dist = require("../../dist-B-mBrEz9.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  node_crypto = require_chunk.__toESM(node_crypto);
9
9
  let crypto$1 = require("crypto");
@@ -6017,8 +6017,158 @@ var require_jsonwebtoken = /* @__PURE__ */ require_chunk.__commonJSMin(((exports
6017
6017
  };
6018
6018
  }));
6019
6019
  //#endregion
6020
- //#region src/auth/auth-manager.ts
6020
+ //#region src/auth/token-expiry.ts
6021
6021
  var import_jsonwebtoken = /* @__PURE__ */ require_chunk.__toESM(require_jsonwebtoken());
6022
+ /**
6023
+ * Validation for the operator-configured session-JWT lifetime
6024
+ * (`auth.tokenExpiry`, System → Settings → Authentication → Token Expiry).
6025
+ *
6026
+ * WHY THIS EXISTS. The field is a free-text box that reaches `jwt.sign`'s
6027
+ * `expiresIn`. Two things go wrong without a gate in front of it:
6028
+ *
6029
+ * 1. `jsonwebtoken` THROWS on a value it cannot parse. The only caller is
6030
+ * the login/refresh path, so one typo in a settings form turns every
6031
+ * login into a 500 — a self-inflicted outage from a text field.
6032
+ * 2. `jsonwebtoken` hands a bare numeric STRING to `ms()`, which reads it
6033
+ * as MILLISECONDS. An operator who types `3600` meaning "an hour" gets
6034
+ * a token that dies in 3.6 seconds. A bare number is therefore rejected
6035
+ * rather than interpreted — the field's own description, placeholder and
6036
+ * default are all duration strings (`24h`, `7d`, `30d`).
6037
+ *
6038
+ * A rejection never becomes an unbounded lifetime — it resolves to
6039
+ * {@link DEFAULT_TOKEN_EXPIRY}, the single documented default — and it is
6040
+ * never silent: `parseTokenExpiry` reports the refusal in its result and
6041
+ * `logTokenExpiryRejection` renders it. The two are separate so the caller
6042
+ * owns how often it speaks; the signer re-reads on every mint.
6043
+ */
6044
+ /**
6045
+ * The `ms`-compatible duration grammar `jsonwebtoken` accepts, MINUS the bare
6046
+ * number (see the docblock above). A unit is mandatory.
6047
+ */
6048
+ var DURATION_PATTERN = /^(\d+(?:\.\d+)?)\s*(ms|milliseconds?|msecs?|s|secs?|seconds?|m|mins?|minutes?|h|hrs?|hours?|d|days?|w|weeks?|y|yrs?|years?)$/i;
6049
+ /** Multiplier from each accepted unit to milliseconds. */
6050
+ var UNIT_MS = {
6051
+ ms: 1,
6052
+ msec: 1,
6053
+ msecs: 1,
6054
+ millisecond: 1,
6055
+ milliseconds: 1,
6056
+ s: 1e3,
6057
+ sec: 1e3,
6058
+ secs: 1e3,
6059
+ second: 1e3,
6060
+ seconds: 1e3,
6061
+ m: 6e4,
6062
+ min: 6e4,
6063
+ mins: 6e4,
6064
+ minute: 6e4,
6065
+ minutes: 6e4,
6066
+ h: 36e5,
6067
+ hr: 36e5,
6068
+ hrs: 36e5,
6069
+ hour: 36e5,
6070
+ hours: 36e5,
6071
+ d: 864e5,
6072
+ day: 864e5,
6073
+ days: 864e5,
6074
+ w: 6048e5,
6075
+ week: 6048e5,
6076
+ weeks: 6048e5,
6077
+ y: 315576e5,
6078
+ yr: 315576e5,
6079
+ yrs: 315576e5,
6080
+ year: 315576e5,
6081
+ years: 315576e5
6082
+ };
6083
+ /**
6084
+ * Shortest accepted lifetime. Below a minute the token expires inside the
6085
+ * round trip that issued it and the operator sees "logged out immediately",
6086
+ * which reads as a broken server rather than as a bad setting.
6087
+ */
6088
+ var MIN_EXPIRY_MS = 6e4;
6089
+ /**
6090
+ * Longest accepted lifetime. A year is already far past "a device left
6091
+ * unopened for weeks"; anything longer is an infinite-lifetime token wearing
6092
+ * a duration string.
6093
+ */
6094
+ var MAX_EXPIRY_MS = 365 * 864e5;
6095
+ /** Milliseconds for a duration string, or `null` when it is not one. */
6096
+ function durationMs(candidate) {
6097
+ const match = DURATION_PATTERN.exec(candidate);
6098
+ if (match === null) return null;
6099
+ const amount = Number(match[1]);
6100
+ const unitMs = UNIT_MS[match[2].toLowerCase()];
6101
+ if (!Number.isFinite(amount) || unitMs === void 0) return null;
6102
+ return amount * unitMs;
6103
+ }
6104
+ /**
6105
+ * The default, pre-parsed. Computed from {@link DEFAULT_TOKEN_EXPIRY} rather
6106
+ * than written out again — a second spelling of the same default is the defect
6107
+ * this module exists to close.
6108
+ */
6109
+ var DEFAULT_SECONDS = (() => {
6110
+ const ms = durationMs("30d");
6111
+ /* c8 ignore next -- unreachable unless DEFAULT_TOKEN_EXPIRY stops being a duration */
6112
+ if (ms === null) throw new Error(`DEFAULT_TOKEN_EXPIRY is not a duration: 30d`);
6113
+ return ms / 1e3;
6114
+ })();
6115
+ function refuse(reason) {
6116
+ return {
6117
+ ok: false,
6118
+ reason,
6119
+ seconds: DEFAULT_SECONDS,
6120
+ value: "30d"
6121
+ };
6122
+ }
6123
+ /**
6124
+ * Parse a configured `auth.tokenExpiry`. An absent/blank value is not a
6125
+ * rejection — it is "operator never set it" and resolves to the default
6126
+ * without a log line.
6127
+ */
6128
+ function parseTokenExpiry(configured) {
6129
+ if (configured === void 0 || configured === null) return {
6130
+ ok: true,
6131
+ seconds: DEFAULT_SECONDS,
6132
+ value: "30d"
6133
+ };
6134
+ if (typeof configured !== "string") return refuse("not-a-string");
6135
+ const trimmed = configured.trim();
6136
+ if (trimmed === "") return {
6137
+ ok: true,
6138
+ seconds: DEFAULT_SECONDS,
6139
+ value: "30d"
6140
+ };
6141
+ const totalMs = durationMs(trimmed);
6142
+ if (totalMs === null) return refuse("malformed");
6143
+ if (totalMs < MIN_EXPIRY_MS) return refuse("too-short");
6144
+ if (totalMs > MAX_EXPIRY_MS) return refuse("too-long");
6145
+ return {
6146
+ ok: true,
6147
+ seconds: totalMs / 1e3,
6148
+ value: trimmed
6149
+ };
6150
+ }
6151
+ /** Human-readable tail for the rejection log line. */
6152
+ var REJECTION_DETAIL = {
6153
+ "not-a-string": "value is not a string",
6154
+ malformed: "not a duration string (expected e.g. 24h, 7d, 30d — a bare number is NOT seconds)",
6155
+ "too-short": `shorter than the ${MIN_EXPIRY_MS / 1e3}s minimum`,
6156
+ "too-long": `longer than the ${MAX_EXPIRY_MS / 864e5}d maximum`
6157
+ };
6158
+ /**
6159
+ * Report a refused `auth.tokenExpiry`. Separate from {@link parseTokenExpiry}
6160
+ * so the caller owns HOW OFTEN it speaks — the signer re-reads the setting on
6161
+ * every mint and must not turn one bad save into a line per login.
6162
+ */
6163
+ function logTokenExpiryRejection(configured, rejection, logger) {
6164
+ logger.warn(`auth.tokenExpiry REJECTED — ${REJECTION_DETAIL[rejection.reason]}; signing with the default 30d instead`, { meta: {
6165
+ configured,
6166
+ reason: rejection.reason,
6167
+ applied: rejection.value
6168
+ } });
6169
+ }
6170
+ //#endregion
6171
+ //#region src/auth/auth-manager.ts
6022
6172
  /** Minimal no-op logger for default parameter */
6023
6173
  var noopLogger = {
6024
6174
  debug() {},
@@ -6036,6 +6186,16 @@ var AuthManager = class {
6036
6186
  config;
6037
6187
  jwtSecret;
6038
6188
  logger;
6189
+ /**
6190
+ * Offending `auth.tokenExpiry` values already reported by this instance.
6191
+ *
6192
+ * `signToken` re-reads the setting on EVERY mint so an operator's change
6193
+ * takes effect without a restart — which also means a bad value would log
6194
+ * once per login and once per silent refresh from every viewer. The refusal
6195
+ * must be visible, not repeated: the first mint after a bad save logs, the
6196
+ * rest resolve silently to the same default until the value changes.
6197
+ */
6198
+ reportedInvalidExpiries = /* @__PURE__ */ new Set();
6039
6199
  constructor(config, logger = noopLogger) {
6040
6200
  this.config = config;
6041
6201
  this.logger = logger;
@@ -6050,8 +6210,15 @@ var AuthManager = class {
6050
6210
  }
6051
6211
  signToken(payload) {
6052
6212
  const configured = this.config.get("auth.tokenExpiry");
6053
- const expiresIn = typeof configured === "string" && configured.trim() !== "" ? configured.trim() : "30d";
6054
- return import_jsonwebtoken.sign({ ...payload }, this.jwtSecret, { expiresIn });
6213
+ const parsed = parseTokenExpiry(configured);
6214
+ if (!parsed.ok) {
6215
+ const seen = typeof configured === "string" ? configured : JSON.stringify(configured);
6216
+ if (!this.reportedInvalidExpiries.has(seen)) {
6217
+ this.reportedInvalidExpiries.add(seen);
6218
+ logTokenExpiryRejection(configured, parsed, this.logger);
6219
+ }
6220
+ }
6221
+ return import_jsonwebtoken.sign({ ...payload }, this.jwtSecret, { expiresIn: parsed.seconds });
6055
6222
  }
6056
6223
  verifyToken(token) {
6057
6224
  return import_jsonwebtoken.verify(token, this.jwtSecret);
@@ -8822,6 +8989,38 @@ var TotpManager = class {
8822
8989
  }
8823
8990
  };
8824
8991
  //#endregion
8992
+ //#region src/builtins/local-auth/auth-config-reader.ts
8993
+ /**
8994
+ * Every config path this reader can answer. Anything else resolves to
8995
+ * `undefined` by construction — see the module docblock.
8996
+ */
8997
+ function createAuthConfigReader(resolved, writeAuthSection) {
8998
+ return {
8999
+ get(path) {
9000
+ switch (path) {
9001
+ case "auth.jwtSecret": return narrow(resolved.jwtSecret);
9002
+ case "auth.adminUsername": return narrow(resolved.adminUsername);
9003
+ case "auth.adminPassword": return narrow(resolved.adminPassword);
9004
+ case "auth.tokenExpiry": return narrow(resolved.tokenExpiry);
9005
+ default: return;
9006
+ }
9007
+ },
9008
+ update(_section, data) {
9009
+ if (typeof data["jwtSecret"] === "string") writeAuthSection({ jwtSecret: data["jwtSecret"] });
9010
+ }
9011
+ };
9012
+ }
9013
+ /**
9014
+ * The one documented type boundary in this module. `AuthConfigReader.get` is
9015
+ * the legacy `get<T>(path)` shape shared with `ConfigManager.get`, which is
9016
+ * likewise unchecked at runtime — every value returned here is a `string |
9017
+ * undefined` the caller immediately re-validates (`parseTokenExpiry`, the
9018
+ * truthiness gate on `jwtSecret`).
9019
+ */
9020
+ function narrow(value) {
9021
+ return value;
9022
+ }
9023
+ //#endregion
8825
9024
  //#region src/builtins/local-auth/auth-schema.ts
8826
9025
  /**
8827
9026
  * @durable class=config owner=local-auth
@@ -9470,19 +9669,14 @@ var LocalAuthAddon = class extends require_dist.BaseAddon {
9470
9669
  }
9471
9670
  async onInitialize() {
9472
9671
  const authSection = await this.ctx.settings?.getSection("auth") ?? {};
9473
- const resolvedJwtSecret = typeof authSection["jwtSecret"] === "string" ? authSection["jwtSecret"] : this.config.jwtSecret ?? "";
9474
- const resolvedAdminUser = typeof authSection["adminUsername"] === "string" && authSection["adminUsername"] ? authSection["adminUsername"] : this.config.adminUsername ?? "";
9475
- const resolvedAdminPass = typeof authSection["adminPassword"] === "string" && authSection["adminPassword"] ? authSection["adminPassword"] : this.config.adminPassword ?? "";
9476
- const reader = {
9477
- get(path) {
9478
- if (path === "auth.jwtSecret") return resolvedJwtSecret;
9479
- if (path === "auth.adminUsername") return resolvedAdminUser;
9480
- if (path === "auth.adminPassword") return resolvedAdminPass;
9481
- },
9482
- update: (_section, data) => {
9483
- if (typeof data["jwtSecret"] === "string") this.ctx.settings?.setSection("auth", { jwtSecret: data["jwtSecret"] });
9484
- }
9485
- };
9672
+ const reader = createAuthConfigReader({
9673
+ jwtSecret: typeof authSection["jwtSecret"] === "string" ? authSection["jwtSecret"] : this.config.jwtSecret ?? "",
9674
+ adminUsername: typeof authSection["adminUsername"] === "string" && authSection["adminUsername"] ? authSection["adminUsername"] : this.config.adminUsername ?? "",
9675
+ adminPassword: typeof authSection["adminPassword"] === "string" && authSection["adminPassword"] ? authSection["adminPassword"] : this.config.adminPassword ?? "",
9676
+ tokenExpiry: typeof authSection["tokenExpiry"] === "string" && authSection["tokenExpiry"] ? authSection["tokenExpiry"] : void 0
9677
+ }, (patch) => {
9678
+ this.ctx.settings?.setSection("auth", patch);
9679
+ });
9486
9680
  this.authManager = new AuthManager(reader, this.ctx.logger);
9487
9681
  const store = this.ctx.api?.settingsStore;
9488
9682
  if (store) {