@mpen/routekit 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/dist/bin.d.mts +4 -0
  2. package/dist/client/react.d.mts +178 -0
  3. package/dist/client/react.mjs +142 -0
  4. package/dist/client.d.mts +433 -0
  5. package/dist/client.mjs +264 -0
  6. package/dist/content-BuDOmhH_.mjs +102 -0
  7. package/dist/core-CzUCxvGk.d.mts +140 -0
  8. package/dist/core-DbmQauwS.mjs +81 -0
  9. package/dist/handlers.d.mts +72 -0
  10. package/dist/handlers.mjs +153 -0
  11. package/dist/index.d.mts +3 -0
  12. package/dist/index.mjs +1152 -0
  13. package/dist/middleware.d.mts +388 -0
  14. package/dist/middleware.mjs +1222 -0
  15. package/dist/request-Dn0zc-xm.mjs +1025 -0
  16. package/dist/response/content.d.mts +79 -0
  17. package/dist/response/content.mjs +2 -0
  18. package/dist/response/json-rpc.d.mts +1 -0
  19. package/dist/response/json-rpc.mjs +1 -0
  20. package/dist/response/problem/valibot.d.mts +230 -0
  21. package/dist/response/problem/valibot.mjs +258 -0
  22. package/dist/response/problem.d.mts +415 -0
  23. package/dist/response/problem.mjs +183 -0
  24. package/dist/response/status.d.mts +45 -0
  25. package/dist/response/status.mjs +2 -0
  26. package/dist/responses-B379Ep9Y.d.mts +296 -0
  27. package/dist/responses-BpVrgeYi.mjs +101 -0
  28. package/dist/router-Cwb7ak0J.d.mts +1819 -0
  29. package/dist/routes.d.mts +282 -0
  30. package/dist/routes.mjs +311 -0
  31. package/dist/status-C-8mw-FB.mjs +59 -0
  32. package/dist/valibot-D7liFYyB.d.mts +290 -0
  33. package/dist/valibot-Du97X-TS.mjs +326 -0
  34. package/package.json +8 -2
  35. package/src/bin/gen-api-client.test.ts +0 -70
  36. package/src/bin/gen-api-client.ts +0 -986
  37. package/src/client/headers.ts +0 -31
  38. package/src/client/index.ts +0 -8
  39. package/src/client/promise.ts +0 -11
  40. package/src/client/react/index.test.tsx +0 -266
  41. package/src/client/react/index.ts +0 -431
  42. package/src/client/responses.test.ts +0 -151
  43. package/src/client/responses.ts +0 -278
  44. package/src/client/transport.ts +0 -74
  45. package/src/client/transports/body-codec.ts +0 -61
  46. package/src/client/transports/fetch.ts +0 -113
  47. package/src/client/tsconfig.json +0 -9
  48. package/src/client/types.ts +0 -15
  49. package/src/client/url.ts +0 -31
  50. package/src/index.ts +0 -63
  51. package/src/router/fetch-types.ts +0 -13
  52. package/src/router/handlers/index.ts +0 -2
  53. package/src/router/handlers/openapi/index.ts +0 -2
  54. package/src/router/handlers/openapi/openapi.ts +0 -293
  55. package/src/router/integration/zod-openapi.test.ts +0 -74
  56. package/src/router/lib/charset.test.ts +0 -22
  57. package/src/router/lib/charset.ts +0 -133
  58. package/src/router/lib/collections.ts +0 -3
  59. package/src/router/lib/format.test.ts +0 -67
  60. package/src/router/lib/format.ts +0 -35
  61. package/src/router/lib/host.ts +0 -4
  62. package/src/router/lib/json-schema.ts +0 -6
  63. package/src/router/lib/media-type.test.ts +0 -122
  64. package/src/router/lib/media-type.ts +0 -289
  65. package/src/router/lib/pathname.test.ts +0 -18
  66. package/src/router/lib/pathname.ts +0 -19
  67. package/src/router/lib/route-names.ts +0 -70
  68. package/src/router/lib/route-normalize.test.ts +0 -36
  69. package/src/router/lib/route-normalize.ts +0 -67
  70. package/src/router/lib/schema-merge.ts +0 -56
  71. package/src/router/middleware/accept-ctx.test.ts +0 -33
  72. package/src/router/middleware/accept-ctx.ts +0 -12
  73. package/src/router/middleware/body-limit.test.ts +0 -112
  74. package/src/router/middleware/body-limit.ts +0 -121
  75. package/src/router/middleware/content-type-context.ts +0 -0
  76. package/src/router/middleware/cors.test.ts +0 -269
  77. package/src/router/middleware/cors.ts +0 -490
  78. package/src/router/middleware/csrf.test.ts +0 -106
  79. package/src/router/middleware/csrf.ts +0 -192
  80. package/src/router/middleware/define.ts +0 -249
  81. package/src/router/middleware/index.ts +0 -34
  82. package/src/router/middleware/jsxhtml-response.ts +0 -0
  83. package/src/router/middleware/oas-swagger.ts +0 -0
  84. package/src/router/middleware/rate-limit.test.ts +0 -886
  85. package/src/router/middleware/rate-limit.ts +0 -920
  86. package/src/router/middleware/request-id-ctx.test.ts +0 -183
  87. package/src/router/middleware/request-id-ctx.ts +0 -135
  88. package/src/router/middleware/request-logger-format.test.ts +0 -16
  89. package/src/router/middleware/request-logger-format.ts +0 -269
  90. package/src/router/middleware/request-logger.test.ts +0 -267
  91. package/src/router/middleware/request-logger.ts +0 -131
  92. package/src/router/middleware/start-time-ctx.ts +0 -5
  93. package/src/router/request.ts +0 -611
  94. package/src/router/response/core.ts +0 -181
  95. package/src/router/response/directives.ts +0 -233
  96. package/src/router/response/formats/content/bodyless.ts +0 -54
  97. package/src/router/response/formats/content/content.ts +0 -79
  98. package/src/router/response/formats/content/index.ts +0 -2
  99. package/src/router/response/formats/json-rpc/index.ts +0 -2
  100. package/src/router/response/formats/problem/badRequest.ts +0 -90
  101. package/src/router/response/formats/problem/conflict.ts +0 -90
  102. package/src/router/response/formats/problem/created.ts +0 -40
  103. package/src/router/response/formats/problem/index.ts +0 -27
  104. package/src/router/response/formats/problem/notFound.ts +0 -90
  105. package/src/router/response/formats/problem/permissionDenied.ts +0 -90
  106. package/src/router/response/formats/problem/problem.test.ts +0 -888
  107. package/src/router/response/formats/problem/rateLimited.ts +0 -90
  108. package/src/router/response/formats/problem/responses.ts +0 -219
  109. package/src/router/response/formats/problem/root-errors.ts +0 -48
  110. package/src/router/response/formats/problem/sessionExpired.ts +0 -90
  111. package/src/router/response/formats/problem/types.ts +0 -170
  112. package/src/router/response/formats/problem/unauthenticated.ts +0 -90
  113. package/src/router/response/formats/problem/valibot.ts +0 -410
  114. package/src/router/response/formats/status/index.ts +0 -1
  115. package/src/router/response/formats/status/responses.ts +0 -59
  116. package/src/router/response/formats/status/status.test.ts +0 -21
  117. package/src/router/response/framers.ts +0 -85
  118. package/src/router/response/index.ts +0 -28
  119. package/src/router/response/openapi.test.ts +0 -96
  120. package/src/router/response/openapi.ts +0 -1
  121. package/src/router/response/serializers.ts +0 -66
  122. package/src/router/response/stream.ts +0 -35
  123. package/src/router/router.test.ts +0 -1571
  124. package/src/router/router.ts +0 -1965
  125. package/src/router/routes/index.ts +0 -46
  126. package/src/router/routes/valibot/index.ts +0 -18
  127. package/src/router/routes/valibot/valibot.ts +0 -1393
  128. package/src/router/routes/valibot.test.ts +0 -286
  129. package/src/router/routes/zod/index.ts +0 -18
  130. package/src/router/routes/zod/zod.ts +0 -1318
  131. package/src/router/routes/zod.test.ts +0 -280
  132. package/src/router/server-interface.ts +0 -31
  133. package/src/router/types.ts +0 -657
@@ -0,0 +1,433 @@
1
+ //#region src/client/promise.d.ts
2
+ /**
3
+ * A value that may be returned immediately or through a promise.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * const headers: MaybePromise<HeadersInit> = { authorization: 'Bearer token' }
8
+ * ```
9
+ *
10
+ * @typeParam T - The resolved value type.
11
+ */
12
+ type MaybePromise<T> = T | Promise<T>;
13
+ //#endregion
14
+ //#region src/client/headers.d.ts
15
+ type ClientHeadersInit$1 = NonNullable<ConstructorParameters<typeof Headers>[0]>;
16
+ /**
17
+ * Context passed to global header providers.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const headers = (context: ClientHeaderContext) =>
22
+ * context.url.startsWith('/admin') ? { authorization: `Bearer ${token}` } : undefined
23
+ * ```
24
+ */
25
+ interface ClientHeaderContext {
26
+ /** The request URL before base URL resolution. */
27
+ url: string;
28
+ /** The request init object before execution. */
29
+ init: RequestInit;
30
+ }
31
+ /**
32
+ * Headers or a function that provides headers for each request.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * const headers: ClientHeaders = () => ({ authorization: `Bearer ${token}` })
37
+ * ```
38
+ */
39
+ type ClientHeaders = ClientHeadersInit$1 | ((context: ClientHeaderContext) => MaybePromise<ClientHeadersInit$1 | undefined>);
40
+ //#endregion
41
+ //#region src/client/transport.d.ts
42
+ type ClientHeadersInit = NonNullable<ConstructorParameters<typeof Headers>[0]>;
43
+ /**
44
+ * A generated client request before it is executed by a transport.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * const request: ClientRequest = {
49
+ * url: '/widgets',
50
+ * init: { method: 'POST' },
51
+ * body: { name: 'demo' },
52
+ * }
53
+ * ```
54
+ */
55
+ interface ClientRequest {
56
+ /** The URL path generated for the route. */
57
+ url: string;
58
+ /** Fetch-compatible request initialization. */
59
+ init: RequestInit;
60
+ /** The unencoded request body value. */
61
+ body?: unknown;
62
+ }
63
+ /**
64
+ * A minimal response returned by client transports.
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * const response: ApiTransportResponse = {
69
+ * status: 200,
70
+ * body: { ok: true },
71
+ * }
72
+ * ```
73
+ */
74
+ interface ApiTransportResponse {
75
+ /** The response status code. */
76
+ status: number;
77
+ /** The response headers, when available. */
78
+ headers?: ClientHeadersInit;
79
+ /** The parsed response body. */
80
+ body: unknown;
81
+ }
82
+ /**
83
+ * The async response wrapper returned by client transports.
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * const response: ApiTransportResponsePromise = transport.request(request)
88
+ * ```
89
+ */
90
+ type ApiTransportResponsePromise = Promise<ApiTransportResponse>;
91
+ /**
92
+ * Transport used by generated API clients to execute typed requests.
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * class AxiosTransport implements ClientTransport {
97
+ * async request(request: ClientRequest): ApiTransportResponsePromise {
98
+ * throw new Error('Adapt Axios into a Response-shaped object here')
99
+ * }
100
+ * }
101
+ * ```
102
+ */
103
+ interface ClientTransport {
104
+ /**
105
+ * Execute a generated client request.
106
+ *
107
+ * @param request - The generated request metadata and init.
108
+ * @returns A transport response promise.
109
+ */
110
+ request(request: ClientRequest): ApiTransportResponsePromise;
111
+ }
112
+ //#endregion
113
+ //#region src/client/responses.d.ts
114
+ /**
115
+ * A typed response returned by generated API clients.
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * const response: ApiResponse<{ ok: true }> = await client.health.get()
120
+ * if (response.status === 200) {
121
+ * response.body.ok
122
+ * }
123
+ * ```
124
+ *
125
+ * @typeParam T - The parsed response body type.
126
+ */
127
+ interface ApiResponse<T> {
128
+ /** Whether the response status is in the successful 200-299 range. */
129
+ ok: boolean;
130
+ /** The response status code. */
131
+ status: number;
132
+ /** The response headers. */
133
+ headers: Headers;
134
+ /** The parsed response body. */
135
+ body: T;
136
+ }
137
+ type ApiResponseStatus<TStatus> = TStatus extends number ? TStatus : TStatus extends `${infer TNumber extends number}` ? TNumber : number;
138
+ /**
139
+ * A typed response union keyed by HTTP status code.
140
+ *
141
+ * @example
142
+ * ```ts
143
+ * type WidgetResponse = ApiResponseByStatus<{
144
+ * 200: { id: number }
145
+ * 400: { message: string }
146
+ * }>
147
+ *
148
+ * if (response.status === 400) {
149
+ * response.body.message
150
+ * }
151
+ * ```
152
+ *
153
+ * @typeParam T - A map from response status codes to parsed response body types.
154
+ */
155
+ type ApiResponseByStatus<T extends object> = { [TStatus in keyof T]: Omit<ApiResponse<T[TStatus]>, 'status'> & {
156
+ /** The narrowed response status code. */status: ApiResponseStatus<TStatus>;
157
+ } }[keyof T];
158
+ /**
159
+ * The default async response wrapper used by generated API clients.
160
+ *
161
+ * @example
162
+ * ```ts
163
+ * const response: ApiResponsePromise<{ id: number }> = client.items.byId.get(123)
164
+ * ```
165
+ *
166
+ * @typeParam T - The parsed response body type.
167
+ */
168
+ type ApiResponsePromise<T> = Promise<ApiResponse<T>>;
169
+ /**
170
+ * The async response wrapper used by generated API clients for routes with response body types
171
+ * keyed by HTTP status code.
172
+ *
173
+ * @example
174
+ * ```ts
175
+ * const response: ApiResponseByStatusPromise<{ 200: { ok: true } }> = client.health.get()
176
+ * ```
177
+ *
178
+ * @typeParam T - A map from response status codes to parsed response body types.
179
+ */
180
+ type ApiResponseByStatusPromise<T extends object> = Promise<ApiResponseByStatus<T>>;
181
+ /**
182
+ * Extracts the successful `data` payload from a Routekit Problem-style success envelope.
183
+ *
184
+ * @example
185
+ * ```ts
186
+ * type Body = { success: true; data: { id: string } }
187
+ * type Data = RoutekitProblemSuccessData<Body> // { id: string }
188
+ * ```
189
+ *
190
+ * @typeParam TSuccessBody - Routekit Problem-style successful response body.
191
+ */
192
+ type RoutekitProblemSuccessData<TSuccessBody> = TSuccessBody extends {
193
+ success: true;
194
+ data: infer Data;
195
+ } ? Data : unknown;
196
+ /**
197
+ * Error thrown for Routekit Problem-style response bodies.
198
+ *
199
+ * @example
200
+ * ```ts
201
+ * try {
202
+ * await resolveRoutekitProblemData(response)
203
+ * } catch (error) {
204
+ * if (isRoutekitProblemError(error)) {
205
+ * console.error(error.status, error.body.error.code)
206
+ * }
207
+ * }
208
+ * ```
209
+ *
210
+ * @typeParam TProblem - Routekit Problem-style error response body.
211
+ */
212
+ declare class RoutekitProblemError<TProblem> extends Error {
213
+ /**
214
+ * The response status code.
215
+ */
216
+ readonly status: number;
217
+ /**
218
+ * The response headers.
219
+ */
220
+ readonly headers: Headers;
221
+ /**
222
+ * The Routekit Problem-style response body.
223
+ */
224
+ readonly body: TProblem;
225
+ /**
226
+ * The full normalized API response.
227
+ */
228
+ readonly response: ApiResponse<TProblem>;
229
+ /**
230
+ * Create an error for a Routekit Problem-style response.
231
+ *
232
+ * @param response - Normalized API response containing a problem body.
233
+ */
234
+ constructor(response: ApiResponse<TProblem>);
235
+ }
236
+ /**
237
+ * Checks whether a value is a [`RoutekitProblemError`]{@link RoutekitProblemError}.
238
+ *
239
+ * @example
240
+ * ```ts
241
+ * if (isRoutekitProblemError(error)) {
242
+ * error.body
243
+ * }
244
+ * ```
245
+ *
246
+ * @param value - Value to inspect.
247
+ * @returns Whether the value is a Routekit Problem error.
248
+ * @typeParam TProblem - Routekit Problem-style error response body.
249
+ */
250
+ declare function isRoutekitProblemError<TProblem = unknown>(value: unknown): value is RoutekitProblemError<TProblem>;
251
+ /**
252
+ * Normalize a transport response into the public generated API response shape.
253
+ *
254
+ * @example
255
+ * ```ts
256
+ * const response = await resolveApiResponse(transport.request(request))
257
+ * ```
258
+ *
259
+ * @param response - A transport response or transport response promise.
260
+ * @returns The normalized API response.
261
+ * @typeParam T - The parsed response body type.
262
+ */
263
+ declare function resolveApiResponse<T>(response: ApiTransportResponse | ApiTransportResponsePromise): ApiResponsePromise<T>;
264
+ /**
265
+ * Normalize a transport response into a generated API response union keyed by status code.
266
+ *
267
+ * @example
268
+ * ```ts
269
+ * const response = await resolveApiResponseByStatus<{ 200: { ok: true } }>(
270
+ * transport.request(request),
271
+ * )
272
+ * ```
273
+ *
274
+ * @param response - A transport response or transport response promise.
275
+ * @returns The normalized API response.
276
+ * @typeParam T - A map from response status codes to parsed response body types.
277
+ */
278
+ declare function resolveApiResponseByStatus<T extends object>(response: ApiTransportResponse | ApiTransportResponsePromise): ApiResponseByStatusPromise<T>;
279
+ /**
280
+ * Resolve a Routekit Problem-style API response into its successful data payload.
281
+ *
282
+ * This helper unwraps `{ success: true, data }` bodies and throws
283
+ * [`RoutekitProblemError`]{@link RoutekitProblemError} for non-success problem bodies, which lets
284
+ * TanStack Query use its built-in success and error states while preserving rich problem details.
285
+ *
286
+ * @example
287
+ * ```ts
288
+ * const data = await resolveRoutekitProblemData<
289
+ * { success: true; data: { id: string } },
290
+ * { success: false; error: { code: 'not_found'; message: string } }
291
+ * >(transport.request(request))
292
+ * ```
293
+ *
294
+ * @param response - A transport response, normalized API response, or promise for either.
295
+ * @returns The unwrapped successful response `data` payload.
296
+ * @typeParam TSuccessBody - Routekit Problem-style successful response body.
297
+ * @typeParam TProblemBody - Routekit Problem-style problem response body.
298
+ */
299
+ declare function resolveRoutekitProblemData<TSuccessBody, TProblemBody>(response: ApiResponse<TSuccessBody | TProblemBody> | ApiTransportResponse | ApiTransportResponsePromise | Promise<ApiResponse<TSuccessBody | TProblemBody>>): Promise<RoutekitProblemSuccessData<TSuccessBody>>;
300
+ //#endregion
301
+ //#region src/client/types.d.ts
302
+ /**
303
+ * Extracts the type of a single path parameter from a path parameter object type.
304
+ *
305
+ * @example
306
+ * ```ts
307
+ * type Params = { id: string }
308
+ * type IdParam = SinglePathParam<Params, 'id'> // string
309
+ * ```
310
+ *
311
+ * @typeParam TParams - The path parameter object type.
312
+ * @typeParam TKey - The parameter key to extract.
313
+ */
314
+ type SinglePathParam<TParams, TKey extends string> = TParams extends { [K in TKey]: infer V } ? V : unknown;
315
+ //#endregion
316
+ //#region src/client/transports/body-codec.d.ts
317
+ type ClientBodyInit = NonNullable<RequestInit['body']>;
318
+ /**
319
+ * The response body stream exposed to response body codecs.
320
+ *
321
+ * @example
322
+ * ```ts
323
+ * const text = await new Response(body).text()
324
+ * ```
325
+ */
326
+ type ResponseBodyReader = ReadableStream<Uint8Array<ArrayBufferLike>> | null;
327
+ /**
328
+ * Serializes request bodies and deserializes response bodies for generated clients.
329
+ *
330
+ * @example
331
+ * ```ts
332
+ * const textJsonCodec: BodyCodec = {
333
+ * serialize: (value) => JSON.stringify(value),
334
+ * deserialize: async (body) => JSON.parse(await new Response(body).text()),
335
+ * }
336
+ * ```
337
+ */
338
+ interface BodyCodec {
339
+ /**
340
+ * Serialize a generated client body value into a Fetch-compatible body.
341
+ *
342
+ * @param value - The generated client body value.
343
+ * @returns A Fetch-compatible body, or nullish to omit the request body.
344
+ */
345
+ serialize(value: unknown): ClientBodyInit | null | undefined;
346
+ /**
347
+ * Deserialize a response body for generated `response.body` values.
348
+ *
349
+ * @param body - The response body stream.
350
+ * @param contentType - The response content type, when present.
351
+ * @returns The deserialized response body.
352
+ * @typeParam T - The expected response body type.
353
+ */
354
+ deserialize<T>(body: ResponseBodyReader, contentType: string | null): Promise<T>;
355
+ }
356
+ /**
357
+ * The default JSON codec used by generated API clients.
358
+ *
359
+ * @example
360
+ * ```ts
361
+ * const client = new ApiClient(new FetchTransport({ bodyCodec: jsonBodyCodec }))
362
+ * ```
363
+ */
364
+ declare const jsonBodyCodec: BodyCodec;
365
+ //#endregion
366
+ //#region src/client/transports/fetch.d.ts
367
+ /**
368
+ * Options for [`FetchTransport`]{@link FetchTransport}.
369
+ *
370
+ * @example
371
+ * ```ts
372
+ * const transport = new FetchTransport({
373
+ * baseUrl: 'https://api.example.com',
374
+ * headers: () => ({ authorization: `Bearer ${token}` }),
375
+ * })
376
+ * ```
377
+ */
378
+ interface FetchTransportOptions {
379
+ /** Base URL used to resolve generated relative route URLs. */
380
+ baseUrl?: string | URL;
381
+ /** Fetch implementation to call. Defaults to `globalThis.fetch`. */
382
+ fetch?: (url: string | URL | Request, init?: RequestInit) => Promise<Response>;
383
+ /** Global headers, or a function that returns headers for each request. */
384
+ headers?: ClientHeaders;
385
+ /** Default body codec. Defaults to [`jsonBodyCodec`]{@link jsonBodyCodec}. */
386
+ bodyCodec?: BodyCodec;
387
+ }
388
+ /**
389
+ * Fetch-based transport for generated API clients.
390
+ *
391
+ * @example
392
+ * ```ts
393
+ * const client = new ApiClient(
394
+ * new FetchTransport({
395
+ * baseUrl: 'https://api.example.com',
396
+ * headers: { authorization: 'Bearer token' },
397
+ * }),
398
+ * )
399
+ * ```
400
+ */
401
+ declare class FetchTransport implements ClientTransport {
402
+ #private;
403
+ /**
404
+ * Create a Fetch-backed generated client transport.
405
+ *
406
+ * @param options - Transport configuration.
407
+ */
408
+ constructor(options?: FetchTransportOptions);
409
+ /**
410
+ * Execute a generated client request with Fetch.
411
+ *
412
+ * @param request - The generated client request.
413
+ * @returns A transport response promise.
414
+ */
415
+ request(request: ClientRequest): ApiTransportResponsePromise;
416
+ }
417
+ //#endregion
418
+ //#region src/client/url.d.ts
419
+ /**
420
+ * Append an object of query values to a URL.
421
+ *
422
+ * @example
423
+ * ```ts
424
+ * withQuery('/widgets', { view: 'full', tag: ['a', 'b'] })
425
+ * ```
426
+ *
427
+ * @param url - URL without generated query parameters.
428
+ * @param query - Query parameter object.
429
+ * @returns The URL with serialized query parameters.
430
+ */
431
+ declare function withQuery(url: string, query: object): string;
432
+ //#endregion
433
+ export { ApiResponse, ApiResponseByStatus, ApiResponseByStatusPromise, ApiResponsePromise, ApiTransportResponse, ApiTransportResponsePromise, BodyCodec, ClientHeaderContext, ClientHeaders, ClientRequest, ClientTransport, FetchTransport, FetchTransportOptions, MaybePromise, ResponseBodyReader, RoutekitProblemError, RoutekitProblemSuccessData, SinglePathParam, isRoutekitProblemError, jsonBodyCodec, resolveApiResponse, resolveApiResponseByStatus, resolveRoutekitProblemData, withQuery };
@@ -0,0 +1,264 @@
1
+ //#region src/client/responses.ts
2
+ /**
3
+ * Error thrown for Routekit Problem-style response bodies.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * try {
8
+ * await resolveRoutekitProblemData(response)
9
+ * } catch (error) {
10
+ * if (isRoutekitProblemError(error)) {
11
+ * console.error(error.status, error.body.error.code)
12
+ * }
13
+ * }
14
+ * ```
15
+ *
16
+ * @typeParam TProblem - Routekit Problem-style error response body.
17
+ */
18
+ var RoutekitProblemError = class extends Error {
19
+ /**
20
+ * The response status code.
21
+ */
22
+ status;
23
+ /**
24
+ * The response headers.
25
+ */
26
+ headers;
27
+ /**
28
+ * The Routekit Problem-style response body.
29
+ */
30
+ body;
31
+ /**
32
+ * The full normalized API response.
33
+ */
34
+ response;
35
+ /**
36
+ * Create an error for a Routekit Problem-style response.
37
+ *
38
+ * @param response - Normalized API response containing a problem body.
39
+ */
40
+ constructor(response) {
41
+ super(getRoutekitProblemMessage(response.body));
42
+ this.name = "RoutekitProblemError";
43
+ this.status = response.status;
44
+ this.headers = response.headers;
45
+ this.body = response.body;
46
+ this.response = response;
47
+ }
48
+ };
49
+ /**
50
+ * Checks whether a value is a [`RoutekitProblemError`]{@link RoutekitProblemError}.
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * if (isRoutekitProblemError(error)) {
55
+ * error.body
56
+ * }
57
+ * ```
58
+ *
59
+ * @param value - Value to inspect.
60
+ * @returns Whether the value is a Routekit Problem error.
61
+ * @typeParam TProblem - Routekit Problem-style error response body.
62
+ */
63
+ function isRoutekitProblemError(value) {
64
+ return value instanceof RoutekitProblemError;
65
+ }
66
+ function getRoutekitProblemMessage(body) {
67
+ if (!body || typeof body !== "object") return "Routekit problem response";
68
+ const error = body.error;
69
+ return typeof error?.message === "string" ? error.message : "Routekit problem response";
70
+ }
71
+ function isRoutekitProblemSuccessBody(body) {
72
+ return !!body && typeof body === "object" && body.success === true && "data" in body;
73
+ }
74
+ function isApiResponse(response) {
75
+ return "ok" in response && typeof response.ok === "boolean" && response.headers instanceof Headers;
76
+ }
77
+ /**
78
+ * Normalize a transport response into the public generated API response shape.
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * const response = await resolveApiResponse(transport.request(request))
83
+ * ```
84
+ *
85
+ * @param response - A transport response or transport response promise.
86
+ * @returns The normalized API response.
87
+ * @typeParam T - The parsed response body type.
88
+ */
89
+ async function resolveApiResponse(response) {
90
+ const resolved = await response;
91
+ return {
92
+ ok: resolved.status >= 200 && resolved.status < 300,
93
+ status: resolved.status,
94
+ headers: new Headers(resolved.headers),
95
+ body: resolved.body
96
+ };
97
+ }
98
+ /**
99
+ * Normalize a transport response into a generated API response union keyed by status code.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * const response = await resolveApiResponseByStatus<{ 200: { ok: true } }>(
104
+ * transport.request(request),
105
+ * )
106
+ * ```
107
+ *
108
+ * @param response - A transport response or transport response promise.
109
+ * @returns The normalized API response.
110
+ * @typeParam T - A map from response status codes to parsed response body types.
111
+ */
112
+ function resolveApiResponseByStatus(response) {
113
+ return resolveApiResponse(response);
114
+ }
115
+ /**
116
+ * Resolve a Routekit Problem-style API response into its successful data payload.
117
+ *
118
+ * This helper unwraps `{ success: true, data }` bodies and throws
119
+ * [`RoutekitProblemError`]{@link RoutekitProblemError} for non-success problem bodies, which lets
120
+ * TanStack Query use its built-in success and error states while preserving rich problem details.
121
+ *
122
+ * @example
123
+ * ```ts
124
+ * const data = await resolveRoutekitProblemData<
125
+ * { success: true; data: { id: string } },
126
+ * { success: false; error: { code: 'not_found'; message: string } }
127
+ * >(transport.request(request))
128
+ * ```
129
+ *
130
+ * @param response - A transport response, normalized API response, or promise for either.
131
+ * @returns The unwrapped successful response `data` payload.
132
+ * @typeParam TSuccessBody - Routekit Problem-style successful response body.
133
+ * @typeParam TProblemBody - Routekit Problem-style problem response body.
134
+ */
135
+ async function resolveRoutekitProblemData(response) {
136
+ const resolvedResponse = await response;
137
+ const apiResponse = isApiResponse(resolvedResponse) ? resolvedResponse : await resolveApiResponse(resolvedResponse);
138
+ if (apiResponse.ok && isRoutekitProblemSuccessBody(apiResponse.body)) return apiResponse.body.data;
139
+ throw new RoutekitProblemError(apiResponse);
140
+ }
141
+ //#endregion
142
+ //#region src/client/transports/body-codec.ts
143
+ /**
144
+ * The default JSON codec used by generated API clients.
145
+ *
146
+ * @example
147
+ * ```ts
148
+ * const client = new ApiClient(new FetchTransport({ bodyCodec: jsonBodyCodec }))
149
+ * ```
150
+ */
151
+ const jsonBodyCodec = {
152
+ serialize: (value) => JSON.stringify(value),
153
+ deserialize: async (body, contentType) => {
154
+ if (!body) return void 0;
155
+ const text = await new Response(body).text();
156
+ if (text.length === 0) return void 0;
157
+ if (contentType?.includes("json")) return JSON.parse(text);
158
+ return text;
159
+ }
160
+ };
161
+ //#endregion
162
+ //#region src/client/transports/fetch.ts
163
+ function mergeHeaders(...sources) {
164
+ const headers = new Headers();
165
+ for (const source of sources) {
166
+ if (!source) continue;
167
+ new Headers(source).forEach((value, key) => {
168
+ headers.set(key, value);
169
+ });
170
+ }
171
+ return headers;
172
+ }
173
+ function resolveUrl(url, baseUrl) {
174
+ return baseUrl ? new URL(url, baseUrl).toString() : url;
175
+ }
176
+ /**
177
+ * Fetch-based transport for generated API clients.
178
+ *
179
+ * @example
180
+ * ```ts
181
+ * const client = new ApiClient(
182
+ * new FetchTransport({
183
+ * baseUrl: 'https://api.example.com',
184
+ * headers: { authorization: 'Bearer token' },
185
+ * }),
186
+ * )
187
+ * ```
188
+ */
189
+ var FetchTransport = class {
190
+ #baseUrl;
191
+ #bodyCodec;
192
+ #fetch;
193
+ #headers;
194
+ /**
195
+ * Create a Fetch-backed generated client transport.
196
+ *
197
+ * @param options - Transport configuration.
198
+ */
199
+ constructor(options = {}) {
200
+ this.#baseUrl = options.baseUrl;
201
+ this.#bodyCodec = options.bodyCodec ?? jsonBodyCodec;
202
+ this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
203
+ this.#headers = options.headers;
204
+ }
205
+ /**
206
+ * Execute a generated client request with Fetch.
207
+ *
208
+ * @param request - The generated client request.
209
+ * @returns A transport response promise.
210
+ */
211
+ async request(request) {
212
+ const codec = this.#bodyCodec;
213
+ const init = { ...request.init };
214
+ const headerContext = {
215
+ url: request.url,
216
+ init
217
+ };
218
+ const headers = mergeHeaders(typeof this.#headers === "function" ? await this.#headers(headerContext) : this.#headers, init.headers);
219
+ if (Object.hasOwn(request, "body")) {
220
+ const body = codec.serialize(request.body);
221
+ if (body != null) {
222
+ init.body = body;
223
+ if (!headers.has("content-type")) headers.set("content-type", "application/json");
224
+ }
225
+ }
226
+ if ([...headers].length > 0) init.headers = headers;
227
+ else delete init.headers;
228
+ const response = await this.#fetch(resolveUrl(request.url, this.#baseUrl), init);
229
+ return {
230
+ status: response.status,
231
+ headers: response.headers,
232
+ body: await codec.deserialize(response.body, response.headers.get("content-type"))
233
+ };
234
+ }
235
+ };
236
+ //#endregion
237
+ //#region src/client/url.ts
238
+ /**
239
+ * Append an object of query values to a URL.
240
+ *
241
+ * @example
242
+ * ```ts
243
+ * withQuery('/widgets', { view: 'full', tag: ['a', 'b'] })
244
+ * ```
245
+ *
246
+ * @param url - URL without generated query parameters.
247
+ * @param query - Query parameter object.
248
+ * @returns The URL with serialized query parameters.
249
+ */
250
+ function withQuery(url, query) {
251
+ const searchParams = new URLSearchParams();
252
+ for (const [key, value] of Object.entries(query)) {
253
+ if (value == null) continue;
254
+ if (Array.isArray(value)) {
255
+ for (const item of value) if (item != null) searchParams.append(key, typeof item === "object" ? JSON.stringify(item) : String(item));
256
+ continue;
257
+ }
258
+ searchParams.append(key, typeof value === "object" ? JSON.stringify(value) : String(value));
259
+ }
260
+ const search = searchParams.toString();
261
+ return search.length > 0 ? `${url}?${search}` : url;
262
+ }
263
+ //#endregion
264
+ export { FetchTransport, RoutekitProblemError, isRoutekitProblemError, jsonBodyCodec, resolveApiResponse, resolveApiResponseByStatus, resolveRoutekitProblemData, withQuery };