@sparkletree/core 0.1.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/LICENSE +21 -0
- package/README.md +105 -0
- package/dist/analytics.d.ts +75 -0
- package/dist/analytics.d.ts.map +1 -0
- package/dist/analytics.js +170 -0
- package/dist/analytics.js.map +1 -0
- package/dist/audio.d.ts +85 -0
- package/dist/audio.d.ts.map +1 -0
- package/dist/audio.js +465 -0
- package/dist/audio.js.map +1 -0
- package/dist/client.d.ts +79 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +251 -0
- package/dist/client.js.map +1 -0
- package/dist/context.d.ts +104 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +277 -0
- package/dist/context.js.map +1 -0
- package/dist/fragments.d.ts +108 -0
- package/dist/fragments.d.ts.map +1 -0
- package/dist/fragments.js +223 -0
- package/dist/fragments.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol.d.ts +70 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +97 -0
- package/dist/protocol.js.map +1 -0
- package/dist/publishableKey.d.ts +38 -0
- package/dist/publishableKey.d.ts.map +1 -0
- package/dist/publishableKey.js +69 -0
- package/dist/publishableKey.js.map +1 -0
- package/dist/safeUrl.d.ts +56 -0
- package/dist/safeUrl.d.ts.map +1 -0
- package/dist/safeUrl.js +117 -0
- package/dist/safeUrl.js.map +1 -0
- package/dist/sse.d.ts +51 -0
- package/dist/sse.d.ts.map +1 -0
- package/dist/sse.js +137 -0
- package/dist/sse.js.map +1 -0
- package/dist/state.d.ts +321 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +594 -0
- package/dist/state.js.map +1 -0
- package/dist/text.d.ts +10 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +29 -0
- package/dist/text.js.map +1 -0
- package/dist/theme.d.ts +52 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +88 -0
- package/dist/theme.js.map +1 -0
- package/dist/trust.d.ts +52 -0
- package/dist/trust.d.ts.map +1 -0
- package/dist/trust.js +95 -0
- package/dist/trust.js.map +1 -0
- package/dist/variant.d.ts +117 -0
- package/dist/variant.d.ts.map +1 -0
- package/dist/variant.js +167 -0
- package/dist/variant.js.map +1 -0
- package/dist/wire.d.ts +564 -0
- package/dist/wire.d.ts.map +1 -0
- package/dist/wire.js +133 -0
- package/dist/wire.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fragments — the core client for Declarative Context rung 2 (spec §2.3/§4.3).
|
|
3
|
+
*
|
|
4
|
+
* The keys and fallback strings the developer writes ARE the request schema:
|
|
5
|
+
* there is nothing to create in a dashboard before the first render. The flow
|
|
6
|
+
* is fixed and deliberately boring:
|
|
7
|
+
*
|
|
8
|
+
* 1. The caller's fallbacks are the result shape, synchronously computable.
|
|
9
|
+
* 2. ctxhash + schemahash are computed locally (declared dims ONLY — env
|
|
10
|
+
* signals never enter the fragment identity, so cardinality stays "the
|
|
11
|
+
* number of call sites in the org's source").
|
|
12
|
+
* 3. One cacheable GET. Warm hit → serve those fields; anything else → the
|
|
13
|
+
* fallbacks stand.
|
|
14
|
+
* 4. On a miss with `pending: true`, ONE fire-and-forget POST /mint,
|
|
15
|
+
* deduplicated per session. No second GET, no polling, no SSE — twenty
|
|
16
|
+
* tokens do not need choreography.
|
|
17
|
+
*
|
|
18
|
+
* `fetchFragments` NEVER rejects. Any failure — network, malformed body,
|
|
19
|
+
* hashing, a hostile response — resolves to fallback-shaped fields with
|
|
20
|
+
* source "static". The developer's copy is the floor.
|
|
21
|
+
*/
|
|
22
|
+
import { type DeclaredContext } from "./context.js";
|
|
23
|
+
import type { FieldState } from "./state.js";
|
|
24
|
+
import type { ContentSource, FragmentKind, FragmentWarmth } from "./wire.js";
|
|
25
|
+
/** Bare-string shorthand: `"Sign in"` means `{ kind: "body", fallback: "Sign in" }`. */
|
|
26
|
+
export type FragmentFieldSpec = string | {
|
|
27
|
+
kind?: FragmentKind;
|
|
28
|
+
fallback: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* A signed mint token, or a function producing one.
|
|
32
|
+
*
|
|
33
|
+
* The token is minted by the CUSTOMER'S BACKEND (POST /fragments/mint-token
|
|
34
|
+
* with an org-pinned `st_` key) and is bound to one (org, ctxhash, schemahash)
|
|
35
|
+
* for 300 seconds. The browser only echoes it. The callback form exists
|
|
36
|
+
* because of that TTL: a page open longer than five minutes needs a fresh
|
|
37
|
+
* token at mint time, not the one that shipped with the HTML.
|
|
38
|
+
*/
|
|
39
|
+
export type MintTokenSource = string | (() => string | null | undefined | Promise<string | null | undefined>);
|
|
40
|
+
export interface FetchFragmentsOptions {
|
|
41
|
+
organizationId: string;
|
|
42
|
+
apiBase: string;
|
|
43
|
+
context?: DeclaredContext;
|
|
44
|
+
fields: Record<string, FragmentFieldSpec>;
|
|
45
|
+
/** Sent on the mint POST only — never on the GET, which is a shared CDN
|
|
46
|
+
* object and must be byte-identical for every caller. Optional: without it
|
|
47
|
+
* the mint goes unsigned and the server admits it or not. */
|
|
48
|
+
mintToken?: MintTokenSource;
|
|
49
|
+
/** Where `apiBase` came from. The publishable key is an unsigned address
|
|
50
|
+
* anyone can hand-encode, so a key-derived apiBase earns content fetches
|
|
51
|
+
* but NOT the mint POST (which carries the signed token — a real spend
|
|
52
|
+
* credential — and the declared dims) unless the host is first-party or
|
|
53
|
+
* loopback (S-2). Defaults to "explicit": a caller passing apiBase
|
|
54
|
+
* directly has chosen it themselves. */
|
|
55
|
+
apiBaseProvenance?: "explicit" | "key";
|
|
56
|
+
/** Test seam; defaults to the platform fetch. */
|
|
57
|
+
fetchImpl?: typeof fetch;
|
|
58
|
+
}
|
|
59
|
+
export interface FragmentsResult {
|
|
60
|
+
/** One FieldState per requested key — the exact shape islands produce. */
|
|
61
|
+
fields: Record<string, FieldState>;
|
|
62
|
+
/** Locally computed declared-context hash. Third leg of the pin key. */
|
|
63
|
+
contextHash: string;
|
|
64
|
+
warmth: FragmentWarmth;
|
|
65
|
+
/** decideChrome over the strongest served provenance (cached counts as
|
|
66
|
+
* adapted). The group-level Art. 50 disclosure hangs off this. */
|
|
67
|
+
marked: boolean;
|
|
68
|
+
}
|
|
69
|
+
/** The normalized request schema: what /mint receives, what schemahash covers. */
|
|
70
|
+
export interface NormalizedFragmentField {
|
|
71
|
+
key: string;
|
|
72
|
+
kind: FragmentKind;
|
|
73
|
+
budget: number;
|
|
74
|
+
fallback: string;
|
|
75
|
+
}
|
|
76
|
+
/** Expand shorthands and derive budgets. Deterministic: same fields object →
|
|
77
|
+
* same schema → same schemahash. */
|
|
78
|
+
export declare function normalizeFieldSpecs(fields: Record<string, FragmentFieldSpec>): NormalizedFragmentField[];
|
|
79
|
+
/** Strongest provenance across a set of field states (static < cached < generated). */
|
|
80
|
+
export declare function strongestFragmentSource(fields: Iterable<FieldState>): ContentSource;
|
|
81
|
+
/**
|
|
82
|
+
* Resolve fragments for a declared context. Resolves fast; NEVER rejects;
|
|
83
|
+
* fallback-shaped on any failure.
|
|
84
|
+
*/
|
|
85
|
+
export declare function fetchFragments(options: FetchFragmentsOptions): Promise<FragmentsResult>;
|
|
86
|
+
export interface CreateClientOptions {
|
|
87
|
+
/** `st_pk_…` from the dashboard. An address, not a credential (see
|
|
88
|
+
* publishableKey.ts) — explicit organizationId/apiBase win over it. */
|
|
89
|
+
publishableKey?: string;
|
|
90
|
+
organizationId?: string;
|
|
91
|
+
apiBase?: string;
|
|
92
|
+
/** Signed mint token (or a function returning one), sent on the mint POST. */
|
|
93
|
+
mintToken?: MintTokenSource;
|
|
94
|
+
fetchImpl?: typeof fetch;
|
|
95
|
+
}
|
|
96
|
+
export interface SparkletreeClient {
|
|
97
|
+
fragments(options: {
|
|
98
|
+
context?: DeclaredContext;
|
|
99
|
+
fields: Record<string, FragmentFieldSpec>;
|
|
100
|
+
}): Promise<FragmentsResult>;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Vanilla entry point, in core where the wire lives. Throws TypeError on a
|
|
104
|
+
* malformed key or missing configuration — setup errors surface at setup
|
|
105
|
+
* time; only the resolved `fragments()` call is the never-rejects path.
|
|
106
|
+
*/
|
|
107
|
+
export declare function createClient(options: CreateClientOptions): SparkletreeClient;
|
|
108
|
+
//# sourceMappingURL=fragments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fragments.d.ts","sourceRoot":"","sources":["../src/fragments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAKL,KAAK,eAAe,EACrB,MAAM,cAAc,CAAC;AAGtB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7C,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,cAAc,EAEf,MAAM,WAAW,CAAC;AAEnB,wFAAwF;AACxF,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,YAAY,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnF;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GACvB,MAAM,GACN,CAAC,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC;AAE3E,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC1C;;iEAE6D;IAC7D,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B;;;;;4CAKwC;IACxC,iBAAiB,CAAC,EAAE,UAAU,GAAG,KAAK,CAAC;IACvC,iDAAiD;IACjD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,wEAAwE;IACxE,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,cAAc,CAAC;IACvB;sEACkE;IAClE,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,kFAAkF;AAClF,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAKD;oCACoC;AACpC,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GACxC,uBAAuB,EAAE,CAU3B;AAED,uFAAuF;AACvF,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,aAAa,CAMnF;AAqFD;;;GAGG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CA+D7F;AAID,MAAM,WAAW,mBAAmB;IAClC;2EACuE;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,OAAO,EAAE;QACjB,OAAO,CAAC,EAAE,eAAe,CAAC;QAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;KAC3C,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,iBAAiB,CA+B5E"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fragments — the core client for Declarative Context rung 2 (spec §2.3/§4.3).
|
|
3
|
+
*
|
|
4
|
+
* The keys and fallback strings the developer writes ARE the request schema:
|
|
5
|
+
* there is nothing to create in a dashboard before the first render. The flow
|
|
6
|
+
* is fixed and deliberately boring:
|
|
7
|
+
*
|
|
8
|
+
* 1. The caller's fallbacks are the result shape, synchronously computable.
|
|
9
|
+
* 2. ctxhash + schemahash are computed locally (declared dims ONLY — env
|
|
10
|
+
* signals never enter the fragment identity, so cardinality stays "the
|
|
11
|
+
* number of call sites in the org's source").
|
|
12
|
+
* 3. One cacheable GET. Warm hit → serve those fields; anything else → the
|
|
13
|
+
* fallbacks stand.
|
|
14
|
+
* 4. On a miss with `pending: true`, ONE fire-and-forget POST /mint,
|
|
15
|
+
* deduplicated per session. No second GET, no polling, no SSE — twenty
|
|
16
|
+
* tokens do not need choreography.
|
|
17
|
+
*
|
|
18
|
+
* `fetchFragments` NEVER rejects. Any failure — network, malformed body,
|
|
19
|
+
* hashing, a hostile response — resolves to fallback-shaped fields with
|
|
20
|
+
* source "static". The developer's copy is the floor.
|
|
21
|
+
*/
|
|
22
|
+
import { computeFieldBudget, computeSchemaHash, declaredToDims, hashDeclaredContext, } from "./context.js";
|
|
23
|
+
import { decodePublishableKey } from "./publishableKey.js";
|
|
24
|
+
import { isFirstPartyApiBase, isSecureApiBase } from "./safeUrl.js";
|
|
25
|
+
import { decideChrome } from "./trust.js";
|
|
26
|
+
import { FRAGMENT_KINDS, FRAGMENT_WARMTHS } from "./wire.js";
|
|
27
|
+
const VALID_SOURCES = new Set(["generated", "cached", "static"]);
|
|
28
|
+
const SOURCE_RANK = { static: 0, cached: 1, generated: 2 };
|
|
29
|
+
/** Expand shorthands and derive budgets. Deterministic: same fields object →
|
|
30
|
+
* same schema → same schemahash. */
|
|
31
|
+
export function normalizeFieldSpecs(fields) {
|
|
32
|
+
return Object.entries(fields).map(([key, spec]) => {
|
|
33
|
+
const expanded = typeof spec === "string" ? { fallback: spec } : spec;
|
|
34
|
+
const kind = expanded.kind && FRAGMENT_KINDS.includes(expanded.kind)
|
|
35
|
+
? expanded.kind
|
|
36
|
+
: "body";
|
|
37
|
+
const fallback = expanded.fallback ?? "";
|
|
38
|
+
return { key, kind, budget: computeFieldBudget(fallback, kind), fallback };
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/** Strongest provenance across a set of field states (static < cached < generated). */
|
|
42
|
+
export function strongestFragmentSource(fields) {
|
|
43
|
+
let strongest = "static";
|
|
44
|
+
for (const field of fields) {
|
|
45
|
+
if (SOURCE_RANK[field.source] > SOURCE_RANK[strongest])
|
|
46
|
+
strongest = field.source;
|
|
47
|
+
}
|
|
48
|
+
return strongest;
|
|
49
|
+
}
|
|
50
|
+
/** FieldState for fragment copy. Fragments never type: text is set once,
|
|
51
|
+
* settled, with no caret — the single rewrite, not choreography. */
|
|
52
|
+
function fragmentField(text, source) {
|
|
53
|
+
return { text, source, mode: null, typing: false, settled: true };
|
|
54
|
+
}
|
|
55
|
+
function fallbackFields(schema) {
|
|
56
|
+
return Object.fromEntries(schema.map((f) => [f.key, fragmentField(f.fallback, "static")]));
|
|
57
|
+
}
|
|
58
|
+
/** The header the mint POST echoes the signed token on. */
|
|
59
|
+
const MINT_TOKEN_HEADER = "x-st-mint-token";
|
|
60
|
+
/**
|
|
61
|
+
* Resolve a mint token to a header-ready string, or "" for "send no header".
|
|
62
|
+
*
|
|
63
|
+
* Defensive on purpose: the callback reaches into the host app's auth code,
|
|
64
|
+
* and a token endpoint that is down, slow or throwing must cost a mint at
|
|
65
|
+
* worst — never a render. The server decides admission; an unsigned mint is a
|
|
66
|
+
* refusal it can make cheaply, and the fallback copy already shipped.
|
|
67
|
+
*/
|
|
68
|
+
async function resolveMintToken(source) {
|
|
69
|
+
try {
|
|
70
|
+
const value = typeof source === "function" ? await source() : source;
|
|
71
|
+
return typeof value === "string" ? value.trim() : "";
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return "";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Fire the one permitted mint for this (ctxhash, schemahash), deduplicated in
|
|
79
|
+
* sessionStorage. Outside a browser there is no page view to warm — skip
|
|
80
|
+
* entirely. In a privacy mode where sessionStorage throws, the POST still
|
|
81
|
+
* fires once per call; the server's queue dedup (SET NX) absorbs repeats.
|
|
82
|
+
*/
|
|
83
|
+
function maybeMint(options, schema, contextHash, schemaHash) {
|
|
84
|
+
if (typeof window === "undefined")
|
|
85
|
+
return;
|
|
86
|
+
// S-2: a key-derived apiBase is an address anyone can hand-encode. The mint
|
|
87
|
+
// POST carries the signed token (a spend credential) and the declared dims,
|
|
88
|
+
// so it only follows an apiBase the developer chose explicitly, or one
|
|
89
|
+
// provably first-party/loopback. Withheld, fragments simply stay cold —
|
|
90
|
+
// the fallback already shipped, and nothing leaks.
|
|
91
|
+
if (options.apiBaseProvenance === "key" && !isFirstPartyApiBase(options.apiBase))
|
|
92
|
+
return;
|
|
93
|
+
const dedupKey = `st:frag:mint:${contextHash}:${schemaHash}`;
|
|
94
|
+
try {
|
|
95
|
+
if (window.sessionStorage.getItem(dedupKey) === "1")
|
|
96
|
+
return;
|
|
97
|
+
window.sessionStorage.setItem(dedupKey, "1");
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// SSR shims and private modes may throw on ACCESS; dedup is best-effort.
|
|
101
|
+
}
|
|
102
|
+
const doFetch = options.fetchImpl ?? fetch;
|
|
103
|
+
const url = new URL("/api/embed/v1/fragments/mint", options.apiBase);
|
|
104
|
+
// Fire-and-forget: the response is ignored except in dev, and a failed mint
|
|
105
|
+
// is indistinguishable from a cold cache — the fallback already shipped.
|
|
106
|
+
void Promise.resolve()
|
|
107
|
+
.then(async () => {
|
|
108
|
+
const token = await resolveMintToken(options.mintToken);
|
|
109
|
+
return doFetch(url.toString(), {
|
|
110
|
+
method: "POST",
|
|
111
|
+
headers: {
|
|
112
|
+
"Content-Type": "application/json",
|
|
113
|
+
// Absent, not empty: a blank token is a PRESENT-but-bad token to the
|
|
114
|
+
// server, which refuses outright rather than falling back to Origin.
|
|
115
|
+
...(token ? { [MINT_TOKEN_HEADER]: token } : {}),
|
|
116
|
+
},
|
|
117
|
+
body: JSON.stringify({
|
|
118
|
+
organization: options.organizationId,
|
|
119
|
+
// Declared dims only, short-keyed — the server re-canonicalizes and
|
|
120
|
+
// recomputes both hashes; ours are cache hints, never identity.
|
|
121
|
+
context: declaredToDims(options.context ?? {}),
|
|
122
|
+
schema,
|
|
123
|
+
}),
|
|
124
|
+
});
|
|
125
|
+
})
|
|
126
|
+
.catch(() => { });
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Resolve fragments for a declared context. Resolves fast; NEVER rejects;
|
|
130
|
+
* fallback-shaped on any failure.
|
|
131
|
+
*/
|
|
132
|
+
export async function fetchFragments(options) {
|
|
133
|
+
const schema = normalizeFieldSpecs(options.fields);
|
|
134
|
+
// Same apiBase rule as openStream/fetchContent: this path is reached
|
|
135
|
+
// directly by the React bindings, which never go through createClient —
|
|
136
|
+
// without the guard here, an http: apiBase would carry context and the
|
|
137
|
+
// mint token over plaintext from the flagship integration path.
|
|
138
|
+
if (!isSecureApiBase(options.apiBase)) {
|
|
139
|
+
return { fields: fallbackFields(schema), contextHash: "", warmth: "cold", marked: false };
|
|
140
|
+
}
|
|
141
|
+
let contextHash = "";
|
|
142
|
+
try {
|
|
143
|
+
const [context, schemaHash] = await Promise.all([
|
|
144
|
+
hashDeclaredContext(options.context ?? {}),
|
|
145
|
+
computeSchemaHash(schema),
|
|
146
|
+
]);
|
|
147
|
+
contextHash = context.hash;
|
|
148
|
+
const url = new URL("/api/embed/v1/fragments", options.apiBase);
|
|
149
|
+
// Identity only — no prose, no SDK params: this URL is a shared CDN
|
|
150
|
+
// object and every extra param would fragment the cache.
|
|
151
|
+
url.searchParams.set("o", options.organizationId);
|
|
152
|
+
url.searchParams.set("c", contextHash);
|
|
153
|
+
url.searchParams.set("s", schemaHash);
|
|
154
|
+
const doFetch = options.fetchImpl ?? fetch;
|
|
155
|
+
const response = await doFetch(url.toString());
|
|
156
|
+
if (!response.ok) {
|
|
157
|
+
return { fields: fallbackFields(schema), contextHash, warmth: "cold", marked: false };
|
|
158
|
+
}
|
|
159
|
+
const body = (await response.json());
|
|
160
|
+
const served = body && typeof body === "object" && body.fragments && typeof body.fragments === "object"
|
|
161
|
+
? body.fragments
|
|
162
|
+
: {};
|
|
163
|
+
// Only keys we asked for, only shapes we recognize; everything else —
|
|
164
|
+
// extras, missing keys, unknown provenance — is the fallback. A response
|
|
165
|
+
// is a suggestion; the schema is the contract.
|
|
166
|
+
const fields = {};
|
|
167
|
+
for (const field of schema) {
|
|
168
|
+
const candidate = served[field.key];
|
|
169
|
+
fields[field.key] =
|
|
170
|
+
candidate &&
|
|
171
|
+
typeof candidate.text === "string" &&
|
|
172
|
+
candidate.text !== "" &&
|
|
173
|
+
VALID_SOURCES.has(candidate.source)
|
|
174
|
+
? fragmentField(candidate.text, candidate.source)
|
|
175
|
+
: fragmentField(field.fallback, "static");
|
|
176
|
+
}
|
|
177
|
+
const warmth = body?.warmth && FRAGMENT_WARMTHS.includes(body.warmth)
|
|
178
|
+
? body.warmth
|
|
179
|
+
: "cold";
|
|
180
|
+
if (body?.pending === true)
|
|
181
|
+
maybeMint(options, schema, contextHash, schemaHash);
|
|
182
|
+
// Core has no viewer preference to consult; the React layer re-decides
|
|
183
|
+
// with `prefersStandard`. This is the provenance-honest default.
|
|
184
|
+
const marked = decideChrome(strongestFragmentSource(Object.values(fields)), false).marked;
|
|
185
|
+
return { fields, contextHash, warmth, marked };
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return { fields: fallbackFields(schema), contextHash, warmth: "cold", marked: false };
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Vanilla entry point, in core where the wire lives. Throws TypeError on a
|
|
193
|
+
* malformed key or missing configuration — setup errors surface at setup
|
|
194
|
+
* time; only the resolved `fragments()` call is the never-rejects path.
|
|
195
|
+
*/
|
|
196
|
+
export function createClient(options) {
|
|
197
|
+
const decoded = options.publishableKey ? decodePublishableKey(options.publishableKey) : null;
|
|
198
|
+
const organizationId = options.organizationId ?? decoded?.organizationId;
|
|
199
|
+
const apiBase = options.apiBase ?? decoded?.apiBase;
|
|
200
|
+
if (!organizationId || !apiBase) {
|
|
201
|
+
throw new TypeError("SparkleTree: createClient needs a publishableKey, or organizationId + apiBase.");
|
|
202
|
+
}
|
|
203
|
+
// Setup errors surface at setup time: an apiBase that is neither https nor
|
|
204
|
+
// loopback http would send org configuration over a channel any middlebox
|
|
205
|
+
// can rewrite. Refuse here, where a throw is the package's documented
|
|
206
|
+
// contract for misconfiguration, rather than per-request.
|
|
207
|
+
if (!isSecureApiBase(apiBase)) {
|
|
208
|
+
throw new TypeError("SparkleTree: createClient apiBase must be https (http is allowed for localhost only).");
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
fragments: (callOptions) => fetchFragments({
|
|
212
|
+
organizationId,
|
|
213
|
+
apiBase,
|
|
214
|
+
// An explicit apiBase is the developer's own choice; a key-derived
|
|
215
|
+
// one is an unsigned address and earns no mint (see maybeMint, S-2).
|
|
216
|
+
apiBaseProvenance: options.apiBase ? "explicit" : "key",
|
|
217
|
+
mintToken: options.mintToken,
|
|
218
|
+
fetchImpl: options.fetchImpl,
|
|
219
|
+
...callOptions,
|
|
220
|
+
}),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=fragments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fragments.js","sourceRoot":"","sources":["../src/fragments.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @sparkletree/core — the framework-agnostic half of the SparkleTree SDK.
|
|
3
|
+
*
|
|
4
|
+
* Everything that touches the wire lives here, and ONLY here. Components
|
|
5
|
+
* distributed by `sparkletree add` are copied into a customer's repo and are
|
|
6
|
+
* therefore un-patchable by us; they must stay wire-ignorant, rendering state
|
|
7
|
+
* this package hands them. That split is what lets a protocol change ship as a
|
|
8
|
+
* version bump instead of a migration guide.
|
|
9
|
+
*
|
|
10
|
+
* Wire types are re-exported so consumers never import the generated contract
|
|
11
|
+
* directly — that path is an implementation detail and would make the
|
|
12
|
+
* generated artifacts part of the public API.
|
|
13
|
+
*/
|
|
14
|
+
export type { ContentSource, CopyMode, DegradeReason, DoneMeta, FragmentField, FragmentKind, FragmentWarmth, FragmentsResponse, ThemePayload, WireEventName, } from "./wire.js";
|
|
15
|
+
export { FRAGMENT_KINDS, FRAGMENT_WARMTHS } from "./wire.js";
|
|
16
|
+
export { PROTOCOL_CURRENT, PROTOCOL_DEFAULT, PROTOCOL_HEADER, PROTOCOL_PARAM, PROTOCOL_VERSIONS, SDK_PARAM, SDK_PARAM_VALUE, VARIANT_PARAM, buildContentUrl, buildStreamUrl, negotiatedProtocol, type StreamTarget, } from "./protocol.js";
|
|
17
|
+
export { SseParser, readSseStream, type SseMessage } from "./sse.js";
|
|
18
|
+
export { isFirstPartyApiBase, isSecureApiBase, safeActionUrl } from "./safeUrl.js";
|
|
19
|
+
export { CONTEXT_DAYPARTS, CONTEXT_DAY_TYPES, CONTEXT_DEVICES, CONTEXT_INTENTS, CONTEXT_SURFACE_TYPES, CONTEXT_TEMP_BANDS, CONTEXT_WEATHER, FRAGMENT_KIND_FLOORS, MAX_CUSTOM_DIMS, MAX_NOTES_LENGTH, canonicalContextString, computeContextHash, computeFieldBudget, computeSchemaHash, declaredToDims, hashDeclaredContext, normalizeDims, normalizeNotes, type CanonicalDims, type DeclaredContext, type NormalizeResult, } from "./context.js";
|
|
20
|
+
export { COPY_FIELDS, COPY_HOLD_MS, CopyChoreographer, type ChoreographerOptions, type CopyField, type CreativeState, type FieldState, type Phase, } from "./state.js";
|
|
21
|
+
export { fetchContent, openStream, type ContentSnapshot, type StreamHandle, type StreamOptions, } from "./client.js";
|
|
22
|
+
export { TOKEN_PREFIX, applyTheme, themeStyle, tokenName, tokenSource, type ResolvedTheme, } from "./theme.js";
|
|
23
|
+
export { STANDARD_MODE_KEY, decideChrome, setViewerPrefersStandard, viewerPrefersStandard, type ChromeDecision, } from "./trust.js";
|
|
24
|
+
export { ImpressionRecorder, observeVisibility, sessionId, track, type AnalyticsContext, type EventType, type ImpressionDetail, type VisibilityOptions, } from "./analytics.js";
|
|
25
|
+
export { RESERVATION_TIMEOUT_MS, VariantRegistry, pageVariants, type FollowerReservation, type LeaderReservation, type VariantReservation, } from "./variant.js";
|
|
26
|
+
export { activePlayer, destroyActiveAudio, mountAudio, type AudioBlock, type AudioCapabilities, type AudioEvent, type AudioHandle, type AudioProvider, type AudioState, } from "./audio.js";
|
|
27
|
+
export { plainText } from "./text.js";
|
|
28
|
+
export { readableTextColor } from "./theme.js";
|
|
29
|
+
export { decodePublishableKey, encodePublishableKey, type PublishableKeyEnv, type PublishableKeyPayload, } from "./publishableKey.js";
|
|
30
|
+
export { createClient, fetchFragments, normalizeFieldSpecs, strongestFragmentSource, type CreateClientOptions, type FetchFragmentsOptions, type FragmentFieldSpec, type FragmentsResult, type MintTokenSource, type NormalizedFragmentField, type SparkletreeClient, } from "./fragments.js";
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @sparkletree/core — the framework-agnostic half of the SparkleTree SDK.
|
|
3
|
+
*
|
|
4
|
+
* Everything that touches the wire lives here, and ONLY here. Components
|
|
5
|
+
* distributed by `sparkletree add` are copied into a customer's repo and are
|
|
6
|
+
* therefore un-patchable by us; they must stay wire-ignorant, rendering state
|
|
7
|
+
* this package hands them. That split is what lets a protocol change ship as a
|
|
8
|
+
* version bump instead of a migration guide.
|
|
9
|
+
*
|
|
10
|
+
* Wire types are re-exported so consumers never import the generated contract
|
|
11
|
+
* directly — that path is an implementation detail and would make the
|
|
12
|
+
* generated artifacts part of the public API.
|
|
13
|
+
*/
|
|
14
|
+
export { FRAGMENT_KINDS, FRAGMENT_WARMTHS } from "./wire.js";
|
|
15
|
+
export { PROTOCOL_CURRENT, PROTOCOL_DEFAULT, PROTOCOL_HEADER, PROTOCOL_PARAM, PROTOCOL_VERSIONS, SDK_PARAM, SDK_PARAM_VALUE, VARIANT_PARAM, buildContentUrl, buildStreamUrl, negotiatedProtocol, } from "./protocol.js";
|
|
16
|
+
export { SseParser, readSseStream } from "./sse.js";
|
|
17
|
+
export { isFirstPartyApiBase, isSecureApiBase, safeActionUrl } from "./safeUrl.js";
|
|
18
|
+
export { CONTEXT_DAYPARTS, CONTEXT_DAY_TYPES, CONTEXT_DEVICES, CONTEXT_INTENTS, CONTEXT_SURFACE_TYPES, CONTEXT_TEMP_BANDS, CONTEXT_WEATHER, FRAGMENT_KIND_FLOORS, MAX_CUSTOM_DIMS, MAX_NOTES_LENGTH, canonicalContextString, computeContextHash, computeFieldBudget, computeSchemaHash, declaredToDims, hashDeclaredContext, normalizeDims, normalizeNotes, } from "./context.js";
|
|
19
|
+
export { COPY_FIELDS, COPY_HOLD_MS, CopyChoreographer, } from "./state.js";
|
|
20
|
+
export { fetchContent, openStream, } from "./client.js";
|
|
21
|
+
export { TOKEN_PREFIX, applyTheme, themeStyle, tokenName, tokenSource, } from "./theme.js";
|
|
22
|
+
export { STANDARD_MODE_KEY, decideChrome, setViewerPrefersStandard, viewerPrefersStandard, } from "./trust.js";
|
|
23
|
+
export { ImpressionRecorder, observeVisibility, sessionId, track, } from "./analytics.js";
|
|
24
|
+
export { RESERVATION_TIMEOUT_MS, VariantRegistry, pageVariants, } from "./variant.js";
|
|
25
|
+
export { activePlayer, destroyActiveAudio, mountAudio, } from "./audio.js";
|
|
26
|
+
export { plainText } from "./text.js";
|
|
27
|
+
export { readableTextColor } from "./theme.js";
|
|
28
|
+
export { decodePublishableKey, encodePublishableKey, } from "./publishableKey.js";
|
|
29
|
+
export { createClient, fetchFragments, normalizeFieldSpecs, strongestFragmentSource, } from "./fragments.js";
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAeH,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7D,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,SAAS,EACT,eAAe,EACf,aAAa,EACb,eAAe,EACf,cAAc,EACd,kBAAkB,GAEnB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,SAAS,EAAE,aAAa,EAAmB,MAAM,UAAU,CAAC;AAErE,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAEnF,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,sBAAsB,EACtB,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,cAAc,GAIf,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,iBAAiB,GAMlB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,YAAY,EACZ,UAAU,GAIX,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,YAAY,EACZ,UAAU,EACV,UAAU,EACV,SAAS,EACT,WAAW,GAEZ,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,wBAAwB,EACxB,qBAAqB,GAEtB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,SAAS,EACT,KAAK,GAKN,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,YAAY,GAIb,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,UAAU,GAOX,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE/C,OAAO,EACL,oBAAoB,EACpB,oBAAoB,GAGrB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,uBAAuB,GAQxB,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protocol surface: what the SDK asks for, and what it does when the server
|
|
3
|
+
* cannot give it.
|
|
4
|
+
*
|
|
5
|
+
* Everything here is generated from `shared/sparkletree/wire/contract.ts`, so
|
|
6
|
+
* the constants below cannot drift from the Go edge or the Next twin. What
|
|
7
|
+
* this file adds is the CLIENT half of the handshake, which the contract
|
|
8
|
+
* describes but does not implement.
|
|
9
|
+
*/
|
|
10
|
+
import { PROTOCOL_CURRENT, PROTOCOL_DEFAULT, PROTOCOL_HEADER, PROTOCOL_PARAM, PROTOCOL_RESPONSE_HEADER, PROTOCOL_VERSIONS, SDK_PARAM, SDK_PARAM_VALUE, VARIANT_PARAM } from "./wire.js";
|
|
11
|
+
export { PROTOCOL_CURRENT, PROTOCOL_DEFAULT, PROTOCOL_HEADER, PROTOCOL_PARAM, PROTOCOL_RESPONSE_HEADER, PROTOCOL_VERSIONS, SDK_PARAM, SDK_PARAM_VALUE, VARIANT_PARAM, };
|
|
12
|
+
/** Everything needed to address one island stream. */
|
|
13
|
+
export interface StreamTarget {
|
|
14
|
+
/** Base URL of the embed API, e.g. `https://api.sparkletree.io`. */
|
|
15
|
+
apiBase: string;
|
|
16
|
+
organizationId: string;
|
|
17
|
+
campaignId?: string;
|
|
18
|
+
surfaceId?: string;
|
|
19
|
+
island: "hero" | "cta";
|
|
20
|
+
/**
|
|
21
|
+
* Variant pinning (plan v1.1 §3.3). The provider resolves the variant ONCE
|
|
22
|
+
* per page view and pins it here for every later call. Without it, ε-greedy
|
|
23
|
+
* runs per request and a single page shows a mixed-variant creative — hero
|
|
24
|
+
* from one arm, CTA from another — which is not an experiment, it is noise.
|
|
25
|
+
*/
|
|
26
|
+
variantId?: string;
|
|
27
|
+
/** Context snapshot (time, geo, weather, device). Serialised as JSON. */
|
|
28
|
+
context?: Record<string, unknown>;
|
|
29
|
+
/** Conversation session id for copy continuity. Individual surfaces only. */
|
|
30
|
+
sessionId?: string;
|
|
31
|
+
/** Extra params passed through verbatim (preview tokens, etc.). */
|
|
32
|
+
extra?: Record<string, string | undefined>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Build the stream URL.
|
|
36
|
+
*
|
|
37
|
+
* `sdk=1` is not optional and not a feature flag: it tells the server to
|
|
38
|
+
* suppress its own server-side impression for this request. The SDK renders
|
|
39
|
+
* inside the customer's page, so it is the only party that knows whether the
|
|
40
|
+
* creative was ever actually on screen — and a server-side impression on top
|
|
41
|
+
* of the client's own would double-count every view, and would keep counting
|
|
42
|
+
* from a CDN-cached SSR response that no human ever saw.
|
|
43
|
+
*/
|
|
44
|
+
export declare function buildStreamUrl(target: StreamTarget, protocol?: number): string;
|
|
45
|
+
/**
|
|
46
|
+
* Build the `/content` URL used for first paint. Same pinning rules as the
|
|
47
|
+
* stream — but deliberately NOT the same `sdk=1` rule.
|
|
48
|
+
*
|
|
49
|
+
* `sdk=1` is a promise: "the SDK will record this impression itself". The
|
|
50
|
+
* stream path keeps that promise — `useIsland` owns a visibility-gated
|
|
51
|
+
* recorder. The content path CANNOT: its consumers (CopyCard, ProductCard,
|
|
52
|
+
* the cookbook's content-only recipes) have no observer and no recorder, so
|
|
53
|
+
* sending `sdk=1` here told the server to stop counting while nobody else
|
|
54
|
+
* started — zero impressions for every content-only integration. Omitting it
|
|
55
|
+
* leaves the server owning the `/content` impression, exactly as it does for
|
|
56
|
+
* every non-SDK consumer of the endpoint.
|
|
57
|
+
*/
|
|
58
|
+
export declare function buildContentUrl(target: StreamTarget, protocol?: number): string;
|
|
59
|
+
/**
|
|
60
|
+
* Read the negotiated version off a response.
|
|
61
|
+
*
|
|
62
|
+
* An absent header means an older deployment that predates negotiation, which
|
|
63
|
+
* by contract is protocol 1 — not an error, and specifically not a reason to
|
|
64
|
+
* refuse the stream. Treating silence as failure here would break every
|
|
65
|
+
* customer whose traffic happens to land on an edge revision mid-rollout.
|
|
66
|
+
*/
|
|
67
|
+
export declare function negotiatedProtocol(response: {
|
|
68
|
+
headers: Headers;
|
|
69
|
+
}): number;
|
|
70
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,wBAAwB,EACxB,iBAAiB,EACjB,SAAS,EACT,eAAe,EACf,aAAa,EACd,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,wBAAwB,EACxB,iBAAiB,EACjB,SAAS,EACT,eAAe,EACf,aAAa,GACd,CAAC;AAEF,sDAAsD;AACtD,MAAM,WAAW,YAAY;IAC3B,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;IACvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC5C;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,GAAE,MAAyB,GAAG,MAAM,CAoBhG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,GAAE,MAAyB,GAAG,MAAM,CAkBjG;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAKzE"}
|
package/dist/protocol.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protocol surface: what the SDK asks for, and what it does when the server
|
|
3
|
+
* cannot give it.
|
|
4
|
+
*
|
|
5
|
+
* Everything here is generated from `shared/sparkletree/wire/contract.ts`, so
|
|
6
|
+
* the constants below cannot drift from the Go edge or the Next twin. What
|
|
7
|
+
* this file adds is the CLIENT half of the handshake, which the contract
|
|
8
|
+
* describes but does not implement.
|
|
9
|
+
*/
|
|
10
|
+
import { PROTOCOL_CURRENT, PROTOCOL_DEFAULT, PROTOCOL_HEADER, PROTOCOL_PARAM, PROTOCOL_RESPONSE_HEADER, PROTOCOL_VERSIONS, SDK_PARAM, SDK_PARAM_VALUE, VARIANT_PARAM, } from "./wire.js";
|
|
11
|
+
export { PROTOCOL_CURRENT, PROTOCOL_DEFAULT, PROTOCOL_HEADER, PROTOCOL_PARAM, PROTOCOL_RESPONSE_HEADER, PROTOCOL_VERSIONS, SDK_PARAM, SDK_PARAM_VALUE, VARIANT_PARAM, };
|
|
12
|
+
/**
|
|
13
|
+
* Build the stream URL.
|
|
14
|
+
*
|
|
15
|
+
* `sdk=1` is not optional and not a feature flag: it tells the server to
|
|
16
|
+
* suppress its own server-side impression for this request. The SDK renders
|
|
17
|
+
* inside the customer's page, so it is the only party that knows whether the
|
|
18
|
+
* creative was ever actually on screen — and a server-side impression on top
|
|
19
|
+
* of the client's own would double-count every view, and would keep counting
|
|
20
|
+
* from a CDN-cached SSR response that no human ever saw.
|
|
21
|
+
*/
|
|
22
|
+
export function buildStreamUrl(target, protocol = PROTOCOL_CURRENT) {
|
|
23
|
+
const url = new URL("/api/embed/v1/islands/stream", target.apiBase);
|
|
24
|
+
const params = url.searchParams;
|
|
25
|
+
params.set("organization", target.organizationId);
|
|
26
|
+
if (target.surfaceId)
|
|
27
|
+
params.set("surface", target.surfaceId);
|
|
28
|
+
else if (target.campaignId)
|
|
29
|
+
params.set("campaign", target.campaignId);
|
|
30
|
+
params.set("island", target.island);
|
|
31
|
+
if (target.context)
|
|
32
|
+
params.set("context", JSON.stringify(target.context));
|
|
33
|
+
if (target.variantId)
|
|
34
|
+
params.set(VARIANT_PARAM, target.variantId);
|
|
35
|
+
if (target.sessionId)
|
|
36
|
+
params.set("session", target.sessionId);
|
|
37
|
+
params.set(SDK_PARAM, SDK_PARAM_VALUE);
|
|
38
|
+
// Sent as a param as well as a header: EventSource cannot set headers, and
|
|
39
|
+
// a consumer that swaps our fetch transport for one must still negotiate.
|
|
40
|
+
params.set(PROTOCOL_PARAM, String(protocol));
|
|
41
|
+
for (const [key, value] of Object.entries(target.extra ?? {})) {
|
|
42
|
+
if (value !== undefined)
|
|
43
|
+
params.set(key, value);
|
|
44
|
+
}
|
|
45
|
+
return url.toString();
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Build the `/content` URL used for first paint. Same pinning rules as the
|
|
49
|
+
* stream — but deliberately NOT the same `sdk=1` rule.
|
|
50
|
+
*
|
|
51
|
+
* `sdk=1` is a promise: "the SDK will record this impression itself". The
|
|
52
|
+
* stream path keeps that promise — `useIsland` owns a visibility-gated
|
|
53
|
+
* recorder. The content path CANNOT: its consumers (CopyCard, ProductCard,
|
|
54
|
+
* the cookbook's content-only recipes) have no observer and no recorder, so
|
|
55
|
+
* sending `sdk=1` here told the server to stop counting while nobody else
|
|
56
|
+
* started — zero impressions for every content-only integration. Omitting it
|
|
57
|
+
* leaves the server owning the `/content` impression, exactly as it does for
|
|
58
|
+
* every non-SDK consumer of the endpoint.
|
|
59
|
+
*/
|
|
60
|
+
export function buildContentUrl(target, protocol = PROTOCOL_CURRENT) {
|
|
61
|
+
const url = new URL("/api/embed/v1/content", target.apiBase);
|
|
62
|
+
const params = url.searchParams;
|
|
63
|
+
// Same param names the stream uses — the Go handler (contentRequest in
|
|
64
|
+
// mosaic-edge-serve/internal/handler/content.go) reads organization/
|
|
65
|
+
// campaign/surface; the *Id spellings were never read by any server.
|
|
66
|
+
params.set("organization", target.organizationId);
|
|
67
|
+
if (target.surfaceId)
|
|
68
|
+
params.set("surface", target.surfaceId);
|
|
69
|
+
if (target.campaignId)
|
|
70
|
+
params.set("campaign", target.campaignId);
|
|
71
|
+
if (target.context)
|
|
72
|
+
params.set("context", JSON.stringify(target.context));
|
|
73
|
+
if (target.variantId)
|
|
74
|
+
params.set(VARIANT_PARAM, target.variantId);
|
|
75
|
+
params.set(PROTOCOL_PARAM, String(protocol));
|
|
76
|
+
for (const [key, value] of Object.entries(target.extra ?? {})) {
|
|
77
|
+
if (value !== undefined)
|
|
78
|
+
params.set(key, value);
|
|
79
|
+
}
|
|
80
|
+
return url.toString();
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Read the negotiated version off a response.
|
|
84
|
+
*
|
|
85
|
+
* An absent header means an older deployment that predates negotiation, which
|
|
86
|
+
* by contract is protocol 1 — not an error, and specifically not a reason to
|
|
87
|
+
* refuse the stream. Treating silence as failure here would break every
|
|
88
|
+
* customer whose traffic happens to land on an edge revision mid-rollout.
|
|
89
|
+
*/
|
|
90
|
+
export function negotiatedProtocol(response) {
|
|
91
|
+
const raw = response.headers.get(PROTOCOL_RESPONSE_HEADER);
|
|
92
|
+
if (!raw)
|
|
93
|
+
return PROTOCOL_DEFAULT;
|
|
94
|
+
const parsed = Number.parseInt(raw, 10);
|
|
95
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : PROTOCOL_DEFAULT;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
|