@silverbulletmd/silverbullet 2.10.0 → 2.12.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 +6 -6
- package/client/asset_bundle/bundle.ts +0 -1
- package/client/config.ts +343 -0
- package/client/data/kv_primitives.ts +2 -2
- package/client/markdown_parser/constants.ts +5 -1
- package/client/plugos/hooks/command.ts +8 -6
- package/client/plugos/hooks/event.ts +60 -24
- package/client/plugos/hooks/mq.ts +0 -3
- package/client/plugos/hooks/slash_command.ts +3 -20
- package/client/plugos/hooks/syscall.ts +0 -4
- package/client/plugos/manifest_cache.ts +2 -34
- package/client/plugos/plug.ts +0 -3
- package/client/plugos/plug_compile.ts +3 -13
- package/client/plugos/sandboxes/sandbox.ts +2 -2
- package/client/plugos/sandboxes/worker_sandbox.ts +19 -9
- package/client/plugos/syscalls/capture.ts +104 -0
- package/client/plugos/syscalls/editor.ts +197 -56
- package/client/plugos/syscalls/event.ts +12 -9
- 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 +138 -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 +70 -4
- package/client/plugos/syscalls/system.ts +39 -0
- package/client/plugos/system.ts +9 -7
- package/client/plugos/types.ts +6 -6
- package/client/plugos/worker_runtime.ts +0 -2
- package/client/space_lua/aggregates.ts +6 -22
- 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 +2 -7
- package/client/space_lua/parse.ts +0 -17
- package/client/space_lua/quarantine.ts +127 -0
- package/client/space_lua/query_collection.ts +18 -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 +18 -1
- package/client/space_lua/runtime.ts +64 -77
- 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 +18 -23
- package/client/space_lua/stdlib/space_lua.ts +1 -2
- package/client/space_lua/stdlib/string_pack.ts +4 -7
- 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 +3 -4
- package/package.json +52 -41
- 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 +1 -9
- 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 +5 -44
- package/plug-api/syscall.ts +1 -5
- package/plug-api/syscalls/config.ts +2 -2
- package/plug-api/syscalls/editor.ts +75 -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 +31 -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 +20 -4
- package/plug-api/types/datastore.ts +11 -2
- package/plug-api/types/index.ts +10 -0
- package/plug-api/types/manifest.ts +7 -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 +285 -0
- package/plug-api/ui/tree_types.ts +67 -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
|
@@ -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/" +
|
|
@@ -147,7 +147,6 @@ function buildSpecMap(d: Date, utc: boolean): Record<string, () => string> {
|
|
|
147
147
|
const dow = () => wd(d, utc);
|
|
148
148
|
|
|
149
149
|
return {
|
|
150
|
-
// Date
|
|
151
150
|
Y: () => yr(d, utc).toString(),
|
|
152
151
|
y: () => pad2(yr(d, utc) % 100),
|
|
153
152
|
C: () => pad2(Math.floor(yr(d, utc) / 100)),
|
|
@@ -156,14 +155,12 @@ function buildSpecMap(d: Date, utc: boolean): Record<string, () => string> {
|
|
|
156
155
|
e: () => da(d, utc).toString().padStart(2, " "),
|
|
157
156
|
j: () => pad3(dayOfYear(d, utc)),
|
|
158
157
|
|
|
159
|
-
// Time
|
|
160
158
|
H: () => pad2(h()),
|
|
161
159
|
I: () => pad2(h12()),
|
|
162
160
|
M: () => pad2(mi(d, utc)),
|
|
163
161
|
S: () => pad2(sc(d, utc)),
|
|
164
162
|
p: () => (h() >= 12 ? "PM" : "AM"),
|
|
165
163
|
|
|
166
|
-
// Weekday
|
|
167
164
|
A: () =>
|
|
168
165
|
d.toLocaleString("en-US", {
|
|
169
166
|
weekday: "long",
|
|
@@ -177,7 +174,6 @@ function buildSpecMap(d: Date, utc: boolean): Record<string, () => string> {
|
|
|
177
174
|
w: () => dow().toString(),
|
|
178
175
|
u: () => (dow() === 0 ? 7 : dow()).toString(),
|
|
179
176
|
|
|
180
|
-
// Month name
|
|
181
177
|
b: () =>
|
|
182
178
|
d.toLocaleString("en-US", {
|
|
183
179
|
month: "short",
|
|
@@ -194,14 +190,12 @@ function buildSpecMap(d: Date, utc: boolean): Record<string, () => string> {
|
|
|
194
190
|
...(utc ? { timeZone: "UTC" } : {}),
|
|
195
191
|
}),
|
|
196
192
|
|
|
197
|
-
// Week number
|
|
198
193
|
U: () => pad2(weekNumber(d, utc, 0, false)),
|
|
199
194
|
W: () => pad2(weekNumber(d, utc, 1, false)),
|
|
200
195
|
V: () => pad2(weekNumber(d, utc, 1, true)),
|
|
201
196
|
G: () => isoWeekYear(d, utc).toString(),
|
|
202
197
|
g: () => pad2(isoWeekYear(d, utc) % 100),
|
|
203
198
|
|
|
204
|
-
// Composite specifiers
|
|
205
199
|
c: () =>
|
|
206
200
|
d.toLocaleString("en-US", {
|
|
207
201
|
...(utc ? { timeZone: "UTC" } : {}),
|
|
@@ -225,14 +219,11 @@ function buildSpecMap(d: Date, utc: boolean): Record<string, () => string> {
|
|
|
225
219
|
h() >= 12 ? "PM" : "AM"
|
|
226
220
|
}`,
|
|
227
221
|
|
|
228
|
-
// Epoch
|
|
229
222
|
s: () => Math.floor(d.getTime() / 1000).toString(),
|
|
230
223
|
|
|
231
|
-
// Whitespace
|
|
232
224
|
n: () => "\n",
|
|
233
225
|
t: () => "\t",
|
|
234
226
|
|
|
235
|
-
// Timezone
|
|
236
227
|
Z: () => {
|
|
237
228
|
if (utc) return "UTC";
|
|
238
229
|
const match = d.toTimeString().match(/\((.*)\)/);
|
|
@@ -246,7 +237,6 @@ function buildSpecMap(d: Date, utc: boolean): Record<string, () => string> {
|
|
|
246
237
|
return `${sign}${pad2(Math.floor(abs / 60))}${pad2(abs % 60)}`;
|
|
247
238
|
},
|
|
248
239
|
|
|
249
|
-
// Literal
|
|
250
240
|
"%": () => "%",
|
|
251
241
|
};
|
|
252
242
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// Lua Optimized Pattern Matching Engine Implementation
|
|
2
|
-
|
|
3
1
|
const CH_ESC = 37; // '%'
|
|
4
2
|
const CH_OPEN = 40; // '('
|
|
5
3
|
const CH_CLOSE = 41; // ')'
|
|
@@ -34,27 +32,27 @@ const MAX_MATCH_DEPTH = 200;
|
|
|
34
32
|
const CAP_UNFINISHED = -1;
|
|
35
33
|
const CAP_POSITION = -2;
|
|
36
34
|
|
|
37
|
-
|
|
35
|
+
type Capture = {
|
|
38
36
|
init: number;
|
|
39
37
|
len: number;
|
|
40
|
-
}
|
|
38
|
+
};
|
|
41
39
|
|
|
42
|
-
export
|
|
43
|
-
src: string;
|
|
44
|
-
s:
|
|
40
|
+
export type MatchState = {
|
|
41
|
+
src: string;
|
|
42
|
+
s: Uint16Array;
|
|
45
43
|
slen: number;
|
|
46
|
-
p:
|
|
44
|
+
p: Uint16Array;
|
|
47
45
|
plen: number;
|
|
48
46
|
level: number;
|
|
49
47
|
capture: Capture[]; // pre-allocated, length `MAX_CAPTURES`
|
|
50
48
|
matchdepth: number;
|
|
51
|
-
}
|
|
49
|
+
};
|
|
52
50
|
|
|
53
|
-
function
|
|
51
|
+
function toCodeUnits(s: string): Uint16Array {
|
|
54
52
|
const len = s.length;
|
|
55
|
-
const arr = new
|
|
53
|
+
const arr = new Uint16Array(len);
|
|
56
54
|
for (let i = 0; i < len; i++) {
|
|
57
|
-
arr[i] = s.charCodeAt(i)
|
|
55
|
+
arr[i] = s.charCodeAt(i);
|
|
58
56
|
}
|
|
59
57
|
return arr;
|
|
60
58
|
}
|
|
@@ -108,7 +106,7 @@ function matchClass(c: number, cl: number): boolean {
|
|
|
108
106
|
return cl >= 97 && cl <= 122 ? res : !res;
|
|
109
107
|
}
|
|
110
108
|
|
|
111
|
-
function classEnd(p:
|
|
109
|
+
function classEnd(p: Uint16Array, plen: number, pi: number): number {
|
|
112
110
|
const ch = p[pi];
|
|
113
111
|
pi++;
|
|
114
112
|
if (ch === CH_ESC) {
|
|
@@ -138,7 +136,7 @@ function classEnd(p: Uint8Array, plen: number, pi: number): number {
|
|
|
138
136
|
|
|
139
137
|
function matchBracketClass(
|
|
140
138
|
c: number,
|
|
141
|
-
p:
|
|
139
|
+
p: Uint16Array,
|
|
142
140
|
pi: number,
|
|
143
141
|
ec: number,
|
|
144
142
|
): boolean {
|
|
@@ -404,8 +402,8 @@ function match(ms: MatchState, si: number, pi: number): number {
|
|
|
404
402
|
}
|
|
405
403
|
|
|
406
404
|
function createMatchState(s: string, p: string): MatchState {
|
|
407
|
-
const sb =
|
|
408
|
-
const pb =
|
|
405
|
+
const sb = toCodeUnits(s);
|
|
406
|
+
const pb = toCodeUnits(p);
|
|
409
407
|
const capture: Capture[] = new Array(MAX_CAPTURES);
|
|
410
408
|
for (let i = 0; i < MAX_CAPTURES; i++) {
|
|
411
409
|
capture[i] = { init: 0, len: 0 };
|
|
@@ -434,15 +432,13 @@ function noSpecials(p: string): boolean {
|
|
|
434
432
|
return true;
|
|
435
433
|
}
|
|
436
434
|
|
|
437
|
-
// Public API
|
|
438
|
-
|
|
439
435
|
export type CaptureResult = { s: string } | { position: number };
|
|
440
436
|
|
|
441
|
-
|
|
437
|
+
type RawCapture = {
|
|
442
438
|
kind: 0 | 1; // 0 = string slice, 1 = position
|
|
443
439
|
start: number;
|
|
444
440
|
len: number; // kind = 0: substring length; kind = 1: 1-based position
|
|
445
|
-
}
|
|
441
|
+
};
|
|
446
442
|
|
|
447
443
|
function getOneRawCapture(
|
|
448
444
|
ms: MatchState,
|
|
@@ -615,7 +611,6 @@ function expandReplacementString(
|
|
|
615
611
|
throw new Error("invalid use of '%' in replacement string");
|
|
616
612
|
}
|
|
617
613
|
} else {
|
|
618
|
-
// Collect consecutive literal characters
|
|
619
614
|
let j = i + 1;
|
|
620
615
|
while (j < repl.length && repl.charCodeAt(j) !== CH_ESC) j++;
|
|
621
616
|
parts.push(repl.substring(i, j));
|
|
@@ -627,13 +622,13 @@ function expandReplacementString(
|
|
|
627
622
|
return parts.join("");
|
|
628
623
|
}
|
|
629
624
|
|
|
630
|
-
export
|
|
625
|
+
export type GsubCallbacks = {
|
|
631
626
|
replString?: string;
|
|
632
627
|
replFunction?: (
|
|
633
628
|
...captures: CaptureResult[]
|
|
634
629
|
) => Promise<string | null | undefined> | string | null | undefined;
|
|
635
630
|
replTable?: (key: string) => string | null | undefined;
|
|
636
|
-
}
|
|
631
|
+
};
|
|
637
632
|
|
|
638
633
|
export async function patternGsub(
|
|
639
634
|
s: string,
|
|
@@ -180,7 +180,6 @@ export async function interpolateLuaString(
|
|
|
180
180
|
|
|
181
181
|
result += template.slice(currentIndex, startIndex);
|
|
182
182
|
|
|
183
|
-
// Find matching closing brace by counting nesting
|
|
184
183
|
let nestLevel = 1;
|
|
185
184
|
let endIndex = startIndex + 2;
|
|
186
185
|
while (nestLevel > 0 && endIndex < template.length) {
|
|
@@ -519,7 +518,7 @@ export const spaceluaApi = new LuaTable({
|
|
|
519
518
|
*/
|
|
520
519
|
baseUrl: new LuaBuiltinFunction({
|
|
521
520
|
callback: () => {
|
|
522
|
-
//
|
|
521
|
+
// The origin contract excludes a trailing slash.
|
|
523
522
|
return document.baseURI.replace(/\/*$/, "");
|
|
524
523
|
},
|
|
525
524
|
description:
|
|
@@ -28,16 +28,16 @@ type KOption =
|
|
|
28
28
|
| "paddalign"
|
|
29
29
|
| "nop";
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
type ParsedOption = {
|
|
32
32
|
opt: KOption;
|
|
33
33
|
size: number; // byte width
|
|
34
34
|
ntoalign: number; // padding bytes before this field
|
|
35
|
-
}
|
|
35
|
+
};
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
type Header = {
|
|
38
38
|
islittle: boolean;
|
|
39
39
|
maxalign: number;
|
|
40
|
-
}
|
|
40
|
+
};
|
|
41
41
|
|
|
42
42
|
function makeHeader(): Header {
|
|
43
43
|
return { islittle: NATIVE_LITTLE, maxalign: NATIVE_MAXALIGN };
|
|
@@ -145,7 +145,6 @@ function getOption(
|
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
// Compute alignment padding
|
|
149
148
|
function getDetails(
|
|
150
149
|
fmt: string,
|
|
151
150
|
pos: number,
|
|
@@ -264,7 +263,6 @@ export const strPackFn = new LuaBuiltinFunction({
|
|
|
264
263
|
let opt: ParsedOption;
|
|
265
264
|
[opt, pos] = getDetails(fmt, pos, h, totalsize);
|
|
266
265
|
|
|
267
|
-
// alignment padding
|
|
268
266
|
if (opt.ntoalign > 0) {
|
|
269
267
|
parts.push(new Uint8Array(opt.ntoalign));
|
|
270
268
|
totalsize += opt.ntoalign;
|
|
@@ -349,7 +347,6 @@ export const strPackFn = new LuaBuiltinFunction({
|
|
|
349
347
|
}
|
|
350
348
|
}
|
|
351
349
|
|
|
352
|
-
// Concatenate all parts into one binary string (latin-1 encoding)
|
|
353
350
|
let total = 0;
|
|
354
351
|
for (const p of parts) total += p.length;
|
|
355
352
|
const out = new Uint8Array(total);
|
|
@@ -274,12 +274,10 @@ export const tableApi = new LuaTable({
|
|
|
274
274
|
t: number,
|
|
275
275
|
a2?: LuaTable | any[],
|
|
276
276
|
) => {
|
|
277
|
-
// a2 defaults to a1
|
|
278
277
|
if (a2 === undefined || a2 === null) {
|
|
279
278
|
a2 = a1;
|
|
280
279
|
}
|
|
281
280
|
|
|
282
|
-
// Empty range: nothing to do, return destination
|
|
283
281
|
if (e < f) {
|
|
284
282
|
return a2;
|
|
285
283
|
}
|
|
@@ -422,7 +420,6 @@ export const tableApi = new LuaTable({
|
|
|
422
420
|
return false;
|
|
423
421
|
}
|
|
424
422
|
if (tbl instanceof LuaTable) {
|
|
425
|
-
// Iterate over the table
|
|
426
423
|
for (const key of tbl.keys()) {
|
|
427
424
|
if (luaEquals(tbl.get(key), value)) {
|
|
428
425
|
return true;
|
|
@@ -459,9 +456,7 @@ export const tableApi = new LuaTable({
|
|
|
459
456
|
tbl: LuaTable | Record<string, any>,
|
|
460
457
|
...keys: LuaValue[]
|
|
461
458
|
) => {
|
|
462
|
-
// Normalize arguments
|
|
463
459
|
if (Array.isArray(keys[0])) {
|
|
464
|
-
// First argument is key array, let's unpack
|
|
465
460
|
keys = keys[0];
|
|
466
461
|
} else if (keys[0] instanceof LuaTable) {
|
|
467
462
|
keys = keys[0].toJSArray();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { LuaBudgetStopped } from "./budget.ts";
|
|
1
2
|
import {
|
|
2
3
|
getMetatable,
|
|
3
4
|
type ILuaFunction,
|
|
@@ -101,7 +102,6 @@ const ipairsFunction = new LuaBuiltinFunction({
|
|
|
101
102
|
|
|
102
103
|
const pairsFunction = new LuaBuiltinFunction({
|
|
103
104
|
callback: (sf, t: LuaTable | any[] | Record<string, any>) => {
|
|
104
|
-
// Respect `__pairs` metamethod for Lua tables
|
|
105
105
|
if (isLuaTable(t)) {
|
|
106
106
|
const mt = (t as any).metatable as LuaTable | null | undefined;
|
|
107
107
|
if (mt) {
|
|
@@ -295,27 +295,24 @@ async function pcallBoundary(
|
|
|
295
295
|
const msg = errMsgOf(e);
|
|
296
296
|
try {
|
|
297
297
|
await luaCloseFromMark(sf, mark, msg);
|
|
298
|
-
return { ok: false, message: msg };
|
|
299
298
|
} catch (closeErr: any) {
|
|
300
|
-
|
|
299
|
+
if (!(e instanceof LuaBudgetStopped)) {
|
|
300
|
+
return { ok: false, message: errMsgOf(closeErr) };
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
// A user-initiated stop is not a recoverable Lua error.
|
|
304
|
+
if (e instanceof LuaBudgetStopped) {
|
|
305
|
+
throw e;
|
|
301
306
|
}
|
|
307
|
+
return { ok: false, message: msg };
|
|
302
308
|
}
|
|
303
309
|
}
|
|
304
310
|
|
|
305
311
|
const pcallFunction = new LuaBuiltinFunction({
|
|
306
312
|
callback: async (sf, fn: ILuaFunction, ...args) => {
|
|
307
|
-
//
|
|
308
|
-
//
|
|
309
|
-
//
|
|
310
|
-
// after that.
|
|
311
|
-
//
|
|
312
|
-
// The protected call boundary must be established *before*
|
|
313
|
-
// evaluating the function and its arguments. Otherwise, any
|
|
314
|
-
// `<close>` locals created while evaluating `pcall`'s arguments
|
|
315
|
-
// will be wrongly treated as "inside" the protected call, and
|
|
316
|
-
// `pcall` may end up closing them (or affecting close ordering).
|
|
317
|
-
//
|
|
318
|
-
// `threadState` is read-only on the stack frame; do not reassign!
|
|
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.
|
|
319
316
|
const res = await pcallBoundary(sf, fn, args);
|
|
320
317
|
if (res.ok) {
|
|
321
318
|
return new LuaMultiRes([true, ...res.values]);
|
|
@@ -345,7 +342,6 @@ const xpcallFunction = new LuaBuiltinFunction({
|
|
|
345
342
|
errorHandler: ILuaFunction,
|
|
346
343
|
...args
|
|
347
344
|
) => {
|
|
348
|
-
// Same semantic as `pcall` (see comments there)
|
|
349
345
|
const res = await pcallBoundary(sf, fn, args);
|
|
350
346
|
if (res.ok) {
|
|
351
347
|
return new LuaMultiRes([true, ...res.values]);
|
|
@@ -505,6 +501,12 @@ const dofileFunction = new LuaBuiltinFunction({
|
|
|
505
501
|
const env = new LuaEnv(global);
|
|
506
502
|
await evalStatement(parsedExpr, env, sf.withCtx(parsedExpr.ctx));
|
|
507
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
|
+
}
|
|
508
510
|
throw new LuaRuntimeError(
|
|
509
511
|
`Error evaluating "${filename}": ${e.message}`,
|
|
510
512
|
sf,
|
|
@@ -582,30 +584,24 @@ const nextFunction = new LuaBuiltinFunction({
|
|
|
582
584
|
index: number | null = null,
|
|
583
585
|
) => {
|
|
584
586
|
if (!table) {
|
|
585
|
-
// When nil value
|
|
586
587
|
return null;
|
|
587
588
|
}
|
|
588
589
|
const keys = luaKeys(table);
|
|
589
590
|
|
|
590
|
-
// Empty table -> null return value
|
|
591
591
|
if (keys.length === 0) {
|
|
592
592
|
return null;
|
|
593
593
|
}
|
|
594
594
|
|
|
595
595
|
if (index === null) {
|
|
596
|
-
// Return the first key, value
|
|
597
596
|
const key = keys[0];
|
|
598
597
|
return new LuaMultiRes([key, luaGet(table, key, sf.astCtx ?? null, sf)]);
|
|
599
598
|
}
|
|
600
|
-
// Find index in the key list
|
|
601
599
|
const idx = keys.indexOf(index);
|
|
602
600
|
if (idx === -1) {
|
|
603
|
-
// Not found
|
|
604
601
|
throw new LuaRuntimeError("invalid key to 'next': key not found", sf);
|
|
605
602
|
}
|
|
606
603
|
const key = keys[idx + 1];
|
|
607
604
|
if (key === undefined) {
|
|
608
|
-
// When called with the last key, should return nil
|
|
609
605
|
return null;
|
|
610
606
|
}
|
|
611
607
|
return new LuaMultiRes([key, luaGet(table, key, sf.astCtx ?? null, sf)]);
|
|
@@ -622,7 +618,6 @@ const nextFunction = new LuaBuiltinFunction({
|
|
|
622
618
|
],
|
|
623
619
|
});
|
|
624
620
|
|
|
625
|
-
// Non-standard, but useful
|
|
626
621
|
const someFunction = new LuaBuiltinFunction({
|
|
627
622
|
callback: async (_sf, value: any) => {
|
|
628
623
|
switch (await luaTypeOf(value)) {
|
|
@@ -691,13 +686,11 @@ function annotateBuiltinApi(
|
|
|
691
686
|
|
|
692
687
|
export function luaBuildStandardEnv() {
|
|
693
688
|
const env = new LuaEnv();
|
|
694
|
-
// _G global
|
|
695
689
|
env.set("_G", env);
|
|
696
690
|
// Lua version string - for now it signals Lua 5.4 compatibility with
|
|
697
691
|
// selective 5.5 features; kept non-standard so callers can distinguish
|
|
698
692
|
// Space Lua from a plain Lua runtime.
|
|
699
693
|
env.set("_VERSION", "Lua 5.4+");
|
|
700
|
-
// Top-level builtins
|
|
701
694
|
env.set("print", printFunction);
|
|
702
695
|
env.set("assert", assertFunction);
|
|
703
696
|
env.set("type", typeFunction);
|
|
@@ -705,10 +698,8 @@ export function luaBuildStandardEnv() {
|
|
|
705
698
|
env.set("tonumber", tonumberFunction);
|
|
706
699
|
env.set("select", selectFunction);
|
|
707
700
|
env.set("next", nextFunction);
|
|
708
|
-
// Iterators
|
|
709
701
|
env.set("pairs", pairsFunction);
|
|
710
702
|
env.set("ipairs", ipairsFunction);
|
|
711
|
-
// meta table stuff
|
|
712
703
|
env.set("setmetatable", setmetatableFunction);
|
|
713
704
|
env.set("getmetatable", getmetatableFunction);
|
|
714
705
|
env.set("rawlen", rawlenFunction);
|
|
@@ -716,19 +707,15 @@ export function luaBuildStandardEnv() {
|
|
|
716
707
|
env.set("rawget", rawgetFunction);
|
|
717
708
|
env.set("rawequal", rawequalFunction);
|
|
718
709
|
env.set("dofile", dofileFunction);
|
|
719
|
-
// Error handling
|
|
720
710
|
env.set("error", errorFunction);
|
|
721
711
|
env.set("pcall", pcallFunction);
|
|
722
712
|
env.set("xpcall", xpcallFunction);
|
|
723
|
-
// Evaluation
|
|
724
713
|
env.set("load", loadFunction);
|
|
725
|
-
// APIs
|
|
726
714
|
env.set("string", stringApi);
|
|
727
715
|
env.set("table", tableApi);
|
|
728
716
|
env.set("os", osApi);
|
|
729
717
|
env.set("js", jsApi);
|
|
730
718
|
env.set("math", mathApi);
|
|
731
|
-
// Non-standard
|
|
732
719
|
env.set("each", eachFunction);
|
|
733
720
|
env.set("spacelua", spaceluaApi);
|
|
734
721
|
env.set("encoding", encodingApi);
|
|
@@ -142,7 +142,6 @@ function parseInt(s: string): { ok: boolean; value: number } {
|
|
|
142
142
|
let acc = 0;
|
|
143
143
|
let any = false;
|
|
144
144
|
|
|
145
|
-
// hex?
|
|
146
145
|
if (s.charCodeAt(i) === 48 && i + 1 < n) {
|
|
147
146
|
// '0'
|
|
148
147
|
const x = s.charCodeAt(i + 1);
|
|
@@ -179,7 +178,6 @@ function parseInt(s: string): { ok: boolean; value: number } {
|
|
|
179
178
|
}
|
|
180
179
|
}
|
|
181
180
|
|
|
182
|
-
// decimal integer
|
|
183
181
|
while (i < n) {
|
|
184
182
|
const c = s.charCodeAt(i);
|
|
185
183
|
if (c < 48 || c > 57) {
|
|
@@ -221,7 +219,6 @@ function parseDecFloat(s: string): { ok: boolean; value: number } {
|
|
|
221
219
|
let fracAny = false;
|
|
222
220
|
let val = 0;
|
|
223
221
|
|
|
224
|
-
// integer part
|
|
225
222
|
while (i < n) {
|
|
226
223
|
const c = s.charCodeAt(i);
|
|
227
224
|
if (c < 48 || c > 57) {
|
|
@@ -233,7 +230,6 @@ function parseDecFloat(s: string): { ok: boolean; value: number } {
|
|
|
233
230
|
i++;
|
|
234
231
|
}
|
|
235
232
|
|
|
236
|
-
// fractional part
|
|
237
233
|
if (i < n) {
|
|
238
234
|
if (s.charCodeAt(i) === 46) {
|
|
239
235
|
// '.'
|
|
@@ -253,7 +249,6 @@ function parseDecFloat(s: string): { ok: boolean; value: number } {
|
|
|
253
249
|
}
|
|
254
250
|
}
|
|
255
251
|
|
|
256
|
-
// exponent
|
|
257
252
|
let exp = 0;
|
|
258
253
|
let hasExp = false;
|
|
259
254
|
if (i < n) {
|
|
@@ -347,7 +342,6 @@ function parseHexFloat(s: string): { ok: boolean; value: number } {
|
|
|
347
342
|
let anyHex = false;
|
|
348
343
|
let sawDot = false;
|
|
349
344
|
|
|
350
|
-
// integer hex digits
|
|
351
345
|
while (i < n) {
|
|
352
346
|
const c = s.charCodeAt(i);
|
|
353
347
|
let d = -1;
|
|
@@ -371,7 +365,6 @@ function parseHexFloat(s: string): { ok: boolean; value: number } {
|
|
|
371
365
|
i++;
|
|
372
366
|
}
|
|
373
367
|
|
|
374
|
-
// optional fractional part
|
|
375
368
|
if (i < n) {
|
|
376
369
|
if (s.charCodeAt(i) === 46) {
|
|
377
370
|
// '.'
|
|
@@ -444,7 +437,6 @@ function parseHexFloat(s: string): { ok: boolean; value: number } {
|
|
|
444
437
|
return { ok: false, value: 0 };
|
|
445
438
|
}
|
|
446
439
|
} else {
|
|
447
|
-
// no exponent marker
|
|
448
440
|
if (!sawDot) {
|
|
449
441
|
// without dot we must reject so parseInt can claim it as integer
|
|
450
442
|
return { ok: false, value: 0 };
|
|
@@ -456,7 +448,6 @@ function parseHexFloat(s: string): { ok: boolean; value: number } {
|
|
|
456
448
|
exp = 0; // implicit p0
|
|
457
449
|
}
|
|
458
450
|
} else {
|
|
459
|
-
// end of string
|
|
460
451
|
if (!sawDot) {
|
|
461
452
|
return { ok: false, value: 0 };
|
|
462
453
|
}
|
package/dist/plug-compile.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
6
|
// version.ts
|
|
7
|
-
var version = "2.
|
|
7
|
+
var version = "2.12.0";
|
|
8
8
|
|
|
9
9
|
// client/plugos/plug_compile.ts
|
|
10
10
|
import * as path from "node:path";
|
|
@@ -1330,7 +1330,7 @@ var workerRuntimePlugin = {
|
|
|
1330
1330
|
namespace: "worker-runtime"
|
|
1331
1331
|
}));
|
|
1332
1332
|
build2.onLoad({ filter: /.*/, namespace: "worker-runtime" }, () => ({
|
|
1333
|
-
contents: '// plug-api/lib/crypto.ts\nfunction base64Decode(s) {\n const binString = atob(s);\n const len = binString.length;\n const bytes = new Uint8Array(len);\n for (let i = 0; i < len; i++) {\n bytes[i] = binString.charCodeAt(i);\n }\n return bytes;\n}\nfunction base64Encode(buffer) {\n if (typeof buffer === "string") {\n buffer = new TextEncoder().encode(buffer);\n }\n let binary = "";\n const len = buffer.byteLength;\n for (let i = 0; i < len; i++) {\n binary += String.fromCharCode(buffer[i]);\n }\n return btoa(binary);\n}\nvar fixedCounter = new Uint8Array(16);\n\n// client/lib/logger.ts\nvar Logger = class {\n constructor(prefix = "", maxCaptureSize = 1e3) {\n this.prefix = prefix;\n this.maxCaptureSize = maxCaptureSize;\n this.prefix = prefix;\n this.originalConsole = {\n log: console.log.bind(console),\n info: console.info.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n debug: console.debug.bind(console)\n };\n this.patchConsole();\n }\n originalConsole;\n logBuffer = [];\n patchConsole() {\n const createPatchedMethod = (level) => {\n return (...args) => {\n const prefixedArgs = this.prefix ? [this.prefix, ...args] : args;\n this.originalConsole[level](...prefixedArgs);\n this.captureLog(level, args);\n };\n };\n console.log = createPatchedMethod("log");\n console.info = createPatchedMethod("info");\n console.warn = createPatchedMethod("warn");\n console.error = createPatchedMethod("error");\n console.debug = createPatchedMethod("debug");\n }\n captureLog(level, args) {\n const entry = {\n level,\n timestamp: Date.now(),\n message: args.map((arg) => {\n if (typeof arg === "string") {\n return arg;\n }\n try {\n return JSON.stringify(arg);\n } catch {\n return String(arg);\n }\n }).join(" ")\n };\n this.logBuffer.push(entry);\n if (this.logBuffer.length > this.maxCaptureSize) {\n this.logBuffer.shift();\n }\n }\n /**\n * Posts all buffered logs to a server endpoint\n */\n async postToServer(logEndpoint, source) {\n const logs = this.logBuffer;\n if (logs.length > 0) {\n const logCopy = [...this.logBuffer];\n this.logBuffer = [];\n try {\n const resp = await fetch(logEndpoint, {\n method: "POST",\n headers: {\n "Content-Type": "application/json"\n },\n body: JSON.stringify(logCopy.map((entry) => ({ ...entry, source })))\n });\n if (!resp.ok) {\n throw new Error("Failed to post logs to server");\n }\n } catch (e) {\n console.warn("Could not post logs to server", e.message);\n this.logBuffer.unshift(...logCopy);\n }\n }\n }\n};\nvar globalLogger;\nfunction initLogger(prefix = "") {\n globalLogger = new Logger(prefix);\n return globalLogger;\n}\n\n// client/plugos/worker_runtime.ts\nvar workerPostMessage = (_msg) => {\n throw new Error("Not initialized yet");\n};\nvar runningAsWebWorker = typeof window === "undefined" && // @ts-expect-error: globalThis\ntypeof globalThis.WebSocketPair === "undefined";\nvar pendingRequests = /* @__PURE__ */ new Map();\nvar syscallReqId = 0;\nif (runningAsWebWorker) {\n globalThis.syscall = async (name, ...args) => {\n return await new Promise((resolve, reject) => {\n syscallReqId++;\n pendingRequests.set(syscallReqId, { resolve, reject });\n workerPostMessage({\n type: "sys",\n id: syscallReqId,\n name,\n args\n });\n });\n };\n}\nfunction setupMessageListener(functionMapping, manifest, postMessageFn) {\n if (!runningAsWebWorker) {\n return;\n }\n workerPostMessage = postMessageFn;\n self.addEventListener("message", (event) => {\n (async () => {\n const data = event.data;\n switch (data.type) {\n case "inv":\n {\n const fn = functionMapping[data.name];\n if (!fn) {\n throw new Error(`Function not loaded: ${data.name}`);\n }\n try {\n const result = await Promise.resolve(fn(...data.args || []));\n workerPostMessage({\n type: "invr",\n id: data.id,\n result\n });\n } catch (e) {\n console.error(\n "An exception was thrown as a result of invoking function",\n data.name,\n "error:",\n e.message\n );\n workerPostMessage({\n type: "invr",\n id: data.id,\n error: e.message\n });\n }\n }\n break;\n case "sysr":\n {\n const syscallId = data.id;\n const lookup = pendingRequests.get(syscallId);\n if (!lookup) {\n throw Error("Invalid request id");\n }\n pendingRequests.delete(syscallId);\n if (data.error) {\n lookup.reject(new Error(data.error));\n } else {\n lookup.resolve(data.result);\n }\n }\n break;\n }\n })().catch(console.error);\n });\n workerPostMessage({\n type: "manifest",\n manifest\n });\n initLogger(`[${manifest.name} plug]`);\n}\nasync function sandboxFetch(reqInfo, options) {\n if (typeof reqInfo !== "string") {\n const body = new Uint8Array(await reqInfo.arrayBuffer());\n const encodedBody = body.length > 0 ? base64Encode(body) : void 0;\n options = {\n method: reqInfo.method,\n headers: Object.fromEntries(reqInfo.headers.entries()),\n base64Body: encodedBody\n };\n reqInfo = reqInfo.url;\n }\n return syscall("sandboxFetch.fetch", reqInfo, options);\n}\nglobalThis.nativeFetch = globalThis.fetch;\nfunction monkeyPatchFetch() {\n globalThis.fetch = async (reqInfo, init) => {\n const encodedBody = init?.body ? base64Encode(\n new Uint8Array(await new Response(init.body).arrayBuffer())\n ) : void 0;\n const r = await sandboxFetch(\n reqInfo,\n init && {\n method: init.method,\n headers: init.headers,\n base64Body: encodedBody\n }\n );\n return new Response(\n r.base64Body ? base64Decode(r.base64Body) : null,\n {\n status: r.status,\n headers: r.headers\n }\n );\n };\n}\nif (runningAsWebWorker) {\n monkeyPatchFetch();\n}\nexport {\n monkeyPatchFetch,\n sandboxFetch,\n setupMessageListener\n};\n',
|
|
1333
|
+
contents: '// plug-api/lib/crypto.ts\nfunction base64Decode(s) {\n const binString = atob(s);\n const len = binString.length;\n const bytes = new Uint8Array(len);\n for (let i = 0; i < len; i++) {\n bytes[i] = binString.charCodeAt(i);\n }\n return bytes;\n}\nfunction base64Encode(buffer) {\n if (typeof buffer === "string") {\n buffer = new TextEncoder().encode(buffer);\n }\n let binary = "";\n const len = buffer.byteLength;\n for (let i = 0; i < len; i++) {\n binary += String.fromCharCode(buffer[i]);\n }\n return btoa(binary);\n}\nvar fixedCounter = new Uint8Array(16);\n\n// client/lib/logger.ts\nvar Logger = class {\n constructor(prefix = "", maxCaptureSize = 1e3) {\n this.prefix = prefix;\n this.maxCaptureSize = maxCaptureSize;\n this.prefix = prefix;\n this.originalConsole = {\n log: console.log.bind(console),\n info: console.info.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n debug: console.debug.bind(console)\n };\n this.patchConsole();\n }\n prefix;\n maxCaptureSize;\n originalConsole;\n logBuffer = [];\n patchConsole() {\n const createPatchedMethod = (level) => {\n return (...args) => {\n const prefixedArgs = this.prefix ? [this.prefix, ...args] : args;\n this.originalConsole[level](...prefixedArgs);\n this.captureLog(level, args);\n };\n };\n console.log = createPatchedMethod("log");\n console.info = createPatchedMethod("info");\n console.warn = createPatchedMethod("warn");\n console.error = createPatchedMethod("error");\n console.debug = createPatchedMethod("debug");\n }\n captureLog(level, args) {\n const entry = {\n level,\n timestamp: Date.now(),\n message: args.map((arg) => {\n if (typeof arg === "string") {\n return arg;\n }\n try {\n return JSON.stringify(arg);\n } catch {\n return String(arg);\n }\n }).join(" ")\n };\n this.logBuffer.push(entry);\n if (this.logBuffer.length > this.maxCaptureSize) {\n this.logBuffer.shift();\n }\n }\n /**\n * Posts all buffered logs to a server endpoint\n */\n async postToServer(logEndpoint, source) {\n const logs = this.logBuffer;\n if (logs.length > 0) {\n const logCopy = [...this.logBuffer];\n this.logBuffer = [];\n try {\n const resp = await fetch(logEndpoint, {\n method: "POST",\n headers: {\n "Content-Type": "application/json"\n },\n body: JSON.stringify(logCopy.map((entry) => ({ ...entry, source })))\n });\n if (!resp.ok) {\n throw new Error("Failed to post logs to server");\n }\n } catch (e) {\n console.warn("Could not post logs to server", e.message);\n this.logBuffer.unshift(...logCopy);\n }\n }\n }\n};\nvar globalLogger;\nfunction initLogger(prefix = "") {\n globalLogger = new Logger(prefix);\n return globalLogger;\n}\n\n// client/plugos/worker_runtime.ts\nvar workerPostMessage = (_msg) => {\n throw new Error("Not initialized yet");\n};\nvar runningAsWebWorker = typeof window === "undefined" && // @ts-expect-error: globalThis\ntypeof globalThis.WebSocketPair === "undefined";\nvar pendingRequests = /* @__PURE__ */ new Map();\nvar syscallReqId = 0;\nif (runningAsWebWorker) {\n globalThis.syscall = async (name, ...args) => {\n return await new Promise((resolve, reject) => {\n syscallReqId++;\n pendingRequests.set(syscallReqId, { resolve, reject });\n workerPostMessage({\n type: "sys",\n id: syscallReqId,\n name,\n args\n });\n });\n };\n}\nfunction setupMessageListener(functionMapping, manifest, postMessageFn) {\n if (!runningAsWebWorker) {\n return;\n }\n workerPostMessage = postMessageFn;\n self.addEventListener("message", (event) => {\n (async () => {\n const data = event.data;\n switch (data.type) {\n case "inv":\n {\n const fn = functionMapping[data.name];\n if (!fn) {\n throw new Error(`Function not loaded: ${data.name}`);\n }\n try {\n const result = await Promise.resolve(fn(...data.args || []));\n workerPostMessage({\n type: "invr",\n id: data.id,\n result\n });\n } catch (e) {\n console.error(\n "An exception was thrown as a result of invoking function",\n data.name,\n "error:",\n e.message\n );\n workerPostMessage({\n type: "invr",\n id: data.id,\n error: e.message\n });\n }\n }\n break;\n case "sysr":\n {\n const syscallId = data.id;\n const lookup = pendingRequests.get(syscallId);\n if (!lookup) {\n throw Error("Invalid request id");\n }\n pendingRequests.delete(syscallId);\n if (data.error) {\n lookup.reject(new Error(data.error));\n } else {\n lookup.resolve(data.result);\n }\n }\n break;\n }\n })().catch(console.error);\n });\n workerPostMessage({\n type: "manifest",\n manifest\n });\n initLogger(`[${manifest.name} plug]`);\n}\nasync function sandboxFetch(reqInfo, options) {\n if (typeof reqInfo !== "string") {\n const body = new Uint8Array(await reqInfo.arrayBuffer());\n const encodedBody = body.length > 0 ? base64Encode(body) : void 0;\n options = {\n method: reqInfo.method,\n headers: Object.fromEntries(reqInfo.headers.entries()),\n base64Body: encodedBody\n };\n reqInfo = reqInfo.url;\n }\n return syscall("sandboxFetch.fetch", reqInfo, options);\n}\nglobalThis.nativeFetch = globalThis.fetch;\nfunction monkeyPatchFetch() {\n globalThis.fetch = async (reqInfo, init) => {\n const encodedBody = init?.body ? base64Encode(\n new Uint8Array(await new Response(init.body).arrayBuffer())\n ) : void 0;\n const r = await sandboxFetch(\n reqInfo,\n init && {\n method: init.method,\n headers: init.headers,\n base64Body: encodedBody\n }\n );\n return new Response(\n r.base64Body ? base64Decode(r.base64Body) : null,\n {\n status: r.status,\n headers: r.headers\n }\n );\n };\n}\nif (runningAsWebWorker) {\n monkeyPatchFetch();\n}\nexport {\n monkeyPatchFetch,\n sandboxFetch,\n setupMessageListener\n};\n',
|
|
1334
1334
|
loader: "js"
|
|
1335
1335
|
}));
|
|
1336
1336
|
} else {
|
|
@@ -1371,8 +1371,7 @@ ${Object.entries(manifest.functions).map(([funcName, def]) => {
|
|
|
1371
1371
|
}
|
|
1372
1372
|
let [filePath, jsFunctionName] = def.path.split(":");
|
|
1373
1373
|
filePath = path.join(rootPath, filePath);
|
|
1374
|
-
return `import {${jsFunctionName} as ${funcName}} from "${
|
|
1375
|
-
path.resolve(filePath).replaceAll("\\", "\\\\")}";
|
|
1374
|
+
return `import {${jsFunctionName} as ${funcName}} from "${path.resolve(filePath).replaceAll("\\", "\\\\")}";
|
|
1376
1375
|
`;
|
|
1377
1376
|
}).join("")}
|
|
1378
1377
|
|