@web-ts-toolkit/access-router-client 0.32.0 → 0.33.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.
Files changed (7) hide show
  1. package/README.md +258 -14
  2. package/index.d.mts +453 -85
  3. package/index.d.ts +453 -85
  4. package/index.js +1133 -368
  5. package/index.mjs +1133 -368
  6. package/llms.txt +39 -5
  7. package/package.json +11 -4
package/README.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  Typed client utilities for `@web-ts-toolkit/access-router` APIs.
4
4
 
5
+ ## Supported Runtimes
6
+
7
+ The package ships at an `es2022` bundle target and is officially supported in
8
+ **Node 22+** (declared via `engines.node`) and **modern evergreen browsers**
9
+ (Chrome 94+, Edge 94+, Firefox 93+, Safari 16+, declared via `browserslist`).
10
+ See [Browser And Node Support](#browser-and-node-support) below for the
11
+ authentication contract, what Node-only and browser-only paths can and
12
+ cannot do, and the smoke-test coverage that catches Node built-in leaks.
13
+
5
14
  ## Installation
6
15
 
7
16
  ```sh
@@ -15,6 +24,26 @@ pnpm add @web-ts-toolkit/access-router-client
15
24
  - `Model<T>` wrappers with dirty tracking and `save()`
16
25
  - normalized response and error handling around Axios
17
26
 
27
+ ## Unreleased Migration
28
+
29
+ This remediation release tightens several public runtime and TypeScript
30
+ contracts. When upgrading from the previous client contract:
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. |
41
+
42
+ Grouped entry `headers` are now `{}` because the root protocol has no
43
+ per-operation headers. Structured grouped failure fields remain in `raw`.
44
+ The complete release-level before/after record is in the repository
45
+ `CHANGELOG.md`.
46
+
18
47
  ## Quick Start
19
48
 
20
49
  ```ts
@@ -43,8 +72,10 @@ const listResponse = await userService.listAdvanced(
43
72
 
44
73
  const user = await userService.read('user-id-1');
45
74
 
46
- user.data.role = 'owner';
47
- await user.data.save();
75
+ if (user.success) {
76
+ user.data.role = 'owner';
77
+ await user.data.save();
78
+ }
48
79
 
49
80
  const grouped = await adapter.group(
50
81
  userService.readAdvanced('user-id-1', { select: ['name'] }),
@@ -52,21 +83,234 @@ const grouped = await adapter.group(
52
83
  );
53
84
  ```
54
85
 
86
+ ## Contract
87
+
88
+ The full website docs at
89
+ https://web-ts-toolkit.pages.dev/docs/packages/access-router-client describe
90
+ the same contract the installed package honors. The key points an installed
91
+ consumer needs:
92
+
93
+ - **Adapter defaults** (`createAdapter(axiosConfig?, adapterOptions?)`): the
94
+ adapter applies the following Axios defaults unless overridden by your
95
+ `axiosConfig`: `baseURL: '/api'`, `timeout: 0`, `withCredentials: true`, and
96
+ the response-busting headers `Cache-Control: no-cache`, `Pragma: no-cache`,
97
+ `Expires: 0`. Service `queryPath` defaults to `'__query'` and `mutationPath`
98
+ defaults to `'__mutation'`; `rootRouterPath` defaults to `'root'`.
99
+ - **Cache & authentication policy:** credentialed requests are **never**
100
+ cached unless `cachePartition` returns a stable, non-secret identity token.
101
+ Sensitive headers (`authorization`, `cookie`, `set-cookie`,
102
+ `proxy-authorization`, `www-authenticate`) are excluded from cache keys
103
+ regardless of the partition token. Only GET requests with supported JSON or
104
+ text response semantics are cached; mutations and custom transforms or
105
+ serializers always bypass caching. `cacheTTL: 0` (the default) disables the
106
+ cache entirely, while enabled caches retain at most 100 entries by default.
107
+ `clearCache()` drops every cached entry; `disposeCache()`
108
+ drops entries and releases cache timers (call on adapter teardown so timers
109
+ do not keep a Node process alive).
110
+ - **Direct vs grouped:** service methods return a lazy `LazyRequest<T>` that
111
+ does not execute until `await`, `.then()`, `.catch()`, `.finally()`, or
112
+ `.exec()`. `adapter.group(...)` batches multiple lazy requests into one
113
+ root-router round trip; it only accepts lazy requests from **this**
114
+ adapter's services, rejects already-started requests, and requires every
115
+ member to share the same `AxiosRequestConfig` and effective `throwOnError`
116
+ policy. Effective policy follows per-call, service, then adapter precedence;
117
+ mixed policies reject before dispatch. Non-throwing groups return every
118
+ normalized entry, including partial failures. Throwing groups run every
119
+ executed entry's callback exactly once, then reject with the first failed
120
+ entry's `ServiceError`. Once you `await` a lazy request it is no longer
121
+ batchable. Group results preserve input order. Group entry `headers` are
122
+ empty because the root protocol supplies only outer batch headers, not
123
+ per-operation headers.
124
+ - **Response narrowing:** `Response<TRaw, TData = TRaw, TError = unknown>` is a discriminated
125
+ union of `SuccessResult<TRaw, TData>` and `FailureResult<TError>`. Branch on
126
+ `result.success` — on the `true` branch both `raw` and `data` are non-null;
127
+ on the `false` branch `data` is always `null` (the server error payload
128
+ lives in `raw`, when one was received). Pass a third generic to opt into a
129
+ known error payload. List responses carry `totalCount`
130
+ on `ListModelResponse<T>`; subdocument list responses carry `count` (the
131
+ server's field) on `SubDocumentListResponse<S>`, never `totalCount`.
132
+ - **Subdocument shape:** `ModelService<T>.id(id).subs(sub)` helpers return
133
+ **plain data**, not `Model<S>` instances. `list(...)`, `listAdvanced(...)`,
134
+ `create(...)`, and `bulkUpdate(...)` return `SubDocumentListResponse<S>`.
135
+ `read(...)` and `readAdvanced(...)` return `SubDocumentResponse<S>`.
136
+ `create(...)` accepts a single object **or** an array and always returns the
137
+ post-create subdocument array. Persist a subdocument by calling the
138
+ parent-scoped helper explicitly — there is no subdocument `save()`.
139
+ - **Model create cardinality:** `create(...)` and `createAdvanced(...)` accept
140
+ either one object or an array. Scalar input returns `ModelResponse<T>`;
141
+ array input returns `ArrayModelResponse<T>`, including for a one-item array.
142
+ - **Nested model edits:** `Model<T>` tracks modified top-level paths and
143
+ reconciles writes against the last loaded/saved snapshot. Direct mutation
144
+ of nested objects/arrays (`obj.arr.push(...)`, `obj.sub.field = x`) is
145
+ **not** tracked. Use `set('path.to.field', value)` (applies + reconciles)
146
+ or `markModified('topLevelField')` after a direct mutation (forces dirty
147
+ without reconciling). Reverting a value to its snapshot clears the dirty
148
+ flag. `save()` persists only tracked modified top-level fields; if `_id`
149
+ exists it calls `update(...)`, otherwise it calls `create(...)`.
150
+ - **Supported runtimes:** Node 22+ and modern evergreen browsers (see
151
+ [Supported Runtimes](#supported-runtimes) and
152
+ [Browser And Node Support](#browser-and-node-support) above).
153
+
55
154
  ## Main Exports
56
155
 
57
- - `createAdapter(...)`
58
- - `ModelService`
59
- - `DataService`
60
- - `Model`
61
- - response and query helper types
156
+ The package is named-export-only (no default export). Import every public
157
+ symbol from the package root:
158
+
159
+ ```ts
160
+ import {
161
+ // Adapter factory — the primary entry point.
162
+ createAdapter,
163
+ // Service classes. `ModelService` and `DataService` are what
164
+ // `createAdapter(...)` constructs; `Service` is an advanced base class
165
+ // for callers that need a bespoke service shape.
166
+ ModelService,
167
+ DataService,
168
+ Service,
169
+ // Dirty-tracking model wrapper.
170
+ Model,
171
+ // Thrown when `throwOnError` is enabled and a request resolves to a
172
+ // `{ success: false }` result.
173
+ ServiceError,
174
+ // Thrown instead of creating a duplicate when an existing projected model
175
+ // has no recoverable persistence identity.
176
+ MissingPersistenceIdentityError,
177
+ // Lazy-promise wrapper with non-enumerable metadata and a single
178
+ // shared execution. Used internally by service methods; exported so
179
+ // consumers can build compatible lazy promises for custom batches.
180
+ wrapLazyPromise,
181
+ // Normalized response-count / pagination header names.
182
+ CustomHeaders,
183
+ // Generic list helpers used internally by model list methods; useful for
184
+ // callers that manipulate `Model<T>[]` directly.
185
+ replaceItemById,
186
+ removeItemById,
187
+ } from '@web-ts-toolkit/access-router-client';
188
+
189
+ import type {
190
+ // Adapter and per-factory option types.
191
+ AdapterOptions,
192
+ ModelServiceOptions,
193
+ DataServiceOptions,
194
+ // Cache policy types referenced by `AdapterOptions`.
195
+ CacheController,
196
+ CachePartitioner,
197
+ // Discriminated response union and success/failure members.
198
+ Response,
199
+ SuccessResult,
200
+ FailureResult,
201
+ // Model and data response aliases.
202
+ ModelResponse,
203
+ ArrayModelResponse,
204
+ ListModelResponse,
205
+ DataResponse,
206
+ ArrayDataResponse,
207
+ ListDataResponse,
208
+ SubDocumentResponse,
209
+ SubDocumentListResponse,
210
+ // Per-method args and options for both `ModelService<T>` and `DataService<T>`.
211
+ // (See the "TypeScript And Errors" doc page for the full list.)
212
+ Defaults,
213
+ DataDefaults,
214
+ // Filter, projection, populate, sort, and request-meta primitives.
215
+ FilterQuery,
216
+ DottedPathFilter,
217
+ ServerSideCast,
218
+ Projection,
219
+ Populate,
220
+ Sort,
221
+ Document,
222
+ } from '@web-ts-toolkit/access-router-client';
223
+
224
+ void [
225
+ createAdapter,
226
+ ModelService,
227
+ DataService,
228
+ Service,
229
+ Model,
230
+ ServiceError,
231
+ MissingPersistenceIdentityError,
232
+ wrapLazyPromise,
233
+ CustomHeaders,
234
+ replaceItemById,
235
+ removeItemById,
236
+ ];
237
+
238
+ type StablePublicTypes = [
239
+ AdapterOptions,
240
+ ModelServiceOptions,
241
+ DataServiceOptions,
242
+ CacheController,
243
+ CachePartitioner,
244
+ Response<unknown>,
245
+ SuccessResult<unknown>,
246
+ FailureResult,
247
+ ModelResponse<Document>,
248
+ ArrayModelResponse<Document>,
249
+ ListModelResponse<Document>,
250
+ DataResponse<unknown>,
251
+ ArrayDataResponse<unknown>,
252
+ ListDataResponse<unknown>,
253
+ SubDocumentResponse<unknown>,
254
+ SubDocumentListResponse<unknown>,
255
+ Defaults,
256
+ DataDefaults,
257
+ FilterQuery<Document>,
258
+ DottedPathFilter<Document>,
259
+ ServerSideCast<Document>,
260
+ Projection,
261
+ Populate,
262
+ Sort,
263
+ Document,
264
+ ];
265
+ void (null as unknown as StablePublicTypes);
266
+ ```
267
+
268
+ Only the names above are part of the stable public surface. The package
269
+ ships a runtime export contract test (`access-router-client.exports.unit.test.ts`)
270
+ that fails on accidental additions or removals, so implementation internals
271
+ such as `useCacheInterceptors`, `cloneConfigWithCacheBypass`,
272
+ `finalizeRootEntry`, `applyGroupCallbacks`, `makeRequest`, `createWrapHelper`,
273
+ `ADAPTER_ID_KEY`, `STARTED_KEY`, `CACHE_HEADER`, `CachePolicy`, and `RootEntry`
274
+ are intentionally not exported. Configure caching through `AdapterOptions`
275
+ (`cacheTTL`, `cachePartition`, `cacheCapacity`); control an existing cache
276
+ through the returned adapter's `clearCache()` and `disposeCache()` methods.
277
+
278
+ ## Browser And Node Support
279
+
280
+ - **Bundle target:** `es2022` (see `tsup.config.ts`). The single shared target
281
+ runs in Node 22+ and all evergreen browsers without transpilation; the
282
+ source imports no Node built-ins.
283
+ - **Runtime metadata:** `engines.node: ">=22"` (npm/pnpm warn or refuse on
284
+ older Node) and `browserslist: ["supports es2022-module"]` (bundler tools
285
+ narrow to the same matrix). Unsupported environments fail clearly via
286
+ engine warnings rather than appearing accidentally supported.
287
+ - **Authentication contract:** `withCredentials: true` is the adapter
288
+ default, so the browser runtime transmits cookies + the `Authorization`
289
+ header and the cache partitions credentialed requests via the
290
+ `cachePartition` option (see [Cache Controls](#cache-controls)). In Node,
291
+ `withCredentials` is honored by Axios's HTTP adapter the same way for
292
+ `Cookie` headers you set manually; credentialed caching still requires an
293
+ explicit `cachePartition` token so one identity cannot receive another's
294
+ cached response.
295
+ - **Cache timers:** the in-memory cache uses `setTimeout`/`clearTimeout`
296
+ (available in both runtimes). The optional Node `unref()` guard is
297
+ feature-detected and is a no-op in browsers, so `clearCache()` and
298
+ `disposeCache()` are safe to call in either runtime.
299
+ - **Smoke test:** `pnpm --filter @web-ts-toolkit/access-router-client
300
+ 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 fails if a Node built-in leaks into the bundle or the
303
+ bundle emits syntax the declared `browserslist` floor cannot run. This
304
+ smoke test also runs as part of the default `pnpm test` for the package.
62
305
 
63
306
  ## Documentation
64
307
 
65
- Full package documentation lives in `website/docs/packages/access-router-client/`.
308
+ Full package documentation lives online (the website sources are not packed
309
+ into the npm tarball, so the links below point to the published website rather
310
+ than repository-relative paths that would not resolve after install):
66
311
 
67
- - live docs: https://web-ts-toolkit.pages.dev/docs/packages/access-router-client
68
- - overview: `website/docs/packages/access-router-client/index.md`
69
- - adapter: `website/docs/packages/access-router-client/adapter.mdx`
70
- - services: `website/docs/packages/access-router-client/services.mdx`
71
- - model wrapper: `website/docs/packages/access-router-client/model.mdx`
72
- - typing and errors: `website/docs/packages/access-router-client/typescript-and-errors.mdx`
312
+ - overview: https://web-ts-toolkit.pages.dev/docs/packages/access-router-client
313
+ - adapter: https://web-ts-toolkit.pages.dev/docs/packages/access-router-client/adapter
314
+ - services: https://web-ts-toolkit.pages.dev/docs/packages/access-router-client/services
315
+ - model wrapper: https://web-ts-toolkit.pages.dev/docs/packages/access-router-client/model
316
+ - typing and errors: https://web-ts-toolkit.pages.dev/docs/packages/access-router-client/typescript-and-errors