Commit graph

93 commits

Author SHA1 Message Date
enzotar
30a3485440 fix: complete collision — checks both top row and bottom cell
Added grid[py+2] at px+1 check for the T-piece's protruding
bottom cell. Collision now checks 4 cells total per piece:
- grid[py+1] at px, px+1, px+2 (top row destination)
- grid[py+2] at px+1 (bottom cell destination)

This prevents pieces from passing through the angled/protruding
parts of frozen T-shaped blocks.
2026-02-27 12:47:37 -08:00
enzotar
d4f353394f fix: keyboard inputs now respect collision — soft drop and hard drop gated on blocked
ArrowDown soft drop, Space hard drop, and Drop button all bypassed
the blocked signal, letting pieces pass through frozen blocks.
Now all three gate on blocked before modifying py.
2026-02-27 12:44:19 -08:00
enzotar
b8fb60d8c4 fix: collision off-by-one — pieces now stack adjacently
Collision was checking grid[py+2] instead of grid[py+1].
Pieces stopped one row too early, creating gaps.
Now checks grid[py+1] (where top cells would land) with full
3-column width (px, px+1, px+2).
Pieces now stack directly on top of each other.
2026-02-27 12:29:42 -08:00
enzotar
df8b74bab3 feat: live signal debug panel for tetris
Replaced static signal key with live debug panel showing:
- Piece state: piece, px, py, rotation, blocked, lockTick
- Physics: score, lines, level, gravityTick, dropInterval
- Grid rows: g13-g19 with full array contents

Blocks confirmed persisting correctly after collision.
2026-02-27 12:26:01 -08:00
enzotar
d9e0e31d1b fix: tetris collision detection — pieces now stack properly
Added 'blocked' signal that checks frozen grid cells below active piece.
Collision check branches on py to inspect the correct grid row (13-19).
Gravity only drops when not blocked. Lock triggers on blocked state.
Freeze writes piece into correct row based on py (7 rows supported).
Added grid rendering for rows 13-15 (total 7 visible frozen rows).
Build output: 84KB
2026-02-27 12:20:09 -08:00
enzotar
075c4a20fe feat: tetris — signal composition showcase with 6 reactive layers
game-tetris.ds demonstrates DreamStack signal composition:
- Layer 1: Data signals (20 grid rows, piece state, next piece)
- Layer 2: Physics signals (gravity tick, drop speed, level scaling)
- Layer 3: Input signals (keyboard events -> movement commands)
- Layer 4: Derived signals (lock detection, line clear, game over)
- Layer 5: Sound signals (lock, clear, game over tones)
- Layer 6: Stream signals (30+ signals broadcast for spectators)

Board: 10x20 grid, 7 piece types, ghost piece indicator
Controls: Arrow keys, space (hard drop), P (pause)
Side panel: next piece preview, stats, signal layer key
Compiles to 76KB HTML+JS
2026-02-27 12:13:22 -08:00
enzotar
003118ec10 refine: type system second pass — deeper unification throughout
Refinement pass:
- DotAccess: unwraps Signal/Derived before field lookup
- UnaryOp: uses unification instead of manual matching
- Call: unifies each arg with param type, applies subst to return
- List: unifies all element types (not just first)
- If/else: unifies both branches, checks condition is Bool
- When/else: unifies body with else body, checks condition
- Match: unifies all arm types for consistency
- Assign: checks assigned value compatible with variable type
- ForIn: binds iteration variable from Array element type

Tests: 39 ds-types (up from 34), 164 workspace total, 0 failures
2026-02-27 11:54:15 -08:00
enzotar
8fb2214ac0 feat: complete type system — HM unification, signal-aware types, effect scoping
Type System Completion:
- Add unify() with occurs check for proper type variable binding
- Add apply_subst() to chase type variables through substitution map
- Add SignalInfo/SignalClass for graph-based signal classification
- Add check_program_with_signals() accepting optional signal graph data
- Push Dom effect handler scope automatically when checking view blocks
- Wire unification into BinOp, comparison, and logical operator inference
- Include List/Record literals in source signal heuristic

Tests: 34 ds-types tests (up from 11), 159 workspace total, 0 failures
2026-02-27 11:36:28 -08:00
enzotar
ebf11889a3 docs: comprehensive documentation update
- DREAMSTACK.md: rewritten with accurate counts (48 examples, 14
  components, 136 tests), full CLI reference, architecture diagrams,
  quick start guide, comparison table, and phased roadmap
- IMPLEMENTATION_PLAN.md: rewritten with all 10 phases showing
  accurate completion status, current capabilities, and next steps
- BITSTREAM_INTEGRATION.md: updated test count (82 → 136)
- USE_CASES.md and STREAM_COMPOSITION.md: already current, unchanged
2026-02-27 11:15:54 -08:00
enzotar
d4c7ba2385 feat: core language & stream improvements
Language improvements:
- Reactive container class: prop (wraps in DS.effect when signal-dependent)
- Stream output filtering (_streamDiff skips non-output signals)
- Stream exponential reconnect backoff (2s → 4s → 8s, max 30s)
- Breakout game: classic row order with all 5 rows having collision

Verified: reactive text + derived signals already work.
All 48 examples compile, 136 tests pass
2026-02-27 11:03:53 -08:00
enzotar
4ac584c81e fix: reactive component props + breakout improvements
Compiler fixes:
- Component props with signal-dependent expressions now wrapped as
  reactive getters (() => expr) at call site
- Component declarations handle fn-typed props via
  { get value() { return props.x(); } } for live reactivity
- Container style: prop wrapped in DS.effect when expr contains .value
- Timer merging: all same-interval 'every' statements grouped into
  single setInterval with one DS.flush()

Breakout game improvements:
- Classic row order: blue top (far), red bottom (near paddle)
- All 5 rows have full collision detection (was only 2)
- Faster ball (4px/frame) and paddle (40px/keypress)
- Score/Lives badges now update in real-time

All 48 examples compile, 136 tests pass
2026-02-27 10:53:27 -08:00
enzotar
e8bbfcbcb7 perf: merge same-interval timers + breakout game + beats viewer
Performance:
- All every N statements with same interval now merge into one setInterval
- Pong: 24 timers → 1, Breakout: 38 timers → 1 (single DS.flush per frame)

New examples:
- game-breakout.ds: brick-breaker with 5×10 colored bricks, keyboard, audio
- beats-viewer.ds: step sequencer spectator via relay

Fixes:
- _playTone/_playNoise early-exit when freq/duration <= 0
- Breakout score race: score+bounce checks before brick destruction
- Score sound effects in pong (220Hz/440Hz sawtooth)

All 48 examples compile, 136 tests pass
2026-02-27 10:24:13 -08:00
enzotar
eb21aa2137 feat: beats viewer, score sounds, audio early-exit guards
- New beats-viewer.ds: step sequencer spectator via relay stream
- game-pong.ds: added score sound effects (220Hz/440Hz sawtooth)
- Runtime: _playTone skips when freq<=0, _playNoise skips when dur<=0
- All 47 examples compile, 136 tests pass
2026-02-27 10:05:32 -08:00
enzotar
25b960fa29 feat: pong spectator viewer + stream proxy reactivity fix
- New pong-viewer.ds: full visual court rendered from streamed state
- Extended container prop signal detection: js_val.contains('.value')
  fixes DotAccess on stream proxies (game.value.p1y) not being reactive
- Verified two-tab relay demo: player → relay → viewer syncs in real-time
- All 46 examples compile, 136 tests pass
2026-02-27 09:59:34 -08:00
enzotar
bd926b9e0a feat: keyboard input, Web Audio synthesis, and multiplayer demo
Compiler changes:
- emit_handler: DOM events (keydown/keyup/etc) route to document.addEventListener
- emit_handler: handler params (ev) pushed into scope to prevent .value suffix
- play_tone(freq, dur, type) and play_noise(dur, vol) builtins
- Web Audio runtime: _playTone (oscillator) and _playNoise (filtered noise)
- Reactive textContent: signal-dependent If expressions wrapped in DS.effect

Example updates:
- game-pong.ds: Arrow Up/Down for paddle, Space for pause/resume, paddle-hit sounds
- step-sequencer.ds: 4 audio trigger timers (kick=60Hz, snare=noise, hihat=noise, bass=110Hz)

Verification:
- All 136 tests pass, 45 examples compile
- Relay connects successfully, multiplayer sync confirmed
- Keyboard controls and pause toggle verified in browser
2026-02-27 09:34:20 -08:00
enzotar
0e23ddd88b feat: game-pong.ds + two compiler improvements
- Native DreamStack pong game with visual court (stack container),
  CSS-positioned paddles/ball, 30fps game loop, AI tracking, auto-serve
- Parser: containers (column/row/stack) now support leading props
  e.g. column { style: '...' } [children]
- Codegen: fixed signal detection for container layout props
  (strip .value suffix for signal graph lookup)
- All 136 tests pass, 45 examples compile
2026-02-27 00:02:58 -08:00
enzotar
62830fa82a fix: for-in parser token mismatch + enhanced step sequencer
- Fix for...in parser: TokenKind::In → TokenKind::InKw (was using wrong token)
- Remove dead In variant from TokenKind enum
- Fix pre-existing test: When pattern match 2→3 fields
- Enhanced step-sequencer.ds: 16 steps, 4 instrument arrays, play/pause,
  BPM controls with limits, presets, dynamic variants, streaming output
- All 118 tests pass
2026-02-26 23:42:29 -08:00
enzotar
f7f7363230 feat: snake game streaming via relay
game-snake.html — Full canvas Snake game:
- 20x20 grid with gradient snake (eyes, body fade)
- Keyboard (WASD/arrows) + button controls
- Wall wrapping, self-collision detection
- Speed increases on food eat (200ms → 80ms min)
- Game over screen with restart
- Streams every frame via DreamStack relay (0x31 SignalDiff)
- Periodic auto-sync (0x30 every 50 frames)
- Graceful fallback when relay unavailable

game-viewer.ds — DreamStack receiver:
- Connects to ws://localhost:9100/stream/snake
- Shows live score, length, speed, position
- PLAYING/GAME OVER status badge

game-snake.ds — DreamStack source (simplified)
game-reaction.ds — Reaction game (bonus)
2026-02-26 20:46:53 -08:00
enzotar
f4e5ace37c feat: *= /= operators + 6 new array methods
New assignment operators (full pipeline: lexer → parser → analyzer → codegen):
- *= (MulAssign): count *= 2
- /= (DivAssign): count /= 2

New array methods in event handler (all flush signal + stream diff):
- .clear()   → items = []
- .insert(i, v) → splice at index
- .sort()    → immutable sort
- .reverse() → immutable reverse
- .filter(fn) → filter in place
- .map(fn)   → map in place

New example: language-features.ds (65KB)
- Tests all assignment ops (+=, -=, *=, /=)
- Tests array methods (push, pop, sort, reverse, clear)
- Tests match expressions
- Tests comparison operators
- Tests derived signals (let doubled = count * 2)
- Browser-verified: zero console errors

All 11 examples pass.
2026-02-26 20:29:35 -08:00
enzotar
0125c6e714 feat: expanded variant system — 30+ new CSS class mappings
variant_to_css expanded for all 14 components:
- Button: outline, toggle, toggle-on/off, tab-active/inactive, select
- Text: card-title/subtitle/footer, input-label/error/helper,
  dialog-title, stat-*, separator-label, avatar-*
- Column: stat-card, dialog-panel/overlay, avatar, separator-*,
  progress-fill-*, toast-*, alert-*
- Row: tabs, separator, select-group
- Input: input-default, input-error

variant_map_js expanded for dynamic variant resolution

New CSS:
- ds-btn-outline (transparent with indigo border)
- ds-tab / ds-tab-active (tab switcher styles)
- ds-tabs (flex container with subtle bg)
- ds-select-group (flex wrap)
- ds-separator-row / ds-separator-v
- ds-toast-success/error/warning/info colors
- ds-avatar-status

All 10 examples pass. Build sizes: gallery 93KB, dashboard 60KB.
2026-02-26 19:17:49 -08:00
enzotar
1d554ae7ab feat: enhanced 14 registry components + component gallery
Registry components enhanced with proper DreamStack patterns:
- Card: conditional title/subtitle/footer via when guards, slot for children
- Button: variant as string prop, disabled support
- Dialog: when-guarded overlay, slot for content, close button
- Progress: conditional label, variant support
- Input: conditional label, error state, helper text
- Alert: variant prop, slot for custom content
- Toggle: when-guarded label display
- Toast: when-guarded visibility
- Avatar: initials with variant
- Badge: label with variant prop
- Separator: optional centered label

New components:
- Stat: dashboard metric card (label, value, change delta)
- Tabs: tab switcher supporting 2-3 tabs
- Select: button-based option selector

New example:
- component-gallery.ds (89KB): imports 12 components, uses Card+Button,
  Input+Alert, Progress, Badge+when/else, match expressions,
  Stat metrics, Toggle, Avatar, Toast, Separator, every timer

All 10 examples pass. Browser-tested with zero errors.
2026-02-26 18:25:49 -08:00
enzotar
08e36573a5 feat: HTTP /meta API, signal dedup, periodic auto-sync
Relay HTTP /meta endpoint:
- GET /meta → JSON with all channel stats
- GET /meta/{name} → JSON with specific channel stats, schema, current state
- Uses TCP peek to intercept raw HTTP before WS handshake
- CORS headers for browser access

Signal deduplication:
- _lastSentValues tracks last-sent value per signal
- JSON.stringify comparison skips unchanged values
- Prevents redundant WS frames from derived signals

Periodic auto-sync:
- Every 50 diff batches, source sends full SignalSync (0x30)
- Relay can compact its cache instead of accumulating infinite diffs
- Resets pending_signal_diffs in relay cache

All 57 relay tests pass. All 9 examples pass.
2026-02-26 18:17:25 -08:00
enzotar
598ecde59c feat: comprehensive streaming improvements
Runtime _connectStream improvements:
- Connection status as reactive signals: _connected, _latency, _frames, _reconnects
  injected on stream proxy so UIs can show connection health
- Fixed RLE decoder: 2-byte LE count (was 1-byte, mismatched relay encoder)
- Schema caching: 0x32 SchemaAnnounce frames now parsed and cached
- RTT tracking: receivers send periodic pings (5s), measure round-trip latency
- Better reconnect logging: includes URL and attempt count

Relay tests (57 total):
- catchup_merges_multiple_diffs: sync + 3 diffs → 1 merged frame
- catchup_diffs_only_no_sync: diffs without sync → merged frame
- catchup_preserves_version_counters: conflict resolution versions kept

New example:
- timer-multi-action.ds: every timer + multi-action buttons verified

Documentation:
- STREAM_COMPOSITION.md: 4 new sections (Diff Batching, Connection Status, RTT, Relay Merging)
- Updated example table with streaming-dashboard.ds and timer-multi-action.ds

All 9 examples pass regression (44-70KB each)
2026-02-26 18:09:14 -08:00
enzotar
746b76fe4f perf: streaming core improvements — batched diffs, RTT tracking, relay merging
Runtime improvements:
- Diff batching: multiple signal changes coalesced into 1 WS frame
  via _pendingDiffs + microtask Promise.resolve()
- Connection status: _streamConnected, _streamLatency, _streamReconnects,
  _streamFrameCount, _streamByteCount tracked for stream health
- RTT tracking: periodic ping/pong every 5s measures round-trip latency
- Removed redundant _streamSync from flush() — diffs are already batched

Relay improvements:
- Diff merging: late joiners receive 1 consolidated sync frame instead
  of replaying hundreds of individual diffs
- Version counters merged: conflict resolution preserved across catchup
- Fallback: if JSON parse fails, falls back to raw sync + all diffs

Test updates:
- state_cache_signal_sync verifies merged payload (count: 1)
- All 54 relay tests pass
- All 8 examples pass regression
2026-02-26 18:02:31 -08:00
enzotar
f29673cbd8 feat: streaming dashboard with imported components + live data
- New streaming-dashboard.ds: first example combining components + streaming
- 4 Card grid with Badge, Button, match, stream receivers
- Receives live data from counter, clock, stats streams
- Button callbacks (Refresh/Reset) work within stream context
- All 8 examples pass regression (47,799 bytes)
2026-02-26 17:51:08 -08:00
enzotar
c47852957f fix: merge duplicate click props + upgrade streaming examples
- Duplicate prop keys now merge into Block expressions
  click: a then click: b → Block([a, b])
- All actions fire in one click (was silently overwriting)
- streaming-mood.ds upgraded to semicolons
- streaming-stats.ds upgraded to semicolons
- Browser-verified: Happy button fires 3 actions (mood+color+energy)
- All 7 examples pass regression
2026-02-26 17:44:21 -08:00
enzotar
10b2717281 feat: multi-statement event handlers with semicolons
- Added Semicolon token to lexer
- Enables: click: items.push(x); input = ""
- Push-and-clear, increment-and-reset, clear-all patterns
- Browser-verified: both actions fire in one click
- All existing examples pass regression
2026-02-26 17:29:47 -08:00
enzotar
6c9d109ebd fix: match parser allows container bodies in arms
- Added can_be_pattern() with look-ahead disambiguation
- Ident only treated as pattern if followed by -> or (
- Keywords (row/column/when/each) correctly terminate match
- Match arms now support: "active" -> row [ Badge + text ]
- Siblings after match (text, button, row) no longer consumed
- Project Manager updated with rich row/Badge match arms
- All 6 existing examples pass regression
2026-02-26 17:19:50 -08:00
enzotar
70c9589573 feat: Project Manager demo — comprehensive 4-page routed app
- Dashboard with metrics, match status, progress bars
- Task Manager with dynamic add/remove via push/remove
- Team directory with member cards and status badges
- Settings with toggle and about section
- 64,622 bytes, zero console errors, all 11 components used
2026-02-26 17:10:32 -08:00
enzotar
51c9c60bfe feat: multi-page routing demo with 4 routes
- Home/Counter/Todos/About with hash navigation
- State persists across route changes
- Uses existing router infrastructure (no compiler changes)
- navigate keyword, matchRoute, _route signal
2026-02-26 17:04:49 -08:00
enzotar
f63ff52e2a feat: upgrade init starter app to showcase all DreamStack features
- Imports Card, Badge, Button from components/
- Counter with callback props, Greeting with when/else
- Mood match expression with Badge, Todo dynamic lists
- Full init → build flow verified (51,401 bytes, 0 errors)
2026-02-26 16:57:08 -08:00
enzotar
9d01f1b702 feat: component event callbacks + function prop forwarding
- ComponentUse wraps Assign/MethodCall/Block props as arrow functions
- Component prop wrapper preserves function props (typeof check)
- Event handler fallthrough calls function-type identifiers
- New: examples/callback-demo.ds with Button onClick callbacks
- All existing examples build successfully
2026-02-26 16:51:58 -08:00
enzotar
cbd6dfc7a6 feat: dynamic lists (push/remove/pop) + TodoMVC demo
- MethodCall AST node: obj.method(args) parsing
- Array push: items.push(x) → immutable spread+append
- Array remove: items.remove(idx) → filter by index
- Array pop: items.pop() → slice(0, -1)
- Fix: loop vars (todo, _idx) emitted without .value via is_local_var()
- Fix: _idx added to each loop scope for index-based event handlers
- New: examples/todomvc.ds — add, remove, clear all, fully reactive
2026-02-26 16:46:06 -08:00
enzotar
a7af39e900 fix: when/else parentNode null guard for slot context + match parser boundaries + showcase demo
- When/else inside slots: anchor parentNode is null during initial effect
  Fixed with named effect function + requestAnimationFrame retry
- Match parser now terminates on ] } else tokens (works inside containers)
- Updated Progress/Badge components
- Added examples/showcase.ds: 5-section demo exercising all features
2026-02-26 16:34:45 -08:00
enzotar
5425d7768c feat: dev server improvements - recursive watching, auto-open browser
- Recursive file watching: catches imported file changes from registry/
- Auto-detect project root: walks up to find registry/ or examples/ dir
- Auto-open browser on startup (xdg-open/open/cmd)
- Verified HMR live reload: 0ms recompile on file save
2026-02-26 16:19:33 -08:00
enzotar
76bb1bb3a2 feat: slot/children composition for components
- AST: added Expr::Slot variant
- Parser: 'slot' keyword renders children, [...] bracket children after ComponentUse
- Codegen: DS_{name}(props, __children) factory pattern
- Type checker: Slot => Type::View
- Updated Card component with slot for children
- Added examples/slot-demo.ds
2026-02-26 16:14:35 -08:00
enzotar
bb65e10f5c feat: when/else conditional branching
- AST: When(cond, body) -> When(cond, body, Option<else_body>)
- Parser: optional 'else -> expr' after when body
- Codegen: reactive DOM swap with anchor comments
- Signal graph + type checker updated for 3-arg When
- Component prop signal wrapping: .value compatible accessors
- Added examples/when-else-demo.ds
2026-02-26 16:03:29 -08:00
enzotar
55dc24eecc fix: component prop signal wrapping + import demo
- emit_component_decl now wraps props in signal-like accessors
- Props use { get value() { return props.X; } } pattern
- Missing props default to empty string instead of null
- Added examples/import-demo.ds demonstrating 3-component import
- Zero console errors when rendering imported Card, Badge, Button
2026-02-26 15:46:55 -08:00
enzotar
8a318e380e fix: integer division + streaming restart
Codegen: BinOp::Div now emits Math.trunc(l / r)
- Clock displays 0:1:30 instead of 0.016:1.5:30
- Affects both emit_expr and predicate_to_js

Lexer: removed duplicate 'in' keyword mapping
- InKw at line 312 is canonical, removed old In at line 338

Examples: added project-tracker.ds (each loops + cards)
2026-02-26 15:22:54 -08:00
enzotar
008f164ae7 feat: each loop, dreamstack init, expanded registry
Language:
- each item in list -> template (reactive list rendering)
- each/in tokens in lexer, Expr::Each in AST
- Reactive forEach codegen with scope push/pop
- Container trailing props: column [...] { variant: card }

CLI:
- dreamstack init [name] - scaffold new project
- Generates app.ds, components/, dreamstack.json
- 4 starter components (button, card, badge, input)

Registry expanded to 11 components:
- NEW: progress, alert, separator, toggle, avatar
- All embedded via include_str!

CSS: progress bar, avatar, separator, alert variants,
toggle switch, stat values (230+ lines design system)

Examples:
- each-demo.ds: list rendering demo
- dashboard.ds: glassmorphism cards with container variant
2026-02-26 14:42:00 -08:00
enzotar
a290bc1891 feat: container variant props, 11-component registry, rich dashboard
Parser:
- column/row/stack now parse trailing { props } after ]
- Enables: column [...] { variant: "card" }

Codegen:
- Container props dispatch: variant, class, click, style, layout
- variant_to_css() maps (tag, variant) → CSS class
- variant_map_js() for dynamic variants via inline JS map
- 230+ lines design system CSS (button/badge/card/dialog/toast/
  progress/alert/separator/toggle/avatar/stat)

Registry (11 components):
- button, input, card, badge, dialog, toast
- NEW: progress, alert, separator, toggle, avatar
- All embedded via include_str! for offline use

Examples:
- showcase.ds: component gallery with variant prop
- dashboard.ds: admin dashboard with glassmorphism cards
2026-02-26 13:58:33 -08:00
enzotar
7805b94704 feat: component registry with styled variants, dreamstack add/convert CLI, and showcase
- Phase 1: Component parser + codegen (emit_component_decl, emit_component_use, emit_match)
- Phase 2: 6 registry components (button, input, card, badge, dialog, toast)
- Phase 3: dreamstack add CLI with dependency resolution and --list/--all
- Phase 4: dreamstack convert TSX→DS transpiler with --shadcn GitHub fetch
- Phase 5: 120+ lines variant CSS (buttons, badges, cards, dialog, toast, input)
- New example: showcase.ds demonstrating all component styles
2026-02-26 13:27:49 -08:00
enzotar
61c26acfa7 improve: dependent types review — cycle detection, precision, error quality
- Fix ast_to_pred_expr silent Value fallback → opaque Call prevents false positives
- check_refinement now handles BoolLit/StringLit with human-readable display
- Integer equality/inequality uses exact i64 via try_resolve_ints (no f64 loss)
- Type::Refined handled in unwrap_reactive + new unwrap_refined() utility
- Type alias cycle detection via type_expr_references in Pass 0
- 9 new tests: predicate display, evaluate_static (int/compound/eq_precision),
  unwrap_refined, refined type display, cycle detection, readable violations

127 workspace tests pass (25 in ds-types), 0 failures
2026-02-26 11:21:43 -08:00
enzotar
9ef28bb53a feat: dependent types — refinement types, type aliases, type annotations
- Lexer: added 'type' and 'where' keywords
- AST: TypeExpr::Refined, Declaration::TypeAlias, LetDecl.type_annotation
- Parser: parse_type_alias_decl, parse_type_expr (Named, Generic, where)
- Type system: Type::Refined, Predicate/PredicateExpr with evaluate_static()
- Errors: RefinementViolation, TypeAliasCycle (Elm-style messages)
- Checker: type alias registry, resolve_type_expr, ast_to_predicate,
  static refinement checking for literal values
- Codegen: Phase 1b runtime guards, predicate_to_js helper

Syntax: type PositiveInt = Int where value > 0
        let count: PositiveInt = 5  -- static check passes
        let count: PositiveInt = -1 -- compile error

118 tests pass (16 in ds-types, 5 new for refinements)
2026-02-26 11:09:33 -08:00
enzotar
5dcbbdca86 docs: add STREAM_COMPOSITION.md — full API reference and protocol spec 2026-02-26 10:12:29 -08:00
enzotar
a8235c48b3 feat: stream composition API — select, schema, relay filtering
1. Receiver-side `select` clause:
   - `stream from "url" { select: field1, field2 }`
   - Parser, AST, codegen all updated
   - Emits: `_connectStream(url, ["field1","field2"])`
   - Client-side _csFilter strips unwanted fields

2. Schema announcement (0x32):
   - Sources send output schema on connect
   - Lists registered signal names and mode

3. Relay schema cache:
   - ChannelState stores schema from 0x32
   - Forwarded to late-joining receivers

4. Relay-side subscribe filter (0x33):
   - Receivers send wanted fields after connecting
   - Relay strips unwanted JSON keys from 0x30/0x31
     frames before forwarding — saves bandwidth

Protocol: SchemaAnnounce=0x32, SubscribeFilter=0x33
54 tests pass, all crates build clean.
2026-02-26 10:07:47 -08:00
enzotar
b5d813b9af feat: chained signal composition — 3→1→final + mood mixing
Add compose-metrics.ds (Layer 1): receives counter+clock+stats,
derives uptime/events/status, re-streams on /peer/metrics. This app
is BOTH a receiver and a source.

Add compose-master.ds (Layer 2): receives chained metrics from
Layer 1 + mood direct from Layer 0. Demonstrates multi-layer
signal composition with independent stream mixing.

Verified: Uptime: 51s, Total Events: 9 flowing through the full
three-layer chain to the master dashboard.
2026-02-26 09:51:36 -08:00
enzotar
442a2db65e fix: use explicit /peer/counter channel for streaming-counter
The 'default' relay channel accumulated stale Source connections from
previous sessions, causing frame delivery issues to Receivers on
/stream/default. Moving counter to an explicit /peer/counter channel
(matching clock, stats, mood pattern) fixes the composition dashboard.

All 4 streams now show live data: Count: 3, Doubled: 6.
2026-02-26 09:45:07 -08:00
enzotar
8775860fdd feat: 4-app signal composition demo with explicit outputs
Add 3 new streaming apps with explicit output declarations:
- streaming-clock.ds: output hours, minutes, seconds (tick private)
- streaming-stats.ds: output total, average, max (sum private)
- streaming-mood.ds: output mood, energy, color (clicks private)

Add compose-dashboard.ds that receives all 4 streams via unique
relay channels (/stream/default, /stream/clock, /stream/stats,
/stream/mood) into a single dashboard view.

Each app demonstrates selective signal registration — only declared
outputs are streamed, internal state remains private.
2026-02-26 09:04:22 -08:00
enzotar
627ee44275 feat: explicit signal output API for stream declarations
Add 'output: field1, field2' syntax to stream declarations to control
which signals are exposed over the relay. Only listed signals are
registered in _signalRegistry (and thus streamed). Omitting output
streams all signals (backwards-compatible).

Also strips internal sync metadata (_pid, _v) from receiver state
so composition consumers only see clean signal values.

Parser: parse comma-separated idents after 'output:' key
AST: Vec<String> output field on StreamDecl
Codegen: conditional _registerSignal, delete _pid/_v on receive

Example: stream counter on 'ws://...' { mode: signal, output: count, doubled }
2026-02-26 08:56:32 -08:00