@bgub/fig 0.1.0-alpha.0 → 0.1.0-alpha.1
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/CHANGELOG.md +495 -0
- package/README.md +50 -19
- package/dist/element-BZ7r9ncY.d.ts +342 -0
- package/dist/element-DmbzNH0T.js +299 -0
- package/dist/element-DmbzNH0T.js.map +1 -0
- package/dist/hooks-QDRabULx.d.ts +158 -0
- package/dist/index.d.ts +3 -5
- package/dist/index.js +4 -4
- package/dist/internal.d.ts +212 -6
- package/dist/internal.js +14 -43
- package/dist/internal.js.map +1 -1
- package/dist/jsx-runtime.d.ts +1 -1
- package/dist/jsx-runtime.js +3 -3
- package/dist/jsx-runtime.js.map +1 -1
- package/dist/payload-format-KTNaSI4h.js +366 -0
- package/dist/payload-format-KTNaSI4h.js.map +1 -0
- package/dist/payload.d.ts +92 -0
- package/dist/payload.js +429 -0
- package/dist/payload.js.map +1 -0
- package/dist/{transition-DEqcImne.js → resource-kXeIR52S.js} +121 -71
- package/dist/resource-kXeIR52S.js.map +1 -0
- package/dist/{data-store-CRnNAT9-.js → transition-CC4kjjrU.js} +165 -54
- package/dist/transition-CC4kjjrU.js.map +1 -0
- package/package.json +5 -6
- package/dist/data-CHJh_xU9.d.ts +0 -79
- package/dist/data-h46EcMnE.js +0 -37
- package/dist/data-h46EcMnE.js.map +0 -1
- package/dist/data-store-CRnNAT9-.js.map +0 -1
- package/dist/data-store-EQOLQsW4.d.ts +0 -32
- package/dist/element-Bh_k9c3N.js +0 -179
- package/dist/element-Bh_k9c3N.js.map +0 -1
- package/dist/element-CCOOi4Ka.d.ts +0 -209
- package/dist/server.browser.d.ts +0 -9
- package/dist/server.browser.js +0 -8
- package/dist/server.browser.js.map +0 -1
- package/dist/server.d.ts +0 -9
- package/dist/server.js +0 -9
- package/dist/server.js.map +0 -1
- package/dist/transition-Cl6mAPcx.d.ts +0 -96
- package/dist/transition-DEqcImne.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,498 @@
|
|
|
1
|
+
## @bgub/fig@0.1.0-alpha.1
|
|
2
|
+
|
|
3
|
+
### Asset descriptors use native names and preserve native ordering
|
|
4
|
+
|
|
5
|
+
Client-inserted and host-rendered stylesheets now form precedence buckets in
|
|
6
|
+
the order each distinct precedence value is first discovered. A stylesheet
|
|
7
|
+
discovered later for an existing bucket is inserted before the following
|
|
8
|
+
bucket, keeping lazy and payload-delivered CSS in its intended cascade order.
|
|
9
|
+
|
|
10
|
+
Raw `<script>` elements now enter the asset registry only when explicitly
|
|
11
|
+
marked `async`. Non-async scripts retain their native document position and
|
|
12
|
+
execution semantics; explicit `script()` descriptors continue to support all
|
|
13
|
+
asset-delivery modes.
|
|
14
|
+
|
|
15
|
+
Asset descriptor options and serialized payload asset rows now use native HTML
|
|
16
|
+
attribute names: `crossorigin`, `fetchpriority`, and `http-equiv`. The previous
|
|
17
|
+
React-style `crossOrigin`, `fetchPriority`, and `httpEquiv` spellings are
|
|
18
|
+
removed.
|
|
19
|
+
|
|
20
|
+
Host resource resolution now receives the actual host parent and fixes
|
|
21
|
+
out-of-band placement once per fiber. SVG and MathML titles consequently stay
|
|
22
|
+
in their native namespace, while HTML titles carrying `itemprop` stay in-tree;
|
|
23
|
+
ordinary document titles continue to use the shared head registry.
|
|
24
|
+
|
|
25
|
+
### Drop the `clientReferenceAssets` helper from the main entry
|
|
26
|
+
|
|
27
|
+
The runtime helper `clientReferenceAssets(reference)` (read a client
|
|
28
|
+
reference's declared assets, resolving thunks) had two homes; it is now
|
|
29
|
+
exported only from `@bgub/fig/internal`, where its consumers — the payload
|
|
30
|
+
serializer and framework manifest plumbing — already import it. Apps
|
|
31
|
+
declare assets with `clientReference({ assets })` and never call the
|
|
32
|
+
helper. The `ClientReferenceAssets` type stays on the main entry because
|
|
33
|
+
it appears in the public `ClientReferenceOptions.assets` signature.
|
|
34
|
+
|
|
35
|
+
### Stable client-reference identity across decodes
|
|
36
|
+
|
|
37
|
+
`@bgub/fig/payload` exposes `createPayloadClientReferenceResolver(resolve)`:
|
|
38
|
+
a caller-owned stateful resolver passed as the `resolveClientReference`
|
|
39
|
+
decode option. With one, every resolvable client reference decodes to one
|
|
40
|
+
resolver-owned wrapper per reference id across all decodes sharing the
|
|
41
|
+
resolver — gated, ungated, or asynchronously resolved — so re-decoding a
|
|
42
|
+
payload updates islands in place instead of remounting them. Reveal gates
|
|
43
|
+
ride the decoded element instances rather than the wrapper: each decode
|
|
44
|
+
gates exactly its own content, so a newer decode's pending assets never
|
|
45
|
+
re-suspend an island already on screen, while its new island instances
|
|
46
|
+
still wait for the stylesheets they declared. The caller owns the
|
|
47
|
+
resolver's lifetime; under fast refresh no manual invalidation is needed
|
|
48
|
+
(hot edits remap the latched resolution through component families, and
|
|
49
|
+
unaccepted updates full-reload), while `delete`/`clear` cover manifest
|
|
50
|
+
swaps without a reload. With a plain resolve function, gated and async
|
|
51
|
+
references keep their per-decode wrapper identity, now with the same
|
|
52
|
+
per-element gating.
|
|
53
|
+
|
|
54
|
+
`@bgub/fig-dom`'s `payloadDataLoader` accepts the stateful resolver through
|
|
55
|
+
its existing `resolveClientReference` option.
|
|
56
|
+
|
|
57
|
+
Framework adapters can retain one resolver across refreshes and navigations,
|
|
58
|
+
preventing asset-gated islands from remounting on every re-decode.
|
|
59
|
+
|
|
60
|
+
### Publish metadata only with its visible owner
|
|
61
|
+
|
|
62
|
+
Title and meta resources now travel through Payload as owner-bound
|
|
63
|
+
declarations and update the document only when their decoded tree commits.
|
|
64
|
+
Pending or superseded refreshes keep the previous metadata visible.
|
|
65
|
+
|
|
66
|
+
Streaming HTML now treats Suspense fallbacks as metadata owners and reconciles
|
|
67
|
+
the completed visible metadata snapshot in the boundary reveal operation.
|
|
68
|
+
Partial segments and failed or abandoned primary work cannot mutate the head.
|
|
69
|
+
|
|
70
|
+
The obsolete `onAssetError` option and its asset-diagnostic types are removed:
|
|
71
|
+
late metadata is delivered with its owner instead of being dropped.
|
|
72
|
+
|
|
73
|
+
### Supersession abort waits for the successor's value
|
|
74
|
+
|
|
75
|
+
Refreshing a fulfilled data-resource entry no longer aborts the previous
|
|
76
|
+
generation's signal when the new load starts. Authority transfers when the
|
|
77
|
+
successor's value publishes: the visible stale value keeps streaming through
|
|
78
|
+
the refresh window (payload holes keep filling), subscribers re-render onto
|
|
79
|
+
the new tree in the same pass the old generation retires in, and a failed
|
|
80
|
+
refresh leaves the previous generation fully alive — stale value usable,
|
|
81
|
+
live holes included. Value-less pending loads still abort immediately when
|
|
82
|
+
superseded, and hydrate-over/eviction/disposal still end every generation.
|
|
83
|
+
|
|
84
|
+
This closes the gap where refreshing a serialized-component resource while
|
|
85
|
+
its holes were still streaming surfaced "Payload decode aborted." through
|
|
86
|
+
the nearest ErrorBoundary — cancellation now stays retirement, never a user
|
|
87
|
+
error, for plain consumers too.
|
|
88
|
+
|
|
89
|
+
### DevTools show per-fiber data-resource dependencies
|
|
90
|
+
|
|
91
|
+
`@bgub/fig` data stores expose `inspectDataDependencyCanonicalKeys(owner)`,
|
|
92
|
+
a dev-only inspection read of the canonical keys an owner's committed
|
|
93
|
+
`readData` subscriptions point at (returns an empty array in prod builds).
|
|
94
|
+
|
|
95
|
+
`@bgub/fig-reconciler` devtools snapshots record those keys on every fiber
|
|
96
|
+
as `FigDevtoolsFiberSnapshot.dataResourceCanonicalKeys`. The field is
|
|
97
|
+
required, so external snapshot producers must supply it (empty array when
|
|
98
|
+
unknown).
|
|
99
|
+
|
|
100
|
+
The `@bgub/fig-devtools` panel filters the Data section by the selected
|
|
101
|
+
fiber's dependencies instead of always listing the entire store, and tree
|
|
102
|
+
rows show a green badge with the fiber's data-resource read count next to
|
|
103
|
+
the blue hook-count badge. Selecting
|
|
104
|
+
the root still lists every entry, including those with no committed
|
|
105
|
+
subscriber (unclaimed preloads, hydrated-but-unread rows). Refreshing
|
|
106
|
+
entries no longer render a redundant `Pending` row.
|
|
107
|
+
|
|
108
|
+
### `ensureData`: the awaitable read for code outside render
|
|
109
|
+
|
|
110
|
+
`ensureData(resource, ...args)` resolves the value a key would render with:
|
|
111
|
+
the cached value when the entry has one (kicking the same background
|
|
112
|
+
revalidation a stale `readData` does), or the in-flight load's settlement on
|
|
113
|
+
a cache miss. It rejects with the error `readData` would throw, follows
|
|
114
|
+
superseding loads and server hydrations to the authoritative value, and never
|
|
115
|
+
subscribes — pair it with `readData` in the component, which claims the
|
|
116
|
+
settled entry within the preload retention window. An awaiting caller retains
|
|
117
|
+
the entry, so the unclaimed-preload eviction cannot abort a load out from
|
|
118
|
+
under an ensure.
|
|
119
|
+
|
|
120
|
+
Available as a free function (ambient store) and on the explicit handle
|
|
121
|
+
(`readDataStore()`, `root.data`). This is the delegation verb for external
|
|
122
|
+
routers: a route loader awaits `ensureData`, the component reads with
|
|
123
|
+
`readData`, and the data store stays the single cache.
|
|
124
|
+
|
|
125
|
+
### Serialized components move to the data-resource model
|
|
126
|
+
|
|
127
|
+
Serialized trees are now ordinary data resources: servers return plain
|
|
128
|
+
Payload streams, and clients consume them through keyed `dataResource`
|
|
129
|
+
instances with `payloadDataLoader`. Refresh is `refreshData`, navigation can
|
|
130
|
+
select a new key, and back/forward navigations reuse cached entries. Commits
|
|
131
|
+
wait for the incoming Payload, island modules, and stylesheet gates.
|
|
132
|
+
|
|
133
|
+
Supporting API additions: `payloadDataLoader` accepts a `prepareAssets`
|
|
134
|
+
override (defaults to `insertAssetResources`), and `decodePayloadStream`
|
|
135
|
+
accepts an `onClientReference` observer for reference metadata.
|
|
136
|
+
|
|
137
|
+
### Compose host behavior with mixins
|
|
138
|
+
|
|
139
|
+
Core now exports `createMixin()` and resolves render-time host behavior through
|
|
140
|
+
the `mix` prop. Mixins may contribute host props or nested mixins while keeping
|
|
141
|
+
the host type and subtree fixed. Explicit host props form the baseline; mixins
|
|
142
|
+
run in authoring order, and later results win.
|
|
143
|
+
|
|
144
|
+
DOM event listeners now use `mix={on(type, callback)}`. Migrate
|
|
145
|
+
`events={[on(type, callback)]}` to `mix={on(type, callback)}`; multiple or
|
|
146
|
+
conditional descriptors move into `mix={[...]}` and preserve positional
|
|
147
|
+
listener identity.
|
|
148
|
+
|
|
149
|
+
### Keep `useId` stable through selective hydration
|
|
150
|
+
|
|
151
|
+
`useId` now follows one canonical server/hydration tree path through Suspense
|
|
152
|
+
and Activity. Dehydrated boundaries snapshot that path when they claim their
|
|
153
|
+
server marker, then restore it when hydration resumes, so client updates that
|
|
154
|
+
insert or move surrounding siblings cannot renumber ids already present in the
|
|
155
|
+
server HTML. Suspense's private Activity wrapper is transparent to the path.
|
|
156
|
+
|
|
157
|
+
Components mounted only on the client now receive ids from a separate
|
|
158
|
+
`fig-C-*` namespace. Those ids remain stable for the component lifetime and
|
|
159
|
+
cannot collide with ids reserved by server-rendered content that has not
|
|
160
|
+
hydrated yet.
|
|
161
|
+
|
|
162
|
+
### `isValidElement` has a single home on the main entry
|
|
163
|
+
|
|
164
|
+
`isValidElement` was the one runtime export with two homes: the app-facing
|
|
165
|
+
main entry and `@bgub/fig/internal` (grouped with the other `$$typeof`
|
|
166
|
+
brand predicates). It is now exported only from `@bgub/fig`; the renderer
|
|
167
|
+
and server packages import it from there. The internal-only predicates
|
|
168
|
+
(`isSuspense`, `isPortal`, ...) are unchanged.
|
|
169
|
+
|
|
170
|
+
### Expose the payload decoder on JSR
|
|
171
|
+
|
|
172
|
+
The JSR manifest now exports `@bgub/fig/payload`, matching the npm package
|
|
173
|
+
and making the browser-safe payload decoder available from both registries.
|
|
174
|
+
|
|
175
|
+
### Payload exposes rendering and decoding, not its implementation
|
|
176
|
+
|
|
177
|
+
The payload packages now present two primary operations:
|
|
178
|
+
`renderToPayloadStream` on the server and `decodePayloadStream` in the
|
|
179
|
+
browser-safe core entry. Row/model types, value encoding, codec machinery,
|
|
180
|
+
content-type negotiation helpers, and framework document transports are
|
|
181
|
+
internal implementation details.
|
|
182
|
+
|
|
183
|
+
Client references use one `resolveClientReference(reference)` seam that may
|
|
184
|
+
return a component synchronously or asynchronously. It replaces the separate
|
|
185
|
+
load, resolve, and observation callbacks; reference metadata now includes its
|
|
186
|
+
stream-safe assets. The unused `load` field is also removed from
|
|
187
|
+
`clientReference(...)` declarations.
|
|
188
|
+
|
|
189
|
+
`payloadDataLoader` keeps only `request`, `resolveClientReference`, and the
|
|
190
|
+
optional `prepareAssets` override. Payload encoding is fixed internally rather
|
|
191
|
+
than exposed as a speculative custom-codec interface.
|
|
192
|
+
|
|
193
|
+
### Generation-lifetime loader signals and `payloadDataLoader`
|
|
194
|
+
|
|
195
|
+
Data-resource loader signals now live as long as their load's generation,
|
|
196
|
+
not just the pending promise: the `{ signal }` a loader receives stays
|
|
197
|
+
unaborted after the value lands and aborts when the generation loses
|
|
198
|
+
authority — a newer load supersedes it, a server push hydrates over it, the
|
|
199
|
+
entry evicts, or the store is disposed. A rejected load's own signal aborts
|
|
200
|
+
on settlement, and `invalidateData` never aborts (marking stale does not
|
|
201
|
+
revoke authority). Loaders that stream into their value in the background —
|
|
202
|
+
payload decodes filling holes — tie that work to the signal; plain fetch
|
|
203
|
+
loaders are unaffected.
|
|
204
|
+
|
|
205
|
+
The load context also carries an internal, generation-guarded hydration
|
|
206
|
+
capability (symbol-keyed; read through `@bgub/fig/internal`) that hydrates
|
|
207
|
+
server-pushed `data` rows through the calling store only while the load is
|
|
208
|
+
authoritative, skipping rows that target the loading entry's own key.
|
|
209
|
+
|
|
210
|
+
New in `@bgub/fig-dom`: `payloadDataLoader({ request,
|
|
211
|
+
resolveClientReference?, prepareAssets? })` adapts a payload-stream
|
|
212
|
+
endpoint into an ordinary data-resource loader. It validates the response
|
|
213
|
+
(status, body, payload codec content-type; unusable bodies are cancelled),
|
|
214
|
+
wires `decodePayloadStream` to the generation-lifetime signal, hydrates
|
|
215
|
+
`data` rows through the store capability, inserts stream-discovered assets
|
|
216
|
+
with `insertAssetResources` (stylesheet gates delay only dependent reveal),
|
|
217
|
+
and resolves with the decoded root value — so `readData(postResource, slug)`
|
|
218
|
+
suspends like any read and returns renderable elements while streamed holes
|
|
219
|
+
keep filling in the background.
|
|
220
|
+
|
|
221
|
+
### `@bgub/fig/payload`: the browser-safe payload home and `decodePayloadStream`
|
|
222
|
+
|
|
223
|
+
The payload decoder moved from `@bgub/fig-server/payload` to a new browser-safe
|
|
224
|
+
`@bgub/fig/payload` entry. Browser code no longer imports the server package to
|
|
225
|
+
decode; fig-server's serializer and the decoder share their private row/value
|
|
226
|
+
format through `@bgub/fig/internal`.
|
|
227
|
+
|
|
228
|
+
New client half: `decodePayloadStream(stream, options)` returns a live
|
|
229
|
+
`PayloadDecode` — `value` resolves when the root row decodes while outlined
|
|
230
|
+
holes keep streaming in, the never-rejecting `completion` reports
|
|
231
|
+
complete/failed/aborted, and `abort()` idempotently rejects unresolved holes
|
|
232
|
+
with an internal cancellation reason. Options wire data-row hydration
|
|
233
|
+
(`hydrate`), unified client-reference resolution, and asset preparation with
|
|
234
|
+
reveal gating (`prepareAssets`).
|
|
235
|
+
|
|
236
|
+
Internally, `assets` rows now carry an optional `for` — the row id whose
|
|
237
|
+
reveal depends on those assets, decided at serialization scope exit so a
|
|
238
|
+
suspended subtree's assets gate its outlined row rather than the enclosing
|
|
239
|
+
tree.
|
|
240
|
+
|
|
241
|
+
Dev-time enforcement: serialized components are render-only. During
|
|
242
|
+
`renderToPayloadStream`, `useState`, `useActionState`, `useTransition`,
|
|
243
|
+
`useStableEvent`, and the effect hooks now throw in development; the read
|
|
244
|
+
verbs, `useMemo`, `useId`, and `useSyncExternalStore`'s `getServerSnapshot`
|
|
245
|
+
path stay server-safe.
|
|
246
|
+
|
|
247
|
+
### Deliver client-reference assets outside the resolution suspension
|
|
248
|
+
|
|
249
|
+
A decoded client reference's asset declarations now attach above its
|
|
250
|
+
module-load and reveal-gate suspension points. A server document render
|
|
251
|
+
that hits a cold reference module emits the island's stylesheet with the
|
|
252
|
+
segment containing the reference instead of with the late fill, so
|
|
253
|
+
first-request asset ordering matches warm requests.
|
|
254
|
+
|
|
255
|
+
### Move `createPortalNode` to `@bgub/fig/internal`
|
|
256
|
+
|
|
257
|
+
`createPortalNode` is the cross-package seam renderers wrap in their
|
|
258
|
+
container-typed `createPortal`; apps never call it directly. It now lives
|
|
259
|
+
on the internal entry with the other renderer protocol exports instead of
|
|
260
|
+
the app-facing main entry. Portal-creating apps keep using
|
|
261
|
+
`createPortal(children, container, key?)` from `@bgub/fig-dom`; the
|
|
262
|
+
`FigPortal` type stays on the main entry because it appears in public
|
|
263
|
+
signatures.
|
|
264
|
+
|
|
265
|
+
### Promise-shaped payload decoder
|
|
266
|
+
|
|
267
|
+
`decodePayloadStream` now returns `Promise<FigNode>` directly — the root
|
|
268
|
+
value promise — instead of a `PayloadDecode` handle. Cancellation is
|
|
269
|
+
signal-only (`options.signal`, unchanged); the redundant `abort()` method is
|
|
270
|
+
gone. The `completion` promise is replaced by an `onStreamDone(result)`
|
|
271
|
+
decode option, called exactly once when ingestion settles as `complete`,
|
|
272
|
+
`failed`, or `aborted` — post-root failures that strand no pending hole
|
|
273
|
+
remain observable there. The callback is never awaited and its exceptions
|
|
274
|
+
and rejections are swallowed, so an observer — sync or async — cannot break
|
|
275
|
+
decode teardown or leak an unhandled rejection. The
|
|
276
|
+
`PayloadDecode` interface and its non-thenable caveat are deleted;
|
|
277
|
+
`PayloadDecodeCompletion` remains as the callback's result type.
|
|
278
|
+
|
|
279
|
+
`@bgub/fig-dom`'s `payloadDataLoader` migrates internally; its public API is
|
|
280
|
+
unchanged.
|
|
281
|
+
|
|
282
|
+
Also considered and declined: narrowing `ResolveClientReference` to an
|
|
283
|
+
opaque id. The reference's `exportName`/`ssr`/`assets` mirror the `client`
|
|
284
|
+
row's wire fields and are all load-bearing in framework document pipelines;
|
|
285
|
+
the rationale is recorded in `docs/concepts/payload.md`.
|
|
286
|
+
|
|
287
|
+
### Promise-valued children render through Suspense
|
|
288
|
+
|
|
289
|
+
`FigNode` now accepts promises of nodes. Promise children occupy distinct,
|
|
290
|
+
host-transparent child slots, suspend through the nearest `Suspense`, and route
|
|
291
|
+
rejections or invalid resolved children through normal error handling.
|
|
292
|
+
|
|
293
|
+
HTML rendering retains exact promise children as independent streaming tasks,
|
|
294
|
+
while Payload uses node-validated promise rows that decode to the same child
|
|
295
|
+
shape. Payload-rendered async components are invoked once and retain their
|
|
296
|
+
component-scoped assets on the outlined row.
|
|
297
|
+
|
|
298
|
+
### Tighten public component, loader, asset, and bind signatures
|
|
299
|
+
|
|
300
|
+
`@bgub/fig` now names the shared data loader contract as
|
|
301
|
+
`DataResourceLoader`, constrains lazy loaders to components, and exposes
|
|
302
|
+
`ComponentProps` so lazy wrappers preserve the loaded component's props
|
|
303
|
+
without exposing its implementation statics. Client-reference SSR
|
|
304
|
+
implementations stay aligned with the reference's props. Stable-event typing
|
|
305
|
+
now models the trailing lifecycle signal separately from caller arguments.
|
|
306
|
+
|
|
307
|
+
`meta()` descriptors now require exactly one valid metadata identity:
|
|
308
|
+
`charset`, `name` plus `content`, `property` plus `content`, or `http-equiv`
|
|
309
|
+
plus `content`. Raw meta elements that do not satisfy the same shape remain
|
|
310
|
+
ordinary host elements instead of entering the asset registry.
|
|
311
|
+
|
|
312
|
+
`@bgub/fig-dom` payload requests now receive the standard
|
|
313
|
+
`DataResourceLoadContext`, portals retain their DOM container type in the
|
|
314
|
+
returned `FigPortal`, and bind callbacks must return `undefined`; cleanup is
|
|
315
|
+
exclusively driven by their `AbortSignal`.
|
|
316
|
+
|
|
317
|
+
### Attribute rejected payload holes to their data resource
|
|
318
|
+
|
|
319
|
+
Payload hole errors are attributed to the authoritative owning data-resource
|
|
320
|
+
generation. Error boundaries receive `dataResourceKeys`, and
|
|
321
|
+
`invalidateDataError` retires the broken fulfilled value before retrying so a
|
|
322
|
+
remounted boundary suspends on fresh content instead of immediately catching
|
|
323
|
+
the same rejected hole. `decodePayloadStream` also exposes an observational
|
|
324
|
+
`onHoleError` callback.
|
|
325
|
+
|
|
326
|
+
The load context's hydration capability now shares the same authority window:
|
|
327
|
+
a still-visible generation's `data` rows keep hydrating through a superseding
|
|
328
|
+
refresh's window instead of being dropped the moment the refresh starts.
|
|
329
|
+
|
|
330
|
+
### Remove the `FigText` type alias
|
|
331
|
+
|
|
332
|
+
`FigText` was a two-member alias (`string | number`) whose only use was
|
|
333
|
+
as a constituent of the `FigNode` union; no signature anywhere took it
|
|
334
|
+
by name. The union now spells out `string | number` directly, and the
|
|
335
|
+
alias is gone from both the main and internal entries. Code that
|
|
336
|
+
referenced `FigText` should use `string | number` (or `FigNode` where
|
|
337
|
+
the full children type is meant).
|
|
338
|
+
|
|
339
|
+
### Remove the `h` alias for `createElement`
|
|
340
|
+
|
|
341
|
+
`@bgub/fig` no longer exports `createElement` under the second name `h`.
|
|
342
|
+
Every export has one home and one name; migrate by importing
|
|
343
|
+
`createElement` (or alias it locally: `const h = createElement`).
|
|
344
|
+
|
|
345
|
+
### Use one data-resource API in every environment
|
|
346
|
+
|
|
347
|
+
`dataResource` now covers shared, browser, and server-only loaders without a
|
|
348
|
+
second API. Server-only loaders belong behind the framework's server module
|
|
349
|
+
boundary; browser code uses an explicit key-only resource when it needs the
|
|
350
|
+
same hydrated value.
|
|
351
|
+
|
|
352
|
+
The pass-through `serverDataResource` API, `@bgub/fig/server` entry point,
|
|
353
|
+
`figData` Vite transform, and generated browser resource stubs are removed.
|
|
354
|
+
|
|
355
|
+
### The targeted-refresh protocol and payload consumer are gone
|
|
356
|
+
|
|
357
|
+
The serialized-components deletion gate passed across the demo e2e suites,
|
|
358
|
+
so the legacy architecture is removed rather than deprecated:
|
|
359
|
+
|
|
360
|
+
- `@bgub/fig-server/payload` no longer exports `createPayloadConsumer`,
|
|
361
|
+
`PayloadConsumer`, `PayloadBoundary`, `PAYLOAD_BOUNDARY_HEADER`,
|
|
362
|
+
`PayloadFetchError`, or the consumer fetch/ingestion seam, and
|
|
363
|
+
`renderToPayloadStream` drops its `refreshBoundary` option.
|
|
364
|
+
- The wire format loses the `refresh`/`refresh-error` rows and the
|
|
365
|
+
`$fig:"boundary"` model: the refresh unit is the data-resource key that
|
|
366
|
+
delivers the payload, so refreshing is requesting the same stream again.
|
|
367
|
+
|
|
368
|
+
Replacements, all already shipping: `decodePayloadStream` in
|
|
369
|
+
`@bgub/fig/payload` is the client half; fig-dom's `payloadDataLoader`
|
|
370
|
+
delivers a serialized tree as an ordinary data resource; the freshness
|
|
371
|
+
verbs (`refreshData`/`invalidateData`) are the refresh story; sub-tree
|
|
372
|
+
refresh granularity is finer resource keys.
|
|
373
|
+
|
|
374
|
+
### Move HTML escaping helpers to a focused subpath
|
|
375
|
+
|
|
376
|
+
`escapeAttribute`, `escapeText`, `escapeScriptText`, and `escapeScriptJson` now export from
|
|
377
|
+
`@bgub/fig-server/html` instead of the main `@bgub/fig-server` entry.
|
|
378
|
+
The dedicated subpath keeps companion-markup helpers separate from server
|
|
379
|
+
render entry points while preserving their exact escaping behavior. The
|
|
380
|
+
TanStack Start adapter now consumes these helpers, Fig's internal data-store
|
|
381
|
+
brand predicate, and its own storage-context API instead of duplicating them.
|
|
382
|
+
|
|
383
|
+
### Server-route navigations commit content in one pass
|
|
384
|
+
|
|
385
|
+
Navigating to a payload server route used to commit an empty slot for one
|
|
386
|
+
retry beat (stretched to the full animation length when a view transition
|
|
387
|
+
was running) before the content revealed, because three already-settled
|
|
388
|
+
promises still suspended on their first render read:
|
|
389
|
+
|
|
390
|
+
- payload element gates are now tracked at creation, so a gate that settles
|
|
391
|
+
before its first read resolves synchronously;
|
|
392
|
+
- client-reference module resolutions are tracked the same way;
|
|
393
|
+
- pre-commit asset preparation now reveals the island hydration gate,
|
|
394
|
+
so navigations mount real islands instead of paying a placeholder →
|
|
395
|
+
reveal follow-up commit.
|
|
396
|
+
|
|
397
|
+
The route swap now lands as a single commit containing the full decoded
|
|
398
|
+
content, so view transitions capture the destination page with its content
|
|
399
|
+
already present.
|
|
400
|
+
|
|
401
|
+
### Resolved client references keep their identity across decodes
|
|
402
|
+
|
|
403
|
+
`decodePayloadStream` used to wrap every client reference in a fresh
|
|
404
|
+
per-decode component, so re-decoding a surrounding payload (refreshing a
|
|
405
|
+
server component that contains islands) remounted every island and dropped
|
|
406
|
+
its client state.
|
|
407
|
+
|
|
408
|
+
References that `resolveClientReference` resolves synchronously and that carry no asset
|
|
409
|
+
gate now decode to the resolved component itself. The element type is
|
|
410
|
+
identity-stable across decodes, so a refresh of the surrounding payload
|
|
411
|
+
updates islands in place and their state survives. Gated or
|
|
412
|
+
asynchronously resolved references still decode to per-decode wrappers.
|
|
413
|
+
|
|
414
|
+
### Preserve framework-managed asset placement
|
|
415
|
+
|
|
416
|
+
Framework adapters can now keep externally managed head and body tags in their
|
|
417
|
+
declared positions without exposing a DOM prop. TanStack Router uses this for
|
|
418
|
+
route-managed links, styles, and scripts while continuing to map title and meta
|
|
419
|
+
entries through Fig asset resources. Full-document hydration now ignores the
|
|
420
|
+
doctype and one shared marker identifies every server-owned node without a
|
|
421
|
+
client fiber. Declarative asset lists also gain a client commit lifecycle, so
|
|
422
|
+
route titles and metadata update during navigation.
|
|
423
|
+
|
|
424
|
+
### Add Payload routes to the TanStack Start adapter
|
|
425
|
+
|
|
426
|
+
`@bgub/fig-tanstack-start/payload` now exposes `payloadResource`, which compiles
|
|
427
|
+
an inline render callback into a private server function and Fig-owned route
|
|
428
|
+
data resource. Applications supply the cache key and component tree without
|
|
429
|
+
authoring transport plumbing. The shared declaration can stay in one
|
|
430
|
+
`.payload.tsx` module; its render callback and render-only imports are omitted
|
|
431
|
+
from the browser build. The initial SSR response is embedded into the document
|
|
432
|
+
and adopted without refetching; client navigation and refresh use the same
|
|
433
|
+
resource request path. Payload data rows
|
|
434
|
+
hydrate the shared store, asset resources are retained on their owning server
|
|
435
|
+
segments or inserted through the browser registry, and client references retain
|
|
436
|
+
their resolver-defined identity. `decodePayloadStream` and `payloadDataLoader`
|
|
437
|
+
now expose `retainAssets` for server document renderers that need this delivery
|
|
438
|
+
path.
|
|
439
|
+
|
|
440
|
+
`@bgub/fig-tanstack-start/server` exposes the lower-level
|
|
441
|
+
`renderPayloadResponse` used by the generated TanStack server function; it
|
|
442
|
+
defaults its render abort signal to the incoming request, so a disconnected
|
|
443
|
+
client cancels the Payload render. Shell
|
|
444
|
+
HTML streams while outlined Suspense holes settle, and the completed initial
|
|
445
|
+
responses are embedded before TanStack starts full-document hydration. The Vite
|
|
446
|
+
adapter also publishes assets imported only by server modules into the client
|
|
447
|
+
build output. Fig Router links also consume TanStack's `viewTransition`
|
|
448
|
+
navigation option without forwarding it as an invalid attribute to the rendered
|
|
449
|
+
anchor, and derive active state from the resolved route instead of an in-flight
|
|
450
|
+
location.
|
|
451
|
+
|
|
452
|
+
### Add the TanStack Start runtime adapter
|
|
453
|
+
|
|
454
|
+
`createDataStore` now creates a root-neutral Fig store that route loaders can
|
|
455
|
+
populate before a renderer exists. Server and client renderers adopt that exact
|
|
456
|
+
store, preserving one cache while attaching their lifecycle and scheduling.
|
|
457
|
+
|
|
458
|
+
The new TanStack Start runtime uses the store for route loading, server
|
|
459
|
+
rendering, Fig-owned document serialization, client deserialization, and
|
|
460
|
+
hydration. Route-managed head and script output maps through the Router adapter,
|
|
461
|
+
including Fig asset resources. The end-to-end contract verifies no initial
|
|
462
|
+
client refetch and exactly one reload after invalidation.
|
|
463
|
+
|
|
464
|
+
### Share the text-separator protocol constant; small DOM cleanups
|
|
465
|
+
|
|
466
|
+
The `<!--,-->` text-separator comment the server writes between adjacent text
|
|
467
|
+
fibers was hardcoded independently by the server renderer and fig-dom's
|
|
468
|
+
hydration cursor. The comment data now lives in `@bgub/fig/internal` as
|
|
469
|
+
`TEXT_SEPARATOR_DATA`, next to the other streaming protocol constants, so the
|
|
470
|
+
two sides cannot drift. No wire change — the emitted markup is identical.
|
|
471
|
+
|
|
472
|
+
fig-dom also drops an unused internal `rootFor` helper, routes style clearing
|
|
473
|
+
through the shared `isEmptyPropValue` predicate, and simplifies a redundant
|
|
474
|
+
branch in select-value syncing. No behavior change.
|
|
475
|
+
|
|
476
|
+
### Add typed View Transition scopes and abortable lifecycle events
|
|
477
|
+
|
|
478
|
+
`transition` and the `useTransition` starter now accept explicit transition
|
|
479
|
+
types, which Fig carries with the updates that reach each root and forwards to
|
|
480
|
+
the browser without leaking into unrelated commits.
|
|
481
|
+
|
|
482
|
+
`ViewTransition` adds one `onTransition(event, signal)` lifecycle callback for
|
|
483
|
+
enter, exit, share, and update phases. Events expose all participating surfaces
|
|
484
|
+
and the commit's transition types. Fig DOM's optional View Transition entry can
|
|
485
|
+
resolve those opaque surfaces into group, image-pair, old, and new pseudo
|
|
486
|
+
handles for animation and inspection; the signal aborts when the native
|
|
487
|
+
transition finishes.
|
|
488
|
+
|
|
489
|
+
### Bound stalled view-transition waits
|
|
490
|
+
|
|
491
|
+
Transition-eligible commits and annotated streaming reveals now wait at most 60
|
|
492
|
+
seconds for a previous browser View Transition. If its completion promise never
|
|
493
|
+
settles, Fig releases the document mutex and proceeds with the latest work
|
|
494
|
+
instead of parking it forever.
|
|
495
|
+
|
|
1
496
|
## @bgub/fig@0.1.0-alpha.0 (alpha)
|
|
2
497
|
|
|
3
498
|
### Initial alpha release
|
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ function App() {
|
|
|
32
32
|
|
|
33
33
|
return (
|
|
34
34
|
<main>
|
|
35
|
-
<button
|
|
35
|
+
<button mix={on("click", () => setCount((value) => value + 1))}>
|
|
36
36
|
Count {count}
|
|
37
37
|
</button>
|
|
38
38
|
<Suspense fallback={<span>Loading</span>}>
|
|
@@ -52,6 +52,10 @@ createRoot(container).render(<App />);
|
|
|
52
52
|
|
|
53
53
|
- Elements: `createElement`, `isValidElement`, `Fragment`, and the JSX
|
|
54
54
|
runtime.
|
|
55
|
+
- Host behavior: `createMixin(type)` creates render-time prop composers for
|
|
56
|
+
intrinsic elements. A host accepts one descriptor or nested arrays through
|
|
57
|
+
`mix`; mixins may return props or more mixins but cannot replace `children`,
|
|
58
|
+
`key`, or `unsafeHTML`.
|
|
55
59
|
- State and memoization: `useState`, `useDeferredValue`, `useMemo`, and
|
|
56
60
|
`useCallback`.
|
|
57
61
|
- Stable identifiers: `useId()` generates IDs that match server render and
|
|
@@ -91,7 +95,9 @@ createRoot(container).render(<App />);
|
|
|
91
95
|
- `ErrorBoundary` catches render and Fig effect errors. Use `onError` for
|
|
92
96
|
reporting and change the boundary key to reset sticky fallback state. Data
|
|
93
97
|
resource load failures report failed keys on `info.dataResourceKeys`, so
|
|
94
|
-
recovery flows can refresh or invalidate those keys before resetting.
|
|
98
|
+
recovery flows can refresh or invalidate those keys before resetting. This
|
|
99
|
+
includes a streamed payload hole that rejects after its owning entry has
|
|
100
|
+
already fulfilled.
|
|
95
101
|
- `transition(callback)` and `useTransition()` mark updates that may preserve
|
|
96
102
|
already-revealed Suspense content while new work is pending. Updates scheduled
|
|
97
103
|
inside the callback run at transition priority, including updates after an
|
|
@@ -134,7 +140,8 @@ Use `@bgub/fig-dom` for browser rendering:
|
|
|
134
140
|
- `hydrateRoot(container, children, options?)` hydrates server HTML and reports
|
|
135
141
|
recoverable mismatches with `onRecoverableError`.
|
|
136
142
|
- `createPortal(children, container, key?)` renders into external DOM targets.
|
|
137
|
-
- DOM events use `
|
|
143
|
+
- DOM events use `mix={on("click", (event, signal) => ...)}`; arrays compose
|
|
144
|
+
multiple or conditional mixins.
|
|
138
145
|
- DOM node access uses `bind={(node, signal) => ...}`.
|
|
139
146
|
- Raw trusted HTML uses `unsafeHTML="<p>trusted html</p>"`.
|
|
140
147
|
|
|
@@ -169,7 +176,7 @@ function Profile({ id }: { id: string }) {
|
|
|
169
176
|
const user = readData(userResource, id);
|
|
170
177
|
|
|
171
178
|
return (
|
|
172
|
-
<button
|
|
179
|
+
<button mix={on("click", () => void refreshData(userResource, id))}>
|
|
173
180
|
{user.name}
|
|
174
181
|
</button>
|
|
175
182
|
);
|
|
@@ -261,8 +268,24 @@ await save();
|
|
|
261
268
|
data.run(() => refreshData(userResource, "one"));
|
|
262
269
|
```
|
|
263
270
|
|
|
264
|
-
|
|
265
|
-
|
|
271
|
+
Framework adapters can create and populate a root-neutral store before a
|
|
272
|
+
renderer exists, then let exactly one root adopt that same store:
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
const dataStore = createDataStore();
|
|
276
|
+
|
|
277
|
+
await dataStore.ensureData(userResource, "one");
|
|
278
|
+
|
|
279
|
+
const root = createRoot(container, { dataStore });
|
|
280
|
+
root.data === dataStore; // true
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Adoption preserves entry identity and subscriptions. The adopting root owns
|
|
284
|
+
the store lifetime and disposes it when the root unmounts.
|
|
285
|
+
|
|
286
|
+
Client roots accept `dataStore`, `dataPartition`, and `initialData` options.
|
|
287
|
+
`dataStore` is exclusive with the other two options. The partition separates a
|
|
288
|
+
store's internal keyspace without changing public resource keys.
|
|
266
289
|
Loaders receive only their resource arguments plus `{ signal }`; app services,
|
|
267
290
|
request cookies, and auth state belong in the surrounding adapter or closure,
|
|
268
291
|
not in the data store.
|
|
@@ -270,22 +293,30 @@ not in the data store.
|
|
|
270
293
|
### Server Values And Hydration
|
|
271
294
|
|
|
272
295
|
Server renderers expose fulfilled data entries with `getData()`. Pass those
|
|
273
|
-
entries to the client root as `initialData` to hydrate by key
|
|
296
|
+
entries to the client root as `initialData` to hydrate by key, or use
|
|
297
|
+
`createDataStore()` when an adapter needs to populate and render through the
|
|
298
|
+
same live store.
|
|
274
299
|
|
|
275
300
|
For server-only data, split the browser-safe key resource from the server
|
|
276
301
|
loader:
|
|
277
302
|
|
|
278
303
|
```ts
|
|
304
|
+
// user-data.ts
|
|
279
305
|
import { dataResource } from "@bgub/fig";
|
|
280
|
-
import { serverDataResource } from "@bgub/fig/server";
|
|
281
306
|
|
|
282
307
|
export const userKey = (id: string) => ["user", id];
|
|
283
308
|
|
|
284
309
|
export const userResource = dataResource<[string], User>({
|
|
285
310
|
key: userKey,
|
|
286
311
|
});
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
```ts
|
|
315
|
+
// user-data.server.ts
|
|
316
|
+
import { dataResource } from "@bgub/fig";
|
|
317
|
+
import { userKey } from "./user-data.ts";
|
|
287
318
|
|
|
288
|
-
export const userServerResource =
|
|
319
|
+
export const userServerResource = dataResource({
|
|
289
320
|
key: userKey,
|
|
290
321
|
load: async (id, { signal }) => fetchUser(id, signal),
|
|
291
322
|
});
|
|
@@ -297,18 +328,18 @@ resource has no client loader, `refreshData(userResource, id)` resolves with
|
|
|
297
328
|
`{ status: "unsupported", reason: "no-client-loader" }`; revalidation needs a
|
|
298
329
|
payload or framework refresh path.
|
|
299
330
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
331
|
+
Place the loader-backed definition behind the host framework's server-only
|
|
332
|
+
module boundary. Browser code imports only the explicit loader-free resource;
|
|
333
|
+
Fig joins the two definitions by key during hydration without a bundler
|
|
334
|
+
transform. If only server-rendered code reads the value, the single
|
|
335
|
+
loader-backed definition is sufficient.
|
|
303
336
|
|
|
304
337
|
Direct client refreshes of server data are a framework feature, not a core
|
|
305
|
-
data one: an endpoint has to exist to serve them.
|
|
306
|
-
`
|
|
307
|
-
|
|
308
|
-
data
|
|
309
|
-
|
|
310
|
-
make an extra data request either way — data read during the payload render
|
|
311
|
-
streams in that same payload response.
|
|
338
|
+
data one: an endpoint has to exist to serve them. Define an isomorphic
|
|
339
|
+
`dataResource` whose loader calls the framework endpoint; with TanStack Start,
|
|
340
|
+
compose it with `createServerFn`. Server route payload navigations do not make
|
|
341
|
+
an extra data request either way — data read during the payload render streams
|
|
342
|
+
in that same payload response.
|
|
312
343
|
|
|
313
344
|
### Activity, Errors, And DevTools
|
|
314
345
|
|