@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.
Files changed (96) hide show
  1. package/README.md +4 -6
  2. package/client/asset_bundle/bundle.ts +3 -9
  3. package/client/data/datastore.ts +4 -5
  4. package/client/markdown_parser/constants.ts +4 -2
  5. package/client/plugos/hooks/code_widget.ts +3 -5
  6. package/client/plugos/hooks/command.ts +54 -24
  7. package/client/plugos/hooks/document_editor.ts +10 -12
  8. package/client/plugos/hooks/event.ts +33 -36
  9. package/client/plugos/hooks/mq.ts +17 -17
  10. package/client/plugos/hooks/plug_namespace.ts +3 -5
  11. package/client/plugos/hooks/slash_command.ts +15 -28
  12. package/client/plugos/hooks/syscall.ts +3 -3
  13. package/client/plugos/manifest_cache.ts +22 -15
  14. package/client/plugos/plug.ts +7 -7
  15. package/client/plugos/plug_compile.ts +119 -67
  16. package/client/plugos/protocol.ts +28 -28
  17. package/client/plugos/proxy_fetch.ts +7 -6
  18. package/client/plugos/sandboxes/worker_sandbox.ts +26 -15
  19. package/client/plugos/syscalls/asset.ts +1 -3
  20. package/client/plugos/syscalls/code_widget.ts +1 -3
  21. package/client/plugos/syscalls/config.ts +10 -7
  22. package/client/plugos/syscalls/datastore.ts +1 -1
  23. package/client/plugos/syscalls/editor.ts +199 -60
  24. package/client/plugos/syscalls/event.ts +9 -12
  25. package/client/plugos/syscalls/fetch.ts +30 -22
  26. package/client/plugos/syscalls/index.ts +11 -2
  27. package/client/plugos/syscalls/jsonschema.ts +54 -32
  28. package/client/plugos/syscalls/language.ts +9 -5
  29. package/client/plugos/syscalls/markdown.ts +29 -7
  30. package/client/plugos/syscalls/mq.ts +3 -11
  31. package/client/plugos/syscalls/service_registry.ts +1 -4
  32. package/client/plugos/syscalls/shell.ts +2 -5
  33. package/client/plugos/syscalls/sync.ts +69 -60
  34. package/client/plugos/syscalls/system.ts +15 -3
  35. package/client/plugos/system.ts +18 -20
  36. package/client/plugos/types.ts +25 -0
  37. package/client/plugos/util.ts +20 -0
  38. package/client/plugos/worker_runtime.ts +4 -3
  39. package/client/space_lua/aggregates.ts +633 -60
  40. package/client/space_lua/ast.ts +21 -9
  41. package/client/space_lua/ast_narrow.ts +4 -2
  42. package/client/space_lua/eval.ts +842 -536
  43. package/client/space_lua/labels.ts +6 -11
  44. package/client/space_lua/numeric.ts +5 -8
  45. package/client/space_lua/parse.ts +290 -169
  46. package/client/space_lua/query_collection.ts +213 -149
  47. package/client/space_lua/render_lua_markdown.ts +369 -0
  48. package/client/space_lua/rp.ts +5 -4
  49. package/client/space_lua/runtime.ts +245 -142
  50. package/client/space_lua/sliq_null.ts +6 -0
  51. package/client/space_lua/stdlib/format.ts +34 -20
  52. package/client/space_lua/stdlib/js.ts +3 -7
  53. package/client/space_lua/stdlib/load.ts +1 -3
  54. package/client/space_lua/stdlib/math.ts +15 -14
  55. package/client/space_lua/stdlib/net.ts +25 -15
  56. package/client/space_lua/stdlib/os.ts +76 -85
  57. package/client/space_lua/stdlib/pattern.ts +28 -35
  58. package/client/space_lua/stdlib/prng.ts +15 -12
  59. package/client/space_lua/stdlib/space_lua.ts +16 -17
  60. package/client/space_lua/stdlib/string.ts +7 -17
  61. package/client/space_lua/stdlib/string_pack.ts +23 -19
  62. package/client/space_lua/stdlib/table.ts +5 -9
  63. package/client/space_lua/stdlib.ts +20 -30
  64. package/client/space_lua/tonumber.ts +79 -40
  65. package/client/space_lua/util.ts +14 -10
  66. package/dist/plug-compile.js +87 -42
  67. package/package.json +24 -22
  68. package/plug-api/constants.ts +20 -0
  69. package/plug-api/lib/async.ts +19 -6
  70. package/plug-api/lib/crypto.ts +5 -6
  71. package/plug-api/lib/dates.ts +15 -7
  72. package/plug-api/lib/json.ts +10 -4
  73. package/plug-api/lib/ref.ts +27 -20
  74. package/plug-api/lib/resolve.ts +7 -11
  75. package/plug-api/lib/shortcut.ts +16 -0
  76. package/plug-api/lib/tags.ts +13 -4
  77. package/plug-api/lib/transclusion.ts +6 -17
  78. package/plug-api/lib/tree.ts +146 -43
  79. package/plug-api/lib/yaml.ts +25 -15
  80. package/plug-api/syscalls/asset.ts +1 -1
  81. package/plug-api/syscalls/config.ts +38 -4
  82. package/plug-api/syscalls/editor.ts +169 -14
  83. package/plug-api/syscalls/index.ts +18 -0
  84. package/plug-api/syscalls/jsonschema.ts +1 -3
  85. package/plug-api/syscalls/lua.ts +3 -9
  86. package/plug-api/syscalls/mq.ts +1 -4
  87. package/plug-api/syscalls/shell.ts +4 -1
  88. package/plug-api/syscalls/space.ts +3 -10
  89. package/plug-api/syscalls/system.ts +16 -4
  90. package/plug-api/syscalls/yaml.ts +2 -6
  91. package/plug-api/types/client.ts +16 -1
  92. package/plug-api/types/event.ts +6 -4
  93. package/plug-api/types/manifest.ts +71 -9
  94. package/plugs/builtin_plugs.ts +3 -3
  95. package/client/plugos/sandboxes/web_worker_sandbox.ts +0 -17
  96. package/dist/worker_runtime_bundle.js +0 -233
package/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
  ![Docker Pulls](https://img.shields.io/docker/pulls/zefhemel/silverbullet)
3
3
  ![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/silverbulletmd/silverbullet/total)
4
4
  ![GitHub contributors](https://img.shields.io/github/contributors/silverbulletmd/silverbullet)
5
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](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
- npm install
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 build
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}`);
@@ -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 = /!?\[(?<title>[^\]]*)\]\((?<url>.+)\)/g;
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"']+)/ig;
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
- const [name, functionDef] of Object.entries(
15
- plug.manifest!.functions,
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 extends EventEmitter<CommandHookEvents>
9
- implements Hook<CommandHookT> {
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
- const [name, functionDef] of Object.entries(
35
- plug.manifest!.functions,
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
- commands.set(cmd.name, {
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
- if (commands.has(name)) {
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
- const [name, functionDef] of Object.entries(
17
- plug.manifest!.functions,
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
- ) => keys.some((key) => extensions.includes(key)));
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
- name,
42
- { extensions: keys, callback: () => plug.invoke(name, []) },
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 || eventNameToRegex(event).test(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((async () => {
91
- try {
92
- return await plug.invoke(name, args);
93
- } catch (e: any) {
94
- console.error(
95
- `Error dispatching event ${eventName} to ${plug.manifest.name}.${name}: ${e.message}`,
96
- );
97
- throw e;
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((async () => {
113
- return await Promise.resolve(localListener(...args));
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((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
- })());
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
- & MQSubscribeOptions
15
- & {
16
- queue: string;
17
- autoAck?: boolean;
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
- const [name, functionDef] of Object.entries(
62
- plug.manifest.functions,
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(queue, messages.map((m) => m.id));
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(queue, messages.map((m) => m.id));
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
- const [funcName, funcDef] of Object.entries(
35
- plug.manifest.functions,
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
- const [name, functionDef] of Object.entries(
37
- plug.manifest!.functions,
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
- const command of Object.values(
55
- this.client.config.get<Record<string, SlashCommand>>(
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
- const currentNode = syntaxTree(ctx.state).resolveInner(ctx.pos);
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
- (context) => parentNodes.some((node) => node.startsWith(context)),
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
- const [name, functionDef] of Object.entries(plug.manifest!.functions)
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(private kv: KvPrimitives, private manifestPrefix: string) {
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
- 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: { manifest: { ...manifest, assets: undefined }, hash: cacheHash },
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<string, {
43
- manifest: Manifest<T>;
44
- hash: number;
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>,