@jxsuite/server 4.4.5 → 4.5.1
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 +10 -10
- package/src/ai-api.ts +8 -4
- package/src/build.ts +6 -6
- package/src/code-api.ts +29 -16
- package/src/collab.ts +9 -5
- package/src/data-api.ts +190 -137
- package/src/dev-vars.ts +28 -4
- package/src/dev.ts +3 -0
- package/src/extension-catalog.ts +62 -32
- package/src/import-api.ts +95 -53
- package/src/jx-mounts.ts +30 -33
- package/src/live-preview.ts +16 -10
- package/src/oauth-loopback.ts +23 -18
- package/src/packages.ts +26 -36
- package/src/problem.ts +3 -6
- package/src/project-server.ts +34 -4
- package/src/refactor/apply.ts +3 -2
- package/src/refactor/find-refs.ts +0 -0
- package/src/refactor/fs-events.ts +14 -15
- package/src/refactor/paths.ts +11 -3
- package/src/refactor/refs.ts +60 -63
- package/src/refactor/scan.ts +13 -19
- package/src/refactor/watcher.ts +5 -5
- package/src/resolve.ts +235 -83
- package/src/server.ts +51 -29
- package/src/site-output.ts +11 -5
- package/src/sse.ts +16 -2
- package/src/studio-api.ts +264 -115
- package/src/types.ts +17 -7
- package/src/watch.ts +8 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/server",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.1",
|
|
4
4
|
"description": "Jx development server with live reload, server-side proxy, and studio integration",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -44,22 +44,22 @@
|
|
|
44
44
|
"@jxsuite/ai": "^0.39.3",
|
|
45
45
|
"@jxsuite/catalog": "^0.1.0",
|
|
46
46
|
"@jxsuite/collab": "^0.11.1",
|
|
47
|
-
"@jxsuite/compiler": "^5.1.
|
|
48
|
-
"@jxsuite/create": "^1.3.
|
|
49
|
-
"@jxsuite/import": "^0.40.
|
|
47
|
+
"@jxsuite/compiler": "^5.1.3",
|
|
48
|
+
"@jxsuite/create": "^1.3.14",
|
|
49
|
+
"@jxsuite/import": "^0.40.10",
|
|
50
50
|
"@jxsuite/protocol": "^2.6.1",
|
|
51
|
-
"@jxsuite/runtime": "^4.0
|
|
51
|
+
"@jxsuite/runtime": "^4.1.0",
|
|
52
52
|
"@jxsuite/schema": "^3.0.0",
|
|
53
|
-
"@jxsuite/site": "^2.0.
|
|
54
|
-
"@jxsuite/starters": "^2.0.
|
|
53
|
+
"@jxsuite/site": "^2.0.4",
|
|
54
|
+
"@jxsuite/starters": "^2.0.1",
|
|
55
55
|
"chokidar": "^5.0.0",
|
|
56
56
|
"kysely": "^0.29.6",
|
|
57
|
-
"oxfmt": "^0.
|
|
58
|
-
"zod": "^4.6.
|
|
57
|
+
"oxfmt": "^0.73.0",
|
|
58
|
+
"zod": "^4.6.7"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@jxsuite/auth": "^0.6.2",
|
|
62
62
|
"@jxsuite/connector": "^0.6.2",
|
|
63
|
-
"@jxsuite/parser": "^2.0.
|
|
63
|
+
"@jxsuite/parser": "^2.0.1"
|
|
64
64
|
}
|
|
65
65
|
}
|
package/src/ai-api.ts
CHANGED
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
AI_SESSION_HEADER,
|
|
40
40
|
affinityKey,
|
|
41
41
|
createChatHandler,
|
|
42
|
+
DEFAULT_UPSTREAM_BASE_URL,
|
|
42
43
|
extractUpstreamErrorMessage,
|
|
43
44
|
isAiSessionId,
|
|
44
45
|
modelsResponse,
|
|
@@ -46,12 +47,11 @@ import {
|
|
|
46
47
|
} from "@jxsuite/ai/gateway";
|
|
47
48
|
import type { GatewayRefusal, Upstream } from "@jxsuite/ai/gateway";
|
|
48
49
|
import { problemDetails } from "@jxsuite/protocol";
|
|
50
|
+
import { errorMessage } from "@jxsuite/schema/parse";
|
|
49
51
|
import { problemTypeForStatus } from "./problem.ts";
|
|
50
52
|
|
|
51
53
|
// ─── Configuration ───────────────────────────────────────────────────────────
|
|
52
54
|
|
|
53
|
-
const DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
54
|
-
|
|
55
55
|
/** The model a chat request that names none is forwarded with. */
|
|
56
56
|
const DEFAULT_MODEL = "gpt-4o";
|
|
57
57
|
|
|
@@ -120,7 +120,7 @@ function getConfig(req: Request): AiConfig {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
const baseFromHeader = Boolean(baseUrlHeader);
|
|
123
|
-
const baseUrl = baseUrlHeader || process.env.OPENAI_BASE_URL ||
|
|
123
|
+
const baseUrl = baseUrlHeader || process.env.OPENAI_BASE_URL || DEFAULT_UPSTREAM_BASE_URL;
|
|
124
124
|
|
|
125
125
|
if (isBlockedHost(baseUrl)) {
|
|
126
126
|
return {
|
|
@@ -261,6 +261,10 @@ export async function handleModels(req: Request): Promise<Response> {
|
|
|
261
261
|
});
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
+
/* SAFETY: an OpenAI-compatible `/models` answers either the `{ object: "list", data }`
|
|
265
|
+
envelope or a bare array of model entries. Both arrays are confirmed with Array.isArray
|
|
266
|
+
below before use, and a body outside the contract (e.g. `null`) throws into the catch below,
|
|
267
|
+
which answers with the default models. */
|
|
264
268
|
const data = (await upstreamResp.json()) as { data?: ModelEntry[] } | ModelEntry[];
|
|
265
269
|
// OpenAI /models returns { object: "list", data: [{ id, ... }] }
|
|
266
270
|
// Map to our simpler format.
|
|
@@ -285,7 +289,7 @@ export async function handleModels(req: Request): Promise<Response> {
|
|
|
285
289
|
configured: true,
|
|
286
290
|
managed: false,
|
|
287
291
|
upstreamError: "network",
|
|
288
|
-
upstreamMessage: (error
|
|
292
|
+
upstreamMessage: errorMessage(error),
|
|
289
293
|
});
|
|
290
294
|
}
|
|
291
295
|
}
|
package/src/build.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Build.js — Configurable Bun.build pipeline */
|
|
2
2
|
|
|
3
|
+
import { isFunction } from "@jxsuite/schema/guards";
|
|
3
4
|
import type { BuildConfig } from "bun";
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -66,12 +67,11 @@ export async function rebuild(builds: BuildEntry[], changedFile: string) {
|
|
|
66
67
|
if (!entry.match) {
|
|
67
68
|
continue;
|
|
68
69
|
}
|
|
69
|
-
const matches =
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
: false;
|
|
70
|
+
const matches = isFunction(entry.match)
|
|
71
|
+
? entry.match(changedFile)
|
|
72
|
+
: entry.match instanceof RegExp
|
|
73
|
+
? entry.match.test(changedFile)
|
|
74
|
+
: false;
|
|
75
75
|
if (!matches) {
|
|
76
76
|
continue;
|
|
77
77
|
}
|
package/src/code-api.ts
CHANGED
|
@@ -7,20 +7,29 @@
|
|
|
7
7
|
|
|
8
8
|
import { tmpdir } from "node:os";
|
|
9
9
|
import { errorMessage } from "@jxsuite/schema/parse";
|
|
10
|
+
import type { JsonValue } from "@jxsuite/schema/types";
|
|
10
11
|
import { dirname, join } from "node:path";
|
|
11
12
|
import { existsSync } from "node:fs";
|
|
12
13
|
import { unlink } from "node:fs/promises";
|
|
13
14
|
import { format } from "oxfmt";
|
|
14
15
|
import { problem } from "./problem.ts";
|
|
15
16
|
|
|
17
|
+
/** A label's source span; oxlint's other span members pass through to the client unchanged. */
|
|
18
|
+
interface OxcSpan {
|
|
19
|
+
offset: number;
|
|
20
|
+
line: number;
|
|
21
|
+
[key: string]: JsonValue;
|
|
22
|
+
}
|
|
23
|
+
|
|
16
24
|
interface OxcLabel {
|
|
17
|
-
span:
|
|
18
|
-
[key: string]:
|
|
25
|
+
span: OxcSpan;
|
|
26
|
+
[key: string]: JsonValue;
|
|
19
27
|
}
|
|
20
28
|
|
|
29
|
+
/** One entry of `oxlint --format=json` output; members other than `labels` pass through. */
|
|
21
30
|
interface OxcDiagnostic {
|
|
22
31
|
labels?: OxcLabel[];
|
|
23
|
-
[key: string]:
|
|
32
|
+
[key: string]: JsonValue | undefined;
|
|
24
33
|
}
|
|
25
34
|
|
|
26
35
|
interface CodeApiBody {
|
|
@@ -87,19 +96,19 @@ function unwrapFormatted(formatted: string) {
|
|
|
87
96
|
* @param {number} headerLen
|
|
88
97
|
*/
|
|
89
98
|
function adjustDiagnostics(diagnostics: OxcDiagnostic[], headerLen: number) {
|
|
90
|
-
return diagnostics
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
return diagnostics.flatMap((d) => {
|
|
100
|
+
const line = d.labels?.[0]?.span.line;
|
|
101
|
+
// A diagnostic on line 1 points at the synthetic wrapper header, not the user's code
|
|
102
|
+
if (line != null && line <= 1) {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
// Diagnostics are freshly parsed and serialized right after; mutate in place
|
|
106
|
+
for (const label of d.labels ?? []) {
|
|
107
|
+
label.span.line -= 1;
|
|
108
|
+
label.span.offset -= headerLen;
|
|
109
|
+
}
|
|
110
|
+
return [d];
|
|
111
|
+
});
|
|
103
112
|
}
|
|
104
113
|
|
|
105
114
|
// ─── Reusable transpiler ─────────────────────────────────────────────────────
|
|
@@ -120,6 +129,8 @@ export async function handleCodeApi(req: Request, url: URL) {
|
|
|
120
129
|
|
|
121
130
|
let body: CodeApiBody;
|
|
122
131
|
try {
|
|
132
|
+
/* SAFETY: the Studio function editor is this route's only client, and it posts `{ code, args }`
|
|
133
|
+
with a string body and string parameter names; malformed JSON is refused just below. */
|
|
123
134
|
body = (await req.json()) as CodeApiBody;
|
|
124
135
|
} catch {
|
|
125
136
|
return problem("invalidRequest", "Invalid JSON");
|
|
@@ -196,6 +207,8 @@ export async function handleCodeApi(req: Request, url: URL) {
|
|
|
196
207
|
const output = await new Response(proc.stdout).text();
|
|
197
208
|
await proc.exited;
|
|
198
209
|
|
|
210
|
+
/* SAFETY: `oxlint --format=json` prints `{ diagnostics: [...] }` whose labels each carry a
|
|
211
|
+
numeric `span.line`/`span.offset`; output that is not JSON throws into the catch below. */
|
|
199
212
|
const parsed = JSON.parse(output) as { diagnostics?: OxcDiagnostic[] };
|
|
200
213
|
const adjusted = adjustDiagnostics(parsed.diagnostics ?? [], headerLen);
|
|
201
214
|
return Response.json({ diagnostics: adjusted });
|
package/src/collab.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { resolve } from "node:path";
|
|
|
17
17
|
import { readFile, writeFile } from "node:fs/promises";
|
|
18
18
|
import { colorForKey } from "@jxsuite/collab/awareness-types";
|
|
19
19
|
import { createCollabHost } from "@jxsuite/collab/room";
|
|
20
|
+
import { isString } from "@jxsuite/schema/guards";
|
|
20
21
|
import {
|
|
21
22
|
COLLAB_SUBPROTOCOL,
|
|
22
23
|
offeredSubprotocols,
|
|
@@ -215,10 +216,13 @@ export function createCollabRegistry(opts: {
|
|
|
215
216
|
if (answer.reject !== null) {
|
|
216
217
|
return problem("invalidRequest", answer.reject);
|
|
217
218
|
}
|
|
218
|
-
const upgraded =
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
219
|
+
const upgraded =
|
|
220
|
+
answer.echo === null
|
|
221
|
+
? server.upgrade(req, { data: { connection: null } })
|
|
222
|
+
: server.upgrade(req, {
|
|
223
|
+
data: { connection: null },
|
|
224
|
+
headers: { "Sec-WebSocket-Protocol": answer.echo },
|
|
225
|
+
});
|
|
222
226
|
return upgraded ? undefined : problem("invalidRequest", "Upgrade failed");
|
|
223
227
|
}
|
|
224
228
|
/*
|
|
@@ -254,7 +258,7 @@ export function createCollabRegistry(opts: {
|
|
|
254
258
|
ws.data.connection = null;
|
|
255
259
|
},
|
|
256
260
|
message(ws: ServerWebSocket<CollabSocketData>, message: string | Buffer) {
|
|
257
|
-
if (
|
|
261
|
+
if (isString(message)) {
|
|
258
262
|
return;
|
|
259
263
|
}
|
|
260
264
|
ws.data.connection?.handleMessage(new Uint8Array(message));
|