// Sources arrived in retrieval order — an order the reader never sees and // cannot follow: an answer whose first citation was [7] opened a list that // began at [1]. Reference lists are numbered by first appearance for exactly // this reason. // // Ordering is derived from the same token walk that renders the chips, so the // list and the numbers on screen cannot disagree. The text is never rewritten: // `[7]` stays `[7]`, because that number is the source's identity in the saved // chat, the export and every chip. Two earlier versions rewrote the text and // both were wrong — one hit array indexing in code, the other an HTML // attribute — and a third would have hit the next literal context. const test = require('node:test'); const assert = require('node:assert/strict'); const MarkdownIt = require('markdown-it'); globalThis.markdownit = MarkdownIt; let mod; async function load() { if (!mod) { const fs = require('node:fs'); const path = require('node:path'); const src = fs.readFileSync(path.join(__dirname, '..', 'public/js/assistant/citations.js'), 'utf8'); mod = await import('data:text/javascript;charset=utf-8,' + encodeURIComponent(src)); } return mod; } const four = [{ number: 1, title: 'Alpha' }, { number: 2, title: 'Beta' }, { number: 3, title: 'Gamma' }, { number: 4, title: 'Delta' }]; const titles = out => Array.from(out.sources, s => s.title); test('sources come back in the order the answer cites them', async () => { const { orderSourcesByCitation } = await load(); const out = orderSourcesByCitation('Third first [3]. Then the first [1].', four); assert.deepEqual(titles(out).slice(0, 2), ['Gamma', 'Alpha']); assert.deepEqual(Array.from(out.sources.slice(0, 2), s => [s.number, s.sourceNumber]), [[1, 3], [2, 1]]); }); test('the text is returned exactly as given', async () => { const { orderSourcesByCitation } = await load(); const text = 'Third first [3]. Then the first [1].'; assert.equal(orderSourcesByCitation(text, four).text, text); }); test('a source cited twice keeps its first position', async () => { const { orderSourcesByCitation } = await load(); assert.deepEqual(titles(orderSourcesByCitation('[3] ... [1] ... [3] again.', four)).slice(0, 2), ['Gamma', 'Alpha']); }); test('retrieved but uncited sources follow, marked and still numbered', async () => { const { orderSourcesByCitation } = await load(); const out = orderSourcesByCitation('Only this one [2].', four); assert.equal(out.sources[0].title, 'Beta'); assert.equal(out.sources.length, 4, 'nothing is dropped'); assert.deepEqual(Array.from(out.sources.slice(1), s => s.uncited), [true, true, true]); assert.deepEqual(Array.from(out.sources, s => s.number), [1, 2, 3, 4], 'numbering stays contiguous'); }); test('an invented citation reserves no place', async () => { const { orderSourcesByCitation } = await load(); assert.equal(orderSourcesByCitation('Invented [9]. Real [2].', four).sources[0].title, 'Beta'); }); test('a bracket inside code, math or an HTML attribute is not a citation', async () => { // By construction, not by pattern: the rule only runs where the parser runs // inline rules, and those are places it never does. const { orderSourcesByCitation } = await load(); const text = '```\nx = arr[3][1]\n```\n\n`m[4]` and $f[3]$ and t\n\nReal [2].'; const out = orderSourcesByCitation(text, four); assert.equal(out.sources[0].title, 'Beta', 'the first real citation is first'); assert.equal(out.sources[0].sourceNumber, 2); }); test('the rendered chips agree with the ordered list', async () => { const { orderSourcesByCitation, renderAssistantMarkdown } = await load(); const text = 'See [4], then [2], then [4] again.'; const ordered = orderSourcesByCitation(text, four).sources; const html = renderAssistantMarkdown(text, ordered); // [4] is display 1, [2] is display 2 — in the chip text and in the list. assert.match(html, /data-source-number="4"[^>]*data-display-number="1"[^>]*>1<\/a>/); assert.match(html, /data-source-number="2"[^>]*data-display-number="2"[^>]*>2<\/a>/); assert.equal(ordered[0].sourceNumber, 4); assert.equal(ordered[0].number, 1); }); test('the originals are not mutated', async () => { const { orderSourcesByCitation } = await load(); const before = JSON.parse(JSON.stringify(four)); orderSourcesByCitation('[3] [1]', four); assert.deepEqual(four, before); });