Foreign-Language & Cross-Platform Integration

How non-.NET processes — Python, Node/Bun, browsers, and native mobile apps — join the mesh and use mesh features natively, and how any React-capable platform renders mesh layout areas. This is the umbrella over the whole integration; the gRPC transport itself has a deeper-dive companion, ForeignLanguageBridge.md.

There are two halves, and one unifying idea behind each:

  1. Talking to the mesh — a foreign process becomes a first-class mesh participant over gRPC, because the mesh is transport-agnostic: everything crosses one seam, the IMessageDelivery JSON envelope.
  2. Rendering the mesh — a foreign UI renders a mesh layout area, because the UI model is a platform-agnostic JSON UiControl tree — the same tree the Blazor portal and MAUI app render.
  Python · Node/Bun · browser · iOS/Android            .NET mesh (portal)
  ┌───────────────────────────────────┐   gRPC bidi    ┌────────────────────────┐
  │  client SDK   (transport)         │◄═══ stream ═══►│  MeshWeaver.Hosting.Grpc│
  │  @meshweaver/react (UI renderer)  │   UiControl     │  → the mesh (hubs)      │
  └───────────────────────────────────┘   tree (JSON)  └────────────────────────┘
        one tree, many leaf packs ─────────────────────── one envelope, many transports

Part 1 — The transport (joining the mesh over gRPC)

A single gRPC bidirectional stream IS one mesh participant connection — the exact role SignalRConnectionHub plays for the MAUI app and Blazor-WASM clients. We swap the transport skin (SignalR → gRPC) and reuse everything else.

The transport is proven by network-free in-memory round-trip tests (the bidi Open AND the Connect/ Deliver split) and a live Kestrel (h2c) round-trip with a real GrpcChannel. Full detail + diagrams: ForeignLanguageBridge.md.


Part 2 — The client SDKs

Each SDK is the in-language equivalent of IMessageHub + MeshWeaver.Mesh.Operations.MeshOperations, speaking the bidi stream. All build on three primitives, and every mesh operation is a thin composition of them:

Primitive What it does
observe(target, type, msg) request/response — send a delivery, await the reply whose properties.RequestId matches
post(target, type, msg) fire-and-forget
watch(target, streamId, …) live stream — subscribe, demux change events by streamId

search / get / watch / patch and the node-lifecycle ops create / delete / move / copy / execute are all compositions of these over the existing mesh request types — the same surface across all three SDKs (Python, Node, and the browser/RN client-web). The lifecycle ops are WIRE:-annotated where the exact request $type awaits a live capture.

Security: the bearer token travels in gRPC call metadata; the server validates it and stamps every write with the caller's identity. A forged client-side identity is never trusted.

Envelope shape (envelope.py / envelope.ts) and the operation request types (marked WIRE:) are pinned to the mesh's IMessageDelivery JSON — confirm the exact $type/casing against a captured sample (the C# round-trip test emits one). Everything beneath them (transport, correlation, demux) is correct.


Part 3 — The UI: rendering mesh layout areas (@meshweaver/react)

A MeshWeaver layout area is delivered as a JSON UiControl tree (an {areas, data} object, updated via RFC 7396 merge-patches). Rendering it is: walk the tree, map each control's $type to a component, resolve /data bindings, post click/edit events back. clients/react (@meshweaver/react) is exactly that, in React + Fluent UI.

The swappable-core architecture

The crux — and the direct analog of MAUI's MauiViewPack:

        renderer CORE   (@meshweaver/react/core — NO DOM/Fluent)
        dispatch on $type · pop skins · resolve bindings · area stream · post events
                       │  pulls components from a RegistryProvider context
          ┌────────────┴────────────┐
   Fluent DOM pack            RN pack
   @meshweaver/react       <View>/<Text>/<TextInput>
   (web · Electron · Next) (iOS · Android)

ControlRenderer and area/* import no concrete component — they pull the "leaf pack" (control + skin components) from context. The web entry installs a Fluent DOM pack; a React Native app installs a native pack. Same UiControl tree, swappable leaves — exactly how MAUI has a native pack and Blazor a web one. Because the Blazor portal renders with Fluent UI Blazor, the UiControl → Fluent React mapping is near 1:1.

What the pack covers

The Fluent web pack maps the full vocabulary: layout via skins (Stack/LayoutGrid/Tabs/Toolbar/ Splitter/NavMenu/NavGroup/Card), display (Label/Markdown/Html/Badge/Icon/CodeSample/ Exception), data (DataGrid + Property/Template columns, Catalog, Chart), the full input/form family, navigation, feedback, editors (textarea — swap in Monaco), and the mesh controls. Unknown $types render a labeled fallback; extend or override by spreading into the registry.

Data plane

The renderer depends only on an AreaSource (the {areas,data} tree + an event sink), so it's transport-agnostic:

Bindings: a control property is a literal or a JsonPointerReference into /data; form edits write back via the binding's pointer (optimistically applied, exactly as the live stream echoes the merge-patch).

vs MAUI / Blazor

UiControl tree Leaf pack Transport
Blazor portal same Fluent UI Blazor SignalR (in-process circuit)
MAUI app same MauiViewPack (native) SignalR participant
@meshweaver/react (web/Electron/Next) same Fluent DOM gRPC (GrpcAreaSource)
React Native same RN <View> pack gRPC-web (see Targets)

Part 4 — The targets

Target Leaf pack Extra work Status
Web / Vite Fluent (shipped) none demo + 11 vitest tests, screenshot
Next.js Fluent (shipped) "use client" + Fluent SSR (~10 lines) guide (clients/react/docs/nextjs.md)
Electron (desktop) Fluent (shipped) a BrowserWindow (shipped) clients/react/electron/main.cjs
React Native / Expo (the MAUI peer) RN pack (shipped) Expo project + gRPC-web transport (both shipped) MeshWeaver.Plugins/app/react-native (+ src/live.ts), typechecks + 7 headless render tests
Browser / RN live transport n/a (transport) none @meshweaver/client-web (clients/grpc-web), typechecks + builds
Portal example Fluent (shipped) an app shell (shipped) clients/portal, builds, screenshot

Next.js is the easiest target (React-on-the-web → same package, same Fluent pack). React Native is the "vs MAUI" peer — same core, a native leaf pack. Live data in a browser or React Native uses the gRPC-web split (Connect+Deliver) — @grpc/grpc-js is Node-only and gRPC-web can't do the bidi Open. Both halves are shipped: the server (MeshGrpcService.Connect/Deliver, tested) AND the client — @meshweaver/client-web (clients/grpc-web), a MeshConnectionLike over Connect-ES that drops straight into GrpcAreaSource. The RN app wires it via MeshWeaver.Plugins/app/react-native/src/live.ts. Node, Electron-main, and Next.js-server use the bidi Open directly via @meshweaver/client.


Part 5 — Repo layout

clients/
  python/         meshweaver — Python SDK (transport + ops)
  typescript/     @meshweaver/client — Node/Bun SDK (bidi Open)
  grpc-web/       @meshweaver/client-web — browser + RN client (Connect+Deliver split)
  react/          @meshweaver/react — Fluent UI renderer (core + web pack) + GrpcAreaSource
    docs/         react-native.md · nextjs.md · demo.png
  react-native/   meshweaver-mobile — Expo app + RN leaf pack (the MAUI peer) + src/live.ts
  portal/         @meshweaver/portal-example — a web portal built from the renderer
MeshWeaver.Plugins/src/MeshWeaver.Hosting.Grpc/   the gRPC mesh transport (server)
src/MeshWeaver.Documentation/Data/Architecture/ForeignLanguageBridge.md   transport deep-dive
.github/workflows/clients.yml  CI: react (typecheck+test) · client-web (typecheck) · RN (typecheck) · portal (build)

Part 6 — Status & what needs validation

Verified here: the C# transport (in-memory + live Kestrel h2c round-trips), the React renderer (pixel-verified web render, 11 vitest tests, 0.9 MB bundle), the RN connector (typechecks against real react-native types AND 7 headless render tests that mount the sample through the RN pack — proving the core renders with zero DOM/Fluent runtime and resolves bindings), the gRPC-web client (@meshweaver/client-web typechecks + builds from the canonical mesh.proto, 8 vitest tests driving the real connection against an in-memory Connect+Deliver service — ack, RequestId correlation, streamId demux — wired into the RN app's src/live.ts), and the portal (builds + rendered). CI typechecks/tests all of it.

Needs your hardware / a running portal (the WIRE: follow-ups):

Python Code nodes — running Python through the bridge (built here; see PythonCodeNodes.md): the in-process kernel runs only C# (Roslyn), so a Code node with Language == "python" is routed over the mesh to a connected Python worker (clients/python: python -m meshweaver.worker), which executes the script and patches the run's Activity node — output surfaces identically to a C# run. The worker (execute_python + CodeWorker) is unit-tested; the .NET side (SubmitCodeRequest.Language, CodeNodeType re-targeting python, the markdown block forwarding its fence language) compiles under -warnaserror. Remaining: a live portal+worker run, the WIRE:-marked ActivityLog/SubmitCodeResponse shapes, and a worker pool (plus a node/bun worker — the routing branch is the one place that grows).

Reconnecting…
The connection to the server was interrupted. Trying to restore it…
Trying again…
The connection could not be restored. Reloading the page…
The server was updated. Reloading the page to pick up the latest version.