@lacspace/nepali-match 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -91,8 +91,9 @@ var ALIASES = {
91
91
  Kathmandu: { en: ["Katmandu"], ne: ["\u0915\u093E\u0920\u092E\u093E\u0923\u094D\u0921\u094C", "\u0915\u093E\u0920\u092E\u093E\u0928\u094D\u0921\u0941"] },
92
92
  Kavrepalanchok: { en: ["Kavre", "Kabhre", "Kabhrepalanchok", "Kavrepalanchowk"], ne: ["\u0915\u093E\u092D\u094D\u0930\u0947", "\u0915\u093E\u092D\u094D\u0930\u0947\u092A\u0932\u093E\u0928\u094D\u091A\u094B\u0915"] },
93
93
  Sindhupalchok: { en: ["Sindhupalchowk"] },
94
- "Nawalparasi East": { en: ["Nawalparasi (East)", "East Nawalparasi", "Nawalpur"], ne: ["\u0928\u0935\u0932\u092A\u0930\u093E\u0938\u0940 \u092A\u0942\u0930\u094D\u0935", "\u092A\u0942\u0930\u094D\u0935\u0940 \u0928\u0935\u0932\u092A\u0930\u093E\u0938\u0940", "\u0928\u0935\u0932\u092A\u0941\u0930"] },
95
- "Nawalparasi West": { en: ["Nawalparasi (West)", "West Nawalparasi"], ne: ["\u0928\u0935\u0932\u092A\u0930\u093E\u0938\u0940 \u092A\u0936\u094D\u091A\u093F\u092E", "\u092A\u0936\u094D\u091A\u093F\u092E\u0940 \u0928\u0935\u0932\u092A\u0930\u093E\u0938\u0940"] },
94
+ Solukhumbu: { en: ["Solu Khumbu"], ne: ["\u0938\u094B\u0932\u0941 \u0916\u0941\u092E\u094D\u092C\u0941"] },
95
+ "Nawalparasi East": { en: ["Nawalparasi (East)", "East Nawalparasi", "Nawalpur", "Bardaghat Susta East"], ne: ["\u0928\u0935\u0932\u092A\u0930\u093E\u0938\u0940 \u092A\u0942\u0930\u094D\u0935", "\u092A\u0942\u0930\u094D\u0935\u0940 \u0928\u0935\u0932\u092A\u0930\u093E\u0938\u0940", "\u0928\u0935\u0932\u092A\u0941\u0930", "\u092C\u0930\u094D\u0926\u0918\u093E\u091F \u0938\u0941\u0938\u094D\u0924\u093E \u092A\u0942\u0930\u094D\u0935"] },
96
+ "Nawalparasi West": { en: ["Nawalparasi (West)", "West Nawalparasi", "Bardaghat Susta West"], ne: ["\u0928\u0935\u0932\u092A\u0930\u093E\u0938\u0940 \u092A\u0936\u094D\u091A\u093F\u092E", "\u092A\u0936\u094D\u091A\u093F\u092E\u0940 \u0928\u0935\u0932\u092A\u0930\u093E\u0938\u0940", "\u092C\u0930\u094D\u0926\u0918\u093E\u091F \u0938\u0941\u0938\u094D\u0924\u093E \u092A\u0936\u094D\u091A\u093F\u092E"] },
96
97
  "Eastern Rukum": { en: ["Rukum East", "East Rukum", "Rukum (East)"], ne: ["\u0930\u0941\u0915\u0941\u092E \u092A\u0942\u0930\u094D\u0935"] },
97
98
  "Western Rukum": { en: ["Rukum West", "West Rukum", "Rukum (West)"], ne: ["\u0930\u0941\u0915\u0941\u092E \u092A\u0936\u094D\u091A\u093F\u092E"] },
98
99
  Tanahun: { en: ["Tanahu"], ne: ["\u0924\u0928\u0939\u0941"] },
@@ -117,11 +118,13 @@ var slug = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g,
117
118
  function districtTerms() {
118
119
  return DISTRICTS.map((d) => {
119
120
  const extra = ALIASES[d.name] ?? {};
120
- const ne = /* @__PURE__ */ new Set([d.nameNp, ...extra.ne ?? []]);
121
+ const own = d.aliases ?? [];
122
+ const isNe = (x) => /[\u0900-\u097f]/.test(x);
123
+ const ne = /* @__PURE__ */ new Set([d.nameNp, ...extra.ne ?? [], ...own.filter(isNe)]);
121
124
  for (const n of [...ne]) if (n.endsWith("\u0919")) ne.add(n.slice(0, -1) + "\u0902\u0917");
122
125
  return {
123
126
  id: slug(d.name),
124
- en: [d.name, ...extra.en ?? []],
127
+ en: [.../* @__PURE__ */ new Set([d.name, ...extra.en ?? [], ...own.filter((x) => !isNe(x))])],
125
128
  ne: [...ne],
126
129
  province: d.province,
127
130
  ...AMBIGUOUS.has(d.name) ? { ambiguous: true } : {},
@@ -131,7 +134,7 @@ function districtTerms() {
131
134
  }
132
135
 
133
136
  // src/index.ts
134
- var VERSION = "1.0.0";
137
+ var VERSION = "1.1.0";
135
138
  var NUKTA = 2364;
136
139
  var HALANT = 2381;
137
140
  var NASALS = /* @__PURE__ */ new Set([2329, 2334, 2339, 2344, 2350]);
@@ -158,6 +161,8 @@ var LOOSE = {
158
161
  2339: 2344
159
162
  // ण → न
160
163
  };
164
+ var isSpace = (c) => c === 32 || c >= 9 && c <= 13 || c === 160 || c === 5760 || c >= 8192 && c <= 8202 || c === 8232 || c === 8233 || c === 8239 || c === 8287 || c === 12288;
165
+ var isHyphen = (c) => c === 45 || c === 8208 || c === 8209;
161
166
  var isConsonant = (c) => c >= 2325 && c <= 2361 || c >= 2392 && c <= 2399;
162
167
  function mapNormalise(input, opts = {}) {
163
168
  const digits = opts.digits !== false;
@@ -177,7 +182,7 @@ function mapNormalise(input, opts = {}) {
177
182
  for (let i = 0; i < s.length; i++) {
178
183
  let c = s.charCodeAt(i);
179
184
  if (DROP.has(c)) continue;
180
- if (/\s/.test(s[i])) {
185
+ if (isSpace(c) || opts.hyphenAsSpace && isHyphen(c)) {
181
186
  if (!lastSpace) push(" ", i);
182
187
  lastSpace = true;
183
188
  continue;
@@ -251,7 +256,12 @@ var POSTPOSITIONS = [
251
256
  "\u0935\u093E\u0938\u0940",
252
257
  "\u092C\u093E\u0938\u0940",
253
258
  "\u0938\u094D\u0925\u093F\u0924",
254
- "\u0928\u093F\u0935\u093E\u0938\u0940"
259
+ "\u0928\u093F\u0935\u093E\u0938\u0940",
260
+ "\u092E\u093E\u0930\u094D\u092B\u0924",
261
+ "\u092C\u093E\u0930\u0947",
262
+ "\u0905\u0928\u094D\u0924\u0930\u094D\u0917\u0924",
263
+ "\u092E\u093E\u0924\u094D\u0930",
264
+ "\u091C\u0938\u094D\u0924\u093E"
255
265
  ];
256
266
  var isNeLetter = (c) => c >= 2304 && c <= 2403 || c >= 2417 && c <= 2431;
257
267
  var isWordChar = (ch) => !!ch && /[\p{L}\p{M}\p{N}]/u.test(ch);
@@ -261,9 +271,18 @@ function autoCase(term) {
261
271
  const letters = term.replace(/[^\p{L}]/gu, "");
262
272
  return letters.length >= 2 && letters.length <= 5 && letters === letters.toUpperCase() && letters !== letters.toLowerCase();
263
273
  }
274
+ var optsKey = (o) => `${o.loose ? 1 : 0}${o.digits === false ? 0 : 1}${o.hyphenAsSpace ? 1 : 0}`;
275
+ function prepare(text, opts = {}) {
276
+ const nfc = text.normalize("NFC");
277
+ const { text: n, map } = mapNormalise(nfc, opts);
278
+ return { kind: "prepared", original: nfc, text: n, map, key: optsKey(opts) };
279
+ }
280
+ var isPrepared = (x) => typeof x === "object" && x !== null && x.kind === "prepared";
264
281
  function createMatcher(terms, opts = {}) {
265
- const nopts = { loose: opts.loose, digits: opts.digits };
266
- const suffixes = opts.postpositions === false ? [] : [...new Set([...POSTPOSITIONS, ...opts.extraSuffixes ?? []].map((s) => normaliseNe(s, nopts)))].sort((a, b) => b.length - a.length);
282
+ const nopts = { loose: opts.loose, digits: opts.digits, hyphenAsSpace: opts.hyphenAsSpace };
283
+ const key = optsKey(nopts);
284
+ const allCaps = opts.allCaps !== false;
285
+ const suffixes = [...new Set([...opts.postpositions === false ? [] : POSTPOSITIONS, ...opts.extraSuffixes ?? []].map((s) => normaliseNe(s, nopts)))].filter(Boolean).sort((a, b) => b.length - a.length);
267
286
  const variants = [];
268
287
  for (const t of terms) {
269
288
  const obj = typeof t === "string" ? { id: t, aliases: [t] } : t;
@@ -272,12 +291,26 @@ function createMatcher(terms, opts = {}) {
272
291
  const id = obj.id ?? spellings[0] ?? "";
273
292
  for (const sp of new Set(spellings)) {
274
293
  if (hasDevanagari(sp)) {
275
- variants.push({ id, term: sp, lang: "ne", key: normaliseNe(sp, nopts) });
294
+ const k = normaliseNe(sp, nopts);
295
+ variants.push({ id, term: sp, lang: "ne", key: k });
296
+ if (opts.joinNepali && k.includes(" ")) {
297
+ const parts = k.split(" ");
298
+ const gaps = Math.min(parts.length - 1, 4);
299
+ for (let mask = 1; mask < 1 << gaps; mask++) {
300
+ let joined = parts[0];
301
+ for (let g = 1; g < parts.length; g++) joined += (g <= gaps && mask & 1 << g - 1 ? "" : " ") + parts[g];
302
+ variants.push({ id, term: sp, lang: "ne", key: joined });
303
+ }
304
+ }
276
305
  } else {
277
306
  const rule = obj.caseSensitive ?? opts.caseSensitive ?? "auto";
278
307
  const exact = rule === "auto" ? autoCase(sp) : rule;
279
- const body = sp.split(/\s+/).map(escapeRe).join("\\s+");
280
- variants.push({ id, term: sp, lang: "en", key: sp, re: new RegExp(body, exact ? "gu" : "giu") });
308
+ const words = normaliseNe(sp, nopts).split(" ").filter(Boolean);
309
+ const body = words.map(escapeRe).join(" ");
310
+ const letters = sp.replace(/[^\p{L}]/gu, "");
311
+ const shout = exact && allCaps && letters.length > 5 && sp !== sp.toUpperCase();
312
+ const alt = shout ? `|${words.map((w) => escapeRe(w.toUpperCase())).join(" ")}` : "";
313
+ variants.push({ id, term: sp, lang: "en", key: sp, re: new RegExp(`(?:${body}${alt})`, exact ? "gu" : "giu") });
281
314
  }
282
315
  }
283
316
  }
@@ -292,10 +325,11 @@ function createMatcher(terms, opts = {}) {
292
325
  }
293
326
  return -1;
294
327
  };
295
- const find = (text) => {
296
- const { text: n, map } = mapNormalise(text, nopts);
297
- const toOrig = (i) => i >= map.length ? text.normalize("NFC").length : map[i];
298
- const nfc = text.normalize("NFC");
328
+ const prep = (text) => prepare(text, nopts);
329
+ const find = (input) => {
330
+ const p = isPrepared(input) ? input.key === key ? input : prep(input.original) : prep(input);
331
+ const { text: n, map, original: nfc } = p;
332
+ const toOrig = (i) => i >= map.length ? nfc.length : map[i];
299
333
  const found = [];
300
334
  for (const v of variants) {
301
335
  if (v.lang === "ne") {
@@ -343,9 +377,11 @@ function createMatcher(terms, opts = {}) {
343
377
  find,
344
378
  test: (text, id) => find(text).some((m) => id === void 0 || m.id === id),
345
379
  ids: (text) => [...new Set(find(text).map((m) => m.id))],
380
+ prepare: prep,
346
381
  get size() {
347
382
  return variants.length;
348
- }
383
+ },
384
+ kind: "matcher"
349
385
  };
350
386
  }
351
387
  function findTerms(text, terms, opts) {
@@ -368,6 +404,8 @@ function splitSuffix(word) {
368
404
  }
369
405
  return { stem: nfc.slice(0, end >= n.length ? nfc.length : map[end]), suffixes: out };
370
406
  }
407
+ var isMatcher = (x) => typeof x === "object" && x !== null && x.kind === "matcher";
408
+ var isMatchList = (x) => Array.isArray(x) && (x.length === 0 || typeof x[0] === "object" && x[0] !== null && typeof x[0].index === "number" && typeof x[0].end === "number");
371
409
  var dottedAbbr = (s, i) => {
372
410
  let j = i - 1;
373
411
  while (j >= 0 && !/\s/.test(s[j])) j--;
@@ -393,10 +431,16 @@ function near(text, a, b, maxCharsOrOpts = {}, sameSentence) {
393
431
  if (sameSentence !== void 0) opts.sameSentence = sameSentence;
394
432
  const max = opts.maxChars ?? 60;
395
433
  const same = opts.sameSentence !== false;
396
- const am = findTerms(text, a, opts);
397
- const bm = findTerms(text, b, opts);
398
- if (!am.length || !bm.length) return null;
399
- const spans = same ? sentenceSpans(text) : [];
434
+ const resolve = (src) => {
435
+ if (isMatcher(src)) return src.find(text);
436
+ if (isMatchList(src)) return src;
437
+ return findTerms(text, src, opts);
438
+ };
439
+ const am = resolve(a);
440
+ if (!am.length) return null;
441
+ const bm = resolve(b);
442
+ if (!bm.length) return null;
443
+ const spans = same ? sentenceSpans(isPrepared(text) ? text.original : text) : [];
400
444
  const sentenceOf = (i) => spans.findIndex(([s, e]) => i >= s && i < e);
401
445
  let best = null;
402
446
  for (const x of am) {
@@ -411,6 +455,6 @@ function near(text, a, b, maxCharsOrOpts = {}, sameSentence) {
411
455
  return best;
412
456
  }
413
457
 
414
- export { POSTPOSITIONS, VERSION, contains, createMatcher, districtTerms, findTerms, near, normaliseNe, normalizeNe, sentenceSpans, splitSuffix };
458
+ export { POSTPOSITIONS, VERSION, contains, createMatcher, districtTerms, findTerms, near, normaliseNe, normalizeNe, prepare, sentenceSpans, splitSuffix };
415
459
  //# sourceMappingURL=index.js.map
416
460
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../nepali-utils/src/index.ts","../src/districts.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @lacspace/nepali-utils — everyday Nepal helpers: NPR currency formatting,\n * Nepali (South-Asian) digit grouping, Devanagari numerals, amount-in-words,\n * common validators, and province data. Zero-dependency, isomorphic.\n */\n\nconst DEVANAGARI = [\"०\", \"१\", \"२\", \"३\", \"४\", \"५\", \"६\", \"७\", \"८\", \"९\"] as const;\n\n/** Convert Arabic numerals to Devanagari: `\"2081\"` → `\"२०८१\"`. */\nexport function toDevanagari(input: string | number): string {\n return String(input).replace(/[0-9]/g, (d) => DEVANAGARI[Number(d)]!);\n}\n\n/** Convert Devanagari numerals to Arabic: `\"२०८१\"` → `\"2081\"`. */\nexport function fromDevanagari(input: string): string {\n return input.replace(/[०-९]/g, (d) => String(DEVANAGARI.indexOf(d as (typeof DEVANAGARI)[number])));\n}\n\n/**\n * Group a number with the South-Asian (Nepali) system: the last three digits,\n * then groups of two. `1234567` → `\"12,34,567\"`; decimals are kept: `2587.25` → `\"2,587.25\"`.\n */\nexport function groupNepali(value: number | string): string {\n const str = String(value).trim();\n const negative = str.startsWith(\"-\");\n const sign = negative ? \"-\" : \"\";\n const [intRaw = \"\", ...decs] = str.split(\".\");\n const s = intRaw.replace(/[^\\d]/g, \"\");\n const dec = decs.length ? \".\" + decs.join(\"\").replace(/[^\\d]/g, \"\") : \"\";\n if (s.length <= 3) return sign + s + dec;\n const last3 = s.slice(-3);\n const rest = s.slice(0, -3);\n return sign + rest.replace(/\\B(?=(\\d{2})+(?!\\d))/g, \",\") + \",\" + last3 + dec;\n}\n\nexport interface FormatNprOptions {\n /** Show a symbol/prefix. Default `\"Rs. \"`. Use `\"\"` for none. */\n symbol?: string;\n /** Number of decimal places. Default `2`. */\n decimals?: number;\n /** Render digits in Devanagari. Default `false`. */\n devanagari?: boolean;\n}\n\n/**\n * Format an amount as Nepalese Rupees with South-Asian grouping.\n * `formatNPR(1234567.5)` → `\"Rs. 12,34,567.50\"`.\n */\nexport function formatNPR(amount: number, options: FormatNprOptions = {}): string {\n const { symbol = \"Rs. \", decimals = 2, devanagari = false } = options;\n const negative = amount < 0;\n const fixed = Math.abs(amount).toFixed(decimals);\n const [intPart, decPart] = fixed.split(\".\");\n let out = groupNepali(intPart!);\n if (decPart) out += \".\" + decPart;\n if (devanagari) out = toDevanagari(out);\n return (negative ? \"-\" : \"\") + symbol + out;\n}\n\nconst ONES = [\n \"\", \"One\", \"Two\", \"Three\", \"Four\", \"Five\", \"Six\", \"Seven\", \"Eight\", \"Nine\",\n \"Ten\", \"Eleven\", \"Twelve\", \"Thirteen\", \"Fourteen\", \"Fifteen\", \"Sixteen\",\n \"Seventeen\", \"Eighteen\", \"Nineteen\",\n];\nconst TENS = [\"\", \"\", \"Twenty\", \"Thirty\", \"Forty\", \"Fifty\", \"Sixty\", \"Seventy\", \"Eighty\", \"Ninety\"];\n\nfunction twoDigitWords(n: number): string {\n if (n < 20) return ONES[n]!;\n const t = Math.floor(n / 10);\n const o = n % 10;\n return TENS[t]! + (o ? \" \" + ONES[o] : \"\");\n}\n\n/**\n * Amount in words using the South-Asian system (Thousand, Lakh, Crore, Arab, Kharab, Neel, Padma) —\n * perfect for invoices. `numberToWords(1234567)` →\n * `\"Twelve Lakh Thirty Four Thousand Five Hundred Sixty Seven\"`.\n */\nexport function numberToWords(value: number): string {\n let n = Math.floor(Math.abs(value));\n if (n === 0) return \"Zero\";\n const parts: string[] = [];\n const units: [number, string][] = [\n [1e15, \"Padma\"],\n [1e13, \"Neel\"],\n [1e11, \"Kharab\"],\n [1e9, \"Arab\"],\n [1e7, \"Crore\"],\n [1e5, \"Lakh\"],\n [1e3, \"Thousand\"],\n [1e2, \"Hundred\"],\n ];\n for (const [size, name] of units) {\n if (n >= size) {\n const count = Math.floor(n / size);\n // Hundreds are 1–9; middle units take two digits; the top (Padma) unit can\n // itself run into the hundreds/thousands, so render its count recursively.\n const words = size === 100 ? ONES[count]! : count >= 100 ? numberToWords(count) : twoDigitWords(count);\n parts.push(words + \" \" + name);\n n %= size;\n }\n }\n if (n > 0) parts.push(twoDigitWords(n));\n return (value < 0 ? \"Minus \" : \"\") + parts.join(\" \");\n}\n\n/**\n * An amount for the amount-in-words helpers: a number (fine up to ~13 rupee\n * digits), a decimal string (`\"99999999999999.01\"`, grouping commas allowed),\n * or exact integer parts `{ rupees, paisa }`. Strings and parts avoid float\n * drift, so the paisa is always exact.\n */\nexport type AmountInput = number | string | { rupees: number; paisa?: number; negative?: boolean };\n\n/** Split an {@link AmountInput} into exact integer rupees + paisa (0–99) and a sign. */\nexport function splitAmount(input: AmountInput): { rupees: number; paisa: number; negative: boolean } {\n let negative = false, rupees: number, paisa: number;\n if (typeof input === \"number\") {\n if (!Number.isFinite(input)) throw new RangeError(\"amount must be finite\");\n negative = input < 0;\n const a = Math.abs(input);\n rupees = Math.floor(a);\n paisa = Math.round((a - rupees) * 100);\n } else if (typeof input === \"string\") {\n const m = /^\\s*(-)?\\s*(?:rs\\.?|npr|रु\\.?|रू\\.?)?\\s*([\\d,]*)(?:\\.(\\d*))?\\s*$/i.exec(fromDevanagari(input));\n if (!m || (!m[2] && !m[3])) throw new RangeError(`not a decimal amount: ${input}`);\n negative = !!m[1];\n const intDigits = (m[2] || \"0\").replace(/,/g, \"\");\n rupees = Number(intDigits);\n const frac = (m[3] || \"\").padEnd(3, \"0\");\n paisa = Number(frac.slice(0, 2)) + (Number(frac[2]) >= 5 ? 1 : 0); // round half up on the 3rd decimal\n } else {\n rupees = Math.trunc(Math.abs(input.rupees));\n paisa = Math.round(Math.abs(input.paisa ?? 0));\n negative = !!input.negative || input.rupees < 0;\n if (paisa > 99) { rupees += Math.floor(paisa / 100); paisa %= 100; }\n }\n if (paisa === 100) { rupees += 1; paisa = 0; }\n if (!Number.isSafeInteger(rupees)) throw new RangeError(\"rupees exceed Number.MAX_SAFE_INTEGER\");\n return { rupees, paisa, negative: negative && (rupees > 0 || paisa > 0) };\n}\n\n/** `numberToWords` plus a \"Rupees … only\" wrapper for invoices. Accepts a number, decimal string or `{ rupees, paisa }`. */\nexport function amountInWords(amount: AmountInput): string {\n const { rupees, paisa, negative } = splitAmount(amount);\n let out = \"Rupees \" + numberToWords(rupees);\n if (paisa > 0) out += \" and \" + numberToWords(paisa) + \" Paisa\";\n return (negative ? \"Minus \" : \"\") + out + \" Only\";\n}\n\n/** Parse a formatted NPR/Nepali-grouped string back to a number (inverse of {@link formatNPR}). */\nexport function ungroupNepali(input: string | number): number {\n let s = fromDevanagari(String(input)).trim();\n const negative = s.startsWith(\"-\");\n s = s.replace(/₹|rs\\.?|npr|रू\\.?|रु\\.?/gi, \"\"); // strip currency symbols (incl. the dot in \"Rs.\")\n s = s.replace(/[^\\d.]/g, \"\"); // drop grouping commas, spaces, everything else\n if (s === \"\") return NaN;\n const num = Number(s);\n return negative && !Number.isNaN(num) ? -num : num;\n}\n\n/** Alias of {@link ungroupNepali} — parse an NPR string to a number. */\nexport const parseNPR = ungroupNepali;\n\nfunction trimNum(n: number): string {\n return Number(n.toFixed(2)).toString();\n}\n\nexport interface CompactNprOptions {\n symbol?: string;\n /** Use Nepali scale words (हजार/लाख/करोड/अरब/खरब). Default false (K/Lakh/Cr/Arab/Kharab). */\n nepali?: boolean;\n /** Render digits in Devanagari. Default false. */\n devanagari?: boolean;\n /**\n * Word style. \"short\" (default): K/Lakh/Cr/Arab/Kharab, हजार/लाख/करोड/अरब/खरब.\n * \"long\": Thousand/Lakh/Crore/Arba/Kharba, हजार/लाख/करोड/अर्ब/खर्ब (newsroom spelling).\n */\n style?: \"short\" | \"long\";\n /** Fixed decimal places (e.g. 2 → \"4.29\"). Default: up to 2, trailing zeros trimmed. */\n decimals?: number;\n}\n\n/** Compact NPR: `formatCompactNPR(1234567)` → `\"Rs. 12.35 Lakh\"`. */\nexport function formatCompactNPR(amount: number, options: CompactNprOptions = {}): string {\n const { symbol = \"Rs. \", nepali = false, devanagari = false, style = \"short\", decimals } = options;\n const negative = amount < 0;\n const a = Math.abs(amount);\n const long = style === \"long\";\n const units: [number, string, string][] = [\n [1e15, \"Padma\", \"पद्म\"],\n [1e13, \"Neel\", \"नील\"],\n [1e11, long ? \"Kharba\" : \"Kharab\", long ? \"खर्ब\" : \"खरब\"],\n [1e9, long ? \"Arba\" : \"Arab\", long ? \"अर्ब\" : \"अरब\"],\n [1e7, long ? \"Crore\" : \"Cr\", \"करोड\"],\n [1e5, \"Lakh\", \"लाख\"],\n [1e3, long ? \"Thousand\" : \"K\", \"हजार\"],\n ];\n const num = (n: number) => (decimals === undefined ? trimNum(n) : n.toFixed(decimals));\n let out: string;\n const u = units.find(([size]) => a >= size);\n if (u) out = `${num(a / u[0])} ${nepali ? u[2] : u[1]}`;\n else out = num(a);\n if (devanagari) out = toDevanagari(out);\n return (negative ? \"-\" : \"\") + symbol + out;\n}\n\n/* ------------------------------ land-area units ------------------------------ */\n\nexport type LandUnit = \"ropani\" | \"aana\" | \"paisa\" | \"daam\" | \"bigha\" | \"kattha\" | \"dhur\" | \"sqm\" | \"sqft\";\n\nconst SQFT = 0.09290304; // exact: 1 international foot = 0.3048 m\nconst ROPANI = 5476 * SQFT; // 508.73704704 m²\nconst BIGHA = 72900 * SQFT; // 6772.631616 m²\n\n/**\n * Area of one unit in square metres (hilly = ropani system, terai = bigha system).\n * Every sub-unit is derived from its parent (ropani/16/64/256, bigha/20/400), so\n * unit ratios are exact and `convertLand(1, \"ropani\", \"daam\")` is exactly 256.\n */\nexport const LAND_UNIT_SQM: Record<LandUnit, number> = {\n ropani: ROPANI,\n aana: ROPANI / 16,\n paisa: ROPANI / 64,\n daam: ROPANI / 256,\n bigha: BIGHA,\n kattha: BIGHA / 20,\n dhur: BIGHA / 400,\n sqm: 1,\n sqft: SQFT,\n};\n\n/** Total square metres from a mixed measure, e.g. `{ ropani: 2, aana: 3 }` or `{ bigha: 1, kattha: 5 }`. */\nexport function landToSqMeters(measure: Partial<Record<LandUnit, number>>): number {\n let m2 = 0;\n for (const [unit, value] of Object.entries(measure)) m2 += (value ?? 0) * LAND_UNIT_SQM[unit as LandUnit];\n return m2;\n}\n\n/** Convert an area between any two land units. */\nexport function convertLand(value: number, from: LandUnit, to: LandUnit): number {\n return (value * LAND_UNIT_SQM[from]) / LAND_UNIT_SQM[to];\n}\n\n/**\n * Break square metres into the hilly system: `{ ropani, aana, paisa, daam }`.\n * Rounds to the nearest whole daam first, then carries daam → paisa → aana →\n * ropani, so `4 daam` never appears (it is `1 paisa`) and float noise from\n * `landToSqMeters` cannot leave a unit one short.\n */\nexport interface LandBreakdownOptions {\n /** Decimal places kept on the smallest unit (daam / dhur). Default `0` (whole units). */\n decimals?: number;\n}\n\nconst roundTo = (n: number, d: number) => { const f = 10 ** d; return Math.round(n * f) / f; };\n\nexport function sqMetersToRopani(m2: number, options: LandBreakdownOptions = {}): { ropani: number; aana: number; paisa: number; daam: number } {\n const d = Math.max(0, Math.min(6, Math.floor(options.decimals ?? 0)));\n const neg = m2 < 0 ? -1 : 1;\n const total = roundTo(Math.abs(m2) / LAND_UNIT_SQM.daam, d); // daam, rounded once\n const ropani = Math.floor(total / 256 + 1e-9);\n const aana = Math.floor((total - ropani * 256) / 16 + 1e-9);\n const paisa = Math.floor((total - ropani * 256 - aana * 16) / 4 + 1e-9);\n const daam = roundTo(total - ropani * 256 - aana * 16 - paisa * 4, d);\n const z = (v: number) => (v === 0 ? 0 : neg * v);\n return { ropani: z(ropani), aana: z(aana), paisa: z(paisa), daam: z(daam) };\n}\n\n/**\n * Break square metres into the terai system: `{ bigha, kattha, dhur }`.\n * Rounds to the nearest whole dhur first, then carries dhur → kattha → bigha,\n * so `20 dhur` becomes `1 kattha` and `20 kattha` becomes `1 bigha`.\n */\nexport function sqMetersToBigha(m2: number, options: LandBreakdownOptions = {}): { bigha: number; kattha: number; dhur: number } {\n const d = Math.max(0, Math.min(6, Math.floor(options.decimals ?? 0)));\n const neg = m2 < 0 ? -1 : 1;\n const total = roundTo(Math.abs(m2) / LAND_UNIT_SQM.dhur, d); // dhur, rounded once\n const bigha = Math.floor(total / 400 + 1e-9);\n const kattha = Math.floor((total - bigha * 400) / 20 + 1e-9);\n const dhur = roundTo(total - bigha * 400 - kattha * 20, d);\n const z = (v: number) => (v === 0 ? 0 : neg * v);\n return { bigha: z(bigha), kattha: z(kattha), dhur: z(dhur) };\n}\n\n/** `\"2-3-1-0\"` ropani-aana-paisa-daam from square metres. */\nexport function formatRopani(m2: number): string {\n const r = sqMetersToRopani(m2);\n return `${r.ropani}-${r.aana}-${r.paisa}-${r.daam}`;\n}\n\n/** `\"1-5-0\"` bigha-kattha-dhur from square metres. */\nexport function formatBigha(m2: number): string {\n const b = sqMetersToBigha(m2);\n return `${b.bigha}-${b.kattha}-${b.dhur}`;\n}\n\n/* ------------------------------ phone & validators ------------------------------ */\n\n/** Normalize a Nepali mobile to canonical `+9779XXXXXXXXX`, or null if invalid. */\nexport function normalizeMobile(input: string): string | null {\n let s = fromDevanagari(input).replace(/[\\s-]/g, \"\");\n // Strip the country code only when a full 10-digit number follows it, so a\n // 10-digit mobile that itself starts with 977 is not mangled.\n const m = /^\\+?977(\\d{10})$/.exec(s);\n if (m) s = m[1]!;\n else if (s.startsWith(\"+\")) return null;\n return /^9[678]\\d{8}$/.test(s) ? `+977${s}` : null;\n}\n\nexport type Carrier = \"Ntc\" | \"Ncell\" | \"Smart Cell\" | \"UTL\" | \"unknown\";\n\n/**\n * Three-digit mobile prefixes (after +977) per carrier. Nepal Telecom and Ncell\n * are the only live mobile operators (2026); Smart Cell and UTL prefixes are\n * kept so old numbers still resolve, flagged by `CARRIER_STATUS`.\n */\nexport const CARRIER_PREFIXES: Record<Exclude<Carrier, \"unknown\">, readonly string[]> = {\n Ntc: [\"984\", \"985\", \"986\", \"974\", \"975\", \"976\"],\n Ncell: [\"980\", \"981\", \"982\", \"970\"],\n \"Smart Cell\": [\"961\", \"962\", \"988\"],\n UTL: [\"972\"],\n};\n\n/** Whether a carrier still operates a mobile network (NTA licence status, 2026). */\nexport const CARRIER_STATUS: Record<Exclude<Carrier, \"unknown\">, \"active\" | \"defunct\"> = {\n Ntc: \"active\",\n Ncell: \"active\",\n \"Smart Cell\": \"defunct\",\n UTL: \"defunct\",\n};\n\n/** Detect the carrier of a Nepali mobile number (see {@link CARRIER_PREFIXES}). */\nexport function getCarrier(input: string): Carrier {\n const n = normalizeMobile(input);\n if (!n) return \"unknown\";\n const p = n.slice(4, 7); // three digits after +977\n for (const [carrier, prefixes] of Object.entries(CARRIER_PREFIXES)) if (prefixes.includes(p)) return carrier as Carrier;\n return \"unknown\";\n}\n\n/** True for a valid Nepali mobile number (10 digits starting 96–98, optional +977). */\nexport function isValidNepaliMobile(input: string): boolean {\n return normalizeMobile(input) !== null; // one rule for both, so they always agree\n}\n\n/** True for a plausible Nepal landline (area code + number, e.g. 01-4XXXXXX). */\nexport function isValidLandline(input: string): boolean {\n const s = fromDevanagari(input).replace(/[\\s-]/g, \"\");\n return /^0\\d{7,9}$/.test(s);\n}\n\n/** True for a valid Nepal PAN / VAT number (9 digits). */\nexport function isValidPAN(input: string): boolean {\n return /^\\d{9}$/.test(fromDevanagari(input).trim());\n}\n\n/** True for a valid Nepal VAT number (9 digits — same format as PAN). */\nexport const isValidVAT = isValidPAN;\n\n/* ------------------------------ Nepali amount in words ------------------------------ */\n\nconst NEPALI_0_99 = [\n \"शून्य\", \"एक\", \"दुई\", \"तीन\", \"चार\", \"पाँच\", \"छ\", \"सात\", \"आठ\", \"नौ\",\n \"दस\", \"एघार\", \"बाह्र\", \"तेह्र\", \"चौध\", \"पन्ध्र\", \"सोह्र\", \"सत्र\", \"अठार\", \"उन्नाइस\",\n \"बीस\", \"एक्काइस\", \"बाइस\", \"तेइस\", \"चौबिस\", \"पच्चिस\", \"छब्बिस\", \"सत्ताइस\", \"अठ्ठाइस\", \"उनन्तिस\",\n \"तिस\", \"एकतिस\", \"बत्तिस\", \"तेत्तिस\", \"चौँतिस\", \"पैँतिस\", \"छत्तिस\", \"सैँतिस\", \"अठतिस\", \"उनन्चालिस\",\n \"चालिस\", \"एकचालिस\", \"बयालिस\", \"त्रिचालिस\", \"चवालिस\", \"पैँतालिस\", \"छयालिस\", \"सतचालिस\", \"अठचालिस\", \"उनन्चास\",\n \"पचास\", \"एकाउन्न\", \"बाउन्न\", \"त्रिपन्न\", \"चवन्न\", \"पचपन्न\", \"छपन्न\", \"सन्ताउन्न\", \"अन्ठाउन्न\", \"उनन्साठी\",\n \"साठी\", \"एकसट्ठी\", \"बयसट्ठी\", \"त्रिसट्ठी\", \"चौंसट्ठी\", \"पैंसट्ठी\", \"छयसट्ठी\", \"सतसट्ठी\", \"अठसट्ठी\", \"उनन्सत्तरी\",\n \"सत्तरी\", \"एकहत्तर\", \"बहत्तर\", \"त्रिहत्तर\", \"चौहत्तर\", \"पचहत्तर\", \"छयहत्तर\", \"सतहत्तर\", \"अठहत्तर\", \"उनासी\",\n \"असी\", \"एकासी\", \"बयासी\", \"त्रियासी\", \"चौरासी\", \"पचासी\", \"छयासी\", \"सतासी\", \"अठासी\", \"उनान्नब्बे\",\n \"नब्बे\", \"एकानब्बे\", \"बयानब्बे\", \"त्रियानब्बे\", \"चौरानब्बे\", \"पन्चानब्बे\", \"छयानब्बे\", \"सन्तानब्बे\", \"अन्ठानब्बे\", \"उनान्सय\",\n];\n\n/**\n * Amount in words in Nepali (Devanagari), South-Asian scale.\n * `numberToWordsNepali(1234567)` → `\"बाह्र लाख चौँतिस हजार पाँच सय सतसट्ठी\"`.\n */\nexport function numberToWordsNepali(value: number): string {\n let n = Math.floor(Math.abs(value));\n if (n === 0) return NEPALI_0_99[0]!;\n const parts: string[] = [];\n const units: [number, string][] = [\n [1e15, \"पद्म\"],\n [1e13, \"नील\"],\n [1e11, \"खरब\"],\n [1e9, \"अरब\"],\n [1e7, \"करोड\"],\n [1e5, \"लाख\"],\n [1e3, \"हजार\"],\n [1e2, \"सय\"],\n ];\n for (const [size, name] of units) {\n if (n >= size) {\n const count = Math.floor(n / size);\n // NEPALI_0_99 only covers 0–99; the top (पद्म) unit can exceed that, so\n // render its count recursively.\n const words = count >= 100 ? numberToWordsNepali(count) : NEPALI_0_99[count]!;\n parts.push(`${words} ${name}`);\n n %= size;\n }\n }\n if (n > 0) parts.push(NEPALI_0_99[n]!);\n return (value < 0 ? \"माइनस \" : \"\") + parts.join(\" \");\n}\n\n/** `numberToWordsNepali` plus a \"रुपैयाँ … मात्र\" wrapper. Accepts a number, decimal string or `{ rupees, paisa }`. */\nexport function amountInWordsNepali(amount: AmountInput): string {\n const { rupees, paisa, negative } = splitAmount(amount);\n let out = \"रुपैयाँ \" + numberToWordsNepali(rupees);\n if (paisa > 0) out += \" र \" + numberToWordsNepali(paisa) + \" पैसा\";\n return (negative ? \"माइनस \" : \"\") + out + \" मात्र\";\n}\n\nexport interface Province {\n number: number;\n name: string;\n nameNp: string;\n capital: string;\n /** Capital in Nepali (Devanagari). */\n capitalNp: string;\n}\n\n/** Nepal's seven federal provinces. */\nexport const PROVINCES: readonly Province[] = [\n { number: 1, name: \"Koshi\", nameNp: \"कोशी\", capital: \"Biratnagar\", capitalNp: \"विराटनगर\" },\n { number: 2, name: \"Madhesh\", nameNp: \"मधेश\", capital: \"Janakpur\", capitalNp: \"जनकपुर\" },\n { number: 3, name: \"Bagmati\", nameNp: \"बागमती\", capital: \"Hetauda\", capitalNp: \"हेटौँडा\" },\n { number: 4, name: \"Gandaki\", nameNp: \"गण्डकी\", capital: \"Pokhara\", capitalNp: \"पोखरा\" },\n { number: 5, name: \"Lumbini\", nameNp: \"लुम्बिनी\", capital: \"Deukhuri\", capitalNp: \"देउखुरी\" },\n { number: 6, name: \"Karnali\", nameNp: \"कर्णाली\", capital: \"Birendranagar\", capitalNp: \"वीरेन्द्रनगर\" },\n { number: 7, name: \"Sudurpashchim\", nameNp: \"सुदूरपश्चिम\", capital: \"Godawari\", capitalNp: \"गोदावरी\" },\n];\n\nexport interface District {\n name: string;\n nameNp: string;\n /** Province number 1–7. */\n province: number;\n /** Other accepted spellings/names (English or Nepali), matched by {@link findDistrict}. */\n aliases?: readonly string[];\n}\n\n/** Nepal's 77 districts, with their province number. */\nexport const DISTRICTS: readonly District[] = [\n // Koshi (1)\n { name: \"Bhojpur\", nameNp: \"भोजपुर\", province: 1 },\n { name: \"Dhankuta\", nameNp: \"धनकुटा\", province: 1 },\n { name: \"Ilam\", nameNp: \"इलाम\", province: 1 },\n { name: \"Jhapa\", nameNp: \"झापा\", province: 1 },\n { name: \"Khotang\", nameNp: \"खोटाङ\", province: 1 },\n { name: \"Morang\", nameNp: \"मोरङ\", province: 1 },\n { name: \"Okhaldhunga\", nameNp: \"ओखलढुंगा\", province: 1 },\n { name: \"Panchthar\", nameNp: \"पाँचथर\", province: 1 },\n { name: \"Sankhuwasabha\", nameNp: \"संखुवासभा\", province: 1 },\n { name: \"Solukhumbu\", nameNp: \"सोलुखुम्बु\", province: 1 },\n { name: \"Sunsari\", nameNp: \"सुनसरी\", province: 1 },\n { name: \"Taplejung\", nameNp: \"ताप्लेजुङ\", province: 1 },\n { name: \"Terhathum\", nameNp: \"तेह्रथुम\", province: 1 },\n { name: \"Udayapur\", nameNp: \"उदयपुर\", province: 1 },\n // Madhesh (2)\n { name: \"Bara\", nameNp: \"बारा\", province: 2 },\n { name: \"Dhanusha\", nameNp: \"धनुषा\", province: 2 },\n { name: \"Mahottari\", nameNp: \"महोत्तरी\", province: 2 },\n { name: \"Parsa\", nameNp: \"पर्सा\", province: 2 },\n { name: \"Rautahat\", nameNp: \"रौतहट\", province: 2 },\n { name: \"Saptari\", nameNp: \"सप्तरी\", province: 2 },\n { name: \"Sarlahi\", nameNp: \"सर्लाही\", province: 2 },\n { name: \"Siraha\", nameNp: \"सिरहा\", province: 2 },\n // Bagmati (3)\n { name: \"Bhaktapur\", nameNp: \"भक्तपुर\", province: 3 },\n { name: \"Chitwan\", nameNp: \"चितवन\", province: 3 },\n { name: \"Dhading\", nameNp: \"धादिङ\", province: 3 },\n { name: \"Dolakha\", nameNp: \"दोलखा\", province: 3 },\n { name: \"Kathmandu\", nameNp: \"काठमाडौं\", province: 3 },\n { name: \"Kavrepalanchok\", nameNp: \"काभ्रेपलाञ्चोक\", province: 3 },\n { name: \"Lalitpur\", nameNp: \"ललितपुर\", province: 3 },\n { name: \"Makwanpur\", nameNp: \"मकवानपुर\", province: 3 },\n { name: \"Nuwakot\", nameNp: \"नुवाकोट\", province: 3 },\n { name: \"Ramechhap\", nameNp: \"रामेछाप\", province: 3 },\n { name: \"Rasuwa\", nameNp: \"रसुवा\", province: 3 },\n { name: \"Sindhuli\", nameNp: \"सिन्धुली\", province: 3 },\n { name: \"Sindhupalchok\", nameNp: \"सिन्धुपाल्चोक\", province: 3 },\n // Gandaki (4)\n { name: \"Baglung\", nameNp: \"बागलुङ\", province: 4 },\n { name: \"Gorkha\", nameNp: \"गोरखा\", province: 4 },\n { name: \"Kaski\", nameNp: \"कास्की\", province: 4 },\n { name: \"Lamjung\", nameNp: \"लमजुङ\", province: 4 },\n { name: \"Manang\", nameNp: \"मनाङ\", province: 4 },\n { name: \"Mustang\", nameNp: \"मुस्ताङ\", province: 4 },\n { name: \"Myagdi\", nameNp: \"म्याग्दी\", province: 4 },\n { name: \"Nawalparasi East\", nameNp: \"नवलपरासी (बर्दघाट सुस्ता पूर्व)\", province: 4, aliases: [\"Nawalpur\", \"नवलपुर\", \"Nawalparasi (East of Bardaghat Susta)\", \"Nawalparasi (Bardaghat Susta East)\", \"Nawalparasi Purba\", \"नवलपरासी पूर्व\"] },\n { name: \"Parbat\", nameNp: \"पर्वत\", province: 4 },\n { name: \"Syangja\", nameNp: \"स्याङ्जा\", province: 4 },\n { name: \"Tanahun\", nameNp: \"तनहुँ\", province: 4 },\n // Lumbini (5)\n { name: \"Arghakhanchi\", nameNp: \"अर्घाखाँची\", province: 5 },\n { name: \"Banke\", nameNp: \"बाँके\", province: 5 },\n { name: \"Bardiya\", nameNp: \"बर्दिया\", province: 5 },\n { name: \"Dang\", nameNp: \"दाङ\", province: 5 },\n { name: \"Eastern Rukum\", nameNp: \"पूर्वी रुकुम\", province: 5, aliases: [\"Rukum East\", \"Rukum Purba\", \"रुकुम पूर्व\"] },\n { name: \"Gulmi\", nameNp: \"गुल्मी\", province: 5 },\n { name: \"Kapilvastu\", nameNp: \"कपिलवस्तु\", province: 5 },\n { name: \"Nawalparasi West\", nameNp: \"नवलपरासी (बर्दघाट सुस्ता पश्चिम)\", province: 5, aliases: [\"Parasi\", \"परासी\", \"Nawalparasi (West of Bardaghat Susta)\", \"Nawalparasi (Bardaghat Susta West)\", \"Nawalparasi Paschim\", \"नवलपरासी पश्चिम\"] },\n { name: \"Palpa\", nameNp: \"पाल्पा\", province: 5 },\n { name: \"Pyuthan\", nameNp: \"प्युठान\", province: 5 },\n { name: \"Rolpa\", nameNp: \"रोल्पा\", province: 5 },\n { name: \"Rupandehi\", nameNp: \"रूपन्देही\", province: 5 },\n // Karnali (6)\n { name: \"Dailekh\", nameNp: \"दैलेख\", province: 6 },\n { name: \"Dolpa\", nameNp: \"डोल्पा\", province: 6 },\n { name: \"Humla\", nameNp: \"हुम्ला\", province: 6 },\n { name: \"Jajarkot\", nameNp: \"जाजरकोट\", province: 6 },\n { name: \"Jumla\", nameNp: \"जुम्ला\", province: 6 },\n { name: \"Kalikot\", nameNp: \"कालिकोट\", province: 6 },\n { name: \"Mugu\", nameNp: \"मुगु\", province: 6 },\n { name: \"Salyan\", nameNp: \"सल्यान\", province: 6 },\n { name: \"Surkhet\", nameNp: \"सुर्खेत\", province: 6 },\n { name: \"Western Rukum\", nameNp: \"पश्चिमी रुकुम\", province: 6, aliases: [\"Rukum West\", \"Rukum Paschim\", \"रुकुम पश्चिम\"] },\n // Sudurpashchim (7)\n { name: \"Achham\", nameNp: \"अछाम\", province: 7 },\n { name: \"Baitadi\", nameNp: \"बैतडी\", province: 7 },\n { name: \"Bajhang\", nameNp: \"बझाङ\", province: 7 },\n { name: \"Bajura\", nameNp: \"बाजुरा\", province: 7 },\n { name: \"Dadeldhura\", nameNp: \"डडेल्धुरा\", province: 7 },\n { name: \"Darchula\", nameNp: \"दार्चुला\", province: 7 },\n { name: \"Doti\", nameNp: \"डोटी\", province: 7 },\n { name: \"Kailali\", nameNp: \"कैलाली\", province: 7 },\n { name: \"Kanchanpur\", nameNp: \"कञ्चनपुर\", province: 7 },\n];\n\n/** Districts in a given province (1–7). */\nexport function districtsByProvince(province: number): District[] {\n return DISTRICTS.filter((d) => d.province === province);\n}\n\n/** Find a district by English or Nepali name or any alias (case-insensitive). */\nexport function findDistrict(name: string): District | undefined {\n const raw = name.trim();\n const q = raw.toLowerCase();\n return DISTRICTS.find(\n (d) => d.name.toLowerCase() === q || d.nameNp === raw || (d.aliases ?? []).some((a) => a.toLowerCase() === q),\n );\n}\n\n/* ------------------------------------------------------------------------- *\n * New in 1.4.0 — Neel/Padma scale, `{ decimals }` on land breakdowns, stricter plate\n * grammar (province word + Pradesh/Province, province number 1–7 only), and\n * isValidNepaliMobile ≡ normalizeMobile.\n * New in 1.3.0 — Kharab scale, \"र\"/\"ra\" rupee–paisa separator, exact land-unit\n * ratios with carry-correct ropani/bigha breakdowns, Ncell 970 + carrier status,\n * Devanagari digits in landline/PAN validators, Nepali province capitals,\n * district aliases (official Nawalparasi East/West), embossed plate shapes.\n * New in 1.2.0 — Roman (transliterated) amount-in-words, integer-paisa NPR\n * format/parse, digit-conversion aliases, extra shape validators, and\n * province lookup helpers. See `./extras`.\n * ------------------------------------------------------------------------- */\nexport * from \"./extras\";\n\n/* 1.6.0: Nepal Standard Time (UTC+05:45) helpers. */\nexport * from \"./time\";\n","/**\n * Ready-made terms for Nepal's 77 districts (names from @lacspace/nepali-utils), with the\n * spellings that actually turn up in copy: काठमाण्डौ, Kavre/काभ्रे, Rukum East/रुकुम पूर्व …\n */\nimport { DISTRICTS } from \"@lacspace/nepali-utils\";\nimport type { TermInput } from \"./index.js\";\n\nexport interface DistrictTerm {\n id: string;\n en: string[];\n ne: string[];\n province: number;\n /** The Nepali name is also an everyday word (पर्वत = mountain): confirm with context before tagging. */\n ambiguous?: boolean;\n caseSensitive: true;\n}\n\nconst ALIASES: Record<string, { en?: string[]; ne?: string[] }> = {\n Kathmandu: { en: [\"Katmandu\"], ne: [\"काठमाण्डौ\", \"काठमान्डु\"] },\n Kavrepalanchok: { en: [\"Kavre\", \"Kabhre\", \"Kabhrepalanchok\", \"Kavrepalanchowk\"], ne: [\"काभ्रे\", \"काभ्रेपलान्चोक\"] },\n Sindhupalchok: { en: [\"Sindhupalchowk\"] },\n \"Nawalparasi East\": { en: [\"Nawalparasi (East)\", \"East Nawalparasi\", \"Nawalpur\"], ne: [\"नवलपरासी पूर्व\", \"पूर्वी नवलपरासी\", \"नवलपुर\"] },\n \"Nawalparasi West\": { en: [\"Nawalparasi (West)\", \"West Nawalparasi\"], ne: [\"नवलपरासी पश्चिम\", \"पश्चिमी नवलपरासी\"] },\n \"Eastern Rukum\": { en: [\"Rukum East\", \"East Rukum\", \"Rukum (East)\"], ne: [\"रुकुम पूर्व\"] },\n \"Western Rukum\": { en: [\"Rukum West\", \"West Rukum\", \"Rukum (West)\"], ne: [\"रुकुम पश्चिम\"] },\n Tanahun: { en: [\"Tanahu\"], ne: [\"तनहु\"] },\n Dhanusha: { en: [\"Dhanusa\"] },\n Kapilvastu: { en: [\"Kapilbastu\"], ne: [\"कपिलबस्तु\"] },\n Makwanpur: { en: [\"Makawanpur\"] },\n Sankhuwasabha: { en: [\"Sankhuwasava\"] },\n Terhathum: { en: [\"Tehrathum\"], ne: [\"तेहथुम\"] },\n Okhaldhunga: { ne: [\"ओखलढुङ्गा\"] },\n Syangja: { en: [\"Syanja\"], ne: [\"स्याङजा\"] },\n Achham: { en: [\"Accham\"] },\n Udayapur: { en: [\"Udaypur\"] },\n Bardiya: { en: [\"Bardia\"] },\n Ilam: { en: [\"Illam\"] },\n Baglung: { ne: [\"बाग्लुङ\"] },\n Rupandehi: { ne: [\"रुपन्देही\"] },\n Dadeldhura: { ne: [\"डँडेलधुरा\"] },\n Mahottari: { ne: [\"महोतरी\"] },\n};\n\n/** District whose Nepali name is a common word. */\nconst AMBIGUOUS = new Set([\"Parbat\"]);\n\nconst slug = (s: string): string => s.toLowerCase().replace(/[^a-z0-9]+/g, \"-\").replace(/^-|-$/g, \"\");\n\n/** Every district as a {@link TermInput}: ids are slugs (\"nawalparasi-east\"), English is exact-case. */\nexport function districtTerms(): DistrictTerm[] {\n return DISTRICTS.map((d) => {\n const extra = ALIASES[d.name] ?? {};\n const ne = new Set<string>([d.nameNp, ...(extra.ne ?? [])]);\n for (const n of [...ne]) if (n.endsWith(\"ङ\")) ne.add(n.slice(0, -1) + \"ंग\"); // मोरङ / मोरंग\n return {\n id: slug(d.name),\n en: [d.name, ...(extra.en ?? [])],\n ne: [...ne],\n province: d.province,\n ...(AMBIGUOUS.has(d.name) ? { ambiguous: true } : {}),\n caseSensitive: true,\n } satisfies DistrictTerm & TermInput;\n });\n}\n","/**\n * Nepali / English matching for names and keywords in running text.\n *\n * normaliseNe fold spelling variants (chandrabindu, half nasals, long/short vowels, nukta, ZWJ)\n * createMatcher whole-word matching that lets Nepali postpositions follow a word\n * (झापाको → Jhapa) but nothing else (पर्वतारोही ≠ Parbat), and keeps short\n * English acronyms case-sensitive (SEE ≠ \"see\")\n * near two terms within N characters, optionally in the same sentence\n *\n * Every match carries offsets into the ORIGINAL text, so callers can highlight or cut.\n */\n\nexport const VERSION = \"1.0.0\";\n\nexport interface NormaliseOptions {\n /** Also fold श/ष → स, व → ब and ण → न. Default false. */\n loose?: boolean;\n /** Devanagari digits ०-९ → 0-9. Default true. */\n digits?: boolean;\n}\n\n/** Normalised text plus, for each output char, the index of the input char it came from. */\ninterface Mapped {\n text: string;\n map: number[];\n}\n\nconst NUKTA = 0x093c;\nconst HALANT = 0x094d;\nconst NASALS = new Set([0x0919, 0x091e, 0x0923, 0x0928, 0x092e]); // ङ ञ ण न म\nconst DROP = new Set([NUKTA, 0x200c, 0x200d, 0x00ad, 0xfeff]);\nconst FOLD: Record<number, number> = {\n 0x0901: 0x0902, // ँ → ं\n 0x0908: 0x0907, // ई → इ\n 0x090a: 0x0909, // ऊ → उ\n 0x0940: 0x093f, // ी → ि\n 0x0942: 0x0941, // ू → ु\n};\nconst LOOSE: Record<number, number> = {\n 0x0936: 0x0938, // श → स\n 0x0937: 0x0938, // ष → स\n 0x0935: 0x092c, // व → ब\n 0x0923: 0x0928, // ण → न\n};\n\nconst isConsonant = (c: number): boolean => (c >= 0x0915 && c <= 0x0939) || (c >= 0x0958 && c <= 0x095f);\n\nfunction mapNormalise(input: string, opts: NormaliseOptions = {}): Mapped {\n const digits = opts.digits !== false;\n // NFC per char would lose the map; NFC first, then map onto the NFC string.\n const s = input.normalize(\"NFC\");\n const out: string[] = [];\n const map: number[] = [];\n const push = (ch: string, i: number): void => {\n out.push(ch);\n map.push(i);\n };\n // Next code point index after i, skipping chars we drop anyway.\n const next = (i: number): number => {\n let j = i + 1;\n while (j < s.length && DROP.has(s.charCodeAt(j))) j++;\n return j;\n };\n let lastSpace = false;\n for (let i = 0; i < s.length; i++) {\n let c = s.charCodeAt(i);\n if (DROP.has(c)) continue;\n if (/\\s/.test(s[i]!)) {\n if (!lastSpace) push(\" \", i);\n lastSpace = true;\n continue;\n }\n lastSpace = false;\n // Half nasal + consonant → anusvara: चन्द्र → चंद्र, सम्म → संम.\n if (NASALS.has(c)) {\n const h = next(i);\n if (s.charCodeAt(h) === HALANT) {\n const k = next(h);\n if (k < s.length && isConsonant(s.charCodeAt(k))) {\n push(\"ं\", i);\n i = h;\n continue;\n }\n }\n }\n if (FOLD[c] !== undefined) c = FOLD[c]!;\n if (opts.loose && LOOSE[c] !== undefined) c = LOOSE[c]!;\n if (digits && c >= 0x0966 && c <= 0x096f) c = 0x30 + (c - 0x0966);\n if (c >= 0xd800 && c <= 0xdbff && i + 1 < s.length) {\n push(s[i]!, i);\n push(s[i + 1]!, i + 1);\n i++;\n continue;\n }\n push(String.fromCharCode(c), i);\n }\n return { text: out.join(\"\"), map };\n}\n\n/**\n * Fold Nepali spelling variants so equivalent spellings compare equal:\n * NFC; chandrabindu → anusvara; half nasal + consonant → anusvara; ई/ी → इ/ि and ऊ/ू → उ/ु;\n * nukta, ZWJ, ZWNJ and soft hyphens removed; Devanagari digits → ASCII; whitespace collapsed.\n * Latin text passes through unchanged (case included).\n */\nexport function normaliseNe(text: string, opts: NormaliseOptions = {}): string {\n return mapNormalise(text, opts).text;\n}\n/** US spelling alias. */\nexport const normalizeNe = normaliseNe;\n\n/**\n * Words that may be glued to the end of a Nepali name without making it a different word.\n * Matching allows a chain of up to three (जिल्लाहरूमा, काठमाडौंबाटै).\n */\nexport const POSTPOSITIONS: readonly string[] = [\n \"मा\", \"मै\", \"को\", \"का\", \"की\", \"के\", \"कै\", \"ले\", \"लाई\", \"बाट\", \"बाटै\", \"सँग\", \"संग\", \"सित\",\n \"देखि\", \"देखिनै\", \"सम्म\", \"सम्मै\", \"तिर\", \"तर्फ\", \"भित्र\", \"बाहिर\", \"माथि\", \"मुनि\", \"नजिक\", \"पछि\",\n \"अघि\", \"अगाडि\", \"पारि\", \"वारि\", \"द्वारा\", \"हरू\", \"हरु\", \"नै\", \"भर\", \"भरि\", \"वासी\", \"बासी\",\n \"स्थित\", \"निवासी\",\n];\n\nconst isNeLetter = (c: number): boolean => (c >= 0x0900 && c <= 0x0963) || (c >= 0x0971 && c <= 0x097f);\nconst isWordChar = (ch: string | undefined): boolean => !!ch && /[\\p{L}\\p{M}\\p{N}]/u.test(ch);\nconst hasDevanagari = (s: string): boolean => /[ऀ-ॿ]/.test(s);\n\n/** A term to look for: a plain string, or an id with English and/or Nepali spellings. */\nexport type TermInput =\n | string\n | {\n id?: string;\n en?: string | readonly string[];\n ne?: string | readonly string[];\n /** Extra spellings in either script. */\n aliases?: readonly string[];\n /** English case rule for this term. Default \"auto\": all-caps terms up to 5 letters are exact-case. */\n caseSensitive?: boolean | \"auto\";\n };\n\nexport interface MatchOptions extends NormaliseOptions {\n /** Default English case rule. Default \"auto\". */\n caseSensitive?: boolean | \"auto\";\n /** Allow Nepali postpositions after a word. Default true. */\n postpositions?: boolean;\n /** Extra suffixes to allow on top of {@link POSTPOSITIONS}. */\n extraSuffixes?: readonly string[];\n /** Keep overlapping matches; by default the longest wins. */\n overlaps?: boolean;\n}\n\nexport interface Match {\n /** The term's id (or the spelling itself for plain-string terms). */\n id: string;\n /** The spelling that matched, as given. */\n term: string;\n lang: \"ne\" | \"en\";\n /** Offsets into the original text; `text` is `original.slice(index, end)`, suffix included. */\n index: number;\n end: number;\n text: string;\n /** The postposition chain after the word, normalised (e.g. \"मा\", \"हरुमा\"). */\n suffix?: string;\n}\n\ninterface Variant {\n id: string;\n term: string;\n lang: \"ne\" | \"en\";\n key: string; // normalised (ne) or raw (en)\n re?: RegExp; // en\n}\n\nconst escapeRe = (s: string): string => s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\nfunction autoCase(term: string): boolean {\n const letters = term.replace(/[^\\p{L}]/gu, \"\");\n return letters.length >= 2 && letters.length <= 5 && letters === letters.toUpperCase() && letters !== letters.toLowerCase();\n}\n\nexport interface Matcher {\n /** Every match in `text`, in order of position. */\n find(text: string): Match[];\n /** True when any term (or the term with this id) occurs. */\n test(text: string, id?: string): boolean;\n /** Distinct ids found, in order of first appearance. */\n ids(text: string): string[];\n readonly size: number;\n}\n\n/** Compile a set of terms once; reuse the matcher across many texts. */\nexport function createMatcher(terms: readonly TermInput[], opts: MatchOptions = {}): Matcher {\n const nopts: NormaliseOptions = { loose: opts.loose, digits: opts.digits };\n const suffixes = opts.postpositions === false\n ? []\n : [...new Set([...POSTPOSITIONS, ...(opts.extraSuffixes ?? [])].map((s) => normaliseNe(s, nopts)))].sort((a, b) => b.length - a.length);\n const variants: Variant[] = [];\n for (const t of terms) {\n const obj = typeof t === \"string\" ? { id: t, aliases: [t] } : t;\n const list = (v: string | readonly string[] | undefined): string[] => (v === undefined ? [] : typeof v === \"string\" ? [v] : [...v]);\n const spellings = [...list(obj.en), ...list(obj.ne), ...(obj.aliases ?? [])].map((s) => s.trim()).filter(Boolean);\n const id = obj.id ?? spellings[0] ?? \"\";\n for (const sp of new Set(spellings)) {\n if (hasDevanagari(sp)) {\n variants.push({ id, term: sp, lang: \"ne\", key: normaliseNe(sp, nopts) });\n } else {\n const rule = obj.caseSensitive ?? opts.caseSensitive ?? \"auto\";\n const exact = rule === \"auto\" ? autoCase(sp) : rule;\n const body = sp.split(/\\s+/).map(escapeRe).join(\"\\\\s+\");\n variants.push({ id, term: sp, lang: \"en\", key: sp, re: new RegExp(body, exact ? \"gu\" : \"giu\") });\n }\n }\n }\n\n // Length of the postposition chain at `pos`, or -1 when the word continues with other letters.\n const suffixEnd = (s: string, pos: number, depth: number): number => {\n if (pos >= s.length || !isNeLetter(s.charCodeAt(pos))) return pos;\n if (depth >= 3) return -1;\n for (const suf of suffixes) {\n if (s.startsWith(suf, pos)) {\n const e = suffixEnd(s, pos + suf.length, depth + 1);\n if (e >= 0) return e;\n }\n }\n return -1;\n };\n\n const find = (text: string): Match[] => {\n const { text: n, map } = mapNormalise(text, nopts);\n const toOrig = (i: number): number => (i >= map.length ? text.normalize(\"NFC\").length : map[i]!);\n const nfc = text.normalize(\"NFC\");\n const found: Match[] = [];\n for (const v of variants) {\n if (v.lang === \"ne\") {\n if (!v.key) continue;\n let at = n.indexOf(v.key);\n while (at >= 0) {\n const before = n[at - 1];\n if (!isWordChar(before)) {\n const wordEnd = at + v.key.length;\n const end = suffixEnd(n, wordEnd, 0);\n const after = n[end];\n if (end >= 0 && !isWordChar(after)) {\n const s = toOrig(at);\n const e = end >= n.length ? nfc.length : toOrig(end);\n found.push({ id: v.id, term: v.term, lang: \"ne\", index: s, end: e, text: nfc.slice(s, e), ...(end > wordEnd ? { suffix: n.slice(wordEnd, end) } : {}) });\n }\n }\n at = n.indexOf(v.key, at + 1);\n }\n } else {\n v.re!.lastIndex = 0;\n for (const m of n.matchAll(v.re!)) {\n const at = m.index!;\n const end = at + m[0].length;\n if (isWordChar(n[at - 1]) || isWordChar(n[end])) continue;\n const s = toOrig(at);\n const e = end >= n.length ? nfc.length : toOrig(end);\n found.push({ id: v.id, term: v.term, lang: \"en\", index: s, end: e, text: nfc.slice(s, e) });\n }\n }\n }\n found.sort((a, b) => a.index - b.index || b.end - a.end);\n if (opts.overlaps) return found;\n const kept: Match[] = [];\n let reach = -1;\n for (const m of found) {\n if (m.index >= reach) {\n kept.push(m);\n reach = m.end;\n }\n }\n return kept;\n };\n\n return {\n find,\n test: (text, id) => find(text).some((m) => id === undefined || m.id === id),\n ids: (text) => [...new Set(find(text).map((m) => m.id))],\n get size() { return variants.length; },\n };\n}\n\n/** One-off: every match of `terms` in `text`. Compile with {@link createMatcher} for repeated use. */\nexport function findTerms(text: string, terms: TermInput | readonly TermInput[], opts?: MatchOptions): Match[] {\n return createMatcher(Array.isArray(terms) ? (terms as TermInput[]) : [terms as TermInput], opts).find(text);\n}\n\n/** One-off: does `term` occur in `text` as a whole word (Nepali postpositions allowed)? */\nexport function contains(text: string, term: TermInput | readonly TermInput[], opts?: MatchOptions): boolean {\n return findTerms(text, term, opts).length > 0;\n}\n\n/**\n * Split a Nepali word into its stem and trailing postpositions:\n * `splitSuffix(\"जिल्लाहरूमा\")` → `{ stem: \"जिल्ला\", suffixes: [\"हरु\", \"मा\"] }` (suffixes normalised).\n * Only splits when the whole tail is postpositions; the stem keeps the original spelling.\n */\nexport function splitSuffix(word: string): { stem: string; suffixes: string[] } {\n const { text: n, map } = mapNormalise(word.trim());\n const sufs = [...new Set(POSTPOSITIONS.map((s) => normaliseNe(s)))].sort((a, b) => b.length - a.length);\n const nfc = word.trim().normalize(\"NFC\");\n let end = n.length;\n const out: string[] = [];\n while (out.length < 3) {\n const hit = sufs.find((s) => end - s.length >= 2 && n.slice(end - s.length, end) === s);\n if (!hit) break;\n out.unshift(hit);\n end -= hit.length;\n }\n return { stem: nfc.slice(0, end >= n.length ? nfc.length : map[end]), suffixes: out };\n}\n\nexport interface NearOptions extends MatchOptions {\n /** Max characters between the end of one match and the start of the other. Default 60. */\n maxChars?: number;\n /** Require both in the same sentence (। ॥ . ? ! or a newline ends one). Default true. */\n sameSentence?: boolean;\n}\n\nexport interface NearResult {\n a: Match;\n b: Match;\n /** Characters between the two matches (0 when adjacent). */\n gap: number;\n}\n\n// \"ने.क.पा.\" / \"U.S.\": a token with an inner dot is an abbreviation, not a sentence end.\nconst dottedAbbr = (s: string, i: number): boolean => {\n let j = i - 1;\n while (j >= 0 && !/\\s/.test(s[j]!)) j--;\n return s.slice(j + 1, i).includes(\".\");\n};\n\n/** Sentence boundaries as [start, end) offsets into the NFC text. \".\" ends a sentence only before a space or the end. */\nexport function sentenceSpans(text: string): [number, number][] {\n const s = text.normalize(\"NFC\");\n const spans: [number, number][] = [];\n let start = 0;\n for (let i = 0; i < s.length; i++) {\n const ch = s[i]!;\n const end = ch === \"।\" || ch === \"॥\" || ch === \"?\" || ch === \"!\" || ch === \"\\n\" || (ch === \".\" && (i + 1 >= s.length || /\\s/.test(s[i + 1]!)) && !dottedAbbr(s, i));\n if (end) {\n spans.push([start, i + 1]);\n start = i + 1;\n }\n }\n if (start < s.length) spans.push([start, s.length]);\n return spans;\n}\n\n/**\n * The closest pair of an `a` match and a `b` match within `maxChars` of each other\n * (in either order), or null. With `sameSentence` (default) the pair must share a sentence.\n */\nexport function near(\n text: string,\n a: TermInput | readonly TermInput[],\n b: TermInput | readonly TermInput[],\n maxCharsOrOpts: number | NearOptions = {},\n sameSentence?: boolean,\n): NearResult | null {\n const opts: NearOptions = typeof maxCharsOrOpts === \"number\" ? { maxChars: maxCharsOrOpts } : { ...maxCharsOrOpts };\n if (sameSentence !== undefined) opts.sameSentence = sameSentence;\n const max = opts.maxChars ?? 60;\n const same = opts.sameSentence !== false;\n const am = findTerms(text, a, opts);\n const bm = findTerms(text, b, opts);\n if (!am.length || !bm.length) return null;\n const spans = same ? sentenceSpans(text) : [];\n const sentenceOf = (i: number): number => spans.findIndex(([s, e]) => i >= s && i < e);\n let best: NearResult | null = null;\n for (const x of am) {\n for (const y of bm) {\n if (x.index < y.end && y.index < x.end) continue; // same span of text\n const gap = x.end <= y.index ? y.index - x.end : x.index - y.end;\n if (gap > max) continue;\n if (same && sentenceOf(x.index) !== sentenceOf(y.index)) continue;\n if (!best || gap < best.gap) best = { a: x, b: y, gap };\n }\n }\n return best;\n}\n\nexport { districtTerms, type DistrictTerm } from \"./districts.js\";\n"]}
1
+ {"version":3,"sources":["../../nepali-utils/src/index.ts","../src/districts.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @lacspace/nepali-utils — everyday Nepal helpers: NPR currency formatting,\n * Nepali (South-Asian) digit grouping, Devanagari numerals, amount-in-words,\n * common validators, and province data. Zero-dependency, isomorphic.\n */\n\nconst DEVANAGARI = [\"०\", \"१\", \"२\", \"३\", \"४\", \"५\", \"६\", \"७\", \"८\", \"९\"] as const;\n\n/** Convert Arabic numerals to Devanagari: `\"2081\"` → `\"२०८१\"`. */\nexport function toDevanagari(input: string | number): string {\n return String(input).replace(/[0-9]/g, (d) => DEVANAGARI[Number(d)]!);\n}\n\n/** Convert Devanagari numerals to Arabic: `\"२०८१\"` → `\"2081\"`. */\nexport function fromDevanagari(input: string): string {\n return input.replace(/[०-९]/g, (d) => String(DEVANAGARI.indexOf(d as (typeof DEVANAGARI)[number])));\n}\n\n/**\n * Group a number with the South-Asian (Nepali) system: the last three digits,\n * then groups of two. `1234567` → `\"12,34,567\"`; decimals are kept: `2587.25` → `\"2,587.25\"`.\n */\nexport function groupNepali(value: number | string): string {\n const str = String(value).trim();\n const negative = str.startsWith(\"-\");\n const sign = negative ? \"-\" : \"\";\n const [intRaw = \"\", ...decs] = str.split(\".\");\n const s = intRaw.replace(/[^\\d]/g, \"\");\n const dec = decs.length ? \".\" + decs.join(\"\").replace(/[^\\d]/g, \"\") : \"\";\n if (s.length <= 3) return sign + s + dec;\n const last3 = s.slice(-3);\n const rest = s.slice(0, -3);\n return sign + rest.replace(/\\B(?=(\\d{2})+(?!\\d))/g, \",\") + \",\" + last3 + dec;\n}\n\nexport interface FormatNprOptions {\n /** Show a symbol/prefix. Default `\"Rs. \"`. Use `\"\"` for none. */\n symbol?: string;\n /** Number of decimal places. Default `2`. */\n decimals?: number;\n /** Render digits in Devanagari. Default `false`. */\n devanagari?: boolean;\n}\n\n/**\n * Format an amount as Nepalese Rupees with South-Asian grouping.\n * `formatNPR(1234567.5)` → `\"Rs. 12,34,567.50\"`.\n */\nexport function formatNPR(amount: number, options: FormatNprOptions = {}): string {\n const { symbol = \"Rs. \", decimals = 2, devanagari = false } = options;\n const negative = amount < 0;\n const fixed = Math.abs(amount).toFixed(decimals);\n const [intPart, decPart] = fixed.split(\".\");\n let out = groupNepali(intPart!);\n if (decPart) out += \".\" + decPart;\n if (devanagari) out = toDevanagari(out);\n return (negative ? \"-\" : \"\") + symbol + out;\n}\n\nconst ONES = [\n \"\", \"One\", \"Two\", \"Three\", \"Four\", \"Five\", \"Six\", \"Seven\", \"Eight\", \"Nine\",\n \"Ten\", \"Eleven\", \"Twelve\", \"Thirteen\", \"Fourteen\", \"Fifteen\", \"Sixteen\",\n \"Seventeen\", \"Eighteen\", \"Nineteen\",\n];\nconst TENS = [\"\", \"\", \"Twenty\", \"Thirty\", \"Forty\", \"Fifty\", \"Sixty\", \"Seventy\", \"Eighty\", \"Ninety\"];\n\nfunction twoDigitWords(n: number): string {\n if (n < 20) return ONES[n]!;\n const t = Math.floor(n / 10);\n const o = n % 10;\n return TENS[t]! + (o ? \" \" + ONES[o] : \"\");\n}\n\n/**\n * Amount in words using the South-Asian system (Thousand, Lakh, Crore, Arab, Kharab, Neel, Padma) —\n * perfect for invoices. `numberToWords(1234567)` →\n * `\"Twelve Lakh Thirty Four Thousand Five Hundred Sixty Seven\"`.\n */\nexport function numberToWords(value: number): string {\n let n = Math.floor(Math.abs(value));\n if (n === 0) return \"Zero\";\n const parts: string[] = [];\n const units: [number, string][] = [\n [1e15, \"Padma\"],\n [1e13, \"Neel\"],\n [1e11, \"Kharab\"],\n [1e9, \"Arab\"],\n [1e7, \"Crore\"],\n [1e5, \"Lakh\"],\n [1e3, \"Thousand\"],\n [1e2, \"Hundred\"],\n ];\n for (const [size, name] of units) {\n if (n >= size) {\n const count = Math.floor(n / size);\n // Hundreds are 1–9; middle units take two digits; the top (Padma) unit can\n // itself run into the hundreds/thousands, so render its count recursively.\n const words = size === 100 ? ONES[count]! : count >= 100 ? numberToWords(count) : twoDigitWords(count);\n parts.push(words + \" \" + name);\n n %= size;\n }\n }\n if (n > 0) parts.push(twoDigitWords(n));\n return (value < 0 ? \"Minus \" : \"\") + parts.join(\" \");\n}\n\n/**\n * An amount for the amount-in-words helpers: a number (fine up to ~13 rupee\n * digits), a decimal string (`\"99999999999999.01\"`, grouping commas allowed),\n * or exact integer parts `{ rupees, paisa }`. Strings and parts avoid float\n * drift, so the paisa is always exact.\n */\nexport type AmountInput = number | string | { rupees: number; paisa?: number; negative?: boolean };\n\n/** Split an {@link AmountInput} into exact integer rupees + paisa (0–99) and a sign. */\nexport function splitAmount(input: AmountInput): { rupees: number; paisa: number; negative: boolean } {\n let negative = false, rupees: number, paisa: number;\n if (typeof input === \"number\") {\n if (!Number.isFinite(input)) throw new RangeError(\"amount must be finite\");\n negative = input < 0;\n const a = Math.abs(input);\n rupees = Math.floor(a);\n paisa = Math.round((a - rupees) * 100);\n } else if (typeof input === \"string\") {\n const m = /^\\s*(-)?\\s*(?:rs\\.?|npr|रु\\.?|रू\\.?)?\\s*([\\d,]*)(?:\\.(\\d*))?\\s*$/i.exec(fromDevanagari(input));\n if (!m || (!m[2] && !m[3])) throw new RangeError(`not a decimal amount: ${input}`);\n negative = !!m[1];\n const intDigits = (m[2] || \"0\").replace(/,/g, \"\");\n rupees = Number(intDigits);\n const frac = (m[3] || \"\").padEnd(3, \"0\");\n paisa = Number(frac.slice(0, 2)) + (Number(frac[2]) >= 5 ? 1 : 0); // round half up on the 3rd decimal\n } else {\n rupees = Math.trunc(Math.abs(input.rupees));\n paisa = Math.round(Math.abs(input.paisa ?? 0));\n negative = !!input.negative || input.rupees < 0;\n if (paisa > 99) { rupees += Math.floor(paisa / 100); paisa %= 100; }\n }\n if (paisa === 100) { rupees += 1; paisa = 0; }\n if (!Number.isSafeInteger(rupees)) throw new RangeError(\"rupees exceed Number.MAX_SAFE_INTEGER\");\n return { rupees, paisa, negative: negative && (rupees > 0 || paisa > 0) };\n}\n\n/** `numberToWords` plus a \"Rupees … only\" wrapper for invoices. Accepts a number, decimal string or `{ rupees, paisa }`. */\nexport function amountInWords(amount: AmountInput): string {\n const { rupees, paisa, negative } = splitAmount(amount);\n let out = \"Rupees \" + numberToWords(rupees);\n if (paisa > 0) out += \" and \" + numberToWords(paisa) + \" Paisa\";\n return (negative ? \"Minus \" : \"\") + out + \" Only\";\n}\n\n/** Parse a formatted NPR/Nepali-grouped string back to a number (inverse of {@link formatNPR}). */\nexport function ungroupNepali(input: string | number): number {\n let s = fromDevanagari(String(input)).trim();\n const negative = s.startsWith(\"-\");\n s = s.replace(/₹|rs\\.?|npr|रू\\.?|रु\\.?/gi, \"\"); // strip currency symbols (incl. the dot in \"Rs.\")\n s = s.replace(/[^\\d.]/g, \"\"); // drop grouping commas, spaces, everything else\n if (s === \"\") return NaN;\n const num = Number(s);\n return negative && !Number.isNaN(num) ? -num : num;\n}\n\n/** Alias of {@link ungroupNepali} — parse an NPR string to a number. */\nexport const parseNPR = ungroupNepali;\n\nfunction trimNum(n: number): string {\n return Number(n.toFixed(2)).toString();\n}\n\nexport interface CompactNprOptions {\n symbol?: string;\n /** Use Nepali scale words (हजार/लाख/करोड/अरब/खरब). Default false (K/Lakh/Cr/Arab/Kharab). */\n nepali?: boolean;\n /** Render digits in Devanagari. Default false. */\n devanagari?: boolean;\n /**\n * Word style. \"short\" (default): K/Lakh/Cr/Arab/Kharab, हजार/लाख/करोड/अरब/खरब.\n * \"long\": Thousand/Lakh/Crore/Arba/Kharba, हजार/लाख/करोड/अर्ब/खर्ब (newsroom spelling).\n */\n style?: \"short\" | \"long\";\n /** Fixed decimal places (e.g. 2 → \"4.29\"). Default: up to 2, trailing zeros trimmed. */\n decimals?: number;\n}\n\n/** Compact NPR: `formatCompactNPR(1234567)` → `\"Rs. 12.35 Lakh\"`. */\nexport function formatCompactNPR(amount: number, options: CompactNprOptions = {}): string {\n const { symbol = \"Rs. \", nepali = false, devanagari = false, style = \"short\", decimals } = options;\n const negative = amount < 0;\n const a = Math.abs(amount);\n const long = style === \"long\";\n const units: [number, string, string][] = [\n [1e15, \"Padma\", \"पद्म\"],\n [1e13, \"Neel\", \"नील\"],\n [1e11, long ? \"Kharba\" : \"Kharab\", long ? \"खर्ब\" : \"खरब\"],\n [1e9, long ? \"Arba\" : \"Arab\", long ? \"अर्ब\" : \"अरब\"],\n [1e7, long ? \"Crore\" : \"Cr\", \"करोड\"],\n [1e5, \"Lakh\", \"लाख\"],\n [1e3, long ? \"Thousand\" : \"K\", \"हजार\"],\n ];\n const num = (n: number) => (decimals === undefined ? trimNum(n) : n.toFixed(decimals));\n let out: string;\n const u = units.find(([size]) => a >= size);\n if (u) out = `${num(a / u[0])} ${nepali ? u[2] : u[1]}`;\n else out = num(a);\n if (devanagari) out = toDevanagari(out);\n return (negative ? \"-\" : \"\") + symbol + out;\n}\n\n/* ------------------------------ land-area units ------------------------------ */\n\nexport type LandUnit = \"ropani\" | \"aana\" | \"paisa\" | \"daam\" | \"bigha\" | \"kattha\" | \"dhur\" | \"sqm\" | \"sqft\";\n\nconst SQFT = 0.09290304; // exact: 1 international foot = 0.3048 m\nconst ROPANI = 5476 * SQFT; // 508.73704704 m²\nconst BIGHA = 72900 * SQFT; // 6772.631616 m²\n\n/**\n * Area of one unit in square metres (hilly = ropani system, terai = bigha system).\n * Every sub-unit is derived from its parent (ropani/16/64/256, bigha/20/400), so\n * unit ratios are exact and `convertLand(1, \"ropani\", \"daam\")` is exactly 256.\n */\nexport const LAND_UNIT_SQM: Record<LandUnit, number> = {\n ropani: ROPANI,\n aana: ROPANI / 16,\n paisa: ROPANI / 64,\n daam: ROPANI / 256,\n bigha: BIGHA,\n kattha: BIGHA / 20,\n dhur: BIGHA / 400,\n sqm: 1,\n sqft: SQFT,\n};\n\n/** Total square metres from a mixed measure, e.g. `{ ropani: 2, aana: 3 }` or `{ bigha: 1, kattha: 5 }`. */\nexport function landToSqMeters(measure: Partial<Record<LandUnit, number>>): number {\n let m2 = 0;\n for (const [unit, value] of Object.entries(measure)) m2 += (value ?? 0) * LAND_UNIT_SQM[unit as LandUnit];\n return m2;\n}\n\n/** Convert an area between any two land units. */\nexport function convertLand(value: number, from: LandUnit, to: LandUnit): number {\n return (value * LAND_UNIT_SQM[from]) / LAND_UNIT_SQM[to];\n}\n\n/**\n * Break square metres into the hilly system: `{ ropani, aana, paisa, daam }`.\n * Rounds to the nearest whole daam first, then carries daam → paisa → aana →\n * ropani, so `4 daam` never appears (it is `1 paisa`) and float noise from\n * `landToSqMeters` cannot leave a unit one short.\n */\nexport interface LandBreakdownOptions {\n /** Decimal places kept on the smallest unit (daam / dhur). Default `0` (whole units). */\n decimals?: number;\n}\n\nconst roundTo = (n: number, d: number) => { const f = 10 ** d; return Math.round(n * f) / f; };\n\nexport function sqMetersToRopani(m2: number, options: LandBreakdownOptions = {}): { ropani: number; aana: number; paisa: number; daam: number } {\n const d = Math.max(0, Math.min(6, Math.floor(options.decimals ?? 0)));\n const neg = m2 < 0 ? -1 : 1;\n const total = roundTo(Math.abs(m2) / LAND_UNIT_SQM.daam, d); // daam, rounded once\n const ropani = Math.floor(total / 256 + 1e-9);\n const aana = Math.floor((total - ropani * 256) / 16 + 1e-9);\n const paisa = Math.floor((total - ropani * 256 - aana * 16) / 4 + 1e-9);\n const daam = roundTo(total - ropani * 256 - aana * 16 - paisa * 4, d);\n const z = (v: number) => (v === 0 ? 0 : neg * v);\n return { ropani: z(ropani), aana: z(aana), paisa: z(paisa), daam: z(daam) };\n}\n\n/**\n * Break square metres into the terai system: `{ bigha, kattha, dhur }`.\n * Rounds to the nearest whole dhur first, then carries dhur → kattha → bigha,\n * so `20 dhur` becomes `1 kattha` and `20 kattha` becomes `1 bigha`.\n */\nexport function sqMetersToBigha(m2: number, options: LandBreakdownOptions = {}): { bigha: number; kattha: number; dhur: number } {\n const d = Math.max(0, Math.min(6, Math.floor(options.decimals ?? 0)));\n const neg = m2 < 0 ? -1 : 1;\n const total = roundTo(Math.abs(m2) / LAND_UNIT_SQM.dhur, d); // dhur, rounded once\n const bigha = Math.floor(total / 400 + 1e-9);\n const kattha = Math.floor((total - bigha * 400) / 20 + 1e-9);\n const dhur = roundTo(total - bigha * 400 - kattha * 20, d);\n const z = (v: number) => (v === 0 ? 0 : neg * v);\n return { bigha: z(bigha), kattha: z(kattha), dhur: z(dhur) };\n}\n\n/** `\"2-3-1-0\"` ropani-aana-paisa-daam from square metres. */\nexport function formatRopani(m2: number): string {\n const r = sqMetersToRopani(m2);\n return `${r.ropani}-${r.aana}-${r.paisa}-${r.daam}`;\n}\n\n/** `\"1-5-0\"` bigha-kattha-dhur from square metres. */\nexport function formatBigha(m2: number): string {\n const b = sqMetersToBigha(m2);\n return `${b.bigha}-${b.kattha}-${b.dhur}`;\n}\n\n/* ------------------------------ phone & validators ------------------------------ */\n\n/** Normalize a Nepali mobile to canonical `+9779XXXXXXXXX`, or null if invalid. */\nexport function normalizeMobile(input: string): string | null {\n let s = fromDevanagari(input).replace(/[\\s-]/g, \"\");\n // Strip the country code only when a full 10-digit number follows it, so a\n // 10-digit mobile that itself starts with 977 is not mangled.\n const m = /^\\+?977(\\d{10})$/.exec(s);\n if (m) s = m[1]!;\n else if (s.startsWith(\"+\")) return null;\n return /^9[678]\\d{8}$/.test(s) ? `+977${s}` : null;\n}\n\nexport type Carrier = \"Ntc\" | \"Ncell\" | \"Smart Cell\" | \"UTL\" | \"unknown\";\n\n/**\n * Three-digit mobile prefixes (after +977) per carrier. Nepal Telecom and Ncell\n * are the only live mobile operators (2026); Smart Cell and UTL prefixes are\n * kept so old numbers still resolve, flagged by `CARRIER_STATUS`.\n */\nexport const CARRIER_PREFIXES: Record<Exclude<Carrier, \"unknown\">, readonly string[]> = {\n Ntc: [\"984\", \"985\", \"986\", \"974\", \"975\", \"976\"],\n Ncell: [\"980\", \"981\", \"982\", \"970\"],\n \"Smart Cell\": [\"961\", \"962\", \"988\"],\n UTL: [\"972\"],\n};\n\n/** Whether a carrier still operates a mobile network (NTA licence status, 2026). */\nexport const CARRIER_STATUS: Record<Exclude<Carrier, \"unknown\">, \"active\" | \"defunct\"> = {\n Ntc: \"active\",\n Ncell: \"active\",\n \"Smart Cell\": \"defunct\",\n UTL: \"defunct\",\n};\n\n/** Detect the carrier of a Nepali mobile number (see {@link CARRIER_PREFIXES}). */\nexport function getCarrier(input: string): Carrier {\n const n = normalizeMobile(input);\n if (!n) return \"unknown\";\n const p = n.slice(4, 7); // three digits after +977\n for (const [carrier, prefixes] of Object.entries(CARRIER_PREFIXES)) if (prefixes.includes(p)) return carrier as Carrier;\n return \"unknown\";\n}\n\n/** True for a valid Nepali mobile number (10 digits starting 96–98, optional +977). */\nexport function isValidNepaliMobile(input: string): boolean {\n return normalizeMobile(input) !== null; // one rule for both, so they always agree\n}\n\n/** True for a plausible Nepal landline (area code + number, e.g. 01-4XXXXXX). */\nexport function isValidLandline(input: string): boolean {\n const s = fromDevanagari(input).replace(/[\\s-]/g, \"\");\n return /^0\\d{7,9}$/.test(s);\n}\n\n/** True for a valid Nepal PAN / VAT number (9 digits). */\nexport function isValidPAN(input: string): boolean {\n return /^\\d{9}$/.test(fromDevanagari(input).trim());\n}\n\n/** True for a valid Nepal VAT number (9 digits — same format as PAN). */\nexport const isValidVAT = isValidPAN;\n\n/* ------------------------------ Nepali amount in words ------------------------------ */\n\nconst NEPALI_0_99 = [\n \"शून्य\", \"एक\", \"दुई\", \"तीन\", \"चार\", \"पाँच\", \"छ\", \"सात\", \"आठ\", \"नौ\",\n \"दस\", \"एघार\", \"बाह्र\", \"तेह्र\", \"चौध\", \"पन्ध्र\", \"सोह्र\", \"सत्र\", \"अठार\", \"उन्नाइस\",\n \"बीस\", \"एक्काइस\", \"बाइस\", \"तेइस\", \"चौबिस\", \"पच्चिस\", \"छब्बिस\", \"सत्ताइस\", \"अठ्ठाइस\", \"उनन्तिस\",\n \"तिस\", \"एकतिस\", \"बत्तिस\", \"तेत्तिस\", \"चौँतिस\", \"पैँतिस\", \"छत्तिस\", \"सैँतिस\", \"अठतिस\", \"उनन्चालिस\",\n \"चालिस\", \"एकचालिस\", \"बयालिस\", \"त्रिचालिस\", \"चवालिस\", \"पैँतालिस\", \"छयालिस\", \"सतचालिस\", \"अठचालिस\", \"उनन्चास\",\n \"पचास\", \"एकाउन्न\", \"बाउन्न\", \"त्रिपन्न\", \"चवन्न\", \"पचपन्न\", \"छपन्न\", \"सन्ताउन्न\", \"अन्ठाउन्न\", \"उनन्साठी\",\n \"साठी\", \"एकसट्ठी\", \"बयसट्ठी\", \"त्रिसट्ठी\", \"चौंसट्ठी\", \"पैंसट्ठी\", \"छयसट्ठी\", \"सतसट्ठी\", \"अठसट्ठी\", \"उनन्सत्तरी\",\n \"सत्तरी\", \"एकहत्तर\", \"बहत्तर\", \"त्रिहत्तर\", \"चौहत्तर\", \"पचहत्तर\", \"छयहत्तर\", \"सतहत्तर\", \"अठहत्तर\", \"उनासी\",\n \"असी\", \"एकासी\", \"बयासी\", \"त्रियासी\", \"चौरासी\", \"पचासी\", \"छयासी\", \"सतासी\", \"अठासी\", \"उनान्नब्बे\",\n \"नब्बे\", \"एकानब्बे\", \"बयानब्बे\", \"त्रियानब्बे\", \"चौरानब्बे\", \"पन्चानब्बे\", \"छयानब्बे\", \"सन्तानब्बे\", \"अन्ठानब्बे\", \"उनान्सय\",\n];\n\n/**\n * Amount in words in Nepali (Devanagari), South-Asian scale.\n * `numberToWordsNepali(1234567)` → `\"बाह्र लाख चौँतिस हजार पाँच सय सतसट्ठी\"`.\n */\nexport function numberToWordsNepali(value: number): string {\n let n = Math.floor(Math.abs(value));\n if (n === 0) return NEPALI_0_99[0]!;\n const parts: string[] = [];\n const units: [number, string][] = [\n [1e15, \"पद्म\"],\n [1e13, \"नील\"],\n [1e11, \"खरब\"],\n [1e9, \"अरब\"],\n [1e7, \"करोड\"],\n [1e5, \"लाख\"],\n [1e3, \"हजार\"],\n [1e2, \"सय\"],\n ];\n for (const [size, name] of units) {\n if (n >= size) {\n const count = Math.floor(n / size);\n // NEPALI_0_99 only covers 0–99; the top (पद्म) unit can exceed that, so\n // render its count recursively.\n const words = count >= 100 ? numberToWordsNepali(count) : NEPALI_0_99[count]!;\n parts.push(`${words} ${name}`);\n n %= size;\n }\n }\n if (n > 0) parts.push(NEPALI_0_99[n]!);\n return (value < 0 ? \"माइनस \" : \"\") + parts.join(\" \");\n}\n\n/** `numberToWordsNepali` plus a \"रुपैयाँ … मात्र\" wrapper. Accepts a number, decimal string or `{ rupees, paisa }`. */\nexport function amountInWordsNepali(amount: AmountInput): string {\n const { rupees, paisa, negative } = splitAmount(amount);\n let out = \"रुपैयाँ \" + numberToWordsNepali(rupees);\n if (paisa > 0) out += \" र \" + numberToWordsNepali(paisa) + \" पैसा\";\n return (negative ? \"माइनस \" : \"\") + out + \" मात्र\";\n}\n\nexport interface Province {\n number: number;\n name: string;\n nameNp: string;\n capital: string;\n /** Capital in Nepali (Devanagari). */\n capitalNp: string;\n}\n\n/** Nepal's seven federal provinces. */\nexport const PROVINCES: readonly Province[] = [\n { number: 1, name: \"Koshi\", nameNp: \"कोशी\", capital: \"Biratnagar\", capitalNp: \"विराटनगर\" },\n { number: 2, name: \"Madhesh\", nameNp: \"मधेश\", capital: \"Janakpur\", capitalNp: \"जनकपुर\" },\n { number: 3, name: \"Bagmati\", nameNp: \"बागमती\", capital: \"Hetauda\", capitalNp: \"हेटौँडा\" },\n { number: 4, name: \"Gandaki\", nameNp: \"गण्डकी\", capital: \"Pokhara\", capitalNp: \"पोखरा\" },\n { number: 5, name: \"Lumbini\", nameNp: \"लुम्बिनी\", capital: \"Deukhuri\", capitalNp: \"देउखुरी\" },\n { number: 6, name: \"Karnali\", nameNp: \"कर्णाली\", capital: \"Birendranagar\", capitalNp: \"वीरेन्द्रनगर\" },\n { number: 7, name: \"Sudurpashchim\", nameNp: \"सुदूरपश्चिम\", capital: \"Godawari\", capitalNp: \"गोदावरी\" },\n];\n\nexport interface District {\n name: string;\n nameNp: string;\n /** Province number 1–7. */\n province: number;\n /** Other accepted spellings/names (English or Nepali), matched by {@link findDistrict}. */\n aliases?: readonly string[];\n}\n\n/** Nepal's 77 districts, with their province number. */\nexport const DISTRICTS: readonly District[] = [\n // Koshi (1)\n { name: \"Bhojpur\", nameNp: \"भोजपुर\", province: 1 },\n { name: \"Dhankuta\", nameNp: \"धनकुटा\", province: 1 },\n { name: \"Ilam\", nameNp: \"इलाम\", province: 1 },\n { name: \"Jhapa\", nameNp: \"झापा\", province: 1 },\n { name: \"Khotang\", nameNp: \"खोटाङ\", province: 1 },\n { name: \"Morang\", nameNp: \"मोरङ\", province: 1 },\n { name: \"Okhaldhunga\", nameNp: \"ओखलढुंगा\", province: 1 },\n { name: \"Panchthar\", nameNp: \"पाँचथर\", province: 1 },\n { name: \"Sankhuwasabha\", nameNp: \"संखुवासभा\", province: 1 },\n { name: \"Solukhumbu\", nameNp: \"सोलुखुम्बु\", province: 1 },\n { name: \"Sunsari\", nameNp: \"सुनसरी\", province: 1 },\n { name: \"Taplejung\", nameNp: \"ताप्लेजुङ\", province: 1 },\n { name: \"Terhathum\", nameNp: \"तेह्रथुम\", province: 1 },\n { name: \"Udayapur\", nameNp: \"उदयपुर\", province: 1 },\n // Madhesh (2)\n { name: \"Bara\", nameNp: \"बारा\", province: 2 },\n { name: \"Dhanusha\", nameNp: \"धनुषा\", province: 2 },\n { name: \"Mahottari\", nameNp: \"महोत्तरी\", province: 2 },\n { name: \"Parsa\", nameNp: \"पर्सा\", province: 2 },\n { name: \"Rautahat\", nameNp: \"रौतहट\", province: 2 },\n { name: \"Saptari\", nameNp: \"सप्तरी\", province: 2 },\n { name: \"Sarlahi\", nameNp: \"सर्लाही\", province: 2 },\n { name: \"Siraha\", nameNp: \"सिरहा\", province: 2 },\n // Bagmati (3)\n { name: \"Bhaktapur\", nameNp: \"भक्तपुर\", province: 3 },\n { name: \"Chitwan\", nameNp: \"चितवन\", province: 3 },\n { name: \"Dhading\", nameNp: \"धादिङ\", province: 3 },\n { name: \"Dolakha\", nameNp: \"दोलखा\", province: 3 },\n { name: \"Kathmandu\", nameNp: \"काठमाडौं\", province: 3 },\n { name: \"Kavrepalanchok\", nameNp: \"काभ्रेपलाञ्चोक\", province: 3 },\n { name: \"Lalitpur\", nameNp: \"ललितपुर\", province: 3 },\n { name: \"Makwanpur\", nameNp: \"मकवानपुर\", province: 3 },\n { name: \"Nuwakot\", nameNp: \"नुवाकोट\", province: 3 },\n { name: \"Ramechhap\", nameNp: \"रामेछाप\", province: 3 },\n { name: \"Rasuwa\", nameNp: \"रसुवा\", province: 3 },\n { name: \"Sindhuli\", nameNp: \"सिन्धुली\", province: 3 },\n { name: \"Sindhupalchok\", nameNp: \"सिन्धुपाल्चोक\", province: 3 },\n // Gandaki (4)\n { name: \"Baglung\", nameNp: \"बागलुङ\", province: 4 },\n { name: \"Gorkha\", nameNp: \"गोरखा\", province: 4 },\n { name: \"Kaski\", nameNp: \"कास्की\", province: 4 },\n { name: \"Lamjung\", nameNp: \"लमजुङ\", province: 4 },\n { name: \"Manang\", nameNp: \"मनाङ\", province: 4 },\n { name: \"Mustang\", nameNp: \"मुस्ताङ\", province: 4 },\n { name: \"Myagdi\", nameNp: \"म्याग्दी\", province: 4 },\n { name: \"Nawalparasi East\", nameNp: \"नवलपरासी (बर्दघाट सुस्ता पूर्व)\", province: 4, aliases: [\"Nawalpur\", \"नवलपुर\", \"Nawalparasi (East of Bardaghat Susta)\", \"Nawalparasi (Bardaghat Susta East)\", \"Nawalparasi Purba\", \"नवलपरासी पूर्व\"] },\n { name: \"Parbat\", nameNp: \"पर्वत\", province: 4 },\n { name: \"Syangja\", nameNp: \"स्याङ्जा\", province: 4 },\n { name: \"Tanahun\", nameNp: \"तनहुँ\", province: 4 },\n // Lumbini (5)\n { name: \"Arghakhanchi\", nameNp: \"अर्घाखाँची\", province: 5 },\n { name: \"Banke\", nameNp: \"बाँके\", province: 5 },\n { name: \"Bardiya\", nameNp: \"बर्दिया\", province: 5 },\n { name: \"Dang\", nameNp: \"दाङ\", province: 5 },\n { name: \"Eastern Rukum\", nameNp: \"पूर्वी रुकुम\", province: 5, aliases: [\"Rukum East\", \"Rukum Purba\", \"रुकुम पूर्व\"] },\n { name: \"Gulmi\", nameNp: \"गुल्मी\", province: 5 },\n { name: \"Kapilvastu\", nameNp: \"कपिलवस्तु\", province: 5 },\n { name: \"Nawalparasi West\", nameNp: \"नवलपरासी (बर्दघाट सुस्ता पश्चिम)\", province: 5, aliases: [\"Parasi\", \"परासी\", \"Nawalparasi (West of Bardaghat Susta)\", \"Nawalparasi (Bardaghat Susta West)\", \"Nawalparasi Paschim\", \"नवलपरासी पश्चिम\"] },\n { name: \"Palpa\", nameNp: \"पाल्पा\", province: 5 },\n { name: \"Pyuthan\", nameNp: \"प्युठान\", province: 5 },\n { name: \"Rolpa\", nameNp: \"रोल्पा\", province: 5 },\n { name: \"Rupandehi\", nameNp: \"रूपन्देही\", province: 5 },\n // Karnali (6)\n { name: \"Dailekh\", nameNp: \"दैलेख\", province: 6 },\n { name: \"Dolpa\", nameNp: \"डोल्पा\", province: 6 },\n { name: \"Humla\", nameNp: \"हुम्ला\", province: 6 },\n { name: \"Jajarkot\", nameNp: \"जाजरकोट\", province: 6 },\n { name: \"Jumla\", nameNp: \"जुम्ला\", province: 6 },\n { name: \"Kalikot\", nameNp: \"कालिकोट\", province: 6 },\n { name: \"Mugu\", nameNp: \"मुगु\", province: 6 },\n { name: \"Salyan\", nameNp: \"सल्यान\", province: 6 },\n { name: \"Surkhet\", nameNp: \"सुर्खेत\", province: 6 },\n { name: \"Western Rukum\", nameNp: \"पश्चिमी रुकुम\", province: 6, aliases: [\"Rukum West\", \"Rukum Paschim\", \"रुकुम पश्चिम\"] },\n // Sudurpashchim (7)\n { name: \"Achham\", nameNp: \"अछाम\", province: 7 },\n { name: \"Baitadi\", nameNp: \"बैतडी\", province: 7 },\n { name: \"Bajhang\", nameNp: \"बझाङ\", province: 7 },\n { name: \"Bajura\", nameNp: \"बाजुरा\", province: 7 },\n { name: \"Dadeldhura\", nameNp: \"डडेल्धुरा\", province: 7 },\n { name: \"Darchula\", nameNp: \"दार्चुला\", province: 7 },\n { name: \"Doti\", nameNp: \"डोटी\", province: 7 },\n { name: \"Kailali\", nameNp: \"कैलाली\", province: 7 },\n { name: \"Kanchanpur\", nameNp: \"कञ्चनपुर\", province: 7 },\n];\n\n/** Districts in a given province (1–7). */\nexport function districtsByProvince(province: number): District[] {\n return DISTRICTS.filter((d) => d.province === province);\n}\n\n/** Find a district by English or Nepali name or any alias (case-insensitive). */\nexport function findDistrict(name: string): District | undefined {\n const raw = name.trim();\n const q = raw.toLowerCase();\n return DISTRICTS.find(\n (d) => d.name.toLowerCase() === q || d.nameNp === raw || (d.aliases ?? []).some((a) => a.toLowerCase() === q),\n );\n}\n\n/* ------------------------------------------------------------------------- *\n * New in 1.4.0 — Neel/Padma scale, `{ decimals }` on land breakdowns, stricter plate\n * grammar (province word + Pradesh/Province, province number 1–7 only), and\n * isValidNepaliMobile ≡ normalizeMobile.\n * New in 1.3.0 — Kharab scale, \"र\"/\"ra\" rupee–paisa separator, exact land-unit\n * ratios with carry-correct ropani/bigha breakdowns, Ncell 970 + carrier status,\n * Devanagari digits in landline/PAN validators, Nepali province capitals,\n * district aliases (official Nawalparasi East/West), embossed plate shapes.\n * New in 1.2.0 — Roman (transliterated) amount-in-words, integer-paisa NPR\n * format/parse, digit-conversion aliases, extra shape validators, and\n * province lookup helpers. See `./extras`.\n * ------------------------------------------------------------------------- */\nexport * from \"./extras\";\n\n/* 1.6.0: Nepal Standard Time (UTC+05:45) helpers. */\nexport * from \"./time\";\n","/**\n * Ready-made terms for Nepal's 77 districts (names from @lacspace/nepali-utils), with the\n * spellings that actually turn up in copy: काठमाण्डौ, Kavre/काभ्रे, Rukum East/रुकुम पूर्व …\n */\nimport { DISTRICTS } from \"@lacspace/nepali-utils\";\nimport type { TermInput } from \"./index.js\";\n\nexport interface DistrictTerm {\n id: string;\n en: string[];\n ne: string[];\n province: number;\n /** The Nepali name is also an everyday word (पर्वत = mountain): confirm with context before tagging. */\n ambiguous?: boolean;\n caseSensitive: true;\n}\n\nconst ALIASES: Record<string, { en?: string[]; ne?: string[] }> = {\n Kathmandu: { en: [\"Katmandu\"], ne: [\"काठमाण्डौ\", \"काठमान्डु\"] },\n Kavrepalanchok: { en: [\"Kavre\", \"Kabhre\", \"Kabhrepalanchok\", \"Kavrepalanchowk\"], ne: [\"काभ्रे\", \"काभ्रेपलान्चोक\"] },\n Sindhupalchok: { en: [\"Sindhupalchowk\"] },\n Solukhumbu: { en: [\"Solu Khumbu\"], ne: [\"सोलु खुम्बु\"] },\n \"Nawalparasi East\": { en: [\"Nawalparasi (East)\", \"East Nawalparasi\", \"Nawalpur\", \"Bardaghat Susta East\"], ne: [\"नवलपरासी पूर्व\", \"पूर्वी नवलपरासी\", \"नवलपुर\", \"बर्दघाट सुस्ता पूर्व\"] },\n \"Nawalparasi West\": { en: [\"Nawalparasi (West)\", \"West Nawalparasi\", \"Bardaghat Susta West\"], ne: [\"नवलपरासी पश्चिम\", \"पश्चिमी नवलपरासी\", \"बर्दघाट सुस्ता पश्चिम\"] },\n \"Eastern Rukum\": { en: [\"Rukum East\", \"East Rukum\", \"Rukum (East)\"], ne: [\"रुकुम पूर्व\"] },\n \"Western Rukum\": { en: [\"Rukum West\", \"West Rukum\", \"Rukum (West)\"], ne: [\"रुकुम पश्चिम\"] },\n Tanahun: { en: [\"Tanahu\"], ne: [\"तनहु\"] },\n Dhanusha: { en: [\"Dhanusa\"] },\n Kapilvastu: { en: [\"Kapilbastu\"], ne: [\"कपिलबस्तु\"] },\n Makwanpur: { en: [\"Makawanpur\"] },\n Sankhuwasabha: { en: [\"Sankhuwasava\"] },\n Terhathum: { en: [\"Tehrathum\"], ne: [\"तेहथुम\"] },\n Okhaldhunga: { ne: [\"ओखलढुङ्गा\"] },\n Syangja: { en: [\"Syanja\"], ne: [\"स्याङजा\"] },\n Achham: { en: [\"Accham\"] },\n Udayapur: { en: [\"Udaypur\"] },\n Bardiya: { en: [\"Bardia\"] },\n Ilam: { en: [\"Illam\"] },\n Baglung: { ne: [\"बाग्लुङ\"] },\n Rupandehi: { ne: [\"रुपन्देही\"] },\n Dadeldhura: { ne: [\"डँडेलधुरा\"] },\n Mahottari: { ne: [\"महोतरी\"] },\n};\n\n/** District whose Nepali name is a common word. */\nconst AMBIGUOUS = new Set([\"Parbat\"]);\n\nconst slug = (s: string): string => s.toLowerCase().replace(/[^a-z0-9]+/g, \"-\").replace(/^-|-$/g, \"\");\n\n/** Every district as a {@link TermInput}: ids are slugs (\"nawalparasi-east\"), English is exact-case. */\nexport function districtTerms(): DistrictTerm[] {\n return DISTRICTS.map((d) => {\n const extra = ALIASES[d.name] ?? {};\n // nepali-utils' own aliases (Rukum Purba, Parasi/परासी, Bardaghat Susta …), split by script.\n const own = d.aliases ?? [];\n const isNe = (x: string): boolean => /[\\u0900-\\u097f]/.test(x);\n const ne = new Set<string>([d.nameNp, ...(extra.ne ?? []), ...own.filter(isNe)]);\n for (const n of [...ne]) if (n.endsWith(\"ङ\")) ne.add(n.slice(0, -1) + \"ंग\"); // मोरङ / मोरंग\n return {\n id: slug(d.name),\n en: [...new Set([d.name, ...(extra.en ?? []), ...own.filter((x) => !isNe(x))])],\n ne: [...ne],\n province: d.province,\n ...(AMBIGUOUS.has(d.name) ? { ambiguous: true } : {}),\n caseSensitive: true,\n } satisfies DistrictTerm & TermInput;\n });\n}\n","/**\n * Nepali / English matching for names and keywords in running text.\n *\n * normaliseNe fold spelling variants (chandrabindu, half nasals, long/short vowels, nukta, ZWJ)\n * createMatcher whole-word matching that lets Nepali postpositions follow a word\n * (झापाको → Jhapa) but nothing else (पर्वतारोही ≠ Parbat), and keeps short\n * English acronyms case-sensitive (SEE ≠ \"see\")\n * near two terms within N characters, optionally in the same sentence\n *\n * Every match carries offsets into the ORIGINAL text, so callers can highlight or cut.\n */\n\nexport const VERSION = \"1.1.0\";\n\nexport interface NormaliseOptions {\n /** Also fold श/ष → स, व → ब and ण → न. Default false. */\n loose?: boolean;\n /** Devanagari digits ०-९ → 0-9. Default true. */\n digits?: boolean;\n /** Treat hyphens (- ‐ ‑) as spaces, so \"Solu Khumbu\" matches \"Solu-Khumbu\". Default false. @since 1.1.0 */\n hyphenAsSpace?: boolean;\n}\n\n/** Normalised text plus, for each output char, the index of the input char it came from. */\ninterface Mapped {\n text: string;\n map: number[];\n}\n\nconst NUKTA = 0x093c;\nconst HALANT = 0x094d;\nconst NASALS = new Set([0x0919, 0x091e, 0x0923, 0x0928, 0x092e]); // ङ ञ ण न म\nconst DROP = new Set([NUKTA, 0x200c, 0x200d, 0x00ad, 0xfeff]);\nconst FOLD: Record<number, number> = {\n 0x0901: 0x0902, // ँ → ं\n 0x0908: 0x0907, // ई → इ\n 0x090a: 0x0909, // ऊ → उ\n 0x0940: 0x093f, // ी → ि\n 0x0942: 0x0941, // ू → ु\n};\nconst LOOSE: Record<number, number> = {\n 0x0936: 0x0938, // श → स\n 0x0937: 0x0938, // ष → स\n 0x0935: 0x092c, // व → ब\n 0x0923: 0x0928, // ण → न\n};\n\nconst isSpace = (c: number): boolean =>\n c === 32 || (c >= 9 && c <= 13) || c === 0xa0 || c === 0x1680 || (c >= 0x2000 && c <= 0x200a) || c === 0x2028 || c === 0x2029 || c === 0x202f || c === 0x205f || c === 0x3000;\nconst isHyphen = (c: number): boolean => c === 0x2d || c === 0x2010 || c === 0x2011;\nconst isConsonant = (c: number): boolean => (c >= 0x0915 && c <= 0x0939) || (c >= 0x0958 && c <= 0x095f);\n\nfunction mapNormalise(input: string, opts: NormaliseOptions = {}): Mapped {\n const digits = opts.digits !== false;\n // NFC per char would lose the map; NFC first, then map onto the NFC string.\n const s = input.normalize(\"NFC\");\n const out: string[] = [];\n const map: number[] = [];\n const push = (ch: string, i: number): void => {\n out.push(ch);\n map.push(i);\n };\n // Next code point index after i, skipping chars we drop anyway.\n const next = (i: number): number => {\n let j = i + 1;\n while (j < s.length && DROP.has(s.charCodeAt(j))) j++;\n return j;\n };\n let lastSpace = false;\n for (let i = 0; i < s.length; i++) {\n let c = s.charCodeAt(i);\n if (DROP.has(c)) continue;\n if (isSpace(c) || (opts.hyphenAsSpace && isHyphen(c))) {\n if (!lastSpace) push(\" \", i);\n lastSpace = true;\n continue;\n }\n lastSpace = false;\n // Half nasal + consonant → anusvara: चन्द्र → चंद्र, सम्म → संम.\n if (NASALS.has(c)) {\n const h = next(i);\n if (s.charCodeAt(h) === HALANT) {\n const k = next(h);\n if (k < s.length && isConsonant(s.charCodeAt(k))) {\n push(\"ं\", i);\n i = h;\n continue;\n }\n }\n }\n if (FOLD[c] !== undefined) c = FOLD[c]!;\n if (opts.loose && LOOSE[c] !== undefined) c = LOOSE[c]!;\n if (digits && c >= 0x0966 && c <= 0x096f) c = 0x30 + (c - 0x0966);\n if (c >= 0xd800 && c <= 0xdbff && i + 1 < s.length) {\n push(s[i]!, i);\n push(s[i + 1]!, i + 1);\n i++;\n continue;\n }\n push(String.fromCharCode(c), i);\n }\n return { text: out.join(\"\"), map };\n}\n\n/**\n * Fold Nepali spelling variants so equivalent spellings compare equal:\n * NFC; chandrabindu → anusvara; half nasal + consonant → anusvara; ई/ी → इ/ि and ऊ/ू → उ/ु;\n * nukta, ZWJ, ZWNJ and soft hyphens removed; Devanagari digits → ASCII; whitespace collapsed.\n * Latin text passes through unchanged (case included).\n */\nexport function normaliseNe(text: string, opts: NormaliseOptions = {}): string {\n return mapNormalise(text, opts).text;\n}\n/** US spelling alias. */\nexport const normalizeNe = normaliseNe;\n\n/**\n * Words that may be glued to the end of a Nepali name without making it a different word.\n * Matching allows a chain of up to three (जिल्लाहरूमा, काठमाडौंबाटै).\n */\nexport const POSTPOSITIONS: readonly string[] = [\n \"मा\", \"मै\", \"को\", \"का\", \"की\", \"के\", \"कै\", \"ले\", \"लाई\", \"बाट\", \"बाटै\", \"सँग\", \"संग\", \"सित\",\n \"देखि\", \"देखिनै\", \"सम्म\", \"सम्मै\", \"तिर\", \"तर्फ\", \"भित्र\", \"बाहिर\", \"माथि\", \"मुनि\", \"नजिक\", \"पछि\",\n \"अघि\", \"अगाडि\", \"पारि\", \"वारि\", \"द्वारा\", \"हरू\", \"हरु\", \"नै\", \"भर\", \"भरि\", \"वासी\", \"बासी\",\n \"स्थित\", \"निवासी\", \"मार्फत\", \"बारे\", \"अन्तर्गत\", \"मात्र\", \"जस्ता\",\n];\n\nconst isNeLetter = (c: number): boolean => (c >= 0x0900 && c <= 0x0963) || (c >= 0x0971 && c <= 0x097f);\nconst isWordChar = (ch: string | undefined): boolean => !!ch && /[\\p{L}\\p{M}\\p{N}]/u.test(ch);\nconst hasDevanagari = (s: string): boolean => /[ऀ-ॿ]/.test(s);\n\n/** A term to look for: a plain string, or an id with English and/or Nepali spellings. */\nexport type TermInput =\n | string\n | {\n id?: string;\n en?: string | readonly string[];\n ne?: string | readonly string[];\n /** Extra spellings in either script. */\n aliases?: readonly string[];\n /** English case rule for this term. Default \"auto\": all-caps terms up to 5 letters are exact-case. */\n caseSensitive?: boolean | \"auto\";\n };\n\nexport interface MatchOptions extends NormaliseOptions {\n /** Default English case rule. Default \"auto\". */\n caseSensitive?: boolean | \"auto\";\n /** Allow the built-in {@link POSTPOSITIONS} after a Nepali word. Default true. */\n postpositions?: boolean;\n /** Extra suffixes to allow. With `postpositions: false` these are the ONLY suffixes allowed. */\n extraSuffixes?: readonly string[];\n /** Multi-word Nepali terms also match written without the space (सुस्तापूर्व). Default false. @since 1.1.0 */\n joinNepali?: boolean;\n /**\n * An exact-case English term longer than 5 letters also matches in ALL CAPS (\"KATHMANDU\" in a\n * headline). Short acronyms are never affected. Default true. @since 1.1.0\n */\n allCaps?: boolean;\n /** Keep overlapping matches; by default the longest wins. */\n overlaps?: boolean;\n}\n\nexport interface Match {\n /** The term's id (or the spelling itself for plain-string terms). */\n id: string;\n /** The spelling that matched, as given. */\n term: string;\n lang: \"ne\" | \"en\";\n /** Offsets into the original text; `text` is `original.slice(index, end)`, suffix included. */\n index: number;\n end: number;\n text: string;\n /** The postposition chain after the word, normalised (e.g. \"मा\", \"हरुमा\"). */\n suffix?: string;\n}\n\ninterface Variant {\n id: string;\n term: string;\n lang: \"ne\" | \"en\";\n key: string; // normalised (ne) or raw (en)\n re?: RegExp; // en\n}\n\nconst escapeRe = (s: string): string => s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\nfunction autoCase(term: string): boolean {\n const letters = term.replace(/[^\\p{L}]/gu, \"\");\n return letters.length >= 2 && letters.length <= 5 && letters === letters.toUpperCase() && letters !== letters.toLowerCase();\n}\n\n/**\n * Text normalised once, to run many matchers over without re-normalising it each time.\n * Make it with {@link prepare} (or `matcher.prepare`) using the same normalise options as the matchers.\n * @since 1.1.0\n */\nexport interface Prepared {\n readonly kind: \"prepared\";\n /** The NFC original; match offsets point into this. */\n readonly original: string;\n /** The normalised text. */\n readonly text: string;\n /** @internal normalised index → original index. */\n readonly map: readonly number[];\n /** @internal options key. */\n readonly key: string;\n}\n\nconst optsKey = (o: NormaliseOptions): string => `${o.loose ? 1 : 0}${o.digits === false ? 0 : 1}${o.hyphenAsSpace ? 1 : 0}`;\n\n/** Normalise `text` once for repeated matching. @since 1.1.0 */\nexport function prepare(text: string, opts: NormaliseOptions = {}): Prepared {\n const nfc = text.normalize(\"NFC\");\n const { text: n, map } = mapNormalise(nfc, opts);\n return { kind: \"prepared\", original: nfc, text: n, map, key: optsKey(opts) };\n}\n\nconst isPrepared = (x: unknown): x is Prepared => typeof x === \"object\" && x !== null && (x as Prepared).kind === \"prepared\";\n\nexport type TextInput = string | Prepared;\n\nexport interface Matcher {\n /** Every match in `text`, in order of position. Pass a {@link Prepared} to skip normalising. */\n find(text: TextInput): Match[];\n /** True when any term (or the term with this id) occurs. */\n test(text: TextInput, id?: string): boolean;\n /** Distinct ids found, in order of first appearance. */\n ids(text: TextInput): string[];\n /** Normalise text with this matcher's options, for reuse across calls. @since 1.1.0 */\n prepare(text: string): Prepared;\n readonly size: number;\n readonly kind: \"matcher\";\n}\n\n/** Compile a set of terms once; reuse the matcher across many texts. */\nexport function createMatcher(terms: readonly TermInput[], opts: MatchOptions = {}): Matcher {\n const nopts: NormaliseOptions = { loose: opts.loose, digits: opts.digits, hyphenAsSpace: opts.hyphenAsSpace };\n const key = optsKey(nopts);\n const allCaps = opts.allCaps !== false;\n const suffixes = [...new Set([...(opts.postpositions === false ? [] : POSTPOSITIONS), ...(opts.extraSuffixes ?? [])].map((s) => normaliseNe(s, nopts)))]\n .filter(Boolean)\n .sort((a, b) => b.length - a.length);\n const variants: Variant[] = [];\n for (const t of terms) {\n const obj = typeof t === \"string\" ? { id: t, aliases: [t] } : t;\n const list = (v: string | readonly string[] | undefined): string[] => (v === undefined ? [] : typeof v === \"string\" ? [v] : [...v]);\n const spellings = [...list(obj.en), ...list(obj.ne), ...(obj.aliases ?? [])].map((s) => s.trim()).filter(Boolean);\n const id = obj.id ?? spellings[0] ?? \"\";\n for (const sp of new Set(spellings)) {\n if (hasDevanagari(sp)) {\n const k = normaliseNe(sp, nopts);\n variants.push({ id, term: sp, lang: \"ne\", key: k });\n if (opts.joinNepali && k.includes(\" \")) {\n // Every space optional: \"बर्दघाट सुस्ता पूर्व\" also matches \"बर्दघाट सुस्तापूर्व\" and \"बर्दघाटसुस्तापूर्व\".\n const parts = k.split(\" \");\n const gaps = Math.min(parts.length - 1, 4);\n for (let mask = 1; mask < 1 << gaps; mask++) {\n let joined = parts[0]!;\n for (let g = 1; g < parts.length; g++) joined += (g <= gaps && mask & (1 << (g - 1)) ? \"\" : \" \") + parts[g];\n variants.push({ id, term: sp, lang: \"ne\", key: joined });\n }\n }\n } else {\n const rule = obj.caseSensitive ?? opts.caseSensitive ?? \"auto\";\n const exact = rule === \"auto\" ? autoCase(sp) : rule;\n const words = normaliseNe(sp, nopts).split(\" \").filter(Boolean);\n const body = words.map(escapeRe).join(\" \");\n const letters = sp.replace(/[^\\p{L}]/gu, \"\");\n const shout = exact && allCaps && letters.length > 5 && sp !== sp.toUpperCase();\n const alt = shout ? `|${words.map((w) => escapeRe(w.toUpperCase())).join(\" \")}` : \"\";\n variants.push({ id, term: sp, lang: \"en\", key: sp, re: new RegExp(`(?:${body}${alt})`, exact ? \"gu\" : \"giu\") });\n }\n }\n }\n\n // Length of the postposition chain at `pos`, or -1 when the word continues with other letters.\n const suffixEnd = (s: string, pos: number, depth: number): number => {\n if (pos >= s.length || !isNeLetter(s.charCodeAt(pos))) return pos;\n if (depth >= 3) return -1;\n for (const suf of suffixes) {\n if (s.startsWith(suf, pos)) {\n const e = suffixEnd(s, pos + suf.length, depth + 1);\n if (e >= 0) return e;\n }\n }\n return -1;\n };\n\n const prep = (text: string): Prepared => prepare(text, nopts);\n const find = (input: TextInput): Match[] => {\n const p = isPrepared(input) ? (input.key === key ? input : prep(input.original)) : prep(input);\n const { text: n, map, original: nfc } = p;\n const toOrig = (i: number): number => (i >= map.length ? nfc.length : map[i]!);\n const found: Match[] = [];\n for (const v of variants) {\n if (v.lang === \"ne\") {\n if (!v.key) continue;\n let at = n.indexOf(v.key);\n while (at >= 0) {\n const before = n[at - 1];\n if (!isWordChar(before)) {\n const wordEnd = at + v.key.length;\n const end = suffixEnd(n, wordEnd, 0);\n const after = n[end];\n if (end >= 0 && !isWordChar(after)) {\n const s = toOrig(at);\n const e = end >= n.length ? nfc.length : toOrig(end);\n found.push({ id: v.id, term: v.term, lang: \"ne\", index: s, end: e, text: nfc.slice(s, e), ...(end > wordEnd ? { suffix: n.slice(wordEnd, end) } : {}) });\n }\n }\n at = n.indexOf(v.key, at + 1);\n }\n } else {\n v.re!.lastIndex = 0;\n for (const m of n.matchAll(v.re!)) {\n const at = m.index!;\n const end = at + m[0].length;\n if (isWordChar(n[at - 1]) || isWordChar(n[end])) continue;\n const s = toOrig(at);\n const e = end >= n.length ? nfc.length : toOrig(end);\n found.push({ id: v.id, term: v.term, lang: \"en\", index: s, end: e, text: nfc.slice(s, e) });\n }\n }\n }\n found.sort((a, b) => a.index - b.index || b.end - a.end);\n if (opts.overlaps) return found;\n const kept: Match[] = [];\n let reach = -1;\n for (const m of found) {\n if (m.index >= reach) {\n kept.push(m);\n reach = m.end;\n }\n }\n return kept;\n };\n\n return {\n find,\n test: (text, id) => find(text).some((m) => id === undefined || m.id === id),\n ids: (text) => [...new Set(find(text).map((m) => m.id))],\n prepare: prep,\n get size() { return variants.length; },\n kind: \"matcher\",\n };\n}\n\n/** One-off: every match of `terms` in `text`. Compile with {@link createMatcher} for repeated use. */\nexport function findTerms(text: TextInput, terms: TermInput | readonly TermInput[], opts?: MatchOptions): Match[] {\n return createMatcher(Array.isArray(terms) ? (terms as TermInput[]) : [terms as TermInput], opts).find(text);\n}\n\n/** One-off: does `term` occur in `text` as a whole word (Nepali postpositions allowed)? */\nexport function contains(text: TextInput, term: TermInput | readonly TermInput[], opts?: MatchOptions): boolean {\n return findTerms(text, term, opts).length > 0;\n}\n\n/**\n * Split a Nepali word into its stem and trailing postpositions:\n * `splitSuffix(\"जिल्लाहरूमा\")` → `{ stem: \"जिल्ला\", suffixes: [\"हरु\", \"मा\"] }` (suffixes normalised).\n * Only splits when the whole tail is postpositions; the stem keeps the original spelling.\n */\nexport function splitSuffix(word: string): { stem: string; suffixes: string[] } {\n const { text: n, map } = mapNormalise(word.trim());\n const sufs = [...new Set(POSTPOSITIONS.map((s) => normaliseNe(s)))].sort((a, b) => b.length - a.length);\n const nfc = word.trim().normalize(\"NFC\");\n let end = n.length;\n const out: string[] = [];\n while (out.length < 3) {\n const hit = sufs.find((s) => end - s.length >= 2 && n.slice(end - s.length, end) === s);\n if (!hit) break;\n out.unshift(hit);\n end -= hit.length;\n }\n return { stem: nfc.slice(0, end >= n.length ? nfc.length : map[end]), suffixes: out };\n}\n\nexport interface NearOptions extends MatchOptions {\n /** Max characters between the end of one match and the start of the other. Default 60. */\n maxChars?: number;\n /** Require both in the same sentence (। ॥ . ? ! or a newline ends one). Default true. */\n sameSentence?: boolean;\n}\n\n/**\n * What `near` looks for: terms, a compiled {@link Matcher}, or matches you already found in the\n * same text (offsets must come from that text). @since 1.1.0 for Matcher / Match[]\n */\nexport type TermSource = TermInput | readonly TermInput[] | Matcher | readonly Match[];\n\nconst isMatcher = (x: unknown): x is Matcher => typeof x === \"object\" && x !== null && (x as Matcher).kind === \"matcher\";\nconst isMatchList = (x: unknown): x is readonly Match[] =>\n Array.isArray(x) && (x.length === 0 || (typeof x[0] === \"object\" && x[0] !== null && typeof (x[0] as Match).index === \"number\" && typeof (x[0] as Match).end === \"number\"));\n\nexport interface NearResult {\n a: Match;\n b: Match;\n /** Characters between the two matches (0 when adjacent). */\n gap: number;\n}\n\n// \"ने.क.पा.\" / \"U.S.\": a token with an inner dot is an abbreviation, not a sentence end.\nconst dottedAbbr = (s: string, i: number): boolean => {\n let j = i - 1;\n while (j >= 0 && !/\\s/.test(s[j]!)) j--;\n return s.slice(j + 1, i).includes(\".\");\n};\n\n/** Sentence boundaries as [start, end) offsets into the NFC text. \".\" ends a sentence only before a space or the end. */\nexport function sentenceSpans(text: string): [number, number][] {\n const s = text.normalize(\"NFC\");\n const spans: [number, number][] = [];\n let start = 0;\n for (let i = 0; i < s.length; i++) {\n const ch = s[i]!;\n const end = ch === \"।\" || ch === \"॥\" || ch === \"?\" || ch === \"!\" || ch === \"\\n\" || (ch === \".\" && (i + 1 >= s.length || /\\s/.test(s[i + 1]!)) && !dottedAbbr(s, i));\n if (end) {\n spans.push([start, i + 1]);\n start = i + 1;\n }\n }\n if (start < s.length) spans.push([start, s.length]);\n return spans;\n}\n\n/**\n * The closest pair of an `a` match and a `b` match within `maxChars` of each other\n * (in either order), or null. With `sameSentence` (default) the pair must share a sentence.\n */\nexport function near(\n text: TextInput,\n a: TermSource,\n b: TermSource,\n maxCharsOrOpts: number | NearOptions = {},\n sameSentence?: boolean,\n): NearResult | null {\n const opts: NearOptions = typeof maxCharsOrOpts === \"number\" ? { maxChars: maxCharsOrOpts } : { ...maxCharsOrOpts };\n if (sameSentence !== undefined) opts.sameSentence = sameSentence;\n const max = opts.maxChars ?? 60;\n const same = opts.sameSentence !== false;\n const resolve = (src: TermSource): readonly Match[] => {\n if (isMatcher(src)) return src.find(text);\n if (isMatchList(src)) return src;\n return findTerms(text, src as TermInput | readonly TermInput[], opts);\n };\n const am = resolve(a);\n if (!am.length) return null;\n const bm = resolve(b);\n if (!bm.length) return null;\n const spans = same ? sentenceSpans(isPrepared(text) ? text.original : text) : [];\n const sentenceOf = (i: number): number => spans.findIndex(([s, e]) => i >= s && i < e);\n let best: NearResult | null = null;\n for (const x of am) {\n for (const y of bm) {\n if (x.index < y.end && y.index < x.end) continue; // same span of text\n const gap = x.end <= y.index ? y.index - x.end : x.index - y.end;\n if (gap > max) continue;\n if (same && sentenceOf(x.index) !== sentenceOf(y.index)) continue;\n if (!best || gap < best.gap) best = { a: x, b: y, gap };\n }\n }\n return best;\n}\n\nexport { districtTerms, type DistrictTerm } from \"./districts.js\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lacspace/nepali-match",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Nepali and English matching for names and keywords in running text: normaliseNe() folds spelling variants (chandrabindu → anusvara, half nasal + consonant → anusvara, long/short vowels, nukta/ZWJ/ZWNJ, NFC, Devanagari digits); whole-word matching that lets Nepali postpositions follow a word (झापाको → Jhapa, जिल्लाहरूमा, काठमाडौंबाटै) but no other letters (पर्वतारोही ≠ Parbat); English whole words with short all-caps acronyms kept exact-case (\"Come and see\" ≠ SEE); near() for two terms within N characters in the same sentence (। . ? ! newline), dotted abbreviations kept together; offsets into the original text; splitSuffix(); and districtTerms() for all 77 districts with real-world spellings. Pure JS, zero dependencies, React Native safe.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",