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,403 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { CacheValueError } from "../errors/CacheError";
3
+ import { assertCacheKey, measureStringBytes } from "../internal/CacheKeyGuard";
4
+ import type { CacheCodec } from "./CacheCodec";
5
+ import type {
6
+ DistributedCacheDriver,
7
+ DistributedCacheTagWrite,
8
+ DistributedCacheVersionCheck,
9
+ } from "./DistributedCacheDriver";
10
+ import type { DistributedCacheSetOptions, IDistributedCache } from "./IDistributedCache";
11
+
12
+ const DEFAULT_DISTRIBUTED_MAX_KEY_LENGTH = 512;
13
+ const DEFAULT_POLL_INTERVAL_MS = 50;
14
+ const ENVELOPE_MARKER = "bazis-cache-v1";
15
+
16
+ interface StoredEnvelope {
17
+ readonly token: string;
18
+ readonly payload: string;
19
+ }
20
+
21
+ interface DistributedInFlightEntry {
22
+ readonly promise: Promise<unknown>;
23
+ readonly tags: readonly string[];
24
+ }
25
+
26
+ export interface DistributedCacheConfig {
27
+ readonly connectionName: string;
28
+ /** Connection-level prefix prepended to every key. */
29
+ readonly keyPrefix: string;
30
+ /** Extra namespace appended after `keyPrefix` (e.g. `svc:` for service cache). */
31
+ readonly namespace?: string;
32
+ /** Default anti-stampede lock TTL (seconds). */
33
+ readonly defaultLockSeconds: number;
34
+ readonly maxKeyLength?: number;
35
+ /** Max serialized value size (bytes); `set` degrades gracefully past it. */
36
+ readonly maxValueBytes?: number;
37
+ /** Poll interval (ms) while waiting for a peer worker to fill the cache. */
38
+ readonly pollIntervalMs?: number;
39
+ }
40
+
41
+ /**
42
+ * Backend-agnostic distributed cache. Owns all caching policy and delegates raw
43
+ * I/O to a {@link DistributedCacheDriver}. One instance per (connection × namespace).
44
+ *
45
+ * - cross-process anti-stampede: fencing-token lock + in-process in-flight dedup;
46
+ * - tag index for group invalidation;
47
+ * - serialized value-size guard.
48
+ */
49
+ export class DistributedCache<TValue = unknown> implements IDistributedCache<TValue> {
50
+ private readonly inFlight = new Map<string, DistributedInFlightEntry>();
51
+ private readonly config: Readonly<DistributedCacheConfig>;
52
+ private readonly fullPrefix: string;
53
+ private readonly maxKeyLength: number;
54
+ private readonly pollIntervalMs: number;
55
+
56
+ public readonly connectionName: string;
57
+ public readonly keyPrefix: string;
58
+
59
+ public constructor(
60
+ private readonly driver: DistributedCacheDriver,
61
+ private readonly codec: CacheCodec<TValue>,
62
+ config: DistributedCacheConfig,
63
+ ) {
64
+ const snapshot = Object.freeze({ ...config });
65
+ if (!Number.isFinite(snapshot.defaultLockSeconds) || snapshot.defaultLockSeconds <= 0) {
66
+ throw new CacheValueError("defaultLockSeconds must be a positive number");
67
+ }
68
+ if (snapshot.maxKeyLength !== undefined && (!Number.isInteger(snapshot.maxKeyLength) || snapshot.maxKeyLength <= 0)) {
69
+ throw new CacheValueError("maxKeyLength must be a positive integer");
70
+ }
71
+ if (snapshot.maxValueBytes !== undefined && (!Number.isInteger(snapshot.maxValueBytes) || snapshot.maxValueBytes <= 0)) {
72
+ throw new CacheValueError("maxValueBytes must be a positive integer");
73
+ }
74
+ if (snapshot.pollIntervalMs !== undefined && (!Number.isFinite(snapshot.pollIntervalMs) || snapshot.pollIntervalMs <= 0)) {
75
+ throw new CacheValueError("pollIntervalMs must be a positive number");
76
+ }
77
+ this.config = snapshot;
78
+ this.connectionName = snapshot.connectionName;
79
+ this.keyPrefix = snapshot.keyPrefix;
80
+ this.fullPrefix = `${snapshot.keyPrefix}${snapshot.namespace ?? ""}`;
81
+ this.maxKeyLength = snapshot.maxKeyLength ?? DEFAULT_DISTRIBUTED_MAX_KEY_LENGTH;
82
+ this.pollIntervalMs = snapshot.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
83
+ }
84
+
85
+ public async get<T = TValue>(key: string): Promise<T | undefined> {
86
+ assertCacheKey(key, this.maxKeyLength);
87
+ const storageKey = this.storageKey(key);
88
+ const raw = await this.driver.read(storageKey);
89
+ if (raw === null) {
90
+ return undefined;
91
+ }
92
+ try {
93
+ return this.codec.deserialize(decodeEnvelope(raw).payload) as unknown as T;
94
+ } catch {
95
+ try {
96
+ await this.driver.deleteIfValue(storageKey, raw);
97
+ } catch {
98
+ // A corrupt cache entry is still a miss when backend cleanup fails.
99
+ // Do not turn best-effort cache maintenance into a request failure.
100
+ }
101
+ return undefined;
102
+ }
103
+ }
104
+
105
+ public async getOrCreateAsync<T = TValue>(
106
+ key: string,
107
+ factory: () => Promise<T | undefined>,
108
+ options: DistributedCacheSetOptions,
109
+ ): Promise<T | undefined> {
110
+ const normalizedOptions = this.normalizeSetOptions(options);
111
+ const hit = await this.get<T>(key);
112
+ if (hit !== undefined) {
113
+ return hit;
114
+ }
115
+
116
+ const existing = this.inFlight.get(key);
117
+ if (existing !== undefined) {
118
+ return existing.promise as Promise<T | undefined>;
119
+ }
120
+
121
+ const pending = this.runLockedFactory<T>(key, factory, normalizedOptions);
122
+ this.inFlight.set(key, { promise: pending, tags: normalizedOptions.tags ?? Object.freeze([]) });
123
+ try {
124
+ return await pending;
125
+ } finally {
126
+ if (this.inFlight.get(key)?.promise === pending) {
127
+ this.inFlight.delete(key);
128
+ }
129
+ }
130
+ }
131
+
132
+ public async remove(key: string): Promise<boolean> {
133
+ assertCacheKey(key, this.maxKeyLength);
134
+ this.inFlight.delete(key);
135
+ return this.driver.invalidate(this.entryVersionKey(key), this.storageKey(key), this.lockKeyFor(key));
136
+ }
137
+
138
+ public async evictByTag(tag: string): Promise<number> {
139
+ assertCacheKey(tag, this.maxKeyLength);
140
+ for (const [key, entry] of this.inFlight) {
141
+ if (entry.tags.includes(tag)) this.inFlight.delete(key);
142
+ }
143
+ const versionKey = this.tagVersionKey(tag);
144
+ const pendingKey = this.tagPendingKey(tag);
145
+ await this.driver.advanceTagGeneration(versionKey, pendingKey);
146
+ const generations = [...await this.driver.tagMembers(pendingKey)]
147
+ .filter((value) => /^\d+$/.test(value))
148
+ .sort((left, right) => Number(left) - Number(right));
149
+ let removed = 0;
150
+ for (const generation of generations) {
151
+ removed += await this.evictTagGeneration(tag, generation, pendingKey);
152
+ }
153
+ return removed;
154
+ }
155
+
156
+ private async evictTagGeneration(tag: string, generation: string, pendingKey: string): Promise<number> {
157
+ const tagKey = this.tagKey(tag, generation);
158
+ const members = await this.driver.tagMembers(tagKey);
159
+ let removed = 0;
160
+ for (const member of members) {
161
+ const parsed = decodeTagMember(member);
162
+ if (parsed === undefined) {
163
+ continue;
164
+ }
165
+ if (!parsed.storageKey.startsWith(`${this.fullPrefix}entry:`)) {
166
+ continue;
167
+ }
168
+ const raw = await this.driver.read(parsed.storageKey);
169
+ if (raw === null) {
170
+ continue;
171
+ }
172
+ if (decodeEnvelope(raw).token !== parsed.token) {
173
+ continue;
174
+ }
175
+ if (await this.driver.deleteIfValue(parsed.storageKey, raw)) {
176
+ removed += 1;
177
+ }
178
+ }
179
+ await this.driver.delete([tagKey]);
180
+ await this.driver.removeTagMembers(pendingKey, [generation]);
181
+ return removed;
182
+ }
183
+
184
+ private storageKey(key: string): string {
185
+ return `${this.fullPrefix}entry:${digest(key)}`;
186
+ }
187
+
188
+ private entryVersionKey(key: string): string {
189
+ return `${this.fullPrefix}version:entry:${digest(key)}`;
190
+ }
191
+
192
+ private tagVersionKey(tag: string): string {
193
+ return `${this.fullPrefix}version:tag:${digest(tag)}`;
194
+ }
195
+
196
+ private tagPendingKey(tag: string): string {
197
+ return `${this.fullPrefix}pending:tag:${digest(tag)}`;
198
+ }
199
+
200
+ private tagKey(tag: string, generation: string): string {
201
+ return `${this.fullPrefix}tag:${digest(tag)}:${generation}`;
202
+ }
203
+
204
+ private lockKeyFor(key: string): string {
205
+ return `${this.fullPrefix}lock:${digest(key)}`;
206
+ }
207
+
208
+ private async runLockedFactory<T>(
209
+ key: string,
210
+ factory: () => Promise<T | undefined>,
211
+ options: DistributedCacheSetOptions,
212
+ contentionAttempt = 0,
213
+ ): Promise<T | undefined> {
214
+ const lockKey = this.lockKeyFor(key);
215
+ const lockTtl = Math.max(1, Math.floor(options.lockSeconds ?? this.config.defaultLockSeconds));
216
+ const token = randomUUID();
217
+
218
+ const acquired = await this.driver.acquireLock(lockKey, token, lockTtl);
219
+ if (!acquired) {
220
+ const peerValue = await this.waitForPeerResult<T>(key, lockKey, lockTtl);
221
+ if (peerValue !== undefined) return peerValue;
222
+ if (contentionAttempt < 2) {
223
+ return this.runLockedFactory(key, factory, options, contentionAttempt + 1);
224
+ }
225
+ // Availability fallback: another owner repeatedly failed to publish. The
226
+ // caller still receives its service result, but we do not write unlocked.
227
+ return factory();
228
+ }
229
+
230
+ try {
231
+ const again = await this.get<T>(key);
232
+ if (again !== undefined) {
233
+ return again;
234
+ }
235
+ const versionChecks = await this.captureVersions(key, options.tags);
236
+ const value = await factory();
237
+ if (value !== undefined) {
238
+ await this.trySet(key, value, options, lockKey, token, versionChecks);
239
+ }
240
+ return value;
241
+ } finally {
242
+ try {
243
+ // Unlock is best effort: a transport error must not replace a fresh
244
+ // service result or the factory's original exception. The lock still
245
+ // has a bounded TTL, so another worker eventually makes progress.
246
+ await this.driver.releaseLock(lockKey, token, this.entryVersionKey(key));
247
+ } catch {
248
+ // Intentionally preserve the primary outcome.
249
+ }
250
+ }
251
+ }
252
+
253
+ private async waitForPeerResult<T>(key: string, lockKey: string, lockTtl: number): Promise<T | undefined> {
254
+ const deadline = Date.now() + lockTtl * 1000;
255
+ while (Date.now() < deadline) {
256
+ const hit = await this.get<T>(key);
257
+ if (hit !== undefined) {
258
+ return hit;
259
+ }
260
+ if (await this.driver.read(lockKey) === null) {
261
+ break;
262
+ }
263
+ await Bun.sleep(this.pollIntervalMs);
264
+ }
265
+ return undefined;
266
+ }
267
+
268
+ private async trySet<T>(
269
+ key: string,
270
+ value: T,
271
+ options: DistributedCacheSetOptions,
272
+ lockKey: string,
273
+ lockToken: string,
274
+ versionChecks: readonly DistributedCacheVersionCheck[],
275
+ ): Promise<void> {
276
+ try {
277
+ await this.write(key, value as unknown as TValue, options, lockKey, lockToken, versionChecks);
278
+ } catch {
279
+ // Graceful degradation — the caller still receives the fresh value.
280
+ }
281
+ }
282
+
283
+ private async write(
284
+ key: string,
285
+ value: TValue,
286
+ options: DistributedCacheSetOptions,
287
+ lockKey: string,
288
+ lockToken: string,
289
+ versionChecks: readonly DistributedCacheVersionCheck[],
290
+ ): Promise<void> {
291
+ assertCacheKey(key, this.maxKeyLength);
292
+ const serialized = this.codec.serialize(value);
293
+
294
+ if (this.config.maxValueBytes !== undefined) {
295
+ const bytes = measureStringBytes(serialized);
296
+ if (bytes > this.config.maxValueBytes) {
297
+ throw new CacheValueError(`Cache value exceeds max size of ${this.config.maxValueBytes} bytes`);
298
+ }
299
+ }
300
+
301
+ const ttl = options.ttlSeconds !== undefined ? Math.max(1, Math.floor(options.ttlSeconds)) : undefined;
302
+ const storageKey = this.storageKey(key);
303
+ const tags: DistributedCacheTagWrite[] = [];
304
+ for (const tag of options.tags ?? []) {
305
+ const version = versionChecks.find((check) => check.key === this.tagVersionKey(tag))?.expected ?? null;
306
+ tags.push({
307
+ key: this.tagKey(tag, version ?? "0"),
308
+ member: "",
309
+ });
310
+ }
311
+ // Deterministic for one key + tag-generation set: repeated remove/recreate
312
+ // does not grow Redis sets, while changing tags/generation fences stale
313
+ // memberships. Keep the v1 three-item envelope/two-item member wire format
314
+ // so rolling upgrades remain readable by older runtimes.
315
+ const writeToken = digest(JSON.stringify([storageKey, tags.map((tag) => tag.key).sort()]));
316
+ for (let index = 0; index < tags.length; index += 1) {
317
+ const tag = tags[index] as DistributedCacheTagWrite;
318
+ tags[index] = { key: tag.key, member: encodeTagMember(storageKey, writeToken) };
319
+ }
320
+ const envelope = encodeEnvelope(writeToken, serialized);
321
+ await this.driver.writeIfLockOwner({
322
+ lockKey,
323
+ lockToken,
324
+ key: storageKey,
325
+ value: envelope,
326
+ ttlSeconds: ttl,
327
+ versionChecks,
328
+ tags,
329
+ });
330
+ }
331
+
332
+ private async captureVersions(
333
+ key: string,
334
+ tags: readonly string[] | undefined,
335
+ ): Promise<readonly DistributedCacheVersionCheck[]> {
336
+ const keys = [this.entryVersionKey(key), ...(tags ?? []).map((tag) => this.tagVersionKey(tag))];
337
+ return Promise.all(keys.map(async (versionKey) => ({
338
+ key: versionKey,
339
+ expected: await this.driver.read(versionKey),
340
+ })));
341
+ }
342
+
343
+ private normalizeSetOptions(options: DistributedCacheSetOptions): DistributedCacheSetOptions {
344
+ if (options.ttlSeconds !== undefined && (!Number.isFinite(options.ttlSeconds) || options.ttlSeconds <= 0)) {
345
+ throw new CacheValueError("ttlSeconds must be a positive number");
346
+ }
347
+ if (options.lockSeconds !== undefined && (!Number.isFinite(options.lockSeconds) || options.lockSeconds <= 0)) {
348
+ throw new CacheValueError("lockSeconds must be a positive number");
349
+ }
350
+ for (const tag of options.tags ?? []) {
351
+ assertCacheKey(tag, this.maxKeyLength);
352
+ }
353
+ return Object.freeze({
354
+ ...(options.ttlSeconds !== undefined ? { ttlSeconds: options.ttlSeconds } : {}),
355
+ ...(options.lockSeconds !== undefined ? { lockSeconds: options.lockSeconds } : {}),
356
+ ...(options.tags !== undefined ? { tags: Object.freeze([...options.tags]) } : {}),
357
+ });
358
+ }
359
+ }
360
+
361
+ function digest(value: string): string {
362
+ return createHash("sha256").update(value).digest("hex");
363
+ }
364
+
365
+ function encodeEnvelope(token: string, payload: string): string {
366
+ return JSON.stringify([ENVELOPE_MARKER, token, payload]);
367
+ }
368
+
369
+ function decodeEnvelope(raw: string): StoredEnvelope {
370
+ try {
371
+ const parsed = JSON.parse(raw) as unknown;
372
+ if (
373
+ Array.isArray(parsed)
374
+ && parsed.length === 3
375
+ && parsed[0] === ENVELOPE_MARKER
376
+ && typeof parsed[1] === "string"
377
+ && typeof parsed[2] === "string"
378
+ ) {
379
+ return { token: parsed[1], payload: parsed[2] };
380
+ }
381
+ } catch {
382
+ // Older cache entries stored the codec payload directly. Keep them readable
383
+ // during a rolling upgrade; they simply do not participate in new tag sets.
384
+ }
385
+ return { token: "", payload: raw };
386
+ }
387
+
388
+ function encodeTagMember(storageKey: string, token: string): string {
389
+ return JSON.stringify([storageKey, token]);
390
+ }
391
+
392
+ function decodeTagMember(member: string): { readonly storageKey: string; readonly token: string } | undefined {
393
+ try {
394
+ const parsed = JSON.parse(member) as unknown;
395
+ if (Array.isArray(parsed) && parsed.length === 2 && typeof parsed[0] === "string" && typeof parsed[1] === "string") {
396
+ return { storageKey: parsed[0], token: parsed[1] };
397
+ }
398
+ } catch {
399
+ // Ignore malformed/legacy tag members instead of treating their content as
400
+ // a backend key.
401
+ }
402
+ return undefined;
403
+ }
@@ -0,0 +1,83 @@
1
+ /** Version value captured before a cache factory starts. */
2
+ export interface DistributedCacheVersionCheck {
3
+ readonly key: string;
4
+ readonly expected: string | null;
5
+ }
6
+
7
+ /** Tag-set membership written together with a cache value. */
8
+ export interface DistributedCacheTagWrite {
9
+ readonly key: string;
10
+ readonly member: string;
11
+ }
12
+
13
+ /**
14
+ * Atomic, fenced write requested by {@link DistributedCache}.
15
+ *
16
+ * A backend must write neither the value nor its tag memberships unless the
17
+ * caller still owns the lock and every invalidation version is unchanged.
18
+ */
19
+ export interface DistributedCacheFencedWrite {
20
+ readonly lockKey: string;
21
+ readonly lockToken: string;
22
+ readonly key: string;
23
+ readonly value: string;
24
+ readonly ttlSeconds?: number;
25
+ readonly versionChecks: readonly DistributedCacheVersionCheck[];
26
+ readonly tags: readonly DistributedCacheTagWrite[];
27
+ }
28
+
29
+ /**
30
+ * Low-level primitives a distributed backend must provide. All caching policy
31
+ * (anti-stampede, fencing, tag generations, size limits and namespacing) lives
32
+ * in {@link DistributedCache}; a backend only maps these calls to its protocol.
33
+ */
34
+ export interface DistributedCacheDriver {
35
+ /** Reads a raw string value; `null` when the key is absent. */
36
+ read(key: string): Promise<string | null>;
37
+
38
+ /** Writes a raw value with an optional TTL (seconds). */
39
+ write(key: string, value: string, ttlSeconds?: number): Promise<void>;
40
+
41
+ /** Deletes keys; returns the number actually removed. */
42
+ delete(keys: readonly string[]): Promise<number>;
43
+
44
+ /**
45
+ * Acquires a lock by setting `lockKey` to `token` only if absent (`SET NX EX`).
46
+ * Returns `true` when acquired.
47
+ */
48
+ acquireLock(lockKey: string, token: string, ttlSeconds: number): Promise<boolean>;
49
+
50
+ /**
51
+ * Releases a lock **only if** it still holds `token` (fencing). Prevents a slow
52
+ * worker from deleting a lock another worker already re-acquired.
53
+ */
54
+ releaseLock(lockKey: string, token: string, entryVersionKey?: string): Promise<void>;
55
+
56
+ /** Atomically performs a value/tag write while the lock and versions match. */
57
+ writeIfLockOwner(request: DistributedCacheFencedWrite): Promise<boolean>;
58
+
59
+ /** Atomically deletes a key only when its complete raw value still matches. */
60
+ deleteIfValue(key: string, expectedValue: string): Promise<boolean>;
61
+
62
+ /** Atomically increments an invalidation generation and returns its new value. */
63
+ increment(key: string): Promise<number>;
64
+
65
+ /** Advances a tag generation and records the previous one for recoverable cleanup. */
66
+ advanceTagGeneration(versionKey: string, pendingKey: string): Promise<number>;
67
+
68
+ /**
69
+ * Atomically deletes the current cache value and, when `lockKey` is present,
70
+ * advances the generation that fences its owner. Implementations may omit a
71
+ * tombstone when no writer lock exists.
72
+ */
73
+ invalidate(versionKey: string, valueKey: string, lockKey?: string): Promise<boolean>;
74
+
75
+ /** Adds members to a tag set, refreshing the set TTL to avoid unbounded growth. */
76
+ addTagMembers(tagKey: string, members: readonly string[], ttlSeconds?: number): Promise<void>;
77
+
78
+ /** Removes members from a set. */
79
+ removeTagMembers(tagKey: string, members: readonly string[]): Promise<void>;
80
+
81
+ /** Returns the members of a tag set. */
82
+ tagMembers(tagKey: string): Promise<readonly string[]>;
83
+ }
@@ -0,0 +1,28 @@
1
+ import type { CachedHttpPayload } from "../http/CachedHttpPayload";
2
+ import type { IDistributedCache } from "./IDistributedCache";
3
+ import type { NamedCacheRegistry } from "./NamedCacheRegistry";
4
+
5
+ /** Per-connection health probe result. */
6
+ export interface DistributedCachePing {
7
+ readonly connection: string;
8
+ readonly healthy: boolean;
9
+ readonly detail?: string;
10
+ }
11
+
12
+ /**
13
+ * Stores and health supplied by one distributed backend. Resource ownership is
14
+ * separate: the Infra connector manages the connection.
15
+ */
16
+ export interface DistributedCacheStores {
17
+ /** Configured connection names. */
18
+ readonly connectionNames: readonly string[];
19
+
20
+ /** HTTP output payload stores per connection. */
21
+ readonly outputCache: NamedCacheRegistry<IDistributedCache<CachedHttpPayload>>;
22
+
23
+ /** Service value stores per connection. */
24
+ readonly serviceCache: NamedCacheRegistry<IDistributedCache>;
25
+
26
+ /** Liveness probe for each connection. */
27
+ ping(): Promise<readonly DistributedCachePing[]>;
28
+ }
@@ -0,0 +1,40 @@
1
+ import type { CacheSetOptions } from "../types/CacheOptions";
2
+
3
+ /** Default named connection used when a decorator omits `connection`. */
4
+ export const DEFAULT_CACHE_CONNECTION = "default";
5
+
6
+ /** Per-entry options for distributed stores (adds distributed lock TTL). */
7
+ export interface DistributedCacheSetOptions extends CacheSetOptions {
8
+ /** TTL of the anti-stampede lock acquired on a miss. */
9
+ readonly lockSeconds?: number;
10
+ }
11
+
12
+ /**
13
+ * Backend-agnostic distributed cache contract (analogous to .NET `IDistributedCache`).
14
+ *
15
+ * The framework depends only on this interface; concrete backends (Redis, …) live in
16
+ * `@/core/infra/*` and are plugged in via {@link DistributedCacheStores}.
17
+ */
18
+ export interface IDistributedCache<TValue = unknown> {
19
+ readonly connectionName: string;
20
+ readonly keyPrefix: string;
21
+
22
+ /** Reads a value; `undefined` when absent or unreadable. */
23
+ get<T = TValue>(key: string): Promise<T | undefined>;
24
+
25
+ /**
26
+ * Returns a cached value or produces it via `factory` with cross-process
27
+ * anti-stampede (distributed lock + in-process dedup). `undefined` is not stored.
28
+ */
29
+ getOrCreateAsync<T = TValue>(
30
+ key: string,
31
+ factory: () => Promise<T | undefined>,
32
+ options: DistributedCacheSetOptions,
33
+ ): Promise<T | undefined>;
34
+
35
+ /** Removes one entry; `true` when it existed. */
36
+ remove(key: string): Promise<boolean>;
37
+
38
+ /** Removes all entries carrying `tag`; returns the number removed. */
39
+ evictByTag(tag: string): Promise<number>;
40
+ }
@@ -0,0 +1,23 @@
1
+ import { CacheError } from "../errors/CacheError";
2
+ import { DEFAULT_CACHE_CONNECTION } from "./IDistributedCache";
3
+
4
+ /** Resolves named distributed-cache connections registered by a backend. */
5
+ export class NamedCacheRegistry<TCache> {
6
+ public constructor(
7
+ private readonly entries: ReadonlyMap<string, TCache>,
8
+ private readonly kind = "distributed cache",
9
+ ) {}
10
+
11
+ public resolve(name: string = DEFAULT_CACHE_CONNECTION): TCache {
12
+ const found = this.entries.get(name);
13
+ if (found === undefined) {
14
+ const available = [...this.entries.keys()].join(", ") || "(none)";
15
+ throw new CacheError(`Unknown ${this.kind} connection "${name}". Available: ${available}`);
16
+ }
17
+ return found;
18
+ }
19
+
20
+ public get connectionNames(): readonly string[] {
21
+ return [...this.entries.keys()];
22
+ }
23
+ }
@@ -0,0 +1,32 @@
1
+ import type { CachedHttpPayload } from "../http/CachedHttpPayload";
2
+ import type { CacheCodec } from "./CacheCodec";
3
+
4
+ /** JSON codec for arbitrary service values (`@Cacheable` distributed tier). */
5
+ export const jsonCacheCodec: CacheCodec<unknown> = {
6
+ serialize: (value) => JSON.stringify(value),
7
+ deserialize: (raw) => JSON.parse(raw) as unknown,
8
+ };
9
+
10
+ interface SerializedHttpPayload {
11
+ readonly status: number;
12
+ readonly headers: Readonly<Record<string, string>>;
13
+ readonly body: string;
14
+ }
15
+
16
+ /** Codec for HTTP output payloads — body is base64 because JSON cannot hold bytes. */
17
+ export const httpPayloadCacheCodec: CacheCodec<CachedHttpPayload> = {
18
+ serialize: (payload) =>
19
+ JSON.stringify({
20
+ status: payload.status,
21
+ headers: payload.headers,
22
+ body: Buffer.from(payload.body).toString("base64"),
23
+ } satisfies SerializedHttpPayload),
24
+ deserialize: (raw) => {
25
+ const parsed = JSON.parse(raw) as SerializedHttpPayload;
26
+ return {
27
+ status: parsed.status,
28
+ headers: parsed.headers,
29
+ body: Uint8Array.from(Buffer.from(parsed.body, "base64")),
30
+ };
31
+ },
32
+ };
@@ -0,0 +1,19 @@
1
+ export type { CacheCodec } from "./CacheCodec";
2
+ export { jsonCacheCodec, httpPayloadCacheCodec } from "./codecs";
3
+ export {
4
+ DEFAULT_CACHE_CONNECTION,
5
+ type DistributedCacheSetOptions,
6
+ type IDistributedCache,
7
+ } from "./IDistributedCache";
8
+ export type {
9
+ DistributedCacheDriver,
10
+ DistributedCacheFencedWrite,
11
+ DistributedCacheTagWrite,
12
+ DistributedCacheVersionCheck,
13
+ } from "./DistributedCacheDriver";
14
+ export { DistributedCache, type DistributedCacheConfig } from "./DistributedCache";
15
+ export { NamedCacheRegistry } from "./NamedCacheRegistry";
16
+ export type {
17
+ DistributedCacheStores,
18
+ DistributedCachePing,
19
+ } from "./DistributedCacheStores";
@@ -0,0 +1,16 @@
1
+ /** Base error of the cache module. */
2
+ export class CacheError extends Error {
3
+ public constructor(message: string) {
4
+ super(message);
5
+ this.name = new.target.name;
6
+ }
7
+ }
8
+
9
+ /** Invalid cache key (type, length, forbidden name). */
10
+ export class CacheKeyError extends CacheError {}
11
+
12
+ /** Invalid value (size limit exceeded and the like). */
13
+ export class CacheValueError extends CacheError {}
14
+
15
+ /** The limit of unfinished factories is exhausted; the new factory was not started. */
16
+ export class CacheCapacityError extends CacheError {}