memorio 4.9.8 → 4.9.30
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/README.md +84 -3
- package/examples/basic.ts +1 -1
- package/examples/session-advanced.ts +1 -1
- package/examples/store-advanced.ts +1 -1
- package/index.cjs +131 -75
- package/index.js +131 -75
- package/llms.txt +13 -5
- package/modules/redux.cjs +2677 -0
- package/modules/redux.cjs.map +1 -0
- package/modules/redux.d.ts +119 -0
- package/modules/redux.js +2669 -0
- package/modules/redux.js.map +1 -0
- package/package.json +9 -3
- package/types/memorio.d.ts +1 -1
package/index.js
CHANGED
|
@@ -642,24 +642,29 @@ var init_store = __esm({
|
|
|
642
642
|
}, "value")
|
|
643
643
|
},
|
|
644
644
|
/**
|
|
645
|
-
* Calculates total size of all memorio items
|
|
645
|
+
* Calculates total size of all memorio items in kilobytes.
|
|
646
|
+
* Counts each prefixed key and its JSON-serialized value, summing their
|
|
647
|
+
* byte lengths and converting to whole kilobytes (Math.ceil). Replaces
|
|
648
|
+
* the previous character-only total which did not express real usage.
|
|
649
|
+
* @returns Total size in kilobytes (0 when empty / no memorio items).
|
|
646
650
|
*/
|
|
647
651
|
size: {
|
|
648
652
|
value: /* @__PURE__ */ __name(() => {
|
|
649
|
-
let
|
|
653
|
+
let totalBytes = 0;
|
|
650
654
|
if (_hasStorage) {
|
|
651
|
-
|
|
652
|
-
|
|
655
|
+
for (let i = 0; i < _localStorage.length; i++) {
|
|
656
|
+
const key = _localStorage.key(i);
|
|
657
|
+
if (key && key.startsWith(_getPrefix())) {
|
|
653
658
|
const item = _localStorage.getItem(key);
|
|
654
|
-
if (item)
|
|
659
|
+
if (item) totalBytes += key.length + item.length;
|
|
655
660
|
}
|
|
656
|
-
}
|
|
661
|
+
}
|
|
657
662
|
} else {
|
|
658
|
-
_getFallbackStorage().forEach((value) => {
|
|
659
|
-
|
|
663
|
+
_getFallbackStorage().forEach((value, key) => {
|
|
664
|
+
totalBytes += key.length + value.length;
|
|
660
665
|
});
|
|
661
666
|
}
|
|
662
|
-
return
|
|
667
|
+
return totalBytes === 0 ? 0 : Math.ceil(totalBytes / 1024);
|
|
663
668
|
}, "value")
|
|
664
669
|
},
|
|
665
670
|
/**
|
|
@@ -1730,30 +1735,51 @@ var dispatch = {
|
|
|
1730
1735
|
}, "set"),
|
|
1731
1736
|
///
|
|
1732
1737
|
/**
|
|
1733
|
-
*
|
|
1738
|
+
* Subscribes a callback to an event name. Maintains a *list* of handlers per
|
|
1739
|
+
* name, so multiple subscribers on the same path (e.g. several
|
|
1740
|
+
* `useObserver` calls observing `state.report`) each keep firing and each
|
|
1741
|
+
* get an independent unsubscribe. The previous single-slot-per-name
|
|
1742
|
+
* behaviour silently dropped the second subscriber and let any unsubscribe
|
|
1743
|
+
* cancel every other subscriber on that path.
|
|
1734
1744
|
* @param name The name of the event to listen for.
|
|
1735
1745
|
* @param cb The callback function to execute when the event is triggered.
|
|
1746
|
+
* @returns An unsubscribe function for this subscriber, or void if no callback.
|
|
1736
1747
|
*/
|
|
1737
1748
|
listen: /* @__PURE__ */ __name((name, cb = null, _flag) => {
|
|
1738
|
-
if (
|
|
1739
|
-
const
|
|
1749
|
+
if (!cb) return;
|
|
1750
|
+
const wrapped = /* @__PURE__ */ __name((e) => {
|
|
1740
1751
|
const target = e;
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1752
|
+
if (globalThis.queueMicrotask) {
|
|
1753
|
+
globalThis.queueMicrotask(() => cb(target));
|
|
1754
|
+
} else {
|
|
1755
|
+
Promise.resolve().then(() => cb(target));
|
|
1756
|
+
}
|
|
1757
|
+
}, "wrapped");
|
|
1758
|
+
globalThis.addEventListener(name, wrapped);
|
|
1759
|
+
const list = globalThis.events[name] = globalThis.events[name] ?? [];
|
|
1760
|
+
list.push(wrapped);
|
|
1761
|
+
return () => {
|
|
1762
|
+
globalThis.removeEventListener(name, wrapped);
|
|
1763
|
+
const idx = list.indexOf(wrapped);
|
|
1764
|
+
if (idx > -1) list.splice(idx, 1);
|
|
1765
|
+
if (!list.length) delete globalThis.events[name];
|
|
1766
|
+
};
|
|
1745
1767
|
}, "listen"),
|
|
1746
1768
|
///
|
|
1747
1769
|
/**
|
|
1748
|
-
* Removes
|
|
1749
|
-
*
|
|
1770
|
+
* Removes ALL handlers for the specified event name (imperative bulk
|
|
1771
|
+
* removal). For per-subscriber teardown, use the unsubscribe function
|
|
1772
|
+
* returned by `listen`.
|
|
1773
|
+
* @param name The name of the event to remove the listeners for.
|
|
1750
1774
|
*/
|
|
1751
1775
|
remove: /* @__PURE__ */ __name((name) => {
|
|
1752
|
-
const
|
|
1753
|
-
if (
|
|
1754
|
-
globalThis.removeEventListener(name,
|
|
1755
|
-
|
|
1776
|
+
const entry = globalThis.events?.[name];
|
|
1777
|
+
if (Array.isArray(entry)) {
|
|
1778
|
+
entry.forEach((h) => globalThis.removeEventListener(name, h));
|
|
1779
|
+
} else if (typeof entry === "function") {
|
|
1780
|
+
globalThis.removeEventListener(name, entry);
|
|
1756
1781
|
}
|
|
1782
|
+
delete globalThis.events[name];
|
|
1757
1783
|
}, "remove")
|
|
1758
1784
|
};
|
|
1759
1785
|
|
|
@@ -1817,6 +1843,14 @@ var _validateValue = /* @__PURE__ */ __name((value, schema2, path2) => {
|
|
|
1817
1843
|
}
|
|
1818
1844
|
}
|
|
1819
1845
|
}
|
|
1846
|
+
if (schema2.type === "array" && Array.isArray(value) && schema2.items) {
|
|
1847
|
+
for (let i = 0; i < value.length; i++) {
|
|
1848
|
+
const itemResult = _validateValue(value[i], schema2.items, `${path2}[${i}]`);
|
|
1849
|
+
if (!itemResult.valid) {
|
|
1850
|
+
errors.push(...itemResult.errors || []);
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1820
1854
|
return errors.length === 0 ? { valid: true } : { valid: false, errors };
|
|
1821
1855
|
}, "_validateValue");
|
|
1822
1856
|
var validate = /* @__PURE__ */ __name((path2, value) => {
|
|
@@ -1877,7 +1911,8 @@ var buildProxy = /* @__PURE__ */ __name((obj, callback, tree = []) => {
|
|
|
1877
1911
|
const event = /* @__PURE__ */ __name((name) => {
|
|
1878
1912
|
try {
|
|
1879
1913
|
dispatch.set(name, { detail: { name } });
|
|
1880
|
-
} catch (
|
|
1914
|
+
} catch (err) {
|
|
1915
|
+
if (DEV) console.warn("[memorio state] event dispatch failed:", err);
|
|
1881
1916
|
}
|
|
1882
1917
|
return;
|
|
1883
1918
|
}, "event");
|
|
@@ -1896,7 +1931,7 @@ var buildProxy = /* @__PURE__ */ __name((obj, callback, tree = []) => {
|
|
|
1896
1931
|
}
|
|
1897
1932
|
if (prop === "list") {
|
|
1898
1933
|
message_default('Some state could be hidden when you use "state.list". Use "state" to see the complete list ');
|
|
1899
|
-
return
|
|
1934
|
+
return deepRaw(target, /* @__PURE__ */ new WeakSet());
|
|
1900
1935
|
}
|
|
1901
1936
|
if (typeof prop === "string") {
|
|
1902
1937
|
const path2 = tree.length > 0 ? "state." + tree.concat(prop).join(".") : "state." + prop;
|
|
@@ -2032,7 +2067,8 @@ var buildProxy = /* @__PURE__ */ __name((obj, callback, tree = []) => {
|
|
|
2032
2067
|
message_default(`State '${key}' is now unlocked.`);
|
|
2033
2068
|
}, "value")
|
|
2034
2069
|
});
|
|
2035
|
-
} catch (
|
|
2070
|
+
} catch (err) {
|
|
2071
|
+
if (DEV) console.warn("[memorio state] lock/unlock defineProperty failed:", err);
|
|
2036
2072
|
}
|
|
2037
2073
|
}
|
|
2038
2074
|
}
|
|
@@ -2272,27 +2308,28 @@ if (globalThis[SESSION_INSTANCE_KEY]) {
|
|
|
2272
2308
|
}, "value")
|
|
2273
2309
|
},
|
|
2274
2310
|
/**
|
|
2275
|
-
*
|
|
2276
|
-
*
|
|
2311
|
+
* Calculates total size of all memorio session items in kilobytes.
|
|
2312
|
+
* Counts each prefixed key and its JSON-serialized value, summing their
|
|
2313
|
+
* byte lengths and converting to whole kilobytes (Math.ceil).
|
|
2314
|
+
* @returns Total size in kilobytes (0 when empty / no memorio items).
|
|
2277
2315
|
*/
|
|
2278
2316
|
size: {
|
|
2279
2317
|
value: /* @__PURE__ */ __name(() => {
|
|
2280
|
-
let
|
|
2318
|
+
let totalBytes = 0;
|
|
2281
2319
|
if (_hasStorage2) {
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
}
|
|
2320
|
+
for (let i = 0; i < _sessionStorage.length; i++) {
|
|
2321
|
+
const key = _sessionStorage.key(i);
|
|
2322
|
+
if (key && key.startsWith(_getPrefix2())) {
|
|
2323
|
+
const item = _sessionStorage.getItem(key);
|
|
2324
|
+
if (item) totalBytes += key.length + item.length;
|
|
2288
2325
|
}
|
|
2289
|
-
|
|
2326
|
+
}
|
|
2290
2327
|
} else {
|
|
2291
|
-
_getFallbackStorage2().forEach((value) => {
|
|
2292
|
-
|
|
2328
|
+
_getFallbackStorage2().forEach((value, key) => {
|
|
2329
|
+
totalBytes += key.length + value.length;
|
|
2293
2330
|
});
|
|
2294
2331
|
}
|
|
2295
|
-
return
|
|
2332
|
+
return totalBytes === 0 ? 0 : Math.ceil(totalBytes / 1024);
|
|
2296
2333
|
}, "value")
|
|
2297
2334
|
},
|
|
2298
2335
|
/**
|
|
@@ -2511,6 +2548,7 @@ var useObserver = /* @__PURE__ */ __name(((func, deps = []) => {
|
|
|
2511
2548
|
};
|
|
2512
2549
|
}
|
|
2513
2550
|
const handlers = [];
|
|
2551
|
+
const unsubs = [];
|
|
2514
2552
|
const depList = Array.isArray(deps) && !deps.__path ? deps : [deps];
|
|
2515
2553
|
try {
|
|
2516
2554
|
depList.forEach((dep) => {
|
|
@@ -2524,8 +2562,11 @@ var useObserver = /* @__PURE__ */ __name(((func, deps = []) => {
|
|
|
2524
2562
|
if (t) {
|
|
2525
2563
|
t = t.replace(/^(\(\)\s*=>\s*)/, "").replace(/^globalThis\./, "").replace(/^state\./, "");
|
|
2526
2564
|
const path2 = t.startsWith("store.") || t.startsWith("state.") ? t : "state." + t;
|
|
2527
|
-
|
|
2528
|
-
|
|
2565
|
+
if (!handlers.includes(path2)) {
|
|
2566
|
+
handlers.push(path2);
|
|
2567
|
+
const off = dispatch.listen(path2, () => func());
|
|
2568
|
+
if (typeof off === "function") unsubs.push({ path: path2, off });
|
|
2569
|
+
}
|
|
2529
2570
|
} else {
|
|
2530
2571
|
internal_default.clearTrackedPaths();
|
|
2531
2572
|
internal_default.tracking = true;
|
|
@@ -2537,8 +2578,9 @@ var useObserver = /* @__PURE__ */ __name(((func, deps = []) => {
|
|
|
2537
2578
|
internal_default.trackedPaths.forEach((path2) => {
|
|
2538
2579
|
if (!handlers.includes(path2)) {
|
|
2539
2580
|
message_default(`useObserver: [Auto-Discovery] -> "${path2}"`);
|
|
2540
|
-
dispatch.listen(path2, () => func());
|
|
2541
2581
|
handlers.push(path2);
|
|
2582
|
+
const off = dispatch.listen(path2, () => func());
|
|
2583
|
+
if (typeof off === "function") unsubs.push({ path: path2, off });
|
|
2542
2584
|
}
|
|
2543
2585
|
});
|
|
2544
2586
|
}
|
|
@@ -2549,11 +2591,9 @@ var useObserver = /* @__PURE__ */ __name(((func, deps = []) => {
|
|
|
2549
2591
|
console.warn(`useObserver: No paths resolved for monitoring. Check your dependencies!`);
|
|
2550
2592
|
}
|
|
2551
2593
|
return () => {
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
message_default(`useObserver: Stopped monitoring "${path2}"`);
|
|
2556
|
-
}
|
|
2594
|
+
unsubs.forEach(({ path: path2, off }) => {
|
|
2595
|
+
off();
|
|
2596
|
+
message_default(`useObserver: Stopped monitoring "${path2}"`);
|
|
2557
2597
|
});
|
|
2558
2598
|
};
|
|
2559
2599
|
} catch (err) {
|
|
@@ -3260,7 +3300,7 @@ init_internal();
|
|
|
3260
3300
|
|
|
3261
3301
|
// package.json
|
|
3262
3302
|
var package_default = {
|
|
3263
|
-
version: "4.9.
|
|
3303
|
+
version: "4.9.30"};
|
|
3264
3304
|
|
|
3265
3305
|
// core/global.ts
|
|
3266
3306
|
init_env();
|
|
@@ -3279,20 +3319,20 @@ __name(typed, "typed");
|
|
|
3279
3319
|
// functions/history/index.ts
|
|
3280
3320
|
init_esm_shims();
|
|
3281
3321
|
init_internal();
|
|
3322
|
+
init_env();
|
|
3282
3323
|
init_message();
|
|
3283
3324
|
function _clone(value) {
|
|
3284
3325
|
if (value === null || typeof value !== "object") return value;
|
|
3285
|
-
const ctor = value.constructor?.name;
|
|
3286
|
-
if (ctor !== "Object" && ctor !== "Array") return value;
|
|
3287
3326
|
try {
|
|
3288
|
-
return
|
|
3289
|
-
} catch {
|
|
3327
|
+
return deepRaw(value, /* @__PURE__ */ new WeakSet());
|
|
3328
|
+
} catch (err) {
|
|
3329
|
+
if (DEV) console.warn("[memorio history] deep clone failed:", err);
|
|
3290
3330
|
return value;
|
|
3291
3331
|
}
|
|
3292
3332
|
}
|
|
3293
3333
|
__name(_clone, "_clone");
|
|
3294
3334
|
var snapshot = /* @__PURE__ */ __name(() => {
|
|
3295
|
-
return
|
|
3335
|
+
return deepRaw(state2, /* @__PURE__ */ new WeakSet());
|
|
3296
3336
|
}, "snapshot");
|
|
3297
3337
|
var diff = /* @__PURE__ */ __name((snap) => {
|
|
3298
3338
|
const changes = [];
|
|
@@ -3442,9 +3482,11 @@ var clearRedo = /* @__PURE__ */ __name(() => {
|
|
|
3442
3482
|
init_esm_shims();
|
|
3443
3483
|
init_store();
|
|
3444
3484
|
init_idb();
|
|
3485
|
+
init_env();
|
|
3445
3486
|
|
|
3446
3487
|
// functions/memory/journal.ts
|
|
3447
3488
|
init_esm_shims();
|
|
3489
|
+
init_env();
|
|
3448
3490
|
init_store();
|
|
3449
3491
|
var g2 = globalThis;
|
|
3450
3492
|
function namespace() {
|
|
@@ -3465,7 +3507,8 @@ function parseEntry(raw) {
|
|
|
3465
3507
|
try {
|
|
3466
3508
|
if (typeof raw === "string") return JSON.parse(raw);
|
|
3467
3509
|
return raw;
|
|
3468
|
-
} catch {
|
|
3510
|
+
} catch (err) {
|
|
3511
|
+
if (DEV) console.warn("[memorio memory] failed to parse journal entry:", err);
|
|
3469
3512
|
return null;
|
|
3470
3513
|
}
|
|
3471
3514
|
}
|
|
@@ -3549,10 +3592,12 @@ var journal = {
|
|
|
3549
3592
|
* local operations, then optionally pulls newer remote operations and applies
|
|
3550
3593
|
* them. No-op (empty ack) when no provider is configured.
|
|
3551
3594
|
*
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3595
|
+
* When pulling remote entries, HLC timestamps are used to order operations
|
|
3596
|
+
* causally. Concurrent writes to the same key (HLC tie) are resolved via the
|
|
3597
|
+
* provider's `resolveConflict` method (if provided), otherwise the REMOTE
|
|
3598
|
+
* entry wins by default (logged at warn level in dev). Provide a resolver on
|
|
3599
|
+
* the sync provider to override this behaviour and prevent silent local data
|
|
3600
|
+
* loss.
|
|
3556
3601
|
*/
|
|
3557
3602
|
async replay() {
|
|
3558
3603
|
const ack = { synced: [], resolved: [] };
|
|
@@ -3588,7 +3633,11 @@ var journal = {
|
|
|
3588
3633
|
if (ha && hb) {
|
|
3589
3634
|
const cmp = hlc.compare(ha, hb);
|
|
3590
3635
|
if (cmp === 0) {
|
|
3591
|
-
const
|
|
3636
|
+
const hasResolver = typeof provider.resolveConflict === "function";
|
|
3637
|
+
const resolution = hasResolver ? await provider.resolveConflict(localEntry, e) : "remote";
|
|
3638
|
+
if (!hasResolver && DEV) {
|
|
3639
|
+
console.warn(`[memorio memory] replay() conflict for "${e.id}": no resolveConflict handler provided, defaulting to remote-wins`);
|
|
3640
|
+
}
|
|
3592
3641
|
ack.resolved?.push({ id: e.id, resolution });
|
|
3593
3642
|
if (resolution === "local") continue;
|
|
3594
3643
|
} else if (cmp > 0) {
|
|
@@ -3599,7 +3648,8 @@ var journal = {
|
|
|
3599
3648
|
await this.append(e, e.operation || "confirm");
|
|
3600
3649
|
await this.markSynced([e.id]);
|
|
3601
3650
|
}
|
|
3602
|
-
} catch (
|
|
3651
|
+
} catch (err) {
|
|
3652
|
+
if (DEV) console.warn("[memorio memory] sync pull failed:", err);
|
|
3603
3653
|
}
|
|
3604
3654
|
}
|
|
3605
3655
|
return ack;
|
|
@@ -3638,7 +3688,8 @@ async function recordJournal(entry, operation) {
|
|
|
3638
3688
|
if (entry.scope === "hot") return;
|
|
3639
3689
|
try {
|
|
3640
3690
|
await journal_default.append(entry, operation);
|
|
3641
|
-
} catch (
|
|
3691
|
+
} catch (err) {
|
|
3692
|
+
if (DEV) console.warn("[memorio memory] journal append failed:", err);
|
|
3642
3693
|
}
|
|
3643
3694
|
}
|
|
3644
3695
|
__name(recordJournal, "recordJournal");
|
|
@@ -3801,7 +3852,8 @@ async function loadFromScope(id, scope, isSuperseded = false) {
|
|
|
3801
3852
|
const entry = JSON.parse(raw);
|
|
3802
3853
|
entry.id = entry.id || id;
|
|
3803
3854
|
return entry;
|
|
3804
|
-
} catch {
|
|
3855
|
+
} catch (err) {
|
|
3856
|
+
if (DEV) console.warn("[memorio memory] failed to parse memory entry:", err);
|
|
3805
3857
|
return null;
|
|
3806
3858
|
}
|
|
3807
3859
|
}
|
|
@@ -3847,7 +3899,8 @@ async function getAllIndexes() {
|
|
|
3847
3899
|
const raw = store.get(MEMORY_INDEX_KEY);
|
|
3848
3900
|
if (!raw) return {};
|
|
3849
3901
|
return JSON.parse(raw);
|
|
3850
|
-
} catch {
|
|
3902
|
+
} catch (err) {
|
|
3903
|
+
if (DEV) console.warn("[memorio memory] failed to parse memory index:", err);
|
|
3851
3904
|
return {};
|
|
3852
3905
|
}
|
|
3853
3906
|
}
|
|
@@ -4118,6 +4171,7 @@ var memory = {
|
|
|
4118
4171
|
|
|
4119
4172
|
// functions/inspect/index.ts
|
|
4120
4173
|
init_esm_shims();
|
|
4174
|
+
init_env();
|
|
4121
4175
|
var stateKeys = /* @__PURE__ */ __name(() => {
|
|
4122
4176
|
const raw = deepRaw(state2);
|
|
4123
4177
|
return Object.keys(raw).filter(
|
|
@@ -4184,11 +4238,10 @@ function _getValue(path2) {
|
|
|
4184
4238
|
__name(_getValue, "_getValue");
|
|
4185
4239
|
function _clone2(value) {
|
|
4186
4240
|
if (value === null || typeof value !== "object") return value;
|
|
4187
|
-
const ctor = value.constructor?.name;
|
|
4188
|
-
if (ctor !== "Object" && ctor !== "Array") return value;
|
|
4189
4241
|
try {
|
|
4190
|
-
return
|
|
4191
|
-
} catch {
|
|
4242
|
+
return deepRaw(value, /* @__PURE__ */ new WeakSet());
|
|
4243
|
+
} catch (err) {
|
|
4244
|
+
if (DEV) console.warn("[memorio inspect] deep clone failed:", err);
|
|
4192
4245
|
return value;
|
|
4193
4246
|
}
|
|
4194
4247
|
}
|
|
@@ -4215,15 +4268,18 @@ if (!("memorio" in globalThis)) {
|
|
|
4215
4268
|
enumerable: false
|
|
4216
4269
|
}
|
|
4217
4270
|
);
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4271
|
+
if (!("events" in globalThis)) {
|
|
4272
|
+
Object.defineProperty(
|
|
4273
|
+
globalThis,
|
|
4274
|
+
"events",
|
|
4275
|
+
{
|
|
4276
|
+
value: {},
|
|
4277
|
+
writable: true,
|
|
4278
|
+
enumerable: false,
|
|
4279
|
+
configurable: true
|
|
4280
|
+
}
|
|
4281
|
+
);
|
|
4282
|
+
}
|
|
4227
4283
|
if (typeof globalThis.addEventListener === "function") {
|
|
4228
4284
|
dispatch.listen("memorio:memory:cleanup", async () => memory.forgetExpired?.());
|
|
4229
4285
|
}
|
package/llms.txt
CHANGED
|
@@ -100,7 +100,7 @@ store.removeAll()
|
|
|
100
100
|
store.clearAll() // alias
|
|
101
101
|
|
|
102
102
|
// Check size
|
|
103
|
-
console.debug(store.size(), '
|
|
103
|
+
console.debug(store.size(), 'kilobytes')
|
|
104
104
|
|
|
105
105
|
// Check if persistent (real localStorage vs memory fallback)
|
|
106
106
|
console.debug(store.isPersistent) // true → real localStorage
|
|
@@ -134,7 +134,7 @@ session.removeAll()
|
|
|
134
134
|
session.clearAll() // alias
|
|
135
135
|
|
|
136
136
|
// Check size
|
|
137
|
-
console.debug(session.size(), '
|
|
137
|
+
console.debug(session.size(), 'kilobytes')
|
|
138
138
|
|
|
139
139
|
// Check if persistent
|
|
140
140
|
console.debug(session.isPersistent)
|
|
@@ -466,15 +466,23 @@ MIT © Dario Passariello (BigLogic Inc Canada)
|
|
|
466
466
|
|
|
467
467
|
Internal event system used by state changes; also usable directly for custom events.
|
|
468
468
|
|
|
469
|
+
Listeners are stored as a *list* per event name, so multiple subscribers on the
|
|
470
|
+
same name each keep firing. `dispatch.listen` returns an unsubscribe function
|
|
471
|
+
for that specific subscriber — call it to remove only that one listener without
|
|
472
|
+
affecting others registered under the same name.
|
|
473
|
+
|
|
469
474
|
```javascript
|
|
470
475
|
// Dispatch a custom event
|
|
471
476
|
memorio.dispatch.set('custom:event', { detail: { data: 'value' } })
|
|
472
477
|
|
|
473
|
-
// Listen for an event
|
|
474
|
-
memorio.dispatch.listen('custom:event', (e) => {
|
|
478
|
+
// Listen for an event (returns an unsubscribe function)
|
|
479
|
+
const off = memorio.dispatch.listen('custom:event', (e) => {
|
|
475
480
|
console.debug('Event triggered:', e.detail)
|
|
476
481
|
})
|
|
477
482
|
|
|
478
|
-
// Remove listener
|
|
483
|
+
// Remove only this listener via the returned unsubscribe
|
|
484
|
+
off()
|
|
485
|
+
|
|
486
|
+
// Remove *all* listeners for a name (imperative bulk removal)
|
|
479
487
|
memorio.dispatch.remove('custom:event')
|
|
480
488
|
```
|