@barefootjs/jsx 0.25.0 → 0.26.0

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.
Files changed (41) hide show
  1. package/dist/adapters/dangerous-inner-html.d.ts +52 -24
  2. package/dist/adapters/dangerous-inner-html.d.ts.map +1 -1
  3. package/dist/analyzer.d.ts.map +1 -1
  4. package/dist/index.js +248 -110
  5. package/dist/ir-to-client-js/collect-elements.d.ts.map +1 -1
  6. package/dist/ir-to-client-js/control-flow/plan/build-loop-child-arm.d.ts +15 -1
  7. package/dist/ir-to-client-js/control-flow/plan/build-loop-child-arm.d.ts.map +1 -1
  8. package/dist/ir-to-client-js/control-flow/plan/build-reactive-effects.d.ts +9 -6
  9. package/dist/ir-to-client-js/control-flow/plan/build-reactive-effects.d.ts.map +1 -1
  10. package/dist/ir-to-client-js/control-flow/plan/loop-child-arm.d.ts +11 -5
  11. package/dist/ir-to-client-js/control-flow/plan/loop-child-arm.d.ts.map +1 -1
  12. package/dist/ir-to-client-js/control-flow/stringify/loop-child-arm.d.ts +27 -1
  13. package/dist/ir-to-client-js/control-flow/stringify/loop-child-arm.d.ts.map +1 -1
  14. package/dist/ir-to-client-js/control-flow/stringify/reactive-effects.d.ts.map +1 -1
  15. package/dist/ir-to-client-js/reactivity.d.ts +24 -2
  16. package/dist/ir-to-client-js/reactivity.d.ts.map +1 -1
  17. package/dist/ir-to-client-js/types.d.ts +13 -0
  18. package/dist/ir-to-client-js/types.d.ts.map +1 -1
  19. package/dist/to-locale-date-lowering.d.ts +31 -10
  20. package/dist/to-locale-date-lowering.d.ts.map +1 -1
  21. package/dist/types.d.ts +8 -4
  22. package/dist/types.d.ts.map +1 -1
  23. package/package.json +2 -2
  24. package/src/__tests__/dangerous-inner-html-resolver.test.ts +27 -7
  25. package/src/__tests__/nested-loop-conditional.test.ts +133 -0
  26. package/src/__tests__/profile-nested-binding-ids.test.ts +5 -2
  27. package/src/__tests__/reactive-factory-cross-file.test.ts +252 -1
  28. package/src/__tests__/to-locale-date-lowering.test.ts +27 -2
  29. package/src/adapters/dangerous-inner-html.ts +101 -48
  30. package/src/analyzer.ts +222 -58
  31. package/src/ir-to-client-js/collect-elements.ts +28 -2
  32. package/src/ir-to-client-js/control-flow/plan/build-loop-child-arm.ts +56 -2
  33. package/src/ir-to-client-js/control-flow/plan/build-reactive-effects.ts +30 -54
  34. package/src/ir-to-client-js/control-flow/plan/loop-child-arm.ts +11 -5
  35. package/src/ir-to-client-js/control-flow/stringify/loop-child-arm.ts +78 -4
  36. package/src/ir-to-client-js/control-flow/stringify/reactive-effects.ts +3 -25
  37. package/src/ir-to-client-js/reactivity.ts +61 -7
  38. package/src/ir-to-client-js/types.ts +13 -0
  39. package/src/rich-type-refusal.ts +3 -2
  40. package/src/to-locale-date-lowering.ts +50 -13
  41. package/src/types.ts +8 -4
package/dist/index.js CHANGED
@@ -6956,6 +6956,37 @@ function extractFreeIdentifiersFromNode(node) {
6956
6956
  visit2(node);
6957
6957
  return ids;
6958
6958
  }
6959
+ function extractFreeTypeIdentifiersFromNode(node) {
6960
+ const ids = new Set;
6961
+ const boundTypeParams = new Set;
6962
+ function rootName(name) {
6963
+ return ts8.isQualifiedName(name) ? rootName(name.left) : name;
6964
+ }
6965
+ function visit2(n) {
6966
+ if (ts8.isTypeReferenceNode(n)) {
6967
+ const name = rootName(n.typeName).text;
6968
+ if (!boundTypeParams.has(name))
6969
+ ids.add(name);
6970
+ }
6971
+ if (ts8.isTypeQueryNode(n)) {
6972
+ const name = rootName(n.exprName).text;
6973
+ if (!boundTypeParams.has(name))
6974
+ ids.add(name);
6975
+ }
6976
+ if (ts8.isFunctionLike(n) && n.typeParameters && n.typeParameters.length > 0) {
6977
+ const names = n.typeParameters.map((p) => p.name.text);
6978
+ for (const p of names)
6979
+ boundTypeParams.add(p);
6980
+ ts8.forEachChild(n, visit2);
6981
+ for (const p of names)
6982
+ boundTypeParams.delete(p);
6983
+ return;
6984
+ }
6985
+ ts8.forEachChild(n, visit2);
6986
+ }
6987
+ visit2(node);
6988
+ return ids;
6989
+ }
6959
6990
  function initializerShapeContainsJsx(node) {
6960
6991
  let found = false;
6961
6992
  function visit2(n) {
@@ -7800,16 +7831,17 @@ function buildEntryImportIndex(sf, filePath) {
7800
7831
  continue;
7801
7832
  if (!ts8.isStringLiteral(stmt.moduleSpecifier))
7802
7833
  continue;
7803
- if (stmt.importClause?.isTypeOnly)
7804
- continue;
7805
7834
  const src = stmt.moduleSpecifier.text;
7806
7835
  const targetKey = src.startsWith("./") || src.startsWith("../") ? resolveRelativeImportToFile(src, filePath) ?? "unresolved:" + src : src;
7836
+ const wholeTypeOnly = stmt.importClause?.isTypeOnly === true;
7807
7837
  const namedBindings = stmt.importClause?.namedBindings;
7808
7838
  if (namedBindings && ts8.isNamedImports(namedBindings)) {
7809
7839
  for (const el of namedBindings.elements) {
7810
- if (el.isTypeOnly)
7811
- continue;
7812
- index.set(el.name.text, { targetKey, exportedName: (el.propertyName ?? el.name).text });
7840
+ index.set(el.name.text, {
7841
+ targetKey,
7842
+ exportedName: (el.propertyName ?? el.name).text,
7843
+ isTypeOnly: wholeTypeOnly || el.isTypeOnly
7844
+ });
7813
7845
  }
7814
7846
  }
7815
7847
  }
@@ -7839,6 +7871,9 @@ function collectEntryBindingNames(sf) {
7839
7871
  if ((ts8.isFunctionDeclaration(node) || ts8.isClassDeclaration(node) || ts8.isEnumDeclaration(node)) && node.name) {
7840
7872
  names.add(node.name.text);
7841
7873
  }
7874
+ if ((ts8.isTypeAliasDeclaration(node) || ts8.isInterfaceDeclaration(node)) && node.name) {
7875
+ names.add(node.name.text);
7876
+ }
7842
7877
  if (ts8.isFunctionLike(node)) {
7843
7878
  for (const p of node.parameters) {
7844
7879
  const out = [];
@@ -8034,7 +8069,11 @@ function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
8034
8069
  const required = [];
8035
8070
  const pending = [];
8036
8071
  let declinedEntry = null;
8037
- for (const ref of capture.importedRefs) {
8072
+ const allRefs = [
8073
+ ...capture.importedRefs.map((r) => ({ ...r, isTypeOnly: false })),
8074
+ ...capture.importedTypeRefs.map((r) => ({ ...r, isTypeOnly: true }))
8075
+ ];
8076
+ for (const ref of allRefs) {
8038
8077
  let specifier;
8039
8078
  let targetKey;
8040
8079
  if (ref.source.startsWith("./") || ref.source.startsWith("../")) {
@@ -8054,7 +8093,7 @@ function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
8054
8093
  targetKey = ref.source;
8055
8094
  }
8056
8095
  const existing = entryImportIndex.get(ref.localName);
8057
- if (existing && existing.targetKey === targetKey && existing.exportedName === ref.exportedName) {
8096
+ if (existing && existing.targetKey === targetKey && existing.exportedName === ref.exportedName && (ref.isTypeOnly || !existing.isTypeOnly)) {
8058
8097
  continue;
8059
8098
  }
8060
8099
  const planned = plannedInjections.get(ref.localName);
@@ -8068,7 +8107,7 @@ function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
8068
8107
  break;
8069
8108
  }
8070
8109
  pending.push([ref.localName, { targetKey, exportedName: ref.exportedName }]);
8071
- required.push({ localName: ref.localName, exportedName: ref.exportedName, specifier });
8110
+ required.push({ localName: ref.localName, exportedName: ref.exportedName, specifier, isTypeOnly: ref.isTypeOnly || undefined });
8072
8111
  }
8073
8112
  if (declinedEntry) {
8074
8113
  result.declined.set(spec.local, declinedEntry);
@@ -8088,6 +8127,8 @@ function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
8088
8127
  }
8089
8128
  function collectHelperModuleValueBindings(sf) {
8090
8129
  const local = new Set;
8130
+ const localTypes = new Set;
8131
+ const importedTypes = new Map;
8091
8132
  const imported = new Map;
8092
8133
  for (const stmt of sf.statements) {
8093
8134
  if (ts8.isVariableStatement(stmt)) {
@@ -8103,35 +8144,41 @@ function collectHelperModuleValueBindings(sf) {
8103
8144
  local.add(stmt.name.text);
8104
8145
  continue;
8105
8146
  }
8147
+ if ((ts8.isTypeAliasDeclaration(stmt) || ts8.isInterfaceDeclaration(stmt)) && stmt.name) {
8148
+ localTypes.add(stmt.name.text);
8149
+ continue;
8150
+ }
8106
8151
  if (ts8.isImportDeclaration(stmt)) {
8107
- if (stmt.importClause?.isTypeOnly)
8108
- continue;
8109
8152
  if (!ts8.isStringLiteral(stmt.moduleSpecifier))
8110
8153
  continue;
8111
8154
  const src = stmt.moduleSpecifier.text;
8112
8155
  if (src === "@barefootjs/client" || src === "@barefootjs/client/runtime")
8113
8156
  continue;
8157
+ const wholeTypeOnly = stmt.importClause?.isTypeOnly === true;
8114
8158
  if (stmt.importClause?.name)
8115
- local.add(stmt.importClause.name.text);
8159
+ (wholeTypeOnly ? localTypes : local).add(stmt.importClause.name.text);
8116
8160
  const namedBindings = stmt.importClause?.namedBindings;
8117
8161
  if (namedBindings && ts8.isNamedImports(namedBindings)) {
8118
8162
  for (const el of namedBindings.elements) {
8119
- if (el.isTypeOnly)
8120
- continue;
8121
- imported.set(el.name.text, { source: src, exportedName: (el.propertyName ?? el.name).text });
8163
+ const entry = { source: src, exportedName: (el.propertyName ?? el.name).text };
8164
+ if (wholeTypeOnly || el.isTypeOnly)
8165
+ importedTypes.set(el.name.text, entry);
8166
+ else
8167
+ imported.set(el.name.text, entry);
8122
8168
  }
8123
8169
  }
8124
8170
  if (namedBindings && ts8.isNamespaceImport(namedBindings)) {
8125
- local.add(namedBindings.name.text);
8171
+ (wholeTypeOnly ? localTypes : local).add(namedBindings.name.text);
8126
8172
  }
8127
8173
  }
8128
8174
  }
8129
- return { local, imported };
8175
+ return { local, imported, localTypes, importedTypes };
8130
8176
  }
8131
8177
  function moduleCaptureCheck(fn, info, moduleBindings, selfName) {
8132
8178
  if (!fn.body)
8133
- return { captured: [], importedRefs: [] };
8179
+ return { captured: [], importedRefs: [], importedTypeRefs: [] };
8134
8180
  const free = extractFreeIdentifiersFromNode(fn.body);
8181
+ const freeTypes = extractFreeTypeIdentifiersFromNode(fn.body);
8135
8182
  const exclude = new Set(info.params);
8136
8183
  for (const b of info.localBindings)
8137
8184
  exclude.add(b);
@@ -8140,8 +8187,12 @@ function moduleCaptureCheck(fn, info, moduleBindings, selfName) {
8140
8187
  for (const p of REACTIVE_PRIMITIVES)
8141
8188
  exclude.add(p);
8142
8189
  exclude.add(selfName);
8190
+ if (fn.typeParameters)
8191
+ for (const p of fn.typeParameters)
8192
+ exclude.add(p.name.text);
8143
8193
  const captured = [];
8144
8194
  const importedRefs = [];
8195
+ const importedTypeRefs = [];
8145
8196
  for (const id of free) {
8146
8197
  if (exclude.has(id))
8147
8198
  continue;
@@ -8153,9 +8204,28 @@ function moduleCaptureCheck(fn, info, moduleBindings, selfName) {
8153
8204
  if (imp)
8154
8205
  importedRefs.push({ localName: id, source: imp.source, exportedName: imp.exportedName });
8155
8206
  }
8207
+ for (const id of freeTypes) {
8208
+ if (exclude.has(id))
8209
+ continue;
8210
+ if (free.has(id))
8211
+ continue;
8212
+ if (moduleBindings.localTypes.has(id) || moduleBindings.local.has(id)) {
8213
+ captured.push(id);
8214
+ continue;
8215
+ }
8216
+ const typeImp = moduleBindings.importedTypes.get(id);
8217
+ if (typeImp) {
8218
+ importedTypeRefs.push({ localName: id, source: typeImp.source, exportedName: typeImp.exportedName });
8219
+ continue;
8220
+ }
8221
+ const valueImp = moduleBindings.imported.get(id);
8222
+ if (valueImp)
8223
+ importedRefs.push({ localName: id, source: valueImp.source, exportedName: valueImp.exportedName });
8224
+ }
8156
8225
  captured.sort();
8157
8226
  importedRefs.sort((a, b) => a.localName < b.localName ? -1 : 1);
8158
- return { captured, importedRefs };
8227
+ importedTypeRefs.sort((a, b) => a.localName < b.localName ? -1 : 1);
8228
+ return { captured, importedRefs, importedTypeRefs };
8159
8229
  }
8160
8230
  function detectReactiveFactory(node, sourceFile, filePath) {
8161
8231
  if (!node.body || !node.name)
@@ -8494,20 +8564,44 @@ function rewriteFactoryCallsInSource(source, prescan) {
8494
8564
  if (edits.length === 0)
8495
8565
  return source;
8496
8566
  const importsBySpecifier = new Map;
8567
+ const typeImportsBySpecifier = new Map;
8497
8568
  for (const f of inlinedFactories) {
8498
8569
  for (const r of f.requiredImports ?? []) {
8499
- let names = importsBySpecifier.get(r.specifier);
8570
+ const bySpecifier = r.isTypeOnly ? typeImportsBySpecifier : importsBySpecifier;
8571
+ let names = bySpecifier.get(r.specifier);
8500
8572
  if (!names) {
8501
8573
  names = new Map;
8502
- importsBySpecifier.set(r.specifier, names);
8574
+ bySpecifier.set(r.specifier, names);
8503
8575
  }
8504
8576
  names.set(r.localName, r.exportedName);
8505
8577
  }
8506
8578
  }
8507
- if (importsBySpecifier.size > 0) {
8508
- const lines = [...importsBySpecifier].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([spec, names]) => {
8579
+ for (const [spec, typeNames] of typeImportsBySpecifier) {
8580
+ const valueNames = importsBySpecifier.get(spec);
8581
+ if (!valueNames)
8582
+ continue;
8583
+ for (const local of [...typeNames.keys()]) {
8584
+ if (valueNames.has(local))
8585
+ typeNames.delete(local);
8586
+ }
8587
+ if (typeNames.size === 0)
8588
+ typeImportsBySpecifier.delete(spec);
8589
+ }
8590
+ if (importsBySpecifier.size > 0 || typeImportsBySpecifier.size > 0) {
8591
+ const buildLine = (names, keyword, spec) => {
8509
8592
  const specifiers = [...names].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([local, exported]) => exported === local ? local : `${exported} as ${local}`);
8510
- return `import { ${specifiers.join(", ")} } from '${spec}'`;
8593
+ return `import ${keyword}{ ${specifiers.join(", ")} } from '${spec}'`;
8594
+ };
8595
+ const allSpecifiers = new Set([...importsBySpecifier.keys(), ...typeImportsBySpecifier.keys()]);
8596
+ const lines = [...allSpecifiers].sort().flatMap((spec) => {
8597
+ const out2 = [];
8598
+ const valueNames = importsBySpecifier.get(spec);
8599
+ if (valueNames)
8600
+ out2.push(buildLine(valueNames, "", spec));
8601
+ const typeNames = typeImportsBySpecifier.get(spec);
8602
+ if (typeNames)
8603
+ out2.push(buildLine(typeNames, "type ", spec));
8604
+ return out2;
8511
8605
  });
8512
8606
  const at = factoryImportInsertionOffset(sourceFile);
8513
8607
  edits.push({ start: at, end: at, replacement: at === 0 ? lines.join(`
@@ -9198,6 +9292,20 @@ function resolveFreeRefs(node, env) {
9198
9292
 
9199
9293
  // src/to-locale-date-lowering.ts
9200
9294
  var TO_LOCALE_TZ_RE = /^(?:UTC|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/;
9295
+ var tzProbeCache = new Map;
9296
+ function isBuildResolvableTimeZone(value) {
9297
+ const cached = tzProbeCache.get(value);
9298
+ if (cached !== undefined)
9299
+ return cached;
9300
+ let verified;
9301
+ try {
9302
+ verified = new Intl.DateTimeFormat("en-US", { timeZone: value }).resolvedOptions().timeZone === value;
9303
+ } catch {
9304
+ verified = false;
9305
+ }
9306
+ tzProbeCache.set(value, verified);
9307
+ return verified;
9308
+ }
9201
9309
  var PROBE_UTC = new Date(Date.UTC(2001, 1, 3));
9202
9310
  var formatCache = new Map;
9203
9311
  var namesCache = new Map;
@@ -9436,7 +9544,7 @@ function matchToLocaleDateStringCall(callee, args, metadata) {
9436
9544
  return null;
9437
9545
  const value = String(prop.value.value);
9438
9546
  if (prop.key === "timeZone") {
9439
- if (!TO_LOCALE_TZ_RE.test(value))
9547
+ if (!TO_LOCALE_TZ_RE.test(value) && !isBuildResolvableTimeZone(value))
9440
9548
  return null;
9441
9549
  tz = value;
9442
9550
  } else {
@@ -13008,13 +13116,19 @@ function collectEventHandlersFromIR(node) {
13008
13116
  });
13009
13117
  return handlers;
13010
13118
  }
13011
- function traverseElements(node, visitor) {
13119
+ function traverseElements(node, visitor, stopAtReactiveConditionals = false) {
13012
13120
  walkIR(node, 0, {
13013
13121
  element: ({ node: el, scope: domDepth, descend }) => {
13014
13122
  visitor(el, domDepth);
13015
13123
  descend(domDepth + 1);
13016
13124
  },
13017
- loop: () => {}
13125
+ loop: () => {},
13126
+ ...stopAtReactiveConditionals ? {
13127
+ conditional: ({ node: c, scope: domDepth, descend }) => {
13128
+ if (!c.slotId)
13129
+ descend(domDepth);
13130
+ }
13131
+ } : {}
13018
13132
  });
13019
13133
  }
13020
13134
  function collectConditionalBranchEvents(node) {
@@ -13029,7 +13143,7 @@ function collectConditionalBranchEvents(node) {
13029
13143
  });
13030
13144
  }
13031
13145
  }
13032
- });
13146
+ }, true);
13033
13147
  return events;
13034
13148
  }
13035
13149
  function collectConditionalBranchRefs(node) {
@@ -13041,7 +13155,7 @@ function collectConditionalBranchRefs(node) {
13041
13155
  callback: el.ref
13042
13156
  });
13043
13157
  }
13044
- });
13158
+ }, true);
13045
13159
  return refs;
13046
13160
  }
13047
13161
  function collectLoopChildRefs(node) {
@@ -13137,7 +13251,7 @@ function traverseForComponents(node, components, skipConditionals = false) {
13137
13251
  }
13138
13252
  });
13139
13253
  }
13140
- function collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings) {
13254
+ function collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings, stopAtReactiveConditionals = false) {
13141
13255
  const texts = [];
13142
13256
  walkIR(node, false, {
13143
13257
  ...stopAt("loop", "async", "ifStatement"),
@@ -13156,13 +13270,14 @@ function collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings)
13156
13270
  ...expanded.freeIds !== undefined && { freeIdentifiers: expanded.freeIds }
13157
13271
  });
13158
13272
  },
13159
- conditional: ({ descend }) => {
13160
- descend(true);
13273
+ conditional: ({ node: c, descend }) => {
13274
+ if (!stopAtReactiveConditionals || !c.slotId)
13275
+ descend(true);
13161
13276
  }
13162
13277
  });
13163
13278
  return texts;
13164
13279
  }
13165
- function collectLoopChildReactiveAttrs(node, ctx, loopParam, loopParamBindings) {
13280
+ function collectLoopChildReactiveAttrs(node, ctx, loopParam, loopParamBindings, stopAtReactiveConditionals = false) {
13166
13281
  const attrs = [];
13167
13282
  traverseElements(node, (el) => {
13168
13283
  if (el.slotId) {
@@ -13189,7 +13304,7 @@ function collectLoopChildReactiveAttrs(node, ctx, loopParam, loopParamBindings)
13189
13304
  });
13190
13305
  }
13191
13306
  }
13192
- });
13307
+ }, stopAtReactiveConditionals);
13193
13308
  return attrs;
13194
13309
  }
13195
13310
 
@@ -14013,8 +14128,8 @@ function collectLoopChildBindings(children, ctx, siblingOffsets, loopParam, loop
14013
14128
  const bindings = emptyLoopChildBindings();
14014
14129
  for (const child of children) {
14015
14130
  bindings.events.push(...collectLoopChildEventsWithNesting(child));
14016
- bindings.reactiveAttrs.push(...collectLoopChildReactiveAttrs(child, ctx, loopParam, loopParamBindings));
14017
- bindings.reactiveTexts.push(...collectLoopChildReactiveTexts(child, ctx, loopParam, loopParamBindings));
14131
+ bindings.reactiveAttrs.push(...collectLoopChildReactiveAttrs(child, ctx, loopParam, loopParamBindings, true));
14132
+ bindings.reactiveTexts.push(...collectLoopChildReactiveTexts(child, ctx, loopParam, loopParamBindings, true));
14018
14133
  bindings.refs.push(...collectLoopChildRefs(child));
14019
14134
  bindings.conditionals.push(...collectLoopChildConditionals(child, ctx, siblingOffsets, loopParam, loopParamBindings));
14020
14135
  }
@@ -14066,7 +14181,9 @@ function summarizeLoopChildBranch(node, ctx, siblingOffsets, loopParam, loopPara
14066
14181
  childComponents: collectConditionalBranchChildComponents(node),
14067
14182
  innerLoops: inner.length > 0 ? inner : undefined,
14068
14183
  conditionals: collectLoopChildConditionals(node, ctx, siblingOffsets, loopParam, loopParamBindings),
14069
- events: collectConditionalBranchEvents(node)
14184
+ events: collectConditionalBranchEvents(node),
14185
+ reactiveAttrs: collectLoopChildReactiveAttrs(node, ctx, loopParam, loopParamBindings, true),
14186
+ reactiveTexts: node.type === "expression" ? [] : collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings, true)
14070
14187
  };
14071
14188
  }
14072
14189
 
@@ -17074,6 +17191,39 @@ function buildLoopChildConditionalsPlan(args) {
17074
17191
  }
17075
17192
  return plans;
17076
17193
  }
17194
+ function buildArmAttrsPlan(attrs, wrap) {
17195
+ if (!attrs || attrs.length === 0)
17196
+ return [];
17197
+ const bySlot = new Map;
17198
+ for (const attr of attrs) {
17199
+ let bucket = bySlot.get(attr.childSlotId);
17200
+ if (!bucket) {
17201
+ bucket = [];
17202
+ bySlot.set(attr.childSlotId, bucket);
17203
+ }
17204
+ bucket.push(attr);
17205
+ }
17206
+ const slots = [];
17207
+ for (const [slotId, slotAttrs] of bySlot) {
17208
+ slots.push({
17209
+ slotId,
17210
+ attrs: slotAttrs.map((attr) => ({
17211
+ attrName: attr.attrName,
17212
+ wrappedExpression: wrap(attr.expression),
17213
+ meta: pickAttrMeta(attr)
17214
+ }))
17215
+ });
17216
+ }
17217
+ return slots;
17218
+ }
17219
+ function buildArmTextsPlan(texts, wrap) {
17220
+ if (!texts || texts.length === 0)
17221
+ return [];
17222
+ return texts.map((text) => ({
17223
+ slotId: text.slotId,
17224
+ wrappedExpression: wrap(text.expression)
17225
+ }));
17226
+ }
17077
17227
  function buildLoopChildArmPlan(args) {
17078
17228
  const { branch, wrap, loopParam, loopParamBindings } = args;
17079
17229
  return {
@@ -17099,7 +17249,8 @@ function buildLoopChildArmPlan(args) {
17099
17249
  loopParam,
17100
17250
  loopParamBindings
17101
17251
  }),
17102
- texts: []
17252
+ attrs: buildArmAttrsPlan(branch.reactiveAttrs, wrap),
17253
+ texts: buildArmTextsPlan(branch.reactiveTexts, wrap)
17103
17254
  };
17104
17255
  }
17105
17256
 
@@ -17127,49 +17278,20 @@ function buildReactiveEffectsPlan(args) {
17127
17278
  }))
17128
17279
  });
17129
17280
  }
17130
- const textSlotsInConditionals = new Set;
17131
- if (conditionals) {
17132
- for (const cond of conditionals) {
17133
- for (const text of texts) {
17134
- if (cond.whenTrueHtml.includes(`bf:${text.slotId}`) || cond.whenFalseHtml.includes(`bf:${text.slotId}`)) {
17135
- textSlotsInConditionals.add(text.slotId);
17136
- }
17137
- }
17138
- }
17139
- }
17140
- const outerTexts = [];
17141
- for (const text of texts) {
17142
- if (textSlotsInConditionals.has(text.slotId))
17143
- continue;
17144
- outerTexts.push({
17145
- slotId: text.slotId,
17146
- wrappedExpression: wrap(text.expression)
17147
- });
17148
- }
17281
+ const outerTexts = texts.map((text) => ({
17282
+ slotId: text.slotId,
17283
+ wrappedExpression: wrap(text.expression)
17284
+ }));
17149
17285
  const conditionalPlans = [];
17150
17286
  if (conditionals) {
17151
17287
  for (const cond of conditionals) {
17152
- const trueTexts = [];
17153
- const falseTexts = [];
17154
- for (const text of texts) {
17155
- if (!textSlotsInConditionals.has(text.slotId))
17156
- continue;
17157
- const wrapped = {
17158
- slotId: text.slotId,
17159
- wrappedExpression: wrap(text.expression)
17160
- };
17161
- if (cond.whenTrueHtml.includes(`bf:${text.slotId}`))
17162
- trueTexts.push(wrapped);
17163
- if (cond.whenFalseHtml.includes(`bf:${text.slotId}`))
17164
- falseTexts.push(wrapped);
17165
- }
17166
17288
  conditionalPlans.push({
17167
17289
  slotId: cond.slotId,
17168
17290
  wrappedCondition: wrap(cond.condition),
17169
17291
  whenTrueTemplateHtml: addCondAttrToTemplate(wrap(cond.whenTrueHtml), cond.slotId),
17170
17292
  whenFalseTemplateHtml: addCondAttrToTemplate(wrap(cond.whenFalseHtml), cond.slotId),
17171
- whenTrueArm: buildOuterArm(cond.whenTrue, trueTexts, wrap, loopParam, loopParamBindings, profileComponentName),
17172
- whenFalseArm: buildOuterArm(cond.whenFalse, falseTexts, wrap, loopParam, loopParamBindings, profileComponentName)
17293
+ whenTrueArm: buildOuterArm(cond.whenTrue, wrap, loopParam, loopParamBindings, profileComponentName),
17294
+ whenFalseArm: buildOuterArm(cond.whenFalse, wrap, loopParam, loopParamBindings, profileComponentName)
17173
17295
  });
17174
17296
  }
17175
17297
  }
@@ -17180,7 +17302,7 @@ function buildReactiveEffectsPlan(args) {
17180
17302
  profileComponentName
17181
17303
  };
17182
17304
  }
17183
- function buildOuterArm(branch, texts, wrap, loopParam, loopParamBindings, profileComponentName) {
17305
+ function buildOuterArm(branch, wrap, loopParam, loopParamBindings, profileComponentName) {
17184
17306
  return {
17185
17307
  events: buildBranchEventBindingsPlan({
17186
17308
  events: branch.events,
@@ -17205,7 +17327,8 @@ function buildOuterArm(branch, texts, wrap, loopParam, loopParamBindings, profil
17205
17327
  loopParam,
17206
17328
  loopParamBindings
17207
17329
  }),
17208
- texts
17330
+ attrs: buildArmAttrsPlan(branch.reactiveAttrs, wrap),
17331
+ texts: buildArmTextsPlan(branch.reactiveTexts, wrap)
17209
17332
  };
17210
17333
  }
17211
17334
  function buildLoopReactiveEffectsPlan(elem, profileComponentName) {
@@ -17984,6 +18107,21 @@ function emitReactiveChildProps(lines, ctx) {
17984
18107
  }
17985
18108
 
17986
18109
  // src/ir-to-client-js/control-flow/stringify/loop-child-arm.ts
18110
+ function stringifyBranchReactiveAttrs(lines, plan, indent, pc) {
18111
+ for (const slot of plan) {
18112
+ const varName = `__ra_${varSlotId(slot.slotId)}`;
18113
+ lines.push(`${indent}{ const ${varName} = qsa(__branchScope, '[bf="${slot.slotId}"]')`);
18114
+ lines.push(`${indent}if (${varName}) {`);
18115
+ for (const attr of slot.attrs) {
18116
+ lines.push(`${indent} __disposers.push(createDisposableEffect(() => {`);
18117
+ for (const stmt of emitAttrUpdate(varName, attr.attrName, attr.wrappedExpression, attr.meta)) {
18118
+ lines.push(`${indent} ${stmt}`);
18119
+ }
18120
+ lines.push(`${indent} }${profileBindingId(pc, slot.slotId)}))`);
18121
+ }
18122
+ lines.push(`${indent}} }`);
18123
+ }
18124
+ }
17987
18125
  function stringifyBranchEventBindings(lines, plan, indent) {
17988
18126
  for (const slot of plan) {
17989
18127
  const v = varSlotId(slot.slotId);
@@ -18061,12 +18199,22 @@ function stringifyLoopChildArm(lines, arm, armIndent, pc) {
18061
18199
  stringifyBranchEventBindings(lines, arm.events, armIndent);
18062
18200
  stringifyBranchChildComponentInits(lines, arm.childComponents, armIndent);
18063
18201
  stringifyBranchInnerLoops(lines, arm.innerLoops, armIndent, pc);
18064
- stringifyLoopChildConditionals(lines, arm.nestedConditionals, armIndent, pc);
18202
+ const hasDisposables = arm.attrs.length > 0 || arm.texts.length > 0 || arm.nestedConditionals.length > 0;
18203
+ if (!hasDisposables)
18204
+ return;
18205
+ lines.push(`${armIndent}const __disposers = []`);
18206
+ stringifyBranchReactiveAttrs(lines, arm.attrs, armIndent, pc);
18207
+ for (const cond of arm.nestedConditionals) {
18208
+ lines.push(`${armIndent}__disposers.push(createDisposableEffect(() => {`);
18209
+ stringifyLoopChildConditional(lines, cond, `${armIndent} `, pc);
18210
+ lines.push(`${armIndent}}))`);
18211
+ }
18065
18212
  for (const text of arm.texts) {
18066
18213
  const varName = `__rt_${varSlotId(text.slotId)}`;
18067
- lines.push(`${armIndent}{ const [${varName}] = $t(__branchScope, '${text.slotId}')`);
18068
- lines.push(`${armIndent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }${profileBindingId(pc, text.slotId)}) }`);
18214
+ lines.push(`${armIndent}let ${varName} = $t(__branchScope, '${text.slotId}')[0]`);
18215
+ lines.push(`${armIndent}__disposers.push(createDisposableEffect(() => { ${varName} = __bfText(${varName}, ${text.wrappedExpression}) }${profileBindingId(pc, text.slotId)}))`);
18069
18216
  }
18217
+ lines.push(`${armIndent}return () => __disposers.forEach(d => d())`);
18070
18218
  }
18071
18219
 
18072
18220
  // src/ir-to-client-js/control-flow/stringify/reactive-effects.ts
@@ -18111,29 +18259,15 @@ function emitOuterConditional(lines, indent, elVar, cond, pc) {
18111
18259
  lines.push(`${indent}insert(${elVar}, '${cond.slotId}', () => ${cond.wrappedCondition}, {`);
18112
18260
  lines.push(`${indent} template: () => { const __slots = []; return { html: \`${cond.whenTrueTemplateHtml}\`, slots: __slots } },`);
18113
18261
  lines.push(`${indent} bindEvents: (__branchScope, { isFirstRun: __bfFirstRun = false } = {}) => {`);
18114
- emitArmBody(lines, cond.whenTrueArm, armIndent, pc);
18262
+ stringifyLoopChildArm(lines, cond.whenTrueArm, armIndent, pc);
18115
18263
  lines.push(`${indent} }`);
18116
18264
  lines.push(`${indent}}, {`);
18117
18265
  lines.push(`${indent} template: () => { const __slots = []; return { html: \`${cond.whenFalseTemplateHtml}\`, slots: __slots } },`);
18118
18266
  lines.push(`${indent} bindEvents: (__branchScope, { isFirstRun: __bfFirstRun = false } = {}) => {`);
18119
- emitArmBody(lines, cond.whenFalseArm, armIndent, pc);
18267
+ stringifyLoopChildArm(lines, cond.whenFalseArm, armIndent, pc);
18120
18268
  lines.push(`${indent} }`);
18121
18269
  lines.push(`${indent}}${profileBindingId(pc, cond.slotId)})`);
18122
18270
  }
18123
- function emitArmBody(lines, arm, armIndent, pc) {
18124
- stringifyBranchEventBindings(lines, arm.events, armIndent);
18125
- stringifyBranchChildComponentInits(lines, arm.childComponents, armIndent);
18126
- stringifyBranchInnerLoops(lines, arm.innerLoops, armIndent, pc);
18127
- stringifyLoopChildConditionals(lines, arm.nestedConditionals, armIndent, pc);
18128
- for (const text of arm.texts) {
18129
- emitArmText(lines, armIndent, text, pc);
18130
- }
18131
- }
18132
- function emitArmText(lines, indent, text, pc) {
18133
- const varName = `__rt_${varSlotId(text.slotId)}`;
18134
- lines.push(`${indent}{ const [${varName}] = $t(__branchScope, '${text.slotId}')`);
18135
- lines.push(`${indent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }${profileBindingId(pc, text.slotId)}) }`);
18136
- }
18137
18271
 
18138
18272
  // src/ir-to-client-js/control-flow/stringify/skeleton-paths.ts
18139
18273
  function pathExpr(base, path) {
@@ -18879,10 +19013,10 @@ function stringifyInsert(lines, plan, opts) {
18879
19013
  function emitArm(lines, arm, mode, armIndent, bodyIndent, profileComponentName) {
18880
19014
  lines.push(`${armIndent}template: () => { const __slots = []; return { html: \`${arm.templateHtml}\`, slots: __slots } },`);
18881
19015
  lines.push(`${armIndent}bindEvents: (__branchScope, { isFirstRun: __bfFirstRun = false } = {}) => {`);
18882
- emitArmBody2(lines, arm.body, mode, bodyIndent, profileComponentName);
19016
+ emitArmBody(lines, arm.body, mode, bodyIndent, profileComponentName);
18883
19017
  lines.push(`${armIndent}}`);
18884
19018
  }
18885
- function emitArmBody2(lines, body, mode, indent, profileComponentName) {
19019
+ function emitArmBody(lines, body, mode, indent, profileComponentName) {
18886
19020
  const bindingBfId = (slotId) => profileComponentName ? `, ${JSON.stringify(`${profileComponentName}#binding:${slotId}`)}` : "";
18887
19021
  const allSlotIds = new Set;
18888
19022
  for (const ev of body.events)
@@ -21064,7 +21198,7 @@ function pushDiagnostic(errors, seen, loc, method, receiverPath, isProp, typeNam
21064
21198
  return;
21065
21199
  seen.add(key);
21066
21200
  const receiver = isProp ? `prop '${receiverPath}'` : `'${receiverPath}'`;
21067
- const suggestion = method === "toLocaleDateString" && typeName === "Date" ? "Pass a literal locale and an explicit literal timeZone — .toLocaleDateString('ja-JP', { timeZone: 'UTC' }) (or a fixed '±HH:MM' offset) — to compile it to the format_date helper; for a runtime locale, resolve the pattern in your i18n layer and use formatDate(date, pattern, tz) from @barefootjs/client. Alternatively add /* @client */ or pre-compute server-side." : "Add /* @client */ to evaluate this expression on the client only, or pre-compute the value server-side.";
21201
+ const suggestion = method === "toLocaleDateString" && typeName === "Date" ? "Pass a literal locale and an explicit literal timeZone — .toLocaleDateString('ja-JP', { timeZone: 'UTC' }), a fixed '±HH:MM' offset, or a canonical IANA zone ID like 'Asia/Tokyo' (exact case; #2344) — to compile it to the format_date helper; for a runtime locale, resolve the pattern in your i18n layer and use formatDate(date, pattern, tz) from @barefootjs/client. Alternatively add /* @client */ or pre-compute server-side." : "Add /* @client */ to evaluate this expression on the client only, or pre-compute the value server-side.";
21068
21202
  errors.push({
21069
21203
  code: ErrorCodes.UNSUPPORTED_JSX_PATTERN,
21070
21204
  severity: "error",
@@ -22701,27 +22835,31 @@ function resolveDangerousInnerHtml(element) {
22701
22835
  if (attr.clientOnly)
22702
22836
  return null;
22703
22837
  if (attr.value.kind !== "expression") {
22704
- return { kind: "dynamic", expr: "", loc: attr.loc };
22838
+ return { kind: "unlowerable", expr: "", loc: attr.loc };
22705
22839
  }
22706
22840
  const parsed = attr.value.parsed ?? parseExpression(attr.value.expr.trim());
22707
- const html = staticHtmlLiteral(parsed);
22708
- if (html !== null)
22709
- return { kind: "static", html };
22710
- return { kind: "dynamic", expr: attr.value.expr, loc: attr.loc };
22841
+ const value = htmlPropValue(parsed);
22842
+ if (value === null)
22843
+ return { kind: "unlowerable", expr: attr.value.expr, loc: attr.loc };
22844
+ if (value.kind === "literal" && value.literalType === "string") {
22845
+ return { kind: "static", html: value.value };
22846
+ }
22847
+ return {
22848
+ kind: "dynamic",
22849
+ valueExpr: stringifyParsedExpr(value),
22850
+ valueParsed: value,
22851
+ loc: attr.loc
22852
+ };
22711
22853
  }
22712
- function staticHtmlLiteral(parsed) {
22854
+ function htmlPropValue(parsed) {
22713
22855
  if (parsed.kind !== "object-literal")
22714
22856
  return null;
22715
22857
  if (parsed.properties.length !== 1)
22716
22858
  return null;
22717
22859
  const [prop] = parsed.properties;
22718
- if (prop.shorthand)
22719
- return null;
22720
22860
  if (prop.key !== "__html")
22721
22861
  return null;
22722
- if (prop.value.kind !== "literal" || prop.value.literalType !== "string")
22723
- return null;
22724
- return prop.value.value;
22862
+ return prop.value;
22725
22863
  }
22726
22864
  var TEMPLATE_METACHAR_PATTERNS = {
22727
22865
  blade: /\{\{|\{!!|<\?|@\w|<\/?\s*x[-:]/,
@@ -22743,14 +22881,14 @@ function dangerousInnerHtmlMetacharViolation(html, adapterId) {
22743
22881
  return `the literal HTML contains a sequence ${adapterId}'s own template compiler would interpret (not inert text once spliced into the template)`;
22744
22882
  }
22745
22883
  function dangerousInnerHtmlDiagnostic(expr, loc, reason) {
22746
- const detail = reason ? ` — ${reason}.` : "";
22884
+ const base = reason ? `dangerouslySetInnerHTML value cannot be lowered on this adapter — ${reason}` : "dangerouslySetInnerHTML requires an object literal with a single `__html` property (e.g. { __html: value }) — spreads, extra keys, and computed keys are not supported";
22747
22885
  return {
22748
22886
  code: "BF101",
22749
22887
  severity: "error",
22750
- message: `dangerouslySetInnerHTML requires a compile-time string literal __html value on template adapters (e.g. { __html: '...' })${expr ? `: ${expr.trim()}` : ""}${detail}`,
22888
+ message: `${base}${expr ? `: ${expr.trim()}` : ""}`,
22751
22889
  loc,
22752
22890
  suggestion: {
22753
- message: "Dynamic or signal-derived HTML for dangerouslySetInnerHTML is only supported on Hono/CSR today (tracked separately: https://github.com/piconic-ai/barefootjs/issues/2215). Use an inline string literal, or defer it to the client with /* @client */ (e.g. dangerouslySetInnerHTML={/* @client */ { __html: expr }}) so hydration sets it instead of SSR."
22891
+ message: "Pass an object literal { __html: value } with exactly one `__html` property (a string literal is spliced as trusted template text; a prop/signal value is lowered through the adapter's raw-output sink). To force it onto the client instead of SSR, use /* @client */ (e.g. dangerouslySetInnerHTML={/* @client */ { __html: expr }}) so hydration sets it."
22754
22892
  }
22755
22893
  };
22756
22894
  }
@@ -1 +1 @@
1
- {"version":3,"file":"collect-elements.d.ts","sourceRoot":"","sources":["../../src/ir-to-client-js/collect-elements.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,MAAM,EAAoC,KAAK,MAAM,EAAmC,MAAM,aAAa,CAAA;AACzH,OAAO,KAAK,EAAE,eAAe,EAA+H,iBAAiB,EAA0B,oBAAoB,EAAc,UAAU,EAAE,MAAM,YAAY,CAAA;AAyGvQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CA0C7E;AAyBD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,wBAAwB;IACvC;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAED,4DAA4D;AAC5D,eAAO,MAAM,sBAAsB,EAAE,wBAIpC,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EAAE,EACf,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EACrC,cAAc,CAAC,EAAE,MAAM,EACvB,GAAG,CAAC,EAAE,eAAe,EACrB,OAAO,CAAC,EAAE,wBAAwB,GACjC,UAAU,EAAE,CAyId;AAqKD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,eAAe,EACpB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EACrC,iBAAiB,UAAQ,GACxB,IAAI,CA+PN;AA+WD;;;;;;;;;;GAUG;AAEH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,GAAG,EAAE,eAAe,EACpB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EACrC,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,SAAS,OAAO,aAAa,EAAE,gBAAgB,EAAE,GAAG,SAAS,GAC/E,iBAAiB,CAUnB;AAED,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,eAAe,EACpB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EACrC,SAAS,CAAC,EAAE,MAAM,EAClB,iBAAiB,CAAC,EAAE,SAAS,OAAO,aAAa,EAAE,gBAAgB,EAAE,GACpE,oBAAoB,EAAE,CA4DxB"}
1
+ {"version":3,"file":"collect-elements.d.ts","sourceRoot":"","sources":["../../src/ir-to-client-js/collect-elements.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,MAAM,EAAoC,KAAK,MAAM,EAAmC,MAAM,aAAa,CAAA;AACzH,OAAO,KAAK,EAAE,eAAe,EAA+H,iBAAiB,EAA0B,oBAAoB,EAAc,UAAU,EAAE,MAAM,YAAY,CAAA;AAyGvQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CA0C7E;AAyBD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,wBAAwB;IACvC;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAED,4DAA4D;AAC5D,eAAO,MAAM,sBAAsB,EAAE,wBAIpC,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EAAE,EACf,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EACrC,cAAc,CAAC,EAAE,MAAM,EACvB,GAAG,CAAC,EAAE,eAAe,EACrB,OAAO,CAAC,EAAE,wBAAwB,GACjC,UAAU,EAAE,CAyId;AAqKD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,eAAe,EACpB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EACrC,iBAAiB,UAAQ,GACxB,IAAI,CA+PN;AA+WD;;;;;;;;;;GAUG;AAEH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,GAAG,EAAE,eAAe,EACpB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EACrC,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,SAAS,OAAO,aAAa,EAAE,gBAAgB,EAAE,GAAG,SAAS,GAC/E,iBAAiB,CAenB;AAED,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,eAAe,EACpB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EACrC,SAAS,CAAC,EAAE,MAAM,EAClB,iBAAiB,CAAC,EAAE,SAAS,OAAO,aAAa,EAAE,gBAAgB,EAAE,GACpE,oBAAoB,EAAE,CA4DxB"}