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,218 @@
1
+ import type { RedisClient } from "bun";
2
+ import type { SessionState } from "../session-manager";
3
+ import { createInstanceId, type WebSocketAdapter, type WebSocketAdapterHooks } from "./adapter.interface";
4
+ import { RedisWebSocketOperations } from "./redis-operations";
5
+ import { RedisRoomDeliveryStore, type RedisReliableDeliveryOptions } from "./redis-delivery-store";
6
+ import { KEYS, UPDATE_ROOMS, DELETE_ROOMS } from "./redis-delivery-scripts";
7
+
8
+ export interface RedisWebSocketAdapterOptions {
9
+ /** Optional host-owned command client isolating delivery from session I/O; same logical store required. */
10
+ readonly deliveryClient?: RedisClient;
11
+ /** Shared by the nodes of one application; isolate unrelated applications. */
12
+ readonly keyPrefix?: string;
13
+ /** Initial-write/deletion protection window, milliseconds. Default 60,000. */
14
+ readonly writeProtectionMs?: number;
15
+ /** Maximum encoded session record. Default 8 MiB. */
16
+ readonly maxSessionBytes?: number;
17
+ /** Maximum pub/sub packet bytes. Default 1 MiB. */
18
+ readonly maxPublishBytes?: number;
19
+ readonly operationTimeoutMs?: number;
20
+ readonly maxPendingOperations?: number;
21
+ readonly reliable?: RedisReliableDeliveryOptions;
22
+ }
23
+
24
+ // One Redis key per SID. The record survives until both reconnect TTL and the
25
+ // initial-write window have elapsed. Expiring a key can therefore never make an
26
+ // old snapshot eligible for an initial write again. All revision checks and
27
+ // writes execute atomically in the same Lua invocation.
28
+ const SAVE = KEYS + `
29
+ local raw = redis.call('GET', KEYS[1])
30
+ local next = cjson.decode(ARGV[1])
31
+ local expected = tonumber(ARGV[2])
32
+ local window = tonumber(ARGV[3])
33
+ local clock = redis.call('TIME')
34
+ local now = tonumber(clock[1]) * 1000 + math.floor(tonumber(clock[2]) / 1000)
35
+ if raw then
36
+ local current = cjson.decode(raw)
37
+ if current.deleted or next.revision <= current.revision then return 0 end
38
+ if expected >= 0 and expected ~= current.revision then return 0 end
39
+ if next.createdAt ~= current.createdAt or next.creationToken ~= current.creationToken then return 0 end
40
+ else
41
+ if expected > 0 or next.revision ~= 1 then return 0 end
42
+ if next.createdAt <= now - window or next.createdAt > now + 1000 then return 0 end
43
+ end
44
+ local ttl = math.ceil(math.max(next.expiresAt, next.createdAt + window) - now)
45
+ if ttl <= 0 then return 0 end
46
+ ${UPDATE_ROOMS}
47
+ redis.call('SET', KEYS[1], cjson.encode({revision=next.revision, createdAt=next.createdAt, creationToken=next.creationToken, state=ARGV[1]}), 'PX', ttl)
48
+ return 1
49
+ `;
50
+
51
+ const DELETE = KEYS + `
52
+ local raw = redis.call('GET', KEYS[1])
53
+ local current = raw and cjson.decode(raw) or nil
54
+ local revision = tonumber(ARGV[1])
55
+ local clock = redis.call('TIME')
56
+ local now = tonumber(clock[1]) * 1000 + math.floor(tonumber(clock[2]) / 1000)
57
+ if current and revision >= 0 and revision < current.revision then return 0 end
58
+ if revision < 0 then revision = current and current.revision + 1 or 1 end
59
+ ${DELETE_ROOMS}
60
+ local created = current and current.createdAt or now
61
+ local ttl = math.ceil(created + tonumber(ARGV[2]) - now)
62
+ if ttl <= 0 then redis.call('DEL', KEYS[1]); return 1 end
63
+ redis.call('SET', KEYS[1], cjson.encode({revision=revision, createdAt=created, deleted=true}), 'PX', ttl)
64
+ return 1
65
+ `;
66
+
67
+ /**
68
+ * Redis/Valkey transport and atomic session storage using the host's native Bun
69
+ * RedisClient. The adapter owns only a duplicate required by Redis pub/sub mode;
70
+ * it never closes the supplied command client or clears application keys.
71
+ */
72
+ export class RedisWebSocketAdapter implements WebSocketAdapter {
73
+ readonly name = "redis";
74
+ readonly instanceId = createInstanceId("ws-redis");
75
+ readonly reliableRooms: RedisRoomDeliveryStore;
76
+ private readonly operations: RedisWebSocketOperations;
77
+ private readonly prefix: string;
78
+ private readonly channel: string;
79
+ private readonly writeProtectionMs: number;
80
+ private readonly maxSessionBytes: number;
81
+ private readonly maxPublishBytes: number;
82
+ private readonly deliveryClient: RedisClient;
83
+ private subscriber?: RedisClient;
84
+ private hooks?: WebSocketAdapterHooks;
85
+
86
+ constructor(private readonly client: RedisClient, options: RedisWebSocketAdapterOptions = {}) {
87
+ if (options.deliveryClient !== undefined && (!options.deliveryClient || typeof options.deliveryClient.send !== "function")) throw new Error("Invalid WebSocket deliveryClient.");
88
+ this.deliveryClient = options.deliveryClient ?? client;
89
+ this.prefix = options.keyPrefix ?? "bazis:ws";
90
+ if (!/^[a-zA-Z0-9:_-]{1,64}$/.test(this.prefix)) throw new Error("Invalid WebSocket Redis keyPrefix.");
91
+ this.channel = `${this.prefix}:broadcast`;
92
+ this.writeProtectionMs = positive(options.writeProtectionMs, 60_000, "writeProtectionMs");
93
+ this.maxSessionBytes = positive(options.maxSessionBytes, 8 * 1024 * 1024, "maxSessionBytes");
94
+ this.maxPublishBytes = positive(options.maxPublishBytes, 1024 * 1024, "maxPublishBytes");
95
+ this.operations = new RedisWebSocketOperations(client,
96
+ positive(options.operationTimeoutMs, 2000, "operationTimeoutMs"),
97
+ positive(options.maxPendingOperations, 64, "maxPendingOperations"));
98
+ this.reliableRooms = new RedisRoomDeliveryStore({
99
+ send: (command, args) => this.operations.run(() => this.deliveryClient.send(command, args)),
100
+ }, this.prefix, options.reliable);
101
+ }
102
+
103
+ async initialize(hooks: WebSocketAdapterHooks): Promise<void> {
104
+ if (this.subscriber) throw new Error("WebSocket Redis adapter is already initialized.");
105
+ if (this.deliveryClient !== this.client) {
106
+ const key = `${this.prefix}:connection-check:${this.instanceId}`;
107
+ const nonce = crypto.randomUUID();
108
+ await this.operations.send("SET", [key, nonce, "PX", "60000"]);
109
+ try {
110
+ if (await this.operations.run(() => this.deliveryClient.send("GET", [key])) !== nonce) throw new Error("WebSocket DELIVERY_STORE_MISMATCH.");
111
+ } finally { await this.operations.send("DEL", [key]); }
112
+ }
113
+ await this.reliableRooms.initialize();
114
+ let accepting = true;
115
+ let subscriber: RedisClient;
116
+ try {
117
+ subscriber = await this.operations.run(async () => {
118
+ const duplicate = await this.client.duplicate();
119
+ if (!accepting) { duplicate.close(); throw new Error("WebSocket subscription initialization expired."); }
120
+ return duplicate;
121
+ }) as RedisClient;
122
+ } finally { accepting = false; }
123
+ this.subscriber = subscriber;
124
+ this.hooks = hooks;
125
+ try {
126
+ await this.operations.run(() => subscriber.subscribe(this.channel, this.receive));
127
+ } catch (error) {
128
+ this.hooks = undefined;
129
+ this.subscriber = undefined;
130
+ subscriber.close();
131
+ throw error;
132
+ }
133
+ }
134
+
135
+ async publish(topic: string, payload: Uint8Array): Promise<void> {
136
+ if (payload.byteLength > this.maxPublishBytes || topic.length > 1024) throw new Error("WebSocket Redis publish exceeds limits.");
137
+ await this.operations.send("PUBLISH", [this.channel, JSON.stringify({
138
+ source: this.instanceId, topic, payload: Buffer.from(payload).toString("base64"),
139
+ })]);
140
+ }
141
+
142
+ async saveSession(state: SessionState): Promise<void> {
143
+ await this.save(state, -1);
144
+ }
145
+
146
+ async compareAndSwapSession(state: SessionState, expectedRevision: number): Promise<boolean> {
147
+ if (!Number.isSafeInteger(expectedRevision) || expectedRevision < 0) throw new Error("Invalid WebSocket expected revision.");
148
+ return this.save(state, expectedRevision);
149
+ }
150
+
151
+ async loadSession(sid: string): Promise<SessionState | null> {
152
+ const raw = await this.operations.send("GET", [this.key(sid)]) as string | null;
153
+ if (raw === null) return null;
154
+ if (Buffer.byteLength(raw) > this.maxSessionBytes * 2 + 2048) throw new Error("Stored WebSocket session exceeds limits.");
155
+ const record = JSON.parse(raw) as { deleted?: boolean; state?: string };
156
+ // Keep application JSON opaque to Lua/cjson: empty arrays must stay arrays.
157
+ return record.deleted || record.state === undefined ? null : JSON.parse(record.state) as SessionState;
158
+ }
159
+
160
+ async deleteSession(sid: string, revision?: number): Promise<void> {
161
+ if (revision !== undefined && (!Number.isSafeInteger(revision) || revision < 0)) throw new Error("Invalid WebSocket deletion revision.");
162
+ await this.operations.send("EVAL", [DELETE, "1", this.key(sid), String(revision ?? -1), String(this.writeProtectionMs), this.prefix, sid]);
163
+ }
164
+
165
+ async healthCheck(): Promise<void> {
166
+ if (await this.operations.send("PING", []) !== "PONG") throw new Error("WebSocket store unavailable.");
167
+ await this.reliableRooms.healthCheck();
168
+ }
169
+
170
+ async close(): Promise<void> {
171
+ const subscriber = this.subscriber;
172
+ this.subscriber = undefined;
173
+ this.hooks = undefined;
174
+ if (subscriber) {
175
+ try { await this.operations.run(() => subscriber.unsubscribe(this.channel, this.receive)); }
176
+ finally { subscriber.close(); }
177
+ }
178
+ }
179
+
180
+ private async save(state: SessionState, expected: number): Promise<boolean> {
181
+ if (!Number.isSafeInteger(state.revision) || state.revision! < 1
182
+ || !Number.isFinite(state.createdAt) || !Number.isFinite(state.expiresAt)) throw new Error("Invalid WebSocket session version or timestamps.");
183
+ const encoded = JSON.stringify(state);
184
+ if (Buffer.byteLength(encoded) > this.maxSessionBytes) throw new Error("WebSocket session exceeds Redis storage limit.");
185
+ return Number(await this.operations.send("EVAL", [SAVE, "1", this.key(state.sid), encoded, String(expected), String(this.writeProtectionMs), this.prefix])) === 1;
186
+ }
187
+
188
+ private key(sid: string): string {
189
+ if (typeof sid !== "string" || sid.length === 0 || sid.length > 128) throw new Error("Invalid WebSocket session id.");
190
+ return `${this.prefix}:session:${encodeURIComponent(sid)}`;
191
+ }
192
+
193
+ private readonly receive = (message: string): void => {
194
+ if (!this.hooks || message.length > Math.ceil(this.maxPublishBytes * 4 / 3) + 4096) return;
195
+ try {
196
+ const data = JSON.parse(message) as { source?: unknown; topic?: unknown; payload?: unknown; kind?: unknown; sids?: unknown };
197
+ if (data.kind === "deliveries") {
198
+ if (Array.isArray(data.sids) && data.sids.length <= 1000 && data.sids.every((sid) => typeof sid === "string" && sid.length <= 128)) {
199
+ this.hooks.deliveriesReady?.(data.sids);
200
+ }
201
+ return;
202
+ }
203
+ if (typeof data.source !== "string" || data.source === this.instanceId
204
+ || typeof data.topic !== "string" || data.topic.length > 1024 || typeof data.payload !== "string") return;
205
+ const payload = Buffer.from(data.payload, "base64");
206
+ if (payload.byteLength > this.maxPublishBytes) return;
207
+ this.hooks.localPublish(data.topic, payload);
208
+ } catch {
209
+ // Invalid bus envelopes cannot escape a Redis callback into the process.
210
+ }
211
+ };
212
+ }
213
+
214
+ function positive(value: number | undefined, fallback: number, field: string): number {
215
+ if (value === undefined) return fallback;
216
+ if (!Number.isSafeInteger(value) || value < 1) throw new Error(`WebSocket Redis ${field} must be a positive integer.`);
217
+ return value;
218
+ }
@@ -0,0 +1,87 @@
1
+ import type { ClientPacket, ServerPacket } from "../types";
2
+ import { PacketCodecError } from "../packet-codec";
3
+ import type { PacketCodec } from "./packet-codec.interface";
4
+
5
+ /** Binary wire format: 0x01 magic + uint32be length + UTF-8 JSON body */
6
+ const MAGIC = 0x01;
7
+ const HEADER_SIZE = 5;
8
+
9
+ const encoder = new TextEncoder();
10
+ const decoder = new TextDecoder();
11
+
12
+ function wrapJson(json: string): Uint8Array {
13
+ const body = encoder.encode(json);
14
+ const frame = new Uint8Array(HEADER_SIZE + body.length);
15
+ frame[0] = MAGIC;
16
+ new DataView(frame.buffer).setUint32(1, body.length, false);
17
+ frame.set(body, HEADER_SIZE);
18
+ return frame;
19
+ }
20
+
21
+ function unwrapJson(raw: string | Buffer | Uint8Array): string {
22
+ const bytes =
23
+ typeof raw === "string"
24
+ ? encoder.encode(raw)
25
+ : raw instanceof Buffer
26
+ ? new Uint8Array(raw)
27
+ : raw;
28
+
29
+ if (bytes.length < HEADER_SIZE || bytes[0] !== MAGIC) {
30
+ throw new PacketCodecError("Invalid binary packet frame");
31
+ }
32
+
33
+ const length = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint32(1, false);
34
+ const end = HEADER_SIZE + length;
35
+ if (end > bytes.length) {
36
+ throw new PacketCodecError("Truncated binary packet frame");
37
+ }
38
+
39
+ return decoder.decode(bytes.subarray(HEADER_SIZE, end));
40
+ }
41
+
42
+ export class BinaryPacketCodec implements PacketCodec {
43
+ readonly name = "binary";
44
+
45
+ encodeServer(packet: Omit<ServerPacket, "v"> & { v?: 1 }): Uint8Array {
46
+ return wrapJson(JSON.stringify({ v: 1, ...packet }));
47
+ }
48
+
49
+ encodeClient(packet: Omit<ClientPacket, "v"> & { v?: 1 }): Uint8Array {
50
+ return wrapJson(JSON.stringify({ v: 1, ...packet }));
51
+ }
52
+
53
+ decodeClient(raw: string | Buffer | Uint8Array): ClientPacket {
54
+ const text = unwrapJson(raw);
55
+ let parsed: unknown;
56
+ try {
57
+ parsed = JSON.parse(text);
58
+ } catch {
59
+ throw new PacketCodecError("Invalid JSON in binary frame");
60
+ }
61
+
62
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
63
+ throw new PacketCodecError("Packet must be a JSON object");
64
+ }
65
+
66
+ const packet = parsed as Partial<ClientPacket>;
67
+ if (packet.v !== 1) {
68
+ throw new PacketCodecError("Unsupported packet version");
69
+ }
70
+
71
+ if (packet.type !== "event" && packet.type !== "reconnect" && packet.type !== "ping" && packet.type !== "replay-ack") {
72
+ throw new PacketCodecError(`Unknown packet type: ${String(packet.type)}`);
73
+ }
74
+
75
+ return packet as ClientPacket;
76
+ }
77
+
78
+ isBinary(raw: string | Buffer | Uint8Array): boolean {
79
+ if (typeof raw === "string") {
80
+ return false;
81
+ }
82
+ const bytes = raw instanceof Buffer ? new Uint8Array(raw) : raw;
83
+ return bytes.length > 0 && bytes[0] === MAGIC;
84
+ }
85
+ }
86
+
87
+ export const binaryPacketCodec = new BinaryPacketCodec();
@@ -0,0 +1,34 @@
1
+ import type { ClientPacket, ServerPacket } from "../types";
2
+ import {
3
+ decodeClientPacket as decodeJsonClient,
4
+ encodeClientPacket,
5
+ encodeServerPacket,
6
+ PacketCodecError,
7
+ } from "../packet-codec";
8
+ import type { PacketCodec } from "./packet-codec.interface";
9
+
10
+ export class JsonPacketCodec implements PacketCodec {
11
+ readonly name = "json";
12
+
13
+ encodeServer(packet: Omit<ServerPacket, "v"> & { v?: 1 }): string {
14
+ return encodeServerPacket(packet);
15
+ }
16
+
17
+ encodeClient(packet: Omit<ClientPacket, "v"> & { v?: 1 }): string {
18
+ return encodeClientPacket(packet);
19
+ }
20
+
21
+ decodeClient(raw: string | Buffer | Uint8Array): ClientPacket {
22
+ const text = typeof raw === "string" ? raw : new TextDecoder().decode(raw);
23
+ return decodeJsonClient(text);
24
+ }
25
+
26
+ isBinary(raw: string | Buffer | Uint8Array): boolean {
27
+ void raw;
28
+ return false;
29
+ }
30
+ }
31
+
32
+ export const jsonPacketCodec = new JsonPacketCodec();
33
+
34
+ export { PacketCodecError };
@@ -0,0 +1,17 @@
1
+ import type { ClientPacket, ServerPacket } from "../types";
2
+
3
+ export interface PacketCodec {
4
+ readonly name: string;
5
+ encodeServer(packet: Omit<ServerPacket, "v"> & { v?: 1 }): string | Uint8Array;
6
+ encodeClient(packet: Omit<ClientPacket, "v"> & { v?: 1 }): string | Uint8Array;
7
+ decodeClient(raw: string | Buffer | Uint8Array): ClientPacket;
8
+ isBinary(raw: string | Buffer | Uint8Array): boolean;
9
+ }
10
+
11
+ export function payloadToBytes(payload: string | Uint8Array): Uint8Array {
12
+ return typeof payload === "string" ? new TextEncoder().encode(payload) : payload;
13
+ }
14
+
15
+ export function payloadToText(payload: string | Uint8Array): string {
16
+ return typeof payload === "string" ? payload : new TextDecoder().decode(payload);
17
+ }
@@ -0,0 +1,165 @@
1
+ import type { WsMiddleware } from "./middleware";
2
+ import type { BazisSocket } from "./types";
3
+
4
+ // Bun runs TC39 decorators natively, but Symbol.metadata may be absent.
5
+ (Symbol as { metadata?: symbol }).metadata ??= Symbol.for("Symbol.metadata");
6
+
7
+ const GATEWAY_META = Symbol.for("bazis:ws:gateway");
8
+ const HANDLERS_META = Symbol.for("bazis:ws:handlers");
9
+
10
+ /** Per-message rate limit (fixed window). */
11
+ export interface WsRateLimit {
12
+ readonly limit: number;
13
+ readonly windowMs: number;
14
+ /** Custom bucket key; defaults to `${socket.id}:${event}`. */
15
+ readonly key?: (socket: BazisSocket) => string;
16
+ }
17
+
18
+ export interface WebSocketGatewayOptions {
19
+ /** Logical namespace. Default: "/". */
20
+ readonly namespace?: string;
21
+ /** HTTP upgrade path. Default: "/ws" + namespace ("/ws", "/ws/chat"). */
22
+ readonly path?: string;
23
+ /** Allowed origins for the upgrade. Default: app/server origins. */
24
+ readonly cors?: { origins?: string[]; credentials?: boolean };
25
+ /** Gateway-level upgrade middleware. */
26
+ readonly middleware?: WsMiddleware[];
27
+ /** Max inbound payload bytes. Default: 64 KiB. */
28
+ readonly maxPayloadBytes?: number;
29
+ /** Max outbound frame bytes; defaults to maxPayloadBytes. */
30
+ readonly maxOutboundPayloadBytes?: number;
31
+ /** Session TTL for reconnect. Default: 2h. */
32
+ readonly sessionTtlMs?: number;
33
+ }
34
+
35
+ /**
36
+ * Validates/transforms a message body before the handler runs. Return the
37
+ * (possibly coerced) value, or throw to reject. The client receives a generic
38
+ * validation error by default; applications may explicitly opt into exposing
39
+ * details for trusted development environments. Deliberately framework-
40
+ * agnostic: wrap `@/library/validation`, a DTO class, or any schema you like.
41
+ */
42
+ export type WsBodyValidator = (
43
+ body: unknown,
44
+ /** Aborted when message processing is cancelled or times out. */
45
+ signal?: AbortSignal,
46
+ ) => unknown | Promise<unknown>;
47
+
48
+ export interface MessageHandlerDecl {
49
+ readonly propertyKey: string | symbol;
50
+ readonly events: string[];
51
+ readonly rateLimit?: WsRateLimit;
52
+ readonly validate?: WsBodyValidator;
53
+ }
54
+
55
+ interface GatewayCarrier {
56
+ [GATEWAY_META]?: WebSocketGatewayOptions;
57
+ [HANDLERS_META]?: MessageHandlerDecl[];
58
+ }
59
+
60
+ export interface SubscribeMessageOptions {
61
+ readonly rateLimit?: WsRateLimit;
62
+ readonly validate?: WsBodyValidator;
63
+ }
64
+
65
+ /** Marks a class as a WebSocket gateway (analogous to `@Controller`). */
66
+ export function WebSocketGateway(options: WebSocketGatewayOptions = {}) {
67
+ return (_value: abstract new (...args: never[]) => unknown, context: ClassDecoratorContext): void => {
68
+ (context.metadata as GatewayCarrier)[GATEWAY_META] = options;
69
+ };
70
+ }
71
+
72
+ function ownHandlers(carrier: GatewayCarrier): MessageHandlerDecl[] {
73
+ if (!Object.prototype.hasOwnProperty.call(carrier, HANDLERS_META)) {
74
+ carrier[HANDLERS_META] = carrier[HANDLERS_META] ? [...carrier[HANDLERS_META]] : [];
75
+ }
76
+ return carrier[HANDLERS_META]!;
77
+ }
78
+
79
+ /**
80
+ * Subscribes a method to one or more events. The handler is called by
81
+ * positional convention: `(socket, body, ack)` — declare only what you need.
82
+ * Returning a value auto-acks when the client packet carried an `id`.
83
+ */
84
+ export function SubscribeMessage(event: string | readonly string[], options?: SubscribeMessageOptions) {
85
+ return (_value: (this: never, ...args: never[]) => unknown, context: ClassMethodDecoratorContext): void => {
86
+ if (context.static) {
87
+ throw new Error("@SubscribeMessage cannot be applied to static methods.");
88
+ }
89
+ ownHandlers(context.metadata as GatewayCarrier).push({
90
+ propertyKey: context.name,
91
+ events: Array.isArray(event) ? [...event] : [event as string],
92
+ ...(options?.rateLimit ? { rateLimit: options.rateLimit } : {}),
93
+ ...(options?.validate ? { validate: options.validate } : {}),
94
+ });
95
+ };
96
+ }
97
+
98
+ export function getWebSocketGatewayMetadata(ctor: object): WebSocketGatewayOptions | undefined {
99
+ const metadata = (ctor as { [key: symbol]: unknown })[Symbol.metadata as unknown as symbol] as
100
+ | GatewayCarrier
101
+ | undefined;
102
+ return metadata?.[GATEWAY_META];
103
+ }
104
+
105
+ export function getGatewayMessageHandlers(ctor: object): readonly MessageHandlerDecl[] {
106
+ const metadata = (ctor as { [key: symbol]: unknown })[Symbol.metadata as unknown as symbol] as
107
+ | GatewayCarrier
108
+ | undefined;
109
+ return metadata?.[HANDLERS_META] ?? [];
110
+ }
111
+
112
+ /** Normalizes a namespace to a leading-slash form ("chat" -> "/chat", "" -> "/"). */
113
+ export function normalizeNamespace(namespace: string | undefined): string {
114
+ const value = namespace?.trim();
115
+ if (!value || value === "/") {
116
+ return "/";
117
+ }
118
+ const normalized = value.startsWith("/") ? value : `/${value}`;
119
+ if (normalized.includes("\0") || normalized.includes("?") || normalized.includes("#")) {
120
+ throw new Error("WebSocket namespace contains an invalid separator.");
121
+ }
122
+ return normalized;
123
+ }
124
+
125
+ export interface ResolvedGatewayOptions {
126
+ readonly namespace: string;
127
+ readonly path: string;
128
+ readonly cors?: { origins?: string[]; credentials?: boolean };
129
+ readonly middleware: WsMiddleware[];
130
+ readonly maxPayloadBytes: number;
131
+ readonly maxOutboundPayloadBytes?: number;
132
+ readonly sessionTtlMs: number;
133
+ }
134
+
135
+ const DEFAULT_MAX_PAYLOAD_BYTES = 64 * 1024;
136
+ const DEFAULT_SESSION_TTL_MS = 2 * 60 * 60 * 1000;
137
+
138
+ export function resolveWebSocketGatewayOptions(options: WebSocketGatewayOptions): ResolvedGatewayOptions {
139
+ const namespace = normalizeNamespace(options.namespace);
140
+ const path = options.path ?? (namespace === "/" ? "/ws" : `/ws${namespace}`);
141
+ if (!path.startsWith("/") || path.includes("\0") || path.includes("?") || path.includes("#")) {
142
+ throw new Error(`WebSocket path "${path}" must be an absolute path without query/hash separators.`);
143
+ }
144
+ const maxPayloadBytes = positiveOption(options.maxPayloadBytes, DEFAULT_MAX_PAYLOAD_BYTES, "maxPayloadBytes");
145
+ const sessionTtlMs = positiveOption(options.sessionTtlMs, DEFAULT_SESSION_TTL_MS, "sessionTtlMs");
146
+ return {
147
+ namespace,
148
+ path,
149
+ ...(options.cors ? { cors: options.cors } : {}),
150
+ middleware: options.middleware ?? [],
151
+ maxPayloadBytes,
152
+ maxOutboundPayloadBytes: positiveOption(options.maxOutboundPayloadBytes, maxPayloadBytes, "maxOutboundPayloadBytes"),
153
+ sessionTtlMs,
154
+ };
155
+ }
156
+
157
+ function positiveOption(value: number | undefined, fallback: number, field: string): number {
158
+ if (value === undefined) {
159
+ return fallback;
160
+ }
161
+ if (!Number.isFinite(value) || value <= 0) {
162
+ throw new Error(`WebSocket ${field} must be a positive number.`);
163
+ }
164
+ return Math.floor(value);
165
+ }
@@ -0,0 +1,126 @@
1
+ import {
2
+ getGatewayMessageHandlers,
3
+ getWebSocketGatewayMetadata,
4
+ resolveWebSocketGatewayOptions,
5
+ type WsBodyValidator,
6
+ type WsRateLimit,
7
+ } from "./decorators";
8
+ import type { WsMiddleware } from "./middleware";
9
+
10
+ export interface CompiledWsHandler {
11
+ events: string[];
12
+ handler: (...args: unknown[]) => unknown;
13
+ handlerName: string | symbol;
14
+ /** Declared parameter count; drives positional `(socket, body, ack)` binding. */
15
+ arity: number;
16
+ rateLimit?: WsRateLimit;
17
+ validate?: WsBodyValidator;
18
+ }
19
+
20
+ export interface RegisteredNamespace {
21
+ namespace: string;
22
+ path: string;
23
+ gatewayInstance: unknown;
24
+ handlers: Map<string, CompiledWsHandler>;
25
+ middleware: WsMiddleware[];
26
+ corsOrigins: string[];
27
+ maxPayloadBytes: number;
28
+ maxOutboundPayloadBytes?: number;
29
+ sessionTtlMs: number;
30
+ }
31
+
32
+ /**
33
+ * Compiles gateway instances into per-namespace handler maps (event -> handler).
34
+ * Handler binding is positional `(socket, body, ack)` by arity — no parameter
35
+ * decorators (TC39 decorators do not support them).
36
+ */
37
+ export class WebSocketExplorer {
38
+ public explore(gatewayInstances: readonly unknown[]): RegisteredNamespace[] {
39
+ const namespaces: RegisteredNamespace[] = [];
40
+ const namespacesByName = new Map<string, string>();
41
+ const namespacesByPath = new Map<string, string>();
42
+
43
+ for (const instance of gatewayInstances) {
44
+ const gatewayClass = (instance as { constructor: object } | null)?.constructor;
45
+ if (!gatewayClass) {
46
+ continue;
47
+ }
48
+ const metadata = getWebSocketGatewayMetadata(gatewayClass);
49
+ if (!metadata) {
50
+ continue;
51
+ }
52
+
53
+ const resolved = resolveWebSocketGatewayOptions(metadata);
54
+ const gatewayName = (gatewayClass as { name?: string }).name ?? "?";
55
+ const duplicateNamespace = namespacesByName.get(resolved.namespace);
56
+ if (duplicateNamespace !== undefined) {
57
+ throw new Error(
58
+ `Duplicate WebSocket namespace "${resolved.namespace}" on gateways "${duplicateNamespace}" and "${gatewayName}".`,
59
+ );
60
+ }
61
+ const duplicatePath = namespacesByPath.get(resolved.path);
62
+ if (duplicatePath !== undefined) {
63
+ throw new Error(
64
+ `Duplicate WebSocket path "${resolved.path}" on gateways "${duplicatePath}" and "${gatewayName}".`,
65
+ );
66
+ }
67
+ namespacesByName.set(resolved.namespace, gatewayName);
68
+ namespacesByPath.set(resolved.path, gatewayName);
69
+ const handlers = new Map<string, CompiledWsHandler>();
70
+
71
+ for (const decl of getGatewayMessageHandlers(gatewayClass)) {
72
+ const method = (instance as Record<string | symbol, unknown>)[decl.propertyKey];
73
+ if (typeof method !== "function") {
74
+ continue;
75
+ }
76
+ const compiled: CompiledWsHandler = {
77
+ events: decl.events,
78
+ handler: (method as (...args: unknown[]) => unknown).bind(instance),
79
+ handlerName: decl.propertyKey,
80
+ arity: method.length,
81
+ ...(decl.rateLimit ? { rateLimit: decl.rateLimit } : {}),
82
+ ...(decl.validate ? { validate: decl.validate } : {}),
83
+ };
84
+ if (decl.rateLimit !== undefined) {
85
+ assertRateLimit(decl.rateLimit, gatewayName, decl.propertyKey);
86
+ }
87
+ for (const event of decl.events) {
88
+ if (typeof event !== "string" || event.trim().length === 0) {
89
+ throw new Error(`@SubscribeMessage event on ${gatewayName}.${String(decl.propertyKey)} must not be empty.`);
90
+ }
91
+ if (handlers.has(event)) {
92
+ throw new Error(
93
+ `Duplicate @SubscribeMessage("${event}") in gateway "${(gatewayClass as { name?: string }).name ?? "?"}"`,
94
+ );
95
+ }
96
+ handlers.set(event, compiled);
97
+ }
98
+ }
99
+
100
+ namespaces.push({
101
+ namespace: resolved.namespace,
102
+ path: resolved.path,
103
+ gatewayInstance: instance,
104
+ handlers,
105
+ middleware: resolved.middleware,
106
+ // Empty means "inherit the server/module default". An explicit
107
+ // gateway wildcard remains ["*"] and deliberately overrides it.
108
+ corsOrigins: resolved.cors?.origins ? [...resolved.cors.origins] : [],
109
+ maxPayloadBytes: resolved.maxPayloadBytes,
110
+ maxOutboundPayloadBytes: resolved.maxOutboundPayloadBytes,
111
+ sessionTtlMs: resolved.sessionTtlMs,
112
+ });
113
+ }
114
+
115
+ return namespaces;
116
+ }
117
+ }
118
+
119
+ function assertRateLimit(rateLimit: WsRateLimit, gateway: string, method: string | symbol): void {
120
+ if (!Number.isInteger(rateLimit.limit) || rateLimit.limit <= 0) {
121
+ throw new Error(`WebSocket rateLimit.limit on ${gateway}.${String(method)} must be a positive integer.`);
122
+ }
123
+ if (!Number.isFinite(rateLimit.windowMs) || rateLimit.windowMs <= 0) {
124
+ throw new Error(`WebSocket rateLimit.windowMs on ${gateway}.${String(method)} must be positive.`);
125
+ }
126
+ }