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,23 @@
1
+ import { DI, DiContainer, HOSTED_SERVICE, Module, SERVICE_PROVIDER, type BazisModule } from "../di";
2
+ import type { GrpcModuleOptions } from "./contracts";
3
+ import { GrpcServer } from "./GrpcServer";
4
+ import { collectGrpcControllers, GRPC_CONTROLLERS } from "./moduleControllers";
5
+
6
+ /** gRPC counterpart of httpModule: controllers belong to their feature modules. */
7
+ export function grpcModule(options: GrpcModuleOptions = {}): BazisModule {
8
+ const selected = collectGrpcControllers(options.imports ?? [], options.controllers);
9
+ @Module({
10
+ imports: options.imports,
11
+ grpcControllers: options.controllers,
12
+ providers: [DI.singleton(DI.factoryProviderWithResolver(HOSTED_SERVICE, [], (resolver) => {
13
+ const container = resolver.resolve(SERVICE_PROVIDER);
14
+ if (!(container instanceof DiContainer)) throw new TypeError("gRPC requires a module DI container.");
15
+ const controllers = container.getModuleOwnedProviderContributions(GRPC_CONTROLLERS)
16
+ .filter((entry) => selected.has(entry.payload.controller));
17
+ return new GrpcServer(options, container, controllers);
18
+ }))],
19
+ exports: [],
20
+ })
21
+ class GrpcModule {}
22
+ return GrpcModule;
23
+ }
@@ -0,0 +1,35 @@
1
+ import { createGeneratedProviderAttachmentChannel, getGeneratedProviderAttachment } from "../di/module/generatedProviderAttachments";
2
+ import type { RequestModelClass } from "../http/Binding/requestModelRegistry";
3
+ import type { Class } from "../di";
4
+
5
+ interface RequestBinding {
6
+ readonly model: RequestModelClass;
7
+ readonly requestStream: boolean;
8
+ }
9
+ type RequestBindings = Readonly<Record<string, RequestBinding>>;
10
+
11
+ /** @internal Exact-class metadata; committed/rolled back with the generated target. */
12
+ export const GRPC_REQUEST_BINDINGS = createGeneratedProviderAttachmentChannel<RequestBindings>({
13
+ description: "gRPC request DTOs",
14
+ normalize(target, value) {
15
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError("Invalid gRPC DTO bindings.");
16
+ const snapshot: Record<string, RequestBinding> = Object.create(null);
17
+ for (const [name, binding] of Object.entries(value)) {
18
+ if (!binding || typeof binding.model !== "function" || !binding.model.prototype
19
+ || typeof binding.requestStream !== "boolean" || typeof target.prototype[name] !== "function") {
20
+ throw new TypeError(`Invalid gRPC DTO binding: ${target.name}.${name}.`);
21
+ }
22
+ snapshot[name] = Object.freeze({ model: binding.model, requestStream: binding.requestStream });
23
+ }
24
+ return Object.freeze(snapshot);
25
+ },
26
+ equals(left, right) {
27
+ return Object.keys(left).length === Object.keys(right).length
28
+ && Object.keys(left).every((key) => left[key]!.model === right[key]?.model
29
+ && left[key]!.requestStream === right[key]?.requestStream);
30
+ },
31
+ });
32
+
33
+ export function getGrpcRequestBinding(controller: Class<object>, name: string | symbol): RequestBinding | undefined {
34
+ return typeof name === "string" ? getGeneratedProviderAttachment(GRPC_REQUEST_BINDINGS, controller)?.[name] : undefined;
35
+ }
@@ -0,0 +1,73 @@
1
+ import type { ModuleOwnedProviderActivation, ServiceProvider } from "../di";
2
+ import type { GrpcContext } from "./contracts";
3
+ import type { MethodDefinition } from "./serviceDefinition";
4
+ import { GrpcTransport } from "./GrpcTransport";
5
+ import { GrpcStatus } from "./GrpcStatus";
6
+ import type { GrpcRequestBinding } from "./GrpcRequest.binding";
7
+
8
+ /** One RPC owns its handler and DI scope, even after the peer disconnects. */
9
+ export class GrpcCall {
10
+ constructor(
11
+ private readonly transport: GrpcTransport,
12
+ private readonly definition: MethodDefinition,
13
+ private readonly methodName: string | symbol,
14
+ private readonly activation: ModuleOwnedProviderActivation<object>,
15
+ private readonly provider: ServiceProvider,
16
+ private readonly binding?: GrpcRequestBinding,
17
+ ) {}
18
+
19
+ cancel(): void { this.transport.cancel(GrpcStatus.UNAVAILABLE, "Server stopped."); }
20
+
21
+ async run(): Promise<void> {
22
+ const scope = this.provider.createScope();
23
+ const context = this.transport.context;
24
+ let result: unknown;
25
+ let failure: unknown;
26
+ let failed = false;
27
+ let inputFailure: unknown;
28
+ let inputFailed = false;
29
+ const bind = (value: unknown): unknown => {
30
+ try { return this.binding?.bind(value) ?? value; }
31
+ catch (error) { inputFailure = error; inputFailed = true; throw error; }
32
+ };
33
+ const requests = this.transport.requests();
34
+ async function* validatedRequests(): AsyncIterable<unknown> {
35
+ for await (const value of requests) yield bind(value);
36
+ }
37
+ try {
38
+ context.signal.throwIfAborted();
39
+ const input = this.definition.requestStream
40
+ ? (this.binding ? validatedRequests() : requests)
41
+ : bind(await this.transport.unaryRequest());
42
+ const instance = await this.activation.activateAsync(scope);
43
+ context.signal.throwIfAborted();
44
+ const handler = Reflect.get(instance, this.methodName) as (input: unknown, context: GrpcContext) => unknown;
45
+ result = await handler.call(instance, input, context);
46
+ if (inputFailed) throw inputFailure;
47
+ if (this.definition.responseStream) {
48
+ if (!result || typeof (result as AsyncIterable<unknown>)[Symbol.asyncIterator] !== "function") {
49
+ throw new TypeError("A response-streaming gRPC handler must return AsyncIterable.");
50
+ }
51
+ for await (const message of result as AsyncIterable<unknown>) {
52
+ if (inputFailed) throw inputFailure;
53
+ context.signal.throwIfAborted();
54
+ await this.transport.write(message);
55
+ }
56
+ }
57
+ if (inputFailed) throw inputFailure;
58
+ context.signal.throwIfAborted();
59
+ } catch (error) {
60
+ failure = error; failed = true;
61
+ } finally {
62
+ // Do not dispose dependencies while non-cooperative handler code is running.
63
+ try { await scope.dispose(); } catch (error) { failure = error; failed = true; }
64
+ }
65
+ if (failed) this.transport.fail(failure);
66
+ else {
67
+ try {
68
+ if (!this.definition.responseStream) await this.transport.write(result);
69
+ this.transport.finish();
70
+ } catch (error) { this.transport.fail(error); }
71
+ }
72
+ }
73
+ }
@@ -0,0 +1,172 @@
1
+ import { connect, type ClientHttp2Session, type SecureClientSessionOptions } from "node:http2";
2
+ import type { GrpcCallOptions, GrpcClientOptions, GrpcResponse, GrpcResponseStream } from "./clientContracts";
3
+ import { GrpcClientCall } from "./GrpcClientCall";
4
+ import { GrpcError } from "./GrpcError";
5
+ import { GrpcStatus } from "./GrpcStatus";
6
+ import { Metadata } from "./Metadata";
7
+ import type { MethodDefinition, ServiceDefinition } from "./serviceDefinition";
8
+
9
+ /** Standard gRPC/HTTP2 client using only built-in networking and Bazis codecs. */
10
+ export class GrpcClient {
11
+ private readonly methods = new Map<string, MethodDefinition | null>();
12
+ private readonly sessions = new Map<ClientHttp2Session, Set<GrpcClientCall<unknown>>>();
13
+ private readonly calls = new Set<GrpcClientCall<unknown>>();
14
+ private readonly address: string;
15
+ private readonly tls: SecureClientSessionOptions;
16
+ private readonly metadata: Metadata;
17
+ private readonly timeout: number;
18
+ private readonly sendLimit: number;
19
+ private readonly receiveLimit: number;
20
+ private readonly callLimit: number;
21
+ private current?: ClientHttp2Session;
22
+ private closed = false;
23
+
24
+ constructor(service: ServiceDefinition, options: GrpcClientOptions) {
25
+ fields(options, ["address", "tls", "metadata", "timeoutMs", "maxSendMessageLength", "maxReceiveMessageLength", "maxConcurrentCalls"], "GrpcClientOptions");
26
+ if (typeof options.address !== "string" || options.address.trim() !== options.address || !options.address) throw new TypeError("gRPC address is required.");
27
+ const hasScheme = options.address.includes("://");
28
+ if (!hasScheme && !/^(?:\[[^\]]+\]|[^:/?#@\s]+):\d+$/.test(options.address)) throw new TypeError("gRPC address must be host:port or an HTTP(S) origin.");
29
+ const url = new URL(hasScheme ? options.address : `http://${options.address}`);
30
+ if (!["http:", "https:"].includes(url.protocol) || !url.hostname || url.username || url.password
31
+ || url.pathname !== "/" || url.search || url.hash || url.port === "0") throw new TypeError("Invalid gRPC address.");
32
+ this.address = url.origin;
33
+ this.tls = { rejectUnauthorized: true, settings: { enablePush: false, maxHeaderListSize: 8192 }, maxHeaderListPairs: 128 };
34
+ if (options.tls !== undefined) {
35
+ fields(options.tls, ["ca", "cert", "key", "servername"], "GrpcClientTlsOptions");
36
+ if (url.protocol !== "https:") throw new TypeError("gRPC TLS options require an https address.");
37
+ for (const key of ["ca", "cert", "key"] as const) {
38
+ const value = options.tls[key];
39
+ if (value !== undefined) {
40
+ if (!(typeof value === "string" && value.length > 0) && !(Buffer.isBuffer(value) && value.length > 0)) throw new TypeError(`Invalid TLS ${key}.`);
41
+ this.tls[key] = Buffer.isBuffer(value) ? Buffer.from(value) : value;
42
+ }
43
+ }
44
+ if ((options.tls.cert === undefined) !== (options.tls.key === undefined)) throw new TypeError("TLS cert and key must be provided together.");
45
+ if (options.tls.servername !== undefined) {
46
+ if (typeof options.tls.servername !== "string" || !options.tls.servername.trim()) throw new TypeError("Invalid TLS servername.");
47
+ this.tls.servername = options.tls.servername;
48
+ }
49
+ }
50
+ this.metadata = metadataCopy(options.metadata);
51
+ this.timeout = integer(options.timeoutMs, 30_000, 1, 2_147_483_647, "timeoutMs");
52
+ this.sendLimit = integer(options.maxSendMessageLength, 4 * 1024 * 1024, 0, 2_147_483_647, "maxSendMessageLength");
53
+ this.receiveLimit = integer(options.maxReceiveMessageLength, 4 * 1024 * 1024, 0, 2_147_483_647, "maxReceiveMessageLength");
54
+ this.callLimit = integer(options.maxConcurrentCalls, 1024, 1, Number.MAX_SAFE_INTEGER, "maxConcurrentCalls");
55
+ if (!service || typeof service !== "object" || !Object.keys(service).length) throw new TypeError("GrpcClient requires a non-empty service definition.");
56
+ const paths = new Set<string>();
57
+ for (const [key, value] of Object.entries(service)) {
58
+ if (!value || typeof value.path !== "string" || !/^\/[^/\s?#]+\/[^/\s?#]+$/.test(value.path)
59
+ || typeof value.requestStream !== "boolean" || typeof value.responseStream !== "boolean"
60
+ || typeof value.requestSerialize !== "function" || typeof value.responseDeserialize !== "function"
61
+ || value.originalName !== undefined && typeof value.originalName !== "string"
62
+ || paths.has(value.path)) throw new TypeError(`Invalid gRPC client method: ${key}.`);
63
+ paths.add(value.path);
64
+ const method = Object.freeze({ ...value });
65
+ for (const alias of new Set([key, value.originalName, value.path.split("/").at(-1)])) {
66
+ if (alias) this.methods.set(alias, this.methods.has(alias) ? null : method);
67
+ }
68
+ }
69
+ }
70
+
71
+ get activeCalls(): number { return this.calls.size; }
72
+
73
+ async unary<Request, Response>(method: string, request: Request, options?: GrpcCallOptions): Promise<GrpcResponse<Response>> {
74
+ return this.single(this.open<Response>(method, [request], false, false, options));
75
+ }
76
+ serverStream<Request, Response>(method: string, request: Request, options?: GrpcCallOptions): GrpcResponseStream<Response> {
77
+ return this.open<Response>(method, [request], false, true, options);
78
+ }
79
+ async clientStream<Request, Response>(method: string, requests: Iterable<Request> | AsyncIterable<Request>, options?: GrpcCallOptions): Promise<GrpcResponse<Response>> {
80
+ return this.single(this.open<Response>(method, requests, true, false, options));
81
+ }
82
+ bidi<Request, Response>(method: string, requests: Iterable<Request> | AsyncIterable<Request>, options?: GrpcCallOptions): GrpcResponseStream<Response> {
83
+ return this.open<Response>(method, requests, true, true, options);
84
+ }
85
+
86
+ close(): void {
87
+ if (this.closed) return;
88
+ this.closed = true;
89
+ for (const call of this.calls) call.cancel();
90
+ for (const session of this.sessions.keys()) session.destroy();
91
+ this.sessions.clear(); this.current = undefined;
92
+ }
93
+ dispose(): void { this.close(); }
94
+
95
+ private async single<T>(call: GrpcClientCall<T>): Promise<GrpcResponse<T>> {
96
+ let data!: T;
97
+ for await (const value of call) data = value;
98
+ return { data, metadata: await call.metadata, trailers: await call.trailers };
99
+ }
100
+
101
+ private open<T>(name: string, requests: Iterable<unknown> | AsyncIterable<unknown>, requestStream: boolean, responseStream: boolean, options: GrpcCallOptions = {}): GrpcClientCall<T> {
102
+ const method = this.methods.get(name);
103
+ if (!method) throw new TypeError(`Unknown or ambiguous gRPC method: ${name}.`);
104
+ if (method.requestStream !== requestStream || method.responseStream !== responseStream) throw new TypeError(`Wrong gRPC call mode: ${name}.`);
105
+ if (!requests || typeof (requests as Iterable<unknown>)[Symbol.iterator] !== "function"
106
+ && typeof (requests as AsyncIterable<unknown>)[Symbol.asyncIterator] !== "function") throw new TypeError("Streaming requests must be Iterable or AsyncIterable.");
107
+ fields(options, ["metadata", "timeoutMs", "deadline", "signal"], "GrpcCallOptions");
108
+ const metadata = this.metadata.clone(); metadata.merge(metadataCopy(options.metadata));
109
+ const timeout = integer(options.timeoutMs, this.timeout, 1, 2_147_483_647, "timeoutMs");
110
+ let deadline = Date.now() + timeout;
111
+ if (options.deadline !== undefined) {
112
+ const explicit = options.deadline instanceof Date ? options.deadline.getTime() : options.deadline;
113
+ if (typeof explicit !== "number" || !Number.isFinite(explicit)) throw new TypeError("Invalid gRPC deadline.");
114
+ deadline = Math.min(deadline, explicit);
115
+ }
116
+ if (options.signal !== undefined && !(options.signal instanceof AbortSignal)) throw new TypeError("Invalid gRPC signal.");
117
+ if (options.signal?.aborted) throw new GrpcError(GrpcStatus.CANCELLED, "RPC cancelled.");
118
+ if (deadline <= Date.now()) throw new GrpcError(GrpcStatus.DEADLINE_EXCEEDED, "Deadline exceeded.");
119
+ if (this.closed) throw new GrpcError(GrpcStatus.CANCELLED, "gRPC client is closed.");
120
+ if (this.calls.size >= this.callLimit) throw new GrpcError(GrpcStatus.RESOURCE_EXHAUSTED, "Too many concurrent gRPC calls.");
121
+ let session: ClientHttp2Session, stream: ReturnType<ClientHttp2Session["request"]>;
122
+ try {
123
+ session = this.session();
124
+ const remaining = Math.max(1, Math.ceil(deadline - Date.now()));
125
+ const wireTimeout = remaining <= 99_999_999 ? `${remaining}m` : `${Math.ceil(remaining / 1000)}S`;
126
+ stream = session.request({ ...metadata.toHttp2Headers(), ":method": "POST", ":path": method.path,
127
+ "content-type": "application/grpc+proto", te: "trailers", "grpc-accept-encoding": "identity", "grpc-timeout": wireTimeout }, { endStream: false });
128
+ } catch { throw new GrpcError(GrpcStatus.UNAVAILABLE, "Unable to open gRPC connection."); }
129
+ const call = new GrpcClientCall<T>(stream, method, { deadline, signal: options.signal, sendLimit: this.sendLimit, receiveLimit: this.receiveLimit }, () => {
130
+ this.calls.delete(call);
131
+ this.sessions.get(session)?.delete(call);
132
+ if (session.connecting && !this.sessions.get(session)?.size) session.destroy();
133
+ });
134
+ this.calls.add(call); this.sessions.get(session)!.add(call);
135
+ call.start(requests);
136
+ return call;
137
+ }
138
+
139
+ private session(): ClientHttp2Session {
140
+ if (this.current && !this.current.closed && !this.current.destroyed) return this.current;
141
+ const session = connect(this.address, this.tls);
142
+ this.current = session;
143
+ const calls = new Set<GrpcClientCall<unknown>>();
144
+ this.sessions.set(session, calls);
145
+ const disconnected = (): void => {
146
+ if (this.current === session) this.current = undefined;
147
+ for (const call of calls) call.connectionClosed();
148
+ };
149
+ session.on("error", disconnected);
150
+ session.on("close", () => { disconnected(); this.sessions.delete(session); });
151
+ session.on("goaway", () => {
152
+ if (this.current === session) this.current = undefined;
153
+ session.close();
154
+ });
155
+ return session;
156
+ }
157
+ }
158
+
159
+ function fields(value: unknown, allowed: readonly string[], label: string): void {
160
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError(`${label} must be an object.`);
161
+ for (const key of Object.keys(value)) if (!allowed.includes(key)) throw new TypeError(`Unknown ${label} option: ${key}.`);
162
+ }
163
+ function integer(value: unknown, fallback: number, min: number, max: number, label: string): number {
164
+ if (value === undefined) return fallback;
165
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < min || value > max) throw new TypeError(`Invalid gRPC ${label}.`);
166
+ return value;
167
+ }
168
+ function metadataCopy(value: Metadata | undefined): Metadata {
169
+ if (value === undefined) return new Metadata();
170
+ if (!(value instanceof Metadata)) throw new TypeError("Expected Bazis Metadata.");
171
+ return value.clone();
172
+ }
@@ -0,0 +1,267 @@
1
+ import { constants, type Http2Session, type ClientHttp2Stream, type IncomingHttpHeaders } from "node:http2";
2
+ import type { GrpcResponseStream } from "./clientContracts";
3
+ import { GrpcError } from "./GrpcError";
4
+ import { GrpcStatus } from "./GrpcStatus";
5
+ import { Metadata } from "./Metadata";
6
+ import type { MethodDefinition } from "./serviceDefinition";
7
+
8
+ interface CallSettings {
9
+ readonly deadline: number;
10
+ readonly signal?: AbortSignal;
11
+ readonly sendLimit: number;
12
+ readonly receiveLimit: number;
13
+ }
14
+
15
+ function deferred<T>() {
16
+ let resolve!: (value: T) => void, reject!: (reason: unknown) => void;
17
+ const promise = new Promise<T>((yes, no) => { resolve = yes; reject = no; });
18
+ // Metadata is optional to observe; ignoring it must not cause an unhandled rejection.
19
+ void promise.catch(() => {});
20
+ return { promise, resolve, reject };
21
+ }
22
+
23
+ /** @internal One RPC; bounded framing, pull-based download and cancellation ownership. */
24
+ export class GrpcClientCall<T> implements GrpcResponseStream<T> {
25
+ private readonly initial = deferred<Metadata>();
26
+ private readonly terminal = deferred<Metadata>();
27
+ readonly metadata = this.initial.promise;
28
+ readonly trailers = this.terminal.promise;
29
+ private readonly abort = new AbortController();
30
+ private readonly uploadAbort = new AbortController();
31
+ private headers?: IncomingHttpHeaders;
32
+ private terminalHeaders?: IncomingHttpHeaders;
33
+ private timer?: ReturnType<typeof setTimeout>;
34
+ private input?: AsyncIterator<unknown> | Iterator<unknown>;
35
+ private consumed = false;
36
+ private finished = false;
37
+ private failure?: GrpcError;
38
+ private readonly session?: Http2Session;
39
+ private readonly externalCancel = (): void => this.cancel();
40
+
41
+ constructor(
42
+ private readonly stream: ClientHttp2Stream,
43
+ private readonly method: MethodDefinition,
44
+ private readonly settings: CallSettings,
45
+ private readonly onFinish: () => void,
46
+ ) {
47
+ this.session = stream.session;
48
+ stream.on("response", (headers: IncomingHttpHeaders, flags: number) => {
49
+ if (this.finished) return;
50
+ this.headers = headers;
51
+ try {
52
+ this.initial.resolve(Metadata.fromHttp2Headers(headers));
53
+ if (flags & constants.NGHTTP2_FLAG_END_STREAM) this.receiveTrailers(headers);
54
+ else if (headers["grpc-status"] !== undefined) throw new GrpcError(GrpcStatus.INTERNAL, "Non-terminal grpc-status.");
55
+ } catch (error) { this.fail(protocolError(error)); }
56
+ });
57
+ stream.on("trailers", (headers: IncomingHttpHeaders) => this.receiveTrailers(headers));
58
+ stream.on("error", (error: Error) => {
59
+ // A remote status has priority over a subsequent transport reset.
60
+ if (!this.terminalHeaders) this.fail(this.networkError(error));
61
+ });
62
+ stream.on("close", () => {
63
+ if (!this.finished && !this.terminalHeaders && !stream.readableEnded) {
64
+ this.fail(stream.rstCode || !this.headers ? this.networkError() : this.missingStatus());
65
+ }
66
+ });
67
+ }
68
+
69
+ start(requests: Iterable<unknown> | AsyncIterable<unknown>): void {
70
+ this.settings.signal?.addEventListener("abort", this.externalCancel, { once: true });
71
+ if (this.settings.signal?.aborted) { this.cancel(); return; }
72
+ const remaining = this.settings.deadline - Date.now();
73
+ if (remaining <= 0) { this.expire(); return; }
74
+ this.timer = setTimeout(() => this.expire(), remaining);
75
+ void this.send(requests).catch((error: unknown) => {
76
+ if (!this.uploadAbort.signal.aborted) this.fail(error instanceof GrpcError ? error
77
+ : new GrpcError(GrpcStatus.INVALID_ARGUMENT, "Failed to read or encode gRPC request."));
78
+ });
79
+ }
80
+
81
+ cancel(): void { this.fail(new GrpcError(GrpcStatus.CANCELLED, "RPC cancelled.")); }
82
+ connectionClosed(): void {
83
+ if (!this.terminalHeaders) this.fail(new GrpcError(GrpcStatus.UNAVAILABLE, "gRPC connection closed before completion."));
84
+ }
85
+ fail(error: GrpcError): void {
86
+ if (this.finished) return;
87
+ this.failure = error;
88
+ this.abort.abort(error);
89
+ this.initial.reject(error); this.terminal.reject(error);
90
+ this.finish(false);
91
+ }
92
+ private expire(): void { this.fail(new GrpcError(GrpcStatus.DEADLINE_EXCEEDED, "Deadline exceeded.")); }
93
+
94
+ [Symbol.asyncIterator](): AsyncIterator<T> {
95
+ if (this.consumed) throw new TypeError("gRPC response stream can only be consumed once.");
96
+ this.consumed = true;
97
+ const reader = this.read();
98
+ // return() before the first next() must also cancel (generator finally would not run).
99
+ return {
100
+ next: () => reader.next(),
101
+ return: async () => { this.cancel(); return reader.return(undefined); },
102
+ throw: async (error) => { this.cancel(); return reader.throw(error); },
103
+ };
104
+ }
105
+
106
+ private async send(requests: Iterable<unknown> | AsyncIterable<unknown>): Promise<void> {
107
+ const stream = this.stream;
108
+ const asyncSource = requests as AsyncIterable<unknown>;
109
+ this.input = typeof asyncSource[Symbol.asyncIterator] === "function"
110
+ ? asyncSource[Symbol.asyncIterator]() : (requests as Iterable<unknown>)[Symbol.iterator]();
111
+ while (!this.uploadAbort.signal.aborted) {
112
+ const next = await abortable(Promise.resolve(this.input.next()), this.uploadAbort.signal);
113
+ if (next.done) { this.input = undefined; break; }
114
+ this.uploadAbort.signal.throwIfAborted();
115
+ const body = this.method.requestSerialize(next.value);
116
+ if (!Buffer.isBuffer(body)) throw new TypeError("gRPC codecs must return Buffer.");
117
+ if (body.length > this.settings.sendLimit) throw new GrpcError(GrpcStatus.RESOURCE_EXHAUSTED, "gRPC request message is too large.");
118
+ const frame = Buffer.allocUnsafe(body.length + 5);
119
+ frame[0] = 0; frame.writeUInt32BE(body.length, 1); body.copy(frame, 5);
120
+ await abortable(new Promise<void>((resolve, reject) => {
121
+ stream.write(frame, (error?: Error | null) => error ? reject(this.networkError()) : resolve());
122
+ }), this.uploadAbort.signal);
123
+ }
124
+ if (!stream.writableEnded && !stream.destroyed) stream.end();
125
+ }
126
+
127
+ private receiveTrailers(headers: IncomingHttpHeaders): void {
128
+ if (this.finished) return;
129
+ if (this.terminalHeaders) { this.fail(new GrpcError(GrpcStatus.INTERNAL, "Duplicate gRPC trailers.")); return; }
130
+ this.terminalHeaders = headers;
131
+ this.stopUpload();
132
+ // Servers may finish before consuming the request stream.
133
+ if (!this.stream.writableEnded && !this.stream.destroyed) this.stream.end();
134
+ }
135
+
136
+ private async *read(): AsyncGenerator<T> {
137
+ const iterator = this.stream.iterator({ destroyOnReturn: false });
138
+ const prefix = Buffer.alloc(5);
139
+ let prefixBytes = 0, message: Buffer | undefined, messageBytes = 0, count = 0, discarded = 0;
140
+ try {
141
+ while (true) {
142
+ this.abort.signal.throwIfAborted();
143
+ const next = await abortable(iterator.next(), this.abort.signal);
144
+ if (next.done) break;
145
+ const chunk = next.value as Buffer;
146
+ if (!this.validContentType() || Number(this.headers?.[":status"]) !== 200) {
147
+ discarded += chunk.length;
148
+ if (discarded > this.settings.receiveLimit) throw new GrpcError(GrpcStatus.RESOURCE_EXHAUSTED, "Non-gRPC response is too large.");
149
+ continue;
150
+ }
151
+ let offset = 0;
152
+ while (offset < chunk.length) {
153
+ this.abort.signal.throwIfAborted();
154
+ if (!message) {
155
+ const amount = Math.min(5 - prefixBytes, chunk.length - offset);
156
+ chunk.copy(prefix, prefixBytes, offset, offset + amount);
157
+ prefixBytes += amount; offset += amount;
158
+ if (prefixBytes !== 5) continue;
159
+ if (prefix[0] !== 0) throw new GrpcError(prefix[0] === 1 ? GrpcStatus.UNIMPLEMENTED : GrpcStatus.INTERNAL,
160
+ prefix[0] === 1 ? "Compressed gRPC messages are not supported." : "Invalid gRPC compression flag.");
161
+ const length = prefix.readUInt32BE(1);
162
+ if (length > this.settings.receiveLimit) throw new GrpcError(GrpcStatus.RESOURCE_EXHAUSTED, "gRPC response message is too large.");
163
+ message = Buffer.alloc(length); messageBytes = 0;
164
+ }
165
+ const amount = Math.min(message.length - messageBytes, chunk.length - offset);
166
+ chunk.copy(message, messageBytes, offset, offset + amount);
167
+ messageBytes += amount; offset += amount;
168
+ if (messageBytes === message.length) {
169
+ if (++count > 1 && !this.method.responseStream) throw new GrpcError(GrpcStatus.INTERNAL, "Unary response requires exactly one message.");
170
+ let value: T;
171
+ try { value = this.method.responseDeserialize(message) as T; }
172
+ catch { throw new GrpcError(GrpcStatus.INTERNAL, "Invalid protobuf response."); }
173
+ message = undefined; prefixBytes = 0;
174
+ yield value;
175
+ }
176
+ }
177
+ }
178
+ this.abort.signal.throwIfAborted();
179
+ const trailers = this.checkStatus();
180
+ if (message || prefixBytes !== 0) throw new GrpcError(GrpcStatus.INTERNAL, "Incomplete gRPC message.");
181
+ if (!this.method.responseStream && count !== 1) throw new GrpcError(GrpcStatus.INTERNAL, "Unary response requires exactly one message.");
182
+ this.terminal.resolve(trailers);
183
+ this.finish(true);
184
+ } catch (error) {
185
+ let failure = this.failure;
186
+ if (!failure) {
187
+ try { if (this.terminalHeaders) this.checkStatus(); }
188
+ catch (statusError) { failure = protocolError(statusError); }
189
+ }
190
+ failure ??= error instanceof GrpcError ? error : this.networkError();
191
+ this.fail(failure);
192
+ throw failure;
193
+ } finally {
194
+ if (!this.finished) this.cancel();
195
+ void iterator.return?.().catch(() => {});
196
+ }
197
+ }
198
+
199
+ private checkStatus(): Metadata {
200
+ if (!this.terminalHeaders && this.stream.rstCode) throw this.networkError();
201
+ if (!this.headers) throw this.networkError();
202
+ if (!this.terminalHeaders || this.terminalHeaders["grpc-status"] === undefined) throw this.missingStatus();
203
+ const raw = this.terminalHeaders["grpc-status"];
204
+ if (typeof raw !== "string" || !/^(?:[0-9]|1[0-6])$/.test(raw)) throw new GrpcError(GrpcStatus.UNKNOWN, "Invalid grpc-status.");
205
+ const metadata = Metadata.fromHttp2Headers(this.terminalHeaders);
206
+ const code = Number(raw);
207
+ if (code !== GrpcStatus.OK) {
208
+ const value = this.terminalHeaders["grpc-message"];
209
+ let message = typeof value === "string" ? value : "RPC failed.";
210
+ try { message = decodeURIComponent(message); } catch { /* Preserve malformed remote text, never hide status. */ }
211
+ throw new GrpcError(code, message, metadata);
212
+ }
213
+ if (Number(this.headers?.[":status"]) !== 200 || !this.validContentType()) throw new GrpcError(GrpcStatus.INTERNAL, "Invalid gRPC response headers.");
214
+ return metadata;
215
+ }
216
+ private validContentType(): boolean {
217
+ const value = this.headers?.["content-type"];
218
+ return typeof value === "string" && /^application\/grpc(?:\+proto)?(?:;.*)?$/i.test(value);
219
+ }
220
+ private missingStatus(): GrpcError {
221
+ const http = Number(this.headers?.[":status"]);
222
+ const code = http === 400 ? GrpcStatus.INTERNAL : http === 401 ? GrpcStatus.UNAUTHENTICATED
223
+ : http === 403 ? GrpcStatus.PERMISSION_DENIED : http === 404 ? GrpcStatus.UNIMPLEMENTED
224
+ : [429, 502, 503, 504].includes(http) ? GrpcStatus.UNAVAILABLE : GrpcStatus.UNKNOWN;
225
+ return new GrpcError(code, "Response ended without grpc-status.");
226
+ }
227
+ private networkError(cause?: Error): GrpcError {
228
+ // Local connection setup failures may surface as INTERNAL_ERROR on the stream.
229
+ const transportFailure = this.session?.destroyed || this.session?.closed
230
+ || cause && (cause as NodeJS.ErrnoException).code !== "ERR_HTTP2_STREAM_ERROR";
231
+ const reset = this.stream.id === undefined || transportFailure ? undefined : this.stream.rstCode;
232
+ const code = reset === constants.NGHTTP2_CANCEL ? GrpcStatus.CANCELLED
233
+ : reset === constants.NGHTTP2_ENHANCE_YOUR_CALM ? GrpcStatus.RESOURCE_EXHAUSTED
234
+ : reset === constants.NGHTTP2_INADEQUATE_SECURITY ? GrpcStatus.PERMISSION_DENIED
235
+ : reset && reset !== constants.NGHTTP2_REFUSED_STREAM ? GrpcStatus.INTERNAL : GrpcStatus.UNAVAILABLE;
236
+ return new GrpcError(code, "gRPC connection closed before completion.");
237
+ }
238
+ private stopUpload(): void {
239
+ if (this.uploadAbort.signal.aborted) return;
240
+ this.uploadAbort.abort(new GrpcError(GrpcStatus.CANCELLED, "Request upload stopped."));
241
+ const input = this.input; this.input = undefined;
242
+ try { void Promise.resolve(input?.return?.()).catch(() => {}); } catch { /* Best-effort user cleanup. */ }
243
+ }
244
+ private finish(success: boolean): void {
245
+ if (this.finished) return;
246
+ this.finished = true;
247
+ if (this.timer) clearTimeout(this.timer);
248
+ this.settings.signal?.removeEventListener("abort", this.externalCancel);
249
+ this.stopUpload();
250
+ try {
251
+ if (!this.stream.closed && !this.stream.destroyed) this.stream.close(success ? constants.NGHTTP2_NO_ERROR : constants.NGHTTP2_CANCEL);
252
+ } finally { this.onFinish(); }
253
+ }
254
+ }
255
+
256
+ function protocolError(error: unknown): GrpcError {
257
+ return error instanceof GrpcError ? error : new GrpcError(GrpcStatus.INTERNAL, "Invalid gRPC response metadata.");
258
+ }
259
+
260
+ function abortable<T>(pending: Promise<T>, signal: AbortSignal): Promise<T> {
261
+ return new Promise<T>((resolve, reject) => {
262
+ const cancel = (): void => reject(signal.reason);
263
+ if (signal.aborted) { void pending.catch(() => {}); reject(signal.reason); return; }
264
+ signal.addEventListener("abort", cancel, { once: true });
265
+ pending.then(resolve, reject).finally(() => signal.removeEventListener("abort", cancel));
266
+ });
267
+ }
@@ -0,0 +1,19 @@
1
+ import { Metadata } from "./Metadata";
2
+ import { GrpcStatus as status } from "./GrpcStatus";
3
+
4
+ /** An intentional, public RPC error. Other exceptions are redacted to INTERNAL. */
5
+ export class GrpcError extends Error {
6
+ constructor(
7
+ readonly code: status,
8
+ message: string,
9
+ readonly metadata?: Metadata,
10
+ ) {
11
+ super(message);
12
+ this.name = "GrpcError";
13
+ if (!Number.isInteger(code) || code <= status.OK || code > status.UNAUTHENTICATED) {
14
+ throw new TypeError("GrpcError requires a non-OK gRPC status code.");
15
+ }
16
+ if (typeof message !== "string") throw new TypeError("GrpcError message must be a string.");
17
+ if (metadata !== undefined && !(metadata instanceof Metadata)) throw new TypeError("GrpcError metadata must be Bazis Metadata.");
18
+ }
19
+ }
@@ -0,0 +1,45 @@
1
+ import { bindModel } from "../http/Binding/modelBinder";
2
+ import type { ModelValidationIssue, ModelValidator } from "../http/Binding/modelValidator";
3
+ import type { RequestModelClass } from "../http/Binding/requestModelRegistry";
4
+ import { BadRequestError, ModelValidationError } from "../http/Errors/HttpError";
5
+ import { GrpcError } from "./GrpcError";
6
+ import { GrpcStatus } from "./GrpcStatus";
7
+ import { Metadata } from "./Metadata";
8
+
9
+ /** A route-owned binding; all per-message state remains local to bindModel. */
10
+ export class GrpcRequestBinding {
11
+ constructor(private readonly model: RequestModelClass, private readonly validator: ModelValidator) {}
12
+
13
+ bind(value: unknown): object {
14
+ try {
15
+ return bindModel(this.model, value, this.validator, { allowBinary: true });
16
+ } catch (error) {
17
+ if (!(error instanceof BadRequestError)) throw error;
18
+ const issues = error instanceof ModelValidationError ? error.errors
19
+ : [{ property: "$", message: "Invalid request structure.", code: "structure" }];
20
+ throw new GrpcError(GrpcStatus.INVALID_ARGUMENT, "Request validation failed.", validationMetadata(issues));
21
+ }
22
+ }
23
+ }
24
+
25
+ function validationMetadata(issues: readonly ModelValidationIssue[]): Metadata {
26
+ const errors: ModelValidationIssue[] = [];
27
+ let truncated = false;
28
+ let size = Buffer.byteLength('{"errors":[],"truncated":false}');
29
+ for (const issue of issues) {
30
+ const bounded = (value: string, limit: number): string => {
31
+ if (value.length > limit) truncated = true;
32
+ return value.slice(0, limit);
33
+ };
34
+ const item = {
35
+ property: bounded(issue.property, 256), message: bounded(issue.message, 512),
36
+ ...(issue.code === undefined ? {} : { code: bounded(issue.code, 64) }),
37
+ };
38
+ size += Buffer.byteLength(JSON.stringify(item)) + (errors.length === 0 ? 0 : 1);
39
+ if (size > 4096) { truncated = true; break; }
40
+ errors.push(item);
41
+ }
42
+ const metadata = new Metadata();
43
+ metadata.set("bazis-validation-errors-bin", Buffer.from(JSON.stringify({ errors, truncated })));
44
+ return metadata;
45
+ }