@valbuild/server 0.101.1 → 0.102.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.
@@ -20,7 +20,7 @@ export { extractImageMetadata, extractFileMetadata } from "./extractMetadata.js"
20
20
  export { validateMetadata } from "./validateMetadata.js";
21
21
  export { getValidationErrorFileRef } from "./getValidationErrorFileRef.js";
22
22
  export { checkRemoteRef, downloadFileFromRemote, getCachedRemoteFileDir, getCachedRemoteFilePath, } from "./checkRemoteRef.js";
23
- export { hasRemoteFileSchema } from "./hasRemoteFileSchema.js";
23
+ export { hasRemoteFileSchema } from "@valbuild/core";
24
24
  export { getFileExt } from "./getFileExt.js";
25
25
  export { evalValConfigFile, findAndEvalValConfigFile, } from "./evalValConfigFile.js";
26
26
  export { startValLogin, awaitValLoginConfirmation, persistPersonalAccessToken, ValLoginError, DEFAULT_LOGIN_HOST, DEFAULT_LOGIN_MAX_DURATION, DEFAULT_LOGIN_POLL_INTERVAL, } from "./login.js";
@@ -1562,6 +1562,35 @@ function resolveExistingJsonPath(moduleFilePath, importPath) {
1562
1562
  * module errors (see `findNestedJsonValuesRecords`), so they can never reach
1563
1563
  * here — if that guard is ever relaxed, this must become a recursive visitor or
1564
1564
  * nested entries silently get no content validation at all.
1565
+ *
1566
+ * ## NOT validated, and it should be: the entry's FILE PATH
1567
+ *
1568
+ * The key↔file mapping is canonical. `getNewJsonEntryPaths` derives it, and both
1569
+ * write paths use it — the `jsonValues:extract-entry` fix and the patch commit:
1570
+ *
1571
+ * /content/kb.val.ts + key "kb-000" -> /content/kb/kb-000.val.json
1572
+ *
1573
+ * This function does not check it. It calls the thunk and validates whatever
1574
+ * comes back, so an entry may point at ANY file in the project and pass. The
1575
+ * repo's own fixture does exactly that — `examples/next/content/kb.val.ts` has
1576
+ * `"kb-000": c.json(() => import("./kb/entry-000.val.json"))`, where the
1577
+ * canonical path is `./kb/kb-000.val.json`, and `val validate` calls it valid.
1578
+ *
1579
+ * Two consequences, the first a live bug:
1580
+ *
1581
+ * - **A key rename writes to the DERIVED path**, which for a non-canonical entry
1582
+ * is not the file the old key was reading. The old file is orphaned and the new
1583
+ * key reads a file that was never written.
1584
+ * - It blocks localising entry staleness. `jsonEntriesSha` cannot say which entry
1585
+ * changed, so the client drops a whole module's loaded content; with the mapping
1586
+ * guaranteed, a changed file resolves to one key by derivation alone.
1587
+ *
1588
+ * The fix belongs here and needs no new machinery: the module's `.val.ts` is
1589
+ * already parsed (`ValSourceFileHandler`), so the import specifier is readable
1590
+ * from the AST — no `thunk.toString()`, which a bundler would defeat. A
1591
+ * `jsonValues:rename-entry-file` fix could move the file and rewrite the import,
1592
+ * exactly as `extract-entry` writes both. See `packages/ui/spa/stores/openquestions.md`
1593
+ * item 9b.
1565
1594
  */
1566
1595
  async function validateJsonValuesEntries(schema, source, modulePath) {
1567
1596
  const out = {};
@@ -5823,51 +5852,6 @@ function parsePersonalAccessTokenFile(content) {
5823
5852
  }
5824
5853
  }
5825
5854
 
5826
- /**
5827
- * Iterates through all schemas and find if there is 1 or more remote files in them
5828
- */
5829
- function hasRemoteFileSchema(schema) {
5830
- if (schema.type === "file" || schema.type === "image") {
5831
- return !!schema.remote;
5832
- } else if (schema.type === "richtext") {
5833
- var _schema$options;
5834
- if (typeof ((_schema$options = schema.options) === null || _schema$options === void 0 || (_schema$options = _schema$options.inline) === null || _schema$options === void 0 ? void 0 : _schema$options.img) === "object") {
5835
- return hasRemoteFileSchema(schema.options.inline.img);
5836
- }
5837
- return false;
5838
- } else if (schema.type === "array" || schema.type === "record") {
5839
- return hasRemoteFileSchema(schema.item);
5840
- } else if (schema.type === "object") {
5841
- for (const key in schema.items) {
5842
- const hasRemoteFile = hasRemoteFileSchema(schema.items[key]);
5843
- if (hasRemoteFile) {
5844
- return true;
5845
- }
5846
- }
5847
- return false;
5848
- } else if (schema.type === "union") {
5849
- const unionStringSchema = typeof schema.key === "object" && schema.key.type === "literal" ? schema : undefined;
5850
- const unionObjectSchema = typeof schema.key === "string" ? schema : undefined;
5851
- if (unionStringSchema) {
5852
- return false;
5853
- }
5854
- if (unionObjectSchema) {
5855
- for (const key in unionObjectSchema.items) {
5856
- const hasRemoteFile = hasRemoteFileSchema(unionObjectSchema.items[key]);
5857
- if (hasRemoteFile) {
5858
- return true;
5859
- }
5860
- }
5861
- }
5862
- return false;
5863
- } else if (schema.type === "boolean" || schema.type === "number" || schema.type === "string" || schema.type === "literal" || schema.type === "date" || schema.type === "dateTime" || schema.type === "color" || schema.type === "keyOf" || schema.type === "route") {
5864
- return false;
5865
- } else {
5866
- const exhaustiveCheck = schema;
5867
- throw new Error(`Unexpected schema: ${JSON.stringify(exhaustiveCheck)}`);
5868
- }
5869
- }
5870
-
5871
5855
  /* eslint-disable @typescript-eslint/no-unused-vars */
5872
5856
  const ValServer = (valModules, options, callbacks) => {
5873
5857
  const AIContentBlock = z.z.union([z.z.object({
@@ -8584,7 +8568,7 @@ function getIsRemoteRequired(schemas) {
8584
8568
  for (const moduleFilePathS in schemas) {
8585
8569
  const moduleFilePath = moduleFilePathS;
8586
8570
  const schema = schemas[moduleFilePath]["executeSerialize"]();
8587
- const isRemoteRequired = hasRemoteFileSchema(schema);
8571
+ const isRemoteRequired = core.hasRemoteFileSchema(schema);
8588
8572
  if (isRemoteRequired) {
8589
8573
  return true;
8590
8574
  }
@@ -10593,6 +10577,10 @@ function readCapturedReport(snapshotDir) {
10593
10577
  return JSON.parse(fs__default["default"].readFileSync(reportPath, "utf-8"));
10594
10578
  }
10595
10579
 
10580
+ Object.defineProperty(exports, 'hasRemoteFileSchema', {
10581
+ enumerable: true,
10582
+ get: function () { return core.hasRemoteFileSchema; }
10583
+ });
10596
10584
  exports.DEFAULT_LOGIN_HOST = DEFAULT_LOGIN_HOST;
10597
10585
  exports.DEFAULT_LOGIN_MAX_DURATION = DEFAULT_LOGIN_MAX_DURATION;
10598
10586
  exports.DEFAULT_LOGIN_POLL_INTERVAL = DEFAULT_LOGIN_POLL_INTERVAL;
@@ -10633,7 +10621,6 @@ exports.getModulePathRange = getModulePathRange;
10633
10621
  exports.getPersonalAccessTokenPath = getPersonalAccessTokenPath;
10634
10622
  exports.getSettings = getSettings;
10635
10623
  exports.getValidationErrorFileRef = getValidationErrorFileRef;
10636
- exports.hasRemoteFileSchema = hasRemoteFileSchema;
10637
10624
  exports.loadValModules = loadValModules;
10638
10625
  exports.parsePersonalAccessTokenFile = parsePersonalAccessTokenFile;
10639
10626
  exports.patchSourceFile = patchSourceFile;
@@ -1562,6 +1562,35 @@ function resolveExistingJsonPath(moduleFilePath, importPath) {
1562
1562
  * module errors (see `findNestedJsonValuesRecords`), so they can never reach
1563
1563
  * here — if that guard is ever relaxed, this must become a recursive visitor or
1564
1564
  * nested entries silently get no content validation at all.
1565
+ *
1566
+ * ## NOT validated, and it should be: the entry's FILE PATH
1567
+ *
1568
+ * The key↔file mapping is canonical. `getNewJsonEntryPaths` derives it, and both
1569
+ * write paths use it — the `jsonValues:extract-entry` fix and the patch commit:
1570
+ *
1571
+ * /content/kb.val.ts + key "kb-000" -> /content/kb/kb-000.val.json
1572
+ *
1573
+ * This function does not check it. It calls the thunk and validates whatever
1574
+ * comes back, so an entry may point at ANY file in the project and pass. The
1575
+ * repo's own fixture does exactly that — `examples/next/content/kb.val.ts` has
1576
+ * `"kb-000": c.json(() => import("./kb/entry-000.val.json"))`, where the
1577
+ * canonical path is `./kb/kb-000.val.json`, and `val validate` calls it valid.
1578
+ *
1579
+ * Two consequences, the first a live bug:
1580
+ *
1581
+ * - **A key rename writes to the DERIVED path**, which for a non-canonical entry
1582
+ * is not the file the old key was reading. The old file is orphaned and the new
1583
+ * key reads a file that was never written.
1584
+ * - It blocks localising entry staleness. `jsonEntriesSha` cannot say which entry
1585
+ * changed, so the client drops a whole module's loaded content; with the mapping
1586
+ * guaranteed, a changed file resolves to one key by derivation alone.
1587
+ *
1588
+ * The fix belongs here and needs no new machinery: the module's `.val.ts` is
1589
+ * already parsed (`ValSourceFileHandler`), so the import specifier is readable
1590
+ * from the AST — no `thunk.toString()`, which a bundler would defeat. A
1591
+ * `jsonValues:rename-entry-file` fix could move the file and rewrite the import,
1592
+ * exactly as `extract-entry` writes both. See `packages/ui/spa/stores/openquestions.md`
1593
+ * item 9b.
1565
1594
  */
1566
1595
  async function validateJsonValuesEntries(schema, source, modulePath) {
1567
1596
  const out = {};
@@ -5823,51 +5852,6 @@ function parsePersonalAccessTokenFile(content) {
5823
5852
  }
5824
5853
  }
5825
5854
 
5826
- /**
5827
- * Iterates through all schemas and find if there is 1 or more remote files in them
5828
- */
5829
- function hasRemoteFileSchema(schema) {
5830
- if (schema.type === "file" || schema.type === "image") {
5831
- return !!schema.remote;
5832
- } else if (schema.type === "richtext") {
5833
- var _schema$options;
5834
- if (typeof ((_schema$options = schema.options) === null || _schema$options === void 0 || (_schema$options = _schema$options.inline) === null || _schema$options === void 0 ? void 0 : _schema$options.img) === "object") {
5835
- return hasRemoteFileSchema(schema.options.inline.img);
5836
- }
5837
- return false;
5838
- } else if (schema.type === "array" || schema.type === "record") {
5839
- return hasRemoteFileSchema(schema.item);
5840
- } else if (schema.type === "object") {
5841
- for (const key in schema.items) {
5842
- const hasRemoteFile = hasRemoteFileSchema(schema.items[key]);
5843
- if (hasRemoteFile) {
5844
- return true;
5845
- }
5846
- }
5847
- return false;
5848
- } else if (schema.type === "union") {
5849
- const unionStringSchema = typeof schema.key === "object" && schema.key.type === "literal" ? schema : undefined;
5850
- const unionObjectSchema = typeof schema.key === "string" ? schema : undefined;
5851
- if (unionStringSchema) {
5852
- return false;
5853
- }
5854
- if (unionObjectSchema) {
5855
- for (const key in unionObjectSchema.items) {
5856
- const hasRemoteFile = hasRemoteFileSchema(unionObjectSchema.items[key]);
5857
- if (hasRemoteFile) {
5858
- return true;
5859
- }
5860
- }
5861
- }
5862
- return false;
5863
- } else if (schema.type === "boolean" || schema.type === "number" || schema.type === "string" || schema.type === "literal" || schema.type === "date" || schema.type === "dateTime" || schema.type === "color" || schema.type === "keyOf" || schema.type === "route") {
5864
- return false;
5865
- } else {
5866
- const exhaustiveCheck = schema;
5867
- throw new Error(`Unexpected schema: ${JSON.stringify(exhaustiveCheck)}`);
5868
- }
5869
- }
5870
-
5871
5855
  /* eslint-disable @typescript-eslint/no-unused-vars */
5872
5856
  const ValServer = (valModules, options, callbacks) => {
5873
5857
  const AIContentBlock = z.z.union([z.z.object({
@@ -8584,7 +8568,7 @@ function getIsRemoteRequired(schemas) {
8584
8568
  for (const moduleFilePathS in schemas) {
8585
8569
  const moduleFilePath = moduleFilePathS;
8586
8570
  const schema = schemas[moduleFilePath]["executeSerialize"]();
8587
- const isRemoteRequired = hasRemoteFileSchema(schema);
8571
+ const isRemoteRequired = core.hasRemoteFileSchema(schema);
8588
8572
  if (isRemoteRequired) {
8589
8573
  return true;
8590
8574
  }
@@ -10593,6 +10577,10 @@ function readCapturedReport(snapshotDir) {
10593
10577
  return JSON.parse(fs__default["default"].readFileSync(reportPath, "utf-8"));
10594
10578
  }
10595
10579
 
10580
+ Object.defineProperty(exports, 'hasRemoteFileSchema', {
10581
+ enumerable: true,
10582
+ get: function () { return core.hasRemoteFileSchema; }
10583
+ });
10596
10584
  exports.DEFAULT_LOGIN_HOST = DEFAULT_LOGIN_HOST;
10597
10585
  exports.DEFAULT_LOGIN_MAX_DURATION = DEFAULT_LOGIN_MAX_DURATION;
10598
10586
  exports.DEFAULT_LOGIN_POLL_INTERVAL = DEFAULT_LOGIN_POLL_INTERVAL;
@@ -10633,7 +10621,6 @@ exports.getModulePathRange = getModulePathRange;
10633
10621
  exports.getPersonalAccessTokenPath = getPersonalAccessTokenPath;
10634
10622
  exports.getSettings = getSettings;
10635
10623
  exports.getValidationErrorFileRef = getValidationErrorFileRef;
10636
- exports.hasRemoteFileSchema = hasRemoteFileSchema;
10637
10624
  exports.loadValModules = loadValModules;
10638
10625
  exports.parsePersonalAccessTokenFile = parsePersonalAccessTokenFile;
10639
10626
  exports.patchSourceFile = patchSourceFile;
@@ -1,6 +1,7 @@
1
1
  import ts from 'typescript';
2
2
  import { pipe, result, array } from '@valbuild/core/fp';
3
- import { FILE_REF_PROP, FILE_REF_SUBTYPE_TAG, VAL_EXTENSION, derefPatch, RecordSchema, Internal, extractValModules, ImageSchema, DEFAULT_CONTENT_HOST } from '@valbuild/core';
3
+ import { FILE_REF_PROP, FILE_REF_SUBTYPE_TAG, VAL_EXTENSION, derefPatch, RecordSchema, Internal, extractValModules, ImageSchema, DEFAULT_CONTENT_HOST, hasRemoteFileSchema } from '@valbuild/core';
4
+ export { hasRemoteFileSchema } from '@valbuild/core';
4
5
  import { PatchError, deepEqual, parseAndValidateArrayIndex, isNotRoot, applyPatch, JSONOps, deepClone, sourceToPatchPath } from '@valbuild/core/patch';
5
6
  import * as path from 'path';
6
7
  import path__default from 'path';
@@ -1528,6 +1529,35 @@ function resolveExistingJsonPath(moduleFilePath, importPath) {
1528
1529
  * module errors (see `findNestedJsonValuesRecords`), so they can never reach
1529
1530
  * here — if that guard is ever relaxed, this must become a recursive visitor or
1530
1531
  * nested entries silently get no content validation at all.
1532
+ *
1533
+ * ## NOT validated, and it should be: the entry's FILE PATH
1534
+ *
1535
+ * The key↔file mapping is canonical. `getNewJsonEntryPaths` derives it, and both
1536
+ * write paths use it — the `jsonValues:extract-entry` fix and the patch commit:
1537
+ *
1538
+ * /content/kb.val.ts + key "kb-000" -> /content/kb/kb-000.val.json
1539
+ *
1540
+ * This function does not check it. It calls the thunk and validates whatever
1541
+ * comes back, so an entry may point at ANY file in the project and pass. The
1542
+ * repo's own fixture does exactly that — `examples/next/content/kb.val.ts` has
1543
+ * `"kb-000": c.json(() => import("./kb/entry-000.val.json"))`, where the
1544
+ * canonical path is `./kb/kb-000.val.json`, and `val validate` calls it valid.
1545
+ *
1546
+ * Two consequences, the first a live bug:
1547
+ *
1548
+ * - **A key rename writes to the DERIVED path**, which for a non-canonical entry
1549
+ * is not the file the old key was reading. The old file is orphaned and the new
1550
+ * key reads a file that was never written.
1551
+ * - It blocks localising entry staleness. `jsonEntriesSha` cannot say which entry
1552
+ * changed, so the client drops a whole module's loaded content; with the mapping
1553
+ * guaranteed, a changed file resolves to one key by derivation alone.
1554
+ *
1555
+ * The fix belongs here and needs no new machinery: the module's `.val.ts` is
1556
+ * already parsed (`ValSourceFileHandler`), so the import specifier is readable
1557
+ * from the AST — no `thunk.toString()`, which a bundler would defeat. A
1558
+ * `jsonValues:rename-entry-file` fix could move the file and rewrite the import,
1559
+ * exactly as `extract-entry` writes both. See `packages/ui/spa/stores/openquestions.md`
1560
+ * item 9b.
1531
1561
  */
1532
1562
  async function validateJsonValuesEntries(schema, source, modulePath) {
1533
1563
  const out = {};
@@ -5789,51 +5819,6 @@ function parsePersonalAccessTokenFile(content) {
5789
5819
  }
5790
5820
  }
5791
5821
 
5792
- /**
5793
- * Iterates through all schemas and find if there is 1 or more remote files in them
5794
- */
5795
- function hasRemoteFileSchema(schema) {
5796
- if (schema.type === "file" || schema.type === "image") {
5797
- return !!schema.remote;
5798
- } else if (schema.type === "richtext") {
5799
- var _schema$options;
5800
- if (typeof ((_schema$options = schema.options) === null || _schema$options === void 0 || (_schema$options = _schema$options.inline) === null || _schema$options === void 0 ? void 0 : _schema$options.img) === "object") {
5801
- return hasRemoteFileSchema(schema.options.inline.img);
5802
- }
5803
- return false;
5804
- } else if (schema.type === "array" || schema.type === "record") {
5805
- return hasRemoteFileSchema(schema.item);
5806
- } else if (schema.type === "object") {
5807
- for (const key in schema.items) {
5808
- const hasRemoteFile = hasRemoteFileSchema(schema.items[key]);
5809
- if (hasRemoteFile) {
5810
- return true;
5811
- }
5812
- }
5813
- return false;
5814
- } else if (schema.type === "union") {
5815
- const unionStringSchema = typeof schema.key === "object" && schema.key.type === "literal" ? schema : undefined;
5816
- const unionObjectSchema = typeof schema.key === "string" ? schema : undefined;
5817
- if (unionStringSchema) {
5818
- return false;
5819
- }
5820
- if (unionObjectSchema) {
5821
- for (const key in unionObjectSchema.items) {
5822
- const hasRemoteFile = hasRemoteFileSchema(unionObjectSchema.items[key]);
5823
- if (hasRemoteFile) {
5824
- return true;
5825
- }
5826
- }
5827
- }
5828
- return false;
5829
- } else if (schema.type === "boolean" || schema.type === "number" || schema.type === "string" || schema.type === "literal" || schema.type === "date" || schema.type === "dateTime" || schema.type === "color" || schema.type === "keyOf" || schema.type === "route") {
5830
- return false;
5831
- } else {
5832
- const exhaustiveCheck = schema;
5833
- throw new Error(`Unexpected schema: ${JSON.stringify(exhaustiveCheck)}`);
5834
- }
5835
- }
5836
-
5837
5822
  /* eslint-disable @typescript-eslint/no-unused-vars */
5838
5823
  const ValServer = (valModules, options, callbacks) => {
5839
5824
  const AIContentBlock = z.union([z.object({
@@ -10559,4 +10544,4 @@ function readCapturedReport(snapshotDir) {
10559
10544
  return JSON.parse(fs.readFileSync(reportPath, "utf-8"));
10560
10545
  }
10561
10546
 
10562
- export { DEFAULT_LOGIN_HOST, DEFAULT_LOGIN_MAX_DURATION, DEFAULT_LOGIN_POLL_INTERVAL, Service, ValFSHost, ValLoginError, ValModuleLoader, ValOpsFS, ValOpsHttp, ValSourceFileHandler, analyzeValModule, awaitValLoginConfirmation, checkRemoteRef, compareWithCapturedReport, createFixPatch, createJsonEntryPathMap, createModulePathMap, createService, createValApiRouter, createValServer, decodeJwt, downloadFileFromRemote, encodeJwt, evalValConfigFile, extractFileMetadata, extractImageMetadata, extractJsonValuesEntry, findAndEvalValConfigFile, findJsonEntryFilePath, formatPatchSourceError, formatSyntaxErrorTree, getCachedRemoteFileDir, getCachedRemoteFilePath, getCompilerOptions, getExpire, getFileExt, getModulePathRange, getPersonalAccessTokenPath, getSettings, getValidationErrorFileRef, hasRemoteFileSchema, loadValModules, parsePersonalAccessTokenFile, patchSourceFile, persistPersonalAccessToken, readCapturedReport, replaySnapshot, safeReadGit, startValLogin, uploadRemoteFile, validateMetadata };
10547
+ export { DEFAULT_LOGIN_HOST, DEFAULT_LOGIN_MAX_DURATION, DEFAULT_LOGIN_POLL_INTERVAL, Service, ValFSHost, ValLoginError, ValModuleLoader, ValOpsFS, ValOpsHttp, ValSourceFileHandler, analyzeValModule, awaitValLoginConfirmation, checkRemoteRef, compareWithCapturedReport, createFixPatch, createJsonEntryPathMap, createModulePathMap, createService, createValApiRouter, createValServer, decodeJwt, downloadFileFromRemote, encodeJwt, evalValConfigFile, extractFileMetadata, extractImageMetadata, extractJsonValuesEntry, findAndEvalValConfigFile, findJsonEntryFilePath, formatPatchSourceError, formatSyntaxErrorTree, getCachedRemoteFileDir, getCachedRemoteFilePath, getCompilerOptions, getExpire, getFileExt, getModulePathRange, getPersonalAccessTokenPath, getSettings, getValidationErrorFileRef, loadValModules, parsePersonalAccessTokenFile, patchSourceFile, persistPersonalAccessToken, readCapturedReport, 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.101.1",
19
+ "version": "0.102.0",
20
20
  "devDependencies": {
21
21
  "@prettier/sync": "^0.6.1",
22
22
  "@types/jest": "^30.0.0"
@@ -29,9 +29,9 @@
29
29
  "typescript": "^5.9.3",
30
30
  "zod": "^4.3.5",
31
31
  "zod-validation-error": "^5.0.0",
32
- "@valbuild/core": "0.101.0",
33
- "@valbuild/shared": "0.101.1",
34
- "@valbuild/ui": "0.101.1"
32
+ "@valbuild/core": "0.102.0",
33
+ "@valbuild/shared": "0.102.0",
34
+ "@valbuild/ui": "0.102.0"
35
35
  },
36
36
  "engines": {
37
37
  "node": ">=18.17.0"
@@ -1,5 +0,0 @@
1
- import { SerializedSchema } from "@valbuild/core";
2
- /**
3
- * Iterates through all schemas and find if there is 1 or more remote files in them
4
- */
5
- export declare function hasRemoteFileSchema(schema: SerializedSchema): boolean;