@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,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SourcedEntryService — drizzle-bound entry point for the
|
|
3
|
+
* `catalog_sourced_entries` store.
|
|
4
|
+
*
|
|
5
|
+
* One row per sourced entity, written at `discover()` time. The row carries
|
|
6
|
+
* provenance, lifecycle markers, and the canonical local copy of the
|
|
7
|
+
* indexed projection (so the read service and the thin-content synthesizer
|
|
8
|
+
* can dispatch without round-tripping the search index).
|
|
9
|
+
*
|
|
10
|
+
* Three primitives:
|
|
11
|
+
*
|
|
12
|
+
* - `upsertSourcedEntry` — called from `sync.ts` for every projection;
|
|
13
|
+
* idempotent on (entity_module, entity_id) and on
|
|
14
|
+
* (source_kind, source_connection_id, source_ref).
|
|
15
|
+
* - `readSourcedEntry` — point-read by Voyant-side identity. Returns
|
|
16
|
+
* null for owned entities (which have no row here).
|
|
17
|
+
* - `createReadProvenance` — factory that composes `readSourcedEntry`
|
|
18
|
+
* with vertical-specific owned-checkers to produce a unified
|
|
19
|
+
* `readProvenance(db, entity_module, entity_id)` that returns one of:
|
|
20
|
+
* `{ kind: "owned" }`, `{ kind: "sourced", ... }`, or `null`.
|
|
21
|
+
*
|
|
22
|
+
* The factory pattern keeps this package neutral — it doesn't know how to
|
|
23
|
+
* read the products / cruises / hotels owned tables. Each vertical
|
|
24
|
+
* registers its owned-checker once when wiring its content service (Phase
|
|
25
|
+
* D and beyond).
|
|
26
|
+
*
|
|
27
|
+
* See `docs/architecture/catalog-sourced-content.md` §2.5.
|
|
28
|
+
*/
|
|
29
|
+
import { and, eq, inArray, isNull, notInArray, sql } from "drizzle-orm";
|
|
30
|
+
import { catalogSourcedEntriesTable, } from "../schema-sourced-entries.js";
|
|
31
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
32
|
+
// Reads
|
|
33
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
34
|
+
/**
|
|
35
|
+
* Read one sourced-entry row by Voyant-side identity. Returns `null` for
|
|
36
|
+
* entities that aren't in the sourced-entry store — owned entities, or
|
|
37
|
+
* sourced entities the deployment hasn't yet discovered.
|
|
38
|
+
*/
|
|
39
|
+
export async function readSourcedEntry(db, entityModule, entityId) {
|
|
40
|
+
const rows = await db
|
|
41
|
+
.select()
|
|
42
|
+
.from(catalogSourcedEntriesTable)
|
|
43
|
+
.where(and(eq(catalogSourcedEntriesTable.entity_module, entityModule), eq(catalogSourcedEntriesTable.entity_id, entityId)))
|
|
44
|
+
.limit(1);
|
|
45
|
+
return rows[0] ?? null;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Build a unified `readProvenance(db, entity_module, entity_id)` against a
|
|
49
|
+
* registry of vertical-specific owned-checkers. The returned function:
|
|
50
|
+
*
|
|
51
|
+
* 1. Calls the vertical's owned-checker. If it returns `true`, the entity
|
|
52
|
+
* is owned — return `{ kind: "owned", provenance: ... }` without
|
|
53
|
+
* touching the sourced-entry table.
|
|
54
|
+
* 2. Otherwise, look up the sourced-entry row. If found, return
|
|
55
|
+
* `{ kind: "sourced", ... }`.
|
|
56
|
+
* 3. If neither, return `null`.
|
|
57
|
+
*
|
|
58
|
+
* Verticals not in `ownedCheckers` skip the owned check (treated as
|
|
59
|
+
* sourced-only). This is intentional: not every vertical has an owned
|
|
60
|
+
* counterpart for every sourced entity.
|
|
61
|
+
*/
|
|
62
|
+
export function createReadProvenance(options) {
|
|
63
|
+
const ownedCheckers = options.ownedCheckers ?? new Map();
|
|
64
|
+
return async function readProvenance(db, entityModule, entityId) {
|
|
65
|
+
const ownedChecker = ownedCheckers.get(entityModule);
|
|
66
|
+
if (ownedChecker) {
|
|
67
|
+
const isOwned = await ownedChecker(db, entityId);
|
|
68
|
+
if (isOwned) {
|
|
69
|
+
return {
|
|
70
|
+
kind: "owned",
|
|
71
|
+
provenance: { source_kind: "owned", source_freshness: "static" },
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const row = await readSourcedEntry(db, entityModule, entityId);
|
|
76
|
+
if (!row)
|
|
77
|
+
return null;
|
|
78
|
+
return {
|
|
79
|
+
kind: "sourced",
|
|
80
|
+
provenance: {
|
|
81
|
+
source_kind: row.source_kind,
|
|
82
|
+
source_provider: row.source_provider ?? undefined,
|
|
83
|
+
source_connection_id: row.source_connection_id ?? undefined,
|
|
84
|
+
source_ref: row.source_ref ?? undefined,
|
|
85
|
+
source_freshness: row.source_freshness,
|
|
86
|
+
last_sourced_at: row.last_sourced_at ?? undefined,
|
|
87
|
+
},
|
|
88
|
+
entry_id: row.id,
|
|
89
|
+
status: row.status,
|
|
90
|
+
projection: row.projection,
|
|
91
|
+
projection_etag: row.projection_etag,
|
|
92
|
+
projection_seen_at: row.projection_seen_at,
|
|
93
|
+
first_seen_at: row.first_seen_at,
|
|
94
|
+
last_seen_at: row.last_seen_at,
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Upsert a sourced-entry row. Idempotent on `(entity_module, entity_id)`
|
|
100
|
+
* — repeated calls update `projection`, `projection_etag`,
|
|
101
|
+
* `projection_seen_at`, `last_seen_at`, `last_sourced_at`,
|
|
102
|
+
* `source_freshness`, and `updated_at`. The first-seen timestamp is
|
|
103
|
+
* preserved.
|
|
104
|
+
*
|
|
105
|
+
* Owned projections are rejected — `provenance.source_kind === "owned"`
|
|
106
|
+
* has no place in the sourced-entry store. Callers in `sync.ts` should
|
|
107
|
+
* already filter these out, but this guard makes the invariant explicit.
|
|
108
|
+
*/
|
|
109
|
+
export async function upsertSourcedEntry(db, input) {
|
|
110
|
+
const { projection, projectionEtag, lastSourcedAt, status } = input;
|
|
111
|
+
const provenance = projection.provenance;
|
|
112
|
+
if (provenance.source_kind === "owned") {
|
|
113
|
+
throw new Error(`upsertSourcedEntry called with owned provenance for ${projection.entity_module}/${projection.entity_id}; owned entities live in the vertical's owned schema, not catalog_sourced_entries`);
|
|
114
|
+
}
|
|
115
|
+
const now = new Date();
|
|
116
|
+
const stampedAt = lastSourcedAt ?? provenance.last_sourced_at ?? now;
|
|
117
|
+
const rows = await db
|
|
118
|
+
.insert(catalogSourcedEntriesTable)
|
|
119
|
+
.values({
|
|
120
|
+
entity_module: projection.entity_module,
|
|
121
|
+
entity_id: projection.entity_id,
|
|
122
|
+
source_kind: provenance.source_kind,
|
|
123
|
+
source_provider: provenance.source_provider ?? null,
|
|
124
|
+
source_connection_id: provenance.source_connection_id ?? null,
|
|
125
|
+
source_ref: provenance.source_ref ?? null,
|
|
126
|
+
source_freshness: (provenance.source_freshness ?? "static"),
|
|
127
|
+
last_sourced_at: stampedAt,
|
|
128
|
+
status: status ?? "active",
|
|
129
|
+
first_seen_at: now,
|
|
130
|
+
last_seen_at: now,
|
|
131
|
+
projection: projection.fields,
|
|
132
|
+
projection_etag: projectionEtag ?? null,
|
|
133
|
+
projection_seen_at: now,
|
|
134
|
+
created_at: now,
|
|
135
|
+
updated_at: now,
|
|
136
|
+
})
|
|
137
|
+
.onConflictDoUpdate({
|
|
138
|
+
target: [catalogSourcedEntriesTable.entity_module, catalogSourcedEntriesTable.entity_id],
|
|
139
|
+
set: {
|
|
140
|
+
source_kind: sql `excluded.source_kind`,
|
|
141
|
+
source_provider: sql `excluded.source_provider`,
|
|
142
|
+
source_connection_id: sql `excluded.source_connection_id`,
|
|
143
|
+
source_ref: sql `excluded.source_ref`,
|
|
144
|
+
source_freshness: sql `excluded.source_freshness`,
|
|
145
|
+
last_sourced_at: sql `excluded.last_sourced_at`,
|
|
146
|
+
status: sql `excluded.status`,
|
|
147
|
+
last_seen_at: sql `excluded.last_seen_at`,
|
|
148
|
+
projection: sql `excluded.projection`,
|
|
149
|
+
projection_etag: sql `excluded.projection_etag`,
|
|
150
|
+
projection_seen_at: sql `excluded.projection_seen_at`,
|
|
151
|
+
updated_at: sql `excluded.updated_at`,
|
|
152
|
+
},
|
|
153
|
+
})
|
|
154
|
+
.returning();
|
|
155
|
+
const row = rows[0];
|
|
156
|
+
if (!row) {
|
|
157
|
+
throw new Error(`upsertSourcedEntry returned no row for ${projection.entity_module}/${projection.entity_id}`);
|
|
158
|
+
}
|
|
159
|
+
return row;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Mark a sourced-entry row as withdrawn (the upstream stopped emitting
|
|
163
|
+
* it). Used by the periodic withdrawal sweeper or by drift events of kind
|
|
164
|
+
* `entity_archived`. Does not delete the row — withdrawals are auditable.
|
|
165
|
+
*/
|
|
166
|
+
export async function markSourcedEntryWithdrawn(db, entityModule, entityId) {
|
|
167
|
+
const now = new Date();
|
|
168
|
+
await db
|
|
169
|
+
.update(catalogSourcedEntriesTable)
|
|
170
|
+
.set({ status: "withdrawn", updated_at: now })
|
|
171
|
+
.where(and(eq(catalogSourcedEntriesTable.entity_module, entityModule), eq(catalogSourcedEntriesTable.entity_id, entityId)));
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Mark active sourced rows missing from a successful full-source discovery pass
|
|
175
|
+
* as withdrawn. Callers should invoke this only after an adapter completed its
|
|
176
|
+
* projection stream; failed refreshes must leave existing rows untouched.
|
|
177
|
+
*/
|
|
178
|
+
export async function markMissingSourcedEntriesWithdrawn(db, input) {
|
|
179
|
+
const conditions = [
|
|
180
|
+
eq(catalogSourcedEntriesTable.entity_module, input.entityModule),
|
|
181
|
+
eq(catalogSourcedEntriesTable.source_kind, input.sourceKind),
|
|
182
|
+
eq(catalogSourcedEntriesTable.status, "active"),
|
|
183
|
+
input.sourceConnectionId == null
|
|
184
|
+
? isNull(catalogSourcedEntriesTable.source_connection_id)
|
|
185
|
+
: eq(catalogSourcedEntriesTable.source_connection_id, input.sourceConnectionId),
|
|
186
|
+
];
|
|
187
|
+
const seen = [...input.seenEntityIds];
|
|
188
|
+
if (seen.length > 0) {
|
|
189
|
+
conditions.push(notInArray(catalogSourcedEntriesTable.entity_id, seen));
|
|
190
|
+
}
|
|
191
|
+
const rows = await db
|
|
192
|
+
.select()
|
|
193
|
+
.from(catalogSourcedEntriesTable)
|
|
194
|
+
.where(and(...conditions));
|
|
195
|
+
if (rows.length === 0)
|
|
196
|
+
return [];
|
|
197
|
+
const now = new Date();
|
|
198
|
+
await db
|
|
199
|
+
.update(catalogSourcedEntriesTable)
|
|
200
|
+
.set({ status: "withdrawn", updated_at: now })
|
|
201
|
+
.where(inArray(catalogSourcedEntriesTable.id, rows.map((row) => row.id)));
|
|
202
|
+
return rows;
|
|
203
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure-function tests for the sourced-entry service.
|
|
3
|
+
*
|
|
4
|
+
* Integration tests (real Postgres) live in
|
|
5
|
+
* `tests/integration/sourced-entry-service.test.ts`. These cover the
|
|
6
|
+
* `createReadProvenance` factory's branching against stub owned-checkers
|
|
7
|
+
* and a stub DB — no Postgres needed.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=sourced-entry-service.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sourced-entry-service.test.d.ts","sourceRoot":"","sources":["../../src/services/sourced-entry-service.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure-function tests for the sourced-entry service.
|
|
3
|
+
*
|
|
4
|
+
* Integration tests (real Postgres) live in
|
|
5
|
+
* `tests/integration/sourced-entry-service.test.ts`. These cover the
|
|
6
|
+
* `createReadProvenance` factory's branching against stub owned-checkers
|
|
7
|
+
* and a stub DB — no Postgres needed.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, expect, it, vi } from "vitest";
|
|
10
|
+
import { createReadProvenance } from "./sourced-entry-service.js";
|
|
11
|
+
// Minimal stub mirroring the columns `readSourcedEntry` selects. We don't
|
|
12
|
+
// exercise any drizzle methods here — we replace the `readSourcedEntry`
|
|
13
|
+
// shape entirely via vi.mock for the dispatch tests.
|
|
14
|
+
function fakeDb() {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
describe("createReadProvenance — owned check fast path", () => {
|
|
18
|
+
it("returns kind: 'owned' when the owned-checker returns true", async () => {
|
|
19
|
+
const ownedChecker = vi.fn(async () => true);
|
|
20
|
+
const readProvenance = createReadProvenance({
|
|
21
|
+
ownedCheckers: new Map([["products", ownedChecker]]),
|
|
22
|
+
});
|
|
23
|
+
const result = await readProvenance(fakeDb(), "products", "prod_abc");
|
|
24
|
+
expect(result?.kind).toBe("owned");
|
|
25
|
+
if (result?.kind === "owned") {
|
|
26
|
+
expect(result.provenance.source_kind).toBe("owned");
|
|
27
|
+
expect(result.provenance.source_freshness).toBe("static");
|
|
28
|
+
}
|
|
29
|
+
expect(ownedChecker).toHaveBeenCalledWith(expect.anything(), "prod_abc");
|
|
30
|
+
});
|
|
31
|
+
it("skips the owned-checker for verticals not in the registry", async () => {
|
|
32
|
+
const productsOwnedChecker = vi.fn(async () => true);
|
|
33
|
+
const readProvenance = createReadProvenance({
|
|
34
|
+
ownedCheckers: new Map([["products", productsOwnedChecker]]),
|
|
35
|
+
});
|
|
36
|
+
// No checker registered for "cruises" — falls through to sourced-entry
|
|
37
|
+
// lookup directly. We can't hit a real DB here, so we expect this to
|
|
38
|
+
// attempt a query and fail cleanly. The point is the products checker
|
|
39
|
+
// is NOT called for a different vertical.
|
|
40
|
+
await expect(readProvenance(fakeDb(), "cruises", "crus_xyz")).rejects.toBeTruthy();
|
|
41
|
+
expect(productsOwnedChecker).not.toHaveBeenCalled();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
describe("createReadProvenance — empty checker map", () => {
|
|
45
|
+
it("treats every vertical as sourced-only when no checkers are registered", async () => {
|
|
46
|
+
const readProvenance = createReadProvenance({});
|
|
47
|
+
// With no checkers, the function falls straight through to the
|
|
48
|
+
// sourced-entry table read — which fails against the stub DB. We
|
|
49
|
+
// verify only that it didn't throw before reaching the DB read.
|
|
50
|
+
await expect(readProvenance(fakeDb(), "products", "prod_abc")).rejects.toBeTruthy();
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe("CatalogProjection input shape", () => {
|
|
54
|
+
it("requires non-owned provenance for the upsert path", () => {
|
|
55
|
+
// Pure-shape assertion — the upsert function throws if you pass an
|
|
56
|
+
// owned projection. This is a structural property of the input
|
|
57
|
+
// contract; the DB is irrelevant.
|
|
58
|
+
const projection = {
|
|
59
|
+
entity_module: "products",
|
|
60
|
+
entity_id: "prod_abc",
|
|
61
|
+
provenance: { source_kind: "direct:tui", source_freshness: "sync" },
|
|
62
|
+
fields: { title: "Sample" },
|
|
63
|
+
};
|
|
64
|
+
expect(projection.provenance.source_kind).toBe("direct:tui");
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Booking snapshot graph — immutable frozen views of CatalogEntries captured
|
|
3
|
+
* at booking commit time.
|
|
4
|
+
*
|
|
5
|
+
* One booking can produce multiple snapshot rows (one per participating
|
|
6
|
+
* CatalogEntry — a TUI package booking captures the package, the referenced
|
|
7
|
+
* hotel, each selected excursion, the chosen departure, the selected flight).
|
|
8
|
+
* Snapshots are never mutated and are preserved through source disconnection.
|
|
9
|
+
*
|
|
10
|
+
* See `docs/architecture/catalog-architecture.md` §5.3 for the full design.
|
|
11
|
+
*/
|
|
12
|
+
import type { PricingBasis } from "@voyant-travel/catalog-contracts/snapshot";
|
|
13
|
+
import type { AppliedOffer } from "../booking-engine/promotions-contract.js";
|
|
14
|
+
export type { PricingBasis };
|
|
15
|
+
/**
|
|
16
|
+
* `booking_catalog_snapshot` — frozen view of a CatalogEntry captured at
|
|
17
|
+
* booking commit. One booking may produce many snapshot rows (one per
|
|
18
|
+
* participating CatalogEntry).
|
|
19
|
+
*/
|
|
20
|
+
export declare const bookingCatalogSnapshotTable: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
21
|
+
name: "booking_catalog_snapshot";
|
|
22
|
+
schema: undefined;
|
|
23
|
+
columns: {
|
|
24
|
+
id: import("drizzle-orm/pg-core").PgColumn<{
|
|
25
|
+
name: string;
|
|
26
|
+
tableName: "booking_catalog_snapshot";
|
|
27
|
+
dataType: "string";
|
|
28
|
+
columnType: "PgText";
|
|
29
|
+
data: string;
|
|
30
|
+
driverParam: string;
|
|
31
|
+
notNull: true;
|
|
32
|
+
hasDefault: true;
|
|
33
|
+
isPrimaryKey: true;
|
|
34
|
+
isAutoincrement: false;
|
|
35
|
+
hasRuntimeDefault: true;
|
|
36
|
+
enumValues: [string, ...string[]];
|
|
37
|
+
baseColumn: never;
|
|
38
|
+
identity: undefined;
|
|
39
|
+
generated: undefined;
|
|
40
|
+
}, {}, {}>;
|
|
41
|
+
booking_id: import("drizzle-orm/pg-core").PgColumn<{
|
|
42
|
+
name: "booking_id";
|
|
43
|
+
tableName: "booking_catalog_snapshot";
|
|
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
|
+
entity_module: import("drizzle-orm/pg-core").PgColumn<{
|
|
59
|
+
name: "entity_module";
|
|
60
|
+
tableName: "booking_catalog_snapshot";
|
|
61
|
+
dataType: "string";
|
|
62
|
+
columnType: "PgText";
|
|
63
|
+
data: string;
|
|
64
|
+
driverParam: string;
|
|
65
|
+
notNull: true;
|
|
66
|
+
hasDefault: false;
|
|
67
|
+
isPrimaryKey: false;
|
|
68
|
+
isAutoincrement: false;
|
|
69
|
+
hasRuntimeDefault: false;
|
|
70
|
+
enumValues: [string, ...string[]];
|
|
71
|
+
baseColumn: never;
|
|
72
|
+
identity: undefined;
|
|
73
|
+
generated: undefined;
|
|
74
|
+
}, {}, {}>;
|
|
75
|
+
entity_id: import("drizzle-orm/pg-core").PgColumn<{
|
|
76
|
+
name: "entity_id";
|
|
77
|
+
tableName: "booking_catalog_snapshot";
|
|
78
|
+
dataType: "string";
|
|
79
|
+
columnType: "PgText";
|
|
80
|
+
data: string;
|
|
81
|
+
driverParam: string;
|
|
82
|
+
notNull: true;
|
|
83
|
+
hasDefault: false;
|
|
84
|
+
isPrimaryKey: false;
|
|
85
|
+
isAutoincrement: false;
|
|
86
|
+
hasRuntimeDefault: false;
|
|
87
|
+
enumValues: [string, ...string[]];
|
|
88
|
+
baseColumn: never;
|
|
89
|
+
identity: undefined;
|
|
90
|
+
generated: undefined;
|
|
91
|
+
}, {}, {}>;
|
|
92
|
+
source_kind: import("drizzle-orm/pg-core").PgColumn<{
|
|
93
|
+
name: "source_kind";
|
|
94
|
+
tableName: "booking_catalog_snapshot";
|
|
95
|
+
dataType: "string";
|
|
96
|
+
columnType: "PgText";
|
|
97
|
+
data: string;
|
|
98
|
+
driverParam: string;
|
|
99
|
+
notNull: true;
|
|
100
|
+
hasDefault: false;
|
|
101
|
+
isPrimaryKey: false;
|
|
102
|
+
isAutoincrement: false;
|
|
103
|
+
hasRuntimeDefault: false;
|
|
104
|
+
enumValues: [string, ...string[]];
|
|
105
|
+
baseColumn: never;
|
|
106
|
+
identity: undefined;
|
|
107
|
+
generated: undefined;
|
|
108
|
+
}, {}, {}>;
|
|
109
|
+
source_provider: import("drizzle-orm/pg-core").PgColumn<{
|
|
110
|
+
name: "source_provider";
|
|
111
|
+
tableName: "booking_catalog_snapshot";
|
|
112
|
+
dataType: "string";
|
|
113
|
+
columnType: "PgText";
|
|
114
|
+
data: string;
|
|
115
|
+
driverParam: string;
|
|
116
|
+
notNull: false;
|
|
117
|
+
hasDefault: false;
|
|
118
|
+
isPrimaryKey: false;
|
|
119
|
+
isAutoincrement: false;
|
|
120
|
+
hasRuntimeDefault: false;
|
|
121
|
+
enumValues: [string, ...string[]];
|
|
122
|
+
baseColumn: never;
|
|
123
|
+
identity: undefined;
|
|
124
|
+
generated: undefined;
|
|
125
|
+
}, {}, {}>;
|
|
126
|
+
source_connection_id: import("drizzle-orm/pg-core").PgColumn<{
|
|
127
|
+
name: "source_connection_id";
|
|
128
|
+
tableName: "booking_catalog_snapshot";
|
|
129
|
+
dataType: "string";
|
|
130
|
+
columnType: "PgText";
|
|
131
|
+
data: string;
|
|
132
|
+
driverParam: string;
|
|
133
|
+
notNull: false;
|
|
134
|
+
hasDefault: false;
|
|
135
|
+
isPrimaryKey: false;
|
|
136
|
+
isAutoincrement: false;
|
|
137
|
+
hasRuntimeDefault: false;
|
|
138
|
+
enumValues: [string, ...string[]];
|
|
139
|
+
baseColumn: never;
|
|
140
|
+
identity: undefined;
|
|
141
|
+
generated: undefined;
|
|
142
|
+
}, {}, {}>;
|
|
143
|
+
source_ref: import("drizzle-orm/pg-core").PgColumn<{
|
|
144
|
+
name: "source_ref";
|
|
145
|
+
tableName: "booking_catalog_snapshot";
|
|
146
|
+
dataType: "string";
|
|
147
|
+
columnType: "PgText";
|
|
148
|
+
data: string;
|
|
149
|
+
driverParam: string;
|
|
150
|
+
notNull: false;
|
|
151
|
+
hasDefault: false;
|
|
152
|
+
isPrimaryKey: false;
|
|
153
|
+
isAutoincrement: false;
|
|
154
|
+
hasRuntimeDefault: false;
|
|
155
|
+
enumValues: [string, ...string[]];
|
|
156
|
+
baseColumn: never;
|
|
157
|
+
identity: undefined;
|
|
158
|
+
generated: undefined;
|
|
159
|
+
}, {}, {}>;
|
|
160
|
+
frozen_payload: import("drizzle-orm/pg-core").PgColumn<{
|
|
161
|
+
name: "frozen_payload";
|
|
162
|
+
tableName: "booking_catalog_snapshot";
|
|
163
|
+
dataType: "json";
|
|
164
|
+
columnType: "PgJsonb";
|
|
165
|
+
data: unknown;
|
|
166
|
+
driverParam: unknown;
|
|
167
|
+
notNull: true;
|
|
168
|
+
hasDefault: false;
|
|
169
|
+
isPrimaryKey: false;
|
|
170
|
+
isAutoincrement: false;
|
|
171
|
+
hasRuntimeDefault: false;
|
|
172
|
+
enumValues: undefined;
|
|
173
|
+
baseColumn: never;
|
|
174
|
+
identity: undefined;
|
|
175
|
+
generated: undefined;
|
|
176
|
+
}, {}, {}>;
|
|
177
|
+
overlay_state_at_capture: import("drizzle-orm/pg-core").PgColumn<{
|
|
178
|
+
name: "overlay_state_at_capture";
|
|
179
|
+
tableName: "booking_catalog_snapshot";
|
|
180
|
+
dataType: "json";
|
|
181
|
+
columnType: "PgJsonb";
|
|
182
|
+
data: unknown;
|
|
183
|
+
driverParam: unknown;
|
|
184
|
+
notNull: false;
|
|
185
|
+
hasDefault: false;
|
|
186
|
+
isPrimaryKey: false;
|
|
187
|
+
isAutoincrement: false;
|
|
188
|
+
hasRuntimeDefault: false;
|
|
189
|
+
enumValues: undefined;
|
|
190
|
+
baseColumn: never;
|
|
191
|
+
identity: undefined;
|
|
192
|
+
generated: undefined;
|
|
193
|
+
}, {}, {}>;
|
|
194
|
+
pricing_base_amount: import("drizzle-orm/pg-core").PgColumn<{
|
|
195
|
+
name: "pricing_base_amount";
|
|
196
|
+
tableName: "booking_catalog_snapshot";
|
|
197
|
+
dataType: "string";
|
|
198
|
+
columnType: "PgNumeric";
|
|
199
|
+
data: string;
|
|
200
|
+
driverParam: string;
|
|
201
|
+
notNull: false;
|
|
202
|
+
hasDefault: false;
|
|
203
|
+
isPrimaryKey: false;
|
|
204
|
+
isAutoincrement: false;
|
|
205
|
+
hasRuntimeDefault: false;
|
|
206
|
+
enumValues: undefined;
|
|
207
|
+
baseColumn: never;
|
|
208
|
+
identity: undefined;
|
|
209
|
+
generated: undefined;
|
|
210
|
+
}, {}, {}>;
|
|
211
|
+
pricing_taxes: import("drizzle-orm/pg-core").PgColumn<{
|
|
212
|
+
name: "pricing_taxes";
|
|
213
|
+
tableName: "booking_catalog_snapshot";
|
|
214
|
+
dataType: "string";
|
|
215
|
+
columnType: "PgNumeric";
|
|
216
|
+
data: string;
|
|
217
|
+
driverParam: string;
|
|
218
|
+
notNull: false;
|
|
219
|
+
hasDefault: false;
|
|
220
|
+
isPrimaryKey: false;
|
|
221
|
+
isAutoincrement: false;
|
|
222
|
+
hasRuntimeDefault: false;
|
|
223
|
+
enumValues: undefined;
|
|
224
|
+
baseColumn: never;
|
|
225
|
+
identity: undefined;
|
|
226
|
+
generated: undefined;
|
|
227
|
+
}, {}, {}>;
|
|
228
|
+
pricing_fees: import("drizzle-orm/pg-core").PgColumn<{
|
|
229
|
+
name: "pricing_fees";
|
|
230
|
+
tableName: "booking_catalog_snapshot";
|
|
231
|
+
dataType: "string";
|
|
232
|
+
columnType: "PgNumeric";
|
|
233
|
+
data: string;
|
|
234
|
+
driverParam: string;
|
|
235
|
+
notNull: false;
|
|
236
|
+
hasDefault: false;
|
|
237
|
+
isPrimaryKey: false;
|
|
238
|
+
isAutoincrement: false;
|
|
239
|
+
hasRuntimeDefault: false;
|
|
240
|
+
enumValues: undefined;
|
|
241
|
+
baseColumn: never;
|
|
242
|
+
identity: undefined;
|
|
243
|
+
generated: undefined;
|
|
244
|
+
}, {}, {}>;
|
|
245
|
+
pricing_surcharges: import("drizzle-orm/pg-core").PgColumn<{
|
|
246
|
+
name: "pricing_surcharges";
|
|
247
|
+
tableName: "booking_catalog_snapshot";
|
|
248
|
+
dataType: "string";
|
|
249
|
+
columnType: "PgNumeric";
|
|
250
|
+
data: string;
|
|
251
|
+
driverParam: string;
|
|
252
|
+
notNull: false;
|
|
253
|
+
hasDefault: false;
|
|
254
|
+
isPrimaryKey: false;
|
|
255
|
+
isAutoincrement: false;
|
|
256
|
+
hasRuntimeDefault: false;
|
|
257
|
+
enumValues: undefined;
|
|
258
|
+
baseColumn: never;
|
|
259
|
+
identity: undefined;
|
|
260
|
+
generated: undefined;
|
|
261
|
+
}, {}, {}>;
|
|
262
|
+
pricing_currency: import("drizzle-orm/pg-core").PgColumn<{
|
|
263
|
+
name: "pricing_currency";
|
|
264
|
+
tableName: "booking_catalog_snapshot";
|
|
265
|
+
dataType: "string";
|
|
266
|
+
columnType: "PgText";
|
|
267
|
+
data: string;
|
|
268
|
+
driverParam: string;
|
|
269
|
+
notNull: false;
|
|
270
|
+
hasDefault: false;
|
|
271
|
+
isPrimaryKey: false;
|
|
272
|
+
isAutoincrement: false;
|
|
273
|
+
hasRuntimeDefault: false;
|
|
274
|
+
enumValues: [string, ...string[]];
|
|
275
|
+
baseColumn: never;
|
|
276
|
+
identity: undefined;
|
|
277
|
+
generated: undefined;
|
|
278
|
+
}, {}, {}>;
|
|
279
|
+
pricing_breakdown: import("drizzle-orm/pg-core").PgColumn<{
|
|
280
|
+
name: "pricing_breakdown";
|
|
281
|
+
tableName: "booking_catalog_snapshot";
|
|
282
|
+
dataType: "json";
|
|
283
|
+
columnType: "PgJsonb";
|
|
284
|
+
data: Record<string, unknown>;
|
|
285
|
+
driverParam: unknown;
|
|
286
|
+
notNull: false;
|
|
287
|
+
hasDefault: false;
|
|
288
|
+
isPrimaryKey: false;
|
|
289
|
+
isAutoincrement: false;
|
|
290
|
+
hasRuntimeDefault: false;
|
|
291
|
+
enumValues: undefined;
|
|
292
|
+
baseColumn: never;
|
|
293
|
+
identity: undefined;
|
|
294
|
+
generated: undefined;
|
|
295
|
+
}, {}, {
|
|
296
|
+
$type: Record<string, unknown>;
|
|
297
|
+
}>;
|
|
298
|
+
pricing_applied_offers: import("drizzle-orm/pg-core").PgColumn<{
|
|
299
|
+
name: "pricing_applied_offers";
|
|
300
|
+
tableName: "booking_catalog_snapshot";
|
|
301
|
+
dataType: "json";
|
|
302
|
+
columnType: "PgJsonb";
|
|
303
|
+
data: AppliedOffer[];
|
|
304
|
+
driverParam: unknown;
|
|
305
|
+
notNull: false;
|
|
306
|
+
hasDefault: false;
|
|
307
|
+
isPrimaryKey: false;
|
|
308
|
+
isAutoincrement: false;
|
|
309
|
+
hasRuntimeDefault: false;
|
|
310
|
+
enumValues: undefined;
|
|
311
|
+
baseColumn: never;
|
|
312
|
+
identity: undefined;
|
|
313
|
+
generated: undefined;
|
|
314
|
+
}, {}, {
|
|
315
|
+
$type: AppliedOffer[];
|
|
316
|
+
}>;
|
|
317
|
+
idempotency_key: import("drizzle-orm/pg-core").PgColumn<{
|
|
318
|
+
name: "idempotency_key";
|
|
319
|
+
tableName: "booking_catalog_snapshot";
|
|
320
|
+
dataType: "string";
|
|
321
|
+
columnType: "PgText";
|
|
322
|
+
data: string;
|
|
323
|
+
driverParam: string;
|
|
324
|
+
notNull: false;
|
|
325
|
+
hasDefault: false;
|
|
326
|
+
isPrimaryKey: false;
|
|
327
|
+
isAutoincrement: false;
|
|
328
|
+
hasRuntimeDefault: false;
|
|
329
|
+
enumValues: [string, ...string[]];
|
|
330
|
+
baseColumn: never;
|
|
331
|
+
identity: undefined;
|
|
332
|
+
generated: undefined;
|
|
333
|
+
}, {}, {}>;
|
|
334
|
+
captured_at: import("drizzle-orm/pg-core").PgColumn<{
|
|
335
|
+
name: "captured_at";
|
|
336
|
+
tableName: "booking_catalog_snapshot";
|
|
337
|
+
dataType: "date";
|
|
338
|
+
columnType: "PgTimestamp";
|
|
339
|
+
data: Date;
|
|
340
|
+
driverParam: string;
|
|
341
|
+
notNull: true;
|
|
342
|
+
hasDefault: true;
|
|
343
|
+
isPrimaryKey: false;
|
|
344
|
+
isAutoincrement: false;
|
|
345
|
+
hasRuntimeDefault: false;
|
|
346
|
+
enumValues: undefined;
|
|
347
|
+
baseColumn: never;
|
|
348
|
+
identity: undefined;
|
|
349
|
+
generated: undefined;
|
|
350
|
+
}, {}, {}>;
|
|
351
|
+
};
|
|
352
|
+
dialect: "pg";
|
|
353
|
+
}>;
|
|
354
|
+
export type InsertBookingCatalogSnapshot = typeof bookingCatalogSnapshotTable.$inferInsert;
|
|
355
|
+
export type SelectBookingCatalogSnapshot = typeof bookingCatalogSnapshotTable.$inferSelect;
|
|
356
|
+
/**
|
|
357
|
+
* Helper to compose a `PricingBasis` from the structured columns of a
|
|
358
|
+
* snapshot row. Returns `null` if the snapshot has no pricing columns set
|
|
359
|
+
* (volatile-live fields without `on-quote` / `on-book` snapshot mode).
|
|
360
|
+
*/
|
|
361
|
+
export declare function readPricingBasis(row: SelectBookingCatalogSnapshot): PricingBasis | null;
|
|
362
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/snapshot/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2CAA2C,CAAA;AAK7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0CAA0C,CAAA;AAI5E,YAAY,EAAE,YAAY,EAAE,CAAA;AAE5B;;;;GAIG;AACH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+EvC,CAAA;AAED,MAAM,MAAM,4BAA4B,GAAG,OAAO,2BAA2B,CAAC,YAAY,CAAA;AAC1F,MAAM,MAAM,4BAA4B,GAAG,OAAO,2BAA2B,CAAC,YAAY,CAAA;AAE1F;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,4BAA4B,GAAG,YAAY,GAAG,IAAI,CAavF"}
|