localspace 0.1.1 → 0.2.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 +131 -2
- package/dist/drivers/indexeddb.d.ts.map +1 -1
- package/dist/drivers/indexeddb.js +739 -43
- package/dist/drivers/localstorage.d.ts.map +1 -1
- package/dist/drivers/localstorage.js +94 -2
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/localspace.d.ts +5 -1
- package/dist/localspace.d.ts.map +1 -1
- package/dist/localspace.js +34 -2
- package/dist/types.d.ts +129 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/utils/helpers.d.ts +9 -1
- package/dist/utils/helpers.d.ts.map +1 -1
- package/dist/utils/helpers.js +34 -1
- package/package.json +2 -2
- package/src/drivers/indexeddb.ts +939 -42
- package/src/drivers/localstorage.ts +140 -1
- package/src/localspace.ts +42 -1
- package/src/types.ts +174 -0
- package/src/utils/helpers.ts +42 -0
package/README.md
CHANGED
|
@@ -51,11 +51,12 @@ localspace is built on a foundation designed for growth. Here's what's planned:
|
|
|
51
51
|
- [x] TypeScript-first implementation
|
|
52
52
|
- [x] Comprehensive test coverage
|
|
53
53
|
- [x] Modern build pipeline (ES modules, CommonJS, UMD)
|
|
54
|
+
- [x] Batch operations (`setItems()`, `getItems()`, `removeItems()`) for higher throughput
|
|
55
|
+
- [x] Automatic write coalescing (3-10x faster rapid writes, enabled by default)
|
|
56
|
+
- [x] Connection pooling, transaction batching, and warmup
|
|
54
57
|
|
|
55
58
|
### TODO
|
|
56
|
-
- [ ] **Batch operations** - `setItems()`, `getItems()`, `removeItems()` for better performance
|
|
57
59
|
- [ ] **Improved error handling** - Structured error types with detailed context
|
|
58
|
-
- [ ] **Performance optimizations** - Connection pooling, transaction batching
|
|
59
60
|
- [ ] **Plugin system** - Middleware architecture for cross-cutting concerns
|
|
60
61
|
- [ ] **Cache API driver** - Native browser caching with automatic HTTP semantics
|
|
61
62
|
- [ ] **OPFS driver** - Origin Private File System for high-performance file storage
|
|
@@ -123,6 +124,109 @@ localspace.getItem('user', (error, value) => {
|
|
|
123
124
|
});
|
|
124
125
|
```
|
|
125
126
|
|
|
127
|
+
### 🚀 Get automatic performance optimization (enabled by default)
|
|
128
|
+
localspace automatically merges rapid single writes into batched transactions, giving you **3-10x performance improvement** without changing your code. This feature is enabled by default and works transparently in the background.
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
// Your existing code - unchanged
|
|
132
|
+
await Promise.all([
|
|
133
|
+
localspace.setItem('setting1', value1),
|
|
134
|
+
localspace.setItem('setting2', value2),
|
|
135
|
+
localspace.setItem('setting3', value3),
|
|
136
|
+
]);
|
|
137
|
+
// ✅ Automatically batched into one transaction!
|
|
138
|
+
// ✅ 3-10x faster than individual commits
|
|
139
|
+
// ✅ Zero code changes required
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**How it works**: When using IndexedDB, rapid writes within an 8ms window are automatically merged into a single transaction commit. This is transparent to your application and has no impact on single writes.
|
|
143
|
+
|
|
144
|
+
**Want to customize or disable it?**
|
|
145
|
+
```ts
|
|
146
|
+
const instance = localspace.createInstance({
|
|
147
|
+
coalesceWrites: true, // enabled by default
|
|
148
|
+
coalesceWindowMs: 8, // 8ms window (default)
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Or disable if you need strict per-operation durability
|
|
152
|
+
const strict = localspace.createInstance({
|
|
153
|
+
coalesceWrites: false,
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**When is this useful?**
|
|
158
|
+
- Form auto-save that writes multiple fields rapidly
|
|
159
|
+
- Bulk state synchronization loops
|
|
160
|
+
- Real-time collaborative editing
|
|
161
|
+
- Any code with multiple sequential `setItem()` calls
|
|
162
|
+
|
|
163
|
+
**Performance impact**: Single infrequent writes are unaffected. Rapid sequential writes get 3-10x faster automatically.
|
|
164
|
+
|
|
165
|
+
**Want to see the actual performance gains?**
|
|
166
|
+
```ts
|
|
167
|
+
// Get statistics to see how much coalescing helped (IndexedDB only)
|
|
168
|
+
const stats = localspace.getPerformanceStats?.();
|
|
169
|
+
console.log(stats);
|
|
170
|
+
// {
|
|
171
|
+
// totalWrites: 150, // Total write operations
|
|
172
|
+
// coalescedWrites: 120, // Operations that were merged
|
|
173
|
+
// transactionsSaved: 100, // Transactions saved by coalescing
|
|
174
|
+
// avgCoalesceSize: 4.8 // Average batch size
|
|
175
|
+
// }
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Boost throughput with batch operations
|
|
179
|
+
Use the batch APIs to group writes and reads into single transactions for IndexedDB and localStorage. This reduces commit overhead and benefits from Chrome’s relaxed durability defaults (see below).
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
const items = [
|
|
183
|
+
{ key: 'user:1', value: { name: 'Ada' } },
|
|
184
|
+
{ key: 'user:2', value: { name: 'Lin' } },
|
|
185
|
+
];
|
|
186
|
+
|
|
187
|
+
// Single transaction write
|
|
188
|
+
await localspace.setItems(items);
|
|
189
|
+
|
|
190
|
+
// Ordered bulk read
|
|
191
|
+
const result = await localspace.getItems(items.map((item) => item.key));
|
|
192
|
+
console.log(result); // [{ key: 'user:1', value: {…} }, { key: 'user:2', value: {…} }]
|
|
193
|
+
|
|
194
|
+
// Single transaction delete
|
|
195
|
+
await localspace.removeItems(items.map((item) => item.key));
|
|
196
|
+
|
|
197
|
+
// For very large batches, set a chunk size to avoid huge transactions
|
|
198
|
+
const limited = localspace.createInstance({ maxBatchSize: 200 });
|
|
199
|
+
await limited.setDriver([limited.INDEXEDDB]);
|
|
200
|
+
await limited.setItems(items); // will split into 200-item chunks
|
|
201
|
+
|
|
202
|
+
// Optional: coalesce rapid single writes into one transaction (IndexedDB)
|
|
203
|
+
const coalesced = localspace.createInstance({
|
|
204
|
+
coalesceWrites: true,
|
|
205
|
+
coalesceWindowMs: 8,
|
|
206
|
+
});
|
|
207
|
+
await coalesced.setDriver([coalesced.INDEXEDDB]);
|
|
208
|
+
await Promise.all([
|
|
209
|
+
coalesced.setItem('fast-1', 'a'),
|
|
210
|
+
coalesced.setItem('fast-2', 'b'),
|
|
211
|
+
]); // batched into one tx within the window
|
|
212
|
+
|
|
213
|
+
// Note: localStorage batches are not atomic—writes are applied one by one.
|
|
214
|
+
// For critical flows, prefer IndexedDB or handle your own compensating logic.
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Run your own transaction
|
|
218
|
+
When you need atomic multi-step work (migrations, dependent writes), wrap operations in a single transaction. On IndexedDB this uses one `IDBTransaction`; on localStorage it executes sequentially.
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
await localspace.setDriver([localspace.INDEXEDDB]);
|
|
222
|
+
await localspace.runTransaction('readwrite', async (tx) => {
|
|
223
|
+
const current = await tx.get<number>('counter');
|
|
224
|
+
const next = (current ?? 0) + 1;
|
|
225
|
+
await tx.set('counter', next);
|
|
226
|
+
await tx.set('lastUpdated', Date.now());
|
|
227
|
+
});
|
|
228
|
+
```
|
|
229
|
+
|
|
126
230
|
### Configure isolated stores for clear data boundaries
|
|
127
231
|
Create independent instances when you want to separate cache layers or product features. Each instance can override defaults like `name`, `storeName`, and driver order.
|
|
128
232
|
|
|
@@ -144,6 +248,20 @@ await localspace.setDriver([localspace.INDEXEDDB, localspace.LOCALSTORAGE]);
|
|
|
144
248
|
if (!localspace.supports(localspace.INDEXEDDB)) {
|
|
145
249
|
console.warn('IndexedDB unavailable, using localStorage wrapper.');
|
|
146
250
|
}
|
|
251
|
+
|
|
252
|
+
// Hint IndexedDB durability (Chrome defaults to "relaxed" from 121+)
|
|
253
|
+
await localspace.setDriver([localspace.INDEXEDDB]);
|
|
254
|
+
await localspace.ready();
|
|
255
|
+
// Global durability hint for this instance
|
|
256
|
+
localspace.config({ durability: 'strict' }); // or omit to stay relaxed for speed
|
|
257
|
+
|
|
258
|
+
// Use Storage Buckets (Chromium 122+) to isolate data and hints
|
|
259
|
+
const bucketed = localspace.createInstance({
|
|
260
|
+
name: 'mail-cache',
|
|
261
|
+
storeName: 'drafts',
|
|
262
|
+
bucket: { name: 'drafts', durability: 'strict', persisted: true },
|
|
263
|
+
});
|
|
264
|
+
await bucketed.setDriver([bucketed.INDEXEDDB]);
|
|
147
265
|
```
|
|
148
266
|
|
|
149
267
|
**Tip:** Use `defineDriver()` and `getDriver()` to register custom drivers that match the localForage interface.
|
|
@@ -197,6 +315,17 @@ localspace.setItem('key', 'value', (err, value) => {
|
|
|
197
315
|
});
|
|
198
316
|
```
|
|
199
317
|
|
|
318
|
+
## Performance notes
|
|
319
|
+
- **Automatic write coalescing (enabled by default):** localspace automatically merges rapid single writes within an 8ms window into one transaction, giving you 3-10x performance improvement with zero code changes. This is enabled by default for IndexedDB. Set `coalesceWrites: false` if you need strict per-operation durability.
|
|
320
|
+
- **Batch APIs outperform loops:** Playwright benchmark (`test/playwright/benchmark.spec.ts`) on 500 items x 256B showed `setItems()` ~6x faster and `getItems()` ~7.7x faster than per-item loops, with `removeItems()` ~2.8x faster (Chromium, relaxed durability).
|
|
321
|
+
- **Transaction helpers:** `runTransaction()` lets you co-locate reads/writes in a single transaction for atomic migrations and to shorten lock time.
|
|
322
|
+
- **Batch sizing:** Use `maxBatchSize` to split very large batches and keep transaction size in check.
|
|
323
|
+
- **IndexedDB durability defaults:** Chrome 121+ uses relaxed durability by default; keep it for speed or set `durability: 'strict'` in `config` for migration-style writes.
|
|
324
|
+
- **Storage Buckets (Chromium 122+):** supply a `bucket` option to isolate critical data and hint durability/persistence per bucket.
|
|
325
|
+
- **Connection warmup:** IndexedDB instances pre-warm a transaction after init to reduce first-op latency (`prewarmTransactions` enabled by default; set to `false` to skip).
|
|
326
|
+
- **Recommended defaults:** keep `coalesceWrites` enabled (default), `durability` relaxed, and `prewarmTransactions` on. Set `connectionIdleMs` only if you want idle connections to auto-close, and `maxBatchSize` only for very large bulk writes. Prefer IndexedDB for atomic/bulk writes since localStorage batches are non-atomic. Use `maxConcurrentTransactions` to throttle heavy parallel workloads when needed.
|
|
327
|
+
- **localStorage batch atomicity:** When using localStorage driver, batch operations (`setItems()`, `removeItems()`) are **not atomic**. If an error occurs mid-operation, some items may be written or removed while others are not. In contrast, IndexedDB batch operations use transactions and guarantee atomicity (all-or-nothing). If atomicity is critical for your use case, prefer IndexedDB driver or implement application-level rollback logic.
|
|
328
|
+
|
|
200
329
|
When `compatibilityMode` is off, driver setup methods also use Node-style callbacks. Promises are recommended for all new code.
|
|
201
330
|
|
|
202
331
|
## Troubleshooting
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexeddb.d.ts","sourceRoot":"","sources":["../../src/drivers/indexeddb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,
|
|
1
|
+
{"version":3,"file":"indexeddb.d.ts","sourceRoot":"","sources":["../../src/drivers/indexeddb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EASP,MAAM,UAAU,CAAC;AAw7DlB,QAAA,MAAM,YAAY,EAAE,MAkBnB,CAAC;AAEF,eAAe,YAAY,CAAC"}
|