@jxsuite/server 8.2.1 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jxsuite/server",
3
- "version": "8.2.1",
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.42.1",
58
- "@jxsuite/catalog": "^0.1.7",
59
- "@jxsuite/collab": "^0.11.5",
60
- "@jxsuite/compiler": "^8.1.0",
61
- "@jxsuite/create": "^1.5.3",
62
- "@jxsuite/import": "^0.42.3",
63
- "@jxsuite/protocol": "^6.2.1",
64
- "@jxsuite/runtime": "^6.0.2",
65
- "@jxsuite/schema": "^5.1.0",
66
- "@jxsuite/site": "^2.3.0",
67
- "@jxsuite/starters": "^3.2.0",
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.3",
78
- "@jxsuite/connector": "^0.8.3",
79
- "@jxsuite/parser": "^4.2.0",
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
- const result = await buildSite(root, { verbose: false });
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
- if ($base) {
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 ($base) {
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(docAbsDir, v)).split("\\").join("/")}`;
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
- const [out, remotesOut] = await Promise.all([
1797
- runGit(["status", "--porcelain=v2", "--branch", "-z", "--untracked-files=all"]),
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 statusOut = await runGit(["status", "--porcelain"]);
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 conflicts =
1960
+ const conflicting =
1951
1961
  error instanceof GitCommandError
1952
1962
  ? conflictingPaths(`${error.stdout}\n${error.stderr}`)
1953
1963
  : [];
1954
- if (conflicts.length === 0) {
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. Every other verb
2014
- here — status, diff, checkout — is cwd-relative, and so is the `readFile` the studio
2015
- pairs this with, so without it a project nested inside its repo compared two DIFFERENT
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}`]);