@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.
- package/.github/copilot-instructions.md +2 -1
- package/.zed/settings.json +24 -1
- package/ARCHITECTURE.md +1 -1
- package/CHANGELOG.md +15 -0
- package/README.md +41 -0
- package/REQUIREMENTS.md +3 -3
- package/eslint.config.js +2 -1
- package/package.json +5 -4
- package/skills/cause-effect/SKILL.md +69 -0
- package/skills/cause-effect/agents/openai.yaml +4 -0
- package/skills/cause-effect/references/api-facts.md +179 -0
- package/skills/cause-effect/references/error-classes.md +153 -0
- package/skills/cause-effect/references/non-obvious-behaviors.md +173 -0
- package/skills/cause-effect/references/signal-types.md +288 -0
- package/skills/cause-effect/workflows/answer-question.md +54 -0
- package/skills/cause-effect/workflows/debug.md +71 -0
- package/skills/cause-effect/workflows/use-api.md +63 -0
- package/skills/cause-effect-dev/SKILL.md +61 -100
- package/skills/cause-effect-dev/references/api-facts.md +96 -0
- package/skills/cause-effect-dev/references/error-classes.md +97 -0
- package/skills/cause-effect-dev/references/internal-types.md +54 -0
- package/skills/cause-effect-dev/references/non-obvious-behaviors.md +146 -0
- package/skills/cause-effect-dev/references/source-map.md +45 -0
- package/skills/cause-effect-dev/workflows/answer-question.md +55 -0
- package/skills/cause-effect-dev/workflows/fix-bug.md +63 -0
- package/skills/cause-effect-dev/workflows/implement-feature.md +46 -0
- package/skills/cause-effect-dev/workflows/write-tests.md +64 -0
- package/skills/changelog-keeper/SKILL.md +47 -37
- package/skills/tech-writer/SKILL.md +94 -0
- package/skills/tech-writer/references/document-map.md +199 -0
- package/skills/tech-writer/references/tone-guide.md +189 -0
- package/skills/tech-writer/workflows/consistency-review.md +98 -0
- package/skills/tech-writer/workflows/update-after-change.md +65 -0
- package/skills/tech-writer/workflows/update-agent-docs.md +77 -0
- package/skills/tech-writer/workflows/update-architecture.md +61 -0
- package/skills/tech-writer/workflows/update-jsdoc.md +72 -0
- package/skills/tech-writer/workflows/update-public-api.md +59 -0
- package/skills/tech-writer/workflows/update-requirements.md +80 -0
- package/src/graph.ts +2 -0
- package/src/nodes/collection.ts +38 -0
- package/src/nodes/effect.ts +13 -1
- package/src/nodes/list.ts +23 -2
- package/src/nodes/memo.ts +0 -1
- package/src/nodes/sensor.ts +10 -4
- package/src/nodes/store.ts +11 -0
- package/src/signal.ts +6 -0
- 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
|
package/src/nodes/collection.ts
CHANGED
|
@@ -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
|
package/src/nodes/effect.ts
CHANGED
|
@@ -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
|
-
*
|
|
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 -
|
|
244
|
-
* @
|
|
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
|
}
|
package/src/nodes/sensor.ts
CHANGED
|
@@ -35,11 +35,9 @@ type Sensor<T extends {}> = {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
38
|
+
* Configuration options for `createSensor`.
|
|
39
39
|
*
|
|
40
|
-
* @template T - The type of value
|
|
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 === */
|
package/src/nodes/store.ts
CHANGED
|
@@ -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"],
|