Commit 99a3716
Fix localStorage collections for numeric IDs (#845)
* test: add regression tests for issue #397 (update/delete targeting)
Adds comprehensive test coverage for issue #397 which reported that
update() and delete() operations on localStorage collections were
targeting the wrong items.
Investigation findings:
- Bug was fixed by PR #760 (Nov 5, 2025) which changed from using
config.getKey() to mutation.key for consistency
- All tests pass, unable to reproduce the reported issue
- Likely that recent commenter is using an outdated package version
Test Coverage Added:
- Test for updating correct item with multiple items present
- Test for deleting correct item with multiple items present
- Both tests verify collection state AND localStorage persistence
Also includes detailed investigation report (INVESTIGATION_397.md)
documenting the analysis, timeline, and recommendations.
Related: #397, #760
* fix: resolve localStorage numeric ID key type mismatch (issue #397)
Fixes #397 - Update and delete operations on localStorage collections
now work correctly with numeric IDs.
## Root Cause
When using numeric IDs with localStorage collections, a type mismatch
occurred between numeric keys (e.g., 1, 2, 3) and string keys from
localStorage (e.g., "1", "2", "3").
The issue manifested when:
1. Data was loaded from localStorage (keys become strings via JSON.parse)
2. User performed update/delete with numeric ID
3. JavaScript Map lookup failed: Map.has(1) !== Map.has("1")
## The Fix
Convert all mutation.key values to strings before using with lastKnownData
Map, ensuring consistency with localStorage's string-only keys.
Changed in packages/db/src/local-storage.ts:
- Line 419 (wrappedOnInsert): const key = String(mutation.key)
- Line 455 (wrappedOnUpdate): const key = String(mutation.key)
- Line 486 (wrappedOnDelete): const key = String(mutation.key)
- Line 554 (acceptMutations): const key = String(mutation.key)
## Test Coverage
Added 6 comprehensive test cases in packages/db/tests/local-storage.test.ts:
Bug #397 test suite (lines 1618-1985):
- String ID tests (baseline/regression)
- Numeric ID tests (direct operations)
- Numeric ID tests after loading from storage (critical case)
All 43 tests pass ✅
## Impact
- ✅ Fully backward compatible (string IDs unchanged)
- ✅ Numeric IDs now work correctly
- ✅ No breaking changes to API
## Documentation
- BUG_FIX_397.md: Detailed technical explanation
- INVESTIGATION_397.md: Complete investigation report
Closes #397
* chore: add changeset for numeric ID localStorage fix
- Remove investigation documentation files
- Add changeset for patch release
* test: add test for numeric/string ID collision behavior
Documents that numeric ID 1 and string ID "1" will collide in
localStorage due to JSON's string-only object keys. Last write wins.
This is expected behavior and a fundamental localStorage limitation.
* refactor: use __number__ prefix for numeric keys instead of String()
Improves fix for issue #397 to prevent collision between numeric and
string IDs (e.g., numeric 1 vs string "1").
Approach:
- Numeric keys: prefixed with "__number__" → __number__1
- String keys: kept as-is → "1", "first"
Benefits:
- ✅ Fixes numeric ID bug (original issue #397)
- ✅ Prevents numeric/string ID collision
- ✅ Maintains backward compatibility for string IDs
- ✅ All 44 tests passing
Related: #397
* chore: update changeset to focus on original bug
Reworded to emphasize the actual bug (numeric IDs not working)
rather than collision prevention as the main issue.
* test: rename test suite to remove issue number reference
Changed 'Bug #397: update/delete targeting wrong item' to
'numeric and string ID handling' to be more descriptive and
issue-agnostic.
* fix: implement proper type-safe encoding for localStorage keys
- Replace __number__ prefix with type-safe n: and s: encoding scheme
- Extract encoding/decoding logic into helper functions (encodeStorageKey, decodeStorageKey)
- Prevents all possible collisions between numeric and string keys
- Add test case for collision prevention between numeric 1 and string 'n:1'
- Update all tests to use new encoding format
Co-authored-by: Kevin <[email protected]>
* test: update localStorage tests for type-safe encoding
Update all localStorage collection tests to match the new type-safe
key encoding format that prevents collisions between numeric and
string IDs.
Changes:
- Update test assertions to use encoded keys ("s:1" for string "1")
- Update test data setup to use encoded keys in storage
- Fix verifyConsistency helper to decode storage keys properly
- All 17 failing tests now correctly expect the encoded format
Co-authored-by: Kevin <[email protected]>
* fix: update acceptMutations tests to use encoded storage keys
Tests were expecting unencoded keys (e.g., 'tx-1') but the new type-safe
encoding stores string keys with the 's:' prefix (e.g., 's:tx-1').
Updated 6 test cases to access parsed storage with correct encoded keys.
Co-authored-by: Kevin <[email protected]>
---------
Co-authored-by: Claude <[email protected]>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Kevin <[email protected]>1 parent 6ff0f45 commit 99a3716
File tree
3 files changed
+586
-60
lines changed- .changeset
- packages/db
- src
- tests
3 files changed
+586
-60
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
152 | 152 | | |
153 | 153 | | |
154 | 154 | | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
155 | 192 | | |
156 | 193 | | |
157 | 194 | | |
| |||
365 | 402 | | |
366 | 403 | | |
367 | 404 | | |
368 | | - | |
| 405 | + | |
369 | 406 | | |
370 | 407 | | |
371 | 408 | | |
| |||
415 | 452 | | |
416 | 453 | | |
417 | 454 | | |
418 | | - | |
419 | 455 | | |
420 | 456 | | |
421 | 457 | | |
422 | 458 | | |
423 | | - | |
| 459 | + | |
424 | 460 | | |
425 | 461 | | |
426 | 462 | | |
| |||
450 | 486 | | |
451 | 487 | | |
452 | 488 | | |
453 | | - | |
454 | 489 | | |
455 | 490 | | |
456 | 491 | | |
457 | 492 | | |
458 | | - | |
| 493 | + | |
459 | 494 | | |
460 | 495 | | |
461 | 496 | | |
| |||
480 | 515 | | |
481 | 516 | | |
482 | 517 | | |
483 | | - | |
484 | | - | |
| 518 | + | |
485 | 519 | | |
486 | 520 | | |
487 | 521 | | |
| |||
547 | 581 | | |
548 | 582 | | |
549 | 583 | | |
550 | | - | |
551 | | - | |
552 | 584 | | |
553 | 585 | | |
554 | 586 | | |
555 | 587 | | |
556 | 588 | | |
557 | 589 | | |
558 | 590 | | |
559 | | - | |
| 591 | + | |
560 | 592 | | |
561 | 593 | | |
562 | 594 | | |
563 | | - | |
| 595 | + | |
564 | 596 | | |
565 | 597 | | |
566 | 598 | | |
| |||
616 | 648 | | |
617 | 649 | | |
618 | 650 | | |
619 | | - | |
| 651 | + | |
620 | 652 | | |
621 | 653 | | |
622 | 654 | | |
| |||
625 | 657 | | |
626 | 658 | | |
627 | 659 | | |
628 | | - | |
| 660 | + | |
| 661 | + | |
629 | 662 | | |
630 | | - | |
| 663 | + | |
631 | 664 | | |
632 | 665 | | |
633 | 666 | | |
| |||
0 commit comments