UNPKG

jsurl2

Version:

URL friendly JSON-like formatting and parsing

1 lines 14.3 kB
{"version":3,"file":"index.mjs","sources":["../src/index.ts"],"sourcesContent":["// TODO custom objects, support Set, Map etc\n// TODO custom dictionary\nconst stringRE = /^[a-zA-Z]/\nconst numRE = /^[\\d-]/\nconst TRUE = '_T'\nconst FALSE = '_F'\nconst NULL = '_N'\nconst UNDEF = '_U'\nconst NAN = '_n'\nconst INF = '_I'\nconst NINF = '_J'\n\nconst dict = {\n\tT: true,\n\tF: false,\n\tN: null,\n\tU: undefined,\n\tn: NaN,\n\tI: Infinity,\n\tJ: -Infinity,\n} as const\n\nconst fromEscape = {\n\t'*': '*',\n\t_: '_',\n\t'-': '~',\n\tS: '$',\n\tP: '+',\n\t'\"': \"'\",\n\tC: '(', // not necessary but we keep it for symmetry\n\tD: ')',\n\tL: '<',\n\tG: '>', // not necessary but we keep it for symmetry\n\t'.': '%',\n\tQ: '?',\n\tH: '#',\n\tA: '&',\n\tE: '=',\n\tB: '\\\\',\n\tN: '\\n',\n\tR: '\\r',\n\tU: '\\u2028',\n\tV: '\\u2029',\n\tZ: '\\0',\n} as const\nconst toEscape = {\n\t'*': '*',\n\t_: '_',\n\t'~': '-',\n\t$: 'S',\n\t'+': 'P',\n\t\"'\": '\"',\n\t'(': 'C',\n\t')': 'D',\n\t'<': 'L',\n\t'>': 'G',\n\t'%': '.',\n\t'?': 'Q',\n\t'#': 'H',\n\t'&': 'A',\n\t'=': 'E',\n\t'\\\\': 'B',\n\t'\\n': 'N',\n\t'\\r': 'R',\n\t'\\0': 'Z',\n\t'\\u2028': 'U',\n\t'\\u2029': 'V',\n} as const\n\nconst error = (msg: string, value: any) => {\n\tthrow new Error(`${msg} ${JSON.stringify(value)}`)\n}\n\n// Either _ by itself or * followed by an encoded char\nconst origChar = (char: string) => {\n\tif (char === '_') {\n\t\treturn ' '\n\t}\n\tconst decoded = fromEscape[char.charAt(1)]\n\tif (!decoded) {\n\t\terror(`Illegal escape code`, char)\n\t}\n\treturn decoded\n}\n\nconst escCode = char => {\n\tif (char === ' ') {\n\t\treturn '_'\n\t}\n\treturn '*' + toEscape[char]\n}\n\nconst escapeRE = /(_|\\*.)/g\n\nconst unescape = (str: string) => {\n\t// oddly enough, testing first is faster\n\treturn escapeRE.test(str) ? str.replace(escapeRE, origChar) : str\n}\n\n// First half: encoding chars; second half: URI and script chars\nconst replaceRE = /([*_~$+'() <>%?#&=\\\\\\n\\r\\0\\u2028\\u2029])/g\n\nconst escape = (str: string) => {\n\t// oddly enough, testing first is faster\n\treturn replaceRE.test(str) ? str.replace(replaceRE, escCode) : str\n}\n\ntype DecodeState = {\n\t_input: string\n\t_idx: number\n\t_length: number\n}\n\nconst eat = (a: DecodeState) => {\n\tlet j: number, c: string\n\tfor (\n\t\tj = a._idx;\n\t\tj < a._length && ((c = a._input.charAt(j)), c !== '~' && c !== ')');\n\t\tj++\n\t) {}\n\tconst w = a._input.slice(a._idx, j)\n\tif (c! === '~') {\n\t\tj++\n\t}\n\ta._idx = j\n\treturn w\n}\n\nconst peek = (a: DecodeState) => {\n\treturn a._input.charAt(a._idx)\n}\n\nconst eatOne = (a: DecodeState) => {\n\ta._idx++\n}\n\nconst EOS = {} // unique symbol\n\nconst decode = (a: DecodeState) => {\n\tlet out: any, k: string\n\tlet c = peek(a)\n\tif (!c) {\n\t\treturn EOS\n\t}\n\tif (c === '(') {\n\t\teatOne(a)\n\t\tout = {}\n\t\tlet t: string | boolean\n\t\twhile (((c = peek(a)), c && c !== ')')) {\n\t\t\tk = unescape(eat(a))\n\t\t\tc = peek(a)\n\t\t\tif (c && c !== ')') {\n\t\t\t\tt = decode(a)\n\t\t\t} else {\n\t\t\t\tt = true\n\t\t\t}\n\t\t\tout[k] = t\n\t\t}\n\t\tif (c === ')') {\n\t\t\teatOne(a)\n\t\t}\n\t} else if (c === '!') {\n\t\teatOne(a)\n\t\tout = []\n\t\twhile (((c = peek(a)), c && c !== '~' && c !== ')')) {\n\t\t\tout.push(decode(a))\n\t\t}\n\t\tif (c === '~') {\n\t\t\teatOne(a)\n\t\t}\n\t} else if (c === '_') {\n\t\teatOne(a)\n\t\tk = unescape(eat(a))\n\t\tif (k.charAt(0) === 'D') {\n\t\t\tout = new Date(k.slice(1))\n\t\t} else if (k in dict) {\n\t\t\tout = dict[k]\n\t\t} else {\n\t\t\terror(`Unknown dict reference`, k)\n\t\t}\n\t} else if (c === '*') {\n\t\teatOne(a)\n\t\tout = unescape(eat(a))\n\t} else if (c === '~') {\n\t\teatOne(a)\n\t\tout = true\n\t} else if (numRE.test(c)) {\n\t\tout = Number(eat(a))\n\t\tif (isNaN(out)) {\n\t\t\terror(`Not a number`, c)\n\t\t}\n\t} else if (stringRE.test(c)) {\n\t\tout = unescape(eat(a))\n\t} else {\n\t\terror('Cannot decode part ', a._input.slice(a._idx, a._idx + 10))\n\t}\n\treturn out\n} // unique symbol\n\nconst encode = (\n\tvalue: any,\n\tout: string[],\n\trich: boolean | undefined,\n\tdepth: number\n) => {\n\tlet t,\n\t\tT = typeof value\n\n\tif (T === 'number') {\n\t\tout.push(\n\t\t\tisFinite(value)\n\t\t\t\t? value.toString()\n\t\t\t\t: rich\n\t\t\t\t\t? isNaN(value)\n\t\t\t\t\t\t? NAN\n\t\t\t\t\t\t: value > 0\n\t\t\t\t\t\t\t? INF\n\t\t\t\t\t\t\t: NINF\n\t\t\t\t\t: NULL\n\t\t)\n\t} else if (T === 'boolean') {\n\t\tout.push(value ? '' : FALSE)\n\t} else if (T === 'string') {\n\t\tt = escape(value)\n\t\tif (stringRE.test(t)) {\n\t\t\tout.push(t)\n\t\t} else {\n\t\t\tout.push('*' + t)\n\t\t}\n\t} else if (T === 'object') {\n\t\tif (!value) {\n\t\t\tout.push(NULL)\n\t\t} else if (rich && value instanceof Date) {\n\t\t\tout.push('_D' + value.toJSON().replace('T00:00:00.000Z', ''))\n\t\t} else if (typeof value.toJSON === 'function') {\n\t\t\tencode(value.toJSON(), out, rich, depth)\n\t\t} else if (Array.isArray(value)) {\n\t\t\tout.push('!')\n\t\t\tfor (let i = 0; i < value.length; i++) {\n\t\t\t\tt = value[i]\n\t\t\t\t// Special case: only use full -T~ in arrays\n\t\t\t\tif (t === true) {\n\t\t\t\t\tout.push(TRUE)\n\t\t\t\t} else {\n\t\t\t\t\tencode(t, out, rich, depth + 1)\n\t\t\t\t}\n\t\t\t}\n\t\t\tout.push('')\n\t\t} else {\n\t\t\tout.push('(')\n\t\t\tfor (const key of Object.keys(value)) {\n\t\t\t\tt = value[key]\n\t\t\t\tif (t !== undefined && typeof t !== 'function') {\n\t\t\t\t\tout.push(escape(key))\n\t\t\t\t\tencode(t, out, rich, depth + 1)\n\t\t\t\t}\n\t\t\t}\n\t\t\twhile (out[out.length - 1] === '') {\n\t\t\t\tout.pop()\n\t\t\t}\n\t\t\tout.push(')')\n\t\t}\n\t} else {\n\t\t// function or undefined\n\t\tout.push(rich || depth === 0 ? UNDEF : NULL)\n\t}\n} // unique symbol\n\nconst antiJSON = {true: '*true', false: '*false', null: '*null'} as const\n\nexport type StringifyOptions = {\n\t/** Use short encoding for small strings by dropping optional closing characters */\n\tshort?: boolean\n\t/** Use rich encoding for JS objects like Dates */\n\trich?: boolean\n}\n\n/** Convert any JS value to a JsUrl2 encoded string */\nexport const stringify = (value: any, options?: StringifyOptions): string => {\n\tlet out: string[] = [],\n\t\tt: string,\n\t\tstr = '',\n\t\tsep = false,\n\t\tshort = options?.short,\n\t\trich = options?.rich\n\tencode(value, out, rich, 0)\n\t// figure out until where we have to stringify\n\tlet len = out.length\n\tdo {\n\t\tt = out[--len]\n\t} while (t === '' || (short && t === ')'))\n\t// extended join('~')\n\tfor (let i = 0; i <= len; i++) {\n\t\tt = out[i]\n\t\tif (sep && t !== ')') {\n\t\t\tstr += '~'\n\t\t}\n\t\tstr += t\n\t\tsep = !(t === '!' || t === '(' || t === ')')\n\t}\n\tif (short) {\n\t\tif (str.length < 6) {\n\t\t\tt = antiJSON[str]\n\t\t\tif (t) str = t\n\t\t}\n\t} else {\n\t\tstr += '~'\n\t}\n\treturn str\n}\n\n// Clean up URI encoded string, whitespace\nconst clean = (str: string) => {\n\tlet out = ''\n\tlet i = 0\n\tlet j = 0\n\tlet c\n\twhile (i < str.length) {\n\t\tc = str.charCodeAt(i)\n\t\tif (c === 37) {\n\t\t\t// %\n\t\t\tif (i > j) out += str.slice(j, i)\n\t\t\t// Deals with Unicode and invalid escape sequences\n\t\t\tstr = decodeURIComponent(str.slice(i))\n\t\t\ti = j = 0\n\t\t} else if (\n\t\t\tc === 32 ||\n\t\t\tc === 10 ||\n\t\t\tc === 13 ||\n\t\t\tc === 0 ||\n\t\t\tc === 8232 ||\n\t\t\tc === 8233\n\t\t) {\n\t\t\t// Ignore whitespace we encode\n\t\t\tif (i > j) out += str.slice(j, i)\n\t\t\ti++\n\t\t\tj = i\n\t\t} else {\n\t\t\ti++\n\t\t}\n\t}\n\tif (i > j) out += str.slice(j, i)\n\treturn out\n}\n\nconst JSONRE = /^({|\\[|\"|true$|false$|null$)/\n\nexport type ParseOptions = {\n\t/** decode URI escapes and remove spaces.\n\t * JsUrl2 doesn't use % or spaces in its encoding so it can safely decode it. */\n\tdeURI?: boolean\n}\n\n/** Parse a JsUrl2 encoded string. Also parses JSON. */\nexport const parse = <T>(encoded: string, options?: ParseOptions): T => {\n\tif (options && options.deURI) encoded = clean(encoded)\n\tif (JSONRE.test(encoded)) return JSON.parse(encoded)\n\tconst l = encoded.length\n\tconst r = decode({_input: encoded, _idx: 0, _length: l})\n\treturn r === EOS ? true : r\n}\n\n/** Parse a JsUrl2 encoded string, and fall back if it fails. Also parses JSON. */\nexport const tryParse = <T>(\n\tencoded: string,\n\tfallback?: T | undefined,\n\toptions?: ParseOptions\n): T => {\n\ttry {\n\t\treturn parse(encoded, options)\n\t} catch {\n\t\treturn fallback as T\n\t}\n}\n"],"names":["stringRE","numRE","NULL","dict","T","F","N","U","n","NaN","I","Infinity","J","fromEscape","_","S","P","C","D","L","G","Q","H","A","E","B","R","V","Z","toEscape","$","error","msg","value","Error","JSON","stringify","origChar","char","decoded","charAt","escCode","escapeRE","unescape","str","test","replace","replaceRE","escape","eat","a","j","c","_idx","_length","_input","w","slice","peek","eatOne","EOS","decode","out","k","t","push","Date","isNaN","encode","rich","depth","isFinite","toString","toJSON","Array","isArray","i","length","key","Object","keys","pop","antiJSON","true","false","null","options","sep","short","len","JSONRE","parse","encoded","deURI","charCodeAt","decodeURIComponent","l","r","tryParse","fallback"],"mappings":"AAEA,MAAMA,IAAW,aACXC,IAAQ,UAGRC,IAAO,MAMPC,IAAO,EACZC,GAAAA,IACAC,GAAG,IACHC,GAAG,MACHC,GAAAA,QACAC,GAAGC,KACHC,GAAGC,OACHC,GAAAA,OAGKC,GAAAA,IAAa,EAClB,KAAK,KACLC,GAAG,KACH,KAAK,KACLC,GAAG,KACHC,GAAG,KACH,KAAK,KACLC,GAAG,KACHC,GAAG,KACHC,GAAG,KACHC,GAAG,KACH,KAAK,KACLC,GAAG,KACHC,GAAG,KACHC,GAAG,KACHC,GAAG,KACHC,GAAG,MACHnB,GAAG;AAAA,GACHoB,GAAG,MACHnB,GAAG,UACHoB,GAAG,UACHC,GAAG,KAEEC,GAAAA,IAAW,EAChB,KAAK,KACLf,GAAG,KACH,KAAK,KACLgB,GAAG,KACH,KAAK,KACL,KAAK,KACL,KAAK,KACL,KAAK,KACL,KAAK,KACL,KAAK,KACL,KAAK,KACL,KAAK,KACL,KAAK,KACL,KAAK,KACL,KAAK,KACL,MAAM,KACN,MAAM,KACN,MAAM,KACN,MAAM,KACN,UAAU,KACV,UAAU,IAGLC,GAAAA,IAAQ,CAACC,GAAaC,MAAAA;AACrB,QAAIC,MAAM,GAAGF,KAAOG,KAAKC,UAAUH;AAAQ,GAI5CI,IAAYC;AACjB,MAAIA,MAAS;AACL,WAAA;AAER,QAAMC,IAAU1B,EAAWyB,EAAKE,OAAO,CAAA,CAAA;AAIhC,SAHFD,KACJR,EAAM,uBAAuBO,CAAAA,GAEvBC;AAAA,GAGFE,IAAkBH,OACnBA,MAAS,MACL,MAED,MAAMT,EAASS,IAGjBI,IAAW,YAEXC,IAAYC,OAEVF,EAASG,KAAKD,CAAOA,IAAAA,EAAIE,QAAQJ,GAAUL,CAAAA,IAAYO,GAIzDG,IAAY,6CAEZC,IAAUJ,OAERG,EAAUF,KAAKD,CAAOA,IAAAA,EAAIE,QAAQC,GAAWN,CAAAA,IAAWG,GAS1DK,IAAOC,OAAAA;AACZ,MAAIC,GAAWC;AACf,OACCD,IAAID,EAAEG,GACNF,IAAID,EAAEI,MAAaF,IAAIF,EAAEK,EAAOf,OAAOW,IAAKC,MAAM,OAAOA,MAAM,MAC/DD;AAAAA;AAED,QAAMK,IAAIN,EAAEK,EAAOE,MAAMP,EAAEG,GAAMF,CAK1B;AAAA,SAJHC,MAAO,OACVD,KAEDD,EAAEG,IAAOF,GACFK;AAAA,GAGFE,IAAQR,OACNA,EAAEK,EAAOf,OAAOU,EAAEG,IAGpBM,IAAUT,OAAAA;AACbA,IAAAG;AAAA,GAGGO,IAAM,CAAA,GAENC,IAAUX,CAAAA,MACf;AAAA,MAAIY,GAAUC,GACVX,IAAIM,EAAKR,CAAAA;AACb,OAAKE;AACG,WAAAQ;AAER,MAAIR,MAAM,KAAK;AAGV,QAAAY;AACJ,SAHAL,EAAOT,IACPY,IAAM,CAAA,GAEGV,IAAIM,EAAKR,IAAKE,KAAKA,MAAM;AAC7BW,MAAAA,IAAApB,EAASM,EAAIC,CACjBE,CAAAA,GAAAA,IAAIM,EAAKR,CAERc,GAAAA,IAAAA,CADGZ,KAAKA,MAAM,OACVS,EAAOX,CAIZY,GAAAA,EAAIC,KAAKC;AAEA,IAANZ,MAAM,OACTO,EAAOT,CAAAA;AAAAA,EACR,WACUE,MAAM,KAAK;AAGZ,SAFTO,EAAOT,CAAAA,GACPY,IAAM,CACGV,GAAAA,IAAIM,EAAKR,CAAKE,GAAAA,KAAKA,MAAM,OAAOA,MAAM;AAC1CU,MAAAA,EAAAG,KAAKJ,EAAOX,CAEP,CAAA;AAAA,IAANE,MAAM,OACTO,EAAOT,CAAAA;AAAAA,EACR;AACgB,IAANE,MAAM,OAChBO,EAAOT,CACHa,GAAAA,IAAApB,EAASM,EAAIC,CAAAA,CAAAA,GACba,EAAEvB,OAAO,OAAO,MACnBsB,IAAM,IAAII,KAAKH,EAAEN,MAAM,MACbM,KAAK5D,IACf2D,IAAM3D,EAAK4D,KAEXhC,EAAM,0BAA0BgC,MAEvBX,MAAM,OAChBO,EAAOT,CACDY,GAAAA,IAAAnB,EAASM,EAAIC,CAAAA,CAAAA,KACTE,MAAM,OAChBO,EAAOT,IACDY,IAAA,MACI7D,EAAM4C,KAAKO,CAAAA,KACfU,KAAOb,EAAIC,CAAAA,GACbiB,MAAML,CACT/B,KAAAA,EAAM,gBAAgBqB,CAEbpD,KAAAA,EAAS6C,KAAKO,CAClBU,IAAAA,IAAAnB,EAASM,EAAIC,CAAAA,CAAAA,IAEbnB,EAAA,uBAAuBmB,EAAEK,EAAOE,MAAMP,EAAEG,GAAMH,EAAEG,IAAO,EAEvD,CAAA;AAAA,SAAAS;AAAA,GAGFM,IAAS,CACdnC,GACA6B,GACAO,GACAC,MAEI;AAAA,MAAAN,GACH5D,IAAW6B,OAAAA;AAEZ,MAAI7B,MAAM;AACL0D,IAAAA,EAAAG,KACHM,SAAStC,KACNA,EAAMuC,SAAAA,IACNH,IACCF,MAAMlC,KA7MD,OA+MJA,IAAQ,IA9MJ,OACC,OAgNN/B;WAEKE,MAAM;AACZ0D,IAAAA,EAAAG,KAAKhC,IAAQ,KAxNL,IAAA;AAAA,WAyNF7B,MAAM;AAChB4D,IAAAA,IAAIhB,EAAOf,CAAAA,GACPjC,EAAS6C,KAAKmB,KACjBF,EAAIG,KAAKD,KAELF,EAAAG,KAAK,MAAMD,CAEjB;AAAA,WAAW5D,MAAM;AAChB,QAAK6B;AAEL,UAAWoC,KAAQpC,aAAiBiC;AAC/BJ,QAAAA,EAAAG,KAAK,OAAOhC,EAAMwC,SAAS3B,QAAQ,kBAAkB;eACvB,OAAjBb,EAAMwC,UAAW;AAClCL,QAAAA,EAAOnC,EAAMwC,OAAUX,GAAAA,GAAKO,GAAMC,CACxB;AAAA,eAAAI,MAAMC,QAAQ1C,CAAAA,GAAQ;AAChC6B,QAAAA,EAAIG,KAAK;AACT,iBAASW,IAAI,GAAGA,IAAI3C,EAAM4C,QAAQD;AACjCZ,UAAAA,IAAI/B,EAAM2C,IAENZ,WACHF,EAAIG,KA9OI,IAAA,IAgPRG,EAAOJ,GAAGF,GAAKO,GAAMC,IAAQ,CAAA;AAG/BR,QAAAA,EAAIG,KAAK,EAAA;AAAA,MAAE,OACL;AACNH,QAAAA,EAAIG,KAAK,GACT;AAAA,mBAAWa,KAAOC,OAAOC,KAAK/C,CAC7B+B;AAAAA,UAAAA,IAAI/B,EAAM6C,CACA,GAANd,MAAM,UAAoBA,OAAAA,KAAM,eAC/BF,EAAAG,KAAKjB,EAAO8B,KAChBV,EAAOJ,GAAGF,GAAKO,GAAMC,IAAQ,CAG/B;AAAA,eAAOR,EAAIA,EAAIe,SAAS,CACvBf,MAD8B;AAC9BA,UAAAA,EAAImB;AAELnB,QAAAA,EAAIG,KAAK;MACV;AAAA;AA9BCH,MAAAA,EAAIG,KAAK/D,CAiCV4D;AAAAA;AAAAA,IAAAA,EAAIG,KAAKI,KAAQC,MAAU,IAjQf,OAiQ2BpE,CAAAA;AACxC,GAGKgF,IAAW,EAACC,MAAM,SAASC,OAAO,UAAUC,MAAM,QAAA,GAU3CjD,IAAY,CAACH,GAAYqD;AACrC,MACCtB,GADGF,IAAgB,CAEnBlB,GAAAA,IAAM,IACN2C,IAAM,IACNC,IAAQF,GAASE,OACjBnB,IAAOiB,GAASjB;AACVD,EAAAA,EAAAnC,GAAO6B,GAAKO,GAAM,CAAA;AAEzB,MAAIoB,IAAM3B,EAAIe;AACX;AACEb,IAAAA,IAAAF,IAAM2B,CACI;AAAA,SAANzB,MAAM,MAAOwB,KAASxB,MAAM;AAErC,WAASY,IAAI,GAAQa,KAALb,GAAUA;AACzBZ,IAAAA,IAAIF,EAAIc,CAAAA,GACJW,KAAOvB,MAAM,QACTpB,KAAA,MAEDA,KAAAoB,GACPuB,IAAc,EAANvB,MAAM,OAAOA,MAAM,OAAOA,MAAM;AAUlC,SARHwB,IACc,IAAb5C,EAAIiC,WACPb,IAAIkB,EAAStC,CACToB,GAAAA,MAASpB,IAAAoB,MAGPpB,KAAA,KAEDA;AAAA,GAqCF8C,IAAS,gCASFC,IAAQ,CAAIC,GAAiBN,MAAAA;AAErC,MADAA,KAAWA,EAAQO,UAAOD,KA3ChBhD,CAAAA;AACd,QAGIQ,GAHAU,IAAM,IACNc,IAAI,GACJzB,IAAI;AAED,WAAAyB,IAAIhC,EAAIiC;AACVzB,MAAAA,IAAAR,EAAIkD,WAAWlB,CACT,GAANxB,MAAM,MAELwB,IAAIzB,MAAUW,KAAAlB,EAAIa,MAAMN,GAAGyB,CAAAA,IAE/BhC,IAAMmD,mBAAmBnD,EAAIa,MAAMmB,CACnCA,CAAAA,GAAAA,IAAIzB,IAAI,KAERC,MAAM,MACNA,MAAM,MACNA,MAAM,MACNA,MAAM,KACNA,MAAM,QACNA,MAAM,QAGFwB,IAAIzB,MAAUW,KAAAlB,EAAIa,MAAMN,GAAGyB,CAAAA,IAC/BA,KACIzB,IAAAyB,KAEJA;AAIK,WADHA,IAAIzB,MAAUW,KAAAlB,EAAIa,MAAMN,GAAGyB,CAAAA,IACxBd;AAAAA,EAAA,GAauC8B,CAC1CF,IAAAA,EAAO7C,KAAK+C,CAAiB;AAAA,WAAAzD,KAAKwD,MAAMC,CAAAA;AAC5C,QAAMI,IAAIJ,EAAQf,QACZoB,IAAIpC,EAAO,EAACN,GAAQqC,GAASvC,GAAM,GAAGC,GAAS0C;AAC9C,SAAAC,MAAMrC,KAAaqC;AAAA,GAIdC,IAAW,CACvBN,GACAO,GACAb,MAEI;AAAA,MAAA;AACI,WAAAK,EAAMC,GAASN;EAAO,QACtB;AACA,WAAAa;AAAAA,EACR;AAAA;"}