@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
@@ -1,92 +1,89 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const listeners = new Set();
4
- function fireStorageEvent(changes) {
5
- listeners.forEach(listener => {
6
- listener(changes);
7
- });
3
+ const change_notifier_1 = require("./change-notifier");
4
+ const storage_event_router_1 = require("./storage-event-router");
5
+ function toKeyList(keys) {
6
+ return Array.isArray(keys) ? keys : [keys];
8
7
  }
9
- window.addEventListener('storage', event => {
10
- if (event.key) {
11
- const changes = {
12
- [event.key]: {
13
- newValue: event.newValue,
14
- oldValue: event.oldValue,
15
- },
8
+ /**
9
+ * Adapts a Web Storage area to the library's `Storage` contract.
10
+ *
11
+ * Pass `undefined` where the global is missing, as it is on a server: the
12
+ * adapter then reads back nothing and **silently discards every write**, so a
13
+ * consumer keeps its initial value instead of the import throwing. Listeners
14
+ * are still accepted and reported, they simply never fire.
15
+ */
16
+ exports.default = (storage) => {
17
+ // Per-instance notifier: the localStorage and sessionStorage adapters must not
18
+ // share listeners, or a write to one area notifies subscribers of the other.
19
+ const notifier = (0, change_notifier_1.createChangeNotifier)();
20
+ // Only a missing global is inert. Anything else a caller passes has to fail
21
+ // on first use instead of swallowing their writes.
22
+ if (storage === undefined) {
23
+ return {
24
+ get: () => ({}),
25
+ set: () => { },
26
+ remove: () => { },
27
+ onChanged: notifier.onChanged,
16
28
  };
17
- fireStorageEvent(changes);
18
29
  }
19
- });
20
- const onChanged = {
21
- addListener(listener) {
22
- listeners.add(listener);
23
- },
24
- removeListener(listener) {
25
- listeners.delete(listener);
26
- },
27
- hasListener(listener) {
28
- return listeners.has(listener);
29
- },
30
- };
31
- exports.default = (storage) => ({
32
- get: keys => {
33
- const result = {};
34
- if (Array.isArray(keys)) {
35
- keys.forEach(key => {
36
- const item = typeof storage !== 'undefined' ? storage.getItem(key) : undefined;
37
- if (item)
30
+ const route = { storage, fire: notifier.fire };
31
+ // The DOM subscription is shared and reference counted: an adapter joins it
32
+ // with its first listener and lets go with its last, so factory calls nobody
33
+ // listens to leave nothing attached to the global object.
34
+ const onChanged = {
35
+ addListener(listener) {
36
+ notifier.onChanged.addListener(listener);
37
+ (0, storage_event_router_1.addRoute)(route);
38
+ },
39
+ removeListener(listener) {
40
+ notifier.onChanged.removeListener(listener);
41
+ if (!notifier.hasListeners())
42
+ (0, storage_event_router_1.removeRoute)(route);
43
+ },
44
+ hasListener(listener) {
45
+ return notifier.onChanged.hasListener(listener);
46
+ },
47
+ };
48
+ return {
49
+ get: keys => {
50
+ const result = {};
51
+ for (const key of toKeyList(keys)) {
52
+ const item = storage.getItem(key);
53
+ // `null` is the only answer that means absence: an empty string is a
54
+ // value the caller stored, and reporting it as a missing key loses it.
55
+ if (item !== null)
38
56
  result[key] = item;
39
- });
40
- }
41
- else {
42
- const item = typeof storage !== 'undefined' ? storage.getItem(keys) : undefined;
43
- if (item)
44
- result[keys] = item;
45
- }
46
- return result;
47
- },
48
- set: items => {
49
- const changes = {};
50
- Object.entries(items).forEach(([key, value]) => {
51
- const oldValue = typeof storage !== 'undefined' ? storage.getItem(key) : undefined;
52
- if (typeof storage !== 'undefined') {
57
+ }
58
+ return result;
59
+ },
60
+ set: items => {
61
+ const changes = {};
62
+ for (const [key, value] of Object.entries(items)) {
63
+ const oldValue = storage.getItem(key);
53
64
  storage.setItem(key, value);
54
65
  changes[key] = {
55
66
  oldValue,
56
67
  newValue: value,
57
68
  };
58
69
  }
59
- });
60
- if (Object.keys(changes).length > 0)
61
- fireStorageEvent(changes);
62
- },
63
- remove: keys => {
64
- const changes = {};
65
- if (Array.isArray(keys)) {
66
- keys.forEach(key => {
67
- const oldValue = typeof storage !== 'undefined' ? storage.getItem(key) : undefined;
68
- if (typeof storage !== 'undefined') {
69
- storage.removeItem(key);
70
- changes[key] = {
71
- oldValue,
72
- newValue: null,
73
- };
74
- }
75
- });
76
- }
77
- else {
78
- const oldValue = typeof storage !== 'undefined' ? storage.getItem(keys) : undefined;
79
- if (typeof storage !== 'undefined') {
80
- storage.removeItem(keys);
81
- changes[keys] = {
70
+ if (Object.keys(changes).length > 0)
71
+ notifier.fire(changes);
72
+ },
73
+ remove: keys => {
74
+ const changes = {};
75
+ for (const key of toKeyList(keys)) {
76
+ const oldValue = storage.getItem(key);
77
+ storage.removeItem(key);
78
+ changes[key] = {
82
79
  oldValue,
83
80
  newValue: null,
84
81
  };
85
82
  }
86
- }
87
- if (Object.keys(changes).length > 0)
88
- fireStorageEvent(changes);
89
- },
90
- onChanged,
91
- });
83
+ if (Object.keys(changes).length > 0)
84
+ notifier.fire(changes);
85
+ },
86
+ onChanged,
87
+ };
88
+ };
92
89
  //# sourceMappingURL=create-web-storage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-web-storage.js","sourceRoot":"","sources":["../../src/utils/create-web-storage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"create-web-storage.js","sourceRoot":"","sources":["../../src/utils/create-web-storage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import type { StorageChange, StorageChangeEvent } from '../@types/storage';
2
+ declare const TRACKED_AREAS: readonly ["local", "sync", "managed"];
3
+ export type Area = (typeof TRACKED_AREAS)[number];
4
+ /**
5
+ * Extension storage holds arbitrary JSON, while this library only ever writes
6
+ * serialized strings. Anything else under a key belongs to other code and is
7
+ * reported as absent — which is what effectively happened before, once such a
8
+ * value reached `JSON.parse` and the hook fell back to its initial value.
9
+ */
10
+ export declare function toStoredValue(value: unknown): string | null | undefined;
11
+ export declare function toStorageChanges(changes: {
12
+ [key: string]: {
13
+ oldValue?: unknown;
14
+ newValue?: unknown;
15
+ };
16
+ }): {
17
+ [key: string]: StorageChange;
18
+ };
19
+ export declare function toStoredItems(items: {
20
+ [key: string]: unknown;
21
+ }): {
22
+ [key: string]: string;
23
+ };
24
+ export interface ListenerRegistry {
25
+ fire(changes: {
26
+ [key: string]: StorageChange;
27
+ }, area: string): void;
28
+ createOnChanged(area: Area): StorageChangeEvent;
29
+ }
30
+ /**
31
+ * Demultiplexes an extension's single `onChanged` event, which names the area
32
+ * that changed, to one notifier per area. Each adapter owns its own registry,
33
+ * so a change in one browser's storage never reaches listeners registered on
34
+ * another's.
35
+ */
36
+ export declare function createListenerRegistry(): ListenerRegistry;
37
+ export {};
38
+ //# sourceMappingURL=extension-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension-storage.d.ts","sourceRoot":"","sources":["../../src/utils/extension-storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAG1E,QAAA,MAAM,aAAa,uCAAwC,CAAA;AAE3D,MAAM,MAAM,IAAI,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AAQjD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAIvE;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GAAG;IACxG,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAAA;CAC7B,CAWA;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAQ1F;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,OAAO,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAAA;KAAE,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnE,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,kBAAkB,CAAA;CAChD;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,IAAI,gBAAgB,CAiBzD"}
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toStoredValue = toStoredValue;
4
+ exports.toStorageChanges = toStorageChanges;
5
+ exports.toStoredItems = toStoredItems;
6
+ exports.createListenerRegistry = createListenerRegistry;
7
+ const change_notifier_1 = require("./change-notifier");
8
+ const TRACKED_AREAS = ['local', 'sync', 'managed'];
9
+ // Both browsers also report `session` changes, an area this library does not
10
+ // expose. Dispatching one would read an undefined notifier and throw.
11
+ function isTrackedArea(area) {
12
+ return TRACKED_AREAS.includes(area);
13
+ }
14
+ /**
15
+ * Extension storage holds arbitrary JSON, while this library only ever writes
16
+ * serialized strings. Anything else under a key belongs to other code and is
17
+ * reported as absent — which is what effectively happened before, once such a
18
+ * value reached `JSON.parse` and the hook fell back to its initial value.
19
+ */
20
+ function toStoredValue(value) {
21
+ if (value === undefined)
22
+ return;
23
+ return typeof value === 'string' ? value : null;
24
+ }
25
+ function toStorageChanges(changes) {
26
+ const result = {};
27
+ for (const [key, change] of Object.entries(changes)) {
28
+ result[key] = {
29
+ oldValue: toStoredValue(change.oldValue),
30
+ newValue: toStoredValue(change.newValue),
31
+ };
32
+ }
33
+ return result;
34
+ }
35
+ function toStoredItems(items) {
36
+ const result = {};
37
+ for (const [key, value] of Object.entries(items)) {
38
+ if (typeof value === 'string')
39
+ result[key] = value;
40
+ }
41
+ return result;
42
+ }
43
+ /**
44
+ * Demultiplexes an extension's single `onChanged` event, which names the area
45
+ * that changed, to one notifier per area. Each adapter owns its own registry,
46
+ * so a change in one browser's storage never reaches listeners registered on
47
+ * another's.
48
+ */
49
+ function createListenerRegistry() {
50
+ const notifiers = {
51
+ local: (0, change_notifier_1.createChangeNotifier)(),
52
+ sync: (0, change_notifier_1.createChangeNotifier)(),
53
+ managed: (0, change_notifier_1.createChangeNotifier)(),
54
+ };
55
+ return {
56
+ fire(changes, area) {
57
+ if (!isTrackedArea(area))
58
+ return;
59
+ notifiers[area].fire(changes);
60
+ },
61
+ createOnChanged(area) {
62
+ return notifiers[area].onChanged;
63
+ },
64
+ };
65
+ }
66
+ //# sourceMappingURL=extension-storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension-storage.js","sourceRoot":"","sources":["../../src/utils/extension-storage.ts"],"names":[],"mappings":";;AAmBA,sCAIC;AAED,4CAaC;AAED,sCAQC;AAaD,wDAiBC;AA7ED,uDAA6E;AAE7E,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAU,CAAA;AAI3D,6EAA6E;AAC7E,sEAAsE;AACtE,SAAS,aAAa,CAAC,IAAY;IACjC,OAAQ,aAAmC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;AAC5D,CAAC;AAED;;;;;GAKG;AACH,SAAgB,aAAa,CAAC,KAAc;IAC1C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAM;IAE/B,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA;AACjD,CAAC;AAED,SAAgB,gBAAgB,CAAC,OAAsE;IAGrG,MAAM,MAAM,GAAqC,EAAE,CAAA;IAEnD,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACpD,MAAM,CAAC,GAAG,CAAC,GAAG;YACZ,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC;YACxC,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC;SACzC,CAAA;IACH,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAgB,aAAa,CAAC,KAAiC;IAC7D,MAAM,MAAM,GAA8B,EAAE,CAAA;IAE5C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;IACpD,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAOD;;;;;GAKG;AACH,SAAgB,sBAAsB;IACpC,MAAM,SAAS,GAAuC;QACpD,KAAK,EAAE,IAAA,sCAAoB,GAAE;QAC7B,IAAI,EAAE,IAAA,sCAAoB,GAAE;QAC5B,OAAO,EAAE,IAAA,sCAAoB,GAAE;KAChC,CAAA;IAED,OAAO;QACL,IAAI,CAAC,OAAO,EAAE,IAAI;YAChB,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC;gBAAE,OAAM;YAEhC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC/B,CAAC;QACD,eAAe,CAAC,IAAI;YAClB,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,CAAA;QAClC,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -1,2 +1,14 @@
1
- export default function <T>(key: string, persistedItem: string, newValue: T): string;
1
+ /**
2
+ * Merges `newValue` under `key` into the serialized entry a factory shares between all of its
3
+ * hooks, and returns the entry to store.
4
+ *
5
+ * Reports and throws when the entry cannot be merged into, rather than answering with one built
6
+ * from nothing. What this returns is written over the whole entry, so an unreadable entry is the
7
+ * one case with nothing safe to return: every other hook's key would be replaced with nothing,
8
+ * and the bytes a repair still needs would go with them.
9
+ *
10
+ * @throws {SyntaxError} when the entry is not valid JSON.
11
+ * @throws {TypeError} when the entry is valid JSON but not an object of keys.
12
+ */
13
+ export default function getNewItem<T>(key: string, persistedItem: string, newValue: T): string;
2
14
  //# sourceMappingURL=get-new-item.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"get-new-item.d.ts","sourceRoot":"","sources":["../../src/utils/get-new-item.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,WAAU,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,MAAM,CAelF"}
1
+ {"version":3,"file":"get-new-item.d.ts","sourceRoot":"","sources":["../../src/utils/get-new-item.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,MAAM,CAwB7F"}
@@ -1,17 +1,37 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = default_1;
4
- function default_1(key, persistedItem, newValue) {
3
+ exports.default = getNewItem;
4
+ const is_1 = require("@plq/is");
5
+ /**
6
+ * Merges `newValue` under `key` into the serialized entry a factory shares between all of its
7
+ * hooks, and returns the entry to store.
8
+ *
9
+ * Reports and throws when the entry cannot be merged into, rather than answering with one built
10
+ * from nothing. What this returns is written over the whole entry, so an unreadable entry is the
11
+ * one case with nothing safe to return: every other hook's key would be replaced with nothing,
12
+ * and the bytes a repair still needs would go with them.
13
+ *
14
+ * @throws {SyntaxError} when the entry is not valid JSON.
15
+ * @throws {TypeError} when the entry is valid JSON but not an object of keys.
16
+ */
17
+ function getNewItem(key, persistedItem, newValue) {
5
18
  let persist;
6
19
  try {
7
20
  persist = persistedItem ? JSON.parse(persistedItem) : {};
8
21
  }
9
22
  catch (err) {
10
- console.error(err);
11
- persist = {};
23
+ console.error("use-persisted-state: Can't write value to storage", err);
24
+ throw err;
12
25
  }
13
- return JSON.stringify(Object.assign(persist, {
14
- [key]: newValue,
15
- }));
26
+ if (!(0, is_1.isObject)(persist)) {
27
+ // A shared backend can leave any JSON under the key, and neither a primitive nor an array
28
+ // survives being merged into: `JSON.stringify` unwraps the first back to itself and keeps the
29
+ // second an array, so the value being set disappears with the property added to it. A stored
30
+ // `null` did not even get that far - it threw out of `Object.assign` and into the caller.
31
+ const err = new TypeError('the stored entry is not an object of keys');
32
+ console.error("use-persisted-state: Can't write value to storage", err);
33
+ throw err;
34
+ }
35
+ return JSON.stringify({ ...persist, [key]: newValue });
16
36
  }
17
37
  //# sourceMappingURL=get-new-item.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"get-new-item.js","sourceRoot":"","sources":["../../src/utils/get-new-item.ts"],"names":[],"mappings":";;AAAA,4BAeC;AAfD,mBAA2B,GAAW,EAAE,aAAqB,EAAE,QAAW;IACxE,IAAI,OAAiC,CAAA;IAErC,IAAI,CAAC;QACH,OAAO,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAC1D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAClB,OAAO,GAAG,EAAE,CAAA;IACd,CAAC;IAED,OAAO,IAAI,CAAC,SAAS,CACnB,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE;QACrB,CAAC,GAAG,CAAC,EAAE,QAAQ;KAChB,CAAC,CACH,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"get-new-item.js","sourceRoot":"","sources":["../../src/utils/get-new-item.ts"],"names":[],"mappings":";;AAcA,6BAwBC;AAtCD,gCAAkC;AAElC;;;;;;;;;;;GAWG;AACH,SAAwB,UAAU,CAAI,GAAW,EAAE,aAAqB,EAAE,QAAW;IACnF,IAAI,OAAgB,CAAA;IAEpB,IAAI,CAAC;QACH,OAAO,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAC1D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,mDAAmD,EAAE,GAAG,CAAC,CAAA;QAEvE,MAAM,GAAG,CAAA;IACX,CAAC;IAED,IAAI,CAAC,IAAA,aAAQ,EAAC,OAAO,CAAC,EAAE,CAAC;QACvB,0FAA0F;QAC1F,8FAA8F;QAC9F,6FAA6F;QAC7F,0FAA0F;QAC1F,MAAM,GAAG,GAAG,IAAI,SAAS,CAAC,2CAA2C,CAAC,CAAA;QAEtE,OAAO,CAAC,KAAK,CAAC,mDAAmD,EAAE,GAAG,CAAC,CAAA;QAEvE,MAAM,GAAG,CAAA;IACX,CAAC;IAED,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAA;AACxD,CAAC"}
@@ -1,3 +1,3 @@
1
- import React from 'react';
1
+ import type React from 'react';
2
2
  export default function <T>(newState: React.SetStateAction<T>, state: T): T;
3
3
  //# sourceMappingURL=get-new-value.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"get-new-value.d.ts","sourceRoot":"","sources":["../../src/utils/get-new-value.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,MAAM,CAAC,OAAO,WAAW,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAU1E"}
1
+ {"version":3,"file":"get-new-value.d.ts","sourceRoot":"","sources":["../../src/utils/get-new-value.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,MAAM,CAAC,OAAO,WAAW,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAU1E"}
@@ -1,2 +1,9 @@
1
- export default function <T>(key: string, initialValue: T | (() => T), persist?: string): T;
1
+ /**
2
+ * Resolves the value a hook starts from: the persisted entry for `key` when the stored payload
3
+ * carries one, the initial value otherwise.
4
+ *
5
+ * Presence is decided by the key rather than by the value, so a persisted `null` comes back as
6
+ * the value the user set instead of being mistaken for an absence and replaced.
7
+ */
8
+ export default function getPersistedValue<T>(key: string, initialValue: T | (() => T), persist?: string): T;
2
9
  //# sourceMappingURL=get-persisted-value.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"get-persisted-value.d.ts","sourceRoot":"","sources":["../../src/utils/get-persisted-value.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,WAAU,CAAC,EAAE,GAAG,EAAC,MAAM,EAAE,YAAY,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,CAAC,CAgBvF"}
1
+ {"version":3,"file":"get-persisted-value.d.ts","sourceRoot":"","sources":["../../src/utils/get-persisted-value.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,CAAC,CAwB1G"}
@@ -1,18 +1,32 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = default_1;
3
+ exports.default = getPersistedValue;
4
4
  const is_1 = require("@plq/is");
5
- function default_1(key, initialValue, persist) {
5
+ /**
6
+ * Resolves the value a hook starts from: the persisted entry for `key` when the stored payload
7
+ * carries one, the initial value otherwise.
8
+ *
9
+ * Presence is decided by the key rather than by the value, so a persisted `null` comes back as
10
+ * the value the user set instead of being mistaken for an absence and replaced.
11
+ */
12
+ function getPersistedValue(key, initialValue, persist) {
6
13
  let initialPersist;
7
14
  try {
8
15
  initialPersist = persist ? JSON.parse(persist) : {};
9
16
  }
10
- catch {
17
+ catch (err) {
18
+ // A shared backend can hold a foreign or truncated entry, so a parse failure is expected here
19
+ // and must not stop the component mounting. It is reported rather than swallowed because the
20
+ // fallback discards whatever was persisted, and because the change path logs the same failure.
21
+ console.error("use-persisted-state: Can't parse value from storage", err);
11
22
  initialPersist = {};
12
23
  }
13
24
  let initialOrPersistedValue = (0, is_1.isFunction)(initialValue) ? initialValue() : initialValue;
14
- if (initialPersist && key in initialPersist) {
15
- initialOrPersistedValue = initialPersist[key] ?? initialOrPersistedValue;
25
+ // `JSON.parse` yields any JSON value, so a foreign entry can be a primitive,
26
+ // and `in` throws on one. This runs in the `useState` initializer, where a
27
+ // throw stops the component mounting instead of falling back.
28
+ if ((0, is_1.isObject)(initialPersist) && key in initialPersist) {
29
+ initialOrPersistedValue = initialPersist[key];
16
30
  }
17
31
  return initialOrPersistedValue;
18
32
  }
@@ -1 +1 @@
1
- {"version":3,"file":"get-persisted-value.js","sourceRoot":"","sources":["../../src/utils/get-persisted-value.ts"],"names":[],"mappings":";;AAEA,4BAgBC;AAlBD,gCAAoC;AAEpC,mBAA2B,GAAU,EAAE,YAA2B,EAAE,OAAgB;IAClF,IAAI,cAAwC,CAAA;IAE5C,IAAI,CAAC;QACH,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,cAAc,GAAG,EAAE,CAAA;IACrB,CAAC;IAED,IAAI,uBAAuB,GAAG,IAAA,eAAU,EAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,EAAE,CAAE,CAAC,CAAC,YAAY,CAAA;IAEvF,IAAI,cAAc,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;QAC5C,uBAAuB,GAAI,cAAc,CAAC,GAAG,CAAO,IAAI,uBAAuB,CAAA;IACjF,CAAC;IAED,OAAO,uBAAuB,CAAA;AAChC,CAAC"}
1
+ {"version":3,"file":"get-persisted-value.js","sourceRoot":"","sources":["../../src/utils/get-persisted-value.ts"],"names":[],"mappings":";;AASA,oCAwBC;AAjCD,gCAA8C;AAE9C;;;;;;GAMG;AACH,SAAwB,iBAAiB,CAAI,GAAW,EAAE,YAA2B,EAAE,OAAgB;IACrG,IAAI,cAAuB,CAAA;IAE3B,IAAI,CAAC;QACH,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IACrD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,8FAA8F;QAC9F,6FAA6F;QAC7F,+FAA+F;QAC/F,OAAO,CAAC,KAAK,CAAC,qDAAqD,EAAE,GAAG,CAAC,CAAA;QAEzE,cAAc,GAAG,EAAE,CAAA;IACrB,CAAC;IAED,IAAI,uBAAuB,GAAG,IAAA,eAAU,EAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,YAAY,CAAA;IAEtF,6EAA6E;IAC7E,2EAA2E;IAC3E,8DAA8D;IAC9D,IAAI,IAAA,aAAQ,EAAC,cAAc,CAAC,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;QACtD,uBAAuB,GAAG,cAAc,CAAC,GAAG,CAAM,CAAA;IACpD,CAAC;IAED,OAAO,uBAAuB,CAAA;AAChC,CAAC"}
@@ -1,3 +1,10 @@
1
1
  import type { AsyncStorage } from '../@types/storage';
2
+ /**
3
+ * Narrows a storage to {@link AsyncStorage}, deciding which hook the entry point builds.
4
+ *
5
+ * Inspection never writes: `set` and `remove` are only checked for shape, never invoked, so
6
+ * `get` alone decides. A candidate pairing an async `get` with a sync `set` satisfies neither
7
+ * storage interface, which is why proving the other two members is not worth a write.
8
+ */
2
9
  export default function isAsyncStorage(storage: unknown): storage is AsyncStorage;
3
10
  //# sourceMappingURL=is-async-storage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"is-async-storage.d.ts","sourceRoot":"","sources":["../../src/utils/is-async-storage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAA;AAEnD,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,YAAY,CA0BhF"}
1
+ {"version":3,"file":"is-async-storage.d.ts","sourceRoot":"","sources":["../../src/utils/is-async-storage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAcrD;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,YAAY,CAoDhF"}
@@ -2,28 +2,59 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = isAsyncStorage;
4
4
  const is_1 = require("@plq/is");
5
+ // `AsyncStorage` is three methods, so every member has to be callable: a promise standing in for
6
+ // one satisfies nothing the hook then does with it. `@plq/is` classifies async functions as their
7
+ // own type, so `isFunction` rejects them and both checks are needed to admit a callable member.
8
+ const isCallableMember = (member) => (0, is_1.isFunction)(member) || (0, is_1.isAsyncFunction)(member);
9
+ /**
10
+ * Narrows a storage to {@link AsyncStorage}, deciding which hook the entry point builds.
11
+ *
12
+ * Inspection never writes: `set` and `remove` are only checked for shape, never invoked, so
13
+ * `get` alone decides. A candidate pairing an async `get` with a sync `set` satisfies neither
14
+ * storage interface, which is why proving the other two members is not worth a write.
15
+ */
5
16
  function isAsyncStorage(storage) {
6
- const hasGet = Boolean(storage) && typeof storage?.get !== 'undefined';
7
- const hasSet = Boolean(storage) && typeof storage?.set !== 'undefined';
8
- const hasRemove = Boolean(storage) && typeof storage?.remove !== 'undefined';
9
- if (!hasGet || !hasSet || !hasRemove) {
17
+ const candidate = storage;
18
+ if (!candidate) {
10
19
  return false;
11
20
  }
12
- const hasGetPromise = (0, is_1.isPromise)(storage.get)
13
- || (0, is_1.isFunction)(storage.get) && (0, is_1.isPromise)(storage.get(''))
14
- || (0, is_1.isAsyncFunction)(storage.get);
15
- const hasSetPromise = (0, is_1.isPromise)(storage.set)
16
- || (0, is_1.isFunction)(storage.set) && (0, is_1.isPromise)(storage.set({}))
17
- || (0, is_1.isAsyncFunction)(storage.set);
18
- const hasRemovePromise = (0, is_1.isPromise)(storage.remove)
19
- || (0, is_1.isFunction)(storage.remove) && (0, is_1.isPromise)(storage.remove(''))
20
- || (0, is_1.isAsyncFunction)(storage.remove);
21
- return Boolean(storage)
22
- && hasGet
23
- && hasSet
24
- && hasRemove
25
- && hasGetPromise
26
- && hasSetPromise
27
- && hasRemovePromise;
21
+ const { get, set, remove } = candidate;
22
+ if (!isCallableMember(set) || !isCallableMember(remove)) {
23
+ return false;
24
+ }
25
+ // An `async` declaration is the one form of asyncness visible without a call.
26
+ if ((0, is_1.isAsyncFunction)(get)) {
27
+ return true;
28
+ }
29
+ if (!(0, is_1.isFunction)(get)) {
30
+ return false;
31
+ }
32
+ // `AsyncStorage` admits a plain function returning a promise, and nothing short of a call tells
33
+ // that apart from a sync get. Any adapter written that way can only be recognised here, and a
34
+ // third-party one is free to be written that way, so this line stays necessary however the
35
+ // shipped adapters happen to declare themselves. A read is the one probe that leaves the
36
+ // inspected storage as it found it.
37
+ let probe;
38
+ try {
39
+ // Called as a method of the storage it belongs to: an adapter written as an
40
+ // object of methods reads its own state through `this`, and probing the
41
+ // extracted function would throw on a storage that is perfectly valid.
42
+ probe = get.call(candidate, '');
43
+ }
44
+ catch {
45
+ // Not swallowed, deferred: consumers build the factory at module scope, where
46
+ // a throw takes the import down instead of reaching a component. A storage
47
+ // whose read fails is not provably async, and the sync path raises the same
48
+ // failure at the first read, where it can be handled.
49
+ return false;
50
+ }
51
+ if (!(0, is_1.isPromise)(probe)) {
52
+ return false;
53
+ }
54
+ // Only the probe's shape answers the question, so its outcome is discarded by construction.
55
+ // The rejection still has to be claimed: unclaimed, it terminates the process on Node 15+,
56
+ // letting a storage that merely fails a read take the consuming application down with it.
57
+ probe.then(undefined, () => undefined);
58
+ return true;
28
59
  }
29
60
  //# sourceMappingURL=is-async-storage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"is-async-storage.js","sourceRoot":"","sources":["../../src/utils/is-async-storage.ts"],"names":[],"mappings":";;AAGA,iCA0BC;AA7BD,gCAA8D;AAG9D,SAAwB,cAAc,CAAC,OAAgB;IACnD,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,OAAQ,OAAe,EAAE,GAAG,KAAK,WAAW,CAAA;IAC/E,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,OAAQ,OAAe,EAAE,GAAG,KAAK,WAAW,CAAA;IAC/E,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,OAAQ,OAAe,EAAE,MAAM,KAAK,WAAW,CAAA;IAErF,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACnC,OAAO,KAAK,CAAA;IAChB,CAAC;IAED,MAAM,aAAa,GAAG,IAAA,cAAS,EAAE,OAAe,CAAC,GAAG,CAAC;WAC9C,IAAA,eAAU,EAAE,OAAe,CAAC,GAAG,CAAC,IAAI,IAAA,cAAS,EAAE,OAAe,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;WACvE,IAAA,oBAAe,EAAE,OAAe,CAAC,GAAG,CAAC,CAAA;IAC5C,MAAM,aAAa,GAAG,IAAA,cAAS,EAAE,OAAe,CAAC,GAAG,CAAC;WAC9C,IAAA,eAAU,EAAE,OAAe,CAAC,GAAG,CAAC,IAAI,IAAA,cAAS,EAAE,OAAe,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;WACvE,IAAA,oBAAe,EAAE,OAAe,CAAC,GAAG,CAAC,CAAA;IAC5C,MAAM,gBAAgB,GAAG,IAAA,cAAS,EAAE,OAAe,CAAC,MAAM,CAAC;WACpD,IAAA,eAAU,EAAE,OAAe,CAAC,MAAM,CAAC,IAAI,IAAA,cAAS,EAAE,OAAe,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;WAC7E,IAAA,oBAAe,EAAE,OAAe,CAAC,MAAM,CAAC,CAAA;IAE/C,OAAO,OAAO,CAAC,OAAO,CAAC;WAChB,MAAM;WACN,MAAM;WACN,SAAS;WACT,aAAa;WACb,aAAa;WACb,gBAAgB,CAAA;AAC3B,CAAC"}
1
+ {"version":3,"file":"is-async-storage.js","sourceRoot":"","sources":["../../src/utils/is-async-storage.ts"],"names":[],"mappings":";;AAsBA,iCAoDC;AA1ED,gCAAgE;AAUhE,iGAAiG;AACjG,kGAAkG;AAClG,gGAAgG;AAChG,MAAM,gBAAgB,GAAG,CAAC,MAAe,EAAW,EAAE,CAAC,IAAA,eAAU,EAAC,MAAM,CAAC,IAAI,IAAA,oBAAe,EAAC,MAAM,CAAC,CAAA;AAEpG;;;;;;GAMG;AACH,SAAwB,cAAc,CAAC,OAAgB;IACrD,MAAM,SAAS,GAAG,OAA8C,CAAA;IAEhE,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,SAAS,CAAA;IAEtC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC;QACxD,OAAO,KAAK,CAAA;IACd,CAAC;IAED,8EAA8E;IAC9E,IAAI,IAAA,oBAAe,EAAC,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,CAAA;IACb,CAAC;IAED,IAAI,CAAC,IAAA,eAAU,EAAC,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,KAAK,CAAA;IACd,CAAC;IAED,gGAAgG;IAChG,8FAA8F;IAC9F,2FAA2F;IAC3F,yFAAyF;IACzF,oCAAoC;IACpC,IAAI,KAAc,CAAA;IAElB,IAAI,CAAC;QACH,4EAA4E;QAC5E,wEAAwE;QACxE,uEAAuE;QACvE,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,CAAA;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,8EAA8E;QAC9E,2EAA2E;QAC3E,4EAA4E;QAC5E,sDAAsD;QACtD,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,CAAC,IAAA,cAAS,EAAC,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,KAAK,CAAA;IACd,CAAC;IAED,4FAA4F;IAC5F,2FAA2F;IAC3F,0FAA0F;IAC1F,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;IAEtC,OAAO,IAAI,CAAA;AACb,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { StorageChange } from '../@types/storage';
2
+ /** An adapter waiting for DOM storage events about the area it wraps. */
3
+ export interface StorageEventRoute {
4
+ storage: globalThis.Storage;
5
+ fire(changes: {
6
+ [key: string]: StorageChange;
7
+ }): void;
8
+ }
9
+ /**
10
+ * Starts routing DOM storage events to `route`. Every adapter shares one
11
+ * subscription on the global object, so creating adapters nobody listens to
12
+ * costs nothing.
13
+ */
14
+ export declare function addRoute(route: StorageEventRoute): void;
15
+ /** Stops routing to `route`, releasing the shared subscription with the last one. */
16
+ export declare function removeRoute(route: StorageEventRoute): void;
17
+ //# sourceMappingURL=storage-event-router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-event-router.d.ts","sourceRoot":"","sources":["../../src/utils/storage-event-router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAEtD,yEAAyE;AACzE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAA;IAC3B,IAAI,CAAC,OAAO,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAAA;KAAE,GAAG,IAAI,CAAA;CACtD;AAoCD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAQvD;AAED,qFAAqF;AACrF,wBAAgB,WAAW,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAQ1D"}