@valbuild/server 0.21.2 → 0.23.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.
@@ -6,10 +6,12 @@ import { deepEqual, isNotRoot, PatchError, parseAndValidateArrayIndex, applyPatc
6
6
  import * as path from 'path';
7
7
  import path__default from 'path';
8
8
  import fs, { promises } from 'fs';
9
+ import { transform } from 'sucrase';
9
10
  import express, { Router } from 'express';
10
11
  import { createRequestHandler as createRequestHandler$1 } from '@valbuild/ui/server';
11
12
  import z, { z as z$1 } from 'zod';
12
13
  import crypto from 'crypto';
14
+ import { Readable } from 'stream';
13
15
  import sizeOf from 'image-size';
14
16
 
15
17
  class ValSyntaxError {
@@ -609,6 +611,7 @@ globalThis.valModule = {
609
611
  const error = result.error.consume(context.dump);
610
612
  console.error(`Fatal error reading val file: ${error.message}\n`, error.stack);
611
613
  return {
614
+ path: id,
612
615
  errors: {
613
616
  invalidModuleId: id,
614
617
  fatal: [{
@@ -624,12 +627,12 @@ globalThis.valModule = {
624
627
  fatalErrors.push(`Could not find any modules at: ${id}`);
625
628
  } else {
626
629
  if (valModule.id !== id) {
627
- fatalErrors.push(`Expected val id: '${id}' but got: '${valModule.id}'`);
630
+ fatalErrors.push(`Wrong val.content id! In the file of with: '${id}', found: '${valModule.id}'`);
628
631
  }
629
632
  if (!(valModule !== null && valModule !== void 0 && valModule.schema)) {
630
633
  fatalErrors.push(`Expected val id: '${id}' to have a schema`);
631
634
  }
632
- if (!(valModule !== null && valModule !== void 0 && valModule.source)) {
635
+ if ((valModule === null || valModule === void 0 ? void 0 : valModule.source) === undefined) {
633
636
  fatalErrors.push(`Expected val id: '${id}' to have a source`);
634
637
  }
635
638
  }
@@ -649,7 +652,7 @@ globalThis.valModule = {
649
652
  };
650
653
  }
651
654
  return {
652
- path: valModule.id,
655
+ path: valModule.id || id,
653
656
  // NOTE: we use path here, since SerializedModuleContent (maybe bad name?) can be used for whole modules as well as subparts of modules
654
657
  source: valModule.source,
655
658
  schema: valModule.schema,
@@ -703,9 +706,6 @@ const patchValFile = async (id, valConfigPath, patch, sourceFileHandler, runtime
703
706
  sourceFileHandler.writeFile("." + filePath, convertDataUrlToBase64(content).toString("binary"), "binary");
704
707
  }
705
708
  }
706
- for (const [ref, patch] of Object.entries(derefRes.value.remotePatches)) {
707
- throw Error(`Cannot update remote ${ref} with ${JSON.stringify(patch)}: not implemented`);
708
- }
709
709
  sourceFileHandler.writeSourceFile(newSourceFile.value);
710
710
  return readValFile(id, valConfigPath, runtime);
711
711
  };
@@ -784,15 +784,23 @@ class ValSourceFileHandler {
784
784
  const JsFileLookupMapping = [
785
785
  // NOTE: first one matching will be used
786
786
  [".cjs.d.ts", [".esm.js", ".mjs.js"]], [".cjs.js", [".esm.js", ".mjs.js"]], [".cjs", [".mjs"]], [".d.ts", [".js", ".esm.js", ".mjs.js"]]];
787
+ const MAX_CACHE_SIZE = 100 * 1024 * 1024; // 100 mb
788
+ const MAX_OBJECT_KEY_SIZE = 2 ** 27; // https://stackoverflow.com/questions/13367391/is-there-a-limit-on-length-of-the-key-string-in-js-object
789
+
787
790
  class ValModuleLoader {
788
- constructor(projectRoot, compilerOptions, sourceFileHandler, host = {
791
+ constructor(projectRoot,
792
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
793
+ compilerOptions, sourceFileHandler, host = {
789
794
  ...ts.sys,
790
795
  writeFile: fs.writeFileSync
791
- }) {
796
+ }, disableCache = false) {
792
797
  this.projectRoot = projectRoot;
793
798
  this.compilerOptions = compilerOptions;
794
799
  this.sourceFileHandler = sourceFileHandler;
795
800
  this.host = host;
801
+ this.disableCache = disableCache;
802
+ this.cache = {};
803
+ this.cacheSize = 0;
796
804
  }
797
805
  getModule(modulePath) {
798
806
  if (!modulePath) {
@@ -802,18 +810,31 @@ class ValModuleLoader {
802
810
  if (!code) {
803
811
  throw Error(`Could not read file "${modulePath}"`);
804
812
  }
805
- return ts.transpile(code, {
806
- ...this.compilerOptions,
807
- jsx: ts.JsxEmit.React,
808
- // allowJs: true,
809
- // rootDir: this.compilerOptions.rootDir,
810
- module: ts.ModuleKind.ESNext,
811
- target: ts.ScriptTarget.ES2015 // QuickJS supports a lot of ES2020: https://test262.report/, however not all cases are in that report (e.g. export const {} = {})
812
- // moduleResolution: ts.ModuleResolutionKind.NodeNext,
813
- // target: ts.ScriptTarget.ES2020, // QuickJs runs in ES2020 so we must use that
814
- });
815
- }
813
+ let compiledCode;
814
+ if (this.cache[code] && !this.disableCache) {
815
+ // TODO: use hash instead of code as key
816
+ compiledCode = this.cache[code];
817
+ } else {
818
+ compiledCode = transform(code, {
819
+ filePath: modulePath,
820
+ disableESTransforms: true,
821
+ transforms: ["typescript"]
822
+ }).code;
823
+ if (!this.disableCache) {
824
+ if (this.cacheSize > MAX_CACHE_SIZE) {
825
+ console.warn("Cache size exceeded, clearing cache");
826
+ this.cache = {};
827
+ this.cacheSize = 0;
828
+ }
829
+ if (code.length < MAX_OBJECT_KEY_SIZE) {
830
+ this.cache[code] = compiledCode;
831
+ this.cacheSize += code.length + compiledCode.length; // code is mostly ASCII so 1 byte per char
832
+ }
833
+ }
834
+ }
816
835
 
836
+ return compiledCode;
837
+ }
817
838
  resolveModulePath(containingFilePath, requestedModuleName) {
818
839
  var _this$host$realpath, _this$host;
819
840
  let sourceFileName = this.sourceFileHandler.resolveSourceModulePath(containingFilePath, requestedModuleName);
@@ -973,11 +994,9 @@ class Service {
973
994
  path: sourcePath,
974
995
  schema: resolved.schema instanceof Schema ? resolved.schema.serialize() : resolved.schema,
975
996
  source: resolved.source,
976
- errors: valModule.errors && valModule.errors.validation && valModule.errors.validation[sourcePath] ? {
977
- validation: valModule.errors.validation[sourcePath] ? {
978
- [sourcePath]: valModule.errors.validation[sourcePath]
979
- } : undefined,
980
- fatal: valModule.errors && valModule.errors.fatal ? valModule.errors.fatal : undefined
997
+ errors: valModule.errors && valModule.errors.validation ? {
998
+ validation: valModule.errors.validation || undefined,
999
+ fatal: valModule.errors.fatal || undefined
981
1000
  } : false
982
1001
  };
983
1002
  } else {
@@ -1007,13 +1026,10 @@ function createRequestHandler(valServer) {
1007
1026
  router.get("/enable", valServer.enable.bind(valServer));
1008
1027
  router.get("/disable", valServer.disable.bind(valServer));
1009
1028
  router.get("/tree/*", valServer.getTree.bind(valServer));
1029
+ router.get("/files/*", valServer.getFiles.bind(valServer));
1010
1030
  return router;
1011
1031
  }
1012
1032
 
1013
- function getPathFromParams(params) {
1014
- return `/${params[0]}`;
1015
- }
1016
-
1017
1033
  const JSONValueT = z.lazy(() => z.union([z.string(), z.number(), z.boolean(), z.null(), z.array(JSONValueT), z.record(JSONValueT)]));
1018
1034
 
1019
1035
  /**
@@ -1051,6 +1067,7 @@ const OperationJSONT = z.discriminatedUnion("op", [z.object({
1051
1067
  }).strict(), z.object({
1052
1068
  op: z.literal("file"),
1053
1069
  path: z.string(),
1070
+ filePath: z.string(),
1054
1071
  value: z.string()
1055
1072
  }).strict()]);
1056
1073
  const PatchJSON = z.array(OperationJSONT);
@@ -1117,10 +1134,61 @@ function encodeJwt(payload, sessionKey) {
1117
1134
  const VAL_SESSION_COOKIE = Internal.VAL_SESSION_COOKIE;
1118
1135
  const VAL_STATE_COOKIE = Internal.VAL_STATE_COOKIE;
1119
1136
  const VAL_ENABLED_COOKIE = Internal.VAL_ENABLE_COOKIE_NAME;
1137
+ class BrowserReadableStreamWrapper extends Readable {
1138
+ constructor(readableStream) {
1139
+ super();
1140
+ this.reader = readableStream.getReader();
1141
+ }
1142
+ _read() {
1143
+ this.reader.read().then(({
1144
+ done,
1145
+ value
1146
+ }) => {
1147
+ if (done) {
1148
+ this.push(null); // No more data to read
1149
+ } else {
1150
+ this.push(Buffer.from(value));
1151
+ }
1152
+ }).catch(error => {
1153
+ this.emit("error", error);
1154
+ });
1155
+ }
1156
+ }
1120
1157
  class ProxyValServer {
1121
1158
  constructor(options) {
1122
1159
  this.options = options;
1123
1160
  }
1161
+ async getFiles(req, res) {
1162
+ return this.withAuth(req, res, async data => {
1163
+ const url = new URL(`/v1/files/${this.options.valName}/${req.params["0"]}`, this.options.valContentUrl);
1164
+ if (typeof req.query.sha256 === "string") {
1165
+ url.searchParams.append("sha256", req.query.sha256);
1166
+ } else {
1167
+ console.warn("Missing sha256 query param");
1168
+ }
1169
+ const fetchRes = await fetch(url, {
1170
+ headers: this.getAuthHeaders(data.token)
1171
+ });
1172
+ const contentType = fetchRes.headers.get("content-type");
1173
+ if (contentType !== null) {
1174
+ res.setHeader("Content-Type", contentType);
1175
+ }
1176
+ const contentLength = fetchRes.headers.get("content-length");
1177
+ if (contentLength !== null) {
1178
+ res.setHeader("Content-Length", contentLength);
1179
+ }
1180
+ if (fetchRes.ok) {
1181
+ if (fetchRes.body) {
1182
+ new BrowserReadableStreamWrapper(fetchRes.body).pipe(res);
1183
+ } else {
1184
+ console.warn("No body in response");
1185
+ res.sendStatus(500);
1186
+ }
1187
+ } else {
1188
+ res.sendStatus(fetchRes.status);
1189
+ }
1190
+ });
1191
+ }
1124
1192
  async authorize(req, res) {
1125
1193
  const {
1126
1194
  redirect_to
@@ -1214,10 +1282,18 @@ class ProxyValServer {
1214
1282
  schema,
1215
1283
  source
1216
1284
  } = req.query;
1285
+ const commit = this.options.gitCommit;
1286
+ if (!commit) {
1287
+ res.status(401).json({
1288
+ error: "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT."
1289
+ });
1290
+ return;
1291
+ }
1217
1292
  const params = new URLSearchParams({
1218
1293
  patch: (patch === "true").toString(),
1219
1294
  schema: (schema === "true").toString(),
1220
- source: (source === "true").toString()
1295
+ source: (source === "true").toString(),
1296
+ commit
1221
1297
  });
1222
1298
  const url = new URL(`/v1/tree/${this.options.valName}/heads/${this.options.gitBranch}/${req.params["0"]}/?${params}`, this.options.valContentUrl);
1223
1299
  const json = await fetch(url, {
@@ -1227,12 +1303,10 @@ class ProxyValServer {
1227
1303
  });
1228
1304
  }
1229
1305
  async postPatches(req, res) {
1230
- const {
1231
- commit
1232
- } = req.query;
1233
- if (typeof commit !== "string" || typeof commit === "undefined") {
1306
+ const commit = this.options.gitCommit;
1307
+ if (!commit) {
1234
1308
  res.status(401).json({
1235
- error: "Missing or invalid commit query param"
1309
+ error: "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT."
1236
1310
  });
1237
1311
  return;
1238
1312
  }
@@ -1243,18 +1317,20 @@ class ProxyValServer {
1243
1317
  token
1244
1318
  }) => {
1245
1319
  // First validate that the body has the right structure
1246
- const patchJSON = PatchJSON.safeParse(req.body);
1320
+ const patchJSON = z$1.record(PatchJSON).safeParse(req.body);
1247
1321
  if (!patchJSON.success) {
1248
1322
  res.status(401).json(patchJSON.error.issues);
1249
1323
  return;
1250
1324
  }
1251
1325
  // Then parse/validate
1252
- const patch = parsePatch(patchJSON.data);
1253
- if (result.isErr(patch)) {
1254
- res.status(401).json(patch.error);
1255
- return;
1256
- }
1257
- const url = new URL(`/v1/tree/${this.options.valName}/heads/${this.options.gitBranch}/${req.params["0"]}/?${params}`, this.options.valContentUrl);
1326
+ // TODO:
1327
+ const patch = patchJSON.data;
1328
+ // const patch = parsePatch(patchJSON.data);
1329
+ // if (result.isErr(patch)) {
1330
+ // res.status(401).json(patch.error);
1331
+ // return;
1332
+ // }
1333
+ const url = new URL(`/v1/patches/${this.options.valName}/heads/${this.options.gitBranch}/${req.params["0"]}/?${params}`, this.options.valContentUrl);
1258
1334
  // Proxy patch to val.build
1259
1335
  const fetchRes = await fetch(url, {
1260
1336
  method: "POST",
@@ -1535,7 +1611,8 @@ class LocalValServer {
1535
1611
  const modules = Object.fromEntries(serializedModuleContent.map(serializedModuleContent => {
1536
1612
  const module = {
1537
1613
  schema: serializedModuleContent.schema,
1538
- source: serializedModuleContent.source
1614
+ source: serializedModuleContent.source,
1615
+ errors: serializedModuleContent.errors
1539
1616
  };
1540
1617
  return [serializedModuleContent.path, module];
1541
1618
  }));
@@ -1543,9 +1620,7 @@ class LocalValServer {
1543
1620
  modules,
1544
1621
  git: this.options.git
1545
1622
  };
1546
- return walk(rootDir).then(async () => {
1547
- res.send(JSON.stringify(apiTreeResponse));
1548
- });
1623
+ res.send(JSON.stringify(apiTreeResponse));
1549
1624
  } catch (err) {
1550
1625
  console.error(err);
1551
1626
  res.sendStatus(500);
@@ -1558,24 +1633,23 @@ class LocalValServer {
1558
1633
  return disable(req, res);
1559
1634
  }
1560
1635
  async postPatches(req, res) {
1561
- var _getPathFromParams;
1562
- const id = (_getPathFromParams = getPathFromParams(req.params)) === null || _getPathFromParams === void 0 ? void 0 : _getPathFromParams.replace("/~", "");
1563
-
1564
1636
  // First validate that the body has the right structure
1565
- const patchJSON = PatchJSON.safeParse(req.body);
1566
- console.log("patch id", id, patchJSON);
1637
+ const patchJSON = z$1.record(PatchJSON).safeParse(req.body);
1567
1638
  if (!patchJSON.success) {
1568
1639
  res.status(401).json(patchJSON.error.issues);
1569
1640
  return;
1570
1641
  }
1571
- // Then parse/validate
1572
- const patch = parsePatch(patchJSON.data);
1573
- if (result.isErr(patch)) {
1574
- res.status(401).json(patch.error);
1575
- return;
1576
- }
1577
1642
  try {
1578
- await this.options.service.patch(id, patch.value);
1643
+ for (const moduleId in patchJSON.data) {
1644
+ // Then parse/validate
1645
+ // TODO: validate all and then fail instead:
1646
+ const patch = parsePatch(patchJSON.data[moduleId]);
1647
+ if (result.isErr(patch)) {
1648
+ res.status(401).json(patch.error);
1649
+ return;
1650
+ }
1651
+ await this.options.service.patch(moduleId, patch.value);
1652
+ }
1579
1653
  res.json({});
1580
1654
  } catch (err) {
1581
1655
  if (err instanceof PatchError) {
@@ -1606,6 +1680,9 @@ class LocalValServer {
1606
1680
  logout(req, res) {
1607
1681
  return this.badRequest(req, res);
1608
1682
  }
1683
+ getFiles(req, res) {
1684
+ return this.badRequest(req, res);
1685
+ }
1609
1686
  }
1610
1687
 
1611
1688
  async function _createRequestListener(route, opts) {
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.21.2",
15
+ "version": "0.23.0",
16
16
  "scripts": {
17
17
  "typecheck": "tsc --noEmit",
18
18
  "test": "jest",
@@ -25,11 +25,12 @@
25
25
  "concurrently": "^7.6.0"
26
26
  },
27
27
  "dependencies": {
28
- "@valbuild/core": "~0.21.2",
29
- "@valbuild/ui": "~0.21.2",
28
+ "@valbuild/core": "~0.23.0",
29
+ "@valbuild/ui": "~0.23.0",
30
30
  "express": "^4.18.2",
31
31
  "image-size": "^1.0.2",
32
32
  "quickjs-emscripten": "^0.21.1",
33
+ "sucrase": "^3.34.0",
33
34
  "ts-morph": "^17.0.1",
34
35
  "typescript": "^4.9.4",
35
36
  "zod": "^3.20.6"
@@ -2,13 +2,13 @@ import express from "express";
2
2
  import { Service } from "./Service";
3
3
  import { result } from "@valbuild/core/fp";
4
4
  import { parsePatch, PatchError } from "@valbuild/core/patch";
5
- import { getPathFromParams } from "./expressHelpers";
6
5
  import { PatchJSON } from "./patch/validation";
7
6
  import { ValServer } from "./ValServer";
8
7
  import { ApiTreeResponse, ModuleId, ModulePath } from "@valbuild/core";
9
8
  import { disable, enable } from "./ProxyValServer";
10
9
  import { promises as fs } from "fs";
11
10
  import path from "path";
11
+ import { z } from "zod";
12
12
 
13
13
  export type LocalValServerOptions = {
14
14
  service: Service;
@@ -80,6 +80,7 @@ export class LocalValServer implements ValServer {
80
80
  {
81
81
  schema: serializedModuleContent.schema,
82
82
  source: serializedModuleContent.source,
83
+ errors: serializedModuleContent.errors,
83
84
  };
84
85
  return [serializedModuleContent.path, module];
85
86
  })
@@ -88,9 +89,7 @@ export class LocalValServer implements ValServer {
88
89
  modules,
89
90
  git: this.options.git,
90
91
  };
91
- return walk(rootDir).then(async () => {
92
- res.send(JSON.stringify(apiTreeResponse));
93
- });
92
+ res.send(JSON.stringify(apiTreeResponse));
94
93
  } catch (err) {
95
94
  console.error(err);
96
95
  res.sendStatus(500);
@@ -109,23 +108,24 @@ export class LocalValServer implements ValServer {
109
108
  req: express.Request<{ 0: string }>,
110
109
  res: express.Response
111
110
  ): Promise<void> {
112
- const id = getPathFromParams(req.params)?.replace("/~", "");
113
-
114
111
  // First validate that the body has the right structure
115
- const patchJSON = PatchJSON.safeParse(req.body);
116
- console.log("patch id", id, patchJSON);
112
+ const patchJSON = z.record(PatchJSON).safeParse(req.body);
117
113
  if (!patchJSON.success) {
118
114
  res.status(401).json(patchJSON.error.issues);
119
115
  return;
120
116
  }
121
- // Then parse/validate
122
- const patch = parsePatch(patchJSON.data);
123
- if (result.isErr(patch)) {
124
- res.status(401).json(patch.error);
125
- return;
126
- }
117
+
127
118
  try {
128
- await this.options.service.patch(id, patch.value);
119
+ for (const moduleId in patchJSON.data) {
120
+ // Then parse/validate
121
+ // TODO: validate all and then fail instead:
122
+ const patch = parsePatch(patchJSON.data[moduleId]);
123
+ if (result.isErr(patch)) {
124
+ res.status(401).json(patch.error);
125
+ return;
126
+ }
127
+ await this.options.service.patch(moduleId, patch.value);
128
+ }
129
129
  res.json({});
130
130
  } catch (err) {
131
131
  if (err instanceof PatchError) {
@@ -161,4 +161,7 @@ export class LocalValServer implements ValServer {
161
161
  logout(req: express.Request, res: express.Response): Promise<void> {
162
162
  return this.badRequest(req, res);
163
163
  }
164
+ getFiles(req: express.Request, res: express.Response): Promise<void> {
165
+ return this.badRequest(req, res);
166
+ }
164
167
  }
@@ -2,11 +2,10 @@ import express from "express";
2
2
  import crypto from "crypto";
3
3
  import { decodeJwt, encodeJwt, getExpire } from "./jwt";
4
4
  import { PatchJSON } from "./patch/validation";
5
- import { result } from "@valbuild/core/fp";
6
5
  import { ValServer } from "./ValServer";
7
6
  import { z } from "zod";
8
- import { parsePatch } from "@valbuild/core/patch";
9
7
  import { Internal } from "@valbuild/core";
8
+ import { Readable } from "stream";
10
9
 
11
10
  const VAL_SESSION_COOKIE = Internal.VAL_SESSION_COOKIE;
12
11
  const VAL_STATE_COOKIE = Internal.VAL_STATE_COOKIE;
@@ -25,9 +24,68 @@ export type ProxyValServerOptions = {
25
24
  valDisableRedirectUrl?: string;
26
25
  };
27
26
 
27
+ class BrowserReadableStreamWrapper extends Readable {
28
+ private reader: ReadableStreamDefaultReader<Uint8Array>;
29
+
30
+ constructor(readableStream: ReadableStream<Uint8Array>) {
31
+ super();
32
+ this.reader = readableStream.getReader();
33
+ }
34
+
35
+ _read() {
36
+ this.reader
37
+ .read()
38
+ .then(({ done, value }) => {
39
+ if (done) {
40
+ this.push(null); // No more data to read
41
+ } else {
42
+ this.push(Buffer.from(value));
43
+ }
44
+ })
45
+ .catch((error) => {
46
+ this.emit("error", error);
47
+ });
48
+ }
49
+ }
50
+
28
51
  export class ProxyValServer implements ValServer {
29
52
  constructor(readonly options: ProxyValServerOptions) {}
30
53
 
54
+ async getFiles(req: express.Request, res: express.Response): Promise<void> {
55
+ return this.withAuth(req, res, async (data) => {
56
+ const url = new URL(
57
+ `/v1/files/${this.options.valName}/${req.params["0"]}`,
58
+ this.options.valContentUrl
59
+ );
60
+ if (typeof req.query.sha256 === "string") {
61
+ url.searchParams.append("sha256", req.query.sha256 as string);
62
+ } else {
63
+ console.warn("Missing sha256 query param");
64
+ }
65
+ const fetchRes = await fetch(url, {
66
+ headers: this.getAuthHeaders(data.token),
67
+ });
68
+ const contentType = fetchRes.headers.get("content-type");
69
+ if (contentType !== null) {
70
+ res.setHeader("Content-Type", contentType);
71
+ }
72
+ const contentLength = fetchRes.headers.get("content-length");
73
+ if (contentLength !== null) {
74
+ res.setHeader("Content-Length", contentLength);
75
+ }
76
+ if (fetchRes.ok) {
77
+ if (fetchRes.body) {
78
+ new BrowserReadableStreamWrapper(fetchRes.body).pipe(res);
79
+ } else {
80
+ console.warn("No body in response");
81
+ res.sendStatus(500);
82
+ }
83
+ } else {
84
+ res.sendStatus(fetchRes.status);
85
+ }
86
+ });
87
+ }
88
+
31
89
  async authorize(req: express.Request, res: express.Response): Promise<void> {
32
90
  const { redirect_to } = req.query;
33
91
  if (typeof redirect_to !== "string") {
@@ -145,10 +203,19 @@ export class ProxyValServer implements ValServer {
145
203
  async getTree(req: express.Request, res: express.Response): Promise<void> {
146
204
  return this.withAuth(req, res, async (data) => {
147
205
  const { patch, schema, source } = req.query;
206
+ const commit = this.options.gitCommit;
207
+ if (!commit) {
208
+ res.status(401).json({
209
+ error:
210
+ "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT.",
211
+ });
212
+ return;
213
+ }
148
214
  const params = new URLSearchParams({
149
215
  patch: (patch === "true").toString(),
150
216
  schema: (schema === "true").toString(),
151
217
  source: (source === "true").toString(),
218
+ commit,
152
219
  });
153
220
  const url = new URL(
154
221
  `/v1/tree/${this.options.valName}/heads/${this.options.gitBranch}/${req.params["0"]}/?${params}`,
@@ -165,9 +232,12 @@ export class ProxyValServer implements ValServer {
165
232
  req: express.Request<{ 0: string }>,
166
233
  res: express.Response
167
234
  ): Promise<void> {
168
- const { commit } = req.query;
169
- if (typeof commit !== "string" || typeof commit === "undefined") {
170
- res.status(401).json({ error: "Missing or invalid commit query param" });
235
+ const commit = this.options.gitCommit;
236
+ if (!commit) {
237
+ res.status(401).json({
238
+ error:
239
+ "Could not detect the git commit. Check if env is missing VAL_GIT_COMMIT.",
240
+ });
171
241
  return;
172
242
  }
173
243
  const params = new URLSearchParams({
@@ -175,19 +245,21 @@ export class ProxyValServer implements ValServer {
175
245
  });
176
246
  this.withAuth(req, res, async ({ token }) => {
177
247
  // First validate that the body has the right structure
178
- const patchJSON = PatchJSON.safeParse(req.body);
248
+ const patchJSON = z.record(PatchJSON).safeParse(req.body);
179
249
  if (!patchJSON.success) {
180
250
  res.status(401).json(patchJSON.error.issues);
181
251
  return;
182
252
  }
183
253
  // Then parse/validate
184
- const patch = parsePatch(patchJSON.data);
185
- if (result.isErr(patch)) {
186
- res.status(401).json(patch.error);
187
- return;
188
- }
254
+ // TODO:
255
+ const patch = patchJSON.data;
256
+ // const patch = parsePatch(patchJSON.data);
257
+ // if (result.isErr(patch)) {
258
+ // res.status(401).json(patch.error);
259
+ // return;
260
+ // }
189
261
  const url = new URL(
190
- `/v1/tree/${this.options.valName}/heads/${this.options.gitBranch}/${req.params["0"]}/?${params}`,
262
+ `/v1/patches/${this.options.valName}/heads/${this.options.gitBranch}/${req.params["0"]}/?${params}`,
191
263
  this.options.valContentUrl
192
264
  );
193
265
  // Proxy patch to val.build
@@ -11,6 +11,7 @@ export const FATAL_ERROR_TYPES = [
11
11
  "invalid-id",
12
12
  "no-module",
13
13
  ] as const;
14
+ export type FatalErrorType = (typeof FATAL_ERROR_TYPES)[number];
14
15
 
15
16
  export type SerializedModuleContent =
16
17
  | {
@@ -22,14 +23,14 @@ export type SerializedModuleContent =
22
23
  | {
23
24
  source?: Source;
24
25
  schema?: SerializedSchema;
25
- path?: SourcePath;
26
+ path: SourcePath;
26
27
  errors: {
27
28
  invalidModuleId?: ModuleId;
28
29
  validation?: ValidationErrors;
29
30
  fatal?: {
30
31
  message: string;
31
- stack?: string[];
32
- type?: (typeof FATAL_ERROR_TYPES)[number];
32
+ stack?: string;
33
+ type?: FatalErrorType;
33
34
  }[];
34
35
  };
35
36
  };
package/src/Service.ts CHANGED
@@ -22,9 +22,15 @@ export type ServiceOptions = {
22
22
  /**
23
23
  * Relative path to the val.config.js file from the root directory.
24
24
  *
25
- * @example src/val.config
25
+ * @example "./val.config"
26
26
  */
27
27
  valConfigPath: string;
28
+ /**
29
+ * Disable cache for transpilation
30
+ *
31
+ * @default false
32
+ * */
33
+ disableCache?: boolean;
28
34
  };
29
35
 
30
36
  export async function createService(
@@ -89,19 +95,10 @@ export class Service {
89
95
  : resolved.schema,
90
96
  source: resolved.source,
91
97
  errors:
92
- valModule.errors &&
93
- valModule.errors.validation &&
94
- valModule.errors.validation[sourcePath]
98
+ valModule.errors && valModule.errors.validation
95
99
  ? {
96
- validation: valModule.errors.validation[sourcePath]
97
- ? {
98
- [sourcePath]: valModule.errors.validation[sourcePath],
99
- }
100
- : undefined,
101
- fatal:
102
- valModule.errors && valModule.errors.fatal
103
- ? valModule.errors.fatal
104
- : undefined,
100
+ validation: valModule.errors.validation || undefined,
101
+ fatal: valModule.errors.fatal || undefined,
105
102
  }
106
103
  : false,
107
104
  };