@valbuild/server 0.48.0 → 0.49.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.
@@ -31,7 +31,7 @@ export declare class LocalValServer implements ValServer {
|
|
31
31
|
private badRequest;
|
32
32
|
postCommit(): Promise<ValServerJsonResult<{}>>;
|
33
33
|
authorize(): Promise<ValServerRedirectResult<VAL_STATE_COOKIE>>;
|
34
|
-
callback(): Promise<ValServerRedirectResult<VAL_STATE_COOKIE | VAL_SESSION_COOKIE>>;
|
34
|
+
callback(): Promise<ValServerRedirectResult<VAL_STATE_COOKIE | VAL_SESSION_COOKIE | VAL_ENABLE_COOKIE_NAME>>;
|
35
35
|
logout(): Promise<ValServerResult<VAL_STATE_COOKIE | VAL_SESSION_COOKIE>>;
|
36
36
|
getFiles(): Promise<ValServerResult<never, ReadableStream<Uint8Array>>>;
|
37
37
|
getPatches(): Promise<ValServerJsonResult<ApiGetPatchResponse>>;
|
@@ -22,7 +22,7 @@ export interface ValServer {
|
|
22
22
|
callback(query: {
|
23
23
|
code?: string;
|
24
24
|
state?: string;
|
25
|
-
}, cookies: ValCookies<VAL_STATE_COOKIE>): Promise<ValServerRedirectResult<VAL_STATE_COOKIE | VAL_SESSION_COOKIE>>;
|
25
|
+
}, cookies: ValCookies<VAL_STATE_COOKIE>): Promise<ValServerRedirectResult<VAL_STATE_COOKIE | VAL_SESSION_COOKIE | VAL_ENABLE_COOKIE_NAME>>;
|
26
26
|
enable(query: {
|
27
27
|
redirect_to?: string;
|
28
28
|
}): Promise<ValServerRedirectResult<VAL_ENABLE_COOKIE_NAME>>;
|
@@ -731,19 +731,16 @@ globalThis.valModule = {
|
|
731
731
|
} else {
|
732
732
|
result.value.dispose();
|
733
733
|
const valModule = context.getProp(context.global, "valModule").consume(context.dump);
|
734
|
-
if (
|
735
|
-
|
736
|
-
|
734
|
+
if (
|
735
|
+
// if one of these are set it is a Val module, so must validate
|
736
|
+
(valModule === null || valModule === void 0 ? void 0 : valModule.id) !== undefined || (valModule === null || valModule === void 0 ? void 0 : valModule.schema) !== undefined || (valModule === null || valModule === void 0 ? void 0 : valModule.source) !== undefined) {
|
737
737
|
if (valModule.id !== id) {
|
738
738
|
fatalErrors.push(`Wrong val.content id! Expected: '${id}', found: '${valModule.id}'`);
|
739
|
-
}
|
740
|
-
if (encodeURIComponent(valModule.id).replace(/%2F/g, "/") !== valModule.id) {
|
739
|
+
} else if (encodeURIComponent(valModule.id).replace(/%2F/g, "/") !== valModule.id) {
|
741
740
|
fatalErrors.push(`Invalid val.content id! Must be a web-safe path without escape characters, found: '${valModule.id}', which was encoded as: '${encodeURIComponent(valModule.id).replace("%2F", "/")}'`);
|
742
|
-
}
|
743
|
-
if (!(valModule !== null && valModule !== void 0 && valModule.schema)) {
|
741
|
+
} else if ((valModule === null || valModule === void 0 ? void 0 : valModule.schema) === undefined) {
|
744
742
|
fatalErrors.push(`Expected val id: '${id}' to have a schema`);
|
745
|
-
}
|
746
|
-
if ((valModule === null || valModule === void 0 ? void 0 : valModule.source) === undefined) {
|
743
|
+
} else if ((valModule === null || valModule === void 0 ? void 0 : valModule.source) === undefined) {
|
747
744
|
fatalErrors.push(`Expected val id: '${id}' to have a source`);
|
748
745
|
}
|
749
746
|
}
|
@@ -1425,12 +1422,12 @@ class ProxyValServer {
|
|
1425
1422
|
"Content-Type": fetchRes.headers.get("Content-Type") || "",
|
1426
1423
|
"Content-Length": fetchRes.headers.get("Content-Length") || "0"
|
1427
1424
|
},
|
1428
|
-
|
1425
|
+
json: fetchRes.body
|
1429
1426
|
};
|
1430
1427
|
} else {
|
1431
1428
|
return {
|
1432
1429
|
status: 500,
|
1433
|
-
|
1430
|
+
json: {
|
1434
1431
|
message: "No body in response"
|
1435
1432
|
}
|
1436
1433
|
};
|
@@ -1438,7 +1435,7 @@ class ProxyValServer {
|
|
1438
1435
|
} else {
|
1439
1436
|
return {
|
1440
1437
|
status: fetchRes.status,
|
1441
|
-
|
1438
|
+
json: {
|
1442
1439
|
message: "Failed to get files"
|
1443
1440
|
}
|
1444
1441
|
};
|
@@ -1545,6 +1542,7 @@ class ProxyValServer {
|
|
1545
1542
|
[internal.VAL_STATE_COOKIE]: {
|
1546
1543
|
value: null
|
1547
1544
|
},
|
1545
|
+
[internal.VAL_ENABLE_COOKIE_NAME]: ENABLE_COOKIE_VALUE,
|
1548
1546
|
[internal.VAL_SESSION_COOKIE]: {
|
1549
1547
|
value: cookie,
|
1550
1548
|
options: {
|
@@ -1599,7 +1597,7 @@ class ProxyValServer {
|
|
1599
1597
|
console.error(`Failed while processing: ${errorMessageType}`, err);
|
1600
1598
|
return {
|
1601
1599
|
status: 500,
|
1602
|
-
|
1600
|
+
json: {
|
1603
1601
|
message: err.message
|
1604
1602
|
}
|
1605
1603
|
};
|
@@ -1631,8 +1629,9 @@ class ProxyValServer {
|
|
1631
1629
|
} else {
|
1632
1630
|
return {
|
1633
1631
|
status: fetchRes.status,
|
1634
|
-
|
1635
|
-
message: "Failed to
|
1632
|
+
json: {
|
1633
|
+
message: "Failed to authorize",
|
1634
|
+
...(await fetchRes.json())
|
1636
1635
|
}
|
1637
1636
|
};
|
1638
1637
|
}
|
@@ -1649,7 +1648,7 @@ class ProxyValServer {
|
|
1649
1648
|
if (!commit) {
|
1650
1649
|
return {
|
1651
1650
|
status: 400,
|
1652
|
-
|
1651
|
+
json: {
|
1653
1652
|
message: "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT."
|
1654
1653
|
}
|
1655
1654
|
};
|
@@ -1693,7 +1692,7 @@ class ProxyValServer {
|
|
1693
1692
|
} catch (err) {
|
1694
1693
|
return {
|
1695
1694
|
status: 500,
|
1696
|
-
|
1695
|
+
json: {
|
1697
1696
|
message: "Failed to fetch: check network connection"
|
1698
1697
|
}
|
1699
1698
|
};
|
@@ -1708,7 +1707,7 @@ class ProxyValServer {
|
|
1708
1707
|
if (!commit) {
|
1709
1708
|
return {
|
1710
1709
|
status: 400,
|
1711
|
-
|
1710
|
+
json: {
|
1712
1711
|
message: "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT."
|
1713
1712
|
}
|
1714
1713
|
};
|
@@ -1729,7 +1728,7 @@ class ProxyValServer {
|
|
1729
1728
|
} else {
|
1730
1729
|
return {
|
1731
1730
|
status: fetchRes.status,
|
1732
|
-
|
1731
|
+
json: {
|
1733
1732
|
message: "Failed to get patches"
|
1734
1733
|
}
|
1735
1734
|
};
|
@@ -1757,7 +1756,7 @@ class ProxyValServer {
|
|
1757
1756
|
if (!patchJSON.success) {
|
1758
1757
|
return {
|
1759
1758
|
status: 400,
|
1760
|
-
|
1759
|
+
json: {
|
1761
1760
|
message: "Invalid patch",
|
1762
1761
|
details: patchJSON.error.issues
|
1763
1762
|
}
|
@@ -2276,7 +2275,17 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
|
|
2276
2275
|
const filename = path__namespace["default"].join(config.projectRoot, maybeRef);
|
2277
2276
|
const buffer = fs__default["default"].readFileSync(filename);
|
2278
2277
|
const imageSize = sizeOf__default["default"](buffer);
|
2279
|
-
|
2278
|
+
let mimeType = null;
|
2279
|
+
if (imageSize.type) {
|
2280
|
+
const possibleMimeType = `image/${imageSize.type}`;
|
2281
|
+
if (internal.MIME_TYPES_TO_EXT[possibleMimeType]) {
|
2282
|
+
mimeType = possibleMimeType;
|
2283
|
+
}
|
2284
|
+
const filenameBasedLookup = internal.filenameToMimeType(filename);
|
2285
|
+
if (filenameBasedLookup) {
|
2286
|
+
mimeType = filenameBasedLookup;
|
2287
|
+
}
|
2288
|
+
}
|
2280
2289
|
if (!mimeType) {
|
2281
2290
|
throw Error("Cannot determine mimetype of image");
|
2282
2291
|
}
|
@@ -2297,6 +2306,26 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
|
|
2297
2306
|
mimeType
|
2298
2307
|
};
|
2299
2308
|
}
|
2309
|
+
async function getFileMetadata() {
|
2310
|
+
const maybeRef = validationError.value && typeof validationError.value === "object" && core.FILE_REF_PROP in validationError.value && typeof validationError.value[core.FILE_REF_PROP] === "string" ? validationError.value[core.FILE_REF_PROP] : undefined;
|
2311
|
+
if (!maybeRef) {
|
2312
|
+
// TODO:
|
2313
|
+
throw Error("Cannot fix image without a file reference");
|
2314
|
+
}
|
2315
|
+
const filename = path__namespace["default"].join(config.projectRoot, maybeRef);
|
2316
|
+
const buffer = fs__default["default"].readFileSync(filename);
|
2317
|
+
let mimeType = internal.filenameToMimeType(filename);
|
2318
|
+
if (!mimeType) {
|
2319
|
+
mimeType = "application/octet-stream";
|
2320
|
+
}
|
2321
|
+
const sha256 = core.Internal.getSHA256Hash(textEncoder.encode(
|
2322
|
+
// TODO: we should probably store the mimetype in the metadata and reuse it here
|
2323
|
+
`data:${mimeType};base64,${buffer.toString("base64")}`));
|
2324
|
+
return {
|
2325
|
+
sha256,
|
2326
|
+
mimeType
|
2327
|
+
};
|
2328
|
+
}
|
2300
2329
|
const remainingErrors = [];
|
2301
2330
|
const patch$1 = [];
|
2302
2331
|
for (const fix of validationError.fixes || []) {
|
@@ -2382,6 +2411,72 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
|
|
2382
2411
|
}
|
2383
2412
|
});
|
2384
2413
|
}
|
2414
|
+
} else if (fix === "file:add-metadata" || fix === "file:check-metadata") {
|
2415
|
+
const fileMetadata = await getFileMetadata();
|
2416
|
+
if (fileMetadata.sha256 === undefined) {
|
2417
|
+
remainingErrors.push({
|
2418
|
+
...validationError,
|
2419
|
+
message: "Failed to get image metadata",
|
2420
|
+
fixes: undefined
|
2421
|
+
});
|
2422
|
+
} else if (fix === "file:check-metadata") {
|
2423
|
+
const currentValue = validationError.value;
|
2424
|
+
const metadataIsCorrect =
|
2425
|
+
// metadata is a prop that is an object
|
2426
|
+
typeof currentValue === "object" && currentValue && "metadata" in currentValue && currentValue.metadata && typeof currentValue.metadata === "object" &&
|
2427
|
+
// sha256 is correct
|
2428
|
+
"sha256" in currentValue.metadata && currentValue.metadata.sha256 === fileMetadata.sha256 &&
|
2429
|
+
// mimeType is correct
|
2430
|
+
"mimeType" in currentValue.metadata && currentValue.metadata.mimeType === fileMetadata.mimeType;
|
2431
|
+
|
2432
|
+
// skips if the metadata is already correct
|
2433
|
+
if (!metadataIsCorrect) {
|
2434
|
+
if (apply) {
|
2435
|
+
patch$1.push({
|
2436
|
+
op: "replace",
|
2437
|
+
path: patch.sourceToPatchPath(sourcePath).concat("metadata"),
|
2438
|
+
value: {
|
2439
|
+
sha256: fileMetadata.sha256,
|
2440
|
+
...(fileMetadata.mimeType ? {
|
2441
|
+
mimeType: fileMetadata.mimeType
|
2442
|
+
} : {})
|
2443
|
+
}
|
2444
|
+
});
|
2445
|
+
} else {
|
2446
|
+
if (typeof currentValue === "object" && currentValue && "metadata" in currentValue && currentValue.metadata && typeof currentValue.metadata === "object") {
|
2447
|
+
if (!("sha256" in currentValue.metadata) || currentValue.metadata.sha256 !== fileMetadata.sha256) {
|
2448
|
+
remainingErrors.push({
|
2449
|
+
message: "File metadata sha256 is incorrect! Found: " + ("sha256" in currentValue.metadata ? currentValue.metadata.sha256 : "<empty>") + ". Expected: " + fileMetadata.sha256 + ".",
|
2450
|
+
fixes: undefined
|
2451
|
+
});
|
2452
|
+
}
|
2453
|
+
if (!("mimeType" in currentValue.metadata) || currentValue.metadata.mimeType !== fileMetadata.mimeType) {
|
2454
|
+
remainingErrors.push({
|
2455
|
+
message: "File metadata mimeType is incorrect! Found: " + ("mimeType" in currentValue.metadata ? currentValue.metadata.mimeType : "<empty>") + ". Expected: " + fileMetadata.mimeType,
|
2456
|
+
fixes: undefined
|
2457
|
+
});
|
2458
|
+
}
|
2459
|
+
} else {
|
2460
|
+
remainingErrors.push({
|
2461
|
+
...validationError,
|
2462
|
+
message: "Image metadata is not an object!",
|
2463
|
+
fixes: undefined
|
2464
|
+
});
|
2465
|
+
}
|
2466
|
+
}
|
2467
|
+
}
|
2468
|
+
} else if (fix === "file:add-metadata") {
|
2469
|
+
patch$1.push({
|
2470
|
+
op: "add",
|
2471
|
+
path: patch.sourceToPatchPath(sourcePath).concat("metadata"),
|
2472
|
+
value: {
|
2473
|
+
sha256: fileMetadata.sha256,
|
2474
|
+
...(fileMetadata.mimeType ? {
|
2475
|
+
mimeType: fileMetadata.mimeType
|
2476
|
+
} : {})
|
2477
|
+
}
|
2478
|
+
});
|
2479
|
+
}
|
2385
2480
|
}
|
2386
2481
|
}
|
2387
2482
|
if (!validationError.fixes || validationError.fixes.length === 0) {
|
@@ -731,19 +731,16 @@ globalThis.valModule = {
|
|
731
731
|
} else {
|
732
732
|
result.value.dispose();
|
733
733
|
const valModule = context.getProp(context.global, "valModule").consume(context.dump);
|
734
|
-
if (
|
735
|
-
|
736
|
-
|
734
|
+
if (
|
735
|
+
// if one of these are set it is a Val module, so must validate
|
736
|
+
(valModule === null || valModule === void 0 ? void 0 : valModule.id) !== undefined || (valModule === null || valModule === void 0 ? void 0 : valModule.schema) !== undefined || (valModule === null || valModule === void 0 ? void 0 : valModule.source) !== undefined) {
|
737
737
|
if (valModule.id !== id) {
|
738
738
|
fatalErrors.push(`Wrong val.content id! Expected: '${id}', found: '${valModule.id}'`);
|
739
|
-
}
|
740
|
-
if (encodeURIComponent(valModule.id).replace(/%2F/g, "/") !== valModule.id) {
|
739
|
+
} else if (encodeURIComponent(valModule.id).replace(/%2F/g, "/") !== valModule.id) {
|
741
740
|
fatalErrors.push(`Invalid val.content id! Must be a web-safe path without escape characters, found: '${valModule.id}', which was encoded as: '${encodeURIComponent(valModule.id).replace("%2F", "/")}'`);
|
742
|
-
}
|
743
|
-
if (!(valModule !== null && valModule !== void 0 && valModule.schema)) {
|
741
|
+
} else if ((valModule === null || valModule === void 0 ? void 0 : valModule.schema) === undefined) {
|
744
742
|
fatalErrors.push(`Expected val id: '${id}' to have a schema`);
|
745
|
-
}
|
746
|
-
if ((valModule === null || valModule === void 0 ? void 0 : valModule.source) === undefined) {
|
743
|
+
} else if ((valModule === null || valModule === void 0 ? void 0 : valModule.source) === undefined) {
|
747
744
|
fatalErrors.push(`Expected val id: '${id}' to have a source`);
|
748
745
|
}
|
749
746
|
}
|
@@ -1425,12 +1422,12 @@ class ProxyValServer {
|
|
1425
1422
|
"Content-Type": fetchRes.headers.get("Content-Type") || "",
|
1426
1423
|
"Content-Length": fetchRes.headers.get("Content-Length") || "0"
|
1427
1424
|
},
|
1428
|
-
|
1425
|
+
json: fetchRes.body
|
1429
1426
|
};
|
1430
1427
|
} else {
|
1431
1428
|
return {
|
1432
1429
|
status: 500,
|
1433
|
-
|
1430
|
+
json: {
|
1434
1431
|
message: "No body in response"
|
1435
1432
|
}
|
1436
1433
|
};
|
@@ -1438,7 +1435,7 @@ class ProxyValServer {
|
|
1438
1435
|
} else {
|
1439
1436
|
return {
|
1440
1437
|
status: fetchRes.status,
|
1441
|
-
|
1438
|
+
json: {
|
1442
1439
|
message: "Failed to get files"
|
1443
1440
|
}
|
1444
1441
|
};
|
@@ -1545,6 +1542,7 @@ class ProxyValServer {
|
|
1545
1542
|
[internal.VAL_STATE_COOKIE]: {
|
1546
1543
|
value: null
|
1547
1544
|
},
|
1545
|
+
[internal.VAL_ENABLE_COOKIE_NAME]: ENABLE_COOKIE_VALUE,
|
1548
1546
|
[internal.VAL_SESSION_COOKIE]: {
|
1549
1547
|
value: cookie,
|
1550
1548
|
options: {
|
@@ -1599,7 +1597,7 @@ class ProxyValServer {
|
|
1599
1597
|
console.error(`Failed while processing: ${errorMessageType}`, err);
|
1600
1598
|
return {
|
1601
1599
|
status: 500,
|
1602
|
-
|
1600
|
+
json: {
|
1603
1601
|
message: err.message
|
1604
1602
|
}
|
1605
1603
|
};
|
@@ -1631,8 +1629,9 @@ class ProxyValServer {
|
|
1631
1629
|
} else {
|
1632
1630
|
return {
|
1633
1631
|
status: fetchRes.status,
|
1634
|
-
|
1635
|
-
message: "Failed to
|
1632
|
+
json: {
|
1633
|
+
message: "Failed to authorize",
|
1634
|
+
...(await fetchRes.json())
|
1636
1635
|
}
|
1637
1636
|
};
|
1638
1637
|
}
|
@@ -1649,7 +1648,7 @@ class ProxyValServer {
|
|
1649
1648
|
if (!commit) {
|
1650
1649
|
return {
|
1651
1650
|
status: 400,
|
1652
|
-
|
1651
|
+
json: {
|
1653
1652
|
message: "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT."
|
1654
1653
|
}
|
1655
1654
|
};
|
@@ -1693,7 +1692,7 @@ class ProxyValServer {
|
|
1693
1692
|
} catch (err) {
|
1694
1693
|
return {
|
1695
1694
|
status: 500,
|
1696
|
-
|
1695
|
+
json: {
|
1697
1696
|
message: "Failed to fetch: check network connection"
|
1698
1697
|
}
|
1699
1698
|
};
|
@@ -1708,7 +1707,7 @@ class ProxyValServer {
|
|
1708
1707
|
if (!commit) {
|
1709
1708
|
return {
|
1710
1709
|
status: 400,
|
1711
|
-
|
1710
|
+
json: {
|
1712
1711
|
message: "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT."
|
1713
1712
|
}
|
1714
1713
|
};
|
@@ -1729,7 +1728,7 @@ class ProxyValServer {
|
|
1729
1728
|
} else {
|
1730
1729
|
return {
|
1731
1730
|
status: fetchRes.status,
|
1732
|
-
|
1731
|
+
json: {
|
1733
1732
|
message: "Failed to get patches"
|
1734
1733
|
}
|
1735
1734
|
};
|
@@ -1757,7 +1756,7 @@ class ProxyValServer {
|
|
1757
1756
|
if (!patchJSON.success) {
|
1758
1757
|
return {
|
1759
1758
|
status: 400,
|
1760
|
-
|
1759
|
+
json: {
|
1761
1760
|
message: "Invalid patch",
|
1762
1761
|
details: patchJSON.error.issues
|
1763
1762
|
}
|
@@ -2276,7 +2275,17 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
|
|
2276
2275
|
const filename = path__namespace["default"].join(config.projectRoot, maybeRef);
|
2277
2276
|
const buffer = fs__default["default"].readFileSync(filename);
|
2278
2277
|
const imageSize = sizeOf__default["default"](buffer);
|
2279
|
-
|
2278
|
+
let mimeType = null;
|
2279
|
+
if (imageSize.type) {
|
2280
|
+
const possibleMimeType = `image/${imageSize.type}`;
|
2281
|
+
if (internal.MIME_TYPES_TO_EXT[possibleMimeType]) {
|
2282
|
+
mimeType = possibleMimeType;
|
2283
|
+
}
|
2284
|
+
const filenameBasedLookup = internal.filenameToMimeType(filename);
|
2285
|
+
if (filenameBasedLookup) {
|
2286
|
+
mimeType = filenameBasedLookup;
|
2287
|
+
}
|
2288
|
+
}
|
2280
2289
|
if (!mimeType) {
|
2281
2290
|
throw Error("Cannot determine mimetype of image");
|
2282
2291
|
}
|
@@ -2297,6 +2306,26 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
|
|
2297
2306
|
mimeType
|
2298
2307
|
};
|
2299
2308
|
}
|
2309
|
+
async function getFileMetadata() {
|
2310
|
+
const maybeRef = validationError.value && typeof validationError.value === "object" && core.FILE_REF_PROP in validationError.value && typeof validationError.value[core.FILE_REF_PROP] === "string" ? validationError.value[core.FILE_REF_PROP] : undefined;
|
2311
|
+
if (!maybeRef) {
|
2312
|
+
// TODO:
|
2313
|
+
throw Error("Cannot fix image without a file reference");
|
2314
|
+
}
|
2315
|
+
const filename = path__namespace["default"].join(config.projectRoot, maybeRef);
|
2316
|
+
const buffer = fs__default["default"].readFileSync(filename);
|
2317
|
+
let mimeType = internal.filenameToMimeType(filename);
|
2318
|
+
if (!mimeType) {
|
2319
|
+
mimeType = "application/octet-stream";
|
2320
|
+
}
|
2321
|
+
const sha256 = core.Internal.getSHA256Hash(textEncoder.encode(
|
2322
|
+
// TODO: we should probably store the mimetype in the metadata and reuse it here
|
2323
|
+
`data:${mimeType};base64,${buffer.toString("base64")}`));
|
2324
|
+
return {
|
2325
|
+
sha256,
|
2326
|
+
mimeType
|
2327
|
+
};
|
2328
|
+
}
|
2300
2329
|
const remainingErrors = [];
|
2301
2330
|
const patch$1 = [];
|
2302
2331
|
for (const fix of validationError.fixes || []) {
|
@@ -2382,6 +2411,72 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
|
|
2382
2411
|
}
|
2383
2412
|
});
|
2384
2413
|
}
|
2414
|
+
} else if (fix === "file:add-metadata" || fix === "file:check-metadata") {
|
2415
|
+
const fileMetadata = await getFileMetadata();
|
2416
|
+
if (fileMetadata.sha256 === undefined) {
|
2417
|
+
remainingErrors.push({
|
2418
|
+
...validationError,
|
2419
|
+
message: "Failed to get image metadata",
|
2420
|
+
fixes: undefined
|
2421
|
+
});
|
2422
|
+
} else if (fix === "file:check-metadata") {
|
2423
|
+
const currentValue = validationError.value;
|
2424
|
+
const metadataIsCorrect =
|
2425
|
+
// metadata is a prop that is an object
|
2426
|
+
typeof currentValue === "object" && currentValue && "metadata" in currentValue && currentValue.metadata && typeof currentValue.metadata === "object" &&
|
2427
|
+
// sha256 is correct
|
2428
|
+
"sha256" in currentValue.metadata && currentValue.metadata.sha256 === fileMetadata.sha256 &&
|
2429
|
+
// mimeType is correct
|
2430
|
+
"mimeType" in currentValue.metadata && currentValue.metadata.mimeType === fileMetadata.mimeType;
|
2431
|
+
|
2432
|
+
// skips if the metadata is already correct
|
2433
|
+
if (!metadataIsCorrect) {
|
2434
|
+
if (apply) {
|
2435
|
+
patch$1.push({
|
2436
|
+
op: "replace",
|
2437
|
+
path: patch.sourceToPatchPath(sourcePath).concat("metadata"),
|
2438
|
+
value: {
|
2439
|
+
sha256: fileMetadata.sha256,
|
2440
|
+
...(fileMetadata.mimeType ? {
|
2441
|
+
mimeType: fileMetadata.mimeType
|
2442
|
+
} : {})
|
2443
|
+
}
|
2444
|
+
});
|
2445
|
+
} else {
|
2446
|
+
if (typeof currentValue === "object" && currentValue && "metadata" in currentValue && currentValue.metadata && typeof currentValue.metadata === "object") {
|
2447
|
+
if (!("sha256" in currentValue.metadata) || currentValue.metadata.sha256 !== fileMetadata.sha256) {
|
2448
|
+
remainingErrors.push({
|
2449
|
+
message: "File metadata sha256 is incorrect! Found: " + ("sha256" in currentValue.metadata ? currentValue.metadata.sha256 : "<empty>") + ". Expected: " + fileMetadata.sha256 + ".",
|
2450
|
+
fixes: undefined
|
2451
|
+
});
|
2452
|
+
}
|
2453
|
+
if (!("mimeType" in currentValue.metadata) || currentValue.metadata.mimeType !== fileMetadata.mimeType) {
|
2454
|
+
remainingErrors.push({
|
2455
|
+
message: "File metadata mimeType is incorrect! Found: " + ("mimeType" in currentValue.metadata ? currentValue.metadata.mimeType : "<empty>") + ". Expected: " + fileMetadata.mimeType,
|
2456
|
+
fixes: undefined
|
2457
|
+
});
|
2458
|
+
}
|
2459
|
+
} else {
|
2460
|
+
remainingErrors.push({
|
2461
|
+
...validationError,
|
2462
|
+
message: "Image metadata is not an object!",
|
2463
|
+
fixes: undefined
|
2464
|
+
});
|
2465
|
+
}
|
2466
|
+
}
|
2467
|
+
}
|
2468
|
+
} else if (fix === "file:add-metadata") {
|
2469
|
+
patch$1.push({
|
2470
|
+
op: "add",
|
2471
|
+
path: patch.sourceToPatchPath(sourcePath).concat("metadata"),
|
2472
|
+
value: {
|
2473
|
+
sha256: fileMetadata.sha256,
|
2474
|
+
...(fileMetadata.mimeType ? {
|
2475
|
+
mimeType: fileMetadata.mimeType
|
2476
|
+
} : {})
|
2477
|
+
}
|
2478
|
+
});
|
2479
|
+
}
|
2385
2480
|
}
|
2386
2481
|
}
|
2387
2482
|
if (!validationError.fixes || validationError.fixes.length === 0) {
|
@@ -8,7 +8,7 @@ import path__default from 'path';
|
|
8
8
|
import fs, { promises } from 'fs';
|
9
9
|
import { transform } from 'sucrase';
|
10
10
|
import z, { z as z$1 } from 'zod';
|
11
|
-
import { VAL_ENABLE_COOKIE_NAME, VAL_STATE_COOKIE as VAL_STATE_COOKIE$1, VAL_SESSION_COOKIE as VAL_SESSION_COOKIE$1,
|
11
|
+
import { VAL_ENABLE_COOKIE_NAME, VAL_STATE_COOKIE as VAL_STATE_COOKIE$1, VAL_SESSION_COOKIE as VAL_SESSION_COOKIE$1, MIME_TYPES_TO_EXT, filenameToMimeType } from '@valbuild/shared/internal';
|
12
12
|
import crypto from 'crypto';
|
13
13
|
import { createUIRequestHandler } from '@valbuild/ui/server';
|
14
14
|
import sizeOf from 'image-size';
|
@@ -701,19 +701,16 @@ globalThis.valModule = {
|
|
701
701
|
} else {
|
702
702
|
result.value.dispose();
|
703
703
|
const valModule = context.getProp(context.global, "valModule").consume(context.dump);
|
704
|
-
if (
|
705
|
-
|
706
|
-
|
704
|
+
if (
|
705
|
+
// if one of these are set it is a Val module, so must validate
|
706
|
+
(valModule === null || valModule === void 0 ? void 0 : valModule.id) !== undefined || (valModule === null || valModule === void 0 ? void 0 : valModule.schema) !== undefined || (valModule === null || valModule === void 0 ? void 0 : valModule.source) !== undefined) {
|
707
707
|
if (valModule.id !== id) {
|
708
708
|
fatalErrors.push(`Wrong val.content id! Expected: '${id}', found: '${valModule.id}'`);
|
709
|
-
}
|
710
|
-
if (encodeURIComponent(valModule.id).replace(/%2F/g, "/") !== valModule.id) {
|
709
|
+
} else if (encodeURIComponent(valModule.id).replace(/%2F/g, "/") !== valModule.id) {
|
711
710
|
fatalErrors.push(`Invalid val.content id! Must be a web-safe path without escape characters, found: '${valModule.id}', which was encoded as: '${encodeURIComponent(valModule.id).replace("%2F", "/")}'`);
|
712
|
-
}
|
713
|
-
if (!(valModule !== null && valModule !== void 0 && valModule.schema)) {
|
711
|
+
} else if ((valModule === null || valModule === void 0 ? void 0 : valModule.schema) === undefined) {
|
714
712
|
fatalErrors.push(`Expected val id: '${id}' to have a schema`);
|
715
|
-
}
|
716
|
-
if ((valModule === null || valModule === void 0 ? void 0 : valModule.source) === undefined) {
|
713
|
+
} else if ((valModule === null || valModule === void 0 ? void 0 : valModule.source) === undefined) {
|
717
714
|
fatalErrors.push(`Expected val id: '${id}' to have a source`);
|
718
715
|
}
|
719
716
|
}
|
@@ -1395,12 +1392,12 @@ class ProxyValServer {
|
|
1395
1392
|
"Content-Type": fetchRes.headers.get("Content-Type") || "",
|
1396
1393
|
"Content-Length": fetchRes.headers.get("Content-Length") || "0"
|
1397
1394
|
},
|
1398
|
-
|
1395
|
+
json: fetchRes.body
|
1399
1396
|
};
|
1400
1397
|
} else {
|
1401
1398
|
return {
|
1402
1399
|
status: 500,
|
1403
|
-
|
1400
|
+
json: {
|
1404
1401
|
message: "No body in response"
|
1405
1402
|
}
|
1406
1403
|
};
|
@@ -1408,7 +1405,7 @@ class ProxyValServer {
|
|
1408
1405
|
} else {
|
1409
1406
|
return {
|
1410
1407
|
status: fetchRes.status,
|
1411
|
-
|
1408
|
+
json: {
|
1412
1409
|
message: "Failed to get files"
|
1413
1410
|
}
|
1414
1411
|
};
|
@@ -1515,6 +1512,7 @@ class ProxyValServer {
|
|
1515
1512
|
[VAL_STATE_COOKIE$1]: {
|
1516
1513
|
value: null
|
1517
1514
|
},
|
1515
|
+
[VAL_ENABLE_COOKIE_NAME]: ENABLE_COOKIE_VALUE,
|
1518
1516
|
[VAL_SESSION_COOKIE$1]: {
|
1519
1517
|
value: cookie,
|
1520
1518
|
options: {
|
@@ -1569,7 +1567,7 @@ class ProxyValServer {
|
|
1569
1567
|
console.error(`Failed while processing: ${errorMessageType}`, err);
|
1570
1568
|
return {
|
1571
1569
|
status: 500,
|
1572
|
-
|
1570
|
+
json: {
|
1573
1571
|
message: err.message
|
1574
1572
|
}
|
1575
1573
|
};
|
@@ -1601,8 +1599,9 @@ class ProxyValServer {
|
|
1601
1599
|
} else {
|
1602
1600
|
return {
|
1603
1601
|
status: fetchRes.status,
|
1604
|
-
|
1605
|
-
message: "Failed to
|
1602
|
+
json: {
|
1603
|
+
message: "Failed to authorize",
|
1604
|
+
...(await fetchRes.json())
|
1606
1605
|
}
|
1607
1606
|
};
|
1608
1607
|
}
|
@@ -1619,7 +1618,7 @@ class ProxyValServer {
|
|
1619
1618
|
if (!commit) {
|
1620
1619
|
return {
|
1621
1620
|
status: 400,
|
1622
|
-
|
1621
|
+
json: {
|
1623
1622
|
message: "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT."
|
1624
1623
|
}
|
1625
1624
|
};
|
@@ -1663,7 +1662,7 @@ class ProxyValServer {
|
|
1663
1662
|
} catch (err) {
|
1664
1663
|
return {
|
1665
1664
|
status: 500,
|
1666
|
-
|
1665
|
+
json: {
|
1667
1666
|
message: "Failed to fetch: check network connection"
|
1668
1667
|
}
|
1669
1668
|
};
|
@@ -1678,7 +1677,7 @@ class ProxyValServer {
|
|
1678
1677
|
if (!commit) {
|
1679
1678
|
return {
|
1680
1679
|
status: 400,
|
1681
|
-
|
1680
|
+
json: {
|
1682
1681
|
message: "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT."
|
1683
1682
|
}
|
1684
1683
|
};
|
@@ -1699,7 +1698,7 @@ class ProxyValServer {
|
|
1699
1698
|
} else {
|
1700
1699
|
return {
|
1701
1700
|
status: fetchRes.status,
|
1702
|
-
|
1701
|
+
json: {
|
1703
1702
|
message: "Failed to get patches"
|
1704
1703
|
}
|
1705
1704
|
};
|
@@ -1727,7 +1726,7 @@ class ProxyValServer {
|
|
1727
1726
|
if (!patchJSON.success) {
|
1728
1727
|
return {
|
1729
1728
|
status: 400,
|
1730
|
-
|
1729
|
+
json: {
|
1731
1730
|
message: "Invalid patch",
|
1732
1731
|
details: patchJSON.error.issues
|
1733
1732
|
}
|
@@ -2246,7 +2245,17 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
|
|
2246
2245
|
const filename = path__default.join(config.projectRoot, maybeRef);
|
2247
2246
|
const buffer = fs.readFileSync(filename);
|
2248
2247
|
const imageSize = sizeOf(buffer);
|
2249
|
-
|
2248
|
+
let mimeType = null;
|
2249
|
+
if (imageSize.type) {
|
2250
|
+
const possibleMimeType = `image/${imageSize.type}`;
|
2251
|
+
if (MIME_TYPES_TO_EXT[possibleMimeType]) {
|
2252
|
+
mimeType = possibleMimeType;
|
2253
|
+
}
|
2254
|
+
const filenameBasedLookup = filenameToMimeType(filename);
|
2255
|
+
if (filenameBasedLookup) {
|
2256
|
+
mimeType = filenameBasedLookup;
|
2257
|
+
}
|
2258
|
+
}
|
2250
2259
|
if (!mimeType) {
|
2251
2260
|
throw Error("Cannot determine mimetype of image");
|
2252
2261
|
}
|
@@ -2267,6 +2276,26 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
|
|
2267
2276
|
mimeType
|
2268
2277
|
};
|
2269
2278
|
}
|
2279
|
+
async function getFileMetadata() {
|
2280
|
+
const maybeRef = validationError.value && typeof validationError.value === "object" && FILE_REF_PROP in validationError.value && typeof validationError.value[FILE_REF_PROP] === "string" ? validationError.value[FILE_REF_PROP] : undefined;
|
2281
|
+
if (!maybeRef) {
|
2282
|
+
// TODO:
|
2283
|
+
throw Error("Cannot fix image without a file reference");
|
2284
|
+
}
|
2285
|
+
const filename = path__default.join(config.projectRoot, maybeRef);
|
2286
|
+
const buffer = fs.readFileSync(filename);
|
2287
|
+
let mimeType = filenameToMimeType(filename);
|
2288
|
+
if (!mimeType) {
|
2289
|
+
mimeType = "application/octet-stream";
|
2290
|
+
}
|
2291
|
+
const sha256 = Internal.getSHA256Hash(textEncoder.encode(
|
2292
|
+
// TODO: we should probably store the mimetype in the metadata and reuse it here
|
2293
|
+
`data:${mimeType};base64,${buffer.toString("base64")}`));
|
2294
|
+
return {
|
2295
|
+
sha256,
|
2296
|
+
mimeType
|
2297
|
+
};
|
2298
|
+
}
|
2270
2299
|
const remainingErrors = [];
|
2271
2300
|
const patch = [];
|
2272
2301
|
for (const fix of validationError.fixes || []) {
|
@@ -2352,6 +2381,72 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
|
|
2352
2381
|
}
|
2353
2382
|
});
|
2354
2383
|
}
|
2384
|
+
} else if (fix === "file:add-metadata" || fix === "file:check-metadata") {
|
2385
|
+
const fileMetadata = await getFileMetadata();
|
2386
|
+
if (fileMetadata.sha256 === undefined) {
|
2387
|
+
remainingErrors.push({
|
2388
|
+
...validationError,
|
2389
|
+
message: "Failed to get image metadata",
|
2390
|
+
fixes: undefined
|
2391
|
+
});
|
2392
|
+
} else if (fix === "file:check-metadata") {
|
2393
|
+
const currentValue = validationError.value;
|
2394
|
+
const metadataIsCorrect =
|
2395
|
+
// metadata is a prop that is an object
|
2396
|
+
typeof currentValue === "object" && currentValue && "metadata" in currentValue && currentValue.metadata && typeof currentValue.metadata === "object" &&
|
2397
|
+
// sha256 is correct
|
2398
|
+
"sha256" in currentValue.metadata && currentValue.metadata.sha256 === fileMetadata.sha256 &&
|
2399
|
+
// mimeType is correct
|
2400
|
+
"mimeType" in currentValue.metadata && currentValue.metadata.mimeType === fileMetadata.mimeType;
|
2401
|
+
|
2402
|
+
// skips if the metadata is already correct
|
2403
|
+
if (!metadataIsCorrect) {
|
2404
|
+
if (apply) {
|
2405
|
+
patch.push({
|
2406
|
+
op: "replace",
|
2407
|
+
path: sourceToPatchPath(sourcePath).concat("metadata"),
|
2408
|
+
value: {
|
2409
|
+
sha256: fileMetadata.sha256,
|
2410
|
+
...(fileMetadata.mimeType ? {
|
2411
|
+
mimeType: fileMetadata.mimeType
|
2412
|
+
} : {})
|
2413
|
+
}
|
2414
|
+
});
|
2415
|
+
} else {
|
2416
|
+
if (typeof currentValue === "object" && currentValue && "metadata" in currentValue && currentValue.metadata && typeof currentValue.metadata === "object") {
|
2417
|
+
if (!("sha256" in currentValue.metadata) || currentValue.metadata.sha256 !== fileMetadata.sha256) {
|
2418
|
+
remainingErrors.push({
|
2419
|
+
message: "File metadata sha256 is incorrect! Found: " + ("sha256" in currentValue.metadata ? currentValue.metadata.sha256 : "<empty>") + ". Expected: " + fileMetadata.sha256 + ".",
|
2420
|
+
fixes: undefined
|
2421
|
+
});
|
2422
|
+
}
|
2423
|
+
if (!("mimeType" in currentValue.metadata) || currentValue.metadata.mimeType !== fileMetadata.mimeType) {
|
2424
|
+
remainingErrors.push({
|
2425
|
+
message: "File metadata mimeType is incorrect! Found: " + ("mimeType" in currentValue.metadata ? currentValue.metadata.mimeType : "<empty>") + ". Expected: " + fileMetadata.mimeType,
|
2426
|
+
fixes: undefined
|
2427
|
+
});
|
2428
|
+
}
|
2429
|
+
} else {
|
2430
|
+
remainingErrors.push({
|
2431
|
+
...validationError,
|
2432
|
+
message: "Image metadata is not an object!",
|
2433
|
+
fixes: undefined
|
2434
|
+
});
|
2435
|
+
}
|
2436
|
+
}
|
2437
|
+
}
|
2438
|
+
} else if (fix === "file:add-metadata") {
|
2439
|
+
patch.push({
|
2440
|
+
op: "add",
|
2441
|
+
path: sourceToPatchPath(sourcePath).concat("metadata"),
|
2442
|
+
value: {
|
2443
|
+
sha256: fileMetadata.sha256,
|
2444
|
+
...(fileMetadata.mimeType ? {
|
2445
|
+
mimeType: fileMetadata.mimeType
|
2446
|
+
} : {})
|
2447
|
+
}
|
2448
|
+
});
|
2449
|
+
}
|
2355
2450
|
}
|
2356
2451
|
}
|
2357
2452
|
if (!validationError.fixes || validationError.fixes.length === 0) {
|
package/package.json
CHANGED
@@ -12,7 +12,7 @@
|
|
12
12
|
"./package.json": "./package.json"
|
13
13
|
},
|
14
14
|
"types": "dist/valbuild-server.cjs.d.ts",
|
15
|
-
"version": "0.
|
15
|
+
"version": "0.49.0",
|
16
16
|
"scripts": {
|
17
17
|
"typecheck": "tsc --noEmit",
|
18
18
|
"test": "jest",
|
@@ -24,9 +24,9 @@
|
|
24
24
|
"concurrently": "^7.6.0"
|
25
25
|
},
|
26
26
|
"dependencies": {
|
27
|
-
"@valbuild/core": "~0.
|
28
|
-
"@valbuild/shared": "~0.
|
29
|
-
"@valbuild/ui": "~0.
|
27
|
+
"@valbuild/core": "~0.49.0",
|
28
|
+
"@valbuild/shared": "~0.49.0",
|
29
|
+
"@valbuild/ui": "~0.49.0",
|
30
30
|
"express": "^4.18.2",
|
31
31
|
"image-size": "^1.0.2",
|
32
32
|
"queue": "^6.0.2",
|