@kudzujs/core 0.8.36 → 0.8.38

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.
@@ -257,6 +257,8 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
257
257
  const { customHookTimerStates } = normalized
258
258
  const bindingIndex = createBindingIndex(sourceFile)
259
259
  const factory = context.factory
260
+ let activeStateOwners
261
+ let activeKeyedBlock
260
262
  const sourceName = source => relative(root, source.fileName).replaceAll(sep, "/")
261
263
  const componentAnalysis = createComponentAnalysisSession(semantic.componentAnalysis)
262
264
  const descriptors = createDescriptorSession({
@@ -268,6 +270,27 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
268
270
  compileEventCommand,
269
271
  handlerLowering,
270
272
  isPrimitiveLiteral: isPrimitiveDefaultLiteral,
273
+ stateReferences: node => new Map([...stateOwnersForNode(node), ...(activeStateOwners ?? [])]),
274
+ symbolReference: (name, node, aliases) => {
275
+ const names = new Set([name, ...aliases])
276
+ let binding
277
+ const visitName = current => {
278
+ if (binding) return
279
+ if (ts.isIdentifier(current)) {
280
+ if (names.has(current.text)) binding = current
281
+ return
282
+ }
283
+ for (const element of current.elements) if (ts.isBindingElement(element)) visitName(element.name)
284
+ }
285
+ for (let current = node; current && !binding; current = current.parent) if (isFunctionLike(current)) {
286
+ for (const parameter of current.parameters) visitName(parameter.name)
287
+ if (ts.isBlock(current.body)) for (const statement of current.body.statements) {
288
+ if (ts.isVariableStatement(statement)) for (const declaration of statement.declarationList.declarations) visitName(declaration.name)
289
+ if (ts.isFunctionDeclaration(statement) && statement.name) visitName(statement.name)
290
+ }
291
+ }
292
+ return binding ? { kind: "module-symbol", symbol: modules.symbol(binding.getSourceFile().fileName, binding, name) } : undefined
293
+ },
271
294
  sourceName,
272
295
  rejectWorkerConstructions: expression => workerCompiler.rejectConstructions(expression, expression.getSourceFile(), "Relative TypeScript Worker construction is only supported directly inside an inline useEffect() callback")
273
296
  })
@@ -341,24 +364,33 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
341
364
  const ensureOwner = (owner, kind = "component") => componentAnalysis.registerOwner(owner, { kind, name: ownerName(owner), props: analyzedProps(owner), site: analysisSite(owner, "owner"), source: analysisSource(owner) })
342
365
  const registerState = (owner, state, setter, kind, node, externalOwner) => {
343
366
  const ownerRecord = ensureOwner(owner)
344
- const stateOwner = externalOwner ?? `owner:${ownerRecord.slot}`
367
+ const stateRecord = componentAnalysis.registerState(owner, { name: state, setter, kind, ...(externalOwner ? { owner: externalOwner.owner } : {}), site: analysisSite(node, "hook"), source: analysisSource(node) })
368
+ const stateOwner = externalOwner?.state
369
+ ? { kind: "module-symbol", symbol: externalOwner.state }
370
+ : { kind: "state", owner: { kind: "component", slot: ownerRecord.slot }, slot: stateRecord.slot }
345
371
  const stateOwners = stateOwnersByFunction.get(owner) ?? new Map()
346
372
  stateOwners.set(state, stateOwner)
347
373
  stateOwnersByFunction.set(owner, stateOwners)
348
- return componentAnalysis.registerState(owner, { name: state, setter, kind, ...(externalOwner ? { owner: externalOwner } : {}), site: analysisSite(node, "hook"), source: analysisSource(node) })
374
+ return stateRecord
349
375
  }
350
376
  const stateOwnersForNode = node => {
351
377
  for (let current = node.parent; current; current = current.parent) {
352
- if (isFunctionLike(current) && stateOwnersByFunction.has(current)) return stateOwnersByFunction.get(current)
378
+ if (isFunctionLike(current)) {
379
+ const stateOwners = stateOwnersByFunction.get(current) ?? stateOwnersByFunction.get(ts.getOriginalNode(current))
380
+ if (stateOwners) return stateOwners
381
+ const site = analysisSite(current, "owner")
382
+ const owner = site && semantic.componentAnalysis.owners.find(entry => entry.site === site)
383
+ if (owner) return new Map(owner.states.map(state => [state.name, { kind: "state", owner: { kind: "component", slot: owner.slot }, slot: state.slot }]))
384
+ }
353
385
  }
354
386
  return new Map()
355
387
  }
356
388
  const fallbackOwner = node => {
357
389
  for (let current = node.parent; current; current = current.parent) {
358
390
  const owner = isFunctionLike(current) ? componentAnalysis.owner(current) : undefined
359
- if (owner) return `owner:${owner.slot}`
391
+ if (owner) return { kind: "component", slot: owner.slot }
360
392
  }
361
- return "module"
393
+ return { kind: "module" }
362
394
  }
363
395
  let usesBehavior = false
364
396
  let usesBinding = false
@@ -405,16 +437,20 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
405
437
  if (!value || !ts.isObjectLiteralExpression(value)) throw sourceNodeError(provider, providerSource, "Context Provider value must be one direct object literal")
406
438
  const owner = nearestFunction(provider)
407
439
  if (!owner) throw sourceNodeError(provider, providerSource, "Context Provider value must be returned by a component")
408
- const stateOwner = `external:${sourceName(providerSource)}:${owner.getStart(providerSource)}`
440
+ const stateOwner = { kind: "module-symbol", symbol: modules.symbol(providerSource.fileName, owner, ownerName(owner)) }
409
441
 
410
442
  const states = new Map()
443
+ const stateSymbols = new Map()
411
444
  const callbacks = new Map()
412
445
  const hasUseState = hasFrameworkImport(providerSource, "useState")
413
446
  const collectProviderBindings = node => {
414
447
  if (ts.isVariableDeclaration(node) && nearestFunction(node) === owner) {
415
448
  if (hasUseState && ts.isArrayBindingPattern(node.name) && node.initializer && ts.isCallExpression(node.initializer) && ts.isIdentifier(node.initializer.expression) && node.initializer.expression.text === "useState") {
416
449
  const [state, setter] = node.name.elements
417
- if (node.name.elements.length === 2 && state && setter && ts.isBindingElement(state) && ts.isBindingElement(setter) && ts.isIdentifier(state.name) && ts.isIdentifier(setter.name)) states.set(setter.name.text, state.name.text)
450
+ if (node.name.elements.length === 2 && state && setter && ts.isBindingElement(state) && ts.isBindingElement(setter) && ts.isIdentifier(state.name) && ts.isIdentifier(setter.name)) {
451
+ states.set(setter.name.text, state.name.text)
452
+ stateSymbols.set(state.name.text, modules.symbol(providerSource.fileName, state.name, state.name.text))
453
+ }
418
454
  }
419
455
  if (ts.isIdentifier(node.name) && node.initializer && (ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer))) callbacks.set(node.name.text, node.initializer)
420
456
  }
@@ -443,7 +479,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
443
479
  if (!setter || !fields.has(state) || !fields.has(setter)) throw sourceNodeError(callback, providerSource, `Context action ${JSON.stringify(name)} requires exposed state and setter fields for ${JSON.stringify(state)}`)
444
480
  }
445
481
  }
446
- return { callbacks: new Map([...callbacks].filter(([name]) => fields.has(name))), context: true, fields, privateStates: new Set(), stateOwner, states }
482
+ return { callbacks: new Map([...callbacks].filter(([name]) => fields.has(name))), context: true, fields, privateStates: new Set(), stateOwner, stateSymbols, states }
447
483
  }
448
484
 
449
485
  const resolveCustomHook = (binding, call) => {
@@ -518,7 +554,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
518
554
  if (!names.has(state) && !requiredContextStates.has(state)) continue
519
555
  const localSetter = names.has(setter) || requiredContextStates.has(state) ? setter : `__kContextState_${state}`
520
556
  setters.set(localSetter, state)
521
- registerState(owner, state, localSetter, "context", node, hook.stateOwner)
557
+ registerState(owner, state, localSetter, "context", node, { owner: hook.stateOwner, state: hook.stateSymbols.get(state) })
522
558
  if (requiredContextStates.has(state)) {
523
559
  const fields = customHookPrivateFields.get(node) ?? []
524
560
  for (const field of [state, setter]) {
@@ -813,6 +849,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
813
849
  return expanded
814
850
  }
815
851
  const componentSpecializations = new WeakMap()
852
+ const specializedEffectStateOwners = new WeakMap()
816
853
  const setterHookHelpers = new WeakMap()
817
854
  const expandedRowSpecializations = new WeakMap()
818
855
  const nestedRowSpecializations = new Map()
@@ -833,11 +870,11 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
833
870
  if (ts.isIdentifier(expression) && setters.has(expression.text)) signals.add(setters.get(expression.text))
834
871
  const callback = ts.isIdentifier(expression) ? callbacks.get(expression.text) : undefined
835
872
  for (const state of referencedStateNames((callback ?? expression).body ?? callback ?? expression, setters, callback ?? expression, bindingIndex)) signals.add(state)
836
- return [...signals].map(name => ({ name, owner: stateOwners.get(name) ?? (owner ? `owner:${ensureOwner(owner).slot}` : "module") }))
873
+ return [...signals].map(name => descriptors.signal(name, expression, stateOwners))
837
874
  }
838
875
  result.analysis = componentAnalysis.registerSpecialization({
839
876
  kind: label,
840
- ...(owner ? { owner: ensureOwner(owner).slot } : {}),
877
+ ...(owner ? { owner: { kind: "component", slot: ensureOwner(owner).slot } } : {}),
841
878
  ...(analysisSite(call, "component-call") ? { site: analysisSite(call, "component-call") } : {}),
842
879
  ...(analysisSource(call) ? { source: analysisSource(call) } : {}),
843
880
  props: result.props.map(prop => {
@@ -852,8 +889,18 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
852
889
  refs: [...result.rowRefs.map(({ name, source }) => ({ name, kind: "row", ...(analysisSite(source, "hook") ? { site: analysisSite(source, "hook") } : {}), ...(analysisSource(source) ? { source: analysisSource(source) } : {}) })), ...result.ordinaryRefs.map(({ name, source }) => ({ name, kind: "component", ...(analysisSite(source, "hook") ? { site: analysisSite(source, "hook") } : {}), ...(analysisSource(source) ? { source: analysisSource(source) } : {}) }))],
853
890
  ids: result.ordinaryIds.map(({ name, source }) => ({ name, ...(analysisSite(source, "hook") ? { site: analysisSite(source, "hook") } : {}), ...(analysisSource(source) ? { source: analysisSource(source) } : {}) }))
854
891
  })
855
- for (const state of [...result.rowStates, ...result.ordinaryStates]) state.analysisOwner = `specialization:${result.analysis.slot}`
856
- for (const ref of [...result.rowRefs, ...result.ordinaryRefs]) ref.analysisOwner = `specialization:${result.analysis.slot}`
892
+ result.propStateOwners = new Map(result.props.flatMap(prop => {
893
+ const expression = result.propExpressions.get(prop.name)
894
+ const value = expression && unwrapExpression(expression)
895
+ const reference = value && ts.isIdentifier(value) ? stateOwners.get(value.text) : undefined
896
+ return reference ? [[prop.local, reference]] : []
897
+ }))
898
+ for (const effect of result.effects) {
899
+ effect.stateOwners = result.propStateOwners
900
+ effect.analysisOwner = { kind: "specialization", slot: result.analysis.slot }
901
+ }
902
+ for (const [slot, state] of [...result.rowStates, ...result.ordinaryStates].entries()) state.analysisReference = { kind: "state", owner: { kind: "specialization", slot: result.analysis.slot }, slot }
903
+ for (const [slot, ref] of [...result.rowRefs, ...result.ordinaryRefs].entries()) ref.analysisReference = { specialization: result.analysis.slot, ref: slot }
857
904
  return result
858
905
  }
859
906
  const registerRowHooks = (call, specialization) => {
@@ -870,7 +917,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
870
917
  const stateOwners = new Map(stateOwnersByFunction.get(owner))
871
918
  for (const state of specialization.rowStates) {
872
919
  setters.set(state.setter, state.state)
873
- stateOwners.set(state.state, state.analysisOwner)
920
+ stateOwners.set(state.state, state.analysisReference)
874
921
  }
875
922
  settersByFunction.set(owner, setters)
876
923
  stateOwnersByFunction.set(owner, stateOwners)
@@ -911,7 +958,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
911
958
  merged.parent = root.parent
912
959
  return merged
913
960
  }
914
- const expandReducerCallbacks = (root, componentSource, call) => {
961
+ const expandReducerCallbacks = (root, componentSource, call, ownership) => {
915
962
  const componentImports = clientImportBindings(componentSource, componentSource.fileName, sourceFiles)
916
963
  const replacements = new WeakMap()
917
964
  let count = 0
@@ -927,7 +974,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
927
974
  fail(nestedCalls[0], "Reducer callback props require a component imported from a relative TypeScript module")
928
975
  }
929
976
  for (const nestedCall of nestedCalls) {
930
- const nested = specialize(nestedCall, nestedComponent, "Reducer-callback")
977
+ const nested = specialize(nestedCall, nestedComponent, "Reducer-callback", false, false, new Set(), ownership)
931
978
  if (nested.effects.length) fail(nestedCall, "Reducer-callback components cannot declare effects")
932
979
  nested.root = mergeSpecializedImports(nested.root, nestedComponent.getSourceFile(), nestedCall, nested.effects)
933
980
  synthesizeTree(nested.root)
@@ -1001,7 +1048,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1001
1048
  const setters = new Map(parentSetters)
1002
1049
  for (const state of aggregate.ordinaryStates) setters.set(state.setter, state.state)
1003
1050
  const stateOwners = new Map(parentStateOwners)
1004
- for (const state of aggregate.ordinaryStates) stateOwners.set(state.state, state.analysisOwner)
1051
+ for (const state of aggregate.ordinaryStates) stateOwners.set(state.state, state.analysisReference)
1005
1052
  if (jsxSetterCallbackProps(node, setters, functionsForNode(node), reducersForNode(node, reducersByFunction)).length) fail(node, "Setter callbacks cannot cross a second component boundary")
1006
1053
  const nested = specialize(node, component, "Nested setter-callback", true, true, new Set(setters.values()), { setters, stateOwners })
1007
1054
  if (dynamic && (nested.hookDeclarations.length || nested.effects.length)) fail(node, "Hookful nested setter-callback components require an unconditional or statically truthy render path")
@@ -1099,6 +1146,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1099
1146
  const effectCall = factory.updateCallExpression(entry.call, factory.createIdentifier("__kComponentUseEffect"), entry.call.typeArguments, entry.call.arguments)
1100
1147
  synthesizeTree(effectCall)
1101
1148
  ts.setOriginalNode(effectCall, entry.source)
1149
+ specializedEffectStateOwners.set(effectCall, { owner: { kind: "specialization", slot: specialization.analysis.slot }, references: specialization.propStateOwners })
1102
1150
  return factory.createExpressionStatement(effectCall)
1103
1151
  })
1104
1152
  const helper = factory.createFunctionDeclaration(
@@ -1118,8 +1166,8 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1118
1166
  const setters = new Map(settersForNode(call, settersByFunction))
1119
1167
  for (const state of specialization.ordinaryStates) setters.set(state.setter, state.state)
1120
1168
  settersByFunction.set(helper, setters)
1121
- const stateOwners = new Map(stateOwnersForNode(call))
1122
- for (const state of specialization.ordinaryStates) stateOwners.set(state.state, state.analysisOwner)
1169
+ const stateOwners = new Map([...stateOwnersForNode(call), ...specialization.propStateOwners])
1170
+ for (const state of specialization.ordinaryStates) stateOwners.set(state.state, state.analysisReference)
1123
1171
  stateOwnersByFunction.set(helper, stateOwners)
1124
1172
  usesComponentState ||= specialization.ordinaryStates.length > 0
1125
1173
  usesComponentId ||= specialization.usesComponentId
@@ -1162,7 +1210,10 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1162
1210
  if (componentSpecializations.has(call)) fail(call, "Reducer dispatch props cannot be combined with another component specialization")
1163
1211
  const specialization = specialize(call, component.function, "Reducer-dispatch")
1164
1212
  registerRowHooks(call, specialization)
1165
- specialization.root = expandReducerCallbacks(specialization.root, component.function.getSourceFile(), call)
1213
+ specialization.root = expandReducerCallbacks(specialization.root, component.function.getSourceFile(), call, {
1214
+ setters: new Map([...settersForNode(call, settersByFunction), ...[...specialization.rowStates, ...specialization.ordinaryStates].map(state => [state.setter, state.state])]),
1215
+ stateOwners: new Map([...stateOwnersForNode(call), ...[...specialization.rowStates, ...specialization.ordinaryStates].map(state => [state.state, state.analysisReference])])
1216
+ })
1166
1217
  componentSpecializations.set(call, specialization)
1167
1218
  reducerComponentCalls.add(call)
1168
1219
  }
@@ -1185,7 +1236,10 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1185
1236
  if (componentSpecializations.has(call)) fail(call, "Reducer dispatch props cannot be combined with another component specialization")
1186
1237
  const specialization = specialize(call, component, "Reducer-dispatch")
1187
1238
  registerRowHooks(call, specialization)
1188
- specialization.root = expandReducerCallbacks(specialization.root, componentSource, call)
1239
+ specialization.root = expandReducerCallbacks(specialization.root, componentSource, call, {
1240
+ setters: new Map([...settersForNode(call, settersByFunction), ...[...specialization.rowStates, ...specialization.ordinaryStates].map(state => [state.setter, state.state])]),
1241
+ stateOwners: new Map([...stateOwnersForNode(call), ...[...specialization.rowStates, ...specialization.ordinaryStates].map(state => [state.state, state.analysisReference])])
1242
+ })
1189
1243
  specialization.root = mergeSpecializedImports(specialization.root, componentSource, call, specialization.effects)
1190
1244
  synthesizeTree(specialization.root)
1191
1245
  componentSpecializations.set(call, specialization)
@@ -1337,6 +1391,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1337
1391
  const call = factory.updateCallExpression(entry.call, factory.createIdentifier("__kListUseEffect"), entry.call.typeArguments, entry.call.arguments)
1338
1392
  synthesizeTree(call)
1339
1393
  ts.setOriginalNode(call, entry.source)
1394
+ specializedEffectStateOwners.set(call, { owner: entry.analysisOwner, references: entry.stateOwners ?? specialization.propStateOwners })
1340
1395
  return factory.createExpressionStatement(call)
1341
1396
  }))
1342
1397
  }
@@ -1375,7 +1430,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1375
1430
  specializations: [specialization.analysis?.slot, ...(specialization.specializations ?? [])].filter(slot => slot !== undefined),
1376
1431
  rowStates: [...specialization.rowStates, ...specialization.ordinaryStates],
1377
1432
  rowRefs: specialization.rowRefs,
1378
- analysisStateOwners: new Map([...stateOwnersForNode(originalParts.root), ...[...specialization.rowStates, ...specialization.ordinaryStates].map(state => [state.state, state.analysisOwner])])
1433
+ analysisStateOwners: new Map([...stateOwnersForNode(originalParts.root), ...(specialization.propStateOwners ?? []), ...[...specialization.rowStates, ...specialization.ordinaryStates].map(state => [state.state, state.analysisReference])])
1379
1434
  }
1380
1435
  for (const calculation of specialization.calculations) {
1381
1436
  ts.setParentRecursive(calculation, false)
@@ -1404,8 +1459,6 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1404
1459
  )
1405
1460
  }
1406
1461
 
1407
- let activeStateOwners
1408
- let activeKeyedBlock
1409
1462
  const visitWithStateOwners = (node, stateOwners) => {
1410
1463
  const previous = activeStateOwners
1411
1464
  activeStateOwners = new Map([...(previous ?? []), ...stateOwners])
@@ -1419,18 +1472,23 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1419
1472
  usesList = true
1420
1473
  const blockSlot = moduleIR.keyedBlocks.length
1421
1474
  let listSource = listParts.state
1422
- let collection = { kind: "signal", name: listParts.state?.text }
1475
+ const collectionName = listParts.state?.text
1476
+ const collectionReference = collectionName && new Map([...stateOwnersForNode(node), ...(activeStateOwners ?? [])]).get(collectionName)
1477
+ const collectionSymbol = listParts.state && bindingIndex.resolveReference(listParts.state, node)?.slot
1478
+ let collection = collectionReference
1479
+ ? { kind: "signal", signal: descriptors.signal(collectionName, node, new Map([[collectionName, collectionReference]])) }
1480
+ : collectionSymbol !== undefined ? { kind: "symbol", symbol: collectionSymbol } : { kind: "static" }
1423
1481
  if (listParts.calculation) {
1424
1482
  usesBinding = true
1425
- listSource = descriptors.compileReactiveBinding(listParts.calculation, { setters: settersForNode(node, settersByFunction), importBindings, keyedBlock: blockSlot })
1426
- const exportName = ts.isCallExpression(listSource) && ts.isStringLiteral(listSource.arguments[2]) ? listSource.arguments[2].text : undefined
1427
- collection = { kind: "binding", ...(exportName ? { exportName } : {}) }
1483
+ const compiled = descriptors.compileReactiveBinding(listParts.calculation, { setters: settersForNode(node, settersByFunction), importBindings, keyedBlock: blockSlot })
1484
+ listSource = compiled.node
1485
+ collection = { kind: "binding", binding: compiled.binding }
1428
1486
  }
1429
1487
  const derived = listParts.selector?.length ? descriptors.registerDerived("selector", listParts.selector, listParts.selectorStates, node) : undefined
1430
1488
  const parent = activeKeyedBlock?.block
1431
- const rowStates = (listParts.rowStates ?? []).map(state => ({ name: state.state, setter: state.setter, owner: state.analysisOwner, ...(analysisSource(state.source) ? { source: analysisSource(state.source) } : {}) }))
1432
- const rowRefs = (listParts.rowRefs ?? []).map(ref => ({ name: ref.name, owner: ref.analysisOwner, ...(analysisSource(ref.source) ? { source: analysisSource(ref.source) } : {}) }))
1433
- const specializations = [...new Set([...(listParts.specializations ?? []), ...rowStates.map(state => state.owner), ...rowRefs.map(ref => ref.owner)].filter(value => value !== undefined).map(value => typeof value === "string" ? Number(value.slice(value.lastIndexOf(":") + 1)) : value))]
1489
+ const rowStates = (listParts.rowStates ?? []).map(state => ({ name: state.state, setter: state.setter, signal: descriptors.signal(state.state, node, new Map([[state.state, state.analysisReference]])), ...(analysisSource(state.source) ? { source: analysisSource(state.source) } : {}) }))
1490
+ const rowRefs = (listParts.rowRefs ?? []).map(ref => ({ name: ref.name, ...ref.analysisReference, ...(analysisSource(ref.source) ? { source: analysisSource(ref.source) } : {}) }))
1491
+ const specializations = [...new Set([...(listParts.specializations ?? []), ...rowStates.map(state => moduleIR.signals[state.signal].reference.owner?.slot), ...rowRefs.map(ref => ref.specialization)].filter(value => value !== undefined))]
1434
1492
  const block = descriptors.registerKeyedBlock({
1435
1493
  ...(analysisSite(node, "keyed-list") ? { site: analysisSite(node, "keyed-list") } : {}),
1436
1494
  ...(analysisSource(node) ? { source: analysisSource(node) } : {}),
@@ -1444,7 +1502,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1444
1502
  indexed: listParts.indexed,
1445
1503
  static: Boolean(listParts.static),
1446
1504
  ...(derived ? { selector: derived.slot } : {}),
1447
- selectorStates: [...(listParts.selectorStates ?? [])],
1505
+ selectorSignals: [...(listParts.selectorStates ?? [])].map(name => descriptors.signal(name, node)),
1448
1506
  specializations,
1449
1507
  rowStates,
1450
1508
  rowRefs
@@ -1462,7 +1520,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1462
1520
  jsonExpression(derived?.selector ?? listParts.selector ?? [], factory),
1463
1521
  block.indexed ? factory.createTrue() : factory.createFalse()
1464
1522
  ]
1465
- if (block.selectorStates.length || block.static) arguments_.push(factory.createArrayLiteralExpression(block.selectorStates.map(name => factory.createArrayLiteralExpression([factory.createStringLiteral(name), factory.createIdentifier(name)]))))
1523
+ if (block.selectorSignals.length || block.static) arguments_.push(factory.createArrayLiteralExpression([...(listParts.selectorStates ?? [])].map(name => factory.createArrayLiteralExpression([factory.createStringLiteral(name), factory.createIdentifier(name)]))))
1466
1524
  if (block.static) arguments_.push(factory.createTrue())
1467
1525
  return factory.updateJsxExpression(node, factory.createCallExpression(factory.createIdentifier("__kList"), undefined, arguments_))
1468
1526
  }
@@ -1480,7 +1538,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1480
1538
  if (specializedDeclarations.has(node)) return node
1481
1539
  if (componentSpecializations.has(node)) {
1482
1540
  const specialization = componentSpecializations.get(node)
1483
- const stateOwners = new Map([...stateOwnersForNode(node), ...[...specialization.rowStates, ...specialization.ordinaryStates].map(state => [state.state, state.analysisOwner])])
1541
+ const stateOwners = new Map([...stateOwnersForNode(node), ...(specialization.propStateOwners ?? []), ...[...specialization.rowStates, ...specialization.ordinaryStates].map(state => [state.state, state.analysisReference])])
1484
1542
  return visitWithStateOwners(specialization.root, stateOwners)
1485
1543
  }
1486
1544
 
@@ -1595,6 +1653,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1595
1653
  importBindings: specializedEffect?.imports ?? importBindings,
1596
1654
  listItem: dependencyItem,
1597
1655
  keyedBlock: activeKeyedBlock?.block.slot,
1656
+ stateOwners: new Map([...stateOwnersForNode(callbackArgument), ...stateOwnersForNode(node), ...(specializedEffectStateOwners.get(node)?.references ?? []), ...(activeStateOwners ?? [])]),
1598
1657
  deferValues: true,
1599
1658
  snapshotNested: returns.cleanup,
1600
1659
  liveStates: customHookTimerStates
@@ -1604,14 +1663,19 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1604
1663
  const derivedDependencies = hasDerivedDependency ? dependencyEntries.map(entry => entry.kind === "derived" ? descriptors.registerDerived("expression", entry.expression, entry.states, entry.source) : undefined) : []
1605
1664
  const effectSource = specializedEffect?.source ?? node
1606
1665
  const lexicalOwner = nearestFunction(effectSource)
1666
+ const effectStateOwners = new Map([...stateOwnersForNode(callbackArgument), ...stateOwnersForNode(node), ...(specializedEffectStateOwners.get(node)?.references ?? []), ...(activeStateOwners ?? [])])
1667
+ const signalFor = name => descriptors.signal(name, node, effectStateOwners)
1668
+ const subscriptionNames = (hasDerivedDependency ? subscriptionDependencies : ordinaryDependencies).map(dependency => dependency.text)
1669
+ const dependencyStateNames = [...dependencyStates.keys()]
1607
1670
  const effect = descriptors.registerEffect(descriptor, {
1608
1671
  cleanup: returns.cleanup,
1609
- dependencies: hasDerivedDependency ? dependencyEntries.map((entry, index) => entry.kind === "derived" ? { kind: "derived", derived: derivedDependencies[index].slot, sources: [...entry.states] } : { kind: "signal", name: entry.name }) : ordinaryDependencies.map(dependency => ({ kind: "signal", name: dependency.text })),
1610
- subscriptions: (hasDerivedDependency ? subscriptionDependencies : ordinaryDependencies).map(dependency => dependency.text),
1611
- dependencyStates: [...dependencyStates.keys()],
1672
+ dependencies: hasDerivedDependency ? dependencyEntries.map((entry, index) => entry.kind === "derived" ? { kind: "derived", derived: derivedDependencies[index].slot, sources: [...entry.states].map(signalFor) } : { kind: "signal", signal: signalFor(entry.name) }) : ordinaryDependencies.map(dependency => ({ kind: "signal", signal: signalFor(dependency.text) })),
1673
+ subscriptions: subscriptionNames.map(signalFor),
1674
+ dependencySignals: dependencyStateNames.map(signalFor),
1612
1675
  itemDependencies,
1613
1676
  ownership: {
1614
1677
  kind: activeKeyedBlock ? "keyed" : "component",
1678
+ owner: specializedEffectStateOwners.get(node)?.owner ?? fallbackOwner(effectSource),
1615
1679
  ...(activeKeyedBlock ? { keyedBlock: activeKeyedBlock.block.slot } : {}),
1616
1680
  ...(lexicalOwner ? { component: { name: ownerName(lexicalOwner), ...(analysisSite(lexicalOwner, "owner") ? { site: analysisSite(lexicalOwner, "owner") } : {}), ...(analysisSource(lexicalOwner) ? { source: analysisSource(lexicalOwner) } : {}) } } : {})
1617
1681
  },
@@ -1619,10 +1683,10 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1619
1683
  ...(analysisSite(effectSource, "hook") ? { site: analysisSite(effectSource, "hook") } : {}),
1620
1684
  ...(analysisSource(effectSource) ? { source: analysisSource(effectSource) } : {})
1621
1685
  })
1622
- const dependencyExpressions = effect.dependencies.map(dependency => dependency.kind === "derived" ? moduleIR.derived[dependency.derived].expression : ["state", dependency.name])
1686
+ const dependencyExpressions = effect.dependencies.map((dependency, index) => dependency.kind === "derived" ? moduleIR.derived[dependency.derived].expression : ["state", dependencyEntries[index]?.name ?? ordinaryDependencies[index].text])
1623
1687
  return factory.updateCallExpression(node, node.expression, node.typeArguments, [
1624
1688
  callback,
1625
- factory.createArrayLiteralExpression(effect.subscriptions.map(name => factory.createIdentifier(name))),
1689
+ factory.createArrayLiteralExpression(subscriptionNames.map(name => factory.createIdentifier(name))),
1626
1690
  factory.createStringLiteral(handlerUrl),
1627
1691
  factory.createStringLiteral(effect.setup.exportName),
1628
1692
  descriptor.states,
@@ -1631,7 +1695,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1631
1695
  effect.cleanup ? factory.createTrue() : factory.createFalse(),
1632
1696
  factory.createArrayLiteralExpression(effect.itemDependencies.map(field => factory.createStringLiteral(field))),
1633
1697
  hasDerivedDependency ? jsonExpression(dependencyExpressions, factory) : factory.createArrayLiteralExpression(),
1634
- factory.createArrayLiteralExpression(effect.dependencyStates.map(name => factory.createArrayLiteralExpression([factory.createStringLiteral(name), factory.createIdentifier(name)])))
1698
+ factory.createArrayLiteralExpression(dependencyStateNames.map(name => factory.createArrayLiteralExpression([factory.createStringLiteral(name), factory.createIdentifier(name)])))
1635
1699
  ])
1636
1700
  }
1637
1701
 
@@ -1707,7 +1771,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1707
1771
  if ((usedStates.size || captures.size) && !ts.isIdentifier(expression) && !containsJsx(expression)) {
1708
1772
  usesBehavior = true
1709
1773
  usesBinding = true
1710
- return factory.updateJsxExpression(node, descriptors.compileReactiveBinding(expression, { setters, importBindings }))
1774
+ return factory.updateJsxExpression(node, descriptors.compileReactiveBinding(expression, { setters, importBindings }).node)
1711
1775
  }
1712
1776
  }
1713
1777
 
@@ -1721,7 +1785,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1721
1785
  usesBehavior = true
1722
1786
  usesBinding = true
1723
1787
  const compiled = descriptors.compileReactiveBinding(expression, { setters, importBindings })
1724
- return factory.updateJsxAttribute(node, node.name, factory.createJsxExpression(undefined, compiled))
1788
+ return factory.updateJsxAttribute(node, node.name, factory.createJsxExpression(undefined, compiled.node))
1725
1789
  }
1726
1790
  }
1727
1791
 
@@ -1729,7 +1793,7 @@ function createKudzuTransformer({ semantic, handlerUrl, file, sourceFiles, sourc
1729
1793
  const setters = settersForNode(node, settersByFunction)
1730
1794
  const event = descriptors.compileEvent(node.initializer.expression, {
1731
1795
  owner: fallbackOwner(node),
1732
- stateOwners: activeStateOwners ?? stateOwnersForNode(node),
1796
+ stateOwners: new Map([...stateOwnersForNode(node), ...(activeStateOwners ?? [])]),
1733
1797
  setters,
1734
1798
  reducers: reducersForNode(node, reducersByFunction),
1735
1799
  functions: functionsForNode(node),
@@ -2023,7 +2087,7 @@ function validateKeyedList(parts, sourceFile, setters, rowStates, componentSpeci
2023
2087
  specializations: [specialization?.analysis?.slot, ...(specialization?.specializations ?? [])].filter(slot => slot !== undefined),
2024
2088
  rowStates: specializedStates,
2025
2089
  rowRefs: specialization?.rowRefs ?? [],
2026
- analysisStateOwners: new Map([...(parts.analysisStateOwners ?? []), ...specializedStates.map(state => [state.state, state.analysisOwner])])
2090
+ analysisStateOwners: new Map([...(parts.analysisStateOwners ?? []), ...(specialization?.propStateOwners ?? []), ...specializedStates.map(state => [state.state, state.analysisReference])])
2027
2091
  }
2028
2092
  for (const calculation of specialization?.calculations ?? []) {
2029
2093
  ts.setParentRecursive(calculation, false)
@@ -185,6 +185,6 @@ export function renderPage<Props = Record<string, never>>(
185
185
  hasLists: boolean
186
186
  hasListStyles: boolean
187
187
  hasStateSeed: boolean
188
- handlerModules: string[]
188
+ handlerReferences: Array<{ module: string; handler: string }>
189
189
  plan: RouteIR
190
190
  }>
@@ -207,8 +207,10 @@ export function useEffect(callback, dependencies, module, handler, states, scope
207
207
  owner = nextRenderId("e")
208
208
  owners.push(owner)
209
209
  }
210
- if (!renderContext.listDepth || list) renderContext.effects.push({ module, handler, states, scope, source, renderScope: renderContext.renderScope, ...(dependencyIds.length ? { dependencies: dependencyIds } : {}), ...(dependencyExpressions.length ? { dependencyExpressions, dependencyStates: dependencyStateIds } : {}), ...(itemDependencies.length ? { itemDependencies, listState: renderContext.listRoot.state } : {}), ...(cleanup ? { cleanup: true } : {}), ...(owner ? { owner } : {}), ...(list ? { list: true } : {}) })
211
- renderContext.handlerModules.add(module)
210
+ if (!renderContext.listDepth || list) {
211
+ renderContext.effects.push({ module, handler, states, scope, source, renderScope: renderContext.renderScope, ...(dependencyIds.length ? { dependencies: dependencyIds } : {}), ...(dependencyExpressions.length ? { dependencyExpressions, dependencyStates: dependencyStateIds } : {}), ...(itemDependencies.length ? { itemDependencies, listState: renderContext.listRoot.state } : {}), ...(cleanup ? { cleanup: true } : {}), ...(owner ? { owner } : {}), ...(list ? { list: true } : {}) })
212
+ retainHandlerReference(module, handler)
213
+ }
212
214
  renderContext.hasBehaviors = true
213
215
  renderContext.hasEffects = true
214
216
  }
@@ -254,7 +256,6 @@ export function behavior(commands) {
254
256
  }
255
257
 
256
258
  export function nativeBehavior(module, handler, states, scope) {
257
- renderContext?.handlerModules.add(module)
258
259
  return {
259
260
  [nativeBehaviorMarker]: true,
260
261
  module,
@@ -325,7 +326,6 @@ export function listField(read, field) {
325
326
  }
326
327
 
327
328
  export function listExpression(read, module, handler, states = []) {
328
- renderContext?.handlerModules.add(module)
329
329
  const stateMap = Object.fromEntries(states.map(([name, state]) => {
330
330
  if (!state?.[signalMarker] || !validEffectDependency(state.value)) throw new Error(`Derived keyed list item expression state ${JSON.stringify(name)} must be primitive Kudzu state`)
331
331
  return [name, state.id]
@@ -346,7 +346,6 @@ export function listIndex() {
346
346
  }
347
347
 
348
348
  export function listConditional(kind, read, truthy, falsy, module, handler) {
349
- renderContext?.handlerModules.add(module)
350
349
  return { [listConditionalMarker]: true, kind, value: renderContext?.listTemplate ? undefined : read(), truthy, falsy, module, handler }
351
350
  }
352
351
 
@@ -380,7 +379,6 @@ function assertListValue(value, seen) {
380
379
  }
381
380
 
382
381
  function reactiveDescriptor(module, handler, states, scope) {
383
- renderContext?.handlerModules.add(module)
384
382
  const scopeStates = {}
385
383
  const serializedScope = {}
386
384
  const scopeBindings = {}
@@ -402,6 +400,16 @@ function reactiveDescriptor(module, handler, states, scope) {
402
400
  }
403
401
  }
404
402
 
403
+ function retainHandlerReference(module, handler) {
404
+ const reference = { module, handler }
405
+ renderContext?.handlerReferences.set(JSON.stringify([module, handler]), reference)
406
+ }
407
+
408
+ function retainDescriptorHandlers(descriptor) {
409
+ if (descriptor?.module && descriptor.handler) retainHandlerReference(descriptor.module, descriptor.handler)
410
+ for (const nested of Object.values(descriptor?.scopeBindings ?? {})) retainDescriptorHandlers(nested)
411
+ }
412
+
405
413
  export function bindingValue(value) {
406
414
  return value?.[signalMarker] || value?.[bindingMarker] ? value.value : value
407
415
  }
@@ -449,7 +457,7 @@ function serializeCapture(name, value, seen) {
449
457
  }
450
458
 
451
459
  export async function renderPage(component, metadata = {}, props = {}, layout) {
452
- renderContext = { scoped: Boolean(layout), renderScope: layout ? "layout" : "route", counters: { layout: { s: 0, r: 0, c: 0, l: 0, e: 0, p: 0, i: 0 }, route: { s: 0, r: 0, c: 0, l: 0, e: 0, p: 0, i: 0 } }, nextState: 0, nextRef: 0, nextCondition: 0, nextList: 0, nextEffect: 0, nextParam: 0, nextId: 0, conditionDepth: 0, listDepth: 0, listRoot: undefined, listRowRoot: undefined, listTemplate: false, listInitialMarkers: false, listConditionalBranch: false, listFields: undefined, listEffectOwners: [], listRowStates: [], listRowRefs: [], listRowConditions: [], listRowLists: [], effectOwners: [], contexts: [], stores: new Map(), states: {}, textStates: new Set(), conditionStates: new Set(), conditionOwnedStates: new Set(), events: [], effects: [], bindings: [], textBindings: [], conditions: [], lists: [], handlerModules: new Set(), runtimeParamNames: metadata.runtimeParams, paramEntries: [], params: undefined, searchParams: new Map(), searchParamEntries: [], searchParamsWritable: false, hasBehaviors: false, hasNativeBehaviors: false, hasEffects: false, hasParams: false, hasBindings: false, hasLists: false, hasListStyles: false }
460
+ renderContext = { scoped: Boolean(layout), renderScope: layout ? "layout" : "route", counters: { layout: { s: 0, r: 0, c: 0, l: 0, e: 0, p: 0, i: 0 }, route: { s: 0, r: 0, c: 0, l: 0, e: 0, p: 0, i: 0 } }, nextState: 0, nextRef: 0, nextCondition: 0, nextList: 0, nextEffect: 0, nextParam: 0, nextId: 0, conditionDepth: 0, listDepth: 0, listRoot: undefined, listRowRoot: undefined, listTemplate: false, listInitialMarkers: false, listConditionalBranch: false, listFields: undefined, listEffectOwners: [], listRowStates: [], listRowRefs: [], listRowConditions: [], listRowLists: [], effectOwners: [], contexts: [], stores: new Map(), states: {}, textStates: new Set(), conditionStates: new Set(), conditionOwnedStates: new Set(), events: [], effects: [], bindings: [], textBindings: [], conditions: [], lists: [], handlerReferences: new Map(), runtimeParamNames: metadata.runtimeParams, paramEntries: [], params: undefined, searchParams: new Map(), searchParamEntries: [], searchParamsWritable: false, hasBehaviors: false, hasNativeBehaviors: false, hasEffects: false, hasParams: false, hasBindings: false, hasLists: false, hasListStyles: false }
453
461
 
454
462
  try {
455
463
  const page = { [routeScopeMarker]: true, component, props }
@@ -525,7 +533,7 @@ export async function renderPage(component, metadata = {}, props = {}, layout) {
525
533
  hasLists: renderContext.hasLists,
526
534
  hasListStyles: renderContext.hasListStyles,
527
535
  hasStateSeed: initialState.length > 0,
528
- handlerModules: [...renderContext.handlerModules],
536
+ handlerReferences: [...renderContext.handlerReferences.values()],
529
537
  plan: {
530
538
  version: 1,
531
539
  states: Object.entries(renderContext.states).map(([id, state], slot) => ({ slot, id, ...state })),
@@ -648,6 +656,7 @@ async function renderNode(node, namespace, selectValue = noSelectValue) {
648
656
  const metadata = { id, kind: node.kind, initial: node.value, ...descriptor, ...(namespace === "svg" ? { svg: true } : {}), ...(owned ? { owned } : {}), ...(mount ? { mount: true } : {}) }
649
657
  for (const stateId of stateIds) renderContext.conditionStates.add(stateId)
650
658
  renderContext.conditions.push(metadata)
659
+ retainDescriptorHandlers(metadata)
651
660
  renderContext.hasBehaviors = true
652
661
  renderContext.hasBindings = true
653
662
  const encoded = escapeJsonAttribute(metadata)
@@ -667,7 +676,9 @@ async function renderNode(node, namespace, selectValue = noSelectValue) {
667
676
  }
668
677
  if (node?.[listExpressionMarker]) {
669
678
  const descriptor = { module: node.module, handler: node.handler, ...(Object.keys(node.states).length ? { states: node.states } : {}) }
670
- const marker = renderContext.listTemplate || renderContext.listInitialMarkers || renderContext.listConditionalBranch ? ` data-k-list-expression='${escapeJsonAttribute(descriptor)}'` : ""
679
+ const retained = renderContext.listTemplate || renderContext.listInitialMarkers || renderContext.listConditionalBranch
680
+ if (retained) retainDescriptorHandlers(descriptor)
681
+ const marker = retained ? ` data-k-list-expression='${escapeJsonAttribute(descriptor)}'` : ""
671
682
  return `<template${marker}></template>${escapeHtml(node.value ?? "")}<template data-k-list-expression-end></template>`
672
683
  }
673
684
  if (node?.[bindingMarker]) {
@@ -675,6 +686,7 @@ async function renderNode(node, namespace, selectValue = noSelectValue) {
675
686
  const reactive = reactiveStateIds(descriptor).size > 0
676
687
  if (!reactive) return renderNode(node.value, namespace, selectValue)
677
688
  renderContext.bindings.push({ target: "text", ...descriptor })
689
+ retainDescriptorHandlers(descriptor)
678
690
  if (renderContext.conditionDepth || renderContext.listDepth) for (const stateId of reactiveStateIds(descriptor)) renderContext.conditionStates.add(stateId)
679
691
  renderContext.hasBehaviors = true
680
692
  renderContext.hasBindings = true
@@ -685,6 +697,7 @@ async function renderNode(node, namespace, selectValue = noSelectValue) {
685
697
  if (node?.[listConditionalMarker]) {
686
698
  if (namespace === "svg") throw new Error("Keyed row conditions are not supported inside svg")
687
699
  const descriptor = { kind: node.kind, module: node.module, handler: node.handler }
700
+ retainDescriptorHandlers(descriptor)
688
701
  const owner = renderContext.listRoot ?? renderContext.listRowRoot
689
702
  if (owner) owner.conditions = true
690
703
  const previousBranch = renderContext.listConditionalBranch
@@ -800,6 +813,7 @@ async function renderNode(node, namespace, selectValue = noSelectValue) {
800
813
  const native = template
801
814
  attributes += ` data-k-native-${event}='${escapeJsonAttribute(native)}'`
802
815
  renderContext.events.push({ event, native })
816
+ retainDescriptorHandlers(native)
803
817
  if (renderContext.listDepth && Object.values(template.scope).some(entry => entry?.type === "list-item" || entry?.type === "list-index")) listEvents.push([event, template])
804
818
  renderContext.hasNativeBehaviors = true
805
819
  } else {
@@ -817,9 +831,10 @@ async function renderNode(node, namespace, selectValue = noSelectValue) {
817
831
  if (name === "style") renderContext.hasListStyles = true
818
832
  continue
819
833
  }
820
- if (value?.[listExpressionMarker]) {
834
+ if (value?.[listExpressionMarker]) {
821
835
  attributes += renderAttribute(name, value.value)
822
- listExpressionAttributes.push([name, value.module, value.handler, ...(Object.keys(value.states).length ? [value.states] : [])])
836
+ listExpressionAttributes.push([name, value.module, value.handler, ...(Object.keys(value.states).length ? [value.states] : [])])
837
+ if (renderContext.listTemplate || renderContext.listInitialMarkers || renderContext.listConditionalBranch) retainHandlerReference(value.module, value.handler)
823
838
  if (name === "style") renderContext.hasListStyles = true
824
839
  continue
825
840
  }
@@ -837,6 +852,7 @@ async function renderNode(node, namespace, selectValue = noSelectValue) {
837
852
  if (propertyTarget) attributes += ` data-k-bind-${name}='${escapeJsonAttribute(descriptor)}'`
838
853
  else attributeBindings.push({ target: name, ...descriptor })
839
854
  renderContext.bindings.push({ target: name, ...descriptor })
855
+ retainDescriptorHandlers(descriptor)
840
856
  if (renderContext.conditionDepth || renderContext.listDepth) for (const stateId of reactiveStateIds(descriptor)) renderContext.conditionStates.add(stateId)
841
857
  renderContext.hasBehaviors = true
842
858
  renderContext.hasBindings = true
@@ -944,6 +960,7 @@ async function renderList(node, namespace, selectValue) {
944
960
  }
945
961
  if (!node.ownerField || ownerTemplate && !rowList.planned) {
946
962
  renderContext.lists.push(descriptor)
963
+ retainDescriptorHandlers(descriptor.source)
947
964
  if (rowList) rowList.planned = true
948
965
  }
949
966
  renderContext.hasBehaviors = true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kudzujs/core",
3
- "version": "0.8.36",
3
+ "version": "0.8.38",
4
4
  "description": "HTML-first TSX framework with synchronous state semantics and no virtual DOM",
5
5
  "type": "module",
6
6
  "license": "MIT",