@voyant-travel/flights 0.120.1 → 0.121.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/dist/hono.d.ts ADDED
@@ -0,0 +1,41 @@
1
+ import type { HonoModule } from "@voyant-travel/hono";
2
+ import { type Context, Hono } from "hono";
3
+ import type { FlightConnectorAdapter } from "./contract/adapter.js";
4
+ import type { FlightOrder } from "./contract/types.js";
5
+ /** A resolved payment session for a flight hold order. */
6
+ export interface FlightOrderPaymentSummary {
7
+ sessionId: string;
8
+ status: string;
9
+ }
10
+ /**
11
+ * Deployment-supplied payment integration for flight hold orders. The flights
12
+ * module stays payment-provider agnostic; the deployment wires its finance /
13
+ * payment provider here.
14
+ */
15
+ export interface FlightPaymentIntegration {
16
+ /** Ensure (idempotently) a payment session exists for a hold order. */
17
+ ensureOrderSession(c: Context, order: FlightOrder, contact?: {
18
+ email?: string;
19
+ phone?: string;
20
+ }): Promise<FlightOrderPaymentSummary | null>;
21
+ /** Bulk-resolve the most relevant payment session per order id (no N+1). */
22
+ fetchOrderSessions(c: Context, orderIds: string[]): Promise<Map<string, FlightOrderPaymentSummary>>;
23
+ }
24
+ export interface FlightsRouteOptions {
25
+ /**
26
+ * Resolve the flight connector adapter for a request. The deployment picks
27
+ * the demo connector or a real GDS (Sabre / Amadeus / Duffel).
28
+ */
29
+ resolveAdapter(c: Context): FlightConnectorAdapter;
30
+ /** Optional payment-link integration for hold orders. */
31
+ payment?: FlightPaymentIntegration;
32
+ }
33
+ export type FlightsHonoModuleOptions = FlightsRouteOptions;
34
+ /** Build the flight admin routes (relative paths; mount at `/v1/admin/flights`). */
35
+ export declare function createFlightAdminRoutes(options: FlightsRouteOptions): Hono;
36
+ /**
37
+ * The flights route module — mounts the admin routes at `/v1/admin/flights`.
38
+ * A deployment composes this and supplies the connector + payment options.
39
+ */
40
+ export declare function createFlightsHonoModule(options: FlightsHonoModuleOptions): HonoModule;
41
+ //# sourceMappingURL=hono.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../src/hono.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAErD,OAAO,EAAE,KAAK,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAEzC,OAAO,KAAK,EAEV,sBAAsB,EAEvB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,KAAK,EAGV,WAAW,EAIZ,MAAM,qBAAqB,CAAA;AAO5B,0DAA0D;AAC1D,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC,uEAAuE;IACvE,kBAAkB,CAChB,CAAC,EAAE,OAAO,EACV,KAAK,EAAE,WAAW,EAClB,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3C,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAAA;IAC5C,4EAA4E;IAC5E,kBAAkB,CAChB,CAAC,EAAE,OAAO,EACV,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC,CAAA;CACnD;AAED,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,cAAc,CAAC,CAAC,EAAE,OAAO,GAAG,sBAAsB,CAAA;IAClD,yDAAyD;IACzD,OAAO,CAAC,EAAE,wBAAwB,CAAA;CACnC;AAED,MAAM,MAAM,wBAAwB,GAAG,mBAAmB,CAAA;AA8B1D,oFAAoF;AACpF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAsO1E;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,GAAG,UAAU,CAKrF"}
package/dist/hono.js ADDED
@@ -0,0 +1,265 @@
1
+ import { ilike, or } from "drizzle-orm";
2
+ import { Hono } from "hono";
3
+ import { referenceAircraft, referenceAirlines, referenceAirports, } from "./reference/local-postgres.js";
4
+ function buildContext(c) {
5
+ return {
6
+ connectionId: "demo",
7
+ correlationId: c.req.header("x-request-id") ?? undefined,
8
+ };
9
+ }
10
+ // `c.var.db` is set by the createApp DB middleware; the global ContextVariableMap
11
+ // doesn't declare it, so cast at the call site to keep type-safety local.
12
+ function getDb(c) {
13
+ return c.var.db;
14
+ }
15
+ function attachPaymentSession(order, summary) {
16
+ if (!summary)
17
+ return order;
18
+ return {
19
+ ...order,
20
+ providerData: {
21
+ ...(order.providerData ?? {}),
22
+ paymentSessionId: summary.sessionId,
23
+ paymentStatus: summary.status,
24
+ },
25
+ };
26
+ }
27
+ /** Build the flight admin routes (relative paths; mount at `/v1/admin/flights`). */
28
+ export function createFlightAdminRoutes(options) {
29
+ const { resolveAdapter, payment } = options;
30
+ const hono = new Hono();
31
+ // ── Search ──────────────────────────────────────────────────────────────
32
+ hono.post("/search", async (c) => {
33
+ let body;
34
+ try {
35
+ body = await c.req.json();
36
+ }
37
+ catch {
38
+ return c.json({ error: "Invalid JSON body" }, 400);
39
+ }
40
+ if (!body.slices?.length)
41
+ return c.json({ error: "slices is required" }, 400);
42
+ if (!body.passengers?.adults || body.passengers.adults < 1) {
43
+ return c.json({ error: "passengers.adults must be at least 1" }, 400);
44
+ }
45
+ try {
46
+ const response = await resolveAdapter(c).searchFlights(buildContext(c), body);
47
+ return c.json(response);
48
+ }
49
+ catch (err) {
50
+ return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
51
+ }
52
+ });
53
+ // ── Ancillaries ─────────────────────────────────────────────────────────
54
+ hono.post("/ancillaries", async (c) => {
55
+ let body;
56
+ try {
57
+ body = await c.req.json();
58
+ }
59
+ catch {
60
+ return c.json({ error: "Invalid JSON body" }, 400);
61
+ }
62
+ if (!body.offerId)
63
+ return c.json({ error: "offerId is required" }, 400);
64
+ const adapter = resolveAdapter(c);
65
+ if (!adapter.getAncillaries) {
66
+ return c.json({ error: "Connector does not declare flight/ancillaries capability" }, 501);
67
+ }
68
+ try {
69
+ const response = await adapter.getAncillaries(buildContext(c), body);
70
+ return c.json(response);
71
+ }
72
+ catch (err) {
73
+ return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
74
+ }
75
+ });
76
+ // ── Seat map ────────────────────────────────────────────────────────────
77
+ hono.post("/seatmap", async (c) => {
78
+ let body;
79
+ try {
80
+ body = await c.req.json();
81
+ }
82
+ catch {
83
+ return c.json({ error: "Invalid JSON body" }, 400);
84
+ }
85
+ if (!body.offerId)
86
+ return c.json({ error: "offerId is required" }, 400);
87
+ if (!body.segmentId)
88
+ return c.json({ error: "segmentId is required" }, 400);
89
+ const adapter = resolveAdapter(c);
90
+ if (!adapter.getSeatMap) {
91
+ return c.json({ error: "Connector does not declare flight/seatmap capability" }, 501);
92
+ }
93
+ try {
94
+ const response = await adapter.getSeatMap(buildContext(c), body);
95
+ return c.json(response);
96
+ }
97
+ catch (err) {
98
+ const message = err instanceof Error ? err.message : String(err);
99
+ return c.json({ error: message }, /not found/i.test(message) ? 404 : 500);
100
+ }
101
+ });
102
+ // ── Re-price ────────────────────────────────────────────────────────────
103
+ hono.post("/price", async (c) => {
104
+ let body;
105
+ try {
106
+ body = await c.req.json();
107
+ }
108
+ catch {
109
+ return c.json({ error: "Invalid JSON body" }, 400);
110
+ }
111
+ if (!body.offerId)
112
+ return c.json({ error: "offerId is required" }, 400);
113
+ try {
114
+ const response = await resolveAdapter(c).priceOffer(buildContext(c), body);
115
+ return c.json(response);
116
+ }
117
+ catch (err) {
118
+ return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
119
+ }
120
+ });
121
+ // ── Book ────────────────────────────────────────────────────────────────
122
+ hono.post("/book", async (c) => {
123
+ let body;
124
+ try {
125
+ body = await c.req.json();
126
+ }
127
+ catch {
128
+ return c.json({ error: "Invalid JSON body" }, 400);
129
+ }
130
+ if (!body.offerId)
131
+ return c.json({ error: "offerId is required" }, 400);
132
+ if (!body.passengers?.length)
133
+ return c.json({ error: "passengers is required" }, 400);
134
+ try {
135
+ const response = await resolveAdapter(c).bookFlight(buildContext(c), body);
136
+ // Hold is the default intent; eagerly create the payment session so the
137
+ // order page can show the shareable link immediately.
138
+ const isHold = !body.paymentIntent || body.paymentIntent.type === "hold";
139
+ if (isHold && response.order && payment) {
140
+ const summary = await payment.ensureOrderSession(c, response.order, body.contact);
141
+ response.order = attachPaymentSession(response.order, summary);
142
+ }
143
+ return c.json(response);
144
+ }
145
+ catch (err) {
146
+ return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
147
+ }
148
+ });
149
+ // ── List orders ─────────────────────────────────────────────────────────
150
+ hono.get("/orders", async (c) => {
151
+ const adapter = resolveAdapter(c);
152
+ if (!adapter.listOrders) {
153
+ return c.json({ error: "Adapter does not support listing orders" }, 501);
154
+ }
155
+ const url = new URL(c.req.url);
156
+ const limitParam = url.searchParams.get("limit");
157
+ const cursor = url.searchParams.get("cursor") ?? undefined;
158
+ const search = url.searchParams.get("q") ?? url.searchParams.get("search") ?? undefined;
159
+ const statusParam = url.searchParams.getAll("status");
160
+ const status = statusParam.length > 0 ? statusParam : undefined;
161
+ const limit = limitParam
162
+ ? Math.max(1, Math.min(100, Number.parseInt(limitParam, 10)))
163
+ : undefined;
164
+ const paymentStatusParam = url.searchParams.getAll("paymentStatus");
165
+ const paymentStatusFilter = paymentStatusParam.length > 0 ? new Set(paymentStatusParam) : null;
166
+ try {
167
+ const response = await adapter.listOrders(buildContext(c), {
168
+ ...(limit !== undefined ? { limit } : {}),
169
+ ...(cursor !== undefined ? { cursor } : {}),
170
+ ...(search !== undefined ? { search } : {}),
171
+ ...(status !== undefined ? { status } : {}),
172
+ });
173
+ if (payment) {
174
+ const sessionByOrderId = await payment.fetchOrderSessions(c, response.orders.map((o) => o.orderId));
175
+ response.orders = response.orders.map((order) => attachPaymentSession(order, sessionByOrderId.get(order.orderId) ?? null));
176
+ }
177
+ if (paymentStatusFilter) {
178
+ response.orders = response.orders.filter((o) => paymentStatusFilter.has(o.providerData?.paymentStatus ?? "none"));
179
+ }
180
+ return c.json(response);
181
+ }
182
+ catch (err) {
183
+ return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
184
+ }
185
+ });
186
+ // ── Get order ───────────────────────────────────────────────────────────
187
+ hono.get("/orders/:orderId", async (c) => {
188
+ const orderId = c.req.param("orderId");
189
+ if (!orderId)
190
+ return c.json({ error: "orderId is required" }, 400);
191
+ try {
192
+ const response = await resolveAdapter(c).getOrder(buildContext(c), orderId);
193
+ if (response.order && payment) {
194
+ const summary = await payment.ensureOrderSession(c, response.order, response.order.contact);
195
+ response.order = attachPaymentSession(response.order, summary);
196
+ }
197
+ return c.json(response);
198
+ }
199
+ catch (err) {
200
+ const message = err instanceof Error ? err.message : String(err);
201
+ return c.json({ error: message }, /not found/i.test(message) ? 404 : 500);
202
+ }
203
+ });
204
+ // ── Cancel order ────────────────────────────────────────────────────────
205
+ hono.post("/orders/:orderId/cancel", async (c) => {
206
+ const orderId = c.req.param("orderId");
207
+ if (!orderId)
208
+ return c.json({ error: "orderId is required" }, 400);
209
+ let body = {};
210
+ try {
211
+ body = await c.req.json();
212
+ }
213
+ catch {
214
+ // Body is optional for cancel.
215
+ }
216
+ try {
217
+ const response = await resolveAdapter(c).cancelOrder(buildContext(c), orderId, body.reason);
218
+ return c.json(response);
219
+ }
220
+ catch (err) {
221
+ const message = err instanceof Error ? err.message : String(err);
222
+ return c.json({ error: message }, /not found/i.test(message) ? 404 : 500);
223
+ }
224
+ });
225
+ // ── Reference: airports (with substring search) ───────────────────────────
226
+ hono.get("/reference/airports", async (c) => {
227
+ const db = getDb(c);
228
+ const q = c.req.query("q")?.trim();
229
+ const limit = Math.min(Number(c.req.query("limit") ?? 50), 200);
230
+ let rows;
231
+ if (q) {
232
+ const pattern = `%${q}%`;
233
+ rows = await db
234
+ .select()
235
+ .from(referenceAirports)
236
+ .where(or(ilike(referenceAirports.iataCode, pattern), ilike(referenceAirports.city, pattern), ilike(referenceAirports.name, pattern)))
237
+ .limit(limit);
238
+ }
239
+ else {
240
+ rows = await db.select().from(referenceAirports).limit(limit);
241
+ }
242
+ return c.json({ data: rows });
243
+ });
244
+ // ── Reference: airlines (full list) ───────────────────────────────────────
245
+ hono.get("/reference/airlines", async (c) => {
246
+ const rows = await getDb(c).select().from(referenceAirlines);
247
+ return c.json({ data: rows });
248
+ });
249
+ // ── Reference: aircraft (full list) ────────────────────────────────────────
250
+ hono.get("/reference/aircraft", async (c) => {
251
+ const rows = await getDb(c).select().from(referenceAircraft);
252
+ return c.json({ data: rows });
253
+ });
254
+ return hono;
255
+ }
256
+ /**
257
+ * The flights route module — mounts the admin routes at `/v1/admin/flights`.
258
+ * A deployment composes this and supplies the connector + payment options.
259
+ */
260
+ export function createFlightsHonoModule(options) {
261
+ return {
262
+ module: { name: "flights" },
263
+ adminRoutes: createFlightAdminRoutes(options),
264
+ };
265
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=hono.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.test.d.ts","sourceRoot":"","sources":["../src/hono.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,53 @@
1
+ import { Hono } from "hono";
2
+ import { describe, expect, it, vi } from "vitest";
3
+ import { createFlightAdminRoutes, createFlightsHonoModule } from "./hono.js";
4
+ function stubAdapter(over = {}) {
5
+ return {
6
+ capabilities: { provider: "stub", declared: [] },
7
+ searchFlights: vi.fn(async () => ({ offers: [] })),
8
+ priceOffer: vi.fn(async () => ({ offer: {}, valid: true })),
9
+ bookFlight: vi.fn(async () => ({ order: { orderId: "ord_1", passengers: [] } })),
10
+ getOrder: vi.fn(async () => ({ order: { orderId: "ord_1", passengers: [] } })),
11
+ cancelOrder: vi.fn(async () => ({ order: { orderId: "ord_1" } })),
12
+ ...over,
13
+ };
14
+ }
15
+ function mount(adapter) {
16
+ return new Hono().route("/v1/admin/flights", createFlightAdminRoutes({ resolveAdapter: () => adapter }));
17
+ }
18
+ describe("flights hono module", () => {
19
+ it("createFlightsHonoModule exposes adminRoutes on module 'flights'", () => {
20
+ const mod = createFlightsHonoModule({ resolveAdapter: () => stubAdapter() });
21
+ expect(mod.module.name).toBe("flights");
22
+ expect(mod.adminRoutes).toBeDefined();
23
+ });
24
+ it("POST /search delegates to the resolved adapter", async () => {
25
+ const adapter = stubAdapter();
26
+ const app = mount(adapter);
27
+ const res = await app.request("/v1/admin/flights/search", {
28
+ method: "POST",
29
+ headers: { "Content-Type": "application/json" },
30
+ body: JSON.stringify({ slices: [{}], passengers: { adults: 1 } }),
31
+ });
32
+ expect(res.status).toBe(200);
33
+ expect(adapter.searchFlights).toHaveBeenCalledTimes(1);
34
+ });
35
+ it("POST /search validates required fields", async () => {
36
+ const app = mount(stubAdapter());
37
+ const res = await app.request("/v1/admin/flights/search", {
38
+ method: "POST",
39
+ headers: { "Content-Type": "application/json" },
40
+ body: JSON.stringify({ passengers: { adults: 1 } }),
41
+ });
42
+ expect(res.status).toBe(400);
43
+ });
44
+ it("returns 501 when the connector lacks an optional capability", async () => {
45
+ const app = mount(stubAdapter());
46
+ const res = await app.request("/v1/admin/flights/ancillaries", {
47
+ method: "POST",
48
+ headers: { "Content-Type": "application/json" },
49
+ body: JSON.stringify({ offerId: "off_1" }),
50
+ });
51
+ expect(res.status).toBe(501);
52
+ });
53
+ });
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { type AdapterLogger, CAPABILITY_NOT_SUPPORTED, type FlightAdapterCapabilities, type FlightAdapterContext, type FlightAdapterEnvironment, type FlightBookResponse, type FlightCancelReason, type FlightCancelResponse, FlightCapabilityNotSupportedError, type FlightConnectorAdapter, type FlightGetOrderResponse, type FlightPriceRequest, type FlightPriceResponse, type FlightSearchResponse, requireCapability, } from "./contract/adapter.js";
2
2
  export * from "./contract/schemas.js";
3
3
  export * from "./contract/types.js";
4
+ export { createFlightAdminRoutes, createFlightsHonoModule, type FlightOrderPaymentSummary, type FlightPaymentIntegration, type FlightsHonoModuleOptions, type FlightsRouteOptions, } from "./hono.js";
4
5
  export { type ConnectionResult, type ConnectionSearchStatus, type FanOutFlightSearchOptions, type FanOutFlightSearchResult, fanOutFlightSearch, type MergedFlightOffer, } from "./orchestration/fan-out.js";
5
6
  export { itineraryFingerprint } from "./orchestration/fingerprint.js";
6
7
  export { type Aircraft, type Airline, type Airport, dedupeCodes, type ReferenceDataCapabilities, type ReferenceDataProvider, } from "./reference/contract.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,aAAa,EAClB,wBAAwB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,iCAAiC,EACjC,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,iBAAiB,GAClB,MAAM,uBAAuB,CAAA;AAC9B,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,kBAAkB,EAClB,KAAK,iBAAiB,GACvB,MAAM,4BAA4B,CAAA;AAEnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAA;AAErE,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,OAAO,EACZ,KAAK,OAAO,EACZ,WAAW,EACX,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,GAC3B,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,oCAAoC,EACpC,KAAK,qCAAqC,EAC1C,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,+BAA+B,CAAA;AACtC,OAAO,EACL,mCAAmC,EACnC,KAAK,2BAA2B,EAChC,KAAK,yBAAyB,GAC/B,MAAM,8BAA8B,CAAA;AAErC,OAAO,EACL,KAAK,+BAA+B,EACpC,wBAAwB,GACzB,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,aAAa,EAClB,wBAAwB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,iCAAiC,EACjC,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,iBAAiB,GAClB,MAAM,uBAAuB,CAAA;AAC9B,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AAGnC,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,GACzB,MAAM,WAAW,CAAA;AAClB,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,kBAAkB,EAClB,KAAK,iBAAiB,GACvB,MAAM,4BAA4B,CAAA;AAEnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAA;AAErE,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,OAAO,EACZ,KAAK,OAAO,EACZ,WAAW,EACX,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,GAC3B,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,oCAAoC,EACpC,KAAK,qCAAqC,EAC1C,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,+BAA+B,CAAA;AACtC,OAAO,EACL,mCAAmC,EACnC,KAAK,2BAA2B,EAChC,KAAK,yBAAyB,GAC/B,MAAM,8BAA8B,CAAA;AAErC,OAAO,EACL,KAAK,+BAA+B,EACpC,wBAAwB,GACzB,MAAM,eAAe,CAAA"}
package/dist/index.js CHANGED
@@ -3,6 +3,9 @@
3
3
  export { CAPABILITY_NOT_SUPPORTED, FlightCapabilityNotSupportedError, requireCapability, } from "./contract/adapter.js";
4
4
  export * from "./contract/schemas.js";
5
5
  export * from "./contract/types.js";
6
+ // Flight admin HTTP routes (module-owned; the deployment supplies connector +
7
+ // payment options).
8
+ export { createFlightAdminRoutes, createFlightsHonoModule, } from "./hono.js";
6
9
  export { fanOutFlightSearch, } from "./orchestration/fan-out.js";
7
10
  // Orchestration — fingerprinting + multi-connection fan-out.
8
11
  export { itineraryFingerprint } from "./orchestration/fingerprint.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voyant-travel/flights",
3
- "version": "0.120.1",
3
+ "version": "0.121.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -53,6 +53,11 @@
53
53
  "types": "./dist/reference/static-bundle.d.ts",
54
54
  "import": "./dist/reference/static-bundle.js",
55
55
  "default": "./dist/reference/static-bundle.js"
56
+ },
57
+ "./hono": {
58
+ "types": "./dist/hono.d.ts",
59
+ "import": "./dist/hono.js",
60
+ "default": "./dist/hono.js"
56
61
  }
57
62
  },
58
63
  "voyant": {
@@ -69,10 +74,12 @@
69
74
  },
70
75
  "dependencies": {
71
76
  "drizzle-orm": "^0.45.2",
77
+ "hono": "^4.12.10",
72
78
  "zod": "^4.3.6",
73
79
  "@voyant-travel/db": "^0.108.1",
74
- "@voyant-travel/catalog": "^0.118.1",
75
- "@voyant-travel/flights-contracts": "^0.104.4"
80
+ "@voyant-travel/catalog": "^0.119.0",
81
+ "@voyant-travel/flights-contracts": "^0.104.4",
82
+ "@voyant-travel/hono": "^0.111.0"
76
83
  },
77
84
  "devDependencies": {
78
85
  "typescript": "^6.0.2",