@interfere/server-core 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/global-handlers.d.mts +10 -0
- package/dist/global-handlers.mjs +1 -0
- package/dist/node.mjs +1 -1
- package/dist/ownership.mjs +1 -1
- package/dist/process-listeners.d.mts +6 -0
- package/dist/process-listeners.mjs +1 -0
- package/dist/runtime.d.mts +5 -2
- package/dist/runtime.mjs +1 -1
- package/dist/sampler.d.mts +12 -0
- package/dist/sampler.mjs +1 -0
- package/dist/shutdown.d.mts +5 -0
- package/dist/shutdown.mjs +1 -0
- package/package.json +14 -4
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ErrorMechanism } from "@interfere/types/sdk/plugins/payload/errors";
|
|
2
|
+
export interface GlobalHandlerOptions {
|
|
3
|
+
readonly captureUncaughtException: boolean;
|
|
4
|
+
readonly captureUnhandledRejection: boolean;
|
|
5
|
+
readonly capture: (error: unknown, mechanism: ErrorMechanism) => void;
|
|
6
|
+
readonly flush: () => Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
/** Captures process-level errors and flushes on shutdown, replacing listeners from an earlier install; after an uncaught exception it exits only when no other listener keeps the process alive. */
|
|
9
|
+
export declare function installGlobalHandlers(options: GlobalHandlerOptions): void;
|
|
10
|
+
export declare function uninstallGlobalHandlers(): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{contain}from"./contain.mjs";import{isInterfereListener,removeInterfereListeners,tagListener}from"./process-listeners.mjs";import{installShutdownFlush,settleFlush,uninstallShutdownFlush}from"./shutdown.mjs";import{MECHANISM_TYPE}from"@interfere/types/sdk/errors";let exiting=!1;async function handleUncaughtException(error,options){if(exiting)return;let fatal=process.listeners(`uncaughtException`).every(listener=>isInterfereListener(listener));exiting=fatal,contain(`captureError()`,()=>{options.capture(error,{handled:!1,type:MECHANISM_TYPE.node.uncaughtException})}),await settleFlush(options.flush),fatal&&process.exit(1)}async function handleUnhandledRejection(reason,options){contain(`captureError()`,()=>{options.capture(reason,{handled:!1,type:MECHANISM_TYPE.node.unhandledRejection})}),await settleFlush(options.flush)}function installGlobalHandlers(options){uninstallGlobalHandlers(),options.captureUncaughtException&&process.on(`uncaughtException`,tagListener(error=>{handleUncaughtException(error,options)})),options.captureUnhandledRejection&&process.on(`unhandledRejection`,tagListener(reason=>{handleUnhandledRejection(reason,options)})),installShutdownFlush(options.flush)}function uninstallGlobalHandlers(){removeInterfereListeners(`uncaughtException`),removeInterfereListeners(`unhandledRejection`),exiting=!1,uninstallShutdownFlush()}export{installGlobalHandlers,uninstallGlobalHandlers};
|
package/dist/node.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{bridgeConsoleToOtel}from"./console-bridge.mjs";import{containSpanProcessor}from"./contained-span-processor.mjs";import{EXISTING_PROPAGATOR_WARNING,EXISTING_PROVIDER_WARNING,registeredLoggerProvider,registeredTracerProvider,resolveTracingMode}from"./ownership.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{context,propagation,trace}from"@opentelemetry/api";import{SeverityNumber,logs}from"@opentelemetry/api-logs";import{drainUrl}from"@interfere/constants/api";import{correlationBaggageSpanProcessor}from"@interfere/types/sdk/correlation-span-processor";import{VERCEL_EXPORT_TIMEOUT_MS}from"@interfere/types/sdk/server-tracing";import{createUrlRedactionSpanProcessor,resolveUrlRedaction}from"@interfere/types/sdk/url-redaction";import{AsyncLocalStorageContextManager}from"@opentelemetry/context-async-hooks";import{getStringFromEnv}from"@opentelemetry/core";import{OTLPTraceExporter}from"@opentelemetry/exporter-trace-otlp-http";import{OTLPLogExporter}from"@opentelemetry/exporter-logs-otlp-http";import{CompressionAlgorithm}from"@opentelemetry/otlp-exporter-base";import{resourceFromAttributes}from"@opentelemetry/resources";import{BatchLogRecordProcessor,LoggerProvider as LoggerProvider$1}from"@opentelemetry/sdk-logs";import{AlwaysOnSampler,BasicTracerProvider,BatchSpanProcessor
|
|
1
|
+
import{bridgeConsoleToOtel}from"./console-bridge.mjs";import{containSpanProcessor}from"./contained-span-processor.mjs";import{EXISTING_PROPAGATOR_WARNING,EXISTING_PROVIDER_WARNING,registeredLoggerProvider,registeredTracerProvider,resolveTracingMode}from"./ownership.mjs";import{ATTRIBUTE_VALUE_LENGTH_LIMIT}from"./runtime.mjs";import{exceptionSampler}from"./sampler.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{context,propagation,trace}from"@opentelemetry/api";import{SeverityNumber,logs}from"@opentelemetry/api-logs";import{drainUrl}from"@interfere/constants/api";import{correlationBaggageSpanProcessor}from"@interfere/types/sdk/correlation-span-processor";import{VERCEL_EXPORT_TIMEOUT_MS}from"@interfere/types/sdk/server-tracing";import{createUrlRedactionSpanProcessor,resolveUrlRedaction}from"@interfere/types/sdk/url-redaction";import{AsyncLocalStorageContextManager}from"@opentelemetry/context-async-hooks";import{getStringFromEnv}from"@opentelemetry/core";import{OTLPTraceExporter}from"@opentelemetry/exporter-trace-otlp-http";import{OTLPLogExporter}from"@opentelemetry/exporter-logs-otlp-http";import{CompressionAlgorithm}from"@opentelemetry/otlp-exporter-base";import{resourceFromAttributes}from"@opentelemetry/resources";import{BatchLogRecordProcessor,LoggerProvider as LoggerProvider$1}from"@opentelemetry/sdk-logs";import{AlwaysOnSampler,BasicTracerProvider,BatchSpanProcessor}from"@opentelemetry/sdk-trace-base";import{NodeTracerProvider}from"@opentelemetry/sdk-trace-node";function canFlush(provider){return`forceFlush`in provider&&typeof provider.forceFlush==`function`}async function startNodeRuntime(input){let{options,resourceAttributes}=input,mode=resolveTracingMode(options.tracing),existing=mode===`managed`?registeredTracerProvider():null,ownsTracing=mode===`managed`&&existing===null,drain=input.drain&&ownsTracing,resource=resourceFromAttributes(resourceAttributes),urlRedaction=resolveUrlRedaction(options.privacy?.urls),attemptTimeout=input.vercel?VERCEL_EXPORT_TIMEOUT_MS:65e3,batchTimeout=input.vercel?VERCEL_EXPORT_TIMEOUT_MS:7e4,flushTimeout=input.vercel?VERCEL_EXPORT_TIMEOUT_MS:75e3,headers={"x-interfere-producer-version":input.version},spanLimits=getStringFromEnv(`OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT`)??getStringFromEnv(`OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT`)?void 0:{attributeValueLengthLimit:ATTRIBUTE_VALUE_LENGTH_LIMIT},compression=options.compression===`gzip`?CompressionAlgorithm.GZIP:CompressionAlgorithm.NONE,spanProcessors=[containSpanProcessor(createUrlRedactionSpanProcessor(urlRedaction)),correlationBaggageSpanProcessor,...drain?[]:[new BatchSpanProcessor(new OTLPTraceExporter({headers,compression,timeoutMillis:attemptTimeout,url:drainUrl(input.apiUrl,`traces`,input.publicKey)}),{exportTimeoutMillis:batchTimeout})],...options._internalAdditionalSpanProcessors??[]],sampler=exceptionSampler(),provider,ownedProvider,loggerProvider,ownsPropagator=!1,contextManager,instrumentation,restoreConsole;async function shutdown(){restoreConsole?.(),await instrumentation?.shutdown(),ownedProvider&®isteredTracerProvider()===ownedProvider&&trace.disable(),ownsPropagator&&propagation.disable(),contextManager&&(context.disable(),contextManager.disable()),loggerProvider&&logs.getLoggerProvider()===loggerProvider&&logs.disable(),await Promise.all([ownedProvider?.shutdown(),loggerProvider?.shutdown()])}try{if(!ownsTracing)existing&&console.warn(EXISTING_PROVIDER_WARNING),ownedProvider=new BasicTracerProvider(omitUndefined({resource,idGenerator:input.idGenerator,sampler:new AlwaysOnSampler,spanLimits,spanProcessors})),provider=ownedProvider;else if(drain&&input.registerPlatform)provider=await input.registerPlatform({resourceAttributes,spanProcessors,sampler}),ownsPropagator=!1;else{let nodeProvider=new NodeTracerProvider(omitUndefined({resource,idGenerator:input.idGenerator,sampler,spanLimits,spanProcessors}));ownedProvider=nodeProvider,provider=nodeProvider,ownsPropagator=propagation.setGlobalPropagator(input.propagator),ownsPropagator||console.warn(EXISTING_PROPAGATOR_WARNING);let manager=new AsyncLocalStorageContextManager().enable();context.setGlobalContextManager(manager)?contextManager=manager:manager.disable(),nodeProvider.register({contextManager:null,propagator:null})}let hostLogger=!ownsTracing&&mode===`application`?registeredLoggerProvider():null,bridgeTarget=input.logDrain?logs:hostLogger??(loggerProvider=new LoggerProvider$1({resource,processors:[new BatchLogRecordProcessor({exportTimeoutMillis:batchTimeout,exporter:new OTLPLogExporter({headers,compression,timeoutMillis:attemptTimeout,url:drainUrl(input.apiUrl,`logs`,input.publicKey)})}),...options._internalAdditionalLogRecordProcessors??[]]}));if(ownsTracing&&loggerProvider&&logs.setGlobalLoggerProvider(loggerProvider),ownsTracing&&(instrumentation=input.instrument?.(provider,resource,ownsPropagator&&!drain)),!input.logDrain&&options.consoleBridge!==!1){let level=options.consoleBridge!==void 0&&options.consoleBridge!==!0?options.consoleBridge.level??`debug`:`debug`;restoreConsole=bridgeConsoleToOtel(bridgeTarget,{debug:SeverityNumber.DEBUG,info:SeverityNumber.INFO,warn:SeverityNumber.WARN,error:SeverityNumber.ERROR}[level],`${input.name}/console`)}return{mode,provider,isolated:!ownsTracing,capture:{errors:options.errors??`interfere`,recordOnHostSpan:mode===`application`,urlRedaction},flush:async()=>{await Promise.all([canFlush(provider)?provider.forceFlush({timeoutMillis:flushTimeout}):void 0,loggerProvider?.forceFlush({timeoutMillis:flushTimeout}),instrumentation?.flush()])},shutdown}}catch(error){throw await shutdown(),error}}export{startNodeRuntime};
|
package/dist/ownership.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ProxyTracerProvider,
|
|
1
|
+
import{ProxyTracerProvider,trace}from"@opentelemetry/api";import{logs}from"@opentelemetry/api-logs";const EXISTING_PROVIDER_WARNING=`[interfere] Another OpenTelemetry tracer provider is already registered (for example by Sentry or @vercel/otel), so Interfere captures server errors through its own exporter and does not own request tracing. If that provider is yours, call register({ tracing: "application" }) and export its traces to Interfere over standard OTLP.`,EXISTING_PROPAGATOR_WARNING=`[interfere] Another OpenTelemetry propagator already owns trace headers, so Interfere will not inject its own. If that propagator is yours, call register({ tracing: "application" }).`,UNREGISTERED_TRACER_PROVIDER=new ProxyTracerProvider().getDelegate();function registeredTracerProvider(){let provider=trace.getTracerProvider(),delegate=provider instanceof ProxyTracerProvider?provider.getDelegate():provider;return delegate===UNREGISTERED_TRACER_PROVIDER?null:delegate}let adoptionLogged=!1,datadogLogged=!1;function resolveTracingMode(requested=`auto`){let mode;return mode=requested===`auto`?registeredTracerProvider()===null?`managed`:`application`:requested,requested===`auto`&&mode===`application`&&!adoptionLogged&&(adoptionLogged=!0,console.warn(`[interfere] Adopted the OpenTelemetry tracer provider this application registered first (for example Sentry or dd-trace). Server errors still reach Interfere; request traces reach Interfere only if that provider exports them over OTLP. Call register({ tracing: "application" }) to confirm this setup and silence this message.`)),mode===`managed`&&`_ddtrace`in globalThis&&!datadogLogged&&(datadogLogged=!0,console.warn(`[interfere] dd-trace is also tracing this process without its OpenTelemetry bridge, so Datadog and Interfere record separate traces and dd-trace owns outbound trace headers. Set DD_TRACE_OTEL_ENABLED=true and register dd-trace's TracerProvider before Interfere to keep one trace.`)),mode}function registeredLoggerProvider(){let provider=logs.getLoggerProvider();return`_getDelegate`in provider?null:provider}export{EXISTING_PROPAGATOR_WARNING,EXISTING_PROVIDER_WARNING,registeredLoggerProvider,registeredTracerProvider,resolveTracingMode};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
type ProcessEvent = "beforeExit" | "SIGINT" | "SIGTERM" | "uncaughtException" | "unhandledRejection";
|
|
2
|
+
type ProcessListener = (...args: never) => void;
|
|
3
|
+
export declare function isInterfereListener(listener: ProcessListener): boolean;
|
|
4
|
+
export declare function tagListener<T extends ProcessListener>(listener: T): T;
|
|
5
|
+
export declare function removeInterfereListeners(event: ProcessEvent): void;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const LISTENER_TAG=Symbol.for(`interfere.server-core.process-listener`);function isInterfereListener(listener){return Object.hasOwn(listener,LISTENER_TAG)}function tagListener(listener){return Object.defineProperty(listener,LISTENER_TAG,{value:!0}),listener}function removeInterfereListeners(event){for(let listener of process.listeners(event))isInterfereListener(listener)&&process.removeListener(event,listener)}export{isInterfereListener,removeInterfereListeners,tagListener};
|
package/dist/runtime.d.mts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Attributes, TracerProvider } from "@opentelemetry/api";
|
|
2
|
-
import { UrlRedactionPolicy } from "@interfere/types/sdk/url-redaction";
|
|
3
2
|
import { NonErrorException } from "@interfere/types/sdk/errors";
|
|
4
|
-
import {
|
|
3
|
+
import { UrlRedactionPolicy } from "@interfere/types/sdk/url-redaction";
|
|
5
4
|
import { ErrorMechanism } from "@interfere/types/sdk/plugins/payload/errors";
|
|
5
|
+
import { TracingOwnership } from "@interfere/types/sdk/remote-config/tracing-ownership";
|
|
6
|
+
/** Longest span attribute or status message a managed pipeline exports. */
|
|
7
|
+
export declare const ATTRIBUTE_VALUE_LENGTH_LIMIT = 65536;
|
|
6
8
|
export interface CapturePolicy {
|
|
7
9
|
errors: "host" | "interfere";
|
|
8
10
|
recordOnHostSpan: boolean;
|
|
@@ -20,6 +22,7 @@ export interface ServerPipeline {
|
|
|
20
22
|
export declare class ServerRuntime {
|
|
21
23
|
private readonly name;
|
|
22
24
|
private pending;
|
|
25
|
+
private readonly flushes;
|
|
23
26
|
private pipeline;
|
|
24
27
|
private isolatedProvider;
|
|
25
28
|
private policy;
|
package/dist/runtime.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{contain,containAsync}from"./contain.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{ROOT_CONTEXT,SpanKind,SpanStatusCode,isSpanContextValid,trace}from"@opentelemetry/api";import{
|
|
1
|
+
import{contain,containAsync}from"./contain.mjs";import{CAPTURE_KIND_ATTRIBUTE}from"./sampler.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{ROOT_CONTEXT,SpanKind,SpanStatusCode,isSpanContextValid,trace}from"@opentelemetry/api";import{isNonErrorException,toExceptions}from"@interfere/types/sdk/errors";import{createUrlRedactionSpanProcessor,resolveUrlRedaction}from"@interfere/types/sdk/url-redaction";import{EXCEPTION_DELIVERY,EXCEPTION_DELIVERY_ATTRIBUTE}from"@interfere/types/sdk/exception-delivery";import{exceptionSpanName}from"@interfere/types/sdk/throw-site";const ATTRIBUTE_VALUE_LENGTH_LIMIT=65536;var ServerRuntime=class{name;pending=null;flushes=/* @__PURE__ */ new Set;pipeline=null;isolatedProvider=null;policy={errors:`interfere`,recordOnHostSpan:!0,urlRedaction:resolveUrlRedaction()};constructor(name){this.name=name}get mode(){return this.pipeline?.mode??`managed`}async register(start){if(this.pending)return this.pending;if(!this.pipeline)return this.pending=(async()=>{await containAsync(`register()`,async()=>{let pipeline=await start();this.pipeline=pipeline,pipeline&&(this.policy=pipeline.capture,this.isolatedProvider=pipeline.isolated?pipeline.provider:null)}),this.pending=null})(),this.pending}configureCapture(provider,policy){this.isolatedProvider=provider,this.policy=policy}async flush(){await this.pending;let{pipeline}=this,flushing=containAsync(`flush()`,async()=>pipeline?.flush());this.flushes.add(flushing);try{await Promise.all(this.flushes)}finally{this.flushes.delete(flushing)}}async shutdown(){await this.pending;let{pipeline}=this;this.pipeline=null,this.isolatedProvider=null,await containAsync(`shutdown()`,async()=>pipeline?.shutdown())}recordException(value,mechanism,attributes={}){contain(`captureError()`,()=>{let attrs={...attributes,"interfere.exception.handled":mechanism.handled,"interfere.exception.mechanism":mechanism.type,"exception.type":isNonErrorException(value)?value.type:value.name,"exception.message":isNonErrorException(value)?value.value:value.message,"interfere.exception.kind":isNonErrorException(value)?`non-error`:`error`};if(isNonErrorException(value)){let serialized=value.serialized===void 0?`undefined`:JSON.stringify(value.serialized);attrs[`interfere.exception.serialized`]=typeof value.serialized==`string`?value.serialized:serialized}else attrs[`interfere.exception.chain`]=JSON.stringify(toExceptions(value,mechanism)),value.stack&&(attrs[`exception.stacktrace`]=value.stack);createUrlRedactionSpanProcessor(this.policy.urlRedaction).onEnd({attributes:{},events:[{name:`exception`,attributes:attrs}]});let statusMessage=String(attrs[`exception.message`]).slice(0,ATTRIBUTE_VALUE_LENGTH_LIMIT),host=trace.getActiveSpan(),active=this.isolatedProvider&&!this.policy.recordOnHostSpan?void 0:host,timestamp=Date.now(),separate=this.isolatedProvider!==null&&this.policy.errors===`interfere`;if(active?.isRecording()&&(active.addEvent(`exception`,separate?{...attrs,[EXCEPTION_DELIVERY_ATTRIBUTE]:EXCEPTION_DELIVERY.interfere}:attrs,timestamp),mechanism.handled||active.setStatus({code:SpanStatusCode.ERROR,message:statusMessage}),!separate))return;let tracer=(this.isolatedProvider??trace.getTracerProvider()).getTracer(this.name),parent=host?.spanContext(),span=tracer.startSpan(exceptionSpanName(value),omitUndefined({attributes:{[CAPTURE_KIND_ATTRIBUTE]:`exception`},kind:SpanKind.INTERNAL,links:this.isolatedProvider&&parent&&isSpanContextValid(parent)?[{context:parent}]:void 0}),this.isolatedProvider?ROOT_CONTEXT:void 0);span.addEvent(`exception`,attrs,timestamp),mechanism.handled||span.setStatus({code:SpanStatusCode.ERROR,message:statusMessage}),span.end()})}};export{ATTRIBUTE_VALUE_LENGTH_LIMIT,ServerRuntime};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Attributes, Context, Link, SpanKind } from "@opentelemetry/api";
|
|
2
|
+
import { Sampler, SamplingResult } from "@opentelemetry/sdk-trace-base";
|
|
3
|
+
export declare const CAPTURE_KIND_ATTRIBUTE = "interfere.capture.kind";
|
|
4
|
+
declare class ExceptionSampler implements Sampler {
|
|
5
|
+
private readonly delegate;
|
|
6
|
+
constructor(delegate: Sampler);
|
|
7
|
+
shouldSample(parent: Context, traceId: string, name: string, kind: SpanKind, attributes: Attributes, links: Link[]): SamplingResult;
|
|
8
|
+
toString(): string;
|
|
9
|
+
}
|
|
10
|
+
/** Applies the OTEL_TRACES_SAMPLER head-sampling policy to ordinary spans while always keeping Interfere exception spans. */
|
|
11
|
+
export declare function exceptionSampler(): ExceptionSampler;
|
|
12
|
+
export {};
|
package/dist/sampler.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{getStringFromEnv}from"@opentelemetry/core";import{AlwaysOffSampler,AlwaysOnSampler,ParentBasedSampler,SamplingDecision,TraceIdRatioBasedSampler}from"@opentelemetry/sdk-trace-base";const CAPTURE_KIND_ATTRIBUTE=`interfere.capture.kind`;function ratio(){let value=Number(getStringFromEnv(`OTEL_TRACES_SAMPLER_ARG`)??`1`);return Number.isFinite(value)&&value>=0&&value<=1?value:1}const SAMPLERS=/* @__PURE__ */ new Map([[`always_on`,()=>new AlwaysOnSampler],[`always_off`,()=>new AlwaysOffSampler],[`traceidratio`,()=>new TraceIdRatioBasedSampler(ratio())],[`parentbased_always_off`,()=>new ParentBasedSampler({root:new AlwaysOffSampler})],[`parentbased_traceidratio`,()=>new ParentBasedSampler({root:new TraceIdRatioBasedSampler(ratio())})]]);function configuredSampler(){let build=SAMPLERS.get(getStringFromEnv(`OTEL_TRACES_SAMPLER`)??``);return build?build():new ParentBasedSampler({root:new AlwaysOnSampler})}var ExceptionSampler=class{delegate;constructor(delegate){this.delegate=delegate}shouldSample(parent,traceId,name,kind,attributes,links){return attributes[`interfere.capture.kind`]===`exception`?{decision:SamplingDecision.RECORD_AND_SAMPLED}:this.delegate.shouldSample(parent,traceId,name,kind,attributes,links)}toString(){return`ExceptionSampler{${this.delegate.toString()}}`}};function exceptionSampler(){return new ExceptionSampler(configuredSampler())}export{CAPTURE_KIND_ATTRIBUTE,exceptionSampler};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Waits at most 2 s for `flush` and never rejects. */
|
|
2
|
+
export declare function settleFlush(flush: () => Promise<void>): Promise<void>;
|
|
3
|
+
/** Flushes telemetry on SIGTERM, SIGINT and a drained event loop; never delays the host's own shutdown past the flush cap. */
|
|
4
|
+
export declare function installShutdownFlush(flush: () => Promise<void>): void;
|
|
5
|
+
export declare function uninstallShutdownFlush(): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{containAsync}from"./contain.mjs";import{isInterfereListener,removeInterfereListeners,tagListener}from"./process-listeners.mjs";const SHUTDOWN_SIGNALS=[`SIGTERM`,`SIGINT`];async function settleFlush(flush){let{promise:capped,resolve:release}=Promise.withResolvers(),timer=setTimeout(()=>{release(void 0)},2e3);try{await Promise.race([containAsync(`Telemetry flush`,flush),capped])}finally{clearTimeout(timer)}}async function flushThenTerminate(flush,signal){let hostOwned=process.listeners(signal).some(listener=>!isInterfereListener(listener));await settleFlush(flush),hostOwned||(uninstallShutdownFlush(),process.kill(process.pid,signal))}function installShutdownFlush(flush){uninstallShutdownFlush();let drained=!1,onSignal=tagListener(signal=>{flushThenTerminate(flush,signal)}),onBeforeExit=tagListener(()=>{drained||(drained=!0,settleFlush(flush))});for(let signal of SHUTDOWN_SIGNALS)process.on(signal,onSignal);process.on(`beforeExit`,onBeforeExit)}function uninstallShutdownFlush(){for(let signal of SHUTDOWN_SIGNALS)removeInterfereListeners(signal);removeInterfereListeners(`beforeExit`)}export{installShutdownFlush,settleFlush,uninstallShutdownFlush};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@interfere/server-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Shared telemetry runtime for Interfere server adapters.",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -33,6 +33,16 @@
|
|
|
33
33
|
"types": "./dist/contain.d.mts",
|
|
34
34
|
"default": "./dist/contain.mjs"
|
|
35
35
|
},
|
|
36
|
+
"./global-handlers": {
|
|
37
|
+
"monorepo": "./src/global-handlers.ts",
|
|
38
|
+
"types": "./dist/global-handlers.d.mts",
|
|
39
|
+
"default": "./dist/global-handlers.mjs"
|
|
40
|
+
},
|
|
41
|
+
"./shutdown": {
|
|
42
|
+
"monorepo": "./src/shutdown.ts",
|
|
43
|
+
"types": "./dist/shutdown.d.mts",
|
|
44
|
+
"default": "./dist/shutdown.mjs"
|
|
45
|
+
},
|
|
36
46
|
"./contained-span-processor": {
|
|
37
47
|
"monorepo": "./src/contained-span-processor.ts",
|
|
38
48
|
"types": "./dist/contained-span-processor.d.mts",
|
|
@@ -50,8 +60,8 @@
|
|
|
50
60
|
},
|
|
51
61
|
"dependencies": {
|
|
52
62
|
"@interfere/constants": "^9.3.2",
|
|
53
|
-
"@interfere/helpers": "^9.3.
|
|
54
|
-
"@interfere/types": "^11.0.
|
|
63
|
+
"@interfere/helpers": "^9.3.3",
|
|
64
|
+
"@interfere/types": "^11.0.1",
|
|
55
65
|
"@opentelemetry/api": "1.9.1",
|
|
56
66
|
"@opentelemetry/api-logs": "0.222.0",
|
|
57
67
|
"@opentelemetry/context-async-hooks": "2.11.0",
|
|
@@ -65,7 +75,7 @@
|
|
|
65
75
|
"@opentelemetry/sdk-trace-node": "2.11.0"
|
|
66
76
|
},
|
|
67
77
|
"devDependencies": {
|
|
68
|
-
"@interfere/test-utils": "^10.1.
|
|
78
|
+
"@interfere/test-utils": "^10.1.2",
|
|
69
79
|
"@interfere/typescript-config": "^9.0.3",
|
|
70
80
|
"@interfere/vitest-config": "0.0.0",
|
|
71
81
|
"@types/node": "26.6.6",
|