@voyant-travel/charters 0.117.2

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.
Files changed (108) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +16 -0
  3. package/dist/adapters/index.d.ts +254 -0
  4. package/dist/adapters/index.d.ts.map +1 -0
  5. package/dist/adapters/index.js +16 -0
  6. package/dist/adapters/memoize.d.ts +28 -0
  7. package/dist/adapters/memoize.d.ts.map +1 -0
  8. package/dist/adapters/memoize.js +121 -0
  9. package/dist/adapters/mock.d.ts +50 -0
  10. package/dist/adapters/mock.d.ts.map +1 -0
  11. package/dist/adapters/mock.js +194 -0
  12. package/dist/adapters/registry.d.ts +24 -0
  13. package/dist/adapters/registry.d.ts.map +1 -0
  14. package/dist/adapters/registry.js +40 -0
  15. package/dist/booking-extension.d.ts +895 -0
  16. package/dist/booking-extension.d.ts.map +1 -0
  17. package/dist/booking-extension.js +339 -0
  18. package/dist/catalog-policy.d.ts +23 -0
  19. package/dist/catalog-policy.d.ts.map +1 -0
  20. package/dist/catalog-policy.js +400 -0
  21. package/dist/content-shape.d.ts +5 -0
  22. package/dist/content-shape.d.ts.map +1 -0
  23. package/dist/content-shape.js +13 -0
  24. package/dist/draft-shape.d.ts +29 -0
  25. package/dist/draft-shape.d.ts.map +1 -0
  26. package/dist/draft-shape.js +63 -0
  27. package/dist/index.d.ts +31 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +55 -0
  30. package/dist/lib/key.d.ts +22 -0
  31. package/dist/lib/key.d.ts.map +1 -0
  32. package/dist/lib/key.js +24 -0
  33. package/dist/routes-public.d.ts +785 -0
  34. package/dist/routes-public.d.ts.map +1 -0
  35. package/dist/routes-public.js +234 -0
  36. package/dist/routes.d.ts +1744 -0
  37. package/dist/routes.d.ts.map +1 -0
  38. package/dist/routes.js +543 -0
  39. package/dist/schema-core.d.ts +815 -0
  40. package/dist/schema-core.d.ts.map +1 -0
  41. package/dist/schema-core.js +98 -0
  42. package/dist/schema-itinerary.d.ts +239 -0
  43. package/dist/schema-itinerary.d.ts.map +1 -0
  44. package/dist/schema-itinerary.js +30 -0
  45. package/dist/schema-pricing.d.ts +385 -0
  46. package/dist/schema-pricing.d.ts.map +1 -0
  47. package/dist/schema-pricing.js +62 -0
  48. package/dist/schema-shared.d.ts +8 -0
  49. package/dist/schema-shared.d.ts.map +1 -0
  50. package/dist/schema-shared.js +37 -0
  51. package/dist/schema-sourced-content.d.ts +253 -0
  52. package/dist/schema-sourced-content.d.ts.map +1 -0
  53. package/dist/schema-sourced-content.js +44 -0
  54. package/dist/schema-yachts.d.ts +367 -0
  55. package/dist/schema-yachts.d.ts.map +1 -0
  56. package/dist/schema-yachts.js +30 -0
  57. package/dist/schema.d.ts +8 -0
  58. package/dist/schema.d.ts.map +1 -0
  59. package/dist/schema.js +7 -0
  60. package/dist/service-bookings-helpers.d.ts +20 -0
  61. package/dist/service-bookings-helpers.d.ts.map +1 -0
  62. package/dist/service-bookings-helpers.js +67 -0
  63. package/dist/service-bookings-local.d.ts +5 -0
  64. package/dist/service-bookings-local.d.ts.map +1 -0
  65. package/dist/service-bookings-local.js +177 -0
  66. package/dist/service-bookings-types.d.ts +88 -0
  67. package/dist/service-bookings-types.d.ts.map +1 -0
  68. package/dist/service-bookings-types.js +1 -0
  69. package/dist/service-bookings.d.ts +36 -0
  70. package/dist/service-bookings.d.ts.map +1 -0
  71. package/dist/service-bookings.js +267 -0
  72. package/dist/service-catalog-plane.d.ts +58 -0
  73. package/dist/service-catalog-plane.d.ts.map +1 -0
  74. package/dist/service-catalog-plane.js +145 -0
  75. package/dist/service-content-synthesizer.d.ts +42 -0
  76. package/dist/service-content-synthesizer.d.ts.map +1 -0
  77. package/dist/service-content-synthesizer.js +122 -0
  78. package/dist/service-content.d.ts +43 -0
  79. package/dist/service-content.d.ts.map +1 -0
  80. package/dist/service-content.js +248 -0
  81. package/dist/service-myba.d.ts +85 -0
  82. package/dist/service-myba.d.ts.map +1 -0
  83. package/dist/service-myba.js +88 -0
  84. package/dist/service-pricing.d.ts +64 -0
  85. package/dist/service-pricing.d.ts.map +1 -0
  86. package/dist/service-pricing.js +167 -0
  87. package/dist/service.d.ts +131 -0
  88. package/dist/service.d.ts.map +1 -0
  89. package/dist/service.js +279 -0
  90. package/dist/validation-core.d.ts +152 -0
  91. package/dist/validation-core.d.ts.map +1 -0
  92. package/dist/validation-core.js +66 -0
  93. package/dist/validation-itinerary.d.ts +43 -0
  94. package/dist/validation-itinerary.d.ts.map +1 -0
  95. package/dist/validation-itinerary.js +19 -0
  96. package/dist/validation-pricing.d.ts +103 -0
  97. package/dist/validation-pricing.d.ts.map +1 -0
  98. package/dist/validation-pricing.js +28 -0
  99. package/dist/validation-shared.d.ts +61 -0
  100. package/dist/validation-shared.d.ts.map +1 -0
  101. package/dist/validation-shared.js +60 -0
  102. package/dist/validation-yachts.d.ts +76 -0
  103. package/dist/validation-yachts.d.ts.map +1 -0
  104. package/dist/validation-yachts.js +36 -0
  105. package/dist/validation.d.ts +6 -0
  106. package/dist/validation.d.ts.map +1 -0
  107. package/dist/validation.js +5 -0
  108. package/package.json +116 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes-public.d.ts","sourceRoot":"","sources":["../src/routes-public.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAWjE,KAAK,GAAG,GAAG;IACT,SAAS,EAAE;QACT,EAAE,EAAE,kBAAkB,CAAA;KACvB,CAAA;CACF,CAAA;AAUD;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAgM7B,CAAA;AAEJ,MAAM,MAAM,oBAAoB,GAAG,OAAO,oBAAoB,CAAA"}
@@ -0,0 +1,234 @@
1
+ import { parseJsonBody, parseQuery } from "@voyant-travel/hono";
2
+ import { Hono } from "hono";
3
+ import { z } from "zod";
4
+ import { listCharterAdapters, resolveCharterAdapter } from "./adapters/registry.js";
5
+ import { parseUnifiedKey } from "./lib/key.js";
6
+ import { chartersService } from "./service.js";
7
+ import { composePerSuiteQuote, composeWholeYachtQuote, pricingService } from "./service-pricing.js";
8
+ import { productListQuerySchema, voyageListQuerySchema } from "./validation-core.js";
9
+ import { currencyCodeSchema } from "./validation-shared.js";
10
+ const perSuiteQuotePayload = z.object({
11
+ suiteId: z.string(),
12
+ currency: currencyCodeSchema,
13
+ });
14
+ const wholeYachtQuotePayload = z.object({
15
+ currency: currencyCodeSchema,
16
+ });
17
+ /**
18
+ * Public-facing charter routes. Browsing fans out to local `live` products +
19
+ * every registered adapter; detail routes accept either a TypeID slug or a
20
+ * `<provider>:<ref>` external key.
21
+ *
22
+ * Charters has no dedicated search index — the operator universe is small
23
+ * enough that direct `listEntries` fan-out is fine.
24
+ */
25
+ export const chartersPublicRoutes = new Hono()
26
+ .get("/", async (c) => {
27
+ const query = parseQuery(c, productListQuerySchema);
28
+ const local = await chartersService.listProducts(c.get("db"), { ...query, status: "live" });
29
+ const localItems = local.data.map((p) => ({
30
+ source: "local",
31
+ sourceProvider: null,
32
+ sourceRef: null,
33
+ key: p.slug,
34
+ product: p,
35
+ }));
36
+ const adapters = listCharterAdapters();
37
+ const settled = await Promise.allSettled(adapters.map((adapter) => adapter
38
+ .listEntries({ limit: query.limit })
39
+ .then((result) => ({ adapter, result }))));
40
+ const adapterItems = [];
41
+ for (let i = 0; i < settled.length; i++) {
42
+ const outcome = settled[i];
43
+ const adapter = adapters[i];
44
+ if (!outcome || !adapter)
45
+ continue;
46
+ if (outcome.status === "rejected")
47
+ continue;
48
+ for (const entry of outcome.value.result.entries) {
49
+ adapterItems.push({
50
+ source: "external",
51
+ sourceProvider: adapter.name,
52
+ sourceRef: entry.sourceRef,
53
+ key: `${adapter.name}:${entry.sourceRef.externalId}`,
54
+ product: entry,
55
+ });
56
+ }
57
+ }
58
+ return c.json({
59
+ data: [...localItems, ...adapterItems],
60
+ total: local.total + adapterItems.length,
61
+ limit: local.limit,
62
+ offset: local.offset,
63
+ });
64
+ })
65
+ .get("/products/:key", async (c) => {
66
+ const parsed = parseUnifiedKey(c.req.param("key"));
67
+ if (parsed.kind === "external") {
68
+ const adapter = resolveCharterAdapter(parsed.provider);
69
+ if (!adapter)
70
+ return c.json({ error: "adapter_not_registered" }, 501);
71
+ const product = await adapter.fetchProduct({ externalId: parsed.ref });
72
+ if (!product)
73
+ return c.json({ error: "not_found" }, 404);
74
+ const voyages = await adapter.listVoyagesForProduct({ externalId: parsed.ref });
75
+ const yacht = product.defaultYachtRef
76
+ ? await adapter.fetchYacht(product.defaultYachtRef)
77
+ : null;
78
+ return c.json({
79
+ data: {
80
+ source: "external",
81
+ sourceProvider: adapter.name,
82
+ sourceRef: product.sourceRef,
83
+ product,
84
+ voyages,
85
+ yacht,
86
+ },
87
+ });
88
+ }
89
+ if (parsed.kind === "local") {
90
+ const detail = await chartersService.getProductById(c.get("db"), parsed.id, {
91
+ withVoyages: true,
92
+ withYacht: true,
93
+ });
94
+ if (!detail || detail.status !== "live")
95
+ return c.json({ error: "not_found" }, 404);
96
+ return c.json({ data: detail });
97
+ }
98
+ // parsed.kind === "invalid": treat the raw param as a slug lookup
99
+ const result = await chartersService.listProducts(c.get("db"), {
100
+ status: "live",
101
+ limit: 1,
102
+ offset: 0,
103
+ });
104
+ const match = result.data.find((p) => p.slug === parsed.raw);
105
+ if (!match)
106
+ return c.json({ error: "not_found" }, 404);
107
+ const detail = await chartersService.getProductById(c.get("db"), match.id, {
108
+ withVoyages: true,
109
+ withYacht: true,
110
+ });
111
+ if (!detail || detail.status !== "live")
112
+ return c.json({ error: "not_found" }, 404);
113
+ return c.json({ data: detail });
114
+ })
115
+ .get("/voyages", async (c) => {
116
+ const query = parseQuery(c, voyageListQuerySchema);
117
+ const result = await chartersService.listVoyages(c.get("db"), query);
118
+ return c.json(result);
119
+ })
120
+ .get("/voyages/:key", async (c) => {
121
+ const parsed = parseUnifiedKey(c.req.param("key"));
122
+ if (parsed.kind === "invalid")
123
+ return c.json({ error: "invalid_key" }, 400);
124
+ if (parsed.kind === "external") {
125
+ const adapter = resolveCharterAdapter(parsed.provider);
126
+ if (!adapter)
127
+ return c.json({ error: "adapter_not_registered" }, 501);
128
+ const ref = { externalId: parsed.ref };
129
+ const voyage = await adapter.fetchVoyage(ref);
130
+ if (!voyage)
131
+ return c.json({ error: "not_found" }, 404);
132
+ const [suites, schedule] = await Promise.all([
133
+ adapter.fetchVoyageSuites(ref),
134
+ adapter.fetchVoyageSchedule(ref),
135
+ ]);
136
+ return c.json({
137
+ data: {
138
+ source: "external",
139
+ sourceProvider: adapter.name,
140
+ sourceRef: voyage.sourceRef,
141
+ voyage,
142
+ suites,
143
+ schedule,
144
+ },
145
+ });
146
+ }
147
+ const row = await chartersService.getVoyageById(c.get("db"), parsed.id, {
148
+ withSuites: true,
149
+ withSchedule: true,
150
+ });
151
+ if (!row)
152
+ return c.json({ error: "not_found" }, 404);
153
+ return c.json({ data: row });
154
+ })
155
+ .post("/voyages/:key/quote/per-suite", async (c) => {
156
+ const parsed = parseUnifiedKey(c.req.param("key"));
157
+ if (parsed.kind === "invalid")
158
+ return c.json({ error: "invalid_key" }, 400);
159
+ const payload = await parseJsonBody(c, perSuiteQuotePayload);
160
+ if (parsed.kind === "external") {
161
+ const adapter = resolveCharterAdapter(parsed.provider);
162
+ if (!adapter)
163
+ return c.json({ error: "adapter_not_registered" }, 501);
164
+ const suites = await adapter.fetchVoyageSuites({ externalId: parsed.ref });
165
+ const matching = suites.find((s) => s.sourceRef.externalId === payload.suiteId);
166
+ if (!matching)
167
+ return c.json({ error: "no_matching_suite" }, 404);
168
+ const quote = composePerSuiteQuote({
169
+ voyageId: parsed.ref,
170
+ suite: {
171
+ id: matching.sourceRef.externalId,
172
+ suiteName: matching.suiteName,
173
+ pricesByCurrency: matching.pricesByCurrency ?? {},
174
+ portFeesByCurrency: matching.portFeesByCurrency ?? {},
175
+ },
176
+ currency: payload.currency,
177
+ });
178
+ return c.json({ data: quote });
179
+ }
180
+ const quote = await pricingService.quotePerSuite(c.get("db"), {
181
+ suiteId: payload.suiteId,
182
+ currency: payload.currency,
183
+ });
184
+ return c.json({ data: quote });
185
+ })
186
+ .post("/voyages/:key/quote/whole-yacht", async (c) => {
187
+ const parsed = parseUnifiedKey(c.req.param("key"));
188
+ if (parsed.kind === "invalid")
189
+ return c.json({ error: "invalid_key" }, 400);
190
+ const payload = await parseJsonBody(c, wholeYachtQuotePayload);
191
+ if (parsed.kind === "external") {
192
+ const adapter = resolveCharterAdapter(parsed.provider);
193
+ if (!adapter)
194
+ return c.json({ error: "adapter_not_registered" }, 501);
195
+ const ref = { externalId: parsed.ref };
196
+ const voyage = await adapter.fetchVoyage(ref);
197
+ if (!voyage)
198
+ return c.json({ error: "not_found" }, 404);
199
+ const product = await adapter.fetchProduct(voyage.productRef);
200
+ const quote = composeWholeYachtQuote({
201
+ voyage: {
202
+ id: voyage.sourceRef.externalId,
203
+ wholeYachtPricesByCurrency: voyage.wholeYachtPricesByCurrency ?? {},
204
+ apaPercentOverride: voyage.apaPercentOverride ?? null,
205
+ },
206
+ productDefaultApaPercent: product?.defaultApaPercent ?? null,
207
+ currency: payload.currency,
208
+ });
209
+ return c.json({ data: quote });
210
+ }
211
+ const quote = await pricingService.quoteWholeYacht(c.get("db"), {
212
+ voyageId: parsed.id,
213
+ currency: payload.currency,
214
+ });
215
+ return c.json({ data: quote });
216
+ })
217
+ .get("/yachts/:key", async (c) => {
218
+ const parsed = parseUnifiedKey(c.req.param("key"));
219
+ if (parsed.kind === "invalid")
220
+ return c.json({ error: "invalid_key" }, 400);
221
+ if (parsed.kind === "external") {
222
+ const adapter = resolveCharterAdapter(parsed.provider);
223
+ if (!adapter)
224
+ return c.json({ error: "adapter_not_registered" }, 501);
225
+ const yacht = await adapter.fetchYacht({ externalId: parsed.ref });
226
+ if (!yacht)
227
+ return c.json({ error: "not_found" }, 404);
228
+ return c.json({ data: yacht });
229
+ }
230
+ const row = await chartersService.getYachtById(c.get("db"), parsed.id);
231
+ if (!row)
232
+ return c.json({ error: "not_found" }, 404);
233
+ return c.json({ data: row });
234
+ });