@jxsuite/server 2.1.0 → 2.2.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/package.json +12 -12
- package/src/project-server.ts +40 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"description": "Jx development server with live reload, server-side proxy, and studio integration",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -38,21 +38,21 @@
|
|
|
38
38
|
"upgrade": "bunx npm-check-updates -u && bun install"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@jxsuite/collab": "^0.
|
|
42
|
-
"@jxsuite/compiler": "^2.0.
|
|
43
|
-
"@jxsuite/create": "^1.3.
|
|
44
|
-
"@jxsuite/import": "^0.39.
|
|
45
|
-
"@jxsuite/protocol": "^1.1.
|
|
46
|
-
"@jxsuite/runtime": "^2.0.
|
|
47
|
-
"@jxsuite/schema": "^1.
|
|
48
|
-
"@jxsuite/starters": "^1.
|
|
41
|
+
"@jxsuite/collab": "^0.8.1",
|
|
42
|
+
"@jxsuite/compiler": "^2.0.3",
|
|
43
|
+
"@jxsuite/create": "^1.3.3",
|
|
44
|
+
"@jxsuite/import": "^0.39.3",
|
|
45
|
+
"@jxsuite/protocol": "^1.1.2",
|
|
46
|
+
"@jxsuite/runtime": "^2.0.2",
|
|
47
|
+
"@jxsuite/schema": "^1.8.0",
|
|
48
|
+
"@jxsuite/starters": "^1.6.0",
|
|
49
49
|
"chokidar": "^5.0.0",
|
|
50
50
|
"kysely": "^0.29.5",
|
|
51
51
|
"zod": "^4.4.3"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@jxsuite/auth": "^0.5.
|
|
55
|
-
"@jxsuite/connector": "^0.5.
|
|
56
|
-
"@jxsuite/parser": "^1.5.
|
|
54
|
+
"@jxsuite/auth": "^0.5.2",
|
|
55
|
+
"@jxsuite/connector": "^0.5.2",
|
|
56
|
+
"@jxsuite/parser": "^1.5.2"
|
|
57
57
|
}
|
|
58
58
|
}
|
package/src/project-server.ts
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
import { resolve, sep } from "node:path";
|
|
29
|
+
import type { ServerWebSocket } from "bun";
|
|
29
30
|
import { handleJxMounts } from "./jx-mounts.ts";
|
|
30
31
|
import { handleResolve, handleServerFunction, projectAssetMounts } from "./resolve.ts";
|
|
31
32
|
import { handleAiApi } from "./ai-api.ts";
|
|
@@ -77,6 +78,18 @@ export interface ProjectServerHandle {
|
|
|
77
78
|
wsUrl: string;
|
|
78
79
|
rpcToken: string;
|
|
79
80
|
canvasUrl: string;
|
|
81
|
+
/**
|
|
82
|
+
* Push an UNSOLICITED message to the connected shells — the server-to-client half of the RPC.
|
|
83
|
+
*
|
|
84
|
+
* Every other frame on this socket answers an `id` the client chose; these carry a `method` and
|
|
85
|
+
* no `id`, which is how the client's dispatcher tells the two apart. It exists because some
|
|
86
|
+
* things the shell must know are not answers to anything it asked: a file changed on disk, or
|
|
87
|
+
* another window wants this one raised.
|
|
88
|
+
*
|
|
89
|
+
* @param winId Deliver only to sockets stashed with this window id; omit for every socket.
|
|
90
|
+
* @returns How many sockets the message reached — 0 means nothing is listening yet.
|
|
91
|
+
*/
|
|
92
|
+
push: (method: string, params?: unknown, winId?: string | null) => number;
|
|
80
93
|
/**
|
|
81
94
|
* The RFC 8252 loopback authorization host. The desktop launcher drives it: `begin()` for the URL
|
|
82
95
|
* to open in the user's browser, then `await pending.code` and {@link exchangeCode}.
|
|
@@ -106,6 +119,11 @@ export function createProjectServer(options: CreateProjectServerOptions): Projec
|
|
|
106
119
|
/* Outstanding OAuth authorizations (RFC 8252). Per server, so a stopped server abandons them. */
|
|
107
120
|
const authorizer: LoopbackAuthorizer = createLoopbackAuthorizer();
|
|
108
121
|
|
|
122
|
+
/* Live shell sockets, so the server can speak first (see ProjectServerHandle.push). Membership is
|
|
123
|
+
the socket's whole lifetime — added on open, dropped on close — so a push after a window is
|
|
124
|
+
gone reaches nobody rather than throwing. */
|
|
125
|
+
const sockets = new Set<ServerWebSocket<{ winId: string | null }>>();
|
|
126
|
+
|
|
109
127
|
const server = Bun.serve<{ winId: string | null }>({
|
|
110
128
|
hostname,
|
|
111
129
|
port,
|
|
@@ -312,6 +330,12 @@ export function createProjectServer(options: CreateProjectServerOptions): Projec
|
|
|
312
330
|
},
|
|
313
331
|
|
|
314
332
|
websocket: {
|
|
333
|
+
open(ws) {
|
|
334
|
+
sockets.add(ws);
|
|
335
|
+
},
|
|
336
|
+
close(ws) {
|
|
337
|
+
sockets.delete(ws);
|
|
338
|
+
},
|
|
315
339
|
async message(ws, raw) {
|
|
316
340
|
let msg: { id: number; method: string; params?: unknown };
|
|
317
341
|
try {
|
|
@@ -362,6 +386,22 @@ export function createProjectServer(options: CreateProjectServerOptions): Projec
|
|
|
362
386
|
rpcToken,
|
|
363
387
|
canvasUrl,
|
|
364
388
|
authorizer,
|
|
389
|
+
push: (method, params, winId) => {
|
|
390
|
+
const frame = JSON.stringify(params === undefined ? { method } : { method, params });
|
|
391
|
+
let delivered = 0;
|
|
392
|
+
for (const ws of sockets) {
|
|
393
|
+
if (winId !== undefined && winId !== null && ws.data.winId !== winId) {
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
try {
|
|
397
|
+
ws.send(frame);
|
|
398
|
+
delivered += 1;
|
|
399
|
+
} catch {
|
|
400
|
+
// A socket that closed between the iteration and the send is simply not a recipient.
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return delivered;
|
|
404
|
+
},
|
|
365
405
|
stop: () => {
|
|
366
406
|
// Abandon outstanding sign-ins first: their callbacks can no longer arrive.
|
|
367
407
|
authorizer.stop();
|