@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
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createUndiciNetworkCaptureHooks = createUndiciNetworkCaptureHooks;
4
+ const api_1 = require("@opentelemetry/api");
5
+ const node_diagnostics_channel_1 = require("node:diagnostics_channel");
6
+ const utils_js_1 = require("../utils.js");
7
+ const collector_js_1 = require("./collector.js");
8
+ const undiciSessions = new WeakMap();
9
+ let undiciBodyChannelsBound = false;
10
+ function appendHeader(headers, rawName, value) {
11
+ const name = String(rawName).toLowerCase();
12
+ const existing = headers[name];
13
+ if (existing === undefined)
14
+ headers[name] = value;
15
+ else if (Array.isArray(existing))
16
+ existing.push(value);
17
+ else
18
+ headers[name] = [existing, value];
19
+ }
20
+ function undiciRequestHeaders(value) {
21
+ const headers = {};
22
+ if (typeof value === "string") {
23
+ for (const line of value.split("\r\n")) {
24
+ const separator = line.indexOf(":");
25
+ if (separator <= 0)
26
+ continue;
27
+ appendHeader(headers, line.slice(0, separator).trim(), line.slice(separator + 1).trim());
28
+ }
29
+ }
30
+ else if (Array.isArray(value)) {
31
+ for (let index = 0; index + 1 < value.length; index += 2) {
32
+ appendHeader(headers, value[index], value[index + 1]);
33
+ }
34
+ }
35
+ return headers;
36
+ }
37
+ function undiciResponseHeaders(value) {
38
+ if (!Array.isArray(value))
39
+ return {};
40
+ const headers = {};
41
+ for (let index = 0; index + 1 < value.length; index += 2) {
42
+ const name = value[index];
43
+ const headerValue = value[index + 1];
44
+ appendHeader(headers, Buffer.isBuffer(name) ? name.toString("latin1") : name, Buffer.isBuffer(headerValue)
45
+ ? headerValue.toString("latin1")
46
+ : headerValue);
47
+ }
48
+ return headers;
49
+ }
50
+ function undiciRequest(message) {
51
+ if (!message || typeof message !== "object" || !("request" in message)) {
52
+ return undefined;
53
+ }
54
+ const request = message.request;
55
+ return request && typeof request === "object"
56
+ ? request
57
+ : undefined;
58
+ }
59
+ function undiciCollector(session, direction) {
60
+ const field = direction === "request" ? "requestBody" : "responseBody";
61
+ const existing = session[field];
62
+ if (existing)
63
+ return existing;
64
+ const headers = direction === "request" ? session.requestHeaders : session.responseHeaders;
65
+ if (!headers)
66
+ return undefined;
67
+ const created = collector_js_1.BodyCollector.create(session.span, "client", direction, () => headers);
68
+ if (created)
69
+ session[field] = created;
70
+ return created;
71
+ }
72
+ function finishUndici(request, complete) {
73
+ const session = undiciSessions.get(request);
74
+ if (!session)
75
+ return;
76
+ undiciSessions.delete(request);
77
+ session.requestBody?.finalize(complete);
78
+ session.responseBody?.finalize(complete);
79
+ }
80
+ function bindUndiciChannel(name, handle) {
81
+ (0, node_diagnostics_channel_1.subscribe)(name, (message) => {
82
+ (0, utils_js_1.safely)(() => {
83
+ const request = undiciRequest(message);
84
+ if (!request)
85
+ return;
86
+ const session = undiciSessions.get(request);
87
+ if (!session)
88
+ return;
89
+ handle(message, request, session);
90
+ });
91
+ });
92
+ }
93
+ function bindUndiciBodyChannels() {
94
+ if (undiciBodyChannelsBound)
95
+ return;
96
+ undiciBodyChannelsBound = true;
97
+ // Subscribe before UndiciInstrumentation.enable() so trailers run while
98
+ // the span is still recording. BodyCollector is created on first chunk.
99
+ bindUndiciChannel("undici:request:bodyChunkSent", (message, _request, session) => {
100
+ undiciCollector(session, "request")?.observe(message.chunk);
101
+ });
102
+ bindUndiciChannel("undici:request:bodySent", (_message, _request, session) => {
103
+ session.requestBody?.finalize(true);
104
+ });
105
+ bindUndiciChannel("undici:request:bodyChunkReceived", (message, _request, session) => {
106
+ undiciCollector(session, "response")?.observe(message.chunk);
107
+ });
108
+ bindUndiciChannel("undici:request:trailers", (_message, request) => {
109
+ finishUndici(request, true);
110
+ });
111
+ bindUndiciChannel("undici:request:error", (_message, request) => {
112
+ finishUndici(request, false);
113
+ });
114
+ }
115
+ /**
116
+ * Builds OTel Undici/fetch hooks. Headers use the same allowlist as Node HTTP.
117
+ * Bodies are copied from diagnostics_channel chunks — never from fetch streams.
118
+ */
119
+ function createUndiciNetworkCaptureHooks(bodyCaptureEnabled = true) {
120
+ if (bodyCaptureEnabled)
121
+ bindUndiciBodyChannels();
122
+ return {
123
+ requestHook(span, request) {
124
+ const headers = undiciRequestHeaders(request.headers);
125
+ (0, collector_js_1.setHeaderAttributes)(span, "request", headers);
126
+ if (!bodyCaptureEnabled ||
127
+ !span.isRecording() ||
128
+ (span.spanContext().traceFlags & api_1.TraceFlags.SAMPLED) !==
129
+ api_1.TraceFlags.SAMPLED) {
130
+ return;
131
+ }
132
+ undiciSessions.set(request, { span, requestHeaders: headers });
133
+ },
134
+ responseHook(span, info) {
135
+ const headers = undiciResponseHeaders(info.response.headers);
136
+ (0, collector_js_1.setHeaderAttributes)(span, "response", headers);
137
+ if (!bodyCaptureEnabled || info.request === undefined)
138
+ return;
139
+ const session = undiciSessions.get(info.request);
140
+ if (session)
141
+ session.responseHeaders = headers;
142
+ },
143
+ };
144
+ }
@@ -0,0 +1,5 @@
1
+ import { type Context } from "@opentelemetry/api";
2
+ export declare function injectTraceContext(carrier: Record<string, string>): void;
3
+ export declare function extractTraceContext(carrier: Record<string, string>): Context;
4
+ export declare const setBaggage: (key: string, value: string) => void;
5
+ export declare function getBaggage(key: string): string | undefined;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setBaggage = void 0;
4
+ exports.injectTraceContext = injectTraceContext;
5
+ exports.extractTraceContext = extractTraceContext;
6
+ exports.getBaggage = getBaggage;
7
+ const node_async_hooks_1 = require("node:async_hooks");
8
+ const api_1 = require("@opentelemetry/api");
9
+ const core_1 = require("@opentelemetry/core");
10
+ const utils_js_1 = require("./utils.js");
11
+ const state_js_1 = require("./state.js");
12
+ // pcga11: Below are some definitios you may find helpful to understand this file.
13
+ // CompositePropagator is a local propagator used on custom transports (Kafka, SQS, Redis, WebSocket)
14
+ // An "injector/extractor" turns the context into headers and vice versa for distributed tracing
15
+ // Carrier is the headers object can be kafka message, queue payload or any other transport headers
16
+ // Context.active() returns the process' context. Think of it like a local RAM for this request in this Node process. Another service, process or even another Kafka in the same service will have a different context.active().
17
+ const propagator = new core_1.CompositePropagator({
18
+ propagators: [new core_1.W3CTraceContextPropagator(), new core_1.W3CBaggagePropagator()],
19
+ });
20
+ const baggageStorage = new node_async_hooks_1.AsyncLocalStorage();
21
+ function contextWithBaggage() {
22
+ const entries = Object.fromEntries(api_1.propagation.getBaggage(api_1.context.active())?.getAllEntries() ?? []);
23
+ for (const [key, value] of baggageStorage.getStore() ?? []) {
24
+ entries[key] = { value };
25
+ }
26
+ const baggage = api_1.propagation.createBaggage(entries);
27
+ return api_1.propagation.setBaggage(api_1.context.active(), baggage);
28
+ }
29
+ function injectTraceContext(carrier) {
30
+ (0, utils_js_1.safely)(() => {
31
+ propagator.inject(contextWithBaggage(), carrier, api_1.defaultTextMapSetter);
32
+ });
33
+ }
34
+ function extractTraceContext(carrier) {
35
+ return (0, utils_js_1.safely)(() => propagator.extract(api_1.context.active(), carrier, api_1.defaultTextMapGetter), api_1.context.active());
36
+ }
37
+ exports.setBaggage = (0, utils_js_1.whenInitialized)(state_js_1.Signals.baggage, (key, value) => {
38
+ const next = new Map(baggageStorage.getStore() ?? []);
39
+ next.set(key, value);
40
+ baggageStorage.enterWith(next);
41
+ });
42
+ function getBaggage(key) {
43
+ return (0, utils_js_1.safely)(() => baggageStorage.getStore()?.get(key) ??
44
+ api_1.propagation.getBaggage(api_1.context.active())?.getEntry(key)?.value, undefined);
45
+ }
@@ -0,0 +1,3 @@
1
+ import type { Attributes } from "@opentelemetry/api";
2
+ import { type Resource } from "@opentelemetry/resources";
3
+ export declare function createFoamResource(name: string, environment: string, version?: string, additionalResourceAttributes?: Attributes): Resource;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createFoamResource = createFoamResource;
4
+ const resources_1 = require("@opentelemetry/resources");
5
+ const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
6
+ const constants_js_1 = require("./constants.js");
7
+ function createFoamResource(name, environment, version, additionalResourceAttributes) {
8
+ return (0, resources_1.defaultResource)()
9
+ .merge((0, resources_1.detectResources)({
10
+ detectors: [resources_1.envDetector, resources_1.processDetector, resources_1.hostDetector, resources_1.osDetector],
11
+ }))
12
+ // pcga11: Later merges overwrite colliding keys, so Foam name/env/version will win over detectors.
13
+ // This is the desired behavior. Do not change.
14
+ .merge((0, resources_1.resourceFromAttributes)({
15
+ ...additionalResourceAttributes,
16
+ [semantic_conventions_1.ATTR_SERVICE_NAME]: name,
17
+ [semantic_conventions_1.ATTR_DEPLOYMENT_ENVIRONMENT_NAME]: environment,
18
+ ...(version
19
+ ? { [semantic_conventions_1.ATTR_SERVICE_VERSION]: version }
20
+ : {}),
21
+ [semantic_conventions_1.ATTR_TELEMETRY_DISTRO_NAME]: constants_js_1.FOAM_DISTRO_NAME,
22
+ [semantic_conventions_1.ATTR_TELEMETRY_DISTRO_VERSION]: constants_js_1.FOAM_DISTRO_VERSION,
23
+ }));
24
+ }
@@ -0,0 +1,19 @@
1
+ export declare enum Signals {
2
+ traces = "traces",
3
+ metrics = "metrics",
4
+ logs = "logs",
5
+ baggage = "baggage",
6
+ profile = "profile"
7
+ }
8
+ /** Read-only view of the SDK state; mutated only via the setters in state.ts. */
9
+ export interface State {
10
+ readonly initialized: boolean;
11
+ readonly instrumentations: readonly string[];
12
+ readonly signals: Readonly<Record<Signals, boolean>>;
13
+ }
14
+ export declare const setInstrumentations: (names: readonly string[]) => void;
15
+ export declare const setSignal: (signal: Signals, enabled: boolean) => void;
16
+ export declare const getSignal: (signal: Signals) => boolean;
17
+ export declare const setInitialized: (initialized: boolean) => void;
18
+ export declare const getInitialized: () => boolean;
19
+ export declare const getState: () => State;
package/dist/state.js ADDED
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getState = exports.getInitialized = exports.setInitialized = exports.getSignal = exports.setSignal = exports.setInstrumentations = exports.Signals = void 0;
4
+ var Signals;
5
+ (function (Signals) {
6
+ Signals["traces"] = "traces";
7
+ Signals["metrics"] = "metrics";
8
+ Signals["logs"] = "logs";
9
+ Signals["baggage"] = "baggage";
10
+ Signals["profile"] = "profile";
11
+ })(Signals || (exports.Signals = Signals = {}));
12
+ const state = {
13
+ initialized: false,
14
+ instrumentations: [],
15
+ signals: {
16
+ traces: false,
17
+ metrics: false,
18
+ logs: false,
19
+ baggage: false,
20
+ profile: false,
21
+ },
22
+ };
23
+ const setInstrumentations = (names) => {
24
+ state.instrumentations = [...names];
25
+ };
26
+ exports.setInstrumentations = setInstrumentations;
27
+ const setSignal = (signal, enabled) => {
28
+ state.signals[signal] = enabled;
29
+ };
30
+ exports.setSignal = setSignal;
31
+ const getSignal = (signal) => state.signals[signal];
32
+ exports.getSignal = getSignal;
33
+ const setInitialized = (initialized) => {
34
+ state.initialized = initialized;
35
+ };
36
+ exports.setInitialized = setInitialized;
37
+ const getInitialized = () => state.initialized;
38
+ exports.getInitialized = getInitialized;
39
+ const getState = () => ({
40
+ initialized: state.initialized,
41
+ instrumentations: [...state.instrumentations],
42
+ signals: { ...state.signals },
43
+ });
44
+ exports.getState = getState;
@@ -0,0 +1,4 @@
1
+ import { Signals } from "./state.js";
2
+ export declare function safely<T>(operation: () => T, fallback: T): T;
3
+ export declare function safely(operation: () => void): void;
4
+ export declare function whenInitialized<A extends unknown[]>(signal: Signals, operation: (...args: A) => void): (...args: A) => void;
package/dist/utils.js ADDED
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.safely = safely;
4
+ exports.whenInitialized = whenInitialized;
5
+ const state_js_1 = require("./state.js");
6
+ function safely(operation, fallback) {
7
+ try {
8
+ return operation();
9
+ }
10
+ catch {
11
+ return fallback;
12
+ }
13
+ }
14
+ function whenInitialized(signal, operation) {
15
+ return (...args) => {
16
+ if (!(0, state_js_1.getSignal)(signal))
17
+ return;
18
+ safely(() => operation(...args));
19
+ };
20
+ }
package/package.json CHANGED
@@ -1,35 +1,63 @@
1
1
  {
2
2
  "name": "@foam-ai/node",
3
- "version": "0.1.0-alpha.2",
4
- "main": "dist/node/src/index.js",
5
- "types": "dist/node/src/index.d.ts",
3
+ "version": "0.1.0-alpha.4",
4
+ "description": "Foam JavaScript Node.js SDK",
5
+ "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/foam-ai/js-node.git"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public",
12
+ "tag": "alpha"
13
+ },
14
+ "engines": {
15
+ "node": "^18.19.0 || >=20.6.0"
16
+ },
17
+ "main": "./dist/index.js",
18
+ "module": "./dist/index.js",
19
+ "types": "./dist/index.d.ts",
6
20
  "exports": {
7
21
  ".": {
8
- "types": "./dist/node/src/index.d.ts",
9
- "import": "./dist/node/src/index.js",
10
- "require": "./dist/node/src/index.js"
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js",
24
+ "require": "./dist/index.js",
25
+ "default": "./dist/index.js"
11
26
  }
12
27
  },
13
28
  "files": [
14
29
  "dist"
15
30
  ],
16
31
  "scripts": {
17
- "build": "tsc -p tsconfig.json"
32
+ "build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json",
33
+ "typecheck": "tsc --noEmit",
34
+ "test": "vitest run",
35
+ "prepublishOnly": "npm run build"
18
36
  },
19
37
  "dependencies": {
20
- "@opentelemetry/api": "^1.9.0",
21
- "@opentelemetry/api-logs": "^0.214.0",
22
- "@opentelemetry/exporter-logs-otlp-http": "^0.214.0",
23
- "@opentelemetry/exporter-metrics-otlp-http": "^0.214.0",
24
- "@opentelemetry/exporter-trace-otlp-http": "^0.214.0",
25
- "@opentelemetry/instrumentation": "^0.214.0",
26
- "@opentelemetry/resources": "^2.6.1",
27
- "@opentelemetry/sdk-logs": "^0.214.0",
28
- "@opentelemetry/sdk-metrics": "^2.6.1",
29
- "@opentelemetry/sdk-trace-base": "^2.6.1",
30
- "@opentelemetry/sdk-trace-node": "^2.6.1"
38
+ "@opentelemetry/api": "^1.9.1",
39
+ "@opentelemetry/api-logs": "^0.221.0",
40
+ "@opentelemetry/auto-instrumentations-node": "^0.79.0",
41
+ "@opentelemetry/context-async-hooks": "^2.10.0",
42
+ "@opentelemetry/core": "^2.10.0",
43
+ "@opentelemetry/exporter-logs-otlp-http": "^0.221.0",
44
+ "@opentelemetry/exporter-metrics-otlp-http": "^0.221.0",
45
+ "@opentelemetry/exporter-trace-otlp-http": "^0.221.0",
46
+ "@opentelemetry/instrumentation": "^0.221.0",
47
+ "@opentelemetry/instrumentation-console": "^0.3.0",
48
+ "@opentelemetry/instrumentation-http": "^0.221.0",
49
+ "@opentelemetry/instrumentation-undici": "^0.31.0",
50
+ "@opentelemetry/otlp-transformer": "^0.221.0",
51
+ "@opentelemetry/resources": "^2.10.0",
52
+ "@opentelemetry/sdk-logs": "^0.221.0",
53
+ "@opentelemetry/sdk-metrics": "^2.10.0",
54
+ "@opentelemetry/sdk-trace-base": "^2.10.0",
55
+ "@opentelemetry/semantic-conventions": "^1.43.0",
56
+ "@opentelemetry/winston-transport": "^0.31.0"
31
57
  },
32
58
  "devDependencies": {
33
- "typescript": "^6.0.2"
59
+ "@types/node": "^26.2.0",
60
+ "typescript": "^7.0.2",
61
+ "vitest": "^4.1.11"
34
62
  }
35
63
  }
@@ -1,9 +0,0 @@
1
- /**
2
- * Captures an exception and sends it to Foam.
3
- *
4
- * - Records on the active span if one exists (for trace-level visibility)
5
- * - Always emits an OTEL log record so the error reaches Foam even when
6
- * there is no active span or auto-instrumentation
7
- * - Safe to call at any time — silently no-ops if the SDK isn't initialized
8
- */
9
- export declare const captureException: (error: unknown) => void;
@@ -1,31 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.captureException = void 0;
4
- const api_1 = require("@opentelemetry/api");
5
- const api_logs_1 = require("@opentelemetry/api-logs");
6
- const util_1 = require("../../shared/util");
7
- /**
8
- * Captures an exception and sends it to Foam.
9
- *
10
- * - Records on the active span if one exists (for trace-level visibility)
11
- * - Always emits an OTEL log record so the error reaches Foam even when
12
- * there is no active span or auto-instrumentation
13
- * - Safe to call at any time — silently no-ops if the SDK isn't initialized
14
- */
15
- exports.captureException = (0, util_1.safe)((error) => {
16
- const err = error instanceof Error ? error : new Error(String(error));
17
- const span = api_1.trace.getActiveSpan();
18
- if (span) {
19
- span.recordException(err);
20
- span.setStatus({ code: api_1.SpanStatusCode.ERROR });
21
- }
22
- api_logs_1.logs.getLogger('foam').emit({
23
- severityNumber: api_logs_1.SeverityNumber.ERROR,
24
- severityText: 'ERROR',
25
- body: err.message,
26
- attributes: {
27
- 'exception.type': err.name,
28
- 'exception.stacktrace': err.stack ?? '',
29
- },
30
- });
31
- });
@@ -1,9 +0,0 @@
1
- import { captureException } from './capture-exception';
2
- import { init } from './init';
3
- export { captureException, init };
4
- export type { FoamNodeInitOptions } from './init';
5
- declare const foam: {
6
- captureException: (error: unknown) => void;
7
- init: (options: import("./init").FoamNodeInitOptions) => void;
8
- };
9
- export default foam;
@@ -1,12 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.init = exports.captureException = void 0;
4
- const capture_exception_1 = require("./capture-exception");
5
- Object.defineProperty(exports, "captureException", { enumerable: true, get: function () { return capture_exception_1.captureException; } });
6
- const init_1 = require("./init");
7
- Object.defineProperty(exports, "init", { enumerable: true, get: function () { return init_1.init; } });
8
- const foam = {
9
- captureException: capture_exception_1.captureException,
10
- init: init_1.init,
11
- };
12
- exports.default = foam;
@@ -1,27 +0,0 @@
1
- import type { InstrumentationBase } from '@opentelemetry/instrumentation';
2
- export interface FoamNodeInitOptions {
3
- apiKey: string;
4
- serviceName: string;
5
- isProduction: boolean;
6
- /**
7
- * OpenTelemetry instrumentations to register (e.g. getNodeAutoInstrumentations()).
8
- * None are registered by default to keep the SDK webpack/Turbopack-safe.
9
- * For plain Node.js servers, pass getNodeAutoInstrumentations() to get
10
- * automatic HTTP, Express, DNS, etc. instrumentation.
11
- */
12
- instrumentations?: InstrumentationBase[];
13
- }
14
- /**
15
- * Initializes the Foam SDK for Node.js.
16
- *
17
- * - Production-only: silently no-ops when `isProduction` is false.
18
- * - Zero latency: all setup is synchronous object construction; actual telemetry
19
- * export happens asynchronously in background batch intervals.
20
- * - Crash-safe: every signal is wrapped in its own try/catch so a failure in
21
- * one (e.g. traces) never prevents the others (logs, metrics) from starting.
22
- * - Provider-aware: detects each OTEL signal independently. If another SDK
23
- * (Sentry, @vercel/otel, etc.) already registered a provider, Foam attaches
24
- * its exporters on top. If no provider exists, Foam creates one. This
25
- * guarantees all three signals always reach Foam regardless of environment.
26
- */
27
- export declare const init: (options: FoamNodeInitOptions) => void;