@valbuild/server 0.98.0 → 0.99.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/dist/valbuild-server.cjs.dev.js +127 -24
- package/dist/valbuild-server.cjs.prod.js +127 -24
- package/dist/valbuild-server.esm.js +127 -24
- package/package.json +4 -4
|
@@ -7778,6 +7778,33 @@ async function downloadFileFromRemote(ref, filePath) {
|
|
|
7778
7778
|
// record-level fix expands into per-entry errors that should point at the
|
|
7779
7779
|
// individual entry (e.g. `?p="/public/val/logo.png"`) rather than the record.
|
|
7780
7780
|
|
|
7781
|
+
// Gallery entries are keyed by their file path. Remote galleries key uploaded
|
|
7782
|
+
// entries by a remote URL while keeping the file on disk at its local path, so
|
|
7783
|
+
// normalize a remote-URL key back to that local path for on-disk reads.
|
|
7784
|
+
function galleryKeyToLocalPath(key) {
|
|
7785
|
+
const res = core.Internal.remote.splitRemoteRef(key);
|
|
7786
|
+
return res.status === "success" ? `/${res.filePath}` : key;
|
|
7787
|
+
}
|
|
7788
|
+
|
|
7789
|
+
// Patch path of the record that holds a media entry. Media keys are file paths
|
|
7790
|
+
// that can contain dots, which sourceToPatchPath cannot round-trip, so strip
|
|
7791
|
+
// the (JSON-encoded) key segment off the entry source path and convert the
|
|
7792
|
+
// remaining parent path — whose ancestors are plain object keys / array
|
|
7793
|
+
// indices — instead.
|
|
7794
|
+
function parentPatchPathOfMediaEntry(sourcePath, entryKey) {
|
|
7795
|
+
const jsonKey = JSON.stringify(entryKey);
|
|
7796
|
+
let parent = sourcePath;
|
|
7797
|
+
if (parent.endsWith(jsonKey)) {
|
|
7798
|
+
parent = parent.slice(0, parent.length - jsonKey.length);
|
|
7799
|
+
}
|
|
7800
|
+
if (parent.endsWith(".")) {
|
|
7801
|
+
parent = parent.slice(0, -1);
|
|
7802
|
+
} else if (parent.endsWith(core.Internal.ModuleFilePathSep)) {
|
|
7803
|
+
parent = parent.slice(0, parent.length - core.Internal.ModuleFilePathSep.length);
|
|
7804
|
+
}
|
|
7805
|
+
return patch.sourceToPatchPath(parent);
|
|
7806
|
+
}
|
|
7807
|
+
|
|
7781
7808
|
// TODO: find a better name? transformFixesToPatch?
|
|
7782
7809
|
async function createFixPatch(config, apply, sourcePath, validationError, remoteFiles, moduleSource, moduleSchema) {
|
|
7783
7810
|
const remainingErrors = [];
|
|
@@ -7955,6 +7982,61 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
|
7955
7982
|
path: patch.sourceToPatchPath(sourcePath)
|
|
7956
7983
|
});
|
|
7957
7984
|
}
|
|
7985
|
+
} else if (fix === "images:upload-remote" || fix === "files:upload-remote") {
|
|
7986
|
+
// Gallery entry: the record is keyed by the file path, so uploading to
|
|
7987
|
+
// remote means renaming the key from the local path to the remote URL
|
|
7988
|
+
// (remove the old key, add the new one with the same metadata).
|
|
7989
|
+
const remoteFile = remoteFiles[sourcePath];
|
|
7990
|
+
if (!remoteFile) {
|
|
7991
|
+
remainingErrors.push({
|
|
7992
|
+
...validationError,
|
|
7993
|
+
message: "Cannot fix local to remote gallery entry: remote file was not uploaded",
|
|
7994
|
+
fixes: undefined
|
|
7995
|
+
});
|
|
7996
|
+
continue;
|
|
7997
|
+
}
|
|
7998
|
+
const metadata = remoteFile.metadata;
|
|
7999
|
+
if (!metadata) {
|
|
8000
|
+
remainingErrors.push({
|
|
8001
|
+
...validationError,
|
|
8002
|
+
message: "Failed to get metadata for remote gallery file",
|
|
8003
|
+
fixes: undefined
|
|
8004
|
+
});
|
|
8005
|
+
continue;
|
|
8006
|
+
}
|
|
8007
|
+
// The entry is keyed by its (local) file path, which can contain dots
|
|
8008
|
+
// (e.g. image.png). sourceToPatchPath can't round-trip such keys, so
|
|
8009
|
+
// build the patch path from the parent record path plus the key (which
|
|
8010
|
+
// validateMediaKey carries as the error value), like the UI does.
|
|
8011
|
+
const entryKey = validationError.value;
|
|
8012
|
+
if (typeof entryKey !== "string") {
|
|
8013
|
+
remainingErrors.push({
|
|
8014
|
+
...validationError,
|
|
8015
|
+
message: "Cannot rewrite remote gallery entry: missing entry key",
|
|
8016
|
+
fixes: undefined
|
|
8017
|
+
});
|
|
8018
|
+
continue;
|
|
8019
|
+
}
|
|
8020
|
+
const parentPath = parentPatchPathOfMediaEntry(sourcePath, entryKey);
|
|
8021
|
+
const removePath = parentPath.concat(entryKey);
|
|
8022
|
+
const addPath = parentPath.concat(remoteFile.ref);
|
|
8023
|
+
if (!patch.isNotRoot(removePath) || !patch.isNotRoot(addPath)) {
|
|
8024
|
+
remainingErrors.push({
|
|
8025
|
+
...validationError,
|
|
8026
|
+
message: "Cannot rewrite remote gallery entry at root path",
|
|
8027
|
+
fixes: undefined
|
|
8028
|
+
});
|
|
8029
|
+
continue;
|
|
8030
|
+
}
|
|
8031
|
+
patch$1.push({
|
|
8032
|
+
op: "remove",
|
|
8033
|
+
path: removePath
|
|
8034
|
+
});
|
|
8035
|
+
patch$1.push({
|
|
8036
|
+
op: "add",
|
|
8037
|
+
path: addPath,
|
|
8038
|
+
value: metadata
|
|
8039
|
+
});
|
|
7958
8040
|
} else if (fix === "image:download-remote" || fix === "file:download-remote") {
|
|
7959
8041
|
const v = getRemoteValueFromValidationError(validationError);
|
|
7960
8042
|
if (!v.success) {
|
|
@@ -8031,7 +8113,7 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
|
8031
8113
|
}
|
|
8032
8114
|
const gallerySource = moduleSource;
|
|
8033
8115
|
for (const [entryKey, storedEntry] of Object.entries(gallerySource)) {
|
|
8034
|
-
const filename = path__namespace["default"].join(config.projectRoot, entryKey);
|
|
8116
|
+
const filename = path__namespace["default"].join(config.projectRoot, galleryKeyToLocalPath(entryKey));
|
|
8035
8117
|
let buffer;
|
|
8036
8118
|
try {
|
|
8037
8119
|
buffer = fs__default["default"].readFileSync(filename);
|
|
@@ -8556,31 +8638,39 @@ function getModulePathRange(modulePath, modulePathMap,
|
|
|
8556
8638
|
// existing callers.
|
|
8557
8639
|
target = "key") {
|
|
8558
8640
|
var _range$children;
|
|
8559
|
-
// Handle
|
|
8560
|
-
if (
|
|
8561
|
-
return undefined;
|
|
8562
|
-
}
|
|
8563
|
-
let segments;
|
|
8564
|
-
try {
|
|
8565
|
-
// Quote-aware splitter that correctly handles keys containing dots
|
|
8566
|
-
// (e.g. file refs like `"/public/val/images/logo.png"`), unlike a naive
|
|
8567
|
-
// split on ".". Throws on malformed input (e.g. unbalanced quotes).
|
|
8568
|
-
segments = core.Internal.splitModulePath(modulePath);
|
|
8569
|
-
} catch {
|
|
8570
|
-
// Return undefined if the module path is malformed. This can happen when
|
|
8571
|
-
// there are upstream errors in schema serialization.
|
|
8572
|
-
return undefined;
|
|
8573
|
-
}
|
|
8574
|
-
if (segments.length === 0) {
|
|
8641
|
+
// Handle invalid module paths gracefully
|
|
8642
|
+
if (typeof modulePath !== "string") {
|
|
8575
8643
|
return undefined;
|
|
8576
8644
|
}
|
|
8577
|
-
let range
|
|
8578
|
-
|
|
8579
|
-
|
|
8580
|
-
|
|
8581
|
-
|
|
8645
|
+
let range;
|
|
8646
|
+
if (modulePath === "") {
|
|
8647
|
+
// The empty module path is the module root, stored under the "" key: the
|
|
8648
|
+
// source argument of `c.define`. This is what module-level diagnostics
|
|
8649
|
+
// (which have no module path) resolve to.
|
|
8650
|
+
range = modulePathMap[""];
|
|
8651
|
+
} else {
|
|
8652
|
+
let segments;
|
|
8653
|
+
try {
|
|
8654
|
+
// Quote-aware splitter that correctly handles keys containing dots
|
|
8655
|
+
// (e.g. file refs like `"/public/val/images/logo.png"`), unlike a naive
|
|
8656
|
+
// split on ".". Throws on malformed input (e.g. unbalanced quotes).
|
|
8657
|
+
segments = core.Internal.splitModulePath(modulePath);
|
|
8658
|
+
} catch {
|
|
8659
|
+
// Return undefined if the module path is malformed. This can happen when
|
|
8660
|
+
// there are upstream errors in schema serialization.
|
|
8661
|
+
return undefined;
|
|
8662
|
+
}
|
|
8663
|
+
if (segments.length === 0) {
|
|
8664
|
+
return undefined;
|
|
8665
|
+
}
|
|
8666
|
+
range = modulePathMap[segments[0]];
|
|
8667
|
+
for (const pathSegment of segments.slice(1)) {
|
|
8668
|
+
var _range;
|
|
8669
|
+
if (!range) {
|
|
8670
|
+
break;
|
|
8671
|
+
}
|
|
8672
|
+
range = (_range = range) === null || _range === void 0 || (_range = _range.children) === null || _range === void 0 ? void 0 : _range[pathSegment];
|
|
8582
8673
|
}
|
|
8583
|
-
range = (_range = range) === null || _range === void 0 || (_range = _range.children) === null || _range === void 0 ? void 0 : _range[pathSegment];
|
|
8584
8674
|
}
|
|
8585
8675
|
if (!range) {
|
|
8586
8676
|
return undefined;
|
|
@@ -8612,7 +8702,20 @@ function createModulePathMap(sourceFile) {
|
|
|
8612
8702
|
if (ts__default["default"].isExportAssignment(child)) {
|
|
8613
8703
|
const contentNode = child.expression && ts__default["default"].isCallExpression(child.expression) && child.expression.arguments[2];
|
|
8614
8704
|
if (contentNode) {
|
|
8615
|
-
|
|
8705
|
+
const map = traverse(contentNode, sourceFile) ?? {};
|
|
8706
|
+
// Always expose the module root under the "" key so diagnostics without
|
|
8707
|
+
// a module path still resolve to a location. `traverse` already emits ""
|
|
8708
|
+
// for primitive roots (with a tighter range), so keep that if present.
|
|
8709
|
+
// Object/record keys are never "" (see traverseObjectLiteral), so this
|
|
8710
|
+
// cannot collide with a real key.
|
|
8711
|
+
return {
|
|
8712
|
+
...map,
|
|
8713
|
+
"": map[""] ?? {
|
|
8714
|
+
children: {},
|
|
8715
|
+
start: sourceFile.getLineAndCharacterOfPosition(contentNode.getStart(sourceFile)),
|
|
8716
|
+
end: sourceFile.getLineAndCharacterOfPosition(contentNode.getEnd())
|
|
8717
|
+
}
|
|
8718
|
+
};
|
|
8616
8719
|
}
|
|
8617
8720
|
}
|
|
8618
8721
|
}
|
|
@@ -7778,6 +7778,33 @@ async function downloadFileFromRemote(ref, filePath) {
|
|
|
7778
7778
|
// record-level fix expands into per-entry errors that should point at the
|
|
7779
7779
|
// individual entry (e.g. `?p="/public/val/logo.png"`) rather than the record.
|
|
7780
7780
|
|
|
7781
|
+
// Gallery entries are keyed by their file path. Remote galleries key uploaded
|
|
7782
|
+
// entries by a remote URL while keeping the file on disk at its local path, so
|
|
7783
|
+
// normalize a remote-URL key back to that local path for on-disk reads.
|
|
7784
|
+
function galleryKeyToLocalPath(key) {
|
|
7785
|
+
const res = core.Internal.remote.splitRemoteRef(key);
|
|
7786
|
+
return res.status === "success" ? `/${res.filePath}` : key;
|
|
7787
|
+
}
|
|
7788
|
+
|
|
7789
|
+
// Patch path of the record that holds a media entry. Media keys are file paths
|
|
7790
|
+
// that can contain dots, which sourceToPatchPath cannot round-trip, so strip
|
|
7791
|
+
// the (JSON-encoded) key segment off the entry source path and convert the
|
|
7792
|
+
// remaining parent path — whose ancestors are plain object keys / array
|
|
7793
|
+
// indices — instead.
|
|
7794
|
+
function parentPatchPathOfMediaEntry(sourcePath, entryKey) {
|
|
7795
|
+
const jsonKey = JSON.stringify(entryKey);
|
|
7796
|
+
let parent = sourcePath;
|
|
7797
|
+
if (parent.endsWith(jsonKey)) {
|
|
7798
|
+
parent = parent.slice(0, parent.length - jsonKey.length);
|
|
7799
|
+
}
|
|
7800
|
+
if (parent.endsWith(".")) {
|
|
7801
|
+
parent = parent.slice(0, -1);
|
|
7802
|
+
} else if (parent.endsWith(core.Internal.ModuleFilePathSep)) {
|
|
7803
|
+
parent = parent.slice(0, parent.length - core.Internal.ModuleFilePathSep.length);
|
|
7804
|
+
}
|
|
7805
|
+
return patch.sourceToPatchPath(parent);
|
|
7806
|
+
}
|
|
7807
|
+
|
|
7781
7808
|
// TODO: find a better name? transformFixesToPatch?
|
|
7782
7809
|
async function createFixPatch(config, apply, sourcePath, validationError, remoteFiles, moduleSource, moduleSchema) {
|
|
7783
7810
|
const remainingErrors = [];
|
|
@@ -7955,6 +7982,61 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
|
7955
7982
|
path: patch.sourceToPatchPath(sourcePath)
|
|
7956
7983
|
});
|
|
7957
7984
|
}
|
|
7985
|
+
} else if (fix === "images:upload-remote" || fix === "files:upload-remote") {
|
|
7986
|
+
// Gallery entry: the record is keyed by the file path, so uploading to
|
|
7987
|
+
// remote means renaming the key from the local path to the remote URL
|
|
7988
|
+
// (remove the old key, add the new one with the same metadata).
|
|
7989
|
+
const remoteFile = remoteFiles[sourcePath];
|
|
7990
|
+
if (!remoteFile) {
|
|
7991
|
+
remainingErrors.push({
|
|
7992
|
+
...validationError,
|
|
7993
|
+
message: "Cannot fix local to remote gallery entry: remote file was not uploaded",
|
|
7994
|
+
fixes: undefined
|
|
7995
|
+
});
|
|
7996
|
+
continue;
|
|
7997
|
+
}
|
|
7998
|
+
const metadata = remoteFile.metadata;
|
|
7999
|
+
if (!metadata) {
|
|
8000
|
+
remainingErrors.push({
|
|
8001
|
+
...validationError,
|
|
8002
|
+
message: "Failed to get metadata for remote gallery file",
|
|
8003
|
+
fixes: undefined
|
|
8004
|
+
});
|
|
8005
|
+
continue;
|
|
8006
|
+
}
|
|
8007
|
+
// The entry is keyed by its (local) file path, which can contain dots
|
|
8008
|
+
// (e.g. image.png). sourceToPatchPath can't round-trip such keys, so
|
|
8009
|
+
// build the patch path from the parent record path plus the key (which
|
|
8010
|
+
// validateMediaKey carries as the error value), like the UI does.
|
|
8011
|
+
const entryKey = validationError.value;
|
|
8012
|
+
if (typeof entryKey !== "string") {
|
|
8013
|
+
remainingErrors.push({
|
|
8014
|
+
...validationError,
|
|
8015
|
+
message: "Cannot rewrite remote gallery entry: missing entry key",
|
|
8016
|
+
fixes: undefined
|
|
8017
|
+
});
|
|
8018
|
+
continue;
|
|
8019
|
+
}
|
|
8020
|
+
const parentPath = parentPatchPathOfMediaEntry(sourcePath, entryKey);
|
|
8021
|
+
const removePath = parentPath.concat(entryKey);
|
|
8022
|
+
const addPath = parentPath.concat(remoteFile.ref);
|
|
8023
|
+
if (!patch.isNotRoot(removePath) || !patch.isNotRoot(addPath)) {
|
|
8024
|
+
remainingErrors.push({
|
|
8025
|
+
...validationError,
|
|
8026
|
+
message: "Cannot rewrite remote gallery entry at root path",
|
|
8027
|
+
fixes: undefined
|
|
8028
|
+
});
|
|
8029
|
+
continue;
|
|
8030
|
+
}
|
|
8031
|
+
patch$1.push({
|
|
8032
|
+
op: "remove",
|
|
8033
|
+
path: removePath
|
|
8034
|
+
});
|
|
8035
|
+
patch$1.push({
|
|
8036
|
+
op: "add",
|
|
8037
|
+
path: addPath,
|
|
8038
|
+
value: metadata
|
|
8039
|
+
});
|
|
7958
8040
|
} else if (fix === "image:download-remote" || fix === "file:download-remote") {
|
|
7959
8041
|
const v = getRemoteValueFromValidationError(validationError);
|
|
7960
8042
|
if (!v.success) {
|
|
@@ -8031,7 +8113,7 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
|
8031
8113
|
}
|
|
8032
8114
|
const gallerySource = moduleSource;
|
|
8033
8115
|
for (const [entryKey, storedEntry] of Object.entries(gallerySource)) {
|
|
8034
|
-
const filename = path__namespace["default"].join(config.projectRoot, entryKey);
|
|
8116
|
+
const filename = path__namespace["default"].join(config.projectRoot, galleryKeyToLocalPath(entryKey));
|
|
8035
8117
|
let buffer;
|
|
8036
8118
|
try {
|
|
8037
8119
|
buffer = fs__default["default"].readFileSync(filename);
|
|
@@ -8556,31 +8638,39 @@ function getModulePathRange(modulePath, modulePathMap,
|
|
|
8556
8638
|
// existing callers.
|
|
8557
8639
|
target = "key") {
|
|
8558
8640
|
var _range$children;
|
|
8559
|
-
// Handle
|
|
8560
|
-
if (
|
|
8561
|
-
return undefined;
|
|
8562
|
-
}
|
|
8563
|
-
let segments;
|
|
8564
|
-
try {
|
|
8565
|
-
// Quote-aware splitter that correctly handles keys containing dots
|
|
8566
|
-
// (e.g. file refs like `"/public/val/images/logo.png"`), unlike a naive
|
|
8567
|
-
// split on ".". Throws on malformed input (e.g. unbalanced quotes).
|
|
8568
|
-
segments = core.Internal.splitModulePath(modulePath);
|
|
8569
|
-
} catch {
|
|
8570
|
-
// Return undefined if the module path is malformed. This can happen when
|
|
8571
|
-
// there are upstream errors in schema serialization.
|
|
8572
|
-
return undefined;
|
|
8573
|
-
}
|
|
8574
|
-
if (segments.length === 0) {
|
|
8641
|
+
// Handle invalid module paths gracefully
|
|
8642
|
+
if (typeof modulePath !== "string") {
|
|
8575
8643
|
return undefined;
|
|
8576
8644
|
}
|
|
8577
|
-
let range
|
|
8578
|
-
|
|
8579
|
-
|
|
8580
|
-
|
|
8581
|
-
|
|
8645
|
+
let range;
|
|
8646
|
+
if (modulePath === "") {
|
|
8647
|
+
// The empty module path is the module root, stored under the "" key: the
|
|
8648
|
+
// source argument of `c.define`. This is what module-level diagnostics
|
|
8649
|
+
// (which have no module path) resolve to.
|
|
8650
|
+
range = modulePathMap[""];
|
|
8651
|
+
} else {
|
|
8652
|
+
let segments;
|
|
8653
|
+
try {
|
|
8654
|
+
// Quote-aware splitter that correctly handles keys containing dots
|
|
8655
|
+
// (e.g. file refs like `"/public/val/images/logo.png"`), unlike a naive
|
|
8656
|
+
// split on ".". Throws on malformed input (e.g. unbalanced quotes).
|
|
8657
|
+
segments = core.Internal.splitModulePath(modulePath);
|
|
8658
|
+
} catch {
|
|
8659
|
+
// Return undefined if the module path is malformed. This can happen when
|
|
8660
|
+
// there are upstream errors in schema serialization.
|
|
8661
|
+
return undefined;
|
|
8662
|
+
}
|
|
8663
|
+
if (segments.length === 0) {
|
|
8664
|
+
return undefined;
|
|
8665
|
+
}
|
|
8666
|
+
range = modulePathMap[segments[0]];
|
|
8667
|
+
for (const pathSegment of segments.slice(1)) {
|
|
8668
|
+
var _range;
|
|
8669
|
+
if (!range) {
|
|
8670
|
+
break;
|
|
8671
|
+
}
|
|
8672
|
+
range = (_range = range) === null || _range === void 0 || (_range = _range.children) === null || _range === void 0 ? void 0 : _range[pathSegment];
|
|
8582
8673
|
}
|
|
8583
|
-
range = (_range = range) === null || _range === void 0 || (_range = _range.children) === null || _range === void 0 ? void 0 : _range[pathSegment];
|
|
8584
8674
|
}
|
|
8585
8675
|
if (!range) {
|
|
8586
8676
|
return undefined;
|
|
@@ -8612,7 +8702,20 @@ function createModulePathMap(sourceFile) {
|
|
|
8612
8702
|
if (ts__default["default"].isExportAssignment(child)) {
|
|
8613
8703
|
const contentNode = child.expression && ts__default["default"].isCallExpression(child.expression) && child.expression.arguments[2];
|
|
8614
8704
|
if (contentNode) {
|
|
8615
|
-
|
|
8705
|
+
const map = traverse(contentNode, sourceFile) ?? {};
|
|
8706
|
+
// Always expose the module root under the "" key so diagnostics without
|
|
8707
|
+
// a module path still resolve to a location. `traverse` already emits ""
|
|
8708
|
+
// for primitive roots (with a tighter range), so keep that if present.
|
|
8709
|
+
// Object/record keys are never "" (see traverseObjectLiteral), so this
|
|
8710
|
+
// cannot collide with a real key.
|
|
8711
|
+
return {
|
|
8712
|
+
...map,
|
|
8713
|
+
"": map[""] ?? {
|
|
8714
|
+
children: {},
|
|
8715
|
+
start: sourceFile.getLineAndCharacterOfPosition(contentNode.getStart(sourceFile)),
|
|
8716
|
+
end: sourceFile.getLineAndCharacterOfPosition(contentNode.getEnd())
|
|
8717
|
+
}
|
|
8718
|
+
};
|
|
8616
8719
|
}
|
|
8617
8720
|
}
|
|
8618
8721
|
}
|
|
@@ -7744,6 +7744,33 @@ async function downloadFileFromRemote(ref, filePath) {
|
|
|
7744
7744
|
// record-level fix expands into per-entry errors that should point at the
|
|
7745
7745
|
// individual entry (e.g. `?p="/public/val/logo.png"`) rather than the record.
|
|
7746
7746
|
|
|
7747
|
+
// Gallery entries are keyed by their file path. Remote galleries key uploaded
|
|
7748
|
+
// entries by a remote URL while keeping the file on disk at its local path, so
|
|
7749
|
+
// normalize a remote-URL key back to that local path for on-disk reads.
|
|
7750
|
+
function galleryKeyToLocalPath(key) {
|
|
7751
|
+
const res = Internal.remote.splitRemoteRef(key);
|
|
7752
|
+
return res.status === "success" ? `/${res.filePath}` : key;
|
|
7753
|
+
}
|
|
7754
|
+
|
|
7755
|
+
// Patch path of the record that holds a media entry. Media keys are file paths
|
|
7756
|
+
// that can contain dots, which sourceToPatchPath cannot round-trip, so strip
|
|
7757
|
+
// the (JSON-encoded) key segment off the entry source path and convert the
|
|
7758
|
+
// remaining parent path — whose ancestors are plain object keys / array
|
|
7759
|
+
// indices — instead.
|
|
7760
|
+
function parentPatchPathOfMediaEntry(sourcePath, entryKey) {
|
|
7761
|
+
const jsonKey = JSON.stringify(entryKey);
|
|
7762
|
+
let parent = sourcePath;
|
|
7763
|
+
if (parent.endsWith(jsonKey)) {
|
|
7764
|
+
parent = parent.slice(0, parent.length - jsonKey.length);
|
|
7765
|
+
}
|
|
7766
|
+
if (parent.endsWith(".")) {
|
|
7767
|
+
parent = parent.slice(0, -1);
|
|
7768
|
+
} else if (parent.endsWith(Internal.ModuleFilePathSep)) {
|
|
7769
|
+
parent = parent.slice(0, parent.length - Internal.ModuleFilePathSep.length);
|
|
7770
|
+
}
|
|
7771
|
+
return sourceToPatchPath(parent);
|
|
7772
|
+
}
|
|
7773
|
+
|
|
7747
7774
|
// TODO: find a better name? transformFixesToPatch?
|
|
7748
7775
|
async function createFixPatch(config, apply, sourcePath, validationError, remoteFiles, moduleSource, moduleSchema) {
|
|
7749
7776
|
const remainingErrors = [];
|
|
@@ -7921,6 +7948,61 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
|
7921
7948
|
path: sourceToPatchPath(sourcePath)
|
|
7922
7949
|
});
|
|
7923
7950
|
}
|
|
7951
|
+
} else if (fix === "images:upload-remote" || fix === "files:upload-remote") {
|
|
7952
|
+
// Gallery entry: the record is keyed by the file path, so uploading to
|
|
7953
|
+
// remote means renaming the key from the local path to the remote URL
|
|
7954
|
+
// (remove the old key, add the new one with the same metadata).
|
|
7955
|
+
const remoteFile = remoteFiles[sourcePath];
|
|
7956
|
+
if (!remoteFile) {
|
|
7957
|
+
remainingErrors.push({
|
|
7958
|
+
...validationError,
|
|
7959
|
+
message: "Cannot fix local to remote gallery entry: remote file was not uploaded",
|
|
7960
|
+
fixes: undefined
|
|
7961
|
+
});
|
|
7962
|
+
continue;
|
|
7963
|
+
}
|
|
7964
|
+
const metadata = remoteFile.metadata;
|
|
7965
|
+
if (!metadata) {
|
|
7966
|
+
remainingErrors.push({
|
|
7967
|
+
...validationError,
|
|
7968
|
+
message: "Failed to get metadata for remote gallery file",
|
|
7969
|
+
fixes: undefined
|
|
7970
|
+
});
|
|
7971
|
+
continue;
|
|
7972
|
+
}
|
|
7973
|
+
// The entry is keyed by its (local) file path, which can contain dots
|
|
7974
|
+
// (e.g. image.png). sourceToPatchPath can't round-trip such keys, so
|
|
7975
|
+
// build the patch path from the parent record path plus the key (which
|
|
7976
|
+
// validateMediaKey carries as the error value), like the UI does.
|
|
7977
|
+
const entryKey = validationError.value;
|
|
7978
|
+
if (typeof entryKey !== "string") {
|
|
7979
|
+
remainingErrors.push({
|
|
7980
|
+
...validationError,
|
|
7981
|
+
message: "Cannot rewrite remote gallery entry: missing entry key",
|
|
7982
|
+
fixes: undefined
|
|
7983
|
+
});
|
|
7984
|
+
continue;
|
|
7985
|
+
}
|
|
7986
|
+
const parentPath = parentPatchPathOfMediaEntry(sourcePath, entryKey);
|
|
7987
|
+
const removePath = parentPath.concat(entryKey);
|
|
7988
|
+
const addPath = parentPath.concat(remoteFile.ref);
|
|
7989
|
+
if (!isNotRoot(removePath) || !isNotRoot(addPath)) {
|
|
7990
|
+
remainingErrors.push({
|
|
7991
|
+
...validationError,
|
|
7992
|
+
message: "Cannot rewrite remote gallery entry at root path",
|
|
7993
|
+
fixes: undefined
|
|
7994
|
+
});
|
|
7995
|
+
continue;
|
|
7996
|
+
}
|
|
7997
|
+
patch.push({
|
|
7998
|
+
op: "remove",
|
|
7999
|
+
path: removePath
|
|
8000
|
+
});
|
|
8001
|
+
patch.push({
|
|
8002
|
+
op: "add",
|
|
8003
|
+
path: addPath,
|
|
8004
|
+
value: metadata
|
|
8005
|
+
});
|
|
7924
8006
|
} else if (fix === "image:download-remote" || fix === "file:download-remote") {
|
|
7925
8007
|
const v = getRemoteValueFromValidationError(validationError);
|
|
7926
8008
|
if (!v.success) {
|
|
@@ -7997,7 +8079,7 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
|
7997
8079
|
}
|
|
7998
8080
|
const gallerySource = moduleSource;
|
|
7999
8081
|
for (const [entryKey, storedEntry] of Object.entries(gallerySource)) {
|
|
8000
|
-
const filename = path__default.join(config.projectRoot, entryKey);
|
|
8082
|
+
const filename = path__default.join(config.projectRoot, galleryKeyToLocalPath(entryKey));
|
|
8001
8083
|
let buffer;
|
|
8002
8084
|
try {
|
|
8003
8085
|
buffer = fs.readFileSync(filename);
|
|
@@ -8522,31 +8604,39 @@ function getModulePathRange(modulePath, modulePathMap,
|
|
|
8522
8604
|
// existing callers.
|
|
8523
8605
|
target = "key") {
|
|
8524
8606
|
var _range$children;
|
|
8525
|
-
// Handle
|
|
8526
|
-
if (
|
|
8527
|
-
return undefined;
|
|
8528
|
-
}
|
|
8529
|
-
let segments;
|
|
8530
|
-
try {
|
|
8531
|
-
// Quote-aware splitter that correctly handles keys containing dots
|
|
8532
|
-
// (e.g. file refs like `"/public/val/images/logo.png"`), unlike a naive
|
|
8533
|
-
// split on ".". Throws on malformed input (e.g. unbalanced quotes).
|
|
8534
|
-
segments = Internal.splitModulePath(modulePath);
|
|
8535
|
-
} catch {
|
|
8536
|
-
// Return undefined if the module path is malformed. This can happen when
|
|
8537
|
-
// there are upstream errors in schema serialization.
|
|
8538
|
-
return undefined;
|
|
8539
|
-
}
|
|
8540
|
-
if (segments.length === 0) {
|
|
8607
|
+
// Handle invalid module paths gracefully
|
|
8608
|
+
if (typeof modulePath !== "string") {
|
|
8541
8609
|
return undefined;
|
|
8542
8610
|
}
|
|
8543
|
-
let range
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8611
|
+
let range;
|
|
8612
|
+
if (modulePath === "") {
|
|
8613
|
+
// The empty module path is the module root, stored under the "" key: the
|
|
8614
|
+
// source argument of `c.define`. This is what module-level diagnostics
|
|
8615
|
+
// (which have no module path) resolve to.
|
|
8616
|
+
range = modulePathMap[""];
|
|
8617
|
+
} else {
|
|
8618
|
+
let segments;
|
|
8619
|
+
try {
|
|
8620
|
+
// Quote-aware splitter that correctly handles keys containing dots
|
|
8621
|
+
// (e.g. file refs like `"/public/val/images/logo.png"`), unlike a naive
|
|
8622
|
+
// split on ".". Throws on malformed input (e.g. unbalanced quotes).
|
|
8623
|
+
segments = Internal.splitModulePath(modulePath);
|
|
8624
|
+
} catch {
|
|
8625
|
+
// Return undefined if the module path is malformed. This can happen when
|
|
8626
|
+
// there are upstream errors in schema serialization.
|
|
8627
|
+
return undefined;
|
|
8628
|
+
}
|
|
8629
|
+
if (segments.length === 0) {
|
|
8630
|
+
return undefined;
|
|
8631
|
+
}
|
|
8632
|
+
range = modulePathMap[segments[0]];
|
|
8633
|
+
for (const pathSegment of segments.slice(1)) {
|
|
8634
|
+
var _range;
|
|
8635
|
+
if (!range) {
|
|
8636
|
+
break;
|
|
8637
|
+
}
|
|
8638
|
+
range = (_range = range) === null || _range === void 0 || (_range = _range.children) === null || _range === void 0 ? void 0 : _range[pathSegment];
|
|
8548
8639
|
}
|
|
8549
|
-
range = (_range = range) === null || _range === void 0 || (_range = _range.children) === null || _range === void 0 ? void 0 : _range[pathSegment];
|
|
8550
8640
|
}
|
|
8551
8641
|
if (!range) {
|
|
8552
8642
|
return undefined;
|
|
@@ -8578,7 +8668,20 @@ function createModulePathMap(sourceFile) {
|
|
|
8578
8668
|
if (ts.isExportAssignment(child)) {
|
|
8579
8669
|
const contentNode = child.expression && ts.isCallExpression(child.expression) && child.expression.arguments[2];
|
|
8580
8670
|
if (contentNode) {
|
|
8581
|
-
|
|
8671
|
+
const map = traverse(contentNode, sourceFile) ?? {};
|
|
8672
|
+
// Always expose the module root under the "" key so diagnostics without
|
|
8673
|
+
// a module path still resolve to a location. `traverse` already emits ""
|
|
8674
|
+
// for primitive roots (with a tighter range), so keep that if present.
|
|
8675
|
+
// Object/record keys are never "" (see traverseObjectLiteral), so this
|
|
8676
|
+
// cannot collide with a real key.
|
|
8677
|
+
return {
|
|
8678
|
+
...map,
|
|
8679
|
+
"": map[""] ?? {
|
|
8680
|
+
children: {},
|
|
8681
|
+
start: sourceFile.getLineAndCharacterOfPosition(contentNode.getStart(sourceFile)),
|
|
8682
|
+
end: sourceFile.getLineAndCharacterOfPosition(contentNode.getEnd())
|
|
8683
|
+
}
|
|
8684
|
+
};
|
|
8582
8685
|
}
|
|
8583
8686
|
}
|
|
8584
8687
|
}
|
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.
|
|
19
|
+
"version": "0.99.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.
|
|
33
|
-
"@valbuild/shared": "0.
|
|
34
|
-
"@valbuild/ui": "0.
|
|
32
|
+
"@valbuild/core": "0.99.0",
|
|
33
|
+
"@valbuild/shared": "0.99.0",
|
|
34
|
+
"@valbuild/ui": "0.99.0"
|
|
35
35
|
},
|
|
36
36
|
"engines": {
|
|
37
37
|
"node": ">=18.17.0"
|