sparkling-websocket
v0.1.0
Published
Native WebSocket for Lynx/PrimJS — Sparkling Method bridge + JS polyfill (Android & iOS)
Maintainers
Readme
@dollhousestudio/sparkling-websocket
Native WebSocket bridge for Sparkling / ReactLynx, so socket.io-client
(the Parcae SocketTransport) runs on Lynx PrimJS unchanged.
How it fits
┌─ pipe.call('Websocket.connect'/'send'/'close') ──┐
JS (PrimJS, bg thread) │ ▼
installWebSocket() │ ┌─────────────── native ────────────────┐
globalThis.WebSocket│ │ Android: OkHttp WebSocket │
▲ │ │ iOS: URLSessionWebSocketTask │
│ │ └───────────────┬───────────────────────┘
└─ pipe.on('Websocket.event') ◀── emit bridge.ln(event, json)- JS side (verified):
WebSocketPolyfillimplements the standardWebSocketinterface, backed by the three native methods. Socket events stream back through Lynx'sGlobalEventEmitter(pipe.on). The Parcae SDK client inapps/mobile/src/lib/client.tscallsinstallWebSocket()beforecreateClient({ transports: ['websocket'] }). The build isolates socket.io into a background chunk — the main-thread Lepus compiler never sees socket.io's regexes. - Native side (UNVERIFIED — needs Mac/Android): the codegen'd Kotlin
IDL is valid; the codegen'd Swift IDL is empty in v2.0.1 and must be
hand-written. See
android/.../WebsocketMethods.ktandios/.../WebsocketCenter.swift.
Status — verified on the Android emulator
| Layer | Verified |
|---|---|
| JS pipe wrappers (src/index.ts, src/event.ts) | ✅ typecheck + build |
| WebSocketPolyfill (src/polyfill.ts) + base64 (src/base64.ts) | ✅ runtime |
| socket.io on the background thread, main bundle clean | ✅ sparkling build |
| Kotlin impl (OkHttp) connects to dev-api (onOpen 101) | ✅ emulator |
| Binary gzip RPC responses → pako.ungzip → Project.find() returns rows | ✅ emulator |
| Swift impl (iOS) | ❌ needs Xcode 16+ (codegen Swift stubs are empty; hand-written) |
Verified end-to-end: the Parcae SocketTransport connects to
wss://dev-api.dollhouse.world/ws, completes the socket.io handshake + hello,
and a Project.find() RPC returns rows through the gzip+compress-json binary path.
Key native API (verified against sparkling-method 2.0.1)
- Override
handle(params, callback: CompletionBlock<OUTPUT>, type: BridgePlatformType). - Return success with
callback.onSuccess(resultModel, msg). Result models are generated interfaces (code,msg,data.socketId); instantiate them as anonymousobjects and implementconvert()+toJSON(). - Emit a JS event (received by
pipe.on) via the view event bus:
(getSDKContext()?.bridge?.sendEvent("Websocket.event", jsonObject)sendJSRuntimeEventtargets the background runtime, butpipe.onbinds the view'sGlobalEventEmitterin this setup —sendEventis what delivers.) - Binary frames: OkHttp's
onMessage(bytes: ByteString)→bytes.base64()in the event JSON withbinary: true; the JS polyfill decodes toArrayBuffer.
PrimJS compatibility (apps/mobile/src/lib/primjs-compat.ts)
socket.io-client / engine.io-client / @parcae/sdk assume browser-or-node globals
that PrimJS lacks. The shim (imported before @parcae/sdk) mirrors PrimJS's bare
timers onto globalThis and provides performance, process, queueMicrotask,
and structuredClone (used by the SDK's FrontendAdapter).
Build & wire into the app (on a Mac / Android box)
nvm use 24
pnpm --filter @dollhousestudio/sparkling-websocket codegen # regenerate native IDL
pnpm --filter @dollhousestudio/sparkling-websocket build # build dist/
pnpm --filter @dollhousestudio/mobile autolink # wire android/ios deps
pnpm --filter @dollhousestudio/mobile run:android # or run:iosAutolink updates android/settings.gradle.kts + app/build.gradle.kts and
the Podfile, and regenerates SparklingAutolink.kt / SparklingAutolink.swift.
Confirm the three Websocket*Method classes are registered in the global
method table (mirror the router/storage registrations in
SPKServiceRegistrar.swift / SparklingAutolink.kt).
Verifying on device
- Point at dev API:
DOLLHOUSE_API_URL=https://dev-api.dollhouse.world. - Open the app —
apps/mobilemainpage logs socket connection state. - Watch for
Websocket.connect→open→ socket.iohello/resyncround-trips. useQuerylive diffs should flow once the SDK reconnects.
