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