@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
@@ -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,
|
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(
|
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(
|
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.
|
1197
|
+
this.projectRoot = projectRoot;
|
1202
1198
|
}
|
1203
1199
|
async get(moduleId, modulePath, options) {
|
1204
|
-
const valModule = await readValFile(moduleId, this.
|
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.
|
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,
|
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.
|
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.
|
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.
|
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
|
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
|
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
|
-
|
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.
|
2415
|
+
this.moduleCache = null;
|
2609
2416
|
}
|
2610
2417
|
|
2611
2418
|
/** Remote FS dependent methods: */
|
2612
2419
|
|
2613
|
-
async getModule(moduleId,
|
2614
|
-
|
2615
|
-
|
2616
|
-
if (
|
2617
|
-
return
|
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
|
-
|
2620
|
-
|
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
|
-
|
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 =
|
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
|
2693
|
-
const params =
|
2473
|
+
async init(commit, token) {
|
2474
|
+
const params = createParams({
|
2694
2475
|
root: this.apiOptions.root,
|
2695
|
-
commit
|
2696
|
-
|
2697
|
-
|
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/
|
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.
|
2504
|
+
if (typeof json.modules !== "object" || json.modules === null) {
|
2723
2505
|
error = {
|
2724
|
-
details: "Invalid response: missing
|
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
|
2514
|
+
message: "Failed to fetch remote modules",
|
2732
2515
|
...error
|
2733
2516
|
}
|
2734
2517
|
};
|
2735
2518
|
}
|
2736
|
-
|
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
|
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.
|
2765
|
-
return this.
|
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
|
3087
|
-
|
3088
|
-
|
3089
|
-
|
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
|