@farm.js/react 0.1.0-beta.1 → 0.1.0-beta.10

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