@silverbulletmd/silverbullet 2.9.0 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -25
- package/client/asset_bundle/bundle.ts +0 -1
- package/client/config.ts +343 -0
- package/client/markdown_parser/constants.ts +3 -1
- package/client/plugos/hooks/command.ts +0 -5
- package/client/plugos/hooks/event.ts +0 -6
- package/client/plugos/hooks/mq.ts +0 -3
- package/client/plugos/hooks/slash_command.ts +1 -7
- package/client/plugos/hooks/syscall.ts +24 -14
- package/client/plugos/manifest_cache.ts +0 -32
- package/client/plugos/plug.ts +0 -3
- package/client/plugos/plug_compile.ts +3 -13
- package/client/plugos/sandboxes/worker_sandbox.ts +19 -9
- package/client/plugos/syscalls/asset.ts +61 -23
- package/client/plugos/syscalls/clientStore.ts +32 -6
- package/client/plugos/syscalls/client_code_widget.ts +9 -4
- package/client/plugos/syscalls/code_widget.ts +26 -11
- package/client/plugos/syscalls/config.ts +146 -30
- package/client/plugos/syscalls/datastore.ts +146 -40
- package/client/plugos/syscalls/editor.ts +1518 -722
- package/client/plugos/syscalls/event.ts +44 -13
- package/client/plugos/syscalls/fetch.ts +120 -83
- package/client/plugos/syscalls/icon.ts +46 -0
- package/client/plugos/syscalls/index.ts +267 -121
- package/client/plugos/syscalls/jsonschema.ts +107 -9
- package/client/plugos/syscalls/language.ts +38 -12
- package/client/plugos/syscalls/markdown.ts +166 -50
- package/client/plugos/syscalls/mq.ts +92 -20
- package/client/plugos/syscalls/navigator.ts +143 -0
- package/client/plugos/syscalls/schema_introspection.ts +33 -0
- package/client/plugos/syscalls/search.ts +44 -0
- package/client/plugos/syscalls/service_registry.ts +74 -20
- package/client/plugos/syscalls/shell.ts +51 -21
- package/client/plugos/syscalls/space.ts +412 -113
- package/client/plugos/syscalls/sync.ts +103 -20
- package/client/plugos/syscalls/system.ts +274 -139
- package/client/plugos/system.ts +28 -10
- package/client/plugos/worker_runtime.ts +0 -2
- package/client/space_lua/aggregates.ts +0 -16
- package/client/space_lua/api_documentation.ts +107 -0
- package/client/space_lua/ast.ts +13 -5
- package/client/space_lua/ast_narrow.ts +0 -5
- package/client/space_lua/budget.ts +113 -0
- package/client/space_lua/budget_ui.ts +49 -0
- package/client/space_lua/eval.ts +125 -92
- package/client/space_lua/labels.ts +0 -4
- package/client/space_lua/numeric.ts +0 -5
- package/client/space_lua/parse.ts +243 -125
- package/client/space_lua/pretty_print.ts +128 -54
- package/client/space_lua/quarantine.ts +127 -0
- package/client/space_lua/query_collection.ts +7 -51
- package/client/space_lua/query_env.ts +0 -2
- package/client/space_lua/render_lua_markdown.ts +2 -7
- package/client/space_lua/render_widget.ts +173 -0
- package/client/space_lua/runtime.ts +133 -92
- package/client/space_lua/stdlib/crypto.ts +8 -3
- package/client/space_lua/stdlib/encoding.ts +27 -9
- package/client/space_lua/stdlib/format.ts +0 -12
- package/client/space_lua/stdlib/js.ts +136 -23
- package/client/space_lua/stdlib/load.ts +24 -16
- package/client/space_lua/stdlib/math.ts +331 -133
- package/client/space_lua/stdlib/net.ts +57 -13
- package/client/space_lua/stdlib/os.ts +111 -54
- package/client/space_lua/stdlib/pattern.ts +10 -15
- package/client/space_lua/stdlib/space_lua.ts +344 -21
- package/client/space_lua/stdlib/string.ts +282 -104
- package/client/space_lua/stdlib/string_pack.ts +58 -22
- package/client/space_lua/stdlib/table.ts +195 -45
- package/client/space_lua/stdlib.ts +354 -125
- package/client/space_lua/syscalls.ts +270 -0
- package/client/space_lua/tonumber.ts +0 -9
- package/dist/plug-compile.js +2 -3
- package/package.json +15 -5
- package/plug-api/lib/async.ts +1 -6
- package/plug-api/lib/collation.ts +22 -0
- package/plug-api/lib/crypto.ts +16 -15
- package/plug-api/lib/dates.ts +33 -0
- package/plug-api/lib/fuzzy.ts +282 -0
- package/plug-api/lib/json.ts +0 -6
- package/plug-api/lib/limited_map.ts +0 -2
- package/plug-api/lib/link_write.ts +27 -0
- package/plug-api/{ui → lib}/panel_styles.ts +4 -3
- package/plug-api/lib/ref.ts +124 -5
- package/plug-api/lib/resolve.ts +0 -1
- package/plug-api/lib/resolve_path.ts +290 -0
- package/plug-api/lib/shortcut.ts +17 -1
- package/plug-api/lib/tags.ts +0 -3
- package/plug-api/lib/transclusion.ts +28 -1
- package/plug-api/lib/tree.ts +0 -5
- package/plug-api/lib/yaml.ts +3 -42
- package/plug-api/syscall.ts +1 -5
- package/plug-api/syscalls/config.ts +7 -5
- package/plug-api/syscalls/editor.ts +70 -208
- package/plug-api/syscalls/icon.ts +18 -0
- package/plug-api/syscalls/index.ts +39 -5
- package/plug-api/syscalls/jsonschema.ts +9 -0
- package/plug-api/syscalls/lua.ts +7 -0
- package/plug-api/syscalls/search.ts +11 -0
- package/plug-api/syscalls/shell.ts +0 -1
- package/plug-api/syscalls/space.ts +122 -5
- package/plug-api/syscalls/sync.ts +23 -2
- package/plug-api/syscalls/system.ts +53 -0
- package/plug-api/syscalls.ts +2 -0
- package/plug-api/system_mock.ts +15 -2
- package/plug-api/types/client.ts +4 -3
- package/plug-api/types/datastore.ts +11 -2
- package/plug-api/types/index.ts +62 -1
- package/plug-api/types/manifest.ts +22 -4
- package/plug-api/types/profile.ts +13 -0
- package/plug-api/types/revisions.ts +67 -0
- package/plug-api/ui/cx.ts +1 -3
- package/plug-api/ui/description.ts +64 -0
- package/plug-api/ui/hover.ts +69 -0
- package/plug-api/ui/index.ts +61 -2
- package/plug-api/ui/scroll.ts +30 -0
- package/plug-api/ui/slugify.ts +44 -0
- package/plug-api/ui/tree_model.ts +263 -0
- package/plug-api/ui/tree_types.ts +66 -0
- package/plug-api/ui/use_fit_collapse.ts +67 -0
- package/plugs/builtin_plugs.ts +0 -1
- package/plugs/index/types.ts +9 -0
- package/client/plugos/syscalls/lua.ts +0 -81
- package/plug-api/lib/memory_cache.ts +0 -21
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { LuaBudgetStopped } from "./budget.ts";
|
|
1
2
|
import {
|
|
2
3
|
getMetatable,
|
|
3
4
|
type ILuaFunction,
|
|
@@ -14,7 +15,7 @@ import {
|
|
|
14
15
|
LuaMultiRes,
|
|
15
16
|
LuaRuntimeError,
|
|
16
17
|
type LuaStackFrame,
|
|
17
|
-
|
|
18
|
+
LuaTable,
|
|
18
19
|
luaToString,
|
|
19
20
|
luaTypeOf,
|
|
20
21
|
type LuaValue,
|
|
@@ -37,36 +38,70 @@ import { isTaggedFloat, makeLuaFloat } from "./numeric.ts";
|
|
|
37
38
|
import { isPromise } from "./rp.ts";
|
|
38
39
|
import { isSqlNull } from "./sliq_null.ts";
|
|
39
40
|
|
|
40
|
-
const printFunction = new LuaBuiltinFunction(
|
|
41
|
-
|
|
41
|
+
const printFunction = new LuaBuiltinFunction({
|
|
42
|
+
callback: async (_sf, ...args) => {
|
|
43
|
+
console.log(
|
|
44
|
+
"[Lua]",
|
|
45
|
+
...(await Promise.all(args.map((v) => luaToString(v)))),
|
|
46
|
+
);
|
|
47
|
+
},
|
|
48
|
+
description:
|
|
49
|
+
"Prints string representations of its arguments to the runtime log.",
|
|
50
|
+
signatures: ["print(...)"],
|
|
51
|
+
parameters: [{ name: "...", description: "Values to print." }],
|
|
52
|
+
examples: [{ code: 'print("Hello, world!")' }],
|
|
42
53
|
});
|
|
43
54
|
|
|
44
|
-
const assertFunction = new LuaBuiltinFunction(
|
|
45
|
-
async (sf, value: any, message?: string) => {
|
|
55
|
+
const assertFunction = new LuaBuiltinFunction({
|
|
56
|
+
callback: async (sf, value: any, message?: string) => {
|
|
46
57
|
if (!(await value)) {
|
|
47
58
|
throw new LuaRuntimeError(`Assertion failed: ${message}`, sf);
|
|
48
59
|
}
|
|
49
60
|
},
|
|
50
|
-
|
|
61
|
+
description:
|
|
62
|
+
"Raises an error when a value is falsy; otherwise completes successfully.",
|
|
63
|
+
parameters: [
|
|
64
|
+
{ name: "value", description: "Condition to test." },
|
|
65
|
+
{
|
|
66
|
+
name: "message",
|
|
67
|
+
type: "string",
|
|
68
|
+
description: "Error detail.",
|
|
69
|
+
optional: true,
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
examples: [{ code: 'assert(user ~= nil, "user is required")' }],
|
|
73
|
+
});
|
|
51
74
|
|
|
52
|
-
const ipairsFunction = new LuaBuiltinFunction(
|
|
53
|
-
|
|
75
|
+
const ipairsFunction = new LuaBuiltinFunction({
|
|
76
|
+
callback: (sf, t: LuaTable | any[]) => {
|
|
77
|
+
let i = 0;
|
|
54
78
|
|
|
55
|
-
|
|
56
|
-
|
|
79
|
+
return async () => {
|
|
80
|
+
i = i + 1;
|
|
57
81
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
82
|
+
const v = await luaGet(t, i, sf.astCtx ?? null, sf);
|
|
83
|
+
if (v === null || v === undefined) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
62
86
|
|
|
63
|
-
|
|
64
|
-
|
|
87
|
+
return new LuaMultiRes([i, v]);
|
|
88
|
+
};
|
|
89
|
+
},
|
|
90
|
+
description:
|
|
91
|
+
"Returns an iterator over consecutive integer keys starting at 1 and stopping at the first `nil`.",
|
|
92
|
+
parameters: [{ name: "table", type: "table" }],
|
|
93
|
+
returns: [
|
|
94
|
+
{ type: "function", description: "Iterator yielding index and value." },
|
|
95
|
+
],
|
|
96
|
+
examples: [
|
|
97
|
+
{
|
|
98
|
+
code: 'for i, fruit in ipairs({"apple", "banana"}) do\n print(i, fruit)\nend',
|
|
99
|
+
},
|
|
100
|
+
],
|
|
65
101
|
});
|
|
66
102
|
|
|
67
|
-
const pairsFunction = new LuaBuiltinFunction(
|
|
68
|
-
(sf, t: LuaTable | any[] | Record<string, any>) => {
|
|
69
|
-
// Respect `__pairs` metamethod for Lua tables
|
|
103
|
+
const pairsFunction = new LuaBuiltinFunction({
|
|
104
|
+
callback: (sf, t: LuaTable | any[] | Record<string, any>) => {
|
|
70
105
|
if (isLuaTable(t)) {
|
|
71
106
|
const mt = (t as any).metatable as LuaTable | null | undefined;
|
|
72
107
|
if (mt) {
|
|
@@ -105,10 +140,24 @@ const pairsFunction = new LuaBuiltinFunction(
|
|
|
105
140
|
// Must return (iter, state, control) for generic for
|
|
106
141
|
return new LuaMultiRes([iter, t, null]);
|
|
107
142
|
},
|
|
108
|
-
|
|
143
|
+
description:
|
|
144
|
+
"Returns an iterator over all table key-value pairs, respecting `__pairs`.",
|
|
145
|
+
parameters: [{ name: "table", type: "table" }],
|
|
146
|
+
returns: [
|
|
147
|
+
{
|
|
148
|
+
type: "function",
|
|
149
|
+
description: "Iterator plus its state and initial control value.",
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
examples: [
|
|
153
|
+
{
|
|
154
|
+
code: 'for key, value in pairs({name = "Ada", age = 36}) do\n print(key, value)\nend',
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
});
|
|
109
158
|
|
|
110
|
-
export const eachFunction = new LuaBuiltinFunction(
|
|
111
|
-
(sf, ar: LuaTable | any[]) => {
|
|
159
|
+
export const eachFunction = new LuaBuiltinFunction({
|
|
160
|
+
callback: (sf, ar: LuaTable | any[]) => {
|
|
112
161
|
let i = 1;
|
|
113
162
|
const length = (ar as any).length;
|
|
114
163
|
return async () => {
|
|
@@ -120,18 +169,30 @@ export const eachFunction = new LuaBuiltinFunction(
|
|
|
120
169
|
return result;
|
|
121
170
|
};
|
|
122
171
|
},
|
|
123
|
-
|
|
172
|
+
description:
|
|
173
|
+
"Returns a Space Lua iterator over array-like values without yielding indices.",
|
|
174
|
+
parameters: [{ name: "table", type: "table" }],
|
|
175
|
+
returns: [{ type: "function", description: "Iterator yielding values." }],
|
|
176
|
+
examples: [
|
|
177
|
+
{
|
|
178
|
+
code: 'for fruit in each({"apple", "banana"}) do\n print(fruit)\nend',
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
});
|
|
124
182
|
|
|
125
|
-
const typeFunction = new LuaBuiltinFunction(
|
|
126
|
-
(_sf, value: LuaValue): string | Promise<string> => {
|
|
183
|
+
const typeFunction = new LuaBuiltinFunction({
|
|
184
|
+
callback: (_sf, value: LuaValue): string | Promise<string> => {
|
|
127
185
|
return luaTypeOf(value);
|
|
128
186
|
},
|
|
129
|
-
|
|
187
|
+
description: "Returns the Lua type name of a value.",
|
|
188
|
+
parameters: [{ name: "value" }],
|
|
189
|
+
returns: [{ type: "string" }],
|
|
190
|
+
});
|
|
130
191
|
|
|
131
192
|
// tostring() checks `__tostring` metamethod first (with live SF), then
|
|
132
193
|
// falls back to the default `luaToString` representation.
|
|
133
|
-
const tostringFunction = new LuaBuiltinFunction(
|
|
134
|
-
(sf, value: any): string | Promise<string> => {
|
|
194
|
+
const tostringFunction = new LuaBuiltinFunction({
|
|
195
|
+
callback: (sf, value: any): string | Promise<string> => {
|
|
135
196
|
const mt = getMetatable(value, sf);
|
|
136
197
|
if (mt) {
|
|
137
198
|
const mm = mt.rawGet("__tostring");
|
|
@@ -153,10 +214,14 @@ const tostringFunction = new LuaBuiltinFunction(
|
|
|
153
214
|
}
|
|
154
215
|
return luaToString(value);
|
|
155
216
|
},
|
|
156
|
-
|
|
217
|
+
description:
|
|
218
|
+
"Converts a value to a string, respecting its `__tostring` metamethod.",
|
|
219
|
+
parameters: [{ name: "value" }],
|
|
220
|
+
returns: [{ type: "string" }],
|
|
221
|
+
});
|
|
157
222
|
|
|
158
|
-
const tonumberFunction = new LuaBuiltinFunction(
|
|
159
|
-
(sf, value: LuaValue, base?: number) => {
|
|
223
|
+
const tonumberFunction = new LuaBuiltinFunction({
|
|
224
|
+
callback: (sf, value: LuaValue, base?: number) => {
|
|
160
225
|
if (base !== undefined) {
|
|
161
226
|
if (!(typeof base === "number" && base >= 2 && base <= 36)) {
|
|
162
227
|
throw new LuaRuntimeError(
|
|
@@ -188,10 +253,26 @@ const tonumberFunction = new LuaBuiltinFunction(
|
|
|
188
253
|
|
|
189
254
|
return result.value;
|
|
190
255
|
},
|
|
191
|
-
|
|
256
|
+
description:
|
|
257
|
+
"Converts a number or numeric string to a Lua number, optionally in a base from 2 through 36.",
|
|
258
|
+
signatures: [
|
|
259
|
+
"tonumber(value): number|nil",
|
|
260
|
+
"tonumber(value, base): integer|nil",
|
|
261
|
+
],
|
|
262
|
+
parameters: [
|
|
263
|
+
{ name: "value", type: "number|string" },
|
|
264
|
+
{ name: "base", type: "integer", optional: true },
|
|
265
|
+
],
|
|
266
|
+
returns: [{ type: "number|nil" }],
|
|
267
|
+
examples: [{ code: 'print(tonumber("2a", 16)) -- 42' }],
|
|
268
|
+
});
|
|
192
269
|
|
|
193
|
-
const errorFunction = new LuaBuiltinFunction(
|
|
194
|
-
|
|
270
|
+
const errorFunction = new LuaBuiltinFunction({
|
|
271
|
+
callback: (sf, message: string) => {
|
|
272
|
+
throw new LuaRuntimeError(message, sf);
|
|
273
|
+
},
|
|
274
|
+
description: "Raises a Lua runtime error with the supplied message.",
|
|
275
|
+
parameters: [{ name: "message", type: "string" }],
|
|
195
276
|
});
|
|
196
277
|
|
|
197
278
|
async function pcallBoundary(
|
|
@@ -214,38 +295,53 @@ async function pcallBoundary(
|
|
|
214
295
|
const msg = errMsgOf(e);
|
|
215
296
|
try {
|
|
216
297
|
await luaCloseFromMark(sf, mark, msg);
|
|
217
|
-
return { ok: false, message: msg };
|
|
218
298
|
} catch (closeErr: any) {
|
|
219
|
-
|
|
299
|
+
if (!(e instanceof LuaBudgetStopped)) {
|
|
300
|
+
return { ok: false, message: errMsgOf(closeErr) };
|
|
301
|
+
}
|
|
220
302
|
}
|
|
303
|
+
// A user-initiated stop is not a recoverable Lua error.
|
|
304
|
+
if (e instanceof LuaBudgetStopped) {
|
|
305
|
+
throw e;
|
|
306
|
+
}
|
|
307
|
+
return { ok: false, message: msg };
|
|
221
308
|
}
|
|
222
309
|
}
|
|
223
310
|
|
|
224
|
-
const pcallFunction = new LuaBuiltinFunction(
|
|
225
|
-
async (sf, fn: ILuaFunction, ...args) => {
|
|
226
|
-
//
|
|
227
|
-
//
|
|
228
|
-
//
|
|
229
|
-
// after that.
|
|
230
|
-
//
|
|
231
|
-
// The protected call boundary must be established *before*
|
|
232
|
-
// evaluating the function and its arguments. Otherwise, any
|
|
233
|
-
// `<close>` locals created while evaluating `pcall`'s arguments
|
|
234
|
-
// will be wrongly treated as "inside" the protected call, and
|
|
235
|
-
// `pcall` may end up closing them (or affecting close ordering).
|
|
236
|
-
//
|
|
237
|
-
// `threadState` is read-only on the stack frame; do not reassign!
|
|
311
|
+
const pcallFunction = new LuaBuiltinFunction({
|
|
312
|
+
callback: async (sf, fn: ILuaFunction, ...args) => {
|
|
313
|
+
// Snapshot the close stack before evaluating the function and arguments,
|
|
314
|
+
// so argument-owned <close> locals stay outside the protected boundary.
|
|
315
|
+
// Do not reassign the frame’s read-only threadState.
|
|
238
316
|
const res = await pcallBoundary(sf, fn, args);
|
|
239
317
|
if (res.ok) {
|
|
240
318
|
return new LuaMultiRes([true, ...res.values]);
|
|
241
319
|
}
|
|
242
320
|
return new LuaMultiRes([false, res.message]);
|
|
243
321
|
},
|
|
244
|
-
|
|
322
|
+
description:
|
|
323
|
+
"Calls a function in protected mode and returns a success flag followed by results or an error message.",
|
|
324
|
+
signatures: ["pcall(function, ...): boolean, ..."],
|
|
325
|
+
parameters: [
|
|
326
|
+
{ name: "function", type: "function" },
|
|
327
|
+
{ name: "...", description: "Arguments passed to the function." },
|
|
328
|
+
],
|
|
329
|
+
returns: [
|
|
330
|
+
{ type: "boolean", description: "Whether the call succeeded." },
|
|
331
|
+
{ description: "Call results or error message." },
|
|
332
|
+
],
|
|
333
|
+
examples: [
|
|
334
|
+
{ code: "local ok, result = pcall(function() return mightFail() end)" },
|
|
335
|
+
],
|
|
336
|
+
});
|
|
245
337
|
|
|
246
|
-
const xpcallFunction = new LuaBuiltinFunction(
|
|
247
|
-
async (
|
|
248
|
-
|
|
338
|
+
const xpcallFunction = new LuaBuiltinFunction({
|
|
339
|
+
callback: async (
|
|
340
|
+
sf,
|
|
341
|
+
fn: ILuaFunction,
|
|
342
|
+
errorHandler: ILuaFunction,
|
|
343
|
+
...args
|
|
344
|
+
) => {
|
|
249
345
|
const res = await pcallBoundary(sf, fn, args);
|
|
250
346
|
if (res.ok) {
|
|
251
347
|
return new LuaMultiRes([true, ...res.values]);
|
|
@@ -254,30 +350,69 @@ const xpcallFunction = new LuaBuiltinFunction(
|
|
|
254
350
|
const outVals = hr instanceof LuaMultiRes ? hr.flatten().values : [hr];
|
|
255
351
|
return new LuaMultiRes([false, ...outVals]);
|
|
256
352
|
},
|
|
257
|
-
|
|
353
|
+
description:
|
|
354
|
+
"Calls a function in protected mode and transforms any error with an error handler.",
|
|
355
|
+
signatures: ["xpcall(function, errorHandler, ...): boolean, ..."],
|
|
356
|
+
parameters: [
|
|
357
|
+
{ name: "function", type: "function" },
|
|
358
|
+
{ name: "errorHandler", type: "function" },
|
|
359
|
+
{ name: "...", description: "Arguments passed to the function." },
|
|
360
|
+
],
|
|
361
|
+
returns: [
|
|
362
|
+
{ type: "boolean", description: "Whether the call succeeded." },
|
|
363
|
+
{ description: "Call results or handler results." },
|
|
364
|
+
],
|
|
365
|
+
examples: [
|
|
366
|
+
{
|
|
367
|
+
code: 'local ok, message = xpcall(riskyOperation, function(err)\n return "Operation failed: " .. tostring(err)\nend)',
|
|
368
|
+
},
|
|
369
|
+
],
|
|
370
|
+
});
|
|
258
371
|
|
|
259
|
-
const setmetatableFunction = new LuaBuiltinFunction(
|
|
260
|
-
(sf, table: LuaTable, metatable: LuaTable) => {
|
|
372
|
+
const setmetatableFunction = new LuaBuiltinFunction({
|
|
373
|
+
callback: (sf, table: LuaTable, metatable: LuaTable) => {
|
|
261
374
|
if (!metatable) {
|
|
262
375
|
throw new LuaRuntimeError("metatable cannot be set to nil", sf);
|
|
263
376
|
}
|
|
264
377
|
table.metatable = metatable;
|
|
265
378
|
return table;
|
|
266
379
|
},
|
|
267
|
-
|
|
380
|
+
description: "Sets a table's metatable and returns the table.",
|
|
381
|
+
parameters: [
|
|
382
|
+
{ name: "table", type: "table" },
|
|
383
|
+
{ name: "metatable", type: "table" },
|
|
384
|
+
],
|
|
385
|
+
returns: [{ type: "table" }],
|
|
386
|
+
});
|
|
268
387
|
|
|
269
|
-
const rawlenFunction = new LuaBuiltinFunction(
|
|
270
|
-
|
|
388
|
+
const rawlenFunction = new LuaBuiltinFunction({
|
|
389
|
+
callback: (_sf, value: LuaValue) => luaLen(value, _sf, true),
|
|
390
|
+
description: "Returns a string or table length without invoking `__len`.",
|
|
391
|
+
parameters: [{ name: "value", type: "string|table" }],
|
|
392
|
+
returns: [{ type: "integer" }],
|
|
271
393
|
});
|
|
272
394
|
|
|
273
|
-
const rawsetFunction = new LuaBuiltinFunction(
|
|
274
|
-
(_sf, table: LuaTable, key: LuaValue, value: LuaValue) => {
|
|
395
|
+
const rawsetFunction = new LuaBuiltinFunction({
|
|
396
|
+
callback: (_sf, table: LuaTable, key: LuaValue, value: LuaValue) => {
|
|
275
397
|
return (table as any).rawSet(key, value);
|
|
276
398
|
},
|
|
277
|
-
|
|
399
|
+
description:
|
|
400
|
+
"Sets a table key without invoking `__newindex` and returns the table.",
|
|
401
|
+
parameters: [
|
|
402
|
+
{ name: "table", type: "table" },
|
|
403
|
+
{ name: "key" },
|
|
404
|
+
{ name: "value" },
|
|
405
|
+
],
|
|
406
|
+
returns: [{ type: "table" }],
|
|
407
|
+
examples: [
|
|
408
|
+
{
|
|
409
|
+
code: 'local t = setmetatable({}, {__newindex = function() error("blocked") end})\nrawset(t, "name", "Ada")',
|
|
410
|
+
},
|
|
411
|
+
],
|
|
412
|
+
});
|
|
278
413
|
|
|
279
|
-
const rawgetFunction = new LuaBuiltinFunction(
|
|
280
|
-
(_sf, table: any, key: LuaValue) => {
|
|
414
|
+
const rawgetFunction = new LuaBuiltinFunction({
|
|
415
|
+
callback: (_sf, table: any, key: LuaValue) => {
|
|
281
416
|
const isArray = Array.isArray(table);
|
|
282
417
|
|
|
283
418
|
const isPlainObj =
|
|
@@ -328,37 +463,64 @@ const rawgetFunction = new LuaBuiltinFunction(
|
|
|
328
463
|
const v = (table as Record<string | number, any>)[k as any];
|
|
329
464
|
return v === undefined ? null : v;
|
|
330
465
|
},
|
|
331
|
-
|
|
466
|
+
description: "Reads a table key without invoking `__index`.",
|
|
467
|
+
parameters: [{ name: "table", type: "table" }, { name: "key" }],
|
|
468
|
+
returns: [{ description: "Stored value or `nil`." }],
|
|
469
|
+
});
|
|
332
470
|
|
|
333
|
-
const rawequalFunction = new LuaBuiltinFunction(
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
471
|
+
const rawequalFunction = new LuaBuiltinFunction({
|
|
472
|
+
callback: (_sf, a: any, b: any) => {
|
|
473
|
+
const av = isTaggedFloat(a) ? a.value : a;
|
|
474
|
+
const bv = isTaggedFloat(b) ? b.value : b;
|
|
475
|
+
return av === bv;
|
|
476
|
+
},
|
|
477
|
+
description: "Tests two values for equality without invoking `__eq`.",
|
|
478
|
+
parameters: [{ name: "a" }, { name: "b" }],
|
|
479
|
+
returns: [{ type: "boolean" }],
|
|
337
480
|
});
|
|
338
481
|
|
|
339
|
-
const getmetatableFunction = new LuaBuiltinFunction(
|
|
340
|
-
|
|
482
|
+
const getmetatableFunction = new LuaBuiltinFunction({
|
|
483
|
+
callback: (_sf, table: LuaTable) => (table as any).metatable,
|
|
484
|
+
description: "Returns a table's metatable, or `nil` when none is set.",
|
|
485
|
+
parameters: [{ name: "table", type: "table" }],
|
|
486
|
+
returns: [{ type: "table|nil" }],
|
|
341
487
|
});
|
|
342
488
|
|
|
343
|
-
const dofileFunction = new LuaBuiltinFunction(
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
)) as Uint8Array;
|
|
351
|
-
const code = new TextDecoder().decode(file);
|
|
352
|
-
try {
|
|
353
|
-
const parsedExpr = parseBlock(code);
|
|
354
|
-
const env = new LuaEnv(global);
|
|
355
|
-
await evalStatement(parsedExpr, env, sf.withCtx(parsedExpr.ctx));
|
|
356
|
-
} catch (e: any) {
|
|
357
|
-
throw new LuaRuntimeError(
|
|
358
|
-
`Error evaluating "${filename}": ${e.message}`,
|
|
489
|
+
const dofileFunction = new LuaBuiltinFunction({
|
|
490
|
+
callback: async (sf, filename: string) => {
|
|
491
|
+
const global = sf.threadLocal.get("_GLOBAL") as LuaEnv;
|
|
492
|
+
const file = (await luaCall(
|
|
493
|
+
(global.get("space") as any).get("readFile"),
|
|
494
|
+
[filename],
|
|
495
|
+
sf.astCtx!,
|
|
359
496
|
sf,
|
|
360
|
-
);
|
|
361
|
-
|
|
497
|
+
)) as Uint8Array;
|
|
498
|
+
const code = new TextDecoder().decode(file);
|
|
499
|
+
try {
|
|
500
|
+
const parsedExpr = parseBlock(code);
|
|
501
|
+
const env = new LuaEnv(global);
|
|
502
|
+
await evalStatement(parsedExpr, env, sf.withCtx(parsedExpr.ctx));
|
|
503
|
+
} catch (e: any) {
|
|
504
|
+
// A user-initiated stop must keep its identity so an enclosing
|
|
505
|
+
// pcall/xpcall still recognizes and rethrows it, rather than being
|
|
506
|
+
// rewrapped into a recoverable LuaRuntimeError.
|
|
507
|
+
if (e instanceof LuaBudgetStopped) {
|
|
508
|
+
throw e;
|
|
509
|
+
}
|
|
510
|
+
throw new LuaRuntimeError(
|
|
511
|
+
`Error evaluating "${filename}": ${e.message}`,
|
|
512
|
+
sf,
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
},
|
|
516
|
+
description: "Reads and executes a Lua source file from the current space.",
|
|
517
|
+
parameters: [
|
|
518
|
+
{
|
|
519
|
+
name: "path",
|
|
520
|
+
type: "string",
|
|
521
|
+
description: "Space-relative Lua file path.",
|
|
522
|
+
},
|
|
523
|
+
],
|
|
362
524
|
});
|
|
363
525
|
|
|
364
526
|
/**
|
|
@@ -369,8 +531,8 @@ const dofileFunction = new LuaBuiltinFunction(async (sf, filename: string) => {
|
|
|
369
531
|
* argument). Otherwise, index must be the string "#", and select
|
|
370
532
|
* returns the total number of extra arguments it received.
|
|
371
533
|
*/
|
|
372
|
-
const selectFunction = new LuaBuiltinFunction(
|
|
373
|
-
(_sf, index: number | "#", ...args: LuaValue[]) => {
|
|
534
|
+
const selectFunction = new LuaBuiltinFunction({
|
|
535
|
+
callback: (_sf, index: number | "#", ...args: LuaValue[]) => {
|
|
374
536
|
if (index === "#") {
|
|
375
537
|
return args.length;
|
|
376
538
|
}
|
|
@@ -381,7 +543,19 @@ const selectFunction = new LuaBuiltinFunction(
|
|
|
381
543
|
return new LuaMultiRes(args.slice(args.length + index));
|
|
382
544
|
}
|
|
383
545
|
},
|
|
384
|
-
|
|
546
|
+
description:
|
|
547
|
+
"Returns the count of extra arguments or all arguments from a selected position onward.",
|
|
548
|
+
signatures: ['select("#", ...): integer', "select(index, ...): ..."],
|
|
549
|
+
parameters: [
|
|
550
|
+
{
|
|
551
|
+
name: "index",
|
|
552
|
+
type: "integer|string",
|
|
553
|
+
description: "One-based index, negative index from the end, or `#`.",
|
|
554
|
+
},
|
|
555
|
+
{ name: "..." },
|
|
556
|
+
],
|
|
557
|
+
returns: [{ description: "Argument count or selected argument values." }],
|
|
558
|
+
});
|
|
385
559
|
|
|
386
560
|
/**
|
|
387
561
|
* From the Lua docs:
|
|
@@ -403,65 +577,120 @@ const selectFunction = new LuaBuiltinFunction(
|
|
|
403
577
|
* during its traversal. You may however modify existing fields. In
|
|
404
578
|
* particular, you may set existing fields to nil.
|
|
405
579
|
*/
|
|
406
|
-
const nextFunction = new LuaBuiltinFunction(
|
|
407
|
-
|
|
580
|
+
const nextFunction = new LuaBuiltinFunction({
|
|
581
|
+
callback: (
|
|
582
|
+
sf,
|
|
583
|
+
table: LuaTable | Record<string, any>,
|
|
584
|
+
index: number | null = null,
|
|
585
|
+
) => {
|
|
408
586
|
if (!table) {
|
|
409
|
-
// When nil value
|
|
410
587
|
return null;
|
|
411
588
|
}
|
|
412
589
|
const keys = luaKeys(table);
|
|
413
590
|
|
|
414
|
-
// Empty table -> null return value
|
|
415
591
|
if (keys.length === 0) {
|
|
416
592
|
return null;
|
|
417
593
|
}
|
|
418
594
|
|
|
419
595
|
if (index === null) {
|
|
420
|
-
// Return the first key, value
|
|
421
596
|
const key = keys[0];
|
|
422
597
|
return new LuaMultiRes([key, luaGet(table, key, sf.astCtx ?? null, sf)]);
|
|
423
598
|
}
|
|
424
|
-
// Find index in the key list
|
|
425
599
|
const idx = keys.indexOf(index);
|
|
426
600
|
if (idx === -1) {
|
|
427
|
-
// Not found
|
|
428
601
|
throw new LuaRuntimeError("invalid key to 'next': key not found", sf);
|
|
429
602
|
}
|
|
430
603
|
const key = keys[idx + 1];
|
|
431
604
|
if (key === undefined) {
|
|
432
|
-
// When called with the last key, should return nil
|
|
433
605
|
return null;
|
|
434
606
|
}
|
|
435
607
|
return new LuaMultiRes([key, luaGet(table, key, sf.astCtx ?? null, sf)]);
|
|
436
608
|
},
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
break;
|
|
448
|
-
case "table":
|
|
449
|
-
if (luaKeys(value).length === 0) return null;
|
|
450
|
-
}
|
|
451
|
-
return value;
|
|
609
|
+
description:
|
|
610
|
+
"Returns the next table key and value after a given key, or the first pair when the key is omitted.",
|
|
611
|
+
parameters: [
|
|
612
|
+
{ name: "table", type: "table" },
|
|
613
|
+
{ name: "index", description: "Previous key.", optional: true },
|
|
614
|
+
],
|
|
615
|
+
returns: [
|
|
616
|
+
{ description: "Next key or `nil`." },
|
|
617
|
+
{ description: "Value at the next key." },
|
|
618
|
+
],
|
|
452
619
|
});
|
|
453
620
|
|
|
454
|
-
const
|
|
621
|
+
const someFunction = new LuaBuiltinFunction({
|
|
622
|
+
callback: async (_sf, value: any) => {
|
|
623
|
+
switch (await luaTypeOf(value)) {
|
|
624
|
+
case "number":
|
|
625
|
+
if (!Number.isFinite(value)) return null;
|
|
626
|
+
break;
|
|
627
|
+
case "string":
|
|
628
|
+
if (value.trim() === "") return null;
|
|
629
|
+
break;
|
|
630
|
+
case "table":
|
|
631
|
+
if (luaKeys(value).length === 0) return null;
|
|
632
|
+
}
|
|
633
|
+
return value;
|
|
634
|
+
},
|
|
635
|
+
description:
|
|
636
|
+
"Returns `nil` for empty Space Lua values and otherwise returns the value unchanged.",
|
|
637
|
+
parameters: [
|
|
638
|
+
{
|
|
639
|
+
name: "value",
|
|
640
|
+
description:
|
|
641
|
+
"Value to normalize; blank strings, empty tables, infinities, and NaN are empty.",
|
|
642
|
+
},
|
|
643
|
+
],
|
|
644
|
+
returns: [{ description: "Original value or `nil`." }],
|
|
645
|
+
examples: [
|
|
646
|
+
{
|
|
647
|
+
code: 'print(some(" ") or "empty")\nprint(some({}) or "empty")\nprint(some(0))',
|
|
648
|
+
},
|
|
649
|
+
],
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
const loadFunction = new LuaBuiltinFunction({
|
|
653
|
+
callback: (sf, s) => luaLoad(s, sf),
|
|
654
|
+
description:
|
|
655
|
+
"Compiles Lua source into a callable chunk without executing it.",
|
|
656
|
+
parameters: [
|
|
657
|
+
{ name: "chunk", type: "string", description: "Lua source code." },
|
|
658
|
+
],
|
|
659
|
+
returns: [
|
|
660
|
+
{ type: "function|nil", description: "Compiled chunk or `nil`." },
|
|
661
|
+
{ type: "string", description: "Compilation error when unsuccessful." },
|
|
662
|
+
],
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
function annotateBuiltinApi(
|
|
666
|
+
value: unknown,
|
|
667
|
+
path: string,
|
|
668
|
+
page: string,
|
|
669
|
+
seen = new WeakSet<object>(),
|
|
670
|
+
): void {
|
|
671
|
+
if (!value || typeof value !== "object" || seen.has(value)) return;
|
|
672
|
+
seen.add(value);
|
|
673
|
+
if (isILuaFunction(value)) {
|
|
674
|
+
value.info ??= { kind: "builtin" };
|
|
675
|
+
value.info.name ??= path;
|
|
676
|
+
value.info.see ??= page;
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
if (value instanceof LuaTable) {
|
|
680
|
+
for (const key of value.keys()) {
|
|
681
|
+
if (typeof key !== "string") continue;
|
|
682
|
+
annotateBuiltinApi(value.rawGet(key), `${path}.${key}`, page, seen);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
455
686
|
|
|
456
687
|
export function luaBuildStandardEnv() {
|
|
457
688
|
const env = new LuaEnv();
|
|
458
|
-
// _G global
|
|
459
689
|
env.set("_G", env);
|
|
460
690
|
// Lua version string - for now it signals Lua 5.4 compatibility with
|
|
461
691
|
// selective 5.5 features; kept non-standard so callers can distinguish
|
|
462
692
|
// Space Lua from a plain Lua runtime.
|
|
463
693
|
env.set("_VERSION", "Lua 5.4+");
|
|
464
|
-
// Top-level builtins
|
|
465
694
|
env.set("print", printFunction);
|
|
466
695
|
env.set("assert", assertFunction);
|
|
467
696
|
env.set("type", typeFunction);
|
|
@@ -469,10 +698,8 @@ export function luaBuildStandardEnv() {
|
|
|
469
698
|
env.set("tonumber", tonumberFunction);
|
|
470
699
|
env.set("select", selectFunction);
|
|
471
700
|
env.set("next", nextFunction);
|
|
472
|
-
// Iterators
|
|
473
701
|
env.set("pairs", pairsFunction);
|
|
474
702
|
env.set("ipairs", ipairsFunction);
|
|
475
|
-
// meta table stuff
|
|
476
703
|
env.set("setmetatable", setmetatableFunction);
|
|
477
704
|
env.set("getmetatable", getmetatableFunction);
|
|
478
705
|
env.set("rawlen", rawlenFunction);
|
|
@@ -480,24 +707,26 @@ export function luaBuildStandardEnv() {
|
|
|
480
707
|
env.set("rawget", rawgetFunction);
|
|
481
708
|
env.set("rawequal", rawequalFunction);
|
|
482
709
|
env.set("dofile", dofileFunction);
|
|
483
|
-
// Error handling
|
|
484
710
|
env.set("error", errorFunction);
|
|
485
711
|
env.set("pcall", pcallFunction);
|
|
486
712
|
env.set("xpcall", xpcallFunction);
|
|
487
|
-
// Evaluation
|
|
488
713
|
env.set("load", loadFunction);
|
|
489
|
-
// APIs
|
|
490
714
|
env.set("string", stringApi);
|
|
491
715
|
env.set("table", tableApi);
|
|
492
716
|
env.set("os", osApi);
|
|
493
717
|
env.set("js", jsApi);
|
|
494
718
|
env.set("math", mathApi);
|
|
495
|
-
// Non-standard
|
|
496
719
|
env.set("each", eachFunction);
|
|
497
720
|
env.set("spacelua", spaceluaApi);
|
|
498
721
|
env.set("encoding", encodingApi);
|
|
499
722
|
env.set("crypto", cryptoApi);
|
|
500
723
|
env.set("net", netApi);
|
|
501
724
|
env.set("some", someFunction);
|
|
725
|
+
|
|
726
|
+
for (const name of env.keys()) {
|
|
727
|
+
const value = env.get(name);
|
|
728
|
+
const page = value instanceof LuaTable ? `API/${name}` : "API/global";
|
|
729
|
+
annotateBuiltinApi(value, name, page);
|
|
730
|
+
}
|
|
502
731
|
return env;
|
|
503
732
|
}
|