@nkzw/fate-indexeddb 1.5.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1354,13 +1354,13 @@ Then pass `persistence` when creating the client:
1354
1354
 
1355
1355
  ```tsx
1356
1356
  import { createPersistence } from '@nkzw/fate/persistence';
1357
- import { indexedDB } from '@nkzw/fate-indexeddb';
1357
+ import { createIndexedDBStorage } from '@nkzw/fate-indexeddb';
1358
1358
  import { createFateClient } from 'react-fate/client';
1359
1359
 
1360
1360
  const fate = createFateClient({
1361
1361
  persistence: createPersistence({
1362
1362
  key: `workspace:${workspaceId}:user:${userId}`,
1363
- storage: indexedDB(),
1363
+ storage: createIndexedDBStorage(),
1364
1364
  }),
1365
1365
  url: '/api/fate',
1366
1366
  });
@@ -1373,7 +1373,7 @@ persistence: createPersistence({
1373
1373
  key: `workspace:${workspaceId}:user:${userId}`,
1374
1374
  maxAge: 24 * 60 * 60 * 1000,
1375
1375
  maxBytes: 25 * 1024 * 1024,
1376
- storage: indexedDB(),
1376
+ storage: createIndexedDBStorage(),
1377
1377
  }),
1378
1378
  ```
1379
1379
 
@@ -1545,7 +1545,7 @@ const durableHTTP = createHTTPTransport<MyAPI>({
1545
1545
 
1546
1546
  const fate = createFateClient({
1547
1547
  // ...your generated tRPC or GraphQL client options...
1548
- persistence: createPersistence({ key: accountKey, storage: indexedDB() }),
1548
+ persistence: createPersistence({ key: accountKey, storage: createIndexedDBStorage() }),
1549
1549
  mutateDurably: durableHTTP.mutateDurably,
1550
1550
  });
1551
1551
  ```
@@ -1635,7 +1635,6 @@ The core persistence layer has no IndexedDB dependency. You can use another back
1635
1635
  ```ts
1636
1636
  interface PersistenceStorage {
1637
1637
  read(key: string): Promise<unknown>;
1638
- write(key: string, value: unknown): Promise<void>;
1639
1638
  scan(
1640
1639
  prefix: string,
1641
1640
  after?: string,
@@ -1650,9 +1649,8 @@ interface PersistenceStorage {
1650
1649
  Values use fate's hydration codec and can be stored as JSON. The adapter handles storage and coordination:
1651
1650
 
1652
1651
  - `read` returns the saved value for a key.
1653
- - `write` replaces one value atomically and resolves after it has committed.
1654
1652
  - `scan` returns keys under a prefix in ascending order, strictly after the optional cursor, up to the limit (64 by default). Use your backend's ordered index to keep each scan small.
1655
- - `writeBatch` commits all entries atomically. An `undefined` value deletes the key.
1653
+ - `writeBatch` commits all entries atomically and resolves after they have committed. An `undefined` value deletes the key. Use a one-entry batch for a single write.
1656
1654
  - `exclusive` coordinates every tab or process sharing the backend. Different lock names are independent: fate holds a delivery lock during network work and acquires a separate write lock when updating storage. Your adapter must allow that nesting.
1657
1655
  - `subscribe` notifies other clients after a change commits, including keys changed by `writeBatch`. Without notifications, clients check saved mutations during delivery attempts and explicit `retry()` calls.
1658
1656
 
package/lib/index.d.mts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { PersistenceStorage } from "@nkzw/fate/persistence";
2
2
  //#region src/index.d.ts
3
- declare function indexedDB({ name }?: {
3
+ type IndexedDBStorageOptions = Readonly<{
4
4
  name?: string;
5
- }): PersistenceStorage;
5
+ }>;
6
+ declare function createIndexedDBStorage({ name }?: IndexedDBStorageOptions): PersistenceStorage;
6
7
  //#endregion
7
- export { indexedDB };
8
+ export { IndexedDBStorageOptions, createIndexedDBStorage };
package/lib/index.mjs CHANGED
@@ -1,23 +1,23 @@
1
1
  import { openDB } from "idb";
2
2
  //#region src/index.ts
3
- function indexedDB({ name = "fate" } = {}) {
4
- let leases = 0;
3
+ function createIndexedDBStorage({ name = "fate" } = {}) {
4
+ let connectionLeases = 0;
5
5
  let connection;
6
- const release = () => {
7
- if (--leases === 0) {
6
+ const releaseDatabase = () => {
7
+ if (--connectionLeases === 0) {
8
8
  const previous = connection;
9
9
  connection = void 0;
10
10
  previous?.then((db) => db.close(), () => {});
11
11
  }
12
12
  };
13
- const database = async () => {
14
- leases++;
13
+ const acquireDatabase = async () => {
14
+ connectionLeases++;
15
15
  try {
16
16
  return await (connection ??= openDB(name, 1, { upgrade(db) {
17
17
  db.createObjectStore("fate");
18
18
  } }));
19
19
  } catch (error) {
20
- release();
20
+ releaseDatabase();
21
21
  throw error;
22
22
  }
23
23
  };
@@ -43,24 +43,24 @@ function indexedDB({ name = "fate" } = {}) {
43
43
  exclusive(key, run) {
44
44
  if (typeof navigator === "undefined" || !navigator.locks) throw new Error("fate(indexeddb): Web Locks are required for safe durable writes across tabs.");
45
45
  return navigator.locks.request(`fate:${JSON.stringify([name, key])}`, async () => {
46
- leases++;
46
+ connectionLeases++;
47
47
  try {
48
48
  return await run();
49
49
  } finally {
50
- release();
50
+ releaseDatabase();
51
51
  }
52
52
  });
53
53
  },
54
54
  async read(key) {
55
- const db = await database();
55
+ const db = await acquireDatabase();
56
56
  try {
57
57
  return await db.get("fate", key);
58
58
  } finally {
59
- release();
59
+ releaseDatabase();
60
60
  }
61
61
  },
62
62
  async scan(prefix, after, limit = 64) {
63
- const db = await database();
63
+ const db = await acquireDatabase();
64
64
  try {
65
65
  const range = IDBKeyRange.lowerBound(after !== void 0 && after >= prefix ? after : prefix, after !== void 0 && after >= prefix);
66
66
  const transaction = db.transaction("fate");
@@ -76,7 +76,7 @@ function indexedDB({ name = "fate" } = {}) {
76
76
  await transaction.done;
77
77
  return entries;
78
78
  } finally {
79
- release();
79
+ releaseDatabase();
80
80
  }
81
81
  },
82
82
  subscribe(key, listener) {
@@ -101,19 +101,8 @@ function indexedDB({ name = "fate" } = {}) {
101
101
  }
102
102
  };
103
103
  },
104
- async write(key, value) {
105
- const db = await database();
106
- try {
107
- const transaction = db.transaction("fate", "readwrite");
108
- await transaction.store.put(value, key);
109
- await transaction.done;
110
- } finally {
111
- release();
112
- }
113
- broadcast([key]);
114
- },
115
104
  async writeBatch(entries) {
116
- const db = await database();
105
+ const db = await acquireDatabase();
117
106
  try {
118
107
  const transaction = db.transaction("fate", "readwrite");
119
108
  try {
@@ -127,11 +116,11 @@ function indexedDB({ name = "fate" } = {}) {
127
116
  throw error;
128
117
  }
129
118
  } finally {
130
- release();
119
+ releaseDatabase();
131
120
  }
132
121
  broadcast([...new Set(entries.map(([key]) => key))]);
133
122
  }
134
123
  };
135
124
  }
136
125
  //#endregion
137
- export { indexedDB };
126
+ export { createIndexedDBStorage };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nkzw/fate-indexeddb",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "description": "Optional IndexedDB persistence adapter for fate.",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -22,10 +22,10 @@
22
22
  "fake-indexeddb": "^6.2.4",
23
23
  "vite": "npm:@voidzero-dev/vite-plus-core@0.3.0",
24
24
  "vite-plus": "0.3.0",
25
- "@nkzw/fate": "1.5.0"
25
+ "@nkzw/fate": "1.5.1"
26
26
  },
27
27
  "peerDependencies": {
28
- "@nkzw/fate": "^1.4.0"
28
+ "@nkzw/fate": "^1.5.1"
29
29
  },
30
30
  "scripts": {
31
31
  "build": "vp pack --tsconfig tsconfig.json -d lib src/index.ts"