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 +360 -0
- package/__compiled__/cjs/src/bindValue.d.ts +16 -0
- package/__compiled__/cjs/src/bindValue.js +46 -0
- package/__compiled__/cjs/src/bindValue.js.map +1 -0
- package/__compiled__/cjs/src/index.d.ts +2 -0
- package/__compiled__/cjs/src/index.js +8 -0
- package/__compiled__/cjs/src/index.js.map +1 -0
- package/__compiled__/cjs/src/useStorage.d.ts +6 -0
- package/__compiled__/cjs/src/useStorage.js +16 -0
- package/__compiled__/cjs/src/useStorage.js.map +1 -0
- package/__compiled__/esm/src/bindValue.d.mts +16 -0
- package/__compiled__/esm/src/bindValue.mjs +46 -0
- package/__compiled__/esm/src/bindValue.mjs.map +1 -0
- package/__compiled__/esm/src/index.d.mts +2 -0
- package/__compiled__/esm/src/index.mjs +8 -0
- package/__compiled__/esm/src/index.mjs.map +1 -0
- package/__compiled__/esm/src/useStorage.d.mts +6 -0
- package/__compiled__/esm/src/useStorage.mjs +16 -0
- package/__compiled__/esm/src/useStorage.mjs.map +1 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/index.mjs +1 -0
- package/package.json +50 -0
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,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,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 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
|
@@ -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
|
+
}
|