@valbuild/server 0.48.1 → 0.50.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>>;
@@ -320,7 +320,6 @@ const printer = ts__default["default"].createPrinter({
320
320
  newLine: newLine
321
321
  // neverAsciiEscape: true,
322
322
  });
323
-
324
323
  function replaceNodeValue(document, node, value) {
325
324
  const replacementText = printer.printNode(ts__default["default"].EmitHint.Unspecified, toExpression(value), document);
326
325
  const span = ts__default["default"].createTextSpanFromBounds(node.getStart(document, false), node.end);
@@ -663,7 +662,6 @@ const patchValFile = async (id, valConfigPath, patch$1, sourceFileHandler, runti
663
662
  sourceFileHandler.writeSourceFile(newSourceFile.value);
664
663
  // console.timeEnd("patchValFile" + timeId);
665
664
  };
666
-
667
665
  function convertDataUrlToBase64(dataUrl) {
668
666
  const base64 = dataUrl.slice(dataUrl.indexOf(",") + 1);
669
667
  return Buffer.from(base64, "base64");
@@ -888,7 +886,6 @@ class ValModuleLoader {
888
886
  }
889
887
  }
890
888
  }
891
-
892
889
  return compiledCode;
893
890
  }
894
891
  resolveModulePath(containingFilePath, requestedModuleName) {
@@ -1218,7 +1215,7 @@ class LocalValServer {
1218
1215
  if (treePath && !path__namespace["default"].join(dir, file).replace(rootDir, "").startsWith(treePath)) {
1219
1216
  continue;
1220
1217
  }
1221
- moduleIds.push(path__namespace["default"].join(dir, file).replace(rootDir, "").replace(".val.js", "").replace(".val.ts", ""));
1218
+ moduleIds.push(path__namespace["default"].join(dir, file).replace(rootDir, "").replace(".val.js", "").replace(".val.ts", "").split(path__namespace["default"].sep).join("/"));
1222
1219
  }
1223
1220
  }
1224
1221
  };
@@ -1308,7 +1305,6 @@ class LocalValServer {
1308
1305
  json: {} // no patch ids created
1309
1306
  };
1310
1307
  }
1311
-
1312
1308
  badRequest() {
1313
1309
  return {
1314
1310
  status: 400,
@@ -1382,7 +1378,6 @@ function decodeJwt(token, secretKey) {
1382
1378
  function getExpire() {
1383
1379
  return Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 4; // 4 days
1384
1380
  }
1385
-
1386
1381
  const JwtHeaderSchema = z.z.object({
1387
1382
  alg: z.z.literal("HS256"),
1388
1383
  typ: z.z.literal("JWT")
@@ -1422,12 +1417,12 @@ class ProxyValServer {
1422
1417
  "Content-Type": fetchRes.headers.get("Content-Type") || "",
1423
1418
  "Content-Length": fetchRes.headers.get("Content-Length") || "0"
1424
1419
  },
1425
- body: fetchRes.body
1420
+ json: fetchRes.body
1426
1421
  };
1427
1422
  } else {
1428
1423
  return {
1429
1424
  status: 500,
1430
- body: {
1425
+ json: {
1431
1426
  message: "No body in response"
1432
1427
  }
1433
1428
  };
@@ -1435,7 +1430,7 @@ class ProxyValServer {
1435
1430
  } else {
1436
1431
  return {
1437
1432
  status: fetchRes.status,
1438
- body: {
1433
+ json: {
1439
1434
  message: "Failed to get files"
1440
1435
  }
1441
1436
  };
@@ -1468,7 +1463,6 @@ class ProxyValServer {
1468
1463
  }
1469
1464
  }
1470
1465
  },
1471
-
1472
1466
  status: 302,
1473
1467
  redirectTo: appAuthorizeUrl
1474
1468
  };
@@ -1542,6 +1536,7 @@ class ProxyValServer {
1542
1536
  [internal.VAL_STATE_COOKIE]: {
1543
1537
  value: null
1544
1538
  },
1539
+ [internal.VAL_ENABLE_COOKIE_NAME]: ENABLE_COOKIE_VALUE,
1545
1540
  [internal.VAL_SESSION_COOKIE]: {
1546
1541
  value: cookie,
1547
1542
  options: {
@@ -1553,7 +1548,6 @@ class ProxyValServer {
1553
1548
  }
1554
1549
  }
1555
1550
  },
1556
-
1557
1551
  redirectTo: callbackReqSuccess.redirect_uri || "/"
1558
1552
  };
1559
1553
  }
@@ -1596,7 +1590,7 @@ class ProxyValServer {
1596
1590
  console.error(`Failed while processing: ${errorMessageType}`, err);
1597
1591
  return {
1598
1592
  status: 500,
1599
- body: {
1593
+ json: {
1600
1594
  message: err.message
1601
1595
  }
1602
1596
  };
@@ -1628,8 +1622,9 @@ class ProxyValServer {
1628
1622
  } else {
1629
1623
  return {
1630
1624
  status: fetchRes.status,
1631
- body: {
1632
- message: "Failed to get session"
1625
+ json: {
1626
+ message: "Failed to authorize",
1627
+ ...(await fetchRes.json())
1633
1628
  }
1634
1629
  };
1635
1630
  }
@@ -1646,7 +1641,7 @@ class ProxyValServer {
1646
1641
  if (!commit) {
1647
1642
  return {
1648
1643
  status: 400,
1649
- body: {
1644
+ json: {
1650
1645
  message: "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT."
1651
1646
  }
1652
1647
  };
@@ -1690,7 +1685,7 @@ class ProxyValServer {
1690
1685
  } catch (err) {
1691
1686
  return {
1692
1687
  status: 500,
1693
- body: {
1688
+ json: {
1694
1689
  message: "Failed to fetch: check network connection"
1695
1690
  }
1696
1691
  };
@@ -1705,7 +1700,7 @@ class ProxyValServer {
1705
1700
  if (!commit) {
1706
1701
  return {
1707
1702
  status: 400,
1708
- body: {
1703
+ json: {
1709
1704
  message: "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT."
1710
1705
  }
1711
1706
  };
@@ -1726,7 +1721,7 @@ class ProxyValServer {
1726
1721
  } else {
1727
1722
  return {
1728
1723
  status: fetchRes.status,
1729
- body: {
1724
+ json: {
1730
1725
  message: "Failed to get patches"
1731
1726
  }
1732
1727
  };
@@ -1754,7 +1749,7 @@ class ProxyValServer {
1754
1749
  if (!patchJSON.success) {
1755
1750
  return {
1756
1751
  status: 400,
1757
- body: {
1752
+ json: {
1758
1753
  message: "Invalid patch",
1759
1754
  details: patchJSON.error.issues
1760
1755
  }
@@ -2273,7 +2268,17 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
2273
2268
  const filename = path__namespace["default"].join(config.projectRoot, maybeRef);
2274
2269
  const buffer = fs__default["default"].readFileSync(filename);
2275
2270
  const imageSize = sizeOf__default["default"](buffer);
2276
- const mimeType = imageSize.type ? internal.imageTypeToMimeType(imageSize.type) : internal.filenameToMimeType(filename);
2271
+ let mimeType = null;
2272
+ if (imageSize.type) {
2273
+ const possibleMimeType = `image/${imageSize.type}`;
2274
+ if (internal.MIME_TYPES_TO_EXT[possibleMimeType]) {
2275
+ mimeType = possibleMimeType;
2276
+ }
2277
+ const filenameBasedLookup = internal.filenameToMimeType(filename);
2278
+ if (filenameBasedLookup) {
2279
+ mimeType = filenameBasedLookup;
2280
+ }
2281
+ }
2277
2282
  if (!mimeType) {
2278
2283
  throw Error("Cannot determine mimetype of image");
2279
2284
  }
@@ -2294,6 +2299,26 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
2294
2299
  mimeType
2295
2300
  };
2296
2301
  }
2302
+ async function getFileMetadata() {
2303
+ 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;
2304
+ if (!maybeRef) {
2305
+ // TODO:
2306
+ throw Error("Cannot fix image without a file reference");
2307
+ }
2308
+ const filename = path__namespace["default"].join(config.projectRoot, maybeRef);
2309
+ const buffer = fs__default["default"].readFileSync(filename);
2310
+ let mimeType = internal.filenameToMimeType(filename);
2311
+ if (!mimeType) {
2312
+ mimeType = "application/octet-stream";
2313
+ }
2314
+ const sha256 = core.Internal.getSHA256Hash(textEncoder.encode(
2315
+ // TODO: we should probably store the mimetype in the metadata and reuse it here
2316
+ `data:${mimeType};base64,${buffer.toString("base64")}`));
2317
+ return {
2318
+ sha256,
2319
+ mimeType
2320
+ };
2321
+ }
2297
2322
  const remainingErrors = [];
2298
2323
  const patch$1 = [];
2299
2324
  for (const fix of validationError.fixes || []) {
@@ -2379,6 +2404,72 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
2379
2404
  }
2380
2405
  });
2381
2406
  }
2407
+ } else if (fix === "file:add-metadata" || fix === "file:check-metadata") {
2408
+ const fileMetadata = await getFileMetadata();
2409
+ if (fileMetadata.sha256 === undefined) {
2410
+ remainingErrors.push({
2411
+ ...validationError,
2412
+ message: "Failed to get image metadata",
2413
+ fixes: undefined
2414
+ });
2415
+ } else if (fix === "file:check-metadata") {
2416
+ const currentValue = validationError.value;
2417
+ const metadataIsCorrect =
2418
+ // metadata is a prop that is an object
2419
+ typeof currentValue === "object" && currentValue && "metadata" in currentValue && currentValue.metadata && typeof currentValue.metadata === "object" &&
2420
+ // sha256 is correct
2421
+ "sha256" in currentValue.metadata && currentValue.metadata.sha256 === fileMetadata.sha256 &&
2422
+ // mimeType is correct
2423
+ "mimeType" in currentValue.metadata && currentValue.metadata.mimeType === fileMetadata.mimeType;
2424
+
2425
+ // skips if the metadata is already correct
2426
+ if (!metadataIsCorrect) {
2427
+ if (apply) {
2428
+ patch$1.push({
2429
+ op: "replace",
2430
+ path: patch.sourceToPatchPath(sourcePath).concat("metadata"),
2431
+ value: {
2432
+ sha256: fileMetadata.sha256,
2433
+ ...(fileMetadata.mimeType ? {
2434
+ mimeType: fileMetadata.mimeType
2435
+ } : {})
2436
+ }
2437
+ });
2438
+ } else {
2439
+ if (typeof currentValue === "object" && currentValue && "metadata" in currentValue && currentValue.metadata && typeof currentValue.metadata === "object") {
2440
+ if (!("sha256" in currentValue.metadata) || currentValue.metadata.sha256 !== fileMetadata.sha256) {
2441
+ remainingErrors.push({
2442
+ message: "File metadata sha256 is incorrect! Found: " + ("sha256" in currentValue.metadata ? currentValue.metadata.sha256 : "<empty>") + ". Expected: " + fileMetadata.sha256 + ".",
2443
+ fixes: undefined
2444
+ });
2445
+ }
2446
+ if (!("mimeType" in currentValue.metadata) || currentValue.metadata.mimeType !== fileMetadata.mimeType) {
2447
+ remainingErrors.push({
2448
+ message: "File metadata mimeType is incorrect! Found: " + ("mimeType" in currentValue.metadata ? currentValue.metadata.mimeType : "<empty>") + ". Expected: " + fileMetadata.mimeType,
2449
+ fixes: undefined
2450
+ });
2451
+ }
2452
+ } else {
2453
+ remainingErrors.push({
2454
+ ...validationError,
2455
+ message: "Image metadata is not an object!",
2456
+ fixes: undefined
2457
+ });
2458
+ }
2459
+ }
2460
+ }
2461
+ } else if (fix === "file:add-metadata") {
2462
+ patch$1.push({
2463
+ op: "add",
2464
+ path: patch.sourceToPatchPath(sourcePath).concat("metadata"),
2465
+ value: {
2466
+ sha256: fileMetadata.sha256,
2467
+ ...(fileMetadata.mimeType ? {
2468
+ mimeType: fileMetadata.mimeType
2469
+ } : {})
2470
+ }
2471
+ });
2472
+ }
2382
2473
  }
2383
2474
  }
2384
2475
  if (!validationError.fixes || validationError.fixes.length === 0) {
@@ -320,7 +320,6 @@ const printer = ts__default["default"].createPrinter({
320
320
  newLine: newLine
321
321
  // neverAsciiEscape: true,
322
322
  });
323
-
324
323
  function replaceNodeValue(document, node, value) {
325
324
  const replacementText = printer.printNode(ts__default["default"].EmitHint.Unspecified, toExpression(value), document);
326
325
  const span = ts__default["default"].createTextSpanFromBounds(node.getStart(document, false), node.end);
@@ -663,7 +662,6 @@ const patchValFile = async (id, valConfigPath, patch$1, sourceFileHandler, runti
663
662
  sourceFileHandler.writeSourceFile(newSourceFile.value);
664
663
  // console.timeEnd("patchValFile" + timeId);
665
664
  };
666
-
667
665
  function convertDataUrlToBase64(dataUrl) {
668
666
  const base64 = dataUrl.slice(dataUrl.indexOf(",") + 1);
669
667
  return Buffer.from(base64, "base64");
@@ -888,7 +886,6 @@ class ValModuleLoader {
888
886
  }
889
887
  }
890
888
  }
891
-
892
889
  return compiledCode;
893
890
  }
894
891
  resolveModulePath(containingFilePath, requestedModuleName) {
@@ -1218,7 +1215,7 @@ class LocalValServer {
1218
1215
  if (treePath && !path__namespace["default"].join(dir, file).replace(rootDir, "").startsWith(treePath)) {
1219
1216
  continue;
1220
1217
  }
1221
- moduleIds.push(path__namespace["default"].join(dir, file).replace(rootDir, "").replace(".val.js", "").replace(".val.ts", ""));
1218
+ moduleIds.push(path__namespace["default"].join(dir, file).replace(rootDir, "").replace(".val.js", "").replace(".val.ts", "").split(path__namespace["default"].sep).join("/"));
1222
1219
  }
1223
1220
  }
1224
1221
  };
@@ -1308,7 +1305,6 @@ class LocalValServer {
1308
1305
  json: {} // no patch ids created
1309
1306
  };
1310
1307
  }
1311
-
1312
1308
  badRequest() {
1313
1309
  return {
1314
1310
  status: 400,
@@ -1382,7 +1378,6 @@ function decodeJwt(token, secretKey) {
1382
1378
  function getExpire() {
1383
1379
  return Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 4; // 4 days
1384
1380
  }
1385
-
1386
1381
  const JwtHeaderSchema = z.z.object({
1387
1382
  alg: z.z.literal("HS256"),
1388
1383
  typ: z.z.literal("JWT")
@@ -1422,12 +1417,12 @@ class ProxyValServer {
1422
1417
  "Content-Type": fetchRes.headers.get("Content-Type") || "",
1423
1418
  "Content-Length": fetchRes.headers.get("Content-Length") || "0"
1424
1419
  },
1425
- body: fetchRes.body
1420
+ json: fetchRes.body
1426
1421
  };
1427
1422
  } else {
1428
1423
  return {
1429
1424
  status: 500,
1430
- body: {
1425
+ json: {
1431
1426
  message: "No body in response"
1432
1427
  }
1433
1428
  };
@@ -1435,7 +1430,7 @@ class ProxyValServer {
1435
1430
  } else {
1436
1431
  return {
1437
1432
  status: fetchRes.status,
1438
- body: {
1433
+ json: {
1439
1434
  message: "Failed to get files"
1440
1435
  }
1441
1436
  };
@@ -1468,7 +1463,6 @@ class ProxyValServer {
1468
1463
  }
1469
1464
  }
1470
1465
  },
1471
-
1472
1466
  status: 302,
1473
1467
  redirectTo: appAuthorizeUrl
1474
1468
  };
@@ -1542,6 +1536,7 @@ class ProxyValServer {
1542
1536
  [internal.VAL_STATE_COOKIE]: {
1543
1537
  value: null
1544
1538
  },
1539
+ [internal.VAL_ENABLE_COOKIE_NAME]: ENABLE_COOKIE_VALUE,
1545
1540
  [internal.VAL_SESSION_COOKIE]: {
1546
1541
  value: cookie,
1547
1542
  options: {
@@ -1553,7 +1548,6 @@ class ProxyValServer {
1553
1548
  }
1554
1549
  }
1555
1550
  },
1556
-
1557
1551
  redirectTo: callbackReqSuccess.redirect_uri || "/"
1558
1552
  };
1559
1553
  }
@@ -1596,7 +1590,7 @@ class ProxyValServer {
1596
1590
  console.error(`Failed while processing: ${errorMessageType}`, err);
1597
1591
  return {
1598
1592
  status: 500,
1599
- body: {
1593
+ json: {
1600
1594
  message: err.message
1601
1595
  }
1602
1596
  };
@@ -1628,8 +1622,9 @@ class ProxyValServer {
1628
1622
  } else {
1629
1623
  return {
1630
1624
  status: fetchRes.status,
1631
- body: {
1632
- message: "Failed to get session"
1625
+ json: {
1626
+ message: "Failed to authorize",
1627
+ ...(await fetchRes.json())
1633
1628
  }
1634
1629
  };
1635
1630
  }
@@ -1646,7 +1641,7 @@ class ProxyValServer {
1646
1641
  if (!commit) {
1647
1642
  return {
1648
1643
  status: 400,
1649
- body: {
1644
+ json: {
1650
1645
  message: "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT."
1651
1646
  }
1652
1647
  };
@@ -1690,7 +1685,7 @@ class ProxyValServer {
1690
1685
  } catch (err) {
1691
1686
  return {
1692
1687
  status: 500,
1693
- body: {
1688
+ json: {
1694
1689
  message: "Failed to fetch: check network connection"
1695
1690
  }
1696
1691
  };
@@ -1705,7 +1700,7 @@ class ProxyValServer {
1705
1700
  if (!commit) {
1706
1701
  return {
1707
1702
  status: 400,
1708
- body: {
1703
+ json: {
1709
1704
  message: "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT."
1710
1705
  }
1711
1706
  };
@@ -1726,7 +1721,7 @@ class ProxyValServer {
1726
1721
  } else {
1727
1722
  return {
1728
1723
  status: fetchRes.status,
1729
- body: {
1724
+ json: {
1730
1725
  message: "Failed to get patches"
1731
1726
  }
1732
1727
  };
@@ -1754,7 +1749,7 @@ class ProxyValServer {
1754
1749
  if (!patchJSON.success) {
1755
1750
  return {
1756
1751
  status: 400,
1757
- body: {
1752
+ json: {
1758
1753
  message: "Invalid patch",
1759
1754
  details: patchJSON.error.issues
1760
1755
  }
@@ -2273,7 +2268,17 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
2273
2268
  const filename = path__namespace["default"].join(config.projectRoot, maybeRef);
2274
2269
  const buffer = fs__default["default"].readFileSync(filename);
2275
2270
  const imageSize = sizeOf__default["default"](buffer);
2276
- const mimeType = imageSize.type ? internal.imageTypeToMimeType(imageSize.type) : internal.filenameToMimeType(filename);
2271
+ let mimeType = null;
2272
+ if (imageSize.type) {
2273
+ const possibleMimeType = `image/${imageSize.type}`;
2274
+ if (internal.MIME_TYPES_TO_EXT[possibleMimeType]) {
2275
+ mimeType = possibleMimeType;
2276
+ }
2277
+ const filenameBasedLookup = internal.filenameToMimeType(filename);
2278
+ if (filenameBasedLookup) {
2279
+ mimeType = filenameBasedLookup;
2280
+ }
2281
+ }
2277
2282
  if (!mimeType) {
2278
2283
  throw Error("Cannot determine mimetype of image");
2279
2284
  }
@@ -2294,6 +2299,26 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
2294
2299
  mimeType
2295
2300
  };
2296
2301
  }
2302
+ async function getFileMetadata() {
2303
+ 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;
2304
+ if (!maybeRef) {
2305
+ // TODO:
2306
+ throw Error("Cannot fix image without a file reference");
2307
+ }
2308
+ const filename = path__namespace["default"].join(config.projectRoot, maybeRef);
2309
+ const buffer = fs__default["default"].readFileSync(filename);
2310
+ let mimeType = internal.filenameToMimeType(filename);
2311
+ if (!mimeType) {
2312
+ mimeType = "application/octet-stream";
2313
+ }
2314
+ const sha256 = core.Internal.getSHA256Hash(textEncoder.encode(
2315
+ // TODO: we should probably store the mimetype in the metadata and reuse it here
2316
+ `data:${mimeType};base64,${buffer.toString("base64")}`));
2317
+ return {
2318
+ sha256,
2319
+ mimeType
2320
+ };
2321
+ }
2297
2322
  const remainingErrors = [];
2298
2323
  const patch$1 = [];
2299
2324
  for (const fix of validationError.fixes || []) {
@@ -2379,6 +2404,72 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
2379
2404
  }
2380
2405
  });
2381
2406
  }
2407
+ } else if (fix === "file:add-metadata" || fix === "file:check-metadata") {
2408
+ const fileMetadata = await getFileMetadata();
2409
+ if (fileMetadata.sha256 === undefined) {
2410
+ remainingErrors.push({
2411
+ ...validationError,
2412
+ message: "Failed to get image metadata",
2413
+ fixes: undefined
2414
+ });
2415
+ } else if (fix === "file:check-metadata") {
2416
+ const currentValue = validationError.value;
2417
+ const metadataIsCorrect =
2418
+ // metadata is a prop that is an object
2419
+ typeof currentValue === "object" && currentValue && "metadata" in currentValue && currentValue.metadata && typeof currentValue.metadata === "object" &&
2420
+ // sha256 is correct
2421
+ "sha256" in currentValue.metadata && currentValue.metadata.sha256 === fileMetadata.sha256 &&
2422
+ // mimeType is correct
2423
+ "mimeType" in currentValue.metadata && currentValue.metadata.mimeType === fileMetadata.mimeType;
2424
+
2425
+ // skips if the metadata is already correct
2426
+ if (!metadataIsCorrect) {
2427
+ if (apply) {
2428
+ patch$1.push({
2429
+ op: "replace",
2430
+ path: patch.sourceToPatchPath(sourcePath).concat("metadata"),
2431
+ value: {
2432
+ sha256: fileMetadata.sha256,
2433
+ ...(fileMetadata.mimeType ? {
2434
+ mimeType: fileMetadata.mimeType
2435
+ } : {})
2436
+ }
2437
+ });
2438
+ } else {
2439
+ if (typeof currentValue === "object" && currentValue && "metadata" in currentValue && currentValue.metadata && typeof currentValue.metadata === "object") {
2440
+ if (!("sha256" in currentValue.metadata) || currentValue.metadata.sha256 !== fileMetadata.sha256) {
2441
+ remainingErrors.push({
2442
+ message: "File metadata sha256 is incorrect! Found: " + ("sha256" in currentValue.metadata ? currentValue.metadata.sha256 : "<empty>") + ". Expected: " + fileMetadata.sha256 + ".",
2443
+ fixes: undefined
2444
+ });
2445
+ }
2446
+ if (!("mimeType" in currentValue.metadata) || currentValue.metadata.mimeType !== fileMetadata.mimeType) {
2447
+ remainingErrors.push({
2448
+ message: "File metadata mimeType is incorrect! Found: " + ("mimeType" in currentValue.metadata ? currentValue.metadata.mimeType : "<empty>") + ". Expected: " + fileMetadata.mimeType,
2449
+ fixes: undefined
2450
+ });
2451
+ }
2452
+ } else {
2453
+ remainingErrors.push({
2454
+ ...validationError,
2455
+ message: "Image metadata is not an object!",
2456
+ fixes: undefined
2457
+ });
2458
+ }
2459
+ }
2460
+ }
2461
+ } else if (fix === "file:add-metadata") {
2462
+ patch$1.push({
2463
+ op: "add",
2464
+ path: patch.sourceToPatchPath(sourcePath).concat("metadata"),
2465
+ value: {
2466
+ sha256: fileMetadata.sha256,
2467
+ ...(fileMetadata.mimeType ? {
2468
+ mimeType: fileMetadata.mimeType
2469
+ } : {})
2470
+ }
2471
+ });
2472
+ }
2382
2473
  }
2383
2474
  }
2384
2475
  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';
@@ -290,7 +290,6 @@ const printer = ts.createPrinter({
290
290
  newLine: newLine
291
291
  // neverAsciiEscape: true,
292
292
  });
293
-
294
293
  function replaceNodeValue(document, node, value) {
295
294
  const replacementText = printer.printNode(ts.EmitHint.Unspecified, toExpression(value), document);
296
295
  const span = ts.createTextSpanFromBounds(node.getStart(document, false), node.end);
@@ -633,7 +632,6 @@ const patchValFile = async (id, valConfigPath, patch, sourceFileHandler, runtime
633
632
  sourceFileHandler.writeSourceFile(newSourceFile.value);
634
633
  // console.timeEnd("patchValFile" + timeId);
635
634
  };
636
-
637
635
  function convertDataUrlToBase64(dataUrl) {
638
636
  const base64 = dataUrl.slice(dataUrl.indexOf(",") + 1);
639
637
  return Buffer.from(base64, "base64");
@@ -858,7 +856,6 @@ class ValModuleLoader {
858
856
  }
859
857
  }
860
858
  }
861
-
862
859
  return compiledCode;
863
860
  }
864
861
  resolveModulePath(containingFilePath, requestedModuleName) {
@@ -1188,7 +1185,7 @@ class LocalValServer {
1188
1185
  if (treePath && !path__default.join(dir, file).replace(rootDir, "").startsWith(treePath)) {
1189
1186
  continue;
1190
1187
  }
1191
- moduleIds.push(path__default.join(dir, file).replace(rootDir, "").replace(".val.js", "").replace(".val.ts", ""));
1188
+ moduleIds.push(path__default.join(dir, file).replace(rootDir, "").replace(".val.js", "").replace(".val.ts", "").split(path__default.sep).join("/"));
1192
1189
  }
1193
1190
  }
1194
1191
  };
@@ -1278,7 +1275,6 @@ class LocalValServer {
1278
1275
  json: {} // no patch ids created
1279
1276
  };
1280
1277
  }
1281
-
1282
1278
  badRequest() {
1283
1279
  return {
1284
1280
  status: 400,
@@ -1352,7 +1348,6 @@ function decodeJwt(token, secretKey) {
1352
1348
  function getExpire() {
1353
1349
  return Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 4; // 4 days
1354
1350
  }
1355
-
1356
1351
  const JwtHeaderSchema = z$1.object({
1357
1352
  alg: z$1.literal("HS256"),
1358
1353
  typ: z$1.literal("JWT")
@@ -1392,12 +1387,12 @@ class ProxyValServer {
1392
1387
  "Content-Type": fetchRes.headers.get("Content-Type") || "",
1393
1388
  "Content-Length": fetchRes.headers.get("Content-Length") || "0"
1394
1389
  },
1395
- body: fetchRes.body
1390
+ json: fetchRes.body
1396
1391
  };
1397
1392
  } else {
1398
1393
  return {
1399
1394
  status: 500,
1400
- body: {
1395
+ json: {
1401
1396
  message: "No body in response"
1402
1397
  }
1403
1398
  };
@@ -1405,7 +1400,7 @@ class ProxyValServer {
1405
1400
  } else {
1406
1401
  return {
1407
1402
  status: fetchRes.status,
1408
- body: {
1403
+ json: {
1409
1404
  message: "Failed to get files"
1410
1405
  }
1411
1406
  };
@@ -1438,7 +1433,6 @@ class ProxyValServer {
1438
1433
  }
1439
1434
  }
1440
1435
  },
1441
-
1442
1436
  status: 302,
1443
1437
  redirectTo: appAuthorizeUrl
1444
1438
  };
@@ -1512,6 +1506,7 @@ class ProxyValServer {
1512
1506
  [VAL_STATE_COOKIE$1]: {
1513
1507
  value: null
1514
1508
  },
1509
+ [VAL_ENABLE_COOKIE_NAME]: ENABLE_COOKIE_VALUE,
1515
1510
  [VAL_SESSION_COOKIE$1]: {
1516
1511
  value: cookie,
1517
1512
  options: {
@@ -1523,7 +1518,6 @@ class ProxyValServer {
1523
1518
  }
1524
1519
  }
1525
1520
  },
1526
-
1527
1521
  redirectTo: callbackReqSuccess.redirect_uri || "/"
1528
1522
  };
1529
1523
  }
@@ -1566,7 +1560,7 @@ class ProxyValServer {
1566
1560
  console.error(`Failed while processing: ${errorMessageType}`, err);
1567
1561
  return {
1568
1562
  status: 500,
1569
- body: {
1563
+ json: {
1570
1564
  message: err.message
1571
1565
  }
1572
1566
  };
@@ -1598,8 +1592,9 @@ class ProxyValServer {
1598
1592
  } else {
1599
1593
  return {
1600
1594
  status: fetchRes.status,
1601
- body: {
1602
- message: "Failed to get session"
1595
+ json: {
1596
+ message: "Failed to authorize",
1597
+ ...(await fetchRes.json())
1603
1598
  }
1604
1599
  };
1605
1600
  }
@@ -1616,7 +1611,7 @@ class ProxyValServer {
1616
1611
  if (!commit) {
1617
1612
  return {
1618
1613
  status: 400,
1619
- body: {
1614
+ json: {
1620
1615
  message: "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT."
1621
1616
  }
1622
1617
  };
@@ -1660,7 +1655,7 @@ class ProxyValServer {
1660
1655
  } catch (err) {
1661
1656
  return {
1662
1657
  status: 500,
1663
- body: {
1658
+ json: {
1664
1659
  message: "Failed to fetch: check network connection"
1665
1660
  }
1666
1661
  };
@@ -1675,7 +1670,7 @@ class ProxyValServer {
1675
1670
  if (!commit) {
1676
1671
  return {
1677
1672
  status: 400,
1678
- body: {
1673
+ json: {
1679
1674
  message: "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT."
1680
1675
  }
1681
1676
  };
@@ -1696,7 +1691,7 @@ class ProxyValServer {
1696
1691
  } else {
1697
1692
  return {
1698
1693
  status: fetchRes.status,
1699
- body: {
1694
+ json: {
1700
1695
  message: "Failed to get patches"
1701
1696
  }
1702
1697
  };
@@ -1724,7 +1719,7 @@ class ProxyValServer {
1724
1719
  if (!patchJSON.success) {
1725
1720
  return {
1726
1721
  status: 400,
1727
- body: {
1722
+ json: {
1728
1723
  message: "Invalid patch",
1729
1724
  details: patchJSON.error.issues
1730
1725
  }
@@ -2243,7 +2238,17 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
2243
2238
  const filename = path__default.join(config.projectRoot, maybeRef);
2244
2239
  const buffer = fs.readFileSync(filename);
2245
2240
  const imageSize = sizeOf(buffer);
2246
- const mimeType = imageSize.type ? imageTypeToMimeType(imageSize.type) : filenameToMimeType(filename);
2241
+ let mimeType = null;
2242
+ if (imageSize.type) {
2243
+ const possibleMimeType = `image/${imageSize.type}`;
2244
+ if (MIME_TYPES_TO_EXT[possibleMimeType]) {
2245
+ mimeType = possibleMimeType;
2246
+ }
2247
+ const filenameBasedLookup = filenameToMimeType(filename);
2248
+ if (filenameBasedLookup) {
2249
+ mimeType = filenameBasedLookup;
2250
+ }
2251
+ }
2247
2252
  if (!mimeType) {
2248
2253
  throw Error("Cannot determine mimetype of image");
2249
2254
  }
@@ -2264,6 +2269,26 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
2264
2269
  mimeType
2265
2270
  };
2266
2271
  }
2272
+ async function getFileMetadata() {
2273
+ 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;
2274
+ if (!maybeRef) {
2275
+ // TODO:
2276
+ throw Error("Cannot fix image without a file reference");
2277
+ }
2278
+ const filename = path__default.join(config.projectRoot, maybeRef);
2279
+ const buffer = fs.readFileSync(filename);
2280
+ let mimeType = filenameToMimeType(filename);
2281
+ if (!mimeType) {
2282
+ mimeType = "application/octet-stream";
2283
+ }
2284
+ const sha256 = Internal.getSHA256Hash(textEncoder.encode(
2285
+ // TODO: we should probably store the mimetype in the metadata and reuse it here
2286
+ `data:${mimeType};base64,${buffer.toString("base64")}`));
2287
+ return {
2288
+ sha256,
2289
+ mimeType
2290
+ };
2291
+ }
2267
2292
  const remainingErrors = [];
2268
2293
  const patch = [];
2269
2294
  for (const fix of validationError.fixes || []) {
@@ -2349,6 +2374,72 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
2349
2374
  }
2350
2375
  });
2351
2376
  }
2377
+ } else if (fix === "file:add-metadata" || fix === "file:check-metadata") {
2378
+ const fileMetadata = await getFileMetadata();
2379
+ if (fileMetadata.sha256 === undefined) {
2380
+ remainingErrors.push({
2381
+ ...validationError,
2382
+ message: "Failed to get image metadata",
2383
+ fixes: undefined
2384
+ });
2385
+ } else if (fix === "file:check-metadata") {
2386
+ const currentValue = validationError.value;
2387
+ const metadataIsCorrect =
2388
+ // metadata is a prop that is an object
2389
+ typeof currentValue === "object" && currentValue && "metadata" in currentValue && currentValue.metadata && typeof currentValue.metadata === "object" &&
2390
+ // sha256 is correct
2391
+ "sha256" in currentValue.metadata && currentValue.metadata.sha256 === fileMetadata.sha256 &&
2392
+ // mimeType is correct
2393
+ "mimeType" in currentValue.metadata && currentValue.metadata.mimeType === fileMetadata.mimeType;
2394
+
2395
+ // skips if the metadata is already correct
2396
+ if (!metadataIsCorrect) {
2397
+ if (apply) {
2398
+ patch.push({
2399
+ op: "replace",
2400
+ path: sourceToPatchPath(sourcePath).concat("metadata"),
2401
+ value: {
2402
+ sha256: fileMetadata.sha256,
2403
+ ...(fileMetadata.mimeType ? {
2404
+ mimeType: fileMetadata.mimeType
2405
+ } : {})
2406
+ }
2407
+ });
2408
+ } else {
2409
+ if (typeof currentValue === "object" && currentValue && "metadata" in currentValue && currentValue.metadata && typeof currentValue.metadata === "object") {
2410
+ if (!("sha256" in currentValue.metadata) || currentValue.metadata.sha256 !== fileMetadata.sha256) {
2411
+ remainingErrors.push({
2412
+ message: "File metadata sha256 is incorrect! Found: " + ("sha256" in currentValue.metadata ? currentValue.metadata.sha256 : "<empty>") + ". Expected: " + fileMetadata.sha256 + ".",
2413
+ fixes: undefined
2414
+ });
2415
+ }
2416
+ if (!("mimeType" in currentValue.metadata) || currentValue.metadata.mimeType !== fileMetadata.mimeType) {
2417
+ remainingErrors.push({
2418
+ message: "File metadata mimeType is incorrect! Found: " + ("mimeType" in currentValue.metadata ? currentValue.metadata.mimeType : "<empty>") + ". Expected: " + fileMetadata.mimeType,
2419
+ fixes: undefined
2420
+ });
2421
+ }
2422
+ } else {
2423
+ remainingErrors.push({
2424
+ ...validationError,
2425
+ message: "Image metadata is not an object!",
2426
+ fixes: undefined
2427
+ });
2428
+ }
2429
+ }
2430
+ }
2431
+ } else if (fix === "file:add-metadata") {
2432
+ patch.push({
2433
+ op: "add",
2434
+ path: sourceToPatchPath(sourcePath).concat("metadata"),
2435
+ value: {
2436
+ sha256: fileMetadata.sha256,
2437
+ ...(fileMetadata.mimeType ? {
2438
+ mimeType: fileMetadata.mimeType
2439
+ } : {})
2440
+ }
2441
+ });
2442
+ }
2352
2443
  }
2353
2444
  }
2354
2445
  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.50.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.50.0",
28
+ "@valbuild/shared": "~0.50.0",
29
+ "@valbuild/ui": "~0.50.0",
30
30
  "express": "^4.18.2",
31
31
  "image-size": "^1.0.2",
32
32
  "queue": "^6.0.2",