@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.
- package/README.md +4 -5
- package/client/asset_bundle/bundle.ts +3 -9
- package/client/data/datastore.ts +4 -5
- package/client/markdown_parser/constants.ts +3 -2
- package/client/plugos/hooks/code_widget.ts +3 -5
- package/client/plugos/hooks/command.ts +8 -8
- package/client/plugos/hooks/document_editor.ts +10 -12
- package/client/plugos/hooks/event.ts +33 -36
- package/client/plugos/hooks/mq.ts +17 -17
- package/client/plugos/hooks/plug_namespace.ts +3 -5
- package/client/plugos/hooks/slash_command.ts +12 -27
- package/client/plugos/hooks/syscall.ts +3 -3
- package/client/plugos/manifest_cache.ts +22 -15
- package/client/plugos/plug.ts +2 -5
- package/client/plugos/plug_compile.ts +67 -65
- package/client/plugos/protocol.ts +28 -28
- package/client/plugos/proxy_fetch.ts +7 -6
- package/client/plugos/sandboxes/worker_sandbox.ts +16 -15
- package/client/plugos/syscalls/asset.ts +1 -3
- package/client/plugos/syscalls/code_widget.ts +1 -3
- package/client/plugos/syscalls/config.ts +1 -5
- package/client/plugos/syscalls/datastore.ts +1 -1
- package/client/plugos/syscalls/editor.ts +63 -60
- package/client/plugos/syscalls/event.ts +9 -12
- package/client/plugos/syscalls/fetch.ts +30 -22
- package/client/plugos/syscalls/index.ts +10 -1
- package/client/plugos/syscalls/jsonschema.ts +72 -32
- package/client/plugos/syscalls/language.ts +9 -5
- package/client/plugos/syscalls/markdown.ts +29 -7
- package/client/plugos/syscalls/mq.ts +3 -11
- package/client/plugos/syscalls/service_registry.ts +1 -4
- package/client/plugos/syscalls/shell.ts +2 -5
- package/client/plugos/syscalls/sync.ts +69 -60
- package/client/plugos/syscalls/system.ts +2 -3
- package/client/plugos/system.ts +4 -10
- package/client/plugos/worker_runtime.ts +4 -3
- package/client/space_lua/aggregates.ts +632 -59
- package/client/space_lua/ast.ts +21 -9
- package/client/space_lua/ast_narrow.ts +4 -2
- package/client/space_lua/eval.ts +842 -536
- package/client/space_lua/labels.ts +6 -11
- package/client/space_lua/liq_null.ts +6 -0
- package/client/space_lua/numeric.ts +5 -8
- package/client/space_lua/parse.ts +290 -169
- package/client/space_lua/query_collection.ts +213 -149
- package/client/space_lua/render_lua_markdown.ts +369 -0
- package/client/space_lua/rp.ts +5 -4
- package/client/space_lua/runtime.ts +245 -142
- package/client/space_lua/stdlib/format.ts +34 -20
- package/client/space_lua/stdlib/js.ts +3 -7
- package/client/space_lua/stdlib/load.ts +1 -3
- package/client/space_lua/stdlib/math.ts +15 -14
- package/client/space_lua/stdlib/net.ts +25 -15
- package/client/space_lua/stdlib/os.ts +76 -85
- package/client/space_lua/stdlib/pattern.ts +28 -35
- package/client/space_lua/stdlib/prng.ts +15 -12
- package/client/space_lua/stdlib/space_lua.ts +16 -17
- package/client/space_lua/stdlib/string.ts +7 -17
- package/client/space_lua/stdlib/string_pack.ts +23 -19
- package/client/space_lua/stdlib/table.ts +5 -9
- package/client/space_lua/stdlib.ts +20 -30
- package/client/space_lua/tonumber.ts +79 -40
- package/client/space_lua/util.ts +14 -10
- package/dist/plug-compile.js +44 -41
- package/package.json +24 -22
- package/plug-api/lib/async.ts +19 -6
- package/plug-api/lib/crypto.ts +5 -6
- package/plug-api/lib/dates.ts +15 -7
- package/plug-api/lib/json.ts +10 -4
- package/plug-api/lib/ref.ts +18 -18
- package/plug-api/lib/resolve.ts +7 -11
- package/plug-api/lib/tags.ts +13 -4
- package/plug-api/lib/transclusion.ts +6 -17
- package/plug-api/lib/tree.ts +115 -43
- package/plug-api/lib/yaml.ts +25 -15
- package/plug-api/syscalls/asset.ts +1 -1
- package/plug-api/syscalls/config.ts +1 -4
- package/plug-api/syscalls/editor.ts +14 -14
- package/plug-api/syscalls/jsonschema.ts +1 -3
- package/plug-api/syscalls/lua.ts +3 -9
- package/plug-api/syscalls/mq.ts +1 -4
- package/plug-api/syscalls/shell.ts +4 -1
- package/plug-api/syscalls/space.ts +3 -10
- package/plug-api/syscalls/system.ts +1 -4
- package/plug-api/syscalls/yaml.ts +2 -6
- package/plug-api/types/client.ts +16 -1
- package/plug-api/types/event.ts +6 -4
- package/plug-api/types/manifest.ts +8 -9
- package/plugs/builtin_plugs.ts +2 -2
- 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 &&
|
|
115
|
+
if (numPad && flags & FLAG_ZERO && !(flags & FLAG_MINUS)) {
|
|
113
116
|
let signLen = 0;
|
|
114
|
-
if (s.charCodeAt(0) === 45 || s.charCodeAt(0) === 43) {
|
|
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) {
|
|
242
|
+
if (lower === 102) {
|
|
243
|
+
// 'f'
|
|
239
244
|
body = abs.toFixed(prec);
|
|
240
|
-
} else if (lower === 101) {
|
|
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 {
|
|
245
|
-
|
|
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 (
|
|
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 (
|
|
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) {
|
|
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) &
|
|
440
|
-
const frac = bits &
|
|
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) {
|
|
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) {
|
|
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: {
|
|
757
|
+
case 112: {
|
|
758
|
+
// 'p'
|
|
747
759
|
out += formatPointer(args[ai++], spec);
|
|
748
760
|
break;
|
|
749
761
|
}
|
|
750
|
-
case 113: {
|
|
762
|
+
case 113: {
|
|
763
|
+
// 'q'
|
|
751
764
|
out += formatQ(args[ai++]);
|
|
752
765
|
break;
|
|
753
766
|
}
|
|
754
|
-
case 115: {
|
|
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
|
-
|
|
17
|
-
|
|
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))
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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 &&
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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 =
|
|
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 (
|
|
48
|
-
body =
|
|
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 =
|
|
62
|
+
body = new TextDecoder().decode(rawBytes);
|
|
54
63
|
} else {
|
|
55
|
-
body =
|
|
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
|
|
89
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
-
|
|
167
|
+
A: () =>
|
|
176
168
|
d.toLocaleString("en-US", {
|
|
177
169
|
weekday: "long",
|
|
178
170
|
...(utc ? { timeZone: "UTC" } : {}),
|
|
179
171
|
}),
|
|
180
|
-
|
|
172
|
+
a: () =>
|
|
181
173
|
d.toLocaleString("en-US", {
|
|
182
174
|
weekday: "short",
|
|
183
175
|
...(utc ? { timeZone: "UTC" } : {}),
|
|
184
176
|
}),
|
|
185
|
-
|
|
186
|
-
|
|
177
|
+
w: () => dow().toString(),
|
|
178
|
+
u: () => (dow() === 0 ? 7 : dow()).toString(),
|
|
187
179
|
|
|
188
180
|
// Month name
|
|
189
|
-
|
|
181
|
+
b: () =>
|
|
190
182
|
d.toLocaleString("en-US", {
|
|
191
183
|
month: "short",
|
|
192
184
|
...(utc ? { timeZone: "UTC" } : {}),
|
|
193
185
|
}),
|
|
194
|
-
|
|
186
|
+
h: () =>
|
|
195
187
|
d.toLocaleString("en-US", {
|
|
196
188
|
month: "short",
|
|
197
189
|
...(utc ? { timeZone: "UTC" } : {}),
|
|
198
190
|
}),
|
|
199
|
-
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
205
|
+
c: () =>
|
|
214
206
|
d.toLocaleString("en-US", {
|
|
215
207
|
...(utc ? { timeZone: "UTC" } : {}),
|
|
216
208
|
}),
|
|
217
|
-
|
|
209
|
+
x: () =>
|
|
218
210
|
d.toLocaleDateString("en-US", {
|
|
219
211
|
...(utc ? { timeZone: "UTC" } : {}),
|
|
220
212
|
}),
|
|
221
|
-
|
|
213
|
+
X: () =>
|
|
222
214
|
d.toLocaleTimeString("en-US", {
|
|
223
215
|
...(utc ? { timeZone: "UTC" } : {}),
|
|
224
216
|
}),
|
|
225
|
-
|
|
217
|
+
D: () =>
|
|
226
218
|
`${pad2(mo(d, utc) + 1)}/${pad2(da(d, utc))}/${pad2(yr(d, utc) % 100)}`,
|
|
227
|
-
|
|
219
|
+
F: () =>
|
|
228
220
|
`${yr(d, utc).toString()}-${pad2(mo(d, utc) + 1)}-${pad2(da(d, utc))}`,
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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
|
-
|
|
229
|
+
s: () => Math.floor(d.getTime() / 1000).toString(),
|
|
238
230
|
|
|
239
231
|
// Whitespace
|
|
240
|
-
|
|
241
|
-
|
|
232
|
+
n: () => "\n",
|
|
233
|
+
t: () => "\t",
|
|
242
234
|
|
|
243
235
|
// Timezone
|
|
244
|
-
|
|
236
|
+
Z: () => {
|
|
245
237
|
if (utc) return "UTC";
|
|
246
238
|
const match = d.toTimeString().match(/\((.*)\)/);
|
|
247
239
|
return match ? match[1] : "";
|
|
248
240
|
},
|
|
249
|
-
|
|
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
|
-
|
|
349
|
-
|
|
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
|
-
|
|
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
|
-
|
|
362
|
-
|
|
363
|
-
|
|
353
|
+
if (fmt === "*t") {
|
|
354
|
+
return dateTable(d, utc);
|
|
355
|
+
}
|
|
364
356
|
|
|
365
|
-
|
|
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 => {
|