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,579 @@
1
+ # bazis validation module: specification
2
+
3
+ Declarative class validation: one `@Validator(options)` decorator on fields and the
4
+ static methods `Validator.validate` / `Validator.validateAsync`.
5
+
6
+ Quick navigation:
7
+ - [1. What it is and why](#1-what-it-is-and-why)
8
+ - [2. Quick start](#2-quick-start)
9
+ - [3. Decorator options reference](#3-decorator-options-reference)
10
+ - [4. Usage scenarios](#4-usage-scenarios)
11
+ - [5. Result: `ValidationResult` and `ValidationError`](#5-result-validationresult-and-validationerror)
12
+ - [6. Error messages and placeholders](#6-error-messages-and-placeholders)
13
+ - [7. Error codes](#7-error-codes)
14
+ - [8. Performance and binary build](#8-performance-and-binary-build)
15
+ - [9. Security and resilience](#9-security-and-resilience)
16
+ - [10. Limits and anti-patterns](#10-limits-and-anti-patterns)
17
+
18
+ ---
19
+
20
+ ## 1. What it is and why
21
+
22
+ The module solves one task: **check that an object (usually a DTO at the system
23
+ boundary: an HTTP request body, a queue message, a config) follows the rules**, and
24
+ return the full list of violations.
25
+
26
+ Why an own module instead of class-validator:
27
+
28
+ - **No external dependencies**: only Bun APIs, nothing is pulled into the binary.
29
+ - **No reflection**: no `emitDecoratorMetadata` / `reflect-metadata`.
30
+ Rules are stored through standard TC39 decorators and `Symbol.metadata`, which Bun
31
+ runs natively. So the module compiles freely with `bun build --compile`.
32
+ - **One pass, errors of all fields.** Validation does not stop at the first error.
33
+ After a string exceeds its upper length bound, the content checks of the same
34
+ decorator are skipped; the length error stays in the result.
35
+ - **Resilience.** An exception in a user function does not crash validation: it becomes
36
+ a regular error with the `customError` code.
37
+ - **OOP style.** Rules live next to the class fields, the engine behind the
38
+ `Validator` facade.
39
+
40
+ The whole public API is imported from one place:
41
+
42
+ ```ts
43
+ import { Validator, ValidationError, ValidationResult, ValidationCodes } from "@/library/validation";
44
+ ```
45
+
46
+ ---
47
+
48
+ ## 2. Quick start
49
+
50
+ ```ts
51
+ import { Validator } from "bazis/library/validation";
52
+
53
+ class CreateUserDto {
54
+ @Validator({ required: true, minLength: 3, maxLength: 50 })
55
+ username!: string;
56
+
57
+ @Validator({ required: true, email: true })
58
+ email!: string;
59
+
60
+ @Validator({ required: true, min: 18, integer: true })
61
+ age!: number;
62
+ }
63
+
64
+ const dto = new CreateUserDto();
65
+ dto.username = "ab"; // shorter than 3
66
+ dto.age = 17.5; // below 18 and not an integer
67
+
68
+ const result = Validator.validate(dto);
69
+
70
+ result.isValid; // false
71
+ result.errors.length; // 4 (minLength, required email, min, integer)
72
+ result.hasErrorsFor("email"); // true
73
+ result.getErrorsFor("age")[0].message;
74
+ // 'Field "age" must be at least 18'
75
+ ```
76
+
77
+ Class rules are collected and compiled **once** on the first `validate` call (cached in
78
+ a WeakMap); repeated checks rebuild nothing.
79
+
80
+ ---
81
+
82
+ ## 3. Decorator options reference
83
+
84
+ One `@Validator({...})` is one rule set. A field may have several decorators; all of them run.
85
+
86
+ ### General
87
+
88
+ | Option | Type | What it does |
89
+ |---|---|---|
90
+ | `required` | `boolean` | `undefined`/`null` is a `required` error. Without it a missing value skips all checks |
91
+ | `type` | `"string" \| "number" \| "boolean" \| "enum" \| "json" \| "phone" \| "email" \| "date" \| "any"` | a type hint; a mismatch is a `type` error. Optional: the type is also inferred from the rules (`minLength` ⇒ string, `min` ⇒ number) |
92
+ | `validateIf` | `(instance) => boolean` | returned `false`: all other rules **of this decorator** are skipped |
93
+ | `custom` | `(value, instance) => boolean \| string \| void \| ValidationError \| Promise<...>` | a user check, see [4.6](#46-custom-user-checks) |
94
+ | `message` | `string` | a local message template for all rules of the decorator; wins over the global ones |
95
+ | `nested` | `boolean` | recursive check of the value; turns on automatically when not set, `false` turns it off, see [4.9](#49-nested-objects-and-arrays-nested) |
96
+
97
+ ### Strings
98
+
99
+ Any of these rules requires the value to be a string (otherwise one `type` error).
100
+
101
+ | Option | Type | Error when |
102
+ |---|---|---|
103
+ | `notEmpty` | `boolean` | the string is empty `""` |
104
+ | `minLength` / `maxLength` | `number` | the length is below/above the given one |
105
+ | `length` | `[number, number]` | the length is outside the range (inclusive) |
106
+ | `contains` / `notContains` | `string` | the substring is missing / present |
107
+ | `pattern` | `RegExp \| string` | the string does not match (a source string is compiled once) |
108
+ | `email` | `boolean` | not an email address |
109
+ | `url` | `boolean` | not a URL (checked with `URL.canParse`) |
110
+ | `uuid` | `boolean` | not a UUID of versions 1–8 (the nil UUID is allowed, case does not matter) |
111
+ | `json` | `boolean` | does not parse with `JSON.parse` |
112
+ | `phone` | `boolean` | not a phone number: an optional `+`, 7–15 digits; spaces, hyphens and parentheses are ignored |
113
+
114
+ `maxLength` and the upper bound of `length` limit the input of the later content checks
115
+ **of the same decorator**: `contains`, `notContains`, `pattern`, `email`, `url`, `uuid`,
116
+ `json`, `phone`. When exceeded, the length errors stay and the listed checks do not run.
117
+ The lower length bound, the other fields/decorators and `custom` keep their behavior.
118
+ For an untrusted `pattern`, set the upper bound in the same decorator; a separate
119
+ limiting decorator does not change the neighboring rules.
120
+
121
+ ### Numbers
122
+
123
+ They require `typeof value === "number"` and not `NaN` (otherwise a `type` error).
124
+
125
+ | Option | Type | Error when |
126
+ |---|---|---|
127
+ | `min` / `max` | `number` | below/above the bound |
128
+ | `range` | `[number, number]` | outside the range (inclusive) |
129
+ | `positive` / `negative` | `boolean` | the value is `<= 0` / `>= 0` |
130
+ | `integer` | `boolean` | not an integer |
131
+
132
+ ### Boolean
133
+
134
+ | Option | Error when |
135
+ |---|---|
136
+ | `mustBeTrue` | the value is not `true` (the typical case is accepting terms) |
137
+ | `mustBeFalse` | the value is not `false` |
138
+
139
+ ### Enum
140
+
141
+ | Option | Type | What it does |
142
+ |---|---|---|
143
+ | `enumType` | `object` (a TypeScript enum) | the value must be one of the enum values; reverse keys of numeric enums are ignored |
144
+
145
+ ---
146
+
147
+ ## 4. Usage scenarios
148
+
149
+ ### 4.1 Required and optional fields
150
+
151
+ `required` checks only `undefined` and `null`. Everything else is a separate rule (an
152
+ empty string is `notEmpty`, not `required`).
153
+
154
+ ```ts
155
+ class Dto {
156
+ @Validator({ required: true })
157
+ id!: string; // no value -> required error
158
+
159
+ @Validator({ minLength: 3 })
160
+ comment?: string; // no value -> valid; a value -> its length is checked
161
+ }
162
+ ```
163
+
164
+ ### 4.2 Strings
165
+
166
+ ```ts
167
+ class ArticleDto {
168
+ @Validator({ required: true, length: [5, 120], notContains: "<script" })
169
+ title!: string;
170
+
171
+ @Validator({ pattern: /^[a-z0-9-]+$/, message: "Slug: only lowercase letters, digits and hyphens" })
172
+ slug!: string;
173
+
174
+ @Validator({ contains: "@example.com" })
175
+ authorEmail!: string;
176
+ }
177
+ ```
178
+
179
+ `pattern` also accepts a source string: `@Validator({ pattern: "^\\d{4}$" })`.
180
+
181
+ ### 4.3 Formats: email, url, uuid, json, phone, date
182
+
183
+ ```ts
184
+ class ContactDto {
185
+ @Validator({ email: true })
186
+ email?: string; // user@example.com
187
+
188
+ @Validator({ url: true })
189
+ site?: string; // https://bun.sh/docs
190
+
191
+ @Validator({ uuid: true })
192
+ id?: string; // v4 (crypto.randomUUID) and v7 (Bun.randomUUIDv7)
193
+
194
+ @Validator({ json: true })
195
+ payload?: string; // '{"a":1}'
196
+
197
+ @Validator({ phone: true })
198
+ phone?: string; // "+1 (912) 345-67-89": parentheses/spaces/hyphens are allowed
199
+
200
+ @Validator({ type: "date" })
201
+ bornAt?: Date | string; // a Date, an ISO string or a timestamp; Invalid Date is an error
202
+ }
203
+ ```
204
+
205
+ ### 4.4 Numbers and boolean
206
+
207
+ ```ts
208
+ class PaymentDto {
209
+ @Validator({ required: true, range: [1, 1_000_000], integer: true })
210
+ amountCents!: number;
211
+
212
+ @Validator({ negative: true })
213
+ correction?: number;
214
+
215
+ @Validator({ required: true, mustBeTrue: true, message: "Confirm the charge" })
216
+ confirmed!: boolean;
217
+ }
218
+ ```
219
+
220
+ `NaN` does not pass as a number: it gives a `type` error with `{actual}` = `NaN`.
221
+
222
+ ### 4.5 Enum
223
+
224
+ ```ts
225
+ enum Role { Admin = "admin", User = "user" }
226
+ enum Level { Low, High } // a numeric enum
227
+
228
+ class MemberDto {
229
+ @Validator({ required: true, enumType: Role })
230
+ role!: string; // "root" -> error: must be one of: admin, user
231
+
232
+ @Validator({ enumType: Level })
233
+ level?: number; // 0 and 1 are allowed; the string "Low" is NOT allowed
234
+ }
235
+ ```
236
+
237
+ ### 4.6 `custom`: user checks
238
+
239
+ The function gets the field value **and the whole instance**; cross-field checks are
240
+ built on this. The return value is interpreted like this:
241
+
242
+ | Returned | Result |
243
+ |---|---|
244
+ | `true` or nothing (`void`) | the value is valid |
245
+ | `false` | a `custom` error with the standard message |
246
+ | `string` | a `custom` error with this message (wins over `message`) |
247
+ | `ValidationError` | added to the result as is (its own `code` and `property`) |
248
+ | an exception thrown | a `customError` error with the exception text; validation continues |
249
+
250
+ ```ts
251
+ class SignupDto {
252
+ @Validator({
253
+ required: true,
254
+ min: 18,
255
+ custom: (value, instance) => {
256
+ const dto = instance as SignupDto;
257
+ if ((value as number) < 21 && dto.guardianConsent !== true) {
258
+ return "Registration under 21 requires guardian consent";
259
+ }
260
+ return true;
261
+ },
262
+ })
263
+ age!: number;
264
+
265
+ @Validator({ validateIf: (i) => (i as SignupDto).age < 21, required: true })
266
+ guardianConsent?: boolean;
267
+ }
268
+ ```
269
+
270
+ A fully custom error code goes through `ValidationError`:
271
+
272
+ ```ts
273
+ @Validator({
274
+ custom: (value) =>
275
+ RESERVED.has(value as string)
276
+ ? new ValidationError("username", value, "This name is reserved", "reserved")
277
+ : true,
278
+ })
279
+ username!: string;
280
+ ```
281
+
282
+ ### 4.7 `validateIf`: conditional validation
283
+
284
+ If the function returns `false`, **all** rules of this decorator are skipped.
285
+ Handy for fields that are required only in a certain object state:
286
+
287
+ ```ts
288
+ class DeliveryDto {
289
+ @Validator({ required: true, enumType: DeliveryType })
290
+ type!: string;
291
+
292
+ // the address is required only for courier delivery
293
+ @Validator({ validateIf: (i) => (i as DeliveryDto).type === "courier", required: true, notEmpty: true })
294
+ address?: string;
295
+ }
296
+ ```
297
+
298
+ ### 4.8 Several decorators on one field
299
+
300
+ All of them run and their errors add up. This lets you split rules with different
301
+ messages or conditions:
302
+
303
+ ```ts
304
+ class LoginDto {
305
+ @Validator({ minLength: 5, message: "The login is too short" })
306
+ @Validator({ contains: "@corp", message: "The login must be a corporate one" })
307
+ login!: string;
308
+ }
309
+ ```
310
+
311
+ ### 4.9 Nested objects and arrays (`nested`)
312
+
313
+ The field value is checked recursively as a class with its own decorators.
314
+ Errors come with the full path: `address.city`, `items[1].name`.
315
+
316
+ ```ts
317
+ class AddressDto {
318
+ @Validator({ required: true, notEmpty: true })
319
+ city!: string;
320
+
321
+ @Validator({ pattern: /^\d{6}$/ })
322
+ zip?: string;
323
+ }
324
+
325
+ class OrderDto {
326
+ @Validator({ nested: true, required: true })
327
+ address!: AddressDto;
328
+
329
+ @Validator({ nested: true })
330
+ deliveryPoints: AddressDto[] = []; // an array is checked element by element
331
+ }
332
+
333
+ const result = Validator.validate(order);
334
+ result.getErrorsFor("address.city"); // errors of the nested field
335
+ result.hasErrorsFor("deliveryPoints[1].zip");
336
+ ```
337
+
338
+ Three modes:
339
+
340
+ - **`nested: true`**: always recurse (for array elements too);
341
+ - **not set**: auto mode: recursion turns on if the value's class has `@Validator`
342
+ decorators;
343
+ - **`nested: false`**: nested checking is off for the field.
344
+
345
+ Behavior details:
346
+ - circular references (`a.next.next === a`) are skipped safely;
347
+ - with several decorators on a field the nested check runs once, so errors are not
348
+ duplicated;
349
+ - depth is not limited, paths accumulate: `a.b.c.d`.
350
+
351
+ ### 4.10 Asynchronous validation
352
+
353
+ `custom` may be `async` (checking whether a name is taken in the database, an external
354
+ API). Such checks work **only** through `validateAsync`:
355
+
356
+ ```ts
357
+ class RegisterDto {
358
+ @Validator({
359
+ custom: async (value) => {
360
+ const taken = await usersRepo.exists(value as string);
361
+ return taken ? "The name is taken" : true;
362
+ },
363
+ })
364
+ username!: string;
365
+ }
366
+
367
+ const result = await Validator.validateAsync(dto);
368
+ ```
369
+
370
+ Execution rules:
371
+ - all synchronous rules run at once, async `custom` checks run after them, sequentially
372
+ (a deterministic error order);
373
+ - a `reject`/exception in an async function is a `customError` error, not a crash;
374
+ - async `custom` inside nested objects is awaited too;
375
+ - if an async `custom` ends up in the **synchronous** `validate()`, the field is not
376
+ silently treated as valid: an `asyncCustomInSyncCall` error is added.
377
+
378
+ ### 4.11 The `type` hint without other rules
379
+
380
+ When only a type check is needed:
381
+
382
+ ```ts
383
+ class RawDto {
384
+ @Validator({ type: "string" }) a: unknown;
385
+ @Validator({ type: "number" }) b: unknown; // NaN does not pass
386
+ @Validator({ type: "boolean" }) c: unknown;
387
+ @Validator({ type: "any" }) d: unknown; // no type check
388
+ }
389
+ ```
390
+
391
+ ### 4.12 Inheritance
392
+
393
+ Parent class rules work in a subclass; subclass rules do not leak into the parent:
394
+
395
+ ```ts
396
+ class BaseDto {
397
+ @Validator({ required: true })
398
+ id!: string;
399
+ }
400
+
401
+ class ChildDto extends BaseDto {
402
+ @Validator({ min: 0 })
403
+ extra!: number;
404
+ }
405
+
406
+ Validator.validate(new ChildDto()); // checks both id and extra
407
+ Validator.validate(new BaseDto()); // only id
408
+ ```
409
+
410
+ ---
411
+
412
+ ## 5. Result: `ValidationResult` and `ValidationError`
413
+
414
+ ```ts
415
+ const result = Validator.validate(dto);
416
+
417
+ result.isValid; // boolean: no rule is violated
418
+ result.errors; // readonly ValidationError[]: all errors at once
419
+ result.getErrorsFor("email"); // errors of a specific field (or an empty array)
420
+ result.getErrorsFor("address.city"); // the full path for nested fields
421
+ result.hasErrorsFor("age"); // boolean
422
+ ```
423
+
424
+ `ValidationError`:
425
+
426
+ | Field | Type | Description |
427
+ |---|---|---|
428
+ | `property` | `string` | the field name; for nested fields the path (`address.city`, `items[2].name`) |
429
+ | `value` | `unknown` | the actual value at check time |
430
+ | `message` | `string` | the ready message (placeholders already substituted) |
431
+ | `code` | `string?` | the machine-readable rule code (`required`, `minLength`, ...) |
432
+
433
+ The "field → errors" index is built lazily on the first `getErrorsFor`/`hasErrorsFor`,
434
+ so a valid object pays no allocations for it.
435
+
436
+ ---
437
+
438
+ ## 6. Error messages and placeholders
439
+
440
+ Template priority: **the decorator's local `message` > global (`setDefaultMessages`) >
441
+ built-in**. A string returned from `custom` wins over all of them.
442
+ Built-in messages are English; `RU_VALIDATION_MESSAGES` is a Russian set for `setDefaultMessages`.
443
+
444
+ ```ts
445
+ // Globally (once at application start):
446
+ Validator.setDefaultMessages({
447
+ required: "{property} is required",
448
+ minLength: "At least {min} characters",
449
+ });
450
+
451
+ // Locally (for a specific rule):
452
+ @Validator({ pattern: /^\d{6}$/, message: "A ZIP code is exactly 6 digits, got: {value}" })
453
+ zip!: string;
454
+
455
+ // Reset to the built-in ones (handy in tests):
456
+ Validator.resetDefaultMessages();
457
+ ```
458
+
459
+ Placeholders:
460
+
461
+ | Placeholder | Substituted in | Value |
462
+ |---|---|---|
463
+ | `{property}` | all | the field name/path |
464
+ | `{value}` | all | the current value |
465
+ | `{min}` / `{max}` | `minLength`, `maxLength`, `length`, `min`, `max`, `range` | the bounds |
466
+ | `{pattern}` | `pattern` | the regular expression source |
467
+ | `{contains}` | `contains`, `notContains` | the substring looked for |
468
+ | `{allowed}` | `enum` | a comma-separated list of allowed values |
469
+ | `{expected}` / `{actual}` | `type` | the expected and actual type |
470
+ | `{error}` | `customError` | the text of the caught exception |
471
+
472
+ Unknown placeholders stay in the text as is (no exceptions).
473
+ `setDefaultMessages` also accepts your own codes, so you can set a template for a code
474
+ from your own `ValidationError`.
475
+
476
+ ---
477
+
478
+ ## 7. Error codes
479
+
480
+ | Code | When |
481
+ |---|---|
482
+ | `required` | the value is `undefined`/`null` with `required: true` |
483
+ | `notEmpty`, `minLength`, `maxLength`, `length` | the string length |
484
+ | `contains`, `notContains`, `pattern` | the string content |
485
+ | `email`, `url`, `uuid`, `json`, `phone`, `date` | the format |
486
+ | `type` | the value has the wrong type (including `NaN` for number rules) |
487
+ | `min`, `max`, `range`, `positive`, `negative`, `integer` | numeric bounds |
488
+ | `mustBeTrue`, `mustBeFalse` | boolean |
489
+ | `enum` | the value is not in `enumType` |
490
+ | `custom` | `custom` returned `false` or a string |
491
+ | `customError` | `custom` or `validateIf` threw an exception |
492
+ | `asyncCustomInSyncCall` | an async `custom` was called through the synchronous `validate()` |
493
+
494
+ The constants are available as `ValidationCodes.minLength` and so on; use them instead
495
+ of string literals:
496
+
497
+ ```ts
498
+ import { ValidationCodes } from "@/library/validation";
499
+
500
+ if (result.getErrorsFor("age").some((e) => e.code === ValidationCodes.min)) { ... }
501
+ ```
502
+
503
+ ---
504
+
505
+ ## 8. Performance and binary build
506
+
507
+ - **The class plan is compiled once.** On the first `validate` the rules are collected
508
+ from the metadata, `pattern` strings are compiled into RegExp, and enum values go into
509
+ a `Set` (O(1) membership checks). Everything is cached in a `WeakMap` by constructor,
510
+ so classes are not kept from GC.
511
+ - **The hot path barely allocates.** For a valid object only the (empty) error array
512
+ and the `ValidationResult` are created. Message parameter objects are created only
513
+ on the error path.
514
+ - **Binary.** No reflection, `eval` or dynamic imports; Bun runs the standard decorators
515
+ natively. The framework CI (`run ci`) compiles binaries that use this module.
516
+
517
+ ---
518
+
519
+ ## 9. Security and resilience
520
+
521
+ - **Prototype pollution.** The engine reads only the fields declared by decorators
522
+ (service keys like `__proto__` are never traversed); walking passed objects
523
+ (`enumType`, the message dictionary) uses `Object.prototype.hasOwnProperty.call`.
524
+ - **Exceptions in user code.** `custom` and `validateIf` are wrapped in try/catch: an
525
+ exception becomes a `customError` error, and the other fields are still checked.
526
+ - **Async in sync.** A Promise from `custom` in the synchronous `validate()` is an
527
+ explicit `asyncCustomInSyncCall` error, not a silent "valid".
528
+ - **Cycles in the object graph** with `nested` do not loop the validation.
529
+ - **No `eval`** and no dynamic compilation; `pattern: string` is the regular `RegExp`
530
+ constructor.
531
+ - **Length before content.** Exceeding `maxLength`/the upper `length` does not run the
532
+ regex and format parsers of the same decorator. The built-in `email` works with linear
533
+ passes without backtracking; the accepted grammar is unchanged.
534
+ User `pattern`, `custom` and `validateIf` stay the responsibility of the rule author:
535
+ an upper bound does not prove an arbitrary regex is safe.
536
+ - A decorator on a static or private (`#x`) field throws immediately when the class is
537
+ declared (fail-fast); such fields are not supported.
538
+
539
+ ---
540
+
541
+ ## 10. Limits and anti-patterns
542
+
543
+ **There is no `Reflect.getMetadata("design:type")`, on purpose.** Inferring the field
544
+ type automatically needs `emitDecoratorMetadata` and the external `reflect-metadata`
545
+ package, which breaks the framework principles (no dependencies, no reflection,
546
+ binary). The type is inferred from the rules or set with `type`.
547
+
548
+ **Anti-patterns:**
549
+
550
+ ```ts
551
+ // BAD: heavy logic in validateIf: it runs on every validation
552
+ @Validator({ validateIf: (i) => expensiveComputation(i), required: true })
553
+
554
+ // BAD: a database query in a synchronous custom returns a Promise,
555
+ // and the field gets an asyncCustomInSyncCall error
556
+ @Validator({ custom: (v) => usersRepo.exists(v) }) // use validateAsync
557
+
558
+ // BAD: mutating the instance inside custom: validation must be pure
559
+ @Validator({ custom: (v, i) => { (i as Dto).normalized = v.trim(); return true; } })
560
+
561
+ // BAD: one giant custom instead of the built-in rules
562
+ // loses the error codes and message customization
563
+ @Validator({ custom: (v) => typeof v === "string" && v.length >= 3 && v.includes("@") })
564
+ // GOOD:
565
+ @Validator({ minLength: 3, contains: "@" })
566
+ ```
567
+
568
+ **What the module does not do:**
569
+ - it does not transform values (trim, type conversion), it only checks;
570
+ - it does not validate private (`#field`) and static fields;
571
+ - it does not limit nesting depth (cycles are still safe).
572
+
573
+ The module file map is in [README.md](README.md). Scenarios with real assertions are in
574
+ [test/validation.test.ts](test/validation.test.ts).
575
+
576
+ Regressions of string limits and email compatibility:
577
+ [test/validation.string-limits.test.ts](test/validation.string-limits.test.ts).
578
+ The HTTP 400 check and rate limiting before binding:
579
+ [validation-admission.test.ts](../../core/http/test/validation-admission.test.ts).
@@ -0,0 +1,37 @@
1
+ /**
2
+ * One validation error: which field, which value and why it failed.
3
+ *
4
+ * Instances are created by the validation engine, but they can also be created
5
+ * by hand, for example returned from a `custom` function to fully control
6
+ * `code` and `message`:
7
+ *
8
+ * ```ts
9
+ * @Validator({
10
+ * custom: (value) =>
11
+ * isReserved(value)
12
+ * ? new ValidationError("username", value, "This name is reserved", "reserved")
13
+ * : true,
14
+ * })
15
+ * username!: string;
16
+ * ```
17
+ */
18
+ export class ValidationError {
19
+ /**
20
+ * @param property Field name. For nested objects, a dotted path
21
+ * (`address.city`); for arrays, with an index (`items[2].name`).
22
+ * @param value Actual field value at check time.
23
+ * @param message Human-readable message (placeholders already substituted).
24
+ * @param code Machine-readable rule code, for example `"required"` or `"minLength"`.
25
+ */
26
+ constructor(
27
+ readonly property: string,
28
+ readonly value: unknown,
29
+ readonly message: string,
30
+ readonly code?: string,
31
+ ) {}
32
+
33
+ /** Short `property: message` form, handy for logs. */
34
+ toString(): string {
35
+ return `${this.property}: ${this.message}`;
36
+ }
37
+ }
@@ -0,0 +1,63 @@
1
+ import type { ValidationError } from "./ValidationError";
2
+
3
+ const NO_ERRORS: readonly ValidationError[] = Object.freeze([]);
4
+
5
+ /**
6
+ * Validation outcome of an instance: all errors found at once.
7
+ *
8
+ * The engine does not stop at the first error: `errors` holds the full list of
9
+ * problems across all fields, including nested objects.
10
+ *
11
+ * ```ts
12
+ * const result = Validator.validate(dto);
13
+ * if (!result.isValid) {
14
+ * for (const error of result.getErrorsFor("email")) console.log(error.message);
15
+ * }
16
+ * ```
17
+ */
18
+ export class ValidationResult {
19
+ /**
20
+ * "Field -> errors" index, built lazily on the first `getErrorsFor`/`hasErrorsFor`
21
+ * call so a valid object pays no allocations (the hot path is `isValid === true`).
22
+ */
23
+ private index?: Map<string, ValidationError[]>;
24
+
25
+ /** @param errors All errors collected in one validation pass. */
26
+ constructor(readonly errors: readonly ValidationError[]) {}
27
+
28
+ /** `true` if no rule is violated. */
29
+ get isValid(): boolean {
30
+ return this.errors.length === 0;
31
+ }
32
+
33
+ /**
34
+ * Errors of a specific field. For nested fields pass the full path:
35
+ * `getErrorsFor("address.city")`.
36
+ *
37
+ * @returns The field's errors; an empty (frozen, shared) array if there are none.
38
+ */
39
+ getErrorsFor(property: string): readonly ValidationError[] {
40
+ return this.buildIndex().get(property) ?? NO_ERRORS;
41
+ }
42
+
43
+ /** Whether the `property` field has at least one error. */
44
+ hasErrorsFor(property: string): boolean {
45
+ return this.buildIndex().has(property);
46
+ }
47
+
48
+ private buildIndex(): Map<string, ValidationError[]> {
49
+ if (!this.index) {
50
+ const index = new Map<string, ValidationError[]>();
51
+ for (const error of this.errors) {
52
+ const bucket = index.get(error.property);
53
+ if (bucket) {
54
+ bucket.push(error);
55
+ } else {
56
+ index.set(error.property, [error]);
57
+ }
58
+ }
59
+ this.index = index;
60
+ }
61
+ return this.index;
62
+ }
63
+ }