@prover-coder-ai/openapi-effect 1.0.19 → 1.0.21

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.
@@ -1,517 +1,110 @@
1
- // CHANGE: Type-safe createClient API with full request-side enforcement
2
- // WHY: Ensure path/method → operation → request types are all linked
3
- // QUOTE(ТЗ): "path + method определяют operation, и из неё выводятся request/response types"
4
- // REF: PR#3 blocking review sections 3.2, 3.3
5
- // SOURCE: n/a
6
- // PURITY: SHELL
7
- // EFFECT: Creates Effect-based API client
8
- // INVARIANT: All operations are type-safe from path → operation → request → response
9
- // COMPLEXITY: O(1) client creation
1
+ import type { MediaType } from "openapi-typescript-helpers"
10
2
 
11
- import type * as HttpClient from "@effect/platform/HttpClient"
12
- import { Effect } from "effect"
13
- import type { HttpMethod } from "openapi-typescript-helpers"
14
-
15
- import { asDispatchersFor, asStrictApiClient, asStrictRequestInit, type Dispatcher } from "../../core/axioms.js"
16
- import type {
17
- ClientEffect,
18
- ClientOptions,
19
- DispatchersFor,
20
- DispatchersForMethod,
21
- StrictApiClientWithDispatchers
22
- } from "./create-client-types.js"
23
- import type { StrictRequestInit } from "./strict-client.js"
24
- import { createUniversalDispatcher, executeRequest } from "./strict-client.js"
3
+ import { asStrictApiClient } from "../../core/axioms.js"
4
+ import type { RuntimeClient, RuntimeFetchOptions } from "./create-client-runtime-types.js"
5
+ import { createRuntimeClient } from "./create-client-runtime.js"
6
+ import type { Client, ClientEffect, ClientOptions, DispatchersFor, PathBasedClient } from "./create-client-types.js"
25
7
 
26
8
  export type {
9
+ Client,
27
10
  ClientEffect,
11
+ ClientForPath,
12
+ ClientMethod,
28
13
  ClientOptions,
14
+ ClientPathsWithMethod,
15
+ ClientRequestMethod,
29
16
  DispatchersFor,
17
+ FetchOptions,
18
+ FetchResponse,
19
+ HeadersOptions,
20
+ MethodResponse,
21
+ Middleware,
22
+ MiddlewareCallbackParams,
23
+ ParseAs,
24
+ PathBasedClient,
25
+ QuerySerializer,
26
+ QuerySerializerOptions,
27
+ RequestBodyOption,
28
+ RequestOptions,
30
29
  StrictApiClient,
31
30
  StrictApiClientWithDispatchers
32
31
  } from "./create-client-types.js"
33
- export { createUniversalDispatcher } from "./strict-client.js"
34
-
35
- /**
36
- * Primitive value type for path/query parameters
37
- *
38
- * @pure true - type alias only
39
- */
40
- type ParamValue = string | number | boolean
41
-
42
- /**
43
- * Query parameter value - can be primitive or array of primitives
44
- *
45
- * @pure true - type alias only
46
- */
47
- type QueryValue = ParamValue | ReadonlyArray<ParamValue>
48
-
49
- // CHANGE: Add default dispatcher registry for auto-dispatching createClient
50
- // WHY: Allow createClient(options) without explicitly passing dispatcher map
51
- // QUOTE(ТЗ): "const apiClient = createClient<Paths>(clientOptions)"
52
- // REF: user-msg-4
53
- // SOURCE: n/a
54
- // FORMAT THEOREM: ∀ call: defaultDispatchers = dispatchersByPath ⇒ createClient uses dispatcher(path, method)
55
- // PURITY: SHELL
56
- // EFFECT: none
57
- // INVARIANT: defaultDispatchers is set before createClient use
58
- // COMPLEXITY: O(1)
59
- let defaultDispatchers: DispatchersFor<object> | undefined
60
-
61
- /**
62
- * Register default dispatcher map used by createClient(options)
63
- *
64
- * @pure false - mutates module-level registry
65
- * @invariant defaultDispatchers set exactly once per app boot
66
- */
67
- export const registerDefaultDispatchers = <Paths extends object>(
68
- dispatchers: DispatchersFor<Paths>
69
- ): void => {
70
- defaultDispatchers = dispatchers
71
- }
72
-
73
- /**
74
- * Resolve default dispatcher map or fail fast
75
- *
76
- * @pure false - reads module-level registry
77
- * @invariant defaultDispatchers must be set for auto-dispatching client
78
- */
79
- const resolveDefaultDispatchers = <Paths extends object>(): DispatchersFor<Paths> => {
80
- if (defaultDispatchers === undefined) {
81
- throw new Error("Default dispatchers are not registered. Import generated dispatchers module.")
82
- }
83
- return asDispatchersFor<DispatchersFor<Paths>>(defaultDispatchers)
84
- }
85
-
86
- const applyPathParams = (path: string, params?: Record<string, ParamValue>): string => {
87
- if (params === undefined) {
88
- return path
89
- }
90
32
 
91
- let url = path
92
- for (const [key, value] of Object.entries(params)) {
93
- url = url.replace("{" + key + "}", encodeURIComponent(String(value)))
94
- }
95
- return url
33
+ export {
34
+ createFinalURL,
35
+ createQuerySerializer,
36
+ defaultBodySerializer,
37
+ defaultPathSerializer,
38
+ mergeHeaders,
39
+ removeTrailingSlash,
40
+ serializeArrayParam,
41
+ serializeObjectParam,
42
+ serializePrimitiveParam
43
+ } from "./openapi-compat-utils.js"
44
+
45
+ export const createClient = <Paths extends object, Media extends MediaType = MediaType>(
46
+ clientOptions?: ClientOptions
47
+ ): Client<Paths, Media> => asStrictApiClient<Client<Paths, Media>>(createRuntimeClient(clientOptions))
48
+
49
+ class PathCallForwarder {
50
+ constructor(
51
+ private readonly client: RuntimeClient,
52
+ private readonly url: string
53
+ ) {}
54
+
55
+ private readonly call = (
56
+ method: "GET" | "PUT" | "POST" | "DELETE" | "OPTIONS" | "HEAD" | "PATCH" | "TRACE",
57
+ init?: RuntimeFetchOptions
58
+ ) => this.client[method](this.url, init)
59
+
60
+ public readonly GET = (init?: RuntimeFetchOptions) => this.call("GET", init)
61
+ public readonly PUT = (init?: RuntimeFetchOptions) => this.call("PUT", init)
62
+ public readonly POST = (init?: RuntimeFetchOptions) => this.call("POST", init)
63
+ public readonly DELETE = (init?: RuntimeFetchOptions) => this.call("DELETE", init)
64
+ public readonly OPTIONS = (init?: RuntimeFetchOptions) => this.call("OPTIONS", init)
65
+ public readonly HEAD = (init?: RuntimeFetchOptions) => this.call("HEAD", init)
66
+ public readonly PATCH = (init?: RuntimeFetchOptions) => this.call("PATCH", init)
67
+ public readonly TRACE = (init?: RuntimeFetchOptions) => this.call("TRACE", init)
96
68
  }
97
69
 
98
- const buildQueryString = (query?: Record<string, QueryValue>): string => {
99
- if (query === undefined) {
100
- return ""
101
- }
102
-
103
- const searchParams = new URLSearchParams()
104
- for (const [key, value] of Object.entries(query)) {
105
- if (Array.isArray(value)) {
106
- for (const item of value) {
107
- searchParams.append(key, String(item))
70
+ export const wrapAsPathBasedClient = <
71
+ Paths extends Record<string | number, unknown>,
72
+ Media extends MediaType = MediaType
73
+ >(
74
+ client: Client<Paths, Media>
75
+ ): PathBasedClient<Paths, Media> => {
76
+ const cache = new Map<string, object>()
77
+ const target = asStrictApiClient<PathBasedClient<Paths, Media>>({})
78
+
79
+ return new Proxy(target, {
80
+ get: (_target, property) => {
81
+ if (typeof property !== "string") {
82
+ return
108
83
  }
109
- continue
110
- }
111
- searchParams.set(key, String(value))
112
- }
113
- return searchParams.toString()
114
- }
115
-
116
- const appendQueryString = (url: string, queryString: string): string => {
117
- if (queryString.length === 0) {
118
- return url
119
- }
120
- return url.includes("?") ? url + "&" + queryString : url + "?" + queryString
121
- }
122
-
123
- const withBaseUrl = (baseUrl: string | undefined, url: string): string => {
124
- // If baseUrl is not provided, keep a relative URL (browser-friendly)
125
- if (baseUrl === undefined || baseUrl === "") {
126
- return url
127
- }
128
-
129
- // Construct full URL
130
- return new URL(url, baseUrl).toString()
131
- }
132
84
 
133
- /**
134
- * Build URL with path parameters and query string
135
- *
136
- * @param baseUrl - Base URL for the API
137
- * @param path - Path template with placeholders
138
- * @param params - Path parameters to substitute
139
- * @param query - Query parameters to append
140
- * @returns Fully constructed URL
141
- *
142
- * @pure true
143
- * @complexity O(n + m) where n = |params|, m = |query|
144
- */
145
- const buildUrl = (
146
- baseUrl: string | undefined,
147
- path: string,
148
- params?: Record<string, ParamValue>,
149
- query?: Record<string, QueryValue>
150
- ): string => {
151
- const urlWithParams = applyPathParams(path, params)
152
- const queryString = buildQueryString(query)
153
- const urlWithQuery = appendQueryString(urlWithParams, queryString)
154
- return withBaseUrl(baseUrl, urlWithQuery)
155
- }
156
-
157
- /**
158
- * Check if body is already a BodyInit type (not a plain object needing serialization)
159
- *
160
- * @pure true
161
- */
162
- const isBodyInit = (body: BodyInit | object): body is BodyInit =>
163
- typeof body === "string"
164
- || body instanceof Blob
165
- || body instanceof ArrayBuffer
166
- || body instanceof ReadableStream
167
- || body instanceof FormData
168
- || body instanceof URLSearchParams
169
-
170
- /**
171
- * Serialize body to BodyInit - passes through BodyInit types, JSON-stringifies objects
172
- *
173
- * @pure true
174
- * @returns BodyInit or undefined, with consistent return path
175
- */
176
- const serializeBody = (body: BodyInit | object | undefined): BodyInit | undefined => {
177
- // Early return for undefined
178
- if (body === undefined) {
179
- return body
180
- }
181
- // Pass through existing BodyInit types
182
- if (isBodyInit(body)) {
183
- return body
184
- }
185
- // Plain object - serialize to JSON string (which is a valid BodyInit)
186
- const serialized: BodyInit = JSON.stringify(body)
187
- return serialized
188
- }
189
-
190
- /**
191
- * Check if body requires JSON Content-Type header
192
- *
193
- * @pure true
194
- */
195
- const needsJsonContentType = (body: BodyInit | object | undefined): boolean =>
196
- body !== undefined
197
- && typeof body !== "string"
198
- && !(body instanceof Blob)
199
- && !(body instanceof FormData)
200
-
201
- const toHeadersFromRecord = (
202
- headersInit: Record<
203
- string,
204
- | string
205
- | number
206
- | boolean
207
- | ReadonlyArray<string | number | boolean>
208
- | null
209
- | undefined
210
- >
211
- ): Headers => {
212
- const headers = new Headers()
85
+ const cached = cache.get(property)
86
+ if (cached !== undefined) {
87
+ return cached
88
+ }
213
89
 
214
- for (const [key, value] of Object.entries(headersInit)) {
215
- if (value === null || value === undefined) {
216
- continue
90
+ const forwarder = new PathCallForwarder(asStrictApiClient<RuntimeClient>(client), property)
91
+ cache.set(property, forwarder)
92
+ return forwarder
217
93
  }
218
- if (Array.isArray(value)) {
219
- headers.set(key, value.map(String).join(","))
220
- continue
221
- }
222
- headers.set(key, String(value))
223
- }
224
-
225
- return headers
226
- }
227
-
228
- /**
229
- * Merge headers from client options and request options
230
- *
231
- * @pure true
232
- * @complexity O(n) where n = number of headers
233
- */
234
- const toHeaders = (headersInit: ClientOptions["headers"] | undefined): Headers => {
235
- if (headersInit === undefined) {
236
- return new Headers()
237
- }
238
-
239
- if (headersInit instanceof Headers) {
240
- return new Headers(headersInit)
241
- }
242
-
243
- if (Array.isArray(headersInit)) {
244
- return new Headers(headersInit)
245
- }
246
-
247
- return toHeadersFromRecord(headersInit)
248
- }
249
-
250
- const mergeHeaders = (
251
- clientHeaders: ClientOptions["headers"] | undefined,
252
- requestHeaders: ClientOptions["headers"] | undefined
253
- ): Headers => {
254
- const headers = toHeaders(clientHeaders)
255
- const optHeaders = toHeaders(requestHeaders)
256
- for (const [key, value] of optHeaders.entries()) {
257
- headers.set(key, value)
258
- }
259
- return headers
260
- }
261
-
262
- /**
263
- * Request options type for method handlers
264
- *
265
- * @pure true - type alias only
266
- */
267
- type MethodHandlerOptions = {
268
- params?: Record<string, ParamValue> | undefined
269
- query?: Record<string, QueryValue> | undefined
270
- body?: BodyInit | object | undefined
271
- headers?: ClientOptions["headers"] | undefined
272
- signal?: AbortSignal | undefined
273
- }
274
-
275
- /**
276
- * Create HTTP method handler with full type constraints
277
- *
278
- * @param method - HTTP method
279
- * @param clientOptions - Client configuration
280
- * @returns Method handler function
281
- *
282
- * @pure false - creates function that performs HTTP requests
283
- * @complexity O(1) handler creation
284
- */
285
- const createMethodHandler = (
286
- method: HttpMethod,
287
- clientOptions: ClientOptions
288
- ) =>
289
- <Responses>(
290
- path: string,
291
- dispatcher: Dispatcher<Responses>,
292
- options?: MethodHandlerOptions
293
- ) => {
294
- const url = buildUrl(clientOptions.baseUrl, path, options?.params, options?.query)
295
- const headers = mergeHeaders(clientOptions.headers, options?.headers)
296
- const body = serializeBody(options?.body)
297
-
298
- if (needsJsonContentType(options?.body)) {
299
- headers.set("Content-Type", "application/json")
300
- }
301
-
302
- const config: StrictRequestInit<Responses> = asStrictRequestInit({
303
- method,
304
- url,
305
- dispatcher,
306
- headers,
307
- body,
308
- signal: options?.signal
309
94
  })
310
-
311
- return executeRequest(config)
312
95
  }
313
96
 
314
- /**
315
- * Create method handler that infers dispatcher from map
316
- *
317
- * @pure false - creates function that performs HTTP requests
318
- * @complexity O(1) handler creation
319
- */
320
- const createMethodHandlerWithDispatchers = <Paths extends object, Method extends HttpMethod>(
321
- method: Method,
322
- clientOptions: ClientOptions,
323
- dispatchers: DispatchersForMethod<Paths, Method>
324
- ) =>
325
- <Path extends keyof DispatchersForMethod<Paths, Method> & string>(
326
- path: Path,
327
- options?: MethodHandlerOptions
328
- ) =>
329
- createMethodHandler(method, clientOptions)(
330
- path,
331
- dispatchers[path][method],
332
- options
333
- )
334
-
335
- // CHANGE: Create method handler that infers dispatcher from map
336
- // WHY: Allow per-call API without passing dispatcher parameter
337
- // QUOTE(ТЗ): "Зачем передавать что либо в GET"
338
- // REF: user-msg-1
339
- // SOURCE: n/a
340
- // FORMAT THEOREM: ∀ path ∈ PathsForMethod<Paths, method>: dispatchers[path][method] = Dispatcher<ResponsesFor<Op>>
341
- // PURITY: SHELL
342
- // EFFECT: Effect<ApiSuccess<Responses>, ApiFailure<Responses>, HttpClient>
343
- // INVARIANT: Dispatcher lookup is total for all operations in Paths
344
- // COMPLEXITY: O(1) runtime + O(1) dispatcher lookup
345
- /**
346
- * Create type-safe Effect-based API client
347
- *
348
- * The client enforces:
349
- * 1. Method availability: GET only on paths with `get`, POST only on paths with `post`
350
- * 2. Dispatcher correlation: must match operation's responses
351
- * 3. Request options: params/query/body typed from operation
352
- *
353
- * @typeParam Paths - OpenAPI paths type from openapi-typescript
354
- * @param options - Client configuration
355
- * @returns API client with typed methods for all operations
356
- *
357
- * @pure false - creates client that performs HTTP requests
358
- * @effect Client methods return Effect<Success, Failure, HttpClient>
359
- * @invariant ∀ path, method: path ∈ PathsForMethod<Paths, method>
360
- * @complexity O(1) client creation
361
- *
362
- * @example
363
- * ```typescript
364
- * import createClient from "openapi-effect"
365
- * import type { Paths } from "./generated/schema"
366
- * import "./generated/dispatchers-by-path" // registers default dispatchers
367
- *
368
- * const client = createClient<Paths>({
369
- * baseUrl: "https://api.example.com",
370
- * credentials: "include"
371
- * })
372
- *
373
- * // Type-safe call - dispatcher inferred from path+method
374
- * const result = yield* client.GET("/pets/{petId}", {
375
- * params: { petId: "123" } // Required because getPet has path params
376
- * })
377
- *
378
- * // Compile error: "/pets/{petId}" has no "put" method
379
- * // client.PUT("/pets/{petId}", ...) // Type error!
380
- * ```
381
- */
382
- export const createClient = <Paths extends object>(
383
- options: ClientOptions,
384
- dispatchers?: DispatchersFor<Paths>
385
- ): StrictApiClientWithDispatchers<Paths> => {
386
- const resolvedDispatchers = dispatchers ?? resolveDefaultDispatchers<Paths>()
387
-
388
- return asStrictApiClient<StrictApiClientWithDispatchers<Paths>>({
389
- GET: createMethodHandlerWithDispatchers("get", options, resolvedDispatchers),
390
- POST: createMethodHandlerWithDispatchers("post", options, resolvedDispatchers),
391
- PUT: createMethodHandlerWithDispatchers("put", options, resolvedDispatchers),
392
- DELETE: createMethodHandlerWithDispatchers("delete", options, resolvedDispatchers),
393
- PATCH: createMethodHandlerWithDispatchers("patch", options, resolvedDispatchers),
394
- HEAD: createMethodHandlerWithDispatchers("head", options, resolvedDispatchers),
395
- OPTIONS: createMethodHandlerWithDispatchers("options", options, resolvedDispatchers)
396
- })
397
- }
398
-
399
- // CHANGE: Add createMethodHandlerWithUniversalDispatcher for zero-boilerplate client
400
- // WHY: Enable createClientEffect<Paths>(options) without code generation or dispatcher registry
401
- // QUOTE(ТЗ): "Я не хочу создавать какие-то дополнительные модули"
402
- // REF: issue-5
403
- // SOURCE: n/a
404
- // FORMAT THEOREM: ∀ path, method: universalDispatcher handles response classification generically
405
- // PURITY: SHELL
406
- // EFFECT: Effect<ApiSuccess<Responses>, ApiFailure<Responses>, HttpClient>
407
- // INVARIANT: 2xx → success channel, non-2xx → error channel
408
- // COMPLEXITY: O(1) handler creation + O(1) universal dispatcher creation per call
409
- const createMethodHandlerWithUniversalDispatcher = (
410
- method: HttpMethod,
411
- clientOptions: ClientOptions
412
- ) =>
413
- (
414
- path: string,
415
- options?: MethodHandlerOptions
416
- ) =>
417
- createMethodHandler(method, clientOptions)(
418
- path,
419
- createUniversalDispatcher(),
420
- options
421
- )
422
-
423
- type HttpErrorTag = { readonly _tag: "HttpError" }
97
+ export const createPathBasedClient = <
98
+ Paths extends Record<string | number, unknown>,
99
+ Media extends MediaType = MediaType
100
+ >(
101
+ clientOptions?: ClientOptions
102
+ ): PathBasedClient<Paths, Media> => wrapAsPathBasedClient(createClient<Paths, Media>(clientOptions))
424
103
 
425
- const isHttpErrorValue = (error: unknown): error is HttpErrorTag =>
426
- typeof error === "object"
427
- && error !== null
428
- && "_tag" in error
429
- && Reflect.get(error, "_tag") === "HttpError"
430
-
431
- const exposeHttpErrorsAsValues = <A, E>(
432
- request: Effect.Effect<A, E, HttpClient.HttpClient>
433
- ): Effect.Effect<
434
- A | Extract<E, HttpErrorTag>,
435
- Exclude<E, Extract<E, HttpErrorTag>>,
436
- HttpClient.HttpClient
437
- > =>
438
- request.pipe(
439
- Effect.catchIf(
440
- (error): error is Extract<E, HttpErrorTag> => isHttpErrorValue(error),
441
- (error) => Effect.succeed(error)
442
- )
443
- )
444
-
445
- const createMethodHandlerWithUniversalDispatcherValue = (
446
- method: HttpMethod,
447
- clientOptions: ClientOptions
448
- ) =>
449
- (
450
- path: string,
451
- options?: MethodHandlerOptions
452
- ) =>
453
- exposeHttpErrorsAsValues(
454
- createMethodHandlerWithUniversalDispatcher(method, clientOptions)(path, options)
455
- )
456
-
457
- // CHANGE: Add createClientEffect — zero-boilerplate Effect-based API client
458
- // WHY: Enable the user's desired DSL without any generated code or dispatcher setup
459
- // QUOTE(ТЗ): "const apiClientEffect = createClientEffect<Paths>(clientOptions); apiClientEffect.POST('/api/auth/login', { body: credentials })"
460
- // REF: issue-5
461
- // SOURCE: n/a
462
- // FORMAT THEOREM: ∀ Paths, options: createClientEffect<Paths>(options) → ClientEffect<Paths>
463
- // PURITY: SHELL
464
- // EFFECT: Client methods return Effect<ApiSuccess | HttpError, BoundaryError, HttpClient>
465
- // INVARIANT: ∀ path, method: path ∈ PathsForMethod<Paths, method> (compile-time) ∧ response classified by status range (runtime)
466
- // COMPLEXITY: O(1) client creation
467
- /**
468
- * Create type-safe Effect-based API client with zero boilerplate
469
- *
470
- * Uses a universal dispatcher and exposes HTTP statuses as values:
471
- * - 2xx → success value (ApiSuccess)
472
- * - non-2xx schema statuses → success value (HttpError with _tag)
473
- * - boundary/protocol failures stay in error channel
474
- * - JSON parsed automatically for application/json content types
475
- *
476
- * **No code generation needed.** No dispatcher registry needed.
477
- * Just pass your OpenAPI Paths type and client options.
478
- *
479
- * @typeParam Paths - OpenAPI paths type from openapi-typescript
480
- * @param options - Client configuration (baseUrl, credentials, headers, etc.)
481
- * @returns API client with typed methods for all operations
482
- *
483
- * @pure false - creates client that performs HTTP requests
484
- * @effect Client methods return Effect<Success, Failure, HttpClient>
485
- * @invariant ∀ path, method: path ∈ PathsForMethod<Paths, method>
486
- * @complexity O(1) client creation
487
- *
488
- * @example
489
- * ```typescript
490
- * import { createClientEffect, type ClientOptions } from "openapi-effect"
491
- * import type { paths } from "./openapi.d.ts"
492
- *
493
- * const clientOptions: ClientOptions = {
494
- * baseUrl: "https://petstore.example.com",
495
- * credentials: "include"
496
- * }
497
- * const apiClientEffect = createClientEffect<paths>(clientOptions)
498
- *
499
- * // Type-safe call — path, method, and body all enforced at compile time
500
- * const result = yield* apiClientEffect.POST("/api/auth/login", {
501
- * body: { email: "user@example.com", password: "secret" }
502
- * })
503
- * ```
504
- */
505
104
  export const createClientEffect = <Paths extends object>(
506
- options: ClientOptions
507
- ): ClientEffect<Paths> => {
508
- return asStrictApiClient<ClientEffect<Paths>>({
509
- GET: createMethodHandlerWithUniversalDispatcherValue("get", options),
510
- POST: createMethodHandlerWithUniversalDispatcherValue("post", options),
511
- PUT: createMethodHandlerWithUniversalDispatcherValue("put", options),
512
- DELETE: createMethodHandlerWithUniversalDispatcherValue("delete", options),
513
- PATCH: createMethodHandlerWithUniversalDispatcherValue("patch", options),
514
- HEAD: createMethodHandlerWithUniversalDispatcherValue("head", options),
515
- OPTIONS: createMethodHandlerWithUniversalDispatcherValue("options", options)
516
- })
517
- }
105
+ clientOptions?: ClientOptions
106
+ ): ClientEffect<Paths> => createClient<Paths>(clientOptions)
107
+
108
+ export const registerDefaultDispatchers = <Paths extends object>(
109
+ _dispatchers: DispatchersFor<Paths>
110
+ ): void => {}
@@ -28,10 +28,37 @@ export {
28
28
 
29
29
  // High-level client creation API
30
30
  export type {
31
+ Client,
31
32
  ClientEffect,
33
+ ClientForPath,
32
34
  ClientOptions,
33
35
  DispatchersFor,
36
+ FetchOptions,
37
+ FetchResponse,
38
+ HeadersOptions,
39
+ Middleware,
40
+ ParseAs,
41
+ PathBasedClient,
42
+ QuerySerializer,
43
+ QuerySerializerOptions,
44
+ RequestBodyOption,
45
+ RequestOptions as FetchRequestOptions,
34
46
  StrictApiClient,
35
47
  StrictApiClientWithDispatchers
36
48
  } from "./create-client.js"
37
- export { createClient, createClientEffect, registerDefaultDispatchers } from "./create-client.js"
49
+ export {
50
+ createClient,
51
+ createClientEffect,
52
+ createFinalURL,
53
+ createPathBasedClient,
54
+ createQuerySerializer,
55
+ defaultBodySerializer,
56
+ defaultPathSerializer,
57
+ mergeHeaders,
58
+ registerDefaultDispatchers,
59
+ removeTrailingSlash,
60
+ serializeArrayParam,
61
+ serializeObjectParam,
62
+ serializePrimitiveParam,
63
+ wrapAsPathBasedClient
64
+ } from "./create-client.js"