idb-repo 1.0.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 +270 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/src/connection.d.ts +36 -0
- package/dist/src/connection.d.ts.map +1 -0
- package/dist/src/internal/cache.d.ts +38 -0
- package/dist/src/internal/cache.d.ts.map +1 -0
- package/dist/src/internal/cursor.d.ts +21 -0
- package/dist/src/internal/cursor.d.ts.map +1 -0
- package/dist/src/internal/idb-utils.d.ts +12 -0
- package/dist/src/internal/idb-utils.d.ts.map +1 -0
- package/dist/src/internal/ttl.d.ts +14 -0
- package/dist/src/internal/ttl.d.ts.map +1 -0
- package/dist/src/internal/validation.d.ts +13 -0
- package/dist/src/internal/validation.d.ts.map +1 -0
- package/dist/src/internal/value-codec.d.ts +39 -0
- package/dist/src/internal/value-codec.d.ts.map +1 -0
- package/dist/src/kv.d.ts +34 -0
- package/dist/src/kv.d.ts.map +1 -0
- package/dist/src/lib-wrapper.d.ts +25 -0
- package/dist/src/lib-wrapper.d.ts.map +1 -0
- package/dist/src/lib.d.ts +6 -0
- package/dist/src/lib.d.ts.map +1 -0
- package/dist/src/time-utils.d.ts +4 -0
- package/dist/src/time-utils.d.ts.map +1 -0
- package/dist/src/types.d.ts +57 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/web-container.d.ts +25 -0
- package/dist/src/web-container.d.ts.map +1 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# idb-repo
|
|
2
|
+
|
|
3
|
+
Edge KV Storage.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This SDK provides a small, dependency-free abstraction over IndexedDB for building fast, reliable key-value stores in browser and edge-like environments. It focuses on predictable behavior, minimal surface area, and performance characteristics suitable for long-lived client applications.
|
|
8
|
+
|
|
9
|
+
The goal is not to hide IndexedDB, but to make it practical: sane defaults, explicit structure, and a repository pattern that enforces consistency without adding unnecessary complexity.
|
|
10
|
+
|
|
11
|
+
## Design Principles
|
|
12
|
+
SOLID PRINCIPLES w/
|
|
13
|
+
- **Zero dependencies** — no runtime bloat, no transitive risk
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## What This Is
|
|
17
|
+
- A thin repository abstraction over IndexedDB
|
|
18
|
+
- A predictable KV interface with typed boundaries
|
|
19
|
+
- A foundation for local-first and offline-capable systems
|
|
20
|
+
|
|
21
|
+
## What This Is Not
|
|
22
|
+
|
|
23
|
+
- An ORM
|
|
24
|
+
- A sync engine
|
|
25
|
+
- A framework replacement
|
|
26
|
+
- A polyfill for non-browser runtimes
|
|
27
|
+
|
|
28
|
+
## Use Cases
|
|
29
|
+
|
|
30
|
+
- Edge-adjacent web apps (Workers + WebViews)
|
|
31
|
+
- Local-first applications
|
|
32
|
+
- Durable client caches
|
|
33
|
+
- Structured persistence for complex frontends
|
|
34
|
+
- Performance-sensitive UI state storage
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install idb-repo
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
### Basic Setup
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { createIndexedDbKV } from "idb-repo";
|
|
48
|
+
|
|
49
|
+
// Create a KV store instance
|
|
50
|
+
const kv = createIndexedDbKV({
|
|
51
|
+
dbName: "my-app", // IndexedDB database name (default: "kv")
|
|
52
|
+
storeName: "cache", // Object store name (default: "kv")
|
|
53
|
+
version: 1, // Schema version for migrations
|
|
54
|
+
cacheEntries: 2048 // In-memory LRU cache size (default: 2048)
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Core Operations
|
|
59
|
+
|
|
60
|
+
#### Put (Store Data)
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
// Store a string
|
|
64
|
+
await kv.put("key1", "value");
|
|
65
|
+
|
|
66
|
+
// Store JSON
|
|
67
|
+
await kv.put("user:123", { id: 123, name: "Alice" });
|
|
68
|
+
|
|
69
|
+
// Store with metadata and expiration
|
|
70
|
+
await kv.put("session:abc", { token: "xyz" }, {
|
|
71
|
+
metadata: { userId: "123" },
|
|
72
|
+
expirationTtl: 3600 // expires in 1 hour
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Store ArrayBuffer/binary data
|
|
76
|
+
const buffer = new TextEncoder().encode("binary data");
|
|
77
|
+
await kv.put("binary-key", buffer);
|
|
78
|
+
|
|
79
|
+
// Store File or Blob objects
|
|
80
|
+
const file = document.querySelector("input[type=file]")!.files![0];
|
|
81
|
+
await kv.put("files/myfile", file);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
#### Get (Retrieve Data)
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// Get raw value (type determined by stored type)
|
|
88
|
+
const value = await kv.get("key1");
|
|
89
|
+
|
|
90
|
+
// Get with metadata
|
|
91
|
+
const { value, metadata } = await kv.getWithMetadata("session:abc");
|
|
92
|
+
|
|
93
|
+
// Helper functions for typed retrieval
|
|
94
|
+
const text = await kvGetText(kv, "key1");
|
|
95
|
+
const json = await kvGetJson(kv, "user:123");
|
|
96
|
+
const buffer = await kvGetArrayBuffer(kv, "binary-key");
|
|
97
|
+
const stream = await kvGetStream(kv, "large-file");
|
|
98
|
+
|
|
99
|
+
// Get with custom cache TTL (bypasses storage expiration)
|
|
100
|
+
const cached = await kvGetJson(kv, "user:123", 300); // 5 min cache
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### Delete
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// Delete a key
|
|
107
|
+
await kv.delete("key1");
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
#### List (Enumerate Keys)
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// List all keys
|
|
114
|
+
const result = await kv.list();
|
|
115
|
+
// { keys: ["key1", "user:123", ...], cursor: "..." }
|
|
116
|
+
|
|
117
|
+
// List with pagination
|
|
118
|
+
const page = await kv.list({
|
|
119
|
+
limit: 10,
|
|
120
|
+
cursor: "previous-cursor"
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// List with key prefix filter
|
|
124
|
+
const userKeys = await kv.list({
|
|
125
|
+
prefix: "user:",
|
|
126
|
+
limit: 50
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
#### Close
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// Clean up resources
|
|
134
|
+
await kv.close();
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Advanced Usage
|
|
138
|
+
|
|
139
|
+
#### Metadata and Type Hints
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
// Store data with custom metadata
|
|
143
|
+
await kv.put("document:42", documentData, {
|
|
144
|
+
metadata: {
|
|
145
|
+
userId: "user:123",
|
|
146
|
+
createdAt: new Date().toISOString(),
|
|
147
|
+
contentType: "application/json"
|
|
148
|
+
},
|
|
149
|
+
expirationTtl: 86400 // 24 hours
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Retrieve and use metadata
|
|
153
|
+
const { value, metadata } = await kv.getWithMetadata("document:42");
|
|
154
|
+
if (metadata?.userId === "user:123") {
|
|
155
|
+
// Process owned document
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
#### Performance Patterns
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// Batch operations use getAll() internally for ~3-5x better throughput
|
|
163
|
+
const result = await kv.list({ limit: 1000 });
|
|
164
|
+
|
|
165
|
+
// In-memory LRU cache speeds up repeated reads
|
|
166
|
+
// First read: ~5-10ms (IndexedDB)
|
|
167
|
+
// Subsequent reads of same key: ~0.1ms (memory)
|
|
168
|
+
for (let i = 0; i < 100; i++) {
|
|
169
|
+
const user = await kvGetJson(kv, "user:123"); // Cache hit after first call
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Readonly transactions for lists are faster than readwrite
|
|
173
|
+
// Expired records are lazily deleted on read (not on list)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### Storing Files
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
// Store a File object (from file input)
|
|
180
|
+
const fileInput = document.querySelector("input[type=file]") as HTMLInputElement;
|
|
181
|
+
const file = fileInput.files![0];
|
|
182
|
+
await kv.put(`uploads/${file.name}`, file);
|
|
183
|
+
|
|
184
|
+
// Store a Blob
|
|
185
|
+
const blob = new Blob(["Hello, world!"], { type: "text/plain" });
|
|
186
|
+
await kv.put("greeting.txt", blob);
|
|
187
|
+
|
|
188
|
+
// Store binary ArrayBuffer
|
|
189
|
+
const uint8Array = new Uint8Array([1, 2, 3, 4, 5]);
|
|
190
|
+
await kv.put("binary-data", uint8Array);
|
|
191
|
+
|
|
192
|
+
// Store a ReadableStream (useful for large files or streaming uploads)
|
|
193
|
+
const stream = response.body; // from fetch()
|
|
194
|
+
await kv.put("large-file", stream);
|
|
195
|
+
|
|
196
|
+
// Retrieve files
|
|
197
|
+
const file = await kv.get("uploads/myfile.pdf") as ArrayBuffer;
|
|
198
|
+
const fileBlob = await kvGetArrayBuffer(kv, "uploads/myfile.pdf");
|
|
199
|
+
|
|
200
|
+
// Stream large files efficiently
|
|
201
|
+
const largeFile = await kvGetStream(kv, "large-file");
|
|
202
|
+
await largeFile.pipeTo(writableStream);
|
|
203
|
+
|
|
204
|
+
// List all stored files with metadata
|
|
205
|
+
const result = await kv.list({ prefix: "uploads/" });
|
|
206
|
+
for (const file of result.keys) {
|
|
207
|
+
console.log(`${file.name} - uploaded at ${file.metadata?.uploadedAt}`);
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
#### Building Local-First Features
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
// Store user preferences
|
|
215
|
+
await kv.put("settings:display", {
|
|
216
|
+
theme: "dark",
|
|
217
|
+
fontSize: 14,
|
|
218
|
+
sidebarCollapsed: true
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Cache API responses
|
|
222
|
+
await kv.put("posts:feed", postsData, {
|
|
223
|
+
expirationTtl: 300, // 5 minute cache
|
|
224
|
+
metadata: { fetchedAt: Date.now() }
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Store offline queue
|
|
228
|
+
await kv.put(`pending:create:${uuid()}`, actionPayload, {
|
|
229
|
+
metadata: { type: "create", priority: 1 }
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Enumerate pending actions
|
|
233
|
+
const { keys } = await kv.list({ prefix: "pending:" });
|
|
234
|
+
for (const key of keys) {
|
|
235
|
+
const action = await kvGetJson(kv, key);
|
|
236
|
+
await syncAction(action);
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## API Reference
|
|
241
|
+
|
|
242
|
+
### `IndexedDbKV` Class
|
|
243
|
+
|
|
244
|
+
- **`constructor(opts?)`** — Create a new KV store instance
|
|
245
|
+
- `dbName` (string): IndexedDB database name
|
|
246
|
+
- `storeName` (string): Object store name
|
|
247
|
+
- `version` (number): Schema version
|
|
248
|
+
- `cacheEntries` (number): LRU cache size
|
|
249
|
+
|
|
250
|
+
- **`get(key, options?)`** → Promise — Retrieve a value
|
|
251
|
+
- **`getWithMetadata(key, options?)`** → Promise — Retrieve value and metadata
|
|
252
|
+
- **`put(key, value, options?)`** → Promise — Store a value
|
|
253
|
+
- **`delete(key)`** → Promise — Delete a key
|
|
254
|
+
- **`list(options?)`** → Promise — Enumerate keys with pagination
|
|
255
|
+
- **`close()`** → Promise — Close database connection
|
|
256
|
+
|
|
257
|
+
### Helper Functions
|
|
258
|
+
|
|
259
|
+
- **`kvGetText(kv, key, cacheTtl?)`** — Get value as string
|
|
260
|
+
- **`kvGetJson(kv, key, cacheTtl?)`** — Get value as JSON
|
|
261
|
+
- **`kvGetArrayBuffer(kv, key, cacheTtl?)`** — Get value as ArrayBuffer
|
|
262
|
+
- **`kvGetStream(kv, key, cacheTtl?)`** — Get value as ReadableStream
|
|
263
|
+
|
|
264
|
+
## Status
|
|
265
|
+
|
|
266
|
+
This project is intentionally small and opinionated. APIs are stable where exposed, but evolution is expected as real-world constraints surface.
|
|
267
|
+
|
|
268
|
+
## License
|
|
269
|
+
|
|
270
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IndexedDB KV Storage - A performant KV storage implementation using IndexedDB
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { createIndexedDbKV } from '@indexeddb-kv/storage';
|
|
7
|
+
*
|
|
8
|
+
* const kv = await createIndexedDbKV({ dbName: 'my-app' });
|
|
9
|
+
* await kv.put('key', 'value');
|
|
10
|
+
* const value = await kv.get('key');
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export { IndexedDbKV, createIndexedDbKV, kvGetText, kvGetJson, kvGetArrayBuffer, kvGetStream, } from "./src/lib";
|
|
14
|
+
export type { KVGetOptions, KVPutOptions, KVListOptions, KVListKey, KVListResult, KVNamespace, } from "./src/types";
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EACH,WAAW,EACX,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,gBAAgB,EAChB,WAAW,GACd,MAAM,WAAW,CAAC;AAGnB,YAAY,EACR,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,SAAS,EACT,YAAY,EACZ,WAAW,GACd,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class S{cfg;dbPromise=null;constructor(G){this.cfg=G}get db(){if(!this.dbPromise)this.dbPromise=this.open();return this.dbPromise}async close(){if(!this.dbPromise)return;try{(await this.dbPromise).close()}finally{this.dbPromise=null}}open(){let{dbName:G,version:X,storeName:Q}=this.cfg;return new Promise((Z,Y)=>{let _=indexedDB.open(G,X);_.onupgradeneeded=()=>{let P=_.result;if(!P.objectStoreNames.contains(Q)){let $=P.createObjectStore(Q,{keyPath:"key"});$.createIndex("key","key",{unique:!0}),$.createIndex("expiresAt","expiresAt",{unique:!1})}else{let $=_.transaction.objectStore(Q);if(!$.indexNames.contains("expiresAt"))$.createIndex("expiresAt","expiresAt",{unique:!1});if(!$.indexNames.contains("key"))$.createIndex("key","key",{unique:!0})}},_.onsuccess=()=>{let P=_.result;P.onversionchange=()=>{try{P.close()}catch{}},Z(P)},_.onerror=()=>Y(_.error??new Error("Failed to open IndexedDB")),_.onblocked=()=>{Y(new Error("IndexedDB open blocked (another context has the DB open during upgrade)"))}})}}function K(G){return Math.floor(G/1000)}function R(G){return Math.floor(G*1000)}function J(){return Date.now()}class V{max;map=new Map;constructor(G){this.max=Math.max(0,G|0)}get(G){let X=this.map.get(G);if(!X)return null;if(X.expiresAt<=J())return this.map.delete(G),null;return{value:X.value,meta:X.meta}}set(G,X,Q,Z){if(this.max<=0)return;let Y=J()+Math.max(0,Z)*1000;this.map.set(G,{expiresAt:Y,value:X,meta:Q});while(this.map.size>this.max){let _=this.map.keys().next().value;if(_===void 0)break;this.map.delete(_)}}delete(G){this.map.delete(G)}clear(){this.map.clear()}}function q(G){let X=JSON.stringify(G);return btoa(unescape(encodeURIComponent(X))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/g,"")}function f(G){try{let X=G.replace(/-/g,"+").replace(/_/g,"/")+"===".slice((G.length+3)%4),Q=decodeURIComponent(escape(atob(X))),Z=JSON.parse(Q);if(!Z||Z.v!==1||typeof Z.prefix!=="string")return null;if(Z.after!==null&&typeof Z.after!=="string")return null;return Z}catch{return null}}function j(G){return!!G&&typeof G==="object"&&"buffer"in G&&G.buffer instanceof ArrayBuffer}function B(G){if(typeof G!=="string"||G.length===0)throw new TypeError("KV key must be a non-empty string")}function n(G){if(typeof G==="string")return{encoding:"text",stored:G};if(G instanceof Blob)return{encoding:"binary",stored:G};if(G instanceof ArrayBuffer)return{encoding:"binary",stored:new Blob([G])};if(j(G))return{encoding:"binary",stored:new Blob([G.buffer.slice(0)])};if(G instanceof ReadableStream)return null;return{encoding:"clone",stored:structuredClone(G)}}async function w(G){let X=n(G);if(X!==null)return X;return{encoding:"binary",stored:await c(G)}}async function c(G){let X=G.getReader(),Q=[];try{while(!0){let{value:Z,done:Y}=await X.read();if(Y)break;if(Z)Q.push(Z)}}finally{try{X.releaseLock()}catch{}}return new Blob(Q)}function m(G,X){let Q=X??"text";if(G.encoding==="text"){let Y=G.value;if(Q==="text")return Y;if(Q==="json")return JSON.parse(Y);if(Q==="arrayBuffer")return new TextEncoder().encode(Y).buffer;return new Blob([Y]).stream()}if(G.encoding==="json"){let Y=G.value;if(Q==="json")return JSON.parse(Y);if(Q==="text")return Y;if(Q==="arrayBuffer")return new TextEncoder().encode(Y).buffer;return new Blob([Y]).stream()}if(G.encoding==="clone"){let Y=G.value;if(Q==="json")return Y;let _=JSON.stringify(Y);if(_===void 0)throw new TypeError("Stored value cannot be represented as JSON text");if(Q==="text")return _;if(Q==="arrayBuffer")return new TextEncoder().encode(_).buffer;return new Blob([_]).stream()}let Z=G.value;if(Q==="stream")return Z.stream();if(Q==="arrayBuffer")return Z;if(Q==="json")return Z;return Z}async function T(G){return await G.text()}async function h(G){return await G.arrayBuffer()}async function x(G){let X=await G.text();return JSON.parse(X)}function N(G){return new Promise((X,Q)=>{G.onsuccess=()=>X(G.result),G.onerror=()=>Q(G.error??new Error("IndexedDB request failed"))})}function M(G){return new Promise((X,Q)=>{G.oncomplete=()=>X(),G.onabort=()=>Q(G.error??new Error("IndexedDB transaction aborted")),G.onerror=()=>Q(G.error??new Error("IndexedDB transaction failed"))})}function b(G){if(!G)return null;if(typeof G.expirationTtl==="number"){let X=Math.max(0,G.expirationTtl)*1000;return J()+X}if(typeof G.expiration==="number")return R(G.expiration);return null}function v(G){return G.expiresAt!==null&&G.expiresAt<=J()}class D{conn;storeName;cache;constructor(G){let X=G?.dbName??"kv";this.storeName=G?.storeName??"kv";let Q=G?.version??1;this.conn=new S({dbName:X,storeName:this.storeName,version:Q}),this.cache=new V(G?.cacheEntries??2048)}invalidateCache(G){this.cache.delete(`${G}::text`),this.cache.delete(`${G}::json`),this.cache.delete(`${G}::arrayBuffer`),this.cache.delete(`${G}::stream`)}async get(G,X){let{value:Q}=await this.getWithMetadata(G,X);return Q}async getWithMetadata(G,X){B(G);let Q=X?.type??"text",Z=X?.cacheTtl??0,Y=Z>0?`${G}::${Q}`:null;if(Y){let F=this.cache.get(Y);if(F)return{value:F.value,metadata:F.meta}}let $=(await this.conn.db).transaction(this.storeName,"readonly").objectStore(this.storeName),W=await N($.get(G));if(!W)return{value:null,metadata:null};if(v(W))return this.delete(G),{value:null,metadata:null};let H=m(W,Q);if(W.encoding==="binary"){let F=H;if(Q==="stream")H=F.stream()??null;else if(Q==="arrayBuffer")H=await h(F);else if(Q==="json")H=await x(F);else H=await T(F)}let O=W.metadata??null;if(Y)this.cache.set(Y,H,O,Z);return{value:H,metadata:O}}async put(G,X,Q){B(G);let{encoding:Z,stored:Y}=await w(X),_=b(Q),$=(await this.conn.db).transaction(this.storeName,"readwrite"),W=$.objectStore(this.storeName),H=J(),O={key:G,value:Y,encoding:Z,expiresAt:_,metadata:Q?.metadata??null,createdAt:H,updatedAt:H};W.put(O),this.invalidateCache(G),await M($)}async delete(G){B(G);let Q=(await this.conn.db).transaction(this.storeName,"readwrite");Q.objectStore(this.storeName).delete(G),this.invalidateCache(G),await M(Q)}async list(G){let X=G?.prefix??"",Q=Math.min(Math.max(1,G?.limit??1000),1e4),Z=G?.cursor??null,Y=null;if(Z){let L=f(Z);if(L&&L.prefix===X)Y=L.after}let $=(await this.conn.db).transaction(this.storeName,"readonly").objectStore(this.storeName),W=X,H=X+"",O=IDBKeyRange.bound(W,H,!1,!1),F=await N($.getAll(O)),U=[],g=!0,A=null,i=J(),C=Y!==null?F.findIndex((L)=>L.key>Y):0,E=C<0?F.length:C;for(let L=E;L<F.length&&U.length<Q;L++){let z=F[L];if(!z)continue;if(z.expiresAt&&z.expiresAt<=i)continue;let I={name:z.key};if(z.expiresAt)I.expiration=K(z.expiresAt);if(z.metadata)I.metadata=z.metadata;U.push(I),A=z.key}if(U.length>=Q&&E+U.length<F.length)g=!1;if(!g&&A!==null){let L=q({v:1,prefix:X,after:A});return{keys:U,list_complete:!1,cursor:L}}return{keys:U,list_complete:!0}}async close(){await this.conn.close(),this.cache.clear()}}function k(G){return new D(G)}async function u(G,X,Q){return await G.get(X,{type:"text",cacheTtl:Q})}async function p(G,X,Q){return await G.get(X,{type:"json",cacheTtl:Q})}async function y(G,X,Q){return await G.get(X,{type:"arrayBuffer",cacheTtl:Q})}async function d(G,X,Q){return await G.get(X,{type:"stream",cacheTtl:Q})}export{u as kvGetText,d as kvGetStream,p as kvGetJson,y as kvGetArrayBuffer,k as createIndexedDbKV,D as IndexedDbKV};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IndexedDB connection management
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Configuration for opening an IndexedDB database
|
|
6
|
+
*/
|
|
7
|
+
type OpenConfig = {
|
|
8
|
+
dbName: string;
|
|
9
|
+
storeName: string;
|
|
10
|
+
version: number;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Manages lifecycle of an IndexedDB connection
|
|
14
|
+
* - Lazy initialization (opens on first access)
|
|
15
|
+
* - Version change handling
|
|
16
|
+
* - Automatic schema creation/migration
|
|
17
|
+
*/
|
|
18
|
+
export declare class IndexedDbConnection {
|
|
19
|
+
private cfg;
|
|
20
|
+
private dbPromise;
|
|
21
|
+
constructor(cfg: OpenConfig);
|
|
22
|
+
/**
|
|
23
|
+
* Get or open the database
|
|
24
|
+
*/
|
|
25
|
+
get db(): Promise<IDBDatabase>;
|
|
26
|
+
/**
|
|
27
|
+
* Close the database connection
|
|
28
|
+
*/
|
|
29
|
+
close(): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Open the database with automatic schema creation/migration
|
|
32
|
+
*/
|
|
33
|
+
private open;
|
|
34
|
+
}
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=connection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/connection.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;GAEG;AACH,KAAK,UAAU,GAAG;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;GAKG;AACH,qBAAa,mBAAmB;IAC5B,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,SAAS,CAAqC;gBAE1C,GAAG,EAAE,UAAU;IAI3B;;OAEG;IACH,IAAI,EAAE,IAAI,OAAO,CAAC,WAAW,CAAC,CAG7B;IAED;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAU5B;;OAEG;IACH,OAAO,CAAC,IAAI;CA0Cf"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TinyLRU: Small, best-effort, TTL-based in-memory cache for KV reads
|
|
3
|
+
* - Keeps decoded values per (key, type)
|
|
4
|
+
* - Eviction: simple Map insertion order (best-effort)
|
|
5
|
+
* - TTL: automatic expiration on access
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Small, best-effort, TTL-based in-memory cache for reads
|
|
9
|
+
* Preserves "no recency refresh" optimization: doesn't refresh cache entry on hit
|
|
10
|
+
* to avoid 2 Map operations per cache hit. LRU eviction is best-effort anyway,
|
|
11
|
+
* and TTL makes staleness acceptable.
|
|
12
|
+
*/
|
|
13
|
+
export declare class TinyLRU {
|
|
14
|
+
private max;
|
|
15
|
+
private map;
|
|
16
|
+
constructor(maxEntries: number);
|
|
17
|
+
/**
|
|
18
|
+
* Get a value from cache
|
|
19
|
+
* @returns Cached value and metadata, or null if not found or expired
|
|
20
|
+
*/
|
|
21
|
+
get(k: string): {
|
|
22
|
+
value: any;
|
|
23
|
+
meta: any;
|
|
24
|
+
} | null;
|
|
25
|
+
/**
|
|
26
|
+
* Set a value in cache with TTL
|
|
27
|
+
*/
|
|
28
|
+
set(k: string, value: any, meta: any, ttlSeconds: number): void;
|
|
29
|
+
/**
|
|
30
|
+
* Delete a specific key from cache
|
|
31
|
+
*/
|
|
32
|
+
delete(k: string): void;
|
|
33
|
+
/**
|
|
34
|
+
* Clear all entries from cache
|
|
35
|
+
*/
|
|
36
|
+
clear(): void;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/internal/cache.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;;;GAKG;AACH,qBAAa,OAAO;IAChB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,GAAG,CAAmE;gBAElE,UAAU,EAAE,MAAM;IAI9B;;;OAGG;IACH,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,GAAG,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAE,GAAG,IAAI;IAYhD;;OAEG;IACH,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAY/D;;OAEG;IACH,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAIvB;;OAEG;IACH,KAAK,IAAI,IAAI;CAGhB"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cursor encoding/decoding for list pagination
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Internal structure for list pagination cursor
|
|
6
|
+
*/
|
|
7
|
+
export type InternalListCursor = {
|
|
8
|
+
v: 1;
|
|
9
|
+
prefix: string;
|
|
10
|
+
after: string | null;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Encode a cursor to a base64url string
|
|
14
|
+
*/
|
|
15
|
+
export declare function encodeCursor(c: InternalListCursor): string;
|
|
16
|
+
/**
|
|
17
|
+
* Decode a cursor from a base64url string
|
|
18
|
+
* @returns Decoded cursor or null if invalid
|
|
19
|
+
*/
|
|
20
|
+
export declare function decodeCursor(cursor: string): InternalListCursor | null;
|
|
21
|
+
//# sourceMappingURL=cursor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../../src/internal/cursor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC7B,CAAC,EAAE,CAAC,CAAC;IACL,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,CAAC;AAEF;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAK1D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAWtE"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IndexedDB promise wrappers and utilities
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Convert an IDBRequest to a Promise
|
|
6
|
+
*/
|
|
7
|
+
export declare function promisifyRequest<T>(req: IDBRequest<T>): Promise<T>;
|
|
8
|
+
/**
|
|
9
|
+
* Wait for an IDBTransaction to complete
|
|
10
|
+
*/
|
|
11
|
+
export declare function waitTx(tx: IDBTransaction): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=idb-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idb-utils.d.ts","sourceRoot":"","sources":["../../../src/internal/idb-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAKlE;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAMxD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TTL utilities for KV storage
|
|
3
|
+
*/
|
|
4
|
+
import type { KVPutOptions, StoredRecord } from "../types";
|
|
5
|
+
/**
|
|
6
|
+
* Compute expiration timestamp in milliseconds from KV put options
|
|
7
|
+
* @returns Expiration time in ms since epoch, or null if no expiration set
|
|
8
|
+
*/
|
|
9
|
+
export declare function computeExpiresAtMs(options?: KVPutOptions): number | null;
|
|
10
|
+
/**
|
|
11
|
+
* Check if a stored record has expired
|
|
12
|
+
*/
|
|
13
|
+
export declare function isExpired(rec: StoredRecord): boolean;
|
|
14
|
+
//# sourceMappingURL=ttl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ttl.d.ts","sourceRoot":"","sources":["../../../src/internal/ttl.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAG3D;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAUxE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAEpD"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type guards and validation utilities for KV storage
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Type guard to check if a value is an ArrayBufferView (TypedArray, DataView, etc.)
|
|
6
|
+
*/
|
|
7
|
+
export declare function isArrayBufferView(v: unknown): v is ArrayBufferView;
|
|
8
|
+
/**
|
|
9
|
+
* Validate and assert that a key is a non-empty string
|
|
10
|
+
* @throws {TypeError} if key is invalid
|
|
11
|
+
*/
|
|
12
|
+
export declare function assertKey(key: string): void;
|
|
13
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/internal/validation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,eAAe,CAElE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAI3C"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Value encoding/decoding for KV storage
|
|
3
|
+
* Handles multiple formats: text, json, structured clone, and binary
|
|
4
|
+
*/
|
|
5
|
+
import type { KVGetType, KVValue, StoredEncoding, StoredRecord } from "../types";
|
|
6
|
+
/**
|
|
7
|
+
* Sync path for encoding values - optimized for common cases like strings
|
|
8
|
+
* @returns Encoded value or null if async handling required (ReadableStream)
|
|
9
|
+
*/
|
|
10
|
+
export declare function normalizePutValueSync(value: KVValue): {
|
|
11
|
+
encoding: StoredEncoding;
|
|
12
|
+
stored: unknown;
|
|
13
|
+
} | null;
|
|
14
|
+
/**
|
|
15
|
+
* Async wrapper for encoding values
|
|
16
|
+
* Handles all value types including ReadableStream
|
|
17
|
+
*/
|
|
18
|
+
export declare function normalizePutValue(value: KVValue): Promise<{
|
|
19
|
+
encoding: StoredEncoding;
|
|
20
|
+
stored: unknown;
|
|
21
|
+
}>;
|
|
22
|
+
/**
|
|
23
|
+
* Decode a stored record to the requested type
|
|
24
|
+
* Returns decoded value or Blob sentinel for binary data requiring async conversion
|
|
25
|
+
*/
|
|
26
|
+
export declare function decodeValue(rec: StoredRecord, type: KVGetType | undefined): string | ArrayBuffer | ReadableStream<Uint8Array> | unknown;
|
|
27
|
+
/**
|
|
28
|
+
* Convert Blob to text
|
|
29
|
+
*/
|
|
30
|
+
export declare function blobToText(blob: Blob): Promise<string>;
|
|
31
|
+
/**
|
|
32
|
+
* Convert Blob to ArrayBuffer
|
|
33
|
+
*/
|
|
34
|
+
export declare function blobToArrayBuffer(blob: Blob): Promise<ArrayBuffer>;
|
|
35
|
+
/**
|
|
36
|
+
* Convert Blob to JSON (parse as UTF-8 text then JSON)
|
|
37
|
+
*/
|
|
38
|
+
export declare function blobToJson(blob: Blob): Promise<unknown>;
|
|
39
|
+
//# sourceMappingURL=value-codec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"value-codec.d.ts","sourceRoot":"","sources":["../../../src/internal/value-codec.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGjF;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG;IAAE,QAAQ,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAgB1G;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC,CAQ9G;AAwBD;;;GAGG;AACH,wBAAgB,WAAW,CACvB,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,SAAS,GAAG,SAAS,GAC5B,MAAM,GAAG,WAAW,GAAG,cAAc,CAAC,UAAU,CAAC,GAAG,OAAO,CAmD7D;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAE5D;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAExE;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAG7D"}
|
package/dist/src/kv.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IndexedDbKV: High-performance KV over IndexedDB
|
|
3
|
+
*/
|
|
4
|
+
import type { KVGetOptions, KVListOptions, KVListResult, KVNamespace, KVPutOptions, KVValue } from "./types";
|
|
5
|
+
/**
|
|
6
|
+
* High-performance KV implementation over IndexedDB
|
|
7
|
+
*/
|
|
8
|
+
export declare class IndexedDbKV implements KVNamespace {
|
|
9
|
+
private conn;
|
|
10
|
+
private storeName;
|
|
11
|
+
private cache;
|
|
12
|
+
constructor(opts?: {
|
|
13
|
+
dbName?: string;
|
|
14
|
+
storeName?: string;
|
|
15
|
+
version?: number;
|
|
16
|
+
cacheEntries?: number;
|
|
17
|
+
});
|
|
18
|
+
private invalidateCache;
|
|
19
|
+
get(key: string, options?: KVGetOptions): Promise<string | ArrayBuffer | ReadableStream<Uint8Array> | unknown | null>;
|
|
20
|
+
getWithMetadata<T = unknown>(key: string, options?: KVGetOptions): Promise<{
|
|
21
|
+
value: string | ArrayBuffer | ReadableStream<Uint8Array> | unknown | null;
|
|
22
|
+
metadata: T | null;
|
|
23
|
+
}>;
|
|
24
|
+
put(key: string, value: KVValue, options?: KVPutOptions): Promise<void>;
|
|
25
|
+
delete(key: string): Promise<void>;
|
|
26
|
+
list(options?: KVListOptions): Promise<KVListResult>;
|
|
27
|
+
close(): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
export declare function createIndexedDbKV(opts?: ConstructorParameters<typeof IndexedDbKV>[0]): KVNamespace;
|
|
30
|
+
export declare function kvGetText(kv: KVNamespace, key: string, cacheTtl?: number): Promise<string | null>;
|
|
31
|
+
export declare function kvGetJson<T>(kv: KVNamespace, key: string, cacheTtl?: number): Promise<T | null>;
|
|
32
|
+
export declare function kvGetArrayBuffer(kv: KVNamespace, key: string, cacheTtl?: number): Promise<ArrayBuffer | null>;
|
|
33
|
+
export declare function kvGetStream(kv: KVNamespace, key: string, cacheTtl?: number): Promise<ReadableStream<Uint8Array> | null>;
|
|
34
|
+
//# sourceMappingURL=kv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kv.d.ts","sourceRoot":"","sources":["../../src/kv.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAa,WAAW,EAAE,YAAY,EAAE,OAAO,EAAgB,MAAM,SAAS,CAAC;AAUtI;;GAEG;AACH,qBAAa,WAAY,YAAW,WAAW;IAC3C,OAAO,CAAC,IAAI,CAAsB;IAClC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,KAAK,CAAU;gBAEX,IAAI,CAAC,EAAE;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;KACzB;IAQD,OAAO,CAAC,eAAe;IAOjB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,GAAG,WAAW,GAAG,cAAc,CAAC,UAAU,CAAC,GAAG,OAAO,GAAG,IAAI,CAAC;IAKrH,eAAe,CAAC,CAAC,GAAG,OAAO,EAC7B,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,YAAY,GACvB,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,cAAc,CAAC,UAAU,CAAC,GAAG,OAAO,GAAG,IAAI,CAAC;QAAC,QAAQ,EAAE,CAAC,GAAG,IAAI,CAAA;KAAE,CAAC;IA8CvG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BvE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYlC,IAAI,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAyDpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAI/B;AAED,wBAAgB,iBAAiB,CAAC,IAAI,CAAC,EAAE,qBAAqB,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,WAAW,CAElG;AAED,wBAAsB,SAAS,CAAC,EAAE,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEvG;AAED,wBAAsB,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAErG;AAED,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAEnH;AAED,wBAAsB,WAAW,CAC7B,EAAE,EAAE,WAAW,EACf,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,CAE5C"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { KVNamespace } from "./types";
|
|
2
|
+
import { IndexedDbKV } from "./lib";
|
|
3
|
+
type KVMethodName = 'get' | 'getWithMetadata' | 'put' | 'delete' | 'list';
|
|
4
|
+
type WrappedKV = KVNamespace & {
|
|
5
|
+
readonly __original?: IndexedDbKV;
|
|
6
|
+
};
|
|
7
|
+
type WrapperOptions = {
|
|
8
|
+
log?: boolean | ((method: KVMethodName, ...args: any[]) => void);
|
|
9
|
+
time?: boolean;
|
|
10
|
+
prefix?: string;
|
|
11
|
+
validateKeys?: boolean;
|
|
12
|
+
retry?: {
|
|
13
|
+
attempts: number;
|
|
14
|
+
delayMs?: number;
|
|
15
|
+
};
|
|
16
|
+
onError?: (method: KVMethodName, error: unknown) => void;
|
|
17
|
+
metrics?: (method: KVMethodName, durationMs: number, success: boolean) => void;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Higher-order function that wraps an existing KVNamespace instance
|
|
21
|
+
* and adds configurable cross-cutting behavior.
|
|
22
|
+
*/
|
|
23
|
+
export declare function withKVFeatures(base: KVNamespace, opts?: WrapperOptions): WrappedKV;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=lib-wrapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lib-wrapper.d.ts","sourceRoot":"","sources":["../../src/lib-wrapper.ts"],"names":[],"mappings":"AAyDA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAC,WAAW,EAAC,MAAM,OAAO,CAAC;AAElC,KAAK,YAAY,GACX,KAAK,GACL,iBAAiB,GACjB,KAAK,GACL,QAAQ,GACR,MAAM,CAAC;AAEb,KAAK,SAAS,GAAG,WAAW,GAAG;IAE3B,QAAQ,CAAC,UAAU,CAAC,EAAE,WAAW,CAAC;CACrC,CAAC;AAEF,KAAK,cAAc,GAAG;IAClB,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC,CAAC;IACjE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACzD,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CAClF,CAAC;AAEF;;;GAGG;AACH,wBAAgB,cAAc,CAC1B,IAAI,EAAE,WAAW,EACjB,IAAI,GAAE,cAAmB,GAC1B,SAAS,CAoGX"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public API facade for IndexedDB KV storage
|
|
3
|
+
* Re-exports main classes and functions for backward compatibility
|
|
4
|
+
*/
|
|
5
|
+
export { IndexedDbKV, createIndexedDbKV, kvGetText, kvGetJson, kvGetArrayBuffer, kvGetStream, } from "./kv";
|
|
6
|
+
//# sourceMappingURL=lib.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../../src/lib.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACH,WAAW,EACX,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,gBAAgB,EAChB,WAAW,GACd,MAAM,MAAM,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time-utils.d.ts","sourceRoot":"","sources":["../../src/time-utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGtD;AAGD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,KAAK,IAAI,MAAM,CAE9B"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export type KVGetType = "text" | "json" | "arrayBuffer" | "stream";
|
|
2
|
+
export type KVValue = string | ArrayBuffer | ArrayBufferView | Blob | ReadableStream<Uint8Array> | unknown;
|
|
3
|
+
export interface KVGetOptions {
|
|
4
|
+
type?: KVGetType;
|
|
5
|
+
cacheTtl?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface KVPutOptions {
|
|
8
|
+
expiration?: number;
|
|
9
|
+
expirationTtl?: number;
|
|
10
|
+
metadata?: unknown;
|
|
11
|
+
}
|
|
12
|
+
export interface KVListOptions {
|
|
13
|
+
prefix?: string;
|
|
14
|
+
limit?: number;
|
|
15
|
+
cursor?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface KVListKey {
|
|
18
|
+
name: string;
|
|
19
|
+
expiration?: number;
|
|
20
|
+
metadata?: unknown;
|
|
21
|
+
}
|
|
22
|
+
export interface KVListResult {
|
|
23
|
+
keys: KVListKey[];
|
|
24
|
+
list_complete: boolean;
|
|
25
|
+
cursor?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface KVNamespace {
|
|
28
|
+
get(key: string, options?: KVGetOptions): Promise<string | ArrayBuffer | ReadableStream<Uint8Array> | unknown | null>;
|
|
29
|
+
getWithMetadata<T = unknown>(key: string, options?: KVGetOptions): Promise<{
|
|
30
|
+
value: string | ArrayBuffer | ReadableStream<Uint8Array> | unknown | null;
|
|
31
|
+
metadata: T | null;
|
|
32
|
+
}>;
|
|
33
|
+
put(key: string, value: KVValue, options?: KVPutOptions): Promise<void>;
|
|
34
|
+
delete(key: string): Promise<void>;
|
|
35
|
+
list(options?: KVListOptions): Promise<KVListResult>;
|
|
36
|
+
}
|
|
37
|
+
export type StoredEncoding = "text" | "json" | "clone" | "binary";
|
|
38
|
+
export type StoredRecord = {
|
|
39
|
+
key: string;
|
|
40
|
+
value: unknown;
|
|
41
|
+
encoding: StoredEncoding;
|
|
42
|
+
expiresAt: number | null;
|
|
43
|
+
metadata: unknown | null;
|
|
44
|
+
createdAt: number;
|
|
45
|
+
updatedAt: number;
|
|
46
|
+
};
|
|
47
|
+
export type InternalListCursor = {
|
|
48
|
+
v: 1;
|
|
49
|
+
prefix: string;
|
|
50
|
+
after: string | null;
|
|
51
|
+
};
|
|
52
|
+
export type OpenConfig = {
|
|
53
|
+
dbName: string;
|
|
54
|
+
storeName: string;
|
|
55
|
+
version: number;
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,aAAa,GAAG,QAAQ,CAAC;AACnE,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,WAAW,GAAG,eAAe,GAAG,IAAI,GAAG,cAAc,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC;AAG3G,MAAM,WAAW,YAAY;IACzB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IACxB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,GAAG,WAAW,GAAG,cAAc,CAAC,UAAU,CAAC,GAAG,OAAO,GAAG,IAAI,CAAC,CAAC;IACtH,eAAe,CAAC,CAAC,GAAG,OAAO,EACvB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,YAAY,GACvB,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,cAAc,CAAC,UAAU,CAAC,GAAG,OAAO,GAAG,IAAI,CAAC;QAAC,QAAQ,EAAE,CAAC,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IAE9G,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CACxD;AAED,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAElE,MAAM,MAAM,YAAY,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IAMZ,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,cAAc,CAAC;IAEzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAEzB,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC;IAEzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC7B,CAAC,EAAE,CAAC,CAAC;IAEL,MAAM,EAAE,MAAM,CAAC;IAEf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACnB,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type WebContainerOptions = {
|
|
2
|
+
headless?: boolean;
|
|
3
|
+
};
|
|
4
|
+
export declare class WebContainer {
|
|
5
|
+
private browser;
|
|
6
|
+
private page;
|
|
7
|
+
/** Becomes true once WebKit is launched and the page is initialized. */
|
|
8
|
+
ready: boolean;
|
|
9
|
+
/** Thin IndexedDB KV facade (string keys, JSON-serializable values). */
|
|
10
|
+
indexedDB: {
|
|
11
|
+
get<T = unknown>(store: string, key: string): Promise<T | undefined>;
|
|
12
|
+
set<T = unknown>(store: string, key: string, value: T): Promise<void>;
|
|
13
|
+
del(store: string, key: string): Promise<void>;
|
|
14
|
+
};
|
|
15
|
+
private constructor();
|
|
16
|
+
static create(opts?: WebContainerOptions): Promise<WebContainer>;
|
|
17
|
+
private init;
|
|
18
|
+
close(): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
declare global {
|
|
21
|
+
var reqToPromise: (req: IDBRequest) => Promise<any>;
|
|
22
|
+
var txDone: (tx: IDBTransaction) => Promise<any>;
|
|
23
|
+
var openDB: (name: string, version: number, stores: string[]) => Promise<IDBDatabase>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=web-container.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-container.d.ts","sourceRoot":"","sources":["../../src/web-container.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,mBAAmB,GAAG;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;CAKtB,CAAC;AAEF,qBAAa,YAAY;IACrB,OAAO,CAAC,OAAO,CAAW;IAC1B,OAAO,CAAC,IAAI,CAAQ;IAEpB,wEAAwE;IACjE,KAAK,UAAS;IAErB,wEAAwE;IACjE,SAAS,EAAG;QACf,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;QACrE,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACtE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAClD,CAAC;IAEF,OAAO;WAEM,MAAM,CAAC,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAAC,YAAY,CAAC;YAM5D,IAAI;IA4GZ,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAY/B;AAGD,OAAO,CAAC,MAAM,CAAC;IAEX,IAAI,YAAY,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IAEpD,IAAI,MAAM,EAAE,CAAC,EAAE,EAAE,cAAc,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IAEjD,IAAI,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;CACzF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "idb-repo",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A high-performance IndexedDB-based key-value storage implementation with caching, TTL support, and full TypeScript support",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "bun build ./index.ts --target browser --outdir ./dist --minify && tsc --emitDeclarationOnly -p tsconfig.build.json"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"indexeddb",
|
|
24
|
+
"kv-storage",
|
|
25
|
+
"storage",
|
|
26
|
+
"cache",
|
|
27
|
+
"typescript",
|
|
28
|
+
"browser",
|
|
29
|
+
"performance"
|
|
30
|
+
],
|
|
31
|
+
"author": "github.com/geoffsee",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/geoffsee/idb-kv.git"
|
|
36
|
+
},
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/geoffsee/idb-kv/issues"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/geoffsee/idb-kv#readme",
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/bun": "latest",
|
|
43
|
+
"idb-keyval": "^6.2.2",
|
|
44
|
+
"playwright": "^1.58.2"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"typescript": "^5"
|
|
48
|
+
}
|
|
49
|
+
}
|