@foam-ai/node 0.1.0-alpha.1 → 0.1.0-alpha.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 ADDED
@@ -0,0 +1,123 @@
1
+ # @foam-ai/node
2
+
3
+ Foam observability SDK for Node.js. Sends traces, logs, and metrics to Foam with zero configuration overhead.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @foam-ai/node
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ Call `init()` as early as possible in your application entry point:
14
+
15
+ ```typescript
16
+ import foam from '@foam-ai/node';
17
+
18
+ foam.init({
19
+ apiKey: process.env.FOAM_API_KEY,
20
+ serviceName: 'my-service',
21
+ isProduction: process.env.NODE_ENV === 'production',
22
+ });
23
+ ```
24
+
25
+ That's it. In production, Foam will automatically collect:
26
+
27
+ - **Traces** — distributed traces via OpenTelemetry
28
+ - **Logs** — console.log/info/error/warn/debug output
29
+ - **Metrics** — runtime and custom metrics
30
+ - **Uncaught errors** — process-level `uncaughtException` and `unhandledRejection`
31
+
32
+ In non-production environments, `init()` is a silent no-op.
33
+
34
+ ## Capturing errors
35
+
36
+ Unhandled exceptions and rejections are captured automatically. For caught errors you want to report, use `captureException`:
37
+
38
+ ```typescript
39
+ import { captureException } from '@foam-ai/node';
40
+
41
+ try {
42
+ await chargeCustomer(order);
43
+ } catch (err) {
44
+ captureException(err);
45
+ return res.status(500).json({ error: 'Payment failed' });
46
+ }
47
+ ```
48
+
49
+ `captureException` works with or without an active trace span. It always emits an OpenTelemetry log record so errors reach Foam regardless of instrumentation setup.
50
+
51
+ ## Auto-instrumentation (optional)
52
+
53
+ By default, no auto-instrumentations are registered. This keeps the SDK webpack/Turbopack-safe with no bundler configuration needed.
54
+
55
+ For plain Node.js servers (Express, Fastify, etc.), you can opt into automatic HTTP, DNS, and framework instrumentation:
56
+
57
+ ```typescript
58
+ import foam from '@foam-ai/node';
59
+ import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
60
+
61
+ foam.init({
62
+ apiKey: process.env.FOAM_API_KEY,
63
+ serviceName: 'my-api',
64
+ isProduction: process.env.NODE_ENV === 'production',
65
+ instrumentations: getNodeAutoInstrumentations(),
66
+ });
67
+ ```
68
+
69
+ ```bash
70
+ npm install @opentelemetry/auto-instrumentations-node
71
+ ```
72
+
73
+ ## Next.js
74
+
75
+ In a Next.js app, create an `instrumentation.ts` file at the project root:
76
+
77
+ ```typescript
78
+ export async function register() {
79
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
80
+ const foam = await import('@foam-ai/node');
81
+ foam.init({
82
+ apiKey: process.env.FOAM_API_KEY!,
83
+ serviceName: 'my-nextjs-app',
84
+ isProduction: process.env.NODE_ENV === 'production',
85
+ });
86
+ }
87
+ }
88
+ ```
89
+
90
+ No webpack externals or bundler config needed.
91
+
92
+ ## Coexistence with other SDKs
93
+
94
+ Foam detects each OpenTelemetry signal independently. If another SDK (Sentry, @vercel/otel, Datadog, etc.) has already registered a TracerProvider, LoggerProvider, or MeterProvider, Foam attaches its exporters on top rather than replacing them. Both SDKs receive data.
95
+
96
+ If no providers exist, Foam creates its own. This means Foam works in any combination:
97
+
98
+ - Foam alone
99
+ - Foam + Sentry
100
+ - Foam + @vercel/otel
101
+ - Foam + any OpenTelemetry-compatible SDK
102
+
103
+ ## API
104
+
105
+ ### `init(options)`
106
+
107
+ | Option | Type | Required | Description |
108
+ |---|---|---|---|
109
+ | `apiKey` | `string` | Yes | Your Foam API key |
110
+ | `serviceName` | `string` | Yes | Name of your service (appears in Foam dashboard) |
111
+ | `isProduction` | `boolean` | Yes | Set to `true` to enable telemetry. `false` = no-op. |
112
+ | `instrumentations` | `InstrumentationBase[]` | No | OpenTelemetry instrumentations to register |
113
+
114
+ ### `captureException(error)`
115
+
116
+ Captures a caught error and sends it to Foam. Works with or without an active span.
117
+
118
+ ## Design
119
+
120
+ - **HTTP-only exporters** — no gRPC, no Node.js built-in dependencies that break bundlers
121
+ - **Zero latency** — all setup is synchronous; exporting happens in background batch intervals
122
+ - **Crash-safe** — each signal (traces, logs, metrics, console, process handlers) is independently wrapped in try/catch
123
+ - **Idempotent** — safe to call `init()` multiple times; console patching and process handlers use Symbols to prevent double-registration
@@ -190,8 +190,18 @@ function patchConsole() {
190
190
  const original = console[method].bind(console);
191
191
  console[method] = (...args) => {
192
192
  original(...args);
193
- // logger.emit is non-blocking — it queues into the BatchLogRecordProcessor
194
- logger.emit({ severityNumber, severityText, body: (0, util_1.format)(...args) });
193
+ const ctx = api_1.context.active();
194
+ const spanContext = api_1.trace.getSpanContext(ctx);
195
+ const traceAttributes = spanContext && (0, api_1.isSpanContextValid)(spanContext)
196
+ ? { 'trace.id': spanContext.traceId, 'span.id': spanContext.spanId }
197
+ : undefined;
198
+ logger.emit({
199
+ context: ctx,
200
+ severityNumber,
201
+ severityText,
202
+ body: (0, util_1.format)(...args),
203
+ attributes: traceAttributes,
204
+ });
195
205
  };
196
206
  };
197
207
  patch('log', api_logs_1.SeverityNumber.INFO, 'INFO');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foam-ai/node",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.2",
4
4
  "main": "dist/node/src/index.js",
5
5
  "types": "dist/node/src/index.d.ts",
6
6
  "exports": {