Node-style APIs for Tsonic.
This package is part of Tsonic: https://tsonic.org.
Use @tsonic/nodejs when you want Node-like modules (fs, path, events, crypto, process, http, …) while still compiling to a native binary with tsonic.
@tsonic/nodejs supports the default C# target. The package declares this in
tsonic.package.json and tsonic.surface.json with
"supportedTargets": ["csharp"]. User code imports stay target-neutral:
applications use @tsonic/nodejs and Node-style module specifiers such as
node:fs.
- Install the .NET 10 SDK (required by Tsonic): https://dotnet.microsoft.com/download
- Verify:
dotnet --version
mkdir my-app && cd my-app
npx --yes tsonic@latest init --surface @tsonic/js
npx --yes tsonic@latest add npm @tsonic/nodejsimport { join } from "node:path";
import { readFileSync } from "node:fs";
export function main(): void {
const fullPath = join("src", "App.ts");
console.log(fullPath);
console.log(readFileSync(fullPath, "utf-8"));
}npm run devnpx --yes tsonic@latest add npm @tsonic/nodejs@tsonic/nodejs is a regular package, not a surface. Use @tsonic/js for the ambient JavaScript world, and add @tsonic/nodejs when you want node:* module imports. Workspaces that use Node-style packages set surface to @tsonic/js.
This repo is versioned by runtime major:
10→versions/10/→ npm:@tsonic/nodejs@10.x
Before publishing, run npm run selftest.
Publish with:
npm run publish:10assert,buffer,child_process,console,cryptodgram,dns,events,fs,http,net,ospath,perf_hooks,process,querystring,readlinestream,string_decoder,timers,tls,url,util,zlib
The package implements practical Node-style behavior for native Tsonic programs. It is a curated first-party source package, not an embedded Node.js runtime.
import { readFileSync, writeFileSync } from "node:fs";
const content = readFileSync("./package.json", "utf-8");
writeFileSync("./output.txt", "Hello from Tsonic!");import { join, extname, dirname } from "node:path";
const fullPath = join("config", "settings.json");
const ext = extname(fullPath); // ".json"
const dir = dirname(fullPath);import { EventEmitter } from "@tsonic/nodejs/index.js";
class MyEmitter extends EventEmitter {}
const emitter = new MyEmitter();
emitter.on("data", (chunk) => console.log(chunk));import { createHash } from "node:crypto";
const hash = createHash("sha256").update("hello").digest("hex");
void hash;import * as process from "node:process";
const cwd = process.cwd();
void cwd;import { createServer } from "node:http";
const server = createServer((_req, res) => {
res.writeHead(200, "OK");
res.end("Hello from Tsonic!");
});
void server;For JS-surface projects with @tsonic/nodejs installed, prefer Node-style imports:
node:fs,node:path,node:crypto,node:process, ...- bare aliases (
fs,path,crypto, ...) are also supported
Direct ESM imports from @tsonic/nodejs/index.js are still supported.
@tsonic/jsprovides JavaScript runtime APIs (JS-styleconsole,JSON, timers, etc.)@tsonic/nodejsprovides Node-style modules (fs,path,crypto,http, etc.)
- docs/getting-started.md
- docs/imports.md
- Module docs: fs, path, crypto, http, events, process
- https://tsonic.org/nodejs/
@tsonic/nodejsintentionally uses Node/JS-style naming (camelCase members).
Node APIs often accept or emit broad event, stream, DNS, URL, and utility
values. The package uses a source-owned RuntimeValue union for those broad
slots instead of exposing an unbounded any surface. Application code should
prefer concrete module types and narrow broad values before member access.
See __build/ for regeneration scripts.
Run the publish-gated validation suite with:
npm run selftestWhen sibling @tsonic/* repos are checked out locally, the selftest installs those local packages first, whether they are versioned package repos (for example ../js/versions/10) or root-package repos (for example ../aspnetcore). That keeps consumer validation coherent across a local release wave instead of mixing local packages with incompatible published transitive dependencies. The selftest also fails on peer-dependency warnings, so inconsistent package waves are caught before publish.
MIT