@ciq-dev/neoiq-foundation-node 1.0.1-beta.2 → 1.0.1-beta.4
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 +95 -85
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +95 -85
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +165 -301
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +127 -245
- package/dist/index.mjs.map +1 -1
- package/dist/tracing-B8-Ntcll.js +312 -0
- package/dist/tracing-B8-Ntcll.js.map +1 -0
- package/dist/tracing-CcsyQIpB.mjs +192 -0
- package/dist/tracing-CcsyQIpB.mjs.map +1 -0
- package/package.json +12 -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-B8-Ntcll.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,43 @@ 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 method = (error.config?.method ?? "get").toUpperCase();
|
|
661
|
+
const IDEMPOTENT = new Set([
|
|
662
|
+
"GET",
|
|
663
|
+
"HEAD",
|
|
664
|
+
"OPTIONS",
|
|
665
|
+
"PUT",
|
|
666
|
+
"DELETE"
|
|
667
|
+
]);
|
|
668
|
+
if (!retry.retryNonIdempotent && !IDEMPOTENT.has(method)) return false;
|
|
669
|
+
const retryStatusCodes = retry.retryStatusCodes ?? [
|
|
670
|
+
408,
|
|
671
|
+
429,
|
|
672
|
+
500,
|
|
673
|
+
502,
|
|
674
|
+
503,
|
|
675
|
+
504
|
|
676
|
+
];
|
|
677
|
+
return !error.response || retryStatusCodes.includes(error.response?.status || 0);
|
|
678
|
+
},
|
|
679
|
+
onRetry: (retryCount, error, requestConfig) => {
|
|
680
|
+
logger.warn({
|
|
681
|
+
retryCount,
|
|
682
|
+
url: `${requestConfig.baseURL || ""}${requestConfig.url}`,
|
|
683
|
+
error: error.message
|
|
684
|
+
}, "Retrying request");
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
(0, axios_retry.default)(client, retryConfig);
|
|
688
|
+
}
|
|
689
|
+
if (cbOptions.enabled === true) {
|
|
690
|
+
const originalRequest = client.request.bind(client);
|
|
691
|
+
const breaker = new opossum.default(async (config) => originalRequest(config), {
|
|
870
692
|
timeout,
|
|
871
693
|
resetTimeout: cbOptions.resetTimeout ?? 3e4,
|
|
872
694
|
errorThresholdPercentage: cbOptions.errorThresholdPercentage ?? 50,
|
|
@@ -875,7 +697,6 @@ function createHttpClient(options) {
|
|
|
875
697
|
breaker.on("open", () => logger.warn({ targetService: serviceName }, "Circuit breaker OPEN"));
|
|
876
698
|
breaker.on("halfOpen", () => logger.info({ targetService: serviceName }, "Circuit breaker HALF-OPEN"));
|
|
877
699
|
breaker.on("close", () => logger.info({ targetService: serviceName }, "Circuit breaker CLOSED"));
|
|
878
|
-
const originalRequest = client.request.bind(client);
|
|
879
700
|
client.request = (config) => breaker.fire(config);
|
|
880
701
|
client.get = (url, config) => breaker.fire({
|
|
881
702
|
...config,
|
|
@@ -913,15 +734,20 @@ function createHttpClient(options) {
|
|
|
913
734
|
//#endregion
|
|
914
735
|
//#region src/foundation.ts
|
|
915
736
|
const deprecationWarnings = new Set();
|
|
916
|
-
function warnDeprecation(oldPath, newPath) {
|
|
737
|
+
function warnDeprecation(oldPath, newPath, logger) {
|
|
917
738
|
if (deprecationWarnings.has(oldPath)) return;
|
|
918
739
|
deprecationWarnings.add(oldPath);
|
|
919
|
-
|
|
740
|
+
const msg = `foundation.${oldPath}() is deprecated. Use foundation.${newPath}() instead. This alias will be removed in the next major version.`;
|
|
741
|
+
if (logger) logger.warn({
|
|
742
|
+
deprecated: oldPath,
|
|
743
|
+
replacement: newPath
|
|
744
|
+
}, msg);
|
|
745
|
+
else console.warn(`[neoiq-foundation] DEPRECATED: ${msg}`);
|
|
920
746
|
}
|
|
921
747
|
/** Create a fully configured observability foundation */
|
|
922
748
|
function createFoundation(input) {
|
|
923
749
|
const startTime = Date.now();
|
|
924
|
-
const config = parseConfig(input);
|
|
750
|
+
const config = require_tracing.parseConfig(input);
|
|
925
751
|
const { serviceName, serviceVersion, environment, features: featuresConfig, otel, logging: loggingConfig, requestLogging: requestLoggingConfig, redaction: redactionConfig, shutdown: shutdownConfig } = config;
|
|
926
752
|
const features = {
|
|
927
753
|
tracing: featuresConfig.tracing ?? true,
|
|
@@ -969,15 +795,14 @@ function createFoundation(input) {
|
|
|
969
795
|
let tracerInstance = null;
|
|
970
796
|
let tracingError;
|
|
971
797
|
if (features.tracing) try {
|
|
972
|
-
setupTracing({
|
|
798
|
+
require_tracing.setupTracing({
|
|
973
799
|
serviceName,
|
|
974
800
|
serviceVersion,
|
|
975
801
|
environment,
|
|
976
802
|
endpoint: otel.endpoint,
|
|
977
|
-
sampleRate: otel.traceSampleRate,
|
|
978
803
|
autoInstrumentation: featuresConfig.autoInstrumentation
|
|
979
804
|
});
|
|
980
|
-
tracerInstance = getTracer(serviceName);
|
|
805
|
+
tracerInstance = require_tracing.getTracer(serviceName);
|
|
981
806
|
logger.info({
|
|
982
807
|
feature: "tracing",
|
|
983
808
|
endpoint: otel.endpoint
|
|
@@ -1021,7 +846,7 @@ function createFoundation(input) {
|
|
|
1021
846
|
origin
|
|
1022
847
|
}, "Process crash detected, flushing telemetry");
|
|
1023
848
|
const flushPromises = [];
|
|
1024
|
-
if (features.tracing && isTracingEnabled()) flushPromises.push(shutdownTracing());
|
|
849
|
+
if (features.tracing && require_tracing.isTracingEnabled()) flushPromises.push(require_tracing.shutdownTracing());
|
|
1025
850
|
if (features.metrics && isMetricsEnabled()) flushPromises.push(shutdownMetrics());
|
|
1026
851
|
const timeout = new Promise((resolve) => setTimeout(resolve, flushTimeoutMs));
|
|
1027
852
|
Promise.race([Promise.allSettled(flushPromises), timeout]).finally(() => {
|
|
@@ -1033,7 +858,7 @@ function createFoundation(input) {
|
|
|
1033
858
|
logger.info({ flushTimeoutMs }, "Crash-flush handlers registered");
|
|
1034
859
|
}
|
|
1035
860
|
const traceInSpan = async (name, fn) => {
|
|
1036
|
-
const tracer = tracerInstance || getTracer(serviceName);
|
|
861
|
+
const tracer = tracerInstance || require_tracing.getTracer(serviceName);
|
|
1037
862
|
return new Promise((resolve, reject) => {
|
|
1038
863
|
tracer.startActiveSpan(name, async (span) => {
|
|
1039
864
|
try {
|
|
@@ -1062,10 +887,10 @@ function createFoundation(input) {
|
|
|
1062
887
|
logger,
|
|
1063
888
|
tracer: tracerInstance,
|
|
1064
889
|
meter: meterInstance,
|
|
1065
|
-
getTracer: (name) => getTracer(name || serviceName),
|
|
890
|
+
getTracer: (name) => require_tracing.getTracer(name || serviceName),
|
|
1066
891
|
getMeter: (name, version) => getMeter(name, version),
|
|
1067
|
-
getTraceContext,
|
|
1068
|
-
getActiveSpan,
|
|
892
|
+
getTraceContext: require_tracing.getTraceContext,
|
|
893
|
+
getActiveSpan: require_tracing.getActiveSpan,
|
|
1069
894
|
trace: traceInSpan
|
|
1070
895
|
};
|
|
1071
896
|
const httpModule = { createClient: (options) => createHttpClient({
|
|
@@ -1073,13 +898,15 @@ function createFoundation(input) {
|
|
|
1073
898
|
foundation
|
|
1074
899
|
}) };
|
|
1075
900
|
const buildHealthStatus = () => {
|
|
1076
|
-
const tracingUp = !features.tracing || !tracingError && isTracingEnabled();
|
|
901
|
+
const tracingUp = !features.tracing || !tracingError && require_tracing.isTracingEnabled();
|
|
1077
902
|
const metricsUp = !features.metrics || !metricsError && isMetricsEnabled();
|
|
1078
903
|
const loggingUp = !loggingError;
|
|
1079
904
|
const allUp = tracingUp && metricsUp && loggingUp;
|
|
1080
|
-
const
|
|
905
|
+
const allDown = (!tracingUp || !features.tracing) && (!metricsUp || !features.metrics) && !loggingUp;
|
|
906
|
+
let status = "healthy";
|
|
907
|
+
if (!allUp) status = allDown ? "unhealthy" : "degraded";
|
|
1081
908
|
return {
|
|
1082
|
-
status
|
|
909
|
+
status,
|
|
1083
910
|
timestamp: new Date().toISOString(),
|
|
1084
911
|
service: serviceName,
|
|
1085
912
|
version: serviceVersion,
|
|
@@ -1106,13 +933,13 @@ function createFoundation(input) {
|
|
|
1106
933
|
const shutdownFn = async () => {
|
|
1107
934
|
logger.info({}, "Shutting down foundation...");
|
|
1108
935
|
const promises = [];
|
|
1109
|
-
if (features.tracing && isTracingEnabled()) promises.push(shutdownTracing());
|
|
936
|
+
if (features.tracing && require_tracing.isTracingEnabled()) promises.push(require_tracing.shutdownTracing());
|
|
1110
937
|
if (features.metrics && isMetricsEnabled()) promises.push(shutdownMetrics());
|
|
1111
938
|
await Promise.all(promises);
|
|
1112
939
|
logger.info({}, "Foundation shutdown complete");
|
|
1113
940
|
};
|
|
1114
941
|
const isReadyFn = () => {
|
|
1115
|
-
if (features.tracing && !tracingError && !isTracingEnabled()) return false;
|
|
942
|
+
if (features.tracing && !tracingError && !require_tracing.isTracingEnabled()) return false;
|
|
1116
943
|
if (features.metrics && !metricsError && !isMetricsEnabled()) return false;
|
|
1117
944
|
return true;
|
|
1118
945
|
};
|
|
@@ -1153,43 +980,43 @@ function createFoundation(input) {
|
|
|
1153
980
|
tracer: tracerInstance,
|
|
1154
981
|
meter: meterInstance,
|
|
1155
982
|
getTracer: (name) => {
|
|
1156
|
-
warnDeprecation("getTracer", "observability.getTracer");
|
|
983
|
+
warnDeprecation("getTracer", "observability.getTracer", logger);
|
|
1157
984
|
return observability.getTracer(name);
|
|
1158
985
|
},
|
|
1159
986
|
getMeter: (name, version) => {
|
|
1160
|
-
warnDeprecation("getMeter", "observability.getMeter");
|
|
987
|
+
warnDeprecation("getMeter", "observability.getMeter", logger);
|
|
1161
988
|
return observability.getMeter(name, version);
|
|
1162
989
|
},
|
|
1163
990
|
getTraceContext: () => {
|
|
1164
|
-
warnDeprecation("getTraceContext", "observability.getTraceContext");
|
|
991
|
+
warnDeprecation("getTraceContext", "observability.getTraceContext", logger);
|
|
1165
992
|
return observability.getTraceContext();
|
|
1166
993
|
},
|
|
1167
994
|
getActiveSpan: () => {
|
|
1168
|
-
warnDeprecation("getActiveSpan", "observability.getActiveSpan");
|
|
995
|
+
warnDeprecation("getActiveSpan", "observability.getActiveSpan", logger);
|
|
1169
996
|
return observability.getActiveSpan();
|
|
1170
997
|
},
|
|
1171
998
|
createHttpClient: (options) => {
|
|
1172
|
-
warnDeprecation("createHttpClient", "http.createClient");
|
|
999
|
+
warnDeprecation("createHttpClient", "http.createClient", logger);
|
|
1173
1000
|
return httpModule.createClient(options);
|
|
1174
1001
|
},
|
|
1175
1002
|
shutdown: () => {
|
|
1176
|
-
warnDeprecation("shutdown", "lifecycle.shutdown");
|
|
1003
|
+
warnDeprecation("shutdown", "lifecycle.shutdown", logger);
|
|
1177
1004
|
return lifecycle.shutdown();
|
|
1178
1005
|
},
|
|
1179
1006
|
isReady: () => {
|
|
1180
|
-
warnDeprecation("isReady", "lifecycle.isReady");
|
|
1007
|
+
warnDeprecation("isReady", "lifecycle.isReady", logger);
|
|
1181
1008
|
return lifecycle.isReady();
|
|
1182
1009
|
},
|
|
1183
1010
|
health: () => {
|
|
1184
|
-
warnDeprecation("health", "lifecycle.health");
|
|
1011
|
+
warnDeprecation("health", "lifecycle.health", logger);
|
|
1185
1012
|
return lifecycle.health();
|
|
1186
1013
|
},
|
|
1187
1014
|
trace: (name, fn) => {
|
|
1188
|
-
warnDeprecation("trace", "observability.trace");
|
|
1015
|
+
warnDeprecation("trace", "observability.trace", logger);
|
|
1189
1016
|
return observability.trace(name, fn);
|
|
1190
1017
|
},
|
|
1191
1018
|
safeRun: (fn, fallback) => {
|
|
1192
|
-
warnDeprecation("safeRun", "lifecycle.safeRun");
|
|
1019
|
+
warnDeprecation("safeRun", "lifecycle.safeRun", logger);
|
|
1193
1020
|
return lifecycle.safeRun(fn, fallback);
|
|
1194
1021
|
}
|
|
1195
1022
|
};
|
|
@@ -1200,6 +1027,9 @@ const setupObservability = createFoundation;
|
|
|
1200
1027
|
|
|
1201
1028
|
//#endregion
|
|
1202
1029
|
//#region src/integrations/object-store/aws-s3.ts
|
|
1030
|
+
function isAwsError(err) {
|
|
1031
|
+
return typeof err === "object" && err !== null;
|
|
1032
|
+
}
|
|
1203
1033
|
async function loadAwsS3() {
|
|
1204
1034
|
try {
|
|
1205
1035
|
const clientMod = await import("@aws-sdk/client-s3");
|
|
@@ -1214,13 +1044,10 @@ async function loadAwsS3() {
|
|
|
1214
1044
|
getSignedUrl: presignerMod.getSignedUrl
|
|
1215
1045
|
};
|
|
1216
1046
|
} catch (err) {
|
|
1217
|
-
const e = err;
|
|
1047
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
1218
1048
|
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
1049
|
}
|
|
1220
1050
|
}
|
|
1221
|
-
function normalizeBody(body) {
|
|
1222
|
-
return body;
|
|
1223
|
-
}
|
|
1224
1051
|
/**
|
|
1225
1052
|
* AwsS3ObjectStore - wraps AWS S3 behind the provider-agnostic `ObjectStore` interface.
|
|
1226
1053
|
*
|
|
@@ -1249,7 +1076,7 @@ var AwsS3ObjectStore = class {
|
|
|
1249
1076
|
const res = await s3.send(new aws.PutObjectCommand({
|
|
1250
1077
|
Bucket: ref.bucket,
|
|
1251
1078
|
Key: ref.key,
|
|
1252
|
-
Body:
|
|
1079
|
+
Body: body,
|
|
1253
1080
|
ContentType: options.contentType,
|
|
1254
1081
|
CacheControl: options.cacheControl,
|
|
1255
1082
|
Metadata: options.metadata
|
|
@@ -1299,10 +1126,12 @@ var AwsS3ObjectStore = class {
|
|
|
1299
1126
|
lastModified: res.LastModified
|
|
1300
1127
|
};
|
|
1301
1128
|
} catch (err) {
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1129
|
+
if (isAwsError(err)) {
|
|
1130
|
+
const name = String(err.name ?? "");
|
|
1131
|
+
if (name === "NoSuchBucket") throw err;
|
|
1132
|
+
const httpStatus = err.$metadata?.httpStatusCode;
|
|
1133
|
+
if (httpStatus === 404 || name.includes("NotFound") || name.includes("NoSuchKey")) return { exists: false };
|
|
1134
|
+
}
|
|
1306
1135
|
throw err;
|
|
1307
1136
|
}
|
|
1308
1137
|
}
|
|
@@ -1323,8 +1152,9 @@ var AwsS3ObjectStore = class {
|
|
|
1323
1152
|
ContinuationToken: options.continuationToken,
|
|
1324
1153
|
MaxKeys: options.maxKeys
|
|
1325
1154
|
}));
|
|
1155
|
+
const contents = res?.Contents ?? [];
|
|
1326
1156
|
return {
|
|
1327
|
-
objects: (
|
|
1157
|
+
objects: contents.filter((o) => typeof o.Key === "string").map((o) => ({
|
|
1328
1158
|
key: o.Key,
|
|
1329
1159
|
size: o.Size,
|
|
1330
1160
|
etag: o.ETag,
|
|
@@ -1355,19 +1185,31 @@ var AwsS3ObjectStore = class {
|
|
|
1355
1185
|
|
|
1356
1186
|
//#endregion
|
|
1357
1187
|
//#region src/integrations/object-store/in-memory.ts
|
|
1188
|
+
const DEFAULT_MAX_OBJECTS = 1e4;
|
|
1189
|
+
const STREAM_TIMEOUT_MS = 3e4;
|
|
1358
1190
|
function toBuffer(body) {
|
|
1359
1191
|
if (typeof body === "string") return Buffer.from(body);
|
|
1360
1192
|
if (Buffer.isBuffer(body)) return body;
|
|
1361
1193
|
if (body instanceof Uint8Array) return Buffer.from(body);
|
|
1362
1194
|
return new Promise((resolve, reject) => {
|
|
1363
1195
|
const chunks = [];
|
|
1196
|
+
const timer = setTimeout(() => {
|
|
1197
|
+
body.destroy(new Error("InMemoryObjectStore: stream read timed out"));
|
|
1198
|
+
reject(new Error("InMemoryObjectStore: stream read timed out"));
|
|
1199
|
+
}, STREAM_TIMEOUT_MS);
|
|
1364
1200
|
body.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
|
|
1365
|
-
body.on("end", () =>
|
|
1366
|
-
|
|
1201
|
+
body.on("end", () => {
|
|
1202
|
+
clearTimeout(timer);
|
|
1203
|
+
resolve(Buffer.concat(chunks));
|
|
1204
|
+
});
|
|
1205
|
+
body.on("error", (err) => {
|
|
1206
|
+
clearTimeout(timer);
|
|
1207
|
+
reject(err);
|
|
1208
|
+
});
|
|
1367
1209
|
});
|
|
1368
1210
|
}
|
|
1369
|
-
function
|
|
1370
|
-
return `
|
|
1211
|
+
function computeEtag(buf) {
|
|
1212
|
+
return `"${(0, node_crypto.createHash)("md5").update(buf).digest("hex")}"`;
|
|
1371
1213
|
}
|
|
1372
1214
|
/**
|
|
1373
1215
|
* InMemoryObjectStore - useful for local dev, unit tests, and as a safe default.
|
|
@@ -1376,6 +1218,11 @@ function simpleEtag(buf) {
|
|
|
1376
1218
|
var InMemoryObjectStore = class {
|
|
1377
1219
|
provider = "in-memory";
|
|
1378
1220
|
buckets = new Map();
|
|
1221
|
+
maxObjects;
|
|
1222
|
+
objectCount = 0;
|
|
1223
|
+
constructor(options = {}) {
|
|
1224
|
+
this.maxObjects = options.maxObjects ?? DEFAULT_MAX_OBJECTS;
|
|
1225
|
+
}
|
|
1379
1226
|
bucketMap(bucket) {
|
|
1380
1227
|
let m = this.buckets.get(bucket);
|
|
1381
1228
|
if (!m) {
|
|
@@ -1386,15 +1233,19 @@ var InMemoryObjectStore = class {
|
|
|
1386
1233
|
}
|
|
1387
1234
|
async putObject(ref, body, options = {}) {
|
|
1388
1235
|
const buf = await toBuffer(body);
|
|
1236
|
+
const map = this.bucketMap(ref.bucket);
|
|
1237
|
+
const isNew = !map.has(ref.key);
|
|
1238
|
+
if (isNew && this.objectCount >= this.maxObjects) throw new Error(`InMemoryObjectStore: max object limit reached (${this.maxObjects})`);
|
|
1389
1239
|
const obj = {
|
|
1390
1240
|
body: buf,
|
|
1391
1241
|
contentType: options.contentType,
|
|
1392
1242
|
cacheControl: options.cacheControl,
|
|
1393
1243
|
metadata: options.metadata,
|
|
1394
|
-
etag:
|
|
1244
|
+
etag: computeEtag(buf),
|
|
1395
1245
|
lastModified: new Date()
|
|
1396
1246
|
};
|
|
1397
|
-
|
|
1247
|
+
map.set(ref.key, obj);
|
|
1248
|
+
if (isNew) this.objectCount++;
|
|
1398
1249
|
return { etag: obj.etag };
|
|
1399
1250
|
}
|
|
1400
1251
|
async getObject(ref) {
|
|
@@ -1426,18 +1277,31 @@ var InMemoryObjectStore = class {
|
|
|
1426
1277
|
};
|
|
1427
1278
|
}
|
|
1428
1279
|
async deleteObject(ref) {
|
|
1429
|
-
this.bucketMap(ref.bucket)
|
|
1280
|
+
const map = this.bucketMap(ref.bucket);
|
|
1281
|
+
if (map.delete(ref.key)) this.objectCount--;
|
|
1430
1282
|
}
|
|
1431
1283
|
async listObjects(options) {
|
|
1432
|
-
const { bucket, prefix = "" } = options;
|
|
1284
|
+
const { bucket, prefix = "", continuationToken } = options;
|
|
1285
|
+
const maxKeys = Math.max(1, Math.floor(options.maxKeys ?? 1e3));
|
|
1433
1286
|
const map = this.bucketMap(bucket);
|
|
1434
|
-
const
|
|
1287
|
+
const all = [...map.entries()].filter(([key]) => key.startsWith(prefix)).map(([key, obj]) => ({
|
|
1435
1288
|
key,
|
|
1436
1289
|
size: obj.body.length,
|
|
1437
1290
|
etag: obj.etag,
|
|
1438
1291
|
lastModified: obj.lastModified
|
|
1439
1292
|
})).sort((a, b) => a.key.localeCompare(b.key));
|
|
1440
|
-
|
|
1293
|
+
let startIndex = 0;
|
|
1294
|
+
if (continuationToken) {
|
|
1295
|
+
const idx = all.findIndex((o) => o.key === continuationToken);
|
|
1296
|
+
if (idx === -1) throw new Error(`InMemoryObjectStore: invalid continuationToken "${continuationToken}"`);
|
|
1297
|
+
startIndex = idx + 1;
|
|
1298
|
+
}
|
|
1299
|
+
const page = all.slice(startIndex, startIndex + maxKeys);
|
|
1300
|
+
const hasMore = startIndex + maxKeys < all.length;
|
|
1301
|
+
return {
|
|
1302
|
+
objects: page,
|
|
1303
|
+
nextContinuationToken: hasMore ? page[page.length - 1]?.key : void 0
|
|
1304
|
+
};
|
|
1441
1305
|
}
|
|
1442
1306
|
async presignGetObject(_ref, _options) {
|
|
1443
1307
|
throw new Error("InMemoryObjectStore does not support presigned URLs");
|
|
@@ -1448,17 +1312,17 @@ var InMemoryObjectStore = class {
|
|
|
1448
1312
|
};
|
|
1449
1313
|
|
|
1450
1314
|
//#endregion
|
|
1451
|
-
exports.AutoInstrumentationConfigSchema = AutoInstrumentationConfigSchema
|
|
1315
|
+
exports.AutoInstrumentationConfigSchema = require_tracing.AutoInstrumentationConfigSchema
|
|
1452
1316
|
exports.AwsS3ObjectStore = AwsS3ObjectStore
|
|
1453
|
-
exports.FeaturesConfigSchema = FeaturesConfigSchema
|
|
1454
|
-
exports.FoundationConfigSchema = FoundationConfigSchema
|
|
1317
|
+
exports.FeaturesConfigSchema = require_tracing.FeaturesConfigSchema
|
|
1318
|
+
exports.FoundationConfigSchema = require_tracing.FoundationConfigSchema
|
|
1455
1319
|
exports.InMemoryObjectStore = InMemoryObjectStore
|
|
1456
|
-
exports.LoggingConfigSchema = LoggingConfigSchema
|
|
1457
|
-
exports.OtelConfigSchema = OtelConfigSchema
|
|
1320
|
+
exports.LoggingConfigSchema = require_tracing.LoggingConfigSchema
|
|
1321
|
+
exports.OtelConfigSchema = require_tracing.OtelConfigSchema
|
|
1458
1322
|
exports.REDACT_PATHS = REDACT_PATHS
|
|
1459
|
-
exports.RedactionConfigSchema = RedactionConfigSchema
|
|
1460
|
-
exports.RequestLoggingConfigSchema = RequestLoggingConfigSchema
|
|
1461
|
-
exports.ShutdownConfigSchema = ShutdownConfigSchema
|
|
1323
|
+
exports.RedactionConfigSchema = require_tracing.RedactionConfigSchema
|
|
1324
|
+
exports.RequestLoggingConfigSchema = require_tracing.RequestLoggingConfigSchema
|
|
1325
|
+
exports.ShutdownConfigSchema = require_tracing.ShutdownConfigSchema
|
|
1462
1326
|
exports.SpanStatusCode = __opentelemetry_api.SpanStatusCode
|
|
1463
1327
|
exports.buildPinoRedactConfig = buildPinoRedactConfig
|
|
1464
1328
|
exports.context = __opentelemetry_api.context
|
|
@@ -1468,23 +1332,23 @@ exports.createFoundation = createFoundation
|
|
|
1468
1332
|
exports.createHttpClient = createHttpClient
|
|
1469
1333
|
exports.createLogger = createLogger
|
|
1470
1334
|
exports.createObservabilityPlugin = createObservabilityPlugin
|
|
1471
|
-
exports.getActiveSpan = getActiveSpan
|
|
1472
|
-
exports.getDefaultOtelEndpoint = getDefaultOtelEndpoint
|
|
1335
|
+
exports.getActiveSpan = require_tracing.getActiveSpan
|
|
1336
|
+
exports.getDefaultOtelEndpoint = require_tracing.getDefaultOtelEndpoint
|
|
1473
1337
|
exports.getGlobalLogger = getGlobalLogger
|
|
1474
1338
|
exports.getMeter = getMeter
|
|
1475
|
-
exports.getTraceContext = getTraceContext
|
|
1476
|
-
exports.getTracer = getTracer
|
|
1339
|
+
exports.getTraceContext = require_tracing.getTraceContext
|
|
1340
|
+
exports.getTracer = require_tracing.getTracer
|
|
1477
1341
|
exports.isMetricsEnabled = isMetricsEnabled
|
|
1478
|
-
exports.isTracingEnabled = isTracingEnabled
|
|
1342
|
+
exports.isTracingEnabled = require_tracing.isTracingEnabled
|
|
1479
1343
|
exports.metrics = __opentelemetry_api.metrics
|
|
1480
|
-
exports.parseConfig = parseConfig
|
|
1344
|
+
exports.parseConfig = require_tracing.parseConfig
|
|
1481
1345
|
exports.propagation = __opentelemetry_api.propagation
|
|
1482
1346
|
exports.sanitizeBody = sanitizeBody
|
|
1483
1347
|
exports.setGlobalLogger = setGlobalLogger
|
|
1484
1348
|
exports.setupMetrics = setupMetrics
|
|
1485
1349
|
exports.setupObservability = setupObservability
|
|
1486
|
-
exports.setupTracing = setupTracing
|
|
1350
|
+
exports.setupTracing = require_tracing.setupTracing
|
|
1487
1351
|
exports.shutdownMetrics = shutdownMetrics
|
|
1488
|
-
exports.shutdownTracing = shutdownTracing
|
|
1352
|
+
exports.shutdownTracing = require_tracing.shutdownTracing
|
|
1489
1353
|
exports.trace = __opentelemetry_api.trace
|
|
1490
1354
|
//# sourceMappingURL=index.js.map
|