@zeix/cause-effect 1.0.2 → 1.1.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.
Files changed (46) hide show
  1. package/{skills → .claude/skills}/cause-effect/references/api-facts.md +23 -7
  2. package/{skills → .claude/skills}/cause-effect/references/non-obvious-behaviors.md +4 -4
  3. package/{skills → .claude/skills}/cause-effect/references/signal-types.md +19 -7
  4. package/{skills → .claude/skills}/cause-effect-dev/references/api-facts.md +5 -3
  5. package/{skills → .claude/skills}/cause-effect-dev/references/non-obvious-behaviors.md +4 -4
  6. package/CHANGELOG.md +17 -0
  7. package/GUIDE.md +3 -3
  8. package/README.md +30 -4
  9. package/index.dev.js +8 -2
  10. package/index.js +1 -1
  11. package/index.ts +2 -1
  12. package/package.json +3 -3
  13. package/src/nodes/effect.ts +65 -8
  14. package/src/nodes/slot.ts +8 -4
  15. package/test/effect.test.ts +88 -0
  16. package/test/slot.test.ts +19 -0
  17. package/types/index.d.ts +2 -2
  18. package/types/src/nodes/effect.d.ts +36 -1
  19. package/types/src/nodes/slot.d.ts +7 -3
  20. /package/{skills → .claude/skills}/cause-effect/SKILL.md +0 -0
  21. /package/{skills → .claude/skills}/cause-effect/agents/openai.yaml +0 -0
  22. /package/{skills → .claude/skills}/cause-effect/references/error-classes.md +0 -0
  23. /package/{skills → .claude/skills}/cause-effect/workflows/answer-question.md +0 -0
  24. /package/{skills → .claude/skills}/cause-effect/workflows/debug.md +0 -0
  25. /package/{skills → .claude/skills}/cause-effect/workflows/use-api.md +0 -0
  26. /package/{skills → .claude/skills}/cause-effect-dev/SKILL.md +0 -0
  27. /package/{skills → .claude/skills}/cause-effect-dev/agents/openai.yaml +0 -0
  28. /package/{skills → .claude/skills}/cause-effect-dev/references/error-classes.md +0 -0
  29. /package/{skills → .claude/skills}/cause-effect-dev/references/internal-types.md +0 -0
  30. /package/{skills → .claude/skills}/cause-effect-dev/references/source-map.md +0 -0
  31. /package/{skills → .claude/skills}/cause-effect-dev/workflows/answer-question.md +0 -0
  32. /package/{skills → .claude/skills}/cause-effect-dev/workflows/fix-bug.md +0 -0
  33. /package/{skills → .claude/skills}/cause-effect-dev/workflows/implement-feature.md +0 -0
  34. /package/{skills → .claude/skills}/cause-effect-dev/workflows/write-tests.md +0 -0
  35. /package/{skills → .claude/skills}/changelog-keeper/SKILL.md +0 -0
  36. /package/{skills → .claude/skills}/changelog-keeper/agents/openai.yaml +0 -0
  37. /package/{skills → .claude/skills}/tech-writer/SKILL.md +0 -0
  38. /package/{skills → .claude/skills}/tech-writer/references/document-map.md +0 -0
  39. /package/{skills → .claude/skills}/tech-writer/references/tone-guide.md +0 -0
  40. /package/{skills → .claude/skills}/tech-writer/workflows/consistency-review.md +0 -0
  41. /package/{skills → .claude/skills}/tech-writer/workflows/update-after-change.md +0 -0
  42. /package/{skills → .claude/skills}/tech-writer/workflows/update-agent-docs.md +0 -0
  43. /package/{skills → .claude/skills}/tech-writer/workflows/update-architecture.md +0 -0
  44. /package/{skills → .claude/skills}/tech-writer/workflows/update-jsdoc.md +0 -0
  45. /package/{skills → .claude/skills}/tech-writer/workflows/update-public-api.md +0 -0
  46. /package/{skills → .claude/skills}/tech-writer/workflows/update-requirements.md +0 -0
@@ -139,22 +139,38 @@ const results = createTask(async (prev, signal) => {
139
139
  ```
140
140
 
141
141
  **`Slot` is a property descriptor**
142
- - Has `get`, `set`, `configurable`, `enumerable` fields
143
- - Can be passed directly to `Object.defineProperty()`:
142
+ - Has `get`, `set`, `configurable`, `enumerable` fields — pass directly to `Object.defineProperty()`
143
+ - Delegates reads and writes to a swappable backing signal; use `replace(nextSignal)` to swap
144
+ - Is a forwarding layer, not a value owner — has no `update()` method
144
145
 
145
146
  ```typescript
146
- const nameSlot = createSlot(store, 'name')
147
+ const nameState = createState('Alice')
148
+ const nameSlot = createSlot(nameState)
147
149
  Object.defineProperty(element, 'name', nameSlot)
148
150
  ```
149
151
  </callback_patterns>
150
152
 
151
153
  <match_helper>
152
- `match` reads one or more Sensor/Task signals and routes to `ok` or `nil` based on whether
153
- all signals have a value. Use it to safely handle the unset state without try/catch:
154
+ `match` reads one or more Sensor/Task signals and routes to `ok`, `nil`, or `err` based on
155
+ whether all signals have a value. Use it to safely handle the unset state without try/catch.
156
+
157
+ **Single-signal form** — `ok` receives the value directly, `err` receives a single `Error`:
154
158
 
155
159
  ```typescript
156
160
  import { match } from '@zeix/cause-effect'
157
161
 
162
+ createEffect(() => {
163
+ match(task, {
164
+ ok: data => render(data),
165
+ nil: () => showSpinner(),
166
+ err: error => showError(error),
167
+ })
168
+ })
169
+ ```
170
+
171
+ **Tuple form** — for two or more signals; `ok` receives a typed tuple, `err` an `Error[]`:
172
+
173
+ ```typescript
158
174
  createEffect(() => {
159
175
  match([task, sensor], {
160
176
  ok: ([taskResult, sensorValue]) => render(taskResult, sensorValue),
@@ -163,8 +179,8 @@ createEffect(() => {
163
179
  })
164
180
  ```
165
181
 
166
- Read signals you care about eagerly inside `match`'s array — not inside individual branches.
167
- See `non-obvious-behaviors.md → conditional-reads-delay-watched` for why.
182
+ Read all signals you care about eagerly in the signals argument — not inside individual
183
+ branches. See `non-obvious-behaviors.md → conditional-reads-delay-watched` for why.
168
184
  </match_helper>
169
185
 
170
186
  <lifecycle_summary>
@@ -70,8 +70,8 @@ fires on the first effect run:
70
70
  // Bad — `derived` is only read after `task` resolves to `ok`
71
71
  // `derived.watched` does not fire until the task has a value
72
72
  createEffect(() => {
73
- match([task], {
74
- ok: ([result]) => render(derived.get(), result),
73
+ match(task, {
74
+ ok: result => render(derived.get(), result),
75
75
  nil: () => showSpinner(),
76
76
  })
77
77
  })
@@ -179,8 +179,8 @@ createEffect(() => {
179
179
 
180
180
  // Correct — match handles the nil (unset) case explicitly
181
181
  createEffect(() => {
182
- match([tick], {
183
- ok: ([timestamp]) => console.log('tick:', timestamp),
182
+ match(tick, {
183
+ ok: timestamp => console.log('tick:', timestamp),
184
184
  nil: () => console.log('waiting for first tick…'),
185
185
  })
186
186
  })
@@ -115,12 +115,13 @@ const dispose = createScope(() => {
115
115
  - You want property access (`element.name`) to participate in the reactive graph
116
116
 
117
117
  **Key facts:**
118
- - Has `get`, `set`, `configurable`, and `enumerable` fields
119
- - Can be passed directly to `Object.defineProperty`
120
- - Backed by a `State` internally
118
+ - Has `get`, `set`, `configurable`, and `enumerable` fields — pass directly to `Object.defineProperty`
119
+ - Delegates to a swappable backing signal (any signal type); use `replace(nextSignal)` to swap
120
+ - Forwarding layer only — has no `update()` method
121
121
 
122
122
  ```typescript
123
- const nameSlot = createSlot(store, 'name')
123
+ const nameState = createState('Alice')
124
+ const nameSlot = createSlot(nameState)
124
125
  Object.defineProperty(element, 'name', nameSlot)
125
126
  ```
126
127
  </Slot>
@@ -241,9 +242,20 @@ Sensor and Task start unset. Use `match` to handle all states in one expression:
241
242
 
242
243
  ```typescript
243
244
  createEffect(() => {
244
- match([task], {
245
- ok: ([data]) => renderData(data),
246
- err: ([error]) => renderError(error),
245
+ match(task, {
246
+ ok: data => renderData(data),
247
+ err: error => renderError(error),
248
+ nil: () => renderSpinner(),
249
+ })
250
+ })
251
+ ```
252
+
253
+ For two or more signals, use the tuple form — `ok` receives a typed tuple:
254
+
255
+ ```typescript
256
+ createEffect(() => {
257
+ match([task, sensor], {
258
+ ok: ([data, pos]) => render(data, pos),
247
259
  nil: () => renderSpinner(),
248
260
  })
249
261
  })
@@ -72,11 +72,13 @@ const doubled = createMemo((prev) => {
72
72
  ```
73
73
 
74
74
  **`Slot` is a property descriptor**
75
- - Has `get`, `set`, `configurable`, `enumerable` fields
76
- - Can be passed directly to `Object.defineProperty()`:
75
+ - Has `get`, `set`, `configurable`, `enumerable` fields — pass directly to `Object.defineProperty()`
76
+ - Delegates reads and writes to a swappable backing signal; use `replace(nextSignal)` to swap
77
+ - Is a forwarding layer, not a value owner — has no `update()` method
77
78
 
78
79
  ```typescript
79
- const slot = createSlot(store, 'name')
80
+ const nameState = createState('Alice')
81
+ const slot = createSlot(nameState)
80
82
  Object.defineProperty(element, 'name', slot)
81
83
  ```
82
84
 
@@ -55,8 +55,8 @@ Read all signals you care about eagerly — before any conditional logic — to
55
55
  // Bad — `derived` is only read after `task` resolves to `ok`
56
56
  // `derived.watched` does not fire until the task has a value
57
57
  createEffect(() => {
58
- match([task], {
59
- ok: ([result]) => render(derived.get(), result),
58
+ match(task, {
59
+ ok: result => render(derived.get(), result),
60
60
  nil: () => showSpinner(),
61
61
  })
62
62
  })
@@ -149,8 +149,8 @@ createEffect(() => {
149
149
 
150
150
  // Correct — match handles the nil (unset) case explicitly
151
151
  createEffect(() => {
152
- match([sensor], {
153
- ok: ([timestamp]) => console.log(timestamp),
152
+ match(sensor, {
153
+ ok: timestamp => console.log(timestamp),
154
154
  nil: () => console.log('waiting for first value…'),
155
155
  })
156
156
  })
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.1.1
4
+
5
+ ### Fixed
6
+
7
+ - **`Slot.set()` now forwards through Slot-to-Slot chains**: Previously, writing to a Slot whose backing signal was itself a Slot threw `ReadonlySignalError` because `isMutableSignal` does not include `Slot` (by design — a Slot wrapping a read-only signal is not mutable). `set()` now recursively delegates to the next Slot in the chain, allowing the terminal backing signal to determine write permissions. Chains of arbitrary depth are resolved correctly.
8
+
9
+ ## 1.1.0
10
+
11
+ ### Added
12
+
13
+ - **Single-signal overload for `match()`**: `match(signal, handlers)` now accepts a bare signal (not wrapped in an array). The `ok` handler receives the resolved value directly as `(value: T)`, and `err` receives a single `Error` rather than `readonly Error[]`. The existing tuple form is unchanged. This eliminates the boilerplate of wrapping a single source in `[source]`, destructuring `values[0]` in `ok`, and unwrapping `errors[0]!` in `err`.
14
+ - **`SingleMatchHandlers<T>` type**: New exported type that describes the handler object for the single-signal overload. Counterpart to the existing `MatchHandlers<T>` for tuple usage.
15
+
16
+ ### Changed
17
+
18
+ - **Async handler documentation**: Added `@remarks` to the `match()` JSDoc and an expanded section in `README.md` clarifying that async `ok`/`err` handlers are intended for external side effects only (logging, DOM writes, analytics). Any async work that needs to drive reactive state should use a `Task` node, which receives an `AbortSignal` and is auto-cancelled on re-run. Documents the known limitation that rejected async handlers from stale (superseded) runs still call `err`, since the library cannot cancel operations it did not initiate.
19
+
3
20
  ## 1.0.2
4
21
 
5
22
  ### Added
package/GUIDE.md CHANGED
@@ -184,10 +184,10 @@ Use `match()` inside effects to handle all states declaratively:
184
184
 
185
185
  ```ts
186
186
  createEffect(() => {
187
- match([user], {
188
- ok: ([data]) => console.log('User:', data),
187
+ match(user, {
188
+ ok: data => console.log('User:', data),
189
189
  nil: () => console.log('Loading...'),
190
- err: (errors) => console.error(errors[0])
190
+ err: error => console.error(error)
191
191
  })
192
192
  })
193
193
  ```
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Cause & Effect
2
2
 
3
- Version 1.0.2
3
+ Version 1.1.1
4
4
 
5
5
  **Cause & Effect** is a reactive state management primitives library for TypeScript. It provides the foundational building blocks for managing complex, dynamic, composite, and asynchronous state — correctly and performantly — in a unified signal graph.
6
6
 
@@ -392,14 +392,40 @@ const userData = createTask(async (_, abort) => {
392
392
  })
393
393
 
394
394
  createEffect(() => {
395
- match([userData], {
396
- ok: ([user]) => console.log('User:', user),
395
+ match(userData, {
396
+ ok: user => console.log('User:', user),
397
397
  nil: () => console.log('Loading...'),
398
- err: errors => console.error(errors[0])
398
+ err: error => console.error(error)
399
399
  })
400
400
  })
401
401
  ```
402
402
 
403
+ **When to make a handler async.** The `ok` (and `err`) handler may return a `Promise`. Use this for *external* side effects whose result does not need to drive reactive state — sending analytics, writing to IndexedDB, triggering a toast notification, or any fire-and-forget call. A cleanup function returned by the resolved Promise is registered and called synchronously before the next re-run.
404
+
405
+ **Do not set signal state inside an async handler.** If the async result needs to update the graph, model it as a `Task` instead. `Task` receives an `AbortSignal`, is auto-cancelled when its dependencies change, and exposes its pending / resolved / error states as first-class reactive values that compose naturally with `nil` and `err`.
406
+
407
+ ```js
408
+ // ✗ Don't: async handler that writes back into the graph
409
+ createEffect(() => match(trigger, {
410
+ ok: async () => {
411
+ const data = await fetch('/api/data').then(r => r.json())
412
+ result.set(data) // ← side-channel write, not tracked, no cancellation
413
+ }
414
+ }))
415
+
416
+ // ✓ Do: derive the async value as a Task, read it in match()
417
+ const result = createTask(async (_, signal) =>
418
+ fetch('/api/data', { signal }).then(r => r.json()))
419
+
420
+ createEffect(() => match(result, {
421
+ ok: data => render(data),
422
+ nil: () => showSpinner(),
423
+ err: e => showError(e)
424
+ }))
425
+ ```
426
+
427
+ **Stale-run rejections still reach `err`.** When a signal changes and the effect re-runs, the in-flight async handler from the previous run cannot be cancelled (the library did not initiate the underlying operation). If that stale operation eventually rejects, `err` will be called even though a newer run is already active. This is another reason to keep async handlers free of state writes — routing errors to `err` is safe when `err` is a pure side effect (logging, displaying a notification), but it becomes incorrect if `err` calls `.set()` on a signal that run 2 has already updated.
428
+
403
429
  ### Utilities
404
430
 
405
431
  Polymorphic factories and type predicates for generic and library-author code.
package/index.dev.js CHANGED
@@ -1229,10 +1229,14 @@ function createEffect(fn) {
1229
1229
  runEffect(node);
1230
1230
  return dispose;
1231
1231
  }
1232
- function match(signals, handlers) {
1232
+ function match(signalOrSignals, handlers) {
1233
1233
  if (!activeOwner)
1234
1234
  throw new RequiredOwnerError("match");
1235
- const { ok, err = console.error, nil } = handlers;
1235
+ const isSingle = !Array.isArray(signalOrSignals);
1236
+ const signals = isSingle ? [signalOrSignals] : signalOrSignals;
1237
+ const { nil } = handlers;
1238
+ const ok = isSingle ? (values2) => handlers.ok(values2[0]) : (values2) => handlers.ok(values2);
1239
+ const err = isSingle && handlers.err ? (errors2) => handlers.err(errors2[0]) : handlers.err ?? console.error;
1236
1240
  let errors;
1237
1241
  let pending = false;
1238
1242
  const values = new Array(signals.length);
@@ -1603,6 +1607,8 @@ function createSlot(initialSignal, options) {
1603
1607
  return node.value;
1604
1608
  };
1605
1609
  const set = (next) => {
1610
+ if (isSlot(delegated))
1611
+ return delegated.set(next);
1606
1612
  if (!isMutableSignal(delegated))
1607
1613
  throw new ReadonlySignalError(TYPE_SLOT);
1608
1614
  validateSignalValue(TYPE_SLOT, next, guard);
package/index.js CHANGED
@@ -1 +1 @@
1
- function g($){return typeof $==="function"}function o($){return g($)&&$.constructor.name==="AsyncFunction"}function X$($){return g($)&&$.constructor.name!=="AsyncFunction"}function f($,J){return Object.prototype.toString.call($)===`[object ${J}]`}function E($){return f($,"Object")}function f$($,J=(z)=>z!=null){return Array.isArray($)&&$.every(J)}function D$($){return typeof $==="string"?`"${$}"`:!!$&&typeof $==="object"?JSON.stringify($):String($)}class Z$ extends Error{constructor($){super(`[${$}] Circular dependency detected`);this.name="CircularDependencyError"}}class A$ extends TypeError{constructor($){super(`[${$}] Signal value cannot be null or undefined`);this.name="NullishSignalValueError"}}class i extends Error{constructor($){super(`[${$}] Signal value is unset`);this.name="UnsetSignalValueError"}}class W$ extends TypeError{constructor($,J){super(`[${$}] Signal value ${D$(J)} is invalid`);this.name="InvalidSignalValueError"}}class b$ extends TypeError{constructor($,J){super(`[${$}] Callback ${D$(J)} is invalid`);this.name="InvalidCallbackError"}}class P$ extends Error{constructor($){super(`[${$}] Signal is read-only`);this.name="ReadonlySignalError"}}class G$ extends Error{constructor($){super(`[${$}] Active owner is required`);this.name="RequiredOwnerError"}}class e extends Error{constructor($,J,z){super(`[${$}] Could not add key "${J}"${z?` with value ${JSON.stringify(z)}`:""} because it already exists`);this.name="DuplicateKeyError"}}function K($,J,z){if(J==null)throw new A$($);if(z&&!z(J))throw new W$($,J)}function j$($,J){if(J==null)throw new i($)}function S($,J,z=g){if(!z(J))throw new b$($,J)}var c="State",u="Memo",d="Task",s="Sensor",L="List",t="Collection",l="Store",r="Slot",p=0,$$=1,P=2,U$=4,b=8,m=null,_=null,C$=[],R=0,_$=!1,k=($,J)=>$===J,L$=($,J)=>!1;function g$($,J){let z=J.sourcesTail;if(z){let X=J.sources;while(X){if(X===$)return!0;if(X===z)break;X=X.nextSource}}return!1}function F($,J){let z=J.sourcesTail;if(z?.source===$)return;let X=null,j=J.flags&U$;if(j){if(X=z?z.nextSource:J.sources,X?.source===$){J.sourcesTail=X;return}}let B=$.sinksTail;if(B?.sink===J&&(!j||g$(B,J)))return;let D={source:$,sink:J,nextSource:X,prevSink:B,nextSink:null};if(J.sourcesTail=$.sinksTail=D,z)z.nextSource=D;else J.sources=D;if(B)B.nextSink=D;else $.sinks=D}function k$($){let{source:J,nextSource:z,nextSink:X,prevSink:j}=$;if(X)X.prevSink=j;else J.sinksTail=j;if(j)j.nextSink=X;else J.sinks=X;if(!J.sinks){if(J.stop)J.stop(),J.stop=void 0;if("sources"in J&&J.sources){let B=J;B.sourcesTail=null,H$(B)}}return z}function H$($){let J=$.sourcesTail,z=J?J.nextSource:$.sources;while(z)z=k$(z);if(J)J.nextSource=null;else $.sources=null}function x($,J=P){let z=$.flags;if("sinks"in $){if((z&(P|$$))>=J)return;if($.flags=z|J,"controller"in $&&$.controller)$.controller.abort(),$.controller=void 0;for(let X=$.sinks;X;X=X.nextSink)x(X.sink,$$)}else{if((z&(P|$$))>=J)return;let X=z&(P|$$);if($.flags=J,!X)C$.push($)}}function N$($,J){if($.equals($.value,J))return;$.value=J;for(let z=$.sinks;z;z=z.nextSink)x(z.sink);if(R===0)I()}function J$($,J){if(!$.cleanup)$.cleanup=J;else if(Array.isArray($.cleanup))$.cleanup.push(J);else $.cleanup=[$.cleanup,J]}function K$($){if(!$.cleanup)return;if(Array.isArray($.cleanup))for(let J=0;J<$.cleanup.length;J++)$.cleanup[J]();else $.cleanup();$.cleanup=null}function v$($){let J=m;m=$,$.sourcesTail=null,$.flags=U$;let z=!1;try{let X=$.fn($.value);if($.error||!$.equals(X,$.value))$.value=X,$.error=void 0,z=!0}catch(X){z=!0,$.error=X instanceof Error?X:Error(String(X))}finally{m=J,H$($)}if(z){for(let X=$.sinks;X;X=X.nextSink)if(X.sink.flags&$$)X.sink.flags|=P}$.flags=p}function c$($){$.controller?.abort();let J=new AbortController;$.controller=J,$.error=void 0;let z=m;m=$,$.sourcesTail=null,$.flags=U$;let X;try{X=$.fn($.value,J.signal)}catch(j){$.controller=void 0,$.error=j instanceof Error?j:Error(String(j));return}finally{m=z,H$($)}X.then((j)=>{if(J.signal.aborted)return;if($.controller=void 0,$.error||!$.equals(j,$.value)){$.value=j,$.error=void 0;for(let B=$.sinks;B;B=B.nextSink)x(B.sink);if(R===0)I()}},(j)=>{if(J.signal.aborted)return;$.controller=void 0;let B=j instanceof Error?j:Error(String(j));if(!$.error||B.name!==$.error.name||B.message!==$.error.message){$.error=B;for(let D=$.sinks;D;D=D.nextSink)x(D.sink);if(R===0)I()}}),$.flags=p}function T$($){K$($);let J=m,z=_;m=_=$,$.sourcesTail=null,$.flags=U$;try{let X=$.fn();if(typeof X==="function")J$($,X)}finally{m=J,_=z,H$($)}$.flags=p}function w($){if($.flags&$$)for(let J=$.sources;J;J=J.nextSource){if("fn"in J.source)w(J.source);if($.flags&P)break}if($.flags&U$)throw new Z$("controller"in $?d:("value"in $)?u:"Effect");if($.flags&P)if("controller"in $)c$($);else if("value"in $)v$($);else T$($);else $.flags=p}function I(){if(_$)return;_$=!0;try{for(let $=0;$<C$.length;$++){let J=C$[$];if(J.flags&(P|$$))w(J)}C$.length=0}finally{_$=!1}}function z$($){R++;try{$()}finally{if(R--,R===0)I()}}function v($){let J=m;m=null;try{return $()}finally{m=J}}function u$($){let J=_,z={cleanup:null};_=z;try{let X=$();if(typeof X==="function")J$(z,X);let j=()=>K$(z);if(J)J$(J,j);return j}finally{_=J}}function d$($){let J=_;_=null;try{return $()}finally{_=J}}function y($,J){K(c,$,J?.guard);let z={value:$,sinks:null,sinksTail:null,equals:J?.equals??k,guard:J?.guard};return{[Symbol.toStringTag]:c,get(){if(m)F(z,m);return z.value},set(X){K(c,X,z.guard),N$(z,X)},update(X){S(c,X);let j=X(z.value);K(c,j,z.guard),N$(z,j)}}}function V$($){return f($,c)}function n($,J,z){if(Object.is($,J))return!0;if(typeof $!==typeof J)return!1;if($==null||typeof $!=="object"||J==null||typeof J!=="object")return!1;if(!z)z=new WeakSet;if(z.has($)||z.has(J))throw new Z$("isEqual");z.add($),z.add(J);try{let X=Array.isArray($);if(X!==Array.isArray(J))return!1;if(X){let j=$,B=J;if(j.length!==B.length)return!1;for(let D=0;D<j.length;D++)if(!n(j[D],B[D],z))return!1;return!0}if(E($)&&E(J)){let j=Object.keys($),B=Object.keys(J);if(j.length!==B.length)return!1;for(let D of j){if(!(D in J))return!1;if(!n($[D],J[D],z))return!1}return!0}return!1}finally{z.delete($),z.delete(J)}}function O$($,J){if($.length!==J.length)return!1;for(let z=0;z<$.length;z++)if($[z]!==J[z])return!1;return!0}function E$($){let J=0,z=typeof $==="function";return[typeof $==="string"?()=>`${$}${J++}`:z?(X)=>$(X)||String(J++):()=>String(J++),z]}function s$($,J,z,X,j){let B=new WeakSet,D={},M={},C={},N=[],Q=!1,W=new Map;for(let q=0;q<$.length;q++){let Z=z[q],H=$[q];if(Z&&H!==void 0)W.set(Z,H)}let G=new Set;for(let q=0;q<J.length;q++){let Z=J[q];if(Z===void 0)continue;let H=j?X(Z):z[q]??X(Z);if(G.has(H))throw new e(L,H,Z);if(N.push(H),G.add(H),!W.has(H))D[H]=Z,Q=!0;else if(!n(W.get(H),Z,B))M[H]=Z,Q=!0}for(let[q]of W)if(!G.has(q))C[q]=null,Q=!0;if(!Q&&!O$(z,N))Q=!0;return{add:D,change:M,remove:C,newKeys:N,changed:Q}}function Q$($,J){K(L,$,Array.isArray);let z=new Map,X=[],[j,B]=E$(J?.keyConfig),D=()=>X.map((Z)=>z.get(Z)?.get()).filter((Z)=>Z!==void 0),M={fn:D,value:$,flags:P,sources:null,sourcesTail:null,sinks:null,sinksTail:null,equals:n,error:void 0},C=(Z)=>{let H={};for(let U=0;U<Z.length;U++){let V=Z[U];if(V===void 0)continue;let O=X[U];if(!O)O=j(V),X[U]=O;H[O]=V}return H},N=(Z)=>{let H=!1;for(let U in Z.add){let V=Z.add[U];K(`${L} item for key "${U}"`,V),z.set(U,y(V)),H=!0}if(Object.keys(Z.change).length)z$(()=>{for(let U in Z.change){let V=Z.change[U];K(`${L} item for key "${U}"`,V);let O=z.get(U);if(O)O.set(V)}});for(let U in Z.remove){z.delete(U);let V=X.indexOf(U);if(V!==-1)X.splice(V,1);H=!0}if(H)M.flags|=b;return Z.changed},Q=J?.watched,W=Q?()=>{if(m){if(!M.sinks)M.stop=Q();F(M,m)}}:()=>{if(m)F(M,m)},G=C($);for(let Z in G){let H=G[Z];K(`${L} item for key "${Z}"`,H),z.set(Z,y(H))}M.value=$,M.flags=0;let q={[Symbol.toStringTag]:L,[Symbol.isConcatSpreadable]:!0,*[Symbol.iterator](){for(let Z of X){let H=z.get(Z);if(H)yield H}},get length(){return W(),X.length},get(){if(W(),M.sources){if(M.flags){let Z=M.flags&b;if(M.value=v(D),Z){if(M.flags=P,w(M),M.error)throw M.error}else M.flags=p}}else if(w(M),M.error)throw M.error;return M.value},set(Z){let H=M.flags&P?D():M.value,U=s$(H,Z,X,j,B);if(U.changed){X=U.newKeys,N(U),M.flags|=P;for(let V=M.sinks;V;V=V.nextSink)x(V.sink);if(R===0)I()}},update(Z){q.set(Z(q.get()))},at(Z){let H=X[Z];return H!==void 0?z.get(H):void 0},keys(){return W(),X.values()},byKey(Z){return z.get(Z)},keyAt(Z){return X[Z]},indexOfKey(Z){return X.indexOf(Z)},add(Z){let H=j(Z);if(z.has(H))throw new e(L,H,Z);if(!X.includes(H))X.push(H);K(`${L} item for key "${H}"`,Z),z.set(H,y(Z)),M.flags|=P|b;for(let U=M.sinks;U;U=U.nextSink)x(U.sink);if(R===0)I();return H},remove(Z){let H=typeof Z==="number"?X[Z]:Z;if(H===void 0)return;if(z.delete(H)){let V=typeof Z==="number"?Z:X.indexOf(H);if(V>=0)X.splice(V,1);M.flags|=P|b;for(let O=M.sinks;O;O=O.nextSink)x(O.sink);if(R===0)I()}},replace(Z,H){let U=z.get(Z);if(!U)return;if(K(`${L} item for key "${Z}"`,H),U.get()===H)return;U.set(H),M.flags|=P;for(let V=M.sinks;V;V=V.nextSink)x(V.sink);if(R===0)I()},sort(Z){let U=X.map((V)=>[V,z.get(V)?.get()]).sort(g(Z)?(V,O)=>Z(V[1],O[1]):(V,O)=>String(V[1]).localeCompare(String(O[1]))).map(([V])=>V);if(!O$(X,U)){X=U,M.flags|=P;for(let V=M.sinks;V;V=V.nextSink)x(V.sink);if(R===0)I()}},splice(Z,H,...U){let V=X.length,O=Z<0?Math.max(0,V+Z):Math.min(Z,V),Y=Math.max(0,Math.min(H??Math.max(0,V-Math.max(0,O)),V-O)),A={},T={};for(let h=0;h<Y;h++){let a=O+h,Y$=X[a];if(Y$){let y$=z.get(Y$);if(y$)T[Y$]=y$.get()}}let w$=X.slice(0,O);for(let h of U){let a=j(h);if(z.has(a)&&!(a in T))throw new e(L,a,h);w$.push(a),A[a]=h}w$.push(...X.slice(O+Y));let h$=!!(Object.keys(A).length||Object.keys(T).length);if(h$){N({add:A,change:{},remove:T,changed:h$}),X=w$,M.flags|=P;for(let h=M.sinks;h;h=h.nextSink)x(h.sink);if(R===0)I()}return Object.values(T)},deriveCollection(Z){return x$(q,Z)}};return q}function R$($){return f($,L)}function B$($,J){if(S(u,$,X$),J?.value!==void 0)K(u,J.value,J?.guard);let z={fn:$,value:J?.value,flags:P,sources:null,sourcesTail:null,sinks:null,sinksTail:null,equals:J?.equals??k,error:void 0,stop:void 0},X=J?.watched,j=X?()=>{if(m){if(!z.sinks)z.stop=X(()=>{if(x(z),R===0)I()});F(z,m)}}:()=>{if(m)F(z,m)};return{[Symbol.toStringTag]:u,get(){if(j(),w(z),z.error)throw z.error;return j$(u,z.value),z.value}}}function S$($){return f($,u)}function q$($,J){if(S(d,$,o),J?.value!==void 0)K(d,J.value,J?.guard);let z={fn:$,value:J?.value,sources:null,sourcesTail:null,sinks:null,sinksTail:null,flags:P,equals:J?.equals??k,controller:void 0,error:void 0,stop:void 0},X=J?.watched,j=X?()=>{if(m){if(!z.sinks)z.stop=X(()=>{if(x(z),R===0)I()});F(z,m)}}:()=>{if(m)F(z,m)};return{[Symbol.toStringTag]:d,get(){if(j(),w(z),z.error)throw z.error;return j$(d,z.value),z.value},isPending(){return!!z.controller},abort(){z.controller?.abort(),z.controller=void 0}}}function p$($){return f($,d)}function x$($,J){S(t,J);let z=o(J),X=new Map,j=[],B=(q)=>{let Z=z?q$(async(H,U)=>{let V=$.byKey(q)?.get();if(V==null)return H;return J(V,U)}):B$(()=>{let H=$.byKey(q)?.get();if(H==null)return;return J(H)});X.set(q,Z)};function D(q){if(!O$(j,q)){let Z=new Set(j),H=new Set(q);for(let U of j)if(!H.has(U))X.delete(U);for(let U of q)if(!Z.has(U))B(U);j=q,N.flags|=b}}function M(){D(Array.from($.keys()));let q=[];for(let Z of j)try{let H=X.get(Z)?.get();if(H!=null)q.push(H)}catch(H){if(!(H instanceof i))throw H}return q}let N={fn:M,value:[],flags:P,sources:null,sourcesTail:null,sinks:null,sinksTail:null,equals:(q,Z)=>{if(q.length!==Z.length)return!1;for(let H=0;H<q.length;H++)if(q[H]!==Z[H])return!1;return!0},error:void 0};function Q(){if(N.sources){if(N.flags)if(N.value=v(M),N.flags&b){if(N.flags=P,w(N),N.error)throw N.error}else N.flags=p}else if(N.sinks){if(w(N),N.error)throw N.error}else N.value=v(M)}let W=Array.from(v(()=>$.keys()));for(let q of W)B(q);j=W;let G={[Symbol.toStringTag]:t,[Symbol.isConcatSpreadable]:!0,*[Symbol.iterator](){for(let q of j){let Z=X.get(q);if(Z)yield Z}},get length(){if(m)F(N,m);return Q(),j.length},keys(){if(m)F(N,m);return Q(),j.values()},get(){if(m)F(N,m);return Q(),N.value},at(q){let Z=j[q];return Z!==void 0?X.get(Z):void 0},byKey(q){return X.get(q)},keyAt(q){return j[q]},indexOfKey(q){return j.indexOf(q)},deriveCollection(q){return x$(G,q)}};return G}function t$($,J){let z=J?.value??[];if(z.length)K(t,z,Array.isArray);S(t,$,X$);let X=new Map,j=[],B=new Map,[D,M]=E$(J?.keyConfig),C=(Z)=>B.get(Z)??(M?D(Z):void 0),N=J?.createItem??y;function Q(){let Z=[];for(let H of j)try{let U=X.get(H)?.get();if(U!=null)Z.push(U)}catch(U){if(!(U instanceof i))throw U}return Z}let W={fn:Q,value:z,flags:P,sources:null,sourcesTail:null,sinks:null,sinksTail:null,equals:L$,error:void 0};for(let Z of z){let H=D(Z);X.set(H,N(Z)),B.set(Z,H),j.push(H)}W.value=z,W.flags=P;function G(){if(m){if(!W.sinks)W.stop=$((Z)=>{let{add:H,change:U,remove:V}=Z;if(!H?.length&&!U?.length&&!V?.length)return;let O=!1;z$(()=>{if(H)for(let Y of H){let A=D(Y);if(X.set(A,N(Y)),B.set(Y,A),!j.includes(A))j.push(A);O=!0}if(U)for(let Y of U){let A=C(Y);if(!A)continue;let T=X.get(A);if(T&&V$(T))B.delete(T.get()),T.set(Y),B.set(Y,A)}if(V)for(let Y of V){let A=C(Y);if(!A)continue;B.delete(Y),X.delete(A);let T=j.indexOf(A);if(T!==-1)j.splice(T,1);O=!0}W.flags=P|(O?b:0);for(let Y=W.sinks;Y;Y=Y.nextSink)x(Y.sink)})});F(W,m)}}let q={[Symbol.toStringTag]:t,[Symbol.isConcatSpreadable]:!0,*[Symbol.iterator](){for(let Z of j){let H=X.get(Z);if(H)yield H}},get length(){return G(),j.length},keys(){return G(),j.values()},get(){if(G(),W.sources){if(W.flags){let Z=W.flags&b;if(W.value=v(Q),Z){if(W.flags=P,w(W),W.error)throw W.error}else W.flags=p}}else if(w(W),W.error)throw W.error;return W.value},at(Z){let H=j[Z];return H!==void 0?X.get(H):void 0},byKey(Z){return X.get(Z)},keyAt(Z){return j[Z]},indexOfKey(Z){return j.indexOf(Z)},deriveCollection(Z){return x$(q,Z)}};return q}function l$($){return f($,t)}function r$($){S("Effect",$);let J={fn:$,flags:P,sources:null,sourcesTail:null,cleanup:null},z=()=>{K$(J),J.fn=void 0,J.flags=p,J.sourcesTail=null,H$(J)};if(_)J$(_,z);return T$(J),z}function o$($,J){if(!_)throw new G$("match");let{ok:z,err:X=console.error,nil:j}=J,B,D=!1,M=Array($.length);for(let N=0;N<$.length;N++)try{M[N]=$[N].get()}catch(Q){if(Q instanceof i){D=!0;continue}if(!B)B=[];B.push(Q instanceof Error?Q:Error(String(Q)))}let C;try{if(D)C=j?.();else if(B)C=X(B);else C=z(M)}catch(N){X([N instanceof Error?N:Error(String(N))])}if(typeof C==="function")return C;if(C instanceof Promise){let N=_,Q=new AbortController;J$(N,()=>Q.abort()),C.then((W)=>{if(!Q.signal.aborted&&typeof W==="function")J$(N,W)}).catch((W)=>{X([W instanceof Error?W:Error(String(W))])})}}function i$($,J){if(S(s,$,X$),J?.value!==void 0)K(s,J.value,J?.guard);let z={value:J?.value,sinks:null,sinksTail:null,equals:J?.equals??k,guard:J?.guard,stop:void 0};return{[Symbol.toStringTag]:s,get(){if(m){if(!z.sinks)z.stop=$((X)=>{K(s,X,z.guard),N$(z,X)});F(z,m)}return j$(s,z.value),z.value}}}function n$($){return f($,s)}function a$($,J){let z=E($)||Array.isArray($),X=E(J)||Array.isArray(J);if(!z||!X){let W=!Object.is($,J);return{changed:W,add:W&&X?J:{},change:{},remove:W&&z?$:{}}}let j=new WeakSet,B={},D={},M={},C=!1,N=Object.keys($),Q=Object.keys(J);for(let W of Q)if(W in $){if(!n($[W],J[W],j))D[W]=J[W],C=!0}else B[W]=J[W],C=!0;for(let W of N)if(!(W in J))M[W]=void 0,C=!0;return{add:B,change:D,remove:M,changed:C}}function m$($,J){K(l,$,E);let z=new Map,X=(Q,W)=>{if(K(`${l} for key "${Q}"`,W),Array.isArray(W))z.set(Q,Q$(W));else if(E(W))z.set(Q,m$(W));else z.set(Q,y(W))},j=()=>{let Q={};return z.forEach((W,G)=>{Q[G]=W.get()}),Q},B={fn:j,value:$,flags:P,sources:null,sourcesTail:null,sinks:null,sinksTail:null,equals:n,error:void 0},D=(Q)=>{let W=!1;for(let G in Q.add)X(G,Q.add[G]),W=!0;if(Object.keys(Q.change).length)z$(()=>{for(let G in Q.change){let q=Q.change[G];K(`${l} for key "${G}"`,q);let Z=z.get(G);if(Z)if(E(q)!==F$(Z))X(G,q),W=!0;else Z.set(q)}});for(let G in Q.remove)z.delete(G),W=!0;if(W)B.flags|=b;return Q.changed},M=J?.watched,C=M?()=>{if(m){if(!B.sinks)B.stop=M();F(B,m)}}:()=>{if(m)F(B,m)};for(let Q of Object.keys($))X(Q,$[Q]);let N={[Symbol.toStringTag]:l,[Symbol.isConcatSpreadable]:!1,*[Symbol.iterator](){for(let Q of Array.from(z.keys())){let W=z.get(Q);if(W)yield[Q,W]}},keys(){return C(),z.keys()},byKey(Q){return z.get(Q)},get(){if(C(),B.sources){if(B.flags){let Q=B.flags&b;if(B.value=v(j),Q){if(B.flags=P,w(B),B.error)throw B.error}else B.flags=p}}else if(w(B),B.error)throw B.error;return B.value},set(Q){let W=B.flags&P?j():B.value,G=a$(W,Q);if(D(G)){B.flags|=P;for(let q=B.sinks;q;q=q.nextSink)x(q.sink);if(R===0)I()}},update(Q){N.set(Q(N.get()))},add(Q,W){if(z.has(Q))throw new e(l,Q,W);X(Q,W),B.flags|=P|b;for(let G=B.sinks;G;G=G.nextSink)x(G.sink);if(R===0)I();return Q},remove(Q){if(z.delete(Q)){B.flags|=P|b;for(let G=B.sinks;G;G=G.nextSink)x(G.sink);if(R===0)I()}}};return new Proxy(N,{get(Q,W){if(W in Q)return Reflect.get(Q,W);if(typeof W!=="symbol")return Q.byKey(W)},has(Q,W){if(W in Q)return!0;return Q.byKey(String(W))!==void 0},ownKeys(Q){return Array.from(Q.keys())},getOwnPropertyDescriptor(Q,W){if(W in Q)return Reflect.getOwnPropertyDescriptor(Q,W);if(typeof W==="symbol")return;let G=Q.byKey(String(W));return G?{enumerable:!0,configurable:!0,writable:!0,value:G}:void 0}})}function F$($){return f($,l)}function e$($,J){return o($)?q$($,J):B$($,J)}function $J($){if(M$($))return $;if($==null)throw new W$("createSignal",$);if(o($))return q$($);if(g($))return B$($);if(f$($))return Q$($);if(E($))return m$($);return y($)}function JJ($){if(I$($))return $;if($==null||g($)||M$($))throw new W$("createMutableSignal",$);if(f$($))return Q$($);if(E($))return m$($);return y($)}function zJ($){return S$($)||p$($)}function M$($){let J=[c,u,d,s,r,L,t,l],z=Object.prototype.toString.call($).slice(8,-1);return J.includes(z)}function I$($){return V$($)||F$($)||R$($)}function XJ($,J){K(r,$,M$);let z=$,X=J?.guard,j={fn:()=>z.get(),value:void 0,flags:P,sources:null,sourcesTail:null,sinks:null,sinksTail:null,equals:J?.equals??k,error:void 0},B=()=>{if(m)F(j,m);if(w(j),j.error)throw j.error;return j.value},D=(C)=>{if(!I$(z))throw new P$(r);K(r,C,X),z.set(C)},M=(C)=>{K(r,C,M$),z=C,j.flags|=P;for(let N=j.sinks;N;N=N.nextSink)x(N.sink);if(R===0)I()};return{[Symbol.toStringTag]:r,configurable:!0,enumerable:!0,get:B,set:D,replace:M,current:()=>z}}function ZJ($){return f($,r)}export{D$ as valueString,v as untrack,d$ as unown,o$ as match,p$ as isTask,F$ as isStore,V$ as isState,ZJ as isSlot,M$ as isSignal,n$ as isSensor,E as isRecord,f as isObjectOfType,I$ as isMutableSignal,S$ as isMemo,R$ as isList,g as isFunction,n as isEqual,zJ as isComputed,l$ as isCollection,o as isAsyncFunction,q$ as createTask,m$ as createStore,y as createState,XJ as createSlot,$J as createSignal,i$ as createSensor,u$ as createScope,JJ as createMutableSignal,B$ as createMemo,Q$ as createList,r$ as createEffect,e$ as createComputed,t$ as createCollection,z$ as batch,i as UnsetSignalValueError,L$ as SKIP_EQUALITY,G$ as RequiredOwnerError,P$ as ReadonlySignalError,A$ as NullishSignalValueError,W$ as InvalidSignalValueError,b$ as InvalidCallbackError,Z$ as CircularDependencyError};
1
+ function g($){return typeof $==="function"}function l($){return g($)&&$.constructor.name==="AsyncFunction"}function X$($){return g($)&&$.constructor.name!=="AsyncFunction"}function Y($,J){return Object.prototype.toString.call($)===`[object ${J}]`}function E($){return Y($,"Object")}function Y$($,J=(z)=>z!=null){return Array.isArray($)&&$.every(J)}function D$($){return typeof $==="string"?`"${$}"`:!!$&&typeof $==="object"?JSON.stringify($):String($)}class Z$ extends Error{constructor($){super(`[${$}] Circular dependency detected`);this.name="CircularDependencyError"}}class A$ extends TypeError{constructor($){super(`[${$}] Signal value cannot be null or undefined`);this.name="NullishSignalValueError"}}class i extends Error{constructor($){super(`[${$}] Signal value is unset`);this.name="UnsetSignalValueError"}}class j$ extends TypeError{constructor($,J){super(`[${$}] Signal value ${D$(J)} is invalid`);this.name="InvalidSignalValueError"}}class b$ extends TypeError{constructor($,J){super(`[${$}] Callback ${D$(J)} is invalid`);this.name="InvalidCallbackError"}}class P$ extends Error{constructor($){super(`[${$}] Signal is read-only`);this.name="ReadonlySignalError"}}class G$ extends Error{constructor($){super(`[${$}] Active owner is required`);this.name="RequiredOwnerError"}}class e extends Error{constructor($,J,z){super(`[${$}] Could not add key "${J}"${z?` with value ${JSON.stringify(z)}`:""} because it already exists`);this.name="DuplicateKeyError"}}function C($,J,z){if(J==null)throw new A$($);if(z&&!z(J))throw new j$($,J)}function W$($,J){if(J==null)throw new i($)}function S($,J,z=g){if(!z(J))throw new b$($,J)}var c="State",u="Memo",d="Task",t="Sensor",L="List",s="Collection",r="Store",o="Slot",p=0,$$=1,G=2,U$=4,b=8,V=null,_=null,C$=[],O=0,_$=!1,k=($,J)=>$===J,L$=($,J)=>!1;function k$($,J){let z=J.sourcesTail;if(z){let X=J.sources;while(X){if(X===$)return!0;if(X===z)break;X=X.nextSource}}return!1}function x($,J){let z=J.sourcesTail;if(z?.source===$)return;let X=null,W=J.flags&U$;if(W){if(X=z?z.nextSource:J.sources,X?.source===$){J.sourcesTail=X;return}}let B=$.sinksTail;if(B?.sink===J&&(!W||k$(B,J)))return;let D={source:$,sink:J,nextSource:X,prevSink:B,nextSink:null};if(J.sourcesTail=$.sinksTail=D,z)z.nextSource=D;else J.sources=D;if(B)B.nextSink=D;else $.sinks=D}function v$($){let{source:J,nextSource:z,nextSink:X,prevSink:W}=$;if(X)X.prevSink=W;else J.sinksTail=W;if(W)W.nextSink=X;else J.sinks=X;if(!J.sinks){if(J.stop)J.stop(),J.stop=void 0;if("sources"in J&&J.sources){let B=J;B.sourcesTail=null,H$(B)}}return z}function H$($){let J=$.sourcesTail,z=J?J.nextSource:$.sources;while(z)z=v$(z);if(J)J.nextSource=null;else $.sources=null}function F($,J=G){let z=$.flags;if("sinks"in $){if((z&(G|$$))>=J)return;if($.flags=z|J,"controller"in $&&$.controller)$.controller.abort(),$.controller=void 0;for(let X=$.sinks;X;X=X.nextSink)F(X.sink,$$)}else{if((z&(G|$$))>=J)return;let X=z&(G|$$);if($.flags=J,!X)C$.push($)}}function N$($,J){if($.equals($.value,J))return;$.value=J;for(let z=$.sinks;z;z=z.nextSink)F(z.sink);if(O===0)w()}function J$($,J){if(!$.cleanup)$.cleanup=J;else if(Array.isArray($.cleanup))$.cleanup.push(J);else $.cleanup=[$.cleanup,J]}function K$($){if(!$.cleanup)return;if(Array.isArray($.cleanup))for(let J=0;J<$.cleanup.length;J++)$.cleanup[J]();else $.cleanup();$.cleanup=null}function c$($){let J=V;V=$,$.sourcesTail=null,$.flags=U$;let z=!1;try{let X=$.fn($.value);if($.error||!$.equals(X,$.value))$.value=X,$.error=void 0,z=!0}catch(X){z=!0,$.error=X instanceof Error?X:Error(String(X))}finally{V=J,H$($)}if(z){for(let X=$.sinks;X;X=X.nextSink)if(X.sink.flags&$$)X.sink.flags|=G}$.flags=p}function u$($){$.controller?.abort();let J=new AbortController;$.controller=J,$.error=void 0;let z=V;V=$,$.sourcesTail=null,$.flags=U$;let X;try{X=$.fn($.value,J.signal)}catch(W){$.controller=void 0,$.error=W instanceof Error?W:Error(String(W));return}finally{V=z,H$($)}X.then((W)=>{if(J.signal.aborted)return;if($.controller=void 0,$.error||!$.equals(W,$.value)){$.value=W,$.error=void 0;for(let B=$.sinks;B;B=B.nextSink)F(B.sink);if(O===0)w()}},(W)=>{if(J.signal.aborted)return;$.controller=void 0;let B=W instanceof Error?W:Error(String(W));if(!$.error||B.name!==$.error.name||B.message!==$.error.message){$.error=B;for(let D=$.sinks;D;D=D.nextSink)F(D.sink);if(O===0)w()}}),$.flags=p}function T$($){K$($);let J=V,z=_;V=_=$,$.sourcesTail=null,$.flags=U$;try{let X=$.fn();if(typeof X==="function")J$($,X)}finally{V=J,_=z,H$($)}$.flags=p}function I($){if($.flags&$$)for(let J=$.sources;J;J=J.nextSource){if("fn"in J.source)I(J.source);if($.flags&G)break}if($.flags&U$)throw new Z$("controller"in $?d:("value"in $)?u:"Effect");if($.flags&G)if("controller"in $)u$($);else if("value"in $)c$($);else T$($);else $.flags=p}function w(){if(_$)return;_$=!0;try{for(let $=0;$<C$.length;$++){let J=C$[$];if(J.flags&(G|$$))I(J)}C$.length=0}finally{_$=!1}}function z$($){O++;try{$()}finally{if(O--,O===0)w()}}function v($){let J=V;V=null;try{return $()}finally{V=J}}function d$($){let J=_,z={cleanup:null};_=z;try{let X=$();if(typeof X==="function")J$(z,X);let W=()=>K$(z);if(J)J$(J,W);return W}finally{_=J}}function t$($){let J=_;_=null;try{return $()}finally{_=J}}function y($,J){C(c,$,J?.guard);let z={value:$,sinks:null,sinksTail:null,equals:J?.equals??k,guard:J?.guard};return{[Symbol.toStringTag]:c,get(){if(V)x(z,V);return z.value},set(X){C(c,X,z.guard),N$(z,X)},update(X){S(c,X);let W=X(z.value);C(c,W,z.guard),N$(z,W)}}}function V$($){return Y($,c)}function n($,J,z){if(Object.is($,J))return!0;if(typeof $!==typeof J)return!1;if($==null||typeof $!=="object"||J==null||typeof J!=="object")return!1;if(!z)z=new WeakSet;if(z.has($)||z.has(J))throw new Z$("isEqual");z.add($),z.add(J);try{let X=Array.isArray($);if(X!==Array.isArray(J))return!1;if(X){let W=$,B=J;if(W.length!==B.length)return!1;for(let D=0;D<W.length;D++)if(!n(W[D],B[D],z))return!1;return!0}if(E($)&&E(J)){let W=Object.keys($),B=Object.keys(J);if(W.length!==B.length)return!1;for(let D of W){if(!(D in J))return!1;if(!n($[D],J[D],z))return!1}return!0}return!1}finally{z.delete($),z.delete(J)}}function R$($,J){if($.length!==J.length)return!1;for(let z=0;z<$.length;z++)if($[z]!==J[z])return!1;return!0}function E$($){let J=0,z=typeof $==="function";return[typeof $==="string"?()=>`${$}${J++}`:z?(X)=>$(X)||String(J++):()=>String(J++),z]}function s$($,J,z,X,W){let B=new WeakSet,D={},M={},K={},P=[],Q=!1,j=new Map;for(let q=0;q<$.length;q++){let Z=z[q],H=$[q];if(Z&&H!==void 0)j.set(Z,H)}let m=new Set;for(let q=0;q<J.length;q++){let Z=J[q];if(Z===void 0)continue;let H=W?X(Z):z[q]??X(Z);if(m.has(H))throw new e(L,H,Z);if(P.push(H),m.add(H),!j.has(H))D[H]=Z,Q=!0;else if(!n(j.get(H),Z,B))M[H]=Z,Q=!0}for(let[q]of j)if(!m.has(q))K[q]=null,Q=!0;if(!Q&&!R$(z,P))Q=!0;return{add:D,change:M,remove:K,newKeys:P,changed:Q}}function Q$($,J){C(L,$,Array.isArray);let z=new Map,X=[],[W,B]=E$(J?.keyConfig),D=()=>X.map((Z)=>z.get(Z)?.get()).filter((Z)=>Z!==void 0),M={fn:D,value:$,flags:G,sources:null,sourcesTail:null,sinks:null,sinksTail:null,equals:n,error:void 0},K=(Z)=>{let H={};for(let U=0;U<Z.length;U++){let N=Z[U];if(N===void 0)continue;let R=X[U];if(!R)R=W(N),X[U]=R;H[R]=N}return H},P=(Z)=>{let H=!1;for(let U in Z.add){let N=Z.add[U];C(`${L} item for key "${U}"`,N),z.set(U,y(N)),H=!0}if(Object.keys(Z.change).length)z$(()=>{for(let U in Z.change){let N=Z.change[U];C(`${L} item for key "${U}"`,N);let R=z.get(U);if(R)R.set(N)}});for(let U in Z.remove){z.delete(U);let N=X.indexOf(U);if(N!==-1)X.splice(N,1);H=!0}if(H)M.flags|=b;return Z.changed},Q=J?.watched,j=Q?()=>{if(V){if(!M.sinks)M.stop=Q();x(M,V)}}:()=>{if(V)x(M,V)},m=K($);for(let Z in m){let H=m[Z];C(`${L} item for key "${Z}"`,H),z.set(Z,y(H))}M.value=$,M.flags=0;let q={[Symbol.toStringTag]:L,[Symbol.isConcatSpreadable]:!0,*[Symbol.iterator](){for(let Z of X){let H=z.get(Z);if(H)yield H}},get length(){return j(),X.length},get(){if(j(),M.sources){if(M.flags){let Z=M.flags&b;if(M.value=v(D),Z){if(M.flags=G,I(M),M.error)throw M.error}else M.flags=p}}else if(I(M),M.error)throw M.error;return M.value},set(Z){let H=M.flags&G?D():M.value,U=s$(H,Z,X,W,B);if(U.changed){X=U.newKeys,P(U),M.flags|=G;for(let N=M.sinks;N;N=N.nextSink)F(N.sink);if(O===0)w()}},update(Z){q.set(Z(q.get()))},at(Z){let H=X[Z];return H!==void 0?z.get(H):void 0},keys(){return j(),X.values()},byKey(Z){return z.get(Z)},keyAt(Z){return X[Z]},indexOfKey(Z){return X.indexOf(Z)},add(Z){let H=W(Z);if(z.has(H))throw new e(L,H,Z);if(!X.includes(H))X.push(H);C(`${L} item for key "${H}"`,Z),z.set(H,y(Z)),M.flags|=G|b;for(let U=M.sinks;U;U=U.nextSink)F(U.sink);if(O===0)w();return H},remove(Z){let H=typeof Z==="number"?X[Z]:Z;if(H===void 0)return;if(z.delete(H)){let N=typeof Z==="number"?Z:X.indexOf(H);if(N>=0)X.splice(N,1);M.flags|=G|b;for(let R=M.sinks;R;R=R.nextSink)F(R.sink);if(O===0)w()}},replace(Z,H){let U=z.get(Z);if(!U)return;if(C(`${L} item for key "${Z}"`,H),U.get()===H)return;U.set(H),M.flags|=G;for(let N=M.sinks;N;N=N.nextSink)F(N.sink);if(O===0)w()},sort(Z){let U=X.map((N)=>[N,z.get(N)?.get()]).sort(g(Z)?(N,R)=>Z(N[1],R[1]):(N,R)=>String(N[1]).localeCompare(String(R[1]))).map(([N])=>N);if(!R$(X,U)){X=U,M.flags|=G;for(let N=M.sinks;N;N=N.nextSink)F(N.sink);if(O===0)w()}},splice(Z,H,...U){let N=X.length,R=Z<0?Math.max(0,N+Z):Math.min(Z,N),f=Math.max(0,Math.min(H??Math.max(0,N-Math.max(0,R)),N-R)),A={},T={};for(let h=0;h<f;h++){let a=R+h,f$=X[a];if(f$){let y$=z.get(f$);if(y$)T[f$]=y$.get()}}let I$=X.slice(0,R);for(let h of U){let a=W(h);if(z.has(a)&&!(a in T))throw new e(L,a,h);I$.push(a),A[a]=h}I$.push(...X.slice(R+f));let h$=!!(Object.keys(A).length||Object.keys(T).length);if(h$){P({add:A,change:{},remove:T,changed:h$}),X=I$,M.flags|=G;for(let h=M.sinks;h;h=h.nextSink)F(h.sink);if(O===0)w()}return Object.values(T)},deriveCollection(Z){return F$(q,Z)}};return q}function O$($){return Y($,L)}function B$($,J){if(S(u,$,X$),J?.value!==void 0)C(u,J.value,J?.guard);let z={fn:$,value:J?.value,flags:G,sources:null,sourcesTail:null,sinks:null,sinksTail:null,equals:J?.equals??k,error:void 0,stop:void 0},X=J?.watched,W=X?()=>{if(V){if(!z.sinks)z.stop=X(()=>{if(F(z),O===0)w()});x(z,V)}}:()=>{if(V)x(z,V)};return{[Symbol.toStringTag]:u,get(){if(W(),I(z),z.error)throw z.error;return W$(u,z.value),z.value}}}function S$($){return Y($,u)}function q$($,J){if(S(d,$,l),J?.value!==void 0)C(d,J.value,J?.guard);let z={fn:$,value:J?.value,sources:null,sourcesTail:null,sinks:null,sinksTail:null,flags:G,equals:J?.equals??k,controller:void 0,error:void 0,stop:void 0},X=J?.watched,W=X?()=>{if(V){if(!z.sinks)z.stop=X(()=>{if(F(z),O===0)w()});x(z,V)}}:()=>{if(V)x(z,V)};return{[Symbol.toStringTag]:d,get(){if(W(),I(z),z.error)throw z.error;return W$(d,z.value),z.value},isPending(){return!!z.controller},abort(){z.controller?.abort(),z.controller=void 0}}}function p$($){return Y($,d)}function F$($,J){S(s,J);let z=l(J),X=new Map,W=[],B=(q)=>{let Z=z?q$(async(H,U)=>{let N=$.byKey(q)?.get();if(N==null)return H;return J(N,U)}):B$(()=>{let H=$.byKey(q)?.get();if(H==null)return;return J(H)});X.set(q,Z)};function D(q){if(!R$(W,q)){let Z=new Set(W),H=new Set(q);for(let U of W)if(!H.has(U))X.delete(U);for(let U of q)if(!Z.has(U))B(U);W=q,P.flags|=b}}function M(){D(Array.from($.keys()));let q=[];for(let Z of W)try{let H=X.get(Z)?.get();if(H!=null)q.push(H)}catch(H){if(!(H instanceof i))throw H}return q}let P={fn:M,value:[],flags:G,sources:null,sourcesTail:null,sinks:null,sinksTail:null,equals:(q,Z)=>{if(q.length!==Z.length)return!1;for(let H=0;H<q.length;H++)if(q[H]!==Z[H])return!1;return!0},error:void 0};function Q(){if(P.sources){if(P.flags)if(P.value=v(M),P.flags&b){if(P.flags=G,I(P),P.error)throw P.error}else P.flags=p}else if(P.sinks){if(I(P),P.error)throw P.error}else P.value=v(M)}let j=Array.from(v(()=>$.keys()));for(let q of j)B(q);W=j;let m={[Symbol.toStringTag]:s,[Symbol.isConcatSpreadable]:!0,*[Symbol.iterator](){for(let q of W){let Z=X.get(q);if(Z)yield Z}},get length(){if(V)x(P,V);return Q(),W.length},keys(){if(V)x(P,V);return Q(),W.values()},get(){if(V)x(P,V);return Q(),P.value},at(q){let Z=W[q];return Z!==void 0?X.get(Z):void 0},byKey(q){return X.get(q)},keyAt(q){return W[q]},indexOfKey(q){return W.indexOf(q)},deriveCollection(q){return F$(m,q)}};return m}function r$($,J){let z=J?.value??[];if(z.length)C(s,z,Array.isArray);S(s,$,X$);let X=new Map,W=[],B=new Map,[D,M]=E$(J?.keyConfig),K=(Z)=>B.get(Z)??(M?D(Z):void 0),P=J?.createItem??y;function Q(){let Z=[];for(let H of W)try{let U=X.get(H)?.get();if(U!=null)Z.push(U)}catch(U){if(!(U instanceof i))throw U}return Z}let j={fn:Q,value:z,flags:G,sources:null,sourcesTail:null,sinks:null,sinksTail:null,equals:L$,error:void 0};for(let Z of z){let H=D(Z);X.set(H,P(Z)),B.set(Z,H),W.push(H)}j.value=z,j.flags=G;function m(){if(V){if(!j.sinks)j.stop=$((Z)=>{let{add:H,change:U,remove:N}=Z;if(!H?.length&&!U?.length&&!N?.length)return;let R=!1;z$(()=>{if(H)for(let f of H){let A=D(f);if(X.set(A,P(f)),B.set(f,A),!W.includes(A))W.push(A);R=!0}if(U)for(let f of U){let A=K(f);if(!A)continue;let T=X.get(A);if(T&&V$(T))B.delete(T.get()),T.set(f),B.set(f,A)}if(N)for(let f of N){let A=K(f);if(!A)continue;B.delete(f),X.delete(A);let T=W.indexOf(A);if(T!==-1)W.splice(T,1);R=!0}j.flags=G|(R?b:0);for(let f=j.sinks;f;f=f.nextSink)F(f.sink)})});x(j,V)}}let q={[Symbol.toStringTag]:s,[Symbol.isConcatSpreadable]:!0,*[Symbol.iterator](){for(let Z of W){let H=X.get(Z);if(H)yield H}},get length(){return m(),W.length},keys(){return m(),W.values()},get(){if(m(),j.sources){if(j.flags){let Z=j.flags&b;if(j.value=v(Q),Z){if(j.flags=G,I(j),j.error)throw j.error}else j.flags=p}}else if(I(j),j.error)throw j.error;return j.value},at(Z){let H=W[Z];return H!==void 0?X.get(H):void 0},byKey(Z){return X.get(Z)},keyAt(Z){return W[Z]},indexOfKey(Z){return W.indexOf(Z)},deriveCollection(Z){return F$(q,Z)}};return q}function o$($){return Y($,s)}function l$($){S("Effect",$);let J={fn:$,flags:G,sources:null,sourcesTail:null,cleanup:null},z=()=>{K$(J),J.fn=void 0,J.flags=p,J.sourcesTail=null,H$(J)};if(_)J$(_,z);return T$(J),z}function i$($,J){if(!_)throw new G$("match");let z=!Array.isArray($),X=z?[$]:$,{nil:W}=J,B=z?(j)=>J.ok(j[0]):(j)=>J.ok(j),D=z&&J.err?(j)=>J.err(j[0]):J.err??console.error,M,K=!1,P=Array(X.length);for(let j=0;j<X.length;j++)try{P[j]=X[j].get()}catch(m){if(m instanceof i){K=!0;continue}if(!M)M=[];M.push(m instanceof Error?m:Error(String(m)))}let Q;try{if(K)Q=W?.();else if(M)Q=D(M);else Q=B(P)}catch(j){D([j instanceof Error?j:Error(String(j))])}if(typeof Q==="function")return Q;if(Q instanceof Promise){let j=_,m=new AbortController;J$(j,()=>m.abort()),Q.then((q)=>{if(!m.signal.aborted&&typeof q==="function")J$(j,q)}).catch((q)=>{D([q instanceof Error?q:Error(String(q))])})}}function n$($,J){if(S(t,$,X$),J?.value!==void 0)C(t,J.value,J?.guard);let z={value:J?.value,sinks:null,sinksTail:null,equals:J?.equals??k,guard:J?.guard,stop:void 0};return{[Symbol.toStringTag]:t,get(){if(V){if(!z.sinks)z.stop=$((X)=>{C(t,X,z.guard),N$(z,X)});x(z,V)}return W$(t,z.value),z.value}}}function a$($){return Y($,t)}function e$($,J){let z=E($)||Array.isArray($),X=E(J)||Array.isArray(J);if(!z||!X){let j=!Object.is($,J);return{changed:j,add:j&&X?J:{},change:{},remove:j&&z?$:{}}}let W=new WeakSet,B={},D={},M={},K=!1,P=Object.keys($),Q=Object.keys(J);for(let j of Q)if(j in $){if(!n($[j],J[j],W))D[j]=J[j],K=!0}else B[j]=J[j],K=!0;for(let j of P)if(!(j in J))M[j]=void 0,K=!0;return{add:B,change:D,remove:M,changed:K}}function m$($,J){C(r,$,E);let z=new Map,X=(Q,j)=>{if(C(`${r} for key "${Q}"`,j),Array.isArray(j))z.set(Q,Q$(j));else if(E(j))z.set(Q,m$(j));else z.set(Q,y(j))},W=()=>{let Q={};return z.forEach((j,m)=>{Q[m]=j.get()}),Q},B={fn:W,value:$,flags:G,sources:null,sourcesTail:null,sinks:null,sinksTail:null,equals:n,error:void 0},D=(Q)=>{let j=!1;for(let m in Q.add)X(m,Q.add[m]),j=!0;if(Object.keys(Q.change).length)z$(()=>{for(let m in Q.change){let q=Q.change[m];C(`${r} for key "${m}"`,q);let Z=z.get(m);if(Z)if(E(q)!==x$(Z))X(m,q),j=!0;else Z.set(q)}});for(let m in Q.remove)z.delete(m),j=!0;if(j)B.flags|=b;return Q.changed},M=J?.watched,K=M?()=>{if(V){if(!B.sinks)B.stop=M();x(B,V)}}:()=>{if(V)x(B,V)};for(let Q of Object.keys($))X(Q,$[Q]);let P={[Symbol.toStringTag]:r,[Symbol.isConcatSpreadable]:!1,*[Symbol.iterator](){for(let Q of Array.from(z.keys())){let j=z.get(Q);if(j)yield[Q,j]}},keys(){return K(),z.keys()},byKey(Q){return z.get(Q)},get(){if(K(),B.sources){if(B.flags){let Q=B.flags&b;if(B.value=v(W),Q){if(B.flags=G,I(B),B.error)throw B.error}else B.flags=p}}else if(I(B),B.error)throw B.error;return B.value},set(Q){let j=B.flags&G?W():B.value,m=e$(j,Q);if(D(m)){B.flags|=G;for(let q=B.sinks;q;q=q.nextSink)F(q.sink);if(O===0)w()}},update(Q){P.set(Q(P.get()))},add(Q,j){if(z.has(Q))throw new e(r,Q,j);X(Q,j),B.flags|=G|b;for(let m=B.sinks;m;m=m.nextSink)F(m.sink);if(O===0)w();return Q},remove(Q){if(z.delete(Q)){B.flags|=G|b;for(let m=B.sinks;m;m=m.nextSink)F(m.sink);if(O===0)w()}}};return new Proxy(P,{get(Q,j){if(j in Q)return Reflect.get(Q,j);if(typeof j!=="symbol")return Q.byKey(j)},has(Q,j){if(j in Q)return!0;return Q.byKey(String(j))!==void 0},ownKeys(Q){return Array.from(Q.keys())},getOwnPropertyDescriptor(Q,j){if(j in Q)return Reflect.getOwnPropertyDescriptor(Q,j);if(typeof j==="symbol")return;let m=Q.byKey(String(j));return m?{enumerable:!0,configurable:!0,writable:!0,value:m}:void 0}})}function x$($){return Y($,r)}function $J($,J){return l($)?q$($,J):B$($,J)}function JJ($){if(M$($))return $;if($==null)throw new j$("createSignal",$);if(l($))return q$($);if(g($))return B$($);if(Y$($))return Q$($);if(E($))return m$($);return y($)}function zJ($){if(w$($))return $;if($==null||g($)||M$($))throw new j$("createMutableSignal",$);if(Y$($))return Q$($);if(E($))return m$($);return y($)}function XJ($){return S$($)||p$($)}function M$($){let J=[c,u,d,t,o,L,s,r],z=Object.prototype.toString.call($).slice(8,-1);return J.includes(z)}function w$($){return V$($)||x$($)||O$($)}function ZJ($,J){C(o,$,M$);let z=$,X=J?.guard,W={fn:()=>z.get(),value:void 0,flags:G,sources:null,sourcesTail:null,sinks:null,sinksTail:null,equals:J?.equals??k,error:void 0},B=()=>{if(V)x(W,V);if(I(W),W.error)throw W.error;return W.value},D=(K)=>{if(g$(z))return z.set(K);if(!w$(z))throw new P$(o);C(o,K,X),z.set(K)},M=(K)=>{C(o,K,M$),z=K,W.flags|=G;for(let P=W.sinks;P;P=P.nextSink)F(P.sink);if(O===0)w()};return{[Symbol.toStringTag]:o,configurable:!0,enumerable:!0,get:B,set:D,replace:M,current:()=>z}}function g$($){return Y($,o)}export{D$ as valueString,v as untrack,t$ as unown,i$ as match,p$ as isTask,x$ as isStore,V$ as isState,g$ as isSlot,M$ as isSignal,a$ as isSensor,E as isRecord,Y as isObjectOfType,w$ as isMutableSignal,S$ as isMemo,O$ as isList,g as isFunction,n as isEqual,XJ as isComputed,o$ as isCollection,l as isAsyncFunction,q$ as createTask,m$ as createStore,y as createState,ZJ as createSlot,JJ as createSignal,n$ as createSensor,d$ as createScope,zJ as createMutableSignal,B$ as createMemo,Q$ as createList,l$ as createEffect,$J as createComputed,r$ as createCollection,z$ as batch,i as UnsetSignalValueError,L$ as SKIP_EQUALITY,G$ as RequiredOwnerError,P$ as ReadonlySignalError,A$ as NullishSignalValueError,j$ as InvalidSignalValueError,b$ as InvalidCallbackError,Z$ as CircularDependencyError};
package/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @name Cause & Effect
3
- * @version 1.0.2
3
+ * @version 1.1.1
4
4
  * @author Esther Brunner
5
5
  */
6
6
 
@@ -41,6 +41,7 @@ export {
41
41
  export {
42
42
  createEffect,
43
43
  type MatchHandlers,
44
+ type SingleMatchHandlers,
44
45
  type MaybePromise,
45
46
  match,
46
47
  } from './src/nodes/effect'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeix/cause-effect",
3
- "version": "1.0.2",
3
+ "version": "1.1.1",
4
4
  "author": "Esther Brunner",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -8,10 +8,10 @@
8
8
  "types": "types/index.d.ts",
9
9
  "devDependencies": {
10
10
  "@biomejs/biome": "2.4.6",
11
- "bun-types": "latest",
11
+ "@types/bun": "latest",
12
12
  "mitata": "^1.0.34",
13
13
  "random": "^5.4.1",
14
- "typescript": "latest"
14
+ "typescript": "^6.0.2"
15
15
  },
16
16
  "peerDependencies": {
17
17
  "typescript": ">=5.8.0"
@@ -39,6 +39,20 @@ type MatchHandlers<T extends readonly Signal<unknown & {}>[]> = {
39
39
  nil?: () => MaybePromise<MaybeCleanup>
40
40
  }
41
41
 
42
+ /**
43
+ * Handlers for a single signal passed to `match()`.
44
+ *
45
+ * @template T - The value type of the signal being matched
46
+ */
47
+ type SingleMatchHandlers<T extends {}> = {
48
+ /** Called when the signal has a value. Receives the resolved value directly. */
49
+ ok: (value: T) => MaybePromise<MaybeCleanup>
50
+ /** Called when the signal holds an error. Receives the error directly. Defaults to `console.error`. */
51
+ err?: (error: Error) => MaybePromise<MaybeCleanup>
52
+ /** Called when the signal is unset (pending). */
53
+ nil?: () => MaybePromise<MaybeCleanup>
54
+ }
55
+
42
56
  /* === Exported Functions === */
43
57
 
44
58
  /**
@@ -96,6 +110,20 @@ function createEffect(fn: EffectCallback): Cleanup {
96
110
  return dispose
97
111
  }
98
112
 
113
+ /**
114
+ * Reads one or more signals and dispatches to the appropriate handler based on their state.
115
+ * Must be called within an active owner (effect or scope) so async cleanup can be registered.
116
+ *
117
+ * @since 1.1
118
+ * @param signal - A single signal to read.
119
+ * @param handlers - Object with an `ok` branch (receives the value directly) and optional `err` and `nil` branches.
120
+ * @returns An optional cleanup function if the active handler returns one.
121
+ * @throws RequiredOwnerError If called without an active owner.
122
+ */
123
+ function match<T extends {}>(
124
+ signal: Signal<T>,
125
+ handlers: SingleMatchHandlers<T>,
126
+ ): MaybeCleanup
99
127
  /**
100
128
  * Reads one or more signals and dispatches to the appropriate handler based on their state.
101
129
  * Must be called within an active owner (effect or scope) so async cleanup can be registered.
@@ -105,13 +133,41 @@ function createEffect(fn: EffectCallback): Cleanup {
105
133
  * @param handlers - Object with an `ok` branch and optional `err` and `nil` branches.
106
134
  * @returns An optional cleanup function if the active handler returns one.
107
135
  * @throws RequiredOwnerError If called without an active owner.
136
+ *
137
+ * @remarks
138
+ * **Async handlers are for external side effects only** — DOM mutations, analytics, logging,
139
+ * or any fire-and-forget API call whose result does not need to drive reactive state.
140
+ * Do not call `.set()` on a signal inside an async handler: use a `Task` node instead,
141
+ * which receives an `AbortSignal`, is auto-cancelled on re-run, and integrates cleanly
142
+ * with `nil` and `err` branches.
143
+ *
144
+ * Rejections from async handlers are always routed to `err`, including rejections from
145
+ * stale runs that were already superseded by a newer signal value. The library cannot
146
+ * cancel external operations it did not start.
108
147
  */
109
148
  function match<T extends readonly Signal<unknown & {}>[]>(
110
149
  signals: readonly [...T],
111
150
  handlers: MatchHandlers<T>,
151
+ ): MaybeCleanup
152
+ function match(
153
+ signalOrSignals: Signal<unknown & {}> | readonly Signal<unknown & {}>[],
154
+ // biome-ignore lint/suspicious/noExplicitAny: implementation overload, not part of the public API
155
+ handlers: any,
112
156
  ): MaybeCleanup {
113
157
  if (!activeOwner) throw new RequiredOwnerError('match')
114
- const { ok, err = console.error, nil } = handlers
158
+
159
+ const isSingle = !Array.isArray(signalOrSignals)
160
+ const signals = isSingle ? [signalOrSignals] : signalOrSignals
161
+
162
+ const { nil } = handlers
163
+ const ok = isSingle
164
+ ? (values: unknown[]) => handlers.ok(values[0])
165
+ : (values: unknown[]) => handlers.ok(values)
166
+ const err: (errors: readonly Error[]) => MaybePromise<MaybeCleanup> =
167
+ isSingle && handlers.err
168
+ ? (errors: readonly Error[]) => handlers.err(errors[0])
169
+ : (handlers.err ?? console.error)
170
+
115
171
  let errors: Error[] | undefined
116
172
  let pending = false
117
173
  const values = new Array(signals.length)
@@ -133,12 +189,7 @@ function match<T extends readonly Signal<unknown & {}>[]>(
133
189
  try {
134
190
  if (pending) out = nil?.()
135
191
  else if (errors) out = err(errors)
136
- else
137
- out = ok(
138
- values as {
139
- [K in keyof T]: T[K] extends Signal<infer V> ? V : never
140
- },
141
- )
192
+ else out = ok(values)
142
193
  } catch (e) {
143
194
  err([e instanceof Error ? e : new Error(String(e))])
144
195
  }
@@ -158,4 +209,10 @@ function match<T extends readonly Signal<unknown & {}>[]>(
158
209
  }
159
210
  }
160
211
 
161
- export { type MaybePromise, type MatchHandlers, createEffect, match }
212
+ export {
213
+ type MaybePromise,
214
+ type MatchHandlers,
215
+ type SingleMatchHandlers,
216
+ createEffect,
217
+ match,
218
+ }
package/src/nodes/slot.ts CHANGED
@@ -28,6 +28,9 @@ import { isObjectOfType } from '../util'
28
28
  * `get`, `set`, `configurable`, and `enumerable` are used by the property definition;
29
29
  * `replace()` and `current()` are kept on the slot object for integration-layer control.
30
30
  *
31
+ * Slots are not `MutableSignal`s: they are forwarding layers, not value owners.
32
+ * `set()` delegates to the backing signal; `update()` is intentionally absent.
33
+ *
31
34
  * @template T - The type of value held by the delegated signal.
32
35
  */
33
36
  type Slot<T extends {}> = {
@@ -51,10 +54,11 @@ type Slot<T extends {}> = {
51
54
  /**
52
55
  * Creates a slot signal that delegates its value to a swappable backing signal.
53
56
  *
54
- * A slot acts as a stable reactive source that can be used as a property descriptor
55
- * via `Object.defineProperty(target, key, slot)`. Subscribers link to the slot itself,
57
+ * A slot acts as a stable reactive source usable as a property descriptor via
58
+ * `Object.defineProperty(target, key, slot)`. Subscribers link to the slot itself,
56
59
  * so replacing the backing signal with `replace()` invalidates them without breaking
57
- * existing edges. Setter calls forward to the current backing signal when it is writable.
60
+ * existing edges. `set()` forwards to the current backing signal if it is writable;
61
+ * `update()` is absent — a slot is a forwarding layer, not a value owner.
58
62
  *
59
63
  * @since 0.18.3
60
64
  * @template T - The type of value held by the delegated signal.
@@ -93,10 +97,10 @@ function createSlot<T extends {}>(
93
97
  }
94
98
 
95
99
  const set = (next: T): void => {
100
+ if (isSlot(delegated)) return delegated.set(next)
96
101
  if (!isMutableSignal(delegated))
97
102
  throw new ReadonlySignalError(TYPE_SLOT)
98
103
  validateSignalValue(TYPE_SLOT, next, guard)
99
-
100
104
  delegated.set(next)
101
105
  }
102
106
 
@@ -475,6 +475,94 @@ describe('match', () => {
475
475
  expect(() => match([], { ok: () => {} })).toThrow(RequiredOwnerError)
476
476
  })
477
477
 
478
+ describe('Single-signal overload', () => {
479
+ test('should call ok with unwrapped value', () => {
480
+ const s = createState(42)
481
+ let result = 0
482
+ createEffect(() =>
483
+ match(s, {
484
+ ok: value => {
485
+ result = value
486
+ },
487
+ }),
488
+ )
489
+ expect(result).toBe(42)
490
+ s.set(99)
491
+ expect(result).toBe(99)
492
+ })
493
+
494
+ test('should call nil handler when signal is unset', async () => {
495
+ const task = createTask(async () => {
496
+ await wait(50)
497
+ return 42
498
+ })
499
+ let okCount = 0
500
+ let nilCount = 0
501
+ createEffect(() =>
502
+ match(task, {
503
+ ok: value => {
504
+ okCount++
505
+ expect(value).toBe(42)
506
+ },
507
+ nil: () => {
508
+ nilCount++
509
+ },
510
+ }),
511
+ )
512
+ expect(okCount).toBe(0)
513
+ expect(nilCount).toBe(1)
514
+ await wait(60)
515
+ expect(okCount).toBeGreaterThan(0)
516
+ expect(nilCount).toBe(1)
517
+ })
518
+
519
+ test('should call err with unwrapped Error', () => {
520
+ const a = createState(1)
521
+ const b = createMemo(() => {
522
+ if (a.get() > 5) throw new Error('Too high')
523
+ return a.get() * 2
524
+ })
525
+ let okCount = 0
526
+ let errCount = 0
527
+ createEffect(() =>
528
+ match(b, {
529
+ ok: () => {
530
+ okCount++
531
+ },
532
+ err: error => {
533
+ errCount++
534
+ expect(error.message).toBe('Too high')
535
+ },
536
+ }),
537
+ )
538
+ expect(okCount).toBe(1)
539
+ a.set(6)
540
+ expect(errCount).toBe(1)
541
+ a.set(3)
542
+ expect(okCount).toBe(2)
543
+ expect(errCount).toBe(1)
544
+ })
545
+
546
+ test('should fall back to console.error for single signal without err handler', () => {
547
+ const originalConsoleError = console.error
548
+ const mockConsoleError = mock(() => {})
549
+ console.error = mockConsoleError
550
+
551
+ try {
552
+ const a = createState(1)
553
+ const b = createMemo(() => {
554
+ if (a.get() > 5) throw new Error('Too high')
555
+ return a.get() * 2
556
+ })
557
+ createEffect(() => match(b, { ok: () => {} }))
558
+ a.set(6)
559
+ expect(mockConsoleError).toHaveBeenCalled()
560
+ } finally {
561
+ console.error = originalConsoleError
562
+ }
563
+ })
564
+ })
565
+
478
566
  test('should resolve multiple async tasks without waterfalls', async () => {
479
567
  const a = createTask(async () => {
480
568
  await wait(20)
package/test/slot.test.ts CHANGED
@@ -103,6 +103,25 @@ describe('Slot', () => {
103
103
  expect(runs).toBe(2)
104
104
  })
105
105
 
106
+ test('should forward set through a Slot-to-Slot chain', () => {
107
+ const source = createState(1)
108
+ const inner = createSlot(source)
109
+ const outer = createSlot(inner)
110
+
111
+ outer.set(42)
112
+ expect(source.get()).toBe(42)
113
+ expect(outer.get()).toBe(42)
114
+ })
115
+
116
+ test('should throw ReadonlySignalError when chain terminates in a read-only signal', () => {
117
+ const source = createState(2)
118
+ const readonly = createMemo(() => source.get() * 2)
119
+ const inner = createSlot(readonly)
120
+ const outer = createSlot(inner)
121
+
122
+ expect(() => outer.set(99)).toThrow('[Slot] Signal is read-only')
123
+ })
124
+
106
125
  test('should validate initial signal and replacement signal', () => {
107
126
  expect(() => {
108
127
  // @ts-expect-error: deliberate error test
package/types/index.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * @name Cause & Effect
3
- * @version 1.0.2
3
+ * @version 1.1.1
4
4
  * @author Esther Brunner
5
5
  */
6
6
  export { CircularDependencyError, type Guard, InvalidCallbackError, InvalidSignalValueError, NullishSignalValueError, ReadonlySignalError, RequiredOwnerError, UnsetSignalValueError, } from './src/errors';
7
7
  export { batch, type Cleanup, type ComputedOptions, createScope, type EffectCallback, type MaybeCleanup, type MemoCallback, type Signal, type SignalOptions, SKIP_EQUALITY, type TaskCallback, unown, untrack, } from './src/graph';
8
8
  export { type Collection, type CollectionCallback, type CollectionChanges, type CollectionOptions, createCollection, type DeriveCollectionCallback, isCollection, } from './src/nodes/collection';
9
- export { createEffect, type MatchHandlers, type MaybePromise, match, } from './src/nodes/effect';
9
+ export { createEffect, type MatchHandlers, type SingleMatchHandlers, type MaybePromise, match, } from './src/nodes/effect';
10
10
  export { createList, isEqual, isList, type KeyConfig, type List, type ListOptions, } from './src/nodes/list';
11
11
  export { createMemo, isMemo, type Memo } from './src/nodes/memo';
12
12
  export { createSensor, isSensor, type Sensor, type SensorCallback, type SensorOptions, } from './src/nodes/sensor';
@@ -16,6 +16,19 @@ type MatchHandlers<T extends readonly Signal<unknown & {}>[]> = {
16
16
  /** Called when one or more signals are unset (pending). */
17
17
  nil?: () => MaybePromise<MaybeCleanup>;
18
18
  };
19
+ /**
20
+ * Handlers for a single signal passed to `match()`.
21
+ *
22
+ * @template T - The value type of the signal being matched
23
+ */
24
+ type SingleMatchHandlers<T extends {}> = {
25
+ /** Called when the signal has a value. Receives the resolved value directly. */
26
+ ok: (value: T) => MaybePromise<MaybeCleanup>;
27
+ /** Called when the signal holds an error. Receives the error directly. Defaults to `console.error`. */
28
+ err?: (error: Error) => MaybePromise<MaybeCleanup>;
29
+ /** Called when the signal is unset (pending). */
30
+ nil?: () => MaybePromise<MaybeCleanup>;
31
+ };
19
32
  /**
20
33
  * Creates a reactive effect that automatically runs when its dependencies change.
21
34
  * Effects run immediately upon creation and re-run when any tracked signal changes.
@@ -46,6 +59,17 @@ type MatchHandlers<T extends readonly Signal<unknown & {}>[]> = {
46
59
  * ```
47
60
  */
48
61
  declare function createEffect(fn: EffectCallback): Cleanup;
62
+ /**
63
+ * Reads one or more signals and dispatches to the appropriate handler based on their state.
64
+ * Must be called within an active owner (effect or scope) so async cleanup can be registered.
65
+ *
66
+ * @since 1.1
67
+ * @param signal - A single signal to read.
68
+ * @param handlers - Object with an `ok` branch (receives the value directly) and optional `err` and `nil` branches.
69
+ * @returns An optional cleanup function if the active handler returns one.
70
+ * @throws RequiredOwnerError If called without an active owner.
71
+ */
72
+ declare function match<T extends {}>(signal: Signal<T>, handlers: SingleMatchHandlers<T>): MaybeCleanup;
49
73
  /**
50
74
  * Reads one or more signals and dispatches to the appropriate handler based on their state.
51
75
  * Must be called within an active owner (effect or scope) so async cleanup can be registered.
@@ -55,6 +79,17 @@ declare function createEffect(fn: EffectCallback): Cleanup;
55
79
  * @param handlers - Object with an `ok` branch and optional `err` and `nil` branches.
56
80
  * @returns An optional cleanup function if the active handler returns one.
57
81
  * @throws RequiredOwnerError If called without an active owner.
82
+ *
83
+ * @remarks
84
+ * **Async handlers are for external side effects only** — DOM mutations, analytics, logging,
85
+ * or any fire-and-forget API call whose result does not need to drive reactive state.
86
+ * Do not call `.set()` on a signal inside an async handler: use a `Task` node instead,
87
+ * which receives an `AbortSignal`, is auto-cancelled on re-run, and integrates cleanly
88
+ * with `nil` and `err` branches.
89
+ *
90
+ * Rejections from async handlers are always routed to `err`, including rejections from
91
+ * stale runs that were already superseded by a newer signal value. The library cannot
92
+ * cancel external operations it did not start.
58
93
  */
59
94
  declare function match<T extends readonly Signal<unknown & {}>[]>(signals: readonly [...T], handlers: MatchHandlers<T>): MaybeCleanup;
60
- export { type MaybePromise, type MatchHandlers, createEffect, match };
95
+ export { type MaybePromise, type MatchHandlers, type SingleMatchHandlers, createEffect, match, };
@@ -8,6 +8,9 @@ import { type Signal, type SignalOptions } from '../graph';
8
8
  * `get`, `set`, `configurable`, and `enumerable` are used by the property definition;
9
9
  * `replace()` and `current()` are kept on the slot object for integration-layer control.
10
10
  *
11
+ * Slots are not `MutableSignal`s: they are forwarding layers, not value owners.
12
+ * `set()` delegates to the backing signal; `update()` is intentionally absent.
13
+ *
11
14
  * @template T - The type of value held by the delegated signal.
12
15
  */
13
16
  type Slot<T extends {}> = {
@@ -28,10 +31,11 @@ type Slot<T extends {}> = {
28
31
  /**
29
32
  * Creates a slot signal that delegates its value to a swappable backing signal.
30
33
  *
31
- * A slot acts as a stable reactive source that can be used as a property descriptor
32
- * via `Object.defineProperty(target, key, slot)`. Subscribers link to the slot itself,
34
+ * A slot acts as a stable reactive source usable as a property descriptor via
35
+ * `Object.defineProperty(target, key, slot)`. Subscribers link to the slot itself,
33
36
  * so replacing the backing signal with `replace()` invalidates them without breaking
34
- * existing edges. Setter calls forward to the current backing signal when it is writable.
37
+ * existing edges. `set()` forwards to the current backing signal if it is writable;
38
+ * `update()` is absent — a slot is a forwarding layer, not a value owner.
35
39
  *
36
40
  * @since 0.18.3
37
41
  * @template T - The type of value held by the delegated signal.
File without changes