npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@forst/node-runtime

v0.4.2

Published

Node.js runtime bootstrap and TypeScript indexer for Forst Node interop

Readme

@forst/node-runtime

Node runtime for Forst → TypeScript interop. Compiled Go binaries call your legacy .ts and .js modules over a closed RPC channel. The Forst compiler uses this package at build time to index TypeScript exports.

Status: experimental. Pin this package and verify with the examples before production use.

Full guide → Call JavaScript from Forst

Install

npm install @forst/node-runtime
npx jsr add @forst/node-runtime

Requires Node.js 18+. When your Forst program uses import node, you also need tsx on the path for TypeScript loading.

| Registry | Package | | --- | --- | | npm | @forst/node-runtime | | JSR | @forst/node-runtime |

What you get

The runtime is built on Effect: structured logs via Effect.log* and Effect.fn spans, with ForstNodeRuntimeLayer (stderr pretty logging, tracing, and FORST_NODE_LOG_LEVEL) provided at process boundaries via NodeRuntime.runMain or Effect.runPromise.

RPC and runtime hot paths are wrapped in Effect.fn spans (Rpc.dispatch, Runtime.handleSyncCall, …). Span attributes (rpc_method, module_id, …) appear on log lines when FORST_NODE_LOG_LEVEL is debug or trace.

Custom Effect runtime

Default entrypoints (bootstrap.js, @forst/node-runtime/host) use ForstNodeRuntimeLayer. To bring your own logging, tracing, or services, build a setup and pass it at the process boundary:

import { NodeRuntime } from "@effect/platform-node";
import { Effect, Layer } from "effect";
import {
  bootstrapMain,
  bootstrapFatal,
  createNodeRuntimeSetup,
  makeForstNodeRuntimeLayer,
  startForstNodeHost,
} from "@forst/node-runtime";

// Standalone child: Forst owns stderr logging and tracing.
const myLayer = makeForstNodeRuntimeLayer();
const { layer, runtime } = createNodeRuntimeSetup(myLayer);

// Embedded in an Effect app: keep the parent logger and tracer.
const embeddedLayer = Layer.merge(
  appLayer,
  makeForstNodeRuntimeLayer({ replaceLogger: false })
);
const embedded = createNodeRuntimeSetup(embeddedLayer);

// Bootstrap child (local socket RPC). disablePrettyLogger avoids duplicate stdout logging.
NodeRuntime.runMain(
  bootstrapMain({ runtime }).pipe(
    Effect.catchAllDefect((cause) => bootstrapFatal(cause)),
    Effect.provide(layer)
  ),
  { disablePrettyLogger: true }
);

// Host mode: pass runtimeLayer so RPC forks use the same setup
await Effect.runPromise(
  startForstNodeHost({ runtimeLayer: embedded.layer }).pipe(
    Effect.provide(embedded.layer)
  )
);

Use the same layer for Effect.provide and the matching runtime for async RPC dispatch sites (bootstrapMain, host connection forks).

OpenTelemetry (optional)

Install peer packages when you want RPC spans exported to OTLP or the console:

npm install @effect/opentelemetry @opentelemetry/api @opentelemetry/sdk-trace-base @opentelemetry/sdk-trace-node @opentelemetry/exporter-trace-otlp-http

Bootstrap auto-export: the bootstrap child calls resolveForstNodeRuntimeLayer() at startup. When OTEL_EXPORTER_OTLP_ENDPOINT or FORST_NODE_OTEL=1 is set and the peers above are installed, Effect spans (Rpc.dispatch, Runtime.handleSyncCall, …) export through OpenTelemetry automatically.

OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318/v1/traces \
OTEL_SERVICE_NAME=my-app \
FORST_NODE_LOG_LEVEL=debug \
npx forst run -root ./my-service ./main.ft

If OTEL env is set but peers are missing, bootstrap logs a warning and continues with stderr-only spans.

Host / embedded apps: merge OpenTelemetry at your app boundary so you do not duplicate exporters:

import { Layer } from "effect";
import { createNodeRuntimeSetup } from "@forst/node-runtime";
import {
  mergeForstNodeRuntimeWithOpenTelemetry,
  openTelemetryLayerFromEnv,
} from "@forst/node-runtime/opentelemetry";

const layer = mergeForstNodeRuntimeWithOpenTelemetry(
  openTelemetryLayerFromEnv(),
  { replaceLogger: false }
);
const { layer: runtimeLayer, runtime } = createNodeRuntimeSetup(
  Layer.merge(appLayer, layer)
);

Import helpers from @forst/node-runtime/opentelemetry. The main @forst/node-runtime entry also exports resolveForstNodeRuntimeLayer for bootstrap-style resolution without static OTEL imports.

| Piece | Role | | --- | --- | | bootstrap.js | RPC server process Go spawns in bootstrap mode | | @forst/node-runtime/host | In process RPC when your app runs the Node child | | forst-node-index | CLI the compiler invokes to read TypeScript exports | | Schema types | forst-node-manifest-v1 and forst-index-v1 validation |

Project setup

Enable node interop in ftconfig.json:

{
  "files": {
    "include": ["**/*.ft", "**/*.ts"]
  },
  "node": {
    "enabled": true,
    "runtimeEnabled": true
  }
}

Use opt in imports in Forst source:

import node "./legacy/payment"

func main() {
    result := payment.create(100.0, "USD")
}

Build and run with the Forst compiler (@forst/cli):

npx forst build -root . ./main.ft
npx forst run -root . ./main.ft

Runtime modes

Bootstrap (default): Go starts a dedicated Node child that runs dist/bootstrap.js. Isolated process. RPC over a local socket (default .forst/node-bootstrap.sock); child stdout and stderr are forwarded as logs.

Host: Go starts your app (node.binary + node.args). RPC listens on a local socket inside that process so module cache and globals stay shared. Import from @forst/node-runtime/host and call signalForstAppReady() when your app is ready.

See runtime modes in the docs.

CLI

forst-node-index --root . --format forst-index-v1 --files legacy/payment.ts

The compiler calls this during type checking. You rarely run it yourself.

Environment variables

Bootstrap and logging

| Variable | Purpose | | --- | --- | | FORST_NODE_LOG_LEVEL | Log verbosity: trace, debug, info, warn, or error (default info). Per-RPC flow uses Effect spans; set debug or trace to see span annotations (effect.spanName, rpc_method, …) on log lines. | | FORST_NODE_OTEL | When "1", enables OpenTelemetry export in bootstrap (console spans when no OTLP endpoint is set). Requires optional OTEL peer packages. | | OTEL_EXPORTER_OTLP_ENDPOINT | Standard OTLP traces URL (e.g. http://localhost:4318/v1/traces). When set, bootstrap exports RPC spans to this endpoint. | | OTEL_SERVICE_NAME | OpenTelemetry service name (default forst-node-runtime). | | OTEL_EXPORTER_OTLP_HEADERS | Optional OTLP headers (key=value,key2=value2). | | FORST_NODE_LOG_FORMAT | Log format: pretty (default) or json for structured stderr lines. | | FORST_NODE_BOOTSTRAP | Absolute path to bootstrap.js (bootstrap mode spawn planning) | | FORST_NODE_SOCKET | Absolute Unix socket path (TCP URL on Windows) for Go↔Node RPC. Bootstrap default: {boundaryRoot}/.forst/node-bootstrap.sock. Host default: {boundaryRoot}/.forst/node.sock. | | FORST_NODE_HOST_READY | Absolute path to JSON readiness file ({socket}.ready); Go waits for phase: "app" before dialing. |

Host mode (set by Go on the direct app-shim child)

| Variable | Purpose | | --- | --- | | FORST_NODE_HOST | When "1", enables in-process host RPC (startForstNodeHost). Unset in bootstrap mode. | | FORST_NODE_HOST_LEADER | When "1", marks the Go-spawned leader process. Required together with register.mjs in process.execArgv; workers skip binding. | | FORST_NODE_APP_READY_MODULE | Optional path to a module loaded before app readiness when node.hostAppReadyModule is configured. |

See Call JavaScript from Forst — host mode environment variables for spawn layout, readiness phases, and troubleshooting.

Development

From the monorepo:

cd packages/node-runtime
bun run build
bun test

Publishing

Release Please tags node-runtime-v* bump package.json and jsr.json. CI publishes to npm and JSR via .github/workflows/publish-packages.yml.

Manual publish from packages/node-runtime:

bun run build
npm publish --access public
npx jsr publish

Dry run:

bun run pack:dry
npx jsr publish --dry-run

License

MIT. See LICENSE.