🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

statezx

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

statezx - npm Package Compare versions

Comparing version
1.0.0
to
1.0.1
+3
-3
dist/statezx.js
/*
statezx 1.0.0 - Simple client-side state manager with events, synchronization, and indexedDB storage
statezx 1.0.1 - Simple client-side state manager with events, synchronization, and indexedDB storage
https://github.com/craigbuckler/statezx#readme
Craig Buckler, 2023-08-24
Craig Buckler, 2023-08-25
*/
var y=class{#e=null;#r=null;#s=null;constructor(t,e,r){return this.#r=t||"db",this.#s=e||1,this.#i(r)}connect(){return this.#i().then(()=>!0).catch(()=>!1)}close(){this.#e.close(),this.#e=null}#i(t){return new Promise((e,r)=>{if(!("indexedDB"in window)){r(new Error("No indexedDB support"));return}let n=indexedDB.open(this.#r,this.#s);n.onsuccess=()=>{this.#e=n.result,e(this)},n.onerror=s=>{r(new Error(`IndexedDB error ${s.target.errorCode}: ${s.target.message}`,{cause:s}))},t&&(n.onupgradeneeded=s=>{t(n.result,s.oldVersion,s.newVersion)})})}get isConnected(){return!!this.#e}get name(){return this.#r}get version(){return this.#s}#u(t,e,r){return new Promise((n,s)=>{let{transaction:o,store:i}=this.#o(t,null,!0);o.oncomplete=()=>n(),o.onerror=u=>{s(new Error(u.target.error.message,{cause:u}))},e=Array.isArray(e)?e:[e],e.forEach(u=>{r?i.put(u):i.add(u)}),o.commit()})}add({store:t,item:e=[]}={}){return this.#u(t,e,!1)}put({store:t,item:e=[]}={}){return this.#u(t,e,!0)}#t(t,e,r,n){return new Promise((s,o)=>{n=Array.isArray(n)?n:[n];let i=r==="delete"||r==="clear",u=this.#o(t,e,i).store[r](...n);u.onsuccess=()=>s(u.result),u.onerror=()=>o(u.error??!1)})}count({store:t,index:e,lowerBound:r,upperBound:n}={}){return this.#t(t,e,"count",this.#n(r,n))}get({store:t,index:e,key:r}={}){return this.#t(t,e,"get",r)}getAll({store:t,index:e,lowerBound:r,upperBound:n,count:s}={}){return this.#t(t,e,"getAll",[this.#n(r,n),s])}getAllKeys({store:t,index:e,lowerBound:r,upperBound:n,count:s}={}){return this.#t(t,e,"getAllKeys",[this.#n(r,n),s])}delete({store:t,key:e}={}){return this.#t(t,null,"delete",[e])}deleteAll({store:t,index:e,lowerBound:r,upperBound:n}={}){return this.#t(t,e,"delete",[this.#n(r,n)])}clear({store:t}={}){return this.#t(t,null,"clear")}getCursor({store:t,index:e,lowerBound:r,upperBound:n,direction:s="next",callback:o}={}){return new Promise((i,u)=>{let c=this.#o(t,e).store.openCursor(this.#n(r,n),s);c.onsuccess=()=>{let p=c.result;p?p.advance(o&&o(p)||1):i(!0)},c.onerror=()=>u(c.error)})}drop(){return new Promise((t,e)=>{this.close();let r=indexedDB.deleteDatabase(this.#r);r.onsuccess=()=>{this.#r=null,this.#s=null,t(!0)},r.onerror=()=>e(!1)})}#o(t,e,r){let n=this.#e.transaction(t,r?"readwrite":"readonly",{durability:r?"strict":"default"}),s=n.objectStore(t);return{transaction:n,store:e&&!r?s.index(e):s}}#n(t,e){let r;return t&&e?r=IDBKeyRange.bound(t,e):t?r=IDBKeyRange.lowerBound(t):e&&(r=IDBKeyRange.upperBound(e)),r}};var d=new Map,h=new Map,a="statezx",m="stateIdx",x=new BroadcastChannel(a),f,l,w=3,g=class extends EventTarget{#e=null;constructor(e){super(),this.#e=e}get stateId(){return this.#e}set(e,r){return typeof r>"u"?Reflect.deleteProperty(this,e):Reflect.set(this,e,r)}addEventListener(){super.addEventListener.apply(this,arguments)}removeEventListener(){super.removeEventListener.apply(this,arguments)}dispatchEvent(e){let r={detail:e,bubbles:!1,cancelable:!1};super.dispatchEvent(new CustomEvent(e.property,r)),super.dispatchEvent(new CustomEvent("*",r))}},v={set:(t,e,r)=>B(t,e,r),deleteProperty:(t,e)=>B(t,e),get:(t,e)=>{let r=t[e];return typeof r=="function"?function(...n){return r.apply(t,n)}:Reflect.get(t,e)}};async function C(t,e){t=t||a,d.has(t)||d.set(t,new Proxy(new g(t),v));let r=d.get(t);return await E(),await b(r,{...e}),r}async function E(){l||!w||(w--,l=await new y(`${a}DB`,1,(t,e)=>{switch(e){case 0:t.createObjectStore(a,{keyPath:"id"}).createIndex(m,"state",{unique:!1})}}))}async function b(t,e){if(!l)return;(await l.getAll({store:a,index:m,lowerBound:t.stateId,upperBound:t.stateId})).forEach(n=>{let s=n.property;t.set(s,n.value),delete e[s]});for(let n in e)t[n]=e[n]}function B(t,e,r){let n=Reflect.get(t,e),s=!0;if(n===r)return s;if(typeof r>"u"?s=Reflect.deleteProperty(t,e):s=Reflect.set(t,e,r),s){let o=t.stateId+"."+e,i=h.get(o)||{state:t,property:e,valueOld:n};i.value=r,h.set(o,i),f=f||requestAnimationFrame(D)}return s}async function D(){let t=h.values(),e;do{if(e=t.next().value,!e||e.value===e.valueOld)continue;let{state:r,property:n,value:s}=e;if(r.dispatchEvent(e),!l)continue;let o=r.stateId,i=o+"."+n;typeof s>"u"?await l.delete({store:a,key:i}):await l.put({store:a,item:{id:i,state:o,property:n,value:s}}),x.postMessage({state:o,property:n,value:s})}while(e);h.clear(),f=null}x.onmessage=t=>{let{state:e,property:r,value:n}=t.data,s=d.get(e);if(s){let o={state:s,property:r,value:n,valueOld:s[r]};s.set(r,n),s.dispatchEvent(o)}};export{y as PixDB,C as stateZx};
var w=class{#e=null;#r=null;#s=null;constructor(t,e,r){return this.#r=t||"db",this.#s=e||1,this.#i(r)}connect(){return this.#i().then(()=>!0).catch(()=>!1)}close(){this.#e.close(),this.#e=null}#i(t){return new Promise((e,r)=>{if(!("indexedDB"in window)){r(new Error("No indexedDB support"));return}let n=indexedDB.open(this.#r,this.#s);n.onsuccess=()=>{this.#e=n.result,e(this)},n.onerror=s=>{r(new Error(`IndexedDB error ${s.target.errorCode}: ${s.target.message}`,{cause:s}))},t&&(n.onupgradeneeded=s=>{t(n.result,s.oldVersion,s.newVersion)})})}get isConnected(){return!!this.#e}get name(){return this.#r}get version(){return this.#s}#l(t,e,r){return new Promise((n,s)=>{let{transaction:o,store:i}=this.#o(t,null,!0);o.oncomplete=()=>n(),o.onerror=l=>{s(new Error(l.target.error.message,{cause:l}))},e=Array.isArray(e)?e:[e],e.forEach(l=>{r?i.put(l):i.add(l)}),o.commit()})}add({store:t,item:e=[]}={}){return this.#l(t,e,!1)}put({store:t,item:e=[]}={}){return this.#l(t,e,!0)}#t(t,e,r,n){return new Promise((s,o)=>{n=Array.isArray(n)?n:[n];let i=r==="delete"||r==="clear",l=this.#o(t,e,i).store[r](...n);l.onsuccess=()=>s(l.result),l.onerror=()=>o(l.error??!1)})}count({store:t,index:e,lowerBound:r,upperBound:n}={}){return this.#t(t,e,"count",this.#n(r,n))}get({store:t,index:e,key:r}={}){return this.#t(t,e,"get",r)}getAll({store:t,index:e,lowerBound:r,upperBound:n,count:s}={}){return this.#t(t,e,"getAll",[this.#n(r,n),s])}getAllKeys({store:t,index:e,lowerBound:r,upperBound:n,count:s}={}){return this.#t(t,e,"getAllKeys",[this.#n(r,n),s])}delete({store:t,key:e}={}){return this.#t(t,null,"delete",[e])}deleteAll({store:t,index:e,lowerBound:r,upperBound:n}={}){return this.#t(t,e,"delete",[this.#n(r,n)])}clear({store:t}={}){return this.#t(t,null,"clear")}getCursor({store:t,index:e,lowerBound:r,upperBound:n,direction:s="next",callback:o}={}){return new Promise((i,l)=>{let c=this.#o(t,e).store.openCursor(this.#n(r,n),s);c.onsuccess=()=>{let p=c.result;p?p.advance(o&&o(p)||1):i(!0)},c.onerror=()=>l(c.error)})}drop(){return new Promise((t,e)=>{this.close();let r=indexedDB.deleteDatabase(this.#r);r.onsuccess=()=>{this.#r=null,this.#s=null,t(!0)},r.onerror=()=>e(!1)})}#o(t,e,r){let n=this.#e.transaction(t,r?"readwrite":"readonly",{durability:r?"strict":"default"}),s=n.objectStore(t);return{transaction:n,store:e&&!r?s.index(e):s}}#n(t,e){let r;return t&&e?r=IDBKeyRange.bound(t,e):t?r=IDBKeyRange.lowerBound(t):e&&(r=IDBKeyRange.upperBound(e)),r}};var d=new Map,h=new Map,a="statezx",x="stateIdx",m=new BroadcastChannel(a),f,u,y=3,v=window.requestIdleCallback||(t=>setTimeout(t,20)),g=class extends EventTarget{#e=null;constructor(e){super(),this.#e=e}get stateId(){return this.#e}set(e,r){return typeof r>"u"?Reflect.deleteProperty(this,e):Reflect.set(this,e,r)}addEventListener(){super.addEventListener.apply(this,arguments)}removeEventListener(){super.removeEventListener.apply(this,arguments)}dispatchEvent(e){let r={detail:e,bubbles:!1,cancelable:!1};super.dispatchEvent(new CustomEvent(e.property,r)),super.dispatchEvent(new CustomEvent("*",r))}},E={set:(t,e,r)=>B(t,e,r),deleteProperty:(t,e)=>B(t,e),get:(t,e)=>{let r=t[e];return typeof r=="function"?function(...n){return r.apply(t,n)}:Reflect.get(t,e)}};async function R(t,e){t=t||a,d.has(t)||d.set(t,new Proxy(new g(t),E));let r=d.get(t);return await b(),await D(r,{...e}),r}async function b(){u||!y||(y--,u=await new w(`${a}DB`,1,(t,e)=>{switch(e){case 0:t.createObjectStore(a,{keyPath:"id"}).createIndex(x,"state",{unique:!1})}}))}async function D(t,e){if(!u)return;(await u.getAll({store:a,index:x,lowerBound:t.stateId,upperBound:t.stateId})).forEach(n=>{let s=n.property;t.set(s,n.value),delete e[s]});for(let n in e)t[n]=e[n]}function B(t,e,r){let n=Reflect.get(t,e),s=!0;if(n===r)return s;if(typeof r>"u"?s=Reflect.deleteProperty(t,e):s=Reflect.set(t,e,r),s){let o=t.stateId+"."+e,i=h.get(o)||{state:t,property:e,valueOld:n};i.value=r,h.set(o,i),f=f||v(P)}return s}async function P(){let t=h.values(),e;do{if(e=t.next().value,!e||e.value===e.valueOld)continue;let{state:r,property:n,value:s}=e;if(r.dispatchEvent(e),!u)continue;let o=r.stateId,i=o+"."+n;typeof s>"u"?await u.delete({store:a,key:i}):await u.put({store:a,item:{id:i,state:o,property:n,value:s}}),m.postMessage({state:o,property:n,value:s})}while(e);h.clear(),f=null}m.onmessage=t=>{let{state:e,property:r,value:n}=t.data,s=d.get(e);if(s){let o={state:s,property:r,value:n,valueOld:s[r]};s.set(r,n),s.dispatchEvent(o)}};export{w as PixDB,R as stateZx};
{
"name": "statezx",
"version": "1.0.0",
"version": "1.0.1",
"description": "Simple client-side state manager with events, synchronization, and indexedDB storage",

@@ -5,0 +5,0 @@ "type": "module",

+17
-17

@@ -34,3 +34,3 @@ # stateZx

In general, [stateZ](https://github.com/craigbuckler/statez) is a reasonable option for web sites with minimal storage requirements. stateZx may be preferable for more complex web apps.
[stateZ](https://github.com/craigbuckler/statez) is a good option for web sites with minimal storage requirements. stateZx may be preferable for complex web apps storing large amounts of data.

@@ -43,3 +43,3 @@

```js
import { statezx } from 'https://cdn.jsdelivr.net/npm/statezx/dist/statezx.js'
import { stateZx } from 'https://cdn.jsdelivr.net/npm/statezx/dist/statezx.js';
```

@@ -74,3 +74,3 @@

This returns a Promise so `await` is used. Any state object on any page which accesses the same `myState` store has access to the same properties. Values from the database initialize the properties. If `a` does not exist, it uses the value from the initialization object, e.g. `state.a` would be set to `1`.
This returns a Promise so `await` is used. Any state object on any page which accesses the same `myState` store has access to the same properties. Values from the database initialize the properties. If `a` is not defined, the initialization object sets `state.a` to `1`.

@@ -171,3 +171,3 @@ Set and retrieve values:

stateZx uses previously-stored database values by default. Therefore, `state.a` is only set to `1` if it wasn't stored (or it's `1` in the store). Setting a new value stores it in the indexedDB database, triggers events, and synchronizes with other tabs/windows (which trigger their own events).
stateZx uses previously-stored database values by default. Therefore, `state.a` is only set to `1` if it's undefined (or it's `1` in the store). Setting a new value triggers events, stores it in the indexedDB database, and synchronizes with other tabs/windows using stateZx on the same domain (which trigger their own events).

@@ -216,18 +216,18 @@

Values are checked to ensure they've changed before triggering storage, events, and tab/window synchronization. Setting `state.a = 1` only has an effect if it's not already `1`.
Values are checked to ensure they've changed before triggering events, storage, and tab/window synchronization. Setting `state.a = 1` only has an effect when it's not already `1`.
### Setting properties as objects or arrays
### Setting properties to objects or arrays
Setting a property to an object or array will **always** trigger storage, events, and tab/window synchronization. This occurs because objects are passed by reference. Two instances are not the same even when their values are identical:
Setting a property to an object or array will **always** trigger events, storage, and tab/window synchronization. This occurs because objects are passed by reference. Two objects or arrays are not the same even when their values are identical:
```js
console.log( state.myArray ); // [1,2,3]
state.myArray = [1,2,3]; // triggers store, event, sync
state.myArray = [1,2,3]; // triggers event, store, sync
console.log( state.myObject ); // {a:1,b:2}
state.myObject = {a:1,b:2}; // triggers store, event, sync
state.myObject = {a:1,b:2}; // triggers event, store, sync
```
Setting a child property or array element will **not** trigger storage, events, and synchronization:
Setting a child property or array element will **not** trigger events, storage, and synchronization:

@@ -245,3 +245,3 @@ ```js

Sets temporary session-like values in the current tab. It does not trigger storage, events, and synchronization:
Sets temporary session-like values in the current tab. It does not trigger events, storage, and synchronization:

@@ -301,3 +301,3 @@ ```js

A state change will also trigger on other tabs and windows using stateZx on the same domain during synchronization.
A state change also triggers events on other tabs and windows that use stateZx with the same store on the same domain.

@@ -316,3 +316,3 @@ Remove event handlers with the `.removeEventListener()` method:

stateZx records all property changes. A process runs on a later iteration of the JavaScript event loop to make background updates. It executes no more than 60 times per second, although it's unlikely to occur that often. Consider the following code:
stateZx records all property changes. A later iteration of the JavaScript event loop triggers events, updates storage, and synchronizes across tabs/windows when the CPU is idle. Consider the following code:

@@ -328,11 +328,11 @@ ```js

The code will **not** trigger 1,000 storage, event, and synchronization processes. If `state.counter` is initially stored as `0`, the synchronous loop will complete and it's value will become `1000`. The update process runs at some future point which:
The code will **not** trigger 1,000 event, storage, and synchronization processes. If `state.counter` is initially stored as `0`, the synchronous loop will complete and it's value changes to `1000`. The update process runs at some future point which:
1. triggers local events where the `details` object has `.property` set to `'counter'`, `.oldValue` set to `0`, and `.value` set to `1000`
1. updates `counter` in the indexedDB store which changes the value from `0` to `1000`
1. updates `counter` in the indexedDB store to change the value from `0` to `1000`
1. broadcasts a synchronization event to all tabs/windows on the same domain using stateZx which triggers an identical event.
1. broadcasts a synchronization event to all tabs/windows on the same domain using stateZx which triggers identical events.
Intensive state changes do not have a significant impact on performance because stateZx only updates changed properties when the program is idle. Nothing would run if `state.counter = 0;` was added after the loop!
Intensive state changes do not have a significant impact on performance because stateZx makes background updates when the program is idle. Nothing would run if `state.counter = 0;` was added after the loop!

@@ -339,0 +339,0 @@