idb-refined 0.0.2 → 0.0.3
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 +24 -50
- package/dist/cleanWhenTooLarge.d.ts +2 -1
- package/dist/cleanWhenTooLarge.js +3 -1
- package/dist/client.d.ts +21 -0
- package/dist/client.js +84 -0
- package/dist/index.d.ts +2 -13
- package/dist/index.js +1 -7
- package/dist/putWithEviction.d.ts +2 -1
- package/dist/putWithEviction.js +5 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# idb-refined
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Minimal IndexedDB client on top of [idb](https://www.npmjs.com/package/idb). Exposes **add**, **update**, **delete**, and **removeDb**. Init, schema, cleanup and eviction run automatically.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -12,71 +12,45 @@ npm install idb-refined
|
|
|
12
12
|
|
|
13
13
|
## API
|
|
14
14
|
|
|
15
|
-
|
|
|
16
|
-
|
|
17
|
-
| **
|
|
18
|
-
| **
|
|
19
|
-
| **
|
|
20
|
-
| **
|
|
21
|
-
| **
|
|
22
|
-
| **
|
|
23
|
-
| **deleteDB(name)** | Re-export of idb’s `deleteDB`. |
|
|
15
|
+
| Export | Purpose |
|
|
16
|
+
|--------|---------|
|
|
17
|
+
| **createClient(options)** | Returns `{ set, get, update, delete, deleteDb }`. Options: `dbName` (required), `storeName` (optional). |
|
|
18
|
+
| **set(value)** | Store a value. Value must have an `id` property. Expiry and eviction run automatically. |
|
|
19
|
+
| **get(key)** | Get a value by key. Returns `undefined` if not found. |
|
|
20
|
+
| **update(key, value)** | Update an existing entry by key. |
|
|
21
|
+
| **delete(key)** | Delete an entry by key. |
|
|
22
|
+
| **deleteDb()** | Close the DB and delete it from disk. |
|
|
24
23
|
|
|
25
|
-
For
|
|
24
|
+
For details, see **[Advanced documentation](docs/advanced.md)**. Run the **[example](example/)** in the browser (see `example/README.md`).
|
|
26
25
|
|
|
27
|
-
##
|
|
28
|
-
|
|
29
|
-
### Cache with TTL and max size
|
|
26
|
+
## Example
|
|
30
27
|
|
|
31
28
|
```ts
|
|
32
|
-
import {
|
|
33
|
-
|
|
34
|
-
const db = await initDb("my-cache", {
|
|
35
|
-
schema: {
|
|
36
|
-
stores: {
|
|
37
|
-
cache: { keyPath: "id", indexes: ["expiresAt"] },
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
await putWithEviction(db, "cache", { id: "k1", data: "v1" }, {
|
|
43
|
-
dateKey: "expiresAt",
|
|
44
|
-
maxCount: 1000,
|
|
45
|
-
ttlSeconds: 3600,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
await cleanOldEntries(db, "cache", { dateKey: "expiresAt", before: Date.now() });
|
|
49
|
-
await deleteByKey(db, "cache", "k1");
|
|
50
|
-
```
|
|
29
|
+
import { createClient } from "idb-refined";
|
|
51
30
|
|
|
52
|
-
|
|
31
|
+
// Optional: type the stored value for set/get/update
|
|
32
|
+
type User = { id: string; name: string; createdAt?: number; expiresAt?: number };
|
|
33
|
+
const { set, get, update, delete: del, deleteDb } = createClient<User>({ dbName: "my-app" });
|
|
53
34
|
|
|
54
|
-
|
|
55
|
-
await
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### Manual eviction
|
|
62
|
-
|
|
63
|
-
```ts
|
|
64
|
-
const deleted = await cleanWhenTooLarge(db, "cache", { dateKey: "expiresAt", maxCount: 500 });
|
|
65
|
-
console.log(`Evicted ${deleted} entries`);
|
|
35
|
+
await set({ id: "1", name: "Alice" });
|
|
36
|
+
const value = await get("1"); // User | undefined
|
|
37
|
+
await update("1", { name: "Alice Updated" });
|
|
38
|
+
await del("1");
|
|
39
|
+
await deleteDb();
|
|
66
40
|
```
|
|
67
41
|
|
|
68
42
|
## Requirements
|
|
69
43
|
|
|
70
|
-
-
|
|
71
|
-
-
|
|
44
|
+
- Values must include an `id` property (used as the store key).
|
|
45
|
+
- The library uses a single store (default name `"store"`) with indexes on `expiresAt` and `createdAt`. Cleanup and eviction run on set.
|
|
72
46
|
|
|
73
47
|
## Releasing
|
|
74
48
|
|
|
75
49
|
1. Bump version: `pnpm version patch` (or `minor` / `major`).
|
|
76
50
|
2. Commit and push: `git push && git push --tags`.
|
|
77
|
-
3. Pushing a tag matching `v*`
|
|
51
|
+
3. Pushing a tag matching `v*` triggers the [Publish to npm](.github/workflows/publish.yml) workflow.
|
|
78
52
|
|
|
79
|
-
**Required:** Add an `NPM_TOKEN` secret in the repo (Settings → Secrets and variables → Actions).
|
|
53
|
+
**Required:** Add an `NPM_TOKEN` secret in the repo (Settings → Secrets and variables → Actions).
|
|
80
54
|
|
|
81
55
|
## License
|
|
82
56
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { IDBPDatabase } from "idb";
|
|
2
2
|
export interface CleanWhenTooLargeOptions {
|
|
3
3
|
dateKey: string;
|
|
4
|
-
|
|
4
|
+
/** Target max entries after eviction. */
|
|
5
|
+
maxCount?: number;
|
|
5
6
|
}
|
|
6
7
|
/**
|
|
7
8
|
* Evict oldest entries (by dateKey) until store count <= maxCount.
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
const DEFAULT_MAX_COUNT = 1000;
|
|
1
2
|
/**
|
|
2
3
|
* Evict oldest entries (by dateKey) until store count <= maxCount.
|
|
3
4
|
* Requires an index on dateKey. Returns the number of entries deleted.
|
|
4
5
|
*/
|
|
5
6
|
export async function cleanWhenTooLarge(db, storeName, options) {
|
|
6
|
-
const { dateKey
|
|
7
|
+
const { dateKey } = options;
|
|
8
|
+
const maxCount = options.maxCount ?? DEFAULT_MAX_COUNT;
|
|
7
9
|
const count = await db.count(storeName);
|
|
8
10
|
if (count <= maxCount)
|
|
9
11
|
return 0;
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface CreateClientOptions {
|
|
2
|
+
dbName: string;
|
|
3
|
+
storeName?: string;
|
|
4
|
+
}
|
|
5
|
+
/** Stored value must have `id`. `createdAt` and `expiresAt` are set by the client if missing. */
|
|
6
|
+
export type StoredValue = Record<string, unknown> & {
|
|
7
|
+
id: IDBValidKey;
|
|
8
|
+
};
|
|
9
|
+
export interface IdbRefinedClient<T extends StoredValue = StoredValue> {
|
|
10
|
+
add: (value: T) => Promise<void>;
|
|
11
|
+
get: (key: IDBValidKey) => Promise<T | undefined>;
|
|
12
|
+
update: (key: IDBValidKey, value: Partial<T>) => Promise<void>;
|
|
13
|
+
delete: (key: IDBValidKey) => Promise<void>;
|
|
14
|
+
removeDb: () => Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Create a client that exposes add, get, update, delete, removeDb.
|
|
18
|
+
* Init, schema, cleanup and eviction run automatically.
|
|
19
|
+
* @template T - Stored value shape (must include `id`). Omit for a generic client.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createClient<T extends StoredValue = StoredValue>(options: CreateClientOptions): IdbRefinedClient<T>;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { deleteDB } from "idb";
|
|
2
|
+
import { initDb } from "./initDb.js";
|
|
3
|
+
import { cleanOldEntries } from "./cleanOldEntries.js";
|
|
4
|
+
import { cleanWhenTooLarge } from "./cleanWhenTooLarge.js";
|
|
5
|
+
const DEFAULT_STORE_NAME = "store";
|
|
6
|
+
const DEFAULT_KEY_PATH = "id";
|
|
7
|
+
const DATE_INDEXES = ["expiresAt", "createdAt"];
|
|
8
|
+
const DEFAULT_TTL_MS = 3600 * 1000;
|
|
9
|
+
const DEFAULT_MAX_COUNT = 1000;
|
|
10
|
+
const dbCache = new Map();
|
|
11
|
+
function getDefaultSchema(storeName) {
|
|
12
|
+
return {
|
|
13
|
+
stores: {
|
|
14
|
+
[storeName]: {
|
|
15
|
+
keyPath: DEFAULT_KEY_PATH,
|
|
16
|
+
indexes: DATE_INDEXES,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
async function getDb(dbName, storeName) {
|
|
22
|
+
let db = dbCache.get(dbName);
|
|
23
|
+
if (db != null)
|
|
24
|
+
return db;
|
|
25
|
+
const schema = getDefaultSchema(storeName);
|
|
26
|
+
db = await initDb(dbName, { schema });
|
|
27
|
+
dbCache.set(dbName, db);
|
|
28
|
+
return db;
|
|
29
|
+
}
|
|
30
|
+
function setExpiryFields(value) {
|
|
31
|
+
const now = Date.now();
|
|
32
|
+
if (value.createdAt === undefined)
|
|
33
|
+
value.createdAt = now;
|
|
34
|
+
if (value.expiresAt === undefined)
|
|
35
|
+
value.expiresAt = now + DEFAULT_TTL_MS;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create a client that exposes add, get, update, delete, removeDb.
|
|
39
|
+
* Init, schema, cleanup and eviction run automatically.
|
|
40
|
+
* @template T - Stored value shape (must include `id`). Omit for a generic client.
|
|
41
|
+
*/
|
|
42
|
+
export function createClient(options) {
|
|
43
|
+
const { dbName } = options;
|
|
44
|
+
const storeName = options.storeName ?? DEFAULT_STORE_NAME;
|
|
45
|
+
return {
|
|
46
|
+
async add(value) {
|
|
47
|
+
const db = await getDb(dbName, storeName);
|
|
48
|
+
setExpiryFields(value);
|
|
49
|
+
await db.put(storeName, value);
|
|
50
|
+
const count = await db.count(storeName);
|
|
51
|
+
if (count > DEFAULT_MAX_COUNT) {
|
|
52
|
+
await cleanWhenTooLarge(db, storeName, {
|
|
53
|
+
dateKey: "createdAt",
|
|
54
|
+
maxCount: DEFAULT_MAX_COUNT,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
await cleanOldEntries(db, storeName, {
|
|
58
|
+
dateKey: "expiresAt",
|
|
59
|
+
before: Date.now(),
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
async get(key) {
|
|
63
|
+
const db = await getDb(dbName, storeName);
|
|
64
|
+
return (await db.get(storeName, key));
|
|
65
|
+
},
|
|
66
|
+
async update(key, value) {
|
|
67
|
+
const db = await getDb(dbName, storeName);
|
|
68
|
+
const withKey = { ...value, [DEFAULT_KEY_PATH]: key };
|
|
69
|
+
await db.put(storeName, withKey);
|
|
70
|
+
},
|
|
71
|
+
async delete(key) {
|
|
72
|
+
const db = await getDb(dbName, storeName);
|
|
73
|
+
await db.delete(storeName, key);
|
|
74
|
+
},
|
|
75
|
+
async removeDb() {
|
|
76
|
+
const db = dbCache.get(dbName);
|
|
77
|
+
if (db != null) {
|
|
78
|
+
db.close();
|
|
79
|
+
dbCache.delete(dbName);
|
|
80
|
+
}
|
|
81
|
+
await deleteDB(dbName);
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,2 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export type {
|
|
3
|
-
export { cleanOldEntries } from "./cleanOldEntries.js";
|
|
4
|
-
export type { CleanOldEntriesOptions } from "./cleanOldEntries.js";
|
|
5
|
-
export { cleanWhenTooLarge } from "./cleanWhenTooLarge.js";
|
|
6
|
-
export type { CleanWhenTooLargeOptions } from "./cleanWhenTooLarge.js";
|
|
7
|
-
export { putWithEviction } from "./putWithEviction.js";
|
|
8
|
-
export type { PutWithEvictionOptions } from "./putWithEviction.js";
|
|
9
|
-
export { deleteByKey, clearStore } from "./delete.js";
|
|
10
|
-
export { deleteDB } from "idb";
|
|
11
|
-
export type { IDBPDatabase, DBSchema } from "idb";
|
|
12
|
-
export type { SchemaDef, StoreDef } from "./schema.js";
|
|
13
|
-
export { fingerprint, applySchema } from "./schema.js";
|
|
1
|
+
export { createClient } from "./client.js";
|
|
2
|
+
export type { CreateClientOptions, IdbRefinedClient, StoredValue, } from "./client.js";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export { cleanOldEntries } from "./cleanOldEntries.js";
|
|
3
|
-
export { cleanWhenTooLarge } from "./cleanWhenTooLarge.js";
|
|
4
|
-
export { putWithEviction } from "./putWithEviction.js";
|
|
5
|
-
export { deleteByKey, clearStore } from "./delete.js";
|
|
6
|
-
export { deleteDB } from "idb";
|
|
7
|
-
export { fingerprint, applySchema } from "./schema.js";
|
|
1
|
+
export { createClient } from "./client.js";
|
|
@@ -2,7 +2,8 @@ import type { IDBPDatabase } from "idb";
|
|
|
2
2
|
export interface PutWithEvictionOptions {
|
|
3
3
|
key?: IDBValidKey;
|
|
4
4
|
dateKey: string;
|
|
5
|
-
|
|
5
|
+
/** Evict oldest when store count exceeds this. */
|
|
6
|
+
maxCount?: number;
|
|
6
7
|
/** Absolute expiry timestamp (ms). If set, used for the dateKey field on the value. */
|
|
7
8
|
expiresAt?: number;
|
|
8
9
|
/** Relative expiry in seconds. Used when expiresAt is not set; then dateKey = now + ttlSeconds * 1000. */
|
package/dist/putWithEviction.js
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { cleanWhenTooLarge } from "./cleanWhenTooLarge.js";
|
|
2
2
|
const DEFAULT_TTL_SECONDS = 3600;
|
|
3
|
+
const DEFAULT_MAX_COUNT = 1000;
|
|
3
4
|
/**
|
|
4
5
|
* Put a value in the store, then if count > maxCount evict oldest entries (by dateKey).
|
|
5
6
|
* Optionally set the dateKey field from expiresAt (absolute ms) or ttlSeconds (relative seconds).
|
|
6
7
|
* Value must include the keyPath field, or pass options.key.
|
|
7
8
|
*/
|
|
8
9
|
export async function putWithEviction(db, storeName, value, options) {
|
|
9
|
-
const { key, dateKey,
|
|
10
|
+
const { key, dateKey, expiresAt, ttlSeconds } = options;
|
|
11
|
+
const maxCount = options.maxCount ?? DEFAULT_MAX_COUNT;
|
|
10
12
|
if (typeof value === "object" && value !== null) {
|
|
11
13
|
const valueRecord = value;
|
|
12
14
|
if (expiresAt !== undefined) {
|
|
13
15
|
valueRecord[dateKey] = expiresAt;
|
|
14
16
|
}
|
|
15
17
|
else {
|
|
16
|
-
valueRecord[dateKey] =
|
|
18
|
+
valueRecord[dateKey] =
|
|
19
|
+
Date.now() + (ttlSeconds ?? DEFAULT_TTL_SECONDS) * 1000;
|
|
17
20
|
}
|
|
18
21
|
}
|
|
19
22
|
await db.put(storeName, value, key);
|
package/package.json
CHANGED