localspace 0.3.0 → 0.3.1

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
@@ -35,6 +35,7 @@ Starting fresh let us eliminate technical debt while maintaining API compatibili
35
35
  - [Configure isolated stores for clear data boundaries](#configure-isolated-stores-for-clear-data-boundaries)
36
36
  - [Choose drivers with predictable fallbacks](#choose-drivers-with-predictable-fallbacks)
37
37
  - [Handle binary data across browsers](#handle-binary-data-across-browsers)
38
+ - [Advanced: Coalesced Writes (IndexedDB only)](#advanced-coalesced-writes-indexeddb-only)
38
39
  - [Migration Guide](#migration-guide)
39
40
  - [Note differences from localForage before upgrading](#note-differences-from-localforage-before-upgrading)
40
41
  - [Enable compatibility mode for legacy callbacks](#enable-compatibility-mode-for-legacy-callbacks)
@@ -52,7 +53,7 @@ localspace is built on a foundation designed for growth. Here's what's planned:
52
53
  - [x] Comprehensive test coverage
53
54
  - [x] Modern build pipeline (ES modules, CommonJS, UMD)
54
55
  - [x] Batch operations (`setItems()`, `getItems()`, `removeItems()`) for higher throughput
55
- - [x] Automatic write coalescing (3-10x faster rapid writes, enabled by default)
56
+ - [x] Automatic write coalescing (3-10x faster rapid writes, opt-in for IndexedDB)
56
57
  - [x] Connection pooling, transaction batching, and warmup
57
58
  - [x] **Improved error handling** - Structured error types with detailed context
58
59
 
@@ -122,8 +123,8 @@ localspace.getItem('user', (error, value) => {
122
123
  });
123
124
  ```
124
125
 
125
- ### 🚀 Get automatic performance optimization (enabled by default)
126
- 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.
126
+ ### 🚀 Opt into automatic performance optimization (coalesced writes)
127
+ localspace can merge rapid single writes into batched transactions for IndexedDB, giving you **3-10x performance improvement** under write-heavy bursts. This is opt-in so default behavior stays predictable; enable it when you know you have high write pressure.
127
128
 
128
129
  ```ts
129
130
  // Your existing code - unchanged
@@ -137,21 +138,18 @@ await Promise.all([
137
138
  // ✅ Zero code changes required
138
139
  ```
139
140
 
140
- **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.
141
+ **How it works**: When using IndexedDB, rapid writes within an 8ms window are merged into a single transaction commit. This is transparent to your application and has no impact on single writes.
141
142
 
142
- **Want to customize or disable it?**
143
+ **Turn it on or tune it**
143
144
  ```ts
144
145
  const instance = localspace.createInstance({
145
- coalesceWrites: true, // enabled by default
146
+ coalesceWrites: true, // opt-in (default is false)
146
147
  coalesceWindowMs: 8, // 8ms window (default)
147
148
  });
148
-
149
- // Or disable if you need strict per-operation durability
150
- const strict = localspace.createInstance({
151
- coalesceWrites: false,
152
- });
153
149
  ```
154
150
 
151
+ For consistency modes, batch limits, and failure semantics, see **Advanced: Coalesced Writes** below.
152
+
155
153
  **When is this useful?**
156
154
  - Form auto-save that writes multiple fields rapidly
157
155
  - Bulk state synchronization loops
@@ -281,6 +279,132 @@ await localspace.setItem('file', file);
281
279
  const restored = await localspace.getItem<Blob>('file');
282
280
  ```
283
281
 
282
+ ## Advanced: Coalesced Writes (IndexedDB only)
283
+
284
+ localspace offers an opt-in, configurable coalesced write path to cut IndexedDB transaction count and improve throughput under heavy write bursts.
285
+
286
+ > `coalesceWrites` defaults to `false` so behavior stays predictable. Turn it on when you expect high-frequency writes.
287
+
288
+ ### Why coalesce writes?
289
+
290
+ Each IndexedDB write opens a readwrite transaction. At high frequency, transaction startup overhead becomes a bottleneck. With coalescing enabled, `setItem` and `removeItem` calls that land within a short window (default 8 ms) are merged into fewer transactions:
291
+ - Multiple writes can share one transaction.
292
+ - `coalesceMaxBatchSize` caps how many ops each flush processes.
293
+ - `coalesceReadConsistency` controls when writes resolve and when reads see them.
294
+
295
+ ### Configuration
296
+
297
+ Relevant `LocalSpaceConfig` fields:
298
+
299
+ ```ts
300
+ interface LocalSpaceConfig {
301
+ /**
302
+ * Enable coalesced writes (IndexedDB only).
303
+ * Default: false
304
+ */
305
+ coalesceWrites?: boolean;
306
+
307
+ /**
308
+ * Time window (ms) for merging writes into the same batch.
309
+ * Default: 8
310
+ */
311
+ coalesceWindowMs?: number;
312
+
313
+ /**
314
+ * Maximum operations per flush batch. Beyond this, flush immediately
315
+ * and split into multiple transactions.
316
+ * Default: undefined (no limit)
317
+ */
318
+ coalesceMaxBatchSize?: number;
319
+
320
+ /**
321
+ * When coalesceWrites is on:
322
+ * - 'strong' (default): drain pending writes before reads
323
+ * - 'eventual': reads skip draining; writes only guarantee queueing
324
+ */
325
+ coalesceReadConsistency?: 'strong' | 'eventual';
326
+ }
327
+ ```
328
+
329
+ ### Consistency modes
330
+
331
+ #### `coalesceReadConsistency: 'strong'` (default)
332
+ - Writes (`setItem` / `removeItem`): Promises resolve after the data is persisted; flush errors reject.
333
+ - Reads (`getItem`, `iterate`, batch reads): call `drainCoalescedWrites` first so you read what you just wrote.
334
+
335
+ Use this for user settings, drafts, and any flow where you need read-your-writes.
336
+
337
+ #### `coalesceReadConsistency: 'eventual'`
338
+ - Writes: queued and resolve immediately once enqueued; flush happens in the background. Errors log `console.warn('[localspace] coalesced write failed (eventual mode)', error)` but do not reject the earlier Promise.
339
+ - Reads: do not flush pending writes, so you may briefly see stale values.
340
+ - Destructive operations still force a flush to avoid dropping queued writes: `removeItems`, `clear`, `dropInstance`.
341
+
342
+ Use this for logs/analytics or workloads that can tolerate short windows of staleness in exchange for the lightest write path.
343
+
344
+ ### Bounding batch size
345
+
346
+ ```ts
347
+ const store = localspace.createInstance({
348
+ name: 'logs',
349
+ storeName: 'events',
350
+ coalesceWrites: true,
351
+ coalesceWindowMs: 8,
352
+ coalesceMaxBatchSize: 64,
353
+ coalesceReadConsistency: 'eventual',
354
+ });
355
+ ```
356
+
357
+ - When the queue reaches `coalesceMaxBatchSize`, it flushes immediately.
358
+ - Flush splits work into batches of up to 64 ops, each in its own transaction.
359
+ - `getPerformanceStats()` reports `totalWrites`, `coalescedWrites`, and `transactionsSaved` so you can see the gains.
360
+
361
+ ### Recommended recipes
362
+
363
+ 1) Default: coalescing off
364
+ ```ts
365
+ const store = localspace.createInstance({
366
+ name: 'app',
367
+ storeName: 'keyvaluepairs',
368
+ // coalesceWrites is false by default
369
+ });
370
+ ```
371
+
372
+ 2) High-frequency writes with eventual consistency
373
+ ```ts
374
+ const logStore = localspace.createInstance({
375
+ name: 'analytics',
376
+ storeName: 'events',
377
+ coalesceWrites: true,
378
+ coalesceWindowMs: 8,
379
+ coalesceMaxBatchSize: 64,
380
+ coalesceReadConsistency: 'eventual',
381
+ });
382
+ ```
383
+ - `setItem` resolves almost immediately.
384
+ - Short windows of stale reads are acceptable.
385
+ - `clear` and `dropInstance` force-flush so queued writes are not lost.
386
+
387
+ 3) Strong consistency with bounded batches
388
+ ```ts
389
+ const userStore = localspace.createInstance({
390
+ name: 'user-data',
391
+ storeName: 'kv',
392
+ coalesceWrites: true,
393
+ coalesceWindowMs: 8,
394
+ coalesceMaxBatchSize: 32,
395
+ coalesceReadConsistency: 'strong',
396
+ });
397
+ ```
398
+ - Writes resolve after persistence.
399
+ - Reads flush pending writes first.
400
+ - Batching still reduces transaction count.
401
+
402
+ ### Caveats
403
+
404
+ - Coalesced writes apply to the IndexedDB driver only; localStorage always writes per operation.
405
+ - In `eventual` mode, writes can be lost if the page closes before flush completes, and errors surface only via `console.warn`.
406
+ - For critical durability (orders, payments, irreversible state), avoid `eventual` and consider leaving `coalesceWrites` off entirely.
407
+
284
408
  ## Plugin System
285
409
 
286
410
  localspace now ships with a first-class plugin engine. Attach middleware when creating an instance or call `use()` later; plugins can mutate payloads, observe driver context, and run async interceptors around every storage call.
@@ -398,7 +522,7 @@ localspace.setItem('key', 'value', (err, value) => {
398
522
  ```
399
523
 
400
524
  ## Performance notes
401
- - **Automatic write coalescing (enabled by default):** localspace automatically merges rapid single writes (`setItem`/`removeItem`) 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.
525
+ - **Automatic write coalescing (opt-in):** localspace can merge rapid single writes (`setItem`/`removeItem`) within an 8ms window into one transaction for IndexedDB, delivering 3-10x speedups under bursty writes. Enable with `coalesceWrites: true` and see **Advanced: Coalesced Writes** for consistency modes.
402
526
  - **Read-your-writes consistency with coalescing:** Pending coalesced writes are flushed before reads (`getItem`, `getItems`, `iterate`, `keys`, `length`, `key`) and destructive ops (`clear`, `dropInstance`), so immediate reads always observe the latest value. If you need eventual reads for speed, you can switch `coalesceReadConsistency` to `'eventual'`.
403
527
  - **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).
404
528
  - **Transaction helpers:** `runTransaction()` lets you co-locate reads/writes in a single transaction for atomic migrations and to shorten lock time.
@@ -406,7 +530,7 @@ localspace.setItem('key', 'value', (err, value) => {
406
530
  - **IndexedDB durability defaults:** Chrome 121+ uses relaxed durability by default; keep it for speed or set `durability: 'strict'` in `config` for migration-style writes.
407
531
  - **Storage Buckets (Chromium 122+):** supply a `bucket` option to isolate critical data and hint durability/persistence per bucket.
408
532
  - **Connection warmup:** IndexedDB instances pre-warm a transaction after init to reduce first-op latency (`prewarmTransactions` enabled by default; set to `false` to skip).
409
- - **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.
533
+ - **Recommended defaults:** leave `coalesceWrites` off unless you know you need higher write throughput; if you enable it, prefer the default `strong` consistency. Keep `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.
410
534
  - **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.
411
535
 
412
536
  When `compatibilityMode` is off, driver setup methods also use Node-style callbacks. Promises are recommended for all new code.
@@ -1 +1 @@
1
- {"version":3,"file":"indexeddb.d.ts","sourceRoot":"","sources":["../../src/drivers/indexeddb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EASP,MAAM,UAAU,CAAC;AA4oElB,QAAA,MAAM,YAAY,EAAE,MAkBnB,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;AAytElB,QAAA,MAAM,YAAY,EAAE,MAkBnB,CAAC;AAEF,eAAe,YAAY,CAAC"}