@foam-ai/node 0.1.0-alpha.2 → 0.1.0-alpha.4

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.
Files changed (44) hide show
  1. package/README.md +424 -77
  2. package/dist/constants.d.ts +42 -0
  3. package/dist/constants.js +54 -0
  4. package/dist/diagnostics.d.ts +13 -0
  5. package/dist/diagnostics.js +83 -0
  6. package/dist/exporters.d.ts +25 -0
  7. package/dist/exporters.js +105 -0
  8. package/dist/index.d.ts +5 -0
  9. package/dist/index.js +21 -0
  10. package/dist/ingest.d.ts +6 -0
  11. package/dist/ingest.js +58 -0
  12. package/dist/init.d.ts +22 -0
  13. package/dist/init.js +121 -0
  14. package/dist/instrumentations.d.ts +3 -0
  15. package/dist/instrumentations.js +79 -0
  16. package/dist/metrics.d.ts +5 -0
  17. package/dist/metrics.js +29 -0
  18. package/dist/network-capture/collector.d.ts +27 -0
  19. package/dist/network-capture/collector.js +312 -0
  20. package/dist/network-capture/http.d.ts +6 -0
  21. package/dist/network-capture/http.js +223 -0
  22. package/dist/network-capture/index.d.ts +2 -0
  23. package/dist/network-capture/index.js +7 -0
  24. package/dist/network-capture/undici.d.ts +9 -0
  25. package/dist/network-capture/undici.js +144 -0
  26. package/dist/propagation.d.ts +5 -0
  27. package/dist/propagation.js +45 -0
  28. package/dist/resource.d.ts +3 -0
  29. package/dist/resource.js +24 -0
  30. package/dist/state.d.ts +19 -0
  31. package/dist/state.js +44 -0
  32. package/dist/utils.d.ts +4 -0
  33. package/dist/utils.js +20 -0
  34. package/package.json +47 -19
  35. package/dist/node/src/capture-exception.d.ts +0 -9
  36. package/dist/node/src/capture-exception.js +0 -31
  37. package/dist/node/src/index.d.ts +0 -9
  38. package/dist/node/src/index.js +0 -12
  39. package/dist/node/src/init.d.ts +0 -27
  40. package/dist/node/src/init.js +0 -213
  41. package/dist/shared/constants.d.ts +0 -1
  42. package/dist/shared/constants.js +0 -4
  43. package/dist/shared/util.d.ts +0 -9
  44. package/dist/shared/util.js +0 -21
package/README.md CHANGED
@@ -1,123 +1,470 @@
1
- # @foam-ai/node
1
+ # Foam OpenTelemetry SDK for Node.js
2
2
 
3
- Foam observability SDK for Node.js. Sends traces, logs, and metrics to Foam with zero configuration overhead.
3
+ `@foam-ai/node` is Foam's OpenTelemetry distribution for Node.js. It configures
4
+ the official OpenTelemetry JavaScript SDK, node automatic instrumentations, and
5
+ OTLP/HTTP exporters with one `init()` call.
6
+
7
+ ## Requirements
8
+
9
+ - Node.js `^18.19.0 || >=20.6.0`, matching the supported engine range of
10
+ `@opentelemetry/auto-instrumentations-node`
11
+ - A Foam ingest token, stored as `FOAM_OTEL_TOKEN`
4
12
 
5
13
  ## Install
6
14
 
7
- ```bash
15
+ ```sh
8
16
  npm install @foam-ai/node
9
17
  ```
10
18
 
11
- ## Quick start
19
+ ## Initialize
20
+
21
+ Call `init()` before any application code loads so OpenTelemetry can patch
22
+ libraries as they import. Don't import the app from the same file: ESM runs
23
+ static imports before `init()`. Set `enabled: false` to disable Foam. Store
24
+ the ingest token as `FOAM_OTEL_TOKEN` and pass `process.env.FOAM_OTEL_TOKEN`;
25
+ do not hardcode it.
26
+
27
+ ### TypeScript
12
28
 
13
- Call `init()` as early as possible in your application entry point:
29
+ Create `instrumentation.ts`:
14
30
 
15
- ```typescript
16
- import foam from '@foam-ai/node';
31
+ ```ts
32
+ import { init } from "@foam-ai/node";
17
33
 
18
- foam.init({
19
- apiKey: process.env.FOAM_API_KEY,
20
- serviceName: 'my-service',
21
- isProduction: process.env.NODE_ENV === 'production',
34
+ init({
35
+ name: "checkout-api",
36
+ environment: "production",
37
+ enabled: true,
38
+ token: process.env.FOAM_OTEL_TOKEN,
39
+ version: "1.0.0",
22
40
  });
23
41
  ```
24
42
 
25
- That's it. In production, Foam will automatically collect:
43
+ Compile it with the app. If TypeScript emits ESM:
26
44
 
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`
45
+ ```sh
46
+ node --import ./dist/instrumentation.js ./dist/app.js
47
+ ```
48
+
49
+ If TypeScript emits CommonJS:
31
50
 
32
- In non-production environments, `init()` is a silent no-op.
51
+ ```sh
52
+ node --require ./dist/instrumentation.js ./dist/app.js
53
+ ```
33
54
 
34
- ## Capturing errors
55
+ To run TypeScript without compiling first:
35
56
 
36
- Unhandled exceptions and rejections are captured automatically. For caught errors you want to report, use `captureException`:
57
+ ```sh
58
+ node --import tsx --import ./instrumentation.ts ./src/app.ts
59
+ ```
37
60
 
38
- ```typescript
39
- import { captureException } from '@foam-ai/node';
61
+ ### ESM
40
62
 
41
- try {
42
- await chargeCustomer(order);
43
- } catch (err) {
44
- captureException(err);
45
- return res.status(500).json({ error: 'Payment failed' });
46
- }
63
+ Create `instrumentation.mjs` with the same `init()` call, then:
64
+
65
+ ```sh
66
+ node --import ./instrumentation.mjs ./app.js
47
67
  ```
48
68
 
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.
69
+ ### CommonJS
50
70
 
51
- ## Auto-instrumentation (optional)
71
+ Create `instrumentation.cjs`:
52
72
 
53
- By default, no auto-instrumentations are registered. This keeps the SDK webpack/Turbopack-safe with no bundler configuration needed.
73
+ ```js
74
+ const { init } = require("@foam-ai/node");
54
75
 
55
- For plain Node.js servers (Express, Fastify, etc.), you can opt into automatic HTTP, DNS, and framework instrumentation:
76
+ init({
77
+ name: "checkout-api",
78
+ environment: "production",
79
+ enabled: true,
80
+ token: process.env.FOAM_OTEL_TOKEN,
81
+ version: "1.0.0",
82
+ });
83
+ ```
56
84
 
57
- ```typescript
58
- import foam from '@foam-ai/node';
59
- import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
85
+ ```sh
86
+ node --require ./instrumentation.cjs ./app.js
87
+ ```
88
+
89
+ ## Public API
90
+
91
+ ### `init(options)`
60
92
 
61
- foam.init({
62
- apiKey: process.env.FOAM_API_KEY,
63
- serviceName: 'my-api',
64
- isProduction: process.env.NODE_ENV === 'production',
65
- instrumentations: getNodeAutoInstrumentations(),
93
+ Starts Foam: resource, sampler, OTLP exporters, automatic instrumentations,
94
+ and the W3C propagator.
95
+
96
+ Call this once when Foam can own all of one of OTEL slots.
97
+
98
+ ```js
99
+ import { init } from "@foam-ai/node";
100
+
101
+ init({
102
+ name: "checkout-api",
103
+ environment: "production",
104
+ enabled: true,
105
+ token: process.env.FOAM_OTEL_TOKEN,
106
+ version: "1.0.0",
66
107
  });
67
108
  ```
68
109
 
69
- ```bash
70
- npm install @opentelemetry/auto-instrumentations-node
110
+ ```ts
111
+ init({
112
+ name,
113
+ environment,
114
+ enabled,
115
+ token,
116
+ version?,
117
+ sampleRate?,
118
+ additionalInstrumentations?,
119
+ additionalSpanProcessors?,
120
+ additionalLogRecordProcessors?,
121
+ additionalMetricReaders?,
122
+ additionalResourceAttributes?,
123
+ disableLogSending?,
124
+ networkCapture?,
125
+ ignoredOutboundHosts?,
126
+ diagnostics?,
127
+ }): void
71
128
  ```
72
129
 
73
- ## Next.js
130
+ `name`, `environment`, and `enabled` are required.
131
+ `token` is required only when enabled; store it as `FOAM_OTEL_TOKEN`.
132
+ `sampleRate` accepts values from `0` through `1` and defaults to
133
+ `1`.
134
+
135
+ Foam always installs this sampler rather than reading `OTEL_TRACES_SAMPLER`.
136
+ Diagnostics default to enabled.
137
+
138
+ `additionalResourceAttributes` adds custom resource attributes (for example
139
+ `team` or `cloud.region`) to all telemetry. The Basic API's `name`,
140
+ `environment`, and `version` options take precedence over matching keys.
141
+ Foam also sets `telemetry.sdk.*` from the OpenTelemetry SDK and
142
+ `telemetry.distro.name` / `telemetry.distro.version` for this distribution.
143
+
144
+ Foam detects process, host, and OS attributes from this machine. It does not
145
+ run cloud or container resource detectors (AWS, GCP, Azure, Alibaba, or cgroup
146
+ `container.id`), which call metadata APIs at startup. Set cloud or container
147
+ identity with `additionalResourceAttributes`.
148
+
149
+ `additionalInstrumentations` registers extra OpenTelemetry instrumentations
150
+ alongside Foam's Node auto bundle and `ConsoleInstrumentation`. Foam already
151
+ enables [`@opentelemetry/instrumentation-openai`](https://www.npmjs.com/package/@opentelemetry/instrumentation-openai)
152
+ from that bundle (`openai` `>=4.19 <7`); message content is captured only when
153
+ `networkCapture` is `"advanced"`. Use this option for libraries Foam does not
154
+ ship, such as community Anthropic instrumentations
155
+ ([`@traceloop/instrumentation-anthropic`](https://www.npmjs.com/package/@traceloop/instrumentation-anthropic)
156
+ or
157
+ [`@arizeai/openinference-instrumentation-anthropic`](https://www.npmjs.com/package/@arizeai/openinference-instrumentation-anthropic)),
158
+ or to replace a bundled instrumentation by name: last entry wins, and Foam
159
+ disables its own copy. Replacing HTTP or Undici this way also replaces Foam's
160
+ network capture hooks.
161
+
162
+ ### `createFoamIngestSpanProcessor(name, environment, token)`
163
+
164
+ Returns a span processor that exports traces to Foam without taking over the
165
+ global TracerProvider.
166
+
167
+ Use this when another OpenTelemetry SDK already owns traces and you only need
168
+ Foam as an extra export destination.
169
+
170
+ ```js
171
+ import { createFoamIngestSpanProcessor } from "@foam-ai/node";
172
+
173
+ const spanProcessor = createFoamIngestSpanProcessor(
174
+ "checkout-api",
175
+ "production",
176
+ process.env.FOAM_OTEL_TOKEN,
177
+ );
178
+ tracerProvider.addSpanProcessor(spanProcessor);
179
+ ```
180
+
181
+ ### `createFoamIngestLogRecordProcessor(name, environment, token)`
182
+
183
+ Returns a log-record processor that exports logs to Foam without taking over
184
+ the global LoggerProvider.
185
+
186
+ Use this when another SDK already owns logs and you want those records in Foam.
74
187
 
75
- In a Next.js app, create an `instrumentation.ts` file at the project root:
188
+ ```js
189
+ import { createFoamIngestLogRecordProcessor } from "@foam-ai/node";
76
190
 
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
- }
191
+ const logRecordProcessor = createFoamIngestLogRecordProcessor(
192
+ "checkout-api",
193
+ "production",
194
+ process.env.FOAM_OTEL_TOKEN,
195
+ );
196
+ loggerProvider.addLogRecordProcessor(logRecordProcessor);
88
197
  ```
89
198
 
90
- No webpack externals or bundler config needed.
199
+ ### `createFoamIngestMetricReader(name, environment, token)`
91
200
 
92
- ## Coexistence with other SDKs
201
+ Returns a metric reader that exports metrics to Foam without taking over the
202
+ global MeterProvider.
93
203
 
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.
204
+ Use this when another SDK already owns metrics and you want those meters in
205
+ Foam.
95
206
 
96
- If no providers exist, Foam creates its own. This means Foam works in any combination:
207
+ ```js
208
+ import { createFoamIngestMetricReader } from "@foam-ai/node";
97
209
 
98
- - Foam alone
99
- - Foam + Sentry
100
- - Foam + @vercel/otel
101
- - Foam + any OpenTelemetry-compatible SDK
210
+ const metricReader = createFoamIngestMetricReader(
211
+ "checkout-api",
212
+ "production",
213
+ process.env.FOAM_OTEL_TOKEN,
214
+ );
215
+ meterProvider.addMetricReader(metricReader);
216
+ ```
217
+
218
+ The factories do not claim global OTel providers. `name`, `environment`, and
219
+ `token` are required; Foam stamps `service.name`,
220
+ `deployment.environment.name`, and `foam.ingest.tier=external` onto the
221
+ exported resource. If the other SDK already set `service.name` or environment,
222
+ Foam's values overwrite them on the copy sent to Foam only. The original
223
+ spans, logs, and metrics are not mutated.
224
+
225
+ Use `init()` or ingest, not both on the same signal. If another SDK already
226
+ owns traces (or another slot), call `init()` for the free signals and attach
227
+ ingest only to the provider you do not own. Do not pass ingest factories as
228
+ `additionalSpanProcessors` / readers: `init()` already installs Foam exporters.
229
+
230
+ The global propagator is also first-wins. OpenTelemetry allows only one, and
231
+ HTTP, Express, Undici, and Fetch all call it to copy trace headers onto
232
+ requests. Foam registers W3C Trace Context and W3C Baggage
233
+ (`traceparent`, `tracestate`, `baggage`) and nothing else: not B3, Jaeger, or
234
+ AWS X-Ray. If Foam wins the slot, auto-instrumented HTTP is W3C-only. If
235
+ another SDK already registered a propagator, Foam keeps it,
236
+ `getState().signals.baggage` is `false`, and HTTP speaks that winner's format.
237
+ A B3-only (or Jaeger/X-Ray-only) winner will not send or honor `traceparent`,
238
+ so a Foam peer that only understands W3C starts a new trace. Foam does not
239
+ wrap inject/extract to dual-read those formats or add W3C headers on top.
240
+ Initialize Foam first, or have the other SDK register a composite that
241
+ includes W3C, if those headers must be on HTTP.
242
+
243
+ ### `injectTraceContext(headers)`
244
+
245
+ Writes W3C `traceparent`, `tracestate`, and `baggage` onto a header map.
246
+
247
+ Use this when you send a message yourself (Kafka, a queue, a custom socket).
248
+ Auto-instrumented HTTP, Express, Undici, and Fetch already inject these
249
+ headers; do not call this on ordinary HTTP. This path uses Foam's local W3C
250
+ propagator even if Foam lost the global slot.
251
+
252
+ ```js
253
+ const headers = {};
254
+ injectTraceContext(headers);
255
+ await kafka.send({ value: payload, headers });
256
+ ```
102
257
 
103
- ## API
258
+ ### `extractTraceContext(headers)`
104
259
 
105
- ### `init(options)`
260
+ Reads W3C trace and baggage headers and returns a parent OpenTelemetry context.
261
+
262
+ Use this when you receive a message on a custom transport so the work continues
263
+ the same trace. Ignoring those headers starts a new one.
264
+
265
+ ```js
266
+ import { context } from "@opentelemetry/api";
267
+
268
+ const parent = extractTraceContext(message.headers);
269
+ await context.with(parent, () => handle(message));
270
+ ```
271
+
272
+ ### `setBaggage(key, value)`
273
+
274
+ Stores a small string on the current request context.
275
+
276
+ Use this for request-scoped identity (tenant, user id) that should follow the
277
+ request across `await`s without being passed through every function. Values do
278
+ not leak to other concurrent requests. Auto-instrumented HTTP does not send
279
+ these keys until you call `injectTraceContext`.
280
+
281
+ ```js
282
+ setBaggage("tenant.id", "acme");
283
+ ```
284
+
285
+ ### `getBaggage(key)`
286
+
287
+ Returns a baggage value from the current request, or `undefined`.
106
288
 
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 |
289
+ Use this later in the same request to read a value set with `setBaggage`, or a
290
+ key that arrived on an inbound `baggage` header.
291
+
292
+ ```js
293
+ const tenant = getBaggage("tenant.id"); // "acme"
294
+ ```
295
+
296
+ ### `incrementCounter(name, value?, attributes?, options?)`
297
+
298
+ Adds to a counter that only increases.
299
+
300
+ Use this for event counts: orders created, jobs finished, errors.
301
+
302
+ ```js
303
+ incrementCounter("orders.created", 1, { "cloud.region": "us-west-1" });
304
+ ```
113
305
 
114
- ### `captureException(error)`
306
+ ### `recordHistogram(name, value, attributes?, options?)`
115
307
 
116
- Captures a caught error and sends it to Foam. Works with or without an active span.
308
+ Records a measurement in a distribution.
117
309
 
118
- ## Design
310
+ Use this for values you want percentiles or averages of: latency, duration,
311
+ payload size.
312
+
313
+ ```js
314
+ recordHistogram("checkout.duration", 0.142, undefined, { unit: "s" });
315
+ ```
316
+
317
+ ### `addUpDownCounter(name, value, attributes?, options?)`
318
+
319
+ Adds a signed delta to a counter that can go up or down.
320
+
321
+ Use this for occupancy: active jobs, open connections, items in a pool.
322
+
323
+ ```js
324
+ addUpDownCounter("jobs.active", -1);
325
+ ```
326
+
327
+ ### `setMetric(name, value, attributes?, options?)`
328
+
329
+ Sets the current value of a gauge.
330
+
331
+ Use this for a point-in-time level: queue depth, heap used, cache size.
332
+
333
+ ```js
334
+ setMetric("queue.depth", 27);
335
+ ```
336
+
337
+ Each metric helper caches instruments by kind and name. `unit` and
338
+ `description` therefore apply on first use. Observable (pull) instruments are
339
+ not part of this API; use `@opentelemetry/api` or automatic instrumentation
340
+ when a value should be sampled on export rather than recorded on an event.
341
+
342
+ ### `getState()`
343
+
344
+ Returns whether Foam initialized, which instrumentations registered, and which
345
+ signals have an export path.
346
+
347
+ Use this in health checks, tests, or diagnostics to confirm Foam is running.
348
+
349
+ ```js
350
+ import { getState } from "@foam-ai/node";
351
+
352
+ getState();
353
+ // {
354
+ // initialized: true,
355
+ // instrumentations: ["http", "express", "pg", ...],
356
+ // signals: {
357
+ // traces: true,
358
+ // metrics: true,
359
+ // logs: true,
360
+ // baggage: true,
361
+ // profile: false,
362
+ // },
363
+ // }
364
+ ```
119
365
 
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
366
+ `instrumentations` contains only the instrumentations registered with a
367
+ successfully started Foam SDK, after upstream defaults and
368
+ `OTEL_NODE_ENABLED_INSTRUMENTATIONS` /
369
+ `OTEL_NODE_DISABLED_INSTRUMENTATIONS` filtering. Registration means an
370
+ instrumentation is ready to patch a supported module when that module loads; it
371
+ does not prove that the module has loaded or emitted spans.
372
+
373
+ Each `signals` boolean means Foam successfully constructed an export path for
374
+ that signal. This can be a Foam-owned provider from `init()` or a
375
+ processor/reader returned by an ingest factory. For ingest factories, Foam
376
+ cannot prove that the caller subsequently registered the returned object. The
377
+ boolean therefore means telemetry is expected, not that the application has
378
+ already produced or successfully exported it.
379
+
380
+ ## Network capture
381
+
382
+ `networkCapture` defaults to `"basic"` and controls HTTP enrichment: `"off"`
383
+ keeps standard OpenTelemetry HTTP/Undici telemetry with no extra capture,
384
+ `"basic"` adds allowlisted `content-type`, `content-length`, and
385
+ `content-encoding` headers, and `"advanced"` adds those headers plus complete
386
+ Node `http`/`https` (and Fetch/Undici, when diagnostics exist) request and
387
+ response bodies as raw wire bytes up to 1 MiB. Bodies are emitted as
388
+ correlated `foam.http.body.chunk` log events; the HTTP span records semantic
389
+ `http.*.body.size` and, for textual bodies, `http.*.body.content`, while
390
+ `authorization`, `cookie`, HTTP/2, and gRPC are never captured. Advanced
391
+ capture requires Foam to own the logger provider, closes incomplete bodies
392
+ after 60 seconds of inactivity, and is replaced if you override the bundled
393
+ HTTP instrumentation through `additionalInstrumentations`. Foam partitions
394
+ serialized trace and log export batches below 19 MiB to stay under the
395
+ ClickStack collector's 20 MiB OTLP/HTTP limit.
396
+
397
+ ## Logger support and ownership
398
+
399
+ Foam follows the compatibility ranges of its OpenTelemetry logger
400
+ instrumentations:
401
+
402
+ - Bunyan `>=1 <2`
403
+ - Pino `>=5.14 <11`
404
+ - Winston `>=1 <4`
405
+
406
+ The init log line that lists registered instrumentations also names each
407
+ detected logger with its installed version. Initialize Foam before importing or
408
+ constructing the logger; registration alone does not prove that a logger was
409
+ loaded, patched, or produced records.
410
+
411
+ Logger trace correlation remains enabled by default. With
412
+ `disableLogSending: false` (the default), OpenTelemetry automatically sends:
413
+
414
+ - Bunyan records through its added OTel stream
415
+ - Pino 7 and newer records through an added main-thread multistream
416
+ - Winston 3 records through the bundled `OpenTelemetryTransportV3`
417
+
418
+ Pino 5 and 6 and Winston 1 and 2 support correlation only. Set
419
+ `disableLogSending: true` when the application manually installs a Bunyan OTel
420
+ stream, configures `pino-opentelemetry-transport`, installs a Winston OTel
421
+ transport, or otherwise gives another pipeline ownership of logger records.
422
+ This disables automatic sending for Bunyan, Pino, and Winston while preserving
423
+ trace correlation.
424
+
425
+ OpenTelemetry cannot reliably deduplicate manually configured streams and
426
+ transports; explicit `disableLogSending` ownership is the correctness
427
+ mechanism.
428
+
429
+ `ConsoleInstrumentation` separately turns calls to `console.*` into OTel log
430
+ records. It does not make arbitrary stdout/stderr writes observable.
431
+
432
+ ## OpenTelemetry configuration and compliance
433
+
434
+ Foam composes the official OpenTelemetry JavaScript API, SDK, resource,
435
+ propagation, instrumentation, and OTLP exporter packages. It does not fork
436
+ their data model or redefine spans, metrics, logs, resources, or context.
437
+
438
+ Foam does not use any of environment configuration. So they remain available, including:
439
+
440
+ - `OTEL_RESOURCE_ATTRIBUTES` (the Basic API's required service name and
441
+ environment take precedence for those resource attributes)
442
+ - `OTEL_NODE_ENABLED_INSTRUMENTATIONS` and
443
+ `OTEL_NODE_DISABLED_INSTRUMENTATIONS`
444
+ - `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT`
445
+ - `OTEL_TRACES_SAMPLER`
446
+
447
+ The [OpenTelemetry JavaScript compliance matrix](https://github.com/open-telemetry/opentelemetry-specification/blob/main/spec-compliance-matrix/js.yaml)
448
+ describes `opentelemetry-js`, not this distro. It uses `+` (implemented),
449
+ `-` (not implemented), `?` (unknown), and `N/A`. Foam does not reimplement
450
+ those SDK features and does not inherit every `+` row: `init()` only
451
+ constructs the packages and options this distro actually wires (W3C
452
+ propagation, OTLP/HTTP to Foam, a parent-based ratio sampler). Rows the
453
+ matrix marks `+` for JavaScript but Foam never installs stay unavailable
454
+ here, including OTLP/gRPC, Zipkin, Prometheus, Jaeger remote sampling,
455
+ B3/Jaeger propagators, metric views, and `OTEL_TRACES_SAMPLER`. Foam also
456
+ does not implement rows marked `-` or `?`.
457
+
458
+ ## TODO(pcga11): OpenTelemetry profiling
459
+
460
+ Profiling is not supported: the official OpenTelemetry JavaScript SDK has no
461
+ in-process Profiles provider, processor, or exporter yet.
462
+
463
+ Add profiling and report `getState().signals.profile: true` after that ships.
464
+
465
+ Track upstream progress:
466
+
467
+ - [OpenTelemetry JS profiling implementation issue](https://github.com/open-telemetry/opentelemetry-js/issues/6500)
468
+ - [Profiling SIG language SDK support tracker](https://github.com/open-telemetry/sig-profiling/issues/106)
469
+ - [OpenTelemetry Profiles public alpha announcement](https://opentelemetry.io/blog/2026/profiles-alpha/)
470
+ - [OpenTelemetry eBPF profiler](https://github.com/open-telemetry/opentelemetry-ebpf-profiler)
@@ -0,0 +1,42 @@
1
+ export declare const FOAM_ENDPOINT = "https://otel.api.foam.ai";
2
+ export declare const FOAM_IDENTIFIER_NAME = "foam-otel";
3
+ export declare const FOAM_DISTRO_NAME = "@foam-ai/node";
4
+ export declare const FOAM_DISTRO_VERSION = "0.1.0-alpha.4";
5
+ export declare const FOAM_INGEST_TIER = "foam.ingest.tier";
6
+ export declare const FOAM_OTLP_TRACES_PATH = "/v1/traces";
7
+ export declare const FOAM_OTLP_LOGS_PATH = "/v1/logs";
8
+ export declare const FOAM_OTLP_METRICS_PATH = "/v1/metrics";
9
+ export declare const NETWORK_BODY_CHUNK_BYTES: number;
10
+ export declare const NETWORK_BODY_MAX_CAPTURE_BYTES: number;
11
+ export declare const NETWORK_CAPTURE_EVENT = "foam.http.body.chunk";
12
+ export declare const NETWORK_CAPTURE_EVENT_BODY = "HTTP body chunk";
13
+ export declare const NETWORK_CAPTURE_DEADLINE_MS = 60000;
14
+ export declare const MAX_ACTIVE_CAPTURES = 128;
15
+ export declare const ATTR_HTTP_REQUEST_BODY_SIZE = "http.request.body.size";
16
+ export declare const ATTR_HTTP_RESPONSE_BODY_SIZE = "http.response.body.size";
17
+ export declare const ATTR_HTTP_REQUEST_BODY_CONTENT = "http.request.body.content";
18
+ export declare const ATTR_HTTP_RESPONSE_BODY_CONTENT = "http.response.body.content";
19
+ export declare const ATTR_FOAM_HTTP_BODY_ID = "foam.http.body.id";
20
+ export declare const ATTR_FOAM_HTTP_BODY_SIDE = "foam.http.body.side";
21
+ export declare const ATTR_FOAM_HTTP_BODY_DIRECTION = "foam.http.body.direction";
22
+ export declare const ATTR_FOAM_HTTP_BODY_COMPLETE = "foam.http.body.complete";
23
+ export declare const ATTR_FOAM_HTTP_BODY_TRUNCATED = "foam.http.body.truncated";
24
+ export declare const ATTR_FOAM_HTTP_BODY_OUTCOME = "foam.http.body.outcome";
25
+ export declare const ATTR_FOAM_HTTP_BODY_CAPTURED_BYTES = "foam.http.body.captured_bytes";
26
+ export declare const ATTR_FOAM_HTTP_BODY_CHUNK_COUNT = "foam.http.body.chunk.count";
27
+ export declare const ATTR_FOAM_HTTP_BODY_CHUNK_INDEX = "foam.http.body.chunk.index";
28
+ export declare const ATTR_FOAM_HTTP_BODY_CHUNK_BYTES = "foam.http.body.chunk.bytes";
29
+ export declare const ATTR_FOAM_HTTP_BODY_CHUNK_CONTENT = "foam.http.body.chunk.content";
30
+ export declare const ATTR_FOAM_HTTP_REQUEST_BODY_CAPTURE_ID = "foam.http.request.body.capture_id";
31
+ export declare const ATTR_FOAM_HTTP_RESPONSE_BODY_CAPTURE_ID = "foam.http.response.body.capture_id";
32
+ export declare const ATTR_FOAM_HTTP_REQUEST_BODY_COMPLETE = "foam.http.request.body.complete";
33
+ export declare const ATTR_FOAM_HTTP_RESPONSE_BODY_COMPLETE = "foam.http.response.body.complete";
34
+ export declare const ATTR_FOAM_HTTP_REQUEST_BODY_TRUNCATED = "foam.http.request.body.truncated";
35
+ export declare const ATTR_FOAM_HTTP_RESPONSE_BODY_TRUNCATED = "foam.http.response.body.truncated";
36
+ export declare const SAFE_NETWORK_HEADERS: readonly ["content-type", "content-length", "content-encoding"];
37
+ export declare const DIAG_LOG_LEVEL = "OTEL_LOG_LEVEL";
38
+ export declare const SEVERITY_TEXT: {
39
+ readonly 9: "INFO";
40
+ readonly 13: "WARN";
41
+ readonly 17: "ERROR";
42
+ };
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SEVERITY_TEXT = exports.DIAG_LOG_LEVEL = exports.SAFE_NETWORK_HEADERS = exports.ATTR_FOAM_HTTP_RESPONSE_BODY_TRUNCATED = exports.ATTR_FOAM_HTTP_REQUEST_BODY_TRUNCATED = exports.ATTR_FOAM_HTTP_RESPONSE_BODY_COMPLETE = exports.ATTR_FOAM_HTTP_REQUEST_BODY_COMPLETE = exports.ATTR_FOAM_HTTP_RESPONSE_BODY_CAPTURE_ID = exports.ATTR_FOAM_HTTP_REQUEST_BODY_CAPTURE_ID = exports.ATTR_FOAM_HTTP_BODY_CHUNK_CONTENT = exports.ATTR_FOAM_HTTP_BODY_CHUNK_BYTES = exports.ATTR_FOAM_HTTP_BODY_CHUNK_INDEX = exports.ATTR_FOAM_HTTP_BODY_CHUNK_COUNT = exports.ATTR_FOAM_HTTP_BODY_CAPTURED_BYTES = exports.ATTR_FOAM_HTTP_BODY_OUTCOME = exports.ATTR_FOAM_HTTP_BODY_TRUNCATED = exports.ATTR_FOAM_HTTP_BODY_COMPLETE = exports.ATTR_FOAM_HTTP_BODY_DIRECTION = exports.ATTR_FOAM_HTTP_BODY_SIDE = exports.ATTR_FOAM_HTTP_BODY_ID = exports.ATTR_HTTP_RESPONSE_BODY_CONTENT = exports.ATTR_HTTP_REQUEST_BODY_CONTENT = exports.ATTR_HTTP_RESPONSE_BODY_SIZE = exports.ATTR_HTTP_REQUEST_BODY_SIZE = exports.MAX_ACTIVE_CAPTURES = exports.NETWORK_CAPTURE_DEADLINE_MS = exports.NETWORK_CAPTURE_EVENT_BODY = exports.NETWORK_CAPTURE_EVENT = exports.NETWORK_BODY_MAX_CAPTURE_BYTES = exports.NETWORK_BODY_CHUNK_BYTES = exports.FOAM_OTLP_METRICS_PATH = exports.FOAM_OTLP_LOGS_PATH = exports.FOAM_OTLP_TRACES_PATH = exports.FOAM_INGEST_TIER = exports.FOAM_DISTRO_VERSION = exports.FOAM_DISTRO_NAME = exports.FOAM_IDENTIFIER_NAME = exports.FOAM_ENDPOINT = void 0;
4
+ const api_logs_1 = require("@opentelemetry/api-logs");
5
+ exports.FOAM_ENDPOINT = "https://otel.api.foam.ai";
6
+ exports.FOAM_IDENTIFIER_NAME = "foam-otel";
7
+ exports.FOAM_DISTRO_NAME = "@foam-ai/node";
8
+ exports.FOAM_DISTRO_VERSION = "0.1.0-alpha.4";
9
+ exports.FOAM_INGEST_TIER = "foam.ingest.tier";
10
+ exports.FOAM_OTLP_TRACES_PATH = "/v1/traces";
11
+ exports.FOAM_OTLP_LOGS_PATH = "/v1/logs";
12
+ exports.FOAM_OTLP_METRICS_PATH = "/v1/metrics";
13
+ exports.NETWORK_BODY_CHUNK_BYTES = 64 * 1024; // 64Kib
14
+ exports.NETWORK_BODY_MAX_CAPTURE_BYTES = 1024 * 1024; // 1Mib
15
+ exports.NETWORK_CAPTURE_EVENT = "foam.http.body.chunk";
16
+ exports.NETWORK_CAPTURE_EVENT_BODY = "HTTP body chunk";
17
+ exports.NETWORK_CAPTURE_DEADLINE_MS = 60_000;
18
+ exports.MAX_ACTIVE_CAPTURES = 128;
19
+ // pcga11: Do not change these.
20
+ // Spec names copied from OpenTelemetry HTTP semantic conventions. 1.43.0 does
21
+ // not export BODY_CONTENT, and OTel JS recommends copying unstable names
22
+ // instead of importing `@opentelemetry/semantic-conventions/incubating`.
23
+ exports.ATTR_HTTP_REQUEST_BODY_SIZE = "http.request.body.size";
24
+ exports.ATTR_HTTP_RESPONSE_BODY_SIZE = "http.response.body.size";
25
+ exports.ATTR_HTTP_REQUEST_BODY_CONTENT = "http.request.body.content";
26
+ exports.ATTR_HTTP_RESPONSE_BODY_CONTENT = "http.response.body.content";
27
+ exports.ATTR_FOAM_HTTP_BODY_ID = "foam.http.body.id";
28
+ exports.ATTR_FOAM_HTTP_BODY_SIDE = "foam.http.body.side";
29
+ exports.ATTR_FOAM_HTTP_BODY_DIRECTION = "foam.http.body.direction";
30
+ exports.ATTR_FOAM_HTTP_BODY_COMPLETE = "foam.http.body.complete";
31
+ exports.ATTR_FOAM_HTTP_BODY_TRUNCATED = "foam.http.body.truncated";
32
+ exports.ATTR_FOAM_HTTP_BODY_OUTCOME = "foam.http.body.outcome";
33
+ exports.ATTR_FOAM_HTTP_BODY_CAPTURED_BYTES = "foam.http.body.captured_bytes";
34
+ exports.ATTR_FOAM_HTTP_BODY_CHUNK_COUNT = "foam.http.body.chunk.count";
35
+ exports.ATTR_FOAM_HTTP_BODY_CHUNK_INDEX = "foam.http.body.chunk.index";
36
+ exports.ATTR_FOAM_HTTP_BODY_CHUNK_BYTES = "foam.http.body.chunk.bytes";
37
+ exports.ATTR_FOAM_HTTP_BODY_CHUNK_CONTENT = "foam.http.body.chunk.content";
38
+ exports.ATTR_FOAM_HTTP_REQUEST_BODY_CAPTURE_ID = "foam.http.request.body.capture_id";
39
+ exports.ATTR_FOAM_HTTP_RESPONSE_BODY_CAPTURE_ID = "foam.http.response.body.capture_id";
40
+ exports.ATTR_FOAM_HTTP_REQUEST_BODY_COMPLETE = "foam.http.request.body.complete";
41
+ exports.ATTR_FOAM_HTTP_RESPONSE_BODY_COMPLETE = "foam.http.response.body.complete";
42
+ exports.ATTR_FOAM_HTTP_REQUEST_BODY_TRUNCATED = "foam.http.request.body.truncated";
43
+ exports.ATTR_FOAM_HTTP_RESPONSE_BODY_TRUNCATED = "foam.http.response.body.truncated";
44
+ exports.SAFE_NETWORK_HEADERS = [
45
+ "content-type",
46
+ "content-length",
47
+ "content-encoding",
48
+ ];
49
+ exports.DIAG_LOG_LEVEL = "OTEL_LOG_LEVEL";
50
+ exports.SEVERITY_TEXT = {
51
+ [api_logs_1.SeverityNumber.INFO]: "INFO",
52
+ [api_logs_1.SeverityNumber.WARN]: "WARN",
53
+ [api_logs_1.SeverityNumber.ERROR]: "ERROR",
54
+ };
@@ -0,0 +1,13 @@
1
+ import { SeverityNumber } from "@opentelemetry/api-logs";
2
+ export declare function configureDiagnostics(): void;
3
+ export declare const info: (message: string) => void;
4
+ export declare const warn: (message: string) => void;
5
+ export declare const error: (message: string) => void;
6
+ export declare function sendStateDiagnosticLog({ name, environment, token, tier, severity, error, }: {
7
+ name: string;
8
+ environment: string;
9
+ token: string;
10
+ tier: "internal" | "external";
11
+ severity: SeverityNumber;
12
+ error?: string;
13
+ }): Promise<void>;