@nativelayer.dev/restate 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  # restate
8
8
 
9
- version `0.2.0`
9
+ version `0.2.1`
10
10
 
11
11
  A minimal, framework-agnostic reactive state management library built on ES6 proxies. `restate` provides:
12
12
 
@@ -25,19 +25,19 @@ Import directly in a browser or module bundler:
25
25
 
26
26
  ```js
27
27
  // ESM (recommended)
28
- import { restate } from './dist/esm/restate.min.js';
28
+ import { restate } from './dist/restate.esm.min.js';
29
29
 
30
30
  // CommonJS (Node.js)
31
- const { restate } = require('./dist/cjs/restate.min.js');
31
+ const { restate } = require('./dist/restate.cjs.min.js');
32
32
  ```
33
33
 
34
34
  ### Which format should I use?
35
35
 
36
36
  | Your setup | Use this |
37
37
  |------------|----------|
38
- | Vite, esbuild, modern bundler | `dist/esm/` (recommended) |
39
- | Node.js with `require()` | `dist/cjs/` |
40
- | Node.js with `"type": "module"` | `dist/esm/` |
38
+ | Vite, esbuild, modern bundler | `dist/restate.esm.min.js` (recommended) |
39
+ | Node.js with `require()` | `dist/restate.cjs.min.js` |
40
+ | Node.js with `"type": "module"` | `dist/restate.esm.min.js` |
41
41
 
42
42
  ## Basic Usage
43
43
 
@@ -202,7 +202,7 @@ state.count = 7; // Throws or no-op
202
202
  Define reactive computed properties with automatic dependency tracking:
203
203
 
204
204
  ```js
205
- import { restate } from './dist/esm/restate.min.js';
205
+ import { restate } from './dist/restate.esm.min.js';
206
206
 
207
207
  // Initialize state - $computed is built-in, no plugin needed
208
208
  const state = restate({ a: 1, b: 2 });
@@ -232,7 +232,7 @@ console.log(state.double); // logs no 'computing sum' but recomputes double: out
232
232
  Chaining computed values:
233
233
 
234
234
  ```js
235
- import { restate } from './dist/esm/restate.min.js';
235
+ import { restate } from './dist/restate.esm.min.js';
236
236
 
237
237
  // No plugin needed - $computed is built-in
238
238
  const state = restate({ a: 2, b: 3 });
@@ -376,9 +376,9 @@ Plugins hook into `restate`'s lifecycle events (like `beforeSet`, `afterSet`, `b
376
376
  Chain optional plugins for additional features:
377
377
 
378
378
  ```js
379
- import { restate } from './dist/esm/restate.min.js';
380
- import { PersistencePlugin } from './dist/esm/plugins/persistence.min.js';
381
- import { ImmutablePlugin } from './dist/esm/plugins/immutable.min.js';
379
+ import { restate } from './dist/restate.esm.min.js';
380
+ import { PersistencePlugin } from './dist/plugin/persistence.esm.min.js';
381
+ import { ImmutablePlugin } from './dist/plugin/immutable.esm.min.js';
382
382
 
383
383
  // Chainable registration:
384
384
  const state = restate({ count: 0, items: [] })
@@ -533,7 +533,7 @@ state.use(ImmutablePlugin, {
533
533
  By setting `strict: false`, direct property mutations are allowed without throwing:
534
534
 
535
535
  ```js
536
- import { ImmutablePlugin } from './dist/esm/plugins/immutable.min.js';
536
+ import { ImmutablePlugin } from './dist/plugin/immutable.esm.min.js';
537
537
  const state = restate({ count: 0, user: { name: 'Alice' } })
538
538
  .use(ImmutablePlugin, { strict: false });
539
539
 
@@ -561,7 +561,7 @@ state.count = 3; // works without error
561
561
  With `strict: true`, direct property mutations throw errors, but you can still apply updates using `$set`:
562
562
 
563
563
  ```js
564
- import { ImmutablePlugin } from './dist/esm/plugins/immutable.min.js';
564
+ import { ImmutablePlugin } from './dist/plugin/immutable.esm.min.js';
565
565
  const state = restate({ count: 0 })
566
566
  .use(ImmutablePlugin, { strict: true });
567
567
 
@@ -578,8 +578,8 @@ console.log(state.count); // 1
578
578
  You can register custom mutation methods under strict immutability by using `$set` within your methods:
579
579
 
580
580
  ```js
581
- import { restate } from './dist/esm/restate.min.js';
582
- import { ImmutablePlugin } from './dist/esm/plugins/immutable.min.js';
581
+ import { restate } from './dist/restate.esm.min.js';
582
+ import { ImmutablePlugin } from './dist/plugin/immutable.esm.min.js';
583
583
 
584
584
  const state = restate({ count: 0 })
585
585
  .use(ImmutablePlugin, { strict: true });
@@ -635,8 +635,8 @@ Hooks:
635
635
  - `onDestroy` → clear history
636
636
 
637
637
  ```js
638
- import { restate } from './dist/esm/restate.min.js';
639
- import { HistoryPlugin } from './dist/esm/plugins/history.min.js';
638
+ import { restate } from './dist/restate.esm.min.js';
639
+ import { HistoryPlugin } from './dist/plugin/history.esm.min.js';
640
640
 
641
641
  // Initialize state with history tracking
642
642
  const state = restate({ count: 0 })
@@ -812,8 +812,8 @@ Validate types or values on state updates using custom validator functions.
812
812
  Usage:
813
813
 
814
814
  ```js
815
- import { restate } from './dist/esm/restate.min.js';
816
- import { ValidatePlugin } from './dist/esm/plugins/validate.min.js';
815
+ import { restate } from './dist/restate.esm.min.js';
816
+ import { ValidatePlugin } from './dist/plugin/validate.esm.min.js';
817
817
 
818
818
  const state = restate({ age: 0, name: '' })
819
819
  .use(ValidatePlugin({
@@ -905,8 +905,8 @@ listState.items = ['a','b','c']; // throws TypeError
905
905
  ### Simple Persistence + Methods
906
906
 
907
907
  ```js
908
- import { restate } from './dist/esm/restate.min.js';
909
- import { PersistencePlugin } from './dist/esm/plugins/persistence.min.js';
908
+ import { restate } from './dist/restate.esm.min.js';
909
+ import { PersistencePlugin } from './dist/plugin/persistence.esm.min.js';
910
910
 
911
911
  // Initialize with persistence ($methods is built-in, no plugin needed)
912
912
  const state = restate({ count: 0, items: [] })
@@ -1303,47 +1303,12 @@ All libraries have different trade-offs: `restate` provides fine-grained change
1303
1303
 
1304
1304
  ## License
1305
1305
 
1306
- `restate` is **dual-licensed** to support both open-source and commercial use.
1306
+ `restate` is licensed under the **[PolyForm Noncommercial License 1.0.0](./LICENSE)**.
1307
1307
 
1308
- ### Open-Source / Non-Commercial Use
1308
+ - Free for personal, educational, research, and non-commercial use
1309
+ - ✅ Non-profit organizations and public institutions
1310
+ - ❌ Commercial use requires a separate license
1309
1311
 
1310
- `restate` is available under the **MIT License** for:
1312
+ See [LICENSE](./LICENSE) for full terms: https://polyformproject.org/licenses/noncommercial/1.0.0
1311
1313
 
1312
- - Open-source projects (OSI-approved licenses)
1313
- - ✅ Personal projects and non-revenue generating applications
1314
- - ✅ Educational purposes and academic research
1315
- - ✅ Non-profit organizations
1316
- - ✅ Internal company tools (non-revenue generating)
1317
- - ✅ Prototypes and MVPs
1318
-
1319
- See [LICENSE](./LICENSE) and [LICENSE-NONCOMMERCIAL.md](./LICENSE-NONCOMMERCIAL.md) for full details.
1320
-
1321
- ---
1322
-
1323
- ### Commercial Use
1324
-
1325
- A **commercial license** is required for:
1326
-
1327
- - ❌ Proprietary software and closed-source commercial applications
1328
- - ❌ SaaS products and revenue-generating applications
1329
- - ❌ Enterprise deployments and large-scale corporate use
1330
- - ❌ White-label products sold or licensed to third parties
1331
-
1332
- **Commercial licenses include:**
1333
-
1334
- - Legal protection and indemnification
1335
- - Priority support and SLA
1336
- - Updates and bug fixes during license term
1337
- - Custom licensing terms for enterprise needs
1338
-
1339
- See [EULA-COMMERCIAL.md](./EULA-COMMERCIAL.md) for commercial licensing terms.
1340
-
1341
- ---
1342
-
1343
- ### Licensing Inquiries
1344
-
1345
- **For commercial licensing, pricing, or questions:**
1346
-
1347
- > ynck.chrl@protonmail.com
1348
-
1349
- I'll be happy to discuss licensing options that fit your needs.
1314
+ **For commercial licensing:** info@nativelayer.dev
@@ -1,4 +1,4 @@
1
- /*! plugin/async v0.2.0 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
1
+ /*! plugin/async v0.2.1 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
2
2
  "use strict";
3
3
  /*!
4
4
  * restate AsyncPlugin v1.0.0
@@ -1,4 +1,4 @@
1
- /*! plugin/async v0.2.0 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
1
+ /*! plugin/async v0.2.1 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
2
2
  /*!
3
3
  * restate AsyncPlugin v1.0.0
4
4
  * restate Async Plugin
@@ -1,4 +1,4 @@
1
- /*! plugin/history v0.2.0 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
1
+ /*! plugin/history v0.2.1 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
2
2
  "use strict";
3
3
  /*!
4
4
  * restate HistoryPlugin v1.0.0
@@ -1,4 +1,4 @@
1
- /*! plugin/history v0.2.0 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
1
+ /*! plugin/history v0.2.1 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
2
2
  /*!
3
3
  * restate HistoryPlugin v1.0.0
4
4
  * History Plugin for restate (closure-based)
@@ -1,4 +1,4 @@
1
- /*! plugin/immutable v0.2.0 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
1
+ /*! plugin/immutable v0.2.1 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
2
2
  "use strict";
3
3
  /*!
4
4
  * restate ImmutablePlugin v1.0.0
@@ -1,4 +1,4 @@
1
- /*! plugin/immutable v0.2.0 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
1
+ /*! plugin/immutable v0.2.1 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
2
2
  /*!
3
3
  * restate ImmutablePlugin v1.0.0
4
4
  * Immutable Plugin for restate (hook-based, no double proxy)
@@ -1,4 +1,4 @@
1
- /*! plugin/persistence v0.2.0 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
1
+ /*! plugin/persistence v0.2.1 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
2
2
  "use strict";
3
3
  /*!
4
4
  * restate PersistencePlugin v1.0.0
@@ -1,4 +1,4 @@
1
- /*! plugin/persistence v0.2.0 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
1
+ /*! plugin/persistence v0.2.1 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
2
2
  /*!
3
3
  * restate PersistencePlugin v1.0.0
4
4
  * Persistence Plugin for restate (closure-based)
@@ -1,4 +1,4 @@
1
- /*! plugin/validate v0.2.0 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
1
+ /*! plugin/validate v0.2.1 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
2
2
  "use strict";
3
3
  /*!
4
4
  * restate ValidatePlugin v1.0.0
@@ -1,4 +1,4 @@
1
- /*! plugin/validate v0.2.0 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
1
+ /*! plugin/validate v0.2.1 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
2
2
  /*!
3
3
  * restate ValidatePlugin v1.0.0
4
4
  * Plugin to validate types or values on set operations
@@ -1,2 +1,2 @@
1
- /*! restate v0.2.0 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
1
+ /*! restate v0.2.1 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
2
2
  "use strict";const e=["$onChange","$watch","$unwatch","$set","$computed","$dependencies","$methods","$destroy","$registry","use"],t=["_"];class s{constructor(e={},t={}){this._state=e,this._exactWatchers=new Map,this._shallowWildcards=new Map,this._deepWildcards=new Map,this._proxyCache=new WeakMap,this._computed={},this._computedDependencies=new Map,this._computedCache=new Map,this._tracking=!1,this._currentComputedKey=null,this._accessedPaths=new Set,this._maxDepth=t.maxDepth||100,this._onChange=null,this._isDestroyed=!1,this._bulkMode=!1,this._bulkChanges=new Map,this._plugins=new Map,this._pluginLoadOrder=[],this._pluginHooks={beforeNotify:[],afterNotify:[],beforeSet:[],afterSet:[],beforeBulk:[],afterBulk:[],onDestroy:[]},this._methodRegistry=new Map,this._proxy=this._wrap(e,[],!0),this._setupProxyMethods(),t.plugins&&t.plugins.forEach(e=>this.use(e))}_registerMethod(e,t,s=!1,r=null){this._methodRegistry.set(e,{source:t,configurable:s,originalFn:r})}_isReservedPrefix(e){return t.some(t=>e.startsWith(t))}_checkMethodCollision(t,s,r=!1){if(this._isReservedPrefix(t))return{error:`Plugin '${s}' cannot define method '${t}': names starting with '_' are reserved for internal use`};const i=this._methodRegistry.get(t);if(!i)return null;if(e.includes(t))return{error:`Plugin '${s}' cannot override core method '${t}'`};if(!r){const e=this._pluginLoadOrder,r=i.source.startsWith("plugin:")?e.indexOf(i.source.replace("plugin:",""))+1:0,o=e.indexOf(s)+1;return{error:`Method collision: '${t}'\n - First defined by: ${i.source}${r?` (loaded #${r})`:""}\n - Attempted by: plugin '${s}'${o?` (loaded #${o})`:""}\n Consider renaming the method or using 'overrides: ["${t}"]' in the plugin definition.`}}return i.configurable?null:{error:`Plugin '${s}' cannot override '${t}': method is not configurable (defined by ${i.source})`}}use(e){if("function"==typeof e&&(e=e(this)),!e.name)throw new Error("Plugin must have a name");if(this._plugins.has(e.name))throw new Error(`Plugin '${e.name}' is already registered`);if(this._pluginLoadOrder.push(e.name),this._plugins.set(e.name,e),e.hooks&&Object.keys(e.hooks).forEach(t=>{this._pluginHooks[t]&&this._pluginHooks[t].push(e.hooks[t])}),e.wrap&&Object.keys(e.wrap).forEach(t=>{const s=e.wrap[t],r=this._methodRegistry.get(t);if(!r)throw new Error(`Plugin '${e.name}' attempted to wrap '${t}', but it doesn't exist.\n Available methods: ${Array.from(this._methodRegistry.keys()).join(", ")}`);if("function"!=typeof s)throw new Error(`Plugin '${e.name}': wrap['${t}'] must be a function`);const i=this._proxy[t];if("function"!=typeof i)throw new Error(`Plugin '${e.name}': cannot wrap '${t}' - it's not a function`);const o=s(i.bind(this._proxy));Object.defineProperty(this._proxy,t,{value:o.bind(this),enumerable:!1,configurable:!0,writable:!0}),this._methodRegistry.set(t,{...r,wrappedBy:e.name,originalFn:i})}),e.methods){const t=e.overrides||[];Object.keys(e.methods).forEach(s=>{const r=t.includes(s),i=this._checkMethodCollision(s,e.name,r);if(i)throw new Error(i.error);if(r&&this._methodRegistry.has(s)){const t=this._methodRegistry.get(s);console.warn(`restate: Plugin '${e.name}' is overriding method '${s}' (previously defined by ${t.source})`)}Object.defineProperty(this._proxy,s,{value:e.methods[s].bind(this),enumerable:!1,configurable:!0,writable:!0}),this._registerMethod(s,`plugin:${e.name}`,!0)})}return e.install&&e.install.call(this),this}_notify(e,t,s){this._isDestroyed||(this._runHooks("beforeNotify",e,t,s),this._bulkMode?this._bulkChanges.set(e,t):this._notifyImmediate(e,t,s))}_notifyImmediate(e,t,s){if(this._isDestroyed)return;if("function"==typeof this._onChange)try{this._onChange(e,t,s)}catch(e){console.error("restate: Error in onChange handler:",e)}const r=this._exactWatchers.get(e);if(r)try{r(e,t,s)}catch(t){console.error(`restate: Error in watcher for '${e}':`,t)}const i=e.split(".");if(i.length>=2){const r=i.slice(0,-1).join("."),o=this._shallowWildcards.get(r);if(o)for(const i of o)try{i(e,t,s)}catch(e){console.error(`restate: Error in watcher for '${r}.*':`,e)}}let o="";for(let r=0;r<i.length-1;r++){o=0===r?i[0]:o+"."+i[r];const n=this._deepWildcards.get(o);if(n)for(const r of n)try{r(e,t,s)}catch(e){console.error(`restate: Error in watcher for '${o}.**':`,e)}}this._runHooks("afterNotify",e,t,s)}_runHooks(e,...t){const s="beforeSet"===e;this._pluginHooks[e].forEach(r=>{if(s)r.call(this,...t);else try{r.call(this,...t)}catch(t){console.error(`restate: Error in ${e} hook:`,t)}})}_bulk(e){if(!this._isDestroyed){if(this._bulkMode)return e(this._proxy);this._runHooks("beforeBulk"),this._bulkMode=!0;try{const t=e(this._proxy);return this._flushBulkChanges(),t}finally{this._bulkMode=!1,this._runHooks("afterBulk")}}}_flushBulkChanges(){if(0!==this._bulkChanges.size){if("function"==typeof this._onChange)try{this._onChange("bulk",Array.from(this._bulkChanges.entries()))}catch(e){console.error("restate: Error in onChange during bulk:",e)}for(const[e,t]of this._bulkChanges){const s=this._exactWatchers.get(e);if(s)try{s(e,t)}catch(t){console.error(`restate: Error in watcher for '${e}':`,t)}const r=e.split(".");if(r.length>=2){const s=r.slice(0,-1).join("."),i=this._shallowWildcards.get(s);if(i)for(const r of i)try{r(e,t)}catch(e){console.error(`restate: Error in watcher for '${s}.*':`,e)}}let i="";for(let s=0;s<r.length-1;s++){i=0===s?r[0]:i+"."+r[s];const o=this._deepWildcards.get(i);if(o)for(const s of o)try{s(e,t)}catch(e){console.error(`restate: Error in watcher for '${i}.**':`,e)}}}this._bulkChanges.clear()}}_getComputedValue(e,t){if(this._computedCache.has(e))return this._computedCache.get(e);if(this._computedCallStack||(this._computedCallStack=new Set),this._computedCallStack.has(e))return void console.error(`restate: Circular dependency detected in computed property '${e}'`);this._computedCallStack.add(e);const s=this._tracking,r=this._currentComputedKey,i=s?[...this._accessedPaths]:null;let o;this._tracking=!0,this._currentComputedKey=e,this._accessedPaths.clear();try{o=t(this._proxy)}catch(t){console.error(`restate: Error computing '${e}':`,t),o=void 0}if(this._computedDependencies.set(e,new Set(this._accessedPaths)),this._computedCache.set(e,o),this._computedCallStack.delete(e),this._tracking=s,this._currentComputedKey=r,i){this._accessedPaths.clear();for(const e of i)this._accessedPaths.add(e)}return o}_invalidateComputed(e){const t=new Set,s=[e];for(;s.length>0;){const e=s.shift(),r=[];for(const[s,i]of this._computedDependencies)i.has(e)&&!t.has(s)&&r.push(s);for(const e of r)this._computedCache.delete(e),this._computedDependencies.delete(e),t.add(e),s.push(e)}}_wrap(e,t=[],s=!1){if(this._isDestroyed)return e;if("object"!=typeof e||null===e)return e;if(t.length>this._maxDepth)return e;if(!s&&this._proxyCache.has(e))return this._proxyCache.get(e);if(Array.isArray(e))return this._wrapArray(e,t);const r=new Proxy(e,{get:(e,r)=>{if(this._isDestroyed)return e[r];const i=String(r);if(s&&i in this._computed)return this._tracking&&this._currentComputedKey&&this._currentComputedKey!==i&&this._accessedPaths.add(i),this._getComputedValue(i,this._computed[i]);if(this._tracking&&this._currentComputedKey){const e=t.length>0?t.concat(r).join("."):i;this._accessedPaths.add(e)}const o=e[r];return"object"==typeof o&&null!==o?this._wrap(o,t.concat(r)):o},set:(e,s,r)=>{if(this._isDestroyed)return!1;this._runHooks("beforeSet",e,s,r,t);const i=e[s];if(i!==r){"object"==typeof i&&null!==i&&this._proxyCache.delete(i),e[s]=r;const o=t.concat(s).join(".");this._invalidateComputed(String(s)),t.length>0&&this._invalidateComputed(o),this._notify(o,r,i)}return this._runHooks("afterSet",e,s,r,t),!0},has:(e,t)=>{const r=String(t);return!(!s||!(r in this._computed))||t in e},ownKeys:e=>{const t=Reflect.ownKeys(e);if(s){const e=Object.keys(this._computed);for(const s of e)t.includes(s)||t.push(s)}return t},getOwnPropertyDescriptor:(e,t)=>{const r=String(t);return s&&r in this._computed?{configurable:!0,enumerable:!0,value:this._getComputedValue(r,this._computed[r])}:Object.getOwnPropertyDescriptor(e,t)}});return s||this._proxyCache.set(e,r),r}_wrapArray(e,t){if(t.length>this._maxDepth)return e;if(this._proxyCache.has(e))return this._proxyCache.get(e);const s=new Proxy(e,{get:(e,s)=>{if(this._isDestroyed)return e[s];if(this._tracking&&this._currentComputedKey){const e=t.concat(s).join(".");this._accessedPaths.add(e)}if("function"==typeof e[s])return this._wrapArrayMethod(e,s,t);const r=e[s];return"object"==typeof r&&null!==r?this._wrap(r,t.concat(s)):r},set:(e,s,r)=>{if(this._isDestroyed)return!1;const i=e[s];if(i!==r&&("object"==typeof i&&null!==i&&this._proxyCache.delete(i),e[s]=r,!isNaN(s))){const i=t.concat(s).join(".");this._invalidateComputed(i),this._notify(i,r),this._notify(t.concat("length").join("."),e.length)}return!0}});return this._proxyCache.set(e,s),s}_wrapArrayMethod(e,t,s){const r=e[t],i=["push","pop","shift","unshift","splice","sort","reverse","fill","copyWithin"];return function(...o){if(this._isDestroyed)return r.apply(e,o);const n=e.length,h=r.apply(e,o);if(i.includes(t)){const t=s.join(".");this._invalidateComputed(t),this._notify(t,e),e.length!==n&&this._notify(s.concat("length").join("."),e.length)}return h}.bind(this)}_addWatcher(e,t){if(e.endsWith(".**")){const s=e.slice(0,-3);this._deepWildcards.has(s)||this._deepWildcards.set(s,new Set),this._deepWildcards.get(s).add(t)}else if(e.endsWith(".*")){const s=e.slice(0,-2);this._shallowWildcards.has(s)||this._shallowWildcards.set(s,new Set),this._shallowWildcards.get(s).add(t)}else this._exactWatchers.set(e,t)}_removeWatcher(e){if(e.endsWith(".**")){const t=e.slice(0,-3);this._deepWildcards.delete(t)}else if(e.endsWith(".*")){const t=e.slice(0,-2);this._shallowWildcards.delete(t)}else this._exactWatchers.delete(e)}_clearAllWatchers(){this._exactWatchers.clear(),this._shallowWildcards.clear(),this._deepWildcards.clear()}_setupProxyMethods(){const t=(e,t)=>{Object.defineProperty(this._proxy,e,{value:t,enumerable:!1,configurable:!1,writable:!1}),this._registerMethod(e,"core",!1)};t("$onChange",e=>(this._onChange=e,this._proxy)),t("$watch",(e,t)=>"function"==typeof t?(this._addWatcher(e,t),()=>this._proxy.$unwatch(e)):this._proxy),t("$unwatch",e=>(this._removeWatcher(e),this._proxy)),t("$set",e=>"function"==typeof e?this._bulk(e):this._bulk(t=>{this._applyUpdates(t,e)})),t("$computed",e=>{const t=["__proto__","constructor","prototype"];for(const[s,r]of Object.entries(e))t.includes(s)?console.warn(`restate: Skipping reserved computed key '${s}'`):"function"==typeof r?this._computed[s]=r:console.warn(`restate: Computed property '${s}' must be a function`);return this._proxy}),t("$dependencies",()=>{const e={};for(const[t,s]of this._computedDependencies)e[t]=Array.from(s);return e}),t("$methods",t=>(Object.entries(t).forEach(([t,s])=>{if("function"!=typeof s)throw new Error(`restate: Method '${t}' must be a function`);if(e.includes(t))throw new Error(`restate: Cannot define method '${t}' - it's a reserved core method`);if(this._isReservedPrefix(t))throw new Error(`restate: Cannot define method '${t}' - names starting with '_' are reserved`);const r=this._methodRegistry.get(t);r&&r.source.startsWith("plugin:")&&console.warn(`restate: $methods() is overriding '${t}' (previously defined by ${r.source})`),Object.defineProperty(this._proxy,t,{value:s.bind(this._proxy),enumerable:!1,writable:!0,configurable:!0}),this._registerMethod(t,"user",!0)}),this._proxy)),t("$registry",()=>{const e={};for(const[t,s]of this._methodRegistry)e[t]={...s},delete e[t].originalFn;return e}),t("$destroy",()=>(this._runHooks("onDestroy"),this._isDestroyed=!0,this._clearAllWatchers(),this._onChange=null,this._plugins.clear(),this._pluginLoadOrder=[],this._methodRegistry.clear(),this._proxyCache=new WeakMap,this._computed={},this._computedDependencies.clear(),this._computedCache.clear(),this._computedCallStack&&this._computedCallStack.clear(),this._proxy))}_applyUpdates(e,t,s=[]){if(this._isDestroyed)return;const r=["__proto__","constructor","prototype"];for(const[i,o]of Object.entries(t))r.includes(i)||("object"!=typeof o||null===o||Array.isArray(o)?e[i]=o:(e[i]&&"object"==typeof e[i]||(e[i]={}),this._applyUpdates(e[i],o,s.concat(i))))}}exports.restate=function(e={}){const t=new s(e,{plugins:[]}),r=t._proxy;return r.use=function(e,s){const r="function"==typeof e?e(t):e;return t.use(r),t._proxy},r};
@@ -1,2 +1,2 @@
1
- /*! restate v0.2.0 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
1
+ /*! restate v0.2.1 | Copyright (c) 2025 Yannick J.A. Charlery (https://github.com/ynck-chrl/restate) | PolyForm Noncommercial 1.0.0 | https://polyformproject.org/licenses/noncommercial/1.0.0 | Commercial: info@nativelayer.dev */
2
2
  const e=["$onChange","$watch","$unwatch","$set","$computed","$dependencies","$methods","$destroy","$registry","use"],t=["_"];class s{constructor(e={},t={}){this._state=e,this._exactWatchers=new Map,this._shallowWildcards=new Map,this._deepWildcards=new Map,this._proxyCache=new WeakMap,this._computed={},this._computedDependencies=new Map,this._computedCache=new Map,this._tracking=!1,this._currentComputedKey=null,this._accessedPaths=new Set,this._maxDepth=t.maxDepth||100,this._onChange=null,this._isDestroyed=!1,this._bulkMode=!1,this._bulkChanges=new Map,this._plugins=new Map,this._pluginLoadOrder=[],this._pluginHooks={beforeNotify:[],afterNotify:[],beforeSet:[],afterSet:[],beforeBulk:[],afterBulk:[],onDestroy:[]},this._methodRegistry=new Map,this._proxy=this._wrap(e,[],!0),this._setupProxyMethods(),t.plugins&&t.plugins.forEach(e=>this.use(e))}_registerMethod(e,t,s=!1,r=null){this._methodRegistry.set(e,{source:t,configurable:s,originalFn:r})}_isReservedPrefix(e){return t.some(t=>e.startsWith(t))}_checkMethodCollision(t,s,r=!1){if(this._isReservedPrefix(t))return{error:`Plugin '${s}' cannot define method '${t}': names starting with '_' are reserved for internal use`};const i=this._methodRegistry.get(t);if(!i)return null;if(e.includes(t))return{error:`Plugin '${s}' cannot override core method '${t}'`};if(!r){const e=this._pluginLoadOrder,r=i.source.startsWith("plugin:")?e.indexOf(i.source.replace("plugin:",""))+1:0,o=e.indexOf(s)+1;return{error:`Method collision: '${t}'\n - First defined by: ${i.source}${r?` (loaded #${r})`:""}\n - Attempted by: plugin '${s}'${o?` (loaded #${o})`:""}\n Consider renaming the method or using 'overrides: ["${t}"]' in the plugin definition.`}}return i.configurable?null:{error:`Plugin '${s}' cannot override '${t}': method is not configurable (defined by ${i.source})`}}use(e){if("function"==typeof e&&(e=e(this)),!e.name)throw new Error("Plugin must have a name");if(this._plugins.has(e.name))throw new Error(`Plugin '${e.name}' is already registered`);if(this._pluginLoadOrder.push(e.name),this._plugins.set(e.name,e),e.hooks&&Object.keys(e.hooks).forEach(t=>{this._pluginHooks[t]&&this._pluginHooks[t].push(e.hooks[t])}),e.wrap&&Object.keys(e.wrap).forEach(t=>{const s=e.wrap[t],r=this._methodRegistry.get(t);if(!r)throw new Error(`Plugin '${e.name}' attempted to wrap '${t}', but it doesn't exist.\n Available methods: ${Array.from(this._methodRegistry.keys()).join(", ")}`);if("function"!=typeof s)throw new Error(`Plugin '${e.name}': wrap['${t}'] must be a function`);const i=this._proxy[t];if("function"!=typeof i)throw new Error(`Plugin '${e.name}': cannot wrap '${t}' - it's not a function`);const o=s(i.bind(this._proxy));Object.defineProperty(this._proxy,t,{value:o.bind(this),enumerable:!1,configurable:!0,writable:!0}),this._methodRegistry.set(t,{...r,wrappedBy:e.name,originalFn:i})}),e.methods){const t=e.overrides||[];Object.keys(e.methods).forEach(s=>{const r=t.includes(s),i=this._checkMethodCollision(s,e.name,r);if(i)throw new Error(i.error);if(r&&this._methodRegistry.has(s)){const t=this._methodRegistry.get(s);console.warn(`restate: Plugin '${e.name}' is overriding method '${s}' (previously defined by ${t.source})`)}Object.defineProperty(this._proxy,s,{value:e.methods[s].bind(this),enumerable:!1,configurable:!0,writable:!0}),this._registerMethod(s,`plugin:${e.name}`,!0)})}return e.install&&e.install.call(this),this}_notify(e,t,s){this._isDestroyed||(this._runHooks("beforeNotify",e,t,s),this._bulkMode?this._bulkChanges.set(e,t):this._notifyImmediate(e,t,s))}_notifyImmediate(e,t,s){if(this._isDestroyed)return;if("function"==typeof this._onChange)try{this._onChange(e,t,s)}catch(e){console.error("restate: Error in onChange handler:",e)}const r=this._exactWatchers.get(e);if(r)try{r(e,t,s)}catch(t){console.error(`restate: Error in watcher for '${e}':`,t)}const i=e.split(".");if(i.length>=2){const r=i.slice(0,-1).join("."),o=this._shallowWildcards.get(r);if(o)for(const i of o)try{i(e,t,s)}catch(e){console.error(`restate: Error in watcher for '${r}.*':`,e)}}let o="";for(let r=0;r<i.length-1;r++){o=0===r?i[0]:o+"."+i[r];const n=this._deepWildcards.get(o);if(n)for(const r of n)try{r(e,t,s)}catch(e){console.error(`restate: Error in watcher for '${o}.**':`,e)}}this._runHooks("afterNotify",e,t,s)}_runHooks(e,...t){const s="beforeSet"===e;this._pluginHooks[e].forEach(r=>{if(s)r.call(this,...t);else try{r.call(this,...t)}catch(t){console.error(`restate: Error in ${e} hook:`,t)}})}_bulk(e){if(!this._isDestroyed){if(this._bulkMode)return e(this._proxy);this._runHooks("beforeBulk"),this._bulkMode=!0;try{const t=e(this._proxy);return this._flushBulkChanges(),t}finally{this._bulkMode=!1,this._runHooks("afterBulk")}}}_flushBulkChanges(){if(0!==this._bulkChanges.size){if("function"==typeof this._onChange)try{this._onChange("bulk",Array.from(this._bulkChanges.entries()))}catch(e){console.error("restate: Error in onChange during bulk:",e)}for(const[e,t]of this._bulkChanges){const s=this._exactWatchers.get(e);if(s)try{s(e,t)}catch(t){console.error(`restate: Error in watcher for '${e}':`,t)}const r=e.split(".");if(r.length>=2){const s=r.slice(0,-1).join("."),i=this._shallowWildcards.get(s);if(i)for(const r of i)try{r(e,t)}catch(e){console.error(`restate: Error in watcher for '${s}.*':`,e)}}let i="";for(let s=0;s<r.length-1;s++){i=0===s?r[0]:i+"."+r[s];const o=this._deepWildcards.get(i);if(o)for(const s of o)try{s(e,t)}catch(e){console.error(`restate: Error in watcher for '${i}.**':`,e)}}}this._bulkChanges.clear()}}_getComputedValue(e,t){if(this._computedCache.has(e))return this._computedCache.get(e);if(this._computedCallStack||(this._computedCallStack=new Set),this._computedCallStack.has(e))return void console.error(`restate: Circular dependency detected in computed property '${e}'`);this._computedCallStack.add(e);const s=this._tracking,r=this._currentComputedKey,i=s?[...this._accessedPaths]:null;let o;this._tracking=!0,this._currentComputedKey=e,this._accessedPaths.clear();try{o=t(this._proxy)}catch(t){console.error(`restate: Error computing '${e}':`,t),o=void 0}if(this._computedDependencies.set(e,new Set(this._accessedPaths)),this._computedCache.set(e,o),this._computedCallStack.delete(e),this._tracking=s,this._currentComputedKey=r,i){this._accessedPaths.clear();for(const e of i)this._accessedPaths.add(e)}return o}_invalidateComputed(e){const t=new Set,s=[e];for(;s.length>0;){const e=s.shift(),r=[];for(const[s,i]of this._computedDependencies)i.has(e)&&!t.has(s)&&r.push(s);for(const e of r)this._computedCache.delete(e),this._computedDependencies.delete(e),t.add(e),s.push(e)}}_wrap(e,t=[],s=!1){if(this._isDestroyed)return e;if("object"!=typeof e||null===e)return e;if(t.length>this._maxDepth)return e;if(!s&&this._proxyCache.has(e))return this._proxyCache.get(e);if(Array.isArray(e))return this._wrapArray(e,t);const r=new Proxy(e,{get:(e,r)=>{if(this._isDestroyed)return e[r];const i=String(r);if(s&&i in this._computed)return this._tracking&&this._currentComputedKey&&this._currentComputedKey!==i&&this._accessedPaths.add(i),this._getComputedValue(i,this._computed[i]);if(this._tracking&&this._currentComputedKey){const e=t.length>0?t.concat(r).join("."):i;this._accessedPaths.add(e)}const o=e[r];return"object"==typeof o&&null!==o?this._wrap(o,t.concat(r)):o},set:(e,s,r)=>{if(this._isDestroyed)return!1;this._runHooks("beforeSet",e,s,r,t);const i=e[s];if(i!==r){"object"==typeof i&&null!==i&&this._proxyCache.delete(i),e[s]=r;const o=t.concat(s).join(".");this._invalidateComputed(String(s)),t.length>0&&this._invalidateComputed(o),this._notify(o,r,i)}return this._runHooks("afterSet",e,s,r,t),!0},has:(e,t)=>{const r=String(t);return!(!s||!(r in this._computed))||t in e},ownKeys:e=>{const t=Reflect.ownKeys(e);if(s){const e=Object.keys(this._computed);for(const s of e)t.includes(s)||t.push(s)}return t},getOwnPropertyDescriptor:(e,t)=>{const r=String(t);return s&&r in this._computed?{configurable:!0,enumerable:!0,value:this._getComputedValue(r,this._computed[r])}:Object.getOwnPropertyDescriptor(e,t)}});return s||this._proxyCache.set(e,r),r}_wrapArray(e,t){if(t.length>this._maxDepth)return e;if(this._proxyCache.has(e))return this._proxyCache.get(e);const s=new Proxy(e,{get:(e,s)=>{if(this._isDestroyed)return e[s];if(this._tracking&&this._currentComputedKey){const e=t.concat(s).join(".");this._accessedPaths.add(e)}if("function"==typeof e[s])return this._wrapArrayMethod(e,s,t);const r=e[s];return"object"==typeof r&&null!==r?this._wrap(r,t.concat(s)):r},set:(e,s,r)=>{if(this._isDestroyed)return!1;const i=e[s];if(i!==r&&("object"==typeof i&&null!==i&&this._proxyCache.delete(i),e[s]=r,!isNaN(s))){const i=t.concat(s).join(".");this._invalidateComputed(i),this._notify(i,r),this._notify(t.concat("length").join("."),e.length)}return!0}});return this._proxyCache.set(e,s),s}_wrapArrayMethod(e,t,s){const r=e[t],i=["push","pop","shift","unshift","splice","sort","reverse","fill","copyWithin"];return function(...o){if(this._isDestroyed)return r.apply(e,o);const n=e.length,h=r.apply(e,o);if(i.includes(t)){const t=s.join(".");this._invalidateComputed(t),this._notify(t,e),e.length!==n&&this._notify(s.concat("length").join("."),e.length)}return h}.bind(this)}_addWatcher(e,t){if(e.endsWith(".**")){const s=e.slice(0,-3);this._deepWildcards.has(s)||this._deepWildcards.set(s,new Set),this._deepWildcards.get(s).add(t)}else if(e.endsWith(".*")){const s=e.slice(0,-2);this._shallowWildcards.has(s)||this._shallowWildcards.set(s,new Set),this._shallowWildcards.get(s).add(t)}else this._exactWatchers.set(e,t)}_removeWatcher(e){if(e.endsWith(".**")){const t=e.slice(0,-3);this._deepWildcards.delete(t)}else if(e.endsWith(".*")){const t=e.slice(0,-2);this._shallowWildcards.delete(t)}else this._exactWatchers.delete(e)}_clearAllWatchers(){this._exactWatchers.clear(),this._shallowWildcards.clear(),this._deepWildcards.clear()}_setupProxyMethods(){const t=(e,t)=>{Object.defineProperty(this._proxy,e,{value:t,enumerable:!1,configurable:!1,writable:!1}),this._registerMethod(e,"core",!1)};t("$onChange",e=>(this._onChange=e,this._proxy)),t("$watch",(e,t)=>"function"==typeof t?(this._addWatcher(e,t),()=>this._proxy.$unwatch(e)):this._proxy),t("$unwatch",e=>(this._removeWatcher(e),this._proxy)),t("$set",e=>"function"==typeof e?this._bulk(e):this._bulk(t=>{this._applyUpdates(t,e)})),t("$computed",e=>{const t=["__proto__","constructor","prototype"];for(const[s,r]of Object.entries(e))t.includes(s)?console.warn(`restate: Skipping reserved computed key '${s}'`):"function"==typeof r?this._computed[s]=r:console.warn(`restate: Computed property '${s}' must be a function`);return this._proxy}),t("$dependencies",()=>{const e={};for(const[t,s]of this._computedDependencies)e[t]=Array.from(s);return e}),t("$methods",t=>(Object.entries(t).forEach(([t,s])=>{if("function"!=typeof s)throw new Error(`restate: Method '${t}' must be a function`);if(e.includes(t))throw new Error(`restate: Cannot define method '${t}' - it's a reserved core method`);if(this._isReservedPrefix(t))throw new Error(`restate: Cannot define method '${t}' - names starting with '_' are reserved`);const r=this._methodRegistry.get(t);r&&r.source.startsWith("plugin:")&&console.warn(`restate: $methods() is overriding '${t}' (previously defined by ${r.source})`),Object.defineProperty(this._proxy,t,{value:s.bind(this._proxy),enumerable:!1,writable:!0,configurable:!0}),this._registerMethod(t,"user",!0)}),this._proxy)),t("$registry",()=>{const e={};for(const[t,s]of this._methodRegistry)e[t]={...s},delete e[t].originalFn;return e}),t("$destroy",()=>(this._runHooks("onDestroy"),this._isDestroyed=!0,this._clearAllWatchers(),this._onChange=null,this._plugins.clear(),this._pluginLoadOrder=[],this._methodRegistry.clear(),this._proxyCache=new WeakMap,this._computed={},this._computedDependencies.clear(),this._computedCache.clear(),this._computedCallStack&&this._computedCallStack.clear(),this._proxy))}_applyUpdates(e,t,s=[]){if(this._isDestroyed)return;const r=["__proto__","constructor","prototype"];for(const[i,o]of Object.entries(t))r.includes(i)||("object"!=typeof o||null===o||Array.isArray(o)?e[i]=o:(e[i]&&"object"==typeof e[i]||(e[i]={}),this._applyUpdates(e[i],o,s.concat(i))))}}function restate(e={}){const t=new s(e,{plugins:[]}),r=t._proxy;return r.use=function(e,s){const r="function"==typeof e?e(t):e;return t.use(r),t._proxy},r}export{restate};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nativelayer.dev/restate",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "description": "A minimal, framework-agnostic reactive state management library built on ES6 proxies.",
6
6
  "main": "./dist/restate.cjs.min.js",