@valbuild/server 0.108.3 → 0.108.5

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.
@@ -1006,7 +1006,7 @@ function errorMessage(e) {
1006
1006
  return String(e);
1007
1007
  }
1008
1008
 
1009
- const jsonOps$1 = new JSONOps();
1009
+ const jsonOps$2 = new JSONOps();
1010
1010
 
1011
1011
  /**
1012
1012
  * Classification of a single patch op against a module's serialized schema,
@@ -1261,7 +1261,7 @@ function applyJsonValuesEntryPatches(args) {
1261
1261
  // on an entry that does not exist.
1262
1262
  continue;
1263
1263
  }
1264
- const applied = applyPatch(deepClone(content), jsonOps$1, [{
1264
+ const applied = applyPatch(deepClone(content), jsonOps$2, [{
1265
1265
  op: "add",
1266
1266
  path: cls.subPath.concat(...(op.nestedFilePath ?? [])).concat("patch_id"),
1267
1267
  value: patchId
@@ -1315,7 +1315,7 @@ function applyJsonValuesEntryPatches(args) {
1315
1315
  patchId
1316
1316
  };
1317
1317
  }
1318
- const applied = applyPatch(deepClone(content), jsonOps$1, [rebased.value]);
1318
+ const applied = applyPatch(deepClone(content), jsonOps$2, [rebased.value]);
1319
1319
  if (result.isErr(applied)) {
1320
1320
  return {
1321
1321
  kind: "error",
@@ -1353,6 +1353,19 @@ function resolveExistingJsonPath(moduleFilePath, importPath) {
1353
1353
  return path.posix.join(moduleDir, importPath);
1354
1354
  }
1355
1355
 
1356
+ /**
1357
+ * The outcome of {@link validateJsonValuesEntries}: the errors found inside the
1358
+ * entries, plus the content of every entry that could be loaded, keyed by entry
1359
+ * key.
1360
+ *
1361
+ * The content is part of the result because the errors are useless without it.
1362
+ * They are reported at paths that point INSIDE an entry, and the module source
1363
+ * on disk holds a `c.json(...)` marker there — so anything that has to resolve
1364
+ * one of those paths (the `val validate --fix` handlers, chiefly) needs the very
1365
+ * content this function has just loaded. Returning it costs nothing and is the
1366
+ * only way to avoid loading every entry twice.
1367
+ */
1368
+
1356
1369
  /**
1357
1370
  * Validates the content of every `.jsonValues()` entry in a module by loading
1358
1371
  * each backing `*.val.json` (via its lazy import thunk) and checking it against
@@ -1405,14 +1418,19 @@ function resolveExistingJsonPath(moduleFilePath, importPath) {
1405
1418
  */
1406
1419
  async function validateJsonValuesEntries(schema, source, modulePath) {
1407
1420
  const out = {};
1421
+ const loadedEntries = {};
1422
+ const res = {
1423
+ errors: out,
1424
+ loadedEntries
1425
+ };
1408
1426
  if (!(schema instanceof RecordSchema)) {
1409
- return out;
1427
+ return res;
1410
1428
  }
1411
1429
  if (!schema["executeSerialize"]().jsonValues) {
1412
- return out;
1430
+ return res;
1413
1431
  }
1414
1432
  if (source === null || typeof source !== "object" || Array.isArray(source)) {
1415
- return out;
1433
+ return res;
1416
1434
  }
1417
1435
  for (const [key, marker] of Object.entries(source)) {
1418
1436
  const entryPath = Internal.createValPathOfItem(modulePath, key);
@@ -1448,6 +1466,10 @@ async function validateJsonValuesEntries(schema, source, modulePath) {
1448
1466
  }];
1449
1467
  continue;
1450
1468
  }
1469
+ // Kept so a caller can substitute the content back into the module source:
1470
+ // the entries have been paid for here, and a source that still holds
1471
+ // markers cannot resolve a path INTO an entry (see the doc comment above).
1472
+ loadedEntries[key] = content;
1451
1473
  const entryErrors = schema.validateJsonEntryContent(entryPath, content);
1452
1474
  if (entryErrors) {
1453
1475
  for (const [p, errs] of Object.entries(entryErrors)) {
@@ -1455,9 +1477,155 @@ async function validateJsonValuesEntries(schema, source, modulePath) {
1455
1477
  }
1456
1478
  }
1457
1479
  }
1458
- return out;
1480
+ return res;
1481
+ }
1482
+
1483
+ /**
1484
+ * Given the `source` object-literal expression of a `.jsonValues()` record /
1485
+ * router (the 3rd argument to `c.define`, via {@link analyzeValModule}),
1486
+ * extracts each entry's `c.json(() => import("..."))` import path, keyed by
1487
+ * entry key. Entries whose value is not a `c.json(...)` call are skipped.
1488
+ */
1489
+ function analyzeJsonValuesEntries(source) {
1490
+ const entries = new Map();
1491
+ if (!ts.isObjectLiteralExpression(source)) {
1492
+ return entries;
1493
+ }
1494
+ for (const prop of source.properties) {
1495
+ if (!ts.isPropertyAssignment(prop)) {
1496
+ continue;
1497
+ }
1498
+ const key = getPropertyKey(prop.name);
1499
+ if (key === null) {
1500
+ continue;
1501
+ }
1502
+ const entry = analyzeCJsonCall(prop.initializer);
1503
+ if (entry) {
1504
+ entries.set(key, entry);
1505
+ }
1506
+ }
1507
+ return entries;
1508
+ }
1509
+ function getPropertyKey(name) {
1510
+ if (ts.isStringLiteralLike(name)) {
1511
+ return name.text;
1512
+ }
1513
+ if (ts.isIdentifier(name)) {
1514
+ return name.text;
1515
+ }
1516
+ return null;
1517
+ }
1518
+ function analyzeCJsonCall(node) {
1519
+ // c.json(() => import("path"))
1520
+ if (!ts.isCallExpression(node) || !isCJson(node.expression)) {
1521
+ return null;
1522
+ }
1523
+ const [thunk] = node.arguments;
1524
+ if (!thunk) {
1525
+ return null;
1526
+ }
1527
+ const importPath = getImportPathFromThunk(thunk);
1528
+ if (importPath === null) {
1529
+ return null;
1530
+ }
1531
+ return {
1532
+ importPath
1533
+ };
1534
+ }
1535
+ function isCJson(expr) {
1536
+ // c.json
1537
+ return ts.isPropertyAccessExpression(expr) && ts.isIdentifier(expr.expression) && expr.expression.text === "c" && ts.isIdentifier(expr.name) && expr.name.text === "json";
1538
+ }
1539
+ function getImportPathFromThunk(thunk) {
1540
+ // () => import("path") (concise body) OR () => { return import("path"); }
1541
+ if (!ts.isArrowFunction(thunk)) {
1542
+ return null;
1543
+ }
1544
+ let importCall;
1545
+ if (ts.isCallExpression(thunk.body)) {
1546
+ importCall = thunk.body;
1547
+ } else if (ts.isBlock(thunk.body)) {
1548
+ for (const stmt of thunk.body.statements) {
1549
+ if (ts.isReturnStatement(stmt) && stmt.expression && ts.isCallExpression(stmt.expression)) {
1550
+ importCall = stmt.expression;
1551
+ break;
1552
+ }
1553
+ }
1554
+ }
1555
+ if (!importCall || !ts.isCallExpression(importCall)) {
1556
+ return null;
1557
+ }
1558
+ const isImport = importCall.expression.kind === ts.SyntaxKind.ImportKeyword || ts.isIdentifier(importCall.expression) && importCall.expression.text === "import";
1559
+ if (!isImport) {
1560
+ return null;
1561
+ }
1562
+ const arg = importCall.arguments[0];
1563
+ if (arg && ts.isStringLiteralLike(arg)) {
1564
+ return arg.text;
1565
+ }
1566
+ return null;
1567
+ }
1568
+
1569
+ /**
1570
+ * Which `*.val.json` holds a `.jsonValues()` entry's content, given the module's
1571
+ * `.val.ts`.
1572
+ *
1573
+ * Tooling that maps a validation `sourcePath` back to a place in a file needs
1574
+ * this: for a jsonValues module the offending value is NOT in the `.val.ts` at
1575
+ * all — that file only holds `c.json(() => import("./x.val.json"))` — so a
1576
+ * resolver that only ever looks at the `.val.ts` can report the error but not
1577
+ * where it lives, which for a record with hundreds of entries is not much of a
1578
+ * report.
1579
+ *
1580
+ * Returns a path relative to the project root (leading slash), or undefined when
1581
+ * the module has no such entry (not a jsonValues module, unparseable, or the key
1582
+ * is not backed by a `c.json` thunk).
1583
+ */
1584
+ function findJsonEntryFilePath(moduleFilePath, valTsSourceFile, entryKey) {
1585
+ let analysis;
1586
+ try {
1587
+ analysis = analyzeValModule(valTsSourceFile);
1588
+ } catch {
1589
+ return undefined;
1590
+ }
1591
+ if (result.isErr(analysis)) {
1592
+ return undefined;
1593
+ }
1594
+ const entry = analyzeJsonValuesEntries(analysis.value.source).get(entryKey);
1595
+ if (!entry) {
1596
+ return undefined;
1597
+ }
1598
+ return resolveExistingJsonPath(moduleFilePath, entry.importPath);
1459
1599
  }
1460
1600
 
1601
+ const jsonOps$1 = new JSONOps();
1602
+
1603
+ /**
1604
+ * Substitutes loaded `.jsonValues()` entry content back into a module's root
1605
+ * record, in place of the `c.json(...)` markers the `.val.ts` holds.
1606
+ *
1607
+ * Only entries that actually loaded are replaced: an entry whose file is missing
1608
+ * or unparseable keeps its marker, so the failure stays visible as the
1609
+ * validation error it already produced rather than becoming a silent `undefined`
1610
+ * further down.
1611
+ */
1612
+ function withLoadedJsonEntries(source, loadedEntries) {
1613
+ if (Object.keys(loadedEntries).length === 0) {
1614
+ return source;
1615
+ }
1616
+ if (!isSourceObject(source)) {
1617
+ return source;
1618
+ }
1619
+ return {
1620
+ ...source,
1621
+ ...loadedEntries
1622
+ };
1623
+ }
1624
+
1625
+ /** A non-null, non-array source object: the shape a record/router source has. */
1626
+ function isSourceObject(source) {
1627
+ return typeof source === "object" && source !== null && !Array.isArray(source);
1628
+ }
1461
1629
  async function createService(projectRoot, host = {
1462
1630
  ...ts.sys,
1463
1631
  writeFile: (fileName, data, encoding) => {
@@ -1496,13 +1664,16 @@ class Service {
1496
1664
  getModuleFilePaths() {
1497
1665
  return Object.keys(this.extracted.sources);
1498
1666
  }
1667
+ serializedSchemaOf(moduleFilePath) {
1668
+ return this.extracted.serializedSchemas[moduleFilePath];
1669
+ }
1499
1670
  async get(moduleFilePath, modulePath, options) {
1500
1671
  const opts = options ?? {
1501
1672
  validate: true
1502
1673
  };
1503
1674
  const source = this.extracted.sources[moduleFilePath];
1504
1675
  const schema = this.extracted.schemas[moduleFilePath];
1505
- const serializedSchema = this.extracted.serializedSchemas[moduleFilePath];
1676
+ const serializedSchema = this.serializedSchemaOf(moduleFilePath);
1506
1677
  const moduleError = this.extracted.moduleErrors.find(e => e.path === moduleFilePath);
1507
1678
  // A module whose `def()` threw is recorded WITHOUT a path: the import never
1508
1679
  // got far enough to reveal one, so it cannot be matched above. Those errors
@@ -1534,6 +1705,12 @@ class Service {
1534
1705
  // entry content, or an unsupported nested `.jsonValues()`, as VALID, and CI
1535
1706
  // gates on that.
1536
1707
  let jsonValuesModuleError;
1708
+ // The source as it is on disk holds a `c.json(...)` marker for every
1709
+ // `.jsonValues()` entry, and validation reports errors at paths INSIDE those
1710
+ // entries. Resolving such a path against the marker source is impossible —
1711
+ // it throws — so the loaded content is substituted back in before anything
1712
+ // downstream (the `val validate --fix` handlers, chiefly) tries.
1713
+ let resolvedFromSource = source;
1537
1714
  if (opts.validate) {
1538
1715
  const nested = findNestedJsonValuesRecords(serializedSchema);
1539
1716
  if (nested.length > 0) {
@@ -1546,7 +1723,11 @@ class Service {
1546
1723
  // accepted cost of having no revalidation token (locked decision #3):
1547
1724
  // validation is allowed to be slower at scale, but it is not allowed to
1548
1725
  // silently skip content.
1549
- const entryErrors = await validateJsonValuesEntries(schema, source, moduleFilePath);
1726
+ const {
1727
+ errors: entryErrors,
1728
+ loadedEntries
1729
+ } = await validateJsonValuesEntries(schema, source, moduleFilePath);
1730
+ resolvedFromSource = withLoadedJsonEntries(source, loadedEntries);
1550
1731
  if (Object.keys(entryErrors).length > 0) {
1551
1732
  // Concatenate per path, never overwrite: an entry written inline is
1552
1733
  // reported BOTH by the record-level validation (which checks the
@@ -1564,7 +1745,7 @@ class Service {
1564
1745
  }
1565
1746
  }
1566
1747
  }
1567
- const resolved = Internal.resolvePath(modulePath, source, serializedSchema);
1748
+ const resolved = Internal.resolvePath(modulePath, resolvedFromSource, serializedSchema);
1568
1749
  const sourcePath = resolved.path ? [moduleFilePath, resolved.path].join(".") : moduleFilePath;
1569
1750
  if (!validation && !moduleError && !jsonValuesModuleError) {
1570
1751
  return {
@@ -1595,8 +1776,126 @@ class Service {
1595
1776
  }
1596
1777
  };
1597
1778
  }
1779
+
1780
+ /**
1781
+ * Applies a patch to a module's files.
1782
+ *
1783
+ * A `.jsonValues()` entry's content is not in the `.val.ts` — that file holds
1784
+ * only `c.json(() => import("./x.val.json"))` — so an op whose path descends
1785
+ * into an entry has to be replayed against the backing `*.val.json` instead.
1786
+ * That routing is the same one the Studio's commit flow does in
1787
+ * `ValOps.prepare`; both classify with {@link classifyJsonValuesOp} and rebase
1788
+ * with {@link rebaseContentOp}, so a CLI fix and a Studio publish write the
1789
+ * same file the same way.
1790
+ */
1598
1791
  async patch(moduleFilePath, patch) {
1599
- await patchValFile(moduleFilePath, this.projectRoot, patch, this.sourceFileHandler);
1792
+ const serializedSchema = this.serializedSchemaOf(moduleFilePath);
1793
+ const valTsOps = [];
1794
+ const entryOps = new Map();
1795
+ const classify = opPath => serializedSchema ? classifyJsonValuesOp(serializedSchema, opPath) : {
1796
+ kind: "normal"
1797
+ };
1798
+ for (const op of patch) {
1799
+ const cls = classify(op.path);
1800
+ // `move` and `copy` read a SECOND path, and `rebaseContentOp` slices
1801
+ // `from` by the same prefix it slices `path` by. So an op whose two ends
1802
+ // are not in the same place is not merely unsupported, it is silently
1803
+ // wrong: a copy INTO an entry from anywhere else gets its `from`
1804
+ // reinterpreted as a path inside the destination's `*.val.json` and reads
1805
+ // the wrong value. `ValOps.prepare` guards this on the publish path; both
1806
+ // ends have to be classified here for the same reason.
1807
+ const fromCls = op.op === "move" || op.op === "copy" ? classify(op.from) : undefined;
1808
+ if (cls.kind === "normal") {
1809
+ // Unreachable while `.jsonValues()` is root-only -- every top-level path
1810
+ // in such a module IS an entry key, so a normal destination means the
1811
+ // module has no jsonValues record and the source cannot be in one
1812
+ // either. Kept because it is the half of the guard that stops being
1813
+ // theoretical the moment root-only is relaxed.
1814
+ if (fromCls && fromCls.kind !== "normal") {
1815
+ throw Error(`Cannot ${op.op} out of the .jsonValues() entry '${fromCls.entryKey}' of ${moduleFilePath}: the entry's content is not in the .val.ts`);
1816
+ }
1817
+ valTsOps.push(op);
1818
+ continue;
1819
+ }
1820
+ if (cls.recordPath.length > 0) {
1821
+ // Nested `.jsonValues()` is rejected as a module error before any fix
1822
+ // runs, so reaching here means the contract moved without this moving
1823
+ // with it. Refuse rather than write to the wrong file.
1824
+ throw Error(`Cannot patch a nested .jsonValues() record in ${moduleFilePath}: only a root record/router is supported`);
1825
+ }
1826
+ if (cls.subPath.length === 0) {
1827
+ // Adding, removing or replacing a whole entry means writing the
1828
+ // `*.val.json` AND rewriting the `c.json(...)` reference in the
1829
+ // `.val.ts`. Nothing that reaches `Service.patch` produces those today
1830
+ // (`val validate --fix` only ever corrects values inside an entry), and
1831
+ // silently applying it to the `.val.ts` alone would leave the module
1832
+ // pointing at a file that does not exist.
1833
+ throw Error(`Cannot ${op.op} the whole .jsonValues() entry '${cls.entryKey}' of ${moduleFilePath} through Service.patch: only edits INSIDE an entry are supported`);
1834
+ }
1835
+ if (fromCls && (fromCls.kind !== "entry" || fromCls.entryKey !== cls.entryKey)) {
1836
+ throw Error(`Cannot ${op.op} into the .jsonValues() entry '${cls.entryKey}' of ${moduleFilePath} from outside it: each entry is a separate file`);
1837
+ }
1838
+ const ops = entryOps.get(cls.entryKey);
1839
+ if (ops) {
1840
+ ops.push(op);
1841
+ } else {
1842
+ entryOps.set(cls.entryKey, [op]);
1843
+ }
1844
+ }
1845
+ // When nothing is routed away, hand the patch over untouched: an empty
1846
+ // `valTsOps` would otherwise rewrite the `.val.ts` for no reason.
1847
+ if (entryOps.size === 0) {
1848
+ await patchValFile(moduleFilePath, this.projectRoot, patch, this.sourceFileHandler);
1849
+ return;
1850
+ }
1851
+ if (valTsOps.length > 0) {
1852
+ await patchValFile(moduleFilePath, this.projectRoot, valTsOps, this.sourceFileHandler);
1853
+ }
1854
+ for (const [entryKey, ops] of entryOps) {
1855
+ this.patchJsonValuesEntry(moduleFilePath, entryKey, ops);
1856
+ }
1857
+ }
1858
+
1859
+ /** Replays content ops against one `.jsonValues()` entry's `*.val.json`. */
1860
+ patchJsonValuesEntry(moduleFilePath, entryKey, ops) {
1861
+ const valTsPath = this.sourceFileHandler.resolveSourceModulePath(getSyntheticContainingPath(this.projectRoot), `.${moduleFilePath.replace(".val.ts", ".val").replace(".val.js", ".val").replace(".val.jsx", ".val").replace(".val.tsx", ".val")}`);
1862
+ const sourceFile = this.sourceFileHandler.getSourceFile(valTsPath);
1863
+ if (!sourceFile) {
1864
+ throw Error(`Source file ${valTsPath} not found`);
1865
+ }
1866
+ // The import specifier is authoritative, not the derived path: entries may
1867
+ // be hand-placed (hybrid authoring), so deriving would write a file the
1868
+ // module does not read.
1869
+ const jsonPath = findJsonEntryFilePath(moduleFilePath, sourceFile, entryKey);
1870
+ if (!jsonPath) {
1871
+ throw Error(`Could not find the '*.val.json' backing .jsonValues() entry '${entryKey}' of ${moduleFilePath}`);
1872
+ }
1873
+ const absoluteJsonPath = path__default.join(this.projectRoot, jsonPath);
1874
+ const text = this.sourceFileHandler.host.readFile(absoluteJsonPath);
1875
+ if (text === undefined) {
1876
+ throw Error(`Could not read jsonValues entry file: ${jsonPath}`);
1877
+ }
1878
+ let content;
1879
+ try {
1880
+ content = JSON.parse(text);
1881
+ } catch (err) {
1882
+ throw new Error(`Could not parse jsonValues entry file ${jsonPath}`, {
1883
+ cause: err
1884
+ });
1885
+ }
1886
+ for (const op of ops) {
1887
+ // Root record / router only, so the prefix is exactly the entry key.
1888
+ const rebased = rebaseContentOp(op, 1);
1889
+ if (result.isErr(rebased)) {
1890
+ throw Error(`Could not apply ${op.op} to jsonValues entry '${entryKey}' of ${moduleFilePath}: ${rebased.error.message}`);
1891
+ }
1892
+ const applied = applyPatch(deepClone(content), jsonOps$1, [rebased.value]);
1893
+ if (result.isErr(applied)) {
1894
+ throw Error(`Could not apply ${op.op} to ${jsonPath}: ${applied.error.message}`);
1895
+ }
1896
+ content = applied.value;
1897
+ }
1898
+ this.sourceFileHandler.writeFile(absoluteJsonPath, JSON.stringify(content, null, 2) + "\n", "utf8");
1600
1899
  }
1601
1900
  dispose() {
1602
1901
  // No-op: the vm-based loader holds no disposable resources.
@@ -1661,92 +1960,6 @@ function encodeJwt(payload, sessionKey) {
1661
1960
  return `${jwtHeaderBase64}.${payloadBase64}.${crypto$1.createHmac("sha256", sessionKey).update(`${jwtHeaderBase64}.${payloadBase64}`).digest("base64")}`;
1662
1961
  }
1663
1962
 
1664
- /**
1665
- * Given the `source` object-literal expression of a `.jsonValues()` record /
1666
- * router (the 3rd argument to `c.define`, via {@link analyzeValModule}),
1667
- * extracts each entry's `c.json(() => import("..."))` import path, keyed by
1668
- * entry key. Entries whose value is not a `c.json(...)` call are skipped.
1669
- */
1670
- function analyzeJsonValuesEntries(source) {
1671
- const entries = new Map();
1672
- if (!ts.isObjectLiteralExpression(source)) {
1673
- return entries;
1674
- }
1675
- for (const prop of source.properties) {
1676
- if (!ts.isPropertyAssignment(prop)) {
1677
- continue;
1678
- }
1679
- const key = getPropertyKey(prop.name);
1680
- if (key === null) {
1681
- continue;
1682
- }
1683
- const entry = analyzeCJsonCall(prop.initializer);
1684
- if (entry) {
1685
- entries.set(key, entry);
1686
- }
1687
- }
1688
- return entries;
1689
- }
1690
- function getPropertyKey(name) {
1691
- if (ts.isStringLiteralLike(name)) {
1692
- return name.text;
1693
- }
1694
- if (ts.isIdentifier(name)) {
1695
- return name.text;
1696
- }
1697
- return null;
1698
- }
1699
- function analyzeCJsonCall(node) {
1700
- // c.json(() => import("path"))
1701
- if (!ts.isCallExpression(node) || !isCJson(node.expression)) {
1702
- return null;
1703
- }
1704
- const [thunk] = node.arguments;
1705
- if (!thunk) {
1706
- return null;
1707
- }
1708
- const importPath = getImportPathFromThunk(thunk);
1709
- if (importPath === null) {
1710
- return null;
1711
- }
1712
- return {
1713
- importPath
1714
- };
1715
- }
1716
- function isCJson(expr) {
1717
- // c.json
1718
- return ts.isPropertyAccessExpression(expr) && ts.isIdentifier(expr.expression) && expr.expression.text === "c" && ts.isIdentifier(expr.name) && expr.name.text === "json";
1719
- }
1720
- function getImportPathFromThunk(thunk) {
1721
- // () => import("path") (concise body) OR () => { return import("path"); }
1722
- if (!ts.isArrowFunction(thunk)) {
1723
- return null;
1724
- }
1725
- let importCall;
1726
- if (ts.isCallExpression(thunk.body)) {
1727
- importCall = thunk.body;
1728
- } else if (ts.isBlock(thunk.body)) {
1729
- for (const stmt of thunk.body.statements) {
1730
- if (ts.isReturnStatement(stmt) && stmt.expression && ts.isCallExpression(stmt.expression)) {
1731
- importCall = stmt.expression;
1732
- break;
1733
- }
1734
- }
1735
- }
1736
- if (!importCall || !ts.isCallExpression(importCall)) {
1737
- return null;
1738
- }
1739
- const isImport = importCall.expression.kind === ts.SyntaxKind.ImportKeyword || ts.isIdentifier(importCall.expression) && importCall.expression.text === "import";
1740
- if (!isImport) {
1741
- return null;
1742
- }
1743
- const arg = importCall.arguments[0];
1744
- if (arg && ts.isStringLiteralLike(arg)) {
1745
- return arg.text;
1746
- }
1747
- return null;
1748
- }
1749
-
1750
1963
  /* eslint-disable @typescript-eslint/no-unused-vars */
1751
1964
  const jsonOps = new JSONOps();
1752
1965
  const tsOps = new TSOps(document => {
@@ -2621,7 +2834,9 @@ class ValOps {
2621
2834
  const res = schema["executeValidate"](path, source);
2622
2835
  // For `.jsonValues()` records, executeValidate only checks the entry
2623
2836
  // markers; load + validate each entry's backing `*.val.json` content here.
2624
- const jsonValuesErrors = await validateJsonValuesEntries(schema, source, path);
2837
+ const {
2838
+ errors: jsonValuesErrors
2839
+ } = await validateJsonValuesEntries(schema, source, path);
2625
2840
  for (const [sourcePathS, entryErrors] of Object.entries(jsonValuesErrors)) {
2626
2841
  const sourcePath = sourcePathS;
2627
2842
  if (!errors[path]) {
@@ -12486,38 +12701,6 @@ function traverseObjectLiteral(node, sourceFile) {
12486
12701
  }, {});
12487
12702
  }
12488
12703
 
12489
- /**
12490
- * Which `*.val.json` holds a `.jsonValues()` entry's content, given the module's
12491
- * `.val.ts`.
12492
- *
12493
- * Tooling that maps a validation `sourcePath` back to a place in a file needs
12494
- * this: for a jsonValues module the offending value is NOT in the `.val.ts` at
12495
- * all — that file only holds `c.json(() => import("./x.val.json"))` — so a
12496
- * resolver that only ever looks at the `.val.ts` can report the error but not
12497
- * where it lives, which for a record with hundreds of entries is not much of a
12498
- * report.
12499
- *
12500
- * Returns a path relative to the project root (leading slash), or undefined when
12501
- * the module has no such entry (not a jsonValues module, unparseable, or the key
12502
- * is not backed by a `c.json` thunk).
12503
- */
12504
- function findJsonEntryFilePath(moduleFilePath, valTsSourceFile, entryKey) {
12505
- let analysis;
12506
- try {
12507
- analysis = analyzeValModule(valTsSourceFile);
12508
- } catch {
12509
- return undefined;
12510
- }
12511
- if (result.isErr(analysis)) {
12512
- return undefined;
12513
- }
12514
- const entry = analyzeJsonValuesEntries(analysis.value.source).get(entryKey);
12515
- if (!entry) {
12516
- return undefined;
12517
- }
12518
- return resolveExistingJsonPath(moduleFilePath, entry.importPath);
12519
- }
12520
-
12521
12704
  /**
12522
12705
  * Replays a `val debug` snapshot: applies its patches the way /save does and
12523
12706
  * validates the result.
@@ -12617,4 +12800,4 @@ function readCapturedReport(snapshotDir) {
12617
12800
  return JSON.parse(fs.readFileSync(reportPath, "utf-8"));
12618
12801
  }
12619
12802
 
12620
- export { DEFAULT_LOGIN_HOST, DEFAULT_LOGIN_MAX_DURATION, DEFAULT_LOGIN_POLL_INTERVAL, Service, ValFSHost, ValLoginError, ValModuleLoader, ValOpsFS, ValOpsHttp, ValSourceFileHandler, analyzeValModule, awaitValLoginConfirmation, checkRemoteRef, compareWithCapturedReport, createDefaultValFSHost, createFixPatch, createJsonEntryPathMap, createModulePathMap, createService, createValApiRouter, createValModuleFileInspector, createValServer, currentFixHandlers, decodeJwt, describePatchStoreProblems, downloadFileFromRemote, encodeJwt, evalValConfigFile, extractFileMetadata, extractImageMetadata, extractJsonValuesEntry, findAndEvalValConfigFile, findJsonEntryFilePath, fixHandlers, formatPatchSourceError, formatSyntaxErrorTree, getCachedRemoteFileDir, getCachedRemoteFilePath, getCompilerOptions, getExpire, getFileExt, getModulePathRange, getPersonalAccessTokenPath, getSettings, getValidationErrorFileRef, handleCheckAllFiles, handleFileMetadata, handleJsonValuesExtractEntry, handleRemoteFileCheck, handleRemoteFileDownload, handleRemoteFileUpload, handleRemoteGalleryFileUpload, handleUniqueFolderCheck, loadValModules, parsePersonalAccessTokenFile, patchSourceFile, persistPersonalAccessToken, readCapturedReport, readPatchStore, replaySnapshot, safeReadGit, startValLogin, uploadRemoteFile, validateMetadata };
12803
+ export { DEFAULT_LOGIN_HOST, DEFAULT_LOGIN_MAX_DURATION, DEFAULT_LOGIN_POLL_INTERVAL, Service, ValFSHost, ValLoginError, ValModuleLoader, ValOpsFS, ValOpsHttp, ValSourceFileHandler, analyzeValModule, awaitValLoginConfirmation, checkRemoteRef, classifyJsonValuesOp, compareWithCapturedReport, createDefaultValFSHost, createFixPatch, createJsonEntryPathMap, createModulePathMap, createService, createValApiRouter, createValModuleFileInspector, createValServer, currentFixHandlers, decodeJwt, describePatchStoreProblems, downloadFileFromRemote, encodeJwt, evalValConfigFile, extractFileMetadata, extractImageMetadata, extractJsonValuesEntry, findAndEvalValConfigFile, findJsonEntryFilePath, fixHandlers, formatPatchSourceError, formatSyntaxErrorTree, getCachedRemoteFileDir, getCachedRemoteFilePath, getCompilerOptions, getExpire, getFileExt, getModulePathRange, getPersonalAccessTokenPath, getSettings, getValidationErrorFileRef, handleCheckAllFiles, handleFileMetadata, handleJsonValuesExtractEntry, handleRemoteFileCheck, handleRemoteFileDownload, handleRemoteFileUpload, handleRemoteGalleryFileUpload, handleUniqueFolderCheck, loadValModules, parsePersonalAccessTokenFile, patchSourceFile, persistPersonalAccessToken, readCapturedReport, readPatchStore, rebaseContentOp, replaySnapshot, safeReadGit, startValLogin, uploadRemoteFile, validateMetadata };
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "./package.json": "./package.json"
17
17
  },
18
18
  "types": "dist/valbuild-server.cjs.d.ts",
19
- "version": "0.108.3",
19
+ "version": "0.108.5",
20
20
  "devDependencies": {
21
21
  "@prettier/sync": "^0.6.1",
22
22
  "@types/jest": "^30.0.0"
@@ -29,9 +29,9 @@
29
29
  "typescript": "^6.0.3",
30
30
  "zod": "^4.4.3",
31
31
  "zod-validation-error": "^5.0.0",
32
- "@valbuild/core": "0.106.0",
33
32
  "@valbuild/shared": "0.108.0",
34
- "@valbuild/ui": "0.108.3"
33
+ "@valbuild/ui": "0.108.5",
34
+ "@valbuild/core": "0.106.0"
35
35
  },
36
36
  "engines": {
37
37
  "node": "^20.19.0 || >=22"