@valbuild/server 0.12.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.
Files changed (51) hide show
  1. package/.babelrc.json +5 -0
  2. package/CHANGELOG.md +0 -0
  3. package/dist/declarations/src/LocalValServer.d.ts +22 -0
  4. package/dist/declarations/src/ProxyValServer.d.ts +52 -0
  5. package/dist/declarations/src/SerializedModuleContent.d.ts +7 -0
  6. package/dist/declarations/src/Service.d.ts +24 -0
  7. package/dist/declarations/src/ValFS.d.ts +12 -0
  8. package/dist/declarations/src/ValFSHost.d.ts +23 -0
  9. package/dist/declarations/src/ValModuleLoader.d.ts +14 -0
  10. package/dist/declarations/src/ValQuickJSRuntime.d.ts +7 -0
  11. package/dist/declarations/src/ValServer.d.ts +14 -0
  12. package/dist/declarations/src/ValSourceFileHandler.d.ts +12 -0
  13. package/dist/declarations/src/createRequestHandler.d.ts +3 -0
  14. package/dist/declarations/src/expressHelpers.d.ts +4 -0
  15. package/dist/declarations/src/getCompilerOptions.d.ts +2 -0
  16. package/dist/declarations/src/hosting.d.ts +71 -0
  17. package/dist/declarations/src/index.d.ts +12 -0
  18. package/dist/declarations/src/jwt.d.ts +3 -0
  19. package/dist/declarations/src/patch/ts/ops.d.ts +27 -0
  20. package/dist/declarations/src/patch/ts/syntax.d.ts +49 -0
  21. package/dist/declarations/src/patch/ts/valModule.d.ts +8 -0
  22. package/dist/declarations/src/patch/validation.d.ts +4 -0
  23. package/dist/declarations/src/patchValFile.d.ts +9 -0
  24. package/dist/declarations/src/readValFile.d.ts +3 -0
  25. package/dist/valbuild-server.cjs.d.ts +2 -0
  26. package/dist/valbuild-server.cjs.d.ts.map +1 -0
  27. package/dist/valbuild-server.cjs.dev.js +1500 -0
  28. package/dist/valbuild-server.cjs.js +7 -0
  29. package/dist/valbuild-server.cjs.prod.js +1500 -0
  30. package/dist/valbuild-server.esm.js +1478 -0
  31. package/package.json +37 -0
  32. package/test/example-projects/basic-next-javascript/jsconfig.json +8 -0
  33. package/test/example-projects/basic-next-javascript/package.json +23 -0
  34. package/test/example-projects/basic-next-javascript/pages/blogs.val.js +20 -0
  35. package/test/example-projects/basic-next-javascript/val.config.js +4 -0
  36. package/test/example-projects/basic-next-src-typescript/package.json +23 -0
  37. package/test/example-projects/basic-next-src-typescript/src/pages/blogs.val.ts +20 -0
  38. package/test/example-projects/basic-next-src-typescript/src/val.config.ts +5 -0
  39. package/test/example-projects/basic-next-src-typescript/tsconfig.json +24 -0
  40. package/test/example-projects/basic-next-typescript/package.json +23 -0
  41. package/test/example-projects/basic-next-typescript/pages/blogs.val.ts +20 -0
  42. package/test/example-projects/basic-next-typescript/tsconfig.json +25 -0
  43. package/test/example-projects/basic-next-typescript/val.config.ts +5 -0
  44. package/test/example-projects/typescript-description-files/README.md +2 -0
  45. package/test/example-projects/typescript-description-files/jsconfig.json +8 -0
  46. package/test/example-projects/typescript-description-files/package.json +23 -0
  47. package/test/example-projects/typescript-description-files/pages/blogs.val.d.ts +7 -0
  48. package/test/example-projects/typescript-description-files/pages/blogs.val.js +19 -0
  49. package/test/example-projects/typescript-description-files/val.config.d.ts +3 -0
  50. package/test/example-projects/typescript-description-files/val.config.js +5 -0
  51. package/tsconfig.json +11 -0
@@ -0,0 +1,1500 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var quickjsEmscripten = require('quickjs-emscripten');
6
+ var ts = require('typescript');
7
+ var fp = require('@valbuild/core/fp');
8
+ var core = require('@valbuild/core');
9
+ var patch = require('@valbuild/core/patch');
10
+ var path = require('path');
11
+ var fs = require('fs');
12
+ var express = require('express');
13
+ var server = require('@valbuild/ui/server');
14
+ var z = require('zod');
15
+ var crypto = require('crypto');
16
+
17
+ function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
18
+
19
+ var ts__default = /*#__PURE__*/_interopDefault(ts);
20
+ var path__default = /*#__PURE__*/_interopDefault(path);
21
+ var fs__default = /*#__PURE__*/_interopDefault(fs);
22
+ var express__default = /*#__PURE__*/_interopDefault(express);
23
+ var z__default = /*#__PURE__*/_interopDefault(z);
24
+ var crypto__default = /*#__PURE__*/_interopDefault(crypto);
25
+
26
+ class ValSyntaxError {
27
+ constructor(message, node) {
28
+ this.message = message;
29
+ this.node = node;
30
+ }
31
+ }
32
+ function forEachError(tree, callback) {
33
+ if (Array.isArray(tree)) {
34
+ for (const subtree of tree) {
35
+ forEachError(subtree, callback);
36
+ }
37
+ } else {
38
+ callback(tree);
39
+ }
40
+ }
41
+ function flatMapErrors(tree, cb) {
42
+ const result = [];
43
+ forEachError(tree, error => result.push(cb(error)));
44
+ return result;
45
+ }
46
+ function formatSyntaxError(error, sourceFile) {
47
+ const pos = sourceFile.getLineAndCharacterOfPosition(error.node.pos);
48
+ return `${pos.line}:${pos.character} ${error.message}`;
49
+ }
50
+ function formatSyntaxErrorTree(tree, sourceFile) {
51
+ return flatMapErrors(tree, error => formatSyntaxError(error, sourceFile));
52
+ }
53
+ function isLiteralPropertyName(name) {
54
+ return ts__default["default"].isIdentifier(name) || ts__default["default"].isStringLiteral(name) || ts__default["default"].isNumericLiteral(name);
55
+ }
56
+ function validateObjectProperties(nodes) {
57
+ const errors = [];
58
+ for (const node of nodes) {
59
+ if (!ts__default["default"].isPropertyAssignment(node)) {
60
+ errors.push(new ValSyntaxError("Object literal element must be property assignment", node));
61
+ } else if (!isLiteralPropertyName(node.name)) {
62
+ errors.push(new ValSyntaxError("Object literal element key must be an identifier or a literal", node));
63
+ }
64
+ }
65
+ if (errors.length > 0) {
66
+ return fp.result.err(errors);
67
+ }
68
+ return fp.result.ok(nodes);
69
+ }
70
+
71
+ /**
72
+ * Validates that the expression is a JSON compatible type of expression without
73
+ * validating its children.
74
+ */
75
+ function shallowValidateExpression(value) {
76
+ return ts__default["default"].isStringLiteralLike(value) || ts__default["default"].isNumericLiteral(value) || value.kind === ts__default["default"].SyntaxKind.TrueKeyword || value.kind === ts__default["default"].SyntaxKind.FalseKeyword || value.kind === ts__default["default"].SyntaxKind.NullKeyword || ts__default["default"].isArrayLiteralExpression(value) || ts__default["default"].isObjectLiteralExpression(value) || isValFileMethodCall(value) ? undefined : new ValSyntaxError("Expression must be a literal or call val.file", value);
77
+ }
78
+
79
+ /**
80
+ * Evaluates the expression as a JSON value
81
+ */
82
+ function evaluateExpression(value) {
83
+ // The text property of a LiteralExpression stores the interpreted value of the literal in text form. For a StringLiteral,
84
+ // or any literal of a template, this means quotes have been removed and escapes have been converted to actual characters.
85
+ // For a NumericLiteral, the stored value is the toString() representation of the number. For example 1, 1.00, and 1e0 are all stored as just "1".
86
+ // https://github.com/microsoft/TypeScript/blob/4b794fe1dd0d184d3f8f17e94d8187eace57c91e/src/compiler/types.ts#L2127-L2131
87
+
88
+ if (ts__default["default"].isStringLiteralLike(value)) {
89
+ return fp.result.ok(value.text);
90
+ } else if (ts__default["default"].isNumericLiteral(value)) {
91
+ return fp.result.ok(Number(value.text));
92
+ } else if (value.kind === ts__default["default"].SyntaxKind.TrueKeyword) {
93
+ return fp.result.ok(true);
94
+ } else if (value.kind === ts__default["default"].SyntaxKind.FalseKeyword) {
95
+ return fp.result.ok(false);
96
+ } else if (value.kind === ts__default["default"].SyntaxKind.NullKeyword) {
97
+ return fp.result.ok(null);
98
+ } else if (ts__default["default"].isArrayLiteralExpression(value)) {
99
+ return fp.result.all(value.elements.map(evaluateExpression));
100
+ } else if (ts__default["default"].isObjectLiteralExpression(value)) {
101
+ return fp.pipe(validateObjectProperties(value.properties), fp.result.flatMap(assignments => fp.pipe(assignments.map(assignment => fp.pipe(evaluateExpression(assignment.initializer), fp.result.map(value => [assignment.name.text, value]))), fp.result.all)), fp.result.map(Object.fromEntries));
102
+ } else if (isValFileMethodCall(value)) {
103
+ return fp.pipe(findValFileNodeArg(value), fp.result.flatMap(ref => {
104
+ if (value.arguments.length === 2) {
105
+ return fp.pipe(evaluateExpression(value.arguments[1]), fp.result.map(metadata => ({
106
+ [core.FILE_REF_PROP]: ref.text,
107
+ _type: "file",
108
+ metadata
109
+ })));
110
+ } else {
111
+ return fp.result.ok({
112
+ [core.FILE_REF_PROP]: ref.text,
113
+ _type: "file"
114
+ });
115
+ }
116
+ }));
117
+ } else {
118
+ return fp.result.err(new ValSyntaxError("Expression must be a literal or call val.file", value));
119
+ }
120
+ }
121
+ function findObjectPropertyAssignment(value, key) {
122
+ return fp.pipe(validateObjectProperties(value.properties), fp.result.flatMap(assignments => {
123
+ const matchingAssignments = assignments.filter(assignment => assignment.name.text === key);
124
+ if (matchingAssignments.length === 0) return fp.result.ok(undefined);
125
+ if (matchingAssignments.length > 1) {
126
+ return fp.result.err(new ValSyntaxError(`Object key "${key}" is ambiguous`, value));
127
+ }
128
+ const [assignment] = matchingAssignments;
129
+ return fp.result.ok(assignment);
130
+ }));
131
+ }
132
+ function isValFileMethodCall(node) {
133
+ return ts__default["default"].isCallExpression(node) && ts__default["default"].isPropertyAccessExpression(node.expression) && ts__default["default"].isIdentifier(node.expression.expression) && node.expression.expression.text === "val" && node.expression.name.text === "file";
134
+ }
135
+ function findValFileNodeArg(node) {
136
+ if (node.arguments.length === 0) {
137
+ return fp.result.err(new ValSyntaxError(`Invalid val.file() call: missing ref argument`, node));
138
+ } else if (node.arguments.length > 2) {
139
+ return fp.result.err(new ValSyntaxError(`Invalid val.file() call: too many arguments ${node.arguments.length}}`, node));
140
+ } else if (!ts__default["default"].isStringLiteral(node.arguments[0])) {
141
+ return fp.result.err(new ValSyntaxError(`Invalid val.file() call: ref must be a string literal`, node));
142
+ }
143
+ const refNode = node.arguments[0];
144
+ return fp.result.ok(refNode);
145
+ }
146
+ function findValFileMetadataArg(node) {
147
+ if (node.arguments.length === 0) {
148
+ return fp.result.err(new ValSyntaxError(`Invalid val.file() call: missing ref argument`, node));
149
+ } else if (node.arguments.length > 2) {
150
+ return fp.result.err(new ValSyntaxError(`Invalid val.file() call: too many arguments ${node.arguments.length}}`, node));
151
+ } else if (node.arguments.length === 2) {
152
+ if (!ts__default["default"].isObjectLiteralExpression(node.arguments[1])) {
153
+ return fp.result.err(new ValSyntaxError(`Invalid val.file() call: metadata must be a object literal`, node));
154
+ }
155
+ return fp.result.ok(node.arguments[1]);
156
+ }
157
+ return fp.result.ok(undefined);
158
+ }
159
+
160
+ /**
161
+ * Given a list of expressions, validates that all the expressions are not
162
+ * spread elements. In other words, it ensures that the expressions are the
163
+ * initializers of the values at their respective indices in the evaluated list.
164
+ */
165
+ function validateInitializers(nodes) {
166
+ for (const node of nodes) {
167
+ if (ts__default["default"].isSpreadElement(node)) {
168
+ return new ValSyntaxError("Unexpected spread element", node);
169
+ }
170
+ }
171
+ return undefined;
172
+ }
173
+
174
+ function isPath(node, path) {
175
+ let currentNode = node;
176
+ for (let i = path.length - 1; i > 0; --i) {
177
+ const name = path[i];
178
+ if (!ts__default["default"].isPropertyAccessExpression(currentNode)) {
179
+ return false;
180
+ }
181
+ if (!ts__default["default"].isIdentifier(currentNode.name) || currentNode.name.text !== name) {
182
+ return false;
183
+ }
184
+ currentNode = currentNode.expression;
185
+ }
186
+ return ts__default["default"].isIdentifier(currentNode) && currentNode.text === path[0];
187
+ }
188
+ function validateArguments(node, validators) {
189
+ return fp.result.allV([node.arguments.length === validators.length ? fp.result.voidOk : fp.result.err(new ValSyntaxError(`Expected ${validators.length} arguments`, node)), ...node.arguments.slice(0, validators.length).map((argument, index) => validators[index](argument))]);
190
+ }
191
+ function analyzeDefaultExport(node) {
192
+ const valContentCall = node.expression;
193
+ if (!ts__default["default"].isCallExpression(valContentCall)) {
194
+ return fp.result.err(new ValSyntaxError("Expected default expression to be a call expression", valContentCall));
195
+ }
196
+ if (!isPath(valContentCall.expression, ["val", "content"])) {
197
+ return fp.result.err(new ValSyntaxError("Expected default expression to be calling val.content", valContentCall.expression));
198
+ }
199
+ return fp.pipe(validateArguments(valContentCall, [id => {
200
+ // TODO: validate ID value here?
201
+ if (!ts__default["default"].isStringLiteralLike(id)) {
202
+ return fp.result.err(new ValSyntaxError("Expected first argument to val.content to be a string literal", id));
203
+ }
204
+ return fp.result.voidOk;
205
+ }, () => {
206
+ return fp.result.voidOk;
207
+ }, () => {
208
+ return fp.result.voidOk;
209
+ }]), fp.result.map(() => {
210
+ const [, schema, source] = valContentCall.arguments;
211
+ return {
212
+ schema,
213
+ source
214
+ };
215
+ }));
216
+ }
217
+ function analyzeValModule(sourceFile) {
218
+ const analysis = sourceFile.forEachChild(node => {
219
+ if (ts__default["default"].isExportAssignment(node)) {
220
+ return analyzeDefaultExport(node);
221
+ }
222
+ });
223
+ if (!analysis) {
224
+ throw Error("Failed to find fixed content node in val module");
225
+ }
226
+ return analysis;
227
+ }
228
+
229
+ function isValidIdentifier(text) {
230
+ if (text.length === 0) {
231
+ return false;
232
+ }
233
+ if (!ts__default["default"].isIdentifierStart(text.charCodeAt(0), ts__default["default"].ScriptTarget.ES2020)) {
234
+ return false;
235
+ }
236
+ for (let i = 1; i < text.length; ++i) {
237
+ if (!ts__default["default"].isIdentifierPart(text.charCodeAt(i), ts__default["default"].ScriptTarget.ES2020)) {
238
+ return false;
239
+ }
240
+ }
241
+ return true;
242
+ }
243
+ function createPropertyAssignment(key, value) {
244
+ return ts__default["default"].factory.createPropertyAssignment(isValidIdentifier(key) ? ts__default["default"].factory.createIdentifier(key) : ts__default["default"].factory.createStringLiteral(key), toExpression(value));
245
+ }
246
+ function createValFileReference(ref) {
247
+ return ts__default["default"].factory.createCallExpression(ts__default["default"].factory.createPropertyAccessExpression(ts__default["default"].factory.createIdentifier("val"), ts__default["default"].factory.createIdentifier("file")), undefined, [ts__default["default"].factory.createStringLiteral(ref)]);
248
+ }
249
+ function toExpression(value) {
250
+ if (typeof value === "string") {
251
+ // TODO: Use configuration/heuristics to determine use of single quote or double quote
252
+ return ts__default["default"].factory.createStringLiteral(value);
253
+ } else if (typeof value === "number") {
254
+ return ts__default["default"].factory.createNumericLiteral(value);
255
+ } else if (typeof value === "boolean") {
256
+ return value ? ts__default["default"].factory.createTrue() : ts__default["default"].factory.createFalse();
257
+ } else if (value === null) {
258
+ return ts__default["default"].factory.createNull();
259
+ } else if (Array.isArray(value)) {
260
+ return ts__default["default"].factory.createArrayLiteralExpression(value.map(toExpression));
261
+ } else if (typeof value === "object") {
262
+ if (isValFileValue(value)) {
263
+ return createValFileReference(value[core.FILE_REF_PROP]);
264
+ }
265
+ return ts__default["default"].factory.createObjectLiteralExpression(Object.entries(value).map(([key, value]) => createPropertyAssignment(key, value)));
266
+ } else {
267
+ return ts__default["default"].factory.createStringLiteral(value);
268
+ }
269
+ }
270
+
271
+ // TODO: Choose newline based on project settings/heuristics/system default?
272
+ const newLine = ts__default["default"].NewLineKind.LineFeed;
273
+ // TODO: Handle indentation of printed code
274
+ const printer = ts__default["default"].createPrinter({
275
+ newLine: newLine
276
+ // neverAsciiEscape: true,
277
+ });
278
+
279
+ function replaceNodeValue(document, node, value) {
280
+ const replacementText = printer.printNode(ts__default["default"].EmitHint.Unspecified, toExpression(value), document);
281
+ const span = ts__default["default"].createTextSpanFromBounds(node.getStart(document, false), node.end);
282
+ const newText = `${document.text.substring(0, span.start)}${replacementText}${document.text.substring(ts__default["default"].textSpanEnd(span))}`;
283
+ return [document.update(newText, ts__default["default"].createTextChangeRange(span, replacementText.length)), node];
284
+ }
285
+ function isIndentation(s) {
286
+ for (let i = 0; i < s.length; ++i) {
287
+ const c = s.charAt(i);
288
+ if (c !== " " && c !== "\t") {
289
+ return false;
290
+ }
291
+ }
292
+ return true;
293
+ }
294
+ function newLineStr(kind) {
295
+ if (kind === ts__default["default"].NewLineKind.CarriageReturnLineFeed) {
296
+ return "\r\n";
297
+ } else {
298
+ return "\n";
299
+ }
300
+ }
301
+ function getSeparator(document, neighbor) {
302
+ const startPos = neighbor.getStart(document, true);
303
+ const basis = document.getLineAndCharacterOfPosition(startPos);
304
+ const lineStartPos = document.getPositionOfLineAndCharacter(basis.line, 0);
305
+ const maybeIndentation = document.getText().substring(lineStartPos, startPos);
306
+ if (isIndentation(maybeIndentation)) {
307
+ return `,${newLineStr(newLine)}${maybeIndentation}`;
308
+ } else {
309
+ return `, `;
310
+ }
311
+ }
312
+ function insertAt(document, nodes, index, node) {
313
+ let span;
314
+ let replacementText;
315
+ if (nodes.length === 0) {
316
+ // Replace entire range of nodes
317
+ replacementText = printer.printNode(ts__default["default"].EmitHint.Unspecified, node, document);
318
+ span = ts__default["default"].createTextSpanFromBounds(nodes.pos, nodes.end);
319
+ } else if (index === nodes.length) {
320
+ // Insert after last node
321
+ const neighbor = nodes[nodes.length - 1];
322
+ replacementText = `${getSeparator(document, neighbor)}${printer.printNode(ts__default["default"].EmitHint.Unspecified, node, document)}`;
323
+ span = ts__default["default"].createTextSpan(neighbor.end, 0);
324
+ } else {
325
+ // Insert before node
326
+ const neighbor = nodes[index];
327
+ replacementText = `${printer.printNode(ts__default["default"].EmitHint.Unspecified, node, document)}${getSeparator(document, neighbor)}`;
328
+ span = ts__default["default"].createTextSpan(neighbor.getStart(document, true), 0);
329
+ }
330
+ const newText = `${document.text.substring(0, span.start)}${replacementText}${document.text.substring(ts__default["default"].textSpanEnd(span))}`;
331
+ return document.update(newText, ts__default["default"].createTextChangeRange(span, replacementText.length));
332
+ }
333
+ function removeAt(document, nodes, index) {
334
+ const node = nodes[index];
335
+ let span;
336
+ if (nodes.length === 1) {
337
+ span = ts__default["default"].createTextSpanFromBounds(nodes.pos, nodes.end);
338
+ } else if (index === nodes.length - 1) {
339
+ // Remove until previous node
340
+ const neighbor = nodes[index - 1];
341
+ span = ts__default["default"].createTextSpanFromBounds(neighbor.end, node.end);
342
+ } else {
343
+ // Remove before next node
344
+ const neighbor = nodes[index + 1];
345
+ span = ts__default["default"].createTextSpanFromBounds(node.getStart(document, true), neighbor.getStart(document, true));
346
+ }
347
+ const newText = `${document.text.substring(0, span.start)}${document.text.substring(ts__default["default"].textSpanEnd(span))}`;
348
+ return [document.update(newText, ts__default["default"].createTextChangeRange(span, 0)), node];
349
+ }
350
+ function parseAndValidateArrayInsertIndex(key, nodes) {
351
+ if (key === "-") {
352
+ // For insertion, all nodes up until the insertion index must be valid
353
+ // initializers
354
+ const err = validateInitializers(nodes);
355
+ if (err) {
356
+ return fp.result.err(err);
357
+ }
358
+ return fp.result.ok(nodes.length);
359
+ }
360
+ return fp.pipe(patch.parseAndValidateArrayIndex(key), fp.result.flatMap(index => {
361
+ // For insertion, all nodes up until the insertion index must be valid
362
+ // initializers
363
+ const err = validateInitializers(nodes.slice(0, index));
364
+ if (err) {
365
+ return fp.result.err(err);
366
+ }
367
+ if (index > nodes.length) {
368
+ return fp.result.err(new patch.PatchError("Array index out of bounds"));
369
+ } else {
370
+ return fp.result.ok(index);
371
+ }
372
+ }));
373
+ }
374
+ function parseAndValidateArrayInboundsIndex(key, nodes) {
375
+ return fp.pipe(patch.parseAndValidateArrayIndex(key), fp.result.flatMap(index => {
376
+ // For in-bounds operations, all nodes up until and including the index
377
+ // must be valid initializers
378
+ const err = validateInitializers(nodes.slice(0, index + 1));
379
+ if (err) {
380
+ return fp.result.err(err);
381
+ }
382
+ if (index >= nodes.length) {
383
+ return fp.result.err(new patch.PatchError("Array index out of bounds"));
384
+ } else {
385
+ return fp.result.ok(index);
386
+ }
387
+ }));
388
+ }
389
+ function replaceInNode(document, node, key, value) {
390
+ if (ts__default["default"].isArrayLiteralExpression(node)) {
391
+ return fp.pipe(parseAndValidateArrayInboundsIndex(key, node.elements), fp.result.map(index => replaceNodeValue(document, node.elements[index], value)));
392
+ } else if (ts__default["default"].isObjectLiteralExpression(node)) {
393
+ return fp.pipe(findObjectPropertyAssignment(node, key), fp.result.flatMap(assignment => {
394
+ if (!assignment) {
395
+ return fp.result.err(new patch.PatchError("Cannot replace object element which does not exist"));
396
+ }
397
+ return fp.result.ok(assignment);
398
+ }), fp.result.map(assignment => replaceNodeValue(document, assignment.initializer, value)));
399
+ } else if (isValFileMethodCall(node)) {
400
+ if (key === core.FILE_REF_PROP) {
401
+ if (typeof value !== "string") {
402
+ return fp.result.err(new patch.PatchError("Cannot replace val.file reference with non-string value"));
403
+ }
404
+ return fp.pipe(findValFileNodeArg(node), fp.result.map(refNode => replaceNodeValue(document, refNode, value)));
405
+ } else {
406
+ return fp.pipe(findValFileMetadataArg(node), fp.result.flatMap(metadataArgNode => {
407
+ if (!metadataArgNode) {
408
+ return fp.result.err(new patch.PatchError("Cannot replace in val.file metadata when it does not exist"));
409
+ }
410
+ if (key !== "metadata") {
411
+ return fp.result.err(new patch.PatchError(`Cannot replace val.file metadata key ${key} when it does not exist`));
412
+ }
413
+ return replaceInNode(document,
414
+ // TODO: creating a fake object here might not be right - seems to work though
415
+ ts__default["default"].factory.createObjectLiteralExpression([ts__default["default"].factory.createPropertyAssignment(key, metadataArgNode)]), key, value);
416
+ }));
417
+ }
418
+ } else {
419
+ return fp.result.err(shallowValidateExpression(node) ?? new patch.PatchError("Cannot replace in non-object/array"));
420
+ }
421
+ }
422
+ function replaceAtPath(document, rootNode, path, value) {
423
+ if (patch.isNotRoot(path)) {
424
+ return fp.pipe(getPointerFromPath(rootNode, path), fp.result.flatMap(([node, key]) => replaceInNode(document, node, key, value)));
425
+ } else {
426
+ return fp.result.ok(replaceNodeValue(document, rootNode, value));
427
+ }
428
+ }
429
+ function getFromNode(node, key) {
430
+ if (ts__default["default"].isArrayLiteralExpression(node)) {
431
+ return fp.pipe(parseAndValidateArrayInboundsIndex(key, node.elements), fp.result.map(index => node.elements[index]));
432
+ } else if (ts__default["default"].isObjectLiteralExpression(node)) {
433
+ return fp.pipe(findObjectPropertyAssignment(node, key), fp.result.map(assignment => assignment === null || assignment === void 0 ? void 0 : assignment.initializer));
434
+ } else if (isValFileMethodCall(node)) {
435
+ if (key === core.FILE_REF_PROP) {
436
+ return findValFileNodeArg(node);
437
+ }
438
+ return findValFileMetadataArg(node);
439
+ } else {
440
+ return fp.result.err(shallowValidateExpression(node) ?? new patch.PatchError("Cannot access non-object/array"));
441
+ }
442
+ }
443
+ function getPointerFromPath(node, path) {
444
+ let targetNode = node;
445
+ let key = path[0];
446
+ for (let i = 0; i < path.length - 1; ++i, key = path[i]) {
447
+ const childNode = getFromNode(targetNode, key);
448
+ if (fp.result.isErr(childNode)) {
449
+ return childNode;
450
+ }
451
+ if (childNode.value === undefined) {
452
+ return fp.result.err(new patch.PatchError("Path refers to non-existing object/array"));
453
+ }
454
+ targetNode = childNode.value;
455
+ }
456
+ return fp.result.ok([targetNode, key]);
457
+ }
458
+ function getAtPath(rootNode, path) {
459
+ return fp.pipe(path, fp.result.flatMapReduce((node, key) => fp.pipe(getFromNode(node, key), fp.result.filterOrElse(childNode => childNode !== undefined, () => new patch.PatchError("Path refers to non-existing object/array"))), rootNode));
460
+ }
461
+ function removeFromNode(document, node, key) {
462
+ if (ts__default["default"].isArrayLiteralExpression(node)) {
463
+ return fp.pipe(parseAndValidateArrayInboundsIndex(key, node.elements), fp.result.map(index => removeAt(document, node.elements, index)));
464
+ } else if (ts__default["default"].isObjectLiteralExpression(node)) {
465
+ return fp.pipe(findObjectPropertyAssignment(node, key), fp.result.flatMap(assignment => {
466
+ if (!assignment) {
467
+ return fp.result.err(new patch.PatchError("Cannot replace object element which does not exist"));
468
+ }
469
+ return fp.result.ok(assignment);
470
+ }), fp.result.map(assignment => [removeAt(document, node.properties, node.properties.indexOf(assignment))[0], assignment.initializer]));
471
+ } else if (isValFileMethodCall(node)) {
472
+ if (key === core.FILE_REF_PROP) {
473
+ return fp.result.err(new patch.PatchError("Cannot remove a ref from val.file"));
474
+ } else {
475
+ return fp.pipe(findValFileMetadataArg(node), fp.result.flatMap(metadataArgNode => {
476
+ if (!metadataArgNode) {
477
+ return fp.result.err(new patch.PatchError("Cannot remove from val.file metadata when it does not exist"));
478
+ }
479
+ return removeFromNode(document, metadataArgNode, key);
480
+ }));
481
+ }
482
+ } else {
483
+ return fp.result.err(shallowValidateExpression(node) ?? new patch.PatchError("Cannot remove from non-object/array"));
484
+ }
485
+ }
486
+ function removeAtPath(document, rootNode, path) {
487
+ return fp.pipe(getPointerFromPath(rootNode, path), fp.result.flatMap(([node, key]) => removeFromNode(document, node, key)));
488
+ }
489
+ function isValFileValue(value) {
490
+ return !!(typeof value === "object" && value && core.FILE_REF_PROP in value && typeof value[core.FILE_REF_PROP] === "string");
491
+ }
492
+ function addToNode(document, node, key, value) {
493
+ if (ts__default["default"].isArrayLiteralExpression(node)) {
494
+ return fp.pipe(parseAndValidateArrayInsertIndex(key, node.elements), fp.result.map(index => [insertAt(document, node.elements, index, toExpression(value))]));
495
+ } else if (ts__default["default"].isObjectLiteralExpression(node)) {
496
+ if (key === core.FILE_REF_PROP) {
497
+ return fp.result.err(new patch.PatchError("Cannot add a key ref to object"));
498
+ }
499
+ return fp.pipe(findObjectPropertyAssignment(node, key), fp.result.map(assignment => {
500
+ if (!assignment) {
501
+ return [insertAt(document, node.properties, node.properties.length, createPropertyAssignment(key, value))];
502
+ } else {
503
+ return replaceNodeValue(document, assignment.initializer, value);
504
+ }
505
+ }));
506
+ } else if (isValFileMethodCall(node)) {
507
+ if (key === core.FILE_REF_PROP) {
508
+ if (typeof value !== "string") {
509
+ return fp.result.err(new patch.PatchError(`Cannot add ${core.FILE_REF_PROP} key to val.file with non-string value`));
510
+ }
511
+ return fp.pipe(findValFileNodeArg(node), fp.result.map(arg => replaceNodeValue(document, arg, value)));
512
+ } else {
513
+ return fp.pipe(findValFileMetadataArg(node), fp.result.flatMap(metadataArgNode => {
514
+ if (metadataArgNode) {
515
+ return fp.result.err(new patch.PatchError("Cannot add metadata to val.file when it already exists"));
516
+ }
517
+ if (key !== "metadata") {
518
+ return fp.result.err(new patch.PatchError(`Cannot add ${key} key to val.file: only metadata is allowed`));
519
+ }
520
+ return fp.result.ok([insertAt(document, node.arguments, node.arguments.length, toExpression(value))]);
521
+ }));
522
+ }
523
+ } else {
524
+ return fp.result.err(shallowValidateExpression(node) ?? new patch.PatchError("Cannot add to non-object/array"));
525
+ }
526
+ }
527
+ function addAtPath(document, rootNode, path, value) {
528
+ if (patch.isNotRoot(path)) {
529
+ return fp.pipe(getPointerFromPath(rootNode, path), fp.result.flatMap(([node, key]) => addToNode(document, node, key, value)));
530
+ } else {
531
+ return fp.result.ok(replaceNodeValue(document, rootNode, value));
532
+ }
533
+ }
534
+ function pickDocument([document]) {
535
+ return document;
536
+ }
537
+ class TSOps {
538
+ constructor(findRoot) {
539
+ this.findRoot = findRoot;
540
+ }
541
+ get(document, path) {
542
+ return fp.pipe(document, this.findRoot, fp.result.flatMap(rootNode => getAtPath(rootNode, path)), fp.result.flatMap(evaluateExpression));
543
+ }
544
+ add(document, path, value) {
545
+ return fp.pipe(document, this.findRoot, fp.result.flatMap(rootNode => addAtPath(document, rootNode, path, value)), fp.result.map(pickDocument));
546
+ }
547
+ remove(document, path) {
548
+ return fp.pipe(document, this.findRoot, fp.result.flatMap(rootNode => removeAtPath(document, rootNode, path)), fp.result.map(pickDocument));
549
+ }
550
+ replace(document, path, value) {
551
+ return fp.pipe(document, this.findRoot, fp.result.flatMap(rootNode => replaceAtPath(document, rootNode, path, value)), fp.result.map(pickDocument));
552
+ }
553
+ move(document, from, path) {
554
+ return fp.pipe(document, this.findRoot, fp.result.flatMap(rootNode => removeAtPath(document, rootNode, from)), fp.result.flatMap(([document, removedNode]) => fp.pipe(evaluateExpression(removedNode), fp.result.map(removedValue => [document, removedValue]))), fp.result.flatMap(([document, removedValue]) => fp.pipe(document, this.findRoot, fp.result.flatMap(root => addAtPath(document, root, path, removedValue)))), fp.result.map(pickDocument));
555
+ }
556
+ copy(document, from, path) {
557
+ return fp.pipe(document, this.findRoot, fp.result.flatMap(rootNode => fp.pipe(getAtPath(rootNode, from), fp.result.flatMap(evaluateExpression), fp.result.flatMap(value => addAtPath(document, rootNode, path, value)))), fp.result.map(pickDocument));
558
+ }
559
+ test(document, path, value) {
560
+ return fp.pipe(document, this.findRoot, fp.result.flatMap(rootNode => getAtPath(rootNode, path)), fp.result.flatMap(evaluateExpression), fp.result.map(documentValue => patch.deepEqual(value, documentValue)));
561
+ }
562
+ }
563
+
564
+ const readValFile = async (id, valConfigPath, runtime) => {
565
+ const context = runtime.newContext();
566
+ try {
567
+ const modulePath = `.${id}.val`;
568
+ const code = `import * as valModule from ${JSON.stringify(modulePath)};
569
+ import { Internal } from "@valbuild/core";
570
+ globalThis.valModule = {
571
+ id: valModule?.default && Internal.getValPath(valModule?.default),
572
+ schema: valModule?.default && Internal.getSchema(valModule?.default)?.serialize(),
573
+ source: valModule?.default && Internal.getRawSource(valModule?.default),
574
+ };
575
+ `;
576
+ const result = context.evalCode(code,
577
+ // Synthetic module name
578
+ path__default["default"].join(path__default["default"].dirname(valConfigPath), "<val>"));
579
+ if (result.error) {
580
+ const error = result.error.consume(context.dump);
581
+ console.error("Got error", error); // TODO: use this to figure out how to strip out QuickJS specific errors and get the actual stack
582
+
583
+ throw new Error(`Could not read val id: ${id}. Cause:\n${error.name}: ${error.message}${error.stack ? error.stack : ""}`);
584
+ } else {
585
+ result.value.dispose();
586
+ const valModule = context.getProp(context.global, "valModule").consume(context.dump);
587
+ const errors = [];
588
+ if (!valModule) {
589
+ errors.push(`Could not find any modules at: ${id}`);
590
+ } else {
591
+ if (valModule.id !== id) {
592
+ errors.push(`Expected val id: '${id}' but got: '${valModule.id}'`);
593
+ }
594
+ if (!(valModule !== null && valModule !== void 0 && valModule.schema)) {
595
+ errors.push(`Expected val id: '${id}' to have a schema`);
596
+ }
597
+ if (!(valModule !== null && valModule !== void 0 && valModule.source)) {
598
+ errors.push(`Expected val id: '${id}' to have a source`);
599
+ }
600
+ }
601
+ if (errors.length > 0) {
602
+ throw Error(`While processing module of id: ${id}, we got the following errors:\n${errors.join("\n")}`);
603
+ }
604
+ return {
605
+ path: valModule.id,
606
+ // This might not be the asked id/path, however, that should be handled further up in the call chain
607
+ source: valModule.source,
608
+ schema: valModule.schema
609
+ };
610
+ }
611
+ } finally {
612
+ context.dispose();
613
+ }
614
+ };
615
+
616
+ const ops = new TSOps(document => {
617
+ return fp.pipe(analyzeValModule(document), fp.result.map(({
618
+ source
619
+ }) => source));
620
+ });
621
+
622
+ // TODO: rename to patchValFiles since we may write multiple files
623
+ const patchValFile = async (id, valConfigPath, patch$1, sourceFileHandler, runtime) => {
624
+ const filePath = sourceFileHandler.resolveSourceModulePath(valConfigPath, `.${id}.val`);
625
+ const sourceFile = sourceFileHandler.getSourceFile(filePath);
626
+ if (!sourceFile) {
627
+ throw Error(`Source file ${filePath} not found`);
628
+ }
629
+ const derefRes = core.derefPatch(patch$1, sourceFile, ops);
630
+ if (fp.result.isErr(derefRes)) {
631
+ throw derefRes.error;
632
+ }
633
+ const dereferencedPatch = derefRes.value.dereferencedPatch; // TODO: add ref changes to remote replace/add, ...
634
+ const newSourceFile = patchSourceFile(sourceFile, dereferencedPatch);
635
+ if (fp.result.isErr(newSourceFile)) {
636
+ if (newSourceFile.error instanceof patch.PatchError) {
637
+ throw newSourceFile.error;
638
+ } else {
639
+ throw new Error(`${filePath}\n${flatMapErrors(newSourceFile.error, error => formatSyntaxError(error, sourceFile)).join("\n")}`);
640
+ }
641
+ }
642
+ for (const [filePath, content] of Object.entries(derefRes.value.fileUpdates)) {
643
+ // Evaluate if we want to make these writes (more) atomic with a temp file and a move.
644
+ // This can potentially fill mid-way if there is not enough space on disk for example...
645
+ // However, that might be add add bit more complexity in our host and virtual file systems?
646
+ // Example:
647
+ // const tempFilePath = sourceFileHandler.writeTempFile(
648
+ // Buffer.from(content, "base64").toString("binary")
649
+ // );
650
+ // sourceFileHandler.moveFile(tempFilePath, "." + filePath);
651
+ sourceFileHandler.writeFile("." + filePath, convertDataUrlToBase64(content).toString("binary"), "binary");
652
+ }
653
+ for (const [ref, patch] of Object.entries(derefRes.value.remotePatches)) {
654
+ throw Error(`Cannot update remote ${ref} with ${JSON.stringify(patch)}: not implemented`);
655
+ }
656
+ sourceFileHandler.writeSourceFile(newSourceFile.value);
657
+ return readValFile(id, valConfigPath, runtime);
658
+ };
659
+ function convertDataUrlToBase64(dataUrl) {
660
+ const base64 = dataUrl.slice(dataUrl.indexOf(",") + 1);
661
+ return Buffer.from(base64, "base64");
662
+ }
663
+ const patchSourceFile = (sourceFile, patch$1) => {
664
+ return patch.applyPatch(sourceFile, ops, patch$1);
665
+ };
666
+
667
+ const getCompilerOptions = (rootDir, parseConfigHost) => {
668
+ const tsConfigPath = path__default["default"].resolve(rootDir, "tsconfig.json");
669
+ const jsConfigPath = path__default["default"].resolve(rootDir, "jsconfig.json");
670
+ let configFilePath;
671
+ if (parseConfigHost.fileExists(jsConfigPath)) {
672
+ configFilePath = jsConfigPath;
673
+ } else if (parseConfigHost.fileExists(tsConfigPath)) {
674
+ configFilePath = tsConfigPath;
675
+ } else {
676
+ throw Error(`Could not read config from: "${tsConfigPath}" nor "${jsConfigPath}". Root dir: "${rootDir}"`);
677
+ }
678
+ const {
679
+ config,
680
+ error
681
+ } = ts__default["default"].readConfigFile(configFilePath, parseConfigHost.readFile.bind(parseConfigHost));
682
+ if (error) {
683
+ if (typeof error.messageText === "string") {
684
+ throw Error(`Could not parse config file: ${configFilePath}. Error: ${error.messageText}`);
685
+ }
686
+ throw Error(`Could not parse config file: ${configFilePath}. Error: ${error.messageText.messageText}`);
687
+ }
688
+ const optionsOverrides = undefined;
689
+ const parsedConfigFile = ts__default["default"].parseJsonConfigFileContent(config, parseConfigHost, rootDir, optionsOverrides, configFilePath);
690
+ if (parsedConfigFile.errors.length > 0) {
691
+ throw Error(`Could not parse config file: ${configFilePath}. Errors: ${parsedConfigFile.errors.map(e => e.messageText).join("\n")}`);
692
+ }
693
+ return parsedConfigFile.options;
694
+ };
695
+
696
+ class ValSourceFileHandler {
697
+ constructor(projectRoot, compilerOptions, host = {
698
+ ...ts__default["default"].sys,
699
+ writeFile: fs__default["default"].writeFileSync
700
+ }) {
701
+ this.projectRoot = projectRoot;
702
+ this.compilerOptions = compilerOptions;
703
+ this.host = host;
704
+ }
705
+ getSourceFile(filePath) {
706
+ const fileContent = this.host.readFile(filePath);
707
+ const scriptTarget = this.compilerOptions.target ?? ts__default["default"].ScriptTarget.ES2020;
708
+ if (fileContent) {
709
+ return ts__default["default"].createSourceFile(filePath, fileContent, scriptTarget);
710
+ }
711
+ }
712
+ writeSourceFile(sourceFile) {
713
+ return this.writeFile(sourceFile.fileName, sourceFile.text, "utf8");
714
+ }
715
+ writeFile(filePath, content, encoding) {
716
+ this.host.writeFile(filePath, content, encoding);
717
+ }
718
+ resolveSourceModulePath(containingFilePath, requestedModuleName) {
719
+ const resolutionRes = ts__default["default"].resolveModuleName(requestedModuleName, path__default["default"].isAbsolute(containingFilePath) ? containingFilePath : path__default["default"].resolve(this.projectRoot, containingFilePath), this.compilerOptions, this.host, undefined, undefined, ts__default["default"].ModuleKind.ESNext);
720
+ const resolvedModule = resolutionRes.resolvedModule;
721
+ if (!resolvedModule) {
722
+ throw Error(`Could not resolve module "${requestedModuleName}", base: "${containingFilePath}": No resolved modules returned: ${JSON.stringify(resolutionRes)}`);
723
+ }
724
+ return resolvedModule.resolvedFileName;
725
+ }
726
+ }
727
+
728
+ const JsFileLookupMapping = [
729
+ // NOTE: first one matching will be used
730
+ [".cjs.d.ts", [".esm.js", ".mjs.js"]], [".cjs.js", [".esm.js", ".mjs.js"]], [".cjs", [".mjs"]], [".d.ts", [".js"]]];
731
+ class ValModuleLoader {
732
+ constructor(projectRoot, compilerOptions, sourceFileHandler, host = {
733
+ ...ts__default["default"].sys,
734
+ writeFile: fs__default["default"].writeFileSync
735
+ }) {
736
+ this.projectRoot = projectRoot;
737
+ this.compilerOptions = compilerOptions;
738
+ this.sourceFileHandler = sourceFileHandler;
739
+ this.host = host;
740
+ }
741
+ getModule(modulePath) {
742
+ if (!modulePath) {
743
+ throw Error(`Illegal module path: "${modulePath}"`);
744
+ }
745
+ const code = this.host.readFile(modulePath);
746
+ if (!code) {
747
+ throw Error(`Could not read file "${modulePath}"`);
748
+ }
749
+ return ts__default["default"].transpile(code, {
750
+ ...this.compilerOptions,
751
+ // allowJs: true,
752
+ // rootDir: this.compilerOptions.rootDir,
753
+ module: ts__default["default"].ModuleKind.ESNext,
754
+ target: ts__default["default"].ScriptTarget.ES2020 // QuickJS supports a lot of ES2020: https://test262.report/, however not all cases are in that report (e.g. export const {} = {})
755
+ // moduleResolution: ts.ModuleResolutionKind.NodeNext,
756
+ // target: ts.ScriptTarget.ES2020, // QuickJs runs in ES2020 so we must use that
757
+ });
758
+ }
759
+
760
+ resolveModulePath(containingFilePath, requestedModuleName) {
761
+ var _this$host$realpath, _this$host;
762
+ const sourceFileName = this.sourceFileHandler.resolveSourceModulePath(containingFilePath, requestedModuleName);
763
+ const matches = this.findMatchingJsFile(sourceFileName);
764
+ if (matches.match === false) {
765
+ throw Error(`Could not find matching js file for module "${requestedModuleName}". Tried:\n${matches.tried.join("\n")}`);
766
+ }
767
+ const filePath = matches.match;
768
+ // resolve all symlinks (preconstruct for example symlinks the dist folder)
769
+ const followedPath = ((_this$host$realpath = (_this$host = this.host).realpath) === null || _this$host$realpath === void 0 ? void 0 : _this$host$realpath.call(_this$host, filePath)) ?? filePath;
770
+ if (!followedPath) {
771
+ throw Error(`File path was empty: "${filePath}", containing file: "${containingFilePath}", requested module: "${requestedModuleName}"`);
772
+ }
773
+ return followedPath;
774
+ }
775
+ findMatchingJsFile(filePath) {
776
+ let requiresReplacements = false;
777
+ for (const [currentEnding] of JsFileLookupMapping) {
778
+ if (filePath.endsWith(currentEnding)) {
779
+ requiresReplacements = true;
780
+ break;
781
+ }
782
+ }
783
+ // avoid unnecessary calls to fileExists if we don't need to replace anything
784
+ if (!requiresReplacements) {
785
+ if (this.host.fileExists(filePath)) {
786
+ return {
787
+ match: filePath
788
+ };
789
+ }
790
+ }
791
+ const tried = [];
792
+ for (const [currentEnding, replacements] of JsFileLookupMapping) {
793
+ if (filePath.endsWith(currentEnding)) {
794
+ for (const replacement of replacements) {
795
+ const newFilePath = filePath.slice(0, -currentEnding.length) + replacement;
796
+ if (this.host.fileExists(newFilePath)) {
797
+ return {
798
+ match: newFilePath
799
+ };
800
+ } else {
801
+ tried.push(newFilePath);
802
+ }
803
+ }
804
+ }
805
+ }
806
+ return {
807
+ match: false,
808
+ tried: tried.concat(filePath)
809
+ };
810
+ }
811
+ }
812
+
813
+ async function newValQuickJSRuntime(quickJSModule, moduleLoader, {
814
+ maxStackSize = 1024 * 640,
815
+ // TODO: these were randomly chosen, we should figure out what the right values are:
816
+ memoryLimit = 1024 * 640
817
+ } = {}) {
818
+ const runtime = quickJSModule.newRuntime();
819
+ runtime.setMaxStackSize(maxStackSize);
820
+ runtime.setMemoryLimit(memoryLimit);
821
+ runtime.setModuleLoader(modulePath => {
822
+ try {
823
+ return {
824
+ value: moduleLoader.getModule(modulePath)
825
+ };
826
+ } catch (e) {
827
+ return {
828
+ error: Error(`Could not resolve module: ${modulePath}'`)
829
+ };
830
+ }
831
+ }, (baseModuleName, requestedName) => {
832
+ try {
833
+ const modulePath = moduleLoader.resolveModulePath(baseModuleName, requestedName);
834
+ return {
835
+ value: modulePath
836
+ };
837
+ } catch (e) {
838
+ console.debug(`Could not resolve ${requestedName} in ${baseModuleName}`, e);
839
+ return {
840
+ value: requestedName
841
+ };
842
+ }
843
+ });
844
+ return runtime;
845
+ }
846
+
847
+ async function createService(projectRoot, opts, host = {
848
+ ...ts__default["default"].sys,
849
+ writeFile: fs__default["default"].writeFileSync
850
+ }) {
851
+ const compilerOptions = getCompilerOptions(projectRoot, host);
852
+ const sourceFileHandler = new ValSourceFileHandler(projectRoot, compilerOptions, host);
853
+ const loader = new ValModuleLoader(projectRoot, compilerOptions, sourceFileHandler, host);
854
+ const module = await quickjsEmscripten.newQuickJSWASMModule();
855
+ const runtime = await newValQuickJSRuntime(module, loader);
856
+ return new Service(opts, sourceFileHandler, runtime);
857
+ }
858
+ class Service {
859
+ constructor({
860
+ valConfigPath
861
+ }, sourceFileHandler, runtime) {
862
+ this.sourceFileHandler = sourceFileHandler;
863
+ this.runtime = runtime;
864
+ this.valConfigPath = valConfigPath;
865
+ }
866
+ async get(moduleId, modulePath) {
867
+ const valModule = await readValFile(moduleId, this.valConfigPath, this.runtime);
868
+ const resolved = core.Internal.resolvePath(modulePath, valModule.source, valModule.schema);
869
+ return {
870
+ path: [moduleId, resolved.path].join("."),
871
+ schema: resolved.schema instanceof core.Schema ? resolved.schema.serialize() : resolved.schema,
872
+ source: resolved.source
873
+ };
874
+ }
875
+ async patch(moduleId, patch) {
876
+ return patchValFile(moduleId, this.valConfigPath, patch, this.sourceFileHandler, this.runtime);
877
+ }
878
+ dispose() {
879
+ this.runtime.dispose();
880
+ }
881
+ }
882
+
883
+ function createRequestHandler(valServer) {
884
+ const router = express.Router();
885
+ router.use("/static", server.createRequestHandler());
886
+ router.get("/session", valServer.session.bind(valServer));
887
+ router.get("/authorize", valServer.authorize.bind(valServer));
888
+ router.get("/callback", valServer.callback.bind(valServer));
889
+ router.get("/logout", valServer.logout.bind(valServer));
890
+ router.get("/ids/*", valServer.getIds.bind(valServer));
891
+ router.patch("/ids/*", express__default["default"].json({
892
+ type: "application/json-patch+json",
893
+ limit: "10mb"
894
+ }), valServer.patchIds.bind(valServer));
895
+ router.post("/commit", valServer.commit.bind(valServer));
896
+ return router;
897
+ }
898
+
899
+ const JSONValueT = z__default["default"].lazy(() => z__default["default"].union([z__default["default"].string(), z__default["default"].number(), z__default["default"].boolean(), z__default["default"].null(), z__default["default"].array(JSONValueT), z__default["default"].record(JSONValueT)]));
900
+
901
+ /**
902
+ * Raw JSON patch operation.
903
+ */
904
+ const OperationJSONT = z__default["default"].discriminatedUnion("op", [z__default["default"].object({
905
+ op: z__default["default"].literal("add"),
906
+ path: z__default["default"].string(),
907
+ value: JSONValueT
908
+ }).strict(), z__default["default"].object({
909
+ op: z__default["default"].literal("remove"),
910
+ /**
911
+ * Must be non-root
912
+ */
913
+ path: z__default["default"].string()
914
+ }).strict(), z__default["default"].object({
915
+ op: z__default["default"].literal("replace"),
916
+ path: z__default["default"].string(),
917
+ value: JSONValueT
918
+ }).strict(), z__default["default"].object({
919
+ op: z__default["default"].literal("move"),
920
+ /**
921
+ * Must be non-root and not a proper prefix of "path".
922
+ */
923
+ from: z__default["default"].string(),
924
+ path: z__default["default"].string()
925
+ }).strict(), z__default["default"].object({
926
+ op: z__default["default"].literal("copy"),
927
+ from: z__default["default"].string(),
928
+ path: z__default["default"].string()
929
+ }).strict(), z__default["default"].object({
930
+ op: z__default["default"].literal("test"),
931
+ path: z__default["default"].string(),
932
+ value: JSONValueT
933
+ }).strict()]);
934
+ const PatchJSON = z__default["default"].array(OperationJSONT);
935
+
936
+ function getPathFromParams(params) {
937
+ return `/${params[0]}`;
938
+ }
939
+
940
+ class LocalValServer {
941
+ constructor(options) {
942
+ this.options = options;
943
+ }
944
+ async session(_req, res) {
945
+ res.json({
946
+ mode: "local"
947
+ });
948
+ }
949
+ async getIds(req, res) {
950
+ try {
951
+ console.log(req.params);
952
+ const path = getPathFromParams(req.params);
953
+ const [moduleId, modulePath] = core.Internal.splitModuleIdAndModulePath(path);
954
+ const valModule = await this.options.service.get(moduleId, modulePath);
955
+ res.json(valModule);
956
+ } catch (err) {
957
+ console.error(err);
958
+ res.sendStatus(500);
959
+ }
960
+ }
961
+ async patchIds(req, res) {
962
+ // First validate that the body has the right structure
963
+ const patchJSON = PatchJSON.safeParse(req.body);
964
+ if (!patchJSON.success) {
965
+ res.status(401).json(patchJSON.error.issues);
966
+ return;
967
+ }
968
+ // Then parse/validate
969
+ const patch$1 = patch.parsePatch(patchJSON.data);
970
+ if (fp.result.isErr(patch$1)) {
971
+ res.status(401).json(patch$1.error);
972
+ return;
973
+ }
974
+ const id = getPathFromParams(req.params);
975
+ try {
976
+ const valModule = await this.options.service.patch(id, patch$1.value);
977
+ res.json(valModule);
978
+ } catch (err) {
979
+ if (err instanceof patch.PatchError) {
980
+ res.status(401).send(err.message);
981
+ } else {
982
+ console.error(err);
983
+ res.status(500).send(err instanceof Error ? err.message : "Unknown error");
984
+ }
985
+ }
986
+ }
987
+ async badRequest(req, res) {
988
+ console.debug("Local server does handle this request", req.url);
989
+ res.sendStatus(400);
990
+ }
991
+ commit(req, res) {
992
+ return this.badRequest(req, res);
993
+ }
994
+ authorize(req, res) {
995
+ return this.badRequest(req, res);
996
+ }
997
+ callback(req, res) {
998
+ return this.badRequest(req, res);
999
+ }
1000
+ logout(req, res) {
1001
+ return this.badRequest(req, res);
1002
+ }
1003
+ }
1004
+
1005
+ function decodeJwt(token, secretKey) {
1006
+ const [headerBase64, payloadBase64, signatureBase64, ...rest] = token.split(".");
1007
+ if (!headerBase64 || !payloadBase64 || !signatureBase64 || rest.length > 0) {
1008
+ console.debug("Invalid JWT: format is not exactly {header}.{payload}.{signature}", token);
1009
+ return null;
1010
+ }
1011
+ try {
1012
+ const parsedHeader = JSON.parse(Buffer.from(headerBase64, "base64").toString("utf8"));
1013
+ const headerVerification = JwtHeaderSchema.safeParse(parsedHeader);
1014
+ if (!headerVerification.success) {
1015
+ console.debug("Invalid JWT: invalid header", parsedHeader);
1016
+ return null;
1017
+ }
1018
+ if (headerVerification.data.typ !== jwtHeader.typ) {
1019
+ console.debug("Invalid JWT: invalid header typ", parsedHeader);
1020
+ return null;
1021
+ }
1022
+ if (headerVerification.data.alg !== jwtHeader.alg) {
1023
+ console.debug("Invalid JWT: invalid header alg", parsedHeader);
1024
+ return null;
1025
+ }
1026
+ } catch (err) {
1027
+ console.debug("Invalid JWT: could not parse header", err);
1028
+ return null;
1029
+ }
1030
+ if (secretKey) {
1031
+ const signature = crypto__default["default"].createHmac("sha256", secretKey).update(`${headerBase64}.${payloadBase64}`).digest("base64");
1032
+ if (signature !== signatureBase64) {
1033
+ console.debug("Invalid JWT: invalid signature");
1034
+ return null;
1035
+ }
1036
+ }
1037
+ try {
1038
+ const parsedPayload = JSON.parse(Buffer.from(payloadBase64, "base64").toString("utf8"));
1039
+ return parsedPayload;
1040
+ } catch (err) {
1041
+ console.debug("Invalid JWT: could not parse payload", err);
1042
+ return null;
1043
+ }
1044
+ }
1045
+ function getExpire() {
1046
+ return Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 4; // 4 days
1047
+ }
1048
+
1049
+ const JwtHeaderSchema = z.z.object({
1050
+ alg: z.z.literal("HS256"),
1051
+ typ: z.z.literal("JWT")
1052
+ });
1053
+ const jwtHeader = {
1054
+ alg: "HS256",
1055
+ typ: "JWT"
1056
+ };
1057
+ const jwtHeaderBase64 = Buffer.from(JSON.stringify(jwtHeader)).toString("base64");
1058
+ function encodeJwt(payload, sessionKey) {
1059
+ // NOTE: this is only used for authentication, not for authorization (i.e. what a user can do) - this is handled when actually doing operations
1060
+ const payloadBase64 = Buffer.from(JSON.stringify(payload)).toString("base64");
1061
+ return `${jwtHeaderBase64}.${payloadBase64}.${crypto__default["default"].createHmac("sha256", sessionKey).update(`${jwtHeaderBase64}.${payloadBase64}`).digest("base64")}`;
1062
+ }
1063
+
1064
+ const VAL_SESSION_COOKIE = "val_session";
1065
+ const VAL_STATE_COOKIE = "val_state";
1066
+ class ProxyValServer {
1067
+ constructor(options) {
1068
+ this.options = options;
1069
+ }
1070
+ async authorize(req, res) {
1071
+ const {
1072
+ redirect_to
1073
+ } = req.query;
1074
+ if (typeof redirect_to !== "string") {
1075
+ res.redirect(this.getAppErrorUrl("Login failed: missing redirect_to param"));
1076
+ return;
1077
+ }
1078
+ const token = crypto__default["default"].randomUUID();
1079
+ const redirectUrl = new URL(redirect_to);
1080
+ const appAuthorizeUrl = this.getAuthorizeUrl(`${redirectUrl.origin}/${this.options.route}`, token);
1081
+ res.cookie(VAL_STATE_COOKIE, createStateCookie({
1082
+ redirect_to,
1083
+ token
1084
+ }), {
1085
+ httpOnly: true,
1086
+ sameSite: "lax",
1087
+ expires: new Date(Date.now() + 1000 * 60 * 60) // 1 hour
1088
+ }).redirect(appAuthorizeUrl);
1089
+ }
1090
+ async callback(req, res) {
1091
+ const {
1092
+ success: callbackReqSuccess,
1093
+ error: callbackReqError
1094
+ } = verifyCallbackReq(req.cookies[VAL_STATE_COOKIE], req.query);
1095
+ res.clearCookie(VAL_STATE_COOKIE); // we don't need this anymore
1096
+
1097
+ if (callbackReqError !== null) {
1098
+ res.redirect(this.getAppErrorUrl(`Authorization callback failed. Details: ${callbackReqError}`));
1099
+ return;
1100
+ }
1101
+ const data = await this.consumeCode(callbackReqSuccess.code);
1102
+ if (data === null) {
1103
+ res.redirect(this.getAppErrorUrl("Failed to exchange code for user"));
1104
+ return;
1105
+ }
1106
+ const exp = getExpire();
1107
+ const cookie = encodeJwt({
1108
+ ...data,
1109
+ exp // this is the client side exp
1110
+ }, this.options.valSecret);
1111
+ res.cookie(VAL_SESSION_COOKIE, cookie, {
1112
+ httpOnly: true,
1113
+ sameSite: "strict",
1114
+ secure: true,
1115
+ expires: new Date(exp * 1000) // NOTE: this is not used for authorization, only for authentication
1116
+ }).redirect(callbackReqSuccess.redirect_uri || "/");
1117
+ }
1118
+ async logout(_req, res) {
1119
+ res.clearCookie(VAL_SESSION_COOKIE).clearCookie(VAL_STATE_COOKIE).sendStatus(200);
1120
+ }
1121
+ async withAuth(req, res, handler) {
1122
+ const cookie = req.cookies[VAL_SESSION_COOKIE];
1123
+ if (typeof cookie === "string") {
1124
+ const verification = IntegratedServerJwtPayload.safeParse(decodeJwt(cookie, this.options.valSecret));
1125
+ if (!verification.success) {
1126
+ res.sendStatus(401);
1127
+ return;
1128
+ }
1129
+ return handler(verification.data);
1130
+ } else {
1131
+ res.sendStatus(401);
1132
+ }
1133
+ }
1134
+ async session(req, res) {
1135
+ return this.withAuth(req, res, async data => {
1136
+ const url = new URL("/api/val/auth/user/session", this.options.valBuildUrl);
1137
+ const fetchRes = await fetch(url, {
1138
+ headers: this.getAuthHeaders(data.token, "application/json")
1139
+ });
1140
+ if (fetchRes.ok) {
1141
+ res.status(fetchRes.status).json({
1142
+ mode: "proxy",
1143
+ ...(await fetchRes.json())
1144
+ });
1145
+ } else {
1146
+ res.sendStatus(fetchRes.status);
1147
+ }
1148
+ });
1149
+ }
1150
+ async getIds(req, res) {
1151
+ return this.withAuth(req, res, async ({
1152
+ token
1153
+ }) => {
1154
+ const id = getPathFromParams(req.params);
1155
+ const url = new URL(`/api/val/modules/${encodeURIComponent(this.options.gitCommit)}${id}`, this.options.valBuildUrl);
1156
+ const fetchRes = await fetch(url, {
1157
+ headers: this.getAuthHeaders(token)
1158
+ });
1159
+ if (fetchRes.ok) {
1160
+ res.status(fetchRes.status).json(await fetchRes.json());
1161
+ } else {
1162
+ res.sendStatus(fetchRes.status);
1163
+ }
1164
+ }).catch(e => {
1165
+ res.status(500).send({
1166
+ error: {
1167
+ message: e === null || e === void 0 ? void 0 : e.message,
1168
+ status: 500
1169
+ }
1170
+ });
1171
+ });
1172
+ }
1173
+ async patchIds(req, res) {
1174
+ this.withAuth(req, res, async ({
1175
+ token
1176
+ }) => {
1177
+ // First validate that the body has the right structure
1178
+ const patchJSON = PatchJSON.safeParse(req.body);
1179
+ if (!patchJSON.success) {
1180
+ res.status(401).json(patchJSON.error.issues);
1181
+ return;
1182
+ }
1183
+ // Then parse/validate
1184
+ const patch$1 = patch.parsePatch(patchJSON.data);
1185
+ if (fp.result.isErr(patch$1)) {
1186
+ res.status(401).json(patch$1.error);
1187
+ return;
1188
+ }
1189
+ const id = getPathFromParams(req.params);
1190
+ const url = new URL(`/api/val/modules/${encodeURIComponent(this.options.gitCommit)}${id}`, this.options.valBuildUrl);
1191
+ // Proxy patch to val.build
1192
+ const fetchRes = await fetch(url, {
1193
+ method: "PATCH",
1194
+ headers: this.getAuthHeaders(token, "application/json-patch+json"),
1195
+ body: JSON.stringify(patch$1)
1196
+ });
1197
+ if (fetchRes.ok) {
1198
+ res.status(fetchRes.status).json(await fetchRes.json());
1199
+ } else {
1200
+ res.sendStatus(fetchRes.status);
1201
+ }
1202
+ }).catch(e => {
1203
+ res.status(500).send({
1204
+ error: {
1205
+ message: e === null || e === void 0 ? void 0 : e.message,
1206
+ status: 500
1207
+ }
1208
+ });
1209
+ });
1210
+ }
1211
+ async commit(req, res) {
1212
+ this.withAuth(req, res, async ({
1213
+ token
1214
+ }) => {
1215
+ const url = new URL(`/api/val/commit/${encodeURIComponent(this.options.gitBranch)}`, this.options.valBuildUrl);
1216
+ const fetchRes = await fetch(url, {
1217
+ method: "POST",
1218
+ headers: this.getAuthHeaders(token)
1219
+ });
1220
+ if (fetchRes.ok) {
1221
+ res.status(fetchRes.status).json(await fetchRes.json());
1222
+ } else {
1223
+ res.sendStatus(fetchRes.status);
1224
+ }
1225
+ });
1226
+ }
1227
+ getAuthHeaders(token, type) {
1228
+ if (!type) {
1229
+ return {
1230
+ Authorization: `Bearer ${token}`
1231
+ };
1232
+ }
1233
+ return {
1234
+ "Content-Type": type,
1235
+ Authorization: `Bearer ${token}`
1236
+ };
1237
+ }
1238
+ async consumeCode(code) {
1239
+ const url = new URL("/api/val/auth/user/token", this.options.valBuildUrl);
1240
+ url.searchParams.set("code", encodeURIComponent(code));
1241
+ return fetch(url, {
1242
+ method: "POST",
1243
+ headers: this.getAuthHeaders(this.options.apiKey, "application/json") // NOTE: we use apiKey as auth on this endpoint (we do not have a token yet)
1244
+ }).then(async res => {
1245
+ if (res.status === 200) {
1246
+ const token = await res.text();
1247
+ const verification = ValAppJwtPayload.safeParse(decodeJwt(token));
1248
+ if (!verification.success) {
1249
+ return null;
1250
+ }
1251
+ return {
1252
+ ...verification.data,
1253
+ token
1254
+ };
1255
+ } else {
1256
+ console.debug("Failed to get data from code: ", res.status);
1257
+ return null;
1258
+ }
1259
+ }).catch(err => {
1260
+ console.debug("Failed to get user from code: ", err);
1261
+ return null;
1262
+ });
1263
+ }
1264
+ getAuthorizeUrl(publicValApiRoute, token) {
1265
+ const url = new URL("/authorize", this.options.valBuildUrl);
1266
+ url.searchParams.set("redirect_uri", encodeURIComponent(`${publicValApiRoute}/callback`));
1267
+ url.searchParams.set("state", token);
1268
+ return url.toString();
1269
+ }
1270
+ getAppErrorUrl(error) {
1271
+ const url = new URL("/authorize", this.options.valBuildUrl);
1272
+ url.searchParams.set("error", encodeURIComponent(error));
1273
+ return url.toString();
1274
+ }
1275
+ }
1276
+ function verifyCallbackReq(stateCookie, queryParams) {
1277
+ if (typeof stateCookie !== "string") {
1278
+ return {
1279
+ success: false,
1280
+ error: "No state cookie"
1281
+ };
1282
+ }
1283
+ const {
1284
+ code,
1285
+ state: tokenFromQuery
1286
+ } = queryParams;
1287
+ if (typeof code !== "string") {
1288
+ return {
1289
+ success: false,
1290
+ error: "No code query param"
1291
+ };
1292
+ }
1293
+ if (typeof tokenFromQuery !== "string") {
1294
+ return {
1295
+ success: false,
1296
+ error: "No state query param"
1297
+ };
1298
+ }
1299
+ const {
1300
+ success: cookieStateSuccess,
1301
+ error: cookieStateError
1302
+ } = getStateFromCookie(stateCookie);
1303
+ if (cookieStateError !== null) {
1304
+ return {
1305
+ success: false,
1306
+ error: cookieStateError
1307
+ };
1308
+ }
1309
+ if (cookieStateSuccess.token !== tokenFromQuery) {
1310
+ return {
1311
+ success: false,
1312
+ error: "Invalid state token"
1313
+ };
1314
+ }
1315
+ return {
1316
+ success: {
1317
+ code,
1318
+ redirect_uri: cookieStateSuccess.redirect_to
1319
+ },
1320
+ error: null
1321
+ };
1322
+ }
1323
+ function getStateFromCookie(stateCookie) {
1324
+ try {
1325
+ const decoded = Buffer.from(stateCookie, "base64").toString("utf8");
1326
+ const parsed = JSON.parse(decoded);
1327
+ if (!parsed) {
1328
+ return {
1329
+ success: false,
1330
+ error: "Invalid state cookie: could not parse"
1331
+ };
1332
+ }
1333
+ if (typeof parsed !== "object") {
1334
+ return {
1335
+ success: false,
1336
+ error: "Invalid state cookie: parsed object is not an object"
1337
+ };
1338
+ }
1339
+ if ("token" in parsed && "redirect_to" in parsed) {
1340
+ const {
1341
+ token,
1342
+ redirect_to
1343
+ } = parsed;
1344
+ if (typeof token !== "string") {
1345
+ return {
1346
+ success: false,
1347
+ error: "Invalid state cookie: no token in parsed object"
1348
+ };
1349
+ }
1350
+ if (typeof redirect_to !== "string") {
1351
+ return {
1352
+ success: false,
1353
+ error: "Invalid state cookie: no redirect_to in parsed object"
1354
+ };
1355
+ }
1356
+ return {
1357
+ success: {
1358
+ token,
1359
+ redirect_to
1360
+ },
1361
+ error: null
1362
+ };
1363
+ } else {
1364
+ return {
1365
+ success: false,
1366
+ error: "Invalid state cookie: no token or redirect_to in parsed object"
1367
+ };
1368
+ }
1369
+ } catch (err) {
1370
+ return {
1371
+ success: false,
1372
+ error: "Invalid state cookie: could not parse"
1373
+ };
1374
+ }
1375
+ }
1376
+ function createStateCookie(state) {
1377
+ return Buffer.from(JSON.stringify(state), "utf8").toString("base64");
1378
+ }
1379
+ const ValAppJwtPayload = z.z.object({
1380
+ sub: z.z.string(),
1381
+ exp: z.z.number(),
1382
+ project: z.z.string(),
1383
+ org: z.z.string()
1384
+ });
1385
+ const IntegratedServerJwtPayload = z.z.object({
1386
+ sub: z.z.string(),
1387
+ exp: z.z.number(),
1388
+ token: z.z.string(),
1389
+ org: z.z.string(),
1390
+ project: z.z.string()
1391
+ });
1392
+
1393
+ async function _createRequestListener(route, opts) {
1394
+ const serverOpts = await initHandlerOptions(route, opts);
1395
+ let valServer;
1396
+ if (serverOpts.mode === "proxy") {
1397
+ valServer = new ProxyValServer(serverOpts);
1398
+ } else {
1399
+ valServer = new LocalValServer(serverOpts);
1400
+ }
1401
+ const reqHandler = createRequestHandler(valServer);
1402
+ return express__default["default"]().use(route, reqHandler);
1403
+ }
1404
+ async function initHandlerOptions(route, opts) {
1405
+ const maybeApiKey = opts.apiKey || process.env.VAL_API_KEY;
1406
+ const maybeValSecret = opts.valSecret || process.env.VAL_SECRET;
1407
+ const isProxyMode = opts.mode === "proxy" || opts.mode === undefined && (maybeApiKey || maybeValSecret);
1408
+ if (isProxyMode) {
1409
+ const valBuildUrl = opts.valBuildUrl || process.env.VAL_BUILD_URL || "https://app.val.build";
1410
+ if (!maybeApiKey || !maybeValSecret) {
1411
+ throw new Error("VAL_API_KEY and VAL_SECRET env vars must both be set in proxy mode");
1412
+ }
1413
+ const maybeGitCommit = opts.gitCommit || process.env.VAL_GIT_COMMIT;
1414
+ if (!maybeGitCommit) {
1415
+ throw new Error("VAL_GIT_COMMIT env var must be set in proxy mode");
1416
+ }
1417
+ const maybeGitBranch = opts.gitBranch || process.env.VAL_GIT_BRANCH;
1418
+ if (!maybeGitBranch) {
1419
+ throw new Error("VAL_GIT_BRANCH env var must be set in proxy mode");
1420
+ }
1421
+ return {
1422
+ mode: "proxy",
1423
+ route,
1424
+ apiKey: maybeApiKey,
1425
+ valSecret: maybeValSecret,
1426
+ valBuildUrl,
1427
+ gitCommit: maybeGitCommit,
1428
+ gitBranch: maybeGitBranch
1429
+ };
1430
+ } else {
1431
+ const service = await createService(process.cwd(), opts);
1432
+ return {
1433
+ mode: "local",
1434
+ service
1435
+ };
1436
+ }
1437
+ }
1438
+
1439
+ // TODO: rename to createValApiHandlers?
1440
+ function createRequestListener(route, opts) {
1441
+ const handler = _createRequestListener(route, opts);
1442
+ return async (req, res) => {
1443
+ try {
1444
+ return (await handler)(req, res);
1445
+ } catch (e) {
1446
+ res.statusCode = 500;
1447
+ res.write(e instanceof Error ? e.message : "Unknown error");
1448
+ res.end();
1449
+ return;
1450
+ }
1451
+ };
1452
+ }
1453
+
1454
+ /**
1455
+ * An implementation of methods in the various ts.*Host interfaces
1456
+ * that uses ValFS to resolve modules and read/write files.
1457
+ */
1458
+
1459
+ class ValFSHost {
1460
+ constructor(valFS, currentDirectory) {
1461
+ this.valFS = valFS;
1462
+ this.currentDirectory = currentDirectory;
1463
+ }
1464
+ useCaseSensitiveFileNames = true;
1465
+ readDirectory(rootDir, extensions, excludes, includes, depth) {
1466
+ return this.valFS.readDirectory(rootDir, extensions, excludes, includes, depth);
1467
+ }
1468
+ writeFile(fileName, text, encoding) {
1469
+ this.valFS.writeFile(fileName, text, encoding);
1470
+ }
1471
+ getCurrentDirectory() {
1472
+ return this.currentDirectory;
1473
+ }
1474
+ getCanonicalFileName(fileName) {
1475
+ if (path__default["default"].isAbsolute(fileName)) {
1476
+ return path__default["default"].normalize(fileName);
1477
+ }
1478
+ return path__default["default"].resolve(this.getCurrentDirectory(), fileName);
1479
+ }
1480
+ fileExists(fileName) {
1481
+ return this.valFS.fileExists(fileName);
1482
+ }
1483
+ readFile(fileName) {
1484
+ return this.valFS.readFile(fileName);
1485
+ }
1486
+ realpath(path) {
1487
+ return this.valFS.realpath(path);
1488
+ }
1489
+ }
1490
+
1491
+ exports.Service = Service;
1492
+ exports.ValFSHost = ValFSHost;
1493
+ exports.ValModuleLoader = ValModuleLoader;
1494
+ exports.ValSourceFileHandler = ValSourceFileHandler;
1495
+ exports.createRequestHandler = createRequestHandler;
1496
+ exports.createRequestListener = createRequestListener;
1497
+ exports.createService = createService;
1498
+ exports.formatSyntaxErrorTree = formatSyntaxErrorTree;
1499
+ exports.getCompilerOptions = getCompilerOptions;
1500
+ exports.patchSourceFile = patchSourceFile;