@playfast/reform-remote-web 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,48 @@
1
+ <div align="center">
2
+
3
+ # `@playfast/reform-remote-web`
4
+
5
+ **Browser/web client transport for reform's remote UI — with built-in reconnect.**
6
+
7
+ </div>
8
+
9
+ ---
10
+
11
+ A factory that wraps a `WebSocket` into a `RemoteTransport` for
12
+ [`@playfast/reform-remote`](https://www.npmjs.com/package/@playfast/reform-remote)'s `connect`. It
13
+ sends trigger `Invoke`s up and surfaces `Snapshot`/`Patches` down, JSON-framed.
14
+
15
+ ```tsx
16
+ import { createWebSocketClientTransport } from '@playfast/reform-remote-web'
17
+ import { RemoteUI, useConnectionStatus } from '@playfast/reform-remote'
18
+ import { views } from './views'
19
+
20
+ const transport = createWebSocketClientTransport({ url: 'ws://localhost:8080' })
21
+
22
+ const App = () => {
23
+ const status = useConnectionStatus(transport) // 'connecting' | 'open' | 'reconnecting' | 'closed'
24
+ return (
25
+ <>
26
+ {status !== 'open' ? <div>{status}…</div> : null}
27
+ <RemoteUI transport={transport} views={views} />
28
+ </>
29
+ )
30
+ }
31
+ ```
32
+
33
+ **Reconnection is built in.** A dropped socket is retried with exponential backoff
34
+ (`baseDelayMs`/`maxDelayMs`), and because the message handlers persist across sockets, the
35
+ server's first frame on the new connection — always a `Snapshot` — re-syncs the tree
36
+ automatically, with no stale nodes left behind. The returned value *is* a `RemoteTransport`
37
+ (pass it straight to `connect` or `<RemoteUI>`) that additionally carries `status()`,
38
+ `onStatusChange()` — together a `StatusReporter`, so `useConnectionStatus` drives the badge
39
+ above — and `close()` to stop reconnecting.
40
+
41
+ Works anywhere a global `WebSocket` exists — browsers, Bun, and Node 22+. Elsewhere, inject one
42
+ via the `WebSocket` option. Pair it with a server adapter:
43
+ [`@playfast/reform-remote-node`](https://www.npmjs.com/package/@playfast/reform-remote-node) or
44
+ [`@playfast/reform-remote-bun`](https://www.npmjs.com/package/@playfast/reform-remote-bun).
45
+
46
+ ## License
47
+
48
+ MIT
@@ -0,0 +1,22 @@
1
+ import { type Cause } from 'effect';
2
+ /**
3
+ * Tagged errors for the web client transport. Every failure carries a `_tag` and
4
+ * structured fields — never a bare `Error` — so callers can match with Effect
5
+ * `catchTag` / `Match`. Tags are namespaced by package to stay globally unique.
6
+ */
7
+ /**
8
+ * The constructor shape `Data.TaggedError(tag)<A>` produces, named so the
9
+ * generated `.d.ts` can describe the `extends` base under `isolatedDeclarations`
10
+ * (which forbids an inferred expression in an extends clause).
11
+ */
12
+ type TaggedErrorClass<Tag extends string, A extends Record<string, unknown>> = new (args: A) => Cause.YieldableError & {
13
+ readonly _tag: Tag;
14
+ } & Readonly<A>;
15
+ declare const MissingWebSocketBase: TaggedErrorClass<'@playfast/reform-remote-web/MissingWebSocket', {
16
+ readonly message: string;
17
+ }>;
18
+ /** Thrown when no `WebSocket` is available and none was injected via options. */
19
+ export declare class MissingWebSocket extends MissingWebSocketBase {
20
+ }
21
+ export {};
22
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAQ,MAAM,QAAQ,CAAA;AAEzC;;;;GAIG;AAEH;;;;GAIG;AACH,KAAK,gBAAgB,CAAC,GAAG,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,KAC7E,IAAI,EAAE,CAAC,KACJ,KAAK,CAAC,cAAc,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAA;CAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;AAEhE,QAAA,MAAM,oBAAoB,EAAE,gBAAgB,CAC1C,8CAA8C,EAC9C;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAG5B,CAAA;AAEF,iFAAiF;AACjF,qBAAa,gBAAiB,SAAQ,oBAAoB;CAAG"}
package/dist/errors.js ADDED
@@ -0,0 +1,6 @@
1
+ import { Data } from 'effect';
2
+ const MissingWebSocketBase = (Data.TaggedError('@playfast/reform-remote-web/MissingWebSocket'));
3
+ /** Thrown when no `WebSocket` is available and none was injected via options. */
4
+ export class MissingWebSocket extends MissingWebSocketBase {
5
+ }
6
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,IAAI,EAAE,MAAM,QAAQ,CAAA;AAiBzC,MAAM,oBAAoB,GAGtB,CAAA,IAAI,CAAC,WAAW,CAAC,8CAA8C,CAEjE,CAAA,CAAA;AAEF,iFAAiF;AACjF,MAAM,OAAO,gBAAiB,SAAQ,oBAAoB;CAAG"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `@playfast/reform-remote-web` — the browser/web client side of the remote
3
+ * transport. Public API is exposed two ways (Effect-style): namespace barrels for
4
+ * discovery (`import { Transport } from "@playfast/reform-remote-web"`) and
5
+ * per-path subpaths for direct use (`import { createWebSocketClientTransport }
6
+ * from "@playfast/reform-remote-web/transport"`). The select re-exports below keep
7
+ * the common entry points nameable from the root.
8
+ */
9
+ export * as Transport from './transport.js';
10
+ export * as Errors from './errors.js';
11
+ export { type ClientStatus, createWebSocketClientTransport, type WebSocketClientTransport, type WebSocketClientTransportOptions, } from './transport.js';
12
+ export { MissingWebSocket } from './errors.js';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAC3C,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AAErC,OAAO,EACL,KAAK,YAAY,EACjB,8BAA8B,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,+BAA+B,GACrC,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `@playfast/reform-remote-web` — the browser/web client side of the remote
3
+ * transport. Public API is exposed two ways (Effect-style): namespace barrels for
4
+ * discovery (`import { Transport } from "@playfast/reform-remote-web"`) and
5
+ * per-path subpaths for direct use (`import { createWebSocketClientTransport }
6
+ * from "@playfast/reform-remote-web/transport"`). The select re-exports below keep
7
+ * the common entry points nameable from the root.
8
+ */
9
+ export * as Transport from './transport.js';
10
+ export * as Errors from './errors.js';
11
+ export { createWebSocketClientTransport, } from './transport.js';
12
+ export { MissingWebSocket } from './errors.js';
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAC3C,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AAErC,OAAO,EAEL,8BAA8B,GAG/B,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1,55 @@
1
+ import { Redacted } from 'effect';
2
+ import type { InvokeMessage, RemoteTransport, ServerMessage } from '@playfast/reform-remote';
3
+ /**
4
+ * The browser/web client side of the remote transport. A factory that wraps a
5
+ * WebSocket into a `RemoteTransport` you hand to `connect({ transport, views })`:
6
+ * it sends `Invoke`s up and surfaces `Snapshot`/`Patches` down, with JSON framing.
7
+ *
8
+ * It owns reconnection so the caller doesn't have to: a dropped socket is retried
9
+ * with exponential backoff, and because the message handlers persist across
10
+ * sockets, the server's first frame on the new connection — always a `Snapshot` —
11
+ * re-syncs the tree automatically (no stale nodes leak). Works anywhere a global
12
+ * `WebSocket` exists (browsers, Bun, Node 22+); inject one via `options` otherwise.
13
+ * There is no React hook — this is the transport only.
14
+ */
15
+ export type ClientStatus = 'connecting' | 'open' | 'reconnecting' | 'closed';
16
+ /**
17
+ * Options for {@link createWebSocketClientTransport}. A single named-options object
18
+ * (no positional args) so every reform-remote adapter constructor has the same
19
+ * shape and the client transports stay drop-in swappable.
20
+ */
21
+ export interface WebSocketClientTransportOptions {
22
+ /** The WebSocket URL to connect to (e.g. `ws://127.0.0.1:8787/reform`). */
23
+ readonly url: string;
24
+ /** First reconnect delay; doubles each attempt up to `maxDelayMs`. Default 250ms. */
25
+ readonly baseDelayMs?: number;
26
+ /** Ceiling for the backoff delay. Default 5000ms. */
27
+ readonly maxDelayMs?: number;
28
+ /** Subprotocols passed to the `WebSocket` constructor. */
29
+ readonly protocols?: string | ReadonlyArray<string>;
30
+ /**
31
+ * A bearer token to authenticate the connection. A browser `WebSocket` can't set headers,
32
+ * so the only header-like channel is the subprotocol list: the token rides as an extra
33
+ * `bearer.<token>` subprotocol alongside any {@link protocols}. The server reads it off
34
+ * `Sec-WebSocket-Protocol` at the upgrade. Held `Redacted` so it never logs.
35
+ */
36
+ readonly authToken?: Redacted.Redacted<string>;
37
+ /** The `WebSocket` implementation to use. Defaults to the global. */
38
+ readonly WebSocket?: typeof WebSocket;
39
+ /** Notified on every connection-status transition. */
40
+ readonly onStatus?: (status: ClientStatus) => void;
41
+ }
42
+ /** A `RemoteTransport` that also exposes its connection status and a manual close. */
43
+ export type WebSocketClientTransport = RemoteTransport<InvokeMessage, ServerMessage> & {
44
+ readonly status: () => ClientStatus;
45
+ /**
46
+ * Subscribe to status transitions (returns an unsubscribe). With `status()`
47
+ * this satisfies the React binding's `StatusReporter`, so `useConnectionStatus`
48
+ * can drive a reconnecting badge with no manual store.
49
+ */
50
+ readonly onStatusChange: (listener: () => void) => () => void;
51
+ /** Stop reconnecting and close the socket. The transport is inert afterwards. */
52
+ readonly close: () => void;
53
+ };
54
+ export declare const createWebSocketClientTransport: (options: WebSocketClientTransportOptions) => WebSocketClientTransport;
55
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AACjC,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAG5F;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,MAAM,GAAG,cAAc,GAAG,QAAQ,CAAA;AAE5E;;;;GAIG;AACH,MAAM,WAAW,+BAA+B;IAC9C,2EAA2E;IAC3E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,qFAAqF;IACrF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,qDAAqD;IACrD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAA;IACnD;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;IAC9C,qEAAqE;IACrE,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,SAAS,CAAA;IACrC,sDAAsD;IACtD,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAA;CACnD;AAED,sFAAsF;AACtF,MAAM,MAAM,wBAAwB,GAAG,eAAe,CAAC,aAAa,EAAE,aAAa,CAAC,GAAG;IACrF,QAAQ,CAAC,MAAM,EAAE,MAAM,YAAY,CAAA;IACnC;;;;OAIG;IACH,QAAQ,CAAC,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAA;IAC7D,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAA;CAC3B,CAAA;AAYD,eAAO,MAAM,8BAA8B,GACzC,SAAS,+BAA+B,KACvC,wBAuGF,CAAA"}
@@ -0,0 +1,108 @@
1
+ import { Redacted } from 'effect';
2
+ import { MissingWebSocket } from './errors.js';
3
+ const resolveWebSocket = (injected) => {
4
+ const impl = injected ?? globalThis.WebSocket;
5
+ if (impl === undefined) {
6
+ throw new MissingWebSocket({
7
+ message: 'No global WebSocket; pass `WebSocket` in options (e.g. the `ws` package in Node <22).',
8
+ });
9
+ }
10
+ return impl;
11
+ };
12
+ export const createWebSocketClientTransport = (options) => {
13
+ const url = options.url;
14
+ const WS = resolveWebSocket(options.WebSocket);
15
+ const baseDelayMs = options.baseDelayMs ?? 250;
16
+ const maxDelayMs = options.maxDelayMs ?? 5000;
17
+ // Normalize `protocols` to an array and fold in the auth token as a `bearer.<token>`
18
+ // subprotocol. Computed once (the token doesn't change across reconnects). `undefined`
19
+ // when there's nothing to send, so the no-auth path matches the original behavior.
20
+ const baseProtocols = options.protocols === undefined
21
+ ? []
22
+ : typeof options.protocols === 'string'
23
+ ? [options.protocols]
24
+ : options.protocols;
25
+ const protocols = options.authToken !== undefined
26
+ ? [`bearer.${Redacted.value(options.authToken)}`, ...baseProtocols]
27
+ : baseProtocols.length === 0
28
+ ? undefined
29
+ : baseProtocols;
30
+ const handlers = new Set();
31
+ const statusListeners = new Set();
32
+ const queue = [];
33
+ // A const holder whose fields swap — the codebase's no-`let` idiom.
34
+ const state = { socket: null, status: 'connecting', attempts: 0, timer: null };
35
+ const setStatus = (status) => {
36
+ if (status === state.status)
37
+ return;
38
+ state.status = status;
39
+ options.onStatus?.(status);
40
+ for (const listener of statusListeners)
41
+ listener();
42
+ };
43
+ const flush = () => {
44
+ const socket = state.socket;
45
+ if (socket === null || socket.readyState !== WS.OPEN)
46
+ return;
47
+ const pending = queue.splice(0, queue.length);
48
+ for (const message of pending)
49
+ socket.send(JSON.stringify(message));
50
+ };
51
+ const scheduleReconnect = () => {
52
+ if (state.status === 'closed')
53
+ return;
54
+ setStatus('reconnecting');
55
+ const delay = Math.min(maxDelayMs, baseDelayMs * 2 ** state.attempts);
56
+ state.attempts += 1;
57
+ state.timer = setTimeout(connect, delay);
58
+ };
59
+ // A drop while live (or a failed connect) backs off and retries. A socket can
60
+ // fire both `error` and `close`, so guard on identity — null out the current
61
+ // socket on the first drop, so the second event is a no-op and only one
62
+ // reconnect is scheduled. A user `close` flips the status, making this inert.
63
+ const handleDrop = (socket) => {
64
+ if (state.socket !== socket)
65
+ return;
66
+ state.socket = null;
67
+ scheduleReconnect();
68
+ };
69
+ function connect() {
70
+ const socket = protocols === undefined ? new WS(url) : new WS(url, [...protocols]);
71
+ state.socket = socket;
72
+ socket.addEventListener('open', () => {
73
+ state.attempts = 0;
74
+ setStatus('open');
75
+ flush();
76
+ });
77
+ socket.addEventListener('message', (event) => {
78
+ const message = JSON.parse(event.data);
79
+ for (const handler of handlers)
80
+ handler(message);
81
+ });
82
+ socket.addEventListener('close', () => handleDrop(socket));
83
+ socket.addEventListener('error', () => handleDrop(socket));
84
+ }
85
+ connect();
86
+ return {
87
+ send: (message) => {
88
+ queue.push(message);
89
+ flush();
90
+ },
91
+ onMessage: (handler) => {
92
+ handlers.add(handler);
93
+ return () => void handlers.delete(handler);
94
+ },
95
+ status: () => state.status,
96
+ onStatusChange: (listener) => {
97
+ statusListeners.add(listener);
98
+ return () => void statusListeners.delete(listener);
99
+ },
100
+ close: () => {
101
+ setStatus('closed');
102
+ if (state.timer !== null)
103
+ clearTimeout(state.timer);
104
+ state.socket?.close();
105
+ },
106
+ };
107
+ };
108
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@playfast/reform-remote-web",
3
+ "playbook": "./playbook",
4
+ "version": "0.0.2",
5
+ "type": "module",
6
+ "description": "Browser/web WebSocket client transport for reform-remote — streams a server-run reform scene to a thin renderer.",
7
+ "keywords": [
8
+ "reform",
9
+ "effect",
10
+ "websocket",
11
+ "remote",
12
+ "transport"
13
+ ],
14
+ "license": "MIT",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/playfast/reform.git",
18
+ "directory": "packages/reform-remote-web"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/playfast/reform/issues"
22
+ },
23
+ "sideEffects": false,
24
+ "exports": {
25
+ "./package.json": "./package.json",
26
+ ".": "./src/index.ts",
27
+ "./*": "./src/*.ts"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "README.md"
32
+ ],
33
+ "scripts": {
34
+ "clean": "rm -rf dist .tsbuildinfo",
35
+ "check": "tsc --noEmit",
36
+ "build": "tsc -p tsconfig.build.json",
37
+ "test": "vitest run",
38
+ "test:watch": "vitest",
39
+ "coverage": "vitest run --coverage",
40
+ "lint": "oxlint src",
41
+ "lint:fix": "oxlint --fix src"
42
+ },
43
+ "peerDependencies": {
44
+ "effect": "*",
45
+ "@playfast/reform-remote": "*"
46
+ },
47
+ "devDependencies": {
48
+ "@playfast/reform": "*",
49
+ "@playfast/reform-remote": "*",
50
+ "@playfast/reform-remote-node": "*",
51
+ "react": "^19.0.0"
52
+ },
53
+ "publishConfig": {
54
+ "access": "public"
55
+ }
56
+ }