@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
|
1483
|
-
|
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
|
-
|
1555
|
-
const
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1568
|
-
|
1569
|
-
|
1570
|
-
|
1571
|
-
|
1572
|
-
|
1573
|
-
|
1574
|
-
|
1575
|
-
|
1576
|
-
|
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,
|
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, "",
|
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
|
2828
|
-
|
2829
|
-
|
2830
|
-
|
2831
|
-
|
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
|
-
|
2836
|
-
|
2837
|
-
|
2838
|
-
if (
|
2839
|
-
|
2840
|
-
|
2841
|
-
|
2842
|
-
|
2843
|
-
|
2844
|
-
|
2845
|
-
|
2846
|
-
|
2847
|
-
|
2848
|
-
|
2849
|
-
|
2850
|
-
|
2851
|
-
|
2852
|
-
|
2853
|
-
|
2854
|
-
|
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
|
-
|
2860
|
-
|
2861
|
-
|
2862
|
-
|
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
|
-
}
|
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
|
1483
|
-
|
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
|
-
|
1555
|
-
const
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1568
|
-
|
1569
|
-
|
1570
|
-
|
1571
|
-
|
1572
|
-
|
1573
|
-
|
1574
|
-
|
1575
|
-
|
1576
|
-
|
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,
|
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, "",
|
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
|
2828
|
-
|
2829
|
-
|
2830
|
-
|
2831
|
-
|
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
|
-
|
2836
|
-
|
2837
|
-
|
2838
|
-
if (
|
2839
|
-
|
2840
|
-
|
2841
|
-
|
2842
|
-
|
2843
|
-
|
2844
|
-
|
2845
|
-
|
2846
|
-
|
2847
|
-
|
2848
|
-
|
2849
|
-
|
2850
|
-
|
2851
|
-
|
2852
|
-
|
2853
|
-
|
2854
|
-
|
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
|
-
|
2860
|
-
|
2861
|
-
|
2862
|
-
|
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
|
-
}
|
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
|
1453
|
-
|
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
|
-
|
1525
|
-
const
|
1526
|
-
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
1532
|
-
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
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,
|
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, "",
|
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
|
2798
|
-
|
2799
|
-
|
2800
|
-
|
2801
|
-
|
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
|
-
|
2806
|
-
|
2807
|
-
|
2808
|
-
if (
|
2809
|
-
|
2810
|
-
|
2811
|
-
|
2812
|
-
|
2813
|
-
|
2814
|
-
|
2815
|
-
|
2816
|
-
|
2817
|
-
|
2818
|
-
|
2819
|
-
|
2820
|
-
|
2821
|
-
|
2822
|
-
|
2823
|
-
|
2824
|
-
|
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
|
-
|
2830
|
-
|
2831
|
-
|
2832
|
-
|
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
|
-
}
|
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.
|
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.
|
28
|
-
"@valbuild/shared": "~0.60.
|
29
|
-
"@valbuild/ui": "~0.60.
|
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",
|