@ciq-dev/neoiq-foundation-node 1.0.1-beta.2 → 1.0.1-beta.3
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.d.mts +1 -0
- package/dist/bootstrap.d.ts +1 -0
- package/dist/bootstrap.js +31 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/bootstrap.mjs +30 -0
- package/dist/bootstrap.mjs.map +1 -0
- package/dist/index.d.mts +77 -60
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +77 -60
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +154 -300
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +116 -244
- package/dist/index.mjs.map +1 -1
- package/dist/tracing-Cv-Y3fZx.mjs +196 -0
- package/dist/tracing-Cv-Y3fZx.mjs.map +1 -0
- package/dist/tracing-DM5OFo7l.js +316 -0
- package/dist/tracing-DM5OFo7l.js.map +1 -0
- package/package.json +13 -6
package/dist/index.js
CHANGED
|
@@ -1,123 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return to;
|
|
18
|
-
};
|
|
19
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
-
value: mod,
|
|
21
|
-
enumerable: true
|
|
22
|
-
}) : target, mod));
|
|
23
|
-
|
|
24
|
-
//#endregion
|
|
25
|
-
const zod = __toESM(require("zod"));
|
|
26
|
-
const async_hooks = __toESM(require("async_hooks"));
|
|
27
|
-
const __opentelemetry_api = __toESM(require("@opentelemetry/api"));
|
|
28
|
-
const pino = __toESM(require("pino"));
|
|
29
|
-
const __opentelemetry_sdk_node = __toESM(require("@opentelemetry/sdk-node"));
|
|
30
|
-
const __opentelemetry_auto_instrumentations_node = __toESM(require("@opentelemetry/auto-instrumentations-node"));
|
|
31
|
-
const __opentelemetry_exporter_trace_otlp_grpc = __toESM(require("@opentelemetry/exporter-trace-otlp-grpc"));
|
|
32
|
-
const __opentelemetry_resources = __toESM(require("@opentelemetry/resources"));
|
|
33
|
-
const __opentelemetry_semantic_conventions = __toESM(require("@opentelemetry/semantic-conventions"));
|
|
34
|
-
const __opentelemetry_exporter_metrics_otlp_grpc = __toESM(require("@opentelemetry/exporter-metrics-otlp-grpc"));
|
|
35
|
-
const __opentelemetry_sdk_metrics = __toESM(require("@opentelemetry/sdk-metrics"));
|
|
36
|
-
const fastify_plugin = __toESM(require("fastify-plugin"));
|
|
37
|
-
const crypto = __toESM(require("crypto"));
|
|
38
|
-
const axios = __toESM(require("axios"));
|
|
39
|
-
const axios_retry = __toESM(require("axios-retry"));
|
|
40
|
-
const opossum = __toESM(require("opossum"));
|
|
41
|
-
const node_stream = __toESM(require("node:stream"));
|
|
2
|
+
const require_tracing = require('./tracing-DM5OFo7l.js');
|
|
3
|
+
const __opentelemetry_resources = require_tracing.__toESM(require("@opentelemetry/resources"));
|
|
4
|
+
const __opentelemetry_semantic_conventions = require_tracing.__toESM(require("@opentelemetry/semantic-conventions"));
|
|
5
|
+
const __opentelemetry_api = require_tracing.__toESM(require("@opentelemetry/api"));
|
|
6
|
+
const async_hooks = require_tracing.__toESM(require("async_hooks"));
|
|
7
|
+
const pino = require_tracing.__toESM(require("pino"));
|
|
8
|
+
const __opentelemetry_exporter_metrics_otlp_grpc = require_tracing.__toESM(require("@opentelemetry/exporter-metrics-otlp-grpc"));
|
|
9
|
+
const __opentelemetry_sdk_metrics = require_tracing.__toESM(require("@opentelemetry/sdk-metrics"));
|
|
10
|
+
const fastify_plugin = require_tracing.__toESM(require("fastify-plugin"));
|
|
11
|
+
const crypto = require_tracing.__toESM(require("crypto"));
|
|
12
|
+
const axios = require_tracing.__toESM(require("axios"));
|
|
13
|
+
const axios_retry = require_tracing.__toESM(require("axios-retry"));
|
|
14
|
+
const opossum = require_tracing.__toESM(require("opossum"));
|
|
15
|
+
const node_crypto = require_tracing.__toESM(require("node:crypto"));
|
|
16
|
+
const node_stream = require_tracing.__toESM(require("node:stream"));
|
|
42
17
|
|
|
43
|
-
//#region src/config.ts
|
|
44
|
-
const AutoInstrumentationConfigSchema = zod.z.object({
|
|
45
|
-
http: zod.z.boolean().default(true),
|
|
46
|
-
fastify: zod.z.boolean().default(true),
|
|
47
|
-
express: zod.z.boolean().default(true),
|
|
48
|
-
mongodb: zod.z.boolean().default(true),
|
|
49
|
-
pg: zod.z.boolean().default(true),
|
|
50
|
-
mysql: zod.z.boolean().default(true),
|
|
51
|
-
redis: zod.z.boolean().default(true),
|
|
52
|
-
ioredis: zod.z.boolean().default(true),
|
|
53
|
-
grpc: zod.z.boolean().default(true),
|
|
54
|
-
fs: zod.z.boolean().default(false),
|
|
55
|
-
dns: zod.z.boolean().default(false)
|
|
56
|
-
}).partial();
|
|
57
|
-
const FeaturesConfigSchema = zod.z.object({
|
|
58
|
-
tracing: zod.z.boolean().default(true),
|
|
59
|
-
metrics: zod.z.boolean().default(true),
|
|
60
|
-
logging: zod.z.boolean().default(true),
|
|
61
|
-
autoInstrumentation: AutoInstrumentationConfigSchema.default({})
|
|
62
|
-
}).partial();
|
|
63
|
-
const DEFAULT_OTEL_ENDPOINT = "http://otel-stack-deployment-collector.observability.svc.cluster.local:4317";
|
|
64
|
-
const OtelConfigSchema = zod.z.object({
|
|
65
|
-
endpoint: zod.z.string().default(DEFAULT_OTEL_ENDPOINT),
|
|
66
|
-
metricsIntervalMs: zod.z.number().min(1e3).default(5e3),
|
|
67
|
-
traceSampleRate: zod.z.number().min(0).max(1).default(1)
|
|
68
|
-
}).partial();
|
|
69
|
-
const LoggingConfigSchema = zod.z.object({
|
|
70
|
-
level: zod.z.enum([
|
|
71
|
-
"debug",
|
|
72
|
-
"info",
|
|
73
|
-
"warn",
|
|
74
|
-
"error"
|
|
75
|
-
]).default("info"),
|
|
76
|
-
prettyPrint: zod.z.boolean().optional()
|
|
77
|
-
}).partial();
|
|
78
|
-
const RequestLoggingConfigSchema = zod.z.object({
|
|
79
|
-
logHeaders: zod.z.boolean().default(true),
|
|
80
|
-
logBody: zod.z.boolean().default(false),
|
|
81
|
-
logResponseBody: zod.z.boolean().default(false),
|
|
82
|
-
maxBodySize: zod.z.number().default(10 * 1024),
|
|
83
|
-
redactHeaders: zod.z.array(zod.z.string()).optional()
|
|
84
|
-
}).partial();
|
|
85
|
-
const RedactionConfigSchema = zod.z.object({ additionalPaths: zod.z.array(zod.z.string()).default([]) }).partial();
|
|
86
|
-
const ShutdownConfigSchema = zod.z.object({
|
|
87
|
-
flushOnCrash: zod.z.boolean().default(false),
|
|
88
|
-
flushTimeoutMs: zod.z.number().min(100).default(5e3)
|
|
89
|
-
}).partial();
|
|
90
|
-
const FoundationConfigSchema = zod.z.object({
|
|
91
|
-
serviceName: zod.z.string().min(1, "serviceName is required"),
|
|
92
|
-
serviceVersion: zod.z.string().default(process.env.SERVICE_VERSION || "1.0.0"),
|
|
93
|
-
environment: zod.z.enum([
|
|
94
|
-
"development",
|
|
95
|
-
"staging",
|
|
96
|
-
"qa",
|
|
97
|
-
"production"
|
|
98
|
-
]).default(process.env.NODE_ENV || "development"),
|
|
99
|
-
features: FeaturesConfigSchema.default({}),
|
|
100
|
-
otel: OtelConfigSchema.default({}),
|
|
101
|
-
logging: LoggingConfigSchema.default({}),
|
|
102
|
-
requestLogging: RequestLoggingConfigSchema.default({}),
|
|
103
|
-
redaction: RedactionConfigSchema.default({}),
|
|
104
|
-
shutdown: ShutdownConfigSchema.default({})
|
|
105
|
-
});
|
|
106
|
-
/** Parse and validate configuration */
|
|
107
|
-
function parseConfig(input) {
|
|
108
|
-
const result = FoundationConfigSchema.safeParse(input);
|
|
109
|
-
if (!result.success) {
|
|
110
|
-
const errors = result.error.errors.map((e) => ` - ${e.path.join(".")}: ${e.message}`).join("\n");
|
|
111
|
-
throw new Error(`Invalid foundation configuration:\n${errors}`);
|
|
112
|
-
}
|
|
113
|
-
return result.data;
|
|
114
|
-
}
|
|
115
|
-
/** Get default OTEL endpoint */
|
|
116
|
-
function getDefaultOtelEndpoint() {
|
|
117
|
-
return process.env.OTEL_EXPORTER_OTLP_ENDPOINT || DEFAULT_OTEL_ENDPOINT;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
//#endregion
|
|
121
18
|
//#region src/features/context.ts
|
|
122
19
|
const BAGGAGE_CORRELATION_KEY = "correlation.id";
|
|
123
20
|
function setBaggageCorrelationId(correlationId) {
|
|
@@ -404,12 +301,13 @@ function wrapPinoLogger(pinoLogger) {
|
|
|
404
301
|
};
|
|
405
302
|
}
|
|
406
303
|
/** Fallback logger when Pino is not available */
|
|
407
|
-
function createFallbackLogger(serviceName = "unknown") {
|
|
304
|
+
function createFallbackLogger(serviceName = "unknown", baseBindings = {}) {
|
|
408
305
|
const log = (level, obj, msg) => {
|
|
409
306
|
const logObj = {
|
|
410
307
|
timestamp: new Date().toISOString(),
|
|
411
308
|
level,
|
|
412
309
|
service: serviceName,
|
|
310
|
+
...baseBindings,
|
|
413
311
|
...obj,
|
|
414
312
|
msg
|
|
415
313
|
};
|
|
@@ -423,7 +321,10 @@ function createFallbackLogger(serviceName = "unknown") {
|
|
|
423
321
|
info: (obj, msg) => log("info", obj, msg),
|
|
424
322
|
warn: (obj, msg) => log("warn", obj, msg),
|
|
425
323
|
error: (obj, msg) => log("error", obj, msg),
|
|
426
|
-
child: () =>
|
|
324
|
+
child: (bindings) => createFallbackLogger(serviceName, {
|
|
325
|
+
...baseBindings,
|
|
326
|
+
...bindings
|
|
327
|
+
}),
|
|
427
328
|
pino: null
|
|
428
329
|
};
|
|
429
330
|
return fallback;
|
|
@@ -436,98 +337,6 @@ function getGlobalLogger() {
|
|
|
436
337
|
return globalLogger || createFallbackLogger();
|
|
437
338
|
}
|
|
438
339
|
|
|
439
|
-
//#endregion
|
|
440
|
-
//#region src/features/tracing.ts
|
|
441
|
-
let sdk = null;
|
|
442
|
-
let isInitialized$1 = false;
|
|
443
|
-
const MONITORED_MODULES = [
|
|
444
|
-
"pg",
|
|
445
|
-
"mongodb",
|
|
446
|
-
"ioredis",
|
|
447
|
-
"mysql2",
|
|
448
|
-
"express",
|
|
449
|
-
"@grpc/grpc-js"
|
|
450
|
-
];
|
|
451
|
-
function warnPreloadedModules() {
|
|
452
|
-
for (const mod of MONITORED_MODULES) try {
|
|
453
|
-
if (require.resolve(mod) in (require.cache || {})) console.warn(`[neoiq-foundation] "${mod}" was imported before tracing init. Auto-instrumentation may not work for this module. Use node -r @ciq-dev/neoiq-foundation-node/bootstrap or call createFoundation() first.`);
|
|
454
|
-
} catch {}
|
|
455
|
-
}
|
|
456
|
-
/** Initialize OpenTelemetry tracing */
|
|
457
|
-
function setupTracing(options) {
|
|
458
|
-
if (isInitialized$1) {
|
|
459
|
-
console.warn("[neoiq-foundation] Tracing already initialized");
|
|
460
|
-
return;
|
|
461
|
-
}
|
|
462
|
-
warnPreloadedModules();
|
|
463
|
-
const { serviceName, serviceVersion, environment, endpoint = getDefaultOtelEndpoint(), autoInstrumentation = {} } = options;
|
|
464
|
-
const resource = (0, __opentelemetry_resources.resourceFromAttributes)({
|
|
465
|
-
[__opentelemetry_semantic_conventions.ATTR_SERVICE_NAME]: serviceName,
|
|
466
|
-
[__opentelemetry_semantic_conventions.ATTR_SERVICE_VERSION]: serviceVersion,
|
|
467
|
-
"deployment.environment": environment
|
|
468
|
-
});
|
|
469
|
-
const traceExporter = new __opentelemetry_exporter_trace_otlp_grpc.OTLPTraceExporter({ url: endpoint });
|
|
470
|
-
const instrumentationConfig = buildInstrumentationConfig(autoInstrumentation);
|
|
471
|
-
sdk = new __opentelemetry_sdk_node.NodeSDK({
|
|
472
|
-
resource,
|
|
473
|
-
traceExporter,
|
|
474
|
-
instrumentations: [(0, __opentelemetry_auto_instrumentations_node.getNodeAutoInstrumentations)(instrumentationConfig)]
|
|
475
|
-
});
|
|
476
|
-
sdk.start();
|
|
477
|
-
isInitialized$1 = true;
|
|
478
|
-
}
|
|
479
|
-
function buildInstrumentationConfig(config) {
|
|
480
|
-
const mapping = {
|
|
481
|
-
http: "@opentelemetry/instrumentation-http",
|
|
482
|
-
fastify: "@opentelemetry/instrumentation-fastify",
|
|
483
|
-
express: "@opentelemetry/instrumentation-express",
|
|
484
|
-
mongodb: "@opentelemetry/instrumentation-mongodb",
|
|
485
|
-
pg: "@opentelemetry/instrumentation-pg",
|
|
486
|
-
mysql: "@opentelemetry/instrumentation-mysql",
|
|
487
|
-
redis: "@opentelemetry/instrumentation-redis",
|
|
488
|
-
ioredis: "@opentelemetry/instrumentation-ioredis",
|
|
489
|
-
grpc: "@opentelemetry/instrumentation-grpc",
|
|
490
|
-
fs: "@opentelemetry/instrumentation-fs",
|
|
491
|
-
dns: "@opentelemetry/instrumentation-dns"
|
|
492
|
-
};
|
|
493
|
-
const result = {};
|
|
494
|
-
for (const [key, instrumentationName] of Object.entries(mapping)) {
|
|
495
|
-
const userSetting = config[key];
|
|
496
|
-
const defaultValue = key !== "fs" && key !== "dns";
|
|
497
|
-
result[instrumentationName] = { enabled: userSetting ?? defaultValue };
|
|
498
|
-
}
|
|
499
|
-
return result;
|
|
500
|
-
}
|
|
501
|
-
/** Shutdown tracing gracefully */
|
|
502
|
-
async function shutdownTracing() {
|
|
503
|
-
if (!sdk) return;
|
|
504
|
-
try {
|
|
505
|
-
await sdk.shutdown();
|
|
506
|
-
isInitialized$1 = false;
|
|
507
|
-
sdk = null;
|
|
508
|
-
} catch (error) {
|
|
509
|
-
console.error("[neoiq-foundation] Error shutting down tracing:", error);
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
function getTracer(name) {
|
|
513
|
-
return __opentelemetry_api.trace.getTracer(name);
|
|
514
|
-
}
|
|
515
|
-
function getActiveSpan() {
|
|
516
|
-
return __opentelemetry_api.trace.getActiveSpan();
|
|
517
|
-
}
|
|
518
|
-
function getTraceContext() {
|
|
519
|
-
const span = __opentelemetry_api.trace.getActiveSpan();
|
|
520
|
-
if (!span) return {};
|
|
521
|
-
const ctx = span.spanContext();
|
|
522
|
-
return {
|
|
523
|
-
traceId: ctx.traceId,
|
|
524
|
-
spanId: ctx.spanId
|
|
525
|
-
};
|
|
526
|
-
}
|
|
527
|
-
function isTracingEnabled() {
|
|
528
|
-
return isInitialized$1;
|
|
529
|
-
}
|
|
530
|
-
|
|
531
340
|
//#endregion
|
|
532
341
|
//#region src/features/metrics.ts
|
|
533
342
|
let meterProvider = null;
|
|
@@ -538,7 +347,7 @@ function setupMetrics(options) {
|
|
|
538
347
|
console.warn("[neoiq-foundation] Metrics already initialized");
|
|
539
348
|
return;
|
|
540
349
|
}
|
|
541
|
-
const { serviceName, serviceVersion, environment, endpoint = getDefaultOtelEndpoint(), intervalMs = 5e3 } = options;
|
|
350
|
+
const { serviceName, serviceVersion, environment, endpoint = require_tracing.getDefaultOtelEndpoint(), intervalMs = 5e3 } = options;
|
|
542
351
|
const resource = (0, __opentelemetry_resources.resourceFromAttributes)({
|
|
543
352
|
[__opentelemetry_semantic_conventions.ATTR_SERVICE_NAME]: serviceName,
|
|
544
353
|
[__opentelemetry_semantic_conventions.ATTR_SERVICE_VERSION]: serviceVersion,
|
|
@@ -565,6 +374,7 @@ async function shutdownMetrics() {
|
|
|
565
374
|
meterProvider = null;
|
|
566
375
|
} catch (error) {
|
|
567
376
|
console.error("[neoiq-foundation] Error shutting down metrics:", error);
|
|
377
|
+
throw error;
|
|
568
378
|
}
|
|
569
379
|
}
|
|
570
380
|
function getMeter(name, version = "1.0.0") {
|
|
@@ -842,31 +652,34 @@ function createHttpClient(options) {
|
|
|
842
652
|
}
|
|
843
653
|
return Promise.reject(error);
|
|
844
654
|
});
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
655
|
+
if (retry.enabled !== false) {
|
|
656
|
+
const retryConfig = {
|
|
657
|
+
retries: retry.retries ?? 3,
|
|
658
|
+
retryDelay: (retryCount) => (retry.retryDelay ?? 1e3) * Math.pow(2, retryCount - 1),
|
|
659
|
+
retryCondition: (error) => {
|
|
660
|
+
const retryStatusCodes = retry.retryStatusCodes ?? [
|
|
661
|
+
408,
|
|
662
|
+
429,
|
|
663
|
+
500,
|
|
664
|
+
502,
|
|
665
|
+
503,
|
|
666
|
+
504
|
|
667
|
+
];
|
|
668
|
+
return !error.response || retryStatusCodes.includes(error.response?.status || 0);
|
|
669
|
+
},
|
|
670
|
+
onRetry: (retryCount, error, requestConfig) => {
|
|
671
|
+
logger.warn({
|
|
672
|
+
retryCount,
|
|
673
|
+
url: `${requestConfig.baseURL || ""}${requestConfig.url}`,
|
|
674
|
+
error: error.message
|
|
675
|
+
}, "Retrying request");
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
(0, axios_retry.default)(client, retryConfig);
|
|
679
|
+
}
|
|
680
|
+
if (cbOptions.enabled === true) {
|
|
681
|
+
const originalRequest = client.request.bind(client);
|
|
682
|
+
const breaker = new opossum.default(async (config) => originalRequest(config), {
|
|
870
683
|
timeout,
|
|
871
684
|
resetTimeout: cbOptions.resetTimeout ?? 3e4,
|
|
872
685
|
errorThresholdPercentage: cbOptions.errorThresholdPercentage ?? 50,
|
|
@@ -875,7 +688,6 @@ function createHttpClient(options) {
|
|
|
875
688
|
breaker.on("open", () => logger.warn({ targetService: serviceName }, "Circuit breaker OPEN"));
|
|
876
689
|
breaker.on("halfOpen", () => logger.info({ targetService: serviceName }, "Circuit breaker HALF-OPEN"));
|
|
877
690
|
breaker.on("close", () => logger.info({ targetService: serviceName }, "Circuit breaker CLOSED"));
|
|
878
|
-
const originalRequest = client.request.bind(client);
|
|
879
691
|
client.request = (config) => breaker.fire(config);
|
|
880
692
|
client.get = (url, config) => breaker.fire({
|
|
881
693
|
...config,
|
|
@@ -913,15 +725,20 @@ function createHttpClient(options) {
|
|
|
913
725
|
//#endregion
|
|
914
726
|
//#region src/foundation.ts
|
|
915
727
|
const deprecationWarnings = new Set();
|
|
916
|
-
function warnDeprecation(oldPath, newPath) {
|
|
728
|
+
function warnDeprecation(oldPath, newPath, logger) {
|
|
917
729
|
if (deprecationWarnings.has(oldPath)) return;
|
|
918
730
|
deprecationWarnings.add(oldPath);
|
|
919
|
-
|
|
731
|
+
const msg = `foundation.${oldPath}() is deprecated. Use foundation.${newPath}() instead. This alias will be removed in the next major version.`;
|
|
732
|
+
if (logger) logger.warn({
|
|
733
|
+
deprecated: oldPath,
|
|
734
|
+
replacement: newPath
|
|
735
|
+
}, msg);
|
|
736
|
+
else console.warn(`[neoiq-foundation] DEPRECATED: ${msg}`);
|
|
920
737
|
}
|
|
921
738
|
/** Create a fully configured observability foundation */
|
|
922
739
|
function createFoundation(input) {
|
|
923
740
|
const startTime = Date.now();
|
|
924
|
-
const config = parseConfig(input);
|
|
741
|
+
const config = require_tracing.parseConfig(input);
|
|
925
742
|
const { serviceName, serviceVersion, environment, features: featuresConfig, otel, logging: loggingConfig, requestLogging: requestLoggingConfig, redaction: redactionConfig, shutdown: shutdownConfig } = config;
|
|
926
743
|
const features = {
|
|
927
744
|
tracing: featuresConfig.tracing ?? true,
|
|
@@ -969,7 +786,7 @@ function createFoundation(input) {
|
|
|
969
786
|
let tracerInstance = null;
|
|
970
787
|
let tracingError;
|
|
971
788
|
if (features.tracing) try {
|
|
972
|
-
setupTracing({
|
|
789
|
+
require_tracing.setupTracing({
|
|
973
790
|
serviceName,
|
|
974
791
|
serviceVersion,
|
|
975
792
|
environment,
|
|
@@ -977,7 +794,7 @@ function createFoundation(input) {
|
|
|
977
794
|
sampleRate: otel.traceSampleRate,
|
|
978
795
|
autoInstrumentation: featuresConfig.autoInstrumentation
|
|
979
796
|
});
|
|
980
|
-
tracerInstance = getTracer(serviceName);
|
|
797
|
+
tracerInstance = require_tracing.getTracer(serviceName);
|
|
981
798
|
logger.info({
|
|
982
799
|
feature: "tracing",
|
|
983
800
|
endpoint: otel.endpoint
|
|
@@ -1021,7 +838,7 @@ function createFoundation(input) {
|
|
|
1021
838
|
origin
|
|
1022
839
|
}, "Process crash detected, flushing telemetry");
|
|
1023
840
|
const flushPromises = [];
|
|
1024
|
-
if (features.tracing && isTracingEnabled()) flushPromises.push(shutdownTracing());
|
|
841
|
+
if (features.tracing && require_tracing.isTracingEnabled()) flushPromises.push(require_tracing.shutdownTracing());
|
|
1025
842
|
if (features.metrics && isMetricsEnabled()) flushPromises.push(shutdownMetrics());
|
|
1026
843
|
const timeout = new Promise((resolve) => setTimeout(resolve, flushTimeoutMs));
|
|
1027
844
|
Promise.race([Promise.allSettled(flushPromises), timeout]).finally(() => {
|
|
@@ -1033,7 +850,7 @@ function createFoundation(input) {
|
|
|
1033
850
|
logger.info({ flushTimeoutMs }, "Crash-flush handlers registered");
|
|
1034
851
|
}
|
|
1035
852
|
const traceInSpan = async (name, fn) => {
|
|
1036
|
-
const tracer = tracerInstance || getTracer(serviceName);
|
|
853
|
+
const tracer = tracerInstance || require_tracing.getTracer(serviceName);
|
|
1037
854
|
return new Promise((resolve, reject) => {
|
|
1038
855
|
tracer.startActiveSpan(name, async (span) => {
|
|
1039
856
|
try {
|
|
@@ -1062,10 +879,10 @@ function createFoundation(input) {
|
|
|
1062
879
|
logger,
|
|
1063
880
|
tracer: tracerInstance,
|
|
1064
881
|
meter: meterInstance,
|
|
1065
|
-
getTracer: (name) => getTracer(name || serviceName),
|
|
882
|
+
getTracer: (name) => require_tracing.getTracer(name || serviceName),
|
|
1066
883
|
getMeter: (name, version) => getMeter(name, version),
|
|
1067
|
-
getTraceContext,
|
|
1068
|
-
getActiveSpan,
|
|
884
|
+
getTraceContext: require_tracing.getTraceContext,
|
|
885
|
+
getActiveSpan: require_tracing.getActiveSpan,
|
|
1069
886
|
trace: traceInSpan
|
|
1070
887
|
};
|
|
1071
888
|
const httpModule = { createClient: (options) => createHttpClient({
|
|
@@ -1073,13 +890,15 @@ function createFoundation(input) {
|
|
|
1073
890
|
foundation
|
|
1074
891
|
}) };
|
|
1075
892
|
const buildHealthStatus = () => {
|
|
1076
|
-
const tracingUp = !features.tracing || !tracingError && isTracingEnabled();
|
|
893
|
+
const tracingUp = !features.tracing || !tracingError && require_tracing.isTracingEnabled();
|
|
1077
894
|
const metricsUp = !features.metrics || !metricsError && isMetricsEnabled();
|
|
1078
895
|
const loggingUp = !loggingError;
|
|
1079
896
|
const allUp = tracingUp && metricsUp && loggingUp;
|
|
1080
|
-
const
|
|
897
|
+
const allDown = (!tracingUp || !features.tracing) && (!metricsUp || !features.metrics) && !loggingUp;
|
|
898
|
+
let status = "healthy";
|
|
899
|
+
if (!allUp) status = allDown ? "unhealthy" : "degraded";
|
|
1081
900
|
return {
|
|
1082
|
-
status
|
|
901
|
+
status,
|
|
1083
902
|
timestamp: new Date().toISOString(),
|
|
1084
903
|
service: serviceName,
|
|
1085
904
|
version: serviceVersion,
|
|
@@ -1106,13 +925,13 @@ function createFoundation(input) {
|
|
|
1106
925
|
const shutdownFn = async () => {
|
|
1107
926
|
logger.info({}, "Shutting down foundation...");
|
|
1108
927
|
const promises = [];
|
|
1109
|
-
if (features.tracing && isTracingEnabled()) promises.push(shutdownTracing());
|
|
928
|
+
if (features.tracing && require_tracing.isTracingEnabled()) promises.push(require_tracing.shutdownTracing());
|
|
1110
929
|
if (features.metrics && isMetricsEnabled()) promises.push(shutdownMetrics());
|
|
1111
930
|
await Promise.all(promises);
|
|
1112
931
|
logger.info({}, "Foundation shutdown complete");
|
|
1113
932
|
};
|
|
1114
933
|
const isReadyFn = () => {
|
|
1115
|
-
if (features.tracing && !tracingError && !isTracingEnabled()) return false;
|
|
934
|
+
if (features.tracing && !tracingError && !require_tracing.isTracingEnabled()) return false;
|
|
1116
935
|
if (features.metrics && !metricsError && !isMetricsEnabled()) return false;
|
|
1117
936
|
return true;
|
|
1118
937
|
};
|
|
@@ -1153,43 +972,43 @@ function createFoundation(input) {
|
|
|
1153
972
|
tracer: tracerInstance,
|
|
1154
973
|
meter: meterInstance,
|
|
1155
974
|
getTracer: (name) => {
|
|
1156
|
-
warnDeprecation("getTracer", "observability.getTracer");
|
|
975
|
+
warnDeprecation("getTracer", "observability.getTracer", logger);
|
|
1157
976
|
return observability.getTracer(name);
|
|
1158
977
|
},
|
|
1159
978
|
getMeter: (name, version) => {
|
|
1160
|
-
warnDeprecation("getMeter", "observability.getMeter");
|
|
979
|
+
warnDeprecation("getMeter", "observability.getMeter", logger);
|
|
1161
980
|
return observability.getMeter(name, version);
|
|
1162
981
|
},
|
|
1163
982
|
getTraceContext: () => {
|
|
1164
|
-
warnDeprecation("getTraceContext", "observability.getTraceContext");
|
|
983
|
+
warnDeprecation("getTraceContext", "observability.getTraceContext", logger);
|
|
1165
984
|
return observability.getTraceContext();
|
|
1166
985
|
},
|
|
1167
986
|
getActiveSpan: () => {
|
|
1168
|
-
warnDeprecation("getActiveSpan", "observability.getActiveSpan");
|
|
987
|
+
warnDeprecation("getActiveSpan", "observability.getActiveSpan", logger);
|
|
1169
988
|
return observability.getActiveSpan();
|
|
1170
989
|
},
|
|
1171
990
|
createHttpClient: (options) => {
|
|
1172
|
-
warnDeprecation("createHttpClient", "http.createClient");
|
|
991
|
+
warnDeprecation("createHttpClient", "http.createClient", logger);
|
|
1173
992
|
return httpModule.createClient(options);
|
|
1174
993
|
},
|
|
1175
994
|
shutdown: () => {
|
|
1176
|
-
warnDeprecation("shutdown", "lifecycle.shutdown");
|
|
995
|
+
warnDeprecation("shutdown", "lifecycle.shutdown", logger);
|
|
1177
996
|
return lifecycle.shutdown();
|
|
1178
997
|
},
|
|
1179
998
|
isReady: () => {
|
|
1180
|
-
warnDeprecation("isReady", "lifecycle.isReady");
|
|
999
|
+
warnDeprecation("isReady", "lifecycle.isReady", logger);
|
|
1181
1000
|
return lifecycle.isReady();
|
|
1182
1001
|
},
|
|
1183
1002
|
health: () => {
|
|
1184
|
-
warnDeprecation("health", "lifecycle.health");
|
|
1003
|
+
warnDeprecation("health", "lifecycle.health", logger);
|
|
1185
1004
|
return lifecycle.health();
|
|
1186
1005
|
},
|
|
1187
1006
|
trace: (name, fn) => {
|
|
1188
|
-
warnDeprecation("trace", "observability.trace");
|
|
1007
|
+
warnDeprecation("trace", "observability.trace", logger);
|
|
1189
1008
|
return observability.trace(name, fn);
|
|
1190
1009
|
},
|
|
1191
1010
|
safeRun: (fn, fallback) => {
|
|
1192
|
-
warnDeprecation("safeRun", "lifecycle.safeRun");
|
|
1011
|
+
warnDeprecation("safeRun", "lifecycle.safeRun", logger);
|
|
1193
1012
|
return lifecycle.safeRun(fn, fallback);
|
|
1194
1013
|
}
|
|
1195
1014
|
};
|
|
@@ -1200,6 +1019,9 @@ const setupObservability = createFoundation;
|
|
|
1200
1019
|
|
|
1201
1020
|
//#endregion
|
|
1202
1021
|
//#region src/integrations/object-store/aws-s3.ts
|
|
1022
|
+
function isAwsError(err) {
|
|
1023
|
+
return typeof err === "object" && err !== null;
|
|
1024
|
+
}
|
|
1203
1025
|
async function loadAwsS3() {
|
|
1204
1026
|
try {
|
|
1205
1027
|
const clientMod = await import("@aws-sdk/client-s3");
|
|
@@ -1214,13 +1036,10 @@ async function loadAwsS3() {
|
|
|
1214
1036
|
getSignedUrl: presignerMod.getSignedUrl
|
|
1215
1037
|
};
|
|
1216
1038
|
} catch (err) {
|
|
1217
|
-
const e = err;
|
|
1039
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
1218
1040
|
throw new Error(`AWS SDK not available. Install optional peer deps: @aws-sdk/client-s3 and @aws-sdk/s3-request-presigner. Original error: ${e.message}`);
|
|
1219
1041
|
}
|
|
1220
1042
|
}
|
|
1221
|
-
function normalizeBody(body) {
|
|
1222
|
-
return body;
|
|
1223
|
-
}
|
|
1224
1043
|
/**
|
|
1225
1044
|
* AwsS3ObjectStore - wraps AWS S3 behind the provider-agnostic `ObjectStore` interface.
|
|
1226
1045
|
*
|
|
@@ -1249,7 +1068,7 @@ var AwsS3ObjectStore = class {
|
|
|
1249
1068
|
const res = await s3.send(new aws.PutObjectCommand({
|
|
1250
1069
|
Bucket: ref.bucket,
|
|
1251
1070
|
Key: ref.key,
|
|
1252
|
-
Body:
|
|
1071
|
+
Body: body,
|
|
1253
1072
|
ContentType: options.contentType,
|
|
1254
1073
|
CacheControl: options.cacheControl,
|
|
1255
1074
|
Metadata: options.metadata
|
|
@@ -1299,10 +1118,11 @@ var AwsS3ObjectStore = class {
|
|
|
1299
1118
|
lastModified: res.LastModified
|
|
1300
1119
|
};
|
|
1301
1120
|
} catch (err) {
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1121
|
+
if (isAwsError(err)) {
|
|
1122
|
+
const name = String(err.name ?? "");
|
|
1123
|
+
const httpStatus = err.$metadata?.httpStatusCode;
|
|
1124
|
+
if (httpStatus === 404 || name.includes("NotFound") || name.includes("NoSuchKey")) return { exists: false };
|
|
1125
|
+
}
|
|
1306
1126
|
throw err;
|
|
1307
1127
|
}
|
|
1308
1128
|
}
|
|
@@ -1323,8 +1143,9 @@ var AwsS3ObjectStore = class {
|
|
|
1323
1143
|
ContinuationToken: options.continuationToken,
|
|
1324
1144
|
MaxKeys: options.maxKeys
|
|
1325
1145
|
}));
|
|
1146
|
+
const contents = res?.Contents ?? [];
|
|
1326
1147
|
return {
|
|
1327
|
-
objects: (
|
|
1148
|
+
objects: contents.filter((o) => typeof o.Key === "string").map((o) => ({
|
|
1328
1149
|
key: o.Key,
|
|
1329
1150
|
size: o.Size,
|
|
1330
1151
|
etag: o.ETag,
|
|
@@ -1355,19 +1176,31 @@ var AwsS3ObjectStore = class {
|
|
|
1355
1176
|
|
|
1356
1177
|
//#endregion
|
|
1357
1178
|
//#region src/integrations/object-store/in-memory.ts
|
|
1179
|
+
const DEFAULT_MAX_OBJECTS = 1e4;
|
|
1180
|
+
const STREAM_TIMEOUT_MS = 3e4;
|
|
1358
1181
|
function toBuffer(body) {
|
|
1359
1182
|
if (typeof body === "string") return Buffer.from(body);
|
|
1360
1183
|
if (Buffer.isBuffer(body)) return body;
|
|
1361
1184
|
if (body instanceof Uint8Array) return Buffer.from(body);
|
|
1362
1185
|
return new Promise((resolve, reject) => {
|
|
1363
1186
|
const chunks = [];
|
|
1187
|
+
const timer = setTimeout(() => {
|
|
1188
|
+
body.destroy(new Error("InMemoryObjectStore: stream read timed out"));
|
|
1189
|
+
reject(new Error("InMemoryObjectStore: stream read timed out"));
|
|
1190
|
+
}, STREAM_TIMEOUT_MS);
|
|
1364
1191
|
body.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
|
|
1365
|
-
body.on("end", () =>
|
|
1366
|
-
|
|
1192
|
+
body.on("end", () => {
|
|
1193
|
+
clearTimeout(timer);
|
|
1194
|
+
resolve(Buffer.concat(chunks));
|
|
1195
|
+
});
|
|
1196
|
+
body.on("error", (err) => {
|
|
1197
|
+
clearTimeout(timer);
|
|
1198
|
+
reject(err);
|
|
1199
|
+
});
|
|
1367
1200
|
});
|
|
1368
1201
|
}
|
|
1369
|
-
function
|
|
1370
|
-
return `
|
|
1202
|
+
function computeEtag(buf) {
|
|
1203
|
+
return `"${(0, node_crypto.createHash)("md5").update(buf).digest("hex")}"`;
|
|
1371
1204
|
}
|
|
1372
1205
|
/**
|
|
1373
1206
|
* InMemoryObjectStore - useful for local dev, unit tests, and as a safe default.
|
|
@@ -1376,6 +1209,11 @@ function simpleEtag(buf) {
|
|
|
1376
1209
|
var InMemoryObjectStore = class {
|
|
1377
1210
|
provider = "in-memory";
|
|
1378
1211
|
buckets = new Map();
|
|
1212
|
+
maxObjects;
|
|
1213
|
+
objectCount = 0;
|
|
1214
|
+
constructor(options = {}) {
|
|
1215
|
+
this.maxObjects = options.maxObjects ?? DEFAULT_MAX_OBJECTS;
|
|
1216
|
+
}
|
|
1379
1217
|
bucketMap(bucket) {
|
|
1380
1218
|
let m = this.buckets.get(bucket);
|
|
1381
1219
|
if (!m) {
|
|
@@ -1385,16 +1223,20 @@ var InMemoryObjectStore = class {
|
|
|
1385
1223
|
return m;
|
|
1386
1224
|
}
|
|
1387
1225
|
async putObject(ref, body, options = {}) {
|
|
1226
|
+
const map = this.bucketMap(ref.bucket);
|
|
1227
|
+
const isNew = !map.has(ref.key);
|
|
1228
|
+
if (isNew && this.objectCount >= this.maxObjects) throw new Error(`InMemoryObjectStore: max object limit reached (${this.maxObjects})`);
|
|
1388
1229
|
const buf = await toBuffer(body);
|
|
1389
1230
|
const obj = {
|
|
1390
1231
|
body: buf,
|
|
1391
1232
|
contentType: options.contentType,
|
|
1392
1233
|
cacheControl: options.cacheControl,
|
|
1393
1234
|
metadata: options.metadata,
|
|
1394
|
-
etag:
|
|
1235
|
+
etag: computeEtag(buf),
|
|
1395
1236
|
lastModified: new Date()
|
|
1396
1237
|
};
|
|
1397
|
-
|
|
1238
|
+
map.set(ref.key, obj);
|
|
1239
|
+
if (isNew) this.objectCount++;
|
|
1398
1240
|
return { etag: obj.etag };
|
|
1399
1241
|
}
|
|
1400
1242
|
async getObject(ref) {
|
|
@@ -1426,18 +1268,30 @@ var InMemoryObjectStore = class {
|
|
|
1426
1268
|
};
|
|
1427
1269
|
}
|
|
1428
1270
|
async deleteObject(ref) {
|
|
1429
|
-
this.bucketMap(ref.bucket)
|
|
1271
|
+
const map = this.bucketMap(ref.bucket);
|
|
1272
|
+
if (map.delete(ref.key)) this.objectCount--;
|
|
1430
1273
|
}
|
|
1431
1274
|
async listObjects(options) {
|
|
1432
|
-
const { bucket, prefix = "" } = options;
|
|
1275
|
+
const { bucket, prefix = "", continuationToken, maxKeys = 1e3 } = options;
|
|
1433
1276
|
const map = this.bucketMap(bucket);
|
|
1434
|
-
const
|
|
1277
|
+
const all = [...map.entries()].filter(([key]) => key.startsWith(prefix)).map(([key, obj]) => ({
|
|
1435
1278
|
key,
|
|
1436
1279
|
size: obj.body.length,
|
|
1437
1280
|
etag: obj.etag,
|
|
1438
1281
|
lastModified: obj.lastModified
|
|
1439
1282
|
})).sort((a, b) => a.key.localeCompare(b.key));
|
|
1440
|
-
|
|
1283
|
+
let startIndex = 0;
|
|
1284
|
+
if (continuationToken) {
|
|
1285
|
+
const idx = all.findIndex((o) => o.key === continuationToken);
|
|
1286
|
+
if (idx === -1) throw new Error(`InMemoryObjectStore: invalid continuationToken "${continuationToken}"`);
|
|
1287
|
+
startIndex = idx + 1;
|
|
1288
|
+
}
|
|
1289
|
+
const page = all.slice(startIndex, startIndex + maxKeys);
|
|
1290
|
+
const hasMore = startIndex + maxKeys < all.length;
|
|
1291
|
+
return {
|
|
1292
|
+
objects: page,
|
|
1293
|
+
nextContinuationToken: hasMore ? page[page.length - 1]?.key : void 0
|
|
1294
|
+
};
|
|
1441
1295
|
}
|
|
1442
1296
|
async presignGetObject(_ref, _options) {
|
|
1443
1297
|
throw new Error("InMemoryObjectStore does not support presigned URLs");
|
|
@@ -1448,17 +1302,17 @@ var InMemoryObjectStore = class {
|
|
|
1448
1302
|
};
|
|
1449
1303
|
|
|
1450
1304
|
//#endregion
|
|
1451
|
-
exports.AutoInstrumentationConfigSchema = AutoInstrumentationConfigSchema
|
|
1305
|
+
exports.AutoInstrumentationConfigSchema = require_tracing.AutoInstrumentationConfigSchema
|
|
1452
1306
|
exports.AwsS3ObjectStore = AwsS3ObjectStore
|
|
1453
|
-
exports.FeaturesConfigSchema = FeaturesConfigSchema
|
|
1454
|
-
exports.FoundationConfigSchema = FoundationConfigSchema
|
|
1307
|
+
exports.FeaturesConfigSchema = require_tracing.FeaturesConfigSchema
|
|
1308
|
+
exports.FoundationConfigSchema = require_tracing.FoundationConfigSchema
|
|
1455
1309
|
exports.InMemoryObjectStore = InMemoryObjectStore
|
|
1456
|
-
exports.LoggingConfigSchema = LoggingConfigSchema
|
|
1457
|
-
exports.OtelConfigSchema = OtelConfigSchema
|
|
1310
|
+
exports.LoggingConfigSchema = require_tracing.LoggingConfigSchema
|
|
1311
|
+
exports.OtelConfigSchema = require_tracing.OtelConfigSchema
|
|
1458
1312
|
exports.REDACT_PATHS = REDACT_PATHS
|
|
1459
|
-
exports.RedactionConfigSchema = RedactionConfigSchema
|
|
1460
|
-
exports.RequestLoggingConfigSchema = RequestLoggingConfigSchema
|
|
1461
|
-
exports.ShutdownConfigSchema = ShutdownConfigSchema
|
|
1313
|
+
exports.RedactionConfigSchema = require_tracing.RedactionConfigSchema
|
|
1314
|
+
exports.RequestLoggingConfigSchema = require_tracing.RequestLoggingConfigSchema
|
|
1315
|
+
exports.ShutdownConfigSchema = require_tracing.ShutdownConfigSchema
|
|
1462
1316
|
exports.SpanStatusCode = __opentelemetry_api.SpanStatusCode
|
|
1463
1317
|
exports.buildPinoRedactConfig = buildPinoRedactConfig
|
|
1464
1318
|
exports.context = __opentelemetry_api.context
|
|
@@ -1468,23 +1322,23 @@ exports.createFoundation = createFoundation
|
|
|
1468
1322
|
exports.createHttpClient = createHttpClient
|
|
1469
1323
|
exports.createLogger = createLogger
|
|
1470
1324
|
exports.createObservabilityPlugin = createObservabilityPlugin
|
|
1471
|
-
exports.getActiveSpan = getActiveSpan
|
|
1472
|
-
exports.getDefaultOtelEndpoint = getDefaultOtelEndpoint
|
|
1325
|
+
exports.getActiveSpan = require_tracing.getActiveSpan
|
|
1326
|
+
exports.getDefaultOtelEndpoint = require_tracing.getDefaultOtelEndpoint
|
|
1473
1327
|
exports.getGlobalLogger = getGlobalLogger
|
|
1474
1328
|
exports.getMeter = getMeter
|
|
1475
|
-
exports.getTraceContext = getTraceContext
|
|
1476
|
-
exports.getTracer = getTracer
|
|
1329
|
+
exports.getTraceContext = require_tracing.getTraceContext
|
|
1330
|
+
exports.getTracer = require_tracing.getTracer
|
|
1477
1331
|
exports.isMetricsEnabled = isMetricsEnabled
|
|
1478
|
-
exports.isTracingEnabled = isTracingEnabled
|
|
1332
|
+
exports.isTracingEnabled = require_tracing.isTracingEnabled
|
|
1479
1333
|
exports.metrics = __opentelemetry_api.metrics
|
|
1480
|
-
exports.parseConfig = parseConfig
|
|
1334
|
+
exports.parseConfig = require_tracing.parseConfig
|
|
1481
1335
|
exports.propagation = __opentelemetry_api.propagation
|
|
1482
1336
|
exports.sanitizeBody = sanitizeBody
|
|
1483
1337
|
exports.setGlobalLogger = setGlobalLogger
|
|
1484
1338
|
exports.setupMetrics = setupMetrics
|
|
1485
1339
|
exports.setupObservability = setupObservability
|
|
1486
|
-
exports.setupTracing = setupTracing
|
|
1340
|
+
exports.setupTracing = require_tracing.setupTracing
|
|
1487
1341
|
exports.shutdownMetrics = shutdownMetrics
|
|
1488
|
-
exports.shutdownTracing = shutdownTracing
|
|
1342
|
+
exports.shutdownTracing = require_tracing.shutdownTracing
|
|
1489
1343
|
exports.trace = __opentelemetry_api.trace
|
|
1490
1344
|
//# sourceMappingURL=index.js.map
|