@redocly/client-generator 0.0.0-snapshot.1784554531

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 (250) hide show
  1. package/README.md +506 -0
  2. package/lib/config-file.d.ts +8 -0
  3. package/lib/config-file.d.ts.map +1 -0
  4. package/lib/config-file.js +32 -0
  5. package/lib/config-file.js.map +1 -0
  6. package/lib/config.d.ts +63 -0
  7. package/lib/config.d.ts.map +1 -0
  8. package/lib/config.js +2 -0
  9. package/lib/config.js.map +1 -0
  10. package/lib/emitters/auth.d.ts +14 -0
  11. package/lib/emitters/auth.d.ts.map +1 -0
  12. package/lib/emitters/auth.js +30 -0
  13. package/lib/emitters/auth.js.map +1 -0
  14. package/lib/emitters/client-assembly.d.ts +15 -0
  15. package/lib/emitters/client-assembly.d.ts.map +1 -0
  16. package/lib/emitters/client-assembly.js +299 -0
  17. package/lib/emitters/client-assembly.js.map +1 -0
  18. package/lib/emitters/descriptor.d.ts +22 -0
  19. package/lib/emitters/descriptor.d.ts.map +1 -0
  20. package/lib/emitters/descriptor.js +207 -0
  21. package/lib/emitters/descriptor.js.map +1 -0
  22. package/lib/emitters/emit-options.d.ts +71 -0
  23. package/lib/emitters/emit-options.d.ts.map +1 -0
  24. package/lib/emitters/emit-options.js +28 -0
  25. package/lib/emitters/emit-options.js.map +1 -0
  26. package/lib/emitters/faker.d.ts +11 -0
  27. package/lib/emitters/faker.d.ts.map +1 -0
  28. package/lib/emitters/faker.js +204 -0
  29. package/lib/emitters/faker.js.map +1 -0
  30. package/lib/emitters/identifier.d.ts +28 -0
  31. package/lib/emitters/identifier.d.ts.map +1 -0
  32. package/lib/emitters/identifier.js +94 -0
  33. package/lib/emitters/identifier.js.map +1 -0
  34. package/lib/emitters/inline-runtime.d.ts +11 -0
  35. package/lib/emitters/inline-runtime.d.ts.map +1 -0
  36. package/lib/emitters/inline-runtime.js +97 -0
  37. package/lib/emitters/inline-runtime.js.map +1 -0
  38. package/lib/emitters/jsdoc.d.ts +9 -0
  39. package/lib/emitters/jsdoc.d.ts.map +1 -0
  40. package/lib/emitters/jsdoc.js +83 -0
  41. package/lib/emitters/jsdoc.js.map +1 -0
  42. package/lib/emitters/mock.d.ts +22 -0
  43. package/lib/emitters/mock.d.ts.map +1 -0
  44. package/lib/emitters/mock.js +243 -0
  45. package/lib/emitters/mock.js.map +1 -0
  46. package/lib/emitters/operation-aliases.d.ts +27 -0
  47. package/lib/emitters/operation-aliases.d.ts.map +1 -0
  48. package/lib/emitters/operation-aliases.js +138 -0
  49. package/lib/emitters/operation-aliases.js.map +1 -0
  50. package/lib/emitters/operation-signature.d.ts +23 -0
  51. package/lib/emitters/operation-signature.d.ts.map +1 -0
  52. package/lib/emitters/operation-signature.js +34 -0
  53. package/lib/emitters/operation-signature.js.map +1 -0
  54. package/lib/emitters/operation-types.d.ts +32 -0
  55. package/lib/emitters/operation-types.d.ts.map +1 -0
  56. package/lib/emitters/operation-types.js +117 -0
  57. package/lib/emitters/operation-types.js.map +1 -0
  58. package/lib/emitters/operations.d.ts +39 -0
  59. package/lib/emitters/operations.d.ts.map +1 -0
  60. package/lib/emitters/operations.js +33 -0
  61. package/lib/emitters/operations.js.map +1 -0
  62. package/lib/emitters/pagination.d.ts +69 -0
  63. package/lib/emitters/pagination.d.ts.map +1 -0
  64. package/lib/emitters/pagination.js +223 -0
  65. package/lib/emitters/pagination.js.map +1 -0
  66. package/lib/emitters/runtime-sources.d.ts +16 -0
  67. package/lib/emitters/runtime-sources.d.ts.map +1 -0
  68. package/lib/emitters/runtime-sources.js +16 -0
  69. package/lib/emitters/runtime-sources.js.map +1 -0
  70. package/lib/emitters/sample.d.ts +27 -0
  71. package/lib/emitters/sample.d.ts.map +1 -0
  72. package/lib/emitters/sample.js +161 -0
  73. package/lib/emitters/sample.js.map +1 -0
  74. package/lib/emitters/setup-bake.d.ts +15 -0
  75. package/lib/emitters/setup-bake.d.ts.map +1 -0
  76. package/lib/emitters/setup-bake.js +65 -0
  77. package/lib/emitters/setup-bake.js.map +1 -0
  78. package/lib/emitters/sse.d.ts +10 -0
  79. package/lib/emitters/sse.d.ts.map +1 -0
  80. package/lib/emitters/sse.js +46 -0
  81. package/lib/emitters/sse.js.map +1 -0
  82. package/lib/emitters/support.d.ts +13 -0
  83. package/lib/emitters/support.d.ts.map +1 -0
  84. package/lib/emitters/support.js +21 -0
  85. package/lib/emitters/support.js.map +1 -0
  86. package/lib/emitters/swr.d.ts +10 -0
  87. package/lib/emitters/swr.d.ts.map +1 -0
  88. package/lib/emitters/swr.js +88 -0
  89. package/lib/emitters/swr.js.map +1 -0
  90. package/lib/emitters/tanstack-query.d.ts +11 -0
  91. package/lib/emitters/tanstack-query.d.ts.map +1 -0
  92. package/lib/emitters/tanstack-query.js +68 -0
  93. package/lib/emitters/tanstack-query.js.map +1 -0
  94. package/lib/emitters/transformers.d.ts +11 -0
  95. package/lib/emitters/transformers.d.ts.map +1 -0
  96. package/lib/emitters/transformers.js +334 -0
  97. package/lib/emitters/transformers.js.map +1 -0
  98. package/lib/emitters/ts.d.ts +40 -0
  99. package/lib/emitters/ts.d.ts.map +1 -0
  100. package/lib/emitters/ts.js +104 -0
  101. package/lib/emitters/ts.js.map +1 -0
  102. package/lib/emitters/type-guards.d.ts +21 -0
  103. package/lib/emitters/type-guards.d.ts.map +1 -0
  104. package/lib/emitters/type-guards.js +183 -0
  105. package/lib/emitters/type-guards.js.map +1 -0
  106. package/lib/emitters/types.d.ts +23 -0
  107. package/lib/emitters/types.d.ts.map +1 -0
  108. package/lib/emitters/types.js +127 -0
  109. package/lib/emitters/types.js.map +1 -0
  110. package/lib/emitters/wrapper-support.d.ts +40 -0
  111. package/lib/emitters/wrapper-support.d.ts.map +1 -0
  112. package/lib/emitters/wrapper-support.js +112 -0
  113. package/lib/emitters/wrapper-support.js.map +1 -0
  114. package/lib/emitters/zod.d.ts +13 -0
  115. package/lib/emitters/zod.d.ts.map +1 -0
  116. package/lib/emitters/zod.js +344 -0
  117. package/lib/emitters/zod.js.map +1 -0
  118. package/lib/errors.d.ts +4 -0
  119. package/lib/errors.d.ts.map +1 -0
  120. package/lib/errors.js +7 -0
  121. package/lib/errors.js.map +1 -0
  122. package/lib/generators/anchor.d.ts +9 -0
  123. package/lib/generators/anchor.d.ts.map +1 -0
  124. package/lib/generators/anchor.js +10 -0
  125. package/lib/generators/anchor.js.map +1 -0
  126. package/lib/generators/index.d.ts +15 -0
  127. package/lib/generators/index.d.ts.map +1 -0
  128. package/lib/generators/index.js +62 -0
  129. package/lib/generators/index.js.map +1 -0
  130. package/lib/generators/mock.d.ts +9 -0
  131. package/lib/generators/mock.d.ts.map +1 -0
  132. package/lib/generators/mock.js +23 -0
  133. package/lib/generators/mock.js.map +1 -0
  134. package/lib/generators/resolve.d.ts +19 -0
  135. package/lib/generators/resolve.d.ts.map +1 -0
  136. package/lib/generators/resolve.js +68 -0
  137. package/lib/generators/resolve.js.map +1 -0
  138. package/lib/generators/sdk.d.ts +12 -0
  139. package/lib/generators/sdk.d.ts.map +1 -0
  140. package/lib/generators/sdk.js +26 -0
  141. package/lib/generators/sdk.js.map +1 -0
  142. package/lib/generators/swr.d.ts +15 -0
  143. package/lib/generators/swr.d.ts.map +1 -0
  144. package/lib/generators/swr.js +27 -0
  145. package/lib/generators/swr.js.map +1 -0
  146. package/lib/generators/tanstack-query.d.ts +17 -0
  147. package/lib/generators/tanstack-query.d.ts.map +1 -0
  148. package/lib/generators/tanstack-query.js +30 -0
  149. package/lib/generators/tanstack-query.js.map +1 -0
  150. package/lib/generators/transformers.d.ts +19 -0
  151. package/lib/generators/transformers.d.ts.map +1 -0
  152. package/lib/generators/transformers.js +28 -0
  153. package/lib/generators/transformers.js.map +1 -0
  154. package/lib/generators/types.d.ts +66 -0
  155. package/lib/generators/types.d.ts.map +1 -0
  156. package/lib/generators/types.js +2 -0
  157. package/lib/generators/types.js.map +1 -0
  158. package/lib/generators/zod.d.ts +14 -0
  159. package/lib/generators/zod.d.ts.map +1 -0
  160. package/lib/generators/zod.js +23 -0
  161. package/lib/generators/zod.js.map +1 -0
  162. package/lib/index.d.ts +29 -0
  163. package/lib/index.d.ts.map +1 -0
  164. package/lib/index.js +119 -0
  165. package/lib/index.js.map +1 -0
  166. package/lib/intermediate-representation/build.d.ts +4 -0
  167. package/lib/intermediate-representation/build.d.ts.map +1 -0
  168. package/lib/intermediate-representation/build.js +761 -0
  169. package/lib/intermediate-representation/build.js.map +1 -0
  170. package/lib/intermediate-representation/model.d.ts +275 -0
  171. package/lib/intermediate-representation/model.d.ts.map +1 -0
  172. package/lib/intermediate-representation/model.js +5 -0
  173. package/lib/intermediate-representation/model.js.map +1 -0
  174. package/lib/intermediate-representation/normalize-swagger2.d.ts +10 -0
  175. package/lib/intermediate-representation/normalize-swagger2.d.ts.map +1 -0
  176. package/lib/intermediate-representation/normalize-swagger2.js +210 -0
  177. package/lib/intermediate-representation/normalize-swagger2.js.map +1 -0
  178. package/lib/intermediate-representation/sanitize-identifiers.d.ts +30 -0
  179. package/lib/intermediate-representation/sanitize-identifiers.d.ts.map +1 -0
  180. package/lib/intermediate-representation/sanitize-identifiers.js +153 -0
  181. package/lib/intermediate-representation/sanitize-identifiers.js.map +1 -0
  182. package/lib/loader.d.ts +4 -0
  183. package/lib/loader.d.ts.map +1 -0
  184. package/lib/loader.js +14 -0
  185. package/lib/loader.js.map +1 -0
  186. package/lib/plugin.d.ts +19 -0
  187. package/lib/plugin.d.ts.map +1 -0
  188. package/lib/plugin.js +41 -0
  189. package/lib/plugin.js.map +1 -0
  190. package/lib/runtime/auth.d.ts +15 -0
  191. package/lib/runtime/auth.d.ts.map +1 -0
  192. package/lib/runtime/auth.js +67 -0
  193. package/lib/runtime/auth.js.map +1 -0
  194. package/lib/runtime/create-client.d.ts +35 -0
  195. package/lib/runtime/create-client.d.ts.map +1 -0
  196. package/lib/runtime/create-client.js +215 -0
  197. package/lib/runtime/create-client.js.map +1 -0
  198. package/lib/runtime/errors.d.ts +11 -0
  199. package/lib/runtime/errors.d.ts.map +1 -0
  200. package/lib/runtime/errors.js +25 -0
  201. package/lib/runtime/errors.js.map +1 -0
  202. package/lib/runtime/index.d.ts +14 -0
  203. package/lib/runtime/index.d.ts.map +1 -0
  204. package/lib/runtime/index.js +24 -0
  205. package/lib/runtime/index.js.map +1 -0
  206. package/lib/runtime/multipart.d.ts +9 -0
  207. package/lib/runtime/multipart.d.ts.map +1 -0
  208. package/lib/runtime/multipart.js +31 -0
  209. package/lib/runtime/multipart.js.map +1 -0
  210. package/lib/runtime/paginate.d.ts +36 -0
  211. package/lib/runtime/paginate.d.ts.map +1 -0
  212. package/lib/runtime/paginate.js +99 -0
  213. package/lib/runtime/paginate.js.map +1 -0
  214. package/lib/runtime/parse.d.ts +9 -0
  215. package/lib/runtime/parse.d.ts.map +1 -0
  216. package/lib/runtime/parse.js +37 -0
  217. package/lib/runtime/parse.js.map +1 -0
  218. package/lib/runtime/retry.d.ts +15 -0
  219. package/lib/runtime/retry.d.ts.map +1 -0
  220. package/lib/runtime/retry.js +51 -0
  221. package/lib/runtime/retry.js.map +1 -0
  222. package/lib/runtime/send.d.ts +31 -0
  223. package/lib/runtime/send.d.ts.map +1 -0
  224. package/lib/runtime/send.js +138 -0
  225. package/lib/runtime/send.js.map +1 -0
  226. package/lib/runtime/setup.d.ts +11 -0
  227. package/lib/runtime/setup.d.ts.map +1 -0
  228. package/lib/runtime/setup.js +13 -0
  229. package/lib/runtime/setup.js.map +1 -0
  230. package/lib/runtime/sse.d.ts +21 -0
  231. package/lib/runtime/sse.d.ts.map +1 -0
  232. package/lib/runtime/sse.js +163 -0
  233. package/lib/runtime/sse.js.map +1 -0
  234. package/lib/runtime/types.d.ts +232 -0
  235. package/lib/runtime/types.d.ts.map +1 -0
  236. package/lib/runtime/types.js +9 -0
  237. package/lib/runtime/types.js.map +1 -0
  238. package/lib/runtime/url.d.ts +25 -0
  239. package/lib/runtime/url.d.ts.map +1 -0
  240. package/lib/runtime/url.js +97 -0
  241. package/lib/runtime/url.js.map +1 -0
  242. package/lib/runtime-contract.d.ts +27 -0
  243. package/lib/runtime-contract.d.ts.map +1 -0
  244. package/lib/runtime-contract.js +14 -0
  245. package/lib/runtime-contract.js.map +1 -0
  246. package/lib/types.d.ts +118 -0
  247. package/lib/types.d.ts.map +1 -0
  248. package/lib/types.js +2 -0
  249. package/lib/types.js.map +1 -0
  250. package/package.json +59 -0
@@ -0,0 +1,16 @@
1
+ export declare const RUNTIME_SOURCES: {
2
+ readonly 'types.ts': "/**\n * The public type surface of the client runtime — `@redocly/client-generator`'s\n * app-facing runtime module. Pure types, no runtime code (excluded from coverage).\n * The generator emits `OPERATIONS` literals typed\n * `satisfies Record<string, OperationDescriptor>` against this module, so an\n * incompatible runtime/generated pair fails the consumer's build (the semver skew guard).\n */\n\n/** How one operation parameter is sent: its location plus OpenAPI query-serialization hints. */\nexport type ParamSpec = {\n name: string;\n in: 'path' | 'query' | 'header';\n style?: 'form' | 'spaceDelimited' | 'pipeDelimited' | 'deepObject';\n explode?: boolean;\n allowReserved?: boolean;\n};\n\n/** One security scheme, denormalized onto the operation (`scheme` names the spec's scheme). */\nexport type SecuritySpec =\n | { scheme: string; kind: 'bearer' | 'basic' }\n | { scheme: string; kind: 'apiKey'; name: string; in: 'header' | 'query' | 'cookie' };\n\n/**\n * How to auto-iterate a paginated operation (drives its `.pages()`/`.items()` members).\n * `nextCursor` and `items` are RFC 6901 JSON pointers into the page (response) value.\n */\nexport type PaginationSpec = {\n style: 'cursor' | 'offset' | 'page';\n /** The query param the iterator advances: the cursor (`cursor`) or number (`offset`/`page`). */\n param: string;\n /** Optional page-size query param (recorded for tooling; never set by the runtime). */\n limitParam?: string;\n /** Cursor style only: pointer to the next cursor in the page. */\n nextCursor?: string;\n /** Pointer to the page's item array. */\n items: string;\n};\n\n/** The frozen data contract between generated code and the runtime: one operation's wire shape. */\nexport type OperationDescriptor = {\n id: string;\n method: string;\n path: string;\n tags?: readonly string[];\n params?: readonly ParamSpec[];\n /** `multipart: true` marks a typed object body serialized to FormData by the runtime. */\n body?: { contentType: string; multipart?: boolean };\n /** Defaults to `'json'` (content-type negotiation on parse). */\n responseKind?: 'json' | 'text' | 'blob' | 'void' | 'sse';\n sseDataKind?: 'json' | 'text';\n /** OR-alternatives, each an AND-set: the runtime applies the first fully-configured one. */\n security?: readonly (readonly SecuritySpec[])[];\n pagination?: PaginationSpec;\n};\n\n/** A query value: scalars, arrays of scalars, or objects (serialized as deepObject brackets). */\nexport type QueryValue =\n | string\n | number\n | boolean\n | null\n | undefined\n | Array<string | number | boolean | null | undefined>\n | Record<string, unknown>;\n\n/** A credential: a literal, or a (possibly async) function resolved per request (refresh flows). */\nexport type TokenProvider = string | (() => string | Promise<string>);\n\n/** Per-instance credentials, keyed by the scheme kinds the runtime can inject. */\nexport type AuthCredentials = {\n bearer?: TokenProvider;\n basic?: { username: string; password: string };\n apiKey?: Record<string, TokenProvider>;\n};\n\n/** Backoff shape: 'fixed' = constant delay; 'exponential' = doubling per attempt. */\nexport type RetryStrategy = 'fixed' | 'exponential';\n\n/**\n * The operation's identity, exposed to middleware for targeting (`ctx.operation`).\n * Generated clients instantiate the type parameters with the spec's literal unions\n * (`OperationId`/`OperationPath`/`OperationTag`) so a misspelled operation id in a\n * middleware comparison fails to compile; the string defaults keep every\n * spec-independent consumer (`runtime-contract.ts`, the runtime internals) working\n * with the base shape. `tags` stays mutable (`Tag[]`) so setup-contract types\n * (byte-locked to generated output) remain assignable through middleware callbacks.\n */\nexport type OperationContext<\n Id extends string = string,\n Path extends string = string,\n Tag extends string = string,\n> = { id: Id; path: Path; tags: Tag[] };\n\n/** The mutable request context threaded through the middleware chain. */\nexport type RequestContext<Op extends OperationContext = OperationContext> = {\n url: string;\n method: string;\n headers: Record<string, string>;\n body?: unknown;\n operation: Op;\n};\n\n/** The failed attempt handed to a custom `retryOn`: exactly one of `response`/`error` is set. */\nexport type RetryContext<Op extends OperationContext = OperationContext> = {\n attempt: number;\n request: RequestContext<Op>;\n response?: Response;\n error?: unknown;\n};\n\n/** Opt-in retry policy; a per-call override merges field-by-field over the config policy. */\nexport type RetryConfig<Op extends OperationContext = OperationContext> = {\n retries?: number;\n retryDelay?: number;\n retryStrategy?: RetryStrategy;\n jitter?: boolean;\n retryOn?: (ctx: RetryContext<Op>) => boolean | Promise<boolean>;\n};\n\n/**\n * Structural stand-in for the runtime's ApiError so this module stays import-free\n * (pure types); the real `ApiError` class is assignable to it.\n */\nexport type ApiErrorLike = globalThis.Error & {\n url: string;\n status: number;\n statusText: string;\n body: unknown;\n};\n\n/** One interceptor: any subset of the three hooks. */\nexport type Middleware<Op extends OperationContext = OperationContext> = {\n onRequest?: (ctx: RequestContext<Op>) => void | Promise<void>;\n onResponse?: (\n response: Response,\n ctx: RequestContext<Op>\n ) => Response | void | Promise<Response | void>;\n /** Throw mode only: may map/replace the error. */\n // `globalThis.Error` so a spec schema named `Error` cannot shadow it in inline mode.\n onError?: (\n error: ApiErrorLike,\n ctx: RequestContext<Op>\n ) => globalThis.Error | Promise<globalThis.Error>;\n};\n\n/** Client configuration: transport, defaults, retry policy, middleware, and credentials. */\nexport type ClientConfig<Op extends OperationContext = OperationContext> = {\n serverUrl?: string;\n fetch?: typeof fetch;\n headers?:\n | Record<string, string>\n | (() => Record<string, string> | Promise<Record<string, string>>);\n retry?: RetryConfig<Op>;\n middleware?: Middleware<Op>[];\n auth?: AuthCredentials;\n /** Fixed at generate time by the generator (`'throw'` when omitted); `configure()` ignores it. */\n errorMode?: 'throw' | 'result';\n onRequest?: Middleware<Op>['onRequest'];\n onResponse?: Middleware<Op>['onResponse'];\n onError?: Middleware<Op>['onError'];\n};\n\n/** Response readers for the per-call `parseAs` override. */\nexport type ParseAs = 'auto' | 'json' | 'text' | 'blob' | 'arrayBuffer' | 'formData' | 'stream';\n\n/** Per-call options: standard `RequestInit` plus a retry override and a forced reader. */\nexport type RequestOptions = RequestInit & { retry?: RetryConfig; parseAs?: ParseAs };\n\n/** Per-call options for an SSE stream; reconnect defaults to true. */\nexport type SseOptions = RequestInit & { reconnect?: boolean; reconnectDelay?: number };\n\n/** A single decoded Server-Sent Event with its payload typed from the spec. */\nexport type ServerSentEvent<T> = { event?: string; data: T; id?: string; retry?: number };\n\n/** Result-mode return shape: exactly one of `data`/`error` is set. */\nexport type Result<TData, TError> =\n | { data: TData; error: undefined; response: Response }\n | { data: undefined; error: TError; response: Response };\n\n/**\n * The generated `Ops` type's shape: per-operation args/result, plus `kind: 'sse'` for\n * streams and, for paginated operations, `item` (the page's element type) and — on\n * result-mode clients only — `page` (the RAW page type `.pages()` yields, since\n * iteration unwraps the `Result` envelope the one-shot `result` carries).\n */\nexport type OpsShape = Record<\n string,\n { args: object; result: unknown; kind?: 'sse'; item?: unknown; page?: unknown }\n>;\n\n/** The always-present client members (assigned after the operation loop — they win collisions). */\nexport type ClientCore<Op extends OperationContext = OperationContext> = {\n /** Merge into the config; note `middleware` REPLACES the chain (use `use()` to compose). */\n configure(config: ClientConfig<Op>): void;\n /** Append interceptors (composes with baked/publisher middleware). */\n use(...middleware: Middleware<Op>[]): void;\n auth: {\n bearer(token: TokenProvider): void;\n basic(username: string, password: string): void;\n apiKey(scheme: string, value: TokenProvider): void;\n };\n};\n\n/**\n * The standard TypeScript optionality probe: `{}` has no required members, so\n * `{} extends A` is true exactly when every member of `A` is optional.\n */\n// oxlint-disable-next-line typescript/no-empty-object-type\ntype NoRequiredKeys<A> = {} extends A ? true : false;\n\n/**\n * The page type `.pages()` yields: the RAW page declared by `page` (the generator\n * writes it only on result-mode paginated entries, whose `result` is the envelope),\n * or the method's own `result` (throw mode — already the raw page).\n */\ntype PageOf<Entry extends OpsShape[string]> = Entry extends { page: unknown }\n ? Entry['page']\n : Entry['result'];\n\n/**\n * The auto-pagination members intersected onto a paginated method — present exactly when\n * the Ops entry declares `item` (the generator writes it only for paginated operations).\n * Args optionality mirrors the method's own; `unknown` otherwise (identity under `&`).\n * Iteration is error-mode-agnostic: `.pages()`/`.items()` yield raw pages/items, and a\n * failed page aborts iteration by throwing `ApiError`, even on result-mode clients; the\n * `onError` middleware hook (throw-mode-only) is not invoked.\n */\ntype Paginated<Entry extends OpsShape[string]> = 'item' extends keyof Entry\n ? NoRequiredKeys<Entry['args']> extends true\n ? {\n pages(args?: Entry['args'], init?: RequestOptions): AsyncGenerator<PageOf<Entry>>;\n items(args?: Entry['args'], init?: RequestOptions): AsyncGenerator<Entry['item']>;\n }\n : {\n pages(args: Entry['args'], init?: RequestOptions): AsyncGenerator<PageOf<Entry>>;\n items(args: Entry['args'], init?: RequestOptions): AsyncGenerator<Entry['item']>;\n }\n : unknown;\n\n/** The typed instance client: one bound method per operation plus the core members. */\nexport type Client<Ops extends OpsShape, Op extends OperationContext = OperationContext> = {\n [K in keyof Ops]: Ops[K] extends { kind: 'sse' }\n ? NoRequiredKeys<Ops[K]['args']> extends true\n ? (\n args?: Ops[K]['args'],\n init?: SseOptions\n ) => AsyncGenerator<ServerSentEvent<Ops[K]['result']>>\n : (\n args: Ops[K]['args'],\n init?: SseOptions\n ) => AsyncGenerator<ServerSentEvent<Ops[K]['result']>>\n : (NoRequiredKeys<Ops[K]['args']> extends true\n ? (args?: Ops[K]['args'], init?: RequestOptions) => Promise<Ops[K]['result']>\n : (args: Ops[K]['args'], init?: RequestOptions) => Promise<Ops[K]['result']>) &\n Paginated<Ops[K]>;\n} & ClientCore<Op>;\n";
3
+ readonly 'errors.ts': "/** The error thrown (throw mode) for a non-2xx response, carrying the decoded error body. */\nexport class ApiError extends Error {\n public readonly url: string;\n public readonly status: number;\n public readonly statusText: string;\n public readonly body: unknown;\n constructor(url: string, status: number, statusText: string, body: unknown) {\n super(`Request failed with status ${status}`);\n this.name = 'ApiError';\n this.url = url;\n this.status = status;\n this.statusText = statusText;\n this.body = body;\n }\n}\n\n/** The error to throw for an aborted request: the caller's abort reason when it is an Error. */\n// `globalThis.Error` (not bare `Error`) so a spec schema named `Error` cannot shadow it\n// when this module is embedded alongside generated types (inline mode).\nexport function abortError(signal: AbortSignal): globalThis.Error {\n const reason = (signal as { reason?: unknown }).reason;\n if (reason instanceof Error) return reason;\n return new DOMException('The operation was aborted.', 'AbortError');\n}\n";
4
+ readonly 'url.ts': "import type { ParamSpec, QueryValue } from './types.js';\n\n/**\n * The RESOLVED OpenAPI serialization spec for one query parameter — callers apply the\n * OpenAPI defaults (`style: 'form'`, `explode: true`) before building one.\n */\nexport type QueryStyle = {\n style: NonNullable<ParamSpec['style']>;\n explode: boolean;\n allowReserved?: boolean;\n};\n\n/**\n * Encode everything except the RFC-3986 reserved set, for `allowReserved: true` params —\n * `filter=a/b` survives instead of `filter=a%2Fb`.\n */\nexport function encodeReserved(value: string): string {\n return encodeURIComponent(value).replace(\n /%(3A|2F|3F|23|5B|5D|40|21|24|26|27|28|29|2A|2B|2C|3B|3D)/g,\n (match) => decodeURIComponent(match)\n );\n}\n\n/** Substitute `{name}` template segments with encoded values; a missing value is a caller bug. */\nexport function substitutePath(template: string, values: Record<string, unknown>): string {\n return template.replace(/\\{([^{}]+)\\}/g, (_match, name: string) => {\n const value = values[name];\n if (value === undefined) throw new Error(`Missing path parameter \"${name}\"`);\n return encodeURIComponent(String(value));\n });\n}\n\n/**\n * Build the request URL: `serverUrl` (trailing slash trimmed) + path + serialized query.\n * Query parameters honor their OpenAPI `style`/`explode`/`allowReserved` (from `styles`);\n * without a spec, arrays repeat the key (`form`+`explode`), objects serialize as\n * `deepObject` brackets, and `null`/`undefined` entries are skipped.\n */\nexport function buildUrl(\n serverUrl: string,\n path: string,\n query?: Record<string, QueryValue>,\n styles?: Record<string, QueryStyle>\n): string {\n // Trim trailing slashes with a scan, not `/\\/+$/` — an anchored `+` regex is\n // quadratic on adversarial many-slash input (the server URL is caller data).\n let end = serverUrl.length;\n while (end > 0 && serverUrl.charCodeAt(end - 1) === 47 /* '/' */) end--;\n const url = serverUrl.slice(0, end) + path;\n if (!query) return url;\n const params = new URLSearchParams();\n const raw: string[] = [];\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null) continue;\n const spec = styles?.[key];\n if (!spec) {\n if (Array.isArray(value)) {\n for (const v of value) {\n if (v !== undefined && v !== null) params.append(key, String(v));\n }\n } else if (Object(value) === value) {\n // Object-valued query params use `deepObject` style: key[subKey]=subValue.\n for (const [subKey, subValue] of Object.entries(value)) {\n if (subValue !== undefined && subValue !== null) {\n params.append(`${key}[${subKey}]`, String(subValue));\n }\n }\n } else {\n params.append(key, String(value));\n }\n continue;\n }\n if (Array.isArray(value)) {\n const items = value.filter((v) => v !== undefined && v !== null).map(String);\n if (spec.style === 'form' && spec.explode) {\n for (const v of items) {\n if (spec.allowReserved) raw.push(`${key}=${encodeReserved(v)}`);\n else params.append(key, v);\n }\n } else {\n // Delimited styles put the LITERAL delimiter on the wire; only the\n // values are encoded. `%20` (not `+`) is the literal space delimiter.\n const delim =\n spec.style === 'pipeDelimited' ? '|' : spec.style === 'spaceDelimited' ? '%20' : ',';\n const enc = spec.allowReserved ? encodeReserved : encodeURIComponent;\n raw.push(`${encodeURIComponent(key)}=${items.map(enc).join(delim)}`);\n }\n } else if (Object(value) === value) {\n // `deepObject` (and any object spec, for now): key[subKey]=subValue.\n for (const [subKey, subValue] of Object.entries(value)) {\n if (subValue !== undefined && subValue !== null) {\n if (spec.allowReserved) raw.push(`${key}[${subKey}]=${encodeReserved(String(subValue))}`);\n else params.append(`${key}[${subKey}]`, String(subValue));\n }\n }\n } else if (spec.allowReserved) {\n raw.push(`${key}=${encodeReserved(String(value))}`);\n } else {\n params.append(key, String(value));\n }\n }\n const qs = [params.toString(), ...raw].filter(Boolean).join('&');\n return qs ? `${url}?${qs}` : url;\n}\n";
5
+ readonly 'parse.ts': "import type { ParseAs } from './types.js';\n\n/**\n * Read the response body per `kind`. `'auto'` negotiates from the content type\n * (JSON, then `text/*`, then Blob); `'void'` and `204` responses read nothing.\n */\nexport async function parse(response: Response, kind: ParseAs | 'void'): Promise<unknown> {\n if (kind === 'void' || response.status === 204) return undefined;\n if (kind === 'stream') return response.body;\n if (kind === 'blob') return response.blob();\n if (kind === 'arrayBuffer') return response.arrayBuffer();\n if (kind === 'formData') return response.formData();\n if (kind === 'text') return response.text();\n if (kind === 'json') return response.json();\n // 'auto' — negotiate from the response's content type (case-insensitively:\n // `Text/Plain` and `application/JSON` are valid per RFC 9110).\n const contentType = (response.headers.get('content-type') ?? '').toLowerCase();\n if (contentType.includes('json')) return response.json();\n if (contentType.startsWith('text/')) return response.text();\n return response.blob();\n}\n\n/** Best-effort decode of a non-2xx body (JSON when declared, else text; undefined on failure). */\nexport async function readError(response: Response): Promise<unknown> {\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.toLowerCase().includes('json')) {\n return response.json().catch(() => undefined);\n }\n return response.text().catch(() => undefined);\n}\n";
6
+ readonly 'retry.ts': "import { abortError } from './errors.js';\nimport type { RetryConfig, RetryContext } from './types.js';\n\nconst IDEMPOTENT_METHODS = new Set(['GET', 'HEAD', 'PUT', 'DELETE', 'OPTIONS']);\nconst TRANSIENT_STATUS = new Set([408, 429, 500, 502, 503, 504]);\n\n/**\n * The default retry predicate: idempotent methods only, on a transport error or a\n * transient status. A custom `retryOn` fully replaces this (no method check kept).\n */\nexport function defaultRetryOn(ctx: RetryContext): boolean {\n if (!IDEMPOTENT_METHODS.has(ctx.request.method.toUpperCase())) return false;\n return ctx.response === undefined || TRANSIENT_STATUS.has(ctx.response.status);\n}\n\n/**\n * The delay before the next attempt: a `Retry-After` header (seconds or HTTP-date)\n * wins; otherwise fixed/exponential backoff over `retryDelay`, with full jitter\n * unless `jitter === false`.\n */\nexport function retryDelay(retry: RetryConfig, attempt: number, retryAfter: string | null): number {\n if (retryAfter) {\n const seconds = Number(retryAfter);\n if (!Number.isNaN(seconds)) return seconds * 1000;\n const when = Date.parse(retryAfter);\n if (!Number.isNaN(when)) return Math.max(0, when - Date.now());\n }\n const base = retry.retryDelay ?? 1000;\n const raw = retry.retryStrategy === 'fixed' ? base : base * Math.pow(2, attempt - 1);\n return retry.jitter === false ? raw : Math.random() * raw;\n}\n\n/** Abort-aware sleep: resolves after `ms`, rejects with the abort reason immediately on abort. */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(abortError(signal));\n return;\n }\n const onAbort = () => {\n clearTimeout(timer);\n reject(abortError(signal as AbortSignal));\n };\n const timer = setTimeout(() => {\n if (signal) signal.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n if (signal) signal.addEventListener('abort', onAbort, { once: true });\n });\n}\n";
7
+ readonly 'multipart.ts': "/**\n * Serialize a plain object into `FormData` for a typed `multipart/form-data` body\n * (capability module — wired into `createClient`, never imported by the send core).\n * `Blob`/`File` and strings pass through; `Date`s become ISO strings; arrays append\n * one field per item; other objects are JSON-encoded; everything else is stringified.\n * `undefined`/`null` entries are skipped.\n */\nexport function toFormData(body: Record<string, unknown>): FormData {\n const fd = new FormData();\n const append = (key: string, value: unknown): void => {\n if (value === undefined || value === null) return;\n if (value instanceof Blob || typeof value === 'string') fd.append(key, value);\n else if (value instanceof Date) fd.append(key, value.toISOString());\n else if (Object(value) === value) fd.append(key, JSON.stringify(value));\n else fd.append(key, String(value));\n };\n for (const [key, value] of Object.entries(body)) {\n if (Array.isArray(value)) for (const item of value) append(key, item);\n else append(key, value);\n }\n return fd;\n}\n";
8
+ readonly 'auth.ts': "import type { ClientConfig, SecuritySpec, TokenProvider } from './types.js';\n\n/** Resolve a credential: a literal passes through; a function is awaited per request. */\nasync function resolveToken(provider: TokenProvider): Promise<string> {\n return typeof provider === 'function' ? await provider() : provider;\n}\n\n/** UTF-8-safe base64: bare `btoa` throws on non-Latin-1 credentials (RFC 7617 allows UTF-8). */\nfunction encodeBase64(text: string): string {\n let binary = '';\n for (const byte of new TextEncoder().encode(text)) binary += String.fromCharCode(byte);\n return btoa(binary);\n}\n\n/** Whether a credential for this scheme is configured on the instance. */\nfunction isConfigured(scheme: SecuritySpec, config: ClientConfig): boolean {\n if (scheme.kind === 'apiKey') return config.auth?.apiKey?.[scheme.scheme] !== undefined;\n if (scheme.kind === 'bearer') return config.auth?.bearer !== undefined;\n return config.auth?.basic !== undefined;\n}\n\n/**\n * Build the auth headers/query for one operation's `security` OR-alternatives from the\n * instance credentials (`config.auth`) — capability module, wired into `createClient`.\n * The first alternative whose schemes (an AND-set) are all configured is applied, so\n * \"bearer OR apiKey\" works with either credential and never sends both. When none is\n * fully configured, the first alternative's configured schemes are still sent (the\n * server rejects the request, mirroring the previous behavior).\n * Cookie-borne apiKeys fold into a single `Cookie` header joined with `; `.\n */\nexport async function resolveAuth(\n security: readonly (readonly SecuritySpec[])[],\n config: ClientConfig\n): Promise<{ headers: Record<string, string>; query: Record<string, string> }> {\n const alternative =\n security.find((schemes) => schemes.every((scheme) => isConfigured(scheme, config))) ??\n security[0] ??\n [];\n const headers: Record<string, string> = {};\n const query: Record<string, string> = {};\n const cookies: string[] = [];\n for (const scheme of alternative) {\n if (scheme.kind === 'apiKey') {\n const provider = config.auth?.apiKey?.[scheme.scheme];\n if (provider === undefined) continue;\n const value = await resolveToken(provider);\n if (scheme.in === 'header') headers[scheme.name] = value;\n else if (scheme.in === 'query') query[scheme.name] = value;\n // Cookie values may contain reserved characters (`;`, `=`, space, …); percent-encode\n // so the credential can't break the `Cookie` header syntax.\n else cookies.push(`${scheme.name}=${encodeURIComponent(value)}`);\n } else if (scheme.kind === 'bearer') {\n const provider = config.auth?.bearer;\n if (provider !== undefined) headers.Authorization = `Bearer ${await resolveToken(provider)}`;\n } else {\n const basic = config.auth?.basic;\n if (basic !== undefined) {\n headers.Authorization = `Basic ${encodeBase64(`${basic.username}:${basic.password}`)}`;\n }\n }\n }\n if (cookies.length > 0) headers.Cookie = cookies.join('; ');\n return { headers, query };\n}\n";
9
+ readonly 'setup.ts': "import type { ClientConfig, Middleware } from './types.js';\n\n/**\n * Merge a publisher's baked setup (`defineClientSetup({...})`) with the app's config:\n * app config fields win per-field over baked defaults, while middleware composes —\n * baked middleware runs first, then the app's.\n */\nexport function mergeSetup(\n setup: { config?: ClientConfig; middleware?: Middleware[] } | undefined,\n config: ClientConfig = {}\n): ClientConfig {\n return {\n ...setup?.config,\n ...config,\n middleware: [...(setup?.middleware ?? []), ...(config.middleware ?? [])],\n };\n}\n";
10
+ readonly 'send.ts': "import { abortError } from './errors.js';\nimport { defaultRetryOn, retryDelay, sleep } from './retry.js';\nimport type {\n ClientConfig,\n Middleware,\n OperationContext,\n RequestContext,\n RequestOptions,\n RetryConfig,\n} from './types.js';\n\n/**\n * Optional behaviors the send core can use but never statically imports — wired by\n * `createClient` (the same seam the future inline-mode assembler relies on).\n */\nexport type SendCapabilities = {\n /** Serialize a typed multipart body (a plain object) to FormData. */\n serializeMultipart?: (body: Record<string, unknown>) => FormData;\n};\n\n/**\n * Normalize a caller's `HeadersInit` (plain record, `Headers` instance, or entry pairs)\n * to a plain record — spreading a `Headers` or an array contributes no entries.\n */\nexport function toHeaderRecord(headers: HeadersInit | undefined): Record<string, string> {\n if (headers === undefined) return {};\n if (headers instanceof Headers) {\n const record: Record<string, string> = {};\n headers.forEach((value, key) => {\n record[key] = value;\n });\n return record;\n }\n if (Array.isArray(headers)) return Object.fromEntries(headers);\n return headers;\n}\n\n/**\n * The effective middleware chain for a request: the single `onRequest`/`onResponse`/\n * `onError` config hooks as one implicit first middleware, then `config.middleware`.\n */\nexport function middlewareChain(config: ClientConfig): Middleware[] {\n const single =\n config.onRequest || config.onResponse || config.onError\n ? [{ onRequest: config.onRequest, onResponse: config.onResponse, onError: config.onError }]\n : [];\n return [...single, ...(config.middleware ?? [])];\n}\n\n/**\n * The fetch core shared by every operation: default + config + per-call headers, the\n * `onRequest` chain (BEFORE body serialization, so mutations are sent), body\n * serialization (JSON, or FormData via the multipart capability), the retry loop\n * (idempotent-only defaults, `Retry-After`, abandoned-body drain), and the reverse\n * `onResponse` onion. Returns the final response plus the request context.\n */\nexport async function send(\n config: ClientConfig,\n op: OperationContext,\n url: string,\n init: RequestOptions,\n body: unknown | undefined,\n multipart: boolean,\n caps: SendCapabilities\n): Promise<{ response: Response; context: RequestContext }> {\n const { retry: callRetry, ...fetchInit } = init;\n const retry: RetryConfig = { ...config.retry, ...callRetry };\n const extra = typeof config.headers === 'function' ? await config.headers() : config.headers;\n const headers: Record<string, string> = {\n Accept: 'application/json',\n ...extra,\n ...toHeaderRecord(fetchInit.headers),\n };\n const context: RequestContext = {\n url,\n method: fetchInit.method ?? 'GET',\n headers,\n body,\n operation: op,\n };\n const middleware = middlewareChain(config);\n for (const mw of middleware) if (mw.onRequest) await mw.onRequest(context);\n // Serialize AFTER onRequest so body mutations (case conversion, enveloping, signing) take effect.\n let payload: BodyInit | undefined;\n if (context.body !== undefined) {\n const value = context.body;\n const isBinary =\n value instanceof Blob ||\n value instanceof ArrayBuffer ||\n ArrayBuffer.isView(value as ArrayBufferView);\n const isFormData = typeof FormData !== 'undefined' && value instanceof FormData;\n const isURLSearchParams = value instanceof URLSearchParams;\n if (isFormData || isURLSearchParams || isBinary || typeof value === 'string') {\n payload = value as BodyInit;\n } else if (multipart) {\n if (!caps.serializeMultipart) {\n throw new Error('Multipart capability not wired: cannot serialize the request body');\n }\n payload = caps.serializeMultipart(value as Record<string, unknown>);\n } else {\n payload = JSON.stringify(value);\n if (!('Content-Type' in context.headers) && !('content-type' in context.headers)) {\n context.headers['Content-Type'] = 'application/json';\n }\n }\n }\n const doFetch = config.fetch ?? fetch;\n const maxAttempts = 1 + (retry.retries ?? 0);\n const retryOn = retry.retryOn ?? defaultRetryOn;\n const signal = fetchInit.signal ?? undefined;\n\n let attempt = 0;\n while (true) {\n attempt++;\n if (signal?.aborted) throw abortError(signal);\n let response: Response;\n try {\n response = await doFetch(context.url, {\n ...fetchInit,\n method: context.method,\n headers: context.headers,\n body: payload,\n });\n } catch (error) {\n if (\n attempt < maxAttempts &&\n !signal?.aborted &&\n (await retryOn({ attempt, request: context, error }))\n ) {\n await sleep(retryDelay(retry, attempt, null), signal);\n continue;\n }\n throw error;\n }\n // Reverse order: the last-registered middleware wraps closest to the network (onion).\n for (let i = middleware.length - 1; i >= 0; i--) {\n const onResponse = middleware[i].onResponse;\n if (onResponse) {\n const replaced = await onResponse(response, context);\n if (replaced && replaced !== response) {\n // Cancel the abandoned original's body — like the retry path, an unread body\n // keeps its connection checked out under Node/undici.\n await response.body?.cancel().catch(() => undefined);\n response = replaced;\n }\n }\n }\n if (\n !response.ok &&\n attempt < maxAttempts &&\n !signal?.aborted &&\n (await retryOn({ attempt, request: context, response }))\n ) {\n const retryAfter = response.headers.get('retry-after');\n // Drain the abandoned response body before the next attempt: an unread body\n // keeps the connection checked out (and can stall the pool) under Node/undici\n // and other strict HTTP clients. Ignore errors (e.g. a middleware already read it).\n await response.body?.cancel().catch(() => undefined);\n await sleep(retryDelay(retry, attempt, retryAfter), signal);\n continue;\n }\n return { response, context };\n }\n}\n";
11
+ readonly 'sse.ts': "import { ApiError } from './errors.js';\nimport { readError } from './parse.js';\nimport { sleep } from './retry.js';\nimport { send, toHeaderRecord } from './send.js';\nimport type { ClientConfig, OperationContext, ServerSentEvent, SseOptions } from './types.js';\n\n/**\n * A frame delimiter: two consecutive line terminators (each CR, LF, or CRLF, per the SSE\n * spec — so mixed endings like `\\n\\r\\n` are valid boundaries, not just matching pairs).\n */\nconst FRAME_DELIMITER = /(?:\\r\\n|\\r|\\n){2}/;\n\n/**\n * A terminally malformed event stream — unparseable JSON `data` or an unbounded frame.\n * A stable bad payload, not a dropped connection, so the stream never reconnects on it.\n */\nexport class SseParseError extends Error {}\n\n/**\n * Consume a `text/event-stream` operation as typed events (capability module — wired\n * into `createClient`). Auto-reconnects on dropped connections, resuming from the last\n * seen event id via `Last-Event-ID` (backoff: the server's `retry:` value, then\n * `reconnectDelay`, then 1s — exponential with jitter, capped at 30s). A clean stream\n * end flushes a trailing frame and finishes; `break`/abort end the iterator cleanly.\n */\nexport async function* sse<T>(\n config: ClientConfig,\n op: OperationContext,\n prepare: () => Promise<{ url: string; init: SseOptions }>,\n dataKind: 'json' | 'text' = 'text'\n): AsyncGenerator<ServerSentEvent<T>> {\n let lastEventId: string | undefined;\n let serverRetry: number | undefined;\n let failures = 0;\n while (true) {\n // Re-prepare each attempt so a refresh-style TokenProvider yields a fresh credential\n // on reconnect (the auth is baked into `url` query + `init.headers`). `reconnect`,\n // `reconnectDelay`, and `signal` come from the caller's original options unchanged.\n const { url, init } = await prepare();\n const { reconnect = true, reconnectDelay, ...rest } = init;\n const signal = rest.signal ?? undefined;\n if (signal?.aborted) return;\n const headers: Record<string, string> = {\n Accept: 'text/event-stream',\n ...toHeaderRecord(rest.headers),\n };\n const sendHeaders =\n lastEventId === undefined ? headers : { ...headers, 'Last-Event-ID': lastEventId };\n try {\n const { response } = await send(\n config,\n op,\n url,\n { ...rest, method: rest.method ?? 'GET', headers: sendHeaders },\n undefined,\n false,\n {}\n );\n if (!response.ok) {\n const errorBody = await readError(response);\n throw new ApiError(url, response.status, response.statusText, errorBody);\n }\n failures = 0;\n const body = response.body;\n if (!body) return;\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n try {\n while (true) {\n const { done, value } = await reader.read();\n buffer += done ? decoder.decode() : decoder.decode(value, { stream: true });\n let match: RegExpMatchArray | null;\n while ((match = buffer.match(FRAME_DELIMITER)) !== null) {\n const index = match.index!;\n const raw = buffer.slice(0, index);\n buffer = buffer.slice(index + match[0].length);\n const event = parseSseFrame(raw, dataKind);\n if (event) {\n if (event.id !== undefined) lastEventId = event.id;\n if (event.retry !== undefined) serverRetry = event.retry;\n yield event as ServerSentEvent<T>;\n }\n }\n if (done) {\n // Stream closed cleanly. Flush a final event that arrived without a trailing\n // delimiter, then finish — a clean end is not a dropped connection, so do not reconnect.\n const event = buffer.length > 0 ? parseSseFrame(buffer, dataKind) : undefined;\n if (event) {\n if (event.id !== undefined) lastEventId = event.id;\n if (event.retry !== undefined) serverRetry = event.retry;\n yield event as ServerSentEvent<T>;\n }\n return;\n }\n // Bound memory: a server that never sends a frame delimiter would otherwise\n // grow `buffer` without limit. 1 MiB is far above any real SSE frame.\n if (buffer.length > 1048576) {\n throw new SseParseError('SSE frame exceeded 1048576 characters without a delimiter');\n }\n }\n } finally {\n await reader.cancel().catch(() => undefined);\n }\n } catch (error) {\n if (signal?.aborted) return;\n // A non-OK HTTP response (4xx/5xx) or an unparseable JSON payload is a definitive\n // error, not a transient drop — surface it instead of reconnecting in a loop (a\n // stable bad payload would otherwise reconnect forever).\n if (error instanceof ApiError || error instanceof SseParseError) throw error;\n // A transport failure (connect/DNS/reset) when opening the request, or a mid-stream\n // read error, is a dropped connection: fall through to backoff/reconnect when enabled.\n if (!reconnect) throw error;\n }\n // Only the swallowed-drop path reaches here: reconnect is on and the signal not aborted.\n failures++;\n const base = serverRetry ?? reconnectDelay ?? 1000;\n const delay = Math.min(base * Math.pow(2, failures - 1), 30_000);\n try {\n await sleep(Math.random() * delay, signal);\n } catch {\n return; // sleep rejects only on abort — end the iterator cleanly\n }\n }\n}\n\n/** Parse one raw SSE frame (its lines) into an event; returns undefined for comment-only frames. */\nexport function parseSseFrame(\n raw: string,\n dataKind: 'json' | 'text'\n): ServerSentEvent<unknown> | undefined {\n let event: string | undefined;\n const dataLines: string[] = [];\n let id: string | undefined;\n let retry: number | undefined;\n let sawField = false;\n for (const line of raw.split(/\\r\\n|\\n|\\r/)) {\n if (line === '' || line.startsWith(':')) continue;\n const colon = line.indexOf(':');\n const field = colon === -1 ? line : line.slice(0, colon);\n let val = colon === -1 ? '' : line.slice(colon + 1);\n if (val.startsWith(' ')) val = val.slice(1);\n sawField = true;\n if (field === 'event') event = val;\n else if (field === 'data') dataLines.push(val);\n else if (field === 'id') id = val;\n else if (field === 'retry') {\n const n = Number(val);\n if (!Number.isNaN(n)) retry = n;\n }\n }\n if (!sawField) return undefined;\n const dataText = dataLines.join('\\n');\n let data: unknown = dataText;\n if (dataKind === 'json' && dataText !== '') {\n try {\n data = JSON.parse(dataText);\n } catch (error) {\n throw new SseParseError(\n `Failed to parse SSE event data as JSON: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n return { event, data, id, retry };\n}\n";
12
+ readonly 'create-client.ts': "import { ApiError } from './errors.js';\nimport { parse, readError } from './parse.js';\nimport { middlewareChain, send, toHeaderRecord, type SendCapabilities } from './send.js';\nimport type {\n ApiErrorLike,\n Client,\n ClientConfig,\n Middleware,\n OperationContext,\n OperationDescriptor,\n OpsShape,\n PaginationSpec,\n ParseAs,\n QueryValue,\n RequestOptions,\n SecuritySpec,\n ServerSentEvent,\n SseOptions,\n TokenProvider,\n} from './types.js';\nimport { buildUrl, substitutePath, type QueryStyle } from './url.js';\n\n/**\n * The optional behaviors `createClientCore` can dispatch to but never statically\n * imports. The package's public `createClient` wires the full set; the future\n * inline-mode assembler wires only the capabilities a spec needs.\n */\nexport type Capabilities = SendCapabilities & {\n resolveAuth?: (\n security: readonly (readonly SecuritySpec[])[],\n config: ClientConfig\n ) => Promise<{ headers: Record<string, string>; query: Record<string, string> }>;\n sse?: (\n config: ClientConfig,\n op: OperationContext,\n // Re-preparing per (re)connect (not a frozen url/init) lets a refresh-style\n // TokenProvider issue a fresh credential after a dropped stream reconnects.\n prepare: () => Promise<{ url: string; init: SseOptions }>,\n dataKind: 'json' | 'text'\n ) => AsyncGenerator<ServerSentEvent<unknown>>;\n paginate?: {\n pages: (\n call: (args?: OperationArgs, init?: RequestOptions) => Promise<unknown>,\n spec: PaginationSpec,\n args?: OperationArgs,\n init?: RequestOptions\n ) => AsyncGenerator<unknown>;\n items: (\n call: (args?: OperationArgs, init?: RequestOptions) => Promise<unknown>,\n spec: PaginationSpec,\n args?: OperationArgs,\n init?: RequestOptions\n ) => AsyncGenerator<unknown>;\n };\n};\n\n/** The grouped args wire shape: path params by name plus the `params`/`body`/`headers` slots. */\nexport type OperationArgs = {\n params?: Record<string, QueryValue>;\n body?: unknown;\n headers?: Record<string, unknown>;\n} & Record<string, unknown>;\n\n/** The response reader implied by the descriptor (before any per-call `parseAs` override). */\nfunction kindFor(op: OperationDescriptor): ParseAs | 'void' {\n if (op.responseKind === 'void' || op.responseKind === 'blob' || op.responseKind === 'text') {\n return op.responseKind;\n }\n return 'auto';\n}\n\n/** Route the grouped args by the descriptor: path values, query object, body, extra headers. */\nfunction splitArgs(op: OperationDescriptor, args: OperationArgs) {\n const path: Record<string, unknown> = {};\n for (const param of op.params ?? []) {\n if (param.in === 'path') path[param.name] = args[param.name];\n }\n return { path, query: args.params, body: args.body, headers: args.headers };\n}\n\n/**\n * The query-serialization hints for the descriptor's query params. A spec is built only\n * when the param deviates from the OpenAPI defaults (`form` + `explode: true`, encoded),\n * and always fully resolved — so `explode: false` or `allowReserved` alone (no `style`)\n * are honored, and an omitted `explode` keeps the exploded default.\n */\nfunction queryStyles(op: OperationDescriptor): Record<string, QueryStyle> | undefined {\n let styles: Record<string, QueryStyle> | undefined;\n for (const param of op.params ?? []) {\n if (param.in !== 'query') continue;\n const deviates =\n (param.style !== undefined && param.style !== 'form') ||\n param.explode === false ||\n param.allowReserved === true;\n if (!deviates) continue;\n styles ??= {};\n styles[param.name] = {\n style: param.style ?? 'form',\n explode: param.explode ?? true,\n allowReserved: param.allowReserved,\n };\n }\n return styles;\n}\n\n/** Stringify caller-supplied extra headers, skipping empty entries. */\nfunction stringHeaders(headers: Record<string, unknown> | undefined): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers ?? {})) {\n if (value !== undefined && value !== null) out[key] = String(value);\n }\n return out;\n}\n\n/** Build the request pieces an attempt needs: the final URL and the merged per-call init. */\nasync function prepareRequest(\n config: ClientConfig,\n op: OperationDescriptor,\n args: OperationArgs,\n init: RequestOptions | SseOptions,\n caps: Capabilities\n): Promise<{ url: string; init: RequestOptions; body: unknown }> {\n const { path, query, body, headers } = splitArgs(op, args);\n const authed =\n op.security?.length && caps.resolveAuth\n ? await caps.resolveAuth(op.security, config)\n : { headers: {}, query: {} };\n const fullQuery: Record<string, QueryValue> = { ...query, ...authed.query };\n const url = buildUrl(\n config.serverUrl ?? '',\n substitutePath(op.path, path),\n Object.keys(fullQuery).length > 0 ? fullQuery : undefined,\n queryStyles(op)\n );\n const mergedInit: RequestOptions = {\n ...init,\n method: op.method.toUpperCase(),\n // Precedence, lowest → highest (later spreads win): injected auth → explicit\n // header params → caller `init.headers` — the caller always overrides both.\n headers: {\n ...authed.headers,\n ...stringHeaders(headers),\n ...toHeaderRecord(init.headers),\n },\n };\n return { url, init: mergedInit, body };\n}\n\n/** One non-SSE call: send, then branch on the configured error mode. */\nasync function execute(\n config: ClientConfig,\n op: OperationDescriptor,\n args: OperationArgs,\n init: RequestOptions,\n caps: Capabilities\n): Promise<unknown> {\n const prepared = await prepareRequest(config, op, args, init, caps);\n const opCtx: OperationContext = { id: op.id, path: op.path, tags: [...(op.tags ?? [])] };\n const { parseAs, ...sendInit } = prepared.init;\n const { response, context } = await send(\n config,\n opCtx,\n prepared.url,\n sendInit,\n prepared.body,\n op.body?.multipart === true,\n caps\n );\n const readKind = parseAs ?? kindFor(op);\n if (config.errorMode === 'result') {\n if (!response.ok) {\n return { data: undefined, error: await readError(response), response };\n }\n return { data: await parse(response, readKind), error: undefined, response };\n }\n if (!response.ok) {\n let error: globalThis.Error = new ApiError(\n context.url,\n response.status,\n response.statusText,\n await readError(response)\n );\n // Thread the error through each middleware's onError in turn (each may replace it).\n for (const mw of middlewareChain(config)) {\n if (mw.onError) error = await mw.onError(error as ApiErrorLike, context);\n }\n throw error;\n }\n return parse(response, readKind);\n}\n\n/** The paginate capability, or a descriptive throw when a paginated op is iterated unwired. */\nfunction paginateCapability(caps: Capabilities, op: OperationDescriptor) {\n if (!caps.paginate) {\n throw new Error(`Pagination capability not wired: cannot iterate operation \"${op.id}\"`);\n }\n return caps.paginate;\n}\n\n/**\n * The per-page call the iterators drive: the method itself in throw mode; in result\n * mode a wrapper that unwraps the `{ data, error, response }` envelope — the page\n * pointers are data-rooted — rethrowing a failed page as `ApiError` (iteration is\n * error-mode-agnostic; the throw-mode-only `onError` middleware hook is not invoked).\n */\nfunction pageCall(\n method: (args?: OperationArgs, init?: RequestOptions) => Promise<unknown>,\n config: ClientConfig\n) {\n if (config.errorMode !== 'result') return method;\n return async (args?: OperationArgs, init?: RequestOptions) => {\n const envelope = (await method(args, init)) as {\n data: unknown;\n error: unknown;\n response: Response;\n };\n // Failure is `!response.ok` — NOT `data === undefined`: a successful bodyless page\n // (204/void) also parses to undefined data, and a failed page's `error` can be\n // undefined too (unreadable body). The pointers then miss on the undefined data\n // and iteration stops cleanly, which is the correct semantics for an empty page.\n if (!envelope.response.ok) {\n const { response } = envelope;\n throw new ApiError(response.url, response.status, response.statusText, envelope.error);\n }\n return envelope.data;\n };\n}\n\n/**\n * Build a typed instance client over operation descriptors: one real bound method per\n * operation (attached by a construction-time loop — no Proxy), plus the core members\n * (`configure`/`use`/`auth`), which are assigned AFTER the loop so they win any name\n * collision with an operation. All behavior dispatches through the capability seam.\n */\nexport function createClientCore<\n Ops extends OpsShape,\n Id extends string = string,\n Path extends string = string,\n Tag extends string = string,\n>(\n operations: Record<string, OperationDescriptor>,\n initial: ClientConfig<OperationContext<Id, Path, Tag>> = {},\n caps: Capabilities = {}\n): Client<Ops, OperationContext<Id, Path, Tag>> {\n // The literal-union narrowing is a compile-time DX contract only; internally the\n // runtime works with the base (string-typed) context. One cast at this boundary —\n // `ClientConfig<Narrow>` is not assignable to `ClientConfig` (middleware ctx\n // params are contravariant).\n const given = initial as ClientConfig;\n // Private mutable config; the middleware array is copied so `use()` never mutates the caller's.\n const config: ClientConfig = { ...given, middleware: [...(given.middleware ?? [])] };\n const client = {} as Record<string, unknown>;\n\n for (const [name, op] of Object.entries(operations)) {\n if (op.responseKind === 'sse') {\n client[name] = (args: OperationArgs = {}, init: SseOptions = {}) => {\n if (!caps.sse) {\n throw new Error(`SSE capability not wired: cannot stream operation \"${op.id}\"`);\n }\n const stream = caps.sse;\n return (async function* () {\n const opCtx: OperationContext = { id: op.id, path: op.path, tags: [...(op.tags ?? [])] };\n // A thunk the stream re-runs on every (re)connect, so auth (which `prepareRequest`\n // resolves) is refreshed per attempt rather than frozen at the first connect.\n const prepare = async () => {\n const prepared = await prepareRequest(config, op, args, init, caps);\n return { url: prepared.url, init: prepared.init as SseOptions };\n };\n yield* stream(config, opCtx, prepare, op.sseDataKind ?? 'text');\n })();\n };\n } else {\n const method = (args: OperationArgs = {}, init: RequestOptions = {}) =>\n execute(config, op, args, init, caps);\n const spec = op.pagination;\n // Paginated ops keep their one-shot call and gain `.pages`/`.items`, dispatching\n // through the capability seam (like SSE: absent capability throws descriptively).\n // Iteration is error-mode-agnostic: the iterators' pointers are data-rooted, so on\n // a result-mode client (`errorMode` is fixed at construction — `configure()`\n // ignores it) each page's envelope is unwrapped before it reaches the capability.\n // A failed page aborts iteration by throwing ApiError, even on result-mode\n // clients; the `onError` middleware hook (throw-mode-only) is not invoked.\n client[name] = spec\n ? Object.assign(method, {\n pages: (args?: OperationArgs, init?: RequestOptions) =>\n paginateCapability(caps, op).pages(pageCall(method, config), spec, args, init),\n items: (args?: OperationArgs, init?: RequestOptions) =>\n paginateCapability(caps, op).items(pageCall(method, config), spec, args, init),\n })\n : method;\n }\n }\n\n // Core members are assigned AFTER the operation loop — they win over colliding op names.\n client.configure = (next: ClientConfig): void => {\n // `errorMode` is fixed at generate time (it shapes the static types); flipping it at\n // runtime would silently desync return shapes from `Client<Ops>`, so it is ignored.\n const { errorMode: _fixed, auth, ...rest } = next;\n Object.assign(config, rest);\n // `auth` merges into existing credentials (like the `auth.*` setters) rather than\n // replacing wholesale — so `configure({ auth: { bearer } })` keeps a previously set\n // basic/apiKey. `apiKey` merges per scheme.\n if (auth) {\n config.auth = {\n ...config.auth,\n ...auth,\n ...(auth.apiKey ? { apiKey: { ...config.auth?.apiKey, ...auth.apiKey } } : {}),\n };\n }\n };\n client.use = (...middleware: Middleware[]): void => {\n // Reassign (don't push) so a caller-provided `middleware` array isn't mutated.\n config.middleware = [...(config.middleware ?? []), ...middleware];\n };\n client.auth = {\n bearer(token: TokenProvider): void {\n config.auth = { ...config.auth, bearer: token };\n },\n basic(username: string, password: string): void {\n config.auth = { ...config.auth, basic: { username, password } };\n },\n apiKey(scheme: string, value: TokenProvider): void {\n config.auth = { ...config.auth, apiKey: { ...config.auth?.apiKey, [scheme]: value } };\n },\n };\n\n return client as Client<Ops, OperationContext<Id, Path, Tag>>;\n}\n";
13
+ readonly 'paginate.ts': "import type { OperationArgs } from './create-client.js';\nimport type { PaginationSpec, QueryValue, RequestOptions } from './types.js';\n\n/**\n * Auto-pagination (capability module — wired into `createClient`, dispatched by the\n * method's `.pages()`/`.items()`): walk an operation's pages by advancing the descriptor's\n * `param` query parameter, per its `style`. The caller's args are never mutated — each\n * request gets a fresh `params` clone — and `init` is forwarded to every call.\n *\n * Iteration is error-mode-agnostic: `call` always resolves to the RAW page (on a\n * result-mode client the attachment unwraps the envelope first), so a failed page\n * aborts iteration by throwing `ApiError`, even on result-mode clients; the `onError`\n * middleware hook (throw-mode-only) is not invoked.\n */\n\n/**\n * Resolve an RFC 6901 JSON pointer (`~1` → `/`, `~0` → `~`) against a value.\n * The empty pointer is the whole document; anything else must start with `/`.\n * Returns `undefined` on any miss (bad token, absent key, non-object step) — never throws.\n */\nexport function resolvePointer(value: unknown, pointer: string): unknown {\n if (pointer === '') return value;\n if (!pointer.startsWith('/')) return undefined;\n let current = value;\n for (const token of pointer.slice(1).split('/')) {\n const key = token.replaceAll('~1', '/').replaceAll('~0', '~');\n if (Array.isArray(current)) {\n if (!/^(0|[1-9]\\d*)$/.test(key)) return undefined;\n current = current[Number(key)];\n } else if (Object(current) === current && key in (current as object)) {\n current = (current as Record<string, unknown>)[key];\n } else {\n return undefined;\n }\n }\n return current;\n}\n\n/**\n * Iterate an operation's full page results. Every page is yielded before the stop\n * condition is evaluated, so the last page always arrives. Cursor style resumes from a\n * caller-provided `params[spec.param]`, stops when `nextCursor` resolves to\n * `undefined`/`null`/`''`, and throws if the next cursor is not a string or number, or\n * if the same cursor comes back twice in a row (infinite-loop guards). Offset/page\n * styles advance by item count / by one and stop when\n * the `items` pointer misses or the array is empty.\n */\nexport async function* pages<TPage>(\n call: (args?: OperationArgs, init?: RequestOptions) => Promise<TPage>,\n spec: PaginationSpec,\n args: OperationArgs = {},\n init?: RequestOptions\n): AsyncGenerator<TPage> {\n if (spec.style === 'cursor') {\n let cursor: unknown = args.params?.[spec.param];\n while (true) {\n const params = { ...args.params };\n if (cursor !== undefined) params[spec.param] = cursor as QueryValue;\n const page = await call({ ...args, params }, init);\n yield page;\n const next = resolvePointer(page, spec.nextCursor!);\n if (next === undefined || next === null || next === '') return;\n if (typeof next !== 'string' && typeof next !== 'number') {\n // A fresh non-scalar cursor never compares equal, so without this guard a lying\n // server would slip past the did-not-advance check into an infinite loop.\n throw new Error(`Pagination cursor at ${spec.nextCursor} is not a string or number`);\n }\n if (next === cursor) {\n throw new Error('Pagination did not advance: operation returned the same cursor twice');\n }\n cursor = next;\n }\n } else {\n // Coerce the starting position to a number: a caller may pass `params[spec.param]` as a\n // string (common from URL/form input), and `+=` on a string would concatenate.\n const start = args.params?.[spec.param];\n const fallback = spec.style === 'page' ? 1 : 0;\n let position = start === undefined || Number.isNaN(Number(start)) ? fallback : Number(start);\n while (true) {\n const page = await call(\n { ...args, params: { ...args.params, [spec.param]: position } },\n init\n );\n yield page;\n const pageItems = resolvePointer(page, spec.items);\n if (!Array.isArray(pageItems) || pageItems.length === 0) return;\n position += spec.style === 'page' ? 1 : pageItems.length;\n }\n }\n}\n\n/**\n * Iterate the operation's individual items: each page's `items` pointer, flattened.\n * A cursor-style page whose pointer misses yields nothing but pagination continues;\n * for offset/page styles a miss has already stopped `pages`.\n */\nexport async function* items<TItem>(\n call: (args?: OperationArgs, init?: RequestOptions) => Promise<unknown>,\n spec: PaginationSpec,\n args?: OperationArgs,\n init?: RequestOptions\n): AsyncGenerator<TItem> {\n for await (const page of pages(call, spec, args, init)) {\n const pageItems = resolvePointer(page, spec.items);\n if (Array.isArray(pageItems)) yield* pageItems as TItem[];\n }\n}\n";
14
+ };
15
+ export type RuntimeModuleName = keyof typeof RUNTIME_SOURCES;
16
+ //# sourceMappingURL=runtime-sources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-sources.d.ts","sourceRoot":"","sources":["../../src/emitters/runtime-sources.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,eAAe;;;;;;;;;;;;;CAyBlB,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,MAAM,OAAO,eAAe,CAAC"}
@@ -0,0 +1,16 @@
1
+ // GENERATED by scripts/generate-runtime-sources.mjs — do not edit. Regenerated on install (`prepare`); manually: `npm run prepare -w @redocly/client-generator`.
2
+ export const RUNTIME_SOURCES = {
3
+ 'types.ts': "/**\n * The public type surface of the client runtime — `@redocly/client-generator`'s\n * app-facing runtime module. Pure types, no runtime code (excluded from coverage).\n * The generator emits `OPERATIONS` literals typed\n * `satisfies Record<string, OperationDescriptor>` against this module, so an\n * incompatible runtime/generated pair fails the consumer's build (the semver skew guard).\n */\n\n/** How one operation parameter is sent: its location plus OpenAPI query-serialization hints. */\nexport type ParamSpec = {\n name: string;\n in: 'path' | 'query' | 'header';\n style?: 'form' | 'spaceDelimited' | 'pipeDelimited' | 'deepObject';\n explode?: boolean;\n allowReserved?: boolean;\n};\n\n/** One security scheme, denormalized onto the operation (`scheme` names the spec's scheme). */\nexport type SecuritySpec =\n | { scheme: string; kind: 'bearer' | 'basic' }\n | { scheme: string; kind: 'apiKey'; name: string; in: 'header' | 'query' | 'cookie' };\n\n/**\n * How to auto-iterate a paginated operation (drives its `.pages()`/`.items()` members).\n * `nextCursor` and `items` are RFC 6901 JSON pointers into the page (response) value.\n */\nexport type PaginationSpec = {\n style: 'cursor' | 'offset' | 'page';\n /** The query param the iterator advances: the cursor (`cursor`) or number (`offset`/`page`). */\n param: string;\n /** Optional page-size query param (recorded for tooling; never set by the runtime). */\n limitParam?: string;\n /** Cursor style only: pointer to the next cursor in the page. */\n nextCursor?: string;\n /** Pointer to the page's item array. */\n items: string;\n};\n\n/** The frozen data contract between generated code and the runtime: one operation's wire shape. */\nexport type OperationDescriptor = {\n id: string;\n method: string;\n path: string;\n tags?: readonly string[];\n params?: readonly ParamSpec[];\n /** `multipart: true` marks a typed object body serialized to FormData by the runtime. */\n body?: { contentType: string; multipart?: boolean };\n /** Defaults to `'json'` (content-type negotiation on parse). */\n responseKind?: 'json' | 'text' | 'blob' | 'void' | 'sse';\n sseDataKind?: 'json' | 'text';\n /** OR-alternatives, each an AND-set: the runtime applies the first fully-configured one. */\n security?: readonly (readonly SecuritySpec[])[];\n pagination?: PaginationSpec;\n};\n\n/** A query value: scalars, arrays of scalars, or objects (serialized as deepObject brackets). */\nexport type QueryValue =\n | string\n | number\n | boolean\n | null\n | undefined\n | Array<string | number | boolean | null | undefined>\n | Record<string, unknown>;\n\n/** A credential: a literal, or a (possibly async) function resolved per request (refresh flows). */\nexport type TokenProvider = string | (() => string | Promise<string>);\n\n/** Per-instance credentials, keyed by the scheme kinds the runtime can inject. */\nexport type AuthCredentials = {\n bearer?: TokenProvider;\n basic?: { username: string; password: string };\n apiKey?: Record<string, TokenProvider>;\n};\n\n/** Backoff shape: 'fixed' = constant delay; 'exponential' = doubling per attempt. */\nexport type RetryStrategy = 'fixed' | 'exponential';\n\n/**\n * The operation's identity, exposed to middleware for targeting (`ctx.operation`).\n * Generated clients instantiate the type parameters with the spec's literal unions\n * (`OperationId`/`OperationPath`/`OperationTag`) so a misspelled operation id in a\n * middleware comparison fails to compile; the string defaults keep every\n * spec-independent consumer (`runtime-contract.ts`, the runtime internals) working\n * with the base shape. `tags` stays mutable (`Tag[]`) so setup-contract types\n * (byte-locked to generated output) remain assignable through middleware callbacks.\n */\nexport type OperationContext<\n Id extends string = string,\n Path extends string = string,\n Tag extends string = string,\n> = { id: Id; path: Path; tags: Tag[] };\n\n/** The mutable request context threaded through the middleware chain. */\nexport type RequestContext<Op extends OperationContext = OperationContext> = {\n url: string;\n method: string;\n headers: Record<string, string>;\n body?: unknown;\n operation: Op;\n};\n\n/** The failed attempt handed to a custom `retryOn`: exactly one of `response`/`error` is set. */\nexport type RetryContext<Op extends OperationContext = OperationContext> = {\n attempt: number;\n request: RequestContext<Op>;\n response?: Response;\n error?: unknown;\n};\n\n/** Opt-in retry policy; a per-call override merges field-by-field over the config policy. */\nexport type RetryConfig<Op extends OperationContext = OperationContext> = {\n retries?: number;\n retryDelay?: number;\n retryStrategy?: RetryStrategy;\n jitter?: boolean;\n retryOn?: (ctx: RetryContext<Op>) => boolean | Promise<boolean>;\n};\n\n/**\n * Structural stand-in for the runtime's ApiError so this module stays import-free\n * (pure types); the real `ApiError` class is assignable to it.\n */\nexport type ApiErrorLike = globalThis.Error & {\n url: string;\n status: number;\n statusText: string;\n body: unknown;\n};\n\n/** One interceptor: any subset of the three hooks. */\nexport type Middleware<Op extends OperationContext = OperationContext> = {\n onRequest?: (ctx: RequestContext<Op>) => void | Promise<void>;\n onResponse?: (\n response: Response,\n ctx: RequestContext<Op>\n ) => Response | void | Promise<Response | void>;\n /** Throw mode only: may map/replace the error. */\n // `globalThis.Error` so a spec schema named `Error` cannot shadow it in inline mode.\n onError?: (\n error: ApiErrorLike,\n ctx: RequestContext<Op>\n ) => globalThis.Error | Promise<globalThis.Error>;\n};\n\n/** Client configuration: transport, defaults, retry policy, middleware, and credentials. */\nexport type ClientConfig<Op extends OperationContext = OperationContext> = {\n serverUrl?: string;\n fetch?: typeof fetch;\n headers?:\n | Record<string, string>\n | (() => Record<string, string> | Promise<Record<string, string>>);\n retry?: RetryConfig<Op>;\n middleware?: Middleware<Op>[];\n auth?: AuthCredentials;\n /** Fixed at generate time by the generator (`'throw'` when omitted); `configure()` ignores it. */\n errorMode?: 'throw' | 'result';\n onRequest?: Middleware<Op>['onRequest'];\n onResponse?: Middleware<Op>['onResponse'];\n onError?: Middleware<Op>['onError'];\n};\n\n/** Response readers for the per-call `parseAs` override. */\nexport type ParseAs = 'auto' | 'json' | 'text' | 'blob' | 'arrayBuffer' | 'formData' | 'stream';\n\n/** Per-call options: standard `RequestInit` plus a retry override and a forced reader. */\nexport type RequestOptions = RequestInit & { retry?: RetryConfig; parseAs?: ParseAs };\n\n/** Per-call options for an SSE stream; reconnect defaults to true. */\nexport type SseOptions = RequestInit & { reconnect?: boolean; reconnectDelay?: number };\n\n/** A single decoded Server-Sent Event with its payload typed from the spec. */\nexport type ServerSentEvent<T> = { event?: string; data: T; id?: string; retry?: number };\n\n/** Result-mode return shape: exactly one of `data`/`error` is set. */\nexport type Result<TData, TError> =\n | { data: TData; error: undefined; response: Response }\n | { data: undefined; error: TError; response: Response };\n\n/**\n * The generated `Ops` type's shape: per-operation args/result, plus `kind: 'sse'` for\n * streams and, for paginated operations, `item` (the page's element type) and — on\n * result-mode clients only — `page` (the RAW page type `.pages()` yields, since\n * iteration unwraps the `Result` envelope the one-shot `result` carries).\n */\nexport type OpsShape = Record<\n string,\n { args: object; result: unknown; kind?: 'sse'; item?: unknown; page?: unknown }\n>;\n\n/** The always-present client members (assigned after the operation loop — they win collisions). */\nexport type ClientCore<Op extends OperationContext = OperationContext> = {\n /** Merge into the config; note `middleware` REPLACES the chain (use `use()` to compose). */\n configure(config: ClientConfig<Op>): void;\n /** Append interceptors (composes with baked/publisher middleware). */\n use(...middleware: Middleware<Op>[]): void;\n auth: {\n bearer(token: TokenProvider): void;\n basic(username: string, password: string): void;\n apiKey(scheme: string, value: TokenProvider): void;\n };\n};\n\n/**\n * The standard TypeScript optionality probe: `{}` has no required members, so\n * `{} extends A` is true exactly when every member of `A` is optional.\n */\n// oxlint-disable-next-line typescript/no-empty-object-type\ntype NoRequiredKeys<A> = {} extends A ? true : false;\n\n/**\n * The page type `.pages()` yields: the RAW page declared by `page` (the generator\n * writes it only on result-mode paginated entries, whose `result` is the envelope),\n * or the method's own `result` (throw mode — already the raw page).\n */\ntype PageOf<Entry extends OpsShape[string]> = Entry extends { page: unknown }\n ? Entry['page']\n : Entry['result'];\n\n/**\n * The auto-pagination members intersected onto a paginated method — present exactly when\n * the Ops entry declares `item` (the generator writes it only for paginated operations).\n * Args optionality mirrors the method's own; `unknown` otherwise (identity under `&`).\n * Iteration is error-mode-agnostic: `.pages()`/`.items()` yield raw pages/items, and a\n * failed page aborts iteration by throwing `ApiError`, even on result-mode clients; the\n * `onError` middleware hook (throw-mode-only) is not invoked.\n */\ntype Paginated<Entry extends OpsShape[string]> = 'item' extends keyof Entry\n ? NoRequiredKeys<Entry['args']> extends true\n ? {\n pages(args?: Entry['args'], init?: RequestOptions): AsyncGenerator<PageOf<Entry>>;\n items(args?: Entry['args'], init?: RequestOptions): AsyncGenerator<Entry['item']>;\n }\n : {\n pages(args: Entry['args'], init?: RequestOptions): AsyncGenerator<PageOf<Entry>>;\n items(args: Entry['args'], init?: RequestOptions): AsyncGenerator<Entry['item']>;\n }\n : unknown;\n\n/** The typed instance client: one bound method per operation plus the core members. */\nexport type Client<Ops extends OpsShape, Op extends OperationContext = OperationContext> = {\n [K in keyof Ops]: Ops[K] extends { kind: 'sse' }\n ? NoRequiredKeys<Ops[K]['args']> extends true\n ? (\n args?: Ops[K]['args'],\n init?: SseOptions\n ) => AsyncGenerator<ServerSentEvent<Ops[K]['result']>>\n : (\n args: Ops[K]['args'],\n init?: SseOptions\n ) => AsyncGenerator<ServerSentEvent<Ops[K]['result']>>\n : (NoRequiredKeys<Ops[K]['args']> extends true\n ? (args?: Ops[K]['args'], init?: RequestOptions) => Promise<Ops[K]['result']>\n : (args: Ops[K]['args'], init?: RequestOptions) => Promise<Ops[K]['result']>) &\n Paginated<Ops[K]>;\n} & ClientCore<Op>;\n",
4
+ 'errors.ts': "/** The error thrown (throw mode) for a non-2xx response, carrying the decoded error body. */\nexport class ApiError extends Error {\n public readonly url: string;\n public readonly status: number;\n public readonly statusText: string;\n public readonly body: unknown;\n constructor(url: string, status: number, statusText: string, body: unknown) {\n super(`Request failed with status ${status}`);\n this.name = 'ApiError';\n this.url = url;\n this.status = status;\n this.statusText = statusText;\n this.body = body;\n }\n}\n\n/** The error to throw for an aborted request: the caller's abort reason when it is an Error. */\n// `globalThis.Error` (not bare `Error`) so a spec schema named `Error` cannot shadow it\n// when this module is embedded alongside generated types (inline mode).\nexport function abortError(signal: AbortSignal): globalThis.Error {\n const reason = (signal as { reason?: unknown }).reason;\n if (reason instanceof Error) return reason;\n return new DOMException('The operation was aborted.', 'AbortError');\n}\n",
5
+ 'url.ts': "import type { ParamSpec, QueryValue } from './types.js';\n\n/**\n * The RESOLVED OpenAPI serialization spec for one query parameter — callers apply the\n * OpenAPI defaults (`style: 'form'`, `explode: true`) before building one.\n */\nexport type QueryStyle = {\n style: NonNullable<ParamSpec['style']>;\n explode: boolean;\n allowReserved?: boolean;\n};\n\n/**\n * Encode everything except the RFC-3986 reserved set, for `allowReserved: true` params —\n * `filter=a/b` survives instead of `filter=a%2Fb`.\n */\nexport function encodeReserved(value: string): string {\n return encodeURIComponent(value).replace(\n /%(3A|2F|3F|23|5B|5D|40|21|24|26|27|28|29|2A|2B|2C|3B|3D)/g,\n (match) => decodeURIComponent(match)\n );\n}\n\n/** Substitute `{name}` template segments with encoded values; a missing value is a caller bug. */\nexport function substitutePath(template: string, values: Record<string, unknown>): string {\n return template.replace(/\\{([^{}]+)\\}/g, (_match, name: string) => {\n const value = values[name];\n if (value === undefined) throw new Error(`Missing path parameter \"${name}\"`);\n return encodeURIComponent(String(value));\n });\n}\n\n/**\n * Build the request URL: `serverUrl` (trailing slash trimmed) + path + serialized query.\n * Query parameters honor their OpenAPI `style`/`explode`/`allowReserved` (from `styles`);\n * without a spec, arrays repeat the key (`form`+`explode`), objects serialize as\n * `deepObject` brackets, and `null`/`undefined` entries are skipped.\n */\nexport function buildUrl(\n serverUrl: string,\n path: string,\n query?: Record<string, QueryValue>,\n styles?: Record<string, QueryStyle>\n): string {\n // Trim trailing slashes with a scan, not `/\\/+$/` — an anchored `+` regex is\n // quadratic on adversarial many-slash input (the server URL is caller data).\n let end = serverUrl.length;\n while (end > 0 && serverUrl.charCodeAt(end - 1) === 47 /* '/' */) end--;\n const url = serverUrl.slice(0, end) + path;\n if (!query) return url;\n const params = new URLSearchParams();\n const raw: string[] = [];\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null) continue;\n const spec = styles?.[key];\n if (!spec) {\n if (Array.isArray(value)) {\n for (const v of value) {\n if (v !== undefined && v !== null) params.append(key, String(v));\n }\n } else if (Object(value) === value) {\n // Object-valued query params use `deepObject` style: key[subKey]=subValue.\n for (const [subKey, subValue] of Object.entries(value)) {\n if (subValue !== undefined && subValue !== null) {\n params.append(`${key}[${subKey}]`, String(subValue));\n }\n }\n } else {\n params.append(key, String(value));\n }\n continue;\n }\n if (Array.isArray(value)) {\n const items = value.filter((v) => v !== undefined && v !== null).map(String);\n if (spec.style === 'form' && spec.explode) {\n for (const v of items) {\n if (spec.allowReserved) raw.push(`${key}=${encodeReserved(v)}`);\n else params.append(key, v);\n }\n } else {\n // Delimited styles put the LITERAL delimiter on the wire; only the\n // values are encoded. `%20` (not `+`) is the literal space delimiter.\n const delim =\n spec.style === 'pipeDelimited' ? '|' : spec.style === 'spaceDelimited' ? '%20' : ',';\n const enc = spec.allowReserved ? encodeReserved : encodeURIComponent;\n raw.push(`${encodeURIComponent(key)}=${items.map(enc).join(delim)}`);\n }\n } else if (Object(value) === value) {\n // `deepObject` (and any object spec, for now): key[subKey]=subValue.\n for (const [subKey, subValue] of Object.entries(value)) {\n if (subValue !== undefined && subValue !== null) {\n if (spec.allowReserved) raw.push(`${key}[${subKey}]=${encodeReserved(String(subValue))}`);\n else params.append(`${key}[${subKey}]`, String(subValue));\n }\n }\n } else if (spec.allowReserved) {\n raw.push(`${key}=${encodeReserved(String(value))}`);\n } else {\n params.append(key, String(value));\n }\n }\n const qs = [params.toString(), ...raw].filter(Boolean).join('&');\n return qs ? `${url}?${qs}` : url;\n}\n",
6
+ 'parse.ts': "import type { ParseAs } from './types.js';\n\n/**\n * Read the response body per `kind`. `'auto'` negotiates from the content type\n * (JSON, then `text/*`, then Blob); `'void'` and `204` responses read nothing.\n */\nexport async function parse(response: Response, kind: ParseAs | 'void'): Promise<unknown> {\n if (kind === 'void' || response.status === 204) return undefined;\n if (kind === 'stream') return response.body;\n if (kind === 'blob') return response.blob();\n if (kind === 'arrayBuffer') return response.arrayBuffer();\n if (kind === 'formData') return response.formData();\n if (kind === 'text') return response.text();\n if (kind === 'json') return response.json();\n // 'auto' — negotiate from the response's content type (case-insensitively:\n // `Text/Plain` and `application/JSON` are valid per RFC 9110).\n const contentType = (response.headers.get('content-type') ?? '').toLowerCase();\n if (contentType.includes('json')) return response.json();\n if (contentType.startsWith('text/')) return response.text();\n return response.blob();\n}\n\n/** Best-effort decode of a non-2xx body (JSON when declared, else text; undefined on failure). */\nexport async function readError(response: Response): Promise<unknown> {\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.toLowerCase().includes('json')) {\n return response.json().catch(() => undefined);\n }\n return response.text().catch(() => undefined);\n}\n",
7
+ 'retry.ts': "import { abortError } from './errors.js';\nimport type { RetryConfig, RetryContext } from './types.js';\n\nconst IDEMPOTENT_METHODS = new Set(['GET', 'HEAD', 'PUT', 'DELETE', 'OPTIONS']);\nconst TRANSIENT_STATUS = new Set([408, 429, 500, 502, 503, 504]);\n\n/**\n * The default retry predicate: idempotent methods only, on a transport error or a\n * transient status. A custom `retryOn` fully replaces this (no method check kept).\n */\nexport function defaultRetryOn(ctx: RetryContext): boolean {\n if (!IDEMPOTENT_METHODS.has(ctx.request.method.toUpperCase())) return false;\n return ctx.response === undefined || TRANSIENT_STATUS.has(ctx.response.status);\n}\n\n/**\n * The delay before the next attempt: a `Retry-After` header (seconds or HTTP-date)\n * wins; otherwise fixed/exponential backoff over `retryDelay`, with full jitter\n * unless `jitter === false`.\n */\nexport function retryDelay(retry: RetryConfig, attempt: number, retryAfter: string | null): number {\n if (retryAfter) {\n const seconds = Number(retryAfter);\n if (!Number.isNaN(seconds)) return seconds * 1000;\n const when = Date.parse(retryAfter);\n if (!Number.isNaN(when)) return Math.max(0, when - Date.now());\n }\n const base = retry.retryDelay ?? 1000;\n const raw = retry.retryStrategy === 'fixed' ? base : base * Math.pow(2, attempt - 1);\n return retry.jitter === false ? raw : Math.random() * raw;\n}\n\n/** Abort-aware sleep: resolves after `ms`, rejects with the abort reason immediately on abort. */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(abortError(signal));\n return;\n }\n const onAbort = () => {\n clearTimeout(timer);\n reject(abortError(signal as AbortSignal));\n };\n const timer = setTimeout(() => {\n if (signal) signal.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n if (signal) signal.addEventListener('abort', onAbort, { once: true });\n });\n}\n",
8
+ 'multipart.ts': "/**\n * Serialize a plain object into `FormData` for a typed `multipart/form-data` body\n * (capability module — wired into `createClient`, never imported by the send core).\n * `Blob`/`File` and strings pass through; `Date`s become ISO strings; arrays append\n * one field per item; other objects are JSON-encoded; everything else is stringified.\n * `undefined`/`null` entries are skipped.\n */\nexport function toFormData(body: Record<string, unknown>): FormData {\n const fd = new FormData();\n const append = (key: string, value: unknown): void => {\n if (value === undefined || value === null) return;\n if (value instanceof Blob || typeof value === 'string') fd.append(key, value);\n else if (value instanceof Date) fd.append(key, value.toISOString());\n else if (Object(value) === value) fd.append(key, JSON.stringify(value));\n else fd.append(key, String(value));\n };\n for (const [key, value] of Object.entries(body)) {\n if (Array.isArray(value)) for (const item of value) append(key, item);\n else append(key, value);\n }\n return fd;\n}\n",
9
+ 'auth.ts': "import type { ClientConfig, SecuritySpec, TokenProvider } from './types.js';\n\n/** Resolve a credential: a literal passes through; a function is awaited per request. */\nasync function resolveToken(provider: TokenProvider): Promise<string> {\n return typeof provider === 'function' ? await provider() : provider;\n}\n\n/** UTF-8-safe base64: bare `btoa` throws on non-Latin-1 credentials (RFC 7617 allows UTF-8). */\nfunction encodeBase64(text: string): string {\n let binary = '';\n for (const byte of new TextEncoder().encode(text)) binary += String.fromCharCode(byte);\n return btoa(binary);\n}\n\n/** Whether a credential for this scheme is configured on the instance. */\nfunction isConfigured(scheme: SecuritySpec, config: ClientConfig): boolean {\n if (scheme.kind === 'apiKey') return config.auth?.apiKey?.[scheme.scheme] !== undefined;\n if (scheme.kind === 'bearer') return config.auth?.bearer !== undefined;\n return config.auth?.basic !== undefined;\n}\n\n/**\n * Build the auth headers/query for one operation's `security` OR-alternatives from the\n * instance credentials (`config.auth`) — capability module, wired into `createClient`.\n * The first alternative whose schemes (an AND-set) are all configured is applied, so\n * \"bearer OR apiKey\" works with either credential and never sends both. When none is\n * fully configured, the first alternative's configured schemes are still sent (the\n * server rejects the request, mirroring the previous behavior).\n * Cookie-borne apiKeys fold into a single `Cookie` header joined with `; `.\n */\nexport async function resolveAuth(\n security: readonly (readonly SecuritySpec[])[],\n config: ClientConfig\n): Promise<{ headers: Record<string, string>; query: Record<string, string> }> {\n const alternative =\n security.find((schemes) => schemes.every((scheme) => isConfigured(scheme, config))) ??\n security[0] ??\n [];\n const headers: Record<string, string> = {};\n const query: Record<string, string> = {};\n const cookies: string[] = [];\n for (const scheme of alternative) {\n if (scheme.kind === 'apiKey') {\n const provider = config.auth?.apiKey?.[scheme.scheme];\n if (provider === undefined) continue;\n const value = await resolveToken(provider);\n if (scheme.in === 'header') headers[scheme.name] = value;\n else if (scheme.in === 'query') query[scheme.name] = value;\n // Cookie values may contain reserved characters (`;`, `=`, space, …); percent-encode\n // so the credential can't break the `Cookie` header syntax.\n else cookies.push(`${scheme.name}=${encodeURIComponent(value)}`);\n } else if (scheme.kind === 'bearer') {\n const provider = config.auth?.bearer;\n if (provider !== undefined) headers.Authorization = `Bearer ${await resolveToken(provider)}`;\n } else {\n const basic = config.auth?.basic;\n if (basic !== undefined) {\n headers.Authorization = `Basic ${encodeBase64(`${basic.username}:${basic.password}`)}`;\n }\n }\n }\n if (cookies.length > 0) headers.Cookie = cookies.join('; ');\n return { headers, query };\n}\n",
10
+ 'setup.ts': "import type { ClientConfig, Middleware } from './types.js';\n\n/**\n * Merge a publisher's baked setup (`defineClientSetup({...})`) with the app's config:\n * app config fields win per-field over baked defaults, while middleware composes —\n * baked middleware runs first, then the app's.\n */\nexport function mergeSetup(\n setup: { config?: ClientConfig; middleware?: Middleware[] } | undefined,\n config: ClientConfig = {}\n): ClientConfig {\n return {\n ...setup?.config,\n ...config,\n middleware: [...(setup?.middleware ?? []), ...(config.middleware ?? [])],\n };\n}\n",
11
+ 'send.ts': "import { abortError } from './errors.js';\nimport { defaultRetryOn, retryDelay, sleep } from './retry.js';\nimport type {\n ClientConfig,\n Middleware,\n OperationContext,\n RequestContext,\n RequestOptions,\n RetryConfig,\n} from './types.js';\n\n/**\n * Optional behaviors the send core can use but never statically imports — wired by\n * `createClient` (the same seam the future inline-mode assembler relies on).\n */\nexport type SendCapabilities = {\n /** Serialize a typed multipart body (a plain object) to FormData. */\n serializeMultipart?: (body: Record<string, unknown>) => FormData;\n};\n\n/**\n * Normalize a caller's `HeadersInit` (plain record, `Headers` instance, or entry pairs)\n * to a plain record — spreading a `Headers` or an array contributes no entries.\n */\nexport function toHeaderRecord(headers: HeadersInit | undefined): Record<string, string> {\n if (headers === undefined) return {};\n if (headers instanceof Headers) {\n const record: Record<string, string> = {};\n headers.forEach((value, key) => {\n record[key] = value;\n });\n return record;\n }\n if (Array.isArray(headers)) return Object.fromEntries(headers);\n return headers;\n}\n\n/**\n * The effective middleware chain for a request: the single `onRequest`/`onResponse`/\n * `onError` config hooks as one implicit first middleware, then `config.middleware`.\n */\nexport function middlewareChain(config: ClientConfig): Middleware[] {\n const single =\n config.onRequest || config.onResponse || config.onError\n ? [{ onRequest: config.onRequest, onResponse: config.onResponse, onError: config.onError }]\n : [];\n return [...single, ...(config.middleware ?? [])];\n}\n\n/**\n * The fetch core shared by every operation: default + config + per-call headers, the\n * `onRequest` chain (BEFORE body serialization, so mutations are sent), body\n * serialization (JSON, or FormData via the multipart capability), the retry loop\n * (idempotent-only defaults, `Retry-After`, abandoned-body drain), and the reverse\n * `onResponse` onion. Returns the final response plus the request context.\n */\nexport async function send(\n config: ClientConfig,\n op: OperationContext,\n url: string,\n init: RequestOptions,\n body: unknown | undefined,\n multipart: boolean,\n caps: SendCapabilities\n): Promise<{ response: Response; context: RequestContext }> {\n const { retry: callRetry, ...fetchInit } = init;\n const retry: RetryConfig = { ...config.retry, ...callRetry };\n const extra = typeof config.headers === 'function' ? await config.headers() : config.headers;\n const headers: Record<string, string> = {\n Accept: 'application/json',\n ...extra,\n ...toHeaderRecord(fetchInit.headers),\n };\n const context: RequestContext = {\n url,\n method: fetchInit.method ?? 'GET',\n headers,\n body,\n operation: op,\n };\n const middleware = middlewareChain(config);\n for (const mw of middleware) if (mw.onRequest) await mw.onRequest(context);\n // Serialize AFTER onRequest so body mutations (case conversion, enveloping, signing) take effect.\n let payload: BodyInit | undefined;\n if (context.body !== undefined) {\n const value = context.body;\n const isBinary =\n value instanceof Blob ||\n value instanceof ArrayBuffer ||\n ArrayBuffer.isView(value as ArrayBufferView);\n const isFormData = typeof FormData !== 'undefined' && value instanceof FormData;\n const isURLSearchParams = value instanceof URLSearchParams;\n if (isFormData || isURLSearchParams || isBinary || typeof value === 'string') {\n payload = value as BodyInit;\n } else if (multipart) {\n if (!caps.serializeMultipart) {\n throw new Error('Multipart capability not wired: cannot serialize the request body');\n }\n payload = caps.serializeMultipart(value as Record<string, unknown>);\n } else {\n payload = JSON.stringify(value);\n if (!('Content-Type' in context.headers) && !('content-type' in context.headers)) {\n context.headers['Content-Type'] = 'application/json';\n }\n }\n }\n const doFetch = config.fetch ?? fetch;\n const maxAttempts = 1 + (retry.retries ?? 0);\n const retryOn = retry.retryOn ?? defaultRetryOn;\n const signal = fetchInit.signal ?? undefined;\n\n let attempt = 0;\n while (true) {\n attempt++;\n if (signal?.aborted) throw abortError(signal);\n let response: Response;\n try {\n response = await doFetch(context.url, {\n ...fetchInit,\n method: context.method,\n headers: context.headers,\n body: payload,\n });\n } catch (error) {\n if (\n attempt < maxAttempts &&\n !signal?.aborted &&\n (await retryOn({ attempt, request: context, error }))\n ) {\n await sleep(retryDelay(retry, attempt, null), signal);\n continue;\n }\n throw error;\n }\n // Reverse order: the last-registered middleware wraps closest to the network (onion).\n for (let i = middleware.length - 1; i >= 0; i--) {\n const onResponse = middleware[i].onResponse;\n if (onResponse) {\n const replaced = await onResponse(response, context);\n if (replaced && replaced !== response) {\n // Cancel the abandoned original's body — like the retry path, an unread body\n // keeps its connection checked out under Node/undici.\n await response.body?.cancel().catch(() => undefined);\n response = replaced;\n }\n }\n }\n if (\n !response.ok &&\n attempt < maxAttempts &&\n !signal?.aborted &&\n (await retryOn({ attempt, request: context, response }))\n ) {\n const retryAfter = response.headers.get('retry-after');\n // Drain the abandoned response body before the next attempt: an unread body\n // keeps the connection checked out (and can stall the pool) under Node/undici\n // and other strict HTTP clients. Ignore errors (e.g. a middleware already read it).\n await response.body?.cancel().catch(() => undefined);\n await sleep(retryDelay(retry, attempt, retryAfter), signal);\n continue;\n }\n return { response, context };\n }\n}\n",
12
+ 'sse.ts': "import { ApiError } from './errors.js';\nimport { readError } from './parse.js';\nimport { sleep } from './retry.js';\nimport { send, toHeaderRecord } from './send.js';\nimport type { ClientConfig, OperationContext, ServerSentEvent, SseOptions } from './types.js';\n\n/**\n * A frame delimiter: two consecutive line terminators (each CR, LF, or CRLF, per the SSE\n * spec — so mixed endings like `\\n\\r\\n` are valid boundaries, not just matching pairs).\n */\nconst FRAME_DELIMITER = /(?:\\r\\n|\\r|\\n){2}/;\n\n/**\n * A terminally malformed event stream — unparseable JSON `data` or an unbounded frame.\n * A stable bad payload, not a dropped connection, so the stream never reconnects on it.\n */\nexport class SseParseError extends Error {}\n\n/**\n * Consume a `text/event-stream` operation as typed events (capability module — wired\n * into `createClient`). Auto-reconnects on dropped connections, resuming from the last\n * seen event id via `Last-Event-ID` (backoff: the server's `retry:` value, then\n * `reconnectDelay`, then 1s — exponential with jitter, capped at 30s). A clean stream\n * end flushes a trailing frame and finishes; `break`/abort end the iterator cleanly.\n */\nexport async function* sse<T>(\n config: ClientConfig,\n op: OperationContext,\n prepare: () => Promise<{ url: string; init: SseOptions }>,\n dataKind: 'json' | 'text' = 'text'\n): AsyncGenerator<ServerSentEvent<T>> {\n let lastEventId: string | undefined;\n let serverRetry: number | undefined;\n let failures = 0;\n while (true) {\n // Re-prepare each attempt so a refresh-style TokenProvider yields a fresh credential\n // on reconnect (the auth is baked into `url` query + `init.headers`). `reconnect`,\n // `reconnectDelay`, and `signal` come from the caller's original options unchanged.\n const { url, init } = await prepare();\n const { reconnect = true, reconnectDelay, ...rest } = init;\n const signal = rest.signal ?? undefined;\n if (signal?.aborted) return;\n const headers: Record<string, string> = {\n Accept: 'text/event-stream',\n ...toHeaderRecord(rest.headers),\n };\n const sendHeaders =\n lastEventId === undefined ? headers : { ...headers, 'Last-Event-ID': lastEventId };\n try {\n const { response } = await send(\n config,\n op,\n url,\n { ...rest, method: rest.method ?? 'GET', headers: sendHeaders },\n undefined,\n false,\n {}\n );\n if (!response.ok) {\n const errorBody = await readError(response);\n throw new ApiError(url, response.status, response.statusText, errorBody);\n }\n failures = 0;\n const body = response.body;\n if (!body) return;\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n try {\n while (true) {\n const { done, value } = await reader.read();\n buffer += done ? decoder.decode() : decoder.decode(value, { stream: true });\n let match: RegExpMatchArray | null;\n while ((match = buffer.match(FRAME_DELIMITER)) !== null) {\n const index = match.index!;\n const raw = buffer.slice(0, index);\n buffer = buffer.slice(index + match[0].length);\n const event = parseSseFrame(raw, dataKind);\n if (event) {\n if (event.id !== undefined) lastEventId = event.id;\n if (event.retry !== undefined) serverRetry = event.retry;\n yield event as ServerSentEvent<T>;\n }\n }\n if (done) {\n // Stream closed cleanly. Flush a final event that arrived without a trailing\n // delimiter, then finish — a clean end is not a dropped connection, so do not reconnect.\n const event = buffer.length > 0 ? parseSseFrame(buffer, dataKind) : undefined;\n if (event) {\n if (event.id !== undefined) lastEventId = event.id;\n if (event.retry !== undefined) serverRetry = event.retry;\n yield event as ServerSentEvent<T>;\n }\n return;\n }\n // Bound memory: a server that never sends a frame delimiter would otherwise\n // grow `buffer` without limit. 1 MiB is far above any real SSE frame.\n if (buffer.length > 1048576) {\n throw new SseParseError('SSE frame exceeded 1048576 characters without a delimiter');\n }\n }\n } finally {\n await reader.cancel().catch(() => undefined);\n }\n } catch (error) {\n if (signal?.aborted) return;\n // A non-OK HTTP response (4xx/5xx) or an unparseable JSON payload is a definitive\n // error, not a transient drop — surface it instead of reconnecting in a loop (a\n // stable bad payload would otherwise reconnect forever).\n if (error instanceof ApiError || error instanceof SseParseError) throw error;\n // A transport failure (connect/DNS/reset) when opening the request, or a mid-stream\n // read error, is a dropped connection: fall through to backoff/reconnect when enabled.\n if (!reconnect) throw error;\n }\n // Only the swallowed-drop path reaches here: reconnect is on and the signal not aborted.\n failures++;\n const base = serverRetry ?? reconnectDelay ?? 1000;\n const delay = Math.min(base * Math.pow(2, failures - 1), 30_000);\n try {\n await sleep(Math.random() * delay, signal);\n } catch {\n return; // sleep rejects only on abort — end the iterator cleanly\n }\n }\n}\n\n/** Parse one raw SSE frame (its lines) into an event; returns undefined for comment-only frames. */\nexport function parseSseFrame(\n raw: string,\n dataKind: 'json' | 'text'\n): ServerSentEvent<unknown> | undefined {\n let event: string | undefined;\n const dataLines: string[] = [];\n let id: string | undefined;\n let retry: number | undefined;\n let sawField = false;\n for (const line of raw.split(/\\r\\n|\\n|\\r/)) {\n if (line === '' || line.startsWith(':')) continue;\n const colon = line.indexOf(':');\n const field = colon === -1 ? line : line.slice(0, colon);\n let val = colon === -1 ? '' : line.slice(colon + 1);\n if (val.startsWith(' ')) val = val.slice(1);\n sawField = true;\n if (field === 'event') event = val;\n else if (field === 'data') dataLines.push(val);\n else if (field === 'id') id = val;\n else if (field === 'retry') {\n const n = Number(val);\n if (!Number.isNaN(n)) retry = n;\n }\n }\n if (!sawField) return undefined;\n const dataText = dataLines.join('\\n');\n let data: unknown = dataText;\n if (dataKind === 'json' && dataText !== '') {\n try {\n data = JSON.parse(dataText);\n } catch (error) {\n throw new SseParseError(\n `Failed to parse SSE event data as JSON: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n return { event, data, id, retry };\n}\n",
13
+ 'create-client.ts': "import { ApiError } from './errors.js';\nimport { parse, readError } from './parse.js';\nimport { middlewareChain, send, toHeaderRecord, type SendCapabilities } from './send.js';\nimport type {\n ApiErrorLike,\n Client,\n ClientConfig,\n Middleware,\n OperationContext,\n OperationDescriptor,\n OpsShape,\n PaginationSpec,\n ParseAs,\n QueryValue,\n RequestOptions,\n SecuritySpec,\n ServerSentEvent,\n SseOptions,\n TokenProvider,\n} from './types.js';\nimport { buildUrl, substitutePath, type QueryStyle } from './url.js';\n\n/**\n * The optional behaviors `createClientCore` can dispatch to but never statically\n * imports. The package's public `createClient` wires the full set; the future\n * inline-mode assembler wires only the capabilities a spec needs.\n */\nexport type Capabilities = SendCapabilities & {\n resolveAuth?: (\n security: readonly (readonly SecuritySpec[])[],\n config: ClientConfig\n ) => Promise<{ headers: Record<string, string>; query: Record<string, string> }>;\n sse?: (\n config: ClientConfig,\n op: OperationContext,\n // Re-preparing per (re)connect (not a frozen url/init) lets a refresh-style\n // TokenProvider issue a fresh credential after a dropped stream reconnects.\n prepare: () => Promise<{ url: string; init: SseOptions }>,\n dataKind: 'json' | 'text'\n ) => AsyncGenerator<ServerSentEvent<unknown>>;\n paginate?: {\n pages: (\n call: (args?: OperationArgs, init?: RequestOptions) => Promise<unknown>,\n spec: PaginationSpec,\n args?: OperationArgs,\n init?: RequestOptions\n ) => AsyncGenerator<unknown>;\n items: (\n call: (args?: OperationArgs, init?: RequestOptions) => Promise<unknown>,\n spec: PaginationSpec,\n args?: OperationArgs,\n init?: RequestOptions\n ) => AsyncGenerator<unknown>;\n };\n};\n\n/** The grouped args wire shape: path params by name plus the `params`/`body`/`headers` slots. */\nexport type OperationArgs = {\n params?: Record<string, QueryValue>;\n body?: unknown;\n headers?: Record<string, unknown>;\n} & Record<string, unknown>;\n\n/** The response reader implied by the descriptor (before any per-call `parseAs` override). */\nfunction kindFor(op: OperationDescriptor): ParseAs | 'void' {\n if (op.responseKind === 'void' || op.responseKind === 'blob' || op.responseKind === 'text') {\n return op.responseKind;\n }\n return 'auto';\n}\n\n/** Route the grouped args by the descriptor: path values, query object, body, extra headers. */\nfunction splitArgs(op: OperationDescriptor, args: OperationArgs) {\n const path: Record<string, unknown> = {};\n for (const param of op.params ?? []) {\n if (param.in === 'path') path[param.name] = args[param.name];\n }\n return { path, query: args.params, body: args.body, headers: args.headers };\n}\n\n/**\n * The query-serialization hints for the descriptor's query params. A spec is built only\n * when the param deviates from the OpenAPI defaults (`form` + `explode: true`, encoded),\n * and always fully resolved — so `explode: false` or `allowReserved` alone (no `style`)\n * are honored, and an omitted `explode` keeps the exploded default.\n */\nfunction queryStyles(op: OperationDescriptor): Record<string, QueryStyle> | undefined {\n let styles: Record<string, QueryStyle> | undefined;\n for (const param of op.params ?? []) {\n if (param.in !== 'query') continue;\n const deviates =\n (param.style !== undefined && param.style !== 'form') ||\n param.explode === false ||\n param.allowReserved === true;\n if (!deviates) continue;\n styles ??= {};\n styles[param.name] = {\n style: param.style ?? 'form',\n explode: param.explode ?? true,\n allowReserved: param.allowReserved,\n };\n }\n return styles;\n}\n\n/** Stringify caller-supplied extra headers, skipping empty entries. */\nfunction stringHeaders(headers: Record<string, unknown> | undefined): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers ?? {})) {\n if (value !== undefined && value !== null) out[key] = String(value);\n }\n return out;\n}\n\n/** Build the request pieces an attempt needs: the final URL and the merged per-call init. */\nasync function prepareRequest(\n config: ClientConfig,\n op: OperationDescriptor,\n args: OperationArgs,\n init: RequestOptions | SseOptions,\n caps: Capabilities\n): Promise<{ url: string; init: RequestOptions; body: unknown }> {\n const { path, query, body, headers } = splitArgs(op, args);\n const authed =\n op.security?.length && caps.resolveAuth\n ? await caps.resolveAuth(op.security, config)\n : { headers: {}, query: {} };\n const fullQuery: Record<string, QueryValue> = { ...query, ...authed.query };\n const url = buildUrl(\n config.serverUrl ?? '',\n substitutePath(op.path, path),\n Object.keys(fullQuery).length > 0 ? fullQuery : undefined,\n queryStyles(op)\n );\n const mergedInit: RequestOptions = {\n ...init,\n method: op.method.toUpperCase(),\n // Precedence, lowest → highest (later spreads win): injected auth → explicit\n // header params → caller `init.headers` — the caller always overrides both.\n headers: {\n ...authed.headers,\n ...stringHeaders(headers),\n ...toHeaderRecord(init.headers),\n },\n };\n return { url, init: mergedInit, body };\n}\n\n/** One non-SSE call: send, then branch on the configured error mode. */\nasync function execute(\n config: ClientConfig,\n op: OperationDescriptor,\n args: OperationArgs,\n init: RequestOptions,\n caps: Capabilities\n): Promise<unknown> {\n const prepared = await prepareRequest(config, op, args, init, caps);\n const opCtx: OperationContext = { id: op.id, path: op.path, tags: [...(op.tags ?? [])] };\n const { parseAs, ...sendInit } = prepared.init;\n const { response, context } = await send(\n config,\n opCtx,\n prepared.url,\n sendInit,\n prepared.body,\n op.body?.multipart === true,\n caps\n );\n const readKind = parseAs ?? kindFor(op);\n if (config.errorMode === 'result') {\n if (!response.ok) {\n return { data: undefined, error: await readError(response), response };\n }\n return { data: await parse(response, readKind), error: undefined, response };\n }\n if (!response.ok) {\n let error: globalThis.Error = new ApiError(\n context.url,\n response.status,\n response.statusText,\n await readError(response)\n );\n // Thread the error through each middleware's onError in turn (each may replace it).\n for (const mw of middlewareChain(config)) {\n if (mw.onError) error = await mw.onError(error as ApiErrorLike, context);\n }\n throw error;\n }\n return parse(response, readKind);\n}\n\n/** The paginate capability, or a descriptive throw when a paginated op is iterated unwired. */\nfunction paginateCapability(caps: Capabilities, op: OperationDescriptor) {\n if (!caps.paginate) {\n throw new Error(`Pagination capability not wired: cannot iterate operation \"${op.id}\"`);\n }\n return caps.paginate;\n}\n\n/**\n * The per-page call the iterators drive: the method itself in throw mode; in result\n * mode a wrapper that unwraps the `{ data, error, response }` envelope — the page\n * pointers are data-rooted — rethrowing a failed page as `ApiError` (iteration is\n * error-mode-agnostic; the throw-mode-only `onError` middleware hook is not invoked).\n */\nfunction pageCall(\n method: (args?: OperationArgs, init?: RequestOptions) => Promise<unknown>,\n config: ClientConfig\n) {\n if (config.errorMode !== 'result') return method;\n return async (args?: OperationArgs, init?: RequestOptions) => {\n const envelope = (await method(args, init)) as {\n data: unknown;\n error: unknown;\n response: Response;\n };\n // Failure is `!response.ok` — NOT `data === undefined`: a successful bodyless page\n // (204/void) also parses to undefined data, and a failed page's `error` can be\n // undefined too (unreadable body). The pointers then miss on the undefined data\n // and iteration stops cleanly, which is the correct semantics for an empty page.\n if (!envelope.response.ok) {\n const { response } = envelope;\n throw new ApiError(response.url, response.status, response.statusText, envelope.error);\n }\n return envelope.data;\n };\n}\n\n/**\n * Build a typed instance client over operation descriptors: one real bound method per\n * operation (attached by a construction-time loop — no Proxy), plus the core members\n * (`configure`/`use`/`auth`), which are assigned AFTER the loop so they win any name\n * collision with an operation. All behavior dispatches through the capability seam.\n */\nexport function createClientCore<\n Ops extends OpsShape,\n Id extends string = string,\n Path extends string = string,\n Tag extends string = string,\n>(\n operations: Record<string, OperationDescriptor>,\n initial: ClientConfig<OperationContext<Id, Path, Tag>> = {},\n caps: Capabilities = {}\n): Client<Ops, OperationContext<Id, Path, Tag>> {\n // The literal-union narrowing is a compile-time DX contract only; internally the\n // runtime works with the base (string-typed) context. One cast at this boundary —\n // `ClientConfig<Narrow>` is not assignable to `ClientConfig` (middleware ctx\n // params are contravariant).\n const given = initial as ClientConfig;\n // Private mutable config; the middleware array is copied so `use()` never mutates the caller's.\n const config: ClientConfig = { ...given, middleware: [...(given.middleware ?? [])] };\n const client = {} as Record<string, unknown>;\n\n for (const [name, op] of Object.entries(operations)) {\n if (op.responseKind === 'sse') {\n client[name] = (args: OperationArgs = {}, init: SseOptions = {}) => {\n if (!caps.sse) {\n throw new Error(`SSE capability not wired: cannot stream operation \"${op.id}\"`);\n }\n const stream = caps.sse;\n return (async function* () {\n const opCtx: OperationContext = { id: op.id, path: op.path, tags: [...(op.tags ?? [])] };\n // A thunk the stream re-runs on every (re)connect, so auth (which `prepareRequest`\n // resolves) is refreshed per attempt rather than frozen at the first connect.\n const prepare = async () => {\n const prepared = await prepareRequest(config, op, args, init, caps);\n return { url: prepared.url, init: prepared.init as SseOptions };\n };\n yield* stream(config, opCtx, prepare, op.sseDataKind ?? 'text');\n })();\n };\n } else {\n const method = (args: OperationArgs = {}, init: RequestOptions = {}) =>\n execute(config, op, args, init, caps);\n const spec = op.pagination;\n // Paginated ops keep their one-shot call and gain `.pages`/`.items`, dispatching\n // through the capability seam (like SSE: absent capability throws descriptively).\n // Iteration is error-mode-agnostic: the iterators' pointers are data-rooted, so on\n // a result-mode client (`errorMode` is fixed at construction — `configure()`\n // ignores it) each page's envelope is unwrapped before it reaches the capability.\n // A failed page aborts iteration by throwing ApiError, even on result-mode\n // clients; the `onError` middleware hook (throw-mode-only) is not invoked.\n client[name] = spec\n ? Object.assign(method, {\n pages: (args?: OperationArgs, init?: RequestOptions) =>\n paginateCapability(caps, op).pages(pageCall(method, config), spec, args, init),\n items: (args?: OperationArgs, init?: RequestOptions) =>\n paginateCapability(caps, op).items(pageCall(method, config), spec, args, init),\n })\n : method;\n }\n }\n\n // Core members are assigned AFTER the operation loop — they win over colliding op names.\n client.configure = (next: ClientConfig): void => {\n // `errorMode` is fixed at generate time (it shapes the static types); flipping it at\n // runtime would silently desync return shapes from `Client<Ops>`, so it is ignored.\n const { errorMode: _fixed, auth, ...rest } = next;\n Object.assign(config, rest);\n // `auth` merges into existing credentials (like the `auth.*` setters) rather than\n // replacing wholesale — so `configure({ auth: { bearer } })` keeps a previously set\n // basic/apiKey. `apiKey` merges per scheme.\n if (auth) {\n config.auth = {\n ...config.auth,\n ...auth,\n ...(auth.apiKey ? { apiKey: { ...config.auth?.apiKey, ...auth.apiKey } } : {}),\n };\n }\n };\n client.use = (...middleware: Middleware[]): void => {\n // Reassign (don't push) so a caller-provided `middleware` array isn't mutated.\n config.middleware = [...(config.middleware ?? []), ...middleware];\n };\n client.auth = {\n bearer(token: TokenProvider): void {\n config.auth = { ...config.auth, bearer: token };\n },\n basic(username: string, password: string): void {\n config.auth = { ...config.auth, basic: { username, password } };\n },\n apiKey(scheme: string, value: TokenProvider): void {\n config.auth = { ...config.auth, apiKey: { ...config.auth?.apiKey, [scheme]: value } };\n },\n };\n\n return client as Client<Ops, OperationContext<Id, Path, Tag>>;\n}\n",
14
+ 'paginate.ts': "import type { OperationArgs } from './create-client.js';\nimport type { PaginationSpec, QueryValue, RequestOptions } from './types.js';\n\n/**\n * Auto-pagination (capability module — wired into `createClient`, dispatched by the\n * method's `.pages()`/`.items()`): walk an operation's pages by advancing the descriptor's\n * `param` query parameter, per its `style`. The caller's args are never mutated — each\n * request gets a fresh `params` clone — and `init` is forwarded to every call.\n *\n * Iteration is error-mode-agnostic: `call` always resolves to the RAW page (on a\n * result-mode client the attachment unwraps the envelope first), so a failed page\n * aborts iteration by throwing `ApiError`, even on result-mode clients; the `onError`\n * middleware hook (throw-mode-only) is not invoked.\n */\n\n/**\n * Resolve an RFC 6901 JSON pointer (`~1` → `/`, `~0` → `~`) against a value.\n * The empty pointer is the whole document; anything else must start with `/`.\n * Returns `undefined` on any miss (bad token, absent key, non-object step) — never throws.\n */\nexport function resolvePointer(value: unknown, pointer: string): unknown {\n if (pointer === '') return value;\n if (!pointer.startsWith('/')) return undefined;\n let current = value;\n for (const token of pointer.slice(1).split('/')) {\n const key = token.replaceAll('~1', '/').replaceAll('~0', '~');\n if (Array.isArray(current)) {\n if (!/^(0|[1-9]\\d*)$/.test(key)) return undefined;\n current = current[Number(key)];\n } else if (Object(current) === current && key in (current as object)) {\n current = (current as Record<string, unknown>)[key];\n } else {\n return undefined;\n }\n }\n return current;\n}\n\n/**\n * Iterate an operation's full page results. Every page is yielded before the stop\n * condition is evaluated, so the last page always arrives. Cursor style resumes from a\n * caller-provided `params[spec.param]`, stops when `nextCursor` resolves to\n * `undefined`/`null`/`''`, and throws if the next cursor is not a string or number, or\n * if the same cursor comes back twice in a row (infinite-loop guards). Offset/page\n * styles advance by item count / by one and stop when\n * the `items` pointer misses or the array is empty.\n */\nexport async function* pages<TPage>(\n call: (args?: OperationArgs, init?: RequestOptions) => Promise<TPage>,\n spec: PaginationSpec,\n args: OperationArgs = {},\n init?: RequestOptions\n): AsyncGenerator<TPage> {\n if (spec.style === 'cursor') {\n let cursor: unknown = args.params?.[spec.param];\n while (true) {\n const params = { ...args.params };\n if (cursor !== undefined) params[spec.param] = cursor as QueryValue;\n const page = await call({ ...args, params }, init);\n yield page;\n const next = resolvePointer(page, spec.nextCursor!);\n if (next === undefined || next === null || next === '') return;\n if (typeof next !== 'string' && typeof next !== 'number') {\n // A fresh non-scalar cursor never compares equal, so without this guard a lying\n // server would slip past the did-not-advance check into an infinite loop.\n throw new Error(`Pagination cursor at ${spec.nextCursor} is not a string or number`);\n }\n if (next === cursor) {\n throw new Error('Pagination did not advance: operation returned the same cursor twice');\n }\n cursor = next;\n }\n } else {\n // Coerce the starting position to a number: a caller may pass `params[spec.param]` as a\n // string (common from URL/form input), and `+=` on a string would concatenate.\n const start = args.params?.[spec.param];\n const fallback = spec.style === 'page' ? 1 : 0;\n let position = start === undefined || Number.isNaN(Number(start)) ? fallback : Number(start);\n while (true) {\n const page = await call(\n { ...args, params: { ...args.params, [spec.param]: position } },\n init\n );\n yield page;\n const pageItems = resolvePointer(page, spec.items);\n if (!Array.isArray(pageItems) || pageItems.length === 0) return;\n position += spec.style === 'page' ? 1 : pageItems.length;\n }\n }\n}\n\n/**\n * Iterate the operation's individual items: each page's `items` pointer, flattened.\n * A cursor-style page whose pointer misses yields nothing but pagination continues;\n * for offset/page styles a miss has already stopped `pages`.\n */\nexport async function* items<TItem>(\n call: (args?: OperationArgs, init?: RequestOptions) => Promise<unknown>,\n spec: PaginationSpec,\n args?: OperationArgs,\n init?: RequestOptions\n): AsyncGenerator<TItem> {\n for await (const page of pages(call, spec, args, init)) {\n const pageItems = resolvePointer(page, spec.items);\n if (Array.isArray(pageItems)) yield* pageItems as TItem[];\n }\n}\n",
15
+ };
16
+ //# sourceMappingURL=runtime-sources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-sources.js","sourceRoot":"","sources":["../../src/emitters/runtime-sources.ts"],"names":[],"mappings":"AAAA,iKAAiK;AACjK,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,UAAU,EACR,0wVAA0wV;IAC5wV,WAAW,EACT,0iCAA0iC;IAC5iC,QAAQ,EACN,8wIAA8wI;IAChxI,UAAU,EACR,y8CAAy8C;IAC38C,UAAU,EACR,kgEAAkgE;IACpgE,cAAc,EACZ,yjCAAyjC;IAC3jC,SAAS,EACP,ujGAAujG;IACzjG,UAAU,EACR,6kBAA6kB;IAC/kB,SAAS,EACP,+kMAA+kM;IACjlM,QAAQ,EACN,41NAA41N;IAC91N,kBAAkB,EAChB,oqaAAoqa;IACtqa,aAAa,EACX,iwJAAiwJ;CAC3vJ,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { NamedSchemaModel, SchemaModel } from '../intermediate-representation/model.js';
2
+ import type { DateType } from './types.js';
3
+ /** A sampled value the emitter must print as a raw TS expression rather than a JSON
4
+ * literal — e.g. a `format: binary` field, whose generated type is `Blob`. The `code`
5
+ * strings are generator-authored constants (never spec-derived), so emitting them
6
+ * verbatim is safe. */
7
+ export declare class SampleExpression {
8
+ readonly code: string;
9
+ constructor(code: string);
10
+ }
11
+ /** Options for `sampleValue`. `dateType` mirrors the type emitter's `--date-type`
12
+ * knob: when `'Date'`, date/date-time fields are typed `Date` (not `string`), so
13
+ * the sample must be a `new Date(...)` expression to match. Defaults to `'string'`. */
14
+ export type SampleOptions = {
15
+ dateType?: DateType;
16
+ };
17
+ /**
18
+ * Turn an IR schema into a single deterministic JS value for baked mocks. Prefers
19
+ * a spec `example`, then `default`, then a type/format-aware synthesized value —
20
+ * EXCEPT for type-demanding fields (binary, or date/date-time under `dateType: 'Date'`)
21
+ * whose generated type is not a JSON literal: those emit a fixed raw expression that
22
+ * matches the type, regardless of any example/default (see `typeDemandedExpression`).
23
+ * Refs resolve against `schemas`; recursion is cut with a visited-set so cyclic
24
+ * schemas terminate (`null` at the cycle) instead of looping forever.
25
+ */
26
+ export declare function sampleValue(schema: SchemaModel, schemas: NamedSchemaModel[], opts?: SampleOptions): unknown;
27
+ //# sourceMappingURL=sample.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sample.d.ts","sourceRoot":"","sources":["../../src/emitters/sample.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAGhB,WAAW,EACZ,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;wBAGwB;AACxB,qBAAa,gBAAgB;IACf,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM;CAClC;AAED;;wFAEwF;AACxF,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,CAAC,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEpD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,gBAAgB,EAAE,EAC3B,IAAI,GAAE,aAAkB,GACvB,OAAO,CAMT"}
@@ -0,0 +1,161 @@
1
+ import { isPlainObject } from '@redocly/openapi-core';
2
+ /** A sampled value the emitter must print as a raw TS expression rather than a JSON
3
+ * literal — e.g. a `format: binary` field, whose generated type is `Blob`. The `code`
4
+ * strings are generator-authored constants (never spec-derived), so emitting them
5
+ * verbatim is safe. */
6
+ export class SampleExpression {
7
+ code;
8
+ constructor(code) {
9
+ this.code = code;
10
+ }
11
+ }
12
+ /**
13
+ * Turn an IR schema into a single deterministic JS value for baked mocks. Prefers
14
+ * a spec `example`, then `default`, then a type/format-aware synthesized value —
15
+ * EXCEPT for type-demanding fields (binary, or date/date-time under `dateType: 'Date'`)
16
+ * whose generated type is not a JSON literal: those emit a fixed raw expression that
17
+ * matches the type, regardless of any example/default (see `typeDemandedExpression`).
18
+ * Refs resolve against `schemas`; recursion is cut with a visited-set so cyclic
19
+ * schemas terminate (`null` at the cycle) instead of looping forever.
20
+ */
21
+ export function sampleValue(schema, schemas, opts = {}) {
22
+ const byName = new Map(schemas.map((s) => [s.name, s.schema]));
23
+ const value = walk(schema, byName, new Set(), opts.dateType ?? 'string');
24
+ // A `CYCLE` that propagates all the way to the root has no container to absorb it
25
+ // into a valid empty value (e.g. a self-referential union); fall back to null.
26
+ return value === CYCLE ? null : value;
27
+ }
28
+ /**
29
+ * Sentinel returned by `walk` when a `$ref` re-enters a name already on the stack.
30
+ * Containers turn it into the type-correct empty value for their position — an array
31
+ * to `[]`, a record to `{}`, an optional property to omission — so a recursive schema
32
+ * yields data that still satisfies its (non-nullable) generated type. Only a required,
33
+ * non-container self-reference (a genuinely uninhabitable schema) degrades to null.
34
+ */
35
+ const CYCLE = Symbol('cycle');
36
+ /**
37
+ * The expression a field MUST sample to because its generated type is not a JSON
38
+ * literal — so it takes precedence over any spec `example`/`default` (baking those
39
+ * would not type-check). The returned `code` is a generator-authored CONSTANT and
40
+ * is NEVER interpolated from spec data: it flows verbatim into `parseExpression`,
41
+ * so feeding a spec example string in here would be an injection vector. `undefined`
42
+ * when the field has no type demand (the normal example/default/synthesis path applies).
43
+ */
44
+ function typeDemandedExpression(schema, dateType) {
45
+ if (schema.kind !== 'scalar')
46
+ return undefined;
47
+ const format = schema.metadata?.format;
48
+ if (format === 'binary')
49
+ return new SampleExpression('new Blob([])');
50
+ if (dateType === 'Date') {
51
+ if (format === 'date-time')
52
+ return new SampleExpression('new Date("2024-01-01T00:00:00Z")');
53
+ if (format === 'date')
54
+ return new SampleExpression('new Date("2024-01-01")');
55
+ }
56
+ return undefined;
57
+ }
58
+ function walk(schema, byName, visiting, dateType) {
59
+ const demanded = typeDemandedExpression(schema, dateType);
60
+ if (demanded)
61
+ return demanded;
62
+ const meta = schema.metadata;
63
+ if (meta?.example !== undefined)
64
+ return meta.example;
65
+ if (meta?.default !== undefined)
66
+ return meta.default;
67
+ switch (schema.kind) {
68
+ case 'scalar':
69
+ return scalarSample(schema.scalar, meta);
70
+ case 'array': {
71
+ // A cyclic item type collapses the array to `[]` — itself a valid `T[]`.
72
+ const item = walk(schema.items, byName, visiting, dateType);
73
+ return item === CYCLE ? [] : [item];
74
+ }
75
+ case 'object':
76
+ return Object.fromEntries(schema.properties.flatMap((p) => {
77
+ const value = walk(p.schema, byName, visiting, dateType);
78
+ // A cyclic optional property is omitted (a null would not satisfy `T | undefined`);
79
+ // a cyclic required property is uninhabitable, so null is the only stand-in.
80
+ if (value === CYCLE)
81
+ return p.required ? [[p.name, null]] : [];
82
+ return [[p.name, value]];
83
+ }));
84
+ case 'record': {
85
+ const value = walk(schema.value, byName, visiting, dateType);
86
+ return value === CYCLE ? {} : { key: value };
87
+ }
88
+ case 'enum':
89
+ return schema.values[0];
90
+ case 'literal':
91
+ return schema.value;
92
+ case 'union': {
93
+ // Pick the first member that is not itself a cycle; if every member cycles the
94
+ // union is uninhabitable, so propagate `CYCLE` for a container/root to absorb.
95
+ for (const member of schema.members) {
96
+ const value = walk(member, byName, visiting, dateType);
97
+ if (value !== CYCLE)
98
+ return value;
99
+ }
100
+ return schema.members.length > 0 ? CYCLE : null;
101
+ }
102
+ case 'intersection':
103
+ return schema.members.reduce((acc, m) => {
104
+ const part = walk(m, byName, visiting, dateType);
105
+ return isPlainObject(part) ? Object.assign(acc, part) : acc;
106
+ }, {});
107
+ case 'omit': {
108
+ // base is a named schema reference (string), resolve it then drop the listed keys
109
+ const target = byName.get(schema.base);
110
+ if (!target)
111
+ return null;
112
+ const base = walk(target, byName, visiting, dateType);
113
+ if (isPlainObject(base)) {
114
+ const copy = { ...base };
115
+ for (const key of schema.keys)
116
+ delete copy[key];
117
+ return copy;
118
+ }
119
+ return base;
120
+ }
121
+ case 'ref': {
122
+ if (visiting.has(schema.name))
123
+ return CYCLE;
124
+ const target = byName.get(schema.name);
125
+ if (!target)
126
+ return null;
127
+ visiting.add(schema.name);
128
+ const result = walk(target, byName, visiting, dateType);
129
+ visiting.delete(schema.name);
130
+ return result;
131
+ }
132
+ case 'null':
133
+ case 'unknown':
134
+ return null;
135
+ }
136
+ }
137
+ function scalarSample(scalar, meta) {
138
+ if (scalar === 'boolean')
139
+ return true;
140
+ if (scalar === 'integer' || scalar === 'number')
141
+ return 0;
142
+ // Type-demanding formats (binary, and date/date-time under `dateType: 'Date'`) are
143
+ // handled earlier in `walk` via `typeDemandedExpression`; here the date formats fall
144
+ // through to the ISO-string path (the `dateType: 'string'` types them as `string`).
145
+ switch (meta?.format) {
146
+ case 'email':
147
+ return 'user@example.com';
148
+ case 'uuid':
149
+ return '00000000-0000-4000-8000-000000000000';
150
+ case 'date-time':
151
+ return '2024-01-01T00:00:00Z';
152
+ case 'date':
153
+ return '2024-01-01';
154
+ case 'uri':
155
+ case 'url':
156
+ return 'https://example.com';
157
+ default:
158
+ return 'string';
159
+ }
160
+ }
161
+ //# sourceMappingURL=sample.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sample.js","sourceRoot":"","sources":["../../src/emitters/sample.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAUtD;;;wBAGwB;AACxB,MAAM,OAAO,gBAAgB;IACN;IAArB,YAAqB,IAAY;QAAZ,SAAI,GAAJ,IAAI,CAAQ;IAAG,CAAC;CACtC;AAOD;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CACzB,MAAmB,EACnB,OAA2B,EAC3B,OAAsB,EAAE;IAExB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAG,EAAE,EAAE,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,CAAC;IACzE,kFAAkF;IAClF,+EAA+E;IAC/E,OAAO,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;AACxC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;AAE9B;;;;;;;GAOG;AACH,SAAS,sBAAsB,CAC7B,MAAmB,EACnB,QAAkB;IAElB,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC/C,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC;IACvC,IAAI,MAAM,KAAK,QAAQ;QAAE,OAAO,IAAI,gBAAgB,CAAC,cAAc,CAAC,CAAC;IACrE,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QACxB,IAAI,MAAM,KAAK,WAAW;YAAE,OAAO,IAAI,gBAAgB,CAAC,kCAAkC,CAAC,CAAC;QAC5F,IAAI,MAAM,KAAK,MAAM;YAAE,OAAO,IAAI,gBAAgB,CAAC,wBAAwB,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,IAAI,CACX,MAAmB,EACnB,MAAgC,EAChC,QAAqB,EACrB,QAAkB;IAElB,MAAM,QAAQ,GAAG,sBAAsB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC1D,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC7B,IAAI,IAAI,EAAE,OAAO,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC;IACrD,IAAI,IAAI,EAAE,OAAO,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC;IAErD,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,QAAQ;YACX,OAAO,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC3C,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,yEAAyE;YACzE,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC5D,OAAO,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;QACD,KAAK,QAAQ;YACX,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;gBACzD,oFAAoF;gBACpF,6EAA6E;gBAC7E,IAAI,KAAK,KAAK,KAAK;oBAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC/D,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;YAC3B,CAAC,CAAC,CACH,CAAC;QACJ,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC7D,OAAO,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;QAC/C,CAAC;QACD,KAAK,MAAM;YACT,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC1B,KAAK,SAAS;YACZ,OAAO,MAAM,CAAC,KAAK,CAAC;QACtB,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,+EAA+E;YAC/E,+EAA+E;YAC/E,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;gBACvD,IAAI,KAAK,KAAK,KAAK;oBAAE,OAAO,KAAK,CAAC;YACpC,CAAC;YACD,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;QAClD,CAAC;QACD,KAAK,cAAc;YACjB,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAA0B,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;gBAC/D,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;gBACjD,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YAC9D,CAAC,EAAE,EAAE,CAAC,CAAC;QACT,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,kFAAkF;YAClF,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACvC,IAAI,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAC;YACzB,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACtD,IAAI,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxB,MAAM,IAAI,GAAG,EAAE,GAAI,IAAgC,EAAE,CAAC;gBACtD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI;oBAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;gBAChD,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;gBAAE,OAAO,KAAK,CAAC;YAC5C,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACvC,IAAI,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAC;YACzB,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACxD,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC7B,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,KAAK,MAAM,CAAC;QACZ,KAAK,SAAS;YACZ,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,MAAkB,EAAE,IAAgC;IACxE,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACtC,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC;IAC1D,mFAAmF;IACnF,qFAAqF;IACrF,oFAAoF;IACpF,QAAQ,IAAI,EAAE,MAAM,EAAE,CAAC;QACrB,KAAK,OAAO;YACV,OAAO,kBAAkB,CAAC;QAC5B,KAAK,MAAM;YACT,OAAO,sCAAsC,CAAC;QAChD,KAAK,WAAW;YACd,OAAO,sBAAsB,CAAC;QAChC,KAAK,MAAM;YACT,OAAO,YAAY,CAAC;QACtB,KAAK,KAAK,CAAC;QACX,KAAK,KAAK;YACR,OAAO,qBAAqB,CAAC;QAC/B;YACE,OAAO,QAAQ,CAAC;IACpB,CAAC;AACH,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Transform a publisher `--setup` module into the neutral **setup expression** the emitter splices
3
+ * into a client as `const __redoclySetup = <expr>` — merged into the client's config via
4
+ * `mergeSetup`. Strips the (package-only) imports, extracts the default
5
+ * export's `defineClientSetup({ config, middleware })` argument, and — when the file declares
6
+ * helpers — wraps them in an IIFE so they are preserved yet scoped (only `__redoclySetup` lands in
7
+ * module scope, avoiding collisions):
8
+ *
9
+ * (() => { <file helpers…> return <arg>; })()
10
+ *
11
+ * The transform is textual (`node.getText()`): re-printing parsed nodes through the TS printer
12
+ * loses string-literal text, so we preserve the publisher's exact source for each kept node.
13
+ */
14
+ export declare function bakeSetup(source: string): string;
15
+ //# sourceMappingURL=setup-bake.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-bake.d.ts","sourceRoot":"","sources":["../../src/emitters/setup-bake.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAgDhD"}
@@ -0,0 +1,65 @@
1
+ import { NotSupportedError } from '../errors.js';
2
+ import { ts } from './ts.js';
3
+ const SETUP_IMPORT = '@redocly/client-generator';
4
+ /**
5
+ * Transform a publisher `--setup` module into the neutral **setup expression** the emitter splices
6
+ * into a client as `const __redoclySetup = <expr>` — merged into the client's config via
7
+ * `mergeSetup`. Strips the (package-only) imports, extracts the default
8
+ * export's `defineClientSetup({ config, middleware })` argument, and — when the file declares
9
+ * helpers — wraps them in an IIFE so they are preserved yet scoped (only `__redoclySetup` lands in
10
+ * module scope, avoiding collisions):
11
+ *
12
+ * (() => { <file helpers…> return <arg>; })()
13
+ *
14
+ * The transform is textual (`node.getText()`): re-printing parsed nodes through the TS printer
15
+ * loses string-literal text, so we preserve the publisher's exact source for each kept node.
16
+ */
17
+ export function bakeSetup(source) {
18
+ const sf = ts.createSourceFile('setup.ts', source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
19
+ const kept = [];
20
+ let argText;
21
+ // The local name `defineClientSetup` is imported as (it may be aliased:
22
+ // `import { defineClientSetup as setup }`), so `unwrap` matches the right callee.
23
+ let defineClientSetupLocal = 'defineClientSetup';
24
+ for (const stmt of sf.statements) {
25
+ if (ts.isImportDeclaration(stmt)) {
26
+ const spec = stmt.moduleSpecifier.text;
27
+ if (spec !== SETUP_IMPORT) {
28
+ throw new NotSupportedError(`A --setup file may only import from "${SETUP_IMPORT}"; found "${spec}". ` +
29
+ `Setup code may use web globals and the client contract only (keeps the client zero-dependency).`);
30
+ }
31
+ const named = stmt.importClause?.namedBindings;
32
+ if (named && ts.isNamedImports(named)) {
33
+ for (const el of named.elements) {
34
+ if ((el.propertyName?.text ?? el.name.text) === 'defineClientSetup') {
35
+ defineClientSetupLocal = el.name.text;
36
+ }
37
+ }
38
+ }
39
+ continue; // drop — the symbols resolve in the baked client's own scope
40
+ }
41
+ if (ts.isExportAssignment(stmt) && !stmt.isExportEquals) {
42
+ argText = unwrap(stmt.expression, defineClientSetupLocal).getText(sf);
43
+ continue;
44
+ }
45
+ kept.push(stmt.getText(sf));
46
+ }
47
+ if (argText === undefined) {
48
+ throw new NotSupportedError('A --setup file must `export default defineClientSetup({ config, middleware })` (or a setup object).');
49
+ }
50
+ if (kept.length === 0)
51
+ return argText;
52
+ return `(() => {\n${kept.join('\n')}\nreturn ${argText};\n})()`;
53
+ }
54
+ /** `defineClientSetup(<arg>)` → `<arg>`; a bare object literal passes through unchanged.
55
+ * `localName` is the (possibly aliased) local binding the setup module imported it as. */
56
+ function unwrap(expr, localName) {
57
+ if (ts.isCallExpression(expr) &&
58
+ ts.isIdentifier(expr.expression) &&
59
+ expr.expression.text === localName &&
60
+ expr.arguments.length === 1) {
61
+ return expr.arguments[0];
62
+ }
63
+ return expr;
64
+ }
65
+ //# sourceMappingURL=setup-bake.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-bake.js","sourceRoot":"","sources":["../../src/emitters/setup-bake.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAE7B,MAAM,YAAY,GAAG,2BAA2B,CAAC;AAEjD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,MAAM,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAC5B,UAAU,EACV,MAAM,EACN,EAAE,CAAC,YAAY,CAAC,MAAM,EACtB,IAAI,EACJ,EAAE,CAAC,UAAU,CAAC,EAAE,CACjB,CAAC;IACF,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,IAAI,OAA2B,CAAC;IAChC,wEAAwE;IACxE,kFAAkF;IAClF,IAAI,sBAAsB,GAAG,mBAAmB,CAAC;IAEjD,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,UAAU,EAAE,CAAC;QACjC,IAAI,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,GAAI,IAAI,CAAC,eAAoC,CAAC,IAAI,CAAC;YAC7D,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;gBAC1B,MAAM,IAAI,iBAAiB,CACzB,wCAAwC,YAAY,aAAa,IAAI,KAAK;oBACxE,iGAAiG,CACpG,CAAC;YACJ,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,EAAE,aAAa,CAAC;YAC/C,IAAI,KAAK,IAAI,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;gBACtC,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;oBAChC,IAAI,CAAC,EAAE,CAAC,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,mBAAmB,EAAE,CAAC;wBACpE,sBAAsB,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;oBACxC,CAAC;gBACH,CAAC;YACH,CAAC;YACD,SAAS,CAAC,6DAA6D;QACzE,CAAC;QACD,IAAI,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YACxD,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACtE,SAAS;QACX,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9B,CAAC;IAED,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,iBAAiB,CACzB,qGAAqG,CACtG,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IACtC,OAAO,aAAa,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,OAAO,SAAS,CAAC;AAClE,CAAC;AAED;0FAC0F;AAC1F,SAAS,MAAM,CAAC,IAAmB,EAAE,SAAiB;IACpD,IACE,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC;QACzB,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC;QAChC,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,SAAS;QAClC,IAAI,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAC3B,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}