UNPKG

@loqate/compose

Version:

Loqate Compose SDK — high-level flows that compose base API clients into easy-to-use sessions.

1 lines 9.65 kB
{"version":3,"file":"parser-D7NErKda.cjs","names":["DEFAULT_HELPERS: Helpers","VERIFICATION_STATUS_RANKING: Record<string, number>","PARSING_STATUS_RANKING: Record<string, number>"],"sources":["../src/utils/handles.ts","../src/utils/avc/parser.ts"],"sourcesContent":["// Handlebars like templater\nexport type Helpers = Record<string, (...args: any[]) => any>;\nexport type RenderOptions = { helpers?: Helpers; escapeHtml?: boolean };\n\nconst DEFAULT_HELPERS: Helpers = {\n upper: (s: any) => String(s).toUpperCase(),\n lower: (s: any) => String(s).toLowerCase(),\n truncate: (s: any, n = 0) => String(s).slice(0, Number(n) || 0),\n replace: (s: any, a: string, b: string) => String(s).split(a).join(b),\n};\n\nconst ESC = (s: string) =>\n s.replace(\n /[&<>\"']/g,\n (c) =>\n ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\", \"'\": \"&#39;\" }[\n c\n ]!)\n );\n\nconst get = (obj: any, path: string): any => {\n if (path === \"this\") return obj;\n if (path === \"@index\") return obj?.[\"@index\"];\n return path\n .split(\".\")\n .reduce((acc, k) => (acc == null ? undefined : acc[k]), obj);\n};\n\nconst applyPipes = (\n val: any,\n rawPipe: string | undefined,\n helpers: Helpers\n) => {\n if (!rawPipe) return val;\n // e.g. \" | upper | truncate 10\"\n for (const seg of rawPipe\n .split(\"|\")\n .map((s) => s.trim())\n .filter(Boolean)) {\n const [name, ...args] = seg.split(/\\s+/);\n const fn = helpers[name];\n if (typeof fn === \"function\")\n val = fn(val, ...args.map((a) => (isFinite(Number(a)) ? Number(a) : a)));\n }\n return val;\n};\n\nexport function render(\n template: string,\n data: any,\n opts: RenderOptions = {}\n): string {\n const helpers = { ...DEFAULT_HELPERS, ...(opts.helpers || {}) };\n const escapeHtml = opts.escapeHtml !== false;\n\n // Resolve innermost #each blocks\n template = template.replace(\n /{{#each\\s+([^}]+)}}([\\s\\S]*?){{\\/each}}/g,\n (_m, path, body) => {\n const list = get(data, path) ?? [];\n if (!Array.isArray(list)) return \"\";\n return list\n .map((item, i) => {\n const scope = Object.assign(Object.create(data ?? null), item, {\n \"@index\": i,\n this: item,\n });\n return render(body, scope, opts);\n })\n .join(\"\");\n }\n );\n\n // Resolve #if (with optional {{else}})\n template = template.replace(\n /{{#if\\s+([^}]+)}}([\\s\\S]*?){{\\/if}}/g,\n (_m, condPath, body) => {\n const [truthy, falsy = \"\"] = String(body).split(/{{\\s*else\\s*}}/);\n const cond = !!get(data, condPath);\n return render(cond ? truthy : falsy, data, opts);\n }\n );\n\n // Triple mustache (no escape, still supports pipes)\n template = template.replace(\n /{{{\\s*([^}|]+)(\\s*\\|[^}]+)?\\s*}}}/g,\n (_m, path, pipe) => {\n path = path.trim();\n const val = applyPipes(get(data, path), pipe, helpers);\n return val == null ? \"\" : String(val);\n }\n );\n\n // Simple mustache with escape and pipes\n template = template.replace(\n /{{\\s*([^}|]+)(\\s*\\|[^}]+)?\\s*}}/g,\n (_m, path, pipe) => {\n path = path.trim();\n const val = applyPipes(get(data, path), pipe, helpers);\n const s = val == null ? \"\" : String(val);\n return escapeHtml ? ESC(s) : s;\n }\n );\n\n return template;\n}\n","export type ParsedAVC = {\n code: string;\n matchscore: number;\n\n verificationStatus: string;\n postMatchLevel: number;\n preMatchLevel: number;\n\n parsingStatus: string;\n lexiconIdentificationMatchLevel: number;\n contextIdentificationMatchLevel: number;\n\n postcodeStatus: string;\n postcodeStatusNumeric: number;\n};\n\nexport const VERIFICATION_STATUS_RANKING: Record<string, number> = {\n V: 5, // Verified\n P: 4, // Partially Verified\n A: 3, // Ambiguous\n R: 2, // Reverted\n U: 1, // Unverified\n};\n\nexport const PARSING_STATUS_RANKING: Record<string, number> = {\n I: 2, // Identified and Parsed\n U: 1, // Unable to parse\n};\n\nexport function parseAVC(code: string): ParsedAVC {\n if (!code || typeof code !== \"string\") {\n throw new Error(\"AVC code must be a non-empty string.\");\n }\n\n const upper = code.toUpperCase().trim();\n const parts = upper.split(\"-\");\n\n if (parts.length !== 4) {\n throw new Error(\"Invalid AVC format. Expected 4 parts.\");\n }\n\n const [part1, part2, postcodeStatus, matchscoreStr] = parts;\n\n const matchscore = numericOrThrow(matchscoreStr, \"Matchscore\");\n\n // part1: [verificationStatus][postMatchLevel][preMatchLevel]\n const p1 = part1.split(\"\");\n if (p1.length !== 3) {\n throw new Error(\"Invalid part1 format. Expected 3 characters.\");\n }\n\n const verificationStatus = p1[0];\n const postMatchLevel = numericOrThrow(\n p1[1],\n \"Post-Processed Verification Match Level\"\n );\n const preMatchLevel = numericOrThrow(\n p1[2],\n \"Pre-Processed Verification Match Level\"\n );\n\n if (!(verificationStatus in VERIFICATION_STATUS_RANKING)) {\n throw new Error(`Invalid Verification Status: ${verificationStatus}`);\n }\n\n // part2: [parsingStatus][lexiconIdentificationMatchLevel][contextIdentificationMatchLevel]\n const p2 = part2.split(\"\");\n if (p2.length !== 3) {\n throw new Error(\"Invalid part2 format. Expected 3 characters.\");\n }\n\n const parsingStatus = p2[0];\n const lexiconIdentificationMatchLevel = numericOrThrow(\n p2[1],\n \"Lexicon Identification Match Level\"\n );\n const contextIdentificationMatchLevel = numericOrThrow(\n p2[2],\n \"Context Identification Match Level\"\n );\n\n if (!(parsingStatus in PARSING_STATUS_RANKING)) {\n throw new Error(`Invalid Parsing Status: ${parsingStatus}`);\n }\n\n const postcodeStatusNumeric = postcodeStatusToNumeric(postcodeStatus);\n\n return {\n code: upper,\n matchscore,\n verificationStatus,\n postMatchLevel,\n preMatchLevel,\n parsingStatus,\n lexiconIdentificationMatchLevel,\n contextIdentificationMatchLevel,\n postcodeStatus,\n postcodeStatusNumeric,\n };\n}\n\nfunction numericOrThrow(value: string, valueName: string): number {\n const num = Number(value);\n if (isNaN(num)) {\n throw new Error(`${valueName} must be numeric.`);\n }\n return num;\n}\n\nfunction postcodeStatusToNumeric(value: string): number {\n const chars = value.split(\"\");\n if (chars.length !== 2) {\n throw new Error(\"Invalid Postcode Status format. Expected 2 characters.\");\n }\n return numericOrThrow(chars[1], \"Postcode Status\");\n}\n"],"mappings":"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"}