@valbuild/server 0.60.21 → 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.
- package/dist/declarations/src/LocalValServer.d.ts +3 -1
- package/dist/declarations/src/Service.d.ts +3 -9
- package/dist/declarations/src/ValServer.d.ts +3 -5
- package/dist/declarations/src/createValApiRouter.d.ts +7 -1
- package/dist/valbuild-server.cjs.dev.js +121 -285
- package/dist/valbuild-server.cjs.prod.js +121 -285
- package/dist/valbuild-server.esm.js +121 -284
- package/package.json +4 -4
@@ -12,7 +12,6 @@ import z, { z as z$1 } from 'zod';
|
|
12
12
|
import { MIME_TYPES_TO_EXT, filenameToMimeType, VAL_ENABLE_COOKIE_NAME, VAL_STATE_COOKIE as VAL_STATE_COOKIE$1, VAL_SESSION_COOKIE as VAL_SESSION_COOKIE$1 } from '@valbuild/shared/internal';
|
13
13
|
import sizeOf from 'image-size';
|
14
14
|
import crypto from 'crypto';
|
15
|
-
import minimatch from 'minimatch';
|
16
15
|
import { createUIRequestHandler } from '@valbuild/ui/server';
|
17
16
|
|
18
17
|
class ValSyntaxError {
|
@@ -662,7 +661,7 @@ const patchSourceFile = (sourceFile, patch) => {
|
|
662
661
|
return applyPatch(sourceFile, ops$1, patch);
|
663
662
|
};
|
664
663
|
|
665
|
-
const readValFile = async (id,
|
664
|
+
const readValFile = async (id, rootDirPath, runtime, options) => {
|
666
665
|
const context = runtime.newContext();
|
667
666
|
|
668
667
|
// avoid failures when console.log is called
|
@@ -714,7 +713,7 @@ globalThis.valModule = {
|
|
714
713
|
`;
|
715
714
|
const result = context.evalCode(code,
|
716
715
|
// Synthetic module name
|
717
|
-
path__default.join(
|
716
|
+
path__default.join(rootDirPath, "<val>"));
|
718
717
|
const fatalErrors = [];
|
719
718
|
if (result.error) {
|
720
719
|
const error = result.error.consume(context.dump);
|
@@ -1159,18 +1158,16 @@ async function createService(projectRoot, opts, host = {
|
|
1159
1158
|
const sourceFileHandler = new ValSourceFileHandler(projectRoot, compilerOptions, host);
|
1160
1159
|
const module = await newQuickJSWASMModule();
|
1161
1160
|
const runtime = await newValQuickJSRuntime(module, loader || new ValModuleLoader(projectRoot, compilerOptions, sourceFileHandler, host, opts.disableCache === undefined ? process.env.NODE_ENV === "development" ? false : true : opts.disableCache));
|
1162
|
-
return new Service(
|
1161
|
+
return new Service(projectRoot, sourceFileHandler, runtime);
|
1163
1162
|
}
|
1164
1163
|
class Service {
|
1165
|
-
constructor({
|
1166
|
-
valConfigPath
|
1167
|
-
}, sourceFileHandler, runtime) {
|
1164
|
+
constructor(projectRoot, sourceFileHandler, runtime) {
|
1168
1165
|
this.sourceFileHandler = sourceFileHandler;
|
1169
1166
|
this.runtime = runtime;
|
1170
|
-
this.
|
1167
|
+
this.projectRoot = projectRoot;
|
1171
1168
|
}
|
1172
1169
|
async get(moduleId, modulePath, options) {
|
1173
|
-
const valModule = await readValFile(moduleId, this.
|
1170
|
+
const valModule = await readValFile(moduleId, this.projectRoot, this.runtime, options ?? {
|
1174
1171
|
validate: true,
|
1175
1172
|
source: true,
|
1176
1173
|
schema: true
|
@@ -1194,7 +1191,7 @@ class Service {
|
|
1194
1191
|
}
|
1195
1192
|
}
|
1196
1193
|
async patch(moduleId, patch) {
|
1197
|
-
await patchValFile(moduleId, this.
|
1194
|
+
await patchValFile(moduleId, this.projectRoot, patch, this.sourceFileHandler, this.runtime);
|
1198
1195
|
}
|
1199
1196
|
dispose() {
|
1200
1197
|
this.runtime.dispose();
|
@@ -1385,9 +1382,8 @@ function getValidationErrorMetadata(validationError) {
|
|
1385
1382
|
|
1386
1383
|
const ops = new JSONOps();
|
1387
1384
|
class ValServer {
|
1388
|
-
constructor(cwd,
|
1385
|
+
constructor(cwd, options, callbacks) {
|
1389
1386
|
this.cwd = cwd;
|
1390
|
-
this.host = host;
|
1391
1387
|
this.options = options;
|
1392
1388
|
this.callbacks = callbacks;
|
1393
1389
|
}
|
@@ -1423,19 +1419,10 @@ class ValServer {
|
|
1423
1419
|
redirectTo: redirectToRes
|
1424
1420
|
};
|
1425
1421
|
}
|
1426
|
-
getAllModules(treePath) {
|
1427
|
-
const moduleIds = this.host.readDirectory(this.cwd, ["ts", "js"], ["node_modules", ".*"], ["**/*.val.ts", "**/*.val.js"]).filter(file => {
|
1428
|
-
if (treePath) {
|
1429
|
-
return file.replace(this.cwd, "").startsWith(treePath);
|
1430
|
-
}
|
1431
|
-
return true;
|
1432
|
-
}).map(file => file.replace(this.cwd, "").replace(".val.js", "").replace(".val.ts", "").split(path__default.sep).join("/"));
|
1433
|
-
return moduleIds;
|
1434
|
-
}
|
1435
1422
|
async getTree(treePath,
|
1436
1423
|
// TODO: use the params: patch, schema, source now we return everything, every time
|
1437
1424
|
query, cookies, requestHeaders) {
|
1438
|
-
const ensureRes = await this.
|
1425
|
+
const ensureRes = await this.ensureInitialized("getTree", cookies);
|
1439
1426
|
if (result.isErr(ensureRes)) {
|
1440
1427
|
return ensureRes.error;
|
1441
1428
|
}
|
@@ -1443,7 +1430,7 @@ class ValServer {
|
|
1443
1430
|
const execValidations = query.validate === "true";
|
1444
1431
|
const includeSource = query.source === "true";
|
1445
1432
|
const includeSchema = query.schema === "true";
|
1446
|
-
const moduleIds = this.getAllModules(treePath);
|
1433
|
+
const moduleIds = await this.getAllModules(treePath);
|
1447
1434
|
let {
|
1448
1435
|
patchIdsByModuleId,
|
1449
1436
|
patchesById,
|
@@ -1483,14 +1470,14 @@ class ValServer {
|
|
1483
1470
|
};
|
1484
1471
|
}
|
1485
1472
|
async postValidate(rawBody, cookies, requestHeaders) {
|
1486
|
-
const ensureRes = await this.
|
1473
|
+
const ensureRes = await this.ensureInitialized("postValidate", cookies);
|
1487
1474
|
if (result.isErr(ensureRes)) {
|
1488
1475
|
return ensureRes.error;
|
1489
1476
|
}
|
1490
1477
|
return this.validateThenMaybeCommit(rawBody, false, cookies, requestHeaders);
|
1491
1478
|
}
|
1492
1479
|
async postCommit(rawBody, cookies, requestHeaders) {
|
1493
|
-
const ensureRes = await this.
|
1480
|
+
const ensureRes = await this.ensureInitialized("postCommit", cookies);
|
1494
1481
|
if (result.isErr(ensureRes)) {
|
1495
1482
|
return ensureRes.error;
|
1496
1483
|
}
|
@@ -1516,7 +1503,6 @@ class ValServer {
|
|
1516
1503
|
}
|
1517
1504
|
|
1518
1505
|
/* */
|
1519
|
-
|
1520
1506
|
async applyAllPatchesThenValidate(moduleId, patchIdsByModuleId, patchesById, fileUpdates, cookies, requestHeaders, applyPatches, validate, includeSource, includeSchema) {
|
1521
1507
|
const serializedModuleContent = await this.getModule(moduleId, {
|
1522
1508
|
validate: validate,
|
@@ -1781,7 +1767,7 @@ class ValServer {
|
|
1781
1767
|
fileUpdates
|
1782
1768
|
} = res.value;
|
1783
1769
|
const validationErrorsByModuleId = {};
|
1784
|
-
for (const moduleIdStr of this.getAllModules("/")) {
|
1770
|
+
for (const moduleIdStr of await this.getAllModules("/")) {
|
1785
1771
|
const moduleId = moduleIdStr;
|
1786
1772
|
const serializedModuleContent = await this.applyAllPatchesThenValidate(moduleId, filterPatchesByModuleIdRes.data.patches ||
|
1787
1773
|
// TODO: refine to ModuleId and PatchId when parsing
|
@@ -2024,10 +2010,11 @@ class LocalValServer extends ValServer {
|
|
2024
2010
|
static PATCHES_DIR = "patches";
|
2025
2011
|
static FILES_DIR = "files";
|
2026
2012
|
constructor(options, callbacks) {
|
2027
|
-
super(options.service.sourceFileHandler.projectRoot, options
|
2013
|
+
super(options.service.sourceFileHandler.projectRoot, options, callbacks);
|
2028
2014
|
this.options = options;
|
2029
2015
|
this.callbacks = callbacks;
|
2030
2016
|
this.patchesRootPath = options.cacheDir || path__default.join(options.service.sourceFileHandler.projectRoot, ".val");
|
2017
|
+
this.host = this.options.service.sourceFileHandler.host;
|
2031
2018
|
}
|
2032
2019
|
async session() {
|
2033
2020
|
return {
|
@@ -2285,13 +2272,22 @@ class LocalValServer extends ValServer {
|
|
2285
2272
|
}
|
2286
2273
|
};
|
2287
2274
|
}
|
2288
|
-
async
|
2275
|
+
async ensureInitialized() {
|
2289
2276
|
// No RemoteFS so nothing to ensure
|
2290
2277
|
return result.ok(undefined);
|
2291
2278
|
}
|
2292
2279
|
getModule(moduleId, options) {
|
2293
2280
|
return this.options.service.get(moduleId, "", options);
|
2294
2281
|
}
|
2282
|
+
async getAllModules(treePath) {
|
2283
|
+
const moduleIds = this.host.readDirectory(this.cwd, ["ts", "js"], ["node_modules", ".*"], ["**/*.val.ts", "**/*.val.js"]).filter(file => {
|
2284
|
+
if (treePath) {
|
2285
|
+
return file.replace(this.cwd, "").startsWith(treePath);
|
2286
|
+
}
|
2287
|
+
return true;
|
2288
|
+
}).map(file => file.replace(this.cwd, "").replace(".val.js", "").replace(".val.ts", "").split(path__default.sep).join("/"));
|
2289
|
+
return moduleIds;
|
2290
|
+
}
|
2295
2291
|
async execCommit(patches) {
|
2296
2292
|
for (const [patchId, moduleId, patch] of patches) {
|
2297
2293
|
// TODO: patch the entire module content directly by using a { path: "", op: "replace", value: patchedData }?
|
@@ -2378,219 +2374,32 @@ function encodeJwt(payload, sessionKey) {
|
|
2378
2374
|
return `${jwtHeaderBase64}.${payloadBase64}.${crypto.createHmac("sha256", sessionKey).update(`${jwtHeaderBase64}.${payloadBase64}`).digest("base64")}`;
|
2379
2375
|
}
|
2380
2376
|
|
2381
|
-
const SEPARATOR = "/";
|
2382
|
-
class RemoteFS {
|
2383
|
-
initialized = false;
|
2384
|
-
constructor() {
|
2385
|
-
this.data = {};
|
2386
|
-
this.modifiedFiles = [];
|
2387
|
-
this.deletedFiles = [];
|
2388
|
-
}
|
2389
|
-
useCaseSensitiveFileNames = true;
|
2390
|
-
isInitialized() {
|
2391
|
-
return this.initialized;
|
2392
|
-
}
|
2393
|
-
async initializeWith(data) {
|
2394
|
-
this.data = data;
|
2395
|
-
this.initialized = true;
|
2396
|
-
}
|
2397
|
-
async getPendingOperations() {
|
2398
|
-
const modified = {};
|
2399
|
-
for (const modifiedFile of this.modifiedFiles) {
|
2400
|
-
const {
|
2401
|
-
directory,
|
2402
|
-
filename
|
2403
|
-
} = RemoteFS.parsePath(modifiedFile);
|
2404
|
-
modified[modifiedFile] = this.data[directory].utf8Files[filename];
|
2405
|
-
}
|
2406
|
-
return {
|
2407
|
-
modified: modified,
|
2408
|
-
deleted: this.deletedFiles
|
2409
|
-
};
|
2410
|
-
}
|
2411
|
-
changedDirectories = {};
|
2412
|
-
readDirectory = (rootDir, extensions, excludes, includes, depth) => {
|
2413
|
-
// TODO: rewrite this! And make some tests! This is a mess!
|
2414
|
-
// Considered using glob which typescript seems to use, but that works on an entire typeof fs
|
2415
|
-
// glob uses minimatch internally, so using that instead
|
2416
|
-
const files = [];
|
2417
|
-
for (const dir in this.data) {
|
2418
|
-
const depthExceeded = depth ? dir.replace(rootDir, "").split(SEPARATOR).length > depth : false;
|
2419
|
-
if (dir.startsWith(rootDir) && !depthExceeded) {
|
2420
|
-
for (const file in this.data[dir].utf8Files) {
|
2421
|
-
for (const extension of extensions) {
|
2422
|
-
if (file.endsWith(extension)) {
|
2423
|
-
const path = `${dir}/${file}`;
|
2424
|
-
for (const include of includes ?? []) {
|
2425
|
-
// TODO: should default includes be ['**/*']?
|
2426
|
-
if (minimatch(path, include)) {
|
2427
|
-
let isExcluded = false;
|
2428
|
-
for (const exlude of excludes ?? []) {
|
2429
|
-
if (minimatch(path, exlude)) {
|
2430
|
-
isExcluded = true;
|
2431
|
-
break;
|
2432
|
-
}
|
2433
|
-
}
|
2434
|
-
if (!isExcluded) {
|
2435
|
-
files.push(path);
|
2436
|
-
}
|
2437
|
-
}
|
2438
|
-
}
|
2439
|
-
}
|
2440
|
-
}
|
2441
|
-
}
|
2442
|
-
}
|
2443
|
-
}
|
2444
|
-
return ts.sys.readDirectory(rootDir, extensions, excludes, includes, depth).concat(files);
|
2445
|
-
};
|
2446
|
-
writeFile = (filePath, data, encoding) => {
|
2447
|
-
// never write real fs
|
2448
|
-
const {
|
2449
|
-
directory,
|
2450
|
-
filename
|
2451
|
-
} = RemoteFS.parsePath(filePath);
|
2452
|
-
if (this.data[directory] === undefined) {
|
2453
|
-
throw new Error(`Directory not found: ${directory}`);
|
2454
|
-
}
|
2455
|
-
this.changedDirectories[directory] = this.changedDirectories[directory] ?? new Set();
|
2456
|
-
|
2457
|
-
// if it fails below this should not be added, so maybe a try/catch?
|
2458
|
-
this.changedDirectories[directory].add(filename);
|
2459
|
-
this.data[directory].utf8Files[filename] = data;
|
2460
|
-
this.modifiedFiles.push(filePath);
|
2461
|
-
};
|
2462
|
-
rmFile(filePath) {
|
2463
|
-
// never remove from real fs
|
2464
|
-
const {
|
2465
|
-
directory,
|
2466
|
-
filename
|
2467
|
-
} = RemoteFS.parsePath(filePath);
|
2468
|
-
if (this.data[directory] === undefined) {
|
2469
|
-
throw new Error(`Directory not found: ${directory}`);
|
2470
|
-
}
|
2471
|
-
this.changedDirectories[directory] = this.changedDirectories[directory] ?? new Set();
|
2472
|
-
|
2473
|
-
// if it fails below this should not be added, so maybe a try/catch?
|
2474
|
-
this.changedDirectories[directory].add(filename);
|
2475
|
-
delete this.data[directory].utf8Files[filename];
|
2476
|
-
delete this.data[directory].symlinks[filename];
|
2477
|
-
this.deletedFiles.push(filePath);
|
2478
|
-
}
|
2479
|
-
fileExists = filePath => {
|
2480
|
-
var _this$data$directory;
|
2481
|
-
if (ts.sys.fileExists(filePath)) {
|
2482
|
-
return true;
|
2483
|
-
}
|
2484
|
-
const {
|
2485
|
-
directory,
|
2486
|
-
filename
|
2487
|
-
} = 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
|
2488
|
-
);
|
2489
|
-
return !!((_this$data$directory = this.data[directory]) !== null && _this$data$directory !== void 0 && _this$data$directory.utf8Files[filename]);
|
2490
|
-
};
|
2491
|
-
readFile = filePath => {
|
2492
|
-
const realFile = ts.sys.readFile(filePath);
|
2493
|
-
if (realFile !== undefined) {
|
2494
|
-
return realFile;
|
2495
|
-
}
|
2496
|
-
const {
|
2497
|
-
directory,
|
2498
|
-
filename
|
2499
|
-
} = RemoteFS.parsePath(filePath);
|
2500
|
-
const dirNode = this.data[directory];
|
2501
|
-
if (!dirNode) {
|
2502
|
-
return undefined;
|
2503
|
-
}
|
2504
|
-
const content = dirNode.utf8Files[filename];
|
2505
|
-
return content;
|
2506
|
-
};
|
2507
|
-
realpath(fullPath) {
|
2508
|
-
if (ts.sys.fileExists(fullPath) && ts.sys.realpath) {
|
2509
|
-
return ts.sys.realpath(fullPath);
|
2510
|
-
}
|
2511
|
-
// TODO: this only works in a very limited way.
|
2512
|
-
// It does not support symlinks to symlinks nor symlinked directories for instance.
|
2513
|
-
const {
|
2514
|
-
directory,
|
2515
|
-
filename
|
2516
|
-
} = RemoteFS.parsePath(fullPath);
|
2517
|
-
if (this.data[directory] === undefined) {
|
2518
|
-
return fullPath;
|
2519
|
-
}
|
2520
|
-
if (this.data[directory].utf8Files[filename] === undefined) {
|
2521
|
-
const link = this.data[directory].symlinks[filename];
|
2522
|
-
if (link === undefined) {
|
2523
|
-
return fullPath;
|
2524
|
-
} else {
|
2525
|
-
return link;
|
2526
|
-
}
|
2527
|
-
} else {
|
2528
|
-
return path__default.join(directory, filename);
|
2529
|
-
}
|
2530
|
-
}
|
2531
|
-
|
2532
|
-
/**
|
2533
|
-
*
|
2534
|
-
* @param path
|
2535
|
-
* @returns directory and filename. NOTE: directory might be empty string
|
2536
|
-
*/
|
2537
|
-
static parsePath(path) {
|
2538
|
-
const pathParts = path.split(SEPARATOR);
|
2539
|
-
const filename = pathParts.pop();
|
2540
|
-
if (!filename) {
|
2541
|
-
throw new Error(`Invalid path: '${path}'. Node filename: '${filename}'`);
|
2542
|
-
}
|
2543
|
-
const directory = pathParts.join(SEPARATOR);
|
2544
|
-
return {
|
2545
|
-
directory,
|
2546
|
-
filename
|
2547
|
-
};
|
2548
|
-
}
|
2549
|
-
}
|
2550
|
-
|
2551
|
-
/**
|
2552
|
-
* Represents directories
|
2553
|
-
* NOTE: the keys of directory nodes are the "full" path, i.e. "foo/bar"
|
2554
|
-
* NOTE: the keys of file nodes are the "filename" only, i.e. "baz.txt"
|
2555
|
-
*
|
2556
|
-
* @example
|
2557
|
-
* {
|
2558
|
-
* "foo/bar": { // <- directory. NOTE: this is the "full" path
|
2559
|
-
* gitHubSha: "123",
|
2560
|
-
* files: {
|
2561
|
-
* "baz.txt": "hello world" // <- file. NOTE: this is the "filename" only
|
2562
|
-
* },
|
2563
|
-
* },
|
2564
|
-
* };
|
2565
|
-
*/
|
2566
|
-
// TODO: a Map would be better here
|
2567
|
-
|
2568
2377
|
class ProxyValServer extends ValServer {
|
2569
|
-
moduleCache =
|
2378
|
+
moduleCache = null;
|
2570
2379
|
constructor(cwd, options, apiOptions, callbacks) {
|
2571
|
-
|
2572
|
-
super(cwd, remoteFS, options, callbacks);
|
2380
|
+
super(cwd, options, callbacks);
|
2573
2381
|
this.cwd = cwd;
|
2574
2382
|
this.options = options;
|
2575
2383
|
this.apiOptions = apiOptions;
|
2576
2384
|
this.callbacks = callbacks;
|
2577
|
-
this.
|
2385
|
+
this.moduleCache = null;
|
2578
2386
|
}
|
2579
2387
|
|
2580
2388
|
/** Remote FS dependent methods: */
|
2581
2389
|
|
2582
|
-
async getModule(moduleId,
|
2583
|
-
|
2584
|
-
|
2585
|
-
if (
|
2586
|
-
return
|
2390
|
+
async getModule(moduleId,
|
2391
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
2392
|
+
_options) {
|
2393
|
+
if (this.moduleCache) {
|
2394
|
+
return this.moduleCache[moduleId];
|
2587
2395
|
}
|
2588
|
-
|
2589
|
-
|
2396
|
+
throw new Error("Module cache not initialized");
|
2397
|
+
}
|
2398
|
+
async getAllModules(treePath) {
|
2399
|
+
if (!this.moduleCache) {
|
2400
|
+
throw new Error("Module cache not initialized");
|
2590
2401
|
}
|
2591
|
-
|
2592
|
-
this.moduleCache[cacheKey] = currentModule;
|
2593
|
-
return currentModule;
|
2402
|
+
return Object.keys(this.moduleCache).filter(moduleId => moduleId.startsWith(treePath));
|
2594
2403
|
}
|
2595
2404
|
execCommit(patches, cookies) {
|
2596
2405
|
return withAuth(this.options.valSecret, cookies, "execCommit", async ({
|
@@ -2605,46 +2414,19 @@ class ProxyValServer extends ValServer {
|
|
2605
2414
|
}
|
2606
2415
|
};
|
2607
2416
|
}
|
2608
|
-
const params =
|
2417
|
+
const params = createParams({
|
2418
|
+
root: this.apiOptions.root,
|
2419
|
+
commit,
|
2420
|
+
ext: ["ts", "js", "json"],
|
2421
|
+
package: ["@valbuild/core@" + this.options.versions.core, "@valbuild/next@" + this.options.versions.next],
|
2422
|
+
include: ["**/*.val.{js,ts},package.json,tsconfig.json,jsconfig.json"]
|
2423
|
+
});
|
2609
2424
|
const url = new URL(`/v1/commit/${this.options.remote}/heads/${this.options.git.branch}/~?${params}`, this.options.valContentUrl);
|
2610
|
-
|
2611
|
-
// Creates a fresh copy of the fs. We cannot touch the existing fs, since there might be parallel operations?
|
2612
|
-
// 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.
|
2613
|
-
// It is a concern we have, since we might be using quite a lot of memory when having the whole FS in memory.
|
2614
|
-
// 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.
|
2615
|
-
// 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.
|
2616
|
-
const remoteFS = new RemoteFS();
|
2617
|
-
const initRes = await this.initRemoteFS(commit, remoteFS, token);
|
2618
|
-
if (initRes.status !== 200) {
|
2619
|
-
return initRes;
|
2620
|
-
}
|
2621
|
-
const service = await createService(this.cwd, this.apiOptions, remoteFS);
|
2622
|
-
// TODO: optimize patches, e.g. only take the last replace for a given thing, etc...
|
2623
|
-
const patchIds = [];
|
2624
|
-
const binaryFileUpdates = {};
|
2625
|
-
for (const [patchId, moduleId, patch] of patches) {
|
2626
|
-
const patchableOps = [];
|
2627
|
-
for (const op of patch) {
|
2628
|
-
if (isCachedPatchFileOp(op)) {
|
2629
|
-
binaryFileUpdates[op.filePath] = op.value;
|
2630
|
-
} else {
|
2631
|
-
if (Internal.isFileOp(op)) {
|
2632
|
-
throw new Error(`Val: Unexpected file operation (file: ${op.filePath}). This is likely a Val bug.`);
|
2633
|
-
}
|
2634
|
-
patchableOps.push(op);
|
2635
|
-
}
|
2636
|
-
}
|
2637
|
-
await service.patch(moduleId, patchableOps);
|
2638
|
-
patchIds.push(patchId);
|
2639
|
-
}
|
2640
|
-
const sourceFileUpdates = await remoteFS.getPendingOperations();
|
2425
|
+
const patchIds = patches.map(([patchId]) => patchId);
|
2641
2426
|
const fetchRes = await fetch(url, {
|
2642
2427
|
method: "POST",
|
2643
2428
|
headers: getAuthHeaders(token, "application/json"),
|
2644
2429
|
body: JSON.stringify({
|
2645
|
-
sourceFileUpdates: sourceFileUpdates.modified,
|
2646
|
-
binaryFileUpdates,
|
2647
|
-
deletedFiles: sourceFileUpdates.deleted,
|
2648
2430
|
patchIds
|
2649
2431
|
})
|
2650
2432
|
});
|
@@ -2658,14 +2440,15 @@ class ProxyValServer extends ValServer {
|
|
2658
2440
|
}
|
2659
2441
|
});
|
2660
2442
|
}
|
2661
|
-
async
|
2662
|
-
const params =
|
2443
|
+
async init(commit, token) {
|
2444
|
+
const params = createParams({
|
2663
2445
|
root: this.apiOptions.root,
|
2664
|
-
commit
|
2665
|
-
|
2666
|
-
|
2446
|
+
commit,
|
2447
|
+
ext: ["ts", "js", "json"],
|
2448
|
+
package: ["@valbuild/core@" + this.options.versions.core, "@valbuild/next@" + this.options.versions.next],
|
2449
|
+
include: ["**/*.val.{js,ts},package.json,tsconfig.json,jsconfig.json"]
|
2667
2450
|
});
|
2668
|
-
const url = new URL(`/v1/
|
2451
|
+
const url = new URL(`/v1/eval/${this.options.remote}/heads/${this.options.git.branch}/~?${params}`, this.options.valContentUrl);
|
2669
2452
|
try {
|
2670
2453
|
const fetchRes = await fetch(url, {
|
2671
2454
|
headers: getAuthHeaders(token, "application/json")
|
@@ -2688,22 +2471,22 @@ class ProxyValServer extends ValServer {
|
|
2688
2471
|
details: "Invalid response: missing git.commit"
|
2689
2472
|
};
|
2690
2473
|
}
|
2691
|
-
if (typeof json.
|
2474
|
+
if (typeof json.modules !== "object" || json.modules === null) {
|
2692
2475
|
error = {
|
2693
|
-
details: "Invalid response: missing
|
2476
|
+
details: "Invalid response: missing modules"
|
2694
2477
|
};
|
2695
2478
|
}
|
2696
2479
|
if (error) {
|
2480
|
+
console.error("Could not initialize remote modules", error);
|
2697
2481
|
return {
|
2698
2482
|
status: 500,
|
2699
2483
|
json: {
|
2700
|
-
message: "Failed to fetch remote
|
2484
|
+
message: "Failed to fetch remote modules",
|
2701
2485
|
...error
|
2702
2486
|
}
|
2703
2487
|
};
|
2704
2488
|
}
|
2705
|
-
|
2706
|
-
), content])));
|
2489
|
+
this.moduleCache = json.modules;
|
2707
2490
|
return {
|
2708
2491
|
status: 200
|
2709
2492
|
};
|
@@ -2719,7 +2502,7 @@ class ProxyValServer extends ValServer {
|
|
2719
2502
|
};
|
2720
2503
|
}
|
2721
2504
|
}
|
2722
|
-
async
|
2505
|
+
async ensureInitialized(errorMessageType, cookies) {
|
2723
2506
|
const commit = this.options.git.commit;
|
2724
2507
|
if (!commit) {
|
2725
2508
|
return result.err({
|
@@ -2730,8 +2513,8 @@ class ProxyValServer extends ValServer {
|
|
2730
2513
|
});
|
2731
2514
|
}
|
2732
2515
|
const res = await withAuth(this.options.valSecret, cookies, errorMessageType, async data => {
|
2733
|
-
if (!this.
|
2734
|
-
return this.
|
2516
|
+
if (!this.moduleCache) {
|
2517
|
+
return this.init(commit, data.token);
|
2735
2518
|
} else {
|
2736
2519
|
return {
|
2737
2520
|
status: 200
|
@@ -3052,11 +2835,33 @@ class ProxyValServer extends ValServer {
|
|
3052
2835
|
};
|
3053
2836
|
}
|
3054
2837
|
const host = `${reqHeaders["x-forwarded-proto"]}://${reqHeaders["host"]}`;
|
3055
|
-
const
|
3056
|
-
|
3057
|
-
|
3058
|
-
|
3059
|
-
|
2838
|
+
const staticPublicUrl = new URL(filePath.slice("/public".length), host).toString();
|
2839
|
+
const fetchRes = await fetch(staticPublicUrl, {
|
2840
|
+
headers: getAuthHeaders(data.token)
|
2841
|
+
});
|
2842
|
+
if (fetchRes.status === 200) {
|
2843
|
+
// TODO: does this stream data?
|
2844
|
+
if (fetchRes.body) {
|
2845
|
+
return {
|
2846
|
+
status: fetchRes.status,
|
2847
|
+
headers: {
|
2848
|
+
"Content-Type": fetchRes.headers.get("Content-Type") || "",
|
2849
|
+
"Content-Length": fetchRes.headers.get("Content-Length") || "0",
|
2850
|
+
"Cache-Control": "public, max-age=31536000, immutable"
|
2851
|
+
},
|
2852
|
+
body: fetchRes.body
|
2853
|
+
};
|
2854
|
+
} else {
|
2855
|
+
return {
|
2856
|
+
status: 500,
|
2857
|
+
json: {
|
2858
|
+
message: "No body in response"
|
2859
|
+
}
|
2860
|
+
};
|
2861
|
+
}
|
2862
|
+
} else {
|
2863
|
+
throw new Error("Failed to fetch file: " + filePath);
|
2864
|
+
}
|
3060
2865
|
}
|
3061
2866
|
});
|
3062
2867
|
}
|
@@ -3248,6 +3053,25 @@ function getAuthHeaders(token, type) {
|
|
3248
3053
|
Authorization: `Bearer ${token}`
|
3249
3054
|
};
|
3250
3055
|
}
|
3056
|
+
function createParams(params) {
|
3057
|
+
let paramIdx = 0;
|
3058
|
+
let paramsString = "";
|
3059
|
+
for (const key in params) {
|
3060
|
+
const param = params[key];
|
3061
|
+
if (Array.isArray(param)) {
|
3062
|
+
for (const value of param) {
|
3063
|
+
paramsString += `${key}=${encodeURIComponent(value)}&`;
|
3064
|
+
}
|
3065
|
+
} else if (param) {
|
3066
|
+
paramsString += `${key}=${encodeURIComponent(param)}`;
|
3067
|
+
}
|
3068
|
+
if (paramIdx < Object.keys(params).length - 1) {
|
3069
|
+
paramsString += "&";
|
3070
|
+
}
|
3071
|
+
paramIdx++;
|
3072
|
+
}
|
3073
|
+
return paramsString;
|
3074
|
+
}
|
3251
3075
|
|
3252
3076
|
async function createValServer(route, opts, callbacks) {
|
3253
3077
|
const serverOpts = await initHandlerOptions(route, opts);
|
@@ -3263,6 +3087,7 @@ async function initHandlerOptions(route, opts) {
|
|
3263
3087
|
const maybeValSecret = opts.valSecret || process.env.VAL_SECRET;
|
3264
3088
|
const isProxyMode = opts.mode === "proxy" || opts.mode === undefined && (maybeApiKey || maybeValSecret);
|
3265
3089
|
if (isProxyMode) {
|
3090
|
+
var _opts$versions, _opts$versions2;
|
3266
3091
|
const valBuildUrl = opts.valBuildUrl || process.env.VAL_BUILD_URL || "https://app.val.build";
|
3267
3092
|
if (!maybeApiKey || !maybeValSecret) {
|
3268
3093
|
throw new Error("VAL_API_KEY and VAL_SECRET env vars must both be set in proxy mode");
|
@@ -3280,6 +3105,14 @@ async function initHandlerOptions(route, opts) {
|
|
3280
3105
|
if (!maybeValRemote) {
|
3281
3106
|
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.");
|
3282
3107
|
}
|
3108
|
+
const coreVersion = (_opts$versions = opts.versions) === null || _opts$versions === void 0 ? void 0 : _opts$versions.core;
|
3109
|
+
if (!coreVersion) {
|
3110
|
+
throw new Error("Could not determine version of @valbuild/core");
|
3111
|
+
}
|
3112
|
+
const nextVersion = (_opts$versions2 = opts.versions) === null || _opts$versions2 === void 0 ? void 0 : _opts$versions2.next;
|
3113
|
+
if (!nextVersion) {
|
3114
|
+
throw new Error("Could not determine version of @valbuild/next");
|
3115
|
+
}
|
3283
3116
|
return {
|
3284
3117
|
mode: "proxy",
|
3285
3118
|
route,
|
@@ -3287,6 +3120,10 @@ async function initHandlerOptions(route, opts) {
|
|
3287
3120
|
valSecret: maybeValSecret,
|
3288
3121
|
valBuildUrl,
|
3289
3122
|
valContentUrl,
|
3123
|
+
versions: {
|
3124
|
+
core: coreVersion,
|
3125
|
+
next: nextVersion
|
3126
|
+
},
|
3290
3127
|
git: {
|
3291
3128
|
commit: maybeGitCommit,
|
3292
3129
|
branch: maybeGitBranch
|
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.60.
|
15
|
+
"version": "0.60.22",
|
16
16
|
"scripts": {
|
17
17
|
"typecheck": "tsc --noEmit",
|
18
18
|
"test": "jest",
|
@@ -24,9 +24,9 @@
|
|
24
24
|
"concurrently": "^7.6.0"
|
25
25
|
},
|
26
26
|
"dependencies": {
|
27
|
-
"@valbuild/core": "~0.60.
|
28
|
-
"@valbuild/shared": "~0.60.
|
29
|
-
"@valbuild/ui": "~0.60.
|
27
|
+
"@valbuild/core": "~0.60.22",
|
28
|
+
"@valbuild/shared": "~0.60.22",
|
29
|
+
"@valbuild/ui": "~0.60.22",
|
30
30
|
"express": "^4.18.2",
|
31
31
|
"image-size": "^1.0.2",
|
32
32
|
"minimatch": "^3.0.4",
|