threadforge 0.1.1 → 0.2.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 (358) hide show
  1. package/README.md +52 -20
  2. package/bin/forge.js +2 -1058
  3. package/bin/host-commands.d.ts +2 -0
  4. package/bin/host-commands.d.ts.map +1 -0
  5. package/bin/host-commands.js +7 -8
  6. package/bin/platform-commands.d.ts +2 -0
  7. package/bin/platform-commands.d.ts.map +1 -0
  8. package/bin/platform-commands.js +118 -36
  9. package/dist/cli/base-command.d.ts +12 -0
  10. package/dist/cli/base-command.d.ts.map +1 -0
  11. package/dist/cli/base-command.js +25 -0
  12. package/dist/cli/base-command.js.map +1 -0
  13. package/dist/cli/commands/build.d.ts +10 -0
  14. package/dist/cli/commands/build.d.ts.map +1 -0
  15. package/dist/cli/commands/build.js +110 -0
  16. package/dist/cli/commands/build.js.map +1 -0
  17. package/dist/cli/commands/deploy.d.ts +12 -0
  18. package/dist/cli/commands/deploy.d.ts.map +1 -0
  19. package/dist/cli/commands/deploy.js +143 -0
  20. package/dist/cli/commands/deploy.js.map +1 -0
  21. package/dist/cli/commands/dev.d.ts +10 -0
  22. package/dist/cli/commands/dev.d.ts.map +1 -0
  23. package/dist/cli/commands/dev.js +138 -0
  24. package/dist/cli/commands/dev.js.map +1 -0
  25. package/dist/cli/commands/generate.d.ts +10 -0
  26. package/dist/cli/commands/generate.d.ts.map +1 -0
  27. package/dist/cli/commands/generate.js +76 -0
  28. package/dist/cli/commands/generate.js.map +1 -0
  29. package/dist/cli/commands/host.d.ts +8 -0
  30. package/dist/cli/commands/host.d.ts.map +1 -0
  31. package/dist/cli/commands/host.js +20 -0
  32. package/dist/cli/commands/host.js.map +1 -0
  33. package/dist/cli/commands/init.d.ts +16 -0
  34. package/dist/cli/commands/init.d.ts.map +1 -0
  35. package/dist/cli/commands/init.js +246 -0
  36. package/dist/cli/commands/init.js.map +1 -0
  37. package/dist/cli/commands/platform.d.ts +8 -0
  38. package/dist/cli/commands/platform.d.ts.map +1 -0
  39. package/dist/cli/commands/platform.js +20 -0
  40. package/dist/cli/commands/platform.js.map +1 -0
  41. package/dist/cli/commands/restart.d.ts +8 -0
  42. package/dist/cli/commands/restart.d.ts.map +1 -0
  43. package/dist/cli/commands/restart.js +13 -0
  44. package/dist/cli/commands/restart.js.map +1 -0
  45. package/dist/cli/commands/scaffold/frontend.d.ts +10 -0
  46. package/dist/cli/commands/scaffold/frontend.d.ts.map +1 -0
  47. package/dist/cli/commands/scaffold/frontend.js +130 -0
  48. package/dist/cli/commands/scaffold/frontend.js.map +1 -0
  49. package/dist/cli/commands/scaffold/react.d.ts +7 -0
  50. package/dist/cli/commands/scaffold/react.d.ts.map +1 -0
  51. package/dist/cli/commands/scaffold/react.js +12 -0
  52. package/dist/cli/commands/scaffold/react.js.map +1 -0
  53. package/dist/cli/commands/scale.d.ts +8 -0
  54. package/dist/cli/commands/scale.d.ts.map +1 -0
  55. package/dist/cli/commands/scale.js +13 -0
  56. package/dist/cli/commands/scale.js.map +1 -0
  57. package/dist/cli/commands/start.d.ts +10 -0
  58. package/dist/cli/commands/start.d.ts.map +1 -0
  59. package/dist/cli/commands/start.js +71 -0
  60. package/dist/cli/commands/start.js.map +1 -0
  61. package/dist/cli/commands/status.d.ts +11 -0
  62. package/dist/cli/commands/status.d.ts.map +1 -0
  63. package/dist/cli/commands/status.js +60 -0
  64. package/dist/cli/commands/status.js.map +1 -0
  65. package/dist/cli/commands/stop.d.ts +10 -0
  66. package/dist/cli/commands/stop.d.ts.map +1 -0
  67. package/dist/cli/commands/stop.js +89 -0
  68. package/dist/cli/commands/stop.js.map +1 -0
  69. package/dist/cli/util/config-discovery.d.ts +8 -0
  70. package/dist/cli/util/config-discovery.d.ts.map +1 -0
  71. package/dist/cli/util/config-discovery.js +70 -0
  72. package/dist/cli/util/config-discovery.js.map +1 -0
  73. package/dist/cli/util/config-patcher.d.ts +17 -0
  74. package/dist/cli/util/config-patcher.d.ts.map +1 -0
  75. package/dist/cli/util/config-patcher.js +439 -0
  76. package/dist/cli/util/config-patcher.js.map +1 -0
  77. package/dist/cli/util/frontend-dev.d.ts +8 -0
  78. package/dist/cli/util/frontend-dev.d.ts.map +1 -0
  79. package/dist/cli/util/frontend-dev.js +117 -0
  80. package/dist/cli/util/frontend-dev.js.map +1 -0
  81. package/dist/cli/util/process.d.ts +5 -0
  82. package/dist/cli/util/process.d.ts.map +1 -0
  83. package/dist/cli/util/process.js +17 -0
  84. package/dist/cli/util/process.js.map +1 -0
  85. package/dist/cli/util/templates.d.ts +10 -0
  86. package/dist/cli/util/templates.d.ts.map +1 -0
  87. package/dist/cli/util/templates.js +157 -0
  88. package/dist/cli/util/templates.js.map +1 -0
  89. package/dist/core/AlertSink.d.ts +83 -0
  90. package/dist/core/AlertSink.d.ts.map +1 -0
  91. package/dist/core/AlertSink.js +126 -0
  92. package/dist/core/AlertSink.js.map +1 -0
  93. package/dist/core/DirectMessageBus.d.ts +88 -0
  94. package/dist/core/DirectMessageBus.d.ts.map +1 -0
  95. package/dist/core/DirectMessageBus.js +352 -0
  96. package/dist/core/DirectMessageBus.js.map +1 -0
  97. package/dist/core/EndpointResolver.d.ts +111 -0
  98. package/dist/core/EndpointResolver.d.ts.map +1 -0
  99. package/dist/core/EndpointResolver.js +336 -0
  100. package/dist/core/EndpointResolver.js.map +1 -0
  101. package/dist/core/ForgeContext.d.ts +221 -0
  102. package/dist/core/ForgeContext.d.ts.map +1 -0
  103. package/dist/core/ForgeContext.js +1169 -0
  104. package/dist/core/ForgeContext.js.map +1 -0
  105. package/dist/core/ForgeEndpoints.d.ts +71 -0
  106. package/dist/core/ForgeEndpoints.d.ts.map +1 -0
  107. package/dist/core/ForgeEndpoints.js +442 -0
  108. package/dist/core/ForgeEndpoints.js.map +1 -0
  109. package/dist/core/ForgeHost.d.ts +82 -0
  110. package/dist/core/ForgeHost.d.ts.map +1 -0
  111. package/dist/core/ForgeHost.js +107 -0
  112. package/dist/core/ForgeHost.js.map +1 -0
  113. package/dist/core/ForgePlatform.d.ts +96 -0
  114. package/dist/core/ForgePlatform.d.ts.map +1 -0
  115. package/dist/core/ForgePlatform.js +136 -0
  116. package/dist/core/ForgePlatform.js.map +1 -0
  117. package/dist/core/ForgeWebSocket.d.ts +56 -0
  118. package/dist/core/ForgeWebSocket.d.ts.map +1 -0
  119. package/dist/core/ForgeWebSocket.js +415 -0
  120. package/dist/core/ForgeWebSocket.js.map +1 -0
  121. package/dist/core/Ingress.d.ts +329 -0
  122. package/dist/core/Ingress.d.ts.map +1 -0
  123. package/dist/core/Ingress.js +694 -0
  124. package/dist/core/Ingress.js.map +1 -0
  125. package/dist/core/Interceptors.d.ts +134 -0
  126. package/dist/core/Interceptors.d.ts.map +1 -0
  127. package/dist/core/Interceptors.js +416 -0
  128. package/dist/core/Interceptors.js.map +1 -0
  129. package/dist/core/Logger.d.ts +20 -0
  130. package/dist/core/Logger.d.ts.map +1 -0
  131. package/dist/core/Logger.js +77 -0
  132. package/dist/core/Logger.js.map +1 -0
  133. package/dist/core/MessageBus.d.ts +15 -0
  134. package/dist/core/MessageBus.d.ts.map +1 -0
  135. package/dist/core/MessageBus.js +18 -0
  136. package/dist/core/MessageBus.js.map +1 -0
  137. package/dist/core/Prometheus.d.ts +80 -0
  138. package/dist/core/Prometheus.d.ts.map +1 -0
  139. package/dist/core/Prometheus.js +332 -0
  140. package/dist/core/Prometheus.js.map +1 -0
  141. package/dist/core/RequestContext.d.ts +214 -0
  142. package/dist/core/RequestContext.d.ts.map +1 -0
  143. package/dist/core/RequestContext.js +556 -0
  144. package/dist/core/RequestContext.js.map +1 -0
  145. package/dist/core/Router.d.ts +45 -0
  146. package/dist/core/Router.d.ts.map +1 -0
  147. package/dist/core/Router.js +285 -0
  148. package/dist/core/Router.js.map +1 -0
  149. package/dist/core/RoutingStrategy.d.ts +116 -0
  150. package/dist/core/RoutingStrategy.d.ts.map +1 -0
  151. package/dist/core/RoutingStrategy.js +306 -0
  152. package/dist/core/RoutingStrategy.js.map +1 -0
  153. package/dist/core/RpcConfig.d.ts +72 -0
  154. package/dist/core/RpcConfig.d.ts.map +1 -0
  155. package/dist/core/RpcConfig.js +127 -0
  156. package/dist/core/RpcConfig.js.map +1 -0
  157. package/dist/core/SignatureCache.d.ts +81 -0
  158. package/dist/core/SignatureCache.d.ts.map +1 -0
  159. package/dist/core/SignatureCache.js +172 -0
  160. package/dist/core/SignatureCache.js.map +1 -0
  161. package/dist/core/StaticFileServer.d.ts +34 -0
  162. package/dist/core/StaticFileServer.d.ts.map +1 -0
  163. package/dist/core/StaticFileServer.js +497 -0
  164. package/dist/core/StaticFileServer.js.map +1 -0
  165. package/dist/core/Supervisor.d.ts +198 -0
  166. package/dist/core/Supervisor.d.ts.map +1 -0
  167. package/dist/core/Supervisor.js +1418 -0
  168. package/dist/core/Supervisor.js.map +1 -0
  169. package/dist/core/ThreadAllocator.d.ts +52 -0
  170. package/dist/core/ThreadAllocator.d.ts.map +1 -0
  171. package/dist/core/ThreadAllocator.js +174 -0
  172. package/dist/core/ThreadAllocator.js.map +1 -0
  173. package/dist/core/WorkerChannelManager.d.ts +130 -0
  174. package/dist/core/WorkerChannelManager.d.ts.map +1 -0
  175. package/dist/core/WorkerChannelManager.js +956 -0
  176. package/dist/core/WorkerChannelManager.js.map +1 -0
  177. package/dist/core/config-enums.d.ts +41 -0
  178. package/dist/core/config-enums.d.ts.map +1 -0
  179. package/dist/core/config-enums.js +59 -0
  180. package/dist/core/config-enums.js.map +1 -0
  181. package/dist/core/config.d.ts +159 -0
  182. package/dist/core/config.d.ts.map +1 -0
  183. package/dist/core/config.js +694 -0
  184. package/dist/core/config.js.map +1 -0
  185. package/dist/core/host-config.d.ts +146 -0
  186. package/dist/core/host-config.d.ts.map +1 -0
  187. package/dist/core/host-config.js +312 -0
  188. package/dist/core/host-config.js.map +1 -0
  189. package/dist/core/ipc-errors.d.ts +27 -0
  190. package/dist/core/ipc-errors.d.ts.map +1 -0
  191. package/dist/core/ipc-errors.js +36 -0
  192. package/dist/core/ipc-errors.js.map +1 -0
  193. package/dist/core/network-utils.d.ts +35 -0
  194. package/dist/core/network-utils.d.ts.map +1 -0
  195. package/dist/core/network-utils.js +145 -0
  196. package/dist/core/network-utils.js.map +1 -0
  197. package/dist/core/platform-config.d.ts +142 -0
  198. package/dist/core/platform-config.d.ts.map +1 -0
  199. package/dist/core/platform-config.js +299 -0
  200. package/dist/core/platform-config.js.map +1 -0
  201. package/dist/decorators/ServiceProxy.d.ts +175 -0
  202. package/dist/decorators/ServiceProxy.d.ts.map +1 -0
  203. package/dist/decorators/ServiceProxy.js +969 -0
  204. package/dist/decorators/ServiceProxy.js.map +1 -0
  205. package/dist/decorators/index.d.ts +146 -0
  206. package/dist/decorators/index.d.ts.map +1 -0
  207. package/dist/decorators/index.js +545 -0
  208. package/dist/decorators/index.js.map +1 -0
  209. package/dist/deploy/NginxGenerator.d.ts +165 -0
  210. package/dist/deploy/NginxGenerator.d.ts.map +1 -0
  211. package/dist/deploy/NginxGenerator.js +781 -0
  212. package/dist/deploy/NginxGenerator.js.map +1 -0
  213. package/dist/deploy/PlatformManifestGenerator.d.ts +43 -0
  214. package/dist/deploy/PlatformManifestGenerator.d.ts.map +1 -0
  215. package/dist/deploy/PlatformManifestGenerator.js +80 -0
  216. package/dist/deploy/PlatformManifestGenerator.js.map +1 -0
  217. package/dist/deploy/RouteManifestGenerator.d.ts +42 -0
  218. package/dist/deploy/RouteManifestGenerator.d.ts.map +1 -0
  219. package/dist/deploy/RouteManifestGenerator.js +105 -0
  220. package/dist/deploy/RouteManifestGenerator.js.map +1 -0
  221. package/dist/deploy/index.d.ts +210 -0
  222. package/dist/deploy/index.d.ts.map +1 -0
  223. package/dist/deploy/index.js +918 -0
  224. package/dist/deploy/index.js.map +1 -0
  225. package/dist/frontend/FrontendDevLifecycle.d.ts +26 -0
  226. package/dist/frontend/FrontendDevLifecycle.d.ts.map +1 -0
  227. package/dist/frontend/FrontendDevLifecycle.js +60 -0
  228. package/dist/frontend/FrontendDevLifecycle.js.map +1 -0
  229. package/dist/frontend/FrontendPluginOrchestrator.d.ts +64 -0
  230. package/dist/frontend/FrontendPluginOrchestrator.d.ts.map +1 -0
  231. package/dist/frontend/FrontendPluginOrchestrator.js +167 -0
  232. package/dist/frontend/FrontendPluginOrchestrator.js.map +1 -0
  233. package/dist/frontend/SiteResolver.d.ts +33 -0
  234. package/dist/frontend/SiteResolver.d.ts.map +1 -0
  235. package/dist/frontend/SiteResolver.js +53 -0
  236. package/dist/frontend/SiteResolver.js.map +1 -0
  237. package/dist/frontend/StaticMountRegistry.d.ts +36 -0
  238. package/dist/frontend/StaticMountRegistry.d.ts.map +1 -0
  239. package/dist/frontend/StaticMountRegistry.js +94 -0
  240. package/dist/frontend/StaticMountRegistry.js.map +1 -0
  241. package/dist/frontend/index.d.ts +7 -0
  242. package/dist/frontend/index.d.ts.map +1 -0
  243. package/{src → dist}/frontend/index.js +4 -2
  244. package/dist/frontend/index.js.map +1 -0
  245. package/dist/frontend/pathUtils.d.ts +8 -0
  246. package/dist/frontend/pathUtils.d.ts.map +1 -0
  247. package/dist/frontend/pathUtils.js +17 -0
  248. package/dist/frontend/pathUtils.js.map +1 -0
  249. package/dist/frontend/plugins/index.d.ts +2 -0
  250. package/dist/frontend/plugins/index.d.ts.map +1 -0
  251. package/{src → dist}/frontend/plugins/index.js +1 -1
  252. package/dist/frontend/plugins/index.js.map +1 -0
  253. package/dist/frontend/plugins/viteFrontend.d.ts +51 -0
  254. package/dist/frontend/plugins/viteFrontend.d.ts.map +1 -0
  255. package/dist/frontend/plugins/viteFrontend.js +134 -0
  256. package/dist/frontend/plugins/viteFrontend.js.map +1 -0
  257. package/dist/frontend/types.d.ts +25 -0
  258. package/dist/frontend/types.d.ts.map +1 -0
  259. package/dist/frontend/types.js +2 -0
  260. package/dist/frontend/types.js.map +1 -0
  261. package/dist/index.d.ts +17 -0
  262. package/dist/index.d.ts.map +1 -0
  263. package/dist/index.js +32 -0
  264. package/dist/index.js.map +1 -0
  265. package/dist/internals.d.ts +21 -0
  266. package/dist/internals.d.ts.map +1 -0
  267. package/{src → dist}/internals.js +12 -14
  268. package/dist/internals.js.map +1 -0
  269. package/dist/plugins/PluginManager.d.ts +209 -0
  270. package/dist/plugins/PluginManager.d.ts.map +1 -0
  271. package/dist/plugins/PluginManager.js +365 -0
  272. package/dist/plugins/PluginManager.js.map +1 -0
  273. package/dist/plugins/ScopedPostgres.d.ts +78 -0
  274. package/dist/plugins/ScopedPostgres.d.ts.map +1 -0
  275. package/dist/plugins/ScopedPostgres.js +190 -0
  276. package/dist/plugins/ScopedPostgres.js.map +1 -0
  277. package/dist/plugins/ScopedRedis.d.ts +88 -0
  278. package/dist/plugins/ScopedRedis.d.ts.map +1 -0
  279. package/dist/plugins/ScopedRedis.js +169 -0
  280. package/dist/plugins/ScopedRedis.js.map +1 -0
  281. package/dist/plugins/index.d.ts +289 -0
  282. package/dist/plugins/index.d.ts.map +1 -0
  283. package/dist/plugins/index.js +1942 -0
  284. package/dist/plugins/index.js.map +1 -0
  285. package/dist/plugins/types.d.ts +59 -0
  286. package/dist/plugins/types.d.ts.map +1 -0
  287. package/dist/plugins/types.js +2 -0
  288. package/dist/plugins/types.js.map +1 -0
  289. package/dist/registry/ServiceRegistry.d.ts +305 -0
  290. package/dist/registry/ServiceRegistry.d.ts.map +1 -0
  291. package/dist/registry/ServiceRegistry.js +735 -0
  292. package/dist/registry/ServiceRegistry.js.map +1 -0
  293. package/dist/scaling/ScaleAdvisor.d.ts +214 -0
  294. package/dist/scaling/ScaleAdvisor.d.ts.map +1 -0
  295. package/dist/scaling/ScaleAdvisor.js +526 -0
  296. package/dist/scaling/ScaleAdvisor.js.map +1 -0
  297. package/dist/services/Service.d.ts +164 -0
  298. package/dist/services/Service.d.ts.map +1 -0
  299. package/dist/services/Service.js +106 -0
  300. package/dist/services/Service.js.map +1 -0
  301. package/dist/services/worker-bootstrap.d.ts +15 -0
  302. package/dist/services/worker-bootstrap.d.ts.map +1 -0
  303. package/dist/services/worker-bootstrap.js +744 -0
  304. package/dist/services/worker-bootstrap.js.map +1 -0
  305. package/dist/templates/auth-service.d.ts +42 -0
  306. package/dist/templates/auth-service.d.ts.map +1 -0
  307. package/dist/templates/auth-service.js +54 -0
  308. package/dist/templates/auth-service.js.map +1 -0
  309. package/dist/templates/identity-service.d.ts +50 -0
  310. package/dist/templates/identity-service.d.ts.map +1 -0
  311. package/dist/templates/identity-service.js +62 -0
  312. package/dist/templates/identity-service.js.map +1 -0
  313. package/dist/types/contract.d.ts +120 -0
  314. package/dist/types/contract.d.ts.map +1 -0
  315. package/dist/types/contract.js +69 -0
  316. package/dist/types/contract.js.map +1 -0
  317. package/package.json +78 -20
  318. package/src/core/DirectMessageBus.js +0 -364
  319. package/src/core/EndpointResolver.js +0 -259
  320. package/src/core/ForgeContext.js +0 -2236
  321. package/src/core/ForgeHost.js +0 -122
  322. package/src/core/ForgePlatform.js +0 -145
  323. package/src/core/Ingress.js +0 -768
  324. package/src/core/Interceptors.js +0 -420
  325. package/src/core/MessageBus.js +0 -321
  326. package/src/core/Prometheus.js +0 -305
  327. package/src/core/RequestContext.js +0 -413
  328. package/src/core/RoutingStrategy.js +0 -330
  329. package/src/core/Supervisor.js +0 -1349
  330. package/src/core/ThreadAllocator.js +0 -196
  331. package/src/core/WorkerChannelManager.js +0 -879
  332. package/src/core/config.js +0 -637
  333. package/src/core/host-config.js +0 -311
  334. package/src/core/network-utils.js +0 -166
  335. package/src/core/platform-config.js +0 -308
  336. package/src/decorators/ServiceProxy.js +0 -904
  337. package/src/decorators/index.js +0 -571
  338. package/src/deploy/NginxGenerator.js +0 -865
  339. package/src/deploy/PlatformManifestGenerator.js +0 -96
  340. package/src/deploy/RouteManifestGenerator.js +0 -112
  341. package/src/deploy/index.js +0 -984
  342. package/src/frontend/FrontendDevLifecycle.js +0 -65
  343. package/src/frontend/FrontendPluginOrchestrator.js +0 -187
  344. package/src/frontend/SiteResolver.js +0 -63
  345. package/src/frontend/StaticMountRegistry.js +0 -90
  346. package/src/frontend/plugins/viteFrontend.js +0 -79
  347. package/src/frontend/types.js +0 -35
  348. package/src/index.js +0 -58
  349. package/src/plugins/PluginManager.js +0 -537
  350. package/src/plugins/ScopedPostgres.js +0 -192
  351. package/src/plugins/ScopedRedis.js +0 -142
  352. package/src/plugins/index.js +0 -1756
  353. package/src/registry/ServiceRegistry.js +0 -797
  354. package/src/scaling/ScaleAdvisor.js +0 -442
  355. package/src/services/Service.js +0 -195
  356. package/src/services/worker-bootstrap.js +0 -679
  357. package/src/templates/auth-service.js +0 -65
  358. package/src/templates/identity-service.js +0 -75
@@ -0,0 +1,306 @@
1
+ /**
2
+ * Routing Strategies
3
+ *
4
+ * When a service has multiple workers, the proxy needs to decide
5
+ * which worker handles each request. Different strategies suit
6
+ * different service patterns.
7
+ *
8
+ * Strategies:
9
+ *
10
+ * round-robin Default. Cycles through workers sequentially.
11
+ * Best for: stateless services, uniform workloads.
12
+ *
13
+ * hash Routes by a key (e.g., userId). Same key always
14
+ * goes to the same worker. Uses consistent hashing
15
+ * so adding/removing workers only remaps ~1/N keys.
16
+ * Best for: stateful services, in-memory caches,
17
+ * services that benefit from data locality.
18
+ *
19
+ * least-pending Routes to the worker with fewest in-flight requests.
20
+ * Best for: variable-latency services (DB queries,
21
+ * external API calls).
22
+ *
23
+ * broadcast Sends to ALL workers. Returns the first response.
24
+ * Best for: search/aggregation across sharded data.
25
+ *
26
+ * primary Always routes to worker 0. Others are standbys.
27
+ * Best for: singleton services (cron scheduler,
28
+ * leader election).
29
+ */
30
+ import { createHmac, randomBytes } from "node:crypto";
31
+ import { ROUTING_STRATEGY_NAMES, } from "./config-enums.js";
32
+ // RT-L1: Allow configurable salt via FORGE_HASH_SALT for cross-restart consistency
33
+ const HASH_SALT = process.env.FORGE_HASH_SALT || randomBytes(16).toString("hex");
34
+ // P-1: Fast non-cryptographic FNV-1a hash for runtime routing.
35
+ // HMAC-SHA256 is ~100x slower and unnecessary for load distribution.
36
+ function fnv1a(str) {
37
+ let hash = 0x811c9dc5 | 0; // FNV offset basis
38
+ for (let i = 0; i < str.length; i++) {
39
+ hash ^= str.charCodeAt(i);
40
+ hash = (hash * 0x01000193) | 0; // FNV prime
41
+ }
42
+ return hash >>> 0; // Convert to unsigned 32-bit
43
+ }
44
+ // ─── Round-Robin ────────────────────────────────────────────
45
+ export class RoundRobinStrategy {
46
+ index;
47
+ constructor() {
48
+ this.index = 0;
49
+ }
50
+ pick(workers, _callContext) {
51
+ if (workers.length === 0)
52
+ return null;
53
+ const worker = workers[this.index % workers.length];
54
+ this.index = (this.index + 1) % 1_000_000_000;
55
+ return worker;
56
+ }
57
+ get name() {
58
+ return "round-robin";
59
+ }
60
+ }
61
+ // ─── Hash-Based (Consistent Hashing) ────────────────────────
62
+ export class HashStrategy {
63
+ keyField;
64
+ vnodes;
65
+ // biome-ignore lint/correctness/noUnusedPrivateClassMembers: used as ring-built sentinel at runtime
66
+ _ring;
67
+ _workerCacheKey;
68
+ _ringHashes;
69
+ _ringKeys;
70
+ _fallbackCounter;
71
+ _salt;
72
+ constructor(options = {}) {
73
+ this.keyField = options.key ?? null;
74
+ this.vnodes = options.vnodes ?? 150;
75
+ this._ring = null;
76
+ this._workerCacheKey = "";
77
+ this._fallbackCounter = 0;
78
+ // RT-L1: Allow per-instance salt override for cross-restart consistency
79
+ this._salt = options.salt ?? HASH_SALT;
80
+ }
81
+ pick(workers, callContext) {
82
+ if (workers.length === 0)
83
+ return null;
84
+ if (workers.length === 1)
85
+ return workers[0];
86
+ // Extract the hash key from the call context
87
+ const hashKey = this._extractKey(callContext ?? null);
88
+ if (!hashKey) {
89
+ // No key available — fall back to counter-based round-robin
90
+ const idx = this._fallbackCounter % workers.length;
91
+ this._fallbackCounter = (this._fallbackCounter + 1) % 1_000_000_000;
92
+ return workers[idx];
93
+ }
94
+ // Rebuild ring when worker identities change (not just count).
95
+ // This ensures the ring is correct when workers restart with new keys.
96
+ const currentCacheKey = workers
97
+ .map((w) => w.key)
98
+ .sort()
99
+ .join(",");
100
+ if (this._workerCacheKey !== currentCacheKey) {
101
+ this._buildRing(workers);
102
+ }
103
+ // Find the worker on the ring
104
+ const hash = this._simpleHash(hashKey);
105
+ return this._findOnRing(hash, workers);
106
+ }
107
+ _extractKey(callContext) {
108
+ if (!callContext)
109
+ return null;
110
+ // If a key field is specified, look for it in the call args
111
+ if (this.keyField) {
112
+ const args = callContext.__forge_args ?? [];
113
+ // Check first arg (usually the main parameter)
114
+ if (args.length > 0) {
115
+ if (typeof args[0] === "string" || typeof args[0] === "number") {
116
+ return String(args[0]);
117
+ }
118
+ if (typeof args[0] === "object" && args[0] !== null && args[0][this.keyField]) {
119
+ return String(args[0][this.keyField]);
120
+ }
121
+ }
122
+ }
123
+ // Try to use __forge_hash if explicitly set by the caller
124
+ if (callContext.__forge_hash) {
125
+ return String(callContext.__forge_hash);
126
+ }
127
+ return null;
128
+ }
129
+ _buildRing(workers) {
130
+ // P5: Pre-compute all virtual node hashes and store sorted for binary search
131
+ const totalNodes = workers.length * this.vnodes;
132
+ const hashes = new Uint32Array(totalNodes);
133
+ // RT-H3: Store worker keys instead of positional indices to avoid
134
+ // incorrect mapping when the workers array arrives in a different order.
135
+ const keys = new Array(totalNodes);
136
+ let pos = 0;
137
+ for (let i = 0; i < workers.length; i++) {
138
+ for (let v = 0; v < this.vnodes; v++) {
139
+ hashes[pos] = this._computeHash(`${workers[i].key}:${v}`);
140
+ keys[pos] = workers[i].key;
141
+ pos++;
142
+ }
143
+ }
144
+ // Sort by hash value, keeping keys aligned
145
+ const sortOrder = Array.from({ length: totalNodes }, (_, i) => i);
146
+ sortOrder.sort((a, b) => hashes[a] - hashes[b]);
147
+ this._ringHashes = new Uint32Array(totalNodes);
148
+ this._ringKeys = new Array(totalNodes);
149
+ for (let i = 0; i < totalNodes; i++) {
150
+ this._ringHashes[i] = hashes[sortOrder[i]];
151
+ this._ringKeys[i] = keys[sortOrder[i]];
152
+ }
153
+ this._ring = true; // sentinel
154
+ this._workerCacheKey = workers
155
+ .map((w) => w.key)
156
+ .sort()
157
+ .join(",");
158
+ }
159
+ _findOnRing(hash, workers) {
160
+ const hashes = this._ringHashes;
161
+ // Binary search for the first ring entry >= hash
162
+ let lo = 0, hi = hashes.length;
163
+ while (lo < hi) {
164
+ const mid = (lo + hi) >>> 1;
165
+ if (hashes[mid] < hash)
166
+ lo = mid + 1;
167
+ else
168
+ hi = mid;
169
+ }
170
+ // Wrap around
171
+ const idx = lo < hashes.length ? lo : 0;
172
+ // RT-H3: Look up worker by key instead of positional index
173
+ const targetKey = this._ringKeys[idx];
174
+ return workers.find((w) => w.key === targetKey) ?? workers[0];
175
+ }
176
+ /**
177
+ * Pre-compute a keyed SHA-256 hash for ring building (called at build time).
178
+ */
179
+ _computeHash(str) {
180
+ return createHmac("sha256", this._salt).update(str).digest().readUInt32BE(0);
181
+ }
182
+ /**
183
+ * P-1: Fast FNV-1a hash for runtime routing decisions.
184
+ * Salted to prevent predictable distribution from external input.
185
+ */
186
+ _simpleHash(str) {
187
+ return fnv1a(this._salt + str);
188
+ }
189
+ get name() {
190
+ return `hash(${this.keyField ?? "auto"})`;
191
+ }
192
+ }
193
+ // ─── Least-Pending ──────────────────────────────────────────
194
+ export class LeastPendingStrategy {
195
+ _pending;
196
+ // RT-M1: Track worker identity set (not just count) for accurate pruning
197
+ _lastWorkerKeys;
198
+ constructor() {
199
+ this._pending = new Map();
200
+ this._lastWorkerKeys = "";
201
+ }
202
+ /**
203
+ * Pick the worker with the fewest in-flight requests.
204
+ */
205
+ pick(workers, _callContext) {
206
+ if (workers.length === 0)
207
+ return null;
208
+ if (workers.length === 1)
209
+ return workers[0];
210
+ // RT-M1: Prune when worker identity set changes (handles replacements at same count)
211
+ const currentKeys = workers.map((w) => w.key).sort().join(",");
212
+ if (this._pending.size > 0 && currentKeys !== this._lastWorkerKeys) {
213
+ const activeKeys = new Set(workers.map((w) => w.key));
214
+ for (const key of this._pending.keys()) {
215
+ if (!activeKeys.has(key)) {
216
+ this._pending.delete(key);
217
+ }
218
+ }
219
+ }
220
+ this._lastWorkerKeys = currentKeys;
221
+ let best = workers[0];
222
+ let bestPending = this._pending.get(best.key) ?? 0;
223
+ for (let i = 1; i < workers.length; i++) {
224
+ const p = this._pending.get(workers[i].key) ?? 0;
225
+ if (p < bestPending) {
226
+ best = workers[i];
227
+ bestPending = p;
228
+ }
229
+ }
230
+ return best;
231
+ }
232
+ /** Call when dispatching a request to a worker */
233
+ acquire(workerKey) {
234
+ this._pending.set(workerKey, (this._pending.get(workerKey) ?? 0) + 1);
235
+ }
236
+ /** Call when a request to a worker completes */
237
+ release(workerKey) {
238
+ const count = this._pending.get(workerKey) ?? 0;
239
+ if (count <= 0) {
240
+ if (process.env.NODE_ENV !== "production") {
241
+ console.warn("[LeastPendingStrategy] Spurious release()");
242
+ }
243
+ this._pending.delete(workerKey);
244
+ return;
245
+ }
246
+ if (count <= 1) {
247
+ this._pending.delete(workerKey);
248
+ }
249
+ else {
250
+ this._pending.set(workerKey, count - 1);
251
+ }
252
+ }
253
+ get name() {
254
+ return "least-pending";
255
+ }
256
+ }
257
+ // ─── Broadcast ──────────────────────────────────────────────
258
+ export class BroadcastStrategy {
259
+ /**
260
+ * Returns ALL workers. The caller is responsible for sending
261
+ * to all of them and handling multiple responses.
262
+ */
263
+ pick(workers, _callContext) {
264
+ return workers; // Return the full array — caller detects this
265
+ }
266
+ get name() {
267
+ return "broadcast";
268
+ }
269
+ get isBroadcast() {
270
+ return true;
271
+ }
272
+ }
273
+ // ─── Primary ────────────────────────────────────────────────
274
+ export class PrimaryStrategy {
275
+ /**
276
+ * Always routes to worker index 0. If worker 0 is down,
277
+ * routes to the next available.
278
+ */
279
+ pick(workers, _callContext) {
280
+ if (workers.length === 0)
281
+ return null;
282
+ return workers[0]; // Primary is always first
283
+ }
284
+ get name() {
285
+ return "primary";
286
+ }
287
+ }
288
+ // ─── Strategy Factory ───────────────────────────────────────
289
+ const STRATEGIES = {
290
+ "round-robin": (_opts) => new RoundRobinStrategy(),
291
+ hash: (opts) => new HashStrategy(opts),
292
+ "least-pending": (_opts) => new LeastPendingStrategy(),
293
+ broadcast: (_opts) => new BroadcastStrategy(),
294
+ primary: (_opts) => new PrimaryStrategy(),
295
+ };
296
+ /**
297
+ * Create a routing strategy by name.
298
+ */
299
+ export function createStrategy(name, options = {}) {
300
+ const factory = STRATEGIES[name];
301
+ if (!factory) {
302
+ throw new Error(`Unknown routing strategy: "${name}". Available: ${ROUTING_STRATEGY_NAMES.join(", ")}`);
303
+ }
304
+ return factory(options);
305
+ }
306
+ //# sourceMappingURL=RoutingStrategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RoutingStrategy.js","sourceRoot":"","sources":["../../src/core/RoutingStrategy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * RPC Configuration Utilities
3
+ *
4
+ * Centralizes validation, merge logic, and resolution for per-service
5
+ * and per-target RPC options (circuit breaker, retry, timeout, bulkhead,
6
+ * concurrency).
7
+ *
8
+ * Merge precedence (lowest → highest):
9
+ * 1. Framework defaults (in Interceptors.ts constructors)
10
+ * 2. Service-level `rpc` field
11
+ * 3. Target-level `rpcTargets[targetName]`
12
+ * 4. Contract-level options (static contract = { circuitBreaker: ... })
13
+ * 5. Per-method decorator options (@Expose({ retry: false }))
14
+ *
15
+ * Steps 1-3 are resolved here. Steps 4-5 are resolved in ServiceProxy.
16
+ */
17
+ export interface CircuitBreakerOptions {
18
+ failureThreshold?: number;
19
+ /** Percentage-based failure rate threshold (0-100). When set, the breaker
20
+ * opens if the failure rate within the sliding window exceeds this value
21
+ * AND the minimum sample count is met. Takes precedence over failureThreshold. */
22
+ failureRateThreshold?: number;
23
+ /** Minimum number of calls in the window before failure rate is evaluated. */
24
+ minimumCallCount?: number;
25
+ resetTimeoutMs?: number;
26
+ successThreshold?: number;
27
+ windowMs?: number;
28
+ /** Number of concurrent probe requests allowed in half-open state. */
29
+ halfOpenMaxProbes?: number;
30
+ }
31
+ export interface RetryOptions {
32
+ maxAttempts?: number;
33
+ baseDelayMs?: number;
34
+ maxDelayMs?: number;
35
+ }
36
+ export interface BulkheadOptions {
37
+ maxConcurrent?: number;
38
+ }
39
+ export interface ConcurrencyOptions {
40
+ initialLimit?: number;
41
+ minLimit?: number;
42
+ maxLimit?: number;
43
+ smoothing?: number;
44
+ tolerance?: number;
45
+ }
46
+ export interface RpcOptions {
47
+ circuitBreaker?: CircuitBreakerOptions;
48
+ retry?: RetryOptions | false;
49
+ defaultTimeout?: number;
50
+ bulkhead?: BulkheadOptions;
51
+ concurrency?: ConcurrencyOptions;
52
+ }
53
+ export interface RpcConfig {
54
+ rpc?: RpcOptions;
55
+ rpcTargets?: Record<string, RpcOptions>;
56
+ }
57
+ /**
58
+ * Validate an RPC options object shape.
59
+ */
60
+ export declare function validateRpcOptions(serviceName: string, label: string, options: unknown): void;
61
+ /**
62
+ * Deep-merge two RPC options objects. Override values take precedence.
63
+ * For nested objects (circuitBreaker, retry, bulkhead, concurrency),
64
+ * individual fields merge so you only need to specify what changes.
65
+ */
66
+ export declare function mergeRpcOptions(base?: RpcOptions, override?: RpcOptions): RpcOptions;
67
+ /**
68
+ * Resolve the final RPC options for a specific target service.
69
+ * Merges service-level `rpc` defaults with target-specific `rpcTargets` overrides.
70
+ */
71
+ export declare function resolveRpcOptionsForTarget(config: RpcConfig | undefined, targetName: string): RpcOptions;
72
+ //# sourceMappingURL=RpcConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RpcConfig.d.ts","sourceRoot":"","sources":["../../src/core/RpcConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,qBAAqB;IACpC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;uFAEmF;IACnF,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,cAAc,CAAC,EAAE,qBAAqB,CAAC;IACvC,KAAK,CAAC,EAAE,YAAY,GAAG,KAAK,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CACzC;AAqBD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAuC7F;AA0BD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,UAAU,GAAG,UAAU,CAmCpF;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,UAAU,CAIxG"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * RPC Configuration Utilities
3
+ *
4
+ * Centralizes validation, merge logic, and resolution for per-service
5
+ * and per-target RPC options (circuit breaker, retry, timeout, bulkhead,
6
+ * concurrency).
7
+ *
8
+ * Merge precedence (lowest → highest):
9
+ * 1. Framework defaults (in Interceptors.ts constructors)
10
+ * 2. Service-level `rpc` field
11
+ * 3. Target-level `rpcTargets[targetName]`
12
+ * 4. Contract-level options (static contract = { circuitBreaker: ... })
13
+ * 5. Per-method decorator options (@Expose({ retry: false }))
14
+ *
15
+ * Steps 1-3 are resolved here. Steps 4-5 are resolved in ServiceProxy.
16
+ */
17
+ const VALID_RPC_KEYS = new Set([
18
+ "circuitBreaker", "retry", "defaultTimeout", "bulkhead", "concurrency",
19
+ ]);
20
+ const VALID_CB_KEYS = new Set([
21
+ "failureThreshold", "failureRateThreshold", "minimumCallCount",
22
+ "resetTimeoutMs", "successThreshold", "windowMs", "halfOpenMaxProbes",
23
+ ]);
24
+ const VALID_RETRY_KEYS = new Set([
25
+ "maxAttempts", "baseDelayMs", "maxDelayMs",
26
+ ]);
27
+ const VALID_BULKHEAD_KEYS = new Set(["maxConcurrent"]);
28
+ const VALID_CONCURRENCY_KEYS = new Set([
29
+ "initialLimit", "minLimit", "maxLimit", "smoothing", "tolerance",
30
+ ]);
31
+ /**
32
+ * Validate an RPC options object shape.
33
+ */
34
+ export function validateRpcOptions(serviceName, label, options) {
35
+ if (options === undefined || options === null)
36
+ return;
37
+ if (typeof options !== "object" || Array.isArray(options)) {
38
+ throw new Error(`Service "${serviceName}": ${label} must be an object`);
39
+ }
40
+ const opts = options;
41
+ for (const key of Object.keys(opts)) {
42
+ if (!VALID_RPC_KEYS.has(key)) {
43
+ throw new Error(`Service "${serviceName}": unknown ${label} key "${key}". ` +
44
+ `Valid keys: ${[...VALID_RPC_KEYS].join(", ")}`);
45
+ }
46
+ }
47
+ if (opts.circuitBreaker !== undefined) {
48
+ _validateSubObject(serviceName, `${label}.circuitBreaker`, opts.circuitBreaker, VALID_CB_KEYS, "number");
49
+ }
50
+ if (opts.retry !== undefined && opts.retry !== false) {
51
+ _validateSubObject(serviceName, `${label}.retry`, opts.retry, VALID_RETRY_KEYS, "number");
52
+ }
53
+ if (opts.defaultTimeout !== undefined) {
54
+ if (typeof opts.defaultTimeout !== "number" || opts.defaultTimeout <= 0) {
55
+ throw new Error(`Service "${serviceName}": ${label}.defaultTimeout must be a positive number`);
56
+ }
57
+ }
58
+ if (opts.bulkhead !== undefined) {
59
+ _validateSubObject(serviceName, `${label}.bulkhead`, opts.bulkhead, VALID_BULKHEAD_KEYS, "number");
60
+ }
61
+ if (opts.concurrency !== undefined) {
62
+ _validateSubObject(serviceName, `${label}.concurrency`, opts.concurrency, VALID_CONCURRENCY_KEYS, "number");
63
+ }
64
+ }
65
+ function _validateSubObject(serviceName, path, obj, validKeys, valueType) {
66
+ if (typeof obj !== "object" || Array.isArray(obj) || obj === null) {
67
+ throw new Error(`Service "${serviceName}": ${path} must be an object`);
68
+ }
69
+ const record = obj;
70
+ for (const key of Object.keys(record)) {
71
+ if (!validKeys.has(key)) {
72
+ throw new Error(`Service "${serviceName}": unknown ${path} key "${key}". ` +
73
+ `Valid keys: ${[...validKeys].join(", ")}`);
74
+ }
75
+ if (typeof record[key] !== valueType) {
76
+ throw new Error(`Service "${serviceName}": ${path}.${key} must be a ${valueType}`);
77
+ }
78
+ }
79
+ }
80
+ /**
81
+ * Deep-merge two RPC options objects. Override values take precedence.
82
+ * For nested objects (circuitBreaker, retry, bulkhead, concurrency),
83
+ * individual fields merge so you only need to specify what changes.
84
+ */
85
+ export function mergeRpcOptions(base, override) {
86
+ if (!override)
87
+ return base ?? {};
88
+ if (!base)
89
+ return override;
90
+ const merged = {};
91
+ // circuitBreaker: deep merge
92
+ if (base.circuitBreaker || override.circuitBreaker) {
93
+ merged.circuitBreaker = { ...base.circuitBreaker, ...override.circuitBreaker };
94
+ }
95
+ // retry: false disables retries; object values deep-merge; undefined inherits from base
96
+ if (override.retry === false) {
97
+ merged.retry = false;
98
+ }
99
+ else if (override.retry !== undefined) {
100
+ const baseRetry = base.retry === false ? {} : (base.retry ?? {});
101
+ merged.retry = { ...baseRetry, ...override.retry };
102
+ }
103
+ else if (base.retry !== undefined) {
104
+ merged.retry = base.retry;
105
+ }
106
+ // defaultTimeout: scalar override
107
+ merged.defaultTimeout = override.defaultTimeout ?? base.defaultTimeout;
108
+ // bulkhead: deep merge
109
+ if (base.bulkhead || override.bulkhead) {
110
+ merged.bulkhead = { ...base.bulkhead, ...override.bulkhead };
111
+ }
112
+ // concurrency: deep merge
113
+ if (base.concurrency || override.concurrency) {
114
+ merged.concurrency = { ...base.concurrency, ...override.concurrency };
115
+ }
116
+ return merged;
117
+ }
118
+ /**
119
+ * Resolve the final RPC options for a specific target service.
120
+ * Merges service-level `rpc` defaults with target-specific `rpcTargets` overrides.
121
+ */
122
+ export function resolveRpcOptionsForTarget(config, targetName) {
123
+ const base = config?.rpc ?? {};
124
+ const targetOverride = config?.rpcTargets?.[targetName];
125
+ return mergeRpcOptions(base, targetOverride);
126
+ }
127
+ //# sourceMappingURL=RpcConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RpcConfig.js","sourceRoot":"","sources":["../../src/core/RpcConfig.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * SignatureCache - Rotating HMAC signature cache for internal RPC optimization
3
+ *
4
+ * P-PERF-29: Reduce per-request HMAC computation cost by caching signatures with
5
+ * periodic rotation. Maintains replay protection guarantees while reducing CPU overhead.
6
+ *
7
+ * Security Model:
8
+ * - Signatures rotate every 5 seconds (configurable)
9
+ * - Multiple signatures active during overlap window for clock drift tolerance
10
+ * - Replay protection window: 30 seconds (INTERNAL_SIG_MAX_AGE_MS)
11
+ * - Rotation interval (5s) << replay window (30s) = safe
12
+ *
13
+ * Performance:
14
+ * - Reduces HMAC computations by ~5s/rotation_interval ratio
15
+ * - At 1000 RPS: 5000 HMAC ops/rotation vs 1 per rotation = ~5000x reduction
16
+ * - Memory overhead: ~200 bytes per cached signature entry
17
+ */
18
+ /**
19
+ * Configuration for signature cache behavior
20
+ */
21
+ export interface SignatureCacheConfig {
22
+ /** How often to rotate signatures (ms). Default: 5000 (5s) */
23
+ rotationIntervalMs?: number;
24
+ /** Whether caching is enabled. Default: false (opt-in for safety) */
25
+ enabled?: boolean;
26
+ }
27
+ /**
28
+ * SignatureCache maintains a rotating set of precomputed HMAC signatures
29
+ * for internal RPC authentication. Reduces per-request CPU cost while
30
+ * preserving replay protection guarantees.
31
+ */
32
+ export declare class SignatureCache {
33
+ private readonly secret;
34
+ private readonly rotationIntervalMs;
35
+ private readonly enabled;
36
+ private currentSignatures;
37
+ private previousSignatures;
38
+ private currentTimestamp;
39
+ private previousTimestamp;
40
+ private rotationTimer;
41
+ constructor(secret: string, config?: SignatureCacheConfig);
42
+ /**
43
+ * Get a signature for the given method and path.
44
+ * If caching is disabled, computes a fresh signature each time.
45
+ * If caching is enabled, returns the current cached signature.
46
+ */
47
+ getSignature(method: string, path: string): {
48
+ signature: string;
49
+ timestamp: string;
50
+ };
51
+ /**
52
+ * Check if caching is enabled
53
+ */
54
+ isEnabled(): boolean;
55
+ /**
56
+ * Get cache statistics for monitoring
57
+ */
58
+ getStats(): {
59
+ enabled: boolean;
60
+ rotationIntervalMs: number;
61
+ currentTimestamp: number | null;
62
+ previousTimestamp: number | null;
63
+ };
64
+ /**
65
+ * Stop the rotation timer (for cleanup)
66
+ */
67
+ stop(): void;
68
+ /**
69
+ * Force an immediate rotation (useful for testing)
70
+ */
71
+ rotate(): void;
72
+ private _initializeCache;
73
+ private _startRotation;
74
+ private _rotateSignature;
75
+ private _computeFreshSignature;
76
+ }
77
+ /**
78
+ * Create a signature cache instance from environment configuration
79
+ */
80
+ export declare function createSignatureCacheFromEnv(): SignatureCache | null;
81
+ //# sourceMappingURL=SignatureCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SignatureCache.d.ts","sourceRoot":"","sources":["../../src/core/SignatureCache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAYH;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,8DAA8D;IAC9D,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,qEAAqE;IACrE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;GAIG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAGlC,OAAO,CAAC,iBAAiB,CAA0C;IACnE,OAAO,CAAC,kBAAkB,CAA0C;IAGpE,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,iBAAiB,CAAuB;IAGhD,OAAO,CAAC,aAAa,CAA+B;gBAExC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,oBAAyB;IAmC7D;;;;OAIG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IA8BpF;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,QAAQ,IAAI;QACV,OAAO,EAAE,OAAO,CAAC;QACjB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;KAClC;IASD;;OAEG;IACH,IAAI,IAAI,IAAI;IAOZ;;OAEG;IACH,MAAM,IAAI,IAAI;IAMd,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,sBAAsB;CAK/B;AAED;;GAEG;AACH,wBAAgB,2BAA2B,IAAI,cAAc,GAAG,IAAI,CAenE"}