Sources
The GatewaySources contract: how the gateway reads your source of truth, field by field — and the guarantees that govern what it renders.
The contract
A source is a lookup from a request URL to your source-of-truth data. Return the data when the URL is yours; return null (or undefined) when it isn’t. Sync or async both work — but resolvers run on the edge hot path (p95 < 50ms compute budget), so back them with your in-memory catalog, a KV cache, or a fast local store, not an origin round-trip.
interface GatewaySources { // all optional; missing source => pass through
product?: (url: URL) => ProductSource | null | Promise<ProductSource | null>
policies?: (url: URL) => PolicySource[] | null | Promise<PolicySource[] | null>
catalog?: (url: URL) => CatalogItemSource[] | null | Promise<CatalogItemSource[] | null>
}Resolution order & error containment
- The order is fixed:
product→policies→catalog. The first source returning data wins — product wins when several would match a URL. - A source that throws is treated as "no match" and resolution continues with the next source. It never breaks your site.
- If the render itself fails, the request passes through to HTML — the gateway never substitutes a lower-precedence document for the one that matched.
- An empty
policies/catalogarray is no match. So is a missing source: configure only the resolvers you have.
ProductSource
One product detail page. Rendering order is fixed and front-loaded: title (linked to the canonical URL) → brand → price (compare-at struck through when present: ~~$120.00~~ $89.00) → availability → shipping → returns → variants table → description → attributes → images as markdown links. Absent optionals render nothing.
| Field | Type | Required | Notes |
|---|---|---|---|
url | string | yes | Canonical product URL — the linked title points here. |
title | string | yes | Product title. |
brand | string | no | Rendered as a Brand fact line. |
description | string | no | Rendered verbatim under a Description heading. |
price | Money | yes | Current price. Minor units (see Money below). |
compareAtPrice | Money | no | Struck through next to the price when present. |
availability | Availability | yes | 'in_stock' | 'out_of_stock' | 'preorder' | 'backorder' — rendered with deterministic labels (In stock, …). |
variants | ProductVariantSource[] | no | Rendered as a table: id / title / options / price / availability. |
shipping | ShippingSource | no | Front-loaded shipping facts. |
returns | ReturnsSource | no | Front-loaded returns facts. |
images | { url: string; alt?: string }[] | no | Rendered as markdown links at the bottom. |
attributes | Record<string, string> | no | Material, size-chart facts, etc. — rendered verbatim under Details. |
ProductVariantSource, ShippingSource, ReturnsSource
| Field | Type | Required | Notes |
|---|---|---|---|
ProductVariantSource.id | string | yes | Variant identifier (SKU-like id shown in the table). |
ProductVariantSource.title | string | yes | Variant title. |
ProductVariantSource.price | Money | yes | Per-variant price. |
ProductVariantSource.availability | Availability | yes | Per-variant stock state. |
ProductVariantSource.sku | string | no | Explicit SKU when distinct from id. |
ProductVariantSource.options | Record<string, string> | no | e.g. { Color: "Juniper Green" }. |
ShippingSource.summary | string | yes | Merchant-authored shipping summary. Rendered verbatim. |
ShippingSource.freeThreshold | Money | no | Free-shipping threshold. |
ShippingSource.regions | string[] | no | Ship-to regions. |
ShippingSource.etaDays | [number, number] | no | [min, max] delivery estimate in days, from the merchant. |
ReturnsSource.summary | string | yes | Merchant-authored returns summary. Rendered verbatim. |
ReturnsSource.windowDays | number | no | Return window in days. |
ReturnsSource.url | string | no | Link to the full returns policy. |
PolicySource
Policy documents (shipping, returns, warranty, …), rendered as linked headings + verbatim bodies.
| Field | Type | Required | Notes |
|---|---|---|---|
title | string | yes | Policy title — rendered as a linked heading. |
url | string | yes | Canonical policy URL. |
body | string | yes | Policy text. Rendered verbatim — never summarized or reworded. |
CatalogItemSource
Collection/catalog listings, rendered as a markdown table (linked title, price, availability).
| Field | Type | Required | Notes |
|---|---|---|---|
url | string | yes | Item URL — the table’s linked title points here. |
title | string | yes | Item title. |
price | Money | yes | Item price. |
availability | Availability | yes | Same enum as products. |
Money
| Field | Type | Notes |
|---|---|---|
amount | number | Integer minor units (cents for USD): 8900 → $89.00. Zero-decimal currencies are handled (¥4,900). |
currency | string | ISO currency code, e.g. USD. |
A non-integer amount throws rather than rounds — silently altering a price is never acceptable.
What rendering guarantees
Size budget: output is capped at maxBytes (default 5120 bytes, UTF-8). Buying facts are front-loaded, and truncation only ever removes content from the bottom (description, attributes, images) on whole lines, appending a fixed truncation note — never the front-loaded facts or the variants table. If the facts alone exceed the budget, they are emitted anyway: facts are never sacrificed to the byte budget.