ljharb

side-channel-list

Store information about any JS value in a side channel, using a linked list.

by ljharb

JavaScript

JavaScript100%

Commit activity

Last 12 weeks · 0 commits

Full graph →

Community health

4 of 6 standards met

Community profile →
85
✓README✓License✓Contributing✓Code of Conduct○Issue Template○PR Template
DragonDragonDDD

Add the explicit entry for users that need storage but not assertions. This exposes a storage-only subpath while keeping the existing root API, assertion behavior, native-intrinsic captures, and collection fallbacks. Consumers that do not call assert can avoid loading its formatter without changing their storage algorithm. The get/has/set/delete implementation is shared by the new subpath and the existing root entry. The root still supplies assert, including its existing error messages and dynamic lookup of channel.has. Validation with the coordinated source branches: Existing and new test suites pass on Node 16.20.2 and 24.19.0. ESLint passes (existing warnings remain); declarations and checked JS pass TypeScript 5.9.3 with dependency paths pointing to the coordinated source branches. An additional local before/after differential passes with WeakMap+Map, Map-only, WeakMap-only, and neither, including primitive/NaN keys, aliases/cycles, filter-created objects, Proxy access order, and late collection-prototype mutation. A cold-load regression test rejects any attempt by the storage entry to load object-inspect. Package dry runs include storage.js and storage.d.ts. The root now wraps a storage object to preserve its existing own-property order and closed-over assertion semantics. That adds one wrapper object for root-entry users; no runtime-performance improvement is claimed. The Node <16 CI matrix remains to be run upstream. This is the first step of a coordinated storage-only path for side-channel and qs. No release number or published dependency range is assumed by this PR. Related discussion: https://github.com/ljharb/qs/issues/404 and https://github.com/ljharb/side-channel/pull/12. Unlike the dependency-removal proposal, this retains the existing storage algorithms, call-bound/get-intrinsic behavior where applicable, and the public assertion formatter. Coordinated integration: https://github.com/ljharb/side-channel/pull/17; consumer: https://github.com/ljharb/qs/pull/632.

DragonDragonDDD · 12h ago
ChALkeR

Reproduced: Deleting the first node in a multi-node list incorrectly resets to , discarding all remaining nodes. The condition checks whether the deleted node was the head of the list, but doesn't verify the list is actually empty. After deletion of the head node, still points to remaining nodes, but is set to , silently losing all other stored key-value pairs. (confidence: 9/10) Data structure: is a sentinel/root node (no key/value), and actual data nodes are linked via . So the "first data node" is . Scenario: List has two entries: set('a', 1) then set('b', 2). After these operations: , , Now delete('b'): 1. → 2. → calls 3. In : , . → true. → (correctly removes nodeB from the list) Since is true, we skip the move-to-head logic. Returns . 4. Back in : , , so → true 5. → $o is reset to undefined!* But was pointing to (which still has key 'a'). By setting , we've lost nodeA entirely. The bug is real: the intent of the check seems to be "if the list is now empty, clean up $o", but only checks if the deleted node was the first* data node, not whether the list is empty after deletion. The correct check should be something like (i.e., the sentinel has no more children).

ChALkeR · 6mo ago
Structured data for AI agents

Repository: ljharb/side-channel-list. Description: Store information about any JS value in a side channel, using a linked list. Stars: 4, Forks: 2. Primary language: JavaScript. Languages: JavaScript (100%). License: MIT. Open PRs: 0, open issues: 1. Last activity: 6mo ago. Community health: 85%. Top contributors: ljharb.