balises 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -268,45 +268,59 @@ When rendering lists that change frequently, use `each()` for keyed reconciliati
268
268
 
269
269
  **Note:** The `each()` function is opt-in via the `balises/each` import to keep the base bundle small. Use `html.with(eachPlugin)` to enable keyed list support.
270
270
 
271
+ **Two forms:**
272
+
273
+ 1. **Two-arg form** (object reference as key): Render receives raw item. DOM reused only when same object reference appears.
274
+ 2. **Three-arg form** (explicit key function): Render receives `ReadonlySignal<T>`. DOM reused when keys match, even with new object references.
275
+
271
276
  ```ts
272
277
  import { html as baseHtml, signal } from "balises";
273
278
  import eachPlugin, { each } from "balises/each";
274
279
 
275
280
  const html = baseHtml.with(eachPlugin);
276
281
 
277
- const items = signal([
278
- { id: 1, name: signal("Alice") },
279
- { id: 2, name: signal("Bob") },
282
+ // Three-arg form: explicit key, receives ReadonlySignal
283
+ // DOM is preserved when keys match (ideal for API data)
284
+ const users = signal([
285
+ { id: 1, name: "Alice" },
286
+ { id: 2, name: "Bob" },
280
287
  ]);
281
288
 
282
289
  html`
283
290
  <ul>
284
291
  ${each(
285
- items,
286
- (item) => item.id,
287
- (item) => html`<li>${item.name}</li>`,
292
+ users,
293
+ (user) => user.id,
294
+ (userSignal) => html`<li>${() => userSignal.value.name}</li>`,
288
295
  )}
289
296
  </ul>
290
297
  `.render();
291
298
 
292
- // Append: only creates one new node
293
- items.value = [...items.value, { id: 3, name: signal("Carol") }];
299
+ // Refetch from API - DOM preserved, content updated via signal
300
+ users.value = [
301
+ { id: 1, name: "Alicia" }, // Same key, new object - DOM preserved!
302
+ { id: 2, name: "Bobby" },
303
+ { id: 3, name: "Carol" }, // New key - new DOM created
304
+ ];
294
305
 
295
- // Update content: surgical update, no list diffing
296
- items.value[0].name.value = "Alicia";
306
+ // Two-arg form: object reference as key, receives raw item
307
+ const items = signal([{ name: "Item 1" }, { name: "Item 2" }]);
297
308
 
298
- // Reorder: moves existing nodes, no recreation
299
- items.value = [...items.value].reverse();
309
+ html`
310
+ <ul>
311
+ ${each(items, (item) => html`<li>${item.name}</li>`)}
312
+ </ul>
313
+ `.render();
300
314
  ```
301
315
 
302
316
  Signatures:
303
317
 
304
318
  ```ts
305
- each(list, keyFn, renderFn); // keyed by keyFn(item, index)
306
- each(list, renderFn); // keyed by object reference or index
319
+ each(list, keyFn, renderFn); // Three-arg: keyFn extracts key, renderFn receives ReadonlySignal<T>
320
+ each(list, renderFn); // Two-arg: object reference as key, renderFn receives raw T
307
321
  ```
308
322
 
309
- If you want to update item content without triggering list reconciliation, nest signals inside your items (like `name: signal("Alice")` above).
323
+ **Important:** When using the three-arg form, access item properties through `itemSignal.value` and wrap in `() => ...` for reactive updates.
310
324
 
311
325
  ## Reactivity API
312
326
 
@@ -334,6 +348,16 @@ count.value = count.value + 1;
334
348
  count.value = count.value * 2;
335
349
  ```
336
350
 
351
+ **Reading without tracking dependencies:**
352
+
353
+ ```ts
354
+ const count = signal(0);
355
+
356
+ // peek() reads without creating a dependency
357
+ // Useful in event handlers where you don't want reactivity
358
+ button.onclick = () => console.log(count.peek());
359
+ ```
360
+
337
361
  ### `computed<T>(fn)`
338
362
 
339
363
  Derives a value from other signals. Automatically tracks dependencies.
@@ -665,23 +689,23 @@ Performance comparison of Balises against other popular reactive libraries. Benc
665
689
  ┌───────┬───────────────────┬───────┬───────────────┬──────────────────┐
666
690
  │ Rank │ Library │ Score │ Avg Time (μs) │ vs Fastest │
667
691
  ├───────┼───────────────────┼───────┼───────────────┼──────────────────┤
668
- │ #1 🏆 │ preact@1.12.1 │ 0.000 │ 65.61 │ 1.00x (baseline) │
692
+ │ #1 🏆 │ preact@1.12.1 │ 0.000 │ 64.17 │ 1.00x (baseline) │
669
693
  ├───────┼───────────────────┼───────┼───────────────┼──────────────────┤
670
- │ #2 │ balises@0.5.0 │ 0.02381.43 │ 1.24x
694
+ │ #2 │ balises@0.7.0 │ 0.03385.48 │ 1.33x
671
695
  ├───────┼───────────────────┼───────┼───────────────┼──────────────────┤
672
- │ #3 │ vue@3.5.26 │ 0.094 │ 94.79 │ 1.44x
696
+ │ #3 │ vue@3.5.26 │ 0.095 │ 94.27 │ 1.47x
673
697
  ├───────┼───────────────────┼───────┼───────────────┼──────────────────┤
674
- │ #4 │ maverick@6.0.0 │ 0.145 │ 124.58 │ 1.90x
698
+ │ #4 │ maverick@6.0.0 │ 0.146 │ 124.89 │ 1.95x
675
699
  ├───────┼───────────────────┼───────┼───────────────┼──────────────────┤
676
- │ #5 │ usignal@0.10.0 │ 0.179 │ 133.41 │ 2.03x
700
+ │ #5 │ usignal@0.10.0 │ 0.175 │ 133.48 │ 2.08x
677
701
  ├───────┼───────────────────┼───────┼───────────────┼──────────────────┤
678
- │ #6 │ angular@19.2.17 │ 0.205160.61 │ 2.45x
702
+ │ #6 │ angular@19.2.17 │ 0.214169.86 │ 2.65x
679
703
  ├───────┼───────────────────┼───────┼───────────────┼──────────────────┤
680
- │ #7 │ solid@1.9.10 │ 0.346265.23 │ 4.04x
704
+ │ #7 │ solid@1.9.10 │ 0.343260.01 │ 4.05x
681
705
  ├───────┼───────────────────┼───────┼───────────────┼──────────────────┤
682
- │ #8 │ mobx@6.15.0 │ 0.858919.91 │ 14.02x
706
+ │ #8 │ mobx@6.15.0 │ 0.854910.41 │ 14.19x
683
707
  ├───────┼───────────────────┼───────┼───────────────┼──────────────────┤
684
- │ #9 │ hyperactiv@0.11.3 │ 1.000 │ 1056.56 │ 16.10x
708
+ │ #9 │ hyperactiv@0.11.3 │ 1.000 │ 1051.12 │ 16.38x
685
709
  └───────┴───────────────────┴───────┴───────────────┴──────────────────┘
686
710
  ```
687
711
 
@@ -693,13 +717,13 @@ Performance comparison of Balises against other popular reactive libraries. Benc
693
717
  ├───────────────────┼───────────────┼─────────────┼────────────────┼────────────────────┼─────────────┼──────────────┼──────────┤
694
718
  │ preact@1.12.1 │ #1 🏆 │ #1 🏆 │ #2 │ #1 🏆 │ #1 🏆 │ #2 │ 1.3 │
695
719
  ├───────────────────┼───────────────┼─────────────┼────────────────┼────────────────────┼─────────────┼──────────────┼──────────┤
696
- │ balises@0.5.0 │ #2 │ #2 │ #1 🏆 │ #2 │ #2 │ #1 🏆 │ 1.7
720
+ │ balises@0.7.0 │ #3 │ #2 │ #1 🏆 │ #2 │ #2 │ #1 🏆 │ 1.8
697
721
  ├───────────────────┼───────────────┼─────────────┼────────────────┼────────────────────┼─────────────┼──────────────┼──────────┤
698
- │ vue@3.5.26 │ #3 │ #3 │ #5 │ #3 │ #3 │ #4 │ 3.5 │
722
+ │ vue@3.5.26 │ #2 │ #3 │ #5 │ #3 │ #3 │ #5 │ 3.5 │
699
723
  ├───────────────────┼───────────────┼─────────────┼────────────────┼────────────────────┼─────────────┼──────────────┼──────────┤
700
- │ maverick@6.0.0 │ #5 │ #4 │ #4 │ #4 │ #4 │ #5 │ 4.3 │
724
+ │ maverick@6.0.0 │ #5 │ #5 │ #4 │ #4 │ #4 │ #4 │ 4.3 │
701
725
  ├───────────────────┼───────────────┼─────────────┼────────────────┼────────────────────┼─────────────┼──────────────┼──────────┤
702
- │ usignal@0.10.0 │ #4 │ #5 │ #3 │ #5 │ #8 │ #6 │ 5.2
726
+ │ usignal@0.10.0 │ #4 │ #4 │ #3 │ #5 │ #8 │ #6 │ 5.0
703
727
  ├───────────────────┼───────────────┼─────────────┼────────────────┼────────────────────┼─────────────┼──────────────┼──────────┤
704
728
  │ angular@19.2.17 │ #6 │ #6 │ #6 │ #6 │ #5 │ #3 │ 5.3 │
705
729
  ├───────────────────┼───────────────┼─────────────┼────────────────┼────────────────────┼─────────────┼──────────────┼──────────┤
@@ -726,7 +750,7 @@ Performance comparison of Balises against other popular reactive libraries. Benc
726
750
  - These are synthetic benchmarks measuring pure reactivity - real apps should consider the whole picture (ecosystem, docs, community, etc.)
727
751
  - Lower rank = better performance
728
752
 
729
- _Last updated: 2026-01-03_
753
+ _Last updated: 2026-01-05_
730
754
 
731
755
  <!-- BENCHMARK_RESULTS_END -->
732
756
 
@@ -141,6 +141,19 @@ var Signal = class {
141
141
  update(fn) {
142
142
  this.value = fn(this.#value);
143
143
  }
144
+ /**
145
+ * Read the signal value without tracking dependencies.
146
+ * Useful in event handlers where you want the current value
147
+ * but don't want to create a reactive dependency.
148
+ *
149
+ * @example
150
+ * const count = signal(0);
151
+ * // In an event handler - no dependency tracking
152
+ * button.onclick = () => console.log(count.peek());
153
+ */
154
+ peek() {
155
+ return this.#value;
156
+ }
144
157
  /** @internal */
145
158
  get targets() {
146
159
  return this.#targets;
@@ -152,6 +165,32 @@ var Signal = class {
152
165
  };
153
166
  /** Create a new signal with the given initial value. */
154
167
  const signal = (value) => new Signal(value);
168
+ /**
169
+ * A read-only view of a Signal.
170
+ * Provides reactive access without allowing external mutation.
171
+ * Used by `each()` to pass item signals to render functions.
172
+ */
173
+ var ReadonlySignal = class {
174
+ #signal;
175
+ /** @internal */
176
+ constructor(signal$1) {
177
+ this.#signal = signal$1;
178
+ }
179
+ get value() {
180
+ return this.#signal.value;
181
+ }
182
+ /**
183
+ * Read the signal value without tracking dependencies.
184
+ * Useful in event handlers where you want the current value
185
+ * but don't want to create a reactive dependency.
186
+ */
187
+ peek() {
188
+ return this.#signal.peek();
189
+ }
190
+ subscribe(fn) {
191
+ return this.#signal.subscribe(fn);
192
+ }
193
+ };
155
194
 
156
195
  //#endregion
157
196
  //#region src/signals/computed.ts
@@ -407,7 +446,7 @@ function store(obj) {
407
446
  //#endregion
408
447
  //#region src/signals/index.ts
409
448
  /** Check if a value is a reactive signal or computed. @internal */
410
- const isSignal = (value) => value instanceof Signal || value instanceof Computed;
449
+ const isSignal = (value) => value instanceof Signal || value instanceof Computed || value instanceof ReadonlySignal;
411
450
 
412
451
  //#endregion
413
452
  //#region src/parser.ts
@@ -1 +1 @@
1
- {"version":3,"file":"balises.esm.js","names":["context: Computed<unknown> | null","batchQueue: Set<Subscriber> | null","disposalStack: Array<Array<() => void>> | null","disposers: Array<() => void>","onTrack: {\n current: ((source: Signal<unknown> | Computed<unknown>) => void) | null;\n}","#value","#targets","#subs","#fn","#recompute","#dirty","#value","#subs","#sources","#sourceIndex","queue: Computed<unknown>[]","toNotify: Array<{ c: Computed<unknown>; old: unknown }>","#targets","#computing","cleanup: (() => void) | undefined","disposeScope: (() => void) | undefined","dispose: (() => void) | undefined","strings: TemplateStringsArray","values: unknown[]","plugins: InterpolationPlugin[]","disposers: (() => void)[]","stack: (Element | DocumentFragment)[]","reactives: Reactive<unknown>[]","pluginDisposers: (() => void)[]","nodes: Node[]","childDisposers: (() => void)[]","html: HtmlTag"],"sources":["../src/signals/context.ts","../src/signals/signal.ts","../src/signals/computed.ts","../src/signals/effect.ts","../src/signals/store.ts","../src/signals/index.ts","../src/parser.ts","../src/template.ts"],"sourcesContent":["/**\n * Global state and batching for the reactive system.\n */\n\nimport type { Computed } from \"./computed.js\";\nimport type { Signal } from \"./signal.js\";\n\n/** Callback function for subscribers */\nexport type Subscriber = () => void;\n\n/** The currently executing computed (for dependency tracking) */\nexport let context: Computed<unknown> | null = null;\n\n/** Set the current execution context */\nexport function setContext(c: Computed<unknown> | null): void {\n context = c;\n}\n\n/** Batching: defer subscriber notifications until batch completes */\nlet batchDepth = 0;\nlet batchQueue: Set<Subscriber> | null = null;\n\n/**\n * Batch multiple signal updates into a single notification pass.\n * Subscribers are only notified after the batch function completes.\n */\nexport function batch<T>(fn: () => T): T {\n batchDepth++;\n if (batchDepth === 1) batchQueue = new Set();\n try {\n return fn();\n } finally {\n if (--batchDepth === 0) {\n const q = batchQueue!;\n batchQueue = null;\n for (const sub of q) sub();\n }\n }\n}\n\n/** Check if currently batching */\nexport function isBatching(): boolean {\n return batchDepth > 0;\n}\n\n/** Add a subscriber to the batch queue */\nexport function enqueueBatchOne(sub: Subscriber): void {\n batchQueue!.add(sub);\n}\n\n/** Add multiple subscribers to the batch queue */\nexport function enqueueBatchAll(subs: Subscriber[]): void {\n for (let i = 0; i < subs.length; i++) batchQueue!.add(subs[i]!);\n}\n\n/** Scope disposal: collect all disposers in a scope */\nlet disposalStack: Array<Array<() => void>> | null = null;\n\n/**\n * Create a disposal scope that collects all subscriptions and computeds created within.\n * Returns the result of the function and a dispose function that cleans up all resources.\n *\n * @example\n * ```ts\n * const [result, dispose] = scope(() => {\n * const count = signal(0);\n * const doubled = computed(() => count.value * 2);\n * effect(() => console.log(doubled.value));\n * return { count, doubled };\n * });\n *\n * // Later: clean up all subscriptions and computeds\n * dispose();\n * ```\n */\nexport function scope<T>(fn: () => T): [result: T, dispose: () => void] {\n const disposers: Array<() => void> = [];\n\n // Push new disposal context\n if (!disposalStack) disposalStack = [];\n disposalStack.push(disposers);\n\n try {\n const result = fn();\n return [\n result,\n () => {\n for (let i = disposers.length - 1; i >= 0; i--) {\n disposers[i]!();\n }\n disposers.length = 0;\n },\n ];\n } finally {\n // Pop disposal context\n disposalStack.pop();\n if (!disposalStack.length) disposalStack = null;\n }\n}\n\n/**\n * Register a disposer in the current scope (if any).\n * This is called internally by computed/effect when they create cleanup functions.\n */\nexport function registerDisposer(dispose: () => void): void {\n disposalStack?.at(-1)?.push(dispose);\n}\n\n/**\n * Hook for tracking signal/computed accesses.\n * Set by async.ts when track() is active to capture dependencies.\n * Using an object wrapper so async.ts can mutate the current value.\n */\nexport const onTrack: {\n current: ((source: Signal<unknown> | Computed<unknown>) => void) | null;\n} = { current: null };\n","/**\n * Signal - A reactive value container.\n */\n\nimport type { Computed } from \"./computed.js\";\nimport {\n context,\n isBatching,\n enqueueBatchAll,\n onTrack,\n type Subscriber,\n} from \"./context.js\";\n\n/**\n * Remove an item from an array using swap-and-pop (O(1) removal).\n */\nexport function removeFromArray<T>(array: T[], item: T): void {\n const i = array.indexOf(item);\n if (i >= 0) {\n array[i] = array[array.length - 1]!;\n array.pop();\n }\n}\n\n/**\n * A reactive value container. When the value changes, all dependent\n * computeds are marked dirty and subscribers are notified.\n *\n * Uses Object.is() for equality checks to correctly handle NaN values.\n */\nexport class Signal<T> {\n #value: T;\n #subs: Subscriber[] = [];\n #targets: Computed<unknown>[] = [];\n\n constructor(value: T) {\n this.#value = value;\n }\n\n get value(): T {\n if (context) context.trackSource(this);\n if (onTrack.current) onTrack.current(this);\n return this.#value;\n }\n\n set value(v: T) {\n if (Object.is(this.#value, v)) return;\n this.#value = v;\n\n // Mark all dependent computeds as dirty\n const targets = this.#targets;\n for (let i = 0; i < targets.length; i++) {\n targets[i]!.markDirty();\n }\n\n // Notify subscribers\n if (this.#subs.length) {\n if (isBatching()) {\n enqueueBatchAll(this.#subs);\n } else {\n // Copy array to avoid issues if subscribers modify the array during iteration\n const subs = [...this.#subs];\n for (let i = 0; i < subs.length; i++) subs[i]!();\n }\n }\n }\n\n subscribe(fn: Subscriber): () => void {\n this.#subs.push(fn);\n return () => removeFromArray(this.#subs, fn);\n }\n\n /**\n * Update the signal value using an updater function.\n *\n * @param fn - Function that receives current value and returns new value\n *\n * @example\n * const count = signal(0);\n * count.update(n => n + 1); // increment\n */\n update(fn: (current: T) => T): void {\n this.value = fn(this.#value);\n }\n\n /** @internal */\n get targets(): Computed<unknown>[] {\n return this.#targets;\n }\n\n /** @internal */\n deleteTarget(target: Computed<unknown>): void {\n removeFromArray(this.#targets, target);\n }\n}\n\n/** Create a new signal with the given initial value. */\nexport const signal = <T>(value: T) => new Signal(value);\n","/**\n * Computed - A derived reactive value.\n */\n\nimport { Signal, removeFromArray } from \"./signal.js\";\nimport {\n context,\n setContext,\n isBatching,\n enqueueBatchOne,\n registerDisposer,\n onTrack,\n type Subscriber,\n} from \"./context.js\";\n\n/**\n * A derived reactive value. Automatically tracks dependencies and\n * recomputes when any dependency changes.\n *\n * Uses Object.is() for equality checks to correctly handle NaN values.\n */\nexport class Computed<T> {\n #fn: (() => T) | undefined;\n #value: T | undefined;\n #dirty = true;\n #computing = false;\n #subs: Subscriber[] = [];\n #targets: Computed<unknown>[] = [];\n #sources: (Signal<unknown> | Computed<unknown>)[] = [];\n #sourceIndex = 0;\n\n constructor(fn: () => T) {\n this.#fn = fn;\n this.#recompute();\n\n // Auto-register disposal in current root scope\n registerDisposer(() => this.dispose());\n }\n\n get value(): T {\n if (this.#dirty) this.#recompute();\n if (context && context !== this) context.trackSource(this);\n if (onTrack.current) onTrack.current(this);\n return this.#value as T;\n }\n\n subscribe(fn: Subscriber): () => void {\n this.#subs.push(fn);\n return () => removeFromArray(this.#subs, fn);\n }\n\n dispose(): void {\n this.#fn = undefined;\n const sources = this.#sources;\n for (let i = 0; i < sources.length; i++) {\n const source = sources[i];\n if (source) source.deleteTarget(this);\n }\n this.#sources = [];\n this.#subs.length = 0;\n }\n\n /**\n * Called by sources when accessed during recompute.\n * @internal\n */\n trackSource(source: Signal<unknown> | Computed<unknown>): void {\n // Skip tracking if disposed (can happen if dispose() is called during #fn execution)\n if (!this.#fn) return;\n\n const sources = this.#sources;\n const idx = this.#sourceIndex++;\n\n if (idx < sources.length) {\n if (sources[idx] === source) {\n // Same source at same position - nothing to do\n return;\n }\n // Different source - unlink old ones from this position\n for (let i = idx; i < sources.length; i++) {\n const s = sources[i];\n if (s) s.deleteTarget(this);\n }\n sources.length = idx;\n }\n // Add new source\n sources.push(source);\n source.targets.push(this);\n }\n\n /**\n * Mark this computed and all its dependents as dirty.\n * Uses a two-phase approach to avoid cascading issues:\n * 1. Mark all dependents as dirty (no subscriber calls)\n * 2. Notify subscribers after all dirty flags are set\n * @internal\n */\n markDirty(): void {\n if (this.#dirty) return;\n\n // Phase 1: Mark all dependents as dirty, collect subscribers\n const queue: Computed<unknown>[] = [this];\n const toNotify: Array<{ c: Computed<unknown>; old: unknown }> = [];\n\n for (let i = 0; i < queue.length; i++) {\n const c = queue[i]!;\n if (c.#dirty) continue;\n c.#dirty = true;\n\n // Propagate to all targets\n const targets = c.#targets;\n for (let j = 0; j < targets.length; j++) {\n const t = targets[j]!;\n if (!t.#dirty) queue.push(t);\n }\n\n // Collect computeds with subscribers for later notification\n if (c.#subs.length && c.#fn) {\n toNotify.push({ c, old: c.#value });\n }\n }\n\n // Phase 2: Notify subscribers (after all dirty flags are set)\n for (let i = 0; i < toNotify.length; i++) {\n const { c, old } = toNotify[i]!;\n const notify = () => {\n if (c.#fn) {\n c.#recompute();\n if (!Object.is(c.#value, old)) {\n const subs = c.#subs;\n for (let j = 0; j < subs.length; j++) subs[j]!();\n }\n }\n };\n void (isBatching() ? enqueueBatchOne(notify) : notify());\n }\n }\n\n /** @internal */\n get targets(): Computed<unknown>[] {\n return this.#targets;\n }\n\n /** @internal */\n deleteTarget(target: Computed<unknown>): void {\n removeFromArray(this.#targets, target);\n }\n\n #recompute(): void {\n if (this.#computing || !this.#fn) return;\n this.#computing = true;\n\n this.#sourceIndex = 0;\n const prevLen = this.#sources.length;\n\n const prev = context;\n setContext(this);\n try {\n this.#value = this.#fn();\n } finally {\n setContext(prev);\n\n // Unlink removed sources\n const newLen = this.#sourceIndex;\n if (newLen < prevLen) {\n const sources = this.#sources;\n for (let i = newLen; i < prevLen; i++) {\n const source = sources[i];\n if (source) {\n source.deleteTarget(this);\n }\n }\n sources.length = newLen;\n }\n\n this.#dirty = false;\n this.#computing = false;\n }\n }\n}\n\n/** Create a new computed from the given function. */\nexport const computed = <T>(fn: () => T) => new Computed(fn);\n","/**\n * Effect - Run side effects reactively.\n */\n\nimport { computed } from \"./computed.js\";\nimport { context, setContext, registerDisposer } from \"./context.js\";\n\n/**\n * Create a reactive effect that automatically tracks dependencies\n * and re-runs when they change.\n *\n * The effect function can optionally return a cleanup function that will be\n * called before the effect re-runs and when the effect is disposed.\n *\n * @param fn - The effect function to run. May return a cleanup function.\n * @returns A dispose function to stop the effect\n *\n * @example\n * const count = signal(0);\n * const dispose = effect(() => {\n * console.log(\"Count is:\", count.value);\n * });\n *\n * count.value = 1; // logs: \"Count is: 1\"\n * dispose(); // stop the effect\n *\n * @example\n * // With cleanup function\n * const userId = signal(1);\n * const dispose = effect(() => {\n * const subscription = api.subscribe(userId.value);\n * return () => subscription.unsubscribe(); // cleanup\n * });\n *\n * userId.value = 2; // cleanup runs, then effect re-runs with new subscription\n * dispose(); // final cleanup runs\n */\nexport function effect(fn: () => void | (() => void)): () => void {\n let cleanup: (() => void) | undefined;\n let disposed = false;\n\n const c = computed(() => {\n // Run cleanup outside of tracking context to avoid\n // reactive reads in cleanup creating new dependencies\n if (cleanup) {\n const prev = context;\n setContext(null);\n try {\n cleanup();\n } finally {\n setContext(prev);\n }\n }\n cleanup = fn() ?? undefined;\n return undefined;\n });\n // Subscribe to make it reactive (rerun on dependency changes)\n const unsub = c.subscribe(() => {});\n\n const dispose = () => {\n if (disposed) return;\n disposed = true;\n unsub();\n c.dispose();\n cleanup?.();\n };\n\n // Register full effect dispose (with cleanup) in current scope\n // This overrides the computed's auto-registration with a more complete cleanup\n registerDisposer(dispose);\n\n return dispose;\n}\n","/**\n * Store - Reactive wrapper for plain objects.\n */\n\nimport { Signal } from \"./signal.js\";\n\nconst STORE = Symbol();\n\n/**\n * Create a reactive store from a plain object.\n * Each property becomes a signal, and nested objects are recursively wrapped.\n */\nexport function store<T extends object>(obj: T): T {\n const signals = new Map<string | symbol, Signal<unknown>>();\n\n /** Recursively wrap nested objects and arrays */\n const wrap = (value: unknown): unknown => {\n if (typeof value !== \"object\" || value === null) return value;\n if (STORE in value) return value; // Already a store\n if ((value as object).constructor === Object) {\n return store(value as Record<string, unknown>);\n }\n if (Array.isArray(value)) {\n return value.map(wrap);\n }\n return value;\n };\n\n /** Get or create a signal for a property */\n const getSignal = (key: string | symbol, initialValue: unknown) => {\n let sig = signals.get(key);\n if (!sig) {\n sig = new Signal(wrap(initialValue));\n signals.set(key, sig);\n }\n return sig;\n };\n\n return new Proxy(obj, {\n get(target, key) {\n // Allow symbol access (for STORE check and other internal symbols)\n if (typeof key === \"symbol\") {\n return target[key as keyof T];\n }\n return getSignal(key, target[key as keyof T]).value;\n },\n\n set(target, key, value) {\n if (typeof key === \"symbol\") {\n target[key as keyof T] = value;\n return true;\n }\n const wrapped = wrap(value);\n getSignal(key, target[key as keyof T]).value = wrapped;\n target[key as keyof T] = wrapped as T[keyof T];\n return true;\n },\n\n has(target, key) {\n return key === STORE || key in target;\n },\n });\n}\n","/**\n * Reactive signals with automatic dependency tracking.\n *\n * Uses index-based tracking: computed functions are assumed to access\n * their dependencies in the same order on each run. This enables O(1)\n * dependency checks without complex linked-list structures.\n */\n\nexport { Signal, signal } from \"./signal.js\";\nexport { Computed, computed } from \"./computed.js\";\nexport { effect } from \"./effect.js\";\nexport { store } from \"./store.js\";\nexport { batch, scope, type Subscriber } from \"./context.js\";\n\nimport { Signal } from \"./signal.js\";\nimport { Computed } from \"./computed.js\";\n\n/** Common interface for reactive values (Signal or Computed). */\nexport interface Reactive<T> {\n readonly value: T;\n subscribe(fn: () => void): () => void;\n}\n\n/** Check if a value is a reactive signal or computed. @internal */\nexport const isSignal = (value: unknown): value is Reactive<unknown> =>\n value instanceof Signal || value instanceof Computed;\n","/**\n * Streaming HTML parser for template literals.\n * State machine: Text=0, TagOpen=1, TagName=2, InTag=3, AttrName=4, AttrEq=5, AttrVal=6, CloseTag=7, Comment=8\n */\n\n/** Pre-compiled attribute: static strings interleaved with slot indexes */\nexport type Attr = [name: string, statics: string[], indexes: number[]];\n\nexport interface ParseCallbacks {\n onText: (text: string) => void;\n onOpenTag: (tag: string, attrs: Attr[], selfClosing: boolean) => void;\n onClose: () => void;\n onSlot: (index: number) => void;\n}\n\nexport class HTMLParser {\n private s = 0; // state\n private tag = \"\";\n private attr = \"\";\n private statics: string[] = [];\n private indexes: number[] = [];\n private attrs: Attr[] = [];\n private text = \"\";\n private q = \"\";\n\n parseTemplate(strings: TemplateStringsArray, cb: ParseCallbacks) {\n for (let i = 0; i < strings.length; i++) {\n this.parse(strings[i]!, cb);\n if (i < strings.length - 1) this.slot(i, cb);\n }\n if (this.text) {\n cb.onText(this.text);\n this.text = \"\";\n }\n }\n\n private parse(str: string, cb: ParseCallbacks) {\n for (let i = 0; i < str.length; i++) {\n const ch = str[i]!,\n nx = str[i + 1],\n n2 = str[i + 2];\n\n if (this.s === 0) {\n // Text\n if (ch === \"<\") {\n if (this.text) {\n cb.onText(this.text);\n this.text = \"\";\n }\n this.s = 1;\n } else this.text += ch;\n } else if (this.s === 1) {\n // TagOpen\n if (ch === \"/\") this.s = 7;\n else if (ch === \"!\" && nx === \"-\" && n2 === \"-\") {\n i += 2;\n this.s = 8;\n } else if (ch === \"!\" || ch === \"?\") {\n while (i < str.length && str[i] !== \">\") i++;\n this.s = 0;\n } else if (this.isA(ch)) {\n this.tag = ch;\n this.s = 2;\n } else {\n this.text += \"<\" + ch;\n this.s = 0;\n }\n } else if (this.s === 2) {\n // TagName\n if (this.isT(ch)) this.tag += ch;\n else if (this.isW(ch)) {\n this.s = 3;\n } else if (ch === \">\") {\n this.emit(cb, false);\n } else if (ch === \"/\" && nx === \">\") {\n i++;\n this.emit(cb, true);\n }\n } else if (this.s === 3) {\n // InTag\n if (this.isW(ch)) continue;\n if (ch === \">\") this.emit(cb, false);\n else if (ch === \"/\" && nx === \">\") {\n i++;\n this.emit(cb, true);\n } else {\n this.attr = ch;\n this.statics = [\"\"];\n this.indexes = [];\n this.s = 4;\n }\n } else if (this.s === 4) {\n // AttrName\n if (this.isT(ch) || ch === \"_\") this.attr += ch;\n else if (ch === \"=\") this.s = 5;\n else if (this.isW(ch)) {\n this.emitAttr();\n this.s = 3;\n } else if (ch === \">\") {\n this.emitAttr();\n this.emit(cb, false);\n } else if (ch === \"/\" && nx === \">\") {\n this.emitAttr();\n i++;\n this.emit(cb, true);\n }\n } else if (this.s === 5) {\n // AttrEq\n if (ch === '\"' || ch === \"'\") {\n this.q = ch;\n this.s = 6;\n } else if (!this.isW(ch)) {\n this.q = \"\";\n this.statics[0] += ch;\n this.s = 6;\n }\n } else if (this.s === 6) {\n // AttrVal\n const end = this.q\n ? ch === this.q\n : this.isW(ch) || ch === \">\" || ch === \"/\";\n if (end) {\n this.emitAttr();\n this.q = \"\";\n this.s = 3;\n if (ch === \">\") this.emit(cb, false);\n else if (ch === \"/\" && nx === \">\") {\n i++;\n this.emit(cb, true);\n }\n } else {\n this.statics[this.statics.length - 1] += ch;\n }\n } else if (this.s === 7) {\n // CloseTag\n if (ch === \">\") {\n cb.onClose();\n this.s = 0;\n }\n } else if (this.s === 8) {\n // Comment\n if (ch === \"-\" && nx === \"-\" && n2 === \">\") {\n i += 2;\n this.s = 0;\n }\n }\n }\n }\n\n private slot(index: number, cb: ParseCallbacks) {\n if (this.s === 5 || this.s === 6) {\n this.indexes.push(index);\n this.statics.push(\"\");\n if (this.s === 5) this.s = 6;\n } else {\n if (this.text) {\n cb.onText(this.text);\n this.text = \"\";\n }\n cb.onSlot(index);\n }\n }\n\n private emit(cb: ParseCallbacks, self: boolean) {\n cb.onOpenTag(this.tag, this.attrs, self);\n this.tag = \"\";\n this.attrs = [];\n this.s = 0;\n }\n\n private emitAttr() {\n if (this.attr) this.attrs.push([this.attr, this.statics, this.indexes]);\n this.attr = \"\";\n this.statics = [];\n this.indexes = [];\n }\n\n private isA(c: string) {\n return (c >= \"a\" && c <= \"z\") || (c >= \"A\" && c <= \"Z\");\n }\n private isT(c: string) {\n return (\n (c >= \"a\" && c <= \"z\") ||\n (c >= \"A\" && c <= \"Z\") ||\n (c >= \"0\" && c <= \"9\") ||\n c === \"-\" ||\n c === \":\"\n );\n }\n private isW(c: string) {\n return c <= \" \" && c !== \"\";\n }\n}\n","/**\n * HTML template rendering with reactive bindings.\n *\n * Uses tagged template literals to create reactive DOM:\n * - Text interpolation: ${value} or ${signal}\n * - Attribute binding: class=\"${signal}\" (reactive)\n * - Event binding: @click=${handler}\n * - Property binding: .value=${signal} (sets DOM property, not attribute)\n * - Nested templates: ${html`<span>...</span>`}\n * - Arrays: ${items.map(i => html`<li>${i}</li>`)}\n *\n * Extend with plugins via html.with(...plugins) for additional interpolation types.\n */\n\nimport { computed, isSignal, scope, type Reactive } from \"./signals/index.js\";\nimport { HTMLParser, type Attr } from \"./parser.js\";\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\";\n\n/**\n * Plugin that handles custom interpolation types.\n * Return a bind function if this plugin handles the value, null otherwise.\n * First plugin to return non-null wins.\n */\nexport interface InterpolationPlugin {\n (\n value: unknown,\n ): ((marker: Comment, disposers: (() => void)[]) => void) | null;\n}\n\n/**\n * Html template tag function with plugin composition.\n */\nexport interface HtmlTag {\n (strings: TemplateStringsArray, ...values: unknown[]): Template;\n /** Create a new html tag with additional plugins */\n with(...plugins: InterpolationPlugin[]): HtmlTag;\n}\n\n/**\n * Create a computed that wraps function execution in a scope.\n * Nested computeds/effects are automatically disposed on re-run.\n * Returns [computed, dispose] - dispose cleans up both the computed\n * and any nested reactives from the last run.\n */\nfunction scopedComputed<T>(fn: () => T) {\n let disposeScope: (() => void) | undefined;\n\n const c = computed(() => {\n disposeScope?.();\n const [result, dispose] = scope(fn);\n disposeScope = dispose;\n return result;\n });\n\n return [c, () => (c.dispose(), disposeScope?.())] as const;\n}\n\n/**\n * Bind a value to an update function.\n * If reactive, subscribes and returns unsubscribe. Otherwise returns null.\n * Functions are wrapped in computed() for automatic reactivity.\n * Nested computeds/effects created inside functions are automatically\n * disposed when the function re-runs or the binding is disposed.\n */\nfunction bind(\n value: unknown,\n update: (v: unknown) => void,\n): (() => void) | null | undefined {\n let dispose: (() => void) | undefined;\n if (typeof value === \"function\") {\n [value, dispose] = scopedComputed(value as () => unknown);\n }\n if (isSignal(value)) {\n update(value.value);\n const unsub = value.subscribe(() =>\n update((value as Reactive<unknown>).value),\n );\n return dispose ? () => (unsub(), dispose()) : unsub;\n }\n update(value);\n}\n\n/** Result of rendering a template */\nexport interface RenderResult {\n fragment: DocumentFragment;\n dispose: () => void;\n}\n\n/** A parsed HTML template. Call render() to create live DOM. */\nexport class Template {\n constructor(\n private strings: TemplateStringsArray,\n private values: unknown[],\n private plugins: InterpolationPlugin[] = [],\n ) {}\n\n /**\n * Parse template and create live DOM.\n * Returns the fragment and a dispose function to clean up subscriptions.\n */\n render(): RenderResult {\n const fragment = document.createDocumentFragment();\n const disposers: (() => void)[] = [];\n const stack: (Element | DocumentFragment)[] = [fragment];\n const parser = new HTMLParser();\n const values = this.values;\n const plugins = this.plugins;\n\n const handleAttribute = (el: Element, [name, statics, indexes]: Attr) => {\n const idx0 = indexes[0];\n\n // Event binding: @click=${handler}\n if (name[0] === \"@\") {\n if (idx0 != null) {\n const handler = values[idx0] as EventListener;\n el.addEventListener(name.slice(1), handler);\n disposers.push(() => el.removeEventListener(name.slice(1), handler));\n }\n return;\n }\n\n // Property binding: .value=${data} - sets DOM property directly\n if (name[0] === \".\") {\n if (idx0 != null) {\n const unsub = bind(values[idx0], (v) => {\n (el as unknown as Record<string, unknown>)[name.slice(1)] = v;\n });\n if (unsub) disposers.push(unsub);\n }\n return;\n }\n\n // Static attribute (no dynamic parts)\n if (!indexes.length) {\n const value = statics[0] ?? \"\";\n el.setAttribute(name, value);\n return;\n }\n\n // Wrap functions in scoped computed, collect all reactive sources\n const reactives: Reactive<unknown>[] = [];\n for (const idx of indexes) {\n const v = values[idx];\n if (typeof v === \"function\") {\n const [c, dispose] = scopedComputed(v as () => unknown);\n values[idx] = c;\n reactives.push(c);\n disposers.push(dispose);\n } else if (isSignal(v)) {\n reactives.push(v);\n }\n }\n\n const update = () => {\n let result = statics[0]!;\n for (let i = 0; i < indexes.length; i++) {\n const v = values[indexes[i]!];\n const val = isSignal(v) ? (v as Reactive<unknown>).value : v;\n // Single dynamic attr: handle boolean/null specially\n if (indexes.length === 1 && (val == null || val === false)) {\n el.removeAttribute(name);\n return;\n }\n result += (val === true ? \"\" : (val ?? \"\")) + statics[i + 1]!;\n }\n el.setAttribute(name, result);\n };\n\n update();\n for (const s of reactives) disposers.push(s.subscribe(update));\n };\n\n /**\n * Bind dynamic content at a marker position.\n * Tries plugins first (first match wins), then falls back to default handling.\n */\n const bindContent = (marker: Comment, value: unknown): (() => void) => {\n // Try plugins first (first match wins)\n for (const plugin of plugins) {\n const binder = plugin(value);\n if (binder) {\n const pluginDisposers: (() => void)[] = [];\n binder(marker, pluginDisposers);\n return () => pluginDisposers.forEach((d) => d());\n }\n }\n\n // Default handling\n let nodes: Node[] = [];\n let childDisposers: (() => void)[] = [];\n\n const clear = () => {\n childDisposers.forEach((d) => d());\n childDisposers = [];\n nodes.forEach((n) => (n as ChildNode).remove());\n nodes = [];\n };\n\n const update = (v: unknown) => {\n clear();\n renderContent(marker, v, nodes, childDisposers);\n };\n\n const unsub = bind(value, update);\n return () => {\n unsub?.();\n clear();\n };\n };\n\n parser.parseTemplate(this.strings, {\n onText: (text) => {\n stack.at(-1)!.append(text);\n },\n\n onOpenTag: (tag, attrs, selfClosing) => {\n const parent = stack.at(-1)!;\n const isSvg =\n tag === \"svg\" ||\n tag === \"SVG\" ||\n (parent instanceof Element && parent.namespaceURI === SVG_NS);\n const el = isSvg\n ? document.createElementNS(SVG_NS, tag)\n : document.createElement(tag);\n for (const attr of attrs) handleAttribute(el, attr);\n parent.appendChild(el);\n if (!selfClosing) stack.push(el);\n },\n\n onClose: () => {\n if (stack.length > 1) stack.pop();\n },\n\n onSlot: (index) => {\n // Marker comment anchors dynamic content\n const marker = document.createComment(\"\");\n stack.at(-1)!.appendChild(marker);\n disposers.push(bindContent(marker, values[index]));\n },\n });\n\n return { fragment, dispose: () => disposers.forEach((d) => d()) };\n }\n}\n\n/**\n * Render content and insert nodes before marker.\n * Handles Templates, primitives, arrays.\n */\nfunction renderContent(\n marker: Comment,\n v: unknown,\n nodes: Node[],\n childDisposers: (() => void)[],\n): void {\n const parent = marker.parentNode!;\n\n for (const item of Array.isArray(v) ? v : [v]) {\n // Handle templates\n if (item instanceof Template) {\n const { fragment, dispose } = item.render();\n childDisposers.push(dispose);\n nodes.push(...fragment.childNodes);\n parent.insertBefore(fragment, marker);\n }\n // Handle primitives (ignore null/undefined/boolean)\n else if (item != null && typeof item !== \"boolean\") {\n const node = document.createTextNode(String(item));\n nodes.push(node);\n parent.insertBefore(node, marker);\n }\n }\n}\n\n/**\n * Create an html tag function with the given plugins.\n */\nfunction createHtmlWithPlugins(plugins: InterpolationPlugin[]): HtmlTag {\n const tag = ((strings: TemplateStringsArray, ...values: unknown[]) =>\n new Template(strings, values, plugins)) as HtmlTag;\n\n tag.with = (...morePlugins: InterpolationPlugin[]) =>\n createHtmlWithPlugins([...plugins, ...morePlugins]);\n\n return tag;\n}\n\n/**\n * Tagged template literal for creating reactive HTML templates.\n * Use .with(...plugins) to add interpolation handlers like each() or async generators.\n *\n * @example\n * ```ts\n * import { html } from \"balises\";\n * import eachPlugin, { each } from \"balises/each\";\n * import asyncPlugin from \"balises/async\";\n *\n * const html = baseHtml.with(eachPlugin, asyncPlugin);\n *\n * html`<div>${async function* () { ... }}</div>`.render();\n * ```\n */\nexport const html: HtmlTag = createHtmlWithPlugins([]);\n"],"mappings":"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"}
1
+ {"version":3,"file":"balises.esm.js","names":["context: Computed<unknown> | null","batchQueue: Set<Subscriber> | null","disposalStack: Array<Array<() => void>> | null","disposers: Array<() => void>","onTrack: {\n current: ((source: Signal<unknown> | Computed<unknown>) => void) | null;\n}","#value","#targets","#subs","#signal","signal","#fn","#recompute","#dirty","#value","#subs","#sources","#sourceIndex","queue: Computed<unknown>[]","toNotify: Array<{ c: Computed<unknown>; old: unknown }>","#targets","#computing","cleanup: (() => void) | undefined","disposeScope: (() => void) | undefined","dispose: (() => void) | undefined","strings: TemplateStringsArray","values: unknown[]","plugins: InterpolationPlugin[]","disposers: (() => void)[]","stack: (Element | DocumentFragment)[]","reactives: Reactive<unknown>[]","pluginDisposers: (() => void)[]","nodes: Node[]","childDisposers: (() => void)[]","html: HtmlTag"],"sources":["../src/signals/context.ts","../src/signals/signal.ts","../src/signals/computed.ts","../src/signals/effect.ts","../src/signals/store.ts","../src/signals/index.ts","../src/parser.ts","../src/template.ts"],"sourcesContent":["/**\n * Global state and batching for the reactive system.\n */\n\nimport type { Computed } from \"./computed.js\";\nimport type { Signal } from \"./signal.js\";\n\n/** Callback function for subscribers */\nexport type Subscriber = () => void;\n\n/** The currently executing computed (for dependency tracking) */\nexport let context: Computed<unknown> | null = null;\n\n/** Set the current execution context */\nexport function setContext(c: Computed<unknown> | null): void {\n context = c;\n}\n\n/** Batching: defer subscriber notifications until batch completes */\nlet batchDepth = 0;\nlet batchQueue: Set<Subscriber> | null = null;\n\n/**\n * Batch multiple signal updates into a single notification pass.\n * Subscribers are only notified after the batch function completes.\n */\nexport function batch<T>(fn: () => T): T {\n batchDepth++;\n if (batchDepth === 1) batchQueue = new Set();\n try {\n return fn();\n } finally {\n if (--batchDepth === 0) {\n const q = batchQueue!;\n batchQueue = null;\n for (const sub of q) sub();\n }\n }\n}\n\n/** Check if currently batching */\nexport function isBatching(): boolean {\n return batchDepth > 0;\n}\n\n/** Add a subscriber to the batch queue */\nexport function enqueueBatchOne(sub: Subscriber): void {\n batchQueue!.add(sub);\n}\n\n/** Add multiple subscribers to the batch queue */\nexport function enqueueBatchAll(subs: Subscriber[]): void {\n for (let i = 0; i < subs.length; i++) batchQueue!.add(subs[i]!);\n}\n\n/** Scope disposal: collect all disposers in a scope */\nlet disposalStack: Array<Array<() => void>> | null = null;\n\n/**\n * Create a disposal scope that collects all subscriptions and computeds created within.\n * Returns the result of the function and a dispose function that cleans up all resources.\n *\n * @example\n * ```ts\n * const [result, dispose] = scope(() => {\n * const count = signal(0);\n * const doubled = computed(() => count.value * 2);\n * effect(() => console.log(doubled.value));\n * return { count, doubled };\n * });\n *\n * // Later: clean up all subscriptions and computeds\n * dispose();\n * ```\n */\nexport function scope<T>(fn: () => T): [result: T, dispose: () => void] {\n const disposers: Array<() => void> = [];\n\n // Push new disposal context\n if (!disposalStack) disposalStack = [];\n disposalStack.push(disposers);\n\n try {\n const result = fn();\n return [\n result,\n () => {\n for (let i = disposers.length - 1; i >= 0; i--) {\n disposers[i]!();\n }\n disposers.length = 0;\n },\n ];\n } finally {\n // Pop disposal context\n disposalStack.pop();\n if (!disposalStack.length) disposalStack = null;\n }\n}\n\n/**\n * Register a disposer in the current scope (if any).\n * This is called internally by computed/effect when they create cleanup functions.\n */\nexport function registerDisposer(dispose: () => void): void {\n disposalStack?.at(-1)?.push(dispose);\n}\n\n/**\n * Hook for tracking signal/computed accesses.\n * Set by async.ts when track() is active to capture dependencies.\n * Using an object wrapper so async.ts can mutate the current value.\n */\nexport const onTrack: {\n current: ((source: Signal<unknown> | Computed<unknown>) => void) | null;\n} = { current: null };\n","/**\n * Signal - A reactive value container.\n */\n\nimport type { Computed } from \"./computed.js\";\nimport {\n context,\n isBatching,\n enqueueBatchAll,\n onTrack,\n type Subscriber,\n} from \"./context.js\";\n\n/**\n * Remove an item from an array using swap-and-pop (O(1) removal).\n */\nexport function removeFromArray<T>(array: T[], item: T): void {\n const i = array.indexOf(item);\n if (i >= 0) {\n array[i] = array[array.length - 1]!;\n array.pop();\n }\n}\n\n/**\n * A reactive value container. When the value changes, all dependent\n * computeds are marked dirty and subscribers are notified.\n *\n * Uses Object.is() for equality checks to correctly handle NaN values.\n */\nexport class Signal<T> {\n #value: T;\n #subs: Subscriber[] = [];\n #targets: Computed<unknown>[] = [];\n\n constructor(value: T) {\n this.#value = value;\n }\n\n get value(): T {\n if (context) context.trackSource(this);\n if (onTrack.current) onTrack.current(this);\n return this.#value;\n }\n\n set value(v: T) {\n if (Object.is(this.#value, v)) return;\n this.#value = v;\n\n // Mark all dependent computeds as dirty\n const targets = this.#targets;\n for (let i = 0; i < targets.length; i++) {\n targets[i]!.markDirty();\n }\n\n // Notify subscribers\n if (this.#subs.length) {\n if (isBatching()) {\n enqueueBatchAll(this.#subs);\n } else {\n // Copy array to avoid issues if subscribers modify the array during iteration\n const subs = [...this.#subs];\n for (let i = 0; i < subs.length; i++) subs[i]!();\n }\n }\n }\n\n subscribe(fn: Subscriber): () => void {\n this.#subs.push(fn);\n return () => removeFromArray(this.#subs, fn);\n }\n\n /**\n * Update the signal value using an updater function.\n *\n * @param fn - Function that receives current value and returns new value\n *\n * @example\n * const count = signal(0);\n * count.update(n => n + 1); // increment\n */\n update(fn: (current: T) => T): void {\n this.value = fn(this.#value);\n }\n\n /**\n * Read the signal value without tracking dependencies.\n * Useful in event handlers where you want the current value\n * but don't want to create a reactive dependency.\n *\n * @example\n * const count = signal(0);\n * // In an event handler - no dependency tracking\n * button.onclick = () => console.log(count.peek());\n */\n peek(): T {\n return this.#value;\n }\n\n /** @internal */\n get targets(): Computed<unknown>[] {\n return this.#targets;\n }\n\n /** @internal */\n deleteTarget(target: Computed<unknown>): void {\n removeFromArray(this.#targets, target);\n }\n}\n\n/** Create a new signal with the given initial value. */\nexport const signal = <T>(value: T) => new Signal(value);\n\n/**\n * A read-only view of a Signal.\n * Provides reactive access without allowing external mutation.\n * Used by `each()` to pass item signals to render functions.\n */\nexport class ReadonlySignal<T> {\n #signal: Signal<T>;\n\n /** @internal */\n constructor(signal: Signal<T>) {\n this.#signal = signal;\n }\n\n get value(): T {\n return this.#signal.value;\n }\n\n /**\n * Read the signal value without tracking dependencies.\n * Useful in event handlers where you want the current value\n * but don't want to create a reactive dependency.\n */\n peek(): T {\n return this.#signal.peek();\n }\n\n subscribe(fn: Subscriber): () => void {\n return this.#signal.subscribe(fn);\n }\n}\n","/**\n * Computed - A derived reactive value.\n */\n\nimport { Signal, removeFromArray } from \"./signal.js\";\nimport {\n context,\n setContext,\n isBatching,\n enqueueBatchOne,\n registerDisposer,\n onTrack,\n type Subscriber,\n} from \"./context.js\";\n\n/**\n * A derived reactive value. Automatically tracks dependencies and\n * recomputes when any dependency changes.\n *\n * Uses Object.is() for equality checks to correctly handle NaN values.\n */\nexport class Computed<T> {\n #fn: (() => T) | undefined;\n #value: T | undefined;\n #dirty = true;\n #computing = false;\n #subs: Subscriber[] = [];\n #targets: Computed<unknown>[] = [];\n #sources: (Signal<unknown> | Computed<unknown>)[] = [];\n #sourceIndex = 0;\n\n constructor(fn: () => T) {\n this.#fn = fn;\n this.#recompute();\n\n // Auto-register disposal in current root scope\n registerDisposer(() => this.dispose());\n }\n\n get value(): T {\n if (this.#dirty) this.#recompute();\n if (context && context !== this) context.trackSource(this);\n if (onTrack.current) onTrack.current(this);\n return this.#value as T;\n }\n\n subscribe(fn: Subscriber): () => void {\n this.#subs.push(fn);\n return () => removeFromArray(this.#subs, fn);\n }\n\n dispose(): void {\n this.#fn = undefined;\n const sources = this.#sources;\n for (let i = 0; i < sources.length; i++) {\n const source = sources[i];\n if (source) source.deleteTarget(this);\n }\n this.#sources = [];\n this.#subs.length = 0;\n }\n\n /**\n * Called by sources when accessed during recompute.\n * @internal\n */\n trackSource(source: Signal<unknown> | Computed<unknown>): void {\n // Skip tracking if disposed (can happen if dispose() is called during #fn execution)\n if (!this.#fn) return;\n\n const sources = this.#sources;\n const idx = this.#sourceIndex++;\n\n if (idx < sources.length) {\n if (sources[idx] === source) {\n // Same source at same position - nothing to do\n return;\n }\n // Different source - unlink old ones from this position\n for (let i = idx; i < sources.length; i++) {\n const s = sources[i];\n if (s) s.deleteTarget(this);\n }\n sources.length = idx;\n }\n // Add new source\n sources.push(source);\n source.targets.push(this);\n }\n\n /**\n * Mark this computed and all its dependents as dirty.\n * Uses a two-phase approach to avoid cascading issues:\n * 1. Mark all dependents as dirty (no subscriber calls)\n * 2. Notify subscribers after all dirty flags are set\n * @internal\n */\n markDirty(): void {\n if (this.#dirty) return;\n\n // Phase 1: Mark all dependents as dirty, collect subscribers\n const queue: Computed<unknown>[] = [this];\n const toNotify: Array<{ c: Computed<unknown>; old: unknown }> = [];\n\n for (let i = 0; i < queue.length; i++) {\n const c = queue[i]!;\n if (c.#dirty) continue;\n c.#dirty = true;\n\n // Propagate to all targets\n const targets = c.#targets;\n for (let j = 0; j < targets.length; j++) {\n const t = targets[j]!;\n if (!t.#dirty) queue.push(t);\n }\n\n // Collect computeds with subscribers for later notification\n if (c.#subs.length && c.#fn) {\n toNotify.push({ c, old: c.#value });\n }\n }\n\n // Phase 2: Notify subscribers (after all dirty flags are set)\n for (let i = 0; i < toNotify.length; i++) {\n const { c, old } = toNotify[i]!;\n const notify = () => {\n if (c.#fn) {\n c.#recompute();\n if (!Object.is(c.#value, old)) {\n const subs = c.#subs;\n for (let j = 0; j < subs.length; j++) subs[j]!();\n }\n }\n };\n void (isBatching() ? enqueueBatchOne(notify) : notify());\n }\n }\n\n /** @internal */\n get targets(): Computed<unknown>[] {\n return this.#targets;\n }\n\n /** @internal */\n deleteTarget(target: Computed<unknown>): void {\n removeFromArray(this.#targets, target);\n }\n\n #recompute(): void {\n if (this.#computing || !this.#fn) return;\n this.#computing = true;\n\n this.#sourceIndex = 0;\n const prevLen = this.#sources.length;\n\n const prev = context;\n setContext(this);\n try {\n this.#value = this.#fn();\n } finally {\n setContext(prev);\n\n // Unlink removed sources\n const newLen = this.#sourceIndex;\n if (newLen < prevLen) {\n const sources = this.#sources;\n for (let i = newLen; i < prevLen; i++) {\n const source = sources[i];\n if (source) {\n source.deleteTarget(this);\n }\n }\n sources.length = newLen;\n }\n\n this.#dirty = false;\n this.#computing = false;\n }\n }\n}\n\n/** Create a new computed from the given function. */\nexport const computed = <T>(fn: () => T) => new Computed(fn);\n","/**\n * Effect - Run side effects reactively.\n */\n\nimport { computed } from \"./computed.js\";\nimport { context, setContext, registerDisposer } from \"./context.js\";\n\n/**\n * Create a reactive effect that automatically tracks dependencies\n * and re-runs when they change.\n *\n * The effect function can optionally return a cleanup function that will be\n * called before the effect re-runs and when the effect is disposed.\n *\n * @param fn - The effect function to run. May return a cleanup function.\n * @returns A dispose function to stop the effect\n *\n * @example\n * const count = signal(0);\n * const dispose = effect(() => {\n * console.log(\"Count is:\", count.value);\n * });\n *\n * count.value = 1; // logs: \"Count is: 1\"\n * dispose(); // stop the effect\n *\n * @example\n * // With cleanup function\n * const userId = signal(1);\n * const dispose = effect(() => {\n * const subscription = api.subscribe(userId.value);\n * return () => subscription.unsubscribe(); // cleanup\n * });\n *\n * userId.value = 2; // cleanup runs, then effect re-runs with new subscription\n * dispose(); // final cleanup runs\n */\nexport function effect(fn: () => void | (() => void)): () => void {\n let cleanup: (() => void) | undefined;\n let disposed = false;\n\n const c = computed(() => {\n // Run cleanup outside of tracking context to avoid\n // reactive reads in cleanup creating new dependencies\n if (cleanup) {\n const prev = context;\n setContext(null);\n try {\n cleanup();\n } finally {\n setContext(prev);\n }\n }\n cleanup = fn() ?? undefined;\n return undefined;\n });\n // Subscribe to make it reactive (rerun on dependency changes)\n const unsub = c.subscribe(() => {});\n\n const dispose = () => {\n if (disposed) return;\n disposed = true;\n unsub();\n c.dispose();\n cleanup?.();\n };\n\n // Register full effect dispose (with cleanup) in current scope\n // This overrides the computed's auto-registration with a more complete cleanup\n registerDisposer(dispose);\n\n return dispose;\n}\n","/**\n * Store - Reactive wrapper for plain objects.\n */\n\nimport { Signal } from \"./signal.js\";\n\nconst STORE = Symbol();\n\n/**\n * Create a reactive store from a plain object.\n * Each property becomes a signal, and nested objects are recursively wrapped.\n */\nexport function store<T extends object>(obj: T): T {\n const signals = new Map<string | symbol, Signal<unknown>>();\n\n /** Recursively wrap nested objects and arrays */\n const wrap = (value: unknown): unknown => {\n if (typeof value !== \"object\" || value === null) return value;\n if (STORE in value) return value; // Already a store\n if ((value as object).constructor === Object) {\n return store(value as Record<string, unknown>);\n }\n if (Array.isArray(value)) {\n return value.map(wrap);\n }\n return value;\n };\n\n /** Get or create a signal for a property */\n const getSignal = (key: string | symbol, initialValue: unknown) => {\n let sig = signals.get(key);\n if (!sig) {\n sig = new Signal(wrap(initialValue));\n signals.set(key, sig);\n }\n return sig;\n };\n\n return new Proxy(obj, {\n get(target, key) {\n // Allow symbol access (for STORE check and other internal symbols)\n if (typeof key === \"symbol\") {\n return target[key as keyof T];\n }\n return getSignal(key, target[key as keyof T]).value;\n },\n\n set(target, key, value) {\n if (typeof key === \"symbol\") {\n target[key as keyof T] = value;\n return true;\n }\n const wrapped = wrap(value);\n getSignal(key, target[key as keyof T]).value = wrapped;\n target[key as keyof T] = wrapped as T[keyof T];\n return true;\n },\n\n has(target, key) {\n return key === STORE || key in target;\n },\n });\n}\n","/**\n * Reactive signals with automatic dependency tracking.\n *\n * Uses index-based tracking: computed functions are assumed to access\n * their dependencies in the same order on each run. This enables O(1)\n * dependency checks without complex linked-list structures.\n */\n\nexport { Signal, signal, ReadonlySignal } from \"./signal.js\";\nexport { Computed, computed } from \"./computed.js\";\nexport { effect } from \"./effect.js\";\nexport { store } from \"./store.js\";\nexport { batch, scope, type Subscriber } from \"./context.js\";\n\nimport { Signal, ReadonlySignal } from \"./signal.js\";\nimport { Computed } from \"./computed.js\";\n\n/** Common interface for reactive values (Signal or Computed). */\nexport interface Reactive<T> {\n readonly value: T;\n subscribe(fn: () => void): () => void;\n}\n\n/** Check if a value is a reactive signal or computed. @internal */\nexport const isSignal = (value: unknown): value is Reactive<unknown> =>\n value instanceof Signal ||\n value instanceof Computed ||\n value instanceof ReadonlySignal;\n","/**\n * Streaming HTML parser for template literals.\n * State machine: Text=0, TagOpen=1, TagName=2, InTag=3, AttrName=4, AttrEq=5, AttrVal=6, CloseTag=7, Comment=8\n */\n\n/** Pre-compiled attribute: static strings interleaved with slot indexes */\nexport type Attr = [name: string, statics: string[], indexes: number[]];\n\nexport interface ParseCallbacks {\n onText: (text: string) => void;\n onOpenTag: (tag: string, attrs: Attr[], selfClosing: boolean) => void;\n onClose: () => void;\n onSlot: (index: number) => void;\n}\n\nexport class HTMLParser {\n private s = 0; // state\n private tag = \"\";\n private attr = \"\";\n private statics: string[] = [];\n private indexes: number[] = [];\n private attrs: Attr[] = [];\n private text = \"\";\n private q = \"\";\n\n parseTemplate(strings: TemplateStringsArray, cb: ParseCallbacks) {\n for (let i = 0; i < strings.length; i++) {\n this.parse(strings[i]!, cb);\n if (i < strings.length - 1) this.slot(i, cb);\n }\n if (this.text) {\n cb.onText(this.text);\n this.text = \"\";\n }\n }\n\n private parse(str: string, cb: ParseCallbacks) {\n for (let i = 0; i < str.length; i++) {\n const ch = str[i]!,\n nx = str[i + 1],\n n2 = str[i + 2];\n\n if (this.s === 0) {\n // Text\n if (ch === \"<\") {\n if (this.text) {\n cb.onText(this.text);\n this.text = \"\";\n }\n this.s = 1;\n } else this.text += ch;\n } else if (this.s === 1) {\n // TagOpen\n if (ch === \"/\") this.s = 7;\n else if (ch === \"!\" && nx === \"-\" && n2 === \"-\") {\n i += 2;\n this.s = 8;\n } else if (ch === \"!\" || ch === \"?\") {\n while (i < str.length && str[i] !== \">\") i++;\n this.s = 0;\n } else if (this.isA(ch)) {\n this.tag = ch;\n this.s = 2;\n } else {\n this.text += \"<\" + ch;\n this.s = 0;\n }\n } else if (this.s === 2) {\n // TagName\n if (this.isT(ch)) this.tag += ch;\n else if (this.isW(ch)) {\n this.s = 3;\n } else if (ch === \">\") {\n this.emit(cb, false);\n } else if (ch === \"/\" && nx === \">\") {\n i++;\n this.emit(cb, true);\n }\n } else if (this.s === 3) {\n // InTag\n if (this.isW(ch)) continue;\n if (ch === \">\") this.emit(cb, false);\n else if (ch === \"/\" && nx === \">\") {\n i++;\n this.emit(cb, true);\n } else {\n this.attr = ch;\n this.statics = [\"\"];\n this.indexes = [];\n this.s = 4;\n }\n } else if (this.s === 4) {\n // AttrName\n if (this.isT(ch) || ch === \"_\") this.attr += ch;\n else if (ch === \"=\") this.s = 5;\n else if (this.isW(ch)) {\n this.emitAttr();\n this.s = 3;\n } else if (ch === \">\") {\n this.emitAttr();\n this.emit(cb, false);\n } else if (ch === \"/\" && nx === \">\") {\n this.emitAttr();\n i++;\n this.emit(cb, true);\n }\n } else if (this.s === 5) {\n // AttrEq\n if (ch === '\"' || ch === \"'\") {\n this.q = ch;\n this.s = 6;\n } else if (!this.isW(ch)) {\n this.q = \"\";\n this.statics[0] += ch;\n this.s = 6;\n }\n } else if (this.s === 6) {\n // AttrVal\n const end = this.q\n ? ch === this.q\n : this.isW(ch) || ch === \">\" || ch === \"/\";\n if (end) {\n this.emitAttr();\n this.q = \"\";\n this.s = 3;\n if (ch === \">\") this.emit(cb, false);\n else if (ch === \"/\" && nx === \">\") {\n i++;\n this.emit(cb, true);\n }\n } else {\n this.statics[this.statics.length - 1] += ch;\n }\n } else if (this.s === 7) {\n // CloseTag\n if (ch === \">\") {\n cb.onClose();\n this.s = 0;\n }\n } else if (this.s === 8) {\n // Comment\n if (ch === \"-\" && nx === \"-\" && n2 === \">\") {\n i += 2;\n this.s = 0;\n }\n }\n }\n }\n\n private slot(index: number, cb: ParseCallbacks) {\n if (this.s === 5 || this.s === 6) {\n this.indexes.push(index);\n this.statics.push(\"\");\n if (this.s === 5) this.s = 6;\n } else {\n if (this.text) {\n cb.onText(this.text);\n this.text = \"\";\n }\n cb.onSlot(index);\n }\n }\n\n private emit(cb: ParseCallbacks, self: boolean) {\n cb.onOpenTag(this.tag, this.attrs, self);\n this.tag = \"\";\n this.attrs = [];\n this.s = 0;\n }\n\n private emitAttr() {\n if (this.attr) this.attrs.push([this.attr, this.statics, this.indexes]);\n this.attr = \"\";\n this.statics = [];\n this.indexes = [];\n }\n\n private isA(c: string) {\n return (c >= \"a\" && c <= \"z\") || (c >= \"A\" && c <= \"Z\");\n }\n private isT(c: string) {\n return (\n (c >= \"a\" && c <= \"z\") ||\n (c >= \"A\" && c <= \"Z\") ||\n (c >= \"0\" && c <= \"9\") ||\n c === \"-\" ||\n c === \":\"\n );\n }\n private isW(c: string) {\n return c <= \" \" && c !== \"\";\n }\n}\n","/**\n * HTML template rendering with reactive bindings.\n *\n * Uses tagged template literals to create reactive DOM:\n * - Text interpolation: ${value} or ${signal}\n * - Attribute binding: class=\"${signal}\" (reactive)\n * - Event binding: @click=${handler}\n * - Property binding: .value=${signal} (sets DOM property, not attribute)\n * - Nested templates: ${html`<span>...</span>`}\n * - Arrays: ${items.map(i => html`<li>${i}</li>`)}\n *\n * Extend with plugins via html.with(...plugins) for additional interpolation types.\n */\n\nimport { computed, isSignal, scope, type Reactive } from \"./signals/index.js\";\nimport { HTMLParser, type Attr } from \"./parser.js\";\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\";\n\n/**\n * Plugin that handles custom interpolation types.\n * Return a bind function if this plugin handles the value, null otherwise.\n * First plugin to return non-null wins.\n */\nexport interface InterpolationPlugin {\n (\n value: unknown,\n ): ((marker: Comment, disposers: (() => void)[]) => void) | null;\n}\n\n/**\n * Html template tag function with plugin composition.\n */\nexport interface HtmlTag {\n (strings: TemplateStringsArray, ...values: unknown[]): Template;\n /** Create a new html tag with additional plugins */\n with(...plugins: InterpolationPlugin[]): HtmlTag;\n}\n\n/**\n * Create a computed that wraps function execution in a scope.\n * Nested computeds/effects are automatically disposed on re-run.\n * Returns [computed, dispose] - dispose cleans up both the computed\n * and any nested reactives from the last run.\n */\nfunction scopedComputed<T>(fn: () => T) {\n let disposeScope: (() => void) | undefined;\n\n const c = computed(() => {\n disposeScope?.();\n const [result, dispose] = scope(fn);\n disposeScope = dispose;\n return result;\n });\n\n return [c, () => (c.dispose(), disposeScope?.())] as const;\n}\n\n/**\n * Bind a value to an update function.\n * If reactive, subscribes and returns unsubscribe. Otherwise returns null.\n * Functions are wrapped in computed() for automatic reactivity.\n * Nested computeds/effects created inside functions are automatically\n * disposed when the function re-runs or the binding is disposed.\n */\nfunction bind(\n value: unknown,\n update: (v: unknown) => void,\n): (() => void) | null | undefined {\n let dispose: (() => void) | undefined;\n if (typeof value === \"function\") {\n [value, dispose] = scopedComputed(value as () => unknown);\n }\n if (isSignal(value)) {\n update(value.value);\n const unsub = value.subscribe(() =>\n update((value as Reactive<unknown>).value),\n );\n return dispose ? () => (unsub(), dispose()) : unsub;\n }\n update(value);\n}\n\n/** Result of rendering a template */\nexport interface RenderResult {\n fragment: DocumentFragment;\n dispose: () => void;\n}\n\n/** A parsed HTML template. Call render() to create live DOM. */\nexport class Template {\n constructor(\n private strings: TemplateStringsArray,\n private values: unknown[],\n private plugins: InterpolationPlugin[] = [],\n ) {}\n\n /**\n * Parse template and create live DOM.\n * Returns the fragment and a dispose function to clean up subscriptions.\n */\n render(): RenderResult {\n const fragment = document.createDocumentFragment();\n const disposers: (() => void)[] = [];\n const stack: (Element | DocumentFragment)[] = [fragment];\n const parser = new HTMLParser();\n const values = this.values;\n const plugins = this.plugins;\n\n const handleAttribute = (el: Element, [name, statics, indexes]: Attr) => {\n const idx0 = indexes[0];\n\n // Event binding: @click=${handler}\n if (name[0] === \"@\") {\n if (idx0 != null) {\n const handler = values[idx0] as EventListener;\n el.addEventListener(name.slice(1), handler);\n disposers.push(() => el.removeEventListener(name.slice(1), handler));\n }\n return;\n }\n\n // Property binding: .value=${data} - sets DOM property directly\n if (name[0] === \".\") {\n if (idx0 != null) {\n const unsub = bind(values[idx0], (v) => {\n (el as unknown as Record<string, unknown>)[name.slice(1)] = v;\n });\n if (unsub) disposers.push(unsub);\n }\n return;\n }\n\n // Static attribute (no dynamic parts)\n if (!indexes.length) {\n const value = statics[0] ?? \"\";\n el.setAttribute(name, value);\n return;\n }\n\n // Wrap functions in scoped computed, collect all reactive sources\n const reactives: Reactive<unknown>[] = [];\n for (const idx of indexes) {\n const v = values[idx];\n if (typeof v === \"function\") {\n const [c, dispose] = scopedComputed(v as () => unknown);\n values[idx] = c;\n reactives.push(c);\n disposers.push(dispose);\n } else if (isSignal(v)) {\n reactives.push(v);\n }\n }\n\n const update = () => {\n let result = statics[0]!;\n for (let i = 0; i < indexes.length; i++) {\n const v = values[indexes[i]!];\n const val = isSignal(v) ? (v as Reactive<unknown>).value : v;\n // Single dynamic attr: handle boolean/null specially\n if (indexes.length === 1 && (val == null || val === false)) {\n el.removeAttribute(name);\n return;\n }\n result += (val === true ? \"\" : (val ?? \"\")) + statics[i + 1]!;\n }\n el.setAttribute(name, result);\n };\n\n update();\n for (const s of reactives) disposers.push(s.subscribe(update));\n };\n\n /**\n * Bind dynamic content at a marker position.\n * Tries plugins first (first match wins), then falls back to default handling.\n */\n const bindContent = (marker: Comment, value: unknown): (() => void) => {\n // Try plugins first (first match wins)\n for (const plugin of plugins) {\n const binder = plugin(value);\n if (binder) {\n const pluginDisposers: (() => void)[] = [];\n binder(marker, pluginDisposers);\n return () => pluginDisposers.forEach((d) => d());\n }\n }\n\n // Default handling\n let nodes: Node[] = [];\n let childDisposers: (() => void)[] = [];\n\n const clear = () => {\n childDisposers.forEach((d) => d());\n childDisposers = [];\n nodes.forEach((n) => (n as ChildNode).remove());\n nodes = [];\n };\n\n const update = (v: unknown) => {\n clear();\n renderContent(marker, v, nodes, childDisposers);\n };\n\n const unsub = bind(value, update);\n return () => {\n unsub?.();\n clear();\n };\n };\n\n parser.parseTemplate(this.strings, {\n onText: (text) => {\n stack.at(-1)!.append(text);\n },\n\n onOpenTag: (tag, attrs, selfClosing) => {\n const parent = stack.at(-1)!;\n const isSvg =\n tag === \"svg\" ||\n tag === \"SVG\" ||\n (parent instanceof Element && parent.namespaceURI === SVG_NS);\n const el = isSvg\n ? document.createElementNS(SVG_NS, tag)\n : document.createElement(tag);\n for (const attr of attrs) handleAttribute(el, attr);\n parent.appendChild(el);\n if (!selfClosing) stack.push(el);\n },\n\n onClose: () => {\n if (stack.length > 1) stack.pop();\n },\n\n onSlot: (index) => {\n // Marker comment anchors dynamic content\n const marker = document.createComment(\"\");\n stack.at(-1)!.appendChild(marker);\n disposers.push(bindContent(marker, values[index]));\n },\n });\n\n return { fragment, dispose: () => disposers.forEach((d) => d()) };\n }\n}\n\n/**\n * Render content and insert nodes before marker.\n * Handles Templates, primitives, arrays.\n */\nfunction renderContent(\n marker: Comment,\n v: unknown,\n nodes: Node[],\n childDisposers: (() => void)[],\n): void {\n const parent = marker.parentNode!;\n\n for (const item of Array.isArray(v) ? v : [v]) {\n // Handle templates\n if (item instanceof Template) {\n const { fragment, dispose } = item.render();\n childDisposers.push(dispose);\n nodes.push(...fragment.childNodes);\n parent.insertBefore(fragment, marker);\n }\n // Handle primitives (ignore null/undefined/boolean)\n else if (item != null && typeof item !== \"boolean\") {\n const node = document.createTextNode(String(item));\n nodes.push(node);\n parent.insertBefore(node, marker);\n }\n }\n}\n\n/**\n * Create an html tag function with the given plugins.\n */\nfunction createHtmlWithPlugins(plugins: InterpolationPlugin[]): HtmlTag {\n const tag = ((strings: TemplateStringsArray, ...values: unknown[]) =>\n new Template(strings, values, plugins)) as HtmlTag;\n\n tag.with = (...morePlugins: InterpolationPlugin[]) =>\n createHtmlWithPlugins([...plugins, ...morePlugins]);\n\n return tag;\n}\n\n/**\n * Tagged template literal for creating reactive HTML templates.\n * Use .with(...plugins) to add interpolation handlers like each() or async generators.\n *\n * @example\n * ```ts\n * import { html } from \"balises\";\n * import eachPlugin, { each } from \"balises/each\";\n * import asyncPlugin from \"balises/async\";\n *\n * const html = baseHtml.with(eachPlugin, asyncPlugin);\n *\n * html`<div>${async function* () { ... }}</div>`.render();\n * ```\n */\nexport const html: HtmlTag = createHtmlWithPlugins([]);\n"],"mappings":"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"}
@@ -144,6 +144,19 @@ var Balises = (function(exports) {
144
144
  update(fn) {
145
145
  this.value = fn(this.#value);
146
146
  }
147
+ /**
148
+ * Read the signal value without tracking dependencies.
149
+ * Useful in event handlers where you want the current value
150
+ * but don't want to create a reactive dependency.
151
+ *
152
+ * @example
153
+ * const count = signal(0);
154
+ * // In an event handler - no dependency tracking
155
+ * button.onclick = () => console.log(count.peek());
156
+ */
157
+ peek() {
158
+ return this.#value;
159
+ }
147
160
  /** @internal */
148
161
  get targets() {
149
162
  return this.#targets;
@@ -155,6 +168,32 @@ var Balises = (function(exports) {
155
168
  };
156
169
  /** Create a new signal with the given initial value. */
157
170
  const signal = (value) => new Signal(value);
171
+ /**
172
+ * A read-only view of a Signal.
173
+ * Provides reactive access without allowing external mutation.
174
+ * Used by `each()` to pass item signals to render functions.
175
+ */
176
+ var ReadonlySignal = class {
177
+ #signal;
178
+ /** @internal */
179
+ constructor(signal$1) {
180
+ this.#signal = signal$1;
181
+ }
182
+ get value() {
183
+ return this.#signal.value;
184
+ }
185
+ /**
186
+ * Read the signal value without tracking dependencies.
187
+ * Useful in event handlers where you want the current value
188
+ * but don't want to create a reactive dependency.
189
+ */
190
+ peek() {
191
+ return this.#signal.peek();
192
+ }
193
+ subscribe(fn) {
194
+ return this.#signal.subscribe(fn);
195
+ }
196
+ };
158
197
 
159
198
  //#endregion
160
199
  //#region src/signals/computed.ts
@@ -410,7 +449,7 @@ var Balises = (function(exports) {
410
449
  //#endregion
411
450
  //#region src/signals/index.ts
412
451
  /** Check if a value is a reactive signal or computed. @internal */
413
- const isSignal = (value) => value instanceof Signal || value instanceof Computed;
452
+ const isSignal = (value) => value instanceof Signal || value instanceof Computed || value instanceof ReadonlySignal;
414
453
 
415
454
  //#endregion
416
455
  //#region src/parser.ts