@mailwoman/api 9.2.0 → 9.4.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/{app.ts → lib/app.ts} +27 -10
- package/{engine.ts → lib/engine.ts} +2 -2
- package/{index.ts → lib/index.ts} +4 -4
- package/{routes.ts → lib/routes.ts} +35 -22
- package/{schema.ts → lib/schema.ts} +125 -15
- package/out/app.d.ts +9 -2
- package/out/app.d.ts.map +1 -1
- package/out/app.js +10 -7
- package/out/app.js.map +1 -1
- package/out/engine.d.ts +2 -2
- package/out/engine.d.ts.map +1 -1
- package/out/engine.js.map +1 -1
- package/out/index.d.ts +4 -4
- package/out/index.d.ts.map +1 -1
- package/out/index.js +4 -4
- package/out/index.js.map +1 -1
- package/out/routes.d.ts +8 -3
- package/out/routes.d.ts.map +1 -1
- package/out/routes.js +20 -19
- package/out/routes.js.map +1 -1
- package/out/schema.d.ts +341 -11
- package/out/schema.d.ts.map +1 -1
- package/out/schema.js +112 -14
- package/out/schema.js.map +1 -1
- package/package.json +23 -9
package/{app.ts → lib/app.ts}
RENAMED
|
@@ -9,15 +9,20 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { OpenAPIHono } from "@hono/zod-openapi"
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
attachOpenAPIDocs,
|
|
14
|
+
engineHeaders,
|
|
15
|
+
errorResponse,
|
|
16
|
+
type OpenAPIDocInfo,
|
|
17
|
+
readServedDocumentInfo,
|
|
18
|
+
} from "@mailwoman/api-kit"
|
|
19
|
+
import type { EngineStamp } from "@mailwoman/core/license"
|
|
13
20
|
import { bodyLimit } from "hono/body-limit"
|
|
14
21
|
import { cors } from "hono/cors"
|
|
15
22
|
|
|
16
|
-
import
|
|
17
|
-
|
|
18
|
-
import type {
|
|
19
|
-
import { DEFAULT_BATCH_MAX, registerMailwomanAPIRoutes } from "./routes.ts"
|
|
20
|
-
import type { GeocodeOutcomeLike } from "./schema.ts"
|
|
23
|
+
import type { MailwomanAPIEngine } from "#engine"
|
|
24
|
+
import { DEFAULT_BATCH_MAX, registerMailwomanAPIRoutes } from "#routes"
|
|
25
|
+
import type { GeocodeOutcomeLike } from "#schema"
|
|
21
26
|
|
|
22
27
|
/**
|
|
23
28
|
* 2 MiB — carried from the express server's `express.json({ limit: "2mb" })` (`mailwoman/server/index.ts`).
|
|
@@ -45,6 +50,13 @@ export interface MailwomanAPIOptions {
|
|
|
45
50
|
* Max `addresses` rows accepted by `POST /v1/batch`. Default 500 (see `routes.ts`'s `DEFAULT_BATCH_MAX`).
|
|
46
51
|
*/
|
|
47
52
|
batchMax?: number
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The engine stamp to carry on every response: `engine` in each `/v1` body and the `Server` + `Link: rel="license"`
|
|
56
|
+
* headers everywhere. Absent when an embedding application builds the app without the `mailwoman` package; the
|
|
57
|
+
* `mailwoman serve` command always passes one.
|
|
58
|
+
*/
|
|
59
|
+
engine?: EngineStamp
|
|
48
60
|
}
|
|
49
61
|
|
|
50
62
|
/**
|
|
@@ -61,9 +73,7 @@ function summarizeValidationError(error: { issues: Array<{ path: PropertyKey[];
|
|
|
61
73
|
* {@link attachOpenAPIDocs}) uses — one source of truth, no risk of the two drifting.
|
|
62
74
|
*/
|
|
63
75
|
export const MAILWOMAN_API_DOC_INFO: OpenAPIDocInfo = {
|
|
64
|
-
|
|
65
|
-
version: packageJson.version,
|
|
66
|
-
description: packageJson.description,
|
|
76
|
+
...(await readServedDocumentInfo(import.meta.url, "@mailwoman/api")),
|
|
67
77
|
license: { name: "AGPL-3.0-only OR LicenseRef-Commercial", identifier: "AGPL-3.0-only" },
|
|
68
78
|
contact: { name: "Sister Software", url: "https://mailwoman.ai" },
|
|
69
79
|
servers: [
|
|
@@ -110,6 +120,10 @@ export function createMailwomanAPI<T extends Partial<GeocodeOutcomeLike> = Geoco
|
|
|
110
120
|
app.use(cors({ origin: "*", allowMethods: ["GET", "POST", "OPTIONS"], allowHeaders: ["*"], maxAge: 86_400 }))
|
|
111
121
|
}
|
|
112
122
|
|
|
123
|
+
if (options.engine) {
|
|
124
|
+
app.use(engineHeaders(options.engine))
|
|
125
|
+
}
|
|
126
|
+
|
|
113
127
|
// Safety net: an engine fault answers the native envelope, never a crash. `detail` carries the raw message —
|
|
114
128
|
// this surface is ours to design, so (unlike the vendor-constrained drop-in envelopes) we can be helpful.
|
|
115
129
|
app.onError((error, c) => {
|
|
@@ -133,7 +147,10 @@ export function createMailwomanAPI<T extends Partial<GeocodeOutcomeLike> = Geoco
|
|
|
133
147
|
})
|
|
134
148
|
)
|
|
135
149
|
|
|
136
|
-
registerMailwomanAPIRoutes(app, engine, {
|
|
150
|
+
registerMailwomanAPIRoutes(app, engine, {
|
|
151
|
+
batchMax: options.batchMax ?? DEFAULT_BATCH_MAX,
|
|
152
|
+
engine: options.engine,
|
|
153
|
+
})
|
|
137
154
|
|
|
138
155
|
attachOpenAPIDocs(app, MAILWOMAN_API_DOC_INFO)
|
|
139
156
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import type { AddressTree } from "@mailwoman/core"
|
|
12
12
|
|
|
13
|
-
import type { GeocodeOutcomeLike } from "
|
|
13
|
+
import type { GeocodeOutcomeLike } from "#schema"
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* One parsed component in reading order (a `ComponentTag` + the covered text).
|
|
@@ -71,5 +71,5 @@ export interface MailwomanAPIEngine<T extends Partial<GeocodeOutcomeLike> = Geoc
|
|
|
71
71
|
batch?(addresses: string[], opts?: ParseInit): Promise<{ results: BatchResultEntry<T>[] }>
|
|
72
72
|
resolveTree?(tree: AddressTree, opts: Record<string, unknown>): Promise<ResolveTreeOutcome>
|
|
73
73
|
reload?(): Promise<{ reloaded: boolean; versions: unknown }>
|
|
74
|
-
health?(): HealthData
|
|
74
|
+
health?(): Promise<HealthData>
|
|
75
75
|
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* engine contract in `engine.ts`; the zod wire schemas in `schema.ts`.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
export * from "
|
|
20
|
-
export * from "
|
|
21
|
-
export * from "
|
|
22
|
-
export * from "
|
|
19
|
+
export * from "#app"
|
|
20
|
+
export * from "#engine"
|
|
21
|
+
export * from "#routes"
|
|
22
|
+
export * from "#schema"
|
|
@@ -20,14 +20,21 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { createRoute, type OpenAPIHono, z } from "@hono/zod-openapi"
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
geocoderUnavailableError,
|
|
25
|
+
metricsSnapshot,
|
|
26
|
+
recordTimed,
|
|
27
|
+
stampedResponseSchema,
|
|
28
|
+
withEngineStamp,
|
|
29
|
+
APIErrorSchema,
|
|
30
|
+
} from "@mailwoman/api-kit"
|
|
24
31
|
import type { AddressTree } from "@mailwoman/core/decoder"
|
|
32
|
+
import type { EngineStamp } from "@mailwoman/core/license"
|
|
25
33
|
import type { ComponentTag } from "@mailwoman/core/types"
|
|
26
34
|
import { canonicalKey, type ComponentDict, formatAddress, type FormatAddressOptions } from "@mailwoman/formatter"
|
|
27
35
|
|
|
28
|
-
import type { MailwomanAPIEngine } from "
|
|
36
|
+
import type { MailwomanAPIEngine } from "#engine"
|
|
29
37
|
import {
|
|
30
|
-
APIErrorSchema,
|
|
31
38
|
BatchRequestSchema,
|
|
32
39
|
BatchResponseSchema,
|
|
33
40
|
FormatRequestSchema,
|
|
@@ -40,12 +47,12 @@ import {
|
|
|
40
47
|
ResolveRequestSchema,
|
|
41
48
|
ResolveResponseSchema,
|
|
42
49
|
type GeocodeOutcome,
|
|
43
|
-
} from "
|
|
50
|
+
} from "#schema"
|
|
44
51
|
|
|
45
52
|
/**
|
|
46
53
|
* Default `POST /v1/batch` row cap when {@link RegisterMailwomanAPIRoutesOptions.batchMax} is omitted. This is the
|
|
47
54
|
* standalone-engine default, not derived from env — `mailwoman serve` always passes the env-derived value explicitly
|
|
48
|
-
* (`$public.MAILWOMAN_BATCH_MAX`, default 1000; see `
|
|
55
|
+
* (`$public.MAILWOMAN_BATCH_MAX`, default 1000; see `mailwoman/lib/env/schema.ts`).
|
|
49
56
|
*/
|
|
50
57
|
export const DEFAULT_BATCH_MAX = 500
|
|
51
58
|
|
|
@@ -59,6 +66,11 @@ export interface RegisterMailwomanAPIRoutesOptions {
|
|
|
59
66
|
* Max `addresses` rows accepted by `POST /v1/batch`. Default {@link DEFAULT_BATCH_MAX}.
|
|
60
67
|
*/
|
|
61
68
|
batchMax?: number
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The engine stamp attached as `engine` to every `/v1` success body. Absent: no field is added.
|
|
72
|
+
*/
|
|
73
|
+
engine?: EngineStamp
|
|
62
74
|
}
|
|
63
75
|
|
|
64
76
|
const errorContent = (description: string) => ({
|
|
@@ -78,7 +90,7 @@ const parseQueryParams = z.object({
|
|
|
78
90
|
const parseResponses = {
|
|
79
91
|
200: {
|
|
80
92
|
description: "The tokenized input span + ranked solutions.",
|
|
81
|
-
content: { "application/json": { schema: ParseOutcomeSchema } },
|
|
93
|
+
content: { "application/json": { schema: stampedResponseSchema(ParseOutcomeSchema, "StampedParseOutcome") } },
|
|
82
94
|
},
|
|
83
95
|
400: errorContent("`address` is required."),
|
|
84
96
|
501: errorContent("The backing engine method is not wired for this deployment."),
|
|
@@ -87,7 +99,7 @@ const parseResponses = {
|
|
|
87
99
|
const geocodeResponses = {
|
|
88
100
|
200: {
|
|
89
101
|
description: "One geocode result (parse → resolve cascade), passed through from the engine verbatim.",
|
|
90
|
-
content: { "application/json": { schema: GeocodeOutcomeSchema } },
|
|
102
|
+
content: { "application/json": { schema: stampedResponseSchema(GeocodeOutcomeSchema, "StampedGeocodeOutcome") } },
|
|
91
103
|
},
|
|
92
104
|
400: errorContent("`address` is required."),
|
|
93
105
|
503: errorContent("The geocoding engine is not wired for this deployment (dependencies missing)."),
|
|
@@ -96,7 +108,7 @@ const geocodeResponses = {
|
|
|
96
108
|
const batchResponses = {
|
|
97
109
|
200: {
|
|
98
110
|
description: "One result per input address, in input order (per-row error isolation).",
|
|
99
|
-
content: { "application/json": { schema: BatchResponseSchema } },
|
|
111
|
+
content: { "application/json": { schema: stampedResponseSchema(BatchResponseSchema, "StampedBatchResponse") } },
|
|
100
112
|
},
|
|
101
113
|
400: errorContent("Body must be `{ addresses: string[] }`."),
|
|
102
114
|
413: errorContent("`addresses.length` exceeds the configured batch cap."),
|
|
@@ -106,7 +118,7 @@ const batchResponses = {
|
|
|
106
118
|
const resolveResponses = {
|
|
107
119
|
200: {
|
|
108
120
|
description: "The same tree, decorated in place with gazetteer coordinates + attribution.",
|
|
109
|
-
content: { "application/json": { schema: ResolveResponseSchema } },
|
|
121
|
+
content: { "application/json": { schema: stampedResponseSchema(ResolveResponseSchema, "StampedResolveResponse") } },
|
|
110
122
|
},
|
|
111
123
|
400: errorContent("Body must be `{ tree: AddressTree, opts? }`."),
|
|
112
124
|
503: errorContent("The resolver is not wired for this deployment (dependencies missing)."),
|
|
@@ -114,7 +126,7 @@ const resolveResponses = {
|
|
|
114
126
|
|
|
115
127
|
const reloadResponses = {
|
|
116
128
|
200: {
|
|
117
|
-
description: "Versioned data switchover result — the new per-
|
|
129
|
+
description: "Versioned data switchover result — the new per-extract version map.",
|
|
118
130
|
content: {
|
|
119
131
|
"application/json": { schema: z.looseObject({ reloaded: z.boolean(), versions: z.unknown() }) },
|
|
120
132
|
},
|
|
@@ -125,7 +137,7 @@ const reloadResponses = {
|
|
|
125
137
|
const formatResponses = {
|
|
126
138
|
200: {
|
|
127
139
|
description: "The rendered address string + the deterministic canonical match key.",
|
|
128
|
-
content: { "application/json": { schema: FormatResponseSchema } },
|
|
140
|
+
content: { "application/json": { schema: stampedResponseSchema(FormatResponseSchema, "StampedFormatResponse") } },
|
|
129
141
|
},
|
|
130
142
|
400: errorContent("Invalid request body."),
|
|
131
143
|
}
|
|
@@ -198,7 +210,7 @@ const reloadRoute = createRoute({
|
|
|
198
210
|
method: "post",
|
|
199
211
|
path: "/v1/reload",
|
|
200
212
|
operationId: "reload",
|
|
201
|
-
summary: "Reload versioned data
|
|
213
|
+
summary: "Reload versioned data extracts (deploy-only; check at ingress)",
|
|
202
214
|
tags: ["meta"],
|
|
203
215
|
responses: reloadResponses,
|
|
204
216
|
})
|
|
@@ -259,6 +271,7 @@ export function registerMailwomanAPIRoutes<T extends Partial<GeocodeOutcome> = G
|
|
|
259
271
|
options: RegisterMailwomanAPIRoutesOptions = {}
|
|
260
272
|
): void {
|
|
261
273
|
const batchMax = options.batchMax ?? DEFAULT_BATCH_MAX
|
|
274
|
+
const stamp = options.engine
|
|
262
275
|
|
|
263
276
|
app.openapi(parseGetRoute, async (c) => {
|
|
264
277
|
if (!engine.parse) return c.json({ error: "parse not implemented" }, 501)
|
|
@@ -271,7 +284,7 @@ export function registerMailwomanAPIRoutes<T extends Partial<GeocodeOutcome> = G
|
|
|
271
284
|
const inputMode = inputModeRaw === "fragmented" || inputModeRaw === "formatted" ? inputModeRaw : undefined
|
|
272
285
|
const outcome = await engine.parse(address, { debug, inputMode })
|
|
273
286
|
|
|
274
|
-
return c.json(outcome, 200)
|
|
287
|
+
return c.json(withEngineStamp(outcome, stamp), 200)
|
|
275
288
|
})
|
|
276
289
|
|
|
277
290
|
app.openapi(
|
|
@@ -290,7 +303,7 @@ export function registerMailwomanAPIRoutes<T extends Partial<GeocodeOutcome> = G
|
|
|
290
303
|
|
|
291
304
|
const outcome = await engine.parse(trimmed, { debug: debug ?? false, inputMode: input_mode })
|
|
292
305
|
|
|
293
|
-
return c.json(outcome, 200)
|
|
306
|
+
return c.json(withEngineStamp(outcome, stamp), 200)
|
|
294
307
|
},
|
|
295
308
|
(result, c) => {
|
|
296
309
|
if (!result.success) return c.json({ error: "address is required" }, 400)
|
|
@@ -317,7 +330,7 @@ export function registerMailwomanAPIRoutes<T extends Partial<GeocodeOutcome> = G
|
|
|
317
330
|
.then((outcome) => {
|
|
318
331
|
recordTimed(performance.now() - t0, String(outcome.resolution_tier ?? "admin"))
|
|
319
332
|
|
|
320
|
-
return c.json(outcome as
|
|
333
|
+
return c.json(withEngineStamp(outcome as GeocodeOutcome, stamp), 200)
|
|
321
334
|
})
|
|
322
335
|
.catch((error) => {
|
|
323
336
|
recordTimed(performance.now() - t0, "error")
|
|
@@ -339,7 +352,7 @@ export function registerMailwomanAPIRoutes<T extends Partial<GeocodeOutcome> = G
|
|
|
339
352
|
async (c) => {
|
|
340
353
|
const { addresses, input_mode } = c.req.valid("json")
|
|
341
354
|
|
|
342
|
-
if (!addresses.length) return c.json({ results: [] }, 200)
|
|
355
|
+
if (!addresses.length) return c.json(withEngineStamp({ results: [] }, stamp), 200)
|
|
343
356
|
|
|
344
357
|
if (addresses.length > batchMax) {
|
|
345
358
|
return c.json({ error: `batch too large: ${addresses.length} > ${batchMax}` }, 413)
|
|
@@ -361,7 +374,7 @@ export function registerMailwomanAPIRoutes<T extends Partial<GeocodeOutcome> = G
|
|
|
361
374
|
// Same wire-vs-domain cast as `/v1/geocode` above — `BatchRow`'s `GeocodeOutcome` half is a
|
|
362
375
|
// `Record<string, unknown>` passthrough; `BatchResponseSchema` now types its `GeocodeOutcome` union
|
|
363
376
|
// member as the real shape.
|
|
364
|
-
return c.json(outcome as
|
|
377
|
+
return c.json(withEngineStamp(outcome as z.infer<typeof BatchResponseSchema>, stamp), 200)
|
|
365
378
|
} catch (error) {
|
|
366
379
|
recordTimed(performance.now() - t0, "error")
|
|
367
380
|
throw error
|
|
@@ -388,9 +401,9 @@ export function registerMailwomanAPIRoutes<T extends Partial<GeocodeOutcome> = G
|
|
|
388
401
|
|
|
389
402
|
const { tree, opts } = c.req.valid("json")
|
|
390
403
|
|
|
391
|
-
const outcome = await engine.resolveTree(tree as
|
|
404
|
+
const outcome = await engine.resolveTree(tree as AddressTree, opts ?? {})
|
|
392
405
|
|
|
393
|
-
return c.json(outcome, 200)
|
|
406
|
+
return c.json(withEngineStamp(outcome, stamp), 200)
|
|
394
407
|
},
|
|
395
408
|
(result, c) => {
|
|
396
409
|
if (!result.success) return c.json({ error: "body must be { tree: AddressTree, opts? }" }, 400)
|
|
@@ -414,13 +427,13 @@ export function registerMailwomanAPIRoutes<T extends Partial<GeocodeOutcome> = G
|
|
|
414
427
|
const dict = toComponentDict(components)
|
|
415
428
|
const formatted = formatAddress(dict, country, formatOptions as FormatAddressOptions | undefined)
|
|
416
429
|
|
|
417
|
-
return c.json({ formatted, canonicalKey: canonicalKey(dict) }, 200)
|
|
430
|
+
return c.json(withEngineStamp({ formatted, canonicalKey: canonicalKey(dict) }, stamp), 200)
|
|
418
431
|
})
|
|
419
432
|
|
|
420
|
-
app.openapi(healthRoute, (c) => {
|
|
433
|
+
app.openapi(healthRoute, async (c) => {
|
|
421
434
|
const uptimeSeconds = Math.round((Date.now() - startedAt) / 1000)
|
|
422
435
|
|
|
423
|
-
return c.json({ status: "ok", uptime_s: uptimeSeconds, ...engine.health?.() }, 200)
|
|
436
|
+
return c.json({ status: "ok", uptime_s: uptimeSeconds, ...(await engine.health?.()) }, 200)
|
|
424
437
|
})
|
|
425
438
|
|
|
426
439
|
app.openapi(metricsRoute, (c) => c.json(metricsSnapshot(), 200))
|
|
@@ -17,12 +17,23 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { z } from "@hono/zod-openapi"
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
import type { AddressNode } from "@mailwoman/core/decoder"
|
|
21
|
+
import type { DerivationProjection, Evidence } from "@mailwoman/evidence"
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* `POST /v1/parse` request body.
|
|
25
25
|
*/
|
|
26
|
+
/**
|
|
27
|
+
* One node of the decoded address tree. The decoder's `AddressNode` is a recursive union the OpenAPI generator cannot
|
|
28
|
+
* derive a schema for on its own, so it is registered as an open object; the shape is documented by the type.
|
|
29
|
+
*/
|
|
30
|
+
export const AddressNodeSchema = z.custom<AddressNode>().openapi("AddressNode", {
|
|
31
|
+
type: "object",
|
|
32
|
+
additionalProperties: true,
|
|
33
|
+
description:
|
|
34
|
+
"A decoded address-tree node: a tag, its span, and its children. See `AddressNode` in `@mailwoman/core/decoder`.",
|
|
35
|
+
})
|
|
36
|
+
|
|
26
37
|
/**
|
|
27
38
|
* The input register (Decision A / GTM B10): `fragmented` = the map-search register (evidence-bundle channels feed);
|
|
28
39
|
* `formatted` = the validation/record register (channels off). Unset → the engine derives it from the input's shape.
|
|
@@ -70,7 +81,7 @@ export const ParseOutcomeSchema = z
|
|
|
70
81
|
.object({
|
|
71
82
|
input: z.string(),
|
|
72
83
|
components: z.array(ParseComponentSchema),
|
|
73
|
-
tree: z.looseObject({
|
|
84
|
+
tree: z.looseObject({ raw: z.string(), roots: z.array(AddressNodeSchema) }),
|
|
74
85
|
debug: z.string().optional(),
|
|
75
86
|
})
|
|
76
87
|
.openapi("ParseOutcome")
|
|
@@ -150,6 +161,7 @@ const ComponentTagSchema = z.enum([
|
|
|
150
161
|
"sub_block",
|
|
151
162
|
"building_number",
|
|
152
163
|
"building_name",
|
|
164
|
+
"locality_unit",
|
|
153
165
|
])
|
|
154
166
|
|
|
155
167
|
/**
|
|
@@ -186,13 +198,80 @@ const QueryIntentMarkerSchema = z
|
|
|
186
198
|
"near_me",
|
|
187
199
|
"poi_category",
|
|
188
200
|
]),
|
|
189
|
-
code: z.enum([
|
|
201
|
+
code: z.enum([
|
|
202
|
+
"declared_ambiguity",
|
|
203
|
+
"declared_fork",
|
|
204
|
+
"focus_point_required",
|
|
205
|
+
"poi_category",
|
|
206
|
+
"coverage_qualified_absence",
|
|
207
|
+
"authority_designation",
|
|
208
|
+
]),
|
|
190
209
|
mechanism: z.string(),
|
|
191
210
|
message: z.string(),
|
|
192
211
|
evidence: z.record(z.string(), z.unknown()).optional(),
|
|
193
212
|
})
|
|
194
213
|
.openapi("QueryIntentMarker")
|
|
195
214
|
|
|
215
|
+
/**
|
|
216
|
+
* One authoritative-provider match on the wire (#1901) — hoisted so the outcome schema below stays inside the
|
|
217
|
+
* call-nesting bound. Field-for-field mirror of `mailwoman/authoritative.ts`'s `AuthoritativeAssertionMatch`.
|
|
218
|
+
*/
|
|
219
|
+
const AuthoritativeMatchSchema = z.object({
|
|
220
|
+
provider_place_id: z.string(),
|
|
221
|
+
object_ids: z.record(z.string(), z.string()).optional(),
|
|
222
|
+
canonical_fields: z.record(z.string(), z.string()).optional(),
|
|
223
|
+
lat: z.number().optional(),
|
|
224
|
+
lon: z.number().optional(),
|
|
225
|
+
precision: z.string().optional(),
|
|
226
|
+
match_status: z.enum(["exact", "approximate"]),
|
|
227
|
+
provider_score: z.number().optional(),
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
const EpistemicStatusSchema = z.enum(["designated", "observed", "derived", "inferred", "unresolved"])
|
|
231
|
+
|
|
232
|
+
const CoverageBasisSchema = z.enum(["designated", "surveyed", "source_present"])
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* `@mailwoman/evidence`'s `Evidence` union, spelled for the wire. The `EvidencePin` below fails to compile the moment
|
|
236
|
+
* either side gains, loses or retypes a field.
|
|
237
|
+
*/
|
|
238
|
+
const EvidenceSchema = z.discriminatedUnion("kind", [
|
|
239
|
+
z.object({ kind: z.literal("observation"), source: z.string(), vintage: z.string().nullable(), value: z.unknown() }),
|
|
240
|
+
z.object({
|
|
241
|
+
kind: z.literal("exclusion"),
|
|
242
|
+
source: z.string(),
|
|
243
|
+
vintage: z.string(),
|
|
244
|
+
scope: z.object({ layer: z.string(), h3Cell: z.number(), basis: CoverageBasisSchema, fold: z.string() }),
|
|
245
|
+
}),
|
|
246
|
+
z.object({
|
|
247
|
+
kind: z.literal("relation"),
|
|
248
|
+
source: z.string(),
|
|
249
|
+
vintage: z.string(),
|
|
250
|
+
relationship: z.string(),
|
|
251
|
+
assertion: z.enum(["authoritative", "inferred"]),
|
|
252
|
+
score: z.number().optional(),
|
|
253
|
+
}),
|
|
254
|
+
z.object({ kind: z.literal("prior"), source: z.string(), label: z.string(), weight: z.number() }),
|
|
255
|
+
])
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* The derivation behind a geocode answer, present only when the engine was asked to trace — `@mailwoman/evidence`'s
|
|
259
|
+
* `DerivationProjection` on the wire.
|
|
260
|
+
*/
|
|
261
|
+
export const DerivationProjectionSchema = z.object({
|
|
262
|
+
status: EpistemicStatusSchema,
|
|
263
|
+
constraints: z.array(z.object({ label: z.string(), evidence: EvidenceSchema, contribution: z.string() })).readonly(),
|
|
264
|
+
uncertaintyM: z.number().nullable(),
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
// Both directions: the schema's inferred type is exactly the evidence package's, or this does not compile.
|
|
268
|
+
type Mutual<A, B> = [A] extends [B] ? ([B] extends [A] ? true : false) : false
|
|
269
|
+
const evidencePin: Mutual<z.infer<typeof EvidenceSchema>, Evidence> = true
|
|
270
|
+
const derivationPin: Mutual<z.infer<typeof DerivationProjectionSchema>, DerivationProjection> = true
|
|
271
|
+
|
|
272
|
+
void evidencePin
|
|
273
|
+
void derivationPin
|
|
274
|
+
|
|
196
275
|
/**
|
|
197
276
|
* `POST /v1/geocode` response — a hand-modeled mirror of `GeocodeResult`'s wire shape (`mailwoman/geocode-core.ts`),
|
|
198
277
|
* `.loose()` so a field the engine adds that this schema doesn't yet know about still rides through undocumented rather
|
|
@@ -200,8 +279,8 @@ const QueryIntentMarkerSchema = z
|
|
|
200
279
|
* (`GeocodeOutcome = Record<string, unknown>`, `api/engine.ts`) — nothing here validates a real response, so a
|
|
201
280
|
* schema/engine mismatch can never reject or mutate a result at runtime. Deliberately carries NO import from
|
|
202
281
|
* `mailwoman` (the engine-agnosticism boundary — `mailwoman` is the one workspace allowed to depend on
|
|
203
|
-
* `@mailwoman/api`, never the reverse). `mailwoman/test/api-schema-drift.test.ts` is the compile-time
|
|
204
|
-
* catches this shape drifting from the real `GeocodeResult` interface.
|
|
282
|
+
* `@mailwoman/api`, never the reverse). `mailwoman/test/api-schema-drift.test.ts` is the compile-time regression check
|
|
283
|
+
* that catches this shape drifting from the real `GeocodeResult` interface.
|
|
205
284
|
*/
|
|
206
285
|
export const GeocodeOutcomeLikeSchema = z.object({
|
|
207
286
|
input: z.string(),
|
|
@@ -209,6 +288,12 @@ export const GeocodeOutcomeLikeSchema = z.object({
|
|
|
209
288
|
lat: z.number().nullable(),
|
|
210
289
|
lon: z.number().nullable(),
|
|
211
290
|
resolution_tier: z.enum(["address_point", "interpolated", "street", "admin", "venue", "plus_code"]),
|
|
291
|
+
// What the evidence permits a consumer to claim about the coordinate, orthogonal to how it was produced; see
|
|
292
|
+
// `@mailwoman/evidence`'s `EpistemicStatus`.
|
|
293
|
+
epistemic_status: z.enum(["designated", "observed", "derived", "inferred", "unresolved"]),
|
|
294
|
+
// The derivation behind the answer, present only when the engine was asked to trace. `DerivationProjectionSchema` is
|
|
295
|
+
// pinned to `@mailwoman/evidence`'s types below, so the wire contract and the evidence union cannot drift apart.
|
|
296
|
+
derivation: DerivationProjectionSchema.optional(),
|
|
212
297
|
// The fork→entity probe's answer (#1585) — present only on the `venue` tier; see geocode-core's
|
|
213
298
|
// GeocodeResult.entity.
|
|
214
299
|
entity: z
|
|
@@ -234,7 +319,7 @@ export const GeocodeOutcomeLikeSchema = z.object({
|
|
|
234
319
|
countryCode: z.string().nullable(),
|
|
235
320
|
hierarchy: z.array(GeocodeHierarchyEntrySchema),
|
|
236
321
|
candidates: z.array(GeocodeCandidateSchema),
|
|
237
|
-
// The register row's OWN scope tags when the address_point tier answered and its
|
|
322
|
+
// The register row's OWN scope tags when the address_point tier answered and its extract carries
|
|
238
323
|
// them (normalized locality key + postcode of the ROOFTOP) — see geocode-core's GeocodeResult.rooftop.
|
|
239
324
|
rooftop: z
|
|
240
325
|
.object({
|
|
@@ -246,11 +331,17 @@ export const GeocodeOutcomeLikeSchema = z.object({
|
|
|
246
331
|
// OVERRODE the request's country prior — so a caller who asked for US and got an FR answer can see which
|
|
247
332
|
// evidence bought the change instead of reading it as a bug.
|
|
248
333
|
postcode_country_scope: z.string().nullable(),
|
|
334
|
+
// #1880: the capital promotion's firing receipt — the promoted candidate's country, present only when the
|
|
335
|
+
// promotion changed some node's leading candidate. Advisory, same posture as postcode_country_scope.
|
|
336
|
+
capital_promotion: z.string().optional(),
|
|
337
|
+
// #1893: the variant-alias exemption's firing receipt — present (true) only when the winning candidate reached
|
|
338
|
+
// the top because the exemption spared it the cross-country alias penalty. Advisory, same posture again.
|
|
339
|
+
variant_alias_exemption: z.literal(true).optional(),
|
|
249
340
|
// ROAD_TO_V9 §4: query-intent advisories. Always present; empty means the vocabulary looked and had nothing to
|
|
250
341
|
// say. Advisory ONLY — no marker changed which answer won, and a client is free to ignore the array entirely.
|
|
251
342
|
intent_markers: z.array(QueryIntentMarkerSchema),
|
|
252
343
|
// #1717 stage 1: flag-only admin-coherence verdicts — did the winning candidate's resolved ancestry confirm,
|
|
253
|
-
// contradict, or fail to speak to the PARSED region/country qualifiers? Nothing ranks or
|
|
344
|
+
// contradict, or fail to speak to the PARSED region/country qualifiers? Nothing ranks or filters on these; present
|
|
254
345
|
// whenever a winner resolved (both members always populated — `unstated` is the explicit no-qualifier claim),
|
|
255
346
|
// absent when nothing resolved to check against. See mailwoman's `admin-coherence.ts` for the verdict contract.
|
|
256
347
|
admin_coherence: z
|
|
@@ -259,6 +350,23 @@ export const GeocodeOutcomeLikeSchema = z.object({
|
|
|
259
350
|
country: z.enum(["confirmed", "contradicted", "unstated", "unverifiable"]),
|
|
260
351
|
})
|
|
261
352
|
.optional(),
|
|
353
|
+
// #1901: a configured authoritative provider's answer, carried BESIDE the open result — every value inside is
|
|
354
|
+
// the PROVIDER'S assertion, hand-modeled here to match `mailwoman/authoritative.ts`'s wire shape (the
|
|
355
|
+
// engine-agnosticism boundary forbids importing it). Absent when no provider is configured; `refused` is the
|
|
356
|
+
// provider declining (distinct from a parse failure or a gazetteer miss); `transport_error` is the provider
|
|
357
|
+
// being unreachable, reported rather than silently dropped. An `ambiguous` status carries EVERY candidate.
|
|
358
|
+
authoritative: z
|
|
359
|
+
.object({
|
|
360
|
+
provider: z.string(),
|
|
361
|
+
status: z.enum(["matched", "ambiguous", "refused", "transport_error"]),
|
|
362
|
+
matches: z.array(AuthoritativeMatchSchema).optional(),
|
|
363
|
+
attribution: z.string().optional(),
|
|
364
|
+
license: z.string().optional(),
|
|
365
|
+
retrieved_at: z.string().optional(),
|
|
366
|
+
dataset_version: z.string().optional(),
|
|
367
|
+
error: z.string().optional(),
|
|
368
|
+
})
|
|
369
|
+
.optional(),
|
|
262
370
|
// #1755: spans the flat `components` map could not represent. `components` holds one value per tag, so a second
|
|
263
371
|
// `locality` span ceases to exist there — and without this line `region: null` means both "the input named no
|
|
264
372
|
// region" and "it named one and we deleted it". Absent when nothing was dropped; never an empty array on the wire,
|
|
@@ -284,8 +392,8 @@ export type GeocodeOutcomeLike = z.infer<typeof GeocodeOutcomeLikeSchema>
|
|
|
284
392
|
* (`GeocodeOutcome = Record<string, unknown>`, `api/engine.ts`) — nothing here validates a real response, so a
|
|
285
393
|
* schema/engine mismatch can never reject or mutate a result at runtime. Deliberately carries NO import from
|
|
286
394
|
* `mailwoman` (the engine-agnosticism boundary — `mailwoman` is the one workspace allowed to depend on
|
|
287
|
-
* `@mailwoman/api`, never the reverse). `mailwoman/test/api-schema-drift.test.ts` is the compile-time
|
|
288
|
-
* catches this shape drifting from the real `GeocodeResult` interface.
|
|
395
|
+
* `@mailwoman/api`, never the reverse). `mailwoman/test/api-schema-drift.test.ts` is the compile-time regression check
|
|
396
|
+
* that catches this shape drifting from the real `GeocodeResult` interface.
|
|
289
397
|
*/
|
|
290
398
|
export const GeocodeOutcomeSchema = GeocodeOutcomeLikeSchema.loose().openapi("GeocodeOutcome")
|
|
291
399
|
|
|
@@ -331,7 +439,7 @@ export const BatchResponseSchema = z
|
|
|
331
439
|
*/
|
|
332
440
|
export const ResolveRequestSchema = z
|
|
333
441
|
.object({
|
|
334
|
-
tree: z.looseObject({
|
|
442
|
+
tree: z.looseObject({ raw: z.string(), roots: z.array(AddressNodeSchema) }),
|
|
335
443
|
opts: z.looseObject({}).optional(),
|
|
336
444
|
})
|
|
337
445
|
.openapi("ResolveRequest")
|
|
@@ -341,7 +449,7 @@ export const ResolveRequestSchema = z
|
|
|
341
449
|
*/
|
|
342
450
|
export const ResolveResponseSchema = z
|
|
343
451
|
.object({
|
|
344
|
-
tree: z.looseObject({
|
|
452
|
+
tree: z.looseObject({ raw: z.string(), roots: z.array(AddressNodeSchema) }),
|
|
345
453
|
})
|
|
346
454
|
.openapi("ResolveResponse")
|
|
347
455
|
|
|
@@ -377,9 +485,11 @@ export const FormatResponseSchema = z
|
|
|
377
485
|
|
|
378
486
|
/**
|
|
379
487
|
* `GET /health` response — `status`/`uptime_s` are stamped by the ROUTE itself, unconditionally, regardless of engine
|
|
380
|
-
* (`api/routes.ts`'s `healthRoute` handler: `{ status: "ok", uptime_s, ...engine.health?.() }`), so those two are
|
|
381
|
-
*
|
|
382
|
-
*
|
|
488
|
+
* (`api/routes.ts`'s `healthRoute` handler: `{ status: "ok", uptime_s, ...engine.health?.() }`), so those two are
|
|
489
|
+
* cheap
|
|
490
|
+
*
|
|
491
|
+
* - Accurate to pin. Everything else is `HealthData` (`api/engine.ts`) — an engine-defined block (model card, data-root
|
|
492
|
+
* inventory for `mailwoman serve`; something else entirely for another engine) — stays loose.
|
|
383
493
|
*/
|
|
384
494
|
export const HealthResponseSchema = z
|
|
385
495
|
.object({
|
package/out/app.d.ts
CHANGED
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { OpenAPIHono } from "@hono/zod-openapi";
|
|
11
11
|
import { type OpenAPIDocInfo } from "@mailwoman/api-kit";
|
|
12
|
-
import type {
|
|
13
|
-
import type {
|
|
12
|
+
import type { EngineStamp } from "@mailwoman/core/license";
|
|
13
|
+
import type { MailwomanAPIEngine } from "#engine";
|
|
14
|
+
import type { GeocodeOutcomeLike } from "#schema";
|
|
14
15
|
/**
|
|
15
16
|
* Options for {@link createMailwomanAPI}.
|
|
16
17
|
*/
|
|
@@ -30,6 +31,12 @@ export interface MailwomanAPIOptions {
|
|
|
30
31
|
* Max `addresses` rows accepted by `POST /v1/batch`. Default 500 (see `routes.ts`'s `DEFAULT_BATCH_MAX`).
|
|
31
32
|
*/
|
|
32
33
|
batchMax?: number;
|
|
34
|
+
/**
|
|
35
|
+
* The engine stamp to carry on every response: `engine` in each `/v1` body and the `Server` + `Link: rel="license"`
|
|
36
|
+
* headers everywhere. Absent when an embedding application builds the app without the `mailwoman` package; the
|
|
37
|
+
* `mailwoman serve` command always passes one.
|
|
38
|
+
*/
|
|
39
|
+
engine?: EngineStamp;
|
|
33
40
|
}
|
|
34
41
|
/**
|
|
35
42
|
* The document info stamped into the emitted OpenAPI document. Exported (not inlined) so the `mailwoman openapi`
|
package/out/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../app.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../lib/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAIN,KAAK,cAAc,EAEnB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAI1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAEjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAOjD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,OAAO,CAAA;IAEd;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IAEvB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB;;;;OAIG;IACH,MAAM,CAAC,EAAE,WAAW,CAAA;CACpB;AAUD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,cAkBpC,CAAA;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,OAAO,CAAC,kBAAkB,CAAC,GAAG,kBAAkB,EAC5F,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAC7B,OAAO,GAAE,mBAAwB,GAC/B,WAAW,CAyDb"}
|
package/out/app.js
CHANGED
|
@@ -8,11 +8,10 @@
|
|
|
8
8
|
* CLI wires the real parse/geocode/resolve stack (phase 4b); tests inject fixtures.
|
|
9
9
|
*/
|
|
10
10
|
import { OpenAPIHono } from "@hono/zod-openapi";
|
|
11
|
-
import { errorResponse,
|
|
11
|
+
import { attachOpenAPIDocs, engineHeaders, errorResponse, readServedDocumentInfo, } from "@mailwoman/api-kit";
|
|
12
12
|
import { bodyLimit } from "hono/body-limit";
|
|
13
13
|
import { cors } from "hono/cors";
|
|
14
|
-
import
|
|
15
|
-
import { DEFAULT_BATCH_MAX, registerMailwomanAPIRoutes } from "./routes.js";
|
|
14
|
+
import { DEFAULT_BATCH_MAX, registerMailwomanAPIRoutes } from "#routes";
|
|
16
15
|
/**
|
|
17
16
|
* 2 MiB — carried from the express server's `express.json({ limit: "2mb" })` (`mailwoman/server/index.ts`).
|
|
18
17
|
*/
|
|
@@ -30,9 +29,7 @@ function summarizeValidationError(error) {
|
|
|
30
29
|
* {@link attachOpenAPIDocs}) uses — one source of truth, no risk of the two drifting.
|
|
31
30
|
*/
|
|
32
31
|
export const MAILWOMAN_API_DOC_INFO = {
|
|
33
|
-
|
|
34
|
-
version: packageJson.version,
|
|
35
|
-
description: packageJson.description,
|
|
32
|
+
...(await readServedDocumentInfo(import.meta.url, "@mailwoman/api")),
|
|
36
33
|
license: { name: "AGPL-3.0-only OR LicenseRef-Commercial", identifier: "AGPL-3.0-only" },
|
|
37
34
|
contact: { name: "Sister Software", url: "https://mailwoman.ai" },
|
|
38
35
|
servers: [
|
|
@@ -72,6 +69,9 @@ export function createMailwomanAPI(engine, options = {}) {
|
|
|
72
69
|
if (options.cors !== false) {
|
|
73
70
|
app.use(cors({ origin: "*", allowMethods: ["GET", "POST", "OPTIONS"], allowHeaders: ["*"], maxAge: 86_400 }));
|
|
74
71
|
}
|
|
72
|
+
if (options.engine) {
|
|
73
|
+
app.use(engineHeaders(options.engine));
|
|
74
|
+
}
|
|
75
75
|
// Safety net: an engine fault answers the native envelope, never a crash. `detail` carries the raw message —
|
|
76
76
|
// this surface is ours to design, so (unlike the vendor-constrained drop-in envelopes) we can be helpful.
|
|
77
77
|
app.onError((error, c) => {
|
|
@@ -89,7 +89,10 @@ export function createMailwomanAPI(engine, options = {}) {
|
|
|
89
89
|
maxSize: options.bodyLimitBytes ?? DEFAULT_BODY_LIMIT_BYTES,
|
|
90
90
|
onError: (c) => errorResponse(c, 413, "request body too large"),
|
|
91
91
|
}));
|
|
92
|
-
registerMailwomanAPIRoutes(app, engine, {
|
|
92
|
+
registerMailwomanAPIRoutes(app, engine, {
|
|
93
|
+
batchMax: options.batchMax ?? DEFAULT_BATCH_MAX,
|
|
94
|
+
engine: options.engine,
|
|
95
|
+
});
|
|
93
96
|
attachOpenAPIDocs(app, MAILWOMAN_API_DOC_INFO);
|
|
94
97
|
return app;
|
|
95
98
|
}
|
package/out/app.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.js","sourceRoot":"","sources":["../app.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../lib/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EACN,iBAAiB,EACjB,aAAa,EACb,aAAa,EAEb,sBAAsB,GACtB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAGhC,OAAO,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,MAAM,SAAS,CAAA;AAGvE;;GAEG;AACH,MAAM,wBAAwB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAA;AAgChD;;;GAGG;AACH,SAAS,wBAAwB,CAAC,KAAkE;IACnG,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,QAAQ,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACvG,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAmB;IACrD,GAAG,CAAC,MAAM,sBAAsB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;IACpE,OAAO,EAAE,EAAE,IAAI,EAAE,wCAAwC,EAAE,UAAU,EAAE,eAAe,EAAE;IACxF,OAAO,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,GAAG,EAAE,sBAAsB,EAAE;IACjE,OAAO,EAAE;QACR;YACC,GAAG,EAAE,sBAAsB;YAC3B,SAAS,EAAE,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;SACxE;KACD;IACD,QAAQ,EAAE,EAAE;IACZ,IAAI,EAAE;QACL,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,4BAA4B,EAAE;QAC9D,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,mCAAmC,EAAE;QACvE,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,4DAA4D,EAAE;QAChG,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,oDAAoD,EAAE;QACzF,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,8CAA8C,EAAE;KAC7E;CACD,CAAA;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CACjC,MAA6B,EAC7B,UAA+B,EAAE;IAEjC,MAAM,GAAG,GAAG,IAAI,WAAW,CAAC;QAC3B,6FAA6F;QAC7F,iGAAiG;QACjG,qGAAqG;QACrG,uGAAuG;QACvG,sBAAsB;QACtB,WAAW,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YAC1B,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACrB,OAAO,aAAa,CAAC,CAAC,EAAE,GAAG,EAAE,sBAAsB,EAAE,wBAAwB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;YAC7F,CAAC;YAED,OAAO,SAAS,CAAA;QACjB,CAAC;KACD,CAAC,CAAA;IAEF,6GAA6G;IAC7G,oGAAoG;IACpG,IAAI,OAAO,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC5B,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;IAC9G,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;IACvC,CAAC;IAED,6GAA6G;IAC7G,0GAA0G;IAC1G,GAAG,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QACxB,kGAAkG;QAClG,iGAAiG;QACjG,qGAAqG;QACrG,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACxE,OAAO,aAAa,CAAC,CAAC,EAAE,GAAG,EAAE,sBAAsB,EAAE,gBAAgB,CAAC,CAAA;QACvE,CAAC;QAED,OAAO,aAAa,CAAC,CAAC,EAAE,GAAG,EAAE,gBAAgB,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;IACvG,CAAC,CAAC,CAAA;IAEF,yGAAyG;IACzG,kEAAkE;IAClE,GAAG,CAAC,GAAG,CACN,OAAO,EACP,SAAS,CAAC;QACT,OAAO,EAAE,OAAO,CAAC,cAAc,IAAI,wBAAwB;QAC3D,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,GAAG,EAAE,wBAAwB,CAAC;KAC/D,CAAC,CACF,CAAA;IAED,0BAA0B,CAAC,GAAG,EAAE,MAAM,EAAE;QACvC,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,iBAAiB;QAC/C,MAAM,EAAE,OAAO,CAAC,MAAM;KACtB,CAAC,CAAA;IAEF,iBAAiB,CAAC,GAAG,EAAE,sBAAsB,CAAC,CAAA;IAE9C,OAAO,GAAG,CAAA;AACX,CAAC"}
|
package/out/engine.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* exception — it's wired in-package from `@mailwoman/formatter` (the surface exists to expose it).
|
|
9
9
|
*/
|
|
10
10
|
import type { AddressTree } from "@mailwoman/core";
|
|
11
|
-
import type { GeocodeOutcomeLike } from "
|
|
11
|
+
import type { GeocodeOutcomeLike } from "#schema";
|
|
12
12
|
/**
|
|
13
13
|
* One parsed component in reading order (a `ComponentTag` + the covered text).
|
|
14
14
|
*/
|
|
@@ -61,6 +61,6 @@ export interface MailwomanAPIEngine<T extends Partial<GeocodeOutcomeLike> = Geoc
|
|
|
61
61
|
reloaded: boolean;
|
|
62
62
|
versions: unknown;
|
|
63
63
|
}>;
|
|
64
|
-
health?(): HealthData
|
|
64
|
+
health?(): Promise<HealthData>;
|
|
65
65
|
}
|
|
66
66
|
//# sourceMappingURL=engine.d.ts.map
|