@damurka/jovian 0.1.1 → 0.1.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 +1 -1
- package/docs/api/README.md +10 -1
- package/docs/development.md +2 -2
- package/docs/releasing.md +14 -8
- package/docs/troubleshooting.md +4 -3
- package/lib/execution/execution-queue.js +1 -1
- package/lib/session/native-paths.d.ts +1 -1
- package/lib/session/native-paths.js +1 -1
- package/lib/session/r-setup.d.ts +1 -0
- package/lib/session/r-setup.js +35 -4
- package/lib/session/session-manager.d.ts +13 -2
- package/lib/session/session-manager.js +22 -7
- package/lib/session/supervisor-client.d.ts +14 -3
- package/lib/session/supervisor-client.js +39 -6
- package/lib/types/engine.d.ts +25 -1
- package/lib/utils/logger.d.ts +14 -3
- package/lib/utils/logger.js +38 -30
- package/package.json +11 -7
- package/packages/hera/DESCRIPTION +1 -1
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ The pieces are named after moons of Jupiter:
|
|
|
36
36
|
npm install @damurka/jovian
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
The package ships **prebuilt** `themisto`, `elara` and `carpo` binaries — no compiler, CMake or vcpkg — for **Windows x64**
|
|
39
|
+
The package ships **prebuilt** `themisto`, `elara` and `carpo` binaries — no compiler, CMake or vcpkg — for **Windows x64**, **Linux x64 and arm64**, and **macOS x64 (Intel) and arm64 (Apple Silicon)**. Windows on ARM and 32-bit ARM Linux are not supported (there is no ARM64 R for Windows yet); build from source elsewhere and point `JOVIAN_NATIVE_DIR` at `dist/native/Release`. npm installs the matching `@damurka/jovian-<os>-<cpu>` package automatically as an optional dependency, so do not install with `--omit=optional` / `--no-optional`. Node.js ≥ 22.13 is required. The package is an ES module: use `import` (in a project with `"type": "module"`, or `.mjs`/`.mts` files), or `require()` it from CommonJS on Node 22.13+.
|
|
40
40
|
|
|
41
41
|
What you must already have on the machine:
|
|
42
42
|
|
package/docs/api/README.md
CHANGED
|
@@ -21,9 +21,18 @@ import {
|
|
|
21
21
|
Creates sessions and owns the one shared supervisor process (`themisto`) they all talk to. The supervisor is spawned lazily on the first `createSession()` and killed by `stopAll()` / `killAll()` (and, as a safety net, when the Node process exits).
|
|
22
22
|
|
|
23
23
|
```typescript
|
|
24
|
-
const manager = new SessionManager();
|
|
24
|
+
const manager = new SessionManager(); // quiet
|
|
25
|
+
const verbose = new SessionManager({ logLevel: 'debug' }); // everything, plus the kernels' own output
|
|
25
26
|
```
|
|
26
27
|
|
|
28
|
+
**Logging.** The library is quiet by default: it prints only one-time setup notices (the install of the R packages on the first R session), warnings and errors. A kernel that fails to start reports its own error in the exception's message (`Kernel output: …`). To see more:
|
|
29
|
+
|
|
30
|
+
| Option / variable | Effect |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `logLevel: 'trace' \| 'debug' \| 'info' \| 'notice' \| 'warn' \| 'error' \| 'silent'` (or env `JOVIAN_LOG_LEVEL`) | What the built-in console logger prints: this level and above. Default `'notice'`. `'debug'` and `'trace'` also print the kernels' start-up output. |
|
|
33
|
+
| `logger: (level, message, data?) => void` | Receives **every** message (do your own filtering) instead of the console. A session's own `logger` option takes precedence for that session. |
|
|
34
|
+
| `kernelOutput: boolean` (or env `JOVIAN_KERNEL_OUTPUT=1`) | Print the kernels' own `[elara]` / `[carpo]` start-up output to stderr as it happens. Default off (on for `debug`/`trace`). |
|
|
35
|
+
|
|
27
36
|
| Member | Description |
|
|
28
37
|
|---|---|
|
|
29
38
|
| `createSession(options?: EngineOptions): Promise<Session>` | Spawns a kernel process (per `options.kernelType`), waits until it has registered and the session's WebSocket is ready, and resolves with the `Session`. Rejects with the supervisor's message if the kernel could not start: `workingDirectory does not exist or is not a directory: …`, `no kernel executable is configured for kernelType 'python' …`, or — when the kernel process itself failed (R or Python not found, …) — `Kernel process exited before it could register -- check its stderr output for the actual error.` The *actual* R/Python error is printed on stderr with an `[elara]` / `[carpo]` prefix (see [Troubleshooting](../troubleshooting.md)). |
|
package/docs/development.md
CHANGED
|
@@ -88,8 +88,8 @@ The npm packages are built and published by `.github/workflows/release.yml` from
|
|
|
88
88
|
|
|
89
89
|
## Debugging
|
|
90
90
|
|
|
91
|
-
- **Kernel logs.** Everything a kernel prints (`[R Interpreter] …`, `[carpo] …`, and anything R/Python writes outside an execution) appears on the *supervisor's* stderr,
|
|
92
|
-
- **Library logs.**
|
|
91
|
+
- **Kernel logs.** Everything a kernel prints (`[R Interpreter] …`, `[carpo] …`, and anything R/Python writes outside an execution) appears on the *supervisor's* stderr, prefixed `[elara]` / `[carpo]`. `lib/` keeps it quiet by default (and puts the relevant lines in the error when a kernel fails to start); set `JOVIAN_LOG_LEVEL=debug` (or `JOVIAN_KERNEL_OUTPUT=1`, or `new SessionManager({ kernelOutput: true })`) to forward it to your stderr. That is the first place to look when a session fails to start.
|
|
92
|
+
- **Library logs.** Quiet by default; `JOVIAN_LOG_LEVEL=trace` (or `new SessionManager({ logLevel: 'trace' })`) shows queueing, request ids and timeouts, and `logger` receives every message.
|
|
93
93
|
- **Run a kernel by hand.** Generate a kernelspec (`npm run jupyter:kernelspec`) and start it from `jupyter console --kernel elara`, or run `elara -f <connection-file> --r-home …` yourself — no supervisor involved.
|
|
94
94
|
- **Talk to the supervisor directly.** Start `themisto` (it prints `{"type":"supervisorReady","httpPort":…,"wsPort":…}`) and use `curl` against [its HTTP API](protocol.md#2-themistos-http-api) and any WebSocket client against `ws://127.0.0.1:<wsPort>/sessions/<id>/messages`.
|
|
95
95
|
- **One native test.** `dist/native/Release/session_registry_test.exe --gtest_filter=*Interrupt*` (kernel log noise goes to the same stdout; filter with `grep -v "^\[elara\]"`).
|
package/docs/releasing.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Releasing
|
|
2
2
|
|
|
3
|
-
Jovian is published to npm as
|
|
3
|
+
Jovian is published to npm as six packages under the `@damurka` scope, one library and five platform packages (Windows x64, Linux x64 and arm64, macOS x64 and arm64; see [Platforms that are not supported](#platforms-that-are-not-supported)):
|
|
4
4
|
|
|
5
5
|
| Package | Contents |
|
|
6
6
|
|---|---|
|
|
7
7
|
| `@damurka/jovian` | The compiled TypeScript library, the `hera` R package, docs. Lists the platform packages below as `optionalDependencies`. This is the one users install. |
|
|
8
8
|
| `@damurka/jovian-win32-x64` | `themisto.exe`, `elara.exe`, `carpo.exe` and the DLLs they need. |
|
|
9
|
-
| `@damurka/jovian-linux-x64`, `-darwin-arm64` | `themisto`, `elara`, `carpo`. |
|
|
9
|
+
| `@damurka/jovian-linux-x64`, `-linux-arm64`, `-darwin-x64`, `-darwin-arm64` | `themisto`, `elara`, `carpo`. |
|
|
10
10
|
|
|
11
11
|
Each platform package declares `os` and `cpu`, so npm installs only the one that matches the machine. At run time the library finds the binaries in that package (see [`lib/session/native-paths.ts`](../lib/session/native-paths.ts): `JOVIAN_NATIVE_DIR`, then the platform package, then a source checkout's `dist/native/Release`). All four packages are published at the **same version**; the main package pins the platform packages to it.
|
|
12
12
|
|
|
@@ -69,20 +69,26 @@ node scripts/release-smoke.mjs --dir dist/release/tarballs --python
|
|
|
69
69
|
|
|
70
70
|
Nothing there publishes. Publishing by hand is `npm publish <tarball> --access public`, platform packages first.
|
|
71
71
|
|
|
72
|
-
##
|
|
72
|
+
## Platforms that are not supported
|
|
73
|
+
|
|
74
|
+
A platform is published only when there is a real R for it to pair with and a runner that can build and smoke-test it end to end.
|
|
75
|
+
|
|
76
|
+
- **Windows on ARM (`win32-arm64`).** R for Windows on ARM64 is experimental and not on CRAN (no binary packages either), so there is no R for a native ARM64 kernel to load. People on Windows on ARM run x64 R under emulation, which would need the x64 kernel, but npm installs the x64 package only on x64 CPUs. Not supported until R for Windows on ARM64 is released.
|
|
77
|
+
- **32-bit ARM Linux (`linux-armhf`).** GitHub's hosted runners cannot run 32-bit ARM code (the arm64 machines lack AArch32), so it can only be built by cross-compiling and tested under QEMU emulation, which is not set up.
|
|
73
78
|
|
|
74
|
-
|
|
79
|
+
Their users get `jovian: there are no prebuilt kernels for <os>-<cpu>` and can build from source and set `JOVIAN_NATIVE_DIR`.
|
|
80
|
+
|
|
81
|
+
## Adding a platform
|
|
75
82
|
|
|
76
83
|
1. Add the target to `TARGETS` in `scripts/release.mjs` and to `SUPPORTED_PLATFORMS` in `lib/session/native-paths.ts` (a unit test fails if they differ).
|
|
77
|
-
2. Add it to the build matrix in `.github/workflows/release.yml
|
|
84
|
+
2. Add it to the build matrix in `.github/workflows/release.yml`.
|
|
78
85
|
3. Run the workflow by hand (a dry run) and fix what the smoke test finds before tagging a release.
|
|
79
|
-
|
|
80
|
-
Until a platform is added, its users get `jovian: there are no prebuilt kernels for <os>-<cpu>` and can build from source and set `JOVIAN_NATIVE_DIR`.
|
|
86
|
+
4. A new platform is a **new package on npm**, and npm cannot stage the first version of a package: publish it by hand once (see [The first release is published by hand](#the-first-release-is-published-by-hand)) before the main package that depends on it is approved. The workflow prints a warning naming each such package and does not fail.
|
|
81
87
|
|
|
82
88
|
## Things to know
|
|
83
89
|
|
|
84
90
|
- **hera upgrades.** npm resets file modification times, so the kernel's usual "is the hera source newer than the installed one" check cannot fire for an npm install. The staged `hera` `DESCRIPTION` is stamped with `Config/jovian/release: <version>`, and the kernel reinstalls `hera` when that stamp differs from the installed copy's. Users therefore get the matching `hera` after upgrading the package, once.
|
|
85
|
-
- **Linux binaries and glibc.** They are built on `ubuntu-24.04` (glibc 2.39) and run on any distribution with at least that glibc. Building on an older image would widen compatibility;
|
|
91
|
+
- **Linux binaries and glibc.** They are built on `ubuntu-24.04` (glibc 2.39) and run on any distribution with at least that glibc. Building on an older image would widen compatibility; the `linux-arm64` build runs on `ubuntu-24.04-arm`.
|
|
86
92
|
- **macOS.** The build sets `MACOSX_DEPLOYMENT_TARGET=14.0`. The binaries are not code-signed or notarized; binaries installed through npm are not quarantined by Gatekeeper, so this works, but bundling them into a downloaded `.app` would require signing.
|
|
87
93
|
- **Windows.** The DLLs come from vcpkg's `x64-windows` triplet and are copied next to the executables; the binaries link the dynamic Visual C++ runtime (`MSVCP140.dll`, `VCRUNTIME140.dll`, checked with `dumpbin /dependents`), which is **not** bundled: users need the "Microsoft Visual C++ Redistributable" (x64, 2015–2022), and the README's Install section says so. Bundling the runtime DLLs into the package, or linking the runtime statically, would remove that requirement.
|
|
88
94
|
- **Executable bit.** npm does not reliably preserve it; the library `chmod`s the kernels before starting them.
|
package/docs/troubleshooting.md
CHANGED
|
@@ -21,13 +21,13 @@ Messages below are quoted from the code. **First habit:** a kernel that fails to
|
|
|
21
21
|
| Message | Meaning and fix |
|
|
22
22
|
|---|---|
|
|
23
23
|
| `jovian: the kernel binaries were not found. Expected the '@damurka/jovian-<os>-<cpu>' package …` | The platform package was not installed: it is an optional dependency, so `npm install --omit=optional` / `--no-optional` skips it (reinstall without the flag), or a lockfile made on another OS omitted it (`npm install` on this OS). In a source checkout: run `npm run build`, or set `$JOVIAN_NATIVE_DIR`. |
|
|
24
|
-
| `jovian: there are no prebuilt kernels for <os>-<cpu> (supported: …)` | Prebuilt packages exist for `win32-x64`, `linux-x64` and `darwin-arm64` only (
|
|
24
|
+
| `jovian: there are no prebuilt kernels for <os>-<cpu> (supported: …)` | Prebuilt packages exist for `win32-x64`, `linux-x64`, `linux-arm64`, `darwin-x64` and `darwin-arm64` only (Windows on ARM and 32-bit ARM Linux are not supported). Elsewhere, [build from source](../README.md#requirements) and set `$JOVIAN_NATIVE_DIR` to `dist/native/Release`. |
|
|
25
25
|
| `version 'GLIBC_2.xx' not found` on Linux | The prebuilt Linux binaries need a glibc at least as new as the one they were built with (Ubuntu 24.04: 2.39). Build from source on the older system. |
|
|
26
26
|
| `Supervisor process exited before it was ready (code 1)` | `themisto` refused to start — typically `[themisto] FATAL: kernel executable not found at … (pass --kernel-exe to override)`: `elara` is missing next to `themisto`. |
|
|
27
27
|
| `no kernel executable is configured for kernelType 'python' …` | `carpo` was not built (or not found beside `themisto`). It is built by default; check `dist/native/Release/carpo[.exe]` and Themisto's `[themisto] NOTE: no Python kernel executable found` line. R sessions are unaffected. |
|
|
28
28
|
| `workingDirectory does not exist or is not a directory: <path>` | Create the directory first, or fix the path. |
|
|
29
|
-
| `Kernel process exited before it could register -- check its stderr output for the actual error.` | The kernel crashed at start-up.
|
|
30
|
-
| `Did not receive kernel configuration within 60s -- the kernel process is still running but never registered. Check its stderr output for what it's doing.` | The kernel started but hung before registering — e.g. R blocked loading packages, a very slow disk, or a security product scanning the process.
|
|
29
|
+
| `Kernel process exited before it could register -- check its stderr output for the actual error.` | The kernel crashed at start-up. The exception's message ends with `Kernel output:` and the kernel's own error lines; for the full output run with `JOVIAN_LOG_LEVEL=debug` (or `new SessionManager({ kernelOutput: true })`). See the R / Python sections below. |
|
|
30
|
+
| `Did not receive kernel configuration within 60s -- the kernel process is still running but never registered. Check its stderr output for what it's doing.` | The kernel started but hung before registering — e.g. R blocked loading packages, a very slow disk, or a security product scanning the process. Run with `JOVIAN_LOG_LEVEL=debug` to print the kernel's output and see how far it got. |
|
|
31
31
|
| `WebSocket connection to session <id> failed` / `Session <id> closed before it was ready` | The supervisor accepted the session but the WebSocket could not be established/kept — usually the supervisor died. Look for its exit. |
|
|
32
32
|
|
|
33
33
|
### R
|
|
@@ -38,6 +38,7 @@ Messages below are quoted from the code. **First habit:** a kernel that fails to
|
|
|
38
38
|
| `R_HOME is not set -- elara needs a working R installation to run. …` | The library could not find R (it tries `$R_HOME`, `R RHOME`, and on Windows the registry): put R on `PATH` or pass `rHome` (`R RHOME` prints it). |
|
|
39
39
|
| `No libpython3.*.so* … was found under '/lib' … Is Python installed at ''?` | The library could not find Python (it tries `$PYTHONHOME`, then `python3`/`python` on `PATH`): install Python 3 (`sudo apt install python3`) or pass `pythonHome`. |
|
|
40
40
|
| `Could not set up the R packages the kernel needs: …` | The one-time install of `hera` and its dependencies (see the README's Install section) failed; the text after the colon is R's own reason (no internet access to CRAN, no compiler on Linux, a library that cannot be written to). Fix that and call `createSession()` again; it retries. `JOVIAN_SKIP_R_SETUP=1` skips the step. |
|
|
41
|
+
| `ERROR: failed to lock directory '<library>' for modifying. Try removing '<library>/00LOCK-<pkg>'` | An earlier install of that package was interrupted (Ctrl+C, a kill, a timeout) and left its lock folder, which makes R refuse every later install of it. The automatic setup removes locks older than 5 minutes itself; to clear one now: `rm -rf <library>/00LOCK-*` (Windows: delete the `00LOCK-*` folder), then run again. |
|
|
41
42
|
| `unable to load shared object '/usr/lib/R/site-library/<pkg>/libs/<pkg>.so': undefined symbol: SETLENGTH` (or another `undefined symbol`) | Only when the automatic setup is skipped or you install by hand. Debian/Ubuntu: a package installed with `apt` (`r-cran-*`, in `/usr/lib/R/site-library`) was built for an older R than yours (the automatic setup reinstalls these itself). Reinstall it, and everything it needs, from CRAN into your own library, which R searches first. Needs a compiler (`sudo apt install build-essential`):<br>`wanted <- c("remotes","cli","evaluate","glue","IRdisplay","jsonlite","R6","repr","rlang")`<br>`all <- unique(c(wanted, unlist(tools::package_dependencies(wanted, db = available.packages(), recursive = TRUE), use.names = FALSE)))`<br>`ip <- installed.packages(); install.packages(setdiff(all, rownames(ip)[!is.na(ip[, "Priority"])]), repos = "https://cloud.r-project.org")` |
|
|
42
43
|
| `WARNING: 'hera' package could not be loaded (status: install_failed: …)` | Elara could not install the bundled `hera` R package; the text after `install_failed:` is R's own reason. Usually one of `hera`'s CRAN dependencies is missing or cannot be built (install them first: `install.packages(c("remotes", "cli", "evaluate", "glue", "IRdisplay", "jsonlite", "R6", "repr", "rlang"))`), or the R library is not writable. To see the full output run `R CMD INSTALL node_modules/@damurka/jovian/packages/hera`. |
|
|
43
44
|
| `Could not load …/lib/libR.so (…). Is R installed at '…'? If this R was built from source, it needs to have been configured with --enable-R-shlib, or no libR.so exists at all` | Use a distribution/CRAN R, or rebuild R with `--enable-R-shlib`. |
|
|
@@ -141,7 +141,7 @@ export class ExecutionQueue {
|
|
|
141
141
|
break;
|
|
142
142
|
case 'error':
|
|
143
143
|
pending.output.push(message);
|
|
144
|
-
this.logger?.
|
|
144
|
+
this.logger?.debug(`Execution ${message.parentMsgId} reported an R error`, { evalue: message.content?.evalue });
|
|
145
145
|
if (pending.stopOnError) {
|
|
146
146
|
this.abortQueued();
|
|
147
147
|
}
|
|
@@ -7,7 +7,7 @@ export declare const PACKAGE_NAME = "@damurka/jovian";
|
|
|
7
7
|
* the main package only where it matches (each declares `os`/`cpu`). These are
|
|
8
8
|
* the platforms that get a package.
|
|
9
9
|
*/
|
|
10
|
-
export declare const SUPPORTED_PLATFORMS: readonly ['win32-x64', 'linux-x64', 'darwin-arm64'];
|
|
10
|
+
export declare const SUPPORTED_PLATFORMS: readonly ['win32-x64', 'linux-x64', 'linux-arm64', 'darwin-x64', 'darwin-arm64'];
|
|
11
11
|
export declare function platformPackageName(platform?: string, arch?: string): string | undefined;
|
|
12
12
|
export interface NativeLocation {
|
|
13
13
|
/** Directory holding themisto, elara and carpo. */
|
|
@@ -11,7 +11,7 @@ export const PACKAGE_NAME = `${PACKAGE_SCOPE}/jovian`;
|
|
|
11
11
|
* the main package only where it matches (each declares `os`/`cpu`). These are
|
|
12
12
|
* the platforms that get a package.
|
|
13
13
|
*/
|
|
14
|
-
export const SUPPORTED_PLATFORMS = ['win32-x64', 'linux-x64', 'darwin-arm64'];
|
|
14
|
+
export const SUPPORTED_PLATFORMS = ['win32-x64', 'linux-x64', 'linux-arm64', 'darwin-x64', 'darwin-arm64'];
|
|
15
15
|
export function platformPackageName(platform = process.platform, arch = process.arch) {
|
|
16
16
|
const key = `${platform}-${arch}`;
|
|
17
17
|
return SUPPORTED_PLATFORMS.includes(key) ? `${PACKAGE_NAME}-${key}` : undefined;
|
package/lib/session/r-setup.d.ts
CHANGED
package/lib/session/r-setup.js
CHANGED
|
@@ -24,6 +24,7 @@ import { createInterface } from 'node:readline';
|
|
|
24
24
|
export const R_SETUP_SCRIPT = String.raw `
|
|
25
25
|
src <- commandArgs(trailingOnly = TRUE)[1]
|
|
26
26
|
say <- function(...) cat("JOVIAN_R_SETUP: ", ..., "\n", sep = "")
|
|
27
|
+
note <- function(...) cat("JOVIAN_R_SETUP_INFO: ", ..., "\n", sep = "")
|
|
27
28
|
fail <- function(...) {
|
|
28
29
|
cat("JOVIAN_R_SETUP_ERROR: ", ..., "\n", sep = "")
|
|
29
30
|
quit(save = "no", status = 1)
|
|
@@ -41,7 +42,7 @@ hera_current <- function() {
|
|
|
41
42
|
suppressWarnings(suppressMessages(requireNamespace("hera", quietly = TRUE)))
|
|
42
43
|
}
|
|
43
44
|
if (hera_current()) {
|
|
44
|
-
|
|
45
|
+
note("hera is already installed")
|
|
45
46
|
quit(save = "no", status = 0)
|
|
46
47
|
}
|
|
47
48
|
|
|
@@ -59,6 +60,15 @@ if (is.na(lib)) {
|
|
|
59
60
|
.libPaths(c(lib, .libPaths()))
|
|
60
61
|
}
|
|
61
62
|
|
|
63
|
+
for (lock in Sys.glob(file.path(lib, "00LOCK-*"))) {
|
|
64
|
+
# An install that was killed (Ctrl+C, a timeout) leaves its lock behind, and
|
|
65
|
+
# every later install of that package is refused until it is removed.
|
|
66
|
+
if (difftime(Sys.time(), file.info(lock)$mtime, units = "mins") > 5) {
|
|
67
|
+
say("removing a stale lock left by an interrupted install: ", lock)
|
|
68
|
+
unlink(lock, recursive = TRUE)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
62
72
|
ip <- installed.packages()
|
|
63
73
|
base_packages <- rownames(ip)[!is.na(ip[, "Priority"])]
|
|
64
74
|
fields <- tryCatch(read.dcf(file.path(src, "DESCRIPTION"), fields = c("Depends", "Imports", "LinkingTo")), error = function(e) NULL)
|
|
@@ -90,9 +100,28 @@ if (length(missing) > 0) {
|
|
|
90
100
|
error = function(e) fail("installing R packages failed: ", conditionMessage(e)))
|
|
91
101
|
still_missing <- missing[!vapply(missing, usable, logical(1))]
|
|
92
102
|
if (length(still_missing) > 0) {
|
|
103
|
+
# The warnings only say "non-zero exit status". Packages that need
|
|
104
|
+
# another one that failed fail too, so re-run the install of the ones
|
|
105
|
+
# that do not, on their own, and report what R and the compiler said.
|
|
106
|
+
needs <- tryCatch(tools::package_dependencies(still_missing, db = available, recursive = FALSE), error = function(e) list())
|
|
107
|
+
roots <- still_missing[vapply(still_missing, function(p) !any(needs[[p]] %in% still_missing), logical(1))]
|
|
108
|
+
reasons <- character()
|
|
109
|
+
for (p in utils::head(roots, 2)) {
|
|
110
|
+
why <- tryCatch({
|
|
111
|
+
tarball <- utils::download.packages(p, destdir = tempdir(), repos = getOption("repos"), type = "source", quiet = TRUE)[1, 2]
|
|
112
|
+
out <- suppressWarnings(system2(file.path(R.home("bin"), "R"), c("CMD", "INSTALL", paste0("--library=", shQuote(lib)), shQuote(tarball)), stdout = TRUE, stderr = TRUE))
|
|
113
|
+
if (is.null(attr(out, "status")) || identical(attr(out, "status"), 0L)) {
|
|
114
|
+
"it installed when retried on its own, so the failure may be temporary: try again"
|
|
115
|
+
} else {
|
|
116
|
+
important <- grep("error|ERROR|undefined|fatal|cannot|not found|No such file|Killed", out, value = TRUE)
|
|
117
|
+
substr(gsub("[[:space:]]+", " ", paste(utils::tail(if (length(important)) important else out, 5), collapse = " ; ")), 1, 700)
|
|
118
|
+
}
|
|
119
|
+
}, error = function(e) conditionMessage(e))
|
|
120
|
+
reasons <- c(reasons, paste0(p, ": ", why))
|
|
121
|
+
}
|
|
93
122
|
fail("could not install these R packages: ", paste(still_missing, collapse = ", "),
|
|
94
|
-
" --
|
|
95
|
-
|
|
123
|
+
if (length(reasons)) paste0(" -- ", paste(reasons, collapse = " | ")) else "",
|
|
124
|
+
" (on Linux packages are compiled from source and need a compiler: sudo apt install build-essential)")
|
|
96
125
|
}
|
|
97
126
|
}
|
|
98
127
|
|
|
@@ -187,7 +216,9 @@ async function setUp(rscript, heraSrcPath, rLibs, logger, deps) {
|
|
|
187
216
|
if (line.startsWith('JOVIAN_R_SETUP_ERROR: '))
|
|
188
217
|
failure = line.slice('JOVIAN_R_SETUP_ERROR: '.length);
|
|
189
218
|
else if (line.startsWith('JOVIAN_R_SETUP: '))
|
|
190
|
-
logger.
|
|
219
|
+
logger.notice(`R setup: ${line.slice('JOVIAN_R_SETUP: '.length)}`);
|
|
220
|
+
else if (line.startsWith('JOVIAN_R_SETUP_INFO: '))
|
|
221
|
+
logger.info(`R setup: ${line.slice('JOVIAN_R_SETUP_INFO: '.length)}`);
|
|
191
222
|
else if (line.trim())
|
|
192
223
|
otherOutput.push(line.trim());
|
|
193
224
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
|
-
import type { CommInfoReplyContent, CompleteReplyContent, EngineOptions, ExecutionHistoryEntry, ExecutionOptions, ExecutionResult, InspectReplyContent, IsCompleteReplyContent, KernelHistoryEntry, KernelHistoryOptions, KernelInfoReplyContent, SessionStatusInfo, ShinyAppHandle, ShinyAppOptions } from '../types/index.js';
|
|
2
|
+
import type { CommInfoReplyContent, CompleteReplyContent, EngineOptions, ExecutionHistoryEntry, ExecutionOptions, ExecutionResult, InspectReplyContent, IsCompleteReplyContent, KernelHistoryEntry, KernelHistoryOptions, KernelInfoReplyContent, LoggerFunction, LogThreshold, SessionManagerOptions, SessionStatusInfo, ShinyAppHandle, ShinyAppOptions } from '../types/index.js';
|
|
3
3
|
import type { ExecutionState } from '../types/messages.js';
|
|
4
4
|
import { SupervisorClient, type SessionConnectionInfo } from './supervisor-client.js';
|
|
5
5
|
import { Comm } from './comm.js';
|
|
@@ -27,7 +27,10 @@ export declare class Session extends EventEmitter {
|
|
|
27
27
|
private readonly executionHistory;
|
|
28
28
|
private readonly executionHistoryByMsgId;
|
|
29
29
|
private readonly historyStreamChars;
|
|
30
|
-
constructor(info: SessionConnectionInfo, options: EngineOptions, supervisor: SupervisorClient
|
|
30
|
+
constructor(info: SessionConnectionInfo, options: EngineOptions, supervisor: SupervisorClient, logging?: {
|
|
31
|
+
level?: LogThreshold | undefined;
|
|
32
|
+
logger?: LoggerFunction | undefined;
|
|
33
|
+
});
|
|
31
34
|
/**
|
|
32
35
|
* (Re)establishes the WebSocket to this.info's session and resolves
|
|
33
36
|
* once it's ready. Used both by the constructor and by restart() --
|
|
@@ -209,10 +212,18 @@ export declare class Session extends EventEmitter {
|
|
|
209
212
|
kill(): void;
|
|
210
213
|
}
|
|
211
214
|
export declare class SessionManager {
|
|
215
|
+
private readonly logLevel;
|
|
216
|
+
private readonly customLogger;
|
|
212
217
|
private readonly logger;
|
|
213
218
|
private readonly supervisor;
|
|
214
219
|
private readonly sessions;
|
|
215
220
|
private exitHandlerRegistered;
|
|
221
|
+
/**
|
|
222
|
+
* Quiet by default: only one-time setup notices, warnings and errors are
|
|
223
|
+
* printed. See SessionManagerOptions for `logLevel`, `logger` and
|
|
224
|
+
* `kernelOutput` (and the JOVIAN_LOG_LEVEL / JOVIAN_KERNEL_OUTPUT variables).
|
|
225
|
+
*/
|
|
226
|
+
constructor(options?: SessionManagerOptions);
|
|
216
227
|
/** Creates a new R session in its own OS process and waits for it to be ready. */
|
|
217
228
|
createSession(requested?: EngineOptions): Promise<Session>;
|
|
218
229
|
/** Gracefully stops every session managed by this instance. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
2
|
import { randomUUID } from 'crypto';
|
|
3
|
-
import { Logger } from '../utils/logger.js';
|
|
3
|
+
import { Logger, defaultLogLevel } from '../utils/logger.js';
|
|
4
4
|
import { MessageRouter } from '../messaging/message-router.js';
|
|
5
5
|
import { ExecutionQueue } from '../execution/execution-queue.js';
|
|
6
6
|
import { MiddlewareChain } from '../middleware/middleware-chain.js';
|
|
@@ -89,12 +89,12 @@ export class Session extends EventEmitter {
|
|
|
89
89
|
executionHistory = [];
|
|
90
90
|
executionHistoryByMsgId = new Map();
|
|
91
91
|
historyStreamChars = new WeakMap();
|
|
92
|
-
constructor(info, options, supervisor) {
|
|
92
|
+
constructor(info, options, supervisor, logging = {}) {
|
|
93
93
|
super();
|
|
94
94
|
this.info = info;
|
|
95
95
|
this.currentOptions = options;
|
|
96
96
|
this.supervisor = supervisor;
|
|
97
|
-
this.logger = new Logger(options.logger);
|
|
97
|
+
this.logger = new Logger(options.logger ?? logging.logger, logging.level);
|
|
98
98
|
this.on('message', (message) => {
|
|
99
99
|
this.recordExecutionHistory(message);
|
|
100
100
|
this.settleRequest(message);
|
|
@@ -781,10 +781,25 @@ export class Session extends EventEmitter {
|
|
|
781
781
|
}
|
|
782
782
|
}
|
|
783
783
|
export class SessionManager {
|
|
784
|
-
|
|
785
|
-
|
|
784
|
+
logLevel;
|
|
785
|
+
customLogger;
|
|
786
|
+
logger;
|
|
787
|
+
supervisor;
|
|
786
788
|
sessions = new Set();
|
|
787
789
|
exitHandlerRegistered = false;
|
|
790
|
+
/**
|
|
791
|
+
* Quiet by default: only one-time setup notices, warnings and errors are
|
|
792
|
+
* printed. See SessionManagerOptions for `logLevel`, `logger` and
|
|
793
|
+
* `kernelOutput` (and the JOVIAN_LOG_LEVEL / JOVIAN_KERNEL_OUTPUT variables).
|
|
794
|
+
*/
|
|
795
|
+
constructor(options = {}) {
|
|
796
|
+
this.logLevel = options.logLevel ?? defaultLogLevel();
|
|
797
|
+
this.customLogger = options.logger;
|
|
798
|
+
this.logger = new Logger(this.customLogger, this.logLevel);
|
|
799
|
+
const verbose = this.logLevel === 'trace' || this.logLevel === 'debug';
|
|
800
|
+
const forwardKernelOutput = options.kernelOutput ?? (Boolean(process.env.JOVIAN_KERNEL_OUTPUT) || verbose);
|
|
801
|
+
this.supervisor = new SupervisorClient(this.logger, { forwardKernelOutput });
|
|
802
|
+
}
|
|
788
803
|
/** Creates a new R session in its own OS process and waits for it to be ready. */
|
|
789
804
|
async createSession(requested = {}) {
|
|
790
805
|
// Finds R / Python when rHome / pythonHome were not given (see runtimes.ts).
|
|
@@ -793,7 +808,7 @@ export class SessionManager {
|
|
|
793
808
|
// First R session only: installs hera and what it needs (see r-setup.ts).
|
|
794
809
|
await ensureRPackages(options, this.logger);
|
|
795
810
|
const info = await this.supervisor.createSession(options);
|
|
796
|
-
const session = new Session(info, options, this.supervisor);
|
|
811
|
+
const session = new Session(info, options, this.supervisor, { level: this.logLevel, logger: this.customLogger });
|
|
797
812
|
this.sessions.add(session);
|
|
798
813
|
this.registerExitHandler();
|
|
799
814
|
try {
|
|
@@ -809,7 +824,7 @@ export class SessionManager {
|
|
|
809
824
|
async stopAll() {
|
|
810
825
|
await Promise.all([...this.sessions].map((session) => session.stop()));
|
|
811
826
|
this.sessions.clear();
|
|
812
|
-
this.supervisor.kill();
|
|
827
|
+
this.supervisor.kill(true);
|
|
813
828
|
}
|
|
814
829
|
/**
|
|
815
830
|
* Forcibly terminates every session. Prefer stopAll(), but a session
|
|
@@ -10,7 +10,14 @@ export declare class SupervisorClient {
|
|
|
10
10
|
private child;
|
|
11
11
|
private readyPromise;
|
|
12
12
|
private readonly logger;
|
|
13
|
-
|
|
13
|
+
private readonly forwardKernelOutput;
|
|
14
|
+
private readonly recentOutput;
|
|
15
|
+
constructor(logger: Logger, options?: {
|
|
16
|
+
forwardKernelOutput?: boolean;
|
|
17
|
+
});
|
|
18
|
+
private rememberOutput;
|
|
19
|
+
/** The message plus what the kernels said just before, when that was not already printed. */
|
|
20
|
+
private withKernelOutput;
|
|
14
21
|
private ensureStarted;
|
|
15
22
|
private spawnSupervisor;
|
|
16
23
|
createSession(options: EngineOptions): Promise<SessionConnectionInfo>;
|
|
@@ -30,7 +37,11 @@ export declare class SupervisorClient {
|
|
|
30
37
|
* close this session and create a new one just to pick a different R.
|
|
31
38
|
*/
|
|
32
39
|
restartSession(info: SessionConnectionInfo, options?: Partial<EngineOptions>): Promise<void>;
|
|
33
|
-
/**
|
|
34
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Skips graceful per-session shutdown -- only for cleanup on the way out.
|
|
42
|
+
* `expected` is the normal end of stopAll(), after every session was
|
|
43
|
+
* stopped: not worth a warning.
|
|
44
|
+
*/
|
|
45
|
+
kill(expected?: boolean): void;
|
|
35
46
|
}
|
|
36
47
|
//# sourceMappingURL=supervisor-client.d.ts.map
|
|
@@ -33,8 +33,29 @@ export class SupervisorClient {
|
|
|
33
33
|
child;
|
|
34
34
|
readyPromise;
|
|
35
35
|
logger;
|
|
36
|
-
|
|
36
|
+
forwardKernelOutput;
|
|
37
|
+
// The supervisor's stderr is where every kernel's start-up output ends up
|
|
38
|
+
// ([elara] ..., [carpo] ...). It is kept, not printed, unless asked for,
|
|
39
|
+
// and attached to the error when a kernel fails to start.
|
|
40
|
+
recentOutput = [];
|
|
41
|
+
constructor(logger, options = {}) {
|
|
37
42
|
this.logger = logger;
|
|
43
|
+
this.forwardKernelOutput = options.forwardKernelOutput ?? false;
|
|
44
|
+
}
|
|
45
|
+
rememberOutput(line) {
|
|
46
|
+
if (!line.trim())
|
|
47
|
+
return;
|
|
48
|
+
this.recentOutput.push(line);
|
|
49
|
+
if (this.recentOutput.length > 200)
|
|
50
|
+
this.recentOutput.shift();
|
|
51
|
+
}
|
|
52
|
+
/** The message plus what the kernels said just before, when that was not already printed. */
|
|
53
|
+
withKernelOutput(message) {
|
|
54
|
+
if (this.forwardKernelOutput || this.recentOutput.length === 0)
|
|
55
|
+
return message;
|
|
56
|
+
const notable = this.recentOutput.filter((line) => /error|fatal|warning|failed|cannot|not found|no such/i.test(line));
|
|
57
|
+
const lines = (notable.length > 0 ? notable : this.recentOutput).slice(-8);
|
|
58
|
+
return `${message}\nKernel output:\n ${lines.join('\n ')}`;
|
|
38
59
|
}
|
|
39
60
|
ensureStarted() {
|
|
40
61
|
if (!this.readyPromise) {
|
|
@@ -48,7 +69,11 @@ export class SupervisorClient {
|
|
|
48
69
|
this.logger.debug(`Spawning supervisor process from: ${exePath}`);
|
|
49
70
|
const child = spawn(exePath, [], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
50
71
|
this.child = child;
|
|
51
|
-
child.stderr
|
|
72
|
+
createInterface({ input: child.stderr }).on('line', (line) => {
|
|
73
|
+
this.rememberOutput(line);
|
|
74
|
+
if (this.forwardKernelOutput)
|
|
75
|
+
process.stderr.write(`${line}\n`);
|
|
76
|
+
});
|
|
52
77
|
const rl = createInterface({ input: child.stdout });
|
|
53
78
|
const onLine = (line) => {
|
|
54
79
|
let message;
|
|
@@ -87,7 +112,7 @@ export class SupervisorClient {
|
|
|
87
112
|
});
|
|
88
113
|
const body = await res.json();
|
|
89
114
|
if (!res.ok || !body.sessionId) {
|
|
90
|
-
throw new Error(body.error ?? `Supervisor failed to create session (HTTP ${res.status})`);
|
|
115
|
+
throw new Error(this.withKernelOutput(body.error ?? `Supervisor failed to create session (HTTP ${res.status})`));
|
|
91
116
|
}
|
|
92
117
|
return { sessionId: body.sessionId, httpBase, wsBase: `ws://127.0.0.1:${wsPort}` };
|
|
93
118
|
}
|
|
@@ -126,10 +151,18 @@ export class SupervisorClient {
|
|
|
126
151
|
throw new Error(body.error ?? `Supervisor failed to restart session ${info.sessionId} (HTTP ${res.status})`);
|
|
127
152
|
}
|
|
128
153
|
}
|
|
129
|
-
/**
|
|
130
|
-
|
|
154
|
+
/**
|
|
155
|
+
* Skips graceful per-session shutdown -- only for cleanup on the way out.
|
|
156
|
+
* `expected` is the normal end of stopAll(), after every session was
|
|
157
|
+
* stopped: not worth a warning.
|
|
158
|
+
*/
|
|
159
|
+
kill(expected = false) {
|
|
131
160
|
if (this.child && !this.child.killed) {
|
|
132
|
-
|
|
161
|
+
const message = `Force-killing supervisor process (pid ${this.child.pid})`;
|
|
162
|
+
if (expected)
|
|
163
|
+
this.logger.debug(message);
|
|
164
|
+
else
|
|
165
|
+
this.logger.warn(message);
|
|
133
166
|
this.child.kill();
|
|
134
167
|
}
|
|
135
168
|
}
|
package/lib/types/engine.d.ts
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
import type { JupyterMessage } from './messages.js';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* 'notice' is for the few things a user should be told even when the library
|
|
4
|
+
* is otherwise quiet (e.g. the one-time install of the R packages, which takes
|
|
5
|
+
* a while); it sits between 'info' and 'warn'.
|
|
6
|
+
*/
|
|
7
|
+
export type LogLevel = 'trace' | 'debug' | 'info' | 'notice' | 'warn' | 'error';
|
|
8
|
+
/** What the built-in console logger prints: this level and above; 'silent' prints nothing. */
|
|
9
|
+
export type LogThreshold = LogLevel | 'silent';
|
|
10
|
+
export interface SessionManagerOptions {
|
|
11
|
+
/**
|
|
12
|
+
* How much the library prints to the console (default 'notice': one-time
|
|
13
|
+
* setup messages, warnings and errors; env JOVIAN_LOG_LEVEL sets the
|
|
14
|
+
* default). Ignored for messages sent to `logger`, which receives all of them.
|
|
15
|
+
*/
|
|
16
|
+
logLevel?: LogThreshold | undefined;
|
|
17
|
+
/** Receives every log message instead of the console. */
|
|
18
|
+
logger?: LoggerFunction | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Print the kernels' own start-up output (the `[elara]` / `[carpo]` lines)
|
|
21
|
+
* to stderr as it happens. Off by default -- it is included in the error
|
|
22
|
+
* when a kernel fails to start -- and on when logLevel is 'debug' or
|
|
23
|
+
* 'trace' or env JOVIAN_KERNEL_OUTPUT is set.
|
|
24
|
+
*/
|
|
25
|
+
kernelOutput?: boolean | undefined;
|
|
26
|
+
}
|
|
3
27
|
export type LoggerFunction = (level: LogLevel, message: string, data?: any) => void;
|
|
4
28
|
export interface EngineOptions {
|
|
5
29
|
/**
|
package/lib/utils/logger.d.ts
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
|
-
import type { LoggerFunction } from '../types/index.js';
|
|
1
|
+
import type { LogLevel, LoggerFunction, LogThreshold } from '../types/index.js';
|
|
2
2
|
export declare function timestamp(): string;
|
|
3
|
+
/** True when a message at `level` passes `threshold`. */
|
|
4
|
+
export declare function passes(threshold: LogThreshold, level: LogLevel): boolean;
|
|
5
|
+
/** The console threshold when none is given: $JOVIAN_LOG_LEVEL if it names one, else 'notice'. */
|
|
6
|
+
export declare function defaultLogLevel(env?: Record<string, string | undefined>): LogThreshold;
|
|
3
7
|
export declare class Logger {
|
|
4
|
-
private customLogger
|
|
5
|
-
|
|
8
|
+
private readonly customLogger;
|
|
9
|
+
private readonly threshold;
|
|
10
|
+
/**
|
|
11
|
+
* A custom logger receives every message (it does its own filtering, as it
|
|
12
|
+
* always has); `threshold` only limits what is printed to the console.
|
|
13
|
+
*/
|
|
14
|
+
constructor(customLogger?: LoggerFunction, threshold?: LogThreshold);
|
|
15
|
+
private emit;
|
|
6
16
|
trace(message: string, data?: any): void;
|
|
7
17
|
debug(message: string, data?: any): void;
|
|
8
18
|
info(message: string, data?: any): void;
|
|
19
|
+
notice(message: string, data?: any): void;
|
|
9
20
|
warn(message: string, data?: any): void;
|
|
10
21
|
error(message: string, error?: any): void;
|
|
11
22
|
}
|
package/lib/utils/logger.js
CHANGED
|
@@ -9,50 +9,58 @@ export function timestamp() {
|
|
|
9
9
|
return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ` +
|
|
10
10
|
`${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}.${pad(now.getMilliseconds(), 3)}`;
|
|
11
11
|
}
|
|
12
|
+
const ORDER = { trace: 0, debug: 1, info: 2, notice: 3, warn: 4, error: 5, silent: 6 };
|
|
13
|
+
/** True when a message at `level` passes `threshold`. */
|
|
14
|
+
export function passes(threshold, level) {
|
|
15
|
+
return ORDER[level] >= ORDER[threshold];
|
|
16
|
+
}
|
|
17
|
+
/** The console threshold when none is given: $JOVIAN_LOG_LEVEL if it names one, else 'notice'. */
|
|
18
|
+
export function defaultLogLevel(env = process.env) {
|
|
19
|
+
const wanted = env.JOVIAN_LOG_LEVEL?.trim().toLowerCase();
|
|
20
|
+
return wanted && wanted in ORDER ? wanted : 'notice';
|
|
21
|
+
}
|
|
12
22
|
export class Logger {
|
|
13
23
|
customLogger;
|
|
14
|
-
|
|
24
|
+
threshold;
|
|
25
|
+
/**
|
|
26
|
+
* A custom logger receives every message (it does its own filtering, as it
|
|
27
|
+
* always has); `threshold` only limits what is printed to the console.
|
|
28
|
+
*/
|
|
29
|
+
constructor(customLogger, threshold = defaultLogLevel()) {
|
|
15
30
|
this.customLogger = customLogger;
|
|
31
|
+
this.threshold = threshold;
|
|
16
32
|
}
|
|
17
|
-
|
|
33
|
+
emit(level, message, data) {
|
|
18
34
|
if (this.customLogger) {
|
|
19
|
-
this.customLogger(
|
|
20
|
-
|
|
21
|
-
else {
|
|
22
|
-
console.log(`${timestamp()} [trace] ${message}`, data ? data : '');
|
|
35
|
+
this.customLogger(level, message, data);
|
|
36
|
+
return;
|
|
23
37
|
}
|
|
38
|
+
if (!passes(this.threshold, level))
|
|
39
|
+
return;
|
|
40
|
+
const print = level === 'warn' ? console.warn : level === 'error' ? console.error : console.log;
|
|
41
|
+
const line = `${timestamp()} [${level}] ${message}`;
|
|
42
|
+
if (data)
|
|
43
|
+
print(line, data);
|
|
44
|
+
else
|
|
45
|
+
print(line);
|
|
46
|
+
}
|
|
47
|
+
trace(message, data) {
|
|
48
|
+
this.emit('trace', message, data);
|
|
24
49
|
}
|
|
25
50
|
debug(message, data) {
|
|
26
|
-
|
|
27
|
-
this.customLogger('debug', message, data);
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
console.log(`${timestamp()} [debug] ${message}`, data ? data : '');
|
|
31
|
-
}
|
|
51
|
+
this.emit('debug', message, data);
|
|
32
52
|
}
|
|
33
53
|
info(message, data) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
console.log(`${timestamp()} [info] ${message}`, data ? data : '');
|
|
39
|
-
}
|
|
54
|
+
this.emit('info', message, data);
|
|
55
|
+
}
|
|
56
|
+
notice(message, data) {
|
|
57
|
+
this.emit('notice', message, data);
|
|
40
58
|
}
|
|
41
59
|
warn(message, data) {
|
|
42
|
-
|
|
43
|
-
this.customLogger('warn', message, data);
|
|
44
|
-
}
|
|
45
|
-
else {
|
|
46
|
-
console.warn(`${timestamp()} [warn] ${message}`, data ? data : '');
|
|
47
|
-
}
|
|
60
|
+
this.emit('warn', message, data);
|
|
48
61
|
}
|
|
49
62
|
error(message, error) {
|
|
50
|
-
|
|
51
|
-
this.customLogger('error', message, error);
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
console.error(`${timestamp()} [error] ${message}`, error ? error : '');
|
|
55
|
-
}
|
|
63
|
+
this.emit('error', message, error);
|
|
56
64
|
}
|
|
57
65
|
}
|
|
58
66
|
//# sourceMappingURL=logger.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@damurka/jovian",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "David Kariuki",
|
|
6
6
|
"repository": {
|
|
@@ -31,16 +31,18 @@
|
|
|
31
31
|
"exports": {
|
|
32
32
|
".": {
|
|
33
33
|
"types": "./lib/index.d.ts",
|
|
34
|
-
"import": "./lib/index.js"
|
|
34
|
+
"import": "./lib/index.js",
|
|
35
|
+
"default": "./lib/index.js"
|
|
35
36
|
},
|
|
36
37
|
"./types": {
|
|
37
38
|
"types": "./lib/types/index.d.ts",
|
|
38
|
-
"import": "./lib/types/index.js"
|
|
39
|
+
"import": "./lib/types/index.js",
|
|
40
|
+
"default": "./lib/types/index.js"
|
|
39
41
|
},
|
|
40
42
|
"./package.json": "./package.json"
|
|
41
43
|
},
|
|
42
44
|
"engines": {
|
|
43
|
-
"node": ">=22.
|
|
45
|
+
"node": ">=22.13.0"
|
|
44
46
|
},
|
|
45
47
|
"files": [
|
|
46
48
|
"lib",
|
|
@@ -50,8 +52,10 @@
|
|
|
50
52
|
"LICENSE"
|
|
51
53
|
],
|
|
52
54
|
"optionalDependencies": {
|
|
53
|
-
"@damurka/jovian-win32-x64": "0.1.
|
|
54
|
-
"@damurka/jovian-linux-x64": "0.1.
|
|
55
|
-
"@damurka/jovian-
|
|
55
|
+
"@damurka/jovian-win32-x64": "0.1.2",
|
|
56
|
+
"@damurka/jovian-linux-x64": "0.1.2",
|
|
57
|
+
"@damurka/jovian-linux-arm64": "0.1.2",
|
|
58
|
+
"@damurka/jovian-darwin-x64": "0.1.2",
|
|
59
|
+
"@damurka/jovian-darwin-arm64": "0.1.2"
|
|
56
60
|
}
|
|
57
61
|
}
|