diff --git a/src/pipeline/steps/transform.ts b/src/pipeline/steps/transform.ts index 3b6a82c5..d070cc65 100644 --- a/src/pipeline/steps/transform.ts +++ b/src/pipeline/steps/transform.ts @@ -60,9 +60,7 @@ export async function stepSort(_page: IPage | null, params: unknown, data: unkno return [...data].sort((a, b) => { const left = isRecord(a) ? a[key] : undefined; const right = isRecord(b) ? b[key] : undefined; - const va = left ?? ''; - const vb = right ?? ''; - const cmp = va < vb ? -1 : va > vb ? 1 : 0; + const cmp = String(left ?? '').localeCompare(String(right ?? ''), undefined, { numeric: true }); return reverse ? -cmp : cmp; }); } diff --git a/src/pipeline/transform.test.ts b/src/pipeline/transform.test.ts index 7e00157b..e676b5dd 100644 --- a/src/pipeline/transform.test.ts +++ b/src/pipeline/transform.test.ts @@ -101,6 +101,26 @@ describe('stepSort', () => { await stepSort(null, 'score', SAMPLE_DATA, {}); expect(SAMPLE_DATA).toEqual(original); }); + + it('sorts string-encoded numbers naturally by default', async () => { + const data = [ + { name: 'A', volume: '99' }, + { name: 'B', volume: '1000' }, + { name: 'C', volume: '250' }, + ]; + const result = await stepSort(null, { by: 'volume', order: 'desc' }, data, {}); + expect((result as typeof data).map((r) => r.name)).toEqual(['B', 'C', 'A']); + }); + + it('handles missing fields gracefully', async () => { + const data = [ + { name: 'A', value: '10' }, + { name: 'B' }, + { name: 'C', value: '5' }, + ]; + const result = await stepSort(null, { by: 'value', order: 'asc' }, data, {}); + expect((result as typeof data).map((r) => r.name)).toEqual(['B', 'C', 'A']); + }); }); describe('stepLimit', () => {