@voyant-travel/inventory 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/dist/action-ledger-drift.d.ts +29 -0
- package/dist/action-ledger-drift.d.ts.map +1 -0
- package/dist/action-ledger-drift.js +338 -0
- package/dist/action-ledger.d.ts +104 -0
- package/dist/action-ledger.d.ts.map +1 -0
- package/dist/action-ledger.js +100 -0
- package/dist/authoring/builder.d.ts +37 -0
- package/dist/authoring/builder.d.ts.map +1 -0
- package/dist/authoring/builder.js +248 -0
- package/dist/authoring/clone-content.d.ts +38 -0
- package/dist/authoring/clone-content.d.ts.map +1 -0
- package/dist/authoring/clone-content.js +367 -0
- package/dist/authoring/clone-pricing.d.ts +9 -0
- package/dist/authoring/clone-pricing.d.ts.map +1 -0
- package/dist/authoring/clone-pricing.js +242 -0
- package/dist/authoring/clone.d.ts +45 -0
- package/dist/authoring/clone.d.ts.map +1 -0
- package/dist/authoring/clone.js +142 -0
- package/dist/authoring/errors.d.ts +21 -0
- package/dist/authoring/errors.d.ts.map +1 -0
- package/dist/authoring/errors.js +13 -0
- package/dist/authoring/extension.d.ts +248 -0
- package/dist/authoring/extension.d.ts.map +1 -0
- package/dist/authoring/extension.js +116 -0
- package/dist/authoring/index.d.ts +12 -0
- package/dist/authoring/index.d.ts.map +1 -0
- package/dist/authoring/index.js +11 -0
- package/dist/authoring/schema.d.ts +85 -0
- package/dist/authoring/schema.d.ts.map +1 -0
- package/dist/authoring/schema.js +16 -0
- package/dist/authoring/service.d.ts +28 -0
- package/dist/authoring/service.d.ts.map +1 -0
- package/dist/authoring/service.js +66 -0
- package/dist/authoring/spec.d.ts +524 -0
- package/dist/authoring/spec.d.ts.map +1 -0
- package/dist/authoring/spec.js +167 -0
- package/dist/authoring/validate.d.ts +17 -0
- package/dist/authoring/validate.d.ts.map +1 -0
- package/dist/authoring/validate.js +83 -0
- package/dist/authoring.d.ts +2 -0
- package/dist/authoring.d.ts.map +1 -0
- package/dist/authoring.js +1 -0
- package/dist/booking-engine/handler-support.d.ts +91 -0
- package/dist/booking-engine/handler-support.d.ts.map +1 -0
- package/dist/booking-engine/handler-support.js +355 -0
- package/dist/booking-engine/handler.d.ts +404 -0
- package/dist/booking-engine/handler.d.ts.map +1 -0
- package/dist/booking-engine/handler.js +398 -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/booking-engine.d.ts +2 -0
- package/dist/booking-engine.d.ts.map +1 -0
- package/dist/booking-engine.js +1 -0
- package/dist/booking-extension.d.ts +278 -0
- package/dist/booking-extension.d.ts.map +1 -0
- package/dist/booking-extension.js +161 -0
- package/dist/catalog-policy-departures.d.ts +52 -0
- package/dist/catalog-policy-departures.d.ts.map +1 -0
- package/dist/catalog-policy-departures.js +169 -0
- package/dist/catalog-policy-destinations.d.ts +43 -0
- package/dist/catalog-policy-destinations.d.ts.map +1 -0
- package/dist/catalog-policy-destinations.js +165 -0
- package/dist/catalog-policy-pricing.d.ts +55 -0
- package/dist/catalog-policy-pricing.d.ts.map +1 -0
- package/dist/catalog-policy-pricing.js +109 -0
- package/dist/catalog-policy-promotions.d.ts +52 -0
- package/dist/catalog-policy-promotions.d.ts.map +1 -0
- package/dist/catalog-policy-promotions.js +270 -0
- package/dist/catalog-policy-taxonomy.d.ts +51 -0
- package/dist/catalog-policy-taxonomy.d.ts.map +1 -0
- package/dist/catalog-policy-taxonomy.js +191 -0
- package/dist/catalog-policy.d.ts +33 -0
- package/dist/catalog-policy.d.ts.map +1 -0
- package/dist/catalog-policy.js +733 -0
- package/dist/content-shape.d.ts +15 -0
- package/dist/content-shape.d.ts.map +1 -0
- package/dist/content-shape.js +28 -0
- package/dist/draft-shape.d.ts +43 -0
- package/dist/draft-shape.d.ts.map +1 -0
- package/dist/draft-shape.js +48 -0
- package/dist/events.d.ts +37 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +32 -0
- package/dist/extras/catalog-policy.d.ts +30 -0
- package/dist/extras/catalog-policy.d.ts.map +1 -0
- package/dist/extras/catalog-policy.js +319 -0
- package/dist/extras/content-shape.d.ts +5 -0
- package/dist/extras/content-shape.d.ts.map +1 -0
- package/dist/extras/content-shape.js +13 -0
- package/dist/extras/draft-shape.d.ts +34 -0
- package/dist/extras/draft-shape.d.ts.map +1 -0
- package/dist/extras/draft-shape.js +69 -0
- package/dist/extras/routes.d.ts +380 -0
- package/dist/extras/routes.d.ts.map +1 -0
- package/dist/extras/routes.js +59 -0
- package/dist/extras/schema-sourced-content.d.ts +254 -0
- package/dist/extras/schema-sourced-content.d.ts.map +1 -0
- package/dist/extras/schema-sourced-content.js +45 -0
- package/dist/extras/schema.d.ts +628 -0
- package/dist/extras/schema.d.ts.map +1 -0
- package/dist/extras/schema.js +87 -0
- package/dist/extras/service-catalog-plane.d.ts +77 -0
- package/dist/extras/service-catalog-plane.d.ts.map +1 -0
- package/dist/extras/service-catalog-plane.js +219 -0
- package/dist/extras/service-content-synthesizer.d.ts +41 -0
- package/dist/extras/service-content-synthesizer.d.ts.map +1 -0
- package/dist/extras/service-content-synthesizer.js +138 -0
- package/dist/extras/service-content.d.ts +48 -0
- package/dist/extras/service-content.d.ts.map +1 -0
- package/dist/extras/service-content.js +253 -0
- package/dist/extras/service.d.ts +185 -0
- package/dist/extras/service.d.ts.map +1 -0
- package/dist/extras/service.js +96 -0
- package/dist/extras/validation.d.ts +437 -0
- package/dist/extras/validation.d.ts.map +1 -0
- package/dist/extras/validation.js +149 -0
- package/dist/extras.d.ts +267 -0
- package/dist/extras.d.ts.map +1 -0
- package/dist/extras.js +19 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/interface.d.ts +5869 -0
- package/dist/interface.d.ts.map +1 -0
- package/dist/interface.js +54 -0
- package/dist/public-routes.d.ts +2 -0
- package/dist/public-routes.d.ts.map +1 -0
- package/dist/public-routes.js +1 -0
- package/dist/public-validation.d.ts +2 -0
- package/dist/public-validation.d.ts.map +1 -0
- package/dist/public-validation.js +1 -0
- package/dist/read-model.d.ts +25 -0
- package/dist/read-model.d.ts.map +1 -0
- package/dist/read-model.js +99 -0
- package/dist/route-env.d.ts +22 -0
- package/dist/route-env.d.ts.map +1 -0
- package/dist/route-env.js +1 -0
- package/dist/routes-associations.d.ts +164 -0
- package/dist/routes-associations.d.ts.map +1 -0
- package/dist/routes-associations.js +100 -0
- package/dist/routes-catalog.d.ts +436 -0
- package/dist/routes-catalog.d.ts.map +1 -0
- package/dist/routes-catalog.js +104 -0
- package/dist/routes-configuration.d.ts +773 -0
- package/dist/routes-configuration.d.ts.map +1 -0
- package/dist/routes-configuration.js +364 -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-core.d.ts +331 -0
- package/dist/routes-core.d.ts.map +1 -0
- package/dist/routes-core.js +95 -0
- package/dist/routes-itinerary.d.ts +759 -0
- package/dist/routes-itinerary.d.ts.map +1 -0
- package/dist/routes-itinerary.js +387 -0
- package/dist/routes-maintenance.d.ts +32 -0
- package/dist/routes-maintenance.d.ts.map +1 -0
- package/dist/routes-maintenance.js +14 -0
- package/dist/routes-media.d.ts +634 -0
- package/dist/routes-media.d.ts.map +1 -0
- package/dist/routes-media.js +245 -0
- package/dist/routes-merchandising.d.ts +1120 -0
- package/dist/routes-merchandising.d.ts.map +1 -0
- package/dist/routes-merchandising.js +377 -0
- package/dist/routes-options.d.ts +363 -0
- package/dist/routes-options.d.ts.map +1 -0
- package/dist/routes-options.js +173 -0
- package/dist/routes-public.d.ts +776 -0
- package/dist/routes-public.d.ts.map +1 -0
- package/dist/routes-public.js +119 -0
- package/dist/routes-translations.d.ts +489 -0
- package/dist/routes-translations.d.ts.map +1 -0
- package/dist/routes-translations.js +258 -0
- package/dist/routes.d.ts +5097 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +64 -0
- package/dist/schema-core.d.ts +1238 -0
- package/dist/schema-core.d.ts.map +1 -0
- package/dist/schema-core.js +157 -0
- package/dist/schema-itinerary.d.ts +1169 -0
- package/dist/schema-itinerary.d.ts.map +1 -0
- package/dist/schema-itinerary.js +130 -0
- package/dist/schema-relations.d.ts +117 -0
- package/dist/schema-relations.d.ts.map +1 -0
- package/dist/schema-relations.js +192 -0
- package/dist/schema-settings.d.ts +1800 -0
- package/dist/schema-settings.d.ts.map +1 -0
- package/dist/schema-settings.js +220 -0
- package/dist/schema-shared.d.ts +15 -0
- package/dist/schema-shared.d.ts.map +1 -0
- package/dist/schema-shared.js +91 -0
- 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 +1363 -0
- package/dist/schema-taxonomy.d.ts.map +1 -0
- package/dist/schema-taxonomy.js +203 -0
- package/dist/schema.d.ts +10 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +9 -0
- package/dist/service-aggregates.d.ts +29 -0
- package/dist/service-aggregates.d.ts.map +1 -0
- package/dist/service-aggregates.js +56 -0
- package/dist/service-catalog-plane-destinations.d.ts +30 -0
- package/dist/service-catalog-plane-destinations.d.ts.map +1 -0
- package/dist/service-catalog-plane-destinations.js +143 -0
- package/dist/service-catalog-plane-taxonomy.d.ts +73 -0
- package/dist/service-catalog-plane-taxonomy.d.ts.map +1 -0
- package/dist/service-catalog-plane-taxonomy.js +242 -0
- package/dist/service-catalog-plane.d.ts +179 -0
- package/dist/service-catalog-plane.d.ts.map +1 -0
- package/dist/service-catalog-plane.js +431 -0
- package/dist/service-catalog.d.ts +251 -0
- package/dist/service-catalog.d.ts.map +1 -0
- package/dist/service-catalog.js +517 -0
- package/dist/service-configuration.d.ts +261 -0
- package/dist/service-configuration.d.ts.map +1 -0
- package/dist/service-configuration.js +343 -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 +329 -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 +178 -0
- package/dist/service-content.d.ts +106 -0
- package/dist/service-content.d.ts.map +1 -0
- package/dist/service-content.js +388 -0
- package/dist/service-core.d.ts +194 -0
- package/dist/service-core.d.ts.map +1 -0
- package/dist/service-core.js +213 -0
- package/dist/service-delivery-formats.d.ts +58 -0
- package/dist/service-delivery-formats.d.ts.map +1 -0
- package/dist/service-delivery-formats.js +107 -0
- package/dist/service-destinations.d.ts +223 -0
- package/dist/service-destinations.d.ts.map +1 -0
- package/dist/service-destinations.js +310 -0
- package/dist/service-itinerary-history.d.ts +457 -0
- package/dist/service-itinerary-history.d.ts.map +1 -0
- package/dist/service-itinerary-history.js +135 -0
- package/dist/service-itinerary.d.ts +1149 -0
- package/dist/service-itinerary.d.ts.map +1 -0
- package/dist/service-itinerary.js +419 -0
- package/dist/service-media.d.ts +272 -0
- package/dist/service-media.d.ts.map +1 -0
- package/dist/service-media.js +320 -0
- package/dist/service-merchandising.d.ts +184 -0
- package/dist/service-merchandising.d.ts.map +1 -0
- package/dist/service-merchandising.js +181 -0
- package/dist/service-option-translations.d.ts +268 -0
- package/dist/service-option-translations.d.ts.map +1 -0
- package/dist/service-option-translations.js +300 -0
- package/dist/service-options.d.ts +181 -0
- package/dist/service-options.d.ts.map +1 -0
- package/dist/service-options.js +179 -0
- package/dist/service-product-destinations.d.ts +37 -0
- package/dist/service-product-destinations.d.ts.map +1 -0
- package/dist/service-product-destinations.js +94 -0
- package/dist/service-public.d.ts +664 -0
- package/dist/service-public.d.ts.map +1 -0
- package/dist/service-public.js +374 -0
- package/dist/service-taxonomy.d.ts +197 -0
- package/dist/service-taxonomy.d.ts.map +1 -0
- package/dist/service-taxonomy.js +221 -0
- package/dist/service.d.ts +3929 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +28 -0
- package/dist/tasks/brochure-printers.d.ts +31 -0
- package/dist/tasks/brochure-printers.d.ts.map +1 -0
- package/dist/tasks/brochure-printers.js +149 -0
- package/dist/tasks/brochure-templates.d.ts +36 -0
- package/dist/tasks/brochure-templates.d.ts.map +1 -0
- package/dist/tasks/brochure-templates.js +110 -0
- package/dist/tasks/brochures.d.ts +43 -0
- package/dist/tasks/brochures.d.ts.map +1 -0
- package/dist/tasks/brochures.js +72 -0
- package/dist/tasks/generate-pdf.d.ts +8 -0
- package/dist/tasks/generate-pdf.d.ts.map +1 -0
- package/dist/tasks/generate-pdf.js +106 -0
- package/dist/tasks/index.d.ts +5 -0
- package/dist/tasks/index.d.ts.map +1 -0
- package/dist/tasks/index.js +4 -0
- package/dist/tasks/pdf-text.d.ts +2 -0
- package/dist/tasks/pdf-text.d.ts.map +1 -0
- package/dist/tasks/pdf-text.js +40 -0
- package/dist/tasks.d.ts +2 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +1 -0
- package/dist/validation-catalog.d.ts +2 -0
- package/dist/validation-catalog.d.ts.map +1 -0
- package/dist/validation-catalog.js +3 -0
- package/dist/validation-config.d.ts +2 -0
- package/dist/validation-config.d.ts.map +1 -0
- package/dist/validation-config.js +3 -0
- package/dist/validation-content.d.ts +2 -0
- package/dist/validation-content.d.ts.map +1 -0
- package/dist/validation-content.js +3 -0
- package/dist/validation-core.d.ts +2 -0
- package/dist/validation-core.d.ts.map +1 -0
- package/dist/validation-core.js +3 -0
- package/dist/validation-public.d.ts +2 -0
- package/dist/validation-public.d.ts.map +1 -0
- package/dist/validation-public.js +3 -0
- package/dist/validation-shared.d.ts +2 -0
- package/dist/validation-shared.d.ts.map +1 -0
- package/dist/validation-shared.js +3 -0
- package/dist/validation.d.ts +2 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +3 -0
- package/package.json +204 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import type { HonoExtension } from "@voyant-travel/hono/module";
|
|
2
|
+
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
export declare const bookingProductDetails: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
5
|
+
name: "booking_product_details";
|
|
6
|
+
schema: undefined;
|
|
7
|
+
columns: {
|
|
8
|
+
bookingId: import("drizzle-orm/pg-core").PgColumn<{
|
|
9
|
+
name: "booking_id";
|
|
10
|
+
tableName: "booking_product_details";
|
|
11
|
+
dataType: "string";
|
|
12
|
+
columnType: "PgText";
|
|
13
|
+
data: string;
|
|
14
|
+
driverParam: string;
|
|
15
|
+
notNull: true;
|
|
16
|
+
hasDefault: false;
|
|
17
|
+
isPrimaryKey: true;
|
|
18
|
+
isAutoincrement: false;
|
|
19
|
+
hasRuntimeDefault: false;
|
|
20
|
+
enumValues: [string, ...string[]];
|
|
21
|
+
baseColumn: never;
|
|
22
|
+
identity: undefined;
|
|
23
|
+
generated: undefined;
|
|
24
|
+
}, {}, {}>;
|
|
25
|
+
productId: import("drizzle-orm/pg-core").PgColumn<{
|
|
26
|
+
name: "product_id";
|
|
27
|
+
tableName: "booking_product_details";
|
|
28
|
+
dataType: "string";
|
|
29
|
+
columnType: "PgText";
|
|
30
|
+
data: string;
|
|
31
|
+
driverParam: string;
|
|
32
|
+
notNull: false;
|
|
33
|
+
hasDefault: false;
|
|
34
|
+
isPrimaryKey: false;
|
|
35
|
+
isAutoincrement: false;
|
|
36
|
+
hasRuntimeDefault: false;
|
|
37
|
+
enumValues: [string, ...string[]];
|
|
38
|
+
baseColumn: never;
|
|
39
|
+
identity: undefined;
|
|
40
|
+
generated: undefined;
|
|
41
|
+
}, {}, {}>;
|
|
42
|
+
optionId: import("drizzle-orm/pg-core").PgColumn<{
|
|
43
|
+
name: "option_id";
|
|
44
|
+
tableName: "booking_product_details";
|
|
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
|
+
}, {}, {}>;
|
|
59
|
+
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
60
|
+
name: "created_at";
|
|
61
|
+
tableName: "booking_product_details";
|
|
62
|
+
dataType: "date";
|
|
63
|
+
columnType: "PgTimestamp";
|
|
64
|
+
data: Date;
|
|
65
|
+
driverParam: string;
|
|
66
|
+
notNull: true;
|
|
67
|
+
hasDefault: true;
|
|
68
|
+
isPrimaryKey: false;
|
|
69
|
+
isAutoincrement: false;
|
|
70
|
+
hasRuntimeDefault: false;
|
|
71
|
+
enumValues: undefined;
|
|
72
|
+
baseColumn: never;
|
|
73
|
+
identity: undefined;
|
|
74
|
+
generated: undefined;
|
|
75
|
+
}, {}, {}>;
|
|
76
|
+
updatedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
77
|
+
name: "updated_at";
|
|
78
|
+
tableName: "booking_product_details";
|
|
79
|
+
dataType: "date";
|
|
80
|
+
columnType: "PgTimestamp";
|
|
81
|
+
data: Date;
|
|
82
|
+
driverParam: string;
|
|
83
|
+
notNull: true;
|
|
84
|
+
hasDefault: true;
|
|
85
|
+
isPrimaryKey: false;
|
|
86
|
+
isAutoincrement: false;
|
|
87
|
+
hasRuntimeDefault: false;
|
|
88
|
+
enumValues: undefined;
|
|
89
|
+
baseColumn: never;
|
|
90
|
+
identity: undefined;
|
|
91
|
+
generated: undefined;
|
|
92
|
+
}, {}, {}>;
|
|
93
|
+
};
|
|
94
|
+
dialect: "pg";
|
|
95
|
+
}>;
|
|
96
|
+
export type BookingProductDetail = typeof bookingProductDetails.$inferSelect;
|
|
97
|
+
export type NewBookingProductDetail = typeof bookingProductDetails.$inferInsert;
|
|
98
|
+
export declare const bookingItemProductDetails: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
99
|
+
name: "booking_item_product_details";
|
|
100
|
+
schema: undefined;
|
|
101
|
+
columns: {
|
|
102
|
+
bookingItemId: import("drizzle-orm/pg-core").PgColumn<{
|
|
103
|
+
name: "booking_item_id";
|
|
104
|
+
tableName: "booking_item_product_details";
|
|
105
|
+
dataType: "string";
|
|
106
|
+
columnType: "PgText";
|
|
107
|
+
data: string;
|
|
108
|
+
driverParam: string;
|
|
109
|
+
notNull: true;
|
|
110
|
+
hasDefault: false;
|
|
111
|
+
isPrimaryKey: true;
|
|
112
|
+
isAutoincrement: false;
|
|
113
|
+
hasRuntimeDefault: false;
|
|
114
|
+
enumValues: [string, ...string[]];
|
|
115
|
+
baseColumn: never;
|
|
116
|
+
identity: undefined;
|
|
117
|
+
generated: undefined;
|
|
118
|
+
}, {}, {}>;
|
|
119
|
+
productId: import("drizzle-orm/pg-core").PgColumn<{
|
|
120
|
+
name: "product_id";
|
|
121
|
+
tableName: "booking_item_product_details";
|
|
122
|
+
dataType: "string";
|
|
123
|
+
columnType: "PgText";
|
|
124
|
+
data: string;
|
|
125
|
+
driverParam: string;
|
|
126
|
+
notNull: false;
|
|
127
|
+
hasDefault: false;
|
|
128
|
+
isPrimaryKey: false;
|
|
129
|
+
isAutoincrement: false;
|
|
130
|
+
hasRuntimeDefault: false;
|
|
131
|
+
enumValues: [string, ...string[]];
|
|
132
|
+
baseColumn: never;
|
|
133
|
+
identity: undefined;
|
|
134
|
+
generated: undefined;
|
|
135
|
+
}, {}, {}>;
|
|
136
|
+
optionId: import("drizzle-orm/pg-core").PgColumn<{
|
|
137
|
+
name: "option_id";
|
|
138
|
+
tableName: "booking_item_product_details";
|
|
139
|
+
dataType: "string";
|
|
140
|
+
columnType: "PgText";
|
|
141
|
+
data: string;
|
|
142
|
+
driverParam: string;
|
|
143
|
+
notNull: false;
|
|
144
|
+
hasDefault: false;
|
|
145
|
+
isPrimaryKey: false;
|
|
146
|
+
isAutoincrement: false;
|
|
147
|
+
hasRuntimeDefault: false;
|
|
148
|
+
enumValues: [string, ...string[]];
|
|
149
|
+
baseColumn: never;
|
|
150
|
+
identity: undefined;
|
|
151
|
+
generated: undefined;
|
|
152
|
+
}, {}, {}>;
|
|
153
|
+
unitId: import("drizzle-orm/pg-core").PgColumn<{
|
|
154
|
+
name: "unit_id";
|
|
155
|
+
tableName: "booking_item_product_details";
|
|
156
|
+
dataType: "string";
|
|
157
|
+
columnType: "PgText";
|
|
158
|
+
data: string;
|
|
159
|
+
driverParam: string;
|
|
160
|
+
notNull: false;
|
|
161
|
+
hasDefault: false;
|
|
162
|
+
isPrimaryKey: false;
|
|
163
|
+
isAutoincrement: false;
|
|
164
|
+
hasRuntimeDefault: false;
|
|
165
|
+
enumValues: [string, ...string[]];
|
|
166
|
+
baseColumn: never;
|
|
167
|
+
identity: undefined;
|
|
168
|
+
generated: undefined;
|
|
169
|
+
}, {}, {}>;
|
|
170
|
+
supplierServiceId: import("drizzle-orm/pg-core").PgColumn<{
|
|
171
|
+
name: "supplier_service_id";
|
|
172
|
+
tableName: "booking_item_product_details";
|
|
173
|
+
dataType: "string";
|
|
174
|
+
columnType: "PgText";
|
|
175
|
+
data: string;
|
|
176
|
+
driverParam: string;
|
|
177
|
+
notNull: false;
|
|
178
|
+
hasDefault: false;
|
|
179
|
+
isPrimaryKey: false;
|
|
180
|
+
isAutoincrement: false;
|
|
181
|
+
hasRuntimeDefault: false;
|
|
182
|
+
enumValues: [string, ...string[]];
|
|
183
|
+
baseColumn: never;
|
|
184
|
+
identity: undefined;
|
|
185
|
+
generated: undefined;
|
|
186
|
+
}, {}, {}>;
|
|
187
|
+
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
188
|
+
name: "created_at";
|
|
189
|
+
tableName: "booking_item_product_details";
|
|
190
|
+
dataType: "date";
|
|
191
|
+
columnType: "PgTimestamp";
|
|
192
|
+
data: Date;
|
|
193
|
+
driverParam: string;
|
|
194
|
+
notNull: true;
|
|
195
|
+
hasDefault: true;
|
|
196
|
+
isPrimaryKey: false;
|
|
197
|
+
isAutoincrement: false;
|
|
198
|
+
hasRuntimeDefault: false;
|
|
199
|
+
enumValues: undefined;
|
|
200
|
+
baseColumn: never;
|
|
201
|
+
identity: undefined;
|
|
202
|
+
generated: undefined;
|
|
203
|
+
}, {}, {}>;
|
|
204
|
+
updatedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
205
|
+
name: "updated_at";
|
|
206
|
+
tableName: "booking_item_product_details";
|
|
207
|
+
dataType: "date";
|
|
208
|
+
columnType: "PgTimestamp";
|
|
209
|
+
data: Date;
|
|
210
|
+
driverParam: string;
|
|
211
|
+
notNull: true;
|
|
212
|
+
hasDefault: true;
|
|
213
|
+
isPrimaryKey: false;
|
|
214
|
+
isAutoincrement: false;
|
|
215
|
+
hasRuntimeDefault: false;
|
|
216
|
+
enumValues: undefined;
|
|
217
|
+
baseColumn: never;
|
|
218
|
+
identity: undefined;
|
|
219
|
+
generated: undefined;
|
|
220
|
+
}, {}, {}>;
|
|
221
|
+
};
|
|
222
|
+
dialect: "pg";
|
|
223
|
+
}>;
|
|
224
|
+
export type BookingItemProductDetail = typeof bookingItemProductDetails.$inferSelect;
|
|
225
|
+
export type NewBookingItemProductDetail = typeof bookingItemProductDetails.$inferInsert;
|
|
226
|
+
declare const bookingProductDetailSchema: z.ZodObject<{
|
|
227
|
+
productId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
228
|
+
optionId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
229
|
+
}, z.core.$strip>;
|
|
230
|
+
declare const bookingItemProductDetailSchema: z.ZodObject<{
|
|
231
|
+
productId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
232
|
+
optionId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
233
|
+
unitId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
234
|
+
supplierServiceId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
235
|
+
}, z.core.$strip>;
|
|
236
|
+
export declare const bookingProductExtensionService: {
|
|
237
|
+
getBookingDetails(db: PostgresJsDatabase, bookingId: string): Promise<{
|
|
238
|
+
bookingId: string;
|
|
239
|
+
productId: string | null;
|
|
240
|
+
optionId: string | null;
|
|
241
|
+
createdAt: Date;
|
|
242
|
+
updatedAt: Date;
|
|
243
|
+
} | null>;
|
|
244
|
+
upsertBookingDetails(db: PostgresJsDatabase, bookingId: string, data: z.infer<typeof bookingProductDetailSchema>): Promise<{
|
|
245
|
+
createdAt: Date;
|
|
246
|
+
updatedAt: Date;
|
|
247
|
+
productId: string | null;
|
|
248
|
+
optionId: string | null;
|
|
249
|
+
bookingId: string;
|
|
250
|
+
} | null>;
|
|
251
|
+
removeBookingDetails(db: PostgresJsDatabase, bookingId: string): Promise<{
|
|
252
|
+
bookingId: string;
|
|
253
|
+
} | null>;
|
|
254
|
+
getItemDetails(db: PostgresJsDatabase, bookingItemId: string): Promise<{
|
|
255
|
+
bookingItemId: string;
|
|
256
|
+
productId: string | null;
|
|
257
|
+
optionId: string | null;
|
|
258
|
+
unitId: string | null;
|
|
259
|
+
supplierServiceId: string | null;
|
|
260
|
+
createdAt: Date;
|
|
261
|
+
updatedAt: Date;
|
|
262
|
+
} | null>;
|
|
263
|
+
upsertItemDetails(db: PostgresJsDatabase, bookingItemId: string, data: z.infer<typeof bookingItemProductDetailSchema>): Promise<{
|
|
264
|
+
createdAt: Date;
|
|
265
|
+
updatedAt: Date;
|
|
266
|
+
productId: string | null;
|
|
267
|
+
optionId: string | null;
|
|
268
|
+
unitId: string | null;
|
|
269
|
+
supplierServiceId: string | null;
|
|
270
|
+
bookingItemId: string;
|
|
271
|
+
} | null>;
|
|
272
|
+
removeItemDetails(db: PostgresJsDatabase, bookingItemId: string): Promise<{
|
|
273
|
+
bookingItemId: string;
|
|
274
|
+
} | null>;
|
|
275
|
+
};
|
|
276
|
+
export declare const productsBookingExtension: HonoExtension;
|
|
277
|
+
export {};
|
|
278
|
+
//# sourceMappingURL=booking-extension.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"booking-extension.d.ts","sourceRoot":"","sources":["../src/booking-extension.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAG/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAEjE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUjC,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG,OAAO,qBAAqB,CAAC,YAAY,CAAA;AAC5E,MAAM,MAAM,uBAAuB,GAAG,OAAO,qBAAqB,CAAC,YAAY,CAAA;AAE/E,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiBrC,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG,OAAO,yBAAyB,CAAC,YAAY,CAAA;AACpF,MAAM,MAAM,2BAA2B,GAAG,OAAO,yBAAyB,CAAC,YAAY,CAAA;AAIvF,QAAA,MAAM,0BAA0B;;;iBAG9B,CAAA;AAEF,QAAA,MAAM,8BAA8B;;;;;iBAKlC,CAAA;AAIF,eAAO,MAAM,8BAA8B;0BACb,kBAAkB,aAAa,MAAM;;;;;;;6BAU3D,kBAAkB,aACX,MAAM,QACX,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC;;;;;;;6BAqBnB,kBAAkB,aAAa,MAAM;;;uBAQ3C,kBAAkB,iBAAiB,MAAM;;;;;;;;;0BAU5D,kBAAkB,iBACP,MAAM,QACf,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC;;;;;;;;;0BAyB1B,kBAAkB,iBAAiB,MAAM;;;CAOtE,CAAA;AAoFD,eAAO,MAAM,wBAAwB,EAAE,aAGtC,CAAA"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { parseJsonBody } from "@voyant-travel/hono";
|
|
2
|
+
import { eq } from "drizzle-orm";
|
|
3
|
+
import { index, pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
|
4
|
+
import { Hono } from "hono";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
// ---------- schemas ----------
|
|
7
|
+
export const bookingProductDetails = pgTable("booking_product_details", {
|
|
8
|
+
bookingId: text("booking_id").primaryKey(),
|
|
9
|
+
productId: text("product_id"),
|
|
10
|
+
optionId: text("option_id"),
|
|
11
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
12
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
13
|
+
}, (t) => [index("idx_bpd_product").on(t.productId), index("idx_bpd_option").on(t.optionId)]);
|
|
14
|
+
export const bookingItemProductDetails = pgTable("booking_item_product_details", {
|
|
15
|
+
bookingItemId: text("booking_item_id").primaryKey(),
|
|
16
|
+
productId: text("product_id"),
|
|
17
|
+
optionId: text("option_id"),
|
|
18
|
+
unitId: text("unit_id"),
|
|
19
|
+
supplierServiceId: text("supplier_service_id"),
|
|
20
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
21
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
22
|
+
}, (t) => [
|
|
23
|
+
index("idx_bipd_product").on(t.productId),
|
|
24
|
+
index("idx_bipd_option").on(t.optionId),
|
|
25
|
+
index("idx_bipd_unit").on(t.unitId),
|
|
26
|
+
index("idx_bipd_supplier_service").on(t.supplierServiceId),
|
|
27
|
+
]);
|
|
28
|
+
// ---------- validation ----------
|
|
29
|
+
const bookingProductDetailSchema = z.object({
|
|
30
|
+
productId: z.string().optional().nullable(),
|
|
31
|
+
optionId: z.string().optional().nullable(),
|
|
32
|
+
});
|
|
33
|
+
const bookingItemProductDetailSchema = z.object({
|
|
34
|
+
productId: z.string().optional().nullable(),
|
|
35
|
+
optionId: z.string().optional().nullable(),
|
|
36
|
+
unitId: z.string().optional().nullable(),
|
|
37
|
+
supplierServiceId: z.string().optional().nullable(),
|
|
38
|
+
});
|
|
39
|
+
// ---------- service ----------
|
|
40
|
+
export const bookingProductExtensionService = {
|
|
41
|
+
async getBookingDetails(db, bookingId) {
|
|
42
|
+
const [row] = await db
|
|
43
|
+
.select()
|
|
44
|
+
.from(bookingProductDetails)
|
|
45
|
+
.where(eq(bookingProductDetails.bookingId, bookingId))
|
|
46
|
+
.limit(1);
|
|
47
|
+
return row ?? null;
|
|
48
|
+
},
|
|
49
|
+
async upsertBookingDetails(db, bookingId, data) {
|
|
50
|
+
const [row] = await db
|
|
51
|
+
.insert(bookingProductDetails)
|
|
52
|
+
.values({
|
|
53
|
+
bookingId,
|
|
54
|
+
productId: data.productId ?? null,
|
|
55
|
+
optionId: data.optionId ?? null,
|
|
56
|
+
})
|
|
57
|
+
.onConflictDoUpdate({
|
|
58
|
+
target: bookingProductDetails.bookingId,
|
|
59
|
+
set: {
|
|
60
|
+
productId: data.productId ?? null,
|
|
61
|
+
optionId: data.optionId ?? null,
|
|
62
|
+
updatedAt: new Date(),
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
.returning();
|
|
66
|
+
return row ?? null;
|
|
67
|
+
},
|
|
68
|
+
async removeBookingDetails(db, bookingId) {
|
|
69
|
+
const [row] = await db
|
|
70
|
+
.delete(bookingProductDetails)
|
|
71
|
+
.where(eq(bookingProductDetails.bookingId, bookingId))
|
|
72
|
+
.returning({ bookingId: bookingProductDetails.bookingId });
|
|
73
|
+
return row ?? null;
|
|
74
|
+
},
|
|
75
|
+
async getItemDetails(db, bookingItemId) {
|
|
76
|
+
const [row] = await db
|
|
77
|
+
.select()
|
|
78
|
+
.from(bookingItemProductDetails)
|
|
79
|
+
.where(eq(bookingItemProductDetails.bookingItemId, bookingItemId))
|
|
80
|
+
.limit(1);
|
|
81
|
+
return row ?? null;
|
|
82
|
+
},
|
|
83
|
+
async upsertItemDetails(db, bookingItemId, data) {
|
|
84
|
+
const [row] = await db
|
|
85
|
+
.insert(bookingItemProductDetails)
|
|
86
|
+
.values({
|
|
87
|
+
bookingItemId,
|
|
88
|
+
productId: data.productId ?? null,
|
|
89
|
+
optionId: data.optionId ?? null,
|
|
90
|
+
unitId: data.unitId ?? null,
|
|
91
|
+
supplierServiceId: data.supplierServiceId ?? null,
|
|
92
|
+
})
|
|
93
|
+
.onConflictDoUpdate({
|
|
94
|
+
target: bookingItemProductDetails.bookingItemId,
|
|
95
|
+
set: {
|
|
96
|
+
productId: data.productId ?? null,
|
|
97
|
+
optionId: data.optionId ?? null,
|
|
98
|
+
unitId: data.unitId ?? null,
|
|
99
|
+
supplierServiceId: data.supplierServiceId ?? null,
|
|
100
|
+
updatedAt: new Date(),
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
.returning();
|
|
104
|
+
return row ?? null;
|
|
105
|
+
},
|
|
106
|
+
async removeItemDetails(db, bookingItemId) {
|
|
107
|
+
const [row] = await db
|
|
108
|
+
.delete(bookingItemProductDetails)
|
|
109
|
+
.where(eq(bookingItemProductDetails.bookingItemId, bookingItemId))
|
|
110
|
+
.returning({ bookingItemId: bookingItemProductDetails.bookingItemId });
|
|
111
|
+
return row ?? null;
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
const bookingProductExtensionRoutes = new Hono()
|
|
115
|
+
.get("/:bookingId/product-details", async (c) => {
|
|
116
|
+
const row = await bookingProductExtensionService.getBookingDetails(c.get("db"), c.req.param("bookingId"));
|
|
117
|
+
if (!row) {
|
|
118
|
+
return c.json({ data: null });
|
|
119
|
+
}
|
|
120
|
+
return c.json({ data: row });
|
|
121
|
+
})
|
|
122
|
+
.put("/:bookingId/product-details", async (c) => {
|
|
123
|
+
const data = await parseJsonBody(c, bookingProductDetailSchema);
|
|
124
|
+
const row = await bookingProductExtensionService.upsertBookingDetails(c.get("db"), c.req.param("bookingId"), data);
|
|
125
|
+
return c.json({ data: row });
|
|
126
|
+
})
|
|
127
|
+
.delete("/:bookingId/product-details", async (c) => {
|
|
128
|
+
const row = await bookingProductExtensionService.removeBookingDetails(c.get("db"), c.req.param("bookingId"));
|
|
129
|
+
if (!row) {
|
|
130
|
+
return c.json({ error: "Not found" }, 404);
|
|
131
|
+
}
|
|
132
|
+
return c.json({ success: true });
|
|
133
|
+
})
|
|
134
|
+
.get("/:bookingId/items/:itemId/product-details", async (c) => {
|
|
135
|
+
const row = await bookingProductExtensionService.getItemDetails(c.get("db"), c.req.param("itemId"));
|
|
136
|
+
if (!row) {
|
|
137
|
+
return c.json({ data: null });
|
|
138
|
+
}
|
|
139
|
+
return c.json({ data: row });
|
|
140
|
+
})
|
|
141
|
+
.put("/:bookingId/items/:itemId/product-details", async (c) => {
|
|
142
|
+
const data = await parseJsonBody(c, bookingItemProductDetailSchema);
|
|
143
|
+
const row = await bookingProductExtensionService.upsertItemDetails(c.get("db"), c.req.param("itemId"), data);
|
|
144
|
+
return c.json({ data: row });
|
|
145
|
+
})
|
|
146
|
+
.delete("/:bookingId/items/:itemId/product-details", async (c) => {
|
|
147
|
+
const row = await bookingProductExtensionService.removeItemDetails(c.get("db"), c.req.param("itemId"));
|
|
148
|
+
if (!row) {
|
|
149
|
+
return c.json({ error: "Not found" }, 404);
|
|
150
|
+
}
|
|
151
|
+
return c.json({ success: true });
|
|
152
|
+
});
|
|
153
|
+
// ---------- extension export ----------
|
|
154
|
+
const productsBookingExtensionDef = {
|
|
155
|
+
name: "products-booking",
|
|
156
|
+
module: "bookings",
|
|
157
|
+
};
|
|
158
|
+
export const productsBookingExtension = {
|
|
159
|
+
extension: productsBookingExtensionDef,
|
|
160
|
+
routes: bookingProductExtensionRoutes,
|
|
161
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Catalog plane field policy for product → departures denormalization.
|
|
3
|
+
*
|
|
4
|
+
* These fields don't live on the `products` table — they're aggregated
|
|
5
|
+
* from `availability_slots` (filtered to `status = 'open'` and future
|
|
6
|
+
* `startsAt`) at index time and projected onto the product search
|
|
7
|
+
* document. See `docs/architecture/catalog-architecture.md` §5.4.
|
|
8
|
+
*
|
|
9
|
+
* Storefront product cards filter and display by departure surface:
|
|
10
|
+
* - "departing in May" (filter: `departureMonths[]` includes "2026-05")
|
|
11
|
+
* - "available this weekend" (filter: `departureDates[]` overlap)
|
|
12
|
+
* - "available now" (filter: `hasUpcomingDeparture:true`)
|
|
13
|
+
* - sort by next departure (`nextDepartureDate` asc)
|
|
14
|
+
* - show capacity badge (`availableUnitsTotal`)
|
|
15
|
+
*
|
|
16
|
+
* Wire this policy into the products registry by composing with
|
|
17
|
+
* `productCatalogPolicy`:
|
|
18
|
+
*
|
|
19
|
+
* const registry = createFieldPolicyRegistry([
|
|
20
|
+
* ...productCatalogPolicy,
|
|
21
|
+
* ...productDeparturesCatalogPolicy,
|
|
22
|
+
* ])
|
|
23
|
+
*
|
|
24
|
+
* and wire `createProductDeparturesProjectionExtension` into
|
|
25
|
+
* `createProductDocumentBuilder` so the values land in the doc.
|
|
26
|
+
*
|
|
27
|
+
* Reindex semantics: every aggregation depends on `now()` — the same
|
|
28
|
+
* product reindexed an hour later will produce slightly different
|
|
29
|
+
* documents because the future window slides. Operators rely on the
|
|
30
|
+
* channel-push reconciler + scheduled refresh to keep these fields warm
|
|
31
|
+
* without storming the indexer on each tick. Per architecture §5.4,
|
|
32
|
+
* `reindex: "facet-affecting"` is the right tier — these fields drive
|
|
33
|
+
* facets but aren't on the merchandisable hot path.
|
|
34
|
+
*
|
|
35
|
+
* Out of scope here:
|
|
36
|
+
* - Sold-out / cancelled / closed slots. The projection only counts
|
|
37
|
+
* `status = 'open'` so storefront filters never surface unavailable
|
|
38
|
+
* departures. A "show sold-out" toggle is a query-time concern.
|
|
39
|
+
* - Per-option splits. Today's projection rolls up to the product;
|
|
40
|
+
* per-option faceting is a follow-up if storefronts need it.
|
|
41
|
+
* - Duration buckets (nights/days). Slots carry these but products
|
|
42
|
+
* with mixed-duration slots can't be summarized in one number.
|
|
43
|
+
*/
|
|
44
|
+
import { type FieldPolicyInput } from "@voyant-travel/catalog/contract";
|
|
45
|
+
declare const PRODUCT_DEPARTURES_FIELD_POLICY: FieldPolicyInput[];
|
|
46
|
+
/**
|
|
47
|
+
* Resolved departures policy. Compose with `productCatalogPolicy` when
|
|
48
|
+
* building the products registry.
|
|
49
|
+
*/
|
|
50
|
+
export declare const productDeparturesCatalogPolicy: import("@voyant-travel/catalog").FieldPolicy[];
|
|
51
|
+
export { PRODUCT_DEPARTURES_FIELD_POLICY };
|
|
52
|
+
//# sourceMappingURL=catalog-policy-departures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog-policy-departures.d.ts","sourceRoot":"","sources":["../src/catalog-policy-departures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,iCAAiC,CAAA;AAE1F,QAAA,MAAM,+BAA+B,EAAE,gBAAgB,EAyHtD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,8BAA8B,gDAAqD,CAAA;AAEhG,OAAO,EAAE,+BAA+B,EAAE,CAAA"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Catalog plane field policy for product → departures denormalization.
|
|
3
|
+
*
|
|
4
|
+
* These fields don't live on the `products` table — they're aggregated
|
|
5
|
+
* from `availability_slots` (filtered to `status = 'open'` and future
|
|
6
|
+
* `startsAt`) at index time and projected onto the product search
|
|
7
|
+
* document. See `docs/architecture/catalog-architecture.md` §5.4.
|
|
8
|
+
*
|
|
9
|
+
* Storefront product cards filter and display by departure surface:
|
|
10
|
+
* - "departing in May" (filter: `departureMonths[]` includes "2026-05")
|
|
11
|
+
* - "available this weekend" (filter: `departureDates[]` overlap)
|
|
12
|
+
* - "available now" (filter: `hasUpcomingDeparture:true`)
|
|
13
|
+
* - sort by next departure (`nextDepartureDate` asc)
|
|
14
|
+
* - show capacity badge (`availableUnitsTotal`)
|
|
15
|
+
*
|
|
16
|
+
* Wire this policy into the products registry by composing with
|
|
17
|
+
* `productCatalogPolicy`:
|
|
18
|
+
*
|
|
19
|
+
* const registry = createFieldPolicyRegistry([
|
|
20
|
+
* ...productCatalogPolicy,
|
|
21
|
+
* ...productDeparturesCatalogPolicy,
|
|
22
|
+
* ])
|
|
23
|
+
*
|
|
24
|
+
* and wire `createProductDeparturesProjectionExtension` into
|
|
25
|
+
* `createProductDocumentBuilder` so the values land in the doc.
|
|
26
|
+
*
|
|
27
|
+
* Reindex semantics: every aggregation depends on `now()` — the same
|
|
28
|
+
* product reindexed an hour later will produce slightly different
|
|
29
|
+
* documents because the future window slides. Operators rely on the
|
|
30
|
+
* channel-push reconciler + scheduled refresh to keep these fields warm
|
|
31
|
+
* without storming the indexer on each tick. Per architecture §5.4,
|
|
32
|
+
* `reindex: "facet-affecting"` is the right tier — these fields drive
|
|
33
|
+
* facets but aren't on the merchandisable hot path.
|
|
34
|
+
*
|
|
35
|
+
* Out of scope here:
|
|
36
|
+
* - Sold-out / cancelled / closed slots. The projection only counts
|
|
37
|
+
* `status = 'open'` so storefront filters never surface unavailable
|
|
38
|
+
* departures. A "show sold-out" toggle is a query-time concern.
|
|
39
|
+
* - Per-option splits. Today's projection rolls up to the product;
|
|
40
|
+
* per-option faceting is a follow-up if storefronts need it.
|
|
41
|
+
* - Duration buckets (nights/days). Slots carry these but products
|
|
42
|
+
* with mixed-duration slots can't be summarized in one number.
|
|
43
|
+
*/
|
|
44
|
+
import { defineFieldPolicy } from "@voyant-travel/catalog/contract";
|
|
45
|
+
const PRODUCT_DEPARTURES_FIELD_POLICY = [
|
|
46
|
+
// ── Earliest open future departure ──────────────────────────────────────
|
|
47
|
+
// `nextDepartureAt` is the timestamptz for storefront display metadata.
|
|
48
|
+
// `nextDepartureDate` is the slot's local calendar date and backs catalog
|
|
49
|
+
// sort by soonest available.
|
|
50
|
+
{
|
|
51
|
+
path: "nextDepartureAt",
|
|
52
|
+
class: "structural",
|
|
53
|
+
merge: "source-only",
|
|
54
|
+
drift: "low",
|
|
55
|
+
reindex: "facet-affecting",
|
|
56
|
+
snapshot: "on-book",
|
|
57
|
+
query: "indexed-column",
|
|
58
|
+
localized: false,
|
|
59
|
+
visibility: ["staff", "customer", "partner"],
|
|
60
|
+
editRole: "none",
|
|
61
|
+
overrideFriction: "none",
|
|
62
|
+
sourceFreshness: "sync",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
path: "nextDepartureDate",
|
|
66
|
+
class: "structural",
|
|
67
|
+
merge: "source-only",
|
|
68
|
+
drift: "low",
|
|
69
|
+
reindex: "facet-affecting",
|
|
70
|
+
snapshot: "on-book",
|
|
71
|
+
query: "indexed-column",
|
|
72
|
+
localized: false,
|
|
73
|
+
visibility: ["staff", "customer", "partner"],
|
|
74
|
+
editRole: "none",
|
|
75
|
+
overrideFriction: "none",
|
|
76
|
+
sourceFreshness: "sync",
|
|
77
|
+
},
|
|
78
|
+
// ── Existence + count facets ─────────────────────────────────────────────
|
|
79
|
+
// `hasUpcomingDeparture` is the cheap "available now" filter. The count
|
|
80
|
+
// lets storefront sort by "lots of options available" without exposing
|
|
81
|
+
// the date list to the query layer.
|
|
82
|
+
{
|
|
83
|
+
path: "hasUpcomingDeparture",
|
|
84
|
+
class: "structural",
|
|
85
|
+
merge: "source-only",
|
|
86
|
+
drift: "low",
|
|
87
|
+
reindex: "facet-affecting",
|
|
88
|
+
snapshot: "on-book",
|
|
89
|
+
query: "indexed-column",
|
|
90
|
+
localized: false,
|
|
91
|
+
visibility: ["staff", "customer", "partner"],
|
|
92
|
+
editRole: "none",
|
|
93
|
+
overrideFriction: "none",
|
|
94
|
+
sourceFreshness: "sync",
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
path: "upcomingDepartureCount",
|
|
98
|
+
class: "structural",
|
|
99
|
+
merge: "source-only",
|
|
100
|
+
drift: "low",
|
|
101
|
+
reindex: "facet-affecting",
|
|
102
|
+
snapshot: "on-book",
|
|
103
|
+
query: "indexed-column",
|
|
104
|
+
localized: false,
|
|
105
|
+
visibility: ["staff", "customer", "partner"],
|
|
106
|
+
editRole: "none",
|
|
107
|
+
overrideFriction: "none",
|
|
108
|
+
sourceFreshness: "sync",
|
|
109
|
+
},
|
|
110
|
+
// ── Calendar-bucket facets ──────────────────────────────────────────────
|
|
111
|
+
// `departureDates[]` powers fine-grained "this weekend" / "next week"
|
|
112
|
+
// filters; capped at 180 days of distinct calendar dates so document
|
|
113
|
+
// size stays bounded for daily-slot products. `departureMonths[]` is
|
|
114
|
+
// the longer-tail facet — 24 months of "YYYY-MM" tokens covers an
|
|
115
|
+
// operator's typical forward-published inventory horizon.
|
|
116
|
+
{
|
|
117
|
+
path: "departureDates[]",
|
|
118
|
+
class: "structural",
|
|
119
|
+
merge: "source-only",
|
|
120
|
+
drift: "low",
|
|
121
|
+
reindex: "facet-affecting",
|
|
122
|
+
snapshot: "on-book",
|
|
123
|
+
query: "indexed-column",
|
|
124
|
+
localized: false,
|
|
125
|
+
visibility: ["staff", "customer", "partner"],
|
|
126
|
+
editRole: "none",
|
|
127
|
+
overrideFriction: "none",
|
|
128
|
+
sourceFreshness: "sync",
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
path: "departureMonths[]",
|
|
132
|
+
class: "structural",
|
|
133
|
+
merge: "source-only",
|
|
134
|
+
drift: "low",
|
|
135
|
+
reindex: "facet-affecting",
|
|
136
|
+
snapshot: "on-book",
|
|
137
|
+
query: "indexed-column",
|
|
138
|
+
localized: false,
|
|
139
|
+
visibility: ["staff", "customer", "partner"],
|
|
140
|
+
editRole: "none",
|
|
141
|
+
overrideFriction: "none",
|
|
142
|
+
sourceFreshness: "sync",
|
|
143
|
+
},
|
|
144
|
+
// ── Capacity ────────────────────────────────────────────────────────────
|
|
145
|
+
// Sum of `remaining_pax` across upcoming open slots. `null` when ANY
|
|
146
|
+
// counted slot is unlimited — unbounded capacity can't be summarized in
|
|
147
|
+
// a number, and emitting a partial sum would mislead the storefront
|
|
148
|
+
// ("3 seats left" when one slot is actually unlimited).
|
|
149
|
+
{
|
|
150
|
+
path: "availableUnitsTotal",
|
|
151
|
+
class: "structural",
|
|
152
|
+
merge: "source-only",
|
|
153
|
+
drift: "low",
|
|
154
|
+
reindex: "facet-affecting",
|
|
155
|
+
snapshot: "on-book",
|
|
156
|
+
query: "indexed-column",
|
|
157
|
+
localized: false,
|
|
158
|
+
visibility: ["staff", "customer", "partner"],
|
|
159
|
+
editRole: "none",
|
|
160
|
+
overrideFriction: "none",
|
|
161
|
+
sourceFreshness: "sync",
|
|
162
|
+
},
|
|
163
|
+
];
|
|
164
|
+
/**
|
|
165
|
+
* Resolved departures policy. Compose with `productCatalogPolicy` when
|
|
166
|
+
* building the products registry.
|
|
167
|
+
*/
|
|
168
|
+
export const productDeparturesCatalogPolicy = defineFieldPolicy(PRODUCT_DEPARTURES_FIELD_POLICY);
|
|
169
|
+
export { PRODUCT_DEPARTURES_FIELD_POLICY };
|