@voyantjs/products 0.52.2 → 0.52.4

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 (62) hide show
  1. package/dist/action-ledger-drift.d.ts +29 -0
  2. package/dist/action-ledger-drift.d.ts.map +1 -0
  3. package/dist/action-ledger-drift.js +335 -0
  4. package/dist/action-ledger.d.ts +104 -0
  5. package/dist/action-ledger.d.ts.map +1 -0
  6. package/dist/action-ledger.js +100 -0
  7. package/dist/booking-extension.d.ts +3 -3
  8. package/dist/events.d.ts +1 -1
  9. package/dist/events.d.ts.map +1 -1
  10. package/dist/route-env.d.ts +22 -0
  11. package/dist/route-env.d.ts.map +1 -0
  12. package/dist/route-env.js +1 -0
  13. package/dist/routes-associations.d.ts +164 -0
  14. package/dist/routes-associations.d.ts.map +1 -0
  15. package/dist/routes-associations.js +100 -0
  16. package/dist/routes-catalog.d.ts +436 -0
  17. package/dist/routes-catalog.d.ts.map +1 -0
  18. package/dist/routes-catalog.js +104 -0
  19. package/dist/routes-configuration.d.ts +773 -0
  20. package/dist/routes-configuration.d.ts.map +1 -0
  21. package/dist/routes-configuration.js +364 -0
  22. package/dist/routes-core.d.ts +302 -0
  23. package/dist/routes-core.d.ts.map +1 -0
  24. package/dist/routes-core.js +79 -0
  25. package/dist/routes-itinerary.d.ts +614 -0
  26. package/dist/routes-itinerary.d.ts.map +1 -0
  27. package/dist/routes-itinerary.js +309 -0
  28. package/dist/routes-maintenance.d.ts +32 -0
  29. package/dist/routes-maintenance.d.ts.map +1 -0
  30. package/dist/routes-maintenance.js +14 -0
  31. package/dist/routes-media.d.ts +634 -0
  32. package/dist/routes-media.d.ts.map +1 -0
  33. package/dist/routes-media.js +245 -0
  34. package/dist/routes-merchandising.d.ts +1108 -0
  35. package/dist/routes-merchandising.d.ts.map +1 -0
  36. package/dist/routes-merchandising.js +376 -0
  37. package/dist/routes-options.d.ts +363 -0
  38. package/dist/routes-options.d.ts.map +1 -0
  39. package/dist/routes-options.js +173 -0
  40. package/dist/routes-public.d.ts +4 -4
  41. package/dist/routes-translations.d.ts +477 -0
  42. package/dist/routes-translations.d.ts.map +1 -0
  43. package/dist/routes-translations.js +258 -0
  44. package/dist/routes.d.ts +417 -355
  45. package/dist/routes.d.ts.map +1 -1
  46. package/dist/routes.js +21 -1133
  47. package/dist/schema-core.d.ts +3 -3
  48. package/dist/schema-itinerary.d.ts +1 -1
  49. package/dist/schema-settings.d.ts +4 -4
  50. package/dist/service-catalog.d.ts +2 -2
  51. package/dist/service-public.d.ts +4 -4
  52. package/dist/service.d.ts +225 -97
  53. package/dist/service.d.ts.map +1 -1
  54. package/dist/service.js +91 -0
  55. package/dist/tasks/brochures.d.ts +1 -1
  56. package/dist/validation-catalog.d.ts +10 -10
  57. package/dist/validation-config.d.ts +17 -17
  58. package/dist/validation-content.d.ts +26 -26
  59. package/dist/validation-core.d.ts +21 -21
  60. package/dist/validation-public.d.ts +25 -25
  61. package/dist/validation-shared.d.ts +11 -11
  62. package/package.json +13 -7
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes-merchandising.d.ts","sourceRoot":"","sources":["../src/routes-merchandising.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAA;AAIzC,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2CAmfnC,CAAA"}
@@ -0,0 +1,376 @@
1
+ import { parseJsonBody, parseQuery } from "@voyantjs/hono";
2
+ import { Hono } from "hono";
3
+ import { appendProductMutationLedgerEntry, changedMutationFields } from "./action-ledger.js";
4
+ import { emitProductContentChanged } from "./events.js";
5
+ import { productsService } from "./service.js";
6
+ import * as validation from "./validation.js";
7
+ export const productMerchandisingRoutes = new Hono()
8
+ .get("/features", async (c) => {
9
+ const query = parseQuery(c, validation.productFeatureListQuerySchema);
10
+ return c.json(await productsService.listFeatures(c.get("db"), query));
11
+ })
12
+ .get("/features/:id", async (c) => {
13
+ const row = await productsService.getFeatureById(c.get("db"), c.req.param("id"));
14
+ if (!row) {
15
+ return c.json({ error: "Product feature not found" }, 404);
16
+ }
17
+ return c.json({ data: row });
18
+ })
19
+ .post("/:id/features", async (c) => {
20
+ const productId = c.req.param("id");
21
+ const body = await parseJsonBody(c, validation.insertProductFeatureSchema);
22
+ const row = await productsService.createFeature(c.get("db"), productId, body);
23
+ if (!row) {
24
+ return c.json({ error: "Product not found" }, 404);
25
+ }
26
+ await appendProductMutationLedgerEntry(c, {
27
+ action: "create",
28
+ productId,
29
+ changedFields: changedMutationFields(body, null, row),
30
+ subject: "product feature",
31
+ actionName: "product.feature.create",
32
+ routeOrToolName: "products.feature.create",
33
+ });
34
+ await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "feature" });
35
+ return c.json({ data: row }, 201);
36
+ })
37
+ .patch("/features/:id", async (c) => {
38
+ const featureId = c.req.param("id");
39
+ const body = await parseJsonBody(c, validation.updateProductFeatureSchema);
40
+ const before = await productsService.getFeatureById(c.get("db"), featureId);
41
+ if (!before) {
42
+ return c.json({ error: "Product feature not found" }, 404);
43
+ }
44
+ const row = await productsService.updateFeature(c.get("db"), featureId, body);
45
+ if (!row) {
46
+ return c.json({ error: "Product feature not found" }, 404);
47
+ }
48
+ await appendProductMutationLedgerEntry(c, {
49
+ action: "update",
50
+ productId: row.productId,
51
+ changedFields: changedMutationFields(body, before, row),
52
+ subject: "product feature",
53
+ actionName: "product.feature.update",
54
+ routeOrToolName: "products.feature.update",
55
+ });
56
+ await emitProductContentChanged(c.get("eventBus"), { id: row.productId, axis: "feature" });
57
+ return c.json({ data: row });
58
+ })
59
+ .delete("/features/:id", async (c) => {
60
+ const featureId = c.req.param("id");
61
+ const before = await productsService.getFeatureById(c.get("db"), featureId);
62
+ if (!before) {
63
+ return c.json({ error: "Product feature not found" }, 404);
64
+ }
65
+ const row = await productsService.deleteFeature(c.get("db"), featureId);
66
+ if (!row) {
67
+ return c.json({ error: "Product feature not found" }, 404);
68
+ }
69
+ await appendProductMutationLedgerEntry(c, {
70
+ action: "delete",
71
+ productId: before.productId,
72
+ changedFields: [],
73
+ subject: "product feature",
74
+ actionName: "product.feature.delete",
75
+ routeOrToolName: "products.feature.delete",
76
+ });
77
+ await emitProductContentChanged(c.get("eventBus"), { id: before.productId, axis: "feature" });
78
+ return c.json({ success: true }, 200);
79
+ })
80
+ .get("/faqs", async (c) => {
81
+ const query = parseQuery(c, validation.productFaqListQuerySchema);
82
+ return c.json(await productsService.listFaqs(c.get("db"), query));
83
+ })
84
+ .get("/faqs/:id", async (c) => {
85
+ const row = await productsService.getFaqById(c.get("db"), c.req.param("id"));
86
+ if (!row) {
87
+ return c.json({ error: "Product FAQ not found" }, 404);
88
+ }
89
+ return c.json({ data: row });
90
+ })
91
+ .post("/:id/faqs", async (c) => {
92
+ const productId = c.req.param("id");
93
+ const body = await parseJsonBody(c, validation.insertProductFaqSchema);
94
+ const row = await productsService.createFaq(c.get("db"), productId, body);
95
+ if (!row) {
96
+ return c.json({ error: "Product not found" }, 404);
97
+ }
98
+ await appendProductMutationLedgerEntry(c, {
99
+ action: "create",
100
+ productId,
101
+ changedFields: changedMutationFields(body, null, row),
102
+ subject: "product FAQ",
103
+ actionName: "product.faq.create",
104
+ routeOrToolName: "products.faq.create",
105
+ });
106
+ await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "faq" });
107
+ return c.json({ data: row }, 201);
108
+ })
109
+ .patch("/faqs/:id", async (c) => {
110
+ const faqId = c.req.param("id");
111
+ const body = await parseJsonBody(c, validation.updateProductFaqSchema);
112
+ const before = await productsService.getFaqById(c.get("db"), faqId);
113
+ if (!before) {
114
+ return c.json({ error: "Product FAQ not found" }, 404);
115
+ }
116
+ const row = await productsService.updateFaq(c.get("db"), faqId, body);
117
+ if (!row) {
118
+ return c.json({ error: "Product FAQ not found" }, 404);
119
+ }
120
+ await appendProductMutationLedgerEntry(c, {
121
+ action: "update",
122
+ productId: row.productId,
123
+ changedFields: changedMutationFields(body, before, row),
124
+ subject: "product FAQ",
125
+ actionName: "product.faq.update",
126
+ routeOrToolName: "products.faq.update",
127
+ });
128
+ await emitProductContentChanged(c.get("eventBus"), { id: row.productId, axis: "faq" });
129
+ return c.json({ data: row });
130
+ })
131
+ .delete("/faqs/:id", async (c) => {
132
+ const faqId = c.req.param("id");
133
+ const before = await productsService.getFaqById(c.get("db"), faqId);
134
+ if (!before) {
135
+ return c.json({ error: "Product FAQ not found" }, 404);
136
+ }
137
+ const row = await productsService.deleteFaq(c.get("db"), faqId);
138
+ if (!row) {
139
+ return c.json({ error: "Product FAQ not found" }, 404);
140
+ }
141
+ await appendProductMutationLedgerEntry(c, {
142
+ action: "delete",
143
+ productId: before.productId,
144
+ changedFields: [],
145
+ subject: "product FAQ",
146
+ actionName: "product.faq.delete",
147
+ routeOrToolName: "products.faq.delete",
148
+ });
149
+ await emitProductContentChanged(c.get("eventBus"), { id: before.productId, axis: "faq" });
150
+ return c.json({ success: true }, 200);
151
+ })
152
+ .get("/locations", async (c) => {
153
+ const query = parseQuery(c, validation.productLocationListQuerySchema);
154
+ return c.json(await productsService.listLocations(c.get("db"), query));
155
+ })
156
+ .get("/locations/:id", async (c) => {
157
+ const row = await productsService.getLocationById(c.get("db"), c.req.param("id"));
158
+ if (!row) {
159
+ return c.json({ error: "Product location not found" }, 404);
160
+ }
161
+ return c.json({ data: row });
162
+ })
163
+ .post("/:id/locations", async (c) => {
164
+ const productId = c.req.param("id");
165
+ const body = await parseJsonBody(c, validation.insertProductLocationSchema);
166
+ const row = await productsService.createLocation(c.get("db"), productId, body);
167
+ if (!row) {
168
+ return c.json({ error: "Product not found" }, 404);
169
+ }
170
+ await appendProductMutationLedgerEntry(c, {
171
+ action: "create",
172
+ productId,
173
+ changedFields: changedMutationFields(body, null, row),
174
+ subject: "product location",
175
+ actionName: "product.location.create",
176
+ routeOrToolName: "products.location.create",
177
+ });
178
+ await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "location" });
179
+ return c.json({ data: row }, 201);
180
+ })
181
+ .patch("/locations/:id", async (c) => {
182
+ const locationId = c.req.param("id");
183
+ const body = await parseJsonBody(c, validation.updateProductLocationSchema);
184
+ const before = await productsService.getLocationById(c.get("db"), locationId);
185
+ if (!before) {
186
+ return c.json({ error: "Product location not found" }, 404);
187
+ }
188
+ const row = await productsService.updateLocation(c.get("db"), locationId, body);
189
+ if (!row) {
190
+ return c.json({ error: "Product location not found" }, 404);
191
+ }
192
+ await appendProductMutationLedgerEntry(c, {
193
+ action: "update",
194
+ productId: row.productId,
195
+ changedFields: changedMutationFields(body, before, row),
196
+ subject: "product location",
197
+ actionName: "product.location.update",
198
+ routeOrToolName: "products.location.update",
199
+ });
200
+ await emitProductContentChanged(c.get("eventBus"), { id: row.productId, axis: "location" });
201
+ return c.json({ data: row });
202
+ })
203
+ .delete("/locations/:id", async (c) => {
204
+ const locationId = c.req.param("id");
205
+ const before = await productsService.getLocationById(c.get("db"), locationId);
206
+ if (!before) {
207
+ return c.json({ error: "Product location not found" }, 404);
208
+ }
209
+ const row = await productsService.deleteLocation(c.get("db"), locationId);
210
+ if (!row) {
211
+ return c.json({ error: "Product location not found" }, 404);
212
+ }
213
+ await appendProductMutationLedgerEntry(c, {
214
+ action: "delete",
215
+ productId: before.productId,
216
+ changedFields: [],
217
+ subject: "product location",
218
+ actionName: "product.location.delete",
219
+ routeOrToolName: "products.location.delete",
220
+ });
221
+ await emitProductContentChanged(c.get("eventBus"), { id: before.productId, axis: "location" });
222
+ return c.json({ success: true }, 200);
223
+ })
224
+ .get("/destinations", async (c) => {
225
+ const query = parseQuery(c, validation.destinationListQuerySchema);
226
+ return c.json(await productsService.listDestinations(c.get("db"), query));
227
+ })
228
+ .get("/destinations/:id", async (c) => {
229
+ const row = await productsService.getDestinationById(c.get("db"), c.req.param("id"));
230
+ if (!row) {
231
+ return c.json({ error: "Destination not found" }, 404);
232
+ }
233
+ return c.json({ data: row });
234
+ })
235
+ .post("/destinations", async (c) => {
236
+ const row = await productsService.createDestination(c.get("db"), await parseJsonBody(c, validation.insertDestinationSchema));
237
+ return c.json({ data: row }, 201);
238
+ })
239
+ .patch("/destinations/:id", async (c) => {
240
+ const row = await productsService.updateDestination(c.get("db"), c.req.param("id"), await parseJsonBody(c, validation.updateDestinationSchema));
241
+ if (!row) {
242
+ return c.json({ error: "Destination not found" }, 404);
243
+ }
244
+ return c.json({ data: row });
245
+ })
246
+ .delete("/destinations/:id", async (c) => {
247
+ const row = await productsService.deleteDestination(c.get("db"), c.req.param("id"));
248
+ if (!row) {
249
+ return c.json({ error: "Destination not found" }, 404);
250
+ }
251
+ return c.json({ success: true }, 200);
252
+ })
253
+ .get("/destination-translations", async (c) => {
254
+ const query = parseQuery(c, validation.destinationTranslationListQuerySchema);
255
+ return c.json(await productsService.listDestinationTranslations(c.get("db"), query));
256
+ })
257
+ .post("/destinations/:id/translations", async (c) => {
258
+ const row = await productsService.upsertDestinationTranslation(c.get("db"), c.req.param("id"), await parseJsonBody(c, validation.insertDestinationTranslationSchema));
259
+ if (!row) {
260
+ return c.json({ error: "Destination not found" }, 404);
261
+ }
262
+ return c.json({ data: row }, 201);
263
+ })
264
+ .patch("/destination-translations/:id", async (c) => {
265
+ const row = await productsService.updateDestinationTranslation(c.get("db"), c.req.param("id"), await parseJsonBody(c, validation.updateDestinationTranslationSchema));
266
+ if (!row) {
267
+ return c.json({ error: "Destination translation not found" }, 404);
268
+ }
269
+ return c.json({ data: row });
270
+ })
271
+ .delete("/destination-translations/:id", async (c) => {
272
+ const row = await productsService.deleteDestinationTranslation(c.get("db"), c.req.param("id"));
273
+ if (!row) {
274
+ return c.json({ error: "Destination translation not found" }, 404);
275
+ }
276
+ return c.json({ success: true }, 200);
277
+ })
278
+ // ──────────────────────────────────────────────────────────────────────────
279
+ // Product category translations (locale-aware names + descriptions).
280
+ // Mirrors the destinations translation surface above. The catalog plane's
281
+ // taxonomy projection reads these per-slice locale and falls back to
282
+ // `productCategories.name` when no row exists for a given locale.
283
+ // ──────────────────────────────────────────────────────────────────────────
284
+ .get("/product-category-translations", async (c) => {
285
+ const query = parseQuery(c, validation.productCategoryTranslationListQuerySchema);
286
+ return c.json(await productsService.listProductCategoryTranslations(c.get("db"), query));
287
+ })
288
+ .post("/product-categories/:id/translations", async (c) => {
289
+ const row = await productsService.upsertProductCategoryTranslation(c.get("db"), c.req.param("id"), await parseJsonBody(c, validation.insertProductCategoryTranslationSchema));
290
+ if (!row) {
291
+ return c.json({ error: "Product category not found" }, 404);
292
+ }
293
+ return c.json({ data: row }, 201);
294
+ })
295
+ .patch("/product-category-translations/:id", async (c) => {
296
+ const row = await productsService.updateProductCategoryTranslation(c.get("db"), c.req.param("id"), await parseJsonBody(c, validation.updateProductCategoryTranslationSchema));
297
+ if (!row) {
298
+ return c.json({ error: "Product category translation not found" }, 404);
299
+ }
300
+ return c.json({ data: row });
301
+ })
302
+ .delete("/product-category-translations/:id", async (c) => {
303
+ const row = await productsService.deleteProductCategoryTranslation(c.get("db"), c.req.param("id"));
304
+ if (!row) {
305
+ return c.json({ error: "Product category translation not found" }, 404);
306
+ }
307
+ return c.json({ success: true }, 200);
308
+ })
309
+ // ──────────────────────────────────────────────────────────────────────────
310
+ // Product tag translations. Slimmer shape — tags are short labels with no
311
+ // description / SEO blurbs (per #502 non-goals).
312
+ // ──────────────────────────────────────────────────────────────────────────
313
+ .get("/product-tag-translations", async (c) => {
314
+ const query = parseQuery(c, validation.productTagTranslationListQuerySchema);
315
+ return c.json(await productsService.listProductTagTranslations(c.get("db"), query));
316
+ })
317
+ .post("/product-tags/:id/translations", async (c) => {
318
+ const row = await productsService.upsertProductTagTranslation(c.get("db"), c.req.param("id"), await parseJsonBody(c, validation.insertProductTagTranslationSchema));
319
+ if (!row) {
320
+ return c.json({ error: "Product tag not found" }, 404);
321
+ }
322
+ return c.json({ data: row }, 201);
323
+ })
324
+ .patch("/product-tag-translations/:id", async (c) => {
325
+ const row = await productsService.updateProductTagTranslation(c.get("db"), c.req.param("id"), await parseJsonBody(c, validation.updateProductTagTranslationSchema));
326
+ if (!row) {
327
+ return c.json({ error: "Product tag translation not found" }, 404);
328
+ }
329
+ return c.json({ data: row });
330
+ })
331
+ .delete("/product-tag-translations/:id", async (c) => {
332
+ const row = await productsService.deleteProductTagTranslation(c.get("db"), c.req.param("id"));
333
+ if (!row) {
334
+ return c.json({ error: "Product tag translation not found" }, 404);
335
+ }
336
+ return c.json({ success: true }, 200);
337
+ })
338
+ .get("/destination-links", async (c) => {
339
+ const query = parseQuery(c, validation.productDestinationListQuerySchema);
340
+ return c.json(await productsService.listProductDestinations(c.get("db"), query));
341
+ })
342
+ .post("/:id/destinations", async (c) => {
343
+ const productId = c.req.param("id");
344
+ const body = await parseJsonBody(c, validation.insertProductDestinationSchema);
345
+ const row = await productsService.assignProductDestination(c.get("db"), productId, body);
346
+ if (!row) {
347
+ return c.json({ error: "Product or destination not found" }, 404);
348
+ }
349
+ await appendProductMutationLedgerEntry(c, {
350
+ action: "create",
351
+ productId,
352
+ changedFields: changedMutationFields(body, null, row),
353
+ subject: "product destination link",
354
+ actionName: "product.destination_link.create",
355
+ routeOrToolName: "products.destination_link.create",
356
+ });
357
+ await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "destination" });
358
+ return c.json({ data: row }, 201);
359
+ })
360
+ .delete("/:id/destinations/:destinationId", async (c) => {
361
+ const productId = c.req.param("id");
362
+ const row = await productsService.removeProductDestination(c.get("db"), productId, c.req.param("destinationId"));
363
+ if (!row) {
364
+ return c.json({ error: "Product destination link not found" }, 404);
365
+ }
366
+ await appendProductMutationLedgerEntry(c, {
367
+ action: "delete",
368
+ productId,
369
+ changedFields: [],
370
+ subject: "product destination link",
371
+ actionName: "product.destination_link.delete",
372
+ routeOrToolName: "products.destination_link.delete",
373
+ });
374
+ await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "destination" });
375
+ return c.json({ success: true }, 200);
376
+ });