@ciq-dev/neoiq-foundation-node 1.1.2-beta.4 → 1.1.2-beta.6
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/bootstrap.js +1 -1
- package/dist/bootstrap.mjs +1 -1
- package/dist/index.d.mts +14 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/{tracing-BdAbHWu1.mjs → tracing-BDujtW7K.mjs} +30 -4
- package/dist/tracing-BDujtW7K.mjs.map +1 -0
- package/dist/{tracing-Cdy7GW7Y.js → tracing-MDIljIwL.js} +30 -4
- package/dist/tracing-MDIljIwL.js.map +1 -0
- package/package.json +2 -1
- package/dist/tracing-BdAbHWu1.mjs.map +0 -1
- package/dist/tracing-Cdy7GW7Y.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracing-BDujtW7K.mjs","names":["input: FoundationConfigInput","sdk: NodeSDK | null","options: TracingOptions","autoInstrumentation: AutoInstrumentationConfig","instrumentations: Instrumentation[]","config: AutoInstrumentationConfig","mapping: Record<string, string>","result: Record<string, { enabled: boolean }>","name: string"],"sources":["../src/config.ts","../src/features/tracing.ts"],"sourcesContent":["/**\n * Foundation Configuration with Zod Validation\n */\n\nimport { z } from 'zod';\n\n// Auto-Instrumentation Config\nexport const AutoInstrumentationConfigSchema = z\n .object({\n http: z.boolean().default(true),\n fastify: z.boolean().default(true),\n express: z.boolean().default(true),\n mongodb: z.boolean().default(true),\n pg: z.boolean().default(true),\n mysql: z.boolean().default(true),\n redis: z.boolean().default(true),\n ioredis: z.boolean().default(true),\n grpc: z.boolean().default(true),\n fs: z.boolean().default(false),\n dns: z.boolean().default(false),\n })\n .partial();\n\nexport type AutoInstrumentationConfig = z.infer<typeof AutoInstrumentationConfigSchema>;\n\n// Features Config\nexport const FeaturesConfigSchema = z\n .object({\n tracing: z.boolean().default(true),\n metrics: z.boolean().default(true),\n logging: z.boolean().default(true),\n autoInstrumentation: AutoInstrumentationConfigSchema.default({}),\n })\n .partial();\n\nexport type FeaturesConfig = z.infer<typeof FeaturesConfigSchema>;\n\n// OTEL Config\nconst DEFAULT_OTEL_ENDPOINT =\n 'http://otel-stack-deployment-collector.observability.svc.cluster.local:4317';\n\nexport const OtelConfigSchema = z.object({\n endpoint: z\n .string()\n .default(() => process.env.OTEL_EXPORTER_OTLP_ENDPOINT || DEFAULT_OTEL_ENDPOINT),\n metricsIntervalMs: z.number().min(1000).default(5000),\n});\n\nexport type OtelConfig = z.infer<typeof OtelConfigSchema>;\n\n// Logging Config\nexport const LoggingConfigSchema = z\n .object({\n level: z.enum(['debug', 'info', 'warn', 'error']).default('info'),\n prettyPrint: z.boolean().optional(),\n })\n .partial();\n\nexport type LoggingConfig = z.infer<typeof LoggingConfigSchema>;\n\n// Request Logging Config\nexport const RequestLoggingConfigSchema = z\n .object({\n logHeaders: z.boolean().default(true),\n logBody: z.boolean().default(false),\n logResponseBody: z.boolean().default(false),\n maxBodySize: z.number().default(10 * 1024),\n redactHeaders: z.array(z.string()).optional(),\n })\n .partial();\n\nexport type RequestLoggingConfig = z.infer<typeof RequestLoggingConfigSchema>;\n\n// Redaction Config\nexport const RedactionConfigSchema = z\n .object({\n additionalPaths: z.array(z.string()).default([]),\n })\n .partial();\n\nexport type RedactionConfig = z.infer<typeof RedactionConfigSchema>;\n\n// Shutdown Config\nexport const ShutdownConfigSchema = z\n .object({\n /** Flush telemetry on uncaughtException / unhandledRejection before process exits */\n flushOnCrash: z.boolean().default(false),\n /** Grace period (ms) for flushing before forcing exit */\n flushTimeoutMs: z.number().min(100).default(5000),\n })\n .partial();\n\nexport type ShutdownConfig = z.infer<typeof ShutdownConfigSchema>;\n\n// Vault Config\nexport const VaultConfigSchema = z.object({\n enabled: z.boolean().default(false),\n address: z.string().default('https://vault.beta.commerceiq.ai'),\n authMethod: z.enum(['kubernetes', 'token']).default('kubernetes'),\n role: z.string().optional(),\n mountPoint: z.string().default('secret'),\n tokenPath: z.string().default('/var/run/secrets/kubernetes.io/serviceaccount/token'),\n k8sAuthMount: z.string().default('kubernetes'),\n namespace: z.string().optional(),\n});\n\nexport type VaultConfig = z.infer<typeof VaultConfigSchema>;\n\n// Main Foundation Config\nexport const FoundationConfigSchema = z.object({\n serviceName: z.string().min(1, 'serviceName is required'),\n serviceVersion: z.string().default(process.env.SERVICE_VERSION || '1.0.0'),\n environment: z\n .enum(['development', 'staging', 'qa', 'production'])\n .default(\n ['development', 'staging', 'qa', 'production'].includes(process.env.NODE_ENV ?? '')\n ? (process.env.NODE_ENV as 'development' | 'staging' | 'qa' | 'production')\n : 'development'\n ),\n features: FeaturesConfigSchema.default({}),\n otel: OtelConfigSchema.default({}),\n logging: LoggingConfigSchema.default({}),\n requestLogging: RequestLoggingConfigSchema.default({}),\n redaction: RedactionConfigSchema.default({}),\n shutdown: ShutdownConfigSchema.default({}),\n vault: VaultConfigSchema.default({}),\n});\n\nexport type FoundationConfig = z.infer<typeof FoundationConfigSchema>;\n\nexport type FoundationConfigInput = z.input<typeof FoundationConfigSchema> & {\n serviceName: string;\n};\n\n/** Parse and validate configuration */\nexport function parseConfig(input: FoundationConfigInput): FoundationConfig {\n const result = FoundationConfigSchema.safeParse(input);\n\n if (!result.success) {\n const errors = result.error.errors\n .map((e) => ` - ${e.path.join('.')}: ${e.message}`)\n .join('\\n');\n throw new Error(`Invalid foundation configuration:\\n${errors}`);\n }\n\n return result.data;\n}\n\n/** Get default OTEL endpoint */\nexport function getDefaultOtelEndpoint(): string {\n return process.env.OTEL_EXPORTER_OTLP_ENDPOINT || DEFAULT_OTEL_ENDPOINT;\n}\n","/**\n * OpenTelemetry Tracing Setup\n */\n\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';\nimport { trace, context, propagation, SpanStatusCode, type Tracer } from '@opentelemetry/api';\nimport { type IMetricReader } from '@opentelemetry/sdk-metrics';\nimport { FastifyOtelInstrumentation } from '@fastify/otel';\nimport { type AutoInstrumentationConfig, getDefaultOtelEndpoint } from '../config';\n\n/**\n * Element type of the NodeSDK instrumentations list. Derived from\n * getNodeAutoInstrumentations so we don't take a direct import on\n * @opentelemetry/instrumentation (only a transitive dependency here).\n */\ntype Instrumentation = ReturnType<typeof getNodeAutoInstrumentations>[number];\n\nexport interface TracingOptions {\n serviceName: string;\n serviceVersion: string;\n environment: string;\n endpoint?: string;\n autoInstrumentation?: AutoInstrumentationConfig;\n /**\n * Metric reader to attach to the NodeSDK so a single SDK owns both traces and\n * metrics (one global MeterProvider, same gRPC collector). When provided, NodeSDK\n * is the sole owner of metrics; when omitted, NodeSDK is opted out of metrics\n * entirely (see below) so it never registers a stray default MeterProvider.\n */\n metricReader?: IMetricReader;\n}\n\nlet sdk: NodeSDK | null = null;\nlet isInitialized = false;\n\nconst MONITORED_MODULES = ['pg', 'mongodb', 'ioredis', 'mysql2', 'express', '@grpc/grpc-js'];\n\nfunction warnPreloadedModules(): void {\n // require.resolve / require.cache are only available in CJS contexts\n if (typeof require === 'undefined' || typeof require.cache === 'undefined') return;\n\n for (const mod of MONITORED_MODULES) {\n try {\n const resolved = require.resolve(mod);\n if (require.cache[resolved]) {\n console.warn(\n `[neoiq-foundation] \"${mod}\" was imported before tracing init. ` +\n 'Auto-instrumentation may not work for this module. ' +\n 'Use node -r @ciq-dev/neoiq-foundation-node/bootstrap or call createFoundation() first.'\n );\n }\n } catch {\n // Module not installed — no warning needed\n }\n }\n}\n\n/** Initialize OpenTelemetry tracing */\nexport function setupTracing(options: TracingOptions): void {\n if (isInitialized) {\n console.warn('[neoiq-foundation] Tracing already initialized');\n return;\n }\n\n warnPreloadedModules();\n\n const {\n serviceName,\n serviceVersion,\n environment,\n endpoint = getDefaultOtelEndpoint(),\n autoInstrumentation = {},\n metricReader,\n } = options;\n\n const resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: serviceName,\n [ATTR_SERVICE_VERSION]: serviceVersion,\n 'deployment.environment': environment,\n });\n\n const traceExporter = new OTLPTraceExporter({ url: endpoint });\n\n // Foundation owns every OpenTelemetry signal explicitly. Left to its own devices,\n // NodeSDK auto-configures metrics AND logs exporters from env defaults — both OTLP\n // over HTTP to localhost:4318 — which nothing listens on, producing ECONNREFUSED\n // spam every export interval. We therefore configure each signal on NodeSDK directly:\n // - traces: the explicit gRPC exporter above.\n // - metrics: the explicit gRPC reader when provided (single SDK, single global\n // MeterProvider, same collector as traces). When metrics are disabled\n // and the deployer hasn't chosen an exporter, opt NodeSDK out via\n // OTEL_METRICS_EXPORTER=none (the only switch NodeSDK exposes for that).\n // - logs: foundation logs via pino, never OTLP. Passing an empty processor list\n // stops NodeSDK from falling back to its env-default localhost:4318 log\n // exporter (`logRecordProcessors: []` takes the explicit branch and skips\n // configureLoggerProviderFromEnv()).\n if (!metricReader && !process.env.OTEL_METRICS_EXPORTER) {\n process.env.OTEL_METRICS_EXPORTER = 'none';\n }\n\n sdk = new NodeSDK({\n resource,\n traceExporter,\n ...(metricReader ? { metricReaders: [metricReader] } : {}),\n logRecordProcessors: [],\n instrumentations: buildInstrumentations(autoInstrumentation),\n });\n\n sdk.start();\n isInitialized = true;\n}\n\n/**\n * Build the full instrumentation list for the NodeSDK.\n *\n * `auto-instrumentations-node` covers http, express, mongodb, redis, etc., but it\n * no longer ships Fastify support — the legacy `@opentelemetry/instrumentation-fastify`\n * does not work with Fastify v5. Fastify is therefore instrumented via the official\n * `@fastify/otel` plugin, which self-registers on every Fastify instance through the\n * `fastify.initialization` diagnostics channel (`registerOnInitialization`) and builds\n * its request span beneath the http server span created by `instrumentation-http`\n * (enabled above). Without this, Fastify services emit only bare, route-less \"GET\"\n * spans with no request → handler → db waterfall.\n */\nexport function buildInstrumentations(\n autoInstrumentation: AutoInstrumentationConfig = {}\n): Instrumentation[] {\n const instrumentations: Instrumentation[] = [\n ...getNodeAutoInstrumentations(buildInstrumentationConfig(autoInstrumentation)),\n ];\n\n if (autoInstrumentation.fastify !== false) {\n const fastifyOtel = new FastifyOtelInstrumentation({\n registerOnInitialization: true,\n // Rename the Fastify span from the default \"request\" to \"METHOD /route\" so the\n // waterfall reads e.g. \"GET /api/v1/ui-store/dashboards/standalone\".\n requestHook: (span, request) => {\n const routeUrl = (request as { routeOptions?: { url?: string } }).routeOptions?.url;\n const method = (request as { method?: string }).method;\n if (routeUrl && method) span.updateName(`${method} ${routeUrl}`);\n },\n });\n // Cast across a possible @opentelemetry/instrumentation version skew between\n // @fastify/otel and auto-instrumentations-node; the runtime contract is the\n // stable @opentelemetry/api Instrumentation interface, which both satisfy.\n instrumentations.push(fastifyOtel as unknown as Instrumentation);\n }\n\n return instrumentations;\n}\n\nfunction buildInstrumentationConfig(\n config: AutoInstrumentationConfig\n): Record<string, { enabled: boolean }> {\n const mapping: Record<string, string> = {\n http: '@opentelemetry/instrumentation-http',\n // NOTE: Fastify is intentionally absent here — auto-instrumentations-node dropped\n // @opentelemetry/instrumentation-fastify (no Fastify v5 support). Fastify is wired\n // via @fastify/otel in buildInstrumentations() instead.\n express: '@opentelemetry/instrumentation-express',\n mongodb: '@opentelemetry/instrumentation-mongodb',\n pg: '@opentelemetry/instrumentation-pg',\n mysql: '@opentelemetry/instrumentation-mysql',\n redis: '@opentelemetry/instrumentation-redis',\n ioredis: '@opentelemetry/instrumentation-ioredis',\n grpc: '@opentelemetry/instrumentation-grpc',\n fs: '@opentelemetry/instrumentation-fs',\n dns: '@opentelemetry/instrumentation-dns',\n };\n\n const result: Record<string, { enabled: boolean }> = {};\n for (const [key, instrumentationName] of Object.entries(mapping)) {\n const userSetting = config[key as keyof AutoInstrumentationConfig];\n const defaultValue = key !== 'fs' && key !== 'dns';\n result[instrumentationName] = { enabled: userSetting ?? defaultValue };\n }\n return result;\n}\n\n/** Shutdown tracing gracefully */\nexport async function shutdownTracing(): Promise<void> {\n if (!sdk) return;\n try {\n await sdk.shutdown();\n isInitialized = false;\n sdk = null;\n } catch (error) {\n console.error('[neoiq-foundation] Error shutting down tracing:', error);\n throw error;\n }\n}\n\nexport function getTracer(name: string): Tracer {\n return trace.getTracer(name);\n}\n\nexport function getActiveSpan(): ReturnType<typeof trace.getActiveSpan> {\n return trace.getActiveSpan();\n}\n\nexport function getTraceContext(): { traceId?: string; spanId?: string } {\n const span = trace.getActiveSpan();\n if (!span) return {};\n const ctx = span.spanContext();\n return { traceId: ctx.traceId, spanId: ctx.spanId };\n}\n\nexport function isTracingEnabled(): boolean {\n return isInitialized;\n}\n\nexport { trace, context, propagation, SpanStatusCode };\nexport type { Tracer };\n"],"mappings":"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"}
|
|
@@ -28,6 +28,7 @@ const __opentelemetry_exporter_trace_otlp_grpc = __toESM(require("@opentelemetry
|
|
|
28
28
|
const __opentelemetry_resources = __toESM(require("@opentelemetry/resources"));
|
|
29
29
|
const __opentelemetry_semantic_conventions = __toESM(require("@opentelemetry/semantic-conventions"));
|
|
30
30
|
const __opentelemetry_api = __toESM(require("@opentelemetry/api"));
|
|
31
|
+
const __fastify_otel = __toESM(require("@fastify/otel"));
|
|
31
32
|
const zod = __toESM(require("zod"));
|
|
32
33
|
|
|
33
34
|
//#region src/config.ts
|
|
@@ -155,22 +156,47 @@ function setupTracing(options) {
|
|
|
155
156
|
"deployment.environment": environment
|
|
156
157
|
});
|
|
157
158
|
const traceExporter = new __opentelemetry_exporter_trace_otlp_grpc.OTLPTraceExporter({ url: endpoint });
|
|
158
|
-
const instrumentationConfig = buildInstrumentationConfig(autoInstrumentation);
|
|
159
159
|
if (!metricReader && !process.env.OTEL_METRICS_EXPORTER) process.env.OTEL_METRICS_EXPORTER = "none";
|
|
160
160
|
sdk = new __opentelemetry_sdk_node.NodeSDK({
|
|
161
161
|
resource,
|
|
162
162
|
traceExporter,
|
|
163
163
|
...metricReader ? { metricReaders: [metricReader] } : {},
|
|
164
164
|
logRecordProcessors: [],
|
|
165
|
-
instrumentations:
|
|
165
|
+
instrumentations: buildInstrumentations(autoInstrumentation)
|
|
166
166
|
});
|
|
167
167
|
sdk.start();
|
|
168
168
|
isInitialized = true;
|
|
169
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Build the full instrumentation list for the NodeSDK.
|
|
172
|
+
*
|
|
173
|
+
* `auto-instrumentations-node` covers http, express, mongodb, redis, etc., but it
|
|
174
|
+
* no longer ships Fastify support — the legacy `@opentelemetry/instrumentation-fastify`
|
|
175
|
+
* does not work with Fastify v5. Fastify is therefore instrumented via the official
|
|
176
|
+
* `@fastify/otel` plugin, which self-registers on every Fastify instance through the
|
|
177
|
+
* `fastify.initialization` diagnostics channel (`registerOnInitialization`) and builds
|
|
178
|
+
* its request span beneath the http server span created by `instrumentation-http`
|
|
179
|
+
* (enabled above). Without this, Fastify services emit only bare, route-less "GET"
|
|
180
|
+
* spans with no request → handler → db waterfall.
|
|
181
|
+
*/
|
|
182
|
+
function buildInstrumentations(autoInstrumentation = {}) {
|
|
183
|
+
const instrumentations = [...(0, __opentelemetry_auto_instrumentations_node.getNodeAutoInstrumentations)(buildInstrumentationConfig(autoInstrumentation))];
|
|
184
|
+
if (autoInstrumentation.fastify !== false) {
|
|
185
|
+
const fastifyOtel = new __fastify_otel.FastifyOtelInstrumentation({
|
|
186
|
+
registerOnInitialization: true,
|
|
187
|
+
requestHook: (span, request) => {
|
|
188
|
+
const routeUrl = request.routeOptions?.url;
|
|
189
|
+
const method = request.method;
|
|
190
|
+
if (routeUrl && method) span.updateName(`${method} ${routeUrl}`);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
instrumentations.push(fastifyOtel);
|
|
194
|
+
}
|
|
195
|
+
return instrumentations;
|
|
196
|
+
}
|
|
170
197
|
function buildInstrumentationConfig(config) {
|
|
171
198
|
const mapping = {
|
|
172
199
|
http: "@opentelemetry/instrumentation-http",
|
|
173
|
-
fastify: "@opentelemetry/instrumentation-fastify",
|
|
174
200
|
express: "@opentelemetry/instrumentation-express",
|
|
175
201
|
mongodb: "@opentelemetry/instrumentation-mongodb",
|
|
176
202
|
pg: "@opentelemetry/instrumentation-pg",
|
|
@@ -329,4 +355,4 @@ Object.defineProperty(exports, 'shutdownTracing', {
|
|
|
329
355
|
return shutdownTracing;
|
|
330
356
|
}
|
|
331
357
|
});
|
|
332
|
-
//# sourceMappingURL=tracing-
|
|
358
|
+
//# sourceMappingURL=tracing-MDIljIwL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracing-MDIljIwL.js","names":["input: FoundationConfigInput","sdk: NodeSDK | null","options: TracingOptions","autoInstrumentation: AutoInstrumentationConfig","instrumentations: Instrumentation[]","config: AutoInstrumentationConfig","mapping: Record<string, string>","result: Record<string, { enabled: boolean }>","name: string"],"sources":["../src/config.ts","../src/features/tracing.ts"],"sourcesContent":["/**\n * Foundation Configuration with Zod Validation\n */\n\nimport { z } from 'zod';\n\n// Auto-Instrumentation Config\nexport const AutoInstrumentationConfigSchema = z\n .object({\n http: z.boolean().default(true),\n fastify: z.boolean().default(true),\n express: z.boolean().default(true),\n mongodb: z.boolean().default(true),\n pg: z.boolean().default(true),\n mysql: z.boolean().default(true),\n redis: z.boolean().default(true),\n ioredis: z.boolean().default(true),\n grpc: z.boolean().default(true),\n fs: z.boolean().default(false),\n dns: z.boolean().default(false),\n })\n .partial();\n\nexport type AutoInstrumentationConfig = z.infer<typeof AutoInstrumentationConfigSchema>;\n\n// Features Config\nexport const FeaturesConfigSchema = z\n .object({\n tracing: z.boolean().default(true),\n metrics: z.boolean().default(true),\n logging: z.boolean().default(true),\n autoInstrumentation: AutoInstrumentationConfigSchema.default({}),\n })\n .partial();\n\nexport type FeaturesConfig = z.infer<typeof FeaturesConfigSchema>;\n\n// OTEL Config\nconst DEFAULT_OTEL_ENDPOINT =\n 'http://otel-stack-deployment-collector.observability.svc.cluster.local:4317';\n\nexport const OtelConfigSchema = z.object({\n endpoint: z\n .string()\n .default(() => process.env.OTEL_EXPORTER_OTLP_ENDPOINT || DEFAULT_OTEL_ENDPOINT),\n metricsIntervalMs: z.number().min(1000).default(5000),\n});\n\nexport type OtelConfig = z.infer<typeof OtelConfigSchema>;\n\n// Logging Config\nexport const LoggingConfigSchema = z\n .object({\n level: z.enum(['debug', 'info', 'warn', 'error']).default('info'),\n prettyPrint: z.boolean().optional(),\n })\n .partial();\n\nexport type LoggingConfig = z.infer<typeof LoggingConfigSchema>;\n\n// Request Logging Config\nexport const RequestLoggingConfigSchema = z\n .object({\n logHeaders: z.boolean().default(true),\n logBody: z.boolean().default(false),\n logResponseBody: z.boolean().default(false),\n maxBodySize: z.number().default(10 * 1024),\n redactHeaders: z.array(z.string()).optional(),\n })\n .partial();\n\nexport type RequestLoggingConfig = z.infer<typeof RequestLoggingConfigSchema>;\n\n// Redaction Config\nexport const RedactionConfigSchema = z\n .object({\n additionalPaths: z.array(z.string()).default([]),\n })\n .partial();\n\nexport type RedactionConfig = z.infer<typeof RedactionConfigSchema>;\n\n// Shutdown Config\nexport const ShutdownConfigSchema = z\n .object({\n /** Flush telemetry on uncaughtException / unhandledRejection before process exits */\n flushOnCrash: z.boolean().default(false),\n /** Grace period (ms) for flushing before forcing exit */\n flushTimeoutMs: z.number().min(100).default(5000),\n })\n .partial();\n\nexport type ShutdownConfig = z.infer<typeof ShutdownConfigSchema>;\n\n// Vault Config\nexport const VaultConfigSchema = z.object({\n enabled: z.boolean().default(false),\n address: z.string().default('https://vault.beta.commerceiq.ai'),\n authMethod: z.enum(['kubernetes', 'token']).default('kubernetes'),\n role: z.string().optional(),\n mountPoint: z.string().default('secret'),\n tokenPath: z.string().default('/var/run/secrets/kubernetes.io/serviceaccount/token'),\n k8sAuthMount: z.string().default('kubernetes'),\n namespace: z.string().optional(),\n});\n\nexport type VaultConfig = z.infer<typeof VaultConfigSchema>;\n\n// Main Foundation Config\nexport const FoundationConfigSchema = z.object({\n serviceName: z.string().min(1, 'serviceName is required'),\n serviceVersion: z.string().default(process.env.SERVICE_VERSION || '1.0.0'),\n environment: z\n .enum(['development', 'staging', 'qa', 'production'])\n .default(\n ['development', 'staging', 'qa', 'production'].includes(process.env.NODE_ENV ?? '')\n ? (process.env.NODE_ENV as 'development' | 'staging' | 'qa' | 'production')\n : 'development'\n ),\n features: FeaturesConfigSchema.default({}),\n otel: OtelConfigSchema.default({}),\n logging: LoggingConfigSchema.default({}),\n requestLogging: RequestLoggingConfigSchema.default({}),\n redaction: RedactionConfigSchema.default({}),\n shutdown: ShutdownConfigSchema.default({}),\n vault: VaultConfigSchema.default({}),\n});\n\nexport type FoundationConfig = z.infer<typeof FoundationConfigSchema>;\n\nexport type FoundationConfigInput = z.input<typeof FoundationConfigSchema> & {\n serviceName: string;\n};\n\n/** Parse and validate configuration */\nexport function parseConfig(input: FoundationConfigInput): FoundationConfig {\n const result = FoundationConfigSchema.safeParse(input);\n\n if (!result.success) {\n const errors = result.error.errors\n .map((e) => ` - ${e.path.join('.')}: ${e.message}`)\n .join('\\n');\n throw new Error(`Invalid foundation configuration:\\n${errors}`);\n }\n\n return result.data;\n}\n\n/** Get default OTEL endpoint */\nexport function getDefaultOtelEndpoint(): string {\n return process.env.OTEL_EXPORTER_OTLP_ENDPOINT || DEFAULT_OTEL_ENDPOINT;\n}\n","/**\n * OpenTelemetry Tracing Setup\n */\n\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';\nimport { trace, context, propagation, SpanStatusCode, type Tracer } from '@opentelemetry/api';\nimport { type IMetricReader } from '@opentelemetry/sdk-metrics';\nimport { FastifyOtelInstrumentation } from '@fastify/otel';\nimport { type AutoInstrumentationConfig, getDefaultOtelEndpoint } from '../config';\n\n/**\n * Element type of the NodeSDK instrumentations list. Derived from\n * getNodeAutoInstrumentations so we don't take a direct import on\n * @opentelemetry/instrumentation (only a transitive dependency here).\n */\ntype Instrumentation = ReturnType<typeof getNodeAutoInstrumentations>[number];\n\nexport interface TracingOptions {\n serviceName: string;\n serviceVersion: string;\n environment: string;\n endpoint?: string;\n autoInstrumentation?: AutoInstrumentationConfig;\n /**\n * Metric reader to attach to the NodeSDK so a single SDK owns both traces and\n * metrics (one global MeterProvider, same gRPC collector). When provided, NodeSDK\n * is the sole owner of metrics; when omitted, NodeSDK is opted out of metrics\n * entirely (see below) so it never registers a stray default MeterProvider.\n */\n metricReader?: IMetricReader;\n}\n\nlet sdk: NodeSDK | null = null;\nlet isInitialized = false;\n\nconst MONITORED_MODULES = ['pg', 'mongodb', 'ioredis', 'mysql2', 'express', '@grpc/grpc-js'];\n\nfunction warnPreloadedModules(): void {\n // require.resolve / require.cache are only available in CJS contexts\n if (typeof require === 'undefined' || typeof require.cache === 'undefined') return;\n\n for (const mod of MONITORED_MODULES) {\n try {\n const resolved = require.resolve(mod);\n if (require.cache[resolved]) {\n console.warn(\n `[neoiq-foundation] \"${mod}\" was imported before tracing init. ` +\n 'Auto-instrumentation may not work for this module. ' +\n 'Use node -r @ciq-dev/neoiq-foundation-node/bootstrap or call createFoundation() first.'\n );\n }\n } catch {\n // Module not installed — no warning needed\n }\n }\n}\n\n/** Initialize OpenTelemetry tracing */\nexport function setupTracing(options: TracingOptions): void {\n if (isInitialized) {\n console.warn('[neoiq-foundation] Tracing already initialized');\n return;\n }\n\n warnPreloadedModules();\n\n const {\n serviceName,\n serviceVersion,\n environment,\n endpoint = getDefaultOtelEndpoint(),\n autoInstrumentation = {},\n metricReader,\n } = options;\n\n const resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: serviceName,\n [ATTR_SERVICE_VERSION]: serviceVersion,\n 'deployment.environment': environment,\n });\n\n const traceExporter = new OTLPTraceExporter({ url: endpoint });\n\n // Foundation owns every OpenTelemetry signal explicitly. Left to its own devices,\n // NodeSDK auto-configures metrics AND logs exporters from env defaults — both OTLP\n // over HTTP to localhost:4318 — which nothing listens on, producing ECONNREFUSED\n // spam every export interval. We therefore configure each signal on NodeSDK directly:\n // - traces: the explicit gRPC exporter above.\n // - metrics: the explicit gRPC reader when provided (single SDK, single global\n // MeterProvider, same collector as traces). When metrics are disabled\n // and the deployer hasn't chosen an exporter, opt NodeSDK out via\n // OTEL_METRICS_EXPORTER=none (the only switch NodeSDK exposes for that).\n // - logs: foundation logs via pino, never OTLP. Passing an empty processor list\n // stops NodeSDK from falling back to its env-default localhost:4318 log\n // exporter (`logRecordProcessors: []` takes the explicit branch and skips\n // configureLoggerProviderFromEnv()).\n if (!metricReader && !process.env.OTEL_METRICS_EXPORTER) {\n process.env.OTEL_METRICS_EXPORTER = 'none';\n }\n\n sdk = new NodeSDK({\n resource,\n traceExporter,\n ...(metricReader ? { metricReaders: [metricReader] } : {}),\n logRecordProcessors: [],\n instrumentations: buildInstrumentations(autoInstrumentation),\n });\n\n sdk.start();\n isInitialized = true;\n}\n\n/**\n * Build the full instrumentation list for the NodeSDK.\n *\n * `auto-instrumentations-node` covers http, express, mongodb, redis, etc., but it\n * no longer ships Fastify support — the legacy `@opentelemetry/instrumentation-fastify`\n * does not work with Fastify v5. Fastify is therefore instrumented via the official\n * `@fastify/otel` plugin, which self-registers on every Fastify instance through the\n * `fastify.initialization` diagnostics channel (`registerOnInitialization`) and builds\n * its request span beneath the http server span created by `instrumentation-http`\n * (enabled above). Without this, Fastify services emit only bare, route-less \"GET\"\n * spans with no request → handler → db waterfall.\n */\nexport function buildInstrumentations(\n autoInstrumentation: AutoInstrumentationConfig = {}\n): Instrumentation[] {\n const instrumentations: Instrumentation[] = [\n ...getNodeAutoInstrumentations(buildInstrumentationConfig(autoInstrumentation)),\n ];\n\n if (autoInstrumentation.fastify !== false) {\n const fastifyOtel = new FastifyOtelInstrumentation({\n registerOnInitialization: true,\n // Rename the Fastify span from the default \"request\" to \"METHOD /route\" so the\n // waterfall reads e.g. \"GET /api/v1/ui-store/dashboards/standalone\".\n requestHook: (span, request) => {\n const routeUrl = (request as { routeOptions?: { url?: string } }).routeOptions?.url;\n const method = (request as { method?: string }).method;\n if (routeUrl && method) span.updateName(`${method} ${routeUrl}`);\n },\n });\n // Cast across a possible @opentelemetry/instrumentation version skew between\n // @fastify/otel and auto-instrumentations-node; the runtime contract is the\n // stable @opentelemetry/api Instrumentation interface, which both satisfy.\n instrumentations.push(fastifyOtel as unknown as Instrumentation);\n }\n\n return instrumentations;\n}\n\nfunction buildInstrumentationConfig(\n config: AutoInstrumentationConfig\n): Record<string, { enabled: boolean }> {\n const mapping: Record<string, string> = {\n http: '@opentelemetry/instrumentation-http',\n // NOTE: Fastify is intentionally absent here — auto-instrumentations-node dropped\n // @opentelemetry/instrumentation-fastify (no Fastify v5 support). Fastify is wired\n // via @fastify/otel in buildInstrumentations() instead.\n express: '@opentelemetry/instrumentation-express',\n mongodb: '@opentelemetry/instrumentation-mongodb',\n pg: '@opentelemetry/instrumentation-pg',\n mysql: '@opentelemetry/instrumentation-mysql',\n redis: '@opentelemetry/instrumentation-redis',\n ioredis: '@opentelemetry/instrumentation-ioredis',\n grpc: '@opentelemetry/instrumentation-grpc',\n fs: '@opentelemetry/instrumentation-fs',\n dns: '@opentelemetry/instrumentation-dns',\n };\n\n const result: Record<string, { enabled: boolean }> = {};\n for (const [key, instrumentationName] of Object.entries(mapping)) {\n const userSetting = config[key as keyof AutoInstrumentationConfig];\n const defaultValue = key !== 'fs' && key !== 'dns';\n result[instrumentationName] = { enabled: userSetting ?? defaultValue };\n }\n return result;\n}\n\n/** Shutdown tracing gracefully */\nexport async function shutdownTracing(): Promise<void> {\n if (!sdk) return;\n try {\n await sdk.shutdown();\n isInitialized = false;\n sdk = null;\n } catch (error) {\n console.error('[neoiq-foundation] Error shutting down tracing:', error);\n throw error;\n }\n}\n\nexport function getTracer(name: string): Tracer {\n return trace.getTracer(name);\n}\n\nexport function getActiveSpan(): ReturnType<typeof trace.getActiveSpan> {\n return trace.getActiveSpan();\n}\n\nexport function getTraceContext(): { traceId?: string; spanId?: string } {\n const span = trace.getActiveSpan();\n if (!span) return {};\n const ctx = span.spanContext();\n return { traceId: ctx.traceId, spanId: ctx.spanId };\n}\n\nexport function isTracingEnabled(): boolean {\n return isInitialized;\n}\n\nexport { trace, context, propagation, SpanStatusCode };\nexport type { Tracer };\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ciq-dev/neoiq-foundation-node",
|
|
3
|
-
"version": "1.1.2-beta.
|
|
3
|
+
"version": "1.1.2-beta.6",
|
|
4
4
|
"description": "Node.js observability foundation for CommerceIQ services. Integrates with Groundcover via OpenTelemetry.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -78,6 +78,7 @@
|
|
|
78
78
|
"url": "git+https://github.com/commerceiq/neoiq-foundation-node.git"
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
|
+
"@fastify/otel": "^0.18.1",
|
|
81
82
|
"node-vault": "^0.12.0"
|
|
82
83
|
}
|
|
83
84
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tracing-BdAbHWu1.mjs","names":["input: FoundationConfigInput","sdk: NodeSDK | null","options: TracingOptions","config: AutoInstrumentationConfig","mapping: Record<string, string>","result: Record<string, { enabled: boolean }>","name: string"],"sources":["../src/config.ts","../src/features/tracing.ts"],"sourcesContent":["/**\n * Foundation Configuration with Zod Validation\n */\n\nimport { z } from 'zod';\n\n// Auto-Instrumentation Config\nexport const AutoInstrumentationConfigSchema = z\n .object({\n http: z.boolean().default(true),\n fastify: z.boolean().default(true),\n express: z.boolean().default(true),\n mongodb: z.boolean().default(true),\n pg: z.boolean().default(true),\n mysql: z.boolean().default(true),\n redis: z.boolean().default(true),\n ioredis: z.boolean().default(true),\n grpc: z.boolean().default(true),\n fs: z.boolean().default(false),\n dns: z.boolean().default(false),\n })\n .partial();\n\nexport type AutoInstrumentationConfig = z.infer<typeof AutoInstrumentationConfigSchema>;\n\n// Features Config\nexport const FeaturesConfigSchema = z\n .object({\n tracing: z.boolean().default(true),\n metrics: z.boolean().default(true),\n logging: z.boolean().default(true),\n autoInstrumentation: AutoInstrumentationConfigSchema.default({}),\n })\n .partial();\n\nexport type FeaturesConfig = z.infer<typeof FeaturesConfigSchema>;\n\n// OTEL Config\nconst DEFAULT_OTEL_ENDPOINT =\n 'http://otel-stack-deployment-collector.observability.svc.cluster.local:4317';\n\nexport const OtelConfigSchema = z.object({\n endpoint: z\n .string()\n .default(() => process.env.OTEL_EXPORTER_OTLP_ENDPOINT || DEFAULT_OTEL_ENDPOINT),\n metricsIntervalMs: z.number().min(1000).default(5000),\n});\n\nexport type OtelConfig = z.infer<typeof OtelConfigSchema>;\n\n// Logging Config\nexport const LoggingConfigSchema = z\n .object({\n level: z.enum(['debug', 'info', 'warn', 'error']).default('info'),\n prettyPrint: z.boolean().optional(),\n })\n .partial();\n\nexport type LoggingConfig = z.infer<typeof LoggingConfigSchema>;\n\n// Request Logging Config\nexport const RequestLoggingConfigSchema = z\n .object({\n logHeaders: z.boolean().default(true),\n logBody: z.boolean().default(false),\n logResponseBody: z.boolean().default(false),\n maxBodySize: z.number().default(10 * 1024),\n redactHeaders: z.array(z.string()).optional(),\n })\n .partial();\n\nexport type RequestLoggingConfig = z.infer<typeof RequestLoggingConfigSchema>;\n\n// Redaction Config\nexport const RedactionConfigSchema = z\n .object({\n additionalPaths: z.array(z.string()).default([]),\n })\n .partial();\n\nexport type RedactionConfig = z.infer<typeof RedactionConfigSchema>;\n\n// Shutdown Config\nexport const ShutdownConfigSchema = z\n .object({\n /** Flush telemetry on uncaughtException / unhandledRejection before process exits */\n flushOnCrash: z.boolean().default(false),\n /** Grace period (ms) for flushing before forcing exit */\n flushTimeoutMs: z.number().min(100).default(5000),\n })\n .partial();\n\nexport type ShutdownConfig = z.infer<typeof ShutdownConfigSchema>;\n\n// Vault Config\nexport const VaultConfigSchema = z.object({\n enabled: z.boolean().default(false),\n address: z.string().default('https://vault.beta.commerceiq.ai'),\n authMethod: z.enum(['kubernetes', 'token']).default('kubernetes'),\n role: z.string().optional(),\n mountPoint: z.string().default('secret'),\n tokenPath: z.string().default('/var/run/secrets/kubernetes.io/serviceaccount/token'),\n k8sAuthMount: z.string().default('kubernetes'),\n namespace: z.string().optional(),\n});\n\nexport type VaultConfig = z.infer<typeof VaultConfigSchema>;\n\n// Main Foundation Config\nexport const FoundationConfigSchema = z.object({\n serviceName: z.string().min(1, 'serviceName is required'),\n serviceVersion: z.string().default(process.env.SERVICE_VERSION || '1.0.0'),\n environment: z\n .enum(['development', 'staging', 'qa', 'production'])\n .default(\n ['development', 'staging', 'qa', 'production'].includes(process.env.NODE_ENV ?? '')\n ? (process.env.NODE_ENV as 'development' | 'staging' | 'qa' | 'production')\n : 'development'\n ),\n features: FeaturesConfigSchema.default({}),\n otel: OtelConfigSchema.default({}),\n logging: LoggingConfigSchema.default({}),\n requestLogging: RequestLoggingConfigSchema.default({}),\n redaction: RedactionConfigSchema.default({}),\n shutdown: ShutdownConfigSchema.default({}),\n vault: VaultConfigSchema.default({}),\n});\n\nexport type FoundationConfig = z.infer<typeof FoundationConfigSchema>;\n\nexport type FoundationConfigInput = z.input<typeof FoundationConfigSchema> & {\n serviceName: string;\n};\n\n/** Parse and validate configuration */\nexport function parseConfig(input: FoundationConfigInput): FoundationConfig {\n const result = FoundationConfigSchema.safeParse(input);\n\n if (!result.success) {\n const errors = result.error.errors\n .map((e) => ` - ${e.path.join('.')}: ${e.message}`)\n .join('\\n');\n throw new Error(`Invalid foundation configuration:\\n${errors}`);\n }\n\n return result.data;\n}\n\n/** Get default OTEL endpoint */\nexport function getDefaultOtelEndpoint(): string {\n return process.env.OTEL_EXPORTER_OTLP_ENDPOINT || DEFAULT_OTEL_ENDPOINT;\n}\n","/**\n * OpenTelemetry Tracing Setup\n */\n\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';\nimport { trace, context, propagation, SpanStatusCode, type Tracer } from '@opentelemetry/api';\nimport { type IMetricReader } from '@opentelemetry/sdk-metrics';\nimport { type AutoInstrumentationConfig, getDefaultOtelEndpoint } from '../config';\n\nexport interface TracingOptions {\n serviceName: string;\n serviceVersion: string;\n environment: string;\n endpoint?: string;\n autoInstrumentation?: AutoInstrumentationConfig;\n /**\n * Metric reader to attach to the NodeSDK so a single SDK owns both traces and\n * metrics (one global MeterProvider, same gRPC collector). When provided, NodeSDK\n * is the sole owner of metrics; when omitted, NodeSDK is opted out of metrics\n * entirely (see below) so it never registers a stray default MeterProvider.\n */\n metricReader?: IMetricReader;\n}\n\nlet sdk: NodeSDK | null = null;\nlet isInitialized = false;\n\nconst MONITORED_MODULES = ['pg', 'mongodb', 'ioredis', 'mysql2', 'express', '@grpc/grpc-js'];\n\nfunction warnPreloadedModules(): void {\n // require.resolve / require.cache are only available in CJS contexts\n if (typeof require === 'undefined' || typeof require.cache === 'undefined') return;\n\n for (const mod of MONITORED_MODULES) {\n try {\n const resolved = require.resolve(mod);\n if (require.cache[resolved]) {\n console.warn(\n `[neoiq-foundation] \"${mod}\" was imported before tracing init. ` +\n 'Auto-instrumentation may not work for this module. ' +\n 'Use node -r @ciq-dev/neoiq-foundation-node/bootstrap or call createFoundation() first.'\n );\n }\n } catch {\n // Module not installed — no warning needed\n }\n }\n}\n\n/** Initialize OpenTelemetry tracing */\nexport function setupTracing(options: TracingOptions): void {\n if (isInitialized) {\n console.warn('[neoiq-foundation] Tracing already initialized');\n return;\n }\n\n warnPreloadedModules();\n\n const {\n serviceName,\n serviceVersion,\n environment,\n endpoint = getDefaultOtelEndpoint(),\n autoInstrumentation = {},\n metricReader,\n } = options;\n\n const resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: serviceName,\n [ATTR_SERVICE_VERSION]: serviceVersion,\n 'deployment.environment': environment,\n });\n\n const traceExporter = new OTLPTraceExporter({ url: endpoint });\n const instrumentationConfig = buildInstrumentationConfig(autoInstrumentation);\n\n // Foundation owns every OpenTelemetry signal explicitly. Left to its own devices,\n // NodeSDK auto-configures metrics AND logs exporters from env defaults — both OTLP\n // over HTTP to localhost:4318 — which nothing listens on, producing ECONNREFUSED\n // spam every export interval. We therefore configure each signal on NodeSDK directly:\n // - traces: the explicit gRPC exporter above.\n // - metrics: the explicit gRPC reader when provided (single SDK, single global\n // MeterProvider, same collector as traces). When metrics are disabled\n // and the deployer hasn't chosen an exporter, opt NodeSDK out via\n // OTEL_METRICS_EXPORTER=none (the only switch NodeSDK exposes for that).\n // - logs: foundation logs via pino, never OTLP. Passing an empty processor list\n // stops NodeSDK from falling back to its env-default localhost:4318 log\n // exporter (`logRecordProcessors: []` takes the explicit branch and skips\n // configureLoggerProviderFromEnv()).\n if (!metricReader && !process.env.OTEL_METRICS_EXPORTER) {\n process.env.OTEL_METRICS_EXPORTER = 'none';\n }\n\n sdk = new NodeSDK({\n resource,\n traceExporter,\n ...(metricReader ? { metricReaders: [metricReader] } : {}),\n logRecordProcessors: [],\n instrumentations: [getNodeAutoInstrumentations(instrumentationConfig)],\n });\n\n sdk.start();\n isInitialized = true;\n}\n\nfunction buildInstrumentationConfig(\n config: AutoInstrumentationConfig\n): Record<string, { enabled: boolean }> {\n const mapping: Record<string, string> = {\n http: '@opentelemetry/instrumentation-http',\n fastify: '@opentelemetry/instrumentation-fastify',\n express: '@opentelemetry/instrumentation-express',\n mongodb: '@opentelemetry/instrumentation-mongodb',\n pg: '@opentelemetry/instrumentation-pg',\n mysql: '@opentelemetry/instrumentation-mysql',\n redis: '@opentelemetry/instrumentation-redis',\n ioredis: '@opentelemetry/instrumentation-ioredis',\n grpc: '@opentelemetry/instrumentation-grpc',\n fs: '@opentelemetry/instrumentation-fs',\n dns: '@opentelemetry/instrumentation-dns',\n };\n\n const result: Record<string, { enabled: boolean }> = {};\n for (const [key, instrumentationName] of Object.entries(mapping)) {\n const userSetting = config[key as keyof AutoInstrumentationConfig];\n const defaultValue = key !== 'fs' && key !== 'dns';\n result[instrumentationName] = { enabled: userSetting ?? defaultValue };\n }\n return result;\n}\n\n/** Shutdown tracing gracefully */\nexport async function shutdownTracing(): Promise<void> {\n if (!sdk) return;\n try {\n await sdk.shutdown();\n isInitialized = false;\n sdk = null;\n } catch (error) {\n console.error('[neoiq-foundation] Error shutting down tracing:', error);\n throw error;\n }\n}\n\nexport function getTracer(name: string): Tracer {\n return trace.getTracer(name);\n}\n\nexport function getActiveSpan(): ReturnType<typeof trace.getActiveSpan> {\n return trace.getActiveSpan();\n}\n\nexport function getTraceContext(): { traceId?: string; spanId?: string } {\n const span = trace.getActiveSpan();\n if (!span) return {};\n const ctx = span.spanContext();\n return { traceId: ctx.traceId, spanId: ctx.spanId };\n}\n\nexport function isTracingEnabled(): boolean {\n return isInitialized;\n}\n\nexport { trace, context, propagation, SpanStatusCode };\nexport type { Tracer };\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tracing-Cdy7GW7Y.js","names":["input: FoundationConfigInput","sdk: NodeSDK | null","options: TracingOptions","config: AutoInstrumentationConfig","mapping: Record<string, string>","result: Record<string, { enabled: boolean }>","name: string"],"sources":["../src/config.ts","../src/features/tracing.ts"],"sourcesContent":["/**\n * Foundation Configuration with Zod Validation\n */\n\nimport { z } from 'zod';\n\n// Auto-Instrumentation Config\nexport const AutoInstrumentationConfigSchema = z\n .object({\n http: z.boolean().default(true),\n fastify: z.boolean().default(true),\n express: z.boolean().default(true),\n mongodb: z.boolean().default(true),\n pg: z.boolean().default(true),\n mysql: z.boolean().default(true),\n redis: z.boolean().default(true),\n ioredis: z.boolean().default(true),\n grpc: z.boolean().default(true),\n fs: z.boolean().default(false),\n dns: z.boolean().default(false),\n })\n .partial();\n\nexport type AutoInstrumentationConfig = z.infer<typeof AutoInstrumentationConfigSchema>;\n\n// Features Config\nexport const FeaturesConfigSchema = z\n .object({\n tracing: z.boolean().default(true),\n metrics: z.boolean().default(true),\n logging: z.boolean().default(true),\n autoInstrumentation: AutoInstrumentationConfigSchema.default({}),\n })\n .partial();\n\nexport type FeaturesConfig = z.infer<typeof FeaturesConfigSchema>;\n\n// OTEL Config\nconst DEFAULT_OTEL_ENDPOINT =\n 'http://otel-stack-deployment-collector.observability.svc.cluster.local:4317';\n\nexport const OtelConfigSchema = z.object({\n endpoint: z\n .string()\n .default(() => process.env.OTEL_EXPORTER_OTLP_ENDPOINT || DEFAULT_OTEL_ENDPOINT),\n metricsIntervalMs: z.number().min(1000).default(5000),\n});\n\nexport type OtelConfig = z.infer<typeof OtelConfigSchema>;\n\n// Logging Config\nexport const LoggingConfigSchema = z\n .object({\n level: z.enum(['debug', 'info', 'warn', 'error']).default('info'),\n prettyPrint: z.boolean().optional(),\n })\n .partial();\n\nexport type LoggingConfig = z.infer<typeof LoggingConfigSchema>;\n\n// Request Logging Config\nexport const RequestLoggingConfigSchema = z\n .object({\n logHeaders: z.boolean().default(true),\n logBody: z.boolean().default(false),\n logResponseBody: z.boolean().default(false),\n maxBodySize: z.number().default(10 * 1024),\n redactHeaders: z.array(z.string()).optional(),\n })\n .partial();\n\nexport type RequestLoggingConfig = z.infer<typeof RequestLoggingConfigSchema>;\n\n// Redaction Config\nexport const RedactionConfigSchema = z\n .object({\n additionalPaths: z.array(z.string()).default([]),\n })\n .partial();\n\nexport type RedactionConfig = z.infer<typeof RedactionConfigSchema>;\n\n// Shutdown Config\nexport const ShutdownConfigSchema = z\n .object({\n /** Flush telemetry on uncaughtException / unhandledRejection before process exits */\n flushOnCrash: z.boolean().default(false),\n /** Grace period (ms) for flushing before forcing exit */\n flushTimeoutMs: z.number().min(100).default(5000),\n })\n .partial();\n\nexport type ShutdownConfig = z.infer<typeof ShutdownConfigSchema>;\n\n// Vault Config\nexport const VaultConfigSchema = z.object({\n enabled: z.boolean().default(false),\n address: z.string().default('https://vault.beta.commerceiq.ai'),\n authMethod: z.enum(['kubernetes', 'token']).default('kubernetes'),\n role: z.string().optional(),\n mountPoint: z.string().default('secret'),\n tokenPath: z.string().default('/var/run/secrets/kubernetes.io/serviceaccount/token'),\n k8sAuthMount: z.string().default('kubernetes'),\n namespace: z.string().optional(),\n});\n\nexport type VaultConfig = z.infer<typeof VaultConfigSchema>;\n\n// Main Foundation Config\nexport const FoundationConfigSchema = z.object({\n serviceName: z.string().min(1, 'serviceName is required'),\n serviceVersion: z.string().default(process.env.SERVICE_VERSION || '1.0.0'),\n environment: z\n .enum(['development', 'staging', 'qa', 'production'])\n .default(\n ['development', 'staging', 'qa', 'production'].includes(process.env.NODE_ENV ?? '')\n ? (process.env.NODE_ENV as 'development' | 'staging' | 'qa' | 'production')\n : 'development'\n ),\n features: FeaturesConfigSchema.default({}),\n otel: OtelConfigSchema.default({}),\n logging: LoggingConfigSchema.default({}),\n requestLogging: RequestLoggingConfigSchema.default({}),\n redaction: RedactionConfigSchema.default({}),\n shutdown: ShutdownConfigSchema.default({}),\n vault: VaultConfigSchema.default({}),\n});\n\nexport type FoundationConfig = z.infer<typeof FoundationConfigSchema>;\n\nexport type FoundationConfigInput = z.input<typeof FoundationConfigSchema> & {\n serviceName: string;\n};\n\n/** Parse and validate configuration */\nexport function parseConfig(input: FoundationConfigInput): FoundationConfig {\n const result = FoundationConfigSchema.safeParse(input);\n\n if (!result.success) {\n const errors = result.error.errors\n .map((e) => ` - ${e.path.join('.')}: ${e.message}`)\n .join('\\n');\n throw new Error(`Invalid foundation configuration:\\n${errors}`);\n }\n\n return result.data;\n}\n\n/** Get default OTEL endpoint */\nexport function getDefaultOtelEndpoint(): string {\n return process.env.OTEL_EXPORTER_OTLP_ENDPOINT || DEFAULT_OTEL_ENDPOINT;\n}\n","/**\n * OpenTelemetry Tracing Setup\n */\n\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';\nimport { trace, context, propagation, SpanStatusCode, type Tracer } from '@opentelemetry/api';\nimport { type IMetricReader } from '@opentelemetry/sdk-metrics';\nimport { type AutoInstrumentationConfig, getDefaultOtelEndpoint } from '../config';\n\nexport interface TracingOptions {\n serviceName: string;\n serviceVersion: string;\n environment: string;\n endpoint?: string;\n autoInstrumentation?: AutoInstrumentationConfig;\n /**\n * Metric reader to attach to the NodeSDK so a single SDK owns both traces and\n * metrics (one global MeterProvider, same gRPC collector). When provided, NodeSDK\n * is the sole owner of metrics; when omitted, NodeSDK is opted out of metrics\n * entirely (see below) so it never registers a stray default MeterProvider.\n */\n metricReader?: IMetricReader;\n}\n\nlet sdk: NodeSDK | null = null;\nlet isInitialized = false;\n\nconst MONITORED_MODULES = ['pg', 'mongodb', 'ioredis', 'mysql2', 'express', '@grpc/grpc-js'];\n\nfunction warnPreloadedModules(): void {\n // require.resolve / require.cache are only available in CJS contexts\n if (typeof require === 'undefined' || typeof require.cache === 'undefined') return;\n\n for (const mod of MONITORED_MODULES) {\n try {\n const resolved = require.resolve(mod);\n if (require.cache[resolved]) {\n console.warn(\n `[neoiq-foundation] \"${mod}\" was imported before tracing init. ` +\n 'Auto-instrumentation may not work for this module. ' +\n 'Use node -r @ciq-dev/neoiq-foundation-node/bootstrap or call createFoundation() first.'\n );\n }\n } catch {\n // Module not installed — no warning needed\n }\n }\n}\n\n/** Initialize OpenTelemetry tracing */\nexport function setupTracing(options: TracingOptions): void {\n if (isInitialized) {\n console.warn('[neoiq-foundation] Tracing already initialized');\n return;\n }\n\n warnPreloadedModules();\n\n const {\n serviceName,\n serviceVersion,\n environment,\n endpoint = getDefaultOtelEndpoint(),\n autoInstrumentation = {},\n metricReader,\n } = options;\n\n const resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: serviceName,\n [ATTR_SERVICE_VERSION]: serviceVersion,\n 'deployment.environment': environment,\n });\n\n const traceExporter = new OTLPTraceExporter({ url: endpoint });\n const instrumentationConfig = buildInstrumentationConfig(autoInstrumentation);\n\n // Foundation owns every OpenTelemetry signal explicitly. Left to its own devices,\n // NodeSDK auto-configures metrics AND logs exporters from env defaults — both OTLP\n // over HTTP to localhost:4318 — which nothing listens on, producing ECONNREFUSED\n // spam every export interval. We therefore configure each signal on NodeSDK directly:\n // - traces: the explicit gRPC exporter above.\n // - metrics: the explicit gRPC reader when provided (single SDK, single global\n // MeterProvider, same collector as traces). When metrics are disabled\n // and the deployer hasn't chosen an exporter, opt NodeSDK out via\n // OTEL_METRICS_EXPORTER=none (the only switch NodeSDK exposes for that).\n // - logs: foundation logs via pino, never OTLP. Passing an empty processor list\n // stops NodeSDK from falling back to its env-default localhost:4318 log\n // exporter (`logRecordProcessors: []` takes the explicit branch and skips\n // configureLoggerProviderFromEnv()).\n if (!metricReader && !process.env.OTEL_METRICS_EXPORTER) {\n process.env.OTEL_METRICS_EXPORTER = 'none';\n }\n\n sdk = new NodeSDK({\n resource,\n traceExporter,\n ...(metricReader ? { metricReaders: [metricReader] } : {}),\n logRecordProcessors: [],\n instrumentations: [getNodeAutoInstrumentations(instrumentationConfig)],\n });\n\n sdk.start();\n isInitialized = true;\n}\n\nfunction buildInstrumentationConfig(\n config: AutoInstrumentationConfig\n): Record<string, { enabled: boolean }> {\n const mapping: Record<string, string> = {\n http: '@opentelemetry/instrumentation-http',\n fastify: '@opentelemetry/instrumentation-fastify',\n express: '@opentelemetry/instrumentation-express',\n mongodb: '@opentelemetry/instrumentation-mongodb',\n pg: '@opentelemetry/instrumentation-pg',\n mysql: '@opentelemetry/instrumentation-mysql',\n redis: '@opentelemetry/instrumentation-redis',\n ioredis: '@opentelemetry/instrumentation-ioredis',\n grpc: '@opentelemetry/instrumentation-grpc',\n fs: '@opentelemetry/instrumentation-fs',\n dns: '@opentelemetry/instrumentation-dns',\n };\n\n const result: Record<string, { enabled: boolean }> = {};\n for (const [key, instrumentationName] of Object.entries(mapping)) {\n const userSetting = config[key as keyof AutoInstrumentationConfig];\n const defaultValue = key !== 'fs' && key !== 'dns';\n result[instrumentationName] = { enabled: userSetting ?? defaultValue };\n }\n return result;\n}\n\n/** Shutdown tracing gracefully */\nexport async function shutdownTracing(): Promise<void> {\n if (!sdk) return;\n try {\n await sdk.shutdown();\n isInitialized = false;\n sdk = null;\n } catch (error) {\n console.error('[neoiq-foundation] Error shutting down tracing:', error);\n throw error;\n }\n}\n\nexport function getTracer(name: string): Tracer {\n return trace.getTracer(name);\n}\n\nexport function getActiveSpan(): ReturnType<typeof trace.getActiveSpan> {\n return trace.getActiveSpan();\n}\n\nexport function getTraceContext(): { traceId?: string; spanId?: string } {\n const span = trace.getActiveSpan();\n if (!span) return {};\n const ctx = span.spanContext();\n return { traceId: ctx.traceId, spanId: ctx.spanId };\n}\n\nexport function isTracingEnabled(): boolean {\n return isInitialized;\n}\n\nexport { trace, context, propagation, SpanStatusCode };\nexport type { Tracer };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOA,MAAa,kCAAkC,MAC5C,OAAO;CACN,MAAM,MAAE,SAAS,CAAC,QAAQ,KAAK;CAC/B,SAAS,MAAE,SAAS,CAAC,QAAQ,KAAK;CAClC,SAAS,MAAE,SAAS,CAAC,QAAQ,KAAK;CAClC,SAAS,MAAE,SAAS,CAAC,QAAQ,KAAK;CAClC,IAAI,MAAE,SAAS,CAAC,QAAQ,KAAK;CAC7B,OAAO,MAAE,SAAS,CAAC,QAAQ,KAAK;CAChC,OAAO,MAAE,SAAS,CAAC,QAAQ,KAAK;CAChC,SAAS,MAAE,SAAS,CAAC,QAAQ,KAAK;CAClC,MAAM,MAAE,SAAS,CAAC,QAAQ,KAAK;CAC/B,IAAI,MAAE,SAAS,CAAC,QAAQ,MAAM;CAC9B,KAAK,MAAE,SAAS,CAAC,QAAQ,MAAM;AAChC,EAAC,CACD,SAAS;AAKZ,MAAa,uBAAuB,MACjC,OAAO;CACN,SAAS,MAAE,SAAS,CAAC,QAAQ,KAAK;CAClC,SAAS,MAAE,SAAS,CAAC,QAAQ,KAAK;CAClC,SAAS,MAAE,SAAS,CAAC,QAAQ,KAAK;CAClC,qBAAqB,gCAAgC,QAAQ,CAAE,EAAC;AACjE,EAAC,CACD,SAAS;AAKZ,MAAM,wBACJ;AAEF,MAAa,mBAAmB,MAAE,OAAO;CACvC,UAAU,MACP,QAAQ,CACR,QAAQ,MAAM,QAAQ,IAAI,+BAA+B,sBAAsB;CAClF,mBAAmB,MAAE,QAAQ,CAAC,IAAI,IAAK,CAAC,QAAQ,IAAK;AACtD,EAAC;AAKF,MAAa,sBAAsB,MAChC,OAAO;CACN,OAAO,MAAE,KAAK;EAAC;EAAS;EAAQ;EAAQ;CAAQ,EAAC,CAAC,QAAQ,OAAO;CACjE,aAAa,MAAE,SAAS,CAAC,UAAU;AACpC,EAAC,CACD,SAAS;AAKZ,MAAa,6BAA6B,MACvC,OAAO;CACN,YAAY,MAAE,SAAS,CAAC,QAAQ,KAAK;CACrC,SAAS,MAAE,SAAS,CAAC,QAAQ,MAAM;CACnC,iBAAiB,MAAE,SAAS,CAAC,QAAQ,MAAM;CAC3C,aAAa,MAAE,QAAQ,CAAC,QAAQ,KAAK,KAAK;CAC1C,eAAe,MAAE,MAAM,MAAE,QAAQ,CAAC,CAAC,UAAU;AAC9C,EAAC,CACD,SAAS;AAKZ,MAAa,wBAAwB,MAClC,OAAO,EACN,iBAAiB,MAAE,MAAM,MAAE,QAAQ,CAAC,CAAC,QAAQ,CAAE,EAAC,CACjD,EAAC,CACD,SAAS;AAKZ,MAAa,uBAAuB,MACjC,OAAO;CAEN,cAAc,MAAE,SAAS,CAAC,QAAQ,MAAM;CAExC,gBAAgB,MAAE,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,IAAK;AAClD,EAAC,CACD,SAAS;AAKZ,MAAa,oBAAoB,MAAE,OAAO;CACxC,SAAS,MAAE,SAAS,CAAC,QAAQ,MAAM;CACnC,SAAS,MAAE,QAAQ,CAAC,QAAQ,mCAAmC;CAC/D,YAAY,MAAE,KAAK,CAAC,cAAc,OAAQ,EAAC,CAAC,QAAQ,aAAa;CACjE,MAAM,MAAE,QAAQ,CAAC,UAAU;CAC3B,YAAY,MAAE,QAAQ,CAAC,QAAQ,SAAS;CACxC,WAAW,MAAE,QAAQ,CAAC,QAAQ,sDAAsD;CACpF,cAAc,MAAE,QAAQ,CAAC,QAAQ,aAAa;CAC9C,WAAW,MAAE,QAAQ,CAAC,UAAU;AACjC,EAAC;AAKF,MAAa,yBAAyB,MAAE,OAAO;CAC7C,aAAa,MAAE,QAAQ,CAAC,IAAI,GAAG,0BAA0B;CACzD,gBAAgB,MAAE,QAAQ,CAAC,QAAQ,QAAQ,IAAI,mBAAmB,QAAQ;CAC1E,aAAa,MACV,KAAK;EAAC;EAAe;EAAW;EAAM;CAAa,EAAC,CACpD,QACC;EAAC;EAAe;EAAW;EAAM;CAAa,EAAC,SAAS,QAAQ,IAAI,YAAY,GAAG,GAC9E,QAAQ,IAAI,WACb,cACL;CACH,UAAU,qBAAqB,QAAQ,CAAE,EAAC;CAC1C,MAAM,iBAAiB,QAAQ,CAAE,EAAC;CAClC,SAAS,oBAAoB,QAAQ,CAAE,EAAC;CACxC,gBAAgB,2BAA2B,QAAQ,CAAE,EAAC;CACtD,WAAW,sBAAsB,QAAQ,CAAE,EAAC;CAC5C,UAAU,qBAAqB,QAAQ,CAAE,EAAC;CAC1C,OAAO,kBAAkB,QAAQ,CAAE,EAAC;AACrC,EAAC;;AASF,SAAgB,YAAYA,OAAgD;CAC1E,MAAM,SAAS,uBAAuB,UAAU,MAAM;AAEtD,MAAK,OAAO,SAAS;EACnB,MAAM,SAAS,OAAO,MAAM,OACzB,IAAI,CAAC,OAAO,MAAM,EAAE,KAAK,KAAK,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,CACnD,KAAK,KAAK;AACb,QAAM,IAAI,OAAO,qCAAqC,OAAO;CAC9D;AAED,QAAO,OAAO;AACf;;AAGD,SAAgB,yBAAiC;AAC/C,QAAO,QAAQ,IAAI,+BAA+B;AACnD;;;;AC3HD,IAAIC,MAAsB;AAC1B,IAAI,gBAAgB;AAEpB,MAAM,oBAAoB;CAAC;CAAM;CAAW;CAAW;CAAU;CAAW;AAAgB;AAE5F,SAAS,uBAA6B;AAEpC,YAAW,YAAY,sBAAsB,QAAQ,UAAU,YAAa;AAE5E,MAAK,MAAM,OAAO,kBAChB,KAAI;EACF,MAAM,WAAW,QAAQ,QAAQ,IAAI;AACrC,MAAI,QAAQ,MAAM,UAChB,SAAQ,MACL,sBAAsB,IAAI,+KAG5B;CAEJ,QAAO,CAEP;AAEJ;;AAGD,SAAgB,aAAaC,SAA+B;AAC1D,KAAI,eAAe;AACjB,UAAQ,KAAK,iDAAiD;AAC9D;CACD;AAED,uBAAsB;CAEtB,MAAM,EACJ,aACA,gBACA,aACA,WAAW,wBAAwB,EACnC,sBAAsB,CAAE,GACxB,cACD,GAAG;CAEJ,MAAM,WAAW,sDAAuB;GACrC,yDAAoB;GACpB,4DAAuB;EACxB,0BAA0B;CAC3B,EAAC;CAEF,MAAM,gBAAgB,IAAI,2DAAkB,EAAE,KAAK,SAAU;CAC7D,MAAM,wBAAwB,2BAA2B,oBAAoB;AAe7E,MAAK,iBAAiB,QAAQ,IAAI,sBAChC,SAAQ,IAAI,wBAAwB;AAGtC,OAAM,IAAI,iCAAQ;EAChB;EACA;EACA,GAAI,eAAe,EAAE,eAAe,CAAC,YAAa,EAAE,IAAG,CAAE;EACzD,qBAAqB,CAAE;EACvB,kBAAkB,CAAC,4EAA4B,sBAAsB,AAAC;CACvE;AAED,KAAI,OAAO;AACX,iBAAgB;AACjB;AAED,SAAS,2BACPC,QACsC;CACtC,MAAMC,UAAkC;EACtC,MAAM;EACN,SAAS;EACT,SAAS;EACT,SAAS;EACT,IAAI;EACJ,OAAO;EACP,OAAO;EACP,SAAS;EACT,MAAM;EACN,IAAI;EACJ,KAAK;CACN;CAED,MAAMC,SAA+C,CAAE;AACvD,MAAK,MAAM,CAAC,KAAK,oBAAoB,IAAI,OAAO,QAAQ,QAAQ,EAAE;EAChE,MAAM,cAAc,OAAO;EAC3B,MAAM,eAAe,QAAQ,QAAQ,QAAQ;AAC7C,SAAO,uBAAuB,EAAE,SAAS,eAAe,aAAc;CACvE;AACD,QAAO;AACR;;AAGD,eAAsB,kBAAiC;AACrD,MAAK,IAAK;AACV,KAAI;AACF,QAAM,IAAI,UAAU;AACpB,kBAAgB;AAChB,QAAM;CACP,SAAQ,OAAO;AACd,UAAQ,MAAM,mDAAmD,MAAM;AACvE,QAAM;CACP;AACF;AAED,SAAgB,UAAUC,MAAsB;AAC9C,QAAO,0BAAM,UAAU,KAAK;AAC7B;AAED,SAAgB,gBAAwD;AACtE,QAAO,0BAAM,eAAe;AAC7B;AAED,SAAgB,kBAAyD;CACvE,MAAM,OAAO,0BAAM,eAAe;AAClC,MAAK,KAAM,QAAO,CAAE;CACpB,MAAM,MAAM,KAAK,aAAa;AAC9B,QAAO;EAAE,SAAS,IAAI;EAAS,QAAQ,IAAI;CAAQ;AACpD;AAED,SAAgB,mBAA4B;AAC1C,QAAO;AACR"}
|