@jxsuite/server 8.5.0 → 8.7.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jxsuite/server",
3
- "version": "8.5.0",
3
+ "version": "8.7.0",
4
4
  "description": "Jx development server with live reload, server-side proxy, and studio integration",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -55,17 +55,18 @@
55
55
  "upgrade": "bun update --latest && bun install"
56
56
  },
57
57
  "dependencies": {
58
- "@jxsuite/ai": "^0.43.2",
59
- "@jxsuite/catalog": "^0.1.10",
60
- "@jxsuite/collab": "^0.11.8",
61
- "@jxsuite/compiler": "^8.4.0",
62
- "@jxsuite/create": "^1.6.0",
63
- "@jxsuite/import": "^0.43.0",
64
- "@jxsuite/protocol": "^6.2.4",
65
- "@jxsuite/runtime": "^6.2.1",
66
- "@jxsuite/schema": "^5.4.0",
67
- "@jxsuite/site": "^2.5.0",
68
- "@jxsuite/starters": "^3.3.2",
58
+ "@jxsuite/ai": "^0.43.4",
59
+ "@jxsuite/catalog": "^0.1.12",
60
+ "@jxsuite/collab": "^0.11.10",
61
+ "@jxsuite/compiler": "^8.6.0",
62
+ "@jxsuite/create": "^1.8.0",
63
+ "@jxsuite/erase": "^0.2.0",
64
+ "@jxsuite/import": "^0.44.1",
65
+ "@jxsuite/protocol": "^6.3.0",
66
+ "@jxsuite/runtime": "^6.3.0",
67
+ "@jxsuite/schema": "^5.6.0",
68
+ "@jxsuite/site": "^2.6.0",
69
+ "@jxsuite/starters": "^3.5.0",
69
70
  "@npmcli/arborist": "^10.0.4",
70
71
  "chokidar": "^6.0.0",
71
72
  "kysely": "^0.29.8",
@@ -75,9 +76,9 @@
75
76
  "zod": "^4.6.7"
76
77
  },
77
78
  "devDependencies": {
78
- "@jxsuite/auth": "^0.8.6",
79
- "@jxsuite/connector": "^0.8.6",
80
- "@jxsuite/parser": "^4.3.2",
79
+ "@jxsuite/auth": "^0.8.8",
80
+ "@jxsuite/connector": "^0.8.8",
81
+ "@jxsuite/parser": "^4.3.4",
81
82
  "@jxsuite/test-support": "^0.0.0",
82
83
  "@types/npm-package-arg": "^6.1.4",
83
84
  "@types/npmcli__arborist": "^6.3.3",
package/src/code-api.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Code-api.js — OXC-powered code services for the studio function editor
3
3
  *
4
- * Endpoints under /__studio/code/* that provide formatting (oxfmt), minification (Bun.Transpiler),
5
- * and linting (oxlint CLI) for JS snippets.
4
+ * Endpoints under /__studio/code/* that format (oxfmt) and lint (oxlint CLI) a function body. A
5
+ * body may be TypeScript (`spec.md` §23.1), so both read it as `.ts`.
6
6
  */
7
7
 
8
8
  import { tmpdir } from "node:os";
@@ -111,10 +111,6 @@ function adjustDiagnostics(diagnostics: OxcDiagnostic[], headerLen: number) {
111
111
  });
112
112
  }
113
113
 
114
- // ─── Reusable transpiler ─────────────────────────────────────────────────────
115
-
116
- const minifier = new Bun.Transpiler({ minifyWhitespace: true });
117
-
118
114
  // ─── Handler ─────────────────────────────────────────────────────────────────
119
115
 
120
116
  /**
@@ -148,7 +144,7 @@ export async function handleCodeApi(req: Request, url: URL) {
148
144
 
149
145
  try {
150
146
  const wrapped = wrapBody(code, args);
151
- const result = await format("fn.js", wrapped, { useTabs: true });
147
+ const result = await format("fn.ts", wrapped, { useTabs: true });
152
148
  return Response.json({
153
149
  code: unwrapFormatted(result.code),
154
150
  errors: result.errors,
@@ -161,22 +157,6 @@ export async function handleCodeApi(req: Request, url: URL) {
161
157
  }
162
158
  }
163
159
 
164
- // ── Minify ─────────────────────────────────────────────────────────────────
165
-
166
- if (action === "minify") {
167
- const { code } = body;
168
- if (!code?.trim()) {
169
- return Response.json({ code: "" });
170
- }
171
-
172
- try {
173
- const minified = minifier.transformSync(code).trim();
174
- return Response.json({ code: minified });
175
- } catch (error) {
176
- return Response.json({ code, error: errorMessage(error) });
177
- }
178
- }
179
-
180
160
  // ── Lint ───────────────────────────────────────────────────────────────────
181
161
 
182
162
  if (action === "lint") {
@@ -195,12 +175,15 @@ export async function handleCodeApi(req: Request, url: URL) {
195
175
  const headerLen = wrapped.indexOf("\n") + 1;
196
176
  const tmpFile = join(
197
177
  tmpdir(),
198
- `__jx_lint_${Date.now()}_${Math.random().toString(36).slice(2)}.js`,
178
+ `__jx_lint_${Date.now()}_${Math.random().toString(36).slice(2)}.ts`,
199
179
  );
200
180
 
201
181
  try {
202
182
  await Bun.write(tmpFile, wrapped);
183
+ // Run from the temp directory, so the body gets oxlint's defaults and not the rules of
184
+ // The repository the server happens to be started in.
203
185
  const proc = Bun.spawn([oxlintBin, "--format=json", "-A", "no-unused-vars", tmpFile], {
186
+ cwd: tmpdir(),
204
187
  stderr: "pipe",
205
188
  stdout: "pipe",
206
189
  });
@@ -174,6 +174,22 @@ async function loadRuntimeBundle(): Promise<string> {
174
174
  return runtimeBundle;
175
175
  }
176
176
 
177
+ /** The body eraser's browser bundle, built once per process, the first time a page asks for it. */
178
+ let eraserBundle: string | null = null;
179
+
180
+ /**
181
+ * `@jxsuite/erase/browser` as one browser module (`spec.md` §23.1). A page fetches it only when a
182
+ * function body is TypeScript, so most previews never ask, and none pays for it at startup. A build
183
+ * that fails is not kept, so the next page that needs it tries again.
184
+ */
185
+ async function loadEraserBundle(): Promise<string> {
186
+ eraserBundle ??= await bundleToString(
187
+ createRequire(import.meta.url).resolve("@jxsuite/erase/browser"),
188
+ { minify: true, target: "browser" },
189
+ );
190
+ return eraserBundle;
191
+ }
192
+
177
193
  /** Every path under a servable root, plus anything the overlay is holding. */
178
194
  async function treePaths(projectRoot: string, overlay: Map<string, string>): Promise<string[]> {
179
195
  const found = new Set<string>(overlay.keys());
@@ -344,6 +360,15 @@ async function handle(entry: Running, projectRoot: string, req: Request): Promis
344
360
  });
345
361
  }
346
362
 
363
+ if (surface === "eraser.js") {
364
+ return new Response(await loadEraserBundle(), {
365
+ headers: {
366
+ "Cache-Control": "public, max-age=31536000, immutable",
367
+ "Content-Type": "text/javascript; charset=utf-8",
368
+ },
369
+ });
370
+ }
371
+
347
372
  if (surface === "client.js") {
348
373
  return new Response(PREVIEW_CLIENT_JS, {
349
374
  headers: {
@@ -404,6 +429,7 @@ async function handle(entry: Running, projectRoot: string, req: Request): Promis
404
429
  shell: {
405
430
  base: "/",
406
431
  clientScriptUrl: `${LIVE_NAMESPACE}/client.js`,
432
+ eraserUrl: `${LIVE_NAMESPACE}/eraser.js`,
407
433
  resolveToken: entry.token,
408
434
  runtimeUrl: `${LIVE_NAMESPACE}/runtime.js`,
409
435
  styleUrl: `${LIVE_NAMESPACE}/site.css`,
package/src/resolve.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  /** Resolve.js — Generic $src module proxy + timing: "server" function proxy */
2
2
 
3
+ import { eraseClass } from "@jxsuite/erase";
3
4
  import { siteBasePath } from "@jxsuite/schema/asset-paths";
4
5
  import { dirname, isAbsolute, relative, resolve } from "node:path";
5
6
  import { errorMessage, parseClassDef } from "@jxsuite/schema/parse";
@@ -594,7 +595,9 @@ function isCtorDef(
594
595
  *
595
596
  * @param {JxClassDef} classDef
596
597
  */
597
- function classFromSchema(classDef: JxClassDef) {
598
+ function classFromSchema(authored: JxClassDef) {
599
+ // Member bodies as JavaScript before anything compiles them (spec.md §23.1).
600
+ const classDef = eraseClass(authored, authored.title ?? "a class");
598
601
  const fields = classDef.$defs?.fields ?? {};
599
602
  const rawCtor = classDef.$defs?.constructor;
600
603
  const ctor = isCtorDef(rawCtor) ? rawCtor : undefined;