@valbuild/server 0.48.1 → 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>>;
@@ -1422,12 +1422,12 @@ class ProxyValServer {
1422
1422
  "Content-Type": fetchRes.headers.get("Content-Type") || "",
1423
1423
  "Content-Length": fetchRes.headers.get("Content-Length") || "0"
1424
1424
  },
1425
- body: fetchRes.body
1425
+ json: fetchRes.body
1426
1426
  };
1427
1427
  } else {
1428
1428
  return {
1429
1429
  status: 500,
1430
- body: {
1430
+ json: {
1431
1431
  message: "No body in response"
1432
1432
  }
1433
1433
  };
@@ -1435,7 +1435,7 @@ class ProxyValServer {
1435
1435
  } else {
1436
1436
  return {
1437
1437
  status: fetchRes.status,
1438
- body: {
1438
+ json: {
1439
1439
  message: "Failed to get files"
1440
1440
  }
1441
1441
  };
@@ -1542,6 +1542,7 @@ class ProxyValServer {
1542
1542
  [internal.VAL_STATE_COOKIE]: {
1543
1543
  value: null
1544
1544
  },
1545
+ [internal.VAL_ENABLE_COOKIE_NAME]: ENABLE_COOKIE_VALUE,
1545
1546
  [internal.VAL_SESSION_COOKIE]: {
1546
1547
  value: cookie,
1547
1548
  options: {
@@ -1596,7 +1597,7 @@ class ProxyValServer {
1596
1597
  console.error(`Failed while processing: ${errorMessageType}`, err);
1597
1598
  return {
1598
1599
  status: 500,
1599
- body: {
1600
+ json: {
1600
1601
  message: err.message
1601
1602
  }
1602
1603
  };
@@ -1628,8 +1629,9 @@ class ProxyValServer {
1628
1629
  } else {
1629
1630
  return {
1630
1631
  status: fetchRes.status,
1631
- body: {
1632
- message: "Failed to get session"
1632
+ json: {
1633
+ message: "Failed to authorize",
1634
+ ...(await fetchRes.json())
1633
1635
  }
1634
1636
  };
1635
1637
  }
@@ -1646,7 +1648,7 @@ class ProxyValServer {
1646
1648
  if (!commit) {
1647
1649
  return {
1648
1650
  status: 400,
1649
- body: {
1651
+ json: {
1650
1652
  message: "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT."
1651
1653
  }
1652
1654
  };
@@ -1690,7 +1692,7 @@ class ProxyValServer {
1690
1692
  } catch (err) {
1691
1693
  return {
1692
1694
  status: 500,
1693
- body: {
1695
+ json: {
1694
1696
  message: "Failed to fetch: check network connection"
1695
1697
  }
1696
1698
  };
@@ -1705,7 +1707,7 @@ class ProxyValServer {
1705
1707
  if (!commit) {
1706
1708
  return {
1707
1709
  status: 400,
1708
- body: {
1710
+ json: {
1709
1711
  message: "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT."
1710
1712
  }
1711
1713
  };
@@ -1726,7 +1728,7 @@ class ProxyValServer {
1726
1728
  } else {
1727
1729
  return {
1728
1730
  status: fetchRes.status,
1729
- body: {
1731
+ json: {
1730
1732
  message: "Failed to get patches"
1731
1733
  }
1732
1734
  };
@@ -1754,7 +1756,7 @@ class ProxyValServer {
1754
1756
  if (!patchJSON.success) {
1755
1757
  return {
1756
1758
  status: 400,
1757
- body: {
1759
+ json: {
1758
1760
  message: "Invalid patch",
1759
1761
  details: patchJSON.error.issues
1760
1762
  }
@@ -2273,7 +2275,17 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
2273
2275
  const filename = path__namespace["default"].join(config.projectRoot, maybeRef);
2274
2276
  const buffer = fs__default["default"].readFileSync(filename);
2275
2277
  const imageSize = sizeOf__default["default"](buffer);
2276
- const mimeType = imageSize.type ? internal.imageTypeToMimeType(imageSize.type) : internal.filenameToMimeType(filename);
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
+ }
2277
2289
  if (!mimeType) {
2278
2290
  throw Error("Cannot determine mimetype of image");
2279
2291
  }
@@ -2294,6 +2306,26 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
2294
2306
  mimeType
2295
2307
  };
2296
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
+ }
2297
2329
  const remainingErrors = [];
2298
2330
  const patch$1 = [];
2299
2331
  for (const fix of validationError.fixes || []) {
@@ -2379,6 +2411,72 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
2379
2411
  }
2380
2412
  });
2381
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
+ }
2382
2480
  }
2383
2481
  }
2384
2482
  if (!validationError.fixes || validationError.fixes.length === 0) {
@@ -1422,12 +1422,12 @@ class ProxyValServer {
1422
1422
  "Content-Type": fetchRes.headers.get("Content-Type") || "",
1423
1423
  "Content-Length": fetchRes.headers.get("Content-Length") || "0"
1424
1424
  },
1425
- body: fetchRes.body
1425
+ json: fetchRes.body
1426
1426
  };
1427
1427
  } else {
1428
1428
  return {
1429
1429
  status: 500,
1430
- body: {
1430
+ json: {
1431
1431
  message: "No body in response"
1432
1432
  }
1433
1433
  };
@@ -1435,7 +1435,7 @@ class ProxyValServer {
1435
1435
  } else {
1436
1436
  return {
1437
1437
  status: fetchRes.status,
1438
- body: {
1438
+ json: {
1439
1439
  message: "Failed to get files"
1440
1440
  }
1441
1441
  };
@@ -1542,6 +1542,7 @@ class ProxyValServer {
1542
1542
  [internal.VAL_STATE_COOKIE]: {
1543
1543
  value: null
1544
1544
  },
1545
+ [internal.VAL_ENABLE_COOKIE_NAME]: ENABLE_COOKIE_VALUE,
1545
1546
  [internal.VAL_SESSION_COOKIE]: {
1546
1547
  value: cookie,
1547
1548
  options: {
@@ -1596,7 +1597,7 @@ class ProxyValServer {
1596
1597
  console.error(`Failed while processing: ${errorMessageType}`, err);
1597
1598
  return {
1598
1599
  status: 500,
1599
- body: {
1600
+ json: {
1600
1601
  message: err.message
1601
1602
  }
1602
1603
  };
@@ -1628,8 +1629,9 @@ class ProxyValServer {
1628
1629
  } else {
1629
1630
  return {
1630
1631
  status: fetchRes.status,
1631
- body: {
1632
- message: "Failed to get session"
1632
+ json: {
1633
+ message: "Failed to authorize",
1634
+ ...(await fetchRes.json())
1633
1635
  }
1634
1636
  };
1635
1637
  }
@@ -1646,7 +1648,7 @@ class ProxyValServer {
1646
1648
  if (!commit) {
1647
1649
  return {
1648
1650
  status: 400,
1649
- body: {
1651
+ json: {
1650
1652
  message: "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT."
1651
1653
  }
1652
1654
  };
@@ -1690,7 +1692,7 @@ class ProxyValServer {
1690
1692
  } catch (err) {
1691
1693
  return {
1692
1694
  status: 500,
1693
- body: {
1695
+ json: {
1694
1696
  message: "Failed to fetch: check network connection"
1695
1697
  }
1696
1698
  };
@@ -1705,7 +1707,7 @@ class ProxyValServer {
1705
1707
  if (!commit) {
1706
1708
  return {
1707
1709
  status: 400,
1708
- body: {
1710
+ json: {
1709
1711
  message: "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT."
1710
1712
  }
1711
1713
  };
@@ -1726,7 +1728,7 @@ class ProxyValServer {
1726
1728
  } else {
1727
1729
  return {
1728
1730
  status: fetchRes.status,
1729
- body: {
1731
+ json: {
1730
1732
  message: "Failed to get patches"
1731
1733
  }
1732
1734
  };
@@ -1754,7 +1756,7 @@ class ProxyValServer {
1754
1756
  if (!patchJSON.success) {
1755
1757
  return {
1756
1758
  status: 400,
1757
- body: {
1759
+ json: {
1758
1760
  message: "Invalid patch",
1759
1761
  details: patchJSON.error.issues
1760
1762
  }
@@ -2273,7 +2275,17 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
2273
2275
  const filename = path__namespace["default"].join(config.projectRoot, maybeRef);
2274
2276
  const buffer = fs__default["default"].readFileSync(filename);
2275
2277
  const imageSize = sizeOf__default["default"](buffer);
2276
- const mimeType = imageSize.type ? internal.imageTypeToMimeType(imageSize.type) : internal.filenameToMimeType(filename);
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
+ }
2277
2289
  if (!mimeType) {
2278
2290
  throw Error("Cannot determine mimetype of image");
2279
2291
  }
@@ -2294,6 +2306,26 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
2294
2306
  mimeType
2295
2307
  };
2296
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
+ }
2297
2329
  const remainingErrors = [];
2298
2330
  const patch$1 = [];
2299
2331
  for (const fix of validationError.fixes || []) {
@@ -2379,6 +2411,72 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
2379
2411
  }
2380
2412
  });
2381
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
+ }
2382
2480
  }
2383
2481
  }
2384
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, imageTypeToMimeType, filenameToMimeType } from '@valbuild/shared/internal';
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';
@@ -1392,12 +1392,12 @@ class ProxyValServer {
1392
1392
  "Content-Type": fetchRes.headers.get("Content-Type") || "",
1393
1393
  "Content-Length": fetchRes.headers.get("Content-Length") || "0"
1394
1394
  },
1395
- body: fetchRes.body
1395
+ json: fetchRes.body
1396
1396
  };
1397
1397
  } else {
1398
1398
  return {
1399
1399
  status: 500,
1400
- body: {
1400
+ json: {
1401
1401
  message: "No body in response"
1402
1402
  }
1403
1403
  };
@@ -1405,7 +1405,7 @@ class ProxyValServer {
1405
1405
  } else {
1406
1406
  return {
1407
1407
  status: fetchRes.status,
1408
- body: {
1408
+ json: {
1409
1409
  message: "Failed to get files"
1410
1410
  }
1411
1411
  };
@@ -1512,6 +1512,7 @@ class ProxyValServer {
1512
1512
  [VAL_STATE_COOKIE$1]: {
1513
1513
  value: null
1514
1514
  },
1515
+ [VAL_ENABLE_COOKIE_NAME]: ENABLE_COOKIE_VALUE,
1515
1516
  [VAL_SESSION_COOKIE$1]: {
1516
1517
  value: cookie,
1517
1518
  options: {
@@ -1566,7 +1567,7 @@ class ProxyValServer {
1566
1567
  console.error(`Failed while processing: ${errorMessageType}`, err);
1567
1568
  return {
1568
1569
  status: 500,
1569
- body: {
1570
+ json: {
1570
1571
  message: err.message
1571
1572
  }
1572
1573
  };
@@ -1598,8 +1599,9 @@ class ProxyValServer {
1598
1599
  } else {
1599
1600
  return {
1600
1601
  status: fetchRes.status,
1601
- body: {
1602
- message: "Failed to get session"
1602
+ json: {
1603
+ message: "Failed to authorize",
1604
+ ...(await fetchRes.json())
1603
1605
  }
1604
1606
  };
1605
1607
  }
@@ -1616,7 +1618,7 @@ class ProxyValServer {
1616
1618
  if (!commit) {
1617
1619
  return {
1618
1620
  status: 400,
1619
- body: {
1621
+ json: {
1620
1622
  message: "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT."
1621
1623
  }
1622
1624
  };
@@ -1660,7 +1662,7 @@ class ProxyValServer {
1660
1662
  } catch (err) {
1661
1663
  return {
1662
1664
  status: 500,
1663
- body: {
1665
+ json: {
1664
1666
  message: "Failed to fetch: check network connection"
1665
1667
  }
1666
1668
  };
@@ -1675,7 +1677,7 @@ class ProxyValServer {
1675
1677
  if (!commit) {
1676
1678
  return {
1677
1679
  status: 400,
1678
- body: {
1680
+ json: {
1679
1681
  message: "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT."
1680
1682
  }
1681
1683
  };
@@ -1696,7 +1698,7 @@ class ProxyValServer {
1696
1698
  } else {
1697
1699
  return {
1698
1700
  status: fetchRes.status,
1699
- body: {
1701
+ json: {
1700
1702
  message: "Failed to get patches"
1701
1703
  }
1702
1704
  };
@@ -1724,7 +1726,7 @@ class ProxyValServer {
1724
1726
  if (!patchJSON.success) {
1725
1727
  return {
1726
1728
  status: 400,
1727
- body: {
1729
+ json: {
1728
1730
  message: "Invalid patch",
1729
1731
  details: patchJSON.error.issues
1730
1732
  }
@@ -2243,7 +2245,17 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
2243
2245
  const filename = path__default.join(config.projectRoot, maybeRef);
2244
2246
  const buffer = fs.readFileSync(filename);
2245
2247
  const imageSize = sizeOf(buffer);
2246
- const mimeType = imageSize.type ? imageTypeToMimeType(imageSize.type) : filenameToMimeType(filename);
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
+ }
2247
2259
  if (!mimeType) {
2248
2260
  throw Error("Cannot determine mimetype of image");
2249
2261
  }
@@ -2264,6 +2276,26 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
2264
2276
  mimeType
2265
2277
  };
2266
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
+ }
2267
2299
  const remainingErrors = [];
2268
2300
  const patch = [];
2269
2301
  for (const fix of validationError.fixes || []) {
@@ -2349,6 +2381,72 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
2349
2381
  }
2350
2382
  });
2351
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
+ }
2352
2450
  }
2353
2451
  }
2354
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.48.1",
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.48.1",
28
- "@valbuild/shared": "~0.48.1",
29
- "@valbuild/ui": "~0.48.1",
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",