Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
be52f34
Starting signals implementation
soxtoby Feb 28, 2026
36be095
Events
soxtoby Feb 28, 2026
257b56e
Reductions
soxtoby Feb 28, 2026
7de2ff6
Ownership for reductions
soxtoby Mar 1, 2026
a4e1785
Ownership refactoring
soxtoby Mar 1, 2026
f97310e
Much simpler tracking
soxtoby Mar 1, 2026
9dab4dc
Breaking up signals.ts some more and adding reduce and derive functions
soxtoby Mar 2, 2026
23d5c76
Minor cleanup
soxtoby Mar 3, 2026
58e0742
Basic derived state
soxtoby Mar 3, 2026
2d4c3ff
Model decorators
soxtoby Mar 3, 2026
1f59b6c
Reductions update themselves if accessed during another reduction
soxtoby Mar 3, 2026
dda97ef
Moving event map/filter/filterMap into SourceBase
soxtoby Mar 3, 2026
974276c
Event scoping
soxtoby Mar 4, 2026
a7bd6a0
Tracking refactor
soxtoby Mar 6, 2026
b84b4df
Hooks
soxtoby Mar 7, 2026
190b1a2
Rendering
soxtoby Mar 7, 2026
97db5dc
Building paths to nodes
soxtoby Mar 7, 2026
260a1bf
More logging
soxtoby Mar 7, 2026
37f4759
Hooking tests up to signals implementation
soxtoby Mar 9, 2026
ef079ed
Getting Cleanup tests working with signals implementation
soxtoby Mar 11, 2026
eaf7246
Getting Derivation tests working with signals implementation
soxtoby Mar 12, 2026
17f02f7
Better map/filter/filterMap typing
soxtoby Mar 12, 2026
6b4400d
Getting event tests working
soxtoby Mar 13, 2026
ac976c2
Back to shallow scoping for simpler typing
soxtoby Mar 13, 2026
efad5c2
Getting Model tests working with signals implementation
soxtoby Mar 15, 2026
e84e496
Getting events decorator tests working
soxtoby Mar 16, 2026
c93f51d
Merging accessor tests back into Model tests and getting them working
soxtoby Mar 16, 2026
ac0881a
Getting Reduction tests working
soxtoby Mar 16, 2026
bdfc9a7
Getting hooks and rendering tests working
soxtoby Mar 17, 2026
18a1745
Getting State tests working
soxtoby Mar 20, 2026
eccef04
Getting Testing tests working
soxtoby Mar 21, 2026
1fcfda3
Getting example working
soxtoby Mar 21, 2026
5802b7d
Updating legacy tests
soxtoby Mar 22, 2026
dd21cdc
Cleanup
soxtoby Mar 22, 2026
9b7d639
Reworking package and module references
soxtoby Mar 27, 2026
70c013f
Removing bogus rendering test
soxtoby Mar 27, 2026
8250999
Updating packages
soxtoby Mar 27, 2026
c55069d
Updating old observable tests
soxtoby Mar 28, 2026
f3ada14
Replacing old implementation with new signals-based implementation
soxtoby Mar 28, 2026
eaa7a2f
Merge function
soxtoby Mar 29, 2026
a555606
Changing export paths back to /lib/ for backwards compatibility with …
soxtoby Apr 1, 2026
ebe92f3
useModel returns the same type created by callback
soxtoby Apr 1, 2026
9d26ebe
useObservedProps updates props in useEffect to avoid side effects dur…
soxtoby Apr 1, 2026
bcdac6c
Handling context components when logging react elements
soxtoby Apr 1, 2026
e5f12f6
Supressing logging when accessing toJSON derivations
soxtoby Apr 1, 2026
6e6ced2
Atom doesn't trigger a transition when constructed
soxtoby Apr 1, 2026
0794941
Catching exceptions when logging
soxtoby Apr 1, 2026
43b717f
Better typing for AsyncValues
soxtoby Apr 1, 2026
09104fa
Logging async start event with same format as other events
soxtoby Apr 1, 2026
5f35846
Reordering some overloads so they get picked up properly
soxtoby Apr 1, 2026
2e401b4
Fixes for legacy decorators
soxtoby Apr 1, 2026
6ab428f
Fixing model property binding for subclasses of regular sources
soxtoby Apr 1, 2026
dda1b38
Exporting BoundSource so it can be extended
soxtoby Apr 1, 2026
cf90c62
Non-object state properties no longer cause errors
soxtoby Apr 1, 2026
4149dc4
More instructions for UntrackedSourceError
soxtoby Apr 1, 2026
eb536cc
Fix for property initializers accessing sub-class properties
soxtoby Apr 5, 2026
e450e96
Throwing when model decorator is missing
soxtoby Apr 5, 2026
87b0a09
model decorator doesn't override existing toJSON method
soxtoby Apr 8, 2026
236c7c8
filterContext method on async events for old filter behavior where he…
soxtoby May 2, 2026
692a410
Phased event helpers
soxtoby May 2, 2026
6900dce
Splitting tracking into tracking and ownership
soxtoby May 4, 2026
689a57e
Renaming Source to Observable and Atom to ObservableValue
soxtoby May 6, 2026
f430a04
Extracting event and model symbols out to separate module to break cy…
soxtoby May 7, 2026
aa01723
Updating isModel imports in event-reduce-react
soxtoby May 13, 2026
5ad527a
Splitting trackSourceAccessed into start and stop functions to avoid …
soxtoby May 13, 2026
ce076a1
Optimizing circular dependency detection in AccessTracker
soxtoby May 13, 2026
018ecac
RenderEffect uses simple version as trigger state in production builds
soxtoby May 13, 2026
6cb3fc5
disposeGarbage doesn't resolve entire node path unless logging is ena…
soxtoby May 13, 2026
784dcc1
Improved merge typing
soxtoby May 18, 2026
aae4437
Optimize core update paths
soxtoby Jun 13, 2026
555994d
Coalesce React render invalidations
soxtoby Jun 13, 2026
57ca366
Optimizing Effect.isStale
soxtoby May 20, 2026
3ad9767
Optimizing updateChildren
soxtoby May 20, 2026
583c3c1
Removing useObservedProps and replacing useAtom with useObservableValue
soxtoby Jun 13, 2026
14b58e5
Some minor cleanup
soxtoby Jun 13, 2026
0601c3d
Renaming ObservableValue to Signal and adding signal factory function
soxtoby Jun 24, 2026
219fb33
Sending model state to dev tools after transition
soxtoby Jun 24, 2026
9e75649
Adding back valueChanged function
soxtoby Jun 24, 2026
b753b7d
Exporting functions for getting full state from models
soxtoby Jun 24, 2026
bc58279
Utils cleanup
soxtoby Jun 26, 2026
b3b5fc4
Bumping version
soxtoby Jun 26, 2026
8e8f5f0
Updating packages
soxtoby Jun 26, 2026
e1e4661
More cleanup
soxtoby Jun 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:

steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
Expand Down
20 changes: 17 additions & 3 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"version": "2.0.0",
"tasks": [
{
"type": "typescript",
"tsconfig": "tsconfig.json",
"type": "bun",
"script": "prepare",
"label": "Compile TypeScript",
"problemMatcher": [
"$tsc"
Expand All @@ -29,6 +29,20 @@
"kind": "test",
"isDefault": true
}
},
{
"type": "bun",
"script": "run test:modern",
"problemMatcher": [],
"label": "Run modern tests",
"group": "test"
},
{
"type": "shell",
"command": "bun run test:legacy",
"problemMatcher": [],
"label": "Run legacy tests",
"group": "test"
}
]
}
}
96 changes: 96 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Event Reduce

Event Reduce models state as values computed from observable event streams and observable state.

## Language

**Observable**:
A value-producing node that can be observed and composed.
_Avoid_: Source when naming the node itself.

**Source**:
The relationship role of an **Observable** used to compute another **Observable**.
_Avoid_: Observable source as a type name.

**ObservableValue**:
An **Observable** that stores a mutable value.
_Avoid_: Atom.

**State**:
A non-observable value that is included in a model's state.
_Avoid_: ObservableValue.

## Relationships

- An **Observable** may have zero or more **Sources**.
- A **Source** is always an **Observable**.
- An **ObservableValue** is an **Observable**.
- **State** is not an **ObservableValue**.
- APIs that describe dependency relationships should use "source" language.
- Observer callbacks should use "source" language when the changed **Observable** is one of the observer's dependencies.
- Effects should use "source" language for observables they read and for the observable that invalidates them.
- Modules that define observable node types should use "observable" language, not "source" language.
- `Source` and `ISource` should not remain as aliases after the rename; they preserve branch-local drift, not compatibility.
- Specialized observable classes should use "Observable" in the class name.
- Type aliases and interfaces should use "Observable" when they name an observable node concept.
- Async event interfaces should use "Observable" when naming the event stream itself or its phase streams.
- Event functions keep event-function naming even though they are observable.
- Async events keep async-event naming; do not rename `IAsyncEvent`.
- Variables should use "source" only when the value is acting as a dependency of another **Observable**.
- Transition internals such as `iterationSource` may keep existing names when they are not part of the public naming cleanup.
- Error class names should use "Observable" when they name the observable node concept.
- Error properties should use `source` only when the property identifies the source or trigger of the error.
- Helpers that return the observable node behind a value should use "Observable" language.
- Code comments and tests should use "observable" for user-facing streams and "source" only for dependency roles; the README is out of scope for this branch cleanup.
- Public helper parameters should use "observable" language when callers pass observable nodes directly.
- Dependency graph access uses `getSources()` returning an iterable; restoring master's `sources` property is not required.
- Observable implementations should expose dependency graph access through `getSources()`, not public mutable `sources` sets.
- Observer/effect implementations should not expose public mutable source sets.
- Subscription uses branch-style observer objects; restoring master's callback subscription is not required.
- `IObserver` remains the counterpart to `IObservable`; source-specific behavior belongs in method names.
- `IObserver` should remain minimal and should not require source introspection.
- ObservableValue uses branch-style mutation mechanics; restoring master's old helpers is not required.

## Example dialogue

> **Dev:** "Should this type be called Source?"
> **Domain expert:** "No. It is an Observable. It is only a source when another Observable depends on it."

## Flagged ambiguities

- "source" was used on this branch both as the observable node type and as a dependency role; resolved: restore the master-compatible public type name **Observable**, and reserve **Source** for the dependency role.
- `getSources` and `sourceUpdated` are relationship names, not type names.
- `Effect.sources` and `Effect.invalidatingSource` are dependency-role names.
- `Effect` should keep source terminology internally but not expose a public mutable `sources` set.
- `Reduction` should keep source terminology internally but expose dependencies through `getSources()`.
- The module defining observable node concepts should keep the master-compatible `observable` name.
- The bound transform implementation is **BoundObservable**, not BoundSource.
- Names like `ObservableValue`, `MergedObservables`, and `IAsyncObservable` name concepts; names like `sources` and `getSources` name dependency relationships.
- `ObservedValue` names the value type produced by an **Observable**; **ObservableValue** names the mutable value class.
- Generic type parameters may use concept names directly, e.g. `ObservedValue<Observable>`.
- `allSources(observable)` returns the root **Observable** plus all transitive **Sources**.
- `IAsyncObservable` names an async event observable with phase observables.
- `IEventFunction` extends `IObservable`; it does not need "Observable" in its own name.
- `IAsyncEvent` remains the async event concept.
- Use `observable` for a node being inspected generally; use `source` for a node reached through `getSources` or passed to `sourceUpdated`.
- Rename error classes away from Source when they name concepts, but keep `source` properties when they identify causal sources.
- `NoUnderlyingObservableError` pairs with `getUnderlyingObservable`; avoid `NotAnObservableValueError` because **ObservableValue** is a specific class.
- `InvalidStateError` keeps state language; its invalid state holder property is `observable`, not `source`.
- `StateChangeOutsideTransitionError.source` is valid because the observable is both the state container and the source of the error; only its implementation wording should say "custom observable implementation".
- `getUnderlyingObservable` returns an **Observable** node, not a source relationship.
- Test descriptions can say "observable"; local variables can say `source` when the observable is used as a dependency.
- Event factory docs should say "observable for each phase", not "source for each phase".
- `merge` takes an array parameter named `observables`; it may keep a display-name overload, but should not expose a `sources` parameter.
- The merge return type is **MergedObservables**, not MergedSources.
- Compatibility goal is to restore observable naming, not every master API shape.
- Public export order is not a compatibility requirement.
- Generated or built `lib` output is out of scope for this naming cleanup.
- Public export surface should not change beyond the agreed renames.
- `Observable` naming does not imply master's old `subscribe(callback)` API.
- `IObserver.sourceUpdated` carries the dependency-graph semantics without renaming the observer type.
- Classes that track sources may expose their own introspection, but that is not part of `IObserver`.
- The mutable value primitive should use master's **ObservableValue** name, not Atom.
- The mutable value primitive module should use master's `observableValue` name.
- Tests should use public concept names; ObservableValue tests should not keep Atom naming.
- `ObservableValue.setValue` is acceptable even though the class name is restored for compatibility.
- Model **State** names non-observable values included in model state; do not rename state concepts to ObservableValue.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2025 Simon Oxtoby
Copyright (c) 2026 Simon Oxtoby

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
Loading