@zeix/cause-effect 1.0.0 → 1.0.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 (47) hide show
  1. package/.github/copilot-instructions.md +2 -1
  2. package/.zed/settings.json +24 -1
  3. package/ARCHITECTURE.md +1 -1
  4. package/CHANGELOG.md +15 -0
  5. package/README.md +41 -0
  6. package/REQUIREMENTS.md +3 -3
  7. package/eslint.config.js +2 -1
  8. package/package.json +5 -4
  9. package/skills/cause-effect/SKILL.md +69 -0
  10. package/skills/cause-effect/agents/openai.yaml +4 -0
  11. package/skills/cause-effect/references/api-facts.md +179 -0
  12. package/skills/cause-effect/references/error-classes.md +153 -0
  13. package/skills/cause-effect/references/non-obvious-behaviors.md +173 -0
  14. package/skills/cause-effect/references/signal-types.md +288 -0
  15. package/skills/cause-effect/workflows/answer-question.md +54 -0
  16. package/skills/cause-effect/workflows/debug.md +71 -0
  17. package/skills/cause-effect/workflows/use-api.md +63 -0
  18. package/skills/cause-effect-dev/SKILL.md +61 -100
  19. package/skills/cause-effect-dev/references/api-facts.md +96 -0
  20. package/skills/cause-effect-dev/references/error-classes.md +97 -0
  21. package/skills/cause-effect-dev/references/internal-types.md +54 -0
  22. package/skills/cause-effect-dev/references/non-obvious-behaviors.md +146 -0
  23. package/skills/cause-effect-dev/references/source-map.md +45 -0
  24. package/skills/cause-effect-dev/workflows/answer-question.md +55 -0
  25. package/skills/cause-effect-dev/workflows/fix-bug.md +63 -0
  26. package/skills/cause-effect-dev/workflows/implement-feature.md +46 -0
  27. package/skills/cause-effect-dev/workflows/write-tests.md +64 -0
  28. package/skills/changelog-keeper/SKILL.md +47 -37
  29. package/skills/tech-writer/SKILL.md +94 -0
  30. package/skills/tech-writer/references/document-map.md +199 -0
  31. package/skills/tech-writer/references/tone-guide.md +189 -0
  32. package/skills/tech-writer/workflows/consistency-review.md +98 -0
  33. package/skills/tech-writer/workflows/update-after-change.md +65 -0
  34. package/skills/tech-writer/workflows/update-agent-docs.md +77 -0
  35. package/skills/tech-writer/workflows/update-architecture.md +61 -0
  36. package/skills/tech-writer/workflows/update-jsdoc.md +72 -0
  37. package/skills/tech-writer/workflows/update-public-api.md +59 -0
  38. package/skills/tech-writer/workflows/update-requirements.md +80 -0
  39. package/src/graph.ts +2 -0
  40. package/src/nodes/collection.ts +38 -0
  41. package/src/nodes/effect.ts +13 -1
  42. package/src/nodes/list.ts +23 -2
  43. package/src/nodes/memo.ts +0 -1
  44. package/src/nodes/sensor.ts +10 -4
  45. package/src/nodes/store.ts +11 -0
  46. package/src/signal.ts +6 -0
  47. package/tsconfig.json +9 -0
@@ -0,0 +1,80 @@
1
+ <required_reading>
2
+ 1. references/document-map.md — REQUIREMENTS.md section
3
+ 2. references/tone-guide.md — requirements tone rules
4
+ </required_reading>
5
+
6
+ <process>
7
+ ## Step 1: Confirm the update is warranted
8
+
9
+ REQUIREMENTS.md captures the library's vision, audience, design principles, constraints,
10
+ and non-goals. It is intentionally written to survive version bumps. Do NOT update it for:
11
+
12
+ - Bug fixes or internal refactors
13
+ - New options on existing signal types
14
+ - Changed implementation details
15
+ - Documentation or tooling changes
16
+
17
+ Only update REQUIREMENTS.md when one of these applies:
18
+
19
+ | Trigger | Section affected |
20
+ |---|---|
21
+ | A new signal type is added to the library | "Design Principles → Minimal Surface, Maximum Coverage" table |
22
+ | A signal type is removed | Same table + "Non-Goals" if it was previously out of scope |
23
+ | A runtime environment is added or dropped | "Runtime Environments" |
24
+ | A bundle size target changes | "Size and Performance Constraints" |
25
+ | A design principle is added, changed, or removed | "Design Principles" |
26
+ | The primary or secondary audience shifts | "Audience" |
27
+ | A non-goal is resolved or a new one is added | "Non-Goals" |
28
+ | Stability guarantees change | "Stability" |
29
+
30
+ If the change does not meet any of these criteria, stop and explain why REQUIREMENTS.md
31
+ does not need updating. Do not edit it speculatively.
32
+
33
+ ## Step 2: Read the full document
34
+
35
+ Read `REQUIREMENTS.md` in full before touching anything. Identify the single section
36
+ (or at most two) that the change affects. Changes to REQUIREMENTS.md are almost always
37
+ one table row, one list item, or one short paragraph — rarely more.
38
+
39
+ ## Step 3: Make the minimal edit
40
+
41
+ Make only the change the trigger warrants:
42
+
43
+ **Signal type table (most common):**
44
+ Add or remove a row. The row format is:
45
+ `| **Type** | Role description | Data structure |`
46
+ Keep the role description to one short phrase. The table reflects the final state
47
+ of the type set — it is not a changelog.
48
+
49
+ **Runtime environments:**
50
+ Add or remove the environment name from the prose list. One line.
51
+
52
+ **Bundle size targets:**
53
+ Update the number in the table. If the target changed for a reason that future readers
54
+ should understand, add one sentence of rationale — no more.
55
+
56
+ **Non-goals:**
57
+ Each non-goal is a bold heading followed by one sentence. Do not expand them into
58
+ paragraphs. If adding a new non-goal, follow the same pattern. If resolving one,
59
+ remove it entirely — do not mark it as resolved inline.
60
+
61
+ **Stability section:**
62
+ Update only the factual claims (version number, breaking change expectations). Preserve
63
+ the formal register of the surrounding text.
64
+
65
+ ## Step 4: Verify internal consistency
66
+
67
+ After editing, confirm:
68
+ - The signal type table count matches the count stated in the prose ("9 signal types").
69
+ - The "Non-Goals" section does not contradict anything now present in the codebase.
70
+ - The "Stability" section accurately describes the current version and compatibility stance.
71
+ </process>
72
+
73
+ <success_criteria>
74
+ - Update was warranted by one of the listed triggers
75
+ - Only the affected section was changed — no rewrites of accurate content
76
+ - Signal type table count consistent with prose and with `index.ts`
77
+ - Formal, strategic tone preserved throughout per references/tone-guide.md
78
+ - No changelog-style language ("previously", "now", "we changed") — state the current
79
+ truth only
80
+ </success_criteria>
package/src/graph.ts CHANGED
@@ -590,6 +590,8 @@ function createScope(fn: () => MaybeCleanup): Cleanup {
590
590
  * reactive graph.
591
591
  *
592
592
  * @since 0.18.5
593
+ * @param fn - The function to execute without an active owner
594
+ * @returns The return value of `fn`
593
595
  */
594
596
  function unown<T>(fn: () => T): T {
595
597
  const prev = activeOwner
@@ -36,10 +36,24 @@ import { createTask } from './task'
36
36
 
37
37
  type CollectionSource<T extends {}> = List<T> | Collection<T>
38
38
 
39
+ /**
40
+ * Transformation callback for `deriveCollection` — sync or async.
41
+ * Sync callbacks produce a `Memo<T>` per item; async callbacks produce a `Task<T>`
42
+ * with automatic cancellation when the source item changes.
43
+ *
44
+ * @template T - The type of derived items
45
+ * @template U - The type of source items
46
+ */
39
47
  type DeriveCollectionCallback<T extends {}, U extends {}> =
40
48
  | ((sourceValue: U) => T)
41
49
  | ((sourceValue: U, abort: AbortSignal) => Promise<T>)
42
50
 
51
+ /**
52
+ * A read-only reactive keyed collection with per-item reactivity.
53
+ * Created by `createCollection` (externally driven) or via `.deriveCollection()` on a `List` or `Collection`.
54
+ *
55
+ * @template T - The type of items in the collection
56
+ */
43
57
  type Collection<T extends {}> = {
44
58
  readonly [Symbol.toStringTag]: 'Collection'
45
59
  readonly [Symbol.isConcatSpreadable]: true
@@ -59,18 +73,42 @@ type Collection<T extends {}> = {
59
73
  readonly length: number
60
74
  }
61
75
 
76
+ /**
77
+ * Granular mutation descriptor passed to the `applyChanges` callback inside a `CollectionCallback`.
78
+ *
79
+ * @template T - The type of items in the collection
80
+ */
62
81
  type CollectionChanges<T> = {
82
+ /** Items to add. Each item is assigned a new key via the configured `keyConfig`. */
63
83
  add?: T[]
84
+ /** Items whose values have changed. Matched to existing entries by key. */
64
85
  change?: T[]
86
+ /** Items to remove. Matched to existing entries by key. */
65
87
  remove?: T[]
66
88
  }
67
89
 
90
+ /**
91
+ * Configuration options for `createCollection`.
92
+ *
93
+ * @template T - The type of items in the collection
94
+ */
68
95
  type CollectionOptions<T extends {}> = {
96
+ /** Initial items. Defaults to `[]`. */
69
97
  value?: T[]
98
+ /** Key generation strategy. See `KeyConfig`. Defaults to auto-increment. */
70
99
  keyConfig?: KeyConfig<T>
100
+ /** Factory for per-item signals. Defaults to `createState`. */
71
101
  createItem?: (value: T) => Signal<T>
72
102
  }
73
103
 
104
+ /**
105
+ * Setup callback for `createCollection`. Invoked when the collection gains its first downstream
106
+ * subscriber; receives an `applyChanges` function to push granular mutations into the graph.
107
+ *
108
+ * @template T - The type of items in the collection
109
+ * @param apply - Call with a `CollectionChanges` object to add, update, or remove items
110
+ * @returns A cleanup function invoked when the collection loses all subscribers
111
+ */
74
112
  type CollectionCallback<T extends {}> = (
75
113
  apply: (changes: CollectionChanges<T>) => void,
76
114
  ) => Cleanup
@@ -20,13 +20,22 @@ import {
20
20
 
21
21
  /* === Types === */
22
22
 
23
+ /** A value that is either synchronous or a `Promise` — used for handler return types in `match()`. */
23
24
  type MaybePromise<T> = T | Promise<T>
24
25
 
26
+ /**
27
+ * Handlers for all states of one or more signals passed to `match()`.
28
+ *
29
+ * @template T - Tuple of `Signal` types being matched
30
+ */
25
31
  type MatchHandlers<T extends readonly Signal<unknown & {}>[]> = {
32
+ /** Called when all signals have a value. Receives a tuple of resolved values. */
26
33
  ok: (values: {
27
34
  [K in keyof T]: T[K] extends Signal<infer V> ? V : never
28
35
  }) => MaybePromise<MaybeCleanup>
36
+ /** Called when one or more signals hold an error. Defaults to `console.error`. */
29
37
  err?: (errors: readonly Error[]) => MaybePromise<MaybeCleanup>
38
+ /** Called when one or more signals are unset (pending). */
30
39
  nil?: () => MaybePromise<MaybeCleanup>
31
40
  }
32
41
 
@@ -88,10 +97,13 @@ function createEffect(fn: EffectCallback): Cleanup {
88
97
  }
89
98
 
90
99
  /**
91
- * Runs handlers based on the current values of signals.
100
+ * Reads one or more signals and dispatches to the appropriate handler based on their state.
92
101
  * Must be called within an active owner (effect or scope) so async cleanup can be registered.
93
102
  *
94
103
  * @since 0.15.0
104
+ * @param signals - Tuple of signals to read; all must have a value for `ok` to run.
105
+ * @param handlers - Object with an `ok` branch and optional `err` and `nil` branches.
106
+ * @returns An optional cleanup function if the active handler returns one.
95
107
  * @throws RequiredOwnerError If called without an active owner.
96
108
  */
97
109
  function match<T extends readonly Signal<unknown & {}>[]>(
package/src/nodes/list.ts CHANGED
@@ -40,13 +40,33 @@ type DiffResult = {
40
40
  remove: UnknownRecord
41
41
  }
42
42
 
43
+ /**
44
+ * Key generation strategy for `createList` items.
45
+ * A string value is used as a prefix for auto-incremented keys (`prefix0`, `prefix1`, …).
46
+ * A function receives each item and returns a stable string key, or `undefined` to fall back to auto-increment.
47
+ *
48
+ * @template T - The type of items in the list
49
+ */
43
50
  type KeyConfig<T> = string | ((item: T) => string | undefined)
44
51
 
52
+ /**
53
+ * Configuration options for `createList`.
54
+ *
55
+ * @template T - The type of items in the list
56
+ */
45
57
  type ListOptions<T extends {}> = {
58
+ /** Key generation strategy. A string prefix or a function `(item) => string | undefined`. Defaults to auto-increment. */
46
59
  keyConfig?: KeyConfig<T>
60
+ /** Lifecycle callback invoked when the list gains its first downstream subscriber. Must return a cleanup function. */
47
61
  watched?: () => Cleanup
48
62
  }
49
63
 
64
+ /**
65
+ * A reactive ordered array with stable keys and per-item reactivity.
66
+ * Each item is a `State<T>` signal; structural changes (add/remove/sort) propagate reactively.
67
+ *
68
+ * @template T - The type of items in the list
69
+ */
50
70
  type List<T extends {}> = {
51
71
  readonly [Symbol.toStringTag]: 'List'
52
72
  readonly [Symbol.isConcatSpreadable]: true
@@ -240,8 +260,9 @@ function diffArrays<T>(
240
260
  *
241
261
  * @since 0.18.0
242
262
  * @param value - Initial array of items
243
- * @param options - Optional configuration for key generation and watch lifecycle
244
- * @returns A List signal
263
+ * @param options.keyConfig - Key generation strategy: string prefix or `(item) => string | undefined`. Defaults to auto-increment.
264
+ * @param options.watched - Lifecycle callback invoked on first subscriber; must return a cleanup function called on last unsubscribe.
265
+ * @returns A `List` signal with reactive per-item `State` signals
245
266
  */
246
267
  function createList<T extends {}>(
247
268
  value: T[],
package/src/nodes/memo.ts CHANGED
@@ -36,7 +36,6 @@ type Memo<T extends {}> = {
36
36
  * Recomputes if dependencies have changed since last access.
37
37
  * When called inside another reactive context, creates a dependency.
38
38
  * @returns The computed value
39
- * @throws UnsetSignalValueError If the memo value is still unset when read.
40
39
  */
41
40
  get(): T
42
41
  }
@@ -35,11 +35,9 @@ type Sensor<T extends {}> = {
35
35
  }
36
36
 
37
37
  /**
38
- * A callback function for sensors when the sensor starts being watched.
38
+ * Configuration options for `createSensor`.
39
39
  *
40
- * @template T - The type of value observed
41
- * @param set - A function to set the observed value
42
- * @returns A cleanup function when the sensor stops being watched
40
+ * @template T - The type of value produced by the sensor
43
41
  */
44
42
  type SensorOptions<T extends {}> = SignalOptions<T> & {
45
43
  /**
@@ -49,6 +47,14 @@ type SensorOptions<T extends {}> = SignalOptions<T> & {
49
47
  value?: T
50
48
  }
51
49
 
50
+ /**
51
+ * Setup callback for `createSensor`. Invoked when the sensor gains its first downstream
52
+ * subscriber; receives a `set` function to push new values into the graph.
53
+ *
54
+ * @template T - The type of value produced by the sensor
55
+ * @param set - Updates the sensor value and propagates the change to subscribers
56
+ * @returns A cleanup function invoked when the sensor loses all subscribers
57
+ */
52
58
  type SensorCallback<T extends {}> = (set: (next: T) => void) => Cleanup
53
59
 
54
60
  /* === Exported Functions === */
@@ -28,7 +28,11 @@ import { createState, type State } from './state'
28
28
 
29
29
  /* === Types === */
30
30
 
31
+ /**
32
+ * Configuration options for `createStore`.
33
+ */
31
34
  type StoreOptions = {
35
+ /** Invoked when the store gains its first downstream subscriber; returns a cleanup called when the last one unsubscribes. */
32
36
  watched?: () => Cleanup
33
37
  }
34
38
 
@@ -58,6 +62,13 @@ type BaseStore<T extends UnknownRecord> = {
58
62
  remove(key: string): void
59
63
  }
60
64
 
65
+ /**
66
+ * A reactive object with per-property reactivity.
67
+ * Each property is wrapped as a `State`, nested `Store`, or `List` signal, accessible directly via proxy.
68
+ * Updating one property only re-runs effects that read that property.
69
+ *
70
+ * @template T - The plain-object type whose properties become reactive signals
71
+ */
61
72
  type Store<T extends UnknownRecord> = BaseStore<T> & {
62
73
  [K in keyof T]: T[K] extends readonly (infer U extends {})[]
63
74
  ? List<U>
package/src/signal.ts CHANGED
@@ -22,6 +22,12 @@ import { isAsyncFunction, isFunction, isRecord, isUniformArray } from './util'
22
22
 
23
23
  /* === Types === */
24
24
 
25
+ /**
26
+ * A readable and writable signal — the type union of `State`, `Store`, and `List`.
27
+ * Use as a parameter type for generic code that accepts any writable signal.
28
+ *
29
+ * @template T - The type of value held by the signal
30
+ */
25
31
  type MutableSignal<T extends {}> = {
26
32
  get(): T
27
33
  set(value: T): void
package/tsconfig.json CHANGED
@@ -12,6 +12,10 @@
12
12
  "moduleResolution": "bundler",
13
13
  "allowImportingTsExtensions": true,
14
14
  "verbatimModuleSyntax": true,
15
+ "erasableSyntaxOnly": true,
16
+ "isolatedModules": true,
17
+ "resolveJsonModule": true,
18
+ "types": ["bun-types"],
15
19
 
16
20
  // Editor-only mode - no emit
17
21
  "noEmit": true,
@@ -24,11 +28,16 @@
24
28
  "useUnknownInCatchVariables": true,
25
29
  "noUncheckedSideEffectImports": true,
26
30
  "noFallthroughCasesInSwitch": true,
31
+ "forceConsistentCasingInFileNames": true,
27
32
 
28
33
  // Some stricter flags (disabled by default)
29
34
  "noUnusedLocals": false,
30
35
  "noUnusedParameters": false,
31
36
  "noPropertyAccessFromIndexSignature": false,
37
+
38
+ /* Performance */
39
+ "incremental": true,
40
+ "tsBuildInfoFile": "./.tsbuildinfo",
32
41
  },
33
42
  "include": ["./**/*.ts"],
34
43
  "exclude": ["node_modules", "types", "index.js"],