Skip to content

Commit 485a5ff

Browse files
author
Roy Lin
committed
feat: generate TSX protocol declarations
1 parent 4cf2c72 commit 485a5ff

24 files changed

Lines changed: 817 additions & 52 deletions

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
packages/typescript/src/generated/protocol.ts text eol=lf

.github/workflows/ci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ jobs:
3535
shell: bash
3636
run: cargo install just --locked
3737

38+
- name: Install Node.js LTS
39+
uses: actions/setup-node@v6
40+
with:
41+
node-version: "24"
42+
package-manager-cache: false
43+
3844
- name: Run local verification gate
3945
shell: bash
4046
run: just verify

Cargo.lock

Lines changed: 41 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ path = "src/lib.rs"
1818
name = "a3s-gui-native-input-conformance"
1919
path = "src/bin/native_input_conformance.rs"
2020

21+
[[bin]]
22+
name = "a3s-gui-generate-tsx-protocol"
23+
path = "src/bin/generate_tsx_protocol.rs"
24+
required-features = ["typescript-schema"]
25+
2126
[[bin]]
2227
name = "a3s-gui-winui-input-smoke"
2328
path = "src/bin/winui_input_smoke.rs"
@@ -111,6 +116,7 @@ authoring = [
111116
"dep:swc_ecma_parser",
112117
]
113118
design-system = ["authoring"]
119+
typescript-schema = ["dep:ts-rs"]
114120
appkit = []
115121
appkit-native = [
116122
"appkit",
@@ -146,6 +152,7 @@ swc_atoms = { version = "9.0.3", optional = true }
146152
swc_common = { version = "23.0.2", optional = true }
147153
swc_ecma_ast = { version = "25.0.0", optional = true }
148154
swc_ecma_parser = { version = "41.1.2", optional = true }
155+
ts-rs = { version = "12.0.1", optional = true, default-features = false, features = ["serde-compat", "no-serde-warnings"] }
149156
writeable = "0.6.3"
150157

151158
[dev-dependencies]

README.md

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<a href="https://github.com/A3S-Lab/GUI/actions/workflows/ci.yml"><img alt="CI status" src="https://github.com/A3S-Lab/GUI/actions/workflows/ci.yml/badge.svg"></a>
77
<img alt="Rust 1.95.0" src="https://img.shields.io/badge/Rust-1.95.0-2F3945?style=flat-square&logo=rust&logoColor=white">
88
<img alt="Roadmap milestone M3 current" src="https://img.shields.io/badge/roadmap-M3%20current-0067C0?style=flat-square">
9-
<img alt="TSX milestone T1 Rust foundation" src="https://img.shields.io/badge/TSX-T1%20Rust%20foundation-1687D9?style=flat-square">
9+
<img alt="TSX milestone T1 protocol SDK" src="https://img.shields.io/badge/TSX-T1%20protocol%20SDK-1687D9?style=flat-square">
1010
<a href="LICENSE"><img alt="MIT license" src="https://img.shields.io/badge/license-MIT-2F3945?style=flat-square"></a>
1111
</p>
1212

@@ -20,8 +20,9 @@ path uses a DOM, CSSOM, WebView, or framework-owned content renderer.
2020
> AppKit/GTK4/WinUI control hosts are established dogfood baselines. The first
2121
> generic self-drawn layout-to-Scene slice has landed; self-drawn text shaping,
2222
> editing/IME, accessibility bridges, and real thin-host presentation remain
23-
> roadmap work. The TypeScript runtime, executable process host, and npm
24-
> packages have not been implemented yet; the landed Rust TSX protocol is not
23+
> roadmap work. Rust-generated TypeScript wire declarations and a private
24+
> development package now exist, but the JSX runtime, executable process host,
25+
> callback/state runtime, and publishable npm packages do not; this is not yet
2526
> a runnable TSX application. React Aria component parity is not claimed yet.
2627
2728
The target is unambiguous: A3S draws all application content. The existing
@@ -146,12 +147,16 @@ render, host-frame, and event sequences before mutation, preserves the active
146147
revision after a rejected frame, and maps self-drawn snapshots and dispatches
147148
directly. Incremental and blocking codecs enforce a little-endian `u32` length
148149
prefix and reject empty, oversized, truncated, invalid UTF-8, duplicate-field,
149-
unknown-kind, and unsupported-protocol input. Canonical hello, counter render,
150-
commit, and event fixtures plus a Rust RSX/TSX Native IR and accessibility
151-
parity test pin this slice. Actual process I/O, TypeScript declarations, JSX
152-
runtime, callback registry, and packages remain. The design keeps component
153-
state and callbacks in Node, platform/GPU handles inside a separate Rust
154-
process, and Rust as the only native reconciler. Read the
150+
unknown-kind, and unsupported-protocol input. Numeric `u64` wire values are
151+
bounded to JavaScript's exact integer range; arbitrary layout/scene
152+
fingerprints are fixed 16-digit lowercase hexadecimal strings. Rust DTOs now
153+
generate the checked-in `@a3s/gui/protocol` declarations and declaration
154+
fingerprint. Canonical hello, counter render, commit, and event fixtures are
155+
parsed and byte-canonicalized in both Rust and Node 24, while a Rust RSX/TSX
156+
Native IR and accessibility parity test pins the semantic slice. Actual process
157+
I/O, the JSX runtime, callback registry/state, and publishable packages remain.
158+
The design keeps component state and callbacks in Node, platform/GPU handles
159+
inside a separate Rust process, and Rust as the only native reconciler. Read the
155160
[TSX native runtime architecture](docs/tsx-native-runtime.md) for protocol,
156161
identity, failure recovery, packaging, and T0-T5 delivery gates.
157162

@@ -361,7 +366,7 @@ independently.
361366
| M4 · Text and interaction cutover | In progress | Stable-id raw input, long press, move, typed and collection drag/drop, timed drop activation, and fail-closed dynamic drop policy resolution landed; shaping, glyphs, editing/IME, accessibility bridges, overlays, and complete calculator scenarios remain |
362367
| M5 · Default cutover | Planned | Make self-drawn content the default, then delete the three legacy widget renderers |
363368
| H0-H5 · Thin platform hosts | H0 complete; H1 in progress | Atomic frames, lifecycle recovery, stable-id raw input/reducers, long press, captured move, typed drag/drop negotiation and timed target activation, zero-toolkit firewalls, and an interactive calculator landed; the Graphics raw-surface edge remains |
364-
| T0-T5 · TSX native authoring | T1 Rust foundation in progress | Strict handshake/framing, transactional render/commit/event messages, self-drawn snapshot/dispatch adapters, counter golden/parity fixtures, and revision-scoped drop-policy bridge landed; process I/O, TypeScript declarations, JSX runtime, Node callback registry/state, packages, and stable SDK remain |
369+
| T0-T5 · TSX native authoring | T1 protocol SDK in progress | Strict handshake/framing, transactional render/commit/event messages, self-drawn adapters, safe JSON integer/fingerprint encoding, Rust-generated TypeScript declarations with drift CI, private package skeleton, counter golden/parity fixtures, and revision-scoped drop-policy bridge landed; process I/O, JSX runtime, Node callback registry/state, calculator parity, and stable SDK remain |
365370
| M6-M8 · React Aria components | Catalog pinned; conformance planned | 51/51 families mapped; collection DnD authoring/behavior slice landed; eight public parts, full software, accessibility, and three-OS self-drawn evidence remain |
366371

367372
The dependency-ordered plan and acceptance gates are in the
@@ -478,6 +483,10 @@ crate root.
478483
# Graphics paths, examples, and whitespace.
479484
just verify
480485

486+
# Focused Rust-to-TypeScript declaration and Node fixture gates.
487+
just check-tsx-protocol
488+
just test-typescript
489+
481490
# Matching host-native library tests and all-target compile check.
482491
just native-ci
483492
```
@@ -490,6 +499,7 @@ just native-input-conformance path/to/native-evidence.json
490499
just winui-input-smoke path/to/winui-smoke.json
491500
```
492501

502+
The cross-language fixture gate uses Node 24 LTS and has no npm dependencies.
493503
The WinUI smoke runner requires an interactive Windows desktop and Windows App
494504
Runtime 1.7. AppKit and GTK4 native checks require their matching host
495505
toolchains; GTK4 requires 4.14 or newer development libraries.
@@ -505,7 +515,7 @@ src/
505515
|- rsx_app/ ComponentCx, hooks, components, and binding scope
506516
|- rsx_ui/ built-in semantic design-system registry
507517
|- protocol.rs versioned frame, event, action, ACK, and recovery boundary
508-
|- tsx_protocol/ strict handshake/framing plus transactional render, commit, and event sessions
518+
|- tsx_protocol/ strict session DTOs plus Rust-owned TypeScript declaration generation
509519
|- native.rs portable NativeElement UI IR
510520
|- layout/ deterministic records, style projection, diffs, and tests
511521
|- drawing.rs Graphics boundary and reference/GPU renderer wrappers
@@ -518,6 +528,7 @@ src/
518528
`- *_native/ AppKit, GTK4, and WinUI control hosts during migration
519529
520530
examples/ headless, self-drawn, calculator, dogfood, controls, and playground apps
531+
packages/typescript/ private @a3s/gui protocol declarations and cross-language fixtures
521532
docs/ architecture, contracts, packaging, language, and roadmap
522533
packaging/ unsigned native smoke-bundle assets and validators
523534
```

docs/architecture.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -977,9 +977,15 @@ committed state on validation or response-encoding failure. It tracks the TSX
977977
callback-scope revision separately from the self-drawn host snapshot revision,
978978
so callback-only rerenders can reuse identical Native IR safely. Feature-gated
979979
adapters consume `SelfDrawnFrameSnapshot` and `SelfDrawnInputDispatch`
980-
directly. The feature-independent foundation has no Node, Nub, N-API,
981-
Graphics, legacy renderer, or OS-toolkit dependency; commands, process I/O,
982-
generated TypeScript, and the Node implementation remain T1 work.
980+
directly. All numeric `u64` wire values are rejected above JavaScript's safe
981+
integer limit, while arbitrary layout/scene fingerprints use lossless fixed
982+
hexadecimal strings. An optional build-only `typescript-schema` feature walks
983+
the Rust DTO graph, emits a deterministically ordered declaration module, and
984+
pins it with an FNV-1a fingerprint. A private TypeScript package imports that
985+
module in Node 24 and independently canonicalizes the Rust golden fixtures.
986+
The feature-independent foundation has no Node, Nub, N-API, Graphics, legacy
987+
renderer, or OS-toolkit dependency; commands, process I/O, the JSX runtime,
988+
and the Node implementation remain T1 work.
983989

984990
Drop policies that participate in hit testing use a separate synchronous
985991
protocol-v1 exchange rather than an action invocation. A

docs/roadmap.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -626,8 +626,10 @@ Gates:
626626
Status: architecture accepted; the Rust-side strict handshake/framing,
627627
transactional render/commit/event session, self-drawn snapshot/event adapters,
628628
counter golden/parity fixtures, and revision-scoped drop-policy
629-
protocol/resolver adapter have landed. Process I/O, generated TypeScript, the
630-
JSX runtime, Node callback registry, and visible TSX application remain.
629+
protocol/resolver adapter have landed. Rust-generated TypeScript declarations,
630+
a fixed schema fingerprint, a private `@a3s/gui` package skeleton, and shared
631+
Rust/Node fixture gates have also landed. Process I/O, the JSX runtime, Node
632+
callback registry, and visible TSX application remain.
631633

632634
This track is dependency-coupled to the renderer and H0-H5 host programs
633635
without blocking Rust RSX work. Headless protocol and JSX-runtime work can
@@ -653,7 +655,7 @@ decisions are recorded in
653655
- reuse the resolved `ProtocolUiFrameV1` input vocabulary behind a new TSX
654656
session envelope
655657
- extend the landed Rust RSX/static-TSX counter parity fixture to the
656-
calculator and generated TypeScript CI
658+
calculator; generated TypeScript and Node fixture CI now cover the counter
657659

658660
Gate: TSX is a peer authoring frontend and cannot bypass Native IR, layout,
659661
Graphics, interaction, accessibility, or capability checks.
@@ -667,14 +669,17 @@ self-drawn host revisions, 16 MiB-capped little-endian framing, incremental
667669
decoding, four canonical JSON fixtures, and static counter Native
668670
IR/accessibility parity have landed. The core remains free of Node and Graphics
669671
dependencies; self-drawn conversions compile only with `platform-runtime`.
672+
All numeric `u64` fields are bounded to JavaScript's safe integer range, full
673+
64-bit scene/layout fingerprints use fixed hexadecimal strings, and optional
674+
`typescript-schema` generation now produces the checked-in declarations and
675+
fingerprint. Node 24 independently imports that module and canonicalizes the
676+
same four fixtures without an npm dependency.
670677

671-
- generated TypeScript declarations from versioned Rust DTOs
672678
- automatic `jsx-runtime` and `jsx-dev-runtime` exports
673679
- deterministic child, prop, key, and event normalization
674680
- add command messages, local process I/O, and generated structured diagnostic
675681
declarations to the landed application session
676-
- extend the landed static counter semantic parity to TypeScript CI and the
677-
calculator fixture
682+
- extend the landed static counter semantic parity to the calculator fixture
678683

679684
Gates:
680685

docs/tsx-native-runtime.md

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# TSX to Native Runtime Architecture
22

3-
Status: proposed API; the Rust T1 protocol foundation is in progress. No
4-
TypeScript package or executable TSX host is implemented yet.
3+
Status: proposed API; the T1 protocol SDK foundation is in progress. A private
4+
development package contains Rust-generated wire declarations and tests, but
5+
no JSX runtime or executable TSX host is implemented yet.
56

67
This document defines an optional TypeScript authoring path for A3S GUI. The
78
developer experience is a directly executable `.tsx` application:
@@ -380,6 +381,10 @@ contract and is easy to inspect. Each message is framed as a little-endian
380381
oversized, truncated, invalid UTF-8, duplicate-field, unknown-kind, and
381382
unsupported-version messages before state mutation. The initial maximum JSON
382383
payload size is explicit in the handshake and bounded to 16 MiB or less.
384+
Every numeric `u64` field is also bounded to `Number.MAX_SAFE_INTEGER` before
385+
transport. Layout and scene fingerprints may use all 64 bits, so they cross the
386+
JSON boundary as exactly sixteen lowercase hexadecimal digits instead of
387+
lossy numbers.
383388

384389
Landed Rust foundation: `tsx_protocol` defines strict direction-specific
385390
control messages with the fixed `a3s.gui.tsx` identifier, atomic
@@ -393,9 +398,12 @@ ordered `event` batches, permits exactly one render in flight, and leaves the
393398
active revision unchanged on validation, host, or response-encoding failure.
394399
Its feature-gated adapters project `SelfDrawnFrameSnapshot` and
395400
`SelfDrawnInputDispatch` directly; no planned-widget response is involved.
396-
Canonical hello, counter render, committed, and event fixtures pin the wire
397-
spelling. Command messages, actual local process I/O, generated TypeScript,
398-
and the TypeScript peer remain pending.
401+
The optional `typescript-schema` feature walks these Rust DTOs, writes one
402+
deterministically ordered TypeScript module, and fingerprints its declaration
403+
body. Canonical hello, counter render, committed, and event fixtures pin the
404+
wire spelling in Rust and in dependency-free Node 24 tests. Command messages,
405+
actual local process I/O, the JSX runtime, and the TypeScript peer remain
406+
pending.
399407

400408
### Messages
401409

@@ -551,10 +559,12 @@ minimum M4 text/input slice.
551559
Status: architecture accepted; the Rust-side strict handshake/framing,
552560
render/commit/event session, counter parity fixtures, self-drawn adapters, and
553561
drop-policy DTO/resolver adapter are implemented. Calculator and
554-
cross-language fixtures, commands, and Node-side transport are pending.
562+
commands, JSX execution, and Node-side transport are pending. Rust-generated
563+
declarations and shared Rust/Node counter fixtures are implemented.
555564

556565
- accept process, ownership, identity, protocol, and packaging decisions
557-
- extend the landed Rust golden frame/event fixtures into cross-language CI
566+
- retain the landed shared Rust/Node golden frame/event gate and extend it to
567+
the calculator scenario
558568
- define the first counter and calculator parity scenarios
559569
- record unsupported React and browser behaviors explicitly
560570

@@ -567,17 +577,18 @@ Status: Rust transport foundation in progress. `hello`/`welcome`, atomic limit
567577
and renderer negotiation, exact message-id sequencing, 16 MiB-capped framing,
568578
incremental decoding, strict `render`/`committed`/`event` DTOs, transactional
569579
session ordering, self-drawn adapters, four canonical JSON fixtures, and the
570-
static counter Native IR/accessibility parity test have landed.
580+
static counter Native IR/accessibility parity test have landed. Safe-integer
581+
validation, hexadecimal 64-bit fingerprints, deterministic Rust-generated
582+
TypeScript declarations, a fixed schema fingerprint, the private package
583+
skeleton, and Node 24 declaration/fixture tests have also landed.
571584

572585
- extend the landed application messages with command messages and actual
573586
local process I/O
574587
- connect the landed strict drop-policy query/response DTOs to that transport
575588
and the Node callback registry
576-
- generate TypeScript protocol declarations from Rust DTOs
577589
- publish local development exports for `jsx-runtime` and `jsx-dev-runtime`
578590
- implement element/child/prop normalization, keys, and action registration
579-
- extend the landed static counter parity fixture to generated TypeScript CI
580-
and the calculator scenario
591+
- extend the landed static counter parity fixture to the calculator scenario
581592

582593
Gates:
583594

@@ -652,8 +663,8 @@ green.
652663

653664
## First Reviewable Commit Sequence
654665

655-
1. Add session DTOs, framing limits, and protocol golden fixtures.
656-
2. Add generated TypeScript wire declarations and drift CI.
666+
1. Add session DTOs, framing limits, and protocol golden fixtures. Landed.
667+
2. Add generated TypeScript wire declarations and drift CI. Landed.
657668
3. Add the automatic JSX runtime with normalization and key tests.
658669
4. Add action ids, callback scopes, and event-vector dispatch.
659670
5. Add the headless host binary and a static TSX counter fixture.

0 commit comments

Comments
 (0)