@valbuild/server 0.97.7 → 0.98.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.
@@ -13,12 +13,13 @@ var node_module = require('node:module');
13
13
  var internal = require('@valbuild/shared/internal');
14
14
  var server = require('@valbuild/ui/server');
15
15
  var crypto$1 = require('crypto');
16
- var zod = require('zod');
16
+ var z = require('zod');
17
17
  var sizeOf = require('image-size');
18
18
  var zodValidationError = require('zod-validation-error');
19
19
  var sucrase = require('sucrase');
20
20
  var http = require('http');
21
21
  var https = require('https');
22
+ var fs$1 = require('fs/promises');
22
23
 
23
24
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
24
25
 
@@ -45,9 +46,11 @@ var path__namespace = /*#__PURE__*/_interopNamespace(path);
45
46
  var fs__default = /*#__PURE__*/_interopDefault(fs);
46
47
  var vm__default = /*#__PURE__*/_interopDefault(vm);
47
48
  var crypto__default = /*#__PURE__*/_interopDefault(crypto$1);
49
+ var z__default = /*#__PURE__*/_interopDefault(z);
48
50
  var sizeOf__default = /*#__PURE__*/_interopDefault(sizeOf);
49
51
  var http__default = /*#__PURE__*/_interopDefault(http);
50
52
  var https__default = /*#__PURE__*/_interopDefault(https);
53
+ var fs__default$1 = /*#__PURE__*/_interopDefault(fs$1);
51
54
 
52
55
  class ValSyntaxError {
53
56
  constructor(message, node) {
@@ -957,6 +960,15 @@ const getCompilerOptions = (rootDir, parseConfigHost) => {
957
960
  return parsedConfigFile.options;
958
961
  };
959
962
 
963
+ /**
964
+ * The filesystem seam `loadValModules` reads through.
965
+ *
966
+ * Defaults to `ts.sys`, i.e. the real filesystem. An editor integration (see
967
+ * `@valbuild/language-server`) passes a host that overlays unsaved buffers, so
968
+ * that evaluation sees what the user is looking at rather than what was last
969
+ * saved.
970
+ */
971
+
960
972
  /**
961
973
  * Loads the project's root `val.modules.ts` (or `.js`) using Node's `vm`
962
974
  * module and returns its default export (a `ValModules` registry).
@@ -978,24 +990,24 @@ const getCompilerOptions = (rootDir, parseConfigHost) => {
978
990
  * same trust level as running the project's build. It must never be used to
979
991
  * evaluate untrusted or third-party modules.
980
992
  */
981
- function loadValModules(projectRoot) {
982
- const valModulesPath = findValModulesPath(projectRoot);
993
+ function loadValModules(projectRoot, host = ts__default["default"].sys) {
994
+ const valModulesPath = findValModulesPath(projectRoot, host);
983
995
  if (!valModulesPath) {
984
996
  throw Error(`Could not find 'val.modules.ts' nor 'val.modules.js' in project root: '${projectRoot}'`);
985
997
  }
986
- const compilerOptions = getCompilerOptions(projectRoot, ts__default["default"].sys);
998
+ const compilerOptions = getCompilerOptions(projectRoot, host);
987
999
  const cache = {};
988
- const loaded = loadModule(valModulesPath, cache, compilerOptions);
1000
+ const loaded = loadModule(valModulesPath, cache, compilerOptions, host);
989
1001
  const valModules = loaded.exports.default;
990
1002
  if (!valModules) {
991
1003
  throw Error(`Val modules file at path: '${valModulesPath}' must have a default export. Got: ${valModules}`);
992
1004
  }
993
1005
  return valModules;
994
1006
  }
995
- function findValModulesPath(projectRoot) {
1007
+ function findValModulesPath(projectRoot, host) {
996
1008
  for (const fileName of ["val.modules.ts", "val.modules.js"]) {
997
1009
  const candidate = path__namespace["default"].join(projectRoot, fileName);
998
- if (fs__default["default"].existsSync(candidate)) {
1010
+ if (host.fileExists(candidate)) {
999
1011
  return candidate;
1000
1012
  }
1001
1013
  }
@@ -1036,12 +1048,15 @@ function makeStub(spec) {
1036
1048
  const stub = new Proxy({}, handler);
1037
1049
  return stub;
1038
1050
  }
1039
- function loadModule(absPath, cache, compilerOptions) {
1051
+ function loadModule(absPath, cache, compilerOptions, host) {
1040
1052
  const cached = cache[absPath];
1041
1053
  if (cached) {
1042
1054
  return cached;
1043
1055
  }
1044
- const code = fs__default["default"].readFileSync(absPath, "utf-8");
1056
+ const code = host.readFile(absPath);
1057
+ if (code === undefined) {
1058
+ throw Error(`Could not read Val module file: '${absPath}'`);
1059
+ }
1045
1060
  const transpiled = ts__default["default"].transpileModule(code, {
1046
1061
  compilerOptions: {
1047
1062
  target: ts__default["default"].ScriptTarget.ES2020,
@@ -1064,17 +1079,17 @@ function loadModule(absPath, cache, compilerOptions) {
1064
1079
  return makeStub(spec);
1065
1080
  }
1066
1081
  if (spec.startsWith(".") || path__namespace["default"].isAbsolute(spec)) {
1067
- const resolved = resolveRelative(dirName, spec);
1082
+ const resolved = resolveRelative(dirName, spec, host);
1068
1083
  if (!resolved) {
1069
1084
  throw Error(`Could not resolve module '${spec}' from '${absPath}'`);
1070
1085
  }
1071
- return loadModule(resolved, cache, compilerOptions).exports;
1086
+ return loadModule(resolved, cache, compilerOptions, host).exports;
1072
1087
  }
1073
1088
  // Non-relative specifier: it might be a tsconfig path alias (e.g. "_/val.config")
1074
1089
  // pointing at a local source file, or an actual node_modules package.
1075
- const tsResolved = (_ts$resolveModuleName = ts__default["default"].resolveModuleName(spec, absPath, compilerOptions, ts__default["default"].sys).resolvedModule) === null || _ts$resolveModuleName === void 0 ? void 0 : _ts$resolveModuleName.resolvedFileName;
1090
+ const tsResolved = (_ts$resolveModuleName = ts__default["default"].resolveModuleName(spec, absPath, compilerOptions, host).resolvedModule) === null || _ts$resolveModuleName === void 0 ? void 0 : _ts$resolveModuleName.resolvedFileName;
1076
1091
  if (tsResolved && !tsResolved.includes("/node_modules/") && !tsResolved.endsWith(".d.ts")) {
1077
- return loadModule(tsResolved, cache, compilerOptions).exports;
1092
+ return loadModule(tsResolved, cache, compilerOptions, host).exports;
1078
1093
  }
1079
1094
  // Real node_modules package – use the real require so user modules share
1080
1095
  // the same @valbuild/core instance as extractValModules.
@@ -1100,24 +1115,25 @@ function loadModule(absPath, cache, compilerOptions) {
1100
1115
  script.runInContext(context);
1101
1116
  return moduleObj;
1102
1117
  }
1103
- function resolveRelative(dirName, spec) {
1118
+ function resolveRelative(dirName, spec, host) {
1119
+ var _host$directoryExists;
1104
1120
  const base = path__namespace["default"].resolve(dirName, spec);
1105
1121
  // Exact file (with extension)
1106
- if (fs__default["default"].existsSync(base) && fs__default["default"].statSync(base).isFile()) {
1122
+ if (host.fileExists(base)) {
1107
1123
  return base;
1108
1124
  }
1109
1125
  // Probe extensions (handles `./x.val` -> `./x.val.ts`)
1110
1126
  for (const ext of RESOLVE_EXTENSIONS) {
1111
1127
  const candidate = base + ext;
1112
- if (fs__default["default"].existsSync(candidate)) {
1128
+ if (host.fileExists(candidate)) {
1113
1129
  return candidate;
1114
1130
  }
1115
1131
  }
1116
1132
  // Directory index
1117
- if (fs__default["default"].existsSync(base) && fs__default["default"].statSync(base).isDirectory()) {
1133
+ if (((_host$directoryExists = host.directoryExists) === null || _host$directoryExists === void 0 ? void 0 : _host$directoryExists.call(host, base)) ?? fs__default["default"].existsSync(base)) {
1118
1134
  for (const ext of RESOLVE_EXTENSIONS) {
1119
1135
  const candidate = path__namespace["default"].join(base, "index" + ext);
1120
- if (fs__default["default"].existsSync(candidate)) {
1136
+ if (host.fileExists(candidate)) {
1121
1137
  return candidate;
1122
1138
  }
1123
1139
  }
@@ -1144,7 +1160,9 @@ async function createService(projectRoot, host = {
1144
1160
  }) {
1145
1161
  const compilerOptions = getCompilerOptions(projectRoot, host);
1146
1162
  const sourceFileHandler = new ValSourceFileHandler(projectRoot, compilerOptions, host);
1147
- const valModules = loadValModules(projectRoot);
1163
+ // Read val.modules (and everything it imports) through the same host, so an
1164
+ // embedder that overlays unsaved editor buffers evaluates what the user sees.
1165
+ const valModules = loadValModules(projectRoot, host);
1148
1166
  const extracted = await core.extractValModules(valModules);
1149
1167
  return new Service(projectRoot, sourceFileHandler, extracted);
1150
1168
  }
@@ -1254,9 +1272,9 @@ function decodeJwt(token, secretKey) {
1254
1272
  function getExpire() {
1255
1273
  return Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 4; // 4 days
1256
1274
  }
1257
- const JwtHeaderSchema = zod.z.object({
1258
- alg: zod.z.literal("HS256"),
1259
- typ: zod.z.literal("JWT")
1275
+ const JwtHeaderSchema = z.z.object({
1276
+ alg: z.z.literal("HS256"),
1277
+ typ: z.z.literal("JWT")
1260
1278
  });
1261
1279
  const jwtHeader = {
1262
1280
  alg: "HS256",
@@ -2271,9 +2289,9 @@ class ValOpsFS extends ValOps {
2271
2289
  });
2272
2290
  if (res.ok) {
2273
2291
  const json = await res.json();
2274
- const parsed = zod.z.object({
2275
- nonce: zod.z.string(),
2276
- expiresAt: zod.z.string()
2292
+ const parsed = z.z.object({
2293
+ nonce: z.z.string(),
2294
+ expiresAt: z.z.string()
2277
2295
  }).safeParse(json);
2278
2296
  if (parsed.success) {
2279
2297
  return {
@@ -2834,7 +2852,7 @@ class ValOpsFS extends ValOps {
2834
2852
  }]
2835
2853
  };
2836
2854
  }
2837
- const metadataParseRes = this.parseJsonFile(metadataFilePath, zod.z.record(zod.z.string(), zod.z.union([zod.z.string(), zod.z.number()])));
2855
+ const metadataParseRes = this.parseJsonFile(metadataFilePath, z.z.record(z.z.string(), z.z.union([z.z.string(), z.z.number()])));
2838
2856
  if (metadataParseRes.error) {
2839
2857
  return {
2840
2858
  errors: [metadataParseRes.error]
@@ -3240,121 +3258,121 @@ class FSOpsHost {
3240
3258
  fs__default["default"].copyFileSync(from, to);
3241
3259
  }
3242
3260
  }
3243
- const FSPatch = zod.z.object({
3244
- path: zod.z.string().refine(p => p.startsWith("/") && p.includes(".val."), "Path is not valid. Must start with '/' and include '.val.'"),
3261
+ const FSPatch = z.z.object({
3262
+ path: z.z.string().refine(p => p.startsWith("/") && p.includes(".val."), "Path is not valid. Must start with '/' and include '.val.'"),
3245
3263
  patch: internal.Patch,
3246
- patchId: zod.z.string(),
3247
- baseSha: zod.z.string(),
3264
+ patchId: z.z.string(),
3265
+ baseSha: z.z.string(),
3248
3266
  parentRef: internal.ParentRef,
3249
- authorId: zod.z.string().refine(p => true).nullable(),
3250
- createdAt: zod.z.string().datetime(),
3251
- coreVersion: zod.z.string().nullable(),
3267
+ authorId: z.z.string().refine(p => true).nullable(),
3268
+ createdAt: z.z.string().datetime(),
3269
+ coreVersion: z.z.string().nullable(),
3252
3270
  // TODO: use this to check if patch is compatible with current core version?
3253
- sessionId: zod.z.string().nullable()
3271
+ sessionId: z.z.string().nullable()
3254
3272
  });
3255
- const FSPatchBase = zod.z.object({
3256
- baseSha: zod.z.string().refine(p => true),
3257
- timestamp: zod.z.string().datetime()
3273
+ const FSPatchBase = z.z.object({
3274
+ baseSha: z.z.string().refine(p => true),
3275
+ timestamp: z.z.string().datetime()
3258
3276
  });
3259
3277
 
3260
3278
  const textEncoder$1 = new TextEncoder();
3261
- const PatchId = zod.z.string().refine(s => !!s); // TODO: validate
3262
- const CommitSha = zod.z.string().refine(s => !!s); // TODO: validate
3263
- zod.z.string().refine(s => !!s); // TODO: validate
3264
- zod.z.string().refine(s => !!s); // TODO: validate
3265
- const ModuleFilePath = zod.z.string().refine(s => !!s); // TODO: validate
3266
- const Metadata = zod.z.union([zod.z.object({
3267
- mimeType: zod.z.string(),
3268
- width: zod.z.number(),
3269
- height: zod.z.number()
3270
- }), zod.z.object({
3271
- mimeType: zod.z.string()
3279
+ const PatchId = z.z.string().refine(s => !!s); // TODO: validate
3280
+ const CommitSha = z.z.string().refine(s => !!s); // TODO: validate
3281
+ z.z.string().refine(s => !!s); // TODO: validate
3282
+ z.z.string().refine(s => !!s); // TODO: validate
3283
+ const ModuleFilePath = z.z.string().refine(s => !!s); // TODO: validate
3284
+ const Metadata = z.z.union([z.z.object({
3285
+ mimeType: z.z.string(),
3286
+ width: z.z.number(),
3287
+ height: z.z.number()
3288
+ }), z.z.object({
3289
+ mimeType: z.z.string()
3272
3290
  })]);
3273
- const MetadataRes = zod.z.object({
3291
+ const MetadataRes = z.z.object({
3274
3292
  filePath: ModuleFilePath,
3275
3293
  metadata: Metadata,
3276
- type: zod.z.union([zod.z.literal("file"), zod.z.literal("image")]).nullable()
3294
+ type: z.z.union([z.z.literal("file"), z.z.literal("image")]).nullable()
3277
3295
  });
3278
- const SummaryResponse = zod.z.object({
3279
- commitSummary: zod.z.string()
3296
+ const SummaryResponse = z.z.object({
3297
+ commitSummary: z.z.string()
3280
3298
  });
3281
- const GetApplicablePatches = zod.z.object({
3282
- patches: zod.z.array(zod.z.object({
3283
- path: zod.z.string(),
3299
+ const GetApplicablePatches = z.z.object({
3300
+ patches: z.z.array(z.z.object({
3301
+ path: z.z.string(),
3284
3302
  patch: internal.Patch.nullable(),
3285
- patchId: zod.z.string(),
3286
- authorId: zod.z.string().nullable(),
3287
- baseSha: zod.z.string(),
3288
- createdAt: zod.z.string(),
3289
- applied: zod.z.object({
3290
- commitSha: zod.z.string()
3303
+ patchId: z.z.string(),
3304
+ authorId: z.z.string().nullable(),
3305
+ baseSha: z.z.string(),
3306
+ createdAt: z.z.string(),
3307
+ applied: z.z.object({
3308
+ commitSha: z.z.string()
3291
3309
  }).nullable()
3292
3310
  })),
3293
- commits: zod.z.array(zod.z.object({
3294
- commitSha: zod.z.string(),
3295
- clientCommitSha: zod.z.string(),
3296
- parentCommitSha: zod.z.string(),
3297
- commitMessage: zod.z.string().nullable(),
3298
- branch: zod.z.string(),
3299
- creator: zod.z.string(),
3300
- createdAt: zod.z.string()
3311
+ commits: z.z.array(z.z.object({
3312
+ commitSha: z.z.string(),
3313
+ clientCommitSha: z.z.string(),
3314
+ parentCommitSha: z.z.string(),
3315
+ commitMessage: z.z.string().nullable(),
3316
+ branch: z.z.string(),
3317
+ creator: z.z.string(),
3318
+ createdAt: z.z.string()
3301
3319
  })).optional(),
3302
- deployments: zod.z.array(zod.z.object({
3303
- deploymentId: zod.z.string(),
3304
- commitSha: zod.z.string(),
3305
- deploymentState: zod.z.string(),
3306
- createdAt: zod.z.string(),
3307
- updatedAt: zod.z.string()
3320
+ deployments: z.z.array(z.z.object({
3321
+ deploymentId: z.z.string(),
3322
+ commitSha: z.z.string(),
3323
+ deploymentState: z.z.string(),
3324
+ createdAt: z.z.string(),
3325
+ updatedAt: z.z.string()
3308
3326
  })).optional()
3309
3327
  });
3310
- const FilesResponse = zod.z.object({
3311
- files: zod.z.array(zod.z.union([zod.z.object({
3312
- filePath: zod.z.string(),
3313
- location: zod.z.literal("patch"),
3328
+ const FilesResponse = z.z.object({
3329
+ files: z.z.array(z.z.union([z.z.object({
3330
+ filePath: z.z.string(),
3331
+ location: z.z.literal("patch"),
3314
3332
  patchId: PatchId,
3315
- value: zod.z.string(),
3316
- remote: zod.z.boolean()
3317
- }), zod.z.object({
3318
- filePath: zod.z.string(),
3319
- location: zod.z.literal("repo"),
3333
+ value: z.z.string(),
3334
+ remote: z.z.boolean()
3335
+ }), z.z.object({
3336
+ filePath: z.z.string(),
3337
+ location: z.z.literal("repo"),
3320
3338
  commitSha: CommitSha,
3321
- value: zod.z.string()
3339
+ value: z.z.string()
3322
3340
  })])),
3323
- errors: zod.z.array(zod.z.union([zod.z.object({
3324
- filePath: zod.z.string(),
3325
- location: zod.z.literal("patch"),
3341
+ errors: z.z.array(z.z.union([z.z.object({
3342
+ filePath: z.z.string(),
3343
+ location: z.z.literal("patch"),
3326
3344
  patchId: PatchId,
3327
- message: zod.z.string(),
3328
- remote: zod.z.boolean()
3329
- }), zod.z.object({
3330
- filePath: zod.z.string(),
3331
- location: zod.z.literal("repo"),
3345
+ message: z.z.string(),
3346
+ remote: z.z.boolean()
3347
+ }), z.z.object({
3348
+ filePath: z.z.string(),
3349
+ location: z.z.literal("repo"),
3332
3350
  commitSha: CommitSha,
3333
- message: zod.z.string()
3351
+ message: z.z.string()
3334
3352
  })])).optional()
3335
3353
  });
3336
- const SavePatchResponse = zod.z.object({
3354
+ const SavePatchResponse = z.z.object({
3337
3355
  patchId: PatchId
3338
3356
  });
3339
- const DeletePatchesResponse = zod.z.object({
3340
- deleted: zod.z.array(PatchId),
3341
- errors: zod.z.array(zod.z.object({
3342
- message: zod.z.string(),
3357
+ const DeletePatchesResponse = z.z.object({
3358
+ deleted: z.z.array(PatchId),
3359
+ errors: z.z.array(z.z.object({
3360
+ message: z.z.string(),
3343
3361
  patchId: PatchId
3344
3362
  })).optional()
3345
3363
  });
3346
- const SavePatchFileResponse = zod.z.object({
3364
+ const SavePatchFileResponse = z.z.object({
3347
3365
  patchId: PatchId,
3348
3366
  filePath: ModuleFilePath
3349
3367
  });
3350
- const CommitResponse = zod.z.object({
3351
- updatedFiles: zod.z.array(zod.z.string()),
3368
+ const CommitResponse = z.z.object({
3369
+ updatedFiles: z.z.array(z.z.string()),
3352
3370
  commit: CommitSha,
3353
- branch: zod.z.string()
3371
+ branch: z.z.string()
3354
3372
  });
3355
- const NonceResponse = zod.z.object({
3356
- nonce: zod.z.string(),
3357
- url: zod.z.string()
3373
+ const NonceResponse = z.z.object({
3374
+ nonce: z.z.string(),
3375
+ url: z.z.string()
3358
3376
  });
3359
3377
  class ValOpsHttp extends ValOps {
3360
3378
  constructor(contentUrl, project, commitSha,
@@ -3397,9 +3415,9 @@ class ValOpsHttp extends ValOps {
3397
3415
  });
3398
3416
  if (res.ok) {
3399
3417
  const json = await res.json();
3400
- const parsed = zod.z.object({
3401
- nonce: zod.z.string(),
3402
- expiresAt: zod.z.string()
3418
+ const parsed = z.z.object({
3419
+ nonce: z.z.string(),
3420
+ expiresAt: z.z.string()
3403
3421
  }).safeParse(json);
3404
3422
  if (parsed.success) {
3405
3423
  const {
@@ -4238,8 +4256,8 @@ class ValOpsHttp extends ValOps {
4238
4256
  };
4239
4257
  }
4240
4258
  if ((_res$headers$get5 = res.headers.get("Content-Type")) !== null && _res$headers$get5 !== void 0 && _res$headers$get5.includes("application/json")) {
4241
- const json = zod.z.object({
4242
- isNotFastForward: zod.z.boolean().optional()
4259
+ const json = z.z.object({
4260
+ isNotFastForward: z.z.boolean().optional()
4243
4261
  }).safeParse(await res.json());
4244
4262
  if (json.success && json.data.isNotFastForward) {
4245
4263
  return {
@@ -4272,10 +4290,10 @@ class ValOpsHttp extends ValOps {
4272
4290
  }
4273
4291
 
4274
4292
  const host = process.env.VAL_CONTENT_URL || core.DEFAULT_CONTENT_HOST;
4275
- const SettingsSchema = zod.z.object({
4276
- publicProjectId: zod.z.string(),
4277
- remoteFileBuckets: zod.z.array(zod.z.object({
4278
- bucket: zod.z.string()
4293
+ const SettingsSchema = z.z.object({
4294
+ publicProjectId: z.z.string(),
4295
+ remoteFileBuckets: z.z.array(z.z.object({
4296
+ bucket: z.z.string()
4279
4297
  }))
4280
4298
  });
4281
4299
  async function getSettings(projectName, auth) {
@@ -4421,21 +4439,21 @@ function hasRemoteFileSchema(schema) {
4421
4439
 
4422
4440
  /* eslint-disable @typescript-eslint/no-unused-vars */
4423
4441
  const ValServer = (valModules, options, callbacks) => {
4424
- const AIContentBlock = zod.z.union([zod.z.object({
4425
- type: zod.z.literal("text"),
4426
- text: zod.z.string()
4427
- }), zod.z.object({
4428
- type: zod.z.literal("image_url"),
4429
- url: zod.z.string()
4442
+ const AIContentBlock = z.z.union([z.z.object({
4443
+ type: z.z.literal("text"),
4444
+ text: z.z.string()
4445
+ }), z.z.object({
4446
+ type: z.z.literal("image_url"),
4447
+ url: z.z.string()
4430
4448
  })]);
4431
- const AIMessageContent = zod.z.union([zod.z.string(), zod.z.array(AIContentBlock)]);
4432
- const ProfilesResponse = zod.z.object({
4433
- profiles: zod.z.array(zod.z.object({
4434
- profileId: zod.z.string(),
4435
- fullName: zod.z.string(),
4436
- email: zod.z.string().optional(),
4437
- avatar: zod.z.object({
4438
- url: zod.z.string()
4449
+ const AIMessageContent = z.z.union([z.z.string(), z.z.array(AIContentBlock)]);
4450
+ const ProfilesResponse = z.z.object({
4451
+ profiles: z.z.array(z.z.object({
4452
+ profileId: z.z.string(),
4453
+ fullName: z.z.string(),
4454
+ email: z.z.string().optional(),
4455
+ avatar: z.z.object({
4456
+ url: z.z.string()
4439
4457
  }).nullable()
4440
4458
  }))
4441
4459
  });
@@ -4942,12 +4960,12 @@ const ValServer = (valModules, options, callbacks) => {
4942
4960
  headers: getAuthHeaders(data.token, "application/json")
4943
4961
  });
4944
4962
  if (fetchRes.status === 200) {
4945
- const json = zod.z.object({
4946
- member_role: zod.z.union([zod.z.literal("owner"), zod.z.literal("developer"), zod.z.literal("editor")]).optional(),
4947
- id: zod.z.string(),
4948
- full_name: zod.z.string().optional(),
4949
- username: zod.z.string().optional(),
4950
- avatar_url: zod.z.string().optional()
4963
+ const json = z.z.object({
4964
+ member_role: z.z.union([z.z.literal("owner"), z.z.literal("developer"), z.z.literal("editor")]).optional(),
4965
+ id: z.z.string(),
4966
+ full_name: z.z.string().optional(),
4967
+ username: z.z.string().optional(),
4968
+ avatar_url: z.z.string().optional()
4951
4969
  }).safeParse(await fetchRes.json());
4952
4970
  if (json.success) {
4953
4971
  return {
@@ -4969,8 +4987,8 @@ const ValServer = (valModules, options, callbacks) => {
4969
4987
  };
4970
4988
  }
4971
4989
  } else {
4972
- const json = zod.z.object({
4973
- message: zod.z.string()
4990
+ const json = z.z.object({
4991
+ message: z.z.string()
4974
4992
  }).safeParse(await fetchRes.json());
4975
4993
  const message = internal.getErrorMessageFromUnknownJson(json, "Unknown error");
4976
4994
  return {
@@ -5832,8 +5850,8 @@ const ValServer = (valModules, options, callbacks) => {
5832
5850
  }
5833
5851
  };
5834
5852
  }
5835
- const PostSaveBody = zod.z.object({
5836
- patchIds: zod.z.array(zod.z.string().refine(id => true // TODO:
5853
+ const PostSaveBody = z.z.object({
5854
+ patchIds: z.z.array(z.z.string().refine(id => true // TODO:
5837
5855
  ))
5838
5856
  });
5839
5857
  const bodyRes = PostSaveBody.safeParse(body);
@@ -6069,16 +6087,16 @@ const ValServer = (valModules, options, callbacks) => {
6069
6087
  const authData = authDataRes.json.remoteFileAuth;
6070
6088
  const execFetch = async headers => {
6071
6089
  try {
6072
- const SessionsResponse = zod.z.object({
6073
- sessions: zod.z.array(zod.z.object({
6074
- id: zod.z.string(),
6075
- name: zod.z.string().nullable(),
6076
- createdAt: zod.z.string(),
6077
- updatedAt: zod.z.string()
6090
+ const SessionsResponse = z.z.object({
6091
+ sessions: z.z.array(z.z.object({
6092
+ id: z.z.string(),
6093
+ name: z.z.string().nullable(),
6094
+ createdAt: z.z.string(),
6095
+ updatedAt: z.z.string()
6078
6096
  })),
6079
- nextCursor: zod.z.object({
6080
- updatedAt: zod.z.string(),
6081
- id: zod.z.string()
6097
+ nextCursor: z.z.object({
6098
+ updatedAt: z.z.string(),
6099
+ id: z.z.string()
6082
6100
  }).nullable().optional()
6083
6101
  });
6084
6102
  const params = new URLSearchParams();
@@ -6269,14 +6287,14 @@ const ValServer = (valModules, options, callbacks) => {
6269
6287
  const authData = authDataRes.json.remoteFileAuth;
6270
6288
  const execFetch = async headers => {
6271
6289
  try {
6272
- const SessionMessagesResponse = zod.z.object({
6273
- messages: zod.z.array(zod.z.object({
6274
- role: zod.z.string(),
6290
+ const SessionMessagesResponse = z.z.object({
6291
+ messages: z.z.array(z.z.object({
6292
+ role: z.z.string(),
6275
6293
  content: AIMessageContent
6276
6294
  })),
6277
- nextCursor: zod.z.object({
6278
- updatedAt: zod.z.string(),
6279
- id: zod.z.string()
6295
+ nextCursor: z.z.object({
6296
+ updatedAt: z.z.string(),
6297
+ id: z.z.string()
6280
6298
  }).nullable().optional()
6281
6299
  });
6282
6300
  const params = new URLSearchParams();
@@ -6425,14 +6443,14 @@ const ValServer = (valModules, options, callbacks) => {
6425
6443
  };
6426
6444
  }
6427
6445
  const rawUpstreamJson = await upstreamRes.json();
6428
- const UpstreamResponse = zod.z.object({
6429
- patchId: zod.z.string(),
6430
- files: zod.z.array(zod.z.object({
6431
- filePath: zod.z.string(),
6432
- metadata: zod.z.object({
6433
- width: zod.z.number(),
6434
- height: zod.z.number(),
6435
- mimeType: zod.z.string()
6446
+ const UpstreamResponse = z.z.object({
6447
+ patchId: z.z.string(),
6448
+ files: z.z.array(z.z.object({
6449
+ filePath: z.z.string(),
6450
+ metadata: z.z.object({
6451
+ width: z.z.number(),
6452
+ height: z.z.number(),
6453
+ mimeType: z.z.string()
6436
6454
  })
6437
6455
  }))
6438
6456
  });
@@ -6591,8 +6609,8 @@ const ValServer = (valModules, options, callbacks) => {
6591
6609
  }
6592
6610
  };
6593
6611
  }
6594
- const UpstreamResponse = zod.z.object({
6595
- key: zod.z.string()
6612
+ const UpstreamResponse = z.z.object({
6613
+ key: z.z.string()
6596
6614
  });
6597
6615
  const json = UpstreamResponse.safeParse(await upstreamRes.json());
6598
6616
  if (!json.success) {
@@ -6775,18 +6793,18 @@ function getStateFromCookie(stateCookie) {
6775
6793
  function createStateCookie(state) {
6776
6794
  return Buffer.from(JSON.stringify(state), "utf8").toString("base64");
6777
6795
  }
6778
- const ValAppJwtPayload = zod.z.object({
6779
- sub: zod.z.string(),
6780
- exp: zod.z.number(),
6781
- project: zod.z.string(),
6782
- org: zod.z.string()
6796
+ const ValAppJwtPayload = z.z.object({
6797
+ sub: z.z.string(),
6798
+ exp: z.z.number(),
6799
+ project: z.z.string(),
6800
+ org: z.z.string()
6783
6801
  });
6784
- const IntegratedServerJwtPayload = zod.z.object({
6785
- sub: zod.z.string(),
6786
- exp: zod.z.number(),
6787
- token: zod.z.string(),
6788
- org: zod.z.string(),
6789
- project: zod.z.string()
6802
+ const IntegratedServerJwtPayload = z.z.object({
6803
+ sub: z.z.string(),
6804
+ exp: z.z.number(),
6805
+ token: z.z.string(),
6806
+ org: z.z.string(),
6807
+ project: z.z.string()
6790
6808
  });
6791
6809
  async function withAuth(secret, cookies, errorMessageType, handler) {
6792
6810
  const cookie = cookies[internal.VAL_SESSION_COOKIE];
@@ -7282,7 +7300,7 @@ function createValApiRouter(route, valServerPromise, convert) {
7282
7300
  let query = {};
7283
7301
  if (reqDefinition.query) {
7284
7302
  const queryRules = reqDefinition.query;
7285
- const queryRes = zod.z.object(queryRules).safeParse(actualQueryParams);
7303
+ const queryRes = z.z.object(queryRules).safeParse(actualQueryParams);
7286
7304
  if (!queryRes.success) {
7287
7305
  return zodErrorResult(queryRes.error, `invalid query params: (${JSON.stringify(actualQueryParams)})`);
7288
7306
  }
@@ -7356,7 +7374,7 @@ function getCookies(req, cookiesDef) {
7356
7374
  input[name.trim()] = value;
7357
7375
  }
7358
7376
  }
7359
- return zod.z.object(cookiesDef).safeParse(input);
7377
+ return z.z.object(cookiesDef).safeParse(input);
7360
7378
  }
7361
7379
 
7362
7380
  const JsFileLookupMapping = [
@@ -8241,6 +8259,294 @@ async function getFileMetadata(projectRoot, validationError) {
8241
8259
  return extractFileMetadata(fileRef);
8242
8260
  }
8243
8261
 
8262
+ function validateMetadata(actualMetadata, expectedMetadata) {
8263
+ const missingMetadata = [];
8264
+ const erroneousMetadata = {};
8265
+ if (typeof actualMetadata !== "object" || actualMetadata === null) {
8266
+ return {
8267
+ globalErrors: ["Metadata is wrong type: must be an object."]
8268
+ };
8269
+ }
8270
+ if (Array.isArray(actualMetadata)) {
8271
+ return {
8272
+ globalErrors: ["Metadata is wrong type: cannot be an array."]
8273
+ };
8274
+ }
8275
+ const recordMetadata = actualMetadata;
8276
+ const globalErrors = [];
8277
+ for (const anyKey in expectedMetadata) {
8278
+ if (typeof anyKey !== "string") {
8279
+ globalErrors.push(`Expected metadata has key '${anyKey}' that is not typeof 'string', but: '${typeof anyKey}'. This is most likely a Val bug.`);
8280
+ } else {
8281
+ if (anyKey in actualMetadata) {
8282
+ const key = anyKey;
8283
+ if (expectedMetadata[key] !== recordMetadata[key]) {
8284
+ erroneousMetadata[key] = `Expected metadata '${key}' to be ${JSON.stringify(expectedMetadata[key])}, but got ${JSON.stringify(recordMetadata[key])}.`;
8285
+ }
8286
+ } else {
8287
+ missingMetadata.push(anyKey);
8288
+ }
8289
+ }
8290
+ }
8291
+ if (globalErrors.length === 0 && missingMetadata.length === 0 && Object.keys(erroneousMetadata).length === 0) {
8292
+ return false;
8293
+ }
8294
+ return {
8295
+ missingMetadata,
8296
+ erroneousMetadata
8297
+ };
8298
+ }
8299
+
8300
+ /**
8301
+ * NOTE: this is intentionally NOT `SharedValConfig` from `@valbuild/shared`.
8302
+ * That schema requires `files.directory` to be exactly `/public/val`, whereas
8303
+ * this one accepts any path beneath `/public`. Unifying them would change which
8304
+ * configs are accepted, so they are kept separate on purpose.
8305
+ */
8306
+ const ValConfigSchema = z__default["default"].object({
8307
+ project: z__default["default"].string().optional(),
8308
+ root: z__default["default"].string().optional(),
8309
+ files: z__default["default"].object({
8310
+ directory: z__default["default"].string().refine(val => (val === "/public" || val.startsWith("/public/") && !val.endsWith("/")) &&
8311
+ // Reject path traversal so the directory cannot escape /public
8312
+ !val.split("/").some(segment => segment === "." || segment === ".."), {
8313
+ message: "files.directory must start with '/public', must not end with '/' and must not contain '.' or '..' segments"
8314
+ })
8315
+ }).optional(),
8316
+ gitCommit: z__default["default"].string().optional(),
8317
+ gitBranch: z__default["default"].string().optional(),
8318
+ defaultTheme: z__default["default"].union([z__default["default"].literal("light"), z__default["default"].literal("dark")]).optional(),
8319
+ ai: z__default["default"].object({
8320
+ commitMessages: z__default["default"].object({
8321
+ disabled: z__default["default"].boolean().optional()
8322
+ }).optional()
8323
+ }).optional()
8324
+ });
8325
+
8326
+ /**
8327
+ * `ENOENT` (nothing at this path) and `ENOTDIR` (a path segment is a file) both
8328
+ * mean the config file simply is not there.
8329
+ */
8330
+ function isFileNotFound(err) {
8331
+ const code = err && typeof err === "object" && "code" in err ? err.code : undefined;
8332
+ return code === "ENOENT" || code === "ENOTDIR";
8333
+ }
8334
+
8335
+ /**
8336
+ * Read and evaluate a `val.config.{ts,js}` file from disk.
8337
+ *
8338
+ * Returns `null` if the file does not exist, and throws if it exists but does
8339
+ * not export a valid `config` object.
8340
+ *
8341
+ * Used by the Val CLI and by `@valbuild/language-server`, so that an editor
8342
+ * resolves project config exactly the way the CLI does.
8343
+ */
8344
+ async function evalValConfigFile(projectRoot, configFileName) {
8345
+ const valConfigPath = path__namespace["default"].join(projectRoot, configFileName);
8346
+ let code;
8347
+ try {
8348
+ code = await fs__default$1["default"].readFile(valConfigPath, "utf-8");
8349
+ } catch (err) {
8350
+ // A missing file means "this project does not use this config file name",
8351
+ // which callers handle by trying the next candidate. Anything else (no read
8352
+ // permission, an IO error, a directory where a file was expected) is a real
8353
+ // problem and must not be reported as "no config".
8354
+ if (isFileNotFound(err)) {
8355
+ return null;
8356
+ }
8357
+ throw Error(`Could not read Val config file at path: '${valConfigPath}': ${err instanceof Error ? err.message : String(err)}`);
8358
+ }
8359
+ if (!code.trim()) {
8360
+ return null;
8361
+ }
8362
+ const transpiled = ts__default["default"].transpileModule(code, {
8363
+ compilerOptions: {
8364
+ target: ts__default["default"].ScriptTarget.ES2020,
8365
+ module: ts__default["default"].ModuleKind.CommonJS,
8366
+ esModuleInterop: true
8367
+ },
8368
+ fileName: valConfigPath
8369
+ });
8370
+ const projectRootRequire = node_module.createRequire(valConfigPath);
8371
+ const exportsObj = {};
8372
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8373
+ const sandbox = {
8374
+ exports: exportsObj,
8375
+ module: {
8376
+ exports: exportsObj
8377
+ },
8378
+ require: projectRootRequire,
8379
+ // NOTE: this is a security risk, but this code is running in the users own environment at the CLI level
8380
+ __filename: valConfigPath,
8381
+ __dirname: projectRoot,
8382
+ console,
8383
+ process
8384
+ };
8385
+ sandbox.global = sandbox;
8386
+ const context = vm__default["default"].createContext(sandbox);
8387
+ const script = new vm__default["default"].Script(transpiled.outputText, {
8388
+ filename: valConfigPath
8389
+ });
8390
+ script.runInContext(context);
8391
+ const valConfig = sandbox.module.exports.config;
8392
+ if (!valConfig) {
8393
+ throw Error(`Val config file at path: '${valConfigPath}' must export a config object. Got: ${valConfig}`);
8394
+ }
8395
+ const result = ValConfigSchema.safeParse(valConfig);
8396
+ if (!result.success) {
8397
+ throw Error(`Val config file at path: '${valConfigPath}' has invalid schema: ${result.error.message}`);
8398
+ }
8399
+ return result.data;
8400
+ }
8401
+
8402
+ /**
8403
+ * Resolve the project's Val config, trying the TypeScript file first and
8404
+ * falling back to JavaScript. Mirrors what the CLI has always done at each of
8405
+ * its call sites.
8406
+ */
8407
+ async function findAndEvalValConfigFile(projectRoot) {
8408
+ return (await evalValConfigFile(projectRoot, "val.config.ts")) || (await evalValConfigFile(projectRoot, "val.config.js"));
8409
+ }
8410
+
8411
+ /**
8412
+ * The Val login device flow, as reusable primitives.
8413
+ *
8414
+ * The CLI wraps these with terminal output, and `@valbuild/language-server`
8415
+ * wraps them with LSP `window/showDocument` and progress reporting. Neither the
8416
+ * polling nor the token handling is duplicated between them.
8417
+ *
8418
+ * Deliberately no `console` output and no `process.exit`: every failure is a
8419
+ * thrown {@link ValLoginError} so an embedder can decide how to surface it.
8420
+ */
8421
+
8422
+ const DEFAULT_LOGIN_HOST = "https://admin.val.build";
8423
+ function defaultHost() {
8424
+ return process.env.VAL_BUILD_URL || DEFAULT_LOGIN_HOST;
8425
+ }
8426
+ class ValLoginError extends Error {
8427
+ constructor(code, message, details) {
8428
+ super(message);
8429
+ this.code = code;
8430
+ this.details = details;
8431
+ this.name = "ValLoginError";
8432
+ }
8433
+ }
8434
+
8435
+ /** A login attempt that is waiting for the user to confirm in a browser. */
8436
+
8437
+ /**
8438
+ * Begin a login attempt. The caller is responsible for getting
8439
+ * {@link ValLoginSession.url} in front of the user.
8440
+ */
8441
+ async function startValLogin(options = {}) {
8442
+ var _response$headers$get;
8443
+ const host = options.host ?? defaultHost();
8444
+ const response = await fetch(`${host}/api/login`, {
8445
+ method: "POST",
8446
+ headers: {
8447
+ "Content-Type": "application/json"
8448
+ }
8449
+ });
8450
+ if (response.status >= 500) {
8451
+ const text = await response.text().catch(() => "");
8452
+ throw new ValLoginError("server-error", "An error occurred on the server.", text ? `Server response: ${text} (status: ${response.status})` : `Status: ${response.status}`);
8453
+ }
8454
+ if (!((_response$headers$get = response.headers.get("content-type")) !== null && _response$headers$get !== void 0 && _response$headers$get.includes("application/json"))) {
8455
+ const text = await response.text().catch(() => "");
8456
+ throw new ValLoginError("unexpected-content-type", "Unexpected failure while trying to login (content type was not JSON).", text ? `Server response: ${text} (status: ${response.status})` : `Status: ${response.status}`);
8457
+ }
8458
+ const json = await response.json();
8459
+ const nonce = json === null || json === void 0 ? void 0 : json.nonce;
8460
+ const url = json === null || json === void 0 ? void 0 : json.url;
8461
+ if (typeof nonce !== "string" || typeof url !== "string") {
8462
+ throw new ValLoginError("unexpected-response", "Unexpected response from the server.", JSON.stringify(json));
8463
+ }
8464
+ return {
8465
+ nonce,
8466
+ url
8467
+ };
8468
+ }
8469
+ const DEFAULT_LOGIN_MAX_DURATION = 5 * 60 * 1000; // 5 minutes
8470
+ const DEFAULT_LOGIN_POLL_INTERVAL = 1000;
8471
+
8472
+ /**
8473
+ * Poll until the user confirms the login in their browser.
8474
+ *
8475
+ * Accepts an `AbortSignal` so an editor can cancel the flow when the user
8476
+ * dismisses the prompt, instead of leaving a poll loop running for 5 minutes.
8477
+ */
8478
+ async function awaitValLoginConfirmation(nonce, options = {}) {
8479
+ const host = options.host ?? defaultHost();
8480
+ const maxDuration = options.maxDurationMs ?? DEFAULT_LOGIN_MAX_DURATION;
8481
+ const pollInterval = options.pollIntervalMs ?? DEFAULT_LOGIN_POLL_INTERVAL;
8482
+ const now = options.now ?? (() => Date.now());
8483
+ const start = now();
8484
+ while (now() - start < maxDuration) {
8485
+ var _options$signal, _options$signal2;
8486
+ if ((_options$signal = options.signal) !== null && _options$signal !== void 0 && _options$signal.aborted) {
8487
+ throw new ValLoginError("aborted", "Login was cancelled.");
8488
+ }
8489
+ await new Promise(resolve => setTimeout(resolve, pollInterval));
8490
+ if ((_options$signal2 = options.signal) !== null && _options$signal2 !== void 0 && _options$signal2.aborted) {
8491
+ throw new ValLoginError("aborted", "Login was cancelled.");
8492
+ }
8493
+ const response = await fetch(`${host}/api/login?token=${nonce}&consume=true`, {
8494
+ method: "POST"
8495
+ });
8496
+ if (response.status >= 500) {
8497
+ throw new ValLoginError("server-error", "An error occurred on the server.", `Status: ${response.status}`);
8498
+ }
8499
+ if (response.status === 200) {
8500
+ var _json$profile;
8501
+ const json = await response.json().catch(() => null);
8502
+ if (!json) {
8503
+ // A 200 with an empty body is not a confirmation; keep polling rather
8504
+ // than failing the login, which is what the CLI has always done.
8505
+ continue;
8506
+ }
8507
+ if (typeof ((_json$profile = json.profile) === null || _json$profile === void 0 ? void 0 : _json$profile.email) === "string" && typeof json.pat === "string") {
8508
+ return json;
8509
+ }
8510
+ throw new ValLoginError("unexpected-response", "Unexpected response from the server.", JSON.stringify(json));
8511
+ }
8512
+ }
8513
+ throw new ValLoginError("timeout", "Login confirmation timed out.");
8514
+ }
8515
+
8516
+ /**
8517
+ * Write a completed login to the project's personal access token file.
8518
+ *
8519
+ * @returns the path the token was written to.
8520
+ */
8521
+ function persistPersonalAccessToken(projectRoot, result) {
8522
+ const filePath = getPersonalAccessTokenPath(projectRoot);
8523
+ // NOTE: no restrictive mode on the directory. This is the project's shared
8524
+ // `.val`, which also holds the pending patches the dev server reads and
8525
+ // writes; making it owner-only would lock out anyone who did not run the
8526
+ // login. Only the token file itself is tightened.
8527
+ fs__default["default"].mkdirSync(path__namespace["default"].dirname(filePath), {
8528
+ recursive: true
8529
+ });
8530
+ // The token is a credential: keep it owner-only rather than at the mercy of
8531
+ // the process umask. `mode` only applies when the file is created, so an
8532
+ // already existing file is chmod-ed explicitly.
8533
+ fs__default["default"].writeFileSync(filePath, JSON.stringify(result, null, 2), {
8534
+ mode: 0o600
8535
+ });
8536
+ try {
8537
+ fs__default["default"].chmodSync(filePath, 0o600);
8538
+ } catch {
8539
+ // Not all platforms / filesystems support POSIX modes (Windows, some
8540
+ // network mounts). Failing to tighten them must not fail the login.
8541
+ }
8542
+ return filePath;
8543
+ }
8544
+
8545
+ /**
8546
+ * NOTE: `@valbuild/language-server` has a near-identical `modulePathMap.ts`.
8547
+ * Fix traversal bugs in both, or fold them together.
8548
+ */
8549
+
8244
8550
  function getModulePathRange(modulePath, modulePathMap,
8245
8551
  // Which part of an object/record member to point at. For an object property
8246
8552
  // the resolved node's own range is the *key* (property name); the *value*
@@ -8499,12 +8805,19 @@ function readCapturedReport(snapshotDir) {
8499
8805
  return JSON.parse(fs__default["default"].readFileSync(reportPath, "utf-8"));
8500
8806
  }
8501
8807
 
8808
+ exports.DEFAULT_LOGIN_HOST = DEFAULT_LOGIN_HOST;
8809
+ exports.DEFAULT_LOGIN_MAX_DURATION = DEFAULT_LOGIN_MAX_DURATION;
8810
+ exports.DEFAULT_LOGIN_POLL_INTERVAL = DEFAULT_LOGIN_POLL_INTERVAL;
8502
8811
  exports.Service = Service;
8503
8812
  exports.ValFSHost = ValFSHost;
8813
+ exports.ValLoginError = ValLoginError;
8504
8814
  exports.ValModuleLoader = ValModuleLoader;
8505
8815
  exports.ValOpsFS = ValOpsFS;
8506
8816
  exports.ValOpsHttp = ValOpsHttp;
8507
8817
  exports.ValSourceFileHandler = ValSourceFileHandler;
8818
+ exports.analyzeValModule = analyzeValModule;
8819
+ exports.awaitValLoginConfirmation = awaitValLoginConfirmation;
8820
+ exports.checkRemoteRef = checkRemoteRef;
8508
8821
  exports.compareWithCapturedReport = compareWithCapturedReport;
8509
8822
  exports.createFixPatch = createFixPatch;
8510
8823
  exports.createModulePathMap = createModulePathMap;
@@ -8512,18 +8825,31 @@ exports.createService = createService;
8512
8825
  exports.createValApiRouter = createValApiRouter;
8513
8826
  exports.createValServer = createValServer;
8514
8827
  exports.decodeJwt = decodeJwt;
8828
+ exports.downloadFileFromRemote = downloadFileFromRemote;
8515
8829
  exports.encodeJwt = encodeJwt;
8830
+ exports.evalValConfigFile = evalValConfigFile;
8831
+ exports.extractFileMetadata = extractFileMetadata;
8832
+ exports.extractImageMetadata = extractImageMetadata;
8833
+ exports.findAndEvalValConfigFile = findAndEvalValConfigFile;
8516
8834
  exports.formatPatchSourceError = formatPatchSourceError;
8517
8835
  exports.formatSyntaxErrorTree = formatSyntaxErrorTree;
8836
+ exports.getCachedRemoteFileDir = getCachedRemoteFileDir;
8837
+ exports.getCachedRemoteFilePath = getCachedRemoteFilePath;
8518
8838
  exports.getCompilerOptions = getCompilerOptions;
8519
8839
  exports.getExpire = getExpire;
8840
+ exports.getFileExt = getFileExt;
8520
8841
  exports.getModulePathRange = getModulePathRange;
8521
8842
  exports.getPersonalAccessTokenPath = getPersonalAccessTokenPath;
8522
8843
  exports.getSettings = getSettings;
8844
+ exports.getValidationErrorFileRef = getValidationErrorFileRef;
8845
+ exports.hasRemoteFileSchema = hasRemoteFileSchema;
8523
8846
  exports.loadValModules = loadValModules;
8524
8847
  exports.parsePersonalAccessTokenFile = parsePersonalAccessTokenFile;
8525
8848
  exports.patchSourceFile = patchSourceFile;
8849
+ exports.persistPersonalAccessToken = persistPersonalAccessToken;
8526
8850
  exports.readCapturedReport = readCapturedReport;
8527
8851
  exports.replaySnapshot = replaySnapshot;
8528
8852
  exports.safeReadGit = safeReadGit;
8853
+ exports.startValLogin = startValLogin;
8529
8854
  exports.uploadRemoteFile = uploadRemoteFile;
8855
+ exports.validateMetadata = validateMetadata;