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 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,EAKP,MAAM,UAAU,CAAC;AAgkClB,QAAA,MAAM,YAAY,EAAE,MAanB,CAAC;AAEF,eAAe,YAAY,CAAC"}
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"}