@mailwoman/codex 9.4.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.
- package/README.md +221 -75
- package/lib/address/format.ts +144 -0
- package/lib/address/key.ts +134 -0
- package/lib/address/layout.ts +189 -0
- package/lib/address/layouts/generated.ts +1576 -0
- package/lib/address/layouts/index.ts +213 -0
- package/lib/address/render.ts +176 -0
- package/lib/{address-system-conventions.ts → address/system-conventions.ts} +6 -5
- package/lib/component.ts +96 -0
- package/lib/country/index.ts +3 -2
- package/lib/country/{reference.ts → reference/index.ts} +1 -1
- package/lib/country/region-languages.ts +46 -0
- package/lib/es/co-official-languages.ts +62 -0
- package/lib/es/index.ts +1 -0
- package/lib/gb/index.ts +2 -2
- package/lib/index.ts +4 -2
- package/lib/placetype-map.ts +157 -0
- package/lib/postcode/shapes.json +80 -0
- package/lib/postcode/shapes.ts +56 -0
- package/lib/{postcode-systems.ts → postcode/systems.ts} +8 -1
- package/lib/us/index.ts +2 -2
- package/lib/us/po-box.ts +37 -10
- package/lib/us/{street-directional.ts → street/directional.ts} +1 -1
- package/lib/us/{street-suffix.ts → street/suffix.ts} +1 -1
- package/out/address/format.d.ts +87 -0
- package/out/address/format.d.ts.map +1 -0
- package/out/address/format.js +85 -0
- package/out/address/format.js.map +1 -0
- package/out/address/key.d.ts +67 -0
- package/out/address/key.d.ts.map +1 -0
- package/out/address/key.js +91 -0
- package/out/address/key.js.map +1 -0
- package/out/address/layout.d.ts +104 -0
- package/out/address/layout.d.ts.map +1 -0
- package/out/address/layout.js +123 -0
- package/out/address/layout.js.map +1 -0
- package/out/address/layouts/generated.d.ts +21 -0
- package/out/address/layouts/generated.d.ts.map +1 -0
- package/out/address/layouts/generated.js +1380 -0
- package/out/address/layouts/generated.js.map +1 -0
- package/out/address/layouts/index.d.ts +81 -0
- package/out/address/layouts/index.d.ts.map +1 -0
- package/out/address/layouts/index.js +184 -0
- package/out/address/layouts/index.js.map +1 -0
- package/out/address/render.d.ts +64 -0
- package/out/address/render.d.ts.map +1 -0
- package/out/address/render.js +115 -0
- package/out/address/render.js.map +1 -0
- package/out/{address-system-conventions.d.ts → address/system-conventions.d.ts} +6 -5
- package/out/address/system-conventions.d.ts.map +1 -0
- package/out/{address-system-conventions.js → address/system-conventions.js} +2 -2
- package/out/address/system-conventions.js.map +1 -0
- package/out/au/delivery-service.d.ts.map +1 -1
- package/out/au/level-designator.d.ts.map +1 -1
- package/out/au/state.d.ts.map +1 -1
- package/out/ca/province.d.ts.map +1 -1
- package/out/component.d.ts +50 -0
- package/out/component.d.ts.map +1 -0
- package/out/component.js +84 -0
- package/out/component.js.map +1 -0
- package/out/country/codes.d.ts +2 -2
- package/out/country/codes.d.ts.map +1 -1
- package/out/country/display-names.d.ts.map +1 -1
- package/out/country/display-names.js.map +1 -1
- package/out/country/index.d.ts +3 -2
- package/out/country/index.d.ts.map +1 -1
- package/out/country/index.js +3 -2
- package/out/country/index.js.map +1 -1
- package/out/country/{reference-data.d.ts → reference/data.d.ts} +1 -1
- package/out/country/reference/data.d.ts.map +1 -0
- package/out/country/{reference-data.js → reference/data.js} +1 -1
- package/out/country/reference/data.js.map +1 -0
- package/out/country/{reference.d.ts → reference/index.d.ts} +1 -1
- package/out/country/reference/index.d.ts.map +1 -0
- package/out/country/{reference.js → reference/index.js} +2 -2
- package/out/country/reference/index.js.map +1 -0
- package/out/country/region-languages.d.ts +25 -0
- package/out/country/region-languages.d.ts.map +1 -0
- package/out/country/region-languages.js +41 -0
- package/out/country/region-languages.js.map +1 -0
- package/out/de/bundesland.d.ts.map +1 -1
- package/out/de/postleitzahl.d.ts.map +1 -1
- package/out/de/street-type.d.ts.map +1 -1
- package/out/es/co-official-languages.d.ts +37 -0
- package/out/es/co-official-languages.d.ts.map +1 -0
- package/out/es/co-official-languages.js +61 -0
- package/out/es/co-official-languages.js.map +1 -0
- package/out/es/index.d.ts +1 -0
- package/out/es/index.d.ts.map +1 -1
- package/out/es/index.js +1 -0
- package/out/es/index.js.map +1 -1
- package/out/fr/departement.d.ts.map +1 -1
- package/out/fr/region.d.ts.map +1 -1
- package/out/gb/country.d.ts.map +1 -1
- package/out/gb/index.d.ts +2 -2
- package/out/gb/index.d.ts.map +1 -1
- package/out/gb/index.js +2 -2
- package/out/gb/index.js.map +1 -1
- package/out/gb/{postcode-area.d.ts → postcode/area.d.ts} +1 -1
- package/out/gb/postcode/area.d.ts.map +1 -0
- package/out/gb/{postcode-area.js → postcode/area.js} +1 -1
- package/out/gb/postcode/area.js.map +1 -0
- package/out/gb/{postcode.d.ts → postcode/index.d.ts} +1 -1
- package/out/gb/postcode/index.d.ts.map +1 -0
- package/out/gb/{postcode.js → postcode/index.js} +1 -1
- package/out/gb/postcode/index.js.map +1 -0
- package/out/gb/street-type.d.ts.map +1 -1
- package/out/index.d.ts +4 -2
- package/out/index.d.ts.map +1 -1
- package/out/index.js +4 -2
- package/out/index.js.map +1 -1
- package/out/jp/address-unit.d.ts.map +1 -1
- package/out/jp/prefecture.d.ts.map +1 -1
- package/out/level-semantics.d.ts.map +1 -1
- package/out/level-semantics.js.map +1 -1
- package/out/nz/delivery-service.d.ts.map +1 -1
- package/out/placetype-map.d.ts +83 -0
- package/out/placetype-map.d.ts.map +1 -0
- package/out/placetype-map.js +130 -0
- package/out/placetype-map.js.map +1 -0
- package/out/postcode/shapes.d.ts +47 -0
- package/out/postcode/shapes.d.ts.map +1 -0
- package/out/postcode/shapes.js +38 -0
- package/out/postcode/shapes.js.map +1 -0
- package/out/postcode/shapes.json +80 -0
- package/out/{postcode-systems.d.ts → postcode/systems.d.ts} +8 -1
- package/out/postcode/systems.d.ts.map +1 -0
- package/out/{postcode-systems.js → postcode/systems.js} +9 -2
- package/out/postcode/systems.js.map +1 -0
- package/out/us/floor-designator.d.ts.map +1 -1
- package/out/us/index.d.ts +2 -2
- package/out/us/index.js +2 -2
- package/out/us/military-address.d.ts.map +1 -1
- package/out/us/po-box.d.ts +6 -0
- package/out/us/po-box.d.ts.map +1 -1
- package/out/us/po-box.js +35 -10
- package/out/us/po-box.js.map +1 -1
- package/out/us/state.d.ts.map +1 -1
- package/out/us/{street-directional.d.ts → street/directional.d.ts} +1 -1
- package/out/us/street/directional.d.ts.map +1 -0
- package/out/us/{street-directional.js → street/directional.js} +2 -2
- package/out/us/street/directional.js.map +1 -0
- package/out/us/{street-suffix.d.ts → street/suffix.d.ts} +1 -1
- package/out/us/street/suffix.d.ts.map +1 -0
- package/out/us/{street-suffix.js → street/suffix.js} +2 -2
- package/out/us/street/suffix.js.map +1 -0
- package/out/us/zipcode.d.ts +1 -1
- package/out/us/zipcode.d.ts.map +1 -1
- package/package.json +110 -30
- package/lib/tools/build-country-surface-lexicon.ts +0 -177
- package/lib/tools/export-country-surfaces.ts +0 -48
- package/lib/tools/generate-country-population.ts +0 -142
- package/lib/tools/generate-country-reference.ts +0 -168
- package/lib/tools/generate-official-languages.ts +0 -210
- package/lib/tools/index.ts +0 -12
- package/out/address-system-conventions.d.ts.map +0 -1
- package/out/address-system-conventions.js.map +0 -1
- package/out/country/reference-data.d.ts.map +0 -1
- package/out/country/reference-data.js.map +0 -1
- package/out/country/reference.d.ts.map +0 -1
- package/out/country/reference.js.map +0 -1
- package/out/gb/postcode-area.d.ts.map +0 -1
- package/out/gb/postcode-area.js.map +0 -1
- package/out/gb/postcode.d.ts.map +0 -1
- package/out/gb/postcode.js.map +0 -1
- package/out/postcode-systems.d.ts.map +0 -1
- package/out/postcode-systems.js.map +0 -1
- package/out/tools/build-country-surface-lexicon.d.ts +0 -43
- package/out/tools/build-country-surface-lexicon.d.ts.map +0 -1
- package/out/tools/build-country-surface-lexicon.js +0 -149
- package/out/tools/build-country-surface-lexicon.js.map +0 -1
- package/out/tools/export-country-surfaces.d.ts +0 -15
- package/out/tools/export-country-surfaces.d.ts.map +0 -1
- package/out/tools/export-country-surfaces.js +0 -34
- package/out/tools/export-country-surfaces.js.map +0 -1
- package/out/tools/generate-country-population.d.ts +0 -42
- package/out/tools/generate-country-population.d.ts.map +0 -1
- package/out/tools/generate-country-population.js +0 -106
- package/out/tools/generate-country-population.js.map +0 -1
- package/out/tools/generate-country-reference.d.ts +0 -36
- package/out/tools/generate-country-reference.d.ts.map +0 -1
- package/out/tools/generate-country-reference.js +0 -107
- package/out/tools/generate-country-reference.js.map +0 -1
- package/out/tools/generate-official-languages.d.ts +0 -50
- package/out/tools/generate-official-languages.d.ts.map +0 -1
- package/out/tools/generate-official-languages.js +0 -149
- package/out/tools/generate-official-languages.js.map +0 -1
- package/out/tools/index.d.ts +0 -12
- package/out/tools/index.d.ts.map +0 -1
- package/out/tools/index.js +0 -12
- package/out/tools/index.js.map +0 -1
- package/out/us/street-directional.d.ts.map +0 -1
- package/out/us/street-directional.js.map +0 -1
- package/out/us/street-suffix.d.ts.map +0 -1
- package/out/us/street-suffix.js.map +0 -1
- /package/lib/country/{reference-data.ts → reference/data.ts} +0 -0
- /package/lib/gb/{postcode-area.ts → postcode/area.ts} +0 -0
- /package/lib/gb/{postcode.ts → postcode/index.ts} +0 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* Per-address-system LAYOUTS: the order a system prints its components in, as data.
|
|
7
|
+
*
|
|
8
|
+
* A layout is written as a tagged template that reads in the order it prints, so reviewing one means looking at the
|
|
9
|
+
* shape of the address it produces rather than at a nested function call. The interpolations are slots; the literal
|
|
10
|
+
* text between them are connectors.
|
|
11
|
+
*
|
|
12
|
+
* ONE RULE governs rendering, and it replaces four mechanisms that each did part of the job elsewhere — the
|
|
13
|
+
* `filter(isPresent).join(…)` written by hand in 28 files, a pass that stripped a connector a template wrote between
|
|
14
|
+
* two slots when one was empty, a pass that spliced a missing line back in, and a chain of `if (x) parts.push(x)`:
|
|
15
|
+
*
|
|
16
|
+
* > A node that renders nothing removes itself, and its connector goes with it.
|
|
17
|
+
*
|
|
18
|
+
* A connector between two slots renders when a slot rendered on each side of it. A connector at the edge of a line
|
|
19
|
+
* has only one side, so it binds to the single slot it touches — which is how Japan's postal mark 〒 disappears with
|
|
20
|
+
* an absent postcode while an interior space does not.
|
|
21
|
+
*
|
|
22
|
+
* WHY A TEMPLATE RATHER THAN NESTED CALLS. The order has to be readable by someone checking whether a country is
|
|
23
|
+
* right, and the check is "does this look like an address from there". A nested `seq(", ", locality, seq(" ", region,
|
|
24
|
+
* postcode))` encodes the same thing and reads like a parser. The template also needs no nesting for the common case:
|
|
25
|
+
* treating every separator as a connector makes each line flat, and the four outcomes of a partially-filled tail
|
|
26
|
+
* (`New York, NY 10118` / `New York, NY` / `New York, 10118` / `NY 10118`) fall out of the one rule.
|
|
27
|
+
*
|
|
28
|
+
* The RENDERER is the sibling `render.ts`, and the public surface over it is `format.ts`. They are separate modules
|
|
29
|
+
* rather than one so a consumer that only wants the table — a conformance check, a documentation build — imports
|
|
30
|
+
* `#address/layouts` and loads no evaluator.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { COMPONENT_TAGS, type ComponentTag } from "#component"
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A slot: one component tag's value, or nothing when the dict has no value for it.
|
|
37
|
+
*/
|
|
38
|
+
export interface AddressSlot {
|
|
39
|
+
readonly tag: ComponentTag
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* A connector: literal text that renders only between neighbours that rendered. Written as the text BETWEEN
|
|
44
|
+
* interpolations, never constructed by hand.
|
|
45
|
+
*/
|
|
46
|
+
export interface AddressConnector {
|
|
47
|
+
readonly connector: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* First alternative that renders. The street line needs it and nothing else does: an intersection prints as `<a> & <b>`
|
|
52
|
+
* where a street prints as its own family of tags.
|
|
53
|
+
*/
|
|
54
|
+
export interface AddressAlternation {
|
|
55
|
+
readonly alternatives: readonly AddressLayout[]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type AddressAtom = AddressSlot | AddressConnector | AddressAlternation | AddressLayout
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* A layout: lines of atoms, in print order. A line break in the template starts a new line; how lines are joined for
|
|
62
|
+
* single-line output is the caller's choice, and per-system for the systems that join on nothing.
|
|
63
|
+
*/
|
|
64
|
+
export interface AddressLayout {
|
|
65
|
+
readonly lines: ReadonlyArray<readonly AddressAtom[]>
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function isSlot(atom: AddressAtom): atom is AddressSlot {
|
|
69
|
+
return "tag" in atom
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function isConnector(atom: AddressAtom): atom is AddressConnector {
|
|
73
|
+
return "connector" in atom
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function isAlternation(atom: AddressAtom): atom is AddressAlternation {
|
|
77
|
+
return "alternatives" in atom
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function isLayout(atom: AddressAtom): atom is AddressLayout {
|
|
81
|
+
return "lines" in atom
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Every {@linkcode ComponentTag} as a slot, so a layout names a tag by destructuring rather than by quoting it. A
|
|
86
|
+
* misspelled slot is then an unresolved identifier at compile time, and the spelling stays the tag's own —
|
|
87
|
+
* `dependent_locality`, never a parallel camelCase vocabulary.
|
|
88
|
+
*/
|
|
89
|
+
export const SLOTS: Readonly<Record<ComponentTag, AddressSlot>> = Object.freeze(
|
|
90
|
+
Object.fromEntries(COMPONENT_TAGS.map((tag) => [tag, Object.freeze({ tag })])) as Record<ComponentTag, AddressSlot>
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The first alternative that renders wins. Used for the street line, where an intersection and a street name are two
|
|
95
|
+
* ways of saying where, not two things to print.
|
|
96
|
+
*/
|
|
97
|
+
export function either(...alternatives: readonly AddressLayout[]): AddressAlternation {
|
|
98
|
+
return { alternatives }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* A post-office box takes a line of its own directly above the street line, and coexists with one: a record may carry
|
|
103
|
+
* both a box and a street address, and printing the box alone would lose the half a courier needs.
|
|
104
|
+
*
|
|
105
|
+
* That placement is measured, not assumed. The engine this table replaces rendered `P.O. Box 5` + `100 Main St` +
|
|
106
|
+
* `Portland, OR 97214` as three lines in that order, and the same shape for Germany, Australia and Great Britain.
|
|
107
|
+
* libaddressinput models no box at all, which is why the slot is authored here rather than transcribed.
|
|
108
|
+
*/
|
|
109
|
+
const poBoxLine = SLOTS.po_box
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The street line where the number leads: the anglophone order, and France's.
|
|
113
|
+
*
|
|
114
|
+
* An intersection is an ALTERNATIVE to the street name because it is a different way of saying where, not a second
|
|
115
|
+
* thing to print — the shape the old `composeRoad` drew in its own docstring before hand-compiling it into a chain of
|
|
116
|
+
* `if` statements. The box is not an alternative, so it sits outside the choice.
|
|
117
|
+
*/
|
|
118
|
+
export const numberFirstStreet: AddressLayout = addr`${poBoxLine}
|
|
119
|
+
${either(
|
|
120
|
+
addr`${SLOTS.intersection_a} & ${SLOTS.intersection_b}`,
|
|
121
|
+
addr`${SLOTS.house_number} ${SLOTS.street_prefix} ${SLOTS.street_prefix_particle} ${SLOTS.street} ${SLOTS.street_suffix} ${SLOTS.unit}`
|
|
122
|
+
)}`
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* The number-first line where a comma separates the number from the name — India's order.
|
|
126
|
+
*/
|
|
127
|
+
export const numberFirstCommaStreet: AddressLayout = addr`${poBoxLine}
|
|
128
|
+
${either(
|
|
129
|
+
addr`${SLOTS.intersection_a} & ${SLOTS.intersection_b}`,
|
|
130
|
+
addr`${SLOTS.house_number}, ${SLOTS.street_prefix} ${SLOTS.street_prefix_particle} ${SLOTS.street} ${SLOTS.street_suffix} ${SLOTS.unit}`
|
|
131
|
+
)}`
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The street line where the number follows the name: the German-order systems, and Italy.
|
|
135
|
+
*/
|
|
136
|
+
export const numberLastStreet: AddressLayout = addr`${poBoxLine}
|
|
137
|
+
${either(
|
|
138
|
+
addr`${SLOTS.intersection_a} & ${SLOTS.intersection_b}`,
|
|
139
|
+
addr`${SLOTS.street_prefix} ${SLOTS.street_prefix_particle} ${SLOTS.street} ${SLOTS.street_suffix} ${SLOTS.house_number} ${SLOTS.unit}`
|
|
140
|
+
)}`
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The number-last line where a comma separates the name from the number — `Calle Mayor, 12`, and Brazil's order.
|
|
144
|
+
*
|
|
145
|
+
* The separator is not cosmetic. Spain's corpus recipe renders both this form and the space form on purpose, because
|
|
146
|
+
* both occur in what a person types; collapsing one into the other would remove half the signal.
|
|
147
|
+
*/
|
|
148
|
+
export const numberLastCommaStreet: AddressLayout = addr`${poBoxLine}
|
|
149
|
+
${either(
|
|
150
|
+
addr`${SLOTS.intersection_a} & ${SLOTS.intersection_b}`,
|
|
151
|
+
addr`${SLOTS.street_prefix} ${SLOTS.street_prefix_particle} ${SLOTS.street} ${SLOTS.street_suffix}, ${SLOTS.house_number} ${SLOTS.unit}`
|
|
152
|
+
)}`
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Build a layout from a tagged template. A newline in the literal text starts a line; other literal text is a
|
|
156
|
+
* connector; an interpolation is a slot, an alternation, or another layout.
|
|
157
|
+
*/
|
|
158
|
+
export function addr(strings: TemplateStringsArray, ...values: readonly AddressAtom[]): AddressLayout {
|
|
159
|
+
const lines: AddressAtom[][] = [[]]
|
|
160
|
+
|
|
161
|
+
const push = (atom: AddressAtom): void => {
|
|
162
|
+
lines.at(-1)!.push(atom)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
for (const [index, raw] of strings.entries()) {
|
|
166
|
+
// A template's literal segment is bounded by the source text that spells it — one address line at most, and a
|
|
167
|
+
// layout cannot grow at runtime.
|
|
168
|
+
// oxlint-disable-next-line mailwoman/prefer-spliterator -- bounded by the source text, see above
|
|
169
|
+
const segments = raw.split("\n")
|
|
170
|
+
|
|
171
|
+
for (const [segmentIndex, segment] of segments.entries()) {
|
|
172
|
+
if (segmentIndex > 0) {
|
|
173
|
+
lines.push([])
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (segment) {
|
|
177
|
+
push({ connector: segment })
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const value = values[index]
|
|
182
|
+
|
|
183
|
+
if (value) {
|
|
184
|
+
push(value)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return { lines: lines.filter((line) => line.length > 0) }
|
|
189
|
+
}
|