pinia-plugin-persist-state 0.1.2-beta.0 → 0.1.2-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -1
- package/README.md +1 -0
- package/dist/pinia-plugin-persist-state.js +54 -49
- package/dist/types/store.d.ts +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,13 +5,14 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [0.1.2
|
|
8
|
+
## [0.1.2]
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
11
|
- Added an object-based `createPersistStatePlugin` configuration to explicitly select `storage`, `databaseName`, `objectStoreName`, and `cryptKey`.
|
|
12
12
|
- Added `persistenceKey` to persist a store under a stable or application-specific record key instead of its Pinia `$id`.
|
|
13
13
|
- Added opt-in cache metadata and restore policies through `cache`, including `maxAge`, schema `version`, and expiration/version-mismatch actions.
|
|
14
14
|
- Added `debounceMs` for trailing, per-store automatic persistence; automatic writes use a 200 ms delay by default.
|
|
15
|
+
- Added `restoreOnHydrate` to restore state store on client hydration.
|
|
15
16
|
- Added `flushPersistedState()` to immediately write the latest store state and cancel a pending debounced write.
|
|
16
17
|
- Added `transformState` to convert a filtered snapshot into an application-defined persistent representation before it is stored.
|
|
17
18
|
- Added `fake-indexeddb` integration tests for real IndexedDB updates, connection reuse, selective deletion, database isolation, and transaction errors.
|
package/README.md
CHANGED
|
@@ -112,6 +112,7 @@ Fields available when setting `storeOptions`:
|
|
|
112
112
|
- `transformState?: (state) => state` — Optional synchronous transformer applied to the filtered snapshot after selected properties are encrypted and before storage. Use it to convert application-specific values into a persistent representation.
|
|
113
113
|
- `watchMutation?: boolean` — When `true`, plugin watches store mutations and automatically persists changes.
|
|
114
114
|
- `debounceMs?: number` — Delay in milliseconds before an automatic persistence is written. Defaults to `200`; use `0` to persist each mutation immediately.
|
|
115
|
+
- `restoreOnHydrate?: boolean` - Set it to true, and the persisted state will be restored to the store.
|
|
115
116
|
|
|
116
117
|
`CacheOptions` supports `maxAge?: number`, `version?: number`, `onExpired?: 'ignore' | 'remove' | 'restore'`, and `onVersionMismatch?: 'ignore' | 'remove' | 'restore'`. Cache metadata is written only when `cache` is configured.
|
|
117
118
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Store as f, isEmpty as u, CustomConsole as _, PluginSubscriber as w } from "pinia-plugin-subscription";
|
|
2
2
|
import { toRaw as P, nextTick as v } from "vue";
|
|
3
|
-
class
|
|
3
|
+
class E {
|
|
4
4
|
_storage;
|
|
5
5
|
constructor(t) {
|
|
6
6
|
this._storage = t === "localStorage" ? localStorage : sessionStorage;
|
|
@@ -104,7 +104,7 @@ class m {
|
|
|
104
104
|
}
|
|
105
105
|
defineDb() {
|
|
106
106
|
const { databaseName: t, keyPath: e, name: s, objectStoreName: r, storage: a } = this._db_options, o = a ?? (s === "localStorage" || s === "sessionStorage" ? s : "indexedDB");
|
|
107
|
-
return o === "localStorage" || o === "sessionStorage" ? new
|
|
107
|
+
return o === "localStorage" || o === "sessionStorage" ? new E(o) : new l(t ?? s, r ?? "persistedStore", { keyPath: e });
|
|
108
108
|
}
|
|
109
109
|
async getItem(t) {
|
|
110
110
|
return this._db.getItem(t);
|
|
@@ -116,7 +116,7 @@ class m {
|
|
|
116
116
|
this._db instanceof l ? await this._db.setItem({ storeName: t, ...e }) : await this._db.setItem(e, t);
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
|
-
class
|
|
119
|
+
class T {
|
|
120
120
|
_store = /* @__PURE__ */ new Map();
|
|
121
121
|
async clear() {
|
|
122
122
|
this._store.clear();
|
|
@@ -159,7 +159,7 @@ const N = {
|
|
|
159
159
|
};
|
|
160
160
|
class I extends m {
|
|
161
161
|
defineDb() {
|
|
162
|
-
return this.dbName === "localStorage" || this.dbName === "sessionStorage" ? N : new
|
|
162
|
+
return this.dbName === "localStorage" || this.dbName === "sessionStorage" ? N : new T();
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
165
|
class h {
|
|
@@ -278,18 +278,12 @@ class g extends f {
|
|
|
278
278
|
options: e
|
|
279
279
|
}), this.toBeWatched() && (this._watchedStore.add(this.store.$id), this.storeSubscribe = this.storeSubscription.bind(this));
|
|
280
280
|
}
|
|
281
|
-
hydrate() {
|
|
282
|
-
return typeof window > "u" ? Promise.resolve() : this.remember();
|
|
283
|
-
}
|
|
284
281
|
augmentStore() {
|
|
285
282
|
const { isEncrypted: t, persistedPropertiesToEncrypt: e, watchMutation: s } = this.options;
|
|
286
283
|
t === void 0 && (this.options.isEncrypted = !1), e === void 0 && (this.options.persistedPropertiesToEncrypt = []), s === void 0 && (this.options.watchMutation = !1), this.stateHas("isLoading") || this.addToState("isLoading", !1), this.store.persistState = async () => await this.persist(), this.store.flushPersistedState = async () => await this.flushPersistedState(), this.store.remember = async () => await this.remember(), this.store.removePersistedState = this.removePersistedState.bind(this), this.store.watch = this.watch.bind(this), this.store.stopWatch = this.stopWatch.bind(this), this.store.hydrate = this.hydrate.bind(this);
|
|
287
284
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
}
|
|
291
|
-
async encryptProperty(t, e) {
|
|
292
|
-
return t.encrypt(e);
|
|
285
|
+
cancelScheduledPersist() {
|
|
286
|
+
this._persistTimer && (clearTimeout(this._persistTimer), this._persistTimer = void 0);
|
|
293
287
|
}
|
|
294
288
|
async cryptState(t, e = !1) {
|
|
295
289
|
return await new Promise(async (s) => {
|
|
@@ -312,6 +306,9 @@ class g extends f {
|
|
|
312
306
|
static customizeStore(t, e, s, r) {
|
|
313
307
|
return !e.storeOptions?.persist && e.storeOptions?.watchMutation && (e.storeOptions.persist = !0), super.customizeStore(t, e, s, r);
|
|
314
308
|
}
|
|
309
|
+
async decryptProperty(t, e) {
|
|
310
|
+
return t.decrypt(e);
|
|
311
|
+
}
|
|
315
312
|
definePersister(t) {
|
|
316
313
|
const e = this.getPersistedStoreOptions();
|
|
317
314
|
if (!e.storage || e.storage === e.storageOptions?.storage) {
|
|
@@ -327,8 +324,11 @@ class g extends f {
|
|
|
327
324
|
keyPath: "storeName"
|
|
328
325
|
});
|
|
329
326
|
}
|
|
330
|
-
|
|
331
|
-
return
|
|
327
|
+
async encryptProperty(t, e) {
|
|
328
|
+
return t.encrypt(e);
|
|
329
|
+
}
|
|
330
|
+
async flushPersistedState() {
|
|
331
|
+
this.cancelScheduledPersist(), await this.persist();
|
|
332
332
|
}
|
|
333
333
|
getPropertiesToEncrypt() {
|
|
334
334
|
return this.options?.persistedPropertiesToEncrypt ?? [];
|
|
@@ -336,12 +336,12 @@ class g extends f {
|
|
|
336
336
|
getPersistenceKey() {
|
|
337
337
|
return this.getPersistedStoreOptions().persistenceKey ?? this.store.$id;
|
|
338
338
|
}
|
|
339
|
+
getCachedState(t) {
|
|
340
|
+
return this.getCacheOptions() && t && "state" in t && "metadata" in t ? t : void 0;
|
|
341
|
+
}
|
|
339
342
|
getCacheOptions() {
|
|
340
343
|
return this.getPersistedStoreOptions().cache;
|
|
341
344
|
}
|
|
342
|
-
transformState(t) {
|
|
343
|
-
return this.getPersistedStoreOptions().transformState?.(t) ?? t;
|
|
344
|
-
}
|
|
345
345
|
getPersistedStoreOptions() {
|
|
346
346
|
return this.options;
|
|
347
347
|
}
|
|
@@ -360,13 +360,6 @@ class g extends f {
|
|
|
360
360
|
this.logError("getPersistedState()", { persistenceKey: e, e: s });
|
|
361
361
|
}
|
|
362
362
|
}
|
|
363
|
-
getCachedState(t) {
|
|
364
|
-
return this.getCacheOptions() && t && "state" in t && "metadata" in t ? t : void 0;
|
|
365
|
-
}
|
|
366
|
-
async shouldIgnoreCachedState(t, e) {
|
|
367
|
-
const s = this.getCacheOptions(), r = s.maxAge !== void 0 && t.metadata.cachedAt + s.maxAge < Date.now(), a = s.version !== void 0 && s.version !== t.metadata.version, o = r ? s.onExpired : a ? s.onVersionMismatch : void 0;
|
|
368
|
-
return !o || o === "restore" ? !1 : (o === "remove" && await this._persister.removeItem(e), !0);
|
|
369
|
-
}
|
|
370
363
|
async getStateToPersist() {
|
|
371
364
|
const t = this._excludedKeys, e = this.state, s = this._propertiesToEncrypt.size > 0, r = this._crypt;
|
|
372
365
|
s && await r.init();
|
|
@@ -387,12 +380,18 @@ class g extends f {
|
|
|
387
380
|
static hasRequiredKeys(t) {
|
|
388
381
|
return !!t?.persist || !!t?.watchMutation;
|
|
389
382
|
}
|
|
383
|
+
hydrate() {
|
|
384
|
+
return typeof window > "u" || !this.shouldRestoreOnHydrate() ? Promise.resolve() : this.remember();
|
|
385
|
+
}
|
|
390
386
|
initExcludedKeys() {
|
|
391
387
|
return /* @__PURE__ */ new Set([
|
|
392
388
|
...O,
|
|
393
389
|
...this.options.excludedKeys ?? []
|
|
394
390
|
]);
|
|
395
391
|
}
|
|
392
|
+
isEncrypted() {
|
|
393
|
+
return this.options.isEncrypted;
|
|
394
|
+
}
|
|
396
395
|
async persist() {
|
|
397
396
|
const t = ++this._persistenceVersion, e = await this.getStateToPersist();
|
|
398
397
|
if (this.debugLog(`persist() ${this.store.$id} - stateToPersist:`, { state: e }), !u(e)) {
|
|
@@ -403,14 +402,16 @@ class g extends f {
|
|
|
403
402
|
}), await a;
|
|
404
403
|
}
|
|
405
404
|
}
|
|
406
|
-
propertyShouldBePersisted(t) {
|
|
407
|
-
return !this._excludedKeys.has(t);
|
|
408
|
-
}
|
|
409
405
|
async remember() {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
406
|
+
this.state.isLoading = !0;
|
|
407
|
+
try {
|
|
408
|
+
let t = await this.getPersistedState();
|
|
409
|
+
t && !u(t) && this.store.$patch(t);
|
|
410
|
+
} catch (t) {
|
|
411
|
+
this.logError("remember()", { error: t, storeName: this.store.$id });
|
|
412
|
+
} finally {
|
|
413
|
+
this.state.isLoading = !1;
|
|
414
|
+
}
|
|
414
415
|
}
|
|
415
416
|
async removePersistedState() {
|
|
416
417
|
this.cancelScheduledPersist(), ++this._persistenceVersion;
|
|
@@ -420,6 +421,18 @@ class g extends f {
|
|
|
420
421
|
this._writeQueue = t.catch(() => {
|
|
421
422
|
}), await t;
|
|
422
423
|
}
|
|
424
|
+
schedulePersist(t) {
|
|
425
|
+
this.cancelScheduledPersist(), this._persistTimer = setTimeout(() => {
|
|
426
|
+
this._persistTimer = void 0, this.persist().then(() => this.store.mutationCallback?.(this.state, t)).catch((e) => this.logError("persist()", { error: e, storeName: this.store.$id }));
|
|
427
|
+
}, this.getDebounceMs());
|
|
428
|
+
}
|
|
429
|
+
async shouldIgnoreCachedState(t, e) {
|
|
430
|
+
const s = this.getCacheOptions(), r = s.maxAge !== void 0 && t.metadata.cachedAt + s.maxAge < Date.now(), a = s.version !== void 0 && s.version !== t.metadata.version, o = r ? s.onExpired : a ? s.onVersionMismatch : void 0;
|
|
431
|
+
return !o || o === "restore" ? !1 : (o === "remove" && await this._persister.removeItem(e), !0);
|
|
432
|
+
}
|
|
433
|
+
shouldRestoreOnHydrate() {
|
|
434
|
+
return this.options.restoreOnHydrate === !0;
|
|
435
|
+
}
|
|
423
436
|
stopWatch() {
|
|
424
437
|
this.options?.watchMutation && (this.options && (this.options.watchMutation = !1), this._watchedStore.delete(this.store.$id));
|
|
425
438
|
}
|
|
@@ -437,33 +450,25 @@ class g extends f {
|
|
|
437
450
|
this.store
|
|
438
451
|
]), t.type !== "patch object" && this.getWatchMutation() && this.schedulePersist(t);
|
|
439
452
|
}
|
|
440
|
-
cancelScheduledPersist() {
|
|
441
|
-
this._persistTimer && (clearTimeout(this._persistTimer), this._persistTimer = void 0);
|
|
442
|
-
}
|
|
443
|
-
schedulePersist(t) {
|
|
444
|
-
this.cancelScheduledPersist(), this._persistTimer = setTimeout(() => {
|
|
445
|
-
this._persistTimer = void 0, this.persist().then(() => this.store.mutationCallback?.(this.state, t)).catch((e) => this.logError("persist()", { error: e, storeName: this.store.$id }));
|
|
446
|
-
}, this.getDebounceMs());
|
|
447
|
-
}
|
|
448
|
-
async flushPersistedState() {
|
|
449
|
-
this.cancelScheduledPersist(), await this.persist();
|
|
450
|
-
}
|
|
451
453
|
toBeCrypted() {
|
|
452
454
|
return !!(this._crypt && this.getPropertiesToEncrypt());
|
|
453
455
|
}
|
|
454
456
|
toBeWatched() {
|
|
455
457
|
return !this._watchedStore.has(this.store.$id);
|
|
456
458
|
}
|
|
459
|
+
transformState(t) {
|
|
460
|
+
return this.getPersistedStoreOptions().transformState?.(t) ?? t;
|
|
461
|
+
}
|
|
457
462
|
watch() {
|
|
458
463
|
this.toBeWatched() && (this.options && (this.options.watchMutation = !0), this.storeSubscribe = this.storeSubscription.bind(this));
|
|
459
464
|
}
|
|
460
465
|
}
|
|
461
466
|
const b = "pinia-plugin-persist-state";
|
|
462
|
-
class
|
|
467
|
+
class C extends _ {
|
|
463
468
|
_pluginName = b;
|
|
464
469
|
}
|
|
465
|
-
const S = new
|
|
466
|
-
class
|
|
470
|
+
const S = new C();
|
|
471
|
+
class x extends w {
|
|
467
472
|
_resetStoreCallback = (t) => {
|
|
468
473
|
const { removePersistedState: e } = t;
|
|
469
474
|
typeof e == "function" && e();
|
|
@@ -487,13 +492,13 @@ class C extends w {
|
|
|
487
492
|
});
|
|
488
493
|
}
|
|
489
494
|
}
|
|
490
|
-
const y = new
|
|
495
|
+
const y = new x(), A = ["localStorage", "sessionStorage"];
|
|
491
496
|
function L(i, t) {
|
|
492
|
-
const e =
|
|
497
|
+
const e = K(i, t);
|
|
493
498
|
return y.pluginOptions = D(e), y;
|
|
494
499
|
}
|
|
495
|
-
function
|
|
496
|
-
return typeof i != "string" ? i :
|
|
500
|
+
function K(i, t) {
|
|
501
|
+
return typeof i != "string" ? i : A.includes(i) ? { storage: i, cryptKey: t } : {
|
|
497
502
|
storage: "indexedDB",
|
|
498
503
|
databaseName: i,
|
|
499
504
|
objectStoreName: "persistedStore",
|
package/dist/types/store.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export interface PersistedStoreOptions {
|
|
|
35
35
|
persist?: boolean;
|
|
36
36
|
persistenceKey?: string;
|
|
37
37
|
persistedPropertiesToEncrypt?: string[];
|
|
38
|
+
restoreOnHydrate?: boolean;
|
|
38
39
|
storage?: PersistStorage;
|
|
39
40
|
transformState?: PersistedStateTransformer;
|
|
40
41
|
watchMutation?: boolean;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pinia-plugin-persist-state",
|
|
3
3
|
"description": "Pinia plugin for persisting store state",
|
|
4
|
-
"version": "0.1.2-beta.
|
|
4
|
+
"version": "0.1.2-beta.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Alexandre Bidaud <alexbidaud85@gmail.com> (https://github.com/AlxBDo)",
|
|
7
7
|
"license": "MIT",
|