@loqate/compose
Version:
Loqate Compose SDK — high-level flows that compose base API clients into easy-to-use sessions.
145 lines (142 loc) • 4.89 kB
JavaScript
//#region src/utils/handles.ts
const DEFAULT_HELPERS = {
upper: (s) => String(s).toUpperCase(),
lower: (s) => String(s).toLowerCase(),
truncate: (s, n = 0) => String(s).slice(0, Number(n) || 0),
replace: (s, a, b) => String(s).split(a).join(b)
};
const ESC = (s) => s.replace(/[&<>"']/g, (c) => ({
"&": "&",
"<": "<",
">": ">",
"\"": """,
"'": "'"
})[c]);
const get = (obj, path) => {
if (path === "this") return obj;
if (path === "@index") return obj?.["@index"];
return path.split(".").reduce((acc, k) => acc == null ? void 0 : acc[k], obj);
};
const applyPipes = (val, rawPipe, helpers) => {
if (!rawPipe) return val;
for (const seg of rawPipe.split("|").map((s) => s.trim()).filter(Boolean)) {
const [name, ...args] = seg.split(/\s+/);
const fn = helpers[name];
if (typeof fn === "function") val = fn(val, ...args.map((a) => isFinite(Number(a)) ? Number(a) : a));
}
return val;
};
function render(template, data, opts = {}) {
const helpers = {
...DEFAULT_HELPERS,
...opts.helpers || {}
};
const escapeHtml = opts.escapeHtml !== false;
template = template.replace(/{{#each\s+([^}]+)}}([\s\S]*?){{\/each}}/g, (_m, path, body) => {
const list = get(data, path) ?? [];
if (!Array.isArray(list)) return "";
return list.map((item, i) => {
return render(body, Object.assign(Object.create(data ?? null), item, {
"@index": i,
this: item
}), opts);
}).join("");
});
template = template.replace(/{{#if\s+([^}]+)}}([\s\S]*?){{\/if}}/g, (_m, condPath, body) => {
const [truthy, falsy = ""] = String(body).split(/{{\s*else\s*}}/);
return render(!!get(data, condPath) ? truthy : falsy, data, opts);
});
template = template.replace(/{{{\s*([^}|]+)(\s*\|[^}]+)?\s*}}}/g, (_m, path, pipe) => {
path = path.trim();
const val = applyPipes(get(data, path), pipe, helpers);
return val == null ? "" : String(val);
});
template = template.replace(/{{\s*([^}|]+)(\s*\|[^}]+)?\s*}}/g, (_m, path, pipe) => {
path = path.trim();
const val = applyPipes(get(data, path), pipe, helpers);
const s = val == null ? "" : String(val);
return escapeHtml ? ESC(s) : s;
});
return template;
}
//#endregion
//#region src/utils/avc/parser.ts
const VERIFICATION_STATUS_RANKING = {
V: 5,
P: 4,
A: 3,
R: 2,
U: 1
};
const PARSING_STATUS_RANKING = {
I: 2,
U: 1
};
function parseAVC(code) {
if (!code || typeof code !== "string") throw new Error("AVC code must be a non-empty string.");
const upper = code.toUpperCase().trim();
const parts = upper.split("-");
if (parts.length !== 4) throw new Error("Invalid AVC format. Expected 4 parts.");
const [part1, part2, postcodeStatus, matchscoreStr] = parts;
const matchscore = numericOrThrow(matchscoreStr, "Matchscore");
const p1 = part1.split("");
if (p1.length !== 3) throw new Error("Invalid part1 format. Expected 3 characters.");
const verificationStatus = p1[0];
const postMatchLevel = numericOrThrow(p1[1], "Post-Processed Verification Match Level");
const preMatchLevel = numericOrThrow(p1[2], "Pre-Processed Verification Match Level");
if (!(verificationStatus in VERIFICATION_STATUS_RANKING)) throw new Error(`Invalid Verification Status: ${verificationStatus}`);
const p2 = part2.split("");
if (p2.length !== 3) throw new Error("Invalid part2 format. Expected 3 characters.");
const parsingStatus = p2[0];
const lexiconIdentificationMatchLevel = numericOrThrow(p2[1], "Lexicon Identification Match Level");
const contextIdentificationMatchLevel = numericOrThrow(p2[2], "Context Identification Match Level");
if (!(parsingStatus in PARSING_STATUS_RANKING)) throw new Error(`Invalid Parsing Status: ${parsingStatus}`);
return {
code: upper,
matchscore,
verificationStatus,
postMatchLevel,
preMatchLevel,
parsingStatus,
lexiconIdentificationMatchLevel,
contextIdentificationMatchLevel,
postcodeStatus,
postcodeStatusNumeric: postcodeStatusToNumeric(postcodeStatus)
};
}
function numericOrThrow(value, valueName) {
const num = Number(value);
if (isNaN(num)) throw new Error(`${valueName} must be numeric.`);
return num;
}
function postcodeStatusToNumeric(value) {
const chars = value.split("");
if (chars.length !== 2) throw new Error("Invalid Postcode Status format. Expected 2 characters.");
return numericOrThrow(chars[1], "Postcode Status");
}
//#endregion
Object.defineProperty(exports, 'PARSING_STATUS_RANKING', {
enumerable: true,
get: function () {
return PARSING_STATUS_RANKING;
}
});
Object.defineProperty(exports, 'VERIFICATION_STATUS_RANKING', {
enumerable: true,
get: function () {
return VERIFICATION_STATUS_RANKING;
}
});
Object.defineProperty(exports, 'parseAVC', {
enumerable: true,
get: function () {
return parseAVC;
}
});
Object.defineProperty(exports, 'render', {
enumerable: true,
get: function () {
return render;
}
});
//# sourceMappingURL=parser-D7NErKda.cjs.map