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,969 @@
1
+ import { HttpClientError, HttpErrorCode } from "./errors";
2
+ import { InspectableRedirectProtocol as redirectProtocol } from "./redirectProtocol";
3
+ import { InterceptorManager } from "./interceptors";
4
+ import { trackDownload } from "./progress";
5
+ import {
6
+ appendQuery,
7
+ basicAuthHeader,
8
+ isBodyInit,
9
+ mergeHeaderBags,
10
+ resolveUrl,
11
+ shouldPropagateCorrelation,
12
+ } from "./serialize";
13
+ import type {
14
+ HeaderBag,
15
+ HttpResponse,
16
+ RequestConfig,
17
+ RequestTransform,
18
+ ResolvedRequestConfig,
19
+ ResponseTransform,
20
+ } from "./types";
21
+
22
+ const DEFAULT_RETRY_ON: readonly number[] = [408, 429, 500, 502, 503, 504];
23
+ const DEFAULT_MAX_REDIRECTS = 20;
24
+ const MAX_REDIRECTS = 100;
25
+ const IDEMPOTENT_METHODS: ReadonlySet<string> = new Set(["GET", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"]);
26
+ const BODYLESS_METHODS: ReadonlySet<string> = new Set(["GET", "HEAD"]);
27
+ const REDIRECT_STATUSES: ReadonlySet<number> = new Set([301, 302, 303, 307, 308]);
28
+ const SAFE_CROSS_ORIGIN_HEADERS: ReadonlySet<string> = new Set([
29
+ "accept",
30
+ "accept-language",
31
+ "content-language",
32
+ "content-type",
33
+ "range",
34
+ ]);
35
+ const BODY_HEADERS: readonly string[] = [
36
+ "content-encoding",
37
+ "content-language",
38
+ "content-length",
39
+ "content-location",
40
+ "content-type",
41
+ "transfer-encoding",
42
+ ];
43
+
44
+ function defaultValidateStatus(status: number): boolean {
45
+ return status >= 200 && status < 300;
46
+ }
47
+
48
+ function delay(ms: number, signal?: AbortSignal): Promise<void> {
49
+ if (signal?.aborted) {
50
+ return Promise.reject(signal.reason);
51
+ }
52
+ return new Promise((resolve, reject) => {
53
+ const timer = setTimeout(() => {
54
+ signal?.removeEventListener("abort", onAbort);
55
+ resolve();
56
+ }, Math.max(0, ms));
57
+ const onAbort = (): void => {
58
+ clearTimeout(timer);
59
+ signal?.removeEventListener("abort", onAbort);
60
+ reject(signal?.reason);
61
+ };
62
+ signal?.addEventListener("abort", onAbort, { once: true });
63
+ if (signal?.aborted) {
64
+ onAbort();
65
+ }
66
+ });
67
+ }
68
+
69
+ /**
70
+ * Framework-agnostic, axios-class HTTP client over `fetch`.
71
+ *
72
+ * Features: `baseUrl` with prefix-preserving resolution, query `params`,
73
+ * automatic JSON request/response, `responseType` decoding, per-request
74
+ * `timeoutMs` + cancellation, request/response interceptors, `validateStatus`,
75
+ * Basic `auth`, `withCredentials`, idempotent retries with backoff, and
76
+ * pluggable correlation-header propagation. No dependency on the Bazis core.
77
+ */
78
+ export class HttpClient {
79
+ public readonly defaults: RequestConfig;
80
+ public readonly interceptors: {
81
+ readonly request: InterceptorManager<ResolvedRequestConfig>;
82
+ readonly response: InterceptorManager<HttpResponse>;
83
+ };
84
+
85
+ public constructor(defaults: RequestConfig = {}) {
86
+ this.defaults = defaults;
87
+ this.interceptors = {
88
+ request: new InterceptorManager<ResolvedRequestConfig>(),
89
+ response: new InterceptorManager<HttpResponse>(),
90
+ };
91
+ }
92
+
93
+ /** Creates a new client whose defaults are this client's defaults merged with `config`. */
94
+ public create(config: RequestConfig = {}): HttpClient {
95
+ return new HttpClient(this.mergeConfig(this.defaults, config));
96
+ }
97
+
98
+ public static create(config: RequestConfig = {}): HttpClient {
99
+ return new HttpClient(config);
100
+ }
101
+
102
+ public request<T = unknown>(config: RequestConfig): Promise<HttpResponse<T>>;
103
+ public request<T = unknown>(url: string, config?: RequestConfig): Promise<HttpResponse<T>>;
104
+ public request<T = unknown>(configOrUrl: RequestConfig | string, maybeConfig: RequestConfig = {}): Promise<HttpResponse<T>> {
105
+ const config: RequestConfig =
106
+ typeof configOrUrl === "string" ? { ...maybeConfig, url: configOrUrl } : configOrUrl;
107
+ const merged = this.mergeConfig(this.defaults, config);
108
+
109
+ // axios ordering: request interceptors run last-registered-first.
110
+ let promise: Promise<ResolvedRequestConfig> = Promise.resolve(merged);
111
+ const requestHandlers = this.interceptors.request.toArray();
112
+ for (let i = requestHandlers.length - 1; i >= 0; i -= 1) {
113
+ const handler = requestHandlers[i]!;
114
+ promise = promise.then(handler.fulfilled, handler.rejected) as Promise<ResolvedRequestConfig>;
115
+ }
116
+
117
+ let responsePromise = promise.then((cfg) => this.dispatch<T>(cfg)) as Promise<HttpResponse<T>>;
118
+ for (const handler of this.interceptors.response.toArray()) {
119
+ responsePromise = responsePromise.then(
120
+ handler.fulfilled as (value: HttpResponse<T>) => HttpResponse<T>,
121
+ handler.rejected,
122
+ ) as Promise<HttpResponse<T>>;
123
+ }
124
+ return responsePromise;
125
+ }
126
+
127
+ public get<T = unknown>(url: string, config?: RequestConfig): Promise<HttpResponse<T>> {
128
+ return this.request<T>({ ...config, method: "GET", url });
129
+ }
130
+
131
+ public delete<T = unknown>(url: string, config?: RequestConfig): Promise<HttpResponse<T>> {
132
+ return this.request<T>({ ...config, method: "DELETE", url });
133
+ }
134
+
135
+ public head<T = unknown>(url: string, config?: RequestConfig): Promise<HttpResponse<T>> {
136
+ return this.request<T>({ ...config, method: "HEAD", url });
137
+ }
138
+
139
+ public options<T = unknown>(url: string, config?: RequestConfig): Promise<HttpResponse<T>> {
140
+ return this.request<T>({ ...config, method: "OPTIONS", url });
141
+ }
142
+
143
+ public post<T = unknown>(url: string, data?: unknown, config?: RequestConfig): Promise<HttpResponse<T>> {
144
+ return this.request<T>({ ...config, method: "POST", url, data });
145
+ }
146
+
147
+ public put<T = unknown>(url: string, data?: unknown, config?: RequestConfig): Promise<HttpResponse<T>> {
148
+ return this.request<T>({ ...config, method: "PUT", url, data });
149
+ }
150
+
151
+ public patch<T = unknown>(url: string, data?: unknown, config?: RequestConfig): Promise<HttpResponse<T>> {
152
+ return this.request<T>({ ...config, method: "PATCH", url, data });
153
+ }
154
+
155
+ private mergeConfig(base: RequestConfig, override: RequestConfig): ResolvedRequestConfig {
156
+ const headers = mergeHeaderBags(base.headers, override.headers);
157
+ const merged: RequestConfig = { ...base, ...override, headers };
158
+ // Optional values are commonly forwarded as `undefined`. Do not let that
159
+ // accidentally erase inherited safety limits or origin/redirect policy;
160
+ // callers can use explicit values (`0`, `"manual"`) to opt out.
161
+ if (override.timeoutMs === undefined) {
162
+ merged.timeoutMs = base.timeoutMs;
163
+ }
164
+ if (override.maxResponseBytes === undefined) {
165
+ merged.maxResponseBytes = base.maxResponseBytes;
166
+ }
167
+ // An empty override is not a security opt-out: an absolute request URL
168
+ // already bypasses prefix resolution, while the inherited base origin must
169
+ // remain available for cross-origin credential checks.
170
+ if (override.baseUrl === undefined || (base.baseUrl !== undefined && override.baseUrl.trim().length === 0)) {
171
+ merged.baseUrl = base.baseUrl;
172
+ }
173
+ if (override.redirect === undefined) {
174
+ merged.redirect = base.redirect;
175
+ }
176
+ if (override.maxRedirects === undefined) {
177
+ merged.maxRedirects = base.maxRedirects;
178
+ }
179
+ if (override.inspectableRedirects === undefined) {
180
+ merged.inspectableRedirects = base.inspectableRedirects;
181
+ }
182
+ if (base.params || override.params) {
183
+ merged.params = { ...base.params, ...override.params };
184
+ }
185
+ if (base.retry || override.retry) {
186
+ merged.retry = { ...base.retry, ...override.retry };
187
+ }
188
+ return { ...merged, method: String(override.method ?? base.method ?? "GET").toUpperCase() };
189
+ }
190
+
191
+ private async dispatch<T>(config: ResolvedRequestConfig): Promise<HttpResponse<T>> {
192
+ if (config.inspectableRedirects !== undefined && typeof config.inspectableRedirects !== "boolean") {
193
+ throw new HttpClientError("inspectableRedirects must be a boolean", { config });
194
+ }
195
+ validateNonNegativeSafeInteger(config.timeoutMs, "timeoutMs", config);
196
+ validateNonNegativeSafeInteger(config.maxResponseBytes, "maxResponseBytes", config);
197
+ const method = config.method;
198
+ const resolvedUrl = appendQuery(resolveUrl(config.baseUrl, config.url ?? ""), config.params, config.paramsSerializer);
199
+ const builtHeaders = this.buildHeaders(config, resolvedUrl);
200
+ const headers = builtHeaders.values;
201
+ let body: RequestInit["body"];
202
+ if (!BODYLESS_METHODS.has(method)) {
203
+ let data = config.data;
204
+ for (const transform of toArray<RequestTransform>(config.transformRequest)) {
205
+ data = transform(data, headers);
206
+ }
207
+ body = this.buildBody(data, headers);
208
+ }
209
+ // `transformRequest` intentionally receives a mutable header bag. Re-run
210
+ // the origin policy afterwards so a transform cannot re-introduce auth,
211
+ // API-key or other custom secrets that `buildHeaders` already stripped.
212
+ if (!maySendCredentials(config, resolvedUrl)) {
213
+ stripUnsafeCrossOriginHeaderBag(headers, builtHeaders.correlationNames);
214
+ }
215
+
216
+ const init: RequestInit & { duplex?: "half" } = { method, headers: toHeaders(headers) };
217
+ if (body !== undefined) {
218
+ init.body = body;
219
+ // Node Fetch requires an explicit duplex mode for ReadableStream bodies.
220
+ if (!isReplayableBody(body)) init.duplex = "half";
221
+ }
222
+ const credentials = config.credentials ?? (config.withCredentials ? "include" : undefined);
223
+ if (credentials) {
224
+ init.credentials = maySendCredentials(config, resolvedUrl) ? credentials : "omit";
225
+ }
226
+ if (config.cache) init.cache = config.cache;
227
+ if (config.mode) init.mode = config.mode;
228
+ if (config.keepalive !== undefined) init.keepalive = config.keepalive;
229
+
230
+ const fetchImpl = config.fetch ?? globalThis.fetch;
231
+ const transport = await this.execute(fetchImpl, resolvedUrl, init, config, builtHeaders.correlationNames);
232
+ const raw = transport.response;
233
+ let readFailure: unknown;
234
+ let response = enforceResponseLimit(raw, config, error => { readFailure = error; });
235
+
236
+ const validate = config.validateStatus;
237
+ let ok: boolean;
238
+ try {
239
+ ok = validate === null ? true : (validate ?? defaultValidateStatus)(response.status);
240
+ } catch (error) {
241
+ cancelResponseBody(response);
242
+ throw error;
243
+ }
244
+ // Normalize only body-read failures. User callbacks retain their exceptions,
245
+ // even if they throw a TimeoutError or abort the caller's signal themselves.
246
+ const rejectRead = (error: unknown): never => {
247
+ // Browser Response consumers can replace a stream's original error with
248
+ // TypeError. Preserve the reason observed by our reader before that loss.
249
+ throw responseReadError(readFailure ?? error, config, transport.signal);
250
+ };
251
+ if (config.onDownloadProgress && config.responseType !== "stream") {
252
+ response = await trackDownload(response, config.onDownloadProgress, rejectRead);
253
+ }
254
+ let decoded = await decodeBody(response, config, ok, raw, rejectRead);
255
+ for (const transform of toArray<ResponseTransform>(config.transformResponse)) {
256
+ decoded = transform(decoded, response.headers, response.status);
257
+ }
258
+ const data = decoded as T;
259
+ const result: HttpResponse<T> = {
260
+ data,
261
+ status: response.status,
262
+ statusText: response.statusText,
263
+ headers: response.headers,
264
+ config,
265
+ raw,
266
+ };
267
+
268
+ if (!ok) {
269
+ throw new HttpClientError(`Request failed with status ${response.status}`, {
270
+ config,
271
+ code: HttpErrorCode.BadStatus,
272
+ status: response.status,
273
+ response: result,
274
+ });
275
+ }
276
+ return result;
277
+ }
278
+
279
+ private buildHeaders(
280
+ config: ResolvedRequestConfig,
281
+ resolvedUrl: string,
282
+ ): { readonly values: HeaderBag; readonly correlationNames: readonly string[] } {
283
+ let correlation: HeaderBag | undefined;
284
+ if (config.correlationHeaders) {
285
+ const policy = config.propagateCorrelation ?? "same-origin";
286
+ if (shouldPropagateCorrelation(resolvedUrl, policy, config.baseUrl)) {
287
+ correlation = config.correlationHeaders();
288
+ }
289
+ }
290
+ const headers = mergeHeaderBags(correlation, config.headers);
291
+ const allowCredentials = maySendCredentials(config, resolvedUrl);
292
+ if (config.auth && allowCredentials) {
293
+ headers["authorization"] = basicAuthHeader(config.auth.username, config.auth.password);
294
+ }
295
+ if (!allowCredentials) {
296
+ stripUnsafeCrossOriginHeaderBag(headers, Object.keys(correlation ?? {}));
297
+ }
298
+ return {
299
+ values: headers,
300
+ correlationNames: Object.keys(correlation ?? {}).map((name) => name.toLowerCase()),
301
+ };
302
+ }
303
+
304
+ private buildBody(data: unknown, headers: HeaderBag): RequestInit["body"] {
305
+ if (data === undefined || data === null) {
306
+ return undefined;
307
+ }
308
+ if (isBodyInit(data)) {
309
+ return data as RequestInit["body"];
310
+ }
311
+ if (!("content-type" in headers)) {
312
+ headers["content-type"] = "application/json";
313
+ }
314
+ return JSON.stringify(data);
315
+ }
316
+
317
+ private async execute(
318
+ fetchImpl: typeof fetch,
319
+ url: string,
320
+ init: RequestInit,
321
+ config: ResolvedRequestConfig,
322
+ correlationHeaderNames: readonly string[],
323
+ ): Promise<{ response: Response; signal: AbortSignal | undefined }> {
324
+ const userSignal = config.signal ?? undefined;
325
+ const timeoutMs = config.timeoutMs ?? 0;
326
+ const deadline = timeoutMs > 0 ? Date.now() + timeoutMs : undefined;
327
+ const retry = config.retry;
328
+ const configuredRetries = retry?.maxRetries ?? 0;
329
+ const maxRetries = IDEMPOTENT_METHODS.has(config.method)
330
+ && isReplayableBody(init.body)
331
+ && Number.isFinite(configuredRetries)
332
+ ? Math.min(100, Math.max(0, Math.floor(configuredRetries)))
333
+ : 0;
334
+ const configuredBackoff = retry?.backoffMs ?? 100;
335
+ const backoffMs = Number.isFinite(configuredBackoff) && configuredBackoff >= 0
336
+ ? Math.min(configuredBackoff, 2_147_483_647)
337
+ : 100;
338
+ const retryOn = new Set(retry?.retryOn ?? DEFAULT_RETRY_ON);
339
+ const redirectMode = config.redirect ?? "follow";
340
+ const maxRedirects = redirectMode === "follow" ? resolveMaxRedirects(config) : 0;
341
+ const browserInit = browserRedirectInit(url, init, config, redirectMode, maxRedirects, correlationHeaderNames);
342
+
343
+ let lastError: unknown;
344
+ for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
345
+ const remainingMs = deadline === undefined ? 0 : deadline - Date.now();
346
+ if (deadline !== undefined && remainingMs <= 0) {
347
+ throw timeoutError(config, timeoutMs, lastError);
348
+ }
349
+ const signal = buildSignal(remainingMs, userSignal);
350
+ try {
351
+ const response = browserInit ? await fetchImpl(url, { ...browserInit, signal }) : await this.executeRedirectChain(
352
+ fetchImpl,
353
+ url,
354
+ init,
355
+ config,
356
+ redirectMode,
357
+ maxRedirects,
358
+ correlationHeaderNames,
359
+ signal,
360
+ );
361
+ if (attempt < maxRetries && retryOn.has(response.status)) {
362
+ const retryDelay = retryDelayMs(response, exponentialBackoff(backoffMs, attempt));
363
+ cancelResponseBody(response);
364
+ await waitForRetry(retryDelay, userSignal, deadline, config, timeoutMs);
365
+ continue;
366
+ }
367
+ return { response, signal };
368
+ } catch (error) {
369
+ lastError = error;
370
+ if (error instanceof HttpClientError) {
371
+ throw error;
372
+ }
373
+ if (userSignal?.aborted) {
374
+ throw new HttpClientError("Request canceled", { config, code: HttpErrorCode.Canceled, cause: error });
375
+ }
376
+ // WebKit may reject fetch with TypeError instead of forwarding the
377
+ // timeout reason. The signal remains authoritative, including body reads.
378
+ if (isTimeout(error) || (signal?.aborted && isTimeout(signal.reason))) {
379
+ if (attempt < maxRetries) {
380
+ await waitForRetry(exponentialBackoff(backoffMs, attempt), userSignal, deadline, config, timeoutMs);
381
+ continue;
382
+ }
383
+ throw new HttpClientError(`Request timed out after ${timeoutMs}ms`, {
384
+ config,
385
+ code: HttpErrorCode.Timeout,
386
+ cause: error,
387
+ });
388
+ }
389
+ if (attempt >= maxRetries) {
390
+ throw new HttpClientError(messageOf(error) ?? "Network request failed", {
391
+ config,
392
+ code: HttpErrorCode.Network,
393
+ cause: error,
394
+ });
395
+ }
396
+ await waitForRetry(exponentialBackoff(backoffMs, attempt), userSignal, deadline, config, timeoutMs);
397
+ }
398
+ }
399
+ throw new HttpClientError(messageOf(lastError) ?? "Network request failed", {
400
+ config,
401
+ code: HttpErrorCode.Network,
402
+ cause: lastError,
403
+ });
404
+ }
405
+
406
+ private async executeRedirectChain(
407
+ fetchImpl: typeof fetch,
408
+ url: string,
409
+ init: RequestInit,
410
+ config: ResolvedRequestConfig,
411
+ redirectMode: NonNullable<RequestInit["redirect"]>,
412
+ maxRedirects: number,
413
+ correlationHeaderNames: readonly string[],
414
+ signal: AbortSignal | undefined,
415
+ ): Promise<Response> {
416
+ let currentUrl = url;
417
+ let currentInit: RequestInit = { ...init, redirect: "manual" };
418
+ let redirects = 0;
419
+
420
+ while (true) {
421
+ const inspectable = config.inspectableRedirects === true && redirectMode === "follow";
422
+ const requestInit = inspectable ? inspectableRequestInit(currentInit) : currentInit;
423
+ const response = await fetchImpl(
424
+ currentUrl,
425
+ signal ? { ...requestInit, signal, redirect: "manual" } : requestInit,
426
+ );
427
+ if (response.type === "opaqueredirect") {
428
+ if (redirectMode === "manual") {
429
+ return response;
430
+ }
431
+ throw notInspectable(config, "Fetch hides this redirect; exact limits require inspectableRedirects on both client and server");
432
+ }
433
+ const status = inspectable ? inspectableResponseStatus(response, config) : response.status;
434
+ if (!REDIRECT_STATUSES.has(status)) {
435
+ return response;
436
+ }
437
+
438
+ const location = response.headers.get("location");
439
+ if (location === null) {
440
+ return response;
441
+ }
442
+ if (redirectMode === "manual") {
443
+ return response;
444
+ }
445
+ if (redirectMode === "error") {
446
+ cancelResponseBody(response);
447
+ throw redirectError(config, "Redirects are disabled for this request");
448
+ }
449
+ if (redirects >= maxRedirects) {
450
+ cancelResponseBody(response);
451
+ throw new HttpClientError(`Request exceeded ${maxRedirects} redirects`, {
452
+ config,
453
+ code: HttpErrorCode.TooManyRedirects,
454
+ });
455
+ }
456
+
457
+ cancelResponseBody(response);
458
+ const redirectBaseUrl = resolveRedirectBaseUrl(currentUrl, response, config);
459
+ const nextUrl = resolveRedirectUrl(location, redirectBaseUrl, config);
460
+ const nextInit = redirectedRequestInit(
461
+ currentInit,
462
+ status,
463
+ redirectBaseUrl,
464
+ nextUrl,
465
+ config,
466
+ correlationHeaderNames,
467
+ );
468
+ currentUrl = nextUrl;
469
+ currentInit = nextInit;
470
+ redirects += 1;
471
+ }
472
+ }
473
+ }
474
+
475
+ function browserRedirectInit(
476
+ url: string,
477
+ init: RequestInit,
478
+ config: ResolvedRequestConfig,
479
+ redirect: NonNullable<RequestInit["redirect"]>,
480
+ maxRedirects: number,
481
+ correlationHeaderNames: readonly string[],
482
+ ): RequestInit | undefined {
483
+ // Window and Worker fetch hide every manual redirect behind an opaque
484
+ // response, even on the same origin. Let the browser follow only when it
485
+ // can enforce the origin boundary before dispatching the next request.
486
+ const browserLocation = (globalThis as { location?: { href: string; origin: string } }).location;
487
+ if (!browserLocation) return undefined;
488
+ if (redirect !== "follow") return { ...init, redirect };
489
+ if (config.inspectableRedirects) return undefined;
490
+ if (maxRedirects === 0) return { ...init, redirect: "error" };
491
+ // Native Fetch follows at most 20 hops, so larger budgets are safe too.
492
+ // A smaller budget requires visible manual hops: ordinary responses still
493
+ // work, but an opaque redirect must fail before any uncounted follow.
494
+ if (maxRedirects < DEFAULT_MAX_REDIRECTS) return undefined;
495
+ const headers = new Headers(init.headers);
496
+ const restrictedCorrelation = config.propagateCorrelation !== "all"
497
+ && correlationHeaderNames.some((name) => headers.has(name));
498
+ if (config.allowCrossOriginCredentials && !restrictedCorrelation) {
499
+ return { ...init, redirect: "follow" };
500
+ }
501
+ const target = new URL(url, browserLocation.href);
502
+ const samePageOrigin = target.origin !== "null" && target.origin === browserLocation.origin;
503
+ const headersSafeToRedirect = [...headers.keys()].every((name) => SAFE_CROSS_ORIGIN_HEADERS.has(name)
504
+ // Fetch itself removes Authorization at a cross-origin redirect. It does
505
+ // not strip arbitrary API-key or correlation headers.
506
+ || name === "authorization"
507
+ || (config.propagateCorrelation === "all" && correlationHeaderNames.includes(name)));
508
+ if (headersSafeToRedirect && !restrictedCorrelation && init.body == null && init.credentials !== "include") {
509
+ return { ...init, redirect: "follow", credentials: samePageOrigin ? init.credentials ?? "same-origin" : "omit" };
510
+ }
511
+ if (samePageOrigin) {
512
+ return { ...init, redirect: "follow", mode: "same-origin" };
513
+ }
514
+ // An authenticated foreign API may still return an ordinary CORS response.
515
+ // Its hidden redirect cannot be followed safely without an explicit opt-in.
516
+ return undefined;
517
+ }
518
+
519
+ function inspectableRequestInit(init: RequestInit): RequestInit {
520
+ const headers = new Headers(init.headers);
521
+ headers.set(redirectProtocol.header, redirectProtocol.version);
522
+ return { ...init, headers, redirect: "manual", cache: "no-store" };
523
+ }
524
+
525
+ function inspectableResponseStatus(response: Response, config: RequestConfig): number {
526
+ const encoded = response.headers.get(redirectProtocol.statusHeader);
527
+ if (response.headers.get(redirectProtocol.header) !== redirectProtocol.version) {
528
+ // An HTTP error cannot conceal a negotiated redirect (its wire status is
529
+ // always 200). Preserve errors emitted before/after the server pipeline.
530
+ if (response.status >= 400 && encoded === null) return response.status;
531
+ cancelResponseBody(response);
532
+ throw notInspectable(config, "The peer did not acknowledge inspectable redirects; enable the option on the server and allow its CORS headers");
533
+ }
534
+ if (encoded === null) return response.status;
535
+ const status = Number(encoded);
536
+ if (response.status !== 200 || !REDIRECT_STATUSES.has(status) || encoded !== String(status) || !response.headers.has("location")) {
537
+ cancelResponseBody(response);
538
+ throw notInspectable(config, "Invalid inspectable redirect metadata");
539
+ }
540
+ return status;
541
+ }
542
+
543
+ function notInspectable(config: RequestConfig, message: string): HttpClientError {
544
+ return new HttpClientError(message, { config, code: HttpErrorCode.RedirectNotInspectable });
545
+ }
546
+
547
+ function resolveMaxRedirects(config: ResolvedRequestConfig): number {
548
+ const configured = config.maxRedirects ?? DEFAULT_MAX_REDIRECTS;
549
+ if (!Number.isSafeInteger(configured) || configured < 0 || configured > MAX_REDIRECTS) {
550
+ throw new HttpClientError(`maxRedirects must be an integer between 0 and ${MAX_REDIRECTS}`, { config });
551
+ }
552
+ return configured;
553
+ }
554
+
555
+ function resolveRedirectUrl(
556
+ location: string,
557
+ baseUrl: string,
558
+ config: ResolvedRequestConfig,
559
+ ): string {
560
+ try {
561
+ const target = new URL(location, baseUrl);
562
+ if ((target.protocol !== "http:" && target.protocol !== "https:") || target.username || target.password) {
563
+ throw new TypeError("Only credential-free HTTP(S) redirect URLs are supported");
564
+ }
565
+ return target.toString();
566
+ } catch (error) {
567
+ throw redirectError(config, "Invalid redirect URL", error);
568
+ }
569
+ }
570
+
571
+ function resolveRedirectBaseUrl(
572
+ currentUrl: string,
573
+ response: Response,
574
+ config: ResolvedRequestConfig,
575
+ ): string {
576
+ if (absoluteUrl(currentUrl)) {
577
+ return currentUrl;
578
+ }
579
+ if (absoluteUrl(response.url)) {
580
+ return response.url;
581
+ }
582
+ throw redirectError(config, "Redirect base URL is not available");
583
+ }
584
+
585
+ function redirectedRequestInit(
586
+ init: RequestInit,
587
+ status: number,
588
+ currentUrl: string,
589
+ nextUrl: string,
590
+ config: ResolvedRequestConfig,
591
+ correlationHeaderNames: readonly string[],
592
+ ): RequestInit {
593
+ const headers = new Headers(init.headers);
594
+ let method = String(init.method ?? "GET").toUpperCase();
595
+ let body = init.body;
596
+ const switchesToGet = ((status === 301 || status === 302) && method === "POST")
597
+ || (status === 303 && method !== "GET" && method !== "HEAD");
598
+
599
+ if (switchesToGet) {
600
+ method = "GET";
601
+ body = undefined;
602
+ for (const name of BODY_HEADERS) {
603
+ headers.delete(name);
604
+ }
605
+ } else if (body !== undefined && body !== null && !isReplayableBody(body)) {
606
+ throw new HttpClientError("Redirect cannot safely replay the request body", {
607
+ config,
608
+ code: HttpErrorCode.RedirectBodyNotReplayable,
609
+ });
610
+ }
611
+
612
+ let credentials = init.credentials;
613
+ const crossesOrigin = !sameOrigin(currentUrl, nextUrl);
614
+ if (
615
+ crossesOrigin &&
616
+ !config.allowCrossOriginCredentials &&
617
+ body !== undefined &&
618
+ body !== null
619
+ ) {
620
+ throw new HttpClientError(
621
+ "Cross-origin redirect cannot safely replay the request body without allowCrossOriginCredentials",
622
+ { config, code: HttpErrorCode.RedirectBodyNotReplayable },
623
+ );
624
+ }
625
+ if (!config.allowCrossOriginCredentials && crossesOrigin) {
626
+ const policy = config.propagateCorrelation ?? "same-origin";
627
+ const allowedCorrelationNames = shouldPropagateCorrelation(nextUrl, policy, config.baseUrl)
628
+ ? correlationHeaderNames
629
+ : [];
630
+ stripUnsafeCrossOriginHeaders(headers, allowedCorrelationNames);
631
+ credentials = "omit";
632
+ }
633
+ if (correlationHeaderNames.length > 0) {
634
+ const policy = config.propagateCorrelation ?? "same-origin";
635
+ if (!shouldPropagateCorrelation(nextUrl, policy, config.baseUrl)) {
636
+ for (const name of correlationHeaderNames) {
637
+ headers.delete(name);
638
+ }
639
+ }
640
+ }
641
+
642
+ return { ...init, method, body, headers, credentials, redirect: "manual" };
643
+ }
644
+
645
+ function isReplayableBody(body: RequestInit["body"]): boolean {
646
+ if (body === undefined || body === null) {
647
+ return true;
648
+ }
649
+ return !(typeof (body as { getReader?: unknown }).getReader === "function"
650
+ || (typeof ReadableStream !== "undefined" && body instanceof ReadableStream));
651
+ }
652
+
653
+ function sameOrigin(left: string, right: string): boolean {
654
+ try {
655
+ return new URL(left).origin === new URL(right).origin;
656
+ } catch {
657
+ return false;
658
+ }
659
+ }
660
+
661
+ function absoluteUrl(value: string): boolean {
662
+ try {
663
+ return new URL(value).origin !== "null";
664
+ } catch {
665
+ return false;
666
+ }
667
+ }
668
+
669
+ function cancelResponseBody(response: Response): void {
670
+ // Cancellation closes the stream immediately, but the producer's cleanup
671
+ // promise need not settle. It must not retain a retry, redirect or deadline.
672
+ void response.body?.cancel().catch(() => undefined);
673
+ }
674
+
675
+ function redirectError(config: RequestConfig, message: string, cause?: unknown): HttpClientError {
676
+ return new HttpClientError(message, {
677
+ config,
678
+ code: HttpErrorCode.Network,
679
+ cause,
680
+ });
681
+ }
682
+
683
+ function maySendCredentials(config: ResolvedRequestConfig, resolvedUrl: string): boolean {
684
+ if (config.allowCrossOriginCredentials || !config.baseUrl) {
685
+ return true;
686
+ }
687
+ try {
688
+ return new URL(config.baseUrl).origin === new URL(resolvedUrl).origin;
689
+ } catch {
690
+ return false;
691
+ }
692
+ }
693
+
694
+ function stripUnsafeCrossOriginHeaderBag(headers: HeaderBag, allowedCorrelationNames: readonly string[]): void {
695
+ const allowed = new Set(allowedCorrelationNames.map((name) => name.toLowerCase()));
696
+ for (const name of Object.keys(headers)) {
697
+ const normalized = name.toLowerCase();
698
+ if (!SAFE_CROSS_ORIGIN_HEADERS.has(normalized) && !allowed.has(normalized)) {
699
+ delete headers[name];
700
+ }
701
+ }
702
+ }
703
+
704
+ function stripUnsafeCrossOriginHeaders(headers: Headers, allowedCorrelationNames: readonly string[]): void {
705
+ const allowed = new Set(allowedCorrelationNames.map((name) => name.toLowerCase()));
706
+ const remove: string[] = [];
707
+ headers.forEach((_value, name) => {
708
+ const normalized = name.toLowerCase();
709
+ if (!SAFE_CROSS_ORIGIN_HEADERS.has(normalized) && !allowed.has(normalized)) {
710
+ remove.push(name);
711
+ }
712
+ });
713
+ for (const name of remove) {
714
+ headers.delete(name);
715
+ }
716
+ }
717
+
718
+ function validateNonNegativeSafeInteger(
719
+ value: number | undefined,
720
+ field: "timeoutMs" | "maxResponseBytes",
721
+ config: RequestConfig,
722
+ ): void {
723
+ if (value !== undefined && (!Number.isSafeInteger(value) || value < 0)) {
724
+ throw new HttpClientError(`${field} must be a non-negative safe integer`, { config });
725
+ }
726
+ }
727
+
728
+ function enforceResponseLimit(
729
+ response: Response,
730
+ config: ResolvedRequestConfig,
731
+ captureReadFailure: (error: unknown) => void,
732
+ ): Response {
733
+ const configured = config.maxResponseBytes;
734
+ if (configured === undefined || configured === 0) {
735
+ return response;
736
+ }
737
+ // HEAD and bodyless statuses may legitimately advertise the size of the
738
+ // corresponding representation. A response cap limits bytes actually
739
+ // readable by the client, not metadata for a body that is absent.
740
+ if (!response.body || response.status === 204 || response.status === 205 || response.status === 304) {
741
+ return response;
742
+ }
743
+ const advertised = Number(response.headers.get("content-length"));
744
+ const encoding = response.headers.get("content-encoding")?.trim().toLowerCase();
745
+ // Fetch may decode the body while retaining the encoded Content-Length.
746
+ // Only identity representations allow this early check; the stream below
747
+ // always counts the bytes actually exposed to the caller, including gzip.
748
+ // A CORS response can expose Content-Length while hiding Content-Encoding.
749
+ const identityLength = encoding === "identity" || (!encoding && response.type !== "cors");
750
+ if (identityLength && Number.isFinite(advertised) && advertised > configured) {
751
+ cancelResponseBody(response);
752
+ throw responseTooLargeError(config, configured);
753
+ }
754
+
755
+ const reader = response.body.getReader();
756
+ let received = 0;
757
+ let finished = false;
758
+ const finish = (cancel: boolean, reason?: unknown): void => {
759
+ if (finished) return;
760
+ finished = true;
761
+ if (cancel) void reader.cancel(reason).catch(() => undefined);
762
+ reader.releaseLock();
763
+ };
764
+ const body = new ReadableStream<Uint8Array>({
765
+ async pull(controller) {
766
+ try {
767
+ const item = await reader.read();
768
+ if (finished) return;
769
+ if (item.done) {
770
+ finish(false);
771
+ controller.close();
772
+ return;
773
+ }
774
+ received += item.value.byteLength;
775
+ if (received > configured) {
776
+ const error = responseTooLargeError(config, configured);
777
+ captureReadFailure(error);
778
+ finish(true);
779
+ controller.error(error);
780
+ return;
781
+ }
782
+ controller.enqueue(item.value);
783
+ } catch (error) {
784
+ if (finished) return;
785
+ captureReadFailure(error);
786
+ finish(true, error);
787
+ controller.error(error);
788
+ }
789
+ },
790
+ cancel(reason) {
791
+ finish(true, reason);
792
+ },
793
+ });
794
+ return new Response(body, {
795
+ status: response.status,
796
+ statusText: response.statusText,
797
+ headers: response.headers,
798
+ });
799
+ }
800
+
801
+ function responseTooLargeError(config: RequestConfig, maxBytes: number): HttpClientError {
802
+ return new HttpClientError(`Response exceeded ${maxBytes} bytes`, {
803
+ config,
804
+ code: HttpErrorCode.ResponseTooLarge,
805
+ });
806
+ }
807
+
808
+ function timeoutError(config: RequestConfig, timeoutMs: number, cause?: unknown): HttpClientError {
809
+ return new HttpClientError(`Request timed out after ${timeoutMs}ms`, {
810
+ config,
811
+ code: HttpErrorCode.Timeout,
812
+ cause,
813
+ });
814
+ }
815
+
816
+ function retryDelayMs(response: Response, fallbackMs: number): number {
817
+ const retryAfter = response.headers.get("retry-after");
818
+ if (retryAfter === null) {
819
+ return fallbackMs;
820
+ }
821
+ const seconds = Number(retryAfter);
822
+ if (Number.isFinite(seconds) && seconds >= 0) {
823
+ return Math.min(2_147_483_647, seconds * 1_000);
824
+ }
825
+ const date = Date.parse(retryAfter);
826
+ return Number.isFinite(date) ? Math.min(2_147_483_647, Math.max(0, date - Date.now())) : fallbackMs;
827
+ }
828
+
829
+ function exponentialBackoff(baseMs: number, attempt: number): number {
830
+ return Math.min(2_147_483_647, baseMs * 2 ** Math.min(attempt, 30));
831
+ }
832
+
833
+ async function waitForRetry(
834
+ ms: number,
835
+ userSignal: AbortSignal | undefined,
836
+ deadline: number | undefined,
837
+ config: RequestConfig,
838
+ timeoutMs: number,
839
+ ): Promise<void> {
840
+ const remainingMs = deadline === undefined ? 0 : deadline - Date.now();
841
+ if (deadline !== undefined && remainingMs <= 0) {
842
+ throw timeoutError(config, timeoutMs);
843
+ }
844
+ try {
845
+ await delay(ms, buildSignal(remainingMs, userSignal));
846
+ } catch (error) {
847
+ if (userSignal?.aborted) {
848
+ throw new HttpClientError("Request canceled", { config, code: HttpErrorCode.Canceled, cause: error });
849
+ }
850
+ if (isTimeout(error) || (deadline !== undefined && Date.now() >= deadline)) {
851
+ throw timeoutError(config, timeoutMs, error);
852
+ }
853
+ throw error;
854
+ }
855
+ }
856
+
857
+ function toArray<F>(value: F | readonly F[] | undefined): readonly F[] {
858
+ if (value === undefined) {
859
+ return [];
860
+ }
861
+ return Array.isArray(value) ? value : [value as F];
862
+ }
863
+
864
+ function buildSignal(timeoutMs: number, userSignal?: AbortSignal): AbortSignal | undefined {
865
+ const signals: AbortSignal[] = [];
866
+ if (timeoutMs > 0) {
867
+ signals.push(AbortSignal.timeout(timeoutMs));
868
+ }
869
+ if (userSignal) {
870
+ signals.push(userSignal);
871
+ }
872
+ if (signals.length === 0) {
873
+ return undefined;
874
+ }
875
+ if (signals.length === 1) {
876
+ return signals[0];
877
+ }
878
+ return AbortSignal.any(signals);
879
+ }
880
+
881
+ function toHeaders(bag: HeaderBag): Headers {
882
+ const headers = new Headers();
883
+ for (const [key, value] of Object.entries(bag)) {
884
+ headers.set(key, value);
885
+ }
886
+ return headers;
887
+ }
888
+
889
+ async function decodeBody(
890
+ response: Response,
891
+ config: ResolvedRequestConfig,
892
+ statusAccepted: boolean,
893
+ raw: Response,
894
+ rejectRead: (error: unknown) => never,
895
+ ): Promise<unknown> {
896
+ const responseType = config.responseType;
897
+ if (responseType === "stream") {
898
+ return response.body;
899
+ }
900
+ if (responseType === "arrayBuffer") {
901
+ return response.arrayBuffer().catch(rejectRead);
902
+ }
903
+ if (responseType === "blob") {
904
+ return response.blob().catch(rejectRead);
905
+ }
906
+ if (response.status === 204 || response.status === 205 || response.headers.get("content-length") === "0") {
907
+ return undefined;
908
+ }
909
+ const text = await response.text().catch(rejectRead);
910
+ if (responseType === "text") return text;
911
+ // Infer from content-type.
912
+ const contentType = response.headers.get("content-type") ?? "";
913
+ if (responseType === "json" || contentType.includes("application/json") || contentType.includes("+json")) {
914
+ return parseJsonSafe(text, raw, config, statusAccepted);
915
+ }
916
+ return text;
917
+ }
918
+
919
+ function parseJsonSafe(text: string, raw: Response, config: ResolvedRequestConfig, statusAccepted: boolean): unknown {
920
+ if (text.length === 0) {
921
+ return undefined;
922
+ }
923
+ try {
924
+ return JSON.parse(text);
925
+ } catch (cause) {
926
+ // A malformed error page still carries useful HTTP status and headers.
927
+ // Retain the already-read text for diagnostics, without cloning
928
+ // or reading the transport body a second time.
929
+ throw new HttpClientError(
930
+ statusAccepted ? "Response contains invalid JSON" : `Request failed with status ${raw.status}`,
931
+ {
932
+ config,
933
+ code: statusAccepted ? HttpErrorCode.BadResponse : HttpErrorCode.BadStatus,
934
+ response: {
935
+ data: text,
936
+ status: raw.status,
937
+ statusText: raw.statusText,
938
+ headers: raw.headers,
939
+ config,
940
+ raw,
941
+ },
942
+ cause,
943
+ },
944
+ );
945
+ }
946
+ }
947
+
948
+ function responseReadError(error: unknown, config: ResolvedRequestConfig, signal?: AbortSignal): HttpClientError {
949
+ if (error instanceof HttpClientError) return error;
950
+ if (config.signal?.aborted) {
951
+ return new HttpClientError("Request canceled", { config, code: HttpErrorCode.Canceled, cause: error });
952
+ }
953
+ if (isTimeout(error) || (signal?.aborted && isTimeout(signal.reason))) {
954
+ return timeoutError(config, config.timeoutMs ?? 0, error);
955
+ }
956
+ return new HttpClientError(messageOf(error) ?? "Response body read failed", {
957
+ config,
958
+ code: HttpErrorCode.Network,
959
+ cause: error,
960
+ });
961
+ }
962
+
963
+ function isTimeout(error: unknown): boolean {
964
+ return typeof error === "object" && error !== null && (error as { name?: string }).name === "TimeoutError";
965
+ }
966
+
967
+ function messageOf(error: unknown): string | undefined {
968
+ return error instanceof Error ? error.message : undefined;
969
+ }