Let a value be a bundle of named fields

PropertyValue was number | string | boolean | list. A struct joins it, which
costs two things the typechecker found rather than I did.

An object on a data edge used to mean a live entity, so asEntity would have
taken a struct for one and asString would have read .name off it. EntityHandle
now carries a symbol brand – a symbol so no field name can impersonate it, and
it never serializes, which suits a thing that exists only while the game runs.
It is built in exactly one place.

String(value) would have written a struct as [object Object] at 39 call sites.
They go through textOf(), which is identical for primitives.

normalizeGraph dropped everything but primitives from node params, so the List
node lost its value on every save ([3,1,4] round-tripped to undefined).
Variables were spared: they have a schema, params have none.
This commit is contained in:
root committed 2026-09-10 08:14:18 +02:00
1 parent 3b3f374e10
commit 2cf788eedd
34 files changed
+284 -82

No files matched your search

+5 -4
View File
@@ -1,7 +1,7 @@
import type { PropertyBag } from '../model/Properties';
/** `exec` carries control flow, the others values; `any` accepts every value type. */
export type PortType = 'exec' | 'number' | 'boolean' | 'string' | 'object' | 'list' | 'any';
export type PortType = 'exec' | 'number' | 'boolean' | 'string' | 'object' | 'list' | 'struct' | 'any';
export interface PortRef {
node: string;
@@ -19,8 +19,9 @@ export interface GraphNode {
y: number;
params: PropertyBag;
/**
* Ports the node declares itself, on top of the fixed ones of its type. Only the behavior
* start/end nodes carry this – they are what gives a nested behavior its signature.
* Ports the node declares itself, on top of the fixed ones of its type; the types that have
* them say so with `NodeDef.declaresPorts`. A behavior's start/end node is what gives the
* behavior its signature; an Expand/Pack node is what gives a struct its fields.
*/
ports?: DeclaredPort[];
}
@@ -31,7 +32,7 @@ export interface DeclaredPort {
kind: DeclaredPortKind;
}
export const DECLARED_PORT_KINDS = ['number', 'boolean', 'text', 'object', 'list'] as const;
export const DECLARED_PORT_KINDS = ['number', 'boolean', 'text', 'object', 'list', 'struct'] as const;
export type DeclaredPortKind = (typeof DECLARED_PORT_KINDS)[number];
export function declaredPortType(kind: DeclaredPortKind): PortType {