@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,110 +0,0 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { computeTopology } from '../../api/core/cap-providers'
3
-
4
- // Minimal stubs — only the shapes computeTopology reads.
5
- function makeAgentRegistry(): { listNodes: () => Promise<unknown[]> } {
6
- return {
7
- listNodes: async () => [
8
- {
9
- info: {
10
- id: 'hub',
11
- name: 'hub',
12
- hostname: 'hub',
13
- platform: 'darwin',
14
- arch: 'arm64',
15
- cpuModel: 'M2',
16
- cpuCores: 8,
17
- memoryMB: 16384,
18
- pythonRuntimes: [],
19
- },
20
- status: { cpuPercent: 12, memoryPercent: 30 },
21
- isHub: true,
22
- isOnline: true,
23
- connectedSince: Date.now() - 60_000,
24
- subProcesses: [],
25
- agentAddons: [],
26
- localIps: [],
27
- },
28
- ],
29
- }
30
- }
31
-
32
- function makeAddonRegistry(): { listAddons: () => readonly unknown[] } {
33
- return {
34
- listAddons: () => [
35
- {
36
- manifest: { id: 'provider-hikvision' },
37
- declaration: {
38
- id: 'provider-hikvision',
39
- category: 'providers',
40
- capabilities: [{ name: 'stream-params' }],
41
- },
42
- },
43
- {
44
- manifest: { id: 'provider-onvif' },
45
- declaration: {
46
- id: 'provider-onvif',
47
- category: 'providers',
48
- capabilities: [{ name: 'device-provider' }],
49
- },
50
- },
51
- {
52
- manifest: { id: 'stream-broker' },
53
- declaration: {
54
- id: 'stream-broker',
55
- category: 'pipeline',
56
- capabilities: [{ name: 'stream-broker' }],
57
- },
58
- },
59
- {
60
- manifest: { id: 'sqlite-settings' },
61
- declaration: { id: 'sqlite-settings', capabilities: [{ name: 'settings-store' }] },
62
- },
63
- ],
64
- }
65
- }
66
-
67
- describe('computeTopology categories[] aggregation', () => {
68
- it('groups addons by manifest.category, defaults to system, emits totals', async () => {
69
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
- const nodes = await computeTopology(makeAgentRegistry() as any, makeAddonRegistry() as any)
71
- expect(nodes).toHaveLength(1)
72
- const cats = nodes[0]!.categories
73
- const byId = new Map(cats.map((c) => [c.category, c]))
74
- expect(byId.get('providers')?.total).toBe(2)
75
- expect(byId.get('providers')?.healthy).toBe(2)
76
- expect(
77
- byId
78
- .get('providers')
79
- ?.addons.map((a) => a.id)
80
- .toSorted(),
81
- ).toEqual(['provider-hikvision', 'provider-onvif'])
82
- expect(byId.get('pipeline')?.total).toBe(1)
83
- expect(byId.get('system')?.addons.map((a) => a.id)).toEqual(['sqlite-settings'])
84
- })
85
-
86
- it('counts a non-running addon as not-healthy', async () => {
87
- const agentReg = makeAgentRegistry()
88
- // Force the addon registry to return one stopped + one running provider.
89
- const addonReg = {
90
- listAddons: () => [
91
- {
92
- manifest: { id: 'provider-rtsp' },
93
- declaration: { id: 'provider-rtsp', category: 'providers', capabilities: [] },
94
- },
95
- {
96
- manifest: { id: 'provider-hikvision' },
97
- declaration: { id: 'provider-hikvision', category: 'providers', capabilities: [] },
98
- status: 'failed',
99
- },
100
- ],
101
- }
102
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
103
- const nodes = await computeTopology(agentReg as any, addonReg as any)
104
- const providers = nodes[0]!.categories.find((c) => c.category === 'providers')!
105
- expect(providers.total).toBe(2)
106
- // The current TopologyNode `addons[].status` is always `'running'` per existing code;
107
- // failed addons would surface through subProcesses[].state. Document the current contract:
108
- expect(providers.healthy).toBe(2)
109
- })
110
- })
@@ -1,292 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest'
2
- import { buildIntegrationsProvider } from '../../api/core/cap-providers'
3
- import type { CapabilityRegistry } from '@camstack/kernel'
4
- import type { Integration } from '@camstack/types'
5
-
6
- // ── Minimal stubs ────────────────────────────────────────────────────────────
7
-
8
- const INTEGRATION_ID = 'integ-abc-123'
9
- const ADDON_ID = 'provider-test'
10
-
11
- function makeIntegration(id = INTEGRATION_ID): Integration {
12
- return {
13
- id,
14
- addonId: ADDON_ID,
15
- name: 'Test Integration',
16
- enabled: true,
17
- info: null,
18
- settings: null,
19
- createdAt: new Date().toISOString(),
20
- updatedAt: new Date().toISOString(),
21
- } as unknown as Integration
22
- }
23
-
24
- function makeIntegrationRegistry(integration: Integration | null = makeIntegration()) {
25
- return {
26
- getIntegration: vi.fn(async (_id: string) => integration),
27
- deleteIntegration: vi.fn(async (_id: string) => undefined),
28
- listIntegrations: vi.fn(async () => (integration ? [integration] : [])),
29
- createIntegration: vi.fn(),
30
- updateIntegration: vi.fn(),
31
- getIntegrationByAddonId: vi.fn(),
32
- getIntegrationSettings: vi.fn(),
33
- setIntegrationSettings: vi.fn(),
34
- }
35
- }
36
-
37
- function makeAddonRegistry(reg = makeIntegrationRegistry()) {
38
- return {
39
- getIntegrationRegistry: vi.fn(() => reg),
40
- listAddons: vi.fn(() => []),
41
- restartAddon: vi.fn(),
42
- getCapabilityRegistry: vi.fn(() => ({
43
- getProviderByAddon: vi.fn(() => null),
44
- })),
45
- }
46
- }
47
-
48
- function makeEventBus() {
49
- return { emit: vi.fn() }
50
- }
51
-
52
- function makeLogger() {
53
- return {
54
- info: vi.fn(),
55
- warn: vi.fn(),
56
- error: vi.fn(),
57
- }
58
- }
59
-
60
- function makeLoggingService(log = makeLogger()) {
61
- return { createLogger: vi.fn(() => log) }
62
- }
63
-
64
- function makeCapabilityRegistry(
65
- removeByIntegration:
66
- | ((input: { integrationId: string }) => Promise<{ removed: number }>)
67
- | null = vi.fn(async () => ({ removed: 2 })),
68
- ): CapabilityRegistry {
69
- return {
70
- getSingleton: vi.fn((_cap: string) => (removeByIntegration ? { removeByIntegration } : null)),
71
- } as unknown as CapabilityRegistry
72
- }
73
-
74
- // ── Tests ────────────────────────────────────────────────────────────────────
75
-
76
- describe('integrations.delete cascade-removes devices via removeByIntegration', () => {
77
- let integrationReg: ReturnType<typeof makeIntegrationRegistry>
78
- let addonReg: ReturnType<typeof makeAddonRegistry>
79
- let eb: ReturnType<typeof makeEventBus>
80
- let log: ReturnType<typeof makeLogger>
81
- let loggingService: ReturnType<typeof makeLoggingService>
82
-
83
- beforeEach(() => {
84
- integrationReg = makeIntegrationRegistry()
85
- addonReg = makeAddonRegistry(integrationReg)
86
- eb = makeEventBus()
87
- log = makeLogger()
88
- loggingService = makeLoggingService(log)
89
- })
90
-
91
- it('calls removeByIntegration with the integration id before deleteIntegration', async () => {
92
- const removeByIntegration = vi.fn(async () => ({ removed: 3 }))
93
- const capReg = makeCapabilityRegistry(removeByIntegration)
94
-
95
- const provider = buildIntegrationsProvider(
96
- addonReg as never,
97
- eb as never,
98
- loggingService as never,
99
- capReg,
100
- )
101
- await provider.delete({ id: INTEGRATION_ID })
102
-
103
- expect(removeByIntegration).toHaveBeenCalledOnce()
104
- expect(removeByIntegration).toHaveBeenCalledWith({ integrationId: INTEGRATION_ID })
105
- })
106
-
107
- it('still deletes the integration record and emits integration.deleted after the cascade', async () => {
108
- const capReg = makeCapabilityRegistry()
109
-
110
- const provider = buildIntegrationsProvider(
111
- addonReg as never,
112
- eb as never,
113
- loggingService as never,
114
- capReg,
115
- )
116
- const result = await provider.delete({ id: INTEGRATION_ID })
117
-
118
- expect(integrationReg.deleteIntegration).toHaveBeenCalledOnce()
119
- expect(integrationReg.deleteIntegration).toHaveBeenCalledWith(INTEGRATION_ID)
120
-
121
- expect(eb.emit).toHaveBeenCalledOnce()
122
- expect(eb.emit).toHaveBeenCalledWith(
123
- expect.objectContaining({
124
- category: 'integration.deleted',
125
- data: expect.objectContaining({ integrationId: INTEGRATION_ID }),
126
- }),
127
- )
128
-
129
- expect(result).toEqual({ success: true, deletedId: INTEGRATION_ID })
130
- })
131
-
132
- it('logs the removed count from removeByIntegration', async () => {
133
- const removeByIntegration = vi.fn(async () => ({ removed: 5 }))
134
- const capReg = makeCapabilityRegistry(removeByIntegration)
135
-
136
- const provider = buildIntegrationsProvider(
137
- addonReg as never,
138
- eb as never,
139
- loggingService as never,
140
- capReg,
141
- )
142
- await provider.delete({ id: INTEGRATION_ID })
143
-
144
- expect(log.info).toHaveBeenCalledWith(
145
- 'cascade-removed devices',
146
- expect.objectContaining({ meta: expect.objectContaining({ removed: 5 }) }),
147
- )
148
- })
149
-
150
- it('does NOT abort the integration delete when removeByIntegration throws (best-effort)', async () => {
151
- const removeByIntegration = vi.fn(async () => {
152
- throw new Error('device-manager transient error')
153
- })
154
- const capReg = makeCapabilityRegistry(removeByIntegration)
155
-
156
- const provider = buildIntegrationsProvider(
157
- addonReg as never,
158
- eb as never,
159
- loggingService as never,
160
- capReg,
161
- )
162
-
163
- // Should NOT throw
164
- await expect(provider.delete({ id: INTEGRATION_ID })).resolves.toEqual({
165
- success: true,
166
- deletedId: INTEGRATION_ID,
167
- })
168
-
169
- // Integration record deletion and event still fire
170
- expect(integrationReg.deleteIntegration).toHaveBeenCalledOnce()
171
- expect(eb.emit).toHaveBeenCalledOnce()
172
-
173
- // A warning is logged
174
- expect(log.warn).toHaveBeenCalledWith(
175
- 'device cascade-remove failed (best-effort — continuing)',
176
- expect.anything(),
177
- )
178
- })
179
-
180
- it('warns and skips cascade when capabilityRegistry is null', async () => {
181
- const provider = buildIntegrationsProvider(
182
- addonReg as never,
183
- eb as never,
184
- loggingService as never,
185
- null,
186
- )
187
- await provider.delete({ id: INTEGRATION_ID })
188
-
189
- expect(integrationReg.deleteIntegration).toHaveBeenCalledOnce()
190
- expect(eb.emit).toHaveBeenCalledOnce()
191
- expect(log.warn).toHaveBeenCalledWith(
192
- 'device-manager not available — skipping cascade device removal',
193
- expect.anything(),
194
- )
195
- })
196
-
197
- it('warns and skips cascade when device-manager singleton is not registered', async () => {
198
- const capReg = makeCapabilityRegistry(null)
199
-
200
- const provider = buildIntegrationsProvider(
201
- addonReg as never,
202
- eb as never,
203
- loggingService as never,
204
- capReg,
205
- )
206
- await provider.delete({ id: INTEGRATION_ID })
207
-
208
- expect(integrationReg.deleteIntegration).toHaveBeenCalledOnce()
209
- expect(log.warn).toHaveBeenCalledWith(
210
- 'device-manager not available — skipping cascade device removal',
211
- expect.anything(),
212
- )
213
- })
214
-
215
- it('stamps legacy un-tagged devices of the addon BEFORE cascade so they are removed too', async () => {
216
- const removeByIntegration = vi.fn(async () => ({ removed: 1 }))
217
- const setIntegrationId = vi.fn(
218
- async (_input: { deviceId: number; integrationId: string }) => undefined,
219
- )
220
- // One un-tagged top-level device of the integration's addon (claimable),
221
- // one already-tagged (skip), one child (skip), one other-addon (skip).
222
- const listAll = vi.fn(async () => [
223
- { id: 4, addonId: ADDON_ID, parentDeviceId: null },
224
- { id: 5, addonId: ADDON_ID, parentDeviceId: null, integrationId: INTEGRATION_ID },
225
- { id: 6, addonId: ADDON_ID, parentDeviceId: 4 },
226
- { id: 7, addonId: 'provider-other', parentDeviceId: null },
227
- ])
228
- const capReg = {
229
- getSingleton: vi.fn(() => ({ removeByIntegration, listAll, setIntegrationId })),
230
- } as unknown as CapabilityRegistry
231
-
232
- const provider = buildIntegrationsProvider(
233
- addonReg as never,
234
- eb as never,
235
- loggingService as never,
236
- capReg,
237
- )
238
- await provider.delete({ id: INTEGRATION_ID })
239
-
240
- // Only device 4 is claimed (untagged, top-level, this addon's single integration).
241
- expect(setIntegrationId).toHaveBeenCalledOnce()
242
- expect(setIntegrationId).toHaveBeenCalledWith({ deviceId: 4, integrationId: INTEGRATION_ID })
243
- // Cascade still runs after the claim.
244
- expect(removeByIntegration).toHaveBeenCalledWith({ integrationId: INTEGRATION_ID })
245
- })
246
-
247
- it('claim failure does not abort the integration delete (best-effort)', async () => {
248
- const removeByIntegration = vi.fn(async () => ({ removed: 0 }))
249
- const setIntegrationId = vi.fn(async () => {
250
- throw new Error('stamp boom')
251
- })
252
- const listAll = vi.fn(async () => [{ id: 4, addonId: ADDON_ID, parentDeviceId: null }])
253
- const capReg = {
254
- getSingleton: vi.fn(() => ({ removeByIntegration, listAll, setIntegrationId })),
255
- } as unknown as CapabilityRegistry
256
-
257
- const provider = buildIntegrationsProvider(
258
- addonReg as never,
259
- eb as never,
260
- loggingService as never,
261
- capReg,
262
- )
263
- await expect(provider.delete({ id: INTEGRATION_ID })).resolves.toEqual({
264
- success: true,
265
- deletedId: INTEGRATION_ID,
266
- })
267
- expect(integrationReg.deleteIntegration).toHaveBeenCalledOnce()
268
- expect(log.warn).toHaveBeenCalledWith(
269
- 'legacy device claim failed (best-effort — continuing)',
270
- expect.anything(),
271
- )
272
- })
273
-
274
- it('throws when the integration is not found (guard still fires before cascade)', async () => {
275
- integrationReg = makeIntegrationRegistry(null)
276
- addonReg = makeAddonRegistry(integrationReg)
277
- const capReg = makeCapabilityRegistry()
278
-
279
- const provider = buildIntegrationsProvider(
280
- addonReg as never,
281
- eb as never,
282
- loggingService as never,
283
- capReg,
284
- )
285
-
286
- await expect(provider.delete({ id: 'missing-id' })).rejects.toThrow('not found')
287
-
288
- // Nothing should have been cleaned up
289
- expect(integrationReg.deleteIntegration).not.toHaveBeenCalled()
290
- expect(eb.emit).not.toHaveBeenCalled()
291
- })
292
- })