@valbuild/server 0.60.22 → 0.60.24

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.
@@ -2,7 +2,7 @@
2
2
  import { Service } from "./Service.js";
3
3
  import { result } from "@valbuild/core/fp";
4
4
  import { Patch } from "./patch/validation.js";
5
- import { ApiGetPatchResponse, ApiPostPatchResponse, ModuleId, PatchId, ApiDeletePatchResponse } from "@valbuild/core";
5
+ import { ApiGetPatchResponse, ApiPostPatchResponse, ModuleId, PatchId, ApiDeletePatchResponse, FileMetadata, ImageMetadata } from "@valbuild/core";
6
6
  import { VAL_ENABLE_COOKIE_NAME, VAL_SESSION_COOKIE, VAL_STATE_COOKIE, ValServerError, ValServerJsonResult, ValServerRedirectResult, ValServerResult, ValSession } from "@valbuild/shared/internal";
7
7
  import { ValServer, ValServerCallbacks } from "./ValServer.js";
8
8
  import { SerializedModuleContent } from "./SerializedModuleContent.js";
@@ -32,6 +32,7 @@ export declare class LocalValServer extends ValServer {
32
32
  id?: string[];
33
33
  }): Promise<ValServerJsonResult<ApiDeletePatchResponse>>;
34
34
  postPatches(body: unknown): Promise<ValServerJsonResult<ApiPostPatchResponse>>;
35
+ getMetadata(): Promise<FileMetadata | ImageMetadata | undefined>;
35
36
  getFiles(filePath: string, query: {
36
37
  sha256?: string;
37
38
  }): Promise<ValServerResult<never, ReadableStream<Uint8Array>>>;
@@ -44,7 +45,6 @@ export declare class LocalValServer extends ValServer {
44
45
  private badRequest;
45
46
  protected ensureInitialized(): Promise<result.Result<undefined, ValServerError>>;
46
47
  protected getModule(moduleId: ModuleId, options: {
47
- validate: boolean;
48
48
  source: boolean;
49
49
  schema: boolean;
50
50
  }): Promise<SerializedModuleContent>;
@@ -4,7 +4,7 @@ import { VAL_ENABLE_COOKIE_NAME, VAL_SESSION_COOKIE, VAL_STATE_COOKIE, ValCookie
4
4
  import { result } from "@valbuild/core/fp";
5
5
  import { Operation } from "@valbuild/core/patch";
6
6
  import { Patch } from "./patch/validation.js";
7
- import { ModuleId, PatchId } from "@valbuild/core";
7
+ import { ModuleId, PatchId, FileMetadata, ImageMetadata } from "@valbuild/core";
8
8
  import { SerializedModuleContent } from "./SerializedModuleContent.js";
9
9
  export type ValServerOptions = {
10
10
  valEnableRedirectUrl?: string;
@@ -29,11 +29,11 @@ export declare abstract class ValServer implements IValServer {
29
29
  patch?: string;
30
30
  schema?: string;
31
31
  source?: string;
32
- validate?: string;
33
32
  }, cookies: ValCookies<VAL_SESSION_COOKIE>, requestHeaders: RequestHeaders): Promise<ValServerJsonResult<ApiTreeResponse>>;
34
33
  postValidate(rawBody: unknown, cookies: ValCookies<VAL_SESSION_COOKIE>, requestHeaders: RequestHeaders): Promise<ValServerJsonResult<ApiPostValidationResponse | ApiPostValidationErrorResponse>>;
35
34
  postCommit(rawBody: unknown, cookies: ValCookies<VAL_SESSION_COOKIE>, requestHeaders: RequestHeaders): Promise<ValServerJsonResult<ApiCommitResponse, ApiPostValidationErrorResponse>>;
36
35
  private applyAllPatchesThenValidate;
36
+ abstract getMetadata(fileRef: string, sha256?: string): Promise<FileMetadata | ImageMetadata | undefined>;
37
37
  private revalidateImageAndFileValidation;
38
38
  protected abstract getAllModules(treePath: string): Promise<ModuleId[]>;
39
39
  protected sortPatchIds(patchesByModule: Record<ModuleId, {
@@ -58,7 +58,6 @@ export declare abstract class ValServer implements IValServer {
58
58
  * */
59
59
  protected abstract ensureInitialized(errorMessageType: string, cookies: ValCookies<VAL_SESSION_COOKIE>): Promise<result.Result<undefined, ValServerError>>;
60
60
  protected abstract getModule(moduleId: ModuleId, options: {
61
- validate: boolean;
62
61
  source: boolean;
63
62
  schema: boolean;
64
63
  }): Promise<SerializedModuleContent>;
@@ -160,3 +159,4 @@ export type RequestHeaders = {
160
159
  host?: string | null;
161
160
  "x-forwarded-proto"?: string | null;
162
161
  };
162
+ export declare function debugTiming<R>(id: string, fn: () => Promise<R>): Promise<R>;
@@ -1452,15 +1452,14 @@ class ValServer {
1452
1452
  async getTree(treePath,
1453
1453
  // TODO: use the params: patch, schema, source now we return everything, every time
1454
1454
  query, cookies, requestHeaders) {
1455
- const ensureRes = await this.ensureInitialized("getTree", cookies);
1455
+ const ensureRes = await debugTiming("ensureInitialized", () => this.ensureInitialized("getTree", cookies));
1456
1456
  if (fp.result.isErr(ensureRes)) {
1457
1457
  return ensureRes.error;
1458
1458
  }
1459
1459
  const applyPatches = query.patch === "true";
1460
- const execValidations = query.validate === "true";
1461
1460
  const includeSource = query.source === "true";
1462
1461
  const includeSchema = query.schema === "true";
1463
- const moduleIds = await this.getAllModules(treePath);
1462
+ const moduleIds = await debugTiming("getAllModules", () => this.getAllModules(treePath));
1464
1463
  let {
1465
1464
  patchIdsByModuleId,
1466
1465
  patchesById,
@@ -1471,7 +1470,7 @@ class ValServer {
1471
1470
  fileUpdates: {}
1472
1471
  };
1473
1472
  if (applyPatches) {
1474
- const res = await this.readPatches(cookies);
1473
+ const res = await debugTiming("readPatches", () => this.readPatches(cookies));
1475
1474
  if (fp.result.isErr(res)) {
1476
1475
  return res.error;
1477
1476
  }
@@ -1479,9 +1478,10 @@ class ValServer {
1479
1478
  patchesById = res.value.patchesById;
1480
1479
  fileUpdates = res.value.fileUpdates;
1481
1480
  }
1482
- const possiblyPatchedContent = await Promise.all(moduleIds.map(async moduleId => {
1483
- return this.applyAllPatchesThenValidate(moduleId, patchIdsByModuleId, patchesById, fileUpdates, cookies, requestHeaders, applyPatches, execValidations, includeSource, includeSchema);
1484
- }));
1481
+ const validate = false;
1482
+ const possiblyPatchedContent = await debugTiming("applyAllPatchesThenValidate", () => Promise.all(moduleIds.map(async moduleId => {
1483
+ return this.applyAllPatchesThenValidate(moduleId, patchIdsByModuleId, patchesById, fileUpdates, cookies, requestHeaders, applyPatches, validate, includeSource, includeSchema);
1484
+ })));
1485
1485
  const modules = Object.fromEntries(possiblyPatchedContent.map(serializedModuleContent => {
1486
1486
  const module = {
1487
1487
  schema: serializedModuleContent.schema,
@@ -1535,7 +1535,6 @@ class ValServer {
1535
1535
  /* */
1536
1536
  async applyAllPatchesThenValidate(moduleId, patchIdsByModuleId, patchesById, fileUpdates, cookies, requestHeaders, applyPatches, validate, includeSource, includeSchema) {
1537
1537
  const serializedModuleContent = await this.getModule(moduleId, {
1538
- validate: validate,
1539
1538
  source: includeSource,
1540
1539
  schema: includeSchema
1541
1540
  });
@@ -1551,38 +1550,40 @@ class ValServer {
1551
1550
  return serializedModuleContent;
1552
1551
  }
1553
1552
  let source = maybeSource;
1554
- for (const patchId of patchIdsByModuleId[moduleId] ?? []) {
1555
- const patch$1 = patchesById[patchId];
1556
- if (!patch$1) {
1557
- continue;
1558
- }
1559
- const patchRes = patch.applyPatch(source, ops, patch$1.filter(core.Internal.notFileOp));
1560
- if (fp.result.isOk(patchRes)) {
1561
- source = patchRes.value;
1562
- } else {
1563
- console.error("Val: got an unexpected error while applying patch. Is there a mismatch in Val versions? Perhaps Val is misconfigured?", {
1564
- patchId,
1565
- moduleId,
1566
- patch: JSON.stringify(patch$1, null, 2),
1567
- error: patchRes.error
1568
- });
1569
- return {
1570
- path: moduleId,
1571
- schema,
1572
- source,
1573
- errors: {
1574
- fatal: [{
1575
- message: "Unexpected error applying patch",
1576
- type: "invalid-patch"
1577
- }]
1578
- }
1579
- };
1553
+ await debugTiming("applyPatches:" + moduleId, async () => {
1554
+ for (const patchId of patchIdsByModuleId[moduleId] ?? []) {
1555
+ const patch$1 = patchesById[patchId];
1556
+ if (!patch$1) {
1557
+ continue;
1558
+ }
1559
+ const patchRes = patch.applyPatch(source, ops, patch$1.filter(core.Internal.notFileOp));
1560
+ if (fp.result.isOk(patchRes)) {
1561
+ source = patchRes.value;
1562
+ } else {
1563
+ console.error("Val: got an unexpected error while applying patch. Is there a mismatch in Val versions? Perhaps Val is misconfigured?", {
1564
+ patchId,
1565
+ moduleId,
1566
+ patch: JSON.stringify(patch$1, null, 2),
1567
+ error: patchRes.error
1568
+ });
1569
+ return {
1570
+ path: moduleId,
1571
+ schema,
1572
+ source,
1573
+ errors: {
1574
+ fatal: [{
1575
+ message: "Unexpected error applying patch",
1576
+ type: "invalid-patch"
1577
+ }]
1578
+ }
1579
+ };
1580
+ }
1580
1581
  }
1581
- }
1582
+ });
1582
1583
  if (validate) {
1583
- const validationErrors = core.deserializeSchema(schema).validate(moduleId, source);
1584
+ const validationErrors = await debugTiming("validate:" + moduleId, async () => core.deserializeSchema(schema).validate(moduleId, source));
1584
1585
  if (validationErrors) {
1585
- const revalidated = await this.revalidateImageAndFileValidation(validationErrors, fileUpdates, cookies, requestHeaders);
1586
+ const revalidated = await debugTiming("revalidate image/file:" + moduleId, async () => this.revalidateImageAndFileValidation(validationErrors, fileUpdates, cookies, requestHeaders));
1586
1587
  return {
1587
1588
  path: moduleId,
1588
1589
  schema,
@@ -1600,7 +1601,6 @@ class ValServer {
1600
1601
  errors: false
1601
1602
  };
1602
1603
  }
1603
-
1604
1604
  // TODO: name this better: we need to check for image and file validation errors
1605
1605
  // since they cannot be handled directly inside the validation function.
1606
1606
  // The reason is that validate will be called inside QuickJS (in the future, hopefully),
@@ -1618,8 +1618,9 @@ class ValServer {
1618
1618
  )) {
1619
1619
  const fileRef = getValidationErrorFileRef(error);
1620
1620
  if (fileRef) {
1621
+ var _fileUpdates$fileRef;
1621
1622
  const filePath = path__namespace["default"].join(this.cwd, fileRef);
1622
- let expectedMetadata;
1623
+ let expectedMetadata = await this.getMetadata(fileRef, (_fileUpdates$fileRef = fileUpdates[fileRef]) === null || _fileUpdates$fileRef === void 0 ? void 0 : _fileUpdates$fileRef.sha256);
1623
1624
 
1624
1625
  // if this is a new file or we have an actual FS, we read the file and get the metadata
1625
1626
  if (!expectedMetadata) {
@@ -1801,7 +1802,7 @@ class ValServer {
1801
1802
  const moduleId = moduleIdStr;
1802
1803
  const serializedModuleContent = await this.applyAllPatchesThenValidate(moduleId, filterPatchesByModuleIdRes.data.patches ||
1803
1804
  // TODO: refine to ModuleId and PatchId when parsing
1804
- patchIdsByModuleId, patchesById, fileUpdates, cookies, requestHeaders, true, true, true, true);
1805
+ patchIdsByModuleId, patchesById, fileUpdates, cookies, requestHeaders, true, true, true, commit);
1805
1806
  if (serializedModuleContent.errors) {
1806
1807
  validationErrorsByModuleId[moduleId] = serializedModuleContent;
1807
1808
  }
@@ -2034,6 +2035,16 @@ function guessMimeTypeFromPath(filePath) {
2034
2035
  function isCachedPatchFileOp(op) {
2035
2036
  return !!(op.op === "file" && typeof op.filePath === "string" && op.value && typeof op.value === "object" && !Array.isArray(op.value) && "sha256" in op.value && typeof op.value.sha256 === "string");
2036
2037
  }
2038
+ async function debugTiming(id, fn) {
2039
+ if (process.env["VAL_DEBUG_TIMING"] === "true") {
2040
+ const start = Date.now();
2041
+ const r = await fn();
2042
+ console.log(`Timing: ${id} took: ${Date.now() - start}ms (${new Date().toISOString()})`);
2043
+ return r;
2044
+ } else {
2045
+ return fn();
2046
+ }
2047
+ }
2037
2048
 
2038
2049
  const textEncoder = new TextEncoder();
2039
2050
  class LocalValServer extends ValServer {
@@ -2156,6 +2167,9 @@ class LocalValServer extends ValServer {
2156
2167
  json: res
2157
2168
  };
2158
2169
  }
2170
+ async getMetadata() {
2171
+ return undefined;
2172
+ }
2159
2173
  async getFiles(filePath, query) {
2160
2174
  if (query.sha256) {
2161
2175
  const fileExists = this.host.fileExists(this.getFilePath(filePath, query.sha256));
@@ -2307,7 +2321,10 @@ class LocalValServer extends ValServer {
2307
2321
  return fp.result.ok(undefined);
2308
2322
  }
2309
2323
  getModule(moduleId, options) {
2310
- return this.options.service.get(moduleId, "", options);
2324
+ return this.options.service.get(moduleId, "", {
2325
+ ...options,
2326
+ validate: false
2327
+ });
2311
2328
  }
2312
2329
  async getAllModules(treePath) {
2313
2330
  const moduleIds = this.host.readDirectory(this.cwd, ["ts", "js"], ["node_modules", ".*"], ["**/*.val.ts", "**/*.val.js"]).filter(file => {
@@ -2824,76 +2841,72 @@ class ProxyValServer extends ValServer {
2824
2841
  }
2825
2842
  });
2826
2843
  }
2827
- async getFiles(filePath, query, cookies, reqHeaders) {
2828
- return withAuth(this.options.valSecret, cookies, "getFiles", async data => {
2829
- const url = new URL(`/v1/files/${this.options.remote}${filePath}`, this.options.valContentUrl);
2830
- if (typeof query.sha256 === "string") {
2831
- url.searchParams.append("sha256", query.sha256);
2832
- } else {
2833
- console.warn("Missing sha256 query param");
2844
+ async getMetadata(filePath, sha256) {
2845
+ const url = new URL(`/v1/metadata/${this.options.remote}${filePath}?commit=${this.options.git.commit}${sha256 ? `&sha256=${sha256}` : ""}`, this.options.valContentUrl);
2846
+ const fetchRes = await fetch(url, {
2847
+ headers: {
2848
+ Authorization: `Bearer ${this.options.apiKey}`
2834
2849
  }
2835
- const fetchRes = await fetch(url, {
2836
- headers: getAuthHeaders(data.token)
2837
- });
2838
- if (fetchRes.status === 200) {
2839
- // TODO: does this stream data?
2840
- if (fetchRes.body) {
2841
- return {
2842
- status: fetchRes.status,
2843
- headers: {
2844
- "Content-Type": fetchRes.headers.get("Content-Type") || "",
2845
- "Content-Length": fetchRes.headers.get("Content-Length") || "0",
2846
- "Cache-Control": "public, max-age=31536000, immutable"
2847
- },
2848
- body: fetchRes.body
2849
- };
2850
- } else {
2851
- return {
2852
- status: 500,
2853
- json: {
2854
- message: "No body in response"
2855
- }
2856
- };
2857
- }
2850
+ });
2851
+ if (fetchRes.status === 200) {
2852
+ const json = await fetchRes.json();
2853
+ if (json.type === "file") {
2854
+ return json;
2855
+ } else if (json.type === "image") {
2856
+ return json;
2857
+ }
2858
+ }
2859
+ return undefined;
2860
+ }
2861
+ async getFiles(filePath, query, _cookies, reqHeaders) {
2862
+ const url = new URL(`/v1/files/${this.options.remote}${filePath}`, this.options.valContentUrl);
2863
+ if (typeof query.sha256 === "string") {
2864
+ url.searchParams.append("sha256", query.sha256);
2865
+ }
2866
+ const fetchRes = await fetch(url, {
2867
+ headers: {
2868
+ Authorization: `Bearer ${this.options.apiKey}`
2869
+ }
2870
+ });
2871
+ if (fetchRes.status === 200) {
2872
+ // TODO: does this stream data?
2873
+ if (fetchRes.body) {
2874
+ return {
2875
+ status: fetchRes.status,
2876
+ headers: {
2877
+ "Content-Type": fetchRes.headers.get("Content-Type") || "",
2878
+ "Content-Length": fetchRes.headers.get("Content-Length") || "0",
2879
+ "Cache-Control": fetchRes.headers.get("Cache-Control") || ""
2880
+ },
2881
+ body: fetchRes.body
2882
+ };
2858
2883
  } else {
2859
- if (!(reqHeaders.host && reqHeaders["x-forwarded-proto"])) {
2860
- return {
2861
- status: 500,
2862
- json: {
2863
- message: "Missing host or x-forwarded-proto header"
2864
- }
2865
- };
2866
- }
2867
- const host = `${reqHeaders["x-forwarded-proto"]}://${reqHeaders["host"]}`;
2868
- const staticPublicUrl = new URL(filePath.slice("/public".length), host).toString();
2869
- const fetchRes = await fetch(staticPublicUrl, {
2870
- headers: getAuthHeaders(data.token)
2871
- });
2872
- if (fetchRes.status === 200) {
2873
- // TODO: does this stream data?
2874
- if (fetchRes.body) {
2875
- return {
2876
- status: fetchRes.status,
2877
- headers: {
2878
- "Content-Type": fetchRes.headers.get("Content-Type") || "",
2879
- "Content-Length": fetchRes.headers.get("Content-Length") || "0",
2880
- "Cache-Control": "public, max-age=31536000, immutable"
2881
- },
2882
- body: fetchRes.body
2883
- };
2884
- } else {
2885
- return {
2886
- status: 500,
2887
- json: {
2888
- message: "No body in response"
2889
- }
2890
- };
2884
+ return {
2885
+ status: 500,
2886
+ json: {
2887
+ message: "No body in response"
2891
2888
  }
2892
- } else {
2893
- throw new Error("Failed to fetch file: " + filePath);
2894
- }
2889
+ };
2895
2890
  }
2896
- });
2891
+ } else {
2892
+ if (!(reqHeaders.host && reqHeaders["x-forwarded-proto"])) {
2893
+ return {
2894
+ status: 500,
2895
+ json: {
2896
+ message: "Missing host or x-forwarded-proto header"
2897
+ }
2898
+ };
2899
+ }
2900
+ const host = `${reqHeaders["x-forwarded-proto"]}://${reqHeaders["host"]}`;
2901
+ const staticPublicUrl = new URL(filePath.slice("/public".length), host).toString();
2902
+ const fetchRes = await fetch(staticPublicUrl);
2903
+ return {
2904
+ status: fetchRes.status,
2905
+ // forward headers from static public url
2906
+ headers: Object.fromEntries(fetchRes.headers.entries()),
2907
+ body: fetchRes.body
2908
+ };
2909
+ }
2897
2910
  }
2898
2911
  }
2899
2912
  function verifyCallbackReq(stateCookie, queryParams) {
@@ -1452,15 +1452,14 @@ class ValServer {
1452
1452
  async getTree(treePath,
1453
1453
  // TODO: use the params: patch, schema, source now we return everything, every time
1454
1454
  query, cookies, requestHeaders) {
1455
- const ensureRes = await this.ensureInitialized("getTree", cookies);
1455
+ const ensureRes = await debugTiming("ensureInitialized", () => this.ensureInitialized("getTree", cookies));
1456
1456
  if (fp.result.isErr(ensureRes)) {
1457
1457
  return ensureRes.error;
1458
1458
  }
1459
1459
  const applyPatches = query.patch === "true";
1460
- const execValidations = query.validate === "true";
1461
1460
  const includeSource = query.source === "true";
1462
1461
  const includeSchema = query.schema === "true";
1463
- const moduleIds = await this.getAllModules(treePath);
1462
+ const moduleIds = await debugTiming("getAllModules", () => this.getAllModules(treePath));
1464
1463
  let {
1465
1464
  patchIdsByModuleId,
1466
1465
  patchesById,
@@ -1471,7 +1470,7 @@ class ValServer {
1471
1470
  fileUpdates: {}
1472
1471
  };
1473
1472
  if (applyPatches) {
1474
- const res = await this.readPatches(cookies);
1473
+ const res = await debugTiming("readPatches", () => this.readPatches(cookies));
1475
1474
  if (fp.result.isErr(res)) {
1476
1475
  return res.error;
1477
1476
  }
@@ -1479,9 +1478,10 @@ class ValServer {
1479
1478
  patchesById = res.value.patchesById;
1480
1479
  fileUpdates = res.value.fileUpdates;
1481
1480
  }
1482
- const possiblyPatchedContent = await Promise.all(moduleIds.map(async moduleId => {
1483
- return this.applyAllPatchesThenValidate(moduleId, patchIdsByModuleId, patchesById, fileUpdates, cookies, requestHeaders, applyPatches, execValidations, includeSource, includeSchema);
1484
- }));
1481
+ const validate = false;
1482
+ const possiblyPatchedContent = await debugTiming("applyAllPatchesThenValidate", () => Promise.all(moduleIds.map(async moduleId => {
1483
+ return this.applyAllPatchesThenValidate(moduleId, patchIdsByModuleId, patchesById, fileUpdates, cookies, requestHeaders, applyPatches, validate, includeSource, includeSchema);
1484
+ })));
1485
1485
  const modules = Object.fromEntries(possiblyPatchedContent.map(serializedModuleContent => {
1486
1486
  const module = {
1487
1487
  schema: serializedModuleContent.schema,
@@ -1535,7 +1535,6 @@ class ValServer {
1535
1535
  /* */
1536
1536
  async applyAllPatchesThenValidate(moduleId, patchIdsByModuleId, patchesById, fileUpdates, cookies, requestHeaders, applyPatches, validate, includeSource, includeSchema) {
1537
1537
  const serializedModuleContent = await this.getModule(moduleId, {
1538
- validate: validate,
1539
1538
  source: includeSource,
1540
1539
  schema: includeSchema
1541
1540
  });
@@ -1551,38 +1550,40 @@ class ValServer {
1551
1550
  return serializedModuleContent;
1552
1551
  }
1553
1552
  let source = maybeSource;
1554
- for (const patchId of patchIdsByModuleId[moduleId] ?? []) {
1555
- const patch$1 = patchesById[patchId];
1556
- if (!patch$1) {
1557
- continue;
1558
- }
1559
- const patchRes = patch.applyPatch(source, ops, patch$1.filter(core.Internal.notFileOp));
1560
- if (fp.result.isOk(patchRes)) {
1561
- source = patchRes.value;
1562
- } else {
1563
- console.error("Val: got an unexpected error while applying patch. Is there a mismatch in Val versions? Perhaps Val is misconfigured?", {
1564
- patchId,
1565
- moduleId,
1566
- patch: JSON.stringify(patch$1, null, 2),
1567
- error: patchRes.error
1568
- });
1569
- return {
1570
- path: moduleId,
1571
- schema,
1572
- source,
1573
- errors: {
1574
- fatal: [{
1575
- message: "Unexpected error applying patch",
1576
- type: "invalid-patch"
1577
- }]
1578
- }
1579
- };
1553
+ await debugTiming("applyPatches:" + moduleId, async () => {
1554
+ for (const patchId of patchIdsByModuleId[moduleId] ?? []) {
1555
+ const patch$1 = patchesById[patchId];
1556
+ if (!patch$1) {
1557
+ continue;
1558
+ }
1559
+ const patchRes = patch.applyPatch(source, ops, patch$1.filter(core.Internal.notFileOp));
1560
+ if (fp.result.isOk(patchRes)) {
1561
+ source = patchRes.value;
1562
+ } else {
1563
+ console.error("Val: got an unexpected error while applying patch. Is there a mismatch in Val versions? Perhaps Val is misconfigured?", {
1564
+ patchId,
1565
+ moduleId,
1566
+ patch: JSON.stringify(patch$1, null, 2),
1567
+ error: patchRes.error
1568
+ });
1569
+ return {
1570
+ path: moduleId,
1571
+ schema,
1572
+ source,
1573
+ errors: {
1574
+ fatal: [{
1575
+ message: "Unexpected error applying patch",
1576
+ type: "invalid-patch"
1577
+ }]
1578
+ }
1579
+ };
1580
+ }
1580
1581
  }
1581
- }
1582
+ });
1582
1583
  if (validate) {
1583
- const validationErrors = core.deserializeSchema(schema).validate(moduleId, source);
1584
+ const validationErrors = await debugTiming("validate:" + moduleId, async () => core.deserializeSchema(schema).validate(moduleId, source));
1584
1585
  if (validationErrors) {
1585
- const revalidated = await this.revalidateImageAndFileValidation(validationErrors, fileUpdates, cookies, requestHeaders);
1586
+ const revalidated = await debugTiming("revalidate image/file:" + moduleId, async () => this.revalidateImageAndFileValidation(validationErrors, fileUpdates, cookies, requestHeaders));
1586
1587
  return {
1587
1588
  path: moduleId,
1588
1589
  schema,
@@ -1600,7 +1601,6 @@ class ValServer {
1600
1601
  errors: false
1601
1602
  };
1602
1603
  }
1603
-
1604
1604
  // TODO: name this better: we need to check for image and file validation errors
1605
1605
  // since they cannot be handled directly inside the validation function.
1606
1606
  // The reason is that validate will be called inside QuickJS (in the future, hopefully),
@@ -1618,8 +1618,9 @@ class ValServer {
1618
1618
  )) {
1619
1619
  const fileRef = getValidationErrorFileRef(error);
1620
1620
  if (fileRef) {
1621
+ var _fileUpdates$fileRef;
1621
1622
  const filePath = path__namespace["default"].join(this.cwd, fileRef);
1622
- let expectedMetadata;
1623
+ let expectedMetadata = await this.getMetadata(fileRef, (_fileUpdates$fileRef = fileUpdates[fileRef]) === null || _fileUpdates$fileRef === void 0 ? void 0 : _fileUpdates$fileRef.sha256);
1623
1624
 
1624
1625
  // if this is a new file or we have an actual FS, we read the file and get the metadata
1625
1626
  if (!expectedMetadata) {
@@ -1801,7 +1802,7 @@ class ValServer {
1801
1802
  const moduleId = moduleIdStr;
1802
1803
  const serializedModuleContent = await this.applyAllPatchesThenValidate(moduleId, filterPatchesByModuleIdRes.data.patches ||
1803
1804
  // TODO: refine to ModuleId and PatchId when parsing
1804
- patchIdsByModuleId, patchesById, fileUpdates, cookies, requestHeaders, true, true, true, true);
1805
+ patchIdsByModuleId, patchesById, fileUpdates, cookies, requestHeaders, true, true, true, commit);
1805
1806
  if (serializedModuleContent.errors) {
1806
1807
  validationErrorsByModuleId[moduleId] = serializedModuleContent;
1807
1808
  }
@@ -2034,6 +2035,16 @@ function guessMimeTypeFromPath(filePath) {
2034
2035
  function isCachedPatchFileOp(op) {
2035
2036
  return !!(op.op === "file" && typeof op.filePath === "string" && op.value && typeof op.value === "object" && !Array.isArray(op.value) && "sha256" in op.value && typeof op.value.sha256 === "string");
2036
2037
  }
2038
+ async function debugTiming(id, fn) {
2039
+ if (process.env["VAL_DEBUG_TIMING"] === "true") {
2040
+ const start = Date.now();
2041
+ const r = await fn();
2042
+ console.log(`Timing: ${id} took: ${Date.now() - start}ms (${new Date().toISOString()})`);
2043
+ return r;
2044
+ } else {
2045
+ return fn();
2046
+ }
2047
+ }
2037
2048
 
2038
2049
  const textEncoder = new TextEncoder();
2039
2050
  class LocalValServer extends ValServer {
@@ -2156,6 +2167,9 @@ class LocalValServer extends ValServer {
2156
2167
  json: res
2157
2168
  };
2158
2169
  }
2170
+ async getMetadata() {
2171
+ return undefined;
2172
+ }
2159
2173
  async getFiles(filePath, query) {
2160
2174
  if (query.sha256) {
2161
2175
  const fileExists = this.host.fileExists(this.getFilePath(filePath, query.sha256));
@@ -2307,7 +2321,10 @@ class LocalValServer extends ValServer {
2307
2321
  return fp.result.ok(undefined);
2308
2322
  }
2309
2323
  getModule(moduleId, options) {
2310
- return this.options.service.get(moduleId, "", options);
2324
+ return this.options.service.get(moduleId, "", {
2325
+ ...options,
2326
+ validate: false
2327
+ });
2311
2328
  }
2312
2329
  async getAllModules(treePath) {
2313
2330
  const moduleIds = this.host.readDirectory(this.cwd, ["ts", "js"], ["node_modules", ".*"], ["**/*.val.ts", "**/*.val.js"]).filter(file => {
@@ -2824,76 +2841,72 @@ class ProxyValServer extends ValServer {
2824
2841
  }
2825
2842
  });
2826
2843
  }
2827
- async getFiles(filePath, query, cookies, reqHeaders) {
2828
- return withAuth(this.options.valSecret, cookies, "getFiles", async data => {
2829
- const url = new URL(`/v1/files/${this.options.remote}${filePath}`, this.options.valContentUrl);
2830
- if (typeof query.sha256 === "string") {
2831
- url.searchParams.append("sha256", query.sha256);
2832
- } else {
2833
- console.warn("Missing sha256 query param");
2844
+ async getMetadata(filePath, sha256) {
2845
+ const url = new URL(`/v1/metadata/${this.options.remote}${filePath}?commit=${this.options.git.commit}${sha256 ? `&sha256=${sha256}` : ""}`, this.options.valContentUrl);
2846
+ const fetchRes = await fetch(url, {
2847
+ headers: {
2848
+ Authorization: `Bearer ${this.options.apiKey}`
2834
2849
  }
2835
- const fetchRes = await fetch(url, {
2836
- headers: getAuthHeaders(data.token)
2837
- });
2838
- if (fetchRes.status === 200) {
2839
- // TODO: does this stream data?
2840
- if (fetchRes.body) {
2841
- return {
2842
- status: fetchRes.status,
2843
- headers: {
2844
- "Content-Type": fetchRes.headers.get("Content-Type") || "",
2845
- "Content-Length": fetchRes.headers.get("Content-Length") || "0",
2846
- "Cache-Control": "public, max-age=31536000, immutable"
2847
- },
2848
- body: fetchRes.body
2849
- };
2850
- } else {
2851
- return {
2852
- status: 500,
2853
- json: {
2854
- message: "No body in response"
2855
- }
2856
- };
2857
- }
2850
+ });
2851
+ if (fetchRes.status === 200) {
2852
+ const json = await fetchRes.json();
2853
+ if (json.type === "file") {
2854
+ return json;
2855
+ } else if (json.type === "image") {
2856
+ return json;
2857
+ }
2858
+ }
2859
+ return undefined;
2860
+ }
2861
+ async getFiles(filePath, query, _cookies, reqHeaders) {
2862
+ const url = new URL(`/v1/files/${this.options.remote}${filePath}`, this.options.valContentUrl);
2863
+ if (typeof query.sha256 === "string") {
2864
+ url.searchParams.append("sha256", query.sha256);
2865
+ }
2866
+ const fetchRes = await fetch(url, {
2867
+ headers: {
2868
+ Authorization: `Bearer ${this.options.apiKey}`
2869
+ }
2870
+ });
2871
+ if (fetchRes.status === 200) {
2872
+ // TODO: does this stream data?
2873
+ if (fetchRes.body) {
2874
+ return {
2875
+ status: fetchRes.status,
2876
+ headers: {
2877
+ "Content-Type": fetchRes.headers.get("Content-Type") || "",
2878
+ "Content-Length": fetchRes.headers.get("Content-Length") || "0",
2879
+ "Cache-Control": fetchRes.headers.get("Cache-Control") || ""
2880
+ },
2881
+ body: fetchRes.body
2882
+ };
2858
2883
  } else {
2859
- if (!(reqHeaders.host && reqHeaders["x-forwarded-proto"])) {
2860
- return {
2861
- status: 500,
2862
- json: {
2863
- message: "Missing host or x-forwarded-proto header"
2864
- }
2865
- };
2866
- }
2867
- const host = `${reqHeaders["x-forwarded-proto"]}://${reqHeaders["host"]}`;
2868
- const staticPublicUrl = new URL(filePath.slice("/public".length), host).toString();
2869
- const fetchRes = await fetch(staticPublicUrl, {
2870
- headers: getAuthHeaders(data.token)
2871
- });
2872
- if (fetchRes.status === 200) {
2873
- // TODO: does this stream data?
2874
- if (fetchRes.body) {
2875
- return {
2876
- status: fetchRes.status,
2877
- headers: {
2878
- "Content-Type": fetchRes.headers.get("Content-Type") || "",
2879
- "Content-Length": fetchRes.headers.get("Content-Length") || "0",
2880
- "Cache-Control": "public, max-age=31536000, immutable"
2881
- },
2882
- body: fetchRes.body
2883
- };
2884
- } else {
2885
- return {
2886
- status: 500,
2887
- json: {
2888
- message: "No body in response"
2889
- }
2890
- };
2884
+ return {
2885
+ status: 500,
2886
+ json: {
2887
+ message: "No body in response"
2891
2888
  }
2892
- } else {
2893
- throw new Error("Failed to fetch file: " + filePath);
2894
- }
2889
+ };
2895
2890
  }
2896
- });
2891
+ } else {
2892
+ if (!(reqHeaders.host && reqHeaders["x-forwarded-proto"])) {
2893
+ return {
2894
+ status: 500,
2895
+ json: {
2896
+ message: "Missing host or x-forwarded-proto header"
2897
+ }
2898
+ };
2899
+ }
2900
+ const host = `${reqHeaders["x-forwarded-proto"]}://${reqHeaders["host"]}`;
2901
+ const staticPublicUrl = new URL(filePath.slice("/public".length), host).toString();
2902
+ const fetchRes = await fetch(staticPublicUrl);
2903
+ return {
2904
+ status: fetchRes.status,
2905
+ // forward headers from static public url
2906
+ headers: Object.fromEntries(fetchRes.headers.entries()),
2907
+ body: fetchRes.body
2908
+ };
2909
+ }
2897
2910
  }
2898
2911
  }
2899
2912
  function verifyCallbackReq(stateCookie, queryParams) {
@@ -1422,15 +1422,14 @@ class ValServer {
1422
1422
  async getTree(treePath,
1423
1423
  // TODO: use the params: patch, schema, source now we return everything, every time
1424
1424
  query, cookies, requestHeaders) {
1425
- const ensureRes = await this.ensureInitialized("getTree", cookies);
1425
+ const ensureRes = await debugTiming("ensureInitialized", () => this.ensureInitialized("getTree", cookies));
1426
1426
  if (result.isErr(ensureRes)) {
1427
1427
  return ensureRes.error;
1428
1428
  }
1429
1429
  const applyPatches = query.patch === "true";
1430
- const execValidations = query.validate === "true";
1431
1430
  const includeSource = query.source === "true";
1432
1431
  const includeSchema = query.schema === "true";
1433
- const moduleIds = await this.getAllModules(treePath);
1432
+ const moduleIds = await debugTiming("getAllModules", () => this.getAllModules(treePath));
1434
1433
  let {
1435
1434
  patchIdsByModuleId,
1436
1435
  patchesById,
@@ -1441,7 +1440,7 @@ class ValServer {
1441
1440
  fileUpdates: {}
1442
1441
  };
1443
1442
  if (applyPatches) {
1444
- const res = await this.readPatches(cookies);
1443
+ const res = await debugTiming("readPatches", () => this.readPatches(cookies));
1445
1444
  if (result.isErr(res)) {
1446
1445
  return res.error;
1447
1446
  }
@@ -1449,9 +1448,10 @@ class ValServer {
1449
1448
  patchesById = res.value.patchesById;
1450
1449
  fileUpdates = res.value.fileUpdates;
1451
1450
  }
1452
- const possiblyPatchedContent = await Promise.all(moduleIds.map(async moduleId => {
1453
- return this.applyAllPatchesThenValidate(moduleId, patchIdsByModuleId, patchesById, fileUpdates, cookies, requestHeaders, applyPatches, execValidations, includeSource, includeSchema);
1454
- }));
1451
+ const validate = false;
1452
+ const possiblyPatchedContent = await debugTiming("applyAllPatchesThenValidate", () => Promise.all(moduleIds.map(async moduleId => {
1453
+ return this.applyAllPatchesThenValidate(moduleId, patchIdsByModuleId, patchesById, fileUpdates, cookies, requestHeaders, applyPatches, validate, includeSource, includeSchema);
1454
+ })));
1455
1455
  const modules = Object.fromEntries(possiblyPatchedContent.map(serializedModuleContent => {
1456
1456
  const module = {
1457
1457
  schema: serializedModuleContent.schema,
@@ -1505,7 +1505,6 @@ class ValServer {
1505
1505
  /* */
1506
1506
  async applyAllPatchesThenValidate(moduleId, patchIdsByModuleId, patchesById, fileUpdates, cookies, requestHeaders, applyPatches, validate, includeSource, includeSchema) {
1507
1507
  const serializedModuleContent = await this.getModule(moduleId, {
1508
- validate: validate,
1509
1508
  source: includeSource,
1510
1509
  schema: includeSchema
1511
1510
  });
@@ -1521,38 +1520,40 @@ class ValServer {
1521
1520
  return serializedModuleContent;
1522
1521
  }
1523
1522
  let source = maybeSource;
1524
- for (const patchId of patchIdsByModuleId[moduleId] ?? []) {
1525
- const patch = patchesById[patchId];
1526
- if (!patch) {
1527
- continue;
1528
- }
1529
- const patchRes = applyPatch(source, ops, patch.filter(Internal.notFileOp));
1530
- if (result.isOk(patchRes)) {
1531
- source = patchRes.value;
1532
- } else {
1533
- console.error("Val: got an unexpected error while applying patch. Is there a mismatch in Val versions? Perhaps Val is misconfigured?", {
1534
- patchId,
1535
- moduleId,
1536
- patch: JSON.stringify(patch, null, 2),
1537
- error: patchRes.error
1538
- });
1539
- return {
1540
- path: moduleId,
1541
- schema,
1542
- source,
1543
- errors: {
1544
- fatal: [{
1545
- message: "Unexpected error applying patch",
1546
- type: "invalid-patch"
1547
- }]
1548
- }
1549
- };
1523
+ await debugTiming("applyPatches:" + moduleId, async () => {
1524
+ for (const patchId of patchIdsByModuleId[moduleId] ?? []) {
1525
+ const patch = patchesById[patchId];
1526
+ if (!patch) {
1527
+ continue;
1528
+ }
1529
+ const patchRes = applyPatch(source, ops, patch.filter(Internal.notFileOp));
1530
+ if (result.isOk(patchRes)) {
1531
+ source = patchRes.value;
1532
+ } else {
1533
+ console.error("Val: got an unexpected error while applying patch. Is there a mismatch in Val versions? Perhaps Val is misconfigured?", {
1534
+ patchId,
1535
+ moduleId,
1536
+ patch: JSON.stringify(patch, null, 2),
1537
+ error: patchRes.error
1538
+ });
1539
+ return {
1540
+ path: moduleId,
1541
+ schema,
1542
+ source,
1543
+ errors: {
1544
+ fatal: [{
1545
+ message: "Unexpected error applying patch",
1546
+ type: "invalid-patch"
1547
+ }]
1548
+ }
1549
+ };
1550
+ }
1550
1551
  }
1551
- }
1552
+ });
1552
1553
  if (validate) {
1553
- const validationErrors = deserializeSchema(schema).validate(moduleId, source);
1554
+ const validationErrors = await debugTiming("validate:" + moduleId, async () => deserializeSchema(schema).validate(moduleId, source));
1554
1555
  if (validationErrors) {
1555
- const revalidated = await this.revalidateImageAndFileValidation(validationErrors, fileUpdates, cookies, requestHeaders);
1556
+ const revalidated = await debugTiming("revalidate image/file:" + moduleId, async () => this.revalidateImageAndFileValidation(validationErrors, fileUpdates, cookies, requestHeaders));
1556
1557
  return {
1557
1558
  path: moduleId,
1558
1559
  schema,
@@ -1570,7 +1571,6 @@ class ValServer {
1570
1571
  errors: false
1571
1572
  };
1572
1573
  }
1573
-
1574
1574
  // TODO: name this better: we need to check for image and file validation errors
1575
1575
  // since they cannot be handled directly inside the validation function.
1576
1576
  // The reason is that validate will be called inside QuickJS (in the future, hopefully),
@@ -1588,8 +1588,9 @@ class ValServer {
1588
1588
  )) {
1589
1589
  const fileRef = getValidationErrorFileRef(error);
1590
1590
  if (fileRef) {
1591
+ var _fileUpdates$fileRef;
1591
1592
  const filePath = path__default.join(this.cwd, fileRef);
1592
- let expectedMetadata;
1593
+ let expectedMetadata = await this.getMetadata(fileRef, (_fileUpdates$fileRef = fileUpdates[fileRef]) === null || _fileUpdates$fileRef === void 0 ? void 0 : _fileUpdates$fileRef.sha256);
1593
1594
 
1594
1595
  // if this is a new file or we have an actual FS, we read the file and get the metadata
1595
1596
  if (!expectedMetadata) {
@@ -1771,7 +1772,7 @@ class ValServer {
1771
1772
  const moduleId = moduleIdStr;
1772
1773
  const serializedModuleContent = await this.applyAllPatchesThenValidate(moduleId, filterPatchesByModuleIdRes.data.patches ||
1773
1774
  // TODO: refine to ModuleId and PatchId when parsing
1774
- patchIdsByModuleId, patchesById, fileUpdates, cookies, requestHeaders, true, true, true, true);
1775
+ patchIdsByModuleId, patchesById, fileUpdates, cookies, requestHeaders, true, true, true, commit);
1775
1776
  if (serializedModuleContent.errors) {
1776
1777
  validationErrorsByModuleId[moduleId] = serializedModuleContent;
1777
1778
  }
@@ -2004,6 +2005,16 @@ function guessMimeTypeFromPath(filePath) {
2004
2005
  function isCachedPatchFileOp(op) {
2005
2006
  return !!(op.op === "file" && typeof op.filePath === "string" && op.value && typeof op.value === "object" && !Array.isArray(op.value) && "sha256" in op.value && typeof op.value.sha256 === "string");
2006
2007
  }
2008
+ async function debugTiming(id, fn) {
2009
+ if (process.env["VAL_DEBUG_TIMING"] === "true") {
2010
+ const start = Date.now();
2011
+ const r = await fn();
2012
+ console.log(`Timing: ${id} took: ${Date.now() - start}ms (${new Date().toISOString()})`);
2013
+ return r;
2014
+ } else {
2015
+ return fn();
2016
+ }
2017
+ }
2007
2018
 
2008
2019
  const textEncoder = new TextEncoder();
2009
2020
  class LocalValServer extends ValServer {
@@ -2126,6 +2137,9 @@ class LocalValServer extends ValServer {
2126
2137
  json: res
2127
2138
  };
2128
2139
  }
2140
+ async getMetadata() {
2141
+ return undefined;
2142
+ }
2129
2143
  async getFiles(filePath, query) {
2130
2144
  if (query.sha256) {
2131
2145
  const fileExists = this.host.fileExists(this.getFilePath(filePath, query.sha256));
@@ -2277,7 +2291,10 @@ class LocalValServer extends ValServer {
2277
2291
  return result.ok(undefined);
2278
2292
  }
2279
2293
  getModule(moduleId, options) {
2280
- return this.options.service.get(moduleId, "", options);
2294
+ return this.options.service.get(moduleId, "", {
2295
+ ...options,
2296
+ validate: false
2297
+ });
2281
2298
  }
2282
2299
  async getAllModules(treePath) {
2283
2300
  const moduleIds = this.host.readDirectory(this.cwd, ["ts", "js"], ["node_modules", ".*"], ["**/*.val.ts", "**/*.val.js"]).filter(file => {
@@ -2794,76 +2811,72 @@ class ProxyValServer extends ValServer {
2794
2811
  }
2795
2812
  });
2796
2813
  }
2797
- async getFiles(filePath, query, cookies, reqHeaders) {
2798
- return withAuth(this.options.valSecret, cookies, "getFiles", async data => {
2799
- const url = new URL(`/v1/files/${this.options.remote}${filePath}`, this.options.valContentUrl);
2800
- if (typeof query.sha256 === "string") {
2801
- url.searchParams.append("sha256", query.sha256);
2802
- } else {
2803
- console.warn("Missing sha256 query param");
2814
+ async getMetadata(filePath, sha256) {
2815
+ const url = new URL(`/v1/metadata/${this.options.remote}${filePath}?commit=${this.options.git.commit}${sha256 ? `&sha256=${sha256}` : ""}`, this.options.valContentUrl);
2816
+ const fetchRes = await fetch(url, {
2817
+ headers: {
2818
+ Authorization: `Bearer ${this.options.apiKey}`
2804
2819
  }
2805
- const fetchRes = await fetch(url, {
2806
- headers: getAuthHeaders(data.token)
2807
- });
2808
- if (fetchRes.status === 200) {
2809
- // TODO: does this stream data?
2810
- if (fetchRes.body) {
2811
- return {
2812
- status: fetchRes.status,
2813
- headers: {
2814
- "Content-Type": fetchRes.headers.get("Content-Type") || "",
2815
- "Content-Length": fetchRes.headers.get("Content-Length") || "0",
2816
- "Cache-Control": "public, max-age=31536000, immutable"
2817
- },
2818
- body: fetchRes.body
2819
- };
2820
- } else {
2821
- return {
2822
- status: 500,
2823
- json: {
2824
- message: "No body in response"
2825
- }
2826
- };
2827
- }
2820
+ });
2821
+ if (fetchRes.status === 200) {
2822
+ const json = await fetchRes.json();
2823
+ if (json.type === "file") {
2824
+ return json;
2825
+ } else if (json.type === "image") {
2826
+ return json;
2827
+ }
2828
+ }
2829
+ return undefined;
2830
+ }
2831
+ async getFiles(filePath, query, _cookies, reqHeaders) {
2832
+ const url = new URL(`/v1/files/${this.options.remote}${filePath}`, this.options.valContentUrl);
2833
+ if (typeof query.sha256 === "string") {
2834
+ url.searchParams.append("sha256", query.sha256);
2835
+ }
2836
+ const fetchRes = await fetch(url, {
2837
+ headers: {
2838
+ Authorization: `Bearer ${this.options.apiKey}`
2839
+ }
2840
+ });
2841
+ if (fetchRes.status === 200) {
2842
+ // TODO: does this stream data?
2843
+ if (fetchRes.body) {
2844
+ return {
2845
+ status: fetchRes.status,
2846
+ headers: {
2847
+ "Content-Type": fetchRes.headers.get("Content-Type") || "",
2848
+ "Content-Length": fetchRes.headers.get("Content-Length") || "0",
2849
+ "Cache-Control": fetchRes.headers.get("Cache-Control") || ""
2850
+ },
2851
+ body: fetchRes.body
2852
+ };
2828
2853
  } else {
2829
- if (!(reqHeaders.host && reqHeaders["x-forwarded-proto"])) {
2830
- return {
2831
- status: 500,
2832
- json: {
2833
- message: "Missing host or x-forwarded-proto header"
2834
- }
2835
- };
2836
- }
2837
- const host = `${reqHeaders["x-forwarded-proto"]}://${reqHeaders["host"]}`;
2838
- const staticPublicUrl = new URL(filePath.slice("/public".length), host).toString();
2839
- const fetchRes = await fetch(staticPublicUrl, {
2840
- headers: getAuthHeaders(data.token)
2841
- });
2842
- if (fetchRes.status === 200) {
2843
- // TODO: does this stream data?
2844
- if (fetchRes.body) {
2845
- return {
2846
- status: fetchRes.status,
2847
- headers: {
2848
- "Content-Type": fetchRes.headers.get("Content-Type") || "",
2849
- "Content-Length": fetchRes.headers.get("Content-Length") || "0",
2850
- "Cache-Control": "public, max-age=31536000, immutable"
2851
- },
2852
- body: fetchRes.body
2853
- };
2854
- } else {
2855
- return {
2856
- status: 500,
2857
- json: {
2858
- message: "No body in response"
2859
- }
2860
- };
2854
+ return {
2855
+ status: 500,
2856
+ json: {
2857
+ message: "No body in response"
2861
2858
  }
2862
- } else {
2863
- throw new Error("Failed to fetch file: " + filePath);
2864
- }
2859
+ };
2865
2860
  }
2866
- });
2861
+ } else {
2862
+ if (!(reqHeaders.host && reqHeaders["x-forwarded-proto"])) {
2863
+ return {
2864
+ status: 500,
2865
+ json: {
2866
+ message: "Missing host or x-forwarded-proto header"
2867
+ }
2868
+ };
2869
+ }
2870
+ const host = `${reqHeaders["x-forwarded-proto"]}://${reqHeaders["host"]}`;
2871
+ const staticPublicUrl = new URL(filePath.slice("/public".length), host).toString();
2872
+ const fetchRes = await fetch(staticPublicUrl);
2873
+ return {
2874
+ status: fetchRes.status,
2875
+ // forward headers from static public url
2876
+ headers: Object.fromEntries(fetchRes.headers.entries()),
2877
+ body: fetchRes.body
2878
+ };
2879
+ }
2867
2880
  }
2868
2881
  }
2869
2882
  function verifyCallbackReq(stateCookie, queryParams) {
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.60.22",
15
+ "version": "0.60.24",
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.60.22",
28
- "@valbuild/shared": "~0.60.22",
29
- "@valbuild/ui": "~0.60.22",
27
+ "@valbuild/core": "~0.60.24",
28
+ "@valbuild/shared": "~0.60.24",
29
+ "@valbuild/ui": "~0.60.24",
30
30
  "express": "^4.18.2",
31
31
  "image-size": "^1.0.2",
32
32
  "minimatch": "^3.0.4",