/** * Protocol surface: what the SDK asks for, and what it does when the server * cannot give it. * * Everything here is generated from `shared/sparkletree/wire/contract.ts`, so * the constants below cannot drift from the Go edge or the Next twin. What * this file adds is the CLIENT half of the handshake, which the contract * describes but does not implement. */ import { PROTOCOL_CURRENT, PROTOCOL_DEFAULT, PROTOCOL_HEADER, PROTOCOL_PARAM, PROTOCOL_RESPONSE_HEADER, PROTOCOL_VERSIONS, SDK_PARAM, SDK_PARAM_VALUE, VARIANT_PARAM } from "./wire.js"; import type { CopyField } from "./state.js"; export { PROTOCOL_CURRENT, PROTOCOL_DEFAULT, PROTOCOL_HEADER, PROTOCOL_PARAM, PROTOCOL_RESPONSE_HEADER, PROTOCOL_VERSIONS, SDK_PARAM, SDK_PARAM_VALUE, VARIANT_PARAM, }; /** * Query param carrying the publishable key. A param, not a header, for the * same reason as the protocol version: a consumer's EventSource cannot set * headers. (The server also accepts `Authorization: Bearer st_pk_…`.) */ export declare const KEY_PARAM = "st-key"; /** Everything needed to address one island stream. */ export interface StreamTarget { /** Base URL of the embed API, e.g. `https://api.sparkletree.io`. */ apiBase: string; organizationId: string; /** * The publishable key (`st_pk_live_…` / `st_pk_test_…`): a signed * credential minted in the dashboard, required by the server on every * stream and content request. Optional in the type only so self-hosters * with explicit organizationId/apiBase can still build a URL; with * enforcement on, the server refuses a request without it. */ publishableKey?: string; campaignId?: string; surfaceId?: string; island: "hero" | "cta"; /** * Variant pinning (plan v1.1 §3.3). The provider resolves the variant ONCE * per page view and pins it here for every later call. Without it, ε-greedy * runs per request and a single page shows a mixed-variant creative — hero * from one arm, CTA from another — which is not an experiment, it is noise. */ variantId?: string; /** * Context snapshot (time, geo, weather, device, declared placement). * Serialised as JSON, with `custom` dimensions flattened to `x.` * keys as the server reads them. */ context?: Record; /** * The length budget per field, in characters (Unicode code points): the * length of each field's fallback, which sets the size of its slot. Sent * as `len.` so the server writes copy that fits. */ lengths?: Partial>; /** Conversation session id for copy continuity. Individual surfaces only. */ sessionId?: string; /** Extra params passed through verbatim (preview tokens, etc.). */ extra?: Record; } /** * Build the stream URL. * * `sdk=1` is not optional and not a feature flag: it tells the server to * suppress its own server-side impression for this request. The SDK renders * inside the customer's page, so it is the only party that knows whether the * creative was ever actually on screen — and a server-side impression on top * of the client's own would double-count every view, and would keep counting * from a CDN-cached SSR response that no human ever saw. */ export declare function buildStreamUrl(target: StreamTarget, protocol?: number): string; /** * Build the `/content` URL used for first paint. Same pinning rules as the * stream — but deliberately NOT the same `sdk=1` rule. * * `sdk=1` is a promise: "the SDK will record this impression itself". The * stream path keeps that promise — `useIsland` owns a visibility-gated * recorder. The content path CANNOT: its consumers (CopyCard, ProductCard, * the cookbook's content-only recipes) have no observer and no recorder, so * sending `sdk=1` here told the server to stop counting while nobody else * started — zero impressions for every content-only integration. Omitting it * leaves the server owning the `/content` impression, exactly as it does for * every non-SDK consumer of the endpoint. */ export declare function buildContentUrl(target: StreamTarget, protocol?: number): string; /** * Read the negotiated version off a response. * * An absent header means an older deployment that predates negotiation, which * by contract is protocol 1 — not an error, and specifically not a reason to * refuse the stream. Treating silence as failure here would break every * customer whose traffic happens to land on an edge revision mid-rollout. */ export declare function negotiatedProtocol(response: { headers: Headers; }): number; //# sourceMappingURL=protocol.d.ts.map