@ciq-dev/neoiq-foundation-node 1.0.3-0 → 1.1.0-beta.0
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.js.map +1 -1
- package/dist/bootstrap.mjs +1 -1
- package/dist/bootstrap.mjs.map +1 -1
- package/dist/index.d.mts +94 -119
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +94 -119
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +88 -117
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +89 -117
- package/dist/index.mjs.map +1 -1
- package/dist/{tracing-C3oniP3X.mjs → tracing-DUFkYSRe.mjs} +12 -21
- package/dist/tracing-DUFkYSRe.mjs.map +1 -0
- package/dist/{tracing-C0FkkeKE.js → tracing-MhJ1fEY_.js} +11 -26
- package/dist/tracing-MhJ1fEY_.js.map +1 -0
- package/package.json +1 -12
- package/dist/tracing-C0FkkeKE.js.map +0 -1
- package/dist/tracing-C3oniP3X.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AutoInstrumentationConfigSchema, FeaturesConfigSchema, FoundationConfigSchema, LoggingConfigSchema, OtelConfigSchema, RedactionConfigSchema, RequestLoggingConfigSchema, ShutdownConfigSchema, SpanStatusCode,
|
|
1
|
+
import { AutoInstrumentationConfigSchema, FeaturesConfigSchema, FoundationConfigSchema, LoggingConfigSchema, OtelConfigSchema, RedactionConfigSchema, RequestLoggingConfigSchema, ShutdownConfigSchema, SpanStatusCode, VaultConfigSchema, context, getActiveSpan, getDefaultOtelEndpoint, getTraceContext, getTracer, isTracingEnabled, parseConfig, propagation, setupTracing, shutdownTracing, trace } from "./tracing-DUFkYSRe.mjs";
|
|
2
2
|
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
3
3
|
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
|
|
4
4
|
import { SpanStatusCode as SpanStatusCode$1, context as context$1, metrics, propagation as propagation$1, trace as trace$1 } from "@opentelemetry/api";
|
|
@@ -733,111 +733,100 @@ function createHttpClient(options) {
|
|
|
733
733
|
|
|
734
734
|
//#endregion
|
|
735
735
|
//#region src/features/vault.ts
|
|
736
|
+
const DEFAULTS = {
|
|
737
|
+
address: "https://vault.beta.commerceiq.ai",
|
|
738
|
+
authMethod: "kubernetes",
|
|
739
|
+
mountPoint: "secret",
|
|
740
|
+
tokenPath: "/var/run/secrets/kubernetes.io/serviceaccount/token",
|
|
741
|
+
k8sAuthMount: "kubernetes"
|
|
742
|
+
};
|
|
743
|
+
function stripSlashes(value) {
|
|
744
|
+
return value.replace(/^\/+|\/+$/g, "");
|
|
745
|
+
}
|
|
736
746
|
async function loadNodeVault() {
|
|
737
747
|
try {
|
|
738
748
|
const mod = await import("node-vault");
|
|
739
749
|
return mod.default ?? mod;
|
|
740
750
|
} catch {
|
|
741
|
-
throw new Error("node-vault is not installed. Install
|
|
751
|
+
throw new Error("node-vault is not installed. Install it as a peer dependency: npm install node-vault");
|
|
742
752
|
}
|
|
743
753
|
}
|
|
744
754
|
var VaultClient = class {
|
|
745
|
-
|
|
755
|
+
logger;
|
|
756
|
+
address;
|
|
757
|
+
authMethod;
|
|
758
|
+
role;
|
|
759
|
+
mountPoint;
|
|
760
|
+
tokenPath;
|
|
761
|
+
k8sAuthMount;
|
|
762
|
+
namespace;
|
|
746
763
|
clientPromise;
|
|
747
764
|
authenticated = false;
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
logger;
|
|
751
|
-
cacheEnabled;
|
|
752
|
-
cacheTtlMs;
|
|
765
|
+
/** In-flight auth so concurrent getSecret() calls don't all trigger kubernetesLogin. */
|
|
766
|
+
authPromise = null;
|
|
753
767
|
constructor(options) {
|
|
754
|
-
this.config = {
|
|
755
|
-
address: "https://vault.beta.commerceiq.ai/",
|
|
756
|
-
authMethod: "kubernetes",
|
|
757
|
-
role: "",
|
|
758
|
-
mountPoint: "secret",
|
|
759
|
-
tokenPath: "/var/run/secrets/kubernetes.io/serviceaccount/token",
|
|
760
|
-
k8sAuthMount: "kubernetes",
|
|
761
|
-
...options.config
|
|
762
|
-
};
|
|
763
768
|
this.logger = options.logger;
|
|
764
|
-
|
|
765
|
-
this.
|
|
766
|
-
this.
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
769
|
+
const cfg = options.config;
|
|
770
|
+
this.address = (cfg.address ?? DEFAULTS.address).replace(/\/+$/, "");
|
|
771
|
+
this.authMethod = cfg.authMethod ?? DEFAULTS.authMethod;
|
|
772
|
+
this.role = cfg.role;
|
|
773
|
+
this.mountPoint = stripSlashes(cfg.mountPoint ?? DEFAULTS.mountPoint);
|
|
774
|
+
this.tokenPath = cfg.tokenPath ?? DEFAULTS.tokenPath;
|
|
775
|
+
this.k8sAuthMount = cfg.k8sAuthMount ?? DEFAULTS.k8sAuthMount;
|
|
776
|
+
this.namespace = cfg.namespace;
|
|
777
|
+
const factory = options.nodeVaultFactory ? Promise.resolve(options.nodeVaultFactory) : loadNodeVault();
|
|
778
|
+
this.clientPromise = factory.then((create) => create({
|
|
771
779
|
apiVersion: "v1",
|
|
772
|
-
endpoint: this.
|
|
773
|
-
namespace: this.
|
|
774
|
-
});
|
|
775
|
-
this.client = client;
|
|
776
|
-
return client;
|
|
780
|
+
endpoint: this.address,
|
|
781
|
+
namespace: this.namespace
|
|
782
|
+
}));
|
|
777
783
|
}
|
|
778
784
|
async authenticate() {
|
|
779
785
|
if (this.authenticated) return;
|
|
786
|
+
if (this.authPromise) return this.authPromise;
|
|
787
|
+
this.authPromise = this.doAuthenticate().finally(() => {
|
|
788
|
+
if (!this.authenticated) this.authPromise = null;
|
|
789
|
+
});
|
|
790
|
+
return this.authPromise;
|
|
791
|
+
}
|
|
792
|
+
async doAuthenticate() {
|
|
780
793
|
const client = await this.clientPromise;
|
|
781
|
-
if (this.
|
|
794
|
+
if (this.authMethod === "token") {
|
|
782
795
|
const token = process.env.VAULT_TOKEN;
|
|
783
|
-
if (!token) throw new Error("VAULT_TOKEN
|
|
796
|
+
if (!token) throw new Error("VAULT_TOKEN env var is required for token auth");
|
|
784
797
|
client.token = token;
|
|
785
798
|
this.authenticated = true;
|
|
786
799
|
this.logger.info({ authMethod: "token" }, "Vault authenticated via token");
|
|
787
800
|
return;
|
|
788
801
|
}
|
|
789
|
-
|
|
790
|
-
if (!role) throw new Error("Vault role is required for Kubernetes authentication. Set vault.role in config or VAULT_AUTH_ROLE env var.");
|
|
802
|
+
if (!this.role) throw new Error("Vault role is required for Kubernetes auth (set vault.role or VAULT_AUTH_ROLE)");
|
|
791
803
|
let jwt;
|
|
792
804
|
try {
|
|
793
|
-
jwt = await readFile(this.
|
|
805
|
+
jwt = await readFile(this.tokenPath, "utf8");
|
|
794
806
|
} catch (err) {
|
|
795
|
-
const
|
|
796
|
-
throw new Error(`Failed to read
|
|
807
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
808
|
+
throw new Error(`Failed to read service-account token at ${this.tokenPath}: ${msg}`);
|
|
797
809
|
}
|
|
798
810
|
const result = await client.kubernetesLogin({
|
|
799
|
-
role,
|
|
811
|
+
role: this.role,
|
|
800
812
|
jwt,
|
|
801
|
-
mount_point: this.
|
|
813
|
+
mount_point: this.k8sAuthMount
|
|
802
814
|
});
|
|
803
815
|
client.token = result.auth.client_token;
|
|
804
816
|
this.authenticated = true;
|
|
805
817
|
this.logger.info({
|
|
806
818
|
authMethod: "kubernetes",
|
|
807
|
-
role,
|
|
819
|
+
role: this.role,
|
|
808
820
|
leaseDuration: result.auth.lease_duration
|
|
809
821
|
}, "Vault authenticated via Kubernetes service account");
|
|
810
822
|
}
|
|
811
823
|
async getSecret(path) {
|
|
812
|
-
const cacheKey = path;
|
|
813
|
-
if (this.cacheEnabled) {
|
|
814
|
-
const cached = this.cache.get(cacheKey);
|
|
815
|
-
if (cached && cached.expiresAt > Date.now()) {
|
|
816
|
-
this.logger.debug({
|
|
817
|
-
path,
|
|
818
|
-
cache: "hit"
|
|
819
|
-
}, "Vault secret cache hit");
|
|
820
|
-
return cached.data;
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
824
|
await this.authenticate();
|
|
824
825
|
const client = await this.clientPromise;
|
|
825
|
-
const fullPath = `${this.
|
|
826
|
+
const fullPath = `${this.mountPoint}/data/${stripSlashes(path)}`;
|
|
826
827
|
this.logger.debug({ path: fullPath }, "Reading secret from Vault");
|
|
827
828
|
const response = await client.read(fullPath);
|
|
828
|
-
|
|
829
|
-
if (this.cacheEnabled) {
|
|
830
|
-
this.cache.set(cacheKey, {
|
|
831
|
-
data,
|
|
832
|
-
expiresAt: Date.now() + this.cacheTtlMs
|
|
833
|
-
});
|
|
834
|
-
this.logger.debug({
|
|
835
|
-
path,
|
|
836
|
-
cache: "miss",
|
|
837
|
-
ttlMs: this.cacheTtlMs
|
|
838
|
-
}, "Vault secret cached");
|
|
839
|
-
}
|
|
840
|
-
return data;
|
|
829
|
+
return response.data.data;
|
|
841
830
|
}
|
|
842
831
|
async getSecretValue(path, key) {
|
|
843
832
|
const secrets = await this.getSecret(path);
|
|
@@ -846,10 +835,6 @@ var VaultClient = class {
|
|
|
846
835
|
isAuthenticated() {
|
|
847
836
|
return this.authenticated;
|
|
848
837
|
}
|
|
849
|
-
clearCache() {
|
|
850
|
-
this.cache.clear();
|
|
851
|
-
this.logger.debug({}, "Vault secret cache cleared");
|
|
852
|
-
}
|
|
853
838
|
async checkHealth() {
|
|
854
839
|
try {
|
|
855
840
|
await this.authenticate();
|
|
@@ -880,12 +865,11 @@ function warnDeprecation(oldPath, newPath, logger) {
|
|
|
880
865
|
function createFoundation(input) {
|
|
881
866
|
const startTime = Date.now();
|
|
882
867
|
const config = parseConfig(input);
|
|
883
|
-
const { serviceName, serviceVersion, environment, features: featuresConfig, otel, logging: loggingConfig, requestLogging: requestLoggingConfig, redaction: redactionConfig, shutdown: shutdownConfig
|
|
868
|
+
const { serviceName, serviceVersion, environment, features: featuresConfig, otel, logging: loggingConfig, requestLogging: requestLoggingConfig, redaction: redactionConfig, shutdown: shutdownConfig } = config;
|
|
884
869
|
const features = {
|
|
885
870
|
tracing: featuresConfig.tracing ?? true,
|
|
886
871
|
metrics: featuresConfig.metrics ?? true,
|
|
887
|
-
logging: featuresConfig.logging ?? true
|
|
888
|
-
vault: vaultConfig.enabled ?? false
|
|
872
|
+
logging: featuresConfig.logging ?? true
|
|
889
873
|
};
|
|
890
874
|
const contextManager = createContextManager();
|
|
891
875
|
let logger;
|
|
@@ -963,22 +947,6 @@ function createFoundation(input) {
|
|
|
963
947
|
metricsError = err.message;
|
|
964
948
|
logger.error({ error: metricsError }, "Metrics setup failed - continuing without metrics");
|
|
965
949
|
}
|
|
966
|
-
let vaultClient = null;
|
|
967
|
-
let vaultError;
|
|
968
|
-
if (features.vault) try {
|
|
969
|
-
vaultClient = createVaultClient({
|
|
970
|
-
config: vaultConfig,
|
|
971
|
-
logger
|
|
972
|
-
});
|
|
973
|
-
logger.info({
|
|
974
|
-
feature: "vault",
|
|
975
|
-
address: vaultConfig.address,
|
|
976
|
-
authMethod: vaultConfig.authMethod
|
|
977
|
-
}, "Vault client created (lazy auth on first secret read)");
|
|
978
|
-
} catch (err) {
|
|
979
|
-
vaultError = err.message;
|
|
980
|
-
logger.error({ error: vaultError }, "Vault setup failed - continuing without vault");
|
|
981
|
-
}
|
|
982
950
|
logger.info({
|
|
983
951
|
serviceName,
|
|
984
952
|
serviceVersion,
|
|
@@ -1046,45 +1014,50 @@ function createFoundation(input) {
|
|
|
1046
1014
|
...options,
|
|
1047
1015
|
foundation
|
|
1048
1016
|
}) };
|
|
1049
|
-
|
|
1017
|
+
let secrets = null;
|
|
1018
|
+
if (config.vault?.enabled) try {
|
|
1019
|
+
secrets = createVaultClient({
|
|
1020
|
+
config: config.vault,
|
|
1021
|
+
logger
|
|
1022
|
+
});
|
|
1023
|
+
logger.info({
|
|
1024
|
+
address: config.vault.address,
|
|
1025
|
+
authMethod: config.vault.authMethod
|
|
1026
|
+
}, "Vault secrets module initialized");
|
|
1027
|
+
} catch (err) {
|
|
1028
|
+
logger.error({ error: err.message }, "Vault initialization failed - continuing without secrets");
|
|
1029
|
+
}
|
|
1050
1030
|
const buildHealthStatus = () => {
|
|
1051
1031
|
const tracingUp = !features.tracing || !tracingError && isTracingEnabled();
|
|
1052
1032
|
const metricsUp = !features.metrics || !metricsError && isMetricsEnabled();
|
|
1053
1033
|
const loggingUp = !loggingError;
|
|
1054
|
-
const
|
|
1055
|
-
const
|
|
1056
|
-
const allDown = (!tracingUp || !features.tracing) && (!metricsUp || !features.metrics) && (!vaultUp || !features.vault) && !loggingUp;
|
|
1034
|
+
const allUp = tracingUp && metricsUp && loggingUp;
|
|
1035
|
+
const allDown = (!tracingUp || !features.tracing) && (!metricsUp || !features.metrics) && !loggingUp;
|
|
1057
1036
|
let status = "healthy";
|
|
1058
1037
|
if (!allUp) status = allDown ? "unhealthy" : "degraded";
|
|
1059
|
-
const components = {
|
|
1060
|
-
tracing: {
|
|
1061
|
-
enabled: features.tracing,
|
|
1062
|
-
status: !features.tracing ? "disabled" : tracingError ? "down" : "up",
|
|
1063
|
-
message: tracingError
|
|
1064
|
-
},
|
|
1065
|
-
metrics: {
|
|
1066
|
-
enabled: features.metrics,
|
|
1067
|
-
status: !features.metrics ? "disabled" : metricsError ? "down" : "up",
|
|
1068
|
-
message: metricsError
|
|
1069
|
-
},
|
|
1070
|
-
logging: {
|
|
1071
|
-
enabled: features.logging,
|
|
1072
|
-
status: loggingError ? "down" : "up",
|
|
1073
|
-
message: loggingError
|
|
1074
|
-
}
|
|
1075
|
-
};
|
|
1076
|
-
if (features.vault) components.vault = {
|
|
1077
|
-
enabled: true,
|
|
1078
|
-
status: vaultError ? "down" : "up",
|
|
1079
|
-
message: vaultError
|
|
1080
|
-
};
|
|
1081
1038
|
return {
|
|
1082
1039
|
status,
|
|
1083
1040
|
timestamp: new Date().toISOString(),
|
|
1084
1041
|
service: serviceName,
|
|
1085
1042
|
version: serviceVersion,
|
|
1086
1043
|
uptime: Math.floor((Date.now() - startTime) / 1e3),
|
|
1087
|
-
components
|
|
1044
|
+
components: {
|
|
1045
|
+
tracing: {
|
|
1046
|
+
enabled: features.tracing,
|
|
1047
|
+
status: !features.tracing ? "disabled" : tracingError ? "down" : "up",
|
|
1048
|
+
message: tracingError
|
|
1049
|
+
},
|
|
1050
|
+
metrics: {
|
|
1051
|
+
enabled: features.metrics,
|
|
1052
|
+
status: !features.metrics ? "disabled" : metricsError ? "down" : "up",
|
|
1053
|
+
message: metricsError
|
|
1054
|
+
},
|
|
1055
|
+
logging: {
|
|
1056
|
+
enabled: features.logging,
|
|
1057
|
+
status: loggingError ? "down" : "up",
|
|
1058
|
+
message: loggingError
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1088
1061
|
};
|
|
1089
1062
|
};
|
|
1090
1063
|
const shutdownFn = async () => {
|
|
@@ -1092,7 +1065,6 @@ function createFoundation(input) {
|
|
|
1092
1065
|
const promises = [];
|
|
1093
1066
|
if (features.tracing && isTracingEnabled()) promises.push(shutdownTracing());
|
|
1094
1067
|
if (features.metrics && isMetricsEnabled()) promises.push(shutdownMetrics());
|
|
1095
|
-
if (vaultClient) vaultClient.clearCache();
|
|
1096
1068
|
await Promise.all(promises);
|
|
1097
1069
|
logger.info({}, "Foundation shutdown complete");
|
|
1098
1070
|
};
|
|
@@ -1125,7 +1097,7 @@ function createFoundation(input) {
|
|
|
1125
1097
|
observability,
|
|
1126
1098
|
http: httpModule,
|
|
1127
1099
|
lifecycle,
|
|
1128
|
-
secrets
|
|
1100
|
+
secrets,
|
|
1129
1101
|
logger,
|
|
1130
1102
|
context: contextManager,
|
|
1131
1103
|
fastifyPlugin: createObservabilityPlugin({
|
|
@@ -1471,5 +1443,5 @@ var InMemoryObjectStore = class {
|
|
|
1471
1443
|
};
|
|
1472
1444
|
|
|
1473
1445
|
//#endregion
|
|
1474
|
-
export { AutoInstrumentationConfigSchema, AwsS3ObjectStore, FeaturesConfigSchema, FoundationConfigSchema, InMemoryObjectStore, LoggingConfigSchema, OtelConfigSchema, REDACT_PATHS, RedactionConfigSchema, RequestLoggingConfigSchema, ShutdownConfigSchema, SpanStatusCode,
|
|
1446
|
+
export { AutoInstrumentationConfigSchema, AwsS3ObjectStore, FeaturesConfigSchema, FoundationConfigSchema, InMemoryObjectStore, LoggingConfigSchema, OtelConfigSchema, REDACT_PATHS, RedactionConfigSchema, RequestLoggingConfigSchema, ShutdownConfigSchema, SpanStatusCode, VaultClient, VaultConfigSchema, buildPinoRedactConfig, context, createContextManager, createFallbackLogger, createFoundation, createHttpClient, createLogger, createObservabilityPlugin, createVaultClient, getActiveSpan, getDefaultOtelEndpoint, getGlobalLogger, getMeter, getTraceContext, getTracer, isMetricsEnabled, isTracingEnabled, metrics, parseConfig, propagation, sanitizeBody, setGlobalLogger, setupMetrics, setupObservability, setupTracing, shutdownMetrics, shutdownTracing, trace };
|
|
1475
1447
|
//# sourceMappingURL=index.mjs.map
|