react-mnemonic 0.1.1-alpha.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/LICENSE.md +21 -0
- package/README.md +497 -0
- package/dist/index.cjs +996 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1151 -0
- package/dist/index.d.ts +1151 -0
- package/dist/index.js +987 -0
- package/dist/index.js.map +1 -0
- package/package.json +80 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright Scott Dixon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
# react-mnemonic
|
|
2
|
+
|
|
3
|
+
Persistent, type-safe state management for React.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/react-mnemonic)
|
|
6
|
+
[](https://bundlephobia.com/package/react-mnemonic)
|
|
7
|
+
[](./LICENSE.md)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
|
|
10
|
+
> **⚠️ Alpha Software** — This library is in active development. APIs may
|
|
11
|
+
> change between releases without prior notice. Use in production at your own
|
|
12
|
+
> risk.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
**react-mnemonic** gives your React components persistent memory. Values survive
|
|
17
|
+
page refreshes, synchronize across tabs, and stay type-safe end-to-end -- all
|
|
18
|
+
through a single hook that works like `useState`.
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
- **`useState`-like API** -- `useMnemonicKey` returns `{ value, set, reset, remove }`
|
|
23
|
+
- **JSON Schema validation** -- optional schema-based validation using a built-in JSON Schema subset
|
|
24
|
+
- **Namespace isolation** -- `MnemonicProvider` prefixes every key to prevent collisions
|
|
25
|
+
- **Cross-tab sync** -- opt-in `listenCrossTab` uses the browser `storage` event
|
|
26
|
+
- **Pluggable storage** -- bring your own backend via the `StorageLike` interface (IndexedDB, sessionStorage, etc.)
|
|
27
|
+
- **Schema versioning and migration** -- upgrade stored data with versioned schemas and migration rules
|
|
28
|
+
- **Write-time normalization** -- migrations where `fromVersion === toVersion` run on every write
|
|
29
|
+
- **Lifecycle callbacks** -- `onMount` and `onChange` hooks
|
|
30
|
+
- **DevTools** -- inspect and mutate state from the browser console
|
|
31
|
+
- **SSR-safe** -- returns defaults when `window` is unavailable
|
|
32
|
+
- **Tree-shakeable, zero dependencies** -- ships ESM + CJS with full TypeScript declarations
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install react-mnemonic
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
yarn add react-mnemonic
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pnpm add react-mnemonic
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Peer dependencies
|
|
49
|
+
|
|
50
|
+
React 18 or later is required.
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"react": ">=18",
|
|
56
|
+
"react-dom": ">=18"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Quick start
|
|
62
|
+
|
|
63
|
+
Wrap your app in a `MnemonicProvider`, then call `useMnemonicKey` anywhere inside it.
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { MnemonicProvider, useMnemonicKey } from "react-mnemonic";
|
|
67
|
+
|
|
68
|
+
function Counter() {
|
|
69
|
+
const { value: count, set } = useMnemonicKey("count", {
|
|
70
|
+
defaultValue: 0,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<div>
|
|
75
|
+
<p>Count: {count}</p>
|
|
76
|
+
<button onClick={() => set((c) => c + 1)}>Increment</button>
|
|
77
|
+
</div>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export default function App() {
|
|
82
|
+
return (
|
|
83
|
+
<MnemonicProvider namespace="my-app">
|
|
84
|
+
<Counter />
|
|
85
|
+
</MnemonicProvider>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The counter value persists in `localStorage` under the key `my-app.count` and
|
|
91
|
+
survives full page reloads.
|
|
92
|
+
|
|
93
|
+
## API
|
|
94
|
+
|
|
95
|
+
### `<MnemonicProvider>`
|
|
96
|
+
|
|
97
|
+
Context provider that scopes storage keys under a namespace.
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
<MnemonicProvider
|
|
101
|
+
namespace="my-app" // key prefix (required)
|
|
102
|
+
storage={localStorage} // StorageLike backend (default: localStorage)
|
|
103
|
+
schemaMode="default" // "default" | "strict" | "autoschema" (default: "default")
|
|
104
|
+
schemaRegistry={registry} // optional SchemaRegistry for versioned schemas
|
|
105
|
+
enableDevTools={false} // expose console helpers (default: false)
|
|
106
|
+
>
|
|
107
|
+
{children}
|
|
108
|
+
</MnemonicProvider>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Multiple providers with different namespaces can coexist in the same app.
|
|
112
|
+
|
|
113
|
+
### `useMnemonicKey<T>(key, options)`
|
|
114
|
+
|
|
115
|
+
Hook for reading and writing a single persistent value.
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
const { value, set, reset, remove } = useMnemonicKey<T>(key, options);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
| Return | Type | Description |
|
|
122
|
+
| -------- | ------------------------------------ | --------------------------------------------- |
|
|
123
|
+
| `value` | `T` | Current decoded value (or default) |
|
|
124
|
+
| `set` | `(next: T \| (cur: T) => T) => void` | Update the value (direct or updater function) |
|
|
125
|
+
| `reset` | `() => void` | Reset to `defaultValue` and persist it |
|
|
126
|
+
| `remove` | `() => void` | Delete the key from storage entirely |
|
|
127
|
+
|
|
128
|
+
#### Options
|
|
129
|
+
|
|
130
|
+
| Option | Type | Default | Description |
|
|
131
|
+
| ---------------- | ------------------------------------------------- | ----------- | --------------------------------------------------- |
|
|
132
|
+
| `defaultValue` | `T \| ((error?: CodecError \| SchemaError) => T)` | _required_ | Fallback value or error-aware factory |
|
|
133
|
+
| `codec` | `Codec<T>` | `JSONCodec` | Encode/decode strategy (bypasses schema validation) |
|
|
134
|
+
| `onMount` | `(value: T) => void` | -- | Called once with the initial value |
|
|
135
|
+
| `onChange` | `(value: T, prev: T) => void` | -- | Called on every value change |
|
|
136
|
+
| `listenCrossTab` | `boolean` | `false` | Sync via the browser `storage` event |
|
|
137
|
+
| `schema` | `{ version?: number }` | -- | Pin writes to a specific schema version |
|
|
138
|
+
|
|
139
|
+
### Codecs
|
|
140
|
+
|
|
141
|
+
The default codec is `JSONCodec`, which handles all JSON-serializable values.
|
|
142
|
+
You can create custom codecs using `createCodec` for types that need special
|
|
143
|
+
serialization (e.g., `Date`, `Set`, `Map`).
|
|
144
|
+
|
|
145
|
+
Using a custom codec bypasses JSON Schema validation -- the codec is a low-level
|
|
146
|
+
escape hatch for when you need full control over serialization.
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
import { createCodec } from "react-mnemonic";
|
|
150
|
+
|
|
151
|
+
const DateCodec = createCodec<Date>(
|
|
152
|
+
(date) => date.toISOString(),
|
|
153
|
+
(str) => new Date(str),
|
|
154
|
+
);
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### `StorageLike`
|
|
158
|
+
|
|
159
|
+
The interface your custom storage backend must satisfy.
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
interface StorageLike {
|
|
163
|
+
getItem(key: string): string | null;
|
|
164
|
+
setItem(key: string, value: string): void;
|
|
165
|
+
removeItem(key: string): void;
|
|
166
|
+
key?(index: number): string | null;
|
|
167
|
+
readonly length?: number;
|
|
168
|
+
onExternalChange?: (callback: (changedKeys?: string[]) => void) => () => void;
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`onExternalChange` enables cross-tab sync for non-localStorage backends (e.g.
|
|
173
|
+
IndexedDB over `BroadcastChannel`). The library handles all error cases
|
|
174
|
+
internally -- see the `StorageLike` JSDoc for the full error-handling contract.
|
|
175
|
+
|
|
176
|
+
### `validateJsonSchema(schema, value)`
|
|
177
|
+
|
|
178
|
+
Validate an arbitrary value against a JSON Schema (the same subset used by the
|
|
179
|
+
hook). Returns an array of validation errors, empty when the value is valid.
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
import { validateJsonSchema } from "react-mnemonic";
|
|
183
|
+
|
|
184
|
+
const errors = validateJsonSchema(
|
|
185
|
+
{ type: "object", properties: { name: { type: "string" } }, required: ["name"] },
|
|
186
|
+
{ name: 42 },
|
|
187
|
+
);
|
|
188
|
+
// [{ path: ".name", message: 'Expected type "string"' }]
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### `compileSchema(schema)`
|
|
192
|
+
|
|
193
|
+
Pre-compile a JSON Schema into a reusable validator function. The compiled
|
|
194
|
+
validator is cached by schema reference (via `WeakMap`), so calling
|
|
195
|
+
`compileSchema` twice with the same object returns the identical function.
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import { compileSchema } from "react-mnemonic";
|
|
199
|
+
import type { CompiledValidator } from "react-mnemonic";
|
|
200
|
+
|
|
201
|
+
const validate: CompiledValidator = compileSchema({
|
|
202
|
+
type: "object",
|
|
203
|
+
properties: {
|
|
204
|
+
name: { type: "string", minLength: 1 },
|
|
205
|
+
age: { type: "number", minimum: 0 },
|
|
206
|
+
},
|
|
207
|
+
required: ["name"],
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
validate({ name: "Alice", age: 30 }); // []
|
|
211
|
+
validate({ age: -1 }); // [{ path: "", … }, { path: ".age", … }]
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
This is useful when you validate the same schema frequently outside of the hook
|
|
215
|
+
(e.g. in form validation or server responses).
|
|
216
|
+
|
|
217
|
+
### Error classes
|
|
218
|
+
|
|
219
|
+
| Class | Thrown when |
|
|
220
|
+
| ------------- | ------------------------------------ |
|
|
221
|
+
| `CodecError` | Encoding or decoding fails |
|
|
222
|
+
| `SchemaError` | Schema validation or migration fails |
|
|
223
|
+
|
|
224
|
+
Both are passed to `defaultValue` factories so you can inspect or log the
|
|
225
|
+
failure reason.
|
|
226
|
+
|
|
227
|
+
## Usage examples
|
|
228
|
+
|
|
229
|
+
### Cross-tab theme sync
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
const { value: theme, set } = useMnemonicKey<"light" | "dark">("theme", {
|
|
233
|
+
defaultValue: "light",
|
|
234
|
+
listenCrossTab: true,
|
|
235
|
+
onChange: (t) => {
|
|
236
|
+
document.documentElement.setAttribute("data-theme", t);
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Error-aware defaults
|
|
242
|
+
|
|
243
|
+
```tsx
|
|
244
|
+
import { useMnemonicKey, CodecError, SchemaError } from "react-mnemonic";
|
|
245
|
+
|
|
246
|
+
const getDefault = (error?: CodecError | SchemaError) => {
|
|
247
|
+
if (error instanceof CodecError) {
|
|
248
|
+
console.warn("Corrupt stored data:", error.message);
|
|
249
|
+
}
|
|
250
|
+
if (error instanceof SchemaError) {
|
|
251
|
+
console.warn("Schema validation failed:", error.message);
|
|
252
|
+
}
|
|
253
|
+
return { count: 0 };
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const { value } = useMnemonicKey("counter", { defaultValue: getDefault });
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Schema modes and versioning
|
|
260
|
+
|
|
261
|
+
Mnemonic supports optional schema versioning through `schemaMode` and an
|
|
262
|
+
optional `schemaRegistry`.
|
|
263
|
+
|
|
264
|
+
- `default`: Schemas are optional. Reads use a schema when one exists for the
|
|
265
|
+
stored version, otherwise the hook codec. Writes use the highest registered
|
|
266
|
+
schema for the key; if no schemas are registered, writes use an unversioned
|
|
267
|
+
(v0) envelope.
|
|
268
|
+
- `strict`: Every stored version must have a registered schema. Reads without a
|
|
269
|
+
matching schema fall back to `defaultValue` with a `SchemaError`.
|
|
270
|
+
Writes require a registered schema when any schemas exist, but fall back to
|
|
271
|
+
a v0 envelope when the registry has none.
|
|
272
|
+
- `autoschema`: Like `default`, but if no schema exists for a key, the first
|
|
273
|
+
successful read infers and registers a v1 schema. Subsequent reads/writes use
|
|
274
|
+
that schema.
|
|
275
|
+
|
|
276
|
+
Version `0` is valid for schemas and migrations. Schemas at version `0` are
|
|
277
|
+
treated like any other version.
|
|
278
|
+
|
|
279
|
+
### JSON Schema validation
|
|
280
|
+
|
|
281
|
+
Schemas use a subset of JSON Schema for validation. The supported keywords are:
|
|
282
|
+
|
|
283
|
+
- `type` (including array form for nullable types, e.g., `["string", "null"]`)
|
|
284
|
+
- `enum`, `const`
|
|
285
|
+
- `minimum`, `maximum`, `exclusiveMinimum`, `exclusiveMaximum`
|
|
286
|
+
- `minLength`, `maxLength`
|
|
287
|
+
- `properties`, `required`, `additionalProperties`
|
|
288
|
+
- `items`, `minItems`, `maxItems`
|
|
289
|
+
|
|
290
|
+
```ts
|
|
291
|
+
// Schema definition -- fully serializable JSON, no functions
|
|
292
|
+
const schema: KeySchema = {
|
|
293
|
+
key: "profile",
|
|
294
|
+
version: 1,
|
|
295
|
+
schema: {
|
|
296
|
+
type: "object",
|
|
297
|
+
properties: {
|
|
298
|
+
name: { type: "string", minLength: 1 },
|
|
299
|
+
email: { type: "string" },
|
|
300
|
+
age: { type: "number", minimum: 0 },
|
|
301
|
+
},
|
|
302
|
+
required: ["name", "email"],
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Write-time migrations (normalizers)
|
|
308
|
+
|
|
309
|
+
A migration where `fromVersion === toVersion` runs on every write, acting as a
|
|
310
|
+
normalizer. This is useful for trimming whitespace, lowercasing strings, etc.
|
|
311
|
+
|
|
312
|
+
```ts
|
|
313
|
+
const normalizer: MigrationRule = {
|
|
314
|
+
key: "name",
|
|
315
|
+
fromVersion: 1,
|
|
316
|
+
toVersion: 1,
|
|
317
|
+
migrate: (value) => String(value).trim().toLowerCase(),
|
|
318
|
+
};
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Example schema registry
|
|
322
|
+
|
|
323
|
+
A schema registry stores versioned schemas for each key, and resolves migration
|
|
324
|
+
paths to upgrade stored data. Schemas are plain JSON (serializable); migrations
|
|
325
|
+
are procedural functions.
|
|
326
|
+
|
|
327
|
+
```tsx
|
|
328
|
+
import {
|
|
329
|
+
MnemonicProvider,
|
|
330
|
+
useMnemonicKey,
|
|
331
|
+
type SchemaRegistry,
|
|
332
|
+
type KeySchema,
|
|
333
|
+
type MigrationRule,
|
|
334
|
+
} from "react-mnemonic";
|
|
335
|
+
|
|
336
|
+
const schemas = new Map<string, KeySchema>();
|
|
337
|
+
const migrations: MigrationRule[] = [];
|
|
338
|
+
|
|
339
|
+
const registry: SchemaRegistry = {
|
|
340
|
+
getSchema: (key, version) => schemas.get(`${key}:${version}`),
|
|
341
|
+
getLatestSchema: (key) =>
|
|
342
|
+
Array.from(schemas.values())
|
|
343
|
+
.filter((schema) => schema.key === key)
|
|
344
|
+
.sort((a, b) => b.version - a.version)[0],
|
|
345
|
+
getMigrationPath: (key, fromVersion, toVersion) => {
|
|
346
|
+
const byKey = migrations.filter((rule) => rule.key === key);
|
|
347
|
+
const path: MigrationRule[] = [];
|
|
348
|
+
let cur = fromVersion;
|
|
349
|
+
while (cur < toVersion) {
|
|
350
|
+
const next = byKey.find((rule) => rule.fromVersion === cur);
|
|
351
|
+
if (!next) return null;
|
|
352
|
+
path.push(next);
|
|
353
|
+
cur = next.toVersion;
|
|
354
|
+
}
|
|
355
|
+
return path;
|
|
356
|
+
},
|
|
357
|
+
getWriteMigration: (key, version) => {
|
|
358
|
+
return migrations.find((r) => r.key === key && r.fromVersion === version && r.toVersion === version);
|
|
359
|
+
},
|
|
360
|
+
registerSchema: (schema) => {
|
|
361
|
+
const id = `${schema.key}:${schema.version}`;
|
|
362
|
+
if (schemas.has(id)) throw new Error(`Schema already registered for ${id}`);
|
|
363
|
+
schemas.set(id, schema);
|
|
364
|
+
},
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
registry.registerSchema({
|
|
368
|
+
key: "profile",
|
|
369
|
+
version: 1,
|
|
370
|
+
schema: {
|
|
371
|
+
type: "object",
|
|
372
|
+
properties: { name: { type: "string" }, email: { type: "string" } },
|
|
373
|
+
required: ["name", "email"],
|
|
374
|
+
},
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
migrations.push({
|
|
378
|
+
key: "profile",
|
|
379
|
+
fromVersion: 1,
|
|
380
|
+
toVersion: 2,
|
|
381
|
+
migrate: (value) => {
|
|
382
|
+
const v1 = value as { name: string; email: string };
|
|
383
|
+
return { ...v1, migratedAt: new Date().toISOString() };
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
registry.registerSchema({
|
|
388
|
+
key: "profile",
|
|
389
|
+
version: 2,
|
|
390
|
+
schema: {
|
|
391
|
+
type: "object",
|
|
392
|
+
properties: {
|
|
393
|
+
name: { type: "string" },
|
|
394
|
+
email: { type: "string" },
|
|
395
|
+
migratedAt: { type: "string" },
|
|
396
|
+
},
|
|
397
|
+
required: ["name", "email", "migratedAt"],
|
|
398
|
+
},
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
function ProfileEditor() {
|
|
402
|
+
const { value, set } = useMnemonicKey<{ name: string; email: string; migratedAt: string }>("profile", {
|
|
403
|
+
defaultValue: { name: "", email: "", migratedAt: "" },
|
|
404
|
+
});
|
|
405
|
+
return <input value={value.name} onChange={(e) => set({ ...value, name: e.target.value })} />;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
<MnemonicProvider namespace="app" schemaMode="default" schemaRegistry={registry}>
|
|
409
|
+
<ProfileEditor />
|
|
410
|
+
</MnemonicProvider>;
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### Registry immutability
|
|
414
|
+
|
|
415
|
+
In `default` and `strict` modes, the schema registry is treated as immutable for
|
|
416
|
+
the lifetime of the provider. The hook caches registry lookups to keep read and
|
|
417
|
+
write hot paths fast. To ship new schemas or migrations, publish a new app
|
|
418
|
+
version and remount the provider.
|
|
419
|
+
|
|
420
|
+
`autoschema` remains mutable because inferred schemas are registered at runtime.
|
|
421
|
+
|
|
422
|
+
### Custom storage backend
|
|
423
|
+
|
|
424
|
+
```tsx
|
|
425
|
+
import { MnemonicProvider } from "react-mnemonic";
|
|
426
|
+
import type { StorageLike } from "react-mnemonic";
|
|
427
|
+
|
|
428
|
+
const idbStorage: StorageLike = {
|
|
429
|
+
getItem: (key) => /* read from IndexedDB */,
|
|
430
|
+
setItem: (key, value) => /* write to IndexedDB */,
|
|
431
|
+
removeItem: (key) => /* delete from IndexedDB */,
|
|
432
|
+
onExternalChange: (cb) => {
|
|
433
|
+
const bc = new BroadcastChannel("my-app-sync");
|
|
434
|
+
bc.onmessage = (e) => cb(e.data.keys);
|
|
435
|
+
return () => bc.close();
|
|
436
|
+
},
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
<MnemonicProvider namespace="my-app" storage={idbStorage}>
|
|
440
|
+
<App />
|
|
441
|
+
</MnemonicProvider>
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### DevTools
|
|
445
|
+
|
|
446
|
+
Enable the console inspector in development:
|
|
447
|
+
|
|
448
|
+
```tsx
|
|
449
|
+
<MnemonicProvider namespace="app" enableDevTools={process.env.NODE_ENV === "development"}>
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
Then in the browser console:
|
|
453
|
+
|
|
454
|
+
```js
|
|
455
|
+
__REACT_MNEMONIC_DEVTOOLS__.app.dump(); // table of all keys
|
|
456
|
+
__REACT_MNEMONIC_DEVTOOLS__.app.get("theme"); // read a decoded value
|
|
457
|
+
__REACT_MNEMONIC_DEVTOOLS__.app.set("theme", "dark"); // write
|
|
458
|
+
__REACT_MNEMONIC_DEVTOOLS__.app.remove("theme"); // delete
|
|
459
|
+
__REACT_MNEMONIC_DEVTOOLS__.app.keys(); // list all keys
|
|
460
|
+
__REACT_MNEMONIC_DEVTOOLS__.app.clear(); // remove all keys
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
## TypeScript
|
|
464
|
+
|
|
465
|
+
The library is written in strict TypeScript and ships its own declarations.
|
|
466
|
+
All public types are re-exported from the package root:
|
|
467
|
+
|
|
468
|
+
```ts
|
|
469
|
+
import type {
|
|
470
|
+
Codec,
|
|
471
|
+
StorageLike,
|
|
472
|
+
MnemonicProviderOptions,
|
|
473
|
+
MnemonicProviderProps,
|
|
474
|
+
UseMnemonicKeyOptions,
|
|
475
|
+
KeySchema,
|
|
476
|
+
MigrationRule,
|
|
477
|
+
MigrationPath,
|
|
478
|
+
SchemaRegistry,
|
|
479
|
+
SchemaMode,
|
|
480
|
+
JsonSchema,
|
|
481
|
+
JsonSchemaType,
|
|
482
|
+
JsonSchemaValidationError,
|
|
483
|
+
CompiledValidator,
|
|
484
|
+
} from "react-mnemonic";
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
## Disclaimer
|
|
488
|
+
|
|
489
|
+
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
490
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
491
|
+
FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. The authors make no guarantees
|
|
492
|
+
regarding the reliability, availability, or suitability of this library for any
|
|
493
|
+
particular use case. See the [MIT License](./LICENSE.md) for full terms.
|
|
494
|
+
|
|
495
|
+
## License
|
|
496
|
+
|
|
497
|
+
[MIT](./LICENSE.md) -- Copyright Scott Dixon
|