memorio 4.9.31 → 4.9.35

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 CHANGED
@@ -34,6 +34,7 @@ The global API is a **choice**, not an architectural requirement.
34
34
 
35
35
  - [Why memorio](#why-memorio)
36
36
  - [Global is optional](#global-is-optional)
37
+ - [Before / After](#before--after)
37
38
  - [Which layer should I use](#which-layer-should-i-use)
38
39
  - [Install](#install)
39
40
  - [Quick start](#quick-start)
@@ -50,6 +51,7 @@ The global API is a **choice**, not an architectural requirement.
50
51
  - [When to use something else](#when-to-use-something-else)
51
52
  - [Design philosophy](#design-philosophy)
52
53
  - [A mental model](#a-mental-model)
54
+ - [Recipes](#recipes)
53
55
  - [License](#license)
54
56
 
55
57
  ---
@@ -124,6 +126,30 @@ npm install sql.js # sqlite engine
124
126
 
125
127
  **Zero production dependencies** in the core package. See [Security](#security).
126
128
 
129
+ ## Before / After
130
+
131
+ ```ts
132
+ // Without memorio
133
+ const [user, setUser] = useState(null)
134
+ useEffect(() => {
135
+ const saved = localStorage.getItem('user')
136
+ if (saved) setUser(JSON.parse(saved))
137
+ }, [])
138
+ useEffect(() => {
139
+ localStorage.setItem('user', JSON.stringify(user))
140
+ }, [user])
141
+ ```
142
+
143
+ ```ts
144
+ // With memorio
145
+ import 'memorio'
146
+ store.set('user', { name: 'Sara' }) // persistent immediately, no boilerplate
147
+
148
+ const user = store.get('user')
149
+ ```
150
+
151
+ Same result, one reactive line instead of two effects and manual JSON serialization.
152
+
127
153
  ## Quick start
128
154
 
129
155
  ```ts
@@ -529,6 +555,29 @@ memorio is intentionally not everything.
529
555
 
530
556
  The important part is not that memorio has many layers — it's that **you do not have to use them all**.
531
557
 
558
+ ## Sneak Peek
559
+
560
+ ### Making a `state` slice persistent
561
+
562
+ `state` is reactive but not persistent; `store` is persistent but not reactive. `persist()` bridges the two — it restores the value from `store` and then keeps the selected state path synchronized with it:
563
+
564
+ ```ts
565
+ import 'memorio'
566
+
567
+ state.user = { name: 'Sara' }
568
+
569
+ const off = persist('state.user') // restore from store, then persist changes
570
+
571
+ // later, e.g. on component unmount
572
+ off()
573
+ ```
574
+
575
+ `persist()` uses the same path syntax as `observer()`.
576
+
577
+ Nested paths such as `state.user.preferences.theme` are supported, provided the intermediate objects already exist in `state`.
578
+
579
+ In environments without `localStorage` (Node/Deno/edge), `store` falls back to memory. `persist()` still works there, but persistence does not survive process or runtime restarts.
580
+
532
581
  ## License
533
582
 
534
583
  MIT © Dario Passariello
package/index.cjs CHANGED
@@ -1889,6 +1889,7 @@ var schema = {
1889
1889
  };
1890
1890
 
1891
1891
  // functions/state/index.ts
1892
+ init_store();
1892
1893
  init_env();
1893
1894
  var TARGET = /* @__PURE__ */ Symbol("memorio.target");
1894
1895
  var wrapperCache = /* @__PURE__ */ new WeakMap();
@@ -1903,6 +1904,7 @@ var deepRaw = /* @__PURE__ */ __name((val, seen = null) => {
1903
1904
  if (ctor !== "Object" && ctor !== "Array") return val;
1904
1905
  const copy = Array.isArray(val) ? [] : {};
1905
1906
  for (const k of Reflect.ownKeys(val)) {
1907
+ if (!Object.prototype.propertyIsEnumerable.call(val, k)) continue;
1906
1908
  copy[k] = deepRaw(val[k], seen);
1907
1909
  }
1908
1910
  return copy;
@@ -2125,6 +2127,39 @@ if (globalThis[STATE_INSTANCE_KEY]) {
2125
2127
  exports.internal.locked = false;
2126
2128
  message_default("State is now unlocked. Modifications are allowed.");
2127
2129
  };
2130
+ exports.state.persist = (path) => {
2131
+ if (typeof path !== "string" || !path.length) {
2132
+ if (exports.DEV) console.warn('[memorio state] state.persist() requires a non-empty path string (e.g. "state.user")');
2133
+ return () => {
2134
+ };
2135
+ }
2136
+ const normalized = path.startsWith("state.") ? path : "state." + path;
2137
+ const key = normalized.replace(/^state\./, "");
2138
+ const segments = key.split(".");
2139
+ const initial = exports.store.get(key);
2140
+ if (initial !== void 0 && initial !== null) {
2141
+ let acc = exports.state;
2142
+ for (let i = 0; i < segments.length; i++) {
2143
+ const seg = segments[i];
2144
+ if (i === segments.length - 1) {
2145
+ acc[seg] = deepRaw(initial);
2146
+ } else {
2147
+ if (acc[seg] === void 0 || acc[seg] === null) acc[seg] = {};
2148
+ acc = acc[seg];
2149
+ }
2150
+ }
2151
+ }
2152
+ const handler = /* @__PURE__ */ __name(() => {
2153
+ let current = exports.state;
2154
+ for (const seg of segments) {
2155
+ current = current === void 0 || current === null ? void 0 : current[seg];
2156
+ }
2157
+ exports.store.set(key, deepRaw(current));
2158
+ }, "handler");
2159
+ const off = dispatch.listen(normalized, handler);
2160
+ return typeof off === "function" ? off : () => {
2161
+ };
2162
+ };
2128
2163
  globalThis[STATE_INSTANCE_KEY] = exports.state;
2129
2164
  }
2130
2165
 
@@ -3303,7 +3338,7 @@ init_internal();
3303
3338
 
3304
3339
  // package.json
3305
3340
  var package_default = {
3306
- version: "4.9.31"};
3341
+ version: "4.9.35"};
3307
3342
 
3308
3343
  // core/global.ts
3309
3344
  init_env();
package/index.js CHANGED
@@ -1886,6 +1886,7 @@ var schema = {
1886
1886
  };
1887
1887
 
1888
1888
  // functions/state/index.ts
1889
+ init_store();
1889
1890
  init_env();
1890
1891
  var TARGET = /* @__PURE__ */ Symbol("memorio.target");
1891
1892
  var wrapperCache = /* @__PURE__ */ new WeakMap();
@@ -1900,6 +1901,7 @@ var deepRaw = /* @__PURE__ */ __name((val, seen = null) => {
1900
1901
  if (ctor !== "Object" && ctor !== "Array") return val;
1901
1902
  const copy = Array.isArray(val) ? [] : {};
1902
1903
  for (const k of Reflect.ownKeys(val)) {
1904
+ if (!Object.prototype.propertyIsEnumerable.call(val, k)) continue;
1903
1905
  copy[k] = deepRaw(val[k], seen);
1904
1906
  }
1905
1907
  return copy;
@@ -2122,6 +2124,39 @@ if (globalThis[STATE_INSTANCE_KEY]) {
2122
2124
  internal_default.locked = false;
2123
2125
  message_default("State is now unlocked. Modifications are allowed.");
2124
2126
  };
2127
+ state2.persist = (path2) => {
2128
+ if (typeof path2 !== "string" || !path2.length) {
2129
+ if (DEV) console.warn('[memorio state] state.persist() requires a non-empty path string (e.g. "state.user")');
2130
+ return () => {
2131
+ };
2132
+ }
2133
+ const normalized = path2.startsWith("state.") ? path2 : "state." + path2;
2134
+ const key = normalized.replace(/^state\./, "");
2135
+ const segments = key.split(".");
2136
+ const initial = store.get(key);
2137
+ if (initial !== void 0 && initial !== null) {
2138
+ let acc = state2;
2139
+ for (let i = 0; i < segments.length; i++) {
2140
+ const seg = segments[i];
2141
+ if (i === segments.length - 1) {
2142
+ acc[seg] = deepRaw(initial);
2143
+ } else {
2144
+ if (acc[seg] === void 0 || acc[seg] === null) acc[seg] = {};
2145
+ acc = acc[seg];
2146
+ }
2147
+ }
2148
+ }
2149
+ const handler = /* @__PURE__ */ __name(() => {
2150
+ let current = state2;
2151
+ for (const seg of segments) {
2152
+ current = current === void 0 || current === null ? void 0 : current[seg];
2153
+ }
2154
+ store.set(key, deepRaw(current));
2155
+ }, "handler");
2156
+ const off = dispatch.listen(normalized, handler);
2157
+ return typeof off === "function" ? off : () => {
2158
+ };
2159
+ };
2125
2160
  globalThis[STATE_INSTANCE_KEY] = state2;
2126
2161
  }
2127
2162
 
@@ -3300,7 +3335,7 @@ init_internal();
3300
3335
 
3301
3336
  // package.json
3302
3337
  var package_default = {
3303
- version: "4.9.31"};
3338
+ version: "4.9.35"};
3304
3339
 
3305
3340
  // core/global.ts
3306
3341
  init_env();