@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 @@
1
+ {"version":3,"file":"schema-taxonomy.d.ts","sourceRoot":"","sources":["../src/schema-taxonomy.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmBxB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,OAAO,YAAY,CAAC,YAAY,CAAA;AAC1D,MAAM,MAAM,cAAc,GAAG,OAAO,YAAY,CAAC,YAAY,CAAA;AAE7D,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4C7B,CAAA;AAED,MAAM,MAAM,eAAe,GAAG,OAAO,iBAAiB,CAAC,YAAY,CAAA;AACnE,MAAM,MAAM,kBAAkB,GAAG,OAAO,iBAAiB,CAAC,YAAY,CAAA;AAEtE;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+BvC,CAAA;AAED,MAAM,MAAM,0BAA0B,GAAG,OAAO,2BAA2B,CAAC,YAAY,CAAA;AACxF,MAAM,MAAM,6BAA6B,GAAG,OAAO,2BAA2B,CAAC,YAAY,CAAA;AAE3F,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAavB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,OAAO,WAAW,CAAC,YAAY,CAAA;AACxD,MAAM,MAAM,aAAa,GAAG,OAAO,WAAW,CAAC,YAAY,CAAA;AAE3D;;;GAGG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsBlC,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG,OAAO,sBAAsB,CAAC,YAAY,CAAA;AAC9E,MAAM,MAAM,wBAAwB,GAAG,OAAO,sBAAsB,CAAC,YAAY,CAAA;AAEjF,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BxB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,OAAO,YAAY,CAAC,YAAY,CAAA;AAC1D,MAAM,MAAM,cAAc,GAAG,OAAO,YAAY,CAAC,YAAY,CAAA;AAE7D,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiCnC,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG,OAAO,uBAAuB,CAAC,YAAY,CAAA;AAChF,MAAM,MAAM,yBAAyB,GAAG,OAAO,uBAAuB,CAAC,YAAY,CAAA;AAEnF,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkBnC,CAAA;AAED,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAe9B,CAAA;AAED,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkB/B,CAAA"}
@@ -0,0 +1,203 @@
1
+ import { typeId, typeIdRef } from "@voyant-travel/db/lib/typeid-column";
2
+ import { boolean, doublePrecision, index, integer, jsonb, pgTable, primaryKey, text, timestamp, uniqueIndex, } from "drizzle-orm/pg-core";
3
+ import { products } from "./schema-core.js";
4
+ export const productTypes = pgTable("product_types", {
5
+ id: typeId("product_types"),
6
+ name: text("name").notNull(),
7
+ code: text("code").notNull(),
8
+ description: text("description"),
9
+ sortOrder: integer("sort_order").notNull().default(0),
10
+ active: boolean("active").notNull().default(true),
11
+ metadata: jsonb("metadata").$type(),
12
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
13
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
14
+ }, (table) => [
15
+ uniqueIndex("uidx_product_types_code").on(table.code),
16
+ index("idx_product_types_active").on(table.active),
17
+ index("idx_product_types_sort_name").on(table.sortOrder, table.name),
18
+ index("idx_product_types_active_sort_name").on(table.active, table.sortOrder, table.name),
19
+ ]);
20
+ export const productCategories = pgTable("product_categories", {
21
+ id: typeId("product_categories"),
22
+ parentId: text("parent_id"),
23
+ name: text("name").notNull(),
24
+ slug: text("slug").notNull(),
25
+ description: text("description"),
26
+ sortOrder: integer("sort_order").notNull().default(0),
27
+ active: boolean("active").notNull().default(true),
28
+ /**
29
+ * Customer-facing payment policy override. When set, bookings
30
+ * for products in this category inherit these terms (unless
31
+ * the listing or booking sets its own override). Shape mirrors
32
+ * `PaymentPolicy` from `@voyant-travel/finance`.
33
+ *
34
+ * `null` means "inherit from supplier / operator default".
35
+ *
36
+ * Multi-category products: the cascade picks the FIRST category
37
+ * (ordered by `productCategoryProducts.sortOrder` ascending) that
38
+ * has a non-null policy.
39
+ */
40
+ customerPaymentPolicy: jsonb("customer_payment_policy"),
41
+ metadata: jsonb("metadata").$type(),
42
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
43
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
44
+ }, (table) => [
45
+ uniqueIndex("uidx_product_categories_slug").on(table.slug),
46
+ index("idx_product_categories_parent").on(table.parentId),
47
+ index("idx_product_categories_active").on(table.active),
48
+ index("idx_product_categories_sort_name").on(table.sortOrder, table.name),
49
+ index("idx_product_categories_active_sort_name").on(table.active, table.sortOrder, table.name),
50
+ index("idx_product_categories_parent_sort_name").on(table.parentId, table.sortOrder, table.name),
51
+ // Trigram GIN indexes back the `ILIKE '%term%'` category search
52
+ // (name OR slug — both branches need an index for a BitmapOr plan).
53
+ // Requires the pg_trgm extension.
54
+ index("idx_product_categories_name_trgm").using("gin", table.name.op("gin_trgm_ops")),
55
+ index("idx_product_categories_slug_trgm").using("gin", table.slug.op("gin_trgm_ops")),
56
+ ]);
57
+ /**
58
+ * Locale-aware category labels. Mirrors `destinationTranslations`.
59
+ *
60
+ * The catalog plane's taxonomy projection (`catalog-policy-taxonomy.ts` +
61
+ * `service-catalog-plane-taxonomy.ts`) reads this table per-slice locale
62
+ * and falls back to `productCategories.name` when no row exists for a
63
+ * given `(categoryId, languageTag)`. Slug stays single-locale on
64
+ * `productCategories.slug` per #502 non-goals — operators want stable
65
+ * URLs that don't shift when translations are edited.
66
+ */
67
+ export const productCategoryTranslations = pgTable("product_category_translations", {
68
+ id: typeId("product_category_translations"),
69
+ categoryId: typeIdRef("category_id")
70
+ .notNull()
71
+ .references(() => productCategories.id, { onDelete: "cascade" }),
72
+ languageTag: text("language_tag").notNull(),
73
+ name: text("name").notNull(),
74
+ description: text("description"),
75
+ seoTitle: text("seo_title"),
76
+ seoDescription: text("seo_description"),
77
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
78
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
79
+ }, (table) => [
80
+ uniqueIndex("uidx_product_category_translations_locale").on(table.categoryId, table.languageTag),
81
+ index("idx_product_category_translations_language").on(table.languageTag),
82
+ index("idx_product_category_translations_category_language_created").on(table.categoryId, table.languageTag, table.createdAt),
83
+ index("idx_product_category_translations_language_created").on(table.languageTag, table.createdAt),
84
+ ]);
85
+ export const productTags = pgTable("product_tags", {
86
+ id: typeId("product_tags"),
87
+ name: text("name").notNull(),
88
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
89
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
90
+ }, (table) => [
91
+ uniqueIndex("uidx_product_tags_name").on(table.name),
92
+ // Backs the `ILIKE '%term%'` tag search. Requires the pg_trgm extension.
93
+ index("idx_product_tags_name_trgm").using("gin", table.name.op("gin_trgm_ops")),
94
+ ]);
95
+ /**
96
+ * Locale-aware tag labels. Slimmer than category translations — tags are
97
+ * short labels with no description / SEO blurbs (per #502 non-goals).
98
+ */
99
+ export const productTagTranslations = pgTable("product_tag_translations", {
100
+ id: typeId("product_tag_translations"),
101
+ tagId: typeIdRef("tag_id")
102
+ .notNull()
103
+ .references(() => productTags.id, { onDelete: "cascade" }),
104
+ languageTag: text("language_tag").notNull(),
105
+ name: text("name").notNull(),
106
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
107
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
108
+ }, (table) => [
109
+ uniqueIndex("uidx_product_tag_translations_locale").on(table.tagId, table.languageTag),
110
+ index("idx_product_tag_translations_language").on(table.languageTag),
111
+ index("idx_product_tag_translations_tag_language_created").on(table.tagId, table.languageTag, table.createdAt),
112
+ index("idx_product_tag_translations_language_created").on(table.languageTag, table.createdAt),
113
+ ]);
114
+ export const destinations = pgTable("destinations", {
115
+ id: typeId("destinations"),
116
+ parentId: typeIdRef("parent_id"),
117
+ slug: text("slug").notNull(),
118
+ code: text("code"),
119
+ canonicalPlaceId: text("canonical_place_id"),
120
+ destinationType: text("destination_type").notNull().default("destination"),
121
+ latitude: doublePrecision("latitude"),
122
+ longitude: doublePrecision("longitude"),
123
+ sortOrder: integer("sort_order").notNull().default(0),
124
+ active: boolean("active").notNull().default(true),
125
+ metadata: jsonb("metadata").$type(),
126
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
127
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
128
+ }, (table) => [
129
+ uniqueIndex("uidx_destinations_slug").on(table.slug),
130
+ uniqueIndex("uidx_destinations_code").on(table.code),
131
+ index("idx_destinations_parent").on(table.parentId),
132
+ index("idx_destinations_active").on(table.active),
133
+ index("idx_destinations_canonical_place").on(table.canonicalPlaceId),
134
+ index("idx_destinations_sort_slug").on(table.sortOrder, table.slug),
135
+ index("idx_destinations_active_sort_slug").on(table.active, table.sortOrder, table.slug),
136
+ index("idx_destinations_type_sort_slug").on(table.destinationType, table.sortOrder, table.slug),
137
+ index("idx_destinations_parent_sort_slug").on(table.parentId, table.sortOrder, table.slug),
138
+ ]);
139
+ export const destinationTranslations = pgTable("destination_translations", {
140
+ id: typeId("destination_translations"),
141
+ destinationId: typeIdRef("destination_id")
142
+ .notNull()
143
+ .references(() => destinations.id, { onDelete: "cascade" }),
144
+ languageTag: text("language_tag").notNull(),
145
+ name: text("name").notNull(),
146
+ description: text("description"),
147
+ seoTitle: text("seo_title"),
148
+ seoDescription: text("seo_description"),
149
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
150
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
151
+ }, (table) => [
152
+ uniqueIndex("uidx_destination_translations_locale").on(table.destinationId, table.languageTag),
153
+ index("idx_destination_translations_language").on(table.languageTag),
154
+ index("idx_destination_translations_destination_language_created").on(table.destinationId, table.languageTag, table.createdAt),
155
+ index("idx_destination_translations_language_created").on(table.languageTag, table.createdAt),
156
+ // Trigram GIN indexes back the `ILIKE '%term%'` destination search
157
+ // (name OR description — both branches need an index for a BitmapOr
158
+ // plan). Requires the pg_trgm extension.
159
+ index("idx_destination_translations_name_trgm").using("gin", table.name.op("gin_trgm_ops")),
160
+ index("idx_destination_translations_description_trgm").using("gin", table.description.op("gin_trgm_ops")),
161
+ ]);
162
+ export const productCategoryProducts = pgTable("product_category_products", {
163
+ productId: typeIdRef("product_id")
164
+ .notNull()
165
+ .references(() => products.id, { onDelete: "cascade" }),
166
+ categoryId: typeIdRef("category_id")
167
+ .notNull()
168
+ .references(() => productCategories.id, { onDelete: "cascade" }),
169
+ sortOrder: integer("sort_order").notNull().default(0),
170
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
171
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
172
+ }, (table) => [
173
+ primaryKey({ columns: [table.productId, table.categoryId] }),
174
+ index("idx_pcp_product_sort").on(table.productId, table.sortOrder),
175
+ index("idx_pcp_category").on(table.categoryId),
176
+ ]);
177
+ export const productTagProducts = pgTable("product_tag_products", {
178
+ productId: typeIdRef("product_id")
179
+ .notNull()
180
+ .references(() => products.id, { onDelete: "cascade" }),
181
+ tagId: typeIdRef("tag_id")
182
+ .notNull()
183
+ .references(() => productTags.id, { onDelete: "cascade" }),
184
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
185
+ }, (table) => [
186
+ primaryKey({ columns: [table.productId, table.tagId] }),
187
+ index("idx_ptp_tag").on(table.tagId),
188
+ ]);
189
+ export const productDestinations = pgTable("product_destinations", {
190
+ productId: typeIdRef("product_id")
191
+ .notNull()
192
+ .references(() => products.id, { onDelete: "cascade" }),
193
+ destinationId: typeIdRef("destination_id")
194
+ .notNull()
195
+ .references(() => destinations.id, { onDelete: "cascade" }),
196
+ sortOrder: integer("sort_order").notNull().default(0),
197
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
198
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
199
+ }, (table) => [
200
+ primaryKey({ columns: [table.productId, table.destinationId] }),
201
+ index("idx_product_destinations_product_sort").on(table.productId, table.sortOrder),
202
+ index("idx_product_destinations_destination_sort").on(table.destinationId, table.sortOrder),
203
+ ]);
@@ -0,0 +1,10 @@
1
+ export { bookingItemProductDetails, bookingProductDetails } from "./booking-extension.js";
2
+ export * from "./extras/schema.js";
3
+ export * from "./schema-core.js";
4
+ export * from "./schema-itinerary.js";
5
+ export * from "./schema-relations.js";
6
+ export * from "./schema-settings.js";
7
+ export * from "./schema-shared.js";
8
+ export { type InsertProductsSourcedContent, PRODUCTS_CONTENT_MARKET_ANY, type ProductsSourcedContentFetchStatus, productsSourcedContentTable, type SelectProductsSourcedContent, } from "./schema-sourced-content.js";
9
+ export * from "./schema-taxonomy.js";
10
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AACzF,cAAc,oBAAoB,CAAA;AAClC,cAAc,kBAAkB,CAAA;AAChC,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,sBAAsB,CAAA;AACpC,cAAc,oBAAoB,CAAA;AAClC,OAAO,EACL,KAAK,4BAA4B,EACjC,2BAA2B,EAC3B,KAAK,iCAAiC,EACtC,2BAA2B,EAC3B,KAAK,4BAA4B,GAClC,MAAM,6BAA6B,CAAA;AACpC,cAAc,sBAAsB,CAAA"}
package/dist/schema.js ADDED
@@ -0,0 +1,9 @@
1
+ export { bookingItemProductDetails, bookingProductDetails } from "./booking-extension.js";
2
+ export * from "./extras/schema.js";
3
+ export * from "./schema-core.js";
4
+ export * from "./schema-itinerary.js";
5
+ export * from "./schema-relations.js";
6
+ export * from "./schema-settings.js";
7
+ export * from "./schema-shared.js";
8
+ export { PRODUCTS_CONTENT_MARKET_ANY, productsSourcedContentTable, } from "./schema-sourced-content.js";
9
+ export * from "./schema-taxonomy.js";
@@ -0,0 +1,29 @@
1
+ import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
2
+ import { products } from "./schema-core.js";
3
+ type ProductStatus = (typeof products.$inferSelect)["status"];
4
+ export interface ProductAggregates {
5
+ total: number;
6
+ countsByStatus: Array<{
7
+ status: ProductStatus;
8
+ count: number;
9
+ }>;
10
+ /** Shorthand for the `active` bucket — dashboard KPI card. */
11
+ active: number;
12
+ /**
13
+ * Products publicly listed on the storefront: `status = active` AND
14
+ * `activated = true` AND `visibility = 'public'`. Distinct from `active`,
15
+ * which includes internal-only active products.
16
+ */
17
+ publicActive: number;
18
+ /** Product creation count bucketed by UTC yearMonth, oldest first. */
19
+ monthlyCreatedCounts: Array<{
20
+ yearMonth: string;
21
+ count: number;
22
+ }>;
23
+ }
24
+ export declare function getProductAggregates(db: PostgresJsDatabase, options?: {
25
+ from?: string;
26
+ to?: string;
27
+ }): Promise<ProductAggregates>;
28
+ export {};
29
+ //# sourceMappingURL=service-aggregates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-aggregates.d.ts","sourceRoot":"","sources":["../src/service-aggregates.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAEjE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAE3C,KAAK,aAAa,GAAG,CAAC,OAAO,QAAQ,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,CAAA;AAI7D,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,cAAc,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,aAAa,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC/D,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB,sEAAsE;IACtE,oBAAoB,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAClE;AAED,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,kBAAkB,EACtB,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAO,GAC3C,OAAO,CAAC,iBAAiB,CAAC,CA4D5B"}
@@ -0,0 +1,56 @@
1
+ import { and, eq, sql } from "drizzle-orm";
2
+ import { products } from "./schema-core.js";
3
+ const ALL_PRODUCT_STATUSES = ["draft", "active", "archived"];
4
+ export async function getProductAggregates(db, options = {}) {
5
+ const fromDate = options.from ? new Date(options.from) : undefined;
6
+ const toDate = options.to ? new Date(options.to) : undefined;
7
+ const rangeConditions = [];
8
+ // agent-quality: raw-sql reviewed -- owner: inventory; dynamic SQL interpolation uses Drizzle parameter binding or vetted SQL identifiers.
9
+ if (fromDate)
10
+ rangeConditions.push(sql `${products.createdAt} >= ${fromDate.toISOString()}`);
11
+ // agent-quality: raw-sql reviewed -- owner: inventory; dynamic SQL interpolation uses Drizzle parameter binding or vetted SQL identifiers.
12
+ if (toDate)
13
+ rangeConditions.push(sql `${products.createdAt} < ${toDate.toISOString()}`);
14
+ const rangeWhere = rangeConditions.length ? and(...rangeConditions) : undefined;
15
+ // Publicly-listed count ignores the date range — it's a point-in-time KPI
16
+ // ("what's live on the storefront right now"). The range-bound `active`
17
+ // bucket serves the "how many active products did we create this quarter"
18
+ // question instead.
19
+ const [[totalRow], statusRows, [publicActiveRow], monthlyCreatedCountsRows] = await Promise.all([
20
+ db.select({ count: sql `count(*)::int` }).from(products).where(rangeWhere),
21
+ db
22
+ .select({ status: products.status, count: sql `count(*)::int` })
23
+ .from(products)
24
+ .where(rangeWhere)
25
+ .groupBy(products.status),
26
+ db
27
+ .select({ count: sql `count(*)::int` })
28
+ .from(products)
29
+ .where(and(eq(products.status, "active"), eq(products.activated, true), eq(products.visibility, "public"))),
30
+ db
31
+ .select({
32
+ yearMonth: sql `to_char(${products.createdAt} at time zone 'UTC', 'YYYY-MM')`,
33
+ count: sql `count(*)::int`,
34
+ })
35
+ .from(products)
36
+ .where(rangeWhere)
37
+ // agent-quality: raw-sql reviewed -- owner: inventory; dynamic SQL interpolation uses Drizzle parameter binding or vetted SQL identifiers.
38
+ .groupBy(sql `to_char(${products.createdAt} at time zone 'UTC', 'YYYY-MM')`)
39
+ // agent-quality: raw-sql reviewed -- owner: inventory; dynamic SQL interpolation uses Drizzle parameter binding or vetted SQL identifiers.
40
+ .orderBy(sql `to_char(${products.createdAt} at time zone 'UTC', 'YYYY-MM')`),
41
+ ]);
42
+ const statusMap = new Map(statusRows.map((r) => [r.status, r.count]));
43
+ return {
44
+ total: totalRow?.count ?? 0,
45
+ countsByStatus: ALL_PRODUCT_STATUSES.map((status) => ({
46
+ status,
47
+ count: statusMap.get(status) ?? 0,
48
+ })),
49
+ active: statusMap.get("active") ?? 0,
50
+ publicActive: publicActiveRow?.count ?? 0,
51
+ monthlyCreatedCounts: monthlyCreatedCountsRows.map((row) => ({
52
+ yearMonth: row.yearMonth,
53
+ count: row.count,
54
+ })),
55
+ };
56
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Projection extension that joins product → destinations and contributes
3
+ * locale-aware destination fields (regions, countries, cities, slugs, ids)
4
+ * onto the product search document.
5
+ *
6
+ * Wire via `createProductDocumentBuilder({ extensions: [destinationsExtension] })`.
7
+ * Requires the registry to include `productDestinationsCatalogPolicy` —
8
+ * otherwise the contributed fields are silently dropped by the indexer's
9
+ * field-policy filter.
10
+ *
11
+ * Locale handling: `destination_translations` is keyed by `(destination_id,
12
+ * language_tag)`. The projection looks up the slice's locale; missing
13
+ * translations fall back to the destination's canonical `slug`. Operators
14
+ * who want the locale lookup to fall back to a different language (e.g.
15
+ * `fr-CA` → `fr` → `en`) should pre-populate the translation row;
16
+ * multi-tier fallback isn't built into the joins.
17
+ *
18
+ * Today's destinations table has no coordinate columns — geopoints come
19
+ * from `product_locations` and will ship as a separate projection
20
+ * extension once that policy lands.
21
+ */
22
+ import type { ProductProjectionExtension } from "./service-catalog-plane.js";
23
+ /**
24
+ * Construct the destinations projection extension.
25
+ *
26
+ * Returns a `ProductProjectionExtension` ready to pass to
27
+ * `createProductDocumentBuilder`.
28
+ */
29
+ export declare function createProductDestinationsProjectionExtension(): ProductProjectionExtension;
30
+ //# sourceMappingURL=service-catalog-plane-destinations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-catalog-plane-destinations.d.ts","sourceRoot":"","sources":["../src/service-catalog-plane-destinations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAMH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAA;AAsH5E;;;;;GAKG;AACH,wBAAgB,4CAA4C,IAAI,0BAA0B,CAgCzF"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Projection extension that joins product → destinations and contributes
3
+ * locale-aware destination fields (regions, countries, cities, slugs, ids)
4
+ * onto the product search document.
5
+ *
6
+ * Wire via `createProductDocumentBuilder({ extensions: [destinationsExtension] })`.
7
+ * Requires the registry to include `productDestinationsCatalogPolicy` —
8
+ * otherwise the contributed fields are silently dropped by the indexer's
9
+ * field-policy filter.
10
+ *
11
+ * Locale handling: `destination_translations` is keyed by `(destination_id,
12
+ * language_tag)`. The projection looks up the slice's locale; missing
13
+ * translations fall back to the destination's canonical `slug`. Operators
14
+ * who want the locale lookup to fall back to a different language (e.g.
15
+ * `fr-CA` → `fr` → `en`) should pre-populate the translation row;
16
+ * multi-tier fallback isn't built into the joins.
17
+ *
18
+ * Today's destinations table has no coordinate columns — geopoints come
19
+ * from `product_locations` and will ship as a separate projection
20
+ * extension once that policy lands.
21
+ */
22
+ import { and, eq, inArray } from "drizzle-orm";
23
+ import { destinations, destinationTranslations, productDestinations } from "./schema-taxonomy.js";
24
+ async function joinProductDestinations(db, productId, languageTag) {
25
+ // Sub-query the linked destination ids first so the locale lookup is
26
+ // single-shot. Drizzle's join builder doesn't compose left joins on
27
+ // composite predicates cleanly across all dialects, so two queries +
28
+ // an in-memory merge keeps the contract obvious.
29
+ const links = await db
30
+ .select({
31
+ destinationId: productDestinations.destinationId,
32
+ destinationType: destinations.destinationType,
33
+ slug: destinations.slug,
34
+ canonicalPlaceId: destinations.canonicalPlaceId,
35
+ active: destinations.active,
36
+ })
37
+ .from(productDestinations)
38
+ .innerJoin(destinations, eq(productDestinations.destinationId, destinations.id))
39
+ .where(eq(productDestinations.productId, productId));
40
+ const activeLinks = links.filter((row) => row.active);
41
+ if (activeLinks.length === 0)
42
+ return [];
43
+ const destinationIds = activeLinks.map((row) => row.destinationId);
44
+ const translations = await db
45
+ .select({
46
+ destinationId: destinationTranslations.destinationId,
47
+ name: destinationTranslations.name,
48
+ })
49
+ .from(destinationTranslations)
50
+ .where(and(inArray(destinationTranslations.destinationId, destinationIds), eq(destinationTranslations.languageTag, languageTag)));
51
+ const nameByDestinationId = new Map();
52
+ for (const row of translations) {
53
+ nameByDestinationId.set(row.destinationId, row.name);
54
+ }
55
+ return activeLinks.map((row) => ({
56
+ destinationId: row.destinationId,
57
+ destinationType: row.destinationType,
58
+ slug: row.slug,
59
+ canonicalPlaceId: row.canonicalPlaceId ?? null,
60
+ translatedName: nameByDestinationId.get(row.destinationId) ?? null,
61
+ }));
62
+ }
63
+ /** Bucket destinations by `destinationType` and return locale-aware names. */
64
+ function bucketByType(rows) {
65
+ const regions = [];
66
+ const countries = [];
67
+ const cities = [];
68
+ const ports = [];
69
+ const waterways = [];
70
+ const slugs = [];
71
+ const ids = [];
72
+ const canonicalPlaceIds = [];
73
+ for (const row of rows) {
74
+ const label = row.translatedName ?? row.slug;
75
+ switch (row.destinationType) {
76
+ case "region":
77
+ regions.push(label);
78
+ break;
79
+ case "country":
80
+ countries.push(label);
81
+ break;
82
+ case "city":
83
+ cities.push(label);
84
+ break;
85
+ case "port":
86
+ ports.push(label);
87
+ break;
88
+ case "river":
89
+ case "sea":
90
+ case "ocean":
91
+ case "canal":
92
+ case "lake":
93
+ waterways.push(label);
94
+ break;
95
+ // "destination" (the catch-all default) doesn't bucket into any of
96
+ // the typed lists. Its slug + id still land in the doc so storefronts
97
+ // can filter generically.
98
+ }
99
+ slugs.push(row.slug);
100
+ ids.push(row.destinationId);
101
+ if (row.canonicalPlaceId) {
102
+ canonicalPlaceIds.push(row.canonicalPlaceId);
103
+ }
104
+ }
105
+ return { regions, countries, cities, ports, waterways, slugs, ids, canonicalPlaceIds };
106
+ }
107
+ /**
108
+ * Construct the destinations projection extension.
109
+ *
110
+ * Returns a `ProductProjectionExtension` ready to pass to
111
+ * `createProductDocumentBuilder`.
112
+ */
113
+ export function createProductDestinationsProjectionExtension() {
114
+ return {
115
+ name: "products:destinations",
116
+ async project(db, productId, slice) {
117
+ const rows = await joinProductDestinations(db, productId, slice.locale);
118
+ if (rows.length === 0) {
119
+ return new Map([
120
+ ["regions[]", []],
121
+ ["countries[]", []],
122
+ ["cities[]", []],
123
+ ["ports[]", []],
124
+ ["waterways[]", []],
125
+ ["destinationSlugs[]", []],
126
+ ["destinationIds[]", []],
127
+ ["destinationCanonicalPlaceIds[]", []],
128
+ ]);
129
+ }
130
+ const { regions, countries, cities, ports, waterways, slugs, ids, canonicalPlaceIds } = bucketByType(rows);
131
+ return new Map([
132
+ ["regions[]", regions],
133
+ ["countries[]", countries],
134
+ ["cities[]", cities],
135
+ ["ports[]", ports],
136
+ ["waterways[]", waterways],
137
+ ["destinationSlugs[]", slugs],
138
+ ["destinationIds[]", ids],
139
+ ["destinationCanonicalPlaceIds[]", canonicalPlaceIds],
140
+ ]);
141
+ },
142
+ };
143
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Projection extension that joins product → categories (with ancestor walk)
3
+ * and product → tags, contributing taxonomy fields onto the product search
4
+ * document.
5
+ *
6
+ * Wire via `createProductDocumentBuilder({ extensions: [taxonomyExtension] })`.
7
+ * Requires the registry to include `productTaxonomyCatalogPolicy` —
8
+ * otherwise the contributed fields are silently dropped by the indexer's
9
+ * field-policy filter.
10
+ *
11
+ * Hierarchy denormalization: Typesense can't recurse, so a product linked
12
+ * to "Hiking" (parent: "Adventure") needs both labels in `categories[]` for
13
+ * the "Adventure" filter to match. The projection walks the parent chain
14
+ * iteratively, filtering inactive ancestors (so an operator-paused parent
15
+ * stops surfacing its still-active children under the parent filter).
16
+ *
17
+ * Localization (#502): when a `product_category_translations` /
18
+ * `product_tag_translations` row exists for the slice's locale, its `name`
19
+ * wins. Otherwise the projection falls back to the canonical
20
+ * `productCategories.name` / `productTags.name` (the legacy single-locale
21
+ * column). This makes the upgrade non-breaking — operators that haven't
22
+ * created translations keep seeing the English label on every locale slice
23
+ * exactly as before.
24
+ *
25
+ * Slugs stay single-locale on `productCategories.slug` (per #502 non-goals
26
+ * — operators want stable URLs that don't shift when translations are
27
+ * edited).
28
+ */
29
+ import type { ProductProjectionExtension } from "./service-catalog-plane.js";
30
+ interface CategoryRow {
31
+ id: string;
32
+ parentId: string | null;
33
+ name: string;
34
+ slug: string;
35
+ active: boolean;
36
+ }
37
+ interface DirectCategoryLink {
38
+ categoryId: string;
39
+ sortOrder: number;
40
+ }
41
+ interface TagRow {
42
+ id: string;
43
+ name: string;
44
+ }
45
+ interface TaxonomyProjection {
46
+ categoryIds: string[];
47
+ categoryNames: string[];
48
+ categorySlugs: string[];
49
+ primaryCategoryId: string | null;
50
+ primaryCategoryName: string | null;
51
+ primaryCategorySlug: string | null;
52
+ tagIds: string[];
53
+ tagLabels: string[];
54
+ }
55
+ /**
56
+ * Pure aggregation kernel. `categoryNameByLocale` and `tagNameByLocale`
57
+ * carry slice-locale translations; entries override the canonical name on
58
+ * the row. Missing entries fall back to the canonical name. Slug stays
59
+ * canonical regardless — there's no per-locale slug.
60
+ */
61
+ declare function buildTaxonomyProjection(directLinks: ReadonlyArray<DirectCategoryLink>, resolvedCategories: ReadonlyMap<string, CategoryRow>, tags: ReadonlyArray<TagRow>, categoryNameByLocale?: ReadonlyMap<string, string>, tagNameByLocale?: ReadonlyMap<string, string>): TaxonomyProjection;
62
+ /**
63
+ * Construct the taxonomy projection extension.
64
+ *
65
+ * Returns a `ProductProjectionExtension` ready to pass to
66
+ * `createProductDocumentBuilder`.
67
+ */
68
+ export declare function createProductTaxonomyProjectionExtension(): ProductProjectionExtension;
69
+ export declare const __test__: {
70
+ buildTaxonomyProjection: typeof buildTaxonomyProjection;
71
+ };
72
+ export {};
73
+ //# sourceMappingURL=service-catalog-plane-taxonomy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-catalog-plane-taxonomy.d.ts","sourceRoot":"","sources":["../src/service-catalog-plane-taxonomy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAaH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAA;AAE5E,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,OAAO,CAAA;CAChB;AAED,UAAU,kBAAkB;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;CAClB;AAkED,UAAU,MAAM;IACd,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;CACb;AA8DD,UAAU,kBAAkB;IAC1B,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,SAAS,EAAE,MAAM,EAAE,CAAA;CACpB;AAED;;;;;GAKG;AACH,iBAAS,uBAAuB,CAC9B,WAAW,EAAE,aAAa,CAAC,kBAAkB,CAAC,EAC9C,kBAAkB,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,EACpD,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,oBAAoB,GAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAa,EAC7D,eAAe,GAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAa,GACvD,kBAAkB,CAmEpB;AAED;;;;;GAKG;AACH,wBAAgB,wCAAwC,IAAI,0BAA0B,CA6CrF;AAGD,eAAO,MAAM,QAAQ;;CAEpB,CAAA"}