@ciq-dev/neoiq-foundation-node 1.0.1 → 1.0.2-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/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- const require_tracing = require('./tracing-B8-Ntcll.js');
2
+ const require_tracing = require('./tracing-CMXNnEXN.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"));
@@ -12,6 +12,7 @@ const crypto = require_tracing.__toESM(require("crypto"));
12
12
  const axios = require_tracing.__toESM(require("axios"));
13
13
  const axios_retry = require_tracing.__toESM(require("axios-retry"));
14
14
  const opossum = require_tracing.__toESM(require("opossum"));
15
+ const node_fs_promises = require_tracing.__toESM(require("node:fs/promises"));
15
16
  const node_crypto = require_tracing.__toESM(require("node:crypto"));
16
17
  const node_stream = require_tracing.__toESM(require("node:stream"));
17
18
 
@@ -731,6 +732,136 @@ function createHttpClient(options) {
731
732
  return client;
732
733
  }
733
734
 
735
+ //#endregion
736
+ //#region src/features/vault.ts
737
+ async function loadNodeVault() {
738
+ try {
739
+ const mod = await import("node-vault");
740
+ return mod.default ?? mod;
741
+ } catch {
742
+ throw new Error("node-vault is not installed. Install the optional peer dependency: npm install node-vault");
743
+ }
744
+ }
745
+ var VaultClient = class {
746
+ client = null;
747
+ clientPromise;
748
+ authenticated = false;
749
+ cache = new Map();
750
+ config;
751
+ logger;
752
+ cacheEnabled;
753
+ cacheTtlMs;
754
+ 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
+ this.logger = options.logger;
764
+ this.cacheEnabled = this.config.cache?.enabled !== false;
765
+ this.cacheTtlMs = (this.config.cache?.ttlSeconds ?? 300) * 1e3;
766
+ this.clientPromise = this.initClient();
767
+ }
768
+ async initClient() {
769
+ const nodeVault = await loadNodeVault();
770
+ const client = nodeVault({
771
+ apiVersion: "v1",
772
+ endpoint: this.config.address,
773
+ namespace: this.config.namespace
774
+ });
775
+ this.client = client;
776
+ return client;
777
+ }
778
+ async authenticate() {
779
+ if (this.authenticated) return;
780
+ const client = await this.clientPromise;
781
+ if (this.config.authMethod === "token") {
782
+ const token = process.env.VAULT_TOKEN;
783
+ if (!token) throw new Error("VAULT_TOKEN environment variable is required for token-based authentication");
784
+ client.token = token;
785
+ this.authenticated = true;
786
+ this.logger.info({ authMethod: "token" }, "Vault authenticated via token");
787
+ return;
788
+ }
789
+ const role = this.config.role;
790
+ if (!role) throw new Error("Vault role is required for Kubernetes authentication. Set vault.role in config or VAULT_AUTH_ROLE env var.");
791
+ let jwt;
792
+ try {
793
+ jwt = await (0, node_fs_promises.readFile)(this.config.tokenPath, "utf8");
794
+ } catch (err) {
795
+ const message = err instanceof Error ? err.message : String(err);
796
+ throw new Error(`Failed to read Kubernetes service account token from ${this.config.tokenPath}: ${message}`);
797
+ }
798
+ const result = await client.kubernetesLogin({
799
+ role,
800
+ jwt
801
+ });
802
+ client.token = result.auth.client_token;
803
+ this.authenticated = true;
804
+ this.logger.info({
805
+ authMethod: "kubernetes",
806
+ role,
807
+ leaseDuration: result.auth.lease_duration
808
+ }, "Vault authenticated via Kubernetes service account");
809
+ }
810
+ 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
+ await this.authenticate();
823
+ const client = await this.clientPromise;
824
+ const fullPath = `${this.config.mountPoint}/data/${path}`;
825
+ this.logger.debug({ path: fullPath }, "Reading secret from Vault");
826
+ const response = await client.read(fullPath);
827
+ const data = response.data.data;
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;
840
+ }
841
+ async getSecretValue(path, key) {
842
+ const secrets = await this.getSecret(path);
843
+ return secrets[key];
844
+ }
845
+ isAuthenticated() {
846
+ return this.authenticated;
847
+ }
848
+ clearCache() {
849
+ this.cache.clear();
850
+ this.logger.debug({}, "Vault secret cache cleared");
851
+ }
852
+ async checkHealth() {
853
+ try {
854
+ await this.authenticate();
855
+ return true;
856
+ } catch {
857
+ return false;
858
+ }
859
+ }
860
+ };
861
+ function createVaultClient(options) {
862
+ return new VaultClient(options);
863
+ }
864
+
734
865
  //#endregion
735
866
  //#region src/foundation.ts
736
867
  const deprecationWarnings = new Set();
@@ -748,11 +879,12 @@ function warnDeprecation(oldPath, newPath, logger) {
748
879
  function createFoundation(input) {
749
880
  const startTime = Date.now();
750
881
  const config = require_tracing.parseConfig(input);
751
- const { serviceName, serviceVersion, environment, features: featuresConfig, otel, logging: loggingConfig, requestLogging: requestLoggingConfig, redaction: redactionConfig, shutdown: shutdownConfig } = config;
882
+ const { serviceName, serviceVersion, environment, features: featuresConfig, otel, logging: loggingConfig, requestLogging: requestLoggingConfig, redaction: redactionConfig, shutdown: shutdownConfig, vault: vaultConfig } = config;
752
883
  const features = {
753
884
  tracing: featuresConfig.tracing ?? true,
754
885
  metrics: featuresConfig.metrics ?? true,
755
- logging: featuresConfig.logging ?? true
886
+ logging: featuresConfig.logging ?? true,
887
+ vault: vaultConfig.enabled ?? false
756
888
  };
757
889
  const contextManager = createContextManager();
758
890
  let logger;
@@ -830,6 +962,22 @@ function createFoundation(input) {
830
962
  metricsError = err.message;
831
963
  logger.error({ error: metricsError }, "Metrics setup failed - continuing without metrics");
832
964
  }
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
+ }
833
981
  logger.info({
834
982
  serviceName,
835
983
  serviceVersion,
@@ -897,37 +1045,45 @@ function createFoundation(input) {
897
1045
  ...options,
898
1046
  foundation
899
1047
  }) };
1048
+ const secretsModule = vaultClient;
900
1049
  const buildHealthStatus = () => {
901
1050
  const tracingUp = !features.tracing || !tracingError && require_tracing.isTracingEnabled();
902
1051
  const metricsUp = !features.metrics || !metricsError && isMetricsEnabled();
903
1052
  const loggingUp = !loggingError;
904
- const allUp = tracingUp && metricsUp && loggingUp;
905
- const allDown = (!tracingUp || !features.tracing) && (!metricsUp || !features.metrics) && !loggingUp;
1053
+ const vaultUp = !features.vault || !vaultError && !!vaultClient;
1054
+ const allUp = tracingUp && metricsUp && loggingUp && vaultUp;
1055
+ const allDown = (!tracingUp || !features.tracing) && (!metricsUp || !features.metrics) && (!vaultUp || !features.vault) && !loggingUp;
906
1056
  let status = "healthy";
907
1057
  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
+ };
908
1080
  return {
909
1081
  status,
910
1082
  timestamp: new Date().toISOString(),
911
1083
  service: serviceName,
912
1084
  version: serviceVersion,
913
1085
  uptime: Math.floor((Date.now() - startTime) / 1e3),
914
- components: {
915
- tracing: {
916
- enabled: features.tracing,
917
- status: !features.tracing ? "disabled" : tracingError ? "down" : "up",
918
- message: tracingError
919
- },
920
- metrics: {
921
- enabled: features.metrics,
922
- status: !features.metrics ? "disabled" : metricsError ? "down" : "up",
923
- message: metricsError
924
- },
925
- logging: {
926
- enabled: features.logging,
927
- status: loggingError ? "down" : "up",
928
- message: loggingError
929
- }
930
- }
1086
+ components
931
1087
  };
932
1088
  };
933
1089
  const shutdownFn = async () => {
@@ -935,6 +1091,7 @@ function createFoundation(input) {
935
1091
  const promises = [];
936
1092
  if (features.tracing && require_tracing.isTracingEnabled()) promises.push(require_tracing.shutdownTracing());
937
1093
  if (features.metrics && isMetricsEnabled()) promises.push(shutdownMetrics());
1094
+ if (vaultClient) vaultClient.clearCache();
938
1095
  await Promise.all(promises);
939
1096
  logger.info({}, "Foundation shutdown complete");
940
1097
  };
@@ -967,6 +1124,7 @@ function createFoundation(input) {
967
1124
  observability,
968
1125
  http: httpModule,
969
1126
  lifecycle,
1127
+ secrets: secretsModule,
970
1128
  logger,
971
1129
  context: contextManager,
972
1130
  fastifyPlugin: createObservabilityPlugin({
@@ -1324,6 +1482,9 @@ exports.RedactionConfigSchema = require_tracing.RedactionConfigSchema
1324
1482
  exports.RequestLoggingConfigSchema = require_tracing.RequestLoggingConfigSchema
1325
1483
  exports.ShutdownConfigSchema = require_tracing.ShutdownConfigSchema
1326
1484
  exports.SpanStatusCode = __opentelemetry_api.SpanStatusCode
1485
+ exports.VaultCacheConfigSchema = require_tracing.VaultCacheConfigSchema
1486
+ exports.VaultClient = VaultClient
1487
+ exports.VaultConfigSchema = require_tracing.VaultConfigSchema
1327
1488
  exports.buildPinoRedactConfig = buildPinoRedactConfig
1328
1489
  exports.context = __opentelemetry_api.context
1329
1490
  exports.createContextManager = createContextManager
@@ -1332,6 +1493,7 @@ exports.createFoundation = createFoundation
1332
1493
  exports.createHttpClient = createHttpClient
1333
1494
  exports.createLogger = createLogger
1334
1495
  exports.createObservabilityPlugin = createObservabilityPlugin
1496
+ exports.createVaultClient = createVaultClient
1335
1497
  exports.getActiveSpan = require_tracing.getActiveSpan
1336
1498
  exports.getDefaultOtelEndpoint = require_tracing.getDefaultOtelEndpoint
1337
1499
  exports.getGlobalLogger = getGlobalLogger