Skip to content

Changelog

@sparkletree/core, @sparkletree/react and @sparkletree/cli are versioned and released together. This page carries the releases in reader terms; the full changelog in the repository is the complete record.

Every mechanism that had quietly grown two implementations now has one: config resolution (the provider now refuses a plain-http apiBase loudly at setup, as the vanilla client always did), the FieldState constructor, hydration detection. Render costs came down with it — the SSE parser no longer rescans its whole buffer per network chunk, useFragments serializes its declaration once per render instead of three times, and StreamText stops constructing a media query per animation frame. Two small API removals: hashDeclaredContext no longer accepts an environment bag (env signals never enter the fragment identity), and the CLI’s unused targetPathFor export is gone.

0.7.0: the upgrade command grows generations (2026-08-27)

Section titled “0.7.0: the upgrade command grows generations (2026-08-27)”

sparkletree upgrade now ports a copy through every template generation it is missing — decided by the copy’s content, never by guessing which release wrote it. Copies made at 0.4.0 or 0.5.0, which the old check reported “up to date” while their inline URL-vetting helper stayed frozen, now get 0.6.1’s shared safeActionUrl import; 0.3.0 copies get the reservation wiring and then that, landing on the current template. The safety rules are unchanged: exact matches, all-or-nothing per file, your Tailwind edits survive, and nothing is written when your installed packages are older than the rewritten copy needs — the command now names the version to update to.

Ten verified findings from a full-SDK review, fixed together. What your visitors gain: a stream that dies during the hold no longer throws away a headline it had fully delivered; full-bleed heroes taller than the viewport count their impressions again; a late serverRendered={false} stream can no longer retype copy on screen; and nothing that arrives after a stream’s terminal event can restyle or re-action a settled creative. What your code can rely on: impression and click beacons now ride the same fetchImpl you gave the provider, fetchContent returns null for any malformed body instead of a snapshot that throws on first touch, aborting via options.signal tears the stream down completely, and safeActionUrl is one implementation in @sparkletree/core — imported by the packaged components and the copied templates alike, so a future fix reaches all of them at once (sparkletree upgrade ports 0.3.0 copies).

0.6.0: the mark becomes opt-in (2026-08-27)

Section titled “0.6.0: the mark becomes opt-in (2026-08-27)”

No component renders a visible disclosure glyph by default any more. Provenance is unchanged — every non-static field still stamps data-content-source, and decideChrome / $group.marked still report the honest per-viewer decision — but showing a visible label is now yours to opt into: render the new InlineMark where the decision says so. The markLabel and renderMark props are gone, and FragmentGroup no longer imposes position: relative on its container (give it one yourself if you positioned absolute descendants against the group).

The hold spans the whole wait, every entrance types in, fallback and fragments included, and copy a reader can see is never typed over.

The hold belongs to the component, and spans the whole wait. It arms the moment an island mounts, before the browser ever paints, not when the stream request opens. A pinned sibling waiting on the page’s variant draw now holds reserved from its first frame instead of showing the fallback it is about to lose, and one budget covers the whole path from mount through open to delivery. This is the root fix for “fallback shown before stream arrival”, which reproduced on pinned siblings even with fast cached streams.

Every entrance types in. Text landing on a blank held canvas is always a paced, typed entrance, whichever event staged it: a live generation, a cache replay’s composed flush, a fragment’s served copy, or your fallback when the budget runs out. No entrance is a hard paint. FragmentText shares StreamText’s pacer and gains a smooth prop (default true); its settled markup is unchanged. A disabled fragment is still byte-identical to a plain string.

On-screen copy is never typed over, by anything. Deltas aimed at copy a reader can see buffer off-canvas and the field changes at most once, composed, when it settles; if the stream dies first, your fallback simply stands. This now also holds on a mid-view declaration change: an island whose campaignId or context changes after copy has been read never re-arms the hold over it.

Upgrading. No API breaks. Two things you may notice:

  • Tests judge entrances at rest: text entering a blank canvas paces, so await the final text (waitFor, or pass smooth={false} in fixtures), and strip [aria-hidden="true"] nodes before asserting visible text. The entrance’s hidden tail rides inside the element mid-drain, as the reservation already did in 0.4.0.
  • Cache replays and fragment copy no longer hard-cut onto the canvas: they type in. Hydrated/SSR pages are unchanged: copy present at first paint renders whole and is never retyped.
  • 0.4.0 (2026-08-18): the layout doctrine. Adapted copy never moves the page. The hold reserves space instead of blanking, hydrated pages never hold, the layout gate verifies late swaps, and sparkletree upgrade ports 0.3.0 copies to the reservation wiring, proven by a real-browser zero-CLS suite in CI.
  • 0.3.0 (2026-08-17): the server-side cap lift 0.2.0 staged for, section-scale fragments against the fragments-0.3 API.
  • 0.2.0 (2026-08-17): fragments grow from microcopy to whole constructions, with no wire change. Array fallbacks, the length budget, and group-level disclosure.
  • 0.1.x (2026-08-11 to 08-12): the first public releases. The copy choreography (hold, single rewrite, honest provenance), paced live streaming on by default, and the whole-fallback restore when a stream dies mid-sentence.