@voyantjs/products 0.19.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/booking-engine/handler.d.ts +203 -0
- package/dist/booking-engine/handler.d.ts.map +1 -0
- package/dist/booking-engine/handler.js +330 -0
- package/dist/booking-engine/index.d.ts +8 -0
- package/dist/booking-engine/index.d.ts.map +1 -0
- package/dist/booking-engine/index.js +7 -0
- package/dist/catalog-policy.d.ts +33 -0
- package/dist/catalog-policy.d.ts.map +1 -0
- package/dist/catalog-policy.js +421 -0
- package/dist/content-shape.d.ts +217 -0
- package/dist/content-shape.d.ts.map +1 -0
- package/dist/content-shape.js +159 -0
- package/dist/draft-shape.d.ts +43 -0
- package/dist/draft-shape.d.ts.map +1 -0
- package/dist/draft-shape.js +46 -0
- package/dist/events.d.ts +37 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +32 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/routes-content.d.ts +74 -0
- package/dist/routes-content.d.ts.map +1 -0
- package/dist/routes-content.js +117 -0
- package/dist/routes.d.ts +47 -26
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +88 -16
- package/dist/schema-core.d.ts +240 -1
- package/dist/schema-core.d.ts.map +1 -1
- package/dist/schema-core.js +49 -0
- package/dist/schema-itinerary.d.ts +18 -1
- package/dist/schema-itinerary.d.ts.map +1 -1
- package/dist/schema-itinerary.js +1 -0
- package/dist/schema-settings.d.ts +1 -1
- package/dist/schema-sourced-content.d.ts +262 -0
- package/dist/schema-sourced-content.d.ts.map +1 -0
- package/dist/schema-sourced-content.js +69 -0
- package/dist/schema-taxonomy.d.ts +17 -0
- package/dist/schema-taxonomy.d.ts.map +1 -1
- package/dist/schema-taxonomy.js +13 -0
- package/dist/schema.d.ts +1 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +1 -0
- package/dist/service-catalog-plane.d.ts +129 -0
- package/dist/service-catalog-plane.d.ts.map +1 -0
- package/dist/service-catalog-plane.js +212 -0
- package/dist/service-content-owned.d.ts +68 -0
- package/dist/service-content-owned.d.ts.map +1 -0
- package/dist/service-content-owned.js +224 -0
- package/dist/service-content-synthesizer.d.ts +90 -0
- package/dist/service-content-synthesizer.d.ts.map +1 -0
- package/dist/service-content-synthesizer.js +171 -0
- package/dist/service-content.d.ts +106 -0
- package/dist/service-content.d.ts.map +1 -0
- package/dist/service-content.js +365 -0
- package/dist/service.d.ts +82 -28
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +4 -0
- package/dist/tasks/brochures.d.ts +2 -1
- package/dist/tasks/brochures.d.ts.map +1 -1
- package/dist/tasks/brochures.js +3 -0
- package/dist/validation-catalog.d.ts +4 -4
- package/dist/validation-config.d.ts +3 -3
- package/dist/validation-content.d.ts +34 -4
- package/dist/validation-content.d.ts.map +1 -1
- package/dist/validation-content.js +13 -0
- package/dist/validation-core.d.ts +53 -3
- package/dist/validation-core.d.ts.map +1 -1
- package/dist/validation-core.js +16 -0
- package/dist/validation-public.d.ts +9 -9
- package/dist/validation-shared.d.ts +4 -4
- package/package.json +12 -6
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Product content synthesizer — fallback for thin adapters that declare
|
|
3
|
+
* `supportsContentFetch: false`.
|
|
4
|
+
*
|
|
5
|
+
* Per sourced-content §3.6, the synthesizer's job is to produce the
|
|
6
|
+
* **most complete** content payload it legitimately can from what the
|
|
7
|
+
* catalog plane already knows: the durable sourced-entry projection,
|
|
8
|
+
* the editorial overlay store, and plane-level provenance metadata.
|
|
9
|
+
*
|
|
10
|
+
* Returns the same shape as a real `getContent` so consumers (detail
|
|
11
|
+
* pages, the booking journey, snapshot capture) cannot tell the two
|
|
12
|
+
* paths apart from the type signature. Fields the synthesizer cannot
|
|
13
|
+
* fill render as typed empty states — never as missing properties.
|
|
14
|
+
*
|
|
15
|
+
* What it does NOT do:
|
|
16
|
+
* - Mine prior snapshot rows (those carry customer-scoped PII and
|
|
17
|
+
* are point-in-time, not generic content).
|
|
18
|
+
* - Machine-translate (that's the adapter's `getContent` +
|
|
19
|
+
* `machine_translated: true` path).
|
|
20
|
+
* - Synthesize plausible-but-unverified fields ("hotels usually have
|
|
21
|
+
* a pool" is not a basis for an amenity).
|
|
22
|
+
* - Cache its own output (inputs change at projection / overlay
|
|
23
|
+
* write time; a cache layer would just complicate invalidation).
|
|
24
|
+
*/
|
|
25
|
+
import type { ProvenanceReadResult } from "@voyantjs/catalog";
|
|
26
|
+
import type { AnyDrizzleDb } from "@voyantjs/db";
|
|
27
|
+
import { type ProductContent } from "./content-shape.js";
|
|
28
|
+
export interface SynthesizeProductContentOptions {
|
|
29
|
+
/**
|
|
30
|
+
* The provenance read for the entity. Carries the durable projection
|
|
31
|
+
* captured at discover() time — the synthesizer's primary input.
|
|
32
|
+
*/
|
|
33
|
+
provenance: Extract<ProvenanceReadResult, {
|
|
34
|
+
kind: "sourced";
|
|
35
|
+
}>;
|
|
36
|
+
/**
|
|
37
|
+
* Optional locale-aware overlays. When present, layered on top via
|
|
38
|
+
* RFC 6901 JSON pointer paths. Caller passes overlays already
|
|
39
|
+
* filtered to the active scope (locale, audience, market) per the
|
|
40
|
+
* variant fallback chain.
|
|
41
|
+
*/
|
|
42
|
+
overlays?: ReadonlyArray<{
|
|
43
|
+
field_path: string;
|
|
44
|
+
value: unknown;
|
|
45
|
+
}>;
|
|
46
|
+
}
|
|
47
|
+
export interface SynthesizedProductContent {
|
|
48
|
+
/** The synthesized payload. Validated against the products/v1 schema. */
|
|
49
|
+
content: ProductContent;
|
|
50
|
+
/** Schema version stamped on the synthesized payload. */
|
|
51
|
+
content_schema_version: string;
|
|
52
|
+
/**
|
|
53
|
+
* The locale this synthesis represents. Synthesizer output isn't
|
|
54
|
+
* locale-pinned at the projection level — projections capture the
|
|
55
|
+
* adapter's canonical fields. Caller's locale is reported back so
|
|
56
|
+
* UI / SWR machinery can stamp it consistently.
|
|
57
|
+
*/
|
|
58
|
+
served_locale: string;
|
|
59
|
+
/**
|
|
60
|
+
* Whether the upstream adapter is rich (this synthesis is a fallback)
|
|
61
|
+
* vs. thin (this synthesis is the real path).
|
|
62
|
+
*/
|
|
63
|
+
source_kind: string;
|
|
64
|
+
/**
|
|
65
|
+
* Source provenance hints for UI badges ("served by …", "limited
|
|
66
|
+
* content available").
|
|
67
|
+
*/
|
|
68
|
+
source_provider?: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Synthesize a `ProductContent` payload from projection + overlay +
|
|
72
|
+
* plane metadata. Pure-ish: takes a provenance read result and an
|
|
73
|
+
* optional overlay list; returns a typed-empty-state-filled content
|
|
74
|
+
* blob.
|
|
75
|
+
*/
|
|
76
|
+
export declare function synthesizeProductContent(scope: {
|
|
77
|
+
locale: string;
|
|
78
|
+
}, options: SynthesizeProductContentOptions): SynthesizedProductContent;
|
|
79
|
+
/**
|
|
80
|
+
* Drizzle-bound convenience wrapper: fetches active overlays for the
|
|
81
|
+
* entity and runs the synthesizer. Verticals call this from their
|
|
82
|
+
* content service when no `getContent` adapter is registered (or when
|
|
83
|
+
* the synthesizer is the fallback for missing-cache + thin-adapter).
|
|
84
|
+
*/
|
|
85
|
+
export declare function synthesizeProductContentFromDb(db: AnyDrizzleDb, scope: {
|
|
86
|
+
locale: string;
|
|
87
|
+
}, provenance: Extract<ProvenanceReadResult, {
|
|
88
|
+
kind: "sourced";
|
|
89
|
+
}>): Promise<SynthesizedProductContent>;
|
|
90
|
+
//# sourceMappingURL=service-content-synthesizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-content-synthesizer.d.ts","sourceRoot":"","sources":["../src/service-content-synthesizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAE7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,oBAAoB,CAAA;AAE3B,MAAM,WAAW,+BAA+B;IAC9C;;;OAGG;IACH,UAAU,EAAE,OAAO,CAAC,oBAAoB,EAAE;QAAE,IAAI,EAAE,SAAS,CAAA;KAAE,CAAC,CAAA;IAC9D;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;CACjE;AAED,MAAM,WAAW,yBAAyB;IACxC,yEAAyE;IACzE,OAAO,EAAE,cAAc,CAAA;IACvB,yDAAyD;IACzD,sBAAsB,EAAE,MAAM,CAAA;IAC9B;;;;;OAKG;IACH,aAAa,EAAE,MAAM,CAAA;IACrB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,EACzB,OAAO,EAAE,+BAA+B,GACvC,yBAAyB,CAoC3B;AAED;;;;;GAKG;AACH,wBAAsB,8BAA8B,CAClD,EAAE,EAAE,YAAY,EAChB,KAAK,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,EACzB,UAAU,EAAE,OAAO,CAAC,oBAAoB,EAAE;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC,GAC7D,OAAO,CAAC,yBAAyB,CAAC,CAMpC"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Product content synthesizer — fallback for thin adapters that declare
|
|
3
|
+
* `supportsContentFetch: false`.
|
|
4
|
+
*
|
|
5
|
+
* Per sourced-content §3.6, the synthesizer's job is to produce the
|
|
6
|
+
* **most complete** content payload it legitimately can from what the
|
|
7
|
+
* catalog plane already knows: the durable sourced-entry projection,
|
|
8
|
+
* the editorial overlay store, and plane-level provenance metadata.
|
|
9
|
+
*
|
|
10
|
+
* Returns the same shape as a real `getContent` so consumers (detail
|
|
11
|
+
* pages, the booking journey, snapshot capture) cannot tell the two
|
|
12
|
+
* paths apart from the type signature. Fields the synthesizer cannot
|
|
13
|
+
* fill render as typed empty states — never as missing properties.
|
|
14
|
+
*
|
|
15
|
+
* What it does NOT do:
|
|
16
|
+
* - Mine prior snapshot rows (those carry customer-scoped PII and
|
|
17
|
+
* are point-in-time, not generic content).
|
|
18
|
+
* - Machine-translate (that's the adapter's `getContent` +
|
|
19
|
+
* `machine_translated: true` path).
|
|
20
|
+
* - Synthesize plausible-but-unverified fields ("hotels usually have
|
|
21
|
+
* a pool" is not a basis for an amenity).
|
|
22
|
+
* - Cache its own output (inputs change at projection / overlay
|
|
23
|
+
* write time; a cache layer would just complicate invalidation).
|
|
24
|
+
*/
|
|
25
|
+
import { fetchOverlaysForEntity, mergeOverlaysIntoContent } from "@voyantjs/catalog";
|
|
26
|
+
import { PRODUCTS_CONTENT_SCHEMA_VERSION, productContentSchema, } from "./content-shape.js";
|
|
27
|
+
/**
|
|
28
|
+
* Synthesize a `ProductContent` payload from projection + overlay +
|
|
29
|
+
* plane metadata. Pure-ish: takes a provenance read result and an
|
|
30
|
+
* optional overlay list; returns a typed-empty-state-filled content
|
|
31
|
+
* blob.
|
|
32
|
+
*/
|
|
33
|
+
export function synthesizeProductContent(scope, options) {
|
|
34
|
+
const projection = options.provenance.projection;
|
|
35
|
+
const product = pickProductSummary(projection, options.provenance);
|
|
36
|
+
const media = pickMedia(projection);
|
|
37
|
+
const policies = pickPolicies(projection);
|
|
38
|
+
const baseContent = {
|
|
39
|
+
product,
|
|
40
|
+
options: [],
|
|
41
|
+
days: [],
|
|
42
|
+
media,
|
|
43
|
+
policies,
|
|
44
|
+
departures: [],
|
|
45
|
+
};
|
|
46
|
+
let merged = baseContent;
|
|
47
|
+
if (options.overlays && options.overlays.length > 0) {
|
|
48
|
+
const result = mergeOverlaysIntoContent(baseContent, options.overlays, {
|
|
49
|
+
validate(p) {
|
|
50
|
+
const r = productContentSchema.safeParse(p);
|
|
51
|
+
return r.success
|
|
52
|
+
? { valid: true }
|
|
53
|
+
: { valid: false, reason: r.error.issues[0]?.message ?? "invalid" };
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
merged = productContentSchema.parse(result);
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
content: merged,
|
|
60
|
+
content_schema_version: PRODUCTS_CONTENT_SCHEMA_VERSION,
|
|
61
|
+
served_locale: scope.locale,
|
|
62
|
+
source_kind: options.provenance.provenance.source_kind,
|
|
63
|
+
source_provider: options.provenance.provenance.source_provider,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Drizzle-bound convenience wrapper: fetches active overlays for the
|
|
68
|
+
* entity and runs the synthesizer. Verticals call this from their
|
|
69
|
+
* content service when no `getContent` adapter is registered (or when
|
|
70
|
+
* the synthesizer is the fallback for missing-cache + thin-adapter).
|
|
71
|
+
*/
|
|
72
|
+
export async function synthesizeProductContentFromDb(db, scope, provenance) {
|
|
73
|
+
const overlays = await fetchOverlaysForEntity(db, "products", entityIdFromProvenance(provenance));
|
|
74
|
+
return synthesizeProductContent(scope, {
|
|
75
|
+
provenance,
|
|
76
|
+
overlays: overlays.map((o) => ({ field_path: o.field_path, value: o.value })),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
function entityIdFromProvenance(provenance) {
|
|
80
|
+
// The sourced-entry row's entry_id is the catalog-side TypeID; the
|
|
81
|
+
// products module-side id is on the projection row. Synthesizer
|
|
82
|
+
// callers thread the products-module entity_id through scope; we
|
|
83
|
+
// take it off the projection's `id` field when the adapter set it,
|
|
84
|
+
// falling back to the entry_id (which is wrong for read paths but
|
|
85
|
+
// safe for tests).
|
|
86
|
+
const fromProjection = provenance.projection.id;
|
|
87
|
+
if (typeof fromProjection === "string" && fromProjection.length > 0) {
|
|
88
|
+
return fromProjection;
|
|
89
|
+
}
|
|
90
|
+
return provenance.entry_id;
|
|
91
|
+
}
|
|
92
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
93
|
+
// Field pickers
|
|
94
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
95
|
+
function pickProductSummary(projection, provenance) {
|
|
96
|
+
return {
|
|
97
|
+
id: stringOr(projection.id, "") || provenance.entry_id,
|
|
98
|
+
name: stringOr(projection.name, "") || stringOr(projection.title, "") || "Unnamed product",
|
|
99
|
+
status: stringOr(projection.status, undefined),
|
|
100
|
+
description: stringOr(projection.description, null),
|
|
101
|
+
highlights: stringArrayOr(projection.highlights, []),
|
|
102
|
+
hero_image_url: stringOr(projection.hero_image_url, null),
|
|
103
|
+
duration_days: numberOr(projection.duration_days, null),
|
|
104
|
+
start_date: stringOr(projection.start_date, null),
|
|
105
|
+
end_date: stringOr(projection.end_date, null),
|
|
106
|
+
sell_currency: stringOr(projection.sell_currency, null),
|
|
107
|
+
supplier: stringOr(projection.supplier, null) ??
|
|
108
|
+
stringOr(projection.supplier_name, null) ??
|
|
109
|
+
provenance.provenance.source_provider ??
|
|
110
|
+
null,
|
|
111
|
+
country: stringOr(projection.country, null),
|
|
112
|
+
departure_city: stringOr(projection.departure_city, null),
|
|
113
|
+
tags: stringArrayOr(projection.tags, []),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function pickMedia(projection) {
|
|
117
|
+
const heroUrl = stringOr(projection.hero_image_url, null);
|
|
118
|
+
const out = [];
|
|
119
|
+
if (heroUrl) {
|
|
120
|
+
out.push({ url: heroUrl, type: "image", caption: null, alt: null });
|
|
121
|
+
}
|
|
122
|
+
// Some adapters send a `media` array on the projection. Accept it
|
|
123
|
+
// when shaped reasonably; ignore otherwise rather than synthesize.
|
|
124
|
+
const additional = projection.media;
|
|
125
|
+
if (Array.isArray(additional)) {
|
|
126
|
+
for (const item of additional) {
|
|
127
|
+
if (item && typeof item === "object") {
|
|
128
|
+
const obj = item;
|
|
129
|
+
const url = stringOr(obj.url, null);
|
|
130
|
+
if (!url)
|
|
131
|
+
continue;
|
|
132
|
+
out.push({
|
|
133
|
+
url,
|
|
134
|
+
type: pickMediaType(obj.type),
|
|
135
|
+
caption: stringOr(obj.caption, null),
|
|
136
|
+
alt: stringOr(obj.alt, null),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return out;
|
|
142
|
+
}
|
|
143
|
+
function pickMediaType(value) {
|
|
144
|
+
if (value === "video")
|
|
145
|
+
return "video";
|
|
146
|
+
if (value === "document")
|
|
147
|
+
return "document";
|
|
148
|
+
return "image";
|
|
149
|
+
}
|
|
150
|
+
function pickPolicies(projection) {
|
|
151
|
+
const policies = [];
|
|
152
|
+
const cancel = stringOr(projection.cancellation_policy, null);
|
|
153
|
+
if (cancel)
|
|
154
|
+
policies.push({ kind: "cancellation", body: cancel });
|
|
155
|
+
const payment = stringOr(projection.payment_terms, null);
|
|
156
|
+
if (payment)
|
|
157
|
+
policies.push({ kind: "payment", body: payment });
|
|
158
|
+
return policies;
|
|
159
|
+
}
|
|
160
|
+
function stringOr(value, fallback) {
|
|
161
|
+
return typeof value === "string" && value.length > 0 ? value : fallback;
|
|
162
|
+
}
|
|
163
|
+
function numberOr(value, fallback) {
|
|
164
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
165
|
+
}
|
|
166
|
+
function stringArrayOr(value, fallback) {
|
|
167
|
+
if (!Array.isArray(value))
|
|
168
|
+
return fallback;
|
|
169
|
+
const out = value.filter((v) => typeof v === "string");
|
|
170
|
+
return out.length > 0 ? out : fallback;
|
|
171
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Product content service — `getProductContent` with owned-vs-sourced
|
|
3
|
+
* dispatch, locale-resolved cache reads, SWR refresh, and synthesizer
|
|
4
|
+
* fallback.
|
|
5
|
+
*
|
|
6
|
+
* One entry point per vertical; the catalog plane stays neutral about
|
|
7
|
+
* per-vertical content shapes. Detail routes (operator and storefront)
|
|
8
|
+
* call `getProductContent(db, entityId, scope, options)` and get back a
|
|
9
|
+
* fully-resolved `ContentLocaleResolution<ProductContent>` regardless
|
|
10
|
+
* of whether the row is owned or sourced.
|
|
11
|
+
*
|
|
12
|
+
* Owned rows (entities in the products table without a sourced-entry
|
|
13
|
+
* row) read from the products tables directly — out of scope for this
|
|
14
|
+
* file in v1; callers that need owned reads compose them around this
|
|
15
|
+
* service. Phase D ships sourced + synthesizer; owned dispatch
|
|
16
|
+
* narrows when first sourced template adopts.
|
|
17
|
+
*
|
|
18
|
+
* See `docs/architecture/catalog-sourced-content.md` §3.3, §3.4, §3.6.
|
|
19
|
+
*/
|
|
20
|
+
import { type ContentLocaleResolution, type InvalidateOnDrift, type SourceAdapter, type SourceAdapterContext } from "@voyantjs/catalog";
|
|
21
|
+
import type { SourceAdapterRegistry } from "@voyantjs/catalog/booking-engine";
|
|
22
|
+
import type { AnyDrizzleDb } from "@voyantjs/db";
|
|
23
|
+
import { type ProductContent } from "./content-shape.js";
|
|
24
|
+
export interface ProductContentScope {
|
|
25
|
+
/**
|
|
26
|
+
* Ordered locale preference, most-preferred first. The deployment's
|
|
27
|
+
* configured fallback chain should be appended by the caller (e.g.
|
|
28
|
+
* `["ro-RO", "ro", "en-GB", "en"]` for a ro-RO storefront request).
|
|
29
|
+
*/
|
|
30
|
+
preferredLocales: ReadonlyArray<string>;
|
|
31
|
+
/** Optional market — `'*'` (any) by default. */
|
|
32
|
+
market?: string;
|
|
33
|
+
/** Optional currency for SWR refresh. Not used by the cache. */
|
|
34
|
+
currency?: string;
|
|
35
|
+
/**
|
|
36
|
+
* When false, the read service skips machine-translated rows in
|
|
37
|
+
* favor of the next-best non-MT match. False on operator-side views
|
|
38
|
+
* where ops want to see "real" content before deciding to override.
|
|
39
|
+
* Default: true (storefront-friendly).
|
|
40
|
+
*/
|
|
41
|
+
acceptMachineTranslated?: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface GetProductContentOptions {
|
|
44
|
+
/**
|
|
45
|
+
* Adapter registry — used to resolve the adapter for SWR refresh
|
|
46
|
+
* and cache-miss fetches. Required because the read service needs
|
|
47
|
+
* to know which adapter handles a given source_kind.
|
|
48
|
+
*/
|
|
49
|
+
registry: SourceAdapterRegistry;
|
|
50
|
+
/**
|
|
51
|
+
* Builds the per-call adapter context (typically supplies the
|
|
52
|
+
* connection_id + correlation_id).
|
|
53
|
+
*/
|
|
54
|
+
buildAdapterContext?: (adapter: SourceAdapter) => SourceAdapterContext;
|
|
55
|
+
/**
|
|
56
|
+
* Optional sink for per-overlay diagnostics emitted by the
|
|
57
|
+
* content-shape-aware merger.
|
|
58
|
+
*/
|
|
59
|
+
onOverlayError?: (event: {
|
|
60
|
+
field_path: string;
|
|
61
|
+
reason: string;
|
|
62
|
+
}) => void;
|
|
63
|
+
/**
|
|
64
|
+
* Bypass a fresh cache row and fetch directly from the source adapter.
|
|
65
|
+
* Use this for volatile fields embedded in content payloads, such as
|
|
66
|
+
* sourced departure capacity, where a 24h rich-content TTL is too coarse.
|
|
67
|
+
*/
|
|
68
|
+
forceFresh?: boolean;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The successful result of a content read. Carries the resolved content
|
|
72
|
+
* payload, locale-match metadata, and freshness markers so callers can
|
|
73
|
+
* render UI hints ("served in English").
|
|
74
|
+
*/
|
|
75
|
+
export interface ResolvedProductContent {
|
|
76
|
+
content: ProductContent;
|
|
77
|
+
resolution: ContentLocaleResolution<{
|
|
78
|
+
locale: string;
|
|
79
|
+
payload: ProductContent;
|
|
80
|
+
}>;
|
|
81
|
+
/** Where the resolved content came from. */
|
|
82
|
+
source: "sourced-cache" | "sourced-fresh" | "synthesized" | "owned";
|
|
83
|
+
/** True when the cache row was stale and a background refresh was scheduled. */
|
|
84
|
+
served_stale: boolean;
|
|
85
|
+
/** True for synthesizer output. */
|
|
86
|
+
synthesized: boolean;
|
|
87
|
+
/** True when the upstream marked this content machine-translated. */
|
|
88
|
+
machine_translated: boolean;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Read the rich product content for one entity, resolving locale
|
|
92
|
+
* preference, applying overlays, and refreshing in the background when
|
|
93
|
+
* stale. Returns `null` only when the entity is unknown (no
|
|
94
|
+
* sourced-entry row, no owned row).
|
|
95
|
+
*/
|
|
96
|
+
export declare function getProductContent(db: AnyDrizzleDb, entityId: string, scope: ProductContentScope, options: GetProductContentOptions): Promise<ResolvedProductContent | null>;
|
|
97
|
+
/**
|
|
98
|
+
* Drift event consumer — sets `fresh_until = now()` on every cache row
|
|
99
|
+
* matching the event's (entity_module, entity_id [, locale [, market]])
|
|
100
|
+
* scope. The next read serves stale + schedules a SWR refresh.
|
|
101
|
+
*
|
|
102
|
+
* Templates subscribe this to the catalog plane's drift-event bus.
|
|
103
|
+
* Per sourced-content §3.4.1.
|
|
104
|
+
*/
|
|
105
|
+
export declare const invalidateProductContentOnDrift: InvalidateOnDrift;
|
|
106
|
+
//# sourceMappingURL=service-content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-content.d.ts","sourceRoot":"","sources":["../src/service-content.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACL,KAAK,uBAAuB,EAK5B,KAAK,iBAAiB,EAKtB,KAAK,aAAa,EAClB,KAAK,oBAAoB,EAE1B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAA;AAC7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAGhD,OAAO,EAGL,KAAK,cAAc,EAGpB,MAAM,oBAAoB,CAAA;AAe3B,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,gBAAgB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IACvC,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAA;CAClC;AAED,MAAM,WAAW,wBAAwB;IACvC;;;;OAIG;IACH,QAAQ,EAAE,qBAAqB,CAAA;IAC/B;;;OAGG;IACH,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,oBAAoB,CAAA;IACtE;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;IACxE;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,cAAc,CAAA;IACvB,UAAU,EAAE,uBAAuB,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,cAAc,CAAA;KAAE,CAAC,CAAA;IAChF,4CAA4C;IAC5C,MAAM,EAAE,eAAe,GAAG,eAAe,GAAG,aAAa,GAAG,OAAO,CAAA;IACnE,gFAAgF;IAChF,YAAY,EAAE,OAAO,CAAA;IACrB,mCAAmC;IACnC,WAAW,EAAE,OAAO,CAAA;IACpB,qEAAqE;IACrE,kBAAkB,EAAE,OAAO,CAAA;CAC5B;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,YAAY,EAChB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,mBAAmB,EAC1B,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CA+KxC;AAoQD;;;;;;;GAOG;AACH,eAAO,MAAM,+BAA+B,EAAE,iBAG7C,CAAA"}
|