@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 +48 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +6 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/transport.d.ts +55 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +108 -0
- package/dist/transport.js.map +1 -0
- package/package.json +56 -0
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
|
package/dist/errors.d.ts
ADDED
|
@@ -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"}
|
package/dist/index.d.ts
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 { 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":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAEjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAyD9C,MAAM,gBAAgB,GAAG,CAAC,QAAsC,EAAoB,EAAE;IACpF,MAAM,IAAI,GAAG,QAAQ,IAAK,UAA+C,CAAC,SAAS,CAAA;IACnF,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,gBAAgB,CAAC;YACzB,OAAO,EAAE,uFAAuF;SACjG,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAC5C,OAAwC,EACd,EAAE;IAC5B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAA;IACvB,MAAM,EAAE,GAAG,gBAAgB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IAC9C,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,GAAG,CAAA;IAC9C,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,IAAI,CAAA;IAE7C,qFAAqF;IACrF,uFAAuF;IACvF,mFAAmF;IACnF,MAAM,aAAa,GACjB,OAAO,CAAC,SAAS,KAAK,SAAS;QAC7B,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ;YACrC,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;YACrB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAA;IACzB,MAAM,SAAS,GACb,OAAO,CAAC,SAAS,KAAK,SAAS;QAC7B,CAAC,CAAC,CAAC,UAAU,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,GAAG,aAAa,CAAC;QACnE,CAAC,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC;YAC1B,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,aAAa,CAAA;IAErB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAoC,CAAA;IAC5D,MAAM,eAAe,GAAG,IAAI,GAAG,EAAc,CAAA;IAC7C,MAAM,KAAK,GAAyB,EAAE,CAAA;IACtC,oEAAoE;IACpE,MAAM,KAAK,GAKP,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IAEpE,MAAM,SAAS,GAAG,CAAC,MAAoB,EAAQ,EAAE;QAC/C,IAAI,MAAM,KAAK,KAAK,CAAC,MAAM;YAAE,OAAM;QACnC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAA;QACrB,OAAO,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAA;QAC1B,KAAK,MAAM,QAAQ,IAAI,eAAe;YAAE,QAAQ,EAAE,CAAA;IACpD,CAAC,CAAA;IAED,MAAM,KAAK,GAAG,GAAS,EAAE;QACvB,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAA;QAC3B,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,UAAU,KAAK,EAAE,CAAC,IAAI;YAAE,OAAM;QAC5D,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;QAC7C,KAAK,MAAM,OAAO,IAAI,OAAO;YAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAA;IACrE,CAAC,CAAA;IAED,MAAM,iBAAiB,GAAG,GAAS,EAAE;QACnC,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ;YAAE,OAAM;QACrC,SAAS,CAAC,cAAc,CAAC,CAAA;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,WAAW,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAA;QACrE,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAA;QACnB,KAAK,CAAC,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IAC1C,CAAC,CAAA;IAED,8EAA8E;IAC9E,6EAA6E;IAC7E,wEAAwE;IACxE,8EAA8E;IAC9E,MAAM,UAAU,GAAG,CAAC,MAAiB,EAAQ,EAAE;QAC7C,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM;YAAE,OAAM;QACnC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAA;QACnB,iBAAiB,EAAE,CAAA;IACrB,CAAC,CAAA;IAED,SAAS,OAAO;QACd,MAAM,MAAM,GAAG,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAA;QAClF,KAAK,CAAC,MAAM,GAAG,MAAM,CAAA;QACrB,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE;YACnC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAA;YAClB,SAAS,CAAC,MAAM,CAAC,CAAA;YACjB,KAAK,EAAE,CAAA;QACT,CAAC,CAAC,CAAA;QACF,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,KAAmB,EAAE,EAAE;YACzD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAc,CAAkB,CAAA;YACjE,KAAK,MAAM,OAAO,IAAI,QAAQ;gBAAE,OAAO,CAAC,OAAO,CAAC,CAAA;QAClD,CAAC,CAAC,CAAA;QACF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAA;QAC1D,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAA;IAC5D,CAAC;IAED,OAAO,EAAE,CAAA;IAET,OAAO;QACL,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE;YAChB,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YACnB,KAAK,EAAE,CAAA;QACT,CAAC;QACD,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE;YACrB,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;YACrB,OAAO,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QAC5C,CAAC;QACD,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM;QAC1B,cAAc,EAAE,CAAC,QAAQ,EAAE,EAAE;YAC3B,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;YAC7B,OAAO,GAAG,EAAE,CAAC,KAAK,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;QACpD,CAAC;QACD,KAAK,EAAE,GAAG,EAAE;YACV,SAAS,CAAC,QAAQ,CAAC,CAAA;YACnB,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI;gBAAE,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;YACnD,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,CAAA;QACvB,CAAC;KACF,CAAA;AACH,CAAC,CAAA"}
|
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
|
+
}
|