@valbuild/server 0.60.20 → 0.60.22

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.
@@ -15,7 +15,6 @@ var z = require('zod');
15
15
  var internal = require('@valbuild/shared/internal');
16
16
  var sizeOf = require('image-size');
17
17
  var crypto = require('crypto');
18
- var minimatch = require('minimatch');
19
18
  var server = require('@valbuild/ui/server');
20
19
 
21
20
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
@@ -44,7 +43,6 @@ var fs__default = /*#__PURE__*/_interopDefault(fs);
44
43
  var z__default = /*#__PURE__*/_interopDefault(z);
45
44
  var sizeOf__default = /*#__PURE__*/_interopDefault(sizeOf);
46
45
  var crypto__default = /*#__PURE__*/_interopDefault(crypto);
47
- var minimatch__default = /*#__PURE__*/_interopDefault(minimatch);
48
46
 
49
47
  class ValSyntaxError {
50
48
  constructor(message, node) {
@@ -693,7 +691,7 @@ const patchSourceFile = (sourceFile, patch$1) => {
693
691
  return patch.applyPatch(sourceFile, ops$1, patch$1);
694
692
  };
695
693
 
696
- const readValFile = async (id, valConfigPath, runtime, options) => {
694
+ const readValFile = async (id, rootDirPath, runtime, options) => {
697
695
  const context = runtime.newContext();
698
696
 
699
697
  // avoid failures when console.log is called
@@ -745,7 +743,7 @@ globalThis.valModule = {
745
743
  `;
746
744
  const result = context.evalCode(code,
747
745
  // Synthetic module name
748
- path__namespace["default"].join(path__namespace["default"].dirname(valConfigPath), "<val>"));
746
+ path__namespace["default"].join(rootDirPath, "<val>"));
749
747
  const fatalErrors = [];
750
748
  if (result.error) {
751
749
  const error = result.error.consume(context.dump);
@@ -1190,18 +1188,16 @@ async function createService(projectRoot, opts, host = {
1190
1188
  const sourceFileHandler = new ValSourceFileHandler(projectRoot, compilerOptions, host);
1191
1189
  const module = await quickjsEmscripten.newQuickJSWASMModule();
1192
1190
  const runtime = await newValQuickJSRuntime(module, loader || new ValModuleLoader(projectRoot, compilerOptions, sourceFileHandler, host, opts.disableCache === undefined ? true : opts.disableCache));
1193
- return new Service(opts, sourceFileHandler, runtime);
1191
+ return new Service(projectRoot, sourceFileHandler, runtime);
1194
1192
  }
1195
1193
  class Service {
1196
- constructor({
1197
- valConfigPath
1198
- }, sourceFileHandler, runtime) {
1194
+ constructor(projectRoot, sourceFileHandler, runtime) {
1199
1195
  this.sourceFileHandler = sourceFileHandler;
1200
1196
  this.runtime = runtime;
1201
- this.valConfigPath = valConfigPath || "./val.config";
1197
+ this.projectRoot = projectRoot;
1202
1198
  }
1203
1199
  async get(moduleId, modulePath, options) {
1204
- const valModule = await readValFile(moduleId, this.valConfigPath, this.runtime, options ?? {
1200
+ const valModule = await readValFile(moduleId, this.projectRoot, this.runtime, options ?? {
1205
1201
  validate: true,
1206
1202
  source: true,
1207
1203
  schema: true
@@ -1225,7 +1221,7 @@ class Service {
1225
1221
  }
1226
1222
  }
1227
1223
  async patch(moduleId, patch) {
1228
- await patchValFile(moduleId, this.valConfigPath, patch, this.sourceFileHandler, this.runtime);
1224
+ await patchValFile(moduleId, this.projectRoot, patch, this.sourceFileHandler, this.runtime);
1229
1225
  }
1230
1226
  dispose() {
1231
1227
  this.runtime.dispose();
@@ -1416,9 +1412,8 @@ function getValidationErrorMetadata(validationError) {
1416
1412
 
1417
1413
  const ops = new patch.JSONOps();
1418
1414
  class ValServer {
1419
- constructor(cwd, host, options, callbacks) {
1415
+ constructor(cwd, options, callbacks) {
1420
1416
  this.cwd = cwd;
1421
- this.host = host;
1422
1417
  this.options = options;
1423
1418
  this.callbacks = callbacks;
1424
1419
  }
@@ -1454,19 +1449,10 @@ class ValServer {
1454
1449
  redirectTo: redirectToRes
1455
1450
  };
1456
1451
  }
1457
- getAllModules(treePath) {
1458
- const moduleIds = this.host.readDirectory(this.cwd, ["ts", "js"], ["node_modules", ".*"], ["**/*.val.ts", "**/*.val.js"]).filter(file => {
1459
- if (treePath) {
1460
- return file.replace(this.cwd, "").startsWith(treePath);
1461
- }
1462
- return true;
1463
- }).map(file => file.replace(this.cwd, "").replace(".val.js", "").replace(".val.ts", "").split(path__namespace["default"].sep).join("/"));
1464
- return moduleIds;
1465
- }
1466
1452
  async getTree(treePath,
1467
1453
  // TODO: use the params: patch, schema, source now we return everything, every time
1468
1454
  query, cookies, requestHeaders) {
1469
- const ensureRes = await this.ensureRemoteFSInitialized("getTree", cookies);
1455
+ const ensureRes = await this.ensureInitialized("getTree", cookies);
1470
1456
  if (fp.result.isErr(ensureRes)) {
1471
1457
  return ensureRes.error;
1472
1458
  }
@@ -1474,7 +1460,7 @@ class ValServer {
1474
1460
  const execValidations = query.validate === "true";
1475
1461
  const includeSource = query.source === "true";
1476
1462
  const includeSchema = query.schema === "true";
1477
- const moduleIds = this.getAllModules(treePath);
1463
+ const moduleIds = await this.getAllModules(treePath);
1478
1464
  let {
1479
1465
  patchIdsByModuleId,
1480
1466
  patchesById,
@@ -1514,14 +1500,14 @@ class ValServer {
1514
1500
  };
1515
1501
  }
1516
1502
  async postValidate(rawBody, cookies, requestHeaders) {
1517
- const ensureRes = await this.ensureRemoteFSInitialized("postValidate", cookies);
1503
+ const ensureRes = await this.ensureInitialized("postValidate", cookies);
1518
1504
  if (fp.result.isErr(ensureRes)) {
1519
1505
  return ensureRes.error;
1520
1506
  }
1521
1507
  return this.validateThenMaybeCommit(rawBody, false, cookies, requestHeaders);
1522
1508
  }
1523
1509
  async postCommit(rawBody, cookies, requestHeaders) {
1524
- const ensureRes = await this.ensureRemoteFSInitialized("postCommit", cookies);
1510
+ const ensureRes = await this.ensureInitialized("postCommit", cookies);
1525
1511
  if (fp.result.isErr(ensureRes)) {
1526
1512
  return ensureRes.error;
1527
1513
  }
@@ -1547,7 +1533,6 @@ class ValServer {
1547
1533
  }
1548
1534
 
1549
1535
  /* */
1550
-
1551
1536
  async applyAllPatchesThenValidate(moduleId, patchIdsByModuleId, patchesById, fileUpdates, cookies, requestHeaders, applyPatches, validate, includeSource, includeSchema) {
1552
1537
  const serializedModuleContent = await this.getModule(moduleId, {
1553
1538
  validate: validate,
@@ -1812,7 +1797,7 @@ class ValServer {
1812
1797
  fileUpdates
1813
1798
  } = res.value;
1814
1799
  const validationErrorsByModuleId = {};
1815
- for (const moduleIdStr of this.getAllModules("/")) {
1800
+ for (const moduleIdStr of await this.getAllModules("/")) {
1816
1801
  const moduleId = moduleIdStr;
1817
1802
  const serializedModuleContent = await this.applyAllPatchesThenValidate(moduleId, filterPatchesByModuleIdRes.data.patches ||
1818
1803
  // TODO: refine to ModuleId and PatchId when parsing
@@ -2055,10 +2040,11 @@ class LocalValServer extends ValServer {
2055
2040
  static PATCHES_DIR = "patches";
2056
2041
  static FILES_DIR = "files";
2057
2042
  constructor(options, callbacks) {
2058
- super(options.service.sourceFileHandler.projectRoot, options.service.sourceFileHandler.host, options, callbacks);
2043
+ super(options.service.sourceFileHandler.projectRoot, options, callbacks);
2059
2044
  this.options = options;
2060
2045
  this.callbacks = callbacks;
2061
2046
  this.patchesRootPath = options.cacheDir || path__namespace["default"].join(options.service.sourceFileHandler.projectRoot, ".val");
2047
+ this.host = this.options.service.sourceFileHandler.host;
2062
2048
  }
2063
2049
  async session() {
2064
2050
  return {
@@ -2316,13 +2302,22 @@ class LocalValServer extends ValServer {
2316
2302
  }
2317
2303
  };
2318
2304
  }
2319
- async ensureRemoteFSInitialized() {
2305
+ async ensureInitialized() {
2320
2306
  // No RemoteFS so nothing to ensure
2321
2307
  return fp.result.ok(undefined);
2322
2308
  }
2323
2309
  getModule(moduleId, options) {
2324
2310
  return this.options.service.get(moduleId, "", options);
2325
2311
  }
2312
+ async getAllModules(treePath) {
2313
+ const moduleIds = this.host.readDirectory(this.cwd, ["ts", "js"], ["node_modules", ".*"], ["**/*.val.ts", "**/*.val.js"]).filter(file => {
2314
+ if (treePath) {
2315
+ return file.replace(this.cwd, "").startsWith(treePath);
2316
+ }
2317
+ return true;
2318
+ }).map(file => file.replace(this.cwd, "").replace(".val.js", "").replace(".val.ts", "").split(path__namespace["default"].sep).join("/"));
2319
+ return moduleIds;
2320
+ }
2326
2321
  async execCommit(patches) {
2327
2322
  for (const [patchId, moduleId, patch] of patches) {
2328
2323
  // TODO: patch the entire module content directly by using a { path: "", op: "replace", value: patchedData }?
@@ -2409,219 +2404,32 @@ function encodeJwt(payload, sessionKey) {
2409
2404
  return `${jwtHeaderBase64}.${payloadBase64}.${crypto__default["default"].createHmac("sha256", sessionKey).update(`${jwtHeaderBase64}.${payloadBase64}`).digest("base64")}`;
2410
2405
  }
2411
2406
 
2412
- const SEPARATOR = "/";
2413
- class RemoteFS {
2414
- initialized = false;
2415
- constructor() {
2416
- this.data = {};
2417
- this.modifiedFiles = [];
2418
- this.deletedFiles = [];
2419
- }
2420
- useCaseSensitiveFileNames = true;
2421
- isInitialized() {
2422
- return this.initialized;
2423
- }
2424
- async initializeWith(data) {
2425
- this.data = data;
2426
- this.initialized = true;
2427
- }
2428
- async getPendingOperations() {
2429
- const modified = {};
2430
- for (const modifiedFile of this.modifiedFiles) {
2431
- const {
2432
- directory,
2433
- filename
2434
- } = RemoteFS.parsePath(modifiedFile);
2435
- modified[modifiedFile] = this.data[directory].utf8Files[filename];
2436
- }
2437
- return {
2438
- modified: modified,
2439
- deleted: this.deletedFiles
2440
- };
2441
- }
2442
- changedDirectories = {};
2443
- readDirectory = (rootDir, extensions, excludes, includes, depth) => {
2444
- // TODO: rewrite this! And make some tests! This is a mess!
2445
- // Considered using glob which typescript seems to use, but that works on an entire typeof fs
2446
- // glob uses minimatch internally, so using that instead
2447
- const files = [];
2448
- for (const dir in this.data) {
2449
- const depthExceeded = depth ? dir.replace(rootDir, "").split(SEPARATOR).length > depth : false;
2450
- if (dir.startsWith(rootDir) && !depthExceeded) {
2451
- for (const file in this.data[dir].utf8Files) {
2452
- for (const extension of extensions) {
2453
- if (file.endsWith(extension)) {
2454
- const path = `${dir}/${file}`;
2455
- for (const include of includes ?? []) {
2456
- // TODO: should default includes be ['**/*']?
2457
- if (minimatch__default["default"](path, include)) {
2458
- let isExcluded = false;
2459
- for (const exlude of excludes ?? []) {
2460
- if (minimatch__default["default"](path, exlude)) {
2461
- isExcluded = true;
2462
- break;
2463
- }
2464
- }
2465
- if (!isExcluded) {
2466
- files.push(path);
2467
- }
2468
- }
2469
- }
2470
- }
2471
- }
2472
- }
2473
- }
2474
- }
2475
- return ts__default["default"].sys.readDirectory(rootDir, extensions, excludes, includes, depth).concat(files);
2476
- };
2477
- writeFile = (filePath, data, encoding) => {
2478
- // never write real fs
2479
- const {
2480
- directory,
2481
- filename
2482
- } = RemoteFS.parsePath(filePath);
2483
- if (this.data[directory] === undefined) {
2484
- throw new Error(`Directory not found: ${directory}`);
2485
- }
2486
- this.changedDirectories[directory] = this.changedDirectories[directory] ?? new Set();
2487
-
2488
- // if it fails below this should not be added, so maybe a try/catch?
2489
- this.changedDirectories[directory].add(filename);
2490
- this.data[directory].utf8Files[filename] = data;
2491
- this.modifiedFiles.push(filePath);
2492
- };
2493
- rmFile(filePath) {
2494
- // never remove from real fs
2495
- const {
2496
- directory,
2497
- filename
2498
- } = RemoteFS.parsePath(filePath);
2499
- if (this.data[directory] === undefined) {
2500
- throw new Error(`Directory not found: ${directory}`);
2501
- }
2502
- this.changedDirectories[directory] = this.changedDirectories[directory] ?? new Set();
2503
-
2504
- // if it fails below this should not be added, so maybe a try/catch?
2505
- this.changedDirectories[directory].add(filename);
2506
- delete this.data[directory].utf8Files[filename];
2507
- delete this.data[directory].symlinks[filename];
2508
- this.deletedFiles.push(filePath);
2509
- }
2510
- fileExists = filePath => {
2511
- var _this$data$directory;
2512
- if (ts__default["default"].sys.fileExists(filePath)) {
2513
- return true;
2514
- }
2515
- const {
2516
- directory,
2517
- filename
2518
- } = RemoteFS.parsePath(this.realpath(filePath) // ts.sys seems to resolve symlinks while calling fileExists, i.e. a broken symlink (pointing to a non-existing file) is not considered to exist
2519
- );
2520
- return !!((_this$data$directory = this.data[directory]) !== null && _this$data$directory !== void 0 && _this$data$directory.utf8Files[filename]);
2521
- };
2522
- readFile = filePath => {
2523
- const realFile = ts__default["default"].sys.readFile(filePath);
2524
- if (realFile !== undefined) {
2525
- return realFile;
2526
- }
2527
- const {
2528
- directory,
2529
- filename
2530
- } = RemoteFS.parsePath(filePath);
2531
- const dirNode = this.data[directory];
2532
- if (!dirNode) {
2533
- return undefined;
2534
- }
2535
- const content = dirNode.utf8Files[filename];
2536
- return content;
2537
- };
2538
- realpath(fullPath) {
2539
- if (ts__default["default"].sys.fileExists(fullPath) && ts__default["default"].sys.realpath) {
2540
- return ts__default["default"].sys.realpath(fullPath);
2541
- }
2542
- // TODO: this only works in a very limited way.
2543
- // It does not support symlinks to symlinks nor symlinked directories for instance.
2544
- const {
2545
- directory,
2546
- filename
2547
- } = RemoteFS.parsePath(fullPath);
2548
- if (this.data[directory] === undefined) {
2549
- return fullPath;
2550
- }
2551
- if (this.data[directory].utf8Files[filename] === undefined) {
2552
- const link = this.data[directory].symlinks[filename];
2553
- if (link === undefined) {
2554
- return fullPath;
2555
- } else {
2556
- return link;
2557
- }
2558
- } else {
2559
- return path__namespace["default"].join(directory, filename);
2560
- }
2561
- }
2562
-
2563
- /**
2564
- *
2565
- * @param path
2566
- * @returns directory and filename. NOTE: directory might be empty string
2567
- */
2568
- static parsePath(path) {
2569
- const pathParts = path.split(SEPARATOR);
2570
- const filename = pathParts.pop();
2571
- if (!filename) {
2572
- throw new Error(`Invalid path: '${path}'. Node filename: '${filename}'`);
2573
- }
2574
- const directory = pathParts.join(SEPARATOR);
2575
- return {
2576
- directory,
2577
- filename
2578
- };
2579
- }
2580
- }
2581
-
2582
- /**
2583
- * Represents directories
2584
- * NOTE: the keys of directory nodes are the "full" path, i.e. "foo/bar"
2585
- * NOTE: the keys of file nodes are the "filename" only, i.e. "baz.txt"
2586
- *
2587
- * @example
2588
- * {
2589
- * "foo/bar": { // <- directory. NOTE: this is the "full" path
2590
- * gitHubSha: "123",
2591
- * files: {
2592
- * "baz.txt": "hello world" // <- file. NOTE: this is the "filename" only
2593
- * },
2594
- * },
2595
- * };
2596
- */
2597
- // TODO: a Map would be better here
2598
-
2599
2407
  class ProxyValServer extends ValServer {
2600
- moduleCache = {};
2408
+ moduleCache = null;
2601
2409
  constructor(cwd, options, apiOptions, callbacks) {
2602
- const remoteFS = new RemoteFS();
2603
- super(cwd, remoteFS, options, callbacks);
2410
+ super(cwd, options, callbacks);
2604
2411
  this.cwd = cwd;
2605
2412
  this.options = options;
2606
2413
  this.apiOptions = apiOptions;
2607
2414
  this.callbacks = callbacks;
2608
- this.remoteFS = remoteFS;
2415
+ this.moduleCache = null;
2609
2416
  }
2610
2417
 
2611
2418
  /** Remote FS dependent methods: */
2612
2419
 
2613
- async getModule(moduleId, options) {
2614
- const cacheKey = moduleId + ";" + JSON.stringify(options) + this.options.git.commit + this.cwd;
2615
- const cachedModule = this.moduleCache[cacheKey];
2616
- if (cachedModule) {
2617
- return Promise.resolve(cachedModule);
2420
+ async getModule(moduleId,
2421
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2422
+ _options) {
2423
+ if (this.moduleCache) {
2424
+ return this.moduleCache[moduleId];
2618
2425
  }
2619
- if (!this.lazyService) {
2620
- this.lazyService = await createService(this.cwd, this.apiOptions, this.remoteFS);
2426
+ throw new Error("Module cache not initialized");
2427
+ }
2428
+ async getAllModules(treePath) {
2429
+ if (!this.moduleCache) {
2430
+ throw new Error("Module cache not initialized");
2621
2431
  }
2622
- const currentModule = await this.lazyService.get(moduleId, "", options);
2623
- this.moduleCache[cacheKey] = currentModule;
2624
- return currentModule;
2432
+ return Object.keys(this.moduleCache).filter(moduleId => moduleId.startsWith(treePath));
2625
2433
  }
2626
2434
  execCommit(patches, cookies) {
2627
2435
  return withAuth(this.options.valSecret, cookies, "execCommit", async ({
@@ -2636,46 +2444,19 @@ class ProxyValServer extends ValServer {
2636
2444
  }
2637
2445
  };
2638
2446
  }
2639
- const params = `commit=${encodeURIComponent(commit)}&root=${encodeURIComponent(this.apiOptions.root || "/")}&cwd=${encodeURIComponent(this.cwd)}`;
2447
+ const params = createParams({
2448
+ root: this.apiOptions.root,
2449
+ commit,
2450
+ ext: ["ts", "js", "json"],
2451
+ package: ["@valbuild/core@" + this.options.versions.core, "@valbuild/next@" + this.options.versions.next],
2452
+ include: ["**/*.val.{js,ts},package.json,tsconfig.json,jsconfig.json"]
2453
+ });
2640
2454
  const url = new URL(`/v1/commit/${this.options.remote}/heads/${this.options.git.branch}/~?${params}`, this.options.valContentUrl);
2641
-
2642
- // Creates a fresh copy of the fs. We cannot touch the existing fs, since there might be parallel operations?
2643
- // We could perhaps free up the other fs while doing this operation, but uncertain if we can actually do that and if that would actually help on memory.
2644
- // It is a concern we have, since we might be using quite a lot of memory when having the whole FS in memory.
2645
- // NOTE that base64 values from patches are not part of the patches, nor are they part of the fs so at least we do not have to worry about them.
2646
- // This NOTE was written after we wrote the comments above. We are a bit uncertain whether memory usage should be a concern at this point.
2647
- const remoteFS = new RemoteFS();
2648
- const initRes = await this.initRemoteFS(commit, remoteFS, token);
2649
- if (initRes.status !== 200) {
2650
- return initRes;
2651
- }
2652
- const service = await createService(this.cwd, this.apiOptions, remoteFS);
2653
- // TODO: optimize patches, e.g. only take the last replace for a given thing, etc...
2654
- const patchIds = [];
2655
- const binaryFileUpdates = {};
2656
- for (const [patchId, moduleId, patch] of patches) {
2657
- const patchableOps = [];
2658
- for (const op of patch) {
2659
- if (isCachedPatchFileOp(op)) {
2660
- binaryFileUpdates[op.filePath] = op.value;
2661
- } else {
2662
- if (core.Internal.isFileOp(op)) {
2663
- throw new Error(`Val: Unexpected file operation (file: ${op.filePath}). This is likely a Val bug.`);
2664
- }
2665
- patchableOps.push(op);
2666
- }
2667
- }
2668
- await service.patch(moduleId, patchableOps);
2669
- patchIds.push(patchId);
2670
- }
2671
- const sourceFileUpdates = await remoteFS.getPendingOperations();
2455
+ const patchIds = patches.map(([patchId]) => patchId);
2672
2456
  const fetchRes = await fetch(url, {
2673
2457
  method: "POST",
2674
2458
  headers: getAuthHeaders(token, "application/json"),
2675
2459
  body: JSON.stringify({
2676
- sourceFileUpdates: sourceFileUpdates.modified,
2677
- binaryFileUpdates,
2678
- deletedFiles: sourceFileUpdates.deleted,
2679
2460
  patchIds
2680
2461
  })
2681
2462
  });
@@ -2689,14 +2470,15 @@ class ProxyValServer extends ValServer {
2689
2470
  }
2690
2471
  });
2691
2472
  }
2692
- async initRemoteFS(commit, remoteFS, token) {
2693
- const params = new URLSearchParams(this.apiOptions.root ? {
2473
+ async init(commit, token) {
2474
+ const params = createParams({
2694
2475
  root: this.apiOptions.root,
2695
- commit
2696
- } : {
2697
- commit
2476
+ commit,
2477
+ ext: ["ts", "js", "json"],
2478
+ package: ["@valbuild/core@" + this.options.versions.core, "@valbuild/next@" + this.options.versions.next],
2479
+ include: ["**/*.val.{js,ts},package.json,tsconfig.json,jsconfig.json"]
2698
2480
  });
2699
- const url = new URL(`/v1/fs/${this.options.remote}/heads/${this.options.git.branch}/~?${params}`, this.options.valContentUrl);
2481
+ const url = new URL(`/v1/eval/${this.options.remote}/heads/${this.options.git.branch}/~?${params}`, this.options.valContentUrl);
2700
2482
  try {
2701
2483
  const fetchRes = await fetch(url, {
2702
2484
  headers: getAuthHeaders(token, "application/json")
@@ -2719,22 +2501,22 @@ class ProxyValServer extends ValServer {
2719
2501
  details: "Invalid response: missing git.commit"
2720
2502
  };
2721
2503
  }
2722
- if (typeof json.directories !== "object" || json.directories === null) {
2504
+ if (typeof json.modules !== "object" || json.modules === null) {
2723
2505
  error = {
2724
- details: "Invalid response: missing directories"
2506
+ details: "Invalid response: missing modules"
2725
2507
  };
2726
2508
  }
2727
2509
  if (error) {
2510
+ console.error("Could not initialize remote modules", error);
2728
2511
  return {
2729
2512
  status: 500,
2730
2513
  json: {
2731
- message: "Failed to fetch remote files",
2514
+ message: "Failed to fetch remote modules",
2732
2515
  ...error
2733
2516
  }
2734
2517
  };
2735
2518
  }
2736
- remoteFS.initializeWith(Object.fromEntries(Object.entries(json.directories).map(([dir, content]) => [path__namespace["default"].join(this.cwd, ...dir.split("/") // content is always posix - not sure that matters...
2737
- ), content])));
2519
+ this.moduleCache = json.modules;
2738
2520
  return {
2739
2521
  status: 200
2740
2522
  };
@@ -2750,7 +2532,7 @@ class ProxyValServer extends ValServer {
2750
2532
  };
2751
2533
  }
2752
2534
  }
2753
- async ensureRemoteFSInitialized(errorMessageType, cookies) {
2535
+ async ensureInitialized(errorMessageType, cookies) {
2754
2536
  const commit = this.options.git.commit;
2755
2537
  if (!commit) {
2756
2538
  return fp.result.err({
@@ -2761,8 +2543,8 @@ class ProxyValServer extends ValServer {
2761
2543
  });
2762
2544
  }
2763
2545
  const res = await withAuth(this.options.valSecret, cookies, errorMessageType, async data => {
2764
- if (!this.remoteFS.isInitialized()) {
2765
- return this.initRemoteFS(commit, this.remoteFS, data.token);
2546
+ if (!this.moduleCache) {
2547
+ return this.init(commit, data.token);
2766
2548
  } else {
2767
2549
  return {
2768
2550
  status: 200
@@ -3083,11 +2865,33 @@ class ProxyValServer extends ValServer {
3083
2865
  };
3084
2866
  }
3085
2867
  const host = `${reqHeaders["x-forwarded-proto"]}://${reqHeaders["host"]}`;
3086
- const fileUrl = filePath.slice("/public".length);
3087
- return {
3088
- status: 302,
3089
- redirectTo: new URL(fileUrl, host).toString()
3090
- };
2868
+ const staticPublicUrl = new URL(filePath.slice("/public".length), host).toString();
2869
+ const fetchRes = await fetch(staticPublicUrl, {
2870
+ headers: getAuthHeaders(data.token)
2871
+ });
2872
+ if (fetchRes.status === 200) {
2873
+ // TODO: does this stream data?
2874
+ if (fetchRes.body) {
2875
+ return {
2876
+ status: fetchRes.status,
2877
+ headers: {
2878
+ "Content-Type": fetchRes.headers.get("Content-Type") || "",
2879
+ "Content-Length": fetchRes.headers.get("Content-Length") || "0",
2880
+ "Cache-Control": "public, max-age=31536000, immutable"
2881
+ },
2882
+ body: fetchRes.body
2883
+ };
2884
+ } else {
2885
+ return {
2886
+ status: 500,
2887
+ json: {
2888
+ message: "No body in response"
2889
+ }
2890
+ };
2891
+ }
2892
+ } else {
2893
+ throw new Error("Failed to fetch file: " + filePath);
2894
+ }
3091
2895
  }
3092
2896
  });
3093
2897
  }
@@ -3279,6 +3083,25 @@ function getAuthHeaders(token, type) {
3279
3083
  Authorization: `Bearer ${token}`
3280
3084
  };
3281
3085
  }
3086
+ function createParams(params) {
3087
+ let paramIdx = 0;
3088
+ let paramsString = "";
3089
+ for (const key in params) {
3090
+ const param = params[key];
3091
+ if (Array.isArray(param)) {
3092
+ for (const value of param) {
3093
+ paramsString += `${key}=${encodeURIComponent(value)}&`;
3094
+ }
3095
+ } else if (param) {
3096
+ paramsString += `${key}=${encodeURIComponent(param)}`;
3097
+ }
3098
+ if (paramIdx < Object.keys(params).length - 1) {
3099
+ paramsString += "&";
3100
+ }
3101
+ paramIdx++;
3102
+ }
3103
+ return paramsString;
3104
+ }
3282
3105
 
3283
3106
  async function createValServer(route, opts, callbacks) {
3284
3107
  const serverOpts = await initHandlerOptions(route, opts);
@@ -3294,6 +3117,7 @@ async function initHandlerOptions(route, opts) {
3294
3117
  const maybeValSecret = opts.valSecret || process.env.VAL_SECRET;
3295
3118
  const isProxyMode = opts.mode === "proxy" || opts.mode === undefined && (maybeApiKey || maybeValSecret);
3296
3119
  if (isProxyMode) {
3120
+ var _opts$versions, _opts$versions2;
3297
3121
  const valBuildUrl = opts.valBuildUrl || process.env.VAL_BUILD_URL || "https://app.val.build";
3298
3122
  if (!maybeApiKey || !maybeValSecret) {
3299
3123
  throw new Error("VAL_API_KEY and VAL_SECRET env vars must both be set in proxy mode");
@@ -3311,6 +3135,14 @@ async function initHandlerOptions(route, opts) {
3311
3135
  if (!maybeValRemote) {
3312
3136
  throw new Error("Proxy mode does not work unless the 'remote' option in val.config is defined or the VAL_REMOTE env var is set.");
3313
3137
  }
3138
+ const coreVersion = (_opts$versions = opts.versions) === null || _opts$versions === void 0 ? void 0 : _opts$versions.core;
3139
+ if (!coreVersion) {
3140
+ throw new Error("Could not determine version of @valbuild/core");
3141
+ }
3142
+ const nextVersion = (_opts$versions2 = opts.versions) === null || _opts$versions2 === void 0 ? void 0 : _opts$versions2.next;
3143
+ if (!nextVersion) {
3144
+ throw new Error("Could not determine version of @valbuild/next");
3145
+ }
3314
3146
  return {
3315
3147
  mode: "proxy",
3316
3148
  route,
@@ -3318,6 +3150,10 @@ async function initHandlerOptions(route, opts) {
3318
3150
  valSecret: maybeValSecret,
3319
3151
  valBuildUrl,
3320
3152
  valContentUrl,
3153
+ versions: {
3154
+ core: coreVersion,
3155
+ next: nextVersion
3156
+ },
3321
3157
  git: {
3322
3158
  commit: maybeGitCommit,
3323
3159
  branch: maybeGitBranch