Skip to content

olgam4/tsrx-rxjs

Repository files navigation

rxjs-tsrx

RxJS-native single-file components (.tsrx) that compile to native-DOM clone calls over a tiny runtime. State is plain RxJS — BehaviorSubject, Subject, .pipe(...) — and the compiler wires templates, control flow and scoped CSS around it.

import { Subject } from 'rxjs';
import { scan, startWith } from 'rxjs/operators';

export function Counter() @{
  const inc$ = new Subject<void>();
  // A reactive fold: scan accumulates clicks; startWith emits the seed first.
  const count$ = inc$.pipe(scan((n) => n + 1, 0), startWith(0));

  <div>
    <p>Count: <strong>{count$}</strong></p>
    <p>Doubled: {count$ * 2}</p>              <!-- auto-lifted, reactive -->
    <button onClick={() => inc$.next()}>+</button>

    <style>
      strong { color: #2563eb; }
    </style>
  </div>
}

Scope & Support

Status: alpha (0.1.x). Not a general-purpose production framework yet. Built and intended for SPAs and embedded WebViews.

  • Client-only. TSRX renders to the live DOM. There is no SSR / hydration path; template()/createRoot()/__mount() throw a clear error if run without a document.
  • Engine targets: modern Chromium (incl. Android System WebView) and WebKit (incl. iOS WKWebView). No legacy browsers. See browserslist in packages/tsrx-rxjs/package.json.
  • Validation: unit tests run in jsdom (bun run test). A real-browser suite (bun run test:browser) runs in both Chromium and WebKit via Playwright and covers: the runtime (event delegation/composedPath, microtask scheduling, MutationObserver auto-dispose), the compiler (scoped-CSS cascade via getComputedStyle, SVG/MathML namespacing), controlled inputs (typed input, caret survival across the deferred write-back, an IME composition lifecycle), accessibility (axe audit, focus retained across keyed @for reorders, reactive aria-* reflection), and resource cleanup (subscriptions, MutationObservers and DOM all return to baseline across mount/dispose cycles). A separate soak (bun run test:memory, Chromium + CDP) churns the stress-grid through repeated create/clear of 10k rows and asserts heap and live-node counts don't grow, and that updates still flush while the page is backgrounded. Note Playwright's WebKit approximates but is not identical to a real iOS WKWebView device — on-device smoke testing is still recommended before an iOS release.

Security

TSRX never builds HTML from runtime data: reactive text is written to a text node's .data and attributes go through setAttribute / property assignment, so attacker-controlled values can't inject markup or break out of an attribute — there are no escape* helpers to misconfigure (the DOM does the escaping). This is fuzz-tested with a canary suite in both Chromium and WebKit.

What remains the app's responsibility (native semantics, same as React/Solid/Svelte — TSRX does not sanitize these):

  • a javascript: URL bound to href / src (set verbatim; only runs if the user activates it),
  • an explicit innerHTML property/spread (the deliberate raw-HTML escape hatch),
  • CSS bound via an inline style.

Don't bind untrusted data to those sinks. Scoped <style> ships as a real CSS module (never injected as HTML), and the dependency tree is audited (bun audit).

Reactivity model

  • Observables are the primitive. {count$} renders reactively; {count$ * 2} and class={active$ ? 'on' : 'off'} are auto-lifted into derived observables. count$.value is an explicit one-shot snapshot.
  • Naming: a $ suffix marks an observable (load-bearing; the type-checker enforces it).
  • Batching: updates are coalesced on a microtask (initial render is synchronous). Use flushSync() for deterministic draining (e.g. tests) and batch(fn) to coalesce a synchronous transaction.
  • Lifetime: every subscription a tree owns is disposed when its mount is disposed. Call the disposer returned by createRoot on unmount; if the host rips DOM out directly, use createRootWithCleanup (auto-disposes on detach).

Packages

  • packages/tsrx-rxjs — the compiler, runtime, and .tsrx type-checker.
  • packages/vite-plugin-rxjs — Vite plugin for .tsrx.

Examples

examples/counter, examples/todo, examples/pokemon (async search), examples/stress-grid (10k-row keyed list + a derived "diamond", the perf / memory / a11y reference target), and examples/jfb-keyed (a conformant js-framework-benchmark keyed implementation).

bun install
bun run dev              # examples/counter dev server (can also run `bun run dev:todo`)
bun run build:examples   # build all examples
bun run verify           # typecheck + tests + build examples
bun run test:browser     # real-browser suite (Chromium + WebKit)
bun run test:memory      # CDP heap/leak soak on the stress-grid
bun run test:bench       # jfb-keyed conformance + perf-budget gate

License

MIT

About

No description or website provided.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors