@heystack/otel 0.0.1 → 0.1.0

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 CHANGED
@@ -1,15 +1,59 @@
1
1
  # @heystack/otel
2
2
 
3
- One-call OpenTelemetry tracing that exports to Heystack.
3
+ Runtime-aware OpenTelemetry tracing that exports to Heystack. One import per runtime, so the `.` entry stays safe to load anywhere (including Edge/Workers) without dragging in the Node SDK.
4
4
 
5
5
  ```bash
6
6
  npm install @heystack/otel
7
7
  ```
8
8
 
9
+ Always read your key from the environment — never paste it into source:
10
+
11
+ ```bash
12
+ # .env
13
+ HEYSTACK_API_KEY=sk_live_…
14
+ ```
15
+
16
+ ## Runtime matrix
17
+
18
+ | Runtime | Import | Notes |
19
+ | --- | --- | --- |
20
+ | Node / Express / Fastify / workers (long-running) | `@heystack/otel/node` | Auto-instrumentations + graceful shutdown. |
21
+ | Next.js (`instrumentation.ts`) | `@heystack/otel/next` | Runtime-guarded; no-op on Edge. |
22
+ | Cloudflare Workers / Edge | `@heystack/otel/workers` | Coming soon — the Node SDK can't run on Workers/Edge. |
23
+ | Anywhere (pure helpers) | `@heystack/otel` | `buildExporterConfig`, types. No Node SDK loaded. |
24
+
25
+ ## Node / Express / etc.
26
+
9
27
  At the very top of your app's entry file:
28
+
29
+ ```ts
30
+ import { initHeystack } from "@heystack/otel/node";
31
+
32
+ initHeystack({ apiKey: process.env.HEYSTACK_API_KEY, service: "my-app" });
33
+ ```
34
+
35
+ This enables auto-instrumentations (HTTP, Express, etc.) so you get spans without manual wiring.
36
+
37
+ ## Next.js
38
+
39
+ In `instrumentation.ts` at the project root:
40
+
10
41
  ```ts
11
- import { initHeystack } from "@heystack/otel";
12
- initHeystack({ apiKey: process.env.HEYSTACK_KEY!, service: "my-app" });
42
+ export async function register() {
43
+ const { registerHeystack } = await import("@heystack/otel/next");
44
+ await registerHeystack({ service: "my-app" });
45
+ }
13
46
  ```
14
47
 
48
+ `apiKey` defaults to `process.env.HEYSTACK_API_KEY`. `registerHeystack` only initialises on the Node.js runtime — on the Edge runtime it's a no-op, so it's safe to call unconditionally.
49
+
50
+ ## Workers / Edge
51
+
52
+ Coming in `@heystack/otel/workers` — the Node SDK can't run on Workers/Edge, so a dedicated, fetch-based entry handles those runtimes.
53
+
54
+ ## Verify it's working
55
+
56
+ - Pass `debug: true` to `initHeystack` to print OTel diagnostic/export logs to the console.
57
+ - Short-lived processes: `initHeystack` registers `SIGTERM`/`SIGINT` handlers automatically, so the SDK flushes and shuts down before exit and you don't lose the last batch.
58
+
15
59
  Then run your app and make a request — traces appear in your Heystack dashboard within seconds.
package/dist/core.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ export declare const DEFAULT_ENDPOINT = "https://ingest.heystack.dev";
2
+ export interface HeystackOptions {
3
+ /** Your Heystack ingest key (sk_live_...). Prefer process.env.HEYSTACK_API_KEY. */
4
+ apiKey: string;
5
+ /** OTel service.name this app reports as. */
6
+ service: string;
7
+ /** Override the ingest endpoint (defaults to Heystack production). */
8
+ endpoint?: string;
9
+ }
10
+ export interface ExporterConfig {
11
+ url: string;
12
+ headers: {
13
+ Authorization: string;
14
+ };
15
+ }
16
+ /** Pure: derive the OTLP/HTTP traces URL + auth header. Works in any runtime. */
17
+ export declare function buildExporterConfig(o: HeystackOptions): ExporterConfig;
package/dist/core.js ADDED
@@ -0,0 +1,6 @@
1
+ export const DEFAULT_ENDPOINT = "https://ingest.heystack.dev";
2
+ /** Pure: derive the OTLP/HTTP traces URL + auth header. Works in any runtime. */
3
+ export function buildExporterConfig(o) {
4
+ const base = (o.endpoint ?? DEFAULT_ENDPOINT).replace(/\/+$/, "");
5
+ return { url: `${base}/v1/traces`, headers: { Authorization: `Bearer ${o.apiKey}` } };
6
+ }
package/dist/next.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import type { HeystackOptions } from "./core.js";
2
+ /**
3
+ * Call from Next.js instrumentation.ts:
4
+ * export function register() { registerHeystack({ service: "my-app" }); }
5
+ * apiKey defaults to process.env.HEYSTACK_API_KEY. Only initialises on the Node.js
6
+ * runtime — on Edge it's a no-op (the Node SDK can't run there).
7
+ */
8
+ export declare function registerHeystack(o: Partial<HeystackOptions> & {
9
+ service: string;
10
+ }): Promise<void>;
package/dist/next.js ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Call from Next.js instrumentation.ts:
3
+ * export function register() { registerHeystack({ service: "my-app" }); }
4
+ * apiKey defaults to process.env.HEYSTACK_API_KEY. Only initialises on the Node.js
5
+ * runtime — on Edge it's a no-op (the Node SDK can't run there).
6
+ */
7
+ export async function registerHeystack(o) {
8
+ // Next sets NEXT_RUNTIME to "nodejs" | "edge"
9
+ if (process.env.NEXT_RUNTIME !== "nodejs")
10
+ return;
11
+ const apiKey = o.apiKey ?? process.env.HEYSTACK_API_KEY;
12
+ if (!apiKey) {
13
+ console.warn("[heystack] HEYSTACK_API_KEY not set — tracing disabled");
14
+ return;
15
+ }
16
+ const { initHeystack } = await import("./node.js");
17
+ initHeystack({
18
+ apiKey,
19
+ service: o.service,
20
+ endpoint: o.endpoint,
21
+ debug: o.debug,
22
+ });
23
+ }
package/dist/node.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ import { NodeSDK } from "@opentelemetry/sdk-node";
2
+ import { type HeystackOptions } from "./core.js";
3
+ export interface NodeOptions extends HeystackOptions {
4
+ /** Enable OTel diagnostic logging to console to confirm export. */
5
+ debug?: boolean;
6
+ /** Set false to skip auto-instrumentations (you'll then only get framework/manual spans). Default true. */
7
+ autoInstrument?: boolean;
8
+ }
9
+ /** Initialise Heystack tracing on a Node runtime. Call once, as early as possible. Returns the started SDK. */
10
+ export declare function initHeystack(o: NodeOptions): NodeSDK;
11
+ /** Flush + shutdown the SDK on SIGTERM/SIGINT so short-lived processes don't lose the last batch. */
12
+ export declare function shutdownOnSignals(sdk: NodeSDK): void;
package/dist/node.js ADDED
@@ -0,0 +1,30 @@
1
+ import { NodeSDK } from "@opentelemetry/sdk-node";
2
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
3
+ import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
4
+ import { diag, DiagConsoleLogger, DiagLogLevel } from "@opentelemetry/api";
5
+ import { buildExporterConfig } from "./core.js";
6
+ /** Initialise Heystack tracing on a Node runtime. Call once, as early as possible. Returns the started SDK. */
7
+ export function initHeystack(o) {
8
+ if (o.debug)
9
+ diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);
10
+ const cfg = buildExporterConfig(o);
11
+ const sdk = new NodeSDK({
12
+ serviceName: o.service,
13
+ traceExporter: new OTLPTraceExporter({ url: cfg.url, headers: cfg.headers }),
14
+ instrumentations: o.autoInstrument === false ? [] : [getNodeAutoInstrumentations()],
15
+ });
16
+ sdk.start();
17
+ shutdownOnSignals(sdk);
18
+ return sdk;
19
+ }
20
+ /** Flush + shutdown the SDK on SIGTERM/SIGINT so short-lived processes don't lose the last batch. */
21
+ export function shutdownOnSignals(sdk) {
22
+ const stop = () => {
23
+ sdk
24
+ .shutdown()
25
+ .catch(() => { })
26
+ .finally(() => process.exit(0));
27
+ };
28
+ process.once("SIGTERM", stop);
29
+ process.once("SIGINT", stop);
30
+ }
@@ -0,0 +1,106 @@
1
+ import { type Span } from "@opentelemetry/api";
2
+ import { BasicTracerProvider, type ReadableSpan, type SpanExporter } from "@opentelemetry/sdk-trace-base";
3
+ import { type HeystackOptions } from "./core.js";
4
+ declare const ExportResultCode: {
5
+ readonly SUCCESS: 0;
6
+ readonly FAILED: 1;
7
+ };
8
+ interface ExportResult {
9
+ code: (typeof ExportResultCode)[keyof typeof ExportResultCode];
10
+ error?: Error;
11
+ }
12
+ interface OtlpAnyValue {
13
+ stringValue?: string;
14
+ intValue?: string;
15
+ doubleValue?: number;
16
+ boolValue?: boolean;
17
+ arrayValue?: {
18
+ values: OtlpAnyValue[];
19
+ };
20
+ }
21
+ interface OtlpKeyValue {
22
+ key: string;
23
+ value: OtlpAnyValue;
24
+ }
25
+ interface OtlpSpan {
26
+ traceId: string;
27
+ spanId: string;
28
+ parentSpanId: string;
29
+ name: string;
30
+ kind: number;
31
+ startTimeUnixNano: string;
32
+ endTimeUnixNano: string;
33
+ attributes: OtlpKeyValue[];
34
+ status: {
35
+ code: number;
36
+ message?: string;
37
+ };
38
+ }
39
+ interface OtlpTracesPayload {
40
+ resourceSpans: {
41
+ resource: {
42
+ attributes: OtlpKeyValue[];
43
+ };
44
+ scopeSpans: {
45
+ spans: OtlpSpan[];
46
+ }[];
47
+ }[];
48
+ }
49
+ /**
50
+ * Serialize a batch of ended spans into a single OTLP/JSON traces payload.
51
+ *
52
+ * All spans in a SimpleSpanProcessor batch share the same per-request provider,
53
+ * hence the same resource, so we emit a single resourceSpans entry.
54
+ */
55
+ export declare function serializeSpans(spans: ReadableSpan[]): OtlpTracesPayload;
56
+ /**
57
+ * A WinterCG-compatible OTLP/JSON span exporter. POSTs ended spans to the
58
+ * Heystack ingest using the platform `fetch` — no Node built-ins.
59
+ */
60
+ export declare class HeystackSpanExporter implements SpanExporter {
61
+ private readonly url;
62
+ private readonly headers;
63
+ private shutdownState;
64
+ constructor(options: HeystackOptions);
65
+ export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void;
66
+ shutdown(): Promise<void>;
67
+ forceFlush(): Promise<void>;
68
+ }
69
+ export interface WorkersConfig {
70
+ service: string;
71
+ /** Defaults to env.HEYSTACK_API_KEY at request time if omitted. */
72
+ apiKey?: string;
73
+ endpoint?: string;
74
+ }
75
+ /**
76
+ * Build a `BasicTracerProvider` wired to a `HeystackSpanExporter` via a
77
+ * `SimpleSpanProcessor`. Exposed for advanced users who want to add manual
78
+ * spans; for typical use prefer {@link instrument}.
79
+ *
80
+ * On Workers each request should get its own short-lived provider so its spans
81
+ * can be flushed via `ctx.waitUntil(provider.forceFlush())`.
82
+ */
83
+ export declare function createTracerProvider(config: HeystackOptions): BasicTracerProvider;
84
+ /** Reset the once-only no-key warning. Internal/testing helper. */
85
+ export declare function __resetWarnings(): void;
86
+ interface FetchHandler<E> {
87
+ fetch: (req: Request, env: E, ctx: ExecutionContext) => Promise<Response> | Response;
88
+ }
89
+ /**
90
+ * Wrap a Worker's default export so every request is auto-traced with a SERVER
91
+ * span. The export is flushed via `ctx.waitUntil` so it completes after the
92
+ * response is returned.
93
+ *
94
+ * import { instrument } from "@heystack/otel/workers";
95
+ * export default instrument(
96
+ * { async fetch(req, env, ctx) { return new Response("ok"); } },
97
+ * { service: "my-worker" },
98
+ * );
99
+ *
100
+ * If no API key is available (neither `config.apiKey` nor
101
+ * `env.HEYSTACK_API_KEY`), the handler runs untraced.
102
+ */
103
+ export declare function instrument<E = unknown>(handler: FetchHandler<E>, config: WorkersConfig): {
104
+ fetch: (req: Request, env: E, ctx: ExecutionContext) => Promise<Response>;
105
+ };
106
+ export type { Span };
@@ -0,0 +1,255 @@
1
+ /// <reference types="@cloudflare/workers-types" />
2
+ //
3
+ // Native Cloudflare Workers / Vercel Edge tracing path for Heystack.
4
+ //
5
+ // Unlike `@heystack/otel/node`, this entry contains NO Node built-ins. It uses
6
+ // only `@opentelemetry/api`, `@opentelemetry/sdk-trace-base`,
7
+ // `@opentelemetry/resources` and the platform `fetch` — all WinterCG-safe — and
8
+ // ships its own OTLP/JSON-over-fetch span exporter so it runs on Workers/Edge
9
+ // where the Node SDK cannot.
10
+ import { context, trace, SpanKind, SpanStatusCode, } from "@opentelemetry/api";
11
+ import { Resource } from "@opentelemetry/resources";
12
+ import { BasicTracerProvider, SimpleSpanProcessor, } from "@opentelemetry/sdk-trace-base";
13
+ import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
14
+ import { buildExporterConfig } from "./core.js";
15
+ // `ExportResult` / `ExportResultCode` mirror `@opentelemetry/core`. We define
16
+ // them inline (structurally identical) rather than import them: core is only a
17
+ // transitive dep of sdk-trace-base and isn't reliably resolvable, and keeping it
18
+ // out guarantees no extra (potentially node-platform) code in the bundle.
19
+ const ExportResultCode = { SUCCESS: 0, FAILED: 1 };
20
+ /** Convert an OTel HrTime `[seconds, nanos]` tuple to a nanosecond string. */
21
+ function hrTimeToUnixNano(time) {
22
+ // BigInt math keeps full nanosecond precision without float rounding.
23
+ return (BigInt(time[0]) * 1000000000n + BigInt(time[1])).toString();
24
+ }
25
+ /** Map a single attribute value to the OTLP/JSON tagged-union form. */
26
+ function toAnyValue(value) {
27
+ if (typeof value === "string")
28
+ return { stringValue: value };
29
+ if (typeof value === "boolean")
30
+ return { boolValue: value };
31
+ if (typeof value === "number") {
32
+ // OTLP distinguishes int from double; Number.isInteger picks the int tag.
33
+ return Number.isInteger(value)
34
+ ? { intValue: String(value) }
35
+ : { doubleValue: value };
36
+ }
37
+ if (Array.isArray(value)) {
38
+ return { arrayValue: { values: value.map(toAnyValue) } };
39
+ }
40
+ // Fallback: stringify anything else (objects, bigint, etc.).
41
+ return { stringValue: String(value) };
42
+ }
43
+ function toKeyValues(attrs) {
44
+ const out = [];
45
+ if (!attrs)
46
+ return out;
47
+ for (const key of Object.keys(attrs)) {
48
+ const v = attrs[key];
49
+ if (v === undefined || v === null)
50
+ continue;
51
+ out.push({ key, value: toAnyValue(v) });
52
+ }
53
+ return out;
54
+ }
55
+ /**
56
+ * Map the OTel API `SpanKind` enum to the OTLP wire encoding.
57
+ *
58
+ * The API enum is INTERNAL=0, SERVER=1, CLIENT=2, PRODUCER=3, CONSUMER=4, but
59
+ * the OTLP/JSON wire format (and Heystack's schema `SPAN_KIND` map) is
60
+ * UNSPECIFIED=0, INTERNAL=1, SERVER=2, CLIENT=3, PRODUCER=4, CONSUMER=5 — i.e.
61
+ * shifted by one. Without this shift, SERVER spans would be ingested as
62
+ * INTERNAL.
63
+ */
64
+ function toOtlpKind(kind) {
65
+ return kind + 1;
66
+ }
67
+ function toStatus(status) {
68
+ // SpanStatusCode is the int enum (UNSET=0, OK=1, ERROR=2) the schema expects.
69
+ const out = { code: status.code };
70
+ if (status.message)
71
+ out.message = status.message;
72
+ return out;
73
+ }
74
+ function readableSpanToOtlp(span) {
75
+ const ctx = span.spanContext();
76
+ return {
77
+ traceId: ctx.traceId,
78
+ spanId: ctx.spanId,
79
+ parentSpanId: span.parentSpanId ?? "",
80
+ name: span.name,
81
+ kind: toOtlpKind(span.kind),
82
+ startTimeUnixNano: hrTimeToUnixNano(span.startTime),
83
+ endTimeUnixNano: hrTimeToUnixNano(span.endTime),
84
+ attributes: toKeyValues(span.attributes),
85
+ status: toStatus(span.status),
86
+ };
87
+ }
88
+ /**
89
+ * Serialize a batch of ended spans into a single OTLP/JSON traces payload.
90
+ *
91
+ * All spans in a SimpleSpanProcessor batch share the same per-request provider,
92
+ * hence the same resource, so we emit a single resourceSpans entry.
93
+ */
94
+ export function serializeSpans(spans) {
95
+ const resourceAttrs = spans[0]
96
+ ? spans[0].resource.attributes
97
+ : {};
98
+ return {
99
+ resourceSpans: [
100
+ {
101
+ resource: { attributes: toKeyValues(resourceAttrs) },
102
+ scopeSpans: [{ spans: spans.map(readableSpanToOtlp) }],
103
+ },
104
+ ],
105
+ };
106
+ }
107
+ // ---------------------------------------------------------------------------
108
+ // Exporter
109
+ // ---------------------------------------------------------------------------
110
+ /**
111
+ * A WinterCG-compatible OTLP/JSON span exporter. POSTs ended spans to the
112
+ * Heystack ingest using the platform `fetch` — no Node built-ins.
113
+ */
114
+ export class HeystackSpanExporter {
115
+ url;
116
+ headers;
117
+ shutdownState = false;
118
+ constructor(options) {
119
+ const cfg = buildExporterConfig(options);
120
+ this.url = cfg.url;
121
+ this.headers = {
122
+ ...cfg.headers,
123
+ "content-type": "application/json",
124
+ };
125
+ }
126
+ export(spans, resultCallback) {
127
+ if (this.shutdownState) {
128
+ resultCallback({
129
+ code: ExportResultCode.FAILED,
130
+ error: new Error("HeystackSpanExporter has been shut down"),
131
+ });
132
+ return;
133
+ }
134
+ if (spans.length === 0) {
135
+ resultCallback({ code: ExportResultCode.SUCCESS });
136
+ return;
137
+ }
138
+ const body = JSON.stringify(serializeSpans(spans));
139
+ fetch(this.url, { method: "POST", headers: this.headers, body })
140
+ .then((res) => {
141
+ if (res.ok) {
142
+ resultCallback({ code: ExportResultCode.SUCCESS });
143
+ }
144
+ else {
145
+ resultCallback({
146
+ code: ExportResultCode.FAILED,
147
+ error: new Error(`Heystack ingest responded ${res.status} ${res.statusText}`),
148
+ });
149
+ }
150
+ })
151
+ .catch((error) => {
152
+ resultCallback({
153
+ code: ExportResultCode.FAILED,
154
+ error: error instanceof Error ? error : new Error(String(error)),
155
+ });
156
+ });
157
+ }
158
+ shutdown() {
159
+ this.shutdownState = true;
160
+ return Promise.resolve();
161
+ }
162
+ forceFlush() {
163
+ return Promise.resolve();
164
+ }
165
+ }
166
+ /**
167
+ * Build a `BasicTracerProvider` wired to a `HeystackSpanExporter` via a
168
+ * `SimpleSpanProcessor`. Exposed for advanced users who want to add manual
169
+ * spans; for typical use prefer {@link instrument}.
170
+ *
171
+ * On Workers each request should get its own short-lived provider so its spans
172
+ * can be flushed via `ctx.waitUntil(provider.forceFlush())`.
173
+ */
174
+ export function createTracerProvider(config) {
175
+ const exporter = new HeystackSpanExporter(config);
176
+ return new BasicTracerProvider({
177
+ resource: new Resource({ [ATTR_SERVICE_NAME]: config.service }),
178
+ spanProcessors: [new SimpleSpanProcessor(exporter)],
179
+ });
180
+ }
181
+ let warnedNoKey = false;
182
+ function warnOnceNoKey() {
183
+ if (warnedNoKey)
184
+ return;
185
+ warnedNoKey = true;
186
+ console.warn("[heystack] HEYSTACK_API_KEY not set — Workers tracing disabled, handler runs untraced.");
187
+ }
188
+ /** Reset the once-only no-key warning. Internal/testing helper. */
189
+ export function __resetWarnings() {
190
+ warnedNoKey = false;
191
+ }
192
+ /**
193
+ * Wrap a Worker's default export so every request is auto-traced with a SERVER
194
+ * span. The export is flushed via `ctx.waitUntil` so it completes after the
195
+ * response is returned.
196
+ *
197
+ * import { instrument } from "@heystack/otel/workers";
198
+ * export default instrument(
199
+ * { async fetch(req, env, ctx) { return new Response("ok"); } },
200
+ * { service: "my-worker" },
201
+ * );
202
+ *
203
+ * If no API key is available (neither `config.apiKey` nor
204
+ * `env.HEYSTACK_API_KEY`), the handler runs untraced.
205
+ */
206
+ export function instrument(handler, config) {
207
+ return {
208
+ async fetch(req, env, ctx) {
209
+ const apiKey = config.apiKey ?? env?.HEYSTACK_API_KEY;
210
+ // No key → run untraced (warn once).
211
+ if (!apiKey) {
212
+ warnOnceNoKey();
213
+ return handler.fetch(req, env, ctx);
214
+ }
215
+ const provider = createTracerProvider({
216
+ apiKey,
217
+ service: config.service,
218
+ endpoint: config.endpoint,
219
+ });
220
+ const tracer = provider.getTracer("@heystack/otel/workers");
221
+ const url = new URL(req.url);
222
+ const span = tracer.startSpan(`${req.method} ${url.pathname}`, {
223
+ kind: SpanKind.SERVER,
224
+ attributes: {
225
+ "http.request.method": req.method,
226
+ "url.full": req.url,
227
+ "url.path": url.pathname,
228
+ "server.address": url.host,
229
+ },
230
+ });
231
+ const flush = () => ctx.waitUntil(provider.forceFlush());
232
+ try {
233
+ const response = await context.with(trace.setSpan(context.active(), span), () => handler.fetch(req, env, ctx));
234
+ span.setAttribute("http.response.status_code", response.status);
235
+ span.setStatus({
236
+ code: response.status >= 500 ? SpanStatusCode.ERROR : SpanStatusCode.UNSET,
237
+ });
238
+ span.end();
239
+ flush();
240
+ return response;
241
+ }
242
+ catch (error) {
243
+ if (error instanceof Error)
244
+ span.recordException(error);
245
+ span.setStatus({
246
+ code: SpanStatusCode.ERROR,
247
+ message: error instanceof Error ? error.message : String(error),
248
+ });
249
+ span.end();
250
+ flush();
251
+ throw error;
252
+ }
253
+ },
254
+ };
255
+ }
package/package.json CHANGED
@@ -1,12 +1,17 @@
1
1
  {
2
2
  "name": "@heystack/otel",
3
- "version": "0.0.1",
4
- "description": "One-call OpenTelemetry tracing that exports to Heystack.",
3
+ "version": "0.1.0",
4
+ "description": "Runtime-aware OpenTelemetry tracing that exports to Heystack (Node, Next.js, Workers).",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
- "main": "./dist/index.js",
8
- "types": "./dist/index.d.ts",
9
- "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } },
7
+ "main": "./dist/core.js",
8
+ "types": "./dist/core.d.ts",
9
+ "exports": {
10
+ ".": { "types": "./dist/core.d.ts", "import": "./dist/core.js" },
11
+ "./node": { "types": "./dist/node.d.ts", "import": "./dist/node.js" },
12
+ "./next": { "types": "./dist/next.d.ts", "import": "./dist/next.js" },
13
+ "./workers": { "types": "./dist/workers.d.ts", "import": "./dist/workers.js" }
14
+ },
10
15
  "files": ["dist", "README.md"],
11
16
  "publishConfig": { "access": "public" },
12
17
  "scripts": {
@@ -16,14 +21,18 @@
16
21
  "prepublishOnly": "pnpm build"
17
22
  },
18
23
  "dependencies": {
24
+ "@opentelemetry/api": "^1.9.0",
19
25
  "@opentelemetry/sdk-node": "^0.57.0",
20
26
  "@opentelemetry/exporter-trace-otlp-http": "^0.57.0",
27
+ "@opentelemetry/auto-instrumentations-node": "^0.55.0",
28
+ "@opentelemetry/sdk-trace-base": "^1.30.0",
21
29
  "@opentelemetry/resources": "^1.30.0",
22
30
  "@opentelemetry/semantic-conventions": "^1.30.0"
23
31
  },
24
32
  "devDependencies": {
25
33
  "vitest": "^2.1.0",
26
34
  "typescript": "^5.7.0",
27
- "@types/node": "^22.0.0"
35
+ "@types/node": "^22.0.0",
36
+ "@cloudflare/workers-types": "^4"
28
37
  }
29
38
  }
package/dist/index.d.ts DELETED
@@ -1,22 +0,0 @@
1
- import { NodeSDK } from "@opentelemetry/sdk-node";
2
- export interface HeystackOptions {
3
- /** Your Heystack ingest key (sk_live_...). */
4
- apiKey: string;
5
- /** The OTel service.name this app reports as (must match the app's service in Heystack). */
6
- service: string;
7
- /** Override the ingest endpoint (defaults to Heystack production). */
8
- endpoint?: string;
9
- }
10
- export interface ExporterConfig {
11
- url: string;
12
- headers: {
13
- Authorization: string;
14
- };
15
- }
16
- /** Pure: derive the OTLP/HTTP exporter URL + auth header. Unit-tested. */
17
- export declare function buildExporterConfig(o: HeystackOptions): ExporterConfig;
18
- /**
19
- * Initialise Heystack OpenTelemetry tracing. Call once, as early as possible in
20
- * your app's entry point. Returns the started NodeSDK.
21
- */
22
- export declare function initHeystack(o: HeystackOptions): NodeSDK;
package/dist/index.js DELETED
@@ -1,21 +0,0 @@
1
- import { NodeSDK } from "@opentelemetry/sdk-node";
2
- import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
3
- const DEFAULT_ENDPOINT = "https://ingest.heystack.dev";
4
- /** Pure: derive the OTLP/HTTP exporter URL + auth header. Unit-tested. */
5
- export function buildExporterConfig(o) {
6
- const base = (o.endpoint ?? DEFAULT_ENDPOINT).replace(/\/+$/, "");
7
- return { url: `${base}/v1/traces`, headers: { Authorization: `Bearer ${o.apiKey}` } };
8
- }
9
- /**
10
- * Initialise Heystack OpenTelemetry tracing. Call once, as early as possible in
11
- * your app's entry point. Returns the started NodeSDK.
12
- */
13
- export function initHeystack(o) {
14
- const cfg = buildExporterConfig(o);
15
- const sdk = new NodeSDK({
16
- serviceName: o.service,
17
- traceExporter: new OTLPTraceExporter({ url: cfg.url, headers: cfg.headers }),
18
- });
19
- sdk.start();
20
- return sdk;
21
- }