@voyantjs/extras 0.20.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/catalog-policy.d.ts.map +1 -1
- package/dist/catalog-policy.js +15 -1
- package/dist/content-shape.d.ts +118 -0
- package/dist/content-shape.d.ts.map +1 -0
- package/dist/content-shape.js +98 -0
- package/dist/draft-shape.d.ts +34 -0
- package/dist/draft-shape.d.ts.map +1 -0
- package/dist/draft-shape.js +69 -0
- package/dist/routes.d.ts +18 -14
- package/dist/routes.d.ts.map +1 -1
- package/dist/schema-sourced-content.d.ts +254 -0
- package/dist/schema-sourced-content.d.ts.map +1 -0
- package/dist/schema-sourced-content.js +45 -0
- package/dist/schema.d.ts +21 -3
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +6 -0
- package/dist/service-catalog-plane.d.ts.map +1 -1
- package/dist/service-catalog-plane.js +1 -0
- package/dist/service-content-synthesizer.d.ts +41 -0
- package/dist/service-content-synthesizer.d.ts.map +1 -0
- package/dist/service-content-synthesizer.js +138 -0
- package/dist/service-content.d.ts +48 -0
- package/dist/service-content.d.ts.map +1 -0
- package/dist/service-content.js +253 -0
- package/dist/service.d.ts +18 -14
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +2 -0
- package/dist/validation.d.ts +28 -24
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +2 -0
- package/package.json +7 -7
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extras sourced-content cache. Sibling to the products / cruises /
|
|
3
|
+
* hospitality / charters variants — same shape, different vertical.
|
|
4
|
+
*
|
|
5
|
+
* One row per sourced extra × locale × market. Stores the content the
|
|
6
|
+
* upstream adapter served via `getContent`, plus the SWR metadata the
|
|
7
|
+
* read service needs.
|
|
8
|
+
*
|
|
9
|
+
* Extras are booking add-ons (optional line items layered on a parent
|
|
10
|
+
* product). Per `catalog-policy.ts`, extras are a partial-adoption
|
|
11
|
+
* vertical: they don't appear in the search index, but sourced extras
|
|
12
|
+
* (e.g. a TUI excursion add-on) still need rich content for the
|
|
13
|
+
* booking-flow's add-on selection UI. The sourced-content cache covers
|
|
14
|
+
* exactly that surface.
|
|
15
|
+
*
|
|
16
|
+
* See `docs/architecture/catalog-sourced-content.md` §3.2.
|
|
17
|
+
*/
|
|
18
|
+
export type ExtrasSourcedContentFetchStatus = "ok" | "stale" | "error" | "unsupported";
|
|
19
|
+
export declare const EXTRAS_CONTENT_MARKET_ANY = "*";
|
|
20
|
+
export declare const extrasSourcedContentTable: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
21
|
+
name: "extras_sourced_content";
|
|
22
|
+
schema: undefined;
|
|
23
|
+
columns: {
|
|
24
|
+
entity_id: import("drizzle-orm/pg-core").PgColumn<{
|
|
25
|
+
name: "entity_id";
|
|
26
|
+
tableName: "extras_sourced_content";
|
|
27
|
+
dataType: "string";
|
|
28
|
+
columnType: "PgText";
|
|
29
|
+
data: string;
|
|
30
|
+
driverParam: string;
|
|
31
|
+
notNull: true;
|
|
32
|
+
hasDefault: false;
|
|
33
|
+
isPrimaryKey: false;
|
|
34
|
+
isAutoincrement: false;
|
|
35
|
+
hasRuntimeDefault: false;
|
|
36
|
+
enumValues: [string, ...string[]];
|
|
37
|
+
baseColumn: never;
|
|
38
|
+
identity: undefined;
|
|
39
|
+
generated: undefined;
|
|
40
|
+
}, {}, {}>;
|
|
41
|
+
locale: import("drizzle-orm/pg-core").PgColumn<{
|
|
42
|
+
name: "locale";
|
|
43
|
+
tableName: "extras_sourced_content";
|
|
44
|
+
dataType: "string";
|
|
45
|
+
columnType: "PgText";
|
|
46
|
+
data: string;
|
|
47
|
+
driverParam: string;
|
|
48
|
+
notNull: true;
|
|
49
|
+
hasDefault: false;
|
|
50
|
+
isPrimaryKey: false;
|
|
51
|
+
isAutoincrement: false;
|
|
52
|
+
hasRuntimeDefault: false;
|
|
53
|
+
enumValues: [string, ...string[]];
|
|
54
|
+
baseColumn: never;
|
|
55
|
+
identity: undefined;
|
|
56
|
+
generated: undefined;
|
|
57
|
+
}, {}, {}>;
|
|
58
|
+
market: import("drizzle-orm/pg-core").PgColumn<{
|
|
59
|
+
name: "market";
|
|
60
|
+
tableName: "extras_sourced_content";
|
|
61
|
+
dataType: "string";
|
|
62
|
+
columnType: "PgText";
|
|
63
|
+
data: string;
|
|
64
|
+
driverParam: string;
|
|
65
|
+
notNull: true;
|
|
66
|
+
hasDefault: true;
|
|
67
|
+
isPrimaryKey: false;
|
|
68
|
+
isAutoincrement: false;
|
|
69
|
+
hasRuntimeDefault: false;
|
|
70
|
+
enumValues: [string, ...string[]];
|
|
71
|
+
baseColumn: never;
|
|
72
|
+
identity: undefined;
|
|
73
|
+
generated: undefined;
|
|
74
|
+
}, {}, {}>;
|
|
75
|
+
payload: import("drizzle-orm/pg-core").PgColumn<{
|
|
76
|
+
name: "payload";
|
|
77
|
+
tableName: "extras_sourced_content";
|
|
78
|
+
dataType: "json";
|
|
79
|
+
columnType: "PgJsonb";
|
|
80
|
+
data: Record<string, unknown>;
|
|
81
|
+
driverParam: unknown;
|
|
82
|
+
notNull: true;
|
|
83
|
+
hasDefault: false;
|
|
84
|
+
isPrimaryKey: false;
|
|
85
|
+
isAutoincrement: false;
|
|
86
|
+
hasRuntimeDefault: false;
|
|
87
|
+
enumValues: undefined;
|
|
88
|
+
baseColumn: never;
|
|
89
|
+
identity: undefined;
|
|
90
|
+
generated: undefined;
|
|
91
|
+
}, {}, {
|
|
92
|
+
$type: Record<string, unknown>;
|
|
93
|
+
}>;
|
|
94
|
+
content_schema_version: import("drizzle-orm/pg-core").PgColumn<{
|
|
95
|
+
name: "content_schema_version";
|
|
96
|
+
tableName: "extras_sourced_content";
|
|
97
|
+
dataType: "string";
|
|
98
|
+
columnType: "PgText";
|
|
99
|
+
data: string;
|
|
100
|
+
driverParam: string;
|
|
101
|
+
notNull: true;
|
|
102
|
+
hasDefault: false;
|
|
103
|
+
isPrimaryKey: false;
|
|
104
|
+
isAutoincrement: false;
|
|
105
|
+
hasRuntimeDefault: false;
|
|
106
|
+
enumValues: [string, ...string[]];
|
|
107
|
+
baseColumn: never;
|
|
108
|
+
identity: undefined;
|
|
109
|
+
generated: undefined;
|
|
110
|
+
}, {}, {}>;
|
|
111
|
+
returned_locale: import("drizzle-orm/pg-core").PgColumn<{
|
|
112
|
+
name: "returned_locale";
|
|
113
|
+
tableName: "extras_sourced_content";
|
|
114
|
+
dataType: "string";
|
|
115
|
+
columnType: "PgText";
|
|
116
|
+
data: string;
|
|
117
|
+
driverParam: string;
|
|
118
|
+
notNull: true;
|
|
119
|
+
hasDefault: false;
|
|
120
|
+
isPrimaryKey: false;
|
|
121
|
+
isAutoincrement: false;
|
|
122
|
+
hasRuntimeDefault: false;
|
|
123
|
+
enumValues: [string, ...string[]];
|
|
124
|
+
baseColumn: never;
|
|
125
|
+
identity: undefined;
|
|
126
|
+
generated: undefined;
|
|
127
|
+
}, {}, {}>;
|
|
128
|
+
machine_translated: import("drizzle-orm/pg-core").PgColumn<{
|
|
129
|
+
name: "machine_translated";
|
|
130
|
+
tableName: "extras_sourced_content";
|
|
131
|
+
dataType: "boolean";
|
|
132
|
+
columnType: "PgBoolean";
|
|
133
|
+
data: boolean;
|
|
134
|
+
driverParam: boolean;
|
|
135
|
+
notNull: true;
|
|
136
|
+
hasDefault: true;
|
|
137
|
+
isPrimaryKey: false;
|
|
138
|
+
isAutoincrement: false;
|
|
139
|
+
hasRuntimeDefault: false;
|
|
140
|
+
enumValues: undefined;
|
|
141
|
+
baseColumn: never;
|
|
142
|
+
identity: undefined;
|
|
143
|
+
generated: undefined;
|
|
144
|
+
}, {}, {}>;
|
|
145
|
+
source_updated_at: import("drizzle-orm/pg-core").PgColumn<{
|
|
146
|
+
name: "source_updated_at";
|
|
147
|
+
tableName: "extras_sourced_content";
|
|
148
|
+
dataType: "date";
|
|
149
|
+
columnType: "PgTimestamp";
|
|
150
|
+
data: Date;
|
|
151
|
+
driverParam: string;
|
|
152
|
+
notNull: false;
|
|
153
|
+
hasDefault: false;
|
|
154
|
+
isPrimaryKey: false;
|
|
155
|
+
isAutoincrement: false;
|
|
156
|
+
hasRuntimeDefault: false;
|
|
157
|
+
enumValues: undefined;
|
|
158
|
+
baseColumn: never;
|
|
159
|
+
identity: undefined;
|
|
160
|
+
generated: undefined;
|
|
161
|
+
}, {}, {}>;
|
|
162
|
+
fetched_at: import("drizzle-orm/pg-core").PgColumn<{
|
|
163
|
+
name: "fetched_at";
|
|
164
|
+
tableName: "extras_sourced_content";
|
|
165
|
+
dataType: "date";
|
|
166
|
+
columnType: "PgTimestamp";
|
|
167
|
+
data: Date;
|
|
168
|
+
driverParam: string;
|
|
169
|
+
notNull: true;
|
|
170
|
+
hasDefault: true;
|
|
171
|
+
isPrimaryKey: false;
|
|
172
|
+
isAutoincrement: false;
|
|
173
|
+
hasRuntimeDefault: false;
|
|
174
|
+
enumValues: undefined;
|
|
175
|
+
baseColumn: never;
|
|
176
|
+
identity: undefined;
|
|
177
|
+
generated: undefined;
|
|
178
|
+
}, {}, {}>;
|
|
179
|
+
fresh_until: import("drizzle-orm/pg-core").PgColumn<{
|
|
180
|
+
name: "fresh_until";
|
|
181
|
+
tableName: "extras_sourced_content";
|
|
182
|
+
dataType: "date";
|
|
183
|
+
columnType: "PgTimestamp";
|
|
184
|
+
data: Date;
|
|
185
|
+
driverParam: string;
|
|
186
|
+
notNull: false;
|
|
187
|
+
hasDefault: false;
|
|
188
|
+
isPrimaryKey: false;
|
|
189
|
+
isAutoincrement: false;
|
|
190
|
+
hasRuntimeDefault: false;
|
|
191
|
+
enumValues: undefined;
|
|
192
|
+
baseColumn: never;
|
|
193
|
+
identity: undefined;
|
|
194
|
+
generated: undefined;
|
|
195
|
+
}, {}, {}>;
|
|
196
|
+
etag: import("drizzle-orm/pg-core").PgColumn<{
|
|
197
|
+
name: "etag";
|
|
198
|
+
tableName: "extras_sourced_content";
|
|
199
|
+
dataType: "string";
|
|
200
|
+
columnType: "PgText";
|
|
201
|
+
data: string;
|
|
202
|
+
driverParam: string;
|
|
203
|
+
notNull: false;
|
|
204
|
+
hasDefault: false;
|
|
205
|
+
isPrimaryKey: false;
|
|
206
|
+
isAutoincrement: false;
|
|
207
|
+
hasRuntimeDefault: false;
|
|
208
|
+
enumValues: [string, ...string[]];
|
|
209
|
+
baseColumn: never;
|
|
210
|
+
identity: undefined;
|
|
211
|
+
generated: undefined;
|
|
212
|
+
}, {}, {}>;
|
|
213
|
+
fetch_status: import("drizzle-orm/pg-core").PgColumn<{
|
|
214
|
+
name: "fetch_status";
|
|
215
|
+
tableName: "extras_sourced_content";
|
|
216
|
+
dataType: "string";
|
|
217
|
+
columnType: "PgText";
|
|
218
|
+
data: ExtrasSourcedContentFetchStatus;
|
|
219
|
+
driverParam: string;
|
|
220
|
+
notNull: true;
|
|
221
|
+
hasDefault: true;
|
|
222
|
+
isPrimaryKey: false;
|
|
223
|
+
isAutoincrement: false;
|
|
224
|
+
hasRuntimeDefault: false;
|
|
225
|
+
enumValues: [string, ...string[]];
|
|
226
|
+
baseColumn: never;
|
|
227
|
+
identity: undefined;
|
|
228
|
+
generated: undefined;
|
|
229
|
+
}, {}, {
|
|
230
|
+
$type: ExtrasSourcedContentFetchStatus;
|
|
231
|
+
}>;
|
|
232
|
+
fetch_error: import("drizzle-orm/pg-core").PgColumn<{
|
|
233
|
+
name: "fetch_error";
|
|
234
|
+
tableName: "extras_sourced_content";
|
|
235
|
+
dataType: "string";
|
|
236
|
+
columnType: "PgText";
|
|
237
|
+
data: string;
|
|
238
|
+
driverParam: string;
|
|
239
|
+
notNull: false;
|
|
240
|
+
hasDefault: false;
|
|
241
|
+
isPrimaryKey: false;
|
|
242
|
+
isAutoincrement: false;
|
|
243
|
+
hasRuntimeDefault: false;
|
|
244
|
+
enumValues: [string, ...string[]];
|
|
245
|
+
baseColumn: never;
|
|
246
|
+
identity: undefined;
|
|
247
|
+
generated: undefined;
|
|
248
|
+
}, {}, {}>;
|
|
249
|
+
};
|
|
250
|
+
dialect: "pg";
|
|
251
|
+
}>;
|
|
252
|
+
export type InsertExtrasSourcedContent = typeof extrasSourcedContentTable.$inferInsert;
|
|
253
|
+
export type SelectExtrasSourcedContent = typeof extrasSourcedContentTable.$inferSelect;
|
|
254
|
+
//# sourceMappingURL=schema-sourced-content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-sourced-content.d.ts","sourceRoot":"","sources":["../src/schema-sourced-content.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,MAAM,MAAM,+BAA+B,GAAG,IAAI,GAAG,OAAO,GAAG,OAAO,GAAG,aAAa,CAAA;AAEtF,eAAO,MAAM,yBAAyB,MAAM,CAAA;AAE5C,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiCrC,CAAA;AAED,MAAM,MAAM,0BAA0B,GAAG,OAAO,yBAAyB,CAAC,YAAY,CAAA;AACtF,MAAM,MAAM,0BAA0B,GAAG,OAAO,yBAAyB,CAAC,YAAY,CAAA"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extras sourced-content cache. Sibling to the products / cruises /
|
|
3
|
+
* hospitality / charters variants — same shape, different vertical.
|
|
4
|
+
*
|
|
5
|
+
* One row per sourced extra × locale × market. Stores the content the
|
|
6
|
+
* upstream adapter served via `getContent`, plus the SWR metadata the
|
|
7
|
+
* read service needs.
|
|
8
|
+
*
|
|
9
|
+
* Extras are booking add-ons (optional line items layered on a parent
|
|
10
|
+
* product). Per `catalog-policy.ts`, extras are a partial-adoption
|
|
11
|
+
* vertical: they don't appear in the search index, but sourced extras
|
|
12
|
+
* (e.g. a TUI excursion add-on) still need rich content for the
|
|
13
|
+
* booking-flow's add-on selection UI. The sourced-content cache covers
|
|
14
|
+
* exactly that surface.
|
|
15
|
+
*
|
|
16
|
+
* See `docs/architecture/catalog-sourced-content.md` §3.2.
|
|
17
|
+
*/
|
|
18
|
+
import { boolean, index, jsonb, pgTable, primaryKey, text, timestamp } from "drizzle-orm/pg-core";
|
|
19
|
+
export const EXTRAS_CONTENT_MARKET_ANY = "*";
|
|
20
|
+
export const extrasSourcedContentTable = pgTable("extras_sourced_content", {
|
|
21
|
+
/** TypeID matching the extras module — `pxtr_*` (product_extras). */
|
|
22
|
+
entity_id: text("entity_id").notNull(),
|
|
23
|
+
/** BCP 47 language tag. */
|
|
24
|
+
locale: text("locale").notNull(),
|
|
25
|
+
/** Market id, or `'*'` for all-markets. */
|
|
26
|
+
market: text("market").notNull().default(EXTRAS_CONTENT_MARKET_ANY),
|
|
27
|
+
payload: jsonb("payload").$type().notNull(),
|
|
28
|
+
content_schema_version: text("content_schema_version").notNull(),
|
|
29
|
+
returned_locale: text("returned_locale").notNull(),
|
|
30
|
+
machine_translated: boolean("machine_translated").notNull().default(false),
|
|
31
|
+
source_updated_at: timestamp("source_updated_at", { withTimezone: true }),
|
|
32
|
+
fetched_at: timestamp("fetched_at", { withTimezone: true }).notNull().defaultNow(),
|
|
33
|
+
fresh_until: timestamp("fresh_until", { withTimezone: true }),
|
|
34
|
+
etag: text("etag"),
|
|
35
|
+
fetch_status: text("fetch_status")
|
|
36
|
+
.$type()
|
|
37
|
+
.notNull()
|
|
38
|
+
.default("ok"),
|
|
39
|
+
fetch_error: text("fetch_error"),
|
|
40
|
+
}, (table) => [
|
|
41
|
+
primaryKey({ columns: [table.entity_id, table.locale, table.market] }),
|
|
42
|
+
index("extras_sourced_content_locale_fresh_idx").on(table.locale, table.fresh_until),
|
|
43
|
+
index("extras_sourced_content_returned_locale_idx").on(table.entity_id, table.returned_locale),
|
|
44
|
+
index("extras_sourced_content_schema_version_idx").on(table.content_schema_version),
|
|
45
|
+
]);
|
package/dist/schema.d.ts
CHANGED
|
@@ -39,6 +39,23 @@ export declare const productExtras: import("drizzle-orm/pg-core").PgTableWithCol
|
|
|
39
39
|
identity: undefined;
|
|
40
40
|
generated: undefined;
|
|
41
41
|
}, {}, {}>;
|
|
42
|
+
supplierId: import("drizzle-orm/pg-core").PgColumn<{
|
|
43
|
+
name: "supplier_id";
|
|
44
|
+
tableName: "product_extras";
|
|
45
|
+
dataType: "string";
|
|
46
|
+
columnType: "PgText";
|
|
47
|
+
data: string;
|
|
48
|
+
driverParam: string;
|
|
49
|
+
notNull: false;
|
|
50
|
+
hasDefault: false;
|
|
51
|
+
isPrimaryKey: false;
|
|
52
|
+
isAutoincrement: false;
|
|
53
|
+
hasRuntimeDefault: false;
|
|
54
|
+
enumValues: [string, ...string[]];
|
|
55
|
+
baseColumn: never;
|
|
56
|
+
identity: undefined;
|
|
57
|
+
generated: undefined;
|
|
58
|
+
}, {}, {}>;
|
|
42
59
|
code: import("drizzle-orm/pg-core").PgColumn<{
|
|
43
60
|
name: "code";
|
|
44
61
|
tableName: "product_extras";
|
|
@@ -95,7 +112,7 @@ export declare const productExtras: import("drizzle-orm/pg-core").PgTableWithCol
|
|
|
95
112
|
tableName: "product_extras";
|
|
96
113
|
dataType: "string";
|
|
97
114
|
columnType: "PgEnumColumn";
|
|
98
|
-
data: "
|
|
115
|
+
data: "unavailable" | "optional" | "default_selected" | "required";
|
|
99
116
|
driverParam: string;
|
|
100
117
|
notNull: true;
|
|
101
118
|
hasDefault: true;
|
|
@@ -342,7 +359,7 @@ export declare const optionExtraConfigs: import("drizzle-orm/pg-core").PgTableWi
|
|
|
342
359
|
tableName: "option_extra_configs";
|
|
343
360
|
dataType: "string";
|
|
344
361
|
columnType: "PgEnumColumn";
|
|
345
|
-
data: "
|
|
362
|
+
data: "unavailable" | "optional" | "default_selected" | "required";
|
|
346
363
|
driverParam: string;
|
|
347
364
|
notNull: false;
|
|
348
365
|
hasDefault: false;
|
|
@@ -674,7 +691,7 @@ export declare const bookingExtras: import("drizzle-orm/pg-core").PgTableWithCol
|
|
|
674
691
|
tableName: "booking_extras";
|
|
675
692
|
dataType: "string";
|
|
676
693
|
columnType: "PgEnumColumn";
|
|
677
|
-
data: "
|
|
694
|
+
data: "confirmed" | "cancelled" | "draft" | "selected" | "fulfilled";
|
|
678
695
|
driverParam: string;
|
|
679
696
|
notNull: true;
|
|
680
697
|
hasDefault: true;
|
|
@@ -930,4 +947,5 @@ export declare const bookingExtrasRelations: import("drizzle-orm").Relations<"bo
|
|
|
930
947
|
productExtra: import("drizzle-orm").One<"product_extras", false>;
|
|
931
948
|
optionExtraConfig: import("drizzle-orm").One<"option_extra_configs", false>;
|
|
932
949
|
}>;
|
|
950
|
+
export { EXTRAS_CONTENT_MARKET_ANY, type ExtrasSourcedContentFetchStatus, extrasSourcedContentTable, type InsertExtrasSourcedContent, type SelectExtrasSourcedContent, } from "./schema-sourced-content.js";
|
|
933
951
|
//# sourceMappingURL=schema.d.ts.map
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,sBAAsB,mGAKjC,CAAA;AAEF,eAAO,MAAM,oBAAoB,yHAO/B,CAAA;AAEF,eAAO,MAAM,sBAAsB,oGAMjC,CAAA;AAEF,eAAO,MAAM,aAAa
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,sBAAsB,mGAKjC,CAAA;AAEF,eAAO,MAAM,oBAAoB,yHAO/B,CAAA;AAEF,eAAO,MAAM,sBAAsB,oGAMjC,CAAA;AAEF,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgCzB,CAAA;AAED,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyC9B,CAAA;AAED,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyCzB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,OAAO,aAAa,CAAC,YAAY,CAAA;AAC5D,MAAM,MAAM,eAAe,GAAG,OAAO,aAAa,CAAC,YAAY,CAAA;AAC/D,MAAM,MAAM,iBAAiB,GAAG,OAAO,kBAAkB,CAAC,YAAY,CAAA;AACtE,MAAM,MAAM,oBAAoB,GAAG,OAAO,kBAAkB,CAAC,YAAY,CAAA;AACzE,MAAM,MAAM,YAAY,GAAG,OAAO,aAAa,CAAC,YAAY,CAAA;AAC5D,MAAM,MAAM,eAAe,GAAG,OAAO,aAAa,CAAC,YAAY,CAAA;AAE/D,eAAO,MAAM,sBAAsB;;;EAGhC,CAAA;AAEH,eAAO,MAAM,2BAA2B;;;EAMrC,CAAA;AAEH,eAAO,MAAM,sBAAsB;;;EAShC,CAAA;AAKH,OAAO,EACL,yBAAyB,EACzB,KAAK,+BAA+B,EACpC,yBAAyB,EACzB,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,GAChC,MAAM,6BAA6B,CAAA"}
|
package/dist/schema.js
CHANGED
|
@@ -25,6 +25,7 @@ export const bookingExtraStatusEnum = pgEnum("booking_extra_status", [
|
|
|
25
25
|
export const productExtras = pgTable("product_extras", {
|
|
26
26
|
id: typeId("product_extras"),
|
|
27
27
|
productId: text("product_id").notNull(),
|
|
28
|
+
supplierId: text("supplier_id"),
|
|
28
29
|
code: text("code"),
|
|
29
30
|
name: text("name").notNull(),
|
|
30
31
|
description: text("description"),
|
|
@@ -42,6 +43,7 @@ export const productExtras = pgTable("product_extras", {
|
|
|
42
43
|
}, (table) => [
|
|
43
44
|
index("idx_product_extras_sort_name").on(table.sortOrder, table.name),
|
|
44
45
|
index("idx_product_extras_product_sort_name").on(table.productId, table.sortOrder, table.name),
|
|
46
|
+
index("idx_product_extras_supplier_sort_name").on(table.supplierId, table.sortOrder, table.name),
|
|
45
47
|
index("idx_product_extras_active_sort_name").on(table.active, table.sortOrder, table.name),
|
|
46
48
|
uniqueIndex("uidx_product_extras_product_code").on(table.productId, table.code),
|
|
47
49
|
]);
|
|
@@ -124,3 +126,7 @@ export const bookingExtrasRelations = relations(bookingExtras, ({ one }) => ({
|
|
|
124
126
|
references: [optionExtraConfigs.id],
|
|
125
127
|
}),
|
|
126
128
|
}));
|
|
129
|
+
// ─── Sourced-content cache ──────────────────────────────────────────
|
|
130
|
+
// Re-exported here so templates pick it up via the package's `./schema`
|
|
131
|
+
// entry without needing a separate config entry.
|
|
132
|
+
export { EXTRAS_CONTENT_MARKET_ANY, extrasSourcedContentTable, } from "./schema-sourced-content.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service-catalog-plane.d.ts","sourceRoot":"","sources":["../src/service-catalog-plane.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAGL,KAAK,oBAAoB,EAEzB,KAAK,eAAe,EACpB,KAAK,eAAe,EAEpB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,aAAa,EAEnB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAIhD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAU3C;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,OAAO,aAAa,CAAC,YAAY,EACtC,OAAO,EAAE;IAAE,gBAAgB,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7E,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"service-catalog-plane.d.ts","sourceRoot":"","sources":["../src/service-catalog-plane.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAGL,KAAK,oBAAoB,EAEzB,KAAK,eAAe,EACpB,KAAK,eAAe,EAEpB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,aAAa,EAEnB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAIhD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAU3C;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,OAAO,aAAa,CAAC,YAAY,EACtC,OAAO,EAAE;IAAE,gBAAgB,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7E,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CA6B9B;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,OAAO,aAAa,CAAC,YAAY,EACvC,OAAO,EAAE;IAAE,gBAAgB,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7E,UAAU,CAMZ;AAED,MAAM,WAAW,0BAA0B;IACzC,gBAAgB,EAAE,MAAM,CAAA;IACxB,KAAK,EAAE,aAAa,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,YAAY,EAChB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAW9B;AAED,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,YAAY,EAChB,IAAI,EAAE,aAAa,CAAC,OAAO,aAAa,CAAC,YAAY,CAAC,EACtD,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,YAAY,EAAE,CAAC,CAazB;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,YAAY,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,0BAA0B,GAAG;IAAE,YAAY,CAAC,EAAE,YAAY,CAAA;CAAE,GACpE,OAAO,CAAC,IAAI,CAAC,oBAAoB,EAAE,WAAW,CAAC,GAAG,IAAI,CAAC,CAUzD;AAMD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE;IAClD,gBAAgB,EAAE,MAAM,CAAA;IACxB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,GAAG,eAAe,CAAC,OAAO,aAAa,CAAC,YAAY,CAAC,CAarD;AAED,wBAAgB,0BAA0B,CACxC,EAAE,EAAE,YAAY,EAChB,OAAO,EAAE;IAAE,gBAAgB,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7E,eAAe,CAYjB;AAED,YAAY,EACV,oBAAoB,EACpB,eAAe,EACf,eAAe,EACf,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,aAAa,GACd,CAAA"}
|
|
@@ -43,6 +43,7 @@ export function productExtraRowToProjection(row, context) {
|
|
|
43
43
|
["id", row.id],
|
|
44
44
|
["code", row.code],
|
|
45
45
|
["productId", row.productId],
|
|
46
|
+
["supplierId", row.supplierId],
|
|
46
47
|
["createdAt", row.createdAt],
|
|
47
48
|
["updatedAt", row.updatedAt],
|
|
48
49
|
// Snapshot-relevant managed fields
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extras content synthesizer — fallback for thin adapters that declare
|
|
3
|
+
* `supportsContentFetch: false`.
|
|
4
|
+
*
|
|
5
|
+
* Produces the most complete `ExtraContent` blob we can legitimately
|
|
6
|
+
* synthesize from the durable sourced-entry projection + locale-aware
|
|
7
|
+
* overlays + plane-level provenance. Sub-options render as a typed
|
|
8
|
+
* empty state unless the projection genuinely carries them.
|
|
9
|
+
*
|
|
10
|
+
* Per §3.6: never invents plausible-but-unverified fields, never
|
|
11
|
+
* machine-translates, never mines snapshots, never caches its own
|
|
12
|
+
* output.
|
|
13
|
+
*/
|
|
14
|
+
import { type ProvenanceReadResult } from "@voyantjs/catalog";
|
|
15
|
+
import type { AnyDrizzleDb } from "@voyantjs/db";
|
|
16
|
+
import { type ExtraContent } from "./content-shape.js";
|
|
17
|
+
export interface SynthesizeExtraContentOptions {
|
|
18
|
+
provenance: Extract<ProvenanceReadResult, {
|
|
19
|
+
kind: "sourced";
|
|
20
|
+
}>;
|
|
21
|
+
overlays?: ReadonlyArray<{
|
|
22
|
+
field_path: string;
|
|
23
|
+
value: unknown;
|
|
24
|
+
}>;
|
|
25
|
+
}
|
|
26
|
+
export interface SynthesizedExtraContent {
|
|
27
|
+
content: ExtraContent;
|
|
28
|
+
content_schema_version: string;
|
|
29
|
+
served_locale: string;
|
|
30
|
+
source_kind: string;
|
|
31
|
+
source_provider?: string;
|
|
32
|
+
}
|
|
33
|
+
export declare function synthesizeExtraContent(scope: {
|
|
34
|
+
locale: string;
|
|
35
|
+
}, options: SynthesizeExtraContentOptions): SynthesizedExtraContent;
|
|
36
|
+
export declare function synthesizeExtraContentFromDb(db: AnyDrizzleDb, scope: {
|
|
37
|
+
locale: string;
|
|
38
|
+
}, provenance: Extract<ProvenanceReadResult, {
|
|
39
|
+
kind: "sourced";
|
|
40
|
+
}>): Promise<SynthesizedExtraContent>;
|
|
41
|
+
//# sourceMappingURL=service-content-synthesizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-content-synthesizer.d.ts","sourceRoot":"","sources":["../src/service-content-synthesizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD,OAAO,EAEL,KAAK,YAAY,EAElB,MAAM,oBAAoB,CAAA;AAE3B,MAAM,WAAW,6BAA6B;IAC5C,UAAU,EAAE,OAAO,CAAC,oBAAoB,EAAE;QAAE,IAAI,EAAE,SAAS,CAAA;KAAE,CAAC,CAAA;IAC9D,QAAQ,CAAC,EAAE,aAAa,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;CACjE;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,YAAY,CAAA;IACrB,sBAAsB,EAAE,MAAM,CAAA;IAC9B,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,EACzB,OAAO,EAAE,6BAA6B,GACrC,uBAAuB,CAiCzB;AAED,wBAAsB,4BAA4B,CAChD,EAAE,EAAE,YAAY,EAChB,KAAK,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,EACzB,UAAU,EAAE,OAAO,CAAC,oBAAoB,EAAE;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC,GAC7D,OAAO,CAAC,uBAAuB,CAAC,CAOlC"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extras content synthesizer — fallback for thin adapters that declare
|
|
3
|
+
* `supportsContentFetch: false`.
|
|
4
|
+
*
|
|
5
|
+
* Produces the most complete `ExtraContent` blob we can legitimately
|
|
6
|
+
* synthesize from the durable sourced-entry projection + locale-aware
|
|
7
|
+
* overlays + plane-level provenance. Sub-options render as a typed
|
|
8
|
+
* empty state unless the projection genuinely carries them.
|
|
9
|
+
*
|
|
10
|
+
* Per §3.6: never invents plausible-but-unverified fields, never
|
|
11
|
+
* machine-translates, never mines snapshots, never caches its own
|
|
12
|
+
* output.
|
|
13
|
+
*/
|
|
14
|
+
import { fetchOverlaysForEntity, mergeOverlaysIntoContent, } from "@voyantjs/catalog";
|
|
15
|
+
import { EXTRAS_CONTENT_SCHEMA_VERSION, extraContentSchema, } from "./content-shape.js";
|
|
16
|
+
export function synthesizeExtraContent(scope, options) {
|
|
17
|
+
const projection = options.provenance.projection;
|
|
18
|
+
const extra = pickExtraSummary(projection, options.provenance);
|
|
19
|
+
const media = pickMedia(projection);
|
|
20
|
+
const policies = pickPolicies(projection);
|
|
21
|
+
const baseContent = {
|
|
22
|
+
extra,
|
|
23
|
+
options: [],
|
|
24
|
+
media,
|
|
25
|
+
policies,
|
|
26
|
+
};
|
|
27
|
+
let merged = baseContent;
|
|
28
|
+
if (options.overlays && options.overlays.length > 0) {
|
|
29
|
+
const result = mergeOverlaysIntoContent(baseContent, options.overlays, {
|
|
30
|
+
validate(p) {
|
|
31
|
+
const r = extraContentSchema.safeParse(p);
|
|
32
|
+
return r.success
|
|
33
|
+
? { valid: true }
|
|
34
|
+
: { valid: false, reason: r.error.issues[0]?.message ?? "invalid" };
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
merged = extraContentSchema.parse(result);
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
content: merged,
|
|
41
|
+
content_schema_version: EXTRAS_CONTENT_SCHEMA_VERSION,
|
|
42
|
+
served_locale: scope.locale,
|
|
43
|
+
source_kind: options.provenance.provenance.source_kind,
|
|
44
|
+
source_provider: options.provenance.provenance.source_provider,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export async function synthesizeExtraContentFromDb(db, scope, provenance) {
|
|
48
|
+
const entityId = entityIdFromProvenance(provenance);
|
|
49
|
+
const overlays = await fetchOverlaysForEntity(db, "extras", entityId);
|
|
50
|
+
return synthesizeExtraContent(scope, {
|
|
51
|
+
provenance,
|
|
52
|
+
overlays: overlays.map((o) => ({ field_path: o.field_path, value: o.value })),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function entityIdFromProvenance(provenance) {
|
|
56
|
+
const fromProjection = provenance.projection.id;
|
|
57
|
+
if (typeof fromProjection === "string" && fromProjection.length > 0) {
|
|
58
|
+
return fromProjection;
|
|
59
|
+
}
|
|
60
|
+
return provenance.entry_id;
|
|
61
|
+
}
|
|
62
|
+
function pickExtraSummary(projection, provenance) {
|
|
63
|
+
return {
|
|
64
|
+
id: stringOr(projection.id, "") || provenance.entry_id,
|
|
65
|
+
name: stringOr(projection.name, "") || stringOr(projection.title, "") || "Unnamed extra",
|
|
66
|
+
status: stringOr(projection.status, undefined),
|
|
67
|
+
description: stringOr(projection.description, null),
|
|
68
|
+
selection_type: stringOr(projection.selection_type, "optional"),
|
|
69
|
+
pricing_mode: stringOr(projection.pricing_mode, "per_booking"),
|
|
70
|
+
priced_per_person: typeof projection.priced_per_person === "boolean" ? projection.priced_per_person : undefined,
|
|
71
|
+
category: stringOr(projection.category, null),
|
|
72
|
+
hero_image_url: stringOr(projection.hero_image_url, null),
|
|
73
|
+
highlights: stringArrayOr(projection.highlights, []),
|
|
74
|
+
supplier: stringOr(projection.supplier, null) ??
|
|
75
|
+
stringOr(projection.supplier_name, null) ??
|
|
76
|
+
provenance.provenance.source_provider ??
|
|
77
|
+
null,
|
|
78
|
+
duration_minutes: numberOr(projection.duration_minutes, null),
|
|
79
|
+
requirements_summary: stringOr(projection.requirements_summary, null),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function pickMedia(projection) {
|
|
83
|
+
const heroUrl = stringOr(projection.hero_image_url, null);
|
|
84
|
+
const out = [];
|
|
85
|
+
if (heroUrl) {
|
|
86
|
+
out.push({ url: heroUrl, type: "image", caption: null, alt: null });
|
|
87
|
+
}
|
|
88
|
+
const additional = projection.media;
|
|
89
|
+
if (Array.isArray(additional)) {
|
|
90
|
+
for (const item of additional) {
|
|
91
|
+
if (item && typeof item === "object") {
|
|
92
|
+
const obj = item;
|
|
93
|
+
const url = stringOr(obj.url, null);
|
|
94
|
+
if (!url)
|
|
95
|
+
continue;
|
|
96
|
+
out.push({
|
|
97
|
+
url,
|
|
98
|
+
type: pickMediaType(obj.type),
|
|
99
|
+
caption: stringOr(obj.caption, null),
|
|
100
|
+
alt: stringOr(obj.alt, null),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
function pickMediaType(value) {
|
|
108
|
+
if (value === "video")
|
|
109
|
+
return "video";
|
|
110
|
+
if (value === "document")
|
|
111
|
+
return "document";
|
|
112
|
+
return "image";
|
|
113
|
+
}
|
|
114
|
+
function pickPolicies(projection) {
|
|
115
|
+
const out = [];
|
|
116
|
+
const cancel = stringOr(projection.cancellation_policy, null);
|
|
117
|
+
if (cancel)
|
|
118
|
+
out.push({ kind: "cancellation", body: cancel });
|
|
119
|
+
const payment = stringOr(projection.payment_terms, null);
|
|
120
|
+
if (payment)
|
|
121
|
+
out.push({ kind: "payment", body: payment });
|
|
122
|
+
const requirements = stringOr(projection.requirements, null);
|
|
123
|
+
if (requirements)
|
|
124
|
+
out.push({ kind: "requirements", body: requirements });
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
function stringOr(value, fallback) {
|
|
128
|
+
return typeof value === "string" && value.length > 0 ? value : fallback;
|
|
129
|
+
}
|
|
130
|
+
function numberOr(value, fallback) {
|
|
131
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
132
|
+
}
|
|
133
|
+
function stringArrayOr(value, fallback) {
|
|
134
|
+
if (!Array.isArray(value))
|
|
135
|
+
return fallback;
|
|
136
|
+
const out = value.filter((v) => typeof v === "string");
|
|
137
|
+
return out.length > 0 ? out : fallback;
|
|
138
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extras content service — `getExtraContent` with locale-resolved
|
|
3
|
+
* cache reads, SWR refresh, and synthesizer fallback.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors `service-content.ts` in the products / cruises / hospitality
|
|
6
|
+
* / charters packages but extras-shaped. The extras content aggregate
|
|
7
|
+
* (§3.2 / §3.6) is `{ extra, options[], media[], policies[] }` — one
|
|
8
|
+
* payload returned by a single getContent. Pricing stays out (volatile-
|
|
9
|
+
* live, flows through `liveResolve`).
|
|
10
|
+
*
|
|
11
|
+
* Extras don't appear in the search index (per the vertical's catalog-
|
|
12
|
+
* policy.ts), but sourced extras still need rich content for the
|
|
13
|
+
* booking-flow's add-on selection UI. The cache layer covers exactly
|
|
14
|
+
* that surface.
|
|
15
|
+
*/
|
|
16
|
+
import { type ContentLocaleResolution, type InvalidateOnDrift, type SourceAdapter, type SourceAdapterContext } from "@voyantjs/catalog";
|
|
17
|
+
import type { SourceAdapterRegistry } from "@voyantjs/catalog/booking-engine";
|
|
18
|
+
import type { AnyDrizzleDb } from "@voyantjs/db";
|
|
19
|
+
import { type ExtraContent } from "./content-shape.js";
|
|
20
|
+
export interface ExtraContentScope {
|
|
21
|
+
preferredLocales: ReadonlyArray<string>;
|
|
22
|
+
market?: string;
|
|
23
|
+
currency?: string;
|
|
24
|
+
acceptMachineTranslated?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface GetExtraContentOptions {
|
|
27
|
+
registry: SourceAdapterRegistry;
|
|
28
|
+
buildAdapterContext?: (adapter: SourceAdapter) => SourceAdapterContext;
|
|
29
|
+
onOverlayError?: (event: {
|
|
30
|
+
field_path: string;
|
|
31
|
+
reason: string;
|
|
32
|
+
}) => void;
|
|
33
|
+
}
|
|
34
|
+
export interface ResolvedExtraContent {
|
|
35
|
+
content: ExtraContent;
|
|
36
|
+
resolution: ContentLocaleResolution<{
|
|
37
|
+
locale: string;
|
|
38
|
+
payload: ExtraContent;
|
|
39
|
+
}>;
|
|
40
|
+
source: "sourced-cache" | "sourced-fresh" | "synthesized";
|
|
41
|
+
served_stale: boolean;
|
|
42
|
+
synthesized: boolean;
|
|
43
|
+
machine_translated: boolean;
|
|
44
|
+
}
|
|
45
|
+
export declare function getExtraContent(db: AnyDrizzleDb, entityId: string, scope: ExtraContentScope, options: GetExtraContentOptions): Promise<ResolvedExtraContent | null>;
|
|
46
|
+
/** Drift event consumer for the extras content cache. Per §3.4.1. */
|
|
47
|
+
export declare const invalidateExtraContentOnDrift: InvalidateOnDrift;
|
|
48
|
+
//# sourceMappingURL=service-content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-content.d.ts","sourceRoot":"","sources":["../src/service-content.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EACL,KAAK,uBAAuB,EAK5B,KAAK,iBAAiB,EAKtB,KAAK,aAAa,EAClB,KAAK,oBAAoB,EAE1B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAA;AAC7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAGhD,OAAO,EAEL,KAAK,YAAY,EAIlB,MAAM,oBAAoB,CAAA;AAc3B,MAAM,WAAW,iBAAiB;IAChC,gBAAgB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IACvC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,uBAAuB,CAAC,EAAE,OAAO,CAAA;CAClC;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,qBAAqB,CAAA;IAC/B,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,oBAAoB,CAAA;IACtE,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;CACzE;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,YAAY,CAAA;IACrB,UAAU,EAAE,uBAAuB,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,YAAY,CAAA;KAAE,CAAC,CAAA;IAC9E,MAAM,EAAE,eAAe,GAAG,eAAe,GAAG,aAAa,CAAA;IACzD,YAAY,EAAE,OAAO,CAAA;IACrB,WAAW,EAAE,OAAO,CAAA;IACpB,kBAAkB,EAAE,OAAO,CAAA;CAC5B;AAED,wBAAsB,eAAe,CACnC,EAAE,EAAE,YAAY,EAChB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,iBAAiB,EACxB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CA0FtC;AA2ND,qEAAqE;AACrE,eAAO,MAAM,6BAA6B,EAAE,iBAG3C,CAAA"}
|