@kafitra/lynx-async-storage 0.1.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 +282 -0
- package/dist/index.cjs +348 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +226 -0
- package/dist/index.d.ts +224 -0
- package/dist/index.js +333 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# @kafitra/lynx-async-storage
|
|
2
|
+
|
|
3
|
+
Async key-value storage for the [Lynx](https://lynxjs.org) runtime — mirrors the React Native `AsyncStorage` API.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Drop-in API** – same method signatures as React Native `AsyncStorage`
|
|
8
|
+
- **Pure JS** – no native Android/iOS modules, no `prebuild` step
|
|
9
|
+
- **Thin async wrapper** – microtask-scheduled Promises over the synchronous Lynx runtime storage
|
|
10
|
+
- **JSON merge** – shallow object merge via `mergeItem` / `multiMerge`
|
|
11
|
+
- **Pluggable backend** – swap the storage adapter at runtime or in tests
|
|
12
|
+
- **Tree-shakeable** – `"sideEffects": false`, ESM + CJS dual output
|
|
13
|
+
- **Strict TypeScript** – full type safety, no implicit `any`
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm add @kafitra/lynx-async-storage
|
|
21
|
+
# or
|
|
22
|
+
npm install @kafitra/lynx-async-storage
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import AsyncStorage from "@kafitra/lynx-async-storage";
|
|
31
|
+
|
|
32
|
+
// Store a value
|
|
33
|
+
await AsyncStorage.setItem("token", "abc123");
|
|
34
|
+
|
|
35
|
+
// Retrieve a value (null if absent)
|
|
36
|
+
const token = await AsyncStorage.getItem("token");
|
|
37
|
+
console.log(token); // 'abc123'
|
|
38
|
+
|
|
39
|
+
// Remove a value
|
|
40
|
+
await AsyncStorage.removeItem("token");
|
|
41
|
+
|
|
42
|
+
// Clear everything
|
|
43
|
+
await AsyncStorage.clear();
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## API Reference
|
|
49
|
+
|
|
50
|
+
### Single-key methods
|
|
51
|
+
|
|
52
|
+
| Method | Signature | Description |
|
|
53
|
+
| ------------ | ----------------------------------------------- | ------------------------------------------ |
|
|
54
|
+
| `getItem` | `(key: string) => Promise<string \| null>` | Returns the value, or `null` if absent |
|
|
55
|
+
| `setItem` | `(key: string, value: string) => Promise<void>` | Stores a value |
|
|
56
|
+
| `removeItem` | `(key: string) => Promise<void>` | Removes a single key |
|
|
57
|
+
| `clear` | `() => Promise<void>` | Removes **all** keys |
|
|
58
|
+
| `getAllKeys` | `() => Promise<string[]>` | Returns all keys, sorted lexicographically |
|
|
59
|
+
|
|
60
|
+
### Batch methods
|
|
61
|
+
|
|
62
|
+
| Method | Signature | Description |
|
|
63
|
+
| ------------- | --------------------------------------------------------- | ----------------------------------------- |
|
|
64
|
+
| `multiGet` | `(keys: string[]) => Promise<[string, string \| null][]>` | Fetches multiple keys; order is preserved |
|
|
65
|
+
| `multiSet` | `(pairs: [string, string][]) => Promise<void>` | Stores multiple pairs atomically |
|
|
66
|
+
| `multiRemove` | `(keys: string[]) => Promise<void>` | Removes multiple keys |
|
|
67
|
+
|
|
68
|
+
### Merge methods
|
|
69
|
+
|
|
70
|
+
| Method | Signature | Description |
|
|
71
|
+
| ------------ | ----------------------------------------------- | -------------------------------------------------- |
|
|
72
|
+
| `mergeItem` | `(key: string, value: string) => Promise<void>` | Shallow-merges a JSON object into the stored value |
|
|
73
|
+
| `multiMerge` | `(pairs: [string, string][]) => Promise<void>` | Applies `mergeItem` for each pair |
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Usage Examples
|
|
78
|
+
|
|
79
|
+
### Storing structured data
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import AsyncStorage from "@kafitra/lynx-async-storage";
|
|
83
|
+
|
|
84
|
+
const user = { id: 1, name: "Alice", role: "admin" };
|
|
85
|
+
await AsyncStorage.setItem("user", JSON.stringify(user));
|
|
86
|
+
|
|
87
|
+
const raw = await AsyncStorage.getItem("user");
|
|
88
|
+
const parsed = raw ? JSON.parse(raw) : null;
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Batch read / write
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
await AsyncStorage.multiSet([
|
|
95
|
+
["firstName", "Alice"],
|
|
96
|
+
["lastName", "Smith"],
|
|
97
|
+
["city", "Oslo"],
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
const results = await AsyncStorage.multiGet(["firstName", "city"]);
|
|
101
|
+
// [['firstName', 'Alice'], ['city', 'Oslo']]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### JSON merge
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
// Initial state
|
|
108
|
+
await AsyncStorage.setItem(
|
|
109
|
+
"prefs",
|
|
110
|
+
JSON.stringify({ theme: "light", lang: "en" }),
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// Partial update – only theme changes; lang is preserved
|
|
114
|
+
await AsyncStorage.mergeItem("prefs", JSON.stringify({ theme: "dark" }));
|
|
115
|
+
|
|
116
|
+
const prefs = JSON.parse((await AsyncStorage.getItem("prefs")) ?? "{}");
|
|
117
|
+
// { theme: 'dark', lang: 'en' }
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### List all stored keys
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
await AsyncStorage.multiSet([
|
|
124
|
+
["b", "2"],
|
|
125
|
+
["a", "1"],
|
|
126
|
+
["c", "3"],
|
|
127
|
+
]);
|
|
128
|
+
const keys = await AsyncStorage.getAllKeys();
|
|
129
|
+
// ['a', 'b', 'c'] ← always sorted
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Custom / Test Backend
|
|
135
|
+
|
|
136
|
+
Inject any object that satisfies `StorageBackend` for testing or custom adapters:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { AsyncStorage, MemoryBackend } from "@kafitra/lynx-async-storage";
|
|
140
|
+
|
|
141
|
+
const storage = new AsyncStorage(new MemoryBackend());
|
|
142
|
+
|
|
143
|
+
// Or swap the singleton's backend:
|
|
144
|
+
import instance from "@kafitra/lynx-async-storage";
|
|
145
|
+
import { MemoryBackend } from "@kafitra/lynx-async-storage";
|
|
146
|
+
|
|
147
|
+
instance.useBackend(new MemoryBackend());
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Implement `StorageBackend` yourself:
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
import type { StorageBackend } from "@kafitra/lynx-async-storage";
|
|
154
|
+
|
|
155
|
+
class MyCustomBackend implements StorageBackend {
|
|
156
|
+
getItem(key: string): string | null {
|
|
157
|
+
/* … */
|
|
158
|
+
}
|
|
159
|
+
setItem(key: string, value: string): void {
|
|
160
|
+
/* … */
|
|
161
|
+
}
|
|
162
|
+
removeItem(key: string): void {
|
|
163
|
+
/* … */
|
|
164
|
+
}
|
|
165
|
+
clear(): void {
|
|
166
|
+
/* … */
|
|
167
|
+
}
|
|
168
|
+
getAllKeys(): string[] {
|
|
169
|
+
/* … */
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Error Handling
|
|
177
|
+
|
|
178
|
+
All methods return Promises. Errors are always delivered via rejection – no method throws synchronously.
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
import {
|
|
182
|
+
InvalidKeyError,
|
|
183
|
+
InvalidValueError,
|
|
184
|
+
InvalidJsonError,
|
|
185
|
+
} from "@kafitra/lynx-async-storage";
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
await AsyncStorage.setItem("key", "value");
|
|
189
|
+
} catch (err) {
|
|
190
|
+
if (err instanceof InvalidKeyError) {
|
|
191
|
+
/* bad key type */
|
|
192
|
+
}
|
|
193
|
+
if (err instanceof InvalidValueError) {
|
|
194
|
+
/* bad value type */
|
|
195
|
+
}
|
|
196
|
+
if (err instanceof InvalidJsonError) {
|
|
197
|
+
/* merge JSON error */
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Error types
|
|
203
|
+
|
|
204
|
+
| Class | Code | Cause |
|
|
205
|
+
| ------------------- | ----------------------------------- | ---------------------------- |
|
|
206
|
+
| `InvalidKeyError` | `ERR_ASYNC_STORAGE_INVALID_KEY` | Key is not a `string` |
|
|
207
|
+
| `InvalidValueError` | `ERR_ASYNC_STORAGE_INVALID_VALUE` | Value is not a `string` |
|
|
208
|
+
| `InvalidJsonError` | `ERR_ASYNC_STORAGE_INVALID_JSON` | Non-JSON-object during merge |
|
|
209
|
+
| `BackendError` | `ERR_ASYNC_STORAGE_BACKEND_FAILURE` | Underlying storage threw |
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Runtime Backend Detection
|
|
214
|
+
|
|
215
|
+
At startup `createDefaultBackend()` probes the runtime in priority order:
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
1. NativeModules.LynxStorage available? → NativeStorageBackend (disk, persists across restarts)
|
|
219
|
+
2. globalThis.localStorage available? → LocalStorageBackend (browser / WebView)
|
|
220
|
+
3. fallback → MemoryBackend (in-memory, lost on reload)
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
For disk-persistent storage in a Lynx app, install [`@kafitra/lynx-storage`](https://www.npmjs.com/package/@kafitra/lynx-storage) alongside:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
pnpm add @kafitra/lynx-storage @kafitra/lynx-async-storage
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
No extra configuration needed — `NativeModules.LynxStorage` is auto-detected at runtime.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Design Decisions
|
|
234
|
+
|
|
235
|
+
### Why microtask scheduling?
|
|
236
|
+
|
|
237
|
+
`Promise.resolve().then(work)` schedules `work` on the microtask queue. This
|
|
238
|
+
guarantees the public API is always async – callers can safely `await` without
|
|
239
|
+
relying on synchronous completion, even when the backend is synchronous.
|
|
240
|
+
|
|
241
|
+
### Why shallow merge?
|
|
242
|
+
|
|
243
|
+
React Native AsyncStorage specifies a shallow merge for `mergeItem`. Deep merge
|
|
244
|
+
would require a third-party library and introduces ambiguity around array
|
|
245
|
+
handling. Consumers who need deep merge can read-modify-write using `getItem` /
|
|
246
|
+
`setItem`.
|
|
247
|
+
|
|
248
|
+
### Why sorted `getAllKeys`?
|
|
249
|
+
|
|
250
|
+
Sorted output is deterministic and consistent across calls regardless of the
|
|
251
|
+
order in which keys were inserted – essential for reproducible application
|
|
252
|
+
behaviour.
|
|
253
|
+
|
|
254
|
+
### Why fail-fast validation in batch methods?
|
|
255
|
+
|
|
256
|
+
Batch operations (`multiSet`, `multiGet`, etc.) validate **all** inputs before
|
|
257
|
+
any backend write begins. This provides best-effort atomicity: either all writes
|
|
258
|
+
happen or none do, minimising partial-state corruption.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Development
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
# Install dependencies
|
|
266
|
+
pnpm install
|
|
267
|
+
|
|
268
|
+
# Build (ESM + CJS + types)
|
|
269
|
+
pnpm build
|
|
270
|
+
|
|
271
|
+
# Run tests
|
|
272
|
+
pnpm test
|
|
273
|
+
|
|
274
|
+
# Run tests with coverage
|
|
275
|
+
pnpm test:coverage
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## License
|
|
281
|
+
|
|
282
|
+
MIT © Kafitra
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
// src/errors.ts
|
|
6
|
+
var ErrorCodes = {
|
|
7
|
+
INVALID_KEY: "ERR_ASYNC_STORAGE_INVALID_KEY",
|
|
8
|
+
INVALID_VALUE: "ERR_ASYNC_STORAGE_INVALID_VALUE",
|
|
9
|
+
INVALID_JSON: "ERR_ASYNC_STORAGE_INVALID_JSON",
|
|
10
|
+
BACKEND_FAILURE: "ERR_ASYNC_STORAGE_BACKEND_FAILURE"
|
|
11
|
+
};
|
|
12
|
+
var AsyncStorageError = class extends Error {
|
|
13
|
+
constructor(code, message, cause) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = "AsyncStorageError";
|
|
16
|
+
this.code = code;
|
|
17
|
+
if (cause !== void 0) this.cause = cause;
|
|
18
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
var InvalidKeyError = class extends AsyncStorageError {
|
|
22
|
+
constructor(key) {
|
|
23
|
+
super(
|
|
24
|
+
ErrorCodes.INVALID_KEY,
|
|
25
|
+
`AsyncStorage key must be a string, got: ${typeof key}`
|
|
26
|
+
);
|
|
27
|
+
this.name = "InvalidKeyError";
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
var InvalidValueError = class extends AsyncStorageError {
|
|
31
|
+
constructor(value) {
|
|
32
|
+
super(
|
|
33
|
+
ErrorCodes.INVALID_VALUE,
|
|
34
|
+
`AsyncStorage value must be a string, got: ${typeof value}`
|
|
35
|
+
);
|
|
36
|
+
this.name = "InvalidValueError";
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
var InvalidJsonError = class extends AsyncStorageError {
|
|
40
|
+
constructor(context, cause) {
|
|
41
|
+
super(
|
|
42
|
+
ErrorCodes.INVALID_JSON,
|
|
43
|
+
`AsyncStorage mergeItem: ${context} value is not valid JSON`,
|
|
44
|
+
cause
|
|
45
|
+
);
|
|
46
|
+
this.name = "InvalidJsonError";
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var BackendError = class extends AsyncStorageError {
|
|
50
|
+
constructor(operation, cause) {
|
|
51
|
+
super(
|
|
52
|
+
ErrorCodes.BACKEND_FAILURE,
|
|
53
|
+
`AsyncStorage backend error during "${operation}"`,
|
|
54
|
+
cause
|
|
55
|
+
);
|
|
56
|
+
this.name = "BackendError";
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// src/storageBackend.ts
|
|
61
|
+
var MemoryBackend = class {
|
|
62
|
+
constructor() {
|
|
63
|
+
this.store = /* @__PURE__ */ new Map();
|
|
64
|
+
}
|
|
65
|
+
getItem(key) {
|
|
66
|
+
return this.store.has(key) ? this.store.get(key) : null;
|
|
67
|
+
}
|
|
68
|
+
setItem(key, value) {
|
|
69
|
+
this.store.set(key, value);
|
|
70
|
+
}
|
|
71
|
+
removeItem(key) {
|
|
72
|
+
this.store.delete(key);
|
|
73
|
+
}
|
|
74
|
+
clear() {
|
|
75
|
+
this.store.clear();
|
|
76
|
+
}
|
|
77
|
+
getAllKeys() {
|
|
78
|
+
return Array.from(this.store.keys());
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
var NativeStorageBackend = class {
|
|
82
|
+
constructor(native) {
|
|
83
|
+
this.native = native;
|
|
84
|
+
}
|
|
85
|
+
getItem(key) {
|
|
86
|
+
try {
|
|
87
|
+
return this.native.getString(key);
|
|
88
|
+
} catch (err) {
|
|
89
|
+
throw new BackendError("getItem", err);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
setItem(key, value) {
|
|
93
|
+
try {
|
|
94
|
+
this.native.setString(key, value);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
throw new BackendError("setItem", err);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
removeItem(key) {
|
|
100
|
+
try {
|
|
101
|
+
this.native.remove(key);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
throw new BackendError("removeItem", err);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
clear() {
|
|
107
|
+
try {
|
|
108
|
+
this.native.clear();
|
|
109
|
+
} catch (err) {
|
|
110
|
+
throw new BackendError("clear", err);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
getAllKeys() {
|
|
114
|
+
try {
|
|
115
|
+
const raw = this.native.getAllKeys();
|
|
116
|
+
return JSON.parse(raw);
|
|
117
|
+
} catch (err) {
|
|
118
|
+
throw new BackendError("getAllKeys", err);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
var LocalStorageBackend = class {
|
|
123
|
+
constructor(ls) {
|
|
124
|
+
this.ls = ls;
|
|
125
|
+
}
|
|
126
|
+
getItem(key) {
|
|
127
|
+
try {
|
|
128
|
+
return this.ls.getItem(key);
|
|
129
|
+
} catch (err) {
|
|
130
|
+
throw new BackendError("getItem", err);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
setItem(key, value) {
|
|
134
|
+
try {
|
|
135
|
+
this.ls.setItem(key, value);
|
|
136
|
+
} catch (err) {
|
|
137
|
+
throw new BackendError("setItem", err);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
removeItem(key) {
|
|
141
|
+
try {
|
|
142
|
+
this.ls.removeItem(key);
|
|
143
|
+
} catch (err) {
|
|
144
|
+
throw new BackendError("removeItem", err);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
clear() {
|
|
148
|
+
try {
|
|
149
|
+
this.ls.clear();
|
|
150
|
+
} catch (err) {
|
|
151
|
+
throw new BackendError("clear", err);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
getAllKeys() {
|
|
155
|
+
try {
|
|
156
|
+
const keys = [];
|
|
157
|
+
for (let i = 0; i < this.ls.length; i++) {
|
|
158
|
+
const k = this.ls.key(i);
|
|
159
|
+
if (k !== null) keys.push(k);
|
|
160
|
+
}
|
|
161
|
+
return keys;
|
|
162
|
+
} catch (err) {
|
|
163
|
+
throw new BackendError("getAllKeys", err);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
function isNativeStorageAvailable(candidate) {
|
|
168
|
+
if (candidate === null || candidate === void 0 || typeof candidate !== "object") {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
const obj = candidate;
|
|
172
|
+
return typeof obj["getString"] === "function" && typeof obj["setString"] === "function" && typeof obj["remove"] === "function" && typeof obj["clear"] === "function" && typeof obj["getAllKeys"] === "function";
|
|
173
|
+
}
|
|
174
|
+
function isStorageAvailable(candidate) {
|
|
175
|
+
if (candidate === null || candidate === void 0 || typeof candidate !== "object") {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
const obj = candidate;
|
|
179
|
+
return typeof obj["getItem"] === "function" && typeof obj["setItem"] === "function" && typeof obj["removeItem"] === "function" && typeof obj["clear"] === "function" && typeof obj["key"] === "function" && typeof obj["length"] === "number";
|
|
180
|
+
}
|
|
181
|
+
function createDefaultBackend() {
|
|
182
|
+
const g = globalThis;
|
|
183
|
+
try {
|
|
184
|
+
const nm = (
|
|
185
|
+
// eslint-disable-next-line no-undef
|
|
186
|
+
typeof NativeModules !== "undefined" ? NativeModules : g["NativeModules"]
|
|
187
|
+
);
|
|
188
|
+
if (nm && isNativeStorageAvailable(nm.LynxStorage)) {
|
|
189
|
+
return new NativeStorageBackend(nm.LynxStorage);
|
|
190
|
+
}
|
|
191
|
+
} catch {
|
|
192
|
+
}
|
|
193
|
+
if (isStorageAvailable(g["localStorage"])) {
|
|
194
|
+
return new LocalStorageBackend(g["localStorage"]);
|
|
195
|
+
}
|
|
196
|
+
return new MemoryBackend();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// src/AsyncStorage.ts
|
|
200
|
+
function assertKey(key) {
|
|
201
|
+
if (typeof key !== "string") {
|
|
202
|
+
throw new InvalidKeyError(key);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function assertValue(value) {
|
|
206
|
+
if (typeof value !== "string") {
|
|
207
|
+
throw new InvalidValueError(value);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function shallowMergeJson(existingRaw, incomingRaw) {
|
|
211
|
+
const baseString = existingRaw ?? "{}";
|
|
212
|
+
let base;
|
|
213
|
+
let patch;
|
|
214
|
+
try {
|
|
215
|
+
base = JSON.parse(baseString);
|
|
216
|
+
} catch (err) {
|
|
217
|
+
throw new InvalidJsonError("existing", err);
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
patch = JSON.parse(incomingRaw);
|
|
221
|
+
} catch (err) {
|
|
222
|
+
throw new InvalidJsonError("incoming", err);
|
|
223
|
+
}
|
|
224
|
+
if (base === null || typeof base !== "object" || Array.isArray(base)) {
|
|
225
|
+
throw new InvalidJsonError("existing");
|
|
226
|
+
}
|
|
227
|
+
if (patch === null || typeof patch !== "object" || Array.isArray(patch)) {
|
|
228
|
+
throw new InvalidJsonError("incoming");
|
|
229
|
+
}
|
|
230
|
+
const merged = {
|
|
231
|
+
...base,
|
|
232
|
+
...patch
|
|
233
|
+
};
|
|
234
|
+
return JSON.stringify(merged);
|
|
235
|
+
}
|
|
236
|
+
var AsyncStorage = class {
|
|
237
|
+
constructor(backend) {
|
|
238
|
+
this.backend = backend ?? createDefaultBackend();
|
|
239
|
+
}
|
|
240
|
+
// ── Backend injection ────────────────────────────────────────────────────
|
|
241
|
+
useBackend(backend) {
|
|
242
|
+
this.backend = backend;
|
|
243
|
+
}
|
|
244
|
+
// ── Single-key API ───────────────────────────────────────────────────────
|
|
245
|
+
getItem(key) {
|
|
246
|
+
return Promise.resolve().then(() => {
|
|
247
|
+
assertKey(key);
|
|
248
|
+
return this.backend.getItem(key);
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
setItem(key, value) {
|
|
252
|
+
return Promise.resolve().then(() => {
|
|
253
|
+
assertKey(key);
|
|
254
|
+
assertValue(value);
|
|
255
|
+
this.backend.setItem(key, value);
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
removeItem(key) {
|
|
259
|
+
return Promise.resolve().then(() => {
|
|
260
|
+
assertKey(key);
|
|
261
|
+
this.backend.removeItem(key);
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
clear() {
|
|
265
|
+
return Promise.resolve().then(() => {
|
|
266
|
+
this.backend.clear();
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
getAllKeys() {
|
|
270
|
+
return Promise.resolve().then(() => {
|
|
271
|
+
return this.backend.getAllKeys().slice().sort();
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
// ── Batch API ────────────────────────────────────────────────────────────
|
|
275
|
+
multiGet(keys) {
|
|
276
|
+
return Promise.resolve().then(() => {
|
|
277
|
+
for (const key of keys) {
|
|
278
|
+
assertKey(key);
|
|
279
|
+
}
|
|
280
|
+
return keys.map(
|
|
281
|
+
(key) => [key, this.backend.getItem(key)]
|
|
282
|
+
);
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
multiSet(keyValuePairs) {
|
|
286
|
+
return Promise.resolve().then(() => {
|
|
287
|
+
for (const [key, value] of keyValuePairs) {
|
|
288
|
+
assertKey(key);
|
|
289
|
+
assertValue(value);
|
|
290
|
+
}
|
|
291
|
+
for (const [key, value] of keyValuePairs) {
|
|
292
|
+
this.backend.setItem(key, value);
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
multiRemove(keys) {
|
|
297
|
+
return Promise.resolve().then(() => {
|
|
298
|
+
for (const key of keys) {
|
|
299
|
+
assertKey(key);
|
|
300
|
+
}
|
|
301
|
+
for (const key of keys) {
|
|
302
|
+
this.backend.removeItem(key);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
// ── Merge API ────────────────────────────────────────────────────────────
|
|
307
|
+
mergeItem(key, value) {
|
|
308
|
+
return Promise.resolve().then(() => {
|
|
309
|
+
assertKey(key);
|
|
310
|
+
assertValue(value);
|
|
311
|
+
const existing = this.backend.getItem(key);
|
|
312
|
+
const merged = shallowMergeJson(existing, value);
|
|
313
|
+
this.backend.setItem(key, merged);
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
multiMerge(keyValuePairs) {
|
|
317
|
+
return Promise.resolve().then(() => {
|
|
318
|
+
for (const [key, value] of keyValuePairs) {
|
|
319
|
+
assertKey(key);
|
|
320
|
+
assertValue(value);
|
|
321
|
+
}
|
|
322
|
+
for (const [key, value] of keyValuePairs) {
|
|
323
|
+
const existing = this.backend.getItem(key);
|
|
324
|
+
const merged = shallowMergeJson(existing, value);
|
|
325
|
+
this.backend.setItem(key, merged);
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
// src/index.ts
|
|
332
|
+
var instance = new AsyncStorage();
|
|
333
|
+
var index_default = instance;
|
|
334
|
+
|
|
335
|
+
exports.AsyncStorage = AsyncStorage;
|
|
336
|
+
exports.AsyncStorageError = AsyncStorageError;
|
|
337
|
+
exports.BackendError = BackendError;
|
|
338
|
+
exports.ErrorCodes = ErrorCodes;
|
|
339
|
+
exports.InvalidJsonError = InvalidJsonError;
|
|
340
|
+
exports.InvalidKeyError = InvalidKeyError;
|
|
341
|
+
exports.InvalidValueError = InvalidValueError;
|
|
342
|
+
exports.LocalStorageBackend = LocalStorageBackend;
|
|
343
|
+
exports.MemoryBackend = MemoryBackend;
|
|
344
|
+
exports.NativeStorageBackend = NativeStorageBackend;
|
|
345
|
+
exports.createDefaultBackend = createDefaultBackend;
|
|
346
|
+
exports.default = index_default;
|
|
347
|
+
//# sourceMappingURL=index.cjs.map
|
|
348
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/storageBackend.ts","../src/AsyncStorage.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * @kafitra/lynx-async-storage\r\n *\r\n * Typed error classes used across the module.\r\n * All errors extend the built-in Error so they are instanceof-compatible.\r\n */\r\n\r\n// ─── Error codes ──────────────────────────────────────────────────────────────\r\n\r\nexport const ErrorCodes = {\r\n INVALID_KEY: \"ERR_ASYNC_STORAGE_INVALID_KEY\",\r\n INVALID_VALUE: \"ERR_ASYNC_STORAGE_INVALID_VALUE\",\r\n INVALID_JSON: \"ERR_ASYNC_STORAGE_INVALID_JSON\",\r\n BACKEND_FAILURE: \"ERR_ASYNC_STORAGE_BACKEND_FAILURE\",\r\n} as const;\r\n\r\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\r\n\r\n// ─── Base error ───────────────────────────────────────────────────────────────\r\n\r\nexport class AsyncStorageError extends Error {\r\n public readonly code: ErrorCode;\r\n public readonly cause?: unknown;\r\n\r\n constructor(code: ErrorCode, message: string, cause?: unknown) {\r\n super(message);\r\n this.name = \"AsyncStorageError\";\r\n this.code = code;\r\n if (cause !== undefined) this.cause = cause;\r\n\r\n // Maintain proper prototype chain for instanceof checks in transpiled code.\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n }\r\n}\r\n\r\n// ─── Specific error subtypes ──────────────────────────────────────────────────\r\n\r\n/** Thrown when a key is not a non-empty string. */\r\nexport class InvalidKeyError extends AsyncStorageError {\r\n constructor(key: unknown) {\r\n super(\r\n ErrorCodes.INVALID_KEY,\r\n `AsyncStorage key must be a string, got: ${typeof key}`,\r\n );\r\n this.name = \"InvalidKeyError\";\r\n }\r\n}\r\n\r\n/** Thrown when a value is not a string. */\r\nexport class InvalidValueError extends AsyncStorageError {\r\n constructor(value: unknown) {\r\n super(\r\n ErrorCodes.INVALID_VALUE,\r\n `AsyncStorage value must be a string, got: ${typeof value}`,\r\n );\r\n this.name = \"InvalidValueError\";\r\n }\r\n}\r\n\r\n/** Thrown when JSON parsing fails during a merge operation. */\r\nexport class InvalidJsonError extends AsyncStorageError {\r\n constructor(context: \"existing\" | \"incoming\", cause?: unknown) {\r\n super(\r\n ErrorCodes.INVALID_JSON,\r\n `AsyncStorage mergeItem: ${context} value is not valid JSON`,\r\n cause,\r\n );\r\n this.name = \"InvalidJsonError\";\r\n }\r\n}\r\n\r\n/** Thrown when the underlying storage backend raises an exception. */\r\nexport class BackendError extends AsyncStorageError {\r\n constructor(operation: string, cause?: unknown) {\r\n super(\r\n ErrorCodes.BACKEND_FAILURE,\r\n `AsyncStorage backend error during \"${operation}\"`,\r\n cause,\r\n );\r\n this.name = \"BackendError\";\r\n }\r\n}\r\n","/**\r\n * @kafitra/lynx-async-storage\r\n *\r\n * StorageBackend implementations.\r\n *\r\n * Resolution order (createDefaultBackend):\r\n * 1. NativeStorageBackend — NativeModules.LynxStorage (disk-backed, persists across restarts)\r\n * 2. LocalStorageBackend — globalThis.localStorage (browser / some WebView runtimes)\r\n * 3. MemoryBackend — in-memory Map fallback (tests / unknown environments)\r\n *\r\n * Swap the backend at any time via AsyncStorage.useBackend().\r\n */\r\n\r\nimport type { StorageBackend } from \"./types\";\r\nimport { BackendError } from \"./errors\";\r\n\r\n// ─── Lynx NativeModules shape (minimal) ──────────────────────────────────────\r\n\r\ninterface LynxNativeStorage {\r\n getString(key: string): string | null;\r\n setString(key: string, value: string): void;\r\n remove(key: string): void;\r\n clear(): void;\r\n /** Returns a JSON array string, e.g. '[\"a\",\"b\"]' */\r\n getAllKeys(): string;\r\n}\r\n\r\ninterface LynxNativeModules {\r\n LynxStorage?: LynxNativeStorage;\r\n}\r\n\r\n/**\r\n * Lynx injects NativeModules as a top-level global (not via globalThis).\r\n * We declare it here so TypeScript is happy; the typeof guard prevents\r\n * ReferenceError in environments where it doesn't exist (tests, browser).\r\n */\r\ndeclare const NativeModules: LynxNativeModules | undefined;\r\n\r\n// ─── In-memory backend ────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Simple Map-backed store.\r\n *\r\n * Used as the built-in fallback when no runtime storage is detected, and as a\r\n * convenient injectable backend for unit tests.\r\n */\r\nexport class MemoryBackend implements StorageBackend {\r\n private readonly store = new Map<string, string>();\r\n\r\n getItem(key: string): string | null {\r\n return this.store.has(key) ? (this.store.get(key) as string) : null;\r\n }\r\n\r\n setItem(key: string, value: string): void {\r\n this.store.set(key, value);\r\n }\r\n\r\n removeItem(key: string): void {\r\n this.store.delete(key);\r\n }\r\n\r\n clear(): void {\r\n this.store.clear();\r\n }\r\n\r\n getAllKeys(): string[] {\r\n return Array.from(this.store.keys());\r\n }\r\n}\r\n\r\n// ─── Lynx NativeModules backend ───────────────────────────────────────────────\r\n\r\n/**\r\n * Wraps the `NativeModules.LynxStorage` Lynx native module.\r\n *\r\n * This backend provides **disk-persistent** storage that survives app restarts,\r\n * backed by Android SharedPreferences and iOS NSUserDefaults.\r\n *\r\n * Requires `@kafitra/lynx-storage` to be installed and registered in the\r\n * host Android/iOS app:\r\n *\r\n * Android: LynxEnv.inst().registerModule(\"LynxStorage\", LynxStorageModule.class)\r\n * iOS: [globalConfig registerModule:LynxStorageModule.class]\r\n */\r\nexport class NativeStorageBackend implements StorageBackend {\r\n constructor(private readonly native: LynxNativeStorage) {}\r\n\r\n getItem(key: string): string | null {\r\n try {\r\n return this.native.getString(key);\r\n } catch (err) {\r\n throw new BackendError(\"getItem\", err);\r\n }\r\n }\r\n\r\n setItem(key: string, value: string): void {\r\n try {\r\n this.native.setString(key, value);\r\n } catch (err) {\r\n throw new BackendError(\"setItem\", err);\r\n }\r\n }\r\n\r\n removeItem(key: string): void {\r\n try {\r\n this.native.remove(key);\r\n } catch (err) {\r\n throw new BackendError(\"removeItem\", err);\r\n }\r\n }\r\n\r\n clear(): void {\r\n try {\r\n this.native.clear();\r\n } catch (err) {\r\n throw new BackendError(\"clear\", err);\r\n }\r\n }\r\n\r\n getAllKeys(): string[] {\r\n try {\r\n const raw = this.native.getAllKeys();\r\n return JSON.parse(raw) as string[];\r\n } catch (err) {\r\n throw new BackendError(\"getAllKeys\", err);\r\n }\r\n }\r\n}\r\n\r\n// ─── localStorage-compatible backend ─────────────────────────────────────────\r\n\r\n/**\r\n * Wraps any object that matches the Web Storage / Lynx localStorage API.\r\n */\r\nexport class LocalStorageBackend implements StorageBackend {\r\n constructor(private readonly ls: Storage) {}\r\n\r\n getItem(key: string): string | null {\r\n try {\r\n return this.ls.getItem(key);\r\n } catch (err) {\r\n throw new BackendError(\"getItem\", err);\r\n }\r\n }\r\n\r\n setItem(key: string, value: string): void {\r\n try {\r\n this.ls.setItem(key, value);\r\n } catch (err) {\r\n throw new BackendError(\"setItem\", err);\r\n }\r\n }\r\n\r\n removeItem(key: string): void {\r\n try {\r\n this.ls.removeItem(key);\r\n } catch (err) {\r\n throw new BackendError(\"removeItem\", err);\r\n }\r\n }\r\n\r\n clear(): void {\r\n try {\r\n this.ls.clear();\r\n } catch (err) {\r\n throw new BackendError(\"clear\", err);\r\n }\r\n }\r\n\r\n getAllKeys(): string[] {\r\n try {\r\n const keys: string[] = [];\r\n for (let i = 0; i < this.ls.length; i++) {\r\n const k = this.ls.key(i);\r\n if (k !== null) keys.push(k);\r\n }\r\n return keys;\r\n } catch (err) {\r\n throw new BackendError(\"getAllKeys\", err);\r\n }\r\n }\r\n}\r\n\r\n// ─── Runtime detection ────────────────────────────────────────────────────────\r\n\r\nfunction isNativeStorageAvailable(\r\n candidate: unknown,\r\n): candidate is LynxNativeStorage {\r\n if (\r\n candidate === null ||\r\n candidate === undefined ||\r\n typeof candidate !== \"object\"\r\n ) {\r\n return false;\r\n }\r\n const obj = candidate as Record<string, unknown>;\r\n return (\r\n typeof obj[\"getString\"] === \"function\" &&\r\n typeof obj[\"setString\"] === \"function\" &&\r\n typeof obj[\"remove\"] === \"function\" &&\r\n typeof obj[\"clear\"] === \"function\" &&\r\n typeof obj[\"getAllKeys\"] === \"function\"\r\n );\r\n}\r\n\r\nfunction isStorageAvailable(candidate: unknown): candidate is Storage {\r\n if (\r\n candidate === null ||\r\n candidate === undefined ||\r\n typeof candidate !== \"object\"\r\n ) {\r\n return false;\r\n }\r\n\r\n const obj = candidate as Record<string, unknown>;\r\n\r\n return (\r\n typeof obj[\"getItem\"] === \"function\" &&\r\n typeof obj[\"setItem\"] === \"function\" &&\r\n typeof obj[\"removeItem\"] === \"function\" &&\r\n typeof obj[\"clear\"] === \"function\" &&\r\n typeof obj[\"key\"] === \"function\" &&\r\n typeof obj[\"length\"] === \"number\"\r\n );\r\n}\r\n\r\n/**\r\n * Creates and returns the best available backend for the current runtime.\r\n *\r\n * Resolution order:\r\n * 1. NativeModules.LynxStorage (Lynx runtime with @kafitra/lynx-storage linked)\r\n * 2. globalThis.localStorage (browser / jsdom)\r\n * 3. MemoryBackend (fallback)\r\n */\r\nexport function createDefaultBackend(): StorageBackend {\r\n const g = globalThis as Record<string, unknown>;\r\n\r\n // 1. Prefer the Lynx native module — disk-persistent across app restarts\r\n // NativeModules is injected as a top-level global by the Lynx runtime,\r\n // so we check both the declared global and globalThis as a fallback.\r\n try {\r\n const nm: LynxNativeModules | undefined =\r\n // eslint-disable-next-line no-undef\r\n typeof NativeModules !== \"undefined\"\r\n ? NativeModules\r\n : (g[\"NativeModules\"] as LynxNativeModules | undefined);\r\n if (nm && isNativeStorageAvailable(nm.LynxStorage)) {\r\n return new NativeStorageBackend(nm.LynxStorage);\r\n }\r\n } catch {\r\n // NativeModules not available in this runtime\r\n }\r\n\r\n // 2. Fall back to localStorage (browser / WebView runtimes)\r\n if (isStorageAvailable(g[\"localStorage\"])) {\r\n return new LocalStorageBackend(g[\"localStorage\"] as Storage);\r\n }\r\n\r\n // 3. In-memory fallback — data lost on reload, but never crashes\r\n return new MemoryBackend();\r\n}\r\n","/**\r\n * @kafitra/lynx-async-storage\r\n *\r\n * Core AsyncStorage implementation.\r\n *\r\n * Design notes\r\n * ────────────\r\n * • Every public method wraps its synchronous backend call inside a microtask\r\n * (Promise.resolve().then(...)) so callers always receive a Promise, even when\r\n * the underlying operation completes instantaneously.\r\n *\r\n * • Validation (key/value type checks) is performed *inside* the microtask so\r\n * that no method ever throws synchronously.\r\n *\r\n * • mergeItem implements a shallow JSON Object merge as specified: parse both\r\n * values, spread-merge, stringify, and persist.\r\n *\r\n * • getAllKeys returns keys sorted lexicographically for deterministic output.\r\n *\r\n * • multiGet preserves the order of the input `keys` array.\r\n */\r\n\r\nimport type {\r\n AsyncStorageInterface,\r\n KeyValuePair,\r\n KeyValueResult,\r\n StorageBackend,\r\n} from \"./types\";\r\nimport { InvalidKeyError, InvalidValueError, InvalidJsonError } from \"./errors\";\r\nimport { createDefaultBackend } from \"./storageBackend\";\r\n\r\n// ─── Validation helpers ───────────────────────────────────────────────────────\r\n\r\nfunction assertKey(key: unknown): asserts key is string {\r\n if (typeof key !== \"string\") {\r\n throw new InvalidKeyError(key);\r\n }\r\n}\r\n\r\nfunction assertValue(value: unknown): asserts value is string {\r\n if (typeof value !== \"string\") {\r\n throw new InvalidValueError(value);\r\n }\r\n}\r\n\r\n// ─── Merge helper ─────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Performs a shallow merge of two JSON-encoded object strings.\r\n *\r\n * @param existingRaw Current value stored (may be null).\r\n * @param incomingRaw New value to merge in.\r\n * @returns JSON-stringified merged result.\r\n * @throws InvalidJsonError if either value is not a JSON object.\r\n */\r\nfunction shallowMergeJson(\r\n existingRaw: string | null,\r\n incomingRaw: string,\r\n): string {\r\n // Treat null as an empty object – consistent with RN behaviour.\r\n const baseString = existingRaw ?? \"{}\";\r\n\r\n let base: unknown;\r\n let patch: unknown;\r\n\r\n try {\r\n base = JSON.parse(baseString);\r\n } catch (err) {\r\n throw new InvalidJsonError(\"existing\", err);\r\n }\r\n\r\n try {\r\n patch = JSON.parse(incomingRaw);\r\n } catch (err) {\r\n throw new InvalidJsonError(\"incoming\", err);\r\n }\r\n\r\n if (base === null || typeof base !== \"object\" || Array.isArray(base)) {\r\n throw new InvalidJsonError(\"existing\");\r\n }\r\n\r\n if (patch === null || typeof patch !== \"object\" || Array.isArray(patch)) {\r\n throw new InvalidJsonError(\"incoming\");\r\n }\r\n\r\n const merged = {\r\n ...(base as Record<string, unknown>),\r\n ...(patch as Record<string, unknown>),\r\n };\r\n\r\n return JSON.stringify(merged);\r\n}\r\n\r\n// ─── AsyncStorage class ───────────────────────────────────────────────────────\r\n\r\nexport class AsyncStorage implements AsyncStorageInterface {\r\n private backend: StorageBackend;\r\n\r\n constructor(backend?: StorageBackend) {\r\n this.backend = backend ?? createDefaultBackend();\r\n }\r\n\r\n // ── Backend injection ────────────────────────────────────────────────────\r\n useBackend(backend: StorageBackend): void {\r\n this.backend = backend;\r\n }\r\n\r\n // ── Single-key API ───────────────────────────────────────────────────────\r\n\r\n getItem(key: string): Promise<string | null> {\r\n return Promise.resolve().then(() => {\r\n assertKey(key);\r\n return this.backend.getItem(key);\r\n });\r\n }\r\n\r\n setItem(key: string, value: string): Promise<void> {\r\n return Promise.resolve().then(() => {\r\n assertKey(key);\r\n assertValue(value);\r\n this.backend.setItem(key, value);\r\n });\r\n }\r\n\r\n removeItem(key: string): Promise<void> {\r\n return Promise.resolve().then(() => {\r\n assertKey(key);\r\n this.backend.removeItem(key);\r\n });\r\n }\r\n\r\n clear(): Promise<void> {\r\n return Promise.resolve().then(() => {\r\n this.backend.clear();\r\n });\r\n }\r\n\r\n getAllKeys(): Promise<string[]> {\r\n return Promise.resolve().then(() => {\r\n return this.backend.getAllKeys().slice().sort();\r\n });\r\n }\r\n\r\n // ── Batch API ────────────────────────────────────────────────────────────\r\n\r\n multiGet(keys: readonly string[]): Promise<KeyValueResult[]> {\r\n return Promise.resolve().then(() => {\r\n // Validate every key before touching the backend (fail-fast, all-or-nothing).\r\n for (const key of keys) {\r\n assertKey(key);\r\n }\r\n\r\n return keys.map(\r\n (key): KeyValueResult => [key, this.backend.getItem(key)],\r\n );\r\n });\r\n }\r\n\r\n multiSet(keyValuePairs: readonly KeyValuePair[]): Promise<void> {\r\n return Promise.resolve().then(() => {\r\n // Validate all pairs first (best-effort atomic: validate then write).\r\n for (const [key, value] of keyValuePairs) {\r\n assertKey(key);\r\n assertValue(value);\r\n }\r\n\r\n for (const [key, value] of keyValuePairs) {\r\n this.backend.setItem(key, value);\r\n }\r\n });\r\n }\r\n\r\n multiRemove(keys: readonly string[]): Promise<void> {\r\n return Promise.resolve().then(() => {\r\n for (const key of keys) {\r\n assertKey(key);\r\n }\r\n\r\n for (const key of keys) {\r\n this.backend.removeItem(key);\r\n }\r\n });\r\n }\r\n\r\n // ── Merge API ────────────────────────────────────────────────────────────\r\n\r\n mergeItem(key: string, value: string): Promise<void> {\r\n return Promise.resolve().then(() => {\r\n assertKey(key);\r\n assertValue(value);\r\n\r\n const existing = this.backend.getItem(key);\r\n const merged = shallowMergeJson(existing, value);\r\n this.backend.setItem(key, merged);\r\n });\r\n }\r\n\r\n multiMerge(keyValuePairs: readonly KeyValuePair[]): Promise<void> {\r\n return Promise.resolve().then(() => {\r\n // Validate inputs before any writes.\r\n for (const [key, value] of keyValuePairs) {\r\n assertKey(key);\r\n assertValue(value);\r\n }\r\n\r\n for (const [key, value] of keyValuePairs) {\r\n const existing = this.backend.getItem(key);\r\n const merged = shallowMergeJson(existing, value);\r\n this.backend.setItem(key, merged);\r\n }\r\n });\r\n }\r\n}\r\n","/**\r\n * @kafitra/lynx-async-storage\r\n *\r\n * Public barrel export.\r\n *\r\n * Default export: shared singleton (mirrors React Native AsyncStorage usage).\r\n * Named exports: class and types for consumers who need custom instances.\r\n */\r\n\r\nexport { AsyncStorage } from \"./AsyncStorage\";\r\nexport {\r\n MemoryBackend,\r\n LocalStorageBackend,\r\n NativeStorageBackend,\r\n createDefaultBackend,\r\n} from \"./storageBackend\";\r\nexport {\r\n AsyncStorageError,\r\n InvalidKeyError,\r\n InvalidValueError,\r\n InvalidJsonError,\r\n BackendError,\r\n ErrorCodes,\r\n} from \"./errors\";\r\nexport type {\r\n AsyncStorageInterface,\r\n StorageBackend,\r\n KeyValuePair,\r\n KeyValueResult,\r\n} from \"./types\";\r\n\r\nimport { AsyncStorage } from \"./AsyncStorage\";\r\n\r\n/**\r\n * Shared singleton instance – drop-in replacement for React Native AsyncStorage.\r\n *\r\n * @example\r\n * ```ts\r\n * import AsyncStorage from '@kafitra/lynx-async-storage';\r\n *\r\n * await AsyncStorage.setItem('token', 'abc123');\r\n * const token = await AsyncStorage.getItem('token'); // 'abc123'\r\n * ```\r\n */\r\nconst instance = new AsyncStorage();\r\nexport default instance;\r\n"]}
|