@silverbulletmd/silverbullet 2.5.3 → 2.6.1

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 (90) hide show
  1. package/README.md +4 -5
  2. package/client/asset_bundle/bundle.ts +3 -9
  3. package/client/data/datastore.ts +4 -5
  4. package/client/markdown_parser/constants.ts +3 -2
  5. package/client/plugos/hooks/code_widget.ts +3 -5
  6. package/client/plugos/hooks/command.ts +8 -8
  7. package/client/plugos/hooks/document_editor.ts +10 -12
  8. package/client/plugos/hooks/event.ts +33 -36
  9. package/client/plugos/hooks/mq.ts +17 -17
  10. package/client/plugos/hooks/plug_namespace.ts +3 -5
  11. package/client/plugos/hooks/slash_command.ts +12 -27
  12. package/client/plugos/hooks/syscall.ts +3 -3
  13. package/client/plugos/manifest_cache.ts +22 -15
  14. package/client/plugos/plug.ts +2 -5
  15. package/client/plugos/plug_compile.ts +67 -65
  16. package/client/plugos/protocol.ts +28 -28
  17. package/client/plugos/proxy_fetch.ts +7 -6
  18. package/client/plugos/sandboxes/worker_sandbox.ts +16 -15
  19. package/client/plugos/syscalls/asset.ts +1 -3
  20. package/client/plugos/syscalls/code_widget.ts +1 -3
  21. package/client/plugos/syscalls/config.ts +1 -5
  22. package/client/plugos/syscalls/datastore.ts +1 -1
  23. package/client/plugos/syscalls/editor.ts +63 -60
  24. package/client/plugos/syscalls/event.ts +9 -12
  25. package/client/plugos/syscalls/fetch.ts +30 -22
  26. package/client/plugos/syscalls/index.ts +10 -1
  27. package/client/plugos/syscalls/jsonschema.ts +72 -32
  28. package/client/plugos/syscalls/language.ts +9 -5
  29. package/client/plugos/syscalls/markdown.ts +29 -7
  30. package/client/plugos/syscalls/mq.ts +3 -11
  31. package/client/plugos/syscalls/service_registry.ts +1 -4
  32. package/client/plugos/syscalls/shell.ts +2 -5
  33. package/client/plugos/syscalls/sync.ts +69 -60
  34. package/client/plugos/syscalls/system.ts +2 -3
  35. package/client/plugos/system.ts +4 -10
  36. package/client/plugos/worker_runtime.ts +4 -3
  37. package/client/space_lua/aggregates.ts +632 -59
  38. package/client/space_lua/ast.ts +21 -9
  39. package/client/space_lua/ast_narrow.ts +4 -2
  40. package/client/space_lua/eval.ts +842 -536
  41. package/client/space_lua/labels.ts +6 -11
  42. package/client/space_lua/liq_null.ts +6 -0
  43. package/client/space_lua/numeric.ts +5 -8
  44. package/client/space_lua/parse.ts +290 -169
  45. package/client/space_lua/query_collection.ts +213 -149
  46. package/client/space_lua/render_lua_markdown.ts +369 -0
  47. package/client/space_lua/rp.ts +5 -4
  48. package/client/space_lua/runtime.ts +245 -142
  49. package/client/space_lua/stdlib/format.ts +34 -20
  50. package/client/space_lua/stdlib/js.ts +3 -7
  51. package/client/space_lua/stdlib/load.ts +1 -3
  52. package/client/space_lua/stdlib/math.ts +15 -14
  53. package/client/space_lua/stdlib/net.ts +25 -15
  54. package/client/space_lua/stdlib/os.ts +76 -85
  55. package/client/space_lua/stdlib/pattern.ts +28 -35
  56. package/client/space_lua/stdlib/prng.ts +15 -12
  57. package/client/space_lua/stdlib/space_lua.ts +16 -17
  58. package/client/space_lua/stdlib/string.ts +7 -17
  59. package/client/space_lua/stdlib/string_pack.ts +23 -19
  60. package/client/space_lua/stdlib/table.ts +5 -9
  61. package/client/space_lua/stdlib.ts +20 -30
  62. package/client/space_lua/tonumber.ts +79 -40
  63. package/client/space_lua/util.ts +14 -10
  64. package/dist/plug-compile.js +44 -41
  65. package/package.json +24 -22
  66. package/plug-api/lib/async.ts +19 -6
  67. package/plug-api/lib/crypto.ts +5 -6
  68. package/plug-api/lib/dates.ts +15 -7
  69. package/plug-api/lib/json.ts +10 -4
  70. package/plug-api/lib/ref.ts +18 -18
  71. package/plug-api/lib/resolve.ts +7 -11
  72. package/plug-api/lib/tags.ts +13 -4
  73. package/plug-api/lib/transclusion.ts +6 -17
  74. package/plug-api/lib/tree.ts +115 -43
  75. package/plug-api/lib/yaml.ts +25 -15
  76. package/plug-api/syscalls/asset.ts +1 -1
  77. package/plug-api/syscalls/config.ts +1 -4
  78. package/plug-api/syscalls/editor.ts +14 -14
  79. package/plug-api/syscalls/jsonschema.ts +1 -3
  80. package/plug-api/syscalls/lua.ts +3 -9
  81. package/plug-api/syscalls/mq.ts +1 -4
  82. package/plug-api/syscalls/shell.ts +4 -1
  83. package/plug-api/syscalls/space.ts +3 -10
  84. package/plug-api/syscalls/system.ts +1 -4
  85. package/plug-api/syscalls/yaml.ts +2 -6
  86. package/plug-api/types/client.ts +16 -1
  87. package/plug-api/types/event.ts +6 -4
  88. package/plug-api/types/manifest.ts +8 -9
  89. package/plugs/builtin_plugs.ts +2 -2
  90. package/dist/worker_runtime_bundle.js +0 -233
@@ -58,7 +58,8 @@ function parseSpec(
58
58
 
59
59
  // Parse width
60
60
  let width = 0;
61
- if (i < len && fmt.charCodeAt(i) === 42) { // '*'
61
+ if (i < len && fmt.charCodeAt(i) === 42) {
62
+ // '*'
62
63
  width = -1;
63
64
  i++;
64
65
  } else {
@@ -71,10 +72,12 @@ function parseSpec(
71
72
  // Parse precision
72
73
  let hasPrec = false;
73
74
  let prec = 0;
74
- if (i < len && fmt.charCodeAt(i) === 46) { // '.'
75
+ if (i < len && fmt.charCodeAt(i) === 46) {
76
+ // '.'
75
77
  hasPrec = true;
76
78
  i++;
77
- if (i < len && fmt.charCodeAt(i) === 42) { // '*'
79
+ if (i < len && fmt.charCodeAt(i) === 42) {
80
+ // '*'
78
81
  prec = -1;
79
82
  i++;
80
83
  } else {
@@ -109,9 +112,10 @@ function parseSpec(
109
112
  function pad(s: string, width: number, flags: number, numPad: boolean): string {
110
113
  if (width <= 0 || s.length >= width) return s;
111
114
  const n = width - s.length;
112
- if (numPad && (flags & FLAG_ZERO) && !(flags & FLAG_MINUS)) {
115
+ if (numPad && flags & FLAG_ZERO && !(flags & FLAG_MINUS)) {
113
116
  let signLen = 0;
114
- if (s.charCodeAt(0) === 45 || s.charCodeAt(0) === 43) { // '-' or '+'
117
+ if (s.charCodeAt(0) === 45 || s.charCodeAt(0) === 43) {
118
+ // '-' or '+'
115
119
  signLen = 1;
116
120
  } else if (
117
121
  s.charCodeAt(0) === 48 &&
@@ -235,14 +239,17 @@ function formatFloat(n: number, spec: FormatSpec): string {
235
239
 
236
240
  let body: string;
237
241
 
238
- if (lower === 102) { // 'f'
242
+ if (lower === 102) {
243
+ // 'f'
239
244
  body = abs.toFixed(prec);
240
- } else if (lower === 101) { // 'e'
245
+ } else if (lower === 101) {
246
+ // 'e'
241
247
  body = abs.toExponential(prec);
242
248
  // Ensure exponent has at least 2 digits
243
249
  body = ensureExpTwoDigits(body);
244
- } else { // 'g'
245
- const gPrec = (prec === 0) ? 1 : prec;
250
+ } else {
251
+ // 'g'
252
+ const gPrec = prec === 0 ? 1 : prec;
246
253
  if (abs === 0) {
247
254
  body = "0";
248
255
  } else {
@@ -268,7 +275,7 @@ function formatFloat(n: number, spec: FormatSpec): string {
268
275
  }
269
276
 
270
277
  // Alt flag for 'f'/'e': ensure decimal point exists
271
- if ((spec.flags & FLAG_HASH) && lower !== 103) {
278
+ if (spec.flags & FLAG_HASH && lower !== 103) {
272
279
  if (body.indexOf(".") === -1) {
273
280
  // Insert dot before 'e' if present, else append
274
281
  const eIdx = body.indexOf("e");
@@ -283,7 +290,7 @@ function formatFloat(n: number, spec: FormatSpec): string {
283
290
  }
284
291
 
285
292
  // Alt flag for 'g': keep trailing zeros but ensure decimal point
286
- if ((spec.flags & FLAG_HASH) && lower === 103) {
293
+ if (spec.flags & FLAG_HASH && lower === 103) {
287
294
  if (body.indexOf(".") === -1) {
288
295
  const expIdx = findExpIndex(body);
289
296
  if (expIdx !== -1) {
@@ -337,7 +344,8 @@ function stripTrailingZerosG(s: string): string {
337
344
  if (dotIdx === -1) return s; // nothing to strip
338
345
 
339
346
  let end = mantissa.length;
340
- while (end > dotIdx + 1 && mantissa.charCodeAt(end - 1) === 48) { // '0'
347
+ while (end > dotIdx + 1 && mantissa.charCodeAt(end - 1) === 48) {
348
+ // '0'
341
349
  end--;
342
350
  }
343
351
  // Remove dot if nothing after it
@@ -389,7 +397,8 @@ function formatHexFloat(n: number, spec: FormatSpec): string {
389
397
  if (pIdx !== -1) {
390
398
  let hasDot = false;
391
399
  for (let k = 0; k < pIdx; k++) {
392
- if (body.charCodeAt(k) === 46) { // '.'
400
+ if (body.charCodeAt(k) === 46) {
401
+ // '.'
393
402
  hasDot = true;
394
403
  break;
395
404
  }
@@ -436,8 +445,8 @@ function hexFloatBody(abs: number, spec: FormatSpec): string {
436
445
  const view = new DataView(buf.buffer);
437
446
  view.setFloat64(0, abs);
438
447
  const bits = view.getBigUint64(0);
439
- const biasedExp = Number((bits >> 52n) & 0x7FFn);
440
- const frac = bits & 0xFFFFFFFFFFFFFn;
448
+ const biasedExp = Number((bits >> 52n) & 0x7ffn);
449
+ const frac = bits & 0xfffffffffffffn;
441
450
 
442
451
  let exponent: number;
443
452
  let mantBits: bigint;
@@ -557,7 +566,8 @@ function padHexRight(s: string, len: number): string {
557
566
 
558
567
  function stripHexTrailingZeros(s: string): string {
559
568
  let end = s.length;
560
- while (end > 0 && s.charCodeAt(end - 1) === 48) { // '0'
569
+ while (end > 0 && s.charCodeAt(end - 1) === 48) {
570
+ // '0'
561
571
  end--;
562
572
  }
563
573
  if (end === s.length) return s;
@@ -669,7 +679,8 @@ export function luaFormat(fmt: string, ...args: any[]): string {
669
679
 
670
680
  while (i < len) {
671
681
  const c = fmt.charCodeAt(i);
672
- if (c !== 37) { // not '%'
682
+ if (c !== 37) {
683
+ // not '%'
673
684
  // Fast path: scan for next '%' or end
674
685
  let j = i + 1;
675
686
  while (j < len && fmt.charCodeAt(j) !== 37) j++;
@@ -743,15 +754,18 @@ export function luaFormat(fmt: string, ...args: any[]): string {
743
754
  false,
744
755
  );
745
756
  break;
746
- case 112: { // 'p'
757
+ case 112: {
758
+ // 'p'
747
759
  out += formatPointer(args[ai++], spec);
748
760
  break;
749
761
  }
750
- case 113: { // 'q'
762
+ case 113: {
763
+ // 'q'
751
764
  out += formatQ(args[ai++]);
752
765
  break;
753
766
  }
754
- case 115: { // 's'
767
+ case 115: {
768
+ // 's'
755
769
  let s = String(args[ai++]);
756
770
  if (spec.hasPrec && s.length > spec.prec) {
757
771
  s = s.slice(0, spec.prec);
@@ -12,13 +12,9 @@ export const jsApi = new LuaTable({
12
12
  * @param args - The arguments to pass to the constructor.
13
13
  * @returns The new instance.
14
14
  */
15
- new: new LuaBuiltinFunction(
16
- (sf, constructorFn: any, ...args) => {
17
- return new constructorFn(
18
- ...args.map((v) => luaValueToJS(v, sf)),
19
- );
20
- },
21
- ),
15
+ new: new LuaBuiltinFunction((sf, constructorFn: any, ...args) => {
16
+ return new constructorFn(...args.map((v) => luaValueToJS(v, sf)));
17
+ }),
22
18
  /**
23
19
  * Imports a JavaScript module.
24
20
  * @param url - The URL of the module to import.
@@ -18,9 +18,7 @@ export function luaLoad(code: LuaValue, sf: LuaStackFrame): LuaValue {
18
18
 
19
19
  // Be vocal when no _GLOBAL is set
20
20
  if (!globalEnvMaybe) {
21
- console.warn(
22
- "load() called without _GLOBAL in thread-local environment",
23
- );
21
+ console.warn("load() called without _GLOBAL in thread-local environment");
24
22
  return new LuaMultiRes([null, "Global environment not set"]);
25
23
  }
26
24
 
@@ -60,7 +60,8 @@ export const mathApi = new LuaTable({
60
60
  }
61
61
  if (typeof x === "string") {
62
62
  const n = untagNumber(x); // Number(x) coerces the string
63
- if (Number.isNaN(n) || !Number.isFinite(n) || !Number.isInteger(n)) return null;
63
+ if (Number.isNaN(n) || !Number.isFinite(n) || !Number.isInteger(n))
64
+ return null;
64
65
  return n;
65
66
  }
66
67
  return null;
@@ -100,15 +101,15 @@ export const mathApi = new LuaTable({
100
101
  ceil: new LuaBuiltinFunction((_sf, x: number) => Math.ceil(untagNumber(x))),
101
102
  floor: new LuaBuiltinFunction((_sf, x: number) => Math.floor(untagNumber(x))),
102
103
  max: new LuaBuiltinFunction((_sf, ...args: number[]) =>
103
- Math.max(...args.map(untagNumber))
104
+ Math.max(...args.map(untagNumber)),
104
105
  ),
105
106
  min: new LuaBuiltinFunction((_sf, ...args: number[]) =>
106
- Math.min(...args.map(untagNumber))
107
+ Math.min(...args.map(untagNumber)),
107
108
  ),
108
109
 
109
110
  // Rounding and remainder
110
- fmod: new LuaBuiltinFunction((_sf, x: number, y: number) =>
111
- untagNumber(x) % untagNumber(y)
111
+ fmod: new LuaBuiltinFunction(
112
+ (_sf, x: number, y: number) => untagNumber(x) % untagNumber(y),
112
113
  ),
113
114
  modf: new LuaBuiltinFunction((_sf, x: number) => {
114
115
  const xn = untagNumber(x);
@@ -141,8 +142,8 @@ export const mathApi = new LuaTable({
141
142
  }),
142
143
 
143
144
  // Returns m * 2^e (the inverse of frexp). Mirrors C99/Lua.
144
- ldexp: new LuaBuiltinFunction((_sf, m: number, e: number) =>
145
- untagNumber(m) * 2 ** untagNumber(e)
145
+ ldexp: new LuaBuiltinFunction(
146
+ (_sf, m: number, e: number) => untagNumber(m) * 2 ** untagNumber(e),
146
147
  ),
147
148
 
148
149
  // Power and logarithms
@@ -154,8 +155,8 @@ export const mathApi = new LuaTable({
154
155
  return Math.log(untagNumber(x)) / Math.log(untagNumber(base));
155
156
  }),
156
157
  // Power function (deprecated in Lua 5.4 but retained for compatibility)
157
- pow: new LuaBuiltinFunction((_sf, x: number, y: number) =>
158
- untagNumber(x) ** untagNumber(y)
158
+ pow: new LuaBuiltinFunction(
159
+ (_sf, x: number, y: number) => untagNumber(x) ** untagNumber(y),
159
160
  ),
160
161
  sqrt: new LuaBuiltinFunction((_sf, x: number) => Math.sqrt(untagNumber(x))),
161
162
 
@@ -178,14 +179,14 @@ export const mathApi = new LuaTable({
178
179
  tanh: new LuaBuiltinFunction((_sf, x: number) => Math.tanh(untagNumber(x))),
179
180
 
180
181
  // Additional utility
181
- deg: new LuaBuiltinFunction((_sf, x: number) =>
182
- untagNumber(x) * 180 / Math.PI
182
+ deg: new LuaBuiltinFunction(
183
+ (_sf, x: number) => (untagNumber(x) * 180) / Math.PI,
183
184
  ),
184
- rad: new LuaBuiltinFunction((_sf, x: number) =>
185
- untagNumber(x) * Math.PI / 180
185
+ rad: new LuaBuiltinFunction(
186
+ (_sf, x: number) => (untagNumber(x) * Math.PI) / 180,
186
187
  ),
187
188
  ult: new LuaBuiltinFunction((_sf, m: number, n: number) => {
188
- return (untagNumber(m) >>> 0) < (untagNumber(n) >>> 0);
189
+ return untagNumber(m) >>> 0 < untagNumber(n) >>> 0;
189
190
  }),
190
191
 
191
192
  // Keep the cosineSimilarity utility function
@@ -14,17 +14,18 @@ export const netApi = new LuaTable({
14
14
  ): Promise<ProxyFetchResponse> => {
15
15
  // JSONify any non-serializable body
16
16
  if (
17
- options?.body && typeof options.body !== "string" &&
17
+ options?.body &&
18
+ typeof options.body !== "string" &&
18
19
  !(options.body instanceof Uint8Array)
19
20
  ) {
20
21
  options.body = JSON.stringify(options.body);
21
22
  }
22
23
  const fetchOptions = options
23
24
  ? {
24
- method: options.method,
25
- headers: {} as Record<string, string>,
26
- body: options.body,
27
- }
25
+ method: options.method,
26
+ headers: {} as Record<string, string>,
27
+ body: options.body,
28
+ }
28
29
  : {};
29
30
  fetchOptions.headers = buildProxyHeaders(options.headers);
30
31
  const resp = await client.httpSpacePrimitives.authenticatedFetch(
@@ -40,19 +41,27 @@ export const netApi = new LuaTable({
40
41
  };
41
42
  }
42
43
  // Do sensible things with the body based on the content type
44
+ // Read as ArrayBuffer first to safely handle empty responses (e.g.
45
+ // PUT/DELETE returning 204 with Content-Type: application/json).
46
+ // resp.arrayBuffer() never throws on an empty body, whereas
47
+ // resp.json() would throw a SyntaxError.
48
+ const rawBytes = new Uint8Array(await resp.arrayBuffer());
43
49
  let body: any;
44
- const contentTypeHeader = options.responseEncoding ||
50
+ const contentTypeHeader =
51
+ options.responseEncoding ||
45
52
  resp.headers.get("x-proxy-header-content-type");
46
53
  const statusCode = +(resp.headers.get("x-proxy-status-code") || "200");
47
- if (contentTypeHeader?.startsWith("application/json")) {
48
- body = await resp.json();
54
+ if (rawBytes.length === 0) {
55
+ body = null;
56
+ } else if (contentTypeHeader?.startsWith("application/json")) {
57
+ body = JSON.parse(new TextDecoder().decode(rawBytes));
49
58
  } else if (
50
59
  contentTypeHeader?.startsWith("application/xml") ||
51
60
  contentTypeHeader?.startsWith("text/")
52
61
  ) {
53
- body = await resp.text();
62
+ body = new TextDecoder().decode(rawBytes);
54
63
  } else {
55
- body = new Uint8Array(await resp.arrayBuffer());
64
+ body = rawBytes;
56
65
  }
57
66
  return {
58
67
  ok: resp.ok,
@@ -85,8 +94,11 @@ export const netApi = new LuaTable({
85
94
  function buildProxyUrl(client: Client, url: string) {
86
95
  url = url.replace(/^https?:\/\//, "");
87
96
  // Strip off the /.fs and replace with /.proxy
88
- return client.httpSpacePrimitives.url.slice(0, -fsEndpoint.length) +
89
- "/.proxy/" + url;
97
+ return (
98
+ client.httpSpacePrimitives.url.slice(0, -fsEndpoint.length) +
99
+ "/.proxy/" +
100
+ url
101
+ );
90
102
  }
91
103
 
92
104
  function buildProxyHeaders(headers?: Record<string, any>): Record<string, any> {
@@ -100,9 +112,7 @@ function buildProxyHeaders(headers?: Record<string, any>): Record<string, any> {
100
112
  return newHeaders;
101
113
  }
102
114
 
103
- function extractProxyHeaders(
104
- headers: Headers,
105
- ): Record<string, any> {
115
+ function extractProxyHeaders(headers: Headers): Record<string, any> {
106
116
  const newHeaders: Record<string, any> = {};
107
117
  for (const [key, value] of headers.entries()) {
108
118
  if (key.toLowerCase().startsWith("x-proxy-header-")) {
@@ -16,9 +16,7 @@ function weekNumber(
16
16
 
17
17
  if (iso) {
18
18
  const target = new Date(date);
19
- target.setUTCDate(
20
- target.getUTCDate() + 3 - ((target.getUTCDay() + 6) % 7),
21
- );
19
+ target.setUTCDate(target.getUTCDate() + 3 - ((target.getUTCDay() + 6) % 7));
22
20
 
23
21
  const yearStart = new Date(Date.UTC(target.getUTCFullYear(), 0, 4));
24
22
  const weekStart = new Date(yearStart);
@@ -34,11 +32,10 @@ function weekNumber(
34
32
  const offset = (7 + (startDay - weekStartDay)) % 7;
35
33
  const firstWeekStart = new Date(yearStart);
36
34
 
37
- firstWeekStart.setUTCDate(yearStart.getUTCDate() + (7 - offset) % 7);
35
+ firstWeekStart.setUTCDate(yearStart.getUTCDate() + ((7 - offset) % 7));
38
36
 
39
37
  if (date < firstWeekStart) return 0;
40
- return 1 +
41
- Math.floor((date.getTime() - firstWeekStart.getTime()) / ONE_WEEK);
38
+ return 1 + Math.floor((date.getTime() - firstWeekStart.getTime()) / ONE_WEEK);
42
39
  }
43
40
 
44
41
  function dayOfYear(d: Date, utc: boolean): number {
@@ -47,25 +44,25 @@ function dayOfYear(d: Date, utc: boolean): number {
47
44
 
48
45
  const current = utc
49
46
  ? new Date(
50
- Date.UTC(
51
- year,
52
- d.getUTCMonth(),
53
- d.getUTCDate(),
54
- d.getUTCHours(),
55
- d.getUTCMinutes(),
56
- d.getUTCSeconds(),
57
- ),
58
- )
47
+ Date.UTC(
48
+ year,
49
+ d.getUTCMonth(),
50
+ d.getUTCDate(),
51
+ d.getUTCHours(),
52
+ d.getUTCMinutes(),
53
+ d.getUTCSeconds(),
54
+ ),
55
+ )
59
56
  : new Date(
60
- Date.UTC(
61
- year,
62
- d.getMonth(),
63
- d.getDate(),
64
- d.getHours(),
65
- d.getMinutes(),
66
- d.getSeconds(),
67
- ),
68
- );
57
+ Date.UTC(
58
+ year,
59
+ d.getMonth(),
60
+ d.getDate(),
61
+ d.getHours(),
62
+ d.getMinutes(),
63
+ d.getSeconds(),
64
+ ),
65
+ );
69
66
 
70
67
  return Math.floor((current.getTime() - start.getTime()) / ONE_DAY);
71
68
  }
@@ -76,9 +73,7 @@ function isoWeekYear(d: Date, utc: boolean): number {
76
73
  const day = utc ? d.getUTCDate() : d.getDate();
77
74
  const target = new Date(Date.UTC(year, month, day));
78
75
 
79
- target.setUTCDate(
80
- target.getUTCDate() + 3 - ((target.getUTCDay() + 6) % 7),
81
- );
76
+ target.setUTCDate(target.getUTCDate() + 3 - ((target.getUTCDay() + 6) % 7));
82
77
 
83
78
  return target.getUTCFullYear();
84
79
  }
@@ -138,7 +133,7 @@ function dateTable(d: Date, utc: boolean): LuaTable {
138
133
  });
139
134
 
140
135
  if (!utc) {
141
- tbl.rawSet("isdst", isDST(d));
136
+ void tbl.rawSet("isdst", isDST(d));
142
137
  }
143
138
 
144
139
  return tbl;
@@ -146,107 +141,104 @@ function dateTable(d: Date, utc: boolean): LuaTable {
146
141
 
147
142
  // Build the specifier map for a given `Date` and `utc` flag.
148
143
  // Returns a record mapping single-char specifier to its output string.
149
- function buildSpecMap(
150
- d: Date,
151
- utc: boolean,
152
- ): Record<string, () => string> {
144
+ function buildSpecMap(d: Date, utc: boolean): Record<string, () => string> {
153
145
  const h = () => hr(d, utc);
154
146
  const h12 = () => h() % 12 || 12;
155
147
  const dow = () => wd(d, utc);
156
148
 
157
149
  return {
158
150
  // Date
159
- "Y": () => yr(d, utc).toString(),
160
- "y": () => pad2(yr(d, utc) % 100),
161
- "C": () => pad2(Math.floor(yr(d, utc) / 100)),
162
- "m": () => pad2(mo(d, utc) + 1),
163
- "d": () => pad2(da(d, utc)),
164
- "e": () => da(d, utc).toString().padStart(2, " "),
165
- "j": () => pad3(dayOfYear(d, utc)),
151
+ Y: () => yr(d, utc).toString(),
152
+ y: () => pad2(yr(d, utc) % 100),
153
+ C: () => pad2(Math.floor(yr(d, utc) / 100)),
154
+ m: () => pad2(mo(d, utc) + 1),
155
+ d: () => pad2(da(d, utc)),
156
+ e: () => da(d, utc).toString().padStart(2, " "),
157
+ j: () => pad3(dayOfYear(d, utc)),
166
158
 
167
159
  // Time
168
- "H": () => pad2(h()),
169
- "I": () => pad2(h12()),
170
- "M": () => pad2(mi(d, utc)),
171
- "S": () => pad2(sc(d, utc)),
172
- "p": () => h() >= 12 ? "PM" : "AM",
160
+ H: () => pad2(h()),
161
+ I: () => pad2(h12()),
162
+ M: () => pad2(mi(d, utc)),
163
+ S: () => pad2(sc(d, utc)),
164
+ p: () => (h() >= 12 ? "PM" : "AM"),
173
165
 
174
166
  // Weekday
175
- "A": () =>
167
+ A: () =>
176
168
  d.toLocaleString("en-US", {
177
169
  weekday: "long",
178
170
  ...(utc ? { timeZone: "UTC" } : {}),
179
171
  }),
180
- "a": () =>
172
+ a: () =>
181
173
  d.toLocaleString("en-US", {
182
174
  weekday: "short",
183
175
  ...(utc ? { timeZone: "UTC" } : {}),
184
176
  }),
185
- "w": () => dow().toString(),
186
- "u": () => (dow() === 0 ? 7 : dow()).toString(),
177
+ w: () => dow().toString(),
178
+ u: () => (dow() === 0 ? 7 : dow()).toString(),
187
179
 
188
180
  // Month name
189
- "b": () =>
181
+ b: () =>
190
182
  d.toLocaleString("en-US", {
191
183
  month: "short",
192
184
  ...(utc ? { timeZone: "UTC" } : {}),
193
185
  }),
194
- "h": () =>
186
+ h: () =>
195
187
  d.toLocaleString("en-US", {
196
188
  month: "short",
197
189
  ...(utc ? { timeZone: "UTC" } : {}),
198
190
  }),
199
- "B": () =>
191
+ B: () =>
200
192
  d.toLocaleString("en-US", {
201
193
  month: "long",
202
194
  ...(utc ? { timeZone: "UTC" } : {}),
203
195
  }),
204
196
 
205
197
  // Week number
206
- "U": () => pad2(weekNumber(d, utc, 0, false)),
207
- "W": () => pad2(weekNumber(d, utc, 1, false)),
208
- "V": () => pad2(weekNumber(d, utc, 1, true)),
209
- "G": () => isoWeekYear(d, utc).toString(),
210
- "g": () => pad2(isoWeekYear(d, utc) % 100),
198
+ U: () => pad2(weekNumber(d, utc, 0, false)),
199
+ W: () => pad2(weekNumber(d, utc, 1, false)),
200
+ V: () => pad2(weekNumber(d, utc, 1, true)),
201
+ G: () => isoWeekYear(d, utc).toString(),
202
+ g: () => pad2(isoWeekYear(d, utc) % 100),
211
203
 
212
204
  // Composite specifiers
213
- "c": () =>
205
+ c: () =>
214
206
  d.toLocaleString("en-US", {
215
207
  ...(utc ? { timeZone: "UTC" } : {}),
216
208
  }),
217
- "x": () =>
209
+ x: () =>
218
210
  d.toLocaleDateString("en-US", {
219
211
  ...(utc ? { timeZone: "UTC" } : {}),
220
212
  }),
221
- "X": () =>
213
+ X: () =>
222
214
  d.toLocaleTimeString("en-US", {
223
215
  ...(utc ? { timeZone: "UTC" } : {}),
224
216
  }),
225
- "D": () =>
217
+ D: () =>
226
218
  `${pad2(mo(d, utc) + 1)}/${pad2(da(d, utc))}/${pad2(yr(d, utc) % 100)}`,
227
- "F": () =>
219
+ F: () =>
228
220
  `${yr(d, utc).toString()}-${pad2(mo(d, utc) + 1)}-${pad2(da(d, utc))}`,
229
- "R": () => `${pad2(h())}:${pad2(mi(d, utc))}`,
230
- "T": () => `${pad2(h())}:${pad2(mi(d, utc))}:${pad2(sc(d, utc))}`,
231
- "r": () =>
221
+ R: () => `${pad2(h())}:${pad2(mi(d, utc))}`,
222
+ T: () => `${pad2(h())}:${pad2(mi(d, utc))}:${pad2(sc(d, utc))}`,
223
+ r: () =>
232
224
  `${pad2(h12())}:${pad2(mi(d, utc))}:${pad2(sc(d, utc))} ${
233
225
  h() >= 12 ? "PM" : "AM"
234
226
  }`,
235
227
 
236
228
  // Epoch
237
- "s": () => Math.floor(d.getTime() / 1000).toString(),
229
+ s: () => Math.floor(d.getTime() / 1000).toString(),
238
230
 
239
231
  // Whitespace
240
- "n": () => "\n",
241
- "t": () => "\t",
232
+ n: () => "\n",
233
+ t: () => "\t",
242
234
 
243
235
  // Timezone
244
- "Z": () => {
236
+ Z: () => {
245
237
  if (utc) return "UTC";
246
238
  const match = d.toTimeString().match(/\((.*)\)/);
247
239
  return match ? match[1] : "";
248
240
  },
249
- "z": () => {
241
+ z: () => {
250
242
  if (utc) return "+0000";
251
243
  const offset = -d.getTimezoneOffset();
252
244
  const sign = offset >= 0 ? "+" : "-";
@@ -344,27 +336,26 @@ export const osApi = new LuaTable({
344
336
  // Otherwise, format specifiers follow ISO C `strftime`.
345
337
  //
346
338
  // If format is absent, it defaults to `%c`.
347
- date: new LuaBuiltinFunction(
348
- (_sf, format?: string, timestamp?: number) => {
349
- let fmt = format ?? "%c";
350
- let utc = false;
351
-
352
- if (fmt.startsWith("!")) {
353
- utc = true;
354
- fmt = fmt.slice(1);
355
- }
339
+ date: new LuaBuiltinFunction((_sf, format?: string, timestamp?: number) => {
340
+ let fmt = format ?? "%c";
341
+ let utc = false;
356
342
 
357
- const d = timestamp !== undefined && timestamp !== null
343
+ if (fmt.startsWith("!")) {
344
+ utc = true;
345
+ fmt = fmt.slice(1);
346
+ }
347
+
348
+ const d =
349
+ timestamp !== undefined && timestamp !== null
358
350
  ? new Date(timestamp * 1000)
359
351
  : new Date();
360
352
 
361
- if (fmt === "*t") {
362
- return dateTable(d, utc);
363
- }
353
+ if (fmt === "*t") {
354
+ return dateTable(d, utc);
355
+ }
364
356
 
365
- return luaFormatTime(fmt, d, utc);
366
- },
367
- ),
357
+ return luaFormatTime(fmt, d, utc);
358
+ }),
368
359
 
369
360
  // Returns an approximation of CPU time used by the program in seconds.
370
361
  clock: new LuaBuiltinFunction((_sf): number => {