@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 (!valModule) {
735
- fatalErrors.push(`Could not find any modules at: ${id}`);
736
- } else if (valModule.defaultExport) {
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
- body: fetchRes.body
1425
+ json: fetchRes.body
1429
1426
  };
1430
1427
  } else {
1431
1428
  return {
1432
1429
  status: 500,
1433
- body: {
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
- body: {
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
- body: {
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
- body: {
1635
- message: "Failed to get session"
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
- body: {
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
- body: {
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
- body: {
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
- body: {
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
- body: {
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
- 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
+ }
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 (!valModule) {
735
- fatalErrors.push(`Could not find any modules at: ${id}`);
736
- } else if (valModule.defaultExport) {
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
- body: fetchRes.body
1425
+ json: fetchRes.body
1429
1426
  };
1430
1427
  } else {
1431
1428
  return {
1432
1429
  status: 500,
1433
- body: {
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
- body: {
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
- body: {
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
- body: {
1635
- message: "Failed to get session"
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
- body: {
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
- body: {
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
- body: {
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
- body: {
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
- body: {
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
- 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
+ }
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, 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';
@@ -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 (!valModule) {
705
- fatalErrors.push(`Could not find any modules at: ${id}`);
706
- } else if (valModule.defaultExport) {
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
- body: fetchRes.body
1395
+ json: fetchRes.body
1399
1396
  };
1400
1397
  } else {
1401
1398
  return {
1402
1399
  status: 500,
1403
- body: {
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
- body: {
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
- body: {
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
- body: {
1605
- message: "Failed to get session"
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
- body: {
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
- body: {
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
- body: {
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
- body: {
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
- body: {
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
- 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
+ }
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.48.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.48.0",
28
- "@valbuild/shared": "~0.48.0",
29
- "@valbuild/ui": "~0.48.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",