@voyant-travel/inventory 0.1.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.
Files changed (311) hide show
  1. package/LICENSE +201 -0
  2. package/dist/action-ledger-drift.d.ts +29 -0
  3. package/dist/action-ledger-drift.d.ts.map +1 -0
  4. package/dist/action-ledger-drift.js +338 -0
  5. package/dist/action-ledger.d.ts +104 -0
  6. package/dist/action-ledger.d.ts.map +1 -0
  7. package/dist/action-ledger.js +100 -0
  8. package/dist/authoring/builder.d.ts +37 -0
  9. package/dist/authoring/builder.d.ts.map +1 -0
  10. package/dist/authoring/builder.js +248 -0
  11. package/dist/authoring/clone-content.d.ts +38 -0
  12. package/dist/authoring/clone-content.d.ts.map +1 -0
  13. package/dist/authoring/clone-content.js +367 -0
  14. package/dist/authoring/clone-pricing.d.ts +9 -0
  15. package/dist/authoring/clone-pricing.d.ts.map +1 -0
  16. package/dist/authoring/clone-pricing.js +242 -0
  17. package/dist/authoring/clone.d.ts +45 -0
  18. package/dist/authoring/clone.d.ts.map +1 -0
  19. package/dist/authoring/clone.js +142 -0
  20. package/dist/authoring/errors.d.ts +21 -0
  21. package/dist/authoring/errors.d.ts.map +1 -0
  22. package/dist/authoring/errors.js +13 -0
  23. package/dist/authoring/extension.d.ts +248 -0
  24. package/dist/authoring/extension.d.ts.map +1 -0
  25. package/dist/authoring/extension.js +116 -0
  26. package/dist/authoring/index.d.ts +12 -0
  27. package/dist/authoring/index.d.ts.map +1 -0
  28. package/dist/authoring/index.js +11 -0
  29. package/dist/authoring/schema.d.ts +85 -0
  30. package/dist/authoring/schema.d.ts.map +1 -0
  31. package/dist/authoring/schema.js +16 -0
  32. package/dist/authoring/service.d.ts +28 -0
  33. package/dist/authoring/service.d.ts.map +1 -0
  34. package/dist/authoring/service.js +66 -0
  35. package/dist/authoring/spec.d.ts +524 -0
  36. package/dist/authoring/spec.d.ts.map +1 -0
  37. package/dist/authoring/spec.js +167 -0
  38. package/dist/authoring/validate.d.ts +17 -0
  39. package/dist/authoring/validate.d.ts.map +1 -0
  40. package/dist/authoring/validate.js +83 -0
  41. package/dist/authoring.d.ts +2 -0
  42. package/dist/authoring.d.ts.map +1 -0
  43. package/dist/authoring.js +1 -0
  44. package/dist/booking-engine/handler-support.d.ts +91 -0
  45. package/dist/booking-engine/handler-support.d.ts.map +1 -0
  46. package/dist/booking-engine/handler-support.js +355 -0
  47. package/dist/booking-engine/handler.d.ts +404 -0
  48. package/dist/booking-engine/handler.d.ts.map +1 -0
  49. package/dist/booking-engine/handler.js +398 -0
  50. package/dist/booking-engine/index.d.ts +8 -0
  51. package/dist/booking-engine/index.d.ts.map +1 -0
  52. package/dist/booking-engine/index.js +7 -0
  53. package/dist/booking-engine.d.ts +2 -0
  54. package/dist/booking-engine.d.ts.map +1 -0
  55. package/dist/booking-engine.js +1 -0
  56. package/dist/booking-extension.d.ts +278 -0
  57. package/dist/booking-extension.d.ts.map +1 -0
  58. package/dist/booking-extension.js +161 -0
  59. package/dist/catalog-policy-departures.d.ts +52 -0
  60. package/dist/catalog-policy-departures.d.ts.map +1 -0
  61. package/dist/catalog-policy-departures.js +169 -0
  62. package/dist/catalog-policy-destinations.d.ts +43 -0
  63. package/dist/catalog-policy-destinations.d.ts.map +1 -0
  64. package/dist/catalog-policy-destinations.js +165 -0
  65. package/dist/catalog-policy-pricing.d.ts +55 -0
  66. package/dist/catalog-policy-pricing.d.ts.map +1 -0
  67. package/dist/catalog-policy-pricing.js +109 -0
  68. package/dist/catalog-policy-promotions.d.ts +52 -0
  69. package/dist/catalog-policy-promotions.d.ts.map +1 -0
  70. package/dist/catalog-policy-promotions.js +270 -0
  71. package/dist/catalog-policy-taxonomy.d.ts +51 -0
  72. package/dist/catalog-policy-taxonomy.d.ts.map +1 -0
  73. package/dist/catalog-policy-taxonomy.js +191 -0
  74. package/dist/catalog-policy.d.ts +33 -0
  75. package/dist/catalog-policy.d.ts.map +1 -0
  76. package/dist/catalog-policy.js +733 -0
  77. package/dist/content-shape.d.ts +15 -0
  78. package/dist/content-shape.d.ts.map +1 -0
  79. package/dist/content-shape.js +28 -0
  80. package/dist/draft-shape.d.ts +43 -0
  81. package/dist/draft-shape.d.ts.map +1 -0
  82. package/dist/draft-shape.js +48 -0
  83. package/dist/events.d.ts +37 -0
  84. package/dist/events.d.ts.map +1 -0
  85. package/dist/events.js +32 -0
  86. package/dist/extras/catalog-policy.d.ts +30 -0
  87. package/dist/extras/catalog-policy.d.ts.map +1 -0
  88. package/dist/extras/catalog-policy.js +319 -0
  89. package/dist/extras/content-shape.d.ts +5 -0
  90. package/dist/extras/content-shape.d.ts.map +1 -0
  91. package/dist/extras/content-shape.js +13 -0
  92. package/dist/extras/draft-shape.d.ts +34 -0
  93. package/dist/extras/draft-shape.d.ts.map +1 -0
  94. package/dist/extras/draft-shape.js +69 -0
  95. package/dist/extras/routes.d.ts +380 -0
  96. package/dist/extras/routes.d.ts.map +1 -0
  97. package/dist/extras/routes.js +59 -0
  98. package/dist/extras/schema-sourced-content.d.ts +254 -0
  99. package/dist/extras/schema-sourced-content.d.ts.map +1 -0
  100. package/dist/extras/schema-sourced-content.js +45 -0
  101. package/dist/extras/schema.d.ts +628 -0
  102. package/dist/extras/schema.d.ts.map +1 -0
  103. package/dist/extras/schema.js +87 -0
  104. package/dist/extras/service-catalog-plane.d.ts +77 -0
  105. package/dist/extras/service-catalog-plane.d.ts.map +1 -0
  106. package/dist/extras/service-catalog-plane.js +219 -0
  107. package/dist/extras/service-content-synthesizer.d.ts +41 -0
  108. package/dist/extras/service-content-synthesizer.d.ts.map +1 -0
  109. package/dist/extras/service-content-synthesizer.js +138 -0
  110. package/dist/extras/service-content.d.ts +48 -0
  111. package/dist/extras/service-content.d.ts.map +1 -0
  112. package/dist/extras/service-content.js +253 -0
  113. package/dist/extras/service.d.ts +185 -0
  114. package/dist/extras/service.d.ts.map +1 -0
  115. package/dist/extras/service.js +96 -0
  116. package/dist/extras/validation.d.ts +437 -0
  117. package/dist/extras/validation.d.ts.map +1 -0
  118. package/dist/extras/validation.js +149 -0
  119. package/dist/extras.d.ts +267 -0
  120. package/dist/extras.d.ts.map +1 -0
  121. package/dist/extras.js +19 -0
  122. package/dist/index.d.ts +22 -0
  123. package/dist/index.d.ts.map +1 -0
  124. package/dist/index.js +32 -0
  125. package/dist/interface.d.ts +5869 -0
  126. package/dist/interface.d.ts.map +1 -0
  127. package/dist/interface.js +54 -0
  128. package/dist/public-routes.d.ts +2 -0
  129. package/dist/public-routes.d.ts.map +1 -0
  130. package/dist/public-routes.js +1 -0
  131. package/dist/public-validation.d.ts +2 -0
  132. package/dist/public-validation.d.ts.map +1 -0
  133. package/dist/public-validation.js +1 -0
  134. package/dist/read-model.d.ts +25 -0
  135. package/dist/read-model.d.ts.map +1 -0
  136. package/dist/read-model.js +99 -0
  137. package/dist/route-env.d.ts +22 -0
  138. package/dist/route-env.d.ts.map +1 -0
  139. package/dist/route-env.js +1 -0
  140. package/dist/routes-associations.d.ts +164 -0
  141. package/dist/routes-associations.d.ts.map +1 -0
  142. package/dist/routes-associations.js +100 -0
  143. package/dist/routes-catalog.d.ts +436 -0
  144. package/dist/routes-catalog.d.ts.map +1 -0
  145. package/dist/routes-catalog.js +104 -0
  146. package/dist/routes-configuration.d.ts +773 -0
  147. package/dist/routes-configuration.d.ts.map +1 -0
  148. package/dist/routes-configuration.js +364 -0
  149. package/dist/routes-content.d.ts +74 -0
  150. package/dist/routes-content.d.ts.map +1 -0
  151. package/dist/routes-content.js +117 -0
  152. package/dist/routes-core.d.ts +331 -0
  153. package/dist/routes-core.d.ts.map +1 -0
  154. package/dist/routes-core.js +95 -0
  155. package/dist/routes-itinerary.d.ts +759 -0
  156. package/dist/routes-itinerary.d.ts.map +1 -0
  157. package/dist/routes-itinerary.js +387 -0
  158. package/dist/routes-maintenance.d.ts +32 -0
  159. package/dist/routes-maintenance.d.ts.map +1 -0
  160. package/dist/routes-maintenance.js +14 -0
  161. package/dist/routes-media.d.ts +634 -0
  162. package/dist/routes-media.d.ts.map +1 -0
  163. package/dist/routes-media.js +245 -0
  164. package/dist/routes-merchandising.d.ts +1120 -0
  165. package/dist/routes-merchandising.d.ts.map +1 -0
  166. package/dist/routes-merchandising.js +377 -0
  167. package/dist/routes-options.d.ts +363 -0
  168. package/dist/routes-options.d.ts.map +1 -0
  169. package/dist/routes-options.js +173 -0
  170. package/dist/routes-public.d.ts +776 -0
  171. package/dist/routes-public.d.ts.map +1 -0
  172. package/dist/routes-public.js +119 -0
  173. package/dist/routes-translations.d.ts +489 -0
  174. package/dist/routes-translations.d.ts.map +1 -0
  175. package/dist/routes-translations.js +258 -0
  176. package/dist/routes.d.ts +5097 -0
  177. package/dist/routes.d.ts.map +1 -0
  178. package/dist/routes.js +64 -0
  179. package/dist/schema-core.d.ts +1238 -0
  180. package/dist/schema-core.d.ts.map +1 -0
  181. package/dist/schema-core.js +157 -0
  182. package/dist/schema-itinerary.d.ts +1169 -0
  183. package/dist/schema-itinerary.d.ts.map +1 -0
  184. package/dist/schema-itinerary.js +130 -0
  185. package/dist/schema-relations.d.ts +117 -0
  186. package/dist/schema-relations.d.ts.map +1 -0
  187. package/dist/schema-relations.js +192 -0
  188. package/dist/schema-settings.d.ts +1800 -0
  189. package/dist/schema-settings.d.ts.map +1 -0
  190. package/dist/schema-settings.js +220 -0
  191. package/dist/schema-shared.d.ts +15 -0
  192. package/dist/schema-shared.d.ts.map +1 -0
  193. package/dist/schema-shared.js +91 -0
  194. package/dist/schema-sourced-content.d.ts +262 -0
  195. package/dist/schema-sourced-content.d.ts.map +1 -0
  196. package/dist/schema-sourced-content.js +69 -0
  197. package/dist/schema-taxonomy.d.ts +1363 -0
  198. package/dist/schema-taxonomy.d.ts.map +1 -0
  199. package/dist/schema-taxonomy.js +203 -0
  200. package/dist/schema.d.ts +10 -0
  201. package/dist/schema.d.ts.map +1 -0
  202. package/dist/schema.js +9 -0
  203. package/dist/service-aggregates.d.ts +29 -0
  204. package/dist/service-aggregates.d.ts.map +1 -0
  205. package/dist/service-aggregates.js +56 -0
  206. package/dist/service-catalog-plane-destinations.d.ts +30 -0
  207. package/dist/service-catalog-plane-destinations.d.ts.map +1 -0
  208. package/dist/service-catalog-plane-destinations.js +143 -0
  209. package/dist/service-catalog-plane-taxonomy.d.ts +73 -0
  210. package/dist/service-catalog-plane-taxonomy.d.ts.map +1 -0
  211. package/dist/service-catalog-plane-taxonomy.js +242 -0
  212. package/dist/service-catalog-plane.d.ts +179 -0
  213. package/dist/service-catalog-plane.d.ts.map +1 -0
  214. package/dist/service-catalog-plane.js +431 -0
  215. package/dist/service-catalog.d.ts +251 -0
  216. package/dist/service-catalog.d.ts.map +1 -0
  217. package/dist/service-catalog.js +517 -0
  218. package/dist/service-configuration.d.ts +261 -0
  219. package/dist/service-configuration.d.ts.map +1 -0
  220. package/dist/service-configuration.js +343 -0
  221. package/dist/service-content-owned.d.ts +68 -0
  222. package/dist/service-content-owned.d.ts.map +1 -0
  223. package/dist/service-content-owned.js +329 -0
  224. package/dist/service-content-synthesizer.d.ts +90 -0
  225. package/dist/service-content-synthesizer.d.ts.map +1 -0
  226. package/dist/service-content-synthesizer.js +178 -0
  227. package/dist/service-content.d.ts +106 -0
  228. package/dist/service-content.d.ts.map +1 -0
  229. package/dist/service-content.js +388 -0
  230. package/dist/service-core.d.ts +194 -0
  231. package/dist/service-core.d.ts.map +1 -0
  232. package/dist/service-core.js +213 -0
  233. package/dist/service-delivery-formats.d.ts +58 -0
  234. package/dist/service-delivery-formats.d.ts.map +1 -0
  235. package/dist/service-delivery-formats.js +107 -0
  236. package/dist/service-destinations.d.ts +223 -0
  237. package/dist/service-destinations.d.ts.map +1 -0
  238. package/dist/service-destinations.js +310 -0
  239. package/dist/service-itinerary-history.d.ts +457 -0
  240. package/dist/service-itinerary-history.d.ts.map +1 -0
  241. package/dist/service-itinerary-history.js +135 -0
  242. package/dist/service-itinerary.d.ts +1149 -0
  243. package/dist/service-itinerary.d.ts.map +1 -0
  244. package/dist/service-itinerary.js +419 -0
  245. package/dist/service-media.d.ts +272 -0
  246. package/dist/service-media.d.ts.map +1 -0
  247. package/dist/service-media.js +320 -0
  248. package/dist/service-merchandising.d.ts +184 -0
  249. package/dist/service-merchandising.d.ts.map +1 -0
  250. package/dist/service-merchandising.js +181 -0
  251. package/dist/service-option-translations.d.ts +268 -0
  252. package/dist/service-option-translations.d.ts.map +1 -0
  253. package/dist/service-option-translations.js +300 -0
  254. package/dist/service-options.d.ts +181 -0
  255. package/dist/service-options.d.ts.map +1 -0
  256. package/dist/service-options.js +179 -0
  257. package/dist/service-product-destinations.d.ts +37 -0
  258. package/dist/service-product-destinations.d.ts.map +1 -0
  259. package/dist/service-product-destinations.js +94 -0
  260. package/dist/service-public.d.ts +664 -0
  261. package/dist/service-public.d.ts.map +1 -0
  262. package/dist/service-public.js +374 -0
  263. package/dist/service-taxonomy.d.ts +197 -0
  264. package/dist/service-taxonomy.d.ts.map +1 -0
  265. package/dist/service-taxonomy.js +221 -0
  266. package/dist/service.d.ts +3929 -0
  267. package/dist/service.d.ts.map +1 -0
  268. package/dist/service.js +28 -0
  269. package/dist/tasks/brochure-printers.d.ts +31 -0
  270. package/dist/tasks/brochure-printers.d.ts.map +1 -0
  271. package/dist/tasks/brochure-printers.js +149 -0
  272. package/dist/tasks/brochure-templates.d.ts +36 -0
  273. package/dist/tasks/brochure-templates.d.ts.map +1 -0
  274. package/dist/tasks/brochure-templates.js +110 -0
  275. package/dist/tasks/brochures.d.ts +43 -0
  276. package/dist/tasks/brochures.d.ts.map +1 -0
  277. package/dist/tasks/brochures.js +72 -0
  278. package/dist/tasks/generate-pdf.d.ts +8 -0
  279. package/dist/tasks/generate-pdf.d.ts.map +1 -0
  280. package/dist/tasks/generate-pdf.js +106 -0
  281. package/dist/tasks/index.d.ts +5 -0
  282. package/dist/tasks/index.d.ts.map +1 -0
  283. package/dist/tasks/index.js +4 -0
  284. package/dist/tasks/pdf-text.d.ts +2 -0
  285. package/dist/tasks/pdf-text.d.ts.map +1 -0
  286. package/dist/tasks/pdf-text.js +40 -0
  287. package/dist/tasks.d.ts +2 -0
  288. package/dist/tasks.d.ts.map +1 -0
  289. package/dist/tasks.js +1 -0
  290. package/dist/validation-catalog.d.ts +2 -0
  291. package/dist/validation-catalog.d.ts.map +1 -0
  292. package/dist/validation-catalog.js +3 -0
  293. package/dist/validation-config.d.ts +2 -0
  294. package/dist/validation-config.d.ts.map +1 -0
  295. package/dist/validation-config.js +3 -0
  296. package/dist/validation-content.d.ts +2 -0
  297. package/dist/validation-content.d.ts.map +1 -0
  298. package/dist/validation-content.js +3 -0
  299. package/dist/validation-core.d.ts +2 -0
  300. package/dist/validation-core.d.ts.map +1 -0
  301. package/dist/validation-core.js +3 -0
  302. package/dist/validation-public.d.ts +2 -0
  303. package/dist/validation-public.d.ts.map +1 -0
  304. package/dist/validation-public.js +3 -0
  305. package/dist/validation-shared.d.ts +2 -0
  306. package/dist/validation-shared.d.ts.map +1 -0
  307. package/dist/validation-shared.js +3 -0
  308. package/dist/validation.d.ts +2 -0
  309. package/dist/validation.d.ts.map +1 -0
  310. package/dist/validation.js +3 -0
  311. package/package.json +204 -0
@@ -0,0 +1,374 @@
1
+ // agent-quality: file-size exception -- owner: inventory; existing service module stays co-located until a dedicated split preserves behavior and tests.
2
+ import { and, asc, desc, eq, ilike, inArray, notInArray, or, sql } from "drizzle-orm";
3
+ import { destinations, destinationTranslations, productCategories, productCategoryProducts, productDestinations, productLocations, products, productTagProducts, productTags, productTranslations, productVisibilitySettings, } from "./schema.js";
4
+ import { catalogProductsService } from "./service-catalog.js";
5
+ function impossibleCondition() {
6
+ return sql `1 = 0`;
7
+ }
8
+ function normalizeLanguageTag(value) {
9
+ const normalized = value?.trim().toLowerCase();
10
+ return normalized || null;
11
+ }
12
+ async function listProductIdsForCategory(db, categoryId) {
13
+ const rows = await db
14
+ .select({ productId: productCategoryProducts.productId })
15
+ .from(productCategoryProducts)
16
+ .innerJoin(productCategories, eq(productCategories.id, productCategoryProducts.categoryId))
17
+ .where(and(eq(productCategoryProducts.categoryId, categoryId), eq(productCategories.active, true)));
18
+ return rows.map((row) => row.productId);
19
+ }
20
+ async function listProductIdsForTag(db, tagId) {
21
+ const rows = await db
22
+ .select({ productId: productTagProducts.productId })
23
+ .from(productTagProducts)
24
+ .where(eq(productTagProducts.tagId, tagId));
25
+ return rows.map((row) => row.productId);
26
+ }
27
+ async function listProductIdsForDestinationId(db, destinationId) {
28
+ const rows = await db
29
+ .select({ productId: productDestinations.productId })
30
+ .from(productDestinations)
31
+ .where(eq(productDestinations.destinationId, destinationId));
32
+ return rows.map((row) => row.productId);
33
+ }
34
+ async function listProductIdsForDestinationSlug(db, slug) {
35
+ const rows = await db
36
+ .select({ productId: productDestinations.productId })
37
+ .from(productDestinations)
38
+ .innerJoin(destinations, eq(destinations.id, productDestinations.destinationId))
39
+ .where(eq(destinations.slug, slug));
40
+ return rows.map((row) => row.productId);
41
+ }
42
+ async function listFeaturedProductIds(db) {
43
+ const rows = await db
44
+ .select({ productId: productVisibilitySettings.productId })
45
+ .from(productVisibilitySettings)
46
+ .where(eq(productVisibilitySettings.isFeatured, true));
47
+ return rows.map((row) => row.productId);
48
+ }
49
+ async function listProductIdsForLocationTitle(db, title) {
50
+ const rows = await db
51
+ .select({ productId: productLocations.productId })
52
+ .from(productLocations)
53
+ .where(ilike(productLocations.title, title));
54
+ return rows.map((row) => row.productId);
55
+ }
56
+ async function listProductIdsForLocationCity(db, city) {
57
+ const rows = await db
58
+ .select({ productId: productLocations.productId })
59
+ .from(productLocations)
60
+ .where(ilike(productLocations.city, city));
61
+ return rows.map((row) => row.productId);
62
+ }
63
+ async function listProductIdsForLocationCountryCode(db, countryCode) {
64
+ const rows = await db
65
+ .select({ productId: productLocations.productId })
66
+ .from(productLocations)
67
+ .where(eq(productLocations.countryCode, countryCode));
68
+ return rows.map((row) => row.productId);
69
+ }
70
+ async function listProductIdsForLocationType(db, locationType) {
71
+ const rows = await db
72
+ .select({ productId: productLocations.productId })
73
+ .from(productLocations)
74
+ .where(eq(productLocations.locationType, locationType));
75
+ return rows.map((row) => row.productId);
76
+ }
77
+ async function hydrateCatalogProducts(db, productRows, options) {
78
+ return catalogProductsService.hydrateProducts(db, productRows, {
79
+ includeContent: options?.includeContent,
80
+ languageTag: options?.languageTag,
81
+ fallbackLanguageTags: options?.languageTag ? [options.languageTag] : [],
82
+ });
83
+ }
84
+ function orderProducts(query) {
85
+ const direction = query.direction === "desc" ? desc : asc;
86
+ switch (query.sort) {
87
+ case "createdAt":
88
+ return direction(products.createdAt);
89
+ case "startDate":
90
+ return direction(products.startDate);
91
+ case "price":
92
+ return direction(products.sellAmountCents);
93
+ default:
94
+ return direction(products.name);
95
+ }
96
+ }
97
+ export const publicProductsService = {
98
+ async listCatalogProducts(db, query) {
99
+ const conditions = [
100
+ eq(products.status, "active"),
101
+ eq(products.activated, true),
102
+ eq(products.visibility, "public"),
103
+ ];
104
+ if (query.search) {
105
+ const term = `%${query.search}%`;
106
+ const searchCondition = or(ilike(products.name, term), ilike(products.description, term));
107
+ if (searchCondition) {
108
+ conditions.push(searchCondition);
109
+ }
110
+ }
111
+ if (query.bookingMode) {
112
+ conditions.push(eq(products.bookingMode, query.bookingMode));
113
+ }
114
+ if (query.capacityMode) {
115
+ conditions.push(eq(products.capacityMode, query.capacityMode));
116
+ }
117
+ if (query.productTypeId) {
118
+ conditions.push(eq(products.productTypeId, query.productTypeId));
119
+ }
120
+ if (query.categoryId) {
121
+ const productIds = await listProductIdsForCategory(db, query.categoryId);
122
+ conditions.push(productIds.length > 0 ? inArray(products.id, productIds) : impossibleCondition());
123
+ }
124
+ if (query.tagId) {
125
+ const productIds = await listProductIdsForTag(db, query.tagId);
126
+ conditions.push(productIds.length > 0 ? inArray(products.id, productIds) : impossibleCondition());
127
+ }
128
+ if (query.destinationId) {
129
+ const productIds = await listProductIdsForDestinationId(db, query.destinationId);
130
+ conditions.push(productIds.length > 0 ? inArray(products.id, productIds) : impossibleCondition());
131
+ }
132
+ if (query.destinationSlug) {
133
+ const productIds = await listProductIdsForDestinationSlug(db, query.destinationSlug);
134
+ conditions.push(productIds.length > 0 ? inArray(products.id, productIds) : impossibleCondition());
135
+ }
136
+ if (query.featured !== undefined) {
137
+ const productIds = await listFeaturedProductIds(db);
138
+ conditions.push(query.featured
139
+ ? productIds.length > 0
140
+ ? inArray(products.id, productIds)
141
+ : impossibleCondition()
142
+ : productIds.length > 0
143
+ ? notInArray(products.id, productIds)
144
+ : sql `1 = 1`);
145
+ }
146
+ if (query.locationTitle) {
147
+ const productIds = await listProductIdsForLocationTitle(db, query.locationTitle);
148
+ conditions.push(productIds.length > 0 ? inArray(products.id, productIds) : impossibleCondition());
149
+ }
150
+ if (query.locationCity) {
151
+ const productIds = await listProductIdsForLocationCity(db, query.locationCity);
152
+ conditions.push(productIds.length > 0 ? inArray(products.id, productIds) : impossibleCondition());
153
+ }
154
+ if (query.locationCountryCode) {
155
+ const productIds = await listProductIdsForLocationCountryCode(db, query.locationCountryCode.toUpperCase());
156
+ conditions.push(productIds.length > 0 ? inArray(products.id, productIds) : impossibleCondition());
157
+ }
158
+ if (query.locationType) {
159
+ const productIds = await listProductIdsForLocationType(db, query.locationType);
160
+ conditions.push(productIds.length > 0 ? inArray(products.id, productIds) : impossibleCondition());
161
+ }
162
+ const where = and(...conditions);
163
+ const [rows, countResult] = await Promise.all([
164
+ db
165
+ .select()
166
+ .from(products)
167
+ .where(where)
168
+ .orderBy(orderProducts(query), asc(products.id))
169
+ .limit(query.limit)
170
+ .offset(query.offset),
171
+ db.select({ count: sql `count(*)::int` }).from(products).where(where),
172
+ ]);
173
+ return {
174
+ data: await hydrateCatalogProducts(db, rows, {
175
+ includeContent: query.includeContent === true,
176
+ languageTag: normalizeLanguageTag(query.languageTag),
177
+ }),
178
+ total: countResult[0]?.count ?? 0,
179
+ limit: query.limit,
180
+ offset: query.offset,
181
+ };
182
+ },
183
+ async getCatalogProductById(db, id, query = {}) {
184
+ const [row] = await db
185
+ .select()
186
+ .from(products)
187
+ .where(and(eq(products.id, id), eq(products.status, "active"), eq(products.activated, true), eq(products.visibility, "public")))
188
+ .limit(1);
189
+ if (!row) {
190
+ return null;
191
+ }
192
+ const [product] = await hydrateCatalogProducts(db, [row], {
193
+ includeContent: true,
194
+ languageTag: normalizeLanguageTag(query.languageTag),
195
+ });
196
+ return product ?? null;
197
+ },
198
+ async getCatalogProductBySlug(db, slug, query = {}) {
199
+ const normalizedSlug = slug.trim().toLowerCase();
200
+ const normalizedLanguageTag = normalizeLanguageTag(query.languageTag);
201
+ const conditions = [
202
+ // agent-quality: raw-sql reviewed -- owner: inventory; dynamic SQL interpolation uses Drizzle parameter binding or vetted SQL identifiers.
203
+ sql `lower(${productTranslations.slug}) = ${normalizedSlug}`,
204
+ eq(products.status, "active"),
205
+ eq(products.activated, true),
206
+ eq(products.visibility, "public"),
207
+ ];
208
+ if (normalizedLanguageTag) {
209
+ conditions.push(eq(productTranslations.languageTag, normalizedLanguageTag));
210
+ }
211
+ const [row] = await db
212
+ .select({
213
+ productId: products.id,
214
+ languageTag: productTranslations.languageTag,
215
+ })
216
+ .from(productTranslations)
217
+ .innerJoin(products, eq(products.id, productTranslations.productId))
218
+ .where(and(...conditions))
219
+ .orderBy(desc(productTranslations.updatedAt))
220
+ .limit(1);
221
+ if (!row) {
222
+ return null;
223
+ }
224
+ return this.getCatalogProductById(db, row.productId, {
225
+ languageTag: normalizedLanguageTag ?? row.languageTag,
226
+ });
227
+ },
228
+ async getCatalogProductBrochure(db, productId, query = {}) {
229
+ const product = await this.getCatalogProductById(db, productId, query);
230
+ return product && "brochure" in product ? product.brochure : null;
231
+ },
232
+ async listCatalogCategories(db, query) {
233
+ const conditions = [eq(productCategories.active, true)];
234
+ if (query.parentId) {
235
+ conditions.push(eq(productCategories.parentId, query.parentId));
236
+ }
237
+ if (query.search) {
238
+ const term = `%${query.search}%`;
239
+ const searchCondition = or(ilike(productCategories.name, term), ilike(productCategories.slug, term));
240
+ if (searchCondition) {
241
+ conditions.push(searchCondition);
242
+ }
243
+ }
244
+ const where = and(...conditions);
245
+ const [rows, countResult] = await Promise.all([
246
+ db
247
+ .select({
248
+ id: productCategories.id,
249
+ parentId: productCategories.parentId,
250
+ name: productCategories.name,
251
+ slug: productCategories.slug,
252
+ description: productCategories.description,
253
+ sortOrder: productCategories.sortOrder,
254
+ })
255
+ .from(productCategories)
256
+ .where(where)
257
+ .orderBy(asc(productCategories.sortOrder), asc(productCategories.name))
258
+ .limit(query.limit)
259
+ .offset(query.offset),
260
+ db.select({ count: sql `count(*)::int` }).from(productCategories).where(where),
261
+ ]);
262
+ return {
263
+ data: rows.map((row) => ({
264
+ ...row,
265
+ parentId: row.parentId ?? null,
266
+ description: row.description ?? null,
267
+ })),
268
+ total: countResult[0]?.count ?? 0,
269
+ limit: query.limit,
270
+ offset: query.offset,
271
+ };
272
+ },
273
+ async listCatalogTags(db, query) {
274
+ const conditions = [];
275
+ if (query.search) {
276
+ conditions.push(ilike(productTags.name, `%${query.search}%`));
277
+ }
278
+ const where = conditions.length > 0 ? and(...conditions) : undefined;
279
+ const [rows, countResult] = await Promise.all([
280
+ db
281
+ .select({
282
+ id: productTags.id,
283
+ name: productTags.name,
284
+ })
285
+ .from(productTags)
286
+ .where(where)
287
+ .orderBy(asc(productTags.name))
288
+ .limit(query.limit)
289
+ .offset(query.offset),
290
+ db.select({ count: sql `count(*)::int` }).from(productTags).where(where),
291
+ ]);
292
+ return {
293
+ data: rows,
294
+ total: countResult[0]?.count ?? 0,
295
+ limit: query.limit,
296
+ offset: query.offset,
297
+ };
298
+ },
299
+ async listCatalogDestinations(db, query) {
300
+ const conditions = [];
301
+ if (query.parentId) {
302
+ conditions.push(eq(destinations.parentId, query.parentId));
303
+ }
304
+ if (query.active !== undefined) {
305
+ conditions.push(eq(destinations.active, query.active));
306
+ }
307
+ if (query.destinationType) {
308
+ conditions.push(eq(destinations.destinationType, query.destinationType));
309
+ }
310
+ if (query.canonicalPlaceId) {
311
+ conditions.push(eq(destinations.canonicalPlaceId, query.canonicalPlaceId));
312
+ }
313
+ if (query.search) {
314
+ const translationRows = await db
315
+ .select({ destinationId: destinationTranslations.destinationId })
316
+ .from(destinationTranslations)
317
+ .where(and(...(query.languageTag
318
+ ? [eq(destinationTranslations.languageTag, query.languageTag)]
319
+ : []), or(ilike(destinationTranslations.name, `%${query.search}%`), ilike(destinationTranslations.description, `%${query.search}%`))));
320
+ const destinationIds = translationRows.map((row) => row.destinationId);
321
+ conditions.push(destinationIds.length > 0
322
+ ? inArray(destinations.id, destinationIds)
323
+ : impossibleCondition());
324
+ }
325
+ const where = conditions.length > 0 ? and(...conditions) : undefined;
326
+ const [rows, countResult] = await Promise.all([
327
+ db
328
+ .select()
329
+ .from(destinations)
330
+ .where(where)
331
+ .limit(query.limit)
332
+ .offset(query.offset)
333
+ .orderBy(asc(destinations.sortOrder), asc(destinations.slug)),
334
+ db.select({ count: sql `count(*)::int` }).from(destinations).where(where),
335
+ ]);
336
+ const destinationIds = rows.map((row) => row.id);
337
+ const translations = destinationIds.length > 0
338
+ ? await db
339
+ .select()
340
+ .from(destinationTranslations)
341
+ .where(and(inArray(destinationTranslations.destinationId, destinationIds), ...(query.languageTag
342
+ ? [eq(destinationTranslations.languageTag, query.languageTag)]
343
+ : [])))
344
+ : [];
345
+ const translationByDestination = new Map();
346
+ for (const row of translations) {
347
+ if (!translationByDestination.has(row.destinationId)) {
348
+ translationByDestination.set(row.destinationId, row);
349
+ }
350
+ }
351
+ return {
352
+ data: rows.map((row) => {
353
+ const translation = translationByDestination.get(row.id);
354
+ return {
355
+ id: row.id,
356
+ parentId: row.parentId ?? null,
357
+ slug: row.slug,
358
+ canonicalPlaceId: row.canonicalPlaceId ?? null,
359
+ name: translation?.name ?? row.slug,
360
+ description: translation?.description ?? null,
361
+ seoTitle: translation?.seoTitle ?? null,
362
+ seoDescription: translation?.seoDescription ?? null,
363
+ destinationType: row.destinationType,
364
+ latitude: row.latitude ?? null,
365
+ longitude: row.longitude ?? null,
366
+ sortOrder: row.sortOrder,
367
+ };
368
+ }),
369
+ total: countResult[0]?.count ?? 0,
370
+ limit: query.limit,
371
+ offset: query.offset,
372
+ };
373
+ },
374
+ };
@@ -0,0 +1,197 @@
1
+ import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
2
+ import type { z } from "zod";
3
+ import type { insertProductCategorySchema, insertProductTagSchema, insertProductTypeSchema, productCategoryListQuerySchema, productTagListQuerySchema, productTypeListQuerySchema, updateProductCategorySchema, updateProductTagSchema, updateProductTypeSchema } from "./validation.js";
4
+ type ProductTypeListQuery = z.infer<typeof productTypeListQuerySchema>;
5
+ type CreateProductTypeInput = z.infer<typeof insertProductTypeSchema>;
6
+ type UpdateProductTypeInput = z.infer<typeof updateProductTypeSchema>;
7
+ type ProductCategoryListQuery = z.infer<typeof productCategoryListQuerySchema>;
8
+ type CreateProductCategoryInput = z.infer<typeof insertProductCategorySchema>;
9
+ type UpdateProductCategoryInput = z.infer<typeof updateProductCategorySchema>;
10
+ type ProductTagListQuery = z.infer<typeof productTagListQuerySchema>;
11
+ type CreateProductTagInput = z.infer<typeof insertProductTagSchema>;
12
+ type UpdateProductTagInput = z.infer<typeof updateProductTagSchema>;
13
+ export declare const taxonomyProductsService: {
14
+ listProductTypes(db: PostgresJsDatabase, query: ProductTypeListQuery): Promise<{
15
+ data: {
16
+ id: string;
17
+ name: string;
18
+ code: string;
19
+ description: string | null;
20
+ sortOrder: number;
21
+ active: boolean;
22
+ metadata: Record<string, unknown> | null;
23
+ createdAt: Date;
24
+ updatedAt: Date;
25
+ }[];
26
+ total: number;
27
+ limit: number;
28
+ offset: number;
29
+ }>;
30
+ getProductTypeById(db: PostgresJsDatabase, id: string): Promise<{
31
+ id: string;
32
+ name: string;
33
+ code: string;
34
+ description: string | null;
35
+ sortOrder: number;
36
+ active: boolean;
37
+ metadata: Record<string, unknown> | null;
38
+ createdAt: Date;
39
+ updatedAt: Date;
40
+ } | null>;
41
+ createProductType(db: PostgresJsDatabase, data: CreateProductTypeInput): Promise<{
42
+ id: string;
43
+ name: string;
44
+ active: boolean;
45
+ description: string | null;
46
+ createdAt: Date;
47
+ updatedAt: Date;
48
+ metadata: Record<string, unknown> | null;
49
+ code: string;
50
+ sortOrder: number;
51
+ } | undefined>;
52
+ updateProductType(db: PostgresJsDatabase, id: string, data: UpdateProductTypeInput): Promise<{
53
+ id: string;
54
+ name: string;
55
+ code: string;
56
+ description: string | null;
57
+ sortOrder: number;
58
+ active: boolean;
59
+ metadata: Record<string, unknown> | null;
60
+ createdAt: Date;
61
+ updatedAt: Date;
62
+ } | null>;
63
+ deleteProductType(db: PostgresJsDatabase, id: string): Promise<{
64
+ id: string;
65
+ } | null>;
66
+ listProductCategories(db: PostgresJsDatabase, query: ProductCategoryListQuery): Promise<{
67
+ data: {
68
+ id: string;
69
+ parentId: string | null;
70
+ name: string;
71
+ slug: string;
72
+ description: string | null;
73
+ sortOrder: number;
74
+ active: boolean;
75
+ customerPaymentPolicy: unknown;
76
+ metadata: Record<string, unknown> | null;
77
+ createdAt: Date;
78
+ updatedAt: Date;
79
+ }[];
80
+ total: number;
81
+ limit: number;
82
+ offset: number;
83
+ }>;
84
+ getProductCategoryById(db: PostgresJsDatabase, id: string): Promise<{
85
+ id: string;
86
+ parentId: string | null;
87
+ name: string;
88
+ slug: string;
89
+ description: string | null;
90
+ sortOrder: number;
91
+ active: boolean;
92
+ customerPaymentPolicy: unknown;
93
+ metadata: Record<string, unknown> | null;
94
+ createdAt: Date;
95
+ updatedAt: Date;
96
+ } | null>;
97
+ createProductCategory(db: PostgresJsDatabase, data: CreateProductCategoryInput): Promise<{
98
+ id: string;
99
+ name: string;
100
+ active: boolean;
101
+ description: string | null;
102
+ customerPaymentPolicy: unknown;
103
+ createdAt: Date;
104
+ updatedAt: Date;
105
+ metadata: Record<string, unknown> | null;
106
+ sortOrder: number;
107
+ parentId: string | null;
108
+ slug: string;
109
+ } | undefined>;
110
+ updateProductCategory(db: PostgresJsDatabase, id: string, data: UpdateProductCategoryInput): Promise<{
111
+ id: string;
112
+ parentId: string | null;
113
+ name: string;
114
+ slug: string;
115
+ description: string | null;
116
+ sortOrder: number;
117
+ active: boolean;
118
+ customerPaymentPolicy: unknown;
119
+ metadata: Record<string, unknown> | null;
120
+ createdAt: Date;
121
+ updatedAt: Date;
122
+ } | null>;
123
+ deleteProductCategory(db: PostgresJsDatabase, id: string): Promise<{
124
+ id: string;
125
+ } | null>;
126
+ listProductTags(db: PostgresJsDatabase, query: ProductTagListQuery): Promise<{
127
+ data: {
128
+ id: string;
129
+ name: string;
130
+ createdAt: Date;
131
+ updatedAt: Date;
132
+ }[];
133
+ total: number;
134
+ limit: number;
135
+ offset: number;
136
+ }>;
137
+ getProductTagById(db: PostgresJsDatabase, id: string): Promise<{
138
+ id: string;
139
+ name: string;
140
+ createdAt: Date;
141
+ updatedAt: Date;
142
+ } | null>;
143
+ createProductTag(db: PostgresJsDatabase, data: CreateProductTagInput): Promise<{
144
+ id: string;
145
+ name: string;
146
+ createdAt: Date;
147
+ updatedAt: Date;
148
+ } | undefined>;
149
+ updateProductTag(db: PostgresJsDatabase, id: string, data: UpdateProductTagInput): Promise<{
150
+ id: string;
151
+ name: string;
152
+ createdAt: Date;
153
+ updatedAt: Date;
154
+ } | null>;
155
+ deleteProductTag(db: PostgresJsDatabase, id: string): Promise<{
156
+ id: string;
157
+ } | null>;
158
+ addProductToCategory(db: PostgresJsDatabase, productId: string, categoryId: string, sortOrder?: number): Promise<{
159
+ createdAt: Date;
160
+ updatedAt: Date;
161
+ categoryId: string;
162
+ productId: string;
163
+ sortOrder: number;
164
+ } | null>;
165
+ removeProductFromCategory(db: PostgresJsDatabase, productId: string, categoryId: string): Promise<{
166
+ productId: string;
167
+ } | null>;
168
+ listProductCategories_(db: PostgresJsDatabase, productId: string): Promise<{
169
+ id: string;
170
+ parentId: string | null;
171
+ name: string;
172
+ slug: string;
173
+ description: string | null;
174
+ sortOrder: number;
175
+ active: boolean;
176
+ customerPaymentPolicy: unknown;
177
+ metadata: Record<string, unknown> | null;
178
+ createdAt: Date;
179
+ updatedAt: Date;
180
+ }[]>;
181
+ addProductTag(db: PostgresJsDatabase, productId: string, tagId: string): Promise<{
182
+ createdAt: Date;
183
+ productId: string;
184
+ tagId: string;
185
+ } | null>;
186
+ removeProductTag(db: PostgresJsDatabase, productId: string, tagId: string): Promise<{
187
+ productId: string;
188
+ } | null>;
189
+ listProductTags_(db: PostgresJsDatabase, productId: string): Promise<{
190
+ id: string;
191
+ name: string;
192
+ createdAt: Date;
193
+ updatedAt: Date;
194
+ }[]>;
195
+ };
196
+ export {};
197
+ //# sourceMappingURL=service-taxonomy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-taxonomy.d.ts","sourceRoot":"","sources":["../src/service-taxonomy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AACjE,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAQ5B,OAAO,KAAK,EACV,2BAA2B,EAC3B,sBAAsB,EACtB,uBAAuB,EACvB,8BAA8B,EAC9B,yBAAyB,EACzB,0BAA0B,EAC1B,2BAA2B,EAC3B,sBAAsB,EACtB,uBAAuB,EACxB,MAAM,iBAAiB,CAAA;AAExB,KAAK,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAA;AACtE,KAAK,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAA;AACrE,KAAK,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAA;AACrE,KAAK,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAA;AAC9E,KAAK,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAA;AAC7E,KAAK,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAA;AAC7E,KAAK,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAA;AACpE,KAAK,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AACnE,KAAK,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAEnE,eAAO,MAAM,uBAAuB;yBAKP,kBAAkB,SAAS,oBAAoB;;;;;;;;;;;;;;;;2BAiC7C,kBAAkB,MAAM,MAAM;;;;;;;;;;;0BAK/B,kBAAkB,QAAQ,sBAAsB;;;;;;;;;;;0BAKhD,kBAAkB,MAAM,MAAM,QAAQ,sBAAsB;;;;;;;;;;;0BAU5D,kBAAkB,MAAM,MAAM;;;8BAa1B,kBAAkB,SAAS,wBAAwB;;;;;;;;;;;;;;;;;;+BAqClD,kBAAkB,MAAM,MAAM;;;;;;;;;;;;;8BAS/B,kBAAkB,QAAQ,0BAA0B;;;;;;;;;;;;;8BAM9E,kBAAkB,MAClB,MAAM,QACJ,0BAA0B;;;;;;;;;;;;;8BAWF,kBAAkB,MAAM,MAAM;;;wBAapC,kBAAkB,SAAS,mBAAmB;;;;;;;;;;;0BA6B5C,kBAAkB,MAAM,MAAM;;;;;;yBAK/B,kBAAkB,QAAQ,qBAAqB;;;;;;yBAK/C,kBAAkB,MAAM,MAAM,QAAQ,qBAAqB;;;;;;yBAU3D,kBAAkB,MAAM,MAAM;;;6BAcnD,kBAAkB,aACX,MAAM,cACL,MAAM;;;;;;;kCAYgB,kBAAkB,aAAa,MAAM,cAAc,MAAM;;;+BAc5D,kBAAkB,aAAa,MAAM;;;;;;;;;;;;;sBAe9C,kBAAkB,aAAa,MAAM,SAAS,MAAM;;;;;yBAUjD,kBAAkB,aAAa,MAAM,SAAS,MAAM;;;yBASpD,kBAAkB,aAAa,MAAM;;;;;;CAUjE,CAAA"}