@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.
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/index.js CHANGED
@@ -31,6 +31,7 @@ var index_exports = {};
31
31
  __export(index_exports, {
32
32
  CustomHeaders: () => CustomHeaders,
33
33
  DataService: () => DataService,
34
+ MissingPersistenceIdentityError: () => MissingPersistenceIdentityError,
34
35
  Model: () => Model,
35
36
  ModelService: () => ModelService,
36
37
  Service: () => Service,
@@ -44,46 +45,163 @@ module.exports = __toCommonJS(index_exports);
44
45
 
45
46
  // src/adapter.ts
46
47
  var import_axios8 = __toESM(require("axios"));
47
- var import_utils7 = require("@web-ts-toolkit/utils");
48
48
 
49
49
  // src/services/model-service.ts
50
- var import_axios4 = require("axios");
51
- var import_utils5 = require("@web-ts-toolkit/utils");
50
+ var import_axios6 = require("axios");
52
51
 
53
52
  // src/model.ts
54
53
  var import_utils = require("@web-ts-toolkit/utils");
54
+ var MissingPersistenceIdentityError = class extends Error {
55
+ constructor(message) {
56
+ super(message);
57
+ this.name = "MissingPersistenceIdentityError";
58
+ }
59
+ };
55
60
  var Model = class _Model {
56
- constructor(data, adapter) {
61
+ constructor(data, adapter, persistenceId, fromExisting) {
57
62
  this.modifiedPaths = /* @__PURE__ */ new Set();
63
+ this._persistenceId = persistenceId;
64
+ this._fromExisting = fromExisting ?? false;
58
65
  this._snapshot = (0, import_utils.cloneDeep)(data);
59
66
  this.defineHiddenDataProp((0, import_utils.cloneDeep)(data));
60
67
  this.defineHiddenAdapterProp(adapter);
61
68
  this.definePublicDataProps();
62
69
  this.initializeDirtyState();
63
70
  }
64
- static create(data, adapter) {
65
- return new _Model(data, adapter);
66
- }
71
+ static create(data, adapter, persistenceId, fromExisting) {
72
+ return new _Model(data, adapter, persistenceId, fromExisting);
73
+ }
74
+ /**
75
+ * Persists the currently dirty paths to the server, then merges the
76
+ * server's response back into local state.
77
+ *
78
+ * Concurrency contract:
79
+ *
80
+ * 1. Submitted paths and their values are snapshotted before the request
81
+ * starts, so an in-flight response cannot wipe edits that were made
82
+ * while the request was pending.
83
+ * 2. On success, a submitted path is cleared from `modifiedPaths` only if
84
+ * its current local value still equals the submitted value — i.e. the
85
+ * user has not concurrently re-edited it to a different value.
86
+ * 3. Server-returned values overwrite local values for paths the user did
87
+ * NOT concurrently re-modify during the in-flight save; for paths the
88
+ * user did concurrently re-modify, the local value is preserved and
89
+ * the dirty flag is retained so the concurrent edit is resubmitted on
90
+ * the next `save()`. (Deterministic conflict rule: the newer local
91
+ * edit wins for the same path; the server value becomes its reset
92
+ * baseline without replacing the newer local value.)
93
+ * 4. On failure, no dirty state is cleared and no local value is
94
+ * overwritten; the caller can retry `save()` with the same set.
95
+ * 5. The return value echoes `{ ...result, data }` where `data` is a
96
+ * refreshed `Model` snapshot of the post-save local state (or `null`
97
+ * on failure), matching `ModelResponse<T, TData>`.
98
+ *
99
+ * Persistence identity (ARC-21): create-vs-update is resolved from a
100
+ * captured persistence identity rather than from the projected `_data`
101
+ * payload alone, so a read that strips `_id` (e.g. `select: { name: 1,
102
+ * _id: 0 }`) cannot turn a subsequent `save()` into a silent create of
103
+ * a duplicate. When `_data._id` is present it takes precedence so callers
104
+ * can still deliberately aim `_id` at a bogus id to observe a failing
105
+ * save. When neither `_data._id` nor a captured persistence identity is
106
+ * available (e.g. `readAdvancedFilter` with an `_id`-excluding
107
+ * projection), `save()` throws `MissingPersistenceIdentityError` instead
108
+ * of POSTing a new document.
109
+ */
67
110
  async save(reqConfig) {
68
- let result;
69
- if (this._data._id) {
70
- result = await this._service.update(this._data._id, this.prepareData(), { returningAll: false }, reqConfig);
71
- } else {
72
- result = await this._service.create(this.prepareData(), null, reqConfig);
111
+ const submittedPaths = new Set(this.modifiedPaths);
112
+ const submittedValues = {};
113
+ for (const path of submittedPaths) {
114
+ submittedValues[path] = (0, import_utils.cloneDeep)((0, import_utils.get)(this._data, path));
115
+ }
116
+ const submittedData = this.prepareData();
117
+ const persistenceId = this._data._id ?? this._persistenceId;
118
+ if (persistenceId == null && this._fromExisting) {
119
+ throw new MissingPersistenceIdentityError(
120
+ "Model.save() cannot determine create-vs-update without a persistence identity. A read of an existing document produced a Model whose projection strips `_id` and no identity was captured at read time. Use `read(id)` / `readAdvanced(id, ...)` (which capture the identifier as a persistence identity), or include `_id` in the projection."
121
+ );
122
+ }
123
+ const isCreate = persistenceId == null;
124
+ const result = isCreate ? await this._service.create(submittedData, null, reqConfig) : await this._service.update(String(persistenceId), submittedData, { returningAll: false }, reqConfig);
125
+ if (!result.success) {
126
+ return { ...result, data: null };
127
+ }
128
+ const isConcurrentEdit = (path) => {
129
+ if (!submittedPaths.has(path)) {
130
+ return this.modifiedPaths.has(path);
131
+ }
132
+ const current = (0, import_utils.get)(this._data, path);
133
+ const submitted = submittedValues[path];
134
+ return !(0, import_utils.isEqual)(current, submitted);
135
+ };
136
+ const serverData = result.raw ?? {};
137
+ for (const key of Object.keys(serverData)) {
138
+ const normKey = this.normalizePath(key);
139
+ if (normKey === "_id") continue;
140
+ if (isConcurrentEdit(normKey)) {
141
+ continue;
142
+ }
143
+ const serverValue = serverData[key];
144
+ const before = (0, import_utils.get)(this._data, normKey);
145
+ if (!(0, import_utils.isEqual)(before, serverValue)) {
146
+ this._data[normKey] = serverValue;
147
+ }
148
+ this.modifiedPaths.delete(normKey);
73
149
  }
74
- if (result.success) {
75
- this.updateModel(result.raw);
76
- this._snapshot = (0, import_utils.cloneDeep)(this._data);
77
- this.modifiedPaths.clear();
150
+ for (const path of submittedPaths) {
151
+ if (!isConcurrentEdit(path)) {
152
+ this.modifiedPaths.delete(path);
153
+ }
154
+ }
155
+ if (isCreate && serverData._id != null) {
156
+ this._data._id = String(serverData._id);
157
+ this.modifiedPaths.delete("_id");
158
+ }
159
+ const latestId = this._data._id ?? (serverData._id != null ? String(serverData._id) : void 0);
160
+ if (latestId != null) {
161
+ this._persistenceId = latestId;
162
+ }
163
+ const nextSnapshot = Array.isArray(this._snapshot) ? [] : {};
164
+ const dataRecord = this._data;
165
+ for (const key of Object.keys(dataRecord)) {
166
+ const normKey = this.normalizePath(key);
167
+ if (this.modifiedPaths.has(normKey)) {
168
+ if (submittedPaths.has(normKey)) {
169
+ nextSnapshot[key] = (0, import_utils.cloneDeep)((0, import_utils.hasOwn)(serverData, key) ? serverData[key] : submittedValues[normKey]);
170
+ } else {
171
+ nextSnapshot[key] = (0, import_utils.cloneDeep)(this._snapshot[key]);
172
+ }
173
+ } else {
174
+ nextSnapshot[key] = (0, import_utils.cloneDeep)(dataRecord[key]);
175
+ }
78
176
  }
177
+ this._snapshot = nextSnapshot;
178
+ this.definePublicDataProps();
79
179
  return {
80
180
  ...result,
81
- data: result.success ? _Model.create(this._data, this._service) : null
181
+ // The post-save snapshot is always an existing document, so propagate
182
+ // `_fromExisting=true` plus the refreshed persistence identity so the
183
+ // returned wrapper cannot later silently create a duplicate. (If the
184
+ // caller intends a fresh draft, they construct `new Model({...}, s)`
185
+ // directly — `${_fromExisting}` defaults to `false` there.)
186
+ data: _Model.create(this._data, this._service, this._persistenceId, true)
82
187
  };
83
188
  }
84
189
  isDirty(path) {
85
190
  return path ? this.modifiedPaths.has(this.normalizePath(String(path))) : this.modifiedPaths.size > 0;
86
191
  }
192
+ /**
193
+ * Marks a path dirty and skips snapshot reconciliation. This is the
194
+ * explicit "include this path on the next save()" escape hatch: even when
195
+ * the effective value still equals the snapshot, the path stays dirty so
196
+ * callers can force a field to be re-sent to the server (e.g., to retrigger
197
+ * server-side defaults or to re-submit a value that another client may
198
+ * have reverted).
199
+ *
200
+ * For implicit writes that reconcile against the snapshot automatically
201
+ * (reverting a field to its baseline clears the dirty flag), use `set()`,
202
+ * `assign(...)`, or direct property assignment — those entry points all
203
+ * run `reconcilePath` after the write.
204
+ */
87
205
  markModified(path) {
88
206
  this.trackModified(String(path));
89
207
  return this;
@@ -99,6 +217,7 @@ var Model = class _Model {
99
217
  (0, import_utils.set)(this._data, path, value);
100
218
  this.trackModified(path);
101
219
  this.definePublicDataProps();
220
+ this.reconcilePath(this.normalizePath(path));
102
221
  return this;
103
222
  }
104
223
  assign(partial) {
@@ -111,6 +230,9 @@ var Model = class _Model {
111
230
  }
112
231
  }
113
232
  this.definePublicDataProps();
233
+ for (let x = 0; x < keys.length; x++) {
234
+ this.reconcilePath(this.normalizePath(String(keys[x])));
235
+ }
114
236
  return this;
115
237
  }
116
238
  reset() {
@@ -124,10 +246,6 @@ var Model = class _Model {
124
246
  toJSON() {
125
247
  return this.toObject();
126
248
  }
127
- updateModel(data) {
128
- (0, import_utils.assign)(this._data, data);
129
- this.definePublicDataProps();
130
- }
131
249
  replaceData(data) {
132
250
  const nextData = (0, import_utils.cloneDeep)(data);
133
251
  const currentKeys = Object.keys(this._data);
@@ -141,7 +259,7 @@ var Model = class _Model {
141
259
  this.definePublicDataProps();
142
260
  }
143
261
  initializeDirtyState() {
144
- if (this._data._id) {
262
+ if (this._fromExisting || this._data._id) {
145
263
  return;
146
264
  }
147
265
  const keys = Object.keys(this._data);
@@ -165,6 +283,7 @@ var Model = class _Model {
165
283
  }
166
284
  this.trackModified(keystr);
167
285
  target[key] = value;
286
+ this.reconcilePath(this.normalizePath(keystr));
168
287
  return true;
169
288
  }
170
289
  }),
@@ -200,6 +319,25 @@ var Model = class _Model {
200
319
  normalizePath(path) {
201
320
  return path.split(".")[0];
202
321
  }
322
+ /**
323
+ * Removes `path` from the dirty set when its current top-level value deeply
324
+ * equals the snapshot baseline. Used uniformly by `set()`, `assign()`,
325
+ * public property setters (via the proxy), and `markModified()` so all
326
+ * entry points share the same tracking rule.
327
+ *
328
+ * Note: `_id` is intentionally never reconciled away here — it is excluded
329
+ * from `initializeDirtyState` and managed explicitly during `save()`
330
+ * reconciliation.
331
+ */
332
+ reconcilePath(path) {
333
+ if (path === "_id") return;
334
+ if (!this.modifiedPaths.has(path)) return;
335
+ const current = this._data[path];
336
+ const base = this._snapshot[path];
337
+ if ((0, import_utils.isEqual)(current, base)) {
338
+ this.modifiedPaths.delete(path);
339
+ }
340
+ }
203
341
  };
204
342
 
205
343
  // src/services/service.ts
@@ -210,14 +348,13 @@ var CACHE_HEADER = "x-axios-cache";
210
348
 
211
349
  // src/services/wrap.ts
212
350
  var import_axios = require("axios");
213
- var import_utils3 = require("@web-ts-toolkit/utils");
214
351
 
215
352
  // src/helpers.ts
216
353
  var import_utils2 = require("@web-ts-toolkit/utils");
217
354
  function replaceSubQuery(filter) {
218
355
  if (!(0, import_utils2.isPlainObject)(filter)) return filter;
219
356
  const ret = (0, import_utils2.mapValues)(filter, (val) => {
220
- if (val && val.__op && val.__query) {
357
+ if ((0, import_utils2.isPlainObject)(val) && "__op" in val && val.__op && "__query" in val && val.__query) {
221
358
  return {
222
359
  $$sq: val.__query
223
360
  };
@@ -232,16 +369,20 @@ function replaceSubQuery(filter) {
232
369
  });
233
370
  return ret;
234
371
  }
372
+ function encodePathSegment(value) {
373
+ if (value === void 0 || value === null) return "";
374
+ return encodeURIComponent(String(value));
375
+ }
235
376
  function template(templateString, data) {
236
377
  return templateString.replace(/\{\{(\w+)\}\}/g, (match, key) => {
237
- return data[key] !== void 0 ? data[key] : match;
378
+ return data[key] !== void 0 ? encodePathSegment(data[key]) : match;
238
379
  });
239
380
  }
240
381
  function getWrapContext(url, options, config) {
241
382
  const { queryParams, pathParams } = options ?? {};
242
383
  const finalUrl = pathParams ? template(url, pathParams) : url;
243
- if (queryParams && config) config.params = queryParams;
244
- return { finalUrl, finalConfig: config };
384
+ const finalConfig = queryParams && config ? { ...config, params: queryParams } : queryParams && !config ? { params: queryParams } : config;
385
+ return { finalUrl, finalConfig };
245
386
  }
246
387
 
247
388
  // src/services/wrap.ts
@@ -251,10 +392,12 @@ function resolveUrl(basePath, url) {
251
392
  return basePath ? `${removeTrailingSlash(basePath)}/${removeLeadingSlash(url)}` : url;
252
393
  }
253
394
  function prepareConfig(defaultConfig, cacheValue, requestConfig) {
254
- (0, import_utils3.set)(defaultConfig, `headers.${CACHE_HEADER}`, cacheValue);
255
- return (0, import_axios.mergeConfig)(defaultConfig, requestConfig);
395
+ const headerClone = new import_axios.AxiosHeaders(defaultConfig.headers);
396
+ headerClone.set(CACHE_HEADER, cacheValue);
397
+ const defaulted = { ...defaultConfig, headers: headerClone };
398
+ return (0, import_axios.mergeConfig)(defaulted, requestConfig);
256
399
  }
257
- function createWrapHelper(axios2, basePath) {
400
+ function createWrapHelper(axios3, basePath) {
258
401
  return {
259
402
  wrapGet: (url, defaultConfig = {}) => {
260
403
  const _url = resolveUrl(basePath, url);
@@ -264,7 +407,7 @@ function createWrapHelper(axios2, basePath) {
264
407
  options,
265
408
  prepareConfig(defaultConfig, "true", requestConfig)
266
409
  );
267
- return axios2.get(finalUrl, finalConfig);
410
+ return axios3.get(finalUrl, finalConfig);
268
411
  };
269
412
  },
270
413
  wrapPost: (url, defaultConfig = {}) => {
@@ -275,7 +418,7 @@ function createWrapHelper(axios2, basePath) {
275
418
  options,
276
419
  prepareConfig(defaultConfig, "false", requestConfig)
277
420
  );
278
- return axios2.post(finalUrl, data, finalConfig);
421
+ return axios3.post(finalUrl, data, finalConfig);
279
422
  };
280
423
  },
281
424
  wrapPut: (url, defaultConfig = {}) => {
@@ -286,7 +429,7 @@ function createWrapHelper(axios2, basePath) {
286
429
  options,
287
430
  prepareConfig(defaultConfig, "false", requestConfig)
288
431
  );
289
- return axios2.put(finalUrl, data, finalConfig);
432
+ return axios3.put(finalUrl, data, finalConfig);
290
433
  };
291
434
  },
292
435
  wrapPatch: (url, defaultConfig = {}) => {
@@ -297,7 +440,7 @@ function createWrapHelper(axios2, basePath) {
297
440
  options,
298
441
  prepareConfig(defaultConfig, "false", requestConfig)
299
442
  );
300
- return axios2.patch(finalUrl, data, finalConfig);
443
+ return axios3.patch(finalUrl, data, finalConfig);
301
444
  };
302
445
  },
303
446
  wrapDelete: (url, defaultConfig = {}) => {
@@ -308,7 +451,7 @@ function createWrapHelper(axios2, basePath) {
308
451
  options,
309
452
  prepareConfig(defaultConfig, "false", requestConfig)
310
453
  );
311
- return axios2.delete(finalUrl, finalConfig);
454
+ return axios3.delete(finalUrl, finalConfig);
312
455
  };
313
456
  }
314
457
  };
@@ -358,38 +501,77 @@ var stringifyErrorPayload = (value) => {
358
501
  return String(value);
359
502
  }
360
503
  };
504
+ function finalizeOperationResult({
505
+ success,
506
+ raw,
507
+ status,
508
+ headers = {},
509
+ message,
510
+ totalCount
511
+ }) {
512
+ if (success) {
513
+ return {
514
+ success: true,
515
+ raw,
516
+ data: raw,
517
+ message: "",
518
+ status,
519
+ headers,
520
+ ...totalCount == null ? {} : { totalCount }
521
+ };
522
+ }
523
+ return {
524
+ success: false,
525
+ raw,
526
+ data: null,
527
+ message: message ?? stringifyErrorPayload(raw),
528
+ status,
529
+ headers,
530
+ ...totalCount == null ? {} : { totalCount }
531
+ };
532
+ }
533
+ var normalizeTransportFailure = (error) => {
534
+ const transportError = error && typeof error === "object" ? error : {};
535
+ let raw = null;
536
+ let status = 0;
537
+ let headers = {};
538
+ let message;
539
+ if (transportError.response) {
540
+ status = transportError.response.status;
541
+ headers = transportError.response.headers;
542
+ raw = transportError.response.data;
543
+ } else if (transportError.request) {
544
+ message = "The server is not responding";
545
+ } else {
546
+ message = transportError.message;
547
+ }
548
+ return finalizeOperationResult({ success: false, raw, status, headers, message });
549
+ };
361
550
  var Service = class {
362
- constructor(axios2, basePath) {
363
- this._axios = axios2;
551
+ constructor(axios3, basePath, throwOnError = false) {
552
+ this._axios = axios3;
364
553
  this._basePath = basePath;
365
- this._wrap = createWrapHelper(axios2, basePath);
554
+ this._wrap = createWrapHelper(axios3, basePath);
555
+ this._throwOnError = throwOnError;
366
556
  }
367
557
  handleSuccess(res, extra = {}) {
368
- return { success: true, raw: res.data, status: res.status, headers: res.headers, ...extra };
558
+ return {
559
+ ...finalizeOperationResult({
560
+ success: true,
561
+ raw: res.data,
562
+ status: res.status,
563
+ headers: res.headers
564
+ }),
565
+ ...extra
566
+ };
369
567
  }
370
568
  // See https://axios-http.com/docs/handling-errors
371
569
  handleError(error) {
372
- const result = {
373
- success: false,
374
- raw: null,
375
- data: null,
376
- message: "",
377
- status: 0,
378
- headers: {}
379
- };
380
- if (error.response) {
381
- result.status = error.response.status;
382
- result.headers = error.response.headers;
383
- const responseData = error.response.data;
384
- result.raw = responseData;
385
- result.data = responseData;
386
- result.message = stringifyErrorPayload(responseData);
387
- } else if (error.request) {
388
- result.message = "The server is not responding";
389
- } else {
390
- result.message = error.message;
391
- }
392
- return result;
570
+ return normalizeTransportFailure(error);
571
+ }
572
+ /** Resolves per-call policy against the already-resolved service/adapter default. */
573
+ resolveThrowOnError(override) {
574
+ return override ?? this._throwOnError;
393
575
  }
394
576
  wrapGet(url, defaultAxiosRequestConfig = {}) {
395
577
  return this._wrap.wrapGet(url, defaultAxiosRequestConfig);
@@ -406,6 +588,30 @@ var Service = class {
406
588
  wrapDelete(url, defaultAxiosRequestConfig = {}) {
407
589
  return this._wrap.wrapDelete(url, defaultAxiosRequestConfig);
408
590
  }
591
+ /**
592
+ * Public bridge to the per-service success/failure callback pipeline and
593
+ * `throwOnError` policy. Adapter-internal grouping machinery calls this so
594
+ * that grouped entries go through the same finalization the direct path
595
+ * uses (`createResponseHandler`). Returns `res` unchanged on success and
596
+ * throws `ServiceError` when both `res.success === false` and the
597
+ * `throwOnError` override (or the service-level default) are enabled.
598
+ */
599
+ applyResponseCallbacks(res, throwOnErrorOverride) {
600
+ const handler = this._handleCallbacks;
601
+ return handler ? handler(res, throwOnErrorOverride) : res;
602
+ }
603
+ /**
604
+ * Returns a fresh headers object that includes the package-owned
605
+ * `CACHE_HEADER` set to `"true"` (cache eligible) or `"false"` (bypass)
606
+ * according to the `ignoreCache` option. The caller's `CACHE_HEADER`
607
+ * value, if any, wins over the `ignoreCache` default.
608
+ *
609
+ * The input `headers` object is **never mutated**: an `AxiosHeaders`
610
+ * instance is cloned via `.toJSON()` before any value is set, and a
611
+ * plain-object headers input is shallow-copied. Reusing the same
612
+ * caller-owned headers across multiple requests therefore has no
613
+ * hidden side effects, and the order of invocations is irrelevant.
614
+ */
409
615
  updateHeaders(headers, { ignoreCache }) {
410
616
  const cacheValue = ignoreCache ? "false" : "true";
411
617
  if (!headers) {
@@ -413,8 +619,9 @@ var Service = class {
413
619
  }
414
620
  if (headers instanceof import_axios2.AxiosHeaders) {
415
621
  if (headers.has(CACHE_HEADER)) return headers;
416
- headers.set(CACHE_HEADER, cacheValue);
417
- return headers;
622
+ const cloned = new import_axios2.AxiosHeaders(headers.toJSON());
623
+ cloned.set(CACHE_HEADER, cacheValue);
624
+ return cloned;
418
625
  }
419
626
  if (CACHE_HEADER in headers) return headers;
420
627
  return {
@@ -427,14 +634,397 @@ var ServiceError = class extends Error {
427
634
  constructor(result) {
428
635
  super(result.message);
429
636
  this.name = "ServiceError";
430
- this.success = result.success;
637
+ this.success = false;
431
638
  this.raw = result.raw;
432
- this.data = result.data;
639
+ this.data = null;
433
640
  this.status = result.status;
434
641
  this.headers = result.headers;
435
642
  }
436
643
  };
437
644
 
645
+ // src/services/interceptors.ts
646
+ var import_axios4 = __toESM(require("axios"));
647
+
648
+ // src/services/cache-utils.ts
649
+ var import_axios3 = require("axios");
650
+ var import_utils3 = require("@web-ts-toolkit/utils");
651
+ var normalizeConfigValue = (value) => {
652
+ if (value == null) return value;
653
+ if (value instanceof import_axios3.AxiosHeaders) {
654
+ return normalizeConfigValue(value.toJSON());
655
+ }
656
+ if (Array.isArray(value)) {
657
+ return value.map((item) => normalizeConfigValue(item));
658
+ }
659
+ if (typeof value === "object") {
660
+ return Object.entries((0, import_utils3.omitBy)(value, (item) => item === void 0)).sort(([left], [right]) => left.localeCompare(right)).reduce((acc, [key, item]) => {
661
+ acc[key] = normalizeConfigValue(item);
662
+ return acc;
663
+ }, {});
664
+ }
665
+ return value;
666
+ };
667
+
668
+ // src/services/interceptors.ts
669
+ var DEFAULT_CACHE_CAPACITY = 100;
670
+ var CACHEABLE_METHODS = /* @__PURE__ */ new Set(["get"]);
671
+ var MUTATION_METHODS = /* @__PURE__ */ new Set(["post", "put", "patch", "delete"]);
672
+ var CACHEABLE_RESPONSE_TYPES = /* @__PURE__ */ new Set(["", "json", "text"]);
673
+ var SENSITIVE_CACHE_HEADERS = /* @__PURE__ */ new Set([
674
+ "authorization",
675
+ "cookie",
676
+ "set-cookie",
677
+ "proxy-authorization",
678
+ "www-authenticate"
679
+ ]);
680
+ var cloneConfigWithCacheBypass = (config) => {
681
+ const baseConfig = config ?? {};
682
+ const next = { ...baseConfig };
683
+ const sourceHeaders = config?.headers;
684
+ if (sourceHeaders instanceof import_axios4.AxiosHeaders) {
685
+ next.headers = sourceHeaders.toJSON();
686
+ } else if (sourceHeaders && typeof sourceHeaders === "object") {
687
+ next.headers = { ...sourceHeaders };
688
+ } else {
689
+ next.headers = {};
690
+ }
691
+ next.headers[CACHE_HEADER] = "false";
692
+ return next;
693
+ };
694
+ var SimpleCache = class {
695
+ constructor(opts = {}) {
696
+ this.cache = /* @__PURE__ */ new Map();
697
+ this.timers = /* @__PURE__ */ new Map();
698
+ this.capacity = opts.capacity !== void 0 && Number.isFinite(opts.capacity) && opts.capacity > 0 ? Math.floor(opts.capacity) : DEFAULT_CACHE_CAPACITY;
699
+ this.clone = opts.clone ?? defaultClone;
700
+ }
701
+ set(key, value, ttl) {
702
+ if (this.cache.size >= this.capacity && !this.cache.has(key)) {
703
+ const oldestKey = this.cache.keys().next().value;
704
+ if (oldestKey !== void 0) {
705
+ this.delete(oldestKey);
706
+ }
707
+ }
708
+ this.cache.delete(key);
709
+ this.cache.set(key, value);
710
+ if (ttl && ttl > 0) {
711
+ const existing = this.timers.get(key);
712
+ if (existing) clearTimeout(existing);
713
+ const timer = setTimeout(() => {
714
+ this.cache.delete(key);
715
+ this.timers.delete(key);
716
+ }, ttl);
717
+ if (typeof timer === "object" && timer && "unref" in timer && typeof timer.unref === "function") {
718
+ timer.unref();
719
+ }
720
+ this.timers.set(key, timer);
721
+ }
722
+ }
723
+ get(key) {
724
+ const value = this.cache.get(key);
725
+ if (value === void 0) {
726
+ return void 0;
727
+ }
728
+ this.cache.delete(key);
729
+ this.cache.set(key, value);
730
+ return this.clone(value);
731
+ }
732
+ has(key) {
733
+ return this.cache.has(key);
734
+ }
735
+ delete(key) {
736
+ const timer = this.timers.get(key);
737
+ if (timer) {
738
+ clearTimeout(timer);
739
+ this.timers.delete(key);
740
+ }
741
+ return this.cache.delete(key);
742
+ }
743
+ clear() {
744
+ for (const timer of this.timers.values()) {
745
+ clearTimeout(timer);
746
+ }
747
+ this.timers.clear();
748
+ this.cache.clear();
749
+ }
750
+ dispose() {
751
+ this.clear();
752
+ }
753
+ };
754
+ var defaultClone = (value) => {
755
+ if (value == null) return value;
756
+ try {
757
+ return JSON.parse(JSON.stringify(value));
758
+ } catch {
759
+ return value;
760
+ }
761
+ };
762
+ var CACHE_REQUEST_STATE = /* @__PURE__ */ Symbol("access-router-client.cache-request-state");
763
+ var CACHE_DISPOSED_ERROR = "Access router client cache was disposed while the request was in flight";
764
+ var setCacheRequestState = (config, state) => {
765
+ Object.defineProperty(config, CACHE_REQUEST_STATE, {
766
+ configurable: false,
767
+ enumerable: false,
768
+ writable: false,
769
+ value: Object.freeze(state)
770
+ });
771
+ };
772
+ var isUnsupportedResponseBody = (response) => {
773
+ const responseType = response.config?.responseType;
774
+ if (responseType === "stream" || responseType === "arraybuffer" || responseType === "blob" || responseType === "document") {
775
+ return true;
776
+ }
777
+ const data = response.data;
778
+ if (data == null) return false;
779
+ if (typeof data === "string") return false;
780
+ if (typeof data !== "object") return false;
781
+ if (Array.isArray(data)) return false;
782
+ try {
783
+ JSON.stringify(data);
784
+ return false;
785
+ } catch {
786
+ return true;
787
+ }
788
+ };
789
+ var snapshotResponse = (response) => {
790
+ const headers = response.headers instanceof import_axios4.AxiosHeaders ? response.headers.toJSON() : { ...response.headers ?? {} };
791
+ return {
792
+ data: defaultClone(response.data),
793
+ status: response.status,
794
+ statusText: response.statusText,
795
+ headers: defaultClone(headers)
796
+ };
797
+ };
798
+ var serializeHeaders = (headers) => {
799
+ const resolvedHeaders = headers instanceof import_axios4.AxiosHeaders ? headers.toJSON() : headers;
800
+ const normalizedHeaders = Object.entries(resolvedHeaders ?? {}).filter(([key, value]) => {
801
+ const normalizedKey = key.toLowerCase();
802
+ return normalizedKey !== CACHE_HEADER.toLowerCase() && !SENSITIVE_CACHE_HEADERS.has(normalizedKey) && value !== void 0;
803
+ }).reduce((acc, [key, value]) => {
804
+ acc[key.toLowerCase()] = value;
805
+ return acc;
806
+ }, {});
807
+ return JSON.stringify(normalizeConfigValue(normalizedHeaders));
808
+ };
809
+ var hasStableCacheValue = (value, seen = /* @__PURE__ */ new Set()) => {
810
+ if (value == null || typeof value === "string" || typeof value === "number" || typeof value === "boolean")
811
+ return true;
812
+ if (typeof value !== "object") return false;
813
+ if (seen.has(value)) return false;
814
+ if (value instanceof import_axios4.AxiosHeaders) {
815
+ return hasStableCacheValue(value.toJSON(), seen);
816
+ }
817
+ const prototype = Object.getPrototypeOf(value);
818
+ if (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null) return false;
819
+ seen.add(value);
820
+ const stable = Object.values(value).every((item) => hasStableCacheValue(item, seen));
821
+ seen.delete(value);
822
+ return stable;
823
+ };
824
+ var sameTransform = (configured, defaultValue) => {
825
+ const configuredList = Array.isArray(configured) ? configured : [configured];
826
+ const defaultList = Array.isArray(defaultValue) ? defaultValue : [defaultValue];
827
+ return configuredList.length === defaultList.length && configuredList.every((item, index) => item === defaultList[index]);
828
+ };
829
+ var sameConfigIdentity = (configured, defaultValue) => {
830
+ if (Array.isArray(configured) && Array.isArray(defaultValue)) {
831
+ return configured.length === defaultValue.length && configured.every((item, index) => item === defaultValue[index]);
832
+ }
833
+ return configured === defaultValue;
834
+ };
835
+ var isCacheEligible = (config, instance) => {
836
+ const method = (config.method ?? "get").toLowerCase();
837
+ const responseType = config.responseType ?? "";
838
+ return CACHEABLE_METHODS.has(method) && CACHEABLE_RESPONSE_TYPES.has(responseType) && config.paramsSerializer === void 0 && config.auth === void 0 && config.signal === void 0 && config.cancelToken === void 0 && config.onDownloadProgress === void 0 && config.onUploadProgress === void 0 && sameConfigIdentity(config.adapter, instance.defaults.adapter) && sameTransform(config.transformRequest, instance.defaults.transformRequest) && sameTransform(config.transformResponse, instance.defaults.transformResponse) && hasStableCacheValue(config.params) && hasStableCacheValue(config.data) && hasStableCacheValue(config.headers);
839
+ };
840
+ function generateCacheKey(config, partition) {
841
+ const responseSemantics = JSON.stringify({
842
+ responseType: config.responseType ?? "",
843
+ responseEncoding: config.responseEncoding ?? "",
844
+ decompress: config.decompress ?? true,
845
+ timeout: config.timeout ?? 0,
846
+ maxContentLength: config.maxContentLength ?? -1,
847
+ maxBodyLength: config.maxBodyLength ?? -1,
848
+ withCredentials: Boolean(config.withCredentials),
849
+ transitional: normalizeConfigValue(config.transitional)
850
+ });
851
+ const key = `${config.baseURL}/${config.url}_${config.method}_${generateParamKey(config.params)}_${generateDataKey(
852
+ config.data
853
+ )}_${partition ?? ""}_${serializeHeaders(config.headers)}_${responseSemantics}`;
854
+ return encodeURI(key);
855
+ }
856
+ function generateParamKey(params) {
857
+ if (!params) return "";
858
+ return JSON.stringify(normalizeConfigValue(params));
859
+ }
860
+ function generateDataKey(data) {
861
+ if (!data) return "";
862
+ return typeof data === "string" ? data : JSON.stringify(normalizeConfigValue(data));
863
+ }
864
+ var resolveWithCredentials = (config, withCredentialsDefault) => {
865
+ if (config.withCredentials !== void 0) {
866
+ return Boolean(config.withCredentials);
867
+ }
868
+ return withCredentialsDefault;
869
+ };
870
+ function useCacheInterceptors(instance, policyOrTtl) {
871
+ const policy = typeof policyOrTtl === "number" ? { ttl: policyOrTtl } : policyOrTtl;
872
+ const store = new SimpleCache({ capacity: policy.capacity, clone: policy.clone });
873
+ const withCredentialsDefault = policy.withCredentialsDefault ?? Boolean(instance.defaults.withCredentials);
874
+ const inflight = /* @__PURE__ */ new Map();
875
+ let generation = 0;
876
+ let disposed = false;
877
+ const finalizeInflight = (slot) => {
878
+ if (inflight.get(slot.key) === slot) {
879
+ inflight.delete(slot.key);
880
+ }
881
+ };
882
+ const resolveInflight = (slot, response) => {
883
+ if (slot.settled) return;
884
+ slot.settled = true;
885
+ slot.resolve(response);
886
+ finalizeInflight(slot);
887
+ };
888
+ const rejectInflight = (slot, error) => {
889
+ if (slot.settled) return;
890
+ slot.settled = true;
891
+ slot.reject(error);
892
+ finalizeInflight(slot);
893
+ };
894
+ const invalidate = () => {
895
+ generation += 1;
896
+ store.clear();
897
+ inflight.clear();
898
+ };
899
+ instance.interceptors.request.use(
900
+ async (config) => {
901
+ if (disposed || config.headers[CACHE_HEADER] === "false" || !isCacheEligible(config, instance)) return config;
902
+ const isCredentialed = resolveWithCredentials(config, withCredentialsDefault);
903
+ const partitionKey = policy.partitionForRequest?.(config);
904
+ if (isCredentialed && !partitionKey) {
905
+ return config;
906
+ }
907
+ const key = generateCacheKey(config, partitionKey);
908
+ policy.onCacheKey?.(key);
909
+ const snapshot = store.get(key);
910
+ if (snapshot) {
911
+ setCacheRequestState(config, Object.freeze({ key, generation, role: "hit" }));
912
+ config.adapter = async (_config) => {
913
+ return {
914
+ data: snapshot.data,
915
+ status: snapshot.status,
916
+ statusText: snapshot.statusText,
917
+ headers: { ...snapshot.headers, [CACHE_HEADER]: "true" },
918
+ config: _config
919
+ };
920
+ };
921
+ return config;
922
+ }
923
+ const existing = inflight.get(key);
924
+ if (existing) {
925
+ config.adapter = async (_config) => {
926
+ const response = await existing.promise;
927
+ const shared = response;
928
+ return {
929
+ data: defaultClone(shared.data),
930
+ status: shared.status,
931
+ statusText: shared.statusText,
932
+ headers: { ...shared.headers, [CACHE_HEADER]: "true" },
933
+ config: _config
934
+ };
935
+ };
936
+ setCacheRequestState(config, Object.freeze({ key, generation, role: "tail", slot: existing }));
937
+ return config;
938
+ }
939
+ let resolvePromise;
940
+ let rejectPromise;
941
+ const inflightPromise = new Promise((resolve, reject) => {
942
+ resolvePromise = resolve;
943
+ rejectPromise = reject;
944
+ });
945
+ inflightPromise.catch(() => {
946
+ });
947
+ const slot = {
948
+ key,
949
+ generation,
950
+ promise: inflightPromise,
951
+ resolve: resolvePromise,
952
+ reject: rejectPromise,
953
+ settled: false
954
+ };
955
+ inflight.set(key, slot);
956
+ const nextConfig = { ...config };
957
+ setCacheRequestState(nextConfig, Object.freeze({ key, generation, role: "source", slot }));
958
+ let realAdapter = config.adapter;
959
+ if (realAdapter === void 0 || realAdapter === null) {
960
+ realAdapter = instance.defaults.adapter;
961
+ }
962
+ const dispatch = typeof realAdapter === "function" ? realAdapter : typeof import_axios4.default.getAdapter === "function" ? import_axios4.default.getAdapter(
963
+ realAdapter,
964
+ instance.defaults
965
+ ) : void 0;
966
+ nextConfig.adapter = async (adapterConfig) => {
967
+ if (!dispatch) {
968
+ const response = await adapterConfig.adapter;
969
+ return response;
970
+ }
971
+ try {
972
+ const response = await dispatch(adapterConfig);
973
+ return response;
974
+ } catch (error) {
975
+ rejectInflight(slot, error);
976
+ throw error;
977
+ }
978
+ };
979
+ return nextConfig;
980
+ },
981
+ (error) => Promise.reject(error)
982
+ );
983
+ instance.interceptors.response.use(
984
+ (response) => {
985
+ const method = (response.config.method ?? "get").toLowerCase();
986
+ if (response.config.headers[CACHE_HEADER] === "false" || MUTATION_METHODS.has(method)) {
987
+ if (response.status >= 200 && response.status < 300) {
988
+ invalidate();
989
+ }
990
+ return response;
991
+ }
992
+ const state = response.config[CACHE_REQUEST_STATE];
993
+ if (!state || state.role !== "source" || !state.slot) {
994
+ return response;
995
+ }
996
+ if (response.status >= 200 && response.status < 300) {
997
+ if (!disposed && state.generation === generation && !isUnsupportedResponseBody(response)) {
998
+ store.set(state.key, snapshotResponse(response), policy.ttl);
999
+ }
1000
+ }
1001
+ resolveInflight(state.slot, response);
1002
+ return response;
1003
+ },
1004
+ (error) => {
1005
+ const state = (error?.config ?? {})[CACHE_REQUEST_STATE];
1006
+ if (state?.role === "source" && state.slot) {
1007
+ rejectInflight(state.slot, error);
1008
+ }
1009
+ return Promise.reject(error);
1010
+ }
1011
+ );
1012
+ return {
1013
+ clear: invalidate,
1014
+ dispose: () => {
1015
+ if (disposed) return;
1016
+ disposed = true;
1017
+ generation += 1;
1018
+ store.dispose();
1019
+ const error = new Error(CACHE_DISPOSED_ERROR);
1020
+ for (const slot of inflight.values()) {
1021
+ rejectInflight(slot, error);
1022
+ }
1023
+ inflight.clear();
1024
+ }
1025
+ };
1026
+ }
1027
+
438
1028
  // src/services/shared.ts
439
1029
  var import_utils4 = require("@web-ts-toolkit/utils");
440
1030
 
@@ -451,10 +1041,118 @@ var CustomHeaders = /* @__PURE__ */ ((CustomHeaders2) => {
451
1041
  })(CustomHeaders || {});
452
1042
 
453
1043
  // src/services/shared.ts
1044
+ var getSubdocumentResultShape = (query) => {
1045
+ if (query.target !== "model") return void 0;
1046
+ switch (query.op) {
1047
+ case "subList":
1048
+ case "subCreate":
1049
+ case "subBulkUpdate":
1050
+ return "list";
1051
+ case "subRead":
1052
+ case "subUpdate":
1053
+ return "single";
1054
+ case "subDelete":
1055
+ return "scalar";
1056
+ default:
1057
+ return void 0;
1058
+ }
1059
+ };
1060
+ function finalizeRootEntry(query, entry, responseHeaders, service) {
1061
+ const { result, message: entryMessage, statusCode, op } = entry;
1062
+ const success = result.success;
1063
+ const baseResult = finalizeOperationResult({
1064
+ success,
1065
+ raw: success ? result.data : result,
1066
+ status: statusCode,
1067
+ headers: responseHeaders,
1068
+ message: success ? void 0 : entryMessage
1069
+ });
1070
+ let _raw = baseResult.raw;
1071
+ let _data = baseResult.data;
1072
+ const subdocumentResultShape = getSubdocumentResultShape(query);
1073
+ if (!success) {
1074
+ _data = null;
1075
+ } else if (subdocumentResultShape) {
1076
+ if (subdocumentResultShape === "list") {
1077
+ const rows = result.data == null ? [] : (0, import_utils4.castArray)(result.data);
1078
+ _raw = rows;
1079
+ _data = rows;
1080
+ }
1081
+ } else if (query.target === "model") {
1082
+ const modelService = service;
1083
+ if (result.kind === "list" && Array.isArray(result.data)) {
1084
+ if (op === "create" && !Array.isArray(query.data) && result.data.length === 1) {
1085
+ _raw = result.data[0];
1086
+ if (modelService) {
1087
+ _data = Model.create(result.data[0], modelService, void 0, true);
1088
+ }
1089
+ } else if (op !== "distinct") {
1090
+ const rows = (0, import_utils4.castArray)(result.data);
1091
+ if (modelService) {
1092
+ _data = rows.map((item) => Model.create(item, modelService, void 0, true));
1093
+ } else {
1094
+ _data = rows;
1095
+ }
1096
+ }
1097
+ } else if (result.kind === "single" && (op === "new" || op === "read" || op === "update" || op === "upsert")) {
1098
+ if (op === "new" && result.data && typeof result.data === "object") {
1099
+ const { _id: _generatedId, ...draft } = result.data;
1100
+ void _generatedId;
1101
+ _raw = draft;
1102
+ _data = draft;
1103
+ }
1104
+ if (modelService) {
1105
+ const fromExisting = op !== "new";
1106
+ const persistenceId = op === "read" && query.target === "model" && "id" in query ? query.id : void 0;
1107
+ _data = Model.create(_data, modelService, persistenceId, fromExisting);
1108
+ }
1109
+ }
1110
+ }
1111
+ const isSubdocumentList = subdocumentResultShape === "list";
1112
+ const isModelOrDataList = !subdocumentResultShape && query.op === "list";
1113
+ const returnedCount = Array.isArray(_data) ? _data.length : 0;
1114
+ const totalCount = success && query.options?.includeCount === true ? result.totalCount ?? result.count ?? returnedCount : 0;
1115
+ return {
1116
+ success,
1117
+ raw: _raw,
1118
+ data: _data,
1119
+ message: baseResult.message,
1120
+ status: statusCode,
1121
+ ...isSubdocumentList ? { count: success ? returnedCount : 0 } : {},
1122
+ ...isModelOrDataList ? { totalCount } : {},
1123
+ headers: responseHeaders
1124
+ };
1125
+ }
1126
+ function finalizeRootTransportFailure(query, error) {
1127
+ const failure = normalizeTransportFailure(error);
1128
+ const subdocumentResultShape = getSubdocumentResultShape(query);
1129
+ return {
1130
+ ...failure,
1131
+ ...subdocumentResultShape === "list" ? { count: 0 } : {},
1132
+ ...!subdocumentResultShape && query.op === "list" ? { totalCount: 0 } : {}
1133
+ };
1134
+ }
1135
+ function applyGroupCallbacks(entries, services, groupThrowOnError) {
1136
+ let callbackError;
1137
+ for (let i = 0; i < entries.length; i++) {
1138
+ const svc = services[i];
1139
+ try {
1140
+ entries[i] = svc?.applyResponseCallbacks ? svc.applyResponseCallbacks(entries[i], false) : entries[i];
1141
+ } catch (error) {
1142
+ callbackError ??= error;
1143
+ }
1144
+ }
1145
+ if (callbackError) throw callbackError;
1146
+ if (groupThrowOnError) {
1147
+ const failure = entries.find((entry) => !entry.success);
1148
+ if (failure) throw new ServiceError(toResultError(failure));
1149
+ }
1150
+ return entries;
1151
+ }
454
1152
  var toResultError = (result) => ({
455
1153
  success: false,
456
1154
  raw: result.raw ?? null,
457
- data: result.data ?? null,
1155
+ data: null,
458
1156
  message: result.message ?? "",
459
1157
  status: result.status ?? 0,
460
1158
  headers: result.headers ?? {}
@@ -470,6 +1168,10 @@ var setDefaultObjectProp = (obj, key, value) => {
470
1168
  (0, import_utils4.set)(obj, key, value);
471
1169
  }
472
1170
  };
1171
+ var ensureListResultCount = (result) => {
1172
+ result.totalCount ??= 0;
1173
+ return result;
1174
+ };
473
1175
  var createResponseHandler = (onSuccess, onFailure, throwOnError) => {
474
1176
  const successHandler = onSuccess ?? import_utils4.noop;
475
1177
  const failureHandler = onFailure ?? import_utils4.noop;
@@ -486,6 +1188,7 @@ var createResponseHandler = (onSuccess, onFailure, throwOnError) => {
486
1188
  };
487
1189
  };
488
1190
  function processListResult(result, { includeCount, includeExtraHeaders }, wrapItem) {
1191
+ ensureListResultCount(result);
489
1192
  const wrappedRows = (0, import_utils4.get)(result, "raw.data");
490
1193
  const wrappedTotalCount = (0, import_utils4.get)(result, "raw.meta.totalCount");
491
1194
  if (Array.isArray(wrappedRows)) {
@@ -518,11 +1221,40 @@ function processListResult(result, { includeCount, includeExtraHeaders }, wrapIt
518
1221
  }
519
1222
 
520
1223
  // src/lazy-promise.ts
1224
+ var STARTED_KEY = /* @__PURE__ */ Symbol("started");
1225
+ var executionClaims = /* @__PURE__ */ new WeakMap();
1226
+ var claimLazyRequest = (request, mode, owner) => {
1227
+ const claim = executionClaims.get(request);
1228
+ if (!claim) {
1229
+ executionClaims.set(request, { mode, owner });
1230
+ return;
1231
+ }
1232
+ if (mode === "grouped") {
1233
+ if (claim.mode === "direct") {
1234
+ throw new Error(
1235
+ "Cannot group a request that has already started execution; group() must be called before await/then/catch/finally/exec on each input"
1236
+ );
1237
+ }
1238
+ throw new Error("Cannot group a request already claimed for grouped execution");
1239
+ }
1240
+ throw new Error("Cannot execute a request already claimed for grouped execution");
1241
+ };
1242
+ var releaseLazyRequestClaim = (request, owner) => {
1243
+ const claim = executionClaims.get(request);
1244
+ if (claim?.mode === "grouped" && claim.owner === owner) {
1245
+ executionClaims.delete(request);
1246
+ }
1247
+ };
521
1248
  var wrapLazyPromise = (promiseFn, meta) => {
522
1249
  let promise;
523
1250
  const exec = () => {
524
1251
  if (!promise) {
525
- promise = promiseFn();
1252
+ try {
1253
+ claimLazyRequest(prom, "direct");
1254
+ promise = Promise.resolve().then(promiseFn);
1255
+ } catch (error) {
1256
+ promise = Promise.reject(error);
1257
+ }
526
1258
  }
527
1259
  return promise;
528
1260
  };
@@ -545,32 +1277,62 @@ var wrapLazyPromise = (promiseFn, meta) => {
545
1277
  value: "Promise",
546
1278
  writable: false,
547
1279
  enumerable: false,
548
- configurable: true
1280
+ configurable: false
1281
+ });
1282
+ Object.defineProperty(prom, STARTED_KEY, {
1283
+ get: () => executionClaims.has(prom),
1284
+ enumerable: false,
1285
+ configurable: false
549
1286
  });
550
- Object.assign(prom, meta);
1287
+ if (meta != null) {
1288
+ const metaKeys = Object.keys(meta);
1289
+ for (let i = 0; i < metaKeys.length; i++) {
1290
+ const key = metaKeys[i];
1291
+ Object.defineProperty(prom, key, {
1292
+ value: meta[key],
1293
+ enumerable: false,
1294
+ writable: false,
1295
+ configurable: false
1296
+ });
1297
+ }
1298
+ }
551
1299
  return prom;
552
1300
  };
553
1301
 
554
1302
  // src/services/request.ts
555
1303
  function makeRequest(execute, meta) {
556
- return wrapLazyPromise(execute, meta);
1304
+ const effectiveMeta = {
1305
+ ...meta,
1306
+ __throwOnError: meta.__service?.resolveThrowOnError(meta.__throwOnError) ?? Boolean(meta.__throwOnError)
1307
+ };
1308
+ return wrapLazyPromise(execute, effectiveMeta);
557
1309
  }
558
1310
 
559
1311
  // src/services/sub-ops.ts
560
- var import_axios3 = require("axios");
1312
+ var import_axios5 = require("axios");
1313
+ var toArray = (value) => Array.isArray(value) ? value : value == null ? [] : [value];
1314
+ var ensureSubdocumentListCount = (result) => {
1315
+ result.count ??= 0;
1316
+ return result;
1317
+ };
561
1318
  function buildSubDocumentOps(ctx, id, sub) {
562
- const { axios: axios2, basePath, modelName, queryPath, handleSuccess, handleError, _handleCallbacks, parentService } = ctx;
563
- const asS = parentService;
1319
+ const { axios: axios3, basePath, modelName, queryPath, handleSuccess, handleError, _handleCallbacks, parentService } = ctx;
564
1320
  return {
565
1321
  list: (axiosRequestConfig) => {
566
1322
  const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
567
1323
  return makeRequest(
568
- () => axios2.get(`${basePath}/${id}/${sub}`, (0, import_axios3.mergeConfig)(reqConfig, { params: {} })).then(handleSuccess).then((result) => {
569
- result.totalCount = Array.isArray(result.raw) ? result.raw.length : 0;
570
- result.data = Array.isArray(result.raw) ? result.raw.map((item) => Model.create(item, asS)) : [];
1324
+ () => axios3.get(
1325
+ `${basePath}/${encodePathSegment(id)}/${encodePathSegment(sub)}`,
1326
+ (0, import_axios5.mergeConfig)(reqConfig, { params: {} })
1327
+ ).then(handleSuccess).then((result) => {
1328
+ const rawArray = toArray(result.raw);
1329
+ result.raw = rawArray;
1330
+ result.count = rawArray.length;
1331
+ result.data = rawArray;
571
1332
  return result;
572
- }).catch(handleError).then((res) => _handleCallbacks(res, throwOnError)),
1333
+ }).catch(handleError).then(ensureSubdocumentListCount).then((res) => _handleCallbacks(res, throwOnError)),
573
1334
  {
1335
+ __throwOnError: throwOnError,
574
1336
  __op: "listSub",
575
1337
  __query: {
576
1338
  target: "model",
@@ -592,14 +1354,21 @@ function buildSubDocumentOps(ctx, id, sub) {
592
1354
  const select = args?.select;
593
1355
  const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
594
1356
  return makeRequest(
595
- () => axios2.post(`${basePath}/${id}/${sub}/${queryPath}`, { filter, select }, reqConfig).then(handleSuccess).then((result) => {
596
- result.totalCount = Array.isArray(result.raw) ? result.raw.length : 0;
597
- result.data = Array.isArray(result.raw) ? result.raw.map((item) => Model.create(item, asS)) : [];
1357
+ () => axios3.post(
1358
+ `${basePath}/${encodePathSegment(id)}/${encodePathSegment(sub)}/${queryPath}`,
1359
+ { filter, select },
1360
+ reqConfig
1361
+ ).then(handleSuccess).then((result) => {
1362
+ const rawArray = toArray(result.raw);
1363
+ result.raw = rawArray;
1364
+ result.count = rawArray.length;
1365
+ result.data = rawArray;
598
1366
  return result;
599
- }).catch(handleError).then(
1367
+ }).catch(handleError).then(ensureSubdocumentListCount).then(
600
1368
  (res) => _handleCallbacks(res, throwOnError)
601
1369
  ),
602
1370
  {
1371
+ __throwOnError: throwOnError,
603
1372
  __op: "listAdvancedSub",
604
1373
  __query: {
605
1374
  target: "model",
@@ -620,11 +1389,15 @@ function buildSubDocumentOps(ctx, id, sub) {
620
1389
  read: (subId, axiosRequestConfig) => {
621
1390
  const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
622
1391
  return makeRequest(
623
- () => axios2.get(`${basePath}/${id}/${sub}/${subId}`, (0, import_axios3.mergeConfig)(reqConfig, { params: {} })).then(handleSuccess).then((result) => {
624
- result.data = result.success ? Model.create(result.raw, asS) : null;
1392
+ () => axios3.get(
1393
+ `${basePath}/${encodePathSegment(id)}/${encodePathSegment(sub)}/${encodePathSegment(subId)}`,
1394
+ (0, import_axios5.mergeConfig)(reqConfig, { params: {} })
1395
+ ).then(handleSuccess).then((result) => {
1396
+ result.data = result.success ? result.raw : null;
625
1397
  return result;
626
1398
  }).catch(handleError).then((res) => _handleCallbacks(res, throwOnError)),
627
1399
  {
1400
+ __throwOnError: throwOnError,
628
1401
  __op: "readSub",
629
1402
  __query: {
630
1403
  target: "model",
@@ -646,13 +1419,18 @@ function buildSubDocumentOps(ctx, id, sub) {
646
1419
  const { select, populate } = args ?? {};
647
1420
  const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
648
1421
  return makeRequest(
649
- () => axios2.post(`${basePath}/${id}/${sub}/${subId}/${queryPath}`, { select, populate }, reqConfig).then(handleSuccess).then((result) => {
650
- result.data = result.success ? Model.create(result.raw, asS) : null;
1422
+ () => axios3.post(
1423
+ `${basePath}/${encodePathSegment(id)}/${encodePathSegment(sub)}/${encodePathSegment(subId)}/${queryPath}`,
1424
+ { select, populate },
1425
+ reqConfig
1426
+ ).then(handleSuccess).then((result) => {
1427
+ result.data = result.success ? result.raw : null;
651
1428
  return result;
652
1429
  }).catch(handleError).then(
653
1430
  (res) => _handleCallbacks(res, throwOnError)
654
1431
  ),
655
1432
  {
1433
+ __throwOnError: throwOnError,
656
1434
  __op: "readAdvancedSub",
657
1435
  __query: {
658
1436
  target: "model",
@@ -671,13 +1449,18 @@ function buildSubDocumentOps(ctx, id, sub) {
671
1449
  );
672
1450
  },
673
1451
  update: (subId, data, axiosRequestConfig) => {
674
- const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
1452
+ const { throwOnError, ...reqConfig } = cloneConfigWithCacheBypass(axiosRequestConfig ?? {});
675
1453
  return makeRequest(
676
- () => axios2.patch(`${basePath}/${id}/${sub}/${subId}`, data, (0, import_axios3.mergeConfig)(reqConfig, { params: {} })).then(handleSuccess).then((result) => {
677
- result.data = result.success ? Model.create(result.raw, asS) : null;
1454
+ () => axios3.patch(
1455
+ `${basePath}/${encodePathSegment(id)}/${encodePathSegment(sub)}/${encodePathSegment(subId)}`,
1456
+ data,
1457
+ (0, import_axios5.mergeConfig)(reqConfig, { params: {} })
1458
+ ).then(handleSuccess).then((result) => {
1459
+ result.data = result.success ? result.raw : null;
678
1460
  return result;
679
1461
  }).catch(handleError).then((res) => _handleCallbacks(res, throwOnError)),
680
1462
  {
1463
+ __throwOnError: throwOnError,
681
1464
  __op: "updateSub",
682
1465
  __query: {
683
1466
  target: "model",
@@ -696,13 +1479,21 @@ function buildSubDocumentOps(ctx, id, sub) {
696
1479
  );
697
1480
  },
698
1481
  bulkUpdate: (data, axiosRequestConfig) => {
699
- const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
1482
+ const { throwOnError, ...reqConfig } = cloneConfigWithCacheBypass(axiosRequestConfig ?? {});
700
1483
  return makeRequest(
701
- () => axios2.patch(`${basePath}/${id}/${sub}`, data, (0, import_axios3.mergeConfig)(reqConfig, { params: {} })).then(handleSuccess).then((result) => {
702
- result.data = Array.isArray(result.raw) ? result.raw.map((item) => Model.create(item, asS)) : [];
1484
+ () => axios3.patch(
1485
+ `${basePath}/${encodePathSegment(id)}/${encodePathSegment(sub)}`,
1486
+ data,
1487
+ (0, import_axios5.mergeConfig)(reqConfig, { params: {} })
1488
+ ).then(handleSuccess).then((result) => {
1489
+ const rawArray = toArray(result.raw);
1490
+ result.raw = rawArray;
1491
+ result.count = rawArray.length;
1492
+ result.data = rawArray;
703
1493
  return result;
704
- }).catch(handleError).then((res) => _handleCallbacks(res, throwOnError)),
1494
+ }).catch(handleError).then(ensureSubdocumentListCount).then((res) => _handleCallbacks(res, throwOnError)),
705
1495
  {
1496
+ __throwOnError: throwOnError,
706
1497
  __op: "bulkUpdateSub",
707
1498
  __query: {
708
1499
  target: "model",
@@ -720,30 +1511,42 @@ function buildSubDocumentOps(ctx, id, sub) {
720
1511
  );
721
1512
  },
722
1513
  create: (data, axiosRequestConfig) => {
723
- const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
1514
+ const { throwOnError, ...reqConfig } = cloneConfigWithCacheBypass(axiosRequestConfig ?? {});
724
1515
  return makeRequest(
725
- () => axios2.post(`${basePath}/${id}/${sub}`, data, (0, import_axios3.mergeConfig)(reqConfig, { params: {} })).then(handleSuccess).then((result) => {
726
- result.data = result.success ? Model.create(result.raw, asS) : null;
1516
+ () => axios3.post(
1517
+ `${basePath}/${encodePathSegment(id)}/${encodePathSegment(sub)}`,
1518
+ data,
1519
+ (0, import_axios5.mergeConfig)(reqConfig, { params: {} })
1520
+ ).then(handleSuccess).then((result) => {
1521
+ const rawArray = toArray(result.raw);
1522
+ result.raw = rawArray;
1523
+ result.count = rawArray.length;
1524
+ result.data = rawArray;
727
1525
  return result;
728
- }).catch(handleError).then((res) => _handleCallbacks(res, throwOnError)),
1526
+ }).catch(handleError).then(ensureSubdocumentListCount).then((res) => _handleCallbacks(res, throwOnError)),
729
1527
  {
1528
+ __throwOnError: throwOnError,
730
1529
  __op: "createSub",
731
- __query: { target: "model", name: modelName, model: modelName, op: "subCreate", id, sub, data, options: {} },
1530
+ __query: { target: "model", name: modelName, op: "subCreate", id, sub, data, options: {} },
732
1531
  __requestConfig: reqConfig,
733
1532
  __service: parentService
734
1533
  }
735
1534
  );
736
1535
  },
737
1536
  delete: (subId, axiosRequestConfig) => {
738
- const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
1537
+ const { throwOnError, ...reqConfig } = cloneConfigWithCacheBypass(axiosRequestConfig ?? {});
739
1538
  return makeRequest(
740
- () => axios2.delete(`${basePath}/${id}/${sub}/${subId}`, reqConfig).then(handleSuccess).then((result) => {
741
- result.data = result.raw;
1539
+ () => axios3.delete(
1540
+ `${basePath}/${encodePathSegment(id)}/${encodePathSegment(sub)}/${encodePathSegment(subId)}`,
1541
+ reqConfig
1542
+ ).then(handleSuccess).then((result) => {
1543
+ if (result.success) result.data = result.raw;
742
1544
  return result;
743
1545
  }).catch(handleError).then((res) => _handleCallbacks(res, throwOnError)),
744
1546
  {
1547
+ __throwOnError: throwOnError,
745
1548
  __op: "deleteSub",
746
- __query: { target: "model", name: modelName, model: modelName, op: "subDelete", id, sub, subId },
1549
+ __query: { target: "model", name: modelName, op: "subDelete", id, sub, subId },
747
1550
  __requestConfig: reqConfig,
748
1551
  __service: parentService
749
1552
  }
@@ -754,8 +1557,8 @@ function buildSubDocumentOps(ctx, id, sub) {
754
1557
 
755
1558
  // src/services/model-service.ts
756
1559
  var ModelService = class extends Service {
757
- constructor({ axios: axios2, modelName, basePath, queryPath, mutationPath, onSuccess, onFailure, throwOnError }, defaults) {
758
- super(axios2, basePath);
1560
+ constructor({ axios: axios3, modelName, basePath, queryPath, mutationPath, onSuccess, onFailure, throwOnError }, defaults) {
1561
+ super(axios3, basePath, throwOnError);
759
1562
  this._modelName = modelName;
760
1563
  this._queryPath = queryPath;
761
1564
  this._mutationPath = mutationPath;
@@ -803,7 +1606,7 @@ var ModelService = class extends Service {
803
1606
  return makeRequest(
804
1607
  () => this._axios.get(
805
1608
  this._basePath,
806
- (0, import_axios4.mergeConfig)(reqConfig, {
1609
+ (0, import_axios6.mergeConfig)(reqConfig, {
807
1610
  params: {
808
1611
  skip,
809
1612
  limit,
@@ -819,10 +1622,15 @@ var ModelService = class extends Service {
819
1622
  return processListResult(
820
1623
  result,
821
1624
  { includeCount, includeExtraHeaders },
822
- (item) => Model.create(item, this)
1625
+ // ARC-21: list items come from existing documents, so mark
1626
+ // them as `_fromExisting=true` even if the server response
1627
+ // (or a custom adapter) strips `_id` — a save on such an item
1628
+ // must throw rather than silently re-create.
1629
+ (item) => Model.create(item, this, void 0, true)
823
1630
  );
824
- }).catch(this.handleError).then((res) => this._handleCallbacks(res, throwOnError)),
1631
+ }).catch(this.handleError).then(ensureListResultCount).then((res) => this._handleCallbacks(res, throwOnError)),
825
1632
  {
1633
+ __throwOnError: throwOnError,
826
1634
  __op: "list",
827
1635
  __query: {
828
1636
  target: "model",
@@ -831,7 +1639,7 @@ var ModelService = class extends Service {
831
1639
  op: "list",
832
1640
  filter: {},
833
1641
  args: { skip, limit, page, pageSize },
834
- options: { skim, includePermissions, includeCount, includeExtraHeaders },
1642
+ options: { skim, includePermissions, includeCount },
835
1643
  sqOptions: sq
836
1644
  },
837
1645
  __requestConfig: reqConfig,
@@ -884,12 +1692,18 @@ var ModelService = class extends Service {
884
1692
  return processListResult(
885
1693
  result,
886
1694
  { includeCount, includeExtraHeaders },
887
- (item) => Model.create(item, this)
1695
+ (item) => (
1696
+ // ARC-21: listAdvanced items come from existing documents;
1697
+ // mark `_fromExisting=true` so a save cannot silently re-create
1698
+ // when a server response shape drops `_id`.
1699
+ Model.create(item, this, void 0, true)
1700
+ )
888
1701
  );
889
- }).catch(this.handleError).then(
1702
+ }).catch(this.handleError).then(ensureListResultCount).then(
890
1703
  (res) => this._handleCallbacks(res, throwOnError)
891
1704
  ),
892
1705
  {
1706
+ __throwOnError: throwOnError,
893
1707
  __op: "listAdvanced",
894
1708
  __query: {
895
1709
  target: "model",
@@ -898,7 +1712,7 @@ var ModelService = class extends Service {
898
1712
  op: "list",
899
1713
  filter: _filter,
900
1714
  args: { select, sort, populate, include, skip, limit, page, pageSize, tasks },
901
- options: { skim, includePermissions, includeCount, includeExtraHeaders, populateAccess },
1715
+ options: { skim, includePermissions, includeCount, populateAccess },
902
1716
  sqOptions: sq
903
1717
  },
904
1718
  __requestConfig: reqConfig,
@@ -908,14 +1722,25 @@ var ModelService = class extends Service {
908
1722
  }
909
1723
  create(data, options, axiosRequestConfig) {
910
1724
  const { includePermissions = this._defaults.createOptions.includePermissions ?? true } = options ?? {};
911
- const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
912
- (0, import_utils5.set)(reqConfig, `headers.${CACHE_HEADER}`, "false");
1725
+ const { throwOnError, ...reqConfig } = cloneConfigWithCacheBypass(axiosRequestConfig ?? {});
1726
+ const bulk = Array.isArray(data);
913
1727
  return makeRequest(
914
- () => this._axios.post(this._basePath, data, (0, import_axios4.mergeConfig)(reqConfig, { params: { include_permissions: includePermissions } })).then(this.handleSuccess).then((result) => {
915
- result.data = result.success ? Model.create(result.raw, this) : null;
1728
+ () => this._axios.post(this._basePath, data, (0, import_axios6.mergeConfig)(reqConfig, { params: { include_permissions: includePermissions } })).then(this.handleSuccess).then((result) => {
1729
+ if (result.success) {
1730
+ if (bulk) {
1731
+ const rows = Array.isArray(result.raw) ? result.raw : [result.raw];
1732
+ result.raw = rows;
1733
+ result.data = rows.map((row) => Model.create(row, this, void 0, true));
1734
+ } else {
1735
+ result.data = Model.create(result.raw, this, void 0, true);
1736
+ }
1737
+ }
916
1738
  return result;
917
- }).catch(this.handleError).then((res) => this._handleCallbacks(res, throwOnError)),
1739
+ }).catch(this.handleError).then(
1740
+ (res) => this._handleCallbacks(res, throwOnError)
1741
+ ),
918
1742
  {
1743
+ __throwOnError: throwOnError,
919
1744
  __op: "create",
920
1745
  __query: {
921
1746
  target: "model",
@@ -937,20 +1762,27 @@ var ModelService = class extends Service {
937
1762
  includePermissions = this._defaults.createAdvancedOptions.includePermissions ?? true,
938
1763
  populateAccess = this._defaults.createAdvancedOptions.populateAccess
939
1764
  } = options ?? {};
940
- const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
941
- (0, import_utils5.set)(reqConfig, `headers.${CACHE_HEADER}`, "false");
1765
+ const { throwOnError, ...reqConfig } = cloneConfigWithCacheBypass(axiosRequestConfig ?? {});
1766
+ const bulk = Array.isArray(data);
942
1767
  return makeRequest(
943
1768
  () => this._axios.post(
944
1769
  `${this._basePath}/${this._mutationPath}`,
945
1770
  { data, select, populate, tasks, options: { includePermissions, populateAccess } },
946
1771
  reqConfig
947
1772
  ).then(this.handleSuccess).then((result) => {
948
- result.data = result.success ? Model.create(result.raw, this) : null;
1773
+ if (result.success) {
1774
+ if (bulk) {
1775
+ const rows = Array.isArray(result.raw) ? result.raw : [result.raw];
1776
+ result.raw = rows;
1777
+ result.data = rows.map((row) => Model.create(row, this, void 0, true));
1778
+ } else {
1779
+ result.data = Model.create(result.raw, this, void 0, true);
1780
+ }
1781
+ }
949
1782
  return result;
950
- }).catch(this.handleError).then(
951
- (res) => this._handleCallbacks(res, throwOnError)
952
- ),
1783
+ }).catch(this.handleError).then((res) => this._handleCallbacks(res, throwOnError)),
953
1784
  {
1785
+ __throwOnError: throwOnError,
954
1786
  __op: "createAdvanced",
955
1787
  __query: {
956
1788
  target: "model",
@@ -967,15 +1799,24 @@ var ModelService = class extends Service {
967
1799
  );
968
1800
  }
969
1801
  upsert(data, options, axiosRequestConfig) {
970
- const { returningAll = this._defaults.upsertOptions.returningAll ?? true } = options ?? {};
971
- const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
972
- (0, import_utils5.set)(reqConfig, `headers.${CACHE_HEADER}`, "false");
1802
+ const {
1803
+ returningAll = this._defaults.upsertOptions.returningAll ?? true,
1804
+ includePermissions = this._defaults.upsertOptions.includePermissions ?? true
1805
+ } = options ?? {};
1806
+ const { throwOnError, ...reqConfig } = cloneConfigWithCacheBypass(axiosRequestConfig ?? {});
973
1807
  return makeRequest(
974
- () => this._axios.put(this._basePath, data, (0, import_axios4.mergeConfig)(reqConfig, { params: { returning_all: returningAll } })).then(this.handleSuccess).then((result) => {
975
- result.data = result.success ? Model.create(result.raw, this) : null;
1808
+ () => this._axios.put(
1809
+ this._basePath,
1810
+ data,
1811
+ (0, import_axios6.mergeConfig)(reqConfig, {
1812
+ params: { returning_all: returningAll, include_permissions: includePermissions }
1813
+ })
1814
+ ).then(this.handleSuccess).then((result) => {
1815
+ result.data = result.success ? Model.create(result.raw, this, void 0, true) : null;
976
1816
  return result;
977
1817
  }).catch(this.handleError).then((res) => this._handleCallbacks(res, throwOnError)),
978
1818
  {
1819
+ __throwOnError: throwOnError,
979
1820
  __op: "upsert",
980
1821
  __query: {
981
1822
  target: "model",
@@ -983,7 +1824,7 @@ var ModelService = class extends Service {
983
1824
  model: this._modelName,
984
1825
  op: "upsert",
985
1826
  data,
986
- options: { returningAll }
1827
+ options: { returningAll, includePermissions }
987
1828
  },
988
1829
  __requestConfig: reqConfig,
989
1830
  __service: this
@@ -998,8 +1839,7 @@ var ModelService = class extends Service {
998
1839
  includePermissions = this._defaults.upsertAdvancedOptions.includePermissions ?? true,
999
1840
  populateAccess = this._defaults.upsertAdvancedOptions.populateAccess
1000
1841
  } = options ?? {};
1001
- const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
1002
- (0, import_utils5.set)(reqConfig, `headers.${CACHE_HEADER}`, "false");
1842
+ const { throwOnError, ...reqConfig } = cloneConfigWithCacheBypass(axiosRequestConfig ?? {});
1003
1843
  return makeRequest(
1004
1844
  () => this._axios.put(
1005
1845
  `${this._basePath}/${this._mutationPath}`,
@@ -1012,12 +1852,13 @@ var ModelService = class extends Service {
1012
1852
  },
1013
1853
  reqConfig
1014
1854
  ).then(this.handleSuccess).then((result) => {
1015
- result.data = result.success ? Model.create(result.raw, this) : null;
1855
+ result.data = result.success ? Model.create(result.raw, this, void 0, true) : null;
1016
1856
  return result;
1017
1857
  }).catch(this.handleError).then(
1018
1858
  (res) => this._handleCallbacks(res, throwOnError)
1019
1859
  ),
1020
1860
  {
1861
+ __throwOnError: throwOnError,
1021
1862
  __op: "upsertAdvanced",
1022
1863
  __query: {
1023
1864
  target: "model",
@@ -1034,14 +1875,14 @@ var ModelService = class extends Service {
1034
1875
  );
1035
1876
  }
1036
1877
  delete(identifier, axiosRequestConfig) {
1037
- const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
1038
- (0, import_utils5.set)(reqConfig, `headers.${CACHE_HEADER}`, "false");
1878
+ const { throwOnError, ...reqConfig } = cloneConfigWithCacheBypass(axiosRequestConfig ?? {});
1039
1879
  return makeRequest(
1040
- () => this._axios.delete(`${this._basePath}/${identifier}`, reqConfig).then(this.handleSuccess).then((result) => {
1041
- result.data = result.raw;
1880
+ () => this._axios.delete(`${this._basePath}/${encodePathSegment(identifier)}`, reqConfig).then(this.handleSuccess).then((result) => {
1881
+ if (result.success) result.data = result.raw;
1042
1882
  return result;
1043
1883
  }).catch(this.handleError).then((res) => this._handleCallbacks(res, throwOnError)),
1044
1884
  {
1885
+ __throwOnError: throwOnError,
1045
1886
  __op: "delete",
1046
1887
  __query: {
1047
1888
  target: "model",
@@ -1056,15 +1897,17 @@ var ModelService = class extends Service {
1056
1897
  );
1057
1898
  }
1058
1899
  new(axiosRequestConfig) {
1059
- const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
1060
- (0, import_utils5.set)(reqConfig, `headers.${CACHE_HEADER}`, "false");
1900
+ const { throwOnError, ...reqConfig } = cloneConfigWithCacheBypass(axiosRequestConfig ?? {});
1061
1901
  return makeRequest(
1062
1902
  () => this._axios.get(`${this._basePath}/new`, reqConfig).then(this.handleSuccess).then((result) => {
1063
- delete result.raw._id;
1064
- result.data = result.success ? Model.create(result.raw, this) : null;
1903
+ if (result.success) {
1904
+ delete result.raw._id;
1905
+ result.data = Model.create(result.raw, this);
1906
+ }
1065
1907
  return result;
1066
1908
  }).catch(this.handleError).then((res) => this._handleCallbacks(res, throwOnError)),
1067
1909
  {
1910
+ __throwOnError: throwOnError,
1068
1911
  __op: "new",
1069
1912
  __query: {
1070
1913
  target: "model",
@@ -1080,11 +1923,12 @@ var ModelService = class extends Service {
1080
1923
  distinct(field, axiosRequestConfig) {
1081
1924
  const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
1082
1925
  return makeRequest(
1083
- () => this._axios.get(`${this._basePath}/distinct/${field}`, reqConfig).then(this.handleSuccess).then((result) => {
1084
- result.data = result.raw;
1926
+ () => this._axios.get(`${this._basePath}/distinct/${encodePathSegment(field)}`, reqConfig).then(this.handleSuccess).then((result) => {
1927
+ if (result.success) result.data = result.raw;
1085
1928
  return result;
1086
1929
  }).catch(this.handleError).then((res) => this._handleCallbacks(res, throwOnError)),
1087
1930
  {
1931
+ __throwOnError: throwOnError,
1088
1932
  __op: "distinct",
1089
1933
  __query: {
1090
1934
  target: "model",
@@ -1101,11 +1945,12 @@ var ModelService = class extends Service {
1101
1945
  distinctAdvanced(field, conditions, axiosRequestConfig) {
1102
1946
  const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
1103
1947
  return makeRequest(
1104
- () => this._axios.post(`${this._basePath}/distinct/${field}`, conditions, reqConfig).then(this.handleSuccess).then((result) => {
1105
- result.data = result.raw;
1948
+ () => this._axios.post(`${this._basePath}/distinct/${encodePathSegment(field)}`, { filter: conditions }, reqConfig).then(this.handleSuccess).then((result) => {
1949
+ if (result.success) result.data = result.raw;
1106
1950
  return result;
1107
1951
  }).catch(this.handleError).then((res) => this._handleCallbacks(res, throwOnError)),
1108
1952
  {
1953
+ __throwOnError: throwOnError,
1109
1954
  __op: "distinctAdvanced",
1110
1955
  __query: {
1111
1956
  target: "model",
@@ -1124,10 +1969,11 @@ var ModelService = class extends Service {
1124
1969
  const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
1125
1970
  return makeRequest(
1126
1971
  () => this._axios.get(`${this._basePath}/count`, reqConfig).then(this.handleSuccess).then((result) => {
1127
- result.data = result.raw;
1972
+ if (result.success) result.data = result.raw;
1128
1973
  return result;
1129
1974
  }).catch(this.handleError).then((res) => this._handleCallbacks(res, throwOnError)),
1130
1975
  {
1976
+ __throwOnError: throwOnError,
1131
1977
  __op: "count",
1132
1978
  __query: {
1133
1979
  target: "model",
@@ -1140,23 +1986,22 @@ var ModelService = class extends Service {
1140
1986
  }
1141
1987
  );
1142
1988
  }
1143
- countAdvanced(filter, args, axiosRequestConfig) {
1144
- const { access } = args ?? {};
1989
+ countAdvanced(filter, axiosRequestConfig) {
1145
1990
  const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
1146
1991
  return makeRequest(
1147
- () => this._axios.post(`${this._basePath}/count`, { filter, options: { access } }, reqConfig).then(this.handleSuccess).then((result) => {
1148
- result.data = result.raw;
1992
+ () => this._axios.post(`${this._basePath}/count`, { filter }, reqConfig).then(this.handleSuccess).then((result) => {
1993
+ if (result.success) result.data = result.raw;
1149
1994
  return result;
1150
1995
  }).catch(this.handleError).then((res) => this._handleCallbacks(res, throwOnError)),
1151
1996
  {
1997
+ __throwOnError: throwOnError,
1152
1998
  __op: "countAdvanced",
1153
1999
  __query: {
1154
2000
  target: "model",
1155
2001
  name: this._modelName,
1156
2002
  model: this._modelName,
1157
2003
  op: "count",
1158
- filter,
1159
- options: { access }
2004
+ filter
1160
2005
  },
1161
2006
  __requestConfig: reqConfig,
1162
2007
  __service: this
@@ -1177,15 +2022,16 @@ var ModelService = class extends Service {
1177
2022
  reqConfig.headers = this.updateHeaders(reqConfig.headers, { ignoreCache });
1178
2023
  return makeRequest(
1179
2024
  () => this._axios.get(
1180
- `${this._basePath}/${identifier}`,
1181
- (0, import_axios4.mergeConfig)(reqConfig, {
2025
+ `${this._basePath}/${encodePathSegment(identifier)}`,
2026
+ (0, import_axios6.mergeConfig)(reqConfig, {
1182
2027
  params: { include_permissions: includePermissions, try_list: tryList }
1183
2028
  })
1184
2029
  ).then(this.handleSuccess).then((result) => {
1185
- result.data = result.success ? Model.create(result.raw, this) : null;
2030
+ result.data = result.success ? Model.create(result.raw, this, identifier, true) : null;
1186
2031
  return result;
1187
2032
  }).catch(this.handleError).then((res) => this._handleCallbacks(res, throwOnError)),
1188
2033
  {
2034
+ __throwOnError: throwOnError,
1189
2035
  __op: "read",
1190
2036
  __query: {
1191
2037
  target: "model",
@@ -1221,7 +2067,7 @@ var ModelService = class extends Service {
1221
2067
  reqConfig.headers = this.updateHeaders(reqConfig.headers, { ignoreCache });
1222
2068
  return makeRequest(
1223
2069
  () => this._axios.post(
1224
- `${this._basePath}/${this._queryPath}/${identifier}`,
2070
+ `${this._basePath}/${this._queryPath}/${encodePathSegment(identifier)}`,
1225
2071
  {
1226
2072
  select,
1227
2073
  populate,
@@ -1231,12 +2077,13 @@ var ModelService = class extends Service {
1231
2077
  },
1232
2078
  reqConfig
1233
2079
  ).then(this.handleSuccess).then((result) => {
1234
- result.data = result.success ? Model.create(result.raw, this) : null;
2080
+ result.data = result.success ? Model.create(result.raw, this, identifier, true) : null;
1235
2081
  return result;
1236
2082
  }).catch(this.handleError).then(
1237
2083
  (res) => this._handleCallbacks(res, throwOnError)
1238
2084
  ),
1239
2085
  {
2086
+ __throwOnError: throwOnError,
1240
2087
  __op: "readAdvanced",
1241
2088
  __query: {
1242
2089
  target: "model",
@@ -1286,12 +2133,13 @@ var ModelService = class extends Service {
1286
2133
  },
1287
2134
  reqConfig
1288
2135
  ).then(this.handleSuccess).then((result) => {
1289
- result.data = result.success ? Model.create(result.raw, this) : null;
2136
+ result.data = result.success ? Model.create(result.raw, this, void 0, true) : null;
1290
2137
  return result;
1291
2138
  }).catch(this.handleError).then(
1292
2139
  (res) => this._handleCallbacks(res, throwOnError)
1293
2140
  ),
1294
2141
  {
2142
+ __throwOnError: throwOnError,
1295
2143
  __op: "readAdvancedFilter",
1296
2144
  __query: {
1297
2145
  target: "model",
@@ -1309,19 +2157,24 @@ var ModelService = class extends Service {
1309
2157
  );
1310
2158
  }
1311
2159
  update(identifier, data, options, axiosRequestConfig) {
1312
- const { returningAll = this._defaults.updateOptions.returningAll ?? true } = options ?? {};
1313
- const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
1314
- (0, import_utils5.set)(reqConfig, `headers.${CACHE_HEADER}`, "false");
2160
+ const {
2161
+ returningAll = this._defaults.updateOptions.returningAll ?? true,
2162
+ includePermissions = this._defaults.updateOptions.includePermissions ?? true
2163
+ } = options ?? {};
2164
+ const { throwOnError, ...reqConfig } = cloneConfigWithCacheBypass(axiosRequestConfig ?? {});
1315
2165
  return makeRequest(
1316
2166
  () => this._axios.patch(
1317
- `${this._basePath}/${identifier}`,
2167
+ `${this._basePath}/${encodePathSegment(identifier)}`,
1318
2168
  data,
1319
- (0, import_axios4.mergeConfig)(reqConfig, { params: { returning_all: returningAll } })
2169
+ (0, import_axios6.mergeConfig)(reqConfig, {
2170
+ params: { returning_all: returningAll, include_permissions: includePermissions }
2171
+ })
1320
2172
  ).then(this.handleSuccess).then((result) => {
1321
- result.data = result.success ? Model.create(result.raw, this) : null;
2173
+ result.data = result.success ? Model.create(result.raw, this, void 0, true) : null;
1322
2174
  return result;
1323
2175
  }).catch(this.handleError).then((res) => this._handleCallbacks(res, throwOnError)),
1324
2176
  {
2177
+ __throwOnError: throwOnError,
1325
2178
  __op: "update",
1326
2179
  __query: {
1327
2180
  target: "model",
@@ -1330,7 +2183,7 @@ var ModelService = class extends Service {
1330
2183
  op: "update",
1331
2184
  id: identifier,
1332
2185
  data,
1333
- options: { returningAll }
2186
+ options: { returningAll, includePermissions }
1334
2187
  },
1335
2188
  __requestConfig: reqConfig,
1336
2189
  __service: this
@@ -1345,11 +2198,10 @@ var ModelService = class extends Service {
1345
2198
  includePermissions = this._defaults.updateAdvancedOptions.includePermissions ?? true,
1346
2199
  populateAccess = this._defaults.updateAdvancedOptions.populateAccess
1347
2200
  } = options ?? {};
1348
- const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
1349
- (0, import_utils5.set)(reqConfig, `headers.${CACHE_HEADER}`, "false");
2201
+ const { throwOnError, ...reqConfig } = cloneConfigWithCacheBypass(axiosRequestConfig ?? {});
1350
2202
  return makeRequest(
1351
2203
  () => this._axios.patch(
1352
- `${this._basePath}/${this._mutationPath}/${identifier}`,
2204
+ `${this._basePath}/${this._mutationPath}/${encodePathSegment(identifier)}`,
1353
2205
  {
1354
2206
  data,
1355
2207
  select,
@@ -1359,12 +2211,13 @@ var ModelService = class extends Service {
1359
2211
  },
1360
2212
  reqConfig
1361
2213
  ).then(this.handleSuccess).then((result) => {
1362
- result.data = result.success ? Model.create(result.raw, this) : null;
2214
+ result.data = result.success ? Model.create(result.raw, this, void 0, true) : null;
1363
2215
  return result;
1364
2216
  }).catch(this.handleError).then(
1365
2217
  (res) => this._handleCallbacks(res, throwOnError)
1366
2218
  ),
1367
2219
  {
2220
+ __throwOnError: throwOnError,
1368
2221
  __op: "updateAdvanced",
1369
2222
  __query: {
1370
2223
  target: "model",
@@ -1411,10 +2264,10 @@ var ModelService = class extends Service {
1411
2264
  };
1412
2265
 
1413
2266
  // src/services/data-service.ts
1414
- var import_axios5 = require("axios");
2267
+ var import_axios7 = require("axios");
1415
2268
  var DataService = class extends Service {
1416
- constructor({ axios: axios2, dataName, basePath, queryPath, onSuccess, onFailure, throwOnError }, defaults) {
1417
- super(axios2, basePath);
2269
+ constructor({ axios: axios3, dataName, basePath, queryPath, onSuccess, onFailure, throwOnError }, defaults) {
2270
+ super(axios3, basePath, throwOnError);
1418
2271
  this._dataName = dataName;
1419
2272
  this._queryPath = queryPath;
1420
2273
  this._defaults = defaults ?? {};
@@ -1440,7 +2293,6 @@ var DataService = class extends Service {
1440
2293
  pageSize = this._defaults.listArgs.pageSize
1441
2294
  } = args ?? {};
1442
2295
  const {
1443
- includePermissions = this._defaults.listOptions.includePermissions ?? false,
1444
2296
  includeCount = this._defaults.listOptions.includeCount ?? false,
1445
2297
  includeExtraHeaders = this._defaults.listOptions.includeExtraHeaders ?? false,
1446
2298
  ignoreCache = this._defaults.listOptions.ignoreCache ?? false
@@ -1450,21 +2302,21 @@ var DataService = class extends Service {
1450
2302
  return makeRequest(
1451
2303
  () => this._axios.get(
1452
2304
  this._basePath,
1453
- (0, import_axios5.mergeConfig)(reqConfig, {
2305
+ (0, import_axios7.mergeConfig)(reqConfig, {
1454
2306
  params: {
1455
2307
  skip,
1456
2308
  limit,
1457
2309
  page,
1458
2310
  page_size: pageSize,
1459
- include_permissions: includePermissions,
1460
2311
  include_count: includeCount,
1461
2312
  include_extra_headers: includeExtraHeaders
1462
2313
  }
1463
2314
  })
1464
2315
  ).then(this.handleSuccess).then((result) => {
1465
2316
  return processListResult(result, { includeCount, includeExtraHeaders });
1466
- }).catch(this.handleError).then((res) => this._handleCallbacks(res, throwOnError)),
2317
+ }).catch(this.handleError).then(ensureListResultCount).then((res) => this._handleCallbacks(res, throwOnError)),
1467
2318
  {
2319
+ __throwOnError: throwOnError,
1468
2320
  __op: "list",
1469
2321
  __query: {
1470
2322
  target: "data",
@@ -1472,7 +2324,7 @@ var DataService = class extends Service {
1472
2324
  op: "list",
1473
2325
  filter: {},
1474
2326
  args: { skip, limit, page, pageSize },
1475
- options: { includePermissions, includeCount, includeExtraHeaders }
2327
+ options: { includeCount }
1476
2328
  },
1477
2329
  __requestConfig: reqConfig,
1478
2330
  __service: this
@@ -1489,7 +2341,6 @@ var DataService = class extends Service {
1489
2341
  } = args ?? {};
1490
2342
  const select = args?.select ?? this._defaults.listAdvancedArgs.select;
1491
2343
  const {
1492
- includePermissions = this._defaults.listAdvancedOptions.includePermissions ?? false,
1493
2344
  includeCount = this._defaults.listAdvancedOptions.includeCount ?? false,
1494
2345
  includeExtraHeaders = this._defaults.listAdvancedOptions.includeExtraHeaders ?? false,
1495
2346
  ignoreCache = this._defaults.listAdvancedOptions.ignoreCache ?? false
@@ -1508,7 +2359,7 @@ var DataService = class extends Service {
1508
2359
  limit,
1509
2360
  page,
1510
2361
  pageSize,
1511
- options: { includePermissions, includeCount, includeExtraHeaders }
2362
+ options: { includeCount, includeExtraHeaders }
1512
2363
  },
1513
2364
  reqConfig
1514
2365
  ).then(this.handleSuccess).then((result) => {
@@ -1516,10 +2367,11 @@ var DataService = class extends Service {
1516
2367
  includeCount,
1517
2368
  includeExtraHeaders
1518
2369
  });
1519
- }).catch(this.handleError).then(
2370
+ }).catch(this.handleError).then(ensureListResultCount).then(
1520
2371
  (res) => this._handleCallbacks(res, throwOnError)
1521
2372
  ),
1522
2373
  {
2374
+ __throwOnError: throwOnError,
1523
2375
  __op: "listAdvanced",
1524
2376
  __query: {
1525
2377
  target: "data",
@@ -1527,7 +2379,7 @@ var DataService = class extends Service {
1527
2379
  op: "list",
1528
2380
  filter: _filter,
1529
2381
  args: { select, sort, skip, limit, page, pageSize },
1530
- options: { includePermissions, includeCount, includeExtraHeaders }
2382
+ options: { includeCount }
1531
2383
  },
1532
2384
  __requestConfig: reqConfig,
1533
2385
  __service: this
@@ -1538,23 +2390,16 @@ var DataService = class extends Service {
1538
2390
  // Document operations
1539
2391
  // ---------------------------------------------------------------------------
1540
2392
  read(identifier, options, axiosRequestConfig) {
1541
- const {
1542
- includePermissions = this._defaults.readOptions.includePermissions ?? true,
1543
- ignoreCache = this._defaults.readOptions.ignoreCache ?? false
1544
- } = options ?? {};
2393
+ const { ignoreCache = this._defaults.readOptions.ignoreCache ?? false } = options ?? {};
1545
2394
  const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
1546
2395
  reqConfig.headers = this.updateHeaders(reqConfig.headers, { ignoreCache });
1547
2396
  return makeRequest(
1548
- () => this._axios.get(
1549
- `${this._basePath}/${identifier}`,
1550
- (0, import_axios5.mergeConfig)(reqConfig, {
1551
- params: { include_permissions: includePermissions }
1552
- })
1553
- ).then(this.handleSuccess).then((result) => {
1554
- result.data = result.raw;
2397
+ () => this._axios.get(`${this._basePath}/${encodePathSegment(identifier)}`, reqConfig).then(this.handleSuccess).then((result) => {
2398
+ if (result.success) result.data = result.raw;
1555
2399
  return result;
1556
2400
  }).catch(this.handleError).then((res) => this._handleCallbacks(res, throwOnError)),
1557
2401
  {
2402
+ __throwOnError: throwOnError,
1558
2403
  __op: "read",
1559
2404
  __query: {
1560
2405
  target: "data",
@@ -1562,7 +2407,7 @@ var DataService = class extends Service {
1562
2407
  op: "read",
1563
2408
  id: identifier,
1564
2409
  args: {},
1565
- options: { includePermissions }
2410
+ options: {}
1566
2411
  },
1567
2412
  __requestConfig: reqConfig,
1568
2413
  __service: this
@@ -1570,19 +2415,19 @@ var DataService = class extends Service {
1570
2415
  );
1571
2416
  }
1572
2417
  readAdvanced(identifier, args, options, axiosRequestConfig) {
1573
- const { ignoreCache = this._defaults.readAdvancedArgs.ignoreCache ?? false } = args ?? {};
1574
2418
  const select = args?.select ?? this._defaults.readAdvancedArgs.select;
1575
- const { includePermissions = this._defaults.readAdvancedOptions.includePermissions ?? true } = options ?? {};
2419
+ const { ignoreCache = this._defaults.readAdvancedOptions.ignoreCache ?? false } = options ?? {};
1576
2420
  const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
1577
2421
  reqConfig.headers = this.updateHeaders(reqConfig.headers, { ignoreCache });
1578
2422
  return makeRequest(
1579
- () => this._axios.post(`${this._basePath}/${this._queryPath}/${identifier}`, { select }, reqConfig).then(this.handleSuccess).then((result) => {
1580
- result.data = result.raw;
2423
+ () => this._axios.post(`${this._basePath}/${this._queryPath}/${encodePathSegment(identifier)}`, { select }, reqConfig).then(this.handleSuccess).then((result) => {
2424
+ if (result.success) result.data = result.raw;
1581
2425
  return result;
1582
2426
  }).catch(this.handleError).then(
1583
2427
  (res) => this._handleCallbacks(res, throwOnError)
1584
2428
  ),
1585
2429
  {
2430
+ __throwOnError: throwOnError,
1586
2431
  __op: "readAdvanced",
1587
2432
  __query: {
1588
2433
  target: "data",
@@ -1590,7 +2435,7 @@ var DataService = class extends Service {
1590
2435
  op: "read",
1591
2436
  id: identifier,
1592
2437
  args: { select },
1593
- options: { includePermissions }
2438
+ options: {}
1594
2439
  },
1595
2440
  __requestConfig: reqConfig,
1596
2441
  __service: this
@@ -1599,19 +2444,19 @@ var DataService = class extends Service {
1599
2444
  }
1600
2445
  readAdvancedFilter(filter, args, options, axiosRequestConfig) {
1601
2446
  const select = args?.select ?? this._defaults.readAdvancedArgs.select;
1602
- const { ignoreCache = this._defaults.readAdvancedArgs.ignoreCache ?? false } = args ?? {};
1603
- const { includePermissions = this._defaults.readAdvancedOptions.includePermissions ?? true } = options ?? {};
2447
+ const { ignoreCache = this._defaults.readAdvancedOptions.ignoreCache ?? false } = options ?? {};
1604
2448
  const { throwOnError, ...reqConfig } = axiosRequestConfig ?? {};
1605
2449
  reqConfig.headers = this.updateHeaders(reqConfig.headers, { ignoreCache });
1606
2450
  const _filter = replaceSubQuery(filter);
1607
2451
  return makeRequest(
1608
2452
  () => this._axios.post(`${this._basePath}/${this._queryPath}/__filter`, { filter: _filter, select }, reqConfig).then(this.handleSuccess).then((result) => {
1609
- result.data = result.raw;
2453
+ if (result.success) result.data = result.raw;
1610
2454
  return result;
1611
2455
  }).catch(this.handleError).then(
1612
2456
  (res) => this._handleCallbacks(res, throwOnError)
1613
2457
  ),
1614
2458
  {
2459
+ __throwOnError: throwOnError,
1615
2460
  __op: "readAdvancedFilter",
1616
2461
  __query: {
1617
2462
  target: "data",
@@ -1619,7 +2464,7 @@ var DataService = class extends Service {
1619
2464
  op: "read",
1620
2465
  filter: _filter,
1621
2466
  args: { select },
1622
- options: { includePermissions }
2467
+ options: {}
1623
2468
  },
1624
2469
  __requestConfig: reqConfig,
1625
2470
  __service: this
@@ -1628,132 +2473,8 @@ var DataService = class extends Service {
1628
2473
  }
1629
2474
  };
1630
2475
 
1631
- // src/services/interceptors.ts
1632
- var import_axios7 = require("axios");
1633
-
1634
- // src/services/cache-utils.ts
1635
- var import_axios6 = require("axios");
1636
- var import_utils6 = require("@web-ts-toolkit/utils");
1637
- var normalizeConfigValue = (value) => {
1638
- if (value == null) return value;
1639
- if (value instanceof import_axios6.AxiosHeaders) {
1640
- return normalizeConfigValue(value.toJSON());
1641
- }
1642
- if (Array.isArray(value)) {
1643
- return value.map((item) => normalizeConfigValue(item));
1644
- }
1645
- if (typeof value === "object") {
1646
- return Object.entries((0, import_utils6.omitBy)(value, (item) => item === void 0)).sort(([left], [right]) => left.localeCompare(right)).reduce((acc, [key, item]) => {
1647
- acc[key] = normalizeConfigValue(item);
1648
- return acc;
1649
- }, {});
1650
- }
1651
- return value;
1652
- };
1653
-
1654
- // src/services/interceptors.ts
1655
- var SimpleCache = class {
1656
- constructor() {
1657
- this.cache = /* @__PURE__ */ new Map();
1658
- this.timers = /* @__PURE__ */ new Map();
1659
- }
1660
- set(key, value, ttl) {
1661
- this.cache.set(key, value);
1662
- if (ttl && ttl > 0) {
1663
- const existing = this.timers.get(key);
1664
- if (existing) clearTimeout(existing);
1665
- this.timers.set(
1666
- key,
1667
- setTimeout(() => {
1668
- this.cache.delete(key);
1669
- this.timers.delete(key);
1670
- }, ttl)
1671
- );
1672
- }
1673
- }
1674
- get(key) {
1675
- return this.cache.get(key);
1676
- }
1677
- has(key) {
1678
- return this.cache.has(key);
1679
- }
1680
- delete(key) {
1681
- const timer = this.timers.get(key);
1682
- if (timer) {
1683
- clearTimeout(timer);
1684
- this.timers.delete(key);
1685
- }
1686
- return this.cache.delete(key);
1687
- }
1688
- };
1689
- var IGNORED_CACHE_HEADERS = /* @__PURE__ */ new Set([
1690
- "accept",
1691
- "accept-encoding",
1692
- "cache-control",
1693
- "connection",
1694
- "content-length",
1695
- "content-type",
1696
- "expires",
1697
- "host",
1698
- "pragma",
1699
- "user-agent"
1700
- ]);
1701
- var serializeHeaders = (headers) => {
1702
- const resolvedHeaders = headers instanceof import_axios7.AxiosHeaders ? headers.toJSON() : headers;
1703
- const normalizedHeaders = Object.entries(resolvedHeaders ?? {}).filter(([key, value]) => {
1704
- const normalizedKey = key.toLowerCase();
1705
- return normalizedKey !== CACHE_HEADER.toLowerCase() && !IGNORED_CACHE_HEADERS.has(normalizedKey) && value !== void 0;
1706
- }).reduce((acc, [key, value]) => {
1707
- acc[key.toLowerCase()] = value;
1708
- return acc;
1709
- }, {});
1710
- return JSON.stringify(normalizeConfigValue(normalizedHeaders));
1711
- };
1712
- function generateCacheKey(config) {
1713
- const key = `${config.baseURL}/${config.url}_${config.method}_${generateParamKey(config.params)}_${generateDataKey(
1714
- config.data
1715
- )}_${serializeHeaders(config.headers)}`;
1716
- return encodeURI(key);
1717
- }
1718
- function generateParamKey(params) {
1719
- if (!params) return "";
1720
- return JSON.stringify(normalizeConfigValue(params));
1721
- }
1722
- function generateDataKey(data) {
1723
- if (!data) return "";
1724
- return typeof data === "string" ? data : JSON.stringify(normalizeConfigValue(data));
1725
- }
1726
- function useCacheInterceptors(instance, cacheTTL) {
1727
- const store = new SimpleCache();
1728
- instance.interceptors.request.use(
1729
- async (config) => {
1730
- if (config.headers[CACHE_HEADER] === "false") return config;
1731
- const key = generateCacheKey(config);
1732
- const cachedResponse = store.get(key);
1733
- if (!cachedResponse) {
1734
- return config;
1735
- }
1736
- config.adapter = async (_config) => {
1737
- return {
1738
- ...cachedResponse,
1739
- config: _config,
1740
- headers: { ...cachedResponse.headers, [CACHE_HEADER]: "true" }
1741
- };
1742
- };
1743
- return config;
1744
- },
1745
- (error) => Promise.reject(error)
1746
- );
1747
- instance.interceptors.response.use(
1748
- (response) => {
1749
- if (response.config.headers[CACHE_HEADER] === "false") return response;
1750
- const key = generateCacheKey(response.config);
1751
- store.set(key, response, cacheTTL);
1752
- return response;
1753
- },
1754
- (error) => Promise.reject(error)
1755
- );
1756
- }
2476
+ // src/services/symbols.ts
2477
+ var ADAPTER_ID_KEY = /* @__PURE__ */ Symbol("adapterId");
1757
2478
 
1758
2479
  // src/adapter.ts
1759
2480
  var defaultAxiosConfig = Object.freeze({
@@ -1766,7 +2487,12 @@ var defaultAxiosConfig = Object.freeze({
1766
2487
  Expires: "0"
1767
2488
  }
1768
2489
  });
1769
- var isModelQuery = (query) => query.target === "model";
2490
+ var noopCacheController = {
2491
+ clear: () => {
2492
+ },
2493
+ dispose: () => {
2494
+ }
2495
+ };
1770
2496
  var serializeRequestConfig = (config) => JSON.stringify(normalizeConfigValue(config ?? {}));
1771
2497
  var isObjectRecord = (value) => value != null && typeof value === "object" && !Array.isArray(value);
1772
2498
  var mergeServiceDefaults = (adapterDefaults, serviceDefaults) => {
@@ -1798,13 +2524,32 @@ function createAdapter(axiosConfig, adapterOptions) {
1798
2524
  onFailure: onFailureRoot,
1799
2525
  throwOnError: throwOnErrorRoot,
1800
2526
  cacheTTL = 0,
2527
+ cachePartition,
2528
+ cacheCapacity,
1801
2529
  modelDefaults: adapterModelDefaults,
1802
2530
  dataDefaults: adapterDataDefaults
1803
2531
  } = adapterOptions ?? {};
1804
- if (cacheTTL > 0) useCacheInterceptors(instance, cacheTTL);
2532
+ const cacheController = cacheTTL > 0 ? useCacheInterceptors(instance, {
2533
+ ttl: cacheTTL,
2534
+ capacity: cacheCapacity,
2535
+ withCredentialsDefault: Boolean(instance.defaults.withCredentials),
2536
+ partitionForRequest: cachePartition
2537
+ }) : noopCacheController;
1805
2538
  const wraps = createWrapHelper(instance);
2539
+ const adapterId = /* @__PURE__ */ Symbol("adapter");
2540
+ const stampAdapterId = (service) => {
2541
+ Object.defineProperty(service, ADAPTER_ID_KEY, {
2542
+ value: adapterId,
2543
+ enumerable: false,
2544
+ writable: false,
2545
+ configurable: false
2546
+ });
2547
+ return service;
2548
+ };
1806
2549
  return Object.freeze({
1807
2550
  axios: instance,
2551
+ clearCache: cacheController.clear,
2552
+ disposeCache: cacheController.dispose,
1808
2553
  createModelService: ({
1809
2554
  modelName,
1810
2555
  basePath,
@@ -1814,7 +2559,7 @@ function createAdapter(axiosConfig, adapterOptions) {
1814
2559
  onFailure,
1815
2560
  throwOnError
1816
2561
  }, defaults) => {
1817
- return new ModelService(
2562
+ const service = new ModelService(
1818
2563
  {
1819
2564
  axios: instance,
1820
2565
  modelName,
@@ -1827,9 +2572,10 @@ function createAdapter(axiosConfig, adapterOptions) {
1827
2572
  },
1828
2573
  mergeServiceDefaults(adapterModelDefaults, defaults)
1829
2574
  );
2575
+ return stampAdapterId(service);
1830
2576
  },
1831
2577
  createDataService: ({ dataName, basePath, queryPath = "__query", onSuccess, onFailure, throwOnError }, defaults) => {
1832
- return new DataService(
2578
+ const service = new DataService(
1833
2579
  {
1834
2580
  axios: instance,
1835
2581
  dataName,
@@ -1841,6 +2587,7 @@ function createAdapter(axiosConfig, adapterOptions) {
1841
2587
  },
1842
2588
  mergeServiceDefaults(adapterDataDefaults, defaults)
1843
2589
  );
2590
+ return stampAdapterId(service);
1844
2591
  },
1845
2592
  wrapGet: wraps.wrapGet,
1846
2593
  wrapPost: wraps.wrapPost,
@@ -1850,55 +2597,72 @@ function createAdapter(axiosConfig, adapterOptions) {
1850
2597
  group: async (...proms) => {
1851
2598
  let sharedConfig;
1852
2599
  let sharedConfigKey;
2600
+ let groupThrowOnError;
1853
2601
  const defs = proms.map((prom, index) => {
1854
- if (!(0, import_utils7.isEmpty)(prom.__requestConfig)) {
1855
- const configKey = serializeRequestConfig(prom.__requestConfig);
1856
- if (sharedConfigKey && sharedConfigKey !== configKey) {
1857
- throw new Error("Grouped requests must share the same axios request config");
1858
- }
1859
- sharedConfig = prom.__requestConfig;
1860
- sharedConfigKey = configKey;
2602
+ const service = prom.__service;
2603
+ if (!service || service[ADAPTER_ID_KEY] !== adapterId) {
2604
+ throw new Error(
2605
+ "Cannot group a request owned by a different adapter; create the request from this adapter's services"
2606
+ );
2607
+ }
2608
+ if (groupThrowOnError != null && groupThrowOnError !== prom.__throwOnError) {
2609
+ throw new Error("Grouped requests must share the same effective throwOnError policy");
2610
+ }
2611
+ groupThrowOnError = prom.__throwOnError;
2612
+ const configKey = serializeRequestConfig(prom.__requestConfig);
2613
+ if (sharedConfigKey != null && sharedConfigKey !== configKey) {
2614
+ throw new Error("Grouped requests must share the same axios request config");
1861
2615
  }
2616
+ sharedConfig = prom.__requestConfig ?? {};
2617
+ sharedConfigKey = configKey;
1862
2618
  const query = { ...prom.__query };
2619
+ if (query.target === "model") {
2620
+ delete query.model;
2621
+ }
1863
2622
  if (prom.__query.order == null) {
1864
2623
  query.order = index;
1865
2624
  }
1866
2625
  return query;
1867
2626
  });
1868
- const result = await instance.post(rootRouterPath, defs, sharedConfig ?? {}).then((res) => {
1869
- const responseHeaders = res.headers ?? {};
1870
- return res.data.map(({ result: result2, message, statusCode, op }, index) => {
1871
- const service = proms[index].__service;
1872
- const query = proms[index].__query;
1873
- const success = result2.success;
1874
- let _raw = success ? result2.data : null;
1875
- let _data = _raw;
1876
- if (!success) {
1877
- _data = null;
1878
- } else if (isModelQuery(query)) {
1879
- const modelService = service;
1880
- if (result2.kind === "list" && Array.isArray(result2.data)) {
1881
- if (op === "create" && result2.data.length === 1) {
1882
- _raw = result2.data[0];
1883
- _data = Model.create(result2.data[0], modelService);
1884
- } else if (!["distinct", "subList"].includes(op)) {
1885
- _data = (0, import_utils7.castArray)(result2.data).map((item) => Model.create(item, modelService));
1886
- }
1887
- } else if (result2.kind === "single" && ["new", "read", "update", "upsert"].includes(op)) {
1888
- _data = Model.create(result2.data, modelService);
1889
- }
1890
- }
1891
- return {
1892
- success,
1893
- raw: _raw,
1894
- data: _data,
2627
+ const groupOwner = /* @__PURE__ */ Symbol("group");
2628
+ const claimed = [];
2629
+ try {
2630
+ for (const prom of proms) {
2631
+ claimLazyRequest(prom, "grouped", groupOwner);
2632
+ claimed.push(prom);
2633
+ }
2634
+ } catch (error) {
2635
+ for (const prom of claimed) {
2636
+ releaseLazyRequestClaim(prom, groupOwner);
2637
+ }
2638
+ throw error;
2639
+ }
2640
+ const result = await instance.post(rootRouterPath, defs, sharedConfig ?? {}).then(
2641
+ (res) => {
2642
+ const rawEntries = res.data.map(({ result: result2, message, statusCode, op }) => ({
2643
+ result: result2,
1895
2644
  message,
1896
- status: statusCode,
1897
- totalCount: result2.success && result2.kind === "list" ? result2.totalCount ?? result2.count ?? 0 : 0,
1898
- headers: responseHeaders
1899
- };
1900
- });
1901
- });
2645
+ statusCode,
2646
+ op
2647
+ }));
2648
+ const finalized = rawEntries.map(
2649
+ (rawEntry, index) => finalizeRootEntry(proms[index].__query, rawEntry, {}, proms[index].__service)
2650
+ );
2651
+ return applyGroupCallbacks(
2652
+ finalized,
2653
+ proms.map((p) => p.__service),
2654
+ groupThrowOnError ?? false
2655
+ );
2656
+ },
2657
+ (error) => {
2658
+ const failures = proms.map((prom) => finalizeRootTransportFailure(prom.__query, error));
2659
+ return applyGroupCallbacks(
2660
+ failures,
2661
+ proms.map((p) => p.__service),
2662
+ groupThrowOnError ?? false
2663
+ );
2664
+ }
2665
+ );
1902
2666
  return result;
1903
2667
  }
1904
2668
  });
@@ -1923,6 +2687,7 @@ function removeItemById(items, targetItem) {
1923
2687
  0 && (module.exports = {
1924
2688
  CustomHeaders,
1925
2689
  DataService,
2690
+ MissingPersistenceIdentityError,
1926
2691
  Model,
1927
2692
  ModelService,
1928
2693
  Service,