@mailwoman/codex 9.3.0 → 10.0.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.
Files changed (223) hide show
  1. package/README.md +221 -75
  2. package/lib/address/format.ts +144 -0
  3. package/lib/address/key.ts +134 -0
  4. package/lib/address/layout.ts +189 -0
  5. package/lib/address/layouts/generated.ts +1576 -0
  6. package/lib/address/layouts/index.ts +213 -0
  7. package/lib/address/render.ts +176 -0
  8. package/lib/{address-system-conventions.ts → address/system-conventions.ts} +6 -5
  9. package/lib/component.ts +96 -0
  10. package/lib/country/index.ts +3 -2
  11. package/lib/country/{reference.ts → reference/index.ts} +1 -1
  12. package/lib/country/region-languages.ts +46 -0
  13. package/lib/es/co-official-languages.ts +62 -0
  14. package/lib/es/index.ts +1 -0
  15. package/lib/gb/index.ts +2 -2
  16. package/lib/index.ts +4 -2
  17. package/lib/jp/index.ts +1 -0
  18. package/lib/jp/municipality-register.ts +62 -0
  19. package/lib/kr/index.ts +10 -0
  20. package/lib/kr/sigungu-register.ts +294 -0
  21. package/lib/placetype-map.ts +157 -0
  22. package/lib/postcode/shapes.json +80 -0
  23. package/lib/postcode/shapes.ts +56 -0
  24. package/lib/{postcode-systems.ts → postcode/systems.ts} +8 -1
  25. package/lib/register-completion.ts +24 -0
  26. package/lib/us/index.ts +2 -2
  27. package/lib/us/po-box.ts +37 -10
  28. package/lib/us/{street-directional.ts → street/directional.ts} +1 -1
  29. package/lib/us/{street-suffix.ts → street/suffix.ts} +1 -1
  30. package/out/address/format.d.ts +87 -0
  31. package/out/address/format.d.ts.map +1 -0
  32. package/out/address/format.js +85 -0
  33. package/out/address/format.js.map +1 -0
  34. package/out/address/key.d.ts +67 -0
  35. package/out/address/key.d.ts.map +1 -0
  36. package/out/address/key.js +91 -0
  37. package/out/address/key.js.map +1 -0
  38. package/out/address/layout.d.ts +104 -0
  39. package/out/address/layout.d.ts.map +1 -0
  40. package/out/address/layout.js +123 -0
  41. package/out/address/layout.js.map +1 -0
  42. package/out/address/layouts/generated.d.ts +21 -0
  43. package/out/address/layouts/generated.d.ts.map +1 -0
  44. package/out/address/layouts/generated.js +1380 -0
  45. package/out/address/layouts/generated.js.map +1 -0
  46. package/out/address/layouts/index.d.ts +81 -0
  47. package/out/address/layouts/index.d.ts.map +1 -0
  48. package/out/address/layouts/index.js +184 -0
  49. package/out/address/layouts/index.js.map +1 -0
  50. package/out/address/render.d.ts +64 -0
  51. package/out/address/render.d.ts.map +1 -0
  52. package/out/address/render.js +115 -0
  53. package/out/address/render.js.map +1 -0
  54. package/out/{address-system-conventions.d.ts → address/system-conventions.d.ts} +6 -5
  55. package/out/address/system-conventions.d.ts.map +1 -0
  56. package/out/{address-system-conventions.js → address/system-conventions.js} +2 -2
  57. package/out/address/system-conventions.js.map +1 -0
  58. package/out/au/delivery-service.d.ts.map +1 -1
  59. package/out/au/level-designator.d.ts.map +1 -1
  60. package/out/au/state.d.ts.map +1 -1
  61. package/out/ca/province.d.ts.map +1 -1
  62. package/out/component.d.ts +50 -0
  63. package/out/component.d.ts.map +1 -0
  64. package/out/component.js +84 -0
  65. package/out/component.js.map +1 -0
  66. package/out/country/codes.d.ts +2 -2
  67. package/out/country/codes.d.ts.map +1 -1
  68. package/out/country/display-names.d.ts.map +1 -1
  69. package/out/country/display-names.js.map +1 -1
  70. package/out/country/index.d.ts +3 -2
  71. package/out/country/index.d.ts.map +1 -1
  72. package/out/country/index.js +3 -2
  73. package/out/country/index.js.map +1 -1
  74. package/out/country/{reference-data.d.ts → reference/data.d.ts} +1 -1
  75. package/out/country/reference/data.d.ts.map +1 -0
  76. package/out/country/{reference-data.js → reference/data.js} +1 -1
  77. package/out/country/reference/data.js.map +1 -0
  78. package/out/country/{reference.d.ts → reference/index.d.ts} +1 -1
  79. package/out/country/reference/index.d.ts.map +1 -0
  80. package/out/country/{reference.js → reference/index.js} +2 -2
  81. package/out/country/reference/index.js.map +1 -0
  82. package/out/country/region-languages.d.ts +25 -0
  83. package/out/country/region-languages.d.ts.map +1 -0
  84. package/out/country/region-languages.js +41 -0
  85. package/out/country/region-languages.js.map +1 -0
  86. package/out/de/bundesland.d.ts.map +1 -1
  87. package/out/de/postleitzahl.d.ts.map +1 -1
  88. package/out/de/street-type.d.ts.map +1 -1
  89. package/out/es/co-official-languages.d.ts +37 -0
  90. package/out/es/co-official-languages.d.ts.map +1 -0
  91. package/out/es/co-official-languages.js +61 -0
  92. package/out/es/co-official-languages.js.map +1 -0
  93. package/out/es/index.d.ts +1 -0
  94. package/out/es/index.d.ts.map +1 -1
  95. package/out/es/index.js +1 -0
  96. package/out/es/index.js.map +1 -1
  97. package/out/fr/departement.d.ts.map +1 -1
  98. package/out/fr/region.d.ts.map +1 -1
  99. package/out/gb/country.d.ts.map +1 -1
  100. package/out/gb/index.d.ts +2 -2
  101. package/out/gb/index.d.ts.map +1 -1
  102. package/out/gb/index.js +2 -2
  103. package/out/gb/index.js.map +1 -1
  104. package/out/gb/{postcode-area.d.ts → postcode/area.d.ts} +1 -1
  105. package/out/gb/postcode/area.d.ts.map +1 -0
  106. package/out/gb/{postcode-area.js → postcode/area.js} +1 -1
  107. package/out/gb/postcode/area.js.map +1 -0
  108. package/out/gb/{postcode.d.ts → postcode/index.d.ts} +1 -1
  109. package/out/gb/postcode/index.d.ts.map +1 -0
  110. package/out/gb/{postcode.js → postcode/index.js} +1 -1
  111. package/out/gb/postcode/index.js.map +1 -0
  112. package/out/gb/street-type.d.ts.map +1 -1
  113. package/out/index.d.ts +4 -2
  114. package/out/index.d.ts.map +1 -1
  115. package/out/index.js +4 -2
  116. package/out/index.js.map +1 -1
  117. package/out/jp/address-unit.d.ts.map +1 -1
  118. package/out/jp/index.d.ts +1 -0
  119. package/out/jp/index.d.ts.map +1 -1
  120. package/out/jp/index.js +1 -0
  121. package/out/jp/index.js.map +1 -1
  122. package/out/jp/municipality-register.d.ts +48 -0
  123. package/out/jp/municipality-register.d.ts.map +1 -0
  124. package/out/jp/municipality-register.js +41 -0
  125. package/out/jp/municipality-register.js.map +1 -0
  126. package/out/jp/prefecture.d.ts.map +1 -1
  127. package/out/kr/index.d.ts +10 -0
  128. package/out/kr/index.d.ts.map +1 -0
  129. package/out/kr/index.js +10 -0
  130. package/out/kr/index.js.map +1 -0
  131. package/out/kr/sigungu-register.d.ts +37 -0
  132. package/out/kr/sigungu-register.d.ts.map +1 -0
  133. package/out/kr/sigungu-register.js +283 -0
  134. package/out/kr/sigungu-register.js.map +1 -0
  135. package/out/level-semantics.d.ts.map +1 -1
  136. package/out/level-semantics.js.map +1 -1
  137. package/out/nz/delivery-service.d.ts.map +1 -1
  138. package/out/placetype-map.d.ts +83 -0
  139. package/out/placetype-map.d.ts.map +1 -0
  140. package/out/placetype-map.js +130 -0
  141. package/out/placetype-map.js.map +1 -0
  142. package/out/postcode/shapes.d.ts +47 -0
  143. package/out/postcode/shapes.d.ts.map +1 -0
  144. package/out/postcode/shapes.js +38 -0
  145. package/out/postcode/shapes.js.map +1 -0
  146. package/out/postcode/shapes.json +80 -0
  147. package/out/{postcode-systems.d.ts → postcode/systems.d.ts} +8 -1
  148. package/out/postcode/systems.d.ts.map +1 -0
  149. package/out/{postcode-systems.js → postcode/systems.js} +9 -2
  150. package/out/postcode/systems.js.map +1 -0
  151. package/out/register-completion.d.ts +14 -0
  152. package/out/register-completion.d.ts.map +1 -0
  153. package/out/register-completion.js +23 -0
  154. package/out/register-completion.js.map +1 -0
  155. package/out/us/floor-designator.d.ts.map +1 -1
  156. package/out/us/index.d.ts +2 -2
  157. package/out/us/index.js +2 -2
  158. package/out/us/military-address.d.ts.map +1 -1
  159. package/out/us/po-box.d.ts +6 -0
  160. package/out/us/po-box.d.ts.map +1 -1
  161. package/out/us/po-box.js +35 -10
  162. package/out/us/po-box.js.map +1 -1
  163. package/out/us/state.d.ts.map +1 -1
  164. package/out/us/{street-directional.d.ts → street/directional.d.ts} +1 -1
  165. package/out/us/street/directional.d.ts.map +1 -0
  166. package/out/us/{street-directional.js → street/directional.js} +2 -2
  167. package/out/us/street/directional.js.map +1 -0
  168. package/out/us/{street-suffix.d.ts → street/suffix.d.ts} +1 -1
  169. package/out/us/street/suffix.d.ts.map +1 -0
  170. package/out/us/{street-suffix.js → street/suffix.js} +2 -2
  171. package/out/us/street/suffix.js.map +1 -0
  172. package/out/us/zipcode.d.ts +1 -1
  173. package/out/us/zipcode.d.ts.map +1 -1
  174. package/package.json +120 -30
  175. package/lib/tools/build-country-surface-lexicon.ts +0 -177
  176. package/lib/tools/export-country-surfaces.ts +0 -48
  177. package/lib/tools/generate-country-population.ts +0 -142
  178. package/lib/tools/generate-country-reference.ts +0 -168
  179. package/lib/tools/generate-official-languages.ts +0 -210
  180. package/lib/tools/index.ts +0 -12
  181. package/out/address-system-conventions.d.ts.map +0 -1
  182. package/out/address-system-conventions.js.map +0 -1
  183. package/out/country/reference-data.d.ts.map +0 -1
  184. package/out/country/reference-data.js.map +0 -1
  185. package/out/country/reference.d.ts.map +0 -1
  186. package/out/country/reference.js.map +0 -1
  187. package/out/gb/postcode-area.d.ts.map +0 -1
  188. package/out/gb/postcode-area.js.map +0 -1
  189. package/out/gb/postcode.d.ts.map +0 -1
  190. package/out/gb/postcode.js.map +0 -1
  191. package/out/postcode-systems.d.ts.map +0 -1
  192. package/out/postcode-systems.js.map +0 -1
  193. package/out/tools/build-country-surface-lexicon.d.ts +0 -43
  194. package/out/tools/build-country-surface-lexicon.d.ts.map +0 -1
  195. package/out/tools/build-country-surface-lexicon.js +0 -149
  196. package/out/tools/build-country-surface-lexicon.js.map +0 -1
  197. package/out/tools/export-country-surfaces.d.ts +0 -15
  198. package/out/tools/export-country-surfaces.d.ts.map +0 -1
  199. package/out/tools/export-country-surfaces.js +0 -34
  200. package/out/tools/export-country-surfaces.js.map +0 -1
  201. package/out/tools/generate-country-population.d.ts +0 -42
  202. package/out/tools/generate-country-population.d.ts.map +0 -1
  203. package/out/tools/generate-country-population.js +0 -106
  204. package/out/tools/generate-country-population.js.map +0 -1
  205. package/out/tools/generate-country-reference.d.ts +0 -36
  206. package/out/tools/generate-country-reference.d.ts.map +0 -1
  207. package/out/tools/generate-country-reference.js +0 -107
  208. package/out/tools/generate-country-reference.js.map +0 -1
  209. package/out/tools/generate-official-languages.d.ts +0 -50
  210. package/out/tools/generate-official-languages.d.ts.map +0 -1
  211. package/out/tools/generate-official-languages.js +0 -149
  212. package/out/tools/generate-official-languages.js.map +0 -1
  213. package/out/tools/index.d.ts +0 -12
  214. package/out/tools/index.d.ts.map +0 -1
  215. package/out/tools/index.js +0 -12
  216. package/out/tools/index.js.map +0 -1
  217. package/out/us/street-directional.d.ts.map +0 -1
  218. package/out/us/street-directional.js.map +0 -1
  219. package/out/us/street-suffix.d.ts.map +0 -1
  220. package/out/us/street-suffix.js.map +0 -1
  221. /package/lib/country/{reference-data.ts → reference/data.ts} +0 -0
  222. /package/lib/gb/{postcode-area.ts → postcode/area.ts} +0 -0
  223. /package/lib/gb/{postcode.ts → postcode/index.ts} +0 -0
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Postcode SHAPE patterns — which substrings of a line LOOK like a postcode, ordered most-specific
7
+ * to least. Priority IS the index: a lower index wins an overlap.
8
+ *
9
+ * The DATA lives in `./shapes.json` so non-TS consumers read the identical record, the
10
+ * same arrangement `us/street-suffix.json` has. Two runtimes need this table and both used to
11
+ * carry their own typed copy: `@mailwoman/neural`'s postcode repair, and the Python trainer's
12
+ * `features/postcode_shapes.py`, which paints the train-side anchor on the spans inference paints.
13
+ * Hand-mirrored, they drifted twice — the IE Eircode row was TS-only for a month, the BR CEP row
14
+ * for five weeks — and each time the trainer painted one fewer shape than inference, silently.
15
+ *
16
+ * This is a SHAPE test, not a gazetteer-membership test. A bare `68161` matches the US, German,
17
+ * French, Spanish and Italian 5-digit shapes; `./systems.ts` answers the membership
18
+ * question, and neither module reads the other.
19
+ *
20
+ * REGEX DIALECT. The bodies are written in the subset both JavaScript `RegExp` and Python `re`
21
+ * accept, which is what lets one file serve both. A row needing different source text in the two
22
+ * dialects needs a second field and a stated reason, not a loosened comparison on either side.
23
+ */
24
+
25
+ import postcodeShapeData from "./shapes.json" with { type: "json" }
26
+
27
+ /**
28
+ * What a match is eligible to do. `designated` may overwrite any existing label, `alnum` may add a postcode where the
29
+ * model emitted none, and `numeric` may only snap an existing one — so a numeric shape can never invent a postcode over
30
+ * a hyphenated house number.
31
+ */
32
+ export type PostcodeShapeKind = "alnum" | "numeric" | "designated"
33
+
34
+ /**
35
+ * One shape: the label it reports, what it is eligible to do, and the compiled pattern.
36
+ */
37
+ export interface PostcodeShape {
38
+ readonly label: string
39
+ readonly kind: PostcodeShapeKind
40
+ readonly re: RegExp
41
+ }
42
+
43
+ /**
44
+ * Every postcode shape, in priority order. Compiled once at module load; each `RegExp` carries the `g` flag because
45
+ * callers scan a whole line, and `u` as well where the row declares it.
46
+ */
47
+ export const POSTCODE_SHAPES: readonly PostcodeShape[] = postcodeShapeData.shapes.map((shape) => ({
48
+ label: shape.label,
49
+ kind: shape.kind as PostcodeShapeKind,
50
+ re: new RegExp(shape.pattern, "unicode" in shape && shape.unicode ? "gu" : "g"),
51
+ }))
52
+
53
+ /**
54
+ * The record's own version string, so a consumer that vendors a copy can say which revision it holds.
55
+ */
56
+ export const POSTCODE_SHAPES_VERSION: string = postcodeShapeData.version
@@ -186,8 +186,15 @@ export function isUnitGradePostcodeHit(parsed: string, resolverName: string | un
186
186
  * **The JP row is the JP board through the served path** (`jp-served-resolve.run.ts`, 2,000 rows, seed 42): the 586
187
187
  * rows carrying a postcode the candidate table keys, graded on the row's own entrance point — the locality-first answer
188
188
  * is the municipality centroid, the postcode answer the code's 町域 centroid from the WOF extract.
189
+ *
190
+ * **SG** is the limiting case of the tier: a six-digit Singapore postcode names ONE building, so the code's point IS
191
+ * the address, and the only locality above it is the city-state itself. Measured on a 300-row seeded draw of the
192
+ * Overture-SG register (`postalcode-sg-overture.db` folded, each row geocoded as `<number> <street> Singapore
193
+ * <postcode>` and graded on its own point): the postcode point answers 300 of 300 within 1 km (p99 0.16 km);
194
+ * locality-first answers 185, and the other 115 take the "Singapore" locality centroid (p75 6.30 km, p90 11.43 km, p99
195
+ * 24.18 km). The postcode point is closer on those 115 and equal on the rest; it is never farther.
189
196
  */
190
- export const AREA_POSTCODE_FINER_THAN_LOCALITY: ReadonlySet<string> = new Set(["DE", "JP"])
197
+ export const AREA_POSTCODE_FINER_THAN_LOCALITY: ReadonlySet<string> = new Set(["DE", "JP", "SG"])
191
198
 
192
199
  /**
193
200
  * True when this country's area-grade postal code outranks its locality. Absent or unknown country → false, so the
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * The one read every name register offers a decode-time repair: given the surface a model closed a span on and the
7
+ * text that follows, the characters that would extend the surface to a register name. A surface that already IS a
8
+ * name and is not the prefix of a longer one answers null.
9
+ */
10
+
11
+ /**
12
+ * The remainder of the first register name that begins with `surface` and continues into `following`, or null.
13
+ */
14
+ export function completeFromRegister(names: readonly string[], surface: string, following: string): string | null {
15
+ for (const name of names) {
16
+ if (name.length > surface.length && name.startsWith(surface)) {
17
+ const remainder = name.slice(surface.length)
18
+
19
+ if (following.startsWith(remainder)) return remainder
20
+ }
21
+ }
22
+
23
+ return null
24
+ }
package/lib/us/index.ts CHANGED
@@ -13,7 +13,7 @@ export * from "#us/floor-designator"
13
13
  export * from "#us/military-address"
14
14
  export * from "#us/po-box"
15
15
  export * from "#us/state"
16
- export * from "#us/street-directional"
17
- export * from "#us/street-suffix"
16
+ export * from "#us/street/directional"
17
+ export * from "#us/street/suffix"
18
18
  export * from "#us/unit-designator"
19
19
  export * from "#us/zipcode"
package/lib/us/po-box.ts CHANGED
@@ -31,11 +31,37 @@ export const US_PO_BOX_DESIGNATORS = [
31
31
  export type USPoBoxDesignator = (typeof US_PO_BOX_DESIGNATORS)[number]
32
32
 
33
33
  /**
34
- * Matches a leading PO-box designator + its identifier. Allows "P.O. Box", "PO BOX", "Post Office Box", "Box 12-A",
35
- * etc. The id is alphanumeric with optional dashes (USPS caller/firm ids exist).
34
+ * Recognition patterns for {@link US_PO_BOX_DESIGNATORS}. Keeping the surface grammar next to the canonical designator
35
+ * prevents the exported lexicon and matcher from drifting apart.
36
36
  */
37
- const PO_BOX_RE =
38
- /^\s*(?:(p\.?\s*o\.?\s*box)|(post\s+office\s+box)|(firm\s+caller)|(caller)|(drawer)|(lockbox)|(box))\s*#?\s*([\dA-Za-z][\dA-Za-z-]*)\s*$/i
37
+ const PO_BOX_DESIGNATOR_PATTERNS: ReadonlyArray<readonly [USPoBoxDesignator, string]> = [
38
+ ["POST OFFICE BOX", String.raw`post\s+office\s+box`],
39
+ ["PO BOX", String.raw`p\.?\s*o\.?\s*box`],
40
+ ["P O BOX", String.raw`p\.?\s*o\.?\s*box`],
41
+ ["FIRM CALLER", String.raw`firm\s+caller`],
42
+ ["CALLER", "caller"],
43
+ ["DRAWER", "drawer"],
44
+ ["LOCKBOX", "lockbox"],
45
+ ["BOX", "box"],
46
+ ]
47
+
48
+ /**
49
+ * One standalone matcher per USPS designator. The id is alphanumeric with optional dashes (USPS caller/firm ids exist).
50
+ */
51
+ const PO_BOX_MATCHERS = PO_BOX_DESIGNATOR_PATTERNS.map(([designator, pattern]) => ({
52
+ designator,
53
+ designatorRe: new RegExp(String.raw`^\s*${pattern}\s*$`, "i"),
54
+ phraseRe: new RegExp(String.raw`^\s*(${pattern})\s*#?\s*([\dA-Za-z][\dA-Za-z-]*)\s*$`, "i"),
55
+ }))
56
+
57
+ /**
58
+ * True when `input` is a USPS PO-box designator without its box identifier. This is useful to consumers that compose a
59
+ * phrase and need to distinguish USPS vocabulary from their own locale-specific aliases (for example, the corpus's
60
+ * `POB` training variant).
61
+ */
62
+ export function isUSPoBoxDesignator(input: unknown): input is string {
63
+ return typeof input === "string" && PO_BOX_MATCHERS.some(({ designatorRe }) => designatorRe.test(input))
64
+ }
39
65
 
40
66
  /**
41
67
  * Type-predicate: does the input look like a standalone PO Box address? Case-insensitive and tolerant of "P.O. Box",
@@ -43,7 +69,7 @@ const PO_BOX_RE =
43
69
  * all-caps "PO BOX 123".)
44
70
  */
45
71
  export function isPOBox(input: unknown): boolean {
46
- return typeof input === "string" && PO_BOX_RE.test(input)
72
+ return matchPOBox(input) !== null
47
73
  }
48
74
 
49
75
  /**
@@ -67,13 +93,14 @@ export interface PoBoxMatch {
67
93
  */
68
94
  export function matchPOBox(input: unknown): PoBoxMatch | null {
69
95
  if (typeof input !== "string") return null
70
- const m = PO_BOX_RE.exec(input)
71
96
 
72
- if (!m) return null
73
- const matched = (m[1] ?? m[2] ?? m[3] ?? m[4] ?? m[5] ?? m[6] ?? m[7] ?? "").trim()
74
- const id = m[8]!
97
+ for (const { phraseRe } of PO_BOX_MATCHERS) {
98
+ const match = phraseRe.exec(input)
99
+
100
+ if (match) return { matched: match[1]!.trim(), id: match[2]! }
101
+ }
75
102
 
76
- return { matched, id }
103
+ return null
77
104
  }
78
105
 
79
106
  /**
@@ -10,7 +10,7 @@
10
10
  * @see {@link https://pe.usps.com/text/pub28/28apc_002.htm USPS Pub 28 Appendix C1}
11
11
  */
12
12
 
13
- import { matchCase } from "#us/street-suffix"
13
+ import { matchCase } from "#us/street/suffix"
14
14
 
15
15
  /**
16
16
  * The 8 directional abbreviations accepted by the USPS. The USPS prefers the abbreviation over the fully-spelled-out
@@ -24,7 +24,7 @@
24
24
  * @see {@link https://pe.usps.com/text/pub28/28apc_002.htm USPS Street Suffix Abbreviations}
25
25
  */
26
26
 
27
- import streetSuffixData from "./street-suffix.json" with { type: "json" }
27
+ import streetSuffixData from "../street-suffix.json" with { type: "json" }
28
28
 
29
29
  /**
30
30
  * Canonical USPS street suffix → list of recognized variants. The first variant in each list is the preferred USPS
@@ -0,0 +1,87 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Render a `ComponentTag`-keyed dict into a country-localized string — the inverse of the parser.
7
+ *
8
+ * The order lives in `@mailwoman/codex/address-layouts`, as data. This module is the public surface over
9
+ * {@linkcode renderAddress}: {@linkcode formatAddress} is the join, and {@linkcode formatAddressRow} is the join plus
10
+ * the tags the layout printed, which is what every corpus adapter actually wants.
11
+ *
12
+ * IT USED TO WRAP A THIRD-PARTY ENGINE, and 229 of this file's 438 lines existed to work around templates written
13
+ * against OpenStreetMap's tag vocabulary rather than this project's: a pass that parsed 295 mustache templates at
14
+ * module load to discover which of them could render a sub-locality, a second that spliced a missing line back in
15
+ * afterwards, a third that removed a connector the template wrote between two slots when one was empty, and a
16
+ * translation layer between the two vocabularies. Owning the layouts deletes all four — a layout that declares a
17
+ * `dependent_locality` slot needs no interrogation about whether it has one, and a line assembled from present values
18
+ * never writes a connector around an absent one.
19
+ */
20
+ import { type ComponentDict } from "#address/render";
21
+ import type { ComponentTag } from "#component";
22
+ export type { ComponentDict } from "#address/render";
23
+ /**
24
+ * Options accepted by {@linkcode formatAddress} and {@linkcode formatAddressRow}.
25
+ */
26
+ export interface FormatAddressOptions {
27
+ /**
28
+ * Replace the layout's line breaks with this separator. Default `"\n"`: the envelope form.
29
+ */
30
+ separator?: string;
31
+ /**
32
+ * Join the lines the way the COUNTRY does, for the single-line form a query or a corpus row takes — `", "` for most,
33
+ * `" "` for Japan and Korea, and nothing at all for the Chinese-script systems, whose admin run is unseparated.
34
+ *
35
+ * It is an option rather than each caller's literal because the literal is wrong outside the anglophone systems:
36
+ * joining Japan's lines with a comma gives `1-9-1, 丸の内, 千代田区, 東京都 100-0005`, which is the romanized convention
37
+ * printed backwards. `separator` wins when both are given.
38
+ */
39
+ singleLine?: boolean;
40
+ }
41
+ /**
42
+ * Render a component dict into an idiomatic per-country address string.
43
+ *
44
+ * Returns an empty string when the dict is empty, and when no layout names `country` — 55 of the 252 shipped country
45
+ * records carry no usable skeleton, and answering nothing for one of those reports absence rather than inventing an
46
+ * order. Throws nothing; a partial dict degrades to the parts the layout can print.
47
+ */
48
+ export declare function formatAddress(components: ComponentDict, country: string, opts?: FormatAddressOptions): string;
49
+ /**
50
+ * A rendered address and the components that survived the render.
51
+ */
52
+ export interface AddressRow {
53
+ /**
54
+ * The rendered string.
55
+ */
56
+ readonly raw: string;
57
+ /**
58
+ * The subset of the input dict the layout PRINTED, with the caller's original values. This is the half a corpus row
59
+ * needs: a label whose text is not in `raw` cannot be aligned against it.
60
+ */
61
+ readonly components: ComponentDict;
62
+ /**
63
+ * Tags the dict carried a value for that the layout has no slot for, NAMED rather than silently dropped. France
64
+ * absorbing a region into its postcode line is the common case.
65
+ */
66
+ readonly unplaced: readonly ComponentTag[];
67
+ }
68
+ /**
69
+ * Render `components` for `country` and report what the layout printed, in one pass.
70
+ *
71
+ * Returns null when nothing rendered — an empty dict, a country with no layout, or a dict whose every value falls in a
72
+ * slot this country omits. Every corpus adapter asked both questions and paid for two renders to get them, then
73
+ * recovered the alignment by searching the output string for each value; that search cannot tell a component the layout
74
+ * dropped from one whose value happens to sit inside another — `Paris` inside `Rue de Paris`. The render knows, so the
75
+ * answer is read rather than inferred.
76
+ */
77
+ export declare function formatAddressRow(components: ComponentDict, country: string, opts?: FormatAddressOptions): AddressRow | null;
78
+ /**
79
+ * Which of `components` occur verbatim in `raw`, case- and whitespace-insensitively.
80
+ *
81
+ * This is a question about a string somebody else built — a committed golden fixture, a source's own address line — and
82
+ * it is the WEAKER of the two reconciliations: a substring test cannot tell a component the renderer dropped from one
83
+ * whose value happens to sit inside another. Anything rendered through a layout should read
84
+ * {@linkcode formatAddressRow}'s `components` instead, which the render knows rather than infers.
85
+ */
86
+ export declare function componentsPresentIn(components: ComponentDict, raw: string): ComponentDict;
87
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../lib/address/format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAgC,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAClF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACpC;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;CACpB;AAQD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,oBAAyB,GAAG,MAAM,CAEjH;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAA;IAClC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAA;CAC1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC/B,UAAU,EAAE,aAAa,EACzB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,oBAAyB,GAC7B,UAAU,GAAG,IAAI,CAwBnB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,CAazF"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Render a `ComponentTag`-keyed dict into a country-localized string — the inverse of the parser.
7
+ *
8
+ * The order lives in `@mailwoman/codex/address-layouts`, as data. This module is the public surface over
9
+ * {@linkcode renderAddress}: {@linkcode formatAddress} is the join, and {@linkcode formatAddressRow} is the join plus
10
+ * the tags the layout printed, which is what every corpus adapter actually wants.
11
+ *
12
+ * IT USED TO WRAP A THIRD-PARTY ENGINE, and 229 of this file's 438 lines existed to work around templates written
13
+ * against OpenStreetMap's tag vocabulary rather than this project's: a pass that parsed 295 mustache templates at
14
+ * module load to discover which of them could render a sub-locality, a second that spliced a missing line back in
15
+ * afterwards, a third that removed a connector the template wrote between two slots when one was empty, and a
16
+ * translation layer between the two vocabularies. Owning the layouts deletes all four — a layout that declares a
17
+ * `dependent_locality` slot needs no interrogation about whether it has one, and a line assembled from present values
18
+ * never writes a connector around an absent one.
19
+ */
20
+ import { layoutForCountry, lineJoinForCountry } from "#address/layouts/index";
21
+ import { joinRendering, renderAddress } from "#address/render";
22
+ function separatorFor(country, opts) {
23
+ if (opts.separator !== undefined)
24
+ return opts.separator;
25
+ return opts.singleLine ? lineJoinForCountry(country) : "\n";
26
+ }
27
+ /**
28
+ * Render a component dict into an idiomatic per-country address string.
29
+ *
30
+ * Returns an empty string when the dict is empty, and when no layout names `country` — 55 of the 252 shipped country
31
+ * records carry no usable skeleton, and answering nothing for one of those reports absence rather than inventing an
32
+ * order. Throws nothing; a partial dict degrades to the parts the layout can print.
33
+ */
34
+ export function formatAddress(components, country, opts = {}) {
35
+ return formatAddressRow(components, country, opts)?.raw ?? "";
36
+ }
37
+ /**
38
+ * Render `components` for `country` and report what the layout printed, in one pass.
39
+ *
40
+ * Returns null when nothing rendered — an empty dict, a country with no layout, or a dict whose every value falls in a
41
+ * slot this country omits. Every corpus adapter asked both questions and paid for two renders to get them, then
42
+ * recovered the alignment by searching the output string for each value; that search cannot tell a component the layout
43
+ * dropped from one whose value happens to sit inside another — `Paris` inside `Rue de Paris`. The render knows, so the
44
+ * answer is read rather than inferred.
45
+ */
46
+ export function formatAddressRow(components, country, opts = {}) {
47
+ const layout = layoutForCountry(country);
48
+ if (!layout)
49
+ return null;
50
+ const rendering = renderAddress(layout, components);
51
+ if (!rendering.placed.length)
52
+ return null;
53
+ const raw = joinRendering(rendering, separatorFor(country, opts));
54
+ if (!raw)
55
+ return null;
56
+ const placed = {};
57
+ for (const tag of rendering.placed) {
58
+ const value = components[tag];
59
+ if (value) {
60
+ placed[tag] = value;
61
+ }
62
+ }
63
+ return { raw, components: placed, unplaced: rendering.unplaced };
64
+ }
65
+ /**
66
+ * Which of `components` occur verbatim in `raw`, case- and whitespace-insensitively.
67
+ *
68
+ * This is a question about a string somebody else built — a committed golden fixture, a source's own address line — and
69
+ * it is the WEAKER of the two reconciliations: a substring test cannot tell a component the renderer dropped from one
70
+ * whose value happens to sit inside another. Anything rendered through a layout should read
71
+ * {@linkcode formatAddressRow}'s `components` instead, which the render knows rather than infers.
72
+ */
73
+ export function componentsPresentIn(components, raw) {
74
+ const haystack = raw.toLowerCase().replaceAll(/\s+/g, " ");
75
+ const out = {};
76
+ for (const [tag, value] of Object.entries(components)) {
77
+ if (!value)
78
+ continue;
79
+ if (haystack.includes(value.toLowerCase().replaceAll(/\s+/g, " "))) {
80
+ out[tag] = value;
81
+ }
82
+ }
83
+ return out;
84
+ }
85
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../lib/address/format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAC7E,OAAO,EAAE,aAAa,EAAE,aAAa,EAAsB,MAAM,iBAAiB,CAAA;AAyBlF,SAAS,YAAY,CAAC,OAAe,EAAE,IAA0B;IAChE,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC,SAAS,CAAA;IAEvD,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AAC5D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,UAAyB,EAAE,OAAe,EAAE,IAAI,GAAyB,EAAE;IACxG,OAAO,gBAAgB,CAAC,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,CAAA;AAC9D,CAAC;AAsBD;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAC/B,UAAyB,EACzB,OAAe,EACf,IAAI,GAAyB,EAAE;IAE/B,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;IAExC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IAExB,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IAEnD,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IAEzC,MAAM,GAAG,GAAG,aAAa,CAAC,SAAS,EAAE,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAA;IAEjE,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAA;IAErB,MAAM,MAAM,GAAkB,EAAE,CAAA;IAEhC,KAAK,MAAM,GAAG,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC;QACpC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAA;QAE7B,IAAI,KAAK,EAAE,CAAC;YACX,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;QACpB,CAAC;IACF,CAAC;IAED,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,CAAC,QAAQ,EAAE,CAAA;AACjE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAAyB,EAAE,GAAW;IACzE,MAAM,QAAQ,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC1D,MAAM,GAAG,GAAkB,EAAE,CAAA;IAE7B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QACvD,IAAI,CAAC,KAAK;YAAE,SAAQ;QAEpB,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;YACpE,GAAG,CAAC,GAAmB,CAAC,GAAG,KAAK,CAAA;QACjC,CAAC;IACF,CAAC;IAED,OAAO,GAAG,CAAA;AACX,CAAC"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * The canonical match key — a normalized, deterministic string derived from address components,
7
+ * distinct from the human-readable formatted string.
8
+ *
9
+ * Where `format.ts` produces something for a person to read, this produces something for a
10
+ * _machine_ to collide on: lowercased, diacritic-stripped, punctuation-flattened, whitespace-
11
+ * collapsed, fields in a fixed canonical order. Two records for the same address that differ only
12
+ * in spelling, case, or punctuation produce the same key — which is exactly what the matcher's
13
+ * blocking stage wants as one cheap, high-precision candidate signal (alongside geographic
14
+ * proximity, which carries the real weight — see the geocode-first record-matching concept doc).
15
+ *
16
+ * Deliberately NOT done yet (follow-ups, all conditioned on `@mailwoman/codex`): expanding street
17
+ * suffixes (`Ave` → `avenue`) and directionals (`N` → `north`) to a canonical form, and
18
+ * USPS-style standardization. This first version is pure normalization with no dictionary expansion,
19
+ * so the key is stable and explainable; expansion is an additive refinement, not a rewrite.
20
+ */
21
+ import type { ComponentDict } from "#address/format";
22
+ /**
23
+ * Options accepted by {@linkcode canonicalKey}.
24
+ */
25
+ export interface CanonicalKeyOptions {
26
+ /**
27
+ * Field separator in the emitted key. Default `"|"` — preserves field boundaries for blocking.
28
+ */
29
+ separator?: string;
30
+ }
31
+ /**
32
+ * Options for {@linkcode foldForKey} — the two points where the formatter's address-token fold and the record package's
33
+ * fragment fold legitimately differ.
34
+ */
35
+ export interface FoldForKeyOptions {
36
+ /**
37
+ * How connective punctuation folds. `"space"` turns `&`, `+`, and `/` into word boundaries (`"A&B"` → `"a b"`);
38
+ * `"and"` spells `&` and `+` out as the word `and` (`"AT&T"` → `"at and t"`), leaving `/` to the punctuation
39
+ * catch-all (still a word boundary).
40
+ */
41
+ ampersand: "space" | "and";
42
+ /**
43
+ * Intra-token deletion set. When true, periods join the apostrophes as intra-token noise and are deleted (`"S.A."` →
44
+ * `"sa"`), while a backtick falls to the punctuation catch-all. When false or omitted, backticks are deleted
45
+ * alongside the apostrophes and periods become word boundaries (`"S.A."` → `"s a"`).
46
+ */
47
+ dropPeriods?: boolean;
48
+ }
49
+ /**
50
+ * The shared fold behind every match key: NFKD-decompose and strip combining marks (so `é` → `e`), lowercase, delete
51
+ * intra-token punctuation, expand or flatten connective punctuation per {@linkcode FoldForKeyOptions}, space every
52
+ * remaining non-alphanumeric, and collapse whitespace. Deterministic — the same input and options always yield the same
53
+ * output.
54
+ */
55
+ export declare function foldForKey(input: string, options: FoldForKeyOptions): string;
56
+ /**
57
+ * Normalize a single token for matching: {@linkcode foldForKey} with connective punctuation flattened to spaces (so
58
+ * `"A&B"` → `"a b"`, not `"ab"`). Deterministic and reversible-free — the same input always yields the same output.
59
+ */
60
+ export declare function normalizeAddressToken(input: string): string;
61
+ /**
62
+ * Derive the canonical match key from an address component dict: each present, address-identifying field normalized via
63
+ * {@linkcode normalizeAddressToken}, in fixed order, joined by the separator. Empty / whitespace-only fields are
64
+ * skipped. Returns an empty string if nothing identifying remains.
65
+ */
66
+ export declare function canonicalKey(components: ComponentDict, opts?: CanonicalKeyOptions): string;
67
+ //# sourceMappingURL=key.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key.d.ts","sourceRoot":"","sources":["../../lib/address/key.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAyBpD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IACjC;;;;OAIG;IACH,SAAS,EAAE,OAAO,GAAG,KAAK,CAAA;IAC1B;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAqB5E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,aAAa,EAAE,IAAI,GAAE,mBAAwB,GAAG,MAAM,CAgB9F"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * The canonical match key — a normalized, deterministic string derived from address components,
7
+ * distinct from the human-readable formatted string.
8
+ *
9
+ * Where `format.ts` produces something for a person to read, this produces something for a
10
+ * _machine_ to collide on: lowercased, diacritic-stripped, punctuation-flattened, whitespace-
11
+ * collapsed, fields in a fixed canonical order. Two records for the same address that differ only
12
+ * in spelling, case, or punctuation produce the same key — which is exactly what the matcher's
13
+ * blocking stage wants as one cheap, high-precision candidate signal (alongside geographic
14
+ * proximity, which carries the real weight — see the geocode-first record-matching concept doc).
15
+ *
16
+ * Deliberately NOT done yet (follow-ups, all conditioned on `@mailwoman/codex`): expanding street
17
+ * suffixes (`Ave` → `avenue`) and directionals (`N` → `north`) to a canonical form, and
18
+ * USPS-style standardization. This first version is pure normalization with no dictionary expansion,
19
+ * so the key is stable and explainable; expansion is an additive refinement, not a rewrite.
20
+ */
21
+ /**
22
+ * The address-identifying components, in canonical key order. Venue / attention are intentionally excluded — those
23
+ * carry organization identity, which the record layer keys separately.
24
+ */
25
+ const KEY_FIELD_ORDER = [
26
+ "po_box",
27
+ "house_number",
28
+ "street_prefix",
29
+ "street_prefix_particle",
30
+ "street",
31
+ "street_suffix",
32
+ "intersection_a",
33
+ "intersection_b",
34
+ "unit",
35
+ "dependent_locality",
36
+ "locality",
37
+ "subregion",
38
+ "region",
39
+ "postcode",
40
+ "country",
41
+ ];
42
+ /**
43
+ * The shared fold behind every match key: NFKD-decompose and strip combining marks (so `é` → `e`), lowercase, delete
44
+ * intra-token punctuation, expand or flatten connective punctuation per {@linkcode FoldForKeyOptions}, space every
45
+ * remaining non-alphanumeric, and collapse whitespace. Deterministic — the same input and options always yield the same
46
+ * output.
47
+ */
48
+ export function foldForKey(input, options) {
49
+ const folded = input
50
+ .normalize("NFKD")
51
+ // strip combining marks (U+0300–U+036F) left by NFKD decomposition, so "é" → "e"
52
+ .replaceAll(/[\u0300-\u036F]/g, "")
53
+ .toLowerCase()
54
+ // apostrophes are intra-word (possessives, "O'Brien") — delete so the token stays whole
55
+ .replaceAll(options.dropPeriods ? /[.'’]/g : /['’`]/g, "");
56
+ const connected = options.ampersand === "and"
57
+ ? folded.replaceAll("&", " and ").replaceAll("+", " and ")
58
+ : folded.replaceAll(/[&+/]/g, " ");
59
+ return (connected
60
+ // everything else non-alphanumeric (keep spaces) is noise
61
+ .replaceAll(/[^a-z0-9\s]/g, " ")
62
+ .replaceAll(/\s+/g, " ")
63
+ .trim());
64
+ }
65
+ /**
66
+ * Normalize a single token for matching: {@linkcode foldForKey} with connective punctuation flattened to spaces (so
67
+ * `"A&B"` → `"a b"`, not `"ab"`). Deterministic and reversible-free — the same input always yields the same output.
68
+ */
69
+ export function normalizeAddressToken(input) {
70
+ return foldForKey(input, { ampersand: "space" });
71
+ }
72
+ /**
73
+ * Derive the canonical match key from an address component dict: each present, address-identifying field normalized via
74
+ * {@linkcode normalizeAddressToken}, in fixed order, joined by the separator. Empty / whitespace-only fields are
75
+ * skipped. Returns an empty string if nothing identifying remains.
76
+ */
77
+ export function canonicalKey(components, opts = {}) {
78
+ const separator = opts.separator ?? "|";
79
+ const parts = [];
80
+ for (const tag of KEY_FIELD_ORDER) {
81
+ const value = components[tag];
82
+ if (!value)
83
+ continue;
84
+ const normalized = normalizeAddressToken(value);
85
+ if (normalized) {
86
+ parts.push(normalized);
87
+ }
88
+ }
89
+ return parts.join(separator);
90
+ }
91
+ //# sourceMappingURL=key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key.js","sourceRoot":"","sources":["../../lib/address/key.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH;;;GAGG;AACH,MAAM,eAAe,GAAG;IACvB,QAAQ;IACR,cAAc;IACd,eAAe;IACf,wBAAwB;IACxB,QAAQ;IACR,eAAe;IACf,gBAAgB;IAChB,gBAAgB;IAChB,MAAM;IACN,oBAAoB;IACpB,UAAU;IACV,WAAW;IACX,QAAQ;IACR,UAAU;IACV,SAAS;CACkC,CAAA;AA+B5C;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,KAAa,EAAE,OAA0B;IACnE,MAAM,MAAM,GAAG,KAAK;SAClB,SAAS,CAAC,MAAM,CAAC;QAClB,iFAAiF;SAChF,UAAU,CAAC,kBAAkB,EAAE,EAAE,CAAC;SAClC,WAAW,EAAE;QACd,wFAAwF;SACvF,UAAU,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAA;IAE3D,MAAM,SAAS,GACd,OAAO,CAAC,SAAS,KAAK,KAAK;QAC1B,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC;QAC1D,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAA;IAEpC,OAAO,CACN,SAAS;QACR,0DAA0D;SACzD,UAAU,CAAC,cAAc,EAAE,GAAG,CAAC;SAC/B,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC;SACvB,IAAI,EAAE,CACR,CAAA;AACF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAa;IAClD,OAAO,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAA;AACjD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,UAAyB,EAAE,IAAI,GAAwB,EAAE;IACrF,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,GAAG,CAAA;IACvC,MAAM,KAAK,GAAa,EAAE,CAAA;IAE1B,KAAK,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAA;QAE7B,IAAI,CAAC,KAAK;YAAE,SAAQ;QACpB,MAAM,UAAU,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAA;QAE/C,IAAI,UAAU,EAAE,CAAC;YAChB,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACvB,CAAC;IACF,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;AAC7B,CAAC"}