@interfere/node 0.0.1-canary.0 → 0.0.1-canary.1
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 +1 -1
- package/dist/capture.cjs +1 -0
- package/dist/capture.d.cts +35 -0
- package/dist/capture.d.mts +3 -2
- package/dist/capture.mjs +2 -85
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +4 -0
- package/dist/index.mjs +1 -3
- package/dist/init.cjs +1 -0
- package/dist/init.d.cts +62 -0
- package/dist/init.d.mts +5 -4
- package/dist/init.mjs +2 -87
- package/dist/internal/dedupe.cjs +1 -0
- package/dist/internal/dedupe.d.cts +6 -0
- package/dist/internal/dedupe.d.mts +2 -1
- package/dist/internal/dedupe.mjs +2 -11
- package/dist/internal/env.cjs +1 -0
- package/dist/internal/env.d.cts +21 -0
- package/dist/internal/env.d.mts +5 -4
- package/dist/internal/env.mjs +2 -25
- package/dist/internal/global-handlers.cjs +1 -0
- package/dist/internal/global-handlers.d.cts +26 -0
- package/dist/internal/global-handlers.d.mts +2 -1
- package/dist/internal/global-handlers.mjs +2 -68
- package/dist/internal/normalize-request.cjs +1 -0
- package/dist/internal/normalize-request.d.cts +16 -0
- package/dist/internal/normalize-request.d.mts +2 -1
- package/dist/internal/normalize-request.mjs +2 -58
- package/dist/internal/release-slug.cjs +1 -0
- package/dist/internal/release-slug.d.cts +9 -0
- package/dist/internal/release-slug.d.mts +2 -1
- package/dist/internal/release-slug.mjs +2 -32
- package/dist/internal/remote-config.cjs +1 -0
- package/dist/internal/remote-config.d.cts +6 -0
- package/dist/internal/remote-config.d.mts +2 -1
- package/dist/internal/remote-config.mjs +2 -29
- package/dist/internal/types.cjs +0 -0
- package/dist/internal/types.d.cts +17 -0
- package/dist/internal/types.d.mts +2 -1
- package/dist/internal/types.mjs +1 -1
- package/dist/internal/url.cjs +1 -0
- package/dist/internal/url.d.cts +5 -0
- package/dist/internal/url.d.mts +5 -0
- package/dist/internal/url.mjs +2 -0
- package/dist/internal/version.cjs +1 -0
- package/dist/internal/version.d.cts +5 -0
- package/dist/internal/version.d.mts +2 -1
- package/dist/internal/version.mjs +2 -5
- package/dist/package.cjs +1 -0
- package/dist/package.mjs +2 -5
- package/package.json +40 -16
- package/dist/capture.d.mts.map +0 -1
- package/dist/capture.mjs.map +0 -1
- package/dist/init.d.mts.map +0 -1
- package/dist/init.mjs.map +0 -1
- package/dist/internal/dedupe.d.mts.map +0 -1
- package/dist/internal/dedupe.mjs.map +0 -1
- package/dist/internal/env.d.mts.map +0 -1
- package/dist/internal/env.mjs.map +0 -1
- package/dist/internal/global-handlers.d.mts.map +0 -1
- package/dist/internal/global-handlers.mjs.map +0 -1
- package/dist/internal/normalize-request.d.mts.map +0 -1
- package/dist/internal/normalize-request.mjs.map +0 -1
- package/dist/internal/release-slug.d.mts.map +0 -1
- package/dist/internal/release-slug.mjs.map +0 -1
- package/dist/internal/remote-config.d.mts.map +0 -1
- package/dist/internal/remote-config.mjs.map +0 -1
- package/dist/internal/types.d.mts.map +0 -1
- package/dist/internal/version.d.mts.map +0 -1
- package/dist/internal/version.mjs.map +0 -1
- package/dist/package.mjs.map +0 -1
package/README.md
CHANGED
package/dist/capture.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const require_internal_dedupe=require("./internal/dedupe.cjs"),require_internal_env=require("./internal/env.cjs"),require_internal_normalize_request=require("./internal/normalize-request.cjs"),require_internal_remote_config=require("./internal/remote-config.cjs");let _interfere_types_sdk_errors=require("@interfere/types/sdk/errors"),_opentelemetry_api=require("@opentelemetry/api");const TRACER_NAME=`@interfere/node`,DEFAULT_ERROR_MECHANISM={type:_interfere_types_sdk_errors.MECHANISM_TYPE.node.captureError,handled:!0};function captureError(error,request,errorContext){if(!require_internal_env.isEnabledInEnvironment()||!require_internal_remote_config.isPluginEnabled(`errors`)||require_internal_dedupe.isErrorCaptured(error))return;require_internal_dedupe.markErrorCaptured(error);let err=(0,_interfere_types_sdk_errors.toError)(error),normalizedRequest=require_internal_normalize_request.normalizeRequest(request),mechanism=errorContext?.mechanism??DEFAULT_ERROR_MECHANISM,span=_opentelemetry_api.trace.getTracer(TRACER_NAME).startSpan(`interfere.error.captured`,{kind:_opentelemetry_api.SpanKind.INTERNAL},_opentelemetry_api.context.active()),requestMethod=errorContext?.node?.requestMethod??normalizedRequest?.method??null;requestMethod&&span.setAttribute(`http.request.method`,requestMethod);let requestPath=errorContext?.node?.requestPath??normalizedRequest?.path??null;requestPath&&span.setAttribute(`url.path`,requestPath);let sessionId=normalizedRequest?.headers.get(`x-interfere-session`);sessionId&&span.setAttribute(`interfere.session.id`,sessionId);let eventAttrs={"exception.type":err.name,"exception.message":err.message,"interfere.exception.mechanism":mechanism.type,"interfere.exception.handled":mechanism.handled};err.stack&&(eventAttrs[`exception.stacktrace`]=err.stack);let digest=errorContext?.node?.errorDigest;digest&&(eventAttrs[`interfere.error.digest`]=digest),span.addEvent(`exception`,eventAttrs),span.setStatus({code:_opentelemetry_api.SpanStatusCode.ERROR}),span.end()}function captureMessage(message,request,errorContext){captureError(Error(message),request,errorContext)}async function withSpan(name,fn){let span=_opentelemetry_api.trace.getTracer(TRACER_NAME).startSpan(name);try{return await _opentelemetry_api.context.with(_opentelemetry_api.trace.setSpan(_opentelemetry_api.context.active(),span),fn)}catch(error){throw span.recordException(error),span.setStatus({code:_opentelemetry_api.SpanStatusCode.ERROR}),error}finally{span.end()}}exports.captureError=captureError,exports.captureMessage=captureMessage,exports.withSpan=withSpan;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { CaptureErrorContext } from "./internal/types.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/capture.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Reports an error to Interfere. Safe to call from anywhere — no-op when the
|
|
6
|
+
* SDK is not configured (`INTERFERE_PUBLIC_KEY` unset) or when the `errors`
|
|
7
|
+
* plugin is disabled by remote config.
|
|
8
|
+
*
|
|
9
|
+
* Internally opens a short OTel span and records the error as an `exception`
|
|
10
|
+
* event on it. The span ships through the same `OTLPTraceExporter` wired up
|
|
11
|
+
* in `init()`, so error capture inherits the OTel transport (batch, retry,
|
|
12
|
+
* traceparent propagation) without a separate fetch path. The collector's
|
|
13
|
+
* `mapTracesToSpanEvents` lifts the OTel-semconv `exception.*` attrs plus
|
|
14
|
+
* our `interfere.exception.*` taxonomy into the `span_events` datasource.
|
|
15
|
+
*
|
|
16
|
+
* Pass the request (a `Request`, a Node `IncomingMessage`-like object, or
|
|
17
|
+
* any object with `headers`/`method`/`url`) so the span carries request
|
|
18
|
+
* context. If there's an active OTel context (e.g. an HTTP handler span),
|
|
19
|
+
* the exception attaches to it as a child; otherwise it becomes a
|
|
20
|
+
* root span.
|
|
21
|
+
*/
|
|
22
|
+
declare function captureError(error: unknown, request?: unknown, errorContext?: CaptureErrorContext): void;
|
|
23
|
+
/**
|
|
24
|
+
* Reports a string message as an error event.
|
|
25
|
+
*/
|
|
26
|
+
declare function captureMessage(message: string, request?: unknown, errorContext?: CaptureErrorContext): void;
|
|
27
|
+
/**
|
|
28
|
+
* Wraps a function in an OTel span. Thrown errors are recorded on the span
|
|
29
|
+
* and rethrown. Without an active provider (i.e. before `init()`), still
|
|
30
|
+
* runs `fn` but produces no telemetry.
|
|
31
|
+
*/
|
|
32
|
+
declare function withSpan<T>(name: string, fn: () => Promise<T> | T): Promise<T>;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { type CaptureErrorContext, captureError, captureMessage, withSpan };
|
|
35
|
+
//# sourceMappingURL=capture.d.cts.map
|
package/dist/capture.d.mts
CHANGED
|
@@ -3,7 +3,7 @@ import { CaptureErrorContext } from "./internal/types.mjs";
|
|
|
3
3
|
//#region src/capture.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Reports an error to Interfere. Safe to call from anywhere — no-op when the
|
|
6
|
-
* SDK is not configured (`
|
|
6
|
+
* SDK is not configured (`INTERFERE_PUBLIC_KEY` unset) or when the `errors`
|
|
7
7
|
* plugin is disabled by remote config.
|
|
8
8
|
*
|
|
9
9
|
* Internally opens a short OTel span and records the error as an `exception`
|
|
@@ -31,4 +31,5 @@ declare function captureMessage(message: string, request?: unknown, errorContext
|
|
|
31
31
|
*/
|
|
32
32
|
declare function withSpan<T>(name: string, fn: () => Promise<T> | T): Promise<T>;
|
|
33
33
|
//#endregion
|
|
34
|
-
export { type CaptureErrorContext, captureError, captureMessage, withSpan };
|
|
34
|
+
export { type CaptureErrorContext, captureError, captureMessage, withSpan };
|
|
35
|
+
//# sourceMappingURL=capture.d.mts.map
|
package/dist/capture.mjs
CHANGED
|
@@ -1,85 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { normalizeRequest } from "./internal/normalize-request.mjs";
|
|
4
|
-
import { isPluginEnabled } from "./internal/remote-config.mjs";
|
|
5
|
-
import { MECHANISM_TYPE, toError } from "@interfere/types/sdk/errors";
|
|
6
|
-
import { SpanKind, SpanStatusCode, context, trace } from "@opentelemetry/api";
|
|
7
|
-
//#region src/capture.ts
|
|
8
|
-
const TRACER_NAME = "@interfere/node";
|
|
9
|
-
const ERROR_SPAN_NAME = "interfere.error.captured";
|
|
10
|
-
const SESSION_HEADER = "x-interfere-session";
|
|
11
|
-
const DEFAULT_ERROR_MECHANISM = {
|
|
12
|
-
type: MECHANISM_TYPE.node.captureError,
|
|
13
|
-
handled: true
|
|
14
|
-
};
|
|
15
|
-
/**
|
|
16
|
-
* Reports an error to Interfere. Safe to call from anywhere — no-op when the
|
|
17
|
-
* SDK is not configured (`INTERFERE_API_KEY` unset) or when the `errors`
|
|
18
|
-
* plugin is disabled by remote config.
|
|
19
|
-
*
|
|
20
|
-
* Internally opens a short OTel span and records the error as an `exception`
|
|
21
|
-
* event on it. The span ships through the same `OTLPTraceExporter` wired up
|
|
22
|
-
* in `init()`, so error capture inherits the OTel transport (batch, retry,
|
|
23
|
-
* traceparent propagation) without a separate fetch path. The collector's
|
|
24
|
-
* `mapTracesToSpanEvents` lifts the OTel-semconv `exception.*` attrs plus
|
|
25
|
-
* our `interfere.exception.*` taxonomy into the `span_events` datasource.
|
|
26
|
-
*
|
|
27
|
-
* Pass the request (a `Request`, a Node `IncomingMessage`-like object, or
|
|
28
|
-
* any object with `headers`/`method`/`url`) so the span carries request
|
|
29
|
-
* context. If there's an active OTel context (e.g. an HTTP handler span),
|
|
30
|
-
* the exception attaches to it as a child; otherwise it becomes a
|
|
31
|
-
* root span.
|
|
32
|
-
*/
|
|
33
|
-
function captureError(error, request, errorContext) {
|
|
34
|
-
if (!isEnabledInEnvironment()) return;
|
|
35
|
-
if (!isPluginEnabled("errors")) return;
|
|
36
|
-
if (isErrorCaptured(error)) return;
|
|
37
|
-
markErrorCaptured(error);
|
|
38
|
-
const err = toError(error);
|
|
39
|
-
const normalizedRequest = normalizeRequest(request);
|
|
40
|
-
const mechanism = errorContext?.mechanism ?? DEFAULT_ERROR_MECHANISM;
|
|
41
|
-
const span = trace.getTracer(TRACER_NAME).startSpan(ERROR_SPAN_NAME, { kind: SpanKind.INTERNAL }, context.active());
|
|
42
|
-
const requestMethod = errorContext?.node?.requestMethod ?? normalizedRequest?.method ?? null;
|
|
43
|
-
if (requestMethod) span.setAttribute("http.request.method", requestMethod);
|
|
44
|
-
const requestPath = errorContext?.node?.requestPath ?? normalizedRequest?.path ?? null;
|
|
45
|
-
if (requestPath) span.setAttribute("url.path", requestPath);
|
|
46
|
-
const sessionId = normalizedRequest?.headers.get(SESSION_HEADER);
|
|
47
|
-
if (sessionId) span.setAttribute("interfere.session.id", sessionId);
|
|
48
|
-
const eventAttrs = {
|
|
49
|
-
"exception.type": err.name,
|
|
50
|
-
"exception.message": err.message,
|
|
51
|
-
"interfere.exception.mechanism": mechanism.type,
|
|
52
|
-
"interfere.exception.handled": mechanism.handled
|
|
53
|
-
};
|
|
54
|
-
if (err.stack) eventAttrs["exception.stacktrace"] = err.stack;
|
|
55
|
-
const digest = errorContext?.node?.errorDigest;
|
|
56
|
-
if (digest) eventAttrs["interfere.error.digest"] = digest;
|
|
57
|
-
span.addEvent("exception", eventAttrs);
|
|
58
|
-
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
59
|
-
span.end();
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Reports a string message as an error event.
|
|
63
|
-
*/
|
|
64
|
-
function captureMessage(message, request, errorContext) {
|
|
65
|
-
captureError(new Error(message), request, errorContext);
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Wraps a function in an OTel span. Thrown errors are recorded on the span
|
|
69
|
-
* and rethrown. Without an active provider (i.e. before `init()`), still
|
|
70
|
-
* runs `fn` but produces no telemetry.
|
|
71
|
-
*/
|
|
72
|
-
async function withSpan(name, fn) {
|
|
73
|
-
const span = trace.getTracer(TRACER_NAME).startSpan(name);
|
|
74
|
-
try {
|
|
75
|
-
return await context.with(trace.setSpan(context.active(), span), fn);
|
|
76
|
-
} catch (error) {
|
|
77
|
-
span.recordException(error);
|
|
78
|
-
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
79
|
-
throw error;
|
|
80
|
-
} finally {
|
|
81
|
-
span.end();
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
//#endregion
|
|
85
|
-
export { captureError, captureMessage, withSpan };
|
|
1
|
+
import{isErrorCaptured,markErrorCaptured}from"./internal/dedupe.mjs";import{isEnabledInEnvironment}from"./internal/env.mjs";import{normalizeRequest}from"./internal/normalize-request.mjs";import{isPluginEnabled}from"./internal/remote-config.mjs";import{MECHANISM_TYPE,toError}from"@interfere/types/sdk/errors";import{SpanKind,SpanStatusCode,context,trace}from"@opentelemetry/api";const TRACER_NAME=`@interfere/node`,DEFAULT_ERROR_MECHANISM={type:MECHANISM_TYPE.node.captureError,handled:!0};function captureError(error,request,errorContext){if(!isEnabledInEnvironment()||!isPluginEnabled(`errors`)||isErrorCaptured(error))return;markErrorCaptured(error);let err=toError(error),normalizedRequest=normalizeRequest(request),mechanism=errorContext?.mechanism??DEFAULT_ERROR_MECHANISM,span=trace.getTracer(TRACER_NAME).startSpan(`interfere.error.captured`,{kind:SpanKind.INTERNAL},context.active()),requestMethod=errorContext?.node?.requestMethod??normalizedRequest?.method??null;requestMethod&&span.setAttribute(`http.request.method`,requestMethod);let requestPath=errorContext?.node?.requestPath??normalizedRequest?.path??null;requestPath&&span.setAttribute(`url.path`,requestPath);let sessionId=normalizedRequest?.headers.get(`x-interfere-session`);sessionId&&span.setAttribute(`interfere.session.id`,sessionId);let eventAttrs={"exception.type":err.name,"exception.message":err.message,"interfere.exception.mechanism":mechanism.type,"interfere.exception.handled":mechanism.handled};err.stack&&(eventAttrs[`exception.stacktrace`]=err.stack);let digest=errorContext?.node?.errorDigest;digest&&(eventAttrs[`interfere.error.digest`]=digest),span.addEvent(`exception`,eventAttrs),span.setStatus({code:SpanStatusCode.ERROR}),span.end()}function captureMessage(message,request,errorContext){captureError(Error(message),request,errorContext)}async function withSpan(name,fn){let span=trace.getTracer(TRACER_NAME).startSpan(name);try{return await context.with(trace.setSpan(context.active(),span),fn)}catch(error){throw span.recordException(error),span.setStatus({code:SpanStatusCode.ERROR}),error}finally{span.end()}}export{captureError,captureMessage,withSpan};
|
|
2
|
+
//# sourceMappingURL=capture.mjs.map
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const require_capture=require("./capture.cjs"),require_init=require("./init.cjs");exports.captureError=require_capture.captureError,exports.captureMessage=require_capture.captureMessage,exports.close=require_init.close,exports.flush=require_init.flush,exports.init=require_init.init,exports.withSpan=require_capture.withSpan;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { CaptureErrorContext } from "./internal/types.cjs";
|
|
2
|
+
import { captureError, captureMessage, withSpan } from "./capture.cjs";
|
|
3
|
+
import { InitOptions, close, flush, init } from "./init.cjs";
|
|
4
|
+
export { type CaptureErrorContext, type InitOptions, captureError, captureMessage, close, flush, init, withSpan };
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { close, flush, init } from "./init.mjs";
|
|
3
|
-
export { captureError, captureMessage, close, flush, init, withSpan };
|
|
1
|
+
import{captureError,captureMessage,withSpan}from"./capture.mjs";import{close,flush,init}from"./init.mjs";export{captureError,captureMessage,close,flush,init,withSpan};
|
package/dist/init.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const require_internal_env=require("./internal/env.cjs"),require_internal_url=require("./internal/url.cjs"),require_internal_remote_config=require("./internal/remote-config.cjs"),require_internal_global_handlers=require("./internal/global-handlers.cjs"),require_internal_release_slug=require("./internal/release-slug.cjs"),require_internal_version=require("./internal/version.cjs");let _interfere_constants_api=require("@interfere/constants/api"),_interfere_types_releases_slug=require("@interfere/types/releases/slug"),_opentelemetry_context_async_hooks=require("@opentelemetry/context-async-hooks"),_opentelemetry_exporter_trace_otlp_http=require("@opentelemetry/exporter-trace-otlp-http"),_opentelemetry_resources=require("@opentelemetry/resources"),_opentelemetry_sdk_trace_base=require("@opentelemetry/sdk-trace-base"),_opentelemetry_sdk_trace_node=require("@opentelemetry/sdk-trace-node"),provider=null;async function init(options={}){if(provider)return;let env=require_internal_env.readInterfereEnv();if(!env.publicKey)return;let releaseSlug=env.forceEnable?_interfere_types_releases_slug.LOCAL_DEV_RELEASE_SLUG:require_internal_release_slug.resolveReleaseSlug().slug;releaseSlug||console.warn("[interfere] No commit SHA available; server spans will ship without `release.slug`. Set `INTERFERE_SOURCE_ID` (or any of `VERCEL_GIT_COMMIT_SHA`, `GITHUB_SHA`) on the runtime env."),provider=new _opentelemetry_sdk_trace_node.NodeTracerProvider({resource:(0,_opentelemetry_resources.resourceFromAttributes)({"service.name":options.serviceName??process.env.OTEL_SERVICE_NAME??`node-app`,...options.serviceNamespace?{"service.namespace":options.serviceNamespace}:{},"deployment.environment.name":env.nodeEnvironment??`unknown`,"telemetry.sdk.language":`nodejs`,"interfere.sdk.name":`@interfere/node`,"interfere.sdk.version":require_internal_version.PRODUCER_VERSION,...releaseSlug?{"release.slug":releaseSlug}:{}}),spanProcessors:[new _opentelemetry_sdk_trace_base.BatchSpanProcessor(new _opentelemetry_exporter_trace_otlp_http.OTLPTraceExporter({url:require_internal_url.withPublicKey(`${env.apiUrl}${_interfere_constants_api.API_PATHS.SINK}`,env.publicKey),headers:{"user-agent":require_internal_version.PRODUCER_VERSION,"x-interfere-producer-version":require_internal_version.PRODUCER_VERSION,...env.forceEnable?{"x-interfere-force-enable":`1`}:{}}}))]}),provider.register({contextManager:new _opentelemetry_context_async_hooks.AsyncLocalStorageContextManager().enable()}),require_internal_global_handlers.installGlobalHandlers({captureUncaughtException:options.captureUncaughtException??!0,captureUnhandledRejection:options.captureUnhandledRejection??!0,flush}),await require_internal_remote_config.fetchAndCacheRemoteConfig()}async function close(){provider&&=(require_internal_global_handlers.uninstallGlobalHandlers(),await provider.shutdown(),null)}async function flush(){provider&&await provider.forceFlush()}exports.close=close,exports.flush=flush,exports.init=init;
|
package/dist/init.d.cts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
//#region src/init.d.ts
|
|
2
|
+
interface InitOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Install a `process.on("uncaughtException")` listener that captures the
|
|
5
|
+
* error, flushes spans, and exits the process. Default `true`. Disable to
|
|
6
|
+
* use your own handler.
|
|
7
|
+
*/
|
|
8
|
+
readonly captureUncaughtException?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Install a `process.on("unhandledRejection")` listener that captures the
|
|
11
|
+
* rejection and flushes spans. Default `true`. Disable to use your own
|
|
12
|
+
* handler. Whether the process exits is governed by Node's
|
|
13
|
+
* `--unhandled-rejections` flag, not the SDK.
|
|
14
|
+
*/
|
|
15
|
+
readonly captureUnhandledRejection?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Sets the OTel `service.name` resource attribute — the identity spans
|
|
18
|
+
* are grouped by in the dashboard. Defaults to `OTEL_SERVICE_NAME` env
|
|
19
|
+
* var, then `"node-app"`. Use this to distinguish workers, crons, and
|
|
20
|
+
* other processes that share an `INTERFERE_PUBLIC_KEY`.
|
|
21
|
+
*/
|
|
22
|
+
readonly serviceName?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Sets the OTel `service.namespace` resource attribute. Optional; useful
|
|
25
|
+
* for grouping multiple services under one logical product surface
|
|
26
|
+
* (e.g. `"checkout"` across an API and worker fleet).
|
|
27
|
+
*/
|
|
28
|
+
readonly serviceNamespace?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Bootstraps the Interfere Node SDK: wires a private `NodeTracerProvider`
|
|
32
|
+
* whose spans export to the Interfere collector via OTLP/HTTP, registers an
|
|
33
|
+
* `AsyncLocalStorageContextManager` so user spans propagate across async
|
|
34
|
+
* boundaries, and fetches the remote-config gate.
|
|
35
|
+
*
|
|
36
|
+
* Idempotent — safe to call from multiple entry points; only the first call
|
|
37
|
+
* wires up.
|
|
38
|
+
*
|
|
39
|
+
* Bails silently when `INTERFERE_PUBLIC_KEY` is unset so dev runs without the
|
|
40
|
+
* SDK configured don't crash.
|
|
41
|
+
*
|
|
42
|
+
* The `release.slug` resource attribute is derived deterministically from
|
|
43
|
+
* the commit SHA (auto-detected from `INTERFERE_SOURCE_ID`,
|
|
44
|
+
* `VERCEL_GIT_COMMIT_SHA`, `GITHUB_SHA`, or `git rev-parse HEAD`) plus the
|
|
45
|
+
* public key — same algorithm used by `@interfere/next` and the browser SDKs,
|
|
46
|
+
* so spans from cohabiting frontends agree on release identity.
|
|
47
|
+
*/
|
|
48
|
+
declare function init(options?: InitOptions): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Flushes any buffered spans and shuts down the tracer provider. Call from
|
|
51
|
+
* a server shutdown hook (`SIGTERM`, `process.on("beforeExit")`, framework
|
|
52
|
+
* lifecycle close) so in-flight error spans aren't lost on exit.
|
|
53
|
+
*/
|
|
54
|
+
declare function close(): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Forces buffered spans to be exported immediately. Useful in serverless
|
|
57
|
+
* runtimes where the process is suspended between requests.
|
|
58
|
+
*/
|
|
59
|
+
declare function flush(): Promise<void>;
|
|
60
|
+
//#endregion
|
|
61
|
+
export { InitOptions, close, flush, init };
|
|
62
|
+
//# sourceMappingURL=init.d.cts.map
|
package/dist/init.d.mts
CHANGED
|
@@ -17,7 +17,7 @@ interface InitOptions {
|
|
|
17
17
|
* Sets the OTel `service.name` resource attribute — the identity spans
|
|
18
18
|
* are grouped by in the dashboard. Defaults to `OTEL_SERVICE_NAME` env
|
|
19
19
|
* var, then `"node-app"`. Use this to distinguish workers, crons, and
|
|
20
|
-
* other processes that share an `
|
|
20
|
+
* other processes that share an `INTERFERE_PUBLIC_KEY`.
|
|
21
21
|
*/
|
|
22
22
|
readonly serviceName?: string;
|
|
23
23
|
/**
|
|
@@ -36,13 +36,13 @@ interface InitOptions {
|
|
|
36
36
|
* Idempotent — safe to call from multiple entry points; only the first call
|
|
37
37
|
* wires up.
|
|
38
38
|
*
|
|
39
|
-
* Bails silently when `
|
|
39
|
+
* Bails silently when `INTERFERE_PUBLIC_KEY` is unset so dev runs without the
|
|
40
40
|
* SDK configured don't crash.
|
|
41
41
|
*
|
|
42
42
|
* The `release.slug` resource attribute is derived deterministically from
|
|
43
43
|
* the commit SHA (auto-detected from `INTERFERE_SOURCE_ID`,
|
|
44
44
|
* `VERCEL_GIT_COMMIT_SHA`, `GITHUB_SHA`, or `git rev-parse HEAD`) plus the
|
|
45
|
-
*
|
|
45
|
+
* public key — same algorithm used by `@interfere/next` and the browser SDKs,
|
|
46
46
|
* so spans from cohabiting frontends agree on release identity.
|
|
47
47
|
*/
|
|
48
48
|
declare function init(options?: InitOptions): Promise<void>;
|
|
@@ -58,4 +58,5 @@ declare function close(): Promise<void>;
|
|
|
58
58
|
*/
|
|
59
59
|
declare function flush(): Promise<void>;
|
|
60
60
|
//#endregion
|
|
61
|
-
export { InitOptions, close, flush, init };
|
|
61
|
+
export { InitOptions, close, flush, init };
|
|
62
|
+
//# sourceMappingURL=init.d.mts.map
|
package/dist/init.mjs
CHANGED
|
@@ -1,87 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { installGlobalHandlers, uninstallGlobalHandlers } from "./internal/global-handlers.mjs";
|
|
4
|
-
import { resolveReleaseSlug } from "./internal/release-slug.mjs";
|
|
5
|
-
import { PRODUCER_VERSION } from "./internal/version.mjs";
|
|
6
|
-
import { API_PATHS } from "@interfere/constants/api";
|
|
7
|
-
import { AsyncLocalStorageContextManager } from "@opentelemetry/context-async-hooks";
|
|
8
|
-
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
9
|
-
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
10
|
-
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
11
|
-
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
|
12
|
-
//#region src/init.ts
|
|
13
|
-
const DEFAULT_SERVICE_NAME = "node-app";
|
|
14
|
-
let provider = null;
|
|
15
|
-
/**
|
|
16
|
-
* Bootstraps the Interfere Node SDK: wires a private `NodeTracerProvider`
|
|
17
|
-
* whose spans export to the Interfere collector via OTLP/HTTP, registers an
|
|
18
|
-
* `AsyncLocalStorageContextManager` so user spans propagate across async
|
|
19
|
-
* boundaries, and fetches the remote-config gate.
|
|
20
|
-
*
|
|
21
|
-
* Idempotent — safe to call from multiple entry points; only the first call
|
|
22
|
-
* wires up.
|
|
23
|
-
*
|
|
24
|
-
* Bails silently when `INTERFERE_API_KEY` is unset so dev runs without the
|
|
25
|
-
* SDK configured don't crash.
|
|
26
|
-
*
|
|
27
|
-
* The `release.slug` resource attribute is derived deterministically from
|
|
28
|
-
* the commit SHA (auto-detected from `INTERFERE_SOURCE_ID`,
|
|
29
|
-
* `VERCEL_GIT_COMMIT_SHA`, `GITHUB_SHA`, or `git rev-parse HEAD`) plus the
|
|
30
|
-
* API key — same algorithm used by `@interfere/next` and the browser SDKs,
|
|
31
|
-
* so spans from cohabiting frontends agree on release identity.
|
|
32
|
-
*/
|
|
33
|
-
async function init(options = {}) {
|
|
34
|
-
if (provider) return;
|
|
35
|
-
const env = readInterfereEnv();
|
|
36
|
-
if (!env.apiKey) return;
|
|
37
|
-
const releaseSlug = env.forceEnable ? "rel_0000000000000000" : resolveReleaseSlug().slug;
|
|
38
|
-
if (!releaseSlug) console.warn("[interfere] No commit SHA available; server spans will ship without `release.slug`. Set `INTERFERE_SOURCE_ID` (or any of `VERCEL_GIT_COMMIT_SHA`, `GITHUB_SHA`) on the runtime env.");
|
|
39
|
-
provider = new NodeTracerProvider({
|
|
40
|
-
resource: resourceFromAttributes({
|
|
41
|
-
"service.name": options.serviceName ?? process.env["OTEL_SERVICE_NAME"] ?? DEFAULT_SERVICE_NAME,
|
|
42
|
-
...options.serviceNamespace ? { "service.namespace": options.serviceNamespace } : {},
|
|
43
|
-
"deployment.environment.name": env.nodeEnvironment ?? "unknown",
|
|
44
|
-
"telemetry.sdk.language": "nodejs",
|
|
45
|
-
"interfere.sdk.name": "@interfere/node",
|
|
46
|
-
"interfere.sdk.version": PRODUCER_VERSION,
|
|
47
|
-
...releaseSlug ? { "release.slug": releaseSlug } : {}
|
|
48
|
-
}),
|
|
49
|
-
spanProcessors: [new BatchSpanProcessor(new OTLPTraceExporter({
|
|
50
|
-
url: `${env.apiUrl}${API_PATHS.SINK}`,
|
|
51
|
-
headers: {
|
|
52
|
-
"user-agent": PRODUCER_VERSION,
|
|
53
|
-
"x-api-key": env.apiKey,
|
|
54
|
-
"x-interfere-producer-version": PRODUCER_VERSION,
|
|
55
|
-
...env.forceEnable ? { "x-interfere-force-enable": "1" } : {}
|
|
56
|
-
}
|
|
57
|
-
}))]
|
|
58
|
-
});
|
|
59
|
-
provider.register({ contextManager: new AsyncLocalStorageContextManager().enable() });
|
|
60
|
-
installGlobalHandlers({
|
|
61
|
-
captureUncaughtException: options.captureUncaughtException ?? true,
|
|
62
|
-
captureUnhandledRejection: options.captureUnhandledRejection ?? true,
|
|
63
|
-
flush
|
|
64
|
-
});
|
|
65
|
-
await fetchAndCacheRemoteConfig();
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Flushes any buffered spans and shuts down the tracer provider. Call from
|
|
69
|
-
* a server shutdown hook (`SIGTERM`, `process.on("beforeExit")`, framework
|
|
70
|
-
* lifecycle close) so in-flight error spans aren't lost on exit.
|
|
71
|
-
*/
|
|
72
|
-
async function close() {
|
|
73
|
-
if (!provider) return;
|
|
74
|
-
uninstallGlobalHandlers();
|
|
75
|
-
await provider.shutdown();
|
|
76
|
-
provider = null;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Forces buffered spans to be exported immediately. Useful in serverless
|
|
80
|
-
* runtimes where the process is suspended between requests.
|
|
81
|
-
*/
|
|
82
|
-
async function flush() {
|
|
83
|
-
if (!provider) return;
|
|
84
|
-
await provider.forceFlush();
|
|
85
|
-
}
|
|
86
|
-
//#endregion
|
|
87
|
-
export { close, flush, init };
|
|
1
|
+
import{readInterfereEnv}from"./internal/env.mjs";import{withPublicKey}from"./internal/url.mjs";import{fetchAndCacheRemoteConfig}from"./internal/remote-config.mjs";import{installGlobalHandlers,uninstallGlobalHandlers}from"./internal/global-handlers.mjs";import{resolveReleaseSlug}from"./internal/release-slug.mjs";import{PRODUCER_VERSION}from"./internal/version.mjs";import{API_PATHS}from"@interfere/constants/api";import{LOCAL_DEV_RELEASE_SLUG}from"@interfere/types/releases/slug";import{AsyncLocalStorageContextManager}from"@opentelemetry/context-async-hooks";import{OTLPTraceExporter}from"@opentelemetry/exporter-trace-otlp-http";import{resourceFromAttributes}from"@opentelemetry/resources";import{BatchSpanProcessor}from"@opentelemetry/sdk-trace-base";import{NodeTracerProvider}from"@opentelemetry/sdk-trace-node";let provider=null;async function init(options={}){if(provider)return;let env=readInterfereEnv();if(!env.publicKey)return;let releaseSlug=env.forceEnable?LOCAL_DEV_RELEASE_SLUG:resolveReleaseSlug().slug;releaseSlug||console.warn("[interfere] No commit SHA available; server spans will ship without `release.slug`. Set `INTERFERE_SOURCE_ID` (or any of `VERCEL_GIT_COMMIT_SHA`, `GITHUB_SHA`) on the runtime env."),provider=new NodeTracerProvider({resource:resourceFromAttributes({"service.name":options.serviceName??process.env.OTEL_SERVICE_NAME??`node-app`,...options.serviceNamespace?{"service.namespace":options.serviceNamespace}:{},"deployment.environment.name":env.nodeEnvironment??`unknown`,"telemetry.sdk.language":`nodejs`,"interfere.sdk.name":`@interfere/node`,"interfere.sdk.version":PRODUCER_VERSION,...releaseSlug?{"release.slug":releaseSlug}:{}}),spanProcessors:[new BatchSpanProcessor(new OTLPTraceExporter({url:withPublicKey(`${env.apiUrl}${API_PATHS.SINK}`,env.publicKey),headers:{"user-agent":PRODUCER_VERSION,"x-interfere-producer-version":PRODUCER_VERSION,...env.forceEnable?{"x-interfere-force-enable":`1`}:{}}}))]}),provider.register({contextManager:new AsyncLocalStorageContextManager().enable()}),installGlobalHandlers({captureUncaughtException:options.captureUncaughtException??!0,captureUnhandledRejection:options.captureUnhandledRejection??!0,flush}),await fetchAndCacheRemoteConfig()}async function close(){provider&&=(uninstallGlobalHandlers(),await provider.shutdown(),null)}async function flush(){provider&&await provider.forceFlush()}export{close,flush,init};
|
|
2
|
+
//# sourceMappingURL=init.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const seenErrors=new WeakSet;function isErrorCaptured(error){return error instanceof Error&&seenErrors.has(error)}function markErrorCaptured(error){error instanceof Error&&seenErrors.add(error)}exports.isErrorCaptured=isErrorCaptured,exports.markErrorCaptured=markErrorCaptured;
|
|
@@ -2,4 +2,5 @@
|
|
|
2
2
|
declare function isErrorCaptured(error: unknown): boolean;
|
|
3
3
|
declare function markErrorCaptured(error: unknown): void;
|
|
4
4
|
//#endregion
|
|
5
|
-
export { isErrorCaptured, markErrorCaptured };
|
|
5
|
+
export { isErrorCaptured, markErrorCaptured };
|
|
6
|
+
//# sourceMappingURL=dedupe.d.mts.map
|
package/dist/internal/dedupe.mjs
CHANGED
|
@@ -1,11 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
function isErrorCaptured(error) {
|
|
4
|
-
return error instanceof Error && seenErrors.has(error);
|
|
5
|
-
}
|
|
6
|
-
function markErrorCaptured(error) {
|
|
7
|
-
if (!(error instanceof Error)) return;
|
|
8
|
-
seenErrors.add(error);
|
|
9
|
-
}
|
|
10
|
-
//#endregion
|
|
11
|
-
export { isErrorCaptured, markErrorCaptured };
|
|
1
|
+
const seenErrors=new WeakSet;function isErrorCaptured(error){return error instanceof Error&&seenErrors.has(error)}function markErrorCaptured(error){error instanceof Error&&seenErrors.add(error)}export{isErrorCaptured,markErrorCaptured};
|
|
2
|
+
//# sourceMappingURL=dedupe.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let _interfere_constants_api=require("@interfere/constants/api"),_interfere_types_sdk_env=require("@interfere/types/sdk/env"),_interfere_types_sdk_runtime=require("@interfere/types/sdk/runtime");function isEnabledInEnvironment(){return!0}function readInterfereEnv(){let nodeEnvironment=(0,_interfere_types_sdk_runtime.normalizeEnv)(process.env.NODE_ENV);return{apiUrl:(0,_interfere_types_sdk_env.parseEnvValue)(process.env.INTERFERE_API_URL)??_interfere_constants_api.API_URL,forceEnable:process.env.INTERFERE_FORCE_ENABLE===`1`,nodeEnvironment,publicKey:(0,_interfere_types_sdk_env.parseEnvValue)(process.env.INTERFERE_PUBLIC_KEY)}}exports.isEnabledInEnvironment=isEnabledInEnvironment,exports.readInterfereEnv=readInterfereEnv;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Env } from "@interfere/types/sdk/runtime";
|
|
2
|
+
|
|
3
|
+
//#region src/internal/env.d.ts
|
|
4
|
+
interface InterfereEnv {
|
|
5
|
+
readonly apiUrl: string;
|
|
6
|
+
readonly forceEnable: boolean;
|
|
7
|
+
readonly nodeEnvironment: Env;
|
|
8
|
+
readonly publicKey: string | null;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Server SDKs always run end-to-end. There's no `NEXT_PUBLIC_*` force-enable
|
|
12
|
+
* escape hatch here — if `INTERFERE_PUBLIC_KEY` is set, the SDK is on. The Next
|
|
13
|
+
* SDK guards on `NODE_ENV=production || NEXT_PUBLIC_INTERFERE_FORCE_ENABLE`
|
|
14
|
+
* because `next dev` runs the SDK during development without a real release
|
|
15
|
+
* slug; that asymmetry doesn't apply to plain Node servers.
|
|
16
|
+
*/
|
|
17
|
+
declare function isEnabledInEnvironment(): boolean;
|
|
18
|
+
declare function readInterfereEnv(): InterfereEnv;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { InterfereEnv, isEnabledInEnvironment, readInterfereEnv };
|
|
21
|
+
//# sourceMappingURL=env.d.cts.map
|
package/dist/internal/env.d.mts
CHANGED
|
@@ -2,14 +2,14 @@ import { Env } from "@interfere/types/sdk/runtime";
|
|
|
2
2
|
|
|
3
3
|
//#region src/internal/env.d.ts
|
|
4
4
|
interface InterfereEnv {
|
|
5
|
-
readonly apiKey: string | null;
|
|
6
5
|
readonly apiUrl: string;
|
|
7
6
|
readonly forceEnable: boolean;
|
|
8
|
-
readonly nodeEnvironment:
|
|
7
|
+
readonly nodeEnvironment: Env;
|
|
8
|
+
readonly publicKey: string | null;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
11
|
* Server SDKs always run end-to-end. There's no `NEXT_PUBLIC_*` force-enable
|
|
12
|
-
* escape hatch here — if `
|
|
12
|
+
* escape hatch here — if `INTERFERE_PUBLIC_KEY` is set, the SDK is on. The Next
|
|
13
13
|
* SDK guards on `NODE_ENV=production || NEXT_PUBLIC_INTERFERE_FORCE_ENABLE`
|
|
14
14
|
* because `next dev` runs the SDK during development without a real release
|
|
15
15
|
* slug; that asymmetry doesn't apply to plain Node servers.
|
|
@@ -17,4 +17,5 @@ interface InterfereEnv {
|
|
|
17
17
|
declare function isEnabledInEnvironment(): boolean;
|
|
18
18
|
declare function readInterfereEnv(): InterfereEnv;
|
|
19
19
|
//#endregion
|
|
20
|
-
export { InterfereEnv, isEnabledInEnvironment, readInterfereEnv };
|
|
20
|
+
export { InterfereEnv, isEnabledInEnvironment, readInterfereEnv };
|
|
21
|
+
//# sourceMappingURL=env.d.mts.map
|
package/dist/internal/env.mjs
CHANGED
|
@@ -1,25 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { normalizeEnv } from "@interfere/types/sdk/runtime";
|
|
4
|
-
//#region src/internal/env.ts
|
|
5
|
-
/**
|
|
6
|
-
* Server SDKs always run end-to-end. There's no `NEXT_PUBLIC_*` force-enable
|
|
7
|
-
* escape hatch here — if `INTERFERE_API_KEY` is set, the SDK is on. The Next
|
|
8
|
-
* SDK guards on `NODE_ENV=production || NEXT_PUBLIC_INTERFERE_FORCE_ENABLE`
|
|
9
|
-
* because `next dev` runs the SDK during development without a real release
|
|
10
|
-
* slug; that asymmetry doesn't apply to plain Node servers.
|
|
11
|
-
*/
|
|
12
|
-
function isEnabledInEnvironment() {
|
|
13
|
-
return true;
|
|
14
|
-
}
|
|
15
|
-
function readInterfereEnv() {
|
|
16
|
-
const nodeEnvironment = normalizeEnv(process.env["NODE_ENV"]) ?? "production";
|
|
17
|
-
return {
|
|
18
|
-
apiKey: parseEnvValue(process.env["INTERFERE_API_KEY"]),
|
|
19
|
-
apiUrl: parseEnvValue(process.env["INTERFERE_API_URL"]) ?? API_URL,
|
|
20
|
-
forceEnable: process.env["INTERFERE_FORCE_ENABLE"] === "1",
|
|
21
|
-
nodeEnvironment
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
//#endregion
|
|
25
|
-
export { isEnabledInEnvironment, readInterfereEnv };
|
|
1
|
+
import{API_URL}from"@interfere/constants/api";import{parseEnvValue}from"@interfere/types/sdk/env";import{normalizeEnv}from"@interfere/types/sdk/runtime";function isEnabledInEnvironment(){return!0}function readInterfereEnv(){let nodeEnvironment=normalizeEnv(process.env.NODE_ENV);return{apiUrl:parseEnvValue(process.env.INTERFERE_API_URL)??API_URL,forceEnable:process.env.INTERFERE_FORCE_ENABLE===`1`,nodeEnvironment,publicKey:parseEnvValue(process.env.INTERFERE_PUBLIC_KEY)}}export{isEnabledInEnvironment,readInterfereEnv};
|
|
2
|
+
//# sourceMappingURL=env.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const require_capture=require("../capture.cjs");let _interfere_types_sdk_errors=require("@interfere/types/sdk/errors"),uncaughtHandler=null,unhandledHandler=null;function installGlobalHandlers(options){options.captureUncaughtException&&uncaughtHandler===null&&(uncaughtHandler=error=>{handleUncaughtException(error,options.flush).catch(()=>void 0)},process.on(`uncaughtException`,uncaughtHandler)),options.captureUnhandledRejection&&unhandledHandler===null&&(unhandledHandler=reason=>{handleUnhandledRejection(reason,options.flush).catch(()=>void 0)},process.on(`unhandledRejection`,unhandledHandler))}function uninstallGlobalHandlers(){uncaughtHandler!==null&&(process.off(`uncaughtException`,uncaughtHandler),uncaughtHandler=null),unhandledHandler!==null&&(process.off(`unhandledRejection`,unhandledHandler),unhandledHandler=null)}async function handleUncaughtException(error,flush){await require_capture.captureError(error,void 0,{mechanism:{type:_interfere_types_sdk_errors.MECHANISM_TYPE.node.uncaughtException,handled:!1}}),await flushWithTimeout(flush),process.exit(1)}async function handleUnhandledRejection(reason,flush){await require_capture.captureError(reason,void 0,{mechanism:{type:_interfere_types_sdk_errors.MECHANISM_TYPE.node.unhandledRejection,handled:!1}}),await flushWithTimeout(flush)}function flushWithTimeout(flush){return Promise.race([flush(),new Promise(resolve=>{setTimeout(resolve,2e3)})])}exports.installGlobalHandlers=installGlobalHandlers,exports.uninstallGlobalHandlers=uninstallGlobalHandlers;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/internal/global-handlers.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Installs `process.on("uncaughtException")` and
|
|
4
|
+
* `process.on("unhandledRejection")` listeners that report to Interfere.
|
|
5
|
+
*
|
|
6
|
+
* Uncaught exceptions: captured, spans flushed (best-effort, 2s timeout),
|
|
7
|
+
* then `process.exit(1)`. Once an `uncaughtException` listener is
|
|
8
|
+
* registered, Node will not exit on its own — leaving the process alive
|
|
9
|
+
* after an uncaught error usually means a corrupted state, so we own the
|
|
10
|
+
* exit. Customers who need different exit semantics should opt out and
|
|
11
|
+
* register their own handler.
|
|
12
|
+
*
|
|
13
|
+
* Unhandled rejections: captured and flushed, but the SDK does not call
|
|
14
|
+
* `process.exit`. Whether unhandled rejections are fatal is governed by
|
|
15
|
+
* Node's `--unhandled-rejections` flag (default `throw` since v15) and we
|
|
16
|
+
* don't override that.
|
|
17
|
+
*/
|
|
18
|
+
declare function installGlobalHandlers(options: {
|
|
19
|
+
readonly captureUncaughtException: boolean;
|
|
20
|
+
readonly captureUnhandledRejection: boolean;
|
|
21
|
+
readonly flush: () => Promise<void>;
|
|
22
|
+
}): void;
|
|
23
|
+
declare function uninstallGlobalHandlers(): void;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { installGlobalHandlers, uninstallGlobalHandlers };
|
|
26
|
+
//# sourceMappingURL=global-handlers.d.cts.map
|
|
@@ -22,4 +22,5 @@ declare function installGlobalHandlers(options: {
|
|
|
22
22
|
}): void;
|
|
23
23
|
declare function uninstallGlobalHandlers(): void;
|
|
24
24
|
//#endregion
|
|
25
|
-
export { installGlobalHandlers, uninstallGlobalHandlers };
|
|
25
|
+
export { installGlobalHandlers, uninstallGlobalHandlers };
|
|
26
|
+
//# sourceMappingURL=global-handlers.d.mts.map
|
|
@@ -1,68 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
//#region src/internal/global-handlers.ts
|
|
4
|
-
const FLUSH_TIMEOUT_MS = 2e3;
|
|
5
|
-
let uncaughtHandler = null;
|
|
6
|
-
let unhandledHandler = null;
|
|
7
|
-
/**
|
|
8
|
-
* Installs `process.on("uncaughtException")` and
|
|
9
|
-
* `process.on("unhandledRejection")` listeners that report to Interfere.
|
|
10
|
-
*
|
|
11
|
-
* Uncaught exceptions: captured, spans flushed (best-effort, 2s timeout),
|
|
12
|
-
* then `process.exit(1)`. Once an `uncaughtException` listener is
|
|
13
|
-
* registered, Node will not exit on its own — leaving the process alive
|
|
14
|
-
* after an uncaught error usually means a corrupted state, so we own the
|
|
15
|
-
* exit. Customers who need different exit semantics should opt out and
|
|
16
|
-
* register their own handler.
|
|
17
|
-
*
|
|
18
|
-
* Unhandled rejections: captured and flushed, but the SDK does not call
|
|
19
|
-
* `process.exit`. Whether unhandled rejections are fatal is governed by
|
|
20
|
-
* Node's `--unhandled-rejections` flag (default `throw` since v15) and we
|
|
21
|
-
* don't override that.
|
|
22
|
-
*/
|
|
23
|
-
function installGlobalHandlers(options) {
|
|
24
|
-
if (options.captureUncaughtException && uncaughtHandler === null) {
|
|
25
|
-
uncaughtHandler = (error) => {
|
|
26
|
-
handleUncaughtException(error, options.flush).catch(() => void 0);
|
|
27
|
-
};
|
|
28
|
-
process.on("uncaughtException", uncaughtHandler);
|
|
29
|
-
}
|
|
30
|
-
if (options.captureUnhandledRejection && unhandledHandler === null) {
|
|
31
|
-
unhandledHandler = (reason) => {
|
|
32
|
-
handleUnhandledRejection(reason, options.flush).catch(() => void 0);
|
|
33
|
-
};
|
|
34
|
-
process.on("unhandledRejection", unhandledHandler);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function uninstallGlobalHandlers() {
|
|
38
|
-
if (uncaughtHandler !== null) {
|
|
39
|
-
process.off("uncaughtException", uncaughtHandler);
|
|
40
|
-
uncaughtHandler = null;
|
|
41
|
-
}
|
|
42
|
-
if (unhandledHandler !== null) {
|
|
43
|
-
process.off("unhandledRejection", unhandledHandler);
|
|
44
|
-
unhandledHandler = null;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
async function handleUncaughtException(error, flush) {
|
|
48
|
-
await captureError(error, void 0, { mechanism: {
|
|
49
|
-
type: MECHANISM_TYPE.node.uncaughtException,
|
|
50
|
-
handled: false
|
|
51
|
-
} });
|
|
52
|
-
await flushWithTimeout(flush);
|
|
53
|
-
process.exit(1);
|
|
54
|
-
}
|
|
55
|
-
async function handleUnhandledRejection(reason, flush) {
|
|
56
|
-
await captureError(reason, void 0, { mechanism: {
|
|
57
|
-
type: MECHANISM_TYPE.node.unhandledRejection,
|
|
58
|
-
handled: false
|
|
59
|
-
} });
|
|
60
|
-
await flushWithTimeout(flush);
|
|
61
|
-
}
|
|
62
|
-
function flushWithTimeout(flush) {
|
|
63
|
-
return Promise.race([flush(), new Promise((resolve) => {
|
|
64
|
-
setTimeout(resolve, FLUSH_TIMEOUT_MS);
|
|
65
|
-
})]);
|
|
66
|
-
}
|
|
67
|
-
//#endregion
|
|
68
|
-
export { installGlobalHandlers, uninstallGlobalHandlers };
|
|
1
|
+
import{captureError}from"../capture.mjs";import{MECHANISM_TYPE}from"@interfere/types/sdk/errors";let uncaughtHandler=null,unhandledHandler=null;function installGlobalHandlers(options){options.captureUncaughtException&&uncaughtHandler===null&&(uncaughtHandler=error=>{handleUncaughtException(error,options.flush).catch(()=>void 0)},process.on(`uncaughtException`,uncaughtHandler)),options.captureUnhandledRejection&&unhandledHandler===null&&(unhandledHandler=reason=>{handleUnhandledRejection(reason,options.flush).catch(()=>void 0)},process.on(`unhandledRejection`,unhandledHandler))}function uninstallGlobalHandlers(){uncaughtHandler!==null&&(process.off(`uncaughtException`,uncaughtHandler),uncaughtHandler=null),unhandledHandler!==null&&(process.off(`unhandledRejection`,unhandledHandler),unhandledHandler=null)}async function handleUncaughtException(error,flush){await captureError(error,void 0,{mechanism:{type:MECHANISM_TYPE.node.uncaughtException,handled:!1}}),await flushWithTimeout(flush),process.exit(1)}async function handleUnhandledRejection(reason,flush){await captureError(reason,void 0,{mechanism:{type:MECHANISM_TYPE.node.unhandledRejection,handled:!1}}),await flushWithTimeout(flush)}function flushWithTimeout(flush){return Promise.race([flush(),new Promise(resolve=>{setTimeout(resolve,2e3)})])}export{installGlobalHandlers,uninstallGlobalHandlers};
|
|
2
|
+
//# sourceMappingURL=global-handlers.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function normalizeRequest(request){if(request instanceof Request)return{method:normalizeMethod(request.method),path:normalizePath(request.url),headers:request.headers};if(typeof request!=`object`||!request)return null;let value=request;return{method:normalizeMethod(value.method),path:normalizePath(typeof value.path==`string`?value.path:toStringOrDefault(value.url)),headers:normalizeHeaders(value.headers)}}function normalizeMethod(value){if(typeof value!=`string`)return`GET`;let method=value.trim();return method.length>0?method.toUpperCase():`GET`}function normalizePath(value){if(value.length===0)return`/`;try{return new URL(value,`http://localhost`).pathname||`/`}catch{return value.startsWith(`/`)?value:`/${value}`}}function toStringOrDefault(value,fallback=`/`){if(typeof value!=`string`)return fallback;let trimmed=value.trim();return trimmed.length>0?trimmed:fallback}function normalizeHeaders(value){if(value instanceof Headers)return new Headers(value);if(typeof value!=`object`||!value)return new Headers;let headers=new Headers;for(let[key,headerValue]of Object.entries(value))if(headerValue!==void 0){if(Array.isArray(headerValue)){headers.set(key,headerValue.join(`, `));continue}headers.set(key,String(headerValue))}return headers}exports.TRACEPARENT_HEADER=`traceparent`,exports.normalizeRequest=normalizeRequest;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { NormalizedRequest } from "./types.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/internal/normalize-request.d.ts
|
|
4
|
+
declare const TRACEPARENT_HEADER = "traceparent";
|
|
5
|
+
/**
|
|
6
|
+
* Coerces a request-shaped value into the headers/method/path triple used
|
|
7
|
+
* by envelope construction. Handles WHATWG `Request`, Node
|
|
8
|
+
* `IncomingMessage`-like objects, and plain object literals.
|
|
9
|
+
*
|
|
10
|
+
* Returns `null` only for non-objects; otherwise produces a best-effort
|
|
11
|
+
* `NormalizedRequest` with sensible defaults.
|
|
12
|
+
*/
|
|
13
|
+
declare function normalizeRequest(request: unknown): NormalizedRequest | null;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { TRACEPARENT_HEADER, normalizeRequest };
|
|
16
|
+
//# sourceMappingURL=normalize-request.d.cts.map
|
|
@@ -12,4 +12,5 @@ declare const TRACEPARENT_HEADER = "traceparent";
|
|
|
12
12
|
*/
|
|
13
13
|
declare function normalizeRequest(request: unknown): NormalizedRequest | null;
|
|
14
14
|
//#endregion
|
|
15
|
-
export { TRACEPARENT_HEADER, normalizeRequest };
|
|
15
|
+
export { TRACEPARENT_HEADER, normalizeRequest };
|
|
16
|
+
//# sourceMappingURL=normalize-request.d.mts.map
|
|
@@ -1,58 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Coerces a request-shaped value into the headers/method/path triple used
|
|
5
|
-
* by envelope construction. Handles WHATWG `Request`, Node
|
|
6
|
-
* `IncomingMessage`-like objects, and plain object literals.
|
|
7
|
-
*
|
|
8
|
-
* Returns `null` only for non-objects; otherwise produces a best-effort
|
|
9
|
-
* `NormalizedRequest` with sensible defaults.
|
|
10
|
-
*/
|
|
11
|
-
function normalizeRequest(request) {
|
|
12
|
-
if (request instanceof Request) return {
|
|
13
|
-
method: normalizeMethod(request.method),
|
|
14
|
-
path: normalizePath(request.url),
|
|
15
|
-
headers: request.headers
|
|
16
|
-
};
|
|
17
|
-
if (typeof request !== "object" || request === null) return null;
|
|
18
|
-
const value = request;
|
|
19
|
-
return {
|
|
20
|
-
method: normalizeMethod(value.method),
|
|
21
|
-
path: normalizePath(typeof value.path === "string" ? value.path : toStringOrDefault(value.url)),
|
|
22
|
-
headers: normalizeHeaders(value.headers)
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
function normalizeMethod(value) {
|
|
26
|
-
if (typeof value !== "string") return "GET";
|
|
27
|
-
const method = value.trim();
|
|
28
|
-
return method.length > 0 ? method.toUpperCase() : "GET";
|
|
29
|
-
}
|
|
30
|
-
function normalizePath(value) {
|
|
31
|
-
if (value.length === 0) return "/";
|
|
32
|
-
try {
|
|
33
|
-
return new URL(value, "http://localhost").pathname || "/";
|
|
34
|
-
} catch {
|
|
35
|
-
return value.startsWith("/") ? value : `/${value}`;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
function toStringOrDefault(value, fallback = "/") {
|
|
39
|
-
if (typeof value !== "string") return fallback;
|
|
40
|
-
const trimmed = value.trim();
|
|
41
|
-
return trimmed.length > 0 ? trimmed : fallback;
|
|
42
|
-
}
|
|
43
|
-
function normalizeHeaders(value) {
|
|
44
|
-
if (value instanceof Headers) return new Headers(value);
|
|
45
|
-
if (typeof value !== "object" || value === null) return new Headers();
|
|
46
|
-
const headers = new Headers();
|
|
47
|
-
for (const [key, headerValue] of Object.entries(value)) {
|
|
48
|
-
if (headerValue === void 0) continue;
|
|
49
|
-
if (Array.isArray(headerValue)) {
|
|
50
|
-
headers.set(key, headerValue.join(", "));
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
headers.set(key, String(headerValue));
|
|
54
|
-
}
|
|
55
|
-
return headers;
|
|
56
|
-
}
|
|
57
|
-
//#endregion
|
|
58
|
-
export { TRACEPARENT_HEADER, normalizeRequest };
|
|
1
|
+
const TRACEPARENT_HEADER=`traceparent`;function normalizeRequest(request){if(request instanceof Request)return{method:normalizeMethod(request.method),path:normalizePath(request.url),headers:request.headers};if(typeof request!=`object`||!request)return null;let value=request;return{method:normalizeMethod(value.method),path:normalizePath(typeof value.path==`string`?value.path:toStringOrDefault(value.url)),headers:normalizeHeaders(value.headers)}}function normalizeMethod(value){if(typeof value!=`string`)return`GET`;let method=value.trim();return method.length>0?method.toUpperCase():`GET`}function normalizePath(value){if(value.length===0)return`/`;try{return new URL(value,`http://localhost`).pathname||`/`}catch{return value.startsWith(`/`)?value:`/${value}`}}function toStringOrDefault(value,fallback=`/`){if(typeof value!=`string`)return fallback;let trimmed=value.trim();return trimmed.length>0?trimmed:fallback}function normalizeHeaders(value){if(value instanceof Headers)return new Headers(value);if(typeof value!=`object`||!value)return new Headers;let headers=new Headers;for(let[key,headerValue]of Object.entries(value))if(headerValue!==void 0){if(Array.isArray(headerValue)){headers.set(key,headerValue.join(`, `));continue}headers.set(key,String(headerValue))}return headers}export{TRACEPARENT_HEADER,normalizeRequest};
|
|
2
|
+
//# sourceMappingURL=normalize-request.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let _interfere_types_sdk_env=require("@interfere/types/sdk/env"),_interfere_types_releases_slug=require("@interfere/types/releases/slug"),_interfere_types_integrations=require("@interfere/types/integrations"),node_child_process=require("node:child_process");function runGitCommand(command){try{let output=(0,node_child_process.execSync)(command,{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim();return output.length>0?output:null}catch{return null}}function resolveCommitSha(){return(0,_interfere_types_sdk_env.readFirstEnvValue)(process.env,_interfere_types_integrations.releaseSourceIdEnvKeys)??runGitCommand(`git rev-parse HEAD`)}function resolveReleaseSlug(){let commitSha=resolveCommitSha();return{commitSha,slug:commitSha?(0,_interfere_types_releases_slug.deriveReleaseSlug)(commitSha):null}}exports.resolveCommitSha=resolveCommitSha,exports.resolveReleaseSlug=resolveReleaseSlug;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/internal/release-slug.d.ts
|
|
2
|
+
declare function resolveCommitSha(): string | null;
|
|
3
|
+
declare function resolveReleaseSlug(): {
|
|
4
|
+
commitSha: string | null;
|
|
5
|
+
slug: string | null;
|
|
6
|
+
};
|
|
7
|
+
//#endregion
|
|
8
|
+
export { resolveCommitSha, resolveReleaseSlug };
|
|
9
|
+
//# sourceMappingURL=release-slug.d.cts.map
|
|
@@ -1,32 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { deriveReleaseSlug } from "@interfere/types/releases/slug";
|
|
4
|
-
import { execSync } from "node:child_process";
|
|
5
|
-
//#region src/internal/release-slug.ts
|
|
6
|
-
function runGitCommand(command) {
|
|
7
|
-
try {
|
|
8
|
-
const output = execSync(command, {
|
|
9
|
-
encoding: "utf8",
|
|
10
|
-
stdio: [
|
|
11
|
-
"ignore",
|
|
12
|
-
"pipe",
|
|
13
|
-
"ignore"
|
|
14
|
-
]
|
|
15
|
-
}).trim();
|
|
16
|
-
return output.length > 0 ? output : null;
|
|
17
|
-
} catch {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
function resolveCommitSha() {
|
|
22
|
-
return readFirstEnvValue(process.env, releaseSourceIdEnvKeys) ?? runGitCommand("git rev-parse HEAD");
|
|
23
|
-
}
|
|
24
|
-
function resolveReleaseSlug() {
|
|
25
|
-
const commitSha = resolveCommitSha();
|
|
26
|
-
return {
|
|
27
|
-
commitSha,
|
|
28
|
-
slug: commitSha ? deriveReleaseSlug(commitSha) : null
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
//#endregion
|
|
32
|
-
export { resolveCommitSha, resolveReleaseSlug };
|
|
1
|
+
import{readFirstEnvValue}from"@interfere/types/sdk/env";import{deriveReleaseSlug}from"@interfere/types/releases/slug";import{releaseSourceIdEnvKeys}from"@interfere/types/integrations";import{execSync}from"node:child_process";function runGitCommand(command){try{let output=execSync(command,{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim();return output.length>0?output:null}catch{return null}}function resolveCommitSha(){return readFirstEnvValue(process.env,releaseSourceIdEnvKeys)??runGitCommand(`git rev-parse HEAD`)}function resolveReleaseSlug(){let commitSha=resolveCommitSha();return{commitSha,slug:commitSha?deriveReleaseSlug(commitSha):null}}export{resolveCommitSha,resolveReleaseSlug};
|
|
2
|
+
//# sourceMappingURL=release-slug.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const require_internal_env=require("./env.cjs"),require_internal_url=require("./url.cjs");let _interfere_constants_api=require("@interfere/constants/api"),cachedConfig=null;async function fetchAndCacheRemoteConfig(){if(!require_internal_env.isEnabledInEnvironment())return;let env=require_internal_env.readInterfereEnv();if(env.publicKey!==null)try{let url=require_internal_url.withPublicKey(`${env.apiUrl}${_interfere_constants_api.API_PATHS.CONFIG}`,env.publicKey),response=await fetch(url,{method:`GET`,headers:{"content-type":`application/json`},signal:AbortSignal.timeout(1e4)});if(!response.ok)return;let config=await response.json();config?.plugins&&(cachedConfig=config.plugins)}catch{}}function isPluginEnabled(plugin){return cachedConfig?cachedConfig[plugin]!==!1:!0}exports.fetchAndCacheRemoteConfig=fetchAndCacheRemoteConfig,exports.isPluginEnabled=isPluginEnabled;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//#region src/internal/remote-config.d.ts
|
|
2
|
+
declare function fetchAndCacheRemoteConfig(): Promise<void>;
|
|
3
|
+
declare function isPluginEnabled(plugin: string): boolean;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { fetchAndCacheRemoteConfig, isPluginEnabled };
|
|
6
|
+
//# sourceMappingURL=remote-config.d.cts.map
|
|
@@ -2,4 +2,5 @@
|
|
|
2
2
|
declare function fetchAndCacheRemoteConfig(): Promise<void>;
|
|
3
3
|
declare function isPluginEnabled(plugin: string): boolean;
|
|
4
4
|
//#endregion
|
|
5
|
-
export { fetchAndCacheRemoteConfig, isPluginEnabled };
|
|
5
|
+
export { fetchAndCacheRemoteConfig, isPluginEnabled };
|
|
6
|
+
//# sourceMappingURL=remote-config.d.mts.map
|
|
@@ -1,29 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
//#region src/internal/remote-config.ts
|
|
4
|
-
let cachedConfig = null;
|
|
5
|
-
async function fetchAndCacheRemoteConfig() {
|
|
6
|
-
if (!isEnabledInEnvironment()) return;
|
|
7
|
-
const env = readInterfereEnv();
|
|
8
|
-
if (env.apiKey === null) return;
|
|
9
|
-
try {
|
|
10
|
-
const url = `${env.apiUrl}${API_PATHS.CONFIG}`;
|
|
11
|
-
const response = await fetch(url, {
|
|
12
|
-
method: "GET",
|
|
13
|
-
headers: {
|
|
14
|
-
"content-type": "application/json",
|
|
15
|
-
"x-api-key": env.apiKey
|
|
16
|
-
},
|
|
17
|
-
signal: AbortSignal.timeout(1e4)
|
|
18
|
-
});
|
|
19
|
-
if (!response.ok) return;
|
|
20
|
-
const config = await response.json();
|
|
21
|
-
if (config?.plugins) cachedConfig = config.plugins;
|
|
22
|
-
} catch {}
|
|
23
|
-
}
|
|
24
|
-
function isPluginEnabled(plugin) {
|
|
25
|
-
if (!cachedConfig) return true;
|
|
26
|
-
return cachedConfig[plugin] !== false;
|
|
27
|
-
}
|
|
28
|
-
//#endregion
|
|
29
|
-
export { fetchAndCacheRemoteConfig, isPluginEnabled };
|
|
1
|
+
import{isEnabledInEnvironment,readInterfereEnv}from"./env.mjs";import{withPublicKey}from"./url.mjs";import{API_PATHS}from"@interfere/constants/api";let cachedConfig=null;async function fetchAndCacheRemoteConfig(){if(!isEnabledInEnvironment())return;let env=readInterfereEnv();if(env.publicKey!==null)try{let url=withPublicKey(`${env.apiUrl}${API_PATHS.CONFIG}`,env.publicKey),response=await fetch(url,{method:`GET`,headers:{"content-type":`application/json`},signal:AbortSignal.timeout(1e4)});if(!response.ok)return;let config=await response.json();config?.plugins&&(cachedConfig=config.plugins)}catch{}}function isPluginEnabled(plugin){return cachedConfig?cachedConfig[plugin]!==!1:!0}export{fetchAndCacheRemoteConfig,isPluginEnabled};
|
|
2
|
+
//# sourceMappingURL=remote-config.mjs.map
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { NodeContext } from "@interfere/types/sdk/plugins/context/node";
|
|
2
|
+
import { ErrorMechanism } from "@interfere/types/sdk/plugins/payload/errors";
|
|
3
|
+
|
|
4
|
+
//#region src/internal/types.d.ts
|
|
5
|
+
interface CaptureErrorContext {
|
|
6
|
+
readonly mechanism?: ErrorMechanism;
|
|
7
|
+
readonly node?: Omit<NodeContext, "runtime">;
|
|
8
|
+
readonly traceparent?: string;
|
|
9
|
+
}
|
|
10
|
+
interface NormalizedRequest {
|
|
11
|
+
readonly headers: Headers;
|
|
12
|
+
readonly method: string;
|
|
13
|
+
readonly path: string;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { CaptureErrorContext, NormalizedRequest };
|
|
17
|
+
//# sourceMappingURL=types.d.cts.map
|
package/dist/internal/types.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function withPublicKey(url,publicKey){let next=new URL(url);return next.searchParams.set(`pk`,publicKey),next.toString()}exports.withPublicKey=withPublicKey;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const require_package=require("../package.cjs"),PRODUCER_VERSION=`${require_package.name}@${require_package.version}`;exports.PRODUCER_VERSION=PRODUCER_VERSION;
|
|
@@ -1,5 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
//#
|
|
3
|
-
const PRODUCER_VERSION = `${name}@${version}`;
|
|
4
|
-
//#endregion
|
|
5
|
-
export { PRODUCER_VERSION };
|
|
1
|
+
import{name,version}from"../package.mjs";const PRODUCER_VERSION=`${name}@${version}`;export{PRODUCER_VERSION};
|
|
2
|
+
//# sourceMappingURL=version.mjs.map
|
package/dist/package.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var name=`@interfere/node`,version=`0.0.1-canary.1`;Object.defineProperty(exports,"name",{enumerable:!0,get:function(){return name}}),Object.defineProperty(exports,"version",{enumerable:!0,get:function(){return version}});
|
package/dist/package.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@interfere/node",
|
|
3
|
-
"version": "0.0.1-canary.
|
|
3
|
+
"version": "0.0.1-canary.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Node.js SDK for Interfere. Error tracking and OTel instrumentation for any Node server.",
|
|
6
6
|
"keywords": [
|
|
@@ -21,30 +21,54 @@
|
|
|
21
21
|
"directory": "src/packages/public/node"
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
|
24
|
-
"dist"
|
|
24
|
+
"dist/**/*.cjs",
|
|
25
|
+
"dist/**/*.mjs",
|
|
26
|
+
"dist/**/*.d.cts",
|
|
27
|
+
"dist/**/*.d.mts",
|
|
28
|
+
"README.md"
|
|
25
29
|
],
|
|
26
30
|
"type": "module",
|
|
31
|
+
"main": "./dist/index.cjs",
|
|
32
|
+
"module": "./dist/index.mjs",
|
|
33
|
+
"types": "./dist/index.d.cts",
|
|
27
34
|
"exports": {
|
|
28
35
|
".": {
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
36
|
+
"monorepo": "./src/index.ts",
|
|
37
|
+
"import": {
|
|
38
|
+
"types": "./dist/index.d.mts",
|
|
39
|
+
"default": "./dist/index.mjs"
|
|
40
|
+
},
|
|
41
|
+
"require": {
|
|
42
|
+
"types": "./dist/index.d.cts",
|
|
43
|
+
"default": "./dist/index.cjs"
|
|
44
|
+
}
|
|
32
45
|
},
|
|
33
46
|
"./init": {
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
|
|
47
|
+
"monorepo": "./src/init.ts",
|
|
48
|
+
"import": {
|
|
49
|
+
"types": "./dist/init.d.mts",
|
|
50
|
+
"default": "./dist/init.mjs"
|
|
51
|
+
},
|
|
52
|
+
"require": {
|
|
53
|
+
"types": "./dist/init.d.cts",
|
|
54
|
+
"default": "./dist/init.cjs"
|
|
55
|
+
}
|
|
37
56
|
},
|
|
38
57
|
"./capture": {
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
|
|
58
|
+
"monorepo": "./src/capture.ts",
|
|
59
|
+
"import": {
|
|
60
|
+
"types": "./dist/capture.d.mts",
|
|
61
|
+
"default": "./dist/capture.mjs"
|
|
62
|
+
},
|
|
63
|
+
"require": {
|
|
64
|
+
"types": "./dist/capture.d.cts",
|
|
65
|
+
"default": "./dist/capture.cjs"
|
|
66
|
+
}
|
|
42
67
|
}
|
|
43
68
|
},
|
|
44
69
|
"sideEffects": false,
|
|
45
70
|
"publishConfig": {
|
|
46
|
-
"access": "public"
|
|
47
|
-
"tag": "canary"
|
|
71
|
+
"access": "public"
|
|
48
72
|
},
|
|
49
73
|
"scripts": {
|
|
50
74
|
"build": "tsdown",
|
|
@@ -52,11 +76,11 @@
|
|
|
52
76
|
"typecheck": "tsc --noEmit --incremental"
|
|
53
77
|
},
|
|
54
78
|
"dependencies": {
|
|
55
|
-
"@interfere/constants": "^9.0.
|
|
56
|
-
"@interfere/types": "^9.0.0",
|
|
79
|
+
"@interfere/constants": "^9.0.2-canary.0",
|
|
80
|
+
"@interfere/types": "^9.0.3-canary.0",
|
|
57
81
|
"@opentelemetry/api": "^1.9.1",
|
|
58
82
|
"@opentelemetry/context-async-hooks": "^2.7.1",
|
|
59
|
-
"@opentelemetry/exporter-trace-otlp-http": "^0.
|
|
83
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.218.0",
|
|
60
84
|
"@opentelemetry/resources": "^2.7.0",
|
|
61
85
|
"@opentelemetry/sdk-trace-base": "^2.7.0",
|
|
62
86
|
"@opentelemetry/sdk-trace-node": "^2.7.1",
|
package/dist/capture.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"capture.d.mts","names":[],"sources":["../src/capture.ts"],"mappings":";;;;AAwCA;;;;;;;;;;AA8EA;;;;;;;iBA9EgB,YAAA,CACd,KAAA,WACA,OAAA,YACA,YAAA,GAAe,mBAAA;;;AAwFjB;iBAbgB,cAAA,CACd,OAAA,UACA,OAAA,YACA,YAAA,GAAe,mBAAA;;;;;;iBAUK,QAAA,GAAA,CACpB,IAAA,UACA,EAAA,QAAU,OAAA,CAAQ,CAAA,IAAK,CAAA,GACtB,OAAA,CAAQ,CAAA"}
|
package/dist/capture.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"capture.mjs","names":[],"sources":["../src/capture.ts"],"sourcesContent":["import { MECHANISM_TYPE, toError } from \"@interfere/types/sdk/errors\";\nimport type { ErrorMechanism } from \"@interfere/types/sdk/plugins/payload/errors\";\n\nimport { context, SpanKind, SpanStatusCode, trace } from \"@opentelemetry/api\";\n\nimport { isErrorCaptured, markErrorCaptured } from \"./internal/dedupe.js\";\nimport { isEnabledInEnvironment } from \"./internal/env.js\";\nimport { normalizeRequest } from \"./internal/normalize-request.js\";\nimport { isPluginEnabled } from \"./internal/remote-config.js\";\nimport type { CaptureErrorContext } from \"./internal/types.js\";\n\nexport type { CaptureErrorContext } from \"./internal/types.js\";\n\nconst TRACER_NAME = \"@interfere/node\";\nconst ERROR_SPAN_NAME = \"interfere.error.captured\";\nconst SESSION_HEADER = \"x-interfere-session\";\n\nconst DEFAULT_ERROR_MECHANISM: ErrorMechanism = {\n type: MECHANISM_TYPE.node.captureError,\n handled: true,\n};\n\n/**\n * Reports an error to Interfere. Safe to call from anywhere — no-op when the\n * SDK is not configured (`INTERFERE_API_KEY` unset) or when the `errors`\n * plugin is disabled by remote config.\n *\n * Internally opens a short OTel span and records the error as an `exception`\n * event on it. The span ships through the same `OTLPTraceExporter` wired up\n * in `init()`, so error capture inherits the OTel transport (batch, retry,\n * traceparent propagation) without a separate fetch path. The collector's\n * `mapTracesToSpanEvents` lifts the OTel-semconv `exception.*` attrs plus\n * our `interfere.exception.*` taxonomy into the `span_events` datasource.\n *\n * Pass the request (a `Request`, a Node `IncomingMessage`-like object, or\n * any object with `headers`/`method`/`url`) so the span carries request\n * context. If there's an active OTel context (e.g. an HTTP handler span),\n * the exception attaches to it as a child; otherwise it becomes a\n * root span.\n */\nexport function captureError(\n error: unknown,\n request?: unknown,\n errorContext?: CaptureErrorContext\n): void {\n if (!isEnabledInEnvironment()) {\n return;\n }\n\n if (!isPluginEnabled(\"errors\")) {\n return;\n }\n\n if (isErrorCaptured(error)) {\n return;\n }\n markErrorCaptured(error);\n\n const err = toError(error);\n const normalizedRequest = normalizeRequest(request);\n const mechanism = errorContext?.mechanism ?? DEFAULT_ERROR_MECHANISM;\n\n const tracer = trace.getTracer(TRACER_NAME);\n const span = tracer.startSpan(\n ERROR_SPAN_NAME,\n { kind: SpanKind.INTERNAL },\n context.active()\n );\n\n // Request context as span attributes (OTel HTTP semconv). Prefer an\n // explicit `errorContext.node` payload over the normalized request when\n // both are present — callers passing `node` are typically frameworks\n // (Next.js error handlers) that already have the canonical values.\n const requestMethod =\n errorContext?.node?.requestMethod ?? normalizedRequest?.method ?? null;\n if (requestMethod) {\n span.setAttribute(\"http.request.method\", requestMethod);\n }\n const requestPath =\n errorContext?.node?.requestPath ?? normalizedRequest?.path ?? null;\n if (requestPath) {\n span.setAttribute(\"url.path\", requestPath);\n }\n\n // Session header is the legacy bridge: browser/Next forwards\n // `x-interfere-session` so the dashboard can stitch server errors to a\n // user session. Lift it as a span attribute so the mapper can fold it in.\n const sessionId = normalizedRequest?.headers.get(SESSION_HEADER);\n if (sessionId) {\n span.setAttribute(\"interfere.session.id\", sessionId);\n }\n\n // Exception event — OTel semconv naming (`exception.{type,message,\n // stacktrace}`) plus our taxonomy (`interfere.exception.{mechanism,\n // handled}`, `interfere.error.digest`). `mapTracesToSpanEvents` in the\n // collector lifts both sets into typed columns on the `span_events` row.\n const eventAttrs: Record<string, string | boolean> = {\n \"exception.type\": err.name,\n \"exception.message\": err.message,\n \"interfere.exception.mechanism\": mechanism.type,\n \"interfere.exception.handled\": mechanism.handled,\n };\n if (err.stack) {\n eventAttrs[\"exception.stacktrace\"] = err.stack;\n }\n const digest = errorContext?.node?.errorDigest;\n if (digest) {\n eventAttrs[\"interfere.error.digest\"] = digest;\n }\n\n span.addEvent(\"exception\", eventAttrs);\n span.setStatus({ code: SpanStatusCode.ERROR });\n span.end();\n}\n\n/**\n * Reports a string message as an error event.\n */\nexport function captureMessage(\n message: string,\n request?: unknown,\n errorContext?: CaptureErrorContext\n): void {\n captureError(new Error(message), request, errorContext);\n}\n\n/**\n * Wraps a function in an OTel span. Thrown errors are recorded on the span\n * and rethrown. Without an active provider (i.e. before `init()`), still\n * runs `fn` but produces no telemetry.\n */\nexport async function withSpan<T>(\n name: string,\n fn: () => Promise<T> | T\n): Promise<T> {\n const tracer = trace.getTracer(TRACER_NAME);\n const span = tracer.startSpan(name);\n try {\n return await context.with(trace.setSpan(context.active(), span), fn);\n } catch (error) {\n span.recordException(error as Error);\n span.setStatus({ code: SpanStatusCode.ERROR });\n throw error;\n } finally {\n span.end();\n }\n}\n"],"mappings":";;;;;;;AAaA,MAAM,cAAc;AACpB,MAAM,kBAAkB;AACxB,MAAM,iBAAiB;AAEvB,MAAM,0BAA0C;CAC9C,MAAM,eAAe,KAAK;CAC1B,SAAS;CACV;;;;;;;;;;;;;;;;;;;AAoBD,SAAgB,aACd,OACA,SACA,cACM;CACN,IAAI,CAAC,wBAAwB,EAC3B;CAGF,IAAI,CAAC,gBAAgB,SAAS,EAC5B;CAGF,IAAI,gBAAgB,MAAM,EACxB;CAEF,kBAAkB,MAAM;CAExB,MAAM,MAAM,QAAQ,MAAM;CAC1B,MAAM,oBAAoB,iBAAiB,QAAQ;CACnD,MAAM,YAAY,cAAc,aAAa;CAG7C,MAAM,OADS,MAAM,UAAU,YACZ,CAAC,UAClB,iBACA,EAAE,MAAM,SAAS,UAAU,EAC3B,QAAQ,QAAQ,CACjB;CAMD,MAAM,gBACJ,cAAc,MAAM,iBAAiB,mBAAmB,UAAU;CACpE,IAAI,eACF,KAAK,aAAa,uBAAuB,cAAc;CAEzD,MAAM,cACJ,cAAc,MAAM,eAAe,mBAAmB,QAAQ;CAChE,IAAI,aACF,KAAK,aAAa,YAAY,YAAY;CAM5C,MAAM,YAAY,mBAAmB,QAAQ,IAAI,eAAe;CAChE,IAAI,WACF,KAAK,aAAa,wBAAwB,UAAU;CAOtD,MAAM,aAA+C;EACnD,kBAAkB,IAAI;EACtB,qBAAqB,IAAI;EACzB,iCAAiC,UAAU;EAC3C,+BAA+B,UAAU;EAC1C;CACD,IAAI,IAAI,OACN,WAAW,0BAA0B,IAAI;CAE3C,MAAM,SAAS,cAAc,MAAM;CACnC,IAAI,QACF,WAAW,4BAA4B;CAGzC,KAAK,SAAS,aAAa,WAAW;CACtC,KAAK,UAAU,EAAE,MAAM,eAAe,OAAO,CAAC;CAC9C,KAAK,KAAK;;;;;AAMZ,SAAgB,eACd,SACA,SACA,cACM;CACN,aAAa,IAAI,MAAM,QAAQ,EAAE,SAAS,aAAa;;;;;;;AAQzD,eAAsB,SACpB,MACA,IACY;CAEZ,MAAM,OADS,MAAM,UAAU,YACZ,CAAC,UAAU,KAAK;CACnC,IAAI;EACF,OAAO,MAAM,QAAQ,KAAK,MAAM,QAAQ,QAAQ,QAAQ,EAAE,KAAK,EAAE,GAAG;UAC7D,OAAO;EACd,KAAK,gBAAgB,MAAe;EACpC,KAAK,UAAU,EAAE,MAAM,eAAe,OAAO,CAAC;EAC9C,MAAM;WACE;EACR,KAAK,KAAK"}
|
package/dist/init.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.mts","names":[],"sources":["../src/init.ts"],"mappings":";UAqBiB,WAAA;EAAA;;;;;EAAA,SAMN,wBAAA;EAcA;;;;AA2BX;;EA3BW,SAPA,yBAAA;EAkCmD;;;;;;EAAA,SA3BnD,WAAA;EA4GgB;;;;AAa3B;EAb2B,SAtGhB,gBAAA;AAAA;;;;;;;;;;;;;;;;;;;iBAqBW,IAAA,CAAK,OAAA,GAAS,WAAA,GAAmB,OAAA;;;;;;iBAiFjC,KAAA,CAAA,GAAS,OAAA;;;;;iBAaT,KAAA,CAAA,GAAS,OAAA"}
|
package/dist/init.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"init.mjs","names":[],"sources":["../src/init.ts"],"sourcesContent":["import { API_PATHS } from \"@interfere/constants/api\";\n\nimport { AsyncLocalStorageContextManager } from \"@opentelemetry/context-async-hooks\";\nimport { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\";\nimport { resourceFromAttributes } from \"@opentelemetry/resources\";\nimport { BatchSpanProcessor } from \"@opentelemetry/sdk-trace-base\";\nimport { NodeTracerProvider } from \"@opentelemetry/sdk-trace-node\";\n\nimport { readInterfereEnv } from \"./internal/env.js\";\nimport {\n installGlobalHandlers,\n uninstallGlobalHandlers,\n} from \"./internal/global-handlers.js\";\nimport { resolveReleaseSlug } from \"./internal/release-slug.js\";\nimport { fetchAndCacheRemoteConfig } from \"./internal/remote-config.js\";\nimport { PRODUCER_VERSION } from \"./internal/version.js\";\n\nconst DEFAULT_SERVICE_NAME = \"node-app\";\n\nlet provider: NodeTracerProvider | null = null;\n\nexport interface InitOptions {\n /**\n * Install a `process.on(\"uncaughtException\")` listener that captures the\n * error, flushes spans, and exits the process. Default `true`. Disable to\n * use your own handler.\n */\n readonly captureUncaughtException?: boolean;\n /**\n * Install a `process.on(\"unhandledRejection\")` listener that captures the\n * rejection and flushes spans. Default `true`. Disable to use your own\n * handler. Whether the process exits is governed by Node's\n * `--unhandled-rejections` flag, not the SDK.\n */\n readonly captureUnhandledRejection?: boolean;\n /**\n * Sets the OTel `service.name` resource attribute — the identity spans\n * are grouped by in the dashboard. Defaults to `OTEL_SERVICE_NAME` env\n * var, then `\"node-app\"`. Use this to distinguish workers, crons, and\n * other processes that share an `INTERFERE_API_KEY`.\n */\n readonly serviceName?: string;\n /**\n * Sets the OTel `service.namespace` resource attribute. Optional; useful\n * for grouping multiple services under one logical product surface\n * (e.g. `\"checkout\"` across an API and worker fleet).\n */\n readonly serviceNamespace?: string;\n}\n\n/**\n * Bootstraps the Interfere Node SDK: wires a private `NodeTracerProvider`\n * whose spans export to the Interfere collector via OTLP/HTTP, registers an\n * `AsyncLocalStorageContextManager` so user spans propagate across async\n * boundaries, and fetches the remote-config gate.\n *\n * Idempotent — safe to call from multiple entry points; only the first call\n * wires up.\n *\n * Bails silently when `INTERFERE_API_KEY` is unset so dev runs without the\n * SDK configured don't crash.\n *\n * The `release.slug` resource attribute is derived deterministically from\n * the commit SHA (auto-detected from `INTERFERE_SOURCE_ID`,\n * `VERCEL_GIT_COMMIT_SHA`, `GITHUB_SHA`, or `git rev-parse HEAD`) plus the\n * API key — same algorithm used by `@interfere/next` and the browser SDKs,\n * so spans from cohabiting frontends agree on release identity.\n */\nexport async function init(options: InitOptions = {}): Promise<void> {\n if (provider) {\n return;\n }\n\n const env = readInterfereEnv();\n if (!env.apiKey) {\n return;\n }\n\n // Force-enable swaps in the seeded `rel_local` slug so dev runs pass the\n // collector's preflight gate without uploading source maps. Prod\n // collectors drop force-enabled batches with a 2xx, so this header is\n // safe to ship in dev images that escape to prod by mistake. Same\n // pattern as `@interfere/next/config.ts` for `next dev`.\n const releaseSlug = env.forceEnable\n ? \"rel_0000000000000000\"\n : resolveReleaseSlug().slug;\n\n if (!releaseSlug) {\n console.warn(\n \"[interfere] No commit SHA available; server spans will ship without `release.slug`. Set `INTERFERE_SOURCE_ID` (or any of `VERCEL_GIT_COMMIT_SHA`, `GITHUB_SHA`) on the runtime env.\"\n );\n }\n\n const serviceName =\n options.serviceName ??\n process.env[\"OTEL_SERVICE_NAME\"] ??\n DEFAULT_SERVICE_NAME;\n\n const resource = resourceFromAttributes({\n \"service.name\": serviceName,\n ...(options.serviceNamespace\n ? { \"service.namespace\": options.serviceNamespace }\n : {}),\n \"deployment.environment.name\": env.nodeEnvironment ?? \"unknown\",\n \"telemetry.sdk.language\": \"nodejs\",\n \"interfere.sdk.name\": \"@interfere/node\",\n \"interfere.sdk.version\": PRODUCER_VERSION,\n ...(releaseSlug ? { \"release.slug\": releaseSlug } : {}),\n });\n\n const exporter = new OTLPTraceExporter({\n // Single opaque sink endpoint shared with browser/Next exporters; see\n // `@interfere/react/internal/otel/exporter` for the rationale.\n url: `${env.apiUrl}${API_PATHS.SINK}`,\n headers: {\n // The collector's bot filter (`v2/index.ts`) silently 202-drops\n // requests whose UA matches `isBot()`. Bun/Node's default UA gets\n // flagged, so SDKs self-identify; the bypass regex\n // `^@interfere/[a-z][a-z-]*@` lives in `collector/lib/bot-filter.ts`.\n \"user-agent\": PRODUCER_VERSION,\n \"x-api-key\": env.apiKey,\n \"x-interfere-producer-version\": PRODUCER_VERSION,\n ...(env.forceEnable ? { \"x-interfere-force-enable\": \"1\" } : {}),\n },\n });\n\n provider = new NodeTracerProvider({\n resource,\n spanProcessors: [new BatchSpanProcessor(exporter)],\n });\n\n provider.register({\n contextManager: new AsyncLocalStorageContextManager().enable(),\n });\n\n installGlobalHandlers({\n captureUncaughtException: options.captureUncaughtException ?? true,\n captureUnhandledRejection: options.captureUnhandledRejection ?? true,\n flush,\n });\n\n await fetchAndCacheRemoteConfig();\n}\n\n/**\n * Flushes any buffered spans and shuts down the tracer provider. Call from\n * a server shutdown hook (`SIGTERM`, `process.on(\"beforeExit\")`, framework\n * lifecycle close) so in-flight error spans aren't lost on exit.\n */\nexport async function close(): Promise<void> {\n if (!provider) {\n return;\n }\n uninstallGlobalHandlers();\n await provider.shutdown();\n provider = null;\n}\n\n/**\n * Forces buffered spans to be exported immediately. Useful in serverless\n * runtimes where the process is suspended between requests.\n */\nexport async function flush(): Promise<void> {\n if (!provider) {\n return;\n }\n await provider.forceFlush();\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,MAAM,uBAAuB;AAE7B,IAAI,WAAsC;;;;;;;;;;;;;;;;;;;AAiD1C,eAAsB,KAAK,UAAuB,EAAE,EAAiB;CACnE,IAAI,UACF;CAGF,MAAM,MAAM,kBAAkB;CAC9B,IAAI,CAAC,IAAI,QACP;CAQF,MAAM,cAAc,IAAI,cACpB,yBACA,oBAAoB,CAAC;CAEzB,IAAI,CAAC,aACH,QAAQ,KACN,sLACD;CAoCH,WAAW,IAAI,mBAAmB;EAChC,UA7Be,uBAAuB;GACtC,gBALA,QAAQ,eACR,QAAQ,IAAI,wBACZ;GAIA,GAAI,QAAQ,mBACR,EAAE,qBAAqB,QAAQ,kBAAkB,GACjD,EAAE;GACN,+BAA+B,IAAI,mBAAmB;GACtD,0BAA0B;GAC1B,sBAAsB;GACtB,yBAAyB;GACzB,GAAI,cAAc,EAAE,gBAAgB,aAAa,GAAG,EAAE;GACvD,CAmBS;EACR,gBAAgB,CAAC,IAAI,mBAAmB,IAlBrB,kBAAkB;GAGrC,KAAK,GAAG,IAAI,SAAS,UAAU;GAC/B,SAAS;IAKP,cAAc;IACd,aAAa,IAAI;IACjB,gCAAgC;IAChC,GAAI,IAAI,cAAc,EAAE,4BAA4B,KAAK,GAAG,EAAE;IAC/D;GACF,CAIiD,CAAC,CAAC;EACnD,CAAC;CAEF,SAAS,SAAS,EAChB,gBAAgB,IAAI,iCAAiC,CAAC,QAAQ,EAC/D,CAAC;CAEF,sBAAsB;EACpB,0BAA0B,QAAQ,4BAA4B;EAC9D,2BAA2B,QAAQ,6BAA6B;EAChE;EACD,CAAC;CAEF,MAAM,2BAA2B;;;;;;;AAQnC,eAAsB,QAAuB;CAC3C,IAAI,CAAC,UACH;CAEF,yBAAyB;CACzB,MAAM,SAAS,UAAU;CACzB,WAAW;;;;;;AAOb,eAAsB,QAAuB;CAC3C,IAAI,CAAC,UACH;CAEF,MAAM,SAAS,YAAY"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dedupe.d.mts","names":[],"sources":["../../src/internal/dedupe.ts"],"mappings":";iBAEgB,eAAA,CAAgB,KAAA;AAAA,iBAIhB,iBAAA,CAAkB,KAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dedupe.mjs","names":[],"sources":["../../src/internal/dedupe.ts"],"sourcesContent":["const seenErrors = new WeakSet<Error>();\n\nexport function isErrorCaptured(error: unknown): boolean {\n return error instanceof Error && seenErrors.has(error);\n}\n\nexport function markErrorCaptured(error: unknown): void {\n if (!(error instanceof Error)) {\n return;\n }\n\n seenErrors.add(error);\n}\n"],"mappings":";AAAA,MAAM,6BAAa,IAAI,SAAgB;AAEvC,SAAgB,gBAAgB,OAAyB;CACvD,OAAO,iBAAiB,SAAS,WAAW,IAAI,MAAM;;AAGxD,SAAgB,kBAAkB,OAAsB;CACtD,IAAI,EAAE,iBAAiB,QACrB;CAGF,WAAW,IAAI,MAAM"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.mts","names":[],"sources":["../../src/internal/env.ts"],"mappings":";;;UAKiB,YAAA;EAAA,SACN,MAAA;EAAA,SACA,MAAA;EAAA,SACA,WAAA;EAAA,SACA,eAAA,EAAiB,OAAA,CAAQ,GAAA;AAAA;;;;;;;;iBAUpB,sBAAA,CAAA;AAAA,iBAIA,gBAAA,CAAA,GAAoB,YAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"env.mjs","names":[],"sources":["../../src/internal/env.ts"],"sourcesContent":["import { API_URL } from \"@interfere/constants/api\";\nimport { parseEnvValue } from \"@interfere/types/sdk/env\";\nimport type { Env } from \"@interfere/types/sdk/runtime\";\nimport { normalizeEnv } from \"@interfere/types/sdk/runtime\";\n\nexport interface InterfereEnv {\n readonly apiKey: string | null;\n readonly apiUrl: string;\n readonly forceEnable: boolean;\n readonly nodeEnvironment: Exclude<Env, null>;\n}\n\n/**\n * Server SDKs always run end-to-end. There's no `NEXT_PUBLIC_*` force-enable\n * escape hatch here — if `INTERFERE_API_KEY` is set, the SDK is on. The Next\n * SDK guards on `NODE_ENV=production || NEXT_PUBLIC_INTERFERE_FORCE_ENABLE`\n * because `next dev` runs the SDK during development without a real release\n * slug; that asymmetry doesn't apply to plain Node servers.\n */\nexport function isEnabledInEnvironment(): boolean {\n return true;\n}\n\nexport function readInterfereEnv(): InterfereEnv {\n const nodeEnvironment = normalizeEnv(process.env[\"NODE_ENV\"]) ?? \"production\";\n\n return {\n apiKey: parseEnvValue(process.env[\"INTERFERE_API_KEY\"]),\n apiUrl: parseEnvValue(process.env[\"INTERFERE_API_URL\"]) ?? API_URL,\n forceEnable: process.env[\"INTERFERE_FORCE_ENABLE\"] === \"1\",\n nodeEnvironment,\n };\n}\n"],"mappings":";;;;;;;;;;;AAmBA,SAAgB,yBAAkC;CAChD,OAAO;;AAGT,SAAgB,mBAAiC;CAC/C,MAAM,kBAAkB,aAAa,QAAQ,IAAI,YAAY,IAAI;CAEjE,OAAO;EACL,QAAQ,cAAc,QAAQ,IAAI,qBAAqB;EACvD,QAAQ,cAAc,QAAQ,IAAI,qBAAqB,IAAI;EAC3D,aAAa,QAAQ,IAAI,8BAA8B;EACvD;EACD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"global-handlers.d.mts","names":[],"sources":["../../src/internal/global-handlers.ts"],"mappings":";;AAyBA;;;;;;;;;;;AAoBA;;;;iBApBgB,qBAAA,CAAsB,OAAA;EAAA,SAC3B,wBAAA;EAAA,SACA,yBAAA;EAAA,SACA,KAAA,QAAa,OAAA;AAAA;AAAA,iBAiBR,uBAAA,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"global-handlers.mjs","names":[],"sources":["../../src/internal/global-handlers.ts"],"sourcesContent":["import { MECHANISM_TYPE } from \"@interfere/types/sdk/errors\";\n\nimport { captureError } from \"../capture.js\";\n\nconst FLUSH_TIMEOUT_MS = 2000;\n\nlet uncaughtHandler: NodeJS.UncaughtExceptionListener | null = null;\nlet unhandledHandler: NodeJS.UnhandledRejectionListener | null = null;\n\n/**\n * Installs `process.on(\"uncaughtException\")` and\n * `process.on(\"unhandledRejection\")` listeners that report to Interfere.\n *\n * Uncaught exceptions: captured, spans flushed (best-effort, 2s timeout),\n * then `process.exit(1)`. Once an `uncaughtException` listener is\n * registered, Node will not exit on its own — leaving the process alive\n * after an uncaught error usually means a corrupted state, so we own the\n * exit. Customers who need different exit semantics should opt out and\n * register their own handler.\n *\n * Unhandled rejections: captured and flushed, but the SDK does not call\n * `process.exit`. Whether unhandled rejections are fatal is governed by\n * Node's `--unhandled-rejections` flag (default `throw` since v15) and we\n * don't override that.\n */\nexport function installGlobalHandlers(options: {\n readonly captureUncaughtException: boolean;\n readonly captureUnhandledRejection: boolean;\n readonly flush: () => Promise<void>;\n}): void {\n if (options.captureUncaughtException && uncaughtHandler === null) {\n uncaughtHandler = (error) => {\n handleUncaughtException(error, options.flush).catch(() => undefined);\n };\n process.on(\"uncaughtException\", uncaughtHandler);\n }\n\n if (options.captureUnhandledRejection && unhandledHandler === null) {\n unhandledHandler = (reason) => {\n handleUnhandledRejection(reason, options.flush).catch(() => undefined);\n };\n process.on(\"unhandledRejection\", unhandledHandler);\n }\n}\n\nexport function uninstallGlobalHandlers(): void {\n if (uncaughtHandler !== null) {\n process.off(\"uncaughtException\", uncaughtHandler);\n uncaughtHandler = null;\n }\n if (unhandledHandler !== null) {\n process.off(\"unhandledRejection\", unhandledHandler);\n unhandledHandler = null;\n }\n}\n\nasync function handleUncaughtException(\n error: Error,\n flush: () => Promise<void>\n): Promise<void> {\n await captureError(error, undefined, {\n mechanism: {\n type: MECHANISM_TYPE.node.uncaughtException,\n handled: false,\n },\n });\n await flushWithTimeout(flush);\n process.exit(1);\n}\n\nasync function handleUnhandledRejection(\n reason: unknown,\n flush: () => Promise<void>\n): Promise<void> {\n await captureError(reason, undefined, {\n mechanism: {\n type: MECHANISM_TYPE.node.unhandledRejection,\n handled: false,\n },\n });\n await flushWithTimeout(flush);\n}\n\nfunction flushWithTimeout(flush: () => Promise<void>): Promise<void> {\n return Promise.race([\n flush(),\n new Promise<void>((resolve) => {\n setTimeout(resolve, FLUSH_TIMEOUT_MS);\n }),\n ]);\n}\n"],"mappings":";;;AAIA,MAAM,mBAAmB;AAEzB,IAAI,kBAA2D;AAC/D,IAAI,mBAA6D;;;;;;;;;;;;;;;;;AAkBjE,SAAgB,sBAAsB,SAI7B;CACP,IAAI,QAAQ,4BAA4B,oBAAoB,MAAM;EAChE,mBAAmB,UAAU;GAC3B,wBAAwB,OAAO,QAAQ,MAAM,CAAC,YAAY,KAAA,EAAU;;EAEtE,QAAQ,GAAG,qBAAqB,gBAAgB;;CAGlD,IAAI,QAAQ,6BAA6B,qBAAqB,MAAM;EAClE,oBAAoB,WAAW;GAC7B,yBAAyB,QAAQ,QAAQ,MAAM,CAAC,YAAY,KAAA,EAAU;;EAExE,QAAQ,GAAG,sBAAsB,iBAAiB;;;AAItD,SAAgB,0BAAgC;CAC9C,IAAI,oBAAoB,MAAM;EAC5B,QAAQ,IAAI,qBAAqB,gBAAgB;EACjD,kBAAkB;;CAEpB,IAAI,qBAAqB,MAAM;EAC7B,QAAQ,IAAI,sBAAsB,iBAAiB;EACnD,mBAAmB;;;AAIvB,eAAe,wBACb,OACA,OACe;CACf,MAAM,aAAa,OAAO,KAAA,GAAW,EACnC,WAAW;EACT,MAAM,eAAe,KAAK;EAC1B,SAAS;EACV,EACF,CAAC;CACF,MAAM,iBAAiB,MAAM;CAC7B,QAAQ,KAAK,EAAE;;AAGjB,eAAe,yBACb,QACA,OACe;CACf,MAAM,aAAa,QAAQ,KAAA,GAAW,EACpC,WAAW;EACT,MAAM,eAAe,KAAK;EAC1B,SAAS;EACV,EACF,CAAC;CACF,MAAM,iBAAiB,MAAM;;AAG/B,SAAS,iBAAiB,OAA2C;CACnE,OAAO,QAAQ,KAAK,CAClB,OAAO,EACP,IAAI,SAAe,YAAY;EAC7B,WAAW,SAAS,iBAAiB;GACrC,CACH,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"normalize-request.d.mts","names":[],"sources":["../../src/internal/normalize-request.ts"],"mappings":";;;cAEa,kBAAA;;AAAb;;;;;AAUA;;iBAAgB,gBAAA,CAAiB,OAAA,YAAmB,iBAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"normalize-request.mjs","names":[],"sources":["../../src/internal/normalize-request.ts"],"sourcesContent":["import type { NormalizedRequest } from \"./types.js\";\n\nexport const TRACEPARENT_HEADER = \"traceparent\";\n\n/**\n * Coerces a request-shaped value into the headers/method/path triple used\n * by envelope construction. Handles WHATWG `Request`, Node\n * `IncomingMessage`-like objects, and plain object literals.\n *\n * Returns `null` only for non-objects; otherwise produces a best-effort\n * `NormalizedRequest` with sensible defaults.\n */\nexport function normalizeRequest(request: unknown): NormalizedRequest | null {\n if (request instanceof Request) {\n return {\n method: normalizeMethod(request.method),\n path: normalizePath(request.url),\n headers: request.headers,\n };\n }\n\n if (typeof request !== \"object\" || request === null) {\n return null;\n }\n\n const value = request as {\n headers?: unknown;\n method?: unknown;\n path?: unknown;\n url?: unknown;\n };\n\n return {\n method: normalizeMethod(value.method),\n path: normalizePath(\n typeof value.path === \"string\" ? value.path : toStringOrDefault(value.url)\n ),\n headers: normalizeHeaders(value.headers),\n };\n}\n\nfunction normalizeMethod(value: unknown): string {\n if (typeof value !== \"string\") {\n return \"GET\";\n }\n\n const method = value.trim();\n return method.length > 0 ? method.toUpperCase() : \"GET\";\n}\n\nfunction normalizePath(value: string): string {\n if (value.length === 0) {\n return \"/\";\n }\n\n try {\n const parsed = new URL(value, \"http://localhost\");\n return parsed.pathname || \"/\";\n } catch {\n return value.startsWith(\"/\") ? value : `/${value}`;\n }\n}\n\nfunction toStringOrDefault(value: unknown, fallback = \"/\"): string {\n if (typeof value !== \"string\") {\n return fallback;\n }\n\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : fallback;\n}\n\nfunction normalizeHeaders(value: unknown): Headers {\n if (value instanceof Headers) {\n return new Headers(value);\n }\n\n if (typeof value !== \"object\" || value === null) {\n return new Headers();\n }\n\n const headers = new Headers();\n for (const [key, headerValue] of Object.entries(value)) {\n if (headerValue === undefined) {\n continue;\n }\n\n if (Array.isArray(headerValue)) {\n headers.set(key, headerValue.join(\", \"));\n continue;\n }\n\n headers.set(key, String(headerValue));\n }\n\n return headers;\n}\n"],"mappings":";AAEA,MAAa,qBAAqB;;;;;;;;;AAUlC,SAAgB,iBAAiB,SAA4C;CAC3E,IAAI,mBAAmB,SACrB,OAAO;EACL,QAAQ,gBAAgB,QAAQ,OAAO;EACvC,MAAM,cAAc,QAAQ,IAAI;EAChC,SAAS,QAAQ;EAClB;CAGH,IAAI,OAAO,YAAY,YAAY,YAAY,MAC7C,OAAO;CAGT,MAAM,QAAQ;CAOd,OAAO;EACL,QAAQ,gBAAgB,MAAM,OAAO;EACrC,MAAM,cACJ,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,kBAAkB,MAAM,IAAI,CAC3E;EACD,SAAS,iBAAiB,MAAM,QAAQ;EACzC;;AAGH,SAAS,gBAAgB,OAAwB;CAC/C,IAAI,OAAO,UAAU,UACnB,OAAO;CAGT,MAAM,SAAS,MAAM,MAAM;CAC3B,OAAO,OAAO,SAAS,IAAI,OAAO,aAAa,GAAG;;AAGpD,SAAS,cAAc,OAAuB;CAC5C,IAAI,MAAM,WAAW,GACnB,OAAO;CAGT,IAAI;EAEF,OAAO,IADY,IAAI,OAAO,mBACjB,CAAC,YAAY;SACpB;EACN,OAAO,MAAM,WAAW,IAAI,GAAG,QAAQ,IAAI;;;AAI/C,SAAS,kBAAkB,OAAgB,WAAW,KAAa;CACjE,IAAI,OAAO,UAAU,UACnB,OAAO;CAGT,MAAM,UAAU,MAAM,MAAM;CAC5B,OAAO,QAAQ,SAAS,IAAI,UAAU;;AAGxC,SAAS,iBAAiB,OAAyB;CACjD,IAAI,iBAAiB,SACnB,OAAO,IAAI,QAAQ,MAAM;CAG3B,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC,OAAO,IAAI,SAAS;CAGtB,MAAM,UAAU,IAAI,SAAS;CAC7B,KAAK,MAAM,CAAC,KAAK,gBAAgB,OAAO,QAAQ,MAAM,EAAE;EACtD,IAAI,gBAAgB,KAAA,GAClB;EAGF,IAAI,MAAM,QAAQ,YAAY,EAAE;GAC9B,QAAQ,IAAI,KAAK,YAAY,KAAK,KAAK,CAAC;GACxC;;EAGF,QAAQ,IAAI,KAAK,OAAO,YAAY,CAAC;;CAGvC,OAAO"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"release-slug.d.mts","names":[],"sources":["../../src/internal/release-slug.ts"],"mappings":";iBAkBgB,gBAAA,CAAA;AAAA,iBAOA,kBAAA,CAAA;EACd,SAAA;EACA,IAAA;AAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"release-slug.mjs","names":[],"sources":["../../src/internal/release-slug.ts"],"sourcesContent":["import { releaseSourceIdEnvKeys } from \"@interfere/types/integrations\";\nimport { deriveReleaseSlug } from \"@interfere/types/releases/slug\";\nimport { readFirstEnvValue } from \"@interfere/types/sdk/env\";\n\nimport { execSync } from \"node:child_process\";\n\nfunction runGitCommand(command: string): string | null {\n try {\n const output = execSync(command, {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n return output.length > 0 ? output : null;\n } catch {\n return null;\n }\n}\n\nexport function resolveCommitSha(): string | null {\n return (\n readFirstEnvValue(process.env, releaseSourceIdEnvKeys) ??\n runGitCommand(\"git rev-parse HEAD\")\n );\n}\n\nexport function resolveReleaseSlug(): {\n commitSha: string | null;\n slug: string | null;\n} {\n const commitSha = resolveCommitSha();\n return {\n commitSha,\n slug: commitSha ? deriveReleaseSlug(commitSha) : null,\n };\n}\n"],"mappings":";;;;;AAMA,SAAS,cAAc,SAAgC;CACrD,IAAI;EACF,MAAM,SAAS,SAAS,SAAS;GAC/B,UAAU;GACV,OAAO;IAAC;IAAU;IAAQ;IAAS;GACpC,CAAC,CAAC,MAAM;EACT,OAAO,OAAO,SAAS,IAAI,SAAS;SAC9B;EACN,OAAO;;;AAIX,SAAgB,mBAAkC;CAChD,OACE,kBAAkB,QAAQ,KAAK,uBAAuB,IACtD,cAAc,qBAAqB;;AAIvC,SAAgB,qBAGd;CACA,MAAM,YAAY,kBAAkB;CACpC,OAAO;EACL;EACA,MAAM,YAAY,kBAAkB,UAAU,GAAG;EAClD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"remote-config.d.mts","names":[],"sources":["../../src/internal/remote-config.ts"],"mappings":";iBAUsB,yBAAA,CAAA,GAA6B,OAAA;AAAA,iBAkCnC,eAAA,CAAgB,MAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"remote-config.mjs","names":[],"sources":["../../src/internal/remote-config.ts"],"sourcesContent":["import { API_PATHS } from \"@interfere/constants/api\";\nimport type {\n RemoteConfig,\n RemotePluginConfig,\n} from \"@interfere/types/sdk/remote-config\";\n\nimport { isEnabledInEnvironment, readInterfereEnv } from \"./env.js\";\n\nlet cachedConfig: RemotePluginConfig | null = null;\n\nexport async function fetchAndCacheRemoteConfig(): Promise<void> {\n if (!isEnabledInEnvironment()) {\n return;\n }\n\n const env = readInterfereEnv();\n if (env.apiKey === null) {\n return;\n }\n\n try {\n const url = `${env.apiUrl}${API_PATHS.CONFIG}`;\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-api-key\": env.apiKey,\n },\n signal: AbortSignal.timeout(10_000),\n });\n\n if (!response.ok) {\n return;\n }\n\n const config = (await response.json()) as RemoteConfig;\n if (config?.plugins) {\n cachedConfig = config.plugins;\n }\n } catch {\n // Fail silently — all plugins remain enabled\n }\n}\n\nexport function isPluginEnabled(plugin: string): boolean {\n if (!cachedConfig) {\n return true;\n }\n return cachedConfig[plugin as keyof RemotePluginConfig] !== false;\n}\n"],"mappings":";;;AAQA,IAAI,eAA0C;AAE9C,eAAsB,4BAA2C;CAC/D,IAAI,CAAC,wBAAwB,EAC3B;CAGF,MAAM,MAAM,kBAAkB;CAC9B,IAAI,IAAI,WAAW,MACjB;CAGF,IAAI;EACF,MAAM,MAAM,GAAG,IAAI,SAAS,UAAU;EACtC,MAAM,WAAW,MAAM,MAAM,KAAK;GAChC,QAAQ;GACR,SAAS;IACP,gBAAgB;IAChB,aAAa,IAAI;IAClB;GACD,QAAQ,YAAY,QAAQ,IAAO;GACpC,CAAC;EAEF,IAAI,CAAC,SAAS,IACZ;EAGF,MAAM,SAAU,MAAM,SAAS,MAAM;EACrC,IAAI,QAAQ,SACV,eAAe,OAAO;SAElB;;AAKV,SAAgB,gBAAgB,QAAyB;CACvD,IAAI,CAAC,cACH,OAAO;CAET,OAAO,aAAa,YAAwC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","names":[],"sources":["../../src/internal/types.ts"],"mappings":";;;;UAGiB,mBAAA;EAAA,SACN,SAAA,GAAY,cAAA;EAAA,SACZ,IAAA,GAAO,IAAA,CAAK,WAAA;EAAA,SACZ,WAAA;AAAA;AAAA,UAGM,iBAAA;EAAA,SACN,OAAA,EAAS,OAAA;EAAA,SACT,MAAA;EAAA,SACA,IAAA;AAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"version.d.mts","names":[],"sources":["../../src/internal/version.ts"],"mappings":";cAEa,gBAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"version.mjs","names":["pkg.name","pkg.version"],"sources":["../../src/internal/version.ts"],"sourcesContent":["import pkg from \"../../package.json\" with { type: \"json\" };\n\nexport const PRODUCER_VERSION = `${pkg.name}@${pkg.version}`;\n"],"mappings":";;AAEA,MAAa,mBAAmB,GAAGA,KAAS,GAAGC"}
|
package/dist/package.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"package.mjs","names":[],"sources":["../package.json"],"sourcesContent":[""],"mappings":""}
|