@jxsuite/server 8.2.0 → 8.3.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 +15 -15
- package/src/dev.ts +2 -1
- package/src/resolve.ts +7 -12
- package/src/studio-api.ts +26 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/server",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.3.0",
|
|
4
4
|
"description": "Jx development server with live reload, server-side proxy, and studio integration",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -54,17 +54,17 @@
|
|
|
54
54
|
"upgrade": "bun update --latest && bun install"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@jxsuite/ai": "^0.
|
|
58
|
-
"@jxsuite/catalog": "^0.1.
|
|
59
|
-
"@jxsuite/collab": "^0.11.
|
|
60
|
-
"@jxsuite/compiler": "^8.0
|
|
61
|
-
"@jxsuite/create": "^1.5.
|
|
62
|
-
"@jxsuite/import": "^0.42.
|
|
63
|
-
"@jxsuite/protocol": "^6.2.
|
|
64
|
-
"@jxsuite/runtime": "^6.0
|
|
65
|
-
"@jxsuite/schema": "^5.
|
|
66
|
-
"@jxsuite/site": "^2.
|
|
67
|
-
"@jxsuite/starters": "^3.
|
|
57
|
+
"@jxsuite/ai": "^0.43.0",
|
|
58
|
+
"@jxsuite/catalog": "^0.1.8",
|
|
59
|
+
"@jxsuite/collab": "^0.11.6",
|
|
60
|
+
"@jxsuite/compiler": "^8.2.0",
|
|
61
|
+
"@jxsuite/create": "^1.5.4",
|
|
62
|
+
"@jxsuite/import": "^0.42.4",
|
|
63
|
+
"@jxsuite/protocol": "^6.2.2",
|
|
64
|
+
"@jxsuite/runtime": "^6.1.0",
|
|
65
|
+
"@jxsuite/schema": "^5.2.0",
|
|
66
|
+
"@jxsuite/site": "^2.3.1",
|
|
67
|
+
"@jxsuite/starters": "^3.3.0",
|
|
68
68
|
"@npmcli/arborist": "^10.0.4",
|
|
69
69
|
"chokidar": "^6.0.0",
|
|
70
70
|
"kysely": "^0.29.8",
|
|
@@ -74,9 +74,9 @@
|
|
|
74
74
|
"zod": "^4.6.7"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@jxsuite/auth": "^0.8.
|
|
78
|
-
"@jxsuite/connector": "^0.8.
|
|
79
|
-
"@jxsuite/parser": "^4.
|
|
77
|
+
"@jxsuite/auth": "^0.8.4",
|
|
78
|
+
"@jxsuite/connector": "^0.8.4",
|
|
79
|
+
"@jxsuite/parser": "^4.3.0",
|
|
80
80
|
"@jxsuite/test-support": "^0.0.0",
|
|
81
81
|
"@types/npm-package-arg": "^6.1.4",
|
|
82
82
|
"@types/npmcli__arborist": "^6.3.3",
|
package/src/dev.ts
CHANGED
|
@@ -195,7 +195,8 @@ export async function startDev({ port, root }: DevArgs): Promise<DevServerHandle
|
|
|
195
195
|
const rebuild = () => {
|
|
196
196
|
building = building.then(async () => {
|
|
197
197
|
const { buildSite } = await import("@jxsuite/compiler/site");
|
|
198
|
-
|
|
198
|
+
// A dev server shows authors their work, so entries a project holds back stay in it.
|
|
199
|
+
const result = await buildSite(root, { includeHeld: true, verbose: false });
|
|
199
200
|
({ serverEntries } = result);
|
|
200
201
|
if (result.errors.length > 0) {
|
|
201
202
|
for (const err of result.errors) {
|
package/src/resolve.ts
CHANGED
|
@@ -208,7 +208,9 @@ async function loadProjectContext(projectRoot: string): Promise<ProjectContext |
|
|
|
208
208
|
if (!entry) {
|
|
209
209
|
return null;
|
|
210
210
|
}
|
|
211
|
+
// This host draws the canvas and Preview, so entries a project holds back stay visible to authors.
|
|
211
212
|
entry.context ??= loadProjectSections(projectRoot, entry.config, entry.registry, {
|
|
213
|
+
includeHeld: true,
|
|
212
214
|
lazy: true,
|
|
213
215
|
}).then((sections) => ({ config: entry.config, root: projectRoot, ...sections }));
|
|
214
216
|
try {
|
|
@@ -342,20 +344,16 @@ export async function handleResolve(
|
|
|
342
344
|
}
|
|
343
345
|
|
|
344
346
|
let moduleAbsPath;
|
|
347
|
+
let docDir: string | null = null;
|
|
345
348
|
// A bare specifier resolves through Node's module resolution (an installed package), so the
|
|
346
349
|
// Class file — and the `$implementation` sibling it names — are as trusted as the package's own
|
|
347
350
|
// Code. The root-escape guard only binds a caller-supplied relative `$src`/`$base`.
|
|
348
351
|
let trustedResolution = false;
|
|
349
352
|
try {
|
|
353
|
+
docDir = $base ? baseDirectory($base, root, activeProjectRoot || root) : null;
|
|
350
354
|
if ($src.startsWith("./") || $src.startsWith("../")) {
|
|
351
355
|
// Relative path
|
|
352
|
-
|
|
353
|
-
const docUrlPath = new URL($base).pathname;
|
|
354
|
-
const docDir = docUrlPath.slice(0, docUrlPath.lastIndexOf("/") + 1);
|
|
355
|
-
moduleAbsPath = resolve(resolve(root, `.${docDir}`), $src);
|
|
356
|
-
} else {
|
|
357
|
-
moduleAbsPath = resolve(activeProjectRoot || root, $src);
|
|
358
|
-
}
|
|
356
|
+
moduleAbsPath = resolve(docDir ?? (activeProjectRoot || root), $src);
|
|
359
357
|
if (!isImportable(moduleAbsPath, root, activeProjectRoot)) {
|
|
360
358
|
return new Response(`$src "${$src}" escapes the project root`, { status: 403 });
|
|
361
359
|
}
|
|
@@ -371,13 +369,10 @@ export async function handleResolve(
|
|
|
371
369
|
}
|
|
372
370
|
|
|
373
371
|
// Rebase relative config paths from doc-relative to CWD-relative
|
|
374
|
-
if (
|
|
375
|
-
const docUrlPath = new URL($base).pathname;
|
|
376
|
-
const docDir = docUrlPath.slice(0, docUrlPath.lastIndexOf("/") + 1);
|
|
377
|
-
const docAbsDir = resolve(root, `.${docDir}`);
|
|
372
|
+
if (docDir) {
|
|
378
373
|
for (const [k, v] of Object.entries(config)) {
|
|
379
374
|
if (isString(v) && (v.startsWith("./") || v.startsWith("../"))) {
|
|
380
|
-
config[k] = `./${relative(process.cwd(), resolve(
|
|
375
|
+
config[k] = `./${relative(process.cwd(), resolve(docDir, v)).split("\\").join("/")}`;
|
|
381
376
|
}
|
|
382
377
|
}
|
|
383
378
|
}
|
package/src/studio-api.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
missingExtensionsNotice,
|
|
21
21
|
} from "@jxsuite/compiler/format-host";
|
|
22
22
|
import { readBundledProjectSchemas } from "@jxsuite/compiler/schema-command";
|
|
23
|
+
import { projectCommitArgs, projectRelativePath, readGitPrefix } from "@jxsuite/protocol/git-scope";
|
|
23
24
|
import { handleDataApi } from "./data-api.ts";
|
|
24
25
|
import { containedPath } from "./net-guard.ts";
|
|
25
26
|
import { startSitePreview } from "./site-preview.ts";
|
|
@@ -1792,12 +1793,26 @@ export async function handleStudioApi(
|
|
|
1792
1793
|
/* `--untracked-files=all`: every untracked FILE, not its collapsed folder. By default git
|
|
1793
1794
|
reports a wholly untracked `content/` as one entry, so a fresh repository listed a
|
|
1794
1795
|
handful of folders that no group, comparison or checkbox could treat as a file, and the
|
|
1795
|
-
count jumped to the real one the moment staging expanded them (studio.md §22.8).
|
|
1796
|
-
|
|
1797
|
-
|
|
1796
|
+
count jumped to the real one the moment staging expanded them (studio.md §22.8).
|
|
1797
|
+
`-- .` and the prefix: the project's changes only, in the coordinates every other verb
|
|
1798
|
+
reads. Porcelain prints from the repository root whatever the cwd is (git-scope.ts). */
|
|
1799
|
+
const [out, remotesOut, prefix] = await Promise.all([
|
|
1800
|
+
runGit([
|
|
1801
|
+
"status",
|
|
1802
|
+
"--porcelain=v2",
|
|
1803
|
+
"--branch",
|
|
1804
|
+
"-z",
|
|
1805
|
+
"--untracked-files=all",
|
|
1806
|
+
"--",
|
|
1807
|
+
".",
|
|
1808
|
+
]),
|
|
1798
1809
|
runGit(["remote"]),
|
|
1810
|
+
readGitPrefix(runGit),
|
|
1799
1811
|
]);
|
|
1800
1812
|
const status = parseGitStatus(out);
|
|
1813
|
+
for (const file of status.files) {
|
|
1814
|
+
file.path = projectRelativePath(prefix, file.path);
|
|
1815
|
+
}
|
|
1801
1816
|
const fullStatus = {
|
|
1802
1817
|
...status,
|
|
1803
1818
|
isRepo: true,
|
|
@@ -1908,12 +1923,7 @@ export async function handleStudioApi(
|
|
|
1908
1923
|
if (!message || !isString(message)) {
|
|
1909
1924
|
return problem("invalidRequest", "Missing message");
|
|
1910
1925
|
}
|
|
1911
|
-
const
|
|
1912
|
-
const hasStaged = statusOut
|
|
1913
|
-
.split("\n")
|
|
1914
|
-
.some((l) => l.length > 0 && l[0] !== " " && l[0] !== "?");
|
|
1915
|
-
const args = hasStaged ? ["commit", "-m", message] : ["commit", "-a", "-m", message];
|
|
1916
|
-
const out = await runGit(args);
|
|
1926
|
+
const out = await runGit(await projectCommitArgs(runGit, message));
|
|
1917
1927
|
const hashMatch = out.match(/\[[\w/]+ ([a-f0-9]+)\]/);
|
|
1918
1928
|
return Response.json({ hash: hashMatch?.[1] || "", ok: true });
|
|
1919
1929
|
}
|
|
@@ -1947,13 +1957,15 @@ export async function handleStudioApi(
|
|
|
1947
1957
|
try {
|
|
1948
1958
|
await runGit(["pull"]);
|
|
1949
1959
|
} catch (error) {
|
|
1950
|
-
const
|
|
1960
|
+
const conflicting =
|
|
1951
1961
|
error instanceof GitCommandError
|
|
1952
1962
|
? conflictingPaths(`${error.stdout}\n${error.stderr}`)
|
|
1953
1963
|
: [];
|
|
1954
|
-
if (
|
|
1964
|
+
if (conflicting.length === 0) {
|
|
1955
1965
|
throw error;
|
|
1956
1966
|
}
|
|
1967
|
+
const prefix = await readGitPrefix(runGit);
|
|
1968
|
+
const conflicts = conflicting.map((conflict) => projectRelativePath(prefix, conflict));
|
|
1957
1969
|
return problem(
|
|
1958
1970
|
"conflict",
|
|
1959
1971
|
`Pull stopped: ${conflicts.length} file(s) changed on both sides.`,
|
|
@@ -2010,9 +2022,9 @@ export async function handleStudioApi(
|
|
|
2010
2022
|
return problem("invalidRequest", "Invalid path");
|
|
2011
2023
|
}
|
|
2012
2024
|
/* `./` IS LOAD-BEARING. `git show <rev>:<path>` resolves <path> from the REPOSITORY ROOT,
|
|
2013
|
-
whatever the cwd is; only a `./` prefix makes it relative to the cwd.
|
|
2014
|
-
|
|
2015
|
-
|
|
2025
|
+
whatever the cwd is; only a `./` prefix makes it relative to the cwd. The paths this API
|
|
2026
|
+
answers and accepts are project-relative, and so is the `readFile` the studio pairs this
|
|
2027
|
+
with, so without it a project nested inside its repo compared two DIFFERENT
|
|
2016
2028
|
FILES: the working copy of `examples/package.json` against the committed root
|
|
2017
2029
|
`package.json`, with no error anywhere and a plausible-looking diff. */
|
|
2018
2030
|
const content = await runGit(["show", `${ref}:./${fp}`]);
|