Skip to content

feat(data): db.derive inputs overload for external observables#149

Merged
krisnye merged 2 commits into
mainfrom
krisnye/derive-inputs
Jul 16, 2026
Merged

feat(data): db.derive inputs overload for external observables#149
krisnye merged 2 commits into
mainfrom
krisnye/derive-inputs

Conversation

@krisnye

@krisnye krisnye commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Adds a second db.derive shape that folds external Observe<T> values (values that don't live in the ECS — e.g. observables exposed by services) into a derive, alongside ECS reads.

// existing — ECS reads only
db.derive(db => db.resources.x + db.resources.y);

// new — inject a fixed record of external observables' CURRENT values
db.derive(
  { scale: someService.scale$ },            // Observe<number>
  (db, inputs) => (db.get(item, 'size') ?? 0) * inputs.scale,
);
  • inputs is a fixed record of Observe<T>; the callback's second argument is their current values, keyed the same and unwrapped Observe<U> → U, strongly typed (same mapping as Observe.fromProperties).
  • The first argument stays the read projection, so one synchronous body mixes external values with ECS reads.
  • Dual trigger: recomputes when any input emits OR a recorded ECS read could have changed. Structurally deduped.
  • Cold start: withholds the first value until every input has emitted (fromProperties semantics).
  • Static: inputs are subscribed once at the root and fixed for the derive's lifetime — no dynamic service-observe reads inside the body. Anything dynamic (input set depends on data, or an input's args come from an ECS read) composes with Observe.withSwitch / Observe.fromKeys around the derive.

Implemented by composing Observe.fromProperties(inputs) with the existing recording-and-gate — no new machinery. The public surface is two fully-typed overloads; the hidden implementation signature uses any in one param position only because an overloaded impl must stay callable for the typed overload (standard TS idiom; no casts, and the loose derive type already used any).

Tests

  • Compile-time (database-read.type-test.ts): inputs unwrapped to current values, db still the read projection; negatives for wrong value type, unknown key, and non-Observe input.
  • Runtime (observe-derive.test.ts): inject + dual trigger (input change and ECS read change in one body), two inputs combined with an ECS index read, cold-start withholding, structural dedupe.
  • Full data suite green (2823 tests), tsc -b + eslint clean.

Version bumped 0.9.81 → 0.9.82.

🤖 Generated with Claude Code

krisnye and others added 2 commits July 15, 2026 17:45
Add a second `db.derive(inputs, compute)` shape: a fixed record of external
`Observe<T>` values (e.g. service observables that don't live in the ECS) is
subscribed once at the root and injected as the callback's second argument —
current values, keyed like `inputs`, unwrapped `Observe<U> → U`, strongly typed.
The first argument stays the read projection, so one synchronous body can fold
external values and ECS reads together. The derive recomputes when any input
emits OR a recorded ECS read could have changed, and withholds its first value
until every input has emitted (fromProperties semantics). Inputs are static for
the derive's lifetime; dynamic cases still compose with withSwitch / fromKeys.

Includes version bump to 0.9.82.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Resolve observe-derive.test.ts: keep both #148's tests (resource-sum doc +
dependency-set-rebuild) and this branch's inputs-overload tests. No behavioral
overlap; the shared makeSource helper + Observe import auto-merged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@krisnye
krisnye merged commit 63d1ad5 into main Jul 16, 2026
4 checks passed
@krisnye
krisnye deleted the krisnye/derive-inputs branch July 16, 2026 01:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant