@mailwoman/kind-classifier 9.0.0 → 9.2.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.
@@ -0,0 +1,288 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * ROAD_TO_V9 §4 — the query-INTENT rules. Same contract as `rules.ts` (a `(input, shape) => number`
7
+ * in [0, 1], 0 when the rule does not fire) and the same bitter-lesson invariant: universal
8
+ * structural patterns and bounded linguistic categories only, never a place-name dictionary. The
9
+ * one lexicon these kinds consult — the POI synonym table — is INJECTED, exactly as `poi.ts`
10
+ * already does it.
11
+ *
12
+ * ## Why two of these three deliberately lose
13
+ *
14
+ * `bare_toponym` and `route_pair` are scored BELOW the structural kind that already owns their
15
+ * population (`locality_only`, 0.85). They therefore surface in `QueryKindResult.alternatives` and
16
+ * never as the top kind. That is not timidity — it is the D-rule discharge. The top kind is the
17
+ * only thing the coordinator routes on (`deriveInputMode`, `canShortCircuit`, the POI branch), so
18
+ * pinning it is what makes these additions provably answer-neutral on the bare-city-name register,
19
+ * which is the single largest population in map search. The intent they carry travels on the
20
+ * marker instead, where it is advisory by construction.
21
+ *
22
+ * `near_me` DOES win its top slot (0.91), because there is no incumbent worth preserving: a query
23
+ * ending "near me" is not a locality and answering it as one is the bug.
24
+ */
25
+ import { MAX_LOCALITY_ONLY_LENGTH, STREET_SUFFIXES } from "./rules.js";
26
+ /**
27
+ * `locality_only` scores 0.85. Both refinement kinds sit under it by a whole confidence step so no float-comparison
28
+ * accident can flip the top slot, and so the gap reads as deliberate to the next person.
29
+ */
30
+ const BARE_TOPONYM_CONFIDENCE = 0.84;
31
+ /**
32
+ * Lower still, and for a second reason on top of the ranking discipline: a route pair is a HYPOTHESIS about a query
33
+ * whose competing reading (locality + region) is more common in this corpus. The number states that.
34
+ */
35
+ const ROUTE_PAIR_CONFIDENCE = 0.55;
36
+ /**
37
+ * Above `landmark`'s venue ceiling (0.88) and above `poi_query`'s anchored band (0.90), because a deictic tail is a
38
+ * stronger signal than either shape heuristic: nothing else in the vocabulary explains why "me" is at the end of the
39
+ * string.
40
+ */
41
+ const NEAR_ME_CONFIDENCE = 0.91;
42
+ /**
43
+ * Word ceiling for a single bare toponym. Four covers the long tail that actually exists as one place name ("Newcastle
44
+ * upon Tyne", "Sault Sainte Marie", "Las Palmas de Gran Canaria"); past it the input is carrying more than a name.
45
+ */
46
+ const MAX_BARE_TOPONYM_WORDS = 4;
47
+ /**
48
+ * Toponymic HEAD particles — the bounded linguistic category that makes a multi-token string ONE place name.
49
+ *
50
+ * Same justification, and the same boundary, as `@mailwoman/phrase-grouper`'s `PLACE_NAME_PARTICLES` (which covers the
51
+ * INFIX glue: `de`, `am`, `aan den`). This set covers the PREFIX heads, and it exists for exactly one job: keeping
52
+ * `route_pair` off "New York", "San Francisco", "Fort Worth" and their kin. It is a closed morphological class, not a
53
+ * gazetteer — growing it with actual place names is the wrong move, and the pressure for that belongs on the resolver.
54
+ *
55
+ * Case-folded on read, because lowercase is the primary user register and "new york" is the same query.
56
+ */
57
+ const TOPONYM_HEAD_PARTICLES = new Set([
58
+ // English
59
+ "new",
60
+ "old",
61
+ "fort",
62
+ "ft",
63
+ "port",
64
+ "lake",
65
+ "mount",
66
+ "mt",
67
+ "north",
68
+ "south",
69
+ "east",
70
+ "west",
71
+ "upper",
72
+ "lower",
73
+ "great",
74
+ "little",
75
+ "saint",
76
+ "st",
77
+ "st.",
78
+ // Romance
79
+ "san",
80
+ "santa",
81
+ "santo",
82
+ "são",
83
+ "sao",
84
+ "los",
85
+ "las",
86
+ "el",
87
+ "la",
88
+ "le",
89
+ "les",
90
+ "villa",
91
+ "rio",
92
+ "nueva",
93
+ "nuevo",
94
+ "puerto",
95
+ "ciudad",
96
+ "campo",
97
+ "monte",
98
+ "castel",
99
+ "borgo",
100
+ // Germanic / Nordic
101
+ "bad",
102
+ "sankt",
103
+ "neu",
104
+ "alt",
105
+ "groß",
106
+ "gross",
107
+ "klein",
108
+ "ober",
109
+ "unter",
110
+ "nieuw",
111
+ "oud",
112
+ "ny",
113
+ "stor",
114
+ "lille",
115
+ "sint",
116
+ // Definite article as a head — "The Valley" (Anguilla), "The Hague", "The Bottom".
117
+ "the",
118
+ // Generic toponymic heads outside the Latin/Germanic families, added because the 306-case corpus MEASURED them
119
+ // (see `mailwoman/test/kind-intent-invariance.test.ts`): each is a common noun in its own language — Semitic "tel"
120
+ // (mound), Malay "kuala" (confluence), Khmer "phnom" (hill) — that heads a place name the way "mount" does.
121
+ "tel",
122
+ "kuala",
123
+ "phnom",
124
+ "cape",
125
+ "isle",
126
+ "isla",
127
+ "ilha",
128
+ ]);
129
+ /**
130
+ * Generic toponymic TAIL nouns — the other half of the same bounded morphological class. "Belize City", "George Town",
131
+ * "Cape Town", "Palm Springs": a place name whose last token is a settlement/landform generic is ONE name, not two.
132
+ *
133
+ * Measured additions, same as the heads above: `city`, `town` and `valley` each came off a real corpus row that was
134
+ * forking wrongly.
135
+ */
136
+ const TOPONYM_TAIL_NOUNS = new Set([
137
+ "city",
138
+ "town",
139
+ "ville",
140
+ "village",
141
+ "borough",
142
+ "springs",
143
+ "falls",
144
+ "beach",
145
+ "heights",
146
+ "valley",
147
+ "island",
148
+ "islands",
149
+ "bay",
150
+ "harbour",
151
+ "harbor",
152
+ "park",
153
+ "hills",
154
+ "river",
155
+ "creek",
156
+ "point",
157
+ "stadt",
158
+ "burg",
159
+ ]);
160
+ /**
161
+ * Deictic locator tails — "near me", "nearby", "around here", "in my area".
162
+ *
163
+ * The class is `preposition + a reference to the ASKER`, which is why it is bounded and why it is safe: `me`, `here`,
164
+ * `my <noun>` are function words, not places. Anchored to the END of the string (`$`) on purpose — the whole point of
165
+ * the kind is that the query names no anchor, so anything AFTER the locator is an anchor and disqualifies it.
166
+ *
167
+ * Linear by construction: every alternative begins with a required literal, and the only quantifiers are bounded `\s+`
168
+ * runs BETWEEN two required literals or trailing before `$`. No unbounded-whitespace-then-literal prefix, which is the
169
+ * `js/polynomial-redos` shape (see the `ANCHOR_SEPARATOR` docstring in `poi.ts` for the same analysis).
170
+ */
171
+ const DEICTIC_LOCATOR_TAIL = /\b(?:near|close\s+to|next\s+to|around|by|closest\s+to|nearest\s+to)\s+(?:me|us|here|my\s+(?:location|position|area|place|house|home))\s*$/;
172
+ /**
173
+ * The adverbial half of the same class — no preposition, the deixis is baked into the word.
174
+ */
175
+ const DEICTIC_ADVERB_TAIL = /\b(?:nearby|near\s?by|close\s+by|around\s+here|in\s+my\s+(?:area|neighborhood|neighbourhood))\s*$/;
176
+ /**
177
+ * Split on whitespace + commas, the way the other rules in this package do.
178
+ */
179
+ function wordsOf(text) {
180
+ return text.split(/[\s,]+/).filter(Boolean);
181
+ }
182
+ /**
183
+ * True when the input carries a deictic locator tail in EITHER form.
184
+ */
185
+ function hasDeicticTail(lowercased) {
186
+ return DEICTIC_LOCATOR_TAIL.test(lowercased) || DEICTIC_ADVERB_TAIL.test(lowercased);
187
+ }
188
+ /**
189
+ * The gates `bare_toponym` and `route_pair` share: no address grammar of any kind, one segment, alpha throughout.
190
+ *
191
+ * Returns the word list when the input clears them, `null` when it does not. Deliberately a SUPERSET of
192
+ * `scoreLocalityOnly`'s gates (which admits two segments), so `bare_toponym` is a strict refinement of `locality_only`
193
+ * and can never fire where `locality_only` did not — the property `intent-rules.test.ts` asserts and the reason the
194
+ * ranking discipline above is enough to keep the top kind pinned.
195
+ */
196
+ function bareNameWords(input, shape) {
197
+ const text = input.normalized.trim();
198
+ if (!text || text.length > MAX_LOCALITY_ONLY_LENGTH)
199
+ return null;
200
+ // A recognized postcode/known format IS address grammar. Nothing bare survives this.
201
+ if (shape.knownFormats.length)
202
+ return null;
203
+ // `alpha` excludes every house number and every postcode by construction — the cheapest available statement of
204
+ // "no address grammar", and it costs no lexicon.
205
+ if (shape.characterClass !== "alpha")
206
+ return null;
207
+ // A comma is the admin-context marker ("Paris, FR"). One segment, or the name is not bare.
208
+ if ((shape.segments?.length ?? 1) !== 1)
209
+ return null;
210
+ const lowercased = text.toLowerCase();
211
+ if (hasDeicticTail(lowercased))
212
+ return null;
213
+ const words = wordsOf(text);
214
+ if (!words.length || words.length > MAX_BARE_TOPONYM_WORDS)
215
+ return null;
216
+ for (const word of words) {
217
+ if (STREET_SUFFIXES.has(word.toLowerCase()))
218
+ return null;
219
+ }
220
+ return words;
221
+ }
222
+ /**
223
+ * `bare_toponym` rule: a single coherent place-name carrying no address grammar.
224
+ *
225
+ * Feeds the declared-ambiguity path. The rule itself asserts nothing about WHICH place — that is the resolver's
226
+ * question, and `mailwoman/query-intent.ts` is where the answer's dominance margin decides whether the ambiguity gets
227
+ * declared.
228
+ */
229
+ export function scoreBareToponym(input, shape) {
230
+ return bareNameWords(input, shape) ? BARE_TOPONYM_CONFIDENCE : 0;
231
+ }
232
+ /**
233
+ * `route_pair` rule: exactly two toponym-shaped tokens with nothing between them.
234
+ *
235
+ * **The known confound is structural and unfixable here.** "Paris London" and "Moscow Idaho" are the same string shape
236
+ * — two bare capitalized words — and separating them needs to know that Idaho is a region, which is a gazetteer fact,
237
+ * not a structural one. The hard-slice board's 18 `comma_free` rows are that population, and they fire this rule. That
238
+ * is the reason ROAD_TO_V9 §4.3 specifies **classification + a declared fork, never a router**: both readings are named
239
+ * in the marker, neither wins, and the resolver keeps answering exactly as it did.
240
+ *
241
+ * The one class that IS separable structurally is the two-token SINGLE name — "New York", "Fort Worth", "San Francisco"
242
+ * — because those carry a toponymic head particle. That guard is what keeps the fork off the common case.
243
+ */
244
+ export function scoreRoutePair(input, shape) {
245
+ const words = bareNameWords(input, shape);
246
+ if (!words || words.length !== 2)
247
+ return 0;
248
+ const [first, second] = [words[0].toLowerCase(), words[1].toLowerCase()];
249
+ // Reduplication — "Pago Pago", "Baden-Baden", "Walla Walla", "Bora Bora". Nobody travels from a place to itself,
250
+ // so a repeated token is a universal single-name signal and needs no lexicon at all.
251
+ if (first === second)
252
+ return 0;
253
+ if (TOPONYM_HEAD_PARTICLES.has(first) || TOPONYM_HEAD_PARTICLES.has(second))
254
+ return 0;
255
+ if (TOPONYM_TAIL_NOUNS.has(second))
256
+ return 0;
257
+ return ROUTE_PAIR_CONFIDENCE;
258
+ }
259
+ /**
260
+ * `near_me` rule: a subject plus a deictic locator, with no anchor.
261
+ *
262
+ * Requires a non-empty subject before the locator, so a bare "near me" stays with the `landmark` leaders rule rather
263
+ * than claiming to be a category search with a missing focus point.
264
+ */
265
+ export function scoreNearMe(input, _shape) {
266
+ const lowercased = input.normalized.trim().toLowerCase();
267
+ if (!hasDeicticTail(lowercased))
268
+ return 0;
269
+ // The subject is everything before the locator. `hasDeicticTail` already anchored the match to the end, so the
270
+ // first match index is where the subject stops.
271
+ const match = DEICTIC_LOCATOR_TAIL.exec(lowercased) ?? DEICTIC_ADVERB_TAIL.exec(lowercased);
272
+ if (!match)
273
+ return 0;
274
+ return lowercased.slice(0, match.index).trim() ? NEAR_ME_CONFIDENCE : 0;
275
+ }
276
+ /**
277
+ * The subject of a `near_me` query — the category or thing the asker wants, with the locator stripped. Empty string
278
+ * when the rule would not have fired. Used to build the marker's evidence, never to route.
279
+ */
280
+ export function nearMeSubject(input) {
281
+ const trimmed = input.normalized.trim();
282
+ const lowercased = trimmed.toLowerCase();
283
+ const match = DEICTIC_LOCATOR_TAIL.exec(lowercased) ?? DEICTIC_ADVERB_TAIL.exec(lowercased);
284
+ if (!match)
285
+ return "";
286
+ return trimmed.slice(0, match.index).trim();
287
+ }
288
+ //# sourceMappingURL=intent-rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intent-rules.js","sourceRoot":"","sources":["../intent-rules.ts"],"names":[],"mappings":"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"}
package/out/poi.d.ts CHANGED
@@ -14,18 +14,20 @@ import type { NormalizedInputLite, QueryShapeLike } from "./types.ts";
14
14
  */
15
15
  export interface POIPhraseMatch {
16
16
  /**
17
- * The matched subject's identifier string. For `kind: "category"`, a `@mailwoman/poi-taxonomy` category id. For
18
- * `kind: "brand"`, the brand's canonical display name (NOT a taxonomy id) — `matchPOISubject` never reads this field
19
- * itself, so the caller (`mailwoman`'s `poi-intent.ts`) is the one that interprets it per `kind`.
17
+ * The matched subject's identifier string. For `kind: "category"`, a `@mailwoman/poi-taxonomy` category id; for
18
+ * `kind: "brand"` or `kind: "name"`, the canonical display name. `matchPOISubject` treats it opaquely; the caller
19
+ * (`mailwoman`'s `poi-intent.ts`) interprets it per `kind`.
20
20
  */
21
21
  categoryID: string;
22
22
  matchedPhrase: string;
23
23
  confidence: number;
24
+ mechanism?: "exact" | "locale_normalized" | "typo";
25
+ inputPhrase?: string;
24
26
  /**
25
27
  * Absent = "category" (the pre-brand shape) — optional so pre-7.3 POIPhraseLookup implementors stay
26
28
  * source-compatible.
27
29
  */
28
- kind?: "category" | "brand";
30
+ kind?: "category" | "brand" | "name";
29
31
  /**
30
32
  * Wikidata QID, when known. `kind: "brand"` only — absent when a brand resolved by name alone (no QID match).
31
33
  */
@@ -35,6 +37,15 @@ export interface POIPhraseMatch {
35
37
  * Injected phrase→category lookup. Exact-phrase, locale-aware; returns [] on miss.
36
38
  */
37
39
  export type POIPhraseLookup = (phrase: string, locale?: string) => ReadonlyArray<POIPhraseMatch>;
40
+ export type POISpatialRelation = "comma" | "near" | "in" | "at" | "around" | "to";
41
+ export interface POIQuerySpan {
42
+ text: string;
43
+ /**
44
+ * Half-open character offsets into the normalized input.
45
+ */
46
+ start: number;
47
+ end: number;
48
+ }
38
49
  /**
39
50
  * Which lexicon this hit came from. Existing category lookups set `"category"` (backward-compatible default).
40
51
  */
@@ -44,14 +55,21 @@ export interface POISubjectMatch {
44
55
  * The matched subject text as it appeared in the query.
45
56
  */
46
57
  subject: string;
58
+ subjectSpan: POIQuerySpan;
59
+ /**
60
+ * The relation crossing from the subject span to the anchor span.
61
+ */
62
+ relation?: POISpatialRelation;
63
+ relationSpan?: POIQuerySpan;
47
64
  /**
48
65
  * The anchor remainder after the separator; `""` when the whole input matched.
49
66
  */
50
67
  remainder: string;
68
+ anchorSpan?: POIQuerySpan;
51
69
  }
52
70
  /**
53
71
  * Match a POI subject: the whole input, or the text before the FIRST anchor separator WHOSE PREFIX HITS THE LEXICON (≤
54
- * 4 tokens). Scans separator occurrences left-to-right — a lexicon phrase may itself contain a bare separator word
72
+ * 8 tokens). Scans separator occurrences left-to-right — a lexicon phrase may itself contain a bare separator word
55
73
  * (e.g. "walk in clinic"), so the first separator isn't necessarily the right split point. Returns null when the
56
74
  * lexicon never fires — including comma-ridden full addresses whose leading segment isn't a lexicon phrase.
57
75
  */
@@ -63,4 +81,19 @@ export declare function matchPOISubject(text: string, locale: string | undefined
63
81
  * input, scores 0 here.
64
82
  */
65
83
  export declare function createScorePOIQuery(lookup: POIPhraseLookup, locale?: string): (input: NormalizedInputLite, shape: QueryShapeLike) => number;
84
+ /**
85
+ * `poi_category` scorer (ROAD_TO_V9 §4.4) — a bare taxonomy category with nowhere to search: "tacos", "grocery store",
86
+ * "drinking fountain".
87
+ *
88
+ * Fires ONLY on a whole-input lexicon hit (`remainder === ""`) whose subject is a CATEGORY. A brand (`kind: "brand"`)
89
+ * is excluded: a bare "Starbucks" is a name lookup, not a category, and the taxonomy id a category marker promises to
90
+ * carry does not exist for it — `POIPhraseMatch.categoryID` holds the brand's display name in that case, which would
91
+ * make the marker's `categoryID` evidence a lie.
92
+ */
93
+ export declare function createScorePOICategory(lookup: POIPhraseLookup, locale?: string): (input: NormalizedInputLite, shape: QueryShapeLike) => number;
94
+ /**
95
+ * The whole-input category hit behind a `poi_category` verdict, for the marker's evidence. `null` when the input is not
96
+ * a bare category — same gates as {@link createScorePOICategory}, so the two cannot disagree.
97
+ */
98
+ export declare function matchPOICategory(text: string, locale: string | undefined, lookup: POIPhraseLookup): POIPhraseMatch | null;
66
99
  //# sourceMappingURL=poi.d.ts.map
package/out/poi.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"poi.d.ts","sourceRoot":"","sources":["../poi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAQrE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,CAAA;IAC3B;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,aAAa,CAAC,cAAc,CAAC,CAAA;AAEhG;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,cAAc,CAAA;IACrB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;OAEG;IACH,SAAS,EAAE,MAAM,CAAA;CACjB;AA2BD;;;;;GAKG;AACH,wBAAgB,eAAe,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,MAAM,EAAE,eAAe,GACrB,eAAe,GAAG,IAAI,CA6BxB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAClC,MAAM,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,MAAM,GACb,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,KAAK,MAAM,CAiB/D"}
1
+ {"version":3,"file":"poi.d.ts","sourceRoot":"","sources":["../poi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAQrE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,OAAO,GAAG,mBAAmB,GAAG,MAAM,CAAA;IAClD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,GAAG,MAAM,CAAA;IACpC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,aAAa,CAAC,cAAc,CAAC,CAAA;AAEhG,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,QAAQ,GAAG,IAAI,CAAA;AAEjF,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;CACX;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,cAAc,CAAA;IACrB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,YAAY,CAAA;IACzB;;OAEG;IACH,QAAQ,CAAC,EAAE,kBAAkB,CAAA;IAC7B,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,YAAY,CAAA;CACzB;AA2BD;;;;;GAKG;AACH,wBAAgB,eAAe,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,MAAM,EAAE,eAAe,GACrB,eAAe,GAAG,IAAI,CA8DxB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAClC,MAAM,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,MAAM,GACb,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,KAAK,MAAM,CAiB/D;AAUD;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACrC,MAAM,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,MAAM,GACb,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,KAAK,MAAM,CAU/D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC/B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,MAAM,EAAE,eAAe,GACrB,cAAc,GAAG,IAAI,CAQvB"}
package/out/poi.js CHANGED
@@ -14,8 +14,8 @@
14
14
  */
15
15
  const MAX_POI_SEGMENTS = 3;
16
16
  /**
17
- * Anchor separator between subject and place: comma, or near/in/at/around — scanned left-to-right until a prefix hits
18
- * the lexicon.
17
+ * Anchor separator between subject and place: comma, or near/in/at/around/to — scanned left-to-right until a prefix
18
+ * hits the lexicon.
19
19
  *
20
20
  * Linear by construction (no polynomial ReDoS): neither alternative places an unbounded whitespace quantifier _before_
21
21
  * its required literal — the classic `\s*`/`\s+`-then-literal backtracking shape that CodeQL's `js/polynomial-redos`
@@ -31,24 +31,30 @@ const MAX_POI_SEGMENTS = 3;
31
31
  * lastIndex) identical, preserving the exact subsequent-match sequence. Verified: 0 divergences across 22.7k inputs
32
32
  * (systematic + fuzzed adversarial whitespace + shared-whitespace anchor/comma chains).
33
33
  */
34
- const ANCHOR_SEPARATOR = /,\s*|\s(?:near|in|at|around)\s+/gi;
34
+ const ANCHOR_SEPARATOR = /,\s*|\s(near|in|at|around|to)\s+/gi;
35
35
  /**
36
- * Longest subject we accept, in tokens. Lexicon phrases are short; 4 covers the table.
36
+ * Longest subject we accept, in tokens. Eight covers compound taxonomy phrases while bounding lexicon probes.
37
37
  */
38
- const MAX_SUBJECT_TOKENS = 4;
38
+ const MAX_SUBJECT_TOKENS = 8;
39
39
  /**
40
40
  * Match a POI subject: the whole input, or the text before the FIRST anchor separator WHOSE PREFIX HITS THE LEXICON (≤
41
- * 4 tokens). Scans separator occurrences left-to-right — a lexicon phrase may itself contain a bare separator word
41
+ * 8 tokens). Scans separator occurrences left-to-right — a lexicon phrase may itself contain a bare separator word
42
42
  * (e.g. "walk in clinic"), so the first separator isn't necessarily the right split point. Returns null when the
43
43
  * lexicon never fires — including comma-ridden full addresses whose leading segment isn't a lexicon phrase.
44
44
  */
45
45
  export function matchPOISubject(text, locale, lookup) {
46
46
  const trimmed = text.trim();
47
+ const inputStart = text.indexOf(trimmed);
47
48
  if (!trimmed)
48
49
  return null;
49
50
  const whole = lookup(trimmed, locale);
50
51
  if (whole.length) {
51
- return { match: whole[0], subject: trimmed, remainder: "" };
52
+ return {
53
+ match: whole[0],
54
+ subject: trimmed,
55
+ subjectSpan: { text: trimmed, start: inputStart, end: inputStart + trimmed.length },
56
+ remainder: "",
57
+ };
52
58
  }
53
59
  for (const separator of trimmed.matchAll(ANCHOR_SEPARATOR)) {
54
60
  if (separator.index === 0)
@@ -61,7 +67,33 @@ export function matchPOISubject(text, locale, lookup) {
61
67
  if (!hits.length)
62
68
  continue;
63
69
  const remainder = trimmed.slice(separator.index + separator[0].length).trim();
64
- return { match: hits[0], subject, remainder };
70
+ if (!remainder)
71
+ continue;
72
+ const subjectOffset = trimmed.slice(0, separator.index).indexOf(subject);
73
+ const anchorOffset = trimmed.indexOf(remainder, separator.index + separator[0].length);
74
+ const relationText = separator[1] ?? ",";
75
+ const relationOffset = separator[1] ? separator.index + separator[0].indexOf(separator[1]) : separator.index;
76
+ return {
77
+ match: hits[0],
78
+ subject,
79
+ subjectSpan: {
80
+ text: subject,
81
+ start: inputStart + subjectOffset,
82
+ end: inputStart + subjectOffset + subject.length,
83
+ },
84
+ relation: relationText === "," ? "comma" : relationText.toLowerCase(),
85
+ relationSpan: {
86
+ text: relationText,
87
+ start: inputStart + relationOffset,
88
+ end: inputStart + relationOffset + relationText.length,
89
+ },
90
+ remainder,
91
+ anchorSpan: {
92
+ text: remainder,
93
+ start: inputStart + anchorOffset,
94
+ end: inputStart + anchorOffset + remainder.length,
95
+ },
96
+ };
65
97
  }
66
98
  return null;
67
99
  }
@@ -87,4 +119,42 @@ export function createScorePOIQuery(lookup, locale) {
87
119
  return 0.9 * matched.match.confidence;
88
120
  };
89
121
  }
122
+ /**
123
+ * Confidence band for a bare category. One notch above `poi_query`'s whole-input band (0.92) so the anchorless subset
124
+ * takes the top slot from it, and only from it — every anchored POI query keeps scoring `poi_query` exactly as before.
125
+ * The coordinator's POI branch accepts both kinds, so the routing is identical either way; the split exists so the
126
+ * marker can say "you named a category and no place", which is a different thing to tell a caller.
127
+ */
128
+ const POI_CATEGORY_CONFIDENCE = 0.93;
129
+ /**
130
+ * `poi_category` scorer (ROAD_TO_V9 §4.4) — a bare taxonomy category with nowhere to search: "tacos", "grocery store",
131
+ * "drinking fountain".
132
+ *
133
+ * Fires ONLY on a whole-input lexicon hit (`remainder === ""`) whose subject is a CATEGORY. A brand (`kind: "brand"`)
134
+ * is excluded: a bare "Starbucks" is a name lookup, not a category, and the taxonomy id a category marker promises to
135
+ * carry does not exist for it — `POIPhraseMatch.categoryID` holds the brand's display name in that case, which would
136
+ * make the marker's `categoryID` evidence a lie.
137
+ */
138
+ export function createScorePOICategory(lookup, locale) {
139
+ return (input, _shape) => {
140
+ const matched = matchPOISubject(input.normalized, locale ?? input.appliedLocale, lookup);
141
+ if (!matched || matched.remainder !== "")
142
+ return 0;
143
+ if ((matched.match.kind ?? "category") !== "category")
144
+ return 0;
145
+ return POI_CATEGORY_CONFIDENCE * matched.match.confidence;
146
+ };
147
+ }
148
+ /**
149
+ * The whole-input category hit behind a `poi_category` verdict, for the marker's evidence. `null` when the input is not
150
+ * a bare category — same gates as {@link createScorePOICategory}, so the two cannot disagree.
151
+ */
152
+ export function matchPOICategory(text, locale, lookup) {
153
+ const matched = matchPOISubject(text, locale, lookup);
154
+ if (!matched || matched.remainder !== "")
155
+ return null;
156
+ if ((matched.match.kind ?? "category") !== "category")
157
+ return null;
158
+ return matched.match;
159
+ }
90
160
  //# sourceMappingURL=poi.js.map
package/out/poi.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"poi.js","sourceRoot":"","sources":["../poi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH;;;GAGG;AACH,MAAM,gBAAgB,GAAG,CAAC,CAAA;AA6C1B;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,gBAAgB,GAAG,mCAAmC,CAAA;AAE5D;;GAEG;AACH,MAAM,kBAAkB,GAAG,CAAC,CAAA;AAE5B;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC9B,IAAY,EACZ,MAA0B,EAC1B,MAAuB;IAEvB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;IAE3B,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAA;IAEzB,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IAErC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QAClB,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAE,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,CAAA;IAC7D,CAAC;IAED,KAAK,MAAM,SAAS,IAAI,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAC5D,IAAI,SAAS,CAAC,KAAK,KAAK,CAAC;YAAE,SAAQ;QAEnC,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAA;QAExD,uFAAuF;QACvF,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,kBAAkB;YAAE,MAAK;QAE3D,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QAEpC,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,SAAQ;QAE1B,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAA;QAE7E,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAA;IAC/C,CAAC;IAED,OAAO,IAAI,CAAA;AACZ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAClC,MAAuB,EACvB,MAAe;IAEf,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,CAAC,UAAU,EAAE,MAAM,IAAI,KAAK,CAAC,aAAa,EAAE,MAAM,CAAC,CAAA;QAExF,IAAI,CAAC,OAAO;YAAE,OAAO,CAAC,CAAA;QAEtB,IAAI,OAAO,CAAC,SAAS,KAAK,EAAE;YAAE,OAAO,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAA;QAEpE,gFAAgF;QAChF,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;YAAE,OAAO,CAAC,CAAA;QAE9C,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC,CAAA;QAE5C,IAAI,QAAQ,GAAG,gBAAgB;YAAE,OAAO,CAAC,CAAA;QAEzC,OAAO,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAA;IACtC,CAAC,CAAA;AACF,CAAC"}
1
+ {"version":3,"file":"poi.js","sourceRoot":"","sources":["../poi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH;;;GAGG;AACH,MAAM,gBAAgB,GAAG,CAAC,CAAA;AAiE1B;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,gBAAgB,GAAG,oCAAoC,CAAA;AAE7D;;GAEG;AACH,MAAM,kBAAkB,GAAG,CAAC,CAAA;AAE5B;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC9B,IAAY,EACZ,MAA0B,EAC1B,MAAuB;IAEvB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;IAC3B,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAExC,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAA;IAEzB,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IAErC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QAClB,OAAO;YACN,KAAK,EAAE,KAAK,CAAC,CAAC,CAAE;YAChB,OAAO,EAAE,OAAO;YAChB,WAAW,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,EAAE;YACnF,SAAS,EAAE,EAAE;SACb,CAAA;IACF,CAAC;IAED,KAAK,MAAM,SAAS,IAAI,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAC5D,IAAI,SAAS,CAAC,KAAK,KAAK,CAAC;YAAE,SAAQ;QAEnC,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAA;QAExD,uFAAuF;QACvF,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,kBAAkB;YAAE,MAAK;QAE3D,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QAEpC,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,SAAQ;QAE1B,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAA;QAE7E,IAAI,CAAC,SAAS;YAAE,SAAQ;QAExB,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;QACxE,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;QACtF,MAAM,YAAY,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,GAAG,CAAA;QACxC,MAAM,cAAc,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAA;QAE5G,OAAO;YACN,KAAK,EAAE,IAAI,CAAC,CAAC,CAAE;YACf,OAAO;YACP,WAAW,EAAE;gBACZ,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE,UAAU,GAAG,aAAa;gBACjC,GAAG,EAAE,UAAU,GAAG,aAAa,GAAG,OAAO,CAAC,MAAM;aAChD;YACD,QAAQ,EAAE,YAAY,KAAK,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,YAAY,CAAC,WAAW,EAAyB;YAC7F,YAAY,EAAE;gBACb,IAAI,EAAE,YAAY;gBAClB,KAAK,EAAE,UAAU,GAAG,cAAc;gBAClC,GAAG,EAAE,UAAU,GAAG,cAAc,GAAG,YAAY,CAAC,MAAM;aACtD;YACD,SAAS;YACT,UAAU,EAAE;gBACX,IAAI,EAAE,SAAS;gBACf,KAAK,EAAE,UAAU,GAAG,YAAY;gBAChC,GAAG,EAAE,UAAU,GAAG,YAAY,GAAG,SAAS,CAAC,MAAM;aACjD;SACD,CAAA;IACF,CAAC;IAED,OAAO,IAAI,CAAA;AACZ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAClC,MAAuB,EACvB,MAAe;IAEf,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,CAAC,UAAU,EAAE,MAAM,IAAI,KAAK,CAAC,aAAa,EAAE,MAAM,CAAC,CAAA;QAExF,IAAI,CAAC,OAAO;YAAE,OAAO,CAAC,CAAA;QAEtB,IAAI,OAAO,CAAC,SAAS,KAAK,EAAE;YAAE,OAAO,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAA;QAEpE,gFAAgF;QAChF,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;YAAE,OAAO,CAAC,CAAA;QAE9C,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC,CAAA;QAE5C,IAAI,QAAQ,GAAG,gBAAgB;YAAE,OAAO,CAAC,CAAA;QAEzC,OAAO,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAA;IACtC,CAAC,CAAA;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,uBAAuB,GAAG,IAAI,CAAA;AAEpC;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CACrC,MAAuB,EACvB,MAAe;IAEf,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;QACxB,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,CAAC,UAAU,EAAE,MAAM,IAAI,KAAK,CAAC,aAAa,EAAE,MAAM,CAAC,CAAA;QAExF,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,SAAS,KAAK,EAAE;YAAE,OAAO,CAAC,CAAA;QAElD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,UAAU,CAAC,KAAK,UAAU;YAAE,OAAO,CAAC,CAAA;QAE/D,OAAO,uBAAuB,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAA;IAC1D,CAAC,CAAA;AACF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC/B,IAAY,EACZ,MAA0B,EAC1B,MAAuB;IAEvB,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAErD,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,SAAS,KAAK,EAAE;QAAE,OAAO,IAAI,CAAA;IAErD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,UAAU,CAAC,KAAK,UAAU;QAAE,OAAO,IAAI,CAAA;IAElE,OAAO,OAAO,CAAC,KAAK,CAAA;AACrB,CAAC"}
package/out/rules.d.ts CHANGED
@@ -10,6 +10,14 @@
10
10
  * regex set per new locale, not 50K dictionary entries.
11
11
  */
12
12
  import type { NormalizedInputLite, QueryShapeLike } from "./types.ts";
13
+ /**
14
+ * Longest input still plausible as a bare locality name, including a trailing region code.
15
+ *
16
+ * Exported so `intent-rules.ts`'s `bare_toponym` can share the exact same ceiling. Sharing it is what makes
17
+ * "bare_toponym is a strict refinement of locality_only" a structural property rather than two numbers that happen to
18
+ * agree today.
19
+ */
20
+ export declare const MAX_LOCALITY_ONLY_LENGTH = 30;
13
21
  /**
14
22
  * `po_box` rule: high-confidence iff QueryShape detected a po_box format hit. Confidence comes directly from the hit;
15
23
  * covers all locale variants (US "PO Box 123", FR "BP 42", etc.).
@@ -24,6 +32,11 @@ export declare function scoreIntersection(input: NormalizedInputLite, _shape: Qu
24
32
  * location relative to another place.
25
33
  */
26
34
  export declare function scoreLandmark(input: NormalizedInputLite, _shape: QueryShapeLike): number;
35
+ /**
36
+ * Street-suffix tokens that indicate an address, not a venue name. Shared with `intent-rules.ts` — the intent kinds
37
+ * disqualify on the same vocabulary, and a second copy would drift.
38
+ */
39
+ export declare const STREET_SUFFIXES: ReadonlySet<string>;
27
40
  /**
28
41
  * `landmark` rule (venue/named-place variant): short capitalized input with no street suffixes, no postcode hits, and
29
42
  * no region abbreviations. Captures "Pier 39", "Empire State Building", "Wrigley Field", "Grand Central Terminal".
@@ -1 +1 @@
1
- {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAwErE;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAQrF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAQ5F;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,mBAAmB,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAQxF;AA+BD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAkD5F;AAgBD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAc3F;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAe3F;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAmBhG;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,mBAAmB,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAEtF"}
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAmBrE;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAA;AAkD1C;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAQrF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAQ5F;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,mBAAmB,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAQxF;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,MAAM,CAwB9C,CAAA;AAEF;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAkD5F;AAoBD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAc3F;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAe3F;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAmBhG;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,mBAAmB,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAEtF"}
package/out/rules.js CHANGED
@@ -25,8 +25,12 @@ const MAX_POSTCODE_ONLY_LENGTH = 16;
25
25
  const MIN_POSTCODE_COVERAGE = 0.7;
26
26
  /**
27
27
  * Longest input still plausible as a bare locality name, including a trailing region code.
28
+ *
29
+ * Exported so `intent-rules.ts`'s `bare_toponym` can share the exact same ceiling. Sharing it is what makes
30
+ * "bare_toponym is a strict refinement of locality_only" a structural property rather than two numbers that happen to
31
+ * agree today.
28
32
  */
29
- const MAX_LOCALITY_ONLY_LENGTH = 30;
33
+ export const MAX_LOCALITY_ONLY_LENGTH = 30;
30
34
  /**
31
35
  * Word count of a short capitalized phrase — the shape of a venue name like `Empire State Building`. Wider than this
32
36
  * and the phrase is more likely a full address line.
@@ -105,9 +109,10 @@ export function scoreLandmark(input, _shape) {
105
109
  return 0;
106
110
  }
107
111
  /**
108
- * Street-suffix tokens that indicate an address, not a venue name.
112
+ * Street-suffix tokens that indicate an address, not a venue name. Shared with `intent-rules.ts` — the intent kinds
113
+ * disqualify on the same vocabulary, and a second copy would drift.
109
114
  */
110
- const STREET_SUFFIXES = new Set([
115
+ export const STREET_SUFFIXES = new Set([
111
116
  "st",
112
117
  "street",
113
118
  "ave",
@@ -194,6 +199,10 @@ const POSTCODE_FORMATS = new Set([
194
199
  "ca_postcode",
195
200
  "jp_postcode",
196
201
  "nl_postcode",
202
+ "cz_postcode",
203
+ "sk_postcode",
204
+ "se_postcode",
205
+ "gr_postcode",
197
206
  ]);
198
207
  /**
199
208
  * Test whether a format string is a postcode variant. Use the set rather than ad-hoc string-matching to avoid the
package/out/rules.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"rules.js","sourceRoot":"","sources":["../rules.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rules.js","sourceRoot":"","sources":["../rules.ts"],"names":[],"mappings":"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"}
package/out/types.d.ts CHANGED
@@ -6,7 +6,8 @@
6
6
  /**
7
7
  * Re-exports of the canonical types from `@mailwoman/core/pipeline`.
8
8
  */
9
- export type { LocaleHint, QueryKind, QueryKindResult } from "@mailwoman/core/pipeline";
9
+ export type { LocaleHint, QueryIntentMarker, QueryKind, QueryKindResult } from "@mailwoman/core/pipeline";
10
+ export { QueryIntentCode } from "@mailwoman/core/pipeline";
10
11
  /**
11
12
  * Minimal `NormalizedInput` shape consumed by `classifyKind`. Compatible with `@mailwoman/normalize`'s output.
12
13
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAEtF;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,GAAG,EAAE,MAAM,CAAA;IACX,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,YAAY,EAAE,aAAa,CAAC;QAC3B,MAAM,EAAE,MAAM,CAAA;QACd,IAAI,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAA;QACpC,UAAU,EAAE,MAAM,CAAA;KAClB,CAAC,CAAA;IACF,QAAQ,CAAC,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACzD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;CACpB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AACzG,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAE1D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,GAAG,EAAE,MAAM,CAAA;IACX,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,YAAY,EAAE,aAAa,CAAC;QAC3B,MAAM,EAAE,MAAM,CAAA;QACd,IAAI,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAA;QACpC,UAAU,EAAE,MAAM,CAAA;KAClB,CAAC,CAAA;IACF,QAAQ,CAAC,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACzD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;CACpB"}