@mailwoman/codex 7.2.1 → 7.3.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/country/index.ts CHANGED
@@ -12,3 +12,4 @@ export * from "./names.ts"
12
12
  export * from "./official-languages.ts"
13
13
  export * from "./reference-data.ts"
14
14
  export * from "./reference.ts"
15
+ export * from "./subdivision.ts"
@@ -0,0 +1,93 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * ISO 3166-2 subdivision → country reference, the cross-country complement to `country.ts`'s
7
+ * ISO 3166-1 `matchCountry`. A subdivision token ("QC", "Ontario", "Illinois") names a first-level
8
+ * admin unit whose COUNTRY is the piece the resolver needs when a region qualifier is the only
9
+ * signal that the locale-inferred default country is wrong ("Montreal QC" under a US locale).
10
+ *
11
+ * Scope is deliberately minimal: the two subdivision systems whose two-letter codes people write
12
+ * ON THE ADDRESS LINE and whose homonymous localities collide across the border — US states
13
+ * (`us/state.ts`) and Canadian provinces (`ca/province.ts`). Both directions are covered: the ISO
14
+ * code (`QC` → Quebec) and the full name (`Quebec` / `Québec` → the `QC` record), so a resolver
15
+ * can expand the abbreviation the gazetteer FTS index lacks ("QC" is not an alt-name of Québec)
16
+ * into the full name it does carry. This is a soft prior, not a routing decision — the gazetteer
17
+ * still does the geographic confirmation (per the registry-backed-soft-prior doctrine).
18
+ *
19
+ * The US and Canadian code sets are disjoint (no two-letter code, and no full name, collides
20
+ * between the two), so the combined lookup below is unambiguous. `CA` resolves to California the
21
+ * US state (Canada's provinces carry no `CA` subdivision code), never to Canada the country —
22
+ * country recognition stays with `matchCountry`.
23
+ *
24
+ * Source: the underlying `US_STATE_BY_ABBREVIATION` (USPS Publication 28, Appendix B) and
25
+ * `CA_PROVINCES` (ISO 3166-2:CA) tables. No new provenance is introduced here — this module only
26
+ * re-keys those two existing tables into one subdivision→country view.
27
+ */
28
+
29
+ import { CA_PROVINCES } from "../ca/province.ts"
30
+ import { US_STATE_BY_ABBREVIATION } from "../us/state.ts"
31
+
32
+ /** A resolved subdivision: its ISO 3166-2 code (sans country prefix), canonical English name, and ISO 3166-1 country. */
33
+ export interface SubdivisionMatch {
34
+ /** ISO 3166-2 subdivision code without the country prefix (e.g. `QC` for `CA-QC`, `IL` for `US-IL`). */
35
+ code: string
36
+ /** Canonical English name (e.g. `Quebec`, `Illinois`). */
37
+ name: string
38
+ /** ISO 3166-1 alpha-2 country the subdivision belongs to (`CA`, `US`). */
39
+ country: string
40
+ }
41
+
42
+ /** Strip diacritics + lowercase so `Québec`, `Quebec`, and `quebec` all key alike (mirrors `ca/province.ts`). */
43
+ function foldName(s: string): string {
44
+ return s
45
+ .toLowerCase()
46
+ .normalize("NFD")
47
+ .replace(/[\u0300-\u036f]/g, "")
48
+ .replace(/[^a-z0-9]+/g, " ")
49
+ .trim()
50
+ }
51
+
52
+ /**
53
+ * Folded surface form (ISO code, English name, or — for CA — co-official French name) → subdivision. Built once. US
54
+ * states are inserted first and never overwritten, so on the (currently empty) event of a future code/name collision
55
+ * the US entry wins deterministically; today the two sets are disjoint.
56
+ */
57
+ const SUBDIVISION_LOOKUP: ReadonlyMap<string, SubdivisionMatch> = (() => {
58
+ const out = new Map<string, SubdivisionMatch>()
59
+ const put = (key: string, match: SubdivisionMatch): void => {
60
+ const folded = foldName(key)
61
+
62
+ if (folded.length > 0 && !out.has(folded)) {
63
+ out.set(folded, match)
64
+ }
65
+ }
66
+
67
+ for (const [code, name] of Object.entries(US_STATE_BY_ABBREVIATION)) {
68
+ const match: SubdivisionMatch = { code, name, country: "US" }
69
+ put(code, match)
70
+ put(name, match)
71
+ }
72
+
73
+ for (const info of Object.values(CA_PROVINCES)) {
74
+ const match: SubdivisionMatch = { code: info.code, name: info.name, country: "CA" }
75
+ put(info.code, match)
76
+ put(info.name, match)
77
+ put(info.french, match)
78
+ }
79
+
80
+ return out
81
+ })()
82
+
83
+ /**
84
+ * Resolve a first-level subdivision surface form (ISO 3166-2 code, English name, or co-official French name for CA;
85
+ * accents optional) to its `{ code, name, country }`. Case- and diacritic-insensitive. Returns null for anything that
86
+ * isn't a US state or Canadian province/territory — including bare country tokens (use {@link matchCountry} for
87
+ * those).
88
+ */
89
+ export function matchSubdivision(token: string | null | undefined): SubdivisionMatch | null {
90
+ if (!token || typeof token !== "string") return null
91
+
92
+ return SUBDIVISION_LOOKUP.get(foldName(token)) ?? null
93
+ }
@@ -11,4 +11,5 @@ export * from "./names.ts";
11
11
  export * from "./official-languages.ts";
12
12
  export * from "./reference-data.ts";
13
13
  export * from "./reference.ts";
14
+ export * from "./subdivision.ts";
14
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../country/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,yBAAyB,CAAA;AACvC,cAAc,qBAAqB,CAAA;AACnC,cAAc,gBAAgB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../country/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,yBAAyB,CAAA;AACvC,cAAc,qBAAqB,CAAA;AACnC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA"}
@@ -11,4 +11,5 @@ export * from "./names.js";
11
11
  export * from "./official-languages.js";
12
12
  export * from "./reference-data.js";
13
13
  export * from "./reference.js";
14
+ export * from "./subdivision.js";
14
15
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../country/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,yBAAyB,CAAA;AACvC,cAAc,qBAAqB,CAAA;AACnC,cAAc,gBAAgB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../country/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,yBAAyB,CAAA;AACvC,cAAc,qBAAqB,CAAA;AACnC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * ISO 3166-2 subdivision → country reference, the cross-country complement to `country.ts`'s
7
+ * ISO 3166-1 `matchCountry`. A subdivision token ("QC", "Ontario", "Illinois") names a first-level
8
+ * admin unit whose COUNTRY is the piece the resolver needs when a region qualifier is the only
9
+ * signal that the locale-inferred default country is wrong ("Montreal QC" under a US locale).
10
+ *
11
+ * Scope is deliberately minimal: the two subdivision systems whose two-letter codes people write
12
+ * ON THE ADDRESS LINE and whose homonymous localities collide across the border — US states
13
+ * (`us/state.ts`) and Canadian provinces (`ca/province.ts`). Both directions are covered: the ISO
14
+ * code (`QC` → Quebec) and the full name (`Quebec` / `Québec` → the `QC` record), so a resolver
15
+ * can expand the abbreviation the gazetteer FTS index lacks ("QC" is not an alt-name of Québec)
16
+ * into the full name it does carry. This is a soft prior, not a routing decision — the gazetteer
17
+ * still does the geographic confirmation (per the registry-backed-soft-prior doctrine).
18
+ *
19
+ * The US and Canadian code sets are disjoint (no two-letter code, and no full name, collides
20
+ * between the two), so the combined lookup below is unambiguous. `CA` resolves to California the
21
+ * US state (Canada's provinces carry no `CA` subdivision code), never to Canada the country —
22
+ * country recognition stays with `matchCountry`.
23
+ *
24
+ * Source: the underlying `US_STATE_BY_ABBREVIATION` (USPS Publication 28, Appendix B) and
25
+ * `CA_PROVINCES` (ISO 3166-2:CA) tables. No new provenance is introduced here — this module only
26
+ * re-keys those two existing tables into one subdivision→country view.
27
+ */
28
+ /** A resolved subdivision: its ISO 3166-2 code (sans country prefix), canonical English name, and ISO 3166-1 country. */
29
+ export interface SubdivisionMatch {
30
+ /** ISO 3166-2 subdivision code without the country prefix (e.g. `QC` for `CA-QC`, `IL` for `US-IL`). */
31
+ code: string;
32
+ /** Canonical English name (e.g. `Quebec`, `Illinois`). */
33
+ name: string;
34
+ /** ISO 3166-1 alpha-2 country the subdivision belongs to (`CA`, `US`). */
35
+ country: string;
36
+ }
37
+ /**
38
+ * Resolve a first-level subdivision surface form (ISO 3166-2 code, English name, or co-official French name for CA;
39
+ * accents optional) to its `{ code, name, country }`. Case- and diacritic-insensitive. Returns null for anything that
40
+ * isn't a US state or Canadian province/territory — including bare country tokens (use {@link matchCountry} for
41
+ * those).
42
+ */
43
+ export declare function matchSubdivision(token: string | null | undefined): SubdivisionMatch | null;
44
+ //# sourceMappingURL=subdivision.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subdivision.d.ts","sourceRoot":"","sources":["../../country/subdivision.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAKH,yHAAyH;AACzH,MAAM,WAAW,gBAAgB;IAChC,wGAAwG;IACxG,IAAI,EAAE,MAAM,CAAA;IACZ,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAA;IACZ,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAA;CACf;AA2CD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,gBAAgB,GAAG,IAAI,CAI1F"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * ISO 3166-2 subdivision → country reference, the cross-country complement to `country.ts`'s
7
+ * ISO 3166-1 `matchCountry`. A subdivision token ("QC", "Ontario", "Illinois") names a first-level
8
+ * admin unit whose COUNTRY is the piece the resolver needs when a region qualifier is the only
9
+ * signal that the locale-inferred default country is wrong ("Montreal QC" under a US locale).
10
+ *
11
+ * Scope is deliberately minimal: the two subdivision systems whose two-letter codes people write
12
+ * ON THE ADDRESS LINE and whose homonymous localities collide across the border — US states
13
+ * (`us/state.ts`) and Canadian provinces (`ca/province.ts`). Both directions are covered: the ISO
14
+ * code (`QC` → Quebec) and the full name (`Quebec` / `Québec` → the `QC` record), so a resolver
15
+ * can expand the abbreviation the gazetteer FTS index lacks ("QC" is not an alt-name of Québec)
16
+ * into the full name it does carry. This is a soft prior, not a routing decision — the gazetteer
17
+ * still does the geographic confirmation (per the registry-backed-soft-prior doctrine).
18
+ *
19
+ * The US and Canadian code sets are disjoint (no two-letter code, and no full name, collides
20
+ * between the two), so the combined lookup below is unambiguous. `CA` resolves to California the
21
+ * US state (Canada's provinces carry no `CA` subdivision code), never to Canada the country —
22
+ * country recognition stays with `matchCountry`.
23
+ *
24
+ * Source: the underlying `US_STATE_BY_ABBREVIATION` (USPS Publication 28, Appendix B) and
25
+ * `CA_PROVINCES` (ISO 3166-2:CA) tables. No new provenance is introduced here — this module only
26
+ * re-keys those two existing tables into one subdivision→country view.
27
+ */
28
+ import { CA_PROVINCES } from "../ca/province.js";
29
+ import { US_STATE_BY_ABBREVIATION } from "../us/state.js";
30
+ /** Strip diacritics + lowercase so `Québec`, `Quebec`, and `quebec` all key alike (mirrors `ca/province.ts`). */
31
+ function foldName(s) {
32
+ return s
33
+ .toLowerCase()
34
+ .normalize("NFD")
35
+ .replace(/[\u0300-\u036f]/g, "")
36
+ .replace(/[^a-z0-9]+/g, " ")
37
+ .trim();
38
+ }
39
+ /**
40
+ * Folded surface form (ISO code, English name, or — for CA — co-official French name) → subdivision. Built once. US
41
+ * states are inserted first and never overwritten, so on the (currently empty) event of a future code/name collision
42
+ * the US entry wins deterministically; today the two sets are disjoint.
43
+ */
44
+ const SUBDIVISION_LOOKUP = (() => {
45
+ const out = new Map();
46
+ const put = (key, match) => {
47
+ const folded = foldName(key);
48
+ if (folded.length > 0 && !out.has(folded)) {
49
+ out.set(folded, match);
50
+ }
51
+ };
52
+ for (const [code, name] of Object.entries(US_STATE_BY_ABBREVIATION)) {
53
+ const match = { code, name, country: "US" };
54
+ put(code, match);
55
+ put(name, match);
56
+ }
57
+ for (const info of Object.values(CA_PROVINCES)) {
58
+ const match = { code: info.code, name: info.name, country: "CA" };
59
+ put(info.code, match);
60
+ put(info.name, match);
61
+ put(info.french, match);
62
+ }
63
+ return out;
64
+ })();
65
+ /**
66
+ * Resolve a first-level subdivision surface form (ISO 3166-2 code, English name, or co-official French name for CA;
67
+ * accents optional) to its `{ code, name, country }`. Case- and diacritic-insensitive. Returns null for anything that
68
+ * isn't a US state or Canadian province/territory — including bare country tokens (use {@link matchCountry} for
69
+ * those).
70
+ */
71
+ export function matchSubdivision(token) {
72
+ if (!token || typeof token !== "string")
73
+ return null;
74
+ return SUBDIVISION_LOOKUP.get(foldName(token)) ?? null;
75
+ }
76
+ //# sourceMappingURL=subdivision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subdivision.js","sourceRoot":"","sources":["../../country/subdivision.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChD,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA;AAYzD,iHAAiH;AACjH,SAAS,QAAQ,CAAC,CAAS;IAC1B,OAAO,CAAC;SACN,WAAW,EAAE;SACb,SAAS,CAAC,KAAK,CAAC;SAChB,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;SAC/B,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,IAAI,EAAE,CAAA;AACT,CAAC;AAED;;;;GAIG;AACH,MAAM,kBAAkB,GAA0C,CAAC,GAAG,EAAE;IACvE,MAAM,GAAG,GAAG,IAAI,GAAG,EAA4B,CAAA;IAC/C,MAAM,GAAG,GAAG,CAAC,GAAW,EAAE,KAAuB,EAAQ,EAAE;QAC1D,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAA;QAE5B,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3C,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;QACvB,CAAC;IACF,CAAC,CAAA;IAED,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,wBAAwB,CAAC,EAAE,CAAC;QACrE,MAAM,KAAK,GAAqB,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QAC7D,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;QAChB,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACjB,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC;QAChD,MAAM,KAAK,GAAqB,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QACnF,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;QACrB,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;QACrB,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IACxB,CAAC;IAED,OAAO,GAAG,CAAA;AACX,CAAC,CAAC,EAAE,CAAA;AAEJ;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAgC;IAChE,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAEpD,OAAO,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,IAAI,CAAA;AACvD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mailwoman/codex",
3
- "version": "7.2.1",
3
+ "version": "7.3.0",
4
4
  "description": "Per-address-system postal reference data + branded types (USPS street suffixes, US ZIP codes). Pure, zero-runtime-dep — the shared canonical home for postal-system primitives the parser, resolver, and synthesis layers all reach for.",
5
5
  "license": "AGPL-3.0-only OR LicenseRef-Commercial",
6
6
  "repository": {
@@ -124,6 +124,6 @@
124
124
  "type-fest": "^5.8.0"
125
125
  },
126
126
  "devDependencies": {
127
- "@mailwoman/annotations": "7.2.1"
127
+ "@mailwoman/annotations": "7.3.0"
128
128
  }
129
129
  }