@mailwoman/kind-classifier 7.0.0 → 7.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.
package/out/classify.d.ts CHANGED
@@ -12,6 +12,7 @@
12
12
  * surfaces in `alternatives` — the caller decides whether to act on the top kind only or consider
13
13
  * runner-ups.
14
14
  */
15
+ import { type POIPhraseLookup } from "./poi.ts";
15
16
  import type { LocaleHint, NormalizedInputLite, QueryKindResult, QueryShapeLike } from "./types.ts";
16
17
  /**
17
18
  * Classify the query shape into a `QueryKind`. Synchronous + pure — produces the same result for the same `(input,
@@ -24,4 +25,18 @@ export declare function classifyKindSync(input: NormalizedInputLite, shape: Quer
24
25
  * The locale parameter is accepted for future locale-aware rules (Japanese honorifics, etc.) but not currently used.
25
26
  */
26
27
  export declare function classifyKind(input: NormalizedInputLite, shape: QueryShapeLike, _locale?: LocaleHint): Promise<QueryKindResult>;
28
+ /** Options for {@link createKindClassifier}. */
29
+ export interface KindClassifierOpts {
30
+ /**
31
+ * POI phrase lexicon (spec §3.1). When present, a `poi_query` scorer joins the rule set — injected, never imported,
32
+ * so this package stays dictionary-free. Absent → the returned classifier is behaviorally identical to
33
+ * {@link classifyKind}.
34
+ */
35
+ poiLexicon?: POIPhraseLookup;
36
+ }
37
+ /**
38
+ * Build a kind classifier. Without opts this is exactly the default {@link classifyKind}; with a `poiLexicon` it
39
+ * additionally scores `poi_query` and merges it into the ranked result.
40
+ */
41
+ export declare function createKindClassifier(opts?: KindClassifierOpts): (input: NormalizedInputLite, shape: QueryShapeLike, locale?: LocaleHint) => Promise<QueryKindResult>;
27
42
  //# sourceMappingURL=classify.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"classify.d.ts","sourceRoot":"","sources":["../classify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAYH,OAAO,KAAK,EAAE,UAAU,EAAE,mBAAmB,EAAa,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAiB7G;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,eAAe,CAcnG;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CACjC,KAAK,EAAE,mBAAmB,EAC1B,KAAK,EAAE,cAAc,EACrB,OAAO,CAAC,EAAE,UAAU,GAClB,OAAO,CAAC,eAAe,CAAC,CAE1B"}
1
+ {"version":3,"file":"classify.d.ts","sourceRoot":"","sources":["../classify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,UAAU,CAAA;AAWpE,OAAO,KAAK,EAAE,UAAU,EAAE,mBAAmB,EAAa,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAiB7G;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,eAAe,CAcnG;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CACjC,KAAK,EAAE,mBAAmB,EAC1B,KAAK,EAAE,cAAc,EACrB,OAAO,CAAC,EAAE,UAAU,GAClB,OAAO,CAAC,eAAe,CAAC,CAE1B;AAED,gDAAgD;AAChD,MAAM,WAAW,kBAAkB;IAClC;;;;OAIG;IACH,UAAU,CAAC,EAAE,eAAe,CAAA;CAC5B;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CACnC,IAAI,GAAE,kBAAuB,GAC3B,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,UAAU,KAAK,OAAO,CAAC,eAAe,CAAC,CA2BtG"}
package/out/classify.js CHANGED
@@ -12,6 +12,7 @@
12
12
  * surfaces in `alternatives` — the caller decides whether to act on the top kind only or consider
13
13
  * runner-ups.
14
14
  */
15
+ import { createScorePOIQuery } from "./poi.js";
15
16
  import { scoreIntersection, scoreLandmark, scoreLocalityOnly, scorePoBox, scorePostcodeOnly, scoreStructuredAddress, scoreVague, scoreVenueLandmark, } from "./rules.js";
16
17
  const SCORERS = [
17
18
  { kind: "po_box", score: scorePoBox },
@@ -45,4 +46,32 @@ export function classifyKindSync(input, shape) {
45
46
  export async function classifyKind(input, shape, _locale) {
46
47
  return classifyKindSync(input, shape);
47
48
  }
49
+ /**
50
+ * Build a kind classifier. Without opts this is exactly the default {@link classifyKind}; with a `poiLexicon` it
51
+ * additionally scores `poi_query` and merges it into the ranked result.
52
+ */
53
+ export function createKindClassifier(opts = {}) {
54
+ const { poiLexicon } = opts;
55
+ if (!poiLexicon)
56
+ return classifyKind;
57
+ return async (input, shape, locale) => {
58
+ const base = classifyKindSync(input, shape);
59
+ const poiConfidence = createScorePOIQuery(poiLexicon, locale?.locale)(input, shape);
60
+ if (poiConfidence <= 0)
61
+ return base;
62
+ if (poiConfidence > base.confidence) {
63
+ return {
64
+ kind: "poi_query",
65
+ confidence: poiConfidence,
66
+ alternatives: [{ kind: base.kind, confidence: base.confidence }, ...base.alternatives],
67
+ };
68
+ }
69
+ // The literal needs the contextual element type — a bare array literal widens `kind` to string.
70
+ const poiAlternative = { kind: "poi_query", confidence: poiConfidence };
71
+ return {
72
+ ...base,
73
+ alternatives: [...base.alternatives, poiAlternative].sort((a, b) => b.confidence - a.confidence),
74
+ };
75
+ };
76
+ }
48
77
  //# sourceMappingURL=classify.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"classify.js","sourceRoot":"","sources":["../classify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACN,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,iBAAiB,EACjB,sBAAsB,EACtB,UAAU,EACV,kBAAkB,GAClB,MAAM,YAAY,CAAA;AAQnB,MAAM,OAAO,GAA8B;IAC1C,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE;IACrC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;IAC9F,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,iBAAiB,EAAE;IAClD,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,iBAAiB,EAAE;IACnD,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,iBAAiB,EAAE;IACnD,EAAE,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,sBAAsB,EAAE;IAC7D,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE;CACpC,CAAA;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAA0B,EAAE,KAAqB;IACjF,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAC9F,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CACvB,CAAA;IACD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAA;IAElD,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,OAAoB,EAAE,UAAU,EAAE,GAAG,EAAE,CAAA;IACxE,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAA;IAE7F,OAAO;QACN,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,YAAY;KACZ,CAAA;AACF,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CACjC,KAA0B,EAC1B,KAAqB,EACrB,OAAoB;IAEpB,OAAO,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;AACtC,CAAC"}
1
+ {"version":3,"file":"classify.js","sourceRoot":"","sources":["../classify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,mBAAmB,EAAwB,MAAM,UAAU,CAAA;AACpE,OAAO,EACN,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,iBAAiB,EACjB,sBAAsB,EACtB,UAAU,EACV,kBAAkB,GAClB,MAAM,YAAY,CAAA;AAQnB,MAAM,OAAO,GAA8B;IAC1C,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE;IACrC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;IAC9F,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,iBAAiB,EAAE;IAClD,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,iBAAiB,EAAE;IACnD,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,iBAAiB,EAAE;IACnD,EAAE,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,sBAAsB,EAAE;IAC7D,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE;CACpC,CAAA;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAA0B,EAAE,KAAqB;IACjF,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAC9F,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CACvB,CAAA;IACD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAA;IAElD,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,OAAoB,EAAE,UAAU,EAAE,GAAG,EAAE,CAAA;IACxE,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAA;IAE7F,OAAO;QACN,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,YAAY;KACZ,CAAA;AACF,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CACjC,KAA0B,EAC1B,KAAqB,EACrB,OAAoB;IAEpB,OAAO,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;AACtC,CAAC;AAYD;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CACnC,OAA2B,EAAE;IAE7B,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,CAAA;IAE3B,IAAI,CAAC,UAAU;QAAE,OAAO,YAAY,CAAA;IAEpC,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAA4B,EAAE;QAC/D,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;QAC3C,MAAM,aAAa,GAAG,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;QAEnF,IAAI,aAAa,IAAI,CAAC;YAAE,OAAO,IAAI,CAAA;QAEnC,IAAI,aAAa,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YACrC,OAAO;gBACN,IAAI,EAAE,WAAW;gBACjB,UAAU,EAAE,aAAa;gBACzB,YAAY,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC;aACtF,CAAA;QACF,CAAC;QAED,gGAAgG;QAChG,MAAM,cAAc,GAA4C,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,CAAA;QAEhH,OAAO;YACN,GAAG,IAAI;YACP,YAAY,EAAE,CAAC,GAAG,IAAI,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC;SAChG,CAAA;IACF,CAAC,CAAA;AACF,CAAC"}
package/out/index.d.ts CHANGED
@@ -5,14 +5,17 @@
5
5
  *
6
6
  * `@mailwoman/kind-classifier` — Stage 2.5 of the runtime pipeline.
7
7
  *
8
- * Categorize inputs into one of seven `QueryKind`s by composing rule-based scorers over the
8
+ * Categorize inputs into one of eight `QueryKind`s by composing rule-based scorers over the
9
9
  * QueryShape sub-system's output. Pure functions, no ML, no place-name dictionaries. Returns
10
10
  * possibilities (alternatives) alongside the top pick so the coordinator can fall back when the
11
11
  * winning kind isn't actionable.
12
12
  *
13
13
  * See `docs/articles/plan/reference/STAGES.md` § Stage 2.5 for the contract.
14
14
  */
15
- export { classifyKind, classifyKindSync } from "./classify.ts";
15
+ export { classifyKind, classifyKindSync, createKindClassifier } from "./classify.ts";
16
+ export type { KindClassifierOpts } from "./classify.ts";
17
+ export { matchPOISubject } from "./poi.ts";
18
+ export type { POIPhraseMatch, POIPhraseLookup, POISubjectMatch } from "./poi.ts";
16
19
  export { scoreIntersection, scoreLandmark, scoreLocalityOnly, scorePoBox, scorePostcodeOnly, scoreStructuredAddress, scoreVague, } from "./rules.ts";
17
20
  export type { LocaleHint, NormalizedInputLite, QueryKind, QueryKindResult, QueryShapeLike } from "./types.ts";
18
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAC9D,OAAO,EACN,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,iBAAiB,EACjB,sBAAsB,EACtB,UAAU,GACV,MAAM,YAAY,CAAA;AACnB,YAAY,EAAE,UAAU,EAAE,mBAAmB,EAAE,SAAS,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AACpF,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAChF,OAAO,EACN,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,iBAAiB,EACjB,sBAAsB,EACtB,UAAU,GACV,MAAM,YAAY,CAAA;AACnB,YAAY,EAAE,UAAU,EAAE,mBAAmB,EAAE,SAAS,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA"}
package/out/index.js CHANGED
@@ -5,13 +5,14 @@
5
5
  *
6
6
  * `@mailwoman/kind-classifier` — Stage 2.5 of the runtime pipeline.
7
7
  *
8
- * Categorize inputs into one of seven `QueryKind`s by composing rule-based scorers over the
8
+ * Categorize inputs into one of eight `QueryKind`s by composing rule-based scorers over the
9
9
  * QueryShape sub-system's output. Pure functions, no ML, no place-name dictionaries. Returns
10
10
  * possibilities (alternatives) alongside the top pick so the coordinator can fall back when the
11
11
  * winning kind isn't actionable.
12
12
  *
13
13
  * See `docs/articles/plan/reference/STAGES.md` § Stage 2.5 for the contract.
14
14
  */
15
- export { classifyKind, classifyKindSync } from "./classify.js";
15
+ export { classifyKind, classifyKindSync, createKindClassifier } from "./classify.js";
16
+ export { matchPOISubject } from "./poi.js";
16
17
  export { scoreIntersection, scoreLandmark, scoreLocalityOnly, scorePoBox, scorePostcodeOnly, scoreStructuredAddress, scoreVague, } from "./rules.js";
17
18
  //# sourceMappingURL=index.js.map
package/out/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAC9D,OAAO,EACN,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,iBAAiB,EACjB,sBAAsB,EACtB,UAAU,GACV,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AAEpF,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE1C,OAAO,EACN,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,iBAAiB,EACjB,sBAAsB,EACtB,UAAU,GACV,MAAM,YAAY,CAAA"}
package/out/poi.d.ts ADDED
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * POI subject detection for the `poi_query` kind. The lexicon is INJECTED (`POIPhraseLookup`) —
7
+ * this package keeps its bitter-lesson invariant (no dictionaries in-tree); the phrase table
8
+ * lives in `@mailwoman/poi-taxonomy` and is wired in by `createRuntimePipeline` behind the
9
+ * default-OFF `poiQueryKind` flag. Spec §3.1.
10
+ */
11
+ import type { NormalizedInputLite, QueryShapeLike } from "./types.ts";
12
+ /** One lexicon hit for a candidate subject phrase. */
13
+ export interface POIPhraseMatch {
14
+ categoryID: string;
15
+ matchedPhrase: string;
16
+ confidence: number;
17
+ }
18
+ /** Injected phrase→category lookup. Exact-phrase, locale-aware; returns [] on miss. */
19
+ export type POIPhraseLookup = (phrase: string, locale?: string) => ReadonlyArray<POIPhraseMatch>;
20
+ export interface POISubjectMatch {
21
+ match: POIPhraseMatch;
22
+ /** The matched subject text as it appeared in the query. */
23
+ subject: string;
24
+ /** The anchor remainder after the separator; `""` when the whole input matched. */
25
+ remainder: string;
26
+ }
27
+ /**
28
+ * Match a POI subject: the whole input, or the text before the FIRST anchor separator WHOSE PREFIX HITS THE LEXICON (≤
29
+ * 4 tokens). Scans separator occurrences left-to-right — a lexicon phrase may itself contain a bare separator word
30
+ * (e.g. "walk in clinic"), so the first separator isn't necessarily the right split point. Returns null when the
31
+ * lexicon never fires — including comma-ridden full addresses whose leading segment isn't a lexicon phrase.
32
+ */
33
+ export declare function matchPOISubject(text: string, locale: string | undefined, lookup: POIPhraseLookup): POISubjectMatch | null;
34
+ /**
35
+ * `poi_query` scorer over an injected lexicon. Confidence bands: whole-input lexicon hit 0.92 (above venue-landmark's
36
+ * 0.88 ceiling — an exact lexicon phrase beats a shape heuristic); subject + anchor 0.9. Guards below keep venue-led
37
+ * FULL addresses (class 2) on the structured-address path: a remainder that leads with a house number, or a 4+-segment
38
+ * input, scores 0 here.
39
+ */
40
+ export declare function createScorePOIQuery(lookup: POIPhraseLookup, locale?: string): (input: NormalizedInputLite, shape: QueryShapeLike) => number;
41
+ //# sourceMappingURL=poi.d.ts.map
@@ -0,0 +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;AAErE,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC9B,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;CAClB;AAED,uFAAuF;AACvF,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,aAAa,CAAC,cAAc,CAAC,CAAA;AAEhG,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,cAAc,CAAA;IACrB,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAA;IACf,mFAAmF;IACnF,SAAS,EAAE,MAAM,CAAA;CACjB;AAWD;;;;;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"}
package/out/poi.js ADDED
@@ -0,0 +1,69 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * POI subject detection for the `poi_query` kind. The lexicon is INJECTED (`POIPhraseLookup`) —
7
+ * this package keeps its bitter-lesson invariant (no dictionaries in-tree); the phrase table
8
+ * lives in `@mailwoman/poi-taxonomy` and is wired in by `createRuntimePipeline` behind the
9
+ * default-OFF `poiQueryKind` flag. Spec §3.1.
10
+ */
11
+ /**
12
+ * Anchor separator between subject and place: comma, or near/in/at/around — scanned left-to-right until a prefix hits
13
+ * the lexicon.
14
+ */
15
+ const ANCHOR_SEPARATOR = /\s*,\s*|\s+(?:near|in|at|around)\s+/gi;
16
+ /** Longest subject we accept, in tokens. Lexicon phrases are short; 4 covers the table. */
17
+ const MAX_SUBJECT_TOKENS = 4;
18
+ /**
19
+ * Match a POI subject: the whole input, or the text before the FIRST anchor separator WHOSE PREFIX HITS THE LEXICON (≤
20
+ * 4 tokens). Scans separator occurrences left-to-right — a lexicon phrase may itself contain a bare separator word
21
+ * (e.g. "walk in clinic"), so the first separator isn't necessarily the right split point. Returns null when the
22
+ * lexicon never fires — including comma-ridden full addresses whose leading segment isn't a lexicon phrase.
23
+ */
24
+ export function matchPOISubject(text, locale, lookup) {
25
+ const trimmed = text.trim();
26
+ if (!trimmed)
27
+ return null;
28
+ const whole = lookup(trimmed, locale);
29
+ if (whole.length > 0) {
30
+ return { match: whole[0], subject: trimmed, remainder: "" };
31
+ }
32
+ for (const separator of trimmed.matchAll(ANCHOR_SEPARATOR)) {
33
+ if (separator.index === 0)
34
+ continue;
35
+ const subject = trimmed.slice(0, separator.index).trim();
36
+ // Subjects only grow as the scan moves right — once over budget, later splits are too.
37
+ if (subject.split(/\s+/).length > MAX_SUBJECT_TOKENS)
38
+ break;
39
+ const hits = lookup(subject, locale);
40
+ if (hits.length === 0)
41
+ continue;
42
+ const remainder = trimmed.slice(separator.index + separator[0].length).trim();
43
+ return { match: hits[0], subject, remainder };
44
+ }
45
+ return null;
46
+ }
47
+ /**
48
+ * `poi_query` scorer over an injected lexicon. Confidence bands: whole-input lexicon hit 0.92 (above venue-landmark's
49
+ * 0.88 ceiling — an exact lexicon phrase beats a shape heuristic); subject + anchor 0.9. Guards below keep venue-led
50
+ * FULL addresses (class 2) on the structured-address path: a remainder that leads with a house number, or a 4+-segment
51
+ * input, scores 0 here.
52
+ */
53
+ export function createScorePOIQuery(lookup, locale) {
54
+ return (input, shape) => {
55
+ const matched = matchPOISubject(input.normalized, locale ?? input.appliedLocale, lookup);
56
+ if (!matched)
57
+ return 0;
58
+ if (matched.remainder === "")
59
+ return 0.92 * matched.match.confidence;
60
+ // Venue-led full address: "X, 350 5th Ave, …" stays a structured_address parse.
61
+ if (/^\d+\s/.test(matched.remainder))
62
+ return 0;
63
+ const segCount = shape.segments?.length ?? 1;
64
+ if (segCount > 3)
65
+ return 0;
66
+ return 0.9 * matched.match.confidence;
67
+ };
68
+ }
69
+ //# sourceMappingURL=poi.js.map
package/out/poi.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poi.js","sourceRoot":"","sources":["../poi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAsBH;;;GAGG;AACH,MAAM,gBAAgB,GAAG,uCAAuC,CAAA;AAEhE,2FAA2F;AAC3F,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,GAAG,CAAC,EAAE,CAAC;QACtB,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,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,SAAQ;QAE/B,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,CAAC;YAAE,OAAO,CAAC,CAAA;QAE1B,OAAO,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAA;IACtC,CAAC,CAAA;AACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mailwoman/kind-classifier",
3
- "version": "7.0.0",
3
+ "version": "7.2.0",
4
4
  "description": "Stage 2.5 of the runtime pipeline — categorize inputs by query shape (postcode_only / locality_only / structured_address / intersection / po_box / landmark / vague). Rule-based for v1.",
5
5
  "license": "AGPL-3.0-only OR LicenseRef-Commercial",
6
6
  "repository": {
@@ -18,21 +18,15 @@
18
18
  "exports": {
19
19
  "./package.json": "./package.json",
20
20
  ".": {
21
- "types": "./out/index.d.ts",
22
- "default": "./out/index.js"
21
+ "node": "./index.ts",
22
+ "default": "./out/index.js",
23
+ "types": "./out/index.d.ts"
23
24
  }
24
25
  },
25
26
  "publishConfig": {
26
- "exports": {
27
- "./package.json": "./package.json",
28
- ".": {
29
- "types": "./out/index.d.ts",
30
- "default": "./out/index.js"
31
- }
32
- },
33
27
  "access": "public"
34
28
  },
35
29
  "dependencies": {
36
- "@mailwoman/core": "7.0.0"
30
+ "@mailwoman/core": "7.2.0"
37
31
  }
38
32
  }