Read the source and its declared rule
The original format allocates three UTF-8 bytes to name and the remaining two bytes to quantity text. Source bytes encode 你12. The field cut must be a valid character boundary within the supplied byte array; treating three bytes as three JavaScript characters consumes the quantity too.
| Record | Input JSON |
|---|---|
| Base input | {"bytes":[228,189,160,49,50],"nameBytes":3} |
Compare the actual paths
Correct output separates 你 and 12. Strict decoding of each slice also detects a declared cut through the middle of a character.
| Path | Output JSON |
|---|---|
| Misleading path | {"accepted":true,"name":"你12","quantityText":""} |
| Corrected path | {"accepted":true,"name":"你","quantityText":"12"} |
import {DatabaseSync} from 'node:sqlite';
import {TextDecoder} from 'node:util';
import {inflateSync} from 'node:zlib';
import {posix} from 'node:path';
import {createHash} from 'node:crypto';
const rows=(db,sql)=>db.prepare(sql).all().map(r=>({...r}));
const failed=(action)=>{try{action();return null;}catch(e){return {code:e.errcode??e.code??e.name};}};
import assert from 'node:assert/strict';
const run=function run(i,corrected=true){
const bytes=Uint8Array.from(i.bytes);const decoder=new TextDecoder('utf-8',{fatal:true});
try{
if(corrected){return {accepted:true,name:decoder.decode(bytes.slice(0,i.nameBytes)),quantityText:decoder.decode(bytes.slice(i.nameBytes))};}
const text=decoder.decode(bytes);return {accepted:true,name:text.slice(0,i.nameBytes),quantityText:text.slice(i.nameBytes)};
}catch{return {accepted:false,error:'field-splits-encoded-character'};}
};
const cases=[{"label":"misleading base","input":{"bytes":[228,189,160,49,50],"nameBytes":3},"expected":{"accepted":true,"name":"你12","quantityText":""},"corrected":false},{"label":"corrected base","input":{"bytes":[228,189,160,49,50],"nameBytes":3},"expected":{"accepted":true,"name":"你","quantityText":"12"},"corrected":true},{"label":"boundary within character","input":{"bytes":[228,189,160,49,50],"nameBytes":2},"expected":{"accepted":false,"error":"field-splits-encoded-character"},"corrected":true},{"label":"ASCII source boundary","input":{"bytes":[65,66,49,50],"nameBytes":2},"expected":{"accepted":true,"name":"AB","quantityText":"12"},"corrected":true}];
for(const test of cases){
const actual=run(test.input,test.corrected);
assert.deepEqual(actual,test.expected,test.label);
console.log(test.label,actual);
}
Check boundaries before the next load
A two-byte cut splits 你 and rejects. Two ASCII name bytes form AB and leave 12 intact.
| Case | Input JSON | Expected output JSON |
|---|---|---|
| Cut inside a multibyte character | {"bytes":[228,189,160,49,50],"nameBytes":2} | {"accepted":false,"error":"field-splits-encoded-character"} |
| ASCII field boundary | {"bytes":[65,66,49,50],"nameBytes":2} | {"accepted":true,"name":"AB","quantityText":"12"} |
Should the name field include 12 in this source?
Show the worked answer
No. Its allocation is three bytes for 你. Quantity text remains the separate two-byte field 12.