@valbuild/server 0.16.3 → 0.17.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.
- package/dist/declarations/src/LocalValServer.d.ts +8 -4
- package/dist/declarations/src/ValServer.d.ts +4 -4
- package/dist/declarations/src/hosting.d.ts +34 -0
- package/dist/declarations/src/index.d.ts +1 -1
- package/dist/declarations/src/patchValFile.d.ts +1 -1
- package/dist/valbuild-server.cjs.dev.js +324 -116
- package/dist/valbuild-server.cjs.prod.js +324 -116
- package/dist/valbuild-server.esm.js +308 -118
- package/package.json +3 -3
- package/src/LocalValServer.ts +89 -19
- package/src/ProxyValServer.ts +86 -28
- package/src/Service.ts +4 -3
- package/src/ValQuickJSRuntime.ts +18 -0
- package/src/ValServer.ts +6 -6
- package/src/createRequestHandler.ts +7 -5
- package/src/hosting.ts +122 -1
- package/src/index.ts +1 -1
- package/src/patch/ts/ops.test.ts +0 -1
- package/src/patchValFile.ts +8 -1
- package/src/readValFile.ts +2 -2
|
@@ -3,8 +3,9 @@ import ts from 'typescript';
|
|
|
3
3
|
import { result, pipe } from '@valbuild/core/fp';
|
|
4
4
|
import { FILE_REF_PROP, derefPatch, Internal, Schema } from '@valbuild/core';
|
|
5
5
|
import { deepEqual, isNotRoot, PatchError, parseAndValidateArrayIndex, applyPatch, parsePatch, sourceToPatchPath } from '@valbuild/core/patch';
|
|
6
|
-
import path from 'path';
|
|
7
|
-
import
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import path__default from 'path';
|
|
8
|
+
import fs, { promises } from 'fs';
|
|
8
9
|
import express, { Router } from 'express';
|
|
9
10
|
import { createRequestHandler as createRequestHandler$1 } from '@valbuild/ui/server';
|
|
10
11
|
import z, { z as z$1 } from 'zod';
|
|
@@ -558,16 +559,16 @@ import { Internal } from "@valbuild/core";
|
|
|
558
559
|
globalThis.valModule = {
|
|
559
560
|
id: valModule?.default && Internal.getValPath(valModule?.default),
|
|
560
561
|
schema: valModule?.default && Internal.getSchema(valModule?.default)?.serialize(),
|
|
561
|
-
source: valModule?.default && Internal.
|
|
562
|
+
source: valModule?.default && Internal.getSource(valModule?.default),
|
|
562
563
|
validation: valModule?.default && Internal.getSchema(valModule?.default)?.validate(
|
|
563
564
|
valModule?.default && Internal.getValPath(valModule?.default) || "/",
|
|
564
|
-
valModule?.default && Internal.
|
|
565
|
+
valModule?.default && Internal.getSource(valModule?.default)
|
|
565
566
|
)
|
|
566
567
|
};
|
|
567
568
|
`;
|
|
568
569
|
const result = context.evalCode(code,
|
|
569
570
|
// Synthetic module name
|
|
570
|
-
|
|
571
|
+
path__default.join(path__default.dirname(valConfigPath), "<val>"));
|
|
571
572
|
const fatalErrors = [];
|
|
572
573
|
if (result.error) {
|
|
573
574
|
const error = result.error.consume(context.dump);
|
|
@@ -672,12 +673,15 @@ function convertDataUrlToBase64(dataUrl) {
|
|
|
672
673
|
return Buffer.from(base64, "base64");
|
|
673
674
|
}
|
|
674
675
|
const patchSourceFile = (sourceFile, patch) => {
|
|
676
|
+
if (typeof sourceFile === "string") {
|
|
677
|
+
return applyPatch(ts.createSourceFile("<val>", sourceFile, ts.ScriptTarget.ES2015), ops, patch);
|
|
678
|
+
}
|
|
675
679
|
return applyPatch(sourceFile, ops, patch);
|
|
676
680
|
};
|
|
677
681
|
|
|
678
682
|
const getCompilerOptions = (rootDir, parseConfigHost) => {
|
|
679
|
-
const tsConfigPath =
|
|
680
|
-
const jsConfigPath =
|
|
683
|
+
const tsConfigPath = path__default.resolve(rootDir, "tsconfig.json");
|
|
684
|
+
const jsConfigPath = path__default.resolve(rootDir, "jsconfig.json");
|
|
681
685
|
let configFilePath;
|
|
682
686
|
if (parseConfigHost.fileExists(jsConfigPath)) {
|
|
683
687
|
configFilePath = jsConfigPath;
|
|
@@ -727,7 +731,7 @@ class ValSourceFileHandler {
|
|
|
727
731
|
this.host.writeFile(filePath, content, encoding);
|
|
728
732
|
}
|
|
729
733
|
resolveSourceModulePath(containingFilePath, requestedModuleName) {
|
|
730
|
-
const resolutionRes = ts.resolveModuleName(requestedModuleName,
|
|
734
|
+
const resolutionRes = ts.resolveModuleName(requestedModuleName, path__default.isAbsolute(containingFilePath) ? containingFilePath : path__default.resolve(this.projectRoot, containingFilePath), this.compilerOptions, this.host, undefined, undefined, ts.ModuleKind.ESNext);
|
|
731
735
|
const resolvedModule = resolutionRes.resolvedModule;
|
|
732
736
|
if (!resolvedModule) {
|
|
733
737
|
throw Error(`Could not resolve module "${requestedModuleName}", base: "${containingFilePath}": No resolved modules returned: ${JSON.stringify(resolutionRes)}`);
|
|
@@ -847,6 +851,16 @@ async function newValQuickJSRuntime(quickJSModule, moduleLoader, {
|
|
|
847
851
|
value: "export const useVal = () => { throw Error(`Cannot use 'useVal' in this type of file`) }; export const fetchVal = () => { throw Error(`Cannot use 'fetchVal' in this type of file`) }; export const autoTagJSX = () => { /* ignore */ };"
|
|
848
852
|
};
|
|
849
853
|
}
|
|
854
|
+
if (modulePath.startsWith("next")) {
|
|
855
|
+
return {
|
|
856
|
+
value: "export default new Proxy({}, { get() { return () => { throw new Error(`Cannot import 'next' in this file`) } } } )"
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
if (modulePath.startsWith("react")) {
|
|
860
|
+
return {
|
|
861
|
+
value: "export default new Proxy({}, { get() { return () => { throw new Error(`Cannot import 'react' in this file`) } } } )"
|
|
862
|
+
};
|
|
863
|
+
}
|
|
850
864
|
return {
|
|
851
865
|
value: moduleLoader.getModule(modulePath)
|
|
852
866
|
};
|
|
@@ -867,6 +881,16 @@ async function newValQuickJSRuntime(quickJSModule, moduleLoader, {
|
|
|
867
881
|
value: requestedName
|
|
868
882
|
};
|
|
869
883
|
}
|
|
884
|
+
if (requestedName.startsWith("next")) {
|
|
885
|
+
return {
|
|
886
|
+
value: requestedName
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
if (requestedName.startsWith("react")) {
|
|
890
|
+
return {
|
|
891
|
+
value: requestedName
|
|
892
|
+
};
|
|
893
|
+
}
|
|
870
894
|
const modulePath = moduleLoader.resolveModulePath(baseModuleName, requestedName);
|
|
871
895
|
return {
|
|
872
896
|
value: modulePath
|
|
@@ -903,7 +927,7 @@ class Service {
|
|
|
903
927
|
const valModule = await readValFile(moduleId, this.valConfigPath, this.runtime);
|
|
904
928
|
if (valModule.source && valModule.schema) {
|
|
905
929
|
const resolved = Internal.resolvePath(modulePath, valModule.source, valModule.schema);
|
|
906
|
-
const sourcePath = [moduleId, resolved.path].join(".");
|
|
930
|
+
const sourcePath = resolved.path ? [moduleId, resolved.path].join(".") : moduleId;
|
|
907
931
|
return {
|
|
908
932
|
path: sourcePath,
|
|
909
933
|
schema: resolved.schema instanceof Schema ? resolved.schema.serialize() : resolved.schema,
|
|
@@ -934,15 +958,21 @@ function createRequestHandler(valServer) {
|
|
|
934
958
|
router.get("/authorize", valServer.authorize.bind(valServer));
|
|
935
959
|
router.get("/callback", valServer.callback.bind(valServer));
|
|
936
960
|
router.get("/logout", valServer.logout.bind(valServer));
|
|
937
|
-
router.
|
|
938
|
-
|
|
939
|
-
type: "application/json-patch+json",
|
|
961
|
+
router.post("/patches/*", express.json({
|
|
962
|
+
type: "application/json",
|
|
940
963
|
limit: "10mb"
|
|
941
|
-
}), valServer.
|
|
964
|
+
}), valServer.postPatches.bind(valServer));
|
|
942
965
|
router.post("/commit", valServer.commit.bind(valServer));
|
|
966
|
+
router.get("/enable", valServer.enable.bind(valServer));
|
|
967
|
+
router.get("/disable", valServer.disable.bind(valServer));
|
|
968
|
+
router.get("/tree/*", valServer.getTree.bind(valServer));
|
|
943
969
|
return router;
|
|
944
970
|
}
|
|
945
971
|
|
|
972
|
+
function getPathFromParams(params) {
|
|
973
|
+
return `/${params[0]}`;
|
|
974
|
+
}
|
|
975
|
+
|
|
946
976
|
const JSONValueT = z.lazy(() => z.union([z.string(), z.number(), z.boolean(), z.null(), z.array(JSONValueT), z.record(JSONValueT)]));
|
|
947
977
|
|
|
948
978
|
/**
|
|
@@ -980,75 +1010,6 @@ const OperationJSONT = z.discriminatedUnion("op", [z.object({
|
|
|
980
1010
|
}).strict()]);
|
|
981
1011
|
const PatchJSON = z.array(OperationJSONT);
|
|
982
1012
|
|
|
983
|
-
function getPathFromParams(params) {
|
|
984
|
-
return `/${params[0]}`;
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
class LocalValServer {
|
|
988
|
-
constructor(options) {
|
|
989
|
-
this.options = options;
|
|
990
|
-
}
|
|
991
|
-
async session(_req, res) {
|
|
992
|
-
res.json({
|
|
993
|
-
mode: "local"
|
|
994
|
-
});
|
|
995
|
-
}
|
|
996
|
-
async getIds(req, res) {
|
|
997
|
-
try {
|
|
998
|
-
console.log(req.params);
|
|
999
|
-
const path = getPathFromParams(req.params);
|
|
1000
|
-
const [moduleId, modulePath] = Internal.splitModuleIdAndModulePath(path);
|
|
1001
|
-
const valModule = await this.options.service.get(moduleId, modulePath);
|
|
1002
|
-
res.json(valModule);
|
|
1003
|
-
} catch (err) {
|
|
1004
|
-
console.error(err);
|
|
1005
|
-
res.sendStatus(500);
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
async patchIds(req, res) {
|
|
1009
|
-
// First validate that the body has the right structure
|
|
1010
|
-
const patchJSON = PatchJSON.safeParse(req.body);
|
|
1011
|
-
if (!patchJSON.success) {
|
|
1012
|
-
res.status(401).json(patchJSON.error.issues);
|
|
1013
|
-
return;
|
|
1014
|
-
}
|
|
1015
|
-
// Then parse/validate
|
|
1016
|
-
const patch = parsePatch(patchJSON.data);
|
|
1017
|
-
if (result.isErr(patch)) {
|
|
1018
|
-
res.status(401).json(patch.error);
|
|
1019
|
-
return;
|
|
1020
|
-
}
|
|
1021
|
-
const id = getPathFromParams(req.params);
|
|
1022
|
-
try {
|
|
1023
|
-
const valModule = await this.options.service.patch(id, patch.value);
|
|
1024
|
-
res.json(valModule);
|
|
1025
|
-
} catch (err) {
|
|
1026
|
-
if (err instanceof PatchError) {
|
|
1027
|
-
res.status(401).send(err.message);
|
|
1028
|
-
} else {
|
|
1029
|
-
console.error(err);
|
|
1030
|
-
res.status(500).send(err instanceof Error ? err.message : "Unknown error");
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
async badRequest(req, res) {
|
|
1035
|
-
console.debug("Local server does handle this request", req.url);
|
|
1036
|
-
res.sendStatus(400);
|
|
1037
|
-
}
|
|
1038
|
-
commit(req, res) {
|
|
1039
|
-
return this.badRequest(req, res);
|
|
1040
|
-
}
|
|
1041
|
-
authorize(req, res) {
|
|
1042
|
-
return this.badRequest(req, res);
|
|
1043
|
-
}
|
|
1044
|
-
callback(req, res) {
|
|
1045
|
-
return this.badRequest(req, res);
|
|
1046
|
-
}
|
|
1047
|
-
logout(req, res) {
|
|
1048
|
-
return this.badRequest(req, res);
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
1013
|
function decodeJwt(token, secretKey) {
|
|
1053
1014
|
const [headerBase64, payloadBase64, signatureBase64, ...rest] = token.split(".");
|
|
1054
1015
|
if (!headerBase64 || !payloadBase64 || !signatureBase64 || rest.length > 0) {
|
|
@@ -1108,8 +1069,9 @@ function encodeJwt(payload, sessionKey) {
|
|
|
1108
1069
|
return `${jwtHeaderBase64}.${payloadBase64}.${crypto.createHmac("sha256", sessionKey).update(`${jwtHeaderBase64}.${payloadBase64}`).digest("base64")}`;
|
|
1109
1070
|
}
|
|
1110
1071
|
|
|
1111
|
-
const VAL_SESSION_COOKIE =
|
|
1112
|
-
const VAL_STATE_COOKIE =
|
|
1072
|
+
const VAL_SESSION_COOKIE = Internal.VAL_SESSION_COOKIE;
|
|
1073
|
+
const VAL_STATE_COOKIE = Internal.VAL_STATE_COOKIE;
|
|
1074
|
+
const VAL_ENABLED_COOKIE = Internal.VAL_ENABLE_COOKIE_NAME;
|
|
1113
1075
|
class ProxyValServer {
|
|
1114
1076
|
constructor(options) {
|
|
1115
1077
|
this.options = options;
|
|
@@ -1134,6 +1096,12 @@ class ProxyValServer {
|
|
|
1134
1096
|
expires: new Date(Date.now() + 1000 * 60 * 60) // 1 hour
|
|
1135
1097
|
}).redirect(appAuthorizeUrl);
|
|
1136
1098
|
}
|
|
1099
|
+
async enable(req, res) {
|
|
1100
|
+
return enable(req, res, this.options.valEnableRedirectUrl);
|
|
1101
|
+
}
|
|
1102
|
+
async disable(req, res) {
|
|
1103
|
+
return disable(req, res, this.options.valEnableRedirectUrl);
|
|
1104
|
+
}
|
|
1137
1105
|
async callback(req, res) {
|
|
1138
1106
|
const {
|
|
1139
1107
|
success: callbackReqSuccess,
|
|
@@ -1179,7 +1147,6 @@ class ProxyValServer {
|
|
|
1179
1147
|
}
|
|
1180
1148
|
}
|
|
1181
1149
|
async session(req, res) {
|
|
1182
|
-
console.log("hit session");
|
|
1183
1150
|
return this.withAuth(req, res, async data => {
|
|
1184
1151
|
const url = new URL(`/api/val/${this.options.valName}/auth/session`, this.options.valBuildUrl);
|
|
1185
1152
|
const fetchRes = await fetch(url, {
|
|
@@ -1195,30 +1162,38 @@ class ProxyValServer {
|
|
|
1195
1162
|
}
|
|
1196
1163
|
});
|
|
1197
1164
|
}
|
|
1198
|
-
async
|
|
1199
|
-
return this.withAuth(req, res, async
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
} else {
|
|
1210
|
-
res.sendStatus(fetchRes.status);
|
|
1211
|
-
}
|
|
1212
|
-
}).catch(e => {
|
|
1213
|
-
res.status(500).send({
|
|
1214
|
-
error: {
|
|
1215
|
-
message: e === null || e === void 0 ? void 0 : e.message,
|
|
1216
|
-
status: 500
|
|
1217
|
-
}
|
|
1165
|
+
async getTree(req, res) {
|
|
1166
|
+
return this.withAuth(req, res, async data => {
|
|
1167
|
+
const {
|
|
1168
|
+
patch,
|
|
1169
|
+
schema,
|
|
1170
|
+
source
|
|
1171
|
+
} = req.query;
|
|
1172
|
+
const params = new URLSearchParams({
|
|
1173
|
+
patch: (patch === "true").toString(),
|
|
1174
|
+
schema: (schema === "true").toString(),
|
|
1175
|
+
source: (source === "true").toString()
|
|
1218
1176
|
});
|
|
1177
|
+
const url = new URL(`/v1/tree/${this.options.valName}/heads/${this.options.gitBranch}/${req.params["0"]}/?${params}`, this.options.valContentUrl);
|
|
1178
|
+
const json = await fetch(url, {
|
|
1179
|
+
headers: this.getAuthHeaders(data.token, "application/json")
|
|
1180
|
+
}).then(res => res.json());
|
|
1181
|
+
res.send(json);
|
|
1219
1182
|
});
|
|
1220
1183
|
}
|
|
1221
|
-
async
|
|
1184
|
+
async postPatches(req, res) {
|
|
1185
|
+
const {
|
|
1186
|
+
commit
|
|
1187
|
+
} = req.query;
|
|
1188
|
+
if (typeof commit !== "string" || typeof commit === "undefined") {
|
|
1189
|
+
res.status(401).json({
|
|
1190
|
+
error: "Missing or invalid commit query param"
|
|
1191
|
+
});
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
const params = new URLSearchParams({
|
|
1195
|
+
commit
|
|
1196
|
+
});
|
|
1222
1197
|
this.withAuth(req, res, async ({
|
|
1223
1198
|
token
|
|
1224
1199
|
}) => {
|
|
@@ -1234,12 +1209,11 @@ class ProxyValServer {
|
|
|
1234
1209
|
res.status(401).json(patch.error);
|
|
1235
1210
|
return;
|
|
1236
1211
|
}
|
|
1237
|
-
const
|
|
1238
|
-
const url = new URL(`/api/val/modules/${encodeURIComponent(this.options.gitCommit)}${id}`, this.options.valBuildUrl);
|
|
1212
|
+
const url = new URL(`/v1/tree/${this.options.valName}/heads/${this.options.gitBranch}/${req.params["0"]}/?${params}`, this.options.valContentUrl);
|
|
1239
1213
|
// Proxy patch to val.build
|
|
1240
1214
|
const fetchRes = await fetch(url, {
|
|
1241
|
-
method: "
|
|
1242
|
-
headers: this.getAuthHeaders(token, "application/json
|
|
1215
|
+
method: "POST",
|
|
1216
|
+
headers: this.getAuthHeaders(token, "application/json"),
|
|
1243
1217
|
body: JSON.stringify(patch)
|
|
1244
1218
|
});
|
|
1245
1219
|
if (fetchRes.ok) {
|
|
@@ -1421,6 +1395,40 @@ function getStateFromCookie(stateCookie) {
|
|
|
1421
1395
|
};
|
|
1422
1396
|
}
|
|
1423
1397
|
}
|
|
1398
|
+
async function enable(req, res, redirectUrl) {
|
|
1399
|
+
const {
|
|
1400
|
+
redirect_to
|
|
1401
|
+
} = req.query;
|
|
1402
|
+
if (typeof redirect_to === "string" || typeof redirect_to === "undefined") {
|
|
1403
|
+
let redirectUrlToUse = redirect_to || "/";
|
|
1404
|
+
if (redirectUrl) {
|
|
1405
|
+
redirectUrlToUse = redirectUrl + "?redirect_to=" + encodeURIComponent(redirectUrlToUse);
|
|
1406
|
+
}
|
|
1407
|
+
res.cookie(VAL_ENABLED_COOKIE, "true", {
|
|
1408
|
+
httpOnly: false,
|
|
1409
|
+
sameSite: "lax"
|
|
1410
|
+
}).redirect(redirectUrlToUse);
|
|
1411
|
+
} else {
|
|
1412
|
+
res.sendStatus(400);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
async function disable(req, res, redirectUrl) {
|
|
1416
|
+
const {
|
|
1417
|
+
redirect_to
|
|
1418
|
+
} = req.query;
|
|
1419
|
+
if (typeof redirect_to === "string" || typeof redirect_to === "undefined") {
|
|
1420
|
+
let redirectUrlToUse = redirect_to || "/";
|
|
1421
|
+
if (redirectUrl) {
|
|
1422
|
+
redirectUrlToUse = redirectUrl + "?redirect_to=" + encodeURIComponent(redirectUrlToUse);
|
|
1423
|
+
}
|
|
1424
|
+
res.cookie(VAL_ENABLED_COOKIE, "false", {
|
|
1425
|
+
httpOnly: false,
|
|
1426
|
+
sameSite: "lax"
|
|
1427
|
+
}).redirect(redirectUrlToUse);
|
|
1428
|
+
} else {
|
|
1429
|
+
res.sendStatus(400);
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1424
1432
|
function createStateCookie(state) {
|
|
1425
1433
|
return Buffer.from(JSON.stringify(state), "utf8").toString("base64");
|
|
1426
1434
|
}
|
|
@@ -1438,6 +1446,123 @@ const IntegratedServerJwtPayload = z$1.object({
|
|
|
1438
1446
|
project: z$1.string()
|
|
1439
1447
|
});
|
|
1440
1448
|
|
|
1449
|
+
class LocalValServer {
|
|
1450
|
+
constructor(options) {
|
|
1451
|
+
this.options = options;
|
|
1452
|
+
}
|
|
1453
|
+
async session(_req, res) {
|
|
1454
|
+
res.json({
|
|
1455
|
+
mode: "local"
|
|
1456
|
+
});
|
|
1457
|
+
}
|
|
1458
|
+
async getTree(req, res) {
|
|
1459
|
+
try {
|
|
1460
|
+
// TODO: use the params: patch, schema, source
|
|
1461
|
+
const treePath = req.params["0"].replace("~", "");
|
|
1462
|
+
const rootDir = process.cwd();
|
|
1463
|
+
const moduleIds = [];
|
|
1464
|
+
// iterate over all .val files in the root directory
|
|
1465
|
+
const walk = async dir => {
|
|
1466
|
+
const files = await promises.readdir(dir);
|
|
1467
|
+
for (const file of files) {
|
|
1468
|
+
if ((await promises.stat(path__default.join(dir, file))).isDirectory()) {
|
|
1469
|
+
if (file === "node_modules") continue;
|
|
1470
|
+
await walk(path__default.join(dir, file));
|
|
1471
|
+
} else {
|
|
1472
|
+
const isValFile = file.endsWith(".val.js") || file.endsWith(".val.ts");
|
|
1473
|
+
if (!isValFile) {
|
|
1474
|
+
continue;
|
|
1475
|
+
}
|
|
1476
|
+
if (treePath && !path__default.join(dir, file).replace(rootDir, "").startsWith(treePath)) {
|
|
1477
|
+
continue;
|
|
1478
|
+
}
|
|
1479
|
+
moduleIds.push(path__default.join(dir, file).replace(rootDir, "").replace(".val.js", "").replace(".val.ts", ""));
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
};
|
|
1483
|
+
const serializedModuleContent = await walk(rootDir).then(async () => {
|
|
1484
|
+
return Promise.all(moduleIds.map(async moduleId => {
|
|
1485
|
+
return await this.options.service.get(moduleId, "");
|
|
1486
|
+
}));
|
|
1487
|
+
});
|
|
1488
|
+
|
|
1489
|
+
//
|
|
1490
|
+
const modules = Object.fromEntries(serializedModuleContent.map(serializedModuleContent => {
|
|
1491
|
+
const module = {
|
|
1492
|
+
schema: serializedModuleContent.schema,
|
|
1493
|
+
source: serializedModuleContent.source
|
|
1494
|
+
};
|
|
1495
|
+
return [serializedModuleContent.path, module];
|
|
1496
|
+
}));
|
|
1497
|
+
const apiTreeResponse = {
|
|
1498
|
+
modules,
|
|
1499
|
+
git: this.options.git
|
|
1500
|
+
};
|
|
1501
|
+
return walk(rootDir).then(async () => {
|
|
1502
|
+
res.send(JSON.stringify(apiTreeResponse));
|
|
1503
|
+
});
|
|
1504
|
+
} catch (err) {
|
|
1505
|
+
console.error(err);
|
|
1506
|
+
res.sendStatus(500);
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
async enable(req, res) {
|
|
1510
|
+
return enable(req, res);
|
|
1511
|
+
}
|
|
1512
|
+
async disable(req, res) {
|
|
1513
|
+
return disable(req, res);
|
|
1514
|
+
}
|
|
1515
|
+
async postPatches(req, res) {
|
|
1516
|
+
var _getPathFromParams;
|
|
1517
|
+
const id = (_getPathFromParams = getPathFromParams(req.params)) === null || _getPathFromParams === void 0 ? void 0 : _getPathFromParams.replace("/~", "");
|
|
1518
|
+
|
|
1519
|
+
// First validate that the body has the right structure
|
|
1520
|
+
const patchJSON = PatchJSON.safeParse(req.body);
|
|
1521
|
+
console.log("patch id", id, patchJSON);
|
|
1522
|
+
if (!patchJSON.success) {
|
|
1523
|
+
res.status(401).json(patchJSON.error.issues);
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
// Then parse/validate
|
|
1527
|
+
const patch = parsePatch(patchJSON.data);
|
|
1528
|
+
if (result.isErr(patch)) {
|
|
1529
|
+
res.status(401).json(patch.error);
|
|
1530
|
+
return;
|
|
1531
|
+
}
|
|
1532
|
+
try {
|
|
1533
|
+
await this.options.service.patch(id, patch.value);
|
|
1534
|
+
res.json({});
|
|
1535
|
+
} catch (err) {
|
|
1536
|
+
if (err instanceof PatchError) {
|
|
1537
|
+
res.status(400).send({
|
|
1538
|
+
message: err.message
|
|
1539
|
+
});
|
|
1540
|
+
} else {
|
|
1541
|
+
console.error(err);
|
|
1542
|
+
res.status(500).send({
|
|
1543
|
+
message: err instanceof Error ? err.message : "Unknown error"
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
async badRequest(req, res) {
|
|
1549
|
+
console.debug("Local server does handle this request", req.url);
|
|
1550
|
+
res.sendStatus(400);
|
|
1551
|
+
}
|
|
1552
|
+
commit(req, res) {
|
|
1553
|
+
return this.badRequest(req, res);
|
|
1554
|
+
}
|
|
1555
|
+
authorize(req, res) {
|
|
1556
|
+
return this.badRequest(req, res);
|
|
1557
|
+
}
|
|
1558
|
+
callback(req, res) {
|
|
1559
|
+
return this.badRequest(req, res);
|
|
1560
|
+
}
|
|
1561
|
+
logout(req, res) {
|
|
1562
|
+
return this.badRequest(req, res);
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1441
1566
|
async function _createRequestListener(route, opts) {
|
|
1442
1567
|
const serverOpts = await initHandlerOptions(route, opts);
|
|
1443
1568
|
let valServer;
|
|
@@ -1458,6 +1583,7 @@ async function initHandlerOptions(route, opts) {
|
|
|
1458
1583
|
if (!maybeApiKey || !maybeValSecret) {
|
|
1459
1584
|
throw new Error("VAL_API_KEY and VAL_SECRET env vars must both be set in proxy mode");
|
|
1460
1585
|
}
|
|
1586
|
+
const valContentUrl = opts.valContentUrl || process.env.VAL_CONTENT_URL || "https://content.val.build";
|
|
1461
1587
|
const maybeGitCommit = opts.gitCommit || process.env.VAL_GIT_COMMIT;
|
|
1462
1588
|
if (!maybeGitCommit) {
|
|
1463
1589
|
throw new Error("VAL_GIT_COMMIT env var must be set in proxy mode");
|
|
@@ -1476,18 +1602,82 @@ async function initHandlerOptions(route, opts) {
|
|
|
1476
1602
|
apiKey: maybeApiKey,
|
|
1477
1603
|
valSecret: maybeValSecret,
|
|
1478
1604
|
valBuildUrl,
|
|
1605
|
+
valContentUrl,
|
|
1479
1606
|
gitCommit: maybeGitCommit,
|
|
1480
1607
|
gitBranch: maybeGitBranch,
|
|
1481
|
-
valName: maybeValName
|
|
1608
|
+
valName: maybeValName,
|
|
1609
|
+
valEnableRedirectUrl: opts.valEnableRedirectUrl || process.env.VAL_ENABLE_REDIRECT_URL,
|
|
1610
|
+
valDisableRedirectUrl: opts.valDisableRedirectUrl || process.env.VAL_DISABLE_REDIRECT_URL
|
|
1482
1611
|
};
|
|
1483
1612
|
} else {
|
|
1484
|
-
const
|
|
1613
|
+
const cwd = process.cwd();
|
|
1614
|
+
const service = await createService(cwd, opts);
|
|
1615
|
+
const git = await safeReadGit(cwd);
|
|
1485
1616
|
return {
|
|
1486
1617
|
mode: "local",
|
|
1487
|
-
service
|
|
1618
|
+
service,
|
|
1619
|
+
git: {
|
|
1620
|
+
commit: process.env.VAL_GIT_COMMIT || git.commit,
|
|
1621
|
+
branch: process.env.VAL_GIT_BRANCH || git.branch
|
|
1622
|
+
}
|
|
1488
1623
|
};
|
|
1489
1624
|
}
|
|
1490
1625
|
}
|
|
1626
|
+
async function safeReadGit(cwd) {
|
|
1627
|
+
async function findGitHead(currentDir, depth) {
|
|
1628
|
+
const gitHeadPath = path.join(currentDir, ".git", "HEAD");
|
|
1629
|
+
if (depth > 1000) {
|
|
1630
|
+
console.error(`Reached max depth while scanning for .git folder. Current working dir: ${cwd}.`);
|
|
1631
|
+
return {
|
|
1632
|
+
commit: undefined,
|
|
1633
|
+
branch: undefined
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1636
|
+
try {
|
|
1637
|
+
const headContents = await promises.readFile(gitHeadPath, "utf-8");
|
|
1638
|
+
const match = headContents.match(/^ref: refs\/heads\/(.+)/);
|
|
1639
|
+
if (match) {
|
|
1640
|
+
const branchName = match[1];
|
|
1641
|
+
return {
|
|
1642
|
+
branch: branchName,
|
|
1643
|
+
commit: await readCommit(currentDir, branchName)
|
|
1644
|
+
};
|
|
1645
|
+
} else {
|
|
1646
|
+
return {
|
|
1647
|
+
commit: undefined,
|
|
1648
|
+
branch: undefined
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
} catch (error) {
|
|
1652
|
+
const parentDir = path.dirname(currentDir);
|
|
1653
|
+
|
|
1654
|
+
// We've reached the root directory
|
|
1655
|
+
if (parentDir === currentDir) {
|
|
1656
|
+
return {
|
|
1657
|
+
commit: undefined,
|
|
1658
|
+
branch: undefined
|
|
1659
|
+
};
|
|
1660
|
+
}
|
|
1661
|
+
return findGitHead(parentDir, depth + 1);
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
try {
|
|
1665
|
+
return findGitHead(cwd, 0);
|
|
1666
|
+
} catch (err) {
|
|
1667
|
+
console.error("Error while reading .git", err);
|
|
1668
|
+
return {
|
|
1669
|
+
commit: undefined,
|
|
1670
|
+
branch: undefined
|
|
1671
|
+
};
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
async function readCommit(gitDir, branchName) {
|
|
1675
|
+
try {
|
|
1676
|
+
return (await promises.readFile(path.join(gitDir, ".git", "refs", "heads", branchName), "utf-8")).trim();
|
|
1677
|
+
} catch (err) {
|
|
1678
|
+
return undefined;
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1491
1681
|
|
|
1492
1682
|
// TODO: rename to createValApiHandlers?
|
|
1493
1683
|
function createRequestListener(route, opts) {
|
|
@@ -1525,10 +1715,10 @@ class ValFSHost {
|
|
|
1525
1715
|
return this.currentDirectory;
|
|
1526
1716
|
}
|
|
1527
1717
|
getCanonicalFileName(fileName) {
|
|
1528
|
-
if (
|
|
1529
|
-
return
|
|
1718
|
+
if (path__default.isAbsolute(fileName)) {
|
|
1719
|
+
return path__default.normalize(fileName);
|
|
1530
1720
|
}
|
|
1531
|
-
return
|
|
1721
|
+
return path__default.resolve(this.getCurrentDirectory(), fileName);
|
|
1532
1722
|
}
|
|
1533
1723
|
fileExists(fileName) {
|
|
1534
1724
|
return this.valFS.fileExists(fileName);
|
|
@@ -1549,7 +1739,7 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
|
|
|
1549
1739
|
// TODO:
|
|
1550
1740
|
throw Error("Cannot fix image without a file reference");
|
|
1551
1741
|
}
|
|
1552
|
-
const localFile =
|
|
1742
|
+
const localFile = path__default.join(config.projectRoot, maybeRef);
|
|
1553
1743
|
const buffer = fs.readFileSync(localFile);
|
|
1554
1744
|
const sha256 = await getSHA256Hash(buffer);
|
|
1555
1745
|
const imageSize = sizeOf(buffer);
|
|
@@ -1650,4 +1840,4 @@ const getSHA256Hash = async bits => {
|
|
|
1650
1840
|
return hash;
|
|
1651
1841
|
};
|
|
1652
1842
|
|
|
1653
|
-
export { LocalValServer, PatchJSON, Service, ValFSHost, ValModuleLoader, ValSourceFileHandler, createFixPatch, createRequestHandler, createRequestListener, createService, decodeJwt, encodeJwt, formatSyntaxErrorTree, getCompilerOptions, getExpire, patchSourceFile };
|
|
1843
|
+
export { LocalValServer, PatchJSON, Service, ValFSHost, ValModuleLoader, ValSourceFileHandler, createFixPatch, createRequestHandler, createRequestListener, createService, decodeJwt, encodeJwt, formatSyntaxErrorTree, getCompilerOptions, getExpire, patchSourceFile, safeReadGit };
|
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.
|
|
15
|
+
"version": "0.17.0",
|
|
16
16
|
"scripts": {
|
|
17
17
|
"typecheck": "tsc --noEmit",
|
|
18
18
|
"test": "jest",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"concurrently": "^7.6.0"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@valbuild/core": "~0.
|
|
29
|
-
"@valbuild/ui": "~0.
|
|
28
|
+
"@valbuild/core": "~0.17.0",
|
|
29
|
+
"@valbuild/ui": "~0.17.0",
|
|
30
30
|
"express": "^4.18.2",
|
|
31
31
|
"image-size": "^1.0.2",
|
|
32
32
|
"quickjs-emscripten": "^0.21.1",
|