threadforge 0.1.1 → 0.2.2

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 +69 -42
  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 +79 -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
@@ -1,330 +0,0 @@
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
-
31
- import { createHmac, randomBytes } from "node:crypto";
32
-
33
- const HASH_SALT = randomBytes(16).toString("hex");
34
-
35
- // P-1: Fast non-cryptographic FNV-1a hash for runtime routing.
36
- // HMAC-SHA256 is ~100x slower and unnecessary for load distribution.
37
- function fnv1a(str) {
38
- let hash = 0x811c9dc5 | 0; // FNV offset basis
39
- for (let i = 0; i < str.length; i++) {
40
- hash ^= str.charCodeAt(i);
41
- hash = (hash * 0x01000193) | 0; // FNV prime
42
- }
43
- return hash >>> 0; // Convert to unsigned 32-bit
44
- }
45
-
46
- /**
47
- * @typedef {Object} WorkerEntry
48
- * @property {string} key - "serviceName:workerId"
49
- * @property {Object} socket - UDS socket or local reference
50
- * @property {number} pending - Number of in-flight requests
51
- */
52
-
53
- // ─── Round-Robin ────────────────────────────────────────────
54
-
55
- export class RoundRobinStrategy {
56
- constructor() {
57
- this.index = 0;
58
- }
59
-
60
- /**
61
- * @param {WorkerEntry[]} workers
62
- * @param {Object} _callContext - Ignored by round-robin
63
- * @returns {WorkerEntry}
64
- */
65
- pick(workers, _callContext) {
66
- if (workers.length === 0) return null;
67
- const worker = workers[this.index % workers.length];
68
- this.index = (this.index + 1) % 1_000_000_000;
69
- return worker;
70
- }
71
-
72
- get name() {
73
- return "round-robin";
74
- }
75
- }
76
-
77
- // ─── Hash-Based (Consistent Hashing) ────────────────────────
78
-
79
- export class HashStrategy {
80
- /**
81
- * @param {Object} options
82
- * @param {string} options.key - The field name to hash on (e.g., 'userId')
83
- * If the call payload has this field, it's used.
84
- * Otherwise falls back to round-robin.
85
- * @param {number} [options.vnodes=150] - Virtual nodes per worker for
86
- * consistent hash ring.
87
- */
88
- constructor(options = {}) {
89
- this.keyField = options.key ?? null;
90
- this.vnodes = options.vnodes ?? 150;
91
- this._ring = null;
92
- this._workerCacheKey = '';
93
- }
94
-
95
- pick(workers, callContext) {
96
- if (workers.length === 0) return null;
97
- if (workers.length === 1) return workers[0];
98
-
99
- // Extract the hash key from the call context
100
- const hashKey = this._extractKey(callContext);
101
- if (!hashKey) {
102
- // No key available — fall back to round-robin-ish
103
- return workers[Math.abs(this._simpleHash(Date.now().toString())) % workers.length];
104
- }
105
-
106
- // Rebuild ring when worker identities change (not just count).
107
- // This ensures the ring is correct when workers restart with new keys.
108
- const currentCacheKey = workers.map(w => w.key).sort().join(',');
109
- if (this._workerCacheKey !== currentCacheKey) {
110
- this._buildRing(workers);
111
- }
112
-
113
- // Find the worker on the ring
114
- const hash = this._simpleHash(hashKey);
115
- return this._findOnRing(hash, workers);
116
- }
117
-
118
- _extractKey(callContext) {
119
- if (!callContext) return null;
120
-
121
- // If a key field is specified, look for it in the call args
122
- if (this.keyField) {
123
- const args = callContext.__forge_args ?? [];
124
- // Check first arg (usually the main parameter)
125
- if (args.length > 0) {
126
- if (typeof args[0] === "string" || typeof args[0] === "number") {
127
- return String(args[0]);
128
- }
129
- if (typeof args[0] === "object" && args[0]?.[this.keyField]) {
130
- return String(args[0][this.keyField]);
131
- }
132
- }
133
- }
134
-
135
- // Try to use __forge_hash if explicitly set by the caller
136
- if (callContext.__forge_hash) {
137
- return String(callContext.__forge_hash);
138
- }
139
-
140
- return null;
141
- }
142
-
143
- _buildRing(workers) {
144
- // P5: Pre-compute all virtual node hashes and store sorted for binary search
145
- const totalNodes = workers.length * this.vnodes;
146
- const hashes = new Uint32Array(totalNodes);
147
- const indices = new Uint16Array(totalNodes);
148
- let pos = 0;
149
- for (let i = 0; i < workers.length; i++) {
150
- for (let v = 0; v < this.vnodes; v++) {
151
- hashes[pos] = this._computeHash(`${workers[i].key}:${v}`);
152
- indices[pos] = i;
153
- pos++;
154
- }
155
- }
156
- // Sort by hash value, keeping indices aligned
157
- const sortOrder = Array.from({ length: totalNodes }, (_, i) => i);
158
- sortOrder.sort((a, b) => hashes[a] - hashes[b]);
159
- this._ringHashes = new Uint32Array(totalNodes);
160
- this._ringIndices = new Uint16Array(totalNodes);
161
- for (let i = 0; i < totalNodes; i++) {
162
- this._ringHashes[i] = hashes[sortOrder[i]];
163
- this._ringIndices[i] = indices[sortOrder[i]];
164
- }
165
- this._ring = true; // sentinel
166
- this._workerCacheKey = workers.map(w => w.key).sort().join(',');
167
- }
168
-
169
- _findOnRing(hash, workers) {
170
- const hashes = this._ringHashes;
171
- // Binary search for the first ring entry >= hash
172
- let lo = 0,
173
- hi = hashes.length;
174
- while (lo < hi) {
175
- const mid = (lo + hi) >>> 1;
176
- if (hashes[mid] < hash) lo = mid + 1;
177
- else hi = mid;
178
- }
179
- // Wrap around
180
- const idx = lo < hashes.length ? lo : 0;
181
- return workers[this._ringIndices[idx]];
182
- }
183
-
184
- /**
185
- * Pre-compute a keyed SHA-256 hash for ring building (called at build time).
186
- */
187
- _computeHash(str) {
188
- return createHmac("sha256", HASH_SALT).update(str).digest().readUInt32BE(0);
189
- }
190
-
191
- /**
192
- * P-1: Fast FNV-1a hash for runtime routing decisions.
193
- * Salted to prevent predictable distribution from external input.
194
- */
195
- _simpleHash(str) {
196
- return fnv1a(HASH_SALT + str);
197
- }
198
-
199
- get name() {
200
- return `hash(${this.keyField ?? "auto"})`;
201
- }
202
- }
203
-
204
- // ─── Least-Pending ──────────────────────────────────────────
205
-
206
- export class LeastPendingStrategy {
207
- constructor() {
208
- /** @type {Map<string, number>} key → pending count */
209
- this._pending = new Map();
210
- this._lastWorkerCount = 0;
211
- }
212
-
213
- /**
214
- * Pick the worker with the fewest in-flight requests.
215
- */
216
- pick(workers, _callContext) {
217
- if (workers.length === 0) return null;
218
- if (workers.length === 1) return workers[0];
219
-
220
- // P-9: Only prune when worker set changes, not on every pick()
221
- if (this._pending.size > 0 && workers.length !== this._lastWorkerCount) {
222
- const activeKeys = new Set(workers.map(w => w.key));
223
- for (const key of this._pending.keys()) {
224
- if (!activeKeys.has(key)) {
225
- this._pending.delete(key);
226
- }
227
- }
228
- }
229
- this._lastWorkerCount = workers.length;
230
-
231
- let best = workers[0];
232
- let bestPending = this._pending.get(best.key) ?? 0;
233
- for (let i = 1; i < workers.length; i++) {
234
- const p = this._pending.get(workers[i].key) ?? 0;
235
- if (p < bestPending) {
236
- best = workers[i];
237
- bestPending = p;
238
- }
239
- }
240
- return best;
241
- }
242
-
243
- /** Call when dispatching a request to a worker */
244
- acquire(workerKey) {
245
- this._pending.set(workerKey, (this._pending.get(workerKey) ?? 0) + 1);
246
- }
247
-
248
- /** Call when a request to a worker completes */
249
- release(workerKey) {
250
- const count = this._pending.get(workerKey) ?? 0;
251
- if (count <= 0) {
252
- if (process.env.NODE_ENV !== 'production') {
253
- console.warn('[LeastPendingStrategy] Spurious release()');
254
- }
255
- this._pending.delete(workerKey);
256
- return;
257
- }
258
- if (count <= 1) {
259
- this._pending.delete(workerKey);
260
- } else {
261
- this._pending.set(workerKey, count - 1);
262
- }
263
- }
264
-
265
- get name() {
266
- return "least-pending";
267
- }
268
- }
269
-
270
- // ─── Broadcast ──────────────────────────────────────────────
271
-
272
- export class BroadcastStrategy {
273
- /**
274
- * Returns ALL workers. The caller is responsible for sending
275
- * to all of them and handling multiple responses.
276
- */
277
- pick(workers, _callContext) {
278
- return workers; // Return the full array — caller detects this
279
- }
280
-
281
- get name() {
282
- return "broadcast";
283
- }
284
-
285
- get isBroadcast() {
286
- return true;
287
- }
288
- }
289
-
290
- // ─── Primary ────────────────────────────────────────────────
291
-
292
- export class PrimaryStrategy {
293
- /**
294
- * Always routes to worker index 0. If worker 0 is down,
295
- * routes to the next available.
296
- */
297
- pick(workers, _callContext) {
298
- if (workers.length === 0) return null;
299
- return workers[0]; // Primary is always first
300
- }
301
-
302
- get name() {
303
- return "primary";
304
- }
305
- }
306
-
307
- // ─── Strategy Factory ───────────────────────────────────────
308
-
309
- const STRATEGIES = {
310
- "round-robin": (_opts) => new RoundRobinStrategy(),
311
- hash: (opts) => new HashStrategy(opts),
312
- "least-pending": (_opts) => new LeastPendingStrategy(),
313
- broadcast: (_opts) => new BroadcastStrategy(),
314
- primary: (_opts) => new PrimaryStrategy(),
315
- };
316
-
317
- /**
318
- * Create a routing strategy by name.
319
- *
320
- * @param {string} name - Strategy name
321
- * @param {Object} [options] - Strategy-specific options
322
- * @returns {Object} Strategy instance with a pick() method
323
- */
324
- export function createStrategy(name, options = {}) {
325
- const factory = STRATEGIES[name];
326
- if (!factory) {
327
- throw new Error(`Unknown routing strategy: "${name}". Available: ${Object.keys(STRATEGIES).join(", ")}`);
328
- }
329
- return factory(options);
330
- }