From 9207eff3c8dc420e8224d30e5533b7d837d3dc16 Mon Sep 17 00:00:00 2001 From: Andrew Zolotukhin Date: Sat, 17 Oct 2026 11:39:11 +0000 Subject: [PATCH] fix(deep): reject sparse sort input before mutation --- .changeset/deep-sort-sparse.md | 5 +++++ libs/deep/README.md | 1 + libs/deep/src/deepSort.test.ts | 9 +++++++++ libs/deep/src/deepSort.ts | 3 ++- 4 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 .changeset/deep-sort-sparse.md diff --git a/.changeset/deep-sort-sparse.md b/.changeset/deep-sort-sparse.md new file mode 100644 index 00000000..58f887f7 --- /dev/null +++ b/.changeset/deep-sort-sparse.md @@ -0,0 +1,5 @@ +--- +"@cleverbrush/deep": patch +--- + +Reject sparse record arrays before sorting so missing elements cannot duplicate existing records or partially mutate input. diff --git a/libs/deep/README.md b/libs/deep/README.md index 2a9e676b..7a7302ab 100644 --- a/libs/deep/README.md +++ b/libs/deep/README.md @@ -250,3 +250,4 @@ deepSort(nodes, 1, 'position'); `RangeError` is thrown. Fields must be arrays containing finite numeric sort keys, or a `TypeError` is thrown. All keys are checked before the array is changed. Empty input is valid; path depth is independent of the number of records. +Sparse arrays and missing records are rejected before mutation. diff --git a/libs/deep/src/deepSort.test.ts b/libs/deep/src/deepSort.test.ts index 1528e06b..3ea7ce23 100644 --- a/libs/deep/src/deepSort.test.ts +++ b/libs/deep/src/deepSort.test.ts @@ -37,3 +37,12 @@ describe('deepSort', () => { expect(() => deepSort(null as never, 0, 'path')).toThrow(TypeError); }); }); + +it('rejects sparse input before sorting can duplicate an existing item', () => { + const input = new Array<{ path: number[] }>(2); + const existing = { path: [1] }; + input[1] = existing; + expect(() => deepSort(input, 0, 'path')).toThrow(TypeError); + expect(0 in input).toBe(false); + expect(input[1]).toBe(existing); +}); diff --git a/libs/deep/src/deepSort.ts b/libs/deep/src/deepSort.ts index 3e48481e..e5967b32 100644 --- a/libs/deep/src/deepSort.ts +++ b/libs/deep/src/deepSort.ts @@ -1,6 +1,7 @@ /** * Stably sort records in place by one numeric position in an array-valued field. * + * Input must be a dense array of records; missing entries reject before mutation. * All keys are validated before changing the input. An invalid index rejects * with RangeError; missing/non-array fields and non-finite keys reject with * TypeError. Empty input is valid. Equal keys retain their original order. @@ -20,7 +21,7 @@ export function deepSort( throw new RangeError('index must be a non-negative integer.'); } if (!Array.isArray(items)) throw new TypeError('items must be an array.'); - const sorted = items.map(item => { + const sorted = Array.from(items, item => { const values = item?.[field]; if (!Array.isArray(values)) { throw new TypeError('The selected field must be an array.');