char/fjson
friendly JSON implementation in TypeScript
git clone https://git.t4t.associates/char/fjson
bb03820
main
1// vibed 2 3import { 4deepStrictEqual , 5equal , 6ok , 7strictEqual , 8throws , 9} from "node:assert/strict" ; 10import { FjsonError , parse , stringify } from "./mod.ts" ; 11 12Deno . test ( "pretty output uses fjson keys and balanced nested indentation" , () => { 13const value = { 14name :"fJSON" , 15options :{ enabled :true , targets :[ "linux" , "macos" ] }, 16"@scope/pkg" :{ $valid_key2 :'š\nquoted "text"' }, 17}; 18const printed = stringify ( value ); 19strictEqual ( 20printed , 21`name: "fJSON" 22options: { 23enabled: true 24targets: ["linux", "macos"] 25} 26"@scope/pkg": { 27$valid_key2: "š\\nquoted \\"text\\"" 28}` , 29); 30deepStrictEqual ( parse ( printed ), value ); 31}); 32 33Deno . test ( "long arrays expand and printing is stable" , () => { 34const value = { 35records :[{ x :[ 1 , 2 ] }, { x :[ null , false ] }], 36long :Array . from ({ length :40 }, ( _ , i ) => i ), 37}; 38const text = stringify ( value ); 39ok ( text . includes ( "long: [\n" ), text ); 40for ( const line of text . split ( "\n" )) { 41ok ([ ...line ]. length <= 80 , line ); 42} 43ok ( text . includes ( " x: [1, 2]\n }" ), text ); 44deepStrictEqual ( parse ( text ), value ); 45strictEqual ( stringify ( parse ( text )), text ); 46}); 47 48Deno . test ( "parser and printer accept the same nesting depths" , () => { 49for ( const leaf of [ null , [], {}]) { 50let value :unknown = leaf ; 51for ( let depth = 0 ; depth < 132 ; depth += 1 ) { 52for ( const candidate of [ value , { root :value }]) { 53const encoded = JSON . stringify ( candidate ); 54let parseKind :string | undefined ; 55try { 56parse ( encoded ); 57} catch ( error ) { 58ok ( error instanceof FjsonError ); 59parseKind = error . kind ; 60} 61let printed :string | undefined ; 62let printKind :string | undefined ; 63try { 64printed = stringify ( candidate ); 65} catch ( error ) { 66ok ( error instanceof FjsonError ); 67printKind = error . kind ; 68} 69strictEqual ( printKind === undefined , parseKind === undefined , encoded ); 70if ( printed !== undefined ) { 71deepStrictEqual ( parse ( printed ), candidate ); 72} else { 73strictEqual ( printKind , "nesting" ); 74strictEqual ( parseKind , printKind ); 75} 76} 77value = [ value ]; 78} 79} 80}); 81 82Deno . test ( "arbitrary json values round trip including escaped keys" , () => { 83const values :unknown [] = [ 84null , 85true , 8642 , 87- 0.125 , 88" \tš" , 89[], 90{}, 91[{}, [], null ], 92JSON . parse ( 93'{"0": 1, "": 2, "true": 3, "some-key": 4, "ę„ę¬čŖ": "\\r\\n", "__proto__": {"ok": true}}' , 94), 95]; 96for ( const value of values ) { 97const text = stringify ( value ); 98deepStrictEqual ( parse ( text ), JSON . parse ( JSON . stringify ( value )), text ); 99} 100}); 101 102Deno . test ( "roots print without a final newline" , () => { 103strictEqual ( stringify ( null ), "null" ); 104strictEqual ( stringify ( false ), "false" ); 105strictEqual ( stringify ( 42 ), "42" ); 106strictEqual ( stringify ( "š" ), '"š"' ); 107strictEqual ( stringify ([]), "[]" ); 108strictEqual ( stringify ({}), "{}" ); 109strictEqual ( stringify ({ a :1 }), "a: 1" ); 110strictEqual ( stringify ([ 1 , true ]), "[1, true]" ); 111strictEqual ( stringify ({ nested :{} }), "nested: {}" ); 112}); 113 114Deno . test ( "non-finite numbers print as null like JSON" , () => { 115strictEqual ( stringify ( NaN ), "null" ); 116strictEqual ( stringify ( Infinity ), "null" ); 117strictEqual ( stringify ([ NaN ]), "[null]" ); 118}); 119 120Deno . test ( "unserializable values throw TypeError" , () => { 121for ( 122const value of [ 123undefined , 124() => {}, 125Symbol ( "x" ), 12610n , 127(() => { 128const circular :Record < string , unknown > = {}; 129circular [ "self" ] = circular ; 130return circular ; 131})(), 132] 133) { 134throws (() => stringify ( value ), TypeError ); 135} 136}); 137 138Deno . test ( "custom limits control printing" , () => { 139let value :unknown = null ; 140for ( let i = 0 ; i < 6 ; i += 1 ) value = [ value ]; 141const text = stringify ( value , { maxDepth :6 }); 142deepStrictEqual ( parse ( text , { maxDepth :6 }), value ); 143const error = (() => { 144try { 145stringify ( value , { maxDepth :4 }); 146} catch ( caught ) { 147ok ( caught instanceof FjsonError ); 148return caught ; 149} 150throw new Error ( "expected nesting error" ); 151})(); 152strictEqual ( error . kind , "nesting" ); 153ok ( error . message . includes ( "4 levels" ), error . message ); 154ok ( parse ( text , { maxDepth :6 })); 155throws (() => parse ( text , { maxDepth :4 }), FjsonError ); 156equal ( stringify ({ a :[ 0 ] }, { maxDepth :1 }), "a: [0]" ); 157throws (() => stringify ({ a :[[ 0 ]] }, { maxDepth :1 }), FjsonError ); 158});