Files
boos e0a5f57793 feat(desktop): embed BoosAPI Desktop client as a subproject
Vendor the rebranded BoosAPI Desktop client (Tauri + Vue) into metapi as
a monorepo subdirectory under desktop/. Source only — node_modules,
dist, Cargo target and the upstream .git history are excluded.

desktop/ keeps its own independent Cargo workspace (desktop/src-tauri
with crates/*); it is not part of the backend boosapi crate, so a
`cargo check` at the metapi root still compiles only the backend. Build
the desktop app from desktop/src-tauri per desktop/README.md.

Note: app icons are still placeholder assets pending the final BoosAPI
logo. The updater signing keys live outside the repo and are not
included.
2026-06-22 11:32:57 +08:00

113 lines
4.6 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { getMarkdown, parseMarkdownToStructure } from '../src'
function collectByType(nodes: any[], type: string) {
const out: any[] = []
const walk = (node: any) => {
if (!node)
return
if (Array.isArray(node)) {
node.forEach(walk)
return
}
if (node.type === type)
out.push(node)
if (Array.isArray(node.children))
node.children.forEach(walk)
if (Array.isArray(node.items))
node.items.forEach(walk)
}
walk(nodes)
return out
}
function collectText(nodes: any[]) {
return collectByType(nodes, 'text').map((node: any) => String(node.content ?? '')).join('')
}
describe('numeric reference streaming stability', () => {
it('keeps completed numeric references stable during char-by-char chained references', () => {
const md = getMarkdown('numeric-reference-streaming-chain')
const full = '随便输入一些文字[1][2][3]'
const firstReady = full.indexOf('[1]') + '[1]'.length
const secondOpens = full.indexOf('[2]')
const secondReady = secondOpens + '[2]'.length
const thirdReady = full.indexOf('[3]') + '[3]'.length
for (let i = 1; i <= full.length; i++) {
const chunk = full.slice(0, i)
const nodes = parseMarkdownToStructure(chunk, md, { final: false })
const ids = collectByType(nodes, 'reference').map((node: any) => String(node.id ?? ''))
if (i >= firstReady)
expect(ids[0], `prefix(${i}): ${chunk}`).toBe('1')
if (i > secondOpens)
expect(ids[0], `prefix(${i}): ${chunk}`).toBe('1')
if (i >= secondReady)
expect(ids.slice(0, 2), `prefix(${i}): ${chunk}`).toEqual(['1', '2'])
if (i >= thirdReady)
expect(ids.slice(0, 3), `prefix(${i}): ${chunk}`).toEqual(['1', '2', '3'])
}
})
it('keeps the numeric reference once an adjacent inline link becomes distinguishable', () => {
const md = getMarkdown('numeric-reference-streaming-link')
const full = '随便输入一些文字 [1][百度](www.baidu.com)'
const distinguishableAt = full.indexOf('[百度](') + '[百度]('.length
for (let i = 1; i <= full.length; i++) {
const chunk = full.slice(0, i)
const nodes = parseMarkdownToStructure(chunk, md, { final: false })
const ids = collectByType(nodes, 'reference').map((node: any) => String(node.id ?? ''))
const links = collectByType(nodes, 'link')
if (i >= distinguishableAt) {
expect(ids[0], `prefix(${i}): ${chunk}`).toBe('1')
expect(collectText(nodes), `prefix(${i}): ${chunk}`).not.toContain('[1]')
expect(links, `prefix(${i}): ${chunk}`).toHaveLength(1)
expect(String(links[0]?.text ?? ''), `prefix(${i}): ${chunk}`).toContain('百度')
expect(Boolean(links[0]?.loading), `prefix(${i}): ${chunk}`).toBe(i < full.length)
}
}
const finalNodes = parseMarkdownToStructure(full, md, { final: true })
const finalIds = collectByType(finalNodes, 'reference').map((node: any) => String(node.id ?? ''))
const links = collectByType(finalNodes, 'link')
expect(finalIds).toEqual(['1'])
expect(links).toHaveLength(1)
expect(String(links[0]?.href ?? '')).toBe('www.baidu.com')
})
it('keeps the completed numeric reference while trailing text streams in', () => {
const md = getMarkdown('numeric-reference-streaming-suffix')
const full = '随便输入一些文字[1]后面还有文字'
const readyAt = full.indexOf('[1]') + '[1]'.length
for (let i = readyAt; i <= full.length; i++) {
const chunk = full.slice(0, i)
const nodes = parseMarkdownToStructure(chunk, md, { final: false })
const ids = collectByType(nodes, 'reference').map((node: any) => String(node.id ?? ''))
expect(ids[0], `prefix(${i}): ${chunk}`).toBe('1')
}
})
it('still respects validateLink when the trailing markdown link is being recovered', () => {
const md = getMarkdown('numeric-reference-streaming-validate')
md.set?.({ validateLink: (url: string) => !/^\s*javascript:/i.test(url.trim()) })
const chunk = '随便输入一些文字 [1][百度](javascript:alert(1'
const nodes = parseMarkdownToStructure(chunk, md, { final: false })
const ids = collectByType(nodes, 'reference').map((node: any) => String(node.id ?? ''))
const links = collectByType(nodes, 'link')
expect(ids).toEqual(['1'])
expect(links).toHaveLength(1)
expect(String(links[0]?.text ?? '')).toBe('百度')
expect(String(links[0]?.href ?? '')).not.toMatch(/javascript:/i)
expect(Boolean(links[0]?.loading)).toBe(true)
expect(collectText(nodes)).toContain('百度')
})
})