@ai-ecoverse/slicc-kernel 1.106.3 → 1.107.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 +17 -1
- package/dist/kernel-worker.js +1050 -441
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +31 -6
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +856 -241
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +31 -6
- package/dist/process-worker.js.map +2 -2
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +2 -2
- package/dist/types/cron/schedule.d.ts +25 -0
- package/dist/types/cron/service.d.ts +68 -0
- package/dist/types/cron/table.d.ts +23 -0
- package/dist/types/launcher.d.ts +11 -0
- package/dist/types/node.d.ts +4 -0
- package/package.json +1 -1
package/dist/kernel-worker.js
CHANGED
|
@@ -19,9 +19,9 @@ var PathMap = class {
|
|
|
19
19
|
keys() {
|
|
20
20
|
return this.values.keys();
|
|
21
21
|
}
|
|
22
|
-
set(path,
|
|
22
|
+
set(path, value2) {
|
|
23
23
|
if (!this.values.has(path)) this.link(path);
|
|
24
|
-
this.values.set(path,
|
|
24
|
+
this.values.set(path, value2);
|
|
25
25
|
return this;
|
|
26
26
|
}
|
|
27
27
|
delete(path) {
|
|
@@ -136,9 +136,9 @@ var MemoryMeta = class {
|
|
|
136
136
|
function descendants(path) {
|
|
137
137
|
return IDBKeyRange.bound(`${path}/`, `${path}0`, false, true);
|
|
138
138
|
}
|
|
139
|
-
function settle(transaction,
|
|
139
|
+
function settle(transaction, value2) {
|
|
140
140
|
return new Promise((resolve2, reject) => {
|
|
141
|
-
transaction.oncomplete = () => resolve2(
|
|
141
|
+
transaction.oncomplete = () => resolve2(value2());
|
|
142
142
|
transaction.onabort = () => reject(transaction.error ?? new Error("metadata transaction aborted"));
|
|
143
143
|
});
|
|
144
144
|
}
|
|
@@ -834,8 +834,8 @@ var ProtectedFs = class {
|
|
|
834
834
|
for (let at = fs; at && at !== Object.prototype; at = Object.getPrototypeOf(at)) {
|
|
835
835
|
for (const name2 of Object.getOwnPropertyNames(at)) {
|
|
836
836
|
if (name2 === "constructor" || Object.hasOwn(view2, name2)) continue;
|
|
837
|
-
const
|
|
838
|
-
view2[name2] = typeof
|
|
837
|
+
const value2 = fs[name2];
|
|
838
|
+
view2[name2] = typeof value2 === "function" ? value2.bind(fs) : value2;
|
|
839
839
|
}
|
|
840
840
|
}
|
|
841
841
|
return Object.assign(view2, own);
|
|
@@ -965,8 +965,8 @@ function view(target, overrides) {
|
|
|
965
965
|
if (keys.has(prop)) return overrides[prop];
|
|
966
966
|
return Reflect.get(t, prop, receiver);
|
|
967
967
|
},
|
|
968
|
-
set(t, prop,
|
|
969
|
-
return Reflect.set(t, prop,
|
|
968
|
+
set(t, prop, value2) {
|
|
969
|
+
return Reflect.set(t, prop, value2);
|
|
970
970
|
},
|
|
971
971
|
getPrototypeOf(t) {
|
|
972
972
|
return Reflect.getPrototypeOf(t);
|
|
@@ -2160,9 +2160,9 @@ function memoryMedia() {
|
|
|
2160
2160
|
put: async (id2, handle) => void handles.set(id2, handle)
|
|
2161
2161
|
};
|
|
2162
2162
|
}
|
|
2163
|
-
function settle2(request2,
|
|
2163
|
+
function settle2(request2, value2) {
|
|
2164
2164
|
return new Promise((resolve2, reject) => {
|
|
2165
|
-
request2.onsuccess = () => resolve2(
|
|
2165
|
+
request2.onsuccess = () => resolve2(value2());
|
|
2166
2166
|
request2.onerror = () => reject(request2.error);
|
|
2167
2167
|
});
|
|
2168
2168
|
}
|
|
@@ -2718,12 +2718,12 @@ function userCred(uid, gid, groups = [gid]) {
|
|
|
2718
2718
|
function copyCred(cred) {
|
|
2719
2719
|
return { ...cred, groups: [...cred.groups] };
|
|
2720
2720
|
}
|
|
2721
|
-
function id(
|
|
2722
|
-
if (
|
|
2723
|
-
if (!Number.isInteger(
|
|
2724
|
-
throw fsError("EINVAL", `not a user or group id: ${String(
|
|
2721
|
+
function id(value2) {
|
|
2722
|
+
if (value2 === void 0 || value2 === -1 || value2 === 4294967295) return void 0;
|
|
2723
|
+
if (!Number.isInteger(value2) || value2 < 0 || value2 > MAX_ID) {
|
|
2724
|
+
throw fsError("EINVAL", `not a user or group id: ${String(value2)}`);
|
|
2725
2725
|
}
|
|
2726
|
-
return
|
|
2726
|
+
return value2;
|
|
2727
2727
|
}
|
|
2728
2728
|
function changeCred(cur, change) {
|
|
2729
2729
|
if (typeof change !== "object" || change === null || Array.isArray(change)) {
|
|
@@ -2734,10 +2734,10 @@ function changeCred(cur, change) {
|
|
|
2734
2734
|
for (const keys of [UIDS, GIDS]) {
|
|
2735
2735
|
const own = keys.map((key) => cur[key]);
|
|
2736
2736
|
for (const key of keys) {
|
|
2737
|
-
const
|
|
2738
|
-
if (
|
|
2739
|
-
if (!privileged && !own.includes(
|
|
2740
|
-
next3[key] =
|
|
2737
|
+
const value2 = id(change[key]);
|
|
2738
|
+
if (value2 === void 0) continue;
|
|
2739
|
+
if (!privileged && !own.includes(value2)) throw fsError("EPERM", `${key} ${value2}`);
|
|
2740
|
+
next3[key] = value2;
|
|
2741
2741
|
}
|
|
2742
2742
|
}
|
|
2743
2743
|
if (change.groups !== void 0) {
|
|
@@ -3348,11 +3348,11 @@ function ipv6Groups(text) {
|
|
|
3348
3348
|
} catch {
|
|
3349
3349
|
return void 0;
|
|
3350
3350
|
}
|
|
3351
|
-
const [head,
|
|
3351
|
+
const [head, tail2] = canonical.split("::");
|
|
3352
3352
|
const part = (s) => s ? s.split(":").map((h) => Number.parseInt(h, 16)) : [];
|
|
3353
3353
|
const front = part(head);
|
|
3354
|
-
const back = part(
|
|
3355
|
-
return
|
|
3354
|
+
const back = part(tail2);
|
|
3355
|
+
return tail2 === void 0 ? front : [...front, ...new Array(8 - front.length - back.length).fill(0), ...back];
|
|
3356
3356
|
}
|
|
3357
3357
|
function toBits(values, width) {
|
|
3358
3358
|
return values.reduce((acc, v) => acc << BigInt(width) | BigInt(v), 0n);
|
|
@@ -3834,8 +3834,8 @@ var KernelSocket = class _KernelSocket {
|
|
|
3834
3834
|
}
|
|
3835
3835
|
return this.options.get(`${level}:${name2}`) ?? 0;
|
|
3836
3836
|
}
|
|
3837
|
-
setOption(level, name2,
|
|
3838
|
-
this.options.set(`${level}:${name2}`,
|
|
3837
|
+
setOption(level, name2, value2) {
|
|
3838
|
+
this.options.set(`${level}:${name2}`, value2);
|
|
3839
3839
|
}
|
|
3840
3840
|
close() {
|
|
3841
3841
|
const was = this.state;
|
|
@@ -5668,15 +5668,15 @@ function concat(chunks, total = chunks.reduce((n, c) => n + c.length, 0)) {
|
|
|
5668
5668
|
}
|
|
5669
5669
|
return out;
|
|
5670
5670
|
}
|
|
5671
|
-
function safeValue(
|
|
5672
|
-
return !/[\r\n\0]/.test(
|
|
5671
|
+
function safeValue(value2) {
|
|
5672
|
+
return !/[\r\n\0]/.test(value2);
|
|
5673
5673
|
}
|
|
5674
5674
|
function responseHead(status, reason, headers) {
|
|
5675
5675
|
const phrase = /^[\t\x20-\x7e\x80-\xff]*$/.test(reason) ? reason : "";
|
|
5676
5676
|
let head = `HTTP/1.1 ${status} ${phrase}\r
|
|
5677
5677
|
`;
|
|
5678
|
-
for (const [name2,
|
|
5679
|
-
if (TOKEN.test(name2) && safeValue(
|
|
5678
|
+
for (const [name2, value2] of headers) {
|
|
5679
|
+
if (TOKEN.test(name2) && safeValue(value2)) head += `${name2}: ${value2}\r
|
|
5680
5680
|
`;
|
|
5681
5681
|
}
|
|
5682
5682
|
return latin1Bytes(`${head}\r
|
|
@@ -5989,12 +5989,12 @@ function localV4(a, b) {
|
|
|
5989
5989
|
return a === 127 || a === 0 || a === 169 && b === 254;
|
|
5990
5990
|
}
|
|
5991
5991
|
function hextets(address) {
|
|
5992
|
-
const [head,
|
|
5992
|
+
const [head, tail2, extra] = address.split("::");
|
|
5993
5993
|
if (extra !== void 0) return void 0;
|
|
5994
5994
|
const part = (text) => text ? text.split(":").map((h) => Number.parseInt(h, 16)) : [];
|
|
5995
5995
|
const front = part(head);
|
|
5996
|
-
const back = part(
|
|
5997
|
-
const groups =
|
|
5996
|
+
const back = part(tail2);
|
|
5997
|
+
const groups = tail2 === void 0 ? front : [...front, ...new Array(8 - front.length - back.length).fill(0), ...back];
|
|
5998
5998
|
return groups.length === 8 && groups.every((g) => g >= 0 && g <= 65535) ? groups : void 0;
|
|
5999
5999
|
}
|
|
6000
6000
|
function localV6(address) {
|
|
@@ -6551,8 +6551,8 @@ function name(dn) {
|
|
|
6551
6551
|
rdns.push(set(seq(oid("2.5.4.3"), utf8(dn.commonName))));
|
|
6552
6552
|
return seq(...rdns);
|
|
6553
6553
|
}
|
|
6554
|
-
function extension(id2, critical,
|
|
6555
|
-
return critical ? seq(oid(id2), TRUE, octets(
|
|
6554
|
+
function extension(id2, critical, value2) {
|
|
6555
|
+
return critical ? seq(oid(id2), TRUE, octets(value2)) : seq(oid(id2), octets(value2));
|
|
6556
6556
|
}
|
|
6557
6557
|
function children(der) {
|
|
6558
6558
|
const out = [];
|
|
@@ -6609,11 +6609,11 @@ function ipv6(host) {
|
|
|
6609
6609
|
if (!host.includes(":") || !/^[0-9A-Fa-f:.]+$/.test(host)) return void 0;
|
|
6610
6610
|
const v4 = /(\d{1,3}(?:\.\d{1,3}){3})$/.exec(host);
|
|
6611
6611
|
let text = host;
|
|
6612
|
-
const
|
|
6612
|
+
const tail2 = [];
|
|
6613
6613
|
if (v4) {
|
|
6614
6614
|
const bytes = ipv4(v4[1]);
|
|
6615
6615
|
if (!bytes) return void 0;
|
|
6616
|
-
|
|
6616
|
+
tail2.push(bytes[0] << 8 | bytes[1], bytes[2] << 8 | bytes[3]);
|
|
6617
6617
|
text = host.slice(0, -v4[1].length).replace(/:$/, host.endsWith(`::${v4[1]}`) ? ":" : "");
|
|
6618
6618
|
}
|
|
6619
6619
|
const halves = text.split("::");
|
|
@@ -6623,13 +6623,13 @@ function ipv6(host) {
|
|
|
6623
6623
|
const back = halves.length === 2 ? groups(halves[1]) : [];
|
|
6624
6624
|
if (![...head, ...back].every((g) => /^[0-9A-Fa-f]{1,4}$/.test(g))) return void 0;
|
|
6625
6625
|
const words2 = [...head, ...back].map((g) => Number.parseInt(g, 16));
|
|
6626
|
-
const missing4 = 8 - words2.length -
|
|
6626
|
+
const missing4 = 8 - words2.length - tail2.length;
|
|
6627
6627
|
if (halves.length === 2 ? missing4 < 1 : missing4 !== 0) return void 0;
|
|
6628
6628
|
const all = [
|
|
6629
6629
|
...head.map((g) => Number.parseInt(g, 16)),
|
|
6630
6630
|
...new Array(halves.length === 2 ? missing4 : 0).fill(0),
|
|
6631
6631
|
...back.map((g) => Number.parseInt(g, 16)),
|
|
6632
|
-
...
|
|
6632
|
+
...tail2
|
|
6633
6633
|
];
|
|
6634
6634
|
return Uint8Array.from(all.flatMap((w) => [w >> 8, w & 255]));
|
|
6635
6635
|
}
|
|
@@ -7556,8 +7556,8 @@ Connection: close\r
|
|
|
7556
7556
|
out.set(bytes, head.length);
|
|
7557
7557
|
return out;
|
|
7558
7558
|
}
|
|
7559
|
-
function json(
|
|
7560
|
-
return respond(200, `${JSON.stringify(
|
|
7559
|
+
function json(value2) {
|
|
7560
|
+
return respond(200, `${JSON.stringify(value2, null, 2)}
|
|
7561
7561
|
`, "application/json; charset=UTF-8");
|
|
7562
7562
|
}
|
|
7563
7563
|
var CdpFacade = class {
|
|
@@ -7955,9 +7955,9 @@ function comparator(token) {
|
|
|
7955
7955
|
function set2(text) {
|
|
7956
7956
|
const trimmed = text.trim().replace(/(<=|>=|<|>|=|\^|~>?)\s+/g, "$1");
|
|
7957
7957
|
if (trimmed === "") return ANY;
|
|
7958
|
-
const
|
|
7959
|
-
if (
|
|
7960
|
-
const [from, to] = [partial(
|
|
7958
|
+
const range2 = /^(\S+)\s+-\s+(\S+)$/.exec(trimmed);
|
|
7959
|
+
if (range2) {
|
|
7960
|
+
const [from, to] = [partial(range2[1]), partial(range2[2])];
|
|
7961
7961
|
return from && to ? hyphen(from, to) : void 0;
|
|
7962
7962
|
}
|
|
7963
7963
|
const out = [];
|
|
@@ -7976,16 +7976,16 @@ function holds(v, [op, w]) {
|
|
|
7976
7976
|
if (op === ">=") return c >= 0;
|
|
7977
7977
|
return c === 0;
|
|
7978
7978
|
}
|
|
7979
|
-
function satisfies(version,
|
|
7979
|
+
function satisfies(version, range2) {
|
|
7980
7980
|
const v = parseVersion(version);
|
|
7981
7981
|
if (!v) return void 0;
|
|
7982
|
-
const sets =
|
|
7982
|
+
const sets = range2.split("||").map(set2);
|
|
7983
7983
|
if (sets.some((s) => s === void 0)) return void 0;
|
|
7984
7984
|
return sets.some((s) => s.every((c) => holds(v, c)));
|
|
7985
7985
|
}
|
|
7986
7986
|
|
|
7987
7987
|
// src/version.ts
|
|
7988
|
-
var KERNEL_VERSION = true ? "1.
|
|
7988
|
+
var KERNEL_VERSION = true ? "1.107.1" : "0.0.0-dev";
|
|
7989
7989
|
var OS_RELEASE = "6.0.0-slicc";
|
|
7990
7990
|
function procVersion() {
|
|
7991
7991
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -8009,19 +8009,19 @@ function inside(pkg, rel2) {
|
|
|
8009
8009
|
function envOf(pkg, raw) {
|
|
8010
8010
|
const env = {};
|
|
8011
8011
|
if (!raw || typeof raw !== "object") return env;
|
|
8012
|
-
for (const [key,
|
|
8012
|
+
for (const [key, value2] of Object.entries(raw)) {
|
|
8013
8013
|
if (!ENV_KEY.test(key)) continue;
|
|
8014
|
-
if (typeof
|
|
8015
|
-
else if (
|
|
8014
|
+
if (typeof value2 === "string") env[key] = value2.replace(PACKAGE, pkg);
|
|
8015
|
+
else if (value2 === null) env[key] = null;
|
|
8016
8016
|
}
|
|
8017
8017
|
return env;
|
|
8018
8018
|
}
|
|
8019
8019
|
function envFields(merged2) {
|
|
8020
8020
|
const env = {};
|
|
8021
8021
|
const unset = [];
|
|
8022
|
-
for (const [key,
|
|
8023
|
-
if (
|
|
8024
|
-
else env[key] =
|
|
8022
|
+
for (const [key, value2] of Object.entries(merged2)) {
|
|
8023
|
+
if (value2 === null) unset.push(key);
|
|
8024
|
+
else env[key] = value2;
|
|
8025
8025
|
}
|
|
8026
8026
|
return {
|
|
8027
8027
|
...Object.keys(env).length > 0 ? { env } : {},
|
|
@@ -8100,9 +8100,9 @@ function commandsOf(pkg, manifest) {
|
|
|
8100
8100
|
async function withPackagePaths(fs, pkg, command) {
|
|
8101
8101
|
if (!command.env) return command;
|
|
8102
8102
|
const env = {};
|
|
8103
|
-
for (const [key,
|
|
8104
|
-
const path = !
|
|
8105
|
-
env[key] = path && await fs.exists(path) ? path :
|
|
8103
|
+
for (const [key, value2] of Object.entries(command.env)) {
|
|
8104
|
+
const path = !value2.startsWith("/") && value2.includes("/") && inside(pkg, value2);
|
|
8105
|
+
env[key] = path && await fs.exists(path) ? path : value2;
|
|
8106
8106
|
}
|
|
8107
8107
|
return { ...command, env };
|
|
8108
8108
|
}
|
|
@@ -8153,7 +8153,7 @@ function physicalResolver(fs) {
|
|
|
8153
8153
|
const known = /* @__PURE__ */ new Map();
|
|
8154
8154
|
const pending = /* @__PURE__ */ new Set();
|
|
8155
8155
|
let hops = 0;
|
|
8156
|
-
const
|
|
8156
|
+
const step2 = async (path) => {
|
|
8157
8157
|
if (path === "/") return { path, file: false };
|
|
8158
8158
|
const slash = path.lastIndexOf("/");
|
|
8159
8159
|
const parent = await resolve2(path.slice(0, slash) || "/");
|
|
@@ -8170,7 +8170,7 @@ function physicalResolver(fs) {
|
|
|
8170
8170
|
let found = known.get(path);
|
|
8171
8171
|
if (!found) {
|
|
8172
8172
|
pending.add(path);
|
|
8173
|
-
found =
|
|
8173
|
+
found = step2(path).finally(() => pending.delete(path));
|
|
8174
8174
|
known.set(path, found);
|
|
8175
8175
|
}
|
|
8176
8176
|
return found;
|
|
@@ -8205,15 +8205,15 @@ async function binCommandsOf(physical, pkg, manifest, taken) {
|
|
|
8205
8205
|
}
|
|
8206
8206
|
function engineRefusal(manifest, kernel) {
|
|
8207
8207
|
const engines = manifest.engines;
|
|
8208
|
-
const
|
|
8208
|
+
const range2 = engines && typeof engines === "object" ? engines["slicc-kernel"] : void 0;
|
|
8209
8209
|
const running = parseVersion(kernel);
|
|
8210
|
-
if (typeof
|
|
8210
|
+
if (typeof range2 !== "string" || !running || running.slice(0, 3).every((n) => n === 0)) {
|
|
8211
8211
|
return void 0;
|
|
8212
8212
|
}
|
|
8213
|
-
const fits = satisfies(kernel,
|
|
8213
|
+
const fits = satisfies(kernel, range2);
|
|
8214
8214
|
if (fits) return void 0;
|
|
8215
8215
|
const who = `${String(manifest.name ?? "?")}@${String(manifest.version ?? "?")}`;
|
|
8216
|
-
return fits === false ? `${who} needs slicc-kernel ${
|
|
8216
|
+
return fits === false ? `${who} needs slicc-kernel ${range2}; this kernel is ${kernel}` : `${who} has an engines.slicc-kernel range this kernel can't read: ${JSON.stringify(range2)}`;
|
|
8217
8217
|
}
|
|
8218
8218
|
var PACKAGE_NAME = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
|
|
8219
8219
|
function replacesOf(manifest) {
|
|
@@ -8344,6 +8344,795 @@ function binfmtOf(binfmts, path) {
|
|
|
8344
8344
|
return best === void 0 ? void 0 : binfmts.get(best);
|
|
8345
8345
|
}
|
|
8346
8346
|
|
|
8347
|
+
// src/fs/unlinked.ts
|
|
8348
|
+
var UNLINKED_DIR = ".slicc-unlinked";
|
|
8349
|
+
function webLocks() {
|
|
8350
|
+
return globalThis.navigator?.locks;
|
|
8351
|
+
}
|
|
8352
|
+
var within4 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
8353
|
+
var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
8354
|
+
var lockName = (instance) => `slicc-unlinked:${instance}`;
|
|
8355
|
+
var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
|
|
8356
|
+
var PathGate = class {
|
|
8357
|
+
tails = /* @__PURE__ */ new Map();
|
|
8358
|
+
async acquire(path) {
|
|
8359
|
+
const prev = this.tails.get(path) ?? Promise.resolve();
|
|
8360
|
+
const { promise, resolve: resolve2 } = Promise.withResolvers();
|
|
8361
|
+
const tail2 = prev.then(() => promise);
|
|
8362
|
+
this.tails.set(path, tail2);
|
|
8363
|
+
await prev;
|
|
8364
|
+
return () => {
|
|
8365
|
+
resolve2();
|
|
8366
|
+
if (this.tails.get(path) === tail2) this.tails.delete(path);
|
|
8367
|
+
};
|
|
8368
|
+
}
|
|
8369
|
+
async run(path, op) {
|
|
8370
|
+
const release = await this.acquire(path);
|
|
8371
|
+
try {
|
|
8372
|
+
return await op();
|
|
8373
|
+
} finally {
|
|
8374
|
+
release();
|
|
8375
|
+
}
|
|
8376
|
+
}
|
|
8377
|
+
};
|
|
8378
|
+
var UnlinkedKeeper = class {
|
|
8379
|
+
fs;
|
|
8380
|
+
holders;
|
|
8381
|
+
rootOf;
|
|
8382
|
+
locks;
|
|
8383
|
+
busy;
|
|
8384
|
+
waiting = /* @__PURE__ */ new Set();
|
|
8385
|
+
owner;
|
|
8386
|
+
instance;
|
|
8387
|
+
gate = new PathGate();
|
|
8388
|
+
queue = Promise.resolve();
|
|
8389
|
+
count = 0;
|
|
8390
|
+
links = /* @__PURE__ */ new Map();
|
|
8391
|
+
linked = /* @__PURE__ */ new Map();
|
|
8392
|
+
dirOwner;
|
|
8393
|
+
constructor(fs, holders, rootOf, options = {}) {
|
|
8394
|
+
const { locks, owner } = options;
|
|
8395
|
+
this.dirOwner = options.dirOwner;
|
|
8396
|
+
this.fs = fs;
|
|
8397
|
+
this.holders = holders;
|
|
8398
|
+
this.rootOf = rootOf;
|
|
8399
|
+
this.locks = locks;
|
|
8400
|
+
this.busy = options.busy ?? (() => false);
|
|
8401
|
+
this.owner = owner;
|
|
8402
|
+
this.instance = owner ? `${owner.id}~${crypto.randomUUID()}` : crypto.randomUUID();
|
|
8403
|
+
void locks?.request(lockName(this.instance), () => new Promise(() => void 0));
|
|
8404
|
+
}
|
|
8405
|
+
serial(op) {
|
|
8406
|
+
const next3 = this.queue.then(op);
|
|
8407
|
+
this.queue = next3.catch(() => void 0);
|
|
8408
|
+
return next3;
|
|
8409
|
+
}
|
|
8410
|
+
wrap(fs = this.fs) {
|
|
8411
|
+
const keeper = this;
|
|
8412
|
+
const visible = (names) => names.filter((name2) => name2 !== UNLINKED_DIR);
|
|
8413
|
+
const counted2 = (st) => {
|
|
8414
|
+
const hidden = keeper.links.get(`${st.dev ?? 0}:${st.ino}`) ?? 0;
|
|
8415
|
+
return hidden === 0 || st.nlink === void 0 ? st : { ...st, nlink: Math.max(0, st.nlink - hidden) };
|
|
8416
|
+
};
|
|
8417
|
+
return {
|
|
8418
|
+
...fs,
|
|
8419
|
+
links: (path) => mayLink(fs, path),
|
|
8420
|
+
stat: async (path) => counted2(await fs.stat(path)),
|
|
8421
|
+
lstat: async (path) => counted2(await fs.lstat(path)),
|
|
8422
|
+
async rm(path, options) {
|
|
8423
|
+
const target = fs.resolvePath("/", path);
|
|
8424
|
+
const pinned = await keeper.pin(target);
|
|
8425
|
+
if (pinned.moved.has(target)) pinned.commit();
|
|
8426
|
+
else await keeper.settle(pinned, () => fs.rm(path, options));
|
|
8427
|
+
},
|
|
8428
|
+
async rename(from, to) {
|
|
8429
|
+
const target = fs.resolvePath("/", to);
|
|
8430
|
+
if (target === fs.resolvePath("/", from)) return fs.rename(from, to);
|
|
8431
|
+
const pinned = await keeper.pin(target);
|
|
8432
|
+
await keeper.settle(pinned, () => fs.rename(from, to));
|
|
8433
|
+
},
|
|
8434
|
+
readdir: async (path) => visible(await fs.readdir(path)),
|
|
8435
|
+
readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR).map(([name2, st]) => [name2, st && counted2(st)])
|
|
8436
|
+
};
|
|
8437
|
+
}
|
|
8438
|
+
async settle(pinned, op) {
|
|
8439
|
+
try {
|
|
8440
|
+
await op();
|
|
8441
|
+
} catch (err) {
|
|
8442
|
+
await pinned.rollback();
|
|
8443
|
+
throw err;
|
|
8444
|
+
}
|
|
8445
|
+
pinned.commit();
|
|
8446
|
+
}
|
|
8447
|
+
async pin(target) {
|
|
8448
|
+
const held = /* @__PURE__ */ new Map();
|
|
8449
|
+
for (const holder of this.holders) {
|
|
8450
|
+
if (holder.owns(target)) continue;
|
|
8451
|
+
for (const path of holder.held()) {
|
|
8452
|
+
if (!within4(path, target) || holder.isKept(path)) continue;
|
|
8453
|
+
held.set(path, [...held.get(path) ?? [], holder]);
|
|
8454
|
+
}
|
|
8455
|
+
}
|
|
8456
|
+
const paths = [...held.keys()].sort();
|
|
8457
|
+
const releases = [];
|
|
8458
|
+
for (const path of paths) releases.push(await this.gate.acquire(path));
|
|
8459
|
+
const hidden = /* @__PURE__ */ new Map();
|
|
8460
|
+
for (const path of paths) {
|
|
8461
|
+
const at = await this.hide(path);
|
|
8462
|
+
if (at !== void 0) hidden.set(path, at);
|
|
8463
|
+
}
|
|
8464
|
+
const done4 = () => {
|
|
8465
|
+
for (const release of releases) release();
|
|
8466
|
+
};
|
|
8467
|
+
const files = /* @__PURE__ */ new Map();
|
|
8468
|
+
for (const [path, at] of hidden) {
|
|
8469
|
+
const holders = held.get(path);
|
|
8470
|
+
const file = this.keptFile(at, { refs: holders.length + 1 });
|
|
8471
|
+
files.set(path, file);
|
|
8472
|
+
for (const holder of holders) holder.keep(path, file);
|
|
8473
|
+
}
|
|
8474
|
+
return {
|
|
8475
|
+
moved: new Set(hidden.keys()),
|
|
8476
|
+
commit: () => {
|
|
8477
|
+
for (const file of files.values()) file.release();
|
|
8478
|
+
done4();
|
|
8479
|
+
},
|
|
8480
|
+
rollback: async () => {
|
|
8481
|
+
for (const [path, file] of files) {
|
|
8482
|
+
for (const holder of held.get(path)) holder.unkeep(path, file);
|
|
8483
|
+
await this.serial(async () => {
|
|
8484
|
+
await Promise.allSettled([this.fs.rename(file.hidden, path)]);
|
|
8485
|
+
this.unlinkedName(file.hidden);
|
|
8486
|
+
await this.tidy(parentOf5(file.hidden));
|
|
8487
|
+
});
|
|
8488
|
+
}
|
|
8489
|
+
done4();
|
|
8490
|
+
}
|
|
8491
|
+
};
|
|
8492
|
+
}
|
|
8493
|
+
keptFile(hidden, entry) {
|
|
8494
|
+
return {
|
|
8495
|
+
hidden,
|
|
8496
|
+
release: () => {
|
|
8497
|
+
if (--entry.refs === 0) void this.serial(() => this.drop(hidden));
|
|
8498
|
+
}
|
|
8499
|
+
};
|
|
8500
|
+
}
|
|
8501
|
+
hide(path) {
|
|
8502
|
+
return this.serial(async () => {
|
|
8503
|
+
const dir = dirOf(this.rootOf(path));
|
|
8504
|
+
const hidden = `${dir}/${this.instance}.${++this.count}`;
|
|
8505
|
+
try {
|
|
8506
|
+
const owner = this.dirOwner;
|
|
8507
|
+
await this.fs.mkdir(dir, { recursive: true, ...owner ? { owner } : {} });
|
|
8508
|
+
await this.fs.rename(path, hidden);
|
|
8509
|
+
await this.linkedName(hidden);
|
|
8510
|
+
return hidden;
|
|
8511
|
+
} catch {
|
|
8512
|
+
await this.tidy(dir);
|
|
8513
|
+
return void 0;
|
|
8514
|
+
}
|
|
8515
|
+
});
|
|
8516
|
+
}
|
|
8517
|
+
closed(path) {
|
|
8518
|
+
if (this.waiting.delete(path)) void this.serial(() => this.drop(path));
|
|
8519
|
+
}
|
|
8520
|
+
async drop(hidden) {
|
|
8521
|
+
if (this.busy(hidden)) {
|
|
8522
|
+
this.waiting.add(hidden);
|
|
8523
|
+
return;
|
|
8524
|
+
}
|
|
8525
|
+
await this.fs.rm(hidden).catch(() => void 0);
|
|
8526
|
+
this.unlinkedName(hidden);
|
|
8527
|
+
await this.tidy(parentOf5(hidden));
|
|
8528
|
+
}
|
|
8529
|
+
async linkedName(hidden) {
|
|
8530
|
+
if (!mayLink(this.fs, hidden)) return;
|
|
8531
|
+
const st = await this.fs.lstat(hidden).catch(() => void 0);
|
|
8532
|
+
if (st?.nlink === void 0) return;
|
|
8533
|
+
const id2 = `${st.dev ?? 0}:${st.ino}`;
|
|
8534
|
+
this.linked.set(hidden, id2);
|
|
8535
|
+
this.links.set(id2, (this.links.get(id2) ?? 0) + 1);
|
|
8536
|
+
}
|
|
8537
|
+
unlinkedName(hidden) {
|
|
8538
|
+
const id2 = this.linked.get(hidden);
|
|
8539
|
+
if (id2 === void 0) return;
|
|
8540
|
+
this.linked.delete(hidden);
|
|
8541
|
+
const left = (this.links.get(id2) ?? 1) - 1;
|
|
8542
|
+
if (left > 0) this.links.set(id2, left);
|
|
8543
|
+
else this.links.delete(id2);
|
|
8544
|
+
}
|
|
8545
|
+
async tidy(dir) {
|
|
8546
|
+
const left = await this.fs.readdir(dir).catch(() => [UNLINKED_DIR]);
|
|
8547
|
+
if (left.length === 0) await this.fs.rm(dir, { recursive: true }).catch(() => void 0);
|
|
8548
|
+
}
|
|
8549
|
+
sweep(root) {
|
|
8550
|
+
const query = this.locks?.query?.bind(this.locks);
|
|
8551
|
+
const owner = this.owner;
|
|
8552
|
+
if (!query && !owner) return Promise.resolve();
|
|
8553
|
+
return this.serial(async () => {
|
|
8554
|
+
const dir = dirOf(root);
|
|
8555
|
+
const names = await this.fs.readdir(dir).catch(() => void 0);
|
|
8556
|
+
if (!names) return;
|
|
8557
|
+
const held = query && new Set(((await query()).held ?? []).map((lock) => lock.name));
|
|
8558
|
+
for (const name2 of names) {
|
|
8559
|
+
const id2 = name2.slice(0, name2.lastIndexOf("."));
|
|
8560
|
+
const at = id2.indexOf("~");
|
|
8561
|
+
const dead = id2 !== this.instance && (at < 0 ? held !== void 0 && !held.has(lockName(id2)) : owner?.alive(id2.slice(0, at)) === false);
|
|
8562
|
+
if (dead) await this.fs.rm(`${dir}/${name2}`, { recursive: true }).catch(() => void 0);
|
|
8563
|
+
}
|
|
8564
|
+
await this.tidy(dir);
|
|
8565
|
+
});
|
|
8566
|
+
}
|
|
8567
|
+
};
|
|
8568
|
+
|
|
8569
|
+
// src/cron/clock.ts
|
|
8570
|
+
var systemCronClock = {
|
|
8571
|
+
now: () => Date.now(),
|
|
8572
|
+
sleep(until, signal) {
|
|
8573
|
+
if (signal.aborted) return Promise.resolve();
|
|
8574
|
+
return new Promise((resolve2) => {
|
|
8575
|
+
const done4 = () => {
|
|
8576
|
+
clearTimeout(timer);
|
|
8577
|
+
signal.removeEventListener("abort", done4);
|
|
8578
|
+
resolve2();
|
|
8579
|
+
};
|
|
8580
|
+
const timer = setTimeout(done4, Math.max(0, until - Date.now()));
|
|
8581
|
+
timer.unref?.();
|
|
8582
|
+
signal.addEventListener("abort", done4, { once: true });
|
|
8583
|
+
});
|
|
8584
|
+
}
|
|
8585
|
+
};
|
|
8586
|
+
|
|
8587
|
+
// src/cron/schedule.ts
|
|
8588
|
+
var MISSED_CAP = 1e5;
|
|
8589
|
+
var MACROS = {
|
|
8590
|
+
"@yearly": "0 0 1 1 *",
|
|
8591
|
+
"@annually": "0 0 1 1 *",
|
|
8592
|
+
"@monthly": "0 0 1 * *",
|
|
8593
|
+
"@weekly": "0 0 * * 0",
|
|
8594
|
+
"@daily": "0 0 * * *",
|
|
8595
|
+
"@midnight": "0 0 * * *",
|
|
8596
|
+
"@hourly": "0 * * * *"
|
|
8597
|
+
};
|
|
8598
|
+
var MONTHS = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"];
|
|
8599
|
+
var WEEKDAYS = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
|
|
8600
|
+
var FIELDS = [
|
|
8601
|
+
{ name: "minute", min: 0, max: 59 },
|
|
8602
|
+
{ name: "hour", min: 0, max: 23 },
|
|
8603
|
+
{ name: "day of month", min: 1, max: 31 },
|
|
8604
|
+
{ name: "month", min: 1, max: 12, names: MONTHS, offset: 1 },
|
|
8605
|
+
{ name: "day of week", min: 0, max: 7, names: WEEKDAYS }
|
|
8606
|
+
];
|
|
8607
|
+
function value(text, field4) {
|
|
8608
|
+
const named = field4.names?.indexOf(text.toLowerCase()) ?? -1;
|
|
8609
|
+
if (named >= 0) return named + (field4.offset ?? 0);
|
|
8610
|
+
if (!/^\d+$/.test(text)) throw new Error(`${field4.name}: "${text}" is not a number`);
|
|
8611
|
+
const number = Number(text);
|
|
8612
|
+
if (number < field4.min || number > field4.max)
|
|
8613
|
+
throw new Error(`${field4.name}: ${number} is outside ${field4.min}-${field4.max}`);
|
|
8614
|
+
return number;
|
|
8615
|
+
}
|
|
8616
|
+
function range(part, field4) {
|
|
8617
|
+
if (part === "*") return [field4.min, field4.max];
|
|
8618
|
+
const [from, to, extra] = part.split("-");
|
|
8619
|
+
if (extra !== void 0) throw new Error(`${field4.name}: "${part}" is not a range`);
|
|
8620
|
+
const start2 = value(from, field4);
|
|
8621
|
+
const end = to === void 0 ? start2 : value(to, field4);
|
|
8622
|
+
if (end < start2) throw new Error(`${field4.name}: range ${part} runs backwards`);
|
|
8623
|
+
return [start2, end];
|
|
8624
|
+
}
|
|
8625
|
+
function step(text, field4) {
|
|
8626
|
+
if (text === void 0) return 1;
|
|
8627
|
+
if (!/^\d+$/.test(text) || Number(text) === 0)
|
|
8628
|
+
throw new Error(`${field4.name}: step "${text}" must be a positive number`);
|
|
8629
|
+
return Number(text);
|
|
8630
|
+
}
|
|
8631
|
+
function field2(text, spec) {
|
|
8632
|
+
const out = /* @__PURE__ */ new Set();
|
|
8633
|
+
for (const part of text.split(",")) {
|
|
8634
|
+
if (part === "") throw new Error(`${spec.name}: empty list item`);
|
|
8635
|
+
const [base, every, extra] = part.split("/");
|
|
8636
|
+
if (extra !== void 0) throw new Error(`${spec.name}: "${part}" has two steps`);
|
|
8637
|
+
const [start2, end] = range(base, spec);
|
|
8638
|
+
const stride = step(every, spec);
|
|
8639
|
+
const last = every !== void 0 && !base.includes("-") ? spec.max : end;
|
|
8640
|
+
for (let n = start2; n <= last; n += stride) out.add(n);
|
|
8641
|
+
}
|
|
8642
|
+
return out;
|
|
8643
|
+
}
|
|
8644
|
+
function parseSchedule(expression) {
|
|
8645
|
+
const trimmed = expression.trim();
|
|
8646
|
+
const text = MACROS[trimmed.toLowerCase()] ?? trimmed;
|
|
8647
|
+
if (text.startsWith("@")) throw new Error(`unknown schedule ${trimmed}`);
|
|
8648
|
+
const parts2 = text.split(/\s+/);
|
|
8649
|
+
if (parts2.length !== 5)
|
|
8650
|
+
throw new Error(
|
|
8651
|
+
`"${trimmed}" needs 5 fields (minute hour day month weekday), not ${parts2.length}`
|
|
8652
|
+
);
|
|
8653
|
+
const [minutes, hours, days, months, weekdays] = parts2.map(
|
|
8654
|
+
(part, at) => field2(part, FIELDS[at])
|
|
8655
|
+
);
|
|
8656
|
+
if (weekdays.delete(7)) weekdays.add(0);
|
|
8657
|
+
return {
|
|
8658
|
+
minutes,
|
|
8659
|
+
hours,
|
|
8660
|
+
days,
|
|
8661
|
+
months,
|
|
8662
|
+
weekdays,
|
|
8663
|
+
anyDay: parts2[2].startsWith("*"),
|
|
8664
|
+
anyWeekday: parts2[4].startsWith("*")
|
|
8665
|
+
};
|
|
8666
|
+
}
|
|
8667
|
+
function parseWhen(expression) {
|
|
8668
|
+
if (expression.trim().toLowerCase() === "@reboot") return { reboot: true };
|
|
8669
|
+
return { reboot: false, schedule: parseSchedule(expression) };
|
|
8670
|
+
}
|
|
8671
|
+
var LOCAL = {
|
|
8672
|
+
year: (d) => d.getFullYear(),
|
|
8673
|
+
month: (d) => d.getMonth(),
|
|
8674
|
+
date: (d) => d.getDate(),
|
|
8675
|
+
day: (d) => d.getDay(),
|
|
8676
|
+
hours: (d) => d.getHours(),
|
|
8677
|
+
minutes: (d) => d.getMinutes(),
|
|
8678
|
+
setMonth: (d, month) => d.setMonth(month, 1),
|
|
8679
|
+
setDate: (d, date) => d.setDate(date),
|
|
8680
|
+
setHours: (d, hours) => d.setHours(hours, 0, 0, 0),
|
|
8681
|
+
setMinutes: (d, minutes) => d.setMinutes(minutes, 0, 0)
|
|
8682
|
+
};
|
|
8683
|
+
var UTC = {
|
|
8684
|
+
year: (d) => d.getUTCFullYear(),
|
|
8685
|
+
month: (d) => d.getUTCMonth(),
|
|
8686
|
+
date: (d) => d.getUTCDate(),
|
|
8687
|
+
day: (d) => d.getUTCDay(),
|
|
8688
|
+
hours: (d) => d.getUTCHours(),
|
|
8689
|
+
minutes: (d) => d.getUTCMinutes(),
|
|
8690
|
+
setMonth: (d, month) => d.setUTCMonth(month, 1),
|
|
8691
|
+
setDate: (d, date) => d.setUTCDate(date),
|
|
8692
|
+
setHours: (d, hours) => d.setUTCHours(hours, 0, 0, 0),
|
|
8693
|
+
setMinutes: (d, minutes) => d.setUTCMinutes(minutes, 0, 0)
|
|
8694
|
+
};
|
|
8695
|
+
function dayMatches(schedule, cal, date) {
|
|
8696
|
+
const day = schedule.days.has(cal.date(date));
|
|
8697
|
+
const weekday = schedule.weekdays.has(cal.day(date));
|
|
8698
|
+
if (schedule.anyDay || schedule.anyWeekday) return day && weekday;
|
|
8699
|
+
return day || weekday;
|
|
8700
|
+
}
|
|
8701
|
+
var LIMIT_YEARS = 8;
|
|
8702
|
+
function nextFire(schedule, after, utc = false) {
|
|
8703
|
+
const cal = utc ? UTC : LOCAL;
|
|
8704
|
+
const date = new Date(Math.floor(after / 6e4) * 6e4 + 6e4);
|
|
8705
|
+
const limit = cal.year(date) + LIMIT_YEARS;
|
|
8706
|
+
while (cal.year(date) <= limit) {
|
|
8707
|
+
if (!schedule.months.has(cal.month(date) + 1)) {
|
|
8708
|
+
cal.setMonth(date, cal.month(date) + 1);
|
|
8709
|
+
cal.setHours(date, 0);
|
|
8710
|
+
} else if (!dayMatches(schedule, cal, date)) {
|
|
8711
|
+
cal.setDate(date, cal.date(date) + 1);
|
|
8712
|
+
cal.setHours(date, 0);
|
|
8713
|
+
} else if (!schedule.hours.has(cal.hours(date))) {
|
|
8714
|
+
cal.setHours(date, cal.hours(date) + 1);
|
|
8715
|
+
} else if (!schedule.minutes.has(cal.minutes(date))) {
|
|
8716
|
+
cal.setMinutes(date, cal.minutes(date) + 1);
|
|
8717
|
+
} else return date.getTime();
|
|
8718
|
+
}
|
|
8719
|
+
return null;
|
|
8720
|
+
}
|
|
8721
|
+
function missedFires(schedule, from, until, utc = false, cap = MISSED_CAP) {
|
|
8722
|
+
let count2 = 0;
|
|
8723
|
+
let last = null;
|
|
8724
|
+
let at = from;
|
|
8725
|
+
while (count2 < cap) {
|
|
8726
|
+
at = nextFire(schedule, at, utc);
|
|
8727
|
+
if (at === null || at > until) return { count: count2, last, more: false };
|
|
8728
|
+
last = at;
|
|
8729
|
+
count2++;
|
|
8730
|
+
}
|
|
8731
|
+
const next3 = nextFire(schedule, last, utc);
|
|
8732
|
+
return { count: count2, last, more: next3 !== null && next3 <= until };
|
|
8733
|
+
}
|
|
8734
|
+
|
|
8735
|
+
// src/cron/table.ts
|
|
8736
|
+
var SYSTEM_CRONTAB = "/etc/crontab";
|
|
8737
|
+
var SPOOL_DIR = "/var/spool/cron/crontabs";
|
|
8738
|
+
var ENV_LINE = /^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/;
|
|
8739
|
+
var NAME_LINE = /^#\s*name:\s*(.*?)\s*$/;
|
|
8740
|
+
var JOB_NAME = /^[A-Za-z0-9_][A-Za-z0-9_.-]*$/;
|
|
8741
|
+
var USER_NAME = /^[a-z_][a-z0-9_.-]*$/;
|
|
8742
|
+
function unquote(text) {
|
|
8743
|
+
const trimmed = text.trim();
|
|
8744
|
+
const quote = trimmed[0];
|
|
8745
|
+
if ((quote === '"' || quote === "'") && trimmed.length > 1 && trimmed.endsWith(quote))
|
|
8746
|
+
return trimmed.slice(1, -1);
|
|
8747
|
+
return trimmed;
|
|
8748
|
+
}
|
|
8749
|
+
function hashId(text) {
|
|
8750
|
+
let a = 2166136261;
|
|
8751
|
+
let b = 16777619;
|
|
8752
|
+
for (let i = 0; i < text.length; i++) {
|
|
8753
|
+
const c = text.charCodeAt(i);
|
|
8754
|
+
a = Math.imul(a ^ c, 16777619);
|
|
8755
|
+
b = Math.imul(b ^ c, 1540483477);
|
|
8756
|
+
}
|
|
8757
|
+
const hex3 = (n) => (n >>> 0).toString(16).padStart(8, "0");
|
|
8758
|
+
return hex3(a) + hex3(b);
|
|
8759
|
+
}
|
|
8760
|
+
function split2(text, count2) {
|
|
8761
|
+
const words2 = [];
|
|
8762
|
+
let rest2 = text;
|
|
8763
|
+
for (let i = 0; i < count2; i++) {
|
|
8764
|
+
const match = /^(\S+)\s+/.exec(rest2);
|
|
8765
|
+
if (!match) return void 0;
|
|
8766
|
+
words2.push(match[1]);
|
|
8767
|
+
rest2 = rest2.slice(match[0].length);
|
|
8768
|
+
}
|
|
8769
|
+
return [words2, rest2.trim()];
|
|
8770
|
+
}
|
|
8771
|
+
function jobOf(content, pending, ctx) {
|
|
8772
|
+
const fields = content.startsWith("@") ? 1 : 5;
|
|
8773
|
+
const parts2 = split2(content, fields + (ctx.system ? 1 : 0));
|
|
8774
|
+
if (!parts2 || parts2[1] === "") throw new Error("no command");
|
|
8775
|
+
const [words2, command] = parts2;
|
|
8776
|
+
const schedule = words2.slice(0, fields).join(" ");
|
|
8777
|
+
const when = parseWhen(schedule);
|
|
8778
|
+
const user = ctx.system ? words2[fields] : void 0;
|
|
8779
|
+
if (user !== void 0 && !USER_NAME.test(user)) throw new Error(`bad user name ${user}`);
|
|
8780
|
+
if (pending !== void 0 && !JOB_NAME.test(pending))
|
|
8781
|
+
throw new Error(`bad job name "${pending}"`);
|
|
8782
|
+
if (pending !== void 0 && ctx.names.has(pending))
|
|
8783
|
+
throw new Error(`job name ${pending} is taken`);
|
|
8784
|
+
const hashed = hashId(`${ctx.path}
|
|
8785
|
+
${user ?? ""}
|
|
8786
|
+
${schedule}
|
|
8787
|
+
${command}`);
|
|
8788
|
+
let id2 = pending ?? hashed;
|
|
8789
|
+
for (let n = 2; pending === void 0 && ctx.names.has(id2); n++) id2 = `${hashed}.${n}`;
|
|
8790
|
+
ctx.names.add(id2);
|
|
8791
|
+
return {
|
|
8792
|
+
id: id2,
|
|
8793
|
+
...pending !== void 0 ? { name: pending } : {},
|
|
8794
|
+
schedule,
|
|
8795
|
+
when,
|
|
8796
|
+
...user !== void 0 ? { user } : {},
|
|
8797
|
+
command
|
|
8798
|
+
};
|
|
8799
|
+
}
|
|
8800
|
+
function parseCrontab(text, path, system = false) {
|
|
8801
|
+
const jobs = [];
|
|
8802
|
+
const errors = [];
|
|
8803
|
+
const env = {};
|
|
8804
|
+
const ctx = { path, system, names: /* @__PURE__ */ new Set() };
|
|
8805
|
+
let name2;
|
|
8806
|
+
for (const [index, raw] of text.split("\n").entries()) {
|
|
8807
|
+
const line = index + 1;
|
|
8808
|
+
const content = raw.trim();
|
|
8809
|
+
const named = NAME_LINE.exec(content);
|
|
8810
|
+
if (named) {
|
|
8811
|
+
name2 = named[1];
|
|
8812
|
+
continue;
|
|
8813
|
+
}
|
|
8814
|
+
const pending = name2;
|
|
8815
|
+
name2 = void 0;
|
|
8816
|
+
if (content === "" || content.startsWith("#")) continue;
|
|
8817
|
+
const assign = ENV_LINE.exec(content);
|
|
8818
|
+
if (assign) {
|
|
8819
|
+
env[assign[1]] = unquote(assign[2]);
|
|
8820
|
+
continue;
|
|
8821
|
+
}
|
|
8822
|
+
try {
|
|
8823
|
+
jobs.push({ line, ...jobOf(content, pending, ctx), env: { ...env } });
|
|
8824
|
+
} catch (err) {
|
|
8825
|
+
errors.push({ line, message: err.message });
|
|
8826
|
+
}
|
|
8827
|
+
}
|
|
8828
|
+
return { jobs, errors };
|
|
8829
|
+
}
|
|
8830
|
+
|
|
8831
|
+
// src/cron/service.ts
|
|
8832
|
+
var CRON_DIR = "/var/spool/cron";
|
|
8833
|
+
var STAMP_PATH = `${CRON_DIR}/stamp`;
|
|
8834
|
+
var LOG_DIR = "/var/log/cron";
|
|
8835
|
+
var ERR_LOG = `${LOG_DIR}/cron.err`;
|
|
8836
|
+
var LOG_CAP = 256 * 1024;
|
|
8837
|
+
var JOBS_PER_USER = 16;
|
|
8838
|
+
var WAKE_MS = 6e4;
|
|
8839
|
+
var BACK_JUMP_MS = 12e4;
|
|
8840
|
+
var DEFAULT_SHELL = "/bin/bash";
|
|
8841
|
+
var encoder2 = new TextEncoder();
|
|
8842
|
+
var EMPTY_STAMP = JSON.stringify({ jobs: {}, reboot: {} });
|
|
8843
|
+
var messageOf = (err) => err?.message ?? String(err);
|
|
8844
|
+
var keyOf2 = ({ source, job }) => `${source} ${job.id}`;
|
|
8845
|
+
function tail(bytes, cap = LOG_CAP) {
|
|
8846
|
+
return bytes.length > cap ? bytes.slice(bytes.length - cap) : bytes;
|
|
8847
|
+
}
|
|
8848
|
+
function concat3(parts2) {
|
|
8849
|
+
const out = new Uint8Array(parts2.reduce((n, part) => n + part.length, 0));
|
|
8850
|
+
let at = 0;
|
|
8851
|
+
for (const part of parts2) {
|
|
8852
|
+
out.set(part, at);
|
|
8853
|
+
at += part.length;
|
|
8854
|
+
}
|
|
8855
|
+
return out;
|
|
8856
|
+
}
|
|
8857
|
+
var Output = class {
|
|
8858
|
+
parts = [];
|
|
8859
|
+
size = 0;
|
|
8860
|
+
push(bytes) {
|
|
8861
|
+
this.parts.push(bytes.slice());
|
|
8862
|
+
this.size += bytes.length;
|
|
8863
|
+
if (this.size > LOG_CAP * 2) {
|
|
8864
|
+
const kept = tail(concat3(this.parts));
|
|
8865
|
+
this.parts = [kept];
|
|
8866
|
+
this.size = kept.length;
|
|
8867
|
+
}
|
|
8868
|
+
}
|
|
8869
|
+
bytes() {
|
|
8870
|
+
return tail(concat3(this.parts));
|
|
8871
|
+
}
|
|
8872
|
+
};
|
|
8873
|
+
function readStamp(text) {
|
|
8874
|
+
try {
|
|
8875
|
+
const parsed2 = JSON.parse(text ?? "");
|
|
8876
|
+
const jobs = parsed2.jobs && typeof parsed2.jobs === "object" ? parsed2.jobs : {};
|
|
8877
|
+
const reboot = parsed2.reboot && typeof parsed2.reboot === "object" ? parsed2.reboot : {};
|
|
8878
|
+
return { jobs: { ...jobs }, reboot: { ...reboot } };
|
|
8879
|
+
} catch {
|
|
8880
|
+
return { jobs: {}, reboot: {} };
|
|
8881
|
+
}
|
|
8882
|
+
}
|
|
8883
|
+
var CronService = class {
|
|
8884
|
+
host;
|
|
8885
|
+
clock;
|
|
8886
|
+
stopping = new AbortController();
|
|
8887
|
+
gate = new PathGate();
|
|
8888
|
+
running = /* @__PURE__ */ new Map();
|
|
8889
|
+
reported = /* @__PURE__ */ new Map();
|
|
8890
|
+
wake = new AbortController();
|
|
8891
|
+
started;
|
|
8892
|
+
rebooted = false;
|
|
8893
|
+
constructor(host) {
|
|
8894
|
+
this.host = host;
|
|
8895
|
+
this.clock = host.clock ?? systemCronClock;
|
|
8896
|
+
}
|
|
8897
|
+
start() {
|
|
8898
|
+
this.started ??= this.begin();
|
|
8899
|
+
return this.started;
|
|
8900
|
+
}
|
|
8901
|
+
stop() {
|
|
8902
|
+
this.stopping.abort();
|
|
8903
|
+
this.wake.abort();
|
|
8904
|
+
}
|
|
8905
|
+
async begin() {
|
|
8906
|
+
await this.host.ready?.().catch(() => void 0);
|
|
8907
|
+
if (this.stopping.signal.aborted) return;
|
|
8908
|
+
const { locks } = this.host;
|
|
8909
|
+
const fire = () => this.fire().catch((err) => {
|
|
8910
|
+
console.warn(`slicc-kernel cron: stopped: ${messageOf(err)}`);
|
|
8911
|
+
});
|
|
8912
|
+
if (!locks) return fire();
|
|
8913
|
+
await locks.request(this.host.lock, { signal: this.stopping.signal }, fire).catch(() => void 0);
|
|
8914
|
+
}
|
|
8915
|
+
poke = () => this.wake.abort();
|
|
8916
|
+
async fire() {
|
|
8917
|
+
await this.provision();
|
|
8918
|
+
const unwatch = await this.host.watch([SYSTEM_CRONTAB, SPOOL_DIR], this.poke);
|
|
8919
|
+
try {
|
|
8920
|
+
while (!this.stopping.signal.aborted) await this.pass();
|
|
8921
|
+
} finally {
|
|
8922
|
+
unwatch();
|
|
8923
|
+
}
|
|
8924
|
+
}
|
|
8925
|
+
async pass() {
|
|
8926
|
+
let until;
|
|
8927
|
+
try {
|
|
8928
|
+
until = await this.tick();
|
|
8929
|
+
} catch (err) {
|
|
8930
|
+
await this.error(`cron: ${messageOf(err)}`);
|
|
8931
|
+
until = this.clock.now() + WAKE_MS;
|
|
8932
|
+
}
|
|
8933
|
+
if (this.stopping.signal.aborted) return;
|
|
8934
|
+
if (this.wake.signal.aborted) this.wake = new AbortController();
|
|
8935
|
+
await this.clock.sleep(until, this.wake.signal);
|
|
8936
|
+
}
|
|
8937
|
+
async provision() {
|
|
8938
|
+
const { fs, owners } = this.host;
|
|
8939
|
+
for (const [dir, mode] of [
|
|
8940
|
+
[CRON_DIR, 493],
|
|
8941
|
+
[SPOOL_DIR, 448],
|
|
8942
|
+
[LOG_DIR, 493]
|
|
8943
|
+
]) {
|
|
8944
|
+
if (await fs.exists(dir)) continue;
|
|
8945
|
+
await fs.mkdir(dir, { recursive: true });
|
|
8946
|
+
if (owners) await fs.chown?.(dir, { uid: 0, gid: 0, mode, follow: false });
|
|
8947
|
+
}
|
|
8948
|
+
}
|
|
8949
|
+
async tick() {
|
|
8950
|
+
const { fs } = this.host;
|
|
8951
|
+
const loaded = await this.load();
|
|
8952
|
+
const stamp = readStamp(await fs.readFile(STAMP_PATH).catch(() => void 0));
|
|
8953
|
+
const now = this.clock.now();
|
|
8954
|
+
const utc = this.clock.utc === true;
|
|
8955
|
+
const due2 = [];
|
|
8956
|
+
const keys = /* @__PURE__ */ new Set();
|
|
8957
|
+
let next3 = now + WAKE_MS;
|
|
8958
|
+
for (const entry of loaded) {
|
|
8959
|
+
const key = keyOf2(entry);
|
|
8960
|
+
keys.add(key);
|
|
8961
|
+
const { when } = entry.job;
|
|
8962
|
+
if (when.reboot) {
|
|
8963
|
+
if (this.rebooted || stamp.reboot[key] === this.host.bootId) continue;
|
|
8964
|
+
stamp.reboot[key] = this.host.bootId;
|
|
8965
|
+
due2.push({ entry, due: now, missed: 0 });
|
|
8966
|
+
continue;
|
|
8967
|
+
}
|
|
8968
|
+
const last = stamp.jobs[key];
|
|
8969
|
+
if (typeof last !== "number" || last > now + BACK_JUMP_MS) stamp.jobs[key] = now;
|
|
8970
|
+
else {
|
|
8971
|
+
const missed = missedFires(when.schedule, last, now, utc);
|
|
8972
|
+
if (missed.last !== null) {
|
|
8973
|
+
stamp.jobs[key] = now;
|
|
8974
|
+
due2.push({ entry, due: missed.last, missed: missed.count - 1 });
|
|
8975
|
+
}
|
|
8976
|
+
}
|
|
8977
|
+
const coming = nextFire(when.schedule, Math.max(now, stamp.jobs[key]), utc);
|
|
8978
|
+
if (coming !== null && coming < next3) next3 = coming;
|
|
8979
|
+
}
|
|
8980
|
+
this.rebooted = true;
|
|
8981
|
+
for (const map of [stamp.jobs, stamp.reboot])
|
|
8982
|
+
for (const key of Object.keys(map)) if (!keys.has(key)) delete map[key];
|
|
8983
|
+
await this.writeStamp(stamp);
|
|
8984
|
+
for (const each of due2) void this.spawn(each);
|
|
8985
|
+
return next3;
|
|
8986
|
+
}
|
|
8987
|
+
async writeStamp(stamp) {
|
|
8988
|
+
const { fs, owners } = this.host;
|
|
8989
|
+
const text = JSON.stringify(stamp);
|
|
8990
|
+
const old = await fs.readFile(STAMP_PATH).catch(() => void 0);
|
|
8991
|
+
if (old === text || old === void 0 && text === EMPTY_STAMP) return;
|
|
8992
|
+
const fresh2 = `${STAMP_PATH}.new`;
|
|
8993
|
+
const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
|
|
8994
|
+
await fs.writeFile(fresh2, text, owner);
|
|
8995
|
+
await fs.rename(fresh2, STAMP_PATH);
|
|
8996
|
+
}
|
|
8997
|
+
async load() {
|
|
8998
|
+
const { fs } = this.host;
|
|
8999
|
+
const out = [];
|
|
9000
|
+
const system = await this.table(SYSTEM_CRONTAB, 0, true);
|
|
9001
|
+
for (const job of system?.jobs ?? []) {
|
|
9002
|
+
const account = await this.host.lookup(job.user);
|
|
9003
|
+
if (account) out.push({ source: "system", account, job });
|
|
9004
|
+
}
|
|
9005
|
+
const names = await fs.readdir(SPOOL_DIR).catch(() => []);
|
|
9006
|
+
for (const name2 of names.sort()) {
|
|
9007
|
+
if (name2.startsWith(".") || name2.endsWith("~") || name2.endsWith(".new")) continue;
|
|
9008
|
+
const path = `${SPOOL_DIR}/${name2}`;
|
|
9009
|
+
const account = await this.host.lookup(name2);
|
|
9010
|
+
if (!account) {
|
|
9011
|
+
await this.once(path, "no user", [`${path}: skipped: there is no user ${name2}`]);
|
|
9012
|
+
continue;
|
|
9013
|
+
}
|
|
9014
|
+
const table = await this.table(path, account.uid, false);
|
|
9015
|
+
for (const job of table?.jobs ?? []) out.push({ source: name2, account, job });
|
|
9016
|
+
}
|
|
9017
|
+
return out;
|
|
9018
|
+
}
|
|
9019
|
+
async table(path, uid, system) {
|
|
9020
|
+
const { fs, owners } = this.host;
|
|
9021
|
+
const st = await fs.lstat(path).catch(() => void 0);
|
|
9022
|
+
if (!st) {
|
|
9023
|
+
this.reported.delete(path);
|
|
9024
|
+
return void 0;
|
|
9025
|
+
}
|
|
9026
|
+
const trusted = st.isFile && (!owners || (st.uid === uid || st.uid === 0) && (st.mode & 18) === 0);
|
|
9027
|
+
if (!trusted) {
|
|
9028
|
+
const who = uid === 0 ? "root" : "its user or root";
|
|
9029
|
+
const why = `${path}: skipped: it must be a file of ${who}, writable by its owner only`;
|
|
9030
|
+
await this.once(path, "untrusted", [why]);
|
|
9031
|
+
return void 0;
|
|
9032
|
+
}
|
|
9033
|
+
const text = await fs.readFile(path).catch(() => "");
|
|
9034
|
+
const table = parseCrontab(text, path, system);
|
|
9035
|
+
const errors = table.errors.map(({ line, message: message3 }) => `${path}:${line}: ${message3}`);
|
|
9036
|
+
for (const job of table.jobs) {
|
|
9037
|
+
if (job.user !== void 0 && !await this.host.lookup(job.user))
|
|
9038
|
+
errors.push(`${path}:${job.line}: there is no user ${job.user}`);
|
|
9039
|
+
}
|
|
9040
|
+
await this.once(path, text, errors);
|
|
9041
|
+
return table;
|
|
9042
|
+
}
|
|
9043
|
+
async once(path, version, lines2) {
|
|
9044
|
+
if (this.reported.get(path) === version) return;
|
|
9045
|
+
this.reported.set(path, version);
|
|
9046
|
+
for (const line of lines2) await this.error(line);
|
|
9047
|
+
}
|
|
9048
|
+
async error(line) {
|
|
9049
|
+
if (this.stopping.signal.aborted) return;
|
|
9050
|
+
const stamp = new Date(this.clock.now()).toISOString();
|
|
9051
|
+
await this.append(ERR_LOG, encoder2.encode(`${stamp} ${line}
|
|
9052
|
+
`), 0, 0).catch(() => void 0);
|
|
9053
|
+
}
|
|
9054
|
+
async append(path, bytes, uid, gid) {
|
|
9055
|
+
const { fs, owners } = this.host;
|
|
9056
|
+
await this.gate.run(path, async () => {
|
|
9057
|
+
const dir = path.slice(0, path.lastIndexOf("/"));
|
|
9058
|
+
if (!await fs.lstat(dir).catch(() => void 0)) {
|
|
9059
|
+
await fs.mkdir(dir, { recursive: true });
|
|
9060
|
+
if (owners) await fs.chown?.(dir, { uid: 0, gid: 0, mode: 493, follow: false });
|
|
9061
|
+
}
|
|
9062
|
+
const st = await fs.lstat(path).catch(() => void 0);
|
|
9063
|
+
if (owners) await this.trustLog(dir, path, st, uid);
|
|
9064
|
+
const old = st ? await fs.readFileBuffer(path) : void 0;
|
|
9065
|
+
const next3 = tail(old ? concat3([old, bytes]) : bytes);
|
|
9066
|
+
const owner = owners ? { uid, gid, mode: 384 } : void 0;
|
|
9067
|
+
await fs.writeFile(path, next3, old === void 0 ? owner : void 0);
|
|
9068
|
+
});
|
|
9069
|
+
}
|
|
9070
|
+
async trustLog(dir, path, st, uid) {
|
|
9071
|
+
const parent = await this.host.fs.lstat(dir);
|
|
9072
|
+
if (!parent.isDirectory || parent.isSymbolicLink || parent.uid !== 0)
|
|
9073
|
+
throw new Error(`${path} not written: ${dir} must be a directory of root`);
|
|
9074
|
+
if (st && (!st.isFile || st.isSymbolicLink || st.uid !== uid && st.uid !== 0))
|
|
9075
|
+
throw new Error(`${path} not written: it must be a file of uid ${uid} or root`);
|
|
9076
|
+
}
|
|
9077
|
+
logOf({ account, job }) {
|
|
9078
|
+
return normalizePath(`${LOG_DIR}/${account.name}/${job.id}.log`);
|
|
9079
|
+
}
|
|
9080
|
+
async spawn({ entry, due: due2, missed }) {
|
|
9081
|
+
const { account, job } = entry;
|
|
9082
|
+
const log = this.logOf(entry);
|
|
9083
|
+
const iso = new Date(due2).toISOString();
|
|
9084
|
+
const count2 = this.running.get(account.uid) ?? 0;
|
|
9085
|
+
if (count2 >= JOBS_PER_USER) {
|
|
9086
|
+
const why = `${JOBS_PER_USER} cron jobs of ${account.name} are running`;
|
|
9087
|
+
await this.append(
|
|
9088
|
+
log,
|
|
9089
|
+
encoder2.encode(`--- ${iso} skipped: ${why} ---
|
|
9090
|
+
`),
|
|
9091
|
+
account.uid,
|
|
9092
|
+
account.gid
|
|
9093
|
+
).catch((err) => this.error(messageOf(err)));
|
|
9094
|
+
await this.error(`${account.name}/${job.id}: run due ${iso} skipped: ${why}`);
|
|
9095
|
+
return;
|
|
9096
|
+
}
|
|
9097
|
+
this.running.set(account.uid, count2 + 1);
|
|
9098
|
+
const output = new Output();
|
|
9099
|
+
let ended;
|
|
9100
|
+
try {
|
|
9101
|
+
const cwd = await this.host.fs.exists(account.home) ? account.home : "/";
|
|
9102
|
+
const shell = job.env.SHELL ?? DEFAULT_SHELL;
|
|
9103
|
+
ended = await this.host.run({
|
|
9104
|
+
account,
|
|
9105
|
+
argv: [shell, "-c", job.command],
|
|
9106
|
+
cwd,
|
|
9107
|
+
env: {
|
|
9108
|
+
SHELL: shell,
|
|
9109
|
+
...job.env,
|
|
9110
|
+
SLICC_CRON: "1",
|
|
9111
|
+
SLICC_CRON_JOB: job.id,
|
|
9112
|
+
SLICC_CRON_DUE: iso,
|
|
9113
|
+
SLICC_CRON_MISSED: String(missed)
|
|
9114
|
+
},
|
|
9115
|
+
output: (bytes) => output.push(bytes)
|
|
9116
|
+
});
|
|
9117
|
+
} catch (err) {
|
|
9118
|
+
output.push(encoder2.encode(`cron: ${messageOf(err)}
|
|
9119
|
+
`));
|
|
9120
|
+
ended = { status: 127 };
|
|
9121
|
+
} finally {
|
|
9122
|
+
this.running.set(account.uid, (this.running.get(account.uid) ?? 1) - 1);
|
|
9123
|
+
}
|
|
9124
|
+
if (this.stopping.signal.aborted) return;
|
|
9125
|
+
const exit = ended.signal ? `signal=${ended.signal}` : `exit=${ended.status}`;
|
|
9126
|
+
const header3 = encoder2.encode(`--- ${iso} ${exit} missed=${missed} ---
|
|
9127
|
+
`);
|
|
9128
|
+
const body = output.bytes();
|
|
9129
|
+
const newline = body.length > 0 && body[body.length - 1] !== 10 ? encoder2.encode("\n") : new Uint8Array();
|
|
9130
|
+
await this.append(log, concat3([header3, body, newline]), account.uid, account.gid).catch(
|
|
9131
|
+
(err) => this.error(messageOf(err))
|
|
9132
|
+
);
|
|
9133
|
+
}
|
|
9134
|
+
};
|
|
9135
|
+
|
|
8347
9136
|
// src/facade/types.ts
|
|
8348
9137
|
var FACADE_NAME = /^[a-z][a-z0-9-]*$/;
|
|
8349
9138
|
function facadeEnvName(name2) {
|
|
@@ -8872,7 +9661,7 @@ var DEFAULT_UMASK = 18;
|
|
|
8872
9661
|
function isModule(bytes) {
|
|
8873
9662
|
return MAGIC.every((b, i) => bytes[i] === b);
|
|
8874
9663
|
}
|
|
8875
|
-
function
|
|
9664
|
+
function within5(path, dir) {
|
|
8876
9665
|
return path.startsWith(dir === "/" ? "/" : `${dir}/`);
|
|
8877
9666
|
}
|
|
8878
9667
|
var FreshModules = class {
|
|
@@ -8896,10 +9685,10 @@ var FreshModules = class {
|
|
|
8896
9685
|
const target = normalizePath(to);
|
|
8897
9686
|
if (source === target) return;
|
|
8898
9687
|
for (const key of [...this.fresh.keys()]) {
|
|
8899
|
-
if (key === target ||
|
|
9688
|
+
if (key === target || within5(key, target)) this.fresh.delete(key);
|
|
8900
9689
|
}
|
|
8901
9690
|
for (const [key, fresh2] of [...this.fresh]) {
|
|
8902
|
-
if (key !== source && !
|
|
9691
|
+
if (key !== source && !within5(key, source)) continue;
|
|
8903
9692
|
this.fresh.delete(key);
|
|
8904
9693
|
this.fresh.set(target + key.slice(source.length), fresh2);
|
|
8905
9694
|
}
|
|
@@ -8941,7 +9730,7 @@ var FreshModules = class {
|
|
|
8941
9730
|
await fs.rm(path, options);
|
|
8942
9731
|
const at = normalizePath(path);
|
|
8943
9732
|
for (const key of [...this.fresh.keys()]) {
|
|
8944
|
-
if (key === at ||
|
|
9733
|
+
if (key === at || within5(key, at)) this.fresh.delete(key);
|
|
8945
9734
|
}
|
|
8946
9735
|
},
|
|
8947
9736
|
rename: async (from, to) => {
|
|
@@ -8971,12 +9760,12 @@ var FreshModules = class {
|
|
|
8971
9760
|
return new Proxy(fs, {
|
|
8972
9761
|
get(target, key, receiver) {
|
|
8973
9762
|
if (Object.hasOwn(overrides, key)) return overrides[key];
|
|
8974
|
-
const
|
|
8975
|
-
if (typeof
|
|
8976
|
-
let method = bound2.get(
|
|
9763
|
+
const value2 = Reflect.get(target, key, receiver);
|
|
9764
|
+
if (typeof value2 !== "function") return value2;
|
|
9765
|
+
let method = bound2.get(value2);
|
|
8977
9766
|
if (method === void 0) {
|
|
8978
|
-
method =
|
|
8979
|
-
bound2.set(
|
|
9767
|
+
method = value2.bind(target);
|
|
9768
|
+
bound2.set(value2, method);
|
|
8980
9769
|
}
|
|
8981
9770
|
return method;
|
|
8982
9771
|
}
|
|
@@ -9031,228 +9820,6 @@ async function physicalPathAsync(fs, path) {
|
|
|
9031
9820
|
return `/${walk2.done.join("/")}`;
|
|
9032
9821
|
}
|
|
9033
9822
|
|
|
9034
|
-
// src/fs/unlinked.ts
|
|
9035
|
-
var UNLINKED_DIR = ".slicc-unlinked";
|
|
9036
|
-
function webLocks() {
|
|
9037
|
-
return globalThis.navigator?.locks;
|
|
9038
|
-
}
|
|
9039
|
-
var within5 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
9040
|
-
var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
9041
|
-
var lockName = (instance) => `slicc-unlinked:${instance}`;
|
|
9042
|
-
var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
|
|
9043
|
-
var PathGate = class {
|
|
9044
|
-
tails = /* @__PURE__ */ new Map();
|
|
9045
|
-
async acquire(path) {
|
|
9046
|
-
const prev = this.tails.get(path) ?? Promise.resolve();
|
|
9047
|
-
const { promise, resolve: resolve2 } = Promise.withResolvers();
|
|
9048
|
-
const tail = prev.then(() => promise);
|
|
9049
|
-
this.tails.set(path, tail);
|
|
9050
|
-
await prev;
|
|
9051
|
-
return () => {
|
|
9052
|
-
resolve2();
|
|
9053
|
-
if (this.tails.get(path) === tail) this.tails.delete(path);
|
|
9054
|
-
};
|
|
9055
|
-
}
|
|
9056
|
-
async run(path, op) {
|
|
9057
|
-
const release = await this.acquire(path);
|
|
9058
|
-
try {
|
|
9059
|
-
return await op();
|
|
9060
|
-
} finally {
|
|
9061
|
-
release();
|
|
9062
|
-
}
|
|
9063
|
-
}
|
|
9064
|
-
};
|
|
9065
|
-
var UnlinkedKeeper = class {
|
|
9066
|
-
fs;
|
|
9067
|
-
holders;
|
|
9068
|
-
rootOf;
|
|
9069
|
-
locks;
|
|
9070
|
-
busy;
|
|
9071
|
-
waiting = /* @__PURE__ */ new Set();
|
|
9072
|
-
owner;
|
|
9073
|
-
instance;
|
|
9074
|
-
gate = new PathGate();
|
|
9075
|
-
queue = Promise.resolve();
|
|
9076
|
-
count = 0;
|
|
9077
|
-
links = /* @__PURE__ */ new Map();
|
|
9078
|
-
linked = /* @__PURE__ */ new Map();
|
|
9079
|
-
dirOwner;
|
|
9080
|
-
constructor(fs, holders, rootOf, options = {}) {
|
|
9081
|
-
const { locks, owner } = options;
|
|
9082
|
-
this.dirOwner = options.dirOwner;
|
|
9083
|
-
this.fs = fs;
|
|
9084
|
-
this.holders = holders;
|
|
9085
|
-
this.rootOf = rootOf;
|
|
9086
|
-
this.locks = locks;
|
|
9087
|
-
this.busy = options.busy ?? (() => false);
|
|
9088
|
-
this.owner = owner;
|
|
9089
|
-
this.instance = owner ? `${owner.id}~${crypto.randomUUID()}` : crypto.randomUUID();
|
|
9090
|
-
void locks?.request(lockName(this.instance), () => new Promise(() => void 0));
|
|
9091
|
-
}
|
|
9092
|
-
serial(op) {
|
|
9093
|
-
const next3 = this.queue.then(op);
|
|
9094
|
-
this.queue = next3.catch(() => void 0);
|
|
9095
|
-
return next3;
|
|
9096
|
-
}
|
|
9097
|
-
wrap(fs = this.fs) {
|
|
9098
|
-
const keeper = this;
|
|
9099
|
-
const visible = (names) => names.filter((name2) => name2 !== UNLINKED_DIR);
|
|
9100
|
-
const counted2 = (st) => {
|
|
9101
|
-
const hidden = keeper.links.get(`${st.dev ?? 0}:${st.ino}`) ?? 0;
|
|
9102
|
-
return hidden === 0 || st.nlink === void 0 ? st : { ...st, nlink: Math.max(0, st.nlink - hidden) };
|
|
9103
|
-
};
|
|
9104
|
-
return {
|
|
9105
|
-
...fs,
|
|
9106
|
-
links: (path) => mayLink(fs, path),
|
|
9107
|
-
stat: async (path) => counted2(await fs.stat(path)),
|
|
9108
|
-
lstat: async (path) => counted2(await fs.lstat(path)),
|
|
9109
|
-
async rm(path, options) {
|
|
9110
|
-
const target = fs.resolvePath("/", path);
|
|
9111
|
-
const pinned = await keeper.pin(target);
|
|
9112
|
-
if (pinned.moved.has(target)) pinned.commit();
|
|
9113
|
-
else await keeper.settle(pinned, () => fs.rm(path, options));
|
|
9114
|
-
},
|
|
9115
|
-
async rename(from, to) {
|
|
9116
|
-
const target = fs.resolvePath("/", to);
|
|
9117
|
-
if (target === fs.resolvePath("/", from)) return fs.rename(from, to);
|
|
9118
|
-
const pinned = await keeper.pin(target);
|
|
9119
|
-
await keeper.settle(pinned, () => fs.rename(from, to));
|
|
9120
|
-
},
|
|
9121
|
-
readdir: async (path) => visible(await fs.readdir(path)),
|
|
9122
|
-
readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR).map(([name2, st]) => [name2, st && counted2(st)])
|
|
9123
|
-
};
|
|
9124
|
-
}
|
|
9125
|
-
async settle(pinned, op) {
|
|
9126
|
-
try {
|
|
9127
|
-
await op();
|
|
9128
|
-
} catch (err) {
|
|
9129
|
-
await pinned.rollback();
|
|
9130
|
-
throw err;
|
|
9131
|
-
}
|
|
9132
|
-
pinned.commit();
|
|
9133
|
-
}
|
|
9134
|
-
async pin(target) {
|
|
9135
|
-
const held = /* @__PURE__ */ new Map();
|
|
9136
|
-
for (const holder of this.holders) {
|
|
9137
|
-
if (holder.owns(target)) continue;
|
|
9138
|
-
for (const path of holder.held()) {
|
|
9139
|
-
if (!within5(path, target) || holder.isKept(path)) continue;
|
|
9140
|
-
held.set(path, [...held.get(path) ?? [], holder]);
|
|
9141
|
-
}
|
|
9142
|
-
}
|
|
9143
|
-
const paths = [...held.keys()].sort();
|
|
9144
|
-
const releases = [];
|
|
9145
|
-
for (const path of paths) releases.push(await this.gate.acquire(path));
|
|
9146
|
-
const hidden = /* @__PURE__ */ new Map();
|
|
9147
|
-
for (const path of paths) {
|
|
9148
|
-
const at = await this.hide(path);
|
|
9149
|
-
if (at !== void 0) hidden.set(path, at);
|
|
9150
|
-
}
|
|
9151
|
-
const done4 = () => {
|
|
9152
|
-
for (const release of releases) release();
|
|
9153
|
-
};
|
|
9154
|
-
const files = /* @__PURE__ */ new Map();
|
|
9155
|
-
for (const [path, at] of hidden) {
|
|
9156
|
-
const holders = held.get(path);
|
|
9157
|
-
const file = this.keptFile(at, { refs: holders.length + 1 });
|
|
9158
|
-
files.set(path, file);
|
|
9159
|
-
for (const holder of holders) holder.keep(path, file);
|
|
9160
|
-
}
|
|
9161
|
-
return {
|
|
9162
|
-
moved: new Set(hidden.keys()),
|
|
9163
|
-
commit: () => {
|
|
9164
|
-
for (const file of files.values()) file.release();
|
|
9165
|
-
done4();
|
|
9166
|
-
},
|
|
9167
|
-
rollback: async () => {
|
|
9168
|
-
for (const [path, file] of files) {
|
|
9169
|
-
for (const holder of held.get(path)) holder.unkeep(path, file);
|
|
9170
|
-
await this.serial(async () => {
|
|
9171
|
-
await Promise.allSettled([this.fs.rename(file.hidden, path)]);
|
|
9172
|
-
this.unlinkedName(file.hidden);
|
|
9173
|
-
await this.tidy(parentOf5(file.hidden));
|
|
9174
|
-
});
|
|
9175
|
-
}
|
|
9176
|
-
done4();
|
|
9177
|
-
}
|
|
9178
|
-
};
|
|
9179
|
-
}
|
|
9180
|
-
keptFile(hidden, entry) {
|
|
9181
|
-
return {
|
|
9182
|
-
hidden,
|
|
9183
|
-
release: () => {
|
|
9184
|
-
if (--entry.refs === 0) void this.serial(() => this.drop(hidden));
|
|
9185
|
-
}
|
|
9186
|
-
};
|
|
9187
|
-
}
|
|
9188
|
-
hide(path) {
|
|
9189
|
-
return this.serial(async () => {
|
|
9190
|
-
const dir = dirOf(this.rootOf(path));
|
|
9191
|
-
const hidden = `${dir}/${this.instance}.${++this.count}`;
|
|
9192
|
-
try {
|
|
9193
|
-
const owner = this.dirOwner;
|
|
9194
|
-
await this.fs.mkdir(dir, { recursive: true, ...owner ? { owner } : {} });
|
|
9195
|
-
await this.fs.rename(path, hidden);
|
|
9196
|
-
await this.linkedName(hidden);
|
|
9197
|
-
return hidden;
|
|
9198
|
-
} catch {
|
|
9199
|
-
await this.tidy(dir);
|
|
9200
|
-
return void 0;
|
|
9201
|
-
}
|
|
9202
|
-
});
|
|
9203
|
-
}
|
|
9204
|
-
closed(path) {
|
|
9205
|
-
if (this.waiting.delete(path)) void this.serial(() => this.drop(path));
|
|
9206
|
-
}
|
|
9207
|
-
async drop(hidden) {
|
|
9208
|
-
if (this.busy(hidden)) {
|
|
9209
|
-
this.waiting.add(hidden);
|
|
9210
|
-
return;
|
|
9211
|
-
}
|
|
9212
|
-
await this.fs.rm(hidden).catch(() => void 0);
|
|
9213
|
-
this.unlinkedName(hidden);
|
|
9214
|
-
await this.tidy(parentOf5(hidden));
|
|
9215
|
-
}
|
|
9216
|
-
async linkedName(hidden) {
|
|
9217
|
-
if (!mayLink(this.fs, hidden)) return;
|
|
9218
|
-
const st = await this.fs.lstat(hidden).catch(() => void 0);
|
|
9219
|
-
if (st?.nlink === void 0) return;
|
|
9220
|
-
const id2 = `${st.dev ?? 0}:${st.ino}`;
|
|
9221
|
-
this.linked.set(hidden, id2);
|
|
9222
|
-
this.links.set(id2, (this.links.get(id2) ?? 0) + 1);
|
|
9223
|
-
}
|
|
9224
|
-
unlinkedName(hidden) {
|
|
9225
|
-
const id2 = this.linked.get(hidden);
|
|
9226
|
-
if (id2 === void 0) return;
|
|
9227
|
-
this.linked.delete(hidden);
|
|
9228
|
-
const left = (this.links.get(id2) ?? 1) - 1;
|
|
9229
|
-
if (left > 0) this.links.set(id2, left);
|
|
9230
|
-
else this.links.delete(id2);
|
|
9231
|
-
}
|
|
9232
|
-
async tidy(dir) {
|
|
9233
|
-
const left = await this.fs.readdir(dir).catch(() => [UNLINKED_DIR]);
|
|
9234
|
-
if (left.length === 0) await this.fs.rm(dir, { recursive: true }).catch(() => void 0);
|
|
9235
|
-
}
|
|
9236
|
-
sweep(root) {
|
|
9237
|
-
const query = this.locks?.query?.bind(this.locks);
|
|
9238
|
-
const owner = this.owner;
|
|
9239
|
-
if (!query && !owner) return Promise.resolve();
|
|
9240
|
-
return this.serial(async () => {
|
|
9241
|
-
const dir = dirOf(root);
|
|
9242
|
-
const names = await this.fs.readdir(dir).catch(() => void 0);
|
|
9243
|
-
if (!names) return;
|
|
9244
|
-
const held = query && new Set(((await query()).held ?? []).map((lock) => lock.name));
|
|
9245
|
-
for (const name2 of names) {
|
|
9246
|
-
const id2 = name2.slice(0, name2.lastIndexOf("."));
|
|
9247
|
-
const at = id2.indexOf("~");
|
|
9248
|
-
const dead = id2 !== this.instance && (at < 0 ? held !== void 0 && !held.has(lockName(id2)) : owner?.alive(id2.slice(0, at)) === false);
|
|
9249
|
-
if (dead) await this.fs.rm(`${dir}/${name2}`, { recursive: true }).catch(() => void 0);
|
|
9250
|
-
}
|
|
9251
|
-
await this.tidy(dir);
|
|
9252
|
-
});
|
|
9253
|
-
}
|
|
9254
|
-
};
|
|
9255
|
-
|
|
9256
9823
|
// src/fs/watch.ts
|
|
9257
9824
|
var WATCH_BATCH_LIMIT = 1e3;
|
|
9258
9825
|
var under3 = (path, dir) => dir === "/" || path.startsWith(`${dir}/`);
|
|
@@ -9393,8 +9960,8 @@ var FsWatchers = class {
|
|
|
9393
9960
|
return new Proxy(fs, {
|
|
9394
9961
|
get(target, key, receiver) {
|
|
9395
9962
|
if (Object.hasOwn(overrides, key)) return overrides[key];
|
|
9396
|
-
const
|
|
9397
|
-
return typeof
|
|
9963
|
+
const value2 = Reflect.get(target, key, receiver);
|
|
9964
|
+
return typeof value2 === "function" ? value2.bind(target) : value2;
|
|
9398
9965
|
}
|
|
9399
9966
|
});
|
|
9400
9967
|
}
|
|
@@ -9451,7 +10018,7 @@ function captureFile(onData, control) {
|
|
|
9451
10018
|
close: () => control.ended()
|
|
9452
10019
|
});
|
|
9453
10020
|
}
|
|
9454
|
-
function
|
|
10021
|
+
function concat4(chunks) {
|
|
9455
10022
|
const out = new Uint8Array(chunks.reduce((n, c) => n + c.length, 0));
|
|
9456
10023
|
let offset = 0;
|
|
9457
10024
|
for (const chunk2 of chunks) {
|
|
@@ -9621,7 +10188,7 @@ var ChildTable = class {
|
|
|
9621
10188
|
if (!slots2 || !chunks) return new Uint8Array(0);
|
|
9622
10189
|
slots2.delete(slot2);
|
|
9623
10190
|
if (slots2.size === 0) this.leftovers.delete(pid);
|
|
9624
|
-
return
|
|
10191
|
+
return concat4(chunks);
|
|
9625
10192
|
}
|
|
9626
10193
|
};
|
|
9627
10194
|
|
|
@@ -9635,12 +10202,12 @@ async function argvDigest(argv) {
|
|
|
9635
10202
|
const hash = new Uint8Array(await crypto.subtle.digest("SHA-256", bytes));
|
|
9636
10203
|
return [...hash].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
9637
10204
|
}
|
|
9638
|
-
function isStrings(
|
|
9639
|
-
return Array.isArray(
|
|
10205
|
+
function isStrings(value2) {
|
|
10206
|
+
return Array.isArray(value2) && value2.every((item) => typeof item === "string");
|
|
9640
10207
|
}
|
|
9641
|
-
function stringRecord(
|
|
9642
|
-
if (typeof
|
|
9643
|
-
const entries = Object.entries(
|
|
10208
|
+
function stringRecord(value2) {
|
|
10209
|
+
if (typeof value2 !== "object" || value2 === null || Array.isArray(value2)) return void 0;
|
|
10210
|
+
const entries = Object.entries(value2);
|
|
9644
10211
|
return entries.every(([, v]) => typeof v === "string") ? Object.fromEntries(entries) : void 0;
|
|
9645
10212
|
}
|
|
9646
10213
|
var TicketBook = class {
|
|
@@ -9800,14 +10367,14 @@ var DriverConnection = class _DriverConnection {
|
|
|
9800
10367
|
var S_ISGID4 = 1024;
|
|
9801
10368
|
var ID = /^\d{1,10}$/;
|
|
9802
10369
|
var MAX_ID2 = 4294967294;
|
|
9803
|
-
function idOption(
|
|
9804
|
-
return
|
|
10370
|
+
function idOption(value2) {
|
|
10371
|
+
return value2 === void 0 ? 0 : Number(value2);
|
|
9805
10372
|
}
|
|
9806
10373
|
function checkIds(options) {
|
|
9807
10374
|
for (const key of ["uid", "gid"]) {
|
|
9808
|
-
const
|
|
9809
|
-
if (
|
|
9810
|
-
if (!ID.test(
|
|
10375
|
+
const value2 = options[key];
|
|
10376
|
+
if (value2 === void 0) continue;
|
|
10377
|
+
if (!ID.test(value2) || Number(value2) > MAX_ID2) throw errnoError("EINVAL", `${key}=${value2}`);
|
|
9811
10378
|
}
|
|
9812
10379
|
}
|
|
9813
10380
|
var MAX_LINKS7 = 40;
|
|
@@ -10021,7 +10588,7 @@ function statOf(attr2, path, mount) {
|
|
|
10021
10588
|
} : {}
|
|
10022
10589
|
};
|
|
10023
10590
|
}
|
|
10024
|
-
function
|
|
10591
|
+
function concat5(chunks, total) {
|
|
10025
10592
|
if (chunks.length === 1 && chunks[0]?.length === total) return chunks[0];
|
|
10026
10593
|
const out = new Uint8Array(total);
|
|
10027
10594
|
let at = 0;
|
|
@@ -10528,7 +11095,7 @@ var MountFs = class _MountFs {
|
|
|
10528
11095
|
} finally {
|
|
10529
11096
|
await mount.conn.call({ op: "release", fh });
|
|
10530
11097
|
}
|
|
10531
|
-
return
|
|
11098
|
+
return concat5(chunks, total);
|
|
10532
11099
|
}
|
|
10533
11100
|
async writeFile(path, content, owner, redirects = 0) {
|
|
10534
11101
|
const real = await this.follow(path, 0, true);
|
|
@@ -10623,7 +11190,7 @@ var MountFs = class _MountFs {
|
|
|
10623
11190
|
} finally {
|
|
10624
11191
|
await mount.conn.call({ op: "release", fh });
|
|
10625
11192
|
}
|
|
10626
|
-
return
|
|
11193
|
+
return concat5(chunks, total);
|
|
10627
11194
|
}
|
|
10628
11195
|
fits(mount, real, end) {
|
|
10629
11196
|
if (mount.maxFile > 0 && end > mount.maxFile) {
|
|
@@ -11036,7 +11603,7 @@ var MountFs = class _MountFs {
|
|
|
11036
11603
|
await mount.conn.call({
|
|
11037
11604
|
op: "setattr",
|
|
11038
11605
|
path: rel2,
|
|
11039
|
-
...Object.fromEntries(attrs.filter(([,
|
|
11606
|
+
...Object.fromEntries(attrs.filter(([, value2]) => value2 !== void 0))
|
|
11040
11607
|
});
|
|
11041
11608
|
}
|
|
11042
11609
|
async chmod(path, mode, redirects = 0) {
|
|
@@ -11215,8 +11782,8 @@ async function storageUsage() {
|
|
|
11215
11782
|
}
|
|
11216
11783
|
var PLAIN_DIR_MODE = 493;
|
|
11217
11784
|
var done = { ok: true, kind: "void" };
|
|
11218
|
-
function json2(
|
|
11219
|
-
return { ok: true, kind: "json", json:
|
|
11785
|
+
function json2(value2) {
|
|
11786
|
+
return { ok: true, kind: "json", json: value2 };
|
|
11220
11787
|
}
|
|
11221
11788
|
function ownerFor2(entry, mode) {
|
|
11222
11789
|
const cred = entry.cred?.();
|
|
@@ -11775,9 +12342,9 @@ function storeBlocked(header3, [low, high]) {
|
|
|
11775
12342
|
}
|
|
11776
12343
|
var anyBlocked = (mask) => mask !== void 0 && (mask[0] !== 0 || mask[1] !== 0);
|
|
11777
12344
|
var U32 = 2 ** 32;
|
|
11778
|
-
function storeU53(header3, lo, hi,
|
|
11779
|
-
Atomics.store(header3, hi, Math.floor(
|
|
11780
|
-
Atomics.store(header3, lo,
|
|
12345
|
+
function storeU53(header3, lo, hi, value2) {
|
|
12346
|
+
Atomics.store(header3, hi, Math.floor(value2 / U32));
|
|
12347
|
+
Atomics.store(header3, lo, value2 % U32 | 0);
|
|
11781
12348
|
}
|
|
11782
12349
|
var SAB_STATE_READY = 2;
|
|
11783
12350
|
var SAB_STATUS_BYTES = 1;
|
|
@@ -11788,20 +12355,20 @@ var SAB_DEFAULT_WINDOW_BYTES = 1024 * 1024;
|
|
|
11788
12355
|
var SAB_MIN_BYTES = SAB_HEADER_BYTES + 4096;
|
|
11789
12356
|
var SYNC_SAB_REQ_MSG = "sync-sab-req";
|
|
11790
12357
|
var SYNC_SAB_NEXT_MSG = "sync-sab-next";
|
|
11791
|
-
var
|
|
12358
|
+
var encoder3 = new TextEncoder();
|
|
11792
12359
|
var decoder2 = new TextDecoder();
|
|
11793
12360
|
function encodeSabResult(result) {
|
|
11794
12361
|
if (!result.ok) {
|
|
11795
12362
|
return {
|
|
11796
12363
|
status: SAB_STATUS_ERRNO,
|
|
11797
|
-
payload:
|
|
12364
|
+
payload: encoder3.encode(JSON.stringify({ errno: result.errno, message: result.message }))
|
|
11798
12365
|
};
|
|
11799
12366
|
}
|
|
11800
12367
|
switch (result.kind) {
|
|
11801
12368
|
case "bytes":
|
|
11802
12369
|
return { status: SAB_STATUS_BYTES, payload: result.bytes };
|
|
11803
12370
|
case "json":
|
|
11804
|
-
return { status: SAB_STATUS_JSON, payload:
|
|
12371
|
+
return { status: SAB_STATUS_JSON, payload: encoder3.encode(JSON.stringify(result.json)) };
|
|
11805
12372
|
default:
|
|
11806
12373
|
return { status: SAB_STATUS_VOID, payload: new Uint8Array(0) };
|
|
11807
12374
|
}
|
|
@@ -11909,14 +12476,14 @@ function attachSyncSabResponder(port, sab, token, opts) {
|
|
|
11909
12476
|
|
|
11910
12477
|
// src/kernel/cpu-clock.ts
|
|
11911
12478
|
var SYS_CAP_US = 2e4;
|
|
11912
|
-
var
|
|
12479
|
+
var LOCAL2 = SAB_CLOCK_OFFSET;
|
|
11913
12480
|
var BLOCKED = 0;
|
|
11914
12481
|
var IDLE = 1;
|
|
11915
12482
|
function epochMicros() {
|
|
11916
12483
|
return Math.floor((performance.timeOrigin + performance.now()) * 1e3);
|
|
11917
12484
|
}
|
|
11918
12485
|
function slots(sab) {
|
|
11919
|
-
return new BigInt64Array(sab,
|
|
12486
|
+
return new BigInt64Array(sab, LOCAL2, SAB_CLOCK_I64);
|
|
11920
12487
|
}
|
|
11921
12488
|
var rawWait = Atomics.wait.bind(Atomics);
|
|
11922
12489
|
function localIdle(sab, now) {
|
|
@@ -12291,14 +12858,14 @@ var KernelDgram = class {
|
|
|
12291
12858
|
}
|
|
12292
12859
|
return this.options.get(`${level}:${name2}`) ?? 0;
|
|
12293
12860
|
}
|
|
12294
|
-
setOption(level, name2,
|
|
12295
|
-
if (level === SOL_SOCKET2 && name2 === SO_BROADCAST &&
|
|
12861
|
+
setOption(level, name2, value2) {
|
|
12862
|
+
if (level === SOL_SOCKET2 && name2 === SO_BROADCAST && value2 !== 0) {
|
|
12296
12863
|
throw new KernelError("EOPNOTSUPP");
|
|
12297
12864
|
}
|
|
12298
12865
|
if (level === IPPROTO_IP && name2 >= IP_MULTICAST_FIRST && name2 <= IP_MULTICAST_LAST) {
|
|
12299
12866
|
throw new KernelError("ENOPROTOOPT");
|
|
12300
12867
|
}
|
|
12301
|
-
this.options.set(`${level}:${name2}`,
|
|
12868
|
+
this.options.set(`${level}:${name2}`, value2);
|
|
12302
12869
|
}
|
|
12303
12870
|
close() {
|
|
12304
12871
|
if (this.closed) return;
|
|
@@ -12557,7 +13124,7 @@ var PATH_OPS = new Set(SYNC_FS_OPS);
|
|
|
12557
13124
|
var EARLY_WHOLE_FILE = 64 * 1024;
|
|
12558
13125
|
|
|
12559
13126
|
// src/process/process-fds.ts
|
|
12560
|
-
var
|
|
13127
|
+
var field3 = (text) => (text || "none").replace(/[\s\\]/g, (c) => `\\${c.charCodeAt(0).toString(8).padStart(3, "0")}`);
|
|
12561
13128
|
var pinnedRoot;
|
|
12562
13129
|
var rootKind = () => globalThis.navigator?.storage ? "opfs" : "memory";
|
|
12563
13130
|
function mountTable(mounts = []) {
|
|
@@ -12568,7 +13135,7 @@ function mountTable(mounts = []) {
|
|
|
12568
13135
|
if (m.state !== "ok") options.push(m.state);
|
|
12569
13136
|
const access2 = options.includes("ro") ? [] : ["rw"];
|
|
12570
13137
|
lines2.push(
|
|
12571
|
-
`${
|
|
13138
|
+
`${field3(m.source)} ${field3(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
|
|
12572
13139
|
);
|
|
12573
13140
|
}
|
|
12574
13141
|
return `${lines2.join("\n")}
|
|
@@ -12724,7 +13291,7 @@ function memoryInfo(source, used = 0) {
|
|
|
12724
13291
|
["SReclaimable", kb(0)],
|
|
12725
13292
|
["SwapTotal", kb(0)],
|
|
12726
13293
|
["SwapFree", kb(0)]
|
|
12727
|
-
].map(([key,
|
|
13294
|
+
].map(([key, value2]) => `${`${key}:`.padEnd(16)}${value2.padStart(12)}`).concat("").join("\n");
|
|
12728
13295
|
}
|
|
12729
13296
|
function systemFile(name2, listing, now, memory) {
|
|
12730
13297
|
if (name2 === "version") return procVersion();
|
|
@@ -12979,15 +13546,15 @@ async function procAlias(fs, path, follow2) {
|
|
|
12979
13546
|
);
|
|
12980
13547
|
if (!physical) return void 0;
|
|
12981
13548
|
const walk2 = procWalk(path, follow2);
|
|
12982
|
-
let
|
|
12983
|
-
while (!
|
|
12984
|
-
const { at, check } =
|
|
13549
|
+
let step2 = walk2.next();
|
|
13550
|
+
while (!step2.done) {
|
|
13551
|
+
const { at, check } = step2.value;
|
|
12985
13552
|
const link2 = await readlink.call(fs, at).catch(() => void 0);
|
|
12986
13553
|
const isDir = (st) => st.isDirectory === true;
|
|
12987
13554
|
const target = link2 ?? (check ? await lstat.call(fs, at).then(isDir, failed) : true);
|
|
12988
|
-
|
|
13555
|
+
step2 = walk2.next(target);
|
|
12989
13556
|
}
|
|
12990
|
-
return
|
|
13557
|
+
return step2.value;
|
|
12991
13558
|
}
|
|
12992
13559
|
|
|
12993
13560
|
// src/kernel/eventfd.ts
|
|
@@ -13033,23 +13600,23 @@ var EventFd = class _EventFd {
|
|
|
13033
13600
|
blocked(signal);
|
|
13034
13601
|
await this.changes.wait(signal);
|
|
13035
13602
|
}
|
|
13036
|
-
const
|
|
13037
|
-
this.count -=
|
|
13603
|
+
const value2 = this.semaphore ? 1n : this.count;
|
|
13604
|
+
this.count -= value2;
|
|
13038
13605
|
this.changes.wake();
|
|
13039
13606
|
const out = new Uint8Array(_EventFd.SIZE);
|
|
13040
|
-
new DataView(out.buffer).setBigUint64(0,
|
|
13607
|
+
new DataView(out.buffer).setBigUint64(0, value2, true);
|
|
13041
13608
|
return out;
|
|
13042
13609
|
}
|
|
13043
13610
|
async write(bytes, signal) {
|
|
13044
13611
|
if (bytes.length < _EventFd.SIZE) throw new KernelError("EINVAL");
|
|
13045
|
-
const
|
|
13046
|
-
if (
|
|
13047
|
-
while (EVENTFD_MAX - this.count <
|
|
13612
|
+
const value2 = new DataView(bytes.buffer, bytes.byteOffset, _EventFd.SIZE).getBigUint64(0, true);
|
|
13613
|
+
if (value2 > EVENTFD_MAX) throw new KernelError("EINVAL");
|
|
13614
|
+
while (EVENTFD_MAX - this.count < value2) {
|
|
13048
13615
|
blocked(signal);
|
|
13049
13616
|
await this.changes.wait(signal);
|
|
13050
13617
|
}
|
|
13051
|
-
this.count +=
|
|
13052
|
-
if (
|
|
13618
|
+
this.count += value2;
|
|
13619
|
+
if (value2 > 0n) this.changes.wake();
|
|
13053
13620
|
return _EventFd.SIZE;
|
|
13054
13621
|
}
|
|
13055
13622
|
poll() {
|
|
@@ -13336,10 +13903,10 @@ var LockTable = class {
|
|
|
13336
13903
|
}
|
|
13337
13904
|
set(key, owner, kind, start2, end) {
|
|
13338
13905
|
if (kind !== "unlock" && this.holder(key, owner, { key, kind, start: start2, end })) return false;
|
|
13339
|
-
const
|
|
13906
|
+
const range2 = { start: start2, end };
|
|
13340
13907
|
const kept = [];
|
|
13341
13908
|
for (const lock of this.records.get(key) ?? []) {
|
|
13342
|
-
if (lock.owner !== owner || !overlaps(lock,
|
|
13909
|
+
if (lock.owner !== owner || !overlaps(lock, range2)) kept.push(lock);
|
|
13343
13910
|
else {
|
|
13344
13911
|
if (lock.start < start2) kept.push({ ...lock, end: start2 });
|
|
13345
13912
|
if (lock.end > end) kept.push({ ...lock, start: end });
|
|
@@ -13350,13 +13917,13 @@ var LockTable = class {
|
|
|
13350
13917
|
this.wake(key);
|
|
13351
13918
|
return true;
|
|
13352
13919
|
}
|
|
13353
|
-
async wait(key, owner, kind,
|
|
13354
|
-
const want = { key, kind, ...
|
|
13920
|
+
async wait(key, owner, kind, range2, blocking) {
|
|
13921
|
+
const want = { key, kind, ...range2 };
|
|
13355
13922
|
let signal;
|
|
13356
13923
|
for (; ; ) {
|
|
13357
13924
|
const holder = this.holder(key, owner, want);
|
|
13358
13925
|
if (!holder) {
|
|
13359
|
-
this.set(key, owner, kind,
|
|
13926
|
+
this.set(key, owner, kind, range2.start, range2.end);
|
|
13360
13927
|
return;
|
|
13361
13928
|
}
|
|
13362
13929
|
if (this.waitsOn(holder.owner, owner)) throw new KernelError("EDEADLK");
|
|
@@ -13479,7 +14046,7 @@ var HOST_OPS = [
|
|
|
13479
14046
|
"async-settle",
|
|
13480
14047
|
"async-close"
|
|
13481
14048
|
];
|
|
13482
|
-
var json3 = (
|
|
14049
|
+
var json3 = (value2) => ({ ok: true, kind: "json", json: value2 });
|
|
13483
14050
|
var done2 = { ok: true, kind: "void" };
|
|
13484
14051
|
var KEPT_OPS = /* @__PURE__ */ new Set(["fd-write"]);
|
|
13485
14052
|
var unknownSettled = false;
|
|
@@ -13509,9 +14076,9 @@ var AsyncOps = class {
|
|
|
13509
14076
|
);
|
|
13510
14077
|
return id2;
|
|
13511
14078
|
}
|
|
13512
|
-
resolve(
|
|
14079
|
+
resolve(value2) {
|
|
13513
14080
|
const id2 = this.hold();
|
|
13514
|
-
this.complete(id2, json3(
|
|
14081
|
+
this.complete(id2, json3(value2));
|
|
13515
14082
|
return id2;
|
|
13516
14083
|
}
|
|
13517
14084
|
hold() {
|
|
@@ -13656,19 +14223,19 @@ async function recordLock2(req, ctx) {
|
|
|
13656
14223
|
locks.dropFile(await fileKey(target, "close"), owner);
|
|
13657
14224
|
return done2;
|
|
13658
14225
|
}
|
|
13659
|
-
const
|
|
14226
|
+
const range2 = lockRange(req.start, req.len);
|
|
13660
14227
|
const key = await fileKey(target, req.kind, req.op === "lk-set" && req.fd !== void 0);
|
|
13661
14228
|
if (req.op === "lk-get") {
|
|
13662
|
-
const held = locks.holder(key, owner, { key, kind: req.kind, ...
|
|
14229
|
+
const held = locks.holder(key, owner, { key, kind: req.kind, ...range2 });
|
|
13663
14230
|
if (!held) return json3(null);
|
|
13664
14231
|
const len = held.end === Number.POSITIVE_INFINITY ? 0 : held.end - held.start;
|
|
13665
14232
|
return json3({ kind: held.kind, pid: held.owner, start: held.start, len });
|
|
13666
14233
|
}
|
|
13667
14234
|
if (req.kind !== "unlock" && req.wait) {
|
|
13668
|
-
await locks.wait(key, owner, req.kind,
|
|
14235
|
+
await locks.wait(key, owner, req.kind, range2, ctx.blocking);
|
|
13669
14236
|
return done2;
|
|
13670
14237
|
}
|
|
13671
|
-
if (!locks.set(key, owner, req.kind,
|
|
14238
|
+
if (!locks.set(key, owner, req.kind, range2.start, range2.end)) throw new KernelError("EAGAIN");
|
|
13672
14239
|
return done2;
|
|
13673
14240
|
}
|
|
13674
14241
|
|
|
@@ -13704,13 +14271,13 @@ function errno(code, message3) {
|
|
|
13704
14271
|
function requestHead(method, url, headers, extra, first = []) {
|
|
13705
14272
|
if (!TOKEN3.test(method)) throw errno("EINVAL", `method ${method}`);
|
|
13706
14273
|
const lines2 = [`${method} ${url.pathname}${url.search} HTTP/1.1`, `host: ${url.host}`];
|
|
13707
|
-
for (const [name2,
|
|
13708
|
-
for (const [name2,
|
|
14274
|
+
for (const [name2, value2] of first) lines2.push(`${name2}: ${value2}`);
|
|
14275
|
+
for (const [name2, value2] of headers) {
|
|
13709
14276
|
if (OWN_HEADERS.has(name2.toLowerCase())) continue;
|
|
13710
|
-
if (!TOKEN3.test(name2) || !SAFE_VALUE.test(
|
|
13711
|
-
lines2.push(`${name2}: ${
|
|
14277
|
+
if (!TOKEN3.test(name2) || !SAFE_VALUE.test(value2)) throw errno("EINVAL", `header ${name2}`);
|
|
14278
|
+
lines2.push(`${name2}: ${value2}`);
|
|
13712
14279
|
}
|
|
13713
|
-
for (const [name2,
|
|
14280
|
+
for (const [name2, value2] of extra) lines2.push(`${name2}: ${value2}`);
|
|
13714
14281
|
return latin1Bytes(`${lines2.join("\r\n")}\r
|
|
13715
14282
|
\r
|
|
13716
14283
|
`);
|
|
@@ -14487,11 +15054,11 @@ function waitFor(lookup2, signal) {
|
|
|
14487
15054
|
reject(new KernelError("EINTR"));
|
|
14488
15055
|
};
|
|
14489
15056
|
signal?.addEventListener("abort", abort, { once: true });
|
|
14490
|
-
void lookup2.work.then((
|
|
15057
|
+
void lookup2.work.then((value2) => {
|
|
14491
15058
|
if (signal?.aborted) return;
|
|
14492
15059
|
lookup2.waiters--;
|
|
14493
15060
|
signal?.removeEventListener("abort", abort);
|
|
14494
|
-
resolve2(
|
|
15061
|
+
resolve2(value2);
|
|
14495
15062
|
});
|
|
14496
15063
|
});
|
|
14497
15064
|
}
|
|
@@ -14528,7 +15095,7 @@ function defaultTermios() {
|
|
|
14528
15095
|
c_cc: cCc
|
|
14529
15096
|
};
|
|
14530
15097
|
}
|
|
14531
|
-
var
|
|
15098
|
+
var encoder4 = new TextEncoder();
|
|
14532
15099
|
function isContinuation(byte) {
|
|
14533
15100
|
return (byte & 192) === 128;
|
|
14534
15101
|
}
|
|
@@ -14651,7 +15218,7 @@ var KernelTty = class {
|
|
|
14651
15218
|
this.line.push(byte);
|
|
14652
15219
|
this.readable.push(Uint8Array.from(this.line));
|
|
14653
15220
|
this.line = [];
|
|
14654
|
-
if (echo) this.output(
|
|
15221
|
+
if (echo) this.output(encoder4.encode("\n"));
|
|
14655
15222
|
} else {
|
|
14656
15223
|
this.line.push(byte);
|
|
14657
15224
|
if (echo) this.echo(byte);
|
|
@@ -14676,7 +15243,7 @@ var KernelTty = class {
|
|
|
14676
15243
|
const utf83 = (this.termios.c_iflag & IUTF8) !== 0;
|
|
14677
15244
|
const cells = utf83 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
|
|
14678
15245
|
if (cells > 0 && this.termios.c_lflag & ECHO) {
|
|
14679
|
-
this.output(
|
|
15246
|
+
this.output(encoder4.encode("\b \b".repeat(cells)), false);
|
|
14680
15247
|
}
|
|
14681
15248
|
}
|
|
14682
15249
|
echo(byte) {
|
|
@@ -14928,7 +15495,7 @@ function terminalOf(ctx, fd) {
|
|
|
14928
15495
|
if (!tty) throw new KernelError("ENOTTY");
|
|
14929
15496
|
return tty;
|
|
14930
15497
|
}
|
|
14931
|
-
var json4 = (
|
|
15498
|
+
var json4 = (value2) => ({ ok: true, kind: "json", json: value2 });
|
|
14932
15499
|
var done3 = { ok: true, kind: "void" };
|
|
14933
15500
|
function sizeKey(rows, cols) {
|
|
14934
15501
|
return `${rows}x${cols}`;
|
|
@@ -15144,8 +15711,8 @@ var Semaphore = class {
|
|
|
15144
15711
|
gid;
|
|
15145
15712
|
mode;
|
|
15146
15713
|
waiters = [];
|
|
15147
|
-
constructor(
|
|
15148
|
-
this.value =
|
|
15714
|
+
constructor(value2, uid, gid, mode) {
|
|
15715
|
+
this.value = value2;
|
|
15149
15716
|
this.uid = uid;
|
|
15150
15717
|
this.gid = gid;
|
|
15151
15718
|
this.mode = mode;
|
|
@@ -15770,10 +16337,10 @@ var VfsNodes = class {
|
|
|
15770
16337
|
async onEntry(path, op) {
|
|
15771
16338
|
const key = await this.entryKey(path);
|
|
15772
16339
|
const run2 = (this.entries.get(key) ?? Promise.resolve()).then(op);
|
|
15773
|
-
const
|
|
15774
|
-
this.entries.set(key,
|
|
15775
|
-
void
|
|
15776
|
-
if (this.entries.get(key) ===
|
|
16340
|
+
const tail2 = run2.catch(() => void 0);
|
|
16341
|
+
this.entries.set(key, tail2);
|
|
16342
|
+
void tail2.then(() => {
|
|
16343
|
+
if (this.entries.get(key) === tail2) this.entries.delete(key);
|
|
15777
16344
|
});
|
|
15778
16345
|
return run2;
|
|
15779
16346
|
}
|
|
@@ -16398,11 +16965,11 @@ var MAX_READ = 1024 * 1024;
|
|
|
16398
16965
|
var WRITE_ACCESS = 3;
|
|
16399
16966
|
var KEYBOARD = /* @__PURE__ */ new Set([SIG.INT, SIG.QUIT, SIG.TSTP]);
|
|
16400
16967
|
var DEFAULT_UMASK2 = 18;
|
|
16401
|
-
function words(
|
|
16402
|
-
if (!Array.isArray(
|
|
16968
|
+
function words(value2) {
|
|
16969
|
+
if (!Array.isArray(value2) || !value2.every((w) => typeof w === "string")) {
|
|
16403
16970
|
throw new KernelError("EINVAL");
|
|
16404
16971
|
}
|
|
16405
|
-
return [...
|
|
16972
|
+
return [...value2];
|
|
16406
16973
|
}
|
|
16407
16974
|
var ESCAPES = { " ": "\\t", "\n": "\\n", "\r": "\\r" };
|
|
16408
16975
|
function clipboardLine(text) {
|
|
@@ -18375,7 +18942,7 @@ function replayingHops(inner, heldMs = HELD_MS) {
|
|
|
18375
18942
|
clearTimeout(entry.timer);
|
|
18376
18943
|
void entry.final.cancel().catch(() => void 0);
|
|
18377
18944
|
};
|
|
18378
|
-
const
|
|
18945
|
+
const keyOf3 = (owner, method, url) => `${owner} ${method} ${url}`;
|
|
18379
18946
|
const hold = (key, chain2) => {
|
|
18380
18947
|
drop(key);
|
|
18381
18948
|
const mine = [...held.keys()].filter((k) => held.get(k)?.owner === chain2.owner);
|
|
@@ -18394,7 +18961,7 @@ function replayingHops(inner, heldMs = HELD_MS) {
|
|
|
18394
18961
|
const method = followedWith(hop.status, chain2.method);
|
|
18395
18962
|
const away = target.origin !== new URL(hop.url).origin;
|
|
18396
18963
|
const print = away ? unauthorized(chain2.fingerprint) : chain2.fingerprint;
|
|
18397
|
-
hold(
|
|
18964
|
+
hold(keyOf3(chain2.owner, method, target.href), {
|
|
18398
18965
|
...chain2,
|
|
18399
18966
|
method,
|
|
18400
18967
|
fingerprint: print,
|
|
@@ -18408,7 +18975,7 @@ function replayingHops(inner, heldMs = HELD_MS) {
|
|
|
18408
18975
|
},
|
|
18409
18976
|
...inner.websocket ? { websocket: inner.websocket.bind(inner) } : {},
|
|
18410
18977
|
async fetch(request2, owner = KERNEL) {
|
|
18411
|
-
const key =
|
|
18978
|
+
const key = keyOf3(owner, request2.method, request2.url);
|
|
18412
18979
|
const waiting = held.get(key);
|
|
18413
18980
|
if (waiting && waiting.fingerprint === fingerprint(request2.headers)) {
|
|
18414
18981
|
held.delete(key);
|
|
@@ -18433,10 +19000,10 @@ function replayingHops(inner, heldMs = HELD_MS) {
|
|
|
18433
19000
|
var FRAGMENT = 16;
|
|
18434
19001
|
var MIN_VALUE = 8;
|
|
18435
19002
|
var MIN_BASE64 = 8;
|
|
18436
|
-
var
|
|
19003
|
+
var encoder5 = new TextEncoder();
|
|
18437
19004
|
var STD = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
18438
19005
|
function utf82(text) {
|
|
18439
|
-
return
|
|
19006
|
+
return encoder5.encode(text);
|
|
18440
19007
|
}
|
|
18441
19008
|
function base642(bytes, padded = true) {
|
|
18442
19009
|
let out = "";
|
|
@@ -18468,30 +19035,30 @@ function embedded(bytes, offset) {
|
|
|
18468
19035
|
const last = Math.floor(8 * (offset + bytes.length) / 6);
|
|
18469
19036
|
return encoded.slice(first, last);
|
|
18470
19037
|
}
|
|
18471
|
-
function unicode(
|
|
19038
|
+
function unicode(value2, upper) {
|
|
18472
19039
|
let out = "";
|
|
18473
|
-
for (let i = 0; i <
|
|
18474
|
-
const unit =
|
|
19040
|
+
for (let i = 0; i < value2.length; i++) {
|
|
19041
|
+
const unit = value2.charCodeAt(i);
|
|
18475
19042
|
if (unit === 34) out += '\\"';
|
|
18476
19043
|
else if (unit === 92) out += "\\\\";
|
|
18477
19044
|
else if (unit < 32 || unit > 126) {
|
|
18478
19045
|
const code = unit.toString(16).padStart(4, "0");
|
|
18479
19046
|
out += `\\u${upper ? code.toUpperCase() : code}`;
|
|
18480
|
-
} else out +=
|
|
19047
|
+
} else out += value2[i];
|
|
18481
19048
|
}
|
|
18482
19049
|
return out;
|
|
18483
19050
|
}
|
|
18484
|
-
function percent(
|
|
19051
|
+
function percent(value2) {
|
|
18485
19052
|
let component;
|
|
18486
19053
|
try {
|
|
18487
|
-
component = encodeURIComponent(
|
|
19054
|
+
component = encodeURIComponent(value2);
|
|
18488
19055
|
} catch {
|
|
18489
19056
|
return [];
|
|
18490
19057
|
}
|
|
18491
19058
|
const lower = component.replace(/%[0-9A-F]{2}/g, (escape) => escape.toLowerCase());
|
|
18492
19059
|
const plus = component.replace(/%20/g, "+");
|
|
18493
19060
|
let form = "";
|
|
18494
|
-
for (const byte of utf82(
|
|
19061
|
+
for (const byte of utf82(value2)) {
|
|
18495
19062
|
const char = String.fromCharCode(byte);
|
|
18496
19063
|
if (/[A-Za-z0-9*\-._]/.test(char)) form += char;
|
|
18497
19064
|
else if (byte === 32) form += "+";
|
|
@@ -18499,8 +19066,8 @@ function percent(value) {
|
|
|
18499
19066
|
}
|
|
18500
19067
|
return [component, lower, plus, form];
|
|
18501
19068
|
}
|
|
18502
|
-
function html(
|
|
18503
|
-
return
|
|
19069
|
+
function html(value2, apostrophe) {
|
|
19070
|
+
return value2.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, apostrophe);
|
|
18504
19071
|
}
|
|
18505
19072
|
function encodings(bytes) {
|
|
18506
19073
|
const standard = [base642(bytes), base642(bytes, false)];
|
|
@@ -18510,9 +19077,9 @@ function encodings(bytes) {
|
|
|
18510
19077
|
}
|
|
18511
19078
|
return [...standard, ...standard.map(urlSafe)];
|
|
18512
19079
|
}
|
|
18513
|
-
function shapes(
|
|
18514
|
-
const raw = utf82(
|
|
18515
|
-
const seen = /* @__PURE__ */ new Set([
|
|
19080
|
+
function shapes(value2) {
|
|
19081
|
+
const raw = utf82(value2);
|
|
19082
|
+
const seen = /* @__PURE__ */ new Set([value2]);
|
|
18516
19083
|
const out = [];
|
|
18517
19084
|
if (raw.length < FRAGMENT) out.push({ bytes: raw, form: "raw" });
|
|
18518
19085
|
for (let offset = 0; raw.length >= FRAGMENT && offset + FRAGMENT <= raw.length; offset++) {
|
|
@@ -18523,17 +19090,17 @@ function shapes(value) {
|
|
|
18523
19090
|
seen.add(text);
|
|
18524
19091
|
out.push({ bytes: utf82(text), form });
|
|
18525
19092
|
};
|
|
18526
|
-
const json5 = JSON.stringify(
|
|
19093
|
+
const json5 = JSON.stringify(value2).slice(1, -1);
|
|
18527
19094
|
for (const text of [
|
|
18528
19095
|
json5,
|
|
18529
19096
|
json5.replace(/\//g, "\\/"),
|
|
18530
|
-
unicode(
|
|
18531
|
-
unicode(
|
|
19097
|
+
unicode(value2, false),
|
|
19098
|
+
unicode(value2, true)
|
|
18532
19099
|
]) {
|
|
18533
19100
|
add(text, "json");
|
|
18534
19101
|
}
|
|
18535
|
-
for (const text of percent(
|
|
18536
|
-
for (const text of [html(
|
|
19102
|
+
for (const text of percent(value2)) add(text, "percent");
|
|
19103
|
+
for (const text of [html(value2, "'"), html(value2, "'")]) add(text, "html");
|
|
18537
19104
|
for (const text of encodings(raw)) add(text, "base64");
|
|
18538
19105
|
add(hex2(raw), "hex");
|
|
18539
19106
|
add(hex2(raw).toUpperCase(), "hex");
|
|
@@ -18684,15 +19251,15 @@ function resolve(c, input, start2, end, source) {
|
|
|
18684
19251
|
growing: false
|
|
18685
19252
|
};
|
|
18686
19253
|
}
|
|
18687
|
-
const
|
|
19254
|
+
const value2 = c.values[source.secret];
|
|
18688
19255
|
let stop = end;
|
|
18689
19256
|
let at = source.offset + FRAGMENT;
|
|
18690
|
-
while (stop < input.length && at <
|
|
19257
|
+
while (stop < input.length && at < value2.length && input[stop] === value2[at]) {
|
|
18691
19258
|
stop++;
|
|
18692
19259
|
at++;
|
|
18693
19260
|
}
|
|
18694
|
-
const form = source.offset === 0 && at ===
|
|
18695
|
-
const growing = stop === input.length && at <
|
|
19261
|
+
const form = source.offset === 0 && at === value2.length ? "raw" : "fragment";
|
|
19262
|
+
const growing = stop === input.length && at < value2.length;
|
|
18696
19263
|
return { start: start2, end: stop, secret: source.secret, form, rank: RANK[form], growing };
|
|
18697
19264
|
}
|
|
18698
19265
|
function keep(best, candidate) {
|
|
@@ -18740,7 +19307,7 @@ function count(c, hits, candidate) {
|
|
|
18740
19307
|
hit.count += 1;
|
|
18741
19308
|
hits.set(key, hit);
|
|
18742
19309
|
}
|
|
18743
|
-
function
|
|
19310
|
+
function concat6(parts2) {
|
|
18744
19311
|
let length2 = 0;
|
|
18745
19312
|
for (const part of parts2) length2 += part.length;
|
|
18746
19313
|
const out = new Uint8Array(length2);
|
|
@@ -18782,7 +19349,7 @@ function pass(c, input, final) {
|
|
|
18782
19349
|
pos = candidate.end;
|
|
18783
19350
|
}
|
|
18784
19351
|
parts2.push(input.subarray(pos, cut));
|
|
18785
|
-
return { output:
|
|
19352
|
+
return { output: concat6(parts2), cut, hits };
|
|
18786
19353
|
}
|
|
18787
19354
|
function merge(into, from) {
|
|
18788
19355
|
for (const [key, hit] of from) {
|
|
@@ -18825,7 +19392,7 @@ function compile(secrets, options = {}) {
|
|
|
18825
19392
|
};
|
|
18826
19393
|
return {
|
|
18827
19394
|
push(chunk2) {
|
|
18828
|
-
pending =
|
|
19395
|
+
pending = concat6([pending, chunk2]);
|
|
18829
19396
|
return run2(false);
|
|
18830
19397
|
},
|
|
18831
19398
|
idle() {
|
|
@@ -18845,9 +19412,9 @@ function compile(secrets, options = {}) {
|
|
|
18845
19412
|
// src/kernel/scrub-file.ts
|
|
18846
19413
|
var SCRUB_IDLE_MS = 50;
|
|
18847
19414
|
var SCRUB_NONBLOCK_MAX = 64 * 1024;
|
|
18848
|
-
var
|
|
19415
|
+
var encoder6 = new TextEncoder();
|
|
18849
19416
|
function scrubberOf(secrets) {
|
|
18850
|
-
const usable = secrets.filter((s) =>
|
|
19417
|
+
const usable = secrets.filter((s) => encoder6.encode(s.value).length >= MIN_VALUE);
|
|
18851
19418
|
return usable.length > 0 ? compile(usable, { idleMs: SCRUB_IDLE_MS }) : void 0;
|
|
18852
19419
|
}
|
|
18853
19420
|
async function writeAll3(file, bytes) {
|
|
@@ -18865,7 +19432,7 @@ function interrupted2(err) {
|
|
|
18865
19432
|
function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
18866
19433
|
const file = inner.file;
|
|
18867
19434
|
const stream = scrubber.stream();
|
|
18868
|
-
let
|
|
19435
|
+
let tail2 = Promise.resolve();
|
|
18869
19436
|
let pending = 0;
|
|
18870
19437
|
let busy = false;
|
|
18871
19438
|
let timer;
|
|
@@ -18884,7 +19451,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
18884
19451
|
const later = (bytes) => {
|
|
18885
19452
|
if (bytes.length === 0) return;
|
|
18886
19453
|
pending += 1;
|
|
18887
|
-
|
|
19454
|
+
tail2 = tail2.then(() => writeAll3(file, bytes)).catch((err) => {
|
|
18888
19455
|
if (failed3 === void 0) failed3 = err;
|
|
18889
19456
|
}).finally(() => {
|
|
18890
19457
|
pending -= 1;
|
|
@@ -19035,7 +19602,7 @@ var SEALED_OPS = /* @__PURE__ */ new Set([
|
|
|
19035
19602
|
"cancel",
|
|
19036
19603
|
"materialize"
|
|
19037
19604
|
]);
|
|
19038
|
-
var
|
|
19605
|
+
var encoder7 = new TextEncoder();
|
|
19039
19606
|
var decoder3 = new TextDecoder("utf-8", { fatal: true });
|
|
19040
19607
|
function parseSealed(bytes) {
|
|
19041
19608
|
try {
|
|
@@ -19064,8 +19631,8 @@ function materializable(req) {
|
|
|
19064
19631
|
function errorReply(code, message3) {
|
|
19065
19632
|
return { v: 1, error: { code, message: message3 } };
|
|
19066
19633
|
}
|
|
19067
|
-
function isRecord(
|
|
19068
|
-
return typeof
|
|
19634
|
+
function isRecord(value2) {
|
|
19635
|
+
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
19069
19636
|
}
|
|
19070
19637
|
function serialized(reply) {
|
|
19071
19638
|
let plain2;
|
|
@@ -19116,7 +19683,7 @@ var SealedChannel = class {
|
|
|
19116
19683
|
await this.broker.tickets.record(req, reply, caller.pid);
|
|
19117
19684
|
if (this.inflight !== flight) return;
|
|
19118
19685
|
this.inflight = void 0;
|
|
19119
|
-
if (reply) this.reply =
|
|
19686
|
+
if (reply) this.reply = encoder7.encode(serialized(reply));
|
|
19120
19687
|
});
|
|
19121
19688
|
this.inflight = flight;
|
|
19122
19689
|
return length2;
|
|
@@ -19708,8 +20275,8 @@ function umountCall(target, flags2) {
|
|
|
19708
20275
|
var FSTAB_PATH = "/etc/fstab";
|
|
19709
20276
|
var FSTAB_RETRIES = [1e3, 4e3, 16e3];
|
|
19710
20277
|
var FINAL = /* @__PURE__ */ new Set(["EINVAL", "EBUSY", "EPERM", "EACCES"]);
|
|
19711
|
-
function unescape(
|
|
19712
|
-
return
|
|
20278
|
+
function unescape(field4) {
|
|
20279
|
+
return field4.replace(
|
|
19713
20280
|
/\\([0-7]{3})/g,
|
|
19714
20281
|
(_, octal) => String.fromCharCode(Number.parseInt(octal, 8))
|
|
19715
20282
|
);
|
|
@@ -19905,9 +20472,9 @@ var HostfsClient = class {
|
|
|
19905
20472
|
let buffered = "";
|
|
19906
20473
|
try {
|
|
19907
20474
|
for (; ; ) {
|
|
19908
|
-
const { done: done4, value } = await reader.read();
|
|
20475
|
+
const { done: done4, value: value2 } = await reader.read();
|
|
19909
20476
|
if (done4) return;
|
|
19910
|
-
buffered += decoder4.decode(
|
|
20477
|
+
buffered += decoder4.decode(value2, { stream: true });
|
|
19911
20478
|
let end = buffered.indexOf("\n");
|
|
19912
20479
|
while (end >= 0) {
|
|
19913
20480
|
const line = buffered.slice(0, end).trim();
|
|
@@ -20169,10 +20736,10 @@ function timed(transport, ms) {
|
|
|
20169
20736
|
reject(fsError2("ENOTCONN", why));
|
|
20170
20737
|
}, ms);
|
|
20171
20738
|
running.then(
|
|
20172
|
-
(
|
|
20739
|
+
(value2) => {
|
|
20173
20740
|
clearTimeout(timer);
|
|
20174
|
-
if (expired) late?.(
|
|
20175
|
-
resolve2(
|
|
20741
|
+
if (expired) late?.(value2);
|
|
20742
|
+
resolve2(value2);
|
|
20176
20743
|
},
|
|
20177
20744
|
(err) => {
|
|
20178
20745
|
clearTimeout(timer);
|
|
@@ -20484,9 +21051,9 @@ var ChunkedBytes = class {
|
|
|
20484
21051
|
if (size < this.size) {
|
|
20485
21052
|
const keep2 = Math.ceil(size / CHUNK3);
|
|
20486
21053
|
this.chunks.length = Math.min(this.chunks.length, keep2);
|
|
20487
|
-
const
|
|
21054
|
+
const tail2 = this.chunks[keep2 - 1];
|
|
20488
21055
|
const from = size - (keep2 - 1) * CHUNK3;
|
|
20489
|
-
if (
|
|
21056
|
+
if (tail2 && from < tail2.length) tail2.fill(0, from);
|
|
20490
21057
|
}
|
|
20491
21058
|
this.size = size;
|
|
20492
21059
|
}
|
|
@@ -20501,8 +21068,8 @@ var ChunkedBytes = class {
|
|
|
20501
21068
|
};
|
|
20502
21069
|
|
|
20503
21070
|
// src/mount/tmpfs.ts
|
|
20504
|
-
function idOption2(
|
|
20505
|
-
return
|
|
21071
|
+
function idOption2(value2) {
|
|
21072
|
+
return value2 !== void 0 && /^\d+$/.test(value2) ? Number(value2) : 0;
|
|
20506
21073
|
}
|
|
20507
21074
|
function created2(ino, kind, mode, from, owner) {
|
|
20508
21075
|
const now = Date.now();
|
|
@@ -20737,7 +21304,7 @@ function importedMemory(bytes) {
|
|
|
20737
21304
|
const count2 = u32();
|
|
20738
21305
|
for (let i = 0; i < count2; i++) {
|
|
20739
21306
|
const module = name2();
|
|
20740
|
-
const
|
|
21307
|
+
const field4 = name2();
|
|
20741
21308
|
const kind = bytes[at++];
|
|
20742
21309
|
if (kind === 0) u32();
|
|
20743
21310
|
else if (kind === 1) {
|
|
@@ -20745,7 +21312,7 @@ function importedMemory(bytes) {
|
|
|
20745
21312
|
limits();
|
|
20746
21313
|
} else if (kind === 2) {
|
|
20747
21314
|
const l = limits();
|
|
20748
|
-
return { module, name:
|
|
21315
|
+
return { module, name: field4, ...l };
|
|
20749
21316
|
} else if (kind === 3) at += 2;
|
|
20750
21317
|
else if (kind === 4) {
|
|
20751
21318
|
at++;
|
|
@@ -20836,12 +21403,12 @@ function readImports(r, types) {
|
|
|
20836
21403
|
const foreign = {};
|
|
20837
21404
|
for (let i = r.u32(); i > 0; i--) {
|
|
20838
21405
|
const module = r.name();
|
|
20839
|
-
const
|
|
21406
|
+
const field4 = r.name();
|
|
20840
21407
|
const kind = r.byte();
|
|
20841
21408
|
if (kind !== 0) skipImport(r, kind);
|
|
20842
21409
|
else {
|
|
20843
21410
|
const type = types[r.u32()] ?? "other";
|
|
20844
|
-
if (!RESERVED_NAMESPACES.has(module)) (foreign[module] ??= {})[
|
|
21411
|
+
if (!RESERVED_NAMESPACES.has(module)) (foreign[module] ??= {})[field4] = type;
|
|
20845
21412
|
}
|
|
20846
21413
|
}
|
|
20847
21414
|
return foreign;
|
|
@@ -21354,7 +21921,7 @@ var LAYOUT = [
|
|
|
21354
21921
|
];
|
|
21355
21922
|
var LOAD_SAMPLE_MS = 5e3;
|
|
21356
21923
|
var LOAD_WINDOWS_MS = [6e4, 3e5, 9e5];
|
|
21357
|
-
var
|
|
21924
|
+
var encoder8 = new TextEncoder();
|
|
21358
21925
|
var USERS_MODES = ["off", "report", "enforce"];
|
|
21359
21926
|
function usersMode(mode = "off") {
|
|
21360
21927
|
if (USERS_MODES.includes(mode)) return mode;
|
|
@@ -21414,10 +21981,10 @@ function shebangCommand(words2) {
|
|
|
21414
21981
|
const arg2 = words2.slice(1).join(" ");
|
|
21415
21982
|
return { interp: words2[0], args: arg2 ? [arg2] : [] };
|
|
21416
21983
|
}
|
|
21417
|
-
const
|
|
21418
|
-
const [interp, ...rest2] = words2.slice(
|
|
21984
|
+
const split3 = words2[1] === "-S";
|
|
21985
|
+
const [interp, ...rest2] = words2.slice(split3 ? 2 : 1);
|
|
21419
21986
|
const arg = rest2.join(" ");
|
|
21420
|
-
return { interp, args:
|
|
21987
|
+
return { interp, args: split3 ? rest2 : arg ? [arg] : [] };
|
|
21421
21988
|
}
|
|
21422
21989
|
function interpreter(head) {
|
|
21423
21990
|
const words2 = shebang(head);
|
|
@@ -21428,9 +21995,9 @@ function interpreter(head) {
|
|
|
21428
21995
|
}
|
|
21429
21996
|
function expandDefaults(defaults, env) {
|
|
21430
21997
|
const out = {};
|
|
21431
|
-
for (const [key,
|
|
21998
|
+
for (const [key, value2] of Object.entries(defaults ?? {})) {
|
|
21432
21999
|
let missing4 = false;
|
|
21433
|
-
const expanded =
|
|
22000
|
+
const expanded = value2.replace(ENV_REFERENCE, (_, name2) => {
|
|
21434
22001
|
const set3 = Object.hasOwn(env, name2) ? env[name2] : void 0;
|
|
21435
22002
|
if (set3 === void 0) missing4 = true;
|
|
21436
22003
|
return set3 ?? "";
|
|
@@ -21472,7 +22039,7 @@ function childHandle(handle) {
|
|
|
21472
22039
|
onState: (listener) => handle.onState(listener)
|
|
21473
22040
|
};
|
|
21474
22041
|
}
|
|
21475
|
-
function
|
|
22042
|
+
function concat7(chunks) {
|
|
21476
22043
|
const out = new Uint8Array(chunks.reduce((n, chunk2) => n + chunk2.length, 0));
|
|
21477
22044
|
let offset = 0;
|
|
21478
22045
|
for (const chunk2 of chunks) {
|
|
@@ -21577,6 +22144,8 @@ var Launcher = class {
|
|
|
21577
22144
|
fileWatches;
|
|
21578
22145
|
nextPid = 1e3;
|
|
21579
22146
|
boot = Date.now();
|
|
22147
|
+
bootId = crypto.randomUUID();
|
|
22148
|
+
cron;
|
|
21580
22149
|
terminals = 0;
|
|
21581
22150
|
constructor(options) {
|
|
21582
22151
|
this.usersMode = usersMode(options.users);
|
|
@@ -21707,6 +22276,7 @@ var Launcher = class {
|
|
|
21707
22276
|
approve: options.approve,
|
|
21708
22277
|
timeoutMs: options.sudoTimeoutMs
|
|
21709
22278
|
});
|
|
22279
|
+
this.cron = this.cronService(options.cron);
|
|
21710
22280
|
void this.unlinked.sweep("/");
|
|
21711
22281
|
this.watchers.watch(
|
|
21712
22282
|
[this.modulesDir, this.pnpmHome, ...this.appended],
|
|
@@ -21718,6 +22288,38 @@ var Launcher = class {
|
|
|
21718
22288
|
this.resetCatalogs();
|
|
21719
22289
|
});
|
|
21720
22290
|
}
|
|
22291
|
+
cronService(options = {}) {
|
|
22292
|
+
return new CronService({
|
|
22293
|
+
fs: this.fs,
|
|
22294
|
+
owners: this.owners,
|
|
22295
|
+
lock: options.lock ?? "slicc-kernel-cron",
|
|
22296
|
+
bootId: this.bootId,
|
|
22297
|
+
...options.clock ? { clock: options.clock } : {},
|
|
22298
|
+
locks: cronLocks(),
|
|
22299
|
+
ready: () => this.fstab,
|
|
22300
|
+
lookup: (name2) => this.users.lookup(name2),
|
|
22301
|
+
watch: async (paths, notify) => {
|
|
22302
|
+
const local = this.watchers.watch(paths, { recursive: true }, () => notify());
|
|
22303
|
+
const remote = await this.watchFiles(paths, notify);
|
|
22304
|
+
return () => {
|
|
22305
|
+
local();
|
|
22306
|
+
remote();
|
|
22307
|
+
};
|
|
22308
|
+
},
|
|
22309
|
+
run: async ({ account, argv, cwd, env, output }) => {
|
|
22310
|
+
const ended = await this.run(argv, {
|
|
22311
|
+
user: account,
|
|
22312
|
+
cwd,
|
|
22313
|
+
env,
|
|
22314
|
+
login: true,
|
|
22315
|
+
collect: false,
|
|
22316
|
+
onStdout: output,
|
|
22317
|
+
onStderr: output
|
|
22318
|
+
});
|
|
22319
|
+
return { status: ended.status, ...signalField(ended.signal) };
|
|
22320
|
+
}
|
|
22321
|
+
});
|
|
22322
|
+
}
|
|
21721
22323
|
get owners() {
|
|
21722
22324
|
return this.usersMode !== "off";
|
|
21723
22325
|
}
|
|
@@ -22077,10 +22679,10 @@ var Launcher = class {
|
|
|
22077
22679
|
if (!swap) throw new SpawnError("EACCES");
|
|
22078
22680
|
const secrets = [];
|
|
22079
22681
|
for (const [name2, placeholder] of Object.entries(swap.placeholders)) {
|
|
22080
|
-
const
|
|
22081
|
-
if (
|
|
22082
|
-
env[name2] =
|
|
22083
|
-
secrets.push({ name: name2, value, placeholder });
|
|
22682
|
+
const value2 = swap.values[name2];
|
|
22683
|
+
if (value2 === void 0 || env[name2] !== placeholder) continue;
|
|
22684
|
+
env[name2] = value2;
|
|
22685
|
+
secrets.push({ name: name2, value: value2, placeholder });
|
|
22084
22686
|
}
|
|
22085
22687
|
const scrub = scrubberOf(secrets);
|
|
22086
22688
|
return { env, pid, cwd: booked.cwd, ...scrub ? { scrub } : {} };
|
|
@@ -22187,7 +22789,7 @@ var Launcher = class {
|
|
|
22187
22789
|
if (hit !== void 0) return hit;
|
|
22188
22790
|
const revocations = this.revocations;
|
|
22189
22791
|
const loading2 = load();
|
|
22190
|
-
const found = loading2.then(({ value }) =>
|
|
22792
|
+
const found = loading2.then(({ value: value2 }) => value2);
|
|
22191
22793
|
cache.set(key, found);
|
|
22192
22794
|
const drop = () => {
|
|
22193
22795
|
if (cache.get(key) === found) cache.delete(key);
|
|
@@ -23054,6 +23656,7 @@ var Launcher = class {
|
|
|
23054
23656
|
for (const holds2 of this.held) holds2.revoke(at);
|
|
23055
23657
|
}
|
|
23056
23658
|
unmountAll() {
|
|
23659
|
+
this.cron.stop();
|
|
23057
23660
|
this.booting.abort();
|
|
23058
23661
|
const targets = this.mounts.list().map((m) => m.target);
|
|
23059
23662
|
for (const target of targets.sort((a, b) => b.length - a.length)) this.umount(target, true);
|
|
@@ -23120,7 +23723,9 @@ var Launcher = class {
|
|
|
23120
23723
|
if (this.owners) await this.stampLayout();
|
|
23121
23724
|
if (this.owners) await this.sudoer.provision();
|
|
23122
23725
|
await writeCaFile(this.base, this.ca).catch(() => void 0);
|
|
23726
|
+
await this.cron.provision().catch(() => void 0);
|
|
23123
23727
|
this.fstab = this.mountFstab();
|
|
23728
|
+
void this.cron.start();
|
|
23124
23729
|
}
|
|
23125
23730
|
async mountFstab() {
|
|
23126
23731
|
const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
|
|
@@ -23142,9 +23747,9 @@ var Launcher = class {
|
|
|
23142
23747
|
}
|
|
23143
23748
|
);
|
|
23144
23749
|
}
|
|
23145
|
-
environment(cwd, extra, account) {
|
|
23750
|
+
environment(cwd, extra, account, login = false) {
|
|
23146
23751
|
const env = {
|
|
23147
|
-
...this.env,
|
|
23752
|
+
...login ? networkEnv(this.hostname) : this.env,
|
|
23148
23753
|
...userEnv(account, this.pnpmHome),
|
|
23149
23754
|
PWD: cwd,
|
|
23150
23755
|
...extra
|
|
@@ -23296,7 +23901,7 @@ var Launcher = class {
|
|
|
23296
23901
|
},
|
|
23297
23902
|
account
|
|
23298
23903
|
);
|
|
23299
|
-
const report = (message3) => options.onData(
|
|
23904
|
+
const report = (message3) => options.onData(encoder8.encode(`${message3}\r
|
|
23300
23905
|
`));
|
|
23301
23906
|
const handle = await this.launch(planned, {
|
|
23302
23907
|
env,
|
|
@@ -23324,7 +23929,7 @@ var Launcher = class {
|
|
|
23324
23929
|
tagOption(options.tag);
|
|
23325
23930
|
const account = await this.account(options.user);
|
|
23326
23931
|
const { cwd, file, planned } = await this.starting(argv, options.cwd, account, options.tag);
|
|
23327
|
-
const env = this.environment(cwd, options.env, account);
|
|
23932
|
+
const env = this.environment(cwd, options.env, account, options.login);
|
|
23328
23933
|
const out = [];
|
|
23329
23934
|
const err = [];
|
|
23330
23935
|
const keep2 = options.collect !== false;
|
|
@@ -23334,13 +23939,13 @@ var Launcher = class {
|
|
|
23334
23939
|
};
|
|
23335
23940
|
const stdout = collect(out, options.onStdout);
|
|
23336
23941
|
const stderr = collect(err, options.onStderr);
|
|
23337
|
-
const report = (message3) => stderr(
|
|
23942
|
+
const report = (message3) => stderr(encoder8.encode(`${message3}
|
|
23338
23943
|
`));
|
|
23339
23944
|
if (!planned) {
|
|
23340
23945
|
const directory = await this.directory(file, this.fs.resolvePath(cwd, file));
|
|
23341
23946
|
report(`${file}: ${directory ? "Is a directory" : "command not found"}`);
|
|
23342
23947
|
const status2 = directory ? NOT_EXECUTABLE : NOT_FOUND;
|
|
23343
|
-
return { status: status2, stdout:
|
|
23948
|
+
return { status: status2, stdout: concat7(out), stderr: concat7(err) };
|
|
23344
23949
|
}
|
|
23345
23950
|
const fds = new FdTable();
|
|
23346
23951
|
fds.installAt(0, options.stdin ? bytesSource(options.stdin) : deviceFile("null"));
|
|
@@ -23358,9 +23963,12 @@ var Launcher = class {
|
|
|
23358
23963
|
options.onStarted?.(handle.pid);
|
|
23359
23964
|
const status = await handle.exited;
|
|
23360
23965
|
const signal = handle.termsig();
|
|
23361
|
-
return { status, ...signal ? { signal } : {}, stdout:
|
|
23966
|
+
return { status, ...signal ? { signal } : {}, stdout: concat7(out), stderr: concat7(err) };
|
|
23362
23967
|
}
|
|
23363
23968
|
};
|
|
23969
|
+
function cronLocks() {
|
|
23970
|
+
return globalThis.navigator?.locks;
|
|
23971
|
+
}
|
|
23364
23972
|
|
|
23365
23973
|
// src/sealed-relay.ts
|
|
23366
23974
|
function sealedAsker(port) {
|
|
@@ -23557,6 +24165,7 @@ function usersOp(l, req) {
|
|
|
23557
24165
|
}
|
|
23558
24166
|
function hostOptions(req, port, sealed, clipboard, lookup2) {
|
|
23559
24167
|
return {
|
|
24168
|
+
cron: { lock: `slicc-kernel-cron:${req.metadata ?? META_DB}` },
|
|
23560
24169
|
...req.hostname !== void 0 ? { hostname: req.hostname } : {},
|
|
23561
24170
|
...req.users !== void 0 ? { users: req.users } : {},
|
|
23562
24171
|
...req.denials ? { onDenial: (denial) => port.postMessage({ denial }) } : {},
|