@valbuild/server 0.60.2 → 0.60.4

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.
@@ -1,6 +1,5 @@
1
1
  import { newQuickJSWASMModule } from 'quickjs-emscripten';
2
- import * as ts from 'typescript';
3
- import ts__default from 'typescript';
2
+ import ts from 'typescript';
4
3
  import { result, pipe } from '@valbuild/core/fp';
5
4
  import { FILE_REF_PROP, FILE_REF_SUBTYPE_TAG, RT_IMAGE_TAG, VAL_EXTENSION, derefPatch, Internal, Schema, deserializeSchema } from '@valbuild/core';
6
5
  import { deepEqual, isNotRoot, PatchError, parseAndValidateArrayIndex, applyPatch, JSONOps, sourceToPatchPath } from '@valbuild/core/patch';
@@ -43,12 +42,12 @@ function formatSyntaxErrorTree(tree, sourceFile) {
43
42
  return flatMapErrors(tree, error => formatSyntaxError(error, sourceFile));
44
43
  }
45
44
  function isLiteralPropertyName(name) {
46
- return ts__default.isIdentifier(name) || ts__default.isStringLiteral(name) || ts__default.isNumericLiteral(name);
45
+ return ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name);
47
46
  }
48
47
  function validateObjectProperties(nodes) {
49
48
  const errors = [];
50
49
  for (const node of nodes) {
51
- if (!ts__default.isPropertyAssignment(node)) {
50
+ if (!ts.isPropertyAssignment(node)) {
52
51
  errors.push(new ValSyntaxError("Object literal element must be property assignment", node));
53
52
  } else if (!isLiteralPropertyName(node.name)) {
54
53
  errors.push(new ValSyntaxError("Object literal element key must be an identifier or a literal", node));
@@ -65,7 +64,7 @@ function validateObjectProperties(nodes) {
65
64
  * validating its children.
66
65
  */
67
66
  function shallowValidateExpression(value) {
68
- return ts__default.isStringLiteralLike(value) || ts__default.isNumericLiteral(value) || value.kind === ts__default.SyntaxKind.TrueKeyword || value.kind === ts__default.SyntaxKind.FalseKeyword || value.kind === ts__default.SyntaxKind.NullKeyword || ts__default.isArrayLiteralExpression(value) || ts__default.isObjectLiteralExpression(value) || isValFileMethodCall(value) ? undefined : new ValSyntaxError("Expression must be a literal or call c.file", value);
67
+ return ts.isStringLiteralLike(value) || ts.isNumericLiteral(value) || value.kind === ts.SyntaxKind.TrueKeyword || value.kind === ts.SyntaxKind.FalseKeyword || value.kind === ts.SyntaxKind.NullKeyword || ts.isArrayLiteralExpression(value) || ts.isObjectLiteralExpression(value) || isValFileMethodCall(value) ? undefined : new ValSyntaxError("Expression must be a literal or call c.file", value);
69
68
  }
70
69
 
71
70
  /**
@@ -77,19 +76,19 @@ function evaluateExpression(value) {
77
76
  // 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".
78
77
  // https://github.com/microsoft/TypeScript/blob/4b794fe1dd0d184d3f8f17e94d8187eace57c91e/src/compiler/types.ts#L2127-L2131
79
78
 
80
- if (ts__default.isStringLiteralLike(value)) {
79
+ if (ts.isStringLiteralLike(value)) {
81
80
  return result.ok(value.text);
82
- } else if (ts__default.isNumericLiteral(value)) {
81
+ } else if (ts.isNumericLiteral(value)) {
83
82
  return result.ok(Number(value.text));
84
- } else if (value.kind === ts__default.SyntaxKind.TrueKeyword) {
83
+ } else if (value.kind === ts.SyntaxKind.TrueKeyword) {
85
84
  return result.ok(true);
86
- } else if (value.kind === ts__default.SyntaxKind.FalseKeyword) {
85
+ } else if (value.kind === ts.SyntaxKind.FalseKeyword) {
87
86
  return result.ok(false);
88
- } else if (value.kind === ts__default.SyntaxKind.NullKeyword) {
87
+ } else if (value.kind === ts.SyntaxKind.NullKeyword) {
89
88
  return result.ok(null);
90
- } else if (ts__default.isArrayLiteralExpression(value)) {
89
+ } else if (ts.isArrayLiteralExpression(value)) {
91
90
  return result.all(value.elements.map(evaluateExpression));
92
- } else if (ts__default.isObjectLiteralExpression(value)) {
91
+ } else if (ts.isObjectLiteralExpression(value)) {
93
92
  return pipe(validateObjectProperties(value.properties), result.flatMap(assignments => pipe(assignments.map(assignment => pipe(evaluateExpression(assignment.initializer), result.map(value => [assignment.name.text, value]))), result.all)), result.map(Object.fromEntries));
94
93
  } else if (isValFileMethodCall(value)) {
95
94
  return pipe(findValFileNodeArg(value), result.flatMap(ref => {
@@ -122,14 +121,14 @@ function findObjectPropertyAssignment(value, key) {
122
121
  }));
123
122
  }
124
123
  function isValFileMethodCall(node) {
125
- return ts__default.isCallExpression(node) && ts__default.isPropertyAccessExpression(node.expression) && ts__default.isIdentifier(node.expression.expression) && node.expression.expression.text === "c" && node.expression.name.text === "file";
124
+ return ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.isIdentifier(node.expression.expression) && node.expression.expression.text === "c" && node.expression.name.text === "file";
126
125
  }
127
126
  function findValFileNodeArg(node) {
128
127
  if (node.arguments.length === 0) {
129
128
  return result.err(new ValSyntaxError(`Invalid c.file() call: missing ref argument`, node));
130
129
  } else if (node.arguments.length > 2) {
131
130
  return result.err(new ValSyntaxError(`Invalid c.file() call: too many arguments ${node.arguments.length}}`, node));
132
- } else if (!ts__default.isStringLiteral(node.arguments[0])) {
131
+ } else if (!ts.isStringLiteral(node.arguments[0])) {
133
132
  return result.err(new ValSyntaxError(`Invalid c.file() call: ref must be a string literal`, node));
134
133
  }
135
134
  const refNode = node.arguments[0];
@@ -141,7 +140,7 @@ function findValFileMetadataArg(node) {
141
140
  } else if (node.arguments.length > 2) {
142
141
  return result.err(new ValSyntaxError(`Invalid c.file() call: too many arguments ${node.arguments.length}}`, node));
143
142
  } else if (node.arguments.length === 2) {
144
- if (!ts__default.isObjectLiteralExpression(node.arguments[1])) {
143
+ if (!ts.isObjectLiteralExpression(node.arguments[1])) {
145
144
  return result.err(new ValSyntaxError(`Invalid c.file() call: metadata must be a object literal`, node));
146
145
  }
147
146
  return result.ok(node.arguments[1]);
@@ -156,7 +155,7 @@ function findValFileMetadataArg(node) {
156
155
  */
157
156
  function validateInitializers(nodes) {
158
157
  for (const node of nodes) {
159
- if (ts__default.isSpreadElement(node)) {
158
+ if (ts.isSpreadElement(node)) {
160
159
  return new ValSyntaxError("Unexpected spread element", node);
161
160
  }
162
161
  }
@@ -167,22 +166,22 @@ function isPath(node, path) {
167
166
  let currentNode = node;
168
167
  for (let i = path.length - 1; i > 0; --i) {
169
168
  const name = path[i];
170
- if (!ts__default.isPropertyAccessExpression(currentNode)) {
169
+ if (!ts.isPropertyAccessExpression(currentNode)) {
171
170
  return false;
172
171
  }
173
- if (!ts__default.isIdentifier(currentNode.name) || currentNode.name.text !== name) {
172
+ if (!ts.isIdentifier(currentNode.name) || currentNode.name.text !== name) {
174
173
  return false;
175
174
  }
176
175
  currentNode = currentNode.expression;
177
176
  }
178
- return ts__default.isIdentifier(currentNode) && currentNode.text === path[0];
177
+ return ts.isIdentifier(currentNode) && currentNode.text === path[0];
179
178
  }
180
179
  function validateArguments(node, validators) {
181
180
  return result.allV([node.arguments.length === validators.length ? result.voidOk : result.err(new ValSyntaxError(`Expected ${validators.length} arguments`, node)), ...node.arguments.slice(0, validators.length).map((argument, index) => validators[index](argument))]);
182
181
  }
183
182
  function analyzeDefaultExport(node) {
184
183
  const cDefine = node.expression;
185
- if (!ts__default.isCallExpression(cDefine)) {
184
+ if (!ts.isCallExpression(cDefine)) {
186
185
  return result.err(new ValSyntaxError("Expected default expression to be a call expression", cDefine));
187
186
  }
188
187
  if (!isPath(cDefine.expression, ["c", "define"])) {
@@ -190,7 +189,7 @@ function analyzeDefaultExport(node) {
190
189
  }
191
190
  return pipe(validateArguments(cDefine, [id => {
192
191
  // TODO: validate ID value here?
193
- if (!ts__default.isStringLiteralLike(id)) {
192
+ if (!ts.isStringLiteralLike(id)) {
194
193
  return result.err(new ValSyntaxError("Expected first argument to c.define to be a string literal", id));
195
194
  }
196
195
  return result.voidOk;
@@ -208,7 +207,7 @@ function analyzeDefaultExport(node) {
208
207
  }
209
208
  function analyzeValModule(sourceFile) {
210
209
  const analysis = sourceFile.forEachChild(node => {
211
- if (ts__default.isExportAssignment(node)) {
210
+ if (ts.isExportAssignment(node)) {
212
211
  return analyzeDefaultExport(node);
213
212
  }
214
213
  });
@@ -222,62 +221,62 @@ function isValidIdentifier(text) {
222
221
  if (text.length === 0) {
223
222
  return false;
224
223
  }
225
- if (!ts__default.isIdentifierStart(text.charCodeAt(0), ts__default.ScriptTarget.ES2020)) {
224
+ if (!ts.isIdentifierStart(text.charCodeAt(0), ts.ScriptTarget.ES2020)) {
226
225
  return false;
227
226
  }
228
227
  for (let i = 1; i < text.length; ++i) {
229
- if (!ts__default.isIdentifierPart(text.charCodeAt(i), ts__default.ScriptTarget.ES2020)) {
228
+ if (!ts.isIdentifierPart(text.charCodeAt(i), ts.ScriptTarget.ES2020)) {
230
229
  return false;
231
230
  }
232
231
  }
233
232
  return true;
234
233
  }
235
234
  function createPropertyAssignment(key, value) {
236
- return ts__default.factory.createPropertyAssignment(isValidIdentifier(key) ? ts__default.factory.createIdentifier(key) : ts__default.factory.createStringLiteral(key), toExpression(value));
235
+ return ts.factory.createPropertyAssignment(isValidIdentifier(key) ? ts.factory.createIdentifier(key) : ts.factory.createStringLiteral(key), toExpression(value));
237
236
  }
238
237
  function createValFileReference(value) {
239
- const args = [ts__default.factory.createStringLiteral(value[FILE_REF_PROP])];
238
+ const args = [ts.factory.createStringLiteral(value[FILE_REF_PROP])];
240
239
  if (value.metadata) {
241
240
  args.push(toExpression(value.metadata));
242
241
  }
243
- return ts__default.factory.createCallExpression(ts__default.factory.createPropertyAccessExpression(ts__default.factory.createIdentifier("c"), ts__default.factory.createIdentifier("file")), undefined, args);
242
+ return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier("c"), ts.factory.createIdentifier("file")), undefined, args);
244
243
  }
245
244
  function createValRichTextImage(value) {
246
- const args = [ts__default.factory.createStringLiteral(value[FILE_REF_PROP])];
245
+ const args = [ts.factory.createStringLiteral(value[FILE_REF_PROP])];
247
246
  if (value.metadata) {
248
247
  args.push(toExpression(value.metadata));
249
248
  }
250
- return ts__default.factory.createCallExpression(ts__default.factory.createPropertyAccessExpression(ts__default.factory.createPropertyAccessExpression(ts__default.factory.createIdentifier("c"), "rt"), ts__default.factory.createIdentifier("image")), undefined, args);
249
+ return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier("c"), "rt"), ts.factory.createIdentifier("image")), undefined, args);
251
250
  }
252
251
  function createValLink(value) {
253
- const args = [ts__default.factory.createStringLiteral(value.children[0]), toExpression({
252
+ const args = [ts.factory.createStringLiteral(value.children[0]), toExpression({
254
253
  href: value.href
255
254
  })];
256
- return ts__default.factory.createCallExpression(ts__default.factory.createPropertyAccessExpression(ts__default.factory.createPropertyAccessExpression(ts__default.factory.createIdentifier("c"), "rt"), ts__default.factory.createIdentifier("link")), undefined, args);
255
+ return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier("c"), "rt"), ts.factory.createIdentifier("link")), undefined, args);
257
256
  }
258
257
  function createValRichTextTaggedStringTemplate(value) {
259
258
  const {
260
259
  templateStrings: [head, ...others],
261
260
  exprs
262
261
  } = value;
263
- const tag = ts__default.factory.createPropertyAccessExpression(ts__default.factory.createIdentifier("c"), ts__default.factory.createIdentifier("richtext"));
262
+ const tag = ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier("c"), ts.factory.createIdentifier("richtext"));
264
263
  if (exprs.length > 0) {
265
- return ts__default.factory.createTaggedTemplateExpression(tag, undefined, ts__default.factory.createTemplateExpression(ts__default.factory.createTemplateHead(head, head), others.map((s, i) => ts__default.factory.createTemplateSpan(toExpression(exprs[i]), i < others.length - 1 ? ts__default.factory.createTemplateMiddle(s, s) : ts__default.factory.createTemplateTail(s, s)))));
264
+ return ts.factory.createTaggedTemplateExpression(tag, undefined, ts.factory.createTemplateExpression(ts.factory.createTemplateHead(head, head), others.map((s, i) => ts.factory.createTemplateSpan(toExpression(exprs[i]), i < others.length - 1 ? ts.factory.createTemplateMiddle(s, s) : ts.factory.createTemplateTail(s, s)))));
266
265
  }
267
- return ts__default.factory.createTaggedTemplateExpression(tag, undefined, ts__default.factory.createNoSubstitutionTemplateLiteral(head, head));
266
+ return ts.factory.createTaggedTemplateExpression(tag, undefined, ts.factory.createNoSubstitutionTemplateLiteral(head, head));
268
267
  }
269
268
  function toExpression(value) {
270
269
  if (typeof value === "string") {
271
270
  // TODO: Use configuration/heuristics to determine use of single quote or double quote
272
- return ts__default.factory.createStringLiteral(value);
271
+ return ts.factory.createStringLiteral(value);
273
272
  } else if (typeof value === "number") {
274
- return ts__default.factory.createNumericLiteral(value);
273
+ return ts.factory.createNumericLiteral(value);
275
274
  } else if (typeof value === "boolean") {
276
- return value ? ts__default.factory.createTrue() : ts__default.factory.createFalse();
275
+ return value ? ts.factory.createTrue() : ts.factory.createFalse();
277
276
  } else if (value === null) {
278
- return ts__default.factory.createNull();
277
+ return ts.factory.createNull();
279
278
  } else if (Array.isArray(value)) {
280
- return ts__default.factory.createArrayLiteralExpression(value.map(toExpression));
279
+ return ts.factory.createArrayLiteralExpression(value.map(toExpression));
281
280
  } else if (typeof value === "object") {
282
281
  if (isValFileValue(value)) {
283
282
  if (isValRichTextImageValue(value)) {
@@ -289,24 +288,24 @@ function toExpression(value) {
289
288
  } else if (isValRichTextValue(value)) {
290
289
  return createValRichTextTaggedStringTemplate(value);
291
290
  }
292
- return ts__default.factory.createObjectLiteralExpression(Object.entries(value).map(([key, value]) => createPropertyAssignment(key, value)));
291
+ return ts.factory.createObjectLiteralExpression(Object.entries(value).map(([key, value]) => createPropertyAssignment(key, value)));
293
292
  } else {
294
- return ts__default.factory.createStringLiteral(value);
293
+ return ts.factory.createStringLiteral(value);
295
294
  }
296
295
  }
297
296
 
298
297
  // TODO: Choose newline based on project settings/heuristics/system default?
299
- const newLine = ts__default.NewLineKind.LineFeed;
298
+ const newLine = ts.NewLineKind.LineFeed;
300
299
  // TODO: Handle indentation of printed code
301
- const printer = ts__default.createPrinter({
300
+ const printer = ts.createPrinter({
302
301
  newLine: newLine
303
302
  // neverAsciiEscape: true,
304
303
  });
305
304
  function replaceNodeValue(document, node, value) {
306
- const replacementText = printer.printNode(ts__default.EmitHint.Unspecified, toExpression(value), document);
307
- const span = ts__default.createTextSpanFromBounds(node.getStart(document, false), node.end);
308
- const newText = `${document.text.substring(0, span.start)}${replacementText}${document.text.substring(ts__default.textSpanEnd(span))}`;
309
- return [document.update(newText, ts__default.createTextChangeRange(span, replacementText.length)), node];
305
+ const replacementText = printer.printNode(ts.EmitHint.Unspecified, toExpression(value), document);
306
+ const span = ts.createTextSpanFromBounds(node.getStart(document, false), node.end);
307
+ const newText = `${document.text.substring(0, span.start)}${replacementText}${document.text.substring(ts.textSpanEnd(span))}`;
308
+ return [document.update(newText, ts.createTextChangeRange(span, replacementText.length)), node];
310
309
  }
311
310
  function isIndentation(s) {
312
311
  for (let i = 0; i < s.length; ++i) {
@@ -318,7 +317,7 @@ function isIndentation(s) {
318
317
  return true;
319
318
  }
320
319
  function newLineStr(kind) {
321
- if (kind === ts__default.NewLineKind.CarriageReturnLineFeed) {
320
+ if (kind === ts.NewLineKind.CarriageReturnLineFeed) {
322
321
  return "\r\n";
323
322
  } else {
324
323
  return "\n";
@@ -340,38 +339,38 @@ function insertAt(document, nodes, index, node) {
340
339
  let replacementText;
341
340
  if (nodes.length === 0) {
342
341
  // Replace entire range of nodes
343
- replacementText = printer.printNode(ts__default.EmitHint.Unspecified, node, document);
344
- span = ts__default.createTextSpanFromBounds(nodes.pos, nodes.end);
342
+ replacementText = printer.printNode(ts.EmitHint.Unspecified, node, document);
343
+ span = ts.createTextSpanFromBounds(nodes.pos, nodes.end);
345
344
  } else if (index === nodes.length) {
346
345
  // Insert after last node
347
346
  const neighbor = nodes[nodes.length - 1];
348
- replacementText = `${getSeparator(document, neighbor)}${printer.printNode(ts__default.EmitHint.Unspecified, node, document)}`;
349
- span = ts__default.createTextSpan(neighbor.end, 0);
347
+ replacementText = `${getSeparator(document, neighbor)}${printer.printNode(ts.EmitHint.Unspecified, node, document)}`;
348
+ span = ts.createTextSpan(neighbor.end, 0);
350
349
  } else {
351
350
  // Insert before node
352
351
  const neighbor = nodes[index];
353
- replacementText = `${printer.printNode(ts__default.EmitHint.Unspecified, node, document)}${getSeparator(document, neighbor)}`;
354
- span = ts__default.createTextSpan(neighbor.getStart(document, true), 0);
352
+ replacementText = `${printer.printNode(ts.EmitHint.Unspecified, node, document)}${getSeparator(document, neighbor)}`;
353
+ span = ts.createTextSpan(neighbor.getStart(document, true), 0);
355
354
  }
356
- const newText = `${document.text.substring(0, span.start)}${replacementText}${document.text.substring(ts__default.textSpanEnd(span))}`;
357
- return document.update(newText, ts__default.createTextChangeRange(span, replacementText.length));
355
+ const newText = `${document.text.substring(0, span.start)}${replacementText}${document.text.substring(ts.textSpanEnd(span))}`;
356
+ return document.update(newText, ts.createTextChangeRange(span, replacementText.length));
358
357
  }
359
358
  function removeAt(document, nodes, index) {
360
359
  const node = nodes[index];
361
360
  let span;
362
361
  if (nodes.length === 1) {
363
- span = ts__default.createTextSpanFromBounds(nodes.pos, nodes.end);
362
+ span = ts.createTextSpanFromBounds(nodes.pos, nodes.end);
364
363
  } else if (index === nodes.length - 1) {
365
364
  // Remove until previous node
366
365
  const neighbor = nodes[index - 1];
367
- span = ts__default.createTextSpanFromBounds(neighbor.end, node.end);
366
+ span = ts.createTextSpanFromBounds(neighbor.end, node.end);
368
367
  } else {
369
368
  // Remove before next node
370
369
  const neighbor = nodes[index + 1];
371
- span = ts__default.createTextSpanFromBounds(node.getStart(document, true), neighbor.getStart(document, true));
370
+ span = ts.createTextSpanFromBounds(node.getStart(document, true), neighbor.getStart(document, true));
372
371
  }
373
- const newText = `${document.text.substring(0, span.start)}${document.text.substring(ts__default.textSpanEnd(span))}`;
374
- return [document.update(newText, ts__default.createTextChangeRange(span, 0)), node];
372
+ const newText = `${document.text.substring(0, span.start)}${document.text.substring(ts.textSpanEnd(span))}`;
373
+ return [document.update(newText, ts.createTextChangeRange(span, 0)), node];
375
374
  }
376
375
  function parseAndValidateArrayInsertIndex(key, nodes) {
377
376
  if (key === "-") {
@@ -413,9 +412,9 @@ function parseAndValidateArrayInboundsIndex(key, nodes) {
413
412
  }));
414
413
  }
415
414
  function replaceInNode(document, node, key, value) {
416
- if (ts__default.isArrayLiteralExpression(node)) {
415
+ if (ts.isArrayLiteralExpression(node)) {
417
416
  return pipe(parseAndValidateArrayInboundsIndex(key, node.elements), result.map(index => replaceNodeValue(document, node.elements[index], value)));
418
- } else if (ts__default.isObjectLiteralExpression(node)) {
417
+ } else if (ts.isObjectLiteralExpression(node)) {
419
418
  return pipe(findObjectPropertyAssignment(node, key), result.flatMap(assignment => {
420
419
  if (!assignment) {
421
420
  return result.err(new PatchError("Cannot replace object element which does not exist"));
@@ -438,7 +437,7 @@ function replaceInNode(document, node, key, value) {
438
437
  }
439
438
  return replaceInNode(document,
440
439
  // TODO: creating a fake object here might not be right - seems to work though
441
- ts__default.factory.createObjectLiteralExpression([ts__default.factory.createPropertyAssignment(key, metadataArgNode)]), key, value);
440
+ ts.factory.createObjectLiteralExpression([ts.factory.createPropertyAssignment(key, metadataArgNode)]), key, value);
442
441
  }));
443
442
  }
444
443
  } else {
@@ -453,9 +452,9 @@ function replaceAtPath(document, rootNode, path, value) {
453
452
  }
454
453
  }
455
454
  function getFromNode(node, key) {
456
- if (ts__default.isArrayLiteralExpression(node)) {
455
+ if (ts.isArrayLiteralExpression(node)) {
457
456
  return pipe(parseAndValidateArrayInboundsIndex(key, node.elements), result.map(index => node.elements[index]));
458
- } else if (ts__default.isObjectLiteralExpression(node)) {
457
+ } else if (ts.isObjectLiteralExpression(node)) {
459
458
  return pipe(findObjectPropertyAssignment(node, key), result.map(assignment => assignment === null || assignment === void 0 ? void 0 : assignment.initializer));
460
459
  } else if (isValFileMethodCall(node)) {
461
460
  if (key === FILE_REF_PROP) {
@@ -485,9 +484,9 @@ function getAtPath(rootNode, path) {
485
484
  return pipe(path, result.flatMapReduce((node, key) => pipe(getFromNode(node, key), result.filterOrElse(childNode => childNode !== undefined, () => new PatchError("Path refers to non-existing object/array"))), rootNode));
486
485
  }
487
486
  function removeFromNode(document, node, key) {
488
- if (ts__default.isArrayLiteralExpression(node)) {
487
+ if (ts.isArrayLiteralExpression(node)) {
489
488
  return pipe(parseAndValidateArrayInboundsIndex(key, node.elements), result.map(index => removeAt(document, node.elements, index)));
490
- } else if (ts__default.isObjectLiteralExpression(node)) {
489
+ } else if (ts.isObjectLiteralExpression(node)) {
491
490
  return pipe(findObjectPropertyAssignment(node, key), result.flatMap(assignment => {
492
491
  if (!assignment) {
493
492
  return result.err(new PatchError("Cannot replace object element which does not exist"));
@@ -533,9 +532,9 @@ function isValRichTextValue(value) {
533
532
  return !!(typeof value === "object" && value && VAL_EXTENSION in value && value[VAL_EXTENSION] === "richtext" && "templateStrings" in value && typeof value.templateStrings === "object" && Array.isArray(value.templateStrings));
534
533
  }
535
534
  function addToNode(document, node, key, value) {
536
- if (ts__default.isArrayLiteralExpression(node)) {
535
+ if (ts.isArrayLiteralExpression(node)) {
537
536
  return pipe(parseAndValidateArrayInsertIndex(key, node.elements), result.map(index => [insertAt(document, node.elements, index, toExpression(value))]));
538
- } else if (ts__default.isObjectLiteralExpression(node)) {
537
+ } else if (ts.isObjectLiteralExpression(node)) {
539
538
  if (key === FILE_REF_PROP) {
540
539
  return result.err(new PatchError("Cannot add a key ref to object"));
541
540
  }
@@ -657,7 +656,7 @@ function convertDataUrlToBase64(dataUrl) {
657
656
  }
658
657
  const patchSourceFile = (sourceFile, patch) => {
659
658
  if (typeof sourceFile === "string") {
660
- return applyPatch(ts__default.createSourceFile("<val>", sourceFile, ts__default.ScriptTarget.ES2015), ops$1, patch);
659
+ return applyPatch(ts.createSourceFile("<val>", sourceFile, ts.ScriptTarget.ES2015), ops$1, patch);
661
660
  }
662
661
  return applyPatch(sourceFile, ops$1, patch);
663
662
  };
@@ -773,7 +772,7 @@ const getCompilerOptions = (rootDir, parseConfigHost) => {
773
772
  const {
774
773
  config,
775
774
  error
776
- } = ts__default.readConfigFile(configFilePath, parseConfigHost.readFile.bind(parseConfigHost));
775
+ } = ts.readConfigFile(configFilePath, parseConfigHost.readFile.bind(parseConfigHost));
777
776
  if (error) {
778
777
  if (typeof error.messageText === "string") {
779
778
  throw Error(`Could not parse config file: ${configFilePath}. Error: ${error.messageText}`);
@@ -781,7 +780,7 @@ const getCompilerOptions = (rootDir, parseConfigHost) => {
781
780
  throw Error(`Could not parse config file: ${configFilePath}. Error: ${error.messageText.messageText}`);
782
781
  }
783
782
  const optionsOverrides = undefined;
784
- const parsedConfigFile = ts__default.parseJsonConfigFileContent(config, parseConfigHost, rootDir, optionsOverrides, configFilePath);
783
+ const parsedConfigFile = ts.parseJsonConfigFileContent(config, parseConfigHost, rootDir, optionsOverrides, configFilePath);
785
784
  if (parsedConfigFile.errors.length > 0) {
786
785
  throw Error(`Could not parse config file: ${configFilePath}. Errors: ${parsedConfigFile.errors.map(e => e.messageText).join("\n")}`);
787
786
  }
@@ -790,7 +789,7 @@ const getCompilerOptions = (rootDir, parseConfigHost) => {
790
789
 
791
790
  class ValSourceFileHandler {
792
791
  constructor(projectRoot, compilerOptions, host = {
793
- ...ts__default.sys,
792
+ ...ts.sys,
794
793
  writeFile: (fileName, data, encoding) => {
795
794
  fs.mkdirSync(path__default.dirname(fileName), {
796
795
  recursive: true
@@ -805,9 +804,9 @@ class ValSourceFileHandler {
805
804
  }
806
805
  getSourceFile(filePath) {
807
806
  const fileContent = this.host.readFile(filePath);
808
- const scriptTarget = this.compilerOptions.target ?? ts__default.ScriptTarget.ES2020;
807
+ const scriptTarget = this.compilerOptions.target ?? ts.ScriptTarget.ES2020;
809
808
  if (fileContent) {
810
- return ts__default.createSourceFile(filePath, fileContent, scriptTarget);
809
+ return ts.createSourceFile(filePath, fileContent, scriptTarget);
811
810
  }
812
811
  }
813
812
  writeSourceFile(sourceFile) {
@@ -819,7 +818,7 @@ class ValSourceFileHandler {
819
818
  this.host.writeFile(filePath, content, encoding);
820
819
  }
821
820
  resolveSourceModulePath(containingFilePath, requestedModuleName) {
822
- const resolutionRes = ts__default.resolveModuleName(requestedModuleName, path__default.isAbsolute(containingFilePath) ? containingFilePath : path__default.resolve(this.projectRoot, containingFilePath), this.compilerOptions, this.host, undefined, undefined, ts__default.ModuleKind.ESNext);
821
+ const resolutionRes = ts.resolveModuleName(requestedModuleName, path__default.isAbsolute(containingFilePath) ? containingFilePath : path__default.resolve(this.projectRoot, containingFilePath), this.compilerOptions, this.host, undefined, undefined, ts.ModuleKind.ESNext);
823
822
  const resolvedModule = resolutionRes.resolvedModule;
824
823
  if (!resolvedModule) {
825
824
  throw Error(`Could not resolve module "${requestedModuleName}", base: "${containingFilePath}": No resolved modules returned: ${JSON.stringify(resolutionRes)}`);
@@ -840,7 +839,7 @@ class ValModuleLoader {
840
839
  compilerOptions,
841
840
  // TODO: remove this?
842
841
  sourceFileHandler, host = {
843
- ...ts__default.sys,
842
+ ...ts.sys,
844
843
  writeFile: (fileName, data, encoding) => {
845
844
  fs.mkdirSync(path__default.dirname(fileName), {
846
845
  recursive: true
@@ -1100,7 +1099,7 @@ export const IS_DEV = false;2
1100
1099
  }
1101
1100
 
1102
1101
  async function createService(projectRoot, opts, host = {
1103
- ...ts__default.sys,
1102
+ ...ts.sys,
1104
1103
  writeFile: (fileName, data, encoding) => {
1105
1104
  fs.mkdirSync(path__default.dirname(fileName), {
1106
1105
  recursive: true
@@ -1752,7 +1751,11 @@ class ValServer {
1752
1751
  }
1753
1752
  let modules;
1754
1753
  if (commit) {
1755
- modules = await this.execCommit(patches, cookies);
1754
+ const commitRes = await this.execCommit(patches, cookies);
1755
+ if (commitRes.status !== 200) {
1756
+ return commitRes;
1757
+ }
1758
+ modules = commitRes.json;
1756
1759
  } else {
1757
1760
  modules = await this.getPatchedModules(patches);
1758
1761
  }
@@ -1950,6 +1953,9 @@ function guessMimeTypeFromPath(filePath) {
1950
1953
  }
1951
1954
  return null;
1952
1955
  }
1956
+ function isCachedPatchFileOp(op) {
1957
+ return !!(op.op === "file" && typeof op.filePath === "string" && op.value && typeof op.value === "object" && !Array.isArray(op.value) && "sha256" in op.value && typeof op.value.sha256 === "string");
1958
+ }
1953
1959
 
1954
1960
  const textEncoder = new TextEncoder();
1955
1961
  class LocalValServer extends ValServer {
@@ -2218,7 +2224,10 @@ class LocalValServer extends ValServer {
2218
2224
  this.host.rmFile(this.getPatchFilePath(patchId));
2219
2225
  await this.options.service.patch(moduleId, patch);
2220
2226
  }
2221
- return this.getPatchedModules(patches);
2227
+ return {
2228
+ status: 200,
2229
+ json: await this.getPatchedModules(patches)
2230
+ };
2222
2231
  }
2223
2232
 
2224
2233
  /* Bad requests on Local Server: */
@@ -2233,9 +2242,6 @@ class LocalValServer extends ValServer {
2233
2242
  return this.badRequest();
2234
2243
  }
2235
2244
  }
2236
- function isCachedPatchFileOp(op) {
2237
- return !!(op.op === "file" && typeof op.filePath === "string" && op.value && typeof op.value === "object" && !Array.isArray(op.value) && "sha256" in op.value && typeof op.value.sha256 === "string");
2238
- }
2239
2245
 
2240
2246
  function decodeJwt(token, secretKey) {
2241
2247
  const [headerBase64, payloadBase64, signatureBase64, ...rest] = token.split(".");
@@ -2313,8 +2319,12 @@ class RemoteFS {
2313
2319
  }
2314
2320
  async getPendingOperations() {
2315
2321
  const modified = {};
2316
- for (const modifiedFile in this.modifiedFiles) {
2317
- modified[modifiedFile] = this.data[modifiedFile].utf8Files[modifiedFile];
2322
+ for (const modifiedFile of this.modifiedFiles) {
2323
+ const {
2324
+ directory,
2325
+ filename
2326
+ } = RemoteFS.parsePath(modifiedFile);
2327
+ modified[modifiedFile] = this.data[directory].utf8Files[filename];
2318
2328
  }
2319
2329
  return {
2320
2330
  modified: modified,
@@ -2510,28 +2520,47 @@ class ProxyValServer extends ValServer {
2510
2520
  }
2511
2521
  };
2512
2522
  }
2513
- const params = `commit=${encodeURIComponent(commit)}`;
2523
+ const params = `commit=${encodeURIComponent(commit)}&root=${encodeURIComponent(this.apiOptions.root || "/")}&cwd=${encodeURIComponent(this.cwd)}`;
2514
2524
  const url = new URL(`/v1/commit/${this.options.valName}/heads/${this.options.git.branch}/~?${params}`, this.options.valContentUrl);
2515
2525
 
2516
- // Creates a fresh copy of the fs. We cannot touch the existing fs, since we might still want to do
2517
- // other operations on it.
2526
+ // Creates a fresh copy of the fs. We cannot touch the existing fs, since there might be parallel operations?
2518
2527
  // 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.
2519
- // It is a concern we have, since we might be using quite a lot of memory when having the whole FS in memory. In particular because of images / files.
2528
+ // It is a concern we have, since we might be using quite a lot of memory when having the whole FS in memory.
2529
+ // 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.
2530
+ // 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.
2520
2531
  const remoteFS = new RemoteFS();
2521
2532
  const initRes = await this.initRemoteFS(commit, remoteFS, token);
2522
2533
  if (initRes.status !== 200) {
2523
2534
  return initRes;
2524
2535
  }
2525
2536
  const service = await createService(this.cwd, this.apiOptions, remoteFS);
2526
- for (const [, moduleId, patch] of patches) {
2527
- await service.patch(moduleId, patch);
2537
+ // TODO: optimize patches, e.g. only take the last replace for a given thing, etc...
2538
+ const patchIds = [];
2539
+ const binaryFileUpdates = {};
2540
+ for (const [patchId, moduleId, patch] of patches) {
2541
+ const patchableOps = [];
2542
+ for (const op of patch) {
2543
+ if (isCachedPatchFileOp(op)) {
2544
+ binaryFileUpdates[op.filePath] = op.value;
2545
+ } else {
2546
+ if (Internal.isFileOp(op)) {
2547
+ throw new Error(`Val: Unexpected file operation (file: ${op.filePath}). This is likely a Val bug.`);
2548
+ }
2549
+ patchableOps.push(op);
2550
+ }
2551
+ }
2552
+ await service.patch(moduleId, patchableOps);
2553
+ patchIds.push(patchId);
2528
2554
  }
2529
- const fileOps = await remoteFS.getPendingOperations();
2555
+ const sourceFileUpdates = await remoteFS.getPendingOperations();
2530
2556
  const fetchRes = await fetch(url, {
2531
2557
  method: "POST",
2532
2558
  headers: getAuthHeaders(token, "application/json"),
2533
2559
  body: JSON.stringify({
2534
- fileOps
2560
+ sourceFileUpdates: sourceFileUpdates.modified,
2561
+ binaryFileUpdates,
2562
+ deletedFiles: sourceFileUpdates.deleted,
2563
+ patchIds
2535
2564
  })
2536
2565
  });
2537
2566
  if (fetchRes.status === 200) {
@@ -2540,13 +2569,7 @@ class ProxyValServer extends ValServer {
2540
2569
  json: await fetchRes.json()
2541
2570
  };
2542
2571
  } else {
2543
- console.error("Failed to get patches", fetchRes.status, await fetchRes.text());
2544
- return {
2545
- status: fetchRes.status,
2546
- json: {
2547
- message: "Failed to get patches"
2548
- }
2549
- };
2572
+ return createJsonError(fetchRes);
2550
2573
  }
2551
2574
  });
2552
2575
  }
@@ -2566,32 +2589,42 @@ class ProxyValServer extends ValServer {
2566
2589
  });
2567
2590
  if (fetchRes.status === 200) {
2568
2591
  const json = await fetchRes.json();
2569
- remoteFS.initializeWith(json);
2592
+ let error = false;
2593
+ if (typeof json !== "object") {
2594
+ error = {
2595
+ details: "Invalid response: not an object"
2596
+ };
2597
+ }
2598
+ if (typeof json.git !== "object") {
2599
+ error = {
2600
+ details: "Invalid response: missing git"
2601
+ };
2602
+ }
2603
+ if (typeof json.git.commit !== "string") {
2604
+ error = {
2605
+ details: "Invalid response: missing git.commit"
2606
+ };
2607
+ }
2608
+ if (typeof json.directories !== "object" || json.directories === null) {
2609
+ error = {
2610
+ details: "Invalid response: missing directories"
2611
+ };
2612
+ }
2613
+ if (error) {
2614
+ return {
2615
+ status: 500,
2616
+ json: {
2617
+ message: "Failed to fetch remote files",
2618
+ ...error
2619
+ }
2620
+ };
2621
+ }
2622
+ remoteFS.initializeWith(json.directories);
2570
2623
  return {
2571
2624
  status: 200
2572
2625
  };
2573
2626
  } else {
2574
- try {
2575
- var _fetchRes$headers$get;
2576
- if ((_fetchRes$headers$get = fetchRes.headers.get("Content-Type")) !== null && _fetchRes$headers$get !== void 0 && _fetchRes$headers$get.includes("application/json")) {
2577
- const json = await fetchRes.json();
2578
- return {
2579
- status: fetchRes.status,
2580
- json: {
2581
- message: "Failed to fetch remote files",
2582
- details: json
2583
- }
2584
- };
2585
- }
2586
- } catch (err) {
2587
- console.error(err);
2588
- }
2589
- return {
2590
- status: fetchRes.status,
2591
- json: {
2592
- message: "Unknown failure while fetching remote files"
2593
- }
2594
- };
2627
+ return createJsonError(fetchRes);
2595
2628
  }
2596
2629
  } catch (err) {
2597
2630
  return {
@@ -2800,11 +2833,13 @@ class ProxyValServer extends ValServer {
2800
2833
  token
2801
2834
  }) => {
2802
2835
  const patchIds = query.id || [];
2803
- const params = `${patchIds.map(id => `id=${encodeURIComponent(id)}`).join("&")}`;
2804
- const url = new URL(`/v1/patches/${this.options.valName}/heads/${this.options.git.branch}/~?${params}`, this.options.valContentUrl);
2836
+ const url = new URL(`/v1/patches/${this.options.valName}/heads/${this.options.git.branch}/~`, this.options.valContentUrl);
2805
2837
  const fetchRes = await fetch(url, {
2806
- method: "GET",
2807
- headers: getAuthHeaders(token, "application/json")
2838
+ method: "DELETE",
2839
+ headers: getAuthHeaders(token, "application/json"),
2840
+ body: JSON.stringify({
2841
+ patches: patchIds
2842
+ })
2808
2843
  });
2809
2844
  if (fetchRes.status === 200) {
2810
2845
  return {
@@ -2812,13 +2847,7 @@ class ProxyValServer extends ValServer {
2812
2847
  json: await fetchRes.json()
2813
2848
  };
2814
2849
  } else {
2815
- console.error("Failed to delete patches", fetchRes.status, await fetchRes.text());
2816
- return {
2817
- status: fetchRes.status,
2818
- json: {
2819
- message: "Failed to delete patches"
2820
- }
2821
- };
2850
+ return createJsonError(fetchRes);
2822
2851
  }
2823
2852
  });
2824
2853
  }
@@ -2849,13 +2878,7 @@ class ProxyValServer extends ValServer {
2849
2878
  json: await fetchRes.json()
2850
2879
  };
2851
2880
  } else {
2852
- console.error("Failed to get patches", fetchRes.status, await fetchRes.text());
2853
- return {
2854
- status: fetchRes.status,
2855
- json: {
2856
- message: "Failed to get patches"
2857
- }
2858
- };
2881
+ return createJsonError(fetchRes);
2859
2882
  }
2860
2883
  });
2861
2884
  }
@@ -2900,9 +2923,7 @@ class ProxyValServer extends ValServer {
2900
2923
  json: await fetchRes.json()
2901
2924
  };
2902
2925
  } else {
2903
- return {
2904
- status: fetchRes.status
2905
- };
2926
+ return createJsonError(fetchRes);
2906
2927
  }
2907
2928
  });
2908
2929
  }
@@ -3063,6 +3084,22 @@ function getStateFromCookie(stateCookie) {
3063
3084
  };
3064
3085
  }
3065
3086
  }
3087
+ async function createJsonError(fetchRes) {
3088
+ var _fetchRes$headers$get;
3089
+ if ((_fetchRes$headers$get = fetchRes.headers.get("Content-Type")) !== null && _fetchRes$headers$get !== void 0 && _fetchRes$headers$get.includes("application/json")) {
3090
+ return {
3091
+ status: fetchRes.status,
3092
+ json: await fetchRes.json()
3093
+ };
3094
+ }
3095
+ console.error("Unexpected failure (did not get a json) - Val down?", fetchRes.status, await fetchRes.text());
3096
+ return {
3097
+ status: fetchRes.status,
3098
+ json: {
3099
+ message: "Unexpected failure (did not get a json) - Val down?"
3100
+ }
3101
+ };
3102
+ }
3066
3103
  function createStateCookie(state) {
3067
3104
  return Buffer.from(JSON.stringify(state), "utf8").toString("base64");
3068
3105
  }