@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
package/dist/state.js
ADDED
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copy choreography — the rewrite/settle state machine.
|
|
3
|
+
*
|
|
4
|
+
* This is the part of the SDK that is not a fetch wrapper. The doctrine it
|
|
5
|
+
* implements (Squid V2, shipped 2026-07-23) is three rules:
|
|
6
|
+
*
|
|
7
|
+
* 1. FIRST PAINT IS REAL COPY. Never a skeleton, never an empty frame. The
|
|
8
|
+
* client starts from SSR `/content` or a required `fallback`.
|
|
9
|
+
* 2. COPY CHANGES AT MOST ONCE. Text a reader may already be mid-sentence on
|
|
10
|
+
* is never wiped and retyped. The stream lands as ONE deliberate rewrite.
|
|
11
|
+
* 3. TEXT ON THE WIRE IS TEXT WE CAN SHOW. The first CHUNK takes the canvas,
|
|
12
|
+
* whatever the `*-start` called itself. The delivery `mode` is no longer
|
|
13
|
+
* consulted for hold release — an earlier revision released on
|
|
14
|
+
* `mode: "generate"` and kept holding on `replay`, which re-derived from
|
|
15
|
+
* a label what the arrival of actual text proves directly. A replay's
|
|
16
|
+
* single flush still paints once, because one flush is genuinely what
|
|
17
|
+
* arrived — not because it was held back.
|
|
18
|
+
*
|
|
19
|
+
* The hold is the mechanism that reconciles rules 1 and 2. When the canvas
|
|
20
|
+
* already shows copy the stream is about to replace, that copy is a FALLBACK,
|
|
21
|
+
* not a first draft: showing it and then yanking it is worse than briefly
|
|
22
|
+
* showing the art alone. So the canvas holds blank on the art for a budget —
|
|
23
|
+
* a budget for the STORED FALLBACK, not for the stream: the fallback is
|
|
24
|
+
* allowed on screen only once the stream has failed or run out the budget
|
|
25
|
+
* without delivering. A fast stream types the real copy straight in; a slow
|
|
26
|
+
* one gets the fallback back, and any later copy arrives as the single
|
|
27
|
+
* permitted rewrite.
|
|
28
|
+
*
|
|
29
|
+
* Reference implementation (the iframe shell, which must stay behaviourally
|
|
30
|
+
* identical): `mosaic/services/rendererService.ts`, streamInitialCopyCreative.
|
|
31
|
+
*/
|
|
32
|
+
import { CONTENT_SOURCES } from "./wire.js";
|
|
33
|
+
import { safeActionUrl } from "./safeUrl.js";
|
|
34
|
+
export const COPY_FIELDS = ["greeting", "headline", "body", "cta"];
|
|
35
|
+
/**
|
|
36
|
+
* How long the canvas will hold blank waiting for the stream, once the stream
|
|
37
|
+
* has proven it is alive.
|
|
38
|
+
*
|
|
39
|
+
* Armed against the STREAM, not the mount: the budget is for the connection
|
|
40
|
+
* that was just opened. The shell's earlier clock started before the transport
|
|
41
|
+
* had even connected, so on a slow link it expired mid-connect and painted
|
|
42
|
+
* stored copy that the live copy then overwrote — fallback churn nobody asked
|
|
43
|
+
* for, and a rule-2 violation caused by the timer meant to protect rule 2.
|
|
44
|
+
*/
|
|
45
|
+
export const COPY_HOLD_MS = 2500;
|
|
46
|
+
function emptyField(text = "", source = "static") {
|
|
47
|
+
return { text, source, mode: null, typing: false, settled: false };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Owns one island's copy for one page view. Fed wire events; emits states.
|
|
51
|
+
*
|
|
52
|
+
* Pure with respect to the DOM: it decides WHAT should be on screen and when,
|
|
53
|
+
* and never touches how. That is what lets the same choreography drive React,
|
|
54
|
+
* the iframe shell, and anything else without three copies of these rules.
|
|
55
|
+
*/
|
|
56
|
+
export class CopyChoreographer {
|
|
57
|
+
options;
|
|
58
|
+
state;
|
|
59
|
+
listeners = new Set();
|
|
60
|
+
holdMs;
|
|
61
|
+
setTimer;
|
|
62
|
+
clearTimer;
|
|
63
|
+
/**
|
|
64
|
+
* Copy pulled off the canvas when the hold armed. Alive until the stream
|
|
65
|
+
* TERMINATES (`done`/`failed`), not merely until the hold releases: any
|
|
66
|
+
* field the stream never delivers is restored from here, because a field
|
|
67
|
+
* the stream never took must end on its fallback — never on the blank the
|
|
68
|
+
* hold itself painted.
|
|
69
|
+
*/
|
|
70
|
+
heldCopy = null;
|
|
71
|
+
holdTimer = null;
|
|
72
|
+
/**
|
|
73
|
+
* The budget may restart ONCE per hold episode. Without a cap, a server
|
|
74
|
+
* emitting a `*-start` per field would extend the blank window by a full
|
|
75
|
+
* budget per field — proof of life is proof once, and a blank canvas is a
|
|
76
|
+
* cost the visitor pays, so it is not for sale twice.
|
|
77
|
+
*/
|
|
78
|
+
holdRestarted = false;
|
|
79
|
+
/** Text accumulated from the wire while the hold is still on. */
|
|
80
|
+
pending = {};
|
|
81
|
+
/** True once the machine has committed a change to the first paint. */
|
|
82
|
+
rewriteCommitted = false;
|
|
83
|
+
/**
|
|
84
|
+
* Fields whose on-canvas text is restored fallback a reader can see. Live
|
|
85
|
+
* deltas must not type over these — they buffer off-canvas and the field
|
|
86
|
+
* lands ONCE, composed, at its `*-done`. Typing is an entrance for a blank
|
|
87
|
+
* field, never a treatment for text someone is reading.
|
|
88
|
+
*/
|
|
89
|
+
fallbackOnCanvas = new Set();
|
|
90
|
+
/**
|
|
91
|
+
* Fields the stream has closed with a `*-done`, including closes that
|
|
92
|
+
* arrived while the hold was still on and so never reached the canvas at the
|
|
93
|
+
* time. Consulted when the copy finally paints, so `settled` describes the
|
|
94
|
+
* field rather than the route it took.
|
|
95
|
+
*/
|
|
96
|
+
closed = new Set();
|
|
97
|
+
/** Set by dispose(): async completions (image decode) must not patch after it. */
|
|
98
|
+
disposed = false;
|
|
99
|
+
constructor(options = {}) {
|
|
100
|
+
this.options = options;
|
|
101
|
+
this.holdMs = options.holdMs ?? COPY_HOLD_MS;
|
|
102
|
+
this.setTimer = options.setTimeout ?? ((fn, ms) => setTimeout(fn, ms));
|
|
103
|
+
this.clearTimer =
|
|
104
|
+
options.clearTimeout ?? ((handle) => clearTimeout(handle));
|
|
105
|
+
const fields = {};
|
|
106
|
+
for (const field of COPY_FIELDS) {
|
|
107
|
+
fields[field] = emptyField(options.initial?.[field] ?? "");
|
|
108
|
+
}
|
|
109
|
+
this.state = {
|
|
110
|
+
phase: "idle",
|
|
111
|
+
fields,
|
|
112
|
+
theme: null,
|
|
113
|
+
layout: null,
|
|
114
|
+
experienceManifest: null,
|
|
115
|
+
backgroundImage: null,
|
|
116
|
+
sequence: null,
|
|
117
|
+
videoUrl: null,
|
|
118
|
+
ctaStyle: null,
|
|
119
|
+
ctaAction: null,
|
|
120
|
+
meta: null,
|
|
121
|
+
contentSource: "static",
|
|
122
|
+
degraded: null,
|
|
123
|
+
variantId: null,
|
|
124
|
+
error: null,
|
|
125
|
+
rewrites: 0,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
getState() {
|
|
129
|
+
return this.state;
|
|
130
|
+
}
|
|
131
|
+
subscribe(listener) {
|
|
132
|
+
this.listeners.add(listener);
|
|
133
|
+
return () => this.listeners.delete(listener);
|
|
134
|
+
}
|
|
135
|
+
emit() {
|
|
136
|
+
for (const listener of this.listeners) {
|
|
137
|
+
try {
|
|
138
|
+
listener(this.state);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// Swallowed on purpose. A subscriber throwing is the SUBSCRIBER's bug;
|
|
142
|
+
// letting it propagate would tear down the stream for every other
|
|
143
|
+
// listener and break the customer's page from inside a render effect.
|
|
144
|
+
// Same isolation the audio emitter applies (audio.ts createEmitter).
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
patch(next) {
|
|
149
|
+
this.state = { ...this.state, ...next };
|
|
150
|
+
this.emit();
|
|
151
|
+
}
|
|
152
|
+
patchField(field, next) {
|
|
153
|
+
this.state = {
|
|
154
|
+
...this.state,
|
|
155
|
+
fields: { ...this.state.fields, [field]: { ...this.state.fields[field], ...next } },
|
|
156
|
+
};
|
|
157
|
+
this.emit();
|
|
158
|
+
}
|
|
159
|
+
hasInitialCopy() {
|
|
160
|
+
return COPY_FIELDS.some((field) => this.state.fields[field].text.trim() !== "");
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Call when the stream request has been opened.
|
|
164
|
+
*
|
|
165
|
+
* Arms the hold if — and only if — there is fallback copy on the canvas that
|
|
166
|
+
* has NOT already been read. Copy that has been on screen since first paint
|
|
167
|
+
* stays put: it is not a placeholder, and hiding it would be the mid-read
|
|
168
|
+
* wipe this whole machine exists to prevent.
|
|
169
|
+
*/
|
|
170
|
+
streamOpened() {
|
|
171
|
+
if (this.state.phase !== "idle")
|
|
172
|
+
return;
|
|
173
|
+
if (this.hasInitialCopy() && !this.options.initialIsOnScreen) {
|
|
174
|
+
this.heldCopy = {};
|
|
175
|
+
this.holdRestarted = false;
|
|
176
|
+
const blanked = {};
|
|
177
|
+
for (const field of COPY_FIELDS) {
|
|
178
|
+
this.heldCopy[field] = this.state.fields[field].text;
|
|
179
|
+
blanked[field] = { ...this.state.fields[field], text: "" };
|
|
180
|
+
}
|
|
181
|
+
this.state = { ...this.state, phase: "holding", fields: blanked };
|
|
182
|
+
this.holdTimer = this.setTimer(() => this.onHoldExpired(), this.holdMs);
|
|
183
|
+
this.emit();
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
// Nothing to HOLD: either the canvas is genuinely blank (so live typing IS
|
|
187
|
+
// the entrance) or the copy is already being read (so it stays).
|
|
188
|
+
//
|
|
189
|
+
// "Stays" has to mean stays INTACT. Copy on screen since first paint is
|
|
190
|
+
// exactly the text rule 2 protects, so every non-empty field is marked as
|
|
191
|
+
// fallback-on-canvas: live deltas then buffer off-screen and the field
|
|
192
|
+
// lands ONCE, composed, at its `*-done` — or never, if the stream dies
|
|
193
|
+
// first. Without this the SDK types a half sentence over a paragraph
|
|
194
|
+
// someone is reading and strands them there when the stream fails, which
|
|
195
|
+
// is precisely what the iframe shell refuses to do (`!canvasHadCopy`).
|
|
196
|
+
for (const field of COPY_FIELDS) {
|
|
197
|
+
if (this.state.fields[field].text.trim() !== "")
|
|
198
|
+
this.fallbackOnCanvas.add(field);
|
|
199
|
+
}
|
|
200
|
+
this.patch({ phase: "live" });
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Hold expired without the stream taking the canvas.
|
|
204
|
+
*
|
|
205
|
+
* Prefer whatever the stream actually delivered; the stored copy is the last
|
|
206
|
+
* resort, never a first draft.
|
|
207
|
+
*/
|
|
208
|
+
onHoldExpired() {
|
|
209
|
+
this.holdTimer = null;
|
|
210
|
+
if (this.state.phase !== "holding")
|
|
211
|
+
return;
|
|
212
|
+
if (COPY_FIELDS.some((field) => (this.pending[field] ?? "") !== "")) {
|
|
213
|
+
this.releaseHoldToLive(true);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
this.restoreHeld();
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* The stream is over and the canvas is final.
|
|
220
|
+
*
|
|
221
|
+
* `readSseStream` keeps draining after a terminal event, and the parser's
|
|
222
|
+
* flush can emit a trailing frame, so late copy events are reachable rather
|
|
223
|
+
* than theoretical. Without this guard a post-`failed` chunk left
|
|
224
|
+
* `typing: true` forever — a caret animating on a dead stream — and a
|
|
225
|
+
* post-`done` chunk+done replaced settled text WITHOUT incrementing
|
|
226
|
+
* `rewrites`, so the doctrine counter read 1 while the reader saw two
|
|
227
|
+
* changes. Everything after the terminal event is dropped.
|
|
228
|
+
*/
|
|
229
|
+
isTerminal() {
|
|
230
|
+
return this.state.phase === "settled" || this.state.phase === "failed";
|
|
231
|
+
}
|
|
232
|
+
clearHoldTimer() {
|
|
233
|
+
if (this.holdTimer !== null) {
|
|
234
|
+
this.clearTimer(this.holdTimer);
|
|
235
|
+
this.holdTimer = null;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Hand the canvas to the live stream: what has accumulated paints at once,
|
|
240
|
+
* so the rest can stream in behind it.
|
|
241
|
+
*
|
|
242
|
+
* `restoreUndelivered` distinguishes the two callers. On a chunk-driven
|
|
243
|
+
* release the stream is alive and delivering, so undelivered fields stay
|
|
244
|
+
* blank — typing into a blank canvas is their entrance, and `heldCopy`
|
|
245
|
+
* outlives the hold so `done`/`failed` restore any field the stream never
|
|
246
|
+
* takes. On budget EXPIRY the stream has sputtered: whatever it delivered
|
|
247
|
+
* paints, and every undelivered field gets its fallback back now, because
|
|
248
|
+
* blank is never the resting state the budget is allowed to buy.
|
|
249
|
+
*/
|
|
250
|
+
releaseHoldToLive(restoreUndelivered) {
|
|
251
|
+
this.clearHoldTimer();
|
|
252
|
+
const fields = { ...this.state.fields };
|
|
253
|
+
let paintedStreamText = false;
|
|
254
|
+
for (const field of COPY_FIELDS) {
|
|
255
|
+
const text = this.pending[field];
|
|
256
|
+
if (text !== undefined && text !== "") {
|
|
257
|
+
fields[field] = { ...fields[field], text, settled: this.closed.has(field) };
|
|
258
|
+
this.fallbackOnCanvas.delete(field);
|
|
259
|
+
if (text !== (this.heldCopy?.[field] ?? ""))
|
|
260
|
+
paintedStreamText = true;
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (!restoreUndelivered)
|
|
264
|
+
continue;
|
|
265
|
+
const held = this.heldCopy?.[field];
|
|
266
|
+
if (held !== undefined && held !== "") {
|
|
267
|
+
fields[field] = { ...fields[field], text: held };
|
|
268
|
+
this.fallbackOnCanvas.add(field);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// Restoring fallback is not a rewrite — nothing the reader saw changed
|
|
272
|
+
// versus first paint. Stream text landing is the one rewrite.
|
|
273
|
+
if (paintedStreamText)
|
|
274
|
+
this.commitRewrite();
|
|
275
|
+
this.state = { ...this.state, phase: "live", fields };
|
|
276
|
+
this.emit();
|
|
277
|
+
}
|
|
278
|
+
/** Budget spent with nothing delivered: the fallback comes back, composed. */
|
|
279
|
+
restoreHeld() {
|
|
280
|
+
this.clearHoldTimer();
|
|
281
|
+
if (!this.heldCopy) {
|
|
282
|
+
this.patch({ phase: "live" });
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
const fields = { ...this.state.fields };
|
|
286
|
+
for (const field of COPY_FIELDS) {
|
|
287
|
+
const text = this.heldCopy[field];
|
|
288
|
+
if (text !== undefined)
|
|
289
|
+
fields[field] = { ...fields[field], text };
|
|
290
|
+
if (text !== undefined && text !== "")
|
|
291
|
+
this.fallbackOnCanvas.add(field);
|
|
292
|
+
}
|
|
293
|
+
this.heldCopy = null;
|
|
294
|
+
this.state = { ...this.state, phase: "live", fields };
|
|
295
|
+
this.emit();
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Record that the first paint has been replaced. Restoring the SAME fallback
|
|
299
|
+
* text is not a rewrite — nothing on screen changed — which is why every
|
|
300
|
+
* caller commits only when STREAM text actually painted, and restoreHeld
|
|
301
|
+
* never commits at all.
|
|
302
|
+
*/
|
|
303
|
+
commitRewrite() {
|
|
304
|
+
if (this.rewriteCommitted)
|
|
305
|
+
return;
|
|
306
|
+
this.rewriteCommitted = true;
|
|
307
|
+
this.state = { ...this.state, rewrites: this.state.rewrites + 1 };
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Terminal honesty pass over provenance.
|
|
311
|
+
*
|
|
312
|
+
* A field still in `fallbackOnCanvas` when the stream ends is showing the
|
|
313
|
+
* customer's AUTHORED text: its buffered deltas never landed. Its `source`
|
|
314
|
+
* still says `generated` because `*-start` declared an intent the stream
|
|
315
|
+
* then failed to deliver — and `StreamText` marks on `field.source`, so
|
|
316
|
+
* leaving it would stamp "adapted for you" on copy nobody adapted.
|
|
317
|
+
*
|
|
318
|
+
* The mark must describe what is on screen, not what the server meant to
|
|
319
|
+
* send. A missing mark on adapted copy under-claims; a mark on authored copy
|
|
320
|
+
* is a false claim, and that is the worse of the two.
|
|
321
|
+
*
|
|
322
|
+
* `fallbackOnCanvas` is NOT the whole set. A chunk-driven release leaves
|
|
323
|
+
* undelivered fields blank — restoring them mid-stream would stage the
|
|
324
|
+
* wipe-and-retype this machine exists to prevent — so they never enter that
|
|
325
|
+
* set, and are instead restored from `heldCopy` by the terminal handlers.
|
|
326
|
+
* That is the likeliest degradation there is: the server opens a field,
|
|
327
|
+
* then times out. Demoting only `fallbackOnCanvas` left exactly that path
|
|
328
|
+
* rendering "adapted for you" over the customer's own sentence.
|
|
329
|
+
*
|
|
330
|
+
* So callers pass the fields they restored, and both sets are demoted.
|
|
331
|
+
*/
|
|
332
|
+
demoteUndeliveredProvenance(fields, restoredFromHeld = []) {
|
|
333
|
+
for (const field of new Set([...this.fallbackOnCanvas, ...restoredFromHeld])) {
|
|
334
|
+
fields[field] = { ...fields[field], source: "static", mode: null };
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/** True once any field on canvas actually carries adapted copy. */
|
|
338
|
+
hasAdaptedCopyOnCanvas(fields) {
|
|
339
|
+
return COPY_FIELDS.some((field) => fields[field].text.trim() !== "" && fields[field].source !== "static");
|
|
340
|
+
}
|
|
341
|
+
// --- wire events -----------------------------------------------------------
|
|
342
|
+
/**
|
|
343
|
+
* A field opened. Proof of life, and provenance — nothing more.
|
|
344
|
+
*
|
|
345
|
+
* The canvas changes hands on the first CHUNK, not here: the delivery mode
|
|
346
|
+
* is recorded on the field but no longer consulted for hold release. All a
|
|
347
|
+
* start does while holding is RESTART the fallback budget, because the
|
|
348
|
+
* stream has just proven it is alive and the old budget was measuring a
|
|
349
|
+
* connection that had not connected yet.
|
|
350
|
+
*/
|
|
351
|
+
copyStart(field, mode, source) {
|
|
352
|
+
// A caret is only honest when deltas will actually land on canvas; deltas
|
|
353
|
+
// aimed at visible fallback copy buffer off-canvas instead.
|
|
354
|
+
const willType = mode === "generate" && !this.fallbackOnCanvas.has(field);
|
|
355
|
+
this.patchField(field, { mode, source, typing: willType, settled: false });
|
|
356
|
+
this.promoteContentSource(source);
|
|
357
|
+
if (this.state.phase !== "holding")
|
|
358
|
+
return;
|
|
359
|
+
// One restart buys the freshly-proven stream a full budget; further
|
|
360
|
+
// starts do not extend the blank window past that cap.
|
|
361
|
+
if (this.holdRestarted)
|
|
362
|
+
return;
|
|
363
|
+
this.holdRestarted = true;
|
|
364
|
+
this.clearHoldTimer();
|
|
365
|
+
this.holdTimer = this.setTimer(() => this.onHoldExpired(), this.holdMs);
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* A delta landed. Text on the wire is text we can show: the first chunk
|
|
369
|
+
* takes the canvas away from the pending fallback, whatever the start
|
|
370
|
+
* called itself. While the field shows restored fallback copy a reader can
|
|
371
|
+
* see, deltas still buffer off-canvas — that text changes ONCE, composed,
|
|
372
|
+
* at `*-done`, never delta by delta.
|
|
373
|
+
*/
|
|
374
|
+
copyChunk(field, text) {
|
|
375
|
+
if (this.isTerminal())
|
|
376
|
+
return;
|
|
377
|
+
this.pending[field] = (this.pending[field] ?? "") + text;
|
|
378
|
+
if (this.state.phase === "holding") {
|
|
379
|
+
// The release paints everything pending, this chunk included.
|
|
380
|
+
this.releaseHoldToLive(false);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
if (this.fallbackOnCanvas.has(field))
|
|
384
|
+
return;
|
|
385
|
+
// Commit BEFORE the emitting patch: subscribers must see the counter and
|
|
386
|
+
// the text move together, not the counter one event late.
|
|
387
|
+
this.commitRewrite();
|
|
388
|
+
this.patchField(field, { text: this.pending[field], typing: true });
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* A field closed. `text` is authoritative — a client that missed chunks can
|
|
392
|
+
* paint this alone, which is what makes chunk loss a cosmetic problem
|
|
393
|
+
* instead of a correctness one.
|
|
394
|
+
*/
|
|
395
|
+
copyDone(field, text) {
|
|
396
|
+
if (this.isTerminal())
|
|
397
|
+
return;
|
|
398
|
+
this.pending[field] = text;
|
|
399
|
+
// Record the close BEFORE the hold's early return. A replay that lands
|
|
400
|
+
// entirely inside the hold used to leave `settled: false` forever: this
|
|
401
|
+
// returned before patching, and the terminal handlers only cleared
|
|
402
|
+
// `typing`. `settled` is public API and a documented styling hook, so a
|
|
403
|
+
// field whose text is final must say so however it arrived.
|
|
404
|
+
this.closed.add(field);
|
|
405
|
+
if (this.state.phase === "holding")
|
|
406
|
+
return;
|
|
407
|
+
this.fallbackOnCanvas.delete(field);
|
|
408
|
+
// Landing text identical to what is on canvas changes nothing a reader
|
|
409
|
+
// can see, so it spends no rewrite.
|
|
410
|
+
if (text !== this.state.fields[field].text)
|
|
411
|
+
this.commitRewrite();
|
|
412
|
+
this.patchField(field, { text, typing: false, settled: true });
|
|
413
|
+
}
|
|
414
|
+
themeReady(colors, sources) {
|
|
415
|
+
this.patch({ theme: { colors, sources } });
|
|
416
|
+
}
|
|
417
|
+
layoutReady(layout, manifest) {
|
|
418
|
+
this.patch({
|
|
419
|
+
layout,
|
|
420
|
+
...(manifest !== undefined ? { experienceManifest: manifest } : {}),
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Absence of this event means KEEP THE BAKED THEME WASH. It does not mean
|
|
425
|
+
* clear the art layer — the server omits it when there is no creative image,
|
|
426
|
+
* and a client that cleared on absence would blank the hero on exactly the
|
|
427
|
+
* campaigns that never had a picture.
|
|
428
|
+
*
|
|
429
|
+
* The patch waits for the browser to DECODE the image first: consumers
|
|
430
|
+
* render this state directly, and a backgroundImage that lands before
|
|
431
|
+
* pixels exist paints the already-flipped theme against the old (or blank)
|
|
432
|
+
* art — the contrast tear. On failure the patch still lands (the consumer's
|
|
433
|
+
* own loading treatment owns that case); outside a browser it is immediate.
|
|
434
|
+
*/
|
|
435
|
+
backgroundReady(bgImage) {
|
|
436
|
+
if (!bgImage)
|
|
437
|
+
return;
|
|
438
|
+
// The decode wait is async: by the time onload fires the consumer may
|
|
439
|
+
// have disposed this choreographer, and patching then would resurrect
|
|
440
|
+
// state the owner already tore down.
|
|
441
|
+
const apply = () => {
|
|
442
|
+
if (this.disposed)
|
|
443
|
+
return;
|
|
444
|
+
this.patch({ backgroundImage: bgImage });
|
|
445
|
+
};
|
|
446
|
+
if (typeof Image === "undefined") {
|
|
447
|
+
apply();
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
const img = new Image();
|
|
451
|
+
img.onload = () => {
|
|
452
|
+
if (img.decode)
|
|
453
|
+
img.decode().then(apply, apply);
|
|
454
|
+
else
|
|
455
|
+
apply();
|
|
456
|
+
};
|
|
457
|
+
img.onerror = apply;
|
|
458
|
+
img.src = bgImage;
|
|
459
|
+
}
|
|
460
|
+
sequenceReady(frames, heroIndex, fps) {
|
|
461
|
+
if (frames.length)
|
|
462
|
+
this.patch({ sequence: { frames, heroIndex, fps } });
|
|
463
|
+
}
|
|
464
|
+
videoReady(videoUrl) {
|
|
465
|
+
if (videoUrl)
|
|
466
|
+
this.patch({ videoUrl });
|
|
467
|
+
}
|
|
468
|
+
ctaStyleReady(style) {
|
|
469
|
+
this.patch({ ctaStyle: style });
|
|
470
|
+
}
|
|
471
|
+
ctaReady(text, style, action) {
|
|
472
|
+
this.patch({
|
|
473
|
+
ctaStyle: style,
|
|
474
|
+
...(action ? { ctaAction: action } : {}),
|
|
475
|
+
});
|
|
476
|
+
if (text)
|
|
477
|
+
this.copyDone("cta", text);
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Terminal. Anything still held paints now, once, composed — the last
|
|
481
|
+
* chance for the stream's copy to land as a single deliberate change rather
|
|
482
|
+
* than being dropped because the budget happened to outlive the stream.
|
|
483
|
+
*/
|
|
484
|
+
done(meta) {
|
|
485
|
+
// Terminal is terminal: a trailing `done` after `error` (or a duplicate
|
|
486
|
+
// `done` — the parser's flush can re-emit a trailing frame) must not
|
|
487
|
+
// re-open a settled stream or rebrand a failure as a success.
|
|
488
|
+
if (this.isTerminal())
|
|
489
|
+
return;
|
|
490
|
+
if (this.state.phase === "holding") {
|
|
491
|
+
if (COPY_FIELDS.some((field) => (this.pending[field] ?? "") !== "")) {
|
|
492
|
+
// The blank-field restore below handles the undelivered fields.
|
|
493
|
+
this.releaseHoldToLive(false);
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
this.restoreHeld();
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
this.clearHoldTimer();
|
|
500
|
+
const fields = { ...this.state.fields };
|
|
501
|
+
const restored = [];
|
|
502
|
+
for (const field of COPY_FIELDS) {
|
|
503
|
+
fields[field] = {
|
|
504
|
+
...fields[field],
|
|
505
|
+
typing: false,
|
|
506
|
+
settled: fields[field].settled || this.closed.has(field),
|
|
507
|
+
};
|
|
508
|
+
// A field the stream never delivered ends on its fallback, never on
|
|
509
|
+
// the blank the hold painted. Not a rewrite: nothing the reader saw
|
|
510
|
+
// changed versus first paint.
|
|
511
|
+
const held = this.heldCopy?.[field];
|
|
512
|
+
if (fields[field].text === "" && held !== undefined && held !== "") {
|
|
513
|
+
fields[field] = { ...fields[field], text: held };
|
|
514
|
+
restored.push(field);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
this.heldCopy = null;
|
|
518
|
+
this.demoteUndeliveredProvenance(fields, restored);
|
|
519
|
+
this.fallbackOnCanvas.clear();
|
|
520
|
+
const declared = typeof meta.contentSource === "string" &&
|
|
521
|
+
CONTENT_SOURCES.includes(meta.contentSource)
|
|
522
|
+
? meta.contentSource
|
|
523
|
+
: null;
|
|
524
|
+
this.state = {
|
|
525
|
+
...this.state,
|
|
526
|
+
phase: "settled",
|
|
527
|
+
fields,
|
|
528
|
+
meta,
|
|
529
|
+
variantId: typeof meta.variantId === "string" ? meta.variantId : this.state.variantId,
|
|
530
|
+
// The action renders as an <a href>; an unprovable scheme stays out of
|
|
531
|
+
// state and the CTA degrades to a plain button.
|
|
532
|
+
ctaAction: safeActionUrl(typeof meta.ctaAction === "string" ? meta.ctaAction : undefined) ?? this.state.ctaAction,
|
|
533
|
+
degraded: typeof meta.degraded === "string" ? meta.degraded : null,
|
|
534
|
+
// The server's terminal word on provenance beats the per-field
|
|
535
|
+
// observation; it is the value the server itself will stand behind.
|
|
536
|
+
contentSource: declared ?? this.state.contentSource,
|
|
537
|
+
};
|
|
538
|
+
this.emit();
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* The stream failed.
|
|
542
|
+
*
|
|
543
|
+
* Whatever is painted STAYS painted. An error is never a reason to blank
|
|
544
|
+
* copy a reader is mid-sentence on — the worst honest outcome is that the
|
|
545
|
+
* creative simply stopped adapting, which looks like a normal page.
|
|
546
|
+
*/
|
|
547
|
+
failed(message) {
|
|
548
|
+
// Terminal is terminal: a late `error` after `done` must not rebrand a
|
|
549
|
+
// settled stream as a failure — the copy the reader finished with was
|
|
550
|
+
// real and complete.
|
|
551
|
+
if (this.isTerminal())
|
|
552
|
+
return;
|
|
553
|
+
if (this.state.phase === "holding")
|
|
554
|
+
this.restoreHeld();
|
|
555
|
+
this.clearHoldTimer();
|
|
556
|
+
const fields = { ...this.state.fields };
|
|
557
|
+
const restored = [];
|
|
558
|
+
for (const field of COPY_FIELDS) {
|
|
559
|
+
fields[field] = { ...fields[field], typing: false };
|
|
560
|
+
// A blank the machine itself painted must not survive the stream that
|
|
561
|
+
// justified it. Painted copy — including partial live copy — stays; a
|
|
562
|
+
// field still blank when the transport dies gets its fallback back.
|
|
563
|
+
const held = this.heldCopy?.[field];
|
|
564
|
+
if (fields[field].text === "" && held !== undefined && held !== "") {
|
|
565
|
+
fields[field] = { ...fields[field], text: held };
|
|
566
|
+
restored.push(field);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
this.heldCopy = null;
|
|
570
|
+
this.demoteUndeliveredProvenance(fields, restored);
|
|
571
|
+
this.fallbackOnCanvas.clear();
|
|
572
|
+
// No server word to defer to here, so the stream-level value is recomputed
|
|
573
|
+
// from what actually reached the canvas. A failed stream that delivered
|
|
574
|
+
// nothing readable leaves a page of authored copy, and the analytics that
|
|
575
|
+
// reads `contentSource` should say so rather than reporting an adaptation
|
|
576
|
+
// the visitor never saw.
|
|
577
|
+
const contentSource = this.hasAdaptedCopyOnCanvas(fields) ? this.state.contentSource : "static";
|
|
578
|
+
this.state = { ...this.state, phase: "failed", fields, contentSource, error: message };
|
|
579
|
+
this.emit();
|
|
580
|
+
}
|
|
581
|
+
/** Release timers. Safe to call twice. */
|
|
582
|
+
dispose() {
|
|
583
|
+
this.disposed = true;
|
|
584
|
+
this.clearHoldTimer();
|
|
585
|
+
this.listeners.clear();
|
|
586
|
+
}
|
|
587
|
+
promoteContentSource(source) {
|
|
588
|
+
const rank = { static: 0, cached: 1, generated: 2 };
|
|
589
|
+
if (rank[source] > rank[this.state.contentSource]) {
|
|
590
|
+
this.state = { ...this.state, contentSource: source };
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"}
|
package/dist/text.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire text arrives from catalog systems that store rich text — Shopify
|
|
3
|
+
* product descriptions are `body_html`, and campaign tooling pastes markdown.
|
|
4
|
+
* The SDK never renders wire content as HTML (that would hand every catalog
|
|
5
|
+
* entry a script slot), so display text is normalized to plain text instead:
|
|
6
|
+
* tags stripped, block boundaries kept as spaces, the handful of entities
|
|
7
|
+
* that actually occur decoded.
|
|
8
|
+
*/
|
|
9
|
+
export declare function plainText(value: string): string;
|
|
10
|
+
//# sourceMappingURL=text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../src/text.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAkB/C"}
|
package/dist/text.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire text arrives from catalog systems that store rich text — Shopify
|
|
3
|
+
* product descriptions are `body_html`, and campaign tooling pastes markdown.
|
|
4
|
+
* The SDK never renders wire content as HTML (that would hand every catalog
|
|
5
|
+
* entry a script slot), so display text is normalized to plain text instead:
|
|
6
|
+
* tags stripped, block boundaries kept as spaces, the handful of entities
|
|
7
|
+
* that actually occur decoded.
|
|
8
|
+
*/
|
|
9
|
+
/* ponytail: regex strip, not an HTML parser — fine for catalog copy; revisit
|
|
10
|
+
only if real payloads show nested/malformed markup it mangles. */
|
|
11
|
+
export function plainText(value) {
|
|
12
|
+
if (!value)
|
|
13
|
+
return "";
|
|
14
|
+
return (value
|
|
15
|
+
// Block-level closers and breaks become spaces so paragraphs don't fuse.
|
|
16
|
+
.replace(/<(?:br|\/p|\/div|\/h[1-6]|\/li|\/tr|\/ul|\/ol)\b[^>]*>/gi, " ")
|
|
17
|
+
.replace(/<[^>]+>/g, "")
|
|
18
|
+
.replace(/ /gi, " ")
|
|
19
|
+
.replace(/&/gi, "&")
|
|
20
|
+
.replace(/</gi, "<")
|
|
21
|
+
.replace(/>/gi, ">")
|
|
22
|
+
.replace(/"/gi, '"')
|
|
23
|
+
.replace(/�?39;/g, "'")
|
|
24
|
+
// Stray markdown heading runs seen in real Shopify descriptions.
|
|
25
|
+
.replace(/#{2,}\s*/g, "")
|
|
26
|
+
.replace(/\s+/g, " ")
|
|
27
|
+
.trim());
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=text.js.map
|
package/dist/text.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.js","sourceRoot":"","sources":["../src/text.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;oEACoE;AACpE,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACtB,OAAO,CACL,KAAK;QACH,yEAAyE;SACxE,OAAO,CAAC,0DAA0D,EAAE,GAAG,CAAC;SACxE,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC;SACxB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC;SACxB,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC;QACzB,iEAAiE;SAChE,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;SACxB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CACV,CAAC;AACJ,CAAC"}
|