@web-ts-toolkit/access-router-client 0.43.0 → 0.44.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 CHANGED
@@ -29,15 +29,17 @@ pnpm add @web-ts-toolkit/access-router-client
29
29
  This remediation release tightens several public runtime and TypeScript
30
30
  contracts. When upgrading from the previous client contract:
31
31
 
32
- | Area | Before | After / required migration |
33
- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
34
- | Subdocuments | Results could expose parent-backed `Model<S>` values and `save()`. | Results are plain objects/arrays. Persist with the parent-scoped `subs(...)` helper's `update`, `create`, `bulkUpdate`, or `delete` methods. |
35
- | Create and list counts | Subdocument create looked scalar and subdocument lists used `totalCount`; model create was scalar-only. | Subdocument create accepts one or many and always returns the post-create array with `count`. Model create preserves input cardinality: object -> `ModelResponse`, array -> `ArrayModelResponse`. Model/data lists retain `totalCount`. |
36
- | Responses | Failure `data` and success fields could not be narrowed reliably. | Branch on `result.success`. Success has non-null `raw`/`data`; failure has `data: null` and the problem payload in `raw`. Model/data `totalCount` defaults to `0` when metadata is unavailable; read subdocument `count` after success. |
37
- | Cache | An enabled cache could be unbounded and credentialed requests did not require an explicit identity partition. | Cache is still off by default (`cacheTTL: 0`). Enabled caches admit supported GETs only, default to a 100-entry LRU, and require `cachePartition` for credentialed requests. Clear on identity changes and dispose on teardown. |
38
- | Grouping | A lazy request could be replayed or moved between direct and grouped execution; batch error policy could drift by entry. | Each lazy request can be claimed once. Create a new request to execute again. All group members must share one effective `throwOnError` policy. Non-throwing batches return all entries; throwing batches run all callbacks and then reject with the first failure. |
39
- | Protocol types | Data permission options, object/tuple data sorts, and a count access argument were accepted. Filters were broadly permissive. | Remove `includePermissions` from data calls, use string data sorts, call `countAdvanced(filter, config?)`, and fix invalid `FilterQuery<T>` values. Use `DottedPathFilter<T>` or `ServerSideCast<T>` only as explicit escape hatches. |
40
- | Paths, config, and model persistence | Dynamic path values were interpolated directly, inputs could be mutated, and projected models could lose persistence identity. | Pass raw path values for one-pass encoding; caller configs stay immutable. ID-based projected reads retain identity, while an existing model with no recoverable identity throws `MissingPersistenceIdentityError`. Use `set()`/`markModified()` for nested edits. |
32
+ | Area | Before | After / required migration |
33
+ | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
34
+ | Subdocuments | Results could expose parent-backed `Model<S>` values and `save()`. | Results are plain objects/arrays. Persist with the parent-scoped `subs(...)` helper's `update`, `create`, `bulkUpdate`, or `delete` methods. |
35
+ | Create and list counts | Subdocument create looked scalar and subdocument lists used `totalCount`; model create was scalar-only. | Subdocument create accepts one or many and always returns the post-create array with `count`. Model create preserves input cardinality: object -> `ModelResponse`, array -> `ArrayModelResponse`. Model/data lists retain `totalCount`. |
36
+ | Responses | Failure `data` and success fields could not be narrowed reliably. | Branch on `result.success`. Success has non-null `raw`/`data`; failure has `data: null` and the problem payload in `raw`. Model/data `totalCount` defaults to `0` when metadata is unavailable; read subdocument `count` after success. |
37
+ | Cache | An enabled cache could be unbounded and credentialed requests did not require an explicit identity partition. | Cache is still off by default (`cacheTTL: 0`). Enabled caches admit supported GETs only, default to a 100-entry LRU, and require `cachePartition` for credentialed requests. Clear on identity changes and dispose on teardown. |
38
+ | Grouping | A lazy request could be replayed or moved between direct and grouped execution; batch error policy could drift by entry. | Each lazy request can be claimed once. Create a new request to execute again. All group members must share one effective `throwOnError` policy. Non-throwing batches return all entries; throwing batches run all callbacks and then reject with the first failure. |
39
+ | Protocol types | Data permission options, object/tuple data sorts, and a count access argument were accepted. Filters were broadly permissive. | Remove `includePermissions` from data calls, use string data sorts, call `countAdvanced(filter, config?)`, and fix invalid `FilterQuery<T>` values. Use `DottedPathFilter<T>` or `ServerSideCast<T>` only as explicit escape hatches. |
40
+ | Paths, config, and model persistence | Dynamic path values were interpolated directly, inputs could be mutated, and projected models could lose persistence identity. | Pass raw path values for one-pass encoding; caller configs stay immutable. ID-based projected reads retain identity, while an existing model with no recoverable identity throws `MissingPersistenceIdentityError`. Use `set()`/`markModified()` for nested edits (dot, bracket-index, and quoted-key forms share one dirty root). |
41
+ | Service defaults and subqueries | Nested `Date` defaults could serialize as `{}` and accepted `sq` defaults were ignored without per-call `sq`. | Service defaults accept null, string, boolean, finite numbers, valid `Date` values (detached per request), plain objects, and arrays; functions, symbols, bigints, non-finite numbers, invalid `Date` values, non-plain instances, and cycles throw `UnsupportedServiceDefaultValueError`. Adapter/service/per-call `sq` precedence applies on `list`, `listAdvanced`, `read`, `readAdvanced`, and `readAdvancedFilter` (per-call wins). |
42
+ | Distinct result types | `distinct`/`distinctAdvanced` were typed `Response<string[]>`, so string methods compiled on numeric/boolean server values. | Both variants return `Response<unknown[]>` with no server-value stringification. Narrow elements (for example `typeof v === 'string'`) before calling string methods. |
41
43
 
42
44
  Grouped entry `headers` are now `{}` because the root protocol has no
43
45
  per-operation headers. Structured grouped failure fields remain in `raw`.
@@ -101,8 +103,9 @@ consumer needs:
101
103
  Browser cookie credentials, explicit `Authorization`/proxy authorization
102
104
  headers, API-key style headers, and Node `Cookie` headers supplied on the
103
105
  request config are all treated as credentialed.
104
- Sensitive headers (`authorization`, `cookie`, `set-cookie`,
105
- `proxy-authorization`, `www-authenticate`) are excluded from cache keys
106
+ Sensitive headers (`authorization`, `cookie`, `proxy-authorization`,
107
+ `x-api-key`, `x-auth-token`, `x-access-token`, `set-cookie`,
108
+ `www-authenticate`) are excluded from cache keys
106
109
  regardless of the partition token. Only GET requests with supported JSON or
107
110
  text response semantics are cached; mutations and custom transforms or
108
111
  serializers always bypass caching. `cacheTTL` is measured in milliseconds;
package/index.d.mts CHANGED
@@ -289,8 +289,21 @@ declare class ModelService<T extends Document, TCreateInput extends object = Mod
289
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>>>;
290
290
  delete(identifier: string, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<string>>;
291
291
  new<TData extends Partial<T> = T>(axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<ModelResponse<T, TData>>;
292
- distinct(field: string, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<string[]>>;
293
- distinctAdvanced(field: string, conditions: FilterQuery<T>, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<string[]>>;
292
+ /**
293
+ * BND-11: distinct values are `unknown[]`, not `string[]`. The sibling
294
+ * server returns raw distinct values without string conversion, so numeric
295
+ * and boolean values arrive as-is. Source-compat: callers that assumed
296
+ * `string[]` must narrow first (e.g. `typeof v === 'string'` or a type
297
+ * guard) before calling string methods; see the BND-11 task record for
298
+ * migration. No server values are stringified to satisfy the old type.
299
+ */
300
+ distinct(field: string, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<unknown[]>>;
301
+ /**
302
+ * BND-11: filtered distinct variant. Same `unknown[]` contract as
303
+ * {@link distinct}: narrow elements before assuming strings. No
304
+ * stringification is applied; dynamic field names are accepted as `string`.
305
+ */
306
+ distinctAdvanced(field: string, conditions: FilterQuery<T>, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<unknown[]>>;
294
307
  count(axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<number>>;
295
308
  countAdvanced(filter: FilterQuery<T>, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<number>>;
296
309
  read<TData extends Partial<T> = T>(identifier: string, options?: ReadOptions, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<ModelResponse<T, TData>>;
@@ -494,15 +507,11 @@ declare class Model<T extends Document, TData extends Partial<T> = T> {
494
507
  private trackModified;
495
508
  private normalizePath;
496
509
  /**
497
- * Removes `path` from the dirty set when its current top-level value deeply
498
- * equals the snapshot baseline. Used uniformly by `set()`, `assign()`,
499
- * public property setters (via the proxy), and `markModified()` so all
500
- * entry points share the same tracking rule.
501
- *
502
- * Note: `_id` is intentionally never reconciled away here — it is excluded
503
- * from `initializeDirtyState` and managed explicitly during `save()`
504
- * reconciliation.
510
+ * Removes `path` from the dirty set when its current value deeply equals
511
+ * the snapshot baseline. Invariant: unsaved drafts never reconcile clean
512
+ * (snapshot is unpersisted); `_id` is never reconciled here.
505
513
  */
514
+ private isUnsavedDraft;
506
515
  private reconcilePath;
507
516
  }
508
517
  /**
package/index.d.ts CHANGED
@@ -289,8 +289,21 @@ declare class ModelService<T extends Document, TCreateInput extends object = Mod
289
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>>>;
290
290
  delete(identifier: string, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<string>>;
291
291
  new<TData extends Partial<T> = T>(axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<ModelResponse<T, TData>>;
292
- distinct(field: string, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<string[]>>;
293
- distinctAdvanced(field: string, conditions: FilterQuery<T>, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<string[]>>;
292
+ /**
293
+ * BND-11: distinct values are `unknown[]`, not `string[]`. The sibling
294
+ * server returns raw distinct values without string conversion, so numeric
295
+ * and boolean values arrive as-is. Source-compat: callers that assumed
296
+ * `string[]` must narrow first (e.g. `typeof v === 'string'` or a type
297
+ * guard) before calling string methods; see the BND-11 task record for
298
+ * migration. No server values are stringified to satisfy the old type.
299
+ */
300
+ distinct(field: string, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<unknown[]>>;
301
+ /**
302
+ * BND-11: filtered distinct variant. Same `unknown[]` contract as
303
+ * {@link distinct}: narrow elements before assuming strings. No
304
+ * stringification is applied; dynamic field names are accepted as `string`.
305
+ */
306
+ distinctAdvanced(field: string, conditions: FilterQuery<T>, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<unknown[]>>;
294
307
  count(axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<number>>;
295
308
  countAdvanced(filter: FilterQuery<T>, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<Response<number>>;
296
309
  read<TData extends Partial<T> = T>(identifier: string, options?: ReadOptions, axiosRequestConfig?: RequestConfig$1): ModelPromiseMeta & LazyRequest<ModelResponse<T, TData>>;
@@ -494,15 +507,11 @@ declare class Model<T extends Document, TData extends Partial<T> = T> {
494
507
  private trackModified;
495
508
  private normalizePath;
496
509
  /**
497
- * Removes `path` from the dirty set when its current top-level value deeply
498
- * equals the snapshot baseline. Used uniformly by `set()`, `assign()`,
499
- * public property setters (via the proxy), and `markModified()` so all
500
- * entry points share the same tracking rule.
501
- *
502
- * Note: `_id` is intentionally never reconciled away here — it is excluded
503
- * from `initializeDirtyState` and managed explicitly during `save()`
504
- * reconciliation.
510
+ * Removes `path` from the dirty set when its current value deeply equals
511
+ * the snapshot baseline. Invariant: unsaved drafts never reconcile clean
512
+ * (snapshot is unpersisted); `_id` is never reconciled here.
505
513
  */
514
+ private isUnsavedDraft;
506
515
  private reconcilePath;
507
516
  }
508
517
  /**