@valbuild/server 0.146.2 → 0.147.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 (37) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/browser/dist/valbuild-server-browser.cjs.d.ts +2 -0
  3. package/browser/dist/valbuild-server-browser.cjs.dev.js +146 -0
  4. package/browser/dist/valbuild-server-browser.cjs.js +7 -0
  5. package/browser/dist/valbuild-server-browser.cjs.prod.js +146 -0
  6. package/browser/dist/valbuild-server-browser.esm.js +142 -0
  7. package/browser/package.json +4 -0
  8. package/dist/ValRouterCore-3fd3d444.cjs.dev.js +9897 -0
  9. package/dist/ValRouterCore-7ffcc08c.cjs.prod.js +9897 -0
  10. package/dist/ValRouterCore-f5004f8f.esm.js +9866 -0
  11. package/dist/declarations/src/ValOps.d.ts +13 -1
  12. package/dist/declarations/src/ValOpsFS.d.ts +1 -0
  13. package/dist/declarations/src/ValOpsHttp.d.ts +11 -0
  14. package/dist/declarations/src/ValOpsMemory.d.ts +9 -0
  15. package/dist/declarations/src/ValRouter.d.ts +1 -1
  16. package/dist/declarations/src/ValServer.d.ts +9 -2
  17. package/dist/declarations/src/browser/inTabFetch.d.ts +27 -0
  18. package/dist/declarations/src/browser/index.d.ts +9 -0
  19. package/dist/declarations/src/fixHandlers.d.ts +1 -1
  20. package/dist/declarations/src/linkCheck/probeUrl.d.ts +40 -0
  21. package/dist/declarations/src/platform.d.ts +39 -0
  22. package/dist/getJsonEntryAtCommit-1e08d724.esm.js +136 -0
  23. package/dist/getJsonEntryAtCommit-df65d861.cjs.prod.js +143 -0
  24. package/dist/getJsonEntryAtCommit-e96beb9e.cjs.dev.js +143 -0
  25. package/dist/jsonValuesModule-427d68c8.esm.js +273 -0
  26. package/dist/jsonValuesModule-8ae503a8.cjs.dev.js +288 -0
  27. package/dist/jsonValuesModule-d1bb8d8f.cjs.prod.js +288 -0
  28. package/dist/ops-0d05feaf.esm.js +353 -0
  29. package/dist/ops-b15239ac.cjs.prod.js +361 -0
  30. package/dist/ops-f37d18c6.cjs.dev.js +361 -0
  31. package/dist/publishKit-580f558a.cjs.prod.js +41 -0
  32. package/dist/publishKit-5865095c.cjs.dev.js +41 -0
  33. package/dist/publishKit-bc8df53d.esm.js +31 -0
  34. package/dist/valbuild-server.cjs.dev.js +7795 -18093
  35. package/dist/valbuild-server.cjs.prod.js +7795 -18093
  36. package/dist/valbuild-server.esm.js +7988 -18285
  37. package/package.json +18 -5
@@ -0,0 +1,353 @@
1
+ import ts from 'typescript';
2
+ import { pipe, result } from '@valbuild/core/fp';
3
+ import { a as analyzeValModule, e as findObjectPropertyAssignment, s as shallowValidateExpression, g as evaluateExpression, v as validateInitializers } from './jsonValuesModule-427d68c8.esm.js';
4
+ import { PatchError, deepEqual, parseAndValidateArrayIndex, isNotRoot } from '@valbuild/core/patch';
5
+
6
+ function isValidIdentifier(text) {
7
+ if (text.length === 0) {
8
+ return false;
9
+ }
10
+ if (!ts.isIdentifierStart(text.charCodeAt(0), ts.ScriptTarget.ES2020)) {
11
+ return false;
12
+ }
13
+ for (let i = 1; i < text.length; ++i) {
14
+ if (!ts.isIdentifierPart(text.charCodeAt(i), ts.ScriptTarget.ES2020)) {
15
+ return false;
16
+ }
17
+ }
18
+ return true;
19
+ }
20
+ function createPropertyAssignment(key, value) {
21
+ return ts.factory.createPropertyAssignment(isValidIdentifier(key) ? ts.factory.createIdentifier(key) : ts.factory.createStringLiteral(key), toExpression(value));
22
+ }
23
+
24
+ /**
25
+ * Builds the expression `c.json(() => import("<importPath>"))` used to reference
26
+ * a lazily-loaded `*.val.json` entry of a `.jsonValues()` record.
27
+ */
28
+ function createValJsonReference(importPath) {
29
+ // () => import("<importPath>")
30
+ // NOTE: an `import` identifier prints as the dynamic-import keyword call,
31
+ // which avoids casting the ImportKeyword token (not typed as an Expression).
32
+ const importCall = ts.factory.createCallExpression(ts.factory.createIdentifier("import"), undefined, [ts.factory.createStringLiteral(importPath)]);
33
+ const thunk = ts.factory.createArrowFunction(undefined, undefined, [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), importCall);
34
+ // c.json(<thunk>)
35
+ return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier("c"), ts.factory.createIdentifier("json")), undefined, [thunk]);
36
+ }
37
+
38
+ /**
39
+ * Inserts a new `.jsonValues()` entry `"<key>": c.json(() => import("<importPath>"))`
40
+ * into the record's object literal in a `.val.ts` module. `recordPath` is the
41
+ * path from the module source to the record (empty for a root record/router).
42
+ * Fails if the entry key already exists.
43
+ */
44
+ function insertValJsonEntry(document, recordPath, key, importPath) {
45
+ return pipe(analyzeValModule(document), result.flatMap(({
46
+ source
47
+ }) => getAtPath(source, recordPath)), result.flatMap(recordNode => {
48
+ if (!ts.isObjectLiteralExpression(recordNode)) {
49
+ return result.err(new PatchError("Cannot add jsonValues entry: record source is not an object literal"));
50
+ }
51
+ return pipe(findObjectPropertyAssignment(recordNode, key), result.flatMap(assignment => {
52
+ if (assignment) {
53
+ return result.err(new PatchError(`Cannot add jsonValues entry '${key}': it already exists`));
54
+ }
55
+ const property = ts.factory.createPropertyAssignment(isValidIdentifier(key) ? ts.factory.createIdentifier(key) : ts.factory.createStringLiteral(key), createValJsonReference(importPath));
56
+ return result.ok(insertAt(document, recordNode.properties, recordNode.properties.length, property));
57
+ }));
58
+ }));
59
+ }
60
+
61
+ /**
62
+ * Removes the `.jsonValues()` entry `"<key>"` from the record's object literal
63
+ * in a `.val.ts` module. `recordPath` is the path from the module source to the
64
+ * record (empty for a root record/router). Fails if the entry key is missing.
65
+ */
66
+ function removeValJsonEntry(document, recordPath, key) {
67
+ return pipe(analyzeValModule(document), result.flatMap(({
68
+ source
69
+ }) => getAtPath(source, recordPath)), result.flatMap(recordNode => {
70
+ if (!ts.isObjectLiteralExpression(recordNode)) {
71
+ return result.err(new PatchError("Cannot remove jsonValues entry: record source is not an object literal"));
72
+ }
73
+ return pipe(findObjectPropertyAssignment(recordNode, key), result.flatMap(assignment => {
74
+ if (!assignment) {
75
+ return result.err(new PatchError(`Cannot remove jsonValues entry '${key}': it does not exist`));
76
+ }
77
+ const [doc] = removeAt(document, recordNode.properties, recordNode.properties.indexOf(assignment));
78
+ return result.ok(doc);
79
+ }));
80
+ }));
81
+ }
82
+ function toExpression(value) {
83
+ if (typeof value === "string") {
84
+ // TODO: Use configuration/heuristics to determine use of single quote or double quote
85
+ return ts.factory.createStringLiteral(value);
86
+ } else if (typeof value === "number") {
87
+ // TODO: do we want to do something like this for very large numbers?
88
+ // if (value > Number.MAX_SAFE_INTEGER) {
89
+ // return ts.factory.createBigIntLiteral(`${value}n`);
90
+ // }
91
+ // if (value < Number.MIN_SAFE_INTEGER) {
92
+ // return ts.factory.createPrefixUnaryExpression(
93
+ // ts.SyntaxKind.MinusToken,
94
+ // ts.factory.createBigIntLiteral(`${value}n`),
95
+ // );
96
+ // }
97
+ if (value < 0) {
98
+ return ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, ts.factory.createNumericLiteral(Math.abs(value)));
99
+ }
100
+ return ts.factory.createNumericLiteral(value);
101
+ } else if (typeof value === "boolean") {
102
+ return value ? ts.factory.createTrue() : ts.factory.createFalse();
103
+ } else if (value === null) {
104
+ return ts.factory.createNull();
105
+ } else if (Array.isArray(value)) {
106
+ return ts.factory.createArrayLiteralExpression(value.map(toExpression));
107
+ } else if (typeof value === "object") {
108
+ return ts.factory.createObjectLiteralExpression(Object.entries(value)
109
+ // `patch_id` is injected server-side onto a media source whose bytes are
110
+ // not committed yet. It describes the draft, not the content, and a
111
+ // whole-object write built from the client's optimistic view would
112
+ // otherwise print it into the user's file.
113
+ .filter(([key]) => key !== "patch_id").map(([key, value]) => createPropertyAssignment(key, value)));
114
+ } else {
115
+ return ts.factory.createStringLiteral(value);
116
+ }
117
+ }
118
+
119
+ // TODO: Choose newline based on project settings/heuristics/system default?
120
+ const newLine = ts.NewLineKind.LineFeed;
121
+ // TODO: Handle indentation of printed code
122
+ const printer = ts.createPrinter({
123
+ newLine: newLine
124
+ // neverAsciiEscape: true,
125
+ });
126
+ function replaceNodeValue(document, node, value) {
127
+ const replacementText = printer.printNode(ts.EmitHint.Unspecified, toExpression(value), document);
128
+ const span = ts.createTextSpanFromBounds(node.getStart(document, false), node.end);
129
+ const newText = `${document.text.substring(0, span.start)}${replacementText}${document.text.substring(ts.textSpanEnd(span))}`;
130
+ return [document.update(newText, ts.createTextChangeRange(span, replacementText.length)), node];
131
+ }
132
+ function isIndentation(s) {
133
+ for (let i = 0; i < s.length; ++i) {
134
+ const c = s.charAt(i);
135
+ if (c !== " " && c !== "\t") {
136
+ return false;
137
+ }
138
+ }
139
+ return true;
140
+ }
141
+ function newLineStr(kind) {
142
+ if (kind === ts.NewLineKind.CarriageReturnLineFeed) {
143
+ return "\r\n";
144
+ } else {
145
+ return "\n";
146
+ }
147
+ }
148
+ function getSeparator(document, neighbor) {
149
+ const startPos = neighbor.getStart(document, true);
150
+ const basis = document.getLineAndCharacterOfPosition(startPos);
151
+ const lineStartPos = document.getPositionOfLineAndCharacter(basis.line, 0);
152
+ const maybeIndentation = document.getText().substring(lineStartPos, startPos);
153
+ if (isIndentation(maybeIndentation)) {
154
+ return `,${newLineStr(newLine)}${maybeIndentation}`;
155
+ } else {
156
+ return `, `;
157
+ }
158
+ }
159
+ function insertAt(document, nodes, index, node) {
160
+ let span;
161
+ let replacementText;
162
+ if (nodes.length === 0) {
163
+ // Replace entire range of nodes
164
+ replacementText = printer.printNode(ts.EmitHint.Unspecified, node, document);
165
+ span = ts.createTextSpanFromBounds(nodes.pos, nodes.end);
166
+ } else if (index === nodes.length) {
167
+ // Insert after last node
168
+ const neighbor = nodes[nodes.length - 1];
169
+ replacementText = `${getSeparator(document, neighbor)}${printer.printNode(ts.EmitHint.Unspecified, node, document)}`;
170
+ span = ts.createTextSpan(neighbor.end, 0);
171
+ } else {
172
+ // Insert before node
173
+ const neighbor = nodes[index];
174
+ replacementText = `${printer.printNode(ts.EmitHint.Unspecified, node, document)}${getSeparator(document, neighbor)}`;
175
+ span = ts.createTextSpan(neighbor.getStart(document, true), 0);
176
+ }
177
+ const newText = `${document.text.substring(0, span.start)}${replacementText}${document.text.substring(ts.textSpanEnd(span))}`;
178
+ return document.update(newText, ts.createTextChangeRange(span, replacementText.length));
179
+ }
180
+ function removeAt(document, nodes, index) {
181
+ const node = nodes[index];
182
+ let span;
183
+ if (nodes.length === 1) {
184
+ span = ts.createTextSpanFromBounds(nodes.pos, nodes.end);
185
+ } else if (index === nodes.length - 1) {
186
+ // Remove until previous node
187
+ const neighbor = nodes[index - 1];
188
+ span = ts.createTextSpanFromBounds(neighbor.end, node.end);
189
+ } else {
190
+ // Remove before next node
191
+ const neighbor = nodes[index + 1];
192
+ span = ts.createTextSpanFromBounds(node.getStart(document, true), neighbor.getStart(document, true));
193
+ }
194
+ const newText = `${document.text.substring(0, span.start)}${document.text.substring(ts.textSpanEnd(span))}`;
195
+ return [document.update(newText, ts.createTextChangeRange(span, 0)), node];
196
+ }
197
+ function parseAndValidateArrayInsertIndex(key, nodes) {
198
+ if (key === "-") {
199
+ // For insertion, all nodes up until the insertion index must be valid
200
+ // initializers
201
+ const err = validateInitializers(nodes);
202
+ if (err) {
203
+ return result.err(err);
204
+ }
205
+ return result.ok(nodes.length);
206
+ }
207
+ return pipe(parseAndValidateArrayIndex(key), result.flatMap(index => {
208
+ // For insertion, all nodes up until the insertion index must be valid
209
+ // initializers
210
+ const err = validateInitializers(nodes.slice(0, index));
211
+ if (err) {
212
+ return result.err(err);
213
+ }
214
+ if (index > nodes.length) {
215
+ return result.err(new PatchError("Array index out of bounds"));
216
+ } else {
217
+ return result.ok(index);
218
+ }
219
+ }));
220
+ }
221
+ function parseAndValidateArrayInboundsIndex(key, nodes) {
222
+ return pipe(parseAndValidateArrayIndex(key), result.flatMap(index => {
223
+ // For in-bounds operations, all nodes up until and including the index
224
+ // must be valid initializers
225
+ const err = validateInitializers(nodes.slice(0, index + 1));
226
+ if (err) {
227
+ return result.err(err);
228
+ }
229
+ if (index >= nodes.length) {
230
+ return result.err(new PatchError("Array index out of bounds"));
231
+ } else {
232
+ return result.ok(index);
233
+ }
234
+ }));
235
+ }
236
+ function replaceInNode(document, node, key, value) {
237
+ if (ts.isArrayLiteralExpression(node)) {
238
+ return pipe(parseAndValidateArrayInboundsIndex(key, node.elements), result.map(index => replaceNodeValue(document, node.elements[index], value)));
239
+ } else if (ts.isObjectLiteralExpression(node)) {
240
+ return pipe(findObjectPropertyAssignment(node, key), result.flatMap(assignment => {
241
+ if (!assignment) {
242
+ return result.err(new PatchError("Cannot replace object element which does not exist"));
243
+ }
244
+ return result.ok(assignment);
245
+ }), result.map(assignment => replaceNodeValue(document, assignment.initializer, value)));
246
+ } else {
247
+ return result.err(shallowValidateExpression(node) ?? new PatchError("Cannot replace in non-object/array"));
248
+ }
249
+ }
250
+ function replaceAtPath(document, rootNode, path, value) {
251
+ if (isNotRoot(path)) {
252
+ return pipe(getPointerFromPath(rootNode, path), result.flatMap(([node, key]) => replaceInNode(document, node, key, value)));
253
+ } else {
254
+ return result.ok(replaceNodeValue(document, rootNode, value));
255
+ }
256
+ }
257
+ function getFromNode(node, key) {
258
+ if (ts.isArrayLiteralExpression(node)) {
259
+ return pipe(parseAndValidateArrayInboundsIndex(key, node.elements), result.map(index => node.elements[index]));
260
+ } else if (ts.isObjectLiteralExpression(node)) {
261
+ return pipe(findObjectPropertyAssignment(node, key), result.map(assignment => assignment === null || assignment === void 0 ? void 0 : assignment.initializer));
262
+ } else {
263
+ return result.err(shallowValidateExpression(node) ?? new PatchError("Cannot access non-object/array"));
264
+ }
265
+ }
266
+ function getPointerFromPath(node, path) {
267
+ let targetNode = node;
268
+ let key = path[0];
269
+ for (let i = 0; i < path.length - 1; ++i, key = path[i]) {
270
+ const childNode = getFromNode(targetNode, key);
271
+ if (result.isErr(childNode)) {
272
+ return childNode;
273
+ }
274
+ if (childNode.value === undefined) {
275
+ return result.err(new PatchError(`Path refers to non-existing object/array: ${path.join("/")}`));
276
+ }
277
+ targetNode = childNode.value;
278
+ }
279
+ return result.ok([targetNode, key]);
280
+ }
281
+ function getAtPath(rootNode, path) {
282
+ 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));
283
+ }
284
+ function removeFromNode(document, node, key) {
285
+ if (ts.isArrayLiteralExpression(node)) {
286
+ return pipe(parseAndValidateArrayInboundsIndex(key, node.elements), result.map(index => removeAt(document, node.elements, index)));
287
+ } else if (ts.isObjectLiteralExpression(node)) {
288
+ return pipe(findObjectPropertyAssignment(node, key), result.flatMap(assignment => {
289
+ if (!assignment) {
290
+ return result.err(new PatchError("Cannot remove object element which does not exist"));
291
+ }
292
+ return result.ok(assignment);
293
+ }), result.map(assignment => [removeAt(document, node.properties, node.properties.indexOf(assignment))[0], assignment.initializer]));
294
+ } else {
295
+ return result.err(shallowValidateExpression(node) ?? new PatchError("Cannot remove from non-object/array"));
296
+ }
297
+ }
298
+ function removeAtPath(document, rootNode, path) {
299
+ return pipe(getPointerFromPath(rootNode, path), result.flatMap(([node, key]) => removeFromNode(document, node, key)));
300
+ }
301
+ function addToNode(document, node, key, value) {
302
+ if (ts.isArrayLiteralExpression(node)) {
303
+ return pipe(parseAndValidateArrayInsertIndex(key, node.elements), result.map(index => [insertAt(document, node.elements, index, toExpression(value))]));
304
+ } else if (ts.isObjectLiteralExpression(node)) {
305
+ return pipe(findObjectPropertyAssignment(node, key), result.map(assignment => {
306
+ if (!assignment) {
307
+ return [insertAt(document, node.properties, node.properties.length, createPropertyAssignment(key, value))];
308
+ } else {
309
+ return replaceNodeValue(document, assignment.initializer, value);
310
+ }
311
+ }));
312
+ } else {
313
+ return result.err(shallowValidateExpression(node) ?? new PatchError("Cannot add to non-object/array"));
314
+ }
315
+ }
316
+ function addAtPath(document, rootNode, path, value) {
317
+ if (isNotRoot(path)) {
318
+ return pipe(getPointerFromPath(rootNode, path), result.flatMap(([node, key]) => addToNode(document, node, key, value)));
319
+ } else {
320
+ return result.ok(replaceNodeValue(document, rootNode, value));
321
+ }
322
+ }
323
+ function pickDocument([document]) {
324
+ return document;
325
+ }
326
+ class TSOps {
327
+ constructor(findRoot) {
328
+ this.findRoot = findRoot;
329
+ }
330
+ get(document, path) {
331
+ return pipe(document, this.findRoot, result.flatMap(rootNode => getAtPath(rootNode, path)), result.flatMap(evaluateExpression));
332
+ }
333
+ add(document, path, value) {
334
+ return pipe(document, this.findRoot, result.flatMap(rootNode => addAtPath(document, rootNode, path, value)), result.map(pickDocument));
335
+ }
336
+ remove(document, path) {
337
+ return pipe(document, this.findRoot, result.flatMap(rootNode => removeAtPath(document, rootNode, path)), result.map(pickDocument));
338
+ }
339
+ replace(document, path, value) {
340
+ return pipe(document, this.findRoot, result.flatMap(rootNode => replaceAtPath(document, rootNode, path, value)), result.map(pickDocument));
341
+ }
342
+ move(document, from, path) {
343
+ return pipe(document, this.findRoot, result.flatMap(rootNode => removeAtPath(document, rootNode, from)), result.flatMap(([document, removedNode]) => pipe(evaluateExpression(removedNode), result.map(removedValue => [document, removedValue]))), result.flatMap(([document, removedValue]) => pipe(document, this.findRoot, result.flatMap(root => addAtPath(document, root, path, removedValue)))), result.map(pickDocument));
344
+ }
345
+ copy(document, from, path) {
346
+ return pipe(document, this.findRoot, result.flatMap(rootNode => pipe(getAtPath(rootNode, from), result.flatMap(evaluateExpression), result.flatMap(value => addAtPath(document, rootNode, path, value)))), result.map(pickDocument));
347
+ }
348
+ test(document, path, value) {
349
+ return pipe(document, this.findRoot, result.flatMap(rootNode => getAtPath(rootNode, path)), result.flatMap(evaluateExpression), result.map(documentValue => deepEqual(value, documentValue)));
350
+ }
351
+ }
352
+
353
+ export { TSOps as T, insertValJsonEntry as i, removeValJsonEntry as r };