@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
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.
|
|
@@ -22,7 +23,7 @@ And if you are comfortable **programming** a little bit — now we’re really t
|
|
|
22
23
|
Check out the [instructions](https://silverbullet.md/Install).
|
|
23
24
|
|
|
24
25
|
## Developing SilverBullet
|
|
25
|
-
SilverBullet's frontend is written in [TypeScript](https://www.typescriptlang.org/) and built on top of the excellent [CodeMirror 6](https://codemirror.net/) editor component. Additional UI is built using [Preact](https://preactjs.com). [ESBuild](https://esbuild.github.io)
|
|
26
|
+
SilverBullet's frontend is written in [TypeScript](https://www.typescriptlang.org/) and built on top of the excellent [CodeMirror 6](https://codemirror.net/) editor component. Additional UI is built using [Preact](https://preactjs.com). [ESBuild](https://esbuild.github.io) is used to build the frontend.
|
|
26
27
|
|
|
27
28
|
The server backend is written in Go.
|
|
28
29
|
|
|
@@ -43,9 +44,15 @@ If you're considering contributing changes, be aware of the [LLM use policy](htt
|
|
|
43
44
|
* `website/`: silverbullet.md website content
|
|
44
45
|
|
|
45
46
|
### Requirements
|
|
46
|
-
* [
|
|
47
|
+
* [Node.js](https://nodejs.org/) 24+ and npm 10+: Used to build the frontend and plugs
|
|
47
48
|
* [Go](https://go.dev/): Used to build the backend
|
|
48
49
|
|
|
50
|
+
The project includes `.nvmrc` and `.node-version` files. If you use [nvm](https://github.com/nvm-sh/nvm) or another Node version manager, it will automatically use the correct Node.js version:
|
|
51
|
+
|
|
52
|
+
```shell
|
|
53
|
+
nvm use # If using nvm
|
|
54
|
+
```
|
|
55
|
+
|
|
49
56
|
It's convenient to also install [air](https://github.com/air-verse/air) for development, this will automatically rebuild both the frontend and backend when changes are made:
|
|
50
57
|
|
|
51
58
|
```shell
|
|
@@ -53,7 +60,13 @@ go install github.com/air-verse/air@latest
|
|
|
53
60
|
```
|
|
54
61
|
Make sure your `$GOPATH/bin` is in your $PATH.
|
|
55
62
|
|
|
56
|
-
|
|
63
|
+
First, install dependencies:
|
|
64
|
+
|
|
65
|
+
```shell
|
|
66
|
+
make setup
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
To build everything and run the server (which automatically restarts upon file changing):
|
|
57
70
|
|
|
58
71
|
```shell
|
|
59
72
|
air <PATH-TO-YOUR-SPACE>
|
|
@@ -82,10 +95,12 @@ make check
|
|
|
82
95
|
make fmt
|
|
83
96
|
# Run all tests
|
|
84
97
|
make test
|
|
98
|
+
# Run benchmarks
|
|
99
|
+
make bench
|
|
85
100
|
```
|
|
86
101
|
|
|
87
102
|
### Build a docker container
|
|
88
|
-
Note, you do not need
|
|
103
|
+
Note, you do not need Node.js nor Go locally installed for this to work:
|
|
89
104
|
|
|
90
105
|
```shell
|
|
91
106
|
docker build -t silverbullet .
|
|
@@ -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,13 +1,14 @@
|
|
|
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 =
|
|
7
|
-
/(^https?:\/\/([-a-zA-Z0-9@:%_
|
|
8
|
+
/(^https?:\/\/([-a-zA-Z0-9@:%_+~#=]|(?:[.](?!(\s|$)))){1,256})(([-a-zA-Z0-9(@:%_+~#?&=/]|(?:[.,:;)](?!(\s|$))))*)/;
|
|
8
9
|
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 pWikiLinkRegex = new RegExp(
|
|
13
|
+
export const frontmatterMailtoRegex = /(mailto:[^@\s]+@[^@\s"']+)/gi;
|
|
14
|
+
export const pWikiLinkRegex = new RegExp(`^${wikiLinkRegex.source}`); // Modified regex used only in parser
|
|
@@ -7,17 +7,12 @@ export class CodeWidgetHook implements Hook<CodeWidgetT> {
|
|
|
7
7
|
codeWidgetCallbacks = new Map<string, CodeWidgetCallback>();
|
|
8
8
|
codeWidgetModes = new Map<string, "markdown" | "iframe">();
|
|
9
9
|
|
|
10
|
-
constructor() {
|
|
11
|
-
}
|
|
12
|
-
|
|
13
10
|
collectAllCodeWidgets(system: System<CodeWidgetT>) {
|
|
14
11
|
this.codeWidgetCallbacks.clear();
|
|
15
12
|
for (const plug of system.loadedPlugs.values()) {
|
|
16
|
-
for (
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
)
|
|
20
|
-
) {
|
|
13
|
+
for (const [name, functionDef] of Object.entries(
|
|
14
|
+
plug.manifest!.functions,
|
|
15
|
+
)) {
|
|
21
16
|
if (!functionDef.codeWidget) {
|
|
22
17
|
continue;
|
|
23
18
|
}
|
|
@@ -5,8 +5,10 @@ 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();
|
|
@@ -30,11 +32,9 @@ export class CommandHook extends EventEmitter<CommandHookEvents>
|
|
|
30
32
|
return commands;
|
|
31
33
|
}
|
|
32
34
|
for (const plug of this.system.loadedPlugs.values()) {
|
|
33
|
-
for (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
)
|
|
37
|
-
) {
|
|
35
|
+
for (const [name, functionDef] of Object.entries(
|
|
36
|
+
plug.manifest!.functions,
|
|
37
|
+
)) {
|
|
38
38
|
if (!functionDef.command) {
|
|
39
39
|
continue;
|
|
40
40
|
}
|
|
@@ -46,7 +46,7 @@ export class CommandHook extends EventEmitter<CommandHookEvents>
|
|
|
46
46
|
commands.set(cmd.name, {
|
|
47
47
|
...cmd,
|
|
48
48
|
run: (args?: string[]) => {
|
|
49
|
-
return plug.invoke(name, [cmd, ...args ?? []]);
|
|
49
|
+
return plug.invoke(name, [cmd, ...(args ?? [])]);
|
|
50
50
|
},
|
|
51
51
|
});
|
|
52
52
|
}
|
|
@@ -9,17 +9,12 @@ export class DocumentEditorHook implements Hook<DocumentEditorT> {
|
|
|
9
9
|
{ extensions: string[]; callback: DocumentEditorCallback }
|
|
10
10
|
>();
|
|
11
11
|
|
|
12
|
-
constructor() {
|
|
13
|
-
}
|
|
14
|
-
|
|
15
12
|
collectAllDocumentEditors(system: System<DocumentEditorT>) {
|
|
16
13
|
this.documentEditors.clear();
|
|
17
14
|
for (const plug of system.loadedPlugs.values()) {
|
|
18
|
-
for (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
)
|
|
22
|
-
) {
|
|
15
|
+
for (const [name, functionDef] of Object.entries(
|
|
16
|
+
plug.manifest!.functions,
|
|
17
|
+
)) {
|
|
23
18
|
if (!functionDef.editor) {
|
|
24
19
|
continue;
|
|
25
20
|
}
|
|
@@ -28,9 +23,9 @@ export class DocumentEditorHook implements Hook<DocumentEditorT> {
|
|
|
28
23
|
? functionDef.editor
|
|
29
24
|
: [functionDef.editor];
|
|
30
25
|
|
|
31
|
-
const conflict = Array.from(this.documentEditors.entries()).find(
|
|
32
|
-
[_, { extensions }],
|
|
33
|
-
)
|
|
26
|
+
const conflict = Array.from(this.documentEditors.entries()).find(
|
|
27
|
+
([_, { extensions }]) => keys.some((key) => extensions.includes(key)),
|
|
28
|
+
);
|
|
34
29
|
|
|
35
30
|
if (conflict) {
|
|
36
31
|
console.log(
|
|
@@ -40,10 +35,10 @@ export class DocumentEditorHook implements Hook<DocumentEditorT> {
|
|
|
40
35
|
);
|
|
41
36
|
}
|
|
42
37
|
|
|
43
|
-
this.documentEditors.set(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
);
|
|
38
|
+
this.documentEditors.set(name, {
|
|
39
|
+
extensions: keys,
|
|
40
|
+
callback: () => plug.invoke(name, []),
|
|
41
|
+
});
|
|
47
42
|
}
|
|
48
43
|
}
|
|
49
44
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// deno-lint-ignore-file ban-types
|
|
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
|
-
// deno-lint-ignore-file ban-types
|
|
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(
|
|
@@ -15,9 +15,6 @@ type SpaceFunction = {
|
|
|
15
15
|
export class PlugNamespaceHook implements Hook<PlugNamespaceHookT> {
|
|
16
16
|
spaceFunctions: SpaceFunction[] = [];
|
|
17
17
|
|
|
18
|
-
constructor() {
|
|
19
|
-
}
|
|
20
|
-
|
|
21
18
|
apply(system: System<PlugNamespaceHookT>): void {
|
|
22
19
|
system.on({
|
|
23
20
|
plugLoaded: () => {
|
|
@@ -33,11 +30,9 @@ export class PlugNamespaceHook implements Hook<PlugNamespaceHookT> {
|
|
|
33
30
|
this.spaceFunctions = [];
|
|
34
31
|
for (const plug of system.loadedPlugs.values()) {
|
|
35
32
|
if (plug.manifest?.functions) {
|
|
36
|
-
for (
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
)
|
|
40
|
-
) {
|
|
33
|
+
for (const [funcName, funcDef] of Object.entries(
|
|
34
|
+
plug.manifest.functions,
|
|
35
|
+
)) {
|
|
41
36
|
if (funcDef.pageNamespace) {
|
|
42
37
|
this.spaceFunctions.push({
|
|
43
38
|
operation: funcDef.pageNamespace.operation,
|
|
@@ -15,7 +15,7 @@ import type {
|
|
|
15
15
|
SlashCompletions,
|
|
16
16
|
} from "@silverbulletmd/silverbullet/type/client";
|
|
17
17
|
|
|
18
|
-
const slashCommandRegexp = /([^\w:]|^)\/[\w
|
|
18
|
+
const slashCommandRegexp = /([^\w:]|^)\/[\w#-]*/;
|
|
19
19
|
|
|
20
20
|
export class SlashCommandHook implements Hook<SlashCommandHookT> {
|
|
21
21
|
slashCommands: SlashCommand[] = [];
|
|
@@ -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
|
}
|
|
@@ -88,14 +80,14 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
|
|
|
88
80
|
if (
|
|
89
81
|
def.onlyContexts &&
|
|
90
82
|
!def.onlyContexts.some((context) =>
|
|
91
|
-
parentNodes.some((node) => node.startsWith(context))
|
|
83
|
+
parentNodes.some((node) => node.startsWith(context)),
|
|
92
84
|
)
|
|
93
85
|
) {
|
|
94
86
|
continue;
|
|
95
87
|
}
|
|
96
88
|
if (
|
|
97
|
-
def.exceptContexts
|
|
98
|
-
|
|
89
|
+
def.exceptContexts?.some((context) =>
|
|
90
|
+
parentNodes.some((node) => node.startsWith(context)),
|
|
99
91
|
)
|
|
100
92
|
) {
|
|
101
93
|
continue;
|
|
@@ -123,17 +115,10 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
|
|
|
123
115
|
}
|
|
124
116
|
|
|
125
117
|
const slashCompletions: CompletionResult | SlashCompletions | null =
|
|
126
|
-
await this.client
|
|
127
|
-
.completeWithEvent(
|
|
128
|
-
ctx,
|
|
129
|
-
"slash:complete",
|
|
130
|
-
);
|
|
118
|
+
await this.client.completeWithEvent(ctx, "slash:complete");
|
|
131
119
|
|
|
132
120
|
if (slashCompletions) {
|
|
133
|
-
for (
|
|
134
|
-
const slashCompletion of slashCompletions
|
|
135
|
-
.options as SlashCompletionOption[]
|
|
136
|
-
) {
|
|
121
|
+
for (const slashCompletion of slashCompletions.options as SlashCompletionOption[]) {
|
|
137
122
|
options.push({
|
|
138
123
|
label: slashCompletion.label,
|
|
139
124
|
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>,
|
package/client/plugos/plug.ts
CHANGED
|
@@ -26,10 +26,7 @@ export class Plug<HookT> {
|
|
|
26
26
|
cacheHash: number,
|
|
27
27
|
sandboxFactory: SandboxFactory<HookT>,
|
|
28
28
|
): Promise<Plug<HookT>> {
|
|
29
|
-
const plug = new Plug(
|
|
30
|
-
system,
|
|
31
|
-
sandboxFactory,
|
|
32
|
-
);
|
|
29
|
+
const plug = new Plug(system, sandboxFactory);
|
|
33
30
|
|
|
34
31
|
// Retrieve the manifest, which may either come from a cache or be loaded from the worker
|
|
35
32
|
plug.manifest = await system.options.manifestCache!.getManifest(
|
|
@@ -70,7 +67,6 @@ export class Plug<HookT> {
|
|
|
70
67
|
const sandbox = this.sandbox!;
|
|
71
68
|
if (funDef.redirect) {
|
|
72
69
|
// Function redirect, look up
|
|
73
|
-
// deno-lint-ignore no-this-alias
|
|
74
70
|
let plug: Plug<HookT> | undefined = this;
|
|
75
71
|
if (funDef.redirect.indexOf(".") !== -1) {
|
|
76
72
|
const [plugName, functionName] = funDef.redirect.split(".");
|
|
@@ -84,7 +80,7 @@ export class Plug<HookT> {
|
|
|
84
80
|
}
|
|
85
81
|
return plug.invoke(name, args);
|
|
86
82
|
}
|
|
87
|
-
if (!await this.canInvoke(name)) {
|
|
83
|
+
if (!(await this.canInvoke(name))) {
|
|
88
84
|
throw new Error(
|
|
89
85
|
`Function ${name} is not available in ${this.runtimeEnv}`,
|
|
90
86
|
);
|