@voyant-travel/cruises 0.118.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 (210) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +50 -0
  3. package/dist/adapters/connect-compat.d.ts +20 -0
  4. package/dist/adapters/connect-compat.d.ts.map +1 -0
  5. package/dist/adapters/connect-compat.js +71 -0
  6. package/dist/adapters/contract-fixture.d.ts +32 -0
  7. package/dist/adapters/contract-fixture.d.ts.map +1 -0
  8. package/dist/adapters/contract-fixture.js +152 -0
  9. package/dist/adapters/index.d.ts +331 -0
  10. package/dist/adapters/index.d.ts.map +1 -0
  11. package/dist/adapters/index.js +16 -0
  12. package/dist/adapters/memoize.d.ts +28 -0
  13. package/dist/adapters/memoize.d.ts.map +1 -0
  14. package/dist/adapters/memoize.js +131 -0
  15. package/dist/adapters/mock.d.ts +44 -0
  16. package/dist/adapters/mock.d.ts.map +1 -0
  17. package/dist/adapters/mock.js +192 -0
  18. package/dist/adapters/registry.d.ts +26 -0
  19. package/dist/adapters/registry.d.ts.map +1 -0
  20. package/dist/adapters/registry.js +42 -0
  21. package/dist/adapters/source-adapter-shim.d.ts +80 -0
  22. package/dist/adapters/source-adapter-shim.d.ts.map +1 -0
  23. package/dist/adapters/source-adapter-shim.js +390 -0
  24. package/dist/booking-engine/handler.d.ts +108 -0
  25. package/dist/booking-engine/handler.d.ts.map +1 -0
  26. package/dist/booking-engine/handler.js +225 -0
  27. package/dist/booking-engine/index.d.ts +9 -0
  28. package/dist/booking-engine/index.d.ts.map +1 -0
  29. package/dist/booking-engine/index.js +8 -0
  30. package/dist/booking-extension.d.ts +1179 -0
  31. package/dist/booking-extension.d.ts.map +1 -0
  32. package/dist/booking-extension.js +342 -0
  33. package/dist/cabin-features.d.ts +8 -0
  34. package/dist/cabin-features.d.ts.map +1 -0
  35. package/dist/cabin-features.js +7 -0
  36. package/dist/catalog-policy-cabins.d.ts +18 -0
  37. package/dist/catalog-policy-cabins.d.ts.map +1 -0
  38. package/dist/catalog-policy-cabins.js +96 -0
  39. package/dist/catalog-policy-core.d.ts +3 -0
  40. package/dist/catalog-policy-core.d.ts.map +1 -0
  41. package/dist/catalog-policy-core.js +247 -0
  42. package/dist/catalog-policy-structure.d.ts +3 -0
  43. package/dist/catalog-policy-structure.d.ts.map +1 -0
  44. package/dist/catalog-policy-structure.js +387 -0
  45. package/dist/catalog-policy.d.ts +15 -0
  46. package/dist/catalog-policy.d.ts.map +1 -0
  47. package/dist/catalog-policy.js +19 -0
  48. package/dist/content-shape.d.ts +5 -0
  49. package/dist/content-shape.d.ts.map +1 -0
  50. package/dist/content-shape.js +13 -0
  51. package/dist/draft-shape.d.ts +59 -0
  52. package/dist/draft-shape.d.ts.map +1 -0
  53. package/dist/draft-shape.js +98 -0
  54. package/dist/events.d.ts +21 -0
  55. package/dist/events.d.ts.map +1 -0
  56. package/dist/events.js +21 -0
  57. package/dist/index.d.ts +43 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +66 -0
  60. package/dist/lib/key.d.ts +41 -0
  61. package/dist/lib/key.d.ts.map +1 -0
  62. package/dist/lib/key.js +100 -0
  63. package/dist/routes-booking-payloads.d.ts +133 -0
  64. package/dist/routes-booking-payloads.d.ts.map +1 -0
  65. package/dist/routes-booking-payloads.js +142 -0
  66. package/dist/routes-content.d.ts +53 -0
  67. package/dist/routes-content.d.ts.map +1 -0
  68. package/dist/routes-content.js +158 -0
  69. package/dist/routes-core.d.ts +4 -0
  70. package/dist/routes-core.d.ts.map +1 -0
  71. package/dist/routes-core.js +68 -0
  72. package/dist/routes-detail.d.ts +4 -0
  73. package/dist/routes-detail.d.ts.map +1 -0
  74. package/dist/routes-detail.js +261 -0
  75. package/dist/routes-env.d.ts +13 -0
  76. package/dist/routes-env.d.ts.map +1 -0
  77. package/dist/routes-env.js +1 -0
  78. package/dist/routes-keying.d.ts +28 -0
  79. package/dist/routes-keying.d.ts.map +1 -0
  80. package/dist/routes-keying.js +70 -0
  81. package/dist/routes-public.d.ts +911 -0
  82. package/dist/routes-public.d.ts.map +1 -0
  83. package/dist/routes-public.js +252 -0
  84. package/dist/routes-sailings-prices.d.ts +4 -0
  85. package/dist/routes-sailings-prices.d.ts.map +1 -0
  86. package/dist/routes-sailings-prices.js +278 -0
  87. package/dist/routes-search-index.d.ts +4 -0
  88. package/dist/routes-search-index.d.ts.map +1 -0
  89. package/dist/routes-search-index.js +25 -0
  90. package/dist/routes-ships.d.ts +4 -0
  91. package/dist/routes-ships.d.ts.map +1 -0
  92. package/dist/routes-ships.js +147 -0
  93. package/dist/routes-voyage-groups.d.ts +4 -0
  94. package/dist/routes-voyage-groups.d.ts.map +1 -0
  95. package/dist/routes-voyage-groups.js +85 -0
  96. package/dist/routes.d.ts +5 -0
  97. package/dist/routes.d.ts.map +1 -0
  98. package/dist/routes.js +14 -0
  99. package/dist/schema-cabins.d.ts +1098 -0
  100. package/dist/schema-cabins.d.ts.map +1 -0
  101. package/dist/schema-cabins.js +105 -0
  102. package/dist/schema-content.d.ts +577 -0
  103. package/dist/schema-content.d.ts.map +1 -0
  104. package/dist/schema-content.js +63 -0
  105. package/dist/schema-core.d.ts +1790 -0
  106. package/dist/schema-core.d.ts.map +1 -0
  107. package/dist/schema-core.js +171 -0
  108. package/dist/schema-itinerary.d.ts +556 -0
  109. package/dist/schema-itinerary.d.ts.map +1 -0
  110. package/dist/schema-itinerary.js +50 -0
  111. package/dist/schema-pricing.d.ts +633 -0
  112. package/dist/schema-pricing.d.ts.map +1 -0
  113. package/dist/schema-pricing.js +73 -0
  114. package/dist/schema-search.d.ts +611 -0
  115. package/dist/schema-search.d.ts.map +1 -0
  116. package/dist/schema-search.js +64 -0
  117. package/dist/schema-shared.d.ts +23 -0
  118. package/dist/schema-shared.d.ts.map +1 -0
  119. package/dist/schema-shared.js +107 -0
  120. package/dist/schema-sourced-content.d.ts +247 -0
  121. package/dist/schema-sourced-content.d.ts.map +1 -0
  122. package/dist/schema-sourced-content.js +38 -0
  123. package/dist/schema.d.ts +10 -0
  124. package/dist/schema.d.ts.map +1 -0
  125. package/dist/schema.js +9 -0
  126. package/dist/service-booking-helpers.d.ts +12 -0
  127. package/dist/service-booking-helpers.d.ts.map +1 -0
  128. package/dist/service-booking-helpers.js +94 -0
  129. package/dist/service-booking-types.d.ts +101 -0
  130. package/dist/service-booking-types.d.ts.map +1 -0
  131. package/dist/service-booking-types.js +1 -0
  132. package/dist/service-bookings.d.ts +46 -0
  133. package/dist/service-bookings.d.ts.map +1 -0
  134. package/dist/service-bookings.js +420 -0
  135. package/dist/service-catalog-plane-cabins.d.ts +24 -0
  136. package/dist/service-catalog-plane-cabins.d.ts.map +1 -0
  137. package/dist/service-catalog-plane-cabins.js +90 -0
  138. package/dist/service-catalog-plane.d.ts +74 -0
  139. package/dist/service-catalog-plane.d.ts.map +1 -0
  140. package/dist/service-catalog-plane.js +194 -0
  141. package/dist/service-content-synthesizer.d.ts +42 -0
  142. package/dist/service-content-synthesizer.d.ts.map +1 -0
  143. package/dist/service-content-synthesizer.js +144 -0
  144. package/dist/service-content.d.ts +74 -0
  145. package/dist/service-content.d.ts.map +1 -0
  146. package/dist/service-content.js +315 -0
  147. package/dist/service-core.d.ts +134 -0
  148. package/dist/service-core.d.ts.map +1 -0
  149. package/dist/service-core.js +257 -0
  150. package/dist/service-detach.d.ts +18 -0
  151. package/dist/service-detach.d.ts.map +1 -0
  152. package/dist/service-detach.js +199 -0
  153. package/dist/service-enrichment.d.ts +11 -0
  154. package/dist/service-enrichment.d.ts.map +1 -0
  155. package/dist/service-enrichment.js +47 -0
  156. package/dist/service-external-refresh.d.ts +39 -0
  157. package/dist/service-external-refresh.d.ts.map +1 -0
  158. package/dist/service-external-refresh.js +47 -0
  159. package/dist/service-itinerary.d.ts +22 -0
  160. package/dist/service-itinerary.d.ts.map +1 -0
  161. package/dist/service-itinerary.js +34 -0
  162. package/dist/service-prices.d.ts +46 -0
  163. package/dist/service-prices.d.ts.map +1 -0
  164. package/dist/service-prices.js +89 -0
  165. package/dist/service-pricing.d.ts +97 -0
  166. package/dist/service-pricing.d.ts.map +1 -0
  167. package/dist/service-pricing.js +198 -0
  168. package/dist/service-sailings.d.ts +48 -0
  169. package/dist/service-sailings.d.ts.map +1 -0
  170. package/dist/service-sailings.js +145 -0
  171. package/dist/service-search-types.d.ts +54 -0
  172. package/dist/service-search-types.d.ts.map +1 -0
  173. package/dist/service-search-types.js +1 -0
  174. package/dist/service-search.d.ts +65 -0
  175. package/dist/service-search.d.ts.map +1 -0
  176. package/dist/service-search.js +467 -0
  177. package/dist/service-shared.d.ts +22 -0
  178. package/dist/service-shared.d.ts.map +1 -0
  179. package/dist/service-shared.js +22 -0
  180. package/dist/service-ships.d.ts +47 -0
  181. package/dist/service-ships.d.ts.map +1 -0
  182. package/dist/service-ships.js +156 -0
  183. package/dist/service.d.ts +255 -0
  184. package/dist/service.d.ts.map +1 -0
  185. package/dist/service.js +12 -0
  186. package/dist/validation-cabins.d.ts +267 -0
  187. package/dist/validation-cabins.d.ts.map +1 -0
  188. package/dist/validation-cabins.js +77 -0
  189. package/dist/validation-content.d.ts +123 -0
  190. package/dist/validation-content.d.ts.map +1 -0
  191. package/dist/validation-content.js +40 -0
  192. package/dist/validation-core.d.ts +393 -0
  193. package/dist/validation-core.d.ts.map +1 -0
  194. package/dist/validation-core.js +162 -0
  195. package/dist/validation-itinerary.d.ts +123 -0
  196. package/dist/validation-itinerary.d.ts.map +1 -0
  197. package/dist/validation-itinerary.js +47 -0
  198. package/dist/validation-pricing.d.ts +137 -0
  199. package/dist/validation-pricing.d.ts.map +1 -0
  200. package/dist/validation-pricing.js +49 -0
  201. package/dist/validation-search.d.ts +118 -0
  202. package/dist/validation-search.d.ts.map +1 -0
  203. package/dist/validation-search.js +60 -0
  204. package/dist/validation-shared.d.ts +123 -0
  205. package/dist/validation-shared.d.ts.map +1 -0
  206. package/dist/validation-shared.js +103 -0
  207. package/dist/validation.d.ts +8 -0
  208. package/dist/validation.d.ts.map +1 -0
  209. package/dist/validation.js +7 -0
  210. package/package.json +146 -0
@@ -0,0 +1,261 @@
1
+ import { parseJsonBody } from "@voyant-travel/hono";
2
+ import { parseUnifiedKey } from "./lib/key.js";
3
+ import { adapterNotRegistered, entityIdFromExternal, invalidKey, makeExternalKey, readContentScope, registryNotConfigured, resolveExternal, } from "./routes-keying.js";
4
+ import { cruisesService } from "./service.js";
5
+ import { getCruiseContent } from "./service-content.js";
6
+ import { detachExternalCruise } from "./service-detach.js";
7
+ import { insertEnrichmentProgramSchema, replaceEnrichmentProgramsSchema, } from "./validation-content.js";
8
+ import { updateCruiseSchema } from "./validation-core.js";
9
+ import { replaceCruiseDaysSchema } from "./validation-itinerary.js";
10
+ export function registerCruiseDetailRoutes(app) {
11
+ app
12
+ // --- per-cruise (parses unified key, dispatches local or external) ---
13
+ // Keep wildcard key routes after static admin subresources so reserved
14
+ // segments such as /sailings, /ships, and /prices reach their handlers.
15
+ // External branch dispatches through the catalog content service
16
+ // (cache-first, SWR refresh, synthesizer fallback) — flipped from
17
+ // ad-hoc adapter.fetchCruise() per the catalog-sourced-content
18
+ // migration. Returns the rich CruiseContent shape; templates that
19
+ // need backwards-compatible ExternalCruise can post-process the
20
+ // response.
21
+ .get("/:key", async (c) => {
22
+ const parsed = parseUnifiedKey(c.req.param("key"));
23
+ if (parsed.kind === "invalid")
24
+ return c.json(invalidKey(parsed.raw), 400);
25
+ if (parsed.kind === "external") {
26
+ const registry = c.get("sourceAdapterRegistry");
27
+ if (!registry)
28
+ return c.json(registryNotConfigured(), 503);
29
+ const entityId = entityIdFromExternal(parsed);
30
+ const result = await getCruiseContent(c.get("db"), entityId, readContentScope(c), {
31
+ registry,
32
+ });
33
+ if (!result) {
34
+ return c.json({
35
+ error: "not_found",
36
+ detail: `No sourced-entry row for cruise ${parsed.provider}:${parsed.ref} (entity ${entityId}). Run discovery first or check that an adapter is registered for "${parsed.provider}".`,
37
+ }, 404);
38
+ }
39
+ return c.json({
40
+ data: {
41
+ source: "external",
42
+ sourceProvider: parsed.provider,
43
+ sourceRef: parsed.ref,
44
+ entityId,
45
+ content: result.content,
46
+ servedLocale: result.resolution.served_locale,
47
+ matchKind: result.resolution.match_kind,
48
+ contentSource: result.source,
49
+ servedStale: result.served_stale,
50
+ synthesized: result.synthesized,
51
+ machineTranslated: result.machine_translated,
52
+ },
53
+ });
54
+ }
55
+ const includeRaw = c.req.query("include") ?? "";
56
+ const includes = new Set(includeRaw
57
+ .split(",")
58
+ .map((s) => s.trim())
59
+ .filter(Boolean));
60
+ const row = await cruisesService.getCruiseById(c.get("db"), parsed.id, {
61
+ withSailings: includes.has("sailings"),
62
+ withDays: includes.has("days"),
63
+ });
64
+ if (!row)
65
+ return c.json({ error: "not_found" }, 404);
66
+ return c.json({
67
+ data: {
68
+ source: "local",
69
+ sourceProvider: null,
70
+ sourceRef: null,
71
+ cruise: row,
72
+ },
73
+ });
74
+ })
75
+ .put("/:key", async (c) => {
76
+ const parsed = parseUnifiedKey(c.req.param("key"));
77
+ if (parsed.kind === "external") {
78
+ return c.json({
79
+ error: "external_cruise_read_only",
80
+ detail: `External cruise from '${parsed.provider}' cannot be edited locally. Edit at the upstream system, or POST /:key/detach to convert to a local cruise first.`,
81
+ }, 409);
82
+ }
83
+ if (parsed.kind === "invalid")
84
+ return c.json(invalidKey(parsed.raw), 400);
85
+ const data = await parseJsonBody(c, updateCruiseSchema);
86
+ const row = await cruisesService.updateCruise(c.get("db"), parsed.id, data, {
87
+ eventBus: c.get("eventBus"),
88
+ });
89
+ if (!row)
90
+ return c.json({ error: "not_found" }, 404);
91
+ return c.json({ data: row });
92
+ })
93
+ .delete("/:key", async (c) => {
94
+ const parsed = parseUnifiedKey(c.req.param("key"));
95
+ if (parsed.kind === "external") {
96
+ return c.json({
97
+ error: "external_cruise_read_only",
98
+ detail: "External cruises can't be deleted locally.",
99
+ }, 409);
100
+ }
101
+ if (parsed.kind === "invalid")
102
+ return c.json(invalidKey(parsed.raw), 400);
103
+ const row = await cruisesService.archiveCruise(c.get("db"), parsed.id, {
104
+ eventBus: c.get("eventBus"),
105
+ });
106
+ if (!row)
107
+ return c.json({ error: "not_found" }, 404);
108
+ return c.json({ data: row });
109
+ })
110
+ .post("/:key/aggregates/recompute", async (c) => {
111
+ const parsed = parseUnifiedKey(c.req.param("key"));
112
+ if (parsed.kind === "external") {
113
+ return c.json({ error: "external_cruise_read_only", detail: "Aggregates only apply to local cruises." }, 409);
114
+ }
115
+ if (parsed.kind === "invalid")
116
+ return c.json(invalidKey(parsed.raw), 400);
117
+ const row = await cruisesService.recomputeCruiseAggregates(c.get("db"), parsed.id);
118
+ if (!row)
119
+ return c.json({ error: "not_found" }, 404);
120
+ return c.json({ data: row });
121
+ })
122
+ .get("/:key/sailings", async (c) => {
123
+ const parsed = parseUnifiedKey(c.req.param("key"));
124
+ if (parsed.kind === "invalid")
125
+ return c.json(invalidKey(parsed.raw), 400);
126
+ if (parsed.kind === "external") {
127
+ const ext = resolveExternal(parsed);
128
+ if (!ext)
129
+ return c.json(adapterNotRegistered(parsed.provider), 501);
130
+ const sailings = await ext.adapter.listSailingsForCruise(ext.sourceRef);
131
+ return c.json({
132
+ data: sailings.map((s) => ({
133
+ source: "external",
134
+ sourceProvider: ext.adapter.name,
135
+ key: makeExternalKey(ext.adapter, s.sourceRef),
136
+ sailing: s,
137
+ })),
138
+ total: sailings.length,
139
+ });
140
+ }
141
+ const result = await cruisesService.listSailings(c.get("db"), {
142
+ cruiseId: parsed.id,
143
+ limit: 100,
144
+ offset: 0,
145
+ });
146
+ return c.json(result);
147
+ })
148
+ .put("/:key/days/bulk", async (c) => {
149
+ const parsed = parseUnifiedKey(c.req.param("key"));
150
+ if (parsed.kind === "external") {
151
+ return c.json({ error: "external_cruise_read_only" }, 409);
152
+ }
153
+ if (parsed.kind === "invalid")
154
+ return c.json(invalidKey(parsed.raw), 400);
155
+ const payload = await parseJsonBody(c, replaceCruiseDaysSchema.omit({ cruiseId: true }));
156
+ const days = await cruisesService.replaceCruiseDays(c.get("db"), {
157
+ cruiseId: parsed.id,
158
+ days: payload.days,
159
+ });
160
+ return c.json({ data: days });
161
+ })
162
+ // --- external-only operations ---
163
+ // Refresh dispatches through the catalog content service. The
164
+ // invalidator marks the cache row stale; the subsequent
165
+ // getCruiseContent call sees the staleness and triggers a SWR
166
+ // refresh. Templates that need synchronous "force fresh from
167
+ // upstream" semantics should call adapter.getContent() directly
168
+ // — this route's contract is "best effort refresh, eventually
169
+ // consistent."
170
+ .post("/:key/refresh", async (c) => {
171
+ const parsed = parseUnifiedKey(c.req.param("key"));
172
+ if (parsed.kind !== "external")
173
+ return c.json({ error: "local_cruise_no_refresh" }, 400);
174
+ const registry = c.get("sourceAdapterRegistry");
175
+ if (!registry)
176
+ return c.json(registryNotConfigured(), 503);
177
+ const entityId = entityIdFromExternal(parsed);
178
+ const { invalidateCruiseContentOnDrift } = await import("./service-content.js");
179
+ await invalidateCruiseContentOnDrift(c.get("db"), {
180
+ id: `cnde_refresh_${Date.now()}`,
181
+ entity_module: "cruises",
182
+ entity_id: entityId,
183
+ kind: "content_invalidated",
184
+ detected_at: new Date(),
185
+ });
186
+ const result = await getCruiseContent(c.get("db"), entityId, readContentScope(c), {
187
+ registry,
188
+ });
189
+ if (!result) {
190
+ return c.json({
191
+ error: "not_found",
192
+ detail: `No sourced-entry row for cruise ${parsed.provider}:${parsed.ref} (entity ${entityId}).`,
193
+ }, 404);
194
+ }
195
+ return c.json({
196
+ data: {
197
+ source: "external",
198
+ sourceProvider: parsed.provider,
199
+ sourceRef: parsed.ref,
200
+ entityId,
201
+ content: result.content,
202
+ contentSource: result.source,
203
+ servedStale: result.served_stale,
204
+ refreshedAt: new Date().toISOString(),
205
+ },
206
+ });
207
+ })
208
+ .post("/:key/detach", async (c) => {
209
+ const parsed = parseUnifiedKey(c.req.param("key"));
210
+ if (parsed.kind !== "external")
211
+ return c.json({ error: "local_cruise_no_detach" }, 400);
212
+ const ext = resolveExternal(parsed);
213
+ if (!ext)
214
+ return c.json(adapterNotRegistered(parsed.provider), 501);
215
+ const cruise = await detachExternalCruise(c.get("db"), ext.adapter, ext.sourceRef);
216
+ return c.json({ data: cruise }, 201);
217
+ })
218
+ // --- enrichment programs (expedition-focused; local cruises only) ---
219
+ .get("/:key/enrichment", async (c) => {
220
+ const parsed = parseUnifiedKey(c.req.param("key"));
221
+ if (parsed.kind === "external") {
222
+ const ext = resolveExternal(parsed);
223
+ if (!ext)
224
+ return c.json(adapterNotRegistered(parsed.provider), 501);
225
+ // Adapters surface enrichment via the rich cruise detail; we return an
226
+ // empty list here for shape compatibility. Templates that need richer
227
+ // external enrichment should read from adapter.fetchCruise() directly.
228
+ return c.json({ data: [] });
229
+ }
230
+ if (parsed.kind === "invalid")
231
+ return c.json(invalidKey(parsed.raw), 400);
232
+ const programs = await cruisesService.listEnrichmentPrograms(c.get("db"), parsed.id);
233
+ return c.json({ data: programs });
234
+ })
235
+ .post("/:key/enrichment", async (c) => {
236
+ const parsed = parseUnifiedKey(c.req.param("key"));
237
+ if (parsed.kind === "external")
238
+ return c.json({ error: "external_cruise_read_only" }, 409);
239
+ if (parsed.kind === "invalid")
240
+ return c.json(invalidKey(parsed.raw), 400);
241
+ const data = await parseJsonBody(c, insertEnrichmentProgramSchema.omit({ cruiseId: true }));
242
+ const row = await cruisesService.createEnrichmentProgram(c.get("db"), {
243
+ ...data,
244
+ cruiseId: parsed.id,
245
+ });
246
+ return c.json({ data: row }, 201);
247
+ })
248
+ .put("/:key/enrichment/bulk", async (c) => {
249
+ const parsed = parseUnifiedKey(c.req.param("key"));
250
+ if (parsed.kind === "external")
251
+ return c.json({ error: "external_cruise_read_only" }, 409);
252
+ if (parsed.kind === "invalid")
253
+ return c.json(invalidKey(parsed.raw), 400);
254
+ const payload = await parseJsonBody(c, replaceEnrichmentProgramsSchema.omit({ cruiseId: true }));
255
+ const rows = await cruisesService.replaceEnrichmentPrograms(c.get("db"), {
256
+ cruiseId: parsed.id,
257
+ programs: payload.programs,
258
+ });
259
+ return c.json({ data: rows });
260
+ });
261
+ }
@@ -0,0 +1,13 @@
1
+ import type { SourceAdapterRegistry } from "@voyant-travel/catalog/booking-engine";
2
+ import type { EventBus } from "@voyant-travel/core";
3
+ import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
4
+ export type CruiseRoutesEnv = {
5
+ Variables: {
6
+ db: PostgresJsDatabase;
7
+ userId?: string;
8
+ eventBus?: EventBus;
9
+ /** Catalog source-adapter registry used by external cruise content routes. */
10
+ sourceAdapterRegistry?: SourceAdapterRegistry;
11
+ };
12
+ };
13
+ //# sourceMappingURL=routes-env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes-env.d.ts","sourceRoot":"","sources":["../src/routes-env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAA;AAClF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAEjE,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE;QACT,EAAE,EAAE,kBAAkB,CAAA;QACtB,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,QAAQ,CAAC,EAAE,QAAQ,CAAA;QACnB,8EAA8E;QAC9E,qBAAqB,CAAC,EAAE,qBAAqB,CAAA;KAC9C,CAAA;CACF,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ import type { Context } from "hono";
2
+ import type { CruiseAdapter, SourceRef } from "./adapters/index.js";
3
+ import { type ParsedKey } from "./lib/key.js";
4
+ import type { CruiseContentScope } from "./service-content.js";
5
+ export declare const adapterNotRegistered: (provider: string) => {
6
+ error: string;
7
+ detail: string;
8
+ };
9
+ export declare const invalidKey: (raw: string) => {
10
+ error: string;
11
+ detail: string;
12
+ };
13
+ export declare function resolveExternal(parsed: Extract<ParsedKey, {
14
+ kind: "external";
15
+ }>): {
16
+ adapter: CruiseAdapter;
17
+ sourceRef: SourceRef;
18
+ } | null;
19
+ export declare function makeExternalKey(adapter: CruiseAdapter, ref: SourceRef): string;
20
+ export declare const registryNotConfigured: () => {
21
+ error: string;
22
+ detail: string;
23
+ };
24
+ export declare function entityIdFromExternal(parsed: Extract<ParsedKey, {
25
+ kind: "external";
26
+ }>): string;
27
+ export declare function readContentScope(c: Context): CruiseContentScope;
28
+ //# sourceMappingURL=routes-keying.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes-keying.d.ts","sourceRoot":"","sources":["../src/routes-keying.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAEnC,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAEnE,OAAO,EAGL,KAAK,SAAS,EAEf,MAAM,cAAc,CAAA;AACrB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAE9D,eAAO,MAAM,oBAAoB,GAAI,UAAU,MAAM;;;CAMnD,CAAA;AAEF,eAAO,MAAM,UAAU,GAAI,KAAK,MAAM;;;CAGpC,CAAA;AAEF,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,EAAE;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,GAAG;IACjF,OAAO,EAAE,aAAa,CAAA;IACtB,SAAS,EAAE,SAAS,CAAA;CACrB,GAAG,IAAI,CAIP;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,SAAS,GAAG,MAAM,CAE9E;AAED,eAAO,MAAM,qBAAqB;;;CAIhC,CAAA;AAEF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,EAAE;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,GAAG,MAAM,CAE7F;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,GAAG,kBAAkB,CAiB/D"}
@@ -0,0 +1,70 @@
1
+ import { resolveCruiseAdapter } from "./adapters/registry.js";
2
+ import { encodeSourceRef, makeExternalSourceKey, sourceRefFromExternalKeyRef, } from "./lib/key.js";
3
+ export const adapterNotRegistered = (provider) => ({
4
+ error: "adapter_not_registered",
5
+ detail: "No CruiseAdapter registered for source provider '" +
6
+ provider +
7
+ "'. Register one at app startup via registerCruiseAdapter() - see docs/architecture/cruises-module.md section 10.",
8
+ });
9
+ export const invalidKey = (raw) => ({
10
+ error: "invalid_key",
11
+ detail: `Unrecognized cruise key: ${raw}`,
12
+ });
13
+ export function resolveExternal(parsed) {
14
+ const adapter = resolveCruiseAdapter(parsed.provider);
15
+ if (!adapter)
16
+ return null;
17
+ return { adapter, sourceRef: sourceRefFromExternalKeyRef(parsed.ref) };
18
+ }
19
+ export function makeExternalKey(adapter, ref) {
20
+ return makeExternalSourceKey(adapter.name, ref);
21
+ }
22
+ export const registryNotConfigured = () => ({
23
+ error: "registry_not_configured",
24
+ detail: "Cruise external detail/refresh dispatches through the catalog SourceAdapterRegistry. Inject one via Hono middleware: c.set('sourceAdapterRegistry', registry). See cruiseAdapterToSourceAdapter() in @voyant-travel/cruises/adapters.",
25
+ });
26
+ export function entityIdFromExternal(parsed) {
27
+ return `crus_${encodeSourceRef(sourceRefFromExternalKeyRef(parsed.ref))}`;
28
+ }
29
+ export function readContentScope(c) {
30
+ const localeParams = c.req.queries("locale") ?? c.req.queries("locales") ?? [];
31
+ const headerLocale = c.req.header("accept-language");
32
+ const acceptLanguageList = headerLocale ? parseAcceptLanguageHeader(headerLocale) : [];
33
+ const preferredLocales = localeParams.length > 0
34
+ ? localeParams
35
+ : acceptLanguageList.length > 0
36
+ ? acceptLanguageList
37
+ : ["en-GB"];
38
+ const acceptMt = c.req.query("accept_mt");
39
+ return {
40
+ preferredLocales,
41
+ market: c.req.query("market") ?? undefined,
42
+ currency: c.req.query("currency") ?? undefined,
43
+ acceptMachineTranslated: acceptMt != null ? acceptMt !== "false" && acceptMt !== "0" : true,
44
+ };
45
+ }
46
+ function parseAcceptLanguageHeader(header) {
47
+ const parts = header.split(",");
48
+ const ranked = [];
49
+ for (let i = 0; i < parts.length; i += 1) {
50
+ const part = parts[i].trim();
51
+ if (!part)
52
+ continue;
53
+ const [tagRaw, ...params] = part.split(";");
54
+ const tag = tagRaw.trim();
55
+ if (!tag || tag === "*")
56
+ continue;
57
+ let q = 1;
58
+ for (const p of params) {
59
+ const [k, v] = p.split("=").map((s) => s.trim());
60
+ if (k === "q" && v) {
61
+ const parsed = Number.parseFloat(v);
62
+ if (Number.isFinite(parsed))
63
+ q = parsed;
64
+ }
65
+ }
66
+ ranked.push({ tag, q, idx: i });
67
+ }
68
+ ranked.sort((a, b) => b.q - a.q || a.idx - b.idx);
69
+ return ranked.map((r) => r.tag);
70
+ }