@kudzujs/core 0.16.21 → 0.16.22
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/PERFORMANCE.md +11 -0
- package/README.md +1 -1
- package/RELEASES.md +26 -0
- package/docs/next-architecture/application-capability-release-plan.md +16 -1
- package/docs/next-architecture/compiler-current-architecture.md +1 -1
- package/docs/next-architecture/versioning.md +1 -1
- package/framework/compiler/descriptor-session.mjs +2 -2
- package/framework/compiler/source-compiler.mjs +44 -7
- package/package.json +1 -1
package/PERFORMANCE.md
CHANGED
|
@@ -65,6 +65,17 @@ cost, so this proof does not authorize `1.0.0`. This evidence packet changes no
|
|
|
65
65
|
compiler, runtime, public API, dependency, generated application, or browser
|
|
66
66
|
artifact.
|
|
67
67
|
|
|
68
|
+
The 2026-09-04 collection follow-up removes the first blocker for one ordinary
|
|
69
|
+
immutable filtered alias reused by keyed rendering, a top-level count, reactive
|
|
70
|
+
text, and a count-selected branch. It adds no primitive, pass, runtime concept,
|
|
71
|
+
API, dependency, or shared-runtime byte; net production compiler growth is 37
|
|
72
|
+
lines across two existing files. The maintained positive and negative fixtures
|
|
73
|
+
pass all 302 tests with Chrome required. Their exercised interactive route uses
|
|
74
|
+
the unchanged 36,219 B shared runtime closure and 37,870 B total JavaScript with
|
|
75
|
+
route handlers. Benchmark contract and budget deltas are zero; the production AI
|
|
76
|
+
comparison is not rerun until selected-branch reactive text is also addressed.
|
|
77
|
+
This follow-up ships as `@kudzujs/core@0.16.22`.
|
|
78
|
+
|
|
68
79
|
## 0.21.2 Browser Performance And Memory
|
|
69
80
|
|
|
70
81
|
Measured 2026-09-03 at revision
|
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.16.x`: the compiler API and supported TSX surface may change.
|
|
16
16
|
|
|
17
|
-
**Latest release: 0.16.
|
|
17
|
+
**Latest release: 0.16.22 - reactive collection count reuse.** Filtered collection aliases now feed keyed rendering, top-level counts, reactive text, and count-selected branches without duplicated state. This removes the first blocker selected by the failed `0.21.4` AI delivery gate; `1.0.0` remains blocked. Read the [release notes](./RELEASES.md#01622---reactive-collection-count-reuse), open the [release page](https://github.com/kudzujs/kudzu/releases/tag/v0.16.22), 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,31 @@
|
|
|
1
1
|
# Kudzu Releases
|
|
2
2
|
|
|
3
|
+
## 0.16.22 - Reactive Collection Count Reuse
|
|
4
|
+
|
|
5
|
+
Kudzu 0.16.22 removes the first compiler blocker selected by the corrected
|
|
6
|
+
`0.21.4` AI delivery evidence: an ordinary filtered collection alias can now
|
|
7
|
+
feed keyed rendering, a top-level count, reactive text, and a count-selected
|
|
8
|
+
conditional branch without duplicating state or restructuring the component.
|
|
9
|
+
|
|
10
|
+
### Changed in 0.16.22
|
|
11
|
+
|
|
12
|
+
- Normalizes supported collection alias `.length` reads through existing
|
|
13
|
+
collection analysis and reactive binding compilation.
|
|
14
|
+
- Expands count aliases before conditional compilation while preserving initial
|
|
15
|
+
server-rendered values and keyed DOM ownership across filter changes.
|
|
16
|
+
- Verifies reactive count updates, empty-branch unmount, keyed remount, and the
|
|
17
|
+
retained diagnostic for unsupported collection alias consumers in Chrome.
|
|
18
|
+
- Adds no semantic primitive, compiler pass, runtime concept, public API,
|
|
19
|
+
dependency, or shared-runtime byte; `1.0.0` remains unauthorized.
|
|
20
|
+
- Updates `create-kudzu@0.1.147` to generate projects on
|
|
21
|
+
`@kudzujs/core@^0.16.22`.
|
|
22
|
+
|
|
23
|
+
### Upgrade
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
npm install @kudzujs/core@^0.16.22
|
|
27
|
+
```
|
|
28
|
+
|
|
3
29
|
## 0.16.21 - AI Evidence npm Recovery
|
|
4
30
|
|
|
5
31
|
Kudzu 0.16.21 retries npm publication of the unchanged corrected `0.21.4` AI
|
|
@@ -2154,6 +2154,21 @@ and count reuse, then reactive text inside selected conditional branches.
|
|
|
2154
2154
|
pass, runtime concept, public API, dependency, generated behavior, or browser
|
|
2155
2155
|
byte.
|
|
2156
2156
|
|
|
2157
|
+
The first evidence-selected follow-up now normalizes one supported collection
|
|
2158
|
+
alias across a keyed map, a top-level `.length` count, reactive text, and a
|
|
2159
|
+
count-selected JSX branch. It reuses collection analysis plus existing binding,
|
|
2160
|
+
conditional, and keyed ownership: no semantic primitive, compiler pass, runtime
|
|
2161
|
+
concept, API, dependency, or shared-runtime byte was added. The implementation
|
|
2162
|
+
changes two existing compiler files by net 37 lines and extends one positive and
|
|
2163
|
+
one negative fixture. Required Chrome verifies count updates, empty-branch
|
|
2164
|
+
unmount, keyed remount, and retained source diagnostics; all 302 tests, package
|
|
2165
|
+
smoke, and `npm run check` pass. The exercised route ships the unchanged 36,219
|
|
2166
|
+
B shared runtime closure and 37,870 B total JavaScript including its existing
|
|
2167
|
+
route handlers. No benchmark contract or budget changes; the production AI
|
|
2168
|
+
comparison must be rerun only after the remaining selected-branch reactive-text
|
|
2169
|
+
blocker is addressed. This follow-up is released as `@kudzujs/core@0.16.22`
|
|
2170
|
+
with `create-kudzu@0.1.147`. `1.0.0` remains unauthorized.
|
|
2171
|
+
|
|
2157
2172
|
## `1.0.0`: Stable Application Model
|
|
2158
2173
|
|
|
2159
2174
|
`1.0.0` is authorized only after every required `0.21.x` gate passes. It means:
|
|
@@ -2299,4 +2314,4 @@ release transaction where possible or document and publish a forward-fix patch.
|
|
|
2299
2314
|
| `0.21.1` | Released as `0.16.16` | Preserve 100/1,000/10,000-route phase, RSS, output, digest, incremental-equivalence, recovery, and bounded canonical-AST retention evidence. | Full 10,000-route report has one complete measured run after two measured-stage diagnostics; no projection used |
|
|
2300
2315
|
| `0.21.2` | Closed by existing semantics | Preserve initial/session/lazy bytes, keyed/range/navigation median alarms, and bounded endurance ownership evidence. | Same-revision absolute gates; structural artifact bytes are not compressed network transfer |
|
|
2301
2316
|
| `0.21.3` | Released as `0.16.17` | Preserve exact package, browser, benchmark, registry, and fresh-install evidence. | One initial navigation benchmark invocation stalled at the outer timeout; its clean seven-profile rerun passed all alarms |
|
|
2302
|
-
| `0.21.4` | Measured gate failed;
|
|
2317
|
+
| `0.21.4` | Measured gate failed; first blocker released as `0.16.22` | Preserve both raw batches, the corrected revision-2 protocols, the failed success/cost decision, and collection count reuse; keep `1.0.0` blocked. | Kudzu 11/25 versus React + Vite 24/25; next evidence work is conditional-branch reactive text |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Current Compiler Architecture
|
|
2
2
|
|
|
3
|
-
This maps the current `0.16.
|
|
3
|
+
This maps the current `0.16.22` architecture, built on the completed `0.9.0` semantic-compression release and `0.8.23` Goal A compiler foundation. The `0.21.4` AI delivery gate is measured and blocked; file and function names are the stable references, while line numbers are intentionally omitted because later work may still move code.
|
|
4
4
|
|
|
5
5
|
## Responsibility Map
|
|
6
6
|
|
|
@@ -66,7 +66,7 @@ Keep each patch behavior-preserving and independently reviewable. If a boundary
|
|
|
66
66
|
|
|
67
67
|
## Generator Versions
|
|
68
68
|
|
|
69
|
-
`create-kudzu@0.1.
|
|
69
|
+
`create-kudzu@0.1.147` retains the explicit install instructions and generates projects with `@kudzujs/core@^0.16.22`.
|
|
70
70
|
|
|
71
71
|
## Release Boundary
|
|
72
72
|
|
|
@@ -102,11 +102,11 @@ export function createDescriptorSession({ semantic, handlerUrl, factory, context
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
function compileConditional(kind, expression, truthy, falsy, setters) {
|
|
105
|
+
function compileConditional(kind, expression, truthy, falsy, setters, importBindings = new Map()) {
|
|
106
106
|
const state = directStateIdentifier(expression, setters, bindingIndex)
|
|
107
107
|
const thunk = branch => factory.createArrowFunction(undefined, undefined, [], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), branch)
|
|
108
108
|
if (state) return factory.createCallExpression(factory.createIdentifier("__kStateConditional"), undefined, [factory.createStringLiteral(kind), state, thunk(truthy), thunk(falsy)])
|
|
109
|
-
const [initial, ...descriptor] = compileReactiveExpression(expression, setters)
|
|
109
|
+
const [initial, ...descriptor] = compileReactiveExpression(expression, setters, importBindings)
|
|
110
110
|
return factory.createCallExpression(factory.createIdentifier("__kConditional"), undefined, [factory.createStringLiteral(kind), initial, thunk(truthy), thunk(falsy), ...descriptor])
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -1107,7 +1107,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1107
1107
|
visit(calculation.body)
|
|
1108
1108
|
validatedEffectCalculations.add(calculation)
|
|
1109
1109
|
}
|
|
1110
|
-
const validateReactiveJsxExpression = (expression, allowedNames) => {
|
|
1110
|
+
const validateReactiveJsxExpression = (expression, allowedNames, setters) => {
|
|
1111
1111
|
const value = unwrapExpression(expression)
|
|
1112
1112
|
const formatAccess = ts.isCallExpression(value) && !value.questionDotToken && ts.isPropertyAccessExpression(value.expression) && !value.expression.questionDotToken && value.expression.name.text === "format" ? value.expression : undefined
|
|
1113
1113
|
const formatter = formatAccess && unwrapExpression(formatAccess.expression)
|
|
@@ -1115,6 +1115,17 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1115
1115
|
if (!constructor) {
|
|
1116
1116
|
const validate = node => {
|
|
1117
1117
|
const current = unwrapExpression(node)
|
|
1118
|
+
if (ts.isPropertyAccessExpression(current) && current.name.text === "length") {
|
|
1119
|
+
const collection = analyzeCollectionPipeline(current.expression, {
|
|
1120
|
+
setters,
|
|
1121
|
+
declarations: jsxLocalDeclarations.get(nearestFunction(expression)),
|
|
1122
|
+
fail,
|
|
1123
|
+
importedCollections,
|
|
1124
|
+
stateNames: allowedNames,
|
|
1125
|
+
importedCollectionTransforms
|
|
1126
|
+
})
|
|
1127
|
+
if (collection?.state || collection?.calculation) return factory.createNumericLiteral(0)
|
|
1128
|
+
}
|
|
1118
1129
|
if (ts.isPropertyAccessExpression(current) && ts.isCallExpression(unwrapExpression(current.expression))) {
|
|
1119
1130
|
const call = unwrapExpression(current.expression)
|
|
1120
1131
|
if (ts.isIdentifier(call.expression) && importBindings.has(call.expression.text) && importBindings.get(call.expression.text).kind !== "namespace") {
|
|
@@ -1175,7 +1186,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1175
1186
|
if (!usedStates.size) return expression
|
|
1176
1187
|
const captures = captureNames(expanded, expanded, setters, bindingIndex)
|
|
1177
1188
|
const allowedNames = new Set([...setters.values(), ...captures])
|
|
1178
|
-
validateReactiveJsxExpression(expanded, allowedNames)
|
|
1189
|
+
validateReactiveJsxExpression(expanded, allowedNames, setters)
|
|
1179
1190
|
return expanded
|
|
1180
1191
|
}
|
|
1181
1192
|
const componentSpecializations = new WeakMap()
|
|
@@ -1684,9 +1695,22 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1684
1695
|
collectRenderedLists(sourceFile)
|
|
1685
1696
|
const collectionAliasUses = rawRenderedLists.flatMap(({ parts }) => parts.aliasUses ?? [])
|
|
1686
1697
|
const collectionAliasDeclarations = new Set(rawRenderedLists.flatMap(({ parts }) => parts.aliasDeclarations ?? []))
|
|
1698
|
+
const collectionLengthDeclarations = new Set()
|
|
1687
1699
|
for (const declaration of collectionAliasDeclarations) {
|
|
1688
1700
|
const owner = nearestFunction(declaration)
|
|
1689
|
-
const unsupported = identifierReferences(owner.body, declaration.name.text).find(reference =>
|
|
1701
|
+
const unsupported = identifierReferences(owner.body, declaration.name.text).find(reference => {
|
|
1702
|
+
if (collectionAliasUses.includes(reference)) return false
|
|
1703
|
+
if (!ts.isPropertyAccessExpression(reference.parent) || reference.parent.expression !== reference || reference.parent.name.text !== "length") return true
|
|
1704
|
+
for (let current = reference.parent; current && current !== owner; current = current.parent) {
|
|
1705
|
+
if (isFunctionLike(current)) return true
|
|
1706
|
+
if (ts.isVariableDeclaration(current) && current.initializer && current.parent?.parent?.parent === owner.body && isLocalConst(current)) {
|
|
1707
|
+
collectionLengthDeclarations.add(current)
|
|
1708
|
+
return false
|
|
1709
|
+
}
|
|
1710
|
+
if (ts.isJsxExpression(current) && current.initializer === undefined) return false
|
|
1711
|
+
}
|
|
1712
|
+
return true
|
|
1713
|
+
})
|
|
1690
1714
|
if (unsupported) fail(unsupported, `Rendered collection alias "${declaration.name.text}" may only be used as a rendered collection source`)
|
|
1691
1715
|
}
|
|
1692
1716
|
const rejectUnsupportedRenderControl = node => {
|
|
@@ -1850,17 +1874,19 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
1850
1874
|
const parts = conditionalParts(expression)
|
|
1851
1875
|
if (!parts) return ts.visitNode(expression, visitor)
|
|
1852
1876
|
const setters = settersForNode(anchor, settersByFunction)
|
|
1853
|
-
const
|
|
1854
|
-
const
|
|
1877
|
+
const condition = resolveReactiveJsxExpression(parts.condition, nearestFunction(anchor), setters)
|
|
1878
|
+
const usedStates = referencedStateNames(condition, setters, condition, bindingIndex)
|
|
1879
|
+
const captures = captureNames(condition, condition, setters, bindingIndex)
|
|
1855
1880
|
if (!usedStates.size && !captures.size) return ts.visitEachChild(expression, visitor, context)
|
|
1856
1881
|
usesBehavior = true
|
|
1857
1882
|
usesConditional = true
|
|
1858
1883
|
return descriptors.compileConditional(
|
|
1859
1884
|
parts.kind,
|
|
1860
|
-
|
|
1885
|
+
condition,
|
|
1861
1886
|
compileRenderExpression(parts.truthy, anchor),
|
|
1862
1887
|
compileRenderExpression(parts.falsy, anchor),
|
|
1863
|
-
setters
|
|
1888
|
+
setters,
|
|
1889
|
+
importBindings
|
|
1864
1890
|
)
|
|
1865
1891
|
}
|
|
1866
1892
|
|
|
@@ -2192,6 +2218,17 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
|
|
|
2192
2218
|
return factory.updateVariableDeclaration(node, node.name, node.exclamationToken, node.type, factory.createIdentifier("undefined"))
|
|
2193
2219
|
}
|
|
2194
2220
|
|
|
2221
|
+
if (ts.isVariableDeclaration(node) && collectionLengthDeclarations.has(node) && node.initializer) {
|
|
2222
|
+
const setters = settersForNode(node, settersByFunction)
|
|
2223
|
+
const expression = resolveReactiveJsxExpression(node.initializer, nearestFunction(node), setters)
|
|
2224
|
+
const stateNames = new Set(setters.values())
|
|
2225
|
+
const rewrite = current => {
|
|
2226
|
+
if (ts.isIdentifier(current) && stateNames.has(current.text) && isReferenceIdentifier(current)) return factory.createPropertyAccessExpression(current, "value")
|
|
2227
|
+
return ts.visitEachChild(current, rewrite, context)
|
|
2228
|
+
}
|
|
2229
|
+
return factory.updateVariableDeclaration(node, node.name, node.exclamationToken, node.type, ts.visitNode(expression, rewrite))
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2195
2232
|
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && ts.isCallExpression(node.initializer) && ts.isIdentifier(node.initializer.expression) && importBindings.has(node.initializer.expression.text)) {
|
|
2196
2233
|
if (parameterizedDebounceCalls.has(node.initializer)) return node
|
|
2197
2234
|
const setters = settersForNode(node, settersByFunction)
|