@ciq-dev/neoiq-foundation-node 1.0.3-0 → 1.1.0-beta.1
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 +97 -117
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +98 -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,109 @@ 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
|
+
let start = 0;
|
|
745
|
+
let end = value.length;
|
|
746
|
+
while (start < end && value[start] === "/") start += 1;
|
|
747
|
+
while (end > start && value[end - 1] === "/") end -= 1;
|
|
748
|
+
return value.slice(start, end);
|
|
749
|
+
}
|
|
750
|
+
function stripTrailingSlashes(value) {
|
|
751
|
+
let end = value.length;
|
|
752
|
+
while (end > 0 && value[end - 1] === "/") end -= 1;
|
|
753
|
+
return value.slice(0, end);
|
|
754
|
+
}
|
|
736
755
|
async function loadNodeVault() {
|
|
737
756
|
try {
|
|
738
757
|
const mod = await import("node-vault");
|
|
739
758
|
return mod.default ?? mod;
|
|
740
759
|
} catch {
|
|
741
|
-
throw new Error("node-vault is not installed. Install
|
|
760
|
+
throw new Error("node-vault is not installed. Install it as a peer dependency: npm install node-vault");
|
|
742
761
|
}
|
|
743
762
|
}
|
|
744
763
|
var VaultClient = class {
|
|
745
|
-
|
|
764
|
+
logger;
|
|
765
|
+
address;
|
|
766
|
+
authMethod;
|
|
767
|
+
role;
|
|
768
|
+
mountPoint;
|
|
769
|
+
tokenPath;
|
|
770
|
+
k8sAuthMount;
|
|
771
|
+
namespace;
|
|
746
772
|
clientPromise;
|
|
747
773
|
authenticated = false;
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
logger;
|
|
751
|
-
cacheEnabled;
|
|
752
|
-
cacheTtlMs;
|
|
774
|
+
/** In-flight auth so concurrent getSecret() calls don't all trigger kubernetesLogin. */
|
|
775
|
+
authPromise = null;
|
|
753
776
|
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
777
|
this.logger = options.logger;
|
|
764
|
-
|
|
765
|
-
this.
|
|
766
|
-
this.
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
778
|
+
const cfg = options.config;
|
|
779
|
+
this.address = stripTrailingSlashes(cfg.address ?? DEFAULTS.address);
|
|
780
|
+
this.authMethod = cfg.authMethod ?? DEFAULTS.authMethod;
|
|
781
|
+
this.role = cfg.role;
|
|
782
|
+
this.mountPoint = stripSlashes(cfg.mountPoint ?? DEFAULTS.mountPoint);
|
|
783
|
+
this.tokenPath = cfg.tokenPath ?? DEFAULTS.tokenPath;
|
|
784
|
+
this.k8sAuthMount = cfg.k8sAuthMount ?? DEFAULTS.k8sAuthMount;
|
|
785
|
+
this.namespace = cfg.namespace;
|
|
786
|
+
const factory = options.nodeVaultFactory ? Promise.resolve(options.nodeVaultFactory) : loadNodeVault();
|
|
787
|
+
this.clientPromise = factory.then((create) => create({
|
|
771
788
|
apiVersion: "v1",
|
|
772
|
-
endpoint: this.
|
|
773
|
-
namespace: this.
|
|
774
|
-
});
|
|
775
|
-
this.client = client;
|
|
776
|
-
return client;
|
|
789
|
+
endpoint: this.address,
|
|
790
|
+
namespace: this.namespace
|
|
791
|
+
}));
|
|
777
792
|
}
|
|
778
793
|
async authenticate() {
|
|
779
794
|
if (this.authenticated) return;
|
|
795
|
+
if (this.authPromise) return this.authPromise;
|
|
796
|
+
this.authPromise = this.doAuthenticate().finally(() => {
|
|
797
|
+
if (!this.authenticated) this.authPromise = null;
|
|
798
|
+
});
|
|
799
|
+
return this.authPromise;
|
|
800
|
+
}
|
|
801
|
+
async doAuthenticate() {
|
|
780
802
|
const client = await this.clientPromise;
|
|
781
|
-
if (this.
|
|
803
|
+
if (this.authMethod === "token") {
|
|
782
804
|
const token = process.env.VAULT_TOKEN;
|
|
783
|
-
if (!token) throw new Error("VAULT_TOKEN
|
|
805
|
+
if (!token) throw new Error("VAULT_TOKEN env var is required for token auth");
|
|
784
806
|
client.token = token;
|
|
785
807
|
this.authenticated = true;
|
|
786
808
|
this.logger.info({ authMethod: "token" }, "Vault authenticated via token");
|
|
787
809
|
return;
|
|
788
810
|
}
|
|
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.");
|
|
811
|
+
if (!this.role) throw new Error("Vault role is required for Kubernetes auth (set vault.role or VAULT_AUTH_ROLE)");
|
|
791
812
|
let jwt;
|
|
792
813
|
try {
|
|
793
|
-
jwt = await readFile(this.
|
|
814
|
+
jwt = await readFile(this.tokenPath, "utf8");
|
|
794
815
|
} catch (err) {
|
|
795
|
-
const
|
|
796
|
-
throw new Error(`Failed to read
|
|
816
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
817
|
+
throw new Error(`Failed to read service-account token at ${this.tokenPath}: ${msg}`);
|
|
797
818
|
}
|
|
798
819
|
const result = await client.kubernetesLogin({
|
|
799
|
-
role,
|
|
820
|
+
role: this.role,
|
|
800
821
|
jwt,
|
|
801
|
-
mount_point: this.
|
|
822
|
+
mount_point: this.k8sAuthMount
|
|
802
823
|
});
|
|
803
824
|
client.token = result.auth.client_token;
|
|
804
825
|
this.authenticated = true;
|
|
805
826
|
this.logger.info({
|
|
806
827
|
authMethod: "kubernetes",
|
|
807
|
-
role,
|
|
828
|
+
role: this.role,
|
|
808
829
|
leaseDuration: result.auth.lease_duration
|
|
809
830
|
}, "Vault authenticated via Kubernetes service account");
|
|
810
831
|
}
|
|
811
832
|
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
833
|
await this.authenticate();
|
|
824
834
|
const client = await this.clientPromise;
|
|
825
|
-
const fullPath = `${this.
|
|
835
|
+
const fullPath = `${this.mountPoint}/data/${stripSlashes(path)}`;
|
|
826
836
|
this.logger.debug({ path: fullPath }, "Reading secret from Vault");
|
|
827
837
|
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;
|
|
838
|
+
return response.data.data;
|
|
841
839
|
}
|
|
842
840
|
async getSecretValue(path, key) {
|
|
843
841
|
const secrets = await this.getSecret(path);
|
|
@@ -846,10 +844,6 @@ var VaultClient = class {
|
|
|
846
844
|
isAuthenticated() {
|
|
847
845
|
return this.authenticated;
|
|
848
846
|
}
|
|
849
|
-
clearCache() {
|
|
850
|
-
this.cache.clear();
|
|
851
|
-
this.logger.debug({}, "Vault secret cache cleared");
|
|
852
|
-
}
|
|
853
847
|
async checkHealth() {
|
|
854
848
|
try {
|
|
855
849
|
await this.authenticate();
|
|
@@ -880,12 +874,11 @@ function warnDeprecation(oldPath, newPath, logger) {
|
|
|
880
874
|
function createFoundation(input) {
|
|
881
875
|
const startTime = Date.now();
|
|
882
876
|
const config = parseConfig(input);
|
|
883
|
-
const { serviceName, serviceVersion, environment, features: featuresConfig, otel, logging: loggingConfig, requestLogging: requestLoggingConfig, redaction: redactionConfig, shutdown: shutdownConfig
|
|
877
|
+
const { serviceName, serviceVersion, environment, features: featuresConfig, otel, logging: loggingConfig, requestLogging: requestLoggingConfig, redaction: redactionConfig, shutdown: shutdownConfig } = config;
|
|
884
878
|
const features = {
|
|
885
879
|
tracing: featuresConfig.tracing ?? true,
|
|
886
880
|
metrics: featuresConfig.metrics ?? true,
|
|
887
|
-
logging: featuresConfig.logging ?? true
|
|
888
|
-
vault: vaultConfig.enabled ?? false
|
|
881
|
+
logging: featuresConfig.logging ?? true
|
|
889
882
|
};
|
|
890
883
|
const contextManager = createContextManager();
|
|
891
884
|
let logger;
|
|
@@ -963,22 +956,6 @@ function createFoundation(input) {
|
|
|
963
956
|
metricsError = err.message;
|
|
964
957
|
logger.error({ error: metricsError }, "Metrics setup failed - continuing without metrics");
|
|
965
958
|
}
|
|
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
959
|
logger.info({
|
|
983
960
|
serviceName,
|
|
984
961
|
serviceVersion,
|
|
@@ -1046,45 +1023,50 @@ function createFoundation(input) {
|
|
|
1046
1023
|
...options,
|
|
1047
1024
|
foundation
|
|
1048
1025
|
}) };
|
|
1049
|
-
|
|
1026
|
+
let secrets = null;
|
|
1027
|
+
if (config.vault?.enabled) try {
|
|
1028
|
+
secrets = createVaultClient({
|
|
1029
|
+
config: config.vault,
|
|
1030
|
+
logger
|
|
1031
|
+
});
|
|
1032
|
+
logger.info({
|
|
1033
|
+
address: config.vault.address,
|
|
1034
|
+
authMethod: config.vault.authMethod
|
|
1035
|
+
}, "Vault secrets module initialized");
|
|
1036
|
+
} catch (err) {
|
|
1037
|
+
logger.error({ error: err.message }, "Vault initialization failed - continuing without secrets");
|
|
1038
|
+
}
|
|
1050
1039
|
const buildHealthStatus = () => {
|
|
1051
1040
|
const tracingUp = !features.tracing || !tracingError && isTracingEnabled();
|
|
1052
1041
|
const metricsUp = !features.metrics || !metricsError && isMetricsEnabled();
|
|
1053
1042
|
const loggingUp = !loggingError;
|
|
1054
|
-
const
|
|
1055
|
-
const
|
|
1056
|
-
const allDown = (!tracingUp || !features.tracing) && (!metricsUp || !features.metrics) && (!vaultUp || !features.vault) && !loggingUp;
|
|
1043
|
+
const allUp = tracingUp && metricsUp && loggingUp;
|
|
1044
|
+
const allDown = (!tracingUp || !features.tracing) && (!metricsUp || !features.metrics) && !loggingUp;
|
|
1057
1045
|
let status = "healthy";
|
|
1058
1046
|
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
1047
|
return {
|
|
1082
1048
|
status,
|
|
1083
1049
|
timestamp: new Date().toISOString(),
|
|
1084
1050
|
service: serviceName,
|
|
1085
1051
|
version: serviceVersion,
|
|
1086
1052
|
uptime: Math.floor((Date.now() - startTime) / 1e3),
|
|
1087
|
-
components
|
|
1053
|
+
components: {
|
|
1054
|
+
tracing: {
|
|
1055
|
+
enabled: features.tracing,
|
|
1056
|
+
status: !features.tracing ? "disabled" : tracingError ? "down" : "up",
|
|
1057
|
+
message: tracingError
|
|
1058
|
+
},
|
|
1059
|
+
metrics: {
|
|
1060
|
+
enabled: features.metrics,
|
|
1061
|
+
status: !features.metrics ? "disabled" : metricsError ? "down" : "up",
|
|
1062
|
+
message: metricsError
|
|
1063
|
+
},
|
|
1064
|
+
logging: {
|
|
1065
|
+
enabled: features.logging,
|
|
1066
|
+
status: loggingError ? "down" : "up",
|
|
1067
|
+
message: loggingError
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1088
1070
|
};
|
|
1089
1071
|
};
|
|
1090
1072
|
const shutdownFn = async () => {
|
|
@@ -1092,7 +1074,6 @@ function createFoundation(input) {
|
|
|
1092
1074
|
const promises = [];
|
|
1093
1075
|
if (features.tracing && isTracingEnabled()) promises.push(shutdownTracing());
|
|
1094
1076
|
if (features.metrics && isMetricsEnabled()) promises.push(shutdownMetrics());
|
|
1095
|
-
if (vaultClient) vaultClient.clearCache();
|
|
1096
1077
|
await Promise.all(promises);
|
|
1097
1078
|
logger.info({}, "Foundation shutdown complete");
|
|
1098
1079
|
};
|
|
@@ -1125,7 +1106,7 @@ function createFoundation(input) {
|
|
|
1125
1106
|
observability,
|
|
1126
1107
|
http: httpModule,
|
|
1127
1108
|
lifecycle,
|
|
1128
|
-
secrets
|
|
1109
|
+
secrets,
|
|
1129
1110
|
logger,
|
|
1130
1111
|
context: contextManager,
|
|
1131
1112
|
fastifyPlugin: createObservabilityPlugin({
|
|
@@ -1471,5 +1452,5 @@ var InMemoryObjectStore = class {
|
|
|
1471
1452
|
};
|
|
1472
1453
|
|
|
1473
1454
|
//#endregion
|
|
1474
|
-
export { AutoInstrumentationConfigSchema, AwsS3ObjectStore, FeaturesConfigSchema, FoundationConfigSchema, InMemoryObjectStore, LoggingConfigSchema, OtelConfigSchema, REDACT_PATHS, RedactionConfigSchema, RequestLoggingConfigSchema, ShutdownConfigSchema, SpanStatusCode,
|
|
1455
|
+
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
1456
|
//# sourceMappingURL=index.mjs.map
|