import fs from 'node:fs' import path from 'node:path' import assert from 'node:assert/strict' import {fileURLToPath} from 'node:url' import {createDictionary,digitsOf,code14Of,KEY14_GROUPS,display14,candidatePages,edit,erase,preview,learn,sanitizePreferences,chars} from '../src/common/ime-engine.js' const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'../src') let count=0 function check(ok,label){assert.ok(ok,label);count++} const dictionary=createDictionary(async uri=>fs.readFileSync(root+uri,'utf8')) const meta=JSON.parse(fs.readFileSync(root+'/assets/ime-dict/meta.json','utf8')) check(meta.keys>10000&&meta.candidates>50000,'full upstream lexicon present') for(const [input,expected] of [['ni','你'],['nihao','你好'],['shijie','世界'],['zhongguo','中国'],['shurufa','输入法'],['beijing','北京']]){ const alpha=await dictionary.lookup(input,false,{}) check(alpha.some(x=>x.word===expected&&x.consume===input.length),'full pinyin '+input) const t9=await dictionary.lookup(digitsOf(input),true,{}) check(t9.some(x=>x.word===expected&&x.consume===input.length),'T9 '+input) } const full=await dictionary.lookup('nihaoshijie',false,{}) check(full.some(x=>x.word==='你好'&&x.consume===5),'continuous input consumes matched prefix') check(KEY14_GROUPS.length===14&&KEY14_GROUPS.join('').split('').sort().join('')==='abcdefghijklmnopqrstuvwxyz','14 groups cover each QWERTY letter once') check(code14Of('nihao')==='c3754'&&code14Of('a!')==='','14-key stable token mapping rejects nonletters') for(const [input,expected]of[['nihao','你好'],['shijie','世界'],['zhongguo','中国'],['shurufa','输入法'],['beijing','北京'],['lv','绿']]){ const result=await dictionary.lookup(code14Of(input),'14',{}) check(result.some(x=>x.word===expected&&x.consume===input.length),'14-key disambiguation '+input) } const pairSentence=await dictionary.lookup(code14Of('nihaoshijie'),'14',{}) check(pairSentence.some(x=>x.word==='你好'&&x.consume===5),'14-key continuous input consumes only selected prefix') const pairMany=await dictionary.lookup(code14Of('shi'),'14',{}) const fullShi=await dictionary.lookup('shi',false,{}) check(fullShi.filter(x=>x.pinyin==='shi').every(x=>pairMany.some(y=>x.word===y.word&&x.consume===y.consume)),'14-key retains every shi candidate') const pairLast=pairMany.at(-1),pairInput=code14Of('shi'),pairPreferences=learn({},'k14:'+pairInput,pairLast) check((await dictionary.lookup(pairInput,'14',pairPreferences))[0].word===pairLast.word,'14-key learned candidate order uses separate scheme namespace') check((await dictionary.lookup(pairInput,'14',{[pairInput]:[{word:pairLast.word,consume:pairLast.consume}]}))[0].word===(await dictionary.lookup(pairInput,'14',{}))[0].word,'plain pinyin preferences cannot leak into 14-key ranking') check((await dictionary.lookup('hello!','14',{})).length===0,'14-key rejects invalid token stream') check(display14('c3754')==='BN UI GH AS OP'&&display14('c3754'.repeat(6)).length<24,'14-key preview displays bounded paired-letter groups') const many=await dictionary.lookup('shi',false,{}) const grid=candidatePages(many),flatten=pages=>pages.flatMap(rows=>rows.flatMap(row=>row.items.map(entry=>entry.item))) check(grid[0].length===4&&flatten([grid[0]]).length===16,'single characters form a four by four page') check(flatten(grid).every((item,index)=>item===many[index]),'grid retains all candidates in their exact original order') const phrases=[{word:'你好'},{word:'完整词组'},{word:'很长的词组必须能够完整显示而不能被截断'},{word:'😀'}] const phraseGrid=candidatePages(phrases) check(flatten(phraseGrid).length===phrases.length&&phraseGrid[0][0].items.length===2,'phrases use wider cells without dropping results') check(phraseGrid.every(rows=>rows.reduce((sum,row)=>sum+row.units,0)<=4),'long phrases use row units within the page budget') const original=JSON.parse(fs.readFileSync(root+'/assets/ime-dict/s'+digitsOf('shi')+'.json','utf8')).entries.shi check(original.every(word=>many.some(x=>x.word===word&&x.consume===3)),'every original shi candidate remains accessible') check(dictionary.cacheSize<=4,'bounded shard cache') check((await dictionary.lookup('not valid!',false,{})).length===0,'reject invalid composition') const candidate=many.at(-1),preferences=learn({},'shi',candidate) check((await dictionary.lookup('shi',false,preferences))[0].word===candidate.word,'real selected candidate learns ordering') const injected=await dictionary.lookup('shi',false,{shi:[{word:'injected',consume:3}]}) check(injected.every(x=>x.word!=='injected'),'preference cannot inject nonexistent candidate') check(Object.keys(sanitizePreferences(JSON.parse('{"bad!":[],"shi":[{"word":7}]}'))).length===1,'sanitize persisted preference shape') check(edit('你好',1,'😀',3).text==='你😀好','Unicode insertion at cursor') check(erase('你😀好',2).text==='你好','Unicode backspace preserves surrogate pairs') check(edit('你好',2,'世界',3).limited,'maximum length enforced') check(!preview('my-secret',5,true).includes('secret'),'secret preview masked') check(chars('😀').length===1,'Unicode length counts codepoints') dictionary.clear();check(dictionary.cacheSize===0,'cache release') check((await dictionary.lookup('niha',false,{}))[0].word==='你好','unfinished pinyin completes a real word before shorter fallback') check((await dictionary.lookup(digitsOf('niha'),true,{}))[0].word==='你好','unfinished T9 shares the completion policy') check((await dictionary.lookup(code14Of('niha'),'14',{})).some(x=>x.word==='你好'&&x.consume===4&&x.completion),'14-key completion consumes only the actual input tokens') check((await dictionary.lookup('beij',false,{}))[0].word==='北京','word frequency places common completion before rare homophones') const continuation=await dictionary.predict('你好') check(continuation[0].word==='世界'&&continuation.every(x=>x.consume===0)&&continuation.length<=8,'post-commit continuations use the longest context with bounded results') check((await dictionary.predict('你好。')).length===0&&(await dictionary.predict('secret123')).length===0,'punctuation and non-Chinese contexts end prediction') check((await dictionary.predict('前文你好'))[0].word==='世界','prediction uses only the bounded suffix before the cursor') check((await dictionary.lookup('shijie',false,{}))[0].word==='世界','common full-pinyin homophone is ranked by licensed word frequency') check(dictionary.cacheSize<=4,'prediction and completion retain the bounded cache') console.log(JSON.stringify({suite:'ime-engine',assertions:count,keys:meta.keys,candidates:meta.candidates,status:'passed'}))