@silverbulletmd/silverbullet 2.10.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 +4 -4
- 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 +0 -4
- 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/editor.ts +128 -53
- package/client/plugos/syscalls/event.ts +0 -2
- package/client/plugos/syscalls/fetch.ts +0 -4
- package/client/plugos/syscalls/icon.ts +46 -0
- package/client/plugos/syscalls/index.ts +33 -13
- package/client/plugos/syscalls/jsonschema.ts +0 -1
- package/client/plugos/syscalls/mq.ts +0 -1
- package/client/plugos/syscalls/navigator.ts +143 -0
- package/client/plugos/syscalls/search.ts +44 -0
- package/client/plugos/syscalls/shell.ts +0 -1
- package/client/plugos/syscalls/space.ts +250 -7
- package/client/plugos/syscalls/sync.ts +56 -4
- package/client/plugos/syscalls/system.ts +38 -0
- package/client/plugos/system.ts +7 -5
- package/client/plugos/worker_runtime.ts +0 -2
- package/client/space_lua/aggregates.ts +0 -16
- package/client/space_lua/ast.ts +0 -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 +113 -88
- package/client/space_lua/labels.ts +0 -4
- package/client/space_lua/numeric.ts +0 -5
- package/client/space_lua/parse.ts +0 -17
- package/client/space_lua/quarantine.ts +127 -0
- package/client/space_lua/query_collection.ts +3 -47
- 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 +18 -1
- package/client/space_lua/runtime.ts +50 -70
- package/client/space_lua/stdlib/format.ts +0 -12
- package/client/space_lua/stdlib/js.ts +0 -2
- package/client/space_lua/stdlib/load.ts +0 -1
- package/client/space_lua/stdlib/math.ts +0 -7
- package/client/space_lua/stdlib/net.ts +0 -4
- package/client/space_lua/stdlib/os.ts +0 -10
- package/client/space_lua/stdlib/pattern.ts +10 -15
- package/client/space_lua/stdlib/space_lua.ts +1 -2
- package/client/space_lua/stdlib/string_pack.ts +0 -3
- package/client/space_lua/stdlib/table.ts +0 -5
- package/client/space_lua/stdlib.ts +18 -31
- package/client/space_lua/tonumber.ts +0 -9
- package/dist/plug-compile.js +2 -3
- package/package.json +13 -3
- 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/lib/ref.ts +28 -5
- package/plug-api/lib/resolve.ts +0 -1
- package/plug-api/lib/resolve_path.ts +290 -0
- package/plug-api/lib/shortcut.ts +15 -0
- 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 +2 -2
- package/plug-api/syscalls/editor.ts +59 -213
- package/plug-api/syscalls/icon.ts +18 -0
- package/plug-api/syscalls/index.ts +22 -4
- package/plug-api/syscalls/search.ts +11 -0
- package/plug-api/syscalls/space.ts +122 -5
- package/plug-api/syscalls/sync.ts +23 -2
- package/plug-api/syscalls/system.ts +46 -0
- package/plug-api/syscalls.ts +2 -0
- package/plug-api/system_mock.ts +13 -0
- package/plug-api/types/client.ts +4 -3
- package/plug-api/types/datastore.ts +11 -2
- package/plug-api/types/index.ts +10 -0
- package/plug-api/types/profile.ts +13 -0
- package/plug-api/types/revisions.ts +67 -0
- package/plug-api/ui/description.ts +64 -0
- package/plug-api/ui/hover.ts +69 -0
- package/plug-api/ui/index.ts +58 -2
- package/plug-api/ui/scroll.ts +30 -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/plug-api/lib/memory_cache.ts +0 -21
- /package/plug-api/{ui → lib}/panel_styles.ts +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ASTCtx, LuaFunctionBody, NumericType } from "./ast.ts";
|
|
2
|
+
import type { LuaBudget } from "./budget.ts";
|
|
2
3
|
import { evalStatement } from "./eval.ts";
|
|
3
4
|
import { asyncQuickSort } from "./util.ts";
|
|
4
5
|
import { isPromise, rpAll } from "./rp.ts";
|
|
@@ -10,6 +11,14 @@ import type {
|
|
|
10
11
|
LuaFunctionInfo,
|
|
11
12
|
} from "../../plug-api/types/index.ts";
|
|
12
13
|
|
|
14
|
+
let boundaryBudgetFactory: (() => LuaBudget) | undefined;
|
|
15
|
+
|
|
16
|
+
export function setBoundaryBudgetFactory(
|
|
17
|
+
f: (() => LuaBudget) | undefined,
|
|
18
|
+
): void {
|
|
19
|
+
boundaryBudgetFactory = f;
|
|
20
|
+
}
|
|
21
|
+
|
|
13
22
|
export type LuaType =
|
|
14
23
|
| "nil"
|
|
15
24
|
| "boolean"
|
|
@@ -20,7 +29,6 @@ export type LuaType =
|
|
|
20
29
|
| "userdata"
|
|
21
30
|
| "thread";
|
|
22
31
|
|
|
23
|
-
// These types are for documentation only
|
|
24
32
|
export type LuaValue = any;
|
|
25
33
|
export type JSValue = any;
|
|
26
34
|
|
|
@@ -53,7 +61,6 @@ export interface ILuaGettable {
|
|
|
53
61
|
getNumericType?(key: LuaValue): NumericType | undefined;
|
|
54
62
|
}
|
|
55
63
|
|
|
56
|
-
// Small helpers for type safety/readability
|
|
57
64
|
export function isILuaFunction(v: unknown): v is ILuaFunction {
|
|
58
65
|
return !!v && typeof (v as any).call === "function";
|
|
59
66
|
}
|
|
@@ -81,11 +88,11 @@ const EMPTY_CTX = {} as ASTCtx;
|
|
|
81
88
|
|
|
82
89
|
const MAX_TAG_LOOP = 200;
|
|
83
90
|
|
|
84
|
-
// Close-stack support
|
|
85
91
|
export type LuaCloseEntry = { value: LuaValue; ctx: ASTCtx };
|
|
86
92
|
|
|
87
93
|
type LuaThreadState = {
|
|
88
94
|
closeStack?: LuaCloseEntry[];
|
|
95
|
+
budget?: LuaBudget;
|
|
89
96
|
};
|
|
90
97
|
|
|
91
98
|
function isLuaNumber(v: any): boolean {
|
|
@@ -336,14 +343,10 @@ export class LuaEnv implements ILuaSettable, ILuaGettable {
|
|
|
336
343
|
}
|
|
337
344
|
|
|
338
345
|
get(name: string, _sf?: LuaStackFrame): Promise<LuaValue> | LuaValue | null {
|
|
339
|
-
// Fast path: single Map.get() instead of has() + get() for the common case
|
|
340
|
-
// where the variable exists and has a non-undefined value (null = Lua nil is fine).
|
|
341
346
|
const v = this.variables.get(name);
|
|
342
347
|
if (v !== undefined) {
|
|
343
348
|
return v;
|
|
344
349
|
}
|
|
345
|
-
// Variable set to null (Lua nil) returns null from Map.get() which is !== undefined,
|
|
346
|
-
// so it's handled above. Only fall through when the key is truly absent.
|
|
347
350
|
if (this.parent) {
|
|
348
351
|
return this.parent.get(name, _sf);
|
|
349
352
|
}
|
|
@@ -488,39 +491,31 @@ export class LuaFunction implements ILuaFunction {
|
|
|
488
491
|
}
|
|
489
492
|
|
|
490
493
|
call(sf: LuaStackFrame, ...args: LuaValue[]): Promise<LuaValue> | LuaValue {
|
|
491
|
-
// Create a new environment that chains to the captured environment
|
|
492
494
|
const env = new LuaEnv(this.capturedEnv);
|
|
493
|
-
// Set _CTX to the thread local environment from the stack frame
|
|
494
495
|
env.setLocal("_CTX", sf.threadLocal);
|
|
495
496
|
|
|
496
|
-
// Eval using a stack frame that knows the current function
|
|
497
497
|
const sfWithFn = sf.currentFunction === this ? sf : sf.withFunction(this);
|
|
498
498
|
|
|
499
499
|
const params = this.body.parameters;
|
|
500
500
|
const hasVarargs = params.length > 0 && params[params.length - 1] === "...";
|
|
501
501
|
|
|
502
|
-
// Resolve args (sync-first)
|
|
503
502
|
const argsRP = rpAll(args as any[]);
|
|
504
503
|
const resolveArgs = (resolvedArgs: any[]) => {
|
|
505
504
|
if (hasVarargs) {
|
|
506
|
-
// Variadic function: bind named params, collect rest into varargs
|
|
507
505
|
const varargStart = params.length - 1;
|
|
508
506
|
for (let i = 0; i < varargStart; i++) {
|
|
509
507
|
env.setLocal(params[i], resolvedArgs[i] ?? null);
|
|
510
508
|
}
|
|
511
509
|
env.setLocal("...", new LuaMultiRes(resolvedArgs.slice(varargStart)));
|
|
512
510
|
} else {
|
|
513
|
-
// Non-variadic: bind all named params directly
|
|
514
511
|
for (let i = 0; i < params.length; i++) {
|
|
515
512
|
env.setLocal(params[i], resolvedArgs[i] ?? null);
|
|
516
513
|
}
|
|
517
514
|
}
|
|
518
515
|
|
|
519
|
-
// Evaluate the function body with returnOnReturn set to true
|
|
520
516
|
const r = evalStatement(this.body.block, env, sfWithFn, true);
|
|
521
517
|
|
|
522
518
|
if (!isPromise(r)) {
|
|
523
|
-
// Fast path: synchronous result
|
|
524
519
|
if (r === undefined) return;
|
|
525
520
|
if (r && typeof r === "object" && r.ctrl === "return") {
|
|
526
521
|
return mapFunctionReturnValue(r.values);
|
|
@@ -675,14 +670,10 @@ export class LuaBuiltinFunction implements ILuaFunction {
|
|
|
675
670
|
}
|
|
676
671
|
|
|
677
672
|
export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
678
|
-
// To optimize the table implementation we use a combination of different data structures
|
|
679
673
|
public metatable: LuaTable | null;
|
|
680
674
|
|
|
681
|
-
// When tables are used as maps, the common case is that they are string keys, so we use a simple object for that
|
|
682
675
|
private stringKeys: Record<string, any>;
|
|
683
|
-
// Other keys we can support using a Map as a fallback
|
|
684
676
|
private otherKeys: Map<any, any> | null;
|
|
685
|
-
// When tables are used as arrays, we use a native JavaScript array for that
|
|
686
677
|
private arrayPart: any[];
|
|
687
678
|
|
|
688
679
|
// Numeric type metadata at storage boundaries (lazily allocated)
|
|
@@ -719,15 +710,12 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
719
710
|
|
|
720
711
|
const numVal = LuaTable.numKeyValue(key);
|
|
721
712
|
if (numVal !== null) {
|
|
722
|
-
// Normalize -0 to +0
|
|
723
713
|
if (isNegativeZero(numVal)) {
|
|
724
714
|
return 0;
|
|
725
715
|
}
|
|
726
|
-
// Integer-valued numbers normalize to integers
|
|
727
716
|
if (Number.isInteger(numVal)) {
|
|
728
717
|
return numVal;
|
|
729
718
|
}
|
|
730
|
-
// Non-integer floats
|
|
731
719
|
return numVal;
|
|
732
720
|
}
|
|
733
721
|
return key;
|
|
@@ -799,7 +787,6 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
799
787
|
return this.stringKeys[key] !== undefined;
|
|
800
788
|
}
|
|
801
789
|
|
|
802
|
-
// Fast path for plain integer keys (common in for loops)
|
|
803
790
|
if (typeof key === "number") {
|
|
804
791
|
if (key >= 1 && (key | 0) === key) {
|
|
805
792
|
if (this.arrayPart[key - 1] !== undefined) return true;
|
|
@@ -892,7 +879,6 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
892
879
|
return value.then((v) => this.rawSet(key, v, numType));
|
|
893
880
|
}
|
|
894
881
|
|
|
895
|
-
// Fast path: string keys (the dominant case)
|
|
896
882
|
if (typeof key === "string") {
|
|
897
883
|
if (value === null || value === undefined) {
|
|
898
884
|
delete this.stringKeys[key];
|
|
@@ -951,7 +937,6 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
951
937
|
this.promoteIntegerKeysFromHash();
|
|
952
938
|
}
|
|
953
939
|
|
|
954
|
-
// Trailing nil shrink
|
|
955
940
|
if (value === null || value === undefined) {
|
|
956
941
|
let n = this.arrayPart.length;
|
|
957
942
|
while (n > 0) {
|
|
@@ -971,7 +956,6 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
971
956
|
return;
|
|
972
957
|
}
|
|
973
958
|
|
|
974
|
-
// Sparse numeric key
|
|
975
959
|
if (!this.otherKeys) {
|
|
976
960
|
this.otherKeys = new Map();
|
|
977
961
|
}
|
|
@@ -1033,12 +1017,10 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1033
1017
|
);
|
|
1034
1018
|
}
|
|
1035
1019
|
|
|
1036
|
-
// Fast path: no metatable — skip has() check and metamethod machinery
|
|
1037
1020
|
if (this.metatable === null) {
|
|
1038
1021
|
return this.rawSet(key, value, numType);
|
|
1039
1022
|
}
|
|
1040
1023
|
|
|
1041
|
-
// Key exists — rawSet directly, no metamethod needed
|
|
1042
1024
|
if (this.has(key)) {
|
|
1043
1025
|
return this.rawSet(key, value, numType);
|
|
1044
1026
|
}
|
|
@@ -1049,7 +1031,6 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1049
1031
|
return this.rawSet(key, value, numType);
|
|
1050
1032
|
}
|
|
1051
1033
|
|
|
1052
|
-
// Slow path: __newindex metamethod chain (rare)
|
|
1053
1034
|
const errSf = sf || LuaStackFrame.lostFrame;
|
|
1054
1035
|
const ctx = sf?.astCtx ?? EMPTY_CTX;
|
|
1055
1036
|
const k: LuaValue = key;
|
|
@@ -1078,12 +1059,10 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1078
1059
|
return t.rawSet(k, v, nt);
|
|
1079
1060
|
}
|
|
1080
1061
|
|
|
1081
|
-
// Function metamethod: call and stop
|
|
1082
1062
|
if (typeof mm === "function" || isILuaFunction(mm)) {
|
|
1083
1063
|
return luaCall(mm, [t, k, v], ctx, errSf);
|
|
1084
1064
|
}
|
|
1085
1065
|
|
|
1086
|
-
// Table/env metamethod: forward assignment
|
|
1087
1066
|
if (mm instanceof LuaTable || mm instanceof LuaEnv) {
|
|
1088
1067
|
target = mm;
|
|
1089
1068
|
continue;
|
|
@@ -1113,7 +1092,6 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1113
1092
|
if (typeof key === "string") {
|
|
1114
1093
|
return this.stringKeyTypes ? this.stringKeyTypes.get(key) : undefined;
|
|
1115
1094
|
}
|
|
1116
|
-
// Fast path for plain integer keys
|
|
1117
1095
|
if (typeof key === "number") {
|
|
1118
1096
|
if (key >= 1 && (key | 0) === key) {
|
|
1119
1097
|
return this.arrayTypes ? this.arrayTypes[key - 1] : undefined;
|
|
@@ -1136,7 +1114,6 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1136
1114
|
return this.stringKeys[key];
|
|
1137
1115
|
}
|
|
1138
1116
|
|
|
1139
|
-
// Fast path for plain integer keys (common in for loops)
|
|
1140
1117
|
if (typeof key === "number") {
|
|
1141
1118
|
if (key >= 1 && (key | 0) === key) {
|
|
1142
1119
|
const v = this.arrayPart[key - 1];
|
|
@@ -1144,7 +1121,6 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1144
1121
|
if (this.otherKeys) return this.otherKeys.get(key);
|
|
1145
1122
|
return undefined;
|
|
1146
1123
|
}
|
|
1147
|
-
// Non-integer or zero/negative number keys
|
|
1148
1124
|
if (this.otherKeys) return this.otherKeys.get(key);
|
|
1149
1125
|
return undefined;
|
|
1150
1126
|
}
|
|
@@ -1178,7 +1154,6 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1178
1154
|
}
|
|
1179
1155
|
|
|
1180
1156
|
get(key: LuaValue, sf?: LuaStackFrame): LuaValue | Promise<LuaValue> | null {
|
|
1181
|
-
// Fast path: no metatable means rawGet is the final answer (most tables in SilverBullet)
|
|
1182
1157
|
if (this.metatable === null) {
|
|
1183
1158
|
const raw = this.rawGet(key);
|
|
1184
1159
|
return raw !== undefined ? raw : null;
|
|
@@ -1212,11 +1187,9 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1212
1187
|
result[k] = luaValueToJS(this.stringKeys[k], sf);
|
|
1213
1188
|
}
|
|
1214
1189
|
}
|
|
1215
|
-
// Include array part with 1-based keys
|
|
1216
1190
|
for (let i = 0; i < this.arrayPart.length; i++) {
|
|
1217
1191
|
result[i + 1] = luaValueToJS(this.arrayPart[i], sf);
|
|
1218
1192
|
}
|
|
1219
|
-
// Include other keys
|
|
1220
1193
|
if (this.otherKeys) {
|
|
1221
1194
|
for (const [key, val] of this.otherKeys) {
|
|
1222
1195
|
result[key] = luaValueToJS(val, sf);
|
|
@@ -1299,14 +1272,12 @@ export function luaIndexValue(
|
|
|
1299
1272
|
let t: LuaValue = value;
|
|
1300
1273
|
|
|
1301
1274
|
for (let loop = 0; loop < MAX_TAG_LOOP; loop++) {
|
|
1302
|
-
// Primitive get when table
|
|
1303
1275
|
if (t instanceof LuaTable) {
|
|
1304
1276
|
const raw = t.rawGet(key);
|
|
1305
1277
|
if (raw !== undefined) {
|
|
1306
1278
|
if (isSqlNull(raw)) return null;
|
|
1307
1279
|
return raw;
|
|
1308
1280
|
}
|
|
1309
|
-
// If no metatable, raw miss => nil
|
|
1310
1281
|
if (t.metatable === null) {
|
|
1311
1282
|
return null;
|
|
1312
1283
|
}
|
|
@@ -1328,18 +1299,15 @@ export function luaIndexValue(
|
|
|
1328
1299
|
);
|
|
1329
1300
|
}
|
|
1330
1301
|
|
|
1331
|
-
// Function metamethod
|
|
1332
1302
|
if (typeof mm === "function" || isILuaFunction(mm)) {
|
|
1333
1303
|
return luaCall(mm, [t, key], ctx, errSf);
|
|
1334
1304
|
}
|
|
1335
1305
|
|
|
1336
|
-
// Table/metatable delegation: repeat with mm as new "t"
|
|
1337
1306
|
if (mm instanceof LuaTable || mm instanceof LuaEnv) {
|
|
1338
1307
|
t = mm;
|
|
1339
1308
|
continue;
|
|
1340
1309
|
}
|
|
1341
1310
|
|
|
1342
|
-
// Bad metamethod type: make it a Lua-like type error
|
|
1343
1311
|
const ty = luaTypeOf(mm) as any as string;
|
|
1344
1312
|
throw new LuaRuntimeError(
|
|
1345
1313
|
`attempt to index a ${ty} value`,
|
|
@@ -1413,11 +1381,9 @@ export function luaGet(
|
|
|
1413
1381
|
if (isTaggedFloat(key)) {
|
|
1414
1382
|
return (obj as any[])[key.value - 1] ?? null;
|
|
1415
1383
|
}
|
|
1416
|
-
// Native JS object
|
|
1417
1384
|
const k = toNumKey(key);
|
|
1418
1385
|
const val = (obj as Record<string | number, any>)[k];
|
|
1419
1386
|
if (typeof val === "function") {
|
|
1420
|
-
// Automatically bind the function to the object
|
|
1421
1387
|
return val.bind(obj);
|
|
1422
1388
|
}
|
|
1423
1389
|
if (val === undefined) {
|
|
@@ -1438,7 +1404,6 @@ export function luaLen(
|
|
|
1438
1404
|
return obj.length;
|
|
1439
1405
|
}
|
|
1440
1406
|
if (obj instanceof LuaTable) {
|
|
1441
|
-
// Check __len metamethod unless raw access is requested
|
|
1442
1407
|
if (!raw) {
|
|
1443
1408
|
const mt = getMetatable(obj, sf || LuaStackFrame.lostFrame);
|
|
1444
1409
|
const mm = mt ? mt.rawGet("__len") : null;
|
|
@@ -1473,7 +1438,6 @@ export function luaCall(
|
|
|
1473
1438
|
);
|
|
1474
1439
|
}
|
|
1475
1440
|
|
|
1476
|
-
// Fast path: native JS function
|
|
1477
1441
|
if (typeof callee === "function") {
|
|
1478
1442
|
const baseSf = sf || LuaStackFrame.lostFrame;
|
|
1479
1443
|
const len = args.length;
|
|
@@ -1494,7 +1458,6 @@ export function luaCall(
|
|
|
1494
1458
|
return (callee as (...a: any[]) => any)(...args);
|
|
1495
1459
|
}
|
|
1496
1460
|
|
|
1497
|
-
// Lua table: may be callable via __call metamethod
|
|
1498
1461
|
if (callee instanceof LuaTable) {
|
|
1499
1462
|
const metatable = getMetatable(callee, sf);
|
|
1500
1463
|
|
|
@@ -1525,7 +1488,6 @@ export function luaCall(
|
|
|
1525
1488
|
}
|
|
1526
1489
|
}
|
|
1527
1490
|
|
|
1528
|
-
// ILuaFunction (LuaFunction/LuaBuiltinFunction/LuaNativeJSFunction/etc.)
|
|
1529
1491
|
if (isILuaFunction(callee)) {
|
|
1530
1492
|
// Fast path for builtins: skip withFunction, defer withCtx
|
|
1531
1493
|
if (callee instanceof LuaBuiltinFunction) {
|
|
@@ -1550,7 +1512,6 @@ export function luaCall(
|
|
|
1550
1512
|
}
|
|
1551
1513
|
|
|
1552
1514
|
export function luaEquals(a: any, b: any): boolean {
|
|
1553
|
-
// Normalize nil variants (null, undefined, SQL NULL) to null
|
|
1554
1515
|
const an =
|
|
1555
1516
|
a === null || a === undefined || isSqlNull(a)
|
|
1556
1517
|
? null
|
|
@@ -1630,7 +1591,6 @@ export class LuaRuntimeError extends Error {
|
|
|
1630
1591
|
if (!ctx || !ctx.from || !ctx.to) {
|
|
1631
1592
|
break;
|
|
1632
1593
|
}
|
|
1633
|
-
// Find the line and column
|
|
1634
1594
|
let line = 1;
|
|
1635
1595
|
let column = 0;
|
|
1636
1596
|
let lastNewline = -1;
|
|
@@ -1644,7 +1604,6 @@ export class LuaRuntimeError extends Error {
|
|
|
1644
1604
|
}
|
|
1645
1605
|
}
|
|
1646
1606
|
|
|
1647
|
-
// Get the full line of code for context
|
|
1648
1607
|
const lineStart = lastNewline + 1;
|
|
1649
1608
|
const lineEnd = code.indexOf("\n", ctx.from);
|
|
1650
1609
|
const codeLine = code.substring(
|
|
@@ -1652,7 +1611,6 @@ export class LuaRuntimeError extends Error {
|
|
|
1652
1611
|
lineEnd === -1 ? undefined : lineEnd,
|
|
1653
1612
|
);
|
|
1654
1613
|
|
|
1655
|
-
// Add position indicator
|
|
1656
1614
|
const pointer = `${" ".repeat(column)}^`;
|
|
1657
1615
|
|
|
1658
1616
|
traceStr +=
|
|
@@ -1704,12 +1662,10 @@ export function luaToString(
|
|
|
1704
1662
|
return luaFormatNumber(value);
|
|
1705
1663
|
}
|
|
1706
1664
|
|
|
1707
|
-
// Check for circular references
|
|
1708
1665
|
if (typeof value === "object" && visited.has(value)) {
|
|
1709
1666
|
return "<circular reference>";
|
|
1710
1667
|
}
|
|
1711
1668
|
if ((value as any).toStringAsync) {
|
|
1712
|
-
// Add to visited before recursing
|
|
1713
1669
|
visited.add(value);
|
|
1714
1670
|
return (value as any).toStringAsync();
|
|
1715
1671
|
}
|
|
@@ -1721,15 +1677,12 @@ export function luaToString(
|
|
|
1721
1677
|
// Don't recurse into the function body, just show the function signature
|
|
1722
1678
|
return `<lua-function (${value.body.parameters.join(", ")})>`;
|
|
1723
1679
|
}
|
|
1724
|
-
// Handle plain JavaScript objects in a Lua-like format
|
|
1725
1680
|
if (typeof value === "object") {
|
|
1726
|
-
// Add to visited before recursing
|
|
1727
1681
|
visited.add(value);
|
|
1728
1682
|
return (async () => {
|
|
1729
1683
|
let result = "{";
|
|
1730
1684
|
let first = true;
|
|
1731
1685
|
|
|
1732
|
-
// Handle arrays
|
|
1733
1686
|
if (Array.isArray(value)) {
|
|
1734
1687
|
for (const val of value) {
|
|
1735
1688
|
if (first) {
|
|
@@ -1737,14 +1690,12 @@ export function luaToString(
|
|
|
1737
1690
|
} else {
|
|
1738
1691
|
result += ", ";
|
|
1739
1692
|
}
|
|
1740
|
-
// Recursively stringify the value, passing the visited set
|
|
1741
1693
|
const strVal = await luaToString(val, visited);
|
|
1742
1694
|
result += strVal;
|
|
1743
1695
|
}
|
|
1744
1696
|
return `${result}}`;
|
|
1745
1697
|
}
|
|
1746
1698
|
|
|
1747
|
-
// Handle objects
|
|
1748
1699
|
for (const [key, val] of Object.entries(value)) {
|
|
1749
1700
|
if (first) {
|
|
1750
1701
|
first = false;
|
|
@@ -1756,7 +1707,6 @@ export function luaToString(
|
|
|
1756
1707
|
} else {
|
|
1757
1708
|
result += `["${key}"] = `;
|
|
1758
1709
|
}
|
|
1759
|
-
// Recursively stringify the value, passing the visited set
|
|
1760
1710
|
const strVal = await luaToString(val, visited);
|
|
1761
1711
|
result += strVal;
|
|
1762
1712
|
}
|
|
@@ -1777,7 +1727,6 @@ export function luaFormatNumber(n: number, kind?: "int" | "float"): string {
|
|
|
1777
1727
|
if (n === 0) {
|
|
1778
1728
|
return 1 / n === -Infinity ? "-0.0" : "0.0";
|
|
1779
1729
|
}
|
|
1780
|
-
// Delegate to luaFormat for `%.14g`
|
|
1781
1730
|
const s = luaFormat("%.14g", n);
|
|
1782
1731
|
// Guarantee `.01 suffix for integer-valued floats
|
|
1783
1732
|
if (s.indexOf(".") === -1 && s.indexOf("e") === -1) {
|
|
@@ -1834,7 +1783,6 @@ export function jsToLuaValue(value: any): any {
|
|
|
1834
1783
|
return value;
|
|
1835
1784
|
}
|
|
1836
1785
|
if (Array.isArray(value) && "index" in value && "input" in value) {
|
|
1837
|
-
// This is a RegExpMatchArray
|
|
1838
1786
|
const regexMatch = value as RegExpMatchArray;
|
|
1839
1787
|
const regexMatchTable = new LuaTable();
|
|
1840
1788
|
for (let i = 0; i < regexMatch.length; i++) {
|
|
@@ -1865,7 +1813,6 @@ export function jsToLuaValue(value: any): any {
|
|
|
1865
1813
|
return value;
|
|
1866
1814
|
}
|
|
1867
1815
|
|
|
1868
|
-
// Inverse of jsToLuaValue
|
|
1869
1816
|
export function luaValueToJS(value: any, sf: LuaStackFrame): any {
|
|
1870
1817
|
if (isPromise(value)) {
|
|
1871
1818
|
return (value as Promise<any>).then((v) => luaValueToJS(v, sf));
|
|
@@ -1879,14 +1826,47 @@ export function luaValueToJS(value: any, sf: LuaStackFrame): any {
|
|
|
1879
1826
|
value instanceof LuaBuiltinFunction
|
|
1880
1827
|
) {
|
|
1881
1828
|
return (...args: any[]) => {
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1829
|
+
// A converted closure captures one frame forever; without a fresh
|
|
1830
|
+
// thread state per call, every invocation would share one budget.
|
|
1831
|
+
const boundaryBudget = boundaryBudgetFactory?.();
|
|
1832
|
+
const callSf =
|
|
1833
|
+
boundaryBudget === undefined
|
|
1834
|
+
? sf
|
|
1835
|
+
: new LuaStackFrame(sf.threadLocal, sf.astCtx, undefined, undefined, {
|
|
1836
|
+
closeStack: undefined,
|
|
1837
|
+
budget: boundaryBudget,
|
|
1838
|
+
});
|
|
1839
|
+
// Marks this call's own budget (if any) done, so a Stop click that
|
|
1840
|
+
// arrives after the call has already returned is a no-op rather than
|
|
1841
|
+
// quarantining/disabling something that isn't running anymore.
|
|
1842
|
+
const markFinished = () => {
|
|
1843
|
+
if (boundaryBudget !== undefined) {
|
|
1844
|
+
boundaryBudget.finished = true;
|
|
1845
|
+
}
|
|
1846
|
+
};
|
|
1847
|
+
try {
|
|
1848
|
+
const jsArgs = rpAll(args.map((v) => luaValueToJS(v, callSf)));
|
|
1849
|
+
if (isPromise(jsArgs)) {
|
|
1850
|
+
return luaValueToJS(
|
|
1851
|
+
jsArgs
|
|
1852
|
+
.then((jsArgs) => (value as ILuaFunction).call(callSf, ...jsArgs))
|
|
1853
|
+
.finally(markFinished),
|
|
1854
|
+
callSf,
|
|
1855
|
+
);
|
|
1856
|
+
}
|
|
1857
|
+
const result = (value as ILuaFunction).call(callSf, ...jsArgs);
|
|
1858
|
+
if (isPromise(result)) {
|
|
1859
|
+
return luaValueToJS(
|
|
1860
|
+
(result as Promise<any>).finally(markFinished),
|
|
1861
|
+
callSf,
|
|
1862
|
+
);
|
|
1863
|
+
}
|
|
1864
|
+
markFinished();
|
|
1865
|
+
return luaValueToJS(result, callSf);
|
|
1866
|
+
} catch (e) {
|
|
1867
|
+
markFinished();
|
|
1868
|
+
throw e;
|
|
1888
1869
|
}
|
|
1889
|
-
return luaValueToJS((value as ILuaFunction).call(sf, ...jsArgs), sf);
|
|
1890
1870
|
};
|
|
1891
1871
|
}
|
|
1892
1872
|
if (isTaggedFloat(value)) {
|
|
@@ -56,7 +56,6 @@ function parseSpec(
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
// Parse width
|
|
60
59
|
let width = 0;
|
|
61
60
|
if (i < len && fmt.charCodeAt(i) === 42) {
|
|
62
61
|
// '*'
|
|
@@ -69,7 +68,6 @@ function parseSpec(
|
|
|
69
68
|
}
|
|
70
69
|
}
|
|
71
70
|
|
|
72
|
-
// Parse precision
|
|
73
71
|
let hasPrec = false;
|
|
74
72
|
let prec = 0;
|
|
75
73
|
if (i < len && fmt.charCodeAt(i) === 46) {
|
|
@@ -183,7 +181,6 @@ function formatInt(n: number, spec: FormatSpec): string {
|
|
|
183
181
|
|
|
184
182
|
if (upper) digits = digits.toUpperCase();
|
|
185
183
|
|
|
186
|
-
// Precision
|
|
187
184
|
if (spec.hasPrec) {
|
|
188
185
|
if (spec.prec === 0 && v === 0) {
|
|
189
186
|
digits = "";
|
|
@@ -192,7 +189,6 @@ function formatInt(n: number, spec: FormatSpec): string {
|
|
|
192
189
|
}
|
|
193
190
|
}
|
|
194
191
|
|
|
195
|
-
// Alt flag
|
|
196
192
|
let prefix = "";
|
|
197
193
|
if (spec.flags & FLAG_HASH) {
|
|
198
194
|
if (base === 8 && (digits.length === 0 || digits.charCodeAt(0) !== 48)) {
|
|
@@ -277,7 +273,6 @@ function formatFloat(n: number, spec: FormatSpec): string {
|
|
|
277
273
|
// Alt flag for 'f'/'e': ensure decimal point exists
|
|
278
274
|
if (spec.flags & FLAG_HASH && lower !== 103) {
|
|
279
275
|
if (body.indexOf(".") === -1) {
|
|
280
|
-
// Insert dot before 'e' if present, else append
|
|
281
276
|
const eIdx = body.indexOf("e");
|
|
282
277
|
const EIdx = body.indexOf("E");
|
|
283
278
|
const expIdx = eIdx !== -1 ? eIdx : EIdx;
|
|
@@ -348,7 +343,6 @@ function stripTrailingZerosG(s: string): string {
|
|
|
348
343
|
// '0'
|
|
349
344
|
end--;
|
|
350
345
|
}
|
|
351
|
-
// Remove dot if nothing after it
|
|
352
346
|
if (end === dotIdx + 1) {
|
|
353
347
|
end = dotIdx;
|
|
354
348
|
}
|
|
@@ -452,13 +446,11 @@ function hexFloatBody(abs: number, spec: FormatSpec): string {
|
|
|
452
446
|
let mantBits: bigint;
|
|
453
447
|
|
|
454
448
|
if (biasedExp === 0) {
|
|
455
|
-
// Subnormal
|
|
456
449
|
if (frac === 0n) return "0x0p+0";
|
|
457
450
|
const shift = 52 - bitLength(frac) + 1;
|
|
458
451
|
mantBits = frac << BigInt(shift);
|
|
459
452
|
exponent = -1022 - shift;
|
|
460
453
|
} else {
|
|
461
|
-
// Normal
|
|
462
454
|
exponent = biasedExp - 1023;
|
|
463
455
|
mantBits = frac | (1n << 52n);
|
|
464
456
|
}
|
|
@@ -680,8 +672,6 @@ export function luaFormat(fmt: string, ...args: any[]): string {
|
|
|
680
672
|
while (i < len) {
|
|
681
673
|
const c = fmt.charCodeAt(i);
|
|
682
674
|
if (c !== 37) {
|
|
683
|
-
// not '%'
|
|
684
|
-
// Fast path: scan for next '%' or end
|
|
685
675
|
let j = i + 1;
|
|
686
676
|
while (j < len && fmt.charCodeAt(j) !== 37) j++;
|
|
687
677
|
out += fmt.slice(i, j);
|
|
@@ -689,7 +679,6 @@ export function luaFormat(fmt: string, ...args: any[]): string {
|
|
|
689
679
|
continue;
|
|
690
680
|
}
|
|
691
681
|
|
|
692
|
-
// '%' found
|
|
693
682
|
i++;
|
|
694
683
|
if (i >= len) {
|
|
695
684
|
throw new Error("invalid format (ends with '%')");
|
|
@@ -705,7 +694,6 @@ export function luaFormat(fmt: string, ...args: any[]): string {
|
|
|
705
694
|
const { spec, end } = parseSpec(fmt, i);
|
|
706
695
|
i = end + 1;
|
|
707
696
|
|
|
708
|
-
// Resolve `*` width and precision from args
|
|
709
697
|
let width = spec.width;
|
|
710
698
|
if (width === -1) {
|
|
711
699
|
width = Number(args[ai++]) || 0;
|
|
@@ -41,7 +41,6 @@ export const jsApi = new LuaTable({
|
|
|
41
41
|
import: new LuaBuiltinFunction({
|
|
42
42
|
callback: async (_sf, url) => {
|
|
43
43
|
let m = await import(url);
|
|
44
|
-
// Unwrap default if it exists
|
|
45
44
|
if (Object.keys(m).length === 1 && m.default) {
|
|
46
45
|
m = m.default;
|
|
47
46
|
}
|
|
@@ -179,6 +178,5 @@ export const jsApi = new LuaTable({
|
|
|
179
178
|
examples: [{ code: "print(js.stringify({1, 2, 3})) -- [1,2,3]" }],
|
|
180
179
|
}),
|
|
181
180
|
|
|
182
|
-
// Expose the global window object
|
|
183
181
|
window: globalThis,
|
|
184
182
|
});
|
|
@@ -16,7 +16,6 @@ export function luaLoad(code: LuaValue, sf: LuaStackFrame): LuaValue {
|
|
|
16
16
|
const block = parseBlock(s, sf.astCtx || {});
|
|
17
17
|
const globalEnvMaybe = sf.threadLocal.get("_GLOBAL");
|
|
18
18
|
|
|
19
|
-
// Be vocal when no _GLOBAL is set
|
|
20
19
|
if (!globalEnvMaybe) {
|
|
21
20
|
console.warn("load() called without _GLOBAL in thread-local environment");
|
|
22
21
|
return new LuaMultiRes([null, "Global environment not set"]);
|
|
@@ -18,7 +18,6 @@ function untagNumber(x: any): number {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export const mathApi = new LuaTable({
|
|
21
|
-
// math constants
|
|
22
21
|
huge: 1 / 0,
|
|
23
22
|
pi: Math.PI,
|
|
24
23
|
|
|
@@ -145,7 +144,6 @@ export const mathApi = new LuaTable({
|
|
|
145
144
|
],
|
|
146
145
|
}),
|
|
147
146
|
|
|
148
|
-
// Basic functions
|
|
149
147
|
abs: new LuaBuiltinFunction({
|
|
150
148
|
callback: (_sf, x: number) => Math.abs(untagNumber(x)),
|
|
151
149
|
description: "Returns the absolute value of `x`.",
|
|
@@ -177,7 +175,6 @@ export const mathApi = new LuaTable({
|
|
|
177
175
|
returns: [{ type: "number" }],
|
|
178
176
|
}),
|
|
179
177
|
|
|
180
|
-
// Rounding and remainder
|
|
181
178
|
fmod: new LuaBuiltinFunction({
|
|
182
179
|
callback: (_sf, x: number, y: number) => untagNumber(x) % untagNumber(y),
|
|
183
180
|
description:
|
|
@@ -248,7 +245,6 @@ export const mathApi = new LuaTable({
|
|
|
248
245
|
returns: [{ type: "number" }],
|
|
249
246
|
}),
|
|
250
247
|
|
|
251
|
-
// Power and logarithms
|
|
252
248
|
exp: new LuaBuiltinFunction({
|
|
253
249
|
callback: (_sf, x: number) => Math.exp(untagNumber(x)),
|
|
254
250
|
description: "Returns `e` raised to `x`.",
|
|
@@ -289,7 +285,6 @@ export const mathApi = new LuaTable({
|
|
|
289
285
|
returns: [{ type: "number" }],
|
|
290
286
|
}),
|
|
291
287
|
|
|
292
|
-
// Trigonometric functions
|
|
293
288
|
cos: new LuaBuiltinFunction({
|
|
294
289
|
callback: (_sf, x: number) => Math.cos(untagNumber(x)),
|
|
295
290
|
description: "Returns the cosine of `x` radians.",
|
|
@@ -359,7 +354,6 @@ export const mathApi = new LuaTable({
|
|
|
359
354
|
deprecated: "Retained for compatibility with older Lua versions.",
|
|
360
355
|
}),
|
|
361
356
|
|
|
362
|
-
// Additional utility
|
|
363
357
|
deg: new LuaBuiltinFunction({
|
|
364
358
|
callback: (_sf, x: number) => (untagNumber(x) * 180) / Math.PI,
|
|
365
359
|
description: "Converts an angle from radians to degrees.",
|
|
@@ -386,7 +380,6 @@ export const mathApi = new LuaTable({
|
|
|
386
380
|
examples: [{ code: "print(math.ult(2, 3)) -- true" }],
|
|
387
381
|
}),
|
|
388
382
|
|
|
389
|
-
// Keep the cosineSimilarity utility function
|
|
390
383
|
cosineSimilarity: new LuaBuiltinFunction({
|
|
391
384
|
callback: (sf, vecA: LuaTable | number[], vecB: LuaTable | number[]) => {
|
|
392
385
|
if (vecA instanceof LuaTable) {
|
|
@@ -12,7 +12,6 @@ export const netApi = new LuaTable({
|
|
|
12
12
|
url: string,
|
|
13
13
|
options: ProxyFetchRequest = {},
|
|
14
14
|
): Promise<ProxyFetchResponse> => {
|
|
15
|
-
// JSONify any non-serializable body
|
|
16
15
|
if (
|
|
17
16
|
options?.body &&
|
|
18
17
|
typeof options.body !== "string" &&
|
|
@@ -40,7 +39,6 @@ export const netApi = new LuaTable({
|
|
|
40
39
|
body: await resp.text(),
|
|
41
40
|
};
|
|
42
41
|
}
|
|
43
|
-
// Do sensible things with the body based on the content type
|
|
44
42
|
// Read as ArrayBuffer first to safely handle empty responses (e.g.
|
|
45
43
|
// PUT/DELETE returning 204 with Content-Type: application/json).
|
|
46
44
|
// resp.arrayBuffer() never throws on an empty body, whereas
|
|
@@ -138,10 +136,8 @@ export const netApi = new LuaTable({
|
|
|
138
136
|
}),
|
|
139
137
|
});
|
|
140
138
|
|
|
141
|
-
// Utility functions
|
|
142
139
|
function buildProxyUrl(client: Client, url: string) {
|
|
143
140
|
url = url.replace(/^https?:\/\//, "");
|
|
144
|
-
// Strip off the /.fs and replace with /.proxy
|
|
145
141
|
return (
|
|
146
142
|
client.httpSpacePrimitives.url.slice(0, -fsEndpoint.length) +
|
|
147
143
|
"/.proxy/" +
|