idb-refined 0.0.1 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # idb-refined
2
2
 
3
- Thin TypeScript IndexedDB helper on top of [idb](https://www.npmjs.com/package/idb): init DB with auto-version, clean by date or size, add with eviction, and delete helpers.
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,99 +12,45 @@ npm install idb-refined
12
12
 
13
13
  ## API
14
14
 
15
- | Function | Purpose |
16
- |----------|---------|
17
- | **initDb(name, options?)** | Open or create a DB. Version is auto-detected when you pass a declarative `schema`; optional manual `version` + `upgrade`. Returns idb’s `IDBPDatabase`. |
18
- | **cleanOldEntries(db, storeName, options)** | Delete entries where `dateKey` < `before` (timestamp ms). Options: `{ dateKey, before }`. Requires an index on `dateKey`. |
19
- | **cleanWhenTooLarge(db, storeName, options)** | Evict oldest entries (by `dateKey`) until count ≤ `maxCount`. Options: `{ dateKey, maxCount }`. Returns count deleted. |
20
- | **putWithEviction(db, storeName, value, options)** | Put value, then if store count > `maxCount` evict oldest by `dateKey`. Options: `{ key?, dateKey, maxCount }`. |
21
- | **deleteByKey(db, storeName, key)** | Delete a single entry by key. |
22
- | **clearStore(db, storeName)** | Delete all entries in a store. |
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
- ## Use cases
24
+ For details, see **[Advanced documentation](docs/advanced.md)**. Run the **[example](example/)** in the browser (see `example/README.md`).
26
25
 
27
- - **Cache with TTL:** initDb, put items with `expiresAt`, call `cleanOldEntries` on startup or interval.
28
- - **Cache with max size:** Use `putWithEviction` or call `cleanWhenTooLarge` after adding.
29
- - **Cache with TTL + max size:** `putWithEviction` + periodic `cleanOldEntries` for expired.
30
- - **Log / event buffer:** `putWithEviction` with `dateKey: 'createdAt'` and `maxCount`.
31
- - **Simple key-value:** initDb, `db.get` / `db.put` / `deleteByKey`; optional cleanup when too large.
32
-
33
- ## Examples
34
-
35
- ### Cache with TTL and max size
36
-
37
- ```ts
38
- import { initDb, putWithEviction, cleanOldEntries, deleteByKey } from "idb-refined";
39
-
40
- const db = await initDb("my-cache", {
41
- schema: {
42
- stores: {
43
- cache: { keyPath: "id", indexes: ["expiresAt"] },
44
- },
45
- },
46
- });
47
-
48
- // Add item; if store has more than 1000 entries, evict oldest
49
- await putWithEviction(
50
- db,
51
- "cache",
52
- { id: "k1", data: "v1", expiresAt: Date.now() + 3600 },
53
- { dateKey: "expiresAt", maxCount: 1000 }
54
- );
55
-
56
- // Periodic: remove expired
57
- await cleanOldEntries(db, "cache", {
58
- dateKey: "expiresAt",
59
- before: Date.now(),
60
- });
61
-
62
- // Delete one
63
- await deleteByKey(db, "cache", "k1");
64
- ```
65
-
66
- ### Log buffer (cap size)
26
+ ## Example
67
27
 
68
28
  ```ts
69
- await putWithEviction(
70
- db,
71
- "logs",
72
- { id: generateId(), message, createdAt: Date.now() },
73
- { dateKey: "createdAt", maxCount: 5000 }
74
- );
75
- ```
29
+ import { createClient } from "idb-refined";
76
30
 
77
- ### Manual cleanup when too large
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" });
78
34
 
79
- ```ts
80
- const deleted = await cleanWhenTooLarge(db, "cache", {
81
- dateKey: "expiresAt",
82
- maxCount: 500,
83
- });
84
- 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();
85
40
  ```
86
41
 
87
42
  ## Requirements
88
43
 
89
- - Stores that use `cleanOldEntries`, `cleanWhenTooLarge`, or `putWithEviction` must have an **index** on the date field (e.g. `expiresAt`, `createdAt`). Define it in your schema:
90
-
91
- ```ts
92
- schema: {
93
- stores: {
94
- cache: { keyPath: "id", indexes: ["expiresAt"] },
95
- },
96
- }
97
- ```
98
-
99
- - Eviction is **count-based** only (no byte-size or quota check).
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.
100
46
 
101
47
  ## Releasing
102
48
 
103
49
  1. Bump version: `pnpm version patch` (or `minor` / `major`).
104
50
  2. Commit and push: `git push && git push --tags`.
105
- 3. Pushing a tag matching `v*` (e.g. `v0.0.2`) triggers the [Publish to npm](.github/workflows/publish.yml) workflow, which runs build and `pnpm publish`.
51
+ 3. Pushing a tag matching `v*` triggers the [Publish to npm](.github/workflows/publish.yml) workflow.
106
52
 
107
- **Required:** Add an `NPM_TOKEN` secret in the repo (Settings → Secrets and variables → Actions). Use an npm [access token](https://www.npmjs.com/settings/~/tokens) or granular token with publish permission.
53
+ **Required:** Add an `NPM_TOKEN` secret in the repo (Settings → Secrets and variables → Actions).
108
54
 
109
55
  ## License
110
56
 
@@ -1,7 +1,8 @@
1
1
  import type { IDBPDatabase } from "idb";
2
2
  export interface CleanWhenTooLargeOptions {
3
3
  dateKey: string;
4
- maxCount: number;
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, maxCount } = options;
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;
@@ -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 { initDb } from "./initDb.js";
2
- export type { InitDbOptions } from "./initDb.js";
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 { initDb } from "./initDb.js";
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,10 +2,16 @@ import type { IDBPDatabase } from "idb";
2
2
  export interface PutWithEvictionOptions {
3
3
  key?: IDBValidKey;
4
4
  dateKey: string;
5
- maxCount: number;
5
+ /** Evict oldest when store count exceeds this. */
6
+ maxCount?: number;
7
+ /** Absolute expiry timestamp (ms). If set, used for the dateKey field on the value. */
8
+ expiresAt?: number;
9
+ /** Relative expiry in seconds. Used when expiresAt is not set; then dateKey = now + ttlSeconds * 1000. */
10
+ ttlSeconds?: number;
6
11
  }
7
12
  /**
8
13
  * Put a value in the store, then if count > maxCount evict oldest entries (by dateKey).
14
+ * Optionally set the dateKey field from expiresAt (absolute ms) or ttlSeconds (relative seconds).
9
15
  * Value must include the keyPath field, or pass options.key.
10
16
  */
11
17
  export declare function putWithEviction<T = unknown>(db: IDBPDatabase<unknown>, storeName: string, value: T, options: PutWithEvictionOptions): Promise<void>;
@@ -1,10 +1,24 @@
1
1
  import { cleanWhenTooLarge } from "./cleanWhenTooLarge.js";
2
+ const DEFAULT_TTL_SECONDS = 3600;
3
+ const DEFAULT_MAX_COUNT = 1000;
2
4
  /**
3
5
  * Put a value in the store, then if count > maxCount evict oldest entries (by dateKey).
6
+ * Optionally set the dateKey field from expiresAt (absolute ms) or ttlSeconds (relative seconds).
4
7
  * Value must include the keyPath field, or pass options.key.
5
8
  */
6
9
  export async function putWithEviction(db, storeName, value, options) {
7
- const { key, dateKey, maxCount } = options;
10
+ const { key, dateKey, expiresAt, ttlSeconds } = options;
11
+ const maxCount = options.maxCount ?? DEFAULT_MAX_COUNT;
12
+ if (typeof value === "object" && value !== null) {
13
+ const valueRecord = value;
14
+ if (expiresAt !== undefined) {
15
+ valueRecord[dateKey] = expiresAt;
16
+ }
17
+ else {
18
+ valueRecord[dateKey] =
19
+ Date.now() + (ttlSeconds ?? DEFAULT_TTL_SECONDS) * 1000;
20
+ }
21
+ }
8
22
  await db.put(storeName, value, key);
9
23
  const count = await db.count(storeName);
10
24
  if (count > maxCount) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "idb-refined",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Thin TypeScript IndexedDB helper: initDb (auto-version), clean by date/size, add with eviction, delete helpers",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -16,6 +16,16 @@
16
16
  "files": [
17
17
  "dist"
18
18
  ],
19
+ "scripts": {
20
+ "build": "tsc -p tsconfig.build.json",
21
+ "lint": "eslint src",
22
+ "lint:fix": "eslint src --fix",
23
+ "format": "prettier --write \"src/**/*.ts\"",
24
+ "format:check": "prettier --check \"src/**/*.ts\"",
25
+ "test": "vitest run",
26
+ "test:watch": "vitest",
27
+ "prepare": "husky"
28
+ },
19
29
  "lint-staged": {
20
30
  "src/**/*.ts": [
21
31
  "eslint --fix",
@@ -46,14 +56,5 @@
46
56
  "browser",
47
57
  "storage"
48
58
  ],
49
- "license": "MIT",
50
- "scripts": {
51
- "build": "tsc -p tsconfig.build.json",
52
- "lint": "eslint src",
53
- "lint:fix": "eslint src --fix",
54
- "format": "prettier --write \"src/**/*.ts\"",
55
- "format:check": "prettier --check \"src/**/*.ts\"",
56
- "test": "vitest run",
57
- "test:watch": "vitest"
58
- }
59
- }
59
+ "license": "MIT"
60
+ }