@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
@@ -11,6 +11,7 @@ import {
11
11
  import type { ObjectValue } from "@silverbulletmd/silverbullet/type/index";
12
12
  import type { Client } from "../../client.ts";
13
13
  import type { LuaTable } from "../../space_lua/runtime.ts";
14
+ import { describeSchemas, tagSchema } from "./schema_introspection.ts";
14
15
 
15
16
  export function indexSyscalls(
16
17
  objectIndex: ObjectIndex,
@@ -18,143 +19,268 @@ export function indexSyscalls(
18
19
  ): SysCallMapping {
19
20
  return {
20
21
  // Query collection API
21
- "index.tag": (_ctx, tagName: string): LuaQueryCollection => {
22
- return objectIndex.objectsWithTag(tagName);
22
+ "index.tag": {
23
+ callback: (_ctx, tagName: string): LuaQueryCollection => {
24
+ return objectIndex.objectsWithTag(tagName);
25
+ },
26
+ description: "Returns objects carrying a tag as a query collection.",
27
+ signatures: ["index.tag(tagName)"],
23
28
  },
24
29
  // Alias for tag
25
- "index.objects": (_ctx, tagName: string): LuaQueryCollection => {
26
- return objectIndex.objectsWithTag(tagName);
30
+ "index.objects": {
31
+ callback: (_ctx, tagName: string): LuaQueryCollection => {
32
+ return objectIndex.objectsWithTag(tagName);
33
+ },
34
+ description: "Returns objects carrying a tag as a query collection.",
35
+ signatures: ["index.objects(tagName)"],
27
36
  },
28
- "index.pages": (_ctx, tagName?: string): LuaQueryCollection => {
29
- return objectIndex.rootTaggedObjects("page", tagName);
37
+ "index.pages": {
38
+ callback: (_ctx, tagName?: string): LuaQueryCollection => {
39
+ return objectIndex.rootTaggedObjects("page", tagName);
40
+ },
41
+ description:
42
+ "Returns all pages, optionally filtered by an additional tag, as a query collection.",
43
+ signatures: ["index.pages(tagName?)"],
30
44
  },
31
- "index.subPages": (_ctx, pageName: string): LuaQueryCollection => {
32
- return objectIndex.subPages(pageName);
45
+ "index.subPages": {
46
+ callback: (_ctx, pageName: string): LuaQueryCollection => {
47
+ return objectIndex.subPages(pageName);
48
+ },
49
+ description:
50
+ "Returns pages nested below a page name as a query collection.",
51
+ signatures: ["index.subPages(pageName)"],
33
52
  },
34
- "index.documents": (): LuaQueryCollection => {
35
- return objectIndex.objectsWithTag("document");
53
+ "index.documents": {
54
+ callback: (): LuaQueryCollection => {
55
+ return objectIndex.objectsWithTag("document");
56
+ },
57
+ description: "Returns all indexed documents as a query collection.",
36
58
  },
37
- "index.links": (): LuaQueryCollection => {
38
- return objectIndex.objectsWithTag("link");
59
+ "index.links": {
60
+ callback: (): LuaQueryCollection => {
61
+ return objectIndex.objectsWithTag("link");
62
+ },
63
+ description: "Returns all indexed links as a query collection.",
39
64
  },
40
- "index.relations": (): LuaQueryCollection => {
41
- return objectIndex.objectsWithTag("relation");
65
+ "index.relations": {
66
+ callback: (): LuaQueryCollection => {
67
+ return objectIndex.objectsWithTag("relation");
68
+ },
69
+ description: "Returns all indexed relations as a query collection.",
42
70
  },
43
- "index.contentPages": (_ctx, tagName?: string): LuaQueryCollection => {
44
- return objectIndex.contentPages(tagName);
71
+ "index.contentPages": {
72
+ callback: (_ctx, tagName?: string): LuaQueryCollection => {
73
+ return objectIndex.contentPages(tagName);
74
+ },
75
+ description:
76
+ "Returns non-meta pages, optionally filtered by an additional tag, as a query collection.",
77
+ signatures: ["index.contentPages(tagName?)"],
45
78
  },
46
- "index.metaPages": (): LuaQueryCollection => {
47
- return objectIndex.metaPages();
79
+ "index.metaPages": {
80
+ callback: (): LuaQueryCollection => {
81
+ return objectIndex.metaPages();
82
+ },
83
+ description: "Returns all meta pages as a query collection.",
48
84
  },
49
- "index.tasks": (_ctx, tagName?: string): LuaQueryCollection => {
50
- return objectIndex.rootTaggedObjects("task", tagName);
85
+ "index.tasks": {
86
+ callback: (_ctx, tagName?: string): LuaQueryCollection => {
87
+ return objectIndex.rootTaggedObjects("task", tagName);
88
+ },
89
+ description:
90
+ "Returns all tasks, optionally filtered by an additional tag, as a query collection.",
91
+ signatures: ["index.tasks(tagName?)"],
51
92
  },
52
- "index.headers": (_ctx, tagName?: string): LuaQueryCollection => {
53
- return objectIndex.rootTaggedObjects("header", tagName);
93
+ "index.headers": {
94
+ callback: (_ctx, tagName?: string): LuaQueryCollection => {
95
+ return objectIndex.rootTaggedObjects("header", tagName);
96
+ },
97
+ description:
98
+ "Returns all headers, optionally filtered by an additional tag, as a query collection.",
99
+ signatures: ["index.headers(tagName?)"],
54
100
  },
55
- "index.items": (_ctx, tagName?: string): LuaQueryCollection => {
56
- return objectIndex.rootTaggedObjects("item", tagName);
101
+ "index.items": {
102
+ callback: (_ctx, tagName?: string): LuaQueryCollection => {
103
+ return objectIndex.rootTaggedObjects("item", tagName);
104
+ },
105
+ description:
106
+ "Returns all list items, optionally filtered by an additional tag, as a query collection.",
107
+ signatures: ["index.items(tagName?)"],
57
108
  },
58
- "index.paragraphs": (_ctx, tagName?: string): LuaQueryCollection => {
59
- return objectIndex.rootTaggedObjects("paragraph", tagName);
109
+ "index.paragraphs": {
110
+ callback: (_ctx, tagName?: string): LuaQueryCollection => {
111
+ return objectIndex.rootTaggedObjects("paragraph", tagName);
112
+ },
113
+ description:
114
+ "Returns indexed paragraphs, optionally filtered by an additional tag, as a query collection.",
115
+ signatures: ["index.paragraphs(tagName?)"],
60
116
  },
61
- "index.tables": (_ctx, tagName?: string): LuaQueryCollection => {
62
- return objectIndex.rootTaggedObjects("table", tagName);
117
+ "index.tables": {
118
+ callback: (_ctx, tagName?: string): LuaQueryCollection => {
119
+ return objectIndex.rootTaggedObjects("table", tagName);
120
+ },
121
+ description:
122
+ "Returns indexed table rows, optionally filtered by an additional tag, as a query collection.",
123
+ signatures: ["index.tables(tagName?)"],
63
124
  },
64
- "index.tags": (): LuaQueryCollection => {
65
- return objectIndex.objectsWithTag("tag");
125
+ "index.tags": {
126
+ callback: (): LuaQueryCollection => {
127
+ return objectIndex.objectsWithTag("tag");
128
+ },
129
+ description: "Returns all indexed tag objects as a query collection.",
66
130
  },
67
- "index.aspiringPages": (): LuaQueryCollection => {
68
- return objectIndex.aspiringPages();
131
+ // Schema introspection: indexed object-type / tag schemas
132
+ "index.describeSchema": {
133
+ callback: (): Record<string, unknown> => {
134
+ return describeSchemas(client.config.get(["tags"], {}));
135
+ },
136
+ description:
137
+ "Returns raw JSON Schemas for every configured tag that declares one.",
138
+ },
139
+ "index.tagSchema": {
140
+ callback: (_ctx, tagName: string): unknown | null => {
141
+ return tagSchema(client.config.get(["tags"], {}), tagName);
142
+ },
143
+ description:
144
+ "Returns the raw JSON Schema for a tag, or nil when none is declared.",
145
+ signatures: ["index.tagSchema(tagName)"],
146
+ },
147
+ "index.aspiringPages": {
148
+ callback: (): LuaQueryCollection => {
149
+ return objectIndex.aspiringPages();
150
+ },
151
+ description:
152
+ "Returns linked but not yet created pages as a query collection.",
69
153
  },
70
154
  // Internals
71
- "index.aggregates": (): LuaQueryCollection => {
72
- return objectIndex.aggregates();
73
- },
74
- "index.ensureFullIndex": () => {
75
- return objectIndex.ensureFullIndex(client.space);
76
- },
77
- "index.reindexSpace": () => {
78
- return objectIndex.reindexSpace(client.space);
79
- },
80
- "index.indexObjects": (
81
- _ctx,
82
- page: string,
83
- objects: ObjectValue[],
84
- ): Promise<void> => {
85
- return objectIndex.indexObjects(page, objects);
86
- },
87
- "index.validateObjects": async (
88
- _ctx,
89
- page: string,
90
- objects: ObjectValue[],
91
- ): Promise<{ error: string; object: ObjectValue } | null> => {
92
- try {
93
- await objectIndex.validateObjects(page, objects);
94
- return null;
95
- } catch (e: any) {
96
- if (e instanceof ObjectValidationError) {
97
- return {
98
- error: e.message,
99
- object: e.object,
100
- };
101
- } else {
102
- throw e;
155
+ "index.aggregates": {
156
+ callback: (): LuaQueryCollection => {
157
+ return objectIndex.aggregates();
158
+ },
159
+ description: "Returns stored aggregate records as a query collection.",
160
+ },
161
+ "index.ensureFullIndex": {
162
+ callback: () => {
163
+ return objectIndex.ensureFullIndex(client.space);
164
+ },
165
+ description:
166
+ "Ensures the complete object index is available and current.",
167
+ },
168
+ "index.reindexSpace": {
169
+ callback: () => {
170
+ return objectIndex.reindexSpace(client.space);
171
+ },
172
+ description: "Rebuilds the object index for the entire space.",
173
+ },
174
+ "index.indexObjects": {
175
+ callback: (_ctx, page: string, objects: ObjectValue[]): Promise<void> => {
176
+ return objectIndex.indexObjects(page, objects);
177
+ },
178
+ description: "Indexes a collection of objects for a page.",
179
+ signatures: ["index.indexObjects(page, objects)"],
180
+ },
181
+ "index.validateObjects": {
182
+ callback: async (
183
+ _ctx,
184
+ page: string,
185
+ objects: ObjectValue[],
186
+ ): Promise<{ error: string; object: ObjectValue } | null> => {
187
+ try {
188
+ await objectIndex.validateObjects(page, objects);
189
+ return null;
190
+ } catch (e: any) {
191
+ if (e instanceof ObjectValidationError) {
192
+ return {
193
+ error: e.message,
194
+ object: e.object,
195
+ };
196
+ } else {
197
+ throw e;
198
+ }
103
199
  }
104
- }
105
- },
106
- "index.previewProcessedObjects": (
107
- _ctx,
108
- page: string,
109
- objects: ObjectValue[],
110
- ): Promise<{ tag: string; object: ObjectValue }[]> => {
111
- return objectIndex.previewProcessedObjects(page, objects);
112
- },
113
- "index.getObjectByRef": (
114
- _ctx,
115
- page: string,
116
- tag: string,
117
- ref: string,
118
- ): Promise<ObjectValue | undefined> => {
119
- return objectIndex.getObjectByRef(page, tag, ref);
120
- },
121
- "index.queryLuaObjects": (
122
- _ctx,
123
- tag: string,
124
- query: LuaCollectionQuery,
125
- scopedVariables?: Record<string, any>,
126
- ): Promise<ObjectValue[]> => {
127
- return objectIndex.queryLuaObjects(
128
- client.clientSystem.spaceLuaEnv.env,
129
- tag,
130
- query,
131
- scopedVariables,
132
- );
200
+ },
201
+ description:
202
+ "Validates objects for a page and returns the first validation error, if any.",
203
+ signatures: ["index.validateObjects(page, objects)"],
204
+ },
205
+ "index.previewProcessedObjects": {
206
+ callback: (
207
+ _ctx,
208
+ page: string,
209
+ objects: ObjectValue[],
210
+ ): Promise<{ tag: string; object: ObjectValue }[]> => {
211
+ return objectIndex.previewProcessedObjects(page, objects);
212
+ },
213
+ description:
214
+ "Runs the indexing pipeline without writing and returns processed tag/object pairs.",
215
+ signatures: ["index.previewProcessedObjects(page, objects)"],
216
+ },
217
+ "index.getObjectByRef": {
218
+ callback: (
219
+ _ctx,
220
+ page: string,
221
+ tag: string,
222
+ ref: string,
223
+ ): Promise<ObjectValue | undefined> => {
224
+ return objectIndex.getObjectByRef(page, tag, ref);
225
+ },
226
+ description:
227
+ "Returns an indexed object identified by page, tag, and reference.",
228
+ signatures: ["index.getObjectByRef(page, tag, ref)"],
229
+ },
230
+ "index.queryLuaObjects": {
231
+ callback: (
232
+ _ctx,
233
+ tag: string,
234
+ query: LuaCollectionQuery,
235
+ scopedVariables?: Record<string, any>,
236
+ ): Promise<ObjectValue[]> => {
237
+ return objectIndex.queryLuaObjects(
238
+ client.clientSystem.spaceLuaEnv.env,
239
+ tag,
240
+ query,
241
+ scopedVariables,
242
+ );
243
+ },
244
+ description:
245
+ "Executes a structured Lua collection query against indexed objects.",
246
+ signatures: ["index.queryLuaObjects(tag, query, scopedVariables?)"],
133
247
  },
134
248
 
135
- "index.deleteObject": (
136
- _ctx,
137
- page: string,
138
- tag: string,
139
- ref: string,
140
- ): Promise<void> => {
141
- return objectIndex.deleteObject(page, tag, ref);
142
- },
143
- "lua:index.defineTag": (_ctx, tagDef: LuaTable) => {
144
- // Using 'lua:' prefix to _not_ convert tagDef to a JS version (but keep original LuaTable)
145
- if (!tagDef.has("name")) {
146
- throw new Error("A tag name is required");
147
- }
148
- const currentTag = client.config.get(["tags", tagDef.get("name")], null);
149
- if (!currentTag) {
150
- client.config.set(["tags", tagDef.get("name")], {
151
- name: tagDef.get("name"),
152
- });
153
- }
154
- client.config.set(
155
- ["tags", tagDef.get("name"), "metatable"],
156
- tagDef.get("metatable"),
157
- );
249
+ "index.deleteObject": {
250
+ callback: (
251
+ _ctx,
252
+ page: string,
253
+ tag: string,
254
+ ref: string,
255
+ ): Promise<void> => {
256
+ return objectIndex.deleteObject(page, tag, ref);
257
+ },
258
+ description:
259
+ "Deletes an indexed object identified by page, tag, and reference.",
260
+ signatures: ["index.deleteObject(page, tag, ref)"],
261
+ },
262
+ "lua:index.defineTag": {
263
+ callback: (_ctx, tagDef: LuaTable) => {
264
+ // Using 'lua:' prefix to _not_ convert tagDef to a JS version (but keep original LuaTable)
265
+ if (!tagDef.has("name")) {
266
+ throw new Error("A tag name is required");
267
+ }
268
+ const currentTag = client.config.get(
269
+ ["tags", tagDef.get("name")],
270
+ null,
271
+ );
272
+ if (!currentTag) {
273
+ client.config.set(["tags", tagDef.get("name")], {
274
+ name: tagDef.get("name"),
275
+ });
276
+ }
277
+ client.config.set(
278
+ ["tags", tagDef.get("name"), "metatable"],
279
+ tagDef.get("metatable"),
280
+ );
281
+ },
282
+ description: "Defines or updates a tag and its Lua metatable.",
283
+ signatures: ["index.defineTag(tagDefinition)"],
158
284
  },
159
285
  };
160
286
  }
@@ -75,17 +75,116 @@ export function validateSchema(schema: any): undefined | string {
75
75
  return;
76
76
  }
77
77
 
78
+ /**
79
+ * Best-effort: infer a JSON Schema (draft 2020-12) from the *shape* of a single
80
+ * sample value. Types are guessed from one example, so the result is a hint,
81
+ * not a contract — the top-level schema is marked `"x-inferred": true`.
82
+ *
83
+ * Useful when a tag/object type has no declared schema but an example object
84
+ * exists and you want a plausible schema for it.
85
+ */
86
+ export function inferFromObject(value: any): any {
87
+ const schema = inferSchemaNode(value);
88
+ schema["$schema"] = "https://json-schema.org/draft/2020-12/schema";
89
+ schema["x-inferred"] = true;
90
+ return schema;
91
+ }
92
+
93
+ /** Infer a bare JSON Schema node for a value, recursing into arrays/objects. */
94
+ function inferSchemaNode(value: any): any {
95
+ if (value === null || value === undefined) {
96
+ return { type: "null" };
97
+ }
98
+ if (Array.isArray(value)) {
99
+ const node: any = { type: "array" };
100
+ if (value.length > 0) {
101
+ node.items = inferSchemaNode(value[0]);
102
+ }
103
+ return node;
104
+ }
105
+ switch (typeof value) {
106
+ case "boolean":
107
+ return { type: "boolean" };
108
+ case "number":
109
+ return { type: Number.isInteger(value) ? "integer" : "number" };
110
+ case "string":
111
+ return { type: "string" };
112
+ case "object": {
113
+ const properties: Record<string, any> = {};
114
+ for (const [k, v] of Object.entries(value)) {
115
+ properties[k] = inferSchemaNode(v);
116
+ }
117
+ return { type: "object", properties };
118
+ }
119
+ default:
120
+ // functions, symbols, bigint, … — not representable in JSON Schema.
121
+ return {};
122
+ }
123
+ }
124
+
78
125
  export function jsonschemaSyscalls(): SysCallMapping {
79
126
  return {
80
- "jsonschema.validateObject": (
81
- _ctx,
82
- schema: any,
83
- object: any,
84
- ): undefined | string => {
85
- return validateObject(schema, object);
127
+ "jsonschema.validateObject": {
128
+ callback: (_ctx, schema: any, object: any): undefined | string => {
129
+ return validateObject(schema, object);
130
+ },
131
+ description: "Validates a value against a JSON Schema.",
132
+ parameters: [
133
+ {
134
+ name: "schema",
135
+ type: "table",
136
+ description: "JSON Schema to apply.",
137
+ },
138
+ { name: "object", description: "Value to validate." },
139
+ ],
140
+ returns: [
141
+ {
142
+ type: "string",
143
+ description: "Validation error, or nil when valid.",
144
+ },
145
+ ],
146
+ examples: [
147
+ {
148
+ code: 'local schema = {type = "object", properties = {name = {type = "string"}}, required = {"name"}}\nlocal err = jsonschema.validateObject(schema, {name = "John"})',
149
+ },
150
+ ],
151
+ },
152
+ "jsonschema.validateSchema": {
153
+ callback: (_ctx, schema: any): undefined | string => {
154
+ return validateSchema(schema);
155
+ },
156
+ description:
157
+ "Checks whether a JSON Schema has a supported top-level shape.",
158
+ parameters: [{ name: "schema", description: "JSON Schema to validate." }],
159
+ returns: [
160
+ { type: "string", description: "Schema error, or nil when valid." },
161
+ ],
162
+ examples: [
163
+ {
164
+ code: 'local err = jsonschema.validateSchema({type = "object"})',
165
+ },
166
+ ],
86
167
  },
87
- "jsonschema.validateSchema": (_ctx, schema: any): undefined | string => {
88
- return validateSchema(schema);
168
+ "jsonschema.inferFromObject": {
169
+ callback: (_ctx, object: any): any => {
170
+ return inferFromObject(object);
171
+ },
172
+ description:
173
+ "Infers a best-effort draft 2020-12 JSON Schema from a sample value.",
174
+ parameters: [
175
+ {
176
+ name: "object",
177
+ description: "Sample value whose shape is inferred.",
178
+ },
179
+ ],
180
+ returns: [
181
+ { type: "table", description: "Inferred schema marked x-inferred." },
182
+ ],
183
+ examples: [
184
+ {
185
+ code: 'local schema = jsonschema.inferFromObject({name = "Widget", count = 3})',
186
+ },
187
+ ],
89
188
  },
90
189
  };
91
190
  }
@@ -9,19 +9,45 @@ import {
9
9
 
10
10
  export function languageSyscalls(): SysCallMapping {
11
11
  return {
12
- "language.parseLanguage": async (
13
- _ctx,
14
- language: string,
15
- code: string,
16
- ): Promise<ParseTree> => {
17
- const lang = languageFor(language) ?? await loadLanguageFor(language);
18
- if (!lang) {
19
- throw new Error(`Unknown language ${language}`);
20
- }
21
- return parse(lang, code);
12
+ "language.parseLanguage": {
13
+ callback: async (
14
+ _ctx,
15
+ language: string,
16
+ code: string,
17
+ ): Promise<ParseTree> => {
18
+ const lang = languageFor(language) ?? (await loadLanguageFor(language));
19
+ if (!lang) {
20
+ throw new Error(`Unknown language ${language}`);
21
+ }
22
+ return parse(lang, code);
23
+ },
24
+ description: "Parses code using a supported fenced-code-block language.",
25
+ parameters: [
26
+ {
27
+ name: "language",
28
+ type: "string",
29
+ description: "Language name or alias.",
30
+ },
31
+ {
32
+ name: "code",
33
+ type: "string",
34
+ description: "Source code to parse.",
35
+ },
36
+ ],
37
+ returns: [{ type: "table", description: "Parsed syntax tree." }],
38
+ examples: [
39
+ {
40
+ code: 'local tree = language.parseLanguage("javascript", "const answer = 42")',
41
+ },
42
+ ],
22
43
  },
23
- "language.listLanguages": (): string[] => {
24
- return allLanguageNames;
44
+ "language.listLanguages": {
45
+ callback: (): string[] => {
46
+ return allLanguageNames;
47
+ },
48
+ description: "Lists all supported fenced-code-block languages.",
49
+ returns: [{ type: "table", description: "Supported language names." }],
50
+ examples: [{ code: "local languages = language.listLanguages()" }],
25
51
  },
26
52
  };
27
53
  }