@ai-ecoverse/slicc-kernel 1.174.1 → 1.174.2
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 +18 -6
- package/dist/index.js +33 -0
- package/dist/index.js.map +3 -3
- package/dist/kernel-worker.js +216 -70
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.cjs +1 -1
- package/dist/node-process-worker.js +1 -1
- package/dist/node.js +217 -70
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +1 -1
- package/dist/testing.js +4 -2
- package/dist/testing.js.map +2 -2
- package/dist/types/cron/clock.d.ts +3 -1
- package/dist/types/cron/schedule.d.ts +5 -3
- package/dist/types/cron/service.d.ts +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/launcher.d.ts +3 -1
- package/dist/types/node.d.ts +2 -1
- package/dist/types/serve.d.ts +1 -0
- package/dist/types/time-zone.d.ts +11 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -39,6 +39,7 @@ TypeScript declarations ship in `dist/types/`, and each entry (`.`, `./node`, `.
|
|
|
39
39
|
| `onMountPending` | none | `({ target, source, insert }) => void`: an `fsa` mount needs a folder; call `insert()` from a user gesture |
|
|
40
40
|
| `processMounts` | `true` | whether programs may call `mount(2)` and `umount2(2)`: `false`, or `(req) => boolean \| Promise<boolean>` to decide each call (see [Mounting from a program](#mounting-from-a-program)) |
|
|
41
41
|
| `hostname` | `'slicc'` | the kernel's node name (see [Node name](#node-name)) |
|
|
42
|
+
| `timeZone` | the browser's (`Intl.DateTimeFormat().resolvedOptions().timeZone`) | the system time zone, an IANA name such as `'Europe/Berlin'`: `TZ` in every environment, and the zone cron reads schedules in (see [Time zone](#time-zone)). A name `Intl` doesn't know rejects. `createNodeKernel` takes it too |
|
|
42
43
|
| `users` | `'enforce'` | `'off'`, `'report'` or `'enforce'`: whether files have owners and their modes are checked (see [File owners](#file-owners) and [Permissions](#permissions)), and whether `sudo` elevates (see [Sudo](#sudo)). `'off'` behaves exactly as kernels before owners did; any other value rejects. `createNodeKernel` takes it too, with the same default. Kernels up to 1.73 defaulted to `'off'` |
|
|
43
44
|
| `onDenial` | none | `({ pid, uid, op, path, errno, tag? }) => void`: called for every permission check that fails, under `'report'` and `'enforce'` (see [Permissions](#permissions)). `createNodeKernel` takes it too |
|
|
44
45
|
| `sealed` | none | `(req, { signal }) => Promise<object>`, or `{ request, fetch? }` with that function as `request`: `request` answers requests written to [`/dev/sealed`](#devsealed), and `fetch` fills requests that carry a sealed placeholder (see **The fetch hook** under [`/dev/sealed`](#devsealed)). `createNodeKernel` takes it too. An exported `SEALED_FETCH_VERSION`, the number `1`, says that this kernel takes the object |
|
|
@@ -58,7 +59,7 @@ Runs `argv[0]` with `argv` as its arguments and resolves when it exits. `stdout`
|
|
|
58
59
|
| `cwd` | working directory, default `/`; created in OPFS if missing. Under `users: 'enforce'`, a `cwd` the user can't search rejects with `code: 'EACCES'` (`cannot change to <cwd>: Permission denied`) and starts nothing, as a `chdir` there fails on Linux; `openTerminal` does the same. A program's spawn with its own `cwd` (`ctx.spawn`, `ctx.sync.run`, a WASIX `posix_spawn` chdir action) is checked the same way and fails with `EACCES`, while a child that inherits its parent's cwd keeps it unchecked, as on Linux |
|
|
59
60
|
| `user` | the user to run as, by name or uid (see [Users](#users)); default `root`. An unknown user rejects with `code: 'ENOENT'` |
|
|
60
61
|
| `tag` | a string the kernel keeps with the process and every process it starts (fork, spawn and exec), and hands to `approve` with a [`sudo`](#sudo) request, so the embedder knows which agent asks; nothing a program does changes it. Anything but a string rejects with a `TypeError` |
|
|
61
|
-
| `env` | extra environment, over the user's defaults: `HOME`, `USER`, `LOGNAME`, `PNPM_HOME` and `PATH=$PNPM_HOME/bin:/usr/local/bin:/usr/bin:/bin` (see [Users](#users)), and `PWD=<cwd>` |
|
|
62
|
+
| `env` | extra environment, over the user's defaults: `HOME`, `USER`, `LOGNAME`, `PNPM_HOME` and `PATH=$PNPM_HOME/bin:/usr/local/bin:/usr/bin:/bin` (see [Users](#users)), `TZ=<system time zone>` (see [Time zone](#time-zone)), and `PWD=<cwd>` |
|
|
62
63
|
| `stdin` | a string or `Uint8Array`; without it stdin is `/dev/null` |
|
|
63
64
|
| `onStdout`, `onStderr` | called with each chunk of text as it is written |
|
|
64
65
|
|
|
@@ -671,6 +672,15 @@ The kernel has one node name, `slicc` unless `createKernel({ hostname })` (or `c
|
|
|
671
672
|
- **The trade-off:** a raw connection to the real `wasmer.sh` resolves to loopback too. HTTP(S) to it still goes through the realm proxy and the transport, which resolve names themselves; `wasmer.sh` is deliberately not in `no_proxy`.
|
|
672
673
|
- **A real file wins:** a root that has its own `/etc/hostname` (or `/etc/hosts`, `/etc/passwd`) keeps it, and programs read that file rather than the synthetic one. The kernel's name still drives `uname`, `gethostname` and name resolution. A program that opens one of these synthetic files for writing (as root, since `/etc` is root's) gets a real one first: a root-owned `0644` copy of the defaults. So `echo '10.0.0.9 db' >> /etc/hosts` keeps `localhost` and the other default entries, `>` replaces them as on Linux, and `rm` returns to the synthetic file. The copy is `0644` whatever the umask. Programs that read `/etc/hosts` themselves (WASIX/musl ones, such as python's `socket.gethostbyname`) resolve an added name. So does the kernel's own resolver, which Emscripten programs' `getaddrinfo`, WASIX `resolve` and `ctx.net` use ([#898](https://github.com/ai-ecoverse/slicc-kernel/issues/898)). It reads a real `/etc/hosts` as glibc does (`#` comments, aliases, IPv4 and IPv6, first entries first), re-reads it when its inode, size or mtime change, and answers a name the file lists before its built-in names. HTTP that goes through the transport by URL is resolved there, so `curl http://db/` doesn't use the file. A root-written `/etc/hosts` entry for `localhost` applies to `ctx.net` and `getaddrinfo`, as with glibc. The WASI imports' `net.connect` maps the literal name `localhost` to `127.0.0.1`.
|
|
673
674
|
|
|
675
|
+
### Time zone
|
|
676
|
+
|
|
677
|
+
The kernel has one time zone, the browser's unless `createKernel({ timeZone })` (or `createNodeKernel`) names another IANA zone. It is fixed when the kernel starts, and everything that tells the time uses it:
|
|
678
|
+
- **`TZ`** is the zone's name (`TZ=Europe/Berlin`) in the kernel's base environment and in every login environment (cron jobs, `sudo`), so `kernel.run`, terminals and their programs have it. An embedder's own `env.TZ` still wins in the base environment, as any key of `env` does, and a program keeps whatever `TZ` its caller passes.
|
|
679
|
+
- **Cron** reads schedules in it (see [Cron](#cron)), so a job set for `22 18 * * *` runs when `date` in the same environment says 18:22.
|
|
680
|
+
- **Programs:** coreutils (wasm-coreutils 9.12.0-12 on) honours `TZ`; with no `TZ` it would be UTC (homescoop#1626), which is why the kernel sets it. Other Emscripten programs (bash's `printf '%(…)T'`, git, sqlite) and JS programs (`new Date()`) use the browser's zone whatever `TZ` says, which is the same zone unless `timeZone` names another. WASIX programs (python, perl, ruby) read `TZ` through musl, which finds a zone name only in `/usr/share/zoneinfo`; with no such tree in the root they use UTC, as they did before.
|
|
681
|
+
|
|
682
|
+
Until this, `TZ` was unset, so `date` printed UTC while cron read schedules in the browser's zone.
|
|
683
|
+
|
|
674
684
|
### Uplink
|
|
675
685
|
|
|
676
686
|
`createKernel({ network: { uplink } })` (or `createNodeKernel`) gives the kernel a page-owned way into another network, such as a tailnet. It is the whole kernel's: while the page passes an uplink, every process gets its names. The uplink is an object with:
|
|
@@ -732,7 +742,7 @@ In the browser, the page serves the uplink to the kernel worker over the same po
|
|
|
732
742
|
- `reset(code)` (default `ECONNRESET`) breaks the connection both ways: writes the peer has not read fail, the program's next read fails with `ECONNRESET`, and its writes with `EPIPE`.
|
|
733
743
|
- `closed` resolves when the kernel closes the connection.
|
|
734
744
|
|
|
735
|
-
`fakeCronClock(start?,
|
|
745
|
+
`fakeCronClock(start?, zone?)` is a clock for the kernel's cron to run on in tests, so a test moves time instead of waiting for it. It starts at `start` (default 2026-01-01T00:00Z). `zone` sets the [time zone](#time-zone) of a kernel started on it that names no `timeZone` of its own: UTC by default or with `true`, an IANA name such as `'Europe/Berlin'`, or the browser's with `false`. Its `timeZone` is that name (none for `false`) and `utc` is whether it is UTC. `now()` is the fake time. `sleep(until, signal)` resolves when `advance(ms)` moves time to `until` or past it, or when `signal` aborts. `set(ms)` sets the wall clock, forward or back, and wakes every sleeper, as a scheduler's next periodic wake would after a real clock change. `sleepers` counts the sleeps still waiting. `flush()` resolves once every sleeper that a time move woke has gone back to sleep for a later time, which is the point where a scheduler has done what was due. It never resolves while nothing sleeps.
|
|
736
746
|
|
|
737
747
|
`setTestSyscallHook(hook)` is **test-only**: while set, every process kernel call (the `WasmSyscall` requests: process, fd, socket, signal and job calls; not the path-only file system requests such as `read`, `write` or `rename`) of a process in this Node process first awaits `hook({ pid, op })`. `pid` is the kernel's own pid of the process, which for an exec'd image is not the pid the program sees (`getpid()`, `ps` show the pid it took over). A test can then act at an exact point (write a ^C when a shell forks) or hold a call (delay a child's `proc-setpgid`). `setTestSyscallHook(undefined)` clears it. Unset, which is the default, it does nothing.
|
|
738
748
|
|
|
@@ -1032,7 +1042,7 @@ Lowering a soft resource limit (`ulimit -S -n 64`) still fails with `EPERM` in E
|
|
|
1032
1042
|
Every process has credentials in the kernel, as on Linux: real, effective and saved user and group ids, and supplementary groups. A process gets them from the user it was started as (`run`, `openTerminal`, a client's `spawn`, or the client's own user; root by default) and keeps its parent's across fork, spawn and exec. Setuid and setgid bits are never honoured on exec. Files may have owners (see [File owners](#file-owners)), checked against the credentials with `users: 'enforce'` (see [Permissions](#permissions)); the credentials also decide who may signal whom, and what `/proc` and `ps` show.
|
|
1033
1043
|
|
|
1034
1044
|
- **The database:** `/etc/passwd` and `/etc/group` are real files. On first start the kernel writes them when they're missing, with `root` (uid 0, home `/root`, created `0700`) and `nobody` (65534), and the groups `root`, `sudo` (27), `users` (100) and `nogroup` (65534); files already there are kept as they are. The kernel reads them for every lookup, so an edit counts at once. [`kernel.users`](#kernelusers) adds and removes users.
|
|
1035
|
-
- **Environment:** a process started as a user gets `HOME`, `USER`, `LOGNAME`, `PNPM_HOME`, `NPM_CONFIG_PREFIX` and `PATH
|
|
1045
|
+
- **Environment:** a process started as a user gets `HOME`, `USER`, `LOGNAME`, `PNPM_HOME`, `NPM_CONFIG_PREFIX` and `PATH`, and `TZ` (the kernel's [time zone](#time-zone)) with the rest of the base or login environment. A user's `PNPM_HOME` is `~/.local/share/pnpm` and its npm prefix `~/.local`; root's are `/usr/local/share/pnpm` (or the embedder's `env.PNPM_HOME`) and `/usr/local`. `PATH` is `$PNPM_HOME/bin:/usr/local/bin:/usr/bin:/bin`. pnpm makes only `$PNPM_HOME/bin`, so the kernel makes root's `PNPM_HOME` when it is missing, just before a process starts: once at boot, and again after any mount or unmount, a removable medium inserted, a `hostfs` folder coming back, or a change at that directory or above it (removing it, say), and after a failed try (no medium yet). With owners on, the directories it makes there get root's owner and `0755`, as the boot layout does. A process already running when a medium arrives gets it at its next child launch. Writes inside it cost nothing extra.
|
|
1036
1046
|
- **Commands per user:** root's global packages (`pnpm add -g` as root) are commands every user finds in `/usr/bin`. A user's own global packages are commands for that user only: they show up in the user's `~/.local/share/pnpm/bin`, first on its `PATH`, and a program the user starts finds them by name before the system's; another user, root included, doesn't. A command that only a dependency of those packages provides shows up there only when no other root has the name (see [Commands](#commands)). A user's packages can map file suffixes (`binfmt`) for that user too. The kernel picks up an install, a removal and a new user as they happen.
|
|
1037
1047
|
- **Changing credentials:** POSIX `setresuid`/`setresgid` rules. Root (effective uid 0) may set any id and the group list; anyone else may only swap among their own real, effective and saved ids, and gets `EPERM` otherwise.
|
|
1038
1048
|
- **Signals:** a process may signal another if its effective uid is 0, or if its real or effective uid is the target's real or saved uid; otherwise `kill` fails with `EPERM`. A group kill signals the members it may, and fails with `EPERM` only when it may signal none.
|
|
@@ -1097,9 +1107,10 @@ With `users: 'enforce'` the kernel checks every file call that reaches it agains
|
|
|
1097
1107
|
The kernel runs scheduled jobs itself, with no daemon process: a cron service starts with the kernel and stops with `terminate()`. It reads `/etc/crontab` and the per-user tables in `/var/spool/cron/crontabs/<user>`, and runs each due job as `$SHELL -c '<command>'` (`/bin/bash` unless the table sets `SHELL`) as the table's user, in that user's home (`/` if the home is missing), with no terminal and `/dev/null` as standard input.
|
|
1098
1108
|
|
|
1099
1109
|
- **`crontab`** is a command of the kernel itself, in `/usr/bin` (a package's `crontab` never wins over it), with Vixie's options: `crontab -l` prints the caller's table, `crontab -r` removes it, `crontab <file>` and `crontab -` (standard input) install one, and `crontab -e` copies the table to `/tmp`, runs `$VISUAL` (or `$EDITOR`) on the copy through `/bin/bash -c '<editor> "$1"'`, and installs it if it changed and the editor exited with 0. If the edit doesn't parse, the copy is kept and its path is printed. `-u <user>` works on another user's table and needs an effective uid of 0 (`sudo crontab -u cone -l`). An install is checked first: a table with a line that doesn't parse is refused with every error (`"-":<line>: <message>`), and the old one stays. The table becomes the user's `0600` file in the spool, written to a dotfile of a random name and renamed, so the service, which skips dotfiles, never reads half a table. The command only reads the file it is given and passes it on (`ctx.crontab`, see [JS programs](#js-programs)). The kernel decides whose table it is from the caller's real uid, so a user reaches only their own table, and `/etc/crontab` stays root's to edit.
|
|
1100
|
-
- **Format:** Vixie's. Five fields (minute, hour, day of month, month, weekday) with lists, ranges, steps and the names `jan`–`dec` and `sun`–`sat` (7 is Sunday too), or one of `@yearly`/`@annually`, `@monthly`, `@weekly`, `@daily`/`@midnight`, `@hourly` and `@reboot`. As in cronie, when the day of month or the weekday starts with `*`, a day must match both fields; when both are restricted, it may match either. `/etc/crontab` has a user field after the schedule; a user's table doesn't. `NAME=value` lines set the environment for the jobs after them, `#` lines are comments, and `# name: <id>` on the line above a job gives it a stable id (letters, digits, `_`, `.`, `-`), which names its log. Without one, the id is a hash of the table, the user, the schedule and the command. `%` is not special.
|
|
1101
|
-
- **
|
|
1102
|
-
- **
|
|
1110
|
+
- **Format:** Vixie's. Five fields (minute, hour, day of month, month, weekday) with lists, ranges, steps and the names `jan`–`dec` and `sun`–`sat` (7 is Sunday too), or one of `@yearly`/`@annually`, `@monthly`, `@weekly`, `@daily`/`@midnight`, `@hourly` and `@reboot`. As in cronie, when the day of month or the weekday starts with `*`, a day must match both fields; when both are restricted, it may match either. `/etc/crontab` has a user field after the schedule; a user's table doesn't. `NAME=value` lines set the environment for the jobs after them, `#` lines are comments, and `# name: <id>` on the line above a job gives it a stable id (letters, digits, `_`, `.`, `-`), which names its log. Without one, the id is a hash of the table, the user, the schedule and the command. `%` is not special.
|
|
1111
|
+
- **Time zone:** schedules are read in the kernel's [time zone](#time-zone), the one `TZ` names in the job's environment and in `kernel.run`, so `date` shows the time a schedule names. As in cronie, `CRON_TZ=<zone>` sets another IANA zone for the schedules of the jobs after it (and is in their environment); a zone `Intl` doesn't know makes each of those lines an error, so `crontab` refuses the table. A `TZ=` line only sets the jobs' `TZ`, as in cronie; set both to have `date` in the job agree with its `CRON_TZ` schedule. Across daylight saving changes it follows cronie: a job whose minute and hour are both fixed runs once on a day the clock goes back, and one due in the hour the clock skips runs right after the change. A job with `*` in the minute or hour runs by the clock, so it runs in the repeated hour too, and the skipped one has nothing to run. Until this change, schedules were read in the browser's zone while `date` showed UTC.
|
|
1112
|
+
- **Environment:** only what a login of that user gets, and nothing from the page or the embedder's `env`. That is `HOME`, `USER`, `LOGNAME`, `PATH`, `PNPM_HOME`, `NPM_CONFIG_PREFIX`, `TZ` (the kernel's [time zone](#time-zone)), the kernel's network settings (`http_proxy`, `SSL_CERT_FILE`, …), `SHELL`, `PWD`, the table's `NAME=value` lines, and `SLICC_CRON=1`, `SLICC_CRON_JOB=<id>`, `SLICC_CRON_DUE=<ISO time of the due run>` and `SLICC_CRON_MISSED=<runs folded into this one>`. An `agent send` from a job therefore knows it was scheduled.
|
|
1113
|
+
- **Missed runs:** when the kernel wasn't running (a closed tab) or time jumped forward, every due run since the last one is folded into **one** run, with `SLICC_CRON_MISSED` saying how many more were due (counted up to 100 000) and `SLICC_CRON_DUE` naming the latest due time, even past the count's cap ([#843](https://github.com/ai-ecoverse/slicc-kernel/issues/843)). The next run is then computed from now. A line the service hasn't seen before, and a line whose schedule or `CRON_TZ` changed (a `# name:` job given a new schedule), counts from when the service first sees it, so nothing due before it was installed is folded in; a named job whose command alone changed keeps counting. A clock set back more than two minutes runs nothing until the next due time after the new time.
|
|
1103
1114
|
- **At most once:** the service writes each due run to `/var/spool/cron/stamp` before it starts the job. A kernel that ends mid-job (a reload, a crash) never runs that due again; a stamp that can't be read counts every job as run just now.
|
|
1104
1115
|
- **`@reboot`** runs once per kernel boot, when that kernel first fires jobs.
|
|
1105
1116
|
- **Overlap:** a job's runs don't wait for each other, so a slow job can still be running when it is due again. At most 16 jobs of one user run at once; a run past that is skipped, with a line in the job's log and in `/var/log/cron/cron.err`.
|
|
@@ -1341,6 +1352,7 @@ The kernel is ported from SLICC's `packages/webapp/src/kernel/` with the browser
|
|
|
1341
1352
|
- `src/kernel/net/`: the proxy, HTTP/1.1, TLS termination and the local CA, and the bridge to the page's transport; `src/transport.ts` is the page side.
|
|
1342
1353
|
- `src/cdp/`: the CDP facade on `127.0.0.1:9222`, its WebSocket framing, the registry of hosts and the `MessagePort` bridge to them.
|
|
1343
1354
|
- `src/sudo.ts`, `src/sudoers.ts`, `src/sudo-command.ts`: the kernel side of `sudo` and `visudo -c`, the policy parser (both line forms and their diagnostics), and the two commands.
|
|
1355
|
+
- `src/time-zone.ts`: the system time zone (`timeZone`, or the browser's) and UTC offsets of a zone, which cron's schedules and `TZ` both come from.
|
|
1344
1356
|
- `src/cron/`: the cron service (`service.ts`), the schedule and table parsers, the clock, and the `crontab` command with the kernel side of `ctx.crontab`. An embedder that uses `Launcher` directly passes `cron: { lock }` to share the firing lock among the kernels on one root; the default, `slicc-kernel-cron`, is one lock for the whole origin.
|
|
1345
1357
|
|
|
1346
1358
|
|
package/dist/index.js
CHANGED
|
@@ -1149,6 +1149,37 @@ function sealedAnswerer(port, hook, fetchHook) {
|
|
|
1149
1149
|
};
|
|
1150
1150
|
}
|
|
1151
1151
|
|
|
1152
|
+
// src/time-zone.ts
|
|
1153
|
+
var MINUTE_MS = 6e4;
|
|
1154
|
+
var HOUR_MS = 60 * MINUTE_MS;
|
|
1155
|
+
var BUCKET_MS = 15 * MINUTE_MS;
|
|
1156
|
+
function canonicalZone(name) {
|
|
1157
|
+
if (typeof name !== "string" || name === "" || name.length > 64) return void 0;
|
|
1158
|
+
try {
|
|
1159
|
+
return new Intl.DateTimeFormat("en-US", { timeZone: name }).resolvedOptions().timeZone;
|
|
1160
|
+
} catch {
|
|
1161
|
+
return void 0;
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
function hostTimeZone() {
|
|
1165
|
+
let name;
|
|
1166
|
+
try {
|
|
1167
|
+
name = new Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
1168
|
+
} catch {
|
|
1169
|
+
name = void 0;
|
|
1170
|
+
}
|
|
1171
|
+
return canonicalZone(name) ?? "UTC";
|
|
1172
|
+
}
|
|
1173
|
+
function systemTimeZone(given, fallback) {
|
|
1174
|
+
if (given !== void 0) {
|
|
1175
|
+
const zone = canonicalZone(given);
|
|
1176
|
+
if (zone === void 0) throw new Error(`not a time zone: ${String(given)}`);
|
|
1177
|
+
return zone;
|
|
1178
|
+
}
|
|
1179
|
+
return canonicalZone(fallback) ?? hostTimeZone();
|
|
1180
|
+
}
|
|
1181
|
+
var MAX_SHIFT_MS = 3 * HOUR_MS;
|
|
1182
|
+
|
|
1152
1183
|
// src/kernel/net/ws-queue.ts
|
|
1153
1184
|
var INBOUND_LIMIT = 16 * 1024 * 1024;
|
|
1154
1185
|
var MESSAGE_LIMIT = 64 * 1024 * 1024;
|
|
@@ -2498,6 +2529,7 @@ async function initCall(options, transport) {
|
|
|
2498
2529
|
...options.cdp ? { cdp: true } : {},
|
|
2499
2530
|
...facadesInit(options.facades),
|
|
2500
2531
|
...options.hostname !== void 0 ? { hostname: options.hostname } : {},
|
|
2532
|
+
...options.timeZone !== void 0 ? { timeZone: options.timeZone } : {},
|
|
2501
2533
|
users: options.users === void 0 ? DEFAULT_USERS : options.users,
|
|
2502
2534
|
...options.onDenial ? { denials: true } : {},
|
|
2503
2535
|
...hooks.request ? { sealed: true } : {},
|
|
@@ -2669,6 +2701,7 @@ function checkOptions(options) {
|
|
|
2669
2701
|
if (options.hostname !== void 0 && !isHostname(options.hostname)) {
|
|
2670
2702
|
throw new Error(`not a host name: ${String(options.hostname)}`);
|
|
2671
2703
|
}
|
|
2704
|
+
if (options.timeZone !== void 0) systemTimeZone(options.timeZone);
|
|
2672
2705
|
if (options.protect) protectInit(options.protect);
|
|
2673
2706
|
integrityOptions(options.protect?.integrity);
|
|
2674
2707
|
facadesInit(options.facades);
|