@camstack/server 1.2.592 → 1.2.594

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 (256) hide show
  1. package/dist/addon-root-inventory.js +11 -50
  2. package/dist/agent/addon-scan.js +6 -44
  3. package/dist/agent/agent-addon-runtime.js +3 -8
  4. package/dist/agent/agent-addon-updates.js +12 -52
  5. package/dist/agent/agent-cap-dispatch-service.js +3 -5
  6. package/dist/agent/agent-config.js +7 -42
  7. package/dist/agent/agent-deploy-swap.js +5 -41
  8. package/dist/agent/agent-group-runner.js +8 -47
  9. package/dist/agent/agent-http-auth.js +10 -22
  10. package/dist/agent/agent-http.js +69 -115
  11. package/dist/agent/agent-hub-addon-update.js +10 -16
  12. package/dist/agent/agent-hub-registration.js +4 -7
  13. package/dist/agent/agent-infra-capabilities.js +5 -9
  14. package/dist/agent/agent-json-file.js +5 -42
  15. package/dist/agent/agent-mesh-gate.js +5 -8
  16. package/dist/agent/agent-node-certificates.js +25 -28
  17. package/dist/agent/agent-pairing-local.js +10 -47
  18. package/dist/agent/agent-pairing-routes.js +16 -20
  19. package/dist/agent/agent-pairing-store.js +18 -58
  20. package/dist/agent/agent-reconnect.js +1 -4
  21. package/dist/agent/agent-service.js +42 -78
  22. package/dist/agent/agent-sniffing-listener.js +11 -48
  23. package/dist/agent/agent-undeploy.js +5 -41
  24. package/dist/agent/agent-update-routes.js +14 -19
  25. package/dist/agent/agent-update-service.js +38 -80
  26. package/dist/agent/apply-model-distribution.js +1 -4
  27. package/dist/agent/apply-stage-dev-uploads.js +6 -9
  28. package/dist/agent/derive-hub-url.js +3 -8
  29. package/dist/agent/fetch-bundle-from-hub.js +6 -9
  30. package/dist/agent/hubs-found.js +6 -43
  31. package/dist/agent/infra-boot-guard.js +1 -4
  32. package/dist/agent/infra-boot-plan.js +2 -6
  33. package/dist/agent/main.js +158 -186
  34. package/dist/agent/register-agent-cap-dispatch.js +3 -6
  35. package/dist/agent/serial-queue.js +2 -7
  36. package/dist/agent-status-page.js +1 -4
  37. package/dist/api/addon-upload.js +22 -61
  38. package/dist/api/agent-addon-delivery.js +6 -11
  39. package/dist/api/auth-whoami.js +1 -4
  40. package/dist/api/backup-download-pump.js +5 -9
  41. package/dist/api/backup-download.js +7 -10
  42. package/dist/api/core/addon-settings.router.js +16 -19
  43. package/dist/api/core/agent-pairing.router.js +76 -79
  44. package/dist/api/core/agents.router.js +21 -24
  45. package/dist/api/core/auth-admin-tokens.procedures.js +48 -52
  46. package/dist/api/core/auth-device-cert.procedures.js +62 -67
  47. package/dist/api/core/auth-limits.js +16 -25
  48. package/dist/api/core/auth-reauth.procedures.js +32 -37
  49. package/dist/api/core/auth-revoke-credentials.procedures.js +22 -26
  50. package/dist/api/core/auth-sessions.procedures.js +52 -56
  51. package/dist/api/core/auth.router.js +300 -304
  52. package/dist/api/core/cap-providers.js +74 -121
  53. package/dist/api/core/capabilities.router.js +34 -37
  54. package/dist/api/core/capability-preference-writes.js +10 -17
  55. package/dist/api/core/certificates.router.js +106 -109
  56. package/dist/api/core/cluster-nodes.router.js +15 -18
  57. package/dist/api/core/collection-preference.js +2 -6
  58. package/dist/api/core/event-bus-proxy.router.js +34 -37
  59. package/dist/api/core/failure-contributions.js +4 -8
  60. package/dist/api/core/hwaccel.router.js +17 -20
  61. package/dist/api/core/integration-secrets.js +8 -12
  62. package/dist/api/core/lifecycle-job-runner.js +13 -49
  63. package/dist/api/core/live-events.router.js +29 -32
  64. package/dist/api/core/load-contributions.js +4 -8
  65. package/dist/api/core/logging-settings.js +30 -43
  66. package/dist/api/core/logs.router.js +40 -43
  67. package/dist/api/core/notifications.router.js +15 -18
  68. package/dist/api/core/public-event-filter.js +4 -8
  69. package/dist/api/core/repl.router.js +18 -21
  70. package/dist/api/core/request-census-settings.js +11 -16
  71. package/dist/api/core/server-management.provider.js +1 -4
  72. package/dist/api/core/server-name.js +9 -17
  73. package/dist/api/core/site-location.js +16 -24
  74. package/dist/api/core/stream-probe.router.js +21 -24
  75. package/dist/api/core/system-events.router.js +23 -26
  76. package/dist/api/cors/cors-policy.js +20 -29
  77. package/dist/api/cors/register-cors-policy.js +9 -14
  78. package/dist/api/deploy-stage-registry.js +6 -10
  79. package/dist/api/dev-uploads-bundle.js +12 -50
  80. package/dist/api/health/adaptive-probe.routes.js +9 -13
  81. package/dist/api/health/health.routes.js +8 -13
  82. package/dist/api/hub-cluster-url.js +3 -9
  83. package/dist/api/model-distributor.js +12 -50
  84. package/dist/api/oauth2/consent-page.js +10 -13
  85. package/dist/api/oauth2/oauth2-routes.js +17 -25
  86. package/dist/api/oauth2/private-host.js +1 -4
  87. package/dist/api/server-upload-propagate.js +15 -51
  88. package/dist/api/server-upload.js +13 -50
  89. package/dist/api/sse-no-compression.js +1 -4
  90. package/dist/api/static/frame-policy.js +4 -9
  91. package/dist/api/static/precompressed-asset.js +15 -57
  92. package/dist/api/static/spa-mount.js +8 -47
  93. package/dist/api/static/spa-static.js +5 -12
  94. package/dist/api/tarball-manifest.js +7 -44
  95. package/dist/api/trpc/cap-mount-helpers.js +7 -12
  96. package/dist/api/trpc/cap-route-error-formatter.js +10 -13
  97. package/dist/api/trpc/cap-router-runtime.js +40 -51
  98. package/dist/api/trpc/client-ip.js +9 -20
  99. package/dist/api/trpc/core-cap-bridge.js +32 -42
  100. package/dist/api/trpc/device-collection-dispatch.js +23 -27
  101. package/dist/api/trpc/device-config-secret-redaction.js +24 -40
  102. package/dist/api/trpc/generated-cap-mounts.js +219 -221
  103. package/dist/api/trpc/generated-cap-routers.js +4878 -4726
  104. package/dist/api/trpc/http-request-census-hook.js +8 -11
  105. package/dist/api/trpc/http-request-census.js +34 -41
  106. package/dist/api/trpc/http-request-census.singleton.js +4 -8
  107. package/dist/api/trpc/localized-schema-paths.js +1 -4
  108. package/dist/api/trpc/principal-visibility.js +14 -22
  109. package/dist/api/trpc/remote-addon-update-grant.js +1 -4
  110. package/dist/api/trpc/scope-access.js +10 -15
  111. package/dist/api/trpc/server-provided-caps.js +3 -7
  112. package/dist/api/trpc/share-view-access.js +6 -14
  113. package/dist/api/trpc/trpc-error-device-tags.js +3 -6
  114. package/dist/api/trpc/trpc-error-principal.js +2 -6
  115. package/dist/api/trpc/trpc.context.js +20 -25
  116. package/dist/api/trpc/trpc.middleware.js +38 -46
  117. package/dist/api/trpc/trpc.router.js +58 -99
  118. package/dist/api/trpc/user-error.js +5 -8
  119. package/dist/api/trpc/ws-request-census.js +17 -24
  120. package/dist/api/trpc/ws-slow-consumer-guard.js +11 -16
  121. package/dist/api/upload-auth.js +5 -9
  122. package/dist/auth/addon-route-jwt-gate.js +4 -7
  123. package/dist/auth/addon-route-share-gate.js +7 -12
  124. package/dist/auth/app-confirm-page.js +17 -22
  125. package/dist/auth/app-confirm-routes.js +26 -31
  126. package/dist/auth/app-confirmations.js +7 -44
  127. package/dist/auth/app-page-shell.js +17 -30
  128. package/dist/auth/app-page-strings.js +11 -17
  129. package/dist/auth/app-passkey-page.js +16 -21
  130. package/dist/auth/app-passkey-routes.js +37 -41
  131. package/dist/auth/app-refused-route.js +13 -18
  132. package/dist/auth/auth-network-policy.js +11 -15
  133. package/dist/auth/auth-provider-gate.js +4 -9
  134. package/dist/auth/auth-rate-limit.js +3 -7
  135. package/dist/auth/client-address.js +6 -9
  136. package/dist/auth/device-enroll-proofs.js +6 -43
  137. package/dist/auth/external-link-intents.js +3 -7
  138. package/dist/auth/hub-sessions.js +6 -12
  139. package/dist/auth/native-link-completions.js +7 -11
  140. package/dist/auth/native-return.js +9 -13
  141. package/dist/auth/passkey-provider.js +26 -32
  142. package/dist/auth/second-factors.js +6 -10
  143. package/dist/auth/session-connections.js +6 -12
  144. package/dist/auth/session-cookie.js +12 -23
  145. package/dist/auth/session-lifetimes.js +8 -12
  146. package/dist/auth/session-mint.js +4 -11
  147. package/dist/auth/sso-finish-app.js +13 -16
  148. package/dist/auth/sso-finish-deps.js +21 -27
  149. package/dist/auth/sso-finish.js +28 -38
  150. package/dist/auth/sso-routes.js +22 -25
  151. package/dist/auth/used-bridge-ids.js +1 -5
  152. package/dist/boot/boot-config.js +16 -55
  153. package/dist/boot/init-sequence.js +1 -4
  154. package/dist/boot/integration-id-backfill.js +3 -8
  155. package/dist/boot/platform-keychain-key.js +5 -42
  156. package/dist/boot/post-boot.service.js +14 -18
  157. package/dist/boot/reconcile-lifecycle-jobs.js +3 -6
  158. package/dist/boot/retired-public-url-env.js +4 -8
  159. package/dist/bootstrap-packages.js +1 -4
  160. package/dist/closure-node-modules.js +2 -38
  161. package/dist/closure-patches/patch-reach.json +6 -2
  162. package/dist/core/addon/addon-call-gateway.js +1 -5
  163. package/dist/core/addon/addon-package.service.js +83 -123
  164. package/dist/core/addon/addon-process-view.js +2 -6
  165. package/dist/core/addon/addon-realign-plan.js +1 -4
  166. package/dist/core/addon/addon-registry.service.js +254 -197
  167. package/dist/core/addon/addon-row-manifest.js +1 -4
  168. package/dist/core/addon/addon-search.service.js +3 -7
  169. package/dist/core/addon/addon-settings-provider.js +1 -3
  170. package/dist/core/addon/addon-uninstall.js +4 -11
  171. package/dist/core/addon/addon.tokens.js +2 -5
  172. package/dist/core/addon/device-meta-mirror.js +8 -13
  173. package/dist/core/addon/forked-custom-actions.js +6 -11
  174. package/dist/core/addon/integration-visibility.js +3 -6
  175. package/dist/core/addon/materialize-route-path.js +1 -4
  176. package/dist/core/addon/package-dir-utils.js +8 -45
  177. package/dist/core/addon/prune-misplaced-addons.js +5 -43
  178. package/dist/core/addon/runner-convergence.js +1 -4
  179. package/dist/core/addon/runner-serialization.js +1 -4
  180. package/dist/core/addon/runner-spawn-fanout.js +3 -8
  181. package/dist/core/addon/settings-door-wiring.js +5 -8
  182. package/dist/core/addon/single-flight-refresh.js +1 -4
  183. package/dist/core/addon-bridge/addon-bridge.service.js +7 -44
  184. package/dist/core/addon-pages/addon-pages.service.js +3 -40
  185. package/dist/core/addon-widgets/addon-widgets.service.js +4 -42
  186. package/dist/core/agent/agent-addon-backfill.js +9 -17
  187. package/dist/core/agent/agent-delivery-ledger.js +3 -7
  188. package/dist/core/agent/agent-liveness-event.js +2 -6
  189. package/dist/core/agent/agent-pairing-http.js +41 -45
  190. package/dist/core/agent/agent-registry.service.js +53 -92
  191. package/dist/core/agent/cluster-node-history-mirror.js +3 -7
  192. package/dist/core/agent/cluster-node-history-store.js +113 -58
  193. package/dist/core/agent/hub-agent-pairing.js +1 -5
  194. package/dist/core/agent/node-certificates-client.js +3 -6
  195. package/dist/core/agent/node-directory.js +1 -4
  196. package/dist/core/app-links/well-known-app-links.js +2 -6
  197. package/dist/core/auth/auth.service.js +3 -8
  198. package/dist/core/auth/handoff-code.service.js +11 -49
  199. package/dist/core/auth/record-kinds.js +35 -0
  200. package/dist/core/auth/share-token.service.js +174 -122
  201. package/dist/core/capability/capability.service.js +1 -5
  202. package/dist/core/config/config.schema.js +1 -6
  203. package/dist/core/config/config.service.js +2 -6
  204. package/dist/core/events/event-bus.service.js +3 -7
  205. package/dist/core/feature/feature.service.js +2 -6
  206. package/dist/core/lifecycle/lifecycle-runner.singleton.js +5 -10
  207. package/dist/core/lifecycle/lifecycle-state-machine.js +1 -5
  208. package/dist/core/logging/log-ring-buffer.js +1 -5
  209. package/dist/core/logging/logging.service.js +5 -9
  210. package/dist/core/logging/scoped-logger.js +1 -5
  211. package/dist/core/moleculer/agent-readiness-pull.js +1 -4
  212. package/dist/core/moleculer/agent-registration-request.js +3 -7
  213. package/dist/core/moleculer/cap-call-fn.js +39 -5
  214. package/dist/core/moleculer/cap-route-authority.js +3 -8
  215. package/dist/core/moleculer/hub-mesh-guard.js +5 -9
  216. package/dist/core/moleculer/moleculer.service.js +108 -119
  217. package/dist/core/network/network-quality.service.js +2 -6
  218. package/dist/core/notification/notification-wrapper.service.js +3 -7
  219. package/dist/core/notification/toast-wrapper.service.js +3 -7
  220. package/dist/core/provider/provider.tokens.js +1 -4
  221. package/dist/core/repl/repl-engine.service.js +4 -8
  222. package/dist/core/runner-health/runner-boot-check.js +7 -15
  223. package/dist/core/runner-health/update-preflight.js +9 -14
  224. package/dist/core/server-update/server-update.service.js +12 -49
  225. package/dist/core/server-update/system-apply-closure-patches.js +6 -42
  226. package/dist/core/server-update/system-ensure-prebuilds.js +3 -6
  227. package/dist/core/server-update/system-exec-npm.js +2 -5
  228. package/dist/core/storage/fs-storage-backend.js +1 -5
  229. package/dist/core/storage/storage-location-manager.js +1 -5
  230. package/dist/core/storage/storage.service.js +2 -6
  231. package/dist/core/streaming/ffmpeg-stream-info.js +3 -8
  232. package/dist/core/streaming/stream-probe.service.js +12 -16
  233. package/dist/core/streaming/ttl-cache.js +1 -5
  234. package/dist/core/topology/topology-emitter.service.js +19 -23
  235. package/dist/core/update-availability-emitter.js +3 -7
  236. package/dist/core/update-availability-store.js +8 -45
  237. package/dist/core/updates/agent-installed-packages.js +5 -9
  238. package/dist/core/updates/restart-marker.js +3 -8
  239. package/dist/core/updates/update-check-scheduler.js +13 -18
  240. package/dist/core/updates/wrapper-update-checker.js +3 -7
  241. package/dist/first-boot-addon-plan.js +2 -6
  242. package/dist/framework-nodepath.js +2 -38
  243. package/dist/i18n/request-lang.js +7 -11
  244. package/dist/launcher.js +30 -65
  245. package/dist/lifecycle-journal-path.js +2 -38
  246. package/dist/main.js +287 -324
  247. package/dist/manual-boot.js +120 -157
  248. package/dist/node-role.js +1 -4
  249. package/dist/package-inventory.js +7 -48
  250. package/dist/pending-restore.js +7 -43
  251. package/dist/single-copy-cleanup-runner.js +9 -45
  252. package/dist/single-copy-cleanup.js +7 -15
  253. package/dist/sqlite-restore.js +9 -48
  254. package/dist/starter.js +4 -39
  255. package/dist/ui-serving/lazy-ui-serving.js +3 -39
  256. package/package.json +13 -13
@@ -1,39 +1,3 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.ShareTokenService = exports.ShareTokenRecordSchema = exports.ShareTokenScopeSchema = exports.SHARE_TOKEN_MAX_DEVICES = exports.SHARE_TOKEN_TTL_DEFAULT_SEC = exports.SHARE_TOKEN_TTL_MAX_SEC = exports.SHARE_TOKEN_TTL_MIN_SEC = exports.SHARE_TOKEN_PREFIX = void 0;
37
1
  /**
38
2
  * ShareTokenService — scoped, TTL'd VIEW tokens for standalone share links
39
3
  * (grid rework R6: the embed page opened by a third party authenticates with
@@ -58,9 +22,10 @@ exports.ShareTokenService = exports.ShareTokenRecordSchema = exports.ShareTokenS
58
22
  * live in, reached directly instead of over the settings-store cap since
59
23
  * this service is hub-resident). Collection: `share_view_tokens`.
60
24
  */
61
- const crypto = __importStar(require("node:crypto"));
62
- const zod_1 = require("zod");
63
- const types_1 = require("@camstack/types");
25
+ import * as crypto from 'node:crypto';
26
+ import { z } from 'zod';
27
+ import { SHARE_VIEW_KINDS, userError } from '@camstack/types';
28
+ import { coreAuthRecordKinds } from './record-kinds.js';
64
29
  /**
65
30
  * @durable class=config owner=core-auth
66
31
  * write="a user mints a share link; and a best-effort lastUsedAt touch on every successful validation"
@@ -91,12 +56,12 @@ const SHARE_TOKENS_INDEXES = [
91
56
  { name: 'idx_share_view_tokens_user', columns: ['userId'] },
92
57
  ];
93
58
  /** Wire prefix — `csv_` = CamStack Share View (cf. `cst_` scoped tokens). */
94
- exports.SHARE_TOKEN_PREFIX = 'csv_';
59
+ export const SHARE_TOKEN_PREFIX = 'csv_';
95
60
  // ── TTL / scope bounds (enforced at mint, re-checked by the router schema) ──
96
- exports.SHARE_TOKEN_TTL_MIN_SEC = 60;
97
- exports.SHARE_TOKEN_TTL_MAX_SEC = 30 * 24 * 60 * 60; // 30 days
98
- exports.SHARE_TOKEN_TTL_DEFAULT_SEC = 7 * 24 * 60 * 60; // 7 days
99
- exports.SHARE_TOKEN_MAX_DEVICES = 64;
61
+ export const SHARE_TOKEN_TTL_MIN_SEC = 60;
62
+ export const SHARE_TOKEN_TTL_MAX_SEC = 30 * 24 * 60 * 60; // 30 days
63
+ export const SHARE_TOKEN_TTL_DEFAULT_SEC = 7 * 24 * 60 * 60; // 7 days
64
+ export const SHARE_TOKEN_MAX_DEVICES = 64;
100
65
  /**
101
66
  * What a share token is allowed to see. Keyed on `kind` so future share
102
67
  * surfaces (single-camera view, recording clip, …) extend the set without
@@ -115,9 +80,9 @@ exports.SHARE_TOKEN_MAX_DEVICES = 64;
115
80
  * Neither kind is a superset of the other — an events link cannot open a
116
81
  * WebRTC session, and a grid link cannot read a track.
117
82
  */
118
- exports.ShareTokenScopeSchema = zod_1.z.object({
119
- kind: zod_1.z.enum(types_1.SHARE_VIEW_KINDS),
120
- deviceIds: zod_1.z.array(zod_1.z.number().int().nonnegative()).min(1).max(exports.SHARE_TOKEN_MAX_DEVICES),
83
+ export const ShareTokenScopeSchema = z.object({
84
+ kind: z.enum(SHARE_VIEW_KINDS),
85
+ deviceIds: z.array(z.number().int().nonnegative()).min(1).max(SHARE_TOKEN_MAX_DEVICES),
121
86
  /**
122
87
  * TALK-BACK, opt-in at mint time. Absent means OFF, and that is the whole
123
88
  * point: a share link is handed to third parties, so the difference between
@@ -133,27 +98,50 @@ exports.ShareTokenScopeSchema = zod_1.z.object({
133
98
  * It widens WHAT may be called, never WHICH cameras — `deviceIds` still
134
99
  * gates every talk call.
135
100
  */
136
- talk: zod_1.z.boolean().optional(),
101
+ talk: z.boolean().optional(),
137
102
  });
138
103
  /** Persisted record — never leaves the server with `tokenHash` attached. */
139
- exports.ShareTokenRecordSchema = zod_1.z.object({
140
- id: zod_1.z.string(),
104
+ export const ShareTokenRecordSchema = z.object({
105
+ id: z.string(),
141
106
  /** The user that minted the share link (owner — may revoke it). */
142
- userId: zod_1.z.string(),
143
- tokenHash: zod_1.z.string(),
107
+ userId: z.string(),
108
+ tokenHash: z.string(),
144
109
  /** First 12 chars of the raw token — display/audit label only. */
145
- tokenPrefix: zod_1.z.string(),
146
- scope: exports.ShareTokenScopeSchema,
147
- createdAt: zod_1.z.number(),
110
+ tokenPrefix: z.string(),
111
+ scope: ShareTokenScopeSchema,
112
+ createdAt: z.number(),
148
113
  /** Epoch ms — or `null` for a never-expiring token (explicit choice at mint). */
149
- expiresAt: zod_1.z.number().nullable(),
150
- lastUsedAt: zod_1.z.number().optional(),
114
+ expiresAt: z.number().nullable(),
115
+ lastUsedAt: z.number().optional(),
151
116
  });
152
117
  function parseShareToken(data) {
153
- const parsed = exports.ShareTokenRecordSchema.safeParse(data);
118
+ const parsed = ShareTokenRecordSchema.safeParse(data);
154
119
  return parsed.success ? parsed.data : null;
155
120
  }
156
- class ShareTokenService {
121
+ function recordToBody(record) {
122
+ const { id: _id, ...body } = record;
123
+ return body;
124
+ }
125
+ function rowToRecord(id, body) {
126
+ return ShareTokenRecordSchema.safeParse({ id, ...body }).data ?? null;
127
+ }
128
+ async function readAllShareTokenRows(kind, where) {
129
+ const out = [];
130
+ let cursor;
131
+ for (let page = 0; page < 100; page += 1) {
132
+ const answer = await kind.query({
133
+ ...(where === undefined ? {} : { where }),
134
+ limit: 1000,
135
+ ...(cursor === undefined ? {} : { cursor }),
136
+ });
137
+ out.push(...answer.records);
138
+ if (answer.nextCursor === null)
139
+ return out;
140
+ cursor = answer.nextCursor;
141
+ }
142
+ throw new Error('share-view-tokens: row count exceeds pagination bound');
143
+ }
144
+ export class ShareTokenService {
157
145
  getStore;
158
146
  logger;
159
147
  /**
@@ -162,9 +150,59 @@ class ShareTokenService {
162
150
  * the sqlite builtin registers after this service is constructed).
163
151
  */
164
152
  collectionDeclared = false;
165
- constructor(getStore, logger = null) {
153
+ resolveKind;
154
+ constructor(getStore, logger = null, resolveKind = () => null) {
166
155
  this.getStore = getStore;
167
156
  this.logger = logger;
157
+ this.resolveKind = resolveKind;
158
+ }
159
+ /** Hub wiring after `records.forOwner('core-auth').declare(...)`. */
160
+ bindShareTokensKind(resolver) {
161
+ this.resolveKind = resolver;
162
+ this.collectionDeclared = true;
163
+ }
164
+ kind() {
165
+ return this.resolveKind();
166
+ }
167
+ async findById(id) {
168
+ const kind = this.kind();
169
+ if (kind !== null) {
170
+ const row = await kind.get(id);
171
+ return row === null ? null : rowToRecord(row.id, row.data);
172
+ }
173
+ const results = await (await this.store()).query({
174
+ collection: SHARE_TOKENS_COLLECTION,
175
+ filter: { where: { id } },
176
+ });
177
+ const first = results[0];
178
+ return first === undefined ? null : parseShareToken(first.data);
179
+ }
180
+ async persistRecord(record) {
181
+ const kind = this.kind();
182
+ if (kind !== null) {
183
+ const parsed = coreAuthRecordKinds['share-view-tokens'].schema.safeParse(recordToBody(record));
184
+ if (!parsed.success)
185
+ throw new Error(parsed.error.message);
186
+ await kind.put({
187
+ id: record.id,
188
+ expiresAt: record.expiresAt ?? undefined,
189
+ data: parsed.data,
190
+ });
191
+ return;
192
+ }
193
+ await (await this.store()).update({
194
+ collection: SHARE_TOKENS_COLLECTION,
195
+ id: record.id,
196
+ data: { ...record },
197
+ });
198
+ }
199
+ async deleteById(id) {
200
+ const kind = this.kind();
201
+ if (kind !== null) {
202
+ await kind.delete(id);
203
+ return;
204
+ }
205
+ await (await this.store()).delete({ collection: SHARE_TOKENS_COLLECTION, key: id });
168
206
  }
169
207
  /**
170
208
  * Resolve the backend AND ensure `share_view_tokens` is declared before
@@ -174,6 +212,13 @@ class ShareTokenService {
174
212
  * rows keep working with no migration.
175
213
  */
176
214
  async store() {
215
+ if (this.kind() !== null) {
216
+ const store = this.getStore();
217
+ if (!store) {
218
+ throw new Error('Share tokens unavailable — settings backend not ready');
219
+ }
220
+ return store;
221
+ }
177
222
  const store = this.getStore();
178
223
  if (!store) {
179
224
  throw new Error('Share tokens unavailable — settings backend not ready');
@@ -199,16 +244,16 @@ class ShareTokenService {
199
244
  * Returns the RAW token exactly once; only its hash is persisted.
200
245
  */
201
246
  async create(input) {
202
- const scope = exports.ShareTokenScopeSchema.parse(input.scope);
203
- const ttlSec = input.ttlSec ?? exports.SHARE_TOKEN_TTL_DEFAULT_SEC;
247
+ const scope = ShareTokenScopeSchema.parse(input.scope);
248
+ const ttlSec = input.ttlSec ?? SHARE_TOKEN_TTL_DEFAULT_SEC;
204
249
  if (ttlSec !== 'never' &&
205
250
  (!Number.isInteger(ttlSec) ||
206
- ttlSec < exports.SHARE_TOKEN_TTL_MIN_SEC ||
207
- ttlSec > exports.SHARE_TOKEN_TTL_MAX_SEC)) {
251
+ ttlSec < SHARE_TOKEN_TTL_MIN_SEC ||
252
+ ttlSec > SHARE_TOKEN_TTL_MAX_SEC)) {
208
253
  throw new Error(`Share token TTL out of bounds — got ${ttlSec}s, allowed ` +
209
- `[${exports.SHARE_TOKEN_TTL_MIN_SEC}s, ${exports.SHARE_TOKEN_TTL_MAX_SEC}s] or 'never'`);
254
+ `[${SHARE_TOKEN_TTL_MIN_SEC}s, ${SHARE_TOKEN_TTL_MAX_SEC}s] or 'never'`);
210
255
  }
211
- const rawToken = `${exports.SHARE_TOKEN_PREFIX}${crypto.randomBytes(32).toString('hex')}`;
256
+ const rawToken = `${SHARE_TOKEN_PREFIX}${crypto.randomBytes(32).toString('hex')}`;
212
257
  const now = Date.now();
213
258
  const record = {
214
259
  id: crypto.randomUUID(),
@@ -219,10 +264,23 @@ class ShareTokenService {
219
264
  createdAt: now,
220
265
  expiresAt: ttlSec === 'never' ? null : now + ttlSec * 1000,
221
266
  };
222
- await (await this.store()).insert({
223
- collection: SHARE_TOKENS_COLLECTION,
224
- record: { id: record.id, data: { ...record } },
225
- });
267
+ const kind = this.kind();
268
+ if (kind !== null) {
269
+ const parsed = coreAuthRecordKinds['share-view-tokens'].schema.safeParse(recordToBody(record));
270
+ if (!parsed.success)
271
+ throw new Error(parsed.error.message);
272
+ await kind.put({
273
+ id: record.id,
274
+ expiresAt: record.expiresAt ?? undefined,
275
+ data: parsed.data,
276
+ });
277
+ }
278
+ else {
279
+ await (await this.store()).insert({
280
+ collection: SHARE_TOKENS_COLLECTION,
281
+ record: { id: record.id, data: { ...record } },
282
+ });
283
+ }
226
284
  this.logger?.info('Share token minted', {
227
285
  meta: {
228
286
  id: record.id,
@@ -240,18 +298,25 @@ class ShareTokenService {
240
298
  * Never throws on bad input — the auth boundary treats `null` as 401.
241
299
  */
242
300
  async validate(rawToken) {
243
- if (!rawToken.startsWith(exports.SHARE_TOKEN_PREFIX))
301
+ if (!rawToken.startsWith(SHARE_TOKEN_PREFIX))
244
302
  return null;
245
303
  const tokenHash = crypto.createHash('sha256').update(rawToken).digest('hex');
246
- const results = await (await this.store()).query({
247
- collection: SHARE_TOKENS_COLLECTION,
248
- filter: { where: { tokenHash } },
249
- });
250
- const first = results[0];
251
- if (!first)
252
- return null;
253
- const record = parseShareToken(first.data);
254
- if (!record)
304
+ const kind = this.kind();
305
+ let record = null;
306
+ if (kind !== null) {
307
+ const page = await kind.query({ where: { tokenHash }, limit: 1 });
308
+ const row = page.records[0];
309
+ record = row === undefined ? null : rowToRecord(row.id, row.data);
310
+ }
311
+ else {
312
+ const results = await (await this.store()).query({
313
+ collection: SHARE_TOKENS_COLLECTION,
314
+ filter: { where: { tokenHash } },
315
+ });
316
+ const first = results[0];
317
+ record = first === undefined ? null : parseShareToken(first.data);
318
+ }
319
+ if (record === null)
255
320
  return null;
256
321
  // `expiresAt: null` = never expires (explicit mint-time choice);
257
322
  // revocation remains the kill switch for those.
@@ -270,42 +335,30 @@ class ShareTokenService {
270
335
  */
271
336
  async extend(input) {
272
337
  if (!Number.isInteger(input.ttlSec) ||
273
- input.ttlSec < exports.SHARE_TOKEN_TTL_MIN_SEC ||
274
- input.ttlSec > exports.SHARE_TOKEN_TTL_MAX_SEC) {
338
+ input.ttlSec < SHARE_TOKEN_TTL_MIN_SEC ||
339
+ input.ttlSec > SHARE_TOKEN_TTL_MAX_SEC) {
275
340
  throw new Error(`Share token TTL out of bounds — got ${input.ttlSec}s, allowed ` +
276
- `[${exports.SHARE_TOKEN_TTL_MIN_SEC}s, ${exports.SHARE_TOKEN_TTL_MAX_SEC}s]`);
277
- }
278
- const results = await (await this.store()).query({
279
- collection: SHARE_TOKENS_COLLECTION,
280
- filter: { where: { id: input.id } },
281
- });
282
- const first = results[0];
283
- if (!first) {
284
- throw (0, types_1.userError)('auth.share-token-not-found', { id: input.id }, `Share token not found: ${input.id}`);
341
+ `[${SHARE_TOKEN_TTL_MIN_SEC}s, ${SHARE_TOKEN_TTL_MAX_SEC}s]`);
285
342
  }
286
- const record = parseShareToken(first.data);
287
- if (!record) {
288
- throw (0, types_1.userError)('auth.share-token-not-found', { id: input.id }, `Share token not found: ${input.id}`);
343
+ const record = await this.findById(input.id);
344
+ if (record === null) {
345
+ throw userError('auth.share-token-not-found', { id: input.id }, `Share token not found: ${input.id}`);
289
346
  }
290
347
  if (!input.callerIsAdmin && record.userId !== input.callerUserId) {
291
- throw (0, types_1.userError)('auth.share-owner-only-extend', undefined, 'Only the token owner or an admin can extend a share token');
348
+ throw userError('auth.share-owner-only-extend', undefined, 'Only the token owner or an admin can extend a share token');
292
349
  }
293
350
  if (record.expiresAt === null) {
294
- throw (0, types_1.userError)('auth.share-never-expiring-extend', undefined, 'A never-expiring share token cannot be extended');
351
+ throw userError('auth.share-never-expiring-extend', undefined, 'A never-expiring share token cannot be extended');
295
352
  }
296
353
  const now = Date.now();
297
354
  if (now > record.expiresAt) {
298
- throw (0, types_1.userError)('server.share-token-expired', { id: String(input.id) }, `Share token expired: ${input.id}`);
355
+ throw userError('server.share-token-expired', { id: String(input.id) }, `Share token expired: ${input.id}`);
299
356
  }
300
357
  const updated = {
301
358
  ...record,
302
359
  expiresAt: now + input.ttlSec * 1000,
303
360
  };
304
- await (await this.store()).update({
305
- collection: SHARE_TOKENS_COLLECTION,
306
- id: record.id,
307
- data: { ...updated },
308
- });
361
+ await this.persistRecord(updated);
309
362
  this.logger?.info('Share token extended', {
310
363
  meta: {
311
364
  id: updated.id,
@@ -321,23 +374,13 @@ class ShareTokenService {
321
374
  * permission mismatch so the router surfaces a FORBIDDEN.
322
375
  */
323
376
  async revoke(input) {
324
- const results = await (await this.store()).query({
325
- collection: SHARE_TOKENS_COLLECTION,
326
- filter: { where: { id: input.id } },
327
- });
328
- const first = results[0];
329
- if (!first)
377
+ const record = await this.findById(input.id);
378
+ if (record === null)
330
379
  return false;
331
- const record = parseShareToken(first.data);
332
- if (!record) {
333
- // Corrupt record — delete it regardless (it can never validate).
334
- await (await this.store()).delete({ collection: SHARE_TOKENS_COLLECTION, key: input.id });
335
- return true;
336
- }
337
380
  if (!input.callerIsAdmin && record.userId !== input.callerUserId) {
338
- throw (0, types_1.userError)('auth.share-owner-only-revoke', undefined, 'Only the token owner or an admin can revoke a share token');
381
+ throw userError('auth.share-owner-only-revoke', undefined, 'Only the token owner or an admin can revoke a share token');
339
382
  }
340
- await (await this.store()).delete({ collection: SHARE_TOKENS_COLLECTION, key: input.id });
383
+ await this.deleteById(input.id);
341
384
  this.logger?.info('Share token revoked', {
342
385
  meta: { id: record.id, byUserId: input.callerUserId },
343
386
  });
@@ -345,6 +388,13 @@ class ShareTokenService {
345
388
  }
346
389
  /** Every share token minted by `userId`, expired ones included. */
347
390
  async listForUser(userId) {
391
+ const kind = this.kind();
392
+ if (kind !== null) {
393
+ const rows = await readAllShareTokenRows(kind, { userId });
394
+ return rows
395
+ .map((row) => rowToRecord(row.id, row.data))
396
+ .filter((r) => r !== null);
397
+ }
348
398
  const results = await (await this.store()).query({
349
399
  collection: SHARE_TOKENS_COLLECTION,
350
400
  filter: { where: { userId } },
@@ -353,6 +403,13 @@ class ShareTokenService {
353
403
  }
354
404
  /** All share tokens (admin listing). */
355
405
  async listAll() {
406
+ const kind = this.kind();
407
+ if (kind !== null) {
408
+ const rows = await readAllShareTokenRows(kind);
409
+ return rows
410
+ .map((row) => rowToRecord(row.id, row.data))
411
+ .filter((r) => r !== null);
412
+ }
356
413
  const results = await (await this.store()).query({
357
414
  collection: SHARE_TOKENS_COLLECTION,
358
415
  filter: {},
@@ -360,11 +417,6 @@ class ShareTokenService {
360
417
  return results.map((r) => parseShareToken(r.data)).filter((r) => r !== null);
361
418
  }
362
419
  async touchLastUsed(record) {
363
- await (await this.store()).update({
364
- collection: SHARE_TOKENS_COLLECTION,
365
- id: record.id,
366
- data: { ...record, lastUsedAt: Date.now() },
367
- });
420
+ await this.persistRecord({ ...record, lastUsedAt: Date.now() });
368
421
  }
369
422
  }
370
- exports.ShareTokenService = ShareTokenService;
@@ -1,13 +1,10 @@
1
- "use strict";
2
1
  /**
3
2
  * CapabilityService — NestJS-injectable wrapper around the CapabilityRegistry.
4
3
  *
5
4
  * Server services inject this instead of accessing the registry directly.
6
5
  * The registry reference is set once during boot by AddonRegistryService.
7
6
  */
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.CapabilityService = void 0;
10
- class CapabilityService {
7
+ export class CapabilityService {
11
8
  registry = null;
12
9
  /** Called once during boot by AddonRegistryService to wire the registry */
13
10
  setRegistry(registry) {
@@ -55,4 +52,3 @@ class CapabilityService {
55
52
  return this.registry?.resolveCollectionForDevice(capability, deviceId) ?? [];
56
53
  }
57
54
  }
58
- exports.CapabilityService = CapabilityService;
@@ -1,7 +1,2 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RUNTIME_DEFAULTS = exports.bootstrapSchema = void 0;
4
1
  // Re-export from @camstack/system
5
- var system_1 = require("@camstack/system");
6
- Object.defineProperty(exports, "bootstrapSchema", { enumerable: true, get: function () { return system_1.bootstrapSchema; } });
7
- Object.defineProperty(exports, "RUNTIME_DEFAULTS", { enumerable: true, get: function () { return system_1.RUNTIME_DEFAULTS; } });
2
+ export { bootstrapSchema, RUNTIME_DEFAULTS } from '@camstack/system';
@@ -1,7 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ConfigService = void 0;
4
- const system_1 = require("@camstack/system");
5
- class ConfigService extends system_1.ConfigManager {
1
+ import { ConfigManager } from '@camstack/system';
2
+ export class ConfigService extends ConfigManager {
6
3
  }
7
- exports.ConfigService = ConfigService;
@@ -1,7 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EventBusService = void 0;
4
- const system_1 = require("@camstack/system");
1
+ import { getBrokerEventBus } from '@camstack/system';
5
2
  /**
6
3
  * Hub-side event bus. Pre-broker boot the service buffers nothing —
7
4
  * subscribers / emits are no-ops until `attachBroker` lands the
@@ -19,7 +16,7 @@ const system_1 = require("@camstack/system");
19
16
  * `systemEvents.subscribe`, which is enough for the operator to see
20
17
  * continuity across page reloads.
21
18
  */
22
- class EventBusService {
19
+ export class EventBusService {
23
20
  broker = null;
24
21
  inner = null;
25
22
  // Used only as a loose ring buffer for the very first events emitted
@@ -41,7 +38,7 @@ class EventBusService {
41
38
  // addon runners + remote agents leave `retainRecent` false: their events
42
39
  // are one-shot (fire-and-forget), fanned out to local subscribers but never
43
40
  // accumulated, so their heap can't grow an unbounded recent[] backlog.
44
- const inner = (0, system_1.getBrokerEventBus)(broker, { retainRecent: true });
41
+ const inner = getBrokerEventBus(broker, { retainRecent: true });
45
42
  this.inner = inner;
46
43
  // Replay deferred subscriptions onto the real bus.
47
44
  for (const sub of this.deferredSubs) {
@@ -85,4 +82,3 @@ class EventBusService {
85
82
  return this.inner?.getRecent(filter, limit) ?? [];
86
83
  }
87
84
  }
88
- exports.EventBusService = EventBusService;
@@ -1,7 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FeatureService = void 0;
4
- const system_1 = require("@camstack/system");
5
- class FeatureService extends system_1.FeatureManager {
1
+ import { FeatureManager } from '@camstack/system';
2
+ export class FeatureService extends FeatureManager {
6
3
  }
7
- exports.FeatureService = FeatureService;
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  /**
3
2
  * Process-wide singleton holder for the lifecycle job runner.
4
3
  *
@@ -8,19 +7,15 @@
8
7
  * at boot (from the `AddonPackageService` + event bus, wired in `manual-boot`)
9
8
  * and exposes it to every consumer.
10
9
  */
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.initLifecycleRunner = initLifecycleRunner;
13
- exports.getLifecycleRunner = getLifecycleRunner;
14
- exports.__resetLifecycleRunnerForTests = __resetLifecycleRunnerForTests;
15
- const lifecycle_job_runner_js_1 = require("../../api/core/lifecycle-job-runner.js");
10
+ import { createLifecycleJobRunner, } from '../../api/core/lifecycle-job-runner.js';
16
11
  let instance = null;
17
12
  /**
18
13
  * Build the runner once. Idempotent: a second call returns the already-built
19
14
  * instance and ignores the new deps (it does NOT rebuild).
20
15
  */
21
- function initLifecycleRunner(deps) {
16
+ export function initLifecycleRunner(deps) {
22
17
  if (instance === null) {
23
- instance = (0, lifecycle_job_runner_js_1.createLifecycleJobRunner)(deps);
18
+ instance = createLifecycleJobRunner(deps);
24
19
  }
25
20
  return instance;
26
21
  }
@@ -28,13 +23,13 @@ function initLifecycleRunner(deps) {
28
23
  * Return the process-wide runner. Throws if `initLifecycleRunner` has not been
29
24
  * called yet (boot wiring is expected to init it before any consumer runs).
30
25
  */
31
- function getLifecycleRunner() {
26
+ export function getLifecycleRunner() {
32
27
  if (instance === null) {
33
28
  throw new Error('lifecycle runner not initialized — call initLifecycleRunner(deps) at boot before use');
34
29
  }
35
30
  return instance;
36
31
  }
37
32
  /** Test-only: reset the singleton between specs. */
38
- function __resetLifecycleRunnerForTests() {
33
+ export function __resetLifecycleRunnerForTests() {
39
34
  instance = null;
40
35
  }
@@ -1,6 +1,2 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LifecycleStateMachine = void 0;
4
1
  // Re-export from @camstack/system — LifecycleStateMachine is already a plain class
5
- var system_1 = require("@camstack/system");
6
- Object.defineProperty(exports, "LifecycleStateMachine", { enumerable: true, get: function () { return system_1.LifecycleStateMachine; } });
2
+ export { LifecycleStateMachine } from '@camstack/system';
@@ -1,6 +1,2 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LogRingBuffer = void 0;
4
1
  // Re-export from @camstack/system
5
- var system_1 = require("@camstack/system");
6
- Object.defineProperty(exports, "LogRingBuffer", { enumerable: true, get: function () { return system_1.LogRingBuffer; } });
2
+ export { LogRingBuffer } from '@camstack/system';
@@ -1,8 +1,5 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LoggingService = void 0;
4
- const system_1 = require("@camstack/system");
5
- const types_1 = require("@camstack/types");
1
+ import { LogManager } from '@camstack/system';
2
+ import { EventCategory } from '@camstack/types';
6
3
  /** Baseline tags every hub-local logger gets, so console output never
7
4
  * falls back to `?` for the agent slot. Hub-local addons already
8
5
  * re-tag with their addonId; this ensures non-addon scopes (core
@@ -13,7 +10,7 @@ const types_1 = require("@camstack/types");
13
10
  * forked workers via `$hub.log` carry their own `tags.pid` which
14
11
  * wins over this baseline (right-biased merge in `writeFromWorker`). */
15
12
  const HUB_BASELINE_TAGS = { agentId: 'hub', nodeId: 'hub', pid: process.pid };
16
- class LoggingService extends system_1.LogManager {
13
+ export class LoggingService extends LogManager {
17
14
  /**
18
15
  * Device-name cache consulted by the formatter when a log line
19
16
  * carries `tags.deviceId` but no explicit `deviceName`. Populated
@@ -77,7 +74,7 @@ class LoggingService extends system_1.LogManager {
77
74
  */
78
75
  attachDeviceNameStream(eventBus) {
79
76
  const selfLogger = this.createLogger('logging').withTags({ addonId: 'logging' });
80
- eventBus.subscribe({ category: types_1.EventCategory.DeviceRegistered }, (event) => {
77
+ eventBus.subscribe({ category: EventCategory.DeviceRegistered }, (event) => {
81
78
  const data = event.data;
82
79
  if (data && typeof data.deviceId === 'number') {
83
80
  this.upsertDeviceName(data.deviceId, data.name);
@@ -90,7 +87,7 @@ class LoggingService extends system_1.LogManager {
90
87
  });
91
88
  }
92
89
  });
93
- eventBus.subscribe({ category: types_1.EventCategory.DeviceUnregistered }, (event) => {
90
+ eventBus.subscribe({ category: EventCategory.DeviceUnregistered }, (event) => {
94
91
  const data = event.data;
95
92
  if (data && typeof data.deviceId === 'number')
96
93
  this.removeDeviceName(data.deviceId);
@@ -137,4 +134,3 @@ class LoggingService extends system_1.LogManager {
137
134
  }
138
135
  }
139
136
  }
140
- exports.LoggingService = LoggingService;
@@ -1,6 +1,2 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ScopedLogger = void 0;
4
1
  // Re-export from @camstack/system
5
- var system_1 = require("@camstack/system");
6
- Object.defineProperty(exports, "ScopedLogger", { enumerable: true, get: function () { return system_1.ScopedLogger; } });
2
+ export { ScopedLogger } from '@camstack/system';
@@ -1,6 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.filterAgentAuthoritativeRecords = filterAgentAuthoritativeRecords;
4
1
  const VALID_STATES = new Set(['starting', 'ready', 'down']);
5
2
  const VALID_SCOPE_TYPES = new Set(['global', 'node', 'device']);
6
3
  /** Structural validation for a record received from a remote node. */
@@ -27,7 +24,7 @@ function isWellFormedRecord(value) {
27
24
  * Keep only the records `agentNodeId` is authoritative for, dropping
28
25
  * malformed entries. See module doc for why this filter must exist.
29
26
  */
30
- function filterAgentAuthoritativeRecords(records, agentNodeId) {
27
+ export function filterAgentAuthoritativeRecords(records, agentNodeId) {
31
28
  if (!Array.isArray(records))
32
29
  return [];
33
30
  return records.filter((record) => {