@voyantjs/products 0.52.1 → 0.52.3

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 (73) 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/catalog-policy.d.ts.map +1 -1
  9. package/dist/catalog-policy.js +18 -0
  10. package/dist/content-shape.d.ts +2 -0
  11. package/dist/content-shape.d.ts.map +1 -1
  12. package/dist/content-shape.js +2 -0
  13. package/dist/events.d.ts +1 -1
  14. package/dist/events.d.ts.map +1 -1
  15. package/dist/route-env.d.ts +22 -0
  16. package/dist/route-env.d.ts.map +1 -0
  17. package/dist/route-env.js +1 -0
  18. package/dist/routes-associations.d.ts +164 -0
  19. package/dist/routes-associations.d.ts.map +1 -0
  20. package/dist/routes-associations.js +100 -0
  21. package/dist/routes-catalog.d.ts +436 -0
  22. package/dist/routes-catalog.d.ts.map +1 -0
  23. package/dist/routes-catalog.js +104 -0
  24. package/dist/routes-configuration.d.ts +773 -0
  25. package/dist/routes-configuration.d.ts.map +1 -0
  26. package/dist/routes-configuration.js +364 -0
  27. package/dist/routes-core.d.ts +302 -0
  28. package/dist/routes-core.d.ts.map +1 -0
  29. package/dist/routes-core.js +79 -0
  30. package/dist/routes-itinerary.d.ts +614 -0
  31. package/dist/routes-itinerary.d.ts.map +1 -0
  32. package/dist/routes-itinerary.js +309 -0
  33. package/dist/routes-maintenance.d.ts +32 -0
  34. package/dist/routes-maintenance.d.ts.map +1 -0
  35. package/dist/routes-maintenance.js +14 -0
  36. package/dist/routes-media.d.ts +634 -0
  37. package/dist/routes-media.d.ts.map +1 -0
  38. package/dist/routes-media.js +245 -0
  39. package/dist/routes-merchandising.d.ts +1108 -0
  40. package/dist/routes-merchandising.d.ts.map +1 -0
  41. package/dist/routes-merchandising.js +376 -0
  42. package/dist/routes-options.d.ts +363 -0
  43. package/dist/routes-options.d.ts.map +1 -0
  44. package/dist/routes-options.js +173 -0
  45. package/dist/routes-public.d.ts +4 -4
  46. package/dist/routes-translations.d.ts +477 -0
  47. package/dist/routes-translations.d.ts.map +1 -0
  48. package/dist/routes-translations.js +258 -0
  49. package/dist/routes.d.ts +417 -355
  50. package/dist/routes.d.ts.map +1 -1
  51. package/dist/routes.js +21 -1133
  52. package/dist/schema-core.d.ts +3 -3
  53. package/dist/schema-itinerary.d.ts +1 -1
  54. package/dist/schema-settings.d.ts +4 -4
  55. package/dist/service-catalog-plane.d.ts.map +1 -1
  56. package/dist/service-catalog-plane.js +48 -1
  57. package/dist/service-catalog.d.ts +2 -2
  58. package/dist/service-content-owned.d.ts.map +1 -1
  59. package/dist/service-content-owned.js +98 -4
  60. package/dist/service-public.d.ts +4 -4
  61. package/dist/service.d.ts +225 -97
  62. package/dist/service.d.ts.map +1 -1
  63. package/dist/service.js +91 -0
  64. package/dist/tasks/brochures.d.ts +1 -1
  65. package/dist/validation-catalog.d.ts +10 -10
  66. package/dist/validation-config.d.ts +17 -17
  67. package/dist/validation-content.d.ts +26 -26
  68. package/dist/validation-core.d.ts +46 -46
  69. package/dist/validation-core.d.ts.map +1 -1
  70. package/dist/validation-core.js +17 -1
  71. package/dist/validation-public.d.ts +25 -25
  72. package/dist/validation-shared.d.ts +11 -11
  73. package/package.json +13 -7
@@ -0,0 +1,245 @@
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 productMediaRoutes = new Hono()
8
+ // ==========================================================================
9
+ // Media
10
+ // ==========================================================================
11
+ // GET /media/:mediaId — Get single media item
12
+ .get("/media/:mediaId", async (c) => {
13
+ const row = await productsService.getMediaById(c.get("db"), c.req.param("mediaId"));
14
+ if (!row) {
15
+ return c.json({ error: "Media not found" }, 404);
16
+ }
17
+ return c.json({ data: row });
18
+ })
19
+ // PATCH /media/:mediaId — Update media metadata
20
+ .patch("/media/:mediaId", async (c) => {
21
+ const mediaId = c.req.param("mediaId");
22
+ const body = await parseJsonBody(c, validation.updateProductMediaSchema);
23
+ const before = await productsService.getMediaById(c.get("db"), mediaId);
24
+ if (!before) {
25
+ return c.json({ error: "Media not found" }, 404);
26
+ }
27
+ const row = await productsService.updateMedia(c.get("db"), mediaId, body);
28
+ if (!row) {
29
+ return c.json({ error: "Media not found" }, 404);
30
+ }
31
+ await appendProductMutationLedgerEntry(c, {
32
+ action: "update",
33
+ productId: row.productId,
34
+ changedFields: changedMutationFields(body, before, row),
35
+ subject: before.dayId ? "product day media" : "product media",
36
+ actionName: before.dayId ? "product.day_media.update" : "product.media.update",
37
+ routeOrToolName: before.dayId ? "products.day_media.update" : "products.media.update",
38
+ });
39
+ await emitProductContentChanged(c.get("eventBus"), { id: row.productId, axis: "media" });
40
+ return c.json({ data: row });
41
+ })
42
+ // PATCH /media/:mediaId/set-cover — Set as cover image
43
+ .patch("/media/:mediaId/set-cover", async (c) => {
44
+ const media = await productsService.getMediaById(c.get("db"), c.req.param("mediaId"));
45
+ if (!media) {
46
+ return c.json({ error: "Media not found" }, 404);
47
+ }
48
+ const row = await productsService.setCoverMedia(c.get("db"), media.productId, media.id, media.dayId);
49
+ if (!row) {
50
+ return c.json({ error: "Failed to set cover" }, 500);
51
+ }
52
+ await appendProductMutationLedgerEntry(c, {
53
+ action: "update",
54
+ productId: row.productId,
55
+ changedFields: ["isCover"],
56
+ subject: media.dayId ? "product day media" : "product media",
57
+ actionName: media.dayId ? "product.day_media.set_cover" : "product.media.set_cover",
58
+ routeOrToolName: media.dayId ? "products.day_media.set_cover" : "products.media.set_cover",
59
+ summary: "Set product media as cover",
60
+ });
61
+ await emitProductContentChanged(c.get("eventBus"), { id: row.productId, axis: "media" });
62
+ return c.json({ data: row });
63
+ })
64
+ // DELETE /media/:mediaId — Delete media
65
+ .delete("/media/:mediaId", async (c) => {
66
+ const mediaId = c.req.param("mediaId");
67
+ const before = await productsService.getMediaById(c.get("db"), mediaId);
68
+ if (!before) {
69
+ return c.json({ error: "Media not found" }, 404);
70
+ }
71
+ const row = await productsService.deleteMedia(c.get("db"), mediaId);
72
+ if (!row) {
73
+ return c.json({ error: "Media not found" }, 404);
74
+ }
75
+ await appendProductMutationLedgerEntry(c, {
76
+ action: "delete",
77
+ productId: before.productId,
78
+ changedFields: [],
79
+ subject: before.dayId ? "product day media" : "product media",
80
+ actionName: before.dayId ? "product.day_media.delete" : "product.media.delete",
81
+ routeOrToolName: before.dayId ? "products.day_media.delete" : "products.media.delete",
82
+ });
83
+ await emitProductContentChanged(c.get("eventBus"), { id: before.productId, axis: "media" });
84
+ return c.json({ data: row });
85
+ })
86
+ // GET /:id/brochure — Get canonical brochure for product
87
+ .get("/:id/brochure", async (c) => {
88
+ const row = await productsService.getBrochure(c.get("db"), c.req.param("id"));
89
+ if (!row) {
90
+ return c.json({ error: "Product brochure not found" }, 404);
91
+ }
92
+ return c.json({ data: row });
93
+ })
94
+ // GET /:id/brochure/versions — List brochure history for product
95
+ .get("/:id/brochure/versions", async (c) => {
96
+ return c.json({ data: await productsService.listBrochures(c.get("db"), c.req.param("id")) });
97
+ })
98
+ // PUT /:id/brochure — Upsert canonical brochure for product
99
+ .put("/:id/brochure", async (c) => {
100
+ const productId = c.req.param("id");
101
+ const body = await parseJsonBody(c, validation.upsertProductBrochureSchema);
102
+ const row = await productsService.upsertBrochure(c.get("db"), productId, body);
103
+ if (!row) {
104
+ return c.json({ error: "Product not found" }, 404);
105
+ }
106
+ await appendProductMutationLedgerEntry(c, {
107
+ action: "create",
108
+ productId,
109
+ changedFields: changedMutationFields(body, null, row),
110
+ subject: "product brochure version",
111
+ actionName: "product.brochure.create",
112
+ routeOrToolName: "products.brochure.create",
113
+ });
114
+ await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "media" });
115
+ return c.json({ data: row }, 201);
116
+ })
117
+ // DELETE /:id/brochure — Delete canonical brochure for product
118
+ .delete("/:id/brochure", async (c) => {
119
+ const productId = c.req.param("id");
120
+ const row = await productsService.deleteBrochure(c.get("db"), productId);
121
+ if (!row) {
122
+ return c.json({ error: "Product brochure not found" }, 404);
123
+ }
124
+ await appendProductMutationLedgerEntry(c, {
125
+ action: "delete",
126
+ productId,
127
+ changedFields: [],
128
+ subject: "product brochure",
129
+ actionName: "product.brochure.delete_current",
130
+ routeOrToolName: "products.brochure.delete_current",
131
+ });
132
+ await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "media" });
133
+ return c.json({ data: row });
134
+ })
135
+ // POST /:id/brochure/versions/:brochureId/set-current — Promote brochure version
136
+ .post("/:id/brochure/versions/:brochureId/set-current", async (c) => {
137
+ const productId = c.req.param("id");
138
+ const row = await productsService.setCurrentBrochure(c.get("db"), productId, c.req.param("brochureId"));
139
+ if (!row) {
140
+ return c.json({ error: "Product brochure version not found" }, 404);
141
+ }
142
+ await appendProductMutationLedgerEntry(c, {
143
+ action: "update",
144
+ productId,
145
+ changedFields: ["isBrochureCurrent"],
146
+ subject: "product brochure version",
147
+ actionName: "product.brochure.set_current",
148
+ routeOrToolName: "products.brochure.set_current",
149
+ summary: "Set current product brochure version",
150
+ });
151
+ await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "media" });
152
+ return c.json({ data: row });
153
+ })
154
+ // DELETE /:id/brochure/versions/:brochureId — Delete brochure version
155
+ .delete("/:id/brochure/versions/:brochureId", async (c) => {
156
+ const productId = c.req.param("id");
157
+ const row = await productsService.deleteBrochureVersion(c.get("db"), productId, c.req.param("brochureId"));
158
+ if (!row) {
159
+ return c.json({ error: "Product brochure version not found" }, 404);
160
+ }
161
+ await appendProductMutationLedgerEntry(c, {
162
+ action: "delete",
163
+ productId,
164
+ changedFields: [],
165
+ subject: "product brochure version",
166
+ actionName: "product.brochure.delete_version",
167
+ routeOrToolName: "products.brochure.delete_version",
168
+ });
169
+ await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "media" });
170
+ return c.json({ data: row });
171
+ })
172
+ // ==========================================================================
173
+ // Product Media (nested under product)
174
+ // ==========================================================================
175
+ // GET /:id/media — List product-level media
176
+ .get("/:id/media", async (c) => {
177
+ const query = parseQuery(c, validation.productMediaListQuerySchema);
178
+ return c.json(await productsService.listProductLevelMedia(c.get("db"), c.req.param("id"), query));
179
+ })
180
+ // POST /:id/media — Create media for product
181
+ .post("/:id/media", async (c) => {
182
+ const productId = c.req.param("id");
183
+ const body = await parseJsonBody(c, validation.insertProductMediaSchema);
184
+ const row = await productsService.createMedia(c.get("db"), productId, body);
185
+ if (!row) {
186
+ return c.json({ error: "Product not found or invalid dayId" }, 404);
187
+ }
188
+ await appendProductMutationLedgerEntry(c, {
189
+ action: "create",
190
+ productId,
191
+ changedFields: changedMutationFields(body, null, row),
192
+ subject: "product media",
193
+ actionName: "product.media.create",
194
+ routeOrToolName: "products.media.create",
195
+ });
196
+ await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "media" });
197
+ return c.json({ data: row }, 201);
198
+ })
199
+ // POST /:id/media/reorder — Batch reorder media
200
+ .post("/:id/media/reorder", async (c) => {
201
+ const productId = c.req.param("id");
202
+ const data = await parseJsonBody(c, validation.reorderProductMediaSchema);
203
+ const results = await productsService.reorderMedia(c.get("db"), data);
204
+ await appendProductMutationLedgerEntry(c, {
205
+ action: "update",
206
+ productId,
207
+ changedFields: ["sortOrder"],
208
+ subject: "product media order",
209
+ actionName: "product.media.reorder",
210
+ routeOrToolName: "products.media.reorder",
211
+ summary: `Reordered ${results.length} product media items`,
212
+ });
213
+ await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "media" });
214
+ return c.json({ data: results });
215
+ })
216
+ // GET /:id/days/:dayId/media — List day media
217
+ .get("/:id/days/:dayId/media", async (c) => {
218
+ const query = parseQuery(c, validation.productMediaListQuerySchema);
219
+ return c.json(await productsService.listMedia(c.get("db"), c.req.param("id"), {
220
+ ...query,
221
+ dayId: c.req.param("dayId"),
222
+ }));
223
+ })
224
+ // POST /:id/days/:dayId/media — Create day media
225
+ .post("/:id/days/:dayId/media", async (c) => {
226
+ const productId = c.req.param("id");
227
+ const body = await parseJsonBody(c, validation.insertProductMediaSchema);
228
+ const row = await productsService.createMedia(c.get("db"), productId, {
229
+ ...body,
230
+ dayId: c.req.param("dayId"),
231
+ });
232
+ if (!row) {
233
+ return c.json({ error: "Product or day not found" }, 404);
234
+ }
235
+ await appendProductMutationLedgerEntry(c, {
236
+ action: "create",
237
+ productId,
238
+ changedFields: changedMutationFields(body, null, row),
239
+ subject: "product day media",
240
+ actionName: "product.day_media.create",
241
+ routeOrToolName: "products.day_media.create",
242
+ });
243
+ await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "media" });
244
+ return c.json({ data: row }, 201);
245
+ });