@kudzujs/core 0.7.6 → 0.7.9
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/GOAL_A.md +75 -146
- package/GOAL_B.md +56 -92
- package/README.md +18 -13
- package/RELEASES.md +73 -0
- package/framework/README.md +3 -3
- package/framework/build.mjs +54 -26
- package/framework/collection-selector.js +23 -19
- package/framework/core.d.ts +2 -2
- package/framework/core.mjs +17 -5
- package/framework/dev-state.js +3 -3
- package/framework/list-runtime.js +123 -18
- package/package.json +1 -1
package/GOAL_A.md
CHANGED
|
@@ -1,187 +1,116 @@
|
|
|
1
|
-
# Goal A: Static
|
|
1
|
+
# Goal A: Static Application Lifecycles And Opt-In Navigation
|
|
2
2
|
|
|
3
|
-
Goal A
|
|
3
|
+
Goal A is a completed compiler-capability milestone for general React-to-static migration. It proves that ordinary React-shaped applications can retain shared layouts, route lifetimes, asynchronous workflows, and optional same-document navigation while every route remains a complete static document.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The six-route commerce journey is one executable validation fixture. It is not Kudzu's product scope, a commerce API, or the priority model for future development. [`MIGRATION_ROADMAP.md`](./MIGRATION_ROADMAP.md) is the source of truth for product direction and future fixture-driven work.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Status
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Goal A is complete for explicitly configured emitted-route groups with shared layouts. Implemented capabilities include:
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
- complete standalone documents for exact and runtime-parameter routes;
|
|
12
|
+
- native navigation fallback and opt-in same-document navigation;
|
|
13
|
+
- persistent layout state and effects with disposable route state and effects;
|
|
14
|
+
- conditional and keyed DOM ownership within layout and route lifetimes;
|
|
15
|
+
- validated finite prefetch caching for eligible links;
|
|
16
|
+
- optimistic success, rejection, rollback, and stale-write suppression;
|
|
17
|
+
- multiple independent shared-layout groups with overlap diagnostics.
|
|
12
18
|
|
|
13
|
-
|
|
14
|
-
- **Phase 2 complete**: effects inside conditional ranges and supported keyed row components mount with their DOM owner, unsubscribe and clean up on removal, and remount without affecting effect-free output.
|
|
15
|
-
- **Phase 3 complete**: page-exported layouts render complete documents with compiler-owned route boundaries, collision-free layout/route IDs, and state/effect ownership metadata. Effects remain document effects and client navigation is unchanged.
|
|
16
|
-
- **Phase 4 route/layout effects implemented**: layout effects mount once per document session, route effects remount after awaited route cleanup, and primitive dependency subscriptions exist only while their lifetime is mounted. Conditional/keyed effects use per-lifetime owner registries; route registries are fresh on cached revisits. Direct primitive keyed-item properties rerun only changed rows; reorder does not rerun and key changes remount. Disposed effect setters and queued commits are inactive.
|
|
17
|
-
- **Phase 4 document prefetch implemented**: visible, near-visible, hovered, or focused eligible group anchors prefetch and validate complete documents without importing target capabilities. The finite in-memory full-URL cache removes the measured product-cart HTML RTT while preserving retry and native fallback.
|
|
18
|
-
- **Phase 5 complete**: matched async cart success/rejection flows prove immediate optimistic updates, duplicate prevention, accessible errors, rollback, route-local reset, and stale-write suppression without new framework APIs.
|
|
19
|
-
- **Phase 6 expansion probe complete**: one layout-owned mock `EventTarget` stream and one route-owned imperative chart stub use existing effects and a relative TypeScript helper across repeated navigation, with exact listener and disposal assertions. This proves only the compatibility seam; Kudzu does not provide telemetry or chart support.
|
|
20
|
-
- **Desktop and mobile gates complete**: both profiles use seven rotating fresh Chrome runs per target and record zero cold/warm CLS.
|
|
19
|
+
Future migration features do not extend Goal A as a product vertical. They return to the migration roadmap and begin with a failing conventional React fixture.
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
## Capability Contract
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
The post-Goal-A runtime-pattern matcher increases the exact-only commerce navigation asset from 2,306 B to 2,461 B gzip. The mixed exact/runtime navigation fixture emits a 3,105 B gzip navigation asset and a 703 B gzip cache-safe parameter initializer.
|
|
27
|
-
|
|
28
|
-
The post-Goal-A multiple-group fixture emits a 7,448 B raw / 3,099 B gzip (`gzip -9`) mixed runtime/effect group asset, including one native exclusion for an overlapping ungrouped exact route, and a separately specialized 5,681 B raw / 2,448 B gzip exact effect-free group asset. These measurements do not revise the historical Goal A benchmark.
|
|
29
|
-
|
|
30
|
-
In a matched one-effect navigation build with the same conditional capability, moving the effect from the route body into the conditional owner changes the route effect entry from 2,181 B raw / 1,036 B gzip to 4,135 B raw / 1,807 B gzip (`+1,954 B` raw / `+771 B` gzip). Owner-hook unsubscription changes the shared runtime from 1,347 B raw / 718 B gzip to 1,459 B raw / 732 B gzip (`+112 B` raw / `+14 B` gzip). Top-level-only and effect-free navigation builds retain their smaller generators.
|
|
31
|
-
|
|
32
|
-
In matched state-only and item-property keyed-row builds, targeted notification adds 821 B raw / 255 B gzip across the route effect entry, shared runtime, and list runtime. Builds without item-property dependencies retain their previous generated path.
|
|
33
|
-
|
|
34
|
-
In the latest matched 1,000-row keyed-effect runtime microbenchmark, Kudzu measured 3.6 ms selected-row cleanup/update/setup, 2.4 ms unrelated-field update, and 8.8 ms reorder after all rows and effects were ready. React CSR measured 9.8, 5.9, and 16.8 ms; Vue measured 5.8, 2.4, and 10.5 ms; and Svelte measured 5.7, 4.1, and 58.1 ms. Targeted changed-root notification avoids an extra O(n) effect-record scan; list reconciliation remains O(n). Kudzu emits initial rows while these framework fixtures are CSR, so their JavaScript, output, and build observations are not architecture-equivalent claims.
|
|
35
|
-
|
|
36
|
-
A 0.6.4 release-tree desktop rerun of the matched six-route commerce fixture measured Kudzu at 486.8 ms build, 35,355 deploy bytes, 7,334 B gzip product JavaScript, 332/156 ms cold/warm LCP, 122.6 ms startup task, 4.8 ms interaction, and 5.6 ms product-cart navigation. React measured 545.4 ms build, 61,464 B gzip product JavaScript, 332/264 ms LCP, 179.9 ms startup task, 10.2 ms interaction, and 9.9 ms navigation. Kudzu built 10.7% faster and its top-level-only fixture retained byte-identical deploy, product-graph, and navigation-asset sizes after navigation-owned effects were added. Raw arrays and the consolidated report are retained under the local demo benchmark workspace.
|
|
37
|
-
|
|
38
|
-
The Phase 6 chart probe's complete initial module graph is 11,902 B raw / 5,331 B gzip. It adds no framework API or package and does not change the commerce benchmark fixture.
|
|
39
|
-
|
|
40
|
-
The app-mode Kudzu fixture emits 34,879 deploy bytes. Its product route loads 15,800 raw / 7,215 gzip bytes of initial JavaScript, including the 2,306 B gzip navigation capability. Routes outside a configured application group retain the static zero-JavaScript and byte-for-byte gates.
|
|
41
|
-
|
|
42
|
-
The latest matched run used seven rotating production builds and seven rotating fresh Chrome profiles per target at 4x CPU slowdown, 100 ms latency, and 200 KiB/s download. Raw local results retain the full arrays, artifact groups, environment, and limitations.
|
|
43
|
-
|
|
44
|
-
| Target | Product JS gzip | Cold transfer | Cold LCP | Warm LCP | Cold task | Cold heap | Interaction | Product-cart |
|
|
45
|
-
|---|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
46
|
-
| Kudzu | 7,215 B | 34,809 B | 324 ms | 140 ms | 103.3 ms | 648,844 B | 3.7 ms | 5.7 ms |
|
|
47
|
-
| React + Vite | 61,464 B | 202,842 B | 324 ms | 232 ms | 160.0 ms | 1,062,520 B | 10.5 ms | 8.3 ms |
|
|
48
|
-
| Next.js | 190,090 B | 546,581 B | 316 ms | 156 ms | 357.7 ms | 2,157,020 B | 11.4 ms | 26.9 ms |
|
|
49
|
-
| Nuxt | 67,620 B | 195,953 B | 320 ms | 204 ms | 205.1 ms | 1,721,348 B | 3.9 ms | 33.0 ms |
|
|
50
|
-
| SvelteKit | 32,473 B | 90,929 B | 340 ms | 172 ms | 125.8 ms | 999,496 B | 4.7 ms | 20.3 ms |
|
|
51
|
-
|
|
52
|
-
The navigation capability initially measured 128.7 ms because it paid one HTML round trip. Validated near-viewport document prefetch reduced the final seven-run median to 5.7 ms while Kudzu still transferred 62% less than SvelteKit and 83% less than React on the cold product load. A progressive-HTML grid shift found by the benchmark was fixed in the matched CSS; 30 focused Kudzu profiles and the final desktop/mobile cross-framework runs then recorded zero cold and warm CLS.
|
|
53
|
-
|
|
54
|
-
The original final table measured 460.8 ms for Kudzu and 429.5 ms for React, and a focused follow-up confirmed a 6-7% loss primarily from TypeScript ESM/compiler startup. A post-gate startup fix now enables Node's native module compile cache before lazily loading the compiler. The first empty-cache build measured 541.4 ms against the previous 547.6 ms path; after one warm-up, 31 interleaved artifact-clean builds measured Kudzu at 425.8 ms and React at 483.4 ms, making Kudzu 11.9% faster in that run. Cache-disabled and cached builds emit byte-for-byte identical output.
|
|
55
|
-
|
|
56
|
-
The mobile profile uses a 390x844 viewport, 6x CPU slowdown, 150 ms latency, and 150 KiB/s download. Kudzu measured 420 ms cold LCP, 220 ms warm LCP, 5.6 ms interaction, 8.7 ms product-cart navigation, 4.1 ms optimistic rejection, and 158 ms rollback/error.
|
|
57
|
-
|
|
58
|
-
## Product Target
|
|
59
|
-
|
|
60
|
-
A user can build and deploy this flow without React, a VDOM, hydration, or a request server:
|
|
23
|
+
A migrated application may use familiar function components, props, children, JSX, hooks, conditions, collections, and handlers while Kudzu emits:
|
|
61
24
|
|
|
62
25
|
```text
|
|
63
|
-
|
|
26
|
+
complete static document
|
|
27
|
+
-> optional persistent layout range
|
|
28
|
+
-> replaceable route range
|
|
29
|
+
-> only the route-specific ESM capabilities used
|
|
64
30
|
```
|
|
65
31
|
|
|
66
|
-
The
|
|
32
|
+
The capability must preserve:
|
|
67
33
|
|
|
68
|
-
- complete indexable HTML for
|
|
34
|
+
- complete indexable HTML for every route;
|
|
69
35
|
- direct entry, reload, back, forward, query strings, and base paths;
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
- optimistic cart updates with rollback after a rejected request;
|
|
75
|
-
- accessible focus, title, scroll, and announcement behavior after navigation;
|
|
36
|
+
- URL, route, and shared-layout state with explicit lifetimes;
|
|
37
|
+
- async loading, empty, error, retry, cancellation, and stale-result handling;
|
|
38
|
+
- optimistic updates, rejection, rollback, and accessible status output;
|
|
39
|
+
- focus, title, scroll, and announcement behavior after enhanced navigation;
|
|
76
40
|
- ordinary document navigation when JavaScript is absent, late, or fails.
|
|
77
41
|
|
|
78
|
-
|
|
42
|
+
## Invariants
|
|
79
43
|
|
|
80
|
-
|
|
44
|
+
- Native `<a>` navigation is the default.
|
|
45
|
+
- Navigation enhancement is explicit per emitted route group.
|
|
46
|
+
- Every enhanced route also exists as a complete document.
|
|
47
|
+
- Static routes emit zero client JavaScript.
|
|
48
|
+
- Interactive routes emit only the capabilities they use.
|
|
49
|
+
- React, a VDOM, hydration, retained browser components, and browser component rerenders remain forbidden.
|
|
50
|
+
- External, download, modified-click, reload, malformed, unsupported, and ungrouped links keep native behavior.
|
|
51
|
+
- A failed or superseded transition cannot commit stale route state.
|
|
52
|
+
- No separate fragment protocol, router API, cache, or scheduler is added without a measured fixture requirement.
|
|
81
53
|
|
|
82
|
-
|
|
54
|
+
## Ownership Model
|
|
83
55
|
|
|
84
|
-
|
|
85
|
-
Static document
|
|
86
|
-
-> optional persistent layout
|
|
87
|
-
-> replaceable route range
|
|
88
|
-
-> capability-specific route ESM
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
The implementation must use the smallest measured mechanism. A separate fragment format, prefetcher, cache, or router API is not required unless the fixture and browser measurements show that parsing the existing document output is insufficient.
|
|
92
|
-
|
|
93
|
-
Navigation enhancement must not become a general SPA runtime:
|
|
56
|
+
Goal A adds explicit ownership, not a state library or application runtime.
|
|
94
57
|
|
|
95
|
-
|
|
96
|
-
- no global router on routes outside an opted-in application group;
|
|
97
|
-
- no interception of external, download, modified-click, reload, or explicitly native links;
|
|
98
|
-
- no client-only route as the sole representation of indexable content;
|
|
99
|
-
- no duplicated source component execution in the browser.
|
|
100
|
-
|
|
101
|
-
## Ownership And Cleanup
|
|
102
|
-
|
|
103
|
-
Goal A needs explicit ownership, not a new state library:
|
|
104
|
-
|
|
105
|
-
| Owner | Lifetime | Examples |
|
|
58
|
+
| Owner | Lifetime | General examples |
|
|
106
59
|
|---|---|---|
|
|
107
|
-
| Document | Full document | global listeners and document
|
|
108
|
-
| Layout | Enhanced navigation session | user summary,
|
|
109
|
-
| Route | Current route range | filters,
|
|
110
|
-
| DOM range | Conditional or keyed item | listener, effect, imperative child resource |
|
|
111
|
-
|
|
112
|
-
Removing an owner must dispose its effects, listeners, pending work, and descendant registrations exactly once. A failed or superseded navigation must not commit stale route state. Full document navigation remains the recovery path.
|
|
60
|
+
| Document | Full document | global listeners and document-exit cleanup |
|
|
61
|
+
| Layout | Enhanced navigation session | user summary, navigation state, shared resource |
|
|
62
|
+
| Route | Current route range | filters, requests, subscriptions, pending work |
|
|
63
|
+
| DOM range | Conditional or keyed item | listener, effect, ref, imperative child resource |
|
|
113
64
|
|
|
114
|
-
|
|
65
|
+
Removing an owner disposes its effects, listeners, pending work, and descendant registrations exactly once. A non-persisted document exit cleans route ownership before layout ownership. A BFCache-persisted document retains live ownership for restoration.
|
|
115
66
|
|
|
116
|
-
##
|
|
67
|
+
## Validation Fixture
|
|
117
68
|
|
|
118
|
-
|
|
69
|
+
The reduced commerce journey remains a useful integration fixture because it exercises the neutral capability contract in one flow:
|
|
119
70
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
4. **Opt-in navigation**: support eligible links, history, aborts, stale responses, focus, scroll, metadata, and native fallback. **Complete for emitted exact/runtime-parameter routes and layout/route effects, including conditional/keyed DOM ownership.**
|
|
124
|
-
5. **Business workflows**: close only fixture-proven gaps in forms, async requests, optimistic updates, and diagnostics. **Complete for the matched cart success/rejection flow.**
|
|
125
|
-
6. **Expansion probe**: prove that one persistent mock stream and one imperative chart stub can mount, update, navigate, and dispose without adding a component runtime. **Compatibility probe complete; real telemetry and chart engines remain outside Goal A.**
|
|
126
|
-
|
|
127
|
-
Do not begin a later phase while the current phase has an unexplained correctness or performance regression.
|
|
71
|
+
```text
|
|
72
|
+
home -> category -> product -> cart -> checkout -> account
|
|
73
|
+
```
|
|
128
74
|
|
|
129
|
-
|
|
75
|
+
Its product filters prove URL state, variants prove route state, cart/session data proves layout state, rejected updates prove rollback, and product-to-cart transitions prove enhanced navigation. These nouns belong to the fixture only; Kudzu must not add commerce-specific APIs or architecture.
|
|
130
76
|
|
|
131
|
-
|
|
77
|
+
Other applications should validate the same capabilities with domain-appropriate fixtures such as documentation, content, administration, forms, and dashboards.
|
|
132
78
|
|
|
133
|
-
|
|
134
|
-
- React with an equivalent production static deployment;
|
|
135
|
-
- Next.js static export;
|
|
136
|
-
- Vue/Nuxt static generation;
|
|
137
|
-
- Svelte/SvelteKit static generation;
|
|
138
|
-
- hand-written HTML and DOM JavaScript where it provides a useful lower bound.
|
|
79
|
+
## Performance Protocol
|
|
139
80
|
|
|
140
|
-
|
|
81
|
+
Matched comparisons may include React/Vite, static exports from established frameworks, and hand-written HTML/DOM code as a lower bound. Every target must provide the same initial content, behavior, errors, navigation semantics, and accessibility before results are compared directly.
|
|
141
82
|
|
|
142
|
-
|
|
83
|
+
Record at least:
|
|
143
84
|
|
|
144
|
-
- deploy
|
|
145
|
-
- HTML, CSS,
|
|
85
|
+
- deploy bytes and initial raw/gzip JavaScript;
|
|
86
|
+
- HTML, CSS, media, and route transfer bytes;
|
|
146
87
|
- clean build median;
|
|
147
|
-
- cold and warm LCP, CLS, and
|
|
148
|
-
-
|
|
149
|
-
-
|
|
150
|
-
- cart update and rollback latency;
|
|
151
|
-
- browser heap after initial load and repeated navigation;
|
|
152
|
-
- duplicate listeners, unfinished requests, and lifecycle leaks.
|
|
153
|
-
|
|
154
|
-
Production builds receive one warm-up and at least seven interleaved runs. Keep raw results and record the median; use additional runs when ranges overlap or a result controls an architectural decision.
|
|
155
|
-
|
|
156
|
-
## Performance Gates
|
|
157
|
-
|
|
158
|
-
Goal A is not complete unless:
|
|
159
|
-
|
|
160
|
-
- static routes still emit zero JavaScript;
|
|
161
|
-
- routes outside an application group remain byte-for-byte unaffected by navigation support;
|
|
162
|
-
- an application route ships only the layout and route capabilities it uses;
|
|
163
|
-
- no hydration task or browser component tree is introduced;
|
|
164
|
-
- repeated navigation leaves no growing listener, request, state, DOM, or heap ownership;
|
|
165
|
-
- Kudzu has no unexplained material regression against its previous phase;
|
|
166
|
-
- material losses against the fastest matched framework are profiled and either fixed or documented as an explicit product tradeoff;
|
|
167
|
-
- performance claims include fixture limitations and do not compare unequal initial content as equivalent.
|
|
88
|
+
- cold and warm LCP, CLS, INP, startup work, and heap;
|
|
89
|
+
- state update, optimistic rejection, rollback, and route transition latency;
|
|
90
|
+
- repeated-navigation listener, request, state, DOM, and heap ownership.
|
|
168
91
|
|
|
169
|
-
|
|
92
|
+
Production builds receive one warm-up and at least seven interleaved runs. Browser comparisons use rotating fresh profiles and additional runs when ranges overlap. A repeatable browser median change above 5% is material; deterministic byte growth is always reviewed.
|
|
170
93
|
|
|
171
|
-
|
|
94
|
+
## Regression Gates
|
|
172
95
|
|
|
173
|
-
|
|
96
|
+
- Static routes still emit zero JavaScript.
|
|
97
|
+
- Routes outside configured groups remain unaffected by navigation support.
|
|
98
|
+
- No hydration task or retained component tree is introduced.
|
|
99
|
+
- Repeated navigation leaves no growing ownership.
|
|
100
|
+
- Material regressions against Kudzu's previous path are explained and fixed or recorded as explicit tradeoffs.
|
|
101
|
+
- Competitor comparisons disclose unequal rendering architecture and fixture limitations.
|
|
102
|
+
- `npm run check`, `npm test`, and applicable browser checks pass.
|
|
174
103
|
|
|
175
|
-
|
|
104
|
+
## Historical Validation Record
|
|
176
105
|
|
|
177
|
-
The
|
|
106
|
+
The completed commerce and lifecycle measurements remain dated evidence, not current product requirements. The latest retained records include complete desktop/mobile navigation runs, zero cold/warm CLS, exact cleanup counters, capability byte deltas, and matched framework comparisons. Release notes and repository history retain version-specific numbers; new measurements must record their source revision, fixture, browser, hardware, raw arrays, and limitations.
|
|
178
107
|
|
|
179
108
|
## Non-Goals
|
|
180
109
|
|
|
181
110
|
- A default SPA router.
|
|
182
|
-
-
|
|
183
|
-
-
|
|
184
|
-
-
|
|
185
|
-
-
|
|
186
|
-
-
|
|
187
|
-
- Winning
|
|
111
|
+
- A commerce framework or reusable commerce package.
|
|
112
|
+
- Arbitrary React package compatibility.
|
|
113
|
+
- Runtime component registration, hydration, or client component rendering.
|
|
114
|
+
- Request-time SSR, server actions, or a hidden application server.
|
|
115
|
+
- Charting, mapping, telemetry, virtual-grid, or editor engines.
|
|
116
|
+
- Winning synthetic metrics by omitting equivalent content, behavior, accessibility, or fallback.
|
package/GOAL_B.md
CHANGED
|
@@ -1,44 +1,28 @@
|
|
|
1
|
-
# Goal B:
|
|
1
|
+
# Goal B: Relative TypeScript Worker Capability Proof
|
|
2
2
|
|
|
3
|
-
Goal B
|
|
3
|
+
Goal B is a completed cross-application compiler-capability proof for one browser-only pattern used by realtime React-shaped applications: a route-owned relative TypeScript module Worker created and cleaned up from an inline `useEffect`.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The realtime dashboard is a stress and conformance fixture. It is not a Kudzu product vertical, dashboard API, stream runtime, widget framework, server platform, or claim of ThingsBoard compatibility. [`MIGRATION_ROADMAP.md`](./MIGRATION_ROADMAP.md) remains the source of truth for future fixture-driven migration work.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Status
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
The exact relative TypeScript Worker capability is implemented and verified. Kudzu recognizes one statically analyzable constructor form, emits its validated module graph separately, rewrites the URL for the configured base, ties creation and termination to effect ownership, and adds no Worker bytes to routes that do not use it.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
dashboard -> devices -> device detail -> alarms -> settings
|
|
13
|
-
```
|
|
11
|
+
Further Worker, transport, effect, or React compatibility work must begin with a reduced conventional React migration fixture that fails.
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
## Capability Target
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
plain route <-> realtime device dashboard
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
The dashboard receives a logical 1,000 telemetry samples per second, keeps a bounded history in a module Worker, downsamples it, and updates one imperative chart without routing samples through `useState()`.
|
|
22
|
-
|
|
23
|
-
## Runtime Model
|
|
15
|
+
The focused proof uses one Worker-enabled route, one plain companion route, and one unrelated static route:
|
|
24
16
|
|
|
25
17
|
```text
|
|
26
|
-
|
|
27
|
-
-> route-specific effect ESM
|
|
28
|
-
-> route-owned module Worker
|
|
29
|
-
-> bounded telemetry buffer and downsampling
|
|
30
|
-
-> batched imperative chart updates
|
|
18
|
+
plain route <-> Worker-enabled route
|
|
31
19
|
```
|
|
32
20
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
The Worker is a capability, not a framework runtime. Routes that do not create one must not load its graph. Static routes must remain JavaScript-free.
|
|
21
|
+
The stress fixture generates a logical 1,000 samples per second, keeps bounded history in a module Worker, downsamples it, and updates one imperative chart. Telemetry and chart details validate Worker isolation, throughput, and ownership; they are not Kudzu product features or advice to rewrite ordinary declarative UI imperatively.
|
|
36
22
|
|
|
37
|
-
##
|
|
23
|
+
## Supported Form
|
|
38
24
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
Support this exact shape inside a compiled inline `useEffect` callback:
|
|
25
|
+
The Worker must be constructed directly inside a compiled inline effect:
|
|
42
26
|
|
|
43
27
|
```tsx
|
|
44
28
|
useEffect(() => {
|
|
@@ -47,13 +31,13 @@ useEffect(() => {
|
|
|
47
31
|
{ type: "module" },
|
|
48
32
|
)
|
|
49
33
|
|
|
50
|
-
const onMessage = (event: MessageEvent<
|
|
34
|
+
const onMessage = (event: MessageEvent<Frame>) => resource.render(event.data)
|
|
51
35
|
worker.addEventListener("message", onMessage)
|
|
52
36
|
|
|
53
37
|
return () => {
|
|
54
38
|
worker.removeEventListener("message", onMessage)
|
|
55
39
|
worker.terminate()
|
|
56
|
-
|
|
40
|
+
resource.dispose()
|
|
57
41
|
}
|
|
58
42
|
}, [])
|
|
59
43
|
```
|
|
@@ -63,87 +47,67 @@ Compiler requirements:
|
|
|
63
47
|
- accept only an unshadowed `Worker` with `new URL(relativeLiteral, import.meta.url)` and literal `{ type: "module" }`;
|
|
64
48
|
- resolve one `.worker.ts` entry under project source and bundle its relative TypeScript graph separately from window code;
|
|
65
49
|
- emit deterministic content-hashed ESM under `assets/workers` and rewrite the constructor to the base-aware emitted URL;
|
|
66
|
-
- reject package imports, JSX,
|
|
50
|
+
- reject package imports, JSX, import-equals declarations, dynamic imports, `require()`, paths outside source, malformed options, and unsupported Worker forms with source locations;
|
|
67
51
|
- reject ordinary runtime imports or re-exports of `.worker.ts`; type-only imports may erase normally;
|
|
68
|
-
-
|
|
69
|
-
- create the Worker only when
|
|
70
|
-
- preserve
|
|
71
|
-
- reject generated/public asset collisions
|
|
52
|
+
- never mark Worker files as document capability scripts or import them into the window;
|
|
53
|
+
- create the Worker only when its owning effect mounts;
|
|
54
|
+
- preserve zero-cost exclusion for builds without relative TypeScript Workers;
|
|
55
|
+
- reject generated/public asset collisions.
|
|
72
56
|
|
|
73
|
-
Worker construction
|
|
57
|
+
Worker construction in imported helpers or imported keyed-row effects, `SharedWorker`, classic workers, Blob workers, arbitrary dynamic URLs, and package runtime graphs remain outside this completed capability.
|
|
74
58
|
|
|
75
59
|
## Ownership
|
|
76
60
|
|
|
77
|
-
| Owner | Lifetime |
|
|
61
|
+
| Owner | Lifetime | Capability responsibility |
|
|
78
62
|
|---|---|---|
|
|
79
|
-
| Document | Full document |
|
|
80
|
-
| Layout | Enhanced navigation session |
|
|
81
|
-
| Route | Current
|
|
82
|
-
| DOM range |
|
|
83
|
-
| Worker | Explicit owner cleanup | parsing, bounded buffering, aggregation
|
|
63
|
+
| Document | Full document | global diagnostics and document-exit cleanup |
|
|
64
|
+
| Layout | Enhanced navigation session | explicitly shared browser resource |
|
|
65
|
+
| Route | Current route range | Worker, subscription, request cancellation |
|
|
66
|
+
| DOM range | Connected conditional/keyed range | imperative resource and animation work |
|
|
67
|
+
| Worker | Explicit owner cleanup | parsing, bounded buffering, aggregation |
|
|
84
68
|
|
|
85
|
-
|
|
69
|
+
On an enhanced same-document transition, route cleanup removes listeners, stops imperative work, terminates its Worker, and invalidates stale UI writes before the next route mounts. Non-persisted document exit performs cleanup; a BFCache-persisted document retains live ownership for restoration.
|
|
86
70
|
|
|
87
71
|
## Fixture Contract
|
|
88
72
|
|
|
89
|
-
The
|
|
73
|
+
The capability fixture proves:
|
|
90
74
|
|
|
91
|
-
- one complete
|
|
75
|
+
- one complete Worker-enabled document and one complete plain document in an opt-in navigation group;
|
|
92
76
|
- one unrelated static route with zero JavaScript;
|
|
93
77
|
- a relative TypeScript Worker importing at least one relative helper;
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
-
|
|
98
|
-
-
|
|
99
|
-
- exact counters for Worker starts, terminations, messages, renders, listeners, and stale post-cleanup work;
|
|
78
|
+
- bounded deterministic buffering and batched sample generation;
|
|
79
|
+
- bounded frames delivered no faster than display cadence;
|
|
80
|
+
- one imperative resource updated without sample-level Kudzu setters;
|
|
81
|
+
- direct load, enhanced navigation, back/forward, and repeated cached revisits;
|
|
82
|
+
- exact starts, terminations, messages, renders, listeners, disposal, and stale-write counters;
|
|
100
83
|
- native document fallback when JavaScript or Worker creation fails.
|
|
101
84
|
|
|
102
|
-
## Acceptance
|
|
103
|
-
|
|
104
|
-
- the Worker graph is absent from static and plain route HTML and is fetched only after the dashboard effect mounts;
|
|
105
|
-
- two unchanged production builds emit identical Worker names and bytes;
|
|
106
|
-
- changing Worker source changes its content hash;
|
|
107
|
-
- base-prefixed deployment produces a valid same-origin Worker URL;
|
|
108
|
-
- 30 dashboard/plain cycles create and terminate exactly 30 route Workers with no growing listener or chart ownership;
|
|
109
|
-
- messages arriving after cleanup cannot update removed route DOM;
|
|
110
|
-
- the ring buffer remains at its configured capacity under sustained input;
|
|
111
|
-
- chart rendering is batched and sample ingestion does not call `useState()`;
|
|
112
|
-
- routes without Worker capabilities remain byte-for-byte unaffected;
|
|
113
|
-
- output raw/gzip cost, clean build time, sample throughput, render cadence, and lifecycle counters are recorded;
|
|
114
|
-
- `npm run check`, `npm test`, package dry-run, and browser checks pass.
|
|
115
|
-
|
|
116
|
-
Verified measurements for the focused `/dash` fixture: the minified Worker graph is `assets/workers/telemetry.worker-BVG2SA55.js`, 907 B raw and 477 B gzip. The dashboard window graph is 11,388 B raw and 5,148 B gzip across its shared runtime, effect runtime, navigation, route effect entry, and handler module; the Worker is not part of that graph. Seven clean minified builds measured 455.1, 459.9, 460.7, 463.5, 467.6, 472.6, and 475.2 ms, with a 463.5 ms median.
|
|
117
|
-
|
|
118
|
-
The real-Worker browser check uses real wall time and requires sustained generation beyond 1,130 samples at 700-1,300 logical samples/second, an exact 128-sample ring bound, batches of 10, exactly 24 displayed points, multiple renders, and a render ceiling below 25 Hz. Delayed Worker ticks catch up in batches to the logical 1,000 samples/second clock; frames emit no more often than every 50 ms. The imperative chart performs a minimal canvas path draw. The navigation ownership check completed 30 dashboard/plain cycles with exactly 30 starts and 30 terminations, exactly 60 listener additions and removals across message and error listeners, zero retained listeners after every cleanup, disposed every old chart canvas, fresh ownership on back/forward and cached revisits, and no render from a removed message listener invoked after cleanup. Dashboard, plain, and static HTML contain no Worker asset URL; plain does not load the route effect graph, and static contains no script, capability marker, or state payload. A no-Worker equivalent emitted byte-identical shared runtime, effect runtime, navigation, and route effect entry files with no `assets/workers` directory. An unreachable imported-row effect referencing `unused.worker.ts` emitted no Worker asset. Two unchanged builds emitted identical Worker names and bytes, and a controlled downsample-source change changed the emitted hash.
|
|
85
|
+
## Acceptance And Performance Gates
|
|
119
86
|
|
|
120
|
-
|
|
87
|
+
- The Worker graph is absent from static and plain route HTML and fetched only after its effect mounts.
|
|
88
|
+
- Two unchanged builds emit identical Worker names and bytes; source changes alter the content hash.
|
|
89
|
+
- Base-prefixed output constructs a valid same-origin Worker URL.
|
|
90
|
+
- Repeated route cycles create and terminate exactly one route Worker each with no growing ownership.
|
|
91
|
+
- Messages after cleanup cannot update removed route DOM.
|
|
92
|
+
- Worker memory remains bounded by declared capacity.
|
|
93
|
+
- High-frequency ingestion does not create one main-thread task or Kudzu state commit per sample.
|
|
94
|
+
- Imperative rendering is batched to its declared cadence.
|
|
95
|
+
- Builds without this capability remain unaffected.
|
|
96
|
+
- Raw/gzip output, build time, throughput, cadence, and lifecycle counters are recorded.
|
|
97
|
+
- `npm run check`, `npm test`, package dry-run, and applicable browser checks pass.
|
|
121
98
|
|
|
122
|
-
|
|
123
|
-
2. **Capability conformance fixture**: mock telemetry Worker, bounded buffer, downsampling, imperative DOM ownership, and route cleanup.
|
|
99
|
+
## Historical Validation Record
|
|
124
100
|
|
|
125
|
-
|
|
101
|
+
The focused fixture emitted a 907 B raw / 477 B gzip Worker graph and an 11,388 B raw / 5,148 B gzip window graph. Seven clean minified builds measured a 463.5 ms median. Thirty route cycles produced exactly 30 starts and terminations, balanced message/error listener registration, bounded 128-sample history, no retained chart ownership, no stale post-cleanup render, deterministic unchanged builds, and a changed hash after a controlled Worker source edit.
|
|
126
102
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
- sustained 1,000 samples/second does not create one main-thread task or Kudzu state commit per sample;
|
|
130
|
-
- Worker memory is bounded by declared buffer capacity;
|
|
131
|
-
- chart updates are batched to at most one per display frame;
|
|
132
|
-
- dashboard departure stops observable messages and renders before the next route mounts;
|
|
133
|
-
- repeated navigation leaves no growing Worker, timer, listener, chart, state, DOM, or heap ownership;
|
|
134
|
-
- Worker support adds no bytes to routes and builds that do not use it;
|
|
135
|
-
- material losses are profiled and fixed or documented as explicit tradeoffs using matched initial content and behavior.
|
|
103
|
+
These numbers are dated conformance evidence. They do not define a dashboard product target.
|
|
136
104
|
|
|
137
105
|
## Non-Goals
|
|
138
106
|
|
|
139
|
-
-
|
|
140
|
-
-
|
|
141
|
-
-
|
|
142
|
-
-
|
|
143
|
-
-
|
|
144
|
-
-
|
|
145
|
-
-
|
|
146
|
-
|
|
147
|
-
## Completion Definition
|
|
148
|
-
|
|
149
|
-
Goal B Milestone 1 is complete when the focused realtime fixture proves deterministic relative TypeScript Worker emission, bounded high-frequency processing, imperative chart updates, exact route ownership and cleanup across repeated navigation, native/static fallback, zero-cost exclusion, source diagnostics, and recorded production measurements.
|
|
107
|
+
- Device, alarm, tenant, transport, chart, map, or widget product features.
|
|
108
|
+
- A general stream, observable, scheduler, state, or plugin runtime.
|
|
109
|
+
- Storing high-frequency samples in Kudzu component state.
|
|
110
|
+
- Retaining a browser component tree.
|
|
111
|
+
- Request-time SSR, server actions, or a hidden server.
|
|
112
|
+
- Arbitrary third-party React widgets.
|
|
113
|
+
- Treating Worker isolation as a security sandbox.
|
package/README.md
CHANGED
|
@@ -10,11 +10,11 @@ Kudzu is designed so ordinary common React-shaped TSX can migrate with minimal s
|
|
|
10
10
|
|
|
11
11
|
> Experimental `0.7.x`: the compiler API and supported TSX surface may change.
|
|
12
12
|
|
|
13
|
-
**0.7.
|
|
13
|
+
**0.7.9:** Keyed-row prop defaults. Same-file and relative keyed row components retain destructured primitive literal defaults, with missing props substituted at compile time and no new browser runtime. See [release notes](./RELEASES.md#079---keyed-row-prop-defaults).
|
|
14
14
|
|
|
15
15
|
Documentation: [kudzujs.cloud/docs](https://kudzujs.cloud/docs)
|
|
16
16
|
|
|
17
|
-
Development
|
|
17
|
+
Development direction: [React migration roadmap](./MIGRATION_ROADMAP.md)
|
|
18
18
|
|
|
19
19
|
## Install
|
|
20
20
|
|
|
@@ -74,7 +74,7 @@ export default function Header() {
|
|
|
74
74
|
}
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
Kudzu rewrites supported React imports to its compile-time APIs before evaluating the module; neither the React package nor a compatibility runtime enters the deploy output. Named or aliased `useState`, `useReducer`, `useEffect`, `useRef`, `createContext`, and `useContext` imports compile to their canonical forms. Default and namespace imports may call those APIs as direct members such as `React.useState`, and default, namespace, or named `Fragment` also works. `memo(Component)` is erased to a same-file component. Inline `useCallback(function, literalDependencies)` is erased to its function, while inline synchronous `useMemo` callbacks may return one expression over primitive literals/direct local state or an analyzable `filter`, `map`, `flatMap`, and `Array.from` collection pipeline. Scalar expressions inline into existing bindings; collection pipelines lower to existing keyed-list selectors and preserve row identity. Both hooks require inert literal dependency arrays and complete captured-state dependencies; memo locals cannot be duplicated or captured by nested functions. React classes and side-effect or dynamic React imports remain unsupported. A static route using these forms still emits zero JavaScript.
|
|
77
|
+
Kudzu rewrites supported React imports to its compile-time APIs before evaluating the module; neither the React package nor a compatibility runtime enters the deploy output. Named or aliased `useState`, `useReducer`, `useEffect`, `useRef`, `createContext`, and `useContext` imports compile to their canonical forms. Default and namespace imports may call those APIs as direct members such as `React.useState`, and default, namespace, or named `Fragment` also works. `memo(Component)` is erased to a same-file component. Inline `useCallback(function, literalDependencies)` is erased to its function, while inline synchronous `useMemo` callbacks may return one expression over primitive literals/direct local state or an analyzable `filter`, `map`, `flatMap`, and `Array.from` collection pipeline. Collection pipelines may start from local array state or a named relative import whose source is an exported JSON-safe `const` array; imported static collections may be filtered by direct local state listed in the dependency array. Scalar expressions inline into existing bindings; collection pipelines lower to existing keyed-list selectors and preserve row identity. Both hooks require inert literal dependency arrays and complete captured-state dependencies; memo locals cannot be duplicated or captured by nested functions. React classes and side-effect or dynamic React imports remain unsupported. A static route using these forms still emits zero JavaScript.
|
|
78
78
|
|
|
79
79
|
Direct default or named `clsx` imports compile away for string/number literals, literal arrays, literal object conditions, and conditional expressions. Kudzu lowers those calls to ordinary class expressions, so reactive classes reuse existing bindings without shipping `clsx`; spreads, computed object keys, arbitrary calls, and indirect references remain unsupported.
|
|
80
80
|
|
|
@@ -418,7 +418,9 @@ return <ItemList items={items} />
|
|
|
418
418
|
|
|
419
419
|
The original row component remains reusable across multiple lists and ordinary JSX. State-backed list wrappers and row components are specialized to intrinsic JSX at build time; no component function or component runtime is shipped to the browser. Kudzu emits initial items as static HTML, then adds, removes, updates, styles, conditional branches, and moves keyed elements directly. The map may appear directly in JSX, in one top-level immutable `const` rendered once as a JSX child, or in one synchronous wrapper receiving the state identifier as a direct prop. Existing keys move without remounting, preserving uncontrolled descendant state. Direct `item.<field>` reads use compact markers; derived item expressions compile to external ESM evaluators. Nested item-local `&&` and ternary conditions patch bounded branches and mount or unmount their handlers. Item-local handlers and effects receive the latest JSON-safe item for their key. Effects mount after a row is connected and clean up when it is removed. A direct primitive item dependency such as `[item.name]`, optionally mixed with row or page state, reruns only rows whose selected values changed; unrelated fields and reorder do not rerun it, while removal cleans it up. The item remains stored once in shared list state; runtime descriptors carry a placeholder that the list runtime fills when mounting or updating the keyed root.
|
|
420
420
|
|
|
421
|
-
Rendered collections may use one-use top-level aliases and analyzable pipelines over local array state. Inline arrow callbacks accept `(item)` or `(item, index)`; `filter()` supports pure synchronous expressions, `flatMap()` projects one direct array property, `Array.from()` accepts an optional pure mapper, and the final `map()` may use `key={item.field}` or positional `key={index}`. Field keys preserve the matching DOM node through filtering and reorder. Positional keys deliberately preserve the DOM node at each position while its item changes, matching React key semantics.
|
|
421
|
+
Rendered collections may use one-use top-level aliases and analyzable pipelines over local array state or named relative imports of exported JSON-safe `const` arrays. Inline arrow callbacks accept `(item)` or `(item, index)`; `filter()` supports pure synchronous expressions and direct local-state reads, `flatMap()` projects one direct array property, `Array.from()` accepts an optional pure mapper, and the final `map()` may use `key={item.field}` or positional `key={index}`. Field keys preserve the matching DOM node through filtering and reorder. Positional keys deliberately preserve the DOM node at each position while its item changes, matching React key semantics.
|
|
422
|
+
|
|
423
|
+
Compiler-owned static `filter` collections with structural keyed rows validate source items and key tokens once. Removed rows become detached prototypes; restoration clones fresh nodes and inserts only new runs, so retained keys keep identity while restored keys remount. In a 31-fresh-profile, 4x CPU-throttled Chrome benchmark over 1,000 alternating products, Kudzu measured 37.8 ms to visible rows, 8.4 ms to filter to 500, and 3.6 ms to restore 1,000. React measured 86.3/12.9/6.2 ms, Vue 54.1/8.4/4.3 ms, and Svelte 61.4/11.7/8.3 ms. This is a focused static-filter result, not a claim that rendering 100,000 or 1,000,000 DOM rows is appropriate; paginate or window large datasets so only the visible result set enters the document.
|
|
422
424
|
|
|
423
425
|
A keyed row may contain multiple keyed maps over direct array properties of its item at any nesting depth. This supports recursively nested data populated after mount while preserving keyed DOM identity across updates and reorder:
|
|
424
426
|
|
|
@@ -463,7 +465,7 @@ Initial child rows remain complete HTML. Kudzu stores one child row prototype, i
|
|
|
463
465
|
|
|
464
466
|
In the matched 100-parent/1,000-child fixture, Kudzu measured 1.3/0.4/5.0/0.7 ms for child update and condition change, child reverse, parent reverse, and parent removal. Hand-written Astro/native measured 0.5/0.4/3.9/0.2 ms, Svelte 2.7/1.2/6.7/1.3 ms, Vue 4.9/2.5/6.1/2.2 ms, and React 11.8/5.0/8.2/4.4 ms. Kudzu and Astro emit initial rows while the CSR targets do not, so artifact sizes are not architecture-equivalent.
|
|
465
467
|
|
|
466
|
-
Each item must be an ordinary plain object with a unique string or finite-number key; nested data may contain only JSON-safe arrays, ordinary plain objects, and primitive values. Null-prototype objects are rejected to preserve JSON round-trip parity. Collections must remain anchored to local array state; inline callbacks accept one or two identifier parameters, and row roots must be intrinsic JSX or supported same-file/relative components with `key={item.<field>}` or `key={index}`. State-backed list wrappers use one destructured props parameter, an intrinsic return root, no effects, and a direct local-state prop. Whole-item, computed, nested, derived, `__proto__`, `prototype`, and `constructor` effect dependencies are rejected. A collection alias may only be rendered once and cannot be read by other JavaScript. Collection callbacks and derived expressions must be pure and synchronous: supported reads, operators, templates, approved read-only methods, deterministic `Math`, and primitive conversion compile; imported callbacks, browser globals, promises, mutation, arbitrary calls, and prototype-sensitive properties fail. Lazy or dynamic keyed-row state initializers, non-`null` refs, callback refs, package/namespace/star row imports, same-file exported rows, reusable aliases, prop spreads/
|
|
468
|
+
Each item must be an ordinary plain object with a unique string or finite-number key; nested data may contain only JSON-safe arrays, ordinary plain objects, and primitive values. Null-prototype objects are rejected to preserve JSON round-trip parity. Collections must remain anchored to local array state or a supported static named import; inline callbacks accept one or two identifier parameters, and row roots must be intrinsic JSX or supported same-file/relative components with `key={item.<field>}` or `key={index}`. Specialized keyed row components accept missing destructured string, finite-number, boolean, or `null` props with literal defaults. State-backed list wrappers use one destructured props parameter, an intrinsic return root, no effects, and a direct local-state prop. Whole-item, computed, nested, derived, `__proto__`, `prototype`, and `constructor` effect dependencies are rejected. A collection alias may only be rendered once and cannot be read by other JavaScript. Collection callbacks and derived expressions must be pure and synchronous: supported reads, operators, templates, approved read-only methods, deterministic `Math`, and primitive conversion compile; imported callbacks, browser globals, promises, mutation, arbitrary calls, and prototype-sensitive properties fail. Lazy or dynamic keyed-row state initializers, non-`null` refs, callback refs, package/namespace/star row imports, same-file exported rows, reusable aliases, prop spreads/rest, non-primitive defaults, children, fragments, and `dangerouslySetInnerHTML` remain unsupported. Keyed rows must be placed inside an explicit `<tbody>`, `<thead>`, or `<tfoot>`.
|
|
467
469
|
|
|
468
470
|
The focused wrapper fixture emits 1,393 B raw / 500 B gzip HTML and 10,719 B raw / 4,665 B gzip JavaScript across its route capabilities. After one warm-up, seven clean builds measured 314.1, 325.3, 322.3, 327.2, 336.1, 322.4, and 315.0 ms, with a 322.4 ms median.
|
|
469
471
|
|
|
@@ -672,18 +674,21 @@ Supported:
|
|
|
672
674
|
- Layout- and route-lifetime effect mounts in navigation groups
|
|
673
675
|
- Conditional/keyed DOM-owned effects in navigation groups
|
|
674
676
|
|
|
675
|
-
|
|
677
|
+
Selected current migration limits:
|
|
678
|
+
|
|
679
|
+
- Common prop spreads/rest, wrapper `children` forwarding, `forwardRef`, and `useId` still need fixture-driven component specialization.
|
|
680
|
+
- Hookful non-keyed imported components and pure lazy state/reducer initializers remain narrower than ordinary React.
|
|
681
|
+
- Reusable collection aliases, computed child collections, imported pure transforms, and common immutable pagination/sorting forms need failing fixtures before expansion.
|
|
682
|
+
- Effect dependencies remain limited to directly analyzable primitive signals and supported keyed-item properties.
|
|
683
|
+
- Conditional/keyed ranges inside SVG and router-shaped package source remain separate compatibility work.
|
|
684
|
+
- Arbitrary, mutating, asynchronous, or package collection callbacks remain deliberate static-analysis boundaries.
|
|
685
|
+
- Server actions, request-time SSR, a React runtime/ecosystem layer, React islands, and a default SPA router are non-goals.
|
|
676
686
|
|
|
677
|
-
|
|
678
|
-
- Arbitrary, imported, mutating, or asynchronous collection callbacks
|
|
679
|
-
- Lazy/dynamic keyed-row state initializers and callback refs
|
|
680
|
-
- Server actions and request-time SSR
|
|
681
|
-
- React package/runtime/ecosystem compatibility or React islands
|
|
682
|
-
- HMR and framework DevTools
|
|
687
|
+
See the [active fixture queue](./MIGRATION_ROADMAP.md#active-fixture-queue) for development order. Unsupported syntax is not automatically backlog work; a real React migration fixture must fail first.
|
|
683
688
|
|
|
684
689
|
## Benchmarks
|
|
685
690
|
|
|
686
|
-
### Goal A Commerce Journey
|
|
691
|
+
### Goal A Validation Fixture: Commerce Journey
|
|
687
692
|
|
|
688
693
|
The matched fixture covers home, category, product, cart, checkout, and account routes with complete initial HTML, shared application layout state, product options, optimistic cart success and rejection, accessible errors, rollback, and product-to-cart navigation. Kudzu, React + Vite, Next.js, Nuxt, and SvelteKit render the same tested content and interactions.
|
|
689
694
|
|