@jxsuite/server 2.2.7 → 3.0.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 +12 -12
- package/src/net-guard.ts +86 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/server",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
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.8.
|
|
42
|
-
"@jxsuite/compiler": "^2.0.
|
|
43
|
-
"@jxsuite/create": "^1.3.
|
|
44
|
-
"@jxsuite/import": "^0.39.
|
|
45
|
-
"@jxsuite/protocol": "^
|
|
46
|
-
"@jxsuite/runtime": "^2.0
|
|
47
|
-
"@jxsuite/schema": "^1.
|
|
48
|
-
"@jxsuite/starters": "^1.6.
|
|
41
|
+
"@jxsuite/collab": "^0.8.3",
|
|
42
|
+
"@jxsuite/compiler": "^2.0.8",
|
|
43
|
+
"@jxsuite/create": "^1.3.7",
|
|
44
|
+
"@jxsuite/import": "^0.39.8",
|
|
45
|
+
"@jxsuite/protocol": "^2.0.0",
|
|
46
|
+
"@jxsuite/runtime": "^2.1.0",
|
|
47
|
+
"@jxsuite/schema": "^1.9.0",
|
|
48
|
+
"@jxsuite/starters": "^1.6.4",
|
|
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.5",
|
|
55
|
+
"@jxsuite/connector": "^0.5.5",
|
|
56
|
+
"@jxsuite/parser": "^1.5.6"
|
|
57
57
|
}
|
|
58
58
|
}
|
package/src/net-guard.ts
CHANGED
|
@@ -307,14 +307,83 @@ export async function serveContained(absPath: string, root: string): Promise<Res
|
|
|
307
307
|
return null;
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
+
/**
|
|
311
|
+
* Extensions a BUILD publishes as static assets, so a page may reference one by site URL.
|
|
312
|
+
*
|
|
313
|
+
* Deliberately excludes `.json`, `.md` and every other document extension: `serveProjectFile`
|
|
314
|
+
* serves two URL spaces at once — the SITE's, and the project tree's, which is how the Studio
|
|
315
|
+
* canvas fetches a component `$ref` — and only the site's is what {@link BUILD_LANES} defines. A
|
|
316
|
+
* `/components/x-card.json` answered from the project root is Studio's own protocol working
|
|
317
|
+
* correctly; a `/hero.jpg` answered from the project root is a preview telling a lie.
|
|
318
|
+
*/
|
|
319
|
+
const SITE_ASSET_EXTENSIONS = new Set([
|
|
320
|
+
".apng",
|
|
321
|
+
".avif",
|
|
322
|
+
".bmp",
|
|
323
|
+
".css",
|
|
324
|
+
".gif",
|
|
325
|
+
".ico",
|
|
326
|
+
".jpeg",
|
|
327
|
+
".jpg",
|
|
328
|
+
".mp3",
|
|
329
|
+
".mp4",
|
|
330
|
+
".oga",
|
|
331
|
+
".ogg",
|
|
332
|
+
".ogv",
|
|
333
|
+
".otf",
|
|
334
|
+
".pdf",
|
|
335
|
+
".png",
|
|
336
|
+
".svg",
|
|
337
|
+
".ttf",
|
|
338
|
+
".txt",
|
|
339
|
+
".wav",
|
|
340
|
+
".webm",
|
|
341
|
+
".webmanifest",
|
|
342
|
+
".webp",
|
|
343
|
+
".woff",
|
|
344
|
+
".woff2",
|
|
345
|
+
".xml",
|
|
346
|
+
]);
|
|
347
|
+
|
|
348
|
+
/** Paths already warned about, so a page that loads one image fifty times says it once. */
|
|
349
|
+
const rootLaneWarned = new Set<string>();
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Say once, per path, that a file resolves here and will not resolve on the built site.
|
|
353
|
+
*
|
|
354
|
+
* @param {string} decodedPath - The site URL that matched the compatibility lane
|
|
355
|
+
*/
|
|
356
|
+
function warnRootLane(decodedPath: string): void {
|
|
357
|
+
const dot = decodedPath.lastIndexOf(".");
|
|
358
|
+
const ext = dot === -1 ? "" : decodedPath.slice(dot).toLowerCase();
|
|
359
|
+
if (!SITE_ASSET_EXTENSIONS.has(ext) || rootLaneWarned.has(decodedPath)) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
rootLaneWarned.add(decodedPath);
|
|
363
|
+
console.warn(
|
|
364
|
+
`${decodedPath} resolves from the project root, which a BUILD does not do: it publishes ` +
|
|
365
|
+
`public/ at the site root and nothing else (site-architecture.md §9.3). This file loads in ` +
|
|
366
|
+
`the preview and 404s on the deployed site — move it into public/ to fix it. This ` +
|
|
367
|
+
`compatibility lane is going away.`,
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
|
|
310
371
|
/**
|
|
311
372
|
* Try to serve a project file at its natural URL: an extension asset mount, then
|
|
312
|
-
* absolute-under-root, then
|
|
313
|
-
* (realpath). `decodedPath` is the once-decoded URL pathname (leading slash, forward
|
|
373
|
+
* absolute-under-root, then `public/`, then the project root. Each candidate goes through
|
|
374
|
+
* containedPath (realpath). `decodedPath` is the once-decoded URL pathname (leading slash, forward
|
|
375
|
+
* slashes).
|
|
314
376
|
*
|
|
315
377
|
* Mounts come first and are contained against their own directory rather than the project root:
|
|
316
378
|
* they exist precisely to publish directories that may sit outside it (extensions.md §8.5).
|
|
317
379
|
*
|
|
380
|
+
* **`public/` precedes the project root, which is the order a BUILD resolves in.** It did not: the
|
|
381
|
+
* root came first, so a file at `<root>/hero.jpg` loaded at `/hero.jpg` here and 404'd in
|
|
382
|
+
* production — a preview that lies in the one direction that matters, and one the spec never
|
|
383
|
+
* described (`site-architecture.md` §9.3 has always matched the compiler). The root lane survives
|
|
384
|
+
* as a COMPATIBILITY lane that warns, and is going away; it also still answers the project tree's
|
|
385
|
+
* own URL space, which is how the Studio canvas fetches a component `$ref`.
|
|
386
|
+
*
|
|
318
387
|
* The project's BUILT OUTPUT is deliberately NOT here, nor anywhere else in an editing server's
|
|
319
388
|
* chain: these paths mean the project's SOURCES, and a built page addresses the same paths meaning
|
|
320
389
|
* its own output. The built site is served on its own origin instead — see
|
|
@@ -342,7 +411,8 @@ export async function serveProjectFile(
|
|
|
342
411
|
? decodedPath.slice(1)
|
|
343
412
|
: decodedPath.replace(/^\/([A-Za-z]:)/, "$1");
|
|
344
413
|
|
|
345
|
-
|
|
414
|
+
/* 1. Absolute path that falls under the project root. Not a site URL at all — it is how Studio
|
|
415
|
+
addresses a file it already holds an absolute path for, so it stays ahead of both lanes. */
|
|
346
416
|
if (normalizeForCompare(fsPath).startsWith(normalizeForCompare(root))) {
|
|
347
417
|
const res = await serveContained(fsPath, root);
|
|
348
418
|
if (res) {
|
|
@@ -350,21 +420,27 @@ export async function serveProjectFile(
|
|
|
350
420
|
}
|
|
351
421
|
}
|
|
352
422
|
|
|
353
|
-
// 2.
|
|
354
|
-
const relRes = await serveContained(resolve(root, `.${decodedPath}`), root);
|
|
355
|
-
if (relRes) {
|
|
356
|
-
return relRes;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// 3. public/ subdirectory.
|
|
423
|
+
// 2. public/ — the site's own URL space, and the only one a build publishes.
|
|
360
424
|
const pubRes = await serveContained(resolve(root, "public", `.${decodedPath}`), root);
|
|
361
425
|
if (pubRes) {
|
|
362
426
|
return pubRes;
|
|
363
427
|
}
|
|
364
428
|
|
|
429
|
+
// 3. The project root — the project tree's URL space, and a compatibility lane for site URLs.
|
|
430
|
+
const relRes = await serveContained(resolve(root, `.${decodedPath}`), root);
|
|
431
|
+
if (relRes) {
|
|
432
|
+
warnRootLane(decodedPath);
|
|
433
|
+
return relRes;
|
|
434
|
+
}
|
|
435
|
+
|
|
365
436
|
return null;
|
|
366
437
|
}
|
|
367
438
|
|
|
439
|
+
/** Forget which paths have been warned about — for tests, which assert the warning fires. */
|
|
440
|
+
export function resetRootLaneWarnings(): void {
|
|
441
|
+
rootLaneWarned.clear();
|
|
442
|
+
}
|
|
443
|
+
|
|
368
444
|
/** Result of decoding a URL pathname: the safe decoded/normalized path, or a rejection Response. */
|
|
369
445
|
export type DecodedPath = { path: string; normPath: string } | { reject: Response };
|
|
370
446
|
|