@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.
@@ -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 fs from 'fs';
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.getRawSource(valModule?.default),
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.getRawSource(valModule?.default)
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
- path.join(path.dirname(valConfigPath), "<val>"));
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 = path.resolve(rootDir, "tsconfig.json");
680
- const jsConfigPath = path.resolve(rootDir, "jsconfig.json");
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, path.isAbsolute(containingFilePath) ? containingFilePath : path.resolve(this.projectRoot, containingFilePath), this.compilerOptions, this.host, undefined, undefined, ts.ModuleKind.ESNext);
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.get("/ids/*", valServer.getIds.bind(valServer));
938
- router.patch("/ids/*", express.json({
939
- type: "application/json-patch+json",
961
+ router.post("/patches/*", express.json({
962
+ type: "application/json",
940
963
  limit: "10mb"
941
- }), valServer.patchIds.bind(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 = "val_session";
1112
- const VAL_STATE_COOKIE = "val_state";
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 getIds(req, res) {
1199
- return this.withAuth(req, res, async ({
1200
- token
1201
- }) => {
1202
- const id = getPathFromParams(req.params);
1203
- const url = new URL(`/api/val/modules/${encodeURIComponent(this.options.gitCommit)}${id}`, this.options.valBuildUrl);
1204
- const fetchRes = await fetch(url, {
1205
- headers: this.getAuthHeaders(token)
1206
- });
1207
- if (fetchRes.ok) {
1208
- res.status(fetchRes.status).json(await fetchRes.json());
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 patchIds(req, res) {
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 id = getPathFromParams(req.params);
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: "PATCH",
1242
- headers: this.getAuthHeaders(token, "application/json-patch+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 service = await createService(process.cwd(), opts);
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 (path.isAbsolute(fileName)) {
1529
- return path.normalize(fileName);
1718
+ if (path__default.isAbsolute(fileName)) {
1719
+ return path__default.normalize(fileName);
1530
1720
  }
1531
- return path.resolve(this.getCurrentDirectory(), fileName);
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 = path.join(config.projectRoot, maybeRef);
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.16.3",
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.16.0",
29
- "@valbuild/ui": "~0.15.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",