@farm.js/react 0.1.0-beta.2 → 0.1.0-beta.21

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/README.md CHANGED
@@ -23,7 +23,31 @@ export default defineConfig({
23
23
  ```
24
24
 
25
25
  `compiler: true` uses inference: every application TSX/JSX component is considered, eligible
26
- components are compiled, and everything else keeps the normal React path.
26
+ components are compiled with hybrid reactivity, and everything else keeps the normal React path.
27
+
28
+ Hybrid reactivity starts from the compiler's proven state-to-binding dependency lists, then marks
29
+ only multi-state short-circuit bindings for runtime read tracking. For
30
+ `enabled ? activeValue : inactiveValue`, an update to the inactive value does not schedule the
31
+ binding. The runtime uses direct subscription indexes rather than proxies or a component-wide
32
+ scan. Use `compiler: { reactivity: "static" }` to retain only the complete build-time dependency
33
+ lists for comparison or diagnosis.
34
+
35
+ Compiler output also selects its runtime capabilities at build time. A direct counter imports only
36
+ the cell scheduler and direct-binding core; conditionals, keyed rows and LIS, nested host ranges,
37
+ and component islands are retained only when a compiled component in that module needs them. Plain
38
+ keyed rows omit the larger recursive-host and row-conditional extensions. This selection is
39
+ automatic and does not add a configuration option or asynchronous runtime loading. The legacy
40
+ `createCompiledComponent` export remains a complete compatibility entry for hand-authored
41
+ definitions, while generated code uses the tree-shakable feature entry.
42
+
43
+ Plain keyed lists with filter hints and only identity-based selection also use the single-kind
44
+ target runtime. Filtering keeps its existing native-method checks and fallback; mixed Set/Map
45
+ targets, conditional rows, and nested host blocks keep their existing runtime capabilities.
46
+
47
+ The persisted production-size fixtures and regression gate live in
48
+ [`RUNTIME_SIZE_RESULTS.md`](./RUNTIME_SIZE_RESULTS.md). The recorded compiler-selected core runtime
49
+ premium is 85.4% smaller than the complete compatibility runtime premium; the feature-heavy keyed
50
+ benchmark application removes 6,185 gzip bytes from its previous compiler-on build.
27
51
 
28
52
  For selective adoption, use annotation mode:
29
53
 
@@ -34,6 +58,7 @@ renderer: react({
34
58
  mode: "annotation",
35
59
  directive: "use compiler",
36
60
  onUnsupported: "warn",
61
+ reactivity: "hybrid",
37
62
  },
38
63
  },
39
64
  }),
@@ -56,29 +81,37 @@ The current compiler handles components that it can prove have:
56
81
  - one host-element root and a statically known host-element tree around supported conditional,
57
82
  keyed-list, and React component-island boundaries;
58
83
  - an identifier props parameter or flat object destructuring with aliases and defaults;
84
+ - build-time dependency cells for referenced flat primitive props, with runtime identity guards;
59
85
  - top-level `useState` declarations;
60
86
  - optional compiler-safe derived `const` values declared after state and in source order;
61
87
  - whitelisted `Boolean`, `Number`, `String`, and deterministic `Math` calculations;
62
88
  - optional synchronous `const` or function-declaration handlers used by JSX events;
63
89
  - state-driven text, attributes, per-property inline styles, and controlled form properties;
64
90
  - host-rooted `condition && <element>` and `condition ? <element> : <element>` child blocks at
65
- statically known locations, including supported nested boundaries;
91
+ statically known locations, including direct ranges among static siblings and at the component
92
+ root;
66
93
  - item-keyed `collection.map(...)` children and explicit `List` boundaries at statically known
67
94
  container locations, including supported non-mutating collection pipelines;
68
95
  - inline synchronous events inside otherwise eligible host-only keyed rows;
69
- - row-local logical and ternary host branches inside stable keyed-row containers;
96
+ - multiple and recursively nested logical or ternary host branches inside non-interactive keyed
97
+ rows, including branches beside static host siblings;
70
98
  - stable module-level child components with compiler-safe props;
71
99
  - React-managed event handlers; and
72
100
  - no refs, effects, or unsupported dynamic child structures.
73
101
 
74
- The generated component preserves React ownership of initial placement, props, events, SSR, and
75
- hydration. Local state cells batch updates into a microtask and patch only compiler-known DOM
76
- targets. Proven, dedicated host containers can transfer child ownership after mount for host-only
77
- conditional branches and non-interactive host-only keyed rows. An interactive row or a row with
78
- local conditional slots uses a hybrid boundary instead: React retains its events, conditional
79
- Fibers, and structural reconciliation, while Farm patches same-key bindings and refreshes only
80
- changed row-local branches. React still creates or hydrates all initial DOM. Anything outside those
81
- narrow contracts uses a small React-owned boundary or the complete original component.
102
+ The generated component preserves React ownership of initial placement, parent update commits,
103
+ identity-bearing props, events, SSR, and hydration. Local state cells batch updates into a
104
+ microtask; eligible flat destructured primitive props join the same dependency index after React
105
+ commits their parent update. The runtime skips unchanged binding output and patches only
106
+ compiler-known DOM targets. Objects, arrays, functions, symbols, React elements, `children`, and
107
+ identifier props retain the normal React prop-render path. Proven host containers
108
+ can transfer child ownership after mount for host-only conditional
109
+ branches and ranges, plus non-interactive host-only keyed rows and their recursively nested host
110
+ conditions. An interactive row uses a hybrid boundary instead: React retains its events,
111
+ conditional Fibers, and structural reconciliation, while Farm patches same-key bindings and
112
+ refreshes only changed React-owned row-local branches. React still creates or hydrates all initial
113
+ DOM. Anything outside those narrow contracts uses a small React-owned boundary or the complete
114
+ original component.
82
115
 
83
116
  For a common dedicated conditional, no new component or annotation is required:
84
117
 
@@ -96,6 +129,26 @@ or hydration, a same-branch update patches those bindings without replacing the
96
129
  change creates, removes, or replaces only the selected branch. No marker node is added to SSR output.
97
130
  More complex conditional shapes keep the existing React-owned conditional boundary.
98
131
 
132
+ Eligible direct conditionals can also share a host container with static host siblings:
133
+
134
+ ```tsx
135
+ <article data-update={updates}>
136
+ <header>Status</header>
137
+ {loading && <p>Loading {updates}</p>}
138
+ <section>Stable content</section>
139
+ {enabled ? <strong>Enabled</strong> : <span>Disabled</span>}
140
+ <footer>Actions</footer>
141
+ </article>
142
+ ```
143
+
144
+ That `article` may be the component's exact returned root. Farm adopts the original container and
145
+ counts its direct static elements as anchors for the conditional ranges. Same-branch bindings patch
146
+ in place; branch changes insert, remove, or replace only their range. Multiple changed ranges use
147
+ one state snapshot and reconcile from right to left, including when adjacent ranges are empty. The
148
+ root and every unchanged static sibling keep their DOM identity, and no wrapper or marker is added.
149
+ Branch events, components, fragments, refs, SVG, spreads, nested dynamic blocks, or another
150
+ unsupported direct child keep the container on the React-owned path.
151
+
99
152
  For a common keyed map, no new API is required:
100
153
 
101
154
  ```tsx
@@ -113,6 +166,677 @@ Later list updates patch surviving rows by key, create or remove only the change
113
166
  longest increasing subsequence (LIS) to minimize DOM moves during a reorder. The outer user
114
167
  component and the list callback do not rerun for those compiler-cell updates.
115
168
 
169
+ When a separate state cell or primitive prop is compared with the exact row-key expression using
170
+ `===` or `!==`, the compiler emits a key-directed binding proof. A selection change such as
171
+ `item.id === selectedId` then looks up only the previously selected and newly selected row
172
+ instances. Class, attribute, style, and leaf-text bindings on those rows update without evaluating
173
+ the other keyed instances. The proof requires one reactive target used only in strict comparisons;
174
+ mixed structural dependencies, ambiguous expressions, non-primitive runtime targets, React-owned
175
+ rows, and unsupported shapes keep the complete scan or React fallback. No option is required, and
176
+ the compiler report exposes the emitted binding count as `keyedIdentityTargets`.
177
+
178
+ For multi-selection, an exact local-state expression such as `markedIds.has(item.id)` receives a
179
+ separate Set-membership proof. The runtime compares the previous and next native `Set` snapshots and
180
+ patches only row keys in their symmetric difference. It accepts ordinary native sets containing
181
+ primitive keys. Different fields, extra reactive dependencies, Set subclasses or proxies, object
182
+ members, customized `has` behavior, React-owned rows, nested blocks, and structural dependencies
183
+ stay on the existing complete or React-owned paths. The compiler report exposes this count as
184
+ `keyedMembershipTargets`.
185
+
186
+ For per-row data, an exact local-state expression such as `statusById.get(item.id)` receives a
187
+ separate Map-lookup proof. The runtime compares the previous and next native `Map` snapshots and
188
+ patches only row keys whose mapped primitive value changed. It accepts ordinary native maps with
189
+ primitive keys and primitive or nullish values. Different fields, extra dependencies, Map
190
+ subclasses or proxies, object values, customized `get` behavior, React-owned rows, nested blocks,
191
+ and structural dependencies keep the existing complete or React-owned paths. The compiler report
192
+ exposes this count as `keyedMapLookupTargets`.
193
+
194
+ When a targeted Set or Map is compiler-owned, Farm can also carry the exact mutated keys from a
195
+ proven immutable functional update:
196
+
197
+ ```tsx
198
+ setMarkedIds((current) => {
199
+ const next = new Set(current);
200
+ if (next.has(id)) next.delete(id);
201
+ else next.add(id);
202
+ return next;
203
+ });
204
+
205
+ setStatusById((current) => new Map(current).set(id, "ready"));
206
+ ```
207
+
208
+ The application still creates its normal immutable collection. The generated update records only
209
+ the native `add`, `delete`, or `set` calls that execute, and the runtime validates those keys
210
+ against the previous committed collection. It can then update the relevant keyed rows without
211
+ iterating every old and new Set member or Map entry first. Queued functional updates compose, and
212
+ the runtime periodically compacts its persistent snapshot so a long update chain stays bounded.
213
+
214
+ This proof is deliberately conservative. The state must start from a compiler-owned native
215
+ `Set`/`Map`; every setter use must be a fresh same-kind replacement or a recognized immutable
216
+ updater; and the collection, updater parameter, and cloned draft must not escape or be mutated
217
+ through unknown code. Shadowed constructors, custom collections, object keys, object Map values,
218
+ direct state mutation, aliased drafts, or a failed runtime check use the existing snapshot or
219
+ React-owned path before compiled bindings run. No option or syntax is added. The compiler report
220
+ counts emitted native mutation sites as `keyedCollectionUpdateHints`.
221
+
222
+ For a direct keyed `useState` collection, the compiler also recognizes conservative same-order
223
+ updates such as:
224
+
225
+ ```tsx
226
+ setItems((current) =>
227
+ current
228
+ .map((item) => (item.id === targetId ? { ...item, label: nextLabel } : item))
229
+ .map((item) => (item.id === targetId ? { ...item, selected: !item.selected } : item)),
230
+ );
231
+ ```
232
+
233
+ Farm executes every generated native `map()` and then compares the committed and final item
234
+ identities once. It validates final length, keys, and positions and patches each final changed row
235
+ once; intermediate arrays never receive DOM work. Every user `map()` remains O(n); this removes the
236
+ keyed runtime's second full key-and-binding scan. Queued hints compose. The source method lookup,
237
+ native result, callback order, and thrown errors are preserved.
238
+
239
+ The setter may be concise or use a block containing exactly one value-returning `return`. Extra
240
+ updater statements, conditional returns, and missing returns keep complete keyed reconciliation.
241
+ Each stage requires an inline synchronous conditional mapper that returns the original item on at
242
+ least one path and an object-spread replacement on another. The callback may be a concise expression
243
+ or a structured block containing fully returning `if` or `switch` branches and proven local `const`
244
+ aliases. Each alias needs a simple identifier and compiler-safe initializer. A `switch` needs one
245
+ `default`, a complete return from every case, safe discriminant and case expressions, and no trailing
246
+ statements. Consecutive empty labels may share the next fully returning body; partially executed
247
+ fallthrough and dangling labels are rejected. An unsupported mapper anywhere in the chain disables
248
+ the complete setter hint. Custom methods still execute but record no metadata. Key changes, structural
249
+ edits, sparse or subclassed arrays, relevant mixed dependencies, and failed runtime checks use the
250
+ existing complete reconciliation and LIS path. Non-functional setters, derived collections,
251
+ referenced callbacks, mutable or destructured declarations, effectful initializers, other
252
+ intermediate statements, partial or dangling fallthrough, mutating mappers, and other unproven forms
253
+ are simply not hinted. No new option is required. A compiler report counts prepared map calls as
254
+ `keyedMapUpdateHints`. The separate hint runtime capability is imported only when that count is
255
+ nonzero, so direct-only and ordinary keyed builds do not retain it.
256
+
257
+ The same optional runtime recognizes conservative immutable appends on a direct keyed array:
258
+
259
+ ```tsx
260
+ setItems((current) => [...current, nextItem]);
261
+ setItems((current) => [...current, ...nextItems]);
262
+ setItems((current) => {
263
+ return [...current, ...nextItems];
264
+ });
265
+ ```
266
+
267
+ Because every existing item keeps its key and index, Farm validates the committed source and the
268
+ queued append chain, reads keys and descriptors only for the appended suffix, and mounts that
269
+ suffix in one fragment. Existing row DOM is left untouched. The setter may be concise or use a
270
+ block containing exactly one direct value-returning `return`. Extra statements, conditional
271
+ returns, and missing returns keep complete keyed reconciliation. Native arrays are required;
272
+ middle insertion, removal, direct replacement, duplicate keys, rows that read the collection
273
+ itself, and failed runtime checks use complete keyed reconciliation. A key that reads the
274
+ collection prevents hint emission entirely. The compiler report exposes the emitted-site count as
275
+ `keyedArrayAppendHints`.
276
+
277
+ Adjacent removal and append setters can share the same committed-row proof:
278
+
279
+ ```tsx
280
+ setItems((current) => current.filter((item) => item.id !== expiredId));
281
+ setItems((current) => [...current, incoming]);
282
+
283
+ setItems((current) => current.slice(start, end));
284
+ setItems((current) => [...current, ...incoming]);
285
+
286
+ setItems((current) => current.slice(start, end));
287
+ setItems((current) => [...current, incoming]);
288
+ setItems((current) =>
289
+ current.map((item) => (item.id === targetId ? { ...item, label: nextLabel } : item)),
290
+ );
291
+
292
+ setItems((current) => current.filter((item) => item.id !== expiredId));
293
+ setItems((current) =>
294
+ current.map((item) => (item.id === targetId ? { ...item, label: nextLabel } : item)),
295
+ );
296
+ setItems((current) => [...current, incoming]);
297
+ ```
298
+
299
+ The setters still execute in order and still create their normal native arrays. Before changing the
300
+ DOM, Farm validates the original committed token, every filter or slice survivor, the complete final
301
+ length, and every incoming key. It prepares all incoming keys, descriptors, bindings, and detached
302
+ host rows first; only then does it remove rejected rows, update survivor indexes, and append the new
303
+ suffix in one fragment. Existing survivors are neither rebound nor recreated, and additional
304
+ adjacent append setters are collapsed into the same final suffix. This keeps the unavoidable native
305
+ filter or slice work while removing the keyed runtime's second full scan.
306
+
307
+ One or more immediately adjacent safe same-key `map()` setters may run between the filter or slice
308
+ and the append, after the append, or in both positions. A safe map may also precede the structural
309
+ removal. Farm carries the original survivor indices or retained interval through those native maps
310
+ and records each replacement against its committed row. Before touching the DOM it validates the
311
+ complete dense result, every survivor identity, every changed key and binding, and every incoming
312
+ row. It then removes only rejected rows, patches only changed survivors, and creates the suffix from
313
+ its final values. The owner stays mounted, survivor DOM identity is preserved, and the suffix is
314
+ appended once.
315
+
316
+ Only concise updater results and blocks with one direct value-returning `return` that form one
317
+ direct chain for the same state array are linked. The row and key must be compiler-owned and
318
+ index-independent. A reorder in the structural-append chain, an
319
+ unsupported or non-adjacent map, an unhinted update to that state, another dirty row dependency,
320
+ collection-reading binding, custom, sparse, or subclassed array, nested or React-owned row,
321
+ duplicate final key, or reuse of any committed key in the appended suffix keeps complete React
322
+ reconciliation before a DOM write. A mapped survivor whose key changes also falls back before
323
+ mutation. No API or configuration is added. Compiler reports use the existing
324
+ `keyedArrayFilterHints`, `keyedArraySliceHints`, and `keyedArrayAppendHints` counts, and modules
325
+ without both accepted operations omit the composed runtime.
326
+
327
+ The mirror-image prepend form is supported when the keyed row and key do not read the row index:
328
+
329
+ ```tsx
330
+ setItems((current) => [nextItem, ...current]);
331
+ setItems((current) => [...nextItems, ...current]);
332
+ setItems((current) => {
333
+ return [...nextItems, ...current];
334
+ });
335
+
336
+ setItems((current) => current.filter((item) => item.id !== expiredId));
337
+ setItems((current) => [nextItem, ...current]);
338
+
339
+ setItems((current) => current.slice(start, end));
340
+ setItems((current) => [...nextItems, ...current]);
341
+
342
+ setItems((current) => current.filter((item) => item.id !== expiredId));
343
+ setItems((current) => [nextItem, ...current]);
344
+ setItems((current) =>
345
+ current.map((item) => (item.id === targetId ? { ...item, label: nextLabel } : item)),
346
+ );
347
+
348
+ setItems((current) => current.slice(start, end));
349
+ setItems((current) =>
350
+ current.map((item) => (item.id === targetId ? { ...item, label: nextLabel } : item)),
351
+ );
352
+ setItems((current) => [nextItem, ...current]);
353
+ ```
354
+
355
+ Farm validates the committed source and queued prepend chain, creates only the new prefix, inserts
356
+ it before the first existing row, and shifts the stored indexes used by delegated row events.
357
+ When an adjacent filter or bounded slice runs first, the compiler also carries the exact survivor
358
+ positions into the prepend. The runtime removes only rejected rows, preserves every surviving DOM
359
+ node, and creates only the final prefix instead of rescanning and rebinding the whole result.
360
+ Multiple adjacent prepends share the same proof.
361
+
362
+ The setter may be concise or use a block containing exactly one direct value-returning `return`.
363
+ Extra statements, conditional returns, and missing returns keep complete keyed reconciliation.
364
+
365
+ One or more immediately adjacent safe same-key `map()` setters may run after the prepend or between
366
+ the removal and prepend. A safe map may also run immediately before the removal. Farm carries every
367
+ survivor back to its committed row, validates the complete dense result and every final key, then
368
+ patches only changed survivors and creates the prefix from its final mapped values. A surviving DOM
369
+ node keeps its identity even when its item object changes.
370
+
371
+ Index-aware rows, collection-reading bindings or keys, React-owned or nested row structures,
372
+ middle insertion, direct replacement, a reordered structural chain, an unsupported or non-adjacent
373
+ map, duplicate final keys, reuse of any committed key in the new prefix, custom or sparse arrays,
374
+ and failed validation use complete keyed reconciliation before any DOM mutation. A mapped survivor
375
+ whose key changes also falls back before mutation. The compiler report exposes the emitted-site
376
+ count as `keyedArrayPrependHints`; no option or component API is added, and modules without the
377
+ accepted removal, prepend, and map chain omit its mapped runtime.
378
+
379
+ Native slices with compiler-safe bounds can carry their exact retained interval into the same
380
+ removal runtime:
381
+
382
+ ```tsx
383
+ setItems((current) => current.slice(1_000));
384
+ setItems((current) => current.slice(0, -1_000));
385
+ setItems((current) => current.slice(2, 8));
386
+ setItems((current) => current.slice(trimCount));
387
+ setItems((current) => current.slice(visible.start, visible.end));
388
+ setItems((current) => {
389
+ return current.slice(visible.start, visible.end);
390
+ });
391
+ ```
392
+
393
+ For compiler-owned host rows whose render and key do not read the row index, Farm validates the
394
+ committed source and queued slice chain, preserves every surviving DOM row, and removes only rows
395
+ outside the retained interval. A slice-only chain does not reread surviving keys, descriptors, or
396
+ bindings. One or two safe-integer literals or compiler-safe runtime expressions are required.
397
+ Identifiers, property reads, side-effect-free arithmetic and conditionals, and safe `Math` calls
398
+ are supported. The setter may be concise or use a block containing exactly one direct
399
+ value-returning `return`. Farm preserves native method lookup, argument evaluation, coercion,
400
+ results, and errors, then records metadata only when the evaluated bounds are already safe integers.
401
+ Calls, assignments, updates, unsafe evaluated bounds, updater blocks with extra statements,
402
+ directives, conditional returns, or no value-returning `return`, chained updates, custom slice
403
+ methods, sparse or subclassed arrays, index-aware or collection-reading rows, React-owned structures,
404
+ and failed validation keep complete keyed reconciliation. No option or component is added. The
405
+ compiler report exposes the emitted-site count as `keyedArraySliceHints`.
406
+
407
+ A fixed-size feed can combine that retained tail with a new keyed suffix:
408
+
409
+ ```tsx
410
+ setItems((current) => [...current.slice(1), nextItem]);
411
+ setItems((current) => [...current.slice(1_000), ...nextItems]);
412
+
413
+ const trimCount = pageSize * pagesToExpire;
414
+ setItems((current) => [...current.slice(trimCount), ...nextItems]);
415
+ setItems((current) => {
416
+ return [...current.slice(trimCount), ...nextItems];
417
+ });
418
+ ```
419
+
420
+ Farm executes the ordinary native slice and array construction, then validates the complete hint
421
+ chain back to the committed source, retained item identities, and final incoming keys. Multiple
422
+ rolling setters queued before one compiler flush collapse into one cumulative prefix removal; rows
423
+ introduced by an earlier setter are created only if they remain in the final suffix. Farm removes
424
+ only the expired committed prefix, leaves the retained DOM rows in place, and creates only that
425
+ final incoming suffix. The setter may be concise or use a block containing exactly one direct
426
+ value-returning `return`. This form supports one safe-integer literal or compiler-safe runtime slice
427
+ bound and compiler-owned, index-independent host rows. Identifiers, property reads,
428
+ side-effect-free arithmetic and conditionals, and safe `Math` calls are supported while preserving
429
+ native lookup, evaluation, results, and errors.
430
+
431
+ Immediately adjacent safe same-key maps may run before, after, or between one or more rolling
432
+ setters in the same synchronous setter segment:
433
+
434
+ ```tsx
435
+ setItems((current) =>
436
+ current.map((item) => (item.id === editedId ? { ...item, label: nextLabel } : item)),
437
+ );
438
+ setItems((current) => [...current.slice(trimCount), ...nextItems]);
439
+ setItems((current) =>
440
+ current.map((item) => (item.id === selectedId ? { ...item, selected: true } : item)),
441
+ );
442
+ setItems((current) => [...current.slice(secondTrimCount), ...laterItems]);
443
+ setItems((current) =>
444
+ current.map((item) => (item.id === editedId ? { ...item, status: "ready" } : item)),
445
+ );
446
+ ```
447
+
448
+ Each map may use a compiler-safe nested conditional to update several retained rows.
449
+ The compiler carries mapped survivor identity through the retained tail, creates incoming rows
450
+ from their final mapped values, and patches only changed committed survivors. A single rolling
451
+ setter uses the optional structural append/map runtime. Multiple rolling setters keep their
452
+ cumulative retained-window metadata and mapped provenance through an optional runtime dedicated to
453
+ mapped rolling chains. No new public helper, option, or always-loaded browser code is added.
454
+
455
+ Effectful expressions, reused keys, updater blocks with extra statements, directives, conditional
456
+ returns, or no value-returning `return`, custom slice behavior, collection-reading or index-aware
457
+ rows, nested or React-owned rows, mixed or unhinted queued chains, a statement or unsupported setter
458
+ inside the segment, changed mapped keys, unsafe or no-op evaluated bounds, and failed checks use
459
+ complete keyed reconciliation. Unmapped rolling modules retain the smaller optional all-hint
460
+ runtime. Reports expose each emitted rolling site as `keyedArrayRollingWindowHints`, each rolling
461
+ step retained across a multi-window mapped chain as `keyedArrayMappedRollingWindowChainHints`, and
462
+ each safe map as `keyedMapUpdateHints`.
463
+
464
+ Native known-position updates can avoid a complete keyed scan too:
465
+
466
+ ```tsx
467
+ setItems((current) => current.toSpliced(selectedIndex, 0, nextItem));
468
+ setItems((current) => current.toSpliced(selectedIndex, 0, nextA, nextB));
469
+ setItems((current) => current.toSpliced(selectedIndex, 0, ...incomingItems));
470
+ setItems((current) => current.toSpliced(selectedIndex, 1));
471
+ setItems((current) => current.toSpliced(selectedIndex, 25));
472
+ setItems((current) => current.toSpliced(selectedIndex, 1, replacement));
473
+ setItems((current) => current.toSpliced(selectedIndex, 25, ...replacements));
474
+ setItems((current) => current.with(selectedIndex, replacement));
475
+ setItems((current) => {
476
+ return current.toSpliced(selectedIndex, 0, nextItem);
477
+ });
478
+ setItems((current) => {
479
+ return current.with(selectedIndex, replacement);
480
+ });
481
+
482
+ // Two same-key windows may be queued before one compiler flush.
483
+ setItems((current) => current.toSpliced(firstIndex, 25, ...firstRefresh));
484
+ setItems((current) => current.toSpliced(secondIndex, 25, ...secondRefresh));
485
+ ```
486
+
487
+ The compiler recognizes a concise functional setter or a setter block containing exactly one direct
488
+ `return`. Its position must be either a safe-integer literal or a compiler-safe runtime expression,
489
+ such as an identifier, property read, arithmetic, conditional, or safe `Math` call. The update must insert
490
+ one or more compiler-safe items with zero removals, remove one item or a fixed positive safe-integer
491
+ literal range, replace one item through either `toSpliced()` or `with()`, or replace a fixed positive
492
+ safe-integer literal window with compiler-safe explicit items or a safe spread. Farm preserves method lookup,
493
+ evaluates the position and remaining arguments once in their original order, executes the ordinary
494
+ native call, and records metadata only after validating the committed native source, result, and
495
+ actual safe-integer position and clamped removal count. A batch requires at least two evaluated
496
+ items at runtime. Exact-window replacement supports a safe spread that evaluates to zero, one, or
497
+ many items. A fresh-key window creates every incoming key, descriptor, binding snapshot, and
498
+ detached row before changing the live DOM, rejects key collisions, and mounts the complete batch
499
+ through one document fragment. It then removes only the replaced window. When the incoming window
500
+ has the same length and the same keys in the same order, Farm prepares every binding read and
501
+ changed DOM target across the complete window first, patches the existing rows in place, and
502
+ updates the stored row objects used by later events. That path creates no descriptors or DOM rows,
503
+ and preserves row identity, focus, and selection. Validated same-key row and window refreshes also
504
+ keep the existing row map and element lookup, and skip redundant cache cleanup instead of
505
+ rebuilding whole-list bookkeeping. Source and key validation still run, and each flush commits the
506
+ new collection token for subsequent updates. Structural changes retain index rebuilding and cache
507
+ cleanup. A window may instead grow or shrink while it
508
+ reorders keys from inside its own removed interval and mixes them with globally fresh keys. Farm
509
+ prepares all reused binding updates, new descriptors, binding snapshots, and detached rows before
510
+ the first DOM write. It removes only retired rows, preserves each reused row, batches adjacent new
511
+ rows in a fragment, updates shifted suffix indexes, and applies LIS only to the reused part of that
512
+ interval so it moves the fewest connected rows needed by the local permutation. Rows outside the
513
+ interval are not rerendered or rebound.
514
+ Multiple length-preserving same-key windows
515
+ queued before one compiler flush compose into one atomic refresh. Fixed-length queued windows may
516
+ mix same-key refreshes with globally new final keys, and both disjoint and overlapping windows are
517
+ supported. An overlapping position uses the last queued value; intermediate identities are never
518
+ mounted. Farm validates the complete chain and final key set, then prepares every touched key,
519
+ binding value, DOM target, new descriptor, binding snapshot, and disconnected DOM row before the
520
+ first write. It patches same-key positions and swaps only final fresh-key positions. Untouched rows
521
+ keep their identity and are not recreated. The runtime otherwise creates one inserted row,
522
+ removes only the known row or range, or patches/replaces one row at that position without rereading
523
+ every existing key, descriptor, and binding. Surviving rows keep their DOM identity. Index-aware or
524
+ collection-reading rows, custom methods, position expressions with user calls or mutations,
525
+ runtime positions that are not safe integers, dynamic, zero, negative, or fractional removal
526
+ counts, other `toSpliced()` forms, updater blocks with extra statements, directives, conditional
527
+ returns, or no value-returning `return`, unsafe incoming expressions, queued chains containing a
528
+ structural window or unhinted intermediate update, existing keys moved from outside the removed
529
+ interval, duplicate final keys, nested or React-owned rows, and failed checks use complete keyed
530
+ reconciliation before the fast path mutates the DOM.
531
+ Reports expose the site count as `keyedArrayPositionHints`. Batch insertion and exact-window
532
+ replacement use progressively separate optional runtime capabilities, so existing single-position
533
+ and batch-only bundles do not retain window validation or replacement code.
534
+
535
+ A direct native reverse can carry its complete permutation to a separate optional runtime:
536
+
537
+ ```tsx
538
+ setItems((current) => current.toReversed());
539
+ setItems((current) => {
540
+ return current.toReversed();
541
+ });
542
+ ```
543
+
544
+ Farm recognizes the concise form and a block containing exactly one direct `return`. It preserves
545
+ the method lookup, call result, and errors, then records metadata only for the native method on an
546
+ ordinary array whose chain starts at the committed collection. One direct reverse
547
+ verifies equal lengths and every reversed item identity before moving the existing keyed elements
548
+ with the minimum `n - 1` connected DOM moves. It does not reread row keys, descriptors, or bindings
549
+ and does not run the generic LIS calculation. Consecutive concise native `toReversed()` and
550
+ `toSorted()` setters queued before one flush compose into one final validated permutation. The
551
+ runtime skips intermediate DOM states. When the proven chain contains only reversals, Farm tracks
552
+ their parity relative to the committed rows: an even count validates exact identity and performs
553
+ no DOM moves, source-item map construction, or LIS; an odd count validates exact reverse and uses
554
+ the same minimum `n - 1` moves as one reverse. A chain containing a sort keeps the general final
555
+ permutation and uses LIS once. A concise updater or a block containing exactly one direct
556
+ value-returning `return` may also chain two or more native reorder operations:
557
+
558
+ ```tsx
559
+ setItems((current) => current.toSorted((left, right) => left.rank - right.rank).toReversed());
560
+ setItems((current) => {
561
+ return current.toSorted((left, right) => left.rank - right.rank).toReversed();
562
+ });
563
+ ```
564
+
565
+ Farm evaluates every lookup and call in JavaScript order, carries the same committed token through
566
+ the pipeline, and reconciles only its final result. Index-aware or collection-reading rows,
567
+ arguments to `toReversed()`, referenced comparators, computed methods, updater blocks with extra
568
+ statements, directives, conditional returns, or no returned value, custom methods, sparse or
569
+ subclassed behavior, structural calls after reordering, an unhinted intermediate update, nested or
570
+ React-owned rows, and failed checks keep complete keyed reconciliation.
571
+
572
+ A compiler-safe `filter()` or bounded `slice()` prefix may precede the native reorder suffix in a
573
+ concise updater or exact one-return block. A filter predicate may be concise or contain exactly one
574
+ direct value-returning `return`:
575
+
576
+ ```tsx
577
+ setItems((current) =>
578
+ current
579
+ .filter((item) => {
580
+ return item.visible;
581
+ })
582
+ .toSorted((left, right) => left.rank - right.rank)
583
+ .toReversed(),
584
+ );
585
+ ```
586
+
587
+ Farm validates the complete survivor set and final order before the first DOM write, removes only
588
+ rejected rows, and applies LIS once to the survivors. Concise or exact one-return filter/slice and
589
+ reorder setters also compose when queued in that order before one flush. Unsupported calls or a
590
+ structural call after a reorder fall back. Reports count each compiled step in the existing filter,
591
+ slice, sort, or reverse counter, and modules without those operations omit their optional runtimes.
592
+ Farm does not polyfill `Array.prototype.toReversed`.
593
+
594
+ A compiler-safe same-key map may also appear before or after native reorder steps:
595
+
596
+ ```tsx
597
+ setItems((current) =>
598
+ current
599
+ .map((item) => (item.id === editedId ? { ...item, label: nextLabel } : item))
600
+ .map((item) => (item.id === editedId ? { ...item, rank: nextRank } : item))
601
+ .toSorted((left, right) => left.rank - right.rank),
602
+ );
603
+ ```
604
+
605
+ One callback may select several rows through nested expressions, structured early returns, or a
606
+ fully returning `switch`:
607
+
608
+ ```tsx
609
+ setItems((current) =>
610
+ current.map((item) => {
611
+ const matchesReviewed = item.id === reviewedId;
612
+ if (matchesReviewed) return { ...item, status: "reviewed" };
613
+ const matchesEscalated = item.id === escalatedId;
614
+ if (matchesEscalated) {
615
+ return { ...item, status: "escalated", priority: 1 };
616
+ }
617
+ return item;
618
+ }),
619
+ );
620
+ ```
621
+
622
+ Every condition must be compiler-safe, and every leaf must return the original item or a safe
623
+ object spread of it, with at least one unchanged and one replacement path. Concise expressions and
624
+ blocks made from fully returning `if` or `switch` branches plus local `const` aliases are accepted.
625
+ Each alias must have a simple identifier and compiler-safe initializer. A switch requires one
626
+ `default` and complete returning cases. Consecutive empty labels may share the next body, while
627
+ partial or dangling fallthrough is rejected. Mutable or destructured declarations, effectful
628
+ initializers, other intermediate statements, and ambiguous leaves use complete keyed
629
+ reconciliation. A runtime key change also falls back before any DOM write.
630
+
631
+ The functional setter may be concise or a block containing exactly one direct value-returning
632
+ `return`. Farm executes every native map and reorder call normally. For consecutive accepted maps,
633
+ it checks each native call but compares only the input and final result of that map segment, avoiding
634
+ a full lineage scan for every intermediate array. It verifies the committed token, dense-array
635
+ shape, a one-to-one source-item match, and every final replacement key before touching the DOM, then runs one
636
+ LIS and patches each changed row once. Unchanged rows need no second key or binding read. Queued
637
+ supported map-and-reorder setters compose against the same committed rows. Every accepted map
638
+ requires an inline synchronous conditional mapper whose leaves return either the original item or
639
+ an object-spread replacement, plus index-independent compiler-owned host rows. Concise expressions
640
+ and structured blocks with proven local `const` aliases plus fully returning `if` or `switch`
641
+ branches are supported.
642
+ Changed keys, referenced callbacks, mutable, destructured, or effectful declarations, other
643
+ intermediate statements or partial/dangling fallthrough, unconditional replacements, `thisArg`,
644
+ structural calls in the same chain, computed or custom methods, sparse or subclassed arrays,
645
+ collection-reading bindings, nested or React-owned rows, and failed checks use complete keyed
646
+ reconciliation. Reports use the existing map, sort, and reorder hint counters, and unrelated modules
647
+ do not retain this optional runtime.
648
+
649
+ The maps may also finish the concise pipeline:
650
+
651
+ ```tsx
652
+ setItems((current) =>
653
+ current
654
+ .toReversed()
655
+ .map((item) => (item.id === editedId ? { ...item, label: nextLabel } : item))
656
+ .map((item) => (item.id === editedId ? { ...item, rank: nextRank } : item)),
657
+ );
658
+ ```
659
+
660
+ Farm retains the preceding reorder instead of treating the mapped result as an unhinted snapshot.
661
+ An exact reverse uses the minimum-move reverse path without a general source-item lookup or LIS. A
662
+ sort remains an ambiguous permutation, so Farm performs one validated source lookup and LIS pass.
663
+ Safe maps on both sides of a reorder flatten their replacements back to the same committed rows.
664
+
665
+ Safe maps may also run before, between, or after index-independent `filter` or `slice` steps in one
666
+ concise functional setter or one block with a single direct `return`. A final native reorder is optional:
667
+
668
+ ```tsx
669
+ setItems((current) =>
670
+ current
671
+ .filter((item) => item.visible)
672
+ .map((item) => (item.id === editedId ? { ...item, label: nextLabel } : item))
673
+ .slice(0, limit)
674
+ .toSorted((left, right) => left.rank - right.rank),
675
+ );
676
+
677
+ setItems((current) =>
678
+ current
679
+ .map((item) => (item.id === editedId ? { ...item, label: nextLabel } : item))
680
+ .filter((item) => item.visible)
681
+ .slice(0, limit),
682
+ );
683
+ ```
684
+
685
+ The structural helpers retain both the committed survivor indices and mapped-item sources across
686
+ each accepted step. The final commit removes rejected rows, performs LIS moves only when a reorder
687
+ needs them, and patches bindings only for changed survivors. Every native callback and method still
688
+ runs in JavaScript order.
689
+
690
+ The structural and map steps may also be split across immediately adjacent setter calls in either
691
+ order:
692
+
693
+ ```tsx
694
+ // Map, then structural work.
695
+ setItems((current) =>
696
+ current.map((item) => (item.id === editedId ? { ...item, label: nextLabel } : item)),
697
+ );
698
+ setItems((current) => current.filter((item) => item.visible));
699
+ setItems((current) => current.slice(0, limit));
700
+
701
+ // Structural work, then maps.
702
+ setItems((current) => current.filter((item) => item.visible));
703
+ setItems((current) => current.slice(0, limit));
704
+ setItems((current) =>
705
+ current.map((item) => (item.id === editedId ? { ...item, label: nextLabel } : item)),
706
+ );
707
+ setItems((current) =>
708
+ current.map((item) => (item.id === editedId ? { ...item, rank: nextRank } : item)),
709
+ );
710
+ ```
711
+
712
+ Farm links only consecutive calls to the same setter. A leading map retains its first committed
713
+ source through later structural work; a terminal map consumes the saved survivor lineage from the
714
+ preceding filter or slice. Multiple safe steps in either segment may share the proof, and mixed
715
+ map/structural/map sequences compose. Every result and key is validated before the queued commit
716
+ touches the DOM. An intervening statement, another setter, an unsafe callback, a changed key, or
717
+ failed runtime validation keeps complete keyed reconciliation.
718
+
719
+ The survivor proof may continue through adjacent native reorder setters:
720
+
721
+ ```tsx
722
+ setItems((current) => current.filter((item) => item.visible));
723
+ setItems((current) =>
724
+ current.map((item) => (item.id === editedId ? { ...item, rank: nextRank } : item)),
725
+ );
726
+ setItems((current) => current.toSorted((left, right) => left.rank - right.rank));
727
+ setItems((current) => current.toReversed());
728
+ ```
729
+
730
+ Farm retains the committed survivor positions and same-key replacements until the final queued
731
+ value. The commit removes rejected rows, patches changed survivors, and applies the final order in
732
+ one validated DOM transaction. Exact reverses use the minimum-move reverse path; an arbitrary sort
733
+ uses one source lookup and LIS. Non-adjacent calls, another setter, unsupported syntax, or a failed
734
+ shape/key check keep complete reconciliation.
735
+
736
+ The reorder and safe maps may also be adjacent setter calls in the same synchronous block:
737
+
738
+ ```tsx
739
+ setItems((current) => current.toReversed());
740
+ setItems((current) =>
741
+ current.map((item) => (item.id === editedId ? { ...item, label: nextLabel } : item)),
742
+ );
743
+ setItems((current) =>
744
+ current.map((item) => (item.id === editedId ? { ...item, rank: nextRank } : item)),
745
+ );
746
+ ```
747
+
748
+ The compiler links only consecutive calls to the same setter. It keeps the reorder token through
749
+ each accepted native map, then validates row keys before applying the same exact-reverse or
750
+ sort-permutation path at commit. Adjacent compiler-safe filter or slice work switches to the
751
+ structural proof above. An intervening statement, another setter, an unsupported map, or a changed
752
+ key keeps the ordinary complete fallback. Standalone map-only components do not retain the
753
+ map-and-reorder runtime.
754
+
755
+ The mirror order is supported as well: two or more adjacent concise same-key map setters may be
756
+ followed by a concise native reverse or sort setter. Farm records their replacement lineage from
757
+ the committed collection and carries it into the final reorder. Only adjacent calls to the same
758
+ setter are linked; intervening work and unsupported updates retain complete reconciliation.
759
+
760
+ Mapped lineage also continues through multiple adjacent native reorder setters. For example,
761
+ `map -> reverse -> sort -> reverse` keeps the same committed-row proof until the final value, then
762
+ performs one validated keyed reconciliation. A concise `map().toReversed()` pipeline may start the
763
+ same chain. Adjacent compiler-safe filter or slice work changes it into a structural chain while
764
+ retaining the committed source. Every updater and native method still executes in order; another
765
+ setter, intervening work, or an unsupported method ends the chain and preserves the complete
766
+ fallback.
767
+
768
+ A reverse before the safe map pipeline may be queued in a separate setter:
769
+
770
+ ```tsx
771
+ setItems((current) => current.toReversed());
772
+ setItems((current) =>
773
+ current.map((item) => (item.id === editedId ? { ...item, label: nextLabel } : item)).toReversed(),
774
+ );
775
+ ```
776
+
777
+ A direct `toReversed()` suffix is more specific than an arbitrary permutation. When all preceding
778
+ maps retain committed row order, Farm validates the mirrored source-to-result relation and every
779
+ changed binding before touching the DOM, then performs the minimum-move reverse without building a
780
+ second item lookup map or running LIS. Additional exact reversals toggle that proof between reverse
781
+ and identity. Therefore two reversals after safe maps patch changed rows in committed order without
782
+ moving DOM nodes or creating the generic source map; three use the exact reverse path. Exact reverse
783
+ metadata also composes across queued setters: a queued reverse followed by safe maps and another
784
+ reverse retains exact identity, so only changed row bindings are patched. A preceding sort or any
785
+ ambiguous order keeps the general permutation path.
786
+
787
+ A direct native immutable sort can use the same optional reorder runtime:
788
+
789
+ ```tsx
790
+ setItems((current) => current.toSorted((left, right) => left.rank - right.rank));
791
+ setLabels((current) => current.toSorted());
792
+ setItems((current) => {
793
+ return current.toSorted((left, right) => left.rank - right.rank);
794
+ });
795
+ ```
796
+
797
+ Farm recognizes a concise functional setter or a block containing exactly one direct `return`,
798
+ with no comparator or an inline synchronous comparator from the compiler-safe expression subset.
799
+ It preserves the original method lookup, comparator, stable native result, and errors. After the
800
+ native sort runs, the runtime validates an ordinary dense array whose reorder chain starts at the
801
+ committed collection, equal lengths, and a unique one-to-one item-identity permutation. It then
802
+ uses LIS to move only `n - LIS` keyed DOM nodes without rereading row keys, descriptors, or
803
+ bindings. Multiple concise native sorts and reverses queued in one flush, or chained in one concise
804
+ updater, share the original committed token and reconcile only their final permutation. The native
805
+ sorting work itself is unchanged.
806
+
807
+ Index-aware or collection-reading rows, referenced comparators, updater blocks with extra
808
+ statements, directives, conditional returns, or no returned value, computed or unsupported chained
809
+ calls, custom methods, sparse or subclassed arrays, duplicate item identities, unhinted intermediate
810
+ updates, structural calls after reordering, nested or React-owned rows, and failed checks keep
811
+ complete keyed reconciliation. A compiler-safe filter/slice prefix is supported. Reports count each
812
+ compiled sort step as a `keyedArraySortHints` entry; sort shares the optional reorder runtime, and
813
+ Farm does not polyfill `Array.prototype.toSorted`.
814
+
815
+ Concise or single-return block-bodied immutable filters on a direct keyed array can carry removal
816
+ positions into the same optional runtime:
817
+
818
+ ```tsx
819
+ setItems((current) => current.filter((item) => item.id !== removedId));
820
+ setItems((current) => {
821
+ return current.filter((item) => item.id !== removedId);
822
+ });
823
+ setItems((current) =>
824
+ current.filter((item) => {
825
+ return item.id !== removedId;
826
+ }),
827
+ );
828
+ ```
829
+
830
+ For compiler-owned host rows whose render and key do not observe the row index, Farm validates the
831
+ native filter chain, every surviving item identity, and every surviving key before removing only
832
+ the rejected DOM rows. It does not recreate descriptors or reread bindings for unchanged rows,
833
+ and queued filters compose before one compiler flush. Index-aware rows or predicates, updater blocks
834
+ with extra statements, directives, conditional returns, or no value-returning `return`, predicate
835
+ blocks with those unsupported shapes, custom filter methods, sparse or subclassed arrays,
836
+ collection-reading bindings or keys, React-owned row structures, mixed dirty dependencies, and
837
+ failed validation use complete keyed reconciliation. No option or new component is required. The
838
+ compiler report exposes the emitted-site count as `keyedArrayFilterHints`.
839
+
116
840
  An otherwise eligible host row may also contain an inline synchronous React event:
117
841
 
118
842
  ```tsx
@@ -134,7 +858,7 @@ insert, removal, or reorder asks React to reconcile the rows, adopts the committ
134
858
  reapplies current bindings, and then resumes direct same-key patches. This avoids creating eventful
135
859
  DOM outside React's Fiber tree.
136
860
 
137
- A keyed row may also contain dedicated conditional slots:
861
+ A non-interactive keyed row may also transfer its safe host conditions to the same keyed instance:
138
862
 
139
863
  ```tsx
140
864
  <ul>
@@ -142,7 +866,16 @@ A keyed row may also contain dedicated conditional slots:
142
866
  <li key={item.id}>
143
867
  <span>{item.label}</span>
144
868
  <div className="status-slot">
145
- {item.done ? <strong>{item.label} complete</strong> : <span>In progress</span>}
869
+ <i>State</i>
870
+ {item.done ? (
871
+ <article>
872
+ <strong>{item.label} complete</strong>
873
+ <div>{item.details && <small>{item.description}</small>}</div>
874
+ </article>
875
+ ) : (
876
+ <span>In progress</span>
877
+ )}
878
+ <b>Prepared</b>
146
879
  </div>
147
880
  <section className="details-slot">{item.expanded && <p>{item.description}</p>}</section>
148
881
  </li>
@@ -150,13 +883,59 @@ A keyed row may also contain dedicated conditional slots:
150
883
  </ul>
151
884
  ```
152
885
 
153
- Each conditional must be the only meaningful child of its persistent lowercase host container.
154
- The compiler records its test and branch bindings per row key. A same-key collection update compares
155
- those prepared snapshots and asks React to render only the conditional boundaries whose selected
156
- branch or branch values changed. Other conditional rows and the outer map stay untouched. Inserts,
157
- removals, and reorders remain React structural commits for these rows, after which Farm re-adopts
158
- the host skeleton and resumes row-local updates. This keeps empty branches, SSR, hydration,
159
- recoverable hydration errors, error boundaries, and Fast Refresh inside React's ownership.
886
+ The compiler prepares the row descriptor, static sibling positions, branch factories, and
887
+ text/attribute/style bindings at build time. React creates or hydrates the initial list. After
888
+ mount, a same-key update patches the active branch in place or mounts, replaces, or removes only the
889
+ changed branch. Multiple and deeper host-only conditions share that row scope. Insertions and
890
+ removals create or clean one row scope, while reorders use the normal LIS pass. The component body
891
+ and map callback do not rerun, and no wrapper or hydration marker is added.
892
+
893
+ This compiler-owned tier requires a lowercase non-interactive host row. Hooks, custom components,
894
+ branch events, refs, SVG, fragments, spreads, dangerous HTML, controlled inputs inside a condition,
895
+ and unproven expressions stay on React. An otherwise eligible row with inline events continues to
896
+ use the hybrid boundary: React owns its conditional Fibers and structural commits, while Farm
897
+ compares per-key snapshots and refreshes only changed slots. Duplicate runtime keys or invalid
898
+ adopted DOM switch the complete list to React.
899
+
900
+ An eligible outer keyed row may also own nested keyed ranges:
901
+
902
+ ```tsx
903
+ <div>
904
+ {projects.map((project) => (
905
+ <section key={project.id}>
906
+ <h2>{project.name}</h2>
907
+ <ul>
908
+ <li>Tasks</li>
909
+ {project.tasks.map((task) => (
910
+ <li key={task.id}>{task.title}</li>
911
+ ))}
912
+ <li>End</li>
913
+ </ul>
914
+ </section>
915
+ ))}
916
+ </div>
917
+ ```
918
+
919
+ Every outer key receives an isolated inner key table and LIS pass. Moving a project preserves its
920
+ task scope; reordering one task list touches only that project. Surviving project, task, and static
921
+ sibling elements keep their DOM identity, and removing an outer row cleans its inner scope. The
922
+ initial contract accepts non-interactive lowercase inner rows from `.map(...)` or inline `List`,
923
+ including multiple ranges separated by static host siblings. Inner events or forms, Hooks, custom
924
+ components, fragments, refs, SVG, spreads, dangerous HTML, and index keys keep the complete outer
925
+ row on React's fallback. Additional keyed levels that satisfy the same host-only contract are
926
+ analyzed recursively instead of using a fixed nesting limit.
927
+
928
+ When every inner key stays in the same order, the nested scope reuses its row table and skips
929
+ position-map construction and LIS planning. It still validates all keys and updates every child
930
+ scope and binding. Insertions, removals, and reorders keep the existing structural reconciliation;
931
+ duplicate keys or an invalid child scope keep the existing React fallback. This also applies to
932
+ keyed ranges next to host-only conditions inside a nested mixed range.
933
+
934
+ For example, `boards → columns → cards → tags` becomes a tree of keyed scopes. Each stable parent
935
+ key owns its child key table and LIS pass. Moving a board carries its column, card, and tag scopes;
936
+ reordering cards reconciles only the selected column. Block IDs stay globally unique while runtime
937
+ instances are isolated by their complete parent-key path. Removing any parent cleans every
938
+ descendant subscription before its DOM leaves the document.
160
939
 
161
940
  A keyed collection may use derived locals or an inline chain of `filter`, `slice`, `toSorted`, and
162
941
  `toReversed`:
@@ -190,10 +969,9 @@ the TypeScript `lib` with ES2023 and target a runtime that supports them when us
190
969
 
191
970
  React remains the fallback and compatibility boundary. An interactive map beside static children,
192
971
  a non-host sibling between ranges, a row with a non-inline or async handler, a file input or dynamic
193
- form control shape, a fragment, a ref, a custom component, or a conditional mixed directly beside
194
- other children in the same host slot uses the existing React-owned keyed boundary. The outer
195
- compiled component can still be skipped, but React reconciles that list's rows and owns their
196
- events, lifecycle, and state.
972
+ form control shape, a fragment, a ref, a custom component, or any unsupported nested dynamic
973
+ structure uses the existing React-owned keyed boundary. The outer compiled component can still be
974
+ skipped, but React reconciles that list's rows and owns their events, lifecycle, and state.
197
975
 
198
976
  Non-interactive host maps can share a nested or component-root container with static host siblings:
199
977
 
@@ -290,23 +1068,41 @@ Generated callback refs give directly patched host elements stable private targe
290
1068
  React-owned component may therefore return `null`, a fragment, or multiple host nodes without
291
1069
  shifting an unrelated compiler binding. These refs do not add attributes to SSR output.
292
1070
 
293
- Conditional blocks have two safe ownership levels. A dedicated container with exactly one proven
294
- host-only conditional child can use compiler-owned branch instances. Events, keys, custom
295
- components, fragments, refs, SVG, attribute spreads, `dangerouslySetInnerHTML`, nested dynamic
296
- blocks, and static siblings in that container keep React ownership. The more general React-owned
297
- conditional path still accepts supported nested conditionals, keyed lists, and component islands.
298
- An empty ternary branch may be `null` or `false`. The inactive branch is described at build time but
299
- is never pre-mounted or cached. If a logical `&&` evaluates to a number such as `0`, the runtime also
300
- falls back to React so JavaScript and React rendering semantics remain exact.
1071
+ Conditional blocks have three safe ownership levels. One or more proven host-only direct branches
1072
+ may become compiler-owned ranges among static host siblings, including at the exact component root.
1073
+ A dedicated container with exactly one conditional child uses the smaller compiler-owned branch
1074
+ boundary. The more general React-owned conditional path accepts supported events, nested host
1075
+ conditionals, keyed lists, and component islands. A compiler-owned host branch may now recursively
1076
+ contain eligible host-only conditional ranges and non-interactive keyed ranges in nested host
1077
+ containers. Each nested range receives its own dependency entry and updates without rerunning or
1078
+ replacing the outer branch. When conditionals and keyed maps/`List` ranges are direct siblings in
1079
+ one safe host container, the compiler emits one ordered mixed-range controller. It mounts or removes
1080
+ conditional branches, applies independent LIS reconciliation to each keyed range, and preserves
1081
+ static siblings in place. Stateful leaf text, attributes, `className`, and individual inline style
1082
+ properties on those siblings are compiled into stable segment/sibling/path bindings. Their address
1083
+ does not shift when a nearby condition mounts or a keyed range inserts, removes, or reorders rows,
1084
+ so the same controller patches them from the current state snapshot without replacing the sibling
1085
+ or rerunning the owner. The same mixed layout may recurse inside safe branches and keyed rows.
1086
+ Keys on branches, custom components, fragments, refs, SVG, attribute spreads,
1087
+ `dangerouslySetInnerHTML`, branch events, interactive rows, and unsupported mixed children keep
1088
+ React ownership. An empty ternary branch may be `null` or `false`. The
1089
+ inactive branch is described at build time but is never pre-mounted or cached. If a logical `&&`
1090
+ evaluates to a number such as `0`,
1091
+ or adopted DOM no longer matches its descriptor, the affected container remounts through React so
1092
+ JavaScript, hydration, and React rendering semantics remain exact.
301
1093
 
302
1094
  All component-level boundary types share one block graph and one ID sequence. A nested
303
1095
  binding records its nearest conditional parent. If one state flush affects both an outer
304
1096
  conditional and its descendants, the runtime refreshes the mounted outer boundary once and skips
305
- the redundant descendant refreshes. React unmounts inner boundaries normally, their subscriptions
306
- are removed, and a later remount reads the latest compiler-cell values. Host-only keyed rows have
307
- separate runtime instances per key. Row-local conditional IDs are scoped to that instance and
308
- paired with its stable key, so two rows can use the same build-time slot ID without sharing data or
309
- subscriptions. Unsupported row subtrees stay complete React-owned keyed boundaries.
1097
+ the redundant descendant refreshes. React-owned inner boundaries unmount normally. Compiler-owned
1098
+ host scopes explicitly remove every nested subscription before their outer DOM is removed, and a
1099
+ later mount reads the latest compiler-cell values. Surviving keyed rows move with the existing LIS
1100
+ algorithm. Duplicate runtime keys, invalid adoption, or an unsafe logical value transfer the outer
1101
+ container back to React; descendant dependencies remain subscribed so that fallback stays live.
1102
+ Host-only keyed rows have separate runtime instances per key. Row-local conditional and keyed-range
1103
+ IDs are scoped to that instance and paired with its stable key, so two rows can use the same
1104
+ build-time slot ID without sharing data or subscriptions. Unsupported row subtrees stay complete
1105
+ React-owned keyed boundaries.
310
1106
 
311
1107
  Unsupported components fall back to React by default. Use `onUnsupported: "warn"` for diagnostics
312
1108
  or `onUnsupported: "error"` while tightening an annotated migration.
@@ -323,7 +1119,29 @@ compiler: {
323
1119
 
324
1120
  The default path is `.farm/react-compiler.json`. The report covers the production browser graph and
325
1121
  contains project-relative module paths, compiled component names, fallback details, and fallback
326
- reasons aggregated by count. A custom project-relative `reportFile` also enables reporting.
1122
+ reasons aggregated by count. Its summary and per-module `optimizations` also report
1123
+ `keyedIdentityTargets`, the number of scalar key-directed row bindings;
1124
+ `keyedMapLookupTargets`, the number of native-Map keyed lookup bindings;
1125
+ `keyedMembershipTargets`, the number of native-Set membership bindings;
1126
+ `keyedCollectionUpdateHints`, the number of compiler-proven native Set/Map mutation sites; and
1127
+ `keyedMapUpdateHints`, the number of compiler-proven keyed `map()` update calls; and
1128
+ `keyedArrayAppendHints`, the number of compiler-proven direct keyed-array append sites; and
1129
+ `keyedArrayFilterHints`, the number of compiler-proven keyed-array filter sites, including supported
1130
+ structural reorder pipelines; and
1131
+ `keyedArrayPrependHints`, the number of compiler-proven direct keyed-array prepend sites; and
1132
+ `keyedArrayPositionHints`, the number of compiler-proven native exact-position insertion, single or
1133
+ contiguous-range removal, single-row replacement, or exact-window replacement sites, including
1134
+ guarded compiler-safe runtime positions;
1135
+ and
1136
+ `keyedArrayReorderHints`, the number of compiler-proven native keyed-array reverse steps; and
1137
+ `keyedArraySortHints`, the number of compiler-proven native keyed-array sort steps; and
1138
+ `keyedArrayMappedRollingWindowChainHints`, the number of compiler-proven rolling steps retained
1139
+ across multi-window same-key map chains; and
1140
+ `keyedArrayRollingWindowHints`, the number of compiler-proven retained-tail plus incoming-suffix
1141
+ sites; and
1142
+ `keyedArraySliceHints`, the number of compiler-proven keyed-array slice sites with literal or guarded
1143
+ compiler-safe runtime bounds, including supported structural reorder pipelines. A custom
1144
+ project-relative `reportFile` also enables reporting.
327
1145
 
328
1146
  The runtime test compares the same counter interaction on both paths: ordinary React performs a
329
1147
  second component render and commit, while the compiled component remains at one render and one
@@ -331,11 +1149,15 @@ commit and updates its two bindings directly. This is a deterministic structural
331
1149
  assertion; it is not presented as a cross-machine timing benchmark.
332
1150
 
333
1151
  Application and prototype calls, dynamic style objects, handlers outside JSX events, nested,
334
- computed, and rest props patterns, async handlers, unkeyed or index-keyed lists, chained maps,
335
- unsupported conditional roots, effects, and more advanced hook support intentionally stay on React
336
- in this release. Compiler-owned keyed rows support either one dedicated host-only map/`List` or
337
- one or more non-interactive ranges in a nested or component-root host container. Inline synchronous
338
- events and dedicated row-local host conditional slots can use the single-list hybrid path, but
339
- branch events, interactive ranges beside siblings, nested dynamic blocks, conditionals mixed with
340
- siblings in one slot, non-inline or async row handlers, unsupported form shapes, custom components,
341
- fragments, refs, SVG, and duplicate runtime keys keep or switch to React ownership.
1152
+ computed, and rest props patterns, async handlers, unkeyed or index-keyed lists, unsupported map
1153
+ chains, unsupported conditional roots, effects, and more advanced hook support intentionally stay
1154
+ on React in this release. Compiler-owned keyed rows support either one dedicated host-only map/`List` or
1155
+ one or more non-interactive ranges in a nested or component-root host container. A dedicated
1156
+ non-interactive row may also contain multiple recursive logical or ternary host branches beside
1157
+ stateful static host siblings, plus recursively nested non-interactive keyed ranges scoped by every
1158
+ stable parent key. Static sibling bindings support safe leaf text, attributes, `className`, and
1159
+ individual style properties; lifecycle-sensitive shapes continue to fall back. Inline synchronous
1160
+ row events continue to use the hybrid React-owned row path. Branch
1161
+ or nested-row events, interactive ranges beside siblings, non-inline or async row handlers,
1162
+ unsupported form shapes, custom components, fragments, refs, SVG, and duplicate runtime keys keep
1163
+ or switch to React ownership.