@mailwoman/api 8.6.0 → 9.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/out/schema.d.ts CHANGED
@@ -6,9 +6,9 @@
6
6
  * Zod wire schemas for the native `/v1` surface. Unlike the drop-ins (photon, nominatim,
7
7
  * libpostal), nothing here is a vendor contract — this surface is ours to design, so request
8
8
  * bodies are REQUIRED and validator-enforced (no legacy tolerance to preserve). A `defaultHook`
9
- * on the app (wired in Task 3) maps validation failures through the shared `APIErrorSchema`
10
- * envelope (`apiError(c, 400, "invalid request body", <zod summary>)`) — the documented pattern
11
- * boundary from phase 2: where no legacy contract exists, the validator MAY speak, but only in
9
+ * on the app maps validation failures through the shared `APIErrorSchema` envelope
10
+ * (`apiError(c, 400, "invalid request body", <zod summary>)`) — the pattern boundary every
11
+ * surface holds to: where no legacy contract exists, the validator MAY speak, but only in
12
12
  * our envelope.
13
13
  *
14
14
  * `APIErrorSchema` itself is owned by `@mailwoman/api-kit` (plumbing shared by every native
@@ -29,6 +29,20 @@ export declare const InputModeSchema: z.ZodEnum<{
29
29
  fragmented: "fragmented";
30
30
  formatted: "formatted";
31
31
  }>;
32
+ /**
33
+ * Longest accepted `address`, in characters.
34
+ *
35
+ * Sized against what the model can actually read, not against a guess at abuse. The classifier's window is 128
36
+ * SentencePiece pieces — roughly 330 characters of address text — and everything past it is truncated before inference,
37
+ * so input beyond this bound cannot influence a result. The margin over that window leaves room for scripts that
38
+ * tokenize denser than Latin, and for the department-and-division prefixes web forms concatenate.
39
+ *
40
+ * The bound exists because preprocessing is linear but not free: a 1 MB body costs ~1.7 s across normalize, query-shape
41
+ * and the phrase grouper, and Node runs them on the one thread every other request is waiting on. A cap here is cheaper
42
+ * than fairness plumbing, and rejecting is more honest than accepting a body whose tail the parser will silently
43
+ * discard.
44
+ */
45
+ export declare const MAX_ADDRESS_LENGTH = 1024;
32
46
  /**
33
47
  * `POST /v1/parse` request body.
34
48
  */
@@ -101,6 +115,7 @@ export declare const GeocodeOutcomeSchema: z.ZodObject<{
101
115
  street: z.ZodNullable<z.ZodString>;
102
116
  venue: z.ZodNullable<z.ZodString>;
103
117
  dependent_locality: z.ZodNullable<z.ZodString>;
118
+ unit: z.ZodNullable<z.ZodString>;
104
119
  countryCode: z.ZodNullable<z.ZodString>;
105
120
  hierarchy: z.ZodArray<z.ZodObject<{
106
121
  tag: z.ZodString;
@@ -118,6 +133,7 @@ export declare const GeocodeOutcomeSchema: z.ZodObject<{
118
133
  countryCode: z.ZodNullable<z.ZodString>;
119
134
  placeID: z.ZodOptional<z.ZodString>;
120
135
  }, z.core.$strip>>;
136
+ postcode_country_scope: z.ZodNullable<z.ZodString>;
121
137
  }, z.core.$loose>;
122
138
  /**
123
139
  * `POST /v1/batch` request body.
@@ -151,6 +167,7 @@ export declare const BatchResponseSchema: z.ZodObject<{
151
167
  street: z.ZodNullable<z.ZodString>;
152
168
  venue: z.ZodNullable<z.ZodString>;
153
169
  dependent_locality: z.ZodNullable<z.ZodString>;
170
+ unit: z.ZodNullable<z.ZodString>;
154
171
  countryCode: z.ZodNullable<z.ZodString>;
155
172
  hierarchy: z.ZodArray<z.ZodObject<{
156
173
  tag: z.ZodString;
@@ -168,6 +185,7 @@ export declare const BatchResponseSchema: z.ZodObject<{
168
185
  countryCode: z.ZodNullable<z.ZodString>;
169
186
  placeID: z.ZodOptional<z.ZodString>;
170
187
  }, z.core.$strip>>;
188
+ postcode_country_scope: z.ZodNullable<z.ZodString>;
171
189
  }, z.core.$loose>, z.ZodObject<{
172
190
  input: z.ZodString;
173
191
  error: z.ZodString;
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,mBAAmB,CAAA;AAErC,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEnD;;GAEG;AACH;;;;GAIG;AACH,eAAO,MAAM,eAAe;;;EAA2D,CAAA;AAEvF;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;;;;;iBAMN,CAAA;AAEzB;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;iBAA6E,CAAA;AAE9G;;;;GAIG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;iBAON,CAAA;AAEzB;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;;;;iBAKN,CAAA;AAiC3B;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqBN,CAAA;AAE3B;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;;;;iBAQN,CAAA;AAYzB;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAIN,CAAA;AAE1B;;;GAGG;AACH,eAAO,MAAM,oBAAoB;;;;;iBAKN,CAAA;AAE3B;;GAEG;AACH,eAAO,MAAM,qBAAqB;;;;iBAIN,CAAA;AAQ5B;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB;;;;iBAMN,CAAA;AAE1B;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;iBAKN,CAAA;AAE3B;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB;;;iBAMN,CAAA"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,mBAAmB,CAAA;AAErC,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEnD;;GAEG;AACH;;;;GAIG;AACH,eAAO,MAAM,eAAe;;;EAA2D,CAAA;AAEvF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,OAAO,CAAA;AAEtC;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;;;;;iBAMN,CAAA;AAEzB;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;iBAA6E,CAAA;AAE9G;;;;GAIG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;iBAON,CAAA;AAEzB;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;;;;iBAKN,CAAA;AAiC3B;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2BN,CAAA;AAE3B;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;;;;iBAUN,CAAA;AAYzB;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAIN,CAAA;AAE1B;;;GAGG;AACH,eAAO,MAAM,oBAAoB;;;;;iBAKN,CAAA;AAE3B;;GAEG;AACH,eAAO,MAAM,qBAAqB;;;;iBAIN,CAAA;AAQ5B;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB;;;;iBAMN,CAAA;AAE1B;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;iBAKN,CAAA;AAE3B;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB;;;iBAMN,CAAA"}
package/out/schema.js CHANGED
@@ -6,9 +6,9 @@
6
6
  * Zod wire schemas for the native `/v1` surface. Unlike the drop-ins (photon, nominatim,
7
7
  * libpostal), nothing here is a vendor contract — this surface is ours to design, so request
8
8
  * bodies are REQUIRED and validator-enforced (no legacy tolerance to preserve). A `defaultHook`
9
- * on the app (wired in Task 3) maps validation failures through the shared `APIErrorSchema`
10
- * envelope (`apiError(c, 400, "invalid request body", <zod summary>)`) — the documented pattern
11
- * boundary from phase 2: where no legacy contract exists, the validator MAY speak, but only in
9
+ * on the app maps validation failures through the shared `APIErrorSchema` envelope
10
+ * (`apiError(c, 400, "invalid request body", <zod summary>)`) — the pattern boundary every
11
+ * surface holds to: where no legacy contract exists, the validator MAY speak, but only in
12
12
  * our envelope.
13
13
  *
14
14
  * `APIErrorSchema` itself is owned by `@mailwoman/api-kit` (plumbing shared by every native
@@ -26,12 +26,26 @@ export { APIErrorSchema } from "@mailwoman/api-kit";
26
26
  * `/v1/batch` defaults to `formatted` (batch rows are the record register by nature).
27
27
  */
28
28
  export const InputModeSchema = z.enum(["fragmented", "formatted"]).openapi("InputMode");
29
+ /**
30
+ * Longest accepted `address`, in characters.
31
+ *
32
+ * Sized against what the model can actually read, not against a guess at abuse. The classifier's window is 128
33
+ * SentencePiece pieces — roughly 330 characters of address text — and everything past it is truncated before inference,
34
+ * so input beyond this bound cannot influence a result. The margin over that window leaves room for scripts that
35
+ * tokenize denser than Latin, and for the department-and-division prefixes web forms concatenate.
36
+ *
37
+ * The bound exists because preprocessing is linear but not free: a 1 MB body costs ~1.7 s across normalize, query-shape
38
+ * and the phrase grouper, and Node runs them on the one thread every other request is waiting on. A cap here is cheaper
39
+ * than fairness plumbing, and rejecting is more honest than accepting a body whose tail the parser will silently
40
+ * discard.
41
+ */
42
+ export const MAX_ADDRESS_LENGTH = 1024;
29
43
  /**
30
44
  * `POST /v1/parse` request body.
31
45
  */
32
46
  export const ParseRequestSchema = z
33
47
  .object({
34
- address: z.string(),
48
+ address: z.string().max(MAX_ADDRESS_LENGTH),
35
49
  debug: z.boolean().optional(),
36
50
  input_mode: InputModeSchema.optional(),
37
51
  })
@@ -58,7 +72,7 @@ export const ParseOutcomeSchema = z
58
72
  */
59
73
  export const GeocodeRequestSchema = z
60
74
  .object({
61
- address: z.string(),
75
+ address: z.string().max(MAX_ADDRESS_LENGTH),
62
76
  input_mode: InputModeSchema.optional(),
63
77
  })
64
78
  .openapi("GeocodeRequest");
@@ -117,9 +131,15 @@ export const GeocodeOutcomeSchema = z
117
131
  venue: z.string().nullable(),
118
132
  // The parsed dependent-locality span (parse view; `hierarchy` is the resolved view).
119
133
  dependent_locality: z.string().nullable(),
134
+ // The parsed unit / sub-venue span (parse view) — "Terminal 5", "Suite 300".
135
+ unit: z.string().nullable(),
120
136
  countryCode: z.string().nullable(),
121
137
  hierarchy: z.array(GeocodeHierarchyEntrySchema),
122
138
  candidates: z.array(GeocodeCandidateSchema),
139
+ // #42: the country the postcode-country coherence pass scoped the walk to, or null. Non-null ONLY when it
140
+ // OVERRODE the request's country prior — so a caller who asked for US and got an FR answer can see which
141
+ // evidence bought the change instead of reading it as a bug.
142
+ postcode_country_scope: z.string().nullable(),
123
143
  })
124
144
  .loose()
125
145
  .openapi("GeocodeOutcome");
@@ -128,7 +148,9 @@ export const GeocodeOutcomeSchema = z
128
148
  */
129
149
  export const BatchRequestSchema = z
130
150
  .object({
131
- addresses: z.array(z.string()),
151
+ // Per-ROW, not just per-request: the row cap (`batchMax`, default 500) bounds how many addresses arrive,
152
+ // and this bounds how large each may be. Without both, one request is 500 unbounded bodies.
153
+ addresses: z.array(z.string().max(MAX_ADDRESS_LENGTH)),
132
154
  /**
133
155
  * Register override for every row. DEFAULT `"formatted"` — batch rows are the record register by nature.
134
156
  */
package/out/schema.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../schema.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mailwoman/api",
3
- "version": "8.6.0",
3
+ "version": "9.0.0",
4
4
  "description": "The native Mailwoman HTTP API — engine-agnostic /v1 surface (parse, geocode, batch, resolve, format) with health, metrics, and an emitted OpenAPI document.",
5
5
  "license": "AGPL-3.0-only OR LicenseRef-Commercial",
6
6
  "repository": {
@@ -43,9 +43,9 @@
43
43
  },
44
44
  "dependencies": {
45
45
  "@hono/zod-openapi": "^1.5.1",
46
- "@mailwoman/api-kit": "8.6.0",
47
- "@mailwoman/core": "8.6.0",
48
- "@mailwoman/formatter": "8.6.0",
46
+ "@mailwoman/api-kit": "9.0.0",
47
+ "@mailwoman/core": "9.0.0",
48
+ "@mailwoman/formatter": "9.0.0",
49
49
  "hono": "^4.12.32",
50
50
  "zod": "^4.4.3"
51
51
  }
package/schema.ts CHANGED
@@ -6,9 +6,9 @@
6
6
  * Zod wire schemas for the native `/v1` surface. Unlike the drop-ins (photon, nominatim,
7
7
  * libpostal), nothing here is a vendor contract — this surface is ours to design, so request
8
8
  * bodies are REQUIRED and validator-enforced (no legacy tolerance to preserve). A `defaultHook`
9
- * on the app (wired in Task 3) maps validation failures through the shared `APIErrorSchema`
10
- * envelope (`apiError(c, 400, "invalid request body", <zod summary>)`) — the documented pattern
11
- * boundary from phase 2: where no legacy contract exists, the validator MAY speak, but only in
9
+ * on the app maps validation failures through the shared `APIErrorSchema` envelope
10
+ * (`apiError(c, 400, "invalid request body", <zod summary>)`) — the pattern boundary every
11
+ * surface holds to: where no legacy contract exists, the validator MAY speak, but only in
12
12
  * our envelope.
13
13
  *
14
14
  * `APIErrorSchema` itself is owned by `@mailwoman/api-kit` (plumbing shared by every native
@@ -30,12 +30,27 @@ export { APIErrorSchema } from "@mailwoman/api-kit"
30
30
  */
31
31
  export const InputModeSchema = z.enum(["fragmented", "formatted"]).openapi("InputMode")
32
32
 
33
+ /**
34
+ * Longest accepted `address`, in characters.
35
+ *
36
+ * Sized against what the model can actually read, not against a guess at abuse. The classifier's window is 128
37
+ * SentencePiece pieces — roughly 330 characters of address text — and everything past it is truncated before inference,
38
+ * so input beyond this bound cannot influence a result. The margin over that window leaves room for scripts that
39
+ * tokenize denser than Latin, and for the department-and-division prefixes web forms concatenate.
40
+ *
41
+ * The bound exists because preprocessing is linear but not free: a 1 MB body costs ~1.7 s across normalize, query-shape
42
+ * and the phrase grouper, and Node runs them on the one thread every other request is waiting on. A cap here is cheaper
43
+ * than fairness plumbing, and rejecting is more honest than accepting a body whose tail the parser will silently
44
+ * discard.
45
+ */
46
+ export const MAX_ADDRESS_LENGTH = 1024
47
+
33
48
  /**
34
49
  * `POST /v1/parse` request body.
35
50
  */
36
51
  export const ParseRequestSchema = z
37
52
  .object({
38
- address: z.string(),
53
+ address: z.string().max(MAX_ADDRESS_LENGTH),
39
54
  debug: z.boolean().optional(),
40
55
  input_mode: InputModeSchema.optional(),
41
56
  })
@@ -65,7 +80,7 @@ export const ParseOutcomeSchema = z
65
80
  */
66
81
  export const GeocodeRequestSchema = z
67
82
  .object({
68
- address: z.string(),
83
+ address: z.string().max(MAX_ADDRESS_LENGTH),
69
84
  input_mode: InputModeSchema.optional(),
70
85
  })
71
86
  .openapi("GeocodeRequest")
@@ -127,9 +142,15 @@ export const GeocodeOutcomeSchema = z
127
142
  venue: z.string().nullable(),
128
143
  // The parsed dependent-locality span (parse view; `hierarchy` is the resolved view).
129
144
  dependent_locality: z.string().nullable(),
145
+ // The parsed unit / sub-venue span (parse view) — "Terminal 5", "Suite 300".
146
+ unit: z.string().nullable(),
130
147
  countryCode: z.string().nullable(),
131
148
  hierarchy: z.array(GeocodeHierarchyEntrySchema),
132
149
  candidates: z.array(GeocodeCandidateSchema),
150
+ // #42: the country the postcode-country coherence pass scoped the walk to, or null. Non-null ONLY when it
151
+ // OVERRODE the request's country prior — so a caller who asked for US and got an FR answer can see which
152
+ // evidence bought the change instead of reading it as a bug.
153
+ postcode_country_scope: z.string().nullable(),
133
154
  })
134
155
  .loose()
135
156
  .openapi("GeocodeOutcome")
@@ -139,7 +160,9 @@ export const GeocodeOutcomeSchema = z
139
160
  */
140
161
  export const BatchRequestSchema = z
141
162
  .object({
142
- addresses: z.array(z.string()),
163
+ // Per-ROW, not just per-request: the row cap (`batchMax`, default 500) bounds how many addresses arrive,
164
+ // and this bounds how large each may be. Without both, one request is 500 unbounded bodies.
165
+ addresses: z.array(z.string().max(MAX_ADDRESS_LENGTH)),
143
166
  /**
144
167
  * Register override for every row. DEFAULT `"formatted"` — batch rows are the record register by nature.
145
168
  */