Skip to content

Map server integration#86

Merged
gmaclennan merged 24 commits into
mainfrom
map-server-integration
Jun 19, 2026
Merged

Map server integration#86
gmaclennan merged 24 commits into
mainfrom
map-server-integration

Conversation

@gmaclennan

Copy link
Copy Markdown
Member

Integrates @comapeo/map-server into the backend so the app can render maps from a local HTTP server.

Changes

  • Add createMapServer which starts a @comapeo/map-server instance (fallback + custom SMP maps, online style fallback) keyed off the device root key.
  • Start the map server during backend boot and expose its base URL to the app over a new app RPC channel (appRpcClient / createAppRpcServer).
  • Rename ComapeoRpcServerComapeoRpc; it now serves both the Mapeo manager and the map server over a single message port, closing both on disconnect.
  • Wrap the map server in a StartStopStateMachine so listen()/close() are idempotent and reconnects don't hit ERR_SERVER_ALREADY_LISTEN.
  • Update example app to use the app RPC client; add an e2e map-server test.
  • Bump to @comapeo/ipc prerelease, refresh patches/lockfiles, update @mapeo/crypto.

Warning

Draft — work in progress. Tests are still being fixed.

🤖 Generated with Claude Code

achou11 and others added 12 commits May 21, 2026 10:58
* origin/main:
  chore(e2e): add e2e tests on browserstack via Maestro (#56)
  fix(sentry): make exit telemetry lossless and stop cross-process clobbering (#84)
  feat(sentry): land Phases 6 + 7a — Android exit reasons & iOS MetricKit app-exit telemetry (#72)
  chore(build): use npm list instead of custom traversal to get native module versions (#70)
@socket-security

socket-security Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updated@​comapeo/​ipc@​8.0.0 ⏵ 9.0.0-pre.276 +210099 +496100

View full report

@gmaclennan gmaclennan marked this pull request as ready for review June 17, 2026 13:58
gmaclennan and others added 11 commits June 18, 2026 16:08
Adopt the breaking factory/type renames: Mapeo/AppRpc -> ComapeoCore/
ComapeoServices. Rename the public `appRpcClient` export to
`comapeoServicesClient` to match. The services implementation type now
comes from `@comapeo/ipc/server.js` (ComapeoServicesApi).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* origin/main:
  feat(sentry): migrate to @sentry/react-native v8; exit telemetry as Application Metrics (#73)
The embedded map server serves tiles over cleartext HTTP on 127.0.0.1.
Release builds block cleartext by default (Android targetSdk 28+, iOS
ATS), so `fetch()` to the local server fails — the map server was
unusable in release. Scope the exception to loopback only:

- Android: a network-security-config permitting cleartext for
  127.0.0.1/localhost, referenced from the manifest. The rest of the
  app keeps the secure default.
- iOS: NSAllowsLocalNetworking, which lifts ATS for loopback/.local
  while keeping it enforced for the public internet.

Verified on an Android emulator: the e2e suite goes from 37/38 to 38/38
(both map-server specs pass).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Android BrowserStack run overran the 30-min poll: seven real
devices serialise behind a small parallel-session quota, and the
~240 MB app installs slowly per device. Reduce to three devices
spanning the supported OS range (8.1 / 11 / 16) and drop the poll
timeout 1800s -> 1200s on both platforms so a stuck run fails sooner.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
nodejs-mobile on iOS is a jitless Node 18 build (v8_enable_webassembly
=false) that ships without the undici-backed global fetch/Response/
Request/Headers — `new Response(...)` throws "Response is not defined"
(ReadableStream is present). @comapeo/map-server serves over
@whatwg-node + itty-router, both of which construct Response/Request
against the globals, so the HTTP handler threw and never replied: every
request to the in-process map server hung until the client timed out.
Android's nodejs-mobile build keeps these globals, so it was unaffected.

install-fetch.js pulls the implementations from undici (which loads
because install-polywasm runs first) onto globalThis, wired into the iOS
entry right after the WebAssembly polyfill. Verified on BrowserStack
(iPhone 15/17.5 + iPhone 17/26.5): the map server now answers and the
e2e suite is 38/38.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The upload/trigger curls used --fail, which discards the JSON error
body (e.g. queue/parallel-limit messages) and leaves only an opaque
exit code. Capture body + status separately and print the real message
on failure, so a queue-limit rejection is diagnosable instead of an
anonymous non-zero exit.

Also drop the Android matrix to two reliably-available devices: the
2018 Galaxy Note 9 sat queued behind the BrowserStack parallel quota
long enough to overrun the poll timeout while the other devices had
already passed. Keeping the set small (4 sessions total with iOS) keeps
a run inside the quota.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Black-box checks that the in-process map server actually serves over
loopback on device — the nodejs-mobile request→response pipeline, which
differs from plain Node (see the fetch-globals fix). Covers the
fallback map style.json (200 JSON), info, a structured 404, and CORS
headers, using only the built-in fallback map (no project, no uploaded
SMP, no network). The module's own suite covers route/logic behaviour
in Node; these cover the on-device integration. Assertions verified by
running the map server in Node locally; the suite passes on BrowserStack
(iPhone 15/17.5 + iPhone 17/26.5), 41/41.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cover the map-server addition: the `comapeoServicesClient.mapServer`
export and `getBaseUrl()`, the built-in `fallback`/`default`/`custom`
map IDs, and the plugin's loopback-scoped cleartext exception that lets
release builds reach the local HTTP server.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
BrowserStack real devices route app traffic through a system HTTP proxy,
and Android applies it to 127.0.0.1 too (no loopback exclusion list), so
React Native's OkHttp sent the in-process map server's loopback requests
to the proxy — which can't reach the device's own loopback and answers
503 with an HTML error page. Only the map-server HTTP tests failed on CI
(the unix-socket RPC tests don't use OkHttp); they passed locally where
no proxy is configured.

Install an OkHttp ProxySelector that bypasses the proxy for loopback
hosts and delegates everything else to the system default. Scoped to the
e2e app via a config plugin (android/ is generated by expo prebuild), so
the published module and production apps are untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rver boot/shutdown

The map server is now created and listened to directly in index.js (its
listen promise feeds getBaseUrl), so the StartStopStateMachine-based
MapServerApi wrapper in comapeo-rpc.js and its start-stop-state-machine
dependency are dead. Remove them.

Also:
- index.js: isolate each shutdown close() so one failure can't leak the
  others; treat the map server as non-critical at boot (a listen()
  rejection surfaces only to getBaseUrl() callers, never trips the
  global unhandledRejection handler).
- comapeo-rpc.js: register the messagePort "close" listener before
  start() flushes queued messages, so a socket that closes mid-flush
  can't leak the core/services servers.
- index.ios.js: add assert-webassembly.js between install-polywasm and
  install-fetch to turn a wrong import order into a clear failure.
- run-browserstack-maestro action: dump the per-session report on a
  non-pass terminal state so the real Maestro failure is visible in CI.
- Add unit tests for comapeo-rpc, create-map-server, message-port and
  simple-rpc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The removed comment claimed registering the close listener before
start() avoids a leak from a socket closing "during the flush", but the
setup is fully synchronous so no close event can interleave start() in
either ordering. The handler closing both servers on disconnect is
self-explanatory.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@gmaclennan gmaclennan enabled auto-merge (squash) June 19, 2026 09:18
@gmaclennan gmaclennan merged commit 5caff5f into main Jun 19, 2026
11 checks passed
@gmaclennan gmaclennan deleted the map-server-integration branch June 19, 2026 09:29
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.

2 participants