@lavaz/store 1.0.2
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 +94 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +30 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +1 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# @lavaz/store 🚀
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
A lightweight, high-level state management infrastructure for React and JavaScript applications. Designed for complex workspaces, not just simple kanbans.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@lavaz/store)
|
|
8
|
+
[](https://bundlephobia.com/package/@lavaz/store)
|
|
9
|
+
|
|
10
|
+
## ✨ Features
|
|
11
|
+
|
|
12
|
+
- **Ultra Lightweight**: Less than 3kB gzipped.
|
|
13
|
+
- **Type-safe**: Built with TypeScript for excellent developer experience.
|
|
14
|
+
- **High Performance**: Optimized with `useSyncExternalStore` and shallow comparison to prevent unnecessary re-renders.
|
|
15
|
+
- **Zero Configuration**: Easy to set up and integrate into any project.
|
|
16
|
+
|
|
17
|
+
## 📦 Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm add @lavaz/store
|
|
21
|
+
# or
|
|
22
|
+
npm install @lavaz/store
|
|
23
|
+
# or
|
|
24
|
+
yarn add @lavaz/store
|
|
25
|
+
# or
|
|
26
|
+
bun add @lavaz/store@beta
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
# 🚀 Quick Start
|
|
30
|
+
|
|
31
|
+
## 1. Create a Box
|
|
32
|
+
A "Box" is where your state and actions live.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
import { createBox } from "@lavaz/store";
|
|
36
|
+
|
|
37
|
+
export const counterBox = createBox({
|
|
38
|
+
state: { count: 0 },
|
|
39
|
+
actions: (set) => ({
|
|
40
|
+
inc: () => set((s) => ({ count: s.count + 1 })),
|
|
41
|
+
dec: () => set((s) => ({ count: s.count - 1 })),
|
|
42
|
+
}),
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 2. Create a store
|
|
47
|
+
A "Store" is the central hub that manages multiple boxes. It provides a unified interface to access your state and actions across the entire workspace.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
import { createStore } from "@lavaz/store";
|
|
51
|
+
import { authBox } from "./boxes/authBox";
|
|
52
|
+
import { projectBox } from "./boxes/projectBox";
|
|
53
|
+
|
|
54
|
+
// Initialize your global workspace store
|
|
55
|
+
export const store = createStore({
|
|
56
|
+
auth: authBox,
|
|
57
|
+
projects: projectBox,
|
|
58
|
+
// You can add more boxes as your workspace grows
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 3. Use in React
|
|
63
|
+
Use the `useAppStore` hook with a selector for optimal performance.
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
import { useAppStore } from "@lavaz/store";
|
|
67
|
+
import { counterBox } from "./counterBox";
|
|
68
|
+
|
|
69
|
+
const Counter = () => {
|
|
70
|
+
// Only re-renders if 'count' changes
|
|
71
|
+
const [count, { inc, dec }] = useAppStore(counterBox, (s) => s.count);
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<div>
|
|
75
|
+
<h1>{count}</h1>
|
|
76
|
+
<button onClick={inc}>+</button>
|
|
77
|
+
<button onClick={dec}>-</button>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## 🛠Advanced: Shallow Comparison
|
|
84
|
+
Lavaz automatically performs a shallow check on the data returned by your selector.
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
const [user] = useAppStore(userBox, (s) => ({
|
|
88
|
+
name: s.name,
|
|
89
|
+
age: s.age
|
|
90
|
+
}));
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
# 📄 License
|
|
94
|
+
MIT © [Nguyen Huy Long/Lavaz Ecosystem]
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var S=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),d= exports.validateState =(e,r,o,s=new WeakSet)=>{if(e===void 0)throw new Error(`[Lavaz]: ${r} cannot be undefined.`);if(typeof e=="function")throw new Error(`[Lavaz]: ${r} cannot be a function.`);if(S(e)){if(s.has(e))return;s.add(e);let t=Object.keys(e);if(o&&t.length===0)throw new Error("[Lavaz]: `initialState` cannot be an empty object.");for(let i of t)d(e,`${r}.${i}`,!1,s)}Array.isArray(e)&&e.forEach((t,i)=>{d(t,`${r}[${i}]`,!1,s)})};var v=(e,r)=>{let o=typeof process<"u"&&process.env.NODE_ENV==="production"||typeof import.meta<"u"&&import.meta.env.NODE_ENV==="production",s=typeof process<"u"&&(process.env.NODE_ENV==="test"||!!process.env.VITEST),t=null,i={persist:(n,c="local")=>(t={name:n,storage:c},i),create:()=>{(!o||s)&&d(e,"initialState",!0);let n=e,c=new Set,f=t?`lavaz-${t.name}`:null,p=_optionalChain([t, 'optionalAccess', _ => _.storage])==="session"?window.sessionStorage:window.localStorage;if(typeof window<"u"&&f)try{let a=p.getItem(f);a&&(n=JSON.parse(a))}catch (e2){o||console.warn(`[Lavaz]: Hydration failed for ${f}`)}let l=r(a=>{let u=typeof a=="function"?a(n):a;if(u===void 0)throw new Error("[Lavaz]: Cannot update `state` to `undefined`.");Object.is(n,u)||(n=u,f&&p.setItem(f,JSON.stringify(n)),c.forEach(m=>m(n)))});return{getState:()=>n,dispatch:l,subscribe:a=>(c.add(a),()=>c.delete(a)),boxId:_optionalChain([t, 'optionalAccess', _2 => _2.name])||`box-${Math.random().toString(36).substring(2,9)}`}}};return i};var $=e=>{let r=typeof process<"u"&&process.env.NODE_ENV==="production"||typeof import.meta<"u"&&import.meta.env.NODE_ENV==="production",o=typeof process<"u"&&(process.env.NODE_ENV==="test"||!!process.env.VITEST);if(!r||o){if(Object.keys(e).length===0)throw new Error("[Lavaz]: `store` cannot be an empty object.");let t=new Map;for(let[i,n]of Object.entries(e)){let c=t.get(n.boxId);if(c)throw new Error(`[Lavaz]: Conflict. The box you are trying to assign to "${i}" is already used by "${c}`);t.set(n.boxId,i)}}return e};var _react = require('react');var y=(e,r)=>{if(Object.is(e,r))return!0;if(typeof e!="object"||e===null||typeof r!="object"||r===null)return!1;let o=Object.keys(e),s=Object.keys(r);if(o.length!==s.length)return!1;for(let t=0;t<=o.length;t++)if(!Object.prototype.hasOwnProperty.call(r,o[t])||Object.is(e[o[t]],r[o[t]]))return!1;return!0};var z=(e,r=o=>o)=>{let o=_react.useRef.call(void 0, void 0),s=_react.useCallback.call(void 0, ()=>{let n=r(e.getState());return o.current===void 0||!y(o,n)?(o.current=n,n):o.current},[r,e]),t=_react.useSyncExternalStore.call(void 0, e.subscribe,s,s),i=_react.useMemo.call(void 0, ()=>e.dispatch,[e.dispatch]);return[t,i]};exports.createBox = v; exports.createStore = $; exports.shallowCompare = y; exports.useAppStore = z; exports.validateState = d;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
type PrevFn<S> = (prev: S) => S;
|
|
2
|
+
type Next<S> = S | PrevFn<S>;
|
|
3
|
+
type Subscribe<S> = (cb: (state: S) => void) => () => void;
|
|
4
|
+
interface CreateBox<S = unknown, A = unknown> {
|
|
5
|
+
getState: () => S;
|
|
6
|
+
dispatch: A;
|
|
7
|
+
subscribe: Subscribe<S>;
|
|
8
|
+
boxId: string;
|
|
9
|
+
}
|
|
10
|
+
interface BoxBuilder<S, A> {
|
|
11
|
+
persist: (name: string, storage?: "local" | "session") => BoxBuilder<S, A>;
|
|
12
|
+
create: () => CreateBox<S, A>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
declare const createBox: <S, A>(initialState: S, actions: (set: (next: Next<S>) => void) => A) => BoxBuilder<S, A>;
|
|
16
|
+
|
|
17
|
+
declare const createStore: <T extends Record<string, CreateBox>>(boxes: T) => T;
|
|
18
|
+
|
|
19
|
+
declare const useAppStore: <S, A, R>(box: CreateBox<S, A>, selector?: (state: S) => R) => [R, A];
|
|
20
|
+
|
|
21
|
+
declare const validateState: (val: any, path: string, isRoot: boolean, seen?: WeakSet<WeakKey>) => void;
|
|
22
|
+
|
|
23
|
+
declare const shallowCompare: (prevObj: any, newObj: any) => boolean;
|
|
24
|
+
|
|
25
|
+
interface Persistance {
|
|
26
|
+
name: string;
|
|
27
|
+
storage?: "local" | "session";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { type BoxBuilder, type CreateBox, type Next, type Persistance, type PrevFn, type Subscribe, createBox, createStore, shallowCompare, useAppStore, validateState };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
type PrevFn<S> = (prev: S) => S;
|
|
2
|
+
type Next<S> = S | PrevFn<S>;
|
|
3
|
+
type Subscribe<S> = (cb: (state: S) => void) => () => void;
|
|
4
|
+
interface CreateBox<S = unknown, A = unknown> {
|
|
5
|
+
getState: () => S;
|
|
6
|
+
dispatch: A;
|
|
7
|
+
subscribe: Subscribe<S>;
|
|
8
|
+
boxId: string;
|
|
9
|
+
}
|
|
10
|
+
interface BoxBuilder<S, A> {
|
|
11
|
+
persist: (name: string, storage?: "local" | "session") => BoxBuilder<S, A>;
|
|
12
|
+
create: () => CreateBox<S, A>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
declare const createBox: <S, A>(initialState: S, actions: (set: (next: Next<S>) => void) => A) => BoxBuilder<S, A>;
|
|
16
|
+
|
|
17
|
+
declare const createStore: <T extends Record<string, CreateBox>>(boxes: T) => T;
|
|
18
|
+
|
|
19
|
+
declare const useAppStore: <S, A, R>(box: CreateBox<S, A>, selector?: (state: S) => R) => [R, A];
|
|
20
|
+
|
|
21
|
+
declare const validateState: (val: any, path: string, isRoot: boolean, seen?: WeakSet<WeakKey>) => void;
|
|
22
|
+
|
|
23
|
+
declare const shallowCompare: (prevObj: any, newObj: any) => boolean;
|
|
24
|
+
|
|
25
|
+
interface Persistance {
|
|
26
|
+
name: string;
|
|
27
|
+
storage?: "local" | "session";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { type BoxBuilder, type CreateBox, type Next, type Persistance, type PrevFn, type Subscribe, createBox, createStore, shallowCompare, useAppStore, validateState };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var S=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),d=(e,r,o,s=new WeakSet)=>{if(e===void 0)throw new Error(`[Lavaz]: ${r} cannot be undefined.`);if(typeof e=="function")throw new Error(`[Lavaz]: ${r} cannot be a function.`);if(S(e)){if(s.has(e))return;s.add(e);let t=Object.keys(e);if(o&&t.length===0)throw new Error("[Lavaz]: `initialState` cannot be an empty object.");for(let i of t)d(e,`${r}.${i}`,!1,s)}Array.isArray(e)&&e.forEach((t,i)=>{d(t,`${r}[${i}]`,!1,s)})};var v=(e,r)=>{let o=typeof process<"u"&&process.env.NODE_ENV==="production"||typeof import.meta<"u"&&import.meta.env.NODE_ENV==="production",s=typeof process<"u"&&(process.env.NODE_ENV==="test"||!!process.env.VITEST),t=null,i={persist:(n,c="local")=>(t={name:n,storage:c},i),create:()=>{(!o||s)&&d(e,"initialState",!0);let n=e,c=new Set,f=t?`lavaz-${t.name}`:null,p=t?.storage==="session"?window.sessionStorage:window.localStorage;if(typeof window<"u"&&f)try{let a=p.getItem(f);a&&(n=JSON.parse(a))}catch{o||console.warn(`[Lavaz]: Hydration failed for ${f}`)}let l=r(a=>{let u=typeof a=="function"?a(n):a;if(u===void 0)throw new Error("[Lavaz]: Cannot update `state` to `undefined`.");Object.is(n,u)||(n=u,f&&p.setItem(f,JSON.stringify(n)),c.forEach(m=>m(n)))});return{getState:()=>n,dispatch:l,subscribe:a=>(c.add(a),()=>c.delete(a)),boxId:t?.name||`box-${Math.random().toString(36).substring(2,9)}`}}};return i};var $=e=>{let r=typeof process<"u"&&process.env.NODE_ENV==="production"||typeof import.meta<"u"&&import.meta.env.NODE_ENV==="production",o=typeof process<"u"&&(process.env.NODE_ENV==="test"||!!process.env.VITEST);if(!r||o){if(Object.keys(e).length===0)throw new Error("[Lavaz]: `store` cannot be an empty object.");let t=new Map;for(let[i,n]of Object.entries(e)){let c=t.get(n.boxId);if(c)throw new Error(`[Lavaz]: Conflict. The box you are trying to assign to "${i}" is already used by "${c}`);t.set(n.boxId,i)}}return e};import{useCallback as g,useMemo as x,useRef as h,useSyncExternalStore as w}from"react";var y=(e,r)=>{if(Object.is(e,r))return!0;if(typeof e!="object"||e===null||typeof r!="object"||r===null)return!1;let o=Object.keys(e),s=Object.keys(r);if(o.length!==s.length)return!1;for(let t=0;t<=o.length;t++)if(!Object.prototype.hasOwnProperty.call(r,o[t])||Object.is(e[o[t]],r[o[t]]))return!1;return!0};var z=(e,r=o=>o)=>{let o=h(void 0),s=g(()=>{let n=r(e.getState());return o.current===void 0||!y(o,n)?(o.current=n,n):o.current},[r,e]),t=w(e.subscribe,s,s),i=x(()=>e.dispatch,[e.dispatch]);return[t,i]};export{v as createBox,$ as createStore,y as shallowCompare,z as useAppStore,d as validateState};
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lavaz/store",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"sideEffects": false,
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"clean": "rimraf dist",
|
|
15
|
+
"test": "vitest",
|
|
16
|
+
"dev": "tsup --watch",
|
|
17
|
+
"build": "npm run clean && tsup src/index.ts --format esm,cjs --dts --clean --external react --minify --splitting",
|
|
18
|
+
"release:patch": "npm run build && npm version patch && npm publish --access public --tag beta"
|
|
19
|
+
},
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.js",
|
|
24
|
+
"require": "./dist/index.cjs"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"react": ">=18.0.0",
|
|
29
|
+
"react-dom": ">=18.0.0"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
36
|
+
"keywords": [],
|
|
37
|
+
"author": "Nguyen Huy Long",
|
|
38
|
+
"license": "ISC",
|
|
39
|
+
"packageManager": "pnpm@10.32.1",
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
42
|
+
"@testing-library/react": "^16.3.2",
|
|
43
|
+
"@types/node": "^25.5.0",
|
|
44
|
+
"@types/react": "^19.2.14",
|
|
45
|
+
"@types/react-dom": "^19.2.3",
|
|
46
|
+
"@vitejs/plugin-react": "^6.0.1",
|
|
47
|
+
"jsdom": "^29.0.0",
|
|
48
|
+
"react": "^19.2.4",
|
|
49
|
+
"react-dom": "^19.2.4",
|
|
50
|
+
"rimraf": "^6.1.3",
|
|
51
|
+
"rslib": "^0.0.10",
|
|
52
|
+
"tsc-alias": "^1.8.16",
|
|
53
|
+
"tsup": "^8.5.1",
|
|
54
|
+
"typescript": "^5.9.3",
|
|
55
|
+
"vitest": "^4.1.0"
|
|
56
|
+
}
|
|
57
|
+
}
|