@playfast/reform-remote-node 1.1.0 → 1.1.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/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -0
- package/package.json +17 -5
- package/src/index.ts +10 -4
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Server } from 'node:http';
|
|
2
|
+
import { WebSocketServer } from 'ws';
|
|
3
|
+
import type { AnyScene } from '@playfast/reform/internal';
|
|
4
|
+
export interface NodeWebSocketOptionsExternalApi {
|
|
5
|
+
readonly scene: AnyScene;
|
|
6
|
+
readonly port?: number;
|
|
7
|
+
readonly server?: Server;
|
|
8
|
+
readonly path?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface NodeWebSocketServer {
|
|
11
|
+
readonly wss: WebSocketServer;
|
|
12
|
+
readonly stop: () => Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export declare const serveNodeWebSocket: (options: NodeWebSocketOptionsExternalApi) => NodeWebSocketServer;
|
|
15
|
+
//# 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;AAEvC,OAAO,EAAgB,eAAe,EAAE,MAAM,IAAI,CAAA;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAA;AAQzD,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAA;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,GAAG,EAAE,eAAe,CAAA;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CACnC;AAoDD,eAAO,MAAM,kBAAkB,GAC7B,SAAS,+BAA+B,KACvC,mBA4CF,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Option, Schema } from 'effect';
|
|
2
|
+
import { WebSocketServer } from 'ws';
|
|
3
|
+
import { serveShared, } from '@playfast/reform-remote';
|
|
4
|
+
const WirePropSchema = Schema.Union(Schema.Struct({
|
|
5
|
+
_tag: Schema.Literal('Data'),
|
|
6
|
+
name: Schema.String,
|
|
7
|
+
value: Schema.Unknown,
|
|
8
|
+
}), Schema.Struct({
|
|
9
|
+
_tag: Schema.Literal('Event'),
|
|
10
|
+
name: Schema.String,
|
|
11
|
+
handle: Schema.String,
|
|
12
|
+
}));
|
|
13
|
+
const WireNodeSchema = Schema.Struct({
|
|
14
|
+
id: Schema.String,
|
|
15
|
+
name: Schema.String,
|
|
16
|
+
parentId: Schema.NullOr(Schema.String),
|
|
17
|
+
childIndex: Schema.Number,
|
|
18
|
+
slot: Schema.NullOr(Schema.String),
|
|
19
|
+
key: Schema.NullOr(Schema.String),
|
|
20
|
+
props: Schema.Array(WirePropSchema),
|
|
21
|
+
});
|
|
22
|
+
const WirePatchSchema = Schema.Union(Schema.Struct({ _tag: Schema.Literal('Upsert'), node: WireNodeSchema }), Schema.Struct({ _tag: Schema.Literal('Delete'), id: Schema.String }));
|
|
23
|
+
const InvokeMessageJson = Schema.parseJson(Schema.Struct({
|
|
24
|
+
_tag: Schema.Literal('Invoke'),
|
|
25
|
+
handle: Schema.String,
|
|
26
|
+
payload: Schema.Unknown,
|
|
27
|
+
}));
|
|
28
|
+
const ServerMessageJson = Schema.parseJson(Schema.Union(Schema.Struct({
|
|
29
|
+
_tag: Schema.Literal('Snapshot'),
|
|
30
|
+
tree: Schema.Array(WireNodeSchema),
|
|
31
|
+
}), Schema.Struct({
|
|
32
|
+
_tag: Schema.Literal('Patches'),
|
|
33
|
+
patches: Schema.Array(WirePatchSchema),
|
|
34
|
+
})));
|
|
35
|
+
const decodeInvokeMessage = Schema.decodeUnknownSync(InvokeMessageJson);
|
|
36
|
+
const encodeServerMessage = Schema.encodeSync(ServerMessageJson);
|
|
37
|
+
const parseInvoke = (frame) => decodeInvokeMessage(frame.toString());
|
|
38
|
+
export const serveNodeWebSocket = (options) => {
|
|
39
|
+
const serverConfig = options.server !== undefined
|
|
40
|
+
? { server: options.server, path: options.path }
|
|
41
|
+
: { port: options.port === undefined ? 0 : options.port, path: options.path };
|
|
42
|
+
const wss = new WebSocketServer(serverConfig);
|
|
43
|
+
const shared = serveShared({ scene: options.scene });
|
|
44
|
+
wss.on('connection', (socket) => {
|
|
45
|
+
const handlers = new Set();
|
|
46
|
+
const transport = {
|
|
47
|
+
send: (message) => void socket.send(encodeServerMessage(message)),
|
|
48
|
+
onMessage: (handler) => {
|
|
49
|
+
handlers.add(handler);
|
|
50
|
+
return () => void handlers.delete(handler);
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
socket.on('message', (frame) => {
|
|
54
|
+
const message = parseInvoke(frame);
|
|
55
|
+
handlers.forEach((handler) => handler(message));
|
|
56
|
+
});
|
|
57
|
+
const handle = shared.addClient(transport);
|
|
58
|
+
socket.on('close', () => handle.remove());
|
|
59
|
+
});
|
|
60
|
+
return {
|
|
61
|
+
wss,
|
|
62
|
+
stop: async () => {
|
|
63
|
+
await shared.dispose();
|
|
64
|
+
// `wss.close` waits for live sockets to drain.
|
|
65
|
+
wss.clients.forEach((client) => client.terminate());
|
|
66
|
+
await new Promise((resolve, reject) => {
|
|
67
|
+
wss.close((error) => (error === undefined ? resolve() : reject(error)));
|
|
68
|
+
// Bun's `ws` frees the port but never invokes that callback nor emits 'close',
|
|
69
|
+
// which would hang teardown forever. Both runtimes drop `address()` the moment
|
|
70
|
+
// the listener is gone, so that fact — not the signal — is what we settle on.
|
|
71
|
+
if (Option.isNone(Option.fromNullable(wss.address()))) {
|
|
72
|
+
resolve();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
//# 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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playfast/reform-remote-node",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Node WebSocket server adapter for reform-remote — serves a reform scene's UI over the `ws` package, one isolated runtime per connection.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"effect",
|
|
@@ -20,15 +20,26 @@
|
|
|
20
20
|
"directory": "packages/reform-remote-node"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
|
+
"dist",
|
|
23
24
|
"src",
|
|
24
25
|
"README.md"
|
|
25
26
|
],
|
|
26
27
|
"type": "module",
|
|
27
28
|
"sideEffects": false,
|
|
29
|
+
"main": "./dist/index.js",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
28
31
|
"exports": {
|
|
29
32
|
"./package.json": "./package.json",
|
|
30
|
-
".":
|
|
31
|
-
|
|
33
|
+
".": {
|
|
34
|
+
"playfast-src": "./src/index.ts",
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"default": "./dist/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./*": {
|
|
39
|
+
"playfast-src": "./src/*.ts",
|
|
40
|
+
"types": "./dist/*.d.ts",
|
|
41
|
+
"default": "./dist/*.js"
|
|
42
|
+
}
|
|
32
43
|
},
|
|
33
44
|
"publishConfig": {
|
|
34
45
|
"access": "public"
|
|
@@ -36,12 +47,13 @@
|
|
|
36
47
|
"scripts": {
|
|
37
48
|
"clean": "rm -rf dist .tsbuildinfo",
|
|
38
49
|
"check": "tsc --noEmit",
|
|
39
|
-
"build": "tsc -p tsconfig.build.json",
|
|
50
|
+
"build": "rm -rf dist .tsbuildinfo && tsc -p tsconfig.build.json && bun ../../scripts/fix-esm-extensions.ts dist",
|
|
40
51
|
"test": "vitest run",
|
|
41
52
|
"test:watch": "vitest",
|
|
42
53
|
"coverage": "vitest run --coverage",
|
|
43
54
|
"lint": "oxlint src",
|
|
44
|
-
"lint:fix": "oxlint --fix src"
|
|
55
|
+
"lint:fix": "oxlint --fix src",
|
|
56
|
+
"prepack": "bun run build"
|
|
45
57
|
},
|
|
46
58
|
"dependencies": {
|
|
47
59
|
"ws": "^8.18.0"
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Server } from 'node:http'
|
|
2
|
-
import { Schema } from 'effect'
|
|
2
|
+
import { Option, Schema } from 'effect'
|
|
3
3
|
import { type RawData, WebSocketServer } from 'ws'
|
|
4
4
|
import type { AnyScene } from '@playfast/reform/internal'
|
|
5
5
|
import {
|
|
@@ -106,9 +106,15 @@ export const serveNodeWebSocket = (
|
|
|
106
106
|
await shared.dispose()
|
|
107
107
|
// `wss.close` waits for live sockets to drain.
|
|
108
108
|
wss.clients.forEach((client) => client.terminate())
|
|
109
|
-
await new Promise<void>((resolve, reject) =>
|
|
110
|
-
wss.close((error) => (error === undefined ? resolve() : reject(error)))
|
|
111
|
-
|
|
109
|
+
await new Promise<void>((resolve, reject) => {
|
|
110
|
+
wss.close((error) => (error === undefined ? resolve() : reject(error)))
|
|
111
|
+
// Bun's `ws` frees the port but never invokes that callback nor emits 'close',
|
|
112
|
+
// which would hang teardown forever. Both runtimes drop `address()` the moment
|
|
113
|
+
// the listener is gone, so that fact — not the signal — is what we settle on.
|
|
114
|
+
if (Option.isNone(Option.fromNullable(wss.address()))) {
|
|
115
|
+
resolve()
|
|
116
|
+
}
|
|
117
|
+
})
|
|
112
118
|
},
|
|
113
119
|
}
|
|
114
120
|
}
|