@playfast/reform-remote-node 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 +34 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# `@playfast/reform-remote-node`
|
|
4
|
+
|
|
5
|
+
**Serve a reform scene's UI over WebSocket from Node, using the `ws` package.**
|
|
6
|
+
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
A concrete WebSocket server adapter for
|
|
12
|
+
[`@playfast/reform-remote`](https://www.npmjs.com/package/@playfast/reform-remote). Each
|
|
13
|
+
connection gets its own `serve({ scene, transport })` — its own isolated runtime — wrapped around the
|
|
14
|
+
socket with JSON framing. The wire messages are already `_tag`-discriminated and serializable, so
|
|
15
|
+
there is no envelope to write.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { serveNodeWebSocket } from '@playfast/reform-remote-node'
|
|
19
|
+
import { myScene } from './scene'
|
|
20
|
+
|
|
21
|
+
const host = serveNodeWebSocket({ scene: myScene, port: 8080 })
|
|
22
|
+
// …later
|
|
23
|
+
await host.stop()
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Pass `server` to attach to an existing HTTP server instead of opening a port, and `path` to
|
|
27
|
+
restrict upgrades. Connect from the browser (or Node/Bun) with
|
|
28
|
+
[`@playfast/reform-remote-web`](https://www.npmjs.com/package/@playfast/reform-remote-web); the
|
|
29
|
+
Bun-native server equivalent is
|
|
30
|
+
[`@playfast/reform-remote-bun`](https://www.npmjs.com/package/@playfast/reform-remote-bun).
|
|
31
|
+
|
|
32
|
+
## License
|
|
33
|
+
|
|
34
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Server } from 'node:http';
|
|
2
|
+
import { WebSocketServer } from 'ws';
|
|
3
|
+
import type { Scene } from '@playfast/reform';
|
|
4
|
+
/**
|
|
5
|
+
* @playfast/reform-remote-node — serve a reform scene's UI over WebSocket from a
|
|
6
|
+
* Node process, using the `ws` package. Each connection gets its own
|
|
7
|
+
* `serve({ scene, transport })` — its own isolated runtime — wrapped around the
|
|
8
|
+
* socket with JSON framing: the already-`_tag`-discriminated wire messages need
|
|
9
|
+
* no envelope. The Bun-native equivalent is `@playfast/reform-remote-bun`; the
|
|
10
|
+
* browser/web client is `@playfast/reform-remote-web`.
|
|
11
|
+
*/
|
|
12
|
+
export interface NodeWebSocketOptions {
|
|
13
|
+
/** The scene to run — a fresh runtime per connection. */
|
|
14
|
+
readonly scene: Scene;
|
|
15
|
+
/** Listen on this port (use `0` for an ephemeral port; read it back from `wss.address()`). */
|
|
16
|
+
readonly port?: number;
|
|
17
|
+
/** Attach to an existing HTTP server instead of opening a port. */
|
|
18
|
+
readonly server?: Server;
|
|
19
|
+
/** Restrict upgrades to this path. */
|
|
20
|
+
readonly path?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface NodeWebSocketServer {
|
|
23
|
+
/** The underlying `ws` server — for reading the bound address, or further config. */
|
|
24
|
+
readonly wss: WebSocketServer;
|
|
25
|
+
/** Dispose every live connection's runtime and close the server. */
|
|
26
|
+
readonly stop: () => Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
export declare const serveNodeWebSocket: (options: NodeWebSocketOptions) => NodeWebSocketServer;
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,EAAgB,eAAe,EAAE,MAAM,IAAI,CAAA;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAS7C;;;;;;;GAOG;AAEH,MAAM,WAAW,oBAAoB;IACnC,yDAAyD;IACzD,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,8FAA8F;IAC9F,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,mEAAmE;IACnE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,qFAAqF;IACrF,QAAQ,CAAC,GAAG,EAAE,eAAe,CAAA;IAC7B,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CACnC;AAID,eAAO,MAAM,kBAAkB,GAAI,SAAS,oBAAoB,KAAG,mBA8ClE,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { WebSocketServer } from 'ws';
|
|
2
|
+
import { serve, } from '@playfast/reform-remote';
|
|
3
|
+
const parseInvoke = (data) => JSON.parse(data.toString());
|
|
4
|
+
export const serveNodeWebSocket = (options) => {
|
|
5
|
+
const wss = options.server !== undefined
|
|
6
|
+
? new WebSocketServer({ server: options.server, path: options.path })
|
|
7
|
+
: new WebSocketServer({ port: options.port ?? 0, path: options.path });
|
|
8
|
+
// Every open connection's binding, so `stop` can tear them all down.
|
|
9
|
+
const bindings = new Set();
|
|
10
|
+
wss.on('connection', (socket) => {
|
|
11
|
+
const handlers = new Set();
|
|
12
|
+
const transport = {
|
|
13
|
+
send: (message) => socket.send(JSON.stringify(message)),
|
|
14
|
+
onMessage: (handler) => {
|
|
15
|
+
handlers.add(handler);
|
|
16
|
+
return () => void handlers.delete(handler);
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
socket.on('message', (data) => {
|
|
20
|
+
const message = parseInvoke(data);
|
|
21
|
+
for (const handler of handlers)
|
|
22
|
+
handler(message);
|
|
23
|
+
});
|
|
24
|
+
const binding = serve({ scene: options.scene, transport });
|
|
25
|
+
bindings.add(binding);
|
|
26
|
+
socket.on('close', () => {
|
|
27
|
+
bindings.delete(binding);
|
|
28
|
+
void binding.dispose();
|
|
29
|
+
});
|
|
30
|
+
// Push the first snapshot once the socket is live.
|
|
31
|
+
void binding.start();
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
wss,
|
|
35
|
+
stop: async () => {
|
|
36
|
+
await Promise.all([...bindings].map((binding) => binding.dispose()));
|
|
37
|
+
bindings.clear();
|
|
38
|
+
// Terminate live sockets so `wss.close` doesn't block waiting for them to
|
|
39
|
+
// drain on their own — `close` only fires its callback once all are gone.
|
|
40
|
+
for (const client of wss.clients)
|
|
41
|
+
client.terminate();
|
|
42
|
+
await new Promise((resolve, reject) => wss.close((error) => (error === undefined ? resolve() : reject(error))));
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@playfast/reform-remote-node",
|
|
3
|
+
"playbook": "./playbook",
|
|
4
|
+
"version": "0.0.2",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Node WebSocket server adapter for reform-remote — serves a reform scene's UI over the `ws` package, one isolated runtime per connection.",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"reform",
|
|
9
|
+
"effect",
|
|
10
|
+
"websocket",
|
|
11
|
+
"remote",
|
|
12
|
+
"node",
|
|
13
|
+
"server"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/playfast/reform.git",
|
|
19
|
+
"directory": "packages/reform-remote-node"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/playfast/reform/issues"
|
|
23
|
+
},
|
|
24
|
+
"sideEffects": false,
|
|
25
|
+
"exports": {
|
|
26
|
+
"./package.json": "./package.json",
|
|
27
|
+
".": "./src/index.ts",
|
|
28
|
+
"./*": "./src/*.ts"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"clean": "rm -rf dist .tsbuildinfo",
|
|
36
|
+
"check": "tsc --noEmit",
|
|
37
|
+
"build": "tsc -p tsconfig.build.json",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"test:watch": "vitest",
|
|
40
|
+
"coverage": "vitest run --coverage",
|
|
41
|
+
"lint": "oxlint src",
|
|
42
|
+
"lint:fix": "oxlint --fix src"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"ws": "^8.18.0"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"effect": "*",
|
|
49
|
+
"react": "^19.0.0",
|
|
50
|
+
"@playfast/reform": "*",
|
|
51
|
+
"@playfast/reform-remote": "*"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/ws": "^8.5.13"
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
58
|
+
}
|
|
59
|
+
}
|