@camstack/server 0.2.2 → 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,147 +0,0 @@
1
- /**
2
- * Client-IP extraction + LAN/remote classification for the tRPC layer.
3
- *
4
- * Used by the `webrtcSession.createSession` override to decide whether a
5
- * live-view session should force TURN-relay-only ICE: a viewer behind
6
- * CGNAT/4G (a non-LAN source IP) only ever offers a relay candidate, so
7
- * the broker must offer a genuinely relay-only SDP (the patched werift
8
- * emits one under `iceTransportPolicy:'relay'`) to get a clean
9
- * relay↔relay media path. LAN viewers (private/loopback source IP) keep
10
- * the low-latency direct host/srflx path. The SERVER computes this — the
11
- * client never sends a relay-only flag.
12
- */
13
- import type { FastifyRequest } from 'fastify'
14
- import type { IncomingMessage } from 'node:http'
15
-
16
- export type ClientRequest = FastifyRequest | IncomingMessage
17
-
18
- /**
19
- * Best-effort extraction of the originating client IP from a tRPC
20
- * request. Order of preference:
21
- * 1. `X-Forwarded-For` first hop (when behind a reverse proxy — the
22
- * hub is typically fronted by Caddy/nginx/Cloudflare for remote
23
- * access, so the socket peer is the proxy, not the viewer).
24
- * 2. Fastify's `req.ip` (already proxy-aware when `trustProxy` is on).
25
- * 3. The raw socket remote address.
26
- *
27
- * Returns `null` when no address can be determined (e.g. mesh-originated
28
- * calls that carry no HTTP request) — the caller treats `null` as "not
29
- * remote" so the LAN/direct path stays the safe default.
30
- */
31
- export function extractClientIp(req: ClientRequest | undefined): string | null {
32
- if (!req) return null
33
-
34
- // 1. X-Forwarded-For — comma-separated list, client is the FIRST entry.
35
- const xff = req.headers['x-forwarded-for']
36
- const xffValue = Array.isArray(xff) ? xff[0] : xff
37
- if (typeof xffValue === 'string' && xffValue.length > 0) {
38
- const first = xffValue.split(',')[0]?.trim()
39
- if (first) return normalizeIp(first)
40
- }
41
-
42
- // 2. Fastify's parsed `req.ip` (honours `trustProxy` config).
43
- if ('ip' in req && typeof req.ip === 'string' && req.ip.length > 0) {
44
- return normalizeIp(req.ip)
45
- }
46
-
47
- // 3. Raw socket peer (WS / IncomingMessage path).
48
- const remote = req.socket?.remoteAddress
49
- if (typeof remote === 'string' && remote.length > 0) {
50
- return normalizeIp(remote)
51
- }
52
-
53
- return null
54
- }
55
-
56
- /**
57
- * Best-effort read of the originating client's `User-Agent` header. Both
58
- * request shapes in the union (`FastifyRequest` and the raw WS-transport
59
- * `IncomingMessage`) expose `.headers['user-agent']`. Returns `null` when
60
- * absent (mesh-originated calls carry no HTTP request, and some clients
61
- * omit the header). Used by the `webrtc-session` mount to tag a browser
62
- * subscriber's broker attribution with the viewer's UA — the SERVER reads
63
- * it from the request context; any client-supplied value is ignored.
64
- */
65
- export function extractUserAgent(req: ClientRequest | undefined): string | null {
66
- if (!req) return null
67
- const ua = req.headers['user-agent']
68
- const value = Array.isArray(ua) ? ua[0] : ua
69
- if (typeof value === 'string' && value.length > 0) return value
70
- return null
71
- }
72
-
73
- /**
74
- * Strip an IPv4-mapped IPv6 prefix (`::ffff:192.168.1.5` → `192.168.1.5`)
75
- * and any zone id (`fe80::1%en0` → `fe80::1`) so the range checks below
76
- * see a clean address.
77
- */
78
- function normalizeIp(ip: string): string {
79
- let out = ip.trim()
80
- if (out.startsWith('::ffff:')) out = out.slice('::ffff:'.length)
81
- const pct = out.indexOf('%')
82
- if (pct !== -1) out = out.slice(0, pct)
83
- return out
84
- }
85
-
86
- /**
87
- * True when the address is NOT in a private / loopback / link-local /
88
- * Tailscale range — i.e. an internet (remote) viewer. Covers IPv4
89
- * (10/8, 172.16/12, 192.168/16, 127/8, 100.64/10 Tailscale CGNAT) and
90
- * IPv6 (::1, fc00::/7 unique-local, fe80::/10 link-local — fd7a::/16
91
- * Tailscale ULA is a subset of fc00::/7 and is therefore already
92
- * covered).
93
- *
94
- * Tailscale clients (the 100.64.0.0/10 CGNAT overlay or the fd7a::/16
95
- * ULA overlay) are deliberately classified LOCAL: over the Tailscale
96
- * mesh BOTH peers sit on the 100.x / fd7a:: overlay and are mutually
97
- * reachable, so the broker offers ALL candidates (host/srflx/relay)
98
- * — including the hub's Tailscale host candidate — and a direct
99
- * host↔host pair wins ICE with native (non-re-encoded) media. Forcing
100
- * relay-only for Tailscale would push them onto the broken relay path.
101
- *
102
- * Unparseable or null addresses return `false` (treated as LAN — the
103
- * safe default that preserves the existing direct path).
104
- */
105
- export function isRemoteClientIp(ip: string | null): boolean {
106
- if (!ip) return false
107
- if (ip.includes(':')) return !isPrivateIpv6(ip)
108
- if (isIpv4(ip)) return !isPrivateIpv4(ip)
109
- // Not a recognisable IP literal — be conservative, treat as LAN.
110
- return false
111
- }
112
-
113
- function isIpv4(ip: string): boolean {
114
- const parts = ip.split('.')
115
- if (parts.length !== 4) return false
116
- return parts.every((p) => {
117
- if (!/^\d{1,3}$/.test(p)) return false
118
- const n = Number.parseInt(p, 10)
119
- return n >= 0 && n <= 255
120
- })
121
- }
122
-
123
- function isPrivateIpv4(ip: string): boolean {
124
- const parts = ip.split('.').map((p) => Number.parseInt(p, 10))
125
- const [a, b] = parts
126
- if (a === undefined || b === undefined) return false
127
- if (a === 10) return true // 10.0.0.0/8
128
- if (a === 127) return true // 127.0.0.0/8 loopback
129
- if (a === 172 && b >= 16 && b <= 31) return true // 172.16.0.0/12
130
- if (a === 192 && b === 168) return true // 192.168.0.0/16
131
- if (a === 169 && b === 254) return true // 169.254.0.0/16 link-local
132
- // 100.64.0.0/10 — Tailscale CGNAT overlay (100.64.0.0 – 100.127.255.255).
133
- // Both Tailscale peers are mutually reachable on this overlay, so treat
134
- // it as local (direct host↔host pair, no relay).
135
- if (a === 100 && b >= 64 && b <= 127) return true
136
- return false
137
- }
138
-
139
- function isPrivateIpv6(ip: string): boolean {
140
- const lower = ip.toLowerCase()
141
- if (lower === '::1') return true // loopback
142
- if (lower === '::') return true // unspecified
143
- if (lower.startsWith('fe80')) return true // fe80::/10 link-local
144
- // fc00::/7 unique-local — covers fc.. and fd..
145
- if (lower.startsWith('fc') || lower.startsWith('fd')) return true
146
- return false
147
- }
@@ -1,154 +0,0 @@
1
- /**
2
- * Core-capability mesh bridge.
3
- *
4
- * The hub's CORE routers — hand-written single-impl routers plus the
5
- * handful of service-backed cap routers — are mounted only as the hub's
6
- * tRPC `appRouter`. No addon registers a provider for them, so
7
- * `addon-service-factory` never publishes a Moleculer service exposing
8
- * their actions. A forked addon calling `ctx.api.<coreCap>.<method>`
9
- * therefore falls through `localProviderLink` into `brokerTransportLink`,
10
- * whose load-balanced discovery wait has no deadline — the call hangs
11
- * forever (this was the `export-alexa` `redetectHubUrl` hang).
12
- *
13
- * `buildCoreCapService` walks the appRouter, picks the core namespaces,
14
- * and wraps each query/mutation as a `$core-caps.<kebab-cap>.<method>`
15
- * Moleculer action invoked through a trusted-mesh tRPC caller. With the
16
- * service mounted on the hub node, `brokerTransportLink` resolves and
17
- * routes the call exactly like any addon-provided cap.
18
- */
19
- import type { ServiceSchema } from 'moleculer'
20
- import { createCoreCapService, type CoreCapAction } from '@camstack/kernel'
21
- import { createCallerFactory } from './trpc.middleware.js'
22
- import { createMeshTrpcContext } from './trpc.context.js'
23
- import type { AppRouter } from './trpc.router.js'
24
-
25
- /**
26
- * appRouter namespaces that back the CORE API surface and must be
27
- * reachable cross-process. This is the core-router list from
28
- * `trpc.router.ts` (`buildCapabilityRouters`) minus the deliberate
29
- * exclusions below.
30
- *
31
- * Excluded on purpose:
32
- * - `auth` — issuing service / scoped tokens over the trusted mesh
33
- * would let any forked addon mint admin credentials. Addons must
34
- * not perform authentication operations; this stays hub-local.
35
- * - `live`, `systemEvents` — listed in `NEVER_BRIDGED_CAPS`
36
- * (`trpc-links.ts`). They are hub-only push streams, not
37
- * request/reply, and the worker side fast-fails on them by design.
38
- */
39
- const CORE_NAMESPACES: ReadonlySet<string> = new Set<string>([
40
- // Service-backed cap routers (no addon provider).
41
- 'system',
42
- 'toast',
43
- 'integrations',
44
- 'nodes',
45
- 'addons',
46
- // Hand-written single-impl core routers.
47
- 'capabilities',
48
- 'notifications',
49
- 'logs',
50
- 'hwaccel',
51
- 'streamProbe',
52
- 'settingsBackend',
53
- 'eventBusProxy',
54
- 'repl',
55
- 'addonSettingsRaw',
56
- ])
57
-
58
- /**
59
- * camelCase → kebab-case. Mirrors `toKebab` in `trpc-links.ts` so the
60
- * action name matches what `brokerTransportLink` derives from `op.path`.
61
- */
62
- function toKebab(name: string): string {
63
- return name.replace(/[A-Z]/g, (m, i: number) => (i > 0 ? '-' : '') + m.toLowerCase())
64
- }
65
-
66
- /** A tRPC procedure node — distinguished from a router by `_def.procedure`. */
67
- interface ProcedureNode {
68
- readonly _def: { readonly procedure?: unknown; readonly type?: unknown }
69
- }
70
-
71
- function isProcedureNode(value: unknown): value is ProcedureNode {
72
- // A built tRPC procedure is a callable object — `_def.procedures`
73
- // stores the procedure function directly, not a wrapper object.
74
- if (value === null || (typeof value !== 'object' && typeof value !== 'function')) return false
75
- const def: unknown = (value as { _def?: unknown })._def
76
- return (
77
- def !== null && typeof def === 'object' && (def as { procedure?: unknown }).procedure === true
78
- )
79
- }
80
-
81
- interface DiscoveredProcedure {
82
- readonly path: string
83
- readonly type: string
84
- }
85
-
86
- /**
87
- * Recursively flatten a tRPC router record to dotted procedure paths.
88
- * Robust to both layouts tRPC v11 may use for `_def.procedures`: a
89
- * nested record of sub-routers, or a flat record already keyed by
90
- * dotted path (a flat key simply yields its key verbatim).
91
- */
92
- function collectProcedures(
93
- record: Readonly<Record<string, unknown>>,
94
- prefix: string,
95
- out: DiscoveredProcedure[],
96
- ): void {
97
- for (const [key, value] of Object.entries(record)) {
98
- const path = prefix.length > 0 ? `${prefix}.${key}` : key
99
- if (isProcedureNode(value)) {
100
- const type = value._def.type
101
- out.push({ path, type: typeof type === 'string' ? type : 'query' })
102
- } else if (value !== null && typeof value === 'object') {
103
- collectProcedures(value as Readonly<Record<string, unknown>>, path, out)
104
- }
105
- }
106
- }
107
-
108
- type ProcedureInvoker = (input: unknown) => Promise<unknown>
109
-
110
- /**
111
- * Navigate the decorated caller record (a recursive proxy) to the
112
- * procedure function at `path`. Every node on the proxy is callable,
113
- * so navigation uses `Reflect.get` across both objects and functions.
114
- */
115
- function resolveInvoker(caller: unknown, path: string): ProcedureInvoker | null {
116
- let node: unknown = caller
117
- for (const segment of path.split('.')) {
118
- if (node === null || (typeof node !== 'object' && typeof node !== 'function')) return null
119
- // Documented boundary: `node` is an object or function past the
120
- // guard above; `Reflect.get` is typed for `object` targets.
121
- node = Reflect.get(node as object, segment)
122
- }
123
- return typeof node === 'function' ? (node as ProcedureInvoker) : null
124
- }
125
-
126
- /**
127
- * Build the `$core-caps` Moleculer service schema from the hub
128
- * appRouter. Query + mutation procedures in {@link CORE_NAMESPACES} are
129
- * exposed; subscriptions are skipped (the broker transport is
130
- * request/reply only).
131
- */
132
- export function buildCoreCapService(appRouter: AppRouter): ServiceSchema {
133
- const meshCaller: unknown = createCallerFactory(appRouter)(createMeshTrpcContext())
134
-
135
- const discovered: DiscoveredProcedure[] = []
136
- collectProcedures(appRouter._def.procedures, '', discovered)
137
-
138
- const actions: CoreCapAction[] = []
139
- for (const { path, type } of discovered) {
140
- const dot = path.indexOf('.')
141
- if (dot < 0) continue
142
- const namespace = path.slice(0, dot)
143
- if (!CORE_NAMESPACES.has(namespace)) continue
144
- if (type === 'subscription') continue
145
-
146
- const invoke = resolveInvoker(meshCaller, path)
147
- if (invoke === null) continue
148
-
149
- const method = path.slice(dot + 1)
150
- actions.push({ actionName: `${toKebab(namespace)}.${method}`, invoke })
151
- }
152
-
153
- return createCoreCapService({ actions })
154
- }