kvozy 0.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,360 @@
1
+ # Kvozy - React localStorage Binding Library
2
+
3
+ Simple, minimal React library for binding localStorage keys to React state.
4
+
5
+ ## Overview
6
+
7
+ Kvozy separates storage logic from React integration:
8
+
9
+ - **bindValue** - Framework-agnostic core with all localStorage logic
10
+ - **useStorage** - Thin React hook wrapper
11
+
12
+ This architecture makes it easy to add connectors for other frameworks (Vue, Svelte, Angular, etc.) in the future.
13
+
14
+ ## Features
15
+
16
+ - Framework-agnostic core (bindValue)
17
+ - Thin React integration (useStorage)
18
+ - String-only values (simple, predictable)
19
+ - Real localStorage backend
20
+ - Subscription-based reactivity
21
+ - TypeScript support
22
+ - Easy to extend to other frameworks
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ npm install kvozy
28
+ ```
29
+
30
+ ## Quick Start
31
+
32
+ ```typescript
33
+ import { bindValue, useStorage } from 'kvozy';
34
+
35
+ // Define the binding
36
+ const myValue = bindValue({ key: 'my-key' });
37
+
38
+ // Use in component
39
+ const Component = () => {
40
+ const { value, setValue } = useStorage(myValue);
41
+
42
+ return <input value={value || ''} onChange={(e) => setValue(e.target.value)} />;
43
+ };
44
+ ```
45
+
46
+ ## API Reference
47
+
48
+ ### bindValue(options)
49
+
50
+ Creates a storage binding to localStorage.
51
+
52
+ **Parameters:**
53
+
54
+ - `options` (object, required)
55
+ - `key` (string, required) - localStorage key
56
+
57
+ **Returns:** `BindValue` instance
58
+
59
+ **Methods:**
60
+
61
+ - `getValue()` - returns `string | undefined`
62
+ - `set(value: string)` - updates localStorage and notifies subscribers
63
+ - `subscribe(callback)` - subscribe to value changes, returns unsubscribe function
64
+
65
+ **Behavior:**
66
+
67
+ - `subscribe()` does NOT call the callback immediately when subscribing
68
+ - Callbacks are only invoked when value changes via `set()`
69
+
70
+ **Example:**
71
+
72
+ ```typescript
73
+ const binding = bindValue({ key: "user-name" });
74
+
75
+ // Get current value
76
+ const currentValue = binding.getValue();
77
+
78
+ // Set new value
79
+ binding.set("Alice");
80
+
81
+ // Subscribe to changes
82
+ const unsubscribe = binding.subscribe((newValue) => {
83
+ console.log("Value changed:", newValue);
84
+ });
85
+
86
+ // Unsubscribe when done
87
+ unsubscribe();
88
+ ```
89
+
90
+ ### useStorage(binding)
91
+
92
+ React hook that connects a BindValue instance to React state.
93
+
94
+ **Parameters:**
95
+
96
+ - `binding` (BindValue, required) - binding instance from bindValue
97
+
98
+ **Returns:** `{ value, setValue }`
99
+
100
+ - `value` - `string | undefined` - current value from localStorage
101
+ - `setValue` - `(value: string) => void` - function to update value
102
+
103
+ **Behavior:**
104
+
105
+ - `subscribe()` does NOT call the callback immediately when subscribing
106
+ - Callbacks are only invoked when the value changes via `set()`
107
+
108
+ **Example:**
109
+
110
+ ```typescript
111
+ const Component = () => {
112
+ const { value, setValue } = useStorage(myBinding);
113
+
114
+ return <div>
115
+ <p>Current value: {value || '(empty)'}</p>
116
+ <button onClick={() => setValue('new value')}>
117
+ Update Value
118
+ </button>
119
+ </div>;
120
+ };
121
+ ```
122
+
123
+ ## Usage Examples
124
+
125
+ ### Basic Usage
126
+
127
+ ```typescript
128
+ import { bindValue, useStorage } from 'kvozy';
129
+
130
+ const usernameBinding = bindValue({ key: 'username' });
131
+
132
+ const LoginForm = () => {
133
+ const { value, setValue } = useStorage(usernameBinding);
134
+
135
+ return (
136
+ <div>
137
+ <label>
138
+ Username:
139
+ <input
140
+ value={value || ''}
141
+ onChange={(e) => setValue(e.target.value)}
142
+ />
143
+ </label>
144
+ </div>
145
+ );
146
+ };
147
+ ```
148
+
149
+ ### Multiple Components Sharing State
150
+
151
+ ```typescript
152
+ import { bindValue, useStorage } from 'kvozy';
153
+
154
+ const themeBinding = bindValue({ key: 'theme' });
155
+
156
+ const ThemeToggle = () => {
157
+ const { value, setValue } = useStorage(themeBinding);
158
+
159
+ return (
160
+ <button onClick={() => setValue(value === 'dark' ? 'light' : 'dark')}>
161
+ Switch to {value === 'dark' ? 'Light' : 'Dark'} Mode
162
+ </button>
163
+ );
164
+ };
165
+
166
+ const ThemeDisplay = () => {
167
+ const { value } = useStorage(themeBinding);
168
+
169
+ return <p>Current theme: {value}</p>;
170
+ };
171
+
172
+ const App = () => (
173
+ <div>
174
+ <ThemeToggle />
175
+ <ThemeDisplay />
176
+ </div>
177
+ );
178
+ ```
179
+
180
+ Both components stay in sync automatically!
181
+
182
+ ### Handling Undefined Values
183
+
184
+ When a localStorage key doesn't exist, `getValue()` and `useStorage` return `undefined`:
185
+
186
+ ```typescript
187
+ const binding = bindValue({ key: 'non-existent-key' });
188
+ console.log(binding.getValue()); // undefined
189
+
190
+ const Component = () => {
191
+ const { value } = useStorage(binding);
192
+ return <div>{value || 'No value set'}</div>;
193
+ };
194
+ ```
195
+
196
+ ### Persisting Form Data
197
+
198
+ ```typescript
199
+ const formBinding = bindValue({ key: 'form-data' });
200
+
201
+ const Form = () => {
202
+ const { value, setValue } = useStorage(formBinding);
203
+
204
+ const handleSubmit = (e: React.FormEvent) => {
205
+ e.preventDefault();
206
+ const formData = new FormData(e.currentTarget);
207
+ const data = Object.fromEntries(formData);
208
+ setValue(JSON.stringify(data));
209
+ };
210
+
211
+ const formData = value ? JSON.parse(value) : {};
212
+
213
+ return (
214
+ <form onSubmit={handleSubmit}>
215
+ <input name="name" defaultValue={formData.name || ''} />
216
+ <input name="email" defaultValue={formData.email || ''} />
217
+ <button type="submit">Save</button>
218
+ </form>
219
+ );
220
+ };
221
+ ```
222
+
223
+ ### Counter Example
224
+
225
+ ```typescript
226
+ const counterBinding = bindValue({ key: 'counter' });
227
+
228
+ const Counter = () => {
229
+ const { value, setValue } = useStorage(counterBinding);
230
+ const count = parseInt(value || '0', 10);
231
+
232
+ return (
233
+ <div>
234
+ <p>Count: {count}</p>
235
+ <button onClick={() => setValue(String(count + 1))}>
236
+ Increment
237
+ </button>
238
+ <button onClick={() => setValue(String(count - 1))}>
239
+ Decrement
240
+ </button>
241
+ <button onClick={() => setValue('0')}>
242
+ Reset
243
+ </button>
244
+ </div>
245
+ );
246
+ };
247
+ ```
248
+
249
+ ## Architecture
250
+
251
+ ### Core: bindValue
252
+
253
+ All storage logic lives in `bindValue` class:
254
+
255
+ - Framework-agnostic
256
+ - Manages localStorage operations
257
+ - Handles subscriptions
258
+ - Can be used with any UI framework
259
+
260
+ ```typescript
261
+ class BindValue {
262
+ private value: string | undefined;
263
+ private subscribers: Set<(value: string | undefined) => void>;
264
+
265
+ getValue(): string | undefined;
266
+ set(value: string): void;
267
+ subscribe(callback): () => void;
268
+ }
269
+ ```
270
+
271
+ ### React: useStorage
272
+
273
+ Thin wrapper that connects `bindValue` to React:
274
+
275
+ - Subscribes to changes on mount
276
+ - Unsubscribes on unmount
277
+ - Manages React state with `useState`
278
+ - Returns `{ value, setValue }`
279
+
280
+ ```typescript
281
+ function useStorage(binding: BindValue): UseStorageReturn {
282
+ const [value, setValue] = useState(binding.getValue());
283
+
284
+ useEffect(() => {
285
+ const unsubscribe = binding.subscribe(setValue);
286
+ return unsubscribe;
287
+ }, [binding]);
288
+
289
+ const set = (newValue: string) => binding.set(newValue);
290
+
291
+ return { value, setValue: set };
292
+ }
293
+ ```
294
+
295
+ ## Testing
296
+
297
+ ### Unit Tests (Node)
298
+
299
+ Run unit tests for `bindValue`:
300
+
301
+ ```bash
302
+ npm test
303
+ ```
304
+
305
+ Tests are located in `unitTests/bindValue.test.ts` and use mocked localStorage.
306
+
307
+ ### Browser Tests (Playwright)
308
+
309
+ Run browser tests for React integration:
310
+
311
+ ```bash
312
+ npm run test:browser
313
+ ```
314
+
315
+ Tests are located in `browserTests/useStorage.test.ts` and run on Chrome, Firefox, and Webkit.
316
+
317
+ ## Browser Support
318
+
319
+ - Chrome >= 87
320
+ - Firefox >= 78
321
+ - Safari >= 15.4
322
+ - Edge >= 88
323
+
324
+ ## TypeScript Support
325
+
326
+ Kvozy is written in TypeScript and provides full type definitions:
327
+
328
+ ```typescript
329
+ import {
330
+ bindValue,
331
+ useStorage,
332
+ type BindValue,
333
+ type UseStorageReturn,
334
+ } from "kvozy";
335
+
336
+ const binding: BindValue = bindValue({ key: "test" });
337
+ const { value, setValue }: UseStorageReturn = useStorage(binding);
338
+ ```
339
+
340
+ ## Limitations
341
+
342
+ - **String-only values**: Kvozy only supports string values. For objects or arrays, serialize them as JSON.
343
+ - **No SSR support**: Currently designed for client-side only (requires `window.localStorage`).
344
+ - **No cross-tab sync**: Changes in one tab don't update other tabs automatically.
345
+
346
+ ## Future Plans
347
+
348
+ - [ ] Vue connector (`useStorageVue`)
349
+ - [ ] Svelte connector (`useStorageSvelte`)
350
+ - [ ] Angular connector
351
+ - [ ] SSR support
352
+ - [ ] Cross-tab synchronization
353
+
354
+ ## License
355
+
356
+ ISC
357
+
358
+ ## Contributing
359
+
360
+ Contributions are welcome! Please read `src/AGENTS.md` for development guidelines.
@@ -0,0 +1,16 @@
1
+ export interface BindValueOptions {
2
+ key: string;
3
+ }
4
+ export declare class BindValue {
5
+ private options;
6
+ private value;
7
+ private subscribers;
8
+ constructor(options: BindValueOptions);
9
+ getValue(): string | undefined;
10
+ set(newValue: string): void;
11
+ subscribe(callback: (value: string | undefined) => void): () => void;
12
+ private loadFromStorage;
13
+ private saveToStorage;
14
+ private notifySubscribers;
15
+ }
16
+ export declare function bindValue(options: BindValueOptions): BindValue;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
+ class BindValue {
7
+ constructor(options) {
8
+ __publicField(this, "value");
9
+ __publicField(this, "subscribers");
10
+ this.options = options;
11
+ this.subscribers = /* @__PURE__ */ new Set();
12
+ this.value = this.loadFromStorage();
13
+ }
14
+ getValue() {
15
+ return this.value;
16
+ }
17
+ set(newValue) {
18
+ this.value = newValue;
19
+ this.saveToStorage(newValue);
20
+ this.notifySubscribers();
21
+ }
22
+ subscribe(callback) {
23
+ this.subscribers.add(callback);
24
+ return () => {
25
+ this.subscribers.delete(callback);
26
+ };
27
+ }
28
+ loadFromStorage() {
29
+ const value = localStorage.getItem(this.options.key);
30
+ return value !== null ? value : void 0;
31
+ }
32
+ saveToStorage(value) {
33
+ localStorage.setItem(this.options.key, value);
34
+ }
35
+ notifySubscribers() {
36
+ for (const subscriber of this.subscribers) {
37
+ subscriber(this.value);
38
+ }
39
+ }
40
+ }
41
+ function bindValue(options) {
42
+ return new BindValue(options);
43
+ }
44
+ exports.BindValue = BindValue;
45
+ exports.bindValue = bindValue;
46
+ //# sourceMappingURL=bindValue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bindValue.js","sources":["../../../../src/bindValue.ts"],"sourcesContent":["export interface BindValueOptions {\n key: string;\n}\n\nexport class BindValue {\n private value: string | undefined;\n private subscribers: Set<(value: string | undefined) => void>;\n\n constructor(private options: BindValueOptions) {\n this.subscribers = new Set();\n this.value = this.loadFromStorage();\n }\n\n getValue(): string | undefined {\n return this.value;\n }\n\n set(newValue: string): void {\n this.value = newValue;\n this.saveToStorage(newValue);\n this.notifySubscribers();\n }\n\n subscribe(callback: (value: string | undefined) => void): () => void {\n this.subscribers.add(callback);\n return () => {\n this.subscribers.delete(callback);\n };\n }\n\n private loadFromStorage(): string | undefined {\n const value = localStorage.getItem(this.options.key);\n return value !== null ? value : undefined;\n }\n\n private saveToStorage(value: string): void {\n localStorage.setItem(this.options.key, value);\n }\n\n private notifySubscribers(): void {\n for (const subscriber of this.subscribers) {\n subscriber(this.value);\n }\n }\n}\n\nexport function bindValue(options: BindValueOptions): BindValue {\n return new BindValue(options);\n}\n"],"names":[],"mappings":";;;;;AAIO,MAAM,UAAU;AAAA,EAIrB,YAAoB,SAA2B;AAHvC;AACA;AAEY,SAAA,UAAA;AAClB,SAAK,kCAAkB,IAAA;AACvB,SAAK,QAAQ,KAAK,gBAAA;AAAA,EACpB;AAAA,EAEA,WAA+B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,UAAwB;AAC1B,SAAK,QAAQ;AACb,SAAK,cAAc,QAAQ;AAC3B,SAAK,kBAAA;AAAA,EACP;AAAA,EAEA,UAAU,UAA2D;AACnE,SAAK,YAAY,IAAI,QAAQ;AAC7B,WAAO,MAAM;AACX,WAAK,YAAY,OAAO,QAAQ;AAAA,IAClC;AAAA,EACF;AAAA,EAEQ,kBAAsC;AAC5C,UAAM,QAAQ,aAAa,QAAQ,KAAK,QAAQ,GAAG;AACnD,WAAO,UAAU,OAAO,QAAQ;AAAA,EAClC;AAAA,EAEQ,cAAc,OAAqB;AACzC,iBAAa,QAAQ,KAAK,QAAQ,KAAK,KAAK;AAAA,EAC9C;AAAA,EAEQ,oBAA0B;AAChC,eAAW,cAAc,KAAK,aAAa;AACzC,iBAAW,KAAK,KAAK;AAAA,IACvB;AAAA,EACF;AACF;AAEO,SAAS,UAAU,SAAsC;AAC9D,SAAO,IAAI,UAAU,OAAO;AAC9B;;;"}
@@ -0,0 +1,2 @@
1
+ export { bindValue, BindValue, type BindValueOptions } from "./bindValue.js";
2
+ export { useStorage, type UseStorageReturn } from "./useStorage.js";
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const bindValue = require("./bindValue.js");
4
+ const useStorage = require("./useStorage.js");
5
+ exports.BindValue = bindValue.BindValue;
6
+ exports.bindValue = bindValue.bindValue;
7
+ exports.useStorage = useStorage.useStorage;
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;"}
@@ -0,0 +1,6 @@
1
+ import { type BindValue } from "./bindValue.js";
2
+ export interface UseStorageReturn {
3
+ value: string | undefined;
4
+ setValue: (value: string) => void;
5
+ }
6
+ export declare function useStorage(binding: BindValue): UseStorageReturn;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const react = require("react");
4
+ function useStorage(binding) {
5
+ const [value, setValue] = react.useState(binding.getValue());
6
+ react.useEffect(() => {
7
+ const unsubscribe = binding.subscribe(setValue);
8
+ return unsubscribe;
9
+ }, [binding]);
10
+ const set = (newValue) => {
11
+ binding.set(newValue);
12
+ };
13
+ return { value, setValue: set };
14
+ }
15
+ exports.useStorage = useStorage;
16
+ //# sourceMappingURL=useStorage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useStorage.js","sources":["../../../../src/useStorage.ts"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport { type BindValue } from \"./bindValue.js\";\n\nexport interface UseStorageReturn {\n value: string | undefined;\n setValue: (value: string) => void;\n}\n\nexport function useStorage(binding: BindValue): UseStorageReturn {\n const [value, setValue] = useState(binding.getValue());\n\n useEffect(() => {\n const unsubscribe = binding.subscribe(setValue);\n return unsubscribe;\n }, [binding]);\n\n const set = (newValue: string) => {\n binding.set(newValue);\n };\n\n return { value, setValue: set };\n}\n"],"names":["useState","useEffect"],"mappings":";;;AAQO,SAAS,WAAW,SAAsC;AAC/D,QAAM,CAAC,OAAO,QAAQ,IAAIA,MAAAA,SAAS,QAAQ,UAAU;AAErDC,QAAAA,UAAU,MAAM;AACd,UAAM,cAAc,QAAQ,UAAU,QAAQ;AAC9C,WAAO;AAAA,EACT,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,MAAM,CAAC,aAAqB;AAChC,YAAQ,IAAI,QAAQ;AAAA,EACtB;AAEA,SAAO,EAAE,OAAO,UAAU,IAAA;AAC5B;;"}
@@ -0,0 +1,16 @@
1
+ export interface BindValueOptions {
2
+ key: string;
3
+ }
4
+ export declare class BindValue {
5
+ private options;
6
+ private value;
7
+ private subscribers;
8
+ constructor(options: BindValueOptions);
9
+ getValue(): string | undefined;
10
+ set(newValue: string): void;
11
+ subscribe(callback: (value: string | undefined) => void): () => void;
12
+ private loadFromStorage;
13
+ private saveToStorage;
14
+ private notifySubscribers;
15
+ }
16
+ export declare function bindValue(options: BindValueOptions): BindValue;
@@ -0,0 +1,46 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ class BindValue {
5
+ constructor(options) {
6
+ __publicField(this, "value");
7
+ __publicField(this, "subscribers");
8
+ this.options = options;
9
+ this.subscribers = /* @__PURE__ */ new Set();
10
+ this.value = this.loadFromStorage();
11
+ }
12
+ getValue() {
13
+ return this.value;
14
+ }
15
+ set(newValue) {
16
+ this.value = newValue;
17
+ this.saveToStorage(newValue);
18
+ this.notifySubscribers();
19
+ }
20
+ subscribe(callback) {
21
+ this.subscribers.add(callback);
22
+ return () => {
23
+ this.subscribers.delete(callback);
24
+ };
25
+ }
26
+ loadFromStorage() {
27
+ const value = localStorage.getItem(this.options.key);
28
+ return value !== null ? value : void 0;
29
+ }
30
+ saveToStorage(value) {
31
+ localStorage.setItem(this.options.key, value);
32
+ }
33
+ notifySubscribers() {
34
+ for (const subscriber of this.subscribers) {
35
+ subscriber(this.value);
36
+ }
37
+ }
38
+ }
39
+ function bindValue(options) {
40
+ return new BindValue(options);
41
+ }
42
+ export {
43
+ BindValue,
44
+ bindValue
45
+ };
46
+ //# sourceMappingURL=bindValue.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bindValue.mjs","sources":["../../../../src/bindValue.ts"],"sourcesContent":["export interface BindValueOptions {\n key: string;\n}\n\nexport class BindValue {\n private value: string | undefined;\n private subscribers: Set<(value: string | undefined) => void>;\n\n constructor(private options: BindValueOptions) {\n this.subscribers = new Set();\n this.value = this.loadFromStorage();\n }\n\n getValue(): string | undefined {\n return this.value;\n }\n\n set(newValue: string): void {\n this.value = newValue;\n this.saveToStorage(newValue);\n this.notifySubscribers();\n }\n\n subscribe(callback: (value: string | undefined) => void): () => void {\n this.subscribers.add(callback);\n return () => {\n this.subscribers.delete(callback);\n };\n }\n\n private loadFromStorage(): string | undefined {\n const value = localStorage.getItem(this.options.key);\n return value !== null ? value : undefined;\n }\n\n private saveToStorage(value: string): void {\n localStorage.setItem(this.options.key, value);\n }\n\n private notifySubscribers(): void {\n for (const subscriber of this.subscribers) {\n subscriber(this.value);\n }\n }\n}\n\nexport function bindValue(options: BindValueOptions): BindValue {\n return new BindValue(options);\n}\n"],"names":[],"mappings":";;;AAIO,MAAM,UAAU;AAAA,EAIrB,YAAoB,SAA2B;AAHvC;AACA;AAEY,SAAA,UAAA;AAClB,SAAK,kCAAkB,IAAA;AACvB,SAAK,QAAQ,KAAK,gBAAA;AAAA,EACpB;AAAA,EAEA,WAA+B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,UAAwB;AAC1B,SAAK,QAAQ;AACb,SAAK,cAAc,QAAQ;AAC3B,SAAK,kBAAA;AAAA,EACP;AAAA,EAEA,UAAU,UAA2D;AACnE,SAAK,YAAY,IAAI,QAAQ;AAC7B,WAAO,MAAM;AACX,WAAK,YAAY,OAAO,QAAQ;AAAA,IAClC;AAAA,EACF;AAAA,EAEQ,kBAAsC;AAC5C,UAAM,QAAQ,aAAa,QAAQ,KAAK,QAAQ,GAAG;AACnD,WAAO,UAAU,OAAO,QAAQ;AAAA,EAClC;AAAA,EAEQ,cAAc,OAAqB;AACzC,iBAAa,QAAQ,KAAK,QAAQ,KAAK,KAAK;AAAA,EAC9C;AAAA,EAEQ,oBAA0B;AAChC,eAAW,cAAc,KAAK,aAAa;AACzC,iBAAW,KAAK,KAAK;AAAA,IACvB;AAAA,EACF;AACF;AAEO,SAAS,UAAU,SAAsC;AAC9D,SAAO,IAAI,UAAU,OAAO;AAC9B;"}
@@ -0,0 +1,2 @@
1
+ export { bindValue, BindValue, type BindValueOptions } from "./bindValue.js";
2
+ export { useStorage, type UseStorageReturn } from "./useStorage.js";
@@ -0,0 +1,8 @@
1
+ import { BindValue, bindValue } from "./bindValue.mjs";
2
+ import { useStorage } from "./useStorage.mjs";
3
+ export {
4
+ BindValue,
5
+ bindValue,
6
+ useStorage
7
+ };
8
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
@@ -0,0 +1,6 @@
1
+ import { type BindValue } from "./bindValue.js";
2
+ export interface UseStorageReturn {
3
+ value: string | undefined;
4
+ setValue: (value: string) => void;
5
+ }
6
+ export declare function useStorage(binding: BindValue): UseStorageReturn;
@@ -0,0 +1,16 @@
1
+ import { useState, useEffect } from "react";
2
+ function useStorage(binding) {
3
+ const [value, setValue] = useState(binding.getValue());
4
+ useEffect(() => {
5
+ const unsubscribe = binding.subscribe(setValue);
6
+ return unsubscribe;
7
+ }, [binding]);
8
+ const set = (newValue) => {
9
+ binding.set(newValue);
10
+ };
11
+ return { value, setValue: set };
12
+ }
13
+ export {
14
+ useStorage
15
+ };
16
+ //# sourceMappingURL=useStorage.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useStorage.mjs","sources":["../../../../src/useStorage.ts"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport { type BindValue } from \"./bindValue.js\";\n\nexport interface UseStorageReturn {\n value: string | undefined;\n setValue: (value: string) => void;\n}\n\nexport function useStorage(binding: BindValue): UseStorageReturn {\n const [value, setValue] = useState(binding.getValue());\n\n useEffect(() => {\n const unsubscribe = binding.subscribe(setValue);\n return unsubscribe;\n }, [binding]);\n\n const set = (newValue: string) => {\n binding.set(newValue);\n };\n\n return { value, setValue: set };\n}\n"],"names":[],"mappings":";AAQO,SAAS,WAAW,SAAsC;AAC/D,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,QAAQ,UAAU;AAErD,YAAU,MAAM;AACd,UAAM,cAAc,QAAQ,UAAU,QAAQ;AAC9C,WAAO;AAAA,EACT,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,MAAM,CAAC,aAAqB;AAChC,YAAQ,IAAI,QAAQ;AAAA,EACtB;AAEA,SAAO,EAAE,OAAO,UAAU,IAAA;AAC5B;"}
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./__compiled__/cjs/src/index.js";
package/index.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = require("./__compiled__/cjs/src/index.js");
package/index.mjs ADDED
@@ -0,0 +1 @@
1
+ export * from "./__compiled__/esm/src/index.mjs";
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "kvozy",
3
+ "type": "commonjs",
4
+ "version": "0.0.1",
5
+ "types": "./__compiled__/cjs/src/index.d.ts",
6
+ "module": "./__compiled__/esm/src/index.mjs",
7
+ "main": "./__compiled__/cjs/src/index.js",
8
+ "description": "Simple, minimal React library for binding localStorage keys to React state",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./__compiled__/esm/src/index.d.mts",
13
+ "default": "./__compiled__/esm/src/index.mjs"
14
+ },
15
+ "require": {
16
+ "types": "./__compiled__/cjs/src/index.d.ts",
17
+ "default": "./__compiled__/cjs/src/index.js"
18
+ },
19
+ "types": "./__compiled__/cjs/src/index.d.ts",
20
+ "default": "./__compiled__/cjs/src/index.js"
21
+ },
22
+ "./package.json": "./package.json"
23
+ },
24
+ "keywords": [
25
+ "react",
26
+ "localstorage",
27
+ "storage",
28
+ "hook",
29
+ "state",
30
+ "persistence"
31
+ ],
32
+ "author": "",
33
+ "license": "ISC",
34
+ "peerDependencies": {
35
+ "react": "^18.3.0"
36
+ },
37
+ "devDependencies": {
38
+ "@testing-library/react": "^16.3.1",
39
+ "@types/react": "^19.2.7",
40
+ "@vitejs/plugin-react": "^5.1.2",
41
+ "@vitest/browser-playwright": "^4.0.16",
42
+ "@vitest/browser-preview": "^4.0.16",
43
+ "playwright": "^1.57.0",
44
+ "prettier": "^3.7.4",
45
+ "smartbundle": "^0.14.1",
46
+ "typescript": "^5.9.3",
47
+ "vitest": "^4.0.16",
48
+ "vitest-browser-react": "^2.0.2"
49
+ }
50
+ }