@valbuild/server 0.96.3 → 0.97.1

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.
@@ -1289,6 +1289,11 @@ export default new Proxy({}, {
1289
1289
  value: "export const ValApp = new Proxy({}, { get() { return () => { throw new Error(`Cannot import 'ValApp' in this file`) } } } )"
1290
1290
  };
1291
1291
  }
1292
+ if (modulePath.includes("/ValModulesClient")) {
1293
+ return {
1294
+ value: "export const ValModulesClient = new Proxy({}, { get() { return () => { throw new Error(`Cannot import 'ValModulesClient' in this file`) } } } ); export const useRegisterValModules = () => { throw new Error(`Cannot use 'useRegisterValModules' in this type of file`) };"
1295
+ };
1296
+ }
1292
1297
  return {
1293
1298
  value: moduleLoader.getModule(modulePath)
1294
1299
  };
@@ -1359,6 +1364,11 @@ export default new Proxy({}, {
1359
1364
  value: requestedName
1360
1365
  };
1361
1366
  }
1367
+ if (requestedName.includes("/ValModulesClient")) {
1368
+ return {
1369
+ value: requestedName
1370
+ };
1371
+ }
1362
1372
  const modulePath = moduleLoader.resolveModulePath(baseModuleName, requestedName);
1363
1373
  return {
1364
1374
  value: modulePath
@@ -1493,7 +1503,6 @@ function encodeJwt(payload, sessionKey) {
1493
1503
  }
1494
1504
 
1495
1505
  /* eslint-disable @typescript-eslint/no-unused-vars */
1496
- const textEncoder$2 = new TextEncoder();
1497
1506
  const jsonOps = new patch.JSONOps();
1498
1507
  const tsOps = new TSOps(document => {
1499
1508
  return fp.pipe(analyzeValModule(document), fp.result.map(({
@@ -1525,47 +1534,6 @@ class ValOps {
1525
1534
  this.configSha = null;
1526
1535
  this.modulesErrors = null;
1527
1536
  }
1528
- hash(input) {
1529
- if (typeof input === "object") {
1530
- return this.hashObject(input);
1531
- }
1532
- return core.Internal.getSHA256Hash(textEncoder$2.encode(input));
1533
- }
1534
- hashObject(obj) {
1535
- const collector = [];
1536
- this.collectObjectRecursive(obj, collector);
1537
- return core.Internal.getSHA256Hash(textEncoder$2.encode(collector.join("")));
1538
- }
1539
- collectObjectRecursive(item, collector) {
1540
- if (typeof item === "string") {
1541
- collector.push(`"`, item, `"`);
1542
- return;
1543
- } else if (typeof item === "number") {
1544
- collector.push(item.toString());
1545
- return;
1546
- } else if (typeof item === "object") {
1547
- if (Array.isArray(item)) {
1548
- collector.push("[");
1549
- for (let i = 0; i < item.length; i++) {
1550
- this.collectObjectRecursive(item[i], collector);
1551
- if (i !== item.length - 1) collector.push(",");
1552
- }
1553
- collector.push("]");
1554
- } else {
1555
- collector.push("{");
1556
- const keys = Object.keys(item).sort();
1557
- keys.forEach((key, i) => {
1558
- collector.push(`"${key}":`);
1559
- this.collectObjectRecursive(item[key], collector);
1560
- if (i !== keys.length - 1) collector.push(",");
1561
- });
1562
- collector.push("}");
1563
- }
1564
- return;
1565
- } else {
1566
- console.warn("Unknown type encountered when hashing object", typeof item, item);
1567
- }
1568
- }
1569
1537
 
1570
1538
  // #region stat
1571
1539
  /**
@@ -1581,93 +1549,23 @@ class ValOps {
1581
1549
  // #region initTree
1582
1550
  async initSources() {
1583
1551
  if (this.baseSha === null || this.sourcesSha === null || this.configSha === null || this.schemaSha === null || this.sources === null || this.schemas === null || this.modulesErrors === null) {
1584
- const currentModulesErrors = [];
1585
- const addModuleError = (message, index, path) => {
1586
- currentModulesErrors[index] = {
1587
- message,
1588
- path: path
1589
- };
1552
+ const extracted = await core.extractValModules(this.valModules);
1553
+ this.sources = extracted.sources;
1554
+ this.schemas = extracted.schemas;
1555
+ this.baseSha = extracted.baseSha;
1556
+ this.schemaSha = extracted.schemaSha;
1557
+ this.sourcesSha = extracted.sourcesSha;
1558
+ this.configSha = extracted.configSha;
1559
+ this.modulesErrors = extracted.moduleErrors;
1560
+ return {
1561
+ baseSha: this.baseSha,
1562
+ schemaSha: this.schemaSha,
1563
+ sourcesSha: this.sourcesSha,
1564
+ configSha: this.configSha,
1565
+ sources: extracted.sources,
1566
+ schemas: extracted.schemas,
1567
+ moduleErrors: extracted.moduleErrors
1590
1568
  };
1591
- const currentSources = {};
1592
- const currentSchemas = {};
1593
- const configSha = this.hash(JSON.stringify(this.valModules.config));
1594
- let sourcesSha = "";
1595
- let baseSha = configSha;
1596
- let schemaSha = configSha;
1597
- for (let moduleIdx = 0; moduleIdx < this.valModules.modules.length; moduleIdx++) {
1598
- const module = this.valModules.modules[moduleIdx];
1599
- if (!module.def) {
1600
- addModuleError("val.modules is missing 'def' property", moduleIdx);
1601
- continue;
1602
- }
1603
- if (typeof module.def !== "function") {
1604
- addModuleError("val.modules 'def' property is not a function", moduleIdx);
1605
- continue;
1606
- }
1607
- await module.def().then(value => {
1608
- if (!value) {
1609
- addModuleError(`val.modules 'def' did not return a value`, moduleIdx);
1610
- return;
1611
- }
1612
- if (!value.default) {
1613
- addModuleError(`val.modules 'def' did not return a default export`, moduleIdx);
1614
- return;
1615
- }
1616
- const path = core.Internal.getValPath(value.default);
1617
- if (path === undefined) {
1618
- addModuleError(`path is undefined`, moduleIdx);
1619
- return;
1620
- }
1621
- const schema = core.Internal.getSchema(value.default);
1622
- if (schema === undefined) {
1623
- addModuleError(`schema in path '${path}' is undefined`, moduleIdx, path);
1624
- return;
1625
- }
1626
- if (!(schema instanceof core.Schema)) {
1627
- addModuleError(`schema in path '${path}' is not an instance of Schema`, moduleIdx, path);
1628
- return;
1629
- }
1630
- if (typeof schema["executeSerialize"] !== "function") {
1631
- addModuleError(`schema.serialize in path '${path}' is not a function`, moduleIdx, path);
1632
- return;
1633
- }
1634
- const source = core.Internal.getSource(value.default);
1635
- if (source === undefined) {
1636
- addModuleError(`source in ${path} is undefined`, moduleIdx, path);
1637
- return;
1638
- }
1639
- let serializedSchema;
1640
- try {
1641
- serializedSchema = schema["executeSerialize"]();
1642
- } catch (e) {
1643
- const message = e instanceof Error ? e.message : JSON.stringify(e);
1644
- addModuleError(`Could not serialize module: '${path}'. Error: ${message}`, moduleIdx, path);
1645
- return;
1646
- }
1647
- const pathM = path;
1648
- currentSources[pathM] = source;
1649
- currentSchemas[pathM] = schema;
1650
- // make sure the checks above is enough that this does not fail - even if val modules are not set up correctly
1651
- sourcesSha = this.hash(sourcesSha + JSON.stringify({
1652
- path,
1653
- source
1654
- }));
1655
- baseSha = this.hash(baseSha + JSON.stringify({
1656
- path,
1657
- schema: serializedSchema,
1658
- source,
1659
- modulesErrors: currentModulesErrors
1660
- }));
1661
- schemaSha = this.hash(schemaSha + JSON.stringify(serializedSchema));
1662
- });
1663
- }
1664
- this.sources = currentSources;
1665
- this.schemas = currentSchemas;
1666
- this.baseSha = baseSha;
1667
- this.schemaSha = schemaSha;
1668
- this.sourcesSha = sourcesSha;
1669
- this.configSha = configSha;
1670
- this.modulesErrors = currentModulesErrors;
1671
1569
  }
1672
1570
  return {
1673
1571
  baseSha: this.baseSha,
@@ -1847,79 +1745,6 @@ class ValOps {
1847
1745
 
1848
1746
  // #region validateSources
1849
1747
  async validateSources(schemas, sources, patchesByModule) {
1850
- const checkKeyIsValid = async (key, sourcePath) => {
1851
- var _schemas$moduleFilePa;
1852
- const [moduleFilePath] = core.Internal.splitModuleFilePathAndModulePath(sourcePath);
1853
- const keyOfModuleSource = sources[moduleFilePath];
1854
- const keyOfModuleSchema = (_schemas$moduleFilePa = schemas[moduleFilePath]) === null || _schemas$moduleFilePa === void 0 ? void 0 : _schemas$moduleFilePa["executeSerialize"]();
1855
- if (keyOfModuleSchema && keyOfModuleSchema.type !== "record") {
1856
- return {
1857
- error: true,
1858
- message: `Expected key at ${sourcePath} to be of type 'record'`
1859
- };
1860
- }
1861
- if (keyOfModuleSource && typeof keyOfModuleSource === "object" && key in keyOfModuleSource) {
1862
- return {
1863
- error: false
1864
- };
1865
- }
1866
- if (!keyOfModuleSource || typeof keyOfModuleSource !== "object") {
1867
- return {
1868
- error: true,
1869
- message: `Expected ${sourcePath} to be a truthy object`
1870
- };
1871
- }
1872
- return {
1873
- error: true,
1874
- message: `Key '${key}' does not exist in ${sourcePath}.`
1875
- };
1876
- };
1877
- const checkRouteIsValid = async (route, includePattern, excludePattern) => {
1878
- // Find all router modules (record schemas with router property)
1879
- const routerModules = [];
1880
- for (const [moduleFilePath, schema] of Object.entries(schemas)) {
1881
- const serializedSchema = schema["executeSerialize"]();
1882
- if (serializedSchema.type === "record" && serializedSchema.router) {
1883
- const source = sources[moduleFilePath];
1884
- if (source && typeof source === "object") {
1885
- routerModules.push({
1886
- path: moduleFilePath,
1887
- routes: Object.keys(source)
1888
- });
1889
- }
1890
- }
1891
- }
1892
- if (routerModules.length === 0) {
1893
- return {
1894
- error: true,
1895
- message: `No router modules found. Route validation requires at least one s.record().router() module.`
1896
- };
1897
- }
1898
-
1899
- // Check if route exists in any router module
1900
- const allRoutes = routerModules.flatMap(m => m.routes);
1901
- const routeExists = allRoutes.includes(route);
1902
- if (!routeExists) {
1903
- // Filter routes by include/exclude patterns for suggestions
1904
- const validRoutes = internal.filterRoutesByPatterns(allRoutes, includePattern, excludePattern);
1905
- return {
1906
- error: true,
1907
- message: `Route '${route}' does not exist in any router module. Available routes: ${validRoutes.slice(0, 10).join(", ")}${validRoutes.length > 10 ? "..." : ""}`
1908
- };
1909
- }
1910
-
1911
- // Validate against include/exclude patterns
1912
- const patternValidation = internal.validateRoutePatterns(route, includePattern, excludePattern);
1913
- if (!patternValidation.valid) {
1914
- return {
1915
- error: true,
1916
- message: patternValidation.message
1917
- };
1918
- }
1919
- return {
1920
- error: false
1921
- };
1922
- };
1923
1748
  const errors = {};
1924
1749
  const files = {};
1925
1750
  const remoteFiles = {};
@@ -1927,15 +1752,27 @@ class ValOps {
1927
1752
  // Build a map of gallery directory → [ModuleFilePath, ...] across ALL modules
1928
1753
  // (must include all modules, not just those being validated, since conflicts can come from any module)
1929
1754
  const galleryDirectoryToModules = new Map();
1755
+ // Build a schema/source snapshot so the shared resolver can cross-reference
1756
+ // keyof:check-keys and router:check-route against every module's data.
1757
+ const snapshot = {
1758
+ schemas: {},
1759
+ sources: {}
1760
+ };
1930
1761
  for (const [moduleFilePathS, schema] of entries) {
1762
+ const moduleFilePath = moduleFilePathS;
1931
1763
  const serialized = schema["executeSerialize"]();
1764
+ snapshot.schemas[moduleFilePath] = serialized;
1765
+ const sourceForModule = sources[moduleFilePath];
1766
+ if (sourceForModule !== undefined) {
1767
+ snapshot.sources[moduleFilePath] = sourceForModule;
1768
+ }
1932
1769
  if (serialized.type === "record" && serialized.mediaType && serialized.directory) {
1933
1770
  const dir = serialized.directory;
1934
1771
  const existing = galleryDirectoryToModules.get(dir);
1935
1772
  if (existing) {
1936
- existing.push(moduleFilePathS);
1773
+ existing.push(moduleFilePath);
1937
1774
  } else {
1938
- galleryDirectoryToModules.set(dir, [moduleFilePathS]);
1775
+ galleryDirectoryToModules.set(dir, [moduleFilePath]);
1939
1776
  }
1940
1777
  }
1941
1778
  }
@@ -1993,78 +1830,12 @@ class ValOps {
1993
1830
  }
1994
1831
  } else if ((_validationError$fixe = validationError.fixes) !== null && _validationError$fixe !== void 0 && _validationError$fixe.includes("image:check-remote") || (_validationError$fixe2 = validationError.fixes) !== null && _validationError$fixe2 !== void 0 && _validationError$fixe2.includes("file:check-remote")) {
1995
1832
  remoteFiles[sourcePath] = validationError.value;
1996
- } else if ((_validationError$fixe3 = validationError.fixes) !== null && _validationError$fixe3 !== void 0 && _validationError$fixe3.includes("keyof:check-keys")) {
1997
- const TYPE_ERROR_MESSAGE = `This is most likely a Val version mismatch or Val bug.`;
1998
- if (!validationError.value) {
1999
- addError({
2000
- message: `Could not find a value for keyOf at ${sourcePath}. ${TYPE_ERROR_MESSAGE}`,
2001
- // Not sure this is a type error, but it shouldn't happen in a normally functioning Val system
2002
- typeError: true
2003
- });
2004
- } else {
2005
- if (typeof validationError.value !== "object") {
2006
- addError({
2007
- message: `Expected keyOf validation error to have a 'value' property of type 'object'. Found: ${typeof validationError.value}. ${TYPE_ERROR_MESSAGE}`,
2008
- // Not sure this is a type error, but it shouldn't happen in a normally functioning Val system
2009
- typeError: true
2010
- });
2011
- } else {
2012
- const key = "key" in validationError.value && validationError.value.key;
2013
- const validationErrorSourcePath = "sourcePath" in validationError.value && validationError.value.sourcePath;
2014
- if (typeof key !== "string") {
2015
- addError({
2016
- message: `Expected keyOf validation error 'value' to have property 'key' of type 'string'. Found: ${typeof key}. ${TYPE_ERROR_MESSAGE}`,
2017
- // Not sure this is a type error, but it shouldn't happen in a normally functioning Val system
2018
- typeError: true
2019
- });
2020
- } else if (typeof validationErrorSourcePath !== "string") {
2021
- addError({
2022
- message: `Expected keyOf validation error 'value' to have property 'sourcePath' of type 'string'. Found: ${typeof validationErrorSourcePath}. ${TYPE_ERROR_MESSAGE}`,
2023
- // Not sure this is a type error, but it shouldn't happen in a normally functioning Val system
2024
- typeError: true
2025
- });
2026
- } else {
2027
- const res = await checkKeyIsValid(key, validationErrorSourcePath);
2028
- if (res.error) {
2029
- addError({
2030
- message: res.message
2031
- });
2032
- }
2033
- }
2034
- }
2035
- }
2036
- } else if ((_validationError$fixe4 = validationError.fixes) !== null && _validationError$fixe4 !== void 0 && _validationError$fixe4.includes("router:check-route")) {
2037
- const TYPE_ERROR_MESSAGE = `This is most likely a Val version mismatch or Val bug.`;
2038
- if (!validationError.value) {
2039
- addError({
2040
- message: `Could not find a value for route at ${sourcePath}. ${TYPE_ERROR_MESSAGE}`,
2041
- typeError: true
2042
- });
2043
- } else {
2044
- if (typeof validationError.value !== "object") {
2045
- addError({
2046
- message: `Expected route validation error to have a 'value' property of type 'object'. Found: ${typeof validationError.value}. ${TYPE_ERROR_MESSAGE}`,
2047
- typeError: true
2048
- });
2049
- } else {
2050
- const route = "route" in validationError.value && validationError.value.route;
2051
- const includePattern = "include" in validationError.value && validationError.value.include;
2052
- const excludePattern = "exclude" in validationError.value && validationError.value.exclude;
2053
- if (typeof route !== "string") {
2054
- addError({
2055
- message: `Expected route validation error 'value' to have property 'route' of type 'string'. Found: ${typeof route}. ${TYPE_ERROR_MESSAGE}`,
2056
- typeError: true
2057
- });
2058
- } else {
2059
- const res = await checkRouteIsValid(route, includePattern && typeof includePattern === "object" && "source" in includePattern && "flags" in includePattern ? includePattern : undefined, excludePattern && typeof excludePattern === "object" && "source" in excludePattern && "flags" in excludePattern ? excludePattern : undefined);
2060
- if (res.error) {
2061
- addError({
2062
- message: res.message
2063
- });
2064
- }
2065
- }
2066
- }
1833
+ } else if ((_validationError$fixe3 = validationError.fixes) !== null && _validationError$fixe3 !== void 0 && _validationError$fixe3.includes("keyof:check-keys") || (_validationError$fixe4 = validationError.fixes) !== null && _validationError$fixe4 !== void 0 && _validationError$fixe4.includes("router:check-route")) {
1834
+ const resolved = internal.resolveSchemaSourceFixForError(validationError, snapshot);
1835
+ if (resolved && resolved.status === "remaining") {
1836
+ addError(resolved.error);
2067
1837
  }
1838
+ // resolved.status === "resolved" → drop silently
2068
1839
  } else if ((_validationError$fixe5 = validationError.fixes) !== null && _validationError$fixe5 !== void 0 && _validationError$fixe5.includes("images:check-unique-folder") || (_validationError$fixe6 = validationError.fixes) !== null && _validationError$fixe6 !== void 0 && _validationError$fixe6.includes("files:check-unique-folder")) {
2069
1840
  const TYPE_ERROR_MESSAGE = `This is most likely a Val version mismatch or Val bug.`;
2070
1841
  if (!validationError.value || typeof validationError.value !== "object") {
@@ -4741,7 +4512,7 @@ function hasRemoteFileSchema(schema) {
4741
4512
  }
4742
4513
  }
4743
4514
  return false;
4744
- } else if (schema.type === "boolean" || schema.type === "number" || schema.type === "string" || schema.type === "literal" || schema.type === "date" || schema.type === "keyOf" || schema.type === "route") {
4515
+ } else if (schema.type === "boolean" || schema.type === "number" || schema.type === "string" || schema.type === "literal" || schema.type === "date" || schema.type === "dateTime" || schema.type === "keyOf" || schema.type === "route") {
4745
4516
  return false;
4746
4517
  } else {
4747
4518
  const exhaustiveCheck = schema;
@@ -5915,21 +5686,29 @@ const ValServer = (valModules, options, callbacks) => {
5915
5686
  const schemasRes = await serverOps.getSchemas();
5916
5687
  let sourcesRes = await serverOps.getSources();
5917
5688
  const unpatchedSources = sourcesRes.sources;
5918
- const onlyPatchedTreeModules = await serverOps.getSources({
5919
- ...patchAnalysis,
5920
- ...patchOps
5921
- });
5922
- sourcesRes = {
5923
- sources: {
5924
- ...sourcesRes.sources,
5925
- ...(onlyPatchedTreeModules.sources || {})
5926
- },
5927
- errors: {
5928
- ...sourcesRes.errors,
5929
- ...(onlyPatchedTreeModules.errors || {})
5930
- }
5689
+ // Default to true to keep the legacy contract for older clients.
5690
+ // The studio client always passes false: it owns patch application
5691
+ // and rendering, and treats /sources/~ as a pure un-patched read.
5692
+ const applyPatches = query.apply_patches !== false;
5693
+ if (applyPatches) {
5694
+ const onlyPatchedTreeModules = await serverOps.getSources({
5695
+ ...patchAnalysis,
5696
+ ...patchOps
5697
+ });
5698
+ sourcesRes = {
5699
+ sources: {
5700
+ ...sourcesRes.sources,
5701
+ ...(onlyPatchedTreeModules.sources || {})
5702
+ },
5703
+ errors: {
5704
+ ...sourcesRes.errors,
5705
+ ...(onlyPatchedTreeModules.errors || {})
5706
+ }
5707
+ };
5708
+ }
5709
+ const renderRes = applyPatches ? await serverOps.getRenders(schemasRes, sourcesRes.sources) : {
5710
+ renders: {}
5931
5711
  };
5932
- const renderRes = await serverOps.getRenders(schemasRes, sourcesRes.sources);
5933
5712
  let sourcesValidation = {
5934
5713
  errors: {},
5935
5714
  files: {},
@@ -5977,7 +5756,9 @@ const ValServer = (valModules, options, callbacks) => {
5977
5756
  const hasPatches = (((_patchAnalysis$patche2 = patchAnalysis.patchesByModule[moduleFilePath]) === null || _patchAnalysis$patche2 === void 0 ? void 0 : _patchAnalysis$patche2.length) ?? 0) > 0;
5978
5757
  modules[moduleFilePath] = {
5979
5758
  source: module,
5980
- baseSource: hasPatches ? unpatchedSources[moduleFilePath] : undefined,
5759
+ // baseSource is only meaningful when the server applied patches:
5760
+ // with apply_patches=false, `source` is already un-patched.
5761
+ baseSource: applyPatches && hasPatches ? unpatchedSources[moduleFilePath] : undefined,
5981
5762
  render: renderRes.renders[moduleFilePath] || null,
5982
5763
  patches: appliedPatches.length > 0 || skippedPatches.length > 0 || Object.keys(patchErrors).length > 0 ? {
5983
5764
  applied: appliedPatches,