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
|
@@ -1,413 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RequestContext
|
|
3
|
-
*
|
|
4
|
-
* Carries per-request state through the entire call chain using
|
|
5
|
-
* Node's AsyncLocalStorage. No manual threading needed — any
|
|
6
|
-
* async function called during request processing can access
|
|
7
|
-
* the current context.
|
|
8
|
-
*
|
|
9
|
-
* ┌─────────────────────────────────────────────────────────────┐
|
|
10
|
-
* │ HTTP request arrives at users service │
|
|
11
|
-
* │ │
|
|
12
|
-
* │ RequestContext created: │
|
|
13
|
-
* │ correlationId: "req_abc123" │
|
|
14
|
-
* │ traceId: "trace_xyz789" (from ForgeProxy) │
|
|
15
|
-
* │ spanId: "span_001" │
|
|
16
|
-
* │ auth: { userId, tenantId, role } │
|
|
17
|
-
* │ service: "users" │
|
|
18
|
-
* │ method: "createUser" │
|
|
19
|
-
* │ startTime: 1707500000000 │
|
|
20
|
-
* │ deadline: 1707500005000 │
|
|
21
|
-
* │ baggage: Map { custom key-value pairs } │
|
|
22
|
-
* │ │
|
|
23
|
-
* │ this.notifications.userCreated(user) │
|
|
24
|
-
* │ → Proxy reads current RequestContext │
|
|
25
|
-
* │ → Propagates correlationId + traceId + auth via IPC │
|
|
26
|
-
* │ → Notifications service creates child span │
|
|
27
|
-
* │ → Same correlationId, new spanId │
|
|
28
|
-
* │ │
|
|
29
|
-
* │ All log lines include correlationId automatically │
|
|
30
|
-
* │ All metrics include tenantId automatically │
|
|
31
|
-
* │ Prometheus histograms track per-tenant latency │
|
|
32
|
-
* └─────────────────────────────────────────────────────────────┘
|
|
33
|
-
*
|
|
34
|
-
* Usage in service code (automatic — engineers don't touch this):
|
|
35
|
-
*
|
|
36
|
-
* // The framework does this:
|
|
37
|
-
* RequestContext.run(ctx, async () => {
|
|
38
|
-
* await service.createUser(data);
|
|
39
|
-
* });
|
|
40
|
-
*
|
|
41
|
-
* // Any code inside that async tree can do:
|
|
42
|
-
* const ctx = RequestContext.current();
|
|
43
|
-
* ctx.correlationId // always available
|
|
44
|
-
* ctx.auth // if authenticated
|
|
45
|
-
* ctx.tenantId // shortcut
|
|
46
|
-
* ctx.addBaggage('paymentId', 'pay_123');
|
|
47
|
-
*/
|
|
48
|
-
|
|
49
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
50
|
-
import { randomBytes } from "node:crypto";
|
|
51
|
-
|
|
52
|
-
const _store = new AsyncLocalStorage();
|
|
53
|
-
|
|
54
|
-
let _idCounter = 0;
|
|
55
|
-
|
|
56
|
-
/** P15: Pre-generate a pool of random hex IDs to reduce per-request randomBytes calls */
|
|
57
|
-
const ID_POOL_SIZE = 256;
|
|
58
|
-
let _idPool = [];
|
|
59
|
-
let _idIndex = 0;
|
|
60
|
-
|
|
61
|
-
function _refillPool() {
|
|
62
|
-
const buf = randomBytes(16 * ID_POOL_SIZE);
|
|
63
|
-
_idPool = [];
|
|
64
|
-
for (let i = 0; i < ID_POOL_SIZE; i++) {
|
|
65
|
-
_idPool.push(buf.subarray(i * 16, (i + 1) * 16).toString('hex'));
|
|
66
|
-
}
|
|
67
|
-
_idIndex = 0;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function nextId() {
|
|
71
|
-
if (_idIndex >= _idPool.length * 0.75) {
|
|
72
|
-
_refillPool();
|
|
73
|
-
}
|
|
74
|
-
return _idPool[_idIndex++];
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
_refillPool();
|
|
78
|
-
|
|
79
|
-
/** Regex for validating trace/span/correlation IDs */
|
|
80
|
-
const ID_RE = /^[a-zA-Z0-9_:.\-]{1,128}$/;
|
|
81
|
-
|
|
82
|
-
export class RequestContext {
|
|
83
|
-
/**
|
|
84
|
-
* @param {Object} opts
|
|
85
|
-
* @param {string} [opts.correlationId] - Unique ID for this entire request chain
|
|
86
|
-
* @param {string} [opts.traceId] - Distributed trace ID (from ForgeProxy)
|
|
87
|
-
* @param {string} [opts.parentSpanId] - Parent span for distributed tracing
|
|
88
|
-
* @param {Object} [opts.auth] - { userId, tenantId, role }
|
|
89
|
-
* @param {string} [opts.service] - Current service name
|
|
90
|
-
* @param {string} [opts.method] - Method being called
|
|
91
|
-
* @param {number} [opts.deadline] - Absolute deadline (epoch ms)
|
|
92
|
-
*/
|
|
93
|
-
constructor(opts = {}) {
|
|
94
|
-
this.correlationId = opts.correlationId ?? RequestContext.generateId();
|
|
95
|
-
this.traceId = opts.traceId ?? this.correlationId;
|
|
96
|
-
this.spanId = RequestContext.generateSpanId();
|
|
97
|
-
this.parentSpanId = opts.parentSpanId ?? null;
|
|
98
|
-
|
|
99
|
-
this.auth = opts.auth ?? null;
|
|
100
|
-
this.service = opts.service ?? null;
|
|
101
|
-
this.method = opts.method ?? null;
|
|
102
|
-
|
|
103
|
-
this.startTime = performance.now();
|
|
104
|
-
this.startEpoch = Date.now();
|
|
105
|
-
this.deadline = opts.deadline ?? null;
|
|
106
|
-
|
|
107
|
-
/** @type {Map<string, string>} Arbitrary key-value pairs carried across services */
|
|
108
|
-
this.baggage = new Map(opts.baggage ?? []);
|
|
109
|
-
|
|
110
|
-
/** @type {Array<{service, method, startTime, duration, error?}>} */
|
|
111
|
-
this.spans = [];
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/** Shortcut for auth fields */
|
|
115
|
-
get userId() {
|
|
116
|
-
return this.auth?.userId ?? null;
|
|
117
|
-
}
|
|
118
|
-
get tenantId() {
|
|
119
|
-
return this.auth?.tenantId ?? null;
|
|
120
|
-
}
|
|
121
|
-
get projectId() {
|
|
122
|
-
return this.auth?.projectId ?? this.auth?.project_id ?? this._projectId ?? null;
|
|
123
|
-
}
|
|
124
|
-
get role() {
|
|
125
|
-
return this.auth?.role ?? null;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/** Elapsed time since this context was created */
|
|
129
|
-
get elapsed() {
|
|
130
|
-
return performance.now() - this.startTime;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/** Remaining time before deadline */
|
|
134
|
-
get remaining() {
|
|
135
|
-
if (!this.deadline) return Infinity;
|
|
136
|
-
return Math.max(0, this.deadline - Date.now());
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
get isExpired() {
|
|
140
|
-
return this.remaining <= 0;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/** Add custom baggage (propagated to downstream services) */
|
|
144
|
-
addBaggage(key, value) {
|
|
145
|
-
if (!/^[a-zA-Z0-9_-]+$/.test(key)) {
|
|
146
|
-
throw new Error(`Invalid baggage key: "${key}"`);
|
|
147
|
-
}
|
|
148
|
-
if (this.baggage.size >= 32 && !this.baggage.has(key)) {
|
|
149
|
-
throw new Error('Baggage limit exceeded: max 32 entries');
|
|
150
|
-
}
|
|
151
|
-
const strValue = String(value);
|
|
152
|
-
// Check total size using byte length for proper multi-byte character handling
|
|
153
|
-
let totalSize = Buffer.byteLength(key, 'utf8') + Buffer.byteLength(strValue, 'utf8');
|
|
154
|
-
for (const [k, v] of this.baggage) {
|
|
155
|
-
if (k !== key) totalSize += Buffer.byteLength(k, 'utf8') + Buffer.byteLength(v, 'utf8');
|
|
156
|
-
}
|
|
157
|
-
if (totalSize > 4096) {
|
|
158
|
-
throw new Error('Baggage size limit exceeded: max 4KB total');
|
|
159
|
-
}
|
|
160
|
-
this.baggage.set(key, strValue);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/** Record a child span (for tracing visualization) */
|
|
164
|
-
recordSpan(service, method, duration, error = null) {
|
|
165
|
-
this.spans.push({
|
|
166
|
-
service,
|
|
167
|
-
method,
|
|
168
|
-
spanId: RequestContext.generateSpanId(),
|
|
169
|
-
parentSpanId: this.spanId,
|
|
170
|
-
startTime: Date.now() - duration,
|
|
171
|
-
duration,
|
|
172
|
-
error: error?.message ?? null,
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/** Serialize for propagation over IPC/HTTP */
|
|
177
|
-
toHeaders() {
|
|
178
|
-
const headers = {
|
|
179
|
-
"x-correlation-id": this.correlationId,
|
|
180
|
-
"x-trace-id": this.traceId,
|
|
181
|
-
"x-parent-span-id": this.spanId,
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
if (this.auth) {
|
|
185
|
-
headers["x-forge-auth"] = JSON.stringify(this.auth);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
if (this.deadline) {
|
|
189
|
-
const remaining = Math.max(0, this.deadline - Date.now());
|
|
190
|
-
headers["x-forge-deadline"] = String(remaining);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (this.baggage.size > 0) {
|
|
194
|
-
headers["x-forge-baggage"] = JSON.stringify([...this.baggage]);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (this.projectId) {
|
|
198
|
-
headers["x-forge-project"] = this.projectId;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return headers;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/** Serialize for IPC (colocated + cross-process) */
|
|
205
|
-
toIPC() {
|
|
206
|
-
return {
|
|
207
|
-
__forge_correlation_id: this.correlationId,
|
|
208
|
-
__forge_trace_id: this.traceId,
|
|
209
|
-
__forge_parent_span_id: this.spanId,
|
|
210
|
-
__forge_auth: this.auth,
|
|
211
|
-
__forge_deadline: this.deadline,
|
|
212
|
-
__forge_baggage: this.baggage.size > 0 ? [...this.baggage] : null,
|
|
213
|
-
__forge_project: this.projectId,
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/** Sanitize an auth object — must be a plain object, strip dangerous keys */
|
|
218
|
-
static _sanitizeAuth(auth) {
|
|
219
|
-
if (auth == null || typeof auth !== "object" || Array.isArray(auth)) return null;
|
|
220
|
-
const clean = Object.create(null);
|
|
221
|
-
for (const key of Object.keys(auth)) {
|
|
222
|
-
// H-SEC-3: Block all proto pollution keys consistently with body parser
|
|
223
|
-
if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
|
|
224
|
-
clean[key] = auth[key];
|
|
225
|
-
}
|
|
226
|
-
return clean;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/** Validate an absolute deadline value — must be a finite positive epoch ms, capped at 5 min */
|
|
230
|
-
static _sanitizeDeadline(value) {
|
|
231
|
-
if (value == null) return null;
|
|
232
|
-
const num = Number(value);
|
|
233
|
-
if (!Number.isFinite(num) || num <= 0) return null;
|
|
234
|
-
const now = Date.now();
|
|
235
|
-
// Reject past deadlines
|
|
236
|
-
if (num < now) return null;
|
|
237
|
-
// Cap at 5 minutes from now
|
|
238
|
-
const maxDeadline = now + 300000;
|
|
239
|
-
if (num > maxDeadline) return maxDeadline;
|
|
240
|
-
return num;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/** Validate baggage — max 32 entries and max 4KB serialized size */
|
|
244
|
-
static _sanitizeBaggage(baggage) {
|
|
245
|
-
if (!Array.isArray(baggage)) return null;
|
|
246
|
-
if (baggage.length > 32) return null;
|
|
247
|
-
const serialized = JSON.stringify(baggage);
|
|
248
|
-
if (Buffer.byteLength(serialized, 'utf8') > 4096) return null;
|
|
249
|
-
return baggage;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/** Validate a relative deadline (remaining ms) — must be finite, non-negative, max 5 min */
|
|
253
|
-
static _sanitizeRelativeDeadline(value) {
|
|
254
|
-
if (value == null) return null;
|
|
255
|
-
const num = Number(value);
|
|
256
|
-
if (!Number.isFinite(num) || num < 0) return null;
|
|
257
|
-
// Cap at 5 minutes (300000ms) to prevent unreasonable propagated deadlines
|
|
258
|
-
if (num > 300000) return null;
|
|
259
|
-
return num;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/** Reconstruct from IPC message or HTTP headers */
|
|
263
|
-
static fromPropagation(data) {
|
|
264
|
-
// From IPC payload
|
|
265
|
-
if (data.__forge_correlation_id) {
|
|
266
|
-
const rawTraceId = data.__forge_trace_id || '';
|
|
267
|
-
const rawParentSpanId = data.__forge_parent_span_id || '';
|
|
268
|
-
const traceId = (rawTraceId && ID_RE.test(rawTraceId)) ? rawTraceId : null;
|
|
269
|
-
const parentSpanId = (rawParentSpanId && ID_RE.test(rawParentSpanId)) ? rawParentSpanId : null;
|
|
270
|
-
|
|
271
|
-
const ctx = new RequestContext({
|
|
272
|
-
correlationId: data.__forge_correlation_id,
|
|
273
|
-
traceId: traceId ?? undefined,
|
|
274
|
-
parentSpanId: parentSpanId ?? undefined,
|
|
275
|
-
auth: RequestContext._sanitizeAuth(data.__forge_auth),
|
|
276
|
-
deadline: RequestContext._sanitizeDeadline(data.__forge_deadline),
|
|
277
|
-
baggage: RequestContext._sanitizeBaggage(data.__forge_baggage),
|
|
278
|
-
});
|
|
279
|
-
const ipcProject = data.__forge_project;
|
|
280
|
-
if (typeof ipcProject === 'string' && /^[a-zA-Z0-9_-]{1,64}$/.test(ipcProject)) {
|
|
281
|
-
ctx._projectId = ipcProject;
|
|
282
|
-
}
|
|
283
|
-
return ctx;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// From HTTP headers (ForgeProxy → service)
|
|
287
|
-
let auth = null;
|
|
288
|
-
if (data["x-forge-auth"]) {
|
|
289
|
-
try {
|
|
290
|
-
auth = RequestContext._sanitizeAuth(JSON.parse(data["x-forge-auth"]));
|
|
291
|
-
} catch {
|
|
292
|
-
auth = null;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
let baggage = null;
|
|
297
|
-
if (data["x-forge-baggage"]) {
|
|
298
|
-
try {
|
|
299
|
-
const parsed = JSON.parse(data["x-forge-baggage"], (key, value) => {
|
|
300
|
-
if (key === '__proto__' || key === 'constructor' || key === 'prototype') return undefined;
|
|
301
|
-
return value;
|
|
302
|
-
});
|
|
303
|
-
// M7: Validate baggage size — max 32 entries and max 4KB serialized
|
|
304
|
-
if (Array.isArray(parsed)) {
|
|
305
|
-
if (parsed.length <= 32) {
|
|
306
|
-
const serialized = JSON.stringify(parsed);
|
|
307
|
-
if (Buffer.byteLength(serialized, 'utf8') <= 4096) {
|
|
308
|
-
baggage = parsed;
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
} catch {
|
|
313
|
-
baggage = null;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// x-forge-deadline is relative (remaining ms), convert to absolute local clock
|
|
318
|
-
let deadline = null;
|
|
319
|
-
const rawDeadline = data["x-forge-deadline"];
|
|
320
|
-
if (rawDeadline != null) {
|
|
321
|
-
const remaining = RequestContext._sanitizeRelativeDeadline(Number(rawDeadline));
|
|
322
|
-
if (remaining != null) {
|
|
323
|
-
deadline = Date.now() + remaining;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
const rawCorrelationId = data["x-correlation-id"] ?? data["x-request-id"];
|
|
328
|
-
const correlationId = (typeof rawCorrelationId === 'string' && /^[a-zA-Z0-9_.\-]+$/.test(rawCorrelationId) && rawCorrelationId.length <= 128) ? rawCorrelationId : null;
|
|
329
|
-
|
|
330
|
-
const rawTraceId = data["x-trace-id"] || '';
|
|
331
|
-
const rawParentSpanId = data["x-parent-span-id"] || '';
|
|
332
|
-
const traceId = (rawTraceId && ID_RE.test(rawTraceId)) ? rawTraceId : null;
|
|
333
|
-
const parentSpanId = (rawParentSpanId && ID_RE.test(rawParentSpanId)) ? rawParentSpanId : null;
|
|
334
|
-
|
|
335
|
-
const ctx = new RequestContext({
|
|
336
|
-
correlationId,
|
|
337
|
-
traceId: traceId ?? undefined,
|
|
338
|
-
parentSpanId: parentSpanId ?? undefined,
|
|
339
|
-
auth,
|
|
340
|
-
deadline,
|
|
341
|
-
baggage,
|
|
342
|
-
});
|
|
343
|
-
const rawProject = data["x-forge-project"];
|
|
344
|
-
if (typeof rawProject === 'string' && /^[a-zA-Z0-9_-]{1,64}$/.test(rawProject)) {
|
|
345
|
-
ctx._projectId = rawProject;
|
|
346
|
-
}
|
|
347
|
-
return ctx;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/** Create a child context for a downstream call */
|
|
351
|
-
child(service, method) {
|
|
352
|
-
return new RequestContext({
|
|
353
|
-
correlationId: this.correlationId,
|
|
354
|
-
traceId: this.traceId,
|
|
355
|
-
parentSpanId: this.spanId,
|
|
356
|
-
auth: this.auth,
|
|
357
|
-
deadline: this.deadline,
|
|
358
|
-
baggage: [...this.baggage],
|
|
359
|
-
service,
|
|
360
|
-
method,
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// ── Static: AsyncLocalStorage management ──
|
|
365
|
-
|
|
366
|
-
/** Run a function within a request context */
|
|
367
|
-
static run(ctx, fn) {
|
|
368
|
-
return _store.run(ctx, fn);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/** Enter a request context for the current async execution context.
|
|
372
|
-
* Unlike run(), this persists through all subsequent async work
|
|
373
|
-
* on this execution context (including post-next() middleware). */
|
|
374
|
-
static enterWith(ctx) {
|
|
375
|
-
_store.enterWith(ctx);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/** Get the current request context (or null if none) */
|
|
379
|
-
static current() {
|
|
380
|
-
return _store.getStore() ?? null;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
/** Labels for metrics (always includes tenant if available) */
|
|
384
|
-
toMetricLabels() {
|
|
385
|
-
const labels = {};
|
|
386
|
-
if (this.tenantId) labels.tenant_id = this.tenantId;
|
|
387
|
-
if (this.projectId) labels.project_id = this.projectId;
|
|
388
|
-
if (this.service) labels.service = this.service;
|
|
389
|
-
if (this.method) labels.method = this.method;
|
|
390
|
-
return labels;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/** Labels for structured logging */
|
|
394
|
-
toLogFields() {
|
|
395
|
-
const fields = { correlationId: this.correlationId };
|
|
396
|
-
if (this.tenantId) fields.tenantId = this.tenantId;
|
|
397
|
-
if (this.userId) fields.userId = this.userId;
|
|
398
|
-
if (this.traceId !== this.correlationId) fields.traceId = this.traceId;
|
|
399
|
-
return fields;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
static generateId() {
|
|
403
|
-
const workerId = typeof process !== 'undefined' ? process.env?.FORGE_WORKER_ID : undefined;
|
|
404
|
-
const safeWorkerId = workerId && /^[a-zA-Z0-9_-]+$/.test(workerId) ? workerId : '0';
|
|
405
|
-
const hex = nextId();
|
|
406
|
-
return `req_${safeWorkerId}_${(++_idCounter).toString(36)}_${hex.slice(0, 8)}`;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
static generateSpanId() {
|
|
410
|
-
const hex = nextId();
|
|
411
|
-
return hex.slice(0, 16);
|
|
412
|
-
}
|
|
413
|
-
}
|