@plq/use-persisted-state 1.3.0 → 1.4.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.
Files changed (101) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +179 -50
  3. package/esm/index.d.mts +13 -0
  4. package/esm/index.mjs +10 -0
  5. package/esm/storages/browser-storage.d.mts +1 -0
  6. package/esm/storages/browser-storage.mjs +5 -0
  7. package/esm/storages/chrome-storage.d.mts +1 -0
  8. package/esm/storages/chrome-storage.mjs +5 -0
  9. package/esm/storages/local-storage.d.mts +1 -0
  10. package/esm/storages/local-storage.mjs +3 -0
  11. package/esm/storages/session-storage.d.mts +1 -0
  12. package/esm/storages/session-storage.mjs +3 -0
  13. package/lib/@types/storage.d.ts.map +1 -1
  14. package/lib/create-async-persisted-state.d.ts +2 -2
  15. package/lib/create-async-persisted-state.d.ts.map +1 -1
  16. package/lib/create-async-persisted-state.js +136 -17
  17. package/lib/create-async-persisted-state.js.map +1 -1
  18. package/lib/create-persisted-state.d.ts +2 -2
  19. package/lib/create-persisted-state.d.ts.map +1 -1
  20. package/lib/create-persisted-state.js +76 -15
  21. package/lib/create-persisted-state.js.map +1 -1
  22. package/lib/index.d.ts +6 -5
  23. package/lib/index.d.ts.map +1 -1
  24. package/lib/index.js +4 -2
  25. package/lib/index.js.map +1 -1
  26. package/lib/storages/browser-storage.d.ts +2 -2
  27. package/lib/storages/browser-storage.d.ts.map +1 -1
  28. package/lib/storages/browser-storage.js +6 -26
  29. package/lib/storages/browser-storage.js.map +1 -1
  30. package/lib/storages/chrome-storage.d.ts +2 -2
  31. package/lib/storages/chrome-storage.d.ts.map +1 -1
  32. package/lib/storages/chrome-storage.js +10 -27
  33. package/lib/storages/chrome-storage.js.map +1 -1
  34. package/lib/storages/local-storage.d.ts +1 -1
  35. package/lib/storages/local-storage.d.ts.map +1 -1
  36. package/lib/storages/local-storage.js +3 -1
  37. package/lib/storages/local-storage.js.map +1 -1
  38. package/lib/storages/session-storage.d.ts +1 -1
  39. package/lib/storages/session-storage.d.ts.map +1 -1
  40. package/lib/storages/session-storage.js +3 -1
  41. package/lib/storages/session-storage.js.map +1 -1
  42. package/lib/utils/change-notifier.d.ts +18 -0
  43. package/lib/utils/change-notifier.d.ts.map +1 -0
  44. package/lib/utils/change-notifier.js +38 -0
  45. package/lib/utils/change-notifier.js.map +1 -0
  46. package/lib/utils/create-web-storage.d.ts +10 -2
  47. package/lib/utils/create-web-storage.d.ts.map +1 -1
  48. package/lib/utils/create-web-storage.js +72 -75
  49. package/lib/utils/create-web-storage.js.map +1 -1
  50. package/lib/utils/extension-storage.d.ts +38 -0
  51. package/lib/utils/extension-storage.d.ts.map +1 -0
  52. package/lib/utils/extension-storage.js +66 -0
  53. package/lib/utils/extension-storage.js.map +1 -0
  54. package/lib/utils/get-new-item.d.ts +13 -1
  55. package/lib/utils/get-new-item.d.ts.map +1 -1
  56. package/lib/utils/get-new-item.js +27 -7
  57. package/lib/utils/get-new-item.js.map +1 -1
  58. package/lib/utils/get-new-value.d.ts +1 -1
  59. package/lib/utils/get-new-value.d.ts.map +1 -1
  60. package/lib/utils/get-persisted-value.d.ts +8 -1
  61. package/lib/utils/get-persisted-value.d.ts.map +1 -1
  62. package/lib/utils/get-persisted-value.js +19 -5
  63. package/lib/utils/get-persisted-value.js.map +1 -1
  64. package/lib/utils/is-async-storage.d.ts +7 -0
  65. package/lib/utils/is-async-storage.d.ts.map +1 -1
  66. package/lib/utils/is-async-storage.js +51 -20
  67. package/lib/utils/is-async-storage.js.map +1 -1
  68. package/lib/utils/storage-event-router.d.ts +17 -0
  69. package/lib/utils/storage-event-router.d.ts.map +1 -0
  70. package/lib/utils/storage-event-router.js +54 -0
  71. package/lib/utils/storage-event-router.js.map +1 -0
  72. package/lib/utils/use-storage-handler.d.ts +14 -3
  73. package/lib/utils/use-storage-handler.d.ts.map +1 -1
  74. package/lib/utils/use-storage-handler.js +109 -26
  75. package/lib/utils/use-storage-handler.js.map +1 -1
  76. package/package.json +97 -41
  77. package/src/@types/storage.ts +13 -13
  78. package/src/create-async-persisted-state.ts +125 -20
  79. package/src/create-persisted-state.ts +59 -23
  80. package/src/index.ts +12 -7
  81. package/src/storages/browser-storage.ts +8 -36
  82. package/src/storages/chrome-storage.ts +24 -46
  83. package/src/storages/local-storage.ts +3 -1
  84. package/src/storages/session-storage.ts +3 -1
  85. package/src/utils/change-notifier.ts +43 -0
  86. package/src/utils/create-web-storage.ts +74 -77
  87. package/src/utils/extension-storage.ts +79 -0
  88. package/src/utils/get-new-item.ts +32 -9
  89. package/src/utils/get-new-value.ts +1 -1
  90. package/src/utils/get-persisted-value.ts +22 -7
  91. package/src/utils/is-async-storage.ts +72 -27
  92. package/src/utils/storage-event-router.ts +67 -0
  93. package/src/utils/use-storage-handler.ts +144 -36
  94. package/storages/browser-storage.d.ts +1 -0
  95. package/storages/browser-storage.js +11 -0
  96. package/storages/chrome-storage.d.ts +1 -0
  97. package/storages/chrome-storage.js +11 -0
  98. package/storages/local-storage.d.ts +1 -0
  99. package/storages/local-storage.js +9 -0
  100. package/storages/session-storage.d.ts +1 -0
  101. package/storages/session-storage.js +9 -0
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addRoute = addRoute;
4
+ exports.removeRoute = removeRoute;
5
+ const routes = new Set();
6
+ let subscription = null;
7
+ function routeStorageEvent(event) {
8
+ if (!event.key)
9
+ return;
10
+ const changes = {
11
+ [event.key]: {
12
+ newValue: event.newValue,
13
+ oldValue: event.oldValue,
14
+ },
15
+ };
16
+ for (const route of routes) {
17
+ // A browser always names the area it changed, so real cross-tab events
18
+ // reach only the adapter of that area. An event a consumer synthesized —
19
+ // the sole way to test cross-tab sync under jsdom, and a common way to
20
+ // notify the writing tab in production — often cannot name one: the
21
+ // StorageEvent constructor rejects a mocked storage there. Those still go
22
+ // everywhere, exactly as they did before areas were told apart.
23
+ if (event.storageArea == null || event.storageArea === route.storage) {
24
+ route.fire(changes);
25
+ }
26
+ }
27
+ }
28
+ // A runtime can provide storage without the DOM event API, and can provide half
29
+ // of that API: subscribing where nothing can unsubscribe strands a listener and
30
+ // makes releasing it throw, so both halves are required before taking one.
31
+ function canSubscribe() {
32
+ return typeof globalThis.addEventListener === 'function' && typeof globalThis.removeEventListener === 'function';
33
+ }
34
+ /**
35
+ * Starts routing DOM storage events to `route`. Every adapter shares one
36
+ * subscription on the global object, so creating adapters nobody listens to
37
+ * costs nothing.
38
+ */
39
+ function addRoute(route) {
40
+ routes.add(route);
41
+ if (!subscription && canSubscribe()) {
42
+ subscription = routeStorageEvent;
43
+ globalThis.addEventListener('storage', subscription);
44
+ }
45
+ }
46
+ /** Stops routing to `route`, releasing the shared subscription with the last one. */
47
+ function removeRoute(route) {
48
+ routes.delete(route);
49
+ if (routes.size === 0 && subscription) {
50
+ globalThis.removeEventListener('storage', subscription);
51
+ subscription = null;
52
+ }
53
+ }
54
+ //# sourceMappingURL=storage-event-router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-event-router.js","sourceRoot":"","sources":["../../src/utils/storage-event-router.ts"],"names":[],"mappings":";;AA+CA,4BAQC;AAGD,kCAQC;AA1DD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAqB,CAAA;AAE3C,IAAI,YAAY,GAA2C,IAAI,CAAA;AAE/D,SAAS,iBAAiB,CAAC,KAAmB;IAC5C,IAAI,CAAC,KAAK,CAAC,GAAG;QAAE,OAAM;IAEtB,MAAM,OAAO,GAAG;QACd,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;YACX,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACzB;KACF,CAAA;IAED,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,uEAAuE;QACvE,yEAAyE;QACzE,uEAAuE;QACvE,oEAAoE;QACpE,0EAA0E;QAC1E,gEAAgE;QAChE,IAAI,KAAK,CAAC,WAAW,IAAI,IAAI,IAAI,KAAK,CAAC,WAAW,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;YACrE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACrB,CAAC;IACH,CAAC;AACH,CAAC;AAED,gFAAgF;AAChF,gFAAgF;AAChF,2EAA2E;AAC3E,SAAS,YAAY;IACnB,OAAO,OAAO,UAAU,CAAC,gBAAgB,KAAK,UAAU,IAAI,OAAO,UAAU,CAAC,mBAAmB,KAAK,UAAU,CAAA;AAClH,CAAC;AAED;;;;GAIG;AACH,SAAgB,QAAQ,CAAC,KAAwB;IAC/C,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IAEjB,IAAI,CAAC,YAAY,IAAI,YAAY,EAAE,EAAE,CAAC;QACpC,YAAY,GAAG,iBAAiB,CAAA;QAEhC,UAAU,CAAC,gBAAgB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAA;IACtD,CAAC;AACH,CAAC;AAED,qFAAqF;AACrF,SAAgB,WAAW,CAAC,KAAwB;IAClD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAEpB,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,YAAY,EAAE,CAAC;QACtC,UAAU,CAAC,mBAAmB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAA;QAEvD,YAAY,GAAG,IAAI,CAAA;IACrB,CAAC;AACH,CAAC"}
@@ -1,4 +1,15 @@
1
- import React from 'react';
2
- import { AsyncStorage, Storage } from '../@types/storage';
3
- export default function <T>(key: string, storageKey: string, setState: React.Dispatch<React.SetStateAction<T>>, storage: AsyncStorage | Storage, initialValue: T | (() => T)): void;
1
+ import type React from 'react';
2
+ import type { AsyncStorage, Storage } from '../@types/storage';
3
+ /**
4
+ * Remembers an entry the hook is about to write, so the change the backend
5
+ * reports for it can be told apart from someone else's write.
6
+ *
7
+ * Recorded before the write, because a backend may report it before the call
8
+ * settles. One slot was not enough: a backend free to report after its write
9
+ * settles - chrome and browser storage both are - lets the next write overwrite
10
+ * the record of a write still waiting to be reported, and that unsuppressed echo
11
+ * then puts the earlier value back over the later one.
12
+ */
13
+ export declare function recordOwnWrite(pendingOwnWrites: React.RefObject<string[]>, item: string): void;
14
+ export default function useStorageHandler<T>(key: string, storageKey: string, applyValue: (value: T) => void, storage: AsyncStorage | Storage, initialValue: T | (() => T), pendingOwnWrites: React.RefObject<string[]>): void;
4
15
  //# sourceMappingURL=use-storage-handler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-storage-handler.d.ts","sourceRoot":"","sources":["../../src/utils/use-storage-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAA;AACtC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAiB,MAAM,mBAAmB,CAAA;AAiDxE,MAAM,CAAC,OAAO,WAAU,CAAC,EACvB,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EACjD,OAAO,EAAE,YAAY,GAAG,OAAO,EAC/B,YAAY,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAC1B,IAAI,CAYN"}
1
+ {"version":3,"file":"use-storage-handler.d.ts","sourceRoot":"","sources":["../../src/utils/use-storage-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAiB,MAAM,mBAAmB,CAAA;AAkE7E;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,gBAAgB,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAM9F;AA+DD,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,CAAC,EACzC,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,EAC9B,OAAO,EAAE,YAAY,GAAG,OAAO,EAC/B,YAAY,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAC3B,gBAAgB,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,GAC1C,IAAI,CAsBN"}
@@ -1,48 +1,131 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = default_1;
3
+ exports.recordOwnWrite = recordOwnWrite;
4
+ exports.default = useStorageHandler;
4
5
  const react_1 = require("react");
5
6
  const is_1 = require("@plq/is");
6
- function getValue(key, value) {
7
- let newState = null;
7
+ function readStoredValue(key, entry) {
8
+ let parsed;
8
9
  try {
9
- newState = JSON.parse(value);
10
+ parsed = JSON.parse(entry);
10
11
  }
11
12
  catch (err) {
12
- console.error('use-persisted-state: Can\'t parse value from storage', err);
13
+ console.error("use-persisted-state: Can't parse value from storage", err);
14
+ return { status: 'unavailable' };
13
15
  }
14
- return newState && key in newState ? newState[key] : null;
16
+ // A foreign entry can be any JSON value, and `in` throws on a primitive. This
17
+ // runs inside the adapter's notify loop, where a throw also cuts off every
18
+ // listener queued behind this one.
19
+ if (!(0, is_1.isObject)(parsed) || !(key in parsed))
20
+ return { status: 'unavailable' };
21
+ return { status: 'stored', value: parsed[key] };
15
22
  }
16
- function useStorageHandler(itemKey, storageKey, setState, initialValue) {
23
+ // Restores the initial value when the whole entry is removed from the storage.
24
+ function applyRemoval(change, itemKey, applyValue, latestInitialValue) {
25
+ if (change.oldValue === null || change.oldValue === undefined)
26
+ return;
27
+ const oldValue = readStoredValue(itemKey, change.oldValue);
28
+ const declaredInitialValue = latestInitialValue.current;
29
+ // Compared against the resolved value: a factory is never equal to what it
30
+ // produces, so comparing the declaration re-applies the initial value on every
31
+ // removal.
32
+ const initialValue = (0, is_1.isFunction)(declaredInitialValue) ? declaredInitialValue() : declaredInitialValue;
33
+ // An entry with no readable value for the key cannot be shown to have held the
34
+ // initial value, so the fallback is applied rather than skipped. The entry is
35
+ // gone either way, and stale state left in place is worse than a redundant
36
+ // restore.
37
+ if (oldValue.status === 'stored' && oldValue.value === initialValue)
38
+ return;
39
+ applyValue(initialValue);
40
+ }
41
+ // A backend that reports nothing back leaves every record unmatched, so the list
42
+ // needs a ceiling or it grows by one string per write for as long as the hook is
43
+ // mounted. A bound, not a tuning: a backend that does report drains the list on
44
+ // every echo, and one that reports late is what the list exists for.
45
+ const MAX_PENDING_OWN_WRITES = 8;
46
+ /**
47
+ * Remembers an entry the hook is about to write, so the change the backend
48
+ * reports for it can be told apart from someone else's write.
49
+ *
50
+ * Recorded before the write, because a backend may report it before the call
51
+ * settles. One slot was not enough: a backend free to report after its write
52
+ * settles - chrome and browser storage both are - lets the next write overwrite
53
+ * the record of a write still waiting to be reported, and that unsuppressed echo
54
+ * then puts the earlier value back over the later one.
55
+ */
56
+ function recordOwnWrite(pendingOwnWrites, item) {
57
+ const pending = pendingOwnWrites.current;
58
+ if (pending.length >= MAX_PENDING_OWN_WRITES)
59
+ pending.shift();
60
+ pending.push(item);
61
+ }
62
+ /**
63
+ * Reports whether this change is a write the hook itself made, forgetting the
64
+ * record when it is. A backend reports a write to every listener, the one that
65
+ * made it included, and applying that echo is the storage-to-state re-sync this
66
+ * hook was rebuilt without, arriving by another road: it decodes the entry again
67
+ * and hands the caller an equal value with a new identity, plus a render for a
68
+ * value it already holds.
69
+ *
70
+ * The price, accepted knowingly: for a value JSON cannot carry, the writer keeps
71
+ * what it set - NaN - while every other component on the key decodes the null
72
+ * that reached storage, so the two disagree until one of them remounts. Applying
73
+ * the echo would settle that disagreement and bring back both the second
74
+ * storage-to-state path and the render per write, so it is not the repair it
75
+ * looks like.
76
+ *
77
+ * Forgetting on the first match keeps a later identical entry a change. Another
78
+ * hook writing the same bytes is suppressed along with it, and nothing is lost:
79
+ * it would have replaced a value with an equal one.
80
+ */
81
+ function consumeOwnWriteEcho(entry, pendingOwnWrites) {
82
+ const matched = pendingOwnWrites.current.indexOf(entry);
83
+ if (matched === -1)
84
+ return false;
85
+ // Everything recorded before the reported entry goes with it: a backend applies
86
+ // writes in the order it took them and reports them in that order too, so a
87
+ // record still unmatched by now has no echo left to wait for.
88
+ pendingOwnWrites.current.splice(0, matched + 1);
89
+ return true;
90
+ }
91
+ // Builds the change handler. Not a hook, despite living next to one.
92
+ function createStorageHandler(itemKey, storageKey, applyValue, latestInitialValue, pendingOwnWrites) {
17
93
  return (changes) => {
18
- Object.entries(changes).forEach(([key, change]) => {
19
- if (key === storageKey
20
- && (change.newValue === null || change.newValue === undefined)
21
- && change.oldValue !== null
22
- && change.oldValue !== undefined) {
23
- const oldValue = getValue(itemKey, change.oldValue);
24
- if (oldValue !== initialValue)
25
- setState((0, is_1.isFunction)(initialValue) ? initialValue() : initialValue);
26
- }
27
- if (key === storageKey
28
- && change.newValue !== null
29
- && change.newValue !== undefined) {
30
- const newValue = getValue(itemKey, change.newValue);
31
- if (newValue !== null)
32
- setState(newValue);
94
+ for (const [key, change] of Object.entries(changes)) {
95
+ if (key !== storageKey)
96
+ continue;
97
+ // Ahead of the echo check, which needs an entry to match and a removal has
98
+ // none. A removal is never one of this hook's own writes anyway: only the
99
+ // entries it stores are recorded.
100
+ if (change.newValue === null || change.newValue === undefined) {
101
+ applyRemoval(change, itemKey, applyValue, latestInitialValue);
102
+ continue;
33
103
  }
34
- });
104
+ if (consumeOwnWriteEcho(change.newValue, pendingOwnWrites))
105
+ continue;
106
+ const newValue = readStoredValue(itemKey, change.newValue);
107
+ if (newValue.status === 'stored')
108
+ applyValue(newValue.value);
109
+ }
35
110
  };
36
111
  }
37
- function default_1(key, storageKey, setState, storage, initialValue) {
112
+ function useStorageHandler(key, storageKey, applyValue, storage, initialValue, pendingOwnWrites) {
113
+ // A removal restores the initial value the hook holds now, the same one the
114
+ // key-change path reads, so a caller whose default travels with its data gets
115
+ // that record's default back rather than the mounted one. Tracked through a ref
116
+ // rather than a dependency because an inline object or factory has a new
117
+ // identity every render, which would tear the subscription down and rebuild it;
118
+ // written during render, as the key the hook is rendering for is.
119
+ const latestInitialValue = (0, react_1.useRef)(initialValue);
120
+ latestInitialValue.current = initialValue;
38
121
  (0, react_1.useEffect)(() => {
39
- const handleStorage = useStorageHandler(key, storageKey, setState, initialValue);
122
+ const handleStorage = createStorageHandler(key, storageKey, applyValue, latestInitialValue, pendingOwnWrites);
40
123
  storage.onChanged.addListener(handleStorage);
41
124
  return () => {
42
125
  if (storage.onChanged.hasListener(handleStorage)) {
43
126
  storage.onChanged.removeListener(handleStorage);
44
127
  }
45
128
  };
46
- }, [initialValue, key, storage.onChanged, storageKey, setState]);
129
+ }, [key, storage.onChanged, storageKey, applyValue, pendingOwnWrites]);
47
130
  }
48
131
  //# sourceMappingURL=use-storage-handler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-storage-handler.js","sourceRoot":"","sources":["../../src/utils/use-storage-handler.ts"],"names":[],"mappings":";;AAkDA,4BAkBC;AApED,iCAAsC;AAEtC,gCAAoC;AAEpC,SAAS,QAAQ,CAAI,GAAW,EAAE,KAAa;IAC7C,IAAI,QAAQ,GAAG,IAAI,CAAA;IAEnB,IAAI,CAAC;QACH,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,sDAAsD,EAAE,GAAG,CAAC,CAAA;IAC5E,CAAC;IAED,OAAO,QAAQ,IAAI,GAAG,IAAI,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAM,CAAC,CAAC,CAAC,IAAI,CAAA;AAChE,CAAC;AAED,SAAS,iBAAiB,CACxB,OAAe,EACf,UAAkB,EAClB,QAAiD,EACjD,YAA2B;IAE3B,OAAO,CAAC,OAAyC,EAAQ,EAAE;QACzD,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,EAAE;YAChD,IACE,GAAG,KAAK,UAAU;mBACf,CACD,MAAM,CAAC,QAAQ,KAAK,IAAI,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS,CAC1D;mBACE,MAAM,CAAC,QAAQ,KAAK,IAAI;mBACxB,MAAM,CAAC,QAAQ,KAAK,SAAS,EAChC,CAAC;gBACD,MAAM,QAAQ,GAAG,QAAQ,CAAI,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAA;gBAEtD,IAAI,QAAQ,KAAK,YAAY;oBAAE,QAAQ,CAAC,IAAA,eAAU,EAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAA;YACnG,CAAC;YAED,IACE,GAAG,KAAK,UAAU;mBACf,MAAM,CAAC,QAAQ,KAAK,IAAI;mBACxB,MAAM,CAAC,QAAQ,KAAK,SAAS,EAChC,CAAC;gBACD,MAAM,QAAQ,GAAG,QAAQ,CAAI,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAA;gBAEtD,IAAI,QAAQ,KAAK,IAAI;oBAAE,QAAQ,CAAC,QAAQ,CAAC,CAAA;YAC3C,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC,CAAA;AACH,CAAC;AAED,mBACE,GAAW,EACX,UAAkB,EAClB,QAAiD,EACjD,OAA+B,EAC/B,YAA2B;IAE3B,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,MAAM,aAAa,GAAG,iBAAiB,CAAI,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAA;QAEnF,OAAO,CAAC,SAAS,CAAC,WAAW,CAAC,aAAa,CAAC,CAAA;QAE5C,OAAO,GAAG,EAAE;YACV,IAAI,OAAO,CAAC,SAAS,CAAC,WAAW,CAAC,aAAa,CAAC,EAAE,CAAC;gBACjD,OAAO,CAAC,SAAS,CAAC,cAAc,CAAC,aAAa,CAAC,CAAA;YACjD,CAAC;QACH,CAAC,CAAA;IACH,CAAC,EAAE,CAAC,YAAY,EAAE,GAAG,EAAE,OAAO,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAA;AAClE,CAAC"}
1
+ {"version":3,"file":"use-storage-handler.js","sourceRoot":"","sources":["../../src/utils/use-storage-handler.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,49 +1,110 @@
1
1
  {
2
2
  "name": "@plq/use-persisted-state",
3
- "version": "1.3.0",
3
+ "version": "1.4.1",
4
4
  "description": "useState hook with persistence in storage",
5
+ "type": "commonjs",
5
6
  "main": "lib/index.js",
6
7
  "types": "lib/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": {
11
+ "types": "./esm/index.d.mts",
12
+ "default": "./esm/index.mjs"
13
+ },
14
+ "require": {
15
+ "types": "./lib/index.d.ts",
16
+ "default": "./lib/index.js"
17
+ },
18
+ "default": "./lib/index.js"
19
+ },
20
+ "./storages/local-storage": {
21
+ "import": {
22
+ "types": "./esm/storages/local-storage.d.mts",
23
+ "default": "./esm/storages/local-storage.mjs"
24
+ },
25
+ "require": {
26
+ "types": "./lib/storages/local-storage.d.ts",
27
+ "default": "./lib/storages/local-storage.js"
28
+ },
29
+ "default": "./lib/storages/local-storage.js"
30
+ },
31
+ "./storages/session-storage": {
32
+ "import": {
33
+ "types": "./esm/storages/session-storage.d.mts",
34
+ "default": "./esm/storages/session-storage.mjs"
35
+ },
36
+ "require": {
37
+ "types": "./lib/storages/session-storage.d.ts",
38
+ "default": "./lib/storages/session-storage.js"
39
+ },
40
+ "default": "./lib/storages/session-storage.js"
41
+ },
42
+ "./storages/browser-storage": {
43
+ "import": {
44
+ "types": "./esm/storages/browser-storage.d.mts",
45
+ "default": "./esm/storages/browser-storage.mjs"
46
+ },
47
+ "require": {
48
+ "types": "./lib/storages/browser-storage.d.ts",
49
+ "default": "./lib/storages/browser-storage.js"
50
+ },
51
+ "default": "./lib/storages/browser-storage.js"
52
+ },
53
+ "./storages/chrome-storage": {
54
+ "import": {
55
+ "types": "./esm/storages/chrome-storage.d.mts",
56
+ "default": "./esm/storages/chrome-storage.mjs"
57
+ },
58
+ "require": {
59
+ "types": "./lib/storages/chrome-storage.d.ts",
60
+ "default": "./lib/storages/chrome-storage.js"
61
+ },
62
+ "default": "./lib/storages/chrome-storage.js"
63
+ },
64
+ "./lib": "./lib/index.js",
65
+ "./lib/*.js": "./lib/*.js",
66
+ "./lib/*": "./lib/*.js",
67
+ "./src": "./src/index.ts",
68
+ "./src/*.ts": "./src/*.ts",
69
+ "./src/*": "./src/*.ts",
70
+ "./package.json": "./package.json"
71
+ },
7
72
  "engines": {
8
73
  "node": ">=16.0.0"
9
74
  },
10
75
  "files": [
11
76
  "/src",
12
77
  "/lib",
78
+ "/esm",
79
+ "/storages",
13
80
  "LICENSE"
14
81
  ],
15
82
  "scripts": {
16
83
  "build": "tsc",
17
84
  "build:watch": "tsc --watch",
18
85
  "build:types": "tsc --emitDeclarationOnly",
86
+ "typecheck": "tsc -p tsconfig.test.json --noEmit",
19
87
  "test": "jest --verbose",
88
+ "test:random": "jest --randomize --showSeed",
20
89
  "test:watch": "npm test -- --watchAll",
21
- "lint": "eslint ./src/**/*.ts",
90
+ "lint": "biome check",
91
+ "lint:fix": "biome check --write",
92
+ "check:package": "node scripts/check-package.mjs",
93
+ "check:publint": "npx --yes publint",
94
+ "check:attw": "npx --yes @arethetypeswrong/cli --pack . --exclude-entrypoints lib",
95
+ "check:smoke": "node scripts/smoke.mjs",
96
+ "check:types": "tsc -p tsconfig.package.json --noEmit",
97
+ "prepublishOnly": "npm run build",
98
+ "format": "biome format --write",
22
99
  "release": "dotenv release-it",
23
- "demo": "vite demo",
24
- "demo:build": "vite build demo",
25
- "demo:preview": "vite preview demo"
100
+ "demo": "vite",
101
+ "demo:build": "vite build",
102
+ "demo:preview": "vite preview"
26
103
  },
27
104
  "repository": {
28
105
  "type": "git",
29
106
  "url": "git+https://github.com/Akurganow/use-persisted-state.git"
30
107
  },
31
- "release-it": {
32
- "hooks": {
33
- "before:init": [
34
- "npm run lint",
35
- "npm test",
36
- "npm run build"
37
- ],
38
- "after:release": "echo Successfully released ${name} v${version} to ${repo.repository}."
39
- },
40
- "plugins": {
41
- "@release-it/conventional-changelog": {
42
- "preset": "conventionalcommits",
43
- "infile": "CHANGELOG.md"
44
- }
45
- }
46
- },
47
108
  "keywords": [
48
109
  "react",
49
110
  "hook",
@@ -54,8 +115,9 @@
54
115
  "persist",
55
116
  "sync"
56
117
  ],
57
- "author": "Alex Kurganov <me@akurganow.ru>",
118
+ "author": "Alexander Kurganov <a.kurganow@gmail.com>",
58
119
  "license": "MIT",
120
+ "funding": "https://ko-fi.com/akurganow",
59
121
  "publishConfig": {
60
122
  "access": "public"
61
123
  },
@@ -64,38 +126,32 @@
64
126
  },
65
127
  "homepage": "https://github.com/Akurganow/use-persisted-state#readme",
66
128
  "devDependencies": {
67
- "@eslint/js": "^9.32.0",
68
- "@release-it/conventional-changelog": "^10.0.1",
69
- "@testing-library/jest-dom": "^6.1.3",
129
+ "@biomejs/biome": "^2.5.6",
130
+ "@release-it/conventional-changelog": "^12.0.0",
131
+ "@testing-library/dom": "^10.4.1",
132
+ "@testing-library/jest-dom": "^7.0.0",
70
133
  "@testing-library/react": "^16.3.0",
71
- "@types/chrome": "0.1.1",
72
- "@types/firefox-webext-browser": "^120.0.0",
134
+ "@types/chrome": "^0.2.2",
135
+ "@types/firefox-webext-browser": "^143.0.0",
73
136
  "@types/jest": "^30.0.0",
74
137
  "@types/react": "^19.0.0",
75
138
  "@types/react-dom": "^19.0.0",
76
- "@vitejs/plugin-react": "^4.7.0",
77
- "conventional-changelog-cli": "^5.0.0",
78
- "dotenv-cli": "^10.0.0",
79
- "eslint": "^9.32.0",
80
- "eslint-plugin-jest": "^29.0.1",
81
- "eslint-plugin-react": "^7.37.0",
82
- "eslint-plugin-react-hooks": "^5.0.0",
139
+ "@vitejs/plugin-react": "^6.0.0",
140
+ "dotenv-cli": "^11.0.0",
83
141
  "jest": "^30.0.5",
84
142
  "jest-environment-jsdom": "^30.0.5",
85
143
  "jest-localstorage-mock": "^2.4.6",
86
144
  "jest-webextension-mock": "^4.0.0",
87
- "process": "^0.11.10",
88
145
  "react": "^19.1.1",
89
146
  "react-dom": "^19.1.1",
90
- "release-it": "^19.0.4",
147
+ "release-it": "^21.0.0",
91
148
  "ts-jest": "^29.4.0",
92
- "typescript": "^5.8.3",
93
- "typescript-eslint": "^8.15.0",
94
- "vite": "^7.0.6"
149
+ "typescript": "^6.0.3",
150
+ "vite": "^8.2.0"
95
151
  },
96
152
  "peerDependencies": {
97
- "react": ">=16.8.0 || >=18.0.0 || >=19.0.0",
98
- "react-dom": ">=16.8.0 || >=18.0.0 || >=19.0.0"
153
+ "react": ">=16.8.0",
154
+ "react-dom": ">=16.8.0"
99
155
  },
100
156
  "dependencies": {
101
157
  "@plq/is": "^1.2.0"
@@ -1,26 +1,26 @@
1
1
  export interface StorageChange {
2
- oldValue?: string | null;
3
- newValue?: string | null;
2
+ oldValue?: string | null
3
+ newValue?: string | null
4
4
  }
5
5
 
6
6
  export type StorageChangeListener = (changes: { [key: string]: StorageChange }) => void
7
7
 
8
8
  export interface StorageChangeEvent {
9
- addListener(listener: StorageChangeListener): void;
10
- removeListener(listener: StorageChangeListener): void;
11
- hasListener(listener: StorageChangeListener): boolean;
9
+ addListener(listener: StorageChangeListener): void
10
+ removeListener(listener: StorageChangeListener): void
11
+ hasListener(listener: StorageChangeListener): boolean
12
12
  }
13
13
 
14
14
  export interface Storage {
15
- get: (keys: string | string[]) => { [key: string]: string };
16
- set: (items: { [key: string]: string }) => void;
17
- remove: (keys: string | string[]) => void;
18
- onChanged: StorageChangeEvent;
15
+ get: (keys: string | string[]) => { [key: string]: string }
16
+ set: (items: { [key: string]: string }) => void
17
+ remove: (keys: string | string[]) => void
18
+ onChanged: StorageChangeEvent
19
19
  }
20
20
 
21
21
  export interface AsyncStorage {
22
- get: (keys: string | string[]) => Promise<{ [key: string]: string }>;
23
- set: (items: { [key: string]: string }) => Promise<void>;
24
- remove: (keys: string | string[]) => Promise<void>;
25
- onChanged: StorageChangeEvent;
22
+ get: (keys: string | string[]) => Promise<{ [key: string]: string }>
23
+ set: (items: { [key: string]: string }) => Promise<void>
24
+ remove: (keys: string | string[]) => Promise<void>
25
+ onChanged: StorageChangeEvent
26
26
  }