zova-module-a-model 5.1.36 → 5.1.38

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 (32) hide show
  1. package/dist/bean/bean.model/bean.model.persister.d.ts +12 -3
  2. package/dist/bean/bean.model/bean.model.persister.d.ts.map +1 -1
  3. package/dist/bean/bean.model/bean.model.query.d.ts.map +1 -1
  4. package/dist/bean/bean.model/bean.model.useMutation.d.ts.map +1 -1
  5. package/dist/bean/bean.model/bean.model.useQuery.d.ts.map +1 -1
  6. package/dist/bean/bean.model/bean.model.useState.d.ts.map +1 -1
  7. package/dist/index.js +523 -91
  8. package/dist/index.js.map +1 -1
  9. package/dist/lib/index.d.ts +1 -0
  10. package/dist/lib/index.d.ts.map +1 -1
  11. package/dist/lib/modelPersistence.d.ts +39 -0
  12. package/dist/lib/modelPersistence.d.ts.map +1 -0
  13. package/dist/lib/persistenceQueue.d.ts +14 -0
  14. package/dist/lib/persistenceQueue.d.ts.map +1 -0
  15. package/dist/monkey.d.ts.map +1 -1
  16. package/dist/service/storage.d.ts +15 -0
  17. package/dist/service/storage.d.ts.map +1 -1
  18. package/dist/types/query.d.ts +3 -4
  19. package/dist/types/query.d.ts.map +1 -1
  20. package/package.json +2 -2
  21. package/src/bean/bean.model/bean.model.persister.ts +331 -87
  22. package/src/bean/bean.model/bean.model.query.ts +11 -6
  23. package/src/bean/bean.model/bean.model.useMutation.ts +1 -11
  24. package/src/bean/bean.model/bean.model.useQuery.ts +3 -17
  25. package/src/bean/bean.model/bean.model.useState.ts +17 -1
  26. package/src/lib/index.ts +1 -0
  27. package/src/lib/modelPersistence.ts +204 -0
  28. package/src/lib/persistenceQueue.ts +50 -0
  29. package/src/lib/utils.ts +2 -1
  30. package/src/monkey.ts +5 -0
  31. package/src/service/storage.ts +47 -2
  32. package/src/types/query.ts +5 -7
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { BeanBase, BeanInfo, BeanScopeBase, BeanSimple, SymbolBeanFullName, Virtual, cast, createBeanDecorator, deepExtend } from "zova";
2
2
  import { Bean, Scope, Service } from "zova-module-a-bean";
3
3
  import { QueryCache, QueryClient, VueQueryPlugin, defaultShouldDehydrateQuery, dehydrate, hashKey, hydrate, useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
4
- import { checkErrorJwtExpired } from "@cabloy/utils";
5
4
  import localforage from "localforage";
5
+ import { CancelledError, notifyManager } from "@tanstack/query-core";
6
6
  import { experimental_createQueryPersister } from "@tanstack/query-persist-client-core";
7
- import { markRaw } from "vue";
7
+ import { markRaw, shallowRef } from "vue";
8
8
  //#region src/lib/queryRefetch.ts
9
9
  function createQueryRefetch(refetch, getQuery) {
10
10
  return async (refetchOptions) => {
@@ -27,6 +27,56 @@ function resolveMaxAgeTime(maxAge, query) {
27
27
  return typeof maxAge === "function" ? maxAge(query) : maxAge;
28
28
  }
29
29
  //#endregion
30
+ //#region src/lib/persistenceQueue.ts
31
+ var PersistenceQueue = class {
32
+ constructor() {
33
+ this._tail = Promise.resolve();
34
+ this._sequence = 0;
35
+ this._failure = void 0;
36
+ this._generation = 0;
37
+ this._keyGenerations = /* @__PURE__ */ new Map();
38
+ }
39
+ generation(key) {
40
+ return `${this._generation}:${this._keyGenerations.get(key) ?? 0}`;
41
+ }
42
+ invalidate(key) {
43
+ this._keyGenerations.set(key, (this._keyGenerations.get(key) ?? 0) + 1);
44
+ }
45
+ invalidateAll() {
46
+ this._generation++;
47
+ this._keyGenerations.clear();
48
+ }
49
+ enqueue(operation) {
50
+ const sequence = ++this._sequence;
51
+ const result = this._tail.then(operation);
52
+ this._tail = result.catch((error) => {
53
+ this._failure ??= {
54
+ sequence,
55
+ error
56
+ };
57
+ });
58
+ return result;
59
+ }
60
+ async flush() {
61
+ const sequence = this._sequence;
62
+ await this._tail;
63
+ if (this._failure && this._failure.sequence <= sequence) {
64
+ const { error } = this._failure;
65
+ this._failure = void 0;
66
+ throw error;
67
+ }
68
+ }
69
+ };
70
+ var queues = /* @__PURE__ */ new WeakMap();
71
+ function getPersistenceQueue(storage) {
72
+ let queue = queues.get(storage);
73
+ if (!queue) {
74
+ queue = new PersistenceQueue();
75
+ queues.set(storage, queue);
76
+ }
77
+ return queue;
78
+ }
79
+ //#endregion
30
80
  //#region src/.metadata/this.ts
31
81
  var __ThisModule__ = "a-model";
32
82
  //#endregion
@@ -48,7 +98,7 @@ var CookieWrapper = class extends BeanSimple {
48
98
  const opts = { ...this.bean.scope(__ThisModule__).config.persister.cookie.options };
49
99
  let maxAge = resolveMaxAgeTime(this.options.maxAge, this.query);
50
100
  if (maxAge !== void 0) {
51
- if (maxAge === Infinity) maxAge = 1e3 * 60 * 60 * 24 * 365;
101
+ if (maxAge === Infinity) maxAge = 31536e6;
52
102
  opts.expires = new Date(Date.now() + maxAge);
53
103
  }
54
104
  this.app.meta.cookie.setItem(key, value, opts);
@@ -58,6 +108,149 @@ var CookieWrapper = class extends BeanSimple {
58
108
  }
59
109
  };
60
110
  //#endregion
111
+ //#region src/lib/modelPersistence.ts
112
+ var normalize = (value) => value == null ? null : String(value);
113
+ function getModelPersistenceStorageKey(appName, scope, logicalKey) {
114
+ return JSON.stringify([
115
+ "zova-model-db",
116
+ 1,
117
+ appName,
118
+ scope,
119
+ logicalKey
120
+ ]);
121
+ }
122
+ function isModelPersistenceDb(options) {
123
+ if (options === false) return false;
124
+ const persister = options && typeof options === "object" ? options : {};
125
+ return (persister.storage ?? (persister.sync ? "local" : "db")) === "db";
126
+ }
127
+ function emptyQueryState() {
128
+ return {
129
+ data: void 0,
130
+ dataUpdateCount: 0,
131
+ dataUpdatedAt: 0,
132
+ error: null,
133
+ errorUpdateCount: 0,
134
+ errorUpdatedAt: 0,
135
+ fetchFailureCount: 0,
136
+ fetchFailureReason: null,
137
+ fetchMeta: null,
138
+ isInvalidated: false,
139
+ status: "pending",
140
+ fetchStatus: "idle"
141
+ };
142
+ }
143
+ /** App-owned identity; independent of the module providing the current Passport. */
144
+ var ModelPersistenceContext = class {
145
+ constructor(_queryClient, siteId) {
146
+ this._queryClient = _queryClient;
147
+ this._epoch = 0;
148
+ this._version = shallowRef(0);
149
+ this._scope = void 0;
150
+ this._registered = false;
151
+ this._hydrated = /* @__PURE__ */ new Set();
152
+ this._listeners = /* @__PURE__ */ new Set();
153
+ markRaw(this);
154
+ this._scope = Object.freeze([
155
+ null,
156
+ normalize(siteId),
157
+ null
158
+ ]);
159
+ }
160
+ get epoch() {
161
+ this._version.value;
162
+ return this._epoch;
163
+ }
164
+ capture() {
165
+ return Object.freeze({
166
+ scope: this._scope,
167
+ epoch: this.epoch
168
+ });
169
+ }
170
+ current(token) {
171
+ return token.epoch === this.epoch && token.scope === this._scope;
172
+ }
173
+ markHydrated(queries) {
174
+ if (!this._registered) for (const query of queries) this._hydrated.add(query);
175
+ }
176
+ touch(query) {
177
+ if (query) this._hydrated.delete(query);
178
+ }
179
+ subscribe(listener) {
180
+ this._listeners.add(listener);
181
+ return () => this._listeners.delete(listener);
182
+ }
183
+ update(values, options = {}) {
184
+ const scope = Object.freeze([
185
+ normalize(values.instanceId),
186
+ normalize(values.siteId),
187
+ normalize(values.userId)
188
+ ]);
189
+ const adopt = !this._registered && options.initial === true;
190
+ this._registered = true;
191
+ if (scope.every((value, index) => value === this._scope[index])) {
192
+ this._hydrated.clear();
193
+ return;
194
+ }
195
+ const affected = this._queryClient.getQueryCache().getAll().filter((query) => isModelPersistenceDb(query.meta?.persister) && !(adopt && this._hydrated.has(query)));
196
+ this._scope = scope;
197
+ this._hydrated.clear();
198
+ this._invalidate(affected);
199
+ if (options.refetchActive === false) return;
200
+ const token = this.capture();
201
+ Promise.resolve().then(() => {
202
+ if (this.current(token)) return this._queryClient.refetchQueries({
203
+ type: "active",
204
+ predicate: (query) => affected.includes(query)
205
+ });
206
+ });
207
+ }
208
+ clear() {
209
+ this._hydrated.clear();
210
+ this._invalidate(this._queryClient.getQueryCache().getAll());
211
+ }
212
+ _invalidate(queries) {
213
+ this._epoch++;
214
+ for (const query of queries) query.cancel({
215
+ silent: true,
216
+ revert: false
217
+ });
218
+ notifyManager.batch(() => {
219
+ for (const query of queries) {
220
+ query.setOptions({
221
+ ...query.options,
222
+ initialData: void 0,
223
+ initialDataUpdatedAt: void 0
224
+ });
225
+ for (const observer of query.observers) observer.setOptions({
226
+ ...observer.options,
227
+ initialData: void 0,
228
+ initialDataUpdatedAt: void 0,
229
+ placeholderData: void 0
230
+ });
231
+ query.setState(emptyQueryState());
232
+ }
233
+ });
234
+ this._version.value++;
235
+ const token = this.capture();
236
+ for (const listener of this._listeners) listener(token);
237
+ }
238
+ /** Keep later observer option reapplication from reinstalling an old identity. */
239
+ guardOptions(options) {
240
+ if (!isModelPersistenceDb(options.meta?.persister)) return options;
241
+ const token = this.capture();
242
+ const initialData = options.initialData;
243
+ const initialDataUpdatedAt = options.initialDataUpdatedAt;
244
+ const placeholderData = options.placeholderData;
245
+ return {
246
+ ...options,
247
+ initialData: () => this.current(token) ? typeof initialData === "function" ? initialData() : initialData : void 0,
248
+ initialDataUpdatedAt: () => this.current(token) ? typeof initialDataUpdatedAt === "function" ? initialDataUpdatedAt() : initialDataUpdatedAt : void 0,
249
+ placeholderData: (...args) => this.current(token) ? typeof placeholderData === "function" ? placeholderData(...args) : placeholderData : void 0
250
+ };
251
+ }
252
+ };
253
+ //#endregion
61
254
  //#region src/lib/queryPersister.ts
62
255
  function createQueryPersister(persisterFn) {
63
256
  return (queryFn, context, query) => {
@@ -87,50 +280,102 @@ var BeanModelLast = class extends BeanBase {
87
280
  //#endregion
88
281
  //#region src/bean/bean.model/bean.model.persister.ts
89
282
  var BeanModelPersister = class extends BeanModelLast {
90
- _persisterLoad_inner(storage, storageKey, storedData, query, options) {
91
- if (!storedData) return void 0;
283
+ constructor(...args) {
284
+ super(...args);
285
+ this._persisterRestoreErrors = /* @__PURE__ */ new Set();
286
+ }
287
+ $persisterError(queryKey) {
288
+ const query = this.self.$queryFind({ queryKey });
289
+ const options = this._adjustPersisterOptions(query?.meta?.persister);
290
+ return !!options && this._persisterRestoreErrors.has(this._getPersisterStorageKey(options, query, queryKey));
291
+ }
292
+ _persisterLoad_inner(storedData, query, options) {
293
+ if (!storedData) return { remove: false };
92
294
  const persistedQuery = options.deserialize ? options.deserialize(storedData, options.deserializeDefault) : options.deserializeDefault(storedData);
93
295
  if (persistedQuery.state.dataUpdatedAt) {
94
296
  const expired = Date.now() - persistedQuery.state.dataUpdatedAt > (resolveMaxAgeTime(options.maxAge, query) ?? Infinity);
95
297
  const busted = persistedQuery.buster !== options.buster;
96
- if (expired || busted) storage.removeItem(storageKey);
97
- else {
98
- query.setState({
298
+ if (!expired && !busted) return {
299
+ data: persistedQuery.state.data,
300
+ remove: false,
301
+ timestamps: {
99
302
  dataUpdatedAt: persistedQuery.state.dataUpdatedAt,
100
303
  errorUpdatedAt: persistedQuery.state.errorUpdatedAt
101
- });
102
- return persistedQuery.state.data;
103
- }
104
- } else storage.removeItem(storageKey);
304
+ }
305
+ };
306
+ }
307
+ return { remove: true };
105
308
  }
106
309
  $persisterLoad(queryKey) {
310
+ const ctx = this.ctx;
311
+ if (ctx?.disposed) return void 0;
312
+ const persistence = this.$modelPersistence;
107
313
  const query = this.self.$queryFind({ queryKey });
108
314
  if (!query) return void 0;
109
315
  const options = this._adjustPersisterOptions(query.meta?.persister);
110
316
  if (!options) return void 0;
111
317
  const storage = this._getPersisterStorage(options, query);
112
318
  if (!storage) return void 0;
113
- const storageKey = this._getPersisterStorageKey(options, query);
319
+ const token = persistence.capture();
320
+ if (options.storage === "db") persistence.touch(query);
321
+ const storageKey = this._getPersisterStorageKey(options, query, void 0, token);
322
+ this._persisterRestoreErrors.delete(storageKey);
323
+ const queue = getPersistenceQueue(storage);
324
+ const generation = queue.generation(storageKey);
325
+ const current = () => !ctx?.disposed && generation === queue.generation(storageKey) && (options.storage !== "db" || persistence.current(token));
326
+ if (!options.sync) return queue.enqueue(async () => {
327
+ if (!current()) return void 0;
328
+ const storedData = await storage.getItem(storageKey);
329
+ if (!current()) return void 0;
330
+ try {
331
+ const result = this._persisterLoad_inner(storedData, query, options);
332
+ if (!current()) return void 0;
333
+ if (result.timestamps) query.setState(result.timestamps);
334
+ if (result.remove) await storage.removeItem(storageKey);
335
+ return current() ? result.data : void 0;
336
+ } catch {
337
+ if (!current()) return void 0;
338
+ this._persisterRestoreErrors.add(storageKey);
339
+ await storage.removeItem(storageKey);
340
+ this._reportPersisterError("restore");
341
+ return;
342
+ }
343
+ }).catch(() => {
344
+ if (current()) {
345
+ this._persisterRestoreErrors.add(storageKey);
346
+ this._reportPersisterError("restore");
347
+ }
348
+ });
114
349
  try {
115
- const storedData = storage.getItem(storageKey);
116
- if (options.sync) return this._persisterLoad_inner(storage, storageKey, storedData, query, options);
117
- else return storedData.then((storedData) => {
118
- return this._persisterLoad_inner(storage, storageKey, storedData, query, options);
119
- });
120
- } catch (err) {
121
- if (process.env.DEV) {
122
- console.error(err);
123
- console.warn("Encountered an error attempting to restore query cache from persisted location.");
350
+ const result = this._persisterLoad_inner(storage.getItem(storageKey), query, options);
351
+ if (result.timestamps) query.setState(result.timestamps);
352
+ if (result.remove) storage.removeItem(storageKey);
353
+ return result.data;
354
+ } catch {
355
+ this._persisterRestoreErrors.add(storageKey);
356
+ this._reportPersisterError("restore");
357
+ try {
358
+ storage.removeItem(storageKey);
359
+ } catch {
360
+ this._reportPersisterError("remove");
124
361
  }
125
- storage.removeItem(storageKey);
126
362
  }
127
363
  }
364
+ $persisterGeneration(queryKey) {
365
+ const query = this.self.$queryFind({ queryKey });
366
+ const options = this._adjustPersisterOptions(query?.meta?.persister);
367
+ if (!options) return "";
368
+ const storage = this._getPersisterStorage(options, query);
369
+ if (!storage) return "";
370
+ const storageKey = this._getPersisterStorageKey(options, query, queryKey);
371
+ return `${options.storage === "db" ? this.$modelPersistence.epoch : ""}:${storageKey}:${getPersistenceQueue(storage).generation(storageKey)}`;
372
+ }
128
373
  $persisterSave(queryKey) {
129
374
  const query = this.self.$queryFind({ queryKey });
130
- if (!query) return;
375
+ if (!query) return Promise.resolve();
131
376
  const options = this._adjustPersisterOptions(query.meta?.persister);
132
- if (!options) return;
133
- this._persisterSave(query, options);
377
+ if (!options) return Promise.resolve();
378
+ return this._persisterSave(query, options);
134
379
  }
135
380
  $persisterSaveTo(queryKey, storageTarget, value) {
136
381
  const query = this.self.$queryFind({ queryKey });
@@ -145,13 +390,9 @@ var BeanModelPersister = class extends BeanModelLast {
145
390
  sync: true,
146
391
  serializeDefault: (obj) => storageTarget === "cookie" ? String(obj?.state?.data ?? "") : JSON.stringify(obj?.state?.data)
147
392
  });
148
- if (!options) return;
149
- this._persisterSave(query, options, value);
393
+ if (options) this._persisterSave(query, options, value);
150
394
  }
151
- _persisterSave(query, options, value) {
152
- const storage = this._getPersisterStorage(options, query);
153
- if (!storage) return;
154
- const storageKey = this._getPersisterStorageKey(options, query);
395
+ _serializePersister(query, options, value) {
155
396
  const params = {
156
397
  state: value === void 0 ? query.state : {
157
398
  ...query.state,
@@ -161,42 +402,183 @@ var BeanModelPersister = class extends BeanModelLast {
161
402
  queryHash: query.queryHash,
162
403
  buster: options.buster
163
404
  };
164
- const data = options.serialize ? options.serialize(params, options.serializeDefault) : options.serializeDefault(params);
165
- if (options.sync === true) storage.setItem(storageKey, data);
166
- else setTimeout(() => {
405
+ return options.serialize ? options.serialize(params, options.serializeDefault) : options.serializeDefault(params);
406
+ }
407
+ _persisterSave(query, options, value) {
408
+ const storage = this._getPersisterStorage(options, query);
409
+ if (!storage) return Promise.resolve();
410
+ if (options.storage === "db") this.$modelPersistence.touch(query);
411
+ const storageKey = this._getPersisterStorageKey(options, query);
412
+ const queue = getPersistenceQueue(storage);
413
+ queue.invalidate(storageKey);
414
+ let data;
415
+ try {
416
+ data = this._serializePersister(query, options, value);
417
+ } catch (error) {
418
+ return this._handlePersisterOperation(queue.enqueue(() => {
419
+ throw error;
420
+ }), "save");
421
+ }
422
+ if (options.sync === true && options.storage !== "db") try {
167
423
  storage.setItem(storageKey, data);
168
- }, 0);
424
+ return Promise.resolve();
425
+ } catch (error) {
426
+ return this._handlePersisterOperation(Promise.reject(error), "save");
427
+ }
428
+ return this._handlePersisterOperation(queue.enqueue(async () => {
429
+ await storage.setItem(storageKey, data);
430
+ }), "save");
169
431
  }
170
- $persisterRemove(queryKey) {
432
+ $persisterRemove(queryKey, persister) {
171
433
  const query = this.self.$queryFind({ queryKey });
172
- if (!query) return;
173
- const options = this._adjustPersisterOptions(query.meta?.persister);
174
- if (!options) return;
434
+ if (!query && !persister) return Promise.resolve();
435
+ const options = this._adjustPersisterOptions(persister ?? query?.meta?.persister);
436
+ if (!options) return Promise.resolve();
175
437
  const storage = this._getPersisterStorage(options, query);
176
- if (!storage) return;
177
- const storageKey = this._getPersisterStorageKey(options, query);
178
- if (options.sync === true) storage.removeItem(storageKey);
179
- else setTimeout(() => {
438
+ if (!storage) return Promise.resolve();
439
+ if (options.storage === "db") this.$modelPersistence.touch(query);
440
+ const storageKey = this._getPersisterStorageKey(options, query, queryKey);
441
+ const queue = getPersistenceQueue(storage);
442
+ queue.invalidate(storageKey);
443
+ if (options.sync === true && options.storage !== "db") try {
180
444
  storage.removeItem(storageKey);
181
- }, 0);
445
+ return Promise.resolve();
446
+ } catch (error) {
447
+ return this._handlePersisterOperation(Promise.reject(error), "remove");
448
+ }
449
+ return this._handlePersisterOperation(queue.enqueue(async () => {
450
+ await storage.removeItem(storageKey);
451
+ }), "remove");
452
+ }
453
+ async $persisterFlush() {
454
+ if (process.env.SERVER) return;
455
+ await getPersistenceQueue(localforage).flush();
456
+ }
457
+ _handlePersisterOperation(operation, action) {
458
+ operation.catch(() => this._reportPersisterError(action));
459
+ return operation;
460
+ }
461
+ _reportPersisterError(action) {
462
+ if (process.env.DEV) console.warn(`Encountered an error attempting to ${action} persisted query cache.`);
182
463
  }
183
464
  _createPersister(options) {
184
465
  options = this._adjustPersisterOptions(options);
185
466
  if (!options) return void 0;
186
- const persisterFn = experimental_createQueryPersister({
187
- storage: this._getPersisterStorage(options),
188
- maxAge: options.maxAge,
189
- refetchOnRestore: options.refetchOnRestore,
190
- prefix: options.prefix,
191
- buster: options.buster
192
- }).persisterFn;
193
- return createQueryPersister(persisterFn);
467
+ if (options.storage === "db") return this._createDbPersister(options);
468
+ const storage = this._getPersisterStorage(options);
469
+ const persisterOptions = options;
470
+ return createQueryPersister((queryFn, context, query) => {
471
+ const queue = storage ? getPersistenceQueue(storage) : void 0;
472
+ const storageKey = `${persisterOptions.prefix}-${query.queryHash}`;
473
+ const generation = queue?.generation(storageKey);
474
+ const current = () => generation === queue?.generation(storageKey);
475
+ const orderedStorage = storage && queue && !persisterOptions.sync ? {
476
+ getItem: (key) => queue.enqueue(() => current() ? storage.getItem(key) : void 0),
477
+ setItem: (key, data) => this._handlePersisterOperation(queue.enqueue(async () => {
478
+ if (current()) await storage.setItem(key, data);
479
+ }), "save"),
480
+ removeItem: (key) => this._handlePersisterOperation(queue.enqueue(async () => {
481
+ if (current()) await storage.removeItem(key);
482
+ }), "remove")
483
+ } : storage;
484
+ return experimental_createQueryPersister({
485
+ storage: orderedStorage,
486
+ maxAge: persisterOptions.maxAge,
487
+ refetchOnRestore: persisterOptions.refetchOnRestore,
488
+ prefix: persisterOptions.prefix,
489
+ buster: persisterOptions.buster
490
+ }).persisterFn(queryFn, context, query);
491
+ });
492
+ }
493
+ _createDbPersister(options) {
494
+ const ctx = this.ctx;
495
+ const persistence = this.$modelPersistence;
496
+ return async (queryFn, context, query) => {
497
+ if (ctx?.disposed) throw new CancelledError({
498
+ silent: true,
499
+ revert: false
500
+ });
501
+ persistence.touch(query);
502
+ const token = persistence.capture();
503
+ const storage = this._getPersisterStorage(options, query);
504
+ const queue = storage && getPersistenceQueue(storage);
505
+ const storageKey = this._getPersisterStorageKey(options, query, void 0, token);
506
+ const generation = queue?.generation(storageKey);
507
+ const current = () => !ctx?.disposed && persistence.current(token) && generation === queue?.generation(storageKey);
508
+ const assertCurrent = () => {
509
+ if (!current()) throw new CancelledError({
510
+ silent: true,
511
+ revert: false
512
+ });
513
+ };
514
+ const bypass = query.state.fetchMeta?.bypassPersister === true;
515
+ if (!bypass && storage && queue && query.state.data === void 0) {
516
+ this._persisterRestoreErrors.delete(storageKey);
517
+ const restored = await queue.enqueue(async () => {
518
+ if (!current()) return void 0;
519
+ try {
520
+ const storedData = await storage.getItem(storageKey);
521
+ if (!current()) return void 0;
522
+ const result = this._persisterLoad_inner(storedData, query, options);
523
+ if (!current()) return void 0;
524
+ if (result.remove) await storage.removeItem(storageKey);
525
+ return current() ? result : void 0;
526
+ } catch {
527
+ if (current()) {
528
+ this._persisterRestoreErrors.add(storageKey);
529
+ await storage.removeItem(storageKey);
530
+ this._reportPersisterError("restore");
531
+ }
532
+ return;
533
+ }
534
+ }).catch(() => {
535
+ if (current()) {
536
+ this._persisterRestoreErrors.add(storageKey);
537
+ this._reportPersisterError("restore");
538
+ }
539
+ });
540
+ assertCurrent();
541
+ if (restored?.data !== void 0) {
542
+ notifyManager.schedule(() => {
543
+ if (!current()) return;
544
+ if (restored.timestamps) query.setState(restored.timestamps);
545
+ if (options.refetchOnRestore === "always" || options.refetchOnRestore === true && query.isStale()) query.fetch().catch(() => void 0);
546
+ });
547
+ return restored.data;
548
+ }
549
+ }
550
+ assertCurrent();
551
+ let result;
552
+ try {
553
+ result = await queryFn(context);
554
+ } catch (error) {
555
+ assertCurrent();
556
+ throw error;
557
+ }
558
+ assertCurrent();
559
+ if (!bypass && storage && queue) notifyManager.schedule(() => {
560
+ if (!current()) return;
561
+ let data;
562
+ try {
563
+ data = this._serializePersister(query, options, result);
564
+ } catch (error) {
565
+ this._handlePersisterOperation(queue.enqueue(() => {
566
+ throw error;
567
+ }), "save");
568
+ return;
569
+ }
570
+ this._handlePersisterOperation(queue.enqueue(async () => {
571
+ if (current()) await storage.setItem(storageKey, data);
572
+ }), "save");
573
+ });
574
+ return result;
575
+ };
194
576
  }
195
577
  _adjustPersisterOptions(options) {
196
578
  if (options === false) return void 0;
197
- if (options === void 0 || options === true) options = {};
198
- else options = { ...options };
579
+ options = options === void 0 || options === true ? {} : { ...options };
199
580
  options.storage = options.storage ?? (options.sync ? "local" : "db");
581
+ if (options.storage === "db") options.sync = false;
200
582
  options.maxAge = options.maxAge ?? this.scopeSelf.config.persister.maxAge[options.storage];
201
583
  options.refetchOnRestore = options.refetchOnRestore ?? this.scopeSelf.config.persister.refetchOnRestore;
202
584
  options.prefix = options.prefix ?? this._getPersisterPrefix();
@@ -205,9 +587,10 @@ var BeanModelPersister = class extends BeanModelLast {
205
587
  options.deserializeDefault = options.deserializeDefault ?? JSON.parse;
206
588
  return options;
207
589
  }
208
- _getPersisterStorageKey(options, query) {
209
- if (options.storageKeySimplify) return String(query.queryKey[query.queryKey.length - 1]);
210
- return `${options.prefix}-${query.queryHash}`;
590
+ _getPersisterStorageKey(options, query, queryKey, token) {
591
+ const key = query?.queryKey ?? this._forceQueryKeyPrefix(queryKey);
592
+ const logicalKey = options.storageKeySimplify ? String(key[key.length - 1]) : `${options.prefix}-${query?.queryHash ?? hashKey(key)}`;
593
+ return options.storage === "db" ? getModelPersistenceStorageKey(this.sys.env.APP_NAME ?? "", (token ?? this.$modelPersistence.capture()).scope, logicalKey) : logicalKey;
211
594
  }
212
595
  _getPersisterStorage(options, query) {
213
596
  options = this._adjustPersisterOptions(options);
@@ -332,9 +715,13 @@ var BeanModelQuery = class extends BeanModelCookie {
332
715
  })?.setData(value);
333
716
  }
334
717
  async $clear() {
335
- const queries = this.$queryClient.getQueryCache().getAll();
336
- for (const query of queries) query?.setData(void 0);
337
- await localforage.clear();
718
+ const queue = getPersistenceQueue(localforage);
719
+ queue.invalidateAll();
720
+ const clear = process.env.SERVER ? Promise.resolve() : queue.enqueue(async () => {
721
+ await localforage.clear();
722
+ });
723
+ this.$modelPersistence.clear();
724
+ await clear;
338
725
  }
339
726
  $normalizeFilters(filters) {
340
727
  if (!filters) filters = {};
@@ -351,18 +738,10 @@ var BeanModelUseQuery = class extends BeanModelQuery {
351
738
  $useQuery(options, queryClient) {
352
739
  const queryKey = this.self._forceQueryKeyPrefix(options.queryKey);
353
740
  const persister = this._createPersister(options.meta?.persister);
354
- const optionsDefault = {};
355
- if (!cast(options).meta?.disableErrorEffect) optionsDefault.throwOnError = (error, query) => {
356
- let errorInfo = cast(options).meta?.errorInfo;
357
- if (typeof errorInfo === "function") errorInfo = errorInfo(error, query);
358
- if (!error.message.includes("useQuery:") && !checkErrorJwtExpired(error)) error.message = `useQuery: [${queryKey.join(", ")}]: ${error.message}`;
359
- this.$errorHandler(error, errorInfo ?? "useQuery");
360
- return false;
361
- };
362
- options = Object.assign(optionsDefault, options, {
741
+ options = this.$modelPersistence.guardOptions(Object.assign({}, options, {
363
742
  queryKey,
364
743
  persister
365
- });
744
+ }));
366
745
  if ((typeof options.meta?.persister === "object" && options.meta?.persister?.sync) !== true) {
367
746
  const staleTime = options.staleTime ?? this.scopeSelf.config.query.staleTime.async;
368
747
  const queryCacheExists = this.$queryFind({ queryKey })?.state.data !== void 0;
@@ -494,12 +873,14 @@ var BeanModelUseState = class extends BeanModelUseQuery {
494
873
  });
495
874
  }
496
875
  $useStateComputed(options) {
497
- const queryHash = hashKey(this.self._forceQueryKeyPrefix(options.queryKey));
876
+ const queryKey = this.self._forceQueryKeyPrefix(options.queryKey);
877
+ const queryHash = hashKey(queryKey);
498
878
  if (!this[SymbolUseComputeds][queryHash]) this[SymbolUseComputeds][queryHash] = this.$computed(options.queryFn, options.debugOptions);
499
879
  return this[SymbolUseComputeds][queryHash];
500
880
  }
501
881
  $useStateData(options, queryClient) {
502
- const queryHash = hashKey(this.self._forceQueryKeyPrefix(options.queryKey));
882
+ const queryKey = this.self._forceQueryKeyPrefix(options.queryKey);
883
+ const queryHash = hashKey(queryKey);
503
884
  if (!this[SymbolUseQueries][queryHash]) {
504
885
  const useQuery = this.$useQuery(options, queryClient);
505
886
  this[SymbolUseQueries][queryHash] = useQuery;
@@ -508,16 +889,24 @@ var BeanModelUseState = class extends BeanModelUseQuery {
508
889
  return this[SymbolUseQueries][queryHash];
509
890
  }
510
891
  _handleAsyncDataGet(options, queryClient, persister) {
892
+ if (this.ctx?.disposed) return void 0;
893
+ this.$modelPersistence.epoch;
511
894
  const query = this.$useStateData(options, queryClient);
512
895
  if (query.data !== void 0) return query.data;
513
896
  return this._handleAsyncDataGet_inner(options, queryClient, persister);
514
897
  }
515
898
  async _handleAsyncDataGet_inner(options, queryClient, persister) {
899
+ const ctx = this.ctx;
900
+ if (ctx?.disposed) return void 0;
901
+ const persistence = this.$modelPersistence;
516
902
  const queryKey = options.queryKey;
903
+ const token = persistence.capture();
517
904
  const query = this.$useStateData(options, queryClient);
518
905
  if (persister) {
906
+ const generation = this.$persisterGeneration(queryKey);
519
907
  const data = await this.$persisterLoad(queryKey);
520
- if (data !== void 0) this.$setQueryData(queryKey, data, false);
908
+ if (ctx?.disposed || !persistence.current(token) || generation !== this.$persisterGeneration(queryKey)) return;
909
+ if (data !== void 0 && query.data === void 0) this.$setQueryData(queryKey, data, false);
521
910
  }
522
911
  if (query.data === void 0) this._handleSyncDataGet_defaultData(queryKey, options);
523
912
  return query.data;
@@ -577,13 +966,7 @@ var BeanModelUseMutation = class extends BeanModelUseStateGeneral {
577
966
  mutationKey = this.self._forceQueryKeyPrefix(mutationKey);
578
967
  const mutationHash = hashKey(mutationKey);
579
968
  if (!this[SymbolUseMutations][mutationHash]) {
580
- const optionsDefault = {};
581
- if (!cast(mutationOptions).meta?.disableErrorEffect) optionsDefault.onError = (error, variables, context) => {
582
- let errorInfo = cast(mutationOptions).meta?.errorInfo;
583
- if (typeof errorInfo === "function") errorInfo = errorInfo(error, variables, context);
584
- this.$errorHandler(error, errorInfo ?? "useMutationData");
585
- };
586
- mutationOptions = Object.assign(optionsDefault, mutationOptions, { mutationKey });
969
+ mutationOptions = Object.assign({}, mutationOptions, { mutationKey });
587
970
  this[SymbolUseMutations][mutationHash] = this.$useMutation(mutationOptions, queryClient);
588
971
  }
589
972
  return this[SymbolUseMutations][mutationHash];
@@ -594,15 +977,45 @@ var BeanModelUseMutation = class extends BeanModelUseStateGeneral {
594
977
  var BeanModelFirst = class extends BeanModelUseMutation {};
595
978
  //#endregion
596
979
  //#region src/bean/bean.modelBase.ts
597
- var _dec$2, _dec2$2, _dec3, _class$2;
980
+ var _dec$2;
981
+ var _dec2$2;
982
+ var _dec3;
983
+ var _class$2;
598
984
  var BeanModelBase = (_dec$2 = Bean(), _dec2$2 = Virtual(), _dec3 = BeanInfo({ module: "a-model" }), _dec$2(_class$2 = _dec2$2(_class$2 = _dec3(_class$2 = class BeanModelBase extends BeanModelFirst {}) || _class$2) || _class$2) || _class$2);
599
985
  //#endregion
600
986
  //#region src/service/storage.ts
601
- var _dec$1, _dec2$1, _class$1;
987
+ var _dec$1;
988
+ var _dec2$1;
989
+ var _class$1;
602
990
  var ServiceStorage = (_dec$1 = Service(), _dec2$1 = BeanInfo({ module: "a-model" }), _dec$1(_class$1 = _dec2$1(_class$1 = class ServiceStorage extends BeanBase {
603
991
  constructor(...args) {
604
992
  super(...args);
605
993
  this._queryClient = void 0;
994
+ this._persistence = void 0;
995
+ }
996
+ get epoch() {
997
+ return this._persistence.epoch;
998
+ }
999
+ capture() {
1000
+ return this._persistence.capture();
1001
+ }
1002
+ current(token) {
1003
+ return this._persistence.current(token);
1004
+ }
1005
+ update(values, options) {
1006
+ this._persistence.update(values, options);
1007
+ }
1008
+ subscribe(listener) {
1009
+ return this._persistence.subscribe(listener);
1010
+ }
1011
+ clear() {
1012
+ this._persistence.clear();
1013
+ }
1014
+ touch(query) {
1015
+ this._persistence.touch(query);
1016
+ }
1017
+ guardOptions(options) {
1018
+ return this._persistence.guardOptions(options);
606
1019
  }
607
1020
  async moduleLoaded() {
608
1021
  const options = deepExtend({}, this.scope.config.queryClientConfig.defaultOptions);
@@ -611,10 +1024,12 @@ var ServiceStorage = (_dec$1 = Service(), _dec2$1 = BeanInfo({ module: "a-model"
611
1024
  options.queries.gcTime = Infinity;
612
1025
  }
613
1026
  const queryCache = new QueryCache();
614
- const vueQueryPluginOptions = { queryClient: this._queryClient = new QueryClient({
1027
+ const queryClient = this._queryClient = new QueryClient({
615
1028
  defaultOptions: options,
616
1029
  queryCache
617
- }) };
1030
+ });
1031
+ this._persistence = new ModelPersistenceContext(queryClient, this.sys.env.SITE_ID);
1032
+ const vueQueryPluginOptions = { queryClient };
618
1033
  this.app.vue.use(VueQueryPlugin, vueQueryPluginOptions);
619
1034
  }
620
1035
  async appInitialize() {
@@ -624,7 +1039,14 @@ var ServiceStorage = (_dec$1 = Service(), _dec2$1 = BeanInfo({ module: "a-model"
624
1039
  } });
625
1040
  this._queryClient?.clear();
626
1041
  });
627
- if (process.env.CLIENT && this.ctx.meta.$ssr.isRuntimeSsrPreHydration) hydrate(this._queryClient, this.ctx.meta.$ssr.stateDefer.query);
1042
+ if (process.env.CLIENT && this.ctx.meta.$ssr.isRuntimeSsrPreHydration) {
1043
+ const state = this.ctx.meta.$ssr.stateDefer.query;
1044
+ hydrate(this._queryClient, state);
1045
+ this._persistence.markHydrated((state?.queries ?? []).flatMap((item) => {
1046
+ const query = this._queryClient.getQueryCache().get(item.queryHash);
1047
+ return query ? [query] : [];
1048
+ }));
1049
+ }
628
1050
  }
629
1051
  }) || _class$1) || _class$1);
630
1052
  //#endregion
@@ -635,7 +1057,7 @@ var defaultOptions = {
635
1057
  refetchOnWindowFocus: false,
636
1058
  refetchOnMount: false,
637
1059
  refetchOnReconnect: true,
638
- gcTime: 1e3 * 60 * 5
1060
+ gcTime: 3e5
639
1061
  },
640
1062
  dehydrate: {
641
1063
  shouldDehydrateQuery(query) {
@@ -654,7 +1076,7 @@ var config = (_sys) => {
654
1076
  maxAge: {
655
1077
  cookie: void 0,
656
1078
  local: Infinity,
657
- db: 1e3 * 60 * 60 * 24
1079
+ db: 864e5
658
1080
  },
659
1081
  cookie: { options: {} },
660
1082
  refetchOnRestore: true
@@ -687,6 +1109,11 @@ var Monkey = class extends BeanSimple {
687
1109
  if (this._moduleSelf === module) await (await this.getStorage()).moduleLoaded();
688
1110
  }
689
1111
  async beanInit(bean, beanInstance) {
1112
+ bean.defineProperty(beanInstance, "$modelPersistence", {
1113
+ enumerable: false,
1114
+ configurable: true,
1115
+ get: () => this._storage
1116
+ });
690
1117
  bean.defineProperty(beanInstance, "$queryClient", {
691
1118
  enumerable: false,
692
1119
  configurable: true,
@@ -700,7 +1127,9 @@ var Monkey = class extends BeanSimple {
700
1127
  //#region src/.metadata/index.ts
701
1128
  /** monkey: end */
702
1129
  /** scope: begin */
703
- var _dec, _dec2, _class;
1130
+ var _dec;
1131
+ var _dec2;
1132
+ var _class;
704
1133
  var ScopeModuleAModel = (_dec = Scope(), _dec2 = BeanInfo({ module: "a-model" }), _dec(_class = _dec2(_class = class ScopeModuleAModel extends BeanScopeBase {}) || _class) || _class);
705
1134
  /** scope: end */
706
1135
  //#endregion
@@ -741,10 +1170,13 @@ async function $QueriesEnsureLoaded(fn1, fn2, fn3, fn4, fn5, ...fns) {
741
1170
  async function _QueryEnsureLoadedInner(fn) {
742
1171
  if (!fn) return;
743
1172
  const query = fn();
744
- if (query && query.data === void 0) await query.suspense();
1173
+ if (query && query.data === void 0) {
1174
+ const result = await query.suspense();
1175
+ if (result.error) throw result.error;
1176
+ }
745
1177
  return query;
746
1178
  }
747
1179
  //#endregion
748
- export { $QueriesEnsureLoaded, $QueryEnsureFresh, $QueryEnsureLoaded, $QueryGetFresh, BeanModelBase, Model, Monkey, ScopeModuleAModel, ServiceStorage, config, resolveMaxAgeTime, resolveStaleTime };
1180
+ export { $QueriesEnsureLoaded, $QueryEnsureFresh, $QueryEnsureLoaded, $QueryGetFresh, BeanModelBase, Model, ModelPersistenceContext, Monkey, ScopeModuleAModel, ServiceStorage, config, getModelPersistenceStorageKey, isModelPersistenceDb, resolveMaxAgeTime, resolveStaleTime };
749
1181
 
750
1182
  //# sourceMappingURL=index.js.map