@mailwoman/corpus 4.9.0 → 4.10.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/out/src/format.d.ts +6 -71
- package/out/src/format.d.ts.map +1 -1
- package/out/src/format.js +6 -143
- package/out/src/format.js.map +1 -1
- package/package.json +4 -4
package/out/src/format.d.ts
CHANGED
|
@@ -3,77 +3,12 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* Thin re-export of `@mailwoman/formatter`.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* `@fragaria/address-formatter` returns multi-line strings (newline-separated). This module honors
|
|
14
|
-
* that by default and exposes a `separator` option to coerce to single-line when an adapter wants
|
|
15
|
-
* `", "` joined output for the corpus.
|
|
16
|
-
*
|
|
17
|
-
* Limitations (documented, not blockers for Phase 1):
|
|
18
|
-
*
|
|
19
|
-
* - `unit`: OpenCage's vocabulary doesn't have a slot, so units are appended to the road line
|
|
20
|
-
* (`"Pennsylvania Ave NW Apt 4B"`). Phase 2 or beyond can change this if needed.
|
|
21
|
-
* - `intersection_a` / `intersection_b`: joined as `"<a> & <b>"` into the road field.
|
|
22
|
-
* - `cedex` (FR): folded into `postcode` (e.g. `"75008 CEDEX 08"`) so the FR template renders it in
|
|
23
|
-
* the right slot.
|
|
24
|
-
* - JP-specific tags (`prefecture`, `municipality`, ...): no mapping yet — left for Phase 6 when JP
|
|
25
|
-
* becomes a live locale.
|
|
26
|
-
*/
|
|
27
|
-
import type { ComponentTag } from "@mailwoman/core/types";
|
|
28
|
-
/** Options accepted by `formatAddress`. */
|
|
29
|
-
export interface FormatAddressOptions {
|
|
30
|
-
/**
|
|
31
|
-
* Append the country name as a final line (`"USA"`, `"France"`). Default `false`: most corpus
|
|
32
|
-
* rows are intra-country and the country prefix is redundant noise.
|
|
33
|
-
*/
|
|
34
|
-
appendCountry?: boolean;
|
|
35
|
-
/**
|
|
36
|
-
* Apply OpenCage's per-country abbreviation rules (e.g. `"Avenue"` → `"Ave"`, `"Boulevard"` →
|
|
37
|
-
* `"Blvd"`). Default `false`. Phase 1 prefers unabbreviated output because synthesis
|
|
38
|
-
* (`synthesize.ts`) handles abbreviation swaps as its own augmentation pass.
|
|
39
|
-
*/
|
|
40
|
-
abbreviate?: boolean;
|
|
41
|
-
/**
|
|
42
|
-
* Replace the template's newlines with this separator. Default `undefined` (keep newlines). Use
|
|
43
|
-
* `", "` to get a single-line output, or `" "` to remove all internal punctuation.
|
|
44
|
-
*/
|
|
45
|
-
separator?: string;
|
|
46
|
-
}
|
|
47
|
-
type ComponentDict = Partial<Record<ComponentTag, string>>;
|
|
48
|
-
/**
|
|
49
|
-
* After rendering, drop any component whose value isn't actually present in the formatted `raw`.
|
|
50
|
-
* OpenCage's per-country templates legitimately omit some inputs:
|
|
51
|
-
*
|
|
52
|
-
* - **FR**: regions are absorbed by the postcode and rarely render verbatim (`"75008 Paris, France"`
|
|
53
|
-
* — `"Île-de-France"` is dropped).
|
|
54
|
-
* - **US**: state names are abbreviated (`"Oregon"` → `"OR"`); the verbatim form may not survive.
|
|
55
|
-
*
|
|
56
|
-
* Alignment downstream requires `components[tag]` to occur in `raw` (within fuzzy-match tolerance).
|
|
57
|
-
* If a component was dropped or transformed beyond Levenshtein reach, keeping it in the dict
|
|
58
|
-
* guarantees a quarantine reject. Reconciliation prunes the dropouts up front so the row arrives at
|
|
59
|
-
* alignment with a self-consistent (`raw`, `components`) pair.
|
|
60
|
-
*
|
|
61
|
-
* Comparison is case- and whitespace-insensitive. The retained value in `components` is the
|
|
62
|
-
* **original** input — alignment will fuzzy-match it against the raw character span.
|
|
63
|
-
*/
|
|
64
|
-
export declare function reconcileComponents(components: ComponentDict, raw: string): ComponentDict;
|
|
65
|
-
/**
|
|
66
|
-
* Render a component dict into an idiomatic per-country address string.
|
|
67
|
-
*
|
|
68
|
-
* Returns an empty string if `components` is empty after translation. Throws nothing — bad inputs
|
|
69
|
-
* (empty dict, unsupported tag) silently degrade to the longest meaningful prefix.
|
|
70
|
-
*/
|
|
71
|
-
export declare function formatAddress(components: ComponentDict, country: string, opts?: FormatAddressOptions): string;
|
|
72
|
-
/**
|
|
73
|
-
* Translate a `ComponentTag` dict to the OpenCage vocabulary that `@fragaria/address-formatter`
|
|
74
|
-
* expects. Exported for testing and for adapters that want to pre-build the dict for batch
|
|
75
|
-
* formatting.
|
|
8
|
+
* The formatter implementation moved to its own workspace (`@mailwoman/formatter`) so the parser,
|
|
9
|
+
* the corpus pipeline, and the record matcher can share one locale-aware renderer plus the
|
|
10
|
+
* canonical match key. This module stays put as the stable `@mailwoman/corpus/format` entry point
|
|
11
|
+
* the synthesis adapters import `formatAddress` / `reconcileComponents` from.
|
|
76
12
|
*/
|
|
77
|
-
export
|
|
78
|
-
export {};
|
|
13
|
+
export * from "@mailwoman/formatter";
|
|
79
14
|
//# sourceMappingURL=format.d.ts.map
|
package/out/src/format.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/format.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,cAAc,sBAAsB,CAAA"}
|
package/out/src/format.js
CHANGED
|
@@ -3,149 +3,12 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* Thin re-export of `@mailwoman/formatter`.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* `@fragaria/address-formatter` returns multi-line strings (newline-separated). This module honors
|
|
14
|
-
* that by default and exposes a `separator` option to coerce to single-line when an adapter wants
|
|
15
|
-
* `", "` joined output for the corpus.
|
|
16
|
-
*
|
|
17
|
-
* Limitations (documented, not blockers for Phase 1):
|
|
18
|
-
*
|
|
19
|
-
* - `unit`: OpenCage's vocabulary doesn't have a slot, so units are appended to the road line
|
|
20
|
-
* (`"Pennsylvania Ave NW Apt 4B"`). Phase 2 or beyond can change this if needed.
|
|
21
|
-
* - `intersection_a` / `intersection_b`: joined as `"<a> & <b>"` into the road field.
|
|
22
|
-
* - `cedex` (FR): folded into `postcode` (e.g. `"75008 CEDEX 08"`) so the FR template renders it in
|
|
23
|
-
* the right slot.
|
|
24
|
-
* - JP-specific tags (`prefecture`, `municipality`, ...): no mapping yet — left for Phase 6 when JP
|
|
25
|
-
* becomes a live locale.
|
|
26
|
-
*/
|
|
27
|
-
import addressFormatter from "@fragaria/address-formatter";
|
|
28
|
-
/**
|
|
29
|
-
* After rendering, drop any component whose value isn't actually present in the formatted `raw`.
|
|
30
|
-
* OpenCage's per-country templates legitimately omit some inputs:
|
|
31
|
-
*
|
|
32
|
-
* - **FR**: regions are absorbed by the postcode and rarely render verbatim (`"75008 Paris, France"`
|
|
33
|
-
* — `"Île-de-France"` is dropped).
|
|
34
|
-
* - **US**: state names are abbreviated (`"Oregon"` → `"OR"`); the verbatim form may not survive.
|
|
35
|
-
*
|
|
36
|
-
* Alignment downstream requires `components[tag]` to occur in `raw` (within fuzzy-match tolerance).
|
|
37
|
-
* If a component was dropped or transformed beyond Levenshtein reach, keeping it in the dict
|
|
38
|
-
* guarantees a quarantine reject. Reconciliation prunes the dropouts up front so the row arrives at
|
|
39
|
-
* alignment with a self-consistent (`raw`, `components`) pair.
|
|
40
|
-
*
|
|
41
|
-
* Comparison is case- and whitespace-insensitive. The retained value in `components` is the
|
|
42
|
-
* **original** input — alignment will fuzzy-match it against the raw character span.
|
|
43
|
-
*/
|
|
44
|
-
export function reconcileComponents(components, raw) {
|
|
45
|
-
const haystack = raw.toLowerCase().replace(/\s+/g, " ");
|
|
46
|
-
const out = {};
|
|
47
|
-
for (const [k, v] of Object.entries(components)) {
|
|
48
|
-
if (!v)
|
|
49
|
-
continue;
|
|
50
|
-
const needle = v.toLowerCase().replace(/\s+/g, " ");
|
|
51
|
-
if (haystack.includes(needle)) {
|
|
52
|
-
out[k] = v;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return out;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Render a component dict into an idiomatic per-country address string.
|
|
59
|
-
*
|
|
60
|
-
* Returns an empty string if `components` is empty after translation. Throws nothing — bad inputs
|
|
61
|
-
* (empty dict, unsupported tag) silently degrade to the longest meaningful prefix.
|
|
62
|
-
*/
|
|
63
|
-
export function formatAddress(components, country, opts = {}) {
|
|
64
|
-
const ocComponents = toOpenCageComponents(components, country);
|
|
65
|
-
if (Object.keys(ocComponents).length === 0)
|
|
66
|
-
return "";
|
|
67
|
-
const raw = addressFormatter.format(ocComponents, {
|
|
68
|
-
abbreviate: opts.abbreviate ?? false,
|
|
69
|
-
appendCountry: opts.appendCountry ?? false,
|
|
70
|
-
});
|
|
71
|
-
const trimmed = raw.replace(/\s+$/g, "");
|
|
72
|
-
return opts.separator !== undefined ? trimmed.replace(/\n+/g, opts.separator) : trimmed;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Translate a `ComponentTag` dict to the OpenCage vocabulary that `@fragaria/address-formatter`
|
|
76
|
-
* expects. Exported for testing and for adapters that want to pre-build the dict for batch
|
|
77
|
-
* formatting.
|
|
78
|
-
*/
|
|
79
|
-
export function toOpenCageComponents(components, country) {
|
|
80
|
-
const out = {};
|
|
81
|
-
const road = composeRoad(components);
|
|
82
|
-
if (road)
|
|
83
|
-
out.road = road;
|
|
84
|
-
if (components.house_number)
|
|
85
|
-
out.house_number = components.house_number;
|
|
86
|
-
if (components.venue)
|
|
87
|
-
out.house = components.venue;
|
|
88
|
-
if (components.locality)
|
|
89
|
-
out.city = components.locality;
|
|
90
|
-
if (components.dependent_locality)
|
|
91
|
-
out.suburb = components.dependent_locality;
|
|
92
|
-
if (components.subregion)
|
|
93
|
-
out.county = components.subregion;
|
|
94
|
-
if (components.region)
|
|
95
|
-
out.state = components.region;
|
|
96
|
-
const postcode = composePostcode(components);
|
|
97
|
-
if (postcode)
|
|
98
|
-
out.postcode = postcode;
|
|
99
|
-
if (components.po_box)
|
|
100
|
-
out.po_box = components.po_box;
|
|
101
|
-
if (components.attention)
|
|
102
|
-
out.attention = components.attention;
|
|
103
|
-
if (components.country)
|
|
104
|
-
out.country = components.country;
|
|
105
|
-
// country_code drives template selection, not output. Only emit it if at least one other
|
|
106
|
-
// component is present — otherwise the FR/US templates render the bare code as a fallback
|
|
107
|
-
// line ("US"), which is never what a corpus consumer wants.
|
|
108
|
-
const cc = country.trim().toLowerCase();
|
|
109
|
-
if (cc && Object.keys(out).length > 0)
|
|
110
|
-
out.country_code = cc;
|
|
111
|
-
return out;
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Build the `road` line from prefix / particle / street / suffix / unit / intersection components.
|
|
115
|
-
* Order:
|
|
116
|
-
*
|
|
117
|
-
* ```
|
|
118
|
-
* [intersection_a & intersection_b]
|
|
119
|
-
* OR
|
|
120
|
-
* [street_prefix] [street_prefix_particle] [street] [street_suffix] [unit]
|
|
121
|
-
* ```
|
|
122
|
-
*/
|
|
123
|
-
function composeRoad(components) {
|
|
124
|
-
if (components.intersection_a && components.intersection_b) {
|
|
125
|
-
return `${components.intersection_a} & ${components.intersection_b}`;
|
|
126
|
-
}
|
|
127
|
-
const parts = [];
|
|
128
|
-
if (components.street_prefix)
|
|
129
|
-
parts.push(components.street_prefix);
|
|
130
|
-
if (components.street_prefix_particle)
|
|
131
|
-
parts.push(components.street_prefix_particle);
|
|
132
|
-
if (components.street)
|
|
133
|
-
parts.push(components.street);
|
|
134
|
-
if (components.street_suffix)
|
|
135
|
-
parts.push(components.street_suffix);
|
|
136
|
-
if (components.unit)
|
|
137
|
-
parts.push(components.unit);
|
|
138
|
-
return parts.join(" ").replace(/\s+/g, " ").trim();
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Fold CEDEX into postcode for FR-style output: `"75008"` + cedex `"CEDEX 08"` → `"75008 CEDEX
|
|
142
|
-
* 08"`. If only one is present, return it. If neither, return empty.
|
|
8
|
+
* The formatter implementation moved to its own workspace (`@mailwoman/formatter`) so the parser,
|
|
9
|
+
* the corpus pipeline, and the record matcher can share one locale-aware renderer plus the
|
|
10
|
+
* canonical match key. This module stays put as the stable `@mailwoman/corpus/format` entry point
|
|
11
|
+
* the synthesis adapters import `formatAddress` / `reconcileComponents` from.
|
|
143
12
|
*/
|
|
144
|
-
|
|
145
|
-
const base = components.postcode?.trim() ?? "";
|
|
146
|
-
const cedex = components.cedex?.trim() ?? "";
|
|
147
|
-
if (base && cedex)
|
|
148
|
-
return `${base} ${cedex}`.replace(/\s+/g, " ");
|
|
149
|
-
return base || cedex;
|
|
150
|
-
}
|
|
13
|
+
export * from "@mailwoman/formatter";
|
|
151
14
|
//# sourceMappingURL=format.js.map
|
package/out/src/format.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/format.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,cAAc,sBAAsB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mailwoman/corpus",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.10.0",
|
|
4
4
|
"description": "Mailwoman corpus pipeline: BIO-labeled dataset builder for the neural classifier.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"repository": {
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@dsnp/parquetjs": "1.8.7",
|
|
20
|
-
"@
|
|
21
|
-
"@mailwoman/
|
|
22
|
-
"@mailwoman/
|
|
20
|
+
"@mailwoman/codex": "4.10.0",
|
|
21
|
+
"@mailwoman/core": "4.10.0",
|
|
22
|
+
"@mailwoman/formatter": "4.10.0",
|
|
23
23
|
"csv-parse": "^5.6.0",
|
|
24
24
|
"fast-glob": "^3.3.3",
|
|
25
25
|
"fastest-levenshtein": "^1.0.16",
|