@mailwoman/api-kit 6.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/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # @mailwoman/api-kit
2
+
3
+ Plumbing for [Mailwoman](https://mailwoman.sister.software)'s HTTP surfaces — a node `serve` wrapper and
4
+ OpenAPI emit helpers shared by the drop-in packages ([`@mailwoman/libpostal`](../libpostal),
5
+ [`@mailwoman/photon`](../photon), [`@mailwoman/nominatim`](../nominatim)).
6
+
7
+ **Plumbing only.** Domain schemas, routes, and wire contracts live with the package that owns them — this
8
+ package never grows a `ParseRequestSchema` or a `/parse` handler of its own.
9
+
10
+ ```ts
11
+ import { attachOpenAPIDocs, serveNode } from "@mailwoman/api-kit"
12
+ import { OpenAPIHono } from "@hono/zod-openapi"
13
+
14
+ const app = new OpenAPIHono()
15
+ // ...register routes with app.openapi(...)...
16
+
17
+ attachOpenAPIDocs(app, { title: "my-api", version: "1.0.0" })
18
+ serveNode({ fetch: app.fetch, port: 8081, hostname: "0.0.0.0" })
19
+ ```
20
+
21
+ `serveNode` is the one place a node HTTP listener gets created — surface packages stay web-standard
22
+ (`fetch`-shaped apps only), so deploying one to an edge runtime needs no changes to it.
23
+
24
+ ## OpenAPI
25
+
26
+ `attachOpenAPIDocs` mounts a document endpoint (default `/openapi.json`) that's always derived from the
27
+ app's route table — never handwritten. It serves OpenAPI 3.1. Need the 3.0.3 flavor too (client generators
28
+ that lag behind 3.1)? `emitOpenAPIDocuments(app, info)` returns both `{ v31, v30 }` from the same route
29
+ table, for build artifacts or parity tests.
package/out/error.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * The native error envelope. Surfaces that carry a vendor-compat contract (photon, nominatim,
7
+ * libpostal) keep their own error shapes — this envelope is for surfaces OURS to design (the
8
+ * `@mailwoman/api` native `/v1/*` routes), where nothing constrains the wire shape but us.
9
+ */
10
+ import { z } from "@hono/zod-openapi";
11
+ import type { Context } from "hono";
12
+ import type { ContentfulStatusCode } from "hono/utils/http-status";
13
+ /** The native error envelope: a short machine-stable `error` string plus an optional human `detail`. */
14
+ export declare const APIErrorSchema: z.ZodObject<{
15
+ error: z.ZodString;
16
+ detail: z.ZodOptional<z.ZodString>;
17
+ }, z.core.$strip>;
18
+ /**
19
+ * Respond with the native error envelope. `status` is generic (not the flat `ContentfulStatusCode` union) so the
20
+ * returned `TypedResponse`'s status stays the CALLER'S literal (e.g. `503`), not the whole union — required for use
21
+ * inside an `app.openapi(route, handler)` handler body (`@mailwoman/api/routes.ts`), where the framework checks the
22
+ * handler's return type against that specific route's declared per-status `responses` map. A flat-typed `status` param
23
+ * would widen every branch to "any content-carrying status", which no single declared response branch matches.
24
+ */
25
+ export declare function apiError<S extends ContentfulStatusCode>(c: Context, status: S, error: string, detail?: string): Response & import("hono").TypedResponse<{
26
+ error: string;
27
+ } | {
28
+ error: string;
29
+ detail: string;
30
+ }, S, "json">;
31
+ //# sourceMappingURL=error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../error.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,mBAAmB,CAAA;AACrC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAElE,wGAAwG;AACxG,eAAO,MAAM,cAAc;;;iBAKN,CAAA;AAErB;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,oBAAoB,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;;;;;cAE7G"}
package/out/error.js ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * The native error envelope. Surfaces that carry a vendor-compat contract (photon, nominatim,
7
+ * libpostal) keep their own error shapes — this envelope is for surfaces OURS to design (the
8
+ * `@mailwoman/api` native `/v1/*` routes), where nothing constrains the wire shape but us.
9
+ */
10
+ import { z } from "@hono/zod-openapi";
11
+ /** The native error envelope: a short machine-stable `error` string plus an optional human `detail`. */
12
+ export const APIErrorSchema = z
13
+ .object({
14
+ error: z.string(),
15
+ detail: z.string().optional(),
16
+ })
17
+ .openapi("APIError");
18
+ /**
19
+ * Respond with the native error envelope. `status` is generic (not the flat `ContentfulStatusCode` union) so the
20
+ * returned `TypedResponse`'s status stays the CALLER'S literal (e.g. `503`), not the whole union — required for use
21
+ * inside an `app.openapi(route, handler)` handler body (`@mailwoman/api/routes.ts`), where the framework checks the
22
+ * handler's return type against that specific route's declared per-status `responses` map. A flat-typed `status` param
23
+ * would widen every branch to "any content-carrying status", which no single declared response branch matches.
24
+ */
25
+ export function apiError(c, status, error, detail) {
26
+ return c.json(detail === undefined ? { error } : { error, detail }, status);
27
+ }
28
+ //# sourceMappingURL=error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.js","sourceRoot":"","sources":["../error.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,mBAAmB,CAAA;AAIrC,wGAAwG;AACxG,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC;KAC7B,MAAM,CAAC;IACP,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC7B,CAAC;KACD,OAAO,CAAC,UAAU,CAAC,CAAA;AAErB;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAiC,CAAU,EAAE,MAAS,EAAE,KAAa,EAAE,MAAe;IAC7G,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,CAAC,CAAA;AAC5E,CAAC"}
package/out/geo.d.ts ADDED
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * GeoJSON wire atoms shared by the geo-shaped HTTP surfaces (photon, nominatim). Envelope
7
+ * builders only — surface-specific property schemas live with their routes, per the anti-meta
8
+ * guardrails in the 2026-07-12 design spec.
9
+ */
10
+ import { z } from "@hono/zod-openapi";
11
+ /** A GeoJSON Point geometry: `[lon, lat]`. */
12
+ export declare const PointGeometrySchema: z.ZodObject<{
13
+ type: z.ZodLiteral<"Point">;
14
+ coordinates: z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>;
15
+ }, z.core.$strip>;
16
+ /** A `[minLon, minLat, maxLon, maxLat]`-style 4-tuple (photon's `extent` uses `[minLon, maxLat, maxLon, minLat]`). */
17
+ export declare const BBoxSchema: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
18
+ /** GeoJSON Feature envelope over a surface-specific properties schema. */
19
+ export declare function featureSchema<P extends z.ZodTypeAny>(properties: P): z.ZodObject<{
20
+ type: z.ZodLiteral<"Feature">;
21
+ geometry: z.ZodObject<{
22
+ type: z.ZodLiteral<"Point">;
23
+ coordinates: z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>;
24
+ }, z.core.$strip>;
25
+ properties: P;
26
+ }, z.core.$strip>;
27
+ /** GeoJSON FeatureCollection envelope over a feature schema. */
28
+ export declare function featureCollectionSchema<F extends z.ZodTypeAny>(feature: F): z.ZodObject<{
29
+ type: z.ZodLiteral<"FeatureCollection">;
30
+ features: z.ZodArray<F>;
31
+ }, z.core.$strip>;
32
+ //# sourceMappingURL=geo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geo.d.ts","sourceRoot":"","sources":["../geo.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,mBAAmB,CAAA;AAErC,8CAA8C;AAC9C,eAAO,MAAM,mBAAmB;;;iBAKN,CAAA;AAE1B,sHAAsH;AACtH,eAAO,MAAM,UAAU,wEAA4D,CAAA;AAEnF,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EAAE,UAAU,EAAE,CAAC;;;;;;;kBAMlE;AAED,gEAAgE;AAChE,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EAAE,OAAO,EAAE,CAAC;;;kBAKzE"}
package/out/geo.js ADDED
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * GeoJSON wire atoms shared by the geo-shaped HTTP surfaces (photon, nominatim). Envelope
7
+ * builders only — surface-specific property schemas live with their routes, per the anti-meta
8
+ * guardrails in the 2026-07-12 design spec.
9
+ */
10
+ import { z } from "@hono/zod-openapi";
11
+ /** A GeoJSON Point geometry: `[lon, lat]`. */
12
+ export const PointGeometrySchema = z
13
+ .object({
14
+ type: z.literal("Point"),
15
+ coordinates: z.tuple([z.number(), z.number()]),
16
+ })
17
+ .openapi("PointGeometry");
18
+ /** A `[minLon, minLat, maxLon, maxLat]`-style 4-tuple (photon's `extent` uses `[minLon, maxLat, maxLon, minLat]`). */
19
+ export const BBoxSchema = z.tuple([z.number(), z.number(), z.number(), z.number()]);
20
+ /** GeoJSON Feature envelope over a surface-specific properties schema. */
21
+ export function featureSchema(properties) {
22
+ return z.object({
23
+ type: z.literal("Feature"),
24
+ geometry: PointGeometrySchema,
25
+ properties,
26
+ });
27
+ }
28
+ /** GeoJSON FeatureCollection envelope over a feature schema. */
29
+ export function featureCollectionSchema(feature) {
30
+ return z.object({
31
+ type: z.literal("FeatureCollection"),
32
+ features: z.array(feature),
33
+ });
34
+ }
35
+ //# sourceMappingURL=geo.js.map
package/out/geo.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geo.js","sourceRoot":"","sources":["../geo.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,mBAAmB,CAAA;AAErC,8CAA8C;AAC9C,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC;KAClC,MAAM,CAAC;IACP,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;IACxB,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;CAC9C,CAAC;KACD,OAAO,CAAC,eAAe,CAAC,CAAA;AAE1B,sHAAsH;AACtH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;AAEnF,0EAA0E;AAC1E,MAAM,UAAU,aAAa,CAAyB,UAAa;IAClE,OAAO,CAAC,CAAC,MAAM,CAAC;QACf,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;QAC1B,QAAQ,EAAE,mBAAmB;QAC7B,UAAU;KACV,CAAC,CAAA;AACH,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,uBAAuB,CAAyB,OAAU;IACzE,OAAO,CAAC,CAAC,MAAM,CAAC;QACf,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAAC;QACpC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;KAC1B,CAAC,CAAA;AACH,CAAC"}
package/out/index.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * `@mailwoman/api-kit` — plumbing for Mailwoman's HTTP surfaces: the node serve wrapper, OpenAPI
7
+ * emit helpers, generic timing metrics, and the native error envelope. Plumbing only, by rule:
8
+ * domain schemas live next to their routes in the package that owns the wire contract (see the
9
+ * 2026-07-12 design spec's anti-meta guardrails).
10
+ */
11
+ export * from "./error.ts";
12
+ export * from "./geo.ts";
13
+ export * from "./metrics.ts";
14
+ export * from "./openapi.ts";
15
+ export * from "./serve.ts";
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,cAAc,YAAY,CAAA;AAC1B,cAAc,UAAU,CAAA;AACxB,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA"}
package/out/index.js ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * `@mailwoman/api-kit` — plumbing for Mailwoman's HTTP surfaces: the node serve wrapper, OpenAPI
7
+ * emit helpers, generic timing metrics, and the native error envelope. Plumbing only, by rule:
8
+ * domain schemas live next to their routes in the package that owns the wire contract (see the
9
+ * 2026-07-12 design spec's anti-meta guardrails).
10
+ */
11
+ export * from "./error.js";
12
+ export * from "./geo.js";
13
+ export * from "./metrics.js";
14
+ export * from "./openapi.js";
15
+ export * from "./serve.js";
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,cAAc,YAAY,CAAA;AAC1B,cAAc,UAAU,CAAA;AACxB,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Generic in-process timing metrics (ported from `mailwoman/server/metrics.ts`, #485
7
+ * observability, for the api-kit plumbing layer — see the 2026-07-12 Phase 4a plan's
8
+ * dependency-arrow correction). Dependency-free: monotonic counters per string-keyed tier + a
9
+ * bounded reservoir of recent latencies for percentile estimation. Callers own their tier
10
+ * vocabulary (e.g. `mailwoman`'s `ResolutionTier`); this module only ever sees `string`.
11
+ * Surfaced by `GET /metrics`; reset on process restart (no persistence — scrape it). Per-process
12
+ * state: under `node:cluster` each worker reports its own snapshot — aggregate at the scraper.
13
+ */
14
+ /**
15
+ * Record one completed timed operation: its wall-clock latency and which tier produced it (or `"error"`). Tier keys are
16
+ * created on first use; "error" is reserved and counts toward errors instead of a tier.
17
+ */
18
+ export declare function recordTimed(latencyMs: number, tier: string): void;
19
+ export interface MetricsSnapshot {
20
+ uptime_s: number;
21
+ timings: {
22
+ total: number;
23
+ errors: number;
24
+ /**
25
+ * Per-tier counts. Keys are created lazily on the first `recordTimed` call for that tier — a tier never recorded is
26
+ * absent, not zero.
27
+ */
28
+ tiers: Record<string, number>;
29
+ latency_ms: {
30
+ p50: number;
31
+ p90: number;
32
+ p99: number;
33
+ max: number;
34
+ } | null;
35
+ latency_samples: number;
36
+ };
37
+ }
38
+ /** Current metrics snapshot — sorted-reservoir percentiles + counters. */
39
+ export declare function metricsSnapshot(): MetricsSnapshot;
40
+ /** Test-only reset of all counters + the reservoir. */
41
+ export declare function resetMetricsForTest(): void;
42
+ //# sourceMappingURL=metrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../metrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAcH;;;GAGG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAejE;AASD,MAAM,WAAW,eAAe;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE;QACR,KAAK,EAAE,MAAM,CAAA;QACb,MAAM,EAAE,MAAM,CAAA;QACd;;;WAGG;QACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC7B,UAAU,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAA;QACzE,eAAe,EAAE,MAAM,CAAA;KACvB,CAAA;CACD;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,IAAI,eAAe,CAoBjD;AAED,uDAAuD;AACvD,wBAAgB,mBAAmB,IAAI,IAAI,CAU1C"}
package/out/metrics.js ADDED
@@ -0,0 +1,80 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Generic in-process timing metrics (ported from `mailwoman/server/metrics.ts`, #485
7
+ * observability, for the api-kit plumbing layer — see the 2026-07-12 Phase 4a plan's
8
+ * dependency-arrow correction). Dependency-free: monotonic counters per string-keyed tier + a
9
+ * bounded reservoir of recent latencies for percentile estimation. Callers own their tier
10
+ * vocabulary (e.g. `mailwoman`'s `ResolutionTier`); this module only ever sees `string`.
11
+ * Surfaced by `GET /metrics`; reset on process restart (no persistence — scrape it). Per-process
12
+ * state: under `node:cluster` each worker reports its own snapshot — aggregate at the scraper.
13
+ */
14
+ /** Recent-latency reservoir size. ~2k samples gives stable p99 without unbounded memory. */
15
+ const MAX_SAMPLES = 2048;
16
+ const latencies = [];
17
+ let writeIdx = 0;
18
+ /** Null-prototype: tier keys are created lazily on first use, not eagerly pre-populated. */
19
+ const tierCounts = Object.create(null);
20
+ let total = 0;
21
+ let errors = 0;
22
+ const startedAt = Date.now();
23
+ /**
24
+ * Record one completed timed operation: its wall-clock latency and which tier produced it (or `"error"`). Tier keys are
25
+ * created on first use; "error" is reserved and counts toward errors instead of a tier.
26
+ */
27
+ export function recordTimed(latencyMs, tier) {
28
+ total++;
29
+ if (tier === "error") {
30
+ errors++;
31
+ }
32
+ else {
33
+ tierCounts[tier] = (tierCounts[tier] ?? 0) + 1;
34
+ }
35
+ if (latencies.length < MAX_SAMPLES) {
36
+ latencies.push(latencyMs);
37
+ }
38
+ else {
39
+ latencies[writeIdx] = latencyMs;
40
+ writeIdx = (writeIdx + 1) % MAX_SAMPLES;
41
+ }
42
+ }
43
+ function percentile(sorted, p) {
44
+ if (sorted.length === 0)
45
+ return 0;
46
+ const idx = Math.min(sorted.length - 1, Math.floor(p * sorted.length));
47
+ return Math.round(sorted[idx] * 100) / 100;
48
+ }
49
+ /** Current metrics snapshot — sorted-reservoir percentiles + counters. */
50
+ export function metricsSnapshot() {
51
+ const sorted = [...latencies].sort((a, b) => a - b);
52
+ return {
53
+ uptime_s: Math.round((Date.now() - startedAt) / 1000),
54
+ timings: {
55
+ total,
56
+ errors,
57
+ tiers: { ...tierCounts },
58
+ latency_ms: sorted.length
59
+ ? {
60
+ p50: percentile(sorted, 0.5),
61
+ p90: percentile(sorted, 0.9),
62
+ p99: percentile(sorted, 0.99),
63
+ max: Math.round(sorted[sorted.length - 1] * 100) / 100,
64
+ }
65
+ : null,
66
+ latency_samples: sorted.length,
67
+ },
68
+ };
69
+ }
70
+ /** Test-only reset of all counters + the reservoir. */
71
+ export function resetMetricsForTest() {
72
+ latencies.length = 0;
73
+ writeIdx = 0;
74
+ for (const key of Object.keys(tierCounts)) {
75
+ delete tierCounts[key];
76
+ }
77
+ total = 0;
78
+ errors = 0;
79
+ }
80
+ //# sourceMappingURL=metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.js","sourceRoot":"","sources":["../metrics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * OpenAPI emit helpers. The document is always derived from the route table — never
7
+ * handwritten. 3.1 is the published flavor; 3.0 exists solely for client generators that lag
8
+ * (progenitor), replacing the old hand-downgrade step.
9
+ */
10
+ import type { OpenAPIHono } from "@hono/zod-openapi";
11
+ /**
12
+ * The document config stamped into emitted documents: `title`/`version`/`description`/`summary`/`license`/`contact`
13
+ * land under the document's `info` block; `externalDocs`/`servers`/`tags`/`security` are top-level document fields. All
14
+ * fields beyond `title`/`version` are optional — existing callers that only pass those two are unaffected.
15
+ */
16
+ export interface OpenAPIDocInfo {
17
+ title: string;
18
+ version: string;
19
+ description?: string;
20
+ summary?: string;
21
+ license?: {
22
+ name: string;
23
+ identifier?: string;
24
+ };
25
+ contact?: {
26
+ name?: string;
27
+ url?: string;
28
+ };
29
+ externalDocs?: {
30
+ description?: string;
31
+ url: string;
32
+ };
33
+ servers?: Array<{
34
+ url: string;
35
+ description?: string;
36
+ variables?: Record<string, {
37
+ default: string;
38
+ description?: string;
39
+ }>;
40
+ }>;
41
+ tags?: Array<{
42
+ name: string;
43
+ description?: string;
44
+ }>;
45
+ security?: unknown[];
46
+ }
47
+ /** Mount the OpenAPI 3.1 document endpoint on `app` (default `/openapi.json`). */
48
+ export declare function attachOpenAPIDocs(app: OpenAPIHono, info: OpenAPIDocInfo, path?: string): void;
49
+ /** Emit both document flavors programmatically (build artifacts, parity tests, client generation). */
50
+ export declare function emitOpenAPIDocuments(app: OpenAPIHono, info: OpenAPIDocInfo): {
51
+ v31: object;
52
+ v30: object;
53
+ };
54
+ /**
55
+ * The shared body of every surface's `openapi` CLI subcommand (the three drop-ins + `mailwoman openapi`): pick the
56
+ * flavor `emitOpenAPIDocuments` produces (`--flavor 3.0` → the 3.0.3 diet client generators like progenitor want;
57
+ * default 3.1.0), then either print it to stdout or write it to `out`. Always compact (single-line) JSON — never
58
+ * pretty-printed — so the stdout form is a stable `startsWith('{"openapi":"3.1.0"')` smoke check, matching what a live
59
+ * `/openapi.json` response looks like. `out`'s parent directory is created if missing (the docs build writes into a
60
+ * gitignored, not-yet-existing `docs/static/openapi/`). One place owns this so the four emitters can't drift out of
61
+ * lockstep with each other.
62
+ */
63
+ export declare function printOpenAPIDocument(app: OpenAPIHono, info: OpenAPIDocInfo, opts?: {
64
+ flavor?: string;
65
+ out?: string;
66
+ }): void;
67
+ //# sourceMappingURL=openapi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../openapi.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAEpD;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC/C,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACzC,YAAY,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IACpD,OAAO,CAAC,EAAE,KAAK,CAAC;QACf,GAAG,EAAE,MAAM,CAAA;QACX,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KACrE,CAAC,CAAA;IACF,IAAI,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACpD,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAA;CACpB;AAeD,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,SAAkB,GAAG,IAAI,CAItG;AAED,sGAAsG;AACtG,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,cAAc,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAKzG;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CACnC,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,cAAc,EACpB,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAC1C,IAAI,CAUN"}
package/out/openapi.js ADDED
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * OpenAPI emit helpers. The document is always derived from the route table — never
7
+ * handwritten. 3.1 is the published flavor; 3.0 exists solely for client generators that lag
8
+ * (progenitor), replacing the old hand-downgrade step.
9
+ */
10
+ import { mkdirSync, writeFileSync } from "node:fs";
11
+ import { dirname } from "node:path";
12
+ /** Split an `OpenAPIDocInfo` into the document's `info` block and its top-level sibling fields. */
13
+ function toDocumentConfig(info) {
14
+ const { title, version, description, summary, license, contact, externalDocs, servers, tags, security } = info;
15
+ return {
16
+ info: { title, version, description, summary, license, contact },
17
+ externalDocs,
18
+ servers,
19
+ tags,
20
+ security,
21
+ };
22
+ }
23
+ /** Mount the OpenAPI 3.1 document endpoint on `app` (default `/openapi.json`). */
24
+ export function attachOpenAPIDocs(app, info, path = "/openapi.json") {
25
+ // openapi3-ts's InfoObject/OpenAPIObject carry an `x-${string}` extension index signature that
26
+ // a plain interface can't satisfy — cast at the boundary rather than widening the public type.
27
+ app.doc31(path, { openapi: "3.1.0", ...toDocumentConfig(info) });
28
+ }
29
+ /** Emit both document flavors programmatically (build artifacts, parity tests, client generation). */
30
+ export function emitOpenAPIDocuments(app, info) {
31
+ return {
32
+ v31: app.getOpenAPI31Document({ openapi: "3.1.0", ...toDocumentConfig(info) }),
33
+ v30: app.getOpenAPIDocument({ openapi: "3.0.3", ...toDocumentConfig(info) }),
34
+ };
35
+ }
36
+ /**
37
+ * The shared body of every surface's `openapi` CLI subcommand (the three drop-ins + `mailwoman openapi`): pick the
38
+ * flavor `emitOpenAPIDocuments` produces (`--flavor 3.0` → the 3.0.3 diet client generators like progenitor want;
39
+ * default 3.1.0), then either print it to stdout or write it to `out`. Always compact (single-line) JSON — never
40
+ * pretty-printed — so the stdout form is a stable `startsWith('{"openapi":"3.1.0"')` smoke check, matching what a live
41
+ * `/openapi.json` response looks like. `out`'s parent directory is created if missing (the docs build writes into a
42
+ * gitignored, not-yet-existing `docs/static/openapi/`). One place owns this so the four emitters can't drift out of
43
+ * lockstep with each other.
44
+ */
45
+ export function printOpenAPIDocument(app, info, opts = {}) {
46
+ const { v31, v30 } = emitOpenAPIDocuments(app, info);
47
+ const json = JSON.stringify(opts.flavor === "3.0" ? v30 : v31);
48
+ if (opts.out) {
49
+ mkdirSync(dirname(opts.out), { recursive: true });
50
+ writeFileSync(opts.out, `${json}\n`);
51
+ }
52
+ else {
53
+ console.log(json);
54
+ }
55
+ }
56
+ //# sourceMappingURL=openapi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi.js","sourceRoot":"","sources":["../openapi.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AA0BnC,mGAAmG;AACnG,SAAS,gBAAgB,CAAC,IAAoB;IAC7C,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAA;IAE9G,OAAO;QACN,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE;QAChE,YAAY;QACZ,OAAO;QACP,IAAI;QACJ,QAAQ;KACR,CAAA;AACF,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,iBAAiB,CAAC,GAAgB,EAAE,IAAoB,EAAE,IAAI,GAAG,eAAe;IAC/F,+FAA+F;IAC/F,+FAA+F;IAC/F,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,gBAAgB,CAAC,IAAI,CAAC,EAAW,CAAC,CAAA;AAC1E,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,oBAAoB,CAAC,GAAgB,EAAE,IAAoB;IAC1E,OAAO;QACN,GAAG,EAAE,GAAG,CAAC,oBAAoB,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,gBAAgB,CAAC,IAAI,CAAC,EAAW,CAAC;QACvF,GAAG,EAAE,GAAG,CAAC,kBAAkB,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,gBAAgB,CAAC,IAAI,CAAC,EAAW,CAAC;KACrF,CAAA;AACF,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CACnC,GAAgB,EAChB,IAAoB,EACpB,OAA0C,EAAE;IAE5C,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,oBAAoB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IACpD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;IAE9D,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QACd,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QACjD,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,CAAA;IACrC,CAAC;SAAM,CAAC;QACP,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAClB,CAAC;AACF,CAAC"}
package/out/serve.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Node serve wrapper over `@hono/node-server`. The one place the node listener is created —
7
+ * surface packages stay web-standard (they only export `fetch`-shaped apps) so an edge
8
+ * deployment needs no changes to them.
9
+ */
10
+ /** A `fetch`-shaped request handler (what `OpenAPIHono.fetch` provides). */
11
+ export type FetchLike = (request: Request, ...args: never[]) => Response | Promise<Response>;
12
+ export interface ServeNodeOptions {
13
+ fetch: FetchLike;
14
+ port: number;
15
+ hostname: string;
16
+ /** Called once the listener is bound — receives the actual port (useful with `port: 0`). */
17
+ onListen?: (info: {
18
+ port: number;
19
+ address: string;
20
+ }) => void;
21
+ }
22
+ export interface ServerHandle {
23
+ close(): Promise<void>;
24
+ }
25
+ /** Boot a node HTTP listener for a Hono app. Returns a handle whose `close()` resolves when the listener is down. */
26
+ export declare function serveNode(options: ServeNodeOptions): ServerHandle;
27
+ //# sourceMappingURL=serve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../serve.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,4EAA4E;AAC5E,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;AAE5F,MAAM,WAAW,gBAAgB;IAChC,KAAK,EAAE,SAAS,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;CAC5D;AAED,MAAM,WAAW,YAAY;IAC5B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACtB;AAED,qHAAqH;AACrH,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,YAAY,CAWjE"}
package/out/serve.js ADDED
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Node serve wrapper over `@hono/node-server`. The one place the node listener is created —
7
+ * surface packages stay web-standard (they only export `fetch`-shaped apps) so an edge
8
+ * deployment needs no changes to them.
9
+ */
10
+ import { serve } from "@hono/node-server";
11
+ /** Boot a node HTTP listener for a Hono app. Returns a handle whose `close()` resolves when the listener is down. */
12
+ export function serveNode(options) {
13
+ const server = serve({ fetch: options.fetch, port: options.port, hostname: options.hostname }, (info) => options.onListen?.({ port: info.port, address: info.address }));
14
+ return {
15
+ close: () => new Promise((resolve, reject) => {
16
+ server.close((error) => (error ? reject(error) : resolve()));
17
+ }),
18
+ };
19
+ }
20
+ //# sourceMappingURL=serve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../serve.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAiBzC,qHAAqH;AACrH,MAAM,UAAU,SAAS,CAAC,OAAyB;IAClD,MAAM,MAAM,GAAG,KAAK,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAc,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,CAChH,OAAO,CAAC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAC9D,CAAA;IAED,OAAO;QACN,KAAK,EAAE,GAAG,EAAE,CACX,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;QACrE,CAAC,CAAC;KACH,CAAA;AACF,CAAC"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@mailwoman/api-kit",
3
+ "version": "6.0.0",
4
+ "description": "API plumbing for Mailwoman's HTTP surfaces — Hono node serve wrapper, OpenAPI emit helpers, shared wire atoms. Plumbing only: domain schemas live with their routes.",
5
+ "license": "AGPL-3.0-only OR LicenseRef-Commercial",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/sister-software/mailwoman.git",
9
+ "directory": "api-kit"
10
+ },
11
+ "files": [
12
+ "out/**/*.js",
13
+ "out/**/*.js.map",
14
+ "out/**/*.d.ts",
15
+ "out/**/*.d.ts.map",
16
+ "README.md"
17
+ ],
18
+ "type": "module",
19
+ "exports": {
20
+ "./package.json": "./package.json",
21
+ ".": {
22
+ "types": "./out/index.d.ts",
23
+ "default": "./out/index.js"
24
+ }
25
+ },
26
+ "publishConfig": {
27
+ "exports": {
28
+ "./package.json": "./package.json",
29
+ ".": {
30
+ "types": "./out/index.d.ts",
31
+ "default": "./out/index.js"
32
+ }
33
+ },
34
+ "access": "public"
35
+ },
36
+ "dependencies": {
37
+ "@hono/node-server": "^2.0.8",
38
+ "@hono/zod-openapi": "^1.4.0",
39
+ "hono": "^4.12.29",
40
+ "zod": "^4.4.3"
41
+ }
42
+ }