@silverbulletmd/silverbullet 2.4.1 → 2.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +20 -4
  2. package/client/markdown_parser/constants.ts +2 -2
  3. package/client/plugos/hooks/code_widget.ts +0 -3
  4. package/client/plugos/hooks/document_editor.ts +0 -3
  5. package/client/plugos/hooks/event.ts +1 -1
  6. package/client/plugos/hooks/mq.ts +1 -1
  7. package/client/plugos/hooks/plug_namespace.ts +0 -3
  8. package/client/plugos/hooks/slash_command.ts +2 -2
  9. package/client/plugos/plug.ts +0 -1
  10. package/client/plugos/plug_compile.ts +28 -29
  11. package/client/plugos/proxy_fetch.ts +1 -1
  12. package/client/plugos/sandboxes/web_worker_sandbox.ts +1 -1
  13. package/client/plugos/sandboxes/worker_sandbox.ts +2 -3
  14. package/client/plugos/syscalls/editor.ts +12 -12
  15. package/client/plugos/syscalls/fetch.ts +1 -1
  16. package/client/plugos/syscalls/jsonschema.ts +1 -1
  17. package/client/plugos/syscalls/mq.ts +1 -1
  18. package/client/plugos/syscalls/space.ts +1 -1
  19. package/client/plugos/system.ts +2 -2
  20. package/client/plugos/worker_runtime.ts +8 -29
  21. package/client/space_lua/aggregates.ts +209 -0
  22. package/client/space_lua/ast.ts +24 -2
  23. package/client/space_lua/eval.ts +58 -53
  24. package/client/space_lua/labels.ts +1 -1
  25. package/client/space_lua/parse.ts +117 -12
  26. package/client/space_lua/query_collection.ts +850 -70
  27. package/client/space_lua/query_env.ts +26 -0
  28. package/client/space_lua/runtime.ts +47 -17
  29. package/client/space_lua/stdlib/format.ts +19 -19
  30. package/client/space_lua/stdlib/math.ts +73 -48
  31. package/client/space_lua/stdlib/net.ts +2 -2
  32. package/client/space_lua/stdlib/os.ts +5 -0
  33. package/client/space_lua/stdlib/pattern.ts +702 -0
  34. package/client/space_lua/stdlib/prng.ts +145 -0
  35. package/client/space_lua/stdlib/space_lua.ts +3 -8
  36. package/client/space_lua/stdlib/string.ts +103 -181
  37. package/client/space_lua/stdlib/string_pack.ts +486 -0
  38. package/client/space_lua/stdlib/table.ts +73 -9
  39. package/client/space_lua/stdlib.ts +38 -14
  40. package/client/space_lua/tonumber.ts +3 -2
  41. package/client/space_lua/util.ts +43 -9
  42. package/dist/plug-compile.js +23 -69
  43. package/dist/worker_runtime_bundle.js +233 -0
  44. package/package.json +16 -11
  45. package/plug-api/constants.ts +0 -32
  46. package/plug-api/lib/async.ts +2 -2
  47. package/plug-api/lib/crypto.ts +11 -11
  48. package/plug-api/lib/json.ts +1 -1
  49. package/plug-api/lib/limited_map.ts +1 -1
  50. package/plug-api/lib/native_fetch.ts +2 -0
  51. package/plug-api/lib/ref.ts +5 -5
  52. package/plug-api/lib/transclusion.ts +5 -5
  53. package/plug-api/lib/tree.ts +50 -2
  54. package/plug-api/lib/yaml.ts +10 -10
  55. package/plug-api/syscalls/editor.ts +1 -1
  56. package/plug-api/system_mock.ts +0 -1
  57. package/client/plugos/sandboxes/deno_worker_sandbox.ts +0 -6
@@ -0,0 +1,145 @@
1
+ // PRNG based on xoshiro256** for Space Lua
2
+
3
+ export class LuaPRNG {
4
+ private state: BigUint64Array;
5
+
6
+ constructor() {
7
+ this.state = new BigUint64Array(4);
8
+ this.autoSeed();
9
+ }
10
+
11
+ private rotl(x: bigint, k: number): bigint {
12
+ k = k & 63;
13
+ return ((x << BigInt(k)) | (x >> BigInt(64 - k))) & 0xFFFFFFFFFFFFFFFFn;
14
+ }
15
+
16
+ private nextrand(): bigint {
17
+ const s = this.state;
18
+ const s0 = s[0];
19
+ const s1 = s[1];
20
+ const s2 = s[2];
21
+ const s3 = s[3];
22
+
23
+ const res = this.rotl((s1 * 5n) & 0xFFFFFFFFFFFFFFFFn, 7) * 9n &
24
+ 0xFFFFFFFFFFFFFFFFn;
25
+
26
+ const t = (s1 << 17n) & 0xFFFFFFFFFFFFFFFFn;
27
+ s[2] = s2 ^ s0;
28
+ s[3] = s3 ^ s1;
29
+ s[1] = s1 ^ s[2];
30
+ s[0] = s0 ^ s[3];
31
+ s[2] = s[2] ^ t;
32
+ s[3] = this.rotl(s[3], 45);
33
+
34
+ return res;
35
+ }
36
+
37
+ public setSeed(seed1: bigint, seed2: bigint = 0n): [bigint, bigint] {
38
+ const MASK = 0xFFFFFFFFFFFFFFFFn;
39
+ const s = this.state;
40
+
41
+ const sm64 = (x: bigint): bigint => {
42
+ x = (x ^ (x >> 30n)) * 0xBF58476D1CE4E5B9n & MASK;
43
+ x = (x ^ (x >> 27n)) * 0x94D049BB133111EBn & MASK;
44
+ return (x ^ (x >> 31n)) & MASK;
45
+ };
46
+
47
+ s[0] = sm64(seed1 & MASK);
48
+ s[1] = sm64((seed1 & MASK) | 0xFFn);
49
+ s[2] = sm64(seed2 & MASK);
50
+ s[3] = sm64(0n);
51
+
52
+ for (let i = 0; i < 16; i++) {
53
+ this.nextrand();
54
+ }
55
+
56
+ return [seed1, seed2];
57
+ }
58
+
59
+ private autoSeed(): [bigint, bigint] {
60
+ const t = BigInt(Date.now());
61
+ const entropy = BigInt(Math.floor(performance.now() * 1000));
62
+ return this.setSeed(t, entropy);
63
+ }
64
+
65
+ private project(ran: bigint, n: bigint): bigint {
66
+ if (n === 0n) return 0n;
67
+
68
+ let lim = n;
69
+ lim |= lim >> 1n;
70
+ lim |= lim >> 2n;
71
+ lim |= lim >> 4n;
72
+ lim |= lim >> 8n;
73
+ lim |= lim >> 16n;
74
+ lim |= lim >> 32n;
75
+
76
+ while (true) {
77
+ ran &= lim;
78
+ if (ran <= n) return ran;
79
+ ran = this.nextrand();
80
+ }
81
+ }
82
+
83
+ // `random()` yields float in [0, 1)
84
+ // `random(0)` yields raw 64-bit signed integer (all bits random)
85
+ // `random(n)` yields integer in [1, n]
86
+ // `random(m, n)` yields integer in [m, n]
87
+ public random(arg1?: number, arg2?: number): number | bigint {
88
+ const rv = this.nextrand();
89
+
90
+ if (arg1 === undefined) {
91
+ // Top 53 bits for full double precision
92
+ return Number(rv >> 11n) * (1.0 / 9007199254740992.0);
93
+ }
94
+
95
+ if (!Number.isFinite(arg1) || !Number.isInteger(arg1)) {
96
+ throw new Error(
97
+ "bad argument #1 to 'random' (number has no integer representation)",
98
+ );
99
+ }
100
+
101
+ if (arg2 === undefined) {
102
+ if (arg1 === 0) {
103
+ // Raw 64-bit as signed bigint
104
+ const signed = rv > 0x7FFFFFFFFFFFFFFFn
105
+ ? rv - 0x10000000000000000n
106
+ : rv;
107
+ return signed;
108
+ }
109
+ if (arg1 < 1) {
110
+ throw new Error("bad argument #1 to 'random' (interval is empty)");
111
+ }
112
+ return Number(this.project(rv, BigInt(arg1) - 1n) + 1n);
113
+ }
114
+
115
+ if (!Number.isFinite(arg2) || !Number.isInteger(arg2)) {
116
+ throw new Error(
117
+ "bad argument #2 to 'random' (number has no integer representation)",
118
+ );
119
+ }
120
+ if (arg2 < arg1) {
121
+ throw new Error("bad argument #2 to 'random' (interval is empty)");
122
+ }
123
+ return Number(this.project(rv, BigInt(arg2) - BigInt(arg1)) + BigInt(arg1));
124
+ }
125
+
126
+ // Returns [seed1, seed2]
127
+ public randomseed(arg1?: number, arg2?: number): [bigint, bigint] {
128
+ if (arg1 === undefined) {
129
+ return this.autoSeed();
130
+ }
131
+ if (!Number.isFinite(arg1) || !Number.isInteger(arg1)) {
132
+ throw new Error(
133
+ "bad argument #1 to 'randomseed' (number has no integer representation)",
134
+ );
135
+ }
136
+ if (arg2 !== undefined && (!Number.isFinite(arg2) || !Number.isInteger(arg2))) {
137
+ throw new Error(
138
+ "bad argument #2 to 'randomseed' (number has no integer representation)",
139
+ );
140
+ }
141
+ const s1 = BigInt(Math.trunc(arg1));
142
+ const s2 = arg2 !== undefined ? BigInt(Math.trunc(arg2)) : 0n;
143
+ return this.setSeed(s1, s2);
144
+ }
145
+ }
@@ -137,17 +137,12 @@ export const spaceluaApi = new LuaTable({
137
137
  },
138
138
  ),
139
139
  /**
140
- * Returns your SilverBullet instance's base URL, or `undefined` when run on the server
140
+ * Returns your SilverBullet instance's base URL
141
141
  */
142
142
  baseUrl: new LuaBuiltinFunction(
143
143
  () => {
144
- // Deal with Deno
145
- if (typeof location === "undefined") {
146
- return null;
147
- } else {
148
- //NOTE: Removing trailing slash to stay compatible with original code: `location.protocol + "//" + location.host;`
149
- return document.baseURI.replace(/\/*$/, "");
150
- }
144
+ //NOTE: Removing trailing slash to stay compatible with original code: `location.protocol + "//" + location.host;`
145
+ return document.baseURI.replace(/\/*$/, "");
151
146
  },
152
147
  ),
153
148
  });
@@ -6,79 +6,35 @@ import {
6
6
  LuaTable,
7
7
  luaToString,
8
8
  } from "../runtime.ts";
9
- import { untagNumber } from "../numeric.ts";
9
+ import { isTaggedFloat, untagNumber } from "../numeric.ts";
10
10
  import { luaFormat } from "./format.ts";
11
-
12
- // Bits and pieces borrowed from https://github.com/paulcuth/starlight/blob/master/src/runtime/lib/string.js
13
-
14
- const ROSETTA_STONE = {
15
- "([^a-zA-Z0-9%(])-": "$1*?",
16
- "([^%])-([^a-zA-Z0-9?])": "$1*?$2",
17
- "([^%])-$": "$1*?",
18
- "%a": "[a-zA-Z]",
19
- "%A": "[^a-zA-Z]",
20
- "%c": "[\x00-\x1f]",
21
- "%C": "[^\x00-\x1f]",
22
- "%d": "\\d",
23
- "%D": "[^\d]",
24
- "%l": "[a-z]",
25
- "%L": "[^a-z]",
26
- "%p": "[\.\,\"'\?\!\;\:\#\$\%\&\(\)\*\+\-\/\<\>\=\@\\[\\]\\\\^\_\{\}\|\~]",
27
- "%P": "[^\.\,\"'\?\!\;\:\#\$\%\&\(\)\*\+\-\/\<\>\=\@\\[\\]\\\\^\_\{\}\|\~]",
28
- "%s": "[ \\t\\n\\f\\v\\r]",
29
- "%S": "[^ \t\n\f\v\r]",
30
- "%u": "[A-Z]",
31
- "%U": "[^A-Z]",
32
- "%w": "[a-zA-Z0-9]",
33
- "%W": "[^a-zA-Z0-9]",
34
- "%x": "[a-fA-F0-9]",
35
- "%X": "[^a-fA-F0-9]",
36
- "%([^a-zA-Z])": "\\$1",
37
- };
38
-
39
- function translatePattern(pattern: string): string {
40
- pattern = "" + pattern;
41
-
42
- // Replace single backslash with double backslashes
43
- pattern = pattern.replace(new RegExp("\\\\", "g"), "\\\\");
44
- pattern = pattern.replace(new RegExp("\\|", "g"), "\\|");
45
-
46
- for (const [key, value] of Object.entries(ROSETTA_STONE)) {
47
- pattern = pattern.replace(new RegExp(key, "g"), value);
48
- }
49
-
50
- let l = pattern.length;
51
- let n = 0;
52
-
53
- for (let i = 0; i < l; i++) {
54
- const character = pattern.slice(i, 1);
55
- if (i && pattern.slice(i - 1, 1) == "\\") {
56
- continue;
57
- }
58
-
59
- let addSlash = false;
60
-
61
- if (character == "[") {
62
- if (n) addSlash = true;
63
- n++;
64
- } else if (character == "]" && pattern.slice(i - 1, 1) !== "\\") {
65
- n--;
66
- if (n) addSlash = true;
67
- }
68
-
69
- if (addSlash) {
70
- pattern = pattern.slice(0, i) + pattern.slice(i++ + 1);
71
- l++;
72
- }
11
+ import {
12
+ type CaptureResult,
13
+ type GsubCallbacks,
14
+ patternFind,
15
+ patternGmatch,
16
+ patternGsub,
17
+ patternMatch,
18
+ } from "./pattern.ts";
19
+ import { strPackFn, strPackSizeFn, strUnpackFn } from "./string_pack.ts";
20
+
21
+ function capturesToLua(caps: CaptureResult[]): any {
22
+ if (caps.length === 0) return null;
23
+ if (caps.length === 1) {
24
+ const c = caps[0];
25
+ return "s" in c ? c.s : c.position;
73
26
  }
74
-
75
- return pattern;
27
+ return new LuaMultiRes(
28
+ caps.map((c) => ("s" in c ? c.s : c.position)),
29
+ );
76
30
  }
77
31
 
78
32
  export const stringApi = new LuaTable({
79
33
  byte: new LuaBuiltinFunction((_sf, s: string, i?: number, j?: number) => {
80
34
  i = i ?? 1;
81
35
  j = j ?? i;
36
+ if (j > s.length) j = s.length;
37
+ if (i < 1) i = 1;
82
38
  const result = [];
83
39
  for (let k = i; k <= j; k++) {
84
40
  result.push(s.charCodeAt(k - 1));
@@ -90,119 +46,70 @@ export const stringApi = new LuaTable({
90
46
  }),
91
47
  find: new LuaBuiltinFunction(
92
48
  (_sf, s: string, pattern: string, init = 1, plain = false) => {
93
- // Regex
94
- if (!plain) {
95
- pattern = translatePattern(pattern);
96
- const reg = new RegExp(pattern);
97
- const index = s.slice(init - 1).search(reg);
98
-
99
- if (index < 0) return null;
100
-
101
- const match = s.slice(init - 1).match(reg);
102
- const result = [index + init, index + init + match![0].length - 1];
103
-
104
- match!.shift();
105
- return new LuaMultiRes(result.concat(match));
49
+ const r = patternFind(s, pattern, init, plain);
50
+ if (!r) return null;
51
+ const result: any[] = [r.start, r.end];
52
+ for (const c of r.captures) {
53
+ result.push("s" in c ? c.s : c.position);
106
54
  }
107
-
108
- // Plain
109
- const index = s.indexOf(pattern, init - 1);
110
- return (index === -1)
111
- ? null
112
- : new LuaMultiRes([index + 1, index + pattern.length]);
55
+ return new LuaMultiRes(result);
113
56
  },
114
57
  ),
115
58
  format: new LuaBuiltinFunction((_sf, format: string, ...args: any[]) => {
116
- // Unwrap tagged floats so luaFormat sees plain numbers
117
59
  for (let i = 0; i < args.length; i++) {
118
60
  args[i] = untagNumber(args[i]);
119
61
  }
120
62
  return luaFormat(format, ...args);
121
63
  }),
122
- gmatch: new LuaBuiltinFunction((_sf, s: string, pattern: string) => {
123
- pattern = translatePattern(pattern);
124
- const reg = new RegExp(pattern, "g"),
125
- matches = s.match(reg);
126
- return () => {
127
- if (!matches) {
128
- return;
129
- }
130
- const match = matches.shift();
131
- if (!match) {
132
- return;
133
- }
134
- const groups = new RegExp(pattern).exec(match) || [];
135
-
136
- groups.shift();
137
- return groups.length ? new LuaMultiRes(groups) : match;
138
- };
139
- }),
64
+ gmatch: new LuaBuiltinFunction(
65
+ (_sf, s: string, pattern: string, init = 1) => {
66
+ const iter = patternGmatch(s, pattern, init);
67
+ return () => {
68
+ const caps = iter();
69
+ if (!caps) return;
70
+ return capturesToLua(caps);
71
+ };
72
+ },
73
+ ),
140
74
  gsub: new LuaBuiltinFunction(
141
75
  async (
142
76
  sf,
143
77
  s: string,
144
78
  pattern: string,
145
- repl: any, // string or LuaFunction
146
- n = Infinity,
79
+ repl: any,
80
+ n?: number,
147
81
  ) => {
148
- pattern = translatePattern(pattern);
149
- const replIsFunction = repl.call;
150
-
151
- let count = 0,
152
- result = "",
153
- str,
154
- prefix,
155
- match: any,
156
- lastMatch;
157
-
158
- while (
159
- count < n &&
160
- s &&
161
- (match = s.match(pattern))
162
- ) {
163
- if (replIsFunction) {
164
- // If no captures, pass in the whole match
165
- if (match[1] === undefined) {
166
- str = await repl.call(sf, match[0]);
167
- } else {
168
- // Else pass in the captures
169
- str = await repl.call(sf, ...match.slice(1));
170
- }
171
- if (str instanceof LuaMultiRes) {
172
- str = str.values[0];
173
- }
174
- if (str === undefined || str === null) {
175
- str = match[0];
82
+ const callbacks: GsubCallbacks = {};
83
+ if (typeof repl === "string") {
84
+ callbacks.replString = repl;
85
+ } else if (repl instanceof LuaTable) {
86
+ callbacks.replTable = (key: string) => {
87
+ const v = repl.get(key);
88
+ if (v === null || v === undefined || v === false) return null;
89
+ return typeof v === "number"
90
+ ? String(v)
91
+ : String(isTaggedFloat(v) ? v.value : v);
92
+ };
93
+ } else if (repl.call) {
94
+ callbacks.replFunction = async (...caps: CaptureResult[]) => {
95
+ const args = caps.map((c) => ("s" in c ? c.s : c.position));
96
+ let result = await repl.call(sf, ...args);
97
+ if (result instanceof LuaMultiRes) {
98
+ result = result.values[0];
176
99
  }
177
- } else if (repl instanceof LuaTable) {
178
- str = repl.get(match[0]);
179
- } else if (typeof repl === "string") {
180
- str = repl.replaceAll(/%([0-9]+)/g, (_, i) => match[i]);
181
- } else {
182
- throw new LuaRuntimeError(
183
- "string.gsub replacement argument should be a function, table or string",
184
- sf,
185
- );
186
- }
187
-
188
- if (match[0].length === 0) {
189
- if (lastMatch === void 0) {
190
- prefix = "";
191
- } else {
192
- prefix = s.slice(0, 1);
100
+ if (result === null || result === undefined || result === false) {
101
+ return null;
193
102
  }
194
- } else {
195
- prefix = s.slice(0, match.index);
196
- }
197
-
198
- lastMatch = match[0];
199
- result += `${prefix}${str}`;
200
- s = s.slice(`${prefix}${lastMatch}`.length);
201
-
202
- count++;
103
+ return luaToString(result);
104
+ };
105
+ } else {
106
+ throw new LuaRuntimeError(
107
+ "string.gsub replacement argument should be a function, table or string",
108
+ sf,
109
+ );
203
110
  }
204
-
205
- return new LuaMultiRes([`${result}${s}`, count]);
111
+ const [result, count] = await patternGsub(s, pattern, callbacks, n);
112
+ return new LuaMultiRes([result, count]);
206
113
  },
207
114
  ),
208
115
  len: new LuaBuiltinFunction((_sf, s: string) => {
@@ -216,43 +123,58 @@ export const stringApi = new LuaTable({
216
123
  }),
217
124
  match: new LuaBuiltinFunction(
218
125
  (_sf, s: string, pattern: string, init = 1) => {
219
- s = s.slice(init - 1);
220
- const matches = s.match(new RegExp(translatePattern(pattern)));
221
-
222
- if (!matches) {
223
- return null;
224
- }
225
- if (matches[1] === undefined) {
226
- // No captures
227
- return matches[0];
228
- }
229
-
230
- matches.shift();
231
- return new LuaMultiRes(matches);
126
+ const caps = patternMatch(s, pattern, init);
127
+ if (!caps) return null;
128
+ return capturesToLua(caps);
232
129
  },
233
130
  ),
234
131
  rep: new LuaBuiltinFunction((_sf, s: string, n: number, sep?: string) => {
132
+ if (n <= 0) return "";
235
133
  sep = sep ?? "";
236
- return s.repeat(n) + sep;
134
+ const parts: string[] = [];
135
+ for (let i = 0; i < n; i++) {
136
+ parts.push(s);
137
+ }
138
+ return parts.join(sep);
237
139
  }),
238
140
  reverse: new LuaBuiltinFunction((_sf, s: string) => {
239
141
  return s.split("").reverse().join("");
240
142
  }),
241
143
  sub: new LuaBuiltinFunction((_sf, s: string, i: number, j?: number) => {
242
- j = j ?? s.length;
243
- if (i < 0) {
244
- i = s.length + i + 1;
144
+ const len = s.length;
145
+ let start: number;
146
+ if (i > 0) {
147
+ start = i;
148
+ } else if (i < -len) {
149
+ start = 1;
150
+ } else {
151
+ start = i === 0 ? 1 : len + i + 1;
245
152
  }
246
- if (j < 0) {
247
- j = s.length + j + 1;
153
+ let end: number;
154
+ if (j === undefined || j === null || j > len) {
155
+ end = len;
156
+ } else if (j >= 0) {
157
+ end = j;
158
+ } else if (j < -len) {
159
+ end = 0;
160
+ } else {
161
+ end = len + j + 1;
248
162
  }
249
- return s.slice(i - 1, j);
163
+ if (start <= end) {
164
+ return s.substring(start - 1, end);
165
+ }
166
+ return "";
250
167
  }),
168
+
251
169
  split: new LuaBuiltinFunction((_sf, s: string, sep: string) => {
252
170
  return s.split(sep);
253
171
  }),
254
172
 
255
- // Non-standard
173
+ pack: strPackFn,
174
+ unpack: strUnpackFn,
175
+ packsize: strPackSizeFn,
176
+
177
+ // Non-standard extensions
256
178
  startsWith: new LuaBuiltinFunction((_sf, s: string, prefix: string) => {
257
179
  return s.startsWith(prefix);
258
180
  }),