@quanta-lib/q-state 1.0.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 ADDED
@@ -0,0 +1,167 @@
1
+ # Q-State
2
+
3
+ A lightweight React state management library using `useSyncExternalStore` with optional localStorage caching and value transformation.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @quanta-lib/q-state
9
+
10
+ ## Features
11
+
12
+ - **Type-safe state management** - Full TypeScript support with generic types
13
+ - **useSyncExternalStore** - Uses React's built-in concurrent-safe subscription mechanism
14
+ - **Value transformation** - Transform values during updates with custom transformer functions
15
+ - **LocalStorage caching** - Persist state to localStorage with optional caching
16
+ - **Lightweight** - Minimal dependencies, no context providers needed
17
+
18
+ ## Usage
19
+
20
+ ### Basic Usage
21
+
22
+ ```typescript
23
+ import { Q_StateEngine } from "@q-state/core"
24
+
25
+ const qstate = new Q_StateEngine({
26
+ count: 0,
27
+ name: 'Amarix'
28
+ })
29
+
30
+ export function App() {
31
+ const [count] = qstate.useQuantaState('count')
32
+ const [name] = qstate.useQuantaState('name')
33
+
34
+ return (
35
+ <>
36
+ <h1>Counter App</h1>
37
+ <p>Name: {name}</p>
38
+ <p>Count: {count}</p>
39
+ <button onClick={() => qstate.updateValue('count', (prev) => prev + 1)}>
40
+ Increment
41
+ </button>
42
+ </>
43
+ )
44
+ }
45
+ ```
46
+
47
+ ### With Value Transformer
48
+
49
+ Transform values automatically when updating state:
50
+
51
+ ```typescript
52
+ const qstate = new Q_StateEngine({
53
+ name: 'Amarix'
54
+ }, {
55
+ // Transformer function runs after updateValue
56
+ name: (raw) => {
57
+ console.log(raw) // Logs the new value
58
+ return raw.toUpperCase() // Transform to uppercase
59
+ }
60
+ })
61
+
62
+ // When updating name, the transformer will be applied
63
+ qstate.updateValue('name', () => 'hello') // Stores 'HELLO'
64
+ ```
65
+
66
+ ### With LocalStorage Caching
67
+
68
+ Enable caching to persist state across page reloads:
69
+
70
+ ```typescript
71
+ const qstate = new Q_StateEngine({
72
+ count: 0,
73
+ name: 'Amarix'
74
+ }, {
75
+ name: (raw) => console.log(raw)
76
+ }, {
77
+ cache: true // Enable localStorage caching
78
+ })
79
+ ```
80
+
81
+ ### Nested Updates
82
+
83
+ You can update multiple state values in a single update function:
84
+
85
+ ```typescript
86
+ <button onClick={() => qstate.updateValue('name', () => {
87
+ qstate.updateValue('count', () => 5) // Nested update
88
+ return 'helo :3'
89
+ })}>
90
+ Update Name
91
+ </button>
92
+ ```
93
+
94
+ ## API
95
+
96
+ ### Constructor
97
+
98
+ ```typescript
99
+ new Q_StateEngine<T, O>(state: T, transformer?: O, option?: OptionRecord)
100
+ ```
101
+
102
+ - **state** (T): Initial state object with type safety
103
+ - **transformer** (O, optional): Record of transformer functions for specific keys
104
+ - **option** (OptionRecord, optional): Configuration options
105
+ - `cache`: Enable localStorage caching (default: false)
106
+
107
+ ### Methods
108
+
109
+ #### `updateValue(key, func)`
110
+
111
+ Updates a state value using an updater function.
112
+
113
+ ```typescript
114
+ qstate.updateValue('count', (prev) => prev + 1)
115
+ ```
116
+
117
+ - **key**: The state key to update
118
+ - **func**: Updater function that receives the current value and returns the new value
119
+
120
+ #### `useQuantaState(key)`
121
+
122
+ React hook for subscribing to state changes.
123
+
124
+ ```typescript
125
+ const [value] = qstate.useQuantaState('key')
126
+ ```
127
+
128
+ Returns a tuple with the current value. The component re-renders when the specified key changes.
129
+
130
+ ## TypeScript
131
+
132
+ The library provides full type safety:
133
+
134
+ ```typescript
135
+ interface AppState {
136
+ count: number
137
+ name: string
138
+ items: string[]
139
+ }
140
+
141
+ const qstate = new Q_StateEngine<AppState>({
142
+ count: 0,
143
+ name: '',
144
+ items: []
145
+ })
146
+
147
+ // Type-safe updates
148
+ qstate.updateValue('count', (prev) => prev + 1) // ✓
149
+ qstate.updateValue('name', () => 'Hello') // ✓
150
+ qstate.updateValue('items', (prev) => [...prev, 'new']) // ✓
151
+ ```
152
+
153
+ ## How It Works
154
+
155
+ 1. **State Storage**: State is stored in a private object within the Q_StateEngine instance
156
+ 2. **Subscription**: Uses React's `useSyncExternalStore` for concurrent-safe subscriptions
157
+ 3. **Updates**: When `updateValue` is called, it:
158
+ - Executes the updater function with the current value
159
+ - Runs the transformer function (if defined)
160
+ - Updates the internal state
161
+ - Persists to localStorage (if caching enabled)
162
+ - Notifies all subscribers
163
+ 4. **Caching**: On initial render, checks localStorage for cached values and hydrates state
164
+
165
+ ## License
166
+
167
+ MIT
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Q_StateEngine = void 0;
4
+ var react_1 = require("react");
5
+ var Q_StateEngine = /** @class */ (function () {
6
+ function Q_StateEngine(state, transformer, option) {
7
+ var _this = this;
8
+ this.subsribe = function (cb) {
9
+ _this.listener.add(cb);
10
+ return function () { return _this.listener.delete(cb); };
11
+ };
12
+ this.updateValue = function (key, func) {
13
+ var _a;
14
+ var executeFunc = func(_this.obj[key]);
15
+ var transformData = (_this.transformer && _this.transformer[key])
16
+ ? _this.transformer[key](executeFunc)
17
+ : executeFunc;
18
+ // Kita ambil saja data hasil update jika data dari layer transformer nya undefined
19
+ var finalData = transformData !== null && transformData !== void 0 ? transformData : executeFunc;
20
+ _this.obj[key] = finalData;
21
+ if ((_a = _this.option) === null || _a === void 0 ? void 0 : _a.cache)
22
+ localStorage.setItem(key, JSON.stringify(_this.obj[key]));
23
+ _this.listener.forEach(function (cb) { return cb(); });
24
+ };
25
+ this.useQuantaState = function (key) {
26
+ var getCurr = function () {
27
+ var _a;
28
+ if (((_a = _this.option) === null || _a === void 0 ? void 0 : _a.cache) && typeof window !== "undefined") {
29
+ var getcache = localStorage.getItem(key);
30
+ if (getcache !== null && getcache !== 'undefined') {
31
+ var dataFromCache = '';
32
+ try {
33
+ dataFromCache = JSON.parse(getcache);
34
+ }
35
+ catch (_b) {
36
+ dataFromCache = _this.obj[key];
37
+ }
38
+ if (dataFromCache !== _this.obj[key]) {
39
+ _this.obj[key] = dataFromCache;
40
+ }
41
+ }
42
+ }
43
+ return _this.obj[key];
44
+ };
45
+ var syncSpecificData = (0, react_1.useSyncExternalStore)(_this.subsribe, getCurr);
46
+ return [syncSpecificData];
47
+ };
48
+ this.obj = state;
49
+ this.transformer = transformer;
50
+ this.listener = new Set();
51
+ this.option = option;
52
+ }
53
+ return Q_StateEngine;
54
+ }());
55
+ exports.Q_StateEngine = Q_StateEngine;
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@quanta-lib/q-state",
3
+ "version": "1.0.1",
4
+ "description": "A lightweight React state management library using `useSyncExternalStore` with optional localStorage caching and value transformation.",
5
+ "main": "dist/Q-State-Engine.js",
6
+ "types": "src/Q-State-Engine.ts",
7
+ "files": [
8
+ "dist",
9
+ "src"
10
+ ],
11
+ "scripts": {
12
+ "test": "echo \"Error: no test specified\" && exit 1"
13
+ },
14
+ "keywords": [
15
+ "state management",
16
+ "global state",
17
+ "quanta",
18
+ "state"
19
+ ],
20
+ "author": "quanta-lib",
21
+ "license": "ISC",
22
+ "publishConfig": {
23
+ "access": "public"
24
+ }
25
+ }
@@ -0,0 +1,64 @@
1
+ import { useSyncExternalStore } from "react"
2
+
3
+ interface OptionRecord {
4
+ cache: boolean
5
+ }
6
+
7
+ export class Q_StateEngine
8
+ <T extends Record<string, any>,
9
+ O extends Partial<Record<keyof T, (raw: any) => any>>>
10
+ {
11
+ private listener: Set<() => void>
12
+ private obj: T // Type-safe Map sesuai skema T
13
+ private transformer: O
14
+ private option?: OptionRecord
15
+
16
+ constructor(state: T, transformer?: O, option?: OptionRecord) {
17
+ this.obj = state
18
+ this.transformer = transformer as any
19
+ this.listener = new Set()
20
+ this.option = option
21
+ }
22
+
23
+ private subsribe = (cb: any) => {
24
+ this.listener.add(cb)
25
+ return () => this.listener.delete(cb)
26
+ }
27
+
28
+ updateValue = <K extends keyof T>(key: K, func: (prev: T[K]) => T[K]) => {
29
+ const executeFunc = func(this.obj[key])
30
+ const transformData = (this.transformer && this.transformer[key])
31
+ ? this.transformer[key](executeFunc)
32
+ : executeFunc
33
+ // Kita ambil saja data hasil update jika data dari layer transformer nya undefined
34
+ const finalData = transformData ?? executeFunc
35
+ this.obj[key] = finalData
36
+ if(this.option?.cache) localStorage.setItem(key as string, JSON.stringify(this.obj[key]))
37
+ this.listener.forEach(cb => cb())
38
+ }
39
+
40
+ useQuantaState = <K extends keyof T>(key: K): [T[K]] => {
41
+ const getCurr = () => {
42
+ if (this.option?.cache && typeof window !== "undefined") {
43
+ const getcache = localStorage.getItem(key as string)
44
+ if (getcache !== null && getcache !== 'undefined') {
45
+ let dataFromCache = ''
46
+ try {
47
+ dataFromCache = JSON.parse(getcache)
48
+ } catch {
49
+ dataFromCache = this.obj[key]
50
+ }
51
+ if (dataFromCache !== this.obj[key]) {
52
+ this.obj[key] = dataFromCache as any
53
+ }
54
+ }
55
+ }
56
+ return this.obj[key]
57
+ }
58
+ const syncSpecificData = useSyncExternalStore(
59
+ this.subsribe,
60
+ getCurr
61
+ )
62
+ return [syncSpecificData]
63
+ }
64
+ }