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,125 @@
1
+ import { optionsFromSchema } from "../../../library/jsonapi";
2
+ import { AMBIGUOUS_REQUEST_MODEL, findRequestModelByName } from "./requestModelRegistry";
3
+ import { AMBIGUOUS_LIST_MODEL, findListModelByName } from "./listModelRegistry";
4
+ import { HttpSetupError } from "../Errors/HttpError";
5
+ import type { Class } from "../../di";
6
+ import type { ParameterBinding } from "./bindings";
7
+ import type { GeneratedBindingSpec } from "./generatedSpec";
8
+
9
+ type GeneratedControllerClass = abstract new (...args: never[]) => object;
10
+
11
+ // Generated target constructors remain process-owned. Map snapshots make a
12
+ // failed multi-owner publication fully reversible.
13
+ let targetBindings = new Map<GeneratedControllerClass, Readonly<Record<string, readonly GeneratedBindingSpec[]>>>();
14
+ let targetModels = new Map<GeneratedControllerClass, ReadonlyMap<string, Class<object>>>();
15
+
16
+ /** Registers an isolated generated target slice without relying on class.name. */
17
+ export function registerGeneratedBindings(
18
+ controller: Class<object>,
19
+ bindings: Readonly<Record<string, readonly GeneratedBindingSpec[]>>,
20
+ models?: ReadonlyMap<string, Class<object>>,
21
+ ): void {
22
+ targetBindings.set(controller, bindings);
23
+ if (models !== undefined) targetModels.set(controller, models);
24
+ }
25
+
26
+ /**
27
+ * Binding conventions inferred by codegen (`bun run di:generate`) from the
28
+ * controller method signature, the counterpart of DI auto deps:
29
+ *
30
+ * - a parameter named like a route `:name` -> from the route;
31
+ * - a class type -> request body plus validation;
32
+ * - `HttpContext` / `Request` / `ResponseBuilder` -> context/raw request/builder;
33
+ * - other primitives -> query (converted by type; `?`/default -> optional).
34
+ *
35
+ * Headers and raw bodies are read through HttpContext; services are injected
36
+ * into the constructor. Any DTO class parameter is treated as the request body.
37
+ * Model names are resolved once at startup; configuration errors are
38
+ * `HttpSetupError` (fail fast).
39
+ */
40
+ export function resolveGeneratedBindings(
41
+ controller: GeneratedControllerClass,
42
+ methodName: string | symbol,
43
+ ): readonly ParameterBinding[] | undefined {
44
+ const controllerName = controller.name;
45
+ const specs = targetBindings.get(controller)?.[String(methodName)];
46
+ if (specs === undefined) {
47
+ return undefined;
48
+ }
49
+ const models = targetModels.get(controller);
50
+ return specs.map((spec) => toBinding(spec, controllerName, String(methodName), models));
51
+ }
52
+
53
+ function toBinding(spec: GeneratedBindingSpec, controllerName: string, methodName: string, models?: ReadonlyMap<string, Class<object>>): ParameterBinding {
54
+ switch (spec.source) {
55
+ case "route":
56
+ return { source: "route", name: spec.name, type: spec.type, optional: spec.optional };
57
+ case "query":
58
+ return { source: "query", name: spec.name, type: spec.type, optional: spec.optional };
59
+ case "context":
60
+ return { source: "context" };
61
+ case "request":
62
+ return { source: "request" };
63
+ case "response":
64
+ return { source: "response" };
65
+ case "list": {
66
+ if (!spec.model) {
67
+ return { source: "list" };
68
+ }
69
+ // An explicit target controller always resolves through its own
70
+ // constructor-local index. Name fallback is legacy/default-only.
71
+ const ctor = models === undefined ? findListModelByName(spec.model) : models.get(spec.model);
72
+ if (ctor === undefined) {
73
+ throw new HttpSetupError(
74
+ `${controllerName}.${methodName}: list model "${spec.model}" is not registered. ` +
75
+ `Make sure the class extends ListRequest and its module is imported, then run \`bun run di:generate\`.`,
76
+ );
77
+ }
78
+ if (ctor === AMBIGUOUS_LIST_MODEL) {
79
+ throw new HttpSetupError(
80
+ `${controllerName}.${methodName}: list model name "${spec.model}" is ambiguous (several classes share it). ` +
81
+ `Rename one of the classes.`,
82
+ );
83
+ }
84
+ return { source: "list", model: ctor as Class<object>, listOptions: optionsFromSchema(ctor) };
85
+ }
86
+ case "body": {
87
+ if (!spec.model) {
88
+ return { source: "body" };
89
+ }
90
+ // An explicit target controller always resolves through its own
91
+ // constructor-local index. Name fallback is legacy/default-only.
92
+ const model = models === undefined ? findRequestModelByName(spec.model) : models.get(spec.model);
93
+ if (model === undefined) {
94
+ throw new HttpSetupError(
95
+ `${controllerName}.${methodName}: body model "${spec.model}" is not registered. ` +
96
+ `Regenerate the target metadata; for an external DTO use @RequestModel() and import its module.`,
97
+ );
98
+ }
99
+ if (model === AMBIGUOUS_REQUEST_MODEL) {
100
+ throw new HttpSetupError(
101
+ `${controllerName}.${methodName}: body model name "${spec.model}" is ambiguous (several classes share it). ` +
102
+ `Rename one of the classes.`,
103
+ );
104
+ }
105
+ return { source: "body", model };
106
+ }
107
+ }
108
+ }
109
+
110
+ /** Internal generated-runtime transaction support. */
111
+ export function snapshotGeneratedBindings(): {
112
+ readonly targetBindings: typeof targetBindings;
113
+ readonly targetModels: typeof targetModels;
114
+ } {
115
+ return {
116
+ targetBindings: new Map(targetBindings),
117
+ targetModels: new Map(targetModels),
118
+ };
119
+ }
120
+
121
+ /** Internal generated-runtime transaction support. */
122
+ export function restoreGeneratedBindings(snapshot: ReturnType<typeof snapshotGeneratedBindings>): void {
123
+ targetBindings = snapshot.targetBindings;
124
+ targetModels = snapshot.targetModels;
125
+ }
@@ -0,0 +1,19 @@
1
+ import type { Class } from "../../di";
2
+ import type { ListQueryOptions } from "../../../library/jsonapi";
3
+ import type { ValueType } from "./convert";
4
+
5
+ /** Internal descriptors resolved from generated action signatures. */
6
+ export type BindingSource = "route" | "query" | "body" | "request" | "response" | "context" | "list";
7
+
8
+ export interface ParameterBinding {
9
+ readonly source: BindingSource;
10
+ readonly name?: string;
11
+ readonly type?: ValueType;
12
+ readonly defaultValue?: unknown;
13
+ readonly optional?: boolean;
14
+ /** Body model class (instantiated, sanitized and validated). */
15
+ readonly model?: Class<object>;
16
+ /** Whitelist/limits for the class-based list binding (JSON:API list query). */
17
+ readonly listOptions?: ListQueryOptions;
18
+ }
19
+
@@ -0,0 +1,48 @@
1
+ import { BadRequestError } from "../Errors/HttpError";
2
+
3
+ /** Primitive conversion targets for query/header/route values. */
4
+ export type ValueType = "string" | "int" | "number" | "bool";
5
+
6
+ /** Sentinel: conversion failed (distinguishable from any valid value). */
7
+ export const INVALID: unique symbol = Symbol("invalid");
8
+
9
+ const INT_PATTERN = /^-?\d+$/;
10
+
11
+ /** Converts a raw string; returns INVALID instead of throwing (hot path). */
12
+ export function tryConvert(raw: string, type: ValueType): string | number | boolean | typeof INVALID {
13
+ switch (type) {
14
+ case "string":
15
+ return raw;
16
+ case "int": {
17
+ if (!INT_PATTERN.test(raw)) {
18
+ return INVALID;
19
+ }
20
+ const value = Number(raw);
21
+ return Number.isSafeInteger(value) ? value : INVALID;
22
+ }
23
+ case "number": {
24
+ if (raw === "") {
25
+ return INVALID;
26
+ }
27
+ const value = Number(raw);
28
+ return Number.isFinite(value) ? value : INVALID;
29
+ }
30
+ case "bool":
31
+ if (raw === "true" || raw === "1") {
32
+ return true;
33
+ }
34
+ if (raw === "false" || raw === "0") {
35
+ return false;
36
+ }
37
+ return INVALID;
38
+ }
39
+ }
40
+
41
+ /** Converts or throws 400 with the parameter name in the message. */
42
+ export function convertOr400(raw: string, type: ValueType, parameterName: string): string | number | boolean {
43
+ const value = tryConvert(raw, type);
44
+ if (value === INVALID) {
45
+ throw new BadRequestError(`Parameter "${parameterName}" must be of type ${type}, got: "${raw}"`);
46
+ }
47
+ return value;
48
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Format of the generated binding conventions (`bun run di:generate`).
3
+ *
4
+ * Plain data without class references: the body model is given **by name** and
5
+ * resolved at startup through the class index of the generated target.
6
+ * The spec stores the name; a separate generated target supplies the constructors.
7
+ */
8
+ export interface GeneratedBindingSpec {
9
+ readonly source: "route" | "query" | "body" | "context" | "request" | "response" | "list";
10
+ /** Name of the route/query parameter. */
11
+ readonly name?: string;
12
+ /** Primitive conversion (for query, and route params without a constraint in the template). */
13
+ readonly type?: "int" | "number" | "bool" | "string";
14
+ /** The parameter is declared with `?` or a default value. */
15
+ readonly optional?: boolean;
16
+ /** DTO class name (resolved through the generated target; `@RequestModel()` classes are the fallback). */
17
+ readonly model?: string;
18
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * "Class name -> class" registry for list requests (`ListRequest` subclasses).
3
+ *
4
+ * Like {@link requestModelRegistry}: generated binding conventions refer to
5
+ * the class by name (the generated file is plain data), and the class itself
6
+ * is resolved from this registry when the server starts. Registration comes
7
+ * from the app-owned generated runtime (`src/generated/bazis/httpListModels.ts`).
8
+ */
9
+
10
+ /** Marker: several different classes are registered under one name. */
11
+ export const AMBIGUOUS_LIST_MODEL: unique symbol = Symbol("ambiguous-list-model");
12
+
13
+ export type ListModelClass = new () => object;
14
+
15
+ let registry = new Map<string, ListModelClass | typeof AMBIGUOUS_LIST_MODEL>();
16
+
17
+ /** Registers a list-request class (called from the generated runtime). */
18
+ export function registerListModelClass(ctor: ListModelClass): void {
19
+ const name = ctor.name;
20
+ if (!name) {
21
+ return;
22
+ }
23
+ const existing = registry.get(name);
24
+ if (existing === undefined) {
25
+ registry.set(name, ctor);
26
+ } else if (existing !== ctor) {
27
+ registry.set(name, AMBIGUOUS_LIST_MODEL);
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Class by name: `undefined` if not registered, `AMBIGUOUS_LIST_MODEL` if the
33
+ * name is ambiguous (two different classes).
34
+ */
35
+ export function findListModelByName(name: string): ListModelClass | typeof AMBIGUOUS_LIST_MODEL | undefined {
36
+ return registry.get(name);
37
+ }
38
+
39
+ /** Internal generated-runtime transaction support. */
40
+ export function snapshotListModelRegistry(): typeof registry {
41
+ return new Map(registry);
42
+ }
43
+
44
+ /** Internal generated-runtime transaction support. */
45
+ export function restoreListModelRegistry(snapshot: ReturnType<typeof snapshotListModelRegistry>): void {
46
+ registry = snapshot;
47
+ }
@@ -0,0 +1,345 @@
1
+ import type { Class } from "../../di";
2
+ import { BadRequestError, ModelValidationError } from "../Errors/HttpError";
3
+ import { getModelValidator, type ModelValidator } from "./modelValidator";
4
+ import {
5
+ findRequestModelShape,
6
+ type RequestModelClass,
7
+ type RequestModelFieldShape,
8
+ type RequestModelNestedFieldShape,
9
+ type RequestModelPrimitiveFieldShape,
10
+ } from "./requestModelRegistry";
11
+
12
+ /** Keys that must never be copied from external input (prototype pollution). */
13
+ const FORBIDDEN_KEYS = new Set(["__proto__", "constructor", "prototype"]);
14
+ const MAX_JSON_DEPTH = 64;
15
+ const MAX_JSON_NODES = 100_000;
16
+
17
+ interface BindingTraversal {
18
+ readonly stack: WeakSet<object>;
19
+ readonly options: ModelBindingOptions;
20
+ nodes: number;
21
+ }
22
+
23
+ /** Internal binding policy; HTTP keeps its historical stripping defaults. */
24
+ export interface ModelBindingOptions {
25
+ readonly unknownFields?: "strip" | "reject";
26
+ readonly declaredFields?: (model: RequestModelClass) => readonly string[] | undefined;
27
+ /** Protobuf bytes only; HTTP/Agent keep their strict JSON boundary. */
28
+ readonly allowBinary?: boolean;
29
+ /**
30
+ * Reject a JSON value whose type differs from a field declared exactly as
31
+ * `string`, `number` or `boolean` (from generated shapes). HTTP enables it;
32
+ * gRPC (int64 as string/bigint) and agents keep their own conversions.
33
+ */
34
+ readonly primitiveTypes?: boolean;
35
+ }
36
+
37
+ /**
38
+ * Binds a parsed JSON body to a model class instance:
39
+ *
40
+ * 1. requires a plain JSON object (not array/primitive) — otherwise 400;
41
+ * 2. copies only fields declared by the class (plus explicit setters),
42
+ * stripping unknown and prototype-polluting names;
43
+ * 3. runs `@Validator` rules of the model — failures become a 400 with the
44
+ * full list of validation errors (`ModelValidationError`).
45
+ *
46
+ * The model class must have a parameterless constructor. Class-typed nested
47
+ * DTOs (including arrays) are recursively hydrated from metadata emitted by
48
+ * `bun run di:generate`; initialized nested DTO fields remain supported as a
49
+ * runtime fallback.
50
+ */
51
+ export function bindModel<T extends object>(model: Class<T>, data: unknown, validator?: ModelValidator, options: ModelBindingOptions = {}): T {
52
+ if (!isPlainJsonObject(data)) {
53
+ throw new BadRequestError("Request body must be a JSON object");
54
+ }
55
+ const traversal: BindingTraversal = { stack: new WeakSet(), nodes: 0, options };
56
+ const instance = hydrateModel(model as RequestModelClass, data, "", traversal, 0) as T;
57
+ const activeValidator = validator ?? getModelValidator();
58
+ if (activeValidator !== undefined) {
59
+ const result = activeValidator.validate(instance);
60
+ if (!result.isValid) {
61
+ throw new ModelValidationError(result.errors);
62
+ }
63
+ }
64
+ return instance;
65
+ }
66
+
67
+ function hydrateModel(
68
+ model: RequestModelClass,
69
+ source: Record<string, unknown>,
70
+ path: string,
71
+ traversal: BindingTraversal,
72
+ depth: number,
73
+ ): object {
74
+ enterContainer(source, path, traversal, depth);
75
+ const instance = new model();
76
+ // Class fields are emitted as own properties by Bun/TypeScript. Treat that
77
+ // runtime shape, generated nested fields and explicit prototype setters as
78
+ // the input whitelist so over-posted fields never reach application code.
79
+ try {
80
+ const allowed = bindableKeys(instance, model.prototype, model, traversal.options);
81
+ for (const key of Object.keys(source)) {
82
+ if (!acceptKey(source, key, allowed, path, traversal.options)) {
83
+ continue;
84
+ }
85
+ const target = instance as Record<string, unknown>;
86
+ const fieldPath = path === "" ? key : `${path}.${key}`;
87
+ target[key] = bindField(model, key, target[key], source[key], fieldPath, traversal, depth + 1);
88
+ }
89
+ return instance;
90
+ } finally {
91
+ traversal.stack.delete(source);
92
+ }
93
+ }
94
+
95
+ function bindField(
96
+ model: RequestModelClass,
97
+ key: string,
98
+ template: unknown,
99
+ incoming: unknown,
100
+ path: string,
101
+ traversal: BindingTraversal,
102
+ depth: number,
103
+ ): unknown {
104
+ const shape = findFieldShape(model, key);
105
+ if (shape !== undefined && "model" in shape) {
106
+ return hydrateField(shape, incoming, path, traversal, depth);
107
+ }
108
+ if (shape !== undefined && traversal.options.primitiveTypes === true) {
109
+ checkPrimitive(shape, incoming, path);
110
+ }
111
+ return sanitizeBoundValue(template, incoming, path, traversal, depth);
112
+ }
113
+
114
+ /** `undefined` means "absent"; required-ness stays with `@Validator`. */
115
+ function checkPrimitive(shape: RequestModelPrimitiveFieldShape, incoming: unknown, path: string): void {
116
+ if (incoming === undefined || (incoming === null && shape.nullable === true)) {
117
+ return;
118
+ }
119
+ if (shape.array !== true) {
120
+ if (typeof incoming !== shape.primitive) throw bindingTypeError(path, shape.primitive);
121
+ return;
122
+ }
123
+ if (!Array.isArray(incoming)) throw bindingTypeError(path, "array");
124
+ incoming.forEach((item, index) => {
125
+ if (!(item === null && shape.elementNullable === true) && typeof item !== shape.primitive) {
126
+ throw bindingTypeError(`${path}[${index}]`, shape.primitive);
127
+ }
128
+ });
129
+ }
130
+
131
+ function hydrateField(
132
+ shape: RequestModelNestedFieldShape,
133
+ incoming: unknown,
134
+ path: string,
135
+ traversal: BindingTraversal,
136
+ depth: number,
137
+ ): unknown {
138
+ if (incoming === undefined) {
139
+ return undefined;
140
+ }
141
+ if (incoming === null) {
142
+ if (shape.nullable === true) {
143
+ return null;
144
+ }
145
+ throw bindingTypeError(path, shape.array === true ? "array" : "object");
146
+ }
147
+ if (shape.array === true) {
148
+ if (!Array.isArray(incoming)) {
149
+ throw bindingTypeError(path, "array");
150
+ }
151
+ enterContainer(incoming, path, traversal, depth);
152
+ try {
153
+ return incoming.map((item, index) => {
154
+ const itemPath = `${path}[${index}]`;
155
+ if (item === null && shape.elementNullable === true) {
156
+ return null;
157
+ }
158
+ if (!isPlainJsonObject(item)) {
159
+ throw bindingTypeError(itemPath, "object");
160
+ }
161
+ return hydrateModel(shape.model, item, itemPath, traversal, depth + 1);
162
+ });
163
+ } finally {
164
+ traversal.stack.delete(incoming);
165
+ }
166
+ }
167
+ if (!isPlainJsonObject(incoming)) {
168
+ throw bindingTypeError(path, "object");
169
+ }
170
+ return hydrateModel(shape.model, incoming, path, traversal, depth);
171
+ }
172
+
173
+ function bindingTypeError(property: string, expected: string): ModelValidationError {
174
+ return new ModelValidationError([{
175
+ property: property || "$",
176
+ message: `Field "${property || "$"}" must be a ${expected}`,
177
+ code: "type",
178
+ }]);
179
+ }
180
+
181
+ function isPlainJsonObject(value: unknown): value is Record<string, unknown> {
182
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
183
+ return false;
184
+ }
185
+ const prototype = Object.getPrototypeOf(value) as object | null;
186
+ return prototype === Object.prototype || prototype === null;
187
+ }
188
+
189
+ function findFieldShape(model: RequestModelClass, property: string): RequestModelFieldShape | undefined {
190
+ let current: unknown = model;
191
+ while (typeof current === "function" && current !== Function.prototype) {
192
+ const shape = findRequestModelShape(current as RequestModelClass);
193
+ if (shape !== undefined && Object.prototype.hasOwnProperty.call(shape, property)) {
194
+ return shape[property];
195
+ }
196
+ current = Object.getPrototypeOf(current);
197
+ }
198
+ return undefined;
199
+ }
200
+
201
+ function sanitizeBoundValue(
202
+ template: unknown,
203
+ incoming: unknown,
204
+ path: string,
205
+ traversal: BindingTraversal,
206
+ depth: number,
207
+ ): unknown {
208
+ if (incoming === null || typeof incoming !== "object") {
209
+ return incoming;
210
+ }
211
+
212
+ if (traversal.options.allowBinary === true && incoming instanceof Uint8Array) {
213
+ return Buffer.isBuffer(incoming) ? Buffer.from(incoming) : new Uint8Array(incoming);
214
+ }
215
+
216
+ if (Array.isArray(incoming)) {
217
+ if (isNestedModel(template) || isPlainJsonObject(template)) {
218
+ throw bindingTypeError(path, "object");
219
+ }
220
+ enterContainer(incoming, path, traversal, depth);
221
+ try {
222
+ return incoming.map((item, index) =>
223
+ sanitizeBoundValue(undefined, item, `${path}[${index}]`, traversal, depth + 1)
224
+ );
225
+ } finally {
226
+ traversal.stack.delete(incoming);
227
+ }
228
+ }
229
+
230
+ if (!isPlainJsonObject(incoming)) {
231
+ throw bindingTypeError(path, "JSON object or array");
232
+ }
233
+ if (Array.isArray(template)) {
234
+ throw bindingTypeError(path, "array");
235
+ }
236
+
237
+ enterContainer(incoming, path, traversal, depth);
238
+ try {
239
+ if (isNestedModel(template)) {
240
+ const target = template as Record<string, unknown>;
241
+ const targetModel = target.constructor as RequestModelClass;
242
+ const allowed = bindableKeys(target, Object.getPrototypeOf(target) as object, targetModel, traversal.options);
243
+ for (const key of Object.keys(incoming)) {
244
+ if (!acceptKey(incoming, key, allowed, path, traversal.options)) {
245
+ continue;
246
+ }
247
+ const childPath = `${path}.${key}`;
248
+ target[key] = bindField(targetModel, key, target[key], incoming[key], childPath, traversal, depth + 1);
249
+ }
250
+ return target;
251
+ }
252
+
253
+ // Open JSON bags (`Record<string, unknown>`, `{}`, `unknown[]`) retain
254
+ // ordinary data keys, but are cloned recursively so forbidden keys and
255
+ // hostile prototypes cannot cross the HTTP boundary at any depth.
256
+ const output: Record<string, unknown> = {};
257
+ for (const key of Object.keys(incoming)) {
258
+ if (!acceptKey(incoming, key, undefined, path, traversal.options)) {
259
+ continue;
260
+ }
261
+ output[key] = sanitizeBoundValue(undefined, incoming[key], `${path}.${key}`, traversal, depth + 1);
262
+ }
263
+ return output;
264
+ } finally {
265
+ traversal.stack.delete(incoming);
266
+ }
267
+ }
268
+
269
+ function enterContainer(
270
+ value: object,
271
+ path: string,
272
+ traversal: BindingTraversal,
273
+ depth: number,
274
+ ): void {
275
+ const property = path || "$";
276
+ if (depth > MAX_JSON_DEPTH) {
277
+ throw new BadRequestError("Request body is too deeply nested", {
278
+ property,
279
+ maxDepth: MAX_JSON_DEPTH,
280
+ });
281
+ }
282
+ const childCount = Array.isArray(value) ? value.length : Object.keys(value).length;
283
+ traversal.nodes += 1 + childCount;
284
+ if (traversal.nodes > MAX_JSON_NODES) {
285
+ throw new BadRequestError("Request body is too complex", {
286
+ property,
287
+ maxNodes: MAX_JSON_NODES,
288
+ });
289
+ }
290
+ if (traversal.stack.has(value)) {
291
+ throw new BadRequestError("Request body must be acyclic JSON", { property });
292
+ }
293
+ traversal.stack.add(value);
294
+ }
295
+
296
+ function isNestedModel(value: unknown): value is object {
297
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
298
+ return false;
299
+ }
300
+ const prototype = Object.getPrototypeOf(value) as object | null;
301
+ return prototype !== null && prototype !== Object.prototype &&
302
+ !(value instanceof Date) && !(value instanceof Map) && !(value instanceof Set) && !(value instanceof URL);
303
+ }
304
+
305
+ function acceptKey(source: object, key: string, allowed: ReadonlySet<string> | undefined, path: string, options: ModelBindingOptions): boolean {
306
+ if (!Object.prototype.hasOwnProperty.call(source, key)) return false;
307
+ if (!FORBIDDEN_KEYS.has(key) && (allowed === undefined || allowed.has(key))) return true;
308
+ if (options.unknownFields === "reject") {
309
+ const property = path === "" ? key : `${path}.${key}`;
310
+ throw new ModelValidationError([{ property, message: `Field "${property}" is forbidden or not declared by the model.`, code: "unknownField" }]);
311
+ }
312
+ return false;
313
+ }
314
+
315
+ function bindableKeys(instance: object, prototype: object, model: RequestModelClass, options: ModelBindingOptions): Set<string> {
316
+ const declared = options.declaredFields?.(model);
317
+ if (declared !== undefined) return new Set(declared);
318
+ const keys = new Set(Object.keys(instance));
319
+ let shapeModel: unknown = model;
320
+ while (typeof shapeModel === "function" && shapeModel !== Function.prototype) {
321
+ const shape = findRequestModelShape(shapeModel as RequestModelClass);
322
+ if (shape !== undefined) {
323
+ for (const key of Object.keys(shape)) {
324
+ if (!FORBIDDEN_KEYS.has(key)) {
325
+ keys.add(key);
326
+ }
327
+ }
328
+ }
329
+ shapeModel = Object.getPrototypeOf(shapeModel);
330
+ }
331
+ let current: object | null = prototype;
332
+ while (current && current !== Object.prototype) {
333
+ for (const key of Object.getOwnPropertyNames(current)) {
334
+ if (key === "constructor" || FORBIDDEN_KEYS.has(key)) {
335
+ continue;
336
+ }
337
+ const descriptor = Object.getOwnPropertyDescriptor(current, key);
338
+ if (descriptor?.set) {
339
+ keys.add(key);
340
+ }
341
+ }
342
+ current = Object.getPrototypeOf(current) as object | null;
343
+ }
344
+ return keys;
345
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Request-model validation port (dependency inversion).
3
+ *
4
+ * `@/core/http` (the kernel) defines the abstraction but knows nothing about a
5
+ * concrete validation engine. The `@/library/validation` library implements it;
6
+ * the composition root (`runApp`) or a test registers the adapter through
7
+ * {@link useModelValidator}. Without a registered validator the bound body is
8
+ * not validated.
9
+ */
10
+ export interface ModelValidationIssue {
11
+ readonly property: string;
12
+ readonly message: string;
13
+ readonly code?: string;
14
+ }
15
+
16
+ export interface ModelValidator {
17
+ validate(instance: object): { readonly isValid: boolean; readonly errors: readonly ModelValidationIssue[] };
18
+ }
19
+
20
+ let activeValidator: ModelValidator | undefined;
21
+
22
+ /** Plugs in a concrete validator (bridge from `@/library/validation`). Idempotent. */
23
+ export function useModelValidator(validator: ModelValidator): void {
24
+ activeValidator = validator;
25
+ }
26
+
27
+ /** The current validator, or `undefined` if none is plugged in. */
28
+ export function getModelValidator(): ModelValidator | undefined {
29
+ return activeValidator;
30
+ }