@valbuild/server 0.93.0 → 0.95.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/declarations/src/ValFS.d.ts +2 -1
- package/dist/declarations/src/ValFSHost.d.ts +4 -1
- package/dist/declarations/src/ValServer.d.ts +1 -1
- package/dist/valbuild-server.cjs.dev.js +701 -83
- package/dist/valbuild-server.cjs.prod.js +701 -83
- package/dist/valbuild-server.esm.js +701 -83
- package/package.json +4 -4
|
@@ -659,7 +659,7 @@ function removeFromNode(document, node, key) {
|
|
|
659
659
|
} else if (ts__default["default"].isObjectLiteralExpression(node)) {
|
|
660
660
|
return fp.pipe(findObjectPropertyAssignment(node, key), fp.result.flatMap(assignment => {
|
|
661
661
|
if (!assignment) {
|
|
662
|
-
return fp.result.err(new patch.PatchError("Cannot
|
|
662
|
+
return fp.result.err(new patch.PatchError("Cannot remove object element which does not exist"));
|
|
663
663
|
}
|
|
664
664
|
return fp.result.ok(assignment);
|
|
665
665
|
}), fp.result.map(assignment => [removeAt(document, node.properties, node.properties.indexOf(assignment))[0], assignment.initializer]));
|
|
@@ -1026,7 +1026,14 @@ class ValSourceFileHandler {
|
|
|
1026
1026
|
});
|
|
1027
1027
|
fs__default["default"].writeFileSync(fileName, typeof data === "string" ? data : new Uint8Array(data), encoding);
|
|
1028
1028
|
},
|
|
1029
|
-
rmFile: fs__default["default"].rmSync
|
|
1029
|
+
rmFile: fs__default["default"].rmSync,
|
|
1030
|
+
readBuffer: fileName => {
|
|
1031
|
+
try {
|
|
1032
|
+
return fs__default["default"].readFileSync(fileName);
|
|
1033
|
+
} catch {
|
|
1034
|
+
return undefined;
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1030
1037
|
}) {
|
|
1031
1038
|
this.projectRoot = projectRoot;
|
|
1032
1039
|
this.compilerOptions = compilerOptions;
|
|
@@ -1074,7 +1081,14 @@ class ValModuleLoader {
|
|
|
1074
1081
|
});
|
|
1075
1082
|
fs__default["default"].writeFileSync(fileName, typeof data === "string" ? data : new Uint8Array(data), encoding);
|
|
1076
1083
|
},
|
|
1077
|
-
rmFile: fs__default["default"].rmSync
|
|
1084
|
+
rmFile: fs__default["default"].rmSync,
|
|
1085
|
+
readBuffer: fileName => {
|
|
1086
|
+
try {
|
|
1087
|
+
return fs__default["default"].readFileSync(fileName);
|
|
1088
|
+
} catch {
|
|
1089
|
+
return undefined;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1078
1092
|
}, disableCache = false) {
|
|
1079
1093
|
this.projectRoot = projectRoot;
|
|
1080
1094
|
this.compilerOptions = compilerOptions;
|
|
@@ -1367,7 +1381,14 @@ async function createService(projectRoot, opts, host = {
|
|
|
1367
1381
|
});
|
|
1368
1382
|
fs__default["default"].writeFileSync(fileName, typeof data === "string" ? data : new Uint8Array(data), encoding);
|
|
1369
1383
|
},
|
|
1370
|
-
rmFile: fs__default["default"].rmSync
|
|
1384
|
+
rmFile: fs__default["default"].rmSync,
|
|
1385
|
+
readBuffer: fileName => {
|
|
1386
|
+
try {
|
|
1387
|
+
return fs__default["default"].readFileSync(fileName);
|
|
1388
|
+
} catch {
|
|
1389
|
+
return undefined;
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1371
1392
|
}, loader) {
|
|
1372
1393
|
const compilerOptions = getCompilerOptions(projectRoot, host);
|
|
1373
1394
|
const sourceFileHandler = new ValSourceFileHandler(projectRoot, compilerOptions, host);
|
|
@@ -1700,8 +1721,10 @@ class ValOps {
|
|
|
1700
1721
|
const filePath = op.filePath;
|
|
1701
1722
|
fileLastUpdatedByPatchId[filePath] = {
|
|
1702
1723
|
patchId: patch.patchId,
|
|
1703
|
-
remote: op.remote
|
|
1724
|
+
remote: op.remote,
|
|
1725
|
+
isDelete: op.value === null
|
|
1704
1726
|
};
|
|
1727
|
+
continue;
|
|
1705
1728
|
}
|
|
1706
1729
|
const path = patch.path;
|
|
1707
1730
|
if (!patchesByModule[path]) {
|
|
@@ -1776,15 +1799,19 @@ class ValOps {
|
|
|
1776
1799
|
const fileFixOps = {};
|
|
1777
1800
|
for (const op of patchData.patch) {
|
|
1778
1801
|
if (op.op === "file") {
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
op
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1802
|
+
if (op.value !== null) {
|
|
1803
|
+
// NOTE: We insert the last patch_id that modify a file
|
|
1804
|
+
// when constructing the url we use the patch id (and the file path)
|
|
1805
|
+
// to fetch the right file
|
|
1806
|
+
// NOTE: overwrite and use last patch_id if multiple patches modify the same file
|
|
1807
|
+
fileFixOps[op.path.join("/")] = [{
|
|
1808
|
+
op: "add",
|
|
1809
|
+
path: op.path.concat(...(op.nestedFilePath || [])).concat("patch_id"),
|
|
1810
|
+
value: patchId
|
|
1811
|
+
}];
|
|
1812
|
+
}
|
|
1813
|
+
// null value = delete: no patch_id to inject; the "remove" op in
|
|
1814
|
+
// the patch already removes the metadata entry from the source
|
|
1788
1815
|
} else {
|
|
1789
1816
|
applicableOps.push(op);
|
|
1790
1817
|
}
|
|
@@ -1897,6 +1924,21 @@ class ValOps {
|
|
|
1897
1924
|
const files = {};
|
|
1898
1925
|
const remoteFiles = {};
|
|
1899
1926
|
const entries = Object.entries(schemas);
|
|
1927
|
+
// Build a map of gallery directory → [ModuleFilePath, ...] across ALL modules
|
|
1928
|
+
// (must include all modules, not just those being validated, since conflicts can come from any module)
|
|
1929
|
+
const galleryDirectoryToModules = new Map();
|
|
1930
|
+
for (const [moduleFilePathS, schema] of entries) {
|
|
1931
|
+
const serialized = schema["executeSerialize"]();
|
|
1932
|
+
if (serialized.type === "record" && serialized.mediaType && serialized.directory) {
|
|
1933
|
+
const dir = serialized.directory;
|
|
1934
|
+
const existing = galleryDirectoryToModules.get(dir);
|
|
1935
|
+
if (existing) {
|
|
1936
|
+
existing.push(moduleFilePathS);
|
|
1937
|
+
} else {
|
|
1938
|
+
galleryDirectoryToModules.set(dir, [moduleFilePathS]);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1900
1942
|
const modulePathsToValidate = patchesByModule && Object.keys(patchesByModule);
|
|
1901
1943
|
for (const [pathS, schema] of entries) {
|
|
1902
1944
|
if (modulePathsToValidate && !modulePathsToValidate.includes(pathS)) {
|
|
@@ -1937,7 +1979,7 @@ class ValOps {
|
|
|
1937
1979
|
};
|
|
1938
1980
|
if (validationErrors) {
|
|
1939
1981
|
for (const validationError of validationErrors) {
|
|
1940
|
-
var _validationError$fixe, _validationError$fixe2, _validationError$fixe3, _validationError$fixe4;
|
|
1982
|
+
var _validationError$fixe, _validationError$fixe2, _validationError$fixe3, _validationError$fixe4, _validationError$fixe5, _validationError$fixe6, _validationError$fixe7, _validationError$fixe8;
|
|
1941
1983
|
if (isOnlyFileCheckValidationError(validationError)) {
|
|
1942
1984
|
if (files[sourcePath]) {
|
|
1943
1985
|
throw new Error("Cannot have multiple files with same path. Path: " + sourcePath + "; Module: " + path);
|
|
@@ -2020,7 +2062,32 @@ class ValOps {
|
|
|
2020
2062
|
}
|
|
2021
2063
|
}
|
|
2022
2064
|
}
|
|
2023
|
-
} else {
|
|
2065
|
+
} 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")) {
|
|
2066
|
+
const TYPE_ERROR_MESSAGE = `This is most likely a Val version mismatch or Val bug.`;
|
|
2067
|
+
if (!validationError.value || typeof validationError.value !== "object") {
|
|
2068
|
+
addError({
|
|
2069
|
+
message: `Could not find a directory value for gallery at ${sourcePath}. ${TYPE_ERROR_MESSAGE}`,
|
|
2070
|
+
typeError: true
|
|
2071
|
+
});
|
|
2072
|
+
} else {
|
|
2073
|
+
const directory = "directory" in validationError.value && validationError.value.directory;
|
|
2074
|
+
if (typeof directory !== "string") {
|
|
2075
|
+
addError({
|
|
2076
|
+
message: `Expected gallery validation error 'value' to have property 'directory' of type 'string'. Found: ${typeof directory}. ${TYPE_ERROR_MESSAGE}`,
|
|
2077
|
+
typeError: true
|
|
2078
|
+
});
|
|
2079
|
+
} else {
|
|
2080
|
+
const modulesUsingDir = galleryDirectoryToModules.get(directory) ?? [];
|
|
2081
|
+
const conflictingModules = modulesUsingDir.filter(m => m !== path);
|
|
2082
|
+
if (conflictingModules.length > 0) {
|
|
2083
|
+
addError({
|
|
2084
|
+
message: `Gallery directory '${directory}' in ${path} conflicts with: ${conflictingModules.join(", ")}. Each gallery must use a unique directory.`
|
|
2085
|
+
});
|
|
2086
|
+
}
|
|
2087
|
+
// If conflictingModules is empty, directory is unique — silently drop the error.
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
} else if ((_validationError$fixe7 = validationError.fixes) !== null && _validationError$fixe7 !== void 0 && _validationError$fixe7.includes("images:check-all-files") || (_validationError$fixe8 = validationError.fixes) !== null && _validationError$fixe8 !== void 0 && _validationError$fixe8.includes("files:check-all-files")) ; else {
|
|
2024
2091
|
addError(validationError);
|
|
2025
2092
|
}
|
|
2026
2093
|
}
|
|
@@ -2300,16 +2367,22 @@ class ValOps {
|
|
|
2300
2367
|
await Promise.all(Object.entries(fileLastUpdatedByPatchId).map(async ([filePath, patchData]) => {
|
|
2301
2368
|
const {
|
|
2302
2369
|
patchId,
|
|
2303
|
-
remote
|
|
2370
|
+
remote,
|
|
2371
|
+
isDelete
|
|
2304
2372
|
} = patchData;
|
|
2305
2373
|
if (globalAppliedPatches.includes(patchId)) {
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2374
|
+
if (isDelete) {
|
|
2375
|
+
// Signal file deletion via patchedSourceFiles null entry
|
|
2376
|
+
patchedSourceFiles[filePath] = null;
|
|
2377
|
+
} else {
|
|
2378
|
+
// TODO: do we want to make sure the file is there? Then again, it should be rare that it happens (unless there's a Val bug) so it might be enough to fail later (at commit)
|
|
2379
|
+
// TODO: include sha256? This way we can make sure we pick the right file since theoretically there could be multiple files with the same path in the same patch
|
|
2380
|
+
// or is that the case? We are picking the latest file by path so, that should be enough?
|
|
2381
|
+
patchedBinaryFilesDescriptors[filePath] = {
|
|
2382
|
+
patchId,
|
|
2383
|
+
remote
|
|
2384
|
+
};
|
|
2385
|
+
}
|
|
2313
2386
|
} else {
|
|
2314
2387
|
hasErrors = true;
|
|
2315
2388
|
binaryFilePatchErrors[filePath] = {
|
|
@@ -2332,8 +2405,8 @@ class ValOps {
|
|
|
2332
2405
|
}
|
|
2333
2406
|
|
|
2334
2407
|
// #region createPatch
|
|
2335
|
-
async createPatch(path, patch, patchId, parentRef, authorId) {
|
|
2336
|
-
const saveRes = await this.saveSourceFilePatch(path, patch, patchId, parentRef, authorId);
|
|
2408
|
+
async createPatch(path, patch, patchId, parentRef, sessionId, authorId) {
|
|
2409
|
+
const saveRes = await this.saveSourceFilePatch(path, patch, patchId, parentRef, authorId, sessionId);
|
|
2337
2410
|
if (fp.result.isErr(saveRes)) {
|
|
2338
2411
|
console.error(`Could not save source patch at path: '${path}'. Error: ${saveRes.error.errorType === "other" ? saveRes.error.message : saveRes.error.errorType}`);
|
|
2339
2412
|
if (saveRes.error.errorType === "patch-head-conflict") {
|
|
@@ -2355,8 +2428,8 @@ class ValOps {
|
|
|
2355
2428
|
// #region abstract ops
|
|
2356
2429
|
}
|
|
2357
2430
|
function isOnlyFileCheckValidationError(validationError) {
|
|
2358
|
-
var _validationError$
|
|
2359
|
-
if ((_validationError$
|
|
2431
|
+
var _validationError$fixe9;
|
|
2432
|
+
if ((_validationError$fixe9 = validationError.fixes) !== null && _validationError$fixe9 !== void 0 && _validationError$fixe9.every(f => f === "file:check-metadata" || f === "image:check-metadata")) {
|
|
2360
2433
|
return true;
|
|
2361
2434
|
}
|
|
2362
2435
|
return false;
|
|
@@ -2725,11 +2798,17 @@ class ValOpsFS extends ValOps {
|
|
|
2725
2798
|
parentPatchId: dir
|
|
2726
2799
|
});
|
|
2727
2800
|
} else {
|
|
2728
|
-
|
|
2801
|
+
const patchId = parsedPatch.data.patchId;
|
|
2802
|
+
if (includes && includes.length > 0 && !includes.includes(patchId)) {
|
|
2729
2803
|
return;
|
|
2730
2804
|
}
|
|
2731
|
-
patches[
|
|
2732
|
-
|
|
2805
|
+
patches[patchId] = {
|
|
2806
|
+
path: parsedPatch.data.path,
|
|
2807
|
+
patch: parsedPatch.data.patch,
|
|
2808
|
+
parentRef: parsedPatch.data.parentRef,
|
|
2809
|
+
baseSha: parsedPatch.data.baseSha,
|
|
2810
|
+
createdAt: parsedPatch.data.createdAt,
|
|
2811
|
+
authorId: parsedPatch.data.authorId,
|
|
2733
2812
|
appliedAt: null
|
|
2734
2813
|
};
|
|
2735
2814
|
}
|
|
@@ -2900,7 +2979,7 @@ class ValOpsFS extends ValOps {
|
|
|
2900
2979
|
};
|
|
2901
2980
|
}
|
|
2902
2981
|
}
|
|
2903
|
-
async saveSourceFilePatch(path, patch, patchId, parentRef, authorId) {
|
|
2982
|
+
async saveSourceFilePatch(path, patch, patchId, parentRef, authorId, sessionId) {
|
|
2904
2983
|
const patchDir = this.getParentPatchIdFromParentRef(parentRef);
|
|
2905
2984
|
try {
|
|
2906
2985
|
const baseSha = await this.getBaseSha();
|
|
@@ -2910,6 +2989,7 @@ class ValOpsFS extends ValOps {
|
|
|
2910
2989
|
parentRef,
|
|
2911
2990
|
path,
|
|
2912
2991
|
authorId,
|
|
2992
|
+
sessionId,
|
|
2913
2993
|
baseSha,
|
|
2914
2994
|
coreVersion: core.Internal.VERSION.core,
|
|
2915
2995
|
createdAt: new Date().toISOString()
|
|
@@ -2987,6 +3067,14 @@ class ValOpsFS extends ValOps {
|
|
|
2987
3067
|
const patchFilePath = this.getBinaryFilePath(filePath, patchDir);
|
|
2988
3068
|
const metadataFilePath = this.getBinaryFileMetadataPath(filePath, patchDir);
|
|
2989
3069
|
try {
|
|
3070
|
+
if (data === null) {
|
|
3071
|
+
this.host.deleteFile(patchFilePath);
|
|
3072
|
+
this.host.deleteFile(metadataFilePath);
|
|
3073
|
+
return {
|
|
3074
|
+
patchId,
|
|
3075
|
+
filePath
|
|
3076
|
+
};
|
|
3077
|
+
}
|
|
2990
3078
|
const buffer = bufferFromDataUrl(data);
|
|
2991
3079
|
if (!buffer) {
|
|
2992
3080
|
return {
|
|
@@ -3247,7 +3335,11 @@ class ValOpsFS extends ValOps {
|
|
|
3247
3335
|
for (const [filePath, data] of Object.entries(preparedCommit.patchedSourceFiles)) {
|
|
3248
3336
|
const absPath = path__namespace["default"].join(this.rootDir, ...filePath.split("/"));
|
|
3249
3337
|
try {
|
|
3250
|
-
|
|
3338
|
+
if (data === null) {
|
|
3339
|
+
this.host.deleteFile(absPath);
|
|
3340
|
+
} else {
|
|
3341
|
+
this.host.writeUf8File(absPath, data);
|
|
3342
|
+
}
|
|
3251
3343
|
updatedFiles.push(absPath);
|
|
3252
3344
|
} catch (err) {
|
|
3253
3345
|
errors[absPath] = {
|
|
@@ -3336,12 +3428,6 @@ class ValOpsFS extends ValOps {
|
|
|
3336
3428
|
return fp.result.ok(Object.fromEntries(Object.entries(patches.patches).map(([patchId, value]) => [patchId, this.getParentPatchIdFromParentRef(value.parentRef)])));
|
|
3337
3429
|
}
|
|
3338
3430
|
|
|
3339
|
-
// #region profiles
|
|
3340
|
-
async getProfiles() {
|
|
3341
|
-
// We do not have profiles in FS mode
|
|
3342
|
-
return [];
|
|
3343
|
-
}
|
|
3344
|
-
|
|
3345
3431
|
// #region fs file path helpers
|
|
3346
3432
|
getPatchesDir() {
|
|
3347
3433
|
return path__namespace["default"].join(this.rootDir, ValOpsFS.VAL_DIR, "patches");
|
|
@@ -3373,6 +3459,11 @@ class FSOpsHost {
|
|
|
3373
3459
|
});
|
|
3374
3460
|
}
|
|
3375
3461
|
}
|
|
3462
|
+
deleteFile(path) {
|
|
3463
|
+
if (this.fileExists(path)) {
|
|
3464
|
+
fs__default["default"].rmSync(path);
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3376
3467
|
moveDir(from, to) {
|
|
3377
3468
|
fs__default["default"].renameSync(from, to);
|
|
3378
3469
|
}
|
|
@@ -3445,7 +3536,9 @@ const FSPatch = zod.z.object({
|
|
|
3445
3536
|
parentRef: internal.ParentRef,
|
|
3446
3537
|
authorId: zod.z.string().refine(p => true).nullable(),
|
|
3447
3538
|
createdAt: zod.z.string().datetime(),
|
|
3448
|
-
coreVersion: zod.z.string().nullable()
|
|
3539
|
+
coreVersion: zod.z.string().nullable(),
|
|
3540
|
+
// TODO: use this to check if patch is compatible with current core version?
|
|
3541
|
+
sessionId: zod.z.string().nullable()
|
|
3449
3542
|
});
|
|
3450
3543
|
const FSPatchBase = zod.z.object({
|
|
3451
3544
|
baseSha: zod.z.string().refine(p => true),
|
|
@@ -3547,17 +3640,6 @@ const CommitResponse = zod.z.object({
|
|
|
3547
3640
|
commit: CommitSha,
|
|
3548
3641
|
branch: zod.z.string()
|
|
3549
3642
|
});
|
|
3550
|
-
const ProfilesResponse = zod.z.object({
|
|
3551
|
-
profiles: zod.z.array(zod.z.object({
|
|
3552
|
-
profileId: zod.z.string(),
|
|
3553
|
-
fullName: zod.z.string(),
|
|
3554
|
-
email: zod.z.string().optional(),
|
|
3555
|
-
// TODO: make this required once this can be guaranteed
|
|
3556
|
-
avatar: zod.z.object({
|
|
3557
|
-
url: zod.z.string()
|
|
3558
|
-
}).nullable()
|
|
3559
|
-
}))
|
|
3560
|
-
});
|
|
3561
3643
|
const NonceResponse = zod.z.object({
|
|
3562
3644
|
nonce: zod.z.string(),
|
|
3563
3645
|
url: zod.z.string()
|
|
@@ -4008,7 +4090,7 @@ class ValOpsHttp extends ValOps {
|
|
|
4008
4090
|
};
|
|
4009
4091
|
}
|
|
4010
4092
|
}
|
|
4011
|
-
async saveSourceFilePatch(path, patch, patchId, parentRef, authorId) {
|
|
4093
|
+
async saveSourceFilePatch(path, patch, patchId, parentRef, authorId, sessionId) {
|
|
4012
4094
|
const baseSha = await this.getBaseSha();
|
|
4013
4095
|
return fetch(`${this.contentUrl}/v1/${this.project}/patches`, {
|
|
4014
4096
|
method: "POST",
|
|
@@ -4020,6 +4102,7 @@ class ValOpsHttp extends ValOps {
|
|
|
4020
4102
|
path,
|
|
4021
4103
|
patch,
|
|
4022
4104
|
authorId,
|
|
4105
|
+
sessionId,
|
|
4023
4106
|
patchId,
|
|
4024
4107
|
parentPatchId: parentRef.type === "patch" ? parentRef.patchId : null,
|
|
4025
4108
|
baseSha,
|
|
@@ -4443,31 +4526,6 @@ class ValOpsHttp extends ValOps {
|
|
|
4443
4526
|
};
|
|
4444
4527
|
}
|
|
4445
4528
|
}
|
|
4446
|
-
|
|
4447
|
-
// #region profiles
|
|
4448
|
-
async getProfiles() {
|
|
4449
|
-
var _res$headers$get6;
|
|
4450
|
-
const res = await fetch(`${this.contentUrl}/v1/${this.project}/profiles`, {
|
|
4451
|
-
headers: {
|
|
4452
|
-
...this.authHeaders,
|
|
4453
|
-
"Content-Type": "application/json"
|
|
4454
|
-
}
|
|
4455
|
-
});
|
|
4456
|
-
if (res.ok) {
|
|
4457
|
-
const parsed = ProfilesResponse.safeParse(await res.json());
|
|
4458
|
-
if (parsed.error) {
|
|
4459
|
-
console.error("Could not parse profiles response", parsed.error);
|
|
4460
|
-
throw Error(`Could not get profiles from remote server: wrong format. You might need to upgrade Val.`);
|
|
4461
|
-
}
|
|
4462
|
-
return parsed.data.profiles;
|
|
4463
|
-
}
|
|
4464
|
-
if ((_res$headers$get6 = res.headers.get("Content-Type")) !== null && _res$headers$get6 !== void 0 && _res$headers$get6.includes("application/json")) {
|
|
4465
|
-
const json = await res.json();
|
|
4466
|
-
const message = internal.getErrorMessageFromUnknownJson(json, "Unknown error");
|
|
4467
|
-
throw Error(`Could not get profiles (status: ${res.status}): ${message}`);
|
|
4468
|
-
}
|
|
4469
|
-
throw Error(`Could not get profiles. Got status: ${res.status}`);
|
|
4470
|
-
}
|
|
4471
4529
|
}
|
|
4472
4530
|
|
|
4473
4531
|
const host = process.env.VAL_CONTENT_URL || core.DEFAULT_CONTENT_HOST;
|
|
@@ -4620,6 +4678,16 @@ function hasRemoteFileSchema(schema) {
|
|
|
4620
4678
|
|
|
4621
4679
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
4622
4680
|
const ValServer = (valModules, options, callbacks) => {
|
|
4681
|
+
const ProfilesResponse = zod.z.object({
|
|
4682
|
+
profiles: zod.z.array(zod.z.object({
|
|
4683
|
+
profileId: zod.z.string(),
|
|
4684
|
+
fullName: zod.z.string(),
|
|
4685
|
+
email: zod.z.string().optional(),
|
|
4686
|
+
avatar: zod.z.object({
|
|
4687
|
+
url: zod.z.string()
|
|
4688
|
+
}).nullable()
|
|
4689
|
+
}))
|
|
4690
|
+
});
|
|
4623
4691
|
let serverOps;
|
|
4624
4692
|
if (options.mode === "fs") {
|
|
4625
4693
|
serverOps = new ValOpsFS(options.valContentUrl, options.cwd, valModules, {
|
|
@@ -5469,10 +5537,11 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
5469
5537
|
}
|
|
5470
5538
|
const patches = req.body.patches;
|
|
5471
5539
|
let parentRef = req.body.parentRef;
|
|
5540
|
+
const sessionId = req.body.sessionId ?? null;
|
|
5472
5541
|
const authorId = "id" in auth ? auth.id : null;
|
|
5473
5542
|
const newPatchIds = [];
|
|
5474
5543
|
for (const patch of patches) {
|
|
5475
|
-
const createPatchRes = await serverOps.createPatch(patch.path, patch.patch, patch.patchId, parentRef, authorId);
|
|
5544
|
+
const createPatchRes = await serverOps.createPatch(patch.path, patch.patch, patch.patchId, parentRef, sessionId, authorId);
|
|
5476
5545
|
if (fp.result.isErr(createPatchRes)) {
|
|
5477
5546
|
if (createPatchRes.error.errorType === "patch-head-conflict") {
|
|
5478
5547
|
return {
|
|
@@ -5833,13 +5902,87 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
5833
5902
|
}
|
|
5834
5903
|
};
|
|
5835
5904
|
}
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5905
|
+
if (!options.project) {
|
|
5906
|
+
return {
|
|
5907
|
+
status: 500,
|
|
5908
|
+
json: {
|
|
5909
|
+
message: "Project is not configured"
|
|
5910
|
+
}
|
|
5911
|
+
};
|
|
5912
|
+
}
|
|
5913
|
+
const authDataRes = await getRemoteFileAuth();
|
|
5914
|
+
if (authDataRes.status !== 200) {
|
|
5915
|
+
if (serverOps instanceof ValOpsFS && authDataRes.json.errorCode === "pat-error") {
|
|
5916
|
+
return {
|
|
5917
|
+
status: 200,
|
|
5918
|
+
json: {
|
|
5919
|
+
profiles: []
|
|
5920
|
+
}
|
|
5921
|
+
};
|
|
5922
|
+
}
|
|
5923
|
+
return {
|
|
5924
|
+
status: 500,
|
|
5925
|
+
json: {
|
|
5926
|
+
message: authDataRes.json.message
|
|
5927
|
+
}
|
|
5928
|
+
};
|
|
5929
|
+
}
|
|
5930
|
+
const authData = authDataRes.json.remoteFileAuth;
|
|
5931
|
+
const execFetch = async headers => {
|
|
5932
|
+
try {
|
|
5933
|
+
const upstreamUrl = `${options.valContentUrl}/v1/${options.project}/profiles`;
|
|
5934
|
+
const upstreamRes = await fetch(upstreamUrl, {
|
|
5935
|
+
method: "GET",
|
|
5936
|
+
headers
|
|
5937
|
+
});
|
|
5938
|
+
if (!upstreamRes.ok) {
|
|
5939
|
+
const text = await upstreamRes.text();
|
|
5940
|
+
const isAuthError = upstreamRes.status === 401 || upstreamRes.status === 403;
|
|
5941
|
+
return {
|
|
5942
|
+
status: isAuthError ? 401 : 500,
|
|
5943
|
+
json: {
|
|
5944
|
+
message: isAuthError ? `Profile authentication failed: ${upstreamRes.status} ${text}` : `Profiles failed: ${upstreamRes.status} ${text}`
|
|
5945
|
+
}
|
|
5946
|
+
};
|
|
5947
|
+
}
|
|
5948
|
+
const parseRes = ProfilesResponse.safeParse(await upstreamRes.json());
|
|
5949
|
+
if (!parseRes.success) {
|
|
5950
|
+
return {
|
|
5951
|
+
status: 500,
|
|
5952
|
+
json: {
|
|
5953
|
+
message: "Could not parse profiles response: " + zodValidationError.fromError(parseRes.error).toString()
|
|
5954
|
+
}
|
|
5955
|
+
};
|
|
5956
|
+
}
|
|
5957
|
+
return {
|
|
5958
|
+
status: 200,
|
|
5959
|
+
json: {
|
|
5960
|
+
profiles: parseRes.data.profiles
|
|
5961
|
+
}
|
|
5962
|
+
};
|
|
5963
|
+
} catch (err) {
|
|
5964
|
+
return {
|
|
5965
|
+
status: 500,
|
|
5966
|
+
json: {
|
|
5967
|
+
message: err instanceof Error ? err.message : "Profiles request failed"
|
|
5968
|
+
}
|
|
5969
|
+
};
|
|
5841
5970
|
}
|
|
5842
5971
|
};
|
|
5972
|
+
if (serverOps instanceof ValOpsFS) {
|
|
5973
|
+
return execFetch(getProfileAuthHeaders(authData, null, "application/json"));
|
|
5974
|
+
}
|
|
5975
|
+
if (!options.valSecret) {
|
|
5976
|
+
return {
|
|
5977
|
+
status: 500,
|
|
5978
|
+
json: {
|
|
5979
|
+
message: "Secret is not configured"
|
|
5980
|
+
}
|
|
5981
|
+
};
|
|
5982
|
+
}
|
|
5983
|
+
return withAuth(options.valSecret, cookies, "profiles", data => {
|
|
5984
|
+
return execFetch(getProfileAuthHeaders(authData, data, "application/json"));
|
|
5985
|
+
});
|
|
5843
5986
|
}
|
|
5844
5987
|
},
|
|
5845
5988
|
"/commit-summary": {
|
|
@@ -6021,6 +6164,381 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
6021
6164
|
}
|
|
6022
6165
|
}
|
|
6023
6166
|
},
|
|
6167
|
+
//#region ai proxy
|
|
6168
|
+
"/ai/initialize": {
|
|
6169
|
+
POST: async req => {
|
|
6170
|
+
const cookies = req.cookies;
|
|
6171
|
+
const auth = getAuth(cookies);
|
|
6172
|
+
if (auth.error) {
|
|
6173
|
+
return {
|
|
6174
|
+
status: 401,
|
|
6175
|
+
json: {
|
|
6176
|
+
message: auth.error
|
|
6177
|
+
}
|
|
6178
|
+
};
|
|
6179
|
+
}
|
|
6180
|
+
if (!options.project) {
|
|
6181
|
+
return {
|
|
6182
|
+
status: 500,
|
|
6183
|
+
json: {
|
|
6184
|
+
message: "Project is not configured"
|
|
6185
|
+
}
|
|
6186
|
+
};
|
|
6187
|
+
}
|
|
6188
|
+
const authDataRes = await getRemoteFileAuth();
|
|
6189
|
+
if (authDataRes.status !== 200) {
|
|
6190
|
+
return {
|
|
6191
|
+
status: 500,
|
|
6192
|
+
json: {
|
|
6193
|
+
message: authDataRes.json.message
|
|
6194
|
+
}
|
|
6195
|
+
};
|
|
6196
|
+
}
|
|
6197
|
+
const authData = authDataRes.json.remoteFileAuth;
|
|
6198
|
+
const execFetch = async headers => {
|
|
6199
|
+
try {
|
|
6200
|
+
const upstreamUrl = `${options.valContentUrl}/v1/${options.project}/ai/initialize`;
|
|
6201
|
+
const upstreamRes = await fetch(upstreamUrl, {
|
|
6202
|
+
method: "POST",
|
|
6203
|
+
headers,
|
|
6204
|
+
body: JSON.stringify({})
|
|
6205
|
+
});
|
|
6206
|
+
if (!upstreamRes.ok) {
|
|
6207
|
+
const text = await upstreamRes.text();
|
|
6208
|
+
return {
|
|
6209
|
+
status: 500,
|
|
6210
|
+
json: {
|
|
6211
|
+
message: `AI initialize failed: ${upstreamRes.status} ${text}`
|
|
6212
|
+
}
|
|
6213
|
+
};
|
|
6214
|
+
}
|
|
6215
|
+
const json = await upstreamRes.json();
|
|
6216
|
+
const wsUrl = options.valContentUrl.replace(/^https:/, "wss:").replace(/^http:/, "ws:") + `/v1/${options.project}/ai/connect`;
|
|
6217
|
+
return {
|
|
6218
|
+
status: 200,
|
|
6219
|
+
json: {
|
|
6220
|
+
nonce: json.nonce,
|
|
6221
|
+
wsUrl
|
|
6222
|
+
}
|
|
6223
|
+
};
|
|
6224
|
+
} catch (err) {
|
|
6225
|
+
return {
|
|
6226
|
+
status: 500,
|
|
6227
|
+
json: {
|
|
6228
|
+
message: err instanceof Error ? err.message : "AI initialize error"
|
|
6229
|
+
}
|
|
6230
|
+
};
|
|
6231
|
+
}
|
|
6232
|
+
};
|
|
6233
|
+
if (serverOps instanceof ValOpsFS) {
|
|
6234
|
+
return execFetch(getProfileAuthHeaders(authData, null, "application/json"));
|
|
6235
|
+
}
|
|
6236
|
+
if (!options.valSecret) {
|
|
6237
|
+
return {
|
|
6238
|
+
status: 500,
|
|
6239
|
+
json: {
|
|
6240
|
+
message: "Secret is not configured"
|
|
6241
|
+
}
|
|
6242
|
+
};
|
|
6243
|
+
}
|
|
6244
|
+
return withAuth(options.valSecret, cookies, "ai/initialize", data => {
|
|
6245
|
+
return execFetch(getProfileAuthHeaders(authData, data, "application/json"));
|
|
6246
|
+
});
|
|
6247
|
+
}
|
|
6248
|
+
},
|
|
6249
|
+
"/ai/sessions": {
|
|
6250
|
+
GET: async req => {
|
|
6251
|
+
const cookies = req.cookies;
|
|
6252
|
+
const auth = getAuth(cookies);
|
|
6253
|
+
if (auth.error) {
|
|
6254
|
+
return {
|
|
6255
|
+
status: 401,
|
|
6256
|
+
json: {
|
|
6257
|
+
message: auth.error
|
|
6258
|
+
}
|
|
6259
|
+
};
|
|
6260
|
+
}
|
|
6261
|
+
if (!options.project) {
|
|
6262
|
+
return {
|
|
6263
|
+
status: 500,
|
|
6264
|
+
json: {
|
|
6265
|
+
message: "Project is not configured"
|
|
6266
|
+
}
|
|
6267
|
+
};
|
|
6268
|
+
}
|
|
6269
|
+
const authDataRes = await getRemoteFileAuth();
|
|
6270
|
+
if (authDataRes.status !== 200) {
|
|
6271
|
+
return {
|
|
6272
|
+
status: 500,
|
|
6273
|
+
json: {
|
|
6274
|
+
message: authDataRes.json.message
|
|
6275
|
+
}
|
|
6276
|
+
};
|
|
6277
|
+
}
|
|
6278
|
+
const authData = authDataRes.json.remoteFileAuth;
|
|
6279
|
+
const execFetch = async headers => {
|
|
6280
|
+
try {
|
|
6281
|
+
const SessionsResponse = zod.z.object({
|
|
6282
|
+
sessions: zod.z.array(zod.z.object({
|
|
6283
|
+
id: zod.z.string(),
|
|
6284
|
+
name: zod.z.string().nullable(),
|
|
6285
|
+
createdAt: zod.z.string(),
|
|
6286
|
+
updatedAt: zod.z.string()
|
|
6287
|
+
})),
|
|
6288
|
+
nextCursor: zod.z.object({
|
|
6289
|
+
updatedAt: zod.z.string(),
|
|
6290
|
+
id: zod.z.string()
|
|
6291
|
+
}).nullable().optional()
|
|
6292
|
+
});
|
|
6293
|
+
const params = new URLSearchParams();
|
|
6294
|
+
if (req.query.limit) params.set("limit", req.query.limit);
|
|
6295
|
+
if (req.query.cursor_updatedAt) {
|
|
6296
|
+
params.set("cursor_updatedAt", req.query.cursor_updatedAt);
|
|
6297
|
+
}
|
|
6298
|
+
if (req.query.cursor_id) {
|
|
6299
|
+
params.set("cursor_id", req.query.cursor_id);
|
|
6300
|
+
}
|
|
6301
|
+
const qs = params.toString();
|
|
6302
|
+
const upstreamUrl = `${options.valContentUrl}/v1/${options.project}/ai/sessions${qs ? `?${qs}` : ""}`;
|
|
6303
|
+
const upstreamRes = await fetch(upstreamUrl, {
|
|
6304
|
+
headers
|
|
6305
|
+
});
|
|
6306
|
+
if (!upstreamRes.ok) {
|
|
6307
|
+
const text = await upstreamRes.text();
|
|
6308
|
+
return {
|
|
6309
|
+
status: 500,
|
|
6310
|
+
json: {
|
|
6311
|
+
message: `AI sessions failed: ${upstreamRes.status} ${text}`
|
|
6312
|
+
}
|
|
6313
|
+
};
|
|
6314
|
+
}
|
|
6315
|
+
const json = SessionsResponse.safeParse(await upstreamRes.json());
|
|
6316
|
+
if (!json.success) {
|
|
6317
|
+
return {
|
|
6318
|
+
status: 500,
|
|
6319
|
+
json: {
|
|
6320
|
+
message: "Could not parse AI sessions response: " + zodValidationError.fromError(json.error).toString()
|
|
6321
|
+
}
|
|
6322
|
+
};
|
|
6323
|
+
}
|
|
6324
|
+
return {
|
|
6325
|
+
status: 200,
|
|
6326
|
+
json: json.data
|
|
6327
|
+
};
|
|
6328
|
+
} catch (err) {
|
|
6329
|
+
return {
|
|
6330
|
+
status: 500,
|
|
6331
|
+
json: {
|
|
6332
|
+
message: err instanceof Error ? err.message : "AI sessions error"
|
|
6333
|
+
}
|
|
6334
|
+
};
|
|
6335
|
+
}
|
|
6336
|
+
};
|
|
6337
|
+
if (serverOps instanceof ValOpsFS) {
|
|
6338
|
+
return execFetch(getProfileAuthHeaders(authData, null, "application/json"));
|
|
6339
|
+
}
|
|
6340
|
+
if (!options.valSecret) {
|
|
6341
|
+
return {
|
|
6342
|
+
status: 500,
|
|
6343
|
+
json: {
|
|
6344
|
+
message: "Secret is not configured"
|
|
6345
|
+
}
|
|
6346
|
+
};
|
|
6347
|
+
}
|
|
6348
|
+
return withAuth(options.valSecret, cookies, "ai/sessions", data => {
|
|
6349
|
+
return execFetch(getProfileAuthHeaders(authData, data, "application/json"));
|
|
6350
|
+
});
|
|
6351
|
+
},
|
|
6352
|
+
PATCH: async req => {
|
|
6353
|
+
const cookies = req.cookies;
|
|
6354
|
+
const auth = getAuth(cookies);
|
|
6355
|
+
if (auth.error) {
|
|
6356
|
+
return {
|
|
6357
|
+
status: 401,
|
|
6358
|
+
json: {
|
|
6359
|
+
message: auth.error
|
|
6360
|
+
}
|
|
6361
|
+
};
|
|
6362
|
+
}
|
|
6363
|
+
if (!options.project) {
|
|
6364
|
+
return {
|
|
6365
|
+
status: 500,
|
|
6366
|
+
json: {
|
|
6367
|
+
message: "Project is not configured"
|
|
6368
|
+
}
|
|
6369
|
+
};
|
|
6370
|
+
}
|
|
6371
|
+
const pathParts = (req.path || "").split("/").filter(Boolean);
|
|
6372
|
+
const sessionId = pathParts[0];
|
|
6373
|
+
if (!sessionId) {
|
|
6374
|
+
return {
|
|
6375
|
+
status: 500,
|
|
6376
|
+
json: {
|
|
6377
|
+
message: "Missing sessionId in path"
|
|
6378
|
+
}
|
|
6379
|
+
};
|
|
6380
|
+
}
|
|
6381
|
+
const authDataRes = await getRemoteFileAuth();
|
|
6382
|
+
if (authDataRes.status !== 200) {
|
|
6383
|
+
return {
|
|
6384
|
+
status: 500,
|
|
6385
|
+
json: {
|
|
6386
|
+
message: authDataRes.json.message
|
|
6387
|
+
}
|
|
6388
|
+
};
|
|
6389
|
+
}
|
|
6390
|
+
const authData = authDataRes.json.remoteFileAuth;
|
|
6391
|
+
const execFetch = async headers => {
|
|
6392
|
+
try {
|
|
6393
|
+
const upstreamUrl = `${options.valContentUrl}/v1/${options.project}/ai/sessions/${encodeURIComponent(sessionId)}`;
|
|
6394
|
+
const upstreamRes = await fetch(upstreamUrl, {
|
|
6395
|
+
method: "PATCH",
|
|
6396
|
+
headers,
|
|
6397
|
+
body: JSON.stringify({
|
|
6398
|
+
name: req.body.name
|
|
6399
|
+
})
|
|
6400
|
+
});
|
|
6401
|
+
if (!upstreamRes.ok) {
|
|
6402
|
+
const text = await upstreamRes.text();
|
|
6403
|
+
return {
|
|
6404
|
+
status: 500,
|
|
6405
|
+
json: {
|
|
6406
|
+
message: `AI session rename failed: ${upstreamRes.status} ${text}`
|
|
6407
|
+
}
|
|
6408
|
+
};
|
|
6409
|
+
}
|
|
6410
|
+
return {
|
|
6411
|
+
status: 200,
|
|
6412
|
+
json: {}
|
|
6413
|
+
};
|
|
6414
|
+
} catch (err) {
|
|
6415
|
+
return {
|
|
6416
|
+
status: 500,
|
|
6417
|
+
json: {
|
|
6418
|
+
message: err instanceof Error ? err.message : "AI session rename error"
|
|
6419
|
+
}
|
|
6420
|
+
};
|
|
6421
|
+
}
|
|
6422
|
+
};
|
|
6423
|
+
if (serverOps instanceof ValOpsFS) {
|
|
6424
|
+
return execFetch(getProfileAuthHeaders(authData, null, "application/json"));
|
|
6425
|
+
}
|
|
6426
|
+
if (!options.valSecret) {
|
|
6427
|
+
return {
|
|
6428
|
+
status: 500,
|
|
6429
|
+
json: {
|
|
6430
|
+
message: "Secret is not configured"
|
|
6431
|
+
}
|
|
6432
|
+
};
|
|
6433
|
+
}
|
|
6434
|
+
return withAuth(options.valSecret, cookies, "ai/sessions/rename", data => {
|
|
6435
|
+
return execFetch(getProfileAuthHeaders(authData, data, "application/json"));
|
|
6436
|
+
});
|
|
6437
|
+
}
|
|
6438
|
+
},
|
|
6439
|
+
"/ai/messages": {
|
|
6440
|
+
GET: async req => {
|
|
6441
|
+
const cookies = req.cookies;
|
|
6442
|
+
const auth = getAuth(cookies);
|
|
6443
|
+
if (auth.error) {
|
|
6444
|
+
return {
|
|
6445
|
+
status: 401,
|
|
6446
|
+
json: {
|
|
6447
|
+
message: auth.error
|
|
6448
|
+
}
|
|
6449
|
+
};
|
|
6450
|
+
}
|
|
6451
|
+
if (!options.project) {
|
|
6452
|
+
return {
|
|
6453
|
+
status: 500,
|
|
6454
|
+
json: {
|
|
6455
|
+
message: "Project is not configured"
|
|
6456
|
+
}
|
|
6457
|
+
};
|
|
6458
|
+
}
|
|
6459
|
+
const pathParts = (req.path || "").split("/").filter(Boolean);
|
|
6460
|
+
const sessionId = pathParts[0];
|
|
6461
|
+
if (!sessionId) {
|
|
6462
|
+
return {
|
|
6463
|
+
status: 500,
|
|
6464
|
+
json: {
|
|
6465
|
+
message: "Missing sessionId in path"
|
|
6466
|
+
}
|
|
6467
|
+
};
|
|
6468
|
+
}
|
|
6469
|
+
const authDataRes = await getRemoteFileAuth();
|
|
6470
|
+
if (authDataRes.status !== 200) {
|
|
6471
|
+
return {
|
|
6472
|
+
status: 500,
|
|
6473
|
+
json: {
|
|
6474
|
+
message: authDataRes.json.message
|
|
6475
|
+
}
|
|
6476
|
+
};
|
|
6477
|
+
}
|
|
6478
|
+
const authData = authDataRes.json.remoteFileAuth;
|
|
6479
|
+
const execFetch = async headers => {
|
|
6480
|
+
try {
|
|
6481
|
+
const SessionMessagesResponse = zod.z.object({
|
|
6482
|
+
messages: zod.z.array(zod.z.object({
|
|
6483
|
+
role: zod.z.string(),
|
|
6484
|
+
content: zod.z.string()
|
|
6485
|
+
})),
|
|
6486
|
+
nextCursor: zod.z.object({
|
|
6487
|
+
updatedAt: zod.z.string(),
|
|
6488
|
+
id: zod.z.string()
|
|
6489
|
+
}).nullable().optional()
|
|
6490
|
+
});
|
|
6491
|
+
const upstreamUrl = `${options.valContentUrl}/v1/${options.project}/ai/sessions/${encodeURIComponent(sessionId)}/messages`;
|
|
6492
|
+
const upstreamRes = await fetch(upstreamUrl, {
|
|
6493
|
+
headers
|
|
6494
|
+
});
|
|
6495
|
+
if (!upstreamRes.ok) {
|
|
6496
|
+
const text = await upstreamRes.text();
|
|
6497
|
+
return {
|
|
6498
|
+
status: 500,
|
|
6499
|
+
json: {
|
|
6500
|
+
message: `AI session messages failed: ${upstreamRes.status} ${text}`
|
|
6501
|
+
}
|
|
6502
|
+
};
|
|
6503
|
+
}
|
|
6504
|
+
const json = SessionMessagesResponse.safeParse(await upstreamRes.json());
|
|
6505
|
+
if (!json.success) {
|
|
6506
|
+
return {
|
|
6507
|
+
status: 500,
|
|
6508
|
+
json: {
|
|
6509
|
+
message: "Could not parse AI session messages response: " + zodValidationError.fromError(json.error).toString()
|
|
6510
|
+
}
|
|
6511
|
+
};
|
|
6512
|
+
}
|
|
6513
|
+
return {
|
|
6514
|
+
status: 200,
|
|
6515
|
+
json: json.data
|
|
6516
|
+
};
|
|
6517
|
+
} catch (err) {
|
|
6518
|
+
return {
|
|
6519
|
+
status: 500,
|
|
6520
|
+
json: {
|
|
6521
|
+
message: err instanceof Error ? err.message : "AI session messages error"
|
|
6522
|
+
}
|
|
6523
|
+
};
|
|
6524
|
+
}
|
|
6525
|
+
};
|
|
6526
|
+
if (serverOps instanceof ValOpsFS) {
|
|
6527
|
+
return execFetch(getProfileAuthHeaders(authData, null, "application/json"));
|
|
6528
|
+
}
|
|
6529
|
+
if (!options.valSecret) {
|
|
6530
|
+
return {
|
|
6531
|
+
status: 500,
|
|
6532
|
+
json: {
|
|
6533
|
+
message: "Secret is not configured"
|
|
6534
|
+
}
|
|
6535
|
+
};
|
|
6536
|
+
}
|
|
6537
|
+
return withAuth(options.valSecret, cookies, "ai/sessions/messages", data => {
|
|
6538
|
+
return execFetch(getProfileAuthHeaders(authData, data, "application/json"));
|
|
6539
|
+
});
|
|
6540
|
+
}
|
|
6541
|
+
},
|
|
6024
6542
|
//#region files
|
|
6025
6543
|
"/files": {
|
|
6026
6544
|
GET: async req => {
|
|
@@ -6242,6 +6760,21 @@ async function withAuth(secret, cookies, errorMessageType, handler) {
|
|
|
6242
6760
|
};
|
|
6243
6761
|
}
|
|
6244
6762
|
}
|
|
6763
|
+
function getProfileAuthHeaders(auth, data, type) {
|
|
6764
|
+
if ("pat" in auth) {
|
|
6765
|
+
return {
|
|
6766
|
+
"x-val-pat": auth.pat,
|
|
6767
|
+
"Content-Type": type
|
|
6768
|
+
};
|
|
6769
|
+
}
|
|
6770
|
+
if ("apiKey" in auth && data) {
|
|
6771
|
+
return {
|
|
6772
|
+
...getAuthHeaders(auth.apiKey, type),
|
|
6773
|
+
"x-val-profile-id": data.sub
|
|
6774
|
+
};
|
|
6775
|
+
}
|
|
6776
|
+
throw new Error("Invalid auth");
|
|
6777
|
+
}
|
|
6245
6778
|
function getAuthHeaders(token, type) {
|
|
6246
6779
|
if (!type) {
|
|
6247
6780
|
return {
|
|
@@ -6769,6 +7302,9 @@ class ValFSHost {
|
|
|
6769
7302
|
readFile(fileName) {
|
|
6770
7303
|
return this.valFS.readFile(fileName);
|
|
6771
7304
|
}
|
|
7305
|
+
readBuffer(fileName) {
|
|
7306
|
+
return this.valFS.readBuffer(fileName);
|
|
7307
|
+
}
|
|
6772
7308
|
realpath(path) {
|
|
6773
7309
|
return this.valFS.realpath(path);
|
|
6774
7310
|
}
|
|
@@ -7243,6 +7779,88 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
|
7243
7779
|
metadata: v.metadata
|
|
7244
7780
|
} : value
|
|
7245
7781
|
});
|
|
7782
|
+
} else if (fix === "images:check-all-files" || fix === "files:check-all-files") {
|
|
7783
|
+
if (!moduleSource || typeof moduleSource !== "object") {
|
|
7784
|
+
remainingErrors.push({
|
|
7785
|
+
...validationError,
|
|
7786
|
+
message: "Unexpected error while checking gallery metadata (no moduleSource)",
|
|
7787
|
+
fixes: undefined
|
|
7788
|
+
});
|
|
7789
|
+
continue;
|
|
7790
|
+
}
|
|
7791
|
+
const gallerySource = moduleSource;
|
|
7792
|
+
for (const [entryKey, storedEntry] of Object.entries(gallerySource)) {
|
|
7793
|
+
const filename = path__namespace["default"].join(config.projectRoot, entryKey);
|
|
7794
|
+
let buffer;
|
|
7795
|
+
try {
|
|
7796
|
+
buffer = fs__default["default"].readFileSync(filename);
|
|
7797
|
+
} catch {
|
|
7798
|
+
if (apply) {
|
|
7799
|
+
const removePath = patch.sourceToPatchPath(sourcePath).concat([entryKey]);
|
|
7800
|
+
if (patch.isNotRoot(removePath)) {
|
|
7801
|
+
patch$1.push({
|
|
7802
|
+
op: "remove",
|
|
7803
|
+
path: removePath
|
|
7804
|
+
});
|
|
7805
|
+
}
|
|
7806
|
+
} else {
|
|
7807
|
+
remainingErrors.push({
|
|
7808
|
+
...validationError,
|
|
7809
|
+
message: `Could not read file: ${filename} - file might not exist or can not be accessed`,
|
|
7810
|
+
fixes: undefined
|
|
7811
|
+
});
|
|
7812
|
+
}
|
|
7813
|
+
continue;
|
|
7814
|
+
}
|
|
7815
|
+
if (fix === "images:check-all-files") {
|
|
7816
|
+
const actualMetadata = await extractImageMetadata(filename, buffer);
|
|
7817
|
+
const stored = storedEntry;
|
|
7818
|
+
const metadataIsCorrect = stored.width === actualMetadata.width && stored.height === actualMetadata.height && stored.mimeType === actualMetadata.mimeType;
|
|
7819
|
+
if (!metadataIsCorrect) {
|
|
7820
|
+
if (apply) {
|
|
7821
|
+
patch$1.push({
|
|
7822
|
+
op: "replace",
|
|
7823
|
+
path: patch.sourceToPatchPath(sourcePath).concat([entryKey]),
|
|
7824
|
+
value: {
|
|
7825
|
+
...stored,
|
|
7826
|
+
width: actualMetadata.width ?? 0,
|
|
7827
|
+
height: actualMetadata.height ?? 0,
|
|
7828
|
+
mimeType: actualMetadata.mimeType ?? "application/octet-stream"
|
|
7829
|
+
}
|
|
7830
|
+
});
|
|
7831
|
+
} else {
|
|
7832
|
+
remainingErrors.push({
|
|
7833
|
+
...validationError,
|
|
7834
|
+
message: `Image metadata for '${entryKey}' is incorrect (width: ${stored.width ?? "<empty>"} vs ${actualMetadata.width}, height: ${stored.height ?? "<empty>"} vs ${actualMetadata.height}, mimeType: ${stored.mimeType ?? "<empty>"} vs ${actualMetadata.mimeType}). Use --fix to update.`
|
|
7835
|
+
});
|
|
7836
|
+
}
|
|
7837
|
+
}
|
|
7838
|
+
} else if (fix === "files:check-all-files") {
|
|
7839
|
+
const actualMetadata = await extractFileMetadata(filename);
|
|
7840
|
+
const stored = storedEntry;
|
|
7841
|
+
const metadataIsCorrect = stored.mimeType === actualMetadata.mimeType;
|
|
7842
|
+
if (!metadataIsCorrect) {
|
|
7843
|
+
if (apply) {
|
|
7844
|
+
patch$1.push({
|
|
7845
|
+
op: "replace",
|
|
7846
|
+
path: patch.sourceToPatchPath(sourcePath).concat([entryKey]),
|
|
7847
|
+
value: {
|
|
7848
|
+
...stored,
|
|
7849
|
+
mimeType: actualMetadata.mimeType ?? "application/octet-stream"
|
|
7850
|
+
}
|
|
7851
|
+
});
|
|
7852
|
+
} else {
|
|
7853
|
+
remainingErrors.push({
|
|
7854
|
+
...validationError,
|
|
7855
|
+
message: `File metadata for '${entryKey}' has incorrect mimeType: '${stored.mimeType ?? "<empty>"}' vs '${actualMetadata.mimeType}'. Use --fix to update.`
|
|
7856
|
+
});
|
|
7857
|
+
}
|
|
7858
|
+
}
|
|
7859
|
+
} else {
|
|
7860
|
+
const exhaustiveCheck = fix;
|
|
7861
|
+
throw new Error(`Internal error: unhandled fix type ${exhaustiveCheck}`);
|
|
7862
|
+
}
|
|
7863
|
+
}
|
|
7246
7864
|
} else if (fix === "file:check-remote" || fix === "image:check-remote") {
|
|
7247
7865
|
const v = getRemoteValueFromValidationError(validationError);
|
|
7248
7866
|
if (!v.success) {
|