@voyant-travel/catalog 0.117.2
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/README.md +190 -0
- package/dist/adapter/booking-forwarding.d.ts +2 -0
- package/dist/adapter/booking-forwarding.d.ts.map +1 -0
- package/dist/adapter/booking-forwarding.js +1 -0
- package/dist/adapter/channel-push-contracts.d.ts +2 -0
- package/dist/adapter/channel-push-contracts.d.ts.map +1 -0
- package/dist/adapter/channel-push-contracts.js +1 -0
- package/dist/adapter/contract.d.ts +2 -0
- package/dist/adapter/contract.d.ts.map +1 -0
- package/dist/adapter/contract.js +1 -0
- package/dist/adapter/contract.test.d.ts +2 -0
- package/dist/adapter/contract.test.d.ts.map +1 -0
- package/dist/adapter/contract.test.js +390 -0
- package/dist/adapter/provider-contracts.d.ts +2 -0
- package/dist/adapter/provider-contracts.d.ts.map +1 -0
- package/dist/adapter/provider-contracts.js +1 -0
- package/dist/adapter/provider-contracts.test.d.ts +2 -0
- package/dist/adapter/provider-contracts.test.d.ts.map +1 -0
- package/dist/adapter/provider-contracts.test.js +206 -0
- package/dist/adapter/schemas.d.ts +2 -0
- package/dist/adapter/schemas.d.ts.map +1 -0
- package/dist/adapter/schemas.js +1 -0
- package/dist/adapter/schemas.test.d.ts +2 -0
- package/dist/adapter/schemas.test.d.ts.map +1 -0
- package/dist/adapter/schemas.test.js +344 -0
- package/dist/booking-engine/book.d.ts +124 -0
- package/dist/booking-engine/book.d.ts.map +1 -0
- package/dist/booking-engine/book.js +311 -0
- package/dist/booking-engine/cancel.d.ts +40 -0
- package/dist/booking-engine/cancel.d.ts.map +1 -0
- package/dist/booking-engine/cancel.js +56 -0
- package/dist/booking-engine/checkout-finalize.d.ts +146 -0
- package/dist/booking-engine/checkout-finalize.d.ts.map +1 -0
- package/dist/booking-engine/checkout-finalize.js +132 -0
- package/dist/booking-engine/contracts.d.ts +9 -0
- package/dist/booking-engine/contracts.d.ts.map +1 -0
- package/dist/booking-engine/contracts.js +8 -0
- package/dist/booking-engine/contracts.test.d.ts +2 -0
- package/dist/booking-engine/contracts.test.d.ts.map +1 -0
- package/dist/booking-engine/contracts.test.js +116 -0
- package/dist/booking-engine/draft-shape.d.ts +10 -0
- package/dist/booking-engine/draft-shape.d.ts.map +1 -0
- package/dist/booking-engine/draft-shape.js +9 -0
- package/dist/booking-engine/draft-shape.test.d.ts +2 -0
- package/dist/booking-engine/draft-shape.test.d.ts.map +1 -0
- package/dist/booking-engine/draft-shape.test.js +74 -0
- package/dist/booking-engine/drafts-schema.d.ts +302 -0
- package/dist/booking-engine/drafts-schema.d.ts.map +1 -0
- package/dist/booking-engine/drafts-schema.js +53 -0
- package/dist/booking-engine/drafts-service.d.ts +41 -0
- package/dist/booking-engine/drafts-service.d.ts.map +1 -0
- package/dist/booking-engine/drafts-service.js +108 -0
- package/dist/booking-engine/errors.d.ts +81 -0
- package/dist/booking-engine/errors.d.ts.map +1 -0
- package/dist/booking-engine/errors.js +113 -0
- package/dist/booking-engine/index.d.ts +36 -0
- package/dist/booking-engine/index.d.ts.map +1 -0
- package/dist/booking-engine/index.js +34 -0
- package/dist/booking-engine/orders.d.ts +41 -0
- package/dist/booking-engine/orders.d.ts.map +1 -0
- package/dist/booking-engine/orders.js +49 -0
- package/dist/booking-engine/owned-handler.d.ts +166 -0
- package/dist/booking-engine/owned-handler.d.ts.map +1 -0
- package/dist/booking-engine/owned-handler.js +50 -0
- package/dist/booking-engine/owned-handler.test.d.ts +2 -0
- package/dist/booking-engine/owned-handler.test.d.ts.map +1 -0
- package/dist/booking-engine/owned-handler.test.js +63 -0
- package/dist/booking-engine/promotions-contract.d.ts +8 -0
- package/dist/booking-engine/promotions-contract.d.ts.map +1 -0
- package/dist/booking-engine/promotions-contract.js +7 -0
- package/dist/booking-engine/quote-enricher.test.d.ts +12 -0
- package/dist/booking-engine/quote-enricher.test.d.ts.map +1 -0
- package/dist/booking-engine/quote-enricher.test.js +138 -0
- package/dist/booking-engine/quote.d.ts +163 -0
- package/dist/booking-engine/quote.d.ts.map +1 -0
- package/dist/booking-engine/quote.js +259 -0
- package/dist/booking-engine/registry.d.ts +85 -0
- package/dist/booking-engine/registry.d.ts.map +1 -0
- package/dist/booking-engine/registry.js +118 -0
- package/dist/booking-engine/registry.test.d.ts +2 -0
- package/dist/booking-engine/registry.test.d.ts.map +1 -0
- package/dist/booking-engine/registry.test.js +132 -0
- package/dist/booking-engine/routes-contracts.d.ts +169 -0
- package/dist/booking-engine/routes-contracts.d.ts.map +1 -0
- package/dist/booking-engine/routes-contracts.js +63 -0
- package/dist/booking-engine/routes.d.ts +7 -0
- package/dist/booking-engine/routes.d.ts.map +1 -0
- package/dist/booking-engine/routes.js +443 -0
- package/dist/booking-engine/routes.test.d.ts +2 -0
- package/dist/booking-engine/routes.test.d.ts.map +1 -0
- package/dist/booking-engine/routes.test.js +304 -0
- package/dist/booking-engine/schema.d.ts +455 -0
- package/dist/booking-engine/schema.d.ts.map +1 -0
- package/dist/booking-engine/schema.js +75 -0
- package/dist/booking-engine/snapshot-content.d.ts +120 -0
- package/dist/booking-engine/snapshot-content.d.ts.map +1 -0
- package/dist/booking-engine/snapshot-content.js +110 -0
- package/dist/booking-engine/snapshot-content.test.d.ts +2 -0
- package/dist/booking-engine/snapshot-content.test.d.ts.map +1 -0
- package/dist/booking-engine/snapshot-content.test.js +213 -0
- package/dist/booking-engine/sync.d.ts +136 -0
- package/dist/booking-engine/sync.d.ts.map +1 -0
- package/dist/booking-engine/sync.js +177 -0
- package/dist/booking-engine/sync.test.d.ts +2 -0
- package/dist/booking-engine/sync.test.d.ts.map +1 -0
- package/dist/booking-engine/sync.test.js +377 -0
- package/dist/contract.d.ts +2 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +1 -0
- package/dist/contract.test.d.ts +2 -0
- package/dist/contract.test.d.ts.map +1 -0
- package/dist/contract.test.js +107 -0
- package/dist/drift/events.d.ts +2 -0
- package/dist/drift/events.d.ts.map +1 -0
- package/dist/drift/events.js +1 -0
- package/dist/drift/events.test.d.ts +2 -0
- package/dist/drift/events.test.d.ts.map +1 -0
- package/dist/drift/events.test.js +100 -0
- package/dist/embeddings/contract.d.ts +85 -0
- package/dist/embeddings/contract.d.ts.map +1 -0
- package/dist/embeddings/contract.js +42 -0
- package/dist/embeddings/contract.test.d.ts +2 -0
- package/dist/embeddings/contract.test.d.ts.map +1 -0
- package/dist/embeddings/contract.test.js +30 -0
- package/dist/embeddings/gemini.d.ts +110 -0
- package/dist/embeddings/gemini.d.ts.map +1 -0
- package/dist/embeddings/gemini.js +118 -0
- package/dist/embeddings/gemini.test.d.ts +2 -0
- package/dist/embeddings/gemini.test.d.ts.map +1 -0
- package/dist/embeddings/gemini.test.js +132 -0
- package/dist/embeddings/model-registry.d.ts +62 -0
- package/dist/embeddings/model-registry.d.ts.map +1 -0
- package/dist/embeddings/model-registry.js +78 -0
- package/dist/embeddings/model-registry.test.d.ts +2 -0
- package/dist/embeddings/model-registry.test.d.ts.map +1 -0
- package/dist/embeddings/model-registry.test.js +81 -0
- package/dist/embeddings/openai.d.ts +81 -0
- package/dist/embeddings/openai.d.ts.map +1 -0
- package/dist/embeddings/openai.js +123 -0
- package/dist/embeddings/openai.test.d.ts +2 -0
- package/dist/embeddings/openai.test.d.ts.map +1 -0
- package/dist/embeddings/openai.test.js +164 -0
- package/dist/events/taxonomy.d.ts +158 -0
- package/dist/events/taxonomy.d.ts.map +1 -0
- package/dist/events/taxonomy.js +99 -0
- package/dist/events/taxonomy.test.d.ts +2 -0
- package/dist/events/taxonomy.test.d.ts.map +1 -0
- package/dist/events/taxonomy.test.js +48 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/indexer/contract.d.ts +203 -0
- package/dist/indexer/contract.d.ts.map +1 -0
- package/dist/indexer/contract.js +16 -0
- package/dist/indexer/typesense-search-query.d.ts +31 -0
- package/dist/indexer/typesense-search-query.d.ts.map +1 -0
- package/dist/indexer/typesense-search-query.js +185 -0
- package/dist/indexer/typesense.d.ts +105 -0
- package/dist/indexer/typesense.d.ts.map +1 -0
- package/dist/indexer/typesense.js +394 -0
- package/dist/indexer/typesense.test.d.ts +2 -0
- package/dist/indexer/typesense.test.d.ts.map +1 -0
- package/dist/indexer/typesense.test.js +253 -0
- package/dist/overlay/resolver.d.ts +101 -0
- package/dist/overlay/resolver.d.ts.map +1 -0
- package/dist/overlay/resolver.js +167 -0
- package/dist/overlay/resolver.test.d.ts +2 -0
- package/dist/overlay/resolver.test.d.ts.map +1 -0
- package/dist/overlay/resolver.test.js +179 -0
- package/dist/overlay/schema.d.ts +266 -0
- package/dist/overlay/schema.d.ts.map +1 -0
- package/dist/overlay/schema.js +71 -0
- package/dist/provenance.d.ts +2 -0
- package/dist/provenance.d.ts.map +1 -0
- package/dist/provenance.js +1 -0
- package/dist/schema-sourced-entries.d.ts +344 -0
- package/dist/schema-sourced-entries.d.ts.map +1 -0
- package/dist/schema-sourced-entries.js +75 -0
- package/dist/schema.d.ts +21 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +20 -0
- package/dist/search/federate.d.ts +58 -0
- package/dist/search/federate.d.ts.map +1 -0
- package/dist/search/federate.js +103 -0
- package/dist/search/federate.test.d.ts +2 -0
- package/dist/search/federate.test.d.ts.map +1 -0
- package/dist/search/federate.test.js +146 -0
- package/dist/search/rerank.d.ts +77 -0
- package/dist/search/rerank.d.ts.map +1 -0
- package/dist/search/rerank.js +68 -0
- package/dist/search/rerank.test.d.ts +2 -0
- package/dist/search/rerank.test.d.ts.map +1 -0
- package/dist/search/rerank.test.js +60 -0
- package/dist/search/routes.d.ts +144 -0
- package/dist/search/routes.d.ts.map +1 -0
- package/dist/search/routes.js +288 -0
- package/dist/search/routes.test.d.ts +2 -0
- package/dist/search/routes.test.d.ts.map +1 -0
- package/dist/search/routes.test.js +322 -0
- package/dist/search/semantic.d.ts +63 -0
- package/dist/search/semantic.d.ts.map +1 -0
- package/dist/search/semantic.js +75 -0
- package/dist/search/semantic.test.d.ts +2 -0
- package/dist/search/semantic.test.d.ts.map +1 -0
- package/dist/search/semantic.test.js +143 -0
- package/dist/services/build-indexer-document.test.d.ts +2 -0
- package/dist/services/build-indexer-document.test.d.ts.map +1 -0
- package/dist/services/build-indexer-document.test.js +102 -0
- package/dist/services/content-service.d.ts +125 -0
- package/dist/services/content-service.d.ts.map +1 -0
- package/dist/services/content-service.js +139 -0
- package/dist/services/content-service.test.d.ts +2 -0
- package/dist/services/content-service.test.d.ts.map +1 -0
- package/dist/services/content-service.test.js +322 -0
- package/dist/services/indexer-service.d.ts +109 -0
- package/dist/services/indexer-service.d.ts.map +1 -0
- package/dist/services/indexer-service.js +123 -0
- package/dist/services/indexer-service.test.d.ts +2 -0
- package/dist/services/indexer-service.test.d.ts.map +1 -0
- package/dist/services/indexer-service.test.js +176 -0
- package/dist/services/overlay-service.d.ts +108 -0
- package/dist/services/overlay-service.d.ts.map +1 -0
- package/dist/services/overlay-service.js +211 -0
- package/dist/services/overlay-service.test.d.ts +2 -0
- package/dist/services/overlay-service.test.d.ts.map +1 -0
- package/dist/services/overlay-service.test.js +79 -0
- package/dist/services/snapshot-builder.test.d.ts +2 -0
- package/dist/services/snapshot-builder.test.d.ts.map +1 -0
- package/dist/services/snapshot-builder.test.js +93 -0
- package/dist/services/snapshot-service.d.ts +78 -0
- package/dist/services/snapshot-service.d.ts.map +1 -0
- package/dist/services/snapshot-service.js +165 -0
- package/dist/services/sourced-entry-service.d.ts +142 -0
- package/dist/services/sourced-entry-service.d.ts.map +1 -0
- package/dist/services/sourced-entry-service.js +203 -0
- package/dist/services/sourced-entry-service.test.d.ts +10 -0
- package/dist/services/sourced-entry-service.test.d.ts.map +1 -0
- package/dist/services/sourced-entry-service.test.js +66 -0
- package/dist/snapshot/schema.d.ts +362 -0
- package/dist/snapshot/schema.d.ts.map +1 -0
- package/dist/snapshot/schema.js +102 -0
- package/package.json +210 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { adapterCapabilitiesSchema, cancelRequestSchema, cancelResultSchema, catalogProjectionSchema, connectionStateSchema, discoveryCursorSchema, discoveryPageSchema, getContentRequestSchema, getContentResultSchema, getReservationRequestSchema, getReservationResultSchema, listReservationsPageSchema, listReservationsQuerySchema, liveResolveRequestSchema, liveResolveResultSchema, provenanceSchema, pushAvailabilityRequestSchema, pushAvailabilityResultSchema, pushBookingRequestSchema, pushBookingResultSchema, pushContentRequestSchema, pushContentResultSchema, reservationStatusSchema, reserveRequestSchema, reserveResultSchema, sourceAdapterContextSchema, sourceAdapterRequestScopeSchema, sourceAdapterSchema, } from "./schemas.js";
|
|
3
|
+
const typeChecks = [
|
|
4
|
+
true,
|
|
5
|
+
true,
|
|
6
|
+
true,
|
|
7
|
+
true,
|
|
8
|
+
true,
|
|
9
|
+
true,
|
|
10
|
+
true,
|
|
11
|
+
true,
|
|
12
|
+
true,
|
|
13
|
+
true,
|
|
14
|
+
true,
|
|
15
|
+
true,
|
|
16
|
+
true,
|
|
17
|
+
true,
|
|
18
|
+
true,
|
|
19
|
+
true,
|
|
20
|
+
true,
|
|
21
|
+
true,
|
|
22
|
+
true,
|
|
23
|
+
true,
|
|
24
|
+
true,
|
|
25
|
+
true,
|
|
26
|
+
true,
|
|
27
|
+
true,
|
|
28
|
+
true,
|
|
29
|
+
true,
|
|
30
|
+
true,
|
|
31
|
+
true,
|
|
32
|
+
];
|
|
33
|
+
void typeChecks;
|
|
34
|
+
const capabilities = {
|
|
35
|
+
verticals: ["products"],
|
|
36
|
+
supportsLiveResolution: true,
|
|
37
|
+
supportsDriftDetection: true,
|
|
38
|
+
supportsBookingForwarding: true,
|
|
39
|
+
supportsReservationRetrieval: true,
|
|
40
|
+
supportsSyncCancellation: false,
|
|
41
|
+
postBookOperations: ["cancel", "status"],
|
|
42
|
+
cacheTtlSeconds: 300,
|
|
43
|
+
supportsContentFetch: true,
|
|
44
|
+
supportedContentLocales: ["en-GB", "ro-RO"],
|
|
45
|
+
ownsContentCache: true,
|
|
46
|
+
ownsAvailabilityCache: false,
|
|
47
|
+
holdReleaseGraceMs: 1000,
|
|
48
|
+
supportsBookingPush: true,
|
|
49
|
+
supportsAvailabilityPush: true,
|
|
50
|
+
supportsContentPush: true,
|
|
51
|
+
providerCapabilities: [
|
|
52
|
+
{
|
|
53
|
+
capability: "category_availability_counts",
|
|
54
|
+
support: "supported",
|
|
55
|
+
applies_to: ["cruises"],
|
|
56
|
+
evidence: "category inventory feed",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
capability: "physical_inventory_units",
|
|
60
|
+
support: "unsupported",
|
|
61
|
+
applies_to: ["cruises"],
|
|
62
|
+
reason: "provider feed exposes category counts but not cabin numbers",
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
};
|
|
66
|
+
const context = {
|
|
67
|
+
connection_id: "conn_123",
|
|
68
|
+
credentials: { apiKey: "secret" },
|
|
69
|
+
tenant_id: "tenant_1",
|
|
70
|
+
correlation_id: "corr_1",
|
|
71
|
+
};
|
|
72
|
+
const provenance = {
|
|
73
|
+
source_kind: "direct:tui",
|
|
74
|
+
source_provider: "tui",
|
|
75
|
+
source_connection_id: "conn_123",
|
|
76
|
+
source_ref: "TUI-123",
|
|
77
|
+
source_freshness: "sync",
|
|
78
|
+
last_sourced_at: new Date("2026-01-01T00:00:00Z"),
|
|
79
|
+
};
|
|
80
|
+
const projection = {
|
|
81
|
+
entity_module: "products",
|
|
82
|
+
entity_id: "prod_123",
|
|
83
|
+
provenance,
|
|
84
|
+
fields: { title: "Sample tour", country: "RO" },
|
|
85
|
+
};
|
|
86
|
+
const discoveryPage = {
|
|
87
|
+
projections: [projection],
|
|
88
|
+
next_cursor: "cursor_2",
|
|
89
|
+
};
|
|
90
|
+
const liveResolveRequest = {
|
|
91
|
+
ids: ["prod_123"],
|
|
92
|
+
scope: {
|
|
93
|
+
locale: "en-GB",
|
|
94
|
+
audience: "customer",
|
|
95
|
+
market: "GB",
|
|
96
|
+
currency: "GBP",
|
|
97
|
+
},
|
|
98
|
+
parameters: { departureId: "dep_1" },
|
|
99
|
+
};
|
|
100
|
+
const sourceAdapterRequestScope = liveResolveRequest.scope;
|
|
101
|
+
const liveResolveResult = {
|
|
102
|
+
values: {
|
|
103
|
+
prod_123: {
|
|
104
|
+
price: { amount: "100.00", currency: "GBP" },
|
|
105
|
+
remainingPax: 4,
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
failed: { prod_missing: "not_found" },
|
|
109
|
+
};
|
|
110
|
+
const getContentRequest = {
|
|
111
|
+
entity_module: "products",
|
|
112
|
+
entity_id: "prod_123",
|
|
113
|
+
locale: "ro-RO",
|
|
114
|
+
market: "RO",
|
|
115
|
+
currency: "RON",
|
|
116
|
+
};
|
|
117
|
+
const getContentResult = {
|
|
118
|
+
entity_module: "products",
|
|
119
|
+
entity_id: "prod_123",
|
|
120
|
+
source_ref: "TUI-123",
|
|
121
|
+
returned_locale: "en-GB",
|
|
122
|
+
machine_translated: false,
|
|
123
|
+
content: { product: { title: "Sample tour" } },
|
|
124
|
+
content_schema_version: "products/v1",
|
|
125
|
+
source_updated_at: new Date("2026-01-02T00:00:00Z"),
|
|
126
|
+
fresh_until: new Date("2026-01-03T00:00:00Z"),
|
|
127
|
+
etag: "etag-1",
|
|
128
|
+
};
|
|
129
|
+
const reserveRequest = {
|
|
130
|
+
entity_module: "products",
|
|
131
|
+
entity_id: "prod_123",
|
|
132
|
+
parameters: { departureId: "dep_1" },
|
|
133
|
+
party: { travelers: 2 },
|
|
134
|
+
payment_intent: { type: "hold" },
|
|
135
|
+
scope: sourceAdapterRequestScope,
|
|
136
|
+
idempotency_key: "reserve_123",
|
|
137
|
+
};
|
|
138
|
+
const reserveResult = {
|
|
139
|
+
upstream_ref: "booking_123",
|
|
140
|
+
status: "held",
|
|
141
|
+
upstream_payload: { holdToken: "hold_1" },
|
|
142
|
+
};
|
|
143
|
+
const cancelRequest = {
|
|
144
|
+
upstream_ref: "booking_123",
|
|
145
|
+
reason: "customer_request",
|
|
146
|
+
scope: sourceAdapterRequestScope,
|
|
147
|
+
idempotency_key: "cancel_123",
|
|
148
|
+
};
|
|
149
|
+
const cancelResult = {
|
|
150
|
+
status: "pending",
|
|
151
|
+
pending_channel: "partner portal",
|
|
152
|
+
};
|
|
153
|
+
const getReservationRequest = {
|
|
154
|
+
upstream_ref: "booking_123",
|
|
155
|
+
scope: sourceAdapterRequestScope,
|
|
156
|
+
};
|
|
157
|
+
const getReservationResult = {
|
|
158
|
+
upstream_ref: "booking_123",
|
|
159
|
+
status: "confirmed",
|
|
160
|
+
source_updated_at: new Date("2026-01-04T00:00:00Z"),
|
|
161
|
+
upstream_payload: { travelers: 2, supplierStatus: "OK" },
|
|
162
|
+
};
|
|
163
|
+
const listReservationsQuery = {
|
|
164
|
+
cursor: "cursor_1",
|
|
165
|
+
limit: 50,
|
|
166
|
+
status: ["held", "confirmed", "cancelling"],
|
|
167
|
+
updated_after: new Date("2026-01-01T00:00:00Z"),
|
|
168
|
+
scope: sourceAdapterRequestScope,
|
|
169
|
+
};
|
|
170
|
+
const listReservationsPage = {
|
|
171
|
+
reservations: [getReservationResult],
|
|
172
|
+
next_cursor: "cursor_2",
|
|
173
|
+
};
|
|
174
|
+
const pushBookingRequest = {
|
|
175
|
+
idempotencyKey: "idem_1",
|
|
176
|
+
bookingId: "book_1",
|
|
177
|
+
bookingItemId: "item_1",
|
|
178
|
+
externalProductId: "EXT-1",
|
|
179
|
+
externalRateId: "RATE-1",
|
|
180
|
+
externalCategoryId: "CAT-1",
|
|
181
|
+
channelId: "channel_1",
|
|
182
|
+
contractPolicy: { commission: 0.1 },
|
|
183
|
+
payload: { travelers: 2 },
|
|
184
|
+
};
|
|
185
|
+
const pushBookingResult = {
|
|
186
|
+
upstreamRef: "upstream_1",
|
|
187
|
+
externalReference: "CONF-1",
|
|
188
|
+
externalStatus: "confirmed",
|
|
189
|
+
upstreamPayload: { ok: true },
|
|
190
|
+
};
|
|
191
|
+
const pushAvailabilityRequest = {
|
|
192
|
+
channelId: "channel_1",
|
|
193
|
+
externalProductId: "EXT-1",
|
|
194
|
+
externalRateId: "RATE-1",
|
|
195
|
+
externalCategoryId: "CAT-1",
|
|
196
|
+
slotId: "slot_1",
|
|
197
|
+
productId: "prod_123",
|
|
198
|
+
optionId: "opt_1",
|
|
199
|
+
startsAt: new Date("2026-02-01T10:00:00Z"),
|
|
200
|
+
remainingPax: 8,
|
|
201
|
+
source: "booking",
|
|
202
|
+
};
|
|
203
|
+
const pushAvailabilityResult = {
|
|
204
|
+
externalStatus: "updated",
|
|
205
|
+
upstreamPayload: { ok: true },
|
|
206
|
+
};
|
|
207
|
+
const pushContentRequest = {
|
|
208
|
+
channelId: "channel_1",
|
|
209
|
+
externalProductId: "EXT-1",
|
|
210
|
+
productId: "prod_123",
|
|
211
|
+
contentHash: "hash_123",
|
|
212
|
+
content: { product: { title: "Sample tour" } },
|
|
213
|
+
contentSchemaVersion: "products/v1",
|
|
214
|
+
locale: "en-GB",
|
|
215
|
+
};
|
|
216
|
+
const pushContentResult = {
|
|
217
|
+
externalStatus: "updated",
|
|
218
|
+
upstreamPayload: { ok: true },
|
|
219
|
+
acknowledgedHash: "hash_123",
|
|
220
|
+
};
|
|
221
|
+
const sourceAdapter = {
|
|
222
|
+
kind: "direct:tui",
|
|
223
|
+
capabilities,
|
|
224
|
+
async connect() { },
|
|
225
|
+
async getState() {
|
|
226
|
+
return "active";
|
|
227
|
+
},
|
|
228
|
+
async discover() {
|
|
229
|
+
return discoveryPage;
|
|
230
|
+
},
|
|
231
|
+
async getContent(_ctx, request) {
|
|
232
|
+
return { ...getContentResult, entity_id: request.entity_id };
|
|
233
|
+
},
|
|
234
|
+
async getReservation(_ctx, request) {
|
|
235
|
+
return request.upstream_ref === getReservationResult.upstream_ref ? getReservationResult : null;
|
|
236
|
+
},
|
|
237
|
+
async listReservations() {
|
|
238
|
+
return listReservationsPage;
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
const roundTripCases = [
|
|
242
|
+
["adapterCapabilitiesSchema", adapterCapabilitiesSchema, capabilities],
|
|
243
|
+
["connectionStateSchema", connectionStateSchema, "active"],
|
|
244
|
+
["sourceAdapterContextSchema", sourceAdapterContextSchema, context],
|
|
245
|
+
["provenanceSchema", provenanceSchema, provenance],
|
|
246
|
+
["catalogProjectionSchema", catalogProjectionSchema, projection],
|
|
247
|
+
["discoveryCursorSchema", discoveryCursorSchema, "cursor_2"],
|
|
248
|
+
["discoveryPageSchema", discoveryPageSchema, discoveryPage],
|
|
249
|
+
["sourceAdapterRequestScopeSchema", sourceAdapterRequestScopeSchema, sourceAdapterRequestScope],
|
|
250
|
+
["liveResolveRequestSchema", liveResolveRequestSchema, liveResolveRequest],
|
|
251
|
+
["liveResolveResultSchema", liveResolveResultSchema, liveResolveResult],
|
|
252
|
+
["getContentRequestSchema", getContentRequestSchema, getContentRequest],
|
|
253
|
+
["getContentResultSchema", getContentResultSchema, getContentResult],
|
|
254
|
+
["reserveRequestSchema", reserveRequestSchema, reserveRequest],
|
|
255
|
+
["reserveResultSchema", reserveResultSchema, reserveResult],
|
|
256
|
+
["cancelRequestSchema", cancelRequestSchema, cancelRequest],
|
|
257
|
+
["cancelResultSchema", cancelResultSchema, cancelResult],
|
|
258
|
+
["reservationStatusSchema", reservationStatusSchema, "cancelling"],
|
|
259
|
+
["getReservationRequestSchema", getReservationRequestSchema, getReservationRequest],
|
|
260
|
+
["getReservationResultSchema", getReservationResultSchema, getReservationResult],
|
|
261
|
+
["listReservationsQuerySchema", listReservationsQuerySchema, listReservationsQuery],
|
|
262
|
+
["listReservationsPageSchema", listReservationsPageSchema, listReservationsPage],
|
|
263
|
+
["pushBookingRequestSchema", pushBookingRequestSchema, pushBookingRequest],
|
|
264
|
+
["pushBookingResultSchema", pushBookingResultSchema, pushBookingResult],
|
|
265
|
+
["pushAvailabilityRequestSchema", pushAvailabilityRequestSchema, pushAvailabilityRequest],
|
|
266
|
+
["pushAvailabilityResultSchema", pushAvailabilityResultSchema, pushAvailabilityResult],
|
|
267
|
+
["pushContentRequestSchema", pushContentRequestSchema, pushContentRequest],
|
|
268
|
+
["pushContentResultSchema", pushContentResultSchema, pushContentResult],
|
|
269
|
+
["sourceAdapterSchema", sourceAdapterSchema, sourceAdapter],
|
|
270
|
+
];
|
|
271
|
+
const invalidCases = [
|
|
272
|
+
["adapterCapabilitiesSchema", adapterCapabilitiesSchema, { ...capabilities, verticals: [123] }],
|
|
273
|
+
["connectionStateSchema", connectionStateSchema, "reconnecting"],
|
|
274
|
+
["sourceAdapterContextSchema", sourceAdapterContextSchema, { credentials: { apiKey: "secret" } }],
|
|
275
|
+
["provenanceSchema", provenanceSchema, { ...provenance, source_freshness: "live" }],
|
|
276
|
+
["catalogProjectionSchema", catalogProjectionSchema, { ...projection, entity_module: 123 }],
|
|
277
|
+
["discoveryCursorSchema", discoveryCursorSchema, 123],
|
|
278
|
+
["discoveryPageSchema", discoveryPageSchema, { projections: [{ fields: {} }] }],
|
|
279
|
+
[
|
|
280
|
+
"sourceAdapterRequestScopeSchema",
|
|
281
|
+
sourceAdapterRequestScopeSchema,
|
|
282
|
+
{ ...sourceAdapterRequestScope, currency: "GB" },
|
|
283
|
+
],
|
|
284
|
+
[
|
|
285
|
+
"liveResolveRequestSchema",
|
|
286
|
+
liveResolveRequestSchema,
|
|
287
|
+
{ ...liveResolveRequest, scope: { ...liveResolveRequest.scope, locale: "not_locale" } },
|
|
288
|
+
],
|
|
289
|
+
["liveResolveResultSchema", liveResolveResultSchema, { values: {}, failed: { prod_1: "gone" } }],
|
|
290
|
+
["getContentRequestSchema", getContentRequestSchema, { ...getContentRequest, currency: "EU" }],
|
|
291
|
+
[
|
|
292
|
+
"getContentResultSchema",
|
|
293
|
+
getContentResultSchema,
|
|
294
|
+
{ ...getContentResult, content_schema_version: undefined },
|
|
295
|
+
],
|
|
296
|
+
["reserveRequestSchema", reserveRequestSchema, { ...reserveRequest, parameters: undefined }],
|
|
297
|
+
["reserveResultSchema", reserveResultSchema, { ...reserveResult, status: "pending" }],
|
|
298
|
+
["cancelRequestSchema", cancelRequestSchema, { reason: "customer_request" }],
|
|
299
|
+
["cancelResultSchema", cancelResultSchema, { ...cancelResult, refund_currency: "GB" }],
|
|
300
|
+
["reservationStatusSchema", reservationStatusSchema, "expired"],
|
|
301
|
+
[
|
|
302
|
+
"getReservationRequestSchema",
|
|
303
|
+
getReservationRequestSchema,
|
|
304
|
+
{ ...getReservationRequest, upstream_ref: undefined },
|
|
305
|
+
],
|
|
306
|
+
[
|
|
307
|
+
"getReservationResultSchema",
|
|
308
|
+
getReservationResultSchema,
|
|
309
|
+
{ ...getReservationResult, status: "expired" },
|
|
310
|
+
],
|
|
311
|
+
["listReservationsQuerySchema", listReservationsQuerySchema, { limit: 0 }],
|
|
312
|
+
[
|
|
313
|
+
"listReservationsPageSchema",
|
|
314
|
+
listReservationsPageSchema,
|
|
315
|
+
{ reservations: [{ ...getReservationResult, upstream_payload: "raw" }] },
|
|
316
|
+
],
|
|
317
|
+
[
|
|
318
|
+
"pushBookingRequestSchema",
|
|
319
|
+
pushBookingRequestSchema,
|
|
320
|
+
{ ...pushBookingRequest, payload: undefined },
|
|
321
|
+
],
|
|
322
|
+
["pushBookingResultSchema", pushBookingResultSchema, { externalStatus: "confirmed" }],
|
|
323
|
+
[
|
|
324
|
+
"pushAvailabilityRequestSchema",
|
|
325
|
+
pushAvailabilityRequestSchema,
|
|
326
|
+
{ ...pushAvailabilityRequest, remainingPax: -1 },
|
|
327
|
+
],
|
|
328
|
+
["pushAvailabilityResultSchema", pushAvailabilityResultSchema, { upstreamPayload: "ok" }],
|
|
329
|
+
[
|
|
330
|
+
"pushContentRequestSchema",
|
|
331
|
+
pushContentRequestSchema,
|
|
332
|
+
{ ...pushContentRequest, locale: "not_locale" },
|
|
333
|
+
],
|
|
334
|
+
["pushContentResultSchema", pushContentResultSchema, { acknowledgedHash: 123 }],
|
|
335
|
+
["sourceAdapterSchema", sourceAdapterSchema, { ...sourceAdapter, discover: "yes" }],
|
|
336
|
+
];
|
|
337
|
+
describe("catalog source-adapter schemas", () => {
|
|
338
|
+
it.each(roundTripCases)("parses %s fixtures without changing shape", (_name, schema, value) => {
|
|
339
|
+
expect(schema.parse(value)).toEqual(value);
|
|
340
|
+
});
|
|
341
|
+
it.each(invalidCases)("rejects invalid %s fixtures", (_name, schema, value) => {
|
|
342
|
+
expect(schema.safeParse(value).success).toBe(false);
|
|
343
|
+
});
|
|
344
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `bookEntity` — the second step in the booking engine lifecycle.
|
|
3
|
+
*
|
|
4
|
+
* Validates the supplied quote, calls `adapter.reserve`, and writes a
|
|
5
|
+
* `booking_catalog_snapshot` row with the frozen view, source pointer,
|
|
6
|
+
* and pricing breakdown. The snapshot is the audit record; the source
|
|
7
|
+
* pointer is what subsequent `cancelEntity` / status calls dispatch
|
|
8
|
+
* against.
|
|
9
|
+
*
|
|
10
|
+
* `bookingId` is plain text (no FK), so the engine accepts an existing
|
|
11
|
+
* id from the caller (when a `bookings` row already exists) or generates
|
|
12
|
+
* one on demand. This keeps the engine decoupled from `packages/bookings`
|
|
13
|
+
* — the engine and the bookings module evolve independently and are
|
|
14
|
+
* joined at read time via the shared `booking_id` text column.
|
|
15
|
+
*/
|
|
16
|
+
import type { AnyDrizzleDb } from "@voyant-travel/db";
|
|
17
|
+
import type { SourceAdapterContext, SourceAdapterRequestScope } from "../adapter/contract.js";
|
|
18
|
+
import { type PricingBasis, type SelectBookingCatalogSnapshot } from "../snapshot/schema.js";
|
|
19
|
+
/**
|
|
20
|
+
* `BookingPaymentIntent` now lives in `@voyant-travel/catalog-contracts`
|
|
21
|
+
* (the shared server/client seam, alongside `bookRequestV1` whose
|
|
22
|
+
* `paymentIntent` field mirrors it). Re-exported here to keep existing
|
|
23
|
+
* `@voyant-travel/catalog/booking-engine` import paths stable.
|
|
24
|
+
*/
|
|
25
|
+
import type { BookingPaymentIntent } from "./contracts.js";
|
|
26
|
+
import type { OwnedBookingHandlerRegistry } from "./owned-handler.js";
|
|
27
|
+
import type { SourceAdapterRegistry } from "./registry.js";
|
|
28
|
+
import type { SnapshotContentCapturer } from "./snapshot-content.js";
|
|
29
|
+
export type { BookingPaymentIntent };
|
|
30
|
+
export interface BookEntityRequest {
|
|
31
|
+
/** Quote previously returned from `quoteEntity`. */
|
|
32
|
+
quoteId: string;
|
|
33
|
+
/**
|
|
34
|
+
* Existing or newly-created booking shell id. Generated when omitted,
|
|
35
|
+
* using the `bookings` typeid prefix so the value is shape-compatible
|
|
36
|
+
* with `packages/bookings`.
|
|
37
|
+
*/
|
|
38
|
+
bookingId?: string;
|
|
39
|
+
/** Customer / passenger payload echoed to the adapter's reserve call. */
|
|
40
|
+
party?: Record<string, unknown>;
|
|
41
|
+
/** Defaults to `{ type: "hold" }` — see `BookingPaymentIntent`. */
|
|
42
|
+
paymentIntent?: BookingPaymentIntent;
|
|
43
|
+
/** Vertical-specific parameters passed to the adapter. */
|
|
44
|
+
parameters?: Record<string, unknown>;
|
|
45
|
+
/**
|
|
46
|
+
* Caller-supplied idempotency key. When set, a duplicate
|
|
47
|
+
* `bookEntity` call within the snapshot table's lifetime returns
|
|
48
|
+
* the prior booking instead of creating a new one. Length 8–128.
|
|
49
|
+
* Per booking-journey-architecture §12.6.
|
|
50
|
+
*/
|
|
51
|
+
idempotencyKey?: string;
|
|
52
|
+
adapterContext: SourceAdapterContext;
|
|
53
|
+
/**
|
|
54
|
+
* Locale / market / currency scope for snapshot content capture per
|
|
55
|
+
* sourced-content §5.1. Required when `deps.captureSnapshotContent`
|
|
56
|
+
* is wired — the engine refreshes content from the adapter at commit
|
|
57
|
+
* time using this scope and embeds the result as `content_capture`
|
|
58
|
+
* in `frozen_payload`. When the deps callback isn't set, this field
|
|
59
|
+
* is ignored and snapshot behavior is unchanged.
|
|
60
|
+
*/
|
|
61
|
+
contentScope?: {
|
|
62
|
+
locale: string;
|
|
63
|
+
market?: string;
|
|
64
|
+
currency?: string;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Per-request supplier scope forwarded to `SourceAdapter.reserve`.
|
|
68
|
+
* Kept separate from `contentScope`: this controls upstream write
|
|
69
|
+
* semantics, while `contentScope` controls snapshot-content capture.
|
|
70
|
+
*/
|
|
71
|
+
adapterScope?: SourceAdapterRequestScope;
|
|
72
|
+
}
|
|
73
|
+
export interface BookEntityResult {
|
|
74
|
+
bookingId: string;
|
|
75
|
+
orderRef: string;
|
|
76
|
+
status: "held" | "confirmed" | "ticketed" | "failed";
|
|
77
|
+
snapshotId: string;
|
|
78
|
+
pricing?: PricingBasis;
|
|
79
|
+
upstreamPayload?: Record<string, unknown>;
|
|
80
|
+
}
|
|
81
|
+
export interface BookEntityDeps {
|
|
82
|
+
registry: SourceAdapterRegistry;
|
|
83
|
+
/**
|
|
84
|
+
* Owned-arm dispatch — when set and the quote's source kind is
|
|
85
|
+
* `"owned"`, the engine commits via a handler keyed by
|
|
86
|
+
* `entity_module` instead of the SourceAdapterRegistry. Per
|
|
87
|
+
* booking-journey-architecture §6.
|
|
88
|
+
*/
|
|
89
|
+
ownedHandlers?: OwnedBookingHandlerRegistry;
|
|
90
|
+
/**
|
|
91
|
+
* Optional snapshot content capture orchestrator (sourced-content
|
|
92
|
+
* §5.1). When set, called after `adapter.reserve` succeeds. Returns a
|
|
93
|
+
* `SnapshotContentCapture` envelope embedded in `frozen_payload` so
|
|
94
|
+
* audit can later distinguish a fresh capture from a cache fallback.
|
|
95
|
+
*
|
|
96
|
+
* Throws `SnapshotContentUnavailableError` when neither a fresh
|
|
97
|
+
* adapter fetch nor a cache fallback can produce content; the engine
|
|
98
|
+
* propagates that error and aborts the commit. When the entity is
|
|
99
|
+
* owned (no sourced-entry row), the orchestrator should return null
|
|
100
|
+
* and the engine skips the capture.
|
|
101
|
+
*
|
|
102
|
+
* Implementations live in templates: each template composes per-
|
|
103
|
+
* vertical content services into one capturer and threads it through
|
|
104
|
+
* deps.
|
|
105
|
+
*/
|
|
106
|
+
captureSnapshotContent?: SnapshotContentCapturer;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Book the row referenced by `quoteId`. End-to-end: validate quote,
|
|
110
|
+
* dispatch `adapter.reserve`, capture snapshot, mark quote consumed.
|
|
111
|
+
*
|
|
112
|
+
* Throws:
|
|
113
|
+
* - `BookingEngineError(QUOTE_NOT_FOUND)` if the quote doesn't exist.
|
|
114
|
+
* - `QuoteExpiredError` if the quote's `expires_at` has passed.
|
|
115
|
+
* - `QuoteMismatchError` if a future caller tries to book a different
|
|
116
|
+
* entity than the quote was issued for (defensive — the engine
|
|
117
|
+
* re-reads quote.entity_*).
|
|
118
|
+
* - `NoAdapterRegisteredError` if the registry has no adapter for
|
|
119
|
+
* the quote's `source_kind`.
|
|
120
|
+
* - `ReserveFailedError` when the adapter returns `status: "failed"`.
|
|
121
|
+
*/
|
|
122
|
+
export declare function bookEntity(db: AnyDrizzleDb, deps: BookEntityDeps, request: BookEntityRequest): Promise<BookEntityResult>;
|
|
123
|
+
export type { SelectBookingCatalogSnapshot };
|
|
124
|
+
//# sourceMappingURL=book.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"book.d.ts","sourceRoot":"","sources":["../../src/booking-engine/book.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAIrD,OAAO,KAAK,EAEV,oBAAoB,EACpB,yBAAyB,EAC1B,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,4BAA4B,EAClC,MAAM,uBAAuB,CAAA;AAC9B;;;;;GAKG;AACH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAQ1D,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAA;AAErE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAE1D,OAAO,KAAK,EAA0B,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAE5F,YAAY,EAAE,oBAAoB,EAAE,CAAA;AAMpC,MAAM,WAAW,iBAAiB;IAChC,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAA;IAEf;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAE/B,mEAAmE;IACnE,aAAa,CAAC,EAAE,oBAAoB,CAAA;IAEpC,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAEpC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IAEvB,cAAc,EAAE,oBAAoB,CAAA;IAEpC;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE;QACb,MAAM,EAAE,MAAM,CAAA;QACd,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,QAAQ,CAAC,EAAE,MAAM,CAAA;KAClB,CAAA;IAED;;;;OAIG;IACH,YAAY,CAAC,EAAE,yBAAyB,CAAA;CACzC;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,QAAQ,CAAA;IACpD,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC1C;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,qBAAqB,CAAA;IAC/B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,2BAA2B,CAAA;IAC3C;;;;;;;;;;;;;;;OAeG;IACH,sBAAsB,CAAC,EAAE,uBAAuB,CAAA;CACjD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,UAAU,CAC9B,EAAE,EAAE,YAAY,EAChB,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,gBAAgB,CAAC,CAqL3B;AAoID,YAAY,EAAE,4BAA4B,EAAE,CAAA"}
|