@voyantjs/products 0.19.0 → 0.21.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.
- package/dist/booking-engine/handler.d.ts +203 -0
- package/dist/booking-engine/handler.d.ts.map +1 -0
- package/dist/booking-engine/handler.js +330 -0
- package/dist/booking-engine/index.d.ts +8 -0
- package/dist/booking-engine/index.d.ts.map +1 -0
- package/dist/booking-engine/index.js +7 -0
- package/dist/catalog-policy.d.ts +33 -0
- package/dist/catalog-policy.d.ts.map +1 -0
- package/dist/catalog-policy.js +421 -0
- package/dist/content-shape.d.ts +217 -0
- package/dist/content-shape.d.ts.map +1 -0
- package/dist/content-shape.js +159 -0
- package/dist/draft-shape.d.ts +43 -0
- package/dist/draft-shape.d.ts.map +1 -0
- package/dist/draft-shape.js +46 -0
- package/dist/events.d.ts +37 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +32 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/routes-content.d.ts +74 -0
- package/dist/routes-content.d.ts.map +1 -0
- package/dist/routes-content.js +117 -0
- package/dist/routes.d.ts +47 -26
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +88 -16
- package/dist/schema-core.d.ts +240 -1
- package/dist/schema-core.d.ts.map +1 -1
- package/dist/schema-core.js +49 -0
- package/dist/schema-itinerary.d.ts +18 -1
- package/dist/schema-itinerary.d.ts.map +1 -1
- package/dist/schema-itinerary.js +1 -0
- package/dist/schema-settings.d.ts +1 -1
- package/dist/schema-sourced-content.d.ts +262 -0
- package/dist/schema-sourced-content.d.ts.map +1 -0
- package/dist/schema-sourced-content.js +69 -0
- package/dist/schema-taxonomy.d.ts +17 -0
- package/dist/schema-taxonomy.d.ts.map +1 -1
- package/dist/schema-taxonomy.js +13 -0
- package/dist/schema.d.ts +1 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +1 -0
- package/dist/service-catalog-plane.d.ts +129 -0
- package/dist/service-catalog-plane.d.ts.map +1 -0
- package/dist/service-catalog-plane.js +212 -0
- package/dist/service-content-owned.d.ts +68 -0
- package/dist/service-content-owned.d.ts.map +1 -0
- package/dist/service-content-owned.js +224 -0
- package/dist/service-content-synthesizer.d.ts +90 -0
- package/dist/service-content-synthesizer.d.ts.map +1 -0
- package/dist/service-content-synthesizer.js +171 -0
- package/dist/service-content.d.ts +106 -0
- package/dist/service-content.d.ts.map +1 -0
- package/dist/service-content.js +365 -0
- package/dist/service.d.ts +82 -28
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +4 -0
- package/dist/tasks/brochures.d.ts +2 -1
- package/dist/tasks/brochures.d.ts.map +1 -1
- package/dist/tasks/brochures.js +3 -0
- package/dist/validation-catalog.d.ts +4 -4
- package/dist/validation-config.d.ts +3 -3
- package/dist/validation-content.d.ts +34 -4
- package/dist/validation-content.d.ts.map +1 -1
- package/dist/validation-content.js +13 -0
- package/dist/validation-core.d.ts +53 -3
- package/dist/validation-core.d.ts.map +1 -1
- package/dist/validation-core.js +16 -0
- package/dist/validation-public.d.ts +9 -9
- package/dist/validation-shared.d.ts +4 -4
- package/package.json +12 -6
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Catalog plane field policy for `packages/products`.
|
|
3
|
+
*
|
|
4
|
+
* Declares every product field's governance under the
|
|
5
|
+
* `@voyantjs/catalog` 12-attribute contract. Phase B shake-out
|
|
6
|
+
* adoption — see `docs/architecture/catalog-architecture.md` §9.1.
|
|
7
|
+
*
|
|
8
|
+
* Scope of this file:
|
|
9
|
+
* - The root `products` table (from `schema-core.ts`).
|
|
10
|
+
* - Provenance + identity fields the catalog plane needs to track.
|
|
11
|
+
*
|
|
12
|
+
* Out of scope (deferred to follow-up adoption passes):
|
|
13
|
+
* - `productOptions`, `optionUnits`, `productDays`, `productNotes`,
|
|
14
|
+
* `productVersions` — promoted child entities (per composition rule §6.2);
|
|
15
|
+
* each gets its own micro-registry when wired in.
|
|
16
|
+
* - The split of `tags` into `marketing_tags` + `facet_tags` per the
|
|
17
|
+
* human-readable / machine-evaluable rule (§7.1). Today's schema has a
|
|
18
|
+
* single `tags` column; declared here as merchandisable with a TODO.
|
|
19
|
+
*/
|
|
20
|
+
import { defineFieldPolicy } from "@voyantjs/catalog/contract";
|
|
21
|
+
/**
|
|
22
|
+
* Field-policy declarations for `products`. Pass through `defineFieldPolicy`
|
|
23
|
+
* to apply inheritance and produce the runtime registry.
|
|
24
|
+
*/
|
|
25
|
+
const PRODUCT_FIELD_POLICY = [
|
|
26
|
+
// ── Source pointer / provenance ─────────────────────────────────────────
|
|
27
|
+
// These are not columns on the `products` table; they live on the parallel
|
|
28
|
+
// catalog Provenance row. Declared here so the indexer / overlay resolver
|
|
29
|
+
// know how to treat them.
|
|
30
|
+
{
|
|
31
|
+
path: "source.kind",
|
|
32
|
+
class: "managed",
|
|
33
|
+
merge: "source-only",
|
|
34
|
+
drift: "critical",
|
|
35
|
+
reindex: "facet-affecting",
|
|
36
|
+
snapshot: "on-book",
|
|
37
|
+
query: "indexed-column",
|
|
38
|
+
localized: false,
|
|
39
|
+
visibility: ["staff"],
|
|
40
|
+
editRole: "none",
|
|
41
|
+
overrideFriction: "none",
|
|
42
|
+
sourceFreshness: "sync",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
path: "source.ref",
|
|
46
|
+
class: "managed",
|
|
47
|
+
merge: "source-only",
|
|
48
|
+
drift: "critical",
|
|
49
|
+
reindex: "none",
|
|
50
|
+
snapshot: "on-book",
|
|
51
|
+
query: "indexed-column",
|
|
52
|
+
localized: false,
|
|
53
|
+
visibility: ["staff"],
|
|
54
|
+
editRole: "none",
|
|
55
|
+
overrideFriction: "none",
|
|
56
|
+
sourceFreshness: "sync",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
path: "seller.operator_id",
|
|
60
|
+
class: "managed",
|
|
61
|
+
merge: "source-only",
|
|
62
|
+
drift: "critical",
|
|
63
|
+
reindex: "none",
|
|
64
|
+
snapshot: "on-book",
|
|
65
|
+
query: "indexed-column",
|
|
66
|
+
localized: false,
|
|
67
|
+
visibility: ["staff"],
|
|
68
|
+
editRole: "none",
|
|
69
|
+
overrideFriction: "none",
|
|
70
|
+
sourceFreshness: "static",
|
|
71
|
+
},
|
|
72
|
+
// ── Identity / lifecycle ────────────────────────────────────────────────
|
|
73
|
+
{
|
|
74
|
+
path: "id",
|
|
75
|
+
class: "managed",
|
|
76
|
+
merge: "source-only",
|
|
77
|
+
drift: "critical",
|
|
78
|
+
reindex: "none",
|
|
79
|
+
snapshot: "on-book",
|
|
80
|
+
query: "first-class-table",
|
|
81
|
+
localized: false,
|
|
82
|
+
visibility: ["staff", "customer", "partner"],
|
|
83
|
+
editRole: "none",
|
|
84
|
+
overrideFriction: "none",
|
|
85
|
+
sourceFreshness: "static",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
path: "createdAt",
|
|
89
|
+
class: "managed",
|
|
90
|
+
merge: "source-only",
|
|
91
|
+
drift: "none",
|
|
92
|
+
reindex: "none",
|
|
93
|
+
snapshot: "on-book",
|
|
94
|
+
query: "indexed-column",
|
|
95
|
+
localized: false,
|
|
96
|
+
visibility: ["staff"],
|
|
97
|
+
editRole: "none",
|
|
98
|
+
overrideFriction: "none",
|
|
99
|
+
sourceFreshness: "static",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
path: "updatedAt",
|
|
103
|
+
class: "managed",
|
|
104
|
+
merge: "source-only",
|
|
105
|
+
drift: "none",
|
|
106
|
+
reindex: "none",
|
|
107
|
+
snapshot: "never",
|
|
108
|
+
query: "indexed-column",
|
|
109
|
+
localized: false,
|
|
110
|
+
visibility: ["staff"],
|
|
111
|
+
editRole: "none",
|
|
112
|
+
overrideFriction: "none",
|
|
113
|
+
sourceFreshness: "sync",
|
|
114
|
+
},
|
|
115
|
+
// ── Merchandisable / marketing ──────────────────────────────────────────
|
|
116
|
+
// Note: `name` maps to "title" in catalog vocabulary. The existing schema
|
|
117
|
+
// column stays `name` for backwards compatibility; the field-policy path
|
|
118
|
+
// uses the schema column name so the indexer can map it directly.
|
|
119
|
+
{
|
|
120
|
+
path: "name",
|
|
121
|
+
class: "merchandisable",
|
|
122
|
+
merge: "replace",
|
|
123
|
+
drift: "medium",
|
|
124
|
+
reindex: "entry-locale",
|
|
125
|
+
snapshot: "on-book",
|
|
126
|
+
query: "indexed-column",
|
|
127
|
+
localized: true,
|
|
128
|
+
visibility: ["staff", "customer", "partner"],
|
|
129
|
+
editRole: "marketing",
|
|
130
|
+
overrideFriction: "none",
|
|
131
|
+
sourceFreshness: "sync",
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
path: "description",
|
|
135
|
+
class: "merchandisable",
|
|
136
|
+
merge: "replace",
|
|
137
|
+
drift: "low",
|
|
138
|
+
reindex: "entry-locale",
|
|
139
|
+
snapshot: "on-book",
|
|
140
|
+
query: "blob-only",
|
|
141
|
+
localized: true,
|
|
142
|
+
visibility: ["staff", "customer", "partner"],
|
|
143
|
+
editRole: "marketing",
|
|
144
|
+
overrideFriction: "none",
|
|
145
|
+
sourceFreshness: "sync",
|
|
146
|
+
},
|
|
147
|
+
// TODO(catalog): split into marketing_tags + facet_tags per architecture
|
|
148
|
+
// §7.1 (human-readable + machine-evaluable rule). Today's schema has one
|
|
149
|
+
// `tags` column; declared here as merchandisable + additive-set so
|
|
150
|
+
// marketing can extend the source set without trampling it. Until the
|
|
151
|
+
// split lands, search-facet leakage is possible (marketing edits affect
|
|
152
|
+
// search facets); flag for follow-up.
|
|
153
|
+
{
|
|
154
|
+
path: "tags[]",
|
|
155
|
+
class: "merchandisable",
|
|
156
|
+
merge: "additive-set",
|
|
157
|
+
drift: "low",
|
|
158
|
+
reindex: "entry",
|
|
159
|
+
snapshot: "on-book",
|
|
160
|
+
query: "indexed-column",
|
|
161
|
+
localized: false,
|
|
162
|
+
visibility: ["staff", "customer", "partner"],
|
|
163
|
+
editRole: "marketing",
|
|
164
|
+
overrideFriction: "none",
|
|
165
|
+
sourceFreshness: "sync",
|
|
166
|
+
},
|
|
167
|
+
// ── Structural / facet-affecting ───────────────────────────────────────
|
|
168
|
+
{
|
|
169
|
+
path: "status",
|
|
170
|
+
class: "structural",
|
|
171
|
+
merge: "source-only",
|
|
172
|
+
drift: "high",
|
|
173
|
+
reindex: "facet-affecting",
|
|
174
|
+
snapshot: "on-book",
|
|
175
|
+
query: "indexed-column",
|
|
176
|
+
localized: false,
|
|
177
|
+
visibility: ["staff"],
|
|
178
|
+
editRole: "none",
|
|
179
|
+
overrideFriction: "none",
|
|
180
|
+
sourceFreshness: "sync",
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
path: "bookingMode",
|
|
184
|
+
class: "structural",
|
|
185
|
+
merge: "source-only",
|
|
186
|
+
drift: "high",
|
|
187
|
+
reindex: "facet-affecting",
|
|
188
|
+
snapshot: "on-book",
|
|
189
|
+
query: "indexed-column",
|
|
190
|
+
localized: false,
|
|
191
|
+
visibility: ["staff", "customer", "partner"],
|
|
192
|
+
editRole: "none",
|
|
193
|
+
overrideFriction: "none",
|
|
194
|
+
sourceFreshness: "sync",
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
path: "capacityMode",
|
|
198
|
+
class: "structural",
|
|
199
|
+
merge: "source-only",
|
|
200
|
+
drift: "medium",
|
|
201
|
+
reindex: "entry",
|
|
202
|
+
snapshot: "on-book",
|
|
203
|
+
query: "indexed-column",
|
|
204
|
+
localized: false,
|
|
205
|
+
visibility: ["staff"],
|
|
206
|
+
editRole: "none",
|
|
207
|
+
overrideFriction: "none",
|
|
208
|
+
sourceFreshness: "sync",
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
// The `visibility` *column* on the products table — distinct from the
|
|
212
|
+
// catalog plane's audience-visibility axis.
|
|
213
|
+
path: "visibility",
|
|
214
|
+
class: "structural",
|
|
215
|
+
merge: "source-only",
|
|
216
|
+
drift: "high",
|
|
217
|
+
reindex: "facet-affecting",
|
|
218
|
+
snapshot: "on-book",
|
|
219
|
+
query: "indexed-column",
|
|
220
|
+
localized: false,
|
|
221
|
+
visibility: ["staff"],
|
|
222
|
+
editRole: "none",
|
|
223
|
+
overrideFriction: "none",
|
|
224
|
+
sourceFreshness: "sync",
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
path: "activated",
|
|
228
|
+
class: "structural",
|
|
229
|
+
merge: "source-only",
|
|
230
|
+
drift: "high",
|
|
231
|
+
reindex: "facet-affecting",
|
|
232
|
+
snapshot: "on-book",
|
|
233
|
+
query: "indexed-column",
|
|
234
|
+
localized: false,
|
|
235
|
+
visibility: ["staff"],
|
|
236
|
+
editRole: "none",
|
|
237
|
+
overrideFriction: "none",
|
|
238
|
+
sourceFreshness: "sync",
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
path: "productTypeId",
|
|
242
|
+
class: "structural",
|
|
243
|
+
merge: "source-only",
|
|
244
|
+
drift: "medium",
|
|
245
|
+
reindex: "facet-affecting",
|
|
246
|
+
snapshot: "on-book",
|
|
247
|
+
query: "indexed-column",
|
|
248
|
+
localized: false,
|
|
249
|
+
visibility: ["staff", "customer", "partner"],
|
|
250
|
+
editRole: "none",
|
|
251
|
+
overrideFriction: "none",
|
|
252
|
+
sourceFreshness: "sync",
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
path: "facilityId",
|
|
256
|
+
class: "structural",
|
|
257
|
+
merge: "source-only",
|
|
258
|
+
drift: "medium",
|
|
259
|
+
reindex: "entry",
|
|
260
|
+
snapshot: "on-book",
|
|
261
|
+
query: "indexed-column",
|
|
262
|
+
localized: false,
|
|
263
|
+
visibility: ["staff"],
|
|
264
|
+
editRole: "none",
|
|
265
|
+
overrideFriction: "none",
|
|
266
|
+
sourceFreshness: "sync",
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
path: "supplierId",
|
|
270
|
+
class: "structural",
|
|
271
|
+
merge: "source-only",
|
|
272
|
+
drift: "high",
|
|
273
|
+
reindex: "facet-affecting",
|
|
274
|
+
snapshot: "on-book",
|
|
275
|
+
query: "indexed-column",
|
|
276
|
+
localized: false,
|
|
277
|
+
visibility: ["staff"],
|
|
278
|
+
editRole: "none",
|
|
279
|
+
overrideFriction: "none",
|
|
280
|
+
sourceFreshness: "sync",
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
path: "pax",
|
|
284
|
+
class: "structural",
|
|
285
|
+
merge: "source-only",
|
|
286
|
+
drift: "medium",
|
|
287
|
+
reindex: "entry",
|
|
288
|
+
snapshot: "on-book",
|
|
289
|
+
query: "indexed-column",
|
|
290
|
+
localized: false,
|
|
291
|
+
visibility: ["staff", "customer", "partner"],
|
|
292
|
+
editRole: "none",
|
|
293
|
+
overrideFriction: "none",
|
|
294
|
+
sourceFreshness: "sync",
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
path: "startDate",
|
|
298
|
+
class: "structural",
|
|
299
|
+
merge: "source-only",
|
|
300
|
+
drift: "medium",
|
|
301
|
+
reindex: "facet-affecting",
|
|
302
|
+
snapshot: "on-book",
|
|
303
|
+
query: "indexed-column",
|
|
304
|
+
localized: false,
|
|
305
|
+
visibility: ["staff", "customer", "partner"],
|
|
306
|
+
editRole: "none",
|
|
307
|
+
overrideFriction: "none",
|
|
308
|
+
sourceFreshness: "sync",
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
path: "endDate",
|
|
312
|
+
class: "structural",
|
|
313
|
+
merge: "source-only",
|
|
314
|
+
drift: "medium",
|
|
315
|
+
reindex: "facet-affecting",
|
|
316
|
+
snapshot: "on-book",
|
|
317
|
+
query: "indexed-column",
|
|
318
|
+
localized: false,
|
|
319
|
+
visibility: ["staff", "customer", "partner"],
|
|
320
|
+
editRole: "none",
|
|
321
|
+
overrideFriction: "none",
|
|
322
|
+
sourceFreshness: "sync",
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
path: "timezone",
|
|
326
|
+
class: "managed",
|
|
327
|
+
merge: "source-only",
|
|
328
|
+
drift: "low",
|
|
329
|
+
reindex: "none",
|
|
330
|
+
snapshot: "on-book",
|
|
331
|
+
query: "blob-only",
|
|
332
|
+
localized: false,
|
|
333
|
+
visibility: ["staff", "customer", "partner"],
|
|
334
|
+
editRole: "none",
|
|
335
|
+
overrideFriction: "none",
|
|
336
|
+
sourceFreshness: "sync",
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
path: "reservationTimeoutMinutes",
|
|
340
|
+
class: "structural",
|
|
341
|
+
merge: "source-only",
|
|
342
|
+
drift: "low",
|
|
343
|
+
reindex: "none",
|
|
344
|
+
snapshot: "never",
|
|
345
|
+
query: "blob-only",
|
|
346
|
+
localized: false,
|
|
347
|
+
visibility: ["staff"],
|
|
348
|
+
editRole: "none",
|
|
349
|
+
overrideFriction: "none",
|
|
350
|
+
sourceFreshness: "sync",
|
|
351
|
+
},
|
|
352
|
+
// ── Pricing (configured defaults — not the live quote) ──────────────────
|
|
353
|
+
// These are the operator's configured prices on the product. The live
|
|
354
|
+
// quote engine resolves volatile-live `quote_price` separately at quote
|
|
355
|
+
// time and is captured at booking commit (snapshot mode handled by the
|
|
356
|
+
// pricing module's own field policy when it adopts).
|
|
357
|
+
{
|
|
358
|
+
path: "sellAmountCents",
|
|
359
|
+
class: "structural",
|
|
360
|
+
merge: "source-only",
|
|
361
|
+
drift: "high",
|
|
362
|
+
reindex: "entry",
|
|
363
|
+
snapshot: "on-quote-and-book",
|
|
364
|
+
query: "indexed-column",
|
|
365
|
+
localized: false,
|
|
366
|
+
visibility: ["staff", "customer", "partner"],
|
|
367
|
+
editRole: "none",
|
|
368
|
+
overrideFriction: "none",
|
|
369
|
+
sourceFreshness: "sync",
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
path: "sellCurrency",
|
|
373
|
+
class: "managed",
|
|
374
|
+
merge: "source-only",
|
|
375
|
+
drift: "high",
|
|
376
|
+
reindex: "entry",
|
|
377
|
+
snapshot: "on-quote-and-book",
|
|
378
|
+
query: "indexed-column",
|
|
379
|
+
localized: false,
|
|
380
|
+
visibility: ["staff", "customer", "partner"],
|
|
381
|
+
editRole: "none",
|
|
382
|
+
overrideFriction: "none",
|
|
383
|
+
sourceFreshness: "sync",
|
|
384
|
+
},
|
|
385
|
+
// ── Internal / staff-only ──────────────────────────────────────────────
|
|
386
|
+
{
|
|
387
|
+
path: "costAmountCents",
|
|
388
|
+
class: "managed",
|
|
389
|
+
merge: "source-only",
|
|
390
|
+
drift: "medium",
|
|
391
|
+
reindex: "none",
|
|
392
|
+
snapshot: "never",
|
|
393
|
+
query: "blob-only",
|
|
394
|
+
localized: false,
|
|
395
|
+
visibility: ["staff"],
|
|
396
|
+
editRole: "none",
|
|
397
|
+
overrideFriction: "none",
|
|
398
|
+
sourceFreshness: "sync",
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
path: "marginPercent",
|
|
402
|
+
class: "managed",
|
|
403
|
+
merge: "source-only",
|
|
404
|
+
drift: "medium",
|
|
405
|
+
reindex: "none",
|
|
406
|
+
snapshot: "never",
|
|
407
|
+
query: "blob-only",
|
|
408
|
+
localized: false,
|
|
409
|
+
visibility: ["staff"],
|
|
410
|
+
editRole: "none",
|
|
411
|
+
overrideFriction: "none",
|
|
412
|
+
sourceFreshness: "sync",
|
|
413
|
+
},
|
|
414
|
+
];
|
|
415
|
+
/**
|
|
416
|
+
* Resolved field-policy registry for products. Verticals adopt the catalog
|
|
417
|
+
* plane by exporting this; templates wire it into the indexer, overlay
|
|
418
|
+
* resolver, and snapshot capture pipeline.
|
|
419
|
+
*/
|
|
420
|
+
export const productCatalogPolicy = defineFieldPolicy(PRODUCT_FIELD_POLICY);
|
|
421
|
+
export { PRODUCT_FIELD_POLICY };
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Products content shape — the rich detail-page content shape returned
|
|
3
|
+
* by `getContent` and stored in `products_sourced_content.payload`.
|
|
4
|
+
*
|
|
5
|
+
* Schema versions are managed by this module: the constant
|
|
6
|
+
* `PRODUCTS_CONTENT_SCHEMA_VERSION` stamps every cache write; reads
|
|
7
|
+
* skip rows with an unrecognized version (treated as cache miss). Bump
|
|
8
|
+
* the version when the shape changes; old cache rows are then evicted
|
|
9
|
+
* by a single `DELETE WHERE content_schema_version != current`.
|
|
10
|
+
*
|
|
11
|
+
* Pure types + Zod + a vertical-specific `mergeOverlaysIntoProductContent`
|
|
12
|
+
* that wraps the catalog plane's content-shape-aware merger with this
|
|
13
|
+
* vertical's validator.
|
|
14
|
+
*
|
|
15
|
+
* See `docs/architecture/catalog-sourced-content.md` §3.2, §3.5.4, §3.6.
|
|
16
|
+
*/
|
|
17
|
+
import { type ContentOverlay, type MergeOverlaysOptions } from "@voyantjs/catalog";
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
/**
|
|
20
|
+
* The current content-schema version. Stamped on every cache write.
|
|
21
|
+
* Bump when the `productContentSchema` shape changes incompatibly.
|
|
22
|
+
*/
|
|
23
|
+
export declare const PRODUCTS_CONTENT_SCHEMA_VERSION = "products/v1";
|
|
24
|
+
/**
|
|
25
|
+
* Top-level product summary fields. Maps loosely to the owned `products`
|
|
26
|
+
* table — the read service synthesizes from indexed projection + overlay
|
|
27
|
+
* for thin adapters, or stores adapter-served data for rich ones.
|
|
28
|
+
*/
|
|
29
|
+
export declare const productSummarySchema: z.ZodObject<{
|
|
30
|
+
id: z.ZodString;
|
|
31
|
+
name: z.ZodString;
|
|
32
|
+
status: z.ZodOptional<z.ZodString>;
|
|
33
|
+
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
34
|
+
highlights: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
35
|
+
hero_image_url: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
36
|
+
duration_days: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
37
|
+
start_date: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
38
|
+
end_date: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
39
|
+
sell_currency: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
40
|
+
supplier: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
41
|
+
country: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
42
|
+
departure_city: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
43
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
44
|
+
}, z.core.$strip>;
|
|
45
|
+
export declare const productMediaItemSchema: z.ZodObject<{
|
|
46
|
+
url: z.ZodString;
|
|
47
|
+
type: z.ZodDefault<z.ZodEnum<{
|
|
48
|
+
image: "image";
|
|
49
|
+
video: "video";
|
|
50
|
+
document: "document";
|
|
51
|
+
}>>;
|
|
52
|
+
caption: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
53
|
+
alt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
54
|
+
}, z.core.$strip>;
|
|
55
|
+
export declare const productOptionUnitSchema: z.ZodObject<{
|
|
56
|
+
id: z.ZodString;
|
|
57
|
+
type: z.ZodString;
|
|
58
|
+
label: z.ZodOptional<z.ZodString>;
|
|
59
|
+
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
60
|
+
capacity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
61
|
+
}, z.core.$strip>;
|
|
62
|
+
export declare const productOptionSchema: z.ZodObject<{
|
|
63
|
+
id: z.ZodString;
|
|
64
|
+
name: z.ZodString;
|
|
65
|
+
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
66
|
+
units: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
67
|
+
id: z.ZodString;
|
|
68
|
+
type: z.ZodString;
|
|
69
|
+
label: z.ZodOptional<z.ZodString>;
|
|
70
|
+
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
71
|
+
capacity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
72
|
+
}, z.core.$strip>>>>;
|
|
73
|
+
inclusions: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
74
|
+
}, z.core.$strip>;
|
|
75
|
+
export declare const productDaySchema: z.ZodObject<{
|
|
76
|
+
day_number: z.ZodNumber;
|
|
77
|
+
title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
78
|
+
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
79
|
+
location: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
80
|
+
services: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
81
|
+
}, z.core.$strip>;
|
|
82
|
+
export declare const productPolicySchema: z.ZodObject<{
|
|
83
|
+
kind: z.ZodEnum<{
|
|
84
|
+
supplier_notes: "supplier_notes";
|
|
85
|
+
cancellation: "cancellation";
|
|
86
|
+
payment: "payment";
|
|
87
|
+
requirements: "requirements";
|
|
88
|
+
}>;
|
|
89
|
+
body: z.ZodString;
|
|
90
|
+
rules: z.ZodOptional<z.ZodUnknown>;
|
|
91
|
+
}, z.core.$strip>;
|
|
92
|
+
/**
|
|
93
|
+
* A single bookable departure / time slot — the "when" surface of the
|
|
94
|
+
* product. ISO 8601 timestamps for `starts_at` / `ends_at` so locale
|
|
95
|
+
* formatting happens at render time, never in the cache.
|
|
96
|
+
*
|
|
97
|
+
* Owned products derive these from `availability_slots`; sourced
|
|
98
|
+
* adapters return them via `getContent`. Empty array = "always-on"
|
|
99
|
+
* product (e.g. an evergreen transfer service) or one whose schedule
|
|
100
|
+
* is on-request.
|
|
101
|
+
*/
|
|
102
|
+
export declare const productDepartureSchema: z.ZodObject<{
|
|
103
|
+
id: z.ZodString;
|
|
104
|
+
starts_at: z.ZodString;
|
|
105
|
+
ends_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
106
|
+
status: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
107
|
+
capacity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
108
|
+
remaining: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
109
|
+
lowest_price_cents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
110
|
+
currency: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
111
|
+
note: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
112
|
+
}, z.core.$strip>;
|
|
113
|
+
/**
|
|
114
|
+
* The product content payload. Cache writes validate against this
|
|
115
|
+
* schema; cache reads skip rows that don't validate (treated as cache
|
|
116
|
+
* miss to surface adapter integration bugs without corrupting reads).
|
|
117
|
+
*/
|
|
118
|
+
export declare const productContentSchema: z.ZodObject<{
|
|
119
|
+
product: z.ZodObject<{
|
|
120
|
+
id: z.ZodString;
|
|
121
|
+
name: z.ZodString;
|
|
122
|
+
status: z.ZodOptional<z.ZodString>;
|
|
123
|
+
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
124
|
+
highlights: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
125
|
+
hero_image_url: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
126
|
+
duration_days: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
127
|
+
start_date: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
128
|
+
end_date: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
129
|
+
sell_currency: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
130
|
+
supplier: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
131
|
+
country: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
132
|
+
departure_city: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
133
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
134
|
+
}, z.core.$strip>;
|
|
135
|
+
options: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
136
|
+
id: z.ZodString;
|
|
137
|
+
name: z.ZodString;
|
|
138
|
+
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
139
|
+
units: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
140
|
+
id: z.ZodString;
|
|
141
|
+
type: z.ZodString;
|
|
142
|
+
label: z.ZodOptional<z.ZodString>;
|
|
143
|
+
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
144
|
+
capacity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
145
|
+
}, z.core.$strip>>>>;
|
|
146
|
+
inclusions: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
147
|
+
}, z.core.$strip>>>;
|
|
148
|
+
days: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
149
|
+
day_number: z.ZodNumber;
|
|
150
|
+
title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
151
|
+
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
152
|
+
location: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
153
|
+
services: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
154
|
+
}, z.core.$strip>>>;
|
|
155
|
+
media: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
156
|
+
url: z.ZodString;
|
|
157
|
+
type: z.ZodDefault<z.ZodEnum<{
|
|
158
|
+
image: "image";
|
|
159
|
+
video: "video";
|
|
160
|
+
document: "document";
|
|
161
|
+
}>>;
|
|
162
|
+
caption: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
163
|
+
alt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
164
|
+
}, z.core.$strip>>>;
|
|
165
|
+
policies: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
166
|
+
kind: z.ZodEnum<{
|
|
167
|
+
supplier_notes: "supplier_notes";
|
|
168
|
+
cancellation: "cancellation";
|
|
169
|
+
payment: "payment";
|
|
170
|
+
requirements: "requirements";
|
|
171
|
+
}>;
|
|
172
|
+
body: z.ZodString;
|
|
173
|
+
rules: z.ZodOptional<z.ZodUnknown>;
|
|
174
|
+
}, z.core.$strip>>>;
|
|
175
|
+
departures: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
176
|
+
id: z.ZodString;
|
|
177
|
+
starts_at: z.ZodString;
|
|
178
|
+
ends_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
179
|
+
status: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
180
|
+
capacity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
181
|
+
remaining: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
182
|
+
lowest_price_cents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
183
|
+
currency: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
184
|
+
note: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
185
|
+
}, z.core.$strip>>>;
|
|
186
|
+
}, z.core.$strip>;
|
|
187
|
+
export type ProductContent = z.infer<typeof productContentSchema>;
|
|
188
|
+
export type ProductSummary = z.infer<typeof productSummarySchema>;
|
|
189
|
+
export type ProductMediaItem = z.infer<typeof productMediaItemSchema>;
|
|
190
|
+
export type ProductOption = z.infer<typeof productOptionSchema>;
|
|
191
|
+
export type ProductDeparture = z.infer<typeof productDepartureSchema>;
|
|
192
|
+
export type ProductDay = z.infer<typeof productDaySchema>;
|
|
193
|
+
export type ProductPolicy = z.infer<typeof productPolicySchema>;
|
|
194
|
+
/**
|
|
195
|
+
* Validate a `ProductContent` payload. Returns the parsed result on
|
|
196
|
+
* success or a structured failure on rejection. Used by the cache write
|
|
197
|
+
* path and by `mergeOverlaysIntoProductContent` to gate overlay merges.
|
|
198
|
+
*/
|
|
199
|
+
export declare function validateProductContent(payload: unknown): {
|
|
200
|
+
valid: true;
|
|
201
|
+
content: ProductContent;
|
|
202
|
+
} | {
|
|
203
|
+
valid: false;
|
|
204
|
+
reason: string;
|
|
205
|
+
};
|
|
206
|
+
/**
|
|
207
|
+
* Apply a list of editorial overlays to a product content payload via
|
|
208
|
+
* RFC 6901 JSON pointers. Validates the merged result against the
|
|
209
|
+
* vertical's Zod schema; overlays that produce an invalid payload are
|
|
210
|
+
* rolled back and reported via `onOverlayError`.
|
|
211
|
+
*
|
|
212
|
+
* Per sourced-content §3.5.4, this is the "content-shape-aware merger"
|
|
213
|
+
* — the catalog plane stays neutral about the content shape; the
|
|
214
|
+
* vertical plugs in its validator here.
|
|
215
|
+
*/
|
|
216
|
+
export declare function mergeOverlaysIntoProductContent(payload: ProductContent, overlays: ReadonlyArray<ContentOverlay>, options?: Pick<MergeOverlaysOptions, "onOverlayError">): ProductContent;
|
|
217
|
+
//# sourceMappingURL=content-shape.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-shape.d.ts","sourceRoot":"","sources":["../src/content-shape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,oBAAoB,EAE1B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;GAGG;AACH,eAAO,MAAM,+BAA+B,gBAAgB,CAAA;AAE5D;;;;GAIG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;iBAe/B,CAAA;AAEF,eAAO,MAAM,sBAAsB;;;;;;;;;iBAKjC,CAAA;AAEF,eAAO,MAAM,uBAAuB;;;;;;iBAMlC,CAAA;AAEF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;iBAM9B,CAAA;AAEF,eAAO,MAAM,gBAAgB;;;;;;iBAM3B,CAAA;AAEF,eAAO,MAAM,mBAAmB;;;;;;;;;iBAK9B,CAAA;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;iBAejC,CAAA;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAO/B,CAAA;AAEF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACjE,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACjE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AACrE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC/D,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AACrE,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAA;AACzD,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAE/D;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,OAAO,GACf;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAY7E;AAED;;;;;;;;;GASG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,aAAa,CAAC,cAAc,CAAC,EACvC,OAAO,GAAE,IAAI,CAAC,oBAAoB,EAAE,gBAAgB,CAAM,GACzD,cAAc,CAchB"}
|