@kudzujs/core 0.8.50 → 0.8.51

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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  This document is the source of truth for Kudzu's product direction, architecture invariants, and future development order. Read it before extending React-shaped syntax or browser capabilities.
4
4
 
5
- The executable post-`0.8.50` compiler and large-application sequence is maintained in [`docs/next-architecture/large-application-ai-native-roadmap.md`](./docs/next-architecture/large-application-ai-native-roadmap.md). Follow its PR dependencies for implementation work; this document remains authoritative when selecting or accepting a migration capability.
5
+ The executable post-`0.8.51` compiler and large-application sequence is maintained in [`docs/next-architecture/large-application-ai-native-roadmap.md`](./docs/next-architecture/large-application-ai-native-roadmap.md). Follow its PR dependencies for implementation work; this document remains authoritative when selecting or accepting a migration capability.
6
6
 
7
7
  [`GOAL_A.md`](./GOAL_A.md) and [`GOAL_B.md`](./GOAL_B.md) are completed capability-validation records. Their commerce and realtime dashboard fixtures prove general lifecycle, navigation, async-workflow, and Worker capabilities; they are not separate product verticals or future priority lists.
8
8
 
@@ -63,6 +63,7 @@ The following are available building blocks, not future vertical roadmaps:
63
63
  - Function components, props, children, context, direct bindings, conditions, controlled form properties, refs, and synchronous or async handlers.
64
64
  - `useState`, independent repeated non-keyed child state with conditional mount ownership, reduced relative-imported `useReducer`, direct dispatch specialization, and reduced Zustand-shaped shared state lowered through package-neutral shared-state/action IR and proven by migration fixtures.
65
65
  - Mount and dependency effects with cleanup, route/layout lifetimes, conditional/keyed ownership, stale-write isolation, and relative TypeScript Workers.
66
+ - Direct browser-only package references in inline effect setup/cleanup callbacks bundle into route-owned effect ESM without entering build-time component execution or static sibling output.
66
67
  - Keyed local-state and imported static collections, pure selectors, nested direct-property lists, recursive and directly exported row specialization, static/keyed component reuse, analyzable specialized prop spreads, serializable literal defaults, direct intrinsic rest forwarding, forwarded JSX children, latest-item handlers, row state/effects/refs, and stable keyed identity.
67
68
  - Complete-document native fallback and optional shared-layout navigation groups with parameter initialization, finite prefetch caching, focus, history, cleanup, and independent capability output.
68
69
  - React-shaped SVG presentation attributes, direct `clsx` lowering, fixed-locale reactive `Intl.NumberFormat` display formatting, relative TypeScript handler helpers, and source-located diagnostics.
@@ -97,7 +98,7 @@ This queue orders the next investigations by general migration value. Start only
97
98
  - Guarded `localStorage` restoration and persistence compose through existing mount/dependency effects, preserving deterministic static fallbacks without a storage runtime.
98
99
  - Reachable source compilation excludes unused React/Vite modules while preserving source diagnostics for every reachable module.
99
100
  - Direct imported immutable maps fold to static HTML, and relative structured calculations reevaluate consumed scalar fields through existing binding ESM.
100
- - Direct event-only package imports bundle into route handlers without executing during static rendering.
101
+ - Direct event-handler and inline owned-effect package imports bundle into route handler/effect ESM without executing during static rendering.
101
102
  - Production Tailwind output, Inter assets, direct native SVG charts, and Excel export compose without adding Tailwind, Recharts, or a package runtime to Kudzu.
102
103
  - FIRE migration validation covers all fourteen routes, URL/storage updates and reload, reset, presets, chart identity, Quiz recommendation, keyed Debt updates, Excel workbook creation, and zero-JavaScript Home/Books/Apps output.
103
104
 
package/PERFORMANCE.md CHANGED
@@ -6,7 +6,11 @@ Reproducibility classes: `npm run benchmark`, `npm run benchmark:keyed`, `npm ru
6
6
 
7
7
  The maintained 2026-08-13 comparison used Node 24.14.0 and an Intel Core i5-9500 Linux x64 host, one warm-up, and seven alternating fresh-process samples against clean `v0.8.44`. A narrow fast path skips Kudzu semantic transformation for 450 plain `.ts` modules whose runtime edges are exclusively resolvable relative TypeScript imports or exports; all other modules retain the existing transformer. Compile median fell from 2,323.9 ms to 1,413.2 ms (39.2%) and clean-build median from 3,325.3 ms to 2,382.4 ms (28.4%); every paired sample improved. Compile peak-RSS median fell from 571.2 MiB to 552.6 MiB, while build peak RSS was 570.9 MiB versus 568.8 MiB. Compiler scratch fell from 7,328,390 to 1,971,061 bytes. Both targets emitted the same 50 static HTML files, 10,980 bytes, and deploy SHA-256 `e107d78a7f55bc8a1af0ea6e53efeffa19b3d44d21c892484d103fa346e7ba7b`. This is a source-scale compiler comparison, not a cross-framework result.
8
8
 
9
- ## Current 0.8.50 Release Snapshot
9
+ ## Current 0.8.51 Release Snapshot
10
+
11
+ Kudzu 0.8.51 broadens compiler-only package-reference routing into existing owned effect ESM and bundling. Static siblings and routes without the effect retain zero package bytes; no new performance claim is made.
12
+
13
+ ## Maintained 0.8.50 Release Snapshot
10
14
 
11
15
  Kudzu 0.8.50 changes compiler and build-time shared-state metadata while retaining existing RouteIR state ownership, action handler output, and browser runtimes. No new performance claim is made.
12
16
 
package/README.md CHANGED
@@ -14,7 +14,7 @@ Kudzu compiles ordinary React-shaped TypeScript and TSX into complete static HTM
14
14
 
15
15
  > Experimental `0.8.x`: the compiler API and supported TSX surface may change.
16
16
 
17
- **Latest release: 0.8.50 - Package-neutral shared state IR.** Reduced Zustand migration now lowers through validated `SharedStateIR` and `SharedActionIR` records before generic signal and handler consumers. Existing layout ownership, action behavior, and source diagnostics remain unchanged without adding a public store API or runtime. Read the [release notes](./RELEASES.md#0850---package-neutral-shared-state-ir), open the [release page](https://github.com/kudzujs/kudzu/releases/tag/v0.8.50), or follow the [architecture packet](./docs/next-architecture/README.md).
17
+ **Latest release: 0.8.51 - Owned effect package imports.** Browser-only package bindings may be referenced directly inside inline effect setup and cleanup callbacks. Kudzu bundles them only into route-owned effect ESM while build-time component modules and static siblings omit the package. Read the [release notes](./RELEASES.md#0851---owned-effect-package-imports), open the [release page](https://github.com/kudzujs/kudzu/releases/tag/v0.8.51), or follow the [architecture packet](./docs/next-architecture/README.md).
18
18
 
19
19
  - [Documentation](https://kudzujs.cloud/docs)
20
20
  - [Installation guide](https://kudzujs.cloud/docs#install)
package/RELEASES.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Kudzu Releases
2
2
 
3
+ ## 0.8.51 - Owned effect package imports
4
+
5
+ Kudzu 0.8.51 lets inline owned effect setup and cleanup callbacks reference browser-only package imports directly.
6
+
7
+ ### Changed in 0.8.51
8
+
9
+ - Direct package references are accepted inside inline `useEffect` setup callbacks and their directly returned inline cleanup functions.
10
+ - Existing package import records feed the route-specific effect handler ESM and package bundler.
11
+ - Build scratch replaces package-owning effect callbacks with inert functions because build-time rendering records ownership without executing effect code.
12
+ - Render-time, helper-indirect, mixed, side-effect, and dynamic package imports remain source-diagnosed.
13
+
14
+ ### Output and performance
15
+
16
+ - Package code is retained only by routes whose effect graph references it.
17
+ - Build-time component modules and static sibling routes omit the package and its JavaScript.
18
+ - No package runtime, ResourceIR, public adapter API, or new performance claim is added.
19
+
20
+ ### Validation
21
+
22
+ - `npm run check`, `npm test`, and `npm run test:package` pass with all 217 tests and 166 generated pages.
23
+ - A direct setup/cleanup fixture bundles TypeScript package data into one owned effect handler while its static sibling ships zero JavaScript.
24
+ - Focused negative fixtures retain diagnostics for render-time and helper-indirect package references.
25
+
26
+ ### Upgrade
27
+
28
+ ```bash
29
+ npm install @kudzujs/core@^0.8.51
30
+ ```
31
+
3
32
  ## 0.8.50 - Package-neutral shared state IR
4
33
 
5
34
  Kudzu 0.8.50 contains reduced Zustand migration syntax behind package-neutral shared-state and action records before generic signal and handler consumers.
@@ -11,7 +11,7 @@ The top-level [`GOAL_A.md`](../../GOAL_A.md) and [`GOAL_B.md`](../../GOAL_B.md)
11
11
  | C: state/resource model | Research only | Reduced fixtures expose a limitation |
12
12
  | D: routing compatibility | Current behavior preserved | Revisit only with migration evidence and invariant review |
13
13
 
14
- The active post-`0.8.50` implementation sequence is [`large-application-ai-native-roadmap.md`](./large-application-ai-native-roadmap.md). P0.12 validation, property-level object-state dependencies, direct three-boundary callback/ref ownership, direct primitive prop state initialization, repeated direct leaf-handler callback use, direct child callback fan-out, collision-free and private Context action setters, package-neutral shared-state/action IR, measured route-entry output optimization, and the plain TypeScript source-scale fast path are complete. Browser-only package imports in owned effect/resource modules remain migration-backed work. The plan orders compiler semantic generalization, large-application foundations, compatibility boundaries, AI tooling, and production validation without changing the invariants below.
14
+ The active post-`0.8.51` implementation sequence is [`large-application-ai-native-roadmap.md`](./large-application-ai-native-roadmap.md). P0.12 validation, property-level object-state dependencies, direct three-boundary callback/ref ownership, direct primitive prop state initialization, repeated direct leaf-handler callback use, direct child callback fan-out, collision-free and private Context action setters, package-neutral shared-state/action IR, browser-only package imports in owned effects, measured route-entry output optimization, and the plain TypeScript source-scale fast path are complete. ResourceIR remains research requiring two independent lifecycle fixtures. The plan orders compiler semantic generalization, large-application foundations, compatibility boundaries, AI tooling, and production validation without changing the invariants below.
15
15
 
16
16
  ## Required Invariants
17
17
 
@@ -1,6 +1,6 @@
1
1
  # Current Compiler Architecture
2
2
 
3
- This maps the current `0.8.50` architecture, built on the completed `0.8.23` Goal A compiler foundation. File and function names are the stable references; line numbers are intentionally omitted because later work may still move code.
3
+ This maps the current `0.8.51` architecture, built on the completed `0.8.23` Goal A compiler foundation. File and function names are the stable references; line numbers are intentionally omitted because later work may still move code.
4
4
 
5
5
  ## Responsibility Map
6
6
 
@@ -381,7 +381,7 @@ After the relevant P0 foundations, investigate capabilities in this order:
381
381
  1. Property-level derived dependencies over ordinary object state. **Completed in `0.8.40`:** direct property paths and top-level immutable primitive locals over object state reuse tagged DerivedIR, subscribe to the source signal, and compare selected values with `Object.is`; whole-object and dynamic dependencies remain rejected.
382
382
  2. Multi-boundary component/prop/callback/ref/context dataflow. **Three-boundary callback/ref ownership completed in `0.8.43`; collision-free Context action-private state completed in `0.8.44`; action-only Provider setter exposure removed in `0.8.46`; direct primitive prop state initialization completed in `0.8.47`; repeated direct leaf-handler callback use completed in `0.8.48`; direct child callback fan-out completed in `0.8.49`:** forwarding preserves parent SignalIR, Context action lowering uses compiler-owned aliases when consumer locals reuse Provider state/setter names, action-required setters may remain compiler-only when their state is publicly exposed, specialized children may seed local state from a direct primitive parent signal, and one callback may branch through multiple component `on*` props and intrinsic handlers. A fourth callback boundary, callback aliases/non-handler uses, fully hidden Context state, and object/array prop initializers remain fail-closed. Broader prop, callback, ref, and Context graphs remain migration-led work.
383
383
  3. Package-neutral shared state/actions and migration of current Zustand internals. **Completed in `0.8.50`:** Zustand source normalization produces one generic shared-state adapter descriptor; selectors and handlers register JSON-safe SharedStateIR/SharedActionIR records, handler lowering consumes package-neutral actions, and existing RouteIR, layout ownership, same-turn updates, navigation persistence, and browser output remain unchanged. Redux/RTK and public adapter APIs remain unsupported.
384
- 4. Browser-only package imports in owned effect/resource modules.
384
+ 4. Browser-only package imports in owned effect/resource modules. **Completed in `0.8.51` for effects:** direct package references in inline effect setup/cleanup callbacks use existing package import records and route-owned effect ESM bundling; build-time component modules and static siblings omit the package. Helper-indirect, render-time, dynamic-import, and ResourceIR package graphs remain unsupported.
385
385
  5. ResourceIR from at least two independent WebSocket/SSE/SDK fixtures with the same semantics.
386
386
  6. Route/layout capability and CSS chunk closure.
387
387
  7. Incremental source and affected-route builds.
@@ -481,4 +481,4 @@ The first comparison is Kudzu versus React + Vite using the same agent, model, t
481
481
 
482
482
  ## Immediate Decision
483
483
 
484
- PR 1 through PR 12, the `0.8.40` property-dependency slice, the `0.8.41` and `0.8.43` direct multi-boundary callback/ref slices, the `0.8.44` Context alias slice, the `0.8.46` action-only Provider setter slice, the `0.8.47` direct primitive prop initializer slice, the `0.8.48` repeated direct leaf-handler callback slice, the `0.8.49` direct child callback fan-out slice, the `0.8.50` package-neutral shared-state/action slice, and the `0.8.42` measured route-output optimization are complete. Continue with **browser-only package imports in owned effect/resource modules** only from a real migration blocker. Do not skip directly to a public adapter/store API, router, virtualization, or unsupported ecosystem package feature.
484
+ PR 1 through PR 12, the `0.8.40` property-dependency slice, the `0.8.41` and `0.8.43` direct multi-boundary callback/ref slices, the `0.8.44` Context alias slice, the `0.8.46` action-only Provider setter slice, the `0.8.47` direct primitive prop initializer slice, the `0.8.48` repeated direct leaf-handler callback slice, the `0.8.49` direct child callback fan-out slice, the `0.8.50` package-neutral shared-state/action slice, the `0.8.51` owned-effect package import slice, and the `0.8.42` measured route-output optimization are complete. Continue with **ResourceIR only after two independent WebSocket/SSE/SDK fixtures prove the same lifecycle semantics**. Do not skip directly to a public adapter/store API, router, virtualization, or unsupported ecosystem package feature.
@@ -1,6 +1,6 @@
1
1
  # Planned Version Sequence
2
2
 
3
- This is an execution sequence, not release history. `0.8.16` through `0.8.50` are completed scopes represented by package/release records.
3
+ This is an execution sequence, not release history. `0.8.16` through `0.8.51` are completed scopes represented by package/release records.
4
4
 
5
5
  Keep each patch behavior-preserving and independently reviewable. If a boundary proves inseparable, revise this plan before combining releases; do not silently broaden a patch.
6
6
 
@@ -41,6 +41,7 @@ Keep each patch behavior-preserving and independently reviewable. If a boundary
41
41
  | `0.8.48` | Allow one setter-callback prop in multiple direct intrinsic handlers. | Each handler lowers once to the same parent signal; repeated same-handler calls and escaped uses fail without a runtime change. |
42
42
  | `0.8.49` | Fan one setter-callback prop out through multiple direct child component event props. | Every branch lowers to the original parent signal; aliases, ordinary props, spreads, and the fourth boundary fail without a runtime change. |
43
43
  | `0.8.50` | Isolate reduced Zustand migration behind package-neutral shared-state and action IR. | Validated shared identities/actions preserve RouteIR, layout lifetime, same-turn updates, diagnostics, browser behavior, and zero store runtime. |
44
+ | `0.8.51` | Bundle direct browser-only package references from inline owned effect setup/cleanup callbacks. | Package code stays in referenced route effect ESM; build scratch and static siblings omit it while indirect/render-time uses fail. |
44
45
 
45
46
  ## Sequence Rules
46
47
 
@@ -322,8 +322,8 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
322
322
  const packageBindings = packageImportBindings(sourceFile)
323
323
  for (const [name] of packageBindings) {
324
324
  const references = referenceIdentifiers(sourceFile, name)
325
- const invalid = references.find(reference => !insideJsxEventHandler(reference, sourceFile))
326
- if (invalid) throw sourceNodeError(invalid, sourceFile, `Package import ${JSON.stringify(name)} may only be referenced directly inside JSX event handlers`)
325
+ const invalid = references.find(reference => !insideJsxEventHandler(reference, sourceFile) && !insideOwnedEffectCallback(reference, sourceFile))
326
+ if (invalid) throw sourceNodeError(invalid, sourceFile, `Package import ${JSON.stringify(name)} may only be referenced directly inside JSX event handlers or owned effect setup/cleanup callbacks`)
327
327
  }
328
328
  const hasUseEffectImport = sourceFile.statements.some(statement => ts.isImportDeclaration(statement) && ["@kudzujs/core", "react"].includes(statement.moduleSpecifier.text) && statement.importClause?.namedBindings && ts.isNamedImports(statement.importClause.namedBindings) && statement.importClause.namedBindings.elements.some(entry => !entry.propertyName && entry.name.text === "useEffect"))
329
329
  const importedSourceCache = new Map()
@@ -1770,7 +1770,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1770
1770
  const descriptor = descriptors.compileEffectCallback(compiledCallback, {
1771
1771
  setters,
1772
1772
  reducers: reducersForNode(node, reducersByFunction),
1773
- importBindings: specializedEffect?.imports ?? importBindings,
1773
+ importBindings: specializedEffect?.imports ?? new Map([...importBindings, ...packageBindings]),
1774
1774
  listItem: dependencyItem,
1775
1775
  keyedBlock: activeKeyedBlock?.block.slot,
1776
1776
  stateOwners: new Map([...stateOwnersForNode(callbackArgument), ...stateOwnersForNode(node), ...(specializedEffectStateOwners.get(node)?.references ?? []), ...(activeStateOwners ?? [])]),
@@ -1804,8 +1804,11 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1804
1804
  ...(analysisSource(effectSource) ? { source: analysisSource(effectSource) } : {})
1805
1805
  })
1806
1806
  const dependencyExpressions = effect.dependencies.map((dependency, index) => dependency.kind === "derived" ? moduleIR.derived[dependency.derived].expression : ["state", dependencyEntries[index]?.name ?? ordinaryDependencies[index].text])
1807
+ const buildCallback = [...packageBindings].some(([name]) => referenceIdentifiers(callback, name).length)
1808
+ ? factory.createArrowFunction(undefined, undefined, [], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), factory.createBlock([], false))
1809
+ : callback
1807
1810
  return factory.updateCallExpression(node, node.expression, node.typeArguments, [
1808
- callback,
1811
+ buildCallback,
1809
1812
  factory.createArrayLiteralExpression(subscriptionNames.map(name => factory.createIdentifier(name))),
1810
1813
  factory.createStringLiteral(handlerUrl),
1811
1814
  factory.createStringLiteral(effect.setup.exportName),
@@ -2153,6 +2156,24 @@ function insideJsxEventHandler(node, root) {
2153
2156
  return false
2154
2157
  }
2155
2158
 
2159
+ function insideOwnedEffectCallback(node, root) {
2160
+ let callback
2161
+ for (let current = node.parent; current && current !== root.parent; current = current.parent) {
2162
+ if (!isFunctionLike(current)) continue
2163
+ callback = current
2164
+ break
2165
+ }
2166
+ if (!callback) return false
2167
+ if (ts.isCallExpression(callback.parent) && callback.parent.arguments[0] === callback && ts.isIdentifier(callback.parent.expression) && callback.parent.expression.text === "useEffect") return true
2168
+ const returned = callback.parent
2169
+ if (!ts.isReturnStatement(returned)) return false
2170
+ for (let current = returned.parent; current && current !== root.parent; current = current.parent) {
2171
+ if (!isFunctionLike(current)) continue
2172
+ return ts.isCallExpression(current.parent) && current.parent.arguments[0] === current && ts.isIdentifier(current.parent.expression) && current.parent.expression.text === "useEffect"
2173
+ }
2174
+ return false
2175
+ }
2176
+
2156
2177
  function validateKeyedList(parts, sourceFile, setters, rowStates, componentSpecializations, expandedRowSpecializations, nestedRowSpecializations, factory, prepareListCallback, bindingIndex) {
2157
2178
  const fail = (node, message) => {
2158
2179
  throw sourceNodeError(node, sourceFile, message)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kudzujs/core",
3
- "version": "0.8.50",
3
+ "version": "0.8.51",
4
4
  "description": "HTML-first TSX framework with synchronous state semantics and no virtual DOM",
5
5
  "type": "module",
6
6
  "license": "MIT",