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.
- package/README.md +52 -20
- package/bin/forge.js +2 -1058
- package/bin/host-commands.d.ts +2 -0
- package/bin/host-commands.d.ts.map +1 -0
- package/bin/host-commands.js +7 -8
- package/bin/platform-commands.d.ts +2 -0
- package/bin/platform-commands.d.ts.map +1 -0
- package/bin/platform-commands.js +118 -36
- package/dist/cli/base-command.d.ts +12 -0
- package/dist/cli/base-command.d.ts.map +1 -0
- package/dist/cli/base-command.js +25 -0
- package/dist/cli/base-command.js.map +1 -0
- package/dist/cli/commands/build.d.ts +10 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/build.js +110 -0
- package/dist/cli/commands/build.js.map +1 -0
- package/dist/cli/commands/deploy.d.ts +12 -0
- package/dist/cli/commands/deploy.d.ts.map +1 -0
- package/dist/cli/commands/deploy.js +143 -0
- package/dist/cli/commands/deploy.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +10 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/dev.js +138 -0
- package/dist/cli/commands/dev.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +10 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +76 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/commands/host.d.ts +8 -0
- package/dist/cli/commands/host.d.ts.map +1 -0
- package/dist/cli/commands/host.js +20 -0
- package/dist/cli/commands/host.js.map +1 -0
- package/dist/cli/commands/init.d.ts +16 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +246 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/platform.d.ts +8 -0
- package/dist/cli/commands/platform.d.ts.map +1 -0
- package/dist/cli/commands/platform.js +20 -0
- package/dist/cli/commands/platform.js.map +1 -0
- package/dist/cli/commands/restart.d.ts +8 -0
- package/dist/cli/commands/restart.d.ts.map +1 -0
- package/dist/cli/commands/restart.js +13 -0
- package/dist/cli/commands/restart.js.map +1 -0
- package/dist/cli/commands/scaffold/frontend.d.ts +10 -0
- package/dist/cli/commands/scaffold/frontend.d.ts.map +1 -0
- package/dist/cli/commands/scaffold/frontend.js +130 -0
- package/dist/cli/commands/scaffold/frontend.js.map +1 -0
- package/dist/cli/commands/scaffold/react.d.ts +7 -0
- package/dist/cli/commands/scaffold/react.d.ts.map +1 -0
- package/dist/cli/commands/scaffold/react.js +12 -0
- package/dist/cli/commands/scaffold/react.js.map +1 -0
- package/dist/cli/commands/scale.d.ts +8 -0
- package/dist/cli/commands/scale.d.ts.map +1 -0
- package/dist/cli/commands/scale.js +13 -0
- package/dist/cli/commands/scale.js.map +1 -0
- package/dist/cli/commands/start.d.ts +10 -0
- package/dist/cli/commands/start.d.ts.map +1 -0
- package/dist/cli/commands/start.js +71 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/status.d.ts +11 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +60 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/stop.d.ts +10 -0
- package/dist/cli/commands/stop.d.ts.map +1 -0
- package/dist/cli/commands/stop.js +89 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/util/config-discovery.d.ts +8 -0
- package/dist/cli/util/config-discovery.d.ts.map +1 -0
- package/dist/cli/util/config-discovery.js +70 -0
- package/dist/cli/util/config-discovery.js.map +1 -0
- package/dist/cli/util/config-patcher.d.ts +17 -0
- package/dist/cli/util/config-patcher.d.ts.map +1 -0
- package/dist/cli/util/config-patcher.js +439 -0
- package/dist/cli/util/config-patcher.js.map +1 -0
- package/dist/cli/util/frontend-dev.d.ts +8 -0
- package/dist/cli/util/frontend-dev.d.ts.map +1 -0
- package/dist/cli/util/frontend-dev.js +117 -0
- package/dist/cli/util/frontend-dev.js.map +1 -0
- package/dist/cli/util/process.d.ts +5 -0
- package/dist/cli/util/process.d.ts.map +1 -0
- package/dist/cli/util/process.js +17 -0
- package/dist/cli/util/process.js.map +1 -0
- package/dist/cli/util/templates.d.ts +10 -0
- package/dist/cli/util/templates.d.ts.map +1 -0
- package/dist/cli/util/templates.js +157 -0
- package/dist/cli/util/templates.js.map +1 -0
- package/dist/core/AlertSink.d.ts +83 -0
- package/dist/core/AlertSink.d.ts.map +1 -0
- package/dist/core/AlertSink.js +126 -0
- package/dist/core/AlertSink.js.map +1 -0
- package/dist/core/DirectMessageBus.d.ts +88 -0
- package/dist/core/DirectMessageBus.d.ts.map +1 -0
- package/dist/core/DirectMessageBus.js +352 -0
- package/dist/core/DirectMessageBus.js.map +1 -0
- package/dist/core/EndpointResolver.d.ts +111 -0
- package/dist/core/EndpointResolver.d.ts.map +1 -0
- package/dist/core/EndpointResolver.js +336 -0
- package/dist/core/EndpointResolver.js.map +1 -0
- package/dist/core/ForgeContext.d.ts +221 -0
- package/dist/core/ForgeContext.d.ts.map +1 -0
- package/dist/core/ForgeContext.js +1169 -0
- package/dist/core/ForgeContext.js.map +1 -0
- package/dist/core/ForgeEndpoints.d.ts +71 -0
- package/dist/core/ForgeEndpoints.d.ts.map +1 -0
- package/dist/core/ForgeEndpoints.js +442 -0
- package/dist/core/ForgeEndpoints.js.map +1 -0
- package/dist/core/ForgeHost.d.ts +82 -0
- package/dist/core/ForgeHost.d.ts.map +1 -0
- package/dist/core/ForgeHost.js +107 -0
- package/dist/core/ForgeHost.js.map +1 -0
- package/dist/core/ForgePlatform.d.ts +96 -0
- package/dist/core/ForgePlatform.d.ts.map +1 -0
- package/dist/core/ForgePlatform.js +136 -0
- package/dist/core/ForgePlatform.js.map +1 -0
- package/dist/core/ForgeWebSocket.d.ts +56 -0
- package/dist/core/ForgeWebSocket.d.ts.map +1 -0
- package/dist/core/ForgeWebSocket.js +415 -0
- package/dist/core/ForgeWebSocket.js.map +1 -0
- package/dist/core/Ingress.d.ts +329 -0
- package/dist/core/Ingress.d.ts.map +1 -0
- package/dist/core/Ingress.js +694 -0
- package/dist/core/Ingress.js.map +1 -0
- package/dist/core/Interceptors.d.ts +134 -0
- package/dist/core/Interceptors.d.ts.map +1 -0
- package/dist/core/Interceptors.js +416 -0
- package/dist/core/Interceptors.js.map +1 -0
- package/dist/core/Logger.d.ts +20 -0
- package/dist/core/Logger.d.ts.map +1 -0
- package/dist/core/Logger.js +77 -0
- package/dist/core/Logger.js.map +1 -0
- package/dist/core/MessageBus.d.ts +15 -0
- package/dist/core/MessageBus.d.ts.map +1 -0
- package/dist/core/MessageBus.js +18 -0
- package/dist/core/MessageBus.js.map +1 -0
- package/dist/core/Prometheus.d.ts +80 -0
- package/dist/core/Prometheus.d.ts.map +1 -0
- package/dist/core/Prometheus.js +332 -0
- package/dist/core/Prometheus.js.map +1 -0
- package/dist/core/RequestContext.d.ts +214 -0
- package/dist/core/RequestContext.d.ts.map +1 -0
- package/dist/core/RequestContext.js +556 -0
- package/dist/core/RequestContext.js.map +1 -0
- package/dist/core/Router.d.ts +45 -0
- package/dist/core/Router.d.ts.map +1 -0
- package/dist/core/Router.js +285 -0
- package/dist/core/Router.js.map +1 -0
- package/dist/core/RoutingStrategy.d.ts +116 -0
- package/dist/core/RoutingStrategy.d.ts.map +1 -0
- package/dist/core/RoutingStrategy.js +306 -0
- package/dist/core/RoutingStrategy.js.map +1 -0
- package/dist/core/RpcConfig.d.ts +72 -0
- package/dist/core/RpcConfig.d.ts.map +1 -0
- package/dist/core/RpcConfig.js +127 -0
- package/dist/core/RpcConfig.js.map +1 -0
- package/dist/core/SignatureCache.d.ts +81 -0
- package/dist/core/SignatureCache.d.ts.map +1 -0
- package/dist/core/SignatureCache.js +172 -0
- package/dist/core/SignatureCache.js.map +1 -0
- package/dist/core/StaticFileServer.d.ts +34 -0
- package/dist/core/StaticFileServer.d.ts.map +1 -0
- package/dist/core/StaticFileServer.js +497 -0
- package/dist/core/StaticFileServer.js.map +1 -0
- package/dist/core/Supervisor.d.ts +198 -0
- package/dist/core/Supervisor.d.ts.map +1 -0
- package/dist/core/Supervisor.js +1418 -0
- package/dist/core/Supervisor.js.map +1 -0
- package/dist/core/ThreadAllocator.d.ts +52 -0
- package/dist/core/ThreadAllocator.d.ts.map +1 -0
- package/dist/core/ThreadAllocator.js +174 -0
- package/dist/core/ThreadAllocator.js.map +1 -0
- package/dist/core/WorkerChannelManager.d.ts +130 -0
- package/dist/core/WorkerChannelManager.d.ts.map +1 -0
- package/dist/core/WorkerChannelManager.js +956 -0
- package/dist/core/WorkerChannelManager.js.map +1 -0
- package/dist/core/config-enums.d.ts +41 -0
- package/dist/core/config-enums.d.ts.map +1 -0
- package/dist/core/config-enums.js +59 -0
- package/dist/core/config-enums.js.map +1 -0
- package/dist/core/config.d.ts +159 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +694 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/host-config.d.ts +146 -0
- package/dist/core/host-config.d.ts.map +1 -0
- package/dist/core/host-config.js +312 -0
- package/dist/core/host-config.js.map +1 -0
- package/dist/core/ipc-errors.d.ts +27 -0
- package/dist/core/ipc-errors.d.ts.map +1 -0
- package/dist/core/ipc-errors.js +36 -0
- package/dist/core/ipc-errors.js.map +1 -0
- package/dist/core/network-utils.d.ts +35 -0
- package/dist/core/network-utils.d.ts.map +1 -0
- package/dist/core/network-utils.js +145 -0
- package/dist/core/network-utils.js.map +1 -0
- package/dist/core/platform-config.d.ts +142 -0
- package/dist/core/platform-config.d.ts.map +1 -0
- package/dist/core/platform-config.js +299 -0
- package/dist/core/platform-config.js.map +1 -0
- package/dist/decorators/ServiceProxy.d.ts +175 -0
- package/dist/decorators/ServiceProxy.d.ts.map +1 -0
- package/dist/decorators/ServiceProxy.js +969 -0
- package/dist/decorators/ServiceProxy.js.map +1 -0
- package/dist/decorators/index.d.ts +146 -0
- package/dist/decorators/index.d.ts.map +1 -0
- package/dist/decorators/index.js +545 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/deploy/NginxGenerator.d.ts +165 -0
- package/dist/deploy/NginxGenerator.d.ts.map +1 -0
- package/dist/deploy/NginxGenerator.js +781 -0
- package/dist/deploy/NginxGenerator.js.map +1 -0
- package/dist/deploy/PlatformManifestGenerator.d.ts +43 -0
- package/dist/deploy/PlatformManifestGenerator.d.ts.map +1 -0
- package/dist/deploy/PlatformManifestGenerator.js +80 -0
- package/dist/deploy/PlatformManifestGenerator.js.map +1 -0
- package/dist/deploy/RouteManifestGenerator.d.ts +42 -0
- package/dist/deploy/RouteManifestGenerator.d.ts.map +1 -0
- package/dist/deploy/RouteManifestGenerator.js +105 -0
- package/dist/deploy/RouteManifestGenerator.js.map +1 -0
- package/dist/deploy/index.d.ts +210 -0
- package/dist/deploy/index.d.ts.map +1 -0
- package/dist/deploy/index.js +918 -0
- package/dist/deploy/index.js.map +1 -0
- package/dist/frontend/FrontendDevLifecycle.d.ts +26 -0
- package/dist/frontend/FrontendDevLifecycle.d.ts.map +1 -0
- package/dist/frontend/FrontendDevLifecycle.js +60 -0
- package/dist/frontend/FrontendDevLifecycle.js.map +1 -0
- package/dist/frontend/FrontendPluginOrchestrator.d.ts +64 -0
- package/dist/frontend/FrontendPluginOrchestrator.d.ts.map +1 -0
- package/dist/frontend/FrontendPluginOrchestrator.js +167 -0
- package/dist/frontend/FrontendPluginOrchestrator.js.map +1 -0
- package/dist/frontend/SiteResolver.d.ts +33 -0
- package/dist/frontend/SiteResolver.d.ts.map +1 -0
- package/dist/frontend/SiteResolver.js +53 -0
- package/dist/frontend/SiteResolver.js.map +1 -0
- package/dist/frontend/StaticMountRegistry.d.ts +36 -0
- package/dist/frontend/StaticMountRegistry.d.ts.map +1 -0
- package/dist/frontend/StaticMountRegistry.js +94 -0
- package/dist/frontend/StaticMountRegistry.js.map +1 -0
- package/dist/frontend/index.d.ts +7 -0
- package/dist/frontend/index.d.ts.map +1 -0
- package/{src → dist}/frontend/index.js +4 -2
- package/dist/frontend/index.js.map +1 -0
- package/dist/frontend/pathUtils.d.ts +8 -0
- package/dist/frontend/pathUtils.d.ts.map +1 -0
- package/dist/frontend/pathUtils.js +17 -0
- package/dist/frontend/pathUtils.js.map +1 -0
- package/dist/frontend/plugins/index.d.ts +2 -0
- package/dist/frontend/plugins/index.d.ts.map +1 -0
- package/{src → dist}/frontend/plugins/index.js +1 -1
- package/dist/frontend/plugins/index.js.map +1 -0
- package/dist/frontend/plugins/viteFrontend.d.ts +51 -0
- package/dist/frontend/plugins/viteFrontend.d.ts.map +1 -0
- package/dist/frontend/plugins/viteFrontend.js +134 -0
- package/dist/frontend/plugins/viteFrontend.js.map +1 -0
- package/dist/frontend/types.d.ts +25 -0
- package/dist/frontend/types.d.ts.map +1 -0
- package/dist/frontend/types.js +2 -0
- package/dist/frontend/types.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/internals.d.ts +21 -0
- package/dist/internals.d.ts.map +1 -0
- package/{src → dist}/internals.js +12 -14
- package/dist/internals.js.map +1 -0
- package/dist/plugins/PluginManager.d.ts +209 -0
- package/dist/plugins/PluginManager.d.ts.map +1 -0
- package/dist/plugins/PluginManager.js +365 -0
- package/dist/plugins/PluginManager.js.map +1 -0
- package/dist/plugins/ScopedPostgres.d.ts +78 -0
- package/dist/plugins/ScopedPostgres.d.ts.map +1 -0
- package/dist/plugins/ScopedPostgres.js +190 -0
- package/dist/plugins/ScopedPostgres.js.map +1 -0
- package/dist/plugins/ScopedRedis.d.ts +88 -0
- package/dist/plugins/ScopedRedis.d.ts.map +1 -0
- package/dist/plugins/ScopedRedis.js +169 -0
- package/dist/plugins/ScopedRedis.js.map +1 -0
- package/dist/plugins/index.d.ts +289 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +1942 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/types.d.ts +59 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +2 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/registry/ServiceRegistry.d.ts +305 -0
- package/dist/registry/ServiceRegistry.d.ts.map +1 -0
- package/dist/registry/ServiceRegistry.js +735 -0
- package/dist/registry/ServiceRegistry.js.map +1 -0
- package/dist/scaling/ScaleAdvisor.d.ts +214 -0
- package/dist/scaling/ScaleAdvisor.d.ts.map +1 -0
- package/dist/scaling/ScaleAdvisor.js +526 -0
- package/dist/scaling/ScaleAdvisor.js.map +1 -0
- package/dist/services/Service.d.ts +164 -0
- package/dist/services/Service.d.ts.map +1 -0
- package/dist/services/Service.js +106 -0
- package/dist/services/Service.js.map +1 -0
- package/dist/services/worker-bootstrap.d.ts +15 -0
- package/dist/services/worker-bootstrap.d.ts.map +1 -0
- package/dist/services/worker-bootstrap.js +744 -0
- package/dist/services/worker-bootstrap.js.map +1 -0
- package/dist/templates/auth-service.d.ts +42 -0
- package/dist/templates/auth-service.d.ts.map +1 -0
- package/dist/templates/auth-service.js +54 -0
- package/dist/templates/auth-service.js.map +1 -0
- package/dist/templates/identity-service.d.ts +50 -0
- package/dist/templates/identity-service.d.ts.map +1 -0
- package/dist/templates/identity-service.js +62 -0
- package/dist/templates/identity-service.js.map +1 -0
- package/dist/types/contract.d.ts +120 -0
- package/dist/types/contract.d.ts.map +1 -0
- package/dist/types/contract.js +69 -0
- package/dist/types/contract.js.map +1 -0
- package/package.json +78 -20
- package/src/core/DirectMessageBus.js +0 -364
- package/src/core/EndpointResolver.js +0 -259
- package/src/core/ForgeContext.js +0 -2236
- package/src/core/ForgeHost.js +0 -122
- package/src/core/ForgePlatform.js +0 -145
- package/src/core/Ingress.js +0 -768
- package/src/core/Interceptors.js +0 -420
- package/src/core/MessageBus.js +0 -321
- package/src/core/Prometheus.js +0 -305
- package/src/core/RequestContext.js +0 -413
- package/src/core/RoutingStrategy.js +0 -330
- package/src/core/Supervisor.js +0 -1349
- package/src/core/ThreadAllocator.js +0 -196
- package/src/core/WorkerChannelManager.js +0 -879
- package/src/core/config.js +0 -637
- package/src/core/host-config.js +0 -311
- package/src/core/network-utils.js +0 -166
- package/src/core/platform-config.js +0 -308
- package/src/decorators/ServiceProxy.js +0 -904
- package/src/decorators/index.js +0 -571
- package/src/deploy/NginxGenerator.js +0 -865
- package/src/deploy/PlatformManifestGenerator.js +0 -96
- package/src/deploy/RouteManifestGenerator.js +0 -112
- package/src/deploy/index.js +0 -984
- package/src/frontend/FrontendDevLifecycle.js +0 -65
- package/src/frontend/FrontendPluginOrchestrator.js +0 -187
- package/src/frontend/SiteResolver.js +0 -63
- package/src/frontend/StaticMountRegistry.js +0 -90
- package/src/frontend/plugins/viteFrontend.js +0 -79
- package/src/frontend/types.js +0 -35
- package/src/index.js +0 -58
- package/src/plugins/PluginManager.js +0 -537
- package/src/plugins/ScopedPostgres.js +0 -192
- package/src/plugins/ScopedRedis.js +0 -142
- package/src/plugins/index.js +0 -1756
- package/src/registry/ServiceRegistry.js +0 -797
- package/src/scaling/ScaleAdvisor.js +0 -442
- package/src/services/Service.js +0 -195
- package/src/services/worker-bootstrap.js +0 -679
- package/src/templates/auth-service.js +0 -65
- package/src/templates/identity-service.js +0 -75
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
const ROUTE_CACHE_MAX = 10_000;
|
|
2
|
+
/**
|
|
3
|
+
* Simple pattern-matching HTTP router.
|
|
4
|
+
* P11: Includes a route matching cache.
|
|
5
|
+
*/
|
|
6
|
+
export class Router {
|
|
7
|
+
routes;
|
|
8
|
+
middleware;
|
|
9
|
+
_cache;
|
|
10
|
+
constructor() {
|
|
11
|
+
this.routes = new Map();
|
|
12
|
+
this.middleware = [];
|
|
13
|
+
// P11: Route cache — URL→handler lookups
|
|
14
|
+
this._cache = new Map();
|
|
15
|
+
}
|
|
16
|
+
use(fn) {
|
|
17
|
+
this.middleware.push(fn);
|
|
18
|
+
}
|
|
19
|
+
get(pattern, handler) {
|
|
20
|
+
this._add("GET", pattern, handler);
|
|
21
|
+
}
|
|
22
|
+
post(pattern, handler) {
|
|
23
|
+
this._add("POST", pattern, handler);
|
|
24
|
+
}
|
|
25
|
+
put(pattern, handler) {
|
|
26
|
+
this._add("PUT", pattern, handler);
|
|
27
|
+
}
|
|
28
|
+
patch(pattern, handler) {
|
|
29
|
+
this._add("PATCH", pattern, handler);
|
|
30
|
+
}
|
|
31
|
+
delete(pattern, handler) {
|
|
32
|
+
this._add("DELETE", pattern, handler);
|
|
33
|
+
}
|
|
34
|
+
_add(method, pattern, handler) {
|
|
35
|
+
const paramNames = [];
|
|
36
|
+
const isWildcard = pattern.endsWith("/*");
|
|
37
|
+
// Count literal (non-param, non-wildcard) segments for specificity ranking
|
|
38
|
+
const segments = pattern.replace(/\/\*$/, "").split("/").filter(Boolean);
|
|
39
|
+
const literalCount = segments.filter((s) => !s.startsWith(":")).length;
|
|
40
|
+
const totalSegments = segments.length;
|
|
41
|
+
const base = isWildcard ? pattern.slice(0, -2) : pattern;
|
|
42
|
+
// Escape regex-special characters before replacing :param placeholders
|
|
43
|
+
const escaped = base.replace(/([.*+?^${}()|[\]\\])/g, "\\$1");
|
|
44
|
+
const regexStr = escaped.replace(/:(\w+)/g, (_, name) => {
|
|
45
|
+
paramNames.push(name);
|
|
46
|
+
return "([^/]+)";
|
|
47
|
+
});
|
|
48
|
+
if (paramNames.length > 10) {
|
|
49
|
+
throw new Error(`Route "${pattern}" has ${paramNames.length} params (max 10)`);
|
|
50
|
+
}
|
|
51
|
+
let regex;
|
|
52
|
+
if (isWildcard) {
|
|
53
|
+
regex = new RegExp(`^${regexStr}(?:/(.*))?$`);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
regex = new RegExp(`^${regexStr}$`);
|
|
57
|
+
}
|
|
58
|
+
const route = {
|
|
59
|
+
method,
|
|
60
|
+
pattern,
|
|
61
|
+
regex,
|
|
62
|
+
paramNames,
|
|
63
|
+
handler,
|
|
64
|
+
isWildcard,
|
|
65
|
+
literalCount,
|
|
66
|
+
totalSegments,
|
|
67
|
+
};
|
|
68
|
+
if (!this.routes.has(method))
|
|
69
|
+
this.routes.set(method, []);
|
|
70
|
+
this.routes.get(method).push(route);
|
|
71
|
+
// Invalidate route cache when routes change
|
|
72
|
+
this._cache.clear();
|
|
73
|
+
}
|
|
74
|
+
_parseQuery(searchParams) {
|
|
75
|
+
const result = {};
|
|
76
|
+
for (const key of searchParams.keys()) {
|
|
77
|
+
if (key in result)
|
|
78
|
+
continue; // already processed via getAll
|
|
79
|
+
const values = searchParams.getAll(key);
|
|
80
|
+
result[key] = values.length > 1 ? values : values[0];
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
_autoOptions(normalized, query) {
|
|
85
|
+
const methods = new Set();
|
|
86
|
+
for (const [method, bucket] of this.routes) {
|
|
87
|
+
for (const route of bucket) {
|
|
88
|
+
if (normalized.match(route.regex)) {
|
|
89
|
+
methods.add(method);
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (methods.size === 0)
|
|
95
|
+
return null;
|
|
96
|
+
methods.add("OPTIONS");
|
|
97
|
+
if (methods.has("GET"))
|
|
98
|
+
methods.add("HEAD");
|
|
99
|
+
const allow = [...methods].sort().join(", ");
|
|
100
|
+
return {
|
|
101
|
+
handler: (_req, res) => {
|
|
102
|
+
res.writeHead(204, { Allow: allow });
|
|
103
|
+
res.end();
|
|
104
|
+
},
|
|
105
|
+
params: {},
|
|
106
|
+
query,
|
|
107
|
+
pattern: "OPTIONS(auto)",
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
match(method, url) {
|
|
111
|
+
// H-SEC-1: Cache by method:pathname only (not full URL with query string).
|
|
112
|
+
// This prevents cache poisoning via unique query parameters while still
|
|
113
|
+
// providing the performance benefit of caching route matching.
|
|
114
|
+
// The query string is still passed to _matchUncached for parsing.
|
|
115
|
+
const qIdx = url.indexOf("?");
|
|
116
|
+
const pathname = qIdx === -1 ? url : url.slice(0, qIdx);
|
|
117
|
+
const cacheKey = `${method}:${pathname}`;
|
|
118
|
+
const cached = this._cache.get(cacheKey);
|
|
119
|
+
if (cached !== undefined) {
|
|
120
|
+
// LRU: Move to end of Map so eviction removes least-recently-used entries
|
|
121
|
+
this._cache.delete(cacheKey);
|
|
122
|
+
this._cache.set(cacheKey, cached);
|
|
123
|
+
// Re-parse query from original url for the cached result
|
|
124
|
+
if (cached) {
|
|
125
|
+
let query = {};
|
|
126
|
+
if (qIdx !== -1) {
|
|
127
|
+
try {
|
|
128
|
+
query = this._parseQuery(new URL(url, "http://localhost").searchParams);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return null; // Malformed URL — caller should return 400
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return { ...cached, query };
|
|
135
|
+
}
|
|
136
|
+
return cached;
|
|
137
|
+
}
|
|
138
|
+
const result = this._matchUncached(method, url);
|
|
139
|
+
// P11: Store in cache (evict 20% when full). Don't cache null (unmatched)
|
|
140
|
+
// to prevent attackers filling the cache with misses.
|
|
141
|
+
if (result !== null) {
|
|
142
|
+
if (this._cache.size >= ROUTE_CACHE_MAX) {
|
|
143
|
+
const deleteCount = Math.ceil(ROUTE_CACHE_MAX * 0.2);
|
|
144
|
+
const iter = this._cache.keys();
|
|
145
|
+
for (let i = 0; i < deleteCount; i++) {
|
|
146
|
+
const key = iter.next().value;
|
|
147
|
+
if (key !== undefined)
|
|
148
|
+
this._cache.delete(key);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
this._cache.set(cacheKey, result);
|
|
152
|
+
}
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
_matchUncached(method, url) {
|
|
156
|
+
// Reject null bytes — path traversal / request smuggling vector
|
|
157
|
+
if (url.includes("\0"))
|
|
158
|
+
return null;
|
|
159
|
+
let parsed;
|
|
160
|
+
try {
|
|
161
|
+
parsed = new URL(url, "http://localhost");
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return null; // Malformed URL — caller should return 400
|
|
165
|
+
}
|
|
166
|
+
// P-3: Reuse the already-parsed pathname instead of constructing a second URL.
|
|
167
|
+
// URL constructor already normalizes encoded traversals (e.g. ..%2F).
|
|
168
|
+
let normalized = parsed.pathname;
|
|
169
|
+
// Strip trailing slashes (except root "/")
|
|
170
|
+
if (normalized.length > 1 && normalized.endsWith("/")) {
|
|
171
|
+
normalized = normalized.slice(0, -1);
|
|
172
|
+
}
|
|
173
|
+
const query = this._parseQuery(parsed.searchParams);
|
|
174
|
+
// HEAD requests match GET routes (RFC 7231 § 4.3.2)
|
|
175
|
+
const isHead = method === "HEAD";
|
|
176
|
+
const effectiveMethod = isHead ? "GET" : method;
|
|
177
|
+
// Collect route buckets to search — only the relevant method(s)
|
|
178
|
+
const buckets = [];
|
|
179
|
+
if (this.routes.has(effectiveMethod))
|
|
180
|
+
buckets.push(this.routes.get(effectiveMethod));
|
|
181
|
+
if (isHead && this.routes.has("HEAD"))
|
|
182
|
+
buckets.push(this.routes.get("HEAD"));
|
|
183
|
+
// Priority tiers: exact > parameterized > wildcard
|
|
184
|
+
let bestParam = null;
|
|
185
|
+
let bestParamScore = -1;
|
|
186
|
+
let bestWildcard = null;
|
|
187
|
+
let bestWildcardScore = -1;
|
|
188
|
+
for (const bucket of buckets) {
|
|
189
|
+
for (const route of bucket) {
|
|
190
|
+
const match = normalized.match(route.regex);
|
|
191
|
+
if (!match)
|
|
192
|
+
continue;
|
|
193
|
+
const params = {};
|
|
194
|
+
let paramTraversal = false;
|
|
195
|
+
let decodeError = false;
|
|
196
|
+
route.paramNames.forEach((name, i) => {
|
|
197
|
+
let val;
|
|
198
|
+
try {
|
|
199
|
+
val = decodeURIComponent(match[i + 1]);
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
decodeError = true;
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
// Detect double-encoding by attempting further decodes (up to 2 iterations)
|
|
206
|
+
for (let d = 0; d < 2; d++) {
|
|
207
|
+
try {
|
|
208
|
+
const decoded = decodeURIComponent(val);
|
|
209
|
+
if (decoded === val)
|
|
210
|
+
break;
|
|
211
|
+
val = decoded;
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (val.includes("..") || val.includes("\0") || val.includes("%00"))
|
|
218
|
+
paramTraversal = true;
|
|
219
|
+
params[name] = val;
|
|
220
|
+
});
|
|
221
|
+
if (decodeError)
|
|
222
|
+
return null;
|
|
223
|
+
if (paramTraversal)
|
|
224
|
+
continue;
|
|
225
|
+
// Wildcard routes — lowest priority tier
|
|
226
|
+
if (route.isWildcard) {
|
|
227
|
+
const wildcardIndex = route.paramNames.length + 1;
|
|
228
|
+
params["*"] = match[wildcardIndex] ?? "";
|
|
229
|
+
params.wildcard = params["*"];
|
|
230
|
+
const score = route.literalCount * 1000 + route.totalSegments;
|
|
231
|
+
if (score > bestWildcardScore) {
|
|
232
|
+
bestWildcardScore = score;
|
|
233
|
+
bestWildcard = { handler: route.handler, params, query, pattern: route.pattern };
|
|
234
|
+
}
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
// Exact match (no params) — highest priority, return immediately
|
|
238
|
+
if (route.paramNames.length === 0) {
|
|
239
|
+
return { handler: route.handler, params, query, pattern: route.pattern };
|
|
240
|
+
}
|
|
241
|
+
// Parameterized route — pick the most specific
|
|
242
|
+
const score = route.literalCount * 1000 + route.totalSegments;
|
|
243
|
+
if (score > bestParamScore) {
|
|
244
|
+
bestParamScore = score;
|
|
245
|
+
bestParam = { handler: route.handler, params, query, pattern: route.pattern };
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (bestParam)
|
|
250
|
+
return bestParam;
|
|
251
|
+
if (bestWildcard)
|
|
252
|
+
return bestWildcard;
|
|
253
|
+
// Auto OPTIONS if no explicit handler matched
|
|
254
|
+
if (method === "OPTIONS") {
|
|
255
|
+
return this._autoOptions(normalized, query);
|
|
256
|
+
}
|
|
257
|
+
// 405 Method Not Allowed — path matches but method doesn't
|
|
258
|
+
const allowedMethods = new Set();
|
|
259
|
+
for (const [routeMethod, bucket] of this.routes) {
|
|
260
|
+
for (const route of bucket) {
|
|
261
|
+
if (normalized.match(route.regex)) {
|
|
262
|
+
allowedMethods.add(routeMethod);
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (allowedMethods.size > 0) {
|
|
268
|
+
allowedMethods.add("OPTIONS");
|
|
269
|
+
if (allowedMethods.has("GET"))
|
|
270
|
+
allowedMethods.add("HEAD");
|
|
271
|
+
const allow = [...allowedMethods].sort().join(", ");
|
|
272
|
+
return {
|
|
273
|
+
handler: (_req, res) => {
|
|
274
|
+
res.writeHead(405, { "Content-Type": "application/json", Allow: allow });
|
|
275
|
+
res.end(JSON.stringify({ error: "Method Not Allowed" }));
|
|
276
|
+
},
|
|
277
|
+
params: {},
|
|
278
|
+
query,
|
|
279
|
+
pattern: "405(auto)",
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
//# sourceMappingURL=Router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Router.js","sourceRoot":"","sources":["../../src/core/Router.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
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
|
+
interface WorkerEntry {
|
|
31
|
+
key: string;
|
|
32
|
+
socket: unknown;
|
|
33
|
+
pending: number;
|
|
34
|
+
}
|
|
35
|
+
interface CallContext {
|
|
36
|
+
__forge_args?: unknown[];
|
|
37
|
+
__forge_hash?: string | number;
|
|
38
|
+
[key: string]: unknown;
|
|
39
|
+
}
|
|
40
|
+
interface HashStrategyOptions {
|
|
41
|
+
key?: string;
|
|
42
|
+
vnodes?: number;
|
|
43
|
+
salt?: string;
|
|
44
|
+
}
|
|
45
|
+
interface RoutingStrategy {
|
|
46
|
+
pick(workers: WorkerEntry[], callContext?: CallContext | null): WorkerEntry | WorkerEntry[] | null;
|
|
47
|
+
readonly name: string;
|
|
48
|
+
}
|
|
49
|
+
export declare class RoundRobinStrategy implements RoutingStrategy {
|
|
50
|
+
index: number;
|
|
51
|
+
constructor();
|
|
52
|
+
pick(workers: WorkerEntry[], _callContext?: CallContext | null): WorkerEntry | null;
|
|
53
|
+
get name(): string;
|
|
54
|
+
}
|
|
55
|
+
export declare class HashStrategy implements RoutingStrategy {
|
|
56
|
+
keyField: string | null;
|
|
57
|
+
vnodes: number;
|
|
58
|
+
private _ring;
|
|
59
|
+
private _workerCacheKey;
|
|
60
|
+
private _ringHashes;
|
|
61
|
+
private _ringKeys;
|
|
62
|
+
private _fallbackCounter;
|
|
63
|
+
private _salt;
|
|
64
|
+
constructor(options?: HashStrategyOptions);
|
|
65
|
+
pick(workers: WorkerEntry[], callContext?: CallContext | null): WorkerEntry | null;
|
|
66
|
+
private _extractKey;
|
|
67
|
+
private _buildRing;
|
|
68
|
+
private _findOnRing;
|
|
69
|
+
/**
|
|
70
|
+
* Pre-compute a keyed SHA-256 hash for ring building (called at build time).
|
|
71
|
+
*/
|
|
72
|
+
private _computeHash;
|
|
73
|
+
/**
|
|
74
|
+
* P-1: Fast FNV-1a hash for runtime routing decisions.
|
|
75
|
+
* Salted to prevent predictable distribution from external input.
|
|
76
|
+
*/
|
|
77
|
+
private _simpleHash;
|
|
78
|
+
get name(): string;
|
|
79
|
+
}
|
|
80
|
+
export declare class LeastPendingStrategy implements RoutingStrategy {
|
|
81
|
+
private _pending;
|
|
82
|
+
private _lastWorkerKeys;
|
|
83
|
+
constructor();
|
|
84
|
+
/**
|
|
85
|
+
* Pick the worker with the fewest in-flight requests.
|
|
86
|
+
*/
|
|
87
|
+
pick(workers: WorkerEntry[], _callContext?: CallContext | null): WorkerEntry | null;
|
|
88
|
+
/** Call when dispatching a request to a worker */
|
|
89
|
+
acquire(workerKey: string): void;
|
|
90
|
+
/** Call when a request to a worker completes */
|
|
91
|
+
release(workerKey: string): void;
|
|
92
|
+
get name(): string;
|
|
93
|
+
}
|
|
94
|
+
export declare class BroadcastStrategy implements RoutingStrategy {
|
|
95
|
+
/**
|
|
96
|
+
* Returns ALL workers. The caller is responsible for sending
|
|
97
|
+
* to all of them and handling multiple responses.
|
|
98
|
+
*/
|
|
99
|
+
pick(workers: WorkerEntry[], _callContext?: CallContext | null): WorkerEntry[];
|
|
100
|
+
get name(): string;
|
|
101
|
+
get isBroadcast(): boolean;
|
|
102
|
+
}
|
|
103
|
+
export declare class PrimaryStrategy implements RoutingStrategy {
|
|
104
|
+
/**
|
|
105
|
+
* Always routes to worker index 0. If worker 0 is down,
|
|
106
|
+
* routes to the next available.
|
|
107
|
+
*/
|
|
108
|
+
pick(workers: WorkerEntry[], _callContext?: CallContext | null): WorkerEntry | null;
|
|
109
|
+
get name(): string;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Create a routing strategy by name.
|
|
113
|
+
*/
|
|
114
|
+
export declare function createStrategy(name: string, options?: HashStrategyOptions): RoutingStrategy;
|
|
115
|
+
export {};
|
|
116
|
+
//# sourceMappingURL=RoutingStrategy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RoutingStrategy.d.ts","sourceRoot":"","sources":["../../src/core/RoutingStrategy.ts"],"names":[],"mappings":"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"}
|