@dynamicforms/fastapi-viewsets 0.5.6 → 0.6.0

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/dist/index.d.ts CHANGED
@@ -1,11 +1,695 @@
1
- export { BulkCreateMixin, BulkDestroyMixin, BulkOnlyCreateMixin, BulkOnlyDestroyMixin, BulkOnlyUpdateMixin, BulkUpdateMixin, BulkViewSetMixin, CreateMixin, CursorListMixin, DestroyMixin, ListMixin, LookupMixin, PaginatedListMixin, ReadOnlyViewSetMixin, RetrieveMixin, UpdateMixin, ViewSetMixin, } from './mixins';
2
- export type { ActionName, ActionSurface, CursorPage, DestroyReturnData, CursorParams, KeyType, ListParams, LookupItem, PageParams, PaginatedList, } from './mixins';
3
- export type { HttpMethod, ProxyBaseOptions, QueryParams, RequestOptions, ViewSetMixinDeclaration } from './proxy-base';
4
- export { ViewSetInternals, ViewSetProxyBase, ViewSetRequestError } from './proxy-base';
5
- export type { PkFieldName, ViewSetClass, ViewSetMixinClass } from './viewset';
6
- export { muxwsViewSet, restViewSet } from './viewset';
7
- export type { RestProxy, RestProxyOptions } from './rest-proxy';
8
- export { route_rest, RestProxyImpl } from './rest-proxy';
9
- export type { MuxwsPeerLike, MuxwsPeerSource, MuxwsProxy, MuxwsProxyOptions, MuxwsStreamLike } from './muxws-proxy';
10
- export { route_muxws, MuxwsProxyImpl } from './muxws-proxy';
11
- //# sourceMappingURL=index.d.ts.map
1
+ import { AxiosInstance } from 'axios';
2
+ import { TranslateStringsCallback } from '@dynamicforms/translatable';
3
+
4
+ /** Every action name there is: `A = string` selects all of them. */
5
+ export declare type ActionName = keyof ActionSurface<KeyType_2, unknown, never, string>;
6
+
7
+ /**
8
+ * The actions a mixin class names, read off its instance type.
9
+ *
10
+ * The instance type rather than the `actions` tuple, because a tuple would have to be `as const` on
11
+ * every mixin, and a `readonly ['create']` static cannot then be inherited by a composite whose own
12
+ * static is a different tuple (TS2417). `D` is naked so that a union of mixins distributes.
13
+ */
14
+ declare type ActionsOf<D> = D extends abstract new (...args: any[]) => infer I ? Extract<keyof I, ActionName> : never;
15
+
16
+ /**
17
+ * The actions named by `A`, each with the signature the mixin that contributes it declares. One
18
+ * conditional per single-action mixin; the composites are absent on purpose, so that any action's
19
+ * signature is written in exactly one place.
20
+ *
21
+ * An intersection rather than `Pick<>` of a table: a mapped type turns a method into a property,
22
+ * and a subclass cannot then override an action with a method (TS2425).
23
+ */
24
+ export declare type ActionSurface<K extends KeyType_2, T, PK extends keyof T, A> = ('create' extends A ? CreateMixin<T, PK> : unknown) & ('bulkCreate' extends A ? BulkOnlyCreateMixin<T, PK> : unknown) & ('list' extends A ? ListMixin<T> : unknown) & ('listPage' extends A ? PaginatedListMixin<T> : unknown) & ('listCursor' extends A ? CursorListMixin<T> : unknown) & ('retrieve' extends A ? RetrieveMixin<K, T> : unknown) & ('update' extends A ? UpdateMixin<K, T> : unknown) & ('bulkUpdate' extends A ? BulkOnlyUpdateMixin<K, T> : unknown) & ('destroy' extends A ? DestroyMixin<K> : unknown) & ('bulkDestroy' extends A ? BulkOnlyDestroyMixin<K> : unknown) & ('lookup' extends A ? LookupMixin : unknown);
25
+
26
+ /**
27
+ * A failed request's response body. `detail` is always a plain string - unchanged from what it has
28
+ * always been. `detail_code` and `detail_params` are additive, and appear only when the server has
29
+ * registered `df_viewset_exception_handler` (see the Python side's `fastapi_viewsets.exceptions`)
30
+ * for one of this package's own built-in errors; a view's own `raise HTTPException(status_code,
31
+ * detail="...")` never carries them.
32
+ */
33
+ export declare interface ApiErrorBody {
34
+ detail: string;
35
+ detail_code?: string;
36
+ detail_params?: Record<string, unknown>;
37
+ }
38
+
39
+ export declare interface BulkCreateMixin<T, PK extends keyof T> extends CreateMixin<T, PK>, BulkOnlyCreateMixin<T, PK> {
40
+ }
41
+
42
+ export declare class BulkCreateMixin<T, PK extends keyof T> extends CreateMixin<T, PK> {
43
+ static readonly actions: readonly string[];
44
+ }
45
+
46
+ export declare interface BulkDestroyMixin<K extends KeyType_2> extends DestroyMixin<K>, BulkOnlyDestroyMixin<K> {
47
+ }
48
+
49
+ export declare class BulkDestroyMixin<K extends KeyType_2> extends DestroyMixin<K> {
50
+ static readonly actions: readonly string[];
51
+ }
52
+
53
+ export declare interface BulkOnlyCreateMixin<T, PK extends keyof T> {
54
+ bulkCreate(data: Omit<T, PK>[]): Promise<T[]>;
55
+ }
56
+
57
+ export declare class BulkOnlyCreateMixin<T, PK extends keyof T> {
58
+ static readonly actions: readonly string[];
59
+ }
60
+
61
+ export declare interface BulkOnlyDestroyMixin<K extends KeyType_2> {
62
+ bulkDestroy(pks: K[]): Promise<DestroyReturnData[]>;
63
+ }
64
+
65
+ export declare class BulkOnlyDestroyMixin<K extends KeyType_2> {
66
+ static readonly actions: readonly string[];
67
+ }
68
+
69
+ export declare interface BulkOnlyUpdateMixin<K extends KeyType_2, T> {
70
+ bulkUpdate(records: Record<K, T>): Promise<T[]>;
71
+ bulkPartialUpdate(records: Record<K, Partial<T>>): Promise<T[]>;
72
+ }
73
+
74
+ export declare class BulkOnlyUpdateMixin<K extends KeyType_2, T> {
75
+ static readonly actions: readonly string[];
76
+ }
77
+
78
+ export declare interface BulkUpdateMixin<K extends KeyType_2, T> extends UpdateMixin<K, T>, BulkOnlyUpdateMixin<K, T> {
79
+ }
80
+
81
+ export declare class BulkUpdateMixin<K extends KeyType_2, T> extends UpdateMixin<K, T> {
82
+ static readonly actions: readonly string[];
83
+ }
84
+
85
+ export declare interface BulkViewSetMixin<K extends KeyType_2, T, PK extends keyof T> extends ViewSetMixin<K, T, PK>, BulkOnlyCreateMixin<T, PK>, BulkOnlyUpdateMixin<K, T>, BulkOnlyDestroyMixin<K> {
86
+ }
87
+
88
+ export declare class BulkViewSetMixin<K extends KeyType_2, T, PK extends keyof T> extends ViewSetMixin<K, T, PK> {
89
+ static readonly actions: readonly string[];
90
+ }
91
+
92
+ export declare interface CreateMixin<T, PK extends keyof T> {
93
+ create(data: Omit<T, PK>): Promise<T>;
94
+ }
95
+
96
+ export declare class CreateMixin<T, PK extends keyof T> {
97
+ static readonly actions: readonly string[];
98
+ }
99
+
100
+ /** FE counterpart of the BE CursorListMixin. See PaginatedListMixin on declaring several shapes. */
101
+ export declare interface CursorListMixin<T> {
102
+ listCursor(params?: CursorParams): Promise<CursorPage<T>>;
103
+ }
104
+
105
+ export declare class CursorListMixin<T> {
106
+ static readonly actions: readonly string[];
107
+ }
108
+
109
+ /**
110
+ * One cursor page.
111
+ *
112
+ * `next`/`previous` are exclusive, so following them never repeats a row. `first`/`last` are the
113
+ * same two edges read inclusively: they return their own row again — one duplicate to drop — and
114
+ * in exchange they survive rows being inserted at that edge, which is what polling a live list
115
+ * needs. They are present whenever the page is non-empty, even when `next` is null.
116
+ *
117
+ * There is no total count: producing one costs a second full pass per request and is stale by the
118
+ * time it is read.
119
+ */
120
+ export declare interface CursorPage<T> {
121
+ results: T[];
122
+ limit: number;
123
+ hasMore: boolean;
124
+ hasPrevious: boolean;
125
+ next: string | null;
126
+ previous: string | null;
127
+ first: string | null;
128
+ last: string | null;
129
+ }
130
+
131
+ export declare interface CursorParams extends ListParams {
132
+ cursor?: string;
133
+ limit?: number;
134
+ }
135
+
136
+ export declare interface DestroyMixin<K extends KeyType_2> {
137
+ destroy(pk: K): Promise<DestroyReturnData>;
138
+ }
139
+
140
+ export declare class DestroyMixin<K extends KeyType_2> {
141
+ static readonly actions: readonly string[];
142
+ }
143
+
144
+ export declare type DestroyReturnData = Record<KeyType_2, any>;
145
+
146
+ /**
147
+ * Marks a class's `declares` as coming from the ViewSet factory (viewset.ts's `FactoryDeclares<D>`)
148
+ * rather than being hand-written. Declared here, the module both viewset.ts and the two proxy
149
+ * implementations already import from, so all three sides see the same `unique symbol` and a
150
+ * structural check against it type-checks identically everywhere - `route_rest`/`route_muxws` use it
151
+ * to reject a factory-built class at the call site (see rest-proxy.ts, muxws-proxy.ts), which would
152
+ * otherwise type-check and hand back a bare proxy missing the class's own custom methods.
153
+ */
154
+ declare const FACTORY_BUILT: unique symbol;
155
+
156
+ /** Never assigned; its only use is `typeof FACTORY_BUILT_REJECTION` as a self-describing return type. */
157
+ declare const FACTORY_BUILT_REJECTION: 'route_rest cannot take a factory-built class - extend it directly instead';
158
+
159
+ /** Never assigned; its only use is `typeof FACTORY_BUILT_REJECTION` as a self-describing return type. */
160
+ declare const FACTORY_BUILT_REJECTION_2: 'route_muxws cannot take a factory-built class - extend it directly instead';
161
+
162
+ /**
163
+ * The shape a factory-built class's constructor has, purely to give `route_rest` an overload that
164
+ * rejects it - a type parameter conditioned on the argument (`C extends FactoryBuiltClass ? ... :
165
+ * C`) cannot be inferred from that position at all, so an overload with this as a plain parameter
166
+ * type is the only form that actually sees the real argument.
167
+ *
168
+ * `route_rest` uses its `viewSetClass` argument only for the `declares` on it (see the function
169
+ * body - the class itself is never `new`'d), then builds a bare `RestProxyImpl` and hands it back
170
+ * cast to `M`. Pass a factory-built class and this still type-checks without the overload below -
171
+ * `M` is usually inferred as `InstanceType<typeof ItemApi>` - but the object it returns is not an
172
+ * `ItemApi`, so any custom method the factory-built class added is `undefined` at runtime despite
173
+ * compiling. `declares` is required here, not optional: a hand-written class
174
+ * (`class ItemViewSet extends RestProxyImpl<...> {}`) may have no `declares` at all, or one that is
175
+ * a plain array rather than carrying `FACTORY_BUILT`, and either must fall through to the real
176
+ * overload below rather than match this one.
177
+ */
178
+ declare type FactoryBuiltClass = (abstract new (...args: any[]) => any) & {
179
+ declares: {
180
+ readonly [FACTORY_BUILT]: any;
181
+ };
182
+ };
183
+
184
+ /**
185
+ * The shape a factory-built class's constructor has, purely to give `route_muxws` an overload that
186
+ * rejects it - see rest-proxy.ts's `FactoryBuiltClass` for why this has to be a plain overload
187
+ * parameter rather than a type parameter conditioned on the argument.
188
+ */
189
+ declare type FactoryBuiltClass_2 = (abstract new (...args: any[]) => any) & {
190
+ declares: {
191
+ readonly [FACTORY_BUILT]: any;
192
+ };
193
+ };
194
+
195
+ /**
196
+ * Brands a factory-built class's `declares` so a subclass restating it fails on one flat "missing
197
+ * property" line naming `declares` itself, rather than TypeScript recursing into which method each
198
+ * mixin in the plain, unbranded replacement array is missing relative to the original. A record
199
+ * type wrapping `D` behind the phantom key, not `D & {brand}`: an intersection would still expose
200
+ * `D`'s own array shape to the comparison and recurse into it exactly as before; a plain array
201
+ * literal has no property at all under this key, so the mismatch stops at the top. Erased at
202
+ * runtime - `bindViewSet` casts through `unknown`, so the actual `static declares` stays a plain
203
+ * array, and every internal reader (rest-proxy.ts, muxws-proxy.ts, proxy-base.ts's
204
+ * `declaredActions`) already reads it through its own cast rather than this type.
205
+ *
206
+ * `FACTORY_BUILT` lives in proxy-base.ts, not here, so `route_rest`/`route_muxws` can check for the
207
+ * same brand without importing from this module - see proxy-base.ts's doc comment on the symbol.
208
+ */
209
+ declare type FactoryDeclares<D extends readonly ViewSetMixinClass[]> = {
210
+ readonly [FACTORY_BUILT]: D;
211
+ };
212
+
213
+ export declare type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
214
+
215
+ declare type KeyType_2 = string | number;
216
+ export { KeyType_2 as KeyType }
217
+
218
+ export declare interface ListMixin<T> {
219
+ list(params?: ListParams): Promise<T[]>;
220
+ }
221
+
222
+ export declare class ListMixin<T> {
223
+ static readonly actions: readonly string[];
224
+ }
225
+
226
+ /** Query parameters a list call accepts. `sort` is 'column:asc,other:desc'; the rest are filters. */
227
+ export declare interface ListParams {
228
+ sort?: string;
229
+ [key: string]: string | number | boolean | null | undefined | Array<string | number>;
230
+ }
231
+
232
+ /**
233
+ * FE counterpart of BE mixins.py — the mixins a ViewSet declaration is composed of.
234
+ *
235
+ * Each mixin is an interface merged into a class of the same name. The interface names the actions
236
+ * and their signatures; the class carries the `actions` list that the schema check reads at
237
+ * runtime. Both halves are needed because neither alone can do the job: a TypeScript `implements`
238
+ * clause is erased before anything runs, and a runtime list of strings says nothing about types.
239
+ *
240
+ * class ItemViewSet extends restViewSet<Item>()('id', [ReadOnlyViewSetMixin, LookupMixin]) {}
241
+ *
242
+ * The list is written once, as values. `restViewSet` reads the action names off the mixins'
243
+ * instance types to build the ViewSet's public surface, and hands the same list to the proxy so
244
+ * that the schema check can compare it against the BE.
245
+ *
246
+ * The members are methods rather than properties, and are reached through an intersection rather
247
+ * than a `Pick<>` of a lookup table: a mapped type re-emits a method as a function-valued property,
248
+ * and a subclass may then not override an action with a method (TS2425). Overriding one to add
249
+ * caching or reshape parameters works on a hand-written proxy subclass today, and must keep
250
+ * working here.
251
+ *
252
+ * A composite mixin restates nothing: its interface extends the leaves its `actions` spread names,
253
+ * so each action's signature exists in exactly one place.
254
+ *
255
+ * The methods have no implementation anywhere in this file. The implementation is one HTTP call in
256
+ * ViewSetProxyBase, and a mixin carrying its own would be a second copy of it.
257
+ */
258
+ export declare interface LookupItem {
259
+ group: unknown;
260
+ pk: unknown;
261
+ title: string;
262
+ icon: string | null;
263
+ }
264
+
265
+ export declare interface LookupMixin {
266
+ lookup(): Promise<LookupItem[]>;
267
+ }
268
+
269
+ export declare class LookupMixin {
270
+ static readonly actions: readonly string[];
271
+ }
272
+
273
+ export declare interface MuxwsPeerLike {
274
+ open(payload?: unknown, options?: {
275
+ headers?: Record<string, unknown>;
276
+ end?: boolean;
277
+ }): MuxwsStreamLike;
278
+ }
279
+
280
+ /**
281
+ * Where the peer comes from. A bare peer is the simple case; a function is for the common shape
282
+ * where the proxy is constructed at module scope but `connect()` has not resolved yet. The
283
+ * function is called once and its result cached — a muxws Peer survives its own reconnects, so
284
+ * there is nothing to re-resolve afterwards.
285
+ */
286
+ export declare type MuxwsPeerSource = MuxwsPeerLike | (() => MuxwsPeerLike | Promise<MuxwsPeerLike>);
287
+
288
+ export declare type MuxwsProxy<M> = M;
289
+
290
+ export declare class MuxwsProxyImpl<K extends KeyType_2, T, PK extends keyof T> extends ViewSetProxyBase<K, T, PK> {
291
+ private readonly peerSource;
292
+ private readonly timeoutMs?;
293
+ private readonly headers;
294
+ private peerPromise?;
295
+ constructor(options: MuxwsProxyOptions);
296
+ private peer;
297
+ protected request<R>(method: HttpMethod, path: string, options?: RequestOptions): Promise<R>;
298
+ }
299
+
300
+ export declare interface MuxwsProxyOptions extends ProxyBaseOptions {
301
+ peer: MuxwsPeerSource;
302
+ /** Per-request timeout in milliseconds. muxws resets the stream with TIMEOUT when it expires. */
303
+ timeoutMs?: number;
304
+ /**
305
+ * Headers added to every request this proxy makes. The WebSocket handshake already carries
306
+ * whatever identified the session and the server treats that as the baseline; these override it
307
+ * for this proxy's calls. There is no per-call header: `RequestOptions` is `{ query, body }`.
308
+ */
309
+ headers?: Record<string, string>;
310
+ }
311
+
312
+ /**
313
+ * The bits of a muxws Peer this proxy uses. Typed structurally rather than by importing the muxws
314
+ * types, so that `muxws` stays an optional dependency: an application that only uses route_rest
315
+ * should not have to install it.
316
+ */
317
+ export declare interface MuxwsStreamLike {
318
+ result(options?: {
319
+ timeoutMs?: number;
320
+ }): Promise<unknown>;
321
+ readonly replyHeadersArrived: Promise<void>;
322
+ readonly replyHeaders: Record<string, unknown> | null | undefined;
323
+ }
324
+
325
+ /** A muxws ViewSet base class. See `restViewSet` for why the call is curried. */
326
+ export declare function muxwsViewSet<T>(): <PK extends PkFieldName<T> & keyof T, D extends readonly ViewSetMixinClass[]>(pkFieldName: PK, declares: D) => ViewSetClass<T, PK, D, MuxwsProxyOptions>;
327
+
328
+ export declare interface PageParams extends ListParams {
329
+ offset?: number;
330
+ limit?: number;
331
+ }
332
+
333
+ /**
334
+ * One page, mirroring the BE PaginatedList.
335
+ *
336
+ * `count` is null when the backend could not know it without draining a lazy source. `hasMore` and
337
+ * `hasPrevious` are stated rather than inferred — a client that guesses from a null gets the guess
338
+ * wrong exactly at the boundary where it matters.
339
+ */
340
+ export declare interface PaginatedList<T> {
341
+ results: T[];
342
+ offset: number;
343
+ limit: number | null;
344
+ count: number | null;
345
+ hasMore: boolean;
346
+ hasPrevious: boolean;
347
+ }
348
+
349
+ /**
350
+ * FE counterpart of the BE PaginatedListMixin. `GET {basePath}` is one endpoint answering in the
351
+ * shape the BE viewset declared as its default; this client sends no X-List-Shape header, so a
352
+ * ViewSet declares the mixin matching that default rather than one per shape it might want.
353
+ */
354
+ export declare interface PaginatedListMixin<T> {
355
+ listPage(params?: PageParams): Promise<PaginatedList<T>>;
356
+ }
357
+
358
+ export declare class PaginatedListMixin<T> {
359
+ static readonly actions: readonly string[];
360
+ }
361
+
362
+ /** The fields of `T` that could be a primary key. */
363
+ export declare type PkFieldName<T> = Extract<{
364
+ [F in keyof T]-?: NonNullable<T[F]> extends KeyType_2 ? F : never;
365
+ }[keyof T], string>;
366
+
367
+ declare type PkType<T, PK extends keyof T> = NonNullable<T[PK]> & KeyType_2;
368
+
369
+ export declare interface ProxyBaseOptions {
370
+ /** Base path to the resource, e.g. '/items'. */
371
+ basePath: string;
372
+ /** Name of the PK field on the model, e.g. 'id'. */
373
+ pkFieldName: string;
374
+ /** Set false to skip the startup schema check (it is advisory and costs one request). */
375
+ validateSchema?: boolean;
376
+ /**
377
+ * The mixins the ViewSet declares, for the `route_rest` / `route_muxws` path: those build a bare
378
+ * proxy and use the ViewSet class only for typing, so a `static declares` on it would otherwise
379
+ * never reach the object being checked.
380
+ */
381
+ declares?: readonly ViewSetMixinDeclaration[];
382
+ }
383
+
384
+ /** Query values; an array becomes a repeated key, which is how FastAPI binds `list[str]`. */
385
+ export declare type QueryParams = Record<string, string | number | boolean | null | undefined | Array<string | number>>;
386
+
387
+ export declare interface ReadOnlyViewSetMixin<K extends KeyType_2, T> extends ListMixin<T>, RetrieveMixin<K, T> {
388
+ }
389
+
390
+ export declare class ReadOnlyViewSetMixin<K extends KeyType_2, T> extends ListMixin<T> {
391
+ static readonly actions: readonly string[];
392
+ }
393
+
394
+ export declare interface RequestOptions {
395
+ query?: QueryParams;
396
+ body?: unknown;
397
+ }
398
+
399
+ /**
400
+ * The REST proxy type is simply the mixin interface `M` the caller declares.
401
+ * Because TypeScript cannot inspect Python class hierarchies at runtime, the
402
+ * caller provides the explicit type via the generic parameter `M` (see route_rest).
403
+ */
404
+ export declare type RestProxy<M> = M;
405
+
406
+ export declare class RestProxyImpl<K extends KeyType_2, T, PK extends keyof T> extends ViewSetProxyBase<K, T, PK> {
407
+ protected readonly http: AxiosInstance;
408
+ constructor(options: RestProxyOptions);
409
+ /**
410
+ * Dispatches to axios' per-verb methods rather than to `http.request()`, deliberately. Those
411
+ * are the calls this proxy has always made, and they are what application interceptors and test
412
+ * doubles are written against — routing everything through `request()` would be invisible on
413
+ * the wire but would break every one of them.
414
+ *
415
+ * axios throws its own AxiosError on a non-2xx, which already carries `response.status` — the
416
+ * shape ViewSetRequestError mirrors for the muxws side. Nothing to translate here.
417
+ */
418
+ protected request<R>(method: HttpMethod, path: string, options?: RequestOptions): Promise<R>;
419
+ }
420
+
421
+ export declare interface RestProxyOptions extends ProxyBaseOptions {
422
+ /** Optional: existing axios instance. Defaults to the global axios. */
423
+ axiosInstance?: AxiosInstance;
424
+ }
425
+
426
+ /**
427
+ * A REST ViewSet base class.
428
+ *
429
+ * The empty `()` is not decoration: TypeScript has no partial type-argument inference, so the model
430
+ * cannot be given explicitly while the pk field and the mixin list are inferred from arguments in
431
+ * the same call (TS2558). Currying is the only way to have both.
432
+ */
433
+ export declare function restViewSet<T>(): <PK extends PkFieldName<T> & keyof T, D extends readonly ViewSetMixinClass[]>(pkFieldName: PK, declares: D) => ViewSetClass<T, PK, D, RestProxyOptions>;
434
+
435
+ export declare interface RetrieveMixin<K extends KeyType_2, T> {
436
+ retrieve(pk: K): Promise<T>;
437
+ }
438
+
439
+ export declare class RetrieveMixin<K extends KeyType_2, T> {
440
+ static readonly actions: readonly string[];
441
+ }
442
+
443
+ export declare function route_muxws<M = never>(viewSetClass: FactoryBuiltClass_2, options: MuxwsProxyOptions): typeof FACTORY_BUILT_REJECTION_2;
444
+
445
+ /**
446
+ * Registers a muxws proxy for the given ViewSet class. The mirror of route_rest, and it takes the
447
+ * same generic parameter for the same reason: TypeScript cannot inspect the Python class.
448
+ */
449
+ export declare function route_muxws<M>(viewSetClass: ViewSetClass_3, options: MuxwsProxyOptions): MuxwsProxy<M>;
450
+
451
+ /**
452
+ * Registers a REST proxy for the given ViewSet class.
453
+ *
454
+ * The generic parameter `M` determines which mixin interfaces are available —
455
+ * typically the ViewSet type (or a union of mixin interfaces).
456
+ *
457
+ * @example
458
+ * ```ts
459
+ * import type { BulkViewSetMixin, LookupMixin } from './mixins';
460
+ *
461
+ * interface Item { id: number; name: string }
462
+ *
463
+ * // with separate arguments (recommended)
464
+ * const restItems = route_rest<BulkViewSetMixin<number, Item> & LookupMixin>(
465
+ * ItemViewSet, '/items', 'id',
466
+ * );
467
+ *
468
+ * // or with an options object
469
+ * const restItems2 = route_rest<BulkViewSetMixin<number, Item> & LookupMixin>(
470
+ * ItemViewSet, { basePath: '/items', pkFieldName: 'id' },
471
+ * );
472
+ *
473
+ * const items = await restItems.list();
474
+ * const item = await restItems.retrieve(1);
475
+ * ```
476
+ */
477
+ export declare function route_rest<M = never>(viewSetClass: FactoryBuiltClass, basePath: string, pkFieldName: string, axiosInstance?: AxiosInstance): typeof FACTORY_BUILT_REJECTION;
478
+
479
+ export declare function route_rest<M = never>(viewSetClass: FactoryBuiltClass, options: RestProxyOptions): typeof FACTORY_BUILT_REJECTION;
480
+
481
+ export declare function route_rest<M>(_viewSetClass: ViewSetClass_2, basePath: string, pkFieldName: string, axiosInstance?: AxiosInstance): RestProxy<M>;
482
+
483
+ export declare function route_rest<M>(_viewSetClass: ViewSetClass_2, options: RestProxyOptions): RestProxy<M>;
484
+
485
+ export declare const translatableStrings: {
486
+ not_found: string;
487
+ session_expired: string;
488
+ not_authorized: string;
489
+ rate_limited: string;
490
+ unsupported_list_shape: string;
491
+ cursor_unreadable: string;
492
+ cursor_missing_position: string;
493
+ cursor_stale: string;
494
+ cursor_missing_keys: string;
495
+ cursor_value_mismatch: string;
496
+ };
497
+
498
+ /**
499
+ * A translated, interpolated message for a failed request - `body.detail` unchanged when
500
+ * `detail_code` is absent (the server has not registered the handler, or this is a view's own
501
+ * plain-string error) or names a code this table does not (yet) cover.
502
+ */
503
+ export declare function translateApiError(body: ApiErrorBody): string;
504
+
505
+ export declare const translateStrings: (cb: TranslateStringsCallback< {
506
+ not_found: string;
507
+ session_expired: string;
508
+ not_authorized: string;
509
+ rate_limited: string;
510
+ unsupported_list_shape: string;
511
+ cursor_unreadable: string;
512
+ cursor_missing_position: string;
513
+ cursor_stale: string;
514
+ cursor_missing_keys: string;
515
+ cursor_value_mismatch: string;
516
+ }>) => void;
517
+
518
+ export declare interface UpdateMixin<K extends KeyType_2, T> {
519
+ update(pk: K, data: T): Promise<T>;
520
+ partialUpdate(pk: K, data: Partial<T>): Promise<T>;
521
+ }
522
+
523
+ export declare class UpdateMixin<K extends KeyType_2, T> {
524
+ static readonly actions: readonly string[];
525
+ }
526
+
527
+ /**
528
+ * What the factory hands back: a class to extend.
529
+ *
530
+ * A `declares` list naming no action — `[]`, or one annotated `ViewSetMixinClass[]`, which erases
531
+ * which mixins are in it — would otherwise produce a ViewSet with no actions and no complaint.
532
+ * TS2507 prints the type it was given, so the type is the sentence.
533
+ *
534
+ * `pkFieldName: PK` is added on top of `ViewSetInternals` explicitly: the constructed instance's
535
+ * real runtime type is `ProxyBaseOptions`'s `ViewSetProxyBase`, which already carries the public
536
+ * `pkFieldName`, but `ViewSetInternals` is deliberately narrow (see its own doc comment), so this
537
+ * type would otherwise hide a field that is genuinely there. Typed as the literal `PK` rather than
538
+ * `string`, since the factory already knows exactly which field it is.
539
+ */
540
+ export declare type ViewSetClass<T, PK extends keyof T, D extends readonly ViewSetMixinClass[], O extends ProxyBaseOptions> = [
541
+ ActionsOf<D[number]>
542
+ ] extends [never] ? 'declares must name at least one action: pass the mixin classes themselves, unannotated' : {
543
+ new (options: Omit<O, 'pkFieldName' | 'declares'>): ViewSetInternals & ActionSurface<PkType<T, PK>, T, PK, ActionsOf<D[number]>> & {
544
+ readonly pkFieldName: PK;
545
+ };
546
+ readonly declares: FactoryDeclares<D>;
547
+ };
548
+
549
+ /** ViewSet class constructor (for type-level introspection only). */
550
+ declare type ViewSetClass_2 = abstract new (...args: any[]) => any;
551
+
552
+ declare type ViewSetClass_3 = abstract new (...args: any[]) => any;
553
+
554
+ /**
555
+ * What a ViewSet's own methods may reach - everything a custom endpoint needs, and nothing a caller
556
+ * does.
557
+ *
558
+ * A real base class rather than a type, because `protected` is checked nominally: a subclass body
559
+ * reaches `request()` only if it genuinely descends from the class that declared it. The ViewSet
560
+ * factory hands back a class typed as this plus the declared actions, which is how a factory-built
561
+ * ViewSet ends up with a narrow public surface and a usable private one.
562
+ */
563
+ export declare abstract class ViewSetInternals {
564
+ protected readonly basePath: string;
565
+ protected constructor(basePath: string);
566
+ /**
567
+ * Sends one request and returns the decoded response body.
568
+ *
569
+ * `path` is relative to `basePath` — '' for the collection, '/1' for a record, '/bulk', and so
570
+ * on. Implementations must throw on a status of 400 or above, with `response.status` readable on
571
+ * the thrown value. Below 400 the two differ, on a band a caller rarely sees: the muxws proxy
572
+ * returns the body for any 3xx, while the REST proxy follows a redirect it can follow and rejects
573
+ * whatever axios' default `validateStatus` then leaves outside 200-299.
574
+ *
575
+ * Concrete here, and never reached: ViewSetProxyBase re-declares it abstract, which is where a
576
+ * transport is actually held to implementing it. It cannot be abstract at this level because
577
+ * TypeScript propagates an abstract member through a constructor type, and the factory hands one
578
+ * back - every ViewSet a consumer wrote would then fail TS2515 for a method the transport
579
+ * underneath it has always implemented.
580
+ */
581
+ protected request<R>(method: HttpMethod, path: string, options?: RequestOptions): Promise<R>;
582
+ }
583
+
584
+ export declare interface ViewSetMixin<K extends KeyType_2, T, PK extends keyof T> extends ReadOnlyViewSetMixin<K, T>, CreateMixin<T, PK>, UpdateMixin<K, T>, DestroyMixin<K> {
585
+ }
586
+
587
+ export declare class ViewSetMixin<K extends KeyType_2, T, PK extends keyof T> extends ReadOnlyViewSetMixin<K, T> {
588
+ static readonly actions: readonly string[];
589
+ }
590
+
591
+ /** A mixin class: the runtime `actions` the schema check reads, and the type naming those actions. */
592
+ export declare type ViewSetMixinClass = ViewSetMixinDeclaration & (abstract new (...args: any[]) => object);
593
+
594
+ /** One entry of a ViewSet's `static declares` list: a mixin naming the actions it contributes. */
595
+ export declare interface ViewSetMixinDeclaration {
596
+ readonly actions: readonly string[];
597
+ }
598
+
599
+ export declare abstract class ViewSetProxyBase<K extends KeyType_2, T, PK extends keyof T> extends ViewSetInternals implements BulkViewSetMixin<K, T, PK>, CursorListMixin<T>, PaginatedListMixin<T>, LookupMixin {
600
+ /**
601
+ * The mixins this ViewSet is composed of — the FE counterpart of a BE viewset's base classes.
602
+ *
603
+ * class ItemViewSet extends RestProxyImpl<number, Item, 'id'> {
604
+ * static declares = [ReadOnlyViewSetMixin, LookupMixin];
605
+ * }
606
+ *
607
+ * Left undefined, the ViewSet is not checked against the BE schema at all. That is deliberate:
608
+ * a ViewSet that never said what it has cannot be caught contradicting itself, and guessing on
609
+ * its behalf is what made the check report actions nobody ever claimed.
610
+ */
611
+ static declares?: readonly ViewSetMixinDeclaration[];
612
+ /**
613
+ * Which field of `T` is the primary key, e.g. `'id'`. `public`, not `protected`: nothing in this
614
+ * library reads it back, but a generic caller holding a ViewSet instance - a grid or table
615
+ * component needing to know which column identifies a row - has no other way to ask.
616
+ */
617
+ readonly pkFieldName: string;
618
+ private readonly schemaValidationEnabled;
619
+ private readonly declaredMixins?;
620
+ protected constructor(options: ProxyBaseOptions);
621
+ /**
622
+ * Starts the advisory schema check. Subclasses must call this as the *last* statement of their
623
+ * constructor, never the base constructor itself: `request()` reads fields the subclass has not
624
+ * assigned yet while `super()` is still running, and since the check swallows its own errors,
625
+ * doing it here would leave it permanently and silently dead.
626
+ */
627
+ protected initSchemaValidation(): void;
628
+ /** Abstract here, where a transport is actually held to it. See ViewSetInternals.request. */
629
+ protected abstract request<R>(method: HttpMethod, path: string, options?: RequestOptions): Promise<R>;
630
+ /**
631
+ * Fetches the BE schema and compares it against what this ViewSet declared.
632
+ * Logs a console warning for any mismatch found.
633
+ *
634
+ * The comparison is against `static declares`, not against which methods exist on the object:
635
+ * every action is implemented unconditionally on this class, so `typeof this[action]` is true for
636
+ * every ViewSet and answers a question nobody asked. `declares` is the only place the FE says
637
+ * anything a BE viewset could disagree with.
638
+ *
639
+ * Non-critical: errors during fetch or parsing are silently ignored. Note that the schema is
640
+ * fetched over this proxy's own transport, so a muxws proxy validates against the muxws
641
+ * endpoint set and a REST proxy against the REST one — which is the point, since the two are
642
+ * allowed to differ.
643
+ */
644
+ private validateAgainstSchema;
645
+ create(data: Omit<T, PK>): Promise<T>;
646
+ bulkCreate(data: Omit<T, PK>[]): Promise<T[]>;
647
+ list(params?: ListParams): Promise<T[]>;
648
+ /**
649
+ * Fetches one page. Only meaningful against a viewset built on the BE PaginatedListMixin — a
650
+ * plain ListMixin ignores offset/limit and answers with the whole collection, which would not
651
+ * match this return type.
652
+ *
653
+ * The BE speaks snake_case (`has_more`); the rest of this client speaks whatever the model
654
+ * declares, so only the envelope's own fields are renamed here. The records inside are passed
655
+ * through untouched.
656
+ */
657
+ /**
658
+ * Fetches one cursor page. Only meaningful against a viewset built on the BE CursorListMixin.
659
+ *
660
+ * Follow `next` to walk forward. Unlike offset paging, a row inserted or removed behind you
661
+ * cannot make the next page repeat or skip anything.
662
+ */
663
+ listCursor(params?: CursorParams): Promise<CursorPage<T>>;
664
+ listPage(params?: PageParams): Promise<PaginatedList<T>>;
665
+ retrieve(pk: K): Promise<T>;
666
+ update(pk: K, data: T): Promise<T>;
667
+ partialUpdate(pk: K, data: Partial<T>): Promise<T>;
668
+ bulkUpdate(records: Record<K, T>): Promise<T[]>;
669
+ bulkPartialUpdate(records: Record<K, Partial<T>>): Promise<T[]>;
670
+ destroy(pk: K): Promise<DestroyReturnData>;
671
+ bulkDestroy(pks: K[]): Promise<DestroyReturnData[]>;
672
+ lookup(): Promise<LookupItem[]>;
673
+ }
674
+
675
+ /**
676
+ * What a failed call throws over muxws, where there is no axios to raise anything.
677
+ *
678
+ * The shape mirrors `AxiosError` — `error.response.status`, `error.response.data` and
679
+ * `error.response.headers` — so a caller reads the same fields whichever transport the ViewSet
680
+ * speaks. Over HTTP axios raises its own error, already in that shape, and it is passed through
681
+ * untouched.
682
+ *
683
+ * `response` is always set here, unlike `AxiosError.response`, which is absent when the request
684
+ * never reached a reply.
685
+ */
686
+ export declare class ViewSetRequestError extends Error {
687
+ readonly response: {
688
+ status: number;
689
+ data: unknown;
690
+ headers: Record<string, string>;
691
+ };
692
+ constructor(status: number, data: unknown, headers?: Record<string, string>);
693
+ }
694
+
695
+ export { }