@silverbulletmd/silverbullet 2.5.3 → 2.7.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.
- package/README.md +4 -6
- package/client/asset_bundle/bundle.ts +3 -9
- package/client/data/datastore.ts +4 -5
- package/client/markdown_parser/constants.ts +4 -2
- package/client/plugos/hooks/code_widget.ts +3 -5
- package/client/plugos/hooks/command.ts +54 -24
- package/client/plugos/hooks/document_editor.ts +10 -12
- package/client/plugos/hooks/event.ts +33 -36
- package/client/plugos/hooks/mq.ts +17 -17
- package/client/plugos/hooks/plug_namespace.ts +3 -5
- package/client/plugos/hooks/slash_command.ts +15 -28
- package/client/plugos/hooks/syscall.ts +3 -3
- package/client/plugos/manifest_cache.ts +22 -15
- package/client/plugos/plug.ts +7 -7
- package/client/plugos/plug_compile.ts +119 -67
- package/client/plugos/protocol.ts +28 -28
- package/client/plugos/proxy_fetch.ts +7 -6
- package/client/plugos/sandboxes/worker_sandbox.ts +26 -15
- package/client/plugos/syscalls/asset.ts +1 -3
- package/client/plugos/syscalls/code_widget.ts +1 -3
- package/client/plugos/syscalls/config.ts +10 -7
- package/client/plugos/syscalls/datastore.ts +1 -1
- package/client/plugos/syscalls/editor.ts +199 -60
- package/client/plugos/syscalls/event.ts +9 -12
- package/client/plugos/syscalls/fetch.ts +30 -22
- package/client/plugos/syscalls/index.ts +11 -2
- package/client/plugos/syscalls/jsonschema.ts +54 -32
- package/client/plugos/syscalls/language.ts +9 -5
- package/client/plugos/syscalls/markdown.ts +29 -7
- package/client/plugos/syscalls/mq.ts +3 -11
- package/client/plugos/syscalls/service_registry.ts +1 -4
- package/client/plugos/syscalls/shell.ts +2 -5
- package/client/plugos/syscalls/sync.ts +69 -60
- package/client/plugos/syscalls/system.ts +15 -3
- package/client/plugos/system.ts +18 -20
- package/client/plugos/types.ts +25 -0
- package/client/plugos/util.ts +20 -0
- package/client/plugos/worker_runtime.ts +4 -3
- package/client/space_lua/aggregates.ts +633 -60
- package/client/space_lua/ast.ts +21 -9
- package/client/space_lua/ast_narrow.ts +4 -2
- package/client/space_lua/eval.ts +842 -536
- package/client/space_lua/labels.ts +6 -11
- package/client/space_lua/numeric.ts +5 -8
- package/client/space_lua/parse.ts +290 -169
- package/client/space_lua/query_collection.ts +213 -149
- package/client/space_lua/render_lua_markdown.ts +369 -0
- package/client/space_lua/rp.ts +5 -4
- package/client/space_lua/runtime.ts +245 -142
- package/client/space_lua/sliq_null.ts +6 -0
- package/client/space_lua/stdlib/format.ts +34 -20
- 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 +15 -14
- package/client/space_lua/stdlib/net.ts +25 -15
- package/client/space_lua/stdlib/os.ts +76 -85
- package/client/space_lua/stdlib/pattern.ts +28 -35
- package/client/space_lua/stdlib/prng.ts +15 -12
- package/client/space_lua/stdlib/space_lua.ts +16 -17
- package/client/space_lua/stdlib/string.ts +7 -17
- package/client/space_lua/stdlib/string_pack.ts +23 -19
- package/client/space_lua/stdlib/table.ts +5 -9
- package/client/space_lua/stdlib.ts +20 -30
- package/client/space_lua/tonumber.ts +79 -40
- package/client/space_lua/util.ts +14 -10
- package/dist/plug-compile.js +87 -42
- package/package.json +24 -22
- package/plug-api/constants.ts +20 -0
- package/plug-api/lib/async.ts +19 -6
- package/plug-api/lib/crypto.ts +5 -6
- package/plug-api/lib/dates.ts +15 -7
- package/plug-api/lib/json.ts +10 -4
- package/plug-api/lib/ref.ts +27 -20
- package/plug-api/lib/resolve.ts +7 -11
- package/plug-api/lib/shortcut.ts +16 -0
- package/plug-api/lib/tags.ts +13 -4
- package/plug-api/lib/transclusion.ts +6 -17
- package/plug-api/lib/tree.ts +146 -43
- package/plug-api/lib/yaml.ts +25 -15
- package/plug-api/syscalls/asset.ts +1 -1
- package/plug-api/syscalls/config.ts +38 -4
- package/plug-api/syscalls/editor.ts +169 -14
- package/plug-api/syscalls/index.ts +18 -0
- 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 +16 -4
- package/plug-api/syscalls/yaml.ts +2 -6
- package/plug-api/types/client.ts +16 -1
- package/plug-api/types/event.ts +6 -4
- package/plug-api/types/manifest.ts +71 -9
- package/plugs/builtin_plugs.ts +3 -3
- package/client/plugos/sandboxes/web_worker_sandbox.ts +0 -17
- package/dist/worker_runtime_bundle.js +0 -233
package/README.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|

|
|
3
3
|

|
|
4
4
|

|
|
5
|
+
[](https://deepwiki.com/silverbulletmd/silverbullet)
|
|
5
6
|
|
|
6
7
|
# SilverBullet
|
|
7
8
|
SilverBullet is a Programmable, Private, Browser-based, Open Source, Self Hosted, Personal Knowledge Management Platform.
|
|
@@ -62,10 +63,10 @@ Make sure your `$GOPATH/bin` is in your $PATH.
|
|
|
62
63
|
First, install dependencies:
|
|
63
64
|
|
|
64
65
|
```shell
|
|
65
|
-
|
|
66
|
+
make setup
|
|
66
67
|
```
|
|
67
68
|
|
|
68
|
-
To build everything and run the server:
|
|
69
|
+
To build everything and run the server (which automatically restarts upon file changing):
|
|
69
70
|
|
|
70
71
|
```shell
|
|
71
72
|
air <PATH-TO-YOUR-SPACE>
|
|
@@ -74,9 +75,7 @@ air <PATH-TO-YOUR-SPACE>
|
|
|
74
75
|
Alternatively, to build:
|
|
75
76
|
|
|
76
77
|
```shell
|
|
77
|
-
make
|
|
78
|
-
# or
|
|
79
|
-
npm run build
|
|
78
|
+
make
|
|
80
79
|
```
|
|
81
80
|
|
|
82
81
|
To run the resulting server:
|
|
@@ -108,7 +107,6 @@ docker build -t silverbullet .
|
|
|
108
107
|
```
|
|
109
108
|
|
|
110
109
|
To run:
|
|
111
|
-
|
|
112
110
|
```shell
|
|
113
111
|
docker run -p 3000:3000 -v <PATH-TO-YOUR-SPACE>:/space silverbullet
|
|
114
112
|
```
|
|
@@ -23,9 +23,7 @@ export class AssetBundle {
|
|
|
23
23
|
return Object.keys(this.bundle);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
readFileSync(
|
|
27
|
-
path: string,
|
|
28
|
-
): Uint8Array {
|
|
26
|
+
readFileSync(path: string): Uint8Array {
|
|
29
27
|
const content = this.bundle[path];
|
|
30
28
|
if (!content) {
|
|
31
29
|
throw new Error(`No such file ${path}`);
|
|
@@ -42,15 +40,11 @@ export class AssetBundle {
|
|
|
42
40
|
return content.data;
|
|
43
41
|
}
|
|
44
42
|
|
|
45
|
-
readTextFileSync(
|
|
46
|
-
path: string,
|
|
47
|
-
): string {
|
|
43
|
+
readTextFileSync(path: string): string {
|
|
48
44
|
return new TextDecoder().decode(this.readFileSync(path));
|
|
49
45
|
}
|
|
50
46
|
|
|
51
|
-
getMimeType(
|
|
52
|
-
path: string,
|
|
53
|
-
): string {
|
|
47
|
+
getMimeType(path: string): string {
|
|
54
48
|
const entry = this.bundle[path];
|
|
55
49
|
if (!entry) {
|
|
56
50
|
throw new Error(`No such file ${path}`);
|
package/client/data/datastore.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
} from "../space_lua/query_collection.ts";
|
|
5
5
|
import { LuaEnv, LuaStackFrame } from "../space_lua/runtime.ts";
|
|
6
6
|
import type { KvPrimitives, KvQueryOptions } from "./kv_primitives.ts";
|
|
7
|
+
import type { Config } from "../config.ts";
|
|
7
8
|
|
|
8
9
|
import type { KV, KvKey } from "../../plug-api/types/datastore.ts";
|
|
9
10
|
|
|
@@ -12,10 +13,7 @@ import type { KV, KvKey } from "../../plug-api/types/datastore.ts";
|
|
|
12
13
|
* in a more user-friendly way
|
|
13
14
|
*/
|
|
14
15
|
export class DataStore {
|
|
15
|
-
constructor(
|
|
16
|
-
readonly kv: KvPrimitives,
|
|
17
|
-
) {
|
|
18
|
-
}
|
|
16
|
+
constructor(readonly kv: KvPrimitives) {}
|
|
19
17
|
|
|
20
18
|
async get<T = any>(key: KvKey): Promise<T | null> {
|
|
21
19
|
return (await this.batchGet([key]))[0];
|
|
@@ -79,7 +77,8 @@ export class DataStore {
|
|
|
79
77
|
env: LuaEnv = new LuaEnv(),
|
|
80
78
|
sf: LuaStackFrame = LuaStackFrame.lostFrame,
|
|
81
79
|
enricher?: (key: KvKey, item: any) => any,
|
|
80
|
+
config?: Config,
|
|
82
81
|
): Promise<T[]> {
|
|
83
|
-
return queryLua(this.kv, prefix, query, env, sf, enricher);
|
|
82
|
+
return queryLua(this.kv, prefix, query, env, sf, enricher, config);
|
|
84
83
|
}
|
|
85
84
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export const wikiLinkRegex =
|
|
2
2
|
/(?<leadingTrivia>!?\[\[)(?<stringRef>.*?)(?:\|(?<alias>.*?))?(?<trailingTrivia>\]\])/g;
|
|
3
|
-
export const mdLinkRegex =
|
|
3
|
+
export const mdLinkRegex =
|
|
4
|
+
/!?\[(?<title>[^\]\\]*(?:\\.[^\]\\]*)*)\]\((?<url>.+)\)/g;
|
|
4
5
|
export const tagRegex =
|
|
5
6
|
/#(?:(?:\d*[^\d\s!@#$%^&*(),.?":{}|<>\\][^\s!@#$%^&*(),.?":{}|<>\\]*)|(?:<[^>\n]+>))/;
|
|
6
7
|
export const nakedUrlRegex =
|
|
@@ -9,5 +10,6 @@ export const frontmatterQuotesRegex = /["'].*["']/g;
|
|
|
9
10
|
export const frontmatterUrlRegex = /([a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^\s"']+)/g;
|
|
10
11
|
export const frontmatterWikiLinkRegex =
|
|
11
12
|
/(?<leadingTrivia>!?\[\[)(?<stringRef>.*?)(?:\|(?<alias>.*?))?(?<trailingTrivia>\]\])/g;
|
|
12
|
-
export const frontmatterMailtoRegex = /(mailto:[^@\s]+@[^@\s"']+)/
|
|
13
|
+
export const frontmatterMailtoRegex = /(mailto:[^@\s]+@[^@\s"']+)/gi;
|
|
13
14
|
export const pWikiLinkRegex = new RegExp(`^${wikiLinkRegex.source}`); // Modified regex used only in parser
|
|
15
|
+
export const anchorRegex = /\$[A-Za-z_][A-Za-z0-9_/:-]*/;
|
|
@@ -10,11 +10,9 @@ export class CodeWidgetHook implements Hook<CodeWidgetT> {
|
|
|
10
10
|
collectAllCodeWidgets(system: System<CodeWidgetT>) {
|
|
11
11
|
this.codeWidgetCallbacks.clear();
|
|
12
12
|
for (const plug of system.loadedPlugs.values()) {
|
|
13
|
-
for (
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
)
|
|
17
|
-
) {
|
|
13
|
+
for (const [name, functionDef] of Object.entries(
|
|
14
|
+
plug.manifest!.functions,
|
|
15
|
+
)) {
|
|
18
16
|
if (!functionDef.codeWidget) {
|
|
19
17
|
continue;
|
|
20
18
|
}
|
|
@@ -5,13 +5,17 @@ import { throttle } from "@silverbulletmd/silverbullet/lib/async";
|
|
|
5
5
|
import type { Command, CommandHookEvents } from "../../types/command.ts";
|
|
6
6
|
import type { CommandHookT } from "@silverbulletmd/silverbullet/type/manifest";
|
|
7
7
|
|
|
8
|
-
export class CommandHook
|
|
9
|
-
|
|
8
|
+
export class CommandHook
|
|
9
|
+
extends EventEmitter<CommandHookEvents>
|
|
10
|
+
implements Hook<CommandHookT>
|
|
11
|
+
{
|
|
10
12
|
system?: System<CommandHookT>;
|
|
11
13
|
public throttledBuildAllCommandsAndEmit = throttle(() => {
|
|
12
14
|
this.buildAllCommandsAndEmit();
|
|
13
15
|
}, 200);
|
|
14
16
|
|
|
17
|
+
private registeredCommands = new Map<string, Command>();
|
|
18
|
+
|
|
15
19
|
constructor(
|
|
16
20
|
private readOnly: boolean,
|
|
17
21
|
private additionalCommands: Map<string, Command>,
|
|
@@ -19,22 +23,60 @@ export class CommandHook extends EventEmitter<CommandHookEvents>
|
|
|
19
23
|
super();
|
|
20
24
|
}
|
|
21
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Register a command directly with the hook. Intended for client-side code
|
|
28
|
+
* that wants to define commands without going through a plug manifest.
|
|
29
|
+
* Overwrites any previously-registered command with the same name.
|
|
30
|
+
*/
|
|
31
|
+
registerCommand(command: Command): void {
|
|
32
|
+
this.registeredCommands.set(command.name, command);
|
|
33
|
+
this.throttledBuildAllCommandsAndEmit();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private mergeCommand(
|
|
37
|
+
commands: Map<string, Command>,
|
|
38
|
+
name: string,
|
|
39
|
+
cmd: Command,
|
|
40
|
+
) {
|
|
41
|
+
if (commands.has(name)) {
|
|
42
|
+
// Existing command, let's do some inline patching
|
|
43
|
+
const existingCommand = commands.get(name)!;
|
|
44
|
+
const command: Command = {
|
|
45
|
+
...existingCommand,
|
|
46
|
+
...cmd,
|
|
47
|
+
};
|
|
48
|
+
if (cmd.run) {
|
|
49
|
+
command.run = cmd.run;
|
|
50
|
+
}
|
|
51
|
+
commands.set(name, command);
|
|
52
|
+
} else {
|
|
53
|
+
commands.set(name, cmd);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
22
57
|
/**
|
|
23
58
|
* Build the command map
|
|
24
59
|
*/
|
|
25
60
|
buildAllCommands(): Map<string, Command> {
|
|
26
61
|
const commands = new Map<string, Command>();
|
|
62
|
+
// Start with directly-registered commands (built-in client commands).
|
|
63
|
+
// These form the base layer and can be overridden by plug commands or
|
|
64
|
+
// script commands further down.
|
|
65
|
+
for (const [name, cmd] of this.registeredCommands) {
|
|
66
|
+
if (cmd.requireMode === "rw" && this.readOnly) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
commands.set(name, cmd);
|
|
70
|
+
}
|
|
27
71
|
// Add commands from plugs
|
|
28
72
|
if (!this.system) {
|
|
29
73
|
// Not initialized yet
|
|
30
74
|
return commands;
|
|
31
75
|
}
|
|
32
76
|
for (const plug of this.system.loadedPlugs.values()) {
|
|
33
|
-
for (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
)
|
|
37
|
-
) {
|
|
77
|
+
for (const [name, functionDef] of Object.entries(
|
|
78
|
+
plug.manifest!.functions,
|
|
79
|
+
)) {
|
|
38
80
|
if (!functionDef.command) {
|
|
39
81
|
continue;
|
|
40
82
|
}
|
|
@@ -43,30 +85,18 @@ export class CommandHook extends EventEmitter<CommandHookEvents>
|
|
|
43
85
|
// Bit hacky, but don't expose commands that require write mode in read-only mode
|
|
44
86
|
continue;
|
|
45
87
|
}
|
|
46
|
-
|
|
88
|
+
this.mergeCommand(commands, cmd.name, {
|
|
47
89
|
...cmd,
|
|
48
90
|
run: (args?: string[]) => {
|
|
49
|
-
return plug.invoke(name, [cmd, ...args ?? []]);
|
|
91
|
+
return plug.invoke(name, [cmd, ...(args ?? [])]);
|
|
50
92
|
},
|
|
51
93
|
});
|
|
52
94
|
}
|
|
53
95
|
}
|
|
96
|
+
// Script commands come last — they always win over built-ins and plugs,
|
|
97
|
+
// so users can rebind keys / replace run bodies from Lua.
|
|
54
98
|
for (const [name, cmd] of this.additionalCommands) {
|
|
55
|
-
|
|
56
|
-
// Existing command, let's do some inline patching
|
|
57
|
-
const existingCommand = commands.get(name)!;
|
|
58
|
-
const command: Command = {
|
|
59
|
-
...existingCommand,
|
|
60
|
-
...cmd,
|
|
61
|
-
};
|
|
62
|
-
if (cmd.run) {
|
|
63
|
-
command.run = cmd.run;
|
|
64
|
-
}
|
|
65
|
-
commands.set(name, command);
|
|
66
|
-
} else {
|
|
67
|
-
// New command, let's just set
|
|
68
|
-
commands.set(name, cmd);
|
|
69
|
-
}
|
|
99
|
+
this.mergeCommand(commands, name, cmd);
|
|
70
100
|
}
|
|
71
101
|
return commands;
|
|
72
102
|
}
|
|
@@ -12,11 +12,9 @@ export class DocumentEditorHook implements Hook<DocumentEditorT> {
|
|
|
12
12
|
collectAllDocumentEditors(system: System<DocumentEditorT>) {
|
|
13
13
|
this.documentEditors.clear();
|
|
14
14
|
for (const plug of system.loadedPlugs.values()) {
|
|
15
|
-
for (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
)
|
|
19
|
-
) {
|
|
15
|
+
for (const [name, functionDef] of Object.entries(
|
|
16
|
+
plug.manifest!.functions,
|
|
17
|
+
)) {
|
|
20
18
|
if (!functionDef.editor) {
|
|
21
19
|
continue;
|
|
22
20
|
}
|
|
@@ -25,9 +23,9 @@ export class DocumentEditorHook implements Hook<DocumentEditorT> {
|
|
|
25
23
|
? functionDef.editor
|
|
26
24
|
: [functionDef.editor];
|
|
27
25
|
|
|
28
|
-
const conflict = Array.from(this.documentEditors.entries()).find(
|
|
29
|
-
[_, { extensions }],
|
|
30
|
-
)
|
|
26
|
+
const conflict = Array.from(this.documentEditors.entries()).find(
|
|
27
|
+
([_, { extensions }]) => keys.some((key) => extensions.includes(key)),
|
|
28
|
+
);
|
|
31
29
|
|
|
32
30
|
if (conflict) {
|
|
33
31
|
console.log(
|
|
@@ -37,10 +35,10 @@ export class DocumentEditorHook implements Hook<DocumentEditorT> {
|
|
|
37
35
|
);
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
this.documentEditors.set(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
);
|
|
38
|
+
this.documentEditors.set(name, {
|
|
39
|
+
extensions: keys,
|
|
40
|
+
callback: () => plug.invoke(name, []),
|
|
41
|
+
});
|
|
44
42
|
}
|
|
45
43
|
}
|
|
46
44
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import type { Manifest } from "../types.ts";
|
|
3
2
|
import type { System } from "../system.ts";
|
|
4
3
|
import type { EventHookI } from "../eventhook.ts";
|
|
@@ -12,8 +11,7 @@ export class EventHook implements EventHookI {
|
|
|
12
11
|
private system?: System<EventHookT>;
|
|
13
12
|
private localListeners: Map<string, ((...args: any[]) => any)[]> = new Map();
|
|
14
13
|
|
|
15
|
-
constructor(readonly config?: Config) {
|
|
16
|
-
}
|
|
14
|
+
constructor(readonly config?: Config) {}
|
|
17
15
|
|
|
18
16
|
addLocalListener(eventName: string, callback: (...args: any[]) => any) {
|
|
19
17
|
if (!this.localListeners.has(eventName)) {
|
|
@@ -74,29 +72,28 @@ export class EventHook implements EventHookI {
|
|
|
74
72
|
const promises: Promise<any>[] = [];
|
|
75
73
|
for (const plug of this.system.loadedPlugs.values()) {
|
|
76
74
|
const manifest = plug.manifest;
|
|
77
|
-
for (
|
|
78
|
-
const [name, functionDef] of Object.entries(
|
|
79
|
-
manifest!.functions,
|
|
80
|
-
)
|
|
81
|
-
) {
|
|
75
|
+
for (const [name, functionDef] of Object.entries(manifest!.functions)) {
|
|
82
76
|
if (functionDef.events) {
|
|
83
77
|
for (const event of functionDef.events) {
|
|
84
78
|
if (
|
|
85
|
-
event === eventName ||
|
|
79
|
+
event === eventName ||
|
|
80
|
+
eventNameToRegex(event).test(eventName)
|
|
86
81
|
) {
|
|
87
82
|
// Only dispatch functions that can run in this environment
|
|
88
83
|
if (plug.canInvoke(name)) {
|
|
89
84
|
// Queue the promise
|
|
90
|
-
promises.push(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
85
|
+
promises.push(
|
|
86
|
+
(async () => {
|
|
87
|
+
try {
|
|
88
|
+
return await plug.invoke(name, args);
|
|
89
|
+
} catch (e: any) {
|
|
90
|
+
console.error(
|
|
91
|
+
`Error dispatching event ${eventName} to ${plug.manifest.name}.${name}: ${e.message}`,
|
|
92
|
+
);
|
|
93
|
+
throw e;
|
|
94
|
+
}
|
|
95
|
+
})(),
|
|
96
|
+
);
|
|
100
97
|
}
|
|
101
98
|
}
|
|
102
99
|
}
|
|
@@ -109,9 +106,11 @@ export class EventHook implements EventHookI {
|
|
|
109
106
|
if (eventNameToRegex(name).test(eventName)) {
|
|
110
107
|
for (const localListener of localListeners) {
|
|
111
108
|
// Queue the promise
|
|
112
|
-
promises.push(
|
|
113
|
-
|
|
114
|
-
|
|
109
|
+
promises.push(
|
|
110
|
+
(async () => {
|
|
111
|
+
return await Promise.resolve(localListener(...args));
|
|
112
|
+
})(),
|
|
113
|
+
);
|
|
115
114
|
}
|
|
116
115
|
}
|
|
117
116
|
}
|
|
@@ -125,15 +124,17 @@ export class EventHook implements EventHookI {
|
|
|
125
124
|
for (const [name, listeners] of Object.entries(configListeners)) {
|
|
126
125
|
if (eventNameToRegex(name).test(eventName)) {
|
|
127
126
|
for (const listener of listeners) {
|
|
128
|
-
promises.push(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
127
|
+
promises.push(
|
|
128
|
+
(async () => {
|
|
129
|
+
return await Promise.resolve(
|
|
130
|
+
listener({
|
|
131
|
+
name: eventName,
|
|
132
|
+
// Most events have a single argument, so let's optimize for that, otherwise pass all arguments as an array
|
|
133
|
+
data: args.length === 1 ? args[0] : args,
|
|
134
|
+
}),
|
|
135
|
+
);
|
|
136
|
+
})(),
|
|
137
|
+
);
|
|
137
138
|
}
|
|
138
139
|
}
|
|
139
140
|
}
|
|
@@ -167,11 +168,7 @@ export class EventHook implements EventHookI {
|
|
|
167
168
|
|
|
168
169
|
validateManifest(manifest: Manifest<EventHookT>): string[] {
|
|
169
170
|
const errors = [];
|
|
170
|
-
for (
|
|
171
|
-
const [_, functionDef] of Object.entries(
|
|
172
|
-
manifest.functions || {},
|
|
173
|
-
)
|
|
174
|
-
) {
|
|
171
|
+
for (const [_, functionDef] of Object.entries(manifest.functions || {})) {
|
|
175
172
|
if (functionDef.events && !Array.isArray(functionDef.events)) {
|
|
176
173
|
errors.push("'events' key must be an array of strings");
|
|
177
174
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import type { Hook, Manifest } from "../types.ts";
|
|
3
2
|
import type { System } from "../system.ts";
|
|
4
3
|
import { throttle } from "@silverbulletmd/silverbullet/lib/async";
|
|
@@ -10,13 +9,11 @@ import type {
|
|
|
10
9
|
} from "@silverbulletmd/silverbullet/type/datastore";
|
|
11
10
|
import type { Config } from "../../config.ts";
|
|
12
11
|
|
|
13
|
-
export type MQListenerSpec =
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
run: Function;
|
|
19
|
-
};
|
|
12
|
+
export type MQListenerSpec = MQSubscribeOptions & {
|
|
13
|
+
queue: string;
|
|
14
|
+
autoAck?: boolean;
|
|
15
|
+
run: Function;
|
|
16
|
+
};
|
|
20
17
|
|
|
21
18
|
export class MQHook implements Hook<MQHookT> {
|
|
22
19
|
subscriptions: QueueWorker[] = [];
|
|
@@ -28,8 +25,7 @@ export class MQHook implements Hook<MQHookT> {
|
|
|
28
25
|
private system: System<MQHookT>,
|
|
29
26
|
readonly mq: DataStoreMQ,
|
|
30
27
|
readonly config: Config,
|
|
31
|
-
) {
|
|
32
|
-
}
|
|
28
|
+
) {}
|
|
33
29
|
|
|
34
30
|
apply(system: System<MQHookT>): void {
|
|
35
31
|
this.system = system;
|
|
@@ -57,11 +53,9 @@ export class MQHook implements Hook<MQHookT> {
|
|
|
57
53
|
if (!plug.manifest) {
|
|
58
54
|
continue;
|
|
59
55
|
}
|
|
60
|
-
for (
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
)
|
|
64
|
-
) {
|
|
56
|
+
for (const [name, functionDef] of Object.entries(
|
|
57
|
+
plug.manifest.functions,
|
|
58
|
+
)) {
|
|
65
59
|
if (!functionDef.mqSubscriptions) {
|
|
66
60
|
continue;
|
|
67
61
|
}
|
|
@@ -79,7 +73,10 @@ export class MQHook implements Hook<MQHookT> {
|
|
|
79
73
|
try {
|
|
80
74
|
await plug.invoke(name, [messages]);
|
|
81
75
|
if (subscriptionDef.autoAck) {
|
|
82
|
-
await this.mq.batchAck(
|
|
76
|
+
await this.mq.batchAck(
|
|
77
|
+
queue,
|
|
78
|
+
messages.map((m) => m.id),
|
|
79
|
+
);
|
|
83
80
|
}
|
|
84
81
|
} catch (e: any) {
|
|
85
82
|
console.error(
|
|
@@ -118,7 +115,10 @@ export class MQHook implements Hook<MQHookT> {
|
|
|
118
115
|
try {
|
|
119
116
|
await listener.run(messages);
|
|
120
117
|
if (listener.autoAck) {
|
|
121
|
-
await this.mq.batchAck(
|
|
118
|
+
await this.mq.batchAck(
|
|
119
|
+
queue,
|
|
120
|
+
messages.map((m) => m.id),
|
|
121
|
+
);
|
|
122
122
|
}
|
|
123
123
|
} catch (e: any) {
|
|
124
124
|
console.error(
|
|
@@ -30,11 +30,9 @@ export class PlugNamespaceHook implements Hook<PlugNamespaceHookT> {
|
|
|
30
30
|
this.spaceFunctions = [];
|
|
31
31
|
for (const plug of system.loadedPlugs.values()) {
|
|
32
32
|
if (plug.manifest?.functions) {
|
|
33
|
-
for (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
)
|
|
37
|
-
) {
|
|
33
|
+
for (const [funcName, funcDef] of Object.entries(
|
|
34
|
+
plug.manifest.functions,
|
|
35
|
+
)) {
|
|
38
36
|
if (funcDef.pageNamespace) {
|
|
39
37
|
this.spaceFunctions.push({
|
|
40
38
|
operation: funcDef.pageNamespace.operation,
|
|
@@ -23,8 +23,7 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
|
|
|
23
23
|
this.buildAllCommands();
|
|
24
24
|
}, 200);
|
|
25
25
|
|
|
26
|
-
constructor(private client: Client) {
|
|
27
|
-
}
|
|
26
|
+
constructor(private client: Client) {}
|
|
28
27
|
|
|
29
28
|
buildAllCommands() {
|
|
30
29
|
const clientSystem = this.client.clientSystem;
|
|
@@ -32,11 +31,9 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
|
|
|
32
31
|
|
|
33
32
|
this.slashCommands = [];
|
|
34
33
|
for (const plug of system.loadedPlugs.values()) {
|
|
35
|
-
for (
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
)
|
|
39
|
-
) {
|
|
34
|
+
for (const [name, functionDef] of Object.entries(
|
|
35
|
+
plug.manifest!.functions,
|
|
36
|
+
)) {
|
|
40
37
|
if (!functionDef.slashCommand) {
|
|
41
38
|
continue;
|
|
42
39
|
}
|
|
@@ -50,14 +47,9 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
|
|
|
50
47
|
}
|
|
51
48
|
}
|
|
52
49
|
// Iterate over script defined slash commands
|
|
53
|
-
for (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
"slashCommands",
|
|
57
|
-
{},
|
|
58
|
-
),
|
|
59
|
-
)
|
|
60
|
-
) {
|
|
50
|
+
for (const command of Object.values(
|
|
51
|
+
this.client.config.get<Record<string, SlashCommand>>("slashCommands", {}),
|
|
52
|
+
)) {
|
|
61
53
|
this.slashCommands.push(command);
|
|
62
54
|
}
|
|
63
55
|
}
|
|
@@ -74,7 +66,9 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
|
|
|
74
66
|
const options: Completion[] = [];
|
|
75
67
|
|
|
76
68
|
// No slash commands in comment blocks (queries and such) or links
|
|
77
|
-
|
|
69
|
+
// Pass -1 so we resolve into the node ending at the cursor: when typing,
|
|
70
|
+
// the user is referring to preceding text, not what comes after.
|
|
71
|
+
const currentNode = syntaxTree(ctx.state).resolveInner(ctx.pos, -1);
|
|
78
72
|
if (
|
|
79
73
|
currentNode.type.name === "CommentBlock" ||
|
|
80
74
|
currentNode.type.name === "Link"
|
|
@@ -88,14 +82,14 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
|
|
|
88
82
|
if (
|
|
89
83
|
def.onlyContexts &&
|
|
90
84
|
!def.onlyContexts.some((context) =>
|
|
91
|
-
parentNodes.some((node) => node.startsWith(context))
|
|
85
|
+
parentNodes.some((node) => node.startsWith(context)),
|
|
92
86
|
)
|
|
93
87
|
) {
|
|
94
88
|
continue;
|
|
95
89
|
}
|
|
96
90
|
if (
|
|
97
|
-
def.exceptContexts?.some(
|
|
98
|
-
|
|
91
|
+
def.exceptContexts?.some((context) =>
|
|
92
|
+
parentNodes.some((node) => node.startsWith(context)),
|
|
99
93
|
)
|
|
100
94
|
) {
|
|
101
95
|
continue;
|
|
@@ -123,17 +117,10 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
|
|
|
123
117
|
}
|
|
124
118
|
|
|
125
119
|
const slashCompletions: CompletionResult | SlashCompletions | null =
|
|
126
|
-
await this.client
|
|
127
|
-
.completeWithEvent(
|
|
128
|
-
ctx,
|
|
129
|
-
"slash:complete",
|
|
130
|
-
);
|
|
120
|
+
await this.client.completeWithEvent(ctx, "slash:complete");
|
|
131
121
|
|
|
132
122
|
if (slashCompletions) {
|
|
133
|
-
for (
|
|
134
|
-
const slashCompletion of slashCompletions
|
|
135
|
-
.options as SlashCompletionOption[]
|
|
136
|
-
) {
|
|
123
|
+
for (const slashCompletion of slashCompletions.options as SlashCompletionOption[]) {
|
|
137
124
|
options.push({
|
|
138
125
|
label: slashCompletion.label,
|
|
139
126
|
detail: slashCompletion.detail,
|
|
@@ -17,9 +17,9 @@ export class SyscallHook implements Hook<SyscallHookT> {
|
|
|
17
17
|
for (const plug of system.loadedPlugs.values()) {
|
|
18
18
|
const syscalls: SysCallMapping = {};
|
|
19
19
|
|
|
20
|
-
for (
|
|
21
|
-
|
|
22
|
-
) {
|
|
20
|
+
for (const [name, functionDef] of Object.entries(
|
|
21
|
+
plug.manifest!.functions,
|
|
22
|
+
)) {
|
|
23
23
|
if (!functionDef.syscall) {
|
|
24
24
|
continue;
|
|
25
25
|
}
|
|
@@ -11,38 +11,45 @@ export interface ManifestCache<T> {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export class KVPrimitivesManifestCache<T> implements ManifestCache<T> {
|
|
14
|
-
constructor(
|
|
15
|
-
|
|
14
|
+
constructor(
|
|
15
|
+
private kv: KvPrimitives,
|
|
16
|
+
private manifestPrefix: string,
|
|
17
|
+
) {}
|
|
16
18
|
|
|
17
19
|
async getManifest(
|
|
18
20
|
plug: Plug<T>,
|
|
19
21
|
cacheKey: string,
|
|
20
22
|
cacheHash: number,
|
|
21
23
|
): Promise<Manifest<T>> {
|
|
22
|
-
const [cached] = await this.kv.batchGet([[
|
|
23
|
-
this.manifestPrefix,
|
|
24
|
-
cacheKey,
|
|
25
|
-
]]);
|
|
24
|
+
const [cached] = await this.kv.batchGet([[this.manifestPrefix, cacheKey]]);
|
|
26
25
|
if (cached && cached.hash === cacheHash) {
|
|
27
26
|
// console.log("Using KV cached manifest for", plug.name);
|
|
28
27
|
return cached.manifest;
|
|
29
28
|
}
|
|
30
29
|
await plug.sandbox.init();
|
|
31
30
|
const manifest = plug.sandbox.manifest!;
|
|
32
|
-
await this.kv.batchSet([
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
await this.kv.batchSet([
|
|
32
|
+
{
|
|
33
|
+
key: [this.manifestPrefix, cacheKey],
|
|
34
|
+
// Deliverately removing the assets from the manifest to preserve space, will be re-added upon load of actual worker
|
|
35
|
+
value: {
|
|
36
|
+
manifest: { ...manifest, assets: undefined },
|
|
37
|
+
hash: cacheHash,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
]);
|
|
37
41
|
return manifest;
|
|
38
42
|
}
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
export class InMemoryManifestCache<T> implements ManifestCache<T> {
|
|
42
|
-
private cache = new Map<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
private cache = new Map<
|
|
47
|
+
string,
|
|
48
|
+
{
|
|
49
|
+
manifest: Manifest<T>;
|
|
50
|
+
hash: number;
|
|
51
|
+
}
|
|
52
|
+
>();
|
|
46
53
|
|
|
47
54
|
async getManifest(
|
|
48
55
|
plug: Plug<T>,
|