kvozy 0.5.0 → 0.7.0

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 CHANGED
@@ -1,6 +1,6 @@
1
- # Kvozy - React localStorage Binding Library
1
+ # Kvozy - localStorage Binding Library
2
2
 
3
- Simple, minimal React library for binding localStorage keys to React state.
3
+ Simple, minimal library for encapsulating localStorage logic.
4
4
 
5
5
  ## Overview
6
6
 
@@ -14,6 +14,7 @@ This architecture makes it easy to add connectors for other frameworks (Vue, Sve
14
14
  ## Features
15
15
 
16
16
  - Framework-agnostic core (bindValue)
17
+ - Namespace-based bindings (bindValueNS) for grouping related keys
17
18
  - Type-safe generic API with custom serialization/deserialization
18
19
  - Flexible storage support (localStorage, sessionStorage, in-memory)
19
20
  - Graceful fallback to in-memory storage when storage is unavailable
@@ -106,13 +107,94 @@ React hook that connects a BindValue instance to React state.
106
107
 
107
108
  **Behavior:**
108
109
 
109
- - `subscribe()` does NOT call the callback immediately when subscribing
110
- - Callbacks are only invoked when the value changes via `set()`
110
+ - `subscribe()` does NOT call callback immediately when subscribing
111
+ - Callbacks are only invoked when value changes via `set()`
111
112
  - If `serialize()` fails, in-memory value is kept but storage is NOT updated
112
113
  - If `deserialize()` fails, `defaultValue` is returned
113
114
 
114
115
  **Example:**
115
116
 
117
+ ```typescript
118
+ const Component = () => {
119
+ const { value, setValue } = useStorage(myBinding);
120
+
121
+ return <div>
122
+ <p>Current value: {value}</p>
123
+ <button onClick={() => setValue('new value')}>
124
+ Update Value
125
+ </button>
126
+ </div>;
127
+ };
128
+ ```
129
+
130
+ ## Namespace API
131
+
132
+ > ⚠️ **Note:** `bindValueNS` is an internal API. Use `useStorageNS` for React components. Direct usage of `bindValueNS` is not recommended.
133
+
134
+ ### BindValueNSOptions<T>
135
+
136
+ Options for creating a namespace binder. A namespace allows grouping related keys with a shared prefix and configuration.
137
+
138
+ **Parameters:**
139
+
140
+ - `prefix` (string, required) - Prefix for all keys in this namespace (cannot be empty or whitespace)
141
+ - `defaultValue` (T, required) - Default value shared across all keys in namespace
142
+ - `serialize` (function, required) - Convert value to string: `(value: T) => string`
143
+ - `deserialize` (function, required) - Convert string to value: `(serialized: string) => T`
144
+ - `storage` (Storage, optional) - localStorage, sessionStorage, or undefined for in-memory
145
+ - `version` (string, optional) - Schema version for migration support
146
+ - `migrate` (function, optional) - Migration function: `(oldSerialized: string, oldVersion: string | undefined) => T`
147
+
148
+ ### bindValueNS<T>(options)
149
+
150
+ Factory function for creating a namespace binder.
151
+
152
+ **Returns:** `BindValueNS<T>` instance
153
+
154
+ **Behavior:**
155
+
156
+ - Throws error if prefix is empty or whitespace-only
157
+ - All bindings created from the namespace share the same configuration
158
+ - Keys are combined as `${prefix}\x1F${key}` using Unit Separator
159
+
160
+ ### BindValueNS<T>.bind(key)
161
+
162
+ Creates a individual binding for a specific key within the namespace.
163
+
164
+ **Parameters:**
165
+
166
+ - `key` (string, required) - Key for this specific binding
167
+
168
+ **Returns:** `BindValue<T>` instance
169
+
170
+ **Behavior:**
171
+
172
+ - Throws error if key is empty or whitespace-only
173
+ - Combines namespace prefix with key: `${prefix}\x1F${key}`
174
+ - Each binding has independent subscribers and state
175
+
176
+ ### useStorageNS<T>(namespace, options)
177
+
178
+ React hook that connects a namespace to React state with a specific key.
179
+
180
+ **Parameters:**
181
+
182
+ - `namespace` (BindValueNS<T>, required) - Namespace instance
183
+ - `options: { key: string }` - Key for this specific binding
184
+
185
+ **Returns:** `{ value, setValue }`
186
+
187
+ - `value` - `T` - current value from storage
188
+ - `setValue` - `(value: T) => void` - function to update value
189
+
190
+ **Behavior:**
191
+
192
+ - Creates a binding internally using `namespace.bind(options.key)`
193
+ - Delegates to existing `useStorage` hook
194
+ - Components using different keys from the same namespace don't share state
195
+
196
+ **Example:**
197
+
116
198
  ```typescript
117
199
  const Component = () => {
118
200
  const { value, setValue } = useStorage(myBinding);
@@ -293,6 +375,280 @@ const statusBinding = bindEnumValue<Color>({
293
375
  });
294
376
  ```
295
377
 
378
+ ## Namespace Type-Specific Shortcuts
379
+
380
+ Kvozy provides namespace shortcuts for common types, eliminating boilerplate while maintaining the benefits of namespace-based key organization.
381
+
382
+ | Shortcut | Type | Default Value | Storage Format |
383
+ | -------------------- | ---------- | ----------------- | --------------------- |
384
+ | `bindStringValueNS` | `string` | `""` | String as-is |
385
+ | `bindNumberValueNS` | `number` | `0` | Decimal string |
386
+ | `bindBooleanValueNS` | `boolean` | `false` | `"true"` or `"false"` |
387
+ | `bindJSONValueNS<T>` | `T` | User must provide | JSON string |
388
+ | `bindEnumValueNS<E>` | `E` (enum) | User must provide | String/number as-is |
389
+
390
+ ### bindStringValueNS
391
+
392
+ For string values with identity serialization.
393
+
394
+ ```typescript
395
+ import { bindStringValueNS, useStorageNS } from "kvozy";
396
+
397
+ // Default empty string
398
+ const appNS = bindStringValueNS({
399
+ prefix: "app",
400
+ storage: localStorage,
401
+ });
402
+
403
+ // With custom default value
404
+ const userNS = bindStringValueNS({
405
+ prefix: "user",
406
+ defaultValue: "guest",
407
+ storage: localStorage,
408
+ });
409
+
410
+ // Use in components
411
+ const Component = () => {
412
+ const { value: name, setValue: setName } = useStorageNS(userNS, { key: "name" });
413
+ return <input value={name} onChange={(e) => setName(e.target.value)} />;
414
+ };
415
+ ```
416
+
417
+ ### bindNumberValueNS
418
+
419
+ For numeric values.
420
+
421
+ ```typescript
422
+ import { bindNumberValueNS, useStorageNS } from "kvozy";
423
+
424
+ const counterNS = bindNumberValueNS({
425
+ prefix: "counters",
426
+ storage: localStorage,
427
+ });
428
+
429
+ const { value: count, setValue: setCount } = useStorageNS(counterNS, {
430
+ key: "views",
431
+ });
432
+ ```
433
+
434
+ ### bindBooleanValueNS
435
+
436
+ For boolean values. Stores as `"true"` or `"false"` for readability in devtools.
437
+
438
+ ```typescript
439
+ import { bindBooleanValueNS, useStorageNS } from "kvozy";
440
+
441
+ const settingsNS = bindBooleanValueNS({
442
+ prefix: "settings",
443
+ storage: localStorage,
444
+ });
445
+
446
+ const { value: enabled, setValue: setEnabled } = useStorageNS(settingsNS, {
447
+ key: "notifications",
448
+ });
449
+ ```
450
+
451
+ ### bindJSONValueNS<T>
452
+
453
+ For complex objects and arrays. Requires a default value.
454
+
455
+ ```typescript
456
+ import { bindJSONValueNS, useStorageNS } from "kvozy";
457
+
458
+ interface User {
459
+ name: string;
460
+ email: string;
461
+ }
462
+
463
+ const userNS = bindJSONValueNS<User>({
464
+ prefix: "users",
465
+ defaultValue: { name: "", email: "" },
466
+ storage: localStorage,
467
+ });
468
+
469
+ const { value: user, setValue: setUser } = useStorageNS(userNS, {
470
+ key: "current",
471
+ });
472
+ ```
473
+
474
+ ### bindEnumValueNS<E>
475
+
476
+ For TypeScript enums. Works with both string and number enums.
477
+
478
+ ```typescript
479
+ import { bindEnumValueNS, useStorageNS } from "kvozy";
480
+
481
+ enum Theme {
482
+ Light = "light",
483
+ Dark = "dark",
484
+ }
485
+
486
+ const settingsNS = bindEnumValueNS<Theme>({
487
+ prefix: "settings",
488
+ defaultValue: Theme.Light,
489
+ storage: localStorage,
490
+ });
491
+
492
+ const { value: theme, setValue: setTheme } = useStorageNS(settingsNS, {
493
+ key: "theme",
494
+ });
495
+ ```
496
+
497
+ ### When to Use Namespaces vs. Individual Bindings
498
+
499
+ **Use namespaces when:**
500
+
501
+ - You have multiple related keys (e.g., all user settings)
502
+ - You want to share configuration across multiple keys
503
+ - You want to organize keys by feature area (user, app, settings)
504
+ - You want to avoid repeating serialize/deserialize logic
505
+
506
+ **Use individual bindings when:**
507
+
508
+ - You have a single key
509
+ - You need different serialization logic per key
510
+ - You want maximum flexibility per binding
511
+
512
+ ## Namespace Examples
513
+
514
+ ### Basic Namespace Usage
515
+
516
+ ```typescript
517
+ import { bindValueNS, useStorageNS } from 'kvozy';
518
+
519
+ // Create namespace with shared configuration
520
+ const appNS = bindValueNS<string>({
521
+ prefix: 'app',
522
+ defaultValue: '',
523
+ serialize: (v) => v,
524
+ deserialize: (s) => s,
525
+ storage: localStorage,
526
+ });
527
+
528
+ // Use in component with specific key
529
+ const Component = () => {
530
+ const { value, setValue } = useStorageNS(appNS, { key: 'user' });
531
+
532
+ return <input value={value} onChange={(e) => setValue(e.target.value)} />;
533
+ };
534
+ // Storage key will be: 'app\x1Fuser'
535
+ ```
536
+
537
+ ### Multiple Components Sharing Namespace
538
+
539
+ ```typescript
540
+ import { bindValueNS, useStorageNS } from 'kvozy';
541
+
542
+ const appNS = bindValueNS<string>({
543
+ prefix: 'app',
544
+ defaultValue: '',
545
+ serialize: (v) => v,
546
+ deserialize: (s) => s,
547
+ storage: localStorage,
548
+ });
549
+
550
+ const UserSettings = () => {
551
+ const { value: language, setValue: setLanguage } = useStorageNS(appNS, { key: 'language' });
552
+ const { value: theme, setValue: setTheme } = useStorageNS(appNS, { key: 'theme' });
553
+
554
+ return (
555
+ <div>
556
+ <select value={language} onChange={(e) => setLanguage(e.target.value)}>
557
+ <option value="en">English</option>
558
+ <option value="es">Spanish</option>
559
+ </select>
560
+ <button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
561
+ Toggle Theme
562
+ </button>
563
+ </div>
564
+ );
565
+ };
566
+ ```
567
+
568
+ ### Namespace with Versioning
569
+
570
+ ```typescript
571
+ import { bindValueNS, useStorageNS } from "kvozy";
572
+
573
+ const appNS = bindValueNS<UserData>({
574
+ prefix: "app",
575
+ defaultValue: { name: "", email: "" },
576
+ serialize: (v) => JSON.stringify(v),
577
+ deserialize: (s) => JSON.parse(s),
578
+ storage: localStorage,
579
+ version: "2.0.0",
580
+ migrate: (old, oldVersion) => {
581
+ const oldData = JSON.parse(old);
582
+ return { name: oldData.name, email: "" };
583
+ },
584
+ });
585
+
586
+ const { value: user, setValue: setUser } = useStorageNS(appNS, { key: "user" });
587
+ ```
588
+
589
+ ### Namespace Organization Pattern
590
+
591
+ Organize your app by creating separate namespaces for different domains:
592
+
593
+ ```typescript
594
+ // User-related keys
595
+ const userNS = bindValueNS<string>({
596
+ prefix: "user",
597
+ defaultValue: "",
598
+ storage: localStorage,
599
+ });
600
+
601
+ // Application settings keys
602
+ const settingsNS = bindValueNS<string>({
603
+ prefix: "settings",
604
+ defaultValue: "",
605
+ storage: localStorage,
606
+ });
607
+
608
+ // Temporary state keys
609
+ const tempNS = bindValueNS<string>({
610
+ prefix: "temp",
611
+ defaultValue: "",
612
+ storage: undefined, // in-memory only
613
+ });
614
+
615
+ // Keys will be stored as:
616
+ // user\x1Fname', 'user\x1Femail', 'user\x1Ftheme'
617
+ // Settings keys: 'settings\x1Fnotifications', 'settings\x1Flanguage', 'settings\x1Ftheme'
618
+ // Temp keys: 'temp\x1Fdraft', 'temp\x1Funsaved'
619
+ ```
620
+
621
+ ## Namespace Isolation
622
+
623
+ Each namespace maintains complete isolation from other namespaces:
624
+
625
+ ```typescript
626
+ const appNS = bindValueNS<string>({
627
+ prefix: "app",
628
+ defaultValue: "",
629
+ storage: localStorage,
630
+ });
631
+
632
+ const userNS = bindValueNS<string>({
633
+ prefix: "user",
634
+ defaultValue: "",
635
+ storage: localStorage,
636
+ });
637
+
638
+ // These are completely isolated:
639
+ // appNS.bind('name') stores to 'app\x1Fname'
640
+ // userNS.bind('name') stores to 'user\x1Fname'
641
+ // No risk of key collisions between namespaces
642
+ ```
643
+
644
+ **Benefits:**
645
+
646
+ - **Organized storage**: Group related keys with meaningful prefixes
647
+ - **Collision prevention**: Different prefixes create separate storage domains
648
+ - **Shared configuration**: All bindings in a namespace inherit same settings
649
+ - **Easy maintenance**: Update settings once for all keys in namespace
650
+ - **Clean separation**: Logical boundaries between different app features
651
+
296
652
  ### When to Use Shortcuts vs. bindValue
297
653
 
298
654
  **Use shortcuts when:**
@@ -368,6 +724,37 @@ const themeBinding = bindEnumValue<Theme>({
368
724
  });
369
725
  ```
370
726
 
727
+ ## Namespace Isolation
728
+
729
+ Each namespace maintains complete isolation from other namespaces:
730
+
731
+ ```typescript
732
+ const appNS = bindValueNS<string>({
733
+ prefix: "app",
734
+ defaultValue: "",
735
+ storage: localStorage,
736
+ });
737
+
738
+ const userNS = bindValueNS<string>({
739
+ prefix: "user",
740
+ defaultValue: "",
741
+ storage: localStorage,
742
+ });
743
+
744
+ // These are completely isolated:
745
+ // appNS.bind('name') stores to 'app\x1Fname'
746
+ // userNS.bind('name') stores to 'user\x1Fname'
747
+ // No risk of key collisions between namespaces
748
+ ```
749
+
750
+ **Benefits:**
751
+
752
+ - **Organized storage**: Group related keys with meaningful prefixes
753
+ - **Collision prevention**: Different prefixes create separate storage domains
754
+ - **Shared configuration**: All bindings in a namespace inherit same settings
755
+ - **Easy maintenance**: Update settings once for all keys in namespace
756
+ - **Clean separation**: Logical boundaries between different app features
757
+
371
758
  ## Usage Examples
372
759
 
373
760
  ### Basic Usage
@@ -685,6 +1072,46 @@ const Counter = () => {
685
1072
  };
686
1073
  ```
687
1074
 
1075
+ ### Namespace-Based Organization
1076
+
1077
+ Organize your app by creating separate namespaces for different domains:
1078
+
1079
+ ```typescript
1080
+ // User-related keys
1081
+ const userNS = bindValueNS<string>({
1082
+ prefix: 'user',
1083
+ defaultValue: '',
1084
+ storage: localStorage,
1085
+ });
1086
+
1087
+ // Application settings keys
1088
+ const settingsNS = bindValueNS<string>({
1089
+ prefix: 'settings',
1090
+ defaultValue: '',
1091
+ storage: localStorage,
1092
+ });
1093
+
1094
+ const UserPreferences = () => {
1095
+ const { value: language, setValue: setLanguage } = useStorageNS(userNS, { key: 'language' });
1096
+ const { value: notificationsEnabled, setValue: setNotifications } = useStorageNS(settingsNS, { key: 'notifications' });
1097
+
1098
+ return (
1099
+ <div>
1100
+ <select value={language} onChange={(e) => setLanguage(e.target.value)}>
1101
+ <option value="en">English</option>
1102
+ <option value="es">Spanish</option>
1103
+ </select>
1104
+ <button onClick={() => setNotifications(!notificationsEnabled)}>
1105
+ {notificationsEnabled ? 'Disable' : 'Enable'} Notifications
1106
+ </button>
1107
+ </div>
1108
+ );
1109
+ };
1110
+
1111
+ // User keys: 'user\x1Flanguage', 'user\x1Femail', 'user\x1Ftheme'
1112
+ // Settings keys: 'settings\x1Fnotifications', 'settings\x1Flanguage', 'settings\x1Ftheme'
1113
+ ```
1114
+
688
1115
  ## Architecture
689
1116
 
690
1117
  ### Core: bindValue
@@ -708,6 +1135,22 @@ class BindValue<T> {
708
1135
  }
709
1136
  ```
710
1137
 
1138
+ ### Namespace: BindValueNS
1139
+
1140
+ Namespace binder for grouping related keys with shared configuration:
1141
+
1142
+ - Manages namespace configuration (prefix, defaultValue, serialize, deserialize)
1143
+ - Provides `bind(key)` method to create individual `BindValue` instances
1144
+ - Combines prefix and key using Unit Separator: `${prefix}\x1F${key}`
1145
+ - Validates prefix and key are not empty/whitespace
1146
+ - Creates isolated storage domains for different prefixes
1147
+
1148
+ ```typescript
1149
+ class BindValueNS<T> {
1150
+ bind(key: string): BindValue<T>;
1151
+ }
1152
+ ```
1153
+
711
1154
  ### React: useStorage
712
1155
 
713
1156
  Thin wrapper that connects `bindValue` to React:
@@ -737,6 +1180,7 @@ function useStorage<T>(binding: BindValue<T>): UseStorageReturn<T> {
737
1180
  - **No SSR support**: Currently designed for client-side only (requires `window.localStorage`).
738
1181
  - **No cross-tab sync**: Changes in one tab don't update other tabs automatically.
739
1182
  - **Serialize failures**: If `serialize()` fails, the value is kept in memory but not persisted to storage (graceful degradation).
1183
+ - **Namespace state isolation**: Components using the same namespace but different keys don't share React state. Each `useStorageNS(namespace, { key: 'x' })` call creates an independent binding.
740
1184
 
741
1185
  ## Future Plans
742
1186
 
@@ -1,4 +1,4 @@
1
- import { BindValue, type BindValueOptions } from "./bindValue.js";
1
+ import { BindValue, BindValueNS, type BindValueOptions, type BindValueNSOptions } from "./bindValue.js";
2
2
  export declare function bindStringValue(options: Omit<BindValueOptions<string>, "serialize" | "deserialize" | "defaultValue"> & {
3
3
  defaultValue?: string;
4
4
  }): BindValue<string>;
@@ -10,3 +10,14 @@ export declare function bindBooleanValue(options: Omit<BindValueOptions<boolean>
10
10
  }): BindValue<boolean>;
11
11
  export declare function bindJSONValue<T>(options: Omit<BindValueOptions<T>, "serialize" | "deserialize">): BindValue<T>;
12
12
  export declare function bindEnumValue<E extends string | number>(options: Omit<BindValueOptions<E>, "serialize" | "deserialize">): BindValue<E>;
13
+ export declare function bindStringValueNS(options: Omit<BindValueNSOptions<string>, "serialize" | "deserialize" | "defaultValue"> & {
14
+ defaultValue?: string;
15
+ }): BindValueNS<string>;
16
+ export declare function bindNumberValueNS(options: Omit<BindValueNSOptions<number>, "serialize" | "deserialize" | "defaultValue"> & {
17
+ defaultValue?: number;
18
+ }): BindValueNS<number>;
19
+ export declare function bindBooleanValueNS(options: Omit<BindValueNSOptions<boolean>, "serialize" | "deserialize" | "defaultValue"> & {
20
+ defaultValue?: boolean;
21
+ }): BindValueNS<boolean>;
22
+ export declare function bindJSONValueNS<T>(options: Omit<BindValueNSOptions<T>, "serialize" | "deserialize">): BindValueNS<T>;
23
+ export declare function bindEnumValueNS<E extends string | number>(options: Omit<BindValueNSOptions<E>, "serialize" | "deserialize">): BindValueNS<E>;
@@ -56,9 +56,69 @@ function bindEnumValue(options) {
56
56
  migrate: options.migrate
57
57
  });
58
58
  }
59
+ function bindStringValueNS(options) {
60
+ return bindValue.bindValueNS({
61
+ prefix: options.prefix,
62
+ defaultValue: options.defaultValue ?? "",
63
+ serialize: (v) => v,
64
+ deserialize: (s) => s,
65
+ storage: options.storage,
66
+ version: options.version,
67
+ migrate: options.migrate
68
+ });
69
+ }
70
+ function bindNumberValueNS(options) {
71
+ return bindValue.bindValueNS({
72
+ prefix: options.prefix,
73
+ defaultValue: options.defaultValue ?? 0,
74
+ serialize: (v) => String(v),
75
+ deserialize: (s) => Number(s),
76
+ storage: options.storage,
77
+ version: options.version,
78
+ migrate: options.migrate
79
+ });
80
+ }
81
+ function bindBooleanValueNS(options) {
82
+ return bindValue.bindValueNS({
83
+ prefix: options.prefix,
84
+ defaultValue: options.defaultValue ?? false,
85
+ serialize: (v) => String(v),
86
+ deserialize: (s) => s === "true",
87
+ storage: options.storage,
88
+ version: options.version,
89
+ migrate: options.migrate
90
+ });
91
+ }
92
+ function bindJSONValueNS(options) {
93
+ return bindValue.bindValueNS({
94
+ prefix: options.prefix,
95
+ defaultValue: options.defaultValue,
96
+ serialize: (v) => JSON.stringify(v),
97
+ deserialize: (s) => JSON.parse(s),
98
+ storage: options.storage,
99
+ version: options.version,
100
+ migrate: options.migrate
101
+ });
102
+ }
103
+ function bindEnumValueNS(options) {
104
+ return bindValue.bindValueNS({
105
+ prefix: options.prefix,
106
+ defaultValue: options.defaultValue,
107
+ serialize: (v) => String(v),
108
+ deserialize: (s) => typeof options.defaultValue === "number" ? Number(s) : s,
109
+ storage: options.storage,
110
+ version: options.version,
111
+ migrate: options.migrate
112
+ });
113
+ }
59
114
  exports.bindBooleanValue = bindBooleanValue;
115
+ exports.bindBooleanValueNS = bindBooleanValueNS;
60
116
  exports.bindEnumValue = bindEnumValue;
117
+ exports.bindEnumValueNS = bindEnumValueNS;
61
118
  exports.bindJSONValue = bindJSONValue;
119
+ exports.bindJSONValueNS = bindJSONValueNS;
62
120
  exports.bindNumberValue = bindNumberValue;
121
+ exports.bindNumberValueNS = bindNumberValueNS;
63
122
  exports.bindStringValue = bindStringValue;
123
+ exports.bindStringValueNS = bindStringValueNS;
64
124
  //# sourceMappingURL=bindTypes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bindTypes.js","sources":["../../../../src/bindTypes.ts"],"sourcesContent":["import { bindValue, BindValue, type BindValueOptions } from \"./bindValue.js\";\n\nexport function bindStringValue(\n options: Omit<\n BindValueOptions<string>,\n \"serialize\" | \"deserialize\" | \"defaultValue\"\n > & {\n defaultValue?: string;\n },\n): BindValue<string> {\n return bindValue<string>({\n key: options.key,\n defaultValue: options.defaultValue ?? \"\",\n serialize: (v) => v,\n deserialize: (s) => s,\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindNumberValue(\n options: Omit<\n BindValueOptions<number>,\n \"serialize\" | \"deserialize\" | \"defaultValue\"\n > & {\n defaultValue?: number;\n },\n): BindValue<number> {\n return bindValue<number>({\n key: options.key,\n defaultValue: options.defaultValue ?? 0,\n serialize: (v) => String(v),\n deserialize: (s) => Number(s),\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindBooleanValue(\n options: Omit<\n BindValueOptions<boolean>,\n \"serialize\" | \"deserialize\" | \"defaultValue\"\n > & {\n defaultValue?: boolean;\n },\n): BindValue<boolean> {\n return bindValue<boolean>({\n key: options.key,\n defaultValue: options.defaultValue ?? false,\n serialize: (v) => String(v),\n deserialize: (s) => s === \"true\",\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindJSONValue<T>(\n options: Omit<BindValueOptions<T>, \"serialize\" | \"deserialize\">,\n): BindValue<T> {\n return bindValue<T>({\n key: options.key,\n defaultValue: options.defaultValue,\n serialize: (v) => JSON.stringify(v),\n deserialize: (s) => JSON.parse(s),\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindEnumValue<E extends string | number>(\n options: Omit<BindValueOptions<E>, \"serialize\" | \"deserialize\">,\n): BindValue<E> {\n return bindValue<E>({\n key: options.key,\n defaultValue: options.defaultValue,\n serialize: (v) => String(v),\n deserialize: (s) =>\n (typeof options.defaultValue === \"number\" ? Number(s) : s) as E,\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n"],"names":["bindValue"],"mappings":";;;AAEO,SAAS,gBACd,SAMmB;AACnB,SAAOA,oBAAkB;AAAA,IACvB,KAAK,QAAQ;AAAA,IACb,cAAc,QAAQ,gBAAgB;AAAA,IACtC,WAAW,CAAC,MAAM;AAAA,IAClB,aAAa,CAAC,MAAM;AAAA,IACpB,SAAS,QAAQ;AAAA,IACjB,SAAS,QAAQ;AAAA,IACjB,SAAS,QAAQ;AAAA,EAAA,CAClB;AACH;AAEO,SAAS,gBACd,SAMmB;AACnB,SAAOA,oBAAkB;AAAA,IACvB,KAAK,QAAQ;AAAA,IACb,cAAc,QAAQ,gBAAgB;AAAA,IACtC,WAAW,CAAC,MAAM,OAAO,CAAC;AAAA,IAC1B,aAAa,CAAC,MAAM,OAAO,CAAC;AAAA,IAC5B,SAAS,QAAQ;AAAA,IACjB,SAAS,QAAQ;AAAA,IACjB,SAAS,QAAQ;AAAA,EAAA,CAClB;AACH;AAEO,SAAS,iBACd,SAMoB;AACpB,SAAOA,oBAAmB;AAAA,IACxB,KAAK,QAAQ;AAAA,IACb,cAAc,QAAQ,gBAAgB;AAAA,IACtC,WAAW,CAAC,MAAM,OAAO,CAAC;AAAA,IAC1B,aAAa,CAAC,MAAM,MAAM;AAAA,IAC1B,SAAS,QAAQ;AAAA,IACjB,SAAS,QAAQ;AAAA,IACjB,SAAS,QAAQ;AAAA,EAAA,CAClB;AACH;AAEO,SAAS,cACd,SACc;AACd,SAAOA,oBAAa;AAAA,IAClB,KAAK,QAAQ;AAAA,IACb,cAAc,QAAQ;AAAA,IACtB,WAAW,CAAC,MAAM,KAAK,UAAU,CAAC;AAAA,IAClC,aAAa,CAAC,MAAM,KAAK,MAAM,CAAC;AAAA,IAChC,SAAS,QAAQ;AAAA,IACjB,SAAS,QAAQ;AAAA,IACjB,SAAS,QAAQ;AAAA,EAAA,CAClB;AACH;AAEO,SAAS,cACd,SACc;AACd,SAAOA,oBAAa;AAAA,IAClB,KAAK,QAAQ;AAAA,IACb,cAAc,QAAQ;AAAA,IACtB,WAAW,CAAC,MAAM,OAAO,CAAC;AAAA,IAC1B,aAAa,CAAC,MACX,OAAO,QAAQ,iBAAiB,WAAW,OAAO,CAAC,IAAI;AAAA,IAC1D,SAAS,QAAQ;AAAA,IACjB,SAAS,QAAQ;AAAA,IACjB,SAAS,QAAQ;AAAA,EAAA,CAClB;AACH;;;;;;"}
1
+ {"version":3,"file":"bindTypes.js","sources":["../../../../src/bindTypes.ts"],"sourcesContent":["import {\n bindValue,\n bindValueNS,\n BindValue,\n BindValueNS,\n type BindValueOptions,\n type BindValueNSOptions,\n} from \"./bindValue.js\";\n\nexport function bindStringValue(\n options: Omit<\n BindValueOptions<string>,\n \"serialize\" | \"deserialize\" | \"defaultValue\"\n > & {\n defaultValue?: string;\n },\n): BindValue<string> {\n return bindValue<string>({\n key: options.key,\n defaultValue: options.defaultValue ?? \"\",\n serialize: (v) => v,\n deserialize: (s) => s,\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindNumberValue(\n options: Omit<\n BindValueOptions<number>,\n \"serialize\" | \"deserialize\" | \"defaultValue\"\n > & {\n defaultValue?: number;\n },\n): BindValue<number> {\n return bindValue<number>({\n key: options.key,\n defaultValue: options.defaultValue ?? 0,\n serialize: (v) => String(v),\n deserialize: (s) => Number(s),\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindBooleanValue(\n options: Omit<\n BindValueOptions<boolean>,\n \"serialize\" | \"deserialize\" | \"defaultValue\"\n > & {\n defaultValue?: boolean;\n },\n): BindValue<boolean> {\n return bindValue<boolean>({\n key: options.key,\n defaultValue: options.defaultValue ?? false,\n serialize: (v) => String(v),\n deserialize: (s) => s === \"true\",\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindJSONValue<T>(\n options: Omit<BindValueOptions<T>, \"serialize\" | \"deserialize\">,\n): BindValue<T> {\n return bindValue<T>({\n key: options.key,\n defaultValue: options.defaultValue,\n serialize: (v) => JSON.stringify(v),\n deserialize: (s) => JSON.parse(s),\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindEnumValue<E extends string | number>(\n options: Omit<BindValueOptions<E>, \"serialize\" | \"deserialize\">,\n): BindValue<E> {\n return bindValue<E>({\n key: options.key,\n defaultValue: options.defaultValue,\n serialize: (v) => String(v),\n deserialize: (s) =>\n (typeof options.defaultValue === \"number\" ? Number(s) : s) as E,\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindStringValueNS(\n options: Omit<\n BindValueNSOptions<string>,\n \"serialize\" | \"deserialize\" | \"defaultValue\"\n > & {\n defaultValue?: string;\n },\n): BindValueNS<string> {\n return bindValueNS<string>({\n prefix: options.prefix,\n defaultValue: options.defaultValue ?? \"\",\n serialize: (v) => v,\n deserialize: (s) => s,\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindNumberValueNS(\n options: Omit<\n BindValueNSOptions<number>,\n \"serialize\" | \"deserialize\" | \"defaultValue\"\n > & {\n defaultValue?: number;\n },\n): BindValueNS<number> {\n return bindValueNS<number>({\n prefix: options.prefix,\n defaultValue: options.defaultValue ?? 0,\n serialize: (v) => String(v),\n deserialize: (s) => Number(s),\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindBooleanValueNS(\n options: Omit<\n BindValueNSOptions<boolean>,\n \"serialize\" | \"deserialize\" | \"defaultValue\"\n > & {\n defaultValue?: boolean;\n },\n): BindValueNS<boolean> {\n return bindValueNS<boolean>({\n prefix: options.prefix,\n defaultValue: options.defaultValue ?? false,\n serialize: (v) => String(v),\n deserialize: (s) => s === \"true\",\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindJSONValueNS<T>(\n options: Omit<BindValueNSOptions<T>, \"serialize\" | \"deserialize\">,\n): BindValueNS<T> {\n return bindValueNS<T>({\n prefix: options.prefix,\n defaultValue: options.defaultValue,\n serialize: (v) => JSON.stringify(v),\n deserialize: (s) => JSON.parse(s),\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindEnumValueNS<E extends string | number>(\n options: Omit<BindValueNSOptions<E>, \"serialize\" | \"deserialize\">,\n): BindValueNS<E> {\n return bindValueNS<E>({\n prefix: options.prefix,\n defaultValue: options.defaultValue,\n serialize: (v) => String(v),\n deserialize: (s) =>\n (typeof options.defaultValue === \"number\" ? Number(s) : s) as E,\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n"],"names":["bindValue","bindValueNS"],"mappings":"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"}
@@ -7,6 +7,15 @@ export interface BindValueOptions<T> {
7
7
  version?: string;
8
8
  migrate?: (oldSerialized: string, oldVersion: string | undefined) => T;
9
9
  }
10
+ export interface BindValueNSOptions<T> {
11
+ prefix: string;
12
+ defaultValue: T;
13
+ serialize: (value: T) => string;
14
+ deserialize: (serialized: string) => T;
15
+ storage?: Storage;
16
+ version?: string;
17
+ migrate?: (oldSerialized: string, oldVersion: string | undefined) => T;
18
+ }
10
19
  export declare class BindValue<T> {
11
20
  private options;
12
21
  private value;
@@ -16,8 +25,17 @@ export declare class BindValue<T> {
16
25
  getValue(): T;
17
26
  set(newValue: T): void;
18
27
  subscribe(callback: (value: T) => void): () => void;
28
+ getRaw(): string | null;
29
+ setRaw(rawValue: string): void;
19
30
  private loadFromStorage;
20
31
  private saveToStorage;
21
32
  private notifySubscribers;
22
33
  }
23
34
  export declare function bindValue<T>(options: BindValueOptions<T>): BindValue<T>;
35
+ export declare function _clearInMemoryStorage(): void;
36
+ export declare class BindValueNS<T> {
37
+ private options;
38
+ constructor(options: BindValueNSOptions<T>);
39
+ bind(key: string): BindValue<T>;
40
+ }
41
+ export declare function bindValueNS<T>(options: BindValueNSOptions<T>): BindValueNS<T>;
@@ -49,6 +49,48 @@ class BindValue {
49
49
  this.subscribers.delete(callback);
50
50
  };
51
51
  }
52
+ getRaw() {
53
+ return this.storage.getItem(this.options.key);
54
+ }
55
+ setRaw(rawValue) {
56
+ let deserializedValue;
57
+ const version = this.options.version;
58
+ let serializedPart;
59
+ if (version) {
60
+ if (!rawValue.startsWith("\0")) {
61
+ return;
62
+ }
63
+ const parts = rawValue.split("\0");
64
+ if (parts.length < 3) {
65
+ return;
66
+ }
67
+ const versionPart = parts[1];
68
+ if (versionPart !== version) {
69
+ return;
70
+ }
71
+ serializedPart = parts.slice(2).join("\0");
72
+ } else {
73
+ serializedPart = rawValue;
74
+ }
75
+ try {
76
+ deserializedValue = this.options.deserialize(serializedPart);
77
+ } catch {
78
+ return;
79
+ }
80
+ const serializedValue = this.options.serialize(deserializedValue);
81
+ let reconstructedRaw;
82
+ if (version) {
83
+ reconstructedRaw = `\0${version}\0${serializedValue}`;
84
+ } else {
85
+ reconstructedRaw = serializedValue;
86
+ }
87
+ if (reconstructedRaw !== rawValue) {
88
+ return;
89
+ }
90
+ this.storage.setItem(this.options.key, rawValue);
91
+ this.value = deserializedValue;
92
+ this.notifySubscribers();
93
+ }
52
94
  loadFromStorage() {
53
95
  const rawValue = this.storage.getItem(this.options.key);
54
96
  if (rawValue === null) {
@@ -116,6 +158,33 @@ class BindValue {
116
158
  function bindValue(options) {
117
159
  return new BindValue(options);
118
160
  }
161
+ class BindValueNS {
162
+ constructor(options) {
163
+ this.options = options;
164
+ if (!this.options.prefix || this.options.prefix.trim() === "") {
165
+ throw new Error("Prefix cannot be empty");
166
+ }
167
+ }
168
+ bind(key) {
169
+ if (!key || key.trim() === "") {
170
+ throw new Error("Key cannot be empty");
171
+ }
172
+ return new BindValue({
173
+ key: `${this.options.prefix}${key}`,
174
+ defaultValue: this.options.defaultValue,
175
+ serialize: this.options.serialize,
176
+ deserialize: this.options.deserialize,
177
+ storage: this.options.storage,
178
+ version: this.options.version,
179
+ migrate: this.options.migrate
180
+ });
181
+ }
182
+ }
183
+ function bindValueNS(options) {
184
+ return new BindValueNS(options);
185
+ }
119
186
  exports.BindValue = BindValue;
187
+ exports.BindValueNS = BindValueNS;
120
188
  exports.bindValue = bindValue;
189
+ exports.bindValueNS = bindValueNS;
121
190
  //# sourceMappingURL=bindValue.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bindValue.js","sources":["../../../../src/bindValue.ts"],"sourcesContent":["export interface BindValueOptions<T> {\n key: string;\n defaultValue: T;\n serialize: (value: T) => string;\n deserialize: (serialized: string) => T;\n storage?: Storage;\n version?: string;\n migrate?: (oldSerialized: string, oldVersion: string | undefined) => T;\n}\n\nconst memoryStorage = new Map<string, string>();\n\nconst inMemoryStorage: Storage = {\n get length() {\n return memoryStorage.size;\n },\n clear() {\n memoryStorage.clear();\n },\n getItem(key: string) {\n return memoryStorage.get(key) ?? null;\n },\n key(index: number) {\n const keys = Array.from(memoryStorage.keys());\n return keys[index] ?? null;\n },\n removeItem(key: string) {\n memoryStorage.delete(key);\n },\n setItem(key: string, value: string) {\n memoryStorage.set(key, value);\n },\n};\n\nexport class BindValue<T> {\n private value: T;\n private subscribers: Set<(value: T) => void>;\n private storage: Storage;\n\n constructor(private options: BindValueOptions<T>) {\n this.storage = options.storage ?? inMemoryStorage;\n this.subscribers = new Set();\n this.value = this.loadFromStorage();\n }\n\n getValue(): T {\n return this.value;\n }\n\n set(newValue: T): void {\n this.value = newValue;\n this.saveToStorage(newValue);\n this.notifySubscribers();\n }\n\n subscribe(callback: (value: T) => void): () => void {\n this.subscribers.add(callback);\n return () => {\n this.subscribers.delete(callback);\n };\n }\n\n private loadFromStorage(): T {\n const rawValue = this.storage.getItem(this.options.key);\n\n if (rawValue === null) {\n return this.options.defaultValue;\n }\n\n let oldVersion: string | undefined;\n let serializedValue: string;\n\n if (rawValue.startsWith(\"\\x00\")) {\n const parts = rawValue.split(\"\\x00\");\n if (parts.length >= 3) {\n oldVersion = parts[1];\n serializedValue = parts.slice(2).join(\"\\x00\");\n } else {\n serializedValue = rawValue;\n }\n } else {\n serializedValue = rawValue;\n }\n\n const currentVersion = this.options.version;\n\n if (oldVersion !== currentVersion) {\n if (this.options.migrate) {\n try {\n const migratedValue = this.options.migrate(\n serializedValue,\n oldVersion,\n );\n this.saveToStorage(migratedValue);\n return migratedValue;\n } catch {\n this.storage.removeItem(this.options.key);\n return this.options.defaultValue;\n }\n } else {\n this.storage.removeItem(this.options.key);\n return this.options.defaultValue;\n }\n }\n\n try {\n return this.options.deserialize(serializedValue);\n } catch {\n return this.options.defaultValue;\n }\n }\n\n private saveToStorage(value: T): void {\n try {\n const serialized = this.options.serialize(value);\n const version = this.options.version;\n\n if (version) {\n this.storage.setItem(\n this.options.key,\n `\\x00${version}\\x00${serialized}`,\n );\n } else {\n this.storage.setItem(this.options.key, serialized);\n }\n } catch {}\n }\n\n private notifySubscribers(): void {\n for (const subscriber of this.subscribers) {\n subscriber(this.value);\n }\n }\n}\n\nexport function bindValue<T>(options: BindValueOptions<T>): BindValue<T> {\n return new BindValue(options);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bindValue.js","sources":["../../../../src/bindValue.ts"],"sourcesContent":["export interface BindValueOptions<T> {\n key: string;\n defaultValue: T;\n serialize: (value: T) => string;\n deserialize: (serialized: string) => T;\n storage?: Storage;\n version?: string;\n migrate?: (oldSerialized: string, oldVersion: string | undefined) => T;\n}\n\nexport interface BindValueNSOptions<T> {\n prefix: string;\n defaultValue: T;\n serialize: (value: T) => string;\n deserialize: (serialized: string) => T;\n storage?: Storage;\n version?: string;\n migrate?: (oldSerialized: string, oldVersion: string | undefined) => T;\n}\n\nconst memoryStorage = new Map<string, string>();\n\nconst inMemoryStorage: Storage = {\n get length() {\n return memoryStorage.size;\n },\n clear() {\n memoryStorage.clear();\n },\n getItem(key: string) {\n return memoryStorage.get(key) ?? null;\n },\n key(index: number) {\n const keys = Array.from(memoryStorage.keys());\n return keys[index] ?? null;\n },\n removeItem(key: string) {\n memoryStorage.delete(key);\n },\n setItem(key: string, value: string) {\n memoryStorage.set(key, value);\n },\n};\n\nexport class BindValue<T> {\n private value: T;\n private subscribers: Set<(value: T) => void>;\n private storage: Storage;\n\n constructor(private options: BindValueOptions<T>) {\n this.storage = options.storage ?? inMemoryStorage;\n this.subscribers = new Set();\n this.value = this.loadFromStorage();\n }\n\n getValue(): T {\n return this.value;\n }\n\n set(newValue: T): void {\n this.value = newValue;\n this.saveToStorage(newValue);\n this.notifySubscribers();\n }\n\n subscribe(callback: (value: T) => void): () => void {\n this.subscribers.add(callback);\n return () => {\n this.subscribers.delete(callback);\n };\n }\n\n getRaw(): string | null {\n return this.storage.getItem(this.options.key);\n }\n\n setRaw(rawValue: string): void {\n let deserializedValue: T;\n const version = this.options.version;\n let serializedPart: string;\n\n if (version) {\n if (!rawValue.startsWith(\"\\x00\")) {\n return;\n }\n\n const parts = rawValue.split(\"\\x00\");\n if (parts.length < 3) {\n return;\n }\n\n const versionPart = parts[1];\n if (versionPart !== version) {\n return;\n }\n\n serializedPart = parts.slice(2).join(\"\\x00\");\n } else {\n serializedPart = rawValue;\n }\n\n try {\n deserializedValue = this.options.deserialize(serializedPart);\n } catch {\n return;\n }\n const serializedValue = this.options.serialize(deserializedValue);\n let reconstructedRaw: string;\n\n if (version) {\n reconstructedRaw = `\\x00${version}\\x00${serializedValue}`;\n } else {\n reconstructedRaw = serializedValue;\n }\n\n if (reconstructedRaw !== rawValue) {\n return;\n }\n\n this.storage.setItem(this.options.key, rawValue);\n this.value = deserializedValue;\n this.notifySubscribers();\n }\n\n private loadFromStorage(): T {\n const rawValue = this.storage.getItem(this.options.key);\n\n if (rawValue === null) {\n return this.options.defaultValue;\n }\n\n let oldVersion: string | undefined;\n let serializedValue: string;\n\n if (rawValue.startsWith(\"\\x00\")) {\n const parts = rawValue.split(\"\\x00\");\n if (parts.length >= 3) {\n oldVersion = parts[1];\n serializedValue = parts.slice(2).join(\"\\x00\");\n } else {\n serializedValue = rawValue;\n }\n } else {\n serializedValue = rawValue;\n }\n\n const currentVersion = this.options.version;\n\n if (oldVersion !== currentVersion) {\n if (this.options.migrate) {\n try {\n const migratedValue = this.options.migrate(\n serializedValue,\n oldVersion,\n );\n this.saveToStorage(migratedValue);\n return migratedValue;\n } catch {\n this.storage.removeItem(this.options.key);\n return this.options.defaultValue;\n }\n } else {\n this.storage.removeItem(this.options.key);\n return this.options.defaultValue;\n }\n }\n\n try {\n return this.options.deserialize(serializedValue);\n } catch {\n return this.options.defaultValue;\n }\n }\n\n private saveToStorage(value: T): void {\n try {\n const serialized = this.options.serialize(value);\n const version = this.options.version;\n\n if (version) {\n this.storage.setItem(\n this.options.key,\n `\\x00${version}\\x00${serialized}`,\n );\n } else {\n this.storage.setItem(this.options.key, serialized);\n }\n } catch {}\n }\n\n private notifySubscribers(): void {\n for (const subscriber of this.subscribers) {\n subscriber(this.value);\n }\n }\n}\n\nexport function bindValue<T>(options: BindValueOptions<T>): BindValue<T> {\n return new BindValue(options);\n}\n\nexport function _clearInMemoryStorage(): void {\n memoryStorage.clear();\n}\n\nexport class BindValueNS<T> {\n constructor(private options: BindValueNSOptions<T>) {\n if (!this.options.prefix || this.options.prefix.trim() === \"\") {\n throw new Error(\"Prefix cannot be empty\");\n }\n }\n\n bind(key: string): BindValue<T> {\n if (!key || key.trim() === \"\") {\n throw new Error(\"Key cannot be empty\");\n }\n\n return new BindValue<T>({\n key: `${this.options.prefix}\\x1F${key}`,\n defaultValue: this.options.defaultValue,\n serialize: this.options.serialize,\n deserialize: this.options.deserialize,\n storage: this.options.storage,\n version: this.options.version,\n migrate: this.options.migrate,\n });\n }\n}\n\nexport function bindValueNS<T>(options: BindValueNSOptions<T>): BindValueNS<T> {\n return new BindValueNS(options);\n}\n"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
1
  export { bindValue, BindValue, type BindValueOptions } from "./bindValue.js";
2
- export { useStorage, type UseStorageReturn } from "./useStorage.js";
3
- export { bindStringValue, bindNumberValue, bindBooleanValue, bindJSONValue, bindEnumValue, } from "./bindTypes.js";
2
+ export { bindValueNS, BindValueNS, type BindValueNSOptions, } from "./bindValue.js";
3
+ export { useStorage, useStorageNS, type UseStorageReturn, } from "./useStorage.js";
4
+ export { bindStringValue, bindNumberValue, bindBooleanValue, bindJSONValue, bindEnumValue, bindStringValueNS, bindNumberValueNS, bindBooleanValueNS, bindJSONValueNS, bindEnumValueNS, } from "./bindTypes.js";
@@ -4,11 +4,19 @@ const bindValue = require("./bindValue.js");
4
4
  const useStorage = require("./useStorage.js");
5
5
  const bindTypes = require("./bindTypes.js");
6
6
  exports.BindValue = bindValue.BindValue;
7
+ exports.BindValueNS = bindValue.BindValueNS;
7
8
  exports.bindValue = bindValue.bindValue;
9
+ exports.bindValueNS = bindValue.bindValueNS;
8
10
  exports.useStorage = useStorage.useStorage;
11
+ exports.useStorageNS = useStorage.useStorageNS;
9
12
  exports.bindBooleanValue = bindTypes.bindBooleanValue;
13
+ exports.bindBooleanValueNS = bindTypes.bindBooleanValueNS;
10
14
  exports.bindEnumValue = bindTypes.bindEnumValue;
15
+ exports.bindEnumValueNS = bindTypes.bindEnumValueNS;
11
16
  exports.bindJSONValue = bindTypes.bindJSONValue;
17
+ exports.bindJSONValueNS = bindTypes.bindJSONValueNS;
12
18
  exports.bindNumberValue = bindTypes.bindNumberValue;
19
+ exports.bindNumberValueNS = bindTypes.bindNumberValueNS;
13
20
  exports.bindStringValue = bindTypes.bindStringValue;
21
+ exports.bindStringValueNS = bindTypes.bindStringValueNS;
14
22
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;"}
@@ -1,6 +1,9 @@
1
- import { type BindValue } from "./bindValue.js";
1
+ import { type BindValue, type BindValueNS } from "./bindValue.js";
2
2
  export interface UseStorageReturn<T> {
3
3
  value: T;
4
4
  setValue: (value: T) => void;
5
5
  }
6
6
  export declare function useStorage<T>(binding: BindValue<T>): UseStorageReturn<T>;
7
+ export declare function useStorageNS<T>(namespace: BindValueNS<T>, options: {
8
+ key: string;
9
+ }): UseStorageReturn<T>;
@@ -12,5 +12,10 @@ function useStorage(binding) {
12
12
  };
13
13
  return { value, setValue: set };
14
14
  }
15
+ function useStorageNS(namespace, options) {
16
+ const binding = namespace.bind(options.key);
17
+ return useStorage(binding);
18
+ }
15
19
  exports.useStorage = useStorage;
20
+ exports.useStorageNS = useStorageNS;
16
21
  //# sourceMappingURL=useStorage.js.map
@@ -1 +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<T> {\n value: T;\n setValue: (value: T) => void;\n}\n\nexport function useStorage<T>(binding: BindValue<T>): UseStorageReturn<T> {\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: T) => {\n binding.set(newValue);\n };\n\n return { value, setValue: set };\n}\n"],"names":["useState","useEffect"],"mappings":";;;AAQO,SAAS,WAAc,SAA4C;AACxE,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,aAAgB;AAC3B,YAAQ,IAAI,QAAQ;AAAA,EACtB;AAEA,SAAO,EAAE,OAAO,UAAU,IAAA;AAC5B;;"}
1
+ {"version":3,"file":"useStorage.js","sources":["../../../../src/useStorage.ts"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport { type BindValue, type BindValueNS } from \"./bindValue.js\";\n\nexport interface UseStorageReturn<T> {\n value: T;\n setValue: (value: T) => void;\n}\n\nexport function useStorage<T>(binding: BindValue<T>): UseStorageReturn<T> {\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: T) => {\n binding.set(newValue);\n };\n\n return { value, setValue: set };\n}\n\nexport function useStorageNS<T>(\n namespace: BindValueNS<T>,\n options: { key: string },\n): UseStorageReturn<T> {\n const binding = namespace.bind(options.key);\n return useStorage(binding);\n}\n"],"names":["useState","useEffect"],"mappings":";;;AAQO,SAAS,WAAc,SAA4C;AACxE,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,aAAgB;AAC3B,YAAQ,IAAI,QAAQ;AAAA,EACtB;AAEA,SAAO,EAAE,OAAO,UAAU,IAAA;AAC5B;AAEO,SAAS,aACd,WACA,SACqB;AACrB,QAAM,UAAU,UAAU,KAAK,QAAQ,GAAG;AAC1C,SAAO,WAAW,OAAO;AAC3B;;;"}
@@ -1,4 +1,4 @@
1
- import { BindValue, type BindValueOptions } from "./bindValue.js";
1
+ import { BindValue, BindValueNS, type BindValueOptions, type BindValueNSOptions } from "./bindValue.js";
2
2
  export declare function bindStringValue(options: Omit<BindValueOptions<string>, "serialize" | "deserialize" | "defaultValue"> & {
3
3
  defaultValue?: string;
4
4
  }): BindValue<string>;
@@ -10,3 +10,14 @@ export declare function bindBooleanValue(options: Omit<BindValueOptions<boolean>
10
10
  }): BindValue<boolean>;
11
11
  export declare function bindJSONValue<T>(options: Omit<BindValueOptions<T>, "serialize" | "deserialize">): BindValue<T>;
12
12
  export declare function bindEnumValue<E extends string | number>(options: Omit<BindValueOptions<E>, "serialize" | "deserialize">): BindValue<E>;
13
+ export declare function bindStringValueNS(options: Omit<BindValueNSOptions<string>, "serialize" | "deserialize" | "defaultValue"> & {
14
+ defaultValue?: string;
15
+ }): BindValueNS<string>;
16
+ export declare function bindNumberValueNS(options: Omit<BindValueNSOptions<number>, "serialize" | "deserialize" | "defaultValue"> & {
17
+ defaultValue?: number;
18
+ }): BindValueNS<number>;
19
+ export declare function bindBooleanValueNS(options: Omit<BindValueNSOptions<boolean>, "serialize" | "deserialize" | "defaultValue"> & {
20
+ defaultValue?: boolean;
21
+ }): BindValueNS<boolean>;
22
+ export declare function bindJSONValueNS<T>(options: Omit<BindValueNSOptions<T>, "serialize" | "deserialize">): BindValueNS<T>;
23
+ export declare function bindEnumValueNS<E extends string | number>(options: Omit<BindValueNSOptions<E>, "serialize" | "deserialize">): BindValueNS<E>;
@@ -1,4 +1,4 @@
1
- import { bindValue } from "./bindValue.mjs";
1
+ import { bindValue, bindValueNS } from "./bindValue.mjs";
2
2
  function bindStringValue(options) {
3
3
  return bindValue({
4
4
  key: options.key,
@@ -54,11 +54,71 @@ function bindEnumValue(options) {
54
54
  migrate: options.migrate
55
55
  });
56
56
  }
57
+ function bindStringValueNS(options) {
58
+ return bindValueNS({
59
+ prefix: options.prefix,
60
+ defaultValue: options.defaultValue ?? "",
61
+ serialize: (v) => v,
62
+ deserialize: (s) => s,
63
+ storage: options.storage,
64
+ version: options.version,
65
+ migrate: options.migrate
66
+ });
67
+ }
68
+ function bindNumberValueNS(options) {
69
+ return bindValueNS({
70
+ prefix: options.prefix,
71
+ defaultValue: options.defaultValue ?? 0,
72
+ serialize: (v) => String(v),
73
+ deserialize: (s) => Number(s),
74
+ storage: options.storage,
75
+ version: options.version,
76
+ migrate: options.migrate
77
+ });
78
+ }
79
+ function bindBooleanValueNS(options) {
80
+ return bindValueNS({
81
+ prefix: options.prefix,
82
+ defaultValue: options.defaultValue ?? false,
83
+ serialize: (v) => String(v),
84
+ deserialize: (s) => s === "true",
85
+ storage: options.storage,
86
+ version: options.version,
87
+ migrate: options.migrate
88
+ });
89
+ }
90
+ function bindJSONValueNS(options) {
91
+ return bindValueNS({
92
+ prefix: options.prefix,
93
+ defaultValue: options.defaultValue,
94
+ serialize: (v) => JSON.stringify(v),
95
+ deserialize: (s) => JSON.parse(s),
96
+ storage: options.storage,
97
+ version: options.version,
98
+ migrate: options.migrate
99
+ });
100
+ }
101
+ function bindEnumValueNS(options) {
102
+ return bindValueNS({
103
+ prefix: options.prefix,
104
+ defaultValue: options.defaultValue,
105
+ serialize: (v) => String(v),
106
+ deserialize: (s) => typeof options.defaultValue === "number" ? Number(s) : s,
107
+ storage: options.storage,
108
+ version: options.version,
109
+ migrate: options.migrate
110
+ });
111
+ }
57
112
  export {
58
113
  bindBooleanValue,
114
+ bindBooleanValueNS,
59
115
  bindEnumValue,
116
+ bindEnumValueNS,
60
117
  bindJSONValue,
118
+ bindJSONValueNS,
61
119
  bindNumberValue,
62
- bindStringValue
120
+ bindNumberValueNS,
121
+ bindStringValue,
122
+ bindStringValueNS
63
123
  };
64
124
  //# sourceMappingURL=bindTypes.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"bindTypes.mjs","sources":["../../../../src/bindTypes.ts"],"sourcesContent":["import { bindValue, BindValue, type BindValueOptions } from \"./bindValue.js\";\n\nexport function bindStringValue(\n options: Omit<\n BindValueOptions<string>,\n \"serialize\" | \"deserialize\" | \"defaultValue\"\n > & {\n defaultValue?: string;\n },\n): BindValue<string> {\n return bindValue<string>({\n key: options.key,\n defaultValue: options.defaultValue ?? \"\",\n serialize: (v) => v,\n deserialize: (s) => s,\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindNumberValue(\n options: Omit<\n BindValueOptions<number>,\n \"serialize\" | \"deserialize\" | \"defaultValue\"\n > & {\n defaultValue?: number;\n },\n): BindValue<number> {\n return bindValue<number>({\n key: options.key,\n defaultValue: options.defaultValue ?? 0,\n serialize: (v) => String(v),\n deserialize: (s) => Number(s),\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindBooleanValue(\n options: Omit<\n BindValueOptions<boolean>,\n \"serialize\" | \"deserialize\" | \"defaultValue\"\n > & {\n defaultValue?: boolean;\n },\n): BindValue<boolean> {\n return bindValue<boolean>({\n key: options.key,\n defaultValue: options.defaultValue ?? false,\n serialize: (v) => String(v),\n deserialize: (s) => s === \"true\",\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindJSONValue<T>(\n options: Omit<BindValueOptions<T>, \"serialize\" | \"deserialize\">,\n): BindValue<T> {\n return bindValue<T>({\n key: options.key,\n defaultValue: options.defaultValue,\n serialize: (v) => JSON.stringify(v),\n deserialize: (s) => JSON.parse(s),\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindEnumValue<E extends string | number>(\n options: Omit<BindValueOptions<E>, \"serialize\" | \"deserialize\">,\n): BindValue<E> {\n return bindValue<E>({\n key: options.key,\n defaultValue: options.defaultValue,\n serialize: (v) => String(v),\n deserialize: (s) =>\n (typeof options.defaultValue === \"number\" ? Number(s) : s) as E,\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n"],"names":[],"mappings":";AAEO,SAAS,gBACd,SAMmB;AACnB,SAAO,UAAkB;AAAA,IACvB,KAAK,QAAQ;AAAA,IACb,cAAc,QAAQ,gBAAgB;AAAA,IACtC,WAAW,CAAC,MAAM;AAAA,IAClB,aAAa,CAAC,MAAM;AAAA,IACpB,SAAS,QAAQ;AAAA,IACjB,SAAS,QAAQ;AAAA,IACjB,SAAS,QAAQ;AAAA,EAAA,CAClB;AACH;AAEO,SAAS,gBACd,SAMmB;AACnB,SAAO,UAAkB;AAAA,IACvB,KAAK,QAAQ;AAAA,IACb,cAAc,QAAQ,gBAAgB;AAAA,IACtC,WAAW,CAAC,MAAM,OAAO,CAAC;AAAA,IAC1B,aAAa,CAAC,MAAM,OAAO,CAAC;AAAA,IAC5B,SAAS,QAAQ;AAAA,IACjB,SAAS,QAAQ;AAAA,IACjB,SAAS,QAAQ;AAAA,EAAA,CAClB;AACH;AAEO,SAAS,iBACd,SAMoB;AACpB,SAAO,UAAmB;AAAA,IACxB,KAAK,QAAQ;AAAA,IACb,cAAc,QAAQ,gBAAgB;AAAA,IACtC,WAAW,CAAC,MAAM,OAAO,CAAC;AAAA,IAC1B,aAAa,CAAC,MAAM,MAAM;AAAA,IAC1B,SAAS,QAAQ;AAAA,IACjB,SAAS,QAAQ;AAAA,IACjB,SAAS,QAAQ;AAAA,EAAA,CAClB;AACH;AAEO,SAAS,cACd,SACc;AACd,SAAO,UAAa;AAAA,IAClB,KAAK,QAAQ;AAAA,IACb,cAAc,QAAQ;AAAA,IACtB,WAAW,CAAC,MAAM,KAAK,UAAU,CAAC;AAAA,IAClC,aAAa,CAAC,MAAM,KAAK,MAAM,CAAC;AAAA,IAChC,SAAS,QAAQ;AAAA,IACjB,SAAS,QAAQ;AAAA,IACjB,SAAS,QAAQ;AAAA,EAAA,CAClB;AACH;AAEO,SAAS,cACd,SACc;AACd,SAAO,UAAa;AAAA,IAClB,KAAK,QAAQ;AAAA,IACb,cAAc,QAAQ;AAAA,IACtB,WAAW,CAAC,MAAM,OAAO,CAAC;AAAA,IAC1B,aAAa,CAAC,MACX,OAAO,QAAQ,iBAAiB,WAAW,OAAO,CAAC,IAAI;AAAA,IAC1D,SAAS,QAAQ;AAAA,IACjB,SAAS,QAAQ;AAAA,IACjB,SAAS,QAAQ;AAAA,EAAA,CAClB;AACH;"}
1
+ {"version":3,"file":"bindTypes.mjs","sources":["../../../../src/bindTypes.ts"],"sourcesContent":["import {\n bindValue,\n bindValueNS,\n BindValue,\n BindValueNS,\n type BindValueOptions,\n type BindValueNSOptions,\n} from \"./bindValue.js\";\n\nexport function bindStringValue(\n options: Omit<\n BindValueOptions<string>,\n \"serialize\" | \"deserialize\" | \"defaultValue\"\n > & {\n defaultValue?: string;\n },\n): BindValue<string> {\n return bindValue<string>({\n key: options.key,\n defaultValue: options.defaultValue ?? \"\",\n serialize: (v) => v,\n deserialize: (s) => s,\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindNumberValue(\n options: Omit<\n BindValueOptions<number>,\n \"serialize\" | \"deserialize\" | \"defaultValue\"\n > & {\n defaultValue?: number;\n },\n): BindValue<number> {\n return bindValue<number>({\n key: options.key,\n defaultValue: options.defaultValue ?? 0,\n serialize: (v) => String(v),\n deserialize: (s) => Number(s),\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindBooleanValue(\n options: Omit<\n BindValueOptions<boolean>,\n \"serialize\" | \"deserialize\" | \"defaultValue\"\n > & {\n defaultValue?: boolean;\n },\n): BindValue<boolean> {\n return bindValue<boolean>({\n key: options.key,\n defaultValue: options.defaultValue ?? false,\n serialize: (v) => String(v),\n deserialize: (s) => s === \"true\",\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindJSONValue<T>(\n options: Omit<BindValueOptions<T>, \"serialize\" | \"deserialize\">,\n): BindValue<T> {\n return bindValue<T>({\n key: options.key,\n defaultValue: options.defaultValue,\n serialize: (v) => JSON.stringify(v),\n deserialize: (s) => JSON.parse(s),\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindEnumValue<E extends string | number>(\n options: Omit<BindValueOptions<E>, \"serialize\" | \"deserialize\">,\n): BindValue<E> {\n return bindValue<E>({\n key: options.key,\n defaultValue: options.defaultValue,\n serialize: (v) => String(v),\n deserialize: (s) =>\n (typeof options.defaultValue === \"number\" ? Number(s) : s) as E,\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindStringValueNS(\n options: Omit<\n BindValueNSOptions<string>,\n \"serialize\" | \"deserialize\" | \"defaultValue\"\n > & {\n defaultValue?: string;\n },\n): BindValueNS<string> {\n return bindValueNS<string>({\n prefix: options.prefix,\n defaultValue: options.defaultValue ?? \"\",\n serialize: (v) => v,\n deserialize: (s) => s,\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindNumberValueNS(\n options: Omit<\n BindValueNSOptions<number>,\n \"serialize\" | \"deserialize\" | \"defaultValue\"\n > & {\n defaultValue?: number;\n },\n): BindValueNS<number> {\n return bindValueNS<number>({\n prefix: options.prefix,\n defaultValue: options.defaultValue ?? 0,\n serialize: (v) => String(v),\n deserialize: (s) => Number(s),\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindBooleanValueNS(\n options: Omit<\n BindValueNSOptions<boolean>,\n \"serialize\" | \"deserialize\" | \"defaultValue\"\n > & {\n defaultValue?: boolean;\n },\n): BindValueNS<boolean> {\n return bindValueNS<boolean>({\n prefix: options.prefix,\n defaultValue: options.defaultValue ?? false,\n serialize: (v) => String(v),\n deserialize: (s) => s === \"true\",\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindJSONValueNS<T>(\n options: Omit<BindValueNSOptions<T>, \"serialize\" | \"deserialize\">,\n): BindValueNS<T> {\n return bindValueNS<T>({\n prefix: options.prefix,\n defaultValue: options.defaultValue,\n serialize: (v) => JSON.stringify(v),\n deserialize: (s) => JSON.parse(s),\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n\nexport function bindEnumValueNS<E extends string | number>(\n options: Omit<BindValueNSOptions<E>, \"serialize\" | \"deserialize\">,\n): BindValueNS<E> {\n return bindValueNS<E>({\n prefix: options.prefix,\n defaultValue: options.defaultValue,\n serialize: (v) => String(v),\n deserialize: (s) =>\n (typeof options.defaultValue === \"number\" ? Number(s) : s) as E,\n storage: options.storage,\n version: options.version,\n migrate: options.migrate,\n });\n}\n"],"names":[],"mappings":"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"}
@@ -7,6 +7,15 @@ export interface BindValueOptions<T> {
7
7
  version?: string;
8
8
  migrate?: (oldSerialized: string, oldVersion: string | undefined) => T;
9
9
  }
10
+ export interface BindValueNSOptions<T> {
11
+ prefix: string;
12
+ defaultValue: T;
13
+ serialize: (value: T) => string;
14
+ deserialize: (serialized: string) => T;
15
+ storage?: Storage;
16
+ version?: string;
17
+ migrate?: (oldSerialized: string, oldVersion: string | undefined) => T;
18
+ }
10
19
  export declare class BindValue<T> {
11
20
  private options;
12
21
  private value;
@@ -16,8 +25,17 @@ export declare class BindValue<T> {
16
25
  getValue(): T;
17
26
  set(newValue: T): void;
18
27
  subscribe(callback: (value: T) => void): () => void;
28
+ getRaw(): string | null;
29
+ setRaw(rawValue: string): void;
19
30
  private loadFromStorage;
20
31
  private saveToStorage;
21
32
  private notifySubscribers;
22
33
  }
23
34
  export declare function bindValue<T>(options: BindValueOptions<T>): BindValue<T>;
35
+ export declare function _clearInMemoryStorage(): void;
36
+ export declare class BindValueNS<T> {
37
+ private options;
38
+ constructor(options: BindValueNSOptions<T>);
39
+ bind(key: string): BindValue<T>;
40
+ }
41
+ export declare function bindValueNS<T>(options: BindValueNSOptions<T>): BindValueNS<T>;
@@ -47,6 +47,48 @@ class BindValue {
47
47
  this.subscribers.delete(callback);
48
48
  };
49
49
  }
50
+ getRaw() {
51
+ return this.storage.getItem(this.options.key);
52
+ }
53
+ setRaw(rawValue) {
54
+ let deserializedValue;
55
+ const version = this.options.version;
56
+ let serializedPart;
57
+ if (version) {
58
+ if (!rawValue.startsWith("\0")) {
59
+ return;
60
+ }
61
+ const parts = rawValue.split("\0");
62
+ if (parts.length < 3) {
63
+ return;
64
+ }
65
+ const versionPart = parts[1];
66
+ if (versionPart !== version) {
67
+ return;
68
+ }
69
+ serializedPart = parts.slice(2).join("\0");
70
+ } else {
71
+ serializedPart = rawValue;
72
+ }
73
+ try {
74
+ deserializedValue = this.options.deserialize(serializedPart);
75
+ } catch {
76
+ return;
77
+ }
78
+ const serializedValue = this.options.serialize(deserializedValue);
79
+ let reconstructedRaw;
80
+ if (version) {
81
+ reconstructedRaw = `\0${version}\0${serializedValue}`;
82
+ } else {
83
+ reconstructedRaw = serializedValue;
84
+ }
85
+ if (reconstructedRaw !== rawValue) {
86
+ return;
87
+ }
88
+ this.storage.setItem(this.options.key, rawValue);
89
+ this.value = deserializedValue;
90
+ this.notifySubscribers();
91
+ }
50
92
  loadFromStorage() {
51
93
  const rawValue = this.storage.getItem(this.options.key);
52
94
  if (rawValue === null) {
@@ -114,8 +156,35 @@ class BindValue {
114
156
  function bindValue(options) {
115
157
  return new BindValue(options);
116
158
  }
159
+ class BindValueNS {
160
+ constructor(options) {
161
+ this.options = options;
162
+ if (!this.options.prefix || this.options.prefix.trim() === "") {
163
+ throw new Error("Prefix cannot be empty");
164
+ }
165
+ }
166
+ bind(key) {
167
+ if (!key || key.trim() === "") {
168
+ throw new Error("Key cannot be empty");
169
+ }
170
+ return new BindValue({
171
+ key: `${this.options.prefix}${key}`,
172
+ defaultValue: this.options.defaultValue,
173
+ serialize: this.options.serialize,
174
+ deserialize: this.options.deserialize,
175
+ storage: this.options.storage,
176
+ version: this.options.version,
177
+ migrate: this.options.migrate
178
+ });
179
+ }
180
+ }
181
+ function bindValueNS(options) {
182
+ return new BindValueNS(options);
183
+ }
117
184
  export {
118
185
  BindValue,
119
- bindValue
186
+ BindValueNS,
187
+ bindValue,
188
+ bindValueNS
120
189
  };
121
190
  //# sourceMappingURL=bindValue.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"bindValue.mjs","sources":["../../../../src/bindValue.ts"],"sourcesContent":["export interface BindValueOptions<T> {\n key: string;\n defaultValue: T;\n serialize: (value: T) => string;\n deserialize: (serialized: string) => T;\n storage?: Storage;\n version?: string;\n migrate?: (oldSerialized: string, oldVersion: string | undefined) => T;\n}\n\nconst memoryStorage = new Map<string, string>();\n\nconst inMemoryStorage: Storage = {\n get length() {\n return memoryStorage.size;\n },\n clear() {\n memoryStorage.clear();\n },\n getItem(key: string) {\n return memoryStorage.get(key) ?? null;\n },\n key(index: number) {\n const keys = Array.from(memoryStorage.keys());\n return keys[index] ?? null;\n },\n removeItem(key: string) {\n memoryStorage.delete(key);\n },\n setItem(key: string, value: string) {\n memoryStorage.set(key, value);\n },\n};\n\nexport class BindValue<T> {\n private value: T;\n private subscribers: Set<(value: T) => void>;\n private storage: Storage;\n\n constructor(private options: BindValueOptions<T>) {\n this.storage = options.storage ?? inMemoryStorage;\n this.subscribers = new Set();\n this.value = this.loadFromStorage();\n }\n\n getValue(): T {\n return this.value;\n }\n\n set(newValue: T): void {\n this.value = newValue;\n this.saveToStorage(newValue);\n this.notifySubscribers();\n }\n\n subscribe(callback: (value: T) => void): () => void {\n this.subscribers.add(callback);\n return () => {\n this.subscribers.delete(callback);\n };\n }\n\n private loadFromStorage(): T {\n const rawValue = this.storage.getItem(this.options.key);\n\n if (rawValue === null) {\n return this.options.defaultValue;\n }\n\n let oldVersion: string | undefined;\n let serializedValue: string;\n\n if (rawValue.startsWith(\"\\x00\")) {\n const parts = rawValue.split(\"\\x00\");\n if (parts.length >= 3) {\n oldVersion = parts[1];\n serializedValue = parts.slice(2).join(\"\\x00\");\n } else {\n serializedValue = rawValue;\n }\n } else {\n serializedValue = rawValue;\n }\n\n const currentVersion = this.options.version;\n\n if (oldVersion !== currentVersion) {\n if (this.options.migrate) {\n try {\n const migratedValue = this.options.migrate(\n serializedValue,\n oldVersion,\n );\n this.saveToStorage(migratedValue);\n return migratedValue;\n } catch {\n this.storage.removeItem(this.options.key);\n return this.options.defaultValue;\n }\n } else {\n this.storage.removeItem(this.options.key);\n return this.options.defaultValue;\n }\n }\n\n try {\n return this.options.deserialize(serializedValue);\n } catch {\n return this.options.defaultValue;\n }\n }\n\n private saveToStorage(value: T): void {\n try {\n const serialized = this.options.serialize(value);\n const version = this.options.version;\n\n if (version) {\n this.storage.setItem(\n this.options.key,\n `\\x00${version}\\x00${serialized}`,\n );\n } else {\n this.storage.setItem(this.options.key, serialized);\n }\n } catch {}\n }\n\n private notifySubscribers(): void {\n for (const subscriber of this.subscribers) {\n subscriber(this.value);\n }\n }\n}\n\nexport function bindValue<T>(options: BindValueOptions<T>): BindValue<T> {\n return new BindValue(options);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bindValue.mjs","sources":["../../../../src/bindValue.ts"],"sourcesContent":["export interface BindValueOptions<T> {\n key: string;\n defaultValue: T;\n serialize: (value: T) => string;\n deserialize: (serialized: string) => T;\n storage?: Storage;\n version?: string;\n migrate?: (oldSerialized: string, oldVersion: string | undefined) => T;\n}\n\nexport interface BindValueNSOptions<T> {\n prefix: string;\n defaultValue: T;\n serialize: (value: T) => string;\n deserialize: (serialized: string) => T;\n storage?: Storage;\n version?: string;\n migrate?: (oldSerialized: string, oldVersion: string | undefined) => T;\n}\n\nconst memoryStorage = new Map<string, string>();\n\nconst inMemoryStorage: Storage = {\n get length() {\n return memoryStorage.size;\n },\n clear() {\n memoryStorage.clear();\n },\n getItem(key: string) {\n return memoryStorage.get(key) ?? null;\n },\n key(index: number) {\n const keys = Array.from(memoryStorage.keys());\n return keys[index] ?? null;\n },\n removeItem(key: string) {\n memoryStorage.delete(key);\n },\n setItem(key: string, value: string) {\n memoryStorage.set(key, value);\n },\n};\n\nexport class BindValue<T> {\n private value: T;\n private subscribers: Set<(value: T) => void>;\n private storage: Storage;\n\n constructor(private options: BindValueOptions<T>) {\n this.storage = options.storage ?? inMemoryStorage;\n this.subscribers = new Set();\n this.value = this.loadFromStorage();\n }\n\n getValue(): T {\n return this.value;\n }\n\n set(newValue: T): void {\n this.value = newValue;\n this.saveToStorage(newValue);\n this.notifySubscribers();\n }\n\n subscribe(callback: (value: T) => void): () => void {\n this.subscribers.add(callback);\n return () => {\n this.subscribers.delete(callback);\n };\n }\n\n getRaw(): string | null {\n return this.storage.getItem(this.options.key);\n }\n\n setRaw(rawValue: string): void {\n let deserializedValue: T;\n const version = this.options.version;\n let serializedPart: string;\n\n if (version) {\n if (!rawValue.startsWith(\"\\x00\")) {\n return;\n }\n\n const parts = rawValue.split(\"\\x00\");\n if (parts.length < 3) {\n return;\n }\n\n const versionPart = parts[1];\n if (versionPart !== version) {\n return;\n }\n\n serializedPart = parts.slice(2).join(\"\\x00\");\n } else {\n serializedPart = rawValue;\n }\n\n try {\n deserializedValue = this.options.deserialize(serializedPart);\n } catch {\n return;\n }\n const serializedValue = this.options.serialize(deserializedValue);\n let reconstructedRaw: string;\n\n if (version) {\n reconstructedRaw = `\\x00${version}\\x00${serializedValue}`;\n } else {\n reconstructedRaw = serializedValue;\n }\n\n if (reconstructedRaw !== rawValue) {\n return;\n }\n\n this.storage.setItem(this.options.key, rawValue);\n this.value = deserializedValue;\n this.notifySubscribers();\n }\n\n private loadFromStorage(): T {\n const rawValue = this.storage.getItem(this.options.key);\n\n if (rawValue === null) {\n return this.options.defaultValue;\n }\n\n let oldVersion: string | undefined;\n let serializedValue: string;\n\n if (rawValue.startsWith(\"\\x00\")) {\n const parts = rawValue.split(\"\\x00\");\n if (parts.length >= 3) {\n oldVersion = parts[1];\n serializedValue = parts.slice(2).join(\"\\x00\");\n } else {\n serializedValue = rawValue;\n }\n } else {\n serializedValue = rawValue;\n }\n\n const currentVersion = this.options.version;\n\n if (oldVersion !== currentVersion) {\n if (this.options.migrate) {\n try {\n const migratedValue = this.options.migrate(\n serializedValue,\n oldVersion,\n );\n this.saveToStorage(migratedValue);\n return migratedValue;\n } catch {\n this.storage.removeItem(this.options.key);\n return this.options.defaultValue;\n }\n } else {\n this.storage.removeItem(this.options.key);\n return this.options.defaultValue;\n }\n }\n\n try {\n return this.options.deserialize(serializedValue);\n } catch {\n return this.options.defaultValue;\n }\n }\n\n private saveToStorage(value: T): void {\n try {\n const serialized = this.options.serialize(value);\n const version = this.options.version;\n\n if (version) {\n this.storage.setItem(\n this.options.key,\n `\\x00${version}\\x00${serialized}`,\n );\n } else {\n this.storage.setItem(this.options.key, serialized);\n }\n } catch {}\n }\n\n private notifySubscribers(): void {\n for (const subscriber of this.subscribers) {\n subscriber(this.value);\n }\n }\n}\n\nexport function bindValue<T>(options: BindValueOptions<T>): BindValue<T> {\n return new BindValue(options);\n}\n\nexport function _clearInMemoryStorage(): void {\n memoryStorage.clear();\n}\n\nexport class BindValueNS<T> {\n constructor(private options: BindValueNSOptions<T>) {\n if (!this.options.prefix || this.options.prefix.trim() === \"\") {\n throw new Error(\"Prefix cannot be empty\");\n }\n }\n\n bind(key: string): BindValue<T> {\n if (!key || key.trim() === \"\") {\n throw new Error(\"Key cannot be empty\");\n }\n\n return new BindValue<T>({\n key: `${this.options.prefix}\\x1F${key}`,\n defaultValue: this.options.defaultValue,\n serialize: this.options.serialize,\n deserialize: this.options.deserialize,\n storage: this.options.storage,\n version: this.options.version,\n migrate: this.options.migrate,\n });\n }\n}\n\nexport function bindValueNS<T>(options: BindValueNSOptions<T>): BindValueNS<T> {\n return new BindValueNS(options);\n}\n"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
1
  export { bindValue, BindValue, type BindValueOptions } from "./bindValue.js";
2
- export { useStorage, type UseStorageReturn } from "./useStorage.js";
3
- export { bindStringValue, bindNumberValue, bindBooleanValue, bindJSONValue, bindEnumValue, } from "./bindTypes.js";
2
+ export { bindValueNS, BindValueNS, type BindValueNSOptions, } from "./bindValue.js";
3
+ export { useStorage, useStorageNS, type UseStorageReturn, } from "./useStorage.js";
4
+ export { bindStringValue, bindNumberValue, bindBooleanValue, bindJSONValue, bindEnumValue, bindStringValueNS, bindNumberValueNS, bindBooleanValueNS, bindJSONValueNS, bindEnumValueNS, } from "./bindTypes.js";
@@ -1,14 +1,22 @@
1
- import { BindValue, bindValue } from "./bindValue.mjs";
2
- import { useStorage } from "./useStorage.mjs";
3
- import { bindBooleanValue, bindEnumValue, bindJSONValue, bindNumberValue, bindStringValue } from "./bindTypes.mjs";
1
+ import { BindValue, BindValueNS, bindValue, bindValueNS } from "./bindValue.mjs";
2
+ import { useStorage, useStorageNS } from "./useStorage.mjs";
3
+ import { bindBooleanValue, bindBooleanValueNS, bindEnumValue, bindEnumValueNS, bindJSONValue, bindJSONValueNS, bindNumberValue, bindNumberValueNS, bindStringValue, bindStringValueNS } from "./bindTypes.mjs";
4
4
  export {
5
5
  BindValue,
6
+ BindValueNS,
6
7
  bindBooleanValue,
8
+ bindBooleanValueNS,
7
9
  bindEnumValue,
10
+ bindEnumValueNS,
8
11
  bindJSONValue,
12
+ bindJSONValueNS,
9
13
  bindNumberValue,
14
+ bindNumberValueNS,
10
15
  bindStringValue,
16
+ bindStringValueNS,
11
17
  bindValue,
12
- useStorage
18
+ bindValueNS,
19
+ useStorage,
20
+ useStorageNS
13
21
  };
14
22
  //# sourceMappingURL=index.mjs.map
@@ -1,6 +1,9 @@
1
- import { type BindValue } from "./bindValue.js";
1
+ import { type BindValue, type BindValueNS } from "./bindValue.js";
2
2
  export interface UseStorageReturn<T> {
3
3
  value: T;
4
4
  setValue: (value: T) => void;
5
5
  }
6
6
  export declare function useStorage<T>(binding: BindValue<T>): UseStorageReturn<T>;
7
+ export declare function useStorageNS<T>(namespace: BindValueNS<T>, options: {
8
+ key: string;
9
+ }): UseStorageReturn<T>;
@@ -10,7 +10,12 @@ function useStorage(binding) {
10
10
  };
11
11
  return { value, setValue: set };
12
12
  }
13
+ function useStorageNS(namespace, options) {
14
+ const binding = namespace.bind(options.key);
15
+ return useStorage(binding);
16
+ }
13
17
  export {
14
- useStorage
18
+ useStorage,
19
+ useStorageNS
15
20
  };
16
21
  //# sourceMappingURL=useStorage.mjs.map
@@ -1 +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<T> {\n value: T;\n setValue: (value: T) => void;\n}\n\nexport function useStorage<T>(binding: BindValue<T>): UseStorageReturn<T> {\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: T) => {\n binding.set(newValue);\n };\n\n return { value, setValue: set };\n}\n"],"names":[],"mappings":";AAQO,SAAS,WAAc,SAA4C;AACxE,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,aAAgB;AAC3B,YAAQ,IAAI,QAAQ;AAAA,EACtB;AAEA,SAAO,EAAE,OAAO,UAAU,IAAA;AAC5B;"}
1
+ {"version":3,"file":"useStorage.mjs","sources":["../../../../src/useStorage.ts"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport { type BindValue, type BindValueNS } from \"./bindValue.js\";\n\nexport interface UseStorageReturn<T> {\n value: T;\n setValue: (value: T) => void;\n}\n\nexport function useStorage<T>(binding: BindValue<T>): UseStorageReturn<T> {\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: T) => {\n binding.set(newValue);\n };\n\n return { value, setValue: set };\n}\n\nexport function useStorageNS<T>(\n namespace: BindValueNS<T>,\n options: { key: string },\n): UseStorageReturn<T> {\n const binding = namespace.bind(options.key);\n return useStorage(binding);\n}\n"],"names":[],"mappings":";AAQO,SAAS,WAAc,SAA4C;AACxE,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,aAAgB;AAC3B,YAAQ,IAAI,QAAQ;AAAA,EACtB;AAEA,SAAO,EAAE,OAAO,UAAU,IAAA;AAC5B;AAEO,SAAS,aACd,WACA,SACqB;AACrB,QAAM,UAAU,UAAU,KAAK,QAAQ,GAAG;AAC1C,SAAO,WAAW,OAAO;AAC3B;"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kvozy",
3
3
  "type": "commonjs",
4
- "version": "0.5.0",
4
+ "version": "0.7.0",
5
5
  "types": "./__compiled__/cjs/src/index.d.ts",
6
6
  "module": "./__compiled__/esm/src/index.mjs",
7
7
  "main": "./__compiled__/cjs/src/index.js",