@mailwoman/api 9.2.0 → 9.3.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.
@@ -9,15 +9,24 @@
9
9
  */
10
10
 
11
11
  import { OpenAPIHono } from "@hono/zod-openapi"
12
- import { errorResponse, attachOpenAPIDocs, type OpenAPIDocInfo } from "@mailwoman/api-kit"
12
+ import { attachOpenAPIDocs, engineHeaders, errorResponse, type OpenAPIDocInfo } from "@mailwoman/api-kit"
13
+ import { readLocalJSONFile } from "@mailwoman/core/fs/readers"
14
+ import type { EngineStamp } from "@mailwoman/core/license"
15
+ import { resolvePackagePath } from "@mailwoman/core/module/resolvers"
13
16
  import { bodyLimit } from "hono/body-limit"
14
17
  import { cors } from "hono/cors"
15
18
 
16
- import packageJson from "#package.json" with { type: "json" }
19
+ import type { MailwomanAPIEngine } from "#engine"
20
+ import { DEFAULT_BATCH_MAX, registerMailwomanAPIRoutes } from "#routes"
21
+ import type { GeocodeOutcomeLike } from "#schema"
17
22
 
18
- import type { MailwomanAPIEngine } from "./engine.ts"
19
- import { DEFAULT_BATCH_MAX, registerMailwomanAPIRoutes } from "./routes.ts"
20
- import type { GeocodeOutcomeLike } from "./schema.ts"
23
+ /**
24
+ * This package's own manifest, read at load rather than imported as a module: a JSON import makes `tsc` copy the file
25
+ * into `out/`, where it becomes the package scope for the compiled tree and breaks every `#` import in it.
26
+ */
27
+ const packageJson = await readLocalJSONFile<{ name: string; version: string; description: string }>(
28
+ resolvePackagePath("@mailwoman/api", "package.json")
29
+ )
21
30
 
22
31
  /**
23
32
  * 2 MiB — carried from the express server's `express.json({ limit: "2mb" })` (`mailwoman/server/index.ts`).
@@ -45,6 +54,13 @@ export interface MailwomanAPIOptions {
45
54
  * Max `addresses` rows accepted by `POST /v1/batch`. Default 500 (see `routes.ts`'s `DEFAULT_BATCH_MAX`).
46
55
  */
47
56
  batchMax?: number
57
+
58
+ /**
59
+ * The engine stamp to carry on every response: `engine` in each `/v1` body and the `Server` + `Link: rel="license"`
60
+ * headers everywhere. Absent when an embedding application builds the app without the `mailwoman` package; the
61
+ * `mailwoman serve` command always passes one.
62
+ */
63
+ engine?: EngineStamp
48
64
  }
49
65
 
50
66
  /**
@@ -110,6 +126,10 @@ export function createMailwomanAPI<T extends Partial<GeocodeOutcomeLike> = Geoco
110
126
  app.use(cors({ origin: "*", allowMethods: ["GET", "POST", "OPTIONS"], allowHeaders: ["*"], maxAge: 86_400 }))
111
127
  }
112
128
 
129
+ if (options.engine) {
130
+ app.use(engineHeaders(options.engine))
131
+ }
132
+
113
133
  // Safety net: an engine fault answers the native envelope, never a crash. `detail` carries the raw message —
114
134
  // this surface is ours to design, so (unlike the vendor-constrained drop-in envelopes) we can be helpful.
115
135
  app.onError((error, c) => {
@@ -133,7 +153,10 @@ export function createMailwomanAPI<T extends Partial<GeocodeOutcomeLike> = Geoco
133
153
  })
134
154
  )
135
155
 
136
- registerMailwomanAPIRoutes(app, engine, { batchMax: options.batchMax ?? DEFAULT_BATCH_MAX })
156
+ registerMailwomanAPIRoutes(app, engine, {
157
+ batchMax: options.batchMax ?? DEFAULT_BATCH_MAX,
158
+ engine: options.engine,
159
+ })
137
160
 
138
161
  attachOpenAPIDocs(app, MAILWOMAN_API_DOC_INFO)
139
162
 
@@ -10,7 +10,7 @@
10
10
 
11
11
  import type { AddressTree } from "@mailwoman/core"
12
12
 
13
- import type { GeocodeOutcomeLike } from "./schema.ts"
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 "./app.ts"
20
- export * from "./engine.ts"
21
- export * from "./routes.ts"
22
- export * from "./schema.ts"
19
+ export * from "#app"
20
+ export * from "#engine"
21
+ export * from "#routes"
22
+ export * from "#schema"
@@ -20,12 +20,19 @@
20
20
  */
21
21
 
22
22
  import { createRoute, type OpenAPIHono, z } from "@hono/zod-openapi"
23
- import { geocoderUnavailableError, metricsSnapshot, recordTimed } from "@mailwoman/api-kit"
23
+ import {
24
+ geocoderUnavailableError,
25
+ metricsSnapshot,
26
+ recordTimed,
27
+ stampedResponseSchema,
28
+ withEngineStamp,
29
+ } from "@mailwoman/api-kit"
24
30
  import type { AddressTree } from "@mailwoman/core/decoder"
31
+ import type { EngineStamp } from "@mailwoman/core/license"
25
32
  import type { ComponentTag } from "@mailwoman/core/types"
26
33
  import { canonicalKey, type ComponentDict, formatAddress, type FormatAddressOptions } from "@mailwoman/formatter"
27
34
 
28
- import type { MailwomanAPIEngine } from "./engine.ts"
35
+ import type { MailwomanAPIEngine } from "#engine"
29
36
  import {
30
37
  APIErrorSchema,
31
38
  BatchRequestSchema,
@@ -40,12 +47,12 @@ import {
40
47
  ResolveRequestSchema,
41
48
  ResolveResponseSchema,
42
49
  type GeocodeOutcome,
43
- } from "./schema.ts"
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 `core/env/schema.ts`).
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) } },
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) } },
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) } },
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) } },
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-shard version map.",
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) } },
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 shards (deploy-only; gate at ingress)",
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 unknown as GeocodeOutcome, 200)
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 unknown as z.infer<typeof BatchResponseSchema>, 200)
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 unknown as AddressTree, opts ?? {})
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,25 @@
17
17
  */
18
18
 
19
19
  import { z } from "@hono/zod-openapi"
20
+ import type { AddressNode } from "@mailwoman/core/decoder"
21
+ import type { DerivationProjection, Evidence } from "@mailwoman/evidence"
20
22
 
21
23
  export { APIErrorSchema } from "@mailwoman/api-kit"
22
24
 
23
25
  /**
24
26
  * `POST /v1/parse` request body.
25
27
  */
28
+ /**
29
+ * One node of the decoded address tree. The decoder's `AddressNode` is a recursive union the OpenAPI generator cannot
30
+ * derive a schema for on its own, so it is registered as an open object; the shape is documented by the type.
31
+ */
32
+ export const AddressNodeSchema = z.custom<AddressNode>().openapi("AddressNode", {
33
+ type: "object",
34
+ additionalProperties: true,
35
+ description:
36
+ "A decoded address-tree node: a tag, its span, and its children. See `AddressNode` in `@mailwoman/core/decoder`.",
37
+ })
38
+
26
39
  /**
27
40
  * The input register (Decision A / GTM B10): `fragmented` = the map-search register (evidence-bundle channels feed);
28
41
  * `formatted` = the validation/record register (channels off). Unset → the engine derives it from the input's shape.
@@ -70,7 +83,7 @@ export const ParseOutcomeSchema = z
70
83
  .object({
71
84
  input: z.string(),
72
85
  components: z.array(ParseComponentSchema),
73
- tree: z.looseObject({ roots: z.array(z.unknown()) }),
86
+ tree: z.looseObject({ raw: z.string(), roots: z.array(AddressNodeSchema) }),
74
87
  debug: z.string().optional(),
75
88
  })
76
89
  .openapi("ParseOutcome")
@@ -150,6 +163,7 @@ const ComponentTagSchema = z.enum([
150
163
  "sub_block",
151
164
  "building_number",
152
165
  "building_name",
166
+ "locality_unit",
153
167
  ])
154
168
 
155
169
  /**
@@ -186,13 +200,80 @@ const QueryIntentMarkerSchema = z
186
200
  "near_me",
187
201
  "poi_category",
188
202
  ]),
189
- code: z.enum(["declared_ambiguity", "declared_fork", "focus_point_required", "poi_category"]),
203
+ code: z.enum([
204
+ "declared_ambiguity",
205
+ "declared_fork",
206
+ "focus_point_required",
207
+ "poi_category",
208
+ "coverage_qualified_absence",
209
+ "authority_designation",
210
+ ]),
190
211
  mechanism: z.string(),
191
212
  message: z.string(),
192
213
  evidence: z.record(z.string(), z.unknown()).optional(),
193
214
  })
194
215
  .openapi("QueryIntentMarker")
195
216
 
217
+ /**
218
+ * One authoritative-provider match on the wire (#1901) — hoisted so the outcome schema below stays inside the
219
+ * call-nesting bound. Field-for-field mirror of `mailwoman/authoritative.ts`'s `AuthoritativeAssertionMatch`.
220
+ */
221
+ const AuthoritativeMatchSchema = z.object({
222
+ provider_place_id: z.string(),
223
+ object_ids: z.record(z.string(), z.string()).optional(),
224
+ canonical_fields: z.record(z.string(), z.string()).optional(),
225
+ lat: z.number().optional(),
226
+ lon: z.number().optional(),
227
+ precision: z.string().optional(),
228
+ match_status: z.enum(["exact", "approximate"]),
229
+ provider_score: z.number().optional(),
230
+ })
231
+
232
+ const EpistemicStatusSchema = z.enum(["designated", "observed", "derived", "inferred", "unresolved"])
233
+
234
+ const CoverageBasisSchema = z.enum(["designated", "surveyed", "source_present"])
235
+
236
+ /**
237
+ * `@mailwoman/evidence`'s `Evidence` union, spelled for the wire. The `EvidencePin` below fails to compile the moment
238
+ * either side gains, loses or retypes a field.
239
+ */
240
+ const EvidenceSchema = z.discriminatedUnion("kind", [
241
+ z.object({ kind: z.literal("observation"), source: z.string(), vintage: z.string().nullable(), value: z.unknown() }),
242
+ z.object({
243
+ kind: z.literal("exclusion"),
244
+ source: z.string(),
245
+ vintage: z.string(),
246
+ scope: z.object({ layer: z.string(), h3Cell: z.number(), basis: CoverageBasisSchema, fold: z.string() }),
247
+ }),
248
+ z.object({
249
+ kind: z.literal("relation"),
250
+ source: z.string(),
251
+ vintage: z.string(),
252
+ relationship: z.string(),
253
+ assertion: z.enum(["authoritative", "inferred"]),
254
+ score: z.number().optional(),
255
+ }),
256
+ z.object({ kind: z.literal("prior"), source: z.string(), label: z.string(), weight: z.number() }),
257
+ ])
258
+
259
+ /**
260
+ * The derivation behind a geocode answer, present only when the engine was asked to trace — `@mailwoman/evidence`'s
261
+ * `DerivationProjection` on the wire.
262
+ */
263
+ export const DerivationProjectionSchema = z.object({
264
+ status: EpistemicStatusSchema,
265
+ constraints: z.array(z.object({ label: z.string(), evidence: EvidenceSchema, contribution: z.string() })).readonly(),
266
+ uncertaintyM: z.number().nullable(),
267
+ })
268
+
269
+ // Both directions: the schema's inferred type is exactly the evidence package's, or this does not compile.
270
+ type Mutual<A, B> = [A] extends [B] ? ([B] extends [A] ? true : false) : false
271
+ const evidencePin: Mutual<z.infer<typeof EvidenceSchema>, Evidence> = true
272
+ const derivationPin: Mutual<z.infer<typeof DerivationProjectionSchema>, DerivationProjection> = true
273
+
274
+ void evidencePin
275
+ void derivationPin
276
+
196
277
  /**
197
278
  * `POST /v1/geocode` response — a hand-modeled mirror of `GeocodeResult`'s wire shape (`mailwoman/geocode-core.ts`),
198
279
  * `.loose()` so a field the engine adds that this schema doesn't yet know about still rides through undocumented rather
@@ -200,8 +281,8 @@ const QueryIntentMarkerSchema = z
200
281
  * (`GeocodeOutcome = Record<string, unknown>`, `api/engine.ts`) — nothing here validates a real response, so a
201
282
  * schema/engine mismatch can never reject or mutate a result at runtime. Deliberately carries NO import from
202
283
  * `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 tripwire that
204
- * catches this shape drifting from the real `GeocodeResult` interface.
284
+ * `@mailwoman/api`, never the reverse). `mailwoman/test/api-schema-drift.test.ts` is the compile-time regression check
285
+ * that catches this shape drifting from the real `GeocodeResult` interface.
205
286
  */
206
287
  export const GeocodeOutcomeLikeSchema = z.object({
207
288
  input: z.string(),
@@ -209,6 +290,12 @@ export const GeocodeOutcomeLikeSchema = z.object({
209
290
  lat: z.number().nullable(),
210
291
  lon: z.number().nullable(),
211
292
  resolution_tier: z.enum(["address_point", "interpolated", "street", "admin", "venue", "plus_code"]),
293
+ // What the evidence permits a consumer to claim about the coordinate, orthogonal to how it was produced; see
294
+ // `@mailwoman/evidence`'s `EpistemicStatus`.
295
+ epistemic_status: z.enum(["designated", "observed", "derived", "inferred", "unresolved"]),
296
+ // The derivation behind the answer, present only when the engine was asked to trace. `DerivationProjectionSchema` is
297
+ // pinned to `@mailwoman/evidence`'s types below, so the wire contract and the evidence union cannot drift apart.
298
+ derivation: DerivationProjectionSchema.optional(),
212
299
  // The fork→entity probe's answer (#1585) — present only on the `venue` tier; see geocode-core's
213
300
  // GeocodeResult.entity.
214
301
  entity: z
@@ -234,7 +321,7 @@ export const GeocodeOutcomeLikeSchema = z.object({
234
321
  countryCode: z.string().nullable(),
235
322
  hierarchy: z.array(GeocodeHierarchyEntrySchema),
236
323
  candidates: z.array(GeocodeCandidateSchema),
237
- // The register row's OWN scope tags when the address_point tier answered and its shard carries
324
+ // The register row's OWN scope tags when the address_point tier answered and its extract carries
238
325
  // them (normalized locality key + postcode of the ROOFTOP) — see geocode-core's GeocodeResult.rooftop.
239
326
  rooftop: z
240
327
  .object({
@@ -246,11 +333,17 @@ export const GeocodeOutcomeLikeSchema = z.object({
246
333
  // OVERRODE the request's country prior — so a caller who asked for US and got an FR answer can see which
247
334
  // evidence bought the change instead of reading it as a bug.
248
335
  postcode_country_scope: z.string().nullable(),
336
+ // #1880: the capital promotion's firing receipt — the promoted candidate's country, present only when the
337
+ // promotion changed some node's leading candidate. Advisory, same posture as postcode_country_scope.
338
+ capital_promotion: z.string().optional(),
339
+ // #1893: the variant-alias exemption's firing receipt — present (true) only when the winning candidate reached
340
+ // the top because the exemption spared it the cross-country alias penalty. Advisory, same posture again.
341
+ variant_alias_exemption: z.literal(true).optional(),
249
342
  // ROAD_TO_V9 §4: query-intent advisories. Always present; empty means the vocabulary looked and had nothing to
250
343
  // say. Advisory ONLY — no marker changed which answer won, and a client is free to ignore the array entirely.
251
344
  intent_markers: z.array(QueryIntentMarkerSchema),
252
345
  // #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 gates on these; present
346
+ // contradict, or fail to speak to the PARSED region/country qualifiers? Nothing ranks or filters on these; present
254
347
  // whenever a winner resolved (both members always populated — `unstated` is the explicit no-qualifier claim),
255
348
  // absent when nothing resolved to check against. See mailwoman's `admin-coherence.ts` for the verdict contract.
256
349
  admin_coherence: z
@@ -259,6 +352,23 @@ export const GeocodeOutcomeLikeSchema = z.object({
259
352
  country: z.enum(["confirmed", "contradicted", "unstated", "unverifiable"]),
260
353
  })
261
354
  .optional(),
355
+ // #1901: a configured authoritative provider's answer, carried BESIDE the open result — every value inside is
356
+ // the PROVIDER'S assertion, hand-modeled here to match `mailwoman/authoritative.ts`'s wire shape (the
357
+ // engine-agnosticism boundary forbids importing it). Absent when no provider is configured; `refused` is the
358
+ // provider declining (distinct from a parse failure or a gazetteer miss); `transport_error` is the provider
359
+ // being unreachable, reported rather than silently dropped. An `ambiguous` status carries EVERY candidate.
360
+ authoritative: z
361
+ .object({
362
+ provider: z.string(),
363
+ status: z.enum(["matched", "ambiguous", "refused", "transport_error"]),
364
+ matches: z.array(AuthoritativeMatchSchema).optional(),
365
+ attribution: z.string().optional(),
366
+ license: z.string().optional(),
367
+ retrieved_at: z.string().optional(),
368
+ dataset_version: z.string().optional(),
369
+ error: z.string().optional(),
370
+ })
371
+ .optional(),
262
372
  // #1755: spans the flat `components` map could not represent. `components` holds one value per tag, so a second
263
373
  // `locality` span ceases to exist there — and without this line `region: null` means both "the input named no
264
374
  // region" and "it named one and we deleted it". Absent when nothing was dropped; never an empty array on the wire,
@@ -284,8 +394,8 @@ export type GeocodeOutcomeLike = z.infer<typeof GeocodeOutcomeLikeSchema>
284
394
  * (`GeocodeOutcome = Record<string, unknown>`, `api/engine.ts`) — nothing here validates a real response, so a
285
395
  * schema/engine mismatch can never reject or mutate a result at runtime. Deliberately carries NO import from
286
396
  * `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 tripwire that
288
- * catches this shape drifting from the real `GeocodeResult` interface.
397
+ * `@mailwoman/api`, never the reverse). `mailwoman/test/api-schema-drift.test.ts` is the compile-time regression check
398
+ * that catches this shape drifting from the real `GeocodeResult` interface.
289
399
  */
290
400
  export const GeocodeOutcomeSchema = GeocodeOutcomeLikeSchema.loose().openapi("GeocodeOutcome")
291
401
 
@@ -331,7 +441,7 @@ export const BatchResponseSchema = z
331
441
  */
332
442
  export const ResolveRequestSchema = z
333
443
  .object({
334
- tree: z.looseObject({ roots: z.array(z.unknown()) }),
444
+ tree: z.looseObject({ raw: z.string(), roots: z.array(AddressNodeSchema) }),
335
445
  opts: z.looseObject({}).optional(),
336
446
  })
337
447
  .openapi("ResolveRequest")
@@ -341,7 +451,7 @@ export const ResolveRequestSchema = z
341
451
  */
342
452
  export const ResolveResponseSchema = z
343
453
  .object({
344
- tree: z.looseObject({ roots: z.array(z.unknown()) }),
454
+ tree: z.looseObject({ raw: z.string(), roots: z.array(AddressNodeSchema) }),
345
455
  })
346
456
  .openapi("ResolveResponse")
347
457
 
@@ -377,9 +487,11 @@ export const FormatResponseSchema = z
377
487
 
378
488
  /**
379
489
  * `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 cheap
381
- * + accurate to pin. Everything else is `HealthData` (`api/engine.ts`) — an engine-defined block (model card, data-root
382
- * inventory for `mailwoman serve`; something else entirely for another engine) — stays loose.
490
+ * (`api/routes.ts`'s `healthRoute` handler: `{ status: "ok", uptime_s, ...engine.health?.() }`), so those two are
491
+ * cheap
492
+ *
493
+ * - Accurate to pin. Everything else is `HealthData` (`api/engine.ts`) — an engine-defined block (model card, data-root
494
+ * inventory for `mailwoman serve`; something else entirely for another engine) — stays loose.
383
495
  */
384
496
  export const HealthResponseSchema = z
385
497
  .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 { MailwomanAPIEngine } from "./engine.ts";
13
- import type { GeocodeOutcomeLike } from "./schema.ts";
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,EAAoC,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAM1F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAErD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAOrD;;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;CACjB;AAUD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,cAoBpC,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,CAkDb"}
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,EAAmD,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEzG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAK1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAEjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAejD;;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,cAoBpC,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,17 @@
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, attachOpenAPIDocs } from "@mailwoman/api-kit";
11
+ import { attachOpenAPIDocs, engineHeaders, errorResponse } from "@mailwoman/api-kit";
12
+ import { readLocalJSONFile } from "@mailwoman/core/fs/readers";
13
+ import { resolvePackagePath } from "@mailwoman/core/module/resolvers";
12
14
  import { bodyLimit } from "hono/body-limit";
13
15
  import { cors } from "hono/cors";
14
- import packageJson from "#package.json" with { type: "json" };
15
- import { DEFAULT_BATCH_MAX, registerMailwomanAPIRoutes } from "./routes.js";
16
+ import { DEFAULT_BATCH_MAX, registerMailwomanAPIRoutes } from "#routes";
17
+ /**
18
+ * This package's own manifest, read at load rather than imported as a module: a JSON import makes `tsc` copy the file
19
+ * into `out/`, where it becomes the package scope for the compiled tree and breaks every `#` import in it.
20
+ */
21
+ const packageJson = await readLocalJSONFile(resolvePackagePath("@mailwoman/api", "package.json"));
16
22
  /**
17
23
  * 2 MiB — carried from the express server's `express.json({ limit: "2mb" })` (`mailwoman/server/index.ts`).
18
24
  */
@@ -72,6 +78,9 @@ export function createMailwomanAPI(engine, options = {}) {
72
78
  if (options.cors !== false) {
73
79
  app.use(cors({ origin: "*", allowMethods: ["GET", "POST", "OPTIONS"], allowHeaders: ["*"], maxAge: 86_400 }));
74
80
  }
81
+ if (options.engine) {
82
+ app.use(engineHeaders(options.engine));
83
+ }
75
84
  // Safety net: an engine fault answers the native envelope, never a crash. `detail` carries the raw message —
76
85
  // this surface is ours to design, so (unlike the vendor-constrained drop-in envelopes) we can be helpful.
77
86
  app.onError((error, c) => {
@@ -89,7 +98,10 @@ export function createMailwomanAPI(engine, options = {}) {
89
98
  maxSize: options.bodyLimitBytes ?? DEFAULT_BODY_LIMIT_BYTES,
90
99
  onError: (c) => errorResponse(c, 413, "request body too large"),
91
100
  }));
92
- registerMailwomanAPIRoutes(app, engine, { batchMax: options.batchMax ?? DEFAULT_BATCH_MAX });
101
+ registerMailwomanAPIRoutes(app, engine, {
102
+ batchMax: options.batchMax ?? DEFAULT_BATCH_MAX,
103
+ engine: options.engine,
104
+ });
93
105
  attachOpenAPIDocs(app, MAILWOMAN_API_DOC_INFO);
94
106
  return app;
95
107
  }
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,EAAE,aAAa,EAAE,iBAAiB,EAAuB,MAAM,oBAAoB,CAAA;AAC1F,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,OAAO,WAAW,MAAM,eAAe,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AAG7D,OAAO,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAA;AAG3E;;GAEG;AACH,MAAM,wBAAwB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAA;AAyBhD;;;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,KAAK,EAAE,WAAW,CAAC,IAAI;IACvB,OAAO,EAAE,WAAW,CAAC,OAAO;IAC5B,WAAW,EAAE,WAAW,CAAC,WAAW;IACpC,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,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,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,iBAAiB,EAAE,CAAC,CAAA;IAE5F,iBAAiB,CAAC,GAAG,EAAE,sBAAsB,CAAC,CAAA;IAE9C,OAAO,GAAG,CAAA;AACX,CAAC"}
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../lib/app.ts"],"names":[],"mappings":"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"}
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 "./schema.ts";
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
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAElD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAErD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;CACb;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,cAAc,EAAE,CAAA;IAC5B,IAAI,EAAE,WAAW,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACb;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,OAAO,CAAC,kBAAkB,CAAC,GAAG,kBAAkB,IAAI,CAAC,GAAG,kBAAkB,CAAA;AAEjH,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,WAAW,CAAA;CACjB;AAED;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAEhD;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,WAAW,CAAA;AAEtD,MAAM,WAAW,SAAS;IACzB,SAAS,CAAC,EAAE,aAAa,CAAA;IACzB,KAAK,CAAC,EAAE,OAAO,CAAA;CACf;AAED,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,OAAO,CAAC,kBAAkB,CAAC,GAAG,kBAAkB,IAAI,CACzF,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,SAAS,KACZ,OAAO,CAAC,CAAC,CAAC,CAAA;AAEf,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,OAAO,CAAC,kBAAkB,CAAC,GAAG,kBAAkB;IAC7F,KAAK,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;IACtE,OAAO,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,CAAA;IAC5B,KAAK,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAA;KAAE,CAAC,CAAA;IAC1F,WAAW,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAC3F,MAAM,CAAC,IAAI,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IAC5D,MAAM,CAAC,IAAI,UAAU,CAAA;CACrB"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../lib/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAElD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAEjD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;CACb;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,cAAc,EAAE,CAAA;IAC5B,IAAI,EAAE,WAAW,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACb;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,OAAO,CAAC,kBAAkB,CAAC,GAAG,kBAAkB,IAAI,CAAC,GAAG,kBAAkB,CAAA;AAEjH,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,WAAW,CAAA;CACjB;AAED;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAEhD;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,WAAW,CAAA;AAEtD,MAAM,WAAW,SAAS;IACzB,SAAS,CAAC,EAAE,aAAa,CAAA;IACzB,KAAK,CAAC,EAAE,OAAO,CAAA;CACf;AAED,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,OAAO,CAAC,kBAAkB,CAAC,GAAG,kBAAkB,IAAI,CACzF,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,SAAS,KACZ,OAAO,CAAC,CAAC,CAAC,CAAA;AAEf,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,OAAO,CAAC,kBAAkB,CAAC,GAAG,kBAAkB;IAC7F,KAAK,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;IACtE,OAAO,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,CAAA;IAC5B,KAAK,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAA;KAAE,CAAC,CAAA;IAC1F,WAAW,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAC3F,MAAM,CAAC,IAAI,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IAC5D,MAAM,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,CAAA;CAC9B"}