Server-side JavaScript and TypeScript for Node 18+, ESM and CommonJS.
For a backend, not a browser. Every call puts an event on a bounded queue and returns: nothing throws into your code, nothing is awaited on the hot path, and if firstrun is unreachable your program keeps working. No runtime dependencies.
npm install @firstrun/nodeimport { Firstrun } from "@firstrun/node";
const firstrun = new Firstrun({
sourceKey: process.env.FIRSTRUN_SOURCE_KEY!, // fr_server_...
host: "https://app.firstrun.app",
serviceVersion: process.env.GIT_SHA,
onDiagnostic: (d) => log.warn({ firstrun: d }),
});A bad key or host disables the client and reports it rather than throwing, because a typo in an environment variable must not stop your service booting. onDiagnostic is the only reporting channel: nothing is ever written to stdout or stderr.
firstrun.event("exported_csv", { rows: rows.length }, { distinctId: req.user.id });
firstrun.error(err, { "http.route": "/reports/:id" }, { distinctId: req.user.id });
firstrun.log({
name: "queue_depth",
severity: 9,
distinctId: "worker-3",
attributes: { "firstrun.metric": "queue_depth", "firstrun.value": depth },
});
await firstrun.close(2000); // on exit. Bounded, never rejectsNot awaited, and there is nothing here to await. event writes at INFO and error at ERROR, both filling in the conventional attributes; log takes any name, any severity and any attributes. Values are JSON, so a number stays a number. A long-running service needs no close(): beforeExit, SIGTERM and SIGINT already flush with a budget. A CLI or a one-shot job should call it.
distinctId only when the process really is the subject, such as a CLI or a device agent.