A new node takes the first of two values that is not empty (#205)
game-maker / gate (push) Successful in 2m46s
game-maker / suite (push) Successful in 8m38s

A graph that reads an optional value needed a chain of Equals and
Branch just to fall back on a default. The new node does that in one
step: it passes a through unless a is empty, in which case b comes
out. Both ports are any, so it works for numbers, text, lists,
structs and objects alike.

- `First not empty` sits in the data category with two any inputs
  `a` and `b` and one any output `value`. Are both empty, b comes
  out, so the second input doubles as the default.
- `isEmptyValue` in values.ts holds the rule as a pure function:
  nothing connected, 0, an empty text, false, an empty list, and a
  struct whose fields are all empty.
- The struct case is what covers a position: behavior graphs have no
  vector type of their own, so (0, 0) arrives as a struct. `{x: 0,
  y: 2}` therefore counts as set, `{x: 0, y: 0}` as empty.

Findings:

- A real 0 or false on a is passed over. That follows from counting
  0 as empty and is written down in the issue.
- The output stays any. Ports that inherit their type from an input
  do not exist in the graph; `Item at` has the same shape.

Tests:

- ./check.sh: 142 files, 2104 tests, exit 0 (was 2094).
- 10 new catalog cases: a set, a unconnected, 0, "", false, an empty
  list, a position (0, 0), a struct with one field set, an object,
  and both empty.
- Counter-check: with structs never empty and 0 a real value exactly
  those two cases fail, the rest stays green. Originals restored and
  compared with cmp, then rebuilt.
- No e2e check: single data nodes have no e2e coverage here, the
  palette fills itself from BUILT_IN_NODES.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5 committed 2026-09-22 19:58:30 +02:00
1 parent 8c5846f956
commit d98c2898b1
4 files changed
+52

No files matched your search

+24
View File
@@ -694,6 +694,30 @@ const CASES: Record<string, Case[]> = {
outputs: { result: true },
},
],
FirstNotEmpty: [
{
name: 'a is taken when it has a value',
inputs: { a: 'Hero', b: 'Nobody' },
outputs: { value: 'Hero' },
},
{ name: 'an unconnected a gives b', inputs: { b: 5 }, outputs: { value: 5 } },
{ name: '0 counts as empty', inputs: { a: 0, b: 5 }, outputs: { value: 5 } },
{ name: 'empty text counts as empty', inputs: { a: '', b: 'x' }, outputs: { value: 'x' } },
{ name: 'false counts as empty', inputs: { a: false, b: true }, outputs: { value: true } },
{ name: 'an empty list counts as empty', inputs: { a: [], b: [1] }, outputs: { value: [1] } },
{ name: 'a position (0, 0) counts as empty', inputs: { a: { x: 0, y: 0 }, b: 3 }, outputs: { value: 3 } },
{
name: 'a struct with one value set is kept',
inputs: { a: { x: 0, y: 2 }, b: 3 },
outputs: { value: { x: 0, y: 2 } },
},
{
name: 'an object is never empty',
inputs: { a: fakeEntity('Coin'), b: 1 },
outputs: { value: fakeEntity('Coin') },
},
{ name: 'both empty gives b', inputs: { a: '', b: 0 }, outputs: { value: 0 } },
],
And: [
{ name: 'both true', inputs: { a: true, b: true }, outputs: { result: true } },
{ name: 'one false', inputs: { a: true, b: false }, outputs: { result: false } },