Skip to content
This repository was archived by the owner on Jul 4, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
7758eda
[janhq/jan#1635] feat(WIP): decouple nitro engine into a library
InNoobWeTrust Jan 17, 2024
ffcd05d
fix: exclude nitro-node/Makefile from root .gitignore
InNoobWeTrust Jan 22, 2024
81ee8f3
chore(rebase): rebase nitro-node from 'dev' branch of janhq/jan
InNoobWeTrust Jan 22, 2024
85e3189
chore(build): make clean all cached packages and yarn versions
InNoobWeTrust Jan 22, 2024
55c540d
chore(build): download nitro before packing nitro-node
InNoobWeTrust Jan 22, 2024
8ba9202
chore(build): hook for installing nitro when using as dependency
InNoobWeTrust Jan 22, 2024
08657a1
refactor(WIP): decouple from @janhq/core
InNoobWeTrust Jan 23, 2024
6f625d9
fix(postinstall): download path for nitro binary
InNoobWeTrust Jan 24, 2024
8c742cf
fix(downloadnitro): log messages for downloading nitro variants
InNoobWeTrust Jan 24, 2024
5f46220
chore(nitro-node): cleanup dependencies
InNoobWeTrust Jan 24, 2024
9e2dd61
test(nitro-node): First test case for nitro-node
InNoobWeTrust Jan 24, 2024
33293ec
feat(nitro-node): chat completion function
InNoobWeTrust Jan 25, 2024
d703f74
fix(nitro-node): remove dependency on Jan's config file
InNoobWeTrust Jan 25, 2024
60063bd
chore(nitro-node/ci): cleanup CI workflow
InNoobWeTrust Jan 25, 2024
e8e7752
chore(nitro-node/ci): test install on different platforms
InNoobWeTrust Jan 25, 2024
f7ca9c8
chore(nitro-node/ci): allow install from git repo
InNoobWeTrust Jan 25, 2024
152f4b2
chore(nitro-node/ci): fix job name for github action workflow
InNoobWeTrust Jan 25, 2024
c81ff90
fix(nitro-node/ci): correct check condition for existence of bin dirs
InNoobWeTrust Jan 25, 2024
75d8907
chore(nitro-node/test): cleanup test assets
InNoobWeTrust Jan 25, 2024
029a142
fix(nitro-node/ci): remove nitro-node/dist from workflow dependency
InNoobWeTrust Jan 26, 2024
869470b
fix(nitro-node): several fixes
InNoobWeTrust Jan 27, 2024
fb7cb5c
feat(nitro-node): only download binaries if they are not exist
InNoobWeTrust Jan 27, 2024
c60e4f4
feat(nitro-node): allow setting custom bin directory
InNoobWeTrust Jan 27, 2024
6a42350
fix(nitro-node): Fix exporting types
InNoobWeTrust Jan 27, 2024
3697335
refactor(nitro-node): convert throwable functions to async
InNoobWeTrust Jan 28, 2024
9761294
fix(nitro-node/typings): fix export types for submodule scripts
InNoobWeTrust Jan 29, 2024
f026da5
feat(nitro-node/magic-number): Find matching model file by magic number
InNoobWeTrust Jan 29, 2024
1133759
chore(nitro-node/ci): remove feature branch from newly added workflows
InNoobWeTrust Jan 29, 2024
506d277
feat(nitr-node/postinstall): download latest release for nitro by def…
InNoobWeTrust Jan 30, 2024
b4aa567
fix(nitro-node/downloader): accept version tag with/without 'v' prefix
InNoobWeTrust Jan 30, 2024
ae1d4dd
fix(nitro-node): remove excessive console log in nitro.ts
InNoobWeTrust Jan 30, 2024
80dcc55
feat(nitro-node): trap terminate signal in order to kill nitro process
InNoobWeTrust Jan 30, 2024
7c75c7b
Merge remote-tracking branch 'upstream/main' into feat/1635/decouple-…
InNoobWeTrust Jan 30, 2024
0516440
chore(nitro-node/ci): enable CI build on MacOS M1
InNoobWeTrust Jan 31, 2024
e427816
fix(nitro-node/build): electron got included as dependency
InNoobWeTrust Jan 31, 2024
7b2c846
test(nitro-node): update chat completion test with more meaningful us…
InNoobWeTrust Jan 31, 2024
ff2cf08
fix(nitro-node/ci): remove feature branch from trigger
InNoobWeTrust Feb 1, 2024
b2b749d
refactor(nitro-node): nitro release url can be altered upon build
InNoobWeTrust Feb 3, 2024
91e2c24
refactor(nitro-node): move checkMagicBytes functions out to utils
InNoobWeTrust Feb 3, 2024
049ef1d
fix(nitro-node): more sensible wait time for tcp port when starting n…
InNoobWeTrust Feb 3, 2024
f5bc098
fix(nitro-node): default run mode should be based on CUDA availability
InNoobWeTrust Feb 3, 2024
cdbd4a1
fix(nitro-node): missing replace for global constants in index script
InNoobWeTrust Feb 4, 2024
b6727a0
fix(nitro-node): fetch is not available by default on some environment
InNoobWeTrust Feb 4, 2024
d10bbbb
Revert "fix(nitro-node): fetch is not available by default on some en…
InNoobWeTrust Feb 4, 2024
142a60d
fix(nitro-node): use cross-fetch on environments which do not have fetch
InNoobWeTrust Feb 4, 2024
21e3026
feat(nitro-node): allow programmatically specifying the nitro version…
InNoobWeTrust Feb 5, 2024
c6a2656
fix(nitro-node): add missing settings when loading model
InNoobWeTrust Feb 5, 2024
9bd5af3
Merge remote-tracking branch 'main' into feat/1635/decouple-nitro-inf…
InNoobWeTrust Feb 5, 2024
8a309a3
feat(nitro-node): add stdio and event handler for nitro subprocess
InNoobWeTrust Feb 6, 2024
1d79084
chore(nitro-node): split github workflow files into separate commit
InNoobWeTrust Feb 6, 2024
81246f2
fix(nitro-node): remove unused interface
InNoobWeTrust Feb 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ jobs:
./install_deps.sh
mkdir build && cd build
cmake -DWHISPER_COREML=1 -DNITRO_VERSION=${{ needs.set-nitro-version.outputs.version }} ..
CC=gcc-8 make -j $(sysctl -n hw.ncp)
CC=gcc-8 make -j $(sysctl -n hw.ncpu)
ls -la

- name: Package
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ CMakeFiles
CMakeScripts
Testing
Makefile
!nitro-node/Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
Expand Down
19 changes: 19 additions & 0 deletions nitro-node/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.vscode
.env

# Jan inference
.yarn
node_modules
*.tgz
yarn.lock
dist
build
.DS_Store
package-lock.json

*.log

# Nitro binary directory
bin/
# Locally downloaded model for testing
test/test_assets/*.gguf
2 changes: 2 additions & 0 deletions nitro-node/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
scripts-prepend-node-path=true
engine-strict=true
1 change: 1 addition & 0 deletions nitro-node/.yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodeLinker: node-modules
51 changes: 51 additions & 0 deletions nitro-node/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Makefile for Nitro-node - Build and Clean

# Default target, build all
.PHONY: all

all: publish

# Installs yarn dependencies
#install: build-core
install:
ifeq ($(OS),Windows_NT)
yarn config set network-timeout 300000
endif
yarn install

# Build
build: install
yarn run build

# Download Nitro
download-nitro: install
yarn run downloadnitro

test-ci: install
yarn test

# Note, this make target is just for testing on *NIX systems
test: install
@test -e test/test_assets/*.gguf && echo "test/test_assets/*.gguf is already downloaded" || (mkdir -p test/test_assets && cd test/test_assets/ && curl -JLO "https://huggingface.co/TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF/resolve/main/tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf")
yarn test

# Builds and pack
pack: build
yarn run build:publish

# Test that installation will also download nitro binaries
test-e2e-installation: pack
ifeq ($(OS),Windows_NT)
$$env:NITRO_NODE_VERSION=(npm version --json | jq '.["@janhq/nitro-node"]' | foreach {$$_.replace('"','')}) $$env:NITRO_NODE_PKG=(Resolve-Path -Path "janhq-nitro-node$$NITRO_NODE_VERSION.tgz") node ..\.github\scripts\e2e-test-install-nitro-node.js
else
NITRO_NODE_VERSION=$$(npm version --json | jq '.["@janhq/nitro-node"]' | tr -d '"') NITRO_NODE_PKG=$$(realpath "janhq-nitro-node-$${NITRO_NODE_VERSION}.tgz") node ../.github/scripts/e2e-test-install-nitro-node.js
endif

clean:
ifeq ($(OS),Windows_NT)
powershell -Command "Remove-Item -Recurse -Force -Path *.tgz, .yarn, yarn.lock, package-lock.json, bin, dist"
powershell -Command "Get-ChildItem -Path . -Include node_modules -Recurse -Directory | Remove-Item -Recurse -Force"
else
rm -rf *.tgz .yarn yarn.lock package-lock.json bin dist
find . -name "node_modules" -type d -prune -exec rm -rf '{}' +
endif
3 changes: 3 additions & 0 deletions nitro-node/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# NodeJS wrapper for Nitro

**TODO** Documenting on usage
14 changes: 14 additions & 0 deletions nitro-node/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { JestConfigWithTsJest } from "ts-jest";

const jestConfig: JestConfigWithTsJest = {
preset: "ts-jest",
testEnvironment: "node",
transformIgnorePatterns: ["/node_modules/"],
globals: {
RELEASE_URL_PREFIX: "https://api.github.com/repos/janhq/nitro/releases/",
TAGGED_RELEASE_URL_PREFIX:
"https://api.github.com/repos/janhq/nitro/releases/tags",
},
};

export default jestConfig;
78 changes: 78 additions & 0 deletions nitro-node/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
{
"name": "@janhq/nitro-node",
"version": "1.0.0",
"description": "This NodeJS library is a wrapper for Nitro, a lightweight (3mb) inference engine written in C++. See nitro.jan.ai",
"main": "./dist/index",
"module": "./dist/index.esm.js",
"types": "./dist/types/index",
"author": "Jan <service@jan.ai>",
"license": "AGPL-3.0",
"scripts": {
"test": "jest --verbose --detectOpenHandles",
"build": "tsc --module commonjs && rollup -c rollup.config.ts",
"predownloadnitro": "npm run build",
"downloadnitro": "node dist/scripts/index.cjs",
"build:publish": "npm pack",
"postinstall": "node -r @janhq/nitro-node/postinstall"
},
"exports": {
".": {
"import": "./dist/index.esm.js",
"require": "./dist/index.cjs",
"types": "./dist/types/index.d.ts"
},
"./scripts": {
"import": "./dist/scripts/index.esm.js",
"require": "./dist/scripts/index.cjs",
"types": "./dist/types/scripts/index.d.ts"
},
"./postinstall": "./postinstall.js"
},
"typesVersions": {
"*": {
".": [
"./dist/index.esm.js.map",
"./dist/types/index.d.ts"
],
"scripts": [
"./dist/scripts/index.esm.js.map",
"./dist/types/scripts/index.d.ts"
]
}
},
"devDependencies": {
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-replace": "^5.0.5",
"@types/download": "^8.0.5",
"@types/jest": "^29.5.11",
"@types/node": "^20.11.4",
"@types/os-utils": "^0.0.4",
"@types/tcp-port-used": "^1.0.4",
"jest": "^29.7.0",
"rollup": "^2.38.5",
"rollup-plugin-sourcemaps": "^0.6.3",
"rollup-plugin-typescript2": "^0.36.0",
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
},
"dependencies": {
"cross-fetch": "^4.0.0",
"download": "^8.0.0",
"fetch-retry": "^5.0.6",
"os-utils": "^0.0.14",
"tcp-port-used": "^1.0.2"
},
"engines": {
"node": ">=18.0.0"
},
"files": [
"postinstall.js",
"dist",
"package.json",
"tsconfig.json",
"README.md"
]
}
6 changes: 6 additions & 0 deletions nitro-node/postinstall.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Only run if this package is installed as dependency
if (process.env.INIT_CWD === process.cwd()) process.exit();

const path = require("node:path");
const { downloadNitro } = require("@janhq/nitro-node/scripts");
downloadNitro(path.join(__dirname, "bin"));
109 changes: 109 additions & 0 deletions nitro-node/rollup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import sourceMaps from "rollup-plugin-sourcemaps";
import typescript from "rollup-plugin-typescript2";
import json from "@rollup/plugin-json";
import replace from "@rollup/plugin-replace";

export default [
{
input: `src/index.ts`,
output: [
{ file: "dist/index.cjs", format: "cjs", sourcemap: true },
{ file: "dist/index.esm.js", format: "es", sourcemap: true },
],
// Indicate here external modules you don't wanna include in your bundle (i.e.: 'lodash')
external: [
// `download@8.0.0` requires `got@^8.3.1` which then optionally requires `electron`, result in wrong dependency
// Ref: https://github.com/kubernetes-client/javascript/issues/350#issue-500860208
// Ref: https://github.com/kubernetes-client/javascript/issues/350#issuecomment-553644659
"got",
],
watch: {
include: "src/**",
},
plugins: [
replace({
RELEASE_URL_PREFIX: JSON.stringify(
"https://api.github.com/repos/janhq/nitro/releases/",
),
TAGGED_RELEASE_URL_PREFIX: JSON.stringify(
"https://api.github.com/repos/janhq/nitro/releases/tags",
),
}),
// Allow json resolution
json(),
// Allow node_modules resolution, so you can use 'external' to control
// which external modules to include in the bundle
// https://github.com/rollup/rollup-plugin-node-resolve#usage
resolve({
extensions: [".ts", ".js", ".json"],
}),
// Allow bundling cjs modules (unlike webpack, rollup doesn't understand cjs)
// This should be after resolve() plugin
commonjs(),
// Compile TypeScript files
typescript({
useTsconfigDeclarationDir: true,
}),

// Resolve source maps to the original source
sourceMaps(),
],
},
{
input: `src/scripts/index.ts`,
output: [
{
file: "dist/scripts/index.cjs",
format: "cjs",
sourcemap: true,
},
{
file: "dist/scripts/index.esm.js",
format: "es",
sourcemap: true,
},
],
// Indicate here external modules you don't wanna include in your bundle (i.e.: 'lodash')
external: [
// `download@8.0.0` requires `got@^8.3.1` which then optionally requires `electron`, result in wrong dependency
// Ref: https://github.com/kubernetes-client/javascript/issues/350#issue-500860208
// Ref: https://github.com/kubernetes-client/javascript/issues/350#issuecomment-553644659
"got",
],
watch: {
include: "src/scripts/**",
},
plugins: [
replace({
RELEASE_URL_PREFIX: JSON.stringify(
"https://api.github.com/repos/janhq/nitro/releases/",
),
TAGGED_RELEASE_URL_PREFIX: JSON.stringify(
"https://api.github.com/repos/janhq/nitro/releases/tags",
),
}),
// Allow json resolution
json(),

// Allow node_modules resolution, so you can use 'external' to control
// which external modules to include in the bundle
// https://github.com/rollup/rollup-plugin-node-resolve#usage
resolve({
extensions: [".ts", ".js", ".json"],
}),

// Allow bundling cjs modules (unlike webpack, rollup doesn't understand cjs)
// This should be after resolve() plugin
commonjs(),
// Compile TypeScript files
typescript({
useTsconfigDeclarationDir: true,
}),

// Resolve source maps to the original source
sourceMaps(),
],
},
];
64 changes: 64 additions & 0 deletions nitro-node/src/execute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import path from "node:path";
import { getNvidiaConfig } from "./nvidia";

export interface NitroExecutableOptions {
executablePath: string;
cudaVisibleDevices: string;
}

/**
* Find which executable file to run based on the current platform.
* @returns The name of the executable file to run.
*/
export const executableNitroFile = (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I'm seeing here is that this function expects folderPath as the parameters, and there are a bunch of available built binaries for Nitro. In my opinion, it's not a good design.
Personally I think there should be a better way:

  • node-nitro exposes a util function to return the exact binary (e.g: Windows - with CUDA, or Mac ARM64 with Metal).
  • Then run init, in the init function it download if no binary there/ or user can prefill it with another util function.
  • If it fails, let it fail and show user why it failed.
  • getNvidiaConfig() is very specific, we should only use it in utils as Nitro will support many other build (AMD with Vulkan, sycl for Intel, linux arm etc)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Can we split this logic into another PR? Since I don't know what will be the naming agreement for the release artifact (it's with/without CUDA version and some inconsistencies). So defining a single generic logic for the executable file is somewhat out of my control...😅
  • In nitro.ts, the runModel() function already tries to download the binaries automatically if they are not there yet. If we want to define specific binary to download then we can run the system analysis on intialize(). After that, in runModel() we will have all the necessary information to download the correct one.
  • Not quite understand your point about the behavior when failing, can you share with me a logical flow about fail handling so I can understand better what you mean? 😅
  • I'm a little dumb when it comes to AMD and Intel things. For your suggestion, I will move the getNvidiaConfig() out and only call it in an iterative greedy approach in detecting available hardware (loop over supported hardware and run the check for them). Then after we have the supported-hardware array, we will have a decision on what binary to load? 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Why node-nitro needs to care about the model download?
  2. I'm ok with running analysis first and install correct Nitro, not expecting a folder with all nitro build waiting to be initialized (with opinionated name as win-cpu/nitro, mac/nitro. And it should reflect that idea
  3. For logical flow of failure: cpp compiled program can yield arbitrary error (core dump, illegal instructions, etc) that normal binding with subprocess just simply cannot understand. Those have to be handled with node-nitro if you think of providing surplus value here.
  4. It's not dumb or not, it's technical engineering that we think through use cases we MIGHT need to solve, not only the one we CURRENTLY have

Hope you understand. Thank you

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • I don't quite understand this, nitro-node only helps to get nitro binaries available on the user's machine, no downloading of the model here...😅
  • Ok, will go with this. But the logic will be not flexible in the early versions, need to discuss the naming schemes of nitro release archives (can we have only a single generic CUDA build without separating into different versions?)
  • The scope of this PR is to first split the logic from Jan into a library here for re-use and easier to maintain as it will be tightened with nitro development. I'm not yet involved much in the architecture/build decision of nitro so for now I will just stick with the subprocess. Until nitro can be used as a system library then make a node binding later (will be much more work). Let's go with the iterative approach and create another issue for Node binding at the moment, I don't want the PR to become too big to review with unplanned things...😅
  • I believe we can't handle all the unplanned things all at once or we will go nowhere. It might be the use case but not until it's confirmed to be worth our effort (user survey and confirmed to be valid first before doing). If things are not planned yet and not an agreement on the roadmap then I don't think we should spend much time over-engineering it. Just delivering features one step at a time...

For your suggestions, I'm considering and thinking about the best ways to adapt for now with minimal effort possible. But for things in the future, please share the roadmap link of the mentioned point and a defined plan so I can understand what are the steps to iteratively deliver it. I'm not a team member at the moment so I don't know well about your plans so pardon if I'm not understanding your points correctly...😅

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Why node-nitro needs to care about the model download?

    1. I'm ok with running analysis first and install correct Nitro, not expecting a folder with all nitro build waiting to be initialized (with opinionated name as win-cpu/nitro, mac/nitro. And it should reflect that idea

    2. For logical flow of failure: cpp compiled program can yield arbitrary error (core dump, illegal instructions, etc) that normal binding with subprocess just simply cannot understand. Those have to be handled with node-nitro if you think of providing surplus value here.

    3. It's not dumb or not, it's technical engineering that we think through use cases we MIGHT need to solve, not only the one we CURRENTLY have

Hope you understand. Thank you

Sorry, misunderstood the third point. I already exposed the error code and exit signals upon exit of nitro subprocess in the recent commits. But to handle core dump and crash then I'm not yet sure how node handles those cases, let me check and have the appropriate solution for the mentioned case. Initially, my assumption is that if nitro crashes or core dumped then no callback for error code or signal but a disconnect callback will be called, then we detect that and get dump information from the system...🤔

binaryFolder: string,
// Default to GPU if CUDA is available when calling
runMode: "cpu" | "gpu" = getNvidiaConfig().cuda.exist ? "gpu" : "cpu",
): NitroExecutableOptions => {
const nvidiaSettings = getNvidiaConfig();
let cudaVisibleDevices = "";
let binaryName = "nitro";
/**
* The binary folder is different for each platform.
*/
if (process.platform === "win32") {
/**
* For Windows: win-cpu, win-cuda-11-7, win-cuda-12-0
*/
if (runMode === "cpu") {
binaryFolder = path.join(binaryFolder, "win-cpu");
} else {
if (nvidiaSettings["cuda"].version === "12") {
binaryFolder = path.join(binaryFolder, "win-cuda-12-0");
} else {
binaryFolder = path.join(binaryFolder, "win-cuda-11-7");
}
cudaVisibleDevices = nvidiaSettings["gpu_highest_vram"];
}
binaryName = "nitro.exe";
} else if (process.platform === "darwin") {
/**
* For MacOS: mac-arm64 (Silicon), mac-x64 (InteL)
*/
if (process.arch === "arm64") {
binaryFolder = path.join(binaryFolder, "mac-arm64");
} else {
binaryFolder = path.join(binaryFolder, "mac-x64");
}
} else {
if (runMode === "cpu") {
binaryFolder = path.join(binaryFolder, "linux-cpu");
} else {
if (nvidiaSettings["cuda"].version === "12") {
binaryFolder = path.join(binaryFolder, "linux-cuda-12-0");
} else {
binaryFolder = path.join(binaryFolder, "linux-cuda-11-7");
}
cudaVisibleDevices = nvidiaSettings["gpu_highest_vram"];
}
}
return {
executablePath: path.join(binaryFolder, binaryName),
cudaVisibleDevices,
};
};
4 changes: 4 additions & 0 deletions nitro-node/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./types";
export * from "./nitro";
export { getNvidiaConfig, setNvidiaConfig } from "./nvidia";
export { setLogger } from "./logger";
14 changes: 14 additions & 0 deletions nitro-node/src/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import os from "node:os";
import { NitroLogger } from "./types";

// The logger to use, default to stdout
export let log: NitroLogger = (message, ..._) =>
process.stdout.write(message + os.EOL);

/**
* Set logger before running nitro
* @param {NitroLogger} logger The logger to use
*/
export async function setLogger(logger: NitroLogger): Promise<void> {
log = logger;
}
Loading