@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,10 +1,23 @@
|
|
|
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";
|
|
5
6
|
import { isNegativeZero, isTaggedFloat } from "./numeric.ts";
|
|
6
7
|
import { isSqlNull } from "./sliq_null.ts";
|
|
7
8
|
import { luaFormat } from "./stdlib/format.ts";
|
|
9
|
+
import type {
|
|
10
|
+
LuaFunctionDocumentation,
|
|
11
|
+
LuaFunctionInfo,
|
|
12
|
+
} from "../../plug-api/types/index.ts";
|
|
13
|
+
|
|
14
|
+
let boundaryBudgetFactory: (() => LuaBudget) | undefined;
|
|
15
|
+
|
|
16
|
+
export function setBoundaryBudgetFactory(
|
|
17
|
+
f: (() => LuaBudget) | undefined,
|
|
18
|
+
): void {
|
|
19
|
+
boundaryBudgetFactory = f;
|
|
20
|
+
}
|
|
8
21
|
|
|
9
22
|
export type LuaType =
|
|
10
23
|
| "nil"
|
|
@@ -16,14 +29,22 @@ export type LuaType =
|
|
|
16
29
|
| "userdata"
|
|
17
30
|
| "thread";
|
|
18
31
|
|
|
19
|
-
// These types are for documentation only
|
|
20
32
|
export type LuaValue = any;
|
|
21
33
|
export type JSValue = any;
|
|
22
34
|
|
|
35
|
+
type LuaFunctionDefinitionMetadata = LuaFunctionDocumentation &
|
|
36
|
+
Partial<Pick<LuaFunctionInfo, "kind" | "name" | "source">>;
|
|
37
|
+
|
|
38
|
+
export type LuaFunctionDefinition<Callback> = {
|
|
39
|
+
callback: Callback;
|
|
40
|
+
} & LuaFunctionDefinitionMetadata;
|
|
41
|
+
|
|
23
42
|
export interface ILuaFunction {
|
|
24
43
|
call(sf: LuaStackFrame, ...args: LuaValue[]): Promise<LuaValue> | LuaValue;
|
|
25
44
|
|
|
26
45
|
asString(): string;
|
|
46
|
+
|
|
47
|
+
info?: LuaFunctionInfo;
|
|
27
48
|
}
|
|
28
49
|
|
|
29
50
|
export interface ILuaSettable {
|
|
@@ -40,7 +61,6 @@ export interface ILuaGettable {
|
|
|
40
61
|
getNumericType?(key: LuaValue): NumericType | undefined;
|
|
41
62
|
}
|
|
42
63
|
|
|
43
|
-
// Small helpers for type safety/readability
|
|
44
64
|
export function isILuaFunction(v: unknown): v is ILuaFunction {
|
|
45
65
|
return !!v && typeof (v as any).call === "function";
|
|
46
66
|
}
|
|
@@ -68,11 +88,11 @@ const EMPTY_CTX = {} as ASTCtx;
|
|
|
68
88
|
|
|
69
89
|
const MAX_TAG_LOOP = 200;
|
|
70
90
|
|
|
71
|
-
// Close-stack support
|
|
72
91
|
export type LuaCloseEntry = { value: LuaValue; ctx: ASTCtx };
|
|
73
92
|
|
|
74
93
|
type LuaThreadState = {
|
|
75
94
|
closeStack?: LuaCloseEntry[];
|
|
95
|
+
budget?: LuaBudget;
|
|
76
96
|
};
|
|
77
97
|
|
|
78
98
|
function isLuaNumber(v: any): boolean {
|
|
@@ -323,14 +343,10 @@ export class LuaEnv implements ILuaSettable, ILuaGettable {
|
|
|
323
343
|
}
|
|
324
344
|
|
|
325
345
|
get(name: string, _sf?: LuaStackFrame): Promise<LuaValue> | LuaValue | null {
|
|
326
|
-
// Fast path: single Map.get() instead of has() + get() for the common case
|
|
327
|
-
// where the variable exists and has a non-undefined value (null = Lua nil is fine).
|
|
328
346
|
const v = this.variables.get(name);
|
|
329
347
|
if (v !== undefined) {
|
|
330
348
|
return v;
|
|
331
349
|
}
|
|
332
|
-
// Variable set to null (Lua nil) returns null from Map.get() which is !== undefined,
|
|
333
|
-
// so it's handled above. Only fall through when the key is truly absent.
|
|
334
350
|
if (this.parent) {
|
|
335
351
|
return this.parent.get(name, _sf);
|
|
336
352
|
}
|
|
@@ -453,51 +469,53 @@ export function singleResult(value: any): any {
|
|
|
453
469
|
export class LuaFunction implements ILuaFunction {
|
|
454
470
|
private capturedEnv: LuaEnv;
|
|
455
471
|
funcHasGotos?: boolean;
|
|
472
|
+
info: LuaFunctionInfo;
|
|
456
473
|
|
|
457
474
|
constructor(
|
|
458
475
|
readonly body: LuaFunctionBody,
|
|
459
476
|
closure: LuaEnv,
|
|
477
|
+
info: Partial<LuaFunctionInfo> = {},
|
|
460
478
|
) {
|
|
461
479
|
this.capturedEnv = closure;
|
|
480
|
+
const documentation = body.documentation ?? {};
|
|
481
|
+
this.info = {
|
|
482
|
+
kind: "lua",
|
|
483
|
+
source: { ...body.ctx },
|
|
484
|
+
...documentation,
|
|
485
|
+
...info,
|
|
486
|
+
parameters:
|
|
487
|
+
info.parameters ??
|
|
488
|
+
documentation.parameters ??
|
|
489
|
+
body.parameters.map((name) => ({ name })),
|
|
490
|
+
};
|
|
462
491
|
}
|
|
463
492
|
|
|
464
493
|
call(sf: LuaStackFrame, ...args: LuaValue[]): Promise<LuaValue> | LuaValue {
|
|
465
|
-
// Create a new environment that chains to the captured environment
|
|
466
494
|
const env = new LuaEnv(this.capturedEnv);
|
|
467
|
-
// Set _CTX to the thread local environment from the stack frame
|
|
468
495
|
env.setLocal("_CTX", sf.threadLocal);
|
|
469
496
|
|
|
470
|
-
// Eval using a stack frame that knows the current function
|
|
471
497
|
const sfWithFn = sf.currentFunction === this ? sf : sf.withFunction(this);
|
|
472
498
|
|
|
473
499
|
const params = this.body.parameters;
|
|
474
500
|
const hasVarargs = params.length > 0 && params[params.length - 1] === "...";
|
|
475
501
|
|
|
476
|
-
// Resolve args (sync-first)
|
|
477
502
|
const argsRP = rpAll(args as any[]);
|
|
478
503
|
const resolveArgs = (resolvedArgs: any[]) => {
|
|
479
504
|
if (hasVarargs) {
|
|
480
|
-
// Variadic function: bind named params, collect rest into varargs
|
|
481
505
|
const varargStart = params.length - 1;
|
|
482
506
|
for (let i = 0; i < varargStart; i++) {
|
|
483
507
|
env.setLocal(params[i], resolvedArgs[i] ?? null);
|
|
484
508
|
}
|
|
485
|
-
env.setLocal(
|
|
486
|
-
"...",
|
|
487
|
-
new LuaMultiRes(resolvedArgs.slice(varargStart)),
|
|
488
|
-
);
|
|
509
|
+
env.setLocal("...", new LuaMultiRes(resolvedArgs.slice(varargStart)));
|
|
489
510
|
} else {
|
|
490
|
-
// Non-variadic: bind all named params directly
|
|
491
511
|
for (let i = 0; i < params.length; i++) {
|
|
492
512
|
env.setLocal(params[i], resolvedArgs[i] ?? null);
|
|
493
513
|
}
|
|
494
514
|
}
|
|
495
515
|
|
|
496
|
-
// Evaluate the function body with returnOnReturn set to true
|
|
497
516
|
const r = evalStatement(this.body.block, env, sfWithFn, true);
|
|
498
517
|
|
|
499
518
|
if (!isPromise(r)) {
|
|
500
|
-
// Fast path: synchronous result
|
|
501
519
|
if (r === undefined) return;
|
|
502
520
|
if (r && typeof r === "object" && r.ctrl === "return") {
|
|
503
521
|
return mapFunctionReturnValue(r.values);
|
|
@@ -565,7 +583,26 @@ function mapFunctionReturnValue(values: any[]): any {
|
|
|
565
583
|
}
|
|
566
584
|
|
|
567
585
|
export class LuaNativeJSFunction implements ILuaFunction {
|
|
568
|
-
|
|
586
|
+
readonly fn: (...args: JSValue[]) => JSValue;
|
|
587
|
+
public info: LuaFunctionInfo;
|
|
588
|
+
|
|
589
|
+
constructor(
|
|
590
|
+
definition:
|
|
591
|
+
| ((...args: JSValue[]) => JSValue)
|
|
592
|
+
| LuaFunctionDefinition<(...args: JSValue[]) => JSValue>,
|
|
593
|
+
) {
|
|
594
|
+
if (typeof definition === "function") {
|
|
595
|
+
this.fn = definition;
|
|
596
|
+
this.info = { kind: "builtin" };
|
|
597
|
+
} else {
|
|
598
|
+
const { callback, ...info } = definition;
|
|
599
|
+
this.fn = callback;
|
|
600
|
+
this.info = {
|
|
601
|
+
kind: "builtin",
|
|
602
|
+
...info,
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
}
|
|
569
606
|
|
|
570
607
|
// Performs automatic conversion between Lua and JS values for arguments, but not for return values
|
|
571
608
|
call(sf: LuaStackFrame, ...args: LuaValue[]): Promise<LuaValue> | LuaValue {
|
|
@@ -595,9 +632,28 @@ export class LuaNativeJSFunction implements ILuaFunction {
|
|
|
595
632
|
}
|
|
596
633
|
|
|
597
634
|
export class LuaBuiltinFunction implements ILuaFunction {
|
|
635
|
+
readonly fn: (sf: LuaStackFrame, ...args: LuaValue[]) => LuaValue;
|
|
636
|
+
public info: LuaFunctionInfo;
|
|
637
|
+
|
|
598
638
|
constructor(
|
|
599
|
-
|
|
600
|
-
|
|
639
|
+
definition:
|
|
640
|
+
| ((sf: LuaStackFrame, ...args: LuaValue[]) => LuaValue)
|
|
641
|
+
| LuaFunctionDefinition<
|
|
642
|
+
(sf: LuaStackFrame, ...args: LuaValue[]) => LuaValue
|
|
643
|
+
>,
|
|
644
|
+
) {
|
|
645
|
+
if (typeof definition === "function") {
|
|
646
|
+
this.fn = definition;
|
|
647
|
+
this.info = { kind: "builtin" };
|
|
648
|
+
} else {
|
|
649
|
+
const { callback, ...info } = definition;
|
|
650
|
+
this.fn = callback;
|
|
651
|
+
this.info = {
|
|
652
|
+
kind: "builtin",
|
|
653
|
+
...info,
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
}
|
|
601
657
|
|
|
602
658
|
call(sf: LuaStackFrame, ...args: LuaValue[]): Promise<LuaValue> | LuaValue {
|
|
603
659
|
// _CTX is already available via the stack frame
|
|
@@ -614,14 +670,10 @@ export class LuaBuiltinFunction implements ILuaFunction {
|
|
|
614
670
|
}
|
|
615
671
|
|
|
616
672
|
export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
617
|
-
// To optimize the table implementation we use a combination of different data structures
|
|
618
673
|
public metatable: LuaTable | null;
|
|
619
674
|
|
|
620
|
-
// When tables are used as maps, the common case is that they are string keys, so we use a simple object for that
|
|
621
675
|
private stringKeys: Record<string, any>;
|
|
622
|
-
// Other keys we can support using a Map as a fallback
|
|
623
676
|
private otherKeys: Map<any, any> | null;
|
|
624
|
-
// When tables are used as arrays, we use a native JavaScript array for that
|
|
625
677
|
private arrayPart: any[];
|
|
626
678
|
|
|
627
679
|
// Numeric type metadata at storage boundaries (lazily allocated)
|
|
@@ -658,15 +710,12 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
658
710
|
|
|
659
711
|
const numVal = LuaTable.numKeyValue(key);
|
|
660
712
|
if (numVal !== null) {
|
|
661
|
-
// Normalize -0 to +0
|
|
662
713
|
if (isNegativeZero(numVal)) {
|
|
663
714
|
return 0;
|
|
664
715
|
}
|
|
665
|
-
// Integer-valued numbers normalize to integers
|
|
666
716
|
if (Number.isInteger(numVal)) {
|
|
667
717
|
return numVal;
|
|
668
718
|
}
|
|
669
|
-
// Non-integer floats
|
|
670
719
|
return numVal;
|
|
671
720
|
}
|
|
672
721
|
return key;
|
|
@@ -738,7 +787,6 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
738
787
|
return this.stringKeys[key] !== undefined;
|
|
739
788
|
}
|
|
740
789
|
|
|
741
|
-
// Fast path for plain integer keys (common in for loops)
|
|
742
790
|
if (typeof key === "number") {
|
|
743
791
|
if (key >= 1 && (key | 0) === key) {
|
|
744
792
|
if (this.arrayPart[key - 1] !== undefined) return true;
|
|
@@ -831,7 +879,6 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
831
879
|
return value.then((v) => this.rawSet(key, v, numType));
|
|
832
880
|
}
|
|
833
881
|
|
|
834
|
-
// Fast path: string keys (the dominant case)
|
|
835
882
|
if (typeof key === "string") {
|
|
836
883
|
if (value === null || value === undefined) {
|
|
837
884
|
delete this.stringKeys[key];
|
|
@@ -890,7 +937,6 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
890
937
|
this.promoteIntegerKeysFromHash();
|
|
891
938
|
}
|
|
892
939
|
|
|
893
|
-
// Trailing nil shrink
|
|
894
940
|
if (value === null || value === undefined) {
|
|
895
941
|
let n = this.arrayPart.length;
|
|
896
942
|
while (n > 0) {
|
|
@@ -910,7 +956,6 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
910
956
|
return;
|
|
911
957
|
}
|
|
912
958
|
|
|
913
|
-
// Sparse numeric key
|
|
914
959
|
if (!this.otherKeys) {
|
|
915
960
|
this.otherKeys = new Map();
|
|
916
961
|
}
|
|
@@ -972,12 +1017,10 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
972
1017
|
);
|
|
973
1018
|
}
|
|
974
1019
|
|
|
975
|
-
// Fast path: no metatable — skip has() check and metamethod machinery
|
|
976
1020
|
if (this.metatable === null) {
|
|
977
1021
|
return this.rawSet(key, value, numType);
|
|
978
1022
|
}
|
|
979
1023
|
|
|
980
|
-
// Key exists — rawSet directly, no metamethod needed
|
|
981
1024
|
if (this.has(key)) {
|
|
982
1025
|
return this.rawSet(key, value, numType);
|
|
983
1026
|
}
|
|
@@ -988,7 +1031,6 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
988
1031
|
return this.rawSet(key, value, numType);
|
|
989
1032
|
}
|
|
990
1033
|
|
|
991
|
-
// Slow path: __newindex metamethod chain (rare)
|
|
992
1034
|
const errSf = sf || LuaStackFrame.lostFrame;
|
|
993
1035
|
const ctx = sf?.astCtx ?? EMPTY_CTX;
|
|
994
1036
|
const k: LuaValue = key;
|
|
@@ -1017,12 +1059,10 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1017
1059
|
return t.rawSet(k, v, nt);
|
|
1018
1060
|
}
|
|
1019
1061
|
|
|
1020
|
-
// Function metamethod: call and stop
|
|
1021
1062
|
if (typeof mm === "function" || isILuaFunction(mm)) {
|
|
1022
1063
|
return luaCall(mm, [t, k, v], ctx, errSf);
|
|
1023
1064
|
}
|
|
1024
1065
|
|
|
1025
|
-
// Table/env metamethod: forward assignment
|
|
1026
1066
|
if (mm instanceof LuaTable || mm instanceof LuaEnv) {
|
|
1027
1067
|
target = mm;
|
|
1028
1068
|
continue;
|
|
@@ -1052,7 +1092,6 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1052
1092
|
if (typeof key === "string") {
|
|
1053
1093
|
return this.stringKeyTypes ? this.stringKeyTypes.get(key) : undefined;
|
|
1054
1094
|
}
|
|
1055
|
-
// Fast path for plain integer keys
|
|
1056
1095
|
if (typeof key === "number") {
|
|
1057
1096
|
if (key >= 1 && (key | 0) === key) {
|
|
1058
1097
|
return this.arrayTypes ? this.arrayTypes[key - 1] : undefined;
|
|
@@ -1075,7 +1114,6 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1075
1114
|
return this.stringKeys[key];
|
|
1076
1115
|
}
|
|
1077
1116
|
|
|
1078
|
-
// Fast path for plain integer keys (common in for loops)
|
|
1079
1117
|
if (typeof key === "number") {
|
|
1080
1118
|
if (key >= 1 && (key | 0) === key) {
|
|
1081
1119
|
const v = this.arrayPart[key - 1];
|
|
@@ -1083,7 +1121,6 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1083
1121
|
if (this.otherKeys) return this.otherKeys.get(key);
|
|
1084
1122
|
return undefined;
|
|
1085
1123
|
}
|
|
1086
|
-
// Non-integer or zero/negative number keys
|
|
1087
1124
|
if (this.otherKeys) return this.otherKeys.get(key);
|
|
1088
1125
|
return undefined;
|
|
1089
1126
|
}
|
|
@@ -1117,7 +1154,6 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1117
1154
|
}
|
|
1118
1155
|
|
|
1119
1156
|
get(key: LuaValue, sf?: LuaStackFrame): LuaValue | Promise<LuaValue> | null {
|
|
1120
|
-
// Fast path: no metatable means rawGet is the final answer (most tables in SilverBullet)
|
|
1121
1157
|
if (this.metatable === null) {
|
|
1122
1158
|
const raw = this.rawGet(key);
|
|
1123
1159
|
return raw !== undefined ? raw : null;
|
|
@@ -1151,11 +1187,9 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|
|
1151
1187
|
result[k] = luaValueToJS(this.stringKeys[k], sf);
|
|
1152
1188
|
}
|
|
1153
1189
|
}
|
|
1154
|
-
// Include array part with 1-based keys
|
|
1155
1190
|
for (let i = 0; i < this.arrayPart.length; i++) {
|
|
1156
1191
|
result[i + 1] = luaValueToJS(this.arrayPart[i], sf);
|
|
1157
1192
|
}
|
|
1158
|
-
// Include other keys
|
|
1159
1193
|
if (this.otherKeys) {
|
|
1160
1194
|
for (const [key, val] of this.otherKeys) {
|
|
1161
1195
|
result[key] = luaValueToJS(val, sf);
|
|
@@ -1238,14 +1272,12 @@ export function luaIndexValue(
|
|
|
1238
1272
|
let t: LuaValue = value;
|
|
1239
1273
|
|
|
1240
1274
|
for (let loop = 0; loop < MAX_TAG_LOOP; loop++) {
|
|
1241
|
-
// Primitive get when table
|
|
1242
1275
|
if (t instanceof LuaTable) {
|
|
1243
1276
|
const raw = t.rawGet(key);
|
|
1244
1277
|
if (raw !== undefined) {
|
|
1245
1278
|
if (isSqlNull(raw)) return null;
|
|
1246
1279
|
return raw;
|
|
1247
1280
|
}
|
|
1248
|
-
// If no metatable, raw miss => nil
|
|
1249
1281
|
if (t.metatable === null) {
|
|
1250
1282
|
return null;
|
|
1251
1283
|
}
|
|
@@ -1267,18 +1299,15 @@ export function luaIndexValue(
|
|
|
1267
1299
|
);
|
|
1268
1300
|
}
|
|
1269
1301
|
|
|
1270
|
-
// Function metamethod
|
|
1271
1302
|
if (typeof mm === "function" || isILuaFunction(mm)) {
|
|
1272
1303
|
return luaCall(mm, [t, key], ctx, errSf);
|
|
1273
1304
|
}
|
|
1274
1305
|
|
|
1275
|
-
// Table/metatable delegation: repeat with mm as new "t"
|
|
1276
1306
|
if (mm instanceof LuaTable || mm instanceof LuaEnv) {
|
|
1277
1307
|
t = mm;
|
|
1278
1308
|
continue;
|
|
1279
1309
|
}
|
|
1280
1310
|
|
|
1281
|
-
// Bad metamethod type: make it a Lua-like type error
|
|
1282
1311
|
const ty = luaTypeOf(mm) as any as string;
|
|
1283
1312
|
throw new LuaRuntimeError(
|
|
1284
1313
|
`attempt to index a ${ty} value`,
|
|
@@ -1313,11 +1342,9 @@ export function luaSet(
|
|
|
1313
1342
|
const k = toNumKey(normKey);
|
|
1314
1343
|
const jsVal = luaValueToJS(value, sf);
|
|
1315
1344
|
if (isPromise(jsVal)) {
|
|
1316
|
-
return (jsVal as Promise<any>).then(
|
|
1317
|
-
(
|
|
1318
|
-
|
|
1319
|
-
},
|
|
1320
|
-
);
|
|
1345
|
+
return (jsVal as Promise<any>).then((v) => {
|
|
1346
|
+
(obj as Record<string | number, any>)[k] = v;
|
|
1347
|
+
});
|
|
1321
1348
|
}
|
|
1322
1349
|
(obj as Record<string | number, any>)[k] = jsVal;
|
|
1323
1350
|
}
|
|
@@ -1354,11 +1381,9 @@ export function luaGet(
|
|
|
1354
1381
|
if (isTaggedFloat(key)) {
|
|
1355
1382
|
return (obj as any[])[key.value - 1] ?? null;
|
|
1356
1383
|
}
|
|
1357
|
-
// Native JS object
|
|
1358
1384
|
const k = toNumKey(key);
|
|
1359
1385
|
const val = (obj as Record<string | number, any>)[k];
|
|
1360
1386
|
if (typeof val === "function") {
|
|
1361
|
-
// Automatically bind the function to the object
|
|
1362
1387
|
return val.bind(obj);
|
|
1363
1388
|
}
|
|
1364
1389
|
if (val === undefined) {
|
|
@@ -1379,7 +1404,6 @@ export function luaLen(
|
|
|
1379
1404
|
return obj.length;
|
|
1380
1405
|
}
|
|
1381
1406
|
if (obj instanceof LuaTable) {
|
|
1382
|
-
// Check __len metamethod unless raw access is requested
|
|
1383
1407
|
if (!raw) {
|
|
1384
1408
|
const mt = getMetatable(obj, sf || LuaStackFrame.lostFrame);
|
|
1385
1409
|
const mm = mt ? mt.rawGet("__len") : null;
|
|
@@ -1414,7 +1438,6 @@ export function luaCall(
|
|
|
1414
1438
|
);
|
|
1415
1439
|
}
|
|
1416
1440
|
|
|
1417
|
-
// Fast path: native JS function
|
|
1418
1441
|
if (typeof callee === "function") {
|
|
1419
1442
|
const baseSf = sf || LuaStackFrame.lostFrame;
|
|
1420
1443
|
const len = args.length;
|
|
@@ -1435,7 +1458,6 @@ export function luaCall(
|
|
|
1435
1458
|
return (callee as (...a: any[]) => any)(...args);
|
|
1436
1459
|
}
|
|
1437
1460
|
|
|
1438
|
-
// Lua table: may be callable via __call metamethod
|
|
1439
1461
|
if (callee instanceof LuaTable) {
|
|
1440
1462
|
const metatable = getMetatable(callee, sf);
|
|
1441
1463
|
|
|
@@ -1466,7 +1488,6 @@ export function luaCall(
|
|
|
1466
1488
|
}
|
|
1467
1489
|
}
|
|
1468
1490
|
|
|
1469
|
-
// ILuaFunction (LuaFunction/LuaBuiltinFunction/LuaNativeJSFunction/etc.)
|
|
1470
1491
|
if (isILuaFunction(callee)) {
|
|
1471
1492
|
// Fast path for builtins: skip withFunction, defer withCtx
|
|
1472
1493
|
if (callee instanceof LuaBuiltinFunction) {
|
|
@@ -1491,17 +1512,18 @@ export function luaCall(
|
|
|
1491
1512
|
}
|
|
1492
1513
|
|
|
1493
1514
|
export function luaEquals(a: any, b: any): boolean {
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
const bn =
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1515
|
+
const an =
|
|
1516
|
+
a === null || a === undefined || isSqlNull(a)
|
|
1517
|
+
? null
|
|
1518
|
+
: isTaggedFloat(a)
|
|
1519
|
+
? a.value
|
|
1520
|
+
: a;
|
|
1521
|
+
const bn =
|
|
1522
|
+
b === null || b === undefined || isSqlNull(b)
|
|
1523
|
+
? null
|
|
1524
|
+
: isTaggedFloat(b)
|
|
1525
|
+
? b.value
|
|
1526
|
+
: b;
|
|
1505
1527
|
return an === bn;
|
|
1506
1528
|
}
|
|
1507
1529
|
|
|
@@ -1569,7 +1591,6 @@ export class LuaRuntimeError extends Error {
|
|
|
1569
1591
|
if (!ctx || !ctx.from || !ctx.to) {
|
|
1570
1592
|
break;
|
|
1571
1593
|
}
|
|
1572
|
-
// Find the line and column
|
|
1573
1594
|
let line = 1;
|
|
1574
1595
|
let column = 0;
|
|
1575
1596
|
let lastNewline = -1;
|
|
@@ -1583,7 +1604,6 @@ export class LuaRuntimeError extends Error {
|
|
|
1583
1604
|
}
|
|
1584
1605
|
}
|
|
1585
1606
|
|
|
1586
|
-
// Get the full line of code for context
|
|
1587
1607
|
const lineStart = lastNewline + 1;
|
|
1588
1608
|
const lineEnd = code.indexOf("\n", ctx.from);
|
|
1589
1609
|
const codeLine = code.substring(
|
|
@@ -1591,7 +1611,6 @@ export class LuaRuntimeError extends Error {
|
|
|
1591
1611
|
lineEnd === -1 ? undefined : lineEnd,
|
|
1592
1612
|
);
|
|
1593
1613
|
|
|
1594
|
-
// Add position indicator
|
|
1595
1614
|
const pointer = `${" ".repeat(column)}^`;
|
|
1596
1615
|
|
|
1597
1616
|
traceStr +=
|
|
@@ -1643,12 +1662,10 @@ export function luaToString(
|
|
|
1643
1662
|
return luaFormatNumber(value);
|
|
1644
1663
|
}
|
|
1645
1664
|
|
|
1646
|
-
// Check for circular references
|
|
1647
1665
|
if (typeof value === "object" && visited.has(value)) {
|
|
1648
1666
|
return "<circular reference>";
|
|
1649
1667
|
}
|
|
1650
1668
|
if ((value as any).toStringAsync) {
|
|
1651
|
-
// Add to visited before recursing
|
|
1652
1669
|
visited.add(value);
|
|
1653
1670
|
return (value as any).toStringAsync();
|
|
1654
1671
|
}
|
|
@@ -1660,15 +1677,12 @@ export function luaToString(
|
|
|
1660
1677
|
// Don't recurse into the function body, just show the function signature
|
|
1661
1678
|
return `<lua-function (${value.body.parameters.join(", ")})>`;
|
|
1662
1679
|
}
|
|
1663
|
-
// Handle plain JavaScript objects in a Lua-like format
|
|
1664
1680
|
if (typeof value === "object") {
|
|
1665
|
-
// Add to visited before recursing
|
|
1666
1681
|
visited.add(value);
|
|
1667
1682
|
return (async () => {
|
|
1668
1683
|
let result = "{";
|
|
1669
1684
|
let first = true;
|
|
1670
1685
|
|
|
1671
|
-
// Handle arrays
|
|
1672
1686
|
if (Array.isArray(value)) {
|
|
1673
1687
|
for (const val of value) {
|
|
1674
1688
|
if (first) {
|
|
@@ -1676,14 +1690,12 @@ export function luaToString(
|
|
|
1676
1690
|
} else {
|
|
1677
1691
|
result += ", ";
|
|
1678
1692
|
}
|
|
1679
|
-
// Recursively stringify the value, passing the visited set
|
|
1680
1693
|
const strVal = await luaToString(val, visited);
|
|
1681
1694
|
result += strVal;
|
|
1682
1695
|
}
|
|
1683
1696
|
return `${result}}`;
|
|
1684
1697
|
}
|
|
1685
1698
|
|
|
1686
|
-
// Handle objects
|
|
1687
1699
|
for (const [key, val] of Object.entries(value)) {
|
|
1688
1700
|
if (first) {
|
|
1689
1701
|
first = false;
|
|
@@ -1695,7 +1707,6 @@ export function luaToString(
|
|
|
1695
1707
|
} else {
|
|
1696
1708
|
result += `["${key}"] = `;
|
|
1697
1709
|
}
|
|
1698
|
-
// Recursively stringify the value, passing the visited set
|
|
1699
1710
|
const strVal = await luaToString(val, visited);
|
|
1700
1711
|
result += strVal;
|
|
1701
1712
|
}
|
|
@@ -1716,7 +1727,6 @@ export function luaFormatNumber(n: number, kind?: "int" | "float"): string {
|
|
|
1716
1727
|
if (n === 0) {
|
|
1717
1728
|
return 1 / n === -Infinity ? "-0.0" : "0.0";
|
|
1718
1729
|
}
|
|
1719
|
-
// Delegate to luaFormat for `%.14g`
|
|
1720
1730
|
const s = luaFormat("%.14g", n);
|
|
1721
1731
|
// Guarantee `.01 suffix for integer-valued floats
|
|
1722
1732
|
if (s.indexOf(".") === -1 && s.indexOf("e") === -1) {
|
|
@@ -1773,7 +1783,6 @@ export function jsToLuaValue(value: any): any {
|
|
|
1773
1783
|
return value;
|
|
1774
1784
|
}
|
|
1775
1785
|
if (Array.isArray(value) && "index" in value && "input" in value) {
|
|
1776
|
-
// This is a RegExpMatchArray
|
|
1777
1786
|
const regexMatch = value as RegExpMatchArray;
|
|
1778
1787
|
const regexMatchTable = new LuaTable();
|
|
1779
1788
|
for (let i = 0; i < regexMatch.length; i++) {
|
|
@@ -1804,7 +1813,6 @@ export function jsToLuaValue(value: any): any {
|
|
|
1804
1813
|
return value;
|
|
1805
1814
|
}
|
|
1806
1815
|
|
|
1807
|
-
// Inverse of jsToLuaValue
|
|
1808
1816
|
export function luaValueToJS(value: any, sf: LuaStackFrame): any {
|
|
1809
1817
|
if (isPromise(value)) {
|
|
1810
1818
|
return (value as Promise<any>).then((v) => luaValueToJS(v, sf));
|
|
@@ -1818,14 +1826,47 @@ export function luaValueToJS(value: any, sf: LuaStackFrame): any {
|
|
|
1818
1826
|
value instanceof LuaBuiltinFunction
|
|
1819
1827
|
) {
|
|
1820
1828
|
return (...args: any[]) => {
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
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;
|
|
1827
1869
|
}
|
|
1828
|
-
return luaValueToJS((value as ILuaFunction).call(sf, ...jsArgs), sf);
|
|
1829
1870
|
};
|
|
1830
1871
|
}
|
|
1831
1872
|
if (isTaggedFloat(value)) {
|
|
@@ -2,9 +2,14 @@ import { LuaBuiltinFunction, LuaTable } from "../runtime.ts";
|
|
|
2
2
|
import { hashSHA256 } from "../../../plug-api/lib/crypto.ts";
|
|
3
3
|
|
|
4
4
|
export const cryptoApi = new LuaTable({
|
|
5
|
-
sha256: new LuaBuiltinFunction(
|
|
6
|
-
(_sf, s: string | Uint8Array): Promise<string> => {
|
|
5
|
+
sha256: new LuaBuiltinFunction({
|
|
6
|
+
callback: (_sf, s: string | Uint8Array): Promise<string> => {
|
|
7
7
|
return hashSHA256(s);
|
|
8
8
|
},
|
|
9
|
-
|
|
9
|
+
description: "Computes the SHA-256 digest of a string or byte buffer.",
|
|
10
|
+
parameters: [
|
|
11
|
+
{ name: "data", type: "string|bytes", description: "Data to hash." },
|
|
12
|
+
],
|
|
13
|
+
returns: [{ type: "string", description: "Hexadecimal SHA-256 digest." }],
|
|
14
|
+
}),
|
|
10
15
|
});
|
|
@@ -2,18 +2,36 @@ import { LuaBuiltinFunction, LuaTable } from "../runtime.ts";
|
|
|
2
2
|
import { base64Decode, base64Encode } from "../../../plug-api/lib/crypto.ts";
|
|
3
3
|
|
|
4
4
|
export const encodingApi = new LuaTable({
|
|
5
|
-
base64Encode: new LuaBuiltinFunction(
|
|
6
|
-
(_sf, s: string | Uint8Array): string => {
|
|
5
|
+
base64Encode: new LuaBuiltinFunction({
|
|
6
|
+
callback: (_sf, s: string | Uint8Array): string => {
|
|
7
7
|
return base64Encode(s);
|
|
8
8
|
},
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
description: "Encodes a string or byte buffer as Base64.",
|
|
10
|
+
parameters: [{ name: "data", type: "string|bytes" }],
|
|
11
|
+
returns: [{ type: "string", description: "Base64-encoded data." }],
|
|
12
12
|
}),
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
base64Decode: new LuaBuiltinFunction({
|
|
14
|
+
callback: (_sf, s: string): Uint8Array => {
|
|
15
|
+
return base64Decode(s);
|
|
16
|
+
},
|
|
17
|
+
description: "Decodes a Base64 string into a byte buffer.",
|
|
18
|
+
parameters: [{ name: "encoded", type: "string" }],
|
|
19
|
+
returns: [{ type: "bytes", description: "Decoded bytes." }],
|
|
20
|
+
}),
|
|
21
|
+
utf8Encode: new LuaBuiltinFunction({
|
|
22
|
+
callback: (_sf, s: string): Uint8Array => {
|
|
23
|
+
return new TextEncoder().encode(s);
|
|
24
|
+
},
|
|
25
|
+
description: "Encodes a UTF-8 string into a byte buffer.",
|
|
26
|
+
parameters: [{ name: "value", type: "string" }],
|
|
27
|
+
returns: [{ type: "bytes", description: "UTF-8 encoded bytes." }],
|
|
15
28
|
}),
|
|
16
|
-
utf8Decode: new LuaBuiltinFunction(
|
|
17
|
-
|
|
29
|
+
utf8Decode: new LuaBuiltinFunction({
|
|
30
|
+
callback: (_sf, data: Uint8Array): string => {
|
|
31
|
+
return new TextDecoder().decode(data);
|
|
32
|
+
},
|
|
33
|
+
description: "Decodes a UTF-8 byte buffer into a string.",
|
|
34
|
+
parameters: [{ name: "data", type: "bytes" }],
|
|
35
|
+
returns: [{ type: "string", description: "Decoded text." }],
|
|
18
36
|
}),
|
|
19
37
|
});
|