/**
* The client: fetch `/content` for first paint, then open the stream and feed
* every event into the choreographer.
*
* The event dispatch below is the ONLY place in the SDK that knows event
* names, and every name it knows comes from the generated contract. A server
* that starts emitting something newer than this build is not an error — it is
* an older client, which the protocol explicitly supports — so unknown events
* are ignored rather than warned about.
*/
import { type StreamTarget } from "./protocol.js";
import { CopyChoreographer, type ChoreographerOptions, type CreativeState } from "./state.js";
/**
* The image the server sends in `ContentSnapshot.content.image`. It is an
* object, not a URL string: for an `
` src read `.url`, and for a video
* poster read `.heroFrame?.url`.
*/
export interface ContentImage {
url?: string;
alt?: string;
heroFrame?: {
url?: string;
};
/** Opaque; shape owned by the server. */
sequence?: unknown;
/** Opaque; shape owned by the server. */
focal?: unknown;
}
export interface ContentSnapshot {
campaignId: string;
variantId: string;
content: {
headline?: string;
body?: string;
cta?: string | {
text?: string;
};
/**
* An object. Use `image.url` as an `
` src, not `image` itself.
* `null` when the variant has no image: the edge server sends
* `"image": null`, and the app server leaves the key out.
*/
image?: ContentImage | null;
experienceManifest?: unknown;
};
theme?: {
colors: Record;
sources: Record;
};
products?: unknown[];
meta?: Record;
}
export interface StreamOptions extends ChoreographerOptions {
target: StreamTarget;
signal?: AbortSignal;
/** Swap the transport (SSR, tests, a customer's instrumented fetch). */
fetchImpl?: typeof fetch;
/** Called on every state change. */
onState?: (state: CreativeState) => void;
/**
* Drive an existing choreographer instead of creating one. For a renderer
* that started holding before the request could open (a stream waiting on
* a shared variant): one choreographer, so one hold budget, spans the wait
* and the stream. The choreographer options in these options are ignored
* when this is set.
*/
choreographer?: CopyChoreographer;
/**
* Called when the server cannot speak the version this build requested. The
* SDK has already kept its own first paint by the time this fires; the hook
* exists so a consumer can log it once, not so it can retry.
*/
onProtocolUnsupported?: (info: {
requested: number;
supported: number[];
}) => void;
}
export interface StreamHandle {
/** The live choreographer. Subscribe for updates. */
choreographer: CopyChoreographer;
/** Resolves when the stream ends, however it ends. Never rejects. */
finished: Promise;
/** Stop early. Idempotent. */
close: () => void;
}
/**
* Fetch the first-paint content.
*
* Content-first, rewrite-once: this is what the SDK paints BEFORE any stream
* exists, so there is never a skeleton on screen. If it fails, the consumer's
* `fallback` prop is what stands in — which is why that prop is required
* rather than optional.
*/
export declare function fetchContent(target: StreamTarget, options?: {
signal?: AbortSignal;
fetchImpl?: typeof fetch;
}): Promise;
/**
* Open an island stream and choreograph it.
*
* Returns immediately with a handle; `finished` resolves with the terminal
* state. It never rejects — a broken stream leaves whatever was painted on
* screen and reports `phase: "failed"`, because throwing from inside a
* customer's render tree is a worse outcome than a creative that stopped
* adapting.
*/
export declare function openStream(options: StreamOptions): StreamHandle;
//# sourceMappingURL=client.d.ts.map