bazis 0.96.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (513) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +146 -0
  3. package/cli/build.ts +147 -0
  4. package/cli/codegen.ts +40 -0
  5. package/cli/generateModule.ts +187 -0
  6. package/cli/generateProject.ts +183 -0
  7. package/cli/main.ts +125 -0
  8. package/cli/moduleRegistration.ts +127 -0
  9. package/cli/naming.ts +124 -0
  10. package/cli/parseCli.ts +151 -0
  11. package/cli/templates/module.ts +497 -0
  12. package/cli/templates/pack.ts +76 -0
  13. package/cli/templates/passport.ts +178 -0
  14. package/core/agent/AgentContextBuilder.ts +567 -0
  15. package/core/agent/AgentDataDefinition.ts +8 -0
  16. package/core/agent/AgentRegistry.ts +539 -0
  17. package/core/agent/AgentRuntime.ts +47 -0
  18. package/core/agent/AgentToolExecutor.ts +2130 -0
  19. package/core/agent/AgentToolHooks.ts +39 -0
  20. package/core/agent/ModelProvider.ts +21 -0
  21. package/core/agent/README.md +229 -0
  22. package/core/agent/Tool.contract.ts +20 -0
  23. package/core/agent/decorators/Agent.ts +11 -0
  24. package/core/agent/decorators/Prompt.ts +11 -0
  25. package/core/agent/decorators/Task.ts +29 -0
  26. package/core/agent/decorators/Tool.ts +12 -0
  27. package/core/agent/errors.ts +43 -0
  28. package/core/agent/generated/catalog.ts +26 -0
  29. package/core/agent/index.ts +170 -0
  30. package/core/agent/internal/AgentExecutionDriver.ts +1497 -0
  31. package/core/agent/internal/AgentExecutionState.ts +775 -0
  32. package/core/agent/internal/AgentJsonSchema.ts +150 -0
  33. package/core/agent/internal/AgentModelBinding.ts +57 -0
  34. package/core/agent/internal/Schema.contract.ts +36 -0
  35. package/core/agent/internal/ToolAudit.projector.ts +163 -0
  36. package/core/agent/internal/ToolContract.validator.ts +102 -0
  37. package/core/agent/metadata.ts +398 -0
  38. package/core/agent/moduleContributions-v1.ts +113 -0
  39. package/core/agent/semantic.ts +903 -0
  40. package/core/agent/session/codec.ts +33 -0
  41. package/core/agent/session/contracts.ts +96 -0
  42. package/core/agent/session/entities.ts +84 -0
  43. package/core/agent/session/protector.ts +92 -0
  44. package/core/app/index.ts +5 -0
  45. package/core/app/runApp.ts +293 -0
  46. package/core/app/ui/index.ts +29 -0
  47. package/core/app/ui/uiProfileResolver.ts +966 -0
  48. package/core/app/ui/uiSurfaceHosting.ts +38 -0
  49. package/core/app/ui/uiSurfaceHttp.ts +476 -0
  50. package/core/background/BackgroundService.ts +223 -0
  51. package/core/background/backgroundModule.ts +42 -0
  52. package/core/background/decorator.ts +43 -0
  53. package/core/background/delay.ts +27 -0
  54. package/core/background/index.ts +15 -0
  55. package/core/cache/COMPATIBILITY.md +13 -0
  56. package/core/cache/ICache.ts +71 -0
  57. package/core/cache/MemoryCache.ts +394 -0
  58. package/core/cache/README.md +83 -0
  59. package/core/cache/SPEC.md +1355 -0
  60. package/core/cache/cacheModule.ts +21 -0
  61. package/core/cache/decorators/Cacheable.ts +38 -0
  62. package/core/cache/decorators/CacheableRedis.ts +39 -0
  63. package/core/cache/decorators/OutputCache.ts +33 -0
  64. package/core/cache/decorators/OutputRedisCache.ts +33 -0
  65. package/core/cache/decorators/cacheableMetadata.ts +78 -0
  66. package/core/cache/decorators/cacheableRedisMetadata.ts +81 -0
  67. package/core/cache/decorators/outputCacheMetadata.ts +71 -0
  68. package/core/cache/decorators/outputRedisCacheMetadata.ts +77 -0
  69. package/core/cache/di/classProviderHook.ts +26 -0
  70. package/core/cache/distributed/CacheCodec.ts +5 -0
  71. package/core/cache/distributed/DistributedCache.ts +403 -0
  72. package/core/cache/distributed/DistributedCacheDriver.ts +83 -0
  73. package/core/cache/distributed/DistributedCacheStores.ts +28 -0
  74. package/core/cache/distributed/IDistributedCache.ts +40 -0
  75. package/core/cache/distributed/NamedCacheRegistry.ts +23 -0
  76. package/core/cache/distributed/codecs.ts +32 -0
  77. package/core/cache/distributed/index.ts +19 -0
  78. package/core/cache/errors/CacheError.ts +16 -0
  79. package/core/cache/http/CachedHttpPayload.ts +213 -0
  80. package/core/cache/http/applyClientCacheHeaders.ts +61 -0
  81. package/core/cache/http/buildOutputCacheKey.ts +154 -0
  82. package/core/cache/http/buildRedisOutputCacheKey.ts +16 -0
  83. package/core/cache/http/composeOutputCache.ts +130 -0
  84. package/core/cache/http/outputCacheMiddleware.ts +111 -0
  85. package/core/cache/http/outputCacheSecurityWarning.ts +58 -0
  86. package/core/cache/http/outputRedisCacheMiddleware.ts +118 -0
  87. package/core/cache/index.ts +97 -0
  88. package/core/cache/internal/AutomaticCacheKey.ts +106 -0
  89. package/core/cache/internal/CacheKeyGuard.ts +56 -0
  90. package/core/cache/internal/buildCacheProviders.ts +146 -0
  91. package/core/cache/internal/normalizeCacheKey.ts +9 -0
  92. package/core/cache/internal/resolveCachePolicy.ts +32 -0
  93. package/core/cache/internal/resolveCacheableRedisPolicy.ts +17 -0
  94. package/core/cache/internal/resolveOutputRedisCachePolicy.ts +17 -0
  95. package/core/cache/providers/cachedProviders.ts +68 -0
  96. package/core/cache/services/cacheProxy.ts +243 -0
  97. package/core/cache/tokens/CACHE_POLICIES.ts +5 -0
  98. package/core/cache/tokens/DISTRIBUTED_CACHE.ts +30 -0
  99. package/core/cache/types/CacheEntry.ts +7 -0
  100. package/core/cache/types/CacheOptions.ts +60 -0
  101. package/core/cache/types/CachePolicy.ts +74 -0
  102. package/core/cache/types/CacheableRedisOptions.ts +23 -0
  103. package/core/cache/types/OutputRedisCacheOptions.ts +25 -0
  104. package/core/di/README.md +104 -0
  105. package/core/di/ServiceCollection.ts +185 -0
  106. package/core/di/ServiceProvider.ts +689 -0
  107. package/core/di/ServiceScope.ts +61 -0
  108. package/core/di/container.ts +38 -0
  109. package/core/di/errors/AmbiguousNamedDependencyError.ts +10 -0
  110. package/core/di/errors/AsyncResolutionRequiredError.ts +12 -0
  111. package/core/di/errors/CircularDependencyError.ts +10 -0
  112. package/core/di/errors/ClassDependenciesMismatchError.ts +15 -0
  113. package/core/di/errors/DiError.ts +6 -0
  114. package/core/di/errors/InvalidProviderError.ts +9 -0
  115. package/core/di/errors/ModuleEncapsulationError.ts +7 -0
  116. package/core/di/errors/ModuleOwnedProviderConflictError.ts +8 -0
  117. package/core/di/errors/NamedDependencyNotFoundError.ts +7 -0
  118. package/core/di/errors/OptionsValidationError.ts +7 -0
  119. package/core/di/errors/ProviderNotFoundError.ts +10 -0
  120. package/core/di/errors/ScopeDisposedError.ts +7 -0
  121. package/core/di/errors/ScopedServiceFromRootError.ts +9 -0
  122. package/core/di/errors/ServiceValidationError.ts +7 -0
  123. package/core/di/errors/index.ts +14 -0
  124. package/core/di/extensions/activator.ts +39 -0
  125. package/core/di/extensions/application.ts +117 -0
  126. package/core/di/extensions/hosted-service.ts +124 -0
  127. package/core/di/extensions/index.ts +23 -0
  128. package/core/di/extensions/options-reloadable.ts +137 -0
  129. package/core/di/extensions/options.ts +91 -0
  130. package/core/di/extensions/service-provider-token.ts +21 -0
  131. package/core/di/index.ts +97 -0
  132. package/core/di/internal/GraphValidator.ts +119 -0
  133. package/core/di/internal/HostedServicePlan.ts +158 -0
  134. package/core/di/internal/NamedTokenIndex.ts +55 -0
  135. package/core/di/internal/OpenGenericRegistration.ts +14 -0
  136. package/core/di/internal/ResolutionPlan.ts +18 -0
  137. package/core/di/internal/ResolutionScopeState.ts +19 -0
  138. package/core/di/internal/ResolutionTracker.ts +153 -0
  139. package/core/di/internal/ScopeLifecycle.ts +159 -0
  140. package/core/di/internal/ServiceRegistration.ts +14 -0
  141. package/core/di/internal/ServiceRegistry.ts +252 -0
  142. package/core/di/internal/classDeps.ts +38 -0
  143. package/core/di/internal/disposal.ts +58 -0
  144. package/core/di/internal/providerDeps.ts +26 -0
  145. package/core/di/module/DI.ts +146 -0
  146. package/core/di/module/Global.ts +25 -0
  147. package/core/di/module/Module.ts +44 -0
  148. package/core/di/module/ModuleRegistrar.ts +69 -0
  149. package/core/di/module/autoDeps.ts +39 -0
  150. package/core/di/module/classProviderHooks.ts +66 -0
  151. package/core/di/module/collectModuleConfigs.ts +57 -0
  152. package/core/di/module/collectModuleControllers.ts +53 -0
  153. package/core/di/module/collectModuleUiProfiles.ts +53 -0
  154. package/core/di/module/createContainer.ts +261 -0
  155. package/core/di/module/encapsulation.ts +416 -0
  156. package/core/di/module/encapsulationHooks.ts +36 -0
  157. package/core/di/module/generatedProviderAttachments.ts +102 -0
  158. package/core/di/module/index.ts +41 -0
  159. package/core/di/module/moduleExtensions.ts +35 -0
  160. package/core/di/module/moduleNameBinding.ts +136 -0
  161. package/core/di/module/moduleOwnedProviderContributors.ts +409 -0
  162. package/core/di/module/providerSelection.ts +90 -0
  163. package/core/di/module/shortcuts.ts +111 -0
  164. package/core/di/module/types/BazisModule.ts +71 -0
  165. package/core/di/module/types/DiRegistrar.ts +24 -0
  166. package/core/di/module/types/index.ts +2 -0
  167. package/core/di/provider/index.ts +26 -0
  168. package/core/di/provider/providerGuards.ts +17 -0
  169. package/core/di/provider/types/AsyncFactoryProvider.ts +15 -0
  170. package/core/di/provider/types/ClassProvider.ts +18 -0
  171. package/core/di/provider/types/FactoryProvider.ts +20 -0
  172. package/core/di/provider/types/KeyedDependency.ts +20 -0
  173. package/core/di/provider/types/LazyDependency.ts +31 -0
  174. package/core/di/provider/types/NamedDependency.ts +16 -0
  175. package/core/di/provider/types/Provider.ts +10 -0
  176. package/core/di/provider/types/ProviderDefinition.ts +11 -0
  177. package/core/di/provider/types/ProviderDependencyList.ts +11 -0
  178. package/core/di/provider/types/ProviderLifetime.ts +1 -0
  179. package/core/di/provider/types/ProviderOwnership.ts +7 -0
  180. package/core/di/provider/types/ResolvedDeps.ts +18 -0
  181. package/core/di/provider/types/ValueProvider.ts +6 -0
  182. package/core/di/provider/types/index.ts +13 -0
  183. package/core/di/token.ts +84 -0
  184. package/core/di/types/BuildServiceProviderOptions.ts +4 -0
  185. package/core/di/types/ServiceKey.ts +1 -0
  186. package/core/di/types/ServiceResolver.ts +16 -0
  187. package/core/di/types/index.ts +3 -0
  188. package/core/generatedFingerprint.ts +60 -0
  189. package/core/generatedRuntime.ts +187 -0
  190. package/core/grpc/Grpc.module.ts +23 -0
  191. package/core/grpc/GrpcBinding.contract.ts +35 -0
  192. package/core/grpc/GrpcCall.ts +73 -0
  193. package/core/grpc/GrpcClient.ts +172 -0
  194. package/core/grpc/GrpcClientCall.ts +267 -0
  195. package/core/grpc/GrpcError.ts +19 -0
  196. package/core/grpc/GrpcRequest.binding.ts +45 -0
  197. package/core/grpc/GrpcServer.ts +204 -0
  198. package/core/grpc/GrpcStatus.ts +8 -0
  199. package/core/grpc/GrpcTransport.ts +174 -0
  200. package/core/grpc/Metadata.ts +73 -0
  201. package/core/grpc/ProtoCodec.ts +225 -0
  202. package/core/grpc/ProtoParser.ts +192 -0
  203. package/core/grpc/ProtoReader.ts +34 -0
  204. package/core/grpc/ProtoSchema.ts +52 -0
  205. package/core/grpc/ServerCredentials.ts +30 -0
  206. package/core/grpc/clientContracts.ts +39 -0
  207. package/core/grpc/contracts.ts +45 -0
  208. package/core/grpc/decorators.ts +82 -0
  209. package/core/grpc/grpcClientProvider.ts +9 -0
  210. package/core/grpc/index.ts +17 -0
  211. package/core/grpc/moduleControllers.ts +38 -0
  212. package/core/grpc/protobuf.ts +80 -0
  213. package/core/grpc/serviceDefinition.ts +20 -0
  214. package/core/http/Authorization/Authorize.ts +52 -0
  215. package/core/http/Authorization/authorizeComposer.ts +31 -0
  216. package/core/http/Authorization/metadata.ts +158 -0
  217. package/core/http/Binding/ParameterBinder.ts +109 -0
  218. package/core/http/Binding/RequestModel.ts +27 -0
  219. package/core/http/Binding/autoBindings.ts +125 -0
  220. package/core/http/Binding/bindings.ts +19 -0
  221. package/core/http/Binding/convert.ts +48 -0
  222. package/core/http/Binding/generatedSpec.ts +18 -0
  223. package/core/http/Binding/listModelRegistry.ts +47 -0
  224. package/core/http/Binding/modelBinder.ts +345 -0
  225. package/core/http/Binding/modelValidator.ts +30 -0
  226. package/core/http/Binding/requestModelRegistry.ts +117 -0
  227. package/core/http/Decorators/attributes.ts +92 -0
  228. package/core/http/Decorators/controller.ts +24 -0
  229. package/core/http/Decorators/metadata.ts +107 -0
  230. package/core/http/Decorators/routes.ts +131 -0
  231. package/core/http/Errors/HttpError.ts +92 -0
  232. package/core/http/HttpContext/HttpContext.ts +148 -0
  233. package/core/http/HttpContext/ResponseBuilder.ts +37 -0
  234. package/core/http/HttpContext/inspectableRedirects.ts +47 -0
  235. package/core/http/HttpContext/principal.ts +24 -0
  236. package/core/http/HttpContext/responseLifetime.ts +45 -0
  237. package/core/http/HttpServer.ts +627 -0
  238. package/core/http/Middleware/accessLog.ts +90 -0
  239. package/core/http/Middleware/cors.ts +160 -0
  240. package/core/http/Middleware/errorHandler.ts +100 -0
  241. package/core/http/Middleware/pipeline.ts +21 -0
  242. package/core/http/Middleware/rateLimit.ts +104 -0
  243. package/core/http/Middleware/securityHeaders.ts +118 -0
  244. package/core/http/Middleware/types.ts +19 -0
  245. package/core/http/OpenApi/generatedOpenApiRegistry.ts +102 -0
  246. package/core/http/OpenApi/openApiDocs.ts +75 -0
  247. package/core/http/OpenApi/openApiDocument.ts +356 -0
  248. package/core/http/README.md +139 -0
  249. package/core/http/Results/HttpResult.ts +19 -0
  250. package/core/http/Results/normalize.ts +93 -0
  251. package/core/http/Results/results.ts +70 -0
  252. package/core/http/Routing/Router.ts +201 -0
  253. package/core/http/Routing/RouterBuilder.ts +184 -0
  254. package/core/http/Routing/template.ts +112 -0
  255. package/core/http/SPEC.md +981 -0
  256. package/core/http/Versioning/VersionReader.ts +20 -0
  257. package/core/http/WebSocketUpgrade.ts +29 -0
  258. package/core/http/composeRouteMiddlewareComposers.ts +21 -0
  259. package/core/http/correlation/createCorrelationIdMiddleware.ts +79 -0
  260. package/core/http/httpModule.ts +48 -0
  261. package/core/http/index.ts +140 -0
  262. package/core/http/middlewareTokens.ts +48 -0
  263. package/core/http/options.ts +134 -0
  264. package/core/http-client/index.ts +105 -0
  265. package/core/infra/Infra.ts +158 -0
  266. package/core/infra/InfraConnector.ts +86 -0
  267. package/core/infra/InfraLifecycle.ts +86 -0
  268. package/core/infra/cache/RedisDistributedCacheBackend.ts +88 -0
  269. package/core/infra/cache/RedisDistributedCacheDriver.ts +186 -0
  270. package/core/infra/cache/index.ts +7 -0
  271. package/core/infra/connectorConfig.ts +29 -0
  272. package/core/infra/connectorIdentity.ts +39 -0
  273. package/core/infra/connectors/codex/CodexAppServer.ts +146 -0
  274. package/core/infra/connectors/codex/CodexAppServerClient.ts +258 -0
  275. package/core/infra/connectors/codex/CodexPolicy.ts +114 -0
  276. package/core/infra/connectors/codex/CodexTurn.ts +133 -0
  277. package/core/infra/connectors/codex/contracts.ts +82 -0
  278. package/core/infra/connectors/codex.ts +26 -0
  279. package/core/infra/connectors/llm.ts +78 -0
  280. package/core/infra/connectors/llmRouter.ts +443 -0
  281. package/core/infra/connectors/openaiCompatible.ts +590 -0
  282. package/core/infra/connectors/openaiTextStream.ts +134 -0
  283. package/core/infra/connectors/opensearch.ts +307 -0
  284. package/core/infra/connectors/postgres.ts +105 -0
  285. package/core/infra/connectors/redis.ts +132 -0
  286. package/core/infra/index.ts +58 -0
  287. package/core/kernel/ApplicationLifetime.ts +91 -0
  288. package/core/kernel/Bazis.ts +73 -0
  289. package/core/kernel/Environment.ts +53 -0
  290. package/core/kernel/Kernel.ts +391 -0
  291. package/core/kernel/KernelBuilder.ts +172 -0
  292. package/core/kernel/LifecycleCoordinator.ts +204 -0
  293. package/core/kernel/README.md +66 -0
  294. package/core/kernel/SupervisedHostedService.ts +147 -0
  295. package/core/kernel/config/ConfigRegistry.ts +48 -0
  296. package/core/kernel/config/Configuration.ts +110 -0
  297. package/core/kernel/config/README.md +62 -0
  298. package/core/kernel/config/Secret.ts +29 -0
  299. package/core/kernel/config/addConfigOptions.ts +63 -0
  300. package/core/kernel/config/defineConfig.ts +211 -0
  301. package/core/kernel/config/sources.ts +123 -0
  302. package/core/kernel/correlation/requestContext.ts +57 -0
  303. package/core/kernel/errors/ConfigKeyMissingError.ts +7 -0
  304. package/core/kernel/errors/KernelError.ts +7 -0
  305. package/core/kernel/errors/ShutdownTimeoutError.ts +7 -0
  306. package/core/kernel/errors/StartupAbortedError.ts +7 -0
  307. package/core/kernel/errors/StartupTimeoutError.ts +7 -0
  308. package/core/kernel/errors/index.ts +5 -0
  309. package/core/kernel/events/EventBus.ts +140 -0
  310. package/core/kernel/events/EventToken.ts +17 -0
  311. package/core/kernel/events/eventSubscribers.ts +126 -0
  312. package/core/kernel/events/kernelEvents.ts +16 -0
  313. package/core/kernel/events/onEvent.ts +32 -0
  314. package/core/kernel/health/HealthCheckContracts.ts +42 -0
  315. package/core/kernel/health/HealthService.ts +93 -0
  316. package/core/kernel/index.ts +96 -0
  317. package/core/kernel/internal/awaitAbortable.ts +18 -0
  318. package/core/kernel/internal/awaitShutdown.ts +21 -0
  319. package/core/kernel/internal/reportDiagnosticFailure.ts +8 -0
  320. package/core/kernel/internal/validateTimeout.ts +8 -0
  321. package/core/kernel/lifecycleHooks.ts +18 -0
  322. package/core/kernel/logging/ConsoleLogger.ts +68 -0
  323. package/core/kernel/logging/Logger.ts +22 -0
  324. package/core/kernel/types/ConfigSource.ts +9 -0
  325. package/core/kernel/types/EnvironmentName.ts +1 -0
  326. package/core/kernel/types/KernelOptions.ts +23 -0
  327. package/core/kernel/types/LifecycleHook.ts +18 -0
  328. package/core/kernel/types/index.ts +4 -0
  329. package/core/orm/DATABASE_PROVIDER.ts +5 -0
  330. package/core/orm/OrmHostedPlan.validator.ts +55 -0
  331. package/core/orm/OrmLifecycle.ts +146 -0
  332. package/core/orm/OrmOwnedStoreLifecycle.ts +96 -0
  333. package/core/orm/buildOrmProviders.ts +152 -0
  334. package/core/orm/databaseConnector.ts +108 -0
  335. package/core/orm/encapsulationHook.ts +19 -0
  336. package/core/orm/index.ts +95 -0
  337. package/core/orm/listQuery.ts +198 -0
  338. package/core/orm/ormModule.ts +173 -0
  339. package/core/orm/ownedStoreContributions.ts +195 -0
  340. package/core/orm/registerRepositories.ts +27 -0
  341. package/core/orm/repository.ts +45 -0
  342. package/core/orm/strictSchemaHostedIdentity.ts +17 -0
  343. package/core/scripts/agent-codegen.ts +1166 -0
  344. package/core/scripts/di-generate-target.ts +90 -0
  345. package/core/scripts/di-generate.ts +1837 -0
  346. package/core/scripts/orm-predicate-codegen.ts +33 -0
  347. package/core/scripts/request-model-codegen.ts +326 -0
  348. package/core/websocket/DESIGN.md +297 -0
  349. package/core/websocket/WsRateLimiter.ts +101 -0
  350. package/core/websocket/adapter/adapter.interface.ts +45 -0
  351. package/core/websocket/adapter/in-memory.adapter.ts +190 -0
  352. package/core/websocket/adapter/redis-delivery-scripts.ts +135 -0
  353. package/core/websocket/adapter/redis-delivery-store.ts +86 -0
  354. package/core/websocket/adapter/redis-operations.ts +33 -0
  355. package/core/websocket/adapter/redis.adapter.ts +218 -0
  356. package/core/websocket/codec/binary.codec.ts +87 -0
  357. package/core/websocket/codec/json.codec.ts +34 -0
  358. package/core/websocket/codec/packet-codec.interface.ts +17 -0
  359. package/core/websocket/decorators.ts +165 -0
  360. package/core/websocket/explorer.ts +126 -0
  361. package/core/websocket/index.ts +78 -0
  362. package/core/websocket/middleware.ts +146 -0
  363. package/core/websocket/packet-codec.ts +75 -0
  364. package/core/websocket/reliable-delivery.ts +56 -0
  365. package/core/websocket/session-creation.ts +16 -0
  366. package/core/websocket/session-manager.ts +856 -0
  367. package/core/websocket/socket-wrapper.ts +314 -0
  368. package/core/websocket/topic-cache.ts +59 -0
  369. package/core/websocket/types.ts +122 -0
  370. package/core/websocket/websocketModule.ts +96 -0
  371. package/core/websocket/ws-dispatch.ts +123 -0
  372. package/core/websocket/ws-server.ts +1707 -0
  373. package/index.ts +281 -0
  374. package/library/boundary/diagnostics-v1.ts +136 -0
  375. package/library/boundary/index.ts +73 -0
  376. package/library/boundary/json/decodeBoundedJsonV1.ts +518 -0
  377. package/library/boundary/json/limits-v1.ts +46 -0
  378. package/library/boundary/json/normalizeBoundedJsonV1.ts +256 -0
  379. package/library/boundary/json/types-v1.ts +11 -0
  380. package/library/boundary/schema/boundarySchemaHashV1.ts +6 -0
  381. package/library/boundary/schema/collectBoundaryValueDiagnosticsV1.ts +325 -0
  382. package/library/boundary/schema/decodeBoundarySchemaV1.ts +646 -0
  383. package/library/boundary/schema/index.ts +8 -0
  384. package/library/boundary/schema/types-v1.ts +99 -0
  385. package/library/boundary/schema/validateBoundaryValueV1.ts +58 -0
  386. package/library/boundary/serialization/canonicalJsonV1.ts +66 -0
  387. package/library/http-client/HttpClient.ts +969 -0
  388. package/library/http-client/HttpClientFactory.ts +45 -0
  389. package/library/http-client/errors.ts +68 -0
  390. package/library/http-client/index.ts +43 -0
  391. package/library/http-client/interceptors.ts +40 -0
  392. package/library/http-client/progress.ts +62 -0
  393. package/library/http-client/redirectProtocol.ts +6 -0
  394. package/library/http-client/serialize.ts +133 -0
  395. package/library/http-client/types.ts +174 -0
  396. package/library/jsonapi/ListQueryError.ts +25 -0
  397. package/library/jsonapi/ListRequest.ts +37 -0
  398. package/library/jsonapi/decorators.ts +161 -0
  399. package/library/jsonapi/document.ts +124 -0
  400. package/library/jsonapi/index.ts +40 -0
  401. package/library/jsonapi/parseListQuery.ts +291 -0
  402. package/library/jsonapi/types.ts +125 -0
  403. package/library/jwt/JwtEncoder.ts +40 -0
  404. package/library/jwt/JwtKeyRing.ts +164 -0
  405. package/library/jwt/JwtValidator.ts +210 -0
  406. package/library/jwt/TokenIssuer.ts +149 -0
  407. package/library/jwt/TokenService.ts +51 -0
  408. package/library/jwt/base64url.ts +61 -0
  409. package/library/jwt/claims.ts +36 -0
  410. package/library/jwt/errors.ts +44 -0
  411. package/library/jwt/index.ts +59 -0
  412. package/library/jwt/limits.ts +8 -0
  413. package/library/jwt/signing/Hs256Algorithm.ts +62 -0
  414. package/library/jwt/signing/Rs256Algorithm.ts +133 -0
  415. package/library/jwt/signing/SigningAlgorithm.ts +18 -0
  416. package/library/jwt/signing/pem.ts +30 -0
  417. package/library/openapi/codegen.ts +875 -0
  418. package/library/openapi/document.ts +326 -0
  419. package/library/openapi/index.ts +13 -0
  420. package/library/openapi/types.ts +69 -0
  421. package/library/openapi/ui.ts +182 -0
  422. package/library/orm/DatabaseFacade.ts +209 -0
  423. package/library/orm/DbContext.ts +107 -0
  424. package/library/orm/DbContextFactory.ts +25 -0
  425. package/library/orm/DbContextOptions.ts +33 -0
  426. package/library/orm/Metadata/DynamicModelBuilder.ts +497 -0
  427. package/library/orm/Metadata/KeyTuple.ts +36 -0
  428. package/library/orm/Metadata/ModelBuilder.ts +299 -0
  429. package/library/orm/Metadata/OrmModel.ts +80 -0
  430. package/library/orm/Metadata/ValueConverter.ts +57 -0
  431. package/library/orm/Metadata/conventions.ts +54 -0
  432. package/library/orm/Metadata/decorators.ts +396 -0
  433. package/library/orm/Metadata/types.ts +140 -0
  434. package/library/orm/Providers/OwnedStoreCatalog.reader.ts +1802 -0
  435. package/library/orm/Providers/PostgresDialect.ts +234 -0
  436. package/library/orm/Providers/PostgresProvider.ts +1494 -0
  437. package/library/orm/Providers/PostgresSchema.decoder.ts +107 -0
  438. package/library/orm/Providers/limits.ts +29 -0
  439. package/library/orm/Providers/operationDeadline.ts +43 -0
  440. package/library/orm/Providers/ormOwnedStoreRuntime.ts +19 -0
  441. package/library/orm/Providers/ormTransactionRuntime.ts +117 -0
  442. package/library/orm/Providers/propertyConversion.ts +14 -0
  443. package/library/orm/Providers/resolvedForeignKey.ts +16 -0
  444. package/library/orm/Providers/transactionCallbacks.ts +45 -0
  445. package/library/orm/Providers/transactionOutcome.ts +82 -0
  446. package/library/orm/Providers/transactionScopes.ts +78 -0
  447. package/library/orm/Providers/types.ts +207 -0
  448. package/library/orm/Query/DbSet.ts +78 -0
  449. package/library/orm/Query/EntityQuery.ts +247 -0
  450. package/library/orm/Query/ImmediateMutations.ts +226 -0
  451. package/library/orm/Query/IncludeLoader.ts +200 -0
  452. package/library/orm/Query/QueryPlan.ts +55 -0
  453. package/library/orm/Query/SqlTranslator.ts +195 -0
  454. package/library/orm/Query/conditions.ts +127 -0
  455. package/library/orm/Query/materialize.ts +39 -0
  456. package/library/orm/README.md +256 -0
  457. package/library/orm/Repository/README.md +684 -0
  458. package/library/orm/Repository/Repository.ts +68 -0
  459. package/library/orm/Saving/CommandBuilder.ts +139 -0
  460. package/library/orm/Saving/ExecutionStrategy.ts +128 -0
  461. package/library/orm/Saving/SaveExecutor.ts +413 -0
  462. package/library/orm/Saving/applyConventions.ts +64 -0
  463. package/library/orm/Schema/CheckExpression.ts +327 -0
  464. package/library/orm/Schema/ExactSchemaVerifier.ts +168 -0
  465. package/library/orm/Schema/ExpectedSchema.ts +43 -0
  466. package/library/orm/Schema/MigrationRunner.ts +146 -0
  467. package/library/orm/Schema/Migrator.ts +73 -0
  468. package/library/orm/Schema/OrmOwnedStore.ts +123 -0
  469. package/library/orm/Schema/OwnedStoreAdmission.ts +752 -0
  470. package/library/orm/Schema/OwnedStoreCanonical.ts +273 -0
  471. package/library/orm/Schema/OwnedStoreCatalog.ts +1325 -0
  472. package/library/orm/Schema/SafeAdditiveSchema.ts +100 -0
  473. package/library/orm/Schema/SchemaAdmission.ts +62 -0
  474. package/library/orm/Schema/SchemaDiffer.ts +78 -0
  475. package/library/orm/Schema/formatSchemaDifferences.ts +62 -0
  476. package/library/orm/Schema/introspection.ts +52 -0
  477. package/library/orm/Schema/physicalColumnTypes.ts +37 -0
  478. package/library/orm/Schema/tableKey.ts +26 -0
  479. package/library/orm/Tracking/ChangeTracker.ts +448 -0
  480. package/library/orm/Tracking/EntityState.ts +13 -0
  481. package/library/orm/Transactions/OrmTransaction.ts +30 -0
  482. package/library/orm/Transactions/TransactionScopeCoordinator.ts +377 -0
  483. package/library/orm/errors.ts +190 -0
  484. package/library/orm/index.ts +162 -0
  485. package/library/orm/runtime.ts +14 -0
  486. package/library/redaction/index.ts +166 -0
  487. package/library/ui/UiProfile.ts +77 -0
  488. package/library/ui/compiled-v1.ts +131 -0
  489. package/library/ui/compiler/compileUiSurfaceV1.ts +717 -0
  490. package/library/ui/compiler/contracts-v1.ts +25 -0
  491. package/library/ui/compiler/index.ts +12 -0
  492. package/library/ui/compiler/openApiIndex.ts +246 -0
  493. package/library/ui/defineUiProfile.ts +340 -0
  494. package/library/ui/diagnostics-v1.ts +33 -0
  495. package/library/ui/index.ts +129 -0
  496. package/library/ui/profile-v1.ts +191 -0
  497. package/library/ui/serialization.ts +130 -0
  498. package/library/ui/uiProfileAuthoring.ts +289 -0
  499. package/library/ui/uiProfileAuthoringMetadata.ts +78 -0
  500. package/library/validation/MessageRegistry.ts +144 -0
  501. package/library/validation/README.md +62 -0
  502. package/library/validation/RuleCompiler.ts +183 -0
  503. package/library/validation/RuleEngine.ts +401 -0
  504. package/library/validation/SPEC.md +579 -0
  505. package/library/validation/ValidationError.ts +37 -0
  506. package/library/validation/ValidationResult.ts +63 -0
  507. package/library/validation/Validator.ts +101 -0
  508. package/library/validation/index.ts +16 -0
  509. package/library/validation/metadata.ts +69 -0
  510. package/library/validation/modelValidatorAdapter.ts +119 -0
  511. package/library/validation/types/ValidationCode.ts +69 -0
  512. package/library/validation/types/ValidatorOptions.ts +153 -0
  513. package/package.json +80 -0
@@ -0,0 +1,90 @@
1
+ import { REQUEST_ID_STATE_KEY, type Logger } from "../../kernel";
2
+ import type { HttpContext } from "../HttpContext/HttpContext";
3
+ import type { HttpMiddleware } from "./types";
4
+ import { redactSensitive } from "../../../library/redaction";
5
+
6
+ export interface AccessLogEntry {
7
+ readonly method: string;
8
+ readonly path: string;
9
+ readonly status: number;
10
+ /** Wall-clock time spent in the pipeline, in milliseconds. */
11
+ readonly durationMs: number;
12
+ /** Correlation id from {@link createCorrelationIdMiddleware}, if present. */
13
+ readonly requestId?: string;
14
+ }
15
+
16
+ export interface AccessLogOptions {
17
+ /** Sink for entries (highest precedence). Default: structured `logger`, else stdout. */
18
+ readonly log?: (entry: AccessLogEntry) => void;
19
+ /** Structured sink: `logger.info` per request. Used when `log` is absent. */
20
+ readonly logger?: Logger;
21
+ /** Return `true` to skip logging a request (e.g. noisy probes). */
22
+ readonly skip?: (ctx: HttpContext) => boolean;
23
+ }
24
+
25
+ function defaultLog(entry: AccessLogEntry): void {
26
+ const requestId = entry.requestId ? ` ${entry.requestId}` : "";
27
+ console.log(`[http] ${entry.method} ${entry.path} ${entry.status} ${entry.durationMs.toFixed(1)}ms${requestId}`);
28
+ }
29
+
30
+ function resolveSink(options: AccessLogOptions): (entry: AccessLogEntry) => void {
31
+ if (options.log) {
32
+ return options.log;
33
+ }
34
+ const logger = options.logger;
35
+ if (logger) {
36
+ return (entry) =>
37
+ logger.info(
38
+ `${entry.method} ${entry.path} ${entry.status} ${entry.durationMs.toFixed(1)}ms`,
39
+ {
40
+ method: entry.method,
41
+ path: entry.path,
42
+ status: entry.status,
43
+ durationMs: entry.durationMs,
44
+ ...(entry.requestId !== undefined ? { requestId: entry.requestId } : {}),
45
+ },
46
+ );
47
+ }
48
+ return defaultLog;
49
+ }
50
+
51
+ /**
52
+ * Access log middleware: records method, path, final status and duration for
53
+ * every request. The correlation id is read from `ctx.state` (set by
54
+ * {@link createCorrelationIdMiddleware}), so it is captured regardless of the
55
+ * order relative to the correlation middleware.
56
+ *
57
+ * Install it outermost (the server does this automatically for
58
+ * `httpModule({ accessLog })`) so it measures the whole pipeline and observes
59
+ * the final status, including errors mapped by the error boundary.
60
+ */
61
+ export function accessLog(options: AccessLogOptions = {}): HttpMiddleware {
62
+ const log = resolveSink(options);
63
+ const skip = options.skip;
64
+ return async (ctx, next) => {
65
+ const startedAt = performance.now();
66
+ try {
67
+ await next();
68
+ } finally {
69
+ try {
70
+ if (!skip?.(ctx)) {
71
+ const requestId = ctx.state.get(REQUEST_ID_STATE_KEY);
72
+ log({
73
+ method: ctx.method,
74
+ path: ctx.path,
75
+ status: ctx.response?.status ?? 0,
76
+ durationMs: performance.now() - startedAt,
77
+ ...(typeof requestId === "string" ? { requestId } : {}),
78
+ });
79
+ }
80
+ } catch (error) {
81
+ // Observability must never replace an application response.
82
+ try {
83
+ console.error("[http] access log sink failed:", redactSensitive(error));
84
+ } catch {
85
+ // Even the fallback console may be replaced by application code.
86
+ }
87
+ }
88
+ }
89
+ };
90
+ }
@@ -0,0 +1,160 @@
1
+ import type { HttpMiddleware } from "./types";
2
+ import { HttpSetupError } from "../Errors/HttpError";
3
+ import { redactSensitive } from "../../../library/redaction";
4
+
5
+ export interface CorsOptions {
6
+ /**
7
+ * Allowed origins: exact string, list, predicate, or "*" (default).
8
+ * `credentials: true` requires an explicit allow-list or predicate.
9
+ */
10
+ origin?: string | readonly string[] | ((origin: string) => boolean);
11
+ /** Allowed methods for preflight (default: common verbs). */
12
+ methods?: readonly string[];
13
+ /** Allowed request headers for preflight (default: echo requested headers). */
14
+ allowedHeaders?: readonly string[];
15
+ /** Headers exposed to the browser. */
16
+ exposedHeaders?: readonly string[];
17
+ /** Allow cookies/credentials. */
18
+ credentials?: boolean;
19
+ /** Preflight cache time (Access-Control-Max-Age). */
20
+ maxAgeSeconds?: number;
21
+ }
22
+
23
+ const DEFAULT_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"] as const;
24
+
25
+ function assertSafeCorsOptions(options: CorsOptions): void {
26
+ if (!options.credentials) {
27
+ return;
28
+ }
29
+ const { origin } = options;
30
+ const allowsWildcard = origin === undefined || origin === "*" || (Array.isArray(origin) && origin.includes("*"));
31
+ if (allowsWildcard) {
32
+ throw new HttpSetupError(
33
+ "CORS credentials require an explicit origin allow-list or predicate. " +
34
+ "Do not combine credentials: true with omitted origin or '*'.",
35
+ );
36
+ }
37
+ }
38
+
39
+ function resolveOrigin(options: CorsOptions, requestOrigin: string): string | undefined {
40
+ const { origin } = options;
41
+ if (origin === undefined || origin === "*") {
42
+ return options.credentials ? requestOrigin : "*";
43
+ }
44
+ if (typeof origin === "string") {
45
+ return origin === requestOrigin ? requestOrigin : undefined;
46
+ }
47
+ if (typeof origin === "function") {
48
+ return origin(requestOrigin) ? requestOrigin : undefined;
49
+ }
50
+ if (origin.includes("*")) {
51
+ return "*";
52
+ }
53
+ return origin.includes(requestOrigin) ? requestOrigin : undefined;
54
+ }
55
+
56
+ function variesByOrigin(options: CorsOptions): boolean {
57
+ const origin = options.origin;
58
+ return origin !== undefined && origin !== "*" && !(Array.isArray(origin) && origin.includes("*"));
59
+ }
60
+
61
+ function applyCommonHeaders(headers: Headers, options: CorsOptions, allowedOrigin: string): void {
62
+ headers.set("access-control-allow-origin", allowedOrigin);
63
+ if (allowedOrigin !== "*") {
64
+ appendVaryOrigin(headers);
65
+ }
66
+ if (options.credentials) {
67
+ headers.set("access-control-allow-credentials", "true");
68
+ }
69
+ if (options.exposedHeaders?.length) {
70
+ headers.set("access-control-expose-headers", options.exposedHeaders.join(", "));
71
+ }
72
+ }
73
+
74
+ function appendVaryOrigin(headers: Headers): void {
75
+ const vary = headers.get("vary");
76
+ if (vary === null) {
77
+ headers.set("vary", "Origin");
78
+ return;
79
+ }
80
+ if (!vary.toLowerCase().split(",").map((item) => item.trim()).includes("origin")) {
81
+ headers.append("vary", "Origin");
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Is this request a CORS preflight (OPTIONS + Access-Control-Request-Method)?
87
+ * Preflights are answered by the server before routing, so they work even
88
+ * for routes that do not declare an OPTIONS handler.
89
+ */
90
+ export function isPreflight(request: Request): boolean {
91
+ return request.method === "OPTIONS" && request.headers.has("access-control-request-method");
92
+ }
93
+
94
+ /** Builds the preflight response (204 with the negotiated CORS headers). */
95
+ export function preflightResponse(options: CorsOptions, request: Request): Response {
96
+ assertSafeCorsOptions(options);
97
+ const requestOrigin = request.headers.get("origin");
98
+ const headers = new Headers();
99
+ if (variesByOrigin(options)) {
100
+ appendVaryOrigin(headers);
101
+ }
102
+ if (requestOrigin) {
103
+ const allowedOrigin = resolveOrigin(options, requestOrigin);
104
+ if (allowedOrigin) {
105
+ applyCommonHeaders(headers, options, allowedOrigin);
106
+ headers.set("access-control-allow-methods", (options.methods ?? DEFAULT_METHODS).join(", "));
107
+ const requestedHeaders = request.headers.get("access-control-request-headers");
108
+ const allowHeaders = options.allowedHeaders?.join(", ") ?? requestedHeaders;
109
+ if (allowHeaders) {
110
+ headers.set("access-control-allow-headers", allowHeaders);
111
+ }
112
+ if (options.maxAgeSeconds !== undefined) {
113
+ headers.set("access-control-max-age", String(options.maxAgeSeconds));
114
+ }
115
+ }
116
+ }
117
+ return new Response(null, { status: 204, headers });
118
+ }
119
+
120
+ /**
121
+ * CORS middleware: adds response headers for cross-origin requests.
122
+ * Use globally via `httpModule({ cors })` (which also answers preflights)
123
+ * or per controller/route via `@Middleware(cors({...}))`.
124
+ */
125
+ export function cors(options: CorsOptions = {}): HttpMiddleware {
126
+ assertSafeCorsOptions(options);
127
+ return async (ctx, next) => {
128
+ await next();
129
+ const requestOrigin = ctx.header("origin");
130
+ if (!ctx.response) {
131
+ return;
132
+ }
133
+ try {
134
+ // The representation depends on Origin even when this particular origin
135
+ // is absent or denied, so shared caches must always vary on it.
136
+ const varies = variesByOrigin(options);
137
+ if (varies) {
138
+ appendVaryOrigin(ctx.response.headers);
139
+ }
140
+ if (!requestOrigin && varies) {
141
+ return;
142
+ }
143
+ // A wildcard policy is truly invariant: emit its headers even without
144
+ // Origin, so a cached same-origin response also works for a CORS caller.
145
+ const allowedOrigin = requestOrigin ? resolveOrigin(options, requestOrigin) : "*";
146
+ if (allowedOrigin) {
147
+ applyCommonHeaders(ctx.response.headers, options, allowedOrigin);
148
+ }
149
+ } catch (error) {
150
+ // CORS is response decoration, not authorization. A buggy predicate or
151
+ // immutable raw response must not expose Bun's development error page or
152
+ // replace an otherwise valid response.
153
+ try {
154
+ console.error("[http] CORS evaluation failed:", redactSensitive(error));
155
+ } catch {
156
+ // Best-effort diagnostics only.
157
+ }
158
+ }
159
+ };
160
+ }
@@ -0,0 +1,100 @@
1
+ import { redactSensitive } from "../../../library/redaction";
2
+ import type { HttpContext } from "../HttpContext/HttpContext";
3
+ import { HttpError } from "../Errors/HttpError";
4
+ import type { HttpMiddleware } from "./types";
5
+
6
+ export interface ErrorHandlerOptions {
7
+ /**
8
+ * Include the error message and stack in 500 responses (development only —
9
+ * never enable in production).
10
+ */
11
+ exposeDetails?: boolean;
12
+ /** Called for non-{@link HttpError} failures (self-wired via `HTTP_ERROR_HOOK`, e.g. the logging module). */
13
+ onUnexpectedError?: (ctx: HttpContext, error: unknown) => void;
14
+ /** Fallback when {@link onUnexpectedError} is omitted or does not handle the error. */
15
+ logError?: (error: unknown) => void;
16
+ }
17
+
18
+ function jsonResponse(status: number, body: unknown, extraHeaders?: readonly [string, string][]): Response {
19
+ const headers = new Headers({ "content-type": "application/json; charset=utf-8" });
20
+ if (extraHeaders) {
21
+ for (const [name, value] of extraHeaders) {
22
+ headers.set(name, value);
23
+ }
24
+ }
25
+ return new Response(JSON.stringify(body), { status, headers });
26
+ }
27
+
28
+ function safeJsonResponse(status: number, body: unknown, extraHeaders?: readonly [string, string][]): Response {
29
+ try {
30
+ return jsonResponse(status, body, extraHeaders);
31
+ } catch {
32
+ return jsonResponse(500, { error: "Internal Server Error" });
33
+ }
34
+ }
35
+
36
+ function safelyReportUnexpected(
37
+ options: ErrorHandlerOptions,
38
+ fallback: (error: unknown) => void,
39
+ ctx: HttpContext,
40
+ error: unknown,
41
+ ): void {
42
+ try {
43
+ options.onUnexpectedError?.(ctx, error);
44
+ } catch (hookError) {
45
+ try {
46
+ fallback(hookError);
47
+ } catch {
48
+ // Logging hooks are best-effort; response safety is primary.
49
+ }
50
+ }
51
+ try {
52
+ fallback(error);
53
+ } catch {
54
+ // A broken sink must not escape the HTTP error boundary.
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Built-in error boundary (always installed by the server). `HttpError` maps
60
+ * to its status with a safe JSON payload; anything
61
+ * else becomes 500 Internal Server Error.
62
+ */
63
+ export function errorHandler(options: ErrorHandlerOptions = {}): HttpMiddleware {
64
+ const fallback = options.logError ?? ((error: unknown) => console.error("[http] unhandled error:", redactSensitive(error)));
65
+ return async (ctx, next) => {
66
+ try {
67
+ await next();
68
+ } catch (error) {
69
+ if (error instanceof HttpError) {
70
+ const extra: [string, string][] = [];
71
+ const allow = (error as { allow?: readonly string[] }).allow;
72
+ if (allow) {
73
+ extra.push(["allow", allow.join(", ")]);
74
+ }
75
+ const retryAfter = (error as { retryAfterSeconds?: number }).retryAfterSeconds;
76
+ if (retryAfter !== undefined) {
77
+ extra.push(["retry-after", String(retryAfter)]);
78
+ }
79
+ const challenge = (error as { challenge?: string }).challenge;
80
+ if (challenge !== undefined) {
81
+ extra.push(["www-authenticate", challenge]);
82
+ }
83
+ ctx.response = safeJsonResponse(
84
+ error.status,
85
+ error.details === undefined ? { error: error.message } : { error: error.message, details: error.details },
86
+ extra,
87
+ );
88
+ return;
89
+ }
90
+ safelyReportUnexpected(options, fallback, ctx, error);
91
+ ctx.response = options.exposeDetails
92
+ ? safeJsonResponse(500, {
93
+ error: "Internal Server Error",
94
+ message: error instanceof Error ? error.message : String(error),
95
+ stack: error instanceof Error ? error.stack : undefined,
96
+ })
97
+ : safeJsonResponse(500, { error: "Internal Server Error" });
98
+ }
99
+ };
100
+ }
@@ -0,0 +1,21 @@
1
+ import type { HttpContext } from "../HttpContext/HttpContext";
2
+ import type { HttpMiddleware } from "./types";
3
+
4
+ /**
5
+ * Runs a precompiled middleware chain. The chain array is built once per
6
+ * route at startup; per request we only allocate the dispatch closure.
7
+ */
8
+ export function runPipeline(chain: readonly HttpMiddleware[], ctx: HttpContext): Promise<void> {
9
+ let lastIndex = -1;
10
+ const dispatch = async (index: number): Promise<void> => {
11
+ if (index <= lastIndex) {
12
+ throw new Error("next() called multiple times in the same middleware");
13
+ }
14
+ lastIndex = index;
15
+ const middleware = chain[index];
16
+ if (middleware) {
17
+ await middleware(ctx, () => dispatch(index + 1));
18
+ }
19
+ };
20
+ return dispatch(0);
21
+ }
@@ -0,0 +1,104 @@
1
+ import { TooManyRequestsError } from "../Errors/HttpError";
2
+ import type { HttpContext } from "../HttpContext/HttpContext";
3
+ import type { HttpMiddleware } from "./types";
4
+ import { HttpSetupError } from "../Errors/HttpError";
5
+
6
+ export interface RateLimitOptions {
7
+ /** Window size in milliseconds. */
8
+ windowMs: number;
9
+ /** Max requests per key per window. */
10
+ max: number;
11
+ /**
12
+ * Request key (default: Bun's direct peer IP, falling back to a single shared
13
+ * bucket when unavailable). Plug your own: user id, API key, etc.
14
+ */
15
+ keyOf?: (ctx: HttpContext) => string;
16
+ /** Trust the first `X-Forwarded-For` hop. Off by default. */
17
+ trustProxy?: boolean;
18
+ /** Forwarded client-address header used when `trustProxy` is true. */
19
+ proxyHeader?: string;
20
+ /** Hard cap for in-process buckets. New keys receive 429 when full. Default: 10,000. */
21
+ maxBuckets?: number;
22
+ }
23
+
24
+ interface Bucket {
25
+ count: number;
26
+ resetAt: number;
27
+ }
28
+
29
+ /**
30
+ * Fixed-window in-memory rate limiter. Per-process state (no external
31
+ * store) — suitable for a single binary; swap `keyOf` or wrap your own
32
+ * middleware for distributed setups. Exceeding the limit throws 429 with
33
+ * Retry-After. At capacity, new keys are rejected until a bucket expires;
34
+ * existing keys retain their quota and live buckets are never evicted.
35
+ */
36
+ export function rateLimit(options: RateLimitOptions): HttpMiddleware {
37
+ assertRateLimitOptions(options);
38
+ const buckets = new Map<string, Bucket>();
39
+ const maxBuckets = Math.floor(options.maxBuckets ?? 10_000);
40
+ let nextExpiry = Number.POSITIVE_INFINITY;
41
+ const proxyHeader = options.proxyHeader ?? "x-forwarded-for";
42
+ const keyOf = options.keyOf ?? ((ctx: HttpContext) => {
43
+ if (options.trustProxy) {
44
+ const forwarded = ctx.header(proxyHeader)?.split(",", 1)[0]?.trim();
45
+ if (forwarded) {
46
+ return forwarded;
47
+ }
48
+ }
49
+ return ctx.clientIp ?? "*";
50
+ });
51
+
52
+ return async (ctx, next) => {
53
+ const now = Date.now();
54
+ const key = keyOf(ctx);
55
+ let bucket = buckets.get(key);
56
+ if (bucket && bucket.resetAt <= now) {
57
+ buckets.delete(key);
58
+ bucket = undefined;
59
+ }
60
+ if (!bucket) {
61
+ // Avoid scanning a full table for every unknown key under saturation.
62
+ // This lower bound may be stale after a known key renews its own window;
63
+ // a single scan refreshes it without discarding any active quota.
64
+ if (buckets.size >= maxBuckets && now >= nextExpiry) {
65
+ nextExpiry = purgeExpired(buckets, now);
66
+ }
67
+ if (buckets.size >= maxBuckets) {
68
+ throw new TooManyRequestsError(Math.ceil((nextExpiry - now) / 1000));
69
+ }
70
+ bucket = { count: 0, resetAt: now + options.windowMs };
71
+ buckets.set(key, bucket);
72
+ nextExpiry = Math.min(nextExpiry, bucket.resetAt);
73
+ }
74
+ if (bucket.count >= options.max) {
75
+ throw new TooManyRequestsError(Math.ceil((bucket.resetAt - now) / 1000));
76
+ }
77
+ bucket.count += 1;
78
+ await next();
79
+ };
80
+ }
81
+
82
+ function assertRateLimitOptions(options: RateLimitOptions): void {
83
+ if (!Number.isFinite(options.windowMs) || options.windowMs <= 0) {
84
+ throw new HttpSetupError("rateLimit windowMs must be a positive finite number.");
85
+ }
86
+ if (!Number.isSafeInteger(options.max) || options.max <= 0) {
87
+ throw new HttpSetupError("rateLimit max must be a positive integer.");
88
+ }
89
+ if (options.maxBuckets !== undefined && (!Number.isSafeInteger(options.maxBuckets) || options.maxBuckets <= 0)) {
90
+ throw new HttpSetupError("rateLimit maxBuckets must be a positive integer.");
91
+ }
92
+ }
93
+
94
+ function purgeExpired(buckets: Map<string, Bucket>, now: number): number {
95
+ let nextExpiry = Number.POSITIVE_INFINITY;
96
+ for (const [key, bucket] of buckets) {
97
+ if (bucket.resetAt <= now) {
98
+ buckets.delete(key);
99
+ } else {
100
+ nextExpiry = Math.min(nextExpiry, bucket.resetAt);
101
+ }
102
+ }
103
+ return nextExpiry;
104
+ }
@@ -0,0 +1,118 @@
1
+ import type { HttpMiddleware } from "./types";
2
+ import { redactSensitive } from "../../../library/redaction";
3
+
4
+ /** `Strict-Transport-Security` tuning (only meaningful over HTTPS). */
5
+ export interface HstsOptions {
6
+ /** max-age in seconds. Default: 15552000 (180 days). */
7
+ readonly maxAgeSeconds?: number;
8
+ /** Append `includeSubDomains`. Default: true. */
9
+ readonly includeSubDomains?: boolean;
10
+ /** Append `preload`. Default: false. */
11
+ readonly preload?: boolean;
12
+ }
13
+
14
+ export interface SecurityHeadersOptions {
15
+ /** `X-Content-Type-Options: nosniff`. Default: true. */
16
+ readonly contentTypeOptions?: boolean;
17
+ /** `X-Frame-Options`. Default: "DENY". `false` omits it. */
18
+ readonly frameOptions?: "DENY" | "SAMEORIGIN" | false;
19
+ /** `Referrer-Policy`. Default: "no-referrer". `false` omits it. */
20
+ readonly referrerPolicy?: string | false;
21
+ /** `X-DNS-Prefetch-Control`. Default: "off". `false` omits it. */
22
+ readonly dnsPrefetchControl?: "off" | "on" | false;
23
+ /**
24
+ * `Strict-Transport-Security`. Default: off (apps are often behind TLS
25
+ * terminators and dev runs on HTTP). Pass `true` for sane defaults or an
26
+ * object to tune. Never sent unless explicitly enabled.
27
+ */
28
+ readonly hsts?: HstsOptions | boolean;
29
+ /**
30
+ * `Content-Security-Policy`. Default: off (a wrong CSP breaks apps). Provide
31
+ * a policy string to enable.
32
+ */
33
+ readonly contentSecurityPolicy?: string;
34
+ /** Extra headers to set (set-if-absent), e.g. a custom `Permissions-Policy`. */
35
+ readonly headers?: Readonly<Record<string, string>>;
36
+ }
37
+
38
+ function buildHeaders(options: SecurityHeadersOptions): Record<string, string> {
39
+ const headers: Record<string, string> = {};
40
+
41
+ if (options.contentTypeOptions !== false) {
42
+ headers["x-content-type-options"] = "nosniff";
43
+ }
44
+ if (options.frameOptions !== false) {
45
+ headers["x-frame-options"] = options.frameOptions ?? "DENY";
46
+ }
47
+ if (options.referrerPolicy !== false) {
48
+ headers["referrer-policy"] = options.referrerPolicy ?? "no-referrer";
49
+ }
50
+ if (options.dnsPrefetchControl !== false) {
51
+ headers["x-dns-prefetch-control"] = options.dnsPrefetchControl ?? "off";
52
+ }
53
+ if (options.hsts) {
54
+ const hsts: HstsOptions = options.hsts === true ? {} : options.hsts;
55
+ const parts = [`max-age=${hsts.maxAgeSeconds ?? 15_552_000}`];
56
+ if (hsts.includeSubDomains !== false) {
57
+ parts.push("includeSubDomains");
58
+ }
59
+ if (hsts.preload) {
60
+ parts.push("preload");
61
+ }
62
+ headers["strict-transport-security"] = parts.join("; ");
63
+ }
64
+ if (options.contentSecurityPolicy) {
65
+ headers["content-security-policy"] = options.contentSecurityPolicy;
66
+ }
67
+ if (options.headers) {
68
+ for (const [name, value] of Object.entries(options.headers)) {
69
+ headers[name.toLowerCase()] = value;
70
+ }
71
+ }
72
+
73
+ return headers;
74
+ }
75
+
76
+ /**
77
+ * Sets conservative security response headers (secure-by-default): `nosniff`,
78
+ * `X-Frame-Options: DENY`, `Referrer-Policy: no-referrer`, DNS-prefetch off.
79
+ * HSTS and CSP are opt-in (a wrong value breaks real apps). Headers are written
80
+ * set-if-absent, so a route that sets its own value wins.
81
+ *
82
+ * Installed automatically by `httpModule` unless `securityHeaders: false`.
83
+ */
84
+ export function securityHeaders(options: SecurityHeadersOptions = {}): HttpMiddleware {
85
+ const headers = buildHeaders(options);
86
+ return async (ctx, next) => {
87
+ await next();
88
+ const response = ctx.response;
89
+ if (!response) {
90
+ return;
91
+ }
92
+ applySecurityHeaders(response, options, headers);
93
+ };
94
+ }
95
+
96
+ /** Applies the configured defaults to a response without throwing. */
97
+ export function applySecurityHeaders(
98
+ response: Response,
99
+ options: SecurityHeadersOptions = {},
100
+ prepared: Record<string, string> = buildHeaders(options),
101
+ ): Response {
102
+ try {
103
+ for (const [name, value] of Object.entries(prepared)) {
104
+ if (!response.headers.has(name)) {
105
+ response.headers.set(name, value);
106
+ }
107
+ }
108
+ } catch (error) {
109
+ // A raw/foreign Response may expose immutable headers. Keep the original
110
+ // response rather than allowing response decoration to become a bare 500.
111
+ try {
112
+ console.error("[http] security header decoration failed:", redactSensitive(error));
113
+ } catch {
114
+ // Best-effort diagnostics only.
115
+ }
116
+ }
117
+ return response;
118
+ }
@@ -0,0 +1,19 @@
1
+ import type { HttpContext } from "../HttpContext/HttpContext";
2
+
3
+ /**
4
+ * Middleware signature (Koa/ASP.NET Core style): do work, call `next()` to
5
+ * pass control down the pipeline, optionally short-circuit by setting
6
+ * `ctx.response` without calling `next`.
7
+ */
8
+ export type HttpMiddleware = (ctx: HttpContext, next: () => Promise<void>) => void | Promise<void>;
9
+
10
+ /**
11
+ * Action filter (`@ActionFilter`): `before` is an action-boundary policy and
12
+ * therefore also runs before a short-circuiting output cache hit. `after`
13
+ * wraps an actual action invocation and may replace its result by returning a
14
+ * non-undefined value.
15
+ */
16
+ export interface ActionFilterHooks {
17
+ before?(ctx: HttpContext): void | Promise<void>;
18
+ after?(ctx: HttpContext, result: unknown): unknown | Promise<unknown>;
19
+ }