@rebilly/api 0.0.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.
- package/CHANGELOG.md +3 -0
- package/LICENSE +21 -0
- package/README.md +229 -1
- package/dist/api-client-options.d.ts +13 -0
- package/dist/api-client-options.js +45 -0
- package/dist/core-client.d.ts +36842 -0
- package/dist/core-client.js +18406 -0
- package/dist/core-client.schemas.d.ts +28910 -0
- package/dist/core.d.ts +5 -0
- package/dist/core.js +7 -0
- package/dist/create-core-client.d.ts +8 -0
- package/dist/create-core-client.js +9 -0
- package/dist/create-storefront-client.d.ts +6 -0
- package/dist/create-storefront-client.js +12 -0
- package/dist/runtime/auth.d.ts +14 -0
- package/dist/runtime/auth.js +53 -0
- package/dist/runtime/create-client.d.ts +50 -0
- package/dist/runtime/create-client.js +345 -0
- package/dist/runtime/errors.d.ts +18 -0
- package/dist/runtime/errors.js +38 -0
- package/dist/runtime/factory.d.ts +11 -0
- package/dist/runtime/factory.js +16 -0
- package/dist/runtime/parse.d.ts +12 -0
- package/dist/runtime/parse.js +41 -0
- package/dist/runtime/retry.d.ts +15 -0
- package/dist/runtime/retry.js +62 -0
- package/dist/runtime/send.d.ts +35 -0
- package/dist/runtime/send.js +224 -0
- package/dist/runtime/types.d.ts +349 -0
- package/dist/runtime/url.d.ts +24 -0
- package/dist/runtime/url.js +62 -0
- package/dist/storefront-client.d.ts +4252 -0
- package/dist/storefront-client.js +1907 -0
- package/dist/storefront-client.schemas.d.ts +7649 -0
- package/dist/storefront.d.ts +5 -0
- package/dist/storefront.js +7 -0
- package/package.json +35 -5
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The public type surface of the client runtime — `@redocly/client-generator`'s
|
|
3
|
+
* app-facing runtime module. Pure types, no runtime code (excluded from coverage).
|
|
4
|
+
* The generator emits `OPERATIONS` literals typed
|
|
5
|
+
* `satisfies Record<string, OperationDescriptor>` against this module, so an
|
|
6
|
+
* incompatible runtime/generated pair fails the consumer's build (the semver skew guard).
|
|
7
|
+
*/
|
|
8
|
+
/** How one operation parameter is sent: its location plus OpenAPI query-serialization hints. */
|
|
9
|
+
export type ParamSpec = {
|
|
10
|
+
name: string;
|
|
11
|
+
in: 'path' | 'query' | 'header' | 'cookie';
|
|
12
|
+
style?: 'form' | 'spaceDelimited' | 'pipeDelimited' | 'deepObject';
|
|
13
|
+
explode?: boolean;
|
|
14
|
+
allowReserved?: boolean;
|
|
15
|
+
};
|
|
16
|
+
/** One security scheme, denormalized onto the operation (`scheme` names the spec's scheme). */
|
|
17
|
+
export type SecuritySpec = {
|
|
18
|
+
scheme: string;
|
|
19
|
+
kind: 'bearer' | 'basic';
|
|
20
|
+
} | {
|
|
21
|
+
scheme: string;
|
|
22
|
+
kind: 'apiKey';
|
|
23
|
+
name: string;
|
|
24
|
+
in: 'header' | 'query' | 'cookie';
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* How to auto-iterate a paginated operation (drives its `.pages()`/`.items()` members).
|
|
28
|
+
* `nextCursor`, `nextLink`, and `items` are RFC 6901 JSON pointers into the page (response) value.
|
|
29
|
+
*/
|
|
30
|
+
export type PaginationSpec = {
|
|
31
|
+
style: 'cursor';
|
|
32
|
+
/** The query param the iterator advances with the response's cursor. */
|
|
33
|
+
param: string;
|
|
34
|
+
/** Optional page-size query param (recorded for tooling; never set by the runtime). */
|
|
35
|
+
limitParam?: string;
|
|
36
|
+
/** Pointer to the next cursor in the page. */
|
|
37
|
+
nextCursor: string;
|
|
38
|
+
/** Optional pointer to a boolean "more pages" flag — `false` stops iteration. */
|
|
39
|
+
hasMore?: string;
|
|
40
|
+
/** Pointer to the page's item array. */
|
|
41
|
+
items: string;
|
|
42
|
+
} | {
|
|
43
|
+
style: 'offset' | 'page';
|
|
44
|
+
/** The numeric query param the iterator advances. */
|
|
45
|
+
param: string;
|
|
46
|
+
/** Optional page-size query param (recorded for tooling; never set by the runtime). */
|
|
47
|
+
limitParam?: string;
|
|
48
|
+
/** Pointer to the page's item array. */
|
|
49
|
+
items: string;
|
|
50
|
+
} | {
|
|
51
|
+
/** RFC 8288: follow the response's `Link` header `rel="next"`; stop when absent. */
|
|
52
|
+
style: 'link';
|
|
53
|
+
/** Optional page-size query param (recorded for tooling; never set by the runtime). */
|
|
54
|
+
limitParam?: string;
|
|
55
|
+
/** Pointer to the page's item array. */
|
|
56
|
+
items: string;
|
|
57
|
+
} | {
|
|
58
|
+
/** Follow the next page's URL at `nextLink` in the page; stop when it is absent. */
|
|
59
|
+
style: 'body-link';
|
|
60
|
+
/** Optional page-size query param (recorded for tooling; never set by the runtime). */
|
|
61
|
+
limitParam?: string;
|
|
62
|
+
/** Pointer to the next page's URL in the page. */
|
|
63
|
+
nextLink: string;
|
|
64
|
+
/** Pointer to the page's item array. */
|
|
65
|
+
items: string;
|
|
66
|
+
};
|
|
67
|
+
/** The frozen data contract between generated code and the runtime: one operation's wire shape. */
|
|
68
|
+
export type OperationDescriptor = {
|
|
69
|
+
id: string;
|
|
70
|
+
method: string;
|
|
71
|
+
path: string;
|
|
72
|
+
tags?: readonly string[];
|
|
73
|
+
params?: readonly ParamSpec[];
|
|
74
|
+
/** `multipart: true` marks a typed object body serialized to FormData by the runtime. */
|
|
75
|
+
body?: {
|
|
76
|
+
contentType: string;
|
|
77
|
+
multipart?: boolean;
|
|
78
|
+
};
|
|
79
|
+
/** Defaults to `'json'` (content-type negotiation on parse). */
|
|
80
|
+
responseKind?: 'json' | 'text' | 'blob' | 'void' | 'sse';
|
|
81
|
+
sseDataKind?: 'json' | 'text';
|
|
82
|
+
/** OR-alternatives, each an AND-set: the runtime applies the first fully-configured one. */
|
|
83
|
+
security?: readonly (readonly SecuritySpec[])[];
|
|
84
|
+
pagination?: PaginationSpec;
|
|
85
|
+
/**
|
|
86
|
+
* `'grouped'` marks an operation that takes its inputs namespaced by layer even on a
|
|
87
|
+
* `argsStyle: 'flat'` client — the generator sets it where a merged call could not carry
|
|
88
|
+
* one name for two layers, and the operation's own input type says the same.
|
|
89
|
+
*/
|
|
90
|
+
argsStyle?: 'grouped';
|
|
91
|
+
/**
|
|
92
|
+
* Declared success-response headers for throw-mode `{ envelope: true }`.
|
|
93
|
+
* `name` is the lowercased wire name; `key` is the camelCase envelope property.
|
|
94
|
+
*/
|
|
95
|
+
responseHeaders?: readonly ResponseHeaderSpec[];
|
|
96
|
+
};
|
|
97
|
+
/** One declared response header the runtime coerces into the envelope `headers` object. */
|
|
98
|
+
export type ResponseHeaderSpec = {
|
|
99
|
+
name: string;
|
|
100
|
+
key: string;
|
|
101
|
+
type: 'string' | 'number' | 'boolean';
|
|
102
|
+
};
|
|
103
|
+
/** A query value: scalars, arrays of scalars, or objects (serialized as deepObject brackets). */
|
|
104
|
+
export type QueryValue = string | number | boolean | null | undefined | Array<string | number | boolean | null | undefined> | Record<string, unknown>;
|
|
105
|
+
/** A credential: a literal, or a (possibly async) function resolved per request (refresh flows). */
|
|
106
|
+
export type TokenProvider = string | (() => string | Promise<string>);
|
|
107
|
+
/** Per-instance credentials, keyed by the scheme kinds the runtime can inject. */
|
|
108
|
+
export type AuthCredentials = {
|
|
109
|
+
bearer?: TokenProvider;
|
|
110
|
+
basic?: {
|
|
111
|
+
username: string;
|
|
112
|
+
password: string;
|
|
113
|
+
};
|
|
114
|
+
apiKey?: Record<string, TokenProvider>;
|
|
115
|
+
};
|
|
116
|
+
/** Backoff shape: 'fixed' = constant delay; 'exponential' = doubling per attempt. */
|
|
117
|
+
export type RetryStrategy = 'fixed' | 'exponential';
|
|
118
|
+
/**
|
|
119
|
+
* The operation's identity, exposed to middleware for targeting (`ctx.operation`).
|
|
120
|
+
* Generated clients instantiate the type parameters with the spec's literal unions
|
|
121
|
+
* (`OperationId`/`OperationPath`/`OperationTag`) so a misspelled operation id in a
|
|
122
|
+
* middleware comparison fails to compile; the string defaults keep every
|
|
123
|
+
* spec-independent consumer (`runtime-contract.ts`, the runtime internals) working
|
|
124
|
+
* with the base shape. `tags` stays mutable (`Tag[]`) so setup-contract types
|
|
125
|
+
* (byte-locked to generated output) remain assignable through middleware callbacks.
|
|
126
|
+
*/
|
|
127
|
+
export type OperationContext<Id extends string = string, Path extends string = string, Tag extends string = string> = {
|
|
128
|
+
id: Id;
|
|
129
|
+
path: Path;
|
|
130
|
+
tags: Tag[];
|
|
131
|
+
};
|
|
132
|
+
/** The mutable request context threaded through the middleware chain. */
|
|
133
|
+
export type RequestContext<Op extends OperationContext = OperationContext> = {
|
|
134
|
+
url: string;
|
|
135
|
+
method: string;
|
|
136
|
+
headers: Record<string, string>;
|
|
137
|
+
body?: unknown;
|
|
138
|
+
operation: Op;
|
|
139
|
+
};
|
|
140
|
+
/** The failed attempt handed to a custom `retryOn`: exactly one of `response`/`error` is set. */
|
|
141
|
+
export type RetryContext<Op extends OperationContext = OperationContext> = {
|
|
142
|
+
attempt: number;
|
|
143
|
+
request: RequestContext<Op>;
|
|
144
|
+
response?: Response;
|
|
145
|
+
error?: unknown;
|
|
146
|
+
};
|
|
147
|
+
/** Opt-in retry policy; a per-call override merges field-by-field over the config policy. */
|
|
148
|
+
export type RetryConfig<Op extends OperationContext = OperationContext> = {
|
|
149
|
+
retries?: number;
|
|
150
|
+
retryDelay?: number;
|
|
151
|
+
retryStrategy?: RetryStrategy;
|
|
152
|
+
jitter?: boolean;
|
|
153
|
+
retryOn?: (ctx: RetryContext<Op>) => boolean | Promise<boolean>;
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Structural stand-in for the runtime's ApiError so this module stays import-free
|
|
157
|
+
* (pure types); the real `ApiError` class is assignable to it.
|
|
158
|
+
*/
|
|
159
|
+
export type ApiErrorLike = globalThis.Error & {
|
|
160
|
+
url: string;
|
|
161
|
+
status: number;
|
|
162
|
+
statusText: string;
|
|
163
|
+
body: unknown;
|
|
164
|
+
};
|
|
165
|
+
export type SettledCall = {
|
|
166
|
+
response?: Response;
|
|
167
|
+
error?: unknown;
|
|
168
|
+
attempts: number;
|
|
169
|
+
durationMs: number;
|
|
170
|
+
};
|
|
171
|
+
/** One interceptor: any subset of the four hooks. */
|
|
172
|
+
export type Middleware<Op extends OperationContext = OperationContext> = {
|
|
173
|
+
onRequest?: (ctx: RequestContext<Op>) => void | Promise<void>;
|
|
174
|
+
onResponse?: (response: Response, ctx: RequestContext<Op>) => Response | void | Promise<Response | void>;
|
|
175
|
+
/** Throw mode only: may map/replace the error. */
|
|
176
|
+
onError?: (error: ApiErrorLike, ctx: RequestContext<Op>) => globalThis.Error | Promise<globalThis.Error>;
|
|
177
|
+
onSettled?: (ctx: RequestContext<Op>, call: SettledCall) => void | Promise<void>;
|
|
178
|
+
};
|
|
179
|
+
/** Client configuration: transport, defaults, retry policy, middleware, and credentials. */
|
|
180
|
+
export type ClientConfig<Op extends OperationContext = OperationContext> = {
|
|
181
|
+
serverUrl?: string;
|
|
182
|
+
fetch?: typeof fetch;
|
|
183
|
+
headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
|
|
184
|
+
retry?: RetryConfig<Op>;
|
|
185
|
+
/** Milliseconds before a request attempt aborts (covers the body read too; each retry
|
|
186
|
+
* attempt gets a fresh budget). Per-call `timeout` overrides it, `0` disables it.
|
|
187
|
+
* SSE streams are long-lived by design and never inherit this value. */
|
|
188
|
+
timeout?: number;
|
|
189
|
+
/** Send an `Idempotency-Key` header on POST/PATCH (one stable key per logical call,
|
|
190
|
+
* reused across retry attempts) — which also makes those retries safe under the
|
|
191
|
+
* default retry policy. `true` generates a UUID per call; a function supplies the key. */
|
|
192
|
+
idempotencyKey?: boolean | (() => string);
|
|
193
|
+
/** Identifies this client to the API via an `X-Redocly-Client` header (the generator
|
|
194
|
+
* bakes a default). Sent only OUTSIDE browsers — a custom header would force a CORS
|
|
195
|
+
* preflight. Override with your own value, or `false` to disable. */
|
|
196
|
+
clientHeader?: string | false;
|
|
197
|
+
middleware?: Middleware<Op>[];
|
|
198
|
+
auth?: AuthCredentials;
|
|
199
|
+
/** Fixed at generate time by the generator (`'throw'` when omitted); `configure()` ignores it. */
|
|
200
|
+
errorMode?: 'throw' | 'result';
|
|
201
|
+
/**
|
|
202
|
+
* How each call spells its inputs: `'grouped'` (the default) namespaces them by layer —
|
|
203
|
+
* `{ path, query, headers, cookies, body }` — and `'flat'` takes one merged object.
|
|
204
|
+
* Fixed at generate time, like `errorMode`, because it shapes the static types.
|
|
205
|
+
*/
|
|
206
|
+
argsStyle?: 'grouped' | 'flat';
|
|
207
|
+
onRequest?: Middleware<Op>['onRequest'];
|
|
208
|
+
onResponse?: Middleware<Op>['onResponse'];
|
|
209
|
+
onError?: Middleware<Op>['onError'];
|
|
210
|
+
};
|
|
211
|
+
/** Response readers for the per-call `parseAs` override. */
|
|
212
|
+
export type ParseAs = 'auto' | 'json' | 'text' | 'blob' | 'arrayBuffer' | 'formData' | 'stream';
|
|
213
|
+
/** Per-call options: standard `RequestInit` plus a retry override, a timeout override
|
|
214
|
+
* (`0` disables the config default), and a forced reader. */
|
|
215
|
+
export type RequestOptions = RequestInit & {
|
|
216
|
+
retry?: RetryConfig;
|
|
217
|
+
timeout?: number;
|
|
218
|
+
/** Per-call idempotency key: a literal key, `true` to generate one, `false` to skip. */
|
|
219
|
+
idempotencyKey?: string | boolean | (() => string);
|
|
220
|
+
parseAs?: ParseAs;
|
|
221
|
+
/**
|
|
222
|
+
* Throw mode only: return `{ data, headers, response }` instead of the parsed body;
|
|
223
|
+
* ignored in result mode. The explicit `| undefined` keeps the wrappers' emitted
|
|
224
|
+
* `envelope: undefined` strip legal under `exactOptionalPropertyTypes`.
|
|
225
|
+
*/
|
|
226
|
+
envelope?: boolean | undefined;
|
|
227
|
+
};
|
|
228
|
+
/** Throw-mode success envelope when `RequestOptions.envelope` is `true`. */
|
|
229
|
+
export type Envelope<TData, THeaders = Record<string, never>> = {
|
|
230
|
+
data: TData;
|
|
231
|
+
headers: THeaders;
|
|
232
|
+
response: Response;
|
|
233
|
+
};
|
|
234
|
+
/** Per-call options for an SSE stream; reconnect defaults to true. */
|
|
235
|
+
export type SseOptions = RequestInit & {
|
|
236
|
+
reconnect?: boolean;
|
|
237
|
+
reconnectDelay?: number;
|
|
238
|
+
};
|
|
239
|
+
/** A single decoded Server-Sent Event with its payload typed from the spec. */
|
|
240
|
+
export type ServerSentEvent<T> = {
|
|
241
|
+
event?: string;
|
|
242
|
+
data: T;
|
|
243
|
+
id?: string;
|
|
244
|
+
retry?: number;
|
|
245
|
+
};
|
|
246
|
+
/** Result-mode return shape: exactly one of `data`/`error` is set. */
|
|
247
|
+
export type Result<TData, TError> = {
|
|
248
|
+
data: TData;
|
|
249
|
+
error: undefined;
|
|
250
|
+
response: Response;
|
|
251
|
+
} | {
|
|
252
|
+
data: undefined;
|
|
253
|
+
error: TError;
|
|
254
|
+
response: Response;
|
|
255
|
+
};
|
|
256
|
+
/**
|
|
257
|
+
* The generated `Ops` type's shape: per-operation args/result, plus `kind: 'sse'` for
|
|
258
|
+
* streams and, for paginated operations, `item` (the page's element type) and — on
|
|
259
|
+
* result-mode clients only — `page` (the RAW page type `.pages()` yields, since
|
|
260
|
+
* iteration unwraps the `Result` envelope the one-shot `result` carries).
|
|
261
|
+
*/
|
|
262
|
+
export type OpsShape = Record<string, {
|
|
263
|
+
args: object;
|
|
264
|
+
result: unknown;
|
|
265
|
+
kind?: 'sse';
|
|
266
|
+
item?: unknown;
|
|
267
|
+
page?: unknown;
|
|
268
|
+
/** Declared success-response headers for `{ envelope: true }` (camelCase keys). */
|
|
269
|
+
headers?: object;
|
|
270
|
+
/** Result-mode entries ignore the throw-only `envelope` option. */
|
|
271
|
+
mode?: 'result';
|
|
272
|
+
}>;
|
|
273
|
+
/** The always-present client members (assigned after the operation loop — they win collisions). */
|
|
274
|
+
export type ClientCore<Op extends OperationContext = OperationContext> = {
|
|
275
|
+
/** Merge into the config; note `middleware` REPLACES the chain (use `use()` to compose). */
|
|
276
|
+
configure(config: ClientConfig<Op>): void;
|
|
277
|
+
/** Append interceptors (composes with baked/publisher middleware). */
|
|
278
|
+
use(...middleware: Middleware<Op>[]): void;
|
|
279
|
+
auth: {
|
|
280
|
+
bearer(token: TokenProvider): void;
|
|
281
|
+
basic(username: string, password: string): void;
|
|
282
|
+
apiKey(scheme: string, value: TokenProvider): void;
|
|
283
|
+
};
|
|
284
|
+
};
|
|
285
|
+
/**
|
|
286
|
+
* The standard TypeScript optionality probe: `{}` has no required members, so
|
|
287
|
+
* `{} extends A` is true exactly when every member of `A` is optional.
|
|
288
|
+
*/
|
|
289
|
+
type NoRequiredKeys<A> = {} extends A ? true : false;
|
|
290
|
+
/**
|
|
291
|
+
* The page type `.pages()` yields: the RAW page declared by `page` (the generator
|
|
292
|
+
* writes it only on result-mode paginated entries, whose `result` is the envelope),
|
|
293
|
+
* or the method's own `result` (throw mode — already the raw page).
|
|
294
|
+
*/
|
|
295
|
+
type PageOf<Entry extends OpsShape[string]> = Entry extends {
|
|
296
|
+
page: unknown;
|
|
297
|
+
} ? Entry['page'] : Entry['result'];
|
|
298
|
+
/**
|
|
299
|
+
* The auto-pagination members intersected onto a paginated method — present exactly when
|
|
300
|
+
* the Ops entry declares `item` (the generator writes it only for paginated operations).
|
|
301
|
+
* Args optionality mirrors the method's own; `unknown` otherwise (identity under `&`).
|
|
302
|
+
* Iteration is error-mode-agnostic: `.pages()`/`.items()` yield raw pages/items, and a
|
|
303
|
+
* failed page aborts iteration by throwing `ApiError`, even on result-mode clients; the
|
|
304
|
+
* `onError` middleware hook (throw-mode-only) is not invoked.
|
|
305
|
+
*/
|
|
306
|
+
type Paginated<Entry extends OpsShape[string]> = 'item' extends keyof Entry ? NoRequiredKeys<Entry['args']> extends true ? {
|
|
307
|
+
pages(args?: Entry['args'], init?: RequestOptions): AsyncGenerator<PageOf<Entry>>;
|
|
308
|
+
items(args?: Entry['args'], init?: RequestOptions): AsyncGenerator<Entry['item']>;
|
|
309
|
+
} : {
|
|
310
|
+
pages(args: Entry['args'], init?: RequestOptions): AsyncGenerator<PageOf<Entry>>;
|
|
311
|
+
items(args: Entry['args'], init?: RequestOptions): AsyncGenerator<Entry['item']>;
|
|
312
|
+
} : unknown;
|
|
313
|
+
/**
|
|
314
|
+
* The stable identity every client method carries: the SPEC operationId (also set as
|
|
315
|
+
* `fn.name`, but `operationId` is the explicit, minification-proof form) — a robust
|
|
316
|
+
* cache key for consumer wrappers (react-query keys and the like).
|
|
317
|
+
*/
|
|
318
|
+
export type OperationMethodIdentity = {
|
|
319
|
+
readonly operationId: string;
|
|
320
|
+
};
|
|
321
|
+
/** Declared response-header bag for an Ops entry; empty object when none are declared. */
|
|
322
|
+
type HeadersOf<Entry extends OpsShape[string]> = 'headers' extends keyof Entry ? NonNullable<Entry['headers']> : Record<string, never>;
|
|
323
|
+
/**
|
|
324
|
+
* Return type of a throw-mode call: the body by default, `Envelope<…>` for a literal
|
|
325
|
+
* `envelope: true`, their union when the flag is a widened `boolean`. Exact
|
|
326
|
+
* `RequestOptions` stays the body — pre-envelope package-mode flat sugar typed every
|
|
327
|
+
* `init` parameter as `RequestOptions`, and widening that would break upgrades without
|
|
328
|
+
* a regenerate. The `keyof` presence gate keeps `{ headers }` / `{ signal }` as the body
|
|
329
|
+
* (`TInit['envelope']` through `TInit & RequestOptions` would otherwise be
|
|
330
|
+
* `boolean | undefined`).
|
|
331
|
+
*/
|
|
332
|
+
export type EnvelopeResult<TData, THeaders, TInit extends RequestOptions | undefined> = TInit extends undefined ? TData : RequestOptions extends TInit ? TInit extends RequestOptions ? TData : EnvelopeResultForKnownInit<TData, THeaders, TInit> : EnvelopeResultForKnownInit<TData, THeaders, TInit>;
|
|
333
|
+
type EnvelopeResultForKnownInit<TData, THeaders, TInit> = 'envelope' extends keyof TInit ? [TInit['envelope' & keyof TInit]] extends [true] ? Envelope<TData, THeaders> : [TInit['envelope' & keyof TInit]] extends [false | undefined] ? TData : TData | Envelope<TData, THeaders> : TData;
|
|
334
|
+
/** A one-shot method whose return shape never varies with per-call options. */
|
|
335
|
+
type BodyMethod<Entry extends OpsShape[string]> = NoRequiredKeys<Entry['args']> extends true ? (args?: Entry['args'], init?: RequestOptions) => Promise<Entry['result']> : (args: Entry['args'], init?: RequestOptions) => Promise<Entry['result']>;
|
|
336
|
+
/**
|
|
337
|
+
* One-shot (non-SSE) method: default returns the body; `{ envelope: true }` returns
|
|
338
|
+
* `{ data, headers, response }` with typed declared headers.
|
|
339
|
+
*/
|
|
340
|
+
type ThrowMethod<Entry extends OpsShape[string]> = NoRequiredKeys<Entry['args']> extends true ? <Init extends RequestOptions | undefined = undefined>(args?: Entry['args'], init?: Init) => Promise<EnvelopeResult<Entry['result'], HeadersOf<Entry>, Init>> : <Init extends RequestOptions | undefined = undefined>(args: Entry['args'], init?: Init) => Promise<EnvelopeResult<Entry['result'], HeadersOf<Entry>, Init>>;
|
|
341
|
+
/** The typed instance client: one bound method per operation plus the core members. */
|
|
342
|
+
export type Client<Ops extends OpsShape, Op extends OperationContext = OperationContext> = {
|
|
343
|
+
[K in keyof Ops]: Ops[K] extends {
|
|
344
|
+
kind: 'sse';
|
|
345
|
+
} ? (NoRequiredKeys<Ops[K]['args']> extends true ? (args?: Ops[K]['args'], init?: SseOptions) => AsyncGenerator<ServerSentEvent<Ops[K]['result']>> : (args: Ops[K]['args'], init?: SseOptions) => AsyncGenerator<ServerSentEvent<Ops[K]['result']>>) & OperationMethodIdentity : (Ops[K] extends {
|
|
346
|
+
mode: 'result';
|
|
347
|
+
} ? BodyMethod<Ops[K]> : ThrowMethod<Ops[K]>) & OperationMethodIdentity & Paginated<Ops[K]>;
|
|
348
|
+
} & ClientCore<Op>;
|
|
349
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ParamSpec, QueryValue } from './types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* The RESOLVED OpenAPI serialization spec for one query parameter — callers apply the
|
|
4
|
+
* OpenAPI defaults (`style: 'form'`, `explode: true`) before building one.
|
|
5
|
+
*/
|
|
6
|
+
export type QueryStyle = {
|
|
7
|
+
style: NonNullable<ParamSpec['style']>;
|
|
8
|
+
explode: boolean;
|
|
9
|
+
allowReserved?: boolean;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Encode everything except the RFC-3986 reserved set, for `allowReserved: true` params —
|
|
13
|
+
* `filter=a/b` survives instead of `filter=a%2Fb`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function encodeReserved(value: string): string;
|
|
16
|
+
/** Substitute `{name}` template segments with encoded values; a missing value is a caller bug. */
|
|
17
|
+
export declare function substitutePath(template: string, values: Record<string, unknown>): string;
|
|
18
|
+
/**
|
|
19
|
+
* Build the request URL: `serverUrl` (trailing slash trimmed) + path + serialized query.
|
|
20
|
+
* Query parameters honor their OpenAPI `style`/`explode`/`allowReserved` (from `styles`);
|
|
21
|
+
* without a spec, arrays repeat the key (`form`+`explode`), objects serialize as
|
|
22
|
+
* `deepObject` brackets, and `null`/`undefined` entries are skipped.
|
|
23
|
+
*/
|
|
24
|
+
export declare function buildUrl(serverUrl: string, path: string, query?: Record<string, QueryValue>, styles?: Record<string, QueryStyle>): string;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
//#region src/runtime/url.ts
|
|
2
|
+
/**
|
|
3
|
+
* Encode everything except the RFC-3986 reserved set, for `allowReserved: true` params —
|
|
4
|
+
* `filter=a/b` survives instead of `filter=a%2Fb`.
|
|
5
|
+
*/
|
|
6
|
+
function encodeReserved(value) {
|
|
7
|
+
return encodeURIComponent(value).replace(/%(3A|2F|3F|23|5B|5D|40|21|24|26|27|28|29|2A|2B|2C|3B|3D)/g, (match) => decodeURIComponent(match));
|
|
8
|
+
}
|
|
9
|
+
/** Substitute `{name}` template segments with encoded values; a missing value is a caller bug. */
|
|
10
|
+
function substitutePath(template, values) {
|
|
11
|
+
return template.replace(/\{([^{}]+)\}/g, (_match, name) => {
|
|
12
|
+
const value = values[name];
|
|
13
|
+
if (value === void 0) throw new Error(`Missing path parameter "${name}"`);
|
|
14
|
+
return encodeURIComponent(String(value));
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Build the request URL: `serverUrl` (trailing slash trimmed) + path + serialized query.
|
|
19
|
+
* Query parameters honor their OpenAPI `style`/`explode`/`allowReserved` (from `styles`);
|
|
20
|
+
* without a spec, arrays repeat the key (`form`+`explode`), objects serialize as
|
|
21
|
+
* `deepObject` brackets, and `null`/`undefined` entries are skipped.
|
|
22
|
+
*/
|
|
23
|
+
function buildUrl(serverUrl, path, query, styles) {
|
|
24
|
+
let end = serverUrl.length;
|
|
25
|
+
while (end > 0 && serverUrl.charCodeAt(end - 1) === 47) end--;
|
|
26
|
+
const url = serverUrl.slice(0, end) + path;
|
|
27
|
+
if (!query) return url;
|
|
28
|
+
const params = new URLSearchParams();
|
|
29
|
+
const raw = [];
|
|
30
|
+
for (const [key, value] of Object.entries(query)) {
|
|
31
|
+
if (value === void 0 || value === null) continue;
|
|
32
|
+
const spec = styles?.[key];
|
|
33
|
+
if (!spec) {
|
|
34
|
+
if (Array.isArray(value)) {
|
|
35
|
+
for (const v of value) if (v !== void 0 && v !== null) params.append(key, String(v));
|
|
36
|
+
} else if (Object(value) === value) {
|
|
37
|
+
for (const [subKey, subValue] of Object.entries(value)) if (subValue !== void 0 && subValue !== null) params.append(`${key}[${subKey}]`, String(subValue));
|
|
38
|
+
} else params.append(key, String(value));
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (Array.isArray(value)) {
|
|
42
|
+
const items = value.filter((v) => v !== void 0 && v !== null).map(String);
|
|
43
|
+
if (spec.style === "form" && spec.explode) for (const v of items) if (spec.allowReserved) raw.push(`${key}=${encodeReserved(v)}`);
|
|
44
|
+
else params.append(key, v);
|
|
45
|
+
else {
|
|
46
|
+
const delim = spec.style === "pipeDelimited" ? "|" : spec.style === "spaceDelimited" ? "%20" : ",";
|
|
47
|
+
const enc = spec.allowReserved ? encodeReserved : encodeURIComponent;
|
|
48
|
+
raw.push(`${encodeURIComponent(key)}=${items.map(enc).join(delim)}`);
|
|
49
|
+
}
|
|
50
|
+
} else if (Object(value) === value) {
|
|
51
|
+
for (const [subKey, subValue] of Object.entries(value)) if (subValue !== void 0 && subValue !== null) {
|
|
52
|
+
if (spec.allowReserved) raw.push(`${key}[${subKey}]=${encodeReserved(String(subValue))}`);
|
|
53
|
+
else params.append(`${key}[${subKey}]`, String(subValue));
|
|
54
|
+
}
|
|
55
|
+
} else if (spec.allowReserved) raw.push(`${key}=${encodeReserved(String(value))}`);
|
|
56
|
+
else params.append(key, String(value));
|
|
57
|
+
}
|
|
58
|
+
const qs = [params.toString(), ...raw].filter(Boolean).join("&");
|
|
59
|
+
return qs ? `${url}?${qs}` : url;
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
export { buildUrl, encodeReserved, substitutePath };
|