@web-ts-toolkit/access-router-client 0.38.0 → 0.40.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 +48 -25
- package/index.d.mts +95 -60
- package/index.d.ts +95 -60
- package/index.js +605 -373
- package/index.mjs +605 -373
- package/llms.txt +9 -6
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -98,12 +98,16 @@ consumer needs:
|
|
|
98
98
|
defaults to `'__mutation'`; `rootRouterPath` defaults to `'root'`.
|
|
99
99
|
- **Cache & authentication policy:** credentialed requests are **never**
|
|
100
100
|
cached unless `cachePartition` returns a stable, non-secret identity token.
|
|
101
|
+
Browser cookie credentials, explicit `Authorization`/proxy authorization
|
|
102
|
+
headers, API-key style headers, and Node `Cookie` headers supplied on the
|
|
103
|
+
request config are all treated as credentialed.
|
|
101
104
|
Sensitive headers (`authorization`, `cookie`, `set-cookie`,
|
|
102
105
|
`proxy-authorization`, `www-authenticate`) are excluded from cache keys
|
|
103
106
|
regardless of the partition token. Only GET requests with supported JSON or
|
|
104
107
|
text response semantics are cached; mutations and custom transforms or
|
|
105
|
-
serializers always bypass caching. `cacheTTL
|
|
106
|
-
cache entirely, while enabled caches
|
|
108
|
+
serializers always bypass caching. `cacheTTL` is measured in milliseconds;
|
|
109
|
+
`cacheTTL: 0` (the default) disables the cache entirely, while enabled caches
|
|
110
|
+
retain at most 100 entries by default.
|
|
107
111
|
`clearCache()` drops every cached entry; `disposeCache()`
|
|
108
112
|
drops entries and releases cache timers (call on adapter teardown so timers
|
|
109
113
|
do not keep a Node process alive).
|
|
@@ -139,6 +143,15 @@ consumer needs:
|
|
|
139
143
|
- **Model create cardinality:** `create(...)` and `createAdvanced(...)` accept
|
|
140
144
|
either one object or an array. Scalar input returns `ModelResponse<T>`;
|
|
141
145
|
array input returns `ArrayModelResponse<T>`, including for a one-item array.
|
|
146
|
+
- **Mutation input types:** model create/update/upsert payloads default to
|
|
147
|
+
`ModelMutationInput<T>` (`Partial<T>`), so object literals are checked for
|
|
148
|
+
known field names and scalar types without pretending the server can infer
|
|
149
|
+
required create fields. Pass `createModelService<T, TCreateInput,
|
|
150
|
+
TUpdateInput, TUpsertInput>(...)` when request schemas differ from the
|
|
151
|
+
response model. Subdocument create/update helpers use
|
|
152
|
+
`SubDocumentMutationInput<S>` (`Partial<S>` for object subdocuments) by
|
|
153
|
+
default and can be customized through `subs<S, K, TCreateInput,
|
|
154
|
+
TUpdateInput>(...)`.
|
|
142
155
|
- **Nested model edits:** `Model<T>` tracks modified top-level paths and
|
|
143
156
|
reconciles writes against the last loaded/saved snapshot. Direct mutation
|
|
144
157
|
of nested objects/arrays (`obj.arr.push(...)`, `obj.sub.field = x`) is
|
|
@@ -146,12 +159,18 @@ consumer needs:
|
|
|
146
159
|
or `markModified('topLevelField')` after a direct mutation (forces dirty
|
|
147
160
|
without reconciling). Reverting a value to its snapshot clears the dirty
|
|
148
161
|
flag. `save()` persists only tracked modified top-level fields; if `_id`
|
|
149
|
-
exists it calls `update(...)`, otherwise it calls `create(...)`.
|
|
162
|
+
exists it calls `update(...)`, otherwise it calls `create(...)`. Multiple
|
|
163
|
+
overlapping `save()` calls on the same wrapper are serialized in call order.
|
|
164
|
+
Document fields named like model methods (`save`, `reset`, `set`, `get`,
|
|
165
|
+
`assign`, `toJSON`, etc.) are reserved for the wrapper API on direct
|
|
166
|
+
property access; use `get(...)`, `set(...)`, `assign(...)`, or `toObject()`
|
|
167
|
+
for those data fields. The exported `ModelData<T>` helper reflects that
|
|
168
|
+
reserved-name contract in response and `Model.create(...)` types.
|
|
150
169
|
- **Supported runtimes:** Node 22+ and modern evergreen browsers (see
|
|
151
170
|
[Supported Runtimes](#supported-runtimes) and
|
|
152
171
|
[Browser And Node Support](#browser-and-node-support) above).
|
|
153
172
|
|
|
154
|
-
##
|
|
173
|
+
## Primary Exports
|
|
155
174
|
|
|
156
175
|
The package is named-export-only (no default export). Import every public
|
|
157
176
|
symbol from the package root:
|
|
@@ -174,9 +193,9 @@ import {
|
|
|
174
193
|
// Thrown instead of creating a duplicate when an existing projected model
|
|
175
194
|
// has no recoverable persistence identity.
|
|
176
195
|
MissingPersistenceIdentityError,
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
//
|
|
196
|
+
// Low-level lazy-promise wrapper with a single shared execution. Service
|
|
197
|
+
// methods add private adapter metadata required by `adapter.group(...)`;
|
|
198
|
+
// consumer-created wrappers execute directly and are not groupable.
|
|
180
199
|
wrapLazyPromise,
|
|
181
200
|
// Normalized response-count / pagination header names.
|
|
182
201
|
CustomHeaders,
|
|
@@ -202,6 +221,7 @@ import type {
|
|
|
202
221
|
ModelResponse,
|
|
203
222
|
ArrayModelResponse,
|
|
204
223
|
ListModelResponse,
|
|
224
|
+
ModelData,
|
|
205
225
|
DataResponse,
|
|
206
226
|
ArrayDataResponse,
|
|
207
227
|
ListDataResponse,
|
|
@@ -213,6 +233,8 @@ import type {
|
|
|
213
233
|
DataDefaults,
|
|
214
234
|
// Filter, projection, populate, sort, and request-meta primitives.
|
|
215
235
|
FilterQuery,
|
|
236
|
+
ModelMutationInput,
|
|
237
|
+
SubDocumentMutationInput,
|
|
216
238
|
DottedPathFilter,
|
|
217
239
|
ServerSideCast,
|
|
218
240
|
Projection,
|
|
@@ -247,6 +269,7 @@ type StablePublicTypes = [
|
|
|
247
269
|
ModelResponse<Document>,
|
|
248
270
|
ArrayModelResponse<Document>,
|
|
249
271
|
ListModelResponse<Document>,
|
|
272
|
+
ModelData<Document>,
|
|
250
273
|
DataResponse<unknown>,
|
|
251
274
|
ArrayDataResponse<unknown>,
|
|
252
275
|
ListDataResponse<unknown>,
|
|
@@ -255,6 +278,8 @@ type StablePublicTypes = [
|
|
|
255
278
|
Defaults,
|
|
256
279
|
DataDefaults,
|
|
257
280
|
FilterQuery<Document>,
|
|
281
|
+
ModelMutationInput<Document>,
|
|
282
|
+
SubDocumentMutationInput<{ label: string }>,
|
|
258
283
|
DottedPathFilter<Document>,
|
|
259
284
|
ServerSideCast<Document>,
|
|
260
285
|
Projection,
|
|
@@ -265,9 +290,9 @@ type StablePublicTypes = [
|
|
|
265
290
|
void (null as unknown as StablePublicTypes);
|
|
266
291
|
```
|
|
267
292
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
293
|
+
The names above are the primary public imports most consumers need. The full
|
|
294
|
+
root export inventory is locked by `access-router-client.exports.unit.test.ts`
|
|
295
|
+
and mirrored in `llms.txt`, so implementation internals
|
|
271
296
|
such as `useCacheInterceptors`, `cloneConfigWithCacheBypass`,
|
|
272
297
|
`finalizeRootEntry`, `applyGroupCallbacks`, `makeRequest`, `createWrapHelper`,
|
|
273
298
|
`ADAPTER_ID_KEY`, `STARTED_KEY`, `CACHE_HEADER`, `CachePolicy`, and `RootEntry`
|
|
@@ -278,29 +303,27 @@ through the returned adapter's `clearCache()` and `disposeCache()` methods.
|
|
|
278
303
|
## Browser And Node Support
|
|
279
304
|
|
|
280
305
|
- **Bundle target:** `es2022` (see `tsup.config.ts`). The single shared target
|
|
281
|
-
runs in Node 22+ and
|
|
282
|
-
source imports no Node built-ins.
|
|
306
|
+
runs in Node 22+ and the documented evergreen browser floor without
|
|
307
|
+
transpilation; the source imports no Node built-ins.
|
|
283
308
|
- **Runtime metadata:** `engines.node: ">=22"` (npm/pnpm warn or refuse on
|
|
284
|
-
older Node) and `browserslist: ["
|
|
285
|
-
|
|
286
|
-
engine warnings rather than appearing accidentally supported.
|
|
309
|
+
older Node) and `browserslist: ["chrome >= 94", "edge >= 94", "firefox >= 93", "safari >= 16"]`.
|
|
310
|
+
`pnpm exec browserslist` resolves this package config without error.
|
|
287
311
|
- **Authentication contract:** `withCredentials: true` is the adapter
|
|
288
|
-
default, so
|
|
289
|
-
|
|
290
|
-
`
|
|
291
|
-
|
|
292
|
-
`
|
|
293
|
-
|
|
294
|
-
cached response.
|
|
312
|
+
default, so browser requests may include cookies when CORS and cookie policy
|
|
313
|
+
allow them. `Authorization`, proxy authorization, API-key style headers, and
|
|
314
|
+
Node `Cookie` headers are explicit Axios config values; `withCredentials`
|
|
315
|
+
does not create them. Credentialed caching still requires an explicit
|
|
316
|
+
`cachePartition` token so one identity cannot receive another's cached
|
|
317
|
+
response.
|
|
295
318
|
- **Cache timers:** the in-memory cache uses `setTimeout`/`clearTimeout`
|
|
296
319
|
(available in both runtimes). The optional Node `unref()` guard is
|
|
297
320
|
feature-detected and is a no-op in browsers, so `clearCache()` and
|
|
298
321
|
`disposeCache()` are safe to call in either runtime.
|
|
299
322
|
- **Smoke test:** `pnpm --filter @web-ts-toolkit/access-router-client
|
|
300
323
|
test:browser-smoke` (powered by Vite + jsdom) imports the _built_
|
|
301
|
-
`dist/index.mjs` under a browser environment and exercises the public
|
|
302
|
-
runtime surface. It
|
|
303
|
-
|
|
324
|
+
`dist/index.mjs` under a browser-like environment and exercises the public
|
|
325
|
+
runtime surface. It is a smoke check for Node built-in leaks and basic ESM
|
|
326
|
+
browser bundling, not a real-browser engine/version compatibility gate. This
|
|
304
327
|
smoke test also runs as part of the default `pnpm test` for the package.
|
|
305
328
|
|
|
306
329
|
## Documentation
|
package/index.d.mts
CHANGED
|
@@ -207,7 +207,7 @@ declare class Service {
|
|
|
207
207
|
private _wrap;
|
|
208
208
|
private _throwOnError;
|
|
209
209
|
constructor(axios: AxiosInstance, basePath: string, throwOnError?: boolean);
|
|
210
|
-
protected handleSuccess(res: AxiosResponse<unknown, unknown>, extra?: {}):
|
|
210
|
+
protected handleSuccess<T extends Response<unknown, unknown> = Response<unknown>>(res: AxiosResponse<unknown, unknown>, extra?: {}): T;
|
|
211
211
|
protected handleError<T extends Response<unknown, unknown>>(error: unknown): Extract<T, FailureResult>;
|
|
212
212
|
/** Resolves per-call policy against the already-resolved service/adapter default. */
|
|
213
213
|
resolveThrowOnError(override?: boolean): boolean;
|
|
@@ -271,7 +271,8 @@ interface Props$1 {
|
|
|
271
271
|
* const userService = adapter.createModelService<User>({ modelName: 'User', basePath: 'users' });
|
|
272
272
|
* const user = await userService.read('user-id-1');
|
|
273
273
|
*/
|
|
274
|
-
|
|
274
|
+
type InferredSubDocument<T, K extends keyof T, S> = [S] extends [never] ? NonNullable<T[K]> extends readonly (infer TItem)[] ? TItem : never : S;
|
|
275
|
+
declare class ModelService<T extends Document, TCreateInput extends object = ModelMutationInput<T>, TUpdateInput extends object = ModelMutationInput<T>, TUpsertInput extends object = ModelMutationInput<T>> extends Service {
|
|
275
276
|
private _modelName;
|
|
276
277
|
private _queryPath;
|
|
277
278
|
private _mutationPath;
|
|
@@ -280,12 +281,12 @@ declare class ModelService<T extends Document> extends Service {
|
|
|
280
281
|
constructor({ axios, modelName, basePath, queryPath, mutationPath, onSuccess, onFailure, throwOnError }: Props$1, defaults?: Defaults);
|
|
281
282
|
list<TData extends Partial<T> = T>(args?: ListArgs, options?: ListOptions, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<ListModelResponse<T, TData>>;
|
|
282
283
|
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>>>;
|
|
283
|
-
create<TData extends Partial<T> = T>(data:
|
|
284
|
-
create<TData extends Partial<T> = T>(data:
|
|
285
|
-
createAdvanced<TData extends Partial<T> | never = never, TSelect extends Projection = Projection>(data:
|
|
286
|
-
createAdvanced<TData extends Partial<T> | never = never, TSelect extends Projection = Projection>(data:
|
|
287
|
-
upsert<TData extends Partial<T> = T>(data:
|
|
288
|
-
upsertAdvanced<TData extends Partial<T> | never = never, TSelect extends Projection = Projection>(data:
|
|
284
|
+
create<TData extends Partial<T> = T>(data: TCreateInput[], options?: CreateOptions, axiosRequestConfig?: RequestConfig$1): ModelRequest<ArrayModelResponse<T, TData>>;
|
|
285
|
+
create<TData extends Partial<T> = T>(data: TCreateInput, options?: CreateOptions, axiosRequestConfig?: RequestConfig$1): ModelRequest<ModelResponse<T, TData>>;
|
|
286
|
+
createAdvanced<TData extends Partial<T> | never = never, TSelect extends Projection = Projection>(data: TCreateInput[], args?: CreateAdvancedArgs<TSelect>, options?: CreateAdvancedOptions, axiosRequestConfig?: RequestConfig$1): ModelRequest<ArrayModelResponse<T, ResolvedSelectedShape<T, TSelect, TData>>>;
|
|
287
|
+
createAdvanced<TData extends Partial<T> | never = never, TSelect extends Projection = Projection>(data: TCreateInput, args?: CreateAdvancedArgs<TSelect>, options?: CreateAdvancedOptions, axiosRequestConfig?: RequestConfig$1): ModelRequest<ModelResponse<T, ResolvedSelectedShape<T, TSelect, TData>>>;
|
|
288
|
+
upsert<TData extends Partial<T> = T>(data: TUpsertInput, options?: UpsertOptions, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<ModelResponse<T, TData>>;
|
|
289
|
+
upsertAdvanced<TData extends Partial<T> | never = never, TSelect extends Projection = Projection>(data: TUpsertInput, args?: UpsertAdvancedArgs<TSelect>, options?: UpsertAdvancedOptions, axiosRequestConfig?: RequestConfig$1): ModelRequest<ModelResponse<T, ResolvedSelectedShape<T, TSelect, TData>>>;
|
|
289
290
|
delete(identifier: string, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<string>>;
|
|
290
291
|
new<TData extends Partial<T> = T>(axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<ModelResponse<T, TData>>;
|
|
291
292
|
distinct(field: string, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<string[]>>;
|
|
@@ -295,36 +296,36 @@ declare class ModelService<T extends Document> extends Service {
|
|
|
295
296
|
read<TData extends Partial<T> = T>(identifier: string, options?: ReadOptions, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<ModelResponse<T, TData>>;
|
|
296
297
|
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>>>;
|
|
297
298
|
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>>>;
|
|
298
|
-
update<TData extends Partial<T> = T>(identifier: string, data:
|
|
299
|
-
updateAdvanced<TData extends Partial<T> | never = never, TSelect extends Projection = Projection>(identifier: string, data:
|
|
299
|
+
update<TData extends Partial<T> = T>(identifier: string, data: TUpdateInput, options?: UpdateOptions, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<ModelResponse<T, TData>>;
|
|
300
|
+
updateAdvanced<TData extends Partial<T> | never = never, TSelect extends Projection = Projection>(identifier: string, data: TUpdateInput, args?: UpdateAdvancedArgs<TSelect>, options?: UpdateAdvancedOptions, axiosRequestConfig?: RequestConfig$1): ModelRequest<ModelResponse<T, ResolvedSelectedShape<T, TSelect, TData>>>;
|
|
300
301
|
id(id: string): {
|
|
301
|
-
subs: <S = never, K extends keyof T = keyof T
|
|
302
|
+
subs: <S = never, K extends keyof T = keyof T, TSubCreateInput = SubDocumentMutationInput<InferredSubDocument<T, K, S>>, TSubUpdateInput = SubDocumentMutationInput<InferredSubDocument<T, K, S>>>(field: K) => {
|
|
302
303
|
list: (axiosRequestConfig?: AxiosRequestConfig<any, any> & {
|
|
303
304
|
throwOnError?: boolean;
|
|
304
|
-
}) => ModelPromiseMeta & LazyRequest<SubDocumentListResponse<
|
|
305
|
-
listAdvanced: <TData extends Partial<
|
|
305
|
+
}) => ModelPromiseMeta & LazyRequest<SubDocumentListResponse<InferredSubDocument<T, K, S>>>;
|
|
306
|
+
listAdvanced: <TData extends Partial<InferredSubDocument<T, K, S>> = never, TSelect extends readonly string[] = readonly string[]>(filter?: FilterQuery<InferredSubDocument<T, K, S>>, args?: {
|
|
306
307
|
select?: TSelect;
|
|
307
308
|
}, axiosRequestConfig?: AxiosRequestConfig<any, any> & {
|
|
308
309
|
throwOnError?: boolean;
|
|
309
|
-
}) => ModelPromiseMeta & LazyRequest<SubDocumentListResponse<
|
|
310
|
+
}) => ModelPromiseMeta & LazyRequest<SubDocumentListResponse<InferredSubDocument<T, K, S>, ResolvedSelectedShape<InferredSubDocument<T, K, S>, TSelect, TData>>>;
|
|
310
311
|
read: (subId: string, axiosRequestConfig?: AxiosRequestConfig<any, any> & {
|
|
311
312
|
throwOnError?: boolean;
|
|
312
|
-
}) => ModelPromiseMeta & LazyRequest<SubDocumentResponse<
|
|
313
|
-
readAdvanced: <TData extends Partial<
|
|
313
|
+
}) => ModelPromiseMeta & LazyRequest<SubDocumentResponse<InferredSubDocument<T, K, S>>>;
|
|
314
|
+
readAdvanced: <TData extends Partial<InferredSubDocument<T, K, S>> = never, TSelect_1 extends readonly string[] = readonly string[]>(subId: string, args?: {
|
|
314
315
|
select?: TSelect_1;
|
|
315
316
|
populate?: unknown;
|
|
316
317
|
}, axiosRequestConfig?: AxiosRequestConfig<any, any> & {
|
|
317
318
|
throwOnError?: boolean;
|
|
318
|
-
}) => ModelPromiseMeta & LazyRequest<SubDocumentResponse<
|
|
319
|
-
update: (subId: string, data:
|
|
319
|
+
}) => ModelPromiseMeta & LazyRequest<SubDocumentResponse<InferredSubDocument<T, K, S>, ResolvedSelectedShape<InferredSubDocument<T, K, S>, TSelect_1, TData>>>;
|
|
320
|
+
update: (subId: string, data: TSubUpdateInput, axiosRequestConfig?: AxiosRequestConfig<any, any> & {
|
|
320
321
|
throwOnError?: boolean;
|
|
321
|
-
}) => ModelPromiseMeta & LazyRequest<SubDocumentResponse<
|
|
322
|
-
bulkUpdate: (data:
|
|
322
|
+
}) => ModelPromiseMeta & LazyRequest<SubDocumentResponse<InferredSubDocument<T, K, S>>>;
|
|
323
|
+
bulkUpdate: (data: TSubUpdateInput[], axiosRequestConfig?: AxiosRequestConfig<any, any> & {
|
|
323
324
|
throwOnError?: boolean;
|
|
324
|
-
}) => ModelPromiseMeta & LazyRequest<SubDocumentListResponse<
|
|
325
|
-
create: (data:
|
|
325
|
+
}) => ModelPromiseMeta & LazyRequest<SubDocumentListResponse<InferredSubDocument<T, K, S>>>;
|
|
326
|
+
create: (data: TSubCreateInput | TSubCreateInput[], axiosRequestConfig?: AxiosRequestConfig<any, any> & {
|
|
326
327
|
throwOnError?: boolean;
|
|
327
|
-
}) => ModelPromiseMeta & LazyRequest<SubDocumentListResponse<
|
|
328
|
+
}) => ModelPromiseMeta & LazyRequest<SubDocumentListResponse<InferredSubDocument<T, K, S>>>;
|
|
328
329
|
delete: (subId: string, axiosRequestConfig?: AxiosRequestConfig<any, any> & {
|
|
329
330
|
throwOnError?: boolean;
|
|
330
331
|
}) => ModelPromiseMeta & LazyRequest<Response<string>>;
|
|
@@ -380,16 +381,19 @@ declare class MissingPersistenceIdentityError extends Error {
|
|
|
380
381
|
/**
|
|
381
382
|
* A dirty-tracking wrapper around a model document. Constructed via
|
|
382
383
|
* {@link ModelService.create}, {@link ModelService.read},
|
|
383
|
-
* {@link ModelService.
|
|
384
|
+
* {@link ModelService.readAdvanced}, or the list methods that return
|
|
384
385
|
* `Model<T>[]`. Property access through the wrapper directly reads/writes
|
|
385
386
|
* the underlying data; `save()` persists only the paths flagged dirty
|
|
386
387
|
* since the last save and merges the server response per the documented
|
|
387
388
|
* concurrency contract.
|
|
388
389
|
*
|
|
389
|
-
* `Model.create<T>(data, service)` is typed as `Model<T, TData> &
|
|
390
|
-
* callers can read/write fields directly on
|
|
391
|
-
* 'owner'`) while still calling
|
|
392
|
-
*
|
|
390
|
+
* `Model.create<T>(data, service)` is typed as `Model<T, TData> &
|
|
391
|
+
* ModelData<T, TData>` so callers can read/write ordinary fields directly on
|
|
392
|
+
* the wrapper (`user.role = 'owner'`) while still calling
|
|
393
|
+
* `save()`/`reset()`/`isDirty(...)`. Fields whose names collide with public
|
|
394
|
+
* model methods or properties are reserved for the wrapper API and remain
|
|
395
|
+
* reachable through `get(...)`, `set(...)`, `assign(...)`, and `toObject()`.
|
|
396
|
+
* The returned wrapper is a fresh snapshot of the post-operation local state;
|
|
393
397
|
* mutating it does not affect sibling wrappers created from the same
|
|
394
398
|
* underlying document.
|
|
395
399
|
*
|
|
@@ -411,32 +415,37 @@ declare class Model<T extends Document, TData extends Partial<T> = T> {
|
|
|
411
415
|
private _snapshot;
|
|
412
416
|
private readonly _service;
|
|
413
417
|
private modifiedPaths;
|
|
418
|
+
private _saveQueue;
|
|
414
419
|
private _persistenceId;
|
|
415
420
|
private readonly _fromExisting;
|
|
416
421
|
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
|
|
422
|
+
static create<T extends Document, TData extends Partial<T> = T>(data: TData, adapter: ModelService<T>, persistenceId?: string, fromExisting?: boolean): Model<T, TData> & ModelData<T, TData>;
|
|
418
423
|
/**
|
|
419
424
|
* Persists the currently dirty paths to the server, then merges the
|
|
420
425
|
* server's response back into local state.
|
|
421
426
|
*
|
|
422
427
|
* Concurrency contract:
|
|
423
428
|
*
|
|
424
|
-
* 1.
|
|
429
|
+
* 1. Multiple `save()` calls on the same wrapper are serialized in call
|
|
430
|
+
* order. A later save snapshots its dirty paths only after the previous
|
|
431
|
+
* save has finished reconciling, so overlapping callers cannot submit
|
|
432
|
+
* the same stale dirty set concurrently.
|
|
433
|
+
* 2. Submitted paths and their values are snapshotted before the request
|
|
425
434
|
* starts, so an in-flight response cannot wipe edits that were made
|
|
426
435
|
* while the request was pending.
|
|
427
|
-
*
|
|
436
|
+
* 3. On success, a submitted path is cleared from `modifiedPaths` only if
|
|
428
437
|
* its current local value still equals the submitted value — i.e. the
|
|
429
438
|
* user has not concurrently re-edited it to a different value.
|
|
430
|
-
*
|
|
439
|
+
* 4. Server-returned values overwrite local values for paths the user did
|
|
431
440
|
* NOT concurrently re-modify during the in-flight save; for paths the
|
|
432
441
|
* user did concurrently re-modify, the local value is preserved and
|
|
433
442
|
* the dirty flag is retained so the concurrent edit is resubmitted on
|
|
434
443
|
* the next `save()`. (Deterministic conflict rule: the newer local
|
|
435
444
|
* edit wins for the same path; the server value becomes its reset
|
|
436
445
|
* baseline without replacing the newer local value.)
|
|
437
|
-
*
|
|
446
|
+
* 5. On failure, no dirty state is cleared and no local value is
|
|
438
447
|
* overwritten; the caller can retry `save()` with the same set.
|
|
439
|
-
*
|
|
448
|
+
* 6. The return value echoes `{ ...result, data }` where `data` is a
|
|
440
449
|
* refreshed `Model` snapshot of the post-save local state (or `null`
|
|
441
450
|
* on failure), matching `ModelResponse<T, TData>`.
|
|
442
451
|
*
|
|
@@ -452,6 +461,7 @@ declare class Model<T extends Document, TData extends Partial<T> = T> {
|
|
|
452
461
|
* of POSTing a new document.
|
|
453
462
|
*/
|
|
454
463
|
save(reqConfig?: AxiosRequestConfig): Promise<ModelResponse<T, TData>>;
|
|
464
|
+
private saveNow;
|
|
455
465
|
isDirty(path?: keyof TData | string): boolean;
|
|
456
466
|
/**
|
|
457
467
|
* Marks a path dirty and skips snapshot reconciliation. This is the
|
|
@@ -495,6 +505,16 @@ declare class Model<T extends Document, TData extends Partial<T> = T> {
|
|
|
495
505
|
*/
|
|
496
506
|
private reconcilePath;
|
|
497
507
|
}
|
|
508
|
+
/**
|
|
509
|
+
* Directly exposed data fields for a `Model` wrapper.
|
|
510
|
+
*
|
|
511
|
+
* Runtime property forwarding reserves public `Model` member names such as
|
|
512
|
+
* `save`, `reset`, `set`, `get`, `assign`, `toObject`, and `toJSON` for the
|
|
513
|
+
* wrapper API. Documents may still contain those field names, but callers must
|
|
514
|
+
* access them via `get(...)`, `set(...)`, `assign(...)`, or `toObject()` rather
|
|
515
|
+
* than ordinary direct property access.
|
|
516
|
+
*/
|
|
517
|
+
type ModelData<T extends Document, TData extends Partial<T> = T> = Omit<TData, keyof Model<T, TData>>;
|
|
498
518
|
|
|
499
519
|
type AnyArray<T> = T[] | ReadonlyArray<T>;
|
|
500
520
|
type Unpacked<T> = T extends (infer U)[] ? U : T extends ReadonlyArray<infer U> ? U : T;
|
|
@@ -512,7 +532,8 @@ type Unpacked<T> = T extends (infer U)[] ? U : T extends ReadonlyArray<infer U>
|
|
|
512
532
|
* (dynamic dotted paths and explicit server-side casting).
|
|
513
533
|
*/
|
|
514
534
|
type ApplyBasicQueryCasting<T> = T | T[] | (T extends AnyArray<unknown> ? Unpacked<T> : never) | (T extends string ? RegExp : never);
|
|
515
|
-
type
|
|
535
|
+
type QueryOperatorOperand<T> = T extends AnyArray<unknown> ? Unpacked<T> : T;
|
|
536
|
+
type Condition<T> = ApplyBasicQueryCasting<T> | QuerySelector<T> | LazyRequest<unknown>;
|
|
516
537
|
type _FilterQuery<T> = {
|
|
517
538
|
[P in keyof T]?: Condition<T[P]>;
|
|
518
539
|
} & RootQuerySelector<T>;
|
|
@@ -536,15 +557,15 @@ type RootQuerySelector<T> = {
|
|
|
536
557
|
$comment?: string;
|
|
537
558
|
};
|
|
538
559
|
type QuerySelector<T> = {
|
|
539
|
-
$eq?: T
|
|
540
|
-
$gt?: T
|
|
541
|
-
$gte?: T
|
|
542
|
-
$in?:
|
|
543
|
-
$lt?: T
|
|
544
|
-
$lte?: T
|
|
545
|
-
$ne?: T
|
|
546
|
-
$nin?:
|
|
547
|
-
$not?: T extends string ? QuerySelector<T> | RegExp : QuerySelector<T>;
|
|
560
|
+
$eq?: ApplyBasicQueryCasting<T>;
|
|
561
|
+
$gt?: QueryOperatorOperand<T>;
|
|
562
|
+
$gte?: QueryOperatorOperand<T>;
|
|
563
|
+
$in?: QueryOperatorOperand<T>[];
|
|
564
|
+
$lt?: QueryOperatorOperand<T>;
|
|
565
|
+
$lte?: QueryOperatorOperand<T>;
|
|
566
|
+
$ne?: ApplyBasicQueryCasting<T>;
|
|
567
|
+
$nin?: QueryOperatorOperand<T>[];
|
|
568
|
+
$not?: QueryOperatorOperand<T> extends string ? QuerySelector<T> | RegExp : QuerySelector<T>;
|
|
548
569
|
/**
|
|
549
570
|
* When `true`, `$exists` matches the documents that contain the field,
|
|
550
571
|
* including documents where the field value is null.
|
|
@@ -553,9 +574,9 @@ type QuerySelector<T> = {
|
|
|
553
574
|
$type?: string | number;
|
|
554
575
|
$expr?: unknown;
|
|
555
576
|
$jsonSchema?: unknown;
|
|
556
|
-
$mod?: T extends number ? [number, number] : never;
|
|
557
|
-
$regex?: T extends string ? RegExp | string : never;
|
|
558
|
-
$options?: T extends string ? string : never;
|
|
577
|
+
$mod?: QueryOperatorOperand<T> extends number ? [number, number] : never;
|
|
578
|
+
$regex?: QueryOperatorOperand<T> extends string ? RegExp | string : never;
|
|
579
|
+
$options?: QueryOperatorOperand<T> extends string ? string : never;
|
|
559
580
|
};
|
|
560
581
|
/**
|
|
561
582
|
* Escape hatch for dynamic dotted paths and explicit server-side casting.
|
|
@@ -648,6 +669,19 @@ interface Populate {
|
|
|
648
669
|
interface Document {
|
|
649
670
|
_id?: string;
|
|
650
671
|
}
|
|
672
|
+
/**
|
|
673
|
+
* Default request payload type for model mutations.
|
|
674
|
+
*
|
|
675
|
+
* The sibling access-router runtime accepts generic records and does not know
|
|
676
|
+
* a consumer application's required create/update schema. The client therefore
|
|
677
|
+
* defaults mutation inputs to `Partial<T>` so known fields are checked without
|
|
678
|
+
* claiming compile-time requiredness. Consumers with distinct request schemas
|
|
679
|
+
* can pass explicit `ModelService<T, TCreateInput, TUpdateInput, TUpsertInput>`
|
|
680
|
+
* or `createModelService<T, ...>(...)` generics.
|
|
681
|
+
*/
|
|
682
|
+
type ModelMutationInput<T extends Document> = Partial<T>;
|
|
683
|
+
/** Default request payload type for subdocument create/update helpers. */
|
|
684
|
+
type SubDocumentMutationInput<T> = T extends object ? Partial<T> : T;
|
|
651
685
|
/**
|
|
652
686
|
* Successful response. `raw` and `data` are non-null and `success` is
|
|
653
687
|
* narrowed to `true` so `if (result.success)` exposes the documented
|
|
@@ -689,8 +723,8 @@ interface FailureResult<TError = unknown> {
|
|
|
689
723
|
* type unless a caller explicitly chooses that error type.
|
|
690
724
|
*/
|
|
691
725
|
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)[]>;
|
|
726
|
+
type ModelResponse<T extends Document, TData extends Partial<T> = T> = Response<TData, Model<T, TData> & ModelData<T, TData>>;
|
|
727
|
+
type ArrayModelResponse<T extends Document, TData extends Partial<T> = T> = Response<TData[], (Model<T, TData> & ModelData<T, TData>)[]>;
|
|
694
728
|
/**
|
|
695
729
|
* `ListModelResponse` always carries `totalCount` on both branches. The field
|
|
696
730
|
* defaults to `0` at runtime on failure or when the server did not emit count
|
|
@@ -828,11 +862,12 @@ type CachePartitioner = (config: InternalAxiosRequestConfig) => string | undefin
|
|
|
828
862
|
*
|
|
829
863
|
* Cache controls (only in effect when `cacheTTL > 0`):
|
|
830
864
|
*
|
|
831
|
-
* - `cacheTTL` —
|
|
865
|
+
* - `cacheTTL` — milliseconds a cached GET response is reused before revalidation.
|
|
832
866
|
* - `cachePartition` — required to cache credentialed requests safely (see
|
|
833
|
-
* {@link CachePartitioner});
|
|
834
|
-
*
|
|
835
|
-
*
|
|
867
|
+
* {@link CachePartitioner}); requests using browser cookies,
|
|
868
|
+
* `withCredentials`, or explicit auth headers without a stable, non-secret
|
|
869
|
+
* partition token bypass the cache so one identity cannot receive a
|
|
870
|
+
* response created under another.
|
|
836
871
|
* - `cacheCapacity` — bounds the number of cached entries; defaults to 100 and
|
|
837
872
|
* evicts the LRU entry when the limit is exceeded.
|
|
838
873
|
*
|
|
@@ -847,11 +882,11 @@ interface AdapterOptions {
|
|
|
847
882
|
throwOnError?: boolean;
|
|
848
883
|
cacheTTL?: number;
|
|
849
884
|
/**
|
|
850
|
-
* Partition strategy for credentialed cache entries. When
|
|
851
|
-
*
|
|
852
|
-
*
|
|
853
|
-
* without a partition key bypass the cache so that one
|
|
854
|
-
* receive a response created under another identity.
|
|
885
|
+
* Partition strategy for credentialed cache entries. When a request uses
|
|
886
|
+
* browser cookies, `withCredentials`, or explicit auth headers, caching is
|
|
887
|
+
* only enabled when `cachePartition` returns a stable, non-secret identity
|
|
888
|
+
* token. Requests without a partition key bypass the cache so that one
|
|
889
|
+
* identity can never receive a response created under another identity.
|
|
855
890
|
*
|
|
856
891
|
* The returned value must be a stable, non-secret token (for example a user
|
|
857
892
|
* id or tenant id). Never return raw cookies, authorization values, or other
|
|
@@ -935,7 +970,7 @@ declare function createAdapter(axiosConfig?: AxiosRequestConfig, adapterOptions?
|
|
|
935
970
|
axios: axios.AxiosInstance;
|
|
936
971
|
clearCache: () => void;
|
|
937
972
|
disposeCache: () => void;
|
|
938
|
-
createModelService: <T extends Document
|
|
973
|
+
createModelService: <T extends Document, TCreateInput extends object = Partial<T>, TUpdateInput extends object = Partial<T>, TUpsertInput extends object = Partial<T>>({ modelName, basePath, queryPath, mutationPath, onSuccess, onFailure, throwOnError, }: ModelServiceOptions, defaults?: Defaults) => ModelService<T, TCreateInput, TUpdateInput, TUpsertInput>;
|
|
939
974
|
createDataService: <T>({ dataName, basePath, queryPath, onSuccess, onFailure, throwOnError }: DataServiceOptions, defaults?: DataDefaults) => DataService<T>;
|
|
940
975
|
wrapGet: <T = unknown>(url: string, defaultConfig?: AxiosRequestConfig) => (options?: WrapOptions, requestConfig?: AxiosRequestConfig) => Promise<axios.AxiosResponse<T, any, {}, any>>;
|
|
941
976
|
wrapPost: <T = unknown>(url: string, defaultConfig?: AxiosRequestConfig) => (data?: unknown, options?: WrapOptions, requestConfig?: AxiosRequestConfig) => Promise<axios.AxiosResponse<T, any, {}, any>>;
|
|
@@ -964,4 +999,4 @@ declare function removeItemById<T extends {
|
|
|
964
999
|
_id: string;
|
|
965
1000
|
}>(items: T[], targetItem: T): T[];
|
|
966
1001
|
|
|
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 };
|
|
1002
|
+
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 ModelData, type ModelMutationInput, 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 SubDocumentMutationInput, 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 };
|