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,213 @@
1
+ import { CacheValueError } from "../errors/CacheError";
2
+
3
+ /** Headers that must not be stored or replayed from output cache. */
4
+ const STRIP_RESPONSE_HEADERS = new Set([
5
+ "authorization",
6
+ "cookie",
7
+ "proxy-authenticate",
8
+ "proxy-authorization",
9
+ "set-cookie",
10
+ "www-authenticate",
11
+ ]);
12
+
13
+ export function stripSensitiveResponseHeaders(headers: Readonly<Record<string, string>>): Record<string, string> {
14
+ const next: Record<string, string> = {};
15
+ for (const [key, value] of Object.entries(headers)) {
16
+ if (!STRIP_RESPONSE_HEADERS.has(key.toLowerCase())) {
17
+ next[key] = value;
18
+ }
19
+ }
20
+ return next;
21
+ }
22
+
23
+ /** Serialized HTTP response stored in {@link ICache}. */
24
+ export interface CachedHttpPayload {
25
+ readonly status: number;
26
+ readonly headers: Readonly<Record<string, string>>;
27
+ readonly body: Uint8Array;
28
+ }
29
+
30
+ export interface CachedHttpPayloadReadOptions {
31
+ /** Default 16 MiB. Set to 0 only for an explicit unbounded compatibility path. */
32
+ readonly maxBodyBytes?: number;
33
+ /** Total materialization timeout. Default 5000ms; 0 disables it. */
34
+ readonly bodyReadTimeoutMs?: number;
35
+ }
36
+
37
+ export interface ResolvedCachedHttpPayloadReadOptions {
38
+ readonly maxBodyBytes: number;
39
+ readonly bodyReadTimeoutMs: number;
40
+ }
41
+
42
+ export const DEFAULT_OUTPUT_CACHE_MAX_BODY_BYTES = 16 * 1024 * 1024;
43
+ export const DEFAULT_OUTPUT_CACHE_BODY_READ_TIMEOUT_MS = 5_000;
44
+
45
+ export function resolveCachedHttpPayloadReadOptions(
46
+ options: CachedHttpPayloadReadOptions = {},
47
+ ): ResolvedCachedHttpPayloadReadOptions {
48
+ const maxBodyBytes = options.maxBodyBytes ?? DEFAULT_OUTPUT_CACHE_MAX_BODY_BYTES;
49
+ const bodyReadTimeoutMs = options.bodyReadTimeoutMs ?? DEFAULT_OUTPUT_CACHE_BODY_READ_TIMEOUT_MS;
50
+ if (!Number.isSafeInteger(maxBodyBytes) || maxBodyBytes < 0) {
51
+ throw new CacheValueError("output cache maxBodyBytes must be a non-negative safe integer");
52
+ }
53
+ if (!Number.isSafeInteger(bodyReadTimeoutMs) || bodyReadTimeoutMs < 0) {
54
+ throw new CacheValueError("output cache bodyReadTimeoutMs must be a non-negative safe integer");
55
+ }
56
+ return Object.freeze({ maxBodyBytes, bodyReadTimeoutMs });
57
+ }
58
+
59
+ /**
60
+ * Dynamic response directives always win over decorator metadata. This keeps
61
+ * authentication/session responses and application-declared private responses
62
+ * out of shared server caches.
63
+ */
64
+ export function responseAllowsServerCache(
65
+ response: Response,
66
+ configuredVaryHeaders: readonly string[] = [],
67
+ ): boolean {
68
+ if (response.headers.has("set-cookie")) {
69
+ return false;
70
+ }
71
+
72
+ const cacheControl = response.headers.get("cache-control")?.toLowerCase() ?? "";
73
+ const directives = cacheControl.split(",").map((part) => part.trim().split("=", 1)[0]);
74
+ if (directives.includes("no-store") || directives.includes("private")) {
75
+ return false;
76
+ }
77
+
78
+ const vary = response.headers.get("vary");
79
+ if (vary === null || vary.trim().length === 0) {
80
+ return true;
81
+ }
82
+ const allowed = new Set(configuredVaryHeaders.map((name) => name.trim().toLowerCase()));
83
+ for (const name of vary.split(",").map((part) => part.trim().toLowerCase())) {
84
+ if (name === "*" || name.length === 0 || !allowed.has(name)) {
85
+ return false;
86
+ }
87
+ }
88
+ return true;
89
+ }
90
+
91
+ export async function responseToCachedPayload(
92
+ response: Response,
93
+ options: CachedHttpPayloadReadOptions | ResolvedCachedHttpPayloadReadOptions = {},
94
+ ): Promise<CachedHttpPayload | undefined> {
95
+ const limits = resolveCachedHttpPayloadReadOptions(options);
96
+ const advertised = Number(response.headers.get("content-length"));
97
+ if (limits.maxBodyBytes > 0 && Number.isFinite(advertised) && advertised > limits.maxBodyBytes) {
98
+ return undefined;
99
+ }
100
+
101
+ const headers: Record<string, string> = {};
102
+ response.headers.forEach((value, key) => {
103
+ const lower = key.toLowerCase();
104
+ if (!STRIP_RESPONSE_HEADERS.has(lower)) {
105
+ headers[lower] = value;
106
+ }
107
+ });
108
+ if (response.body === null) {
109
+ return { status: response.status, headers, body: new Uint8Array() };
110
+ }
111
+
112
+ let clone: ReturnType<Response["clone"]>;
113
+ try {
114
+ // Read a tee so an over-limit/slow response remains available to the
115
+ // caller and is merely skipped by the cache.
116
+ clone = response.clone();
117
+ } catch {
118
+ return undefined;
119
+ }
120
+ const body = await readBoundedBody(clone.body as unknown as CacheReadableBody | null, limits);
121
+ if (body === undefined) {
122
+ return undefined;
123
+ }
124
+ // The cached payload replaces the original response. Release its tee branch
125
+ // after the clone has been fully materialized.
126
+ await response.body.cancel().catch(() => undefined);
127
+ return { status: response.status, headers, body };
128
+ }
129
+
130
+ export function cachedPayloadToResponse(payload: CachedHttpPayload): Response {
131
+ return new Response(payload.body as unknown as ConstructorParameters<typeof Response>[0], {
132
+ status: payload.status,
133
+ headers: stripSensitiveResponseHeaders(payload.headers),
134
+ });
135
+ }
136
+
137
+ const BODY_READ_TIMEOUT = Symbol("output-cache-body-read-timeout");
138
+
139
+ async function readBoundedBody(
140
+ body: CacheReadableBody | null,
141
+ options: ResolvedCachedHttpPayloadReadOptions,
142
+ ): Promise<Uint8Array | undefined> {
143
+ if (body === null) return new Uint8Array();
144
+ const reader = body.getReader();
145
+ const chunks: Uint8Array[] = [];
146
+ let total = 0;
147
+ const deadline = options.bodyReadTimeoutMs === 0
148
+ ? undefined
149
+ : performance.now() + options.bodyReadTimeoutMs;
150
+ try {
151
+ while (true) {
152
+ const item = await readBeforeDeadline(reader, deadline);
153
+ if (item === BODY_READ_TIMEOUT) {
154
+ void reader.cancel("output cache body read timed out").catch(() => undefined);
155
+ return undefined;
156
+ }
157
+ if (item.done) break;
158
+ total += item.value.byteLength;
159
+ if (options.maxBodyBytes > 0 && total > options.maxBodyBytes) {
160
+ void reader.cancel("output cache body exceeds maxBodyBytes").catch(() => undefined);
161
+ return undefined;
162
+ }
163
+ chunks.push(item.value);
164
+ }
165
+ } catch {
166
+ void reader.cancel().catch(() => undefined);
167
+ return undefined;
168
+ } finally {
169
+ reader.releaseLock();
170
+ }
171
+
172
+ const result = new Uint8Array(total);
173
+ let offset = 0;
174
+ for (const chunk of chunks) {
175
+ result.set(chunk, offset);
176
+ offset += chunk.byteLength;
177
+ }
178
+ return result;
179
+ }
180
+
181
+ async function readBeforeDeadline(
182
+ reader: CacheBodyReader,
183
+ deadline: number | undefined,
184
+ ): Promise<CacheBodyReadResult | typeof BODY_READ_TIMEOUT> {
185
+ if (deadline === undefined) return reader.read();
186
+ const remaining = deadline - performance.now();
187
+ if (remaining <= 0) return BODY_READ_TIMEOUT;
188
+ let timer: ReturnType<typeof setTimeout> | undefined;
189
+ try {
190
+ return await Promise.race([
191
+ reader.read(),
192
+ new Promise<typeof BODY_READ_TIMEOUT>((resolve) => {
193
+ timer = setTimeout(() => resolve(BODY_READ_TIMEOUT), remaining);
194
+ }),
195
+ ]);
196
+ } finally {
197
+ if (timer !== undefined) clearTimeout(timer);
198
+ }
199
+ }
200
+
201
+ type CacheBodyReadResult =
202
+ | { readonly done: true; readonly value?: undefined }
203
+ | { readonly done: false; readonly value: Uint8Array };
204
+
205
+ interface CacheBodyReader {
206
+ read(): Promise<CacheBodyReadResult>;
207
+ cancel(reason?: unknown): Promise<void>;
208
+ releaseLock(): void;
209
+ }
210
+
211
+ interface CacheReadableBody {
212
+ getReader(): CacheBodyReader;
213
+ }
@@ -0,0 +1,61 @@
1
+ import type { ClientCacheOptions } from "../types/CachePolicy";
2
+ import type { OutputCachePolicyFields } from "../types/CachePolicy";
3
+ import { CacheValueError } from "../errors/CacheError";
4
+
5
+ /** Validates client-cache directives and forces personalized responses private. */
6
+ export function resolveOutputClientCacheOptions(
7
+ policy: Pick<OutputCachePolicyFields, "clientCache" | "varyByUser" | "varyByClaim">,
8
+ ): ClientCacheOptions | undefined {
9
+ if (policy.varyByClaim !== undefined && policy.varyByClaim.trim().length === 0) {
10
+ throw new CacheValueError("output cache varyByClaim must be a non-empty string");
11
+ }
12
+ const options = policy.clientCache;
13
+ if (options?.maxAge !== undefined && (!Number.isFinite(options.maxAge) || options.maxAge < 0)) {
14
+ throw new CacheValueError("output cache clientCache.maxAge must be a non-negative finite number");
15
+ }
16
+ if (options?.public === true && options.private === true) {
17
+ throw new CacheValueError("output cache clientCache cannot be both public and private");
18
+ }
19
+ const personalized = policy.varyByUser === true || policy.varyByClaim !== undefined;
20
+ if (!personalized) return options;
21
+ if (options?.public === true) {
22
+ throw new CacheValueError("personalized output cache cannot use clientCache.public");
23
+ }
24
+ return Object.freeze({ ...(options ?? {}), public: false, private: true });
25
+ }
26
+
27
+ /** Applies client/proxy Cache-Control headers (ASP.NET Response Cache layer). */
28
+ export function applyClientCacheHeaders(
29
+ response: Response,
30
+ options: ClientCacheOptions | undefined,
31
+ ): Response {
32
+ if (options === undefined) {
33
+ return response;
34
+ }
35
+
36
+ const directives: string[] = [];
37
+ if (options.noCache === true) {
38
+ directives.push("no-cache");
39
+ }
40
+ if (options.public === true) {
41
+ directives.push("public");
42
+ }
43
+ if (options.private === true) {
44
+ directives.push("private");
45
+ }
46
+ if (options.maxAge !== undefined) {
47
+ directives.push(`max-age=${Math.max(0, Math.floor(options.maxAge))}`);
48
+ }
49
+
50
+ if (directives.length === 0) {
51
+ return response;
52
+ }
53
+
54
+ const headers = new Headers(response.headers);
55
+ headers.set("cache-control", directives.join(", "));
56
+ return new Response(response.body, {
57
+ status: response.status,
58
+ statusText: response.statusText,
59
+ headers,
60
+ });
61
+ }
@@ -0,0 +1,154 @@
1
+ import { PRINCIPAL_STATE_KEY, type HttpContext, type RequestPrincipal } from "../../http";
2
+ import { createHash } from "node:crypto";
3
+ import type { ResolvedOutputCacheOptions } from "../internal/resolveCachePolicy";
4
+
5
+ /**
6
+ * The `ctx.state` key holding the request principal. Equal to the kernel's
7
+ * {@link PRINCIPAL_STATE_KEY}, so `varyByUser`/`unlessAuthenticated` see the same
8
+ * subject the authentication layer stores.
9
+ */
10
+ export const OUTPUT_CACHE_PRINCIPAL_STATE_KEY = PRINCIPAL_STATE_KEY;
11
+
12
+ function appendQueryParts(
13
+ parts: CacheKeyPart[],
14
+ url: URL,
15
+ keys: readonly string[] | "*",
16
+ ): void {
17
+ if (keys === "*") {
18
+ const names = [...new Set(url.searchParams.keys())].sort((left, right) => left.localeCompare(right));
19
+ for (const name of names) {
20
+ parts.push(["query", name, url.searchParams.getAll(name)]);
21
+ }
22
+ return;
23
+ }
24
+ for (const name of keys) {
25
+ const values = url.searchParams.getAll(name);
26
+ if (values.length > 0) {
27
+ parts.push(["query", name, values]);
28
+ }
29
+ }
30
+ }
31
+
32
+ function appendRouteParts(
33
+ parts: CacheKeyPart[],
34
+ params: HttpContext["params"],
35
+ keys: readonly string[],
36
+ ): void {
37
+ for (const name of keys) {
38
+ const value = params[name];
39
+ if (value !== undefined) {
40
+ parts.push(["route", name, value]);
41
+ }
42
+ }
43
+ }
44
+
45
+ function appendHeaderParts(
46
+ parts: CacheKeyPart[],
47
+ ctx: HttpContext,
48
+ keys: readonly string[],
49
+ ): void {
50
+ for (const name of keys) {
51
+ const value = ctx.header(name);
52
+ if (value !== undefined) {
53
+ parts.push(["header", name.toLowerCase(), value]);
54
+ }
55
+ }
56
+ }
57
+
58
+ function appendUserPart(parts: CacheKeyPart[], ctx: HttpContext, varyByUser: boolean, varyByClaim?: string): void {
59
+ const principal = ctx.state.get(OUTPUT_CACHE_PRINCIPAL_STATE_KEY) as RequestPrincipal | undefined;
60
+ if (varyByUser) {
61
+ parts.push(["user", principal === undefined ? "anonymous" : "authenticated", principal?.subject ?? ""]);
62
+ }
63
+ if (varyByClaim !== undefined) {
64
+ parts.push([
65
+ "claim",
66
+ varyByClaim,
67
+ principal === undefined ? "anonymous" : "authenticated",
68
+ principal?.findFirst?.(varyByClaim) ?? "",
69
+ ]);
70
+ }
71
+ }
72
+
73
+ /** Authenticated requests need every configured identity component to exist. */
74
+ export function canBuildPersonalizedOutputCacheKey(
75
+ ctx: HttpContext,
76
+ options: {
77
+ readonly varyByUser?: boolean;
78
+ readonly varyByClaim?: string;
79
+ readonly allowAuthenticatedShared?: boolean;
80
+ },
81
+ ): boolean {
82
+ if (options.varyByClaim !== undefined && options.varyByClaim.trim().length === 0) {
83
+ return false;
84
+ }
85
+ const principal = ctx.state.get(OUTPUT_CACHE_PRINCIPAL_STATE_KEY) as RequestPrincipal | undefined;
86
+ if (principal === undefined) return true;
87
+ if (
88
+ options.varyByUser !== true
89
+ && options.varyByClaim === undefined
90
+ && options.allowAuthenticatedShared !== true
91
+ ) {
92
+ return false;
93
+ }
94
+ if (
95
+ options.varyByUser === true
96
+ && (typeof principal.subject !== "string" || principal.subject.trim().length === 0)
97
+ ) {
98
+ return false;
99
+ }
100
+ if (options.varyByClaim !== undefined) {
101
+ const claim = principal.findFirst?.(options.varyByClaim);
102
+ if (typeof claim !== "string" || claim.trim().length === 0) {
103
+ return false;
104
+ }
105
+ }
106
+ return true;
107
+ }
108
+
109
+ type CacheKeyPart = readonly [kind: string, ...values: readonly (string | number | boolean | readonly string[])[]];
110
+
111
+ /**
112
+ * Cache keys can contain credentials, subject identifiers and arbitrary query
113
+ * values. Keep those values out of memory listings/Redis keyspace and use a
114
+ * collision-resistant digest over an unambiguous JSON tuple representation.
115
+ */
116
+ function digestKey(parts: readonly CacheKeyPart[]): string {
117
+ return `http:${createHash("sha256").update(JSON.stringify(parts)).digest("hex")}`;
118
+ }
119
+
120
+ /** Builds a deterministic output-cache key for the request. */
121
+ export function buildOutputCacheKey(
122
+ ctx: HttpContext,
123
+ routeName: string,
124
+ options: ResolvedOutputCacheOptions,
125
+ ): string {
126
+ // Runtime class names are not globally unique (dynamic modules can contain
127
+ // two same-named controllers). The concrete path is an independent route
128
+ // identity component and also prevents accidental sharing across route-param
129
+ // values when varyByRoute was omitted.
130
+ const parts: CacheKeyPart[] = [
131
+ ["action", ctx.method.toUpperCase(), routeName],
132
+ ["origin", ctx.url.origin],
133
+ ["path", ctx.url.pathname],
134
+ ];
135
+
136
+ // Query is part of the safe default identity. `[]` is the explicit opt-out
137
+ // for an endpoint whose representation is proven query-independent.
138
+ appendQueryParts(parts, ctx.url, options.varyByQuery ?? "*");
139
+ if (options.varyByRoute !== undefined) {
140
+ appendRouteParts(parts, ctx.params, options.varyByRoute);
141
+ }
142
+ if (options.varyByHeader !== undefined) {
143
+ appendHeaderParts(parts, ctx, options.varyByHeader);
144
+ }
145
+ if (options.varyByUser === true || options.varyByClaim !== undefined) {
146
+ appendUserPart(parts, ctx, options.varyByUser === true, options.varyByClaim);
147
+ }
148
+
149
+ if (ctx.apiVersion !== undefined) {
150
+ parts.push(["version", ctx.apiVersion]);
151
+ }
152
+
153
+ return digestKey(parts);
154
+ }
@@ -0,0 +1,16 @@
1
+ import type { HttpContext } from "../../http";
2
+ import { buildOutputCacheKey } from "./buildOutputCacheKey";
3
+ import type { ResolvedOutputRedisCacheOptions } from "../types/OutputRedisCacheOptions";
4
+
5
+ /** Builds Redis storage key (connection prefix applied separately in {@link RedisOutputCache}). */
6
+ export function buildRedisOutputCacheKey(
7
+ ctx: HttpContext,
8
+ routeName: string,
9
+ options: ResolvedOutputRedisCacheOptions,
10
+ ): string {
11
+ const base = buildOutputCacheKey(ctx, routeName, options);
12
+ if (options.keyPrefix === undefined || options.keyPrefix.length === 0) {
13
+ return base;
14
+ }
15
+ return `${options.keyPrefix}${base}`;
16
+ }
@@ -0,0 +1,130 @@
1
+ import type { Class, InjectionToken } from "../../di";
2
+ import type { RouteMiddlewareComposer } from "../../http";
3
+ import type { ActionMeta, ControllerMeta } from "../../http/Decorators/metadata";
4
+ import type { ICache } from "../ICache";
5
+ import {
6
+ outputCacheMetaOf,
7
+ resolveOutputCacheRequirement,
8
+ } from "../decorators/outputCacheMetadata";
9
+ import {
10
+ outputRedisCacheMetaOf,
11
+ resolveOutputRedisCacheRequirement,
12
+ } from "../decorators/outputRedisCacheMetadata";
13
+ import {
14
+ requireCacheSeconds,
15
+ resolveCacheOptions,
16
+ type ResolvedOutputCacheOptions,
17
+ } from "../internal/resolveCachePolicy";
18
+ import { resolveOutputRedisCacheOptions } from "../internal/resolveOutputRedisCachePolicy";
19
+ import type { CachePolicyRegistry } from "../types/CachePolicy";
20
+ import type { ResolvedOutputRedisCacheOptions } from "../types/OutputRedisCacheOptions";
21
+ import type { CachedHttpPayload } from "./CachedHttpPayload";
22
+ import { outputCacheMiddleware } from "./outputCacheMiddleware";
23
+ import { outputRedisCacheMiddleware } from "./outputRedisCacheMiddleware";
24
+ import {
25
+ guardInsecureOutputCacheRoute,
26
+ type OutputCacheSecurityWarningOptions,
27
+ } from "./outputCacheSecurityWarning";
28
+
29
+ /** Composes per-route output cache middleware from `@OutputCache` / `@OutputRedisCache`. */
30
+ export type RouteOutputCacheComposer = RouteMiddlewareComposer;
31
+
32
+ export interface CreateRouteOutputCacheComposerOptions {
33
+ readonly policies: CachePolicyRegistry;
34
+ readonly globalEnabled: boolean;
35
+ readonly cacheToken: InjectionToken<ICache<CachedHttpPayload>>;
36
+ readonly securityWarnings?: OutputCacheSecurityWarningOptions;
37
+ /** Whether a distributed cache backend is configured (enables `@OutputRedisCache`). */
38
+ readonly distributedEnabled?: boolean;
39
+ }
40
+
41
+ export function createRouteOutputCacheComposer(
42
+ policies: CachePolicyRegistry,
43
+ globalEnabled: boolean,
44
+ cacheToken: InjectionToken<ICache<CachedHttpPayload>>,
45
+ securityWarnings?: OutputCacheSecurityWarningOptions,
46
+ distributedEnabled = false,
47
+ ): RouteOutputCacheComposer {
48
+ return createRouteOutputCacheComposerWithOptions({
49
+ policies,
50
+ globalEnabled,
51
+ cacheToken,
52
+ securityWarnings,
53
+ distributedEnabled,
54
+ });
55
+ }
56
+
57
+ export function createRouteOutputCacheComposerWithOptions(
58
+ options: CreateRouteOutputCacheComposerOptions,
59
+ ): RouteOutputCacheComposer {
60
+ const { policies, globalEnabled, cacheToken, securityWarnings, distributedEnabled = false } = options;
61
+
62
+ return (controllerClass: Class<object>, methodName: string | symbol, _httpMeta: ControllerMeta, _action: ActionMeta) => {
63
+ if (!globalEnabled) {
64
+ return [];
65
+ }
66
+
67
+ const middlewares = [];
68
+
69
+ const redisMeta = outputRedisCacheMetaOf(controllerClass);
70
+ const redisRequirement = resolveOutputRedisCacheRequirement(redisMeta, methodName);
71
+ const memoryMeta = outputCacheMetaOf(controllerClass);
72
+ const memoryRequirement = resolveOutputCacheRequirement(memoryMeta, methodName);
73
+
74
+ if (redisRequirement !== undefined && memoryRequirement !== undefined) {
75
+ console.warn(
76
+ `[cache] ${controllerClass.name}.${String(methodName)} has both @OutputCache and @OutputRedisCache — `
77
+ + "using @OutputRedisCache only.",
78
+ );
79
+ }
80
+
81
+ if (redisRequirement !== undefined) {
82
+ if (!distributedEnabled) {
83
+ throw new Error(
84
+ `@OutputRedisCache on ${controllerClass.name}.${String(methodName)} requires a distributed backend (add redisConnect(redisConfig, { cache: "distributed" }) to your @Infra manifest)`,
85
+ );
86
+ }
87
+
88
+ const resolved = resolveOutputRedisCacheOptions(redisRequirement, policies);
89
+ if (resolved.enabled !== false && resolved.noStore !== true) {
90
+ const seconds = requireCacheSeconds(
91
+ resolved,
92
+ `@OutputRedisCache on ${controllerClass.name}.${String(methodName)}`,
93
+ );
94
+ const config: ResolvedOutputRedisCacheOptions = { ...resolved, seconds };
95
+ guardInsecureOutputCacheRoute(controllerClass, methodName, config, securityWarnings);
96
+ middlewares.push(
97
+ outputRedisCacheMiddleware({
98
+ routeName: `${controllerClass.name}.${String(methodName)}`,
99
+ config,
100
+ }),
101
+ );
102
+ }
103
+ return middlewares;
104
+ }
105
+
106
+ if (memoryRequirement === undefined) {
107
+ return [];
108
+ }
109
+
110
+ const resolved = resolveCacheOptions(memoryRequirement, policies);
111
+ if (resolved.enabled === false || resolved.noStore === true) {
112
+ return [];
113
+ }
114
+
115
+ const seconds = requireCacheSeconds(resolved, `@OutputCache on ${controllerClass.name}.${String(methodName)}`);
116
+ const config: ResolvedOutputCacheOptions = { ...resolved, seconds };
117
+
118
+ guardInsecureOutputCacheRoute(controllerClass, methodName, config, securityWarnings);
119
+
120
+ middlewares.push(
121
+ outputCacheMiddleware({
122
+ cacheToken,
123
+ routeName: `${controllerClass.name}.${String(methodName)}`,
124
+ config,
125
+ }),
126
+ );
127
+
128
+ return middlewares;
129
+ };
130
+ }
@@ -0,0 +1,111 @@
1
+ import type { HttpMiddleware } from "../../http";
2
+ import type { InjectionToken } from "../../di";
3
+ import { ICache } from "../ICache";
4
+ import {
5
+ DEFAULT_OUTPUT_CACHE_METHODS,
6
+ DEFAULT_OUTPUT_CACHE_STATUS_CODES,
7
+ type ResolvedOutputCacheOptions,
8
+ } from "../internal/resolveCachePolicy";
9
+ import { canBuildPersonalizedOutputCacheKey, OUTPUT_CACHE_PRINCIPAL_STATE_KEY } from "./buildOutputCacheKey";
10
+ import { buildOutputCacheKey } from "./buildOutputCacheKey";
11
+ import { applyClientCacheHeaders, resolveOutputClientCacheOptions } from "./applyClientCacheHeaders";
12
+ import {
13
+ cachedPayloadToResponse,
14
+ resolveCachedHttpPayloadReadOptions,
15
+ responseAllowsServerCache,
16
+ responseToCachedPayload,
17
+ type CachedHttpPayload,
18
+ } from "./CachedHttpPayload";
19
+
20
+ export interface OutputCacheMiddlewareOptions {
21
+ readonly cacheToken?: InjectionToken<ICache<CachedHttpPayload>>;
22
+ readonly routeName: string;
23
+ readonly config: ResolvedOutputCacheOptions;
24
+ }
25
+
26
+ function isAuthenticated(ctx: Parameters<HttpMiddleware>[0]): boolean {
27
+ return ctx.state.get(OUTPUT_CACHE_PRINCIPAL_STATE_KEY) !== undefined;
28
+ }
29
+
30
+ function allowsMethod(method: string, allowed: readonly string[] | undefined): boolean {
31
+ const methods = allowed ?? DEFAULT_OUTPUT_CACHE_METHODS;
32
+ return methods.includes(method.toUpperCase());
33
+ }
34
+
35
+ function allowsStatus(status: number, allowed: readonly number[] | undefined): boolean {
36
+ const codes = allowed ?? DEFAULT_OUTPUT_CACHE_STATUS_CODES;
37
+ return codes.includes(status);
38
+ }
39
+
40
+ /** Per-route output cache middleware compiled from `@OutputCache` metadata. */
41
+ export function outputCacheMiddleware(options: OutputCacheMiddlewareOptions): HttpMiddleware {
42
+ const { routeName, config } = options;
43
+ const cacheToken = options.cacheToken ?? (ICache as InjectionToken<ICache<CachedHttpPayload>>);
44
+ const payloadReadOptions = resolveCachedHttpPayloadReadOptions(config);
45
+ const clientCache = resolveOutputClientCacheOptions(config);
46
+
47
+ return async (ctx, next) => {
48
+ const cache = ctx.services.resolve(cacheToken);
49
+ if (config.enabled === false || config.noStore === true) {
50
+ await next();
51
+ return;
52
+ }
53
+
54
+ if (!allowsMethod(ctx.method, config.methods)) {
55
+ await next();
56
+ return;
57
+ }
58
+
59
+ if (config.unlessAuthenticated === true && isAuthenticated(ctx)) {
60
+ await next();
61
+ // Never add a public/shared-cache directive to a personalized response.
62
+ // The action may still provide its own deliberate Cache-Control policy.
63
+ return;
64
+ }
65
+
66
+ if (!canBuildPersonalizedOutputCacheKey(ctx, config)) {
67
+ await next();
68
+ return;
69
+ }
70
+
71
+ if (config.when !== undefined) {
72
+ const allowed = await config.when(ctx);
73
+ if (!allowed) {
74
+ await next();
75
+ return;
76
+ }
77
+ }
78
+
79
+ const key = buildOutputCacheKey(ctx, routeName, config);
80
+ const cached = cache.get(key);
81
+ if (cached !== undefined) {
82
+ ctx.response = applyClientCacheHeaders(cachedPayloadToResponse(cached), clientCache);
83
+ return;
84
+ }
85
+
86
+ const setOptions = { ttlSeconds: config.seconds, tags: config.tags };
87
+ const payload = await cache.getOrCreateAsync(key, async () => {
88
+ await next();
89
+ if (
90
+ ctx.response === undefined
91
+ || !allowsStatus(ctx.response.status, config.statusCodes)
92
+ || !responseAllowsServerCache(ctx.response, config.varyByHeader)
93
+ ) {
94
+ return undefined;
95
+ }
96
+ return responseToCachedPayload(ctx.response, payloadReadOptions);
97
+ }, setOptions);
98
+
99
+ if (payload !== undefined) {
100
+ ctx.response = applyClientCacheHeaders(cachedPayloadToResponse(payload), clientCache);
101
+ return;
102
+ }
103
+
104
+ // Anti-stampede may have merged our request with another one whose response turned out
105
+ // uncacheable: then the factory ran on the other context and our `ctx.response`
106
+ // is empty, so run the pipeline ourselves to return a complete response.
107
+ if (ctx.response === undefined) {
108
+ await next();
109
+ }
110
+ };
111
+ }