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,1494 @@
1
+ import { OwnedStoreCatalogReader, selectOwnedStoreRows } from "./OwnedStoreCatalog.reader";
2
+ import { canonicalPostgresColumn, canonicalPostgresCheck, exactPostgresType, collectPostgresIndexes, collectPostgresForeignKeys } from "./PostgresSchema.decoder";
3
+ import { AsyncLocalStorage } from "node:async_hooks";
4
+ import { createHash } from "node:crypto";
5
+ import { isIP } from "node:net";
6
+ import { types } from "node:util";
7
+ import { SQL } from "bun";
8
+ import { redactTraceValues } from "../../redaction";
9
+ import type { IntrospectedColumn, IntrospectedSchema, IntrospectedTable } from "../Schema/introspection";
10
+ import { PostgresDialect } from "./PostgresDialect";
11
+ import { currentPostgresScopeOptions, withPostgresScopeOptions, withoutPostgresScopeOptions, observeProviderDispatch, registerPostgresTransactionCapability, withoutProviderDispatchObserver } from "./ormTransactionRuntime";
12
+ import { runPostCommitCallbacks, runRollbackCallbacks } from "./transactionCallbacks";
13
+ import { isUnknownTransactionOutcome, isConfirmedCommitRejection, isConfirmedStatementRejection, notifyTransactionUncertainty, TransactionOutcomeUnknownError } from "./transactionOutcome";
14
+ import {
15
+ assertTransactionCanCommit,
16
+ beginChildTransactionScope,
17
+ endChildTransactionScope,
18
+ markTransactionRollbackOnly,
19
+ mergeTransactionScopeCallbacks,
20
+ createTransactionCallbackScope,
21
+ type TransactionCallbackScope,
22
+ } from "./transactionScopes";
23
+ import type {
24
+ AfterCommitCallback,
25
+ DatabaseProvider,
26
+ DatabaseProviderDiagnostic,
27
+ DatabaseProviderLimits,
28
+ DbExecutor,
29
+ ExecuteResult,
30
+ NotificationSubscription,
31
+ Row,
32
+ SchemaAdmissionScope,
33
+ SqlDialect,
34
+ SqlParam,
35
+ TransactionCallback,
36
+ } from "./types";
37
+ import { OperationDeadline, positiveTimeout } from "./operationDeadline";
38
+ import { OrmTransactionScopeError, OrmDatabaseTimeError, OrmOwnedStoreAdmissionError, PostCommitError, SchemaAdmissionError } from "../errors";
39
+ import type { OrmDatabaseTimeV1 } from "../Transactions/OrmTransaction";
40
+ import type { ExpectedTable, OrmExpectedSchema } from "../Schema/ExpectedSchema";
41
+ import { renderSafeAdditivePostgres } from "../Schema/SafeAdditiveSchema";
42
+ import { introspectedTableKey } from "../Schema/tableKey";
43
+ import { canonicalOwnedStoreRegistryLockPreimageV1, ownedStoreAdvisoryLockV1 } from "../Schema/OwnedStoreCanonical";
44
+ import { failure, registerPostgresOwnedStoreCapability, type OwnedStoreCreateOperationV1, type OwnedStoreIdentityInsertV1, type OwnedStoreSecondaryLockPlanV1, type RegistryLockedOwnedStoreSessionV1, type SecondaryLockedOwnedStoreSessionV1 } from "./ormOwnedStoreRuntime";
45
+ import type { OwnedStoreRegistrySnapshotV1 } from "../Schema/OwnedStoreCatalog";
46
+ import type { OrmCatalogScopeV1 } from "../Schema/OrmOwnedStore";
47
+
48
+ /** Native Bun `sql.listen` shape (PR oven-sh/bun#32089). */
49
+ type BunListen = (
50
+ channel: string,
51
+ handler: (payload: string) => void | Promise<void>,
52
+ ) => Promise<{ unlisten?: () => Promise<void> | void } | void>;
53
+
54
+ /** Minimal Bun SQL query result shape (an array of rows + metadata). */
55
+ type BunSqlResult = Row[] & { affectedRows?: number; count?: number };
56
+
57
+ /** The Bun SQL surface the provider uses (shared by the pool and transactions). */
58
+ interface SqlLike {
59
+ unsafe(query: string, values?: readonly unknown[]): Promise<BunSqlResult>;
60
+ close?(options?: { timeout?: number }): Promise<void>;
61
+ }
62
+ type CancellableSqlQuery = Promise<BunSqlResult> & { cancel?: () => unknown };
63
+
64
+ /** A reserved Bun SQL connection (for a session-level advisory lock). */
65
+ interface ReservedSqlLike extends SqlLike {
66
+ release(): void | Promise<void>;
67
+ }
68
+
69
+ interface AmbientTransaction {
70
+ readonly executor: DbExecutor;
71
+ readonly session: SqlLike;
72
+ readonly owner?: PhysicalTransactionOwner;
73
+ }
74
+
75
+ type OwnerState = "active" | "quarantining" | "quarantined" | "released";
76
+ interface BackendIdentity { readonly pid: string; readonly datid: string; readonly backendStart: string; readonly postmasterStart: string; readonly address: string; readonly port: string; }
77
+ const CANCEL_BACKEND_MATCH = "a.pid=$1::int AND a.datid=$2::oid AND extract(epoch FROM a.backend_start)=$3::numeric AND extract(epoch FROM pg_catalog.pg_postmaster_start_time())=$4::numeric AND host(inet_server_addr())=$5 AND inet_server_port()::text=$6 AND a.usename=current_user AND a.state='active'";
78
+ const CANCEL_INSPECT_SQL = `/* bazis:cancel-inspect */ SELECT extract(epoch FROM a.query_start)::numeric(20,6)::text AS query_start FROM pg_catalog.pg_stat_activity a WHERE ${CANCEL_BACKEND_MATCH}`;
79
+ const CANCEL_SIGNAL_SQL = `/* bazis:cancel-signal */ SELECT pg_catalog.pg_cancel_backend(a.pid) AS signaled FROM pg_catalog.pg_stat_activity a WHERE ${CANCEL_BACKEND_MATCH} AND extract(epoch FROM a.query_start)=$7::numeric`;
80
+ interface PhysicalTransactionOwner {
81
+ readonly session: ReservedSqlLike;
82
+ readonly lifetime: "owned" | "borrowed";
83
+ state: OwnerState;
84
+ backend?: BackendIdentity;
85
+ quarantinePromise?: Promise<void>;
86
+ releasePromise?: Promise<void>;
87
+ unknownOutcome?: TransactionOutcomeUnknownError;
88
+ operation?: OperationDeadline;
89
+ callbacks?: TransactionCallbackScope;
90
+ closePromise?: Promise<void>;
91
+ /** Original native work, never the AbortSignal-facing projection. */
92
+ pending?: Set<Promise<unknown>>;
93
+ rollbackAcknowledged?: boolean;
94
+ backendAbsenceAcknowledged?: boolean;
95
+ }
96
+ interface OwnedStoreAttemptV1 {
97
+ readonly owner: PhysicalTransactionOwner;
98
+ readonly signal: AbortSignal | undefined;
99
+ phase: "starting" | "registryLocked" | "secondaryLocked" | "callbackClosed" | "committing" | "released";
100
+ generation: number;
101
+ poisoned: boolean;
102
+ lockUncertainty: boolean;
103
+ mutationFailure: boolean;
104
+ semanticFailure: boolean;
105
+ busy: boolean;
106
+ readonly pending: Set<Promise<unknown>>;
107
+ createRegistryCalls: number;
108
+ applyCalls: number;
109
+ insertCalls: number;
110
+ }
111
+
112
+ /** PostgreSQL-private trace boundary; the public scope deliberately omits it. */
113
+ type InternalSchemaAdmissionScope = SchemaAdmissionScope & {
114
+ readonly executeSchemaAdmission: (sql: string, operation: string) => ReturnType<SchemaAdmissionScope["execute"]>;
115
+ };
116
+
117
+ /**
118
+ * Advisory lock key for migrations (an arbitrary bigint constant). All
119
+ * application instances use the same key, so concurrent `migrate()` calls are
120
+ * serialized by PostgreSQL.
121
+ */
122
+ const MIGRATION_ADVISORY_LOCK_KEY = 0x6f_73_6e_76; // "bazis"
123
+ const ADMISSION_LOCK_DOMAIN = "bazis.orm.ensure-created/schema-lock/v1\0";
124
+ const admissionLockOwners = new Map<string, string>();
125
+ /** Exact retained reservations are fenced by object identity, not ALS. */
126
+ const physicalOwners = new WeakMap<object, PhysicalTransactionOwner>();
127
+
128
+ /** PostgreSQL-private strict wire parser; no app clock or fallback is permitted. */
129
+ function postgresDatabaseTime(value: unknown): OrmDatabaseTimeV1 {
130
+ if (typeof value !== "number" && typeof value !== "string" && typeof value !== "bigint") throw new OrmDatabaseTimeError();
131
+ const text = String(value);
132
+ if (!/^(?:0|-[1-9][0-9]*|[1-9][0-9]*)$/.test(text)) throw new OrmDatabaseTimeError();
133
+ const milliseconds = Number(text);
134
+ if (!Number.isSafeInteger(milliseconds) || Number.isNaN(new Date(milliseconds).getTime())) throw new OrmDatabaseTimeError();
135
+ return Object.freeze({ instant: Object.freeze(new Date(milliseconds)), epochMilliseconds: milliseconds, precision: "millisecond" });
136
+ }
137
+
138
+
139
+ const MIGRATION_LOCK_UNAVAILABLE: DatabaseProviderDiagnostic = {
140
+ code: "postgres.migration_lock_unavailable",
141
+ severity: "warning",
142
+ message:
143
+ "PostgreSQL advisory migration lock is unavailable because Bun.SQL.reserve() is not present. ORM migrations will run without an inter-process lock; avoid concurrent production startup or upgrade Bun.",
144
+ };
145
+
146
+ export interface PostgresServerTimeouts {
147
+ readonly statementTimeoutMs?: number;
148
+ readonly lockTimeoutMs?: number;
149
+ readonly idleInTransactionTimeoutMs?: number;
150
+ /** PostgreSQL 17+; terminates the session when exceeded. */
151
+ readonly transactionTimeoutMs?: number;
152
+ }
153
+
154
+ export interface PostgresOperationEvent {
155
+ readonly operation: "query" | "execute" | "admission" | "cancellation";
156
+ readonly outcome: "success" | "error" | "unknown";
157
+ readonly durationMs: number;
158
+ readonly pendingNativeOperations: number;
159
+ }
160
+
161
+ export interface PostgresProviderOptions {
162
+ readonly operationTimeoutMs?: number;
163
+ readonly cancellationTimeoutMs?: number;
164
+ /** Server cancellation uses one additional, lazy control connection. */
165
+ readonly cancellationMode?: "server" | "close";
166
+ readonly maxPendingOperations?: number;
167
+ readonly serverTimeouts?: PostgresServerTimeouts;
168
+ /** Completion telemetry contains no SQL, parameters, credentials or error messages. */
169
+ readonly onOperation?: (event: PostgresOperationEvent) => void;
170
+ /** Connection string, e.g. `postgres://user:pass@localhost:5432/db`. */
171
+ readonly url?: string;
172
+ /** Bun SQL options (host/port/user/password/database/max/idleTimeout/...). */
173
+ readonly options?: Record<string, unknown>;
174
+ /** SQL trace callback, for logging/metrics. */
175
+ readonly onSql?: (sql: string, params: readonly SqlParam[]) => void;
176
+ /**
177
+ * SQL trace params are redacted by default. Pass false only for trusted local
178
+ * debugging where raw query values are acceptable.
179
+ */
180
+ readonly redactSqlParams?: boolean;
181
+ /** Optional warning sink for provider diagnostics; defaults to console.warn. */
182
+ readonly onWarning?: (warning: DatabaseProviderDiagnostic) => void;
183
+ }
184
+
185
+ /**
186
+ * PostgreSQL provider on top of the native Bun `SQL`, with no external
187
+ * dependencies (the driver is built into Bun).
188
+ *
189
+ * All queries are parameterized through `unsafe(sql, params)` with `$1, $2, ...`
190
+ * placeholders. A transaction reserves a dedicated pool connection through
191
+ * `sql.begin`, so inside `transaction()` queries go through a scoped executor;
192
+ * this is safe with concurrent scoped contexts (one per request).
193
+ */
194
+ export class PostgresProvider implements DatabaseProvider {
195
+ readonly name = "postgres";
196
+ readonly dialect: SqlDialect = new PostgresDialect();
197
+ readonly limits: DatabaseProviderLimits = {
198
+ maxParametersPerCommand: 32767,
199
+ maxRowsPerInsert: 5000,
200
+ maxParametersPerInList: 10000,
201
+ };
202
+ readonly schemaAdmissionCapability = {
203
+ version: 1 as const,
204
+ provider: "postgres" as const,
205
+ distributedLock: true as const,
206
+ transactionalDdl: true as const,
207
+ exactIntrospection: true as const,
208
+ withSchemaAdmission: <T>(schemas: readonly string[], work: (scope: import("./types").SchemaAdmissionScope) => Promise<T>) => this.withSchemaAdmission(schemas, work),
209
+ };
210
+
211
+ private readonly operationTimeoutMs: number;
212
+ private readonly cancellationTimeoutMs: number;
213
+ private readonly cancellationMode: "server" | "close";
214
+ private readonly controlConfig: Record<string, unknown>;
215
+ private controlSql?: SQL;
216
+ /** Bun may briefly reissue a retired TLS slot before its close callback.
217
+ * After retirement, validate all later leases for this pool's lifetime. */
218
+ private validateWorkerAdmissions = false;
219
+ private validateControlAdmissions = false;
220
+ private readonly reservationRetirements = new WeakMap<object, Promise<void>>();
221
+ private readonly closing = new AbortController();
222
+ private readonly maxPendingOperations: number;
223
+ private readonly serverTimeouts: PostgresServerTimeouts;
224
+ private readonly onOperation?: (event: PostgresOperationEvent) => void;
225
+ private readonly uncertainErrors = new WeakSet<object>();
226
+ private pendingNativeOperations = 0;
227
+ private activeCancellations = 0;
228
+ private unconfirmedCancellations = 0;
229
+ private closed = false;
230
+ private closePromise?: Promise<void>;
231
+ private readonly sql: SQL;
232
+ private readonly onSql?: (sql: string, params: readonly SqlParam[]) => void;
233
+ private readonly redactSqlParams: boolean;
234
+ private readonly onWarning?: (warning: DatabaseProviderDiagnostic) => void;
235
+ private migrationLockFallbackWarned = false;
236
+ /** Ambient transaction of the current async context (for join semantics). */
237
+ private readonly ambient = new AsyncLocalStorage<AmbientTransaction>();
238
+ private readonly transactionCallbacks = new AsyncLocalStorage<TransactionCallbackScope>();
239
+ /** Session reserved by an advisory lock; transactions reuse it to avoid pool starvation. */
240
+ /** Advisory/schema owners are borrowed by nested transactions. Keep their
241
+ * exact physical lifetime, rather than recreating an owner per BEGIN. */
242
+ private readonly lockedSession = new AsyncLocalStorage<PhysicalTransactionOwner>();
243
+ private nextTransactionScopeId = 0;
244
+
245
+ constructor(options: PostgresProviderOptions = {}) {
246
+ this.operationTimeoutMs = positiveTimeout(options.operationTimeoutMs, 30_000, "operationTimeoutMs");
247
+ this.cancellationTimeoutMs = positiveTimeout(options.cancellationTimeoutMs, 5_000, "cancellationTimeoutMs");
248
+ if (options.cancellationMode !== undefined && options.cancellationMode !== "server" && options.cancellationMode !== "close") throw new TypeError("cancellationMode must be server or close.");
249
+ this.cancellationMode = options.cancellationMode ?? "server";
250
+ this.maxPendingOperations = positiveTimeout(options.maxPendingOperations, 256, "maxPendingOperations");
251
+ this.serverTimeouts = Object.freeze({ ...options.serverTimeouts });
252
+ for (const [name, value] of Object.entries(this.serverTimeouts)) positiveTimeout(value, 30_000, name);
253
+ this.onOperation = options.onOperation;
254
+ const config = options.url ?? options.options ?? {};
255
+ this.sql = new SQL(config as never);
256
+ this.controlConfig = { ...(typeof config === "string" ? { url: config } : config), max: 1 };
257
+ this.onSql = options.onSql;
258
+ this.redactSqlParams = options.redactSqlParams !== false;
259
+ this.onWarning = options.onWarning;
260
+ registerPostgresTransactionCapability(this, {
261
+ operationTimeoutMs: this.operationTimeoutMs,
262
+ isOutcomeUncertain: (error) => typeof error === "object" && error !== null && this.uncertainErrors.has(error),
263
+ operationSignal: () => this.ambient.getStore()?.owner?.operation?.signal,
264
+ quarantineOnPendingDispatch: true,
265
+ databaseTime: async () => {
266
+ try {
267
+ return postgresDatabaseTime((await this.query("SELECT floor(extract(epoch FROM clock_timestamp()) * 1000)::bigint AS epoch_ms", []))[0]?.epoch_ms);
268
+ } catch {
269
+ throw new OrmDatabaseTimeError();
270
+ }
271
+ },
272
+ assertScopedClose: async () => {
273
+ const owner = this.ambient.getStore()?.owner;
274
+ if (!owner || owner.state !== "active" || typeof owner.session.close !== "function") throw new Error("PostgreSQL scoped-session close capability is unavailable.");
275
+ // Capture identity on the retained reservation. Socket polling also
276
+ // supports the explicit connection-close fallback.
277
+ await this.ownerCommand(owner, "SET LOCAL client_connection_check_interval = '250ms'");
278
+ const readback = await this.ownerCommand(owner, "SELECT current_setting('client_connection_check_interval') AS interval");
279
+ if (String(readback[0]?.interval ?? "") !== "250ms") throw new Error("PostgreSQL polling readback failed.");
280
+ owner.backend ??= await this.readBackendIdentity(owner.session);
281
+ },
282
+ // Only an ambient transaction session is eligible. `this.sql` is the
283
+ // root pool and is deliberately never reached by this path.
284
+ quarantine: async () => {
285
+ const owner = this.ambient.getStore()?.owner;
286
+ if (!owner || typeof owner.session.close !== "function") {
287
+ throw new Error("PostgreSQL scoped-session close capability is unavailable.");
288
+ }
289
+ return this.quarantineOwner(owner);
290
+ },
291
+ });
292
+ registerPostgresOwnedStoreCapability(this, { withOwnedStoreAdmission: (signal, work) => this.withOwnedStoreAdmissionV1(signal, work) });
293
+ }
294
+
295
+ diagnostics(): readonly DatabaseProviderDiagnostic[] {
296
+ return this.reserveConnection ? [] : [MIGRATION_LOCK_UNAVAILABLE];
297
+ }
298
+
299
+ async query(sql: string, params: readonly SqlParam[]): Promise<Row[]> {
300
+ const ambient = this.ambient.getStore();
301
+ if (ambient) {
302
+ return ambient.executor.query(sql, params);
303
+ }
304
+ const locked = this.lockedSession.getStore();
305
+ return locked ? this.runQuery(locked.session, sql, params) : this.pooledOperation((session) => this.runQuery(session, sql, params));
306
+ }
307
+
308
+ async execute(sql: string, params: readonly SqlParam[]): Promise<ExecuteResult> {
309
+ const ambient = this.ambient.getStore();
310
+ if (ambient) {
311
+ return ambient.executor.execute(sql, params);
312
+ }
313
+ const locked = this.lockedSession.getStore();
314
+ return locked ? this.runExecute(locked.session, sql, params) : this.pooledOperation((session) => this.runExecute(session, sql, params));
315
+ }
316
+
317
+ transaction<T>(work: (tx: DbExecutor) => Promise<T>): Promise<T> {
318
+ // A nested call joins the ambient transaction (the same reserved
319
+ // connection): "Required" semantics.
320
+ const ambient = this.ambient.getStore();
321
+ if (ambient) {
322
+ const callbacks = this.transactionCallbacks.getStore();
323
+ return Promise.resolve().then(() => work(ambient.executor)).catch((error) => {
324
+ // Required joins have no independent rollback. Even if the caller
325
+ // catches a conversion/application error, earlier writes must unwind.
326
+ // Poison only this frame: a containing savepoint can still recover.
327
+ if (callbacks) callbacks.rollbackOnly ??= error ?? new Error("Joined transaction failed.");
328
+ throw error;
329
+ });
330
+ }
331
+ const lockedOwner = this.lockedSession.getStore();
332
+ if (lockedOwner) {
333
+ return this.transactionOnReservedSession(lockedOwner, work);
334
+ }
335
+ const reserve = this.reserveConnection;
336
+ if (reserve) return this.transactionOnOwnedReservedSession(reserve, work);
337
+ // begin reserves a pool connection and passes a scoped sql; ROLLBACK on
338
+ // error is automatic. Concurrent transactions are safe (pool).
339
+ const callbacks = createTransactionCallbackScope();
340
+ let workCompleted = false;
341
+ const transaction = this.sql.begin((scoped: SqlLike) => {
342
+ const executor: DbExecutor = {
343
+ query: (sql, params) => this.runQuery(scoped, sql, params),
344
+ execute: (sql, params) => this.runExecute(scoped, sql, params),
345
+ };
346
+ return this.transactionCallbacks.run(
347
+ callbacks,
348
+ () => this.ambient.run({ executor, session: scoped }, async () => {
349
+ const result = await work(executor);
350
+ assertTransactionCanCommit(callbacks);
351
+ workCompleted = true;
352
+ return result;
353
+ }),
354
+ );
355
+ }) as Promise<T>;
356
+ return transaction.then(
357
+ (result) => this.completeTransaction(result, callbacks.afterCommit),
358
+ (error) => {
359
+ if (workCompleted && !isConfirmedCommitRejection(error)) {
360
+ notifyTransactionUncertainty(callbacks.afterRollback);
361
+ throw this.uncertainty(error);
362
+ }
363
+ return this.completeRollback(error, callbacks.afterRollback);
364
+ },
365
+ );
366
+ }
367
+
368
+ transactionScope<T>(work: (tx: DbExecutor) => Promise<T>): Promise<T> {
369
+ const ambient = this.ambient.getStore();
370
+ if (ambient === undefined) {
371
+ return this.transaction(work);
372
+ }
373
+ const parent = this.transactionCallbacks.getStore();
374
+ if (parent === undefined) {
375
+ return Promise.reject(new Error("PostgreSQL ambient transaction has no transaction callback scope."));
376
+ }
377
+ return this.runTransactionScope(ambient.executor, parent, work);
378
+ }
379
+
380
+ isTransactionActive(): boolean {
381
+ return this.ambient.getStore() !== undefined;
382
+ }
383
+
384
+ afterCommit(callback: AfterCommitCallback): Promise<void> | void {
385
+ const callbacks = this.transactionCallbacks.getStore()?.afterCommit;
386
+ if (callbacks) {
387
+ callbacks.push(callback);
388
+ return;
389
+ }
390
+ return callback();
391
+ }
392
+
393
+ afterRollback(callback: TransactionCallback): void {
394
+ this.transactionCallbacks.getStore()?.afterRollback.push(callback);
395
+ }
396
+
397
+ private completeTransaction<T>(result: T, callbacks: readonly AfterCommitCallback[]): Promise<T> {
398
+ return withoutPostgresScopeOptions(() => this.ambient.exit(() => this.transactionCallbacks.exit(async () => {
399
+ await runPostCommitCallbacks(callbacks);
400
+ return result;
401
+ })));
402
+ }
403
+
404
+ private completeRollback(error: unknown, callbacks: readonly TransactionCallback[]): Promise<never> {
405
+ return withoutPostgresScopeOptions(() => this.ambient.exit(() =>
406
+ this.transactionCallbacks.exit(() => runRollbackCallbacks(callbacks, error)),
407
+ ));
408
+ }
409
+
410
+ async ping(signal?: AbortSignal): Promise<boolean> {
411
+ try {
412
+ await withPostgresScopeOptions({ signal }, () => this.pooledOperation((session) => this.runQuery(session, "SELECT 1", [])));
413
+ return true;
414
+ } catch {
415
+ return false;
416
+ }
417
+ }
418
+
419
+ async introspect(): Promise<IntrospectedSchema> {
420
+ const tableRows = await this.query(
421
+ `SELECT table_schema, table_name
422
+ FROM information_schema.tables
423
+ WHERE table_type = 'BASE TABLE'
424
+ AND table_schema NOT IN ('pg_catalog', 'information_schema')`,
425
+ [],
426
+ );
427
+
428
+ return this.introspectTables(tableRows.map((row) => ({ schema: String(row.table_schema), table: String(row.table_name) })));
429
+ }
430
+
431
+ private async introspectExpected(expected: OrmExpectedSchema, executor: DbExecutor = this): Promise<IntrospectedSchema> {
432
+ const schemas = new Set<string>();
433
+ for (const schema of new Set(expected.tables.map((table) => table.schema))) {
434
+ if ((await executor.query("SELECT 1 FROM pg_namespace WHERE nspname = $1", [schema])).length) schemas.add(schema);
435
+ }
436
+ const result = await this.introspectTables(
437
+ expected.tables.map((table) => ({ schema: table.schema, table: table.table })),
438
+ executor,
439
+ (schema, table) => `${schema}.${table}`,
440
+ true,
441
+ );
442
+ return { ...result, schemas };
443
+ }
444
+
445
+ private async introspectTables(
446
+ targets: readonly { readonly schema: string; readonly table: string }[],
447
+ executor: DbExecutor = this,
448
+ keyOf: (schema: string, table: string) => string = introspectedTableKey,
449
+ exact = false,
450
+ ): Promise<IntrospectedSchema> {
451
+ const tables = new Map<string, IntrospectedTable>();
452
+ for (const target of targets) {
453
+ const table = await this.introspectTable(target.schema, target.table, executor, exact);
454
+ if (table) tables.set(keyOf(target.schema, target.table), table);
455
+ }
456
+ return { tables };
457
+ }
458
+
459
+ private async introspectTable(schema: string, name: string, executor: DbExecutor = this, exact = false): Promise<IntrospectedTable | undefined> {
460
+ const exists = await executor.query("SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = $1 AND c.relname = $2 AND c.relkind IN ('r', 'p')", [schema, name]);
461
+ if (!exists.length) return undefined;
462
+ const columnRows = await executor.query(`SELECT a.attname AS column_name, a.attnotnull AS not_null, format_type(a.atttypid, a.atttypmod) AS type_name, pg_get_expr(d.adbin, d.adrelid) AS default_expr, a.attidentity AS identity_kind
463
+ FROM pg_attribute a JOIN pg_class c ON c.oid = a.attrelid JOIN pg_namespace n ON n.oid = c.relnamespace LEFT JOIN pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum
464
+ WHERE n.nspname = $1 AND c.relname = $2 AND a.attnum > 0 AND NOT a.attisdropped ORDER BY a.attnum`, [schema, name]);
465
+ const primaryRows = await executor.query(`SELECT c.conname AS constraint_name, a.attname AS column_name, k.ord
466
+ FROM pg_constraint c JOIN pg_class t ON t.oid = c.conrelid JOIN pg_namespace n ON n.oid = t.relnamespace JOIN LATERAL unnest(c.conkey) WITH ORDINALITY k(attnum, ord) ON true JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = k.attnum
467
+ WHERE n.nspname = $1 AND t.relname = $2 AND c.contype = 'p' ORDER BY k.ord`, [schema, name]);
468
+ const primaryKey = primaryRows.length ? { name: String(primaryRows[0]!.constraint_name), columns: primaryRows.map((row) => String(row.column_name)) } : undefined;
469
+ const primaryColumns = new Set(primaryKey?.columns ?? []);
470
+ const columns = new Map<string, IntrospectedColumn>(columnRows.map((row) => {
471
+ const normalized = canonicalPostgresColumn(String(row.type_name), row.default_expr == null ? null : String(row.default_expr), String(row.identity_kind ?? ""));
472
+ const column = String(row.column_name);
473
+ return [column, { name: column, notNull: row.not_null === true, isPrimaryKey: primaryColumns.has(column), ...normalized, physicalType: exact ? exactPostgresType(String(row.type_name), normalized.physicalType) : normalized.physicalType }];
474
+ }));
475
+ const indexRows = await executor.query(`SELECT ic.relname AS index_name, ix.indisunique AS is_unique, am.amname AS method, ix.indpred IS NOT NULL AS has_predicate, ix.indisvalid AS is_valid, ix.indisready AS is_ready, ix.indnkeyatts AS key_count, k.ord, a.attname AS column_name, k.attnum = 0 AS is_expression, k.ord > ix.indnkeyatts AS is_include,
476
+ EXISTS (SELECT 1 FROM pg_constraint c WHERE c.conindid = ix.indexrelid AND c.contype IN ('p', 'u')) AS backing_constraint
477
+ FROM pg_index ix JOIN pg_class ic ON ic.oid = ix.indexrelid JOIN pg_class tc ON tc.oid = ix.indrelid JOIN pg_namespace n ON n.oid = tc.relnamespace JOIN pg_am am ON am.oid = ic.relam JOIN LATERAL unnest(ix.indkey) WITH ORDINALITY k(attnum, ord) ON true LEFT JOIN pg_attribute a ON a.attrelid = tc.oid AND a.attnum = k.attnum
478
+ WHERE n.nspname = $1 AND tc.relname = $2 ORDER BY ic.relname, k.ord`, [schema, name]);
479
+ const indexes = collectPostgresIndexes(indexRows, exact);
480
+ const foreignRows = await executor.query(`SELECT c.conname AS constraint_name, ln.nspname AS target_schema, lt.relname AS target_table, la.attname AS column_name, ra.attname AS target_column, k.ord, c.confdeltype AS delete_code, c.confupdtype AS update_code, c.confmatchtype AS match_code, c.condeferrable AS deferrable, c.convalidated AS validated
481
+ FROM pg_constraint c JOIN pg_class t ON t.oid = c.conrelid JOIN pg_namespace n ON n.oid = t.relnamespace JOIN pg_class lt ON lt.oid = c.confrelid JOIN pg_namespace ln ON ln.oid = lt.relnamespace JOIN LATERAL unnest(c.conkey) WITH ORDINALITY k(local_attnum, ord) ON true JOIN LATERAL unnest(c.confkey) WITH ORDINALITY rk(target_attnum, target_ord) ON rk.target_ord = k.ord JOIN pg_attribute la ON la.attrelid = t.oid AND la.attnum = k.local_attnum JOIN pg_attribute ra ON ra.attrelid = lt.oid AND ra.attnum = rk.target_attnum
482
+ WHERE n.nspname = $1 AND t.relname = $2 AND c.contype = 'f' ORDER BY c.conname, k.ord`, [schema, name]);
483
+ const foreignKeys = collectPostgresForeignKeys(foreignRows, exact);
484
+ const checkRows = await executor.query(`SELECT c.conname AS constraint_name, pg_get_constraintdef(c.oid, true) AS definition
485
+ FROM pg_constraint c JOIN pg_class t ON t.oid = c.conrelid JOIN pg_namespace n ON n.oid = t.relnamespace WHERE n.nspname = $1 AND t.relname = $2 AND c.contype = 'c' ORDER BY c.conname`, [schema, name]);
486
+ const checks = checkRows.map((row) => canonicalPostgresCheck(String(row.constraint_name), String(row.definition)));
487
+ return { name, columns, indexes, primaryKey, foreignKeys, checks };
488
+ }
489
+
490
+ async withMigrationLock<T>(work: () => Promise<T>): Promise<T> {
491
+ // Advisory locks are session-reentrant. Reuse the already locked session
492
+ // instead of reserving a second connection and waiting on ourselves.
493
+ if (this.lockedSession.getStore()) {
494
+ return work();
495
+ }
496
+ const reserve = this.reserveConnection;
497
+ if (!reserve) {
498
+ this.emitWarning(MIGRATION_LOCK_UNAVAILABLE);
499
+ return work();
500
+ }
501
+ const reserved = await reserve();
502
+ const owner: PhysicalTransactionOwner = { session: reserved, lifetime: "borrowed", state: "active" }; physicalOwners.set(reserved, owner);
503
+ try {
504
+ await reserved.unsafe("SELECT pg_advisory_lock($1)", [MIGRATION_ADVISORY_LOCK_KEY]);
505
+ return await this.lockedSession.run(owner, work);
506
+ } finally {
507
+ try {
508
+ if (!owner.unknownOutcome && (owner.state === "active" || owner.rollbackAcknowledged)) await this.unlockOwner(owner, [MIGRATION_ADVISORY_LOCK_KEY]);
509
+ } finally {
510
+ await this.releaseOwner(owner);
511
+ }
512
+ }
513
+ }
514
+
515
+ private async withSchemaAdmission<T>(schemas: readonly string[], work: (scope: SchemaAdmissionScope) => Promise<T>): Promise<T> {
516
+ const normalized = [...new Set(schemas.map((schema) => schema.trim() || "public"))].sort((a, b) => Buffer.compare(Buffer.from(a, "utf8"), Buffer.from(b, "utf8")));
517
+ const keys = normalized.map((schema) => ({ schema, key: createHash("sha256").update(ADMISSION_LOCK_DOMAIN + schema, "utf8").digest().readBigInt64BE(0) }));
518
+ for (const { schema, key } of keys) {
519
+ const keyText = key.toString();
520
+ const owner = admissionLockOwners.get(keyText);
521
+ if (owner !== undefined && owner !== schema) {
522
+ throw new SchemaAdmissionError("ORM_SCHEMA_LOCK_KEY_COLLISION", "PostgreSQL schema admission lock key collision was detected.");
523
+ }
524
+ admissionLockOwners.set(keyText, schema);
525
+ }
526
+ const ambient = this.ambient.getStore();
527
+ if (ambient) {
528
+ this.assertActiveOwner(ambient.session);
529
+ for (const { key } of keys) await ambient.session.unsafe("SELECT pg_advisory_xact_lock($1)", [key]);
530
+ return work(this.createSchemaAdmissionScope(ambient.executor, ambient.session));
531
+ }
532
+ const active = this.lockedSession.getStore();
533
+ if (active) {
534
+ try {
535
+ return await this.transaction(async (tx) => {
536
+ this.assertActiveOwner(active.session);
537
+ for (const { key } of keys) await active.session.unsafe("SELECT pg_advisory_xact_lock($1)", [key]);
538
+ return work(this.createSchemaAdmissionScope(tx, active.session));
539
+ });
540
+ } finally { /* collision ownership is process-lifetime, matching the existing hash-domain guard */ }
541
+ }
542
+ const reserve = this.reserveConnection;
543
+ if (!reserve) {
544
+ throw new SchemaAdmissionError("ORM_SCHEMA_LOCK_UNAVAILABLE", "PostgreSQL schema admission lock is unavailable.");
545
+ }
546
+ const reserved = await reserve();
547
+ const owner: PhysicalTransactionOwner = { session: reserved, lifetime: "borrowed", state: "active" }; physicalOwners.set(reserved, owner);
548
+ try {
549
+ for (const { key } of keys) await reserved.unsafe("SELECT pg_advisory_lock($1)", [key]);
550
+ return await this.lockedSession.run(owner, () => this.transaction(async (tx) => work(this.createSchemaAdmissionScope(tx, reserved))));
551
+ } finally {
552
+ try { if (!owner.unknownOutcome && (owner.state === "active" || owner.rollbackAcknowledged)) await this.unlockOwner(owner, [...keys].reverse().map(({ key }) => key)); }
553
+ finally { await this.releaseOwner(owner); }
554
+ }
555
+ }
556
+
557
+ private createSchemaAdmissionScope(executor: DbExecutor, session: SqlLike): InternalSchemaAdmissionScope {
558
+ const sessionExecutor: DbExecutor = {
559
+ query: (sql, params) => this.runQuery(session, sql, params),
560
+ execute: (sql, params) => this.runExecute(session, sql, params),
561
+ };
562
+ return {
563
+ ...executor,
564
+ // DDL is issued on the advisory-lock session because Bun's transaction
565
+ // facade does not expose its underlying raw connection. Exact final
566
+ // introspection must use that same session or a pooled query cannot see
567
+ // the still-uncommitted catalog rows.
568
+ introspectExpected: (expected) => this.introspectExpected(expected, sessionExecutor),
569
+ executeSchemaAdmission: (sql, operation) => this.runSchemaAdmissionExecute(session, sql, operation),
570
+ };
571
+ }
572
+
573
+ /**
574
+ * Sends a notification through `pg_notify` (the regular pool; no active
575
+ * listener needed). Works on any Bun version.
576
+ */
577
+ async notify(channel: string, payload?: string): Promise<void> {
578
+ await this.query("SELECT pg_notify($1, $2)", [channel, payload ?? ""]);
579
+ }
580
+
581
+ /**
582
+ * Subscribes to a channel through the native Bun `sql.listen` (if the runtime
583
+ * has it). Bun keeps a dedicated connection and reconnects with backoff. If the
584
+ * runtime has no `listen` yet, a clear error is thrown and the subscriber falls
585
+ * back to polling.
586
+ */
587
+ async listen(channel: string, handler: (payload: string) => void | Promise<void>): Promise<NotificationSubscription> {
588
+ const native = (this.sql as unknown as { listen?: BunListen }).listen;
589
+ if (typeof native !== "function") {
590
+ throw new Error("PostgreSQL LISTEN is unavailable in this Bun runtime; falling back to polling.");
591
+ }
592
+ const subscription = await native.call(this.sql, channel, handler);
593
+ const unlisten = subscription && typeof subscription.unlisten === "function" ? subscription.unlisten : undefined;
594
+ return {
595
+ close: () => unlisten?.call(subscription),
596
+ };
597
+ }
598
+
599
+ statistics(): Readonly<{ pendingNativeOperations: number; activeCancellations: number; unconfirmedCancellations: number; closed: boolean }> {
600
+ return Object.freeze({ pendingNativeOperations: this.pendingNativeOperations, activeCancellations: this.activeCancellations, unconfirmedCancellations: this.unconfirmedCancellations, closed: this.closed });
601
+ }
602
+
603
+ close(): Promise<void> {
604
+ this.closed = true;
605
+ this.closing.abort();
606
+ this.closePromise ??= (async () => {
607
+ const deadline = new OperationDeadline(this.cancellationTimeoutMs);
608
+ try {
609
+ const results = await deadline.wait(Promise.allSettled([
610
+ Promise.resolve().then(() => this.sql.close({ timeout: 0 })),
611
+ Promise.resolve().then(() => this.controlSql?.close({ timeout: 0 })),
612
+ ]));
613
+ const failed = results.find((result) => result.status === "rejected");
614
+ if (failed?.status === "rejected") throw failed.reason;
615
+ } finally { deadline.dispose(); }
616
+ })();
617
+ return this.closePromise;
618
+ }
619
+
620
+ private native<T>(work: () => PromiseLike<T>, cleanup = false): Promise<T> {
621
+ if (this.pendingNativeOperations >= this.maxPendingOperations * (cleanup ? 2 : 1)) return Promise.reject(new OrmTransactionScopeError("PostgreSQL pending operation limit reached."));
622
+ this.pendingNativeOperations += 1;
623
+ // A timed-out native promise keeps its slot until it actually settles.
624
+ // Repeated outages cannot create an unbounded number of detached operations.
625
+ return Promise.resolve().then(work).finally(() => { this.pendingNativeOperations -= 1; });
626
+ }
627
+
628
+ private event(operation: PostgresOperationEvent["operation"], start: number, ...errors: [] | [unknown]): void {
629
+ try { this.onOperation?.(Object.freeze({ operation, outcome: errors.length === 0 ? "success" : isUnknownTransactionOutcome(errors[0]) ? "unknown" : "error", durationMs: performance.now() - start, pendingNativeOperations: this.pendingNativeOperations })); } catch { /* telemetry cannot change a database outcome */ }
630
+ }
631
+
632
+ private operation(): OperationDeadline {
633
+ if (this.closed) throw new OrmTransactionScopeError("PostgreSQL provider is closed.");
634
+ const options = currentPostgresScopeOptions();
635
+ return new OperationDeadline(positiveTimeout(options?.timeoutMs, this.operationTimeoutMs, "timeoutMs"), options?.signal);
636
+ }
637
+
638
+ private async reserveSession(operation: OperationDeadline, reserve = this.reserveConnection, control = false): Promise<ReservedSqlLike> {
639
+ if (!reserve) throw new OrmTransactionScopeError("PostgreSQL reservations are unavailable.");
640
+ operation.signal.throwIfAborted();
641
+ const start = performance.now();
642
+ try {
643
+ for (let attempt = 0; ; attempt++) {
644
+ const pending = this.native(() => { operation.signal.throwIfAborted(); return reserve({ signal: operation.signal }); }, control);
645
+ // Also defend against a driver that ignores signal and grants the slot late.
646
+ void pending.then(async (session) => {
647
+ if (operation.signal.aborted || this.closed) {
648
+ try { await this.retireReservation(session, control); } catch { /* retained native slot supplies backpressure if cleanup hangs */ }
649
+ }
650
+ }, () => {});
651
+ let session: ReservedSqlLike | undefined;
652
+ try {
653
+ session = await operation.wait(pending);
654
+ operation.signal.throwIfAborted();
655
+ if (this.closed) throw new OrmTransactionScopeError("PostgreSQL provider is closed.");
656
+ if (control ? this.validateControlAdmissions : this.validateWorkerAdmissions) {
657
+ const retained = session;
658
+ await operation.wait(this.native(() => { operation.signal.throwIfAborted(); return retained.unsafe("/* bazis:admission-check */ SELECT 1"); }, control));
659
+ }
660
+ }
661
+ catch (error) {
662
+ if (session) {
663
+ try { await operation.wait(this.retireReservation(session, control)); }
664
+ catch { /* no release precedes native close settlement */ }
665
+ }
666
+ // Only the read-only admission check may have run. Business SQL,
667
+ // BEGIN and the transaction callback have not been dispatched.
668
+ if (attempt === 0 && !operation.signal.aborted && !this.closed && error instanceof Error && (error as { code?: string }).code === "ERR_POSTGRES_CONNECTION_TIMEOUT") {
669
+ if (control) this.validateControlAdmissions = true; else this.validateWorkerAdmissions = true;
670
+ continue;
671
+ }
672
+ throw error;
673
+ }
674
+ operation.signal.throwIfAborted(); this.event("admission", start); return session;
675
+ }
676
+ } catch (error) { this.event("admission", start, error); throw error; }
677
+ }
678
+
679
+ private retireReservation(session: ReservedSqlLike, control = false): Promise<void> {
680
+ if (control) this.validateControlAdmissions = true; else this.validateWorkerAdmissions = true;
681
+ const existing = this.reservationRetirements.get(session);
682
+ if (existing) return existing;
683
+ const retirement = this.native(async () => {
684
+ if (!session.close) throw new OrmTransactionScopeError("PostgreSQL reservation close is unavailable.");
685
+ await session.close({ timeout: 0 });
686
+ await session.release();
687
+ }, true);
688
+ this.reservationRetirements.set(session, retirement);
689
+ return retirement;
690
+ }
691
+
692
+ private async pooledOperation<T>(work: (session: SqlLike) => Promise<T>): Promise<T> {
693
+ // Legacy test/custom clients without reserve retain their compatibility path.
694
+ if (!this.reserveConnection) return work(this.sql);
695
+ const operation = this.operation();
696
+ let owner: PhysicalTransactionOwner | undefined;
697
+ let completed = false;
698
+ try {
699
+ const session = await this.reserveSession(operation);
700
+ owner = { session, lifetime: "owned", state: "active", operation };
701
+ physicalOwners.set(session, owner);
702
+ const result = await work(session);
703
+ completed = true;
704
+ return result;
705
+ } finally {
706
+ operation.dispose();
707
+ if (owner) {
708
+ try { await this.releaseOwner(owner); }
709
+ catch (error) {
710
+ if (completed) throw new PostCommitError([error]);
711
+ throw error;
712
+ }
713
+ }
714
+ }
715
+ }
716
+
717
+ private async ownerCommand(owner: PhysicalTransactionOwner, sql: string, params: readonly unknown[] = []): Promise<BunSqlResult> {
718
+ const pending = this.ownerNative(owner, () => { this.assertActiveOwner(owner.session); owner.operation?.signal.throwIfAborted(); return owner.session.unsafe(sql, params); });
719
+ return owner.operation ? owner.operation.wait(pending) : pending;
720
+ }
721
+
722
+ private ownerNative<T>(owner: PhysicalTransactionOwner, work: () => PromiseLike<T>, cleanup = false): Promise<T> {
723
+ const pending = this.native(work, cleanup);
724
+ (owner.pending ??= new Set()).add(pending);
725
+ void pending.finally(() => { owner.pending!.delete(pending); }).catch(() => {});
726
+ return pending;
727
+ }
728
+
729
+ private async executeQuery(executor: SqlLike, sql: string, params: readonly SqlParam[], kind: "query" | "execute"): Promise<BunSqlResult> {
730
+ this.assertActiveOwner(executor);
731
+ this.traceSql(sql, params);
732
+ const owner = physicalOwners.get(executor);
733
+ const autocommit = this.ambient.getStore() === undefined;
734
+ const start = performance.now();
735
+ let nativeQuery: CancellableSqlQuery | undefined;
736
+ const dispatch = () => { this.assertActiveOwner(executor); nativeQuery = executor.unsafe(sql, params as readonly unknown[]) as CancellableSqlQuery; return nativeQuery; };
737
+ const pending = owner ? this.ownerNative(owner, dispatch) : this.native(dispatch);
738
+ const settled = (async () => {
739
+ try {
740
+ const result = owner?.operation ? await owner.operation.wait(pending) : await pending;
741
+ if (owner?.quarantinePromise) await owner.quarantinePromise;
742
+ this.event(kind, start);
743
+ return result;
744
+ } catch (error) {
745
+ // Both query() and execute() can write in autocommit mode. Once handed
746
+ // to the driver, a missing reply is not proof that replay is safe.
747
+ if (autocommit && nativeQuery !== undefined && !isConfirmedStatementRejection(error)) {
748
+ const unknown = this.uncertainty(error);
749
+ if (owner) {
750
+ owner.unknownOutcome ??= unknown;
751
+ owner.state = "quarantined";
752
+ void this.closeOwner(owner).catch(() => {});
753
+ }
754
+ this.event(kind, start, owner?.unknownOutcome ?? unknown);
755
+ throw owner?.unknownOutcome ?? unknown;
756
+ }
757
+ try {
758
+ if (owner?.operation?.signal.aborted && !owner.quarantinePromise) await this.quarantineOwner(owner);
759
+ if (owner?.quarantinePromise) await owner.quarantinePromise;
760
+ } catch (uncertain) { this.event(kind, start, uncertain); throw uncertain; }
761
+ this.event(kind, start, error);
762
+ throw error;
763
+ }
764
+ })();
765
+ observeProviderDispatch({ cancel: () => {
766
+ if (owner) {
767
+ // A completed dispatch must not cancel later work or a new borrower.
768
+ if (!owner.pending?.has(pending)) return;
769
+ if (physicalOwners.get(executor) !== owner || owner.state === "released") throw new OrmTransactionScopeError("PostgreSQL cancellation owner expired.");
770
+ return this.quarantineOwner(owner);
771
+ }
772
+ const query = nativeQuery; if (typeof query?.cancel !== "function") throw new Error("PostgreSQL query cancellation is unavailable."); return query.cancel();
773
+ }, settled });
774
+ return settled;
775
+ }
776
+
777
+ private async runQuery(executor: SqlLike, sql: string, params: readonly SqlParam[]): Promise<Row[]> {
778
+ return Array.from(await this.executeQuery(executor, sql, params, "query"));
779
+ }
780
+
781
+ private async runExecute(executor: SqlLike, sql: string, params: readonly SqlParam[]): Promise<ExecuteResult> {
782
+ const result = await this.executeQuery(executor, sql, params, "execute");
783
+ return { changes: result.affectedRows ?? result.count ?? result.length, lastInsertId: 0 };
784
+ }
785
+
786
+ private async runSchemaAdmissionExecute(executor: SqlLike, sql: string, operation: string): Promise<ExecuteResult> {
787
+ this.assertActiveOwner(executor);
788
+ this.traceSchemaAdmission(operation);
789
+ const result = await executor.unsafe(sql, []);
790
+ const changes = result.affectedRows ?? result.count ?? (Array.isArray(result) ? result.length : 0);
791
+ return { changes, lastInsertId: 0 };
792
+ }
793
+
794
+ private assertActiveOwner(executor: SqlLike): void {
795
+ if (this.closed) throw new OrmTransactionScopeError("PostgreSQL provider is closed.");
796
+ const owner = physicalOwners.get(executor);
797
+ if (owner?.unknownOutcome) throw owner.unknownOutcome;
798
+ if (owner && owner.state !== "active") throw new Error("PostgreSQL retained reservation is no longer active.");
799
+ }
800
+
801
+ private async transactionOnReservedSession<T>(owner: PhysicalTransactionOwner, work: (tx: DbExecutor) => Promise<T>): Promise<T> {
802
+ return this.executeReservedTransaction(owner, work, false);
803
+ }
804
+
805
+ private async transactionOnOwnedReservedSession<T>(reserve: (options?: { signal?: AbortSignal }) => Promise<ReservedSqlLike>, work: (tx: DbExecutor) => Promise<T>): Promise<T> {
806
+ const operation = this.operation();
807
+ let session: ReservedSqlLike;
808
+ try { session = await this.reserveSession(operation, reserve); }
809
+ catch (error) { operation.dispose(); throw error; }
810
+ const owner: PhysicalTransactionOwner = { session, lifetime: "owned", state: "active", operation };
811
+ physicalOwners.set(session, owner);
812
+ return this.executeReservedTransaction(owner, work, true);
813
+ }
814
+
815
+ private async executeReservedTransaction<T>(owner: PhysicalTransactionOwner, work: (tx: DbExecutor) => Promise<T>, release: boolean): Promise<T> {
816
+ if (owner.unknownOutcome) throw owner.unknownOutcome;
817
+ if (owner.state !== "active") throw new OrmTransactionScopeError("PostgreSQL retained reservation is unavailable.");
818
+ const operation = owner.operation ?? this.operation();
819
+ owner.operation = operation;
820
+ const executor: DbExecutor = { query: (sql, params) => this.runQuery(owner.session, sql, params), execute: (sql, params) => this.runExecute(owner.session, sql, params) };
821
+ const callbacks = createTransactionCallbackScope();
822
+ owner.callbacks = callbacks;
823
+ let committing = false, committed = false;
824
+ let result!: T;
825
+ let failure: unknown;
826
+ try {
827
+ await this.ownerCommand(owner, "BEGIN");
828
+ await this.configureSession(owner);
829
+ result = await this.transactionCallbacks.run(callbacks, () => this.ambient.run({ executor, session: owner.session, owner }, async () => {
830
+ const value = await operation.wait(work(executor));
831
+ assertTransactionCanCommit(callbacks);
832
+ return value;
833
+ }));
834
+ operation.signal.throwIfAborted();
835
+ committing = true;
836
+ await this.ownerCommand(owner, "COMMIT");
837
+ committed = true;
838
+ } catch (error) {
839
+ failure = error;
840
+ if (committing && !isConfirmedCommitRejection(error)) {
841
+ failure = owner.unknownOutcome = this.uncertainty(error);
842
+ } else if (owner.unknownOutcome) {
843
+ failure = owner.unknownOutcome;
844
+ } else {
845
+ try {
846
+ if (operation.signal.aborted || owner.quarantinePromise) await this.quarantineOwner(owner);
847
+ else {
848
+ const cleanup = new OperationDeadline(this.cancellationTimeoutMs);
849
+ try { await cleanup.wait(this.native(() => owner.session.unsafe("ROLLBACK"))); }
850
+ catch { await this.quarantineOwner(owner); }
851
+ finally { cleanup.dispose(); }
852
+ }
853
+ } catch (error) { failure = error; }
854
+ }
855
+ if (owner.unknownOutcome) notifyTransactionUncertainty(callbacks.afterRollback);
856
+ } finally {
857
+ operation.dispose();
858
+ owner.operation = undefined;
859
+ }
860
+ let releaseError: unknown; let releaseFailed = false;
861
+ if (release) { try { await this.releaseOwner(owner); } catch (error) { releaseFailed = true; releaseError = error; } }
862
+ if (owner.unknownOutcome) throw owner.unknownOutcome;
863
+ if (committed) return withoutPostgresScopeOptions(() => this.ambient.exit(() => this.transactionCallbacks.exit(async () => {
864
+ try { await runPostCommitCallbacks(callbacks.afterCommit); }
865
+ catch (error) {
866
+ if (releaseFailed && error instanceof PostCommitError) throw new PostCommitError([releaseError, ...error.errors]);
867
+ throw error;
868
+ }
869
+ if (releaseFailed) throw new PostCommitError([releaseError]);
870
+ return result;
871
+ })));
872
+ return this.completeRollback(releaseFailed ? new AggregateError([failure, releaseError], "PostgreSQL rollback cleanup failed.") : failure, callbacks.afterRollback);
873
+ }
874
+
875
+ private async configureSession(owner: PhysicalTransactionOwner): Promise<void> {
876
+ const settings = { statementTimeoutMs: "statement_timeout", lockTimeoutMs: "lock_timeout", idleInTransactionTimeoutMs: "idle_in_transaction_session_timeout", transactionTimeoutMs: "transaction_timeout" } as const;
877
+ for (const [key, name] of Object.entries(settings)) {
878
+ const value = this.serverTimeouts[key as keyof PostgresServerTimeouts];
879
+ if (value === undefined) continue;
880
+ await this.ownerCommand(owner, "SELECT set_config($1, $2, true)", [name, `${value}ms`]);
881
+ const rows = await this.ownerCommand(owner, "SELECT setting::bigint AS value FROM pg_settings WHERE name = $1", [name]);
882
+ if (String(rows[0]?.value) !== String(value)) throw new OrmTransactionScopeError("PostgreSQL timeout readback failed.");
883
+ }
884
+ }
885
+
886
+ /** Private owned-store reservation: it never borrows the root pool after reserve. */
887
+ private async withOwnedStoreAdmissionV1<T>(signal: AbortSignal | undefined, work: (session: RegistryLockedOwnedStoreSessionV1) => Promise<T>): Promise<T> {
888
+ if (this.ambient.getStore() || this.lockedSession.getStore()) throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
889
+ const reserve = this.reserveConnection;
890
+ if (!reserve) throw failure("ORM_OWNED_STORE_PROVIDER_UNSUPPORTED");
891
+ let owner: PhysicalTransactionOwner | undefined;
892
+ let attempt: OwnedStoreAttemptV1 | undefined;
893
+ let committed = false, rollbackCertain = false, releaseCertain = false, cleanupAborted = false, providerUnsupported = false, outcome!: T, outcomeSet = false, caught: unknown, caughtSet = false, callbackError: unknown, callbackErrorSet = false;
894
+ try {
895
+ if (this.ownedStoreAborted(signal)) throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
896
+ const session = await reserve();
897
+ owner = { session, lifetime: "owned", state: "active" };
898
+ physicalOwners.set(session, owner);
899
+ if (this.ownedStoreAborted(signal)) throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
900
+ await this.ownedStoreSql(session, "BEGIN", [], "begin", signal);
901
+ if (this.ownedStoreAborted(signal)) throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
902
+ const registryKey = ownedStoreAdvisoryLockV1(canonicalOwnedStoreRegistryLockPreimageV1());
903
+ await this.ownedStoreSql(session, "SELECT pg_catalog.pg_advisory_xact_lock($1::pg_catalog.int8)", [registryKey], "registry-lock", signal);
904
+ if (this.ownedStoreAborted(signal)) throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
905
+ await this.ownedStoreSql(session, "SET LOCAL search_path = pg_catalog, pg_temp", [], "search-path", signal);
906
+ if (this.ownedStoreAborted(signal)) throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
907
+ const contextResult = await this.ownedStoreSql(session, "SELECT pg_catalog.current_setting('max_identifier_length') AS max_identifier_length", [], "server-context", signal);
908
+ if (this.ownedStoreAborted(signal)) throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
909
+ const contextRows = selectOwnedStoreRows(contextResult, ["max_identifier_length"], 1);
910
+ if (!contextRows || contextRows.length !== 1 || typeof contextRows[0]!.max_identifier_length !== "string") throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
911
+ const maximum = this.ownedStoreMaximum(contextRows[0]!.max_identifier_length);
912
+ if (maximum < 63n) { providerUnsupported = true; throw failure("ORM_OWNED_STORE_PROVIDER_UNSUPPORTED"); }
913
+ attempt = { owner, signal, phase: "registryLocked", generation: 1, poisoned: false, lockUncertainty: false, mutationFailure: false, semanticFailure: false, busy: false, pending: new Set(), createRegistryCalls: 0, applyCalls: 0, insertCalls: 0 };
914
+ const activeAttempt = attempt;
915
+ if (!activeAttempt) throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
916
+ const registryGeneration = activeAttempt.generation;
917
+ const registry = Object.freeze({
918
+ get maxIdentifierLength() { return maximum; },
919
+ inspectRegistry: () => this.ownedStoreRegistryV1(activeAttempt, registryGeneration),
920
+ lockSecondary: (plan: OwnedStoreSecondaryLockPlanV1) => this.ownedStoreSecondaryV1(activeAttempt, plan),
921
+ });
922
+ try { outcome = await work(registry); outcomeSet = true; }
923
+ catch (error) { callbackError = error; callbackErrorSet = true; }
924
+ finally {
925
+ activeAttempt.phase = "callbackClosed";
926
+ activeAttempt.generation++;
927
+ if (activeAttempt.pending.size !== 0 || activeAttempt.busy) this.ownedStoreLockUncertainty(activeAttempt);
928
+ }
929
+ if (callbackErrorSet) {
930
+ const code = this.ownedStoreSafeCallbackError(callbackError);
931
+ if (code === "ORM_OWNED_STORE_CREATE_FAILED") activeAttempt.mutationFailure = true;
932
+ else if (code && code !== "ORM_OWNED_STORE_LOCK_UNAVAILABLE" && code !== "ORM_OWNED_STORE_PROVIDER_UNSUPPORTED") activeAttempt.semanticFailure = true;
933
+ throw callbackError;
934
+ }
935
+ if (activeAttempt.pending.size) await Promise.allSettled([...activeAttempt.pending]);
936
+ if (activeAttempt.poisoned || activeAttempt.pending.size || this.ownedStoreAborted(signal)) {
937
+ this.ownedStoreLockUncertainty(activeAttempt);
938
+ throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
939
+ }
940
+ activeAttempt.phase = "committing";
941
+ await this.ownedStoreSql(session, "COMMIT", [], "commit", signal, activeAttempt);
942
+ committed = true;
943
+ if (this.ownedStoreAborted(signal)) throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
944
+ } catch (error) {
945
+ caught = error;
946
+ caughtSet = true;
947
+ if (attempt?.pending.size) {
948
+ this.ownedStoreLockUncertainty(attempt);
949
+ await Promise.allSettled([...attempt.pending]);
950
+ }
951
+ cleanupAborted = this.ownedStoreAbortDuringCleanup(signal);
952
+ if (owner && !committed) {
953
+ try {
954
+ if (owner.state === "active") await this.ownedStoreSql(owner.session, "ROLLBACK", [], "rollback", signal, attempt, false, true);
955
+ rollbackCertain = true;
956
+ cleanupAborted ||= this.ownedStoreAbortDuringCleanup(signal);
957
+ } catch { /* final outcome remains closed */ }
958
+ }
959
+ } finally {
960
+ if (owner) {
961
+ try {
962
+ await this.releaseOwner(owner);
963
+ releaseCertain = true;
964
+ cleanupAborted ||= this.ownedStoreAbortDuringCleanup(signal);
965
+ } catch { /* final outcome remains closed */ }
966
+ }
967
+ }
968
+ if (caughtSet) {
969
+ const callbackCode = callbackErrorSet && caught === callbackError ? this.ownedStoreSafeCallbackError(caught) : undefined;
970
+ if (cleanupAborted || attempt?.lockUncertainty || !rollbackCertain || !releaseCertain) throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
971
+ if (providerUnsupported && rollbackCertain && releaseCertain) throw failure("ORM_OWNED_STORE_PROVIDER_UNSUPPORTED");
972
+ if (attempt?.mutationFailure) throw failure("ORM_OWNED_STORE_CREATE_FAILED");
973
+ if (callbackCode && rollbackCertain && releaseCertain) throw failure(callbackCode);
974
+ throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
975
+ }
976
+ if (!committed || !releaseCertain || !outcomeSet || this.ownedStoreAborted(signal)) throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
977
+ return outcome;
978
+ }
979
+
980
+ private async ownedStoreSql(session: ReservedSqlLike, sql: string, params: readonly unknown[], operation: string, signal: AbortSignal | undefined, attempt?: OwnedStoreAttemptV1, mutation = false, cleanup = false): Promise<BunSqlResult> {
981
+ if (!cleanup && this.ownedStoreAborted(signal)) {
982
+ if (attempt) this.ownedStoreLockUncertainty(attempt);
983
+ throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
984
+ }
985
+ try { this.traceOwnedStore(operation); }
986
+ catch {
987
+ if (attempt) mutation ? this.ownedStoreMutationFailure(attempt) : this.ownedStoreLockUncertainty(attempt);
988
+ throw failure(mutation ? "ORM_OWNED_STORE_CREATE_FAILED" : "ORM_OWNED_STORE_LOCK_UNAVAILABLE");
989
+ }
990
+ if (!cleanup && this.ownedStoreAborted(signal)) {
991
+ if (attempt) this.ownedStoreLockUncertainty(attempt);
992
+ throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
993
+ }
994
+ try { return await session.unsafe(sql, params); }
995
+ catch {
996
+ if (attempt) mutation ? this.ownedStoreMutationFailure(attempt) : this.ownedStoreLockUncertainty(attempt);
997
+ throw failure(mutation ? "ORM_OWNED_STORE_CREATE_FAILED" : "ORM_OWNED_STORE_LOCK_UNAVAILABLE");
998
+ }
999
+ }
1000
+
1001
+ private ownedStoreLockUncertainty(attempt: OwnedStoreAttemptV1): void {
1002
+ attempt.poisoned = true;
1003
+ attempt.lockUncertainty = true;
1004
+ }
1005
+
1006
+ private ownedStoreMutationFailure(attempt: OwnedStoreAttemptV1): void {
1007
+ attempt.poisoned = true;
1008
+ attempt.mutationFailure = true;
1009
+ }
1010
+
1011
+ private ownedStoreAbortDuringCleanup(signal: AbortSignal | undefined): boolean {
1012
+ try { return this.ownedStoreAborted(signal); }
1013
+ catch { return true; }
1014
+ }
1015
+
1016
+ private ownedStoreMaximum(value: unknown): bigint {
1017
+ if (typeof value !== "string" || !/^(0|[1-9][0-9]*)$/u.test(value)) throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
1018
+ try { return BigInt(value); } catch { throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE"); }
1019
+ }
1020
+
1021
+ /** Private wire boundary for the presently implemented small SELECTs only. */
1022
+ private ownedStoreAborted(signal: AbortSignal | undefined): boolean {
1023
+ if (!signal) return false;
1024
+ const getter = Object.getOwnPropertyDescriptor(AbortSignal.prototype, "aborted")?.get;
1025
+ if (!getter) throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
1026
+ try { return getter.call(signal); } catch { throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE"); }
1027
+ }
1028
+
1029
+ private ownedStoreSafeCallbackError(error: unknown): OrmOwnedStoreAdmissionError["code"] | undefined {
1030
+ if (error === null || typeof error !== "object" || types.isProxy(error) || Object.getPrototypeOf(error) !== OrmOwnedStoreAdmissionError.prototype || Object.hasOwn(error, "cause")) return undefined;
1031
+ const code = Object.getOwnPropertyDescriptor(error, "code"), message = Object.getOwnPropertyDescriptor(error, "message");
1032
+ const known = ["ORM_OWNED_STORE_PROVIDER_UNSUPPORTED", "ORM_OWNED_STORE_LOCK_UNAVAILABLE", "ORM_OWNED_STORE_IDENTITY_MISSING", "ORM_OWNED_STORE_IDENTITY_MISMATCH", "ORM_OWNED_STORE_OWNERSHIP_CONFLICT", "ORM_OWNED_STORE_DRIFT", "ORM_OWNED_STORE_CREATE_FAILED"];
1033
+ if (!code || !("value" in code) || typeof code.value !== "string" || !message || !("value" in message) || code.value !== message.value || !known.includes(code.value)) return undefined;
1034
+ return code.value as OrmOwnedStoreAdmissionError["code"];
1035
+ }
1036
+
1037
+ /** Reads the registry snapshot under the registry lock; any failure poisons the attempt, so a partial snapshot is never admitted. */
1038
+ private async ownedStoreRegistryV1(attempt: OwnedStoreAttemptV1, generation: number): Promise<OwnedStoreRegistrySnapshotV1> {
1039
+ if (attempt.phase !== "registryLocked" || attempt.generation !== generation || attempt.busy) { if (attempt.phase !== "callbackClosed" && attempt.phase !== "released" && attempt.phase !== "committing") this.ownedStoreLockUncertainty(attempt); throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE"); }
1040
+ attempt.busy = true;
1041
+ const pending = (async () => {
1042
+ try {
1043
+ const snapshot = await this.catalogReader(attempt).readRegistry();
1044
+ if (attempt.phase !== "registryLocked" || attempt.generation !== generation) {
1045
+ attempt.poisoned = true;
1046
+ throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
1047
+ }
1048
+ return snapshot;
1049
+ }
1050
+ finally { attempt.busy = false; }
1051
+ })();
1052
+ attempt.pending.add(pending);
1053
+ try { return await pending; }
1054
+ catch (error) { attempt.poisoned = true; throw error; }
1055
+ finally { attempt.pending.delete(pending); }
1056
+ }
1057
+
1058
+ private async ownedStoreSecondaryV1(attempt: OwnedStoreAttemptV1, plan: OwnedStoreSecondaryLockPlanV1): Promise<SecondaryLockedOwnedStoreSessionV1> {
1059
+ if (attempt.phase !== "registryLocked" || attempt.busy) {
1060
+ if (attempt.phase !== "callbackClosed" && attempt.phase !== "released" && attempt.phase !== "committing") this.ownedStoreLockUncertainty(attempt);
1061
+ throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
1062
+ }
1063
+ const generation = attempt.generation;
1064
+ const pending = (async () => {
1065
+ attempt.busy = true;
1066
+ try {
1067
+ for (const entry of [...plan.stores, ...plan.scopes]) {
1068
+ await this.ownedStoreSql(attempt.owner.session, "SELECT pg_catalog.pg_advisory_xact_lock($1::pg_catalog.int8)", [entry.key], entry.kind === "store" ? "store-lock" : "scope-lock", attempt.signal, attempt);
1069
+ if (this.ownedStoreAborted(attempt.signal)) throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE");
1070
+ }
1071
+ if (attempt.phase !== "registryLocked" || attempt.generation !== generation) { this.ownedStoreLockUncertainty(attempt); throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE"); }
1072
+ attempt.phase = "secondaryLocked";
1073
+ return Object.freeze({
1074
+ inspectCatalog: (scopes: readonly OrmCatalogScopeV1[]) => this.ownedStoreSecondaryOperation(attempt, generation, async () => this.catalogReader(attempt).readCatalog(scopes)),
1075
+ createRegistryV1: () => this.ownedStoreCreateRegistryV1(attempt, generation),
1076
+ applyCreateOperations: (operations: readonly OwnedStoreCreateOperationV1[]) => this.ownedStoreApplyCreateOperationsV1(attempt, generation, operations),
1077
+ insertIdentities: (rows: readonly OwnedStoreIdentityInsertV1[]) => this.ownedStoreInsertIdentitiesV1(attempt, generation, rows),
1078
+ inspectRegistry: () => this.ownedStoreSecondaryOperation(attempt, generation, async () => this.catalogReader(attempt).readRegistry()),
1079
+ });
1080
+ } catch (error) { attempt.poisoned = true; throw error; }
1081
+ finally { attempt.busy = false; }
1082
+ })();
1083
+ attempt.pending.add(pending);
1084
+ try { return await pending; }
1085
+ finally { attempt.pending.delete(pending); }
1086
+ }
1087
+
1088
+ /**
1089
+ * Catalogue reads deliberately stay on the reservation which owns the
1090
+ * admission locks. Scope admission is repeated here rather than trusting a
1091
+ * caller-owned array: this boundary must not invoke accessors or coerce
1092
+ * values before the SQL fence.
1093
+ */
1094
+ private catalogReader(attempt: OwnedStoreAttemptV1): OwnedStoreCatalogReader {
1095
+ return new OwnedStoreCatalogReader({
1096
+ query: (sql, params, operation) => this.ownedStoreSql(attempt.owner.session, sql, params, operation, attempt.signal, attempt),
1097
+ assertActive: () => { if (this.ownedStoreAborted(attempt.signal)) throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE"); },
1098
+ unavailable: (): never => { this.ownedStoreLockUncertainty(attempt); throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE"); },
1099
+ drift: (): never => { attempt.semanticFailure = true; throw failure("ORM_OWNED_STORE_DRIFT"); },
1100
+ });
1101
+ }
1102
+
1103
+ private async ownedStoreSecondaryOperation<T>(attempt: OwnedStoreAttemptV1, generation: number, work: () => Promise<T>): Promise<T> {
1104
+ if (attempt.phase !== "secondaryLocked" || attempt.generation !== generation || attempt.busy) { if (attempt.phase !== "callbackClosed" && attempt.phase !== "released" && attempt.phase !== "committing") this.ownedStoreLockUncertainty(attempt); throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE"); }
1105
+ attempt.busy = true;
1106
+ const pending = work(); attempt.pending.add(pending);
1107
+ try { const result = await pending; if (this.ownedStoreAborted(attempt.signal)) throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE"); return result; }
1108
+ catch (error) { attempt.poisoned = true; throw error; }
1109
+ finally { attempt.pending.delete(pending); attempt.busy = false; }
1110
+ }
1111
+
1112
+ private async ownedStoreMutation<T>(attempt: OwnedStoreAttemptV1, generation: number, counter: "createRegistryCalls" | "applyCalls" | "insertCalls", work: () => Promise<T>): Promise<T> {
1113
+ return this.ownedStoreSecondaryOperation(attempt, generation, async () => {
1114
+ attempt[counter]++;
1115
+ if (attempt[counter] !== 1) { this.ownedStoreLockUncertainty(attempt); throw failure("ORM_OWNED_STORE_LOCK_UNAVAILABLE"); }
1116
+ try { return await work(); }
1117
+ catch {
1118
+ this.ownedStoreMutationFailure(attempt);
1119
+ throw failure("ORM_OWNED_STORE_CREATE_FAILED");
1120
+ }
1121
+ });
1122
+ }
1123
+
1124
+ private async ownedStoreCreateRegistryV1(attempt: OwnedStoreAttemptV1, generation: number): Promise<void> {
1125
+ return this.ownedStoreMutation(attempt, generation, "createRegistryCalls", async () => {
1126
+ await this.ownedStoreSql(
1127
+ attempt.owner.session,
1128
+ 'CREATE TABLE "public"."__bazis_orm_owned_stores_v1" ("store_key" pg_catalog.text NOT NULL, "contract" pg_catalog.text NOT NULL, "format_version" pg_catalog.int8 NOT NULL, "owned_schema" pg_catalog.text NOT NULL, "table_prefix" pg_catalog.text NOT NULL, "owned_scope_hash" pg_catalog.text NOT NULL, "model_hash" pg_catalog.text NOT NULL, "created_at" pg_catalog.timestamptz NOT NULL, CONSTRAINT "__bazis_orm_owned_stores_v1_pkey" PRIMARY KEY ("store_key"))',
1129
+ [],
1130
+ "registry-create",
1131
+ attempt.signal,
1132
+ attempt,
1133
+ true,
1134
+ );
1135
+ });
1136
+ }
1137
+
1138
+ private async ownedStoreApplyCreateOperationsV1(attempt: OwnedStoreAttemptV1, generation: number, operations: readonly OwnedStoreCreateOperationV1[]): Promise<void> {
1139
+ return this.ownedStoreMutation(attempt, generation, "applyCalls", async () => {
1140
+ for (const operation of operations) {
1141
+ const rendered = this.ownedStoreRenderCreateOperationV1(operation);
1142
+ await this.ownedStoreSql(attempt.owner.session, rendered.sql, [], rendered.trace, attempt.signal, attempt, true);
1143
+ }
1144
+ });
1145
+ }
1146
+
1147
+ private ownedStoreRenderCreateOperationV1(operation: unknown): { readonly sql: string; readonly trace: "schema-create" | "table-create" | "foreign-key-create" | "index-create" } {
1148
+ if (operation === null || typeof operation !== "object" || types.isProxy(operation)) throw failure("ORM_OWNED_STORE_CREATE_FAILED");
1149
+ const candidate = operation as { readonly kind?: unknown; readonly schema?: unknown; readonly table?: unknown; readonly foreignKey?: unknown; readonly index?: unknown };
1150
+ switch (candidate.kind) {
1151
+ case "createSchema":
1152
+ if (typeof candidate.schema !== "string") throw failure("ORM_OWNED_STORE_CREATE_FAILED");
1153
+ return Object.freeze({ sql: renderSafeAdditivePostgres({ kind: "createSchema", schema: candidate.schema }), trace: "schema-create" as const });
1154
+ case "createTable":
1155
+ if (candidate.table === undefined) throw failure("ORM_OWNED_STORE_CREATE_FAILED");
1156
+ return Object.freeze({ sql: renderSafeAdditivePostgres({ kind: "createTable", table: candidate.table as ExpectedTable }), trace: "table-create" as const });
1157
+ case "addForeignKey":
1158
+ if (candidate.table === undefined || candidate.foreignKey === undefined) throw failure("ORM_OWNED_STORE_CREATE_FAILED");
1159
+ return Object.freeze({ sql: renderSafeAdditivePostgres({ kind: "addForeignKey", table: candidate.table as ExpectedTable, foreignKey: candidate.foreignKey as ExpectedTable["foreignKeys"][number] }), trace: "foreign-key-create" as const });
1160
+ case "createIndex":
1161
+ if (candidate.table === undefined || candidate.index === undefined) throw failure("ORM_OWNED_STORE_CREATE_FAILED");
1162
+ return Object.freeze({ sql: renderSafeAdditivePostgres({ kind: "createIndex", table: candidate.table as ExpectedTable, index: candidate.index as ExpectedTable["indexes"][number] }), trace: "index-create" as const });
1163
+ default:
1164
+ throw failure("ORM_OWNED_STORE_CREATE_FAILED");
1165
+ }
1166
+ }
1167
+
1168
+ private async ownedStoreInsertIdentitiesV1(attempt: OwnedStoreAttemptV1, generation: number, rows: readonly OwnedStoreIdentityInsertV1[]): Promise<void> {
1169
+ return this.ownedStoreMutation(attempt, generation, "insertCalls", async () => {
1170
+ for (const row of rows) {
1171
+ const result = await this.ownedStoreSql(
1172
+ attempt.owner.session,
1173
+ 'INSERT INTO "public"."__bazis_orm_owned_stores_v1" ("store_key","contract","format_version","owned_schema","table_prefix","owned_scope_hash","model_hash","created_at") VALUES($1,$2,$3::pg_catalog.int8,$4,$5,$6,$7,pg_catalog.clock_timestamp())',
1174
+ [row.storeKey, row.contract, row.formatVersion, row.ownedSchema, row.tablePrefix, row.ownedScopeHash, row.modelHash],
1175
+ "identity-insert",
1176
+ attempt.signal,
1177
+ attempt,
1178
+ true,
1179
+ );
1180
+ if (!this.ownedStoreExactlyOneAffectedRow(result)) throw failure("ORM_OWNED_STORE_CREATE_FAILED");
1181
+ }
1182
+ });
1183
+ }
1184
+
1185
+ private ownedStoreExactlyOneAffectedRow(result: BunSqlResult): boolean {
1186
+ try {
1187
+ if (!Array.isArray(result) || types.isProxy(result)) return false;
1188
+ const authority = (name: "affectedRows" | "count", nullUnavailable: boolean): "unavailable" | "one" | "invalid" => {
1189
+ const descriptor = Object.getOwnPropertyDescriptor(result, name);
1190
+ if (!descriptor) return "unavailable";
1191
+ if (!("value" in descriptor)) return "invalid";
1192
+ if (nullUnavailable && descriptor.value === null) return "unavailable";
1193
+ return typeof descriptor.value === "number" && Number.isSafeInteger(descriptor.value) && descriptor.value === 1 ? "one" : "invalid";
1194
+ };
1195
+ const affectedRows = authority("affectedRows", true), count = authority("count", false);
1196
+ if (affectedRows === "invalid" || count === "invalid") return false;
1197
+ return affectedRows === "one" || count === "one";
1198
+ } catch { return false; }
1199
+ }
1200
+
1201
+ private uncertainty(cause?: unknown, phase: "commit" | "cancellation" = "commit"): TransactionOutcomeUnknownError {
1202
+ const error = new TransactionOutcomeUnknownError(cause, phase); this.uncertainErrors.add(error); return error;
1203
+ }
1204
+
1205
+ private closeOwner(owner: PhysicalTransactionOwner): Promise<void> {
1206
+ this.validateWorkerAdmissions = true;
1207
+ owner.closePromise ??= this.native(async () => {
1208
+ if (!owner.session.close) throw new OrmTransactionScopeError("PostgreSQL scoped close is unavailable.");
1209
+ await owner.session.close({ timeout: 0 });
1210
+ }, true);
1211
+ return owner.closePromise;
1212
+ }
1213
+
1214
+ private quarantineOwner(owner: PhysicalTransactionOwner): Promise<void> {
1215
+ if (owner.quarantinePromise) return owner.quarantinePromise;
1216
+ if (owner.state !== "active" || physicalOwners.get(owner.session) !== owner) return Promise.reject(new OrmTransactionScopeError("PostgreSQL cancellation owner expired."));
1217
+ const callbacks = this.transactionCallbacks.getStore() ?? owner.callbacks;
1218
+ if (callbacks) markTransactionRollbackOnly(callbacks, new OrmTransactionScopeError("ORM transaction was quarantined."));
1219
+ owner.state = "quarantining";
1220
+ const start = performance.now();
1221
+ this.activeCancellations += 1;
1222
+ owner.quarantinePromise = (async () => {
1223
+ const deadline = new OperationDeadline(this.cancellationTimeoutMs, this.closing.signal);
1224
+ try {
1225
+ if (this.activeCancellations > this.maxPendingOperations) throw new OrmTransactionScopeError("PostgreSQL cancellation admission limit reached.");
1226
+ if (this.cancellationMode === "server" && owner.backend) {
1227
+ await this.cancelThroughServer(owner, deadline);
1228
+ } else {
1229
+ try { await deadline.wait(this.closeOwner(owner)); }
1230
+ catch { deadline.signal.throwIfAborted(); /* Server proof decides even when close rejects. */ }
1231
+ if (!owner.backend) throw new OrmTransactionScopeError("PostgreSQL backend identity is unavailable for cancellation proof.");
1232
+ let delay = 25;
1233
+ for (;;) {
1234
+ try { if (await deadline.wait(this.backendAbsent(owner.backend))) break; }
1235
+ catch { deadline.signal.throwIfAborted(); }
1236
+ await deadline.wait(new Promise<void>((resolve) => setTimeout(resolve, delay)));
1237
+ delay = Math.min(delay * 2, 250);
1238
+ }
1239
+ owner.backendAbsenceAcknowledged = true;
1240
+ }
1241
+ owner.state = "quarantined";
1242
+ this.event("cancellation", start);
1243
+ } catch (error) {
1244
+ owner.unknownOutcome ??= this.uncertainty(error, "cancellation");
1245
+ // Never let failed control admission/replies leave this owner usable.
1246
+ // Native cleanup retains its capacity slot if the driver does not settle.
1247
+ void this.closeOwner(owner).catch(() => {});
1248
+ this.unconfirmedCancellations += 1;
1249
+ if (callbacks) notifyTransactionUncertainty(callbacks.afterRollback);
1250
+ this.event("cancellation", start, owner.unknownOutcome);
1251
+ throw owner.unknownOutcome;
1252
+ } finally { deadline.dispose(); this.activeCancellations -= 1; }
1253
+ })();
1254
+ void owner.quarantinePromise.catch(() => {});
1255
+ return owner.quarantinePromise;
1256
+ }
1257
+
1258
+ private async cancelThroughServer(owner: PhysicalTransactionOwner, deadline: OperationDeadline): Promise<void> {
1259
+ const identity = owner.backend!;
1260
+ const args = [identity.pid, identity.datid, identity.backendStart, identity.postmasterStart, identity.address, identity.port];
1261
+ const rollback = async () => {
1262
+ // No further owner dispatch can pass the synchronous quarantine fence.
1263
+ await deadline.wait(Promise.allSettled([...(owner.pending ?? [])]));
1264
+ await deadline.wait(this.native(() => { deadline.signal.throwIfAborted(); return owner.session.unsafe("ROLLBACK"); }, true));
1265
+ const current = await this.readBackendIdentity(owner.session, deadline);
1266
+ if (Object.entries(identity).some(([key, value]) => current[key as keyof BackendIdentity] !== value)) throw new OrmTransactionScopeError("PostgreSQL rollback backend identity changed.");
1267
+ owner.rollbackAcknowledged = true;
1268
+ };
1269
+ if (!owner.pending?.size) { await rollback(); return; }
1270
+ await this.withCancellationConnection(deadline, async (control) => {
1271
+ let lastSignaledStart: string | undefined;
1272
+ while (owner.pending?.size) {
1273
+ const rows = await deadline.wait(this.native(() => { deadline.signal.throwIfAborted(); return control.unsafe(CANCEL_INSPECT_SQL, args); }, true));
1274
+ const queryStart = rows[0]?.query_start;
1275
+ if (rows.length > 1 || (rows.length === 1 && (typeof queryStart !== "string" || !/^-?(?:0|[1-9][0-9]{0,18})\.[0-9]{6}$/u.test(queryStart)))) throw new OrmTransactionScopeError("PostgreSQL cancellation query identity is unavailable.");
1276
+ if (owner.pending.size && typeof queryStart === "string" && queryStart !== lastSignaledStart) {
1277
+ // Inspect and signal both require the captured backend/server identity.
1278
+ // The retained owner fence prevents dispatch/reuse while this command
1279
+ // is in flight. This SQL API is not the PID+secret CancelRequest API:
1280
+ // OS PID reuse between the server-side check and signal is a limit.
1281
+ const response = await deadline.wait(this.native(() => {
1282
+ deadline.signal.throwIfAborted();
1283
+ if (!owner.pending?.size) return Promise.resolve([] as BunSqlResult);
1284
+ return control.unsafe(CANCEL_SIGNAL_SQL, [...args, queryStart]);
1285
+ }, true));
1286
+ if (response.length > 1 || (response.length === 1 && typeof response[0]?.signaled !== "boolean")) throw new OrmTransactionScopeError("PostgreSQL cancellation response is invalid.");
1287
+ if (response[0]?.signaled === true) lastSignaledStart = queryStart;
1288
+ }
1289
+ if (owner.pending.size) await deadline.wait(new Promise<void>((resolve) => setTimeout(resolve, 5)));
1290
+ }
1291
+ // A successful signal is insufficient. Native settlement and the actual
1292
+ // ROLLBACK response must precede control release and worker reuse.
1293
+ await rollback();
1294
+ });
1295
+ }
1296
+
1297
+ private async withCancellationConnection<T>(deadline: OperationDeadline, work: (session: ReservedSqlLike) => Promise<T>): Promise<T> {
1298
+ deadline.signal.throwIfAborted();
1299
+ if (this.closed) throw new OrmTransactionScopeError("PostgreSQL provider is closed.");
1300
+ this.controlSql ??= new SQL(this.controlConfig as never);
1301
+ const control = this.controlSql;
1302
+ let session: ReservedSqlLike | undefined;
1303
+ let complete = false;
1304
+ try {
1305
+ session = await this.reserveSession(deadline, control.reserve.bind(control), true);
1306
+ deadline.signal.throwIfAborted();
1307
+ const result = await work(session);
1308
+ complete = true;
1309
+ return result;
1310
+ } finally {
1311
+ if (session) {
1312
+ const retained = session;
1313
+ const cleanup = complete ? this.native(async () => { await retained.release(); }, true) : this.retireReservation(retained, true);
1314
+ // A failed or lost response closes the control slot before any release;
1315
+ // a timeout only stops waiting, and never dispatches a late command.
1316
+ await deadline.wait(cleanup);
1317
+ }
1318
+ }
1319
+ }
1320
+
1321
+ private async unlockOwner(owner: PhysicalTransactionOwner, keys: readonly (number | bigint)[]): Promise<void> {
1322
+ const deadline = new OperationDeadline(this.cancellationTimeoutMs);
1323
+ try {
1324
+ for (const key of keys) await deadline.wait(this.native(() => { deadline.signal.throwIfAborted(); return owner.session.unsafe("SELECT pg_advisory_unlock($1)", [key]); }, true));
1325
+ } catch (error) {
1326
+ // A session-level lock must not leak to the next borrower if unlock fails.
1327
+ await deadline.wait(this.closeOwner(owner));
1328
+ throw error;
1329
+ } finally { deadline.dispose(); }
1330
+ }
1331
+
1332
+ private async releaseOwner(owner: PhysicalTransactionOwner): Promise<void> {
1333
+ if (owner.releasePromise) return owner.releasePromise;
1334
+ owner.releasePromise = (async () => {
1335
+ const deadline = new OperationDeadline(this.cancellationTimeoutMs);
1336
+ try {
1337
+ if (owner.quarantinePromise) { try { await deadline.wait(owner.quarantinePromise); } catch { /* preserve owner uncertainty below */ } }
1338
+ owner.state = "released";
1339
+ if (owner.unknownOutcome || (owner.closePromise && !owner.backendAbsenceAcknowledged)) await deadline.wait(this.closeOwner(owner));
1340
+ await deadline.wait(this.native(async () => { await owner.session.release(); }, true));
1341
+ } catch (error) {
1342
+ // Never return an unclosed, potentially busy connection to the pool.
1343
+ if (owner.unknownOutcome) throw owner.unknownOutcome;
1344
+ throw error;
1345
+ } finally { deadline.dispose(); }
1346
+ if (owner.unknownOutcome) throw owner.unknownOutcome;
1347
+ })();
1348
+ return owner.releasePromise;
1349
+ }
1350
+
1351
+ private async readBackendIdentity(session: SqlLike, cleanup?: OperationDeadline): Promise<BackendIdentity> {
1352
+ const owner = physicalOwners.get(session);
1353
+ const work = () => session.unsafe("SELECT pg_backend_pid()::text AS pid, (SELECT oid::text FROM pg_database WHERE datname = current_database()) AS datid, extract(epoch FROM backend_start)::numeric(20,6)::text AS backend_start, extract(epoch FROM pg_postmaster_start_time())::numeric(20,6)::text AS postmaster_start, coalesce(host(inet_server_addr()),'') AS address, coalesce(inet_server_port()::text,'') AS port FROM pg_stat_activity WHERE pid = pg_backend_pid()");
1354
+ const pending = owner ? this.ownerNative(owner, () => { cleanup?.signal.throwIfAborted(); return work(); }, cleanup !== undefined) : this.native(work);
1355
+ const deadline = cleanup ?? owner?.operation;
1356
+ const rows = deadline ? await deadline.wait(pending) : await pending;
1357
+ const row = rows[0];
1358
+ const identity = row && this.parseBackendIdentity(row);
1359
+ if (!identity) throw new Error("PostgreSQL backend identity is unavailable.");
1360
+ return identity;
1361
+ }
1362
+
1363
+ private parseBackendIdentity(row: Row): BackendIdentity | undefined {
1364
+ if (typeof row.pid !== "string" || typeof row.datid !== "string" || typeof row.backend_start !== "string" || typeof row.postmaster_start !== "string" || typeof row.address !== "string" || typeof row.port !== "string") return undefined;
1365
+ const identity = { pid: row.pid, datid: row.datid, backendStart: row.backend_start, postmasterStart: row.postmaster_start, address: row.address, port: row.port };
1366
+ const decimal = (value: string, max: bigint) => /^[1-9][0-9]*$/u.test(value) && BigInt(value) <= max;
1367
+ const epoch = (value: string) => /^-?(?:0|[1-9][0-9]{0,18})\.[0-9]{6}$/u.test(value);
1368
+ const ip = (value: string) => isIP(value) !== 0;
1369
+ if (!decimal(identity.pid, 2147483647n) || !decimal(identity.datid, 4294967295n) || !epoch(identity.backendStart) || !epoch(identity.postmasterStart) || !ip(identity.address) || !decimal(identity.port, 65535n)) return undefined;
1370
+ return identity;
1371
+ }
1372
+
1373
+ private async backendAbsent(identity: BackendIdentity): Promise<boolean> {
1374
+ const rows = await this.ambient.exit(() => this.transactionCallbacks.exit(() => withoutProviderDispatchObserver(() => this.native(() => this.sql.unsafe("SELECT a.pid::text AS pid, a.datid::text AS datid, extract(epoch FROM a.backend_start)::numeric(20,6)::text AS backend_start, extract(epoch FROM pg_postmaster_start_time())::numeric(20,6)::text AS postmaster_start, coalesce(host(inet_server_addr()),'') AS address, coalesce(inet_server_port()::text,'') AS port FROM (SELECT 1) root_probe LEFT JOIN pg_stat_activity a ON a.pid = $1::int", [identity.pid])))));
1375
+ const row = rows[0];
1376
+ if (!row || typeof row.postmaster_start !== "string" || typeof row.address !== "string" || typeof row.port !== "string" || row.postmaster_start !== identity.postmasterStart || row.address !== identity.address || row.port !== identity.port) return false;
1377
+ if (row.pid === null && row.datid === null && row.backend_start === null) return true;
1378
+ const current = this.parseBackendIdentity(row);
1379
+ if (!current || current.postmasterStart !== identity.postmasterStart || current.address !== identity.address || current.port !== identity.port) return false;
1380
+ return current.pid === identity.pid && (current.datid !== identity.datid || current.backendStart !== identity.backendStart);
1381
+ }
1382
+
1383
+ private async runTransactionScope<T>(
1384
+ executor: DbExecutor,
1385
+ parent: TransactionCallbackScope,
1386
+ work: (tx: DbExecutor) => Promise<T>,
1387
+ ): Promise<T> {
1388
+ const child = beginChildTransactionScope(parent);
1389
+ const savepoint = this.nextSavepoint();
1390
+ let created = false;
1391
+ try {
1392
+ try {
1393
+ await executor.execute(`SAVEPOINT ${savepoint}`, []);
1394
+ created = true;
1395
+ } catch (error) {
1396
+ markTransactionRollbackOnly(parent, error);
1397
+ throw error;
1398
+ }
1399
+
1400
+ try {
1401
+ const result = await this.transactionCallbacks.run(child.callbacks, () => work(executor));
1402
+ assertTransactionCanCommit(child.callbacks);
1403
+ await executor.execute(`RELEASE SAVEPOINT ${savepoint}`, []);
1404
+ mergeTransactionScopeCallbacks(parent, child.callbacks);
1405
+ return result;
1406
+ } catch (error) {
1407
+ const unknown = this.ambient.getStore()?.owner?.unknownOutcome;
1408
+ if (unknown) { notifyTransactionUncertainty(child.callbacks.afterRollback); throw unknown; }
1409
+ const failure = created
1410
+ ? await this.rollbackTransactionScope(executor, savepoint, parent, error)
1411
+ : error;
1412
+ return runRollbackCallbacks(child.callbacks.afterRollback, failure);
1413
+ }
1414
+ } finally {
1415
+ endChildTransactionScope(parent, child.ownership);
1416
+ }
1417
+ }
1418
+
1419
+ private async rollbackTransactionScope(
1420
+ executor: DbExecutor,
1421
+ savepoint: string,
1422
+ parent: TransactionCallbackScope,
1423
+ cause: unknown,
1424
+ ): Promise<unknown> {
1425
+ if (this.ambient.getStore()?.owner?.rollbackAcknowledged) {
1426
+ markTransactionRollbackOnly(parent, cause);
1427
+ return cause;
1428
+ }
1429
+ const cleanupErrors: unknown[] = [];
1430
+ try {
1431
+ await executor.execute(`ROLLBACK TO SAVEPOINT ${savepoint}`, []);
1432
+ } catch (error) {
1433
+ cleanupErrors.push(error);
1434
+ }
1435
+ try {
1436
+ await executor.execute(`RELEASE SAVEPOINT ${savepoint}`, []);
1437
+ } catch (error) {
1438
+ cleanupErrors.push(error);
1439
+ }
1440
+ if (cleanupErrors.length === 0) {
1441
+ return cause;
1442
+ }
1443
+ const failure = new AggregateError(
1444
+ [cause, ...cleanupErrors],
1445
+ "PostgreSQL transaction scope failed and its savepoint could not be cleaned up.",
1446
+ );
1447
+ markTransactionRollbackOnly(parent, failure);
1448
+ return failure;
1449
+ }
1450
+
1451
+ private nextSavepoint(): string {
1452
+ this.nextTransactionScopeId = this.nextTransactionScopeId >= Number.MAX_SAFE_INTEGER
1453
+ ? 1
1454
+ : this.nextTransactionScopeId + 1;
1455
+ return this.dialect.quoteId(`bazis_scope_${this.nextTransactionScopeId}`);
1456
+ }
1457
+
1458
+ private traceSql(sql: string, params: readonly SqlParam[]): void {
1459
+ if (!this.onSql) {
1460
+ return;
1461
+ }
1462
+ this.onSql(sql, this.redactSqlParams ? redactTraceValues(params) : params);
1463
+ }
1464
+
1465
+ private traceSchemaAdmission(operation: string): void {
1466
+ this.onSql?.(`bazis.schema-admission:${operation}`, []);
1467
+ }
1468
+
1469
+ private traceOwnedStore(operation: string): void {
1470
+ this.onSql?.(`bazis.owned-store:${operation}`, []);
1471
+ }
1472
+
1473
+ private get reserveConnection(): ((options?: { signal?: AbortSignal }) => Promise<ReservedSqlLike>) | undefined {
1474
+ const reserve = (this.sql as unknown as { reserve?: (options?: { signal?: AbortSignal }) => Promise<ReservedSqlLike> }).reserve;
1475
+ return typeof reserve === "function" ? reserve.bind(this.sql) : undefined;
1476
+ }
1477
+
1478
+ private emitWarning(warning: DatabaseProviderDiagnostic): void {
1479
+ if (this.migrationLockFallbackWarned) {
1480
+ return;
1481
+ }
1482
+ this.migrationLockFallbackWarned = true;
1483
+ if (this.onWarning) {
1484
+ this.onWarning(warning);
1485
+ return;
1486
+ }
1487
+ console.warn(`[orm:postgres] ${warning.message}`);
1488
+ }
1489
+ }
1490
+
1491
+ /** PostgreSQL connection value for `ormModule({ provider: postgres(...) })`. */
1492
+ export function postgres(options: PostgresProviderOptions = {}): PostgresProvider {
1493
+ return new PostgresProvider(options);
1494
+ }