@camstack/server 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/{src/agent-status-page.ts → dist/agent-status-page.js} +30 -45
  2. package/dist/api/addon-upload.js +441 -0
  3. package/dist/api/addons-custom.router.js +91 -0
  4. package/dist/api/auth-whoami.js +55 -0
  5. package/dist/api/bridge-addons.router.js +109 -0
  6. package/dist/api/capabilities.router.js +229 -0
  7. package/dist/api/core/addon-settings.router.js +117 -0
  8. package/dist/api/core/agents.router.js +73 -0
  9. package/dist/api/core/auth.router.js +286 -0
  10. package/dist/api/core/bulk-update-coordinator.js +229 -0
  11. package/dist/api/core/cap-providers.js +1124 -0
  12. package/dist/api/core/capabilities.router.js +138 -0
  13. package/dist/api/core/collection-preference.js +17 -0
  14. package/dist/api/core/event-bus-proxy.router.js +45 -0
  15. package/dist/api/core/hwaccel.router.js +91 -0
  16. package/dist/api/core/live-events.router.js +61 -0
  17. package/dist/api/core/logs.router.js +172 -0
  18. package/dist/api/core/notifications.router.js +67 -0
  19. package/dist/api/core/repl.router.js +35 -0
  20. package/dist/api/core/settings-backend.router.js +121 -0
  21. package/dist/api/core/stream-probe.router.js +58 -0
  22. package/dist/api/core/system-events.router.js +100 -0
  23. package/dist/api/health/health.routes.js +68 -0
  24. package/{src/api/oauth2/consent-page.ts → dist/api/oauth2/consent-page.js} +11 -20
  25. package/dist/api/oauth2/oauth2-routes.js +219 -0
  26. package/dist/api/trpc/cap-mount-helpers.js +194 -0
  27. package/dist/api/trpc/cap-route-error-formatter.js +133 -0
  28. package/dist/api/trpc/client-ip.js +147 -0
  29. package/dist/api/trpc/core-cap-bridge.js +115 -0
  30. package/dist/api/trpc/generated-cap-mounts.js +388 -0
  31. package/dist/api/trpc/generated-cap-routers.js +7635 -0
  32. package/dist/api/trpc/scope-access.js +93 -0
  33. package/dist/api/trpc/trpc.context.js +184 -0
  34. package/dist/api/trpc/trpc.middleware.js +139 -0
  35. package/dist/api/trpc/trpc.router.js +188 -0
  36. package/dist/auth/session-cookie.js +47 -0
  37. package/dist/boot/boot-config.js +241 -0
  38. package/dist/boot/integration-id-backfill.js +76 -0
  39. package/dist/boot/post-boot.service.js +85 -0
  40. package/dist/core/addon/addon-call-gateway.js +99 -0
  41. package/dist/core/addon/addon-package.service.js +1560 -0
  42. package/dist/core/addon/addon-registry.service.js +2739 -0
  43. package/{src/core/addon/addon-row-manifest.ts → dist/core/addon/addon-row-manifest.js} +5 -5
  44. package/dist/core/addon/addon-search.service.js +62 -0
  45. package/dist/core/addon/addon-settings-provider.js +102 -0
  46. package/dist/core/addon/addon.tokens.js +5 -0
  47. package/dist/core/addon-bridge/addon-bridge.service.js +145 -0
  48. package/dist/core/addon-pages/addon-pages.service.js +107 -0
  49. package/dist/core/addon-widgets/addon-widgets.service.js +120 -0
  50. package/dist/core/agent/agent-registry.service.js +477 -0
  51. package/dist/core/auth/auth.service.js +10 -0
  52. package/dist/core/capability/capability.service.js +58 -0
  53. package/dist/core/config/config.schema.js +7 -0
  54. package/dist/core/config/config.service.js +10 -0
  55. package/dist/core/events/event-bus.service.js +83 -0
  56. package/dist/core/feature/feature.service.js +10 -0
  57. package/dist/core/lifecycle/lifecycle-state-machine.js +6 -0
  58. package/dist/core/logging/log-ring-buffer.js +6 -0
  59. package/dist/core/logging/logging.service.js +130 -0
  60. package/dist/core/logging/scoped-logger.js +6 -0
  61. package/dist/core/moleculer/cap-call-fn.js +50 -0
  62. package/dist/core/moleculer/cap-route-authority.js +122 -0
  63. package/dist/core/moleculer/moleculer.service.js +898 -0
  64. package/dist/core/network/network-quality.service.js +7 -0
  65. package/dist/core/notification/notification-wrapper.service.js +33 -0
  66. package/dist/core/notification/toast-wrapper.service.js +25 -0
  67. package/dist/core/provider/provider.tokens.js +4 -0
  68. package/dist/core/repl/repl-engine.service.js +140 -0
  69. package/dist/core/storage/fs-storage-backend.js +6 -0
  70. package/dist/core/storage/storage-location-manager.js +6 -0
  71. package/dist/core/storage/storage.service.js +7 -0
  72. package/dist/core/streaming/stream-probe.service.js +209 -0
  73. package/dist/core/topology/topology-emitter.service.js +106 -0
  74. package/dist/launcher.js +325 -0
  75. package/dist/main.js +1098 -0
  76. package/dist/manual-boot.js +227 -0
  77. package/package.json +5 -1
  78. package/src/__tests__/addon-install-e2e.test.ts +0 -74
  79. package/src/__tests__/addon-pages-e2e.test.ts +0 -200
  80. package/src/__tests__/addon-route-session.test.ts +0 -17
  81. package/src/__tests__/addon-settings-router.spec.ts +0 -67
  82. package/src/__tests__/addon-upload.spec.ts +0 -475
  83. package/src/__tests__/agent-registry.spec.ts +0 -179
  84. package/src/__tests__/agent-status-page.spec.ts +0 -82
  85. package/src/__tests__/auth-session-cookie.test.ts +0 -48
  86. package/src/__tests__/bulk-update-coordinator.spec.ts +0 -303
  87. package/src/__tests__/cap-ownership-authority.spec.ts +0 -431
  88. package/src/__tests__/cap-providers/cap-providers-location-import.spec.ts +0 -206
  89. package/src/__tests__/cap-providers/cap-usage-graph.spec.ts +0 -37
  90. package/src/__tests__/cap-providers/compute-topology-categories.spec.ts +0 -110
  91. package/src/__tests__/cap-providers/integrations-delete-cascade.spec.ts +0 -292
  92. package/src/__tests__/cap-providers-bulk-update.spec.ts +0 -408
  93. package/src/__tests__/cap-route-adapter.spec.ts +0 -302
  94. package/src/__tests__/cap-routers/_meta.spec.ts +0 -199
  95. package/src/__tests__/cap-routers/addon-settings.router.spec.ts +0 -115
  96. package/src/__tests__/cap-routers/broker-routing.router.spec.ts +0 -177
  97. package/src/__tests__/cap-routers/cap-route-error-formatter.spec.ts +0 -125
  98. package/src/__tests__/cap-routers/capabilities-node.spec.ts +0 -68
  99. package/src/__tests__/cap-routers/device-link-overlay.spec.ts +0 -137
  100. package/src/__tests__/cap-routers/device-manager-aggregate.router.spec.ts +0 -194
  101. package/src/__tests__/cap-routers/harness.ts +0 -163
  102. package/src/__tests__/cap-routers/metrics-provider.router.spec.ts +0 -133
  103. package/src/__tests__/cap-routers/null-provider-guard.spec.ts +0 -64
  104. package/src/__tests__/cap-routers/pipeline-executor.router.spec.ts +0 -159
  105. package/src/__tests__/cap-routers/settings-store.router.spec.ts +0 -291
  106. package/src/__tests__/capability-e2e.test.ts +0 -384
  107. package/src/__tests__/cli-e2e.test.ts +0 -150
  108. package/src/__tests__/core-cap-bridge.spec.ts +0 -91
  109. package/src/__tests__/dev-bootstrap-shm-ring.spec.ts +0 -40
  110. package/src/__tests__/device-settings-contribution-dispatch.spec.ts +0 -280
  111. package/src/__tests__/embedded-deps-e2e.test.ts +0 -125
  112. package/src/__tests__/event-bus-proxy-router.spec.ts +0 -75
  113. package/src/__tests__/fixtures/mock-analysis-addon-a.ts +0 -37
  114. package/src/__tests__/fixtures/mock-analysis-addon-b.ts +0 -37
  115. package/src/__tests__/fixtures/mock-log-addon.ts +0 -37
  116. package/src/__tests__/fixtures/mock-storage-addon.ts +0 -40
  117. package/src/__tests__/framework-allowlist.spec.ts +0 -96
  118. package/src/__tests__/framework-installer-defer-restart.spec.ts +0 -165
  119. package/src/__tests__/https-e2e.test.ts +0 -124
  120. package/src/__tests__/lifecycle-e2e.test.ts +0 -189
  121. package/src/__tests__/live-events-subscription.spec.ts +0 -149
  122. package/src/__tests__/moleculer/uds-readiness.spec.ts +0 -150
  123. package/src/__tests__/moleculer/uds-topology.spec.ts +0 -418
  124. package/src/__tests__/moleculer/uds-unowned-call.spec.ts +0 -383
  125. package/src/__tests__/moleculer-register-node-idempotency.spec.ts +0 -273
  126. package/src/__tests__/native-cap-route.spec.ts +0 -427
  127. package/src/__tests__/oauth2-account-linking.spec.ts +0 -867
  128. package/src/__tests__/post-boot-restart.spec.ts +0 -161
  129. package/src/__tests__/singleton-contention.test.ts +0 -499
  130. package/src/__tests__/streaming-diagnostic.test.ts +0 -615
  131. package/src/__tests__/streaming-scale.test.ts +0 -314
  132. package/src/__tests__/uds-addon-call-wiring.spec.ts +0 -242
  133. package/src/__tests__/uds-log-ingest.spec.ts +0 -183
  134. package/src/api/__tests__/addons-custom.spec.ts +0 -148
  135. package/src/api/__tests__/capabilities.router.test.ts +0 -56
  136. package/src/api/addon-upload.ts +0 -529
  137. package/src/api/addons-custom.router.ts +0 -101
  138. package/src/api/auth-whoami.ts +0 -101
  139. package/src/api/bridge-addons.router.ts +0 -122
  140. package/src/api/capabilities.router.ts +0 -265
  141. package/src/api/core/__tests__/auth-router-totp.spec.ts +0 -297
  142. package/src/api/core/__tests__/integration-markers.spec.ts +0 -10
  143. package/src/api/core/addon-settings.router.ts +0 -127
  144. package/src/api/core/agents.router.ts +0 -86
  145. package/src/api/core/auth.router.ts +0 -322
  146. package/src/api/core/bulk-update-coordinator.ts +0 -305
  147. package/src/api/core/cap-providers.ts +0 -1339
  148. package/src/api/core/capabilities.router.ts +0 -149
  149. package/src/api/core/collection-preference.ts +0 -40
  150. package/src/api/core/event-bus-proxy.router.ts +0 -45
  151. package/src/api/core/hwaccel.router.ts +0 -108
  152. package/src/api/core/live-events.router.ts +0 -67
  153. package/src/api/core/logs.router.ts +0 -195
  154. package/src/api/core/notifications.router.ts +0 -66
  155. package/src/api/core/repl.router.ts +0 -39
  156. package/src/api/core/settings-backend.router.ts +0 -140
  157. package/src/api/core/stream-probe.router.ts +0 -57
  158. package/src/api/core/system-events.router.ts +0 -125
  159. package/src/api/health/health.routes.ts +0 -117
  160. package/src/api/oauth2/__tests__/oauth2-routes.spec.ts +0 -62
  161. package/src/api/oauth2/oauth2-routes.ts +0 -281
  162. package/src/api/trpc/__tests__/client-ip.spec.ts +0 -146
  163. package/src/api/trpc/__tests__/scope-access-device.spec.ts +0 -268
  164. package/src/api/trpc/__tests__/scope-access.spec.ts +0 -102
  165. package/src/api/trpc/__tests__/webrtc-session-ua-enrich.spec.ts +0 -136
  166. package/src/api/trpc/cap-mount-helpers.ts +0 -245
  167. package/src/api/trpc/cap-route-error-formatter.ts +0 -171
  168. package/src/api/trpc/client-ip.ts +0 -147
  169. package/src/api/trpc/core-cap-bridge.ts +0 -154
  170. package/src/api/trpc/generated-cap-mounts.ts +0 -1240
  171. package/src/api/trpc/generated-cap-routers.ts +0 -11523
  172. package/src/api/trpc/scope-access.ts +0 -110
  173. package/src/api/trpc/trpc.context.ts +0 -258
  174. package/src/api/trpc/trpc.middleware.ts +0 -146
  175. package/src/api/trpc/trpc.router.ts +0 -389
  176. package/src/auth/session-cookie.ts +0 -54
  177. package/src/boot/__tests__/integration-id-backfill.spec.ts +0 -131
  178. package/src/boot/boot-config.ts +0 -259
  179. package/src/boot/integration-id-backfill.ts +0 -109
  180. package/src/boot/post-boot.service.ts +0 -105
  181. package/src/core/addon/__tests__/addon-registry-capability.test.ts +0 -62
  182. package/src/core/addon/__tests__/addon-row-manifest.spec.ts +0 -62
  183. package/src/core/addon/addon-call-gateway.ts +0 -171
  184. package/src/core/addon/addon-package.service.ts +0 -1787
  185. package/src/core/addon/addon-registry.service.ts +0 -3130
  186. package/src/core/addon/addon-search.service.ts +0 -91
  187. package/src/core/addon/addon-settings-provider.ts +0 -220
  188. package/src/core/addon/addon.tokens.ts +0 -2
  189. package/src/core/addon-bridge/addon-bridge.service.ts +0 -130
  190. package/src/core/addon-pages/addon-pages.service.spec.ts +0 -117
  191. package/src/core/addon-pages/addon-pages.service.ts +0 -82
  192. package/src/core/addon-widgets/addon-widgets.service.ts +0 -95
  193. package/src/core/agent/agent-registry.service.ts +0 -529
  194. package/src/core/auth/auth.service.spec.ts +0 -86
  195. package/src/core/auth/auth.service.ts +0 -8
  196. package/src/core/capability/capability.service.ts +0 -66
  197. package/src/core/config/config.schema.ts +0 -3
  198. package/src/core/config/config.service.spec.ts +0 -175
  199. package/src/core/config/config.service.ts +0 -7
  200. package/src/core/events/event-bus.service.spec.ts +0 -235
  201. package/src/core/events/event-bus.service.ts +0 -89
  202. package/src/core/feature/feature.service.spec.ts +0 -99
  203. package/src/core/feature/feature.service.ts +0 -8
  204. package/src/core/lifecycle/lifecycle-state-machine.spec.ts +0 -166
  205. package/src/core/lifecycle/lifecycle-state-machine.ts +0 -3
  206. package/src/core/logging/log-ring-buffer.ts +0 -3
  207. package/src/core/logging/logging.service.spec.ts +0 -287
  208. package/src/core/logging/logging.service.ts +0 -143
  209. package/src/core/logging/scoped-logger.ts +0 -3
  210. package/src/core/moleculer/cap-call-fn.spec.ts +0 -173
  211. package/src/core/moleculer/cap-call-fn.ts +0 -107
  212. package/src/core/moleculer/cap-route-authority.ts +0 -194
  213. package/src/core/moleculer/moleculer.service.ts +0 -1072
  214. package/src/core/network/network-quality.service.spec.ts +0 -53
  215. package/src/core/network/network-quality.service.ts +0 -5
  216. package/src/core/notification/notification-wrapper.service.ts +0 -34
  217. package/src/core/notification/toast-wrapper.service.ts +0 -27
  218. package/src/core/provider/provider.tokens.ts +0 -1
  219. package/src/core/repl/repl-engine.service.spec.ts +0 -444
  220. package/src/core/repl/repl-engine.service.ts +0 -155
  221. package/src/core/storage/fs-storage-backend.spec.ts +0 -70
  222. package/src/core/storage/fs-storage-backend.ts +0 -3
  223. package/src/core/storage/storage-location-manager.spec.ts +0 -130
  224. package/src/core/storage/storage-location-manager.ts +0 -3
  225. package/src/core/storage/storage.service.spec.ts +0 -73
  226. package/src/core/storage/storage.service.ts +0 -3
  227. package/src/core/streaming/stream-probe.service.ts +0 -221
  228. package/src/core/topology/topology-emitter.service.ts +0 -105
  229. package/src/launcher.ts +0 -314
  230. package/src/main.ts +0 -1245
  231. package/src/manual-boot.ts +0 -301
  232. package/tsconfig.build.json +0 -8
  233. package/tsconfig.json +0 -33
  234. package/vitest.config.ts +0 -26
@@ -1,418 +0,0 @@
1
- /**
2
- * UDS topology wiring — unit tests for Tasks E1 + E2.
3
- *
4
- * E1: When `LocalChildRegistry.onChildRegistered` fires for a hub-local child,
5
- * the hub applies the child's caps to the `CapabilityRegistry` (same effect
6
- * as the `$hub.registerNode` RPC for a `hub/<runner>` node). When
7
- * `onChildGone` fires, the caps are unregistered + synthetic readiness-down
8
- * is emitted.
9
- *
10
- * Idempotency: registering the same child twice (RPC + UDS) does NOT
11
- * duplicate providers or double-emit readiness-down. The diff guard lives in
12
- * `applyNodeManifest` (previous vs desired); `onChildRegistered` fires once
13
- * per UDS connect and calls `onRegisterNode` unconditionally — the second
14
- * call (from the Moleculer RPC path) becomes a no-op at the registry level
15
- * because `applyNodeManifest` detects no cap delta.
16
- *
17
- * Agent nodes (bare nodeId, no '/') still go through the Moleculer RPC
18
- * path — they are NOT touched by the UDS lifecycle.
19
- *
20
- * E2: `LocalChildRegistry.setChildLogLevel(childId, level)` sends a
21
- * `set-log-level` message to the named child and returns `true`. Returns
22
- * `false` (no-op) for an unknown childId so callers can fall back to the
23
- * Moleculer `$node-mgmt.setLogLevel` path when the UDS child is not
24
- * connected.
25
- *
26
- * We do NOT spin up a real broker or UDS server — instead we test the REAL
27
- * exported helpers (`buildChildUdsManifest`, `LocalChildRegistry`) directly so
28
- * the tests exercise actual production code rather than reimplementations.
29
- *
30
- * Integration note: the `onChildRegistered`/`onChildGone` closures are inlined
31
- * in `MoleculerService.onModuleInit` (capturing `this.localChildRegistry` and
32
- * `this.logger`). Extracting them would require threading several private
33
- * dependencies as parameters — invasive for modest gain here. The closures are
34
- * verified at the integration level (real hub boot) while this file unit-tests
35
- * the helpers the closures delegate to.
36
- */
37
- import { describe, it, expect, vi } from 'vitest'
38
- import type { ChildCapDescriptor, RegisteredChild } from '@camstack/kernel'
39
- import { LocalChildRegistry, createLocalTransport } from '@camstack/kernel'
40
- import { buildChildUdsManifest } from '../../core/moleculer/moleculer.service.js'
41
-
42
- // ---------------------------------------------------------------------------
43
- // Fake types that mirror the production interfaces used in the wiring
44
- // ---------------------------------------------------------------------------
45
-
46
- interface FakeLocalChildRegistry {
47
- onChildRegistered(handler: (child: RegisteredChild) => void): void
48
- onChildGone(handler: (childId: string) => void): void
49
- setChildLogLevel(childId: string, level: string): boolean
50
- }
51
-
52
- // ---------------------------------------------------------------------------
53
- // Production wiring function — mirrors the hub's onModuleInit wiring.
54
- //
55
- // This is the extracted glue that E1 adds to moleculer.service.ts.
56
- // Testing it here in isolation:
57
- // 1. proves the logic is correct without a full broker or UDS server,
58
- // 2. guards against regressions when the hub changes other wiring.
59
- // ---------------------------------------------------------------------------
60
-
61
- interface HubChildManifestWiringOptions {
62
- readonly registry: FakeLocalChildRegistry
63
- readonly hubNodeId: string
64
- readonly applyChildManifest: (nodeId: string, caps: readonly ChildCapDescriptor[]) => void
65
- readonly removeChildFromRegistry: (nodeId: string) => void
66
- }
67
-
68
- /**
69
- * Wire `onChildRegistered` → `applyChildManifest` and `onChildGone` →
70
- * `removeChildFromRegistry` for hub-local children. This is the exact
71
- * glue E1 adds to the hub's `onModuleInit`.
72
- */
73
- function wireHubChildManifest(opts: HubChildManifestWiringOptions): void {
74
- const { registry, hubNodeId, applyChildManifest, removeChildFromRegistry } = opts
75
-
76
- registry.onChildRegistered((child) => {
77
- const nodeId = `${hubNodeId}/${child.childId}`
78
- applyChildManifest(nodeId, child.caps)
79
- })
80
-
81
- registry.onChildGone((childId) => {
82
- const nodeId = `${hubNodeId}/${childId}`
83
- removeChildFromRegistry(nodeId)
84
- })
85
- }
86
-
87
- // ---------------------------------------------------------------------------
88
- // Tests — E1: manifest apply + cleanup
89
- // ---------------------------------------------------------------------------
90
-
91
- describe('E1 — wireHubChildManifest', () => {
92
- it('onChildRegistered applies caps to the registry with hub/<childId> nodeId', () => {
93
- const applyChildManifest = vi.fn<[string, readonly ChildCapDescriptor[]], void>()
94
- const removeChildFromRegistry = vi.fn<[string], void>()
95
-
96
- let capturedRegisteredHandler: ((child: RegisteredChild) => void) | null = null
97
- let capturedGoneHandler: ((childId: string) => void) | null = null
98
-
99
- const registry: FakeLocalChildRegistry = {
100
- onChildRegistered: (h) => {
101
- capturedRegisteredHandler = h
102
- },
103
- onChildGone: (h) => {
104
- capturedGoneHandler = h
105
- },
106
- setChildLogLevel: vi.fn<[string, string], boolean>().mockReturnValue(false),
107
- }
108
-
109
- wireHubChildManifest({
110
- registry,
111
- hubNodeId: 'hub',
112
- applyChildManifest,
113
- removeChildFromRegistry,
114
- })
115
-
116
- expect(capturedRegisteredHandler).not.toBeNull()
117
- expect(capturedGoneHandler).not.toBeNull()
118
-
119
- const caps: ChildCapDescriptor[] = [
120
- { capName: 'stream-broker', mode: 'singleton' },
121
- { capName: 'stream-params', mode: 'collection', deviceId: 7 },
122
- ]
123
- capturedRegisteredHandler!({ childId: 'addon-stream-broker', caps })
124
-
125
- expect(applyChildManifest).toHaveBeenCalledOnce()
126
- expect(applyChildManifest).toHaveBeenCalledWith('hub/addon-stream-broker', caps)
127
- })
128
-
129
- it('onChildGone removes the child from the registry with hub/<childId> nodeId', () => {
130
- const applyChildManifest = vi.fn<[string, readonly ChildCapDescriptor[]], void>()
131
- const removeChildFromRegistry = vi.fn<[string], void>()
132
-
133
- let capturedGoneHandler: ((childId: string) => void) | null = null
134
-
135
- const registry: FakeLocalChildRegistry = {
136
- onChildRegistered: vi.fn(),
137
- onChildGone: (h) => {
138
- capturedGoneHandler = h
139
- },
140
- setChildLogLevel: vi.fn<[string, string], boolean>().mockReturnValue(false),
141
- }
142
-
143
- wireHubChildManifest({
144
- registry,
145
- hubNodeId: 'hub',
146
- applyChildManifest,
147
- removeChildFromRegistry,
148
- })
149
-
150
- capturedGoneHandler!('provider-reolink')
151
-
152
- expect(removeChildFromRegistry).toHaveBeenCalledOnce()
153
- expect(removeChildFromRegistry).toHaveBeenCalledWith('hub/provider-reolink')
154
- })
155
-
156
- it('uses the correct hub nodeId prefix', () => {
157
- const applyChildManifest = vi.fn<[string, readonly ChildCapDescriptor[]], void>()
158
- const removeChildFromRegistry = vi.fn<[string], void>()
159
-
160
- let registeredHandler: ((child: RegisteredChild) => void) | null = null
161
- let goneHandler: ((childId: string) => void) | null = null
162
-
163
- const registry: FakeLocalChildRegistry = {
164
- onChildRegistered: (h) => {
165
- registeredHandler = h
166
- },
167
- onChildGone: (h) => {
168
- goneHandler = h
169
- },
170
- setChildLogLevel: vi.fn<[string, string], boolean>().mockReturnValue(false),
171
- }
172
-
173
- wireHubChildManifest({
174
- registry,
175
- hubNodeId: 'custom-hub',
176
- applyChildManifest,
177
- removeChildFromRegistry,
178
- })
179
-
180
- registeredHandler!({ childId: 'my-addon', caps: [] })
181
- goneHandler!('my-addon')
182
-
183
- expect(applyChildManifest).toHaveBeenCalledWith('custom-hub/my-addon', [])
184
- expect(removeChildFromRegistry).toHaveBeenCalledWith('custom-hub/my-addon')
185
- })
186
- })
187
-
188
- // ---------------------------------------------------------------------------
189
- // Tests — E1: buildChildUdsManifest (REAL exported function from moleculer.service.ts)
190
- //
191
- // These tests exercise the actual production helper — not a reimplementation.
192
- // The function synthesises ONE manifest entry (addonId = childId) which is
193
- // correct under the one-addon-one-process invariant (childId = runnerId = addonId).
194
- // ---------------------------------------------------------------------------
195
-
196
- describe('E1 — buildChildUdsManifest (real helper)', () => {
197
- it('uses childId as the synthetic addonId', () => {
198
- const caps: ChildCapDescriptor[] = [{ capName: 'stream-broker', mode: 'singleton' }]
199
- const result = buildChildUdsManifest('hub/addon-stream-broker', 'addon-stream-broker', caps)
200
- expect(result.addons).toHaveLength(1)
201
- expect(result.addons[0]!.addonId).toBe('addon-stream-broker')
202
- })
203
-
204
- it('sets nodeId correctly on the returned params', () => {
205
- const caps: ChildCapDescriptor[] = [{ capName: 'stream-broker', mode: 'singleton' }]
206
- const result = buildChildUdsManifest('hub/addon-stream-broker', 'addon-stream-broker', caps)
207
- expect(result.nodeId).toBe('hub/addon-stream-broker')
208
- })
209
-
210
- it('collects unique system (non-device-scoped) capNames and excludes device-scoped ones', () => {
211
- const caps: ChildCapDescriptor[] = [
212
- { capName: 'stream-broker', mode: 'singleton' },
213
- { capName: 'stream-params', mode: 'collection', deviceId: 7 },
214
- { capName: 'stream-params', mode: 'collection', deviceId: 15 },
215
- { capName: 'pipeline-runner', mode: 'singleton' },
216
- ]
217
- const result = buildChildUdsManifest('hub/addon-x', 'addon-x', caps)
218
- expect(result.addons).toHaveLength(1)
219
- const { capabilities } = result.addons[0]!
220
- // stream-broker + pipeline-runner (system caps only; device-scoped stream-params excluded)
221
- expect(capabilities).toHaveLength(2)
222
- expect(capabilities).toContain('stream-broker')
223
- expect(capabilities).toContain('pipeline-runner')
224
- // stream-params is device-scoped — must NOT appear in the addons array
225
- expect(capabilities).not.toContain('stream-params')
226
- })
227
-
228
- it('handles empty cap list', () => {
229
- const result = buildChildUdsManifest('hub/empty-addon', 'empty-addon', [])
230
- expect(result.addons).toHaveLength(1)
231
- expect(result.addons[0]!.capabilities).toHaveLength(0)
232
- expect(result.addons[0]!.addonId).toBe('empty-addon')
233
- })
234
-
235
- it('deduplicates the same system cap registered multiple times', () => {
236
- // Unusual but defensively handled: same capName appearing in multiple
237
- // descriptors without a deviceId (e.g. a collection cap with no device scope).
238
- const caps: ChildCapDescriptor[] = [
239
- { capName: 'addon-pages-source', mode: 'collection' },
240
- { capName: 'addon-pages-source', mode: 'collection' },
241
- ]
242
- const result = buildChildUdsManifest('hub/addon-ui', 'addon-ui', caps)
243
- const { capabilities } = result.addons[0]!
244
- expect(capabilities).toHaveLength(1)
245
- expect(capabilities).toContain('addon-pages-source')
246
- })
247
- })
248
-
249
- // ---------------------------------------------------------------------------
250
- // Tests — E1: idempotency (double-registration via RPC + UDS)
251
- // ---------------------------------------------------------------------------
252
-
253
- describe('E1 — idempotency (double-registration via RPC + UDS)', () => {
254
- it('applying the same nodeId twice via applyChildManifest is safe (dedupe in applyNodeManifest)', () => {
255
- // This test verifies that the hub production code guards double-apply
256
- // by checking nodeRegistry state before re-registering.
257
- // The wiring function itself calls applyChildManifest unconditionally;
258
- // the diff guard lives in applyNodeManifest (previous vs desired cap sets),
259
- // so the second call is a no-op at the CapabilityRegistry level.
260
- // Here we verify that onChildRegistered fires ONCE per UDS connect.
261
- let callCount = 0
262
- const applyChildManifest = vi.fn<[string, readonly ChildCapDescriptor[]], void>(() => {
263
- callCount++
264
- })
265
-
266
- let registeredHandler: ((child: RegisteredChild) => void) | null = null
267
- const registry: FakeLocalChildRegistry = {
268
- onChildRegistered: (h) => {
269
- registeredHandler = h
270
- },
271
- onChildGone: vi.fn(),
272
- setChildLogLevel: vi.fn<[string, string], boolean>().mockReturnValue(false),
273
- }
274
-
275
- wireHubChildManifest({
276
- registry,
277
- hubNodeId: 'hub',
278
- applyChildManifest,
279
- removeChildFromRegistry: vi.fn(),
280
- })
281
-
282
- const child: RegisteredChild = {
283
- childId: 'provider-reolink',
284
- caps: [{ capName: 'stream-broker', mode: 'singleton' }],
285
- }
286
-
287
- // First registration (UDS connect)
288
- registeredHandler!(child)
289
- // updateCaps re-registration (device restore)
290
- registeredHandler!(child)
291
-
292
- // applyChildManifest called each time — the diff logic inside
293
- // applyNodeManifest makes the second call a no-op at the registry level.
294
- expect(callCount).toBe(2)
295
- })
296
-
297
- it('agent nodes (bare nodeId, no slash) are NOT affected by UDS wiring', () => {
298
- // The UDS wiring only adds hub/<childId> prefixed nodeIds.
299
- // Bare agent nodeIds (e.g. "dev-agent-0") come via the RPC path.
300
- const applyChildManifest = vi.fn<[string, readonly ChildCapDescriptor[]], void>()
301
- let registeredHandler: ((child: RegisteredChild) => void) | null = null
302
-
303
- const registry: FakeLocalChildRegistry = {
304
- onChildRegistered: (h) => {
305
- registeredHandler = h
306
- },
307
- onChildGone: vi.fn(),
308
- setChildLogLevel: vi.fn<[string, string], boolean>().mockReturnValue(false),
309
- }
310
-
311
- wireHubChildManifest({
312
- registry,
313
- hubNodeId: 'hub',
314
- applyChildManifest,
315
- removeChildFromRegistry: vi.fn(),
316
- })
317
-
318
- // A child that registers over UDS always has a childId (runner id).
319
- // The hub always prefixes it with "hub/". Agent nodes never connect
320
- // via the hub's LocalChildRegistry — they are on a different host.
321
- // Simulate a UDS child with a childId that looks like an agent id:
322
- registeredHandler!({ childId: 'dev-agent-0', caps: [] })
323
-
324
- // The wiring adds the hub prefix → "hub/dev-agent-0" — NOT the bare "dev-agent-0"
325
- // which would incorrectly shadow the agent's RPC-registered node.
326
- expect(applyChildManifest).toHaveBeenCalledWith('hub/dev-agent-0', [])
327
- // Bare agent nodeId is handled ONLY by the $hub.registerNode RPC.
328
- expect(applyChildManifest).not.toHaveBeenCalledWith('dev-agent-0', expect.anything())
329
- })
330
- })
331
-
332
- // ---------------------------------------------------------------------------
333
- // Tests — E2: LocalChildRegistry.setChildLogLevel (REAL implementation)
334
- //
335
- // These tests exercise the actual `LocalChildRegistry` class — not a mock.
336
- // They verify the boolean-return semantics added by Fix 1:
337
- // - true when the child is connected and the message was emitted
338
- // - false when the childId is unknown (child not connected)
339
- // ---------------------------------------------------------------------------
340
-
341
- describe('E2 — LocalChildRegistry.setChildLogLevel (real implementation)', () => {
342
- it('returns false for an unknown childId (child not connected)', async () => {
343
- // Spin up a registry with no connected children.
344
- const nodeId = 'hub-test-setloglevel'
345
- const server = createLocalTransport().createServer(nodeId)
346
- const registry = new LocalChildRegistry({ server })
347
- // Do NOT call registry.start() — no UDS server needed for this assertion.
348
- // The children map is empty so any childId lookup returns undefined.
349
- const result = registry.setChildLogLevel('unknown-child', 'debug')
350
- expect(result).toBe(false)
351
- // Cleanup: close the server without starting (no-op on most implementations).
352
- try {
353
- await registry.close()
354
- } catch {
355
- /* server was never started */
356
- }
357
- })
358
-
359
- it('setProcessLogLevel falls back to Moleculer when setChildLogLevel returns false', () => {
360
- // Simulate the cap-providers.ts routing:
361
- // const reachedViaUds = moleculer.setChildLogLevelByNodeId(input.nodeId, input.level)
362
- // if (!reachedViaUds) { await broker.call('$node-mgmt.setLogLevel', ...) }
363
- // When the UDS child is not connected, setChildLogLevelByNodeId returns false
364
- // and the Moleculer fallback is invoked.
365
- const moleculerFallback = vi.fn<[string, string], void>()
366
-
367
- const simulateSetProcessLogLevel = (
368
- nodeId: string,
369
- level: string,
370
- udsResult: boolean,
371
- ): void => {
372
- if (!udsResult) {
373
- moleculerFallback(nodeId, level)
374
- }
375
- }
376
-
377
- // Child not connected — UDS path returns false → Moleculer fallback fires.
378
- simulateSetProcessLogLevel('hub/provider-reolink', 'debug', false)
379
- expect(moleculerFallback).toHaveBeenCalledWith('hub/provider-reolink', 'debug')
380
-
381
- // Child connected — UDS path returns true → Moleculer fallback skipped.
382
- moleculerFallback.mockClear()
383
- simulateSetProcessLogLevel('hub/provider-reolink', 'debug', true)
384
- expect(moleculerFallback).not.toHaveBeenCalled()
385
- })
386
-
387
- it('returns false for an agent nodeId (not hub-local) in MoleculerService routing', () => {
388
- // MoleculerService.setChildLogLevelByNodeId returns false for any nodeId
389
- // that does not start with `${hubNodeId}/`. Agent nodes ("dev-agent-0")
390
- // must fall through to the Moleculer path.
391
- // We test the routing logic inline (no full MoleculerService instantiation).
392
- const hubNodeId = 'hub'
393
-
394
- const routeSetLogLevel = (
395
- nodeId: string,
396
- level: string,
397
- registry: { setChildLogLevel(id: string, lvl: string): boolean } | null,
398
- ): boolean => {
399
- if (!nodeId.startsWith(`${hubNodeId}/`)) return false
400
- const childId = nodeId.slice(hubNodeId.length + 1)
401
- if (registry === null) return false
402
- return registry.setChildLogLevel(childId, level)
403
- }
404
-
405
- // Agent node — not hub-local → always false
406
- expect(routeSetLogLevel('dev-agent-0', 'info', null)).toBe(false)
407
- // Hub node without slash → always false
408
- expect(routeSetLogLevel('hub', 'info', null)).toBe(false)
409
- // Hub child but registry is null → false
410
- expect(routeSetLogLevel('hub/provider-reolink', 'info', null)).toBe(false)
411
- // Hub child + registry that reports connected
412
- const connectedRegistry = { setChildLogLevel: (_id: string, _lvl: string) => true }
413
- expect(routeSetLogLevel('hub/provider-reolink', 'info', connectedRegistry)).toBe(true)
414
- // Hub child + registry that reports NOT connected
415
- const disconnectedRegistry = { setChildLogLevel: (_id: string, _lvl: string) => false }
416
- expect(routeSetLogLevel('hub/provider-reolink', 'info', disconnectedRegistry)).toBe(false)
417
- })
418
- })