Skip to content

feat(windows): React Native Windows support (Nitro install DLL + Winsock transport) - #23

Draft
47PADO47 wants to merge 6 commits into
chore/upgrade-depsfrom
feat/windows-support
Draft

47PADO47 wants to merge 6 commits into
chore/upgrade-depsfrom
feat/windows-support

Conversation

@47PADO47

@47PADO47 47PADO47 commented Oct 5, 2026

Copy link
Copy Markdown
Member

Summary

Adds React Native Windows (New Architecture, RNW 0.84+) support, following the approach of StasDoskalenko/NitromelonDB#65.

Stacked on the dependency-upgrade PR (chore/upgrade-deps). Merge that first.

How it works

  • Nitro install shim. react-native-nitro-modules has no Windows project (mrousavy/nitro#168), so package/windows/Ecr17 (a WinAppSDK DLL) provides the NitroModules TurboModule. Its install() runs margelo::nitro::install with RNW's JSI runtime + CallInvoker, then registers Ecr17Client (the shared C++ core, unchanged) and Ecr17Transport.
  • Winsock transport. HybridEcr17TransportWindows implements the Ecr17Transport spec in C++ and mirrors the Kotlin transport: reader thread, onDisconnect once per unexpected drop (never on disconnect()), and a write-free, non-consuming pre-send liveness probe (instant select + recv(MSG_PEEK)), so a socket the terminal closed between transactions is found before a financial command is sent.
  • MSVC header map. scripts/windows-nitro-shims.mjs generates <NitroModules/…> shims; MSBuild runs it before compiling.
  • Autolinking. react-native.config.js gains a windows entry; apps spread windowsAppDependencies() from @padosoft/react-native-ecr17/windows-autolink to skip Nitro's missing Windows project.
  • Example. example-windows/ is a separate npm project (RNW 0.84 pins RN 0.84.1, while the Expo example is on 0.86): connect, status, totals, last result (G), one payment.

Money-safety is unchanged. RetryPolicy still never replays a financial command; a lost result is recovered with sendLastResult() (G).

Limitation: only one native module per app can provide the NitroModules TurboModule; another Nitro library using the same shim would conflict.

Verification (all local; no Windows CI job by decision)

  • Transport tests (package/windows/tests, CMake + GoogleTest, MSVC): 13/13 pass, and 30 repeated runs (390 test runs) pass. They cover drop detected before send (including while the reader is busy, so only the probe sees the FIN), probe never writes or consumes bytes, exactly one disconnect signal, none on caller disconnect, fast probe, reconnect isolation, and disconnect from inside a callback.
  • RNW build + MSIX deploy of example-windows (VS 2026, SDK 10.0.26100): Ecr17.dll and the app compile, link, package and install.
  • Runtime smoke test in the deployed app against a fake terminal (Node TCP server): Nitro installed via the DLL, createEcr17Client works, state events connecting → connected → disconnected, Winsock connect, a correct s (status) frame on the wire with 3 retransmits (no ACK after 4 attempts), and isConnected() == false after the terminal's FIN.
  • Example typecheck and a full react-native bundle --platform windows (no repo-root node_modules leaks in); docs check + build; README mirror in sync.

Found and fixed during verification

  • The probe was starved by the reader holding the I/O lock across its 100 ms select (MSVC mutexes are unfair: ~340 ms per isConnected()); dropping the lock entirely caused a blocking-peek hang (caught by a test). The reader now locks only around recv, and each test has a 30 s timeout.
  • connect() runs on its own thread instead of Nitro's shared pool, so connectionTimeoutMs still bounds it when the pool is busy.
  • Broken project path in Ecr17.sln, and the generated app vcxproj overriding the Windows SDK pin.

Not yet verified

  • A physical Nexi terminal (Windows is marked 🧪 in the README).
  • Two review notes left as-is: disconnect() joins the reader without a time limit (Kotlin uses 1 s); disconnect() during an in-flight connect() doesn't cancel it (same as Kotlin).

Local verification commands

cmake -S package/windows/tests -B build-win
cmake --build build-win --config Release
ctest --test-dir build-win -C Release --output-on-failure
cd example-windows && npm install && npx react-native run-windows --arch x64

run-windows needs Visual Studio's "Windows application development" workload (MSIX packaging). If its final launch step fails with a Get-AppxPackage error (PowerShell 7), the app is installed anyway: start Ecr17Example from the Start menu.

🤖 Generated with Claude Code

47PADO47 and others added 6 commits October 5, 2026 02:28
…ock transport)

Adds package/windows/Ecr17, a WinAppSDK DLL for RNW New Architecture,
following the approach of StasDoskalenko/NitromelonDB#65:

- react-native-nitro-modules has no Windows project, so the DLL provides the
  `NitroModules` TurboModule: install() runs margelo::nitro::install with
  RNW's JSI runtime + CallInvoker, then registers Ecr17Client (the shared C++
  core) and Ecr17Transport.
- HybridEcr17TransportWindows implements the Ecr17Transport spec in C++ with
  Winsock, mirroring the Kotlin transport: reader thread, onDisconnect once
  per unexpected drop, and a write-free, non-consuming pre-send liveness probe
  (instant select + MSG_PEEK). The reader waits without the I/O lock (MSVC
  mutexes are unfair) and locks only around recv; connect() runs on its own
  thread so it can't queue behind busy Nitro pool workers.
- Nitro platform hooks (ThreadUtils, Logger, UI dispatcher) for Windows.
- scripts/windows-nitro-shims.mjs generates the <NitroModules/...> header
  map MSVC lacks; MSBuild runs it before compiling.
- windows-autolink.js + react-native.config.js windows entry; apps spread
  windowsAppDependencies() to skip Nitro's missing Windows project.

Money-safety is unchanged: RetryPolicy still never replays a financial
command; recovery stays sendLastResult (G).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Standalone CMake + GoogleTest suite (no RNW needed) that builds the real
transport plus Nitro's C++ runtime and JSI, and runs it against a loopback
TCP server: drop detected before send (also while the reader is busy, so
only the probe can see the FIN), probe never writes or consumes bytes,
exactly one disconnect signal per drop, none on caller disconnect, fast
probe on an idle socket, reconnect isolation, and disconnect from inside
a callback. Per-test ctest TIMEOUT so a blocking probe fails instead of
hanging.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RNW 0.84 (cpp-app, New Architecture) console: connect, status, totals,
last result (G) and one payment. A separate npm project, not a bun
workspace: RNW 0.84 pins react-native 0.84.1 while the Expo example is on
0.86. Links the library with file:../package; Metro loads ../package/src
and resolves its bare imports from this app, so the repo root's RN never
leaks into the bundle. Windows SDK pinned to 10.0.26100 (the generated
app vcxproj no longer overrides the pin).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- README (package + root mirror): platforms badge, feature status, Windows
  install snippet, architecture tree; example now Expo SDK 57 / RN 0.86.
- docs-site: new Windows page (in Get Started nav) + installation section.
- AGENTS.md: Windows has no CI job; verify locally (transport tests + RNW
  app build). New package/cpp sources also go in Ecr17.vcxproj.
- LESSON.md: Windows lessons (Nitro install shim, header map, unfair MSVC
  mutex / blocking-peek race, SDK on D:, MSIX-only runtime, SDK pin trap).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RNW enables RestorePackagesWithLockFile; the first MSIX deploy generated
the packaging project's lock file. README: npm run windows needs VS's MSIX
packaging tools, and how to start the app when the final launch step fails
under PowerShell 7.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The deployed example app passed a runtime smoke test against a fake
terminal: Nitro install through Ecr17.dll, client creation, connection
state events, Winsock connect, a correct status frame with 3 retransmits,
and drop detection after the terminal's FIN. Still not run against a
physical terminal.

AGENTS.md/LESSON.md: local verification steps, installing VS's MSIX
packaging workload, the PowerShell 7 Get-AppxPackage launch failure and
how to launch the deployed app, Metro no longer printing console.log.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying react-native-ecr17-protocol with  Cloudflare Pages  Cloudflare Pages

Latest commit: 18adce1
Status:🚫  Build failed.

View logs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant