@silverbulletmd/silverbullet 2.4.2 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -4
- package/client/asset_bundle/bundle.ts +3 -9
- package/client/data/datastore.ts +4 -5
- package/client/markdown_parser/constants.ts +5 -4
- package/client/plugos/hooks/code_widget.ts +3 -8
- package/client/plugos/hooks/command.ts +8 -8
- package/client/plugos/hooks/document_editor.ts +10 -15
- package/client/plugos/hooks/event.ts +33 -36
- package/client/plugos/hooks/mq.ts +17 -17
- package/client/plugos/hooks/plug_namespace.ts +3 -8
- package/client/plugos/hooks/slash_command.ts +13 -28
- package/client/plugos/hooks/syscall.ts +3 -3
- package/client/plugos/manifest_cache.ts +22 -15
- package/client/plugos/plug.ts +2 -6
- package/client/plugos/plug_compile.ts +79 -78
- package/client/plugos/protocol.ts +28 -28
- package/client/plugos/proxy_fetch.ts +7 -6
- package/client/plugos/sandboxes/web_worker_sandbox.ts +1 -1
- package/client/plugos/sandboxes/worker_sandbox.ts +18 -18
- package/client/plugos/syscalls/asset.ts +1 -3
- package/client/plugos/syscalls/code_widget.ts +1 -3
- package/client/plugos/syscalls/config.ts +1 -5
- package/client/plugos/syscalls/datastore.ts +1 -1
- package/client/plugos/syscalls/editor.ts +72 -69
- package/client/plugos/syscalls/event.ts +9 -12
- package/client/plugos/syscalls/fetch.ts +31 -23
- package/client/plugos/syscalls/index.ts +10 -1
- package/client/plugos/syscalls/jsonschema.ts +72 -32
- package/client/plugos/syscalls/language.ts +9 -5
- package/client/plugos/syscalls/markdown.ts +29 -7
- package/client/plugos/syscalls/mq.ts +4 -12
- package/client/plugos/syscalls/service_registry.ts +1 -4
- package/client/plugos/syscalls/shell.ts +2 -5
- package/client/plugos/syscalls/space.ts +1 -1
- package/client/plugos/syscalls/sync.ts +69 -60
- package/client/plugos/syscalls/system.ts +2 -3
- package/client/plugos/system.ts +6 -12
- package/client/plugos/worker_runtime.ts +12 -33
- package/client/space_lua/aggregates.ts +782 -0
- package/client/space_lua/ast.ts +42 -8
- package/client/space_lua/ast_narrow.ts +4 -2
- package/client/space_lua/eval.ts +886 -575
- package/client/space_lua/labels.ts +7 -12
- package/client/space_lua/liq_null.ts +6 -0
- package/client/space_lua/numeric.ts +5 -8
- package/client/space_lua/parse.ts +346 -120
- package/client/space_lua/query_collection.ts +926 -82
- package/client/space_lua/query_env.ts +26 -0
- package/client/space_lua/render_lua_markdown.ts +369 -0
- package/client/space_lua/rp.ts +5 -4
- package/client/space_lua/runtime.ts +288 -155
- package/client/space_lua/stdlib/format.ts +53 -39
- package/client/space_lua/stdlib/js.ts +3 -7
- package/client/space_lua/stdlib/load.ts +1 -3
- package/client/space_lua/stdlib/math.ts +84 -58
- package/client/space_lua/stdlib/net.ts +27 -17
- package/client/space_lua/stdlib/os.ts +81 -85
- package/client/space_lua/stdlib/pattern.ts +695 -0
- package/client/space_lua/stdlib/prng.ts +148 -0
- package/client/space_lua/stdlib/space_lua.ts +17 -23
- package/client/space_lua/stdlib/string.ts +102 -190
- package/client/space_lua/stdlib/string_pack.ts +490 -0
- package/client/space_lua/stdlib/table.ts +76 -16
- package/client/space_lua/stdlib.ts +53 -39
- package/client/space_lua/tonumber.ts +82 -42
- package/client/space_lua/util.ts +53 -15
- package/dist/plug-compile.js +55 -98
- package/package.json +27 -20
- package/plug-api/constants.ts +0 -32
- package/plug-api/lib/async.ts +20 -7
- package/plug-api/lib/crypto.ts +16 -17
- package/plug-api/lib/dates.ts +15 -7
- package/plug-api/lib/json.ts +11 -5
- package/plug-api/lib/limited_map.ts +1 -1
- package/plug-api/lib/native_fetch.ts +2 -0
- package/plug-api/lib/ref.ts +23 -23
- package/plug-api/lib/resolve.ts +7 -11
- package/plug-api/lib/tags.ts +13 -4
- package/plug-api/lib/transclusion.ts +10 -21
- package/plug-api/lib/tree.ts +165 -45
- package/plug-api/lib/yaml.ts +35 -25
- package/plug-api/syscalls/asset.ts +1 -1
- package/plug-api/syscalls/config.ts +1 -4
- package/plug-api/syscalls/editor.ts +15 -15
- package/plug-api/syscalls/jsonschema.ts +1 -3
- package/plug-api/syscalls/lua.ts +3 -9
- package/plug-api/syscalls/mq.ts +1 -4
- package/plug-api/syscalls/shell.ts +4 -1
- package/plug-api/syscalls/space.ts +3 -10
- package/plug-api/syscalls/system.ts +1 -4
- package/plug-api/syscalls/yaml.ts +2 -6
- package/plug-api/system_mock.ts +0 -1
- package/plug-api/types/client.ts +16 -1
- package/plug-api/types/event.ts +6 -4
- package/plug-api/types/manifest.ts +8 -9
- package/plugs/builtin_plugs.ts +2 -2
- package/client/plugos/sandboxes/deno_worker_sandbox.ts +0 -6
|
@@ -18,10 +18,12 @@ import {
|
|
|
18
18
|
} from "@codemirror/commands";
|
|
19
19
|
import type { Transaction } from "@codemirror/state";
|
|
20
20
|
import { EditorView } from "@codemirror/view";
|
|
21
|
-
import {
|
|
21
|
+
import { getVimModule } from "../../vim_loader.ts";
|
|
22
22
|
import type { SysCallMapping } from "../system.ts";
|
|
23
23
|
import type {
|
|
24
24
|
FilterOption,
|
|
25
|
+
NotificationAction,
|
|
26
|
+
NotificationType,
|
|
25
27
|
UploadFile,
|
|
26
28
|
} from "@silverbulletmd/silverbullet/type/client";
|
|
27
29
|
import { openSearchPanel } from "@codemirror/search";
|
|
@@ -48,11 +50,11 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
48
50
|
return client.currentPath();
|
|
49
51
|
},
|
|
50
52
|
"editor.getCurrentEditor": (): string => {
|
|
51
|
-
return client.documentEditor?.name || "page";
|
|
53
|
+
return client.contentManager.documentEditor?.name || "page";
|
|
52
54
|
},
|
|
53
55
|
"editor.getRecentlyOpenedPages": (): PageMeta[] => {
|
|
54
|
-
return client.ui.viewState.allPages.sort(
|
|
55
|
-
(b.lastOpened || 0) - (a.lastOpened || 0)
|
|
56
|
+
return client.ui.viewState.allPages.sort(
|
|
57
|
+
(a, b) => (b.lastOpened || 0) - (a.lastOpened || 0),
|
|
56
58
|
);
|
|
57
59
|
},
|
|
58
60
|
"editor.getText": () => {
|
|
@@ -68,12 +70,14 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
68
70
|
const line = client.editorView.state.doc.lineAt(pos);
|
|
69
71
|
return {
|
|
70
72
|
...line,
|
|
71
|
-
textWithCursor:
|
|
73
|
+
textWithCursor:
|
|
74
|
+
line.text.slice(0, pos - line.from) +
|
|
75
|
+
"|^|" +
|
|
72
76
|
line.text.slice(pos - line.from),
|
|
73
77
|
};
|
|
74
78
|
},
|
|
75
79
|
"editor.setText": (_ctx, newText: string, shouldIsolateHistory = false) => {
|
|
76
|
-
client.setEditorText(newText, shouldIsolateHistory);
|
|
80
|
+
client.contentManager.setEditorText(newText, shouldIsolateHistory);
|
|
77
81
|
},
|
|
78
82
|
"editor.getCursor": (): number => {
|
|
79
83
|
return client.editorView.state.selection.main.from;
|
|
@@ -110,7 +114,7 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
110
114
|
}
|
|
111
115
|
|
|
112
116
|
if (
|
|
113
|
-
// @ts-
|
|
117
|
+
// @ts-expect-error: Legacy support
|
|
114
118
|
ref.page !== undefined
|
|
115
119
|
) {
|
|
116
120
|
console.warn(
|
|
@@ -127,7 +131,7 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
127
131
|
|
|
128
132
|
legacyRef.kind ??= "page";
|
|
129
133
|
|
|
130
|
-
let details: Ref["details"]
|
|
134
|
+
let details: Ref["details"];
|
|
131
135
|
|
|
132
136
|
if (typeof legacyRef.pos === "number") {
|
|
133
137
|
details = {
|
|
@@ -160,13 +164,9 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
160
164
|
// some validation library. Didn't want to use jsonschemas here tho.
|
|
161
165
|
// Ideally this would be moved into a function too
|
|
162
166
|
if (!isValidPath(ref.path) && ref.path !== "") {
|
|
163
|
-
throw new Error(
|
|
164
|
-
"Path passed in ref to `editor.navigate` is invalid",
|
|
165
|
-
);
|
|
167
|
+
throw new Error("Path passed in ref to `editor.navigate` is invalid");
|
|
166
168
|
} else if (typeof ref.meta !== "boolean" && ref.meta !== undefined) {
|
|
167
|
-
throw new Error(
|
|
168
|
-
"ref.meta has to be of type `boolean`",
|
|
169
|
-
);
|
|
169
|
+
throw new Error("ref.meta has to be of type `boolean`");
|
|
170
170
|
} else if (ref.details !== undefined && typeof ref.details !== "object") {
|
|
171
171
|
throw new Error(
|
|
172
172
|
"ref.details has to be of type `object` or `undefined`",
|
|
@@ -181,17 +181,15 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
if (
|
|
184
|
-
ref.details?.type === "position" &&
|
|
184
|
+
ref.details?.type === "position" &&
|
|
185
|
+
typeof ref.details.pos !== "number"
|
|
185
186
|
) {
|
|
186
|
-
throw new Error(
|
|
187
|
-
"ref.details.pos has to be of type `number`",
|
|
188
|
-
);
|
|
187
|
+
throw new Error("ref.details.pos has to be of type `number`");
|
|
189
188
|
} else if (
|
|
190
|
-
ref.details?.type === "header" &&
|
|
189
|
+
ref.details?.type === "header" &&
|
|
190
|
+
typeof ref.details.header !== "string"
|
|
191
191
|
) {
|
|
192
|
-
throw new Error(
|
|
193
|
-
"ref.details.header has to be of type `string`",
|
|
194
|
-
);
|
|
192
|
+
throw new Error("ref.details.header has to be of type `string`");
|
|
195
193
|
} else if (
|
|
196
194
|
ref.details?.type === "linecolumn" &&
|
|
197
195
|
typeof ref.details.line !== "number" &&
|
|
@@ -214,14 +212,12 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
214
212
|
client.rebuildEditorState();
|
|
215
213
|
},
|
|
216
214
|
"editor.reloadConfigAndCommands": async () => {
|
|
217
|
-
await client.clientSystem.system.localSyscall(
|
|
218
|
-
"system.loadScripts",
|
|
219
|
-
[],
|
|
220
|
-
);
|
|
215
|
+
await client.clientSystem.system.localSyscall("system.loadScripts", []);
|
|
221
216
|
await client.clientSystem.system.localSyscall(
|
|
222
217
|
"system.loadSpaceStyles",
|
|
223
218
|
[],
|
|
224
219
|
);
|
|
220
|
+
client.reconfigureLanguage();
|
|
225
221
|
},
|
|
226
222
|
"editor.invokeCommand": (_ctx, name: string, args?: string[]) => {
|
|
227
223
|
return client.runCommandByName(name, args);
|
|
@@ -232,7 +228,7 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
232
228
|
"editor.newWindow": () => {
|
|
233
229
|
globalThis.open(
|
|
234
230
|
location.href,
|
|
235
|
-
|
|
231
|
+
`rnd${Math.random()}`,
|
|
236
232
|
`width=${globalThis.innerWidth},heigh=${globalThis.innerHeight}`,
|
|
237
233
|
);
|
|
238
234
|
},
|
|
@@ -268,7 +264,7 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
268
264
|
const reader = new FileReader();
|
|
269
265
|
reader.readAsArrayBuffer(file);
|
|
270
266
|
reader.onloadend = async (evt) => {
|
|
271
|
-
if (evt.target?.readyState
|
|
267
|
+
if (evt.target?.readyState === FileReader.DONE) {
|
|
272
268
|
resolve({
|
|
273
269
|
name: file.name,
|
|
274
270
|
contentType: file.type,
|
|
@@ -288,15 +284,19 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
288
284
|
reject(e);
|
|
289
285
|
};
|
|
290
286
|
|
|
287
|
+
input.style.display = "none";
|
|
288
|
+
document.body.appendChild(input);
|
|
291
289
|
input.click();
|
|
290
|
+
setTimeout(() => document.body.removeChild(input), 1000);
|
|
292
291
|
});
|
|
293
292
|
},
|
|
294
293
|
"editor.flashNotification": (
|
|
295
294
|
_ctx,
|
|
296
295
|
message: string,
|
|
297
|
-
type:
|
|
296
|
+
type: NotificationType = "info",
|
|
297
|
+
options?: { timeout?: number; actions?: NotificationAction[] },
|
|
298
298
|
) => {
|
|
299
|
-
client.flashNotification(message, type);
|
|
299
|
+
client.ui.flashNotification(message, type, options);
|
|
300
300
|
},
|
|
301
301
|
"editor.filterBox": (
|
|
302
302
|
_ctx,
|
|
@@ -305,13 +305,13 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
305
305
|
helpText = "",
|
|
306
306
|
placeHolder = "",
|
|
307
307
|
): Promise<FilterOption | undefined> => {
|
|
308
|
-
return client.filterBox(label, options, helpText, placeHolder);
|
|
308
|
+
return client.ui.filterBox(label, options, helpText, placeHolder);
|
|
309
309
|
},
|
|
310
310
|
"editor.showPanel": (
|
|
311
311
|
_ctx,
|
|
312
312
|
id: string,
|
|
313
313
|
mode: number,
|
|
314
|
-
html: string,
|
|
314
|
+
html: HTMLElement | HTMLElement[] | string,
|
|
315
315
|
script: string,
|
|
316
316
|
) => {
|
|
317
317
|
client.ui.viewDispatch({
|
|
@@ -339,7 +339,7 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
339
339
|
progressPercentage?: number,
|
|
340
340
|
progressType?: "sync" | "index",
|
|
341
341
|
) => {
|
|
342
|
-
client.showProgress(progressPercentage, progressType);
|
|
342
|
+
client.ui.showProgress(progressPercentage, progressType);
|
|
343
343
|
},
|
|
344
344
|
"editor.insertAtPos": (
|
|
345
345
|
_ctx,
|
|
@@ -351,7 +351,8 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
351
351
|
if (cursorPlaceHolder) {
|
|
352
352
|
cursorPlaceholderPos = text.indexOf("|^|");
|
|
353
353
|
if (cursorPlaceholderPos !== -1) {
|
|
354
|
-
text =
|
|
354
|
+
text =
|
|
355
|
+
text.slice(0, cursorPlaceholderPos) +
|
|
355
356
|
text.slice(cursorPlaceholderPos + 3);
|
|
356
357
|
} else {
|
|
357
358
|
cursorPlaceHolder = false;
|
|
@@ -369,9 +370,7 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
369
370
|
selection: {
|
|
370
371
|
anchor: cursorPos,
|
|
371
372
|
},
|
|
372
|
-
effects: [
|
|
373
|
-
EditorView.scrollIntoView(cursorPos),
|
|
374
|
-
],
|
|
373
|
+
effects: [EditorView.scrollIntoView(cursorPos)],
|
|
375
374
|
});
|
|
376
375
|
}
|
|
377
376
|
},
|
|
@@ -385,7 +384,8 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
385
384
|
let cursorPlaceholderPos = -1;
|
|
386
385
|
if (cursorPlaceHolder) {
|
|
387
386
|
cursorPlaceholderPos = text.indexOf("|^|");
|
|
388
|
-
text =
|
|
387
|
+
text =
|
|
388
|
+
text.slice(0, cursorPlaceholderPos) +
|
|
389
389
|
text.slice(cursorPlaceholderPos + 3);
|
|
390
390
|
}
|
|
391
391
|
client.editorView.dispatch({
|
|
@@ -401,9 +401,7 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
401
401
|
selection: {
|
|
402
402
|
anchor: cursorPos,
|
|
403
403
|
},
|
|
404
|
-
effects: [
|
|
405
|
-
EditorView.scrollIntoView(cursorPos),
|
|
406
|
-
],
|
|
404
|
+
effects: [EditorView.scrollIntoView(cursorPos)],
|
|
407
405
|
});
|
|
408
406
|
}
|
|
409
407
|
},
|
|
@@ -416,12 +414,9 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
416
414
|
if (center) {
|
|
417
415
|
client.editorView.dispatch({
|
|
418
416
|
effects: [
|
|
419
|
-
EditorView.scrollIntoView(
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
y: "center",
|
|
423
|
-
},
|
|
424
|
-
),
|
|
417
|
+
EditorView.scrollIntoView(pos, {
|
|
418
|
+
y: "center",
|
|
419
|
+
}),
|
|
425
420
|
],
|
|
426
421
|
});
|
|
427
422
|
}
|
|
@@ -459,7 +454,8 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
459
454
|
const from = editorView.state.selection.main.from;
|
|
460
455
|
const cursorPlaceholderPos = text.indexOf("|^|");
|
|
461
456
|
if (cursorPlaceHolder && cursorPlaceholderPos !== -1) {
|
|
462
|
-
text =
|
|
457
|
+
text =
|
|
458
|
+
text.slice(0, cursorPlaceholderPos) +
|
|
463
459
|
text.slice(cursorPlaceholderPos + 3);
|
|
464
460
|
} else {
|
|
465
461
|
cursorPlaceHolder = false;
|
|
@@ -485,10 +481,10 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
485
481
|
message: string,
|
|
486
482
|
defaultValue = "",
|
|
487
483
|
): Promise<string | undefined> => {
|
|
488
|
-
return client.prompt(message, defaultValue);
|
|
484
|
+
return client.ui.prompt(message, defaultValue);
|
|
489
485
|
},
|
|
490
486
|
"editor.confirm": (_ctx, message: string): Promise<boolean> => {
|
|
491
|
-
return client.confirm(message);
|
|
487
|
+
return client.ui.confirm(message);
|
|
492
488
|
},
|
|
493
489
|
"editor.alert": (_ctx, message: string) => {
|
|
494
490
|
alert(message);
|
|
@@ -502,17 +498,26 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
502
498
|
key,
|
|
503
499
|
value,
|
|
504
500
|
});
|
|
505
|
-
client.reloadEditor();
|
|
501
|
+
void client.reloadEditor();
|
|
506
502
|
},
|
|
507
503
|
"editor.vimEx": (_ctx, exCommand: string) => {
|
|
508
|
-
const
|
|
509
|
-
if (
|
|
510
|
-
|
|
504
|
+
const vimMod = getVimModule();
|
|
505
|
+
if (!vimMod) {
|
|
506
|
+
throw new Error("Vim module not loaded.");
|
|
507
|
+
}
|
|
508
|
+
const cm = vimMod.getCM(client.editorView);
|
|
509
|
+
if (cm?.state.vim) {
|
|
510
|
+
return vimMod.Vim.handleEx(cm as any, exCommand);
|
|
511
511
|
} else {
|
|
512
512
|
throw new Error("Vim mode not active or not initialized.");
|
|
513
513
|
}
|
|
514
514
|
},
|
|
515
515
|
"editor.configureVimMode": () => {
|
|
516
|
+
const vimMod = getVimModule();
|
|
517
|
+
if (!vimMod) {
|
|
518
|
+
throw new Error("Vim module not loaded.");
|
|
519
|
+
}
|
|
520
|
+
const { Vim } = vimMod;
|
|
516
521
|
// Override the default "o" binding to be more intelligent and follow the markdown editor's behavior
|
|
517
522
|
Vim.mapCommand("o", "action", "newline-continue-markup", {}, {});
|
|
518
523
|
Vim.mapCommand("O", "action", "back-newline-continue-markup", {}, {});
|
|
@@ -555,31 +560,29 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
555
560
|
if (config) {
|
|
556
561
|
config.unmap?.forEach((binding) => {
|
|
557
562
|
if (typeof binding === "string") {
|
|
558
|
-
console.log(
|
|
559
|
-
// @ts-
|
|
563
|
+
console.log(`Unmapping ${binding}`);
|
|
564
|
+
// @ts-expect-error: unmap expects a string for the mode, this is problematic with Ex mappings which requires undefined or false
|
|
560
565
|
Vim.unmap(binding, undefined);
|
|
561
566
|
} else if (binding.key) {
|
|
562
567
|
console.log(
|
|
563
|
-
|
|
568
|
+
`Unmapping ${binding.key} in ${binding.mode ?? "normal"}`,
|
|
564
569
|
);
|
|
565
570
|
Vim.unmap(binding.key, binding.mode ?? "normal");
|
|
566
571
|
}
|
|
567
572
|
});
|
|
568
573
|
config.map?.forEach(({ map, to, mode }) => {
|
|
569
|
-
console.log(
|
|
570
|
-
"Mapping " + map + " to " + to + " for " + (mode ?? "normal"),
|
|
571
|
-
);
|
|
574
|
+
console.log(`Mapping ${map} to ${to} for ${mode ?? "normal"}`);
|
|
572
575
|
Vim.map(map, to, mode ?? "normal");
|
|
573
576
|
});
|
|
574
577
|
config.noremap?.forEach(({ map, to, mode }) => {
|
|
575
|
-
console.log(
|
|
576
|
-
"Noremapping " + map + " to " + to + " for " + (mode ?? "normal"),
|
|
577
|
-
);
|
|
578
|
+
console.log(`Noremapping ${map} to ${to} for ${mode ?? "normal"}`);
|
|
578
579
|
Vim.noremap(map, to, mode ?? "normal");
|
|
579
580
|
});
|
|
580
581
|
config.commands?.forEach(({ ex, command }) => {
|
|
581
|
-
console.log(
|
|
582
|
-
Vim.defineEx(ex, "", () =>
|
|
582
|
+
console.log(`Mapping command '${command}' to Ex ${ex}`);
|
|
583
|
+
Vim.defineEx(ex, "", (_, params) =>
|
|
584
|
+
client.runCommandByName(command, params.args),
|
|
585
|
+
);
|
|
583
586
|
});
|
|
584
587
|
} else {
|
|
585
588
|
console.log("No vim config found");
|
|
@@ -592,7 +595,7 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
592
595
|
client.startPageNavigate(mode);
|
|
593
596
|
},
|
|
594
597
|
"editor.openCommandPalette": () => {
|
|
595
|
-
client.startCommandPalette();
|
|
598
|
+
void client.startCommandPalette();
|
|
596
599
|
},
|
|
597
600
|
"editor.deleteLine": () => {
|
|
598
601
|
deleteLine(client.editorView);
|
|
@@ -651,13 +654,13 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|
|
651
654
|
}
|
|
652
655
|
} catch (e) {
|
|
653
656
|
console.error(e);
|
|
654
|
-
client.flashNotification(`Could not copy to clipboard: ${e}`);
|
|
657
|
+
client.ui.flashNotification(`Could not copy to clipboard: ${e}`);
|
|
655
658
|
}
|
|
656
659
|
},
|
|
657
660
|
"editor.sendMessage": (_ctx, type: string, data: any) => {
|
|
658
|
-
if (!client.isDocumentEditor()) return;
|
|
661
|
+
if (!client.contentManager.isDocumentEditor()) return;
|
|
659
662
|
|
|
660
|
-
client.documentEditor.sendPublicMessage({
|
|
663
|
+
client.contentManager.documentEditor.sendPublicMessage({
|
|
661
664
|
type,
|
|
662
665
|
data,
|
|
663
666
|
});
|
|
@@ -18,19 +18,16 @@ export function eventSyscalls(
|
|
|
18
18
|
/**
|
|
19
19
|
* Define a Lua event listener
|
|
20
20
|
*/
|
|
21
|
-
"event.listen": (
|
|
22
|
-
_ctx,
|
|
23
|
-
def: EventSubscription,
|
|
24
|
-
) => {
|
|
21
|
+
"event.listen": (_ctx, def: EventSubscription) => {
|
|
25
22
|
// console.log("Registering Lua event listener: ", def.name);
|
|
26
|
-
client.config.insert(
|
|
27
|
-
"eventListeners",
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
23
|
+
client.config.insert(
|
|
24
|
+
["eventListeners", def.name],
|
|
25
|
+
async (...args: any[]) => {
|
|
26
|
+
// Convert return value to JS
|
|
27
|
+
const val = await def.run(...args);
|
|
28
|
+
return luaValueToJS(val, LuaStackFrame.lostFrame);
|
|
29
|
+
},
|
|
30
|
+
);
|
|
34
31
|
},
|
|
35
32
|
};
|
|
36
33
|
}
|
|
@@ -12,9 +12,7 @@ import {
|
|
|
12
12
|
} from "@silverbulletmd/silverbullet/lib/crypto";
|
|
13
13
|
import { fsEndpoint } from "../../spaces/constants.ts";
|
|
14
14
|
|
|
15
|
-
export function sandboxFetchSyscalls(
|
|
16
|
-
client: Client,
|
|
17
|
-
): SysCallMapping {
|
|
15
|
+
export function sandboxFetchSyscalls(client: Client): SysCallMapping {
|
|
18
16
|
return {
|
|
19
17
|
// For use in Lua
|
|
20
18
|
"http.request": async (
|
|
@@ -25,17 +23,18 @@ export function sandboxFetchSyscalls(
|
|
|
25
23
|
console.warn("Deprecated: use net.proxyFetch() instead");
|
|
26
24
|
// JSONify any non-serializable body
|
|
27
25
|
if (
|
|
28
|
-
options?.body &&
|
|
26
|
+
options?.body &&
|
|
27
|
+
typeof options.body !== "string" &&
|
|
29
28
|
!(options.body instanceof Uint8Array)
|
|
30
29
|
) {
|
|
31
30
|
options.body = JSON.stringify(options.body);
|
|
32
31
|
}
|
|
33
32
|
const fetchOptions = options
|
|
34
33
|
? {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
method: options.method,
|
|
35
|
+
headers: {} as Record<string, string>,
|
|
36
|
+
body: options.body,
|
|
37
|
+
}
|
|
39
38
|
: {};
|
|
40
39
|
|
|
41
40
|
fetchOptions.headers = buildProxyHeaders(options?.headers);
|
|
@@ -45,19 +44,27 @@ export function sandboxFetchSyscalls(
|
|
|
45
44
|
fetchOptions,
|
|
46
45
|
);
|
|
47
46
|
// Do sensible things with the body based on the content type
|
|
47
|
+
// Read as ArrayBuffer first to safely handle empty responses (e.g.
|
|
48
|
+
// PUT/DELETE returning 204 with Content-Type: application/json).
|
|
49
|
+
// resp.arrayBuffer() never throws on an empty body, whereas
|
|
50
|
+
// resp.json() would throw a SyntaxError.
|
|
51
|
+
const rawBytes = new Uint8Array(await resp.arrayBuffer());
|
|
48
52
|
let body: any;
|
|
49
|
-
const contentTypeHeader =
|
|
53
|
+
const contentTypeHeader =
|
|
54
|
+
options.responseEncoding ||
|
|
50
55
|
resp.headers.get("x-proxy-header-content-type");
|
|
51
56
|
const statusCode = +(resp.headers.get("x-proxy-status-code") || "200");
|
|
52
|
-
if (
|
|
53
|
-
body =
|
|
57
|
+
if (rawBytes.length === 0) {
|
|
58
|
+
body = null;
|
|
59
|
+
} else if (contentTypeHeader?.startsWith("application/json")) {
|
|
60
|
+
body = JSON.parse(new TextDecoder().decode(rawBytes));
|
|
54
61
|
} else if (
|
|
55
62
|
contentTypeHeader?.startsWith("application/xml") ||
|
|
56
63
|
contentTypeHeader?.startsWith("text/")
|
|
57
64
|
) {
|
|
58
|
-
body =
|
|
65
|
+
body = new TextDecoder().decode(rawBytes);
|
|
59
66
|
} else {
|
|
60
|
-
body =
|
|
67
|
+
body = rawBytes;
|
|
61
68
|
}
|
|
62
69
|
return {
|
|
63
70
|
ok: resp.ok,
|
|
@@ -74,15 +81,15 @@ export function sandboxFetchSyscalls(
|
|
|
74
81
|
// console.log("Got sandbox fetch ", url, op);
|
|
75
82
|
const fetchOptions = options
|
|
76
83
|
? {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
84
|
+
method: options.method,
|
|
85
|
+
headers: options.headers,
|
|
86
|
+
body: options.base64Body && base64Decode(options.base64Body),
|
|
87
|
+
}
|
|
81
88
|
: {};
|
|
82
89
|
fetchOptions.headers = buildProxyHeaders(options?.headers);
|
|
83
90
|
const resp = await client.httpSpacePrimitives.authenticatedFetch(
|
|
84
91
|
buildProxyUrl(client, url),
|
|
85
|
-
// Casting
|
|
92
|
+
// Casting to any due to TypeScript fetch type limitations
|
|
86
93
|
fetchOptions as any,
|
|
87
94
|
);
|
|
88
95
|
const statusCode = +(resp.headers.get("x-proxy-status-code") || "200");
|
|
@@ -100,8 +107,11 @@ export function sandboxFetchSyscalls(
|
|
|
100
107
|
function buildProxyUrl(client: Client, url: string) {
|
|
101
108
|
url = url.replace(/^https?:\/\//, "");
|
|
102
109
|
// Strip off the /.fs and replace with /.proxy
|
|
103
|
-
return
|
|
104
|
-
|
|
110
|
+
return (
|
|
111
|
+
client.httpSpacePrimitives.url.slice(0, -fsEndpoint.length) +
|
|
112
|
+
"/.proxy/" +
|
|
113
|
+
url
|
|
114
|
+
);
|
|
105
115
|
}
|
|
106
116
|
|
|
107
117
|
function buildProxyHeaders(headers?: Record<string, any>): Record<string, any> {
|
|
@@ -115,9 +125,7 @@ function buildProxyHeaders(headers?: Record<string, any>): Record<string, any> {
|
|
|
115
125
|
return newHeaders;
|
|
116
126
|
}
|
|
117
127
|
|
|
118
|
-
function extractProxyHeaders(
|
|
119
|
-
headers: Headers,
|
|
120
|
-
): Record<string, any> {
|
|
128
|
+
function extractProxyHeaders(headers: Headers): Record<string, any> {
|
|
121
129
|
const newHeaders: Record<string, any> = {};
|
|
122
130
|
for (const [key, value] of headers.entries()) {
|
|
123
131
|
if (key.toLowerCase().startsWith("x-proxy-header-")) {
|
|
@@ -20,7 +20,16 @@ export function indexSyscalls(
|
|
|
20
20
|
"index.tag": (_ctx, tagName: string): LuaQueryCollection => {
|
|
21
21
|
return objectIndex.tag(tagName);
|
|
22
22
|
},
|
|
23
|
-
"index.
|
|
23
|
+
"index.contentPages": (): LuaQueryCollection => {
|
|
24
|
+
return objectIndex.contentPages();
|
|
25
|
+
},
|
|
26
|
+
"index.metaPages": (): LuaQueryCollection => {
|
|
27
|
+
return objectIndex.metaPages();
|
|
28
|
+
},
|
|
29
|
+
"index.aggregates": (): LuaQueryCollection => {
|
|
30
|
+
return objectIndex.aggregates();
|
|
31
|
+
},
|
|
32
|
+
"index.ensureFullIndex": () => {
|
|
24
33
|
return objectIndex.ensureFullIndex(client.space);
|
|
25
34
|
},
|
|
26
35
|
"index.reindexSpace": () => {
|
|
@@ -1,40 +1,62 @@
|
|
|
1
1
|
import type { SysCallMapping } from "../system.ts";
|
|
2
|
-
import {
|
|
2
|
+
import { type OutputUnit, Validator, format } from "@cfworker/json-schema";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// Register custom formats
|
|
5
|
+
format.email = (data: string) => data.includes("@");
|
|
6
|
+
format["page-ref"] = (data: string) =>
|
|
7
|
+
data.startsWith("[[") && data.endsWith("]]");
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
validate: (data: string) => {
|
|
8
|
-
// TODO: Implement email validation
|
|
9
|
-
return data.includes("@");
|
|
10
|
-
},
|
|
11
|
-
async: false,
|
|
12
|
-
});
|
|
9
|
+
const schemaCache = new Map<string, Validator>();
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Deep-clone a value, replacing any functions with null.
|
|
13
|
+
* JSON schema can't validate functions, so we strip them before validation.
|
|
14
|
+
*/
|
|
15
|
+
function stripFunctions(value: any): any {
|
|
16
|
+
if (typeof value === "function") return null;
|
|
17
|
+
if (value === null || value === undefined || typeof value !== "object") {
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
if (Array.isArray(value)) {
|
|
21
|
+
return value.map(stripFunctions);
|
|
22
|
+
}
|
|
23
|
+
const result: Record<string, any> = {};
|
|
24
|
+
for (const key of Object.keys(value)) {
|
|
25
|
+
result[key] = stripFunctions(value[key]);
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
20
29
|
|
|
21
|
-
|
|
30
|
+
function formatErrors(errors: OutputUnit[]): string {
|
|
31
|
+
// Filter out "properties" wrapper errors, keep only the specific leaf errors
|
|
32
|
+
const leafErrors = errors.filter((e) => e.keyword !== "properties");
|
|
33
|
+
const errorsToUse = leafErrors.length > 0 ? leafErrors : errors;
|
|
34
|
+
|
|
35
|
+
return errorsToUse
|
|
36
|
+
.map((e) => {
|
|
37
|
+
// Convert instanceLocation from "#/foo/bar" to "foo.bar"
|
|
38
|
+
const path =
|
|
39
|
+
e.instanceLocation === "#"
|
|
40
|
+
? ""
|
|
41
|
+
: e.instanceLocation.slice(2).replaceAll("/", ".");
|
|
42
|
+
return path ? `${path}: ${e.error}` : e.error;
|
|
43
|
+
})
|
|
44
|
+
.join(", ");
|
|
45
|
+
}
|
|
22
46
|
|
|
23
47
|
export function validateObject(schema: any, object: any): undefined | string {
|
|
24
48
|
try {
|
|
25
49
|
const schemaKey = JSON.stringify(schema);
|
|
26
50
|
if (!schemaCache.has(schemaKey)) {
|
|
27
|
-
const
|
|
28
|
-
schemaCache.set(schemaKey,
|
|
51
|
+
const validator = new Validator(schema, "7");
|
|
52
|
+
schemaCache.set(schemaKey, validator);
|
|
29
53
|
}
|
|
30
|
-
const
|
|
31
|
-
|
|
54
|
+
const validator = schemaCache.get(schemaKey)!;
|
|
55
|
+
const result = validator.validate(stripFunctions(object));
|
|
56
|
+
if (result.valid) {
|
|
32
57
|
return;
|
|
33
58
|
} else {
|
|
34
|
-
|
|
35
|
-
text = text.replaceAll("/", ".");
|
|
36
|
-
text = text.replace(/^data[\.\s]/, "");
|
|
37
|
-
return text;
|
|
59
|
+
return formatErrors(result.errors);
|
|
38
60
|
}
|
|
39
61
|
} catch (e: any) {
|
|
40
62
|
return e.message;
|
|
@@ -42,12 +64,33 @@ export function validateObject(schema: any, object: any): undefined | string {
|
|
|
42
64
|
}
|
|
43
65
|
|
|
44
66
|
export function validateSchema(schema: any): undefined | string {
|
|
45
|
-
|
|
46
|
-
|
|
67
|
+
if (schema === null || schema === undefined) {
|
|
68
|
+
return "schema must not be null or undefined";
|
|
69
|
+
}
|
|
70
|
+
if (typeof schema === "boolean") {
|
|
47
71
|
return;
|
|
48
|
-
} else {
|
|
49
|
-
return ajv.errorsText(ajv.errors);
|
|
50
72
|
}
|
|
73
|
+
if (typeof schema !== "object" || Array.isArray(schema)) {
|
|
74
|
+
return "schema must be an object or boolean";
|
|
75
|
+
}
|
|
76
|
+
if (schema.type !== undefined) {
|
|
77
|
+
const validTypes = [
|
|
78
|
+
"string",
|
|
79
|
+
"number",
|
|
80
|
+
"integer",
|
|
81
|
+
"boolean",
|
|
82
|
+
"object",
|
|
83
|
+
"array",
|
|
84
|
+
"null",
|
|
85
|
+
];
|
|
86
|
+
const types = Array.isArray(schema.type) ? schema.type : [schema.type];
|
|
87
|
+
for (const t of types) {
|
|
88
|
+
if (!validTypes.includes(t)) {
|
|
89
|
+
return `schema.type must be one of ${validTypes.join(", ")}`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
51
94
|
}
|
|
52
95
|
|
|
53
96
|
export function jsonschemaSyscalls(): SysCallMapping {
|
|
@@ -59,10 +102,7 @@ export function jsonschemaSyscalls(): SysCallMapping {
|
|
|
59
102
|
): undefined | string => {
|
|
60
103
|
return validateObject(schema, object);
|
|
61
104
|
},
|
|
62
|
-
"jsonschema.validateSchema": (
|
|
63
|
-
_ctx,
|
|
64
|
-
schema: any,
|
|
65
|
-
): undefined | string => {
|
|
105
|
+
"jsonschema.validateSchema": (_ctx, schema: any): undefined | string => {
|
|
66
106
|
return validateSchema(schema);
|
|
67
107
|
},
|
|
68
108
|
};
|