@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.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,111 +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
|
-
k8sAuthMount: "kubernetes",
|
|
762
|
-
...options.config
|
|
763
|
-
};
|
|
764
769
|
this.logger = options.logger;
|
|
765
|
-
|
|
766
|
-
this.
|
|
767
|
-
this.
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
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({
|
|
772
780
|
apiVersion: "v1",
|
|
773
|
-
endpoint: this.
|
|
774
|
-
namespace: this.
|
|
775
|
-
});
|
|
776
|
-
this.client = client;
|
|
777
|
-
return client;
|
|
781
|
+
endpoint: this.address,
|
|
782
|
+
namespace: this.namespace
|
|
783
|
+
}));
|
|
778
784
|
}
|
|
779
785
|
async authenticate() {
|
|
780
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() {
|
|
781
794
|
const client = await this.clientPromise;
|
|
782
|
-
if (this.
|
|
795
|
+
if (this.authMethod === "token") {
|
|
783
796
|
const token = process.env.VAULT_TOKEN;
|
|
784
|
-
if (!token) throw new Error("VAULT_TOKEN
|
|
797
|
+
if (!token) throw new Error("VAULT_TOKEN env var is required for token auth");
|
|
785
798
|
client.token = token;
|
|
786
799
|
this.authenticated = true;
|
|
787
800
|
this.logger.info({ authMethod: "token" }, "Vault authenticated via token");
|
|
788
801
|
return;
|
|
789
802
|
}
|
|
790
|
-
|
|
791
|
-
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)");
|
|
792
804
|
let jwt;
|
|
793
805
|
try {
|
|
794
|
-
jwt = await (0, node_fs_promises.readFile)(this.
|
|
806
|
+
jwt = await (0, node_fs_promises.readFile)(this.tokenPath, "utf8");
|
|
795
807
|
} catch (err) {
|
|
796
|
-
const
|
|
797
|
-
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}`);
|
|
798
810
|
}
|
|
799
811
|
const result = await client.kubernetesLogin({
|
|
800
|
-
role,
|
|
812
|
+
role: this.role,
|
|
801
813
|
jwt,
|
|
802
|
-
mount_point: this.
|
|
814
|
+
mount_point: this.k8sAuthMount
|
|
803
815
|
});
|
|
804
816
|
client.token = result.auth.client_token;
|
|
805
817
|
this.authenticated = true;
|
|
806
818
|
this.logger.info({
|
|
807
819
|
authMethod: "kubernetes",
|
|
808
|
-
role,
|
|
820
|
+
role: this.role,
|
|
809
821
|
leaseDuration: result.auth.lease_duration
|
|
810
822
|
}, "Vault authenticated via Kubernetes service account");
|
|
811
823
|
}
|
|
812
824
|
async getSecret(path) {
|
|
813
|
-
const cacheKey = path;
|
|
814
|
-
if (this.cacheEnabled) {
|
|
815
|
-
const cached = this.cache.get(cacheKey);
|
|
816
|
-
if (cached && cached.expiresAt > Date.now()) {
|
|
817
|
-
this.logger.debug({
|
|
818
|
-
path,
|
|
819
|
-
cache: "hit"
|
|
820
|
-
}, "Vault secret cache hit");
|
|
821
|
-
return cached.data;
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
825
|
await this.authenticate();
|
|
825
826
|
const client = await this.clientPromise;
|
|
826
|
-
const fullPath = `${this.
|
|
827
|
+
const fullPath = `${this.mountPoint}/data/${stripSlashes(path)}`;
|
|
827
828
|
this.logger.debug({ path: fullPath }, "Reading secret from Vault");
|
|
828
829
|
const response = await client.read(fullPath);
|
|
829
|
-
|
|
830
|
-
if (this.cacheEnabled) {
|
|
831
|
-
this.cache.set(cacheKey, {
|
|
832
|
-
data,
|
|
833
|
-
expiresAt: Date.now() + this.cacheTtlMs
|
|
834
|
-
});
|
|
835
|
-
this.logger.debug({
|
|
836
|
-
path,
|
|
837
|
-
cache: "miss",
|
|
838
|
-
ttlMs: this.cacheTtlMs
|
|
839
|
-
}, "Vault secret cached");
|
|
840
|
-
}
|
|
841
|
-
return data;
|
|
830
|
+
return response.data.data;
|
|
842
831
|
}
|
|
843
832
|
async getSecretValue(path, key) {
|
|
844
833
|
const secrets = await this.getSecret(path);
|
|
@@ -847,10 +836,6 @@ var VaultClient = class {
|
|
|
847
836
|
isAuthenticated() {
|
|
848
837
|
return this.authenticated;
|
|
849
838
|
}
|
|
850
|
-
clearCache() {
|
|
851
|
-
this.cache.clear();
|
|
852
|
-
this.logger.debug({}, "Vault secret cache cleared");
|
|
853
|
-
}
|
|
854
839
|
async checkHealth() {
|
|
855
840
|
try {
|
|
856
841
|
await this.authenticate();
|
|
@@ -881,12 +866,11 @@ function warnDeprecation(oldPath, newPath, logger) {
|
|
|
881
866
|
function createFoundation(input) {
|
|
882
867
|
const startTime = Date.now();
|
|
883
868
|
const config = require_tracing.parseConfig(input);
|
|
884
|
-
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;
|
|
885
870
|
const features = {
|
|
886
871
|
tracing: featuresConfig.tracing ?? true,
|
|
887
872
|
metrics: featuresConfig.metrics ?? true,
|
|
888
|
-
logging: featuresConfig.logging ?? true
|
|
889
|
-
vault: vaultConfig.enabled ?? false
|
|
873
|
+
logging: featuresConfig.logging ?? true
|
|
890
874
|
};
|
|
891
875
|
const contextManager = createContextManager();
|
|
892
876
|
let logger;
|
|
@@ -964,22 +948,6 @@ function createFoundation(input) {
|
|
|
964
948
|
metricsError = err.message;
|
|
965
949
|
logger.error({ error: metricsError }, "Metrics setup failed - continuing without metrics");
|
|
966
950
|
}
|
|
967
|
-
let vaultClient = null;
|
|
968
|
-
let vaultError;
|
|
969
|
-
if (features.vault) try {
|
|
970
|
-
vaultClient = createVaultClient({
|
|
971
|
-
config: vaultConfig,
|
|
972
|
-
logger
|
|
973
|
-
});
|
|
974
|
-
logger.info({
|
|
975
|
-
feature: "vault",
|
|
976
|
-
address: vaultConfig.address,
|
|
977
|
-
authMethod: vaultConfig.authMethod
|
|
978
|
-
}, "Vault client created (lazy auth on first secret read)");
|
|
979
|
-
} catch (err) {
|
|
980
|
-
vaultError = err.message;
|
|
981
|
-
logger.error({ error: vaultError }, "Vault setup failed - continuing without vault");
|
|
982
|
-
}
|
|
983
951
|
logger.info({
|
|
984
952
|
serviceName,
|
|
985
953
|
serviceVersion,
|
|
@@ -1047,45 +1015,50 @@ function createFoundation(input) {
|
|
|
1047
1015
|
...options,
|
|
1048
1016
|
foundation
|
|
1049
1017
|
}) };
|
|
1050
|
-
|
|
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
|
+
}
|
|
1051
1031
|
const buildHealthStatus = () => {
|
|
1052
1032
|
const tracingUp = !features.tracing || !tracingError && require_tracing.isTracingEnabled();
|
|
1053
1033
|
const metricsUp = !features.metrics || !metricsError && isMetricsEnabled();
|
|
1054
1034
|
const loggingUp = !loggingError;
|
|
1055
|
-
const
|
|
1056
|
-
const
|
|
1057
|
-
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;
|
|
1058
1037
|
let status = "healthy";
|
|
1059
1038
|
if (!allUp) status = allDown ? "unhealthy" : "degraded";
|
|
1060
|
-
const components = {
|
|
1061
|
-
tracing: {
|
|
1062
|
-
enabled: features.tracing,
|
|
1063
|
-
status: !features.tracing ? "disabled" : tracingError ? "down" : "up",
|
|
1064
|
-
message: tracingError
|
|
1065
|
-
},
|
|
1066
|
-
metrics: {
|
|
1067
|
-
enabled: features.metrics,
|
|
1068
|
-
status: !features.metrics ? "disabled" : metricsError ? "down" : "up",
|
|
1069
|
-
message: metricsError
|
|
1070
|
-
},
|
|
1071
|
-
logging: {
|
|
1072
|
-
enabled: features.logging,
|
|
1073
|
-
status: loggingError ? "down" : "up",
|
|
1074
|
-
message: loggingError
|
|
1075
|
-
}
|
|
1076
|
-
};
|
|
1077
|
-
if (features.vault) components.vault = {
|
|
1078
|
-
enabled: true,
|
|
1079
|
-
status: vaultError ? "down" : "up",
|
|
1080
|
-
message: vaultError
|
|
1081
|
-
};
|
|
1082
1039
|
return {
|
|
1083
1040
|
status,
|
|
1084
1041
|
timestamp: new Date().toISOString(),
|
|
1085
1042
|
service: serviceName,
|
|
1086
1043
|
version: serviceVersion,
|
|
1087
1044
|
uptime: Math.floor((Date.now() - startTime) / 1e3),
|
|
1088
|
-
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
|
+
}
|
|
1089
1062
|
};
|
|
1090
1063
|
};
|
|
1091
1064
|
const shutdownFn = async () => {
|
|
@@ -1093,7 +1066,6 @@ function createFoundation(input) {
|
|
|
1093
1066
|
const promises = [];
|
|
1094
1067
|
if (features.tracing && require_tracing.isTracingEnabled()) promises.push(require_tracing.shutdownTracing());
|
|
1095
1068
|
if (features.metrics && isMetricsEnabled()) promises.push(shutdownMetrics());
|
|
1096
|
-
if (vaultClient) vaultClient.clearCache();
|
|
1097
1069
|
await Promise.all(promises);
|
|
1098
1070
|
logger.info({}, "Foundation shutdown complete");
|
|
1099
1071
|
};
|
|
@@ -1126,7 +1098,7 @@ function createFoundation(input) {
|
|
|
1126
1098
|
observability,
|
|
1127
1099
|
http: httpModule,
|
|
1128
1100
|
lifecycle,
|
|
1129
|
-
secrets
|
|
1101
|
+
secrets,
|
|
1130
1102
|
logger,
|
|
1131
1103
|
context: contextManager,
|
|
1132
1104
|
fastifyPlugin: createObservabilityPlugin({
|
|
@@ -1484,7 +1456,6 @@ exports.RedactionConfigSchema = require_tracing.RedactionConfigSchema
|
|
|
1484
1456
|
exports.RequestLoggingConfigSchema = require_tracing.RequestLoggingConfigSchema
|
|
1485
1457
|
exports.ShutdownConfigSchema = require_tracing.ShutdownConfigSchema
|
|
1486
1458
|
exports.SpanStatusCode = __opentelemetry_api.SpanStatusCode
|
|
1487
|
-
exports.VaultCacheConfigSchema = require_tracing.VaultCacheConfigSchema
|
|
1488
1459
|
exports.VaultClient = VaultClient
|
|
1489
1460
|
exports.VaultConfigSchema = require_tracing.VaultConfigSchema
|
|
1490
1461
|
exports.buildPinoRedactConfig = buildPinoRedactConfig
|