@web-ts-toolkit/access-router-client 0.32.0 → 0.34.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/README.md +258 -14
- package/index.d.mts +453 -85
- package/index.d.ts +453 -85
- package/index.js +1133 -368
- package/index.mjs +1133 -368
- package/llms.txt +39 -5
- package/package.json +11 -4
package/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as axios from 'axios';
|
|
2
|
-
import { AxiosInstance, AxiosResponse, AxiosRequestConfig,
|
|
2
|
+
import { AxiosInstance, AxiosResponse, AxiosRequestConfig, InternalAxiosRequestConfig } from 'axios';
|
|
3
3
|
|
|
4
4
|
interface SubQueryOptions {
|
|
5
5
|
path?: string;
|
|
@@ -74,6 +74,7 @@ interface CreateAdvancedOptions {
|
|
|
74
74
|
}
|
|
75
75
|
interface UpdateOptions {
|
|
76
76
|
returningAll?: boolean;
|
|
77
|
+
includePermissions?: boolean;
|
|
77
78
|
}
|
|
78
79
|
interface UpdateAdvancedArgs<TSelect extends Projection = Projection> {
|
|
79
80
|
select?: TSelect;
|
|
@@ -113,35 +114,51 @@ interface DataListArgs {
|
|
|
113
114
|
pageSize?: number;
|
|
114
115
|
}
|
|
115
116
|
interface DataListOptions {
|
|
116
|
-
includePermissions?: boolean;
|
|
117
117
|
includeCount?: boolean;
|
|
118
118
|
includeExtraHeaders?: boolean;
|
|
119
119
|
ignoreCache?: boolean;
|
|
120
120
|
}
|
|
121
121
|
interface DataListAdvancedArgs<TSelect extends Projection = Projection> {
|
|
122
122
|
select?: TSelect;
|
|
123
|
-
sort?:
|
|
123
|
+
sort?: string;
|
|
124
124
|
skip?: string | number;
|
|
125
125
|
limit?: string | number;
|
|
126
126
|
page?: string | number;
|
|
127
127
|
pageSize?: string | number;
|
|
128
128
|
}
|
|
129
129
|
interface DataListAdvancedOptions {
|
|
130
|
-
includePermissions?: boolean;
|
|
131
130
|
includeCount?: boolean;
|
|
132
131
|
includeExtraHeaders?: boolean;
|
|
133
132
|
ignoreCache?: boolean;
|
|
134
133
|
}
|
|
135
134
|
interface DataReadOptions {
|
|
136
|
-
includePermissions?: boolean;
|
|
137
135
|
ignoreCache?: boolean;
|
|
138
136
|
}
|
|
139
137
|
interface DataReadAdvancedArgs<TSelect extends Projection = Projection> {
|
|
140
138
|
select?: TSelect;
|
|
141
|
-
ignoreCache?: boolean;
|
|
142
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* Options for `DataService.readAdvanced` and `DataService.readAdvancedFilter`.
|
|
142
|
+
*
|
|
143
|
+
* `ignoreCache` is the documented cache-bypass knob for advanced reads. It
|
|
144
|
+
* lives here — not on `DataReadAdvancedArgs` — so callers use `{ ignoreCache:
|
|
145
|
+
* true }` in the options position to skip an existing cache entry, matching
|
|
146
|
+
* the placement used by the basic `list`, `listAdvanced`, and `read` service
|
|
147
|
+
* methods.
|
|
148
|
+
*
|
|
149
|
+
* `includePermissions` is intentionally absent. The access-router data
|
|
150
|
+
* router body schema for advanced reads (`dataReadByIdBodySchema` and
|
|
151
|
+
* `dataReadFilterBodySchema`) explicitly rejects the `options` key, and the
|
|
152
|
+
* root router drops `item.options` when dispatching data operations
|
|
153
|
+
* server-side (root-router.ts passes `{}` as the options argument to
|
|
154
|
+
* `findById`/`findOne`). Advertising `includePermissions` here was a
|
|
155
|
+
* type-level promise the server cannot honor and the grouped path silently
|
|
156
|
+
* passed through `__query.options.includePermissions` only for the root
|
|
157
|
+
* router to discard it — a direct/grouped asymmetry. The fix removes the
|
|
158
|
+
* dead-letter field from the type and from `__query.options` so direct and
|
|
159
|
+
* grouped advanced reads compose identical payloads.
|
|
160
|
+
*/
|
|
143
161
|
interface DataReadAdvancedOptions {
|
|
144
|
-
includePermissions?: boolean;
|
|
145
162
|
ignoreCache?: boolean;
|
|
146
163
|
}
|
|
147
164
|
interface DataDefaults {
|
|
@@ -157,67 +174,81 @@ interface AdditionalReqConfig {
|
|
|
157
174
|
throwOnError?: boolean;
|
|
158
175
|
}
|
|
159
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Normalized failure payload. Mirrors {@link FailureResult} but kept
|
|
179
|
+
* structurally loose (the {@link Response} discriminated union narrows
|
|
180
|
+
* these fields automatically when consumers branch on `result.success`).
|
|
181
|
+
*/
|
|
160
182
|
interface ResultError {
|
|
161
|
-
success:
|
|
183
|
+
success: false;
|
|
162
184
|
raw: unknown;
|
|
163
|
-
data:
|
|
185
|
+
data: null;
|
|
164
186
|
message: string;
|
|
165
187
|
status: number;
|
|
166
188
|
headers: Record<string, unknown>;
|
|
189
|
+
totalCount?: number;
|
|
167
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* Low-level base class shared by {@link ModelService} and {@link DataService}.
|
|
193
|
+
* Subclassing is supported as an advanced opt-in for callers that need a
|
|
194
|
+
* bespoke service shape: subclasses extend `Service`, build on the shared
|
|
195
|
+
* Axios instance, and reuse the `wrapGet`/`wrapPost`/... paths registered
|
|
196
|
+
* against the adapter's `basePath`. Most callers should use
|
|
197
|
+
* `adapter.createModelService<T>(...)` / `adapter.createDataService<T>(...)`
|
|
198
|
+
* rather than subclassing `Service` directly.
|
|
199
|
+
*
|
|
200
|
+
* The `handleSuccess`/`handleError` helpers normalize Axios responses into
|
|
201
|
+
* the package's {@link Response} discriminated union so direct subclasses
|
|
202
|
+
* produce the same success/failure contract as the built-in services.
|
|
203
|
+
*/
|
|
168
204
|
declare class Service {
|
|
169
205
|
protected _axios: AxiosInstance;
|
|
170
206
|
protected _basePath: string;
|
|
171
207
|
private _wrap;
|
|
172
|
-
|
|
208
|
+
private _throwOnError;
|
|
209
|
+
constructor(axios: AxiosInstance, basePath: string, throwOnError?: boolean);
|
|
173
210
|
protected handleSuccess(res: AxiosResponse<unknown, unknown>, extra?: {}): Response<unknown>;
|
|
174
|
-
protected handleError<T extends
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
headers: Record<string, unknown>;
|
|
178
|
-
data: unknown;
|
|
179
|
-
};
|
|
180
|
-
request?: unknown;
|
|
181
|
-
message?: string;
|
|
182
|
-
}): T;
|
|
211
|
+
protected handleError<T extends Response<unknown, unknown>>(error: unknown): Extract<T, FailureResult>;
|
|
212
|
+
/** Resolves per-call policy against the already-resolved service/adapter default. */
|
|
213
|
+
resolveThrowOnError(override?: boolean): boolean;
|
|
183
214
|
wrapGet<T = unknown>(url: string, defaultAxiosRequestConfig?: AxiosRequestConfig): (options?: WrapOptions, requestConfig?: AxiosRequestConfig) => Promise<AxiosResponse<T, any, {}>>;
|
|
184
215
|
wrapPost<T = unknown>(url: string, defaultAxiosRequestConfig?: AxiosRequestConfig): (data?: unknown, options?: WrapOptions, requestConfig?: AxiosRequestConfig) => Promise<AxiosResponse<T, any, {}>>;
|
|
185
216
|
wrapPut<T = unknown>(url: string, defaultAxiosRequestConfig?: AxiosRequestConfig): (data?: unknown, options?: WrapOptions, requestConfig?: AxiosRequestConfig) => Promise<AxiosResponse<T, any, {}>>;
|
|
186
217
|
wrapPatch<T = unknown>(url: string, defaultAxiosRequestConfig?: AxiosRequestConfig): (data?: unknown, options?: WrapOptions, requestConfig?: AxiosRequestConfig) => Promise<AxiosResponse<T, any, {}>>;
|
|
187
218
|
wrapDelete<T = unknown>(url: string, defaultAxiosRequestConfig?: AxiosRequestConfig): (options?: WrapOptions, requestConfig?: AxiosRequestConfig) => Promise<AxiosResponse<T, any, {}>>;
|
|
219
|
+
/**
|
|
220
|
+
* Public bridge to the per-service success/failure callback pipeline and
|
|
221
|
+
* `throwOnError` policy. Adapter-internal grouping machinery calls this so
|
|
222
|
+
* that grouped entries go through the same finalization the direct path
|
|
223
|
+
* uses (`createResponseHandler`). Returns `res` unchanged on success and
|
|
224
|
+
* throws `ServiceError` when both `res.success === false` and the
|
|
225
|
+
* `throwOnError` override (or the service-level default) are enabled.
|
|
226
|
+
*/
|
|
227
|
+
applyResponseCallbacks<T extends {
|
|
228
|
+
success: boolean;
|
|
229
|
+
}>(res: T, throwOnErrorOverride?: boolean): T;
|
|
230
|
+
/**
|
|
231
|
+
* Returns a fresh headers object that includes the package-owned
|
|
232
|
+
* `CACHE_HEADER` set to `"true"` (cache eligible) or `"false"` (bypass)
|
|
233
|
+
* according to the `ignoreCache` option. The caller's `CACHE_HEADER`
|
|
234
|
+
* value, if any, wins over the `ignoreCache` default.
|
|
235
|
+
*
|
|
236
|
+
* The input `headers` object is **never mutated**: an `AxiosHeaders`
|
|
237
|
+
* instance is cloned via `.toJSON()` before any value is set, and a
|
|
238
|
+
* plain-object headers input is shallow-copied. Reusing the same
|
|
239
|
+
* caller-owned headers across multiple requests therefore has no
|
|
240
|
+
* hidden side effects, and the order of invocations is irrelevant.
|
|
241
|
+
*/
|
|
188
242
|
updateHeaders(headers: AxiosRequestConfig['headers'], { ignoreCache }: {
|
|
189
243
|
ignoreCache?: boolean;
|
|
190
|
-
}):
|
|
191
|
-
Accept: axios.AxiosHeaderValue;
|
|
192
|
-
"Content-Length": axios.AxiosHeaderValue;
|
|
193
|
-
"User-Agent": axios.AxiosHeaderValue;
|
|
194
|
-
"Content-Encoding": axios.AxiosHeaderValue;
|
|
195
|
-
Authorization: axios.AxiosHeaderValue;
|
|
196
|
-
Location: axios.AxiosHeaderValue;
|
|
197
|
-
} & {
|
|
198
|
-
'Content-Type': axios.AxiosHeaderValue;
|
|
199
|
-
}> & Partial<{
|
|
200
|
-
get: AxiosHeaders;
|
|
201
|
-
delete: AxiosHeaders;
|
|
202
|
-
head: AxiosHeaders;
|
|
203
|
-
options: AxiosHeaders;
|
|
204
|
-
post: AxiosHeaders;
|
|
205
|
-
put: AxiosHeaders;
|
|
206
|
-
patch: AxiosHeaders;
|
|
207
|
-
purge: AxiosHeaders;
|
|
208
|
-
link: AxiosHeaders;
|
|
209
|
-
unlink: AxiosHeaders;
|
|
210
|
-
query: AxiosHeaders;
|
|
211
|
-
} & {
|
|
212
|
-
common: AxiosHeaders;
|
|
213
|
-
}>);
|
|
244
|
+
}): AxiosRequestConfig['headers'];
|
|
214
245
|
}
|
|
215
246
|
declare class ServiceError extends Error {
|
|
216
|
-
success:
|
|
217
|
-
raw: unknown;
|
|
218
|
-
data:
|
|
219
|
-
status: number;
|
|
220
|
-
headers: Record<string, unknown>;
|
|
247
|
+
success: false;
|
|
248
|
+
readonly raw: unknown;
|
|
249
|
+
readonly data: null;
|
|
250
|
+
readonly status: number;
|
|
251
|
+
readonly headers: Record<string, unknown>;
|
|
221
252
|
constructor(result: ResultError);
|
|
222
253
|
}
|
|
223
254
|
|
|
@@ -249,54 +280,54 @@ declare class ModelService<T extends Document> extends Service {
|
|
|
249
280
|
constructor({ axios, modelName, basePath, queryPath, mutationPath, onSuccess, onFailure, throwOnError }: Props$1, defaults?: Defaults);
|
|
250
281
|
list<TData extends Partial<T> = T>(args?: ListArgs, options?: ListOptions, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<ListModelResponse<T, TData>>;
|
|
251
282
|
listAdvanced<TData extends Partial<T> | never = never, TSelect extends Projection = Projection>(filter: FilterQuery<T>, args?: ListAdvancedArgs<TSelect>, options?: ListAdvancedOptions, axiosRequestConfig?: RequestConfig$1): ModelRequest<ListModelResponse<T, ResolvedSelectedShape<T, TSelect, TData>>>;
|
|
252
|
-
create<TData extends Partial<T> = T>(data: object, options?: CreateOptions, axiosRequestConfig?: RequestConfig$1):
|
|
283
|
+
create<TData extends Partial<T> = T>(data: object[], options?: CreateOptions, axiosRequestConfig?: RequestConfig$1): ModelRequest<ArrayModelResponse<T, TData>>;
|
|
284
|
+
create<TData extends Partial<T> = T>(data: object, options?: CreateOptions, axiosRequestConfig?: RequestConfig$1): ModelRequest<ModelResponse<T, TData>>;
|
|
285
|
+
createAdvanced<TData extends Partial<T> | never = never, TSelect extends Projection = Projection>(data: object[], args?: CreateAdvancedArgs<TSelect>, options?: CreateAdvancedOptions, axiosRequestConfig?: RequestConfig$1): ModelRequest<ArrayModelResponse<T, ResolvedSelectedShape<T, TSelect, TData>>>;
|
|
253
286
|
createAdvanced<TData extends Partial<T> | never = never, TSelect extends Projection = Projection>(data: object, args?: CreateAdvancedArgs<TSelect>, options?: CreateAdvancedOptions, axiosRequestConfig?: RequestConfig$1): ModelRequest<ModelResponse<T, ResolvedSelectedShape<T, TSelect, TData>>>;
|
|
254
287
|
upsert<TData extends Partial<T> = T>(data: object, options?: UpsertOptions, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<ModelResponse<T, TData>>;
|
|
255
288
|
upsertAdvanced<TData extends Partial<T> | never = never, TSelect extends Projection = Projection>(data: object, args?: UpsertAdvancedArgs<TSelect>, options?: UpsertAdvancedOptions, axiosRequestConfig?: RequestConfig$1): ModelRequest<ModelResponse<T, ResolvedSelectedShape<T, TSelect, TData>>>;
|
|
256
|
-
delete(identifier: string, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<string
|
|
289
|
+
delete(identifier: string, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<string>>;
|
|
257
290
|
new<TData extends Partial<T> = T>(axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<ModelResponse<T, TData>>;
|
|
258
|
-
distinct(field: string, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<string[]
|
|
259
|
-
distinctAdvanced(field: string, conditions: FilterQuery<T>, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<string[]
|
|
260
|
-
count(axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<number
|
|
261
|
-
countAdvanced(filter: FilterQuery<T>,
|
|
262
|
-
access?: 'list' | 'read';
|
|
263
|
-
}, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<number, number>>;
|
|
291
|
+
distinct(field: string, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<string[]>>;
|
|
292
|
+
distinctAdvanced(field: string, conditions: FilterQuery<T>, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<string[]>>;
|
|
293
|
+
count(axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<number>>;
|
|
294
|
+
countAdvanced(filter: FilterQuery<T>, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<number>>;
|
|
264
295
|
read<TData extends Partial<T> = T>(identifier: string, options?: ReadOptions, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<ModelResponse<T, TData>>;
|
|
265
296
|
readAdvanced<TData extends Partial<T> | never = never, TSelect extends Projection = Projection>(identifier: string, args?: ReadAdvancedArgs<TSelect>, options?: ReadAdvancedOptions, axiosRequestConfig?: RequestConfig$1): ModelRequest<ModelResponse<T, ResolvedSelectedShape<T, TSelect, TData>>>;
|
|
266
297
|
readAdvancedFilter<TData extends Partial<T> | never = never, TSelect extends Projection = Projection>(filter: FilterQuery<T>, args?: ReadAdvancedArgs<TSelect>, options?: ReadAdvancedOptions, axiosRequestConfig?: RequestConfig$1): ModelRequest<ModelResponse<T, ResolvedSelectedShape<T, TSelect, TData>>>;
|
|
267
298
|
update<TData extends Partial<T> = T>(identifier: string, data: object, options?: UpdateOptions, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<ModelResponse<T, TData>>;
|
|
268
299
|
updateAdvanced<TData extends Partial<T> | never = never, TSelect extends Projection = Projection>(identifier: string, data: object, args?: UpdateAdvancedArgs<TSelect>, options?: UpdateAdvancedOptions, axiosRequestConfig?: RequestConfig$1): ModelRequest<ModelResponse<T, ResolvedSelectedShape<T, TSelect, TData>>>;
|
|
269
300
|
id(id: string): {
|
|
270
|
-
subs: <S = T>(field:
|
|
301
|
+
subs: <S = never, K extends keyof T = keyof T>(field: K) => {
|
|
271
302
|
list: (axiosRequestConfig?: AxiosRequestConfig<any> & {
|
|
272
303
|
throwOnError?: boolean;
|
|
273
|
-
}) => ModelPromiseMeta & LazyRequest<
|
|
274
|
-
listAdvanced: <TData extends Partial<S> = never, TSelect extends readonly string[] = readonly string[]>(filter?: FilterQuery<S>, args?: {
|
|
304
|
+
}) => ModelPromiseMeta & LazyRequest<SubDocumentListResponse<[S] extends [never] ? NonNullable<T[K]> extends readonly (infer TItem)[] ? TItem : never : S>>;
|
|
305
|
+
listAdvanced: <TData extends Partial<[S] extends [never] ? NonNullable<T[K]> extends readonly (infer TItem)[] ? TItem : never : S> = never, TSelect extends readonly string[] = readonly string[]>(filter?: FilterQuery<[S] extends [never] ? NonNullable<T[K]> extends readonly (infer TItem)[] ? TItem : never : S>, args?: {
|
|
275
306
|
select?: TSelect;
|
|
276
307
|
}, axiosRequestConfig?: AxiosRequestConfig<any> & {
|
|
277
308
|
throwOnError?: boolean;
|
|
278
|
-
}) => ModelPromiseMeta & LazyRequest<
|
|
309
|
+
}) => ModelPromiseMeta & LazyRequest<SubDocumentListResponse<[S] extends [never] ? NonNullable<T[K]> extends readonly (infer TItem)[] ? TItem : never : S, ResolvedSelectedShape<[S] extends [never] ? NonNullable<T[K]> extends readonly (infer TItem)[] ? TItem : never : S, TSelect, TData>>>;
|
|
279
310
|
read: (subId: string, axiosRequestConfig?: AxiosRequestConfig<any> & {
|
|
280
311
|
throwOnError?: boolean;
|
|
281
|
-
}) => ModelPromiseMeta & LazyRequest<
|
|
282
|
-
readAdvanced: <TData extends Partial<S> = never, TSelect_1 extends readonly string[] = readonly string[]>(subId: string, args?: {
|
|
312
|
+
}) => ModelPromiseMeta & LazyRequest<SubDocumentResponse<[S] extends [never] ? NonNullable<T[K]> extends readonly (infer TItem)[] ? TItem : never : S>>;
|
|
313
|
+
readAdvanced: <TData extends Partial<[S] extends [never] ? NonNullable<T[K]> extends readonly (infer TItem)[] ? TItem : never : S> = never, TSelect_1 extends readonly string[] = readonly string[]>(subId: string, args?: {
|
|
283
314
|
select?: TSelect_1;
|
|
284
315
|
populate?: unknown;
|
|
285
316
|
}, axiosRequestConfig?: AxiosRequestConfig<any> & {
|
|
286
317
|
throwOnError?: boolean;
|
|
287
|
-
}) => ModelPromiseMeta & LazyRequest<
|
|
318
|
+
}) => ModelPromiseMeta & LazyRequest<SubDocumentResponse<[S] extends [never] ? NonNullable<T[K]> extends readonly (infer TItem)[] ? TItem : never : S, ResolvedSelectedShape<[S] extends [never] ? NonNullable<T[K]> extends readonly (infer TItem)[] ? TItem : never : S, TSelect_1, TData>>>;
|
|
288
319
|
update: (subId: string, data: object, axiosRequestConfig?: AxiosRequestConfig<any> & {
|
|
289
320
|
throwOnError?: boolean;
|
|
290
|
-
}) => ModelPromiseMeta & LazyRequest<
|
|
321
|
+
}) => ModelPromiseMeta & LazyRequest<SubDocumentResponse<[S] extends [never] ? NonNullable<T[K]> extends readonly (infer TItem)[] ? TItem : never : S>>;
|
|
291
322
|
bulkUpdate: (data: object[], axiosRequestConfig?: AxiosRequestConfig<any> & {
|
|
292
323
|
throwOnError?: boolean;
|
|
293
|
-
}) => ModelPromiseMeta & LazyRequest<
|
|
294
|
-
create: (data: object, axiosRequestConfig?: AxiosRequestConfig<any> & {
|
|
324
|
+
}) => ModelPromiseMeta & LazyRequest<SubDocumentListResponse<[S] extends [never] ? NonNullable<T[K]> extends readonly (infer TItem)[] ? TItem : never : S>>;
|
|
325
|
+
create: (data: object | object[], axiosRequestConfig?: AxiosRequestConfig<any> & {
|
|
295
326
|
throwOnError?: boolean;
|
|
296
|
-
}) => ModelPromiseMeta & LazyRequest<
|
|
327
|
+
}) => ModelPromiseMeta & LazyRequest<SubDocumentListResponse<[S] extends [never] ? NonNullable<T[K]> extends readonly (infer TItem)[] ? TItem : never : S>>;
|
|
297
328
|
delete: (subId: string, axiosRequestConfig?: AxiosRequestConfig<any> & {
|
|
298
329
|
throwOnError?: boolean;
|
|
299
|
-
}) => ModelPromiseMeta & LazyRequest<Response<string
|
|
330
|
+
}) => ModelPromiseMeta & LazyRequest<Response<string>>;
|
|
300
331
|
};
|
|
301
332
|
fetch: (args?: ReadAdvancedArgs, options?: ReadAdvancedOptions, axiosRequestConfig?: RequestConfig$1) => ModelRequest<ModelResponse<T, Partial<T>>>;
|
|
302
333
|
};
|
|
@@ -332,15 +363,109 @@ declare class DataService<T> extends Service {
|
|
|
332
363
|
readAdvancedFilter<TData extends Partial<T> | never = never, TSelect extends Projection = Projection>(filter: FilterQuery<T>, args?: DataReadAdvancedArgs<TSelect>, options?: DataReadAdvancedOptions, axiosRequestConfig?: RequestConfig): DataRequest<DataResponse<ResolvedSelectedShape<T, TSelect, TData>>>;
|
|
333
364
|
}
|
|
334
365
|
|
|
366
|
+
/**
|
|
367
|
+
* Thrown by {@link Model.save} when the wrapper cannot determine whether
|
|
368
|
+
* to create or update. This is the "no silent create from a projected
|
|
369
|
+
* read" guarantee (ARC-21): when a read projection omits `_id` AND no
|
|
370
|
+
* persistence identity was captured at read time (the case for
|
|
371
|
+
* `readAdvancedFilter` and other list/filter reads that do not know a
|
|
372
|
+
* single document id), `save()` refuses to POST a new document the
|
|
373
|
+
* caller may not have meant to create. Callers can recover by reading the
|
|
374
|
+
* document with `read(id)` / `readAdvanced(id, ...)` (both capture a
|
|
375
|
+
* persistence identity), or by including `_id` in the projection.
|
|
376
|
+
*/
|
|
377
|
+
declare class MissingPersistenceIdentityError extends Error {
|
|
378
|
+
constructor(message: string);
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* A dirty-tracking wrapper around a model document. Constructed via
|
|
382
|
+
* {@link ModelService.create}, {@link ModelService.read},
|
|
383
|
+
* {@link ModelService.findOne}, or the list methods that return
|
|
384
|
+
* `Model<T>[]`. Property access through the wrapper directly reads/writes
|
|
385
|
+
* the underlying data; `save()` persists only the paths flagged dirty
|
|
386
|
+
* since the last save and merges the server response per the documented
|
|
387
|
+
* concurrency contract.
|
|
388
|
+
*
|
|
389
|
+
* `Model.create<T>(data, service)` is typed as `Model<T, TData> & TData` so
|
|
390
|
+
* callers can read/write fields directly on the wrapper (`user.role =
|
|
391
|
+
* 'owner'`) while still calling `save()`/`reset()`/`isDirty(...)`. The
|
|
392
|
+
* returned wrapper is a fresh snapshot of the post-operation local state;
|
|
393
|
+
* mutating it does not affect sibling wrappers created from the same
|
|
394
|
+
* underlying document.
|
|
395
|
+
*
|
|
396
|
+
* Persistence identity (ARC-21): identity is stored SEPARATELY from the
|
|
397
|
+
* projected document data. When a service reads a single document by id
|
|
398
|
+
* (`read`, `readAdvanced`), it threads an explicit `persistenceId` into
|
|
399
|
+
* `Model.create(...)` so that `save()` resolves the create-vs-update
|
|
400
|
+
* branch from that captured identity, not from `_data._id`. This means a
|
|
401
|
+
* read projection that deliberately omits `_id` (e.g. `select: { name: 1,
|
|
402
|
+
* _id: 0 }`) cannot silently cause a subsequent `save()` to create a
|
|
403
|
+
* duplicate — `save()` updates the same document the wrapper was read
|
|
404
|
+
* from. When neither a `persistenceId` nor an `_id` is present (e.g. an
|
|
405
|
+
* `readAdvancedFilter` projection that strips `_id`), `save()` throws an
|
|
406
|
+
* explicit `MissingPersistenceIdentityError` rather than POSTing a new
|
|
407
|
+
* document the user did not mean to create.
|
|
408
|
+
*/
|
|
335
409
|
declare class Model<T extends Document, TData extends Partial<T> = T> {
|
|
336
410
|
private _data;
|
|
337
411
|
private _snapshot;
|
|
338
412
|
private readonly _service;
|
|
339
413
|
private modifiedPaths;
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
414
|
+
private _persistenceId;
|
|
415
|
+
private readonly _fromExisting;
|
|
416
|
+
constructor(data: TData, adapter: ModelService<T>, persistenceId?: string, fromExisting?: boolean);
|
|
417
|
+
static create<T extends Document, TData extends Partial<T> = T>(data: TData, adapter: ModelService<T>, persistenceId?: string, fromExisting?: boolean): Model<T, TData> & TData;
|
|
418
|
+
/**
|
|
419
|
+
* Persists the currently dirty paths to the server, then merges the
|
|
420
|
+
* server's response back into local state.
|
|
421
|
+
*
|
|
422
|
+
* Concurrency contract:
|
|
423
|
+
*
|
|
424
|
+
* 1. Submitted paths and their values are snapshotted before the request
|
|
425
|
+
* starts, so an in-flight response cannot wipe edits that were made
|
|
426
|
+
* while the request was pending.
|
|
427
|
+
* 2. On success, a submitted path is cleared from `modifiedPaths` only if
|
|
428
|
+
* its current local value still equals the submitted value — i.e. the
|
|
429
|
+
* user has not concurrently re-edited it to a different value.
|
|
430
|
+
* 3. Server-returned values overwrite local values for paths the user did
|
|
431
|
+
* NOT concurrently re-modify during the in-flight save; for paths the
|
|
432
|
+
* user did concurrently re-modify, the local value is preserved and
|
|
433
|
+
* the dirty flag is retained so the concurrent edit is resubmitted on
|
|
434
|
+
* the next `save()`. (Deterministic conflict rule: the newer local
|
|
435
|
+
* edit wins for the same path; the server value becomes its reset
|
|
436
|
+
* baseline without replacing the newer local value.)
|
|
437
|
+
* 4. On failure, no dirty state is cleared and no local value is
|
|
438
|
+
* overwritten; the caller can retry `save()` with the same set.
|
|
439
|
+
* 5. The return value echoes `{ ...result, data }` where `data` is a
|
|
440
|
+
* refreshed `Model` snapshot of the post-save local state (or `null`
|
|
441
|
+
* on failure), matching `ModelResponse<T, TData>`.
|
|
442
|
+
*
|
|
443
|
+
* Persistence identity (ARC-21): create-vs-update is resolved from a
|
|
444
|
+
* captured persistence identity rather than from the projected `_data`
|
|
445
|
+
* payload alone, so a read that strips `_id` (e.g. `select: { name: 1,
|
|
446
|
+
* _id: 0 }`) cannot turn a subsequent `save()` into a silent create of
|
|
447
|
+
* a duplicate. When `_data._id` is present it takes precedence so callers
|
|
448
|
+
* can still deliberately aim `_id` at a bogus id to observe a failing
|
|
449
|
+
* save. When neither `_data._id` nor a captured persistence identity is
|
|
450
|
+
* available (e.g. `readAdvancedFilter` with an `_id`-excluding
|
|
451
|
+
* projection), `save()` throws `MissingPersistenceIdentityError` instead
|
|
452
|
+
* of POSTing a new document.
|
|
453
|
+
*/
|
|
454
|
+
save(reqConfig?: AxiosRequestConfig): Promise<ModelResponse<T, TData>>;
|
|
343
455
|
isDirty(path?: keyof TData | string): boolean;
|
|
456
|
+
/**
|
|
457
|
+
* Marks a path dirty and skips snapshot reconciliation. This is the
|
|
458
|
+
* explicit "include this path on the next save()" escape hatch: even when
|
|
459
|
+
* the effective value still equals the snapshot, the path stays dirty so
|
|
460
|
+
* callers can force a field to be re-sent to the server (e.g., to retrigger
|
|
461
|
+
* server-side defaults or to re-submit a value that another client may
|
|
462
|
+
* have reverted).
|
|
463
|
+
*
|
|
464
|
+
* For implicit writes that reconcile against the snapshot automatically
|
|
465
|
+
* (reverting a field to its baseline clears the dirty flag), use `set()`,
|
|
466
|
+
* `assign(...)`, or direct property assignment — those entry points all
|
|
467
|
+
* run `reconcilePath` after the write.
|
|
468
|
+
*/
|
|
344
469
|
markModified(path: keyof TData | string): this;
|
|
345
470
|
get<TKey extends keyof TData>(path: TKey): TData[TKey];
|
|
346
471
|
get(path: string): unknown;
|
|
@@ -350,7 +475,6 @@ declare class Model<T extends Document, TData extends Partial<T> = T> {
|
|
|
350
475
|
reset(): this;
|
|
351
476
|
toObject(): TData;
|
|
352
477
|
toJSON(): TData;
|
|
353
|
-
private updateModel;
|
|
354
478
|
private replaceData;
|
|
355
479
|
private initializeDirtyState;
|
|
356
480
|
private prepareData;
|
|
@@ -359,12 +483,36 @@ declare class Model<T extends Document, TData extends Partial<T> = T> {
|
|
|
359
483
|
private definePublicDataProps;
|
|
360
484
|
private trackModified;
|
|
361
485
|
private normalizePath;
|
|
486
|
+
/**
|
|
487
|
+
* Removes `path` from the dirty set when its current top-level value deeply
|
|
488
|
+
* equals the snapshot baseline. Used uniformly by `set()`, `assign()`,
|
|
489
|
+
* public property setters (via the proxy), and `markModified()` so all
|
|
490
|
+
* entry points share the same tracking rule.
|
|
491
|
+
*
|
|
492
|
+
* Note: `_id` is intentionally never reconciled away here — it is excluded
|
|
493
|
+
* from `initializeDirtyState` and managed explicitly during `save()`
|
|
494
|
+
* reconciliation.
|
|
495
|
+
*/
|
|
496
|
+
private reconcilePath;
|
|
362
497
|
}
|
|
363
498
|
|
|
364
499
|
type AnyArray<T> = T[] | ReadonlyArray<T>;
|
|
365
500
|
type Unpacked<T> = T extends (infer U)[] ? U : T extends ReadonlyArray<infer U> ? U : T;
|
|
366
|
-
|
|
367
|
-
|
|
501
|
+
/**
|
|
502
|
+
* Values a known field condition accepts without an operator wrapper.
|
|
503
|
+
*
|
|
504
|
+
* - The scalar value itself (`name: 'Max'`).
|
|
505
|
+
* - An array of scalars — the sibling server expands this to an `$in` query.
|
|
506
|
+
* - For array-typed document fields, the element type is also accepted as a
|
|
507
|
+
* bare condition (e.g. `tags: 'vip'` matches any array containing `'vip'`).
|
|
508
|
+
* - RegExp is only accepted where `T` is (or unwraps to) `string`.
|
|
509
|
+
*
|
|
510
|
+
* The naked `unknown` that previously terminated this union is gone. Use
|
|
511
|
+
* `ServerSideCast<T>` / `DottedPathFilter<T>` for the cases that needed it
|
|
512
|
+
* (dynamic dotted paths and explicit server-side casting).
|
|
513
|
+
*/
|
|
514
|
+
type ApplyBasicQueryCasting<T> = T | T[] | (T extends AnyArray<unknown> ? Unpacked<T> : never) | (T extends string ? RegExp : never);
|
|
515
|
+
type Condition<T> = ApplyBasicQueryCasting<T> | QuerySelector<ApplyBasicQueryCasting<T>> | LazyRequest<unknown>;
|
|
368
516
|
type _FilterQuery<T> = {
|
|
369
517
|
[P in keyof T]?: Condition<T[P]>;
|
|
370
518
|
} & RootQuerySelector<T>;
|
|
@@ -386,7 +534,6 @@ type RootQuerySelector<T> = {
|
|
|
386
534
|
$where?: string | ((...args: never[]) => unknown);
|
|
387
535
|
/** @see https://www.mongodb.com/docs/manual/reference/operator/query/comment/#op._S_comment */
|
|
388
536
|
$comment?: string;
|
|
389
|
-
[key: string]: unknown;
|
|
390
537
|
};
|
|
391
538
|
type QuerySelector<T> = {
|
|
392
539
|
$eq?: T;
|
|
@@ -410,11 +557,56 @@ type QuerySelector<T> = {
|
|
|
410
557
|
$regex?: T extends string ? RegExp | string : never;
|
|
411
558
|
$options?: T extends string ? string : never;
|
|
412
559
|
};
|
|
560
|
+
/**
|
|
561
|
+
* Escape hatch for dynamic dotted paths and explicit server-side casting.
|
|
562
|
+
*
|
|
563
|
+
* `DottedPathFilter<T>` restores schema-less field matching: every
|
|
564
|
+
* `Record<string, unknown>` value is forwarded to the sibling server
|
|
565
|
+
* untouched, so dotted paths such as `'user.friends.name'` and values cast
|
|
566
|
+
* on the server side still typecheck.
|
|
567
|
+
*
|
|
568
|
+
* Crucially, `DottedPathFilter<T>` restores this looseness only when the
|
|
569
|
+
* caller explicitly asks for it; it does NOT weaken the typed
|
|
570
|
+
* `FilterQuery<T>` surface, so a stray invalid value on a known field still
|
|
571
|
+
* fails to compile.
|
|
572
|
+
*/
|
|
573
|
+
type DottedPathFilter<T> = _FilterQuery<T> & {
|
|
574
|
+
[key: string]: unknown;
|
|
575
|
+
};
|
|
576
|
+
/**
|
|
577
|
+
* Escape hatch for explicit server-side casting. Use this at the call site
|
|
578
|
+
* of any typed `FilterQuery<T>` parameter when you need to forward a value
|
|
579
|
+
* the client type cannot express (server-side casting, aggregation-shaped
|
|
580
|
+
* values for `$expr`, or a dotted-path condition that the typed surface does
|
|
581
|
+
* not model). The sibling server accepts arbitrary objects/arrays for
|
|
582
|
+
* filters (`objectOrArraySchema`), so this never causes a runtime failure;
|
|
583
|
+
* it is purely a deliberate compile-time opt-out.
|
|
584
|
+
*/
|
|
585
|
+
type ServerSideCast<T> = DottedPathFilter<T>;
|
|
413
586
|
|
|
414
587
|
/**
|
|
415
588
|
* Wraps a lazy promise function with optional metadata.
|
|
416
|
-
*
|
|
417
|
-
*
|
|
589
|
+
*
|
|
590
|
+
* The promise is only created when `.then()`, `.catch()`, `.finally()`, or
|
|
591
|
+
* `.exec()` is called, and a single underlying promise is shared across all
|
|
592
|
+
* of those entry points so repeated chaining attaches to the same execution
|
|
593
|
+
* rather than re-invoking the executor.
|
|
594
|
+
*
|
|
595
|
+
* Behavior notes:
|
|
596
|
+
*
|
|
597
|
+
* - **Sync executor failures become rejections.** The executor is invoked
|
|
598
|
+
* through `Promise.resolve().then(execute)`, so a synchronous throw from
|
|
599
|
+
* `execute` is converted to a rejected promise and reaches `.catch()`
|
|
600
|
+
* and `await` as a rejection rather than escaping synchronously.
|
|
601
|
+
* - **Metadata is private.** Each meta entry is installed with
|
|
602
|
+
* `Object.defineProperty(..., { enumerable: false, writable: false,
|
|
603
|
+
* configurable: false })` so consumers cannot accidentally iterate,
|
|
604
|
+
* serialize, or reassign it. Direct property reads (`prom.__query`) still
|
|
605
|
+
* work for adapter-internal machinery (e.g. `adapter.group(...)`).
|
|
606
|
+
* - **One execution.** The first call to `exec()`, `.then()`,
|
|
607
|
+
* `.catch()`, or `.finally()` caches the underlying promise and stamps
|
|
608
|
+
* the wrapper with `STARTED_KEY = true`. Subsequent calls reuse the same
|
|
609
|
+
* promise and never re-invoke the executor.
|
|
418
610
|
*/
|
|
419
611
|
declare const wrapLazyPromise: <T, M = undefined>(promiseFn: () => Promise<T>, meta?: M) => M & LazyRequest<T>;
|
|
420
612
|
|
|
@@ -435,6 +627,7 @@ type Sort = string | {
|
|
|
435
627
|
[key: string]: SortOrder;
|
|
436
628
|
} | [string, SortOrder][] | undefined | null;
|
|
437
629
|
type FilterQuery<T> = _FilterQuery<T>;
|
|
630
|
+
|
|
438
631
|
interface Include {
|
|
439
632
|
model: string;
|
|
440
633
|
op: 'list' | 'read' | 'count';
|
|
@@ -455,19 +648,79 @@ interface Populate {
|
|
|
455
648
|
interface Document {
|
|
456
649
|
_id?: string;
|
|
457
650
|
}
|
|
458
|
-
|
|
459
|
-
|
|
651
|
+
/**
|
|
652
|
+
* Successful response. `raw` and `data` are non-null and `success` is
|
|
653
|
+
* narrowed to `true` so `if (result.success)` exposes the documented
|
|
654
|
+
* payload shape. `message` is initialized for symmetry with failures but
|
|
655
|
+
* may be the empty string when the server omits a message on success.
|
|
656
|
+
*/
|
|
657
|
+
interface SuccessResult<T1, T2 = T1> {
|
|
658
|
+
success: true;
|
|
460
659
|
raw: T1;
|
|
461
660
|
data: T2;
|
|
462
661
|
message: string;
|
|
463
662
|
status: number;
|
|
464
663
|
headers: Record<string, string>;
|
|
465
664
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
665
|
+
/**
|
|
666
|
+
* Failure response. `raw` carries the server error payload (or `null`
|
|
667
|
+
* when no response body was received, e.g. a network error). `data` is
|
|
668
|
+
* always `null` on failure. `message` is populated from the structured
|
|
669
|
+
* problem payload when possible; `status` is the failing HTTP status
|
|
670
|
+
* (or `0` when no response was received).
|
|
671
|
+
*/
|
|
672
|
+
interface FailureResult<TError = unknown> {
|
|
673
|
+
success: false;
|
|
674
|
+
raw: TError | null;
|
|
675
|
+
data: null;
|
|
676
|
+
message: string;
|
|
677
|
+
status: number;
|
|
678
|
+
headers: Record<string, string>;
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Discriminated response union. Branch on `result.success` to narrow
|
|
682
|
+
* `raw`/`data` to their successful shapes or to the documented error
|
|
683
|
+
* payload.
|
|
684
|
+
*
|
|
685
|
+
* `T1` is the successful `raw` payload type; `T2` is the successful `data`
|
|
686
|
+
* payload type (after client wrapping, e.g. `Model<T>`). `TError` is the
|
|
687
|
+
* optional server error payload type and defaults to `unknown`. On failure,
|
|
688
|
+
* `data` is `null` and `raw` is `TError | null`, never the success payload
|
|
689
|
+
* type unless a caller explicitly chooses that error type.
|
|
690
|
+
*/
|
|
691
|
+
type Response<T1, T2 = T1, TError = unknown> = SuccessResult<T1, T2> | FailureResult<TError>;
|
|
692
|
+
type ModelResponse<T extends Document, TData extends Partial<T> = T> = Response<TData, Model<T, TData> & TData>;
|
|
693
|
+
type ArrayModelResponse<T extends Document, TData extends Partial<T> = T> = Response<TData[], (Model<T, TData> & TData)[]>;
|
|
694
|
+
/**
|
|
695
|
+
* `ListModelResponse` always carries `totalCount` on both branches. The field
|
|
696
|
+
* defaults to `0` at runtime on failure or when the server did not emit count
|
|
697
|
+
* metadata (`includeCount: false`), so callers that read it without narrowing
|
|
698
|
+
* see a deterministic number rather than `undefined`.
|
|
699
|
+
*/
|
|
700
|
+
type ListModelResponse<T extends Document, TData extends Partial<T> = T> = ArrayModelResponse<T, TData> & {
|
|
469
701
|
totalCount: number;
|
|
470
702
|
};
|
|
703
|
+
/**
|
|
704
|
+
* Subdocument responses deliberately do NOT wrap `data` in `Model<S>`.
|
|
705
|
+
* Returning a save-capable `Model<S>` here was unsafe because `Model.save()`
|
|
706
|
+
* would target the parent route with the subdocument `_id` instead of
|
|
707
|
+
* `/:parentId/:sub/:subId`. Subdocument callers that need persistence must
|
|
708
|
+
* call `subService.update(subId, data)` (or `create`/`bulkUpdate`) explicitly
|
|
709
|
+
* with the parent-scoped helper returned by `id(parentId).subs(field)`.
|
|
710
|
+
*
|
|
711
|
+
* `SubDocumentResponse` is the single-document shape; `data` is the plain
|
|
712
|
+
* subdocument payload or `null` on failure.
|
|
713
|
+
*/
|
|
714
|
+
type SubDocumentResponse<S, TData extends Partial<S> = S> = Response<TData, TData>;
|
|
715
|
+
/**
|
|
716
|
+
* Subdocument list/array responses. `data` is the plain array of subdocument
|
|
717
|
+
* payloads (no `Model` wrapping) and `raw` is the server's original array
|
|
718
|
+
* payload. `count` mirrors the server's `count` field (the length of the
|
|
719
|
+
* returned array); the sibling server never emits a `totalCount` here.
|
|
720
|
+
*/
|
|
721
|
+
type SubDocumentListResponse<S, TData extends Partial<S> = S> = Response<TData[], TData[]> & {
|
|
722
|
+
count: number;
|
|
723
|
+
};
|
|
471
724
|
interface Task {
|
|
472
725
|
type: string;
|
|
473
726
|
args: unknown;
|
|
@@ -478,7 +731,15 @@ type RootDataOperation = 'list' | 'read';
|
|
|
478
731
|
interface RootModelQueryMeta {
|
|
479
732
|
target: 'model';
|
|
480
733
|
name: string;
|
|
481
|
-
|
|
734
|
+
/**
|
|
735
|
+
* Carries the model name when this entry is consumed as a sub-query
|
|
736
|
+
* source: the sibling server reads `model` from a `$$sq` payload to
|
|
737
|
+
* resolve the target model service. Top-level root entries omit `model`
|
|
738
|
+
* (the sibling `RootQueryEntry` schema uses `name`); the server schema
|
|
739
|
+
* permits extra fields via `.passthrough()`, so a stray `model` is
|
|
740
|
+
* harmless there.
|
|
741
|
+
*/
|
|
742
|
+
model?: string;
|
|
482
743
|
op: RootModelOperation;
|
|
483
744
|
id?: string;
|
|
484
745
|
sub?: string;
|
|
@@ -505,6 +766,7 @@ interface RootDataQueryMeta {
|
|
|
505
766
|
type RootQueryMeta = RootModelQueryMeta | RootDataQueryMeta;
|
|
506
767
|
interface ModelPromiseMeta {
|
|
507
768
|
__op: string;
|
|
769
|
+
__throwOnError?: boolean;
|
|
508
770
|
__query: RootModelQueryMeta;
|
|
509
771
|
__requestConfig?: AxiosRequestConfig;
|
|
510
772
|
__service?: ModelService<Document>;
|
|
@@ -520,6 +782,7 @@ type ListDataResponse<T> = ArrayDataResponse<T> & {
|
|
|
520
782
|
};
|
|
521
783
|
interface DataPromiseMeta {
|
|
522
784
|
__op: string;
|
|
785
|
+
__throwOnError?: boolean;
|
|
523
786
|
__query: RootDataQueryMeta;
|
|
524
787
|
__requestConfig?: AxiosRequestConfig;
|
|
525
788
|
__service?: DataService<unknown>;
|
|
@@ -532,15 +795,86 @@ interface WrapOptions {
|
|
|
532
795
|
pathParams?: Record<string, string | number>;
|
|
533
796
|
}
|
|
534
797
|
|
|
798
|
+
/**
|
|
799
|
+
* Adapter-scoped cache control surface returned by `useCacheInterceptors`.
|
|
800
|
+
* The adapter delegates `clearCache()` to {@link clear} on credential
|
|
801
|
+
* transitions (login/logout/token refresh/tenant change) and
|
|
802
|
+
* `disposeCache()` to {@link dispose} when the adapter is torn down to
|
|
803
|
+
* release cache timers so they do not keep a Node process alive.
|
|
804
|
+
*/
|
|
805
|
+
interface CacheController {
|
|
806
|
+
clear(): void;
|
|
807
|
+
dispose(): void;
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Resolves a stable, non-secret identity partition token for a credentialed
|
|
811
|
+
* request. Requests that share a token share cache entries; requests with
|
|
812
|
+
* different tokens never do. Returning `undefined` bypasses the cache for that
|
|
813
|
+
* credentialed request, so credentials cannot be reused across identities.
|
|
814
|
+
*
|
|
815
|
+
* The token is mixed into the cache key alongside the URL and request body. Do
|
|
816
|
+
* not return raw cookies, authorization values, or other secrets; sensitive
|
|
817
|
+
* auth headers are excluded from cache keys regardless of the returned token.
|
|
818
|
+
*/
|
|
819
|
+
type CachePartitioner = (config: InternalAxiosRequestConfig) => string | undefined;
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Options for {@link createAdapter}. `rootRouterPath` is the single-segment
|
|
823
|
+
* path used by `adapter.group(...)` for batched root requests
|
|
824
|
+
* (defaults to `'root'`). Per-adapter `onSuccess`/`onFailure`/`throwOnError`
|
|
825
|
+
* apply to every service created by this adapter and are overridden by
|
|
826
|
+
* per-service options on {@link ModelServiceOptions} and
|
|
827
|
+
* {@link DataServiceOptions}.
|
|
828
|
+
*
|
|
829
|
+
* Cache controls (only in effect when `cacheTTL > 0`):
|
|
830
|
+
*
|
|
831
|
+
* - `cacheTTL` — seconds a cached GET response is reused before revalidation.
|
|
832
|
+
* - `cachePartition` — required to cache credentialed requests safely (see
|
|
833
|
+
* {@link CachePartitioner}); credentialed requests without a stable,
|
|
834
|
+
* non-secret partition token bypass the cache so one identity cannot
|
|
835
|
+
* receive a response created under another.
|
|
836
|
+
* - `cacheCapacity` — bounds the number of cached entries; defaults to 100 and
|
|
837
|
+
* evicts the LRU entry when the limit is exceeded.
|
|
838
|
+
*
|
|
839
|
+
* Use the returned adapter's `clearCache()` on credential transitions
|
|
840
|
+
* (login/logout/token refresh/tenant change) and `disposeCache()` when the
|
|
841
|
+
* adapter is no longer needed to release cache timers.
|
|
842
|
+
*/
|
|
535
843
|
interface AdapterOptions {
|
|
536
844
|
rootRouterPath?: string;
|
|
537
845
|
onSuccess?: ResponseCallback;
|
|
538
846
|
onFailure?: ResponseCallback;
|
|
539
847
|
throwOnError?: boolean;
|
|
540
848
|
cacheTTL?: number;
|
|
849
|
+
/**
|
|
850
|
+
* Partition strategy for credentialed cache entries. When the adapter is
|
|
851
|
+
* credentialed (which is the default), caching is only enabled for requests
|
|
852
|
+
* whose `cachePartition` returns a stable, non-secret identity token. Requests
|
|
853
|
+
* without a partition key bypass the cache so that one identity can never
|
|
854
|
+
* receive a response created under another identity.
|
|
855
|
+
*
|
|
856
|
+
* The returned value must be a stable, non-secret token (for example a user
|
|
857
|
+
* id or tenant id). Never return raw cookies, authorization values, or other
|
|
858
|
+
* secrets; those headers are excluded from cache keys regardless.
|
|
859
|
+
*/
|
|
860
|
+
cachePartition?: CachePartitioner;
|
|
861
|
+
/**
|
|
862
|
+
* Maximum number of cached entries retained per adapter. Defaults to 100.
|
|
863
|
+
*/
|
|
864
|
+
cacheCapacity?: number;
|
|
541
865
|
modelDefaults?: Defaults;
|
|
542
866
|
dataDefaults?: DataDefaults;
|
|
543
867
|
}
|
|
868
|
+
/**
|
|
869
|
+
* Options for {@link createAdapter}.createModelService<T>. Mirrors the
|
|
870
|
+
* server-side `access-router` model route configuration: `modelName` is the
|
|
871
|
+
* server-registered model name, `basePath` is the URL segment relative to
|
|
872
|
+
* the adapter `baseURL` (e.g. `'users'` resolves to `${baseURL}/users`),
|
|
873
|
+
* `queryPath` defaults to `'__query'` and `mutationPath` defaults to
|
|
874
|
+
* `'__mutation'` — match these to the server's `queryRouteSegment` and
|
|
875
|
+
* mutation route configuration. Per-service `onSuccess`/`onFailure`/
|
|
876
|
+
* `throwOnError` override the adapter-level defaults.
|
|
877
|
+
*/
|
|
544
878
|
interface ModelServiceOptions {
|
|
545
879
|
modelName: string;
|
|
546
880
|
basePath: string;
|
|
@@ -550,6 +884,14 @@ interface ModelServiceOptions {
|
|
|
550
884
|
onFailure?: ResponseCallback;
|
|
551
885
|
throwOnError?: boolean;
|
|
552
886
|
}
|
|
887
|
+
/**
|
|
888
|
+
* Options for {@link createAdapter}.createDataService<T>. Mirrors the
|
|
889
|
+
* server-side `access-router` data route configuration: `dataName` is the
|
|
890
|
+
* server-registered data name, `basePath` is the URL segment relative to
|
|
891
|
+
* the adapter `baseURL`, `queryPath` defaults to `'__query'`. Per-service
|
|
892
|
+
* `onSuccess`/`onFailure`/`throwOnError` override the adapter-level
|
|
893
|
+
* defaults.
|
|
894
|
+
*/
|
|
553
895
|
interface DataServiceOptions {
|
|
554
896
|
dataName: string;
|
|
555
897
|
basePath: string;
|
|
@@ -561,13 +903,39 @@ interface DataServiceOptions {
|
|
|
561
903
|
/**
|
|
562
904
|
* Creates a typed API adapter for `@web-ts-toolkit/access-router` model and data routes.
|
|
563
905
|
*
|
|
906
|
+
* The adapter owns its own Axios instance, optional request cache, and
|
|
907
|
+
* per-adapter identity token (used by {@link group} to reject requests
|
|
908
|
+
* owned by a different adapter before any network activity). The returned
|
|
909
|
+
* adapter is frozen and exposes:
|
|
910
|
+
*
|
|
911
|
+
* - `axios` — the underlying Axios instance for advanced configuration or
|
|
912
|
+
* attaching interceptors (the package's cache interceptors are installed
|
|
913
|
+
* when `cacheTTL > 0`).
|
|
914
|
+
* - `createModelService<T>(...)` / `createDataService<T>(...)` — typed
|
|
915
|
+
* factories for the model and data route clients.
|
|
916
|
+
* - `clearCache()` / `disposeCache()` — adapter-scoped cache controls
|
|
917
|
+
* installed by {@link AdapterOptions.cacheTTL}. `clearCache()` drops all
|
|
918
|
+
* cached entries (call on login/logout/token refresh/tenant switch);
|
|
919
|
+
* `disposeCache()` also releases the cache's timers so a long-lived
|
|
920
|
+
* adapter can be torn down cleanly.
|
|
921
|
+
* - `wrapGet` / `wrapPost` / `wrapPut` / `wrapPatch` / `wrapDelete` —
|
|
922
|
+
* low-level helpers that wrap a raw Axios call to a single path segment
|
|
923
|
+
* with `pathParams`/`queryParams` templating and the package's
|
|
924
|
+
* normalized success/failure handling.
|
|
925
|
+
* - `group(...)` — batches multiple lazy requests created by this
|
|
926
|
+
* adapter's services into one root round trip. Rejected before network
|
|
927
|
+
* activity if any input has already started execution or was created by
|
|
928
|
+
* a different adapter.
|
|
929
|
+
*
|
|
564
930
|
* @example
|
|
565
931
|
* const adapter = createAdapter({ baseURL: 'http://localhost:3000/api' });
|
|
566
932
|
* const userService = adapter.createModelService<User>({ modelName: 'User', basePath: 'users' });
|
|
567
933
|
*/
|
|
568
934
|
declare function createAdapter(axiosConfig?: AxiosRequestConfig, adapterOptions?: AdapterOptions): Readonly<{
|
|
569
935
|
axios: axios.AxiosInstance;
|
|
570
|
-
|
|
936
|
+
clearCache: () => void;
|
|
937
|
+
disposeCache: () => void;
|
|
938
|
+
createModelService: <T extends Document>({ modelName, basePath, queryPath, mutationPath, onSuccess, onFailure, throwOnError, }: ModelServiceOptions, defaults?: Defaults) => ModelService<T>;
|
|
571
939
|
createDataService: <T>({ dataName, basePath, queryPath, onSuccess, onFailure, throwOnError }: DataServiceOptions, defaults?: DataDefaults) => DataService<T>;
|
|
572
940
|
wrapGet: <T = unknown>(url: string, defaultConfig?: AxiosRequestConfig) => (options?: WrapOptions, requestConfig?: AxiosRequestConfig) => Promise<axios.AxiosResponse<T, any, {}>>;
|
|
573
941
|
wrapPost: <T = unknown>(url: string, defaultConfig?: AxiosRequestConfig) => (data?: unknown, options?: WrapOptions, requestConfig?: AxiosRequestConfig) => Promise<axios.AxiosResponse<T, any, {}>>;
|
|
@@ -596,4 +964,4 @@ declare function removeItemById<T extends {
|
|
|
596
964
|
_id: string;
|
|
597
965
|
}>(items: T[], targetItem: T): T[];
|
|
598
966
|
|
|
599
|
-
export { type AdditionalReqConfig, type ArrayDataResponse, type ArrayModelResponse, type CreateAdvancedArgs, type CreateAdvancedOptions, type CreateOptions, CustomHeaders, type DataDefaults, type DataListAdvancedArgs, type DataListAdvancedOptions, type DataListArgs, type DataListOptions, type DataPromiseMeta, type DataReadAdvancedArgs, type DataReadAdvancedOptions, type DataReadOptions, type DataRequest, type DataResponse, DataService, type Defaults, type Document, type FilterQuery, type Include, type KeyValueProjection, type LazyRequest, type ListAdvancedArgs, type ListAdvancedOptions, type ListArgs, type ListDataResponse, type ListModelResponse, type ListOptions, Model, type ModelPromiseMeta, type ModelRequest, type ModelResponse, ModelService, type Populate, type PopulateAccess, type Projection, type ReadAdvancedArgs, type ReadAdvancedOptions, type ReadOptions, type ResolvedSelectedShape, type Response, type ResponseCallback, type ResultError, type RootDataQueryMeta, type RootModelQueryMeta, type RootQueryMeta, type SelectedKeys, type SelectedShape, Service, ServiceError, type Sort, type SortOrder, type SubQueryOptions, type Task, type UpdateAdvancedArgs, type UpdateAdvancedOptions, type UpdateOptions, type UpsertAdvancedArgs, type UpsertAdvancedOptions, type UpsertOptions, type WrapOptions, createAdapter, removeItemById, replaceItemById, wrapLazyPromise };
|
|
967
|
+
export { type AdapterOptions, type AdditionalReqConfig, type ArrayDataResponse, type ArrayModelResponse, type CacheController, type CachePartitioner, type CreateAdvancedArgs, type CreateAdvancedOptions, type CreateOptions, CustomHeaders, type DataDefaults, type DataListAdvancedArgs, type DataListAdvancedOptions, type DataListArgs, type DataListOptions, type DataPromiseMeta, type DataReadAdvancedArgs, type DataReadAdvancedOptions, type DataReadOptions, type DataRequest, type DataResponse, DataService, type DataServiceOptions, type Defaults, type Document, type DottedPathFilter, type FailureResult, type FilterQuery, type Include, type KeyValueProjection, type LazyRequest, type ListAdvancedArgs, type ListAdvancedOptions, type ListArgs, type ListDataResponse, type ListModelResponse, type ListOptions, MissingPersistenceIdentityError, Model, type ModelPromiseMeta, type ModelRequest, type ModelResponse, ModelService, type ModelServiceOptions, type Populate, type PopulateAccess, type Projection, type ReadAdvancedArgs, type ReadAdvancedOptions, type ReadOptions, type ResolvedSelectedShape, type Response, type ResponseCallback, type ResultError, type RootDataQueryMeta, type RootModelQueryMeta, type RootQueryMeta, type SelectedKeys, type SelectedShape, type ServerSideCast, Service, ServiceError, type Sort, type SortOrder, type SubDocumentListResponse, type SubDocumentResponse, type SubQueryOptions, type SuccessResult, type Task, type UpdateAdvancedArgs, type UpdateAdvancedOptions, type UpdateOptions, type UpsertAdvancedArgs, type UpsertAdvancedOptions, type UpsertOptions, type WrapOptions, createAdapter, removeItemById, replaceItemById, wrapLazyPromise };
|