@silverbulletmd/silverbullet 2.9.0 → 2.10.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 (63) hide show
  1. package/README.md +36 -23
  2. package/client/plugos/hooks/syscall.ts +24 -10
  3. package/client/plugos/syscalls/asset.ts +61 -23
  4. package/client/plugos/syscalls/clientStore.ts +32 -6
  5. package/client/plugos/syscalls/client_code_widget.ts +9 -4
  6. package/client/plugos/syscalls/code_widget.ts +26 -11
  7. package/client/plugos/syscalls/config.ts +146 -30
  8. package/client/plugos/syscalls/datastore.ts +146 -40
  9. package/client/plugos/syscalls/editor.ts +1432 -711
  10. package/client/plugos/syscalls/event.ts +46 -13
  11. package/client/plugos/syscalls/fetch.ts +123 -82
  12. package/client/plugos/syscalls/index.ts +242 -116
  13. package/client/plugos/syscalls/jsonschema.ts +107 -8
  14. package/client/plugos/syscalls/language.ts +38 -12
  15. package/client/plugos/syscalls/markdown.ts +166 -50
  16. package/client/plugos/syscalls/mq.ts +93 -20
  17. package/client/plugos/syscalls/schema_introspection.ts +33 -0
  18. package/client/plugos/syscalls/service_registry.ts +74 -20
  19. package/client/plugos/syscalls/shell.ts +51 -20
  20. package/client/plugos/syscalls/space.ts +169 -113
  21. package/client/plugos/syscalls/sync.ts +50 -19
  22. package/client/plugos/syscalls/system.ts +236 -139
  23. package/client/plugos/system.ts +21 -5
  24. package/client/space_lua/api_documentation.ts +107 -0
  25. package/client/space_lua/ast.ts +13 -0
  26. package/client/space_lua/eval.ts +12 -4
  27. package/client/space_lua/parse.ts +243 -108
  28. package/client/space_lua/pretty_print.ts +128 -54
  29. package/client/space_lua/query_collection.ts +4 -4
  30. package/client/space_lua/render_widget.ts +156 -0
  31. package/client/space_lua/runtime.ts +83 -22
  32. package/client/space_lua/stdlib/crypto.ts +8 -3
  33. package/client/space_lua/stdlib/encoding.ts +27 -9
  34. package/client/space_lua/stdlib/js.ts +137 -22
  35. package/client/space_lua/stdlib/load.ts +24 -15
  36. package/client/space_lua/stdlib/math.ts +323 -118
  37. package/client/space_lua/stdlib/net.ts +57 -9
  38. package/client/space_lua/stdlib/os.ts +111 -44
  39. package/client/space_lua/stdlib/space_lua.ts +344 -20
  40. package/client/space_lua/stdlib/string.ts +282 -104
  41. package/client/space_lua/stdlib/string_pack.ts +58 -19
  42. package/client/space_lua/stdlib/table.ts +195 -40
  43. package/client/space_lua/stdlib.ts +335 -93
  44. package/client/space_lua/syscalls.ts +270 -0
  45. package/dist/plug-compile.js +1 -1
  46. package/package.json +3 -3
  47. package/plug-api/lib/ref.ts +96 -0
  48. package/plug-api/lib/shortcut.ts +2 -1
  49. package/plug-api/syscalls/config.ts +5 -3
  50. package/plug-api/syscalls/editor.ts +16 -0
  51. package/plug-api/syscalls/index.ts +17 -1
  52. package/plug-api/syscalls/jsonschema.ts +9 -0
  53. package/plug-api/syscalls/lua.ts +7 -0
  54. package/plug-api/syscalls/shell.ts +0 -1
  55. package/plug-api/syscalls/system.ts +7 -0
  56. package/plug-api/system_mock.ts +2 -2
  57. package/plug-api/types/index.ts +52 -1
  58. package/plug-api/types/manifest.ts +22 -4
  59. package/plug-api/ui/cx.ts +1 -3
  60. package/plug-api/ui/index.ts +3 -0
  61. package/plug-api/ui/panel_styles.ts +4 -3
  62. package/plug-api/ui/slugify.ts +44 -0
  63. package/client/plugos/syscalls/lua.ts +0 -81
package/README.md CHANGED
@@ -27,27 +27,30 @@ You were told there’s no such thing as a silver bullet. You were told wrong.
27
27
  Check out the [instructions](https://silverbullet.md/Install).
28
28
 
29
29
  ## Developing SilverBullet
30
- 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.
30
+ SilverBullet's frontend ("client") 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.
31
31
 
32
- The server backend is written in Go.
32
+ The server backend is written in [Rust](https://www.rust-lang.org/) (a Cargo workspace). It serves the pre-built client bundle, the file/space HTTP API, authentication, and a headless-Chrome runtime for server-side Lua.
33
33
 
34
34
  ## Code structure
35
- * `client/`: The SilverBullet client, implemented with TypeScript
36
- * `server/`: The SilverBullet server, written in Go
37
- * `plugs`: Set of built-in plugs that are distributed with SilverBullet
38
- * `libraries`: A set of libraries (space scripts, page templates, slash templates) distributed with SilverBullet
39
- * `plug-api/`: Useful APIs for use in plugs
35
+ * `bin/silverbullet/`: The standalone `silverbullet` server binary (Rust) (this is the one you generally run)
36
+ * `bin/sb/`: The `sb` CLI client (Rust)
37
+ * `bin/plug-compile.ts`: the plug compiler
38
+ * `client/`: The SilverBullet client (browser, TypeScript)
39
+ * `server/`: The SilverBullet server library (Rust): HTTP router, handlers, auth, runtime seam
40
+ * `server-common/`: Shared Rust crate (space primitives, shared types)
41
+ * `server-runtime-chrome/`: Headless-Chrome runtime backend (Rust)
42
+ * `plugs`: Set of built-in plugs (TypeScript) that are distributed with SilverBullet
43
+ * `libraries`: A set of libraries (space scripts, page templates, slash templates) distributed with SilverBullet (Space Lua)
44
+ * `plug-api/`: Useful APIs for use in plugs and published to NPM
40
45
  * `lib/`: Useful libraries to be used in plugs
41
46
  * `syscalls/`: TypeScript wrappers around syscalls
42
47
  * `types/`: Various (client) types that can be references from plugs
43
- * `bin`
44
- * `plug_compile.ts` the plug compiler
45
48
  * `scripts/`: Useful scripts
46
- * `website/`: silverbullet.md website content
49
+ * `docs/`: documentation (also serves a silverbullet.md website content)
47
50
 
48
51
  ### Requirements
49
- * [Node.js](https://nodejs.org/) 24+ and npm 10+: Used to build the frontend and plugs
50
- * [Go](https://go.dev/): Used to build the backend
52
+ * [Node.js](https://nodejs.org/) 24+ and npm 10+: Used to build the frontend (client) and plugs
53
+ * [Rust](https://www.rust-lang.org/tools/install) (stable, via `rustup`): Used to build the server
51
54
 
52
55
  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:
53
56
 
@@ -55,35 +58,45 @@ The project includes `.nvmrc` and `.node-version` files. If you use [nvm](https:
55
58
  nvm use # If using nvm
56
59
  ```
57
60
 
58
- 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:
61
+ First, install dependencies:
59
62
 
60
63
  ```shell
61
- go install github.com/air-verse/air@latest
64
+ make setup
62
65
  ```
63
- Make sure your `$GOPATH/bin` is in your $PATH.
64
66
 
65
- First, install dependencies:
67
+ To create a (release) build:
66
68
 
67
69
  ```shell
68
- make setup
70
+ make
69
71
  ```
70
72
 
71
- To build everything and run the server (which automatically restarts upon file changing):
73
+ This will produce `silverbullet` and `cli` binaries in `./target/release/`, run `./target/release/silverbullet` to run the server.
74
+
75
+ ### Development workflow
76
+
77
+ SilverBullet has two halves you rebuild **independently**:
78
+
79
+ * The **client** (TypeScript: `client/`) is built by ESBuild into `client_bundle/`, which the server then serves.
80
+ * The **server** (Rust: `server/`, `server-common/`, `server-runtime-chrome/`, `bin/silverbullet/`) is a compiled binary.
81
+
82
+ Run the server in development with `cargo run`. A **debug** build serves the client bundle **live from `client_bundle/` on disk** (a release build embeds it instead):
72
83
 
73
84
  ```shell
74
- air <PATH-TO-YOUR-SPACE>
85
+ npm run build
86
+ cargo run <PATH-TO-YOUR-SPACE>
75
87
  ```
76
88
 
77
- Alternatively, to build:
89
+ **When you change only the client** (TypeScript in `client/`): you do **not** need to restart the server. Rebuild just the client bundle and reload the page in your browser — the debug server serves the freshly-built bundle straight from disk:
78
90
 
79
91
  ```shell
80
- make
92
+ npm run build
81
93
  ```
82
94
 
83
- To run the resulting server:
95
+ To build a self-contained **release** binary (with the client bundle embedded), and run it:
84
96
 
85
97
  ```shell
86
- ./silverbullet <PATH-TO-YOUR-SPACE>
98
+ make
99
+ ./target/release/silverbullet <PATH-TO-YOUR-SPACE>
87
100
  ```
88
101
 
89
102
  ### Useful development tasks
@@ -1,5 +1,5 @@
1
1
  import type { Hook, Manifest } from "../types.ts";
2
- import type { SysCallMapping, System } from "../system.ts";
2
+ import type { SysCallMapping, SyscallContext, System } from "../system.ts";
3
3
  import type { SyscallHookT } from "@silverbulletmd/silverbullet/type/manifest";
4
4
 
5
5
  export class SyscallHook implements Hook<SyscallHookT> {
@@ -24,16 +24,27 @@ export class SyscallHook implements Hook<SyscallHookT> {
24
24
  continue;
25
25
  }
26
26
 
27
- const syscallName = functionDef.syscall;
27
+ const syscallDefinition = functionDef.syscall;
28
+ const syscallName =
29
+ typeof syscallDefinition === "string"
30
+ ? syscallDefinition
31
+ : syscallDefinition.name;
28
32
 
29
33
  // Add the syscall to our mapping
30
- syscalls[syscallName] = (ctx, ...args) => {
31
- // Delegate to the system to invoke the function
32
- return system.syscall(ctx, "system.invokeFunction", [
34
+ const callback = (ctx: SyscallContext, ...args: any[]) =>
35
+ system.syscall(ctx, "system.invokeFunction", [
33
36
  `${plug.manifest!.name}.${name}`,
34
37
  ...args,
35
38
  ]);
36
- };
39
+ if (typeof syscallDefinition === "string") {
40
+ syscalls[syscallName] = callback;
41
+ } else {
42
+ const { name: _name, ...metadata } = syscallDefinition;
43
+ syscalls[syscallName] = {
44
+ callback,
45
+ ...metadata,
46
+ };
47
+ }
37
48
 
38
49
  // Register the syscalls with no required permissions
39
50
  system.registerSyscalls([], syscalls);
@@ -48,16 +59,19 @@ export class SyscallHook implements Hook<SyscallHookT> {
48
59
  continue;
49
60
  }
50
61
 
51
- // Validate syscall name is provided
52
- if (!functionDef.syscall) {
62
+ const syscallName =
63
+ typeof functionDef.syscall === "string"
64
+ ? functionDef.syscall
65
+ : functionDef.syscall.name;
66
+ if (!syscallName) {
53
67
  errors.push(`Function ${name} has a syscall but no name`);
54
68
  continue;
55
69
  }
56
70
 
57
71
  // Validate syscall name format (should be namespaced)
58
- if (!functionDef.syscall.includes(".")) {
72
+ if (!syscallName.includes(".")) {
59
73
  errors.push(
60
- `Function ${name} has invalid syscall name "${functionDef.syscall}" - must be in format "namespace.name"`,
74
+ `Function ${name} has invalid syscall name "${syscallName}" - must be in format "namespace.name"`,
61
75
  );
62
76
  }
63
77
  }
@@ -3,31 +3,69 @@ import type { FileMeta } from "../../../plug-api/types/index.ts";
3
3
 
4
4
  export default function assetSyscalls(system: System<any>): SysCallMapping {
5
5
  return {
6
- "asset.readAsset": (_ctx, plugName: string, name: string): string => {
7
- return system.loadedPlugs.get(plugName)!.assets!.readFileAsDataUrl(name);
6
+ "asset.readAsset": {
7
+ callback: (_ctx, plugName: string, name: string): string => {
8
+ return system.loadedPlugs
9
+ .get(plugName)!
10
+ .assets!.readFileAsDataUrl(name);
11
+ },
12
+ description: "Reads an asset embedded in a plug as a data URL.",
13
+ parameters: [
14
+ { name: "plugName", type: "string", description: "Plug name." },
15
+ { name: "name", type: "string", description: "Asset path." },
16
+ ],
17
+ returns: [{ type: "string", description: "Asset data URL." }],
18
+ examples: [
19
+ { code: 'local image = asset.readAsset("myplug", "image.png")' },
20
+ ],
8
21
  },
9
- "asset.listFiles": (_ctx, plugName: string): FileMeta[] => {
10
- const assets = system.loadedPlugs.get(plugName)!.assets!;
11
- const fileNames = assets.listFiles();
12
- return fileNames.map((name) => ({
13
- name,
14
- contentType: assets.getMimeType(name),
15
- created: assets.getMtime(name),
16
- lastModified: assets.getMtime(name),
17
- size: -1,
18
- perm: "ro",
19
- }));
22
+ "asset.listFiles": {
23
+ callback: (_ctx, plugName: string): FileMeta[] => {
24
+ const assets = system.loadedPlugs.get(plugName)!.assets!;
25
+ const fileNames = assets.listFiles();
26
+ return fileNames.map((name) => ({
27
+ name,
28
+ contentType: assets.getMimeType(name),
29
+ created: assets.getMtime(name),
30
+ lastModified: assets.getMtime(name),
31
+ size: -1,
32
+ perm: "ro",
33
+ }));
34
+ },
35
+ description: "Lists the assets embedded in a plug.",
36
+ parameters: [
37
+ { name: "plugName", type: "string", description: "Plug name." },
38
+ ],
39
+ returns: [{ type: "table", description: "List of file metadata." }],
40
+ examples: [
41
+ {
42
+ code: 'for _, file in ipairs(asset.listFiles("myplug")) do\n print(file.name)\nend',
43
+ },
44
+ ],
20
45
  },
21
- "asset.getFileMeta": (_ctx, plugName: string, name: string): FileMeta => {
22
- const assets = system.loadedPlugs.get(plugName)!.assets!;
23
- return {
24
- name,
25
- contentType: assets.getMimeType(name),
26
- created: assets.getMtime(name),
27
- lastModified: assets.getMtime(name),
28
- size: -1,
29
- perm: "ro",
30
- };
46
+ "asset.getFileMeta": {
47
+ callback: (_ctx, plugName: string, name: string): FileMeta => {
48
+ const assets = system.loadedPlugs.get(plugName)!.assets!;
49
+ return {
50
+ name,
51
+ contentType: assets.getMimeType(name),
52
+ created: assets.getMtime(name),
53
+ lastModified: assets.getMtime(name),
54
+ size: -1,
55
+ perm: "ro",
56
+ };
57
+ },
58
+ description: "Gets metadata for an asset embedded in a plug.",
59
+ parameters: [
60
+ { name: "plugName", type: "string", description: "Plug name." },
61
+ { name: "name", type: "string", description: "Asset path." },
62
+ ],
63
+ returns: [{ type: "table", description: "File metadata." }],
64
+ examples: [
65
+ {
66
+ code: 'local meta = asset.getFileMeta("myplug", "data.txt")\nprint(meta.lastModified)',
67
+ },
68
+ ],
31
69
  },
32
70
  };
33
71
  }
@@ -8,14 +8,40 @@ export function clientStoreSyscalls(
8
8
  prefix: KvKey = ["client"],
9
9
  ): SysCallMapping {
10
10
  return {
11
- "clientStore.get": (_ctx, key: string): Promise<any> => {
12
- return ds.get([...prefix, key]);
11
+ "clientStore.get": {
12
+ callback: (_ctx, key: string): Promise<any> => {
13
+ return ds.get([...prefix, key]);
14
+ },
15
+ description:
16
+ "Gets a client-specific value from the local key-value store.",
17
+ parameters: [
18
+ { name: "key", type: "string", description: "Key to read." },
19
+ ],
20
+ returns: [{ description: "Stored value, or nil when absent." }],
21
+ examples: [{ code: 'local theme = clientStore.get("theme")' }],
13
22
  },
14
- "clientStore.set": (_ctx, key: string, val: any): Promise<void> => {
15
- return ds.set([...prefix, key], val);
23
+ "clientStore.set": {
24
+ callback: (_ctx, key: string, val: any): Promise<void> => {
25
+ return ds.set([...prefix, key], val);
26
+ },
27
+ description:
28
+ "Stores a client-specific value in the local key-value store.",
29
+ parameters: [
30
+ { name: "key", type: "string", description: "Key to set." },
31
+ { name: "value", description: "Value to store." },
32
+ ],
33
+ examples: [{ code: 'clientStore.set("theme", "dark")' }],
16
34
  },
17
- "clientStore.delete": (_ctx, key: string): Promise<void> => {
18
- return ds.delete([...prefix, key]);
35
+ "clientStore.delete": {
36
+ callback: (_ctx, key: string): Promise<void> => {
37
+ return ds.delete([...prefix, key]);
38
+ },
39
+ description:
40
+ "Deletes a client-specific value from the local key-value store.",
41
+ parameters: [
42
+ { name: "key", type: "string", description: "Key to delete." },
43
+ ],
44
+ examples: [{ code: 'clientStore.delete("theme")' }],
19
45
  },
20
46
  };
21
47
  }
@@ -5,10 +5,15 @@ import { broadcastReload } from "../../components/widget_sandbox_iframe.ts";
5
5
 
6
6
  export function clientCodeWidgetSyscalls(client: Client): SysCallMapping {
7
7
  return {
8
- "codeWidget.refreshAll": () => {
9
- client.widgetCache.clearPrewarm();
10
- broadcastReload();
11
- return reloadAllWidgets();
8
+ "codeWidget.refreshAll": {
9
+ callback: () => {
10
+ client.widgetCache.clearPrewarm();
11
+ broadcastReload();
12
+ return reloadAllWidgets();
13
+ },
14
+ description:
15
+ "Refreshes all code widgets on the current page that support refreshing.",
16
+ examples: [{ code: "codeWidget.refreshAll()" }],
12
17
  },
13
18
  };
14
19
  }
@@ -6,17 +6,32 @@ export function codeWidgetSyscalls(
6
6
  codeWidgetHook: CodeWidgetHook,
7
7
  ): SysCallMapping {
8
8
  return {
9
- "codeWidget.render": (
10
- _ctx,
11
- lang: string,
12
- body: string,
13
- pageName: string,
14
- ): Promise<CodeWidgetContent | null> => {
15
- const langCallback = codeWidgetHook.codeWidgetCallbacks.get(lang);
16
- if (!langCallback) {
17
- throw new Error(`Code widget ${lang} not found`);
18
- }
19
- return langCallback(body, pageName);
9
+ "codeWidget.render": {
10
+ callback: (
11
+ _ctx,
12
+ lang: string,
13
+ body: string,
14
+ pageName: string,
15
+ ): Promise<CodeWidgetContent | null> => {
16
+ const langCallback = codeWidgetHook.codeWidgetCallbacks.get(lang);
17
+ if (!langCallback) {
18
+ throw new Error(`Code widget ${lang} not found`);
19
+ }
20
+ return langCallback(body, pageName);
21
+ },
22
+ description: "Renders code through the widget registered for a language.",
23
+ parameters: [
24
+ { name: "language", type: "string", description: "Widget language." },
25
+ { name: "body", type: "string", description: "Code block body." },
26
+ {
27
+ name: "pageName",
28
+ type: "string",
29
+ description: "Containing page name.",
30
+ },
31
+ ],
32
+ returns: [
33
+ { type: "table", description: "Rendered widget content, or nil." },
34
+ ],
20
35
  },
21
36
  };
22
37
  }
@@ -5,45 +5,161 @@ import { stripFunctions } from "../util.ts";
5
5
 
6
6
  export function configSyscalls(config: Config): SysCallMapping {
7
7
  return {
8
- "config.get": (_ctx, path: string, defaultValue: any) => {
9
- return config.get(path, defaultValue);
8
+ "config.get": {
9
+ callback: (_ctx, path: string, defaultValue: any) => {
10
+ return config.get(path, defaultValue);
11
+ },
12
+ description:
13
+ "Gets a configuration value by path, with dot notation support.",
14
+ parameters: [
15
+ { name: "path", type: "string", description: "Configuration path." },
16
+ {
17
+ name: "defaultValue",
18
+ description: "Value returned when the path is absent.",
19
+ },
20
+ ],
21
+ returns: [{ description: "Configured value or the supplied default." }],
22
+ examples: [{ code: 'local theme = config.get("theme", "light")' }],
10
23
  },
11
- "config.set": (
12
- _ctx,
13
- keyOrValues: string | string[] | Record<string, any>,
14
- value?: any,
15
- ) => {
16
- config.set(keyOrValues as any, value);
24
+ "config.set": {
25
+ callback: (
26
+ _ctx,
27
+ keyOrValues: string | string[] | Record<string, any>,
28
+ value?: any,
29
+ ) => {
30
+ config.set(keyOrValues as any, value);
31
+ },
32
+ description: "Sets one configuration value or multiple values at once.",
33
+ signatures: ["config.set(path, value)", "config.set(values)"],
34
+ parameters: [
35
+ {
36
+ name: "pathOrValues",
37
+ description: "Configuration path or table of values.",
38
+ },
39
+ {
40
+ name: "value",
41
+ description: "Value to set when a path is supplied.",
42
+ optional: true,
43
+ },
44
+ ],
45
+ examples: [
46
+ { code: 'config.set("theme", "dark")' },
47
+ { code: 'config.set({theme = "dark", fontSize = 14})' },
48
+ ],
17
49
  },
18
- "lua:config.setLuaValue": async (
19
- _ctx,
20
- keyOrValues: string | string[] | Record<string, any>,
21
- value?: any,
22
- ) => {
23
- // This is for special cases where we explicitly want to NOT convert a value to a JS value, but maintain its Lua version (mostly for LuaTables) — main use case: metatable
24
- keyOrValues = await luaValueToJS(keyOrValues, LuaStackFrame.lostFrame);
25
- config.set(keyOrValues as any, value);
50
+ "lua:config.setLuaValue": {
51
+ callback: async (
52
+ _ctx,
53
+ keyOrValues: string | string[] | Record<string, any>,
54
+ value?: any,
55
+ ) => {
56
+ // This is for special cases where we explicitly want to NOT convert a value to a JS value, but maintain its Lua version (mostly for LuaTables) — main use case: metatable
57
+ keyOrValues = await luaValueToJS(keyOrValues, LuaStackFrame.lostFrame);
58
+ config.set(keyOrValues as any, value);
59
+ },
60
+ description:
61
+ "Sets configuration while preserving the supplied Lua value representation.",
62
+ signatures: [
63
+ "config.setLuaValue(path, value)",
64
+ "config.setLuaValue(values)",
65
+ ],
66
+ parameters: [
67
+ {
68
+ name: "pathOrValues",
69
+ description: "Configuration path or table of values.",
70
+ },
71
+ {
72
+ name: "value",
73
+ description: "Lua value to preserve.",
74
+ optional: true,
75
+ },
76
+ ],
26
77
  },
27
- "config.insert": (_ctx, key: string | string[], value: any) => {
28
- config.insert(key, value);
78
+ "config.insert": {
79
+ callback: (_ctx, key: string | string[], value: any) => {
80
+ config.insert(key, value);
81
+ },
82
+ description: "Appends a value to the configuration array at a path.",
83
+ parameters: [
84
+ { name: "path", description: "Configuration path." },
85
+ { name: "value", description: "Value to append." },
86
+ ],
29
87
  },
30
- "config.has": (_ctx, path: string) => {
31
- return config.has(path);
88
+ "config.has": {
89
+ callback: (_ctx, path: string) => {
90
+ return config.has(path);
91
+ },
92
+ description: "Checks whether a configuration path exists.",
93
+ parameters: [
94
+ { name: "path", type: "string", description: "Configuration path." },
95
+ ],
96
+ returns: [{ type: "boolean", description: "Whether the path exists." }],
32
97
  },
33
- "config.define": (_ctx, key: string, schema: any) => {
34
- config.define(key, schema);
98
+ "config.define": {
99
+ callback: (_ctx, key: string, schema: any) => {
100
+ config.define(key, schema);
101
+ },
102
+ description: "Defines a JSON schema for a configuration key.",
103
+ parameters: [
104
+ { name: "key", type: "string", description: "Configuration key." },
105
+ {
106
+ name: "schema",
107
+ type: "table",
108
+ description:
109
+ "JSON Schema definition; default applies a missing value and ui annotations expose it in the Configuration Manager.",
110
+ },
111
+ ],
112
+ examples: [
113
+ {
114
+ code: 'config.define("shortWikiLinks", {\n description = "Render short wiki link labels",\n type = "boolean",\n default = true,\n ui = {category = "Editor", label = "Short wiki links", priority = 1},\n})',
115
+ },
116
+ ],
35
117
  },
36
- "config.defineCategory": (_ctx, definition: any) => {
37
- config.defineCategory(definition);
118
+ "config.defineCategory": {
119
+ callback: (_ctx, definition: any) => {
120
+ config.defineCategory(definition);
121
+ },
122
+ description: "Defines or updates a Configuration Manager UI category.",
123
+ parameters: [
124
+ {
125
+ name: "definition",
126
+ type: "table",
127
+ description: "Category name, description, and priority.",
128
+ },
129
+ ],
130
+ examples: [
131
+ {
132
+ code: 'config.defineCategory {\n name = "Editor",\n description = "Page editor behavior.",\n priority = 50,\n}',
133
+ },
134
+ ],
38
135
  },
39
- "config.getValues": () => {
40
- return stripFunctions(config.values);
136
+ "config.getValues": {
137
+ callback: () => {
138
+ return stripFunctions(config.values);
139
+ },
140
+ description: "Gets all configuration values as a single table.",
141
+ returns: [{ type: "table", description: "All configuration values." }],
41
142
  },
42
- "config.getSchemas": () => {
43
- return stripFunctions(config.schemas);
143
+ "config.getSchemas": {
144
+ callback: () => {
145
+ return stripFunctions(config.schemas);
146
+ },
147
+ description: "Gets all defined configuration schemas.",
148
+ returns: [
149
+ {
150
+ type: "table",
151
+ description: "Schemas keyed by configuration path.",
152
+ },
153
+ ],
44
154
  },
45
- "config.getCategories": () => {
46
- return { ...config.categories };
155
+ "config.getCategories": {
156
+ callback: () => {
157
+ return { ...config.categories };
158
+ },
159
+ description: "Gets all Configuration Manager UI categories.",
160
+ returns: [
161
+ { type: "table", description: "Category definitions keyed by name." },
162
+ ],
47
163
  },
48
164
  };
49
165
  }