@fluixi/dom 1.0.0-alpha.83 → 1.0.0-alpha.85

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 (182) hide show
  1. package/dist/cdn/dom-client.cjs +1427 -1
  2. package/dist/cdn/dom-client.d.ts +2 -2
  3. package/dist/cdn/dom-client.global.js +2 -1
  4. package/dist/cdn/dom-client.js +3 -3
  5. package/dist/cdn/dom-client.mjs +1430 -1
  6. package/dist/cdn/dom.cjs +3127 -1
  7. package/dist/cdn/dom.d.ts +2 -2
  8. package/dist/cdn/dom.global.js +2 -1
  9. package/dist/cdn/dom.js +4 -4
  10. package/dist/cdn/dom.mjs +3133 -1
  11. package/dist/index.cjs +3155 -1
  12. package/dist/index.js +2 -2
  13. package/dist/index.mjs +3149 -1
  14. package/dist/lib/component/index.cjs +128 -1
  15. package/dist/lib/component/index.mjs +105 -1
  16. package/dist/lib/component/modifiers.cjs +126 -1
  17. package/dist/lib/component/modifiers.d.ts +12 -0
  18. package/dist/lib/component/modifiers.d.ts.map +1 -1
  19. package/dist/lib/component/modifiers.js +12 -84
  20. package/dist/lib/component/modifiers.mjs +105 -1
  21. package/dist/lib/dom/delegate.cjs +60 -1
  22. package/dist/lib/dom/delegate.js +1 -1
  23. package/dist/lib/dom/delegate.mjs +39 -1
  24. package/dist/lib/dom/dynamic-element.cjs +949 -1
  25. package/dist/lib/dom/dynamic-element.d.ts +3 -3
  26. package/dist/lib/dom/dynamic-element.js +2 -2
  27. package/dist/lib/dom/dynamic-element.mjs +928 -1
  28. package/dist/lib/dom/hydration-state.cjs +93 -1
  29. package/dist/lib/dom/hydration-state.d.ts +1 -1
  30. package/dist/lib/dom/hydration-state.js +1 -1
  31. package/dist/lib/dom/hydration-state.mjs +72 -1
  32. package/dist/lib/dom/hydration.cjs +685 -1
  33. package/dist/lib/dom/hydration.js +8 -8
  34. package/dist/lib/dom/hydration.mjs +664 -1
  35. package/dist/lib/dom/index.cjs +2040 -1
  36. package/dist/lib/dom/index.js +1 -1
  37. package/dist/lib/dom/index.mjs +2020 -1
  38. package/dist/lib/dom/integration.cjs +71 -1
  39. package/dist/lib/dom/integration.js +3 -3
  40. package/dist/lib/dom/integration.mjs +51 -1
  41. package/dist/lib/dom/island.cjs +790 -1
  42. package/dist/lib/dom/island.d.ts +1 -1
  43. package/dist/lib/dom/island.js +11 -11
  44. package/dist/lib/dom/island.mjs +769 -1
  45. package/dist/lib/dom/runtime.cjs +1432 -1
  46. package/dist/lib/dom/runtime.d.ts +60 -17
  47. package/dist/lib/dom/runtime.d.ts.map +1 -1
  48. package/dist/lib/dom/runtime.js +122 -79
  49. package/dist/lib/dom/runtime.mjs +1414 -1
  50. package/dist/lib/dom/server/fx-data.cjs +53 -1
  51. package/dist/lib/dom/server/fx-data.d.ts +5 -5
  52. package/dist/lib/dom/server/fx-data.js +6 -6
  53. package/dist/lib/dom/server/fx-data.mjs +32 -1
  54. package/dist/lib/dom/server/host.cjs +66 -1
  55. package/dist/lib/dom/server/host.d.ts +6 -0
  56. package/dist/lib/dom/server/host.d.ts.map +1 -1
  57. package/dist/lib/dom/server/host.js +7 -1
  58. package/dist/lib/dom/server/host.mjs +45 -1
  59. package/dist/lib/dom/server/index.cjs +759 -1
  60. package/dist/lib/dom/server/index.d.ts +1 -1
  61. package/dist/lib/dom/server/index.js +1 -1
  62. package/dist/lib/dom/server/index.mjs +743 -1
  63. package/dist/lib/dom/server/nodes.cjs +302 -1
  64. package/dist/lib/dom/server/nodes.d.ts +2 -2
  65. package/dist/lib/dom/server/nodes.d.ts.map +1 -1
  66. package/dist/lib/dom/server/nodes.js +4 -4
  67. package/dist/lib/dom/server/nodes.mjs +279 -1
  68. package/dist/lib/dom/server/parse-template.cjs +395 -1
  69. package/dist/lib/dom/server/parse-template.d.ts +1 -1
  70. package/dist/lib/dom/server/parse-template.js +4 -4
  71. package/dist/lib/dom/server/parse-template.mjs +372 -1
  72. package/dist/lib/dom/server/render.cjs +691 -1
  73. package/dist/lib/dom/server/render.d.ts +9 -1
  74. package/dist/lib/dom/server/render.d.ts.map +1 -1
  75. package/dist/lib/dom/server/render.js +23 -15
  76. package/dist/lib/dom/server/render.mjs +677 -1
  77. package/dist/lib/dom/server/request-context.cjs +110 -1
  78. package/dist/lib/dom/server/request-context.d.ts +29 -9
  79. package/dist/lib/dom/server/request-context.d.ts.map +1 -1
  80. package/dist/lib/dom/server/request-context.js +28 -8
  81. package/dist/lib/dom/server/request-context.mjs +89 -1
  82. package/dist/lib/dom/server/serialize.cjs +187 -1
  83. package/dist/lib/dom/server/serialize.js +3 -3
  84. package/dist/lib/dom/server/serialize.mjs +164 -1
  85. package/dist/lib/dom/server-renderer.cjs +760 -1
  86. package/dist/lib/dom/server-renderer.js +1 -1
  87. package/dist/lib/dom/server-renderer.mjs +745 -1
  88. package/dist/lib/dom/types.cjs +18 -1
  89. package/dist/lib/dom/utils.cjs +381 -1
  90. package/dist/lib/dom/utils.d.ts +1 -88
  91. package/dist/lib/dom/utils.d.ts.map +1 -1
  92. package/dist/lib/dom/utils.js +2 -99
  93. package/dist/lib/dom/utils.mjs +361 -1
  94. package/dist/lib/dom/versions.cjs +60 -1
  95. package/dist/lib/dom/versions.d.ts +3 -3
  96. package/dist/lib/dom/versions.d.ts.map +1 -1
  97. package/dist/lib/dom/versions.js +17 -5
  98. package/dist/lib/dom/versions.mjs +37 -1
  99. package/dist/lib/element/attributes.cjs +19 -1
  100. package/dist/lib/element/attributes.d.ts +7 -7
  101. package/dist/lib/element/attributes.js +1 -1
  102. package/dist/lib/element/attributes.mjs +1 -0
  103. package/dist/lib/element/index.cjs +19 -1
  104. package/dist/lib/element/index.mjs +1 -0
  105. package/dist/lib/element/types.cjs +18 -1
  106. package/dist/lib/element/types.d.ts +4 -5
  107. package/dist/lib/element/types.d.ts.map +1 -1
  108. package/dist/lib/element/types.js +0 -1
  109. package/dist/lib/flow/class-map.cjs +38 -1
  110. package/dist/lib/flow/class-map.d.ts +3 -3
  111. package/dist/lib/flow/class-map.js +2 -2
  112. package/dist/lib/flow/class-map.mjs +17 -1
  113. package/dist/lib/flow/client-only.cjs +57 -1
  114. package/dist/lib/flow/client-only.d.ts +4 -4
  115. package/dist/lib/flow/client-only.js +3 -3
  116. package/dist/lib/flow/client-only.mjs +36 -1
  117. package/dist/lib/flow/dynamic.cjs +53 -1
  118. package/dist/lib/flow/dynamic.d.ts +2 -2
  119. package/dist/lib/flow/dynamic.js +3 -3
  120. package/dist/lib/flow/dynamic.mjs +32 -1
  121. package/dist/lib/flow/error-boundary.cjs +43 -1
  122. package/dist/lib/flow/error-boundary.d.ts +2 -2
  123. package/dist/lib/flow/error-boundary.js +3 -3
  124. package/dist/lib/flow/error-boundary.mjs +22 -1
  125. package/dist/lib/flow/for.cjs +66 -1
  126. package/dist/lib/flow/for.d.ts +9 -9
  127. package/dist/lib/flow/for.d.ts.map +1 -1
  128. package/dist/lib/flow/for.js +2 -2
  129. package/dist/lib/flow/for.mjs +45 -1
  130. package/dist/lib/flow/index-flow.cjs +67 -1
  131. package/dist/lib/flow/index-flow.d.ts +5 -5
  132. package/dist/lib/flow/index-flow.js +4 -4
  133. package/dist/lib/flow/index-flow.mjs +46 -1
  134. package/dist/lib/flow/index.cjs +805 -1
  135. package/dist/lib/flow/index.mjs +792 -1
  136. package/dist/lib/flow/portal.cjs +538 -1
  137. package/dist/lib/flow/portal.mjs +519 -1
  138. package/dist/lib/flow/show.cjs +81 -1
  139. package/dist/lib/flow/show.d.ts +1 -1
  140. package/dist/lib/flow/show.d.ts.map +1 -1
  141. package/dist/lib/flow/show.js +4 -115
  142. package/dist/lib/flow/show.mjs +67 -1
  143. package/dist/lib/flow/style-map.cjs +43 -1
  144. package/dist/lib/flow/style-map.d.ts +3 -3
  145. package/dist/lib/flow/style-map.js +3 -3
  146. package/dist/lib/flow/style-map.mjs +22 -1
  147. package/dist/lib/flow/switch.cjs +91 -1
  148. package/dist/lib/flow/switch.d.ts +3 -3
  149. package/dist/lib/flow/switch.d.ts.map +1 -1
  150. package/dist/lib/flow/switch.js +6 -6
  151. package/dist/lib/flow/switch.mjs +70 -1
  152. package/dist/lib/flow/utils.cjs +44 -1
  153. package/dist/lib/flow/utils.d.ts +1 -1
  154. package/dist/lib/flow/utils.js +1 -1
  155. package/dist/lib/flow/utils.mjs +23 -1
  156. package/dist/lib/html.cjs +32 -1
  157. package/dist/lib/html.d.ts +6 -6
  158. package/dist/lib/html.js +5 -5
  159. package/dist/lib/html.mjs +11 -1
  160. package/dist/lib/index.cjs +3104 -1
  161. package/dist/lib/index.d.ts +0 -1
  162. package/dist/lib/index.d.ts.map +1 -1
  163. package/dist/lib/index.js +2 -3
  164. package/dist/lib/index.mjs +3097 -1
  165. package/dist/lib/shared/types.cjs +18 -1
  166. package/dist/lib/shared/types.d.ts +8 -8
  167. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  168. package/dist/version.generated.cjs +26 -1
  169. package/dist/version.generated.d.ts +1 -1
  170. package/dist/version.generated.js +2 -2
  171. package/dist/version.generated.mjs +5 -1
  172. package/package.json +7 -6
  173. package/dist/lib/jsx/control-flow/flow.cjs +0 -1
  174. package/dist/lib/jsx/control-flow/flow.d.ts +0 -2
  175. package/dist/lib/jsx/control-flow/flow.d.ts.map +0 -1
  176. package/dist/lib/jsx/control-flow/flow.js +0 -627
  177. package/dist/lib/jsx/control-flow/flow.mjs +0 -0
  178. package/dist/lib/jsx/control-flow/for-flow.cjs +0 -1
  179. package/dist/lib/jsx/control-flow/for-flow.d.ts +0 -2
  180. package/dist/lib/jsx/control-flow/for-flow.d.ts.map +0 -1
  181. package/dist/lib/jsx/control-flow/for-flow.js +0 -1
  182. package/dist/lib/jsx/control-flow/for-flow.mjs +0 -0
@@ -1,10 +1,9 @@
1
1
  /**
2
- * @fileoverview Core Reactive DOM Runtime
3
2
  * @module @fluixi/dom/runtime
4
3
  *
5
4
  * Fine-grained reactive DOM manipulation utilities inspired by SolidJS.
6
5
  * Key design: Event delegation uses `$$eventname` properties on DOM nodes
7
- * (the SolidJS pattern) simpler and more reliable than Map-based lookup.
6
+ * (the SolidJS pattern): simpler and more reliable than Map-based lookup.
8
7
  *
9
8
  * Critical fixes applied:
10
9
  * - insertContent array branch: nested signal content cleanup via onCleanup
@@ -18,7 +17,7 @@ import { stampVersions, warnOnVersionSkew } from './versions.js';
18
17
  import { isComponent } from '../component/modifiers.js';
19
18
  // Reactive primitives come straight from the signal core (the globalThis-singleton graph makes
20
19
  // this duplication-safe). The integration bridge is only for framework-provided glue that is
21
- // NOT in the reactive core (e.g. createComponent).
20
+ // not in the reactive core (e.g. createComponent).
22
21
  import { batch, createEffect, createRenderEffect, createRoot, onCleanup, readChildren, reportReactiveBinding, reportReactiveDirective, untrack } from '@fluixi/reactive/signal';
23
22
  import { isServer, getServerNodes, isDomNode, isDomElement, isDomText, } from './server/host.js';
24
23
  import { hydration, getHydrationWalker, isAdopted, runHydrationAt, ascendPast, } from './hydration-state.js';
@@ -84,14 +83,14 @@ function located(element, at) {
84
83
  // Template cache
85
84
  const templateCache = new Map();
86
85
  // ============================================================================
87
- // Event Delegation SolidJS $$eventname property pattern
86
+ // Event Delegation. SolidJS $$eventname property pattern
88
87
  //
89
88
  // Handlers are stored directly on DOM nodes as `node.$$click = handler`.
90
- // The capture listener reads `node.$$click` directly no Map lookups needed.
89
+ // The capture listener reads `node.$$click` directly, no Map lookups needed.
91
90
  // This approach correctly handles e.preventDefault() and stopPropagation.
92
91
  // ============================================================================
93
92
  const delegatedSet = new Set();
94
- // Events that don't bubble must use direct addEventListener
93
+ // Events that don't bubble: must use direct addEventListener
95
94
  const NON_DELEGATED = new Set(['scroll', 'focus', 'blur', 'load', 'error', 'resize', 'mouseenter', 'mouseleave', 'pointerenter', 'pointerleave', 'gotpointercapture', 'lostpointercapture']);
96
95
  /**
97
96
  * Register global delegated event listener for the given event names.
@@ -125,12 +124,12 @@ function delegatedEventHandler(e) {
125
124
  }
126
125
  }
127
126
  /**
128
- * Add a delegated event handler stores as $$eventname property
127
+ * Add a delegated event handler: stores as $$eventname property
129
128
  */
130
129
  export function addDelegatedEventListener(element, name, handler) {
131
130
  element[`$$${name}`] = handler;
132
131
  if (NON_DELEGATED.has(name)) {
133
- // Non-bubbling event (focus/blur/pointerenter/…): document delegation can't observe it, so
132
+ // Non-bubbling event (focus/blur/pointerenter/...): document delegation can't observe it, so
134
133
  // attach a direct listener once. A stable forwarder reads $$name so updating the handler
135
134
  // doesn't churn addEventListener/removeEventListener.
136
135
  const key = `__fx_${name}`;
@@ -175,7 +174,7 @@ export function isFunction(value) {
175
174
  function createTextNode(value, anchor = false) {
176
175
  if (isServer())
177
176
  return getServerNodes().createText(value);
178
- // Anchors/placeholders (insert markers) are client-only insertion points the server
177
+ // Anchors/placeholders (insert markers) are client-only insertion points, the server
179
178
  // serialized nothing for them, so they must never adopt a server text node. Only real
180
179
  // content goes through the hydration walker.
181
180
  if (hydration.active && !anchor) {
@@ -195,6 +194,17 @@ function createMarker(value) {
195
194
  }
196
195
  return document.createComment(value);
197
196
  }
197
+ /**
198
+ * Create an element, or adopt the server's during hydration.
199
+ *
200
+ * One of the calls the compiler emits, so its three modes are the whole of how a
201
+ * template becomes DOM. Server rendering routes to the realm's node factory and returns
202
+ * a string-serializable stand-in rather than a real element. Hydration asks the walker
203
+ * for the next element the server wrote and reuses it. Otherwise it creates one.
204
+ *
205
+ * `at` is a dev-only source position, stamped onto the node for devtools. A production
206
+ * build passes none and carries no marker.
207
+ */
198
208
  export function createNativeElement(tag, isSVG = false, at) {
199
209
  if (isServer())
200
210
  return getServerNodes().createElement(tag, isSVG);
@@ -205,6 +215,13 @@ export function createNativeElement(tag, isSVG = false, at) {
205
215
  }
206
216
  return located(isSVG ? document.createElementNS(SVG_NAMESPACE, tag) : document.createElement(tag), at);
207
217
  }
218
+ /**
219
+ * Parse static template HTML once and cache it by that string.
220
+ *
221
+ * Every clone of a template comes from one parse, which is most of why a compiled
222
+ * template is cheap. The cache is keyed by the markup rather than by identity, so two
223
+ * modules emitting the same HTML share the parse.
224
+ */
208
225
  export function createTemplate(html, isSVG = false) {
209
226
  const cached = templateCache.get(html);
210
227
  if (cached)
@@ -224,14 +241,14 @@ export function createTemplate(html, isSVG = false) {
224
241
  * needs it.
225
242
  *
226
243
  * The three cases mirror {@link createNativeElement}, which is the point: a template has
227
- * to behave like the elements it replaces, or it works only in client-rendered apps
244
+ * to behave like the elements it replaces, or it works only in client-rendered apps,
228
245
  * which is exactly how it failed before this existed.
229
246
  *
230
- * - **server** carried as text and written out verbatim. Parsing it into elements
247
+ * - **server**: carried as text and written out verbatim. Parsing it into elements
231
248
  * only to serialize them straight back would be pure cost.
232
- * - **hydrating** adopt the server's existing subtree and skip past it. The subtree
249
+ * - **hydrating**: adopt the server's existing subtree and skip past it. The subtree
233
250
  * is inert, so there is nothing inside to wire up.
234
- * - **client** parse once (cached by HTML) and clone per instance.
251
+ * - **client**: parse once (cached by HTML) and clone per instance.
235
252
  */
236
253
  export function templateNode(html, tag, holes = false, isSVG = false, at) {
237
254
  if (isServer()) {
@@ -253,7 +270,7 @@ export function templateNode(html, tag, holes = false, isSVG = false, at) {
253
270
  * Write each element's own source position onto it.
254
271
  *
255
272
  * The whole template is one string, so a source map traces every element in it back to the
256
- * line the root opens at devtools would name the component and nothing finer. This is the
273
+ * line the root opens at: devtools would name the component and nothing finer. This is the
257
274
  * one moment the two line up: the tree holds exactly the elements the compiler listed, in
258
275
  * the same order, because holes are filled after this returns.
259
276
  *
@@ -276,7 +293,7 @@ function stampPositions(node, at) {
276
293
  * for *that hole*.
277
294
  *
278
295
  * Without this, only the hole's top level adopts. `templateNode` moves the cursor past the
279
- * whole template the subtree is inert, so there is nothing in it to walk which leaves
296
+ * whole template, the subtree is inert, so there is nothing in it to walk, which leaves
280
297
  * the cursor in the wrong place for anything the hole builds internally. A component in a
281
298
  * hole would then recreate its own elements: no duplication, but every server node thrown
282
299
  * away, which is most of what hydration is for.
@@ -287,13 +304,13 @@ export function holeScope(start, produce) {
287
304
  if (!hydration.active)
288
305
  return produce();
289
306
  // `produce` is called with the cursor already placed, because the value may be built
290
- // eagerly `props.children` is a getter, and reading it constructs its nodes there and
307
+ // eagerly: `props.children` is a getter, and reading it constructs its nodes there and
291
308
  // then. Wrapping the result instead would be too late: the DOM would already exist,
292
309
  // built against whatever cursor happened to be current, and the server's nodes would be
293
310
  // left behind as duplicates.
294
311
  const value = runHydrationAt(start.nextSibling, produce);
295
312
  // A memo or accessor builds nothing until it is read, which happens inside `insert`'s
296
- // effect so it needs the cursor placed again at that moment. The guard matters: once
313
+ // effect, so it needs the cursor placed again at that moment. The guard matters: once
297
314
  // hydration is over, later updates must render normally rather than re-enter it.
298
315
  if (typeof value === 'function') {
299
316
  const accessor = value;
@@ -307,7 +324,7 @@ export function holeScope(start, produce) {
307
324
  /**
308
325
  * The compiler tags a hole's accessor with where it was written, and devtools reads the tag
309
326
  * off whatever finally reaches `insert`. Under hydration that is this wrapper, not the
310
- * accessor, so the tag has to come along otherwise the binding reports the frame it was
327
+ * accessor, so the tag has to come along: otherwise the binding reports the frame it was
311
328
  * created from, which is a line in this file.
312
329
  */
313
330
  function carryHoleTag(from, to) {
@@ -336,7 +353,7 @@ export function holeEnd(start) {
336
353
  *
337
354
  * On a fresh clone the markers are adjacent and this is empty, so `insert` builds the
338
355
  * content. When hydrating, the server's rendered content sits between them and is handed
339
- * back `insertExpression` then reuses those nodes (setting `.data` on a text node
356
+ * back: `insertExpression` then reuses those nodes (setting `.data` on a text node
340
357
  * rather than creating one), which is what adoption means here.
341
358
  *
342
359
  * Paired markers are what make this exact: a hole's content is *by definition* the nodes
@@ -374,12 +391,12 @@ export function fnName(fn) {
374
391
  return fn._name || fn.name || 'anonymous';
375
392
  }
376
393
  /**
377
- * iterateFn resolves a value by calling through plain function wrappers.
394
+ * iterateFn: resolves a value by calling through plain function wrappers.
378
395
  *
379
396
  * Must stop at signals/memos/resources ($SIGNAL marker) and
380
397
  * component thunks (fluixi-component marker). Without this:
381
398
  * - components returning memos get unwrapped to static values, losing reactivity
382
- * - lazy component createMemo re-triggers C(props) infinite recursion
399
+ * - lazy component createMemo re-triggers C(props) -> infinite recursion
383
400
  */
384
401
  export function iterateFn(item) {
385
402
  // Stop immediately if it's a signal, memo, or resource
@@ -390,7 +407,7 @@ export function iterateFn(item) {
390
407
  return item;
391
408
  if (typeof item !== 'function')
392
409
  return item;
393
- // Unwrap plain function wrappers stop at signals and component thunks
410
+ // Unwrap plain function wrappers: stop at signals and component thunks
394
411
  let content = item();
395
412
  while (typeof content === 'function' &&
396
413
  !isSignal(content) &&
@@ -408,7 +425,7 @@ const MAX_INSERT_DEPTH = 200;
408
425
  // _createInsertEffect
409
426
  //
410
427
  // Creates a reactive render effect for DOM insertion.
411
- // Uses a componentCache to ensure component thunks are called at most once
428
+ // Uses a componentCache to ensure component thunks are called at most once:
412
429
  // same thunk function reference always returns the same output across re-runs.
413
430
  // ============================================================================
414
431
  function _createInsertEffect(getAccessor, parent, marker, initialCurrent, setCurrent) {
@@ -416,7 +433,7 @@ function _createInsertEffect(getAccessor, parent, marker, initialCurrent, setCur
416
433
  const componentCache = new Map();
417
434
  return createRenderEffect(() => {
418
435
  // The accessor, never its result: it carries the compiler's hole position, and calling it
419
- // again here builds the dom twice during hydration, a duplicated subtree.
436
+ // again here builds the dom twice, during hydration, a duplicated subtree.
420
437
  //
421
438
  // Every run, because what this effect writes into moves as the value does.
422
439
  reportReactiveBinding(marker ?? parent, 'content', undefined, getAccessor);
@@ -489,9 +506,22 @@ function _createInsertEffect(getAccessor, parent, marker, initialCurrent, setCur
489
506
  // ============================================================================
490
507
  // insert
491
508
  // ============================================================================
509
+ /**
510
+ * Place content in a parent, and keep it current when the content is reactive.
511
+ *
512
+ * The other half of the compiler's output. `accessor` may be a value or a function: a
513
+ * function is read inside a render effect, so the position updates on its own when
514
+ * anything it read changes. That is where fine-grained updating actually happens, since
515
+ * only this position is touched rather than the subtree around it.
516
+ *
517
+ * `marker` is the node the content sits before, which is what makes several holes in one
518
+ * parent independent. `init` is what the position already holds, empty on a fresh clone
519
+ * and the server's nodes when hydrating, so hydration reconciles against them instead of
520
+ * building a second copy.
521
+ */
492
522
  export function insert(parent, accessor, marker, init) {
493
523
  if (_insertDepth > MAX_INSERT_DEPTH) {
494
- console.error('[insert] Exceeded max call depth possible infinite loop.');
524
+ console.error('[insert] Exceeded max call depth, possible infinite loop.');
495
525
  return () => null;
496
526
  }
497
527
  if (marker !== undefined && !marker) {
@@ -510,7 +540,7 @@ export function insert(parent, accessor, marker, init) {
510
540
  if (typeof accessor !== 'function' && !isReactiveArray(accessor)) {
511
541
  _insertDepth++;
512
542
  try {
513
- // `current` not null. The caller passes what is already in this slot, and for a
543
+ // `current`, not null. The caller passes what is already in this slot, and for a
514
544
  // hydrating template hole that is the server's own nodes (`holeContent`). Dropping it
515
545
  // meant a static value never adopted: insertExpression saw an empty slot and appended
516
546
  // a second copy beside the server's, so `{cmd}` rendered the string twice.
@@ -595,14 +625,14 @@ export function insertExpression(parent, value, marker, current, componentCache)
595
625
  }
596
626
  else if (!isAdopted(parent, node)) {
597
627
  // during hydration createTextNode reuses the in-place server text node and
598
- // already advanced the cursor don't re-insert (that would reorder).
628
+ // already advanced the cursor: don't re-insert (that would reorder).
599
629
  insertBefore(parent, node, marker);
600
630
  }
601
631
  return node;
602
632
  }
603
633
  if (isDomNode(value)) {
604
634
  // Hydration: the node was already produced from the server DOM (reused by
605
- // createNativeElement) and sits in `parent` in place adopt it without
635
+ // createNativeElement) and sits in `parent` in place: adopt it without
606
636
  // re-inserting, and ascend the cursor past its (already-walked) subtree.
607
637
  if (isAdopted(parent, value)) {
608
638
  ascendPast(value);
@@ -646,13 +676,13 @@ export function insertExpression(parent, value, marker, current, componentCache)
646
676
  const reactiveSlots = [];
647
677
  flattenArray(value, array, parent, marker, true, componentCache, reactiveSlots);
648
678
  const reconciled = reconcileArrays(parent, currentArray, array, marker);
649
- // Markers are now in the DOM wire a per-slot reactive insert. insert() sets up a nested
679
+ // Markers are now in the DOM: wire a per-slot reactive insert. insert() sets up a nested
650
680
  // render effect that re-inserts the member's content before its marker on change, so a bare
651
681
  // control-flow component return (<Show>/<Switch>/<For>) updates in place.
652
682
  //
653
683
  // The per-slot content lives OUTSIDE the reconcile array (only the marker is tracked), so if
654
- // this enclosing effect re-runs with a shorter array e.g. a <For> item dropped, or its child
655
- // resolved to a memo (Provider -> component) reconcile removes the marker but would orphan the
684
+ // this enclosing effect re-runs with a shorter array: e.g. a <For> item dropped, or its child
685
+ // resolved to a memo (Provider -> component): reconcile removes the marker but would orphan the
656
686
  // content. Register a cleanup that removes the slot's nodes when its effect is torn down. This
657
687
  // only fires when the enclosing effect re-runs (the array itself changed); an in-place update
658
688
  // (e.g. <Show> toggling) never re-runs this effect, so it keeps updating in place.
@@ -678,12 +708,12 @@ export function insertExpression(parent, value, marker, current, componentCache)
678
708
  }
679
709
  return cleanChildren(parent, current, marker);
680
710
  }
681
- // flattenArray flatten a children array to DOM nodes. A signal whose result is a
711
+ // flattenArray: flatten a children array to DOM nodes. A signal whose result is a
682
712
  // component thunk (e.g. a router memo returning a SuspenseThunk) is recursed through
683
713
  // flattenArray, not read inline, so the thunk goes through componentCache and stays
684
714
  // stable across navigations.
685
715
  function flattenArray(arr, result, parent, marker, track = true, componentCache,
686
- // When provided, a reactive member is NOT read inline (which would subscribe the enclosing
716
+ // When provided, a reactive member is not read inline (which would subscribe the enclosing
687
717
  // render effect and re-render the whole subtree on change). Instead it emits a placeholder
688
718
  // marker and records itself here; the array caller wires a fine-grained insert() per slot
689
719
  // after the markers are placed. This is what makes a bare control-flow component return
@@ -704,14 +734,14 @@ reactiveSlots) {
704
734
  if (typeof item === 'function') {
705
735
  let resolved;
706
736
  if (isComponent(item) && !isSignal(item)) {
707
- // Pure component thunk (e.g. () => <div>) call exactly once and cache so
737
+ // Pure component thunk (e.g. () => <div>): call exactly once and cache so
708
738
  // re-runs of the enclosing render effect return the same DOM nodes (keeps
709
739
  // focus etc. stable across navigations).
710
740
  // must check !isSignal. A memo stamped as a component (e.g. an
711
741
  // Outlet/Routes/Suspense output returned via $component/asComponent) is BOTH
712
742
  // isComponent AND isSignal; caching it would freeze its first value (so a
713
- // boundary that later switches from a single node to an array fallback ->
714
- // children would never update). Read those reactively in the else branch.
743
+ // boundary that later switches from a single node to an array: fallback ->
744
+ // children: would never update). Read those reactively in the else branch.
715
745
  if (!componentCache)
716
746
  componentCache = new Map();
717
747
  if (!componentCache.has(item))
@@ -719,17 +749,17 @@ reactiveSlots) {
719
749
  resolved = componentCache.get(item);
720
750
  }
721
751
  else {
722
- // Signal/memo (or memo-stamped component) read to track reactivity.
752
+ // Signal/memo (or memo-stamped component): read to track reactivity.
723
753
  resolved = item();
724
754
  }
725
755
  // Unwrap plain wrappers; stop at signals and component thunks.
726
- // Cache calls by function reference a plain function returned by a
756
+ // Cache calls by function reference, a plain function returned by a
727
757
  // signal/memo (e.g. `createMemo(() => cond ? () => <JSX> : ...)`) is a
728
758
  // stable reference as long as the memo doesn't re-evaluate. Calling it
729
759
  // once and caching the result means re-runs of the outer render effect
730
760
  // (triggered by unrelated signals like `activeLevel()`) hit the cache and
731
761
  // return the same DOM nodes. reconcileArrays then sees identical references
732
- // and skips the slot the focused <input> is never removed.
762
+ // and skips the slot, the focused <input> is never removed.
733
763
  while (typeof resolved === 'function' &&
734
764
  !isPromise(resolved) &&
735
765
  !isSignal(resolved) &&
@@ -753,8 +783,8 @@ reactiveSlots) {
753
783
  reactiveSlots.push({ marker: slot, sig: resolved });
754
784
  continue;
755
785
  }
756
- // Read the signal tracked inside the enclosing render effect.
757
- // Use flattenArray recursively (NOT normalizeValue) so
786
+ // Read the signal: tracked inside the enclosing render effect.
787
+ // Use flattenArray recursively (not normalizeValue) so
758
788
  // component thunks inside the result go through componentCache.
759
789
  let val = resolved();
760
790
  while (typeof val === 'function' && isSignal(val))
@@ -768,7 +798,7 @@ reactiveSlots) {
768
798
  flattenArray([val], result, parent, marker, track, componentCache);
769
799
  continue;
770
800
  }
771
- // Other function type skip
801
+ // Other function type: skip
772
802
  continue;
773
803
  }
774
804
  // resolved is not a function
@@ -834,15 +864,15 @@ reactiveSlots) {
834
864
  // ============================================================================
835
865
  function reconcileArrays(parent, a, b, marker) {
836
866
  // Pre-process: clone any node already owned by a different parent.
837
- // The Babel plugin hoists static elements to module-level the same node
867
+ // The Babel plugin hoists static elements to module-level, the same node
838
868
  // can appear in `b` for multiple parents simultaneously. Clone to avoid
839
869
  // stealing it from its current owner; future reconciles track the clone.
840
870
  //
841
- // A DocumentFragment is NOT such an owner. `cloneTemplate` hands back the clone still
871
+ // A DocumentFragment is not such an owner. `cloneTemplate` hands back the clone still
842
872
  // sitting in its fragment, so a template-cloned element arrives here with a non-null
843
873
  // parent that nothing else refers to. Cloning it again threw away the node whose props
844
- // had just been applied `cloneNode` copies attributes but not the event-handler
845
- // properties so an element built from a template lost its handlers the moment it was
874
+ // had just been applied: `cloneNode` copies attributes but not the event-handler
875
+ // properties, so an element built from a template lost its handlers the moment it was
846
876
  // inserted as part of an array. Moving it out of the fragment is exactly right.
847
877
  for (let i = 0; i < b.length; i++) {
848
878
  const owner = b[i].parentNode;
@@ -859,18 +889,18 @@ function reconcileArrays(parent, a, b, marker) {
859
889
  ? a[aEnd - 1].nextSibling ?? undefined
860
890
  : marker, map = null;
861
891
  while (aStart < aEnd || bStart < bEnd) {
862
- // 1. Common prefix already in the right place, skip.
892
+ // 1. Common prefix: already in the right place, skip.
863
893
  if (a[aStart] === b[bStart]) {
864
894
  aStart++;
865
895
  bStart++;
866
896
  continue;
867
897
  }
868
- // 2. Common suffix already in the right place, shrink window.
898
+ // 2. Common suffix: already in the right place, shrink window.
869
899
  while (aEnd > aStart && bEnd > bStart && a[aEnd - 1] === b[bEnd - 1]) {
870
900
  aEnd--;
871
901
  bEnd--;
872
902
  }
873
- // 3. Old list exhausted insert remaining new nodes.
903
+ // 3. Old list exhausted: insert remaining new nodes.
874
904
  if (aEnd === aStart) {
875
905
  const node = bEnd < bLength
876
906
  ? bStart
@@ -880,7 +910,7 @@ function reconcileArrays(parent, a, b, marker) {
880
910
  while (bStart < bEnd)
881
911
  insertBefore(parent, b[bStart++], node);
882
912
  }
883
- // 4. New list exhausted remove remaining old nodes.
913
+ // 4. New list exhausted: remove remaining old nodes.
884
914
  else if (bEnd === bStart) {
885
915
  while (aStart < aEnd) {
886
916
  if ((!map || !map.has(a[aStart])) && a[aStart].parentNode === parent)
@@ -918,7 +948,7 @@ function reconcileArrays(parent, a, b, marker) {
918
948
  insertBefore(parent, b[bStart++], node);
919
949
  }
920
950
  else {
921
- // Guard replaceChild: if a[aStart] was moved out by another effect, insert instead.
951
+ // Guard replaceChild, if a[aStart] was moved out by another effect, insert instead.
922
952
  if (a[aStart].parentNode === parent) {
923
953
  parent.replaceChild(b[bStart++], a[aStart++]);
924
954
  }
@@ -1113,7 +1143,7 @@ export function setClassName(element, value) {
1113
1143
  return;
1114
1144
  }
1115
1145
  if (typeof value === 'function') {
1116
- // A render effect, like every other attribute binding `spread` already wraps
1146
+ // A render effect, like every other attribute binding: `spread` already wraps
1117
1147
  // class this way, so going through `applyProps` shouldn't schedule it later.
1118
1148
  createRenderEffect(() => {
1119
1149
  reportReactiveBinding(element, 'attribute', 'class', value);
@@ -1142,7 +1172,7 @@ export function setClassName(element, value) {
1142
1172
  element.setAttribute('class', String(value));
1143
1173
  }
1144
1174
  /**
1145
- * Apply a `classList`-style map `{ 'a b': condition }` toggling each class on
1175
+ * Apply a `classList`-style map `{ 'a b': condition }`, toggling each class on
1146
1176
  * the element. A condition may be a value or a reactive accessor; when
1147
1177
  * `disposers` is provided, accessor entries are wired with a render effect so
1148
1178
  * they update fine-grained (Solid-style `classList`).
@@ -1192,6 +1222,19 @@ export function setStyle(element, _value) {
1192
1222
  // ============================================================================
1193
1223
  // spread
1194
1224
  // ============================================================================
1225
+ /**
1226
+ * Apply a props object to an element, and re-apply what changes.
1227
+ *
1228
+ * Handles every prop kind the compiler can emit: attributes, properties, events,
1229
+ * `class`/`style` in their object and string forms, `ref` and `use:`. Each reactive prop
1230
+ * gets its own effect, so a change touches one attribute rather than re-running the
1231
+ * element.
1232
+ *
1233
+ * `prevProps` is what was applied last, which is how a removed key is cleared rather
1234
+ * than left behind. A getter-backed prop stays reactive: the value is read inside the
1235
+ * effect, not when the object was built, which is what keeps `splitProps` and a
1236
+ * polymorphic wrapper live.
1237
+ */
1195
1238
  export function spread(options) {
1196
1239
  const { element, props, prevProps = EMPTY_OBJ, isSVG = false, skipChildren = false, } = options;
1197
1240
  if (!element || !isDomElement(element)) {
@@ -1204,7 +1247,7 @@ export function spread(options) {
1204
1247
  }
1205
1248
  // Trust the element's real namespace over the compile-time isSVG flag: the codegen only marks
1206
1249
  // some SVG elements (e.g. <svg>/<defs>) and misses nested ones (<linearGradient>, <stop>). For
1207
- // an actually-SVG element, a key like `x1` IS `in element` (read-only SVGAnimatedLength), so the
1250
+ // an actually-SVG element, a key like `x1` is `in element` (read-only SVGAnimatedLength), so the
1208
1251
  // property branch below would throw "Cannot set property x1". Detecting SVG by namespaceURI keeps
1209
1252
  // those on the setAttribute path. (Matters under hydration, where we adopt the real, correctly
1210
1253
  // namespaced server node.)
@@ -1238,7 +1281,7 @@ export function spread(options) {
1238
1281
  continue;
1239
1282
  }
1240
1283
  // Forced bindings: prop:x always assigns the property, attr:x always sets the
1241
- // attribute, bool:x toggles a boolean attribute skipping the heuristic below.
1284
+ // attribute, bool:x toggles a boolean attribute, skipping the heuristic below.
1242
1285
  if (key.startsWith('prop:')) {
1243
1286
  reportReactiveDirective(element, 'prop', key.slice(5), value);
1244
1287
  disposers.push(setDynamicProperty(element, key.slice(5), value));
@@ -1308,12 +1351,12 @@ export function spread(options) {
1308
1351
  : setDynamicAttribute(element, key, value, elementIsSVG));
1309
1352
  }
1310
1353
  else {
1311
- // A plain, non-function value may still be backed by a reactive GETTER —
1354
+ // A plain, non-function value may still be backed by a reactive getter:
1312
1355
  // component props compile to `get x() {...}`, and splitProps/mergeProps
1313
1356
  // forward those getters. Reading `props[key]` once (above) snapshots the
1314
1357
  // current value and drops the dependency, so the DOM never updates. Re-read
1315
1358
  // it inside a render effect so the getter's reads are tracked. (A truly
1316
- // static value just runs the effect once with no deps negligible.)
1359
+ // static value just runs the effect once with no deps: negligible.)
1317
1360
  disposers.push(createRenderEffect(() => asProperty
1318
1361
  ? setProperty(element, key, props[key])
1319
1362
  : setAttribute(element, key, props[key], elementIsSVG)));
@@ -1356,7 +1399,7 @@ export function applyProps(element, props, isSVG = false) {
1356
1399
  addNativeEventListener(element, key.slice(3), val);
1357
1400
  continue;
1358
1401
  }
1359
- // Forced bindings see spread().
1402
+ // Forced bindings: see spread().
1360
1403
  if (key.startsWith('prop:')) {
1361
1404
  reportReactiveDirective(element, 'prop', key.slice(5), val);
1362
1405
  setDynamicProperty(element, key.slice(5), val);
@@ -1376,7 +1419,7 @@ export function applyProps(element, props, isSVG = false) {
1376
1419
  const name = key.slice(2).toLowerCase();
1377
1420
  if (typeof val === 'function') {
1378
1421
  if (NON_DELEGATED.has(name)) {
1379
- // Non-bubbling events (blur, focus, scroll): store handler on the element
1422
+ // Non-bubbling events (blur, focus, scroll...): store handler on the element
1380
1423
  // and attach a single stable forwarder listener. This avoids
1381
1424
  // addEventListener/removeEventListener churn when a reactive scope re-runs,
1382
1425
  // which would silently drop the listener via onCleanup before re-adding it.
@@ -1414,7 +1457,7 @@ export function applyProps(element, props, isSVG = false) {
1414
1457
  }
1415
1458
  if (key === 'style') {
1416
1459
  // An accessor reaches applyProps whenever a component forwards a computed
1417
- // style through a props object rather than as a compiled JSX attribute
1460
+ // style through a props object rather than as a compiled JSX attribute,
1418
1461
  // which is what every polymorphic/`as` wrapper does. class and classList
1419
1462
  // already handled that; style applied once and never updated.
1420
1463
  reportKeys(element, 'style', val);
@@ -1428,9 +1471,9 @@ export function applyProps(element, props, isSVG = false) {
1428
1471
  continue;
1429
1472
  }
1430
1473
  if (key === 'innerHTML') {
1431
- // Accept a static string OR a reactive accessor. A function is wrapped in an effect so
1474
+ // Accept a static string or a reactive accessor. A function is wrapped in an effect so
1432
1475
  // updates apply; a plain value is set once. (The previous code always called val(), which
1433
- // threw on a static string wiping content during hydration.)
1476
+ // threw on a static string, wiping content during hydration.)
1434
1477
  if (typeof val === 'function') {
1435
1478
  const dispose = createEffect(() => {
1436
1479
  let v = val();
@@ -1445,7 +1488,7 @@ export function applyProps(element, props, isSVG = false) {
1445
1488
  }
1446
1489
  continue;
1447
1490
  }
1448
- // Special case: <input>/<textarea> value use DOM property assignment, not
1491
+ // Special case: <input>/<textarea> value: use DOM property assignment, not
1449
1492
  // setAttribute. Skip reactive updates while the element has focus so user
1450
1493
  // input is never interrupted and focus is never lost due to signal writes.
1451
1494
  if (key === 'value' && (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA')) {
@@ -1455,7 +1498,7 @@ export function applyProps(element, props, isSVG = false) {
1455
1498
  // console.log('apply inpit props', v, element);
1456
1499
  while (typeof v === 'function' && v.length === 0)
1457
1500
  v = v();
1458
- // Only update if element is NOT currently focused don't fight user input
1501
+ // Only update if element is not currently focused: don't fight user input
1459
1502
  if (document.activeElement !== element) {
1460
1503
  element.value = v ?? '';
1461
1504
  }
@@ -1481,7 +1524,7 @@ export function applyProps(element, props, isSVG = false) {
1481
1524
  let v = val();
1482
1525
  while (typeof v === 'function' && v.length === 0)
1483
1526
  v = v();
1484
- // Only update if element is NOT currently focused don't fight user input
1527
+ // Only update if element is not currently focused: don't fight user input
1485
1528
  if (document.activeElement !== element) {
1486
1529
  element.checked = v ?? '';
1487
1530
  }
@@ -1489,7 +1532,7 @@ export function applyProps(element, props, isSVG = false) {
1489
1532
  onCleanup(() => { dispose(); });
1490
1533
  }
1491
1534
  else {
1492
- // Plain value possibly behind a reactive getter re-read live. See the
1535
+ // Plain value possibly behind a reactive getter: re-read live. See the
1493
1536
  // value branch above.
1494
1537
  createRenderEffect(() => {
1495
1538
  if (document.activeElement !== element) {
@@ -1499,8 +1542,8 @@ export function applyProps(element, props, isSVG = false) {
1499
1542
  }
1500
1543
  continue;
1501
1544
  }
1502
- // Special case: <select value=...> must assign .value as DOM property
1503
- // AFTER options are in the DOM. We defer the initial run via queueMicrotask
1545
+ // Special case: <select value=...>: must assign .value as DOM property
1546
+ // after options are in the DOM. We defer the initial run via queueMicrotask
1504
1547
  // (children/options are inserted synchronously after applyProps returns).
1505
1548
  if (key === 'value' && element.tagName === 'SELECT') {
1506
1549
  if (typeof val === 'function') {
@@ -1520,7 +1563,7 @@ export function applyProps(element, props, isSVG = false) {
1520
1563
  onCleanup(() => { dispose(); });
1521
1564
  }
1522
1565
  else {
1523
- // Plain value possibly behind a reactive getter re-read live, keeping the
1566
+ // Plain value possibly behind a reactive getter: re-read live, keeping the
1524
1567
  // first assignment deferred until the options are in the DOM.
1525
1568
  let firstRun = true;
1526
1569
  createRenderEffect(() => {
@@ -1560,19 +1603,19 @@ export function applyProps(element, props, isSVG = false) {
1560
1603
  }
1561
1604
  }
1562
1605
  /**
1563
- * Merge several (possibly reactive) prop sources into one, last-source-wins but a later
1564
- * source's `undefined` does NOT clobber an earlier defined value; it falls through to it. This
1606
+ * Merge several (possibly reactive) prop sources into one, last-source-wins, but a later
1607
+ * source's `undefined` does not clobber an earlier defined value; it falls through to it. This
1565
1608
  * is what makes `mergeProps(defaults, props)` work, and why the compiler can emit
1566
1609
  * `mergeProps(...spreads, baseProps)` for JSX spreads (`<div {...a} {...b} />`) without a missing
1567
1610
  * `b.x` wiping `a.x`.
1568
1611
  *
1569
1612
  * Like Solid's mergeProps (and `splitProps` above), the result is a Proxy that resolves each key
1570
- * LAZILY off the sources getter/signal-backed props stay live (re-read on every access, not
1613
+ * LAZILY off the sources: getter/signal-backed props stay live (re-read on every access, not
1571
1614
  * snapshotted), and the key set is the union across all sources (so dynamic prop objects work).
1572
1615
  */
1573
1616
  export function mergeProps(...sources) {
1574
1617
  // Last defined (non-undefined) value wins; scan sources from the end so a later `undefined`
1575
- // falls through to an earlier source. Reads `source[key]` live each call no snapshot.
1618
+ // falls through to an earlier source. Reads `source[key]` live each call, no snapshot.
1576
1619
  const resolve = (key) => {
1577
1620
  for (let i = sources.length - 1; i >= 0; i--) {
1578
1621
  const s = sources[i];
@@ -1605,7 +1648,7 @@ export function mergeProps(...sources) {
1605
1648
  }
1606
1649
  /**
1607
1650
  * Split a (possibly reactive) props object into groups of keys plus a `rest`,
1608
- * preserving reactivity each returned object is a Proxy whose `get` forwards
1651
+ * preserving reactivity, each returned object is a Proxy whose `get` forwards
1609
1652
  * to the original `props`, so a getter/signal-backed prop stays live.
1610
1653
  *
1611
1654
  * const [local, rest] = splitProps(props, ['class', 'id']);
@@ -1742,7 +1785,7 @@ export function isPromise(v) {
1742
1785
  v instanceof Promise);
1743
1786
  }
1744
1787
  // ============================================================================
1745
- // isAccessor deprecated, kept for backward compat
1788
+ // isAccessor: deprecated, kept for backward compat
1746
1789
  // ============================================================================
1747
1790
  export function isAccessor(value) {
1748
1791
  return typeof value === 'function' && value.length === 0 && isSignal(value);
@@ -1759,7 +1802,7 @@ export function render(element, container, context) {
1759
1802
  // This is critical for context propagation (Provider/useContext).
1760
1803
  createRoot((dispose) => {
1761
1804
  batch(() => {
1762
- // Clear container (but not when hydrating we reuse the server DOM).
1805
+ // Clear container (but not when hydrating: we reuse the server DOM).
1763
1806
  if (!hydration.active) {
1764
1807
  container.textContent = '';
1765
1808
  }
@@ -1785,15 +1828,15 @@ export function render(element, container, context) {
1785
1828
  * Normalize what `bind:` was given into a `[read, write]` pair.
1786
1829
  *
1787
1830
  * `createSignal` hands back that pair already; `signal` hands back `{ get, set }`. Both
1788
- * are the same primitive, so `bind:value` has to take either steering people to the
1831
+ * are the same primitive, so `bind:value` has to take either, steering people to the
1789
1832
  * new API and then refusing it in a directive would be its own kind of broken.
1790
1833
  */
1791
1834
  export function bindPair(source) {
1792
1835
  if (Array.isArray(source))
1793
1836
  return source;
1794
- // `signal()` is the accessor itself with `set` attached.
1837
+ // `signal(...)` is the accessor itself with `set` attached.
1795
1838
  if (typeof source === 'function' && typeof source.set === 'function') {
1796
1839
  return [source, source.set];
1797
1840
  }
1798
- throw new TypeError('[fluixi] bind: needs a signal either `signal()` or the `[get, set]` pair from `createSignal()`.');
1841
+ throw new TypeError('[fluixi] bind: needs a signal, either `signal(...)` or the `[get, set]` pair from `createSignal(...)`.');
1799
1842
  }