All files / src/modules/env EnvStore.ts

98% Statements 49/50
88.88% Branches 24/27
93.33% Functions 14/15
97.95% Lines 48/49

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165                                                                        6x 6x 6x       2x     2x 2x 303x 303x             6x 6x 6x 6x 6x 6x 2x   4x     6x 2x   6x             6x 2x     2x           2x 3x 2x         1x             6x 5x   1x         6x       23x       7x       1x       313x       8x 32x         6x                           2x 2x 2x 2x 9x 2x   7x 7x 4x 4x 4x          
import * as Path from 'path';
import * as fs from 'fs';
import * as os from 'os';
import type { IStore } from '../../lib';
import { type EnvKey, StoreGlobal, type StoreItem } from './Global';
import { CoreLogChannel } from '../../classes';
import type { GlobalFolder } from '../../CoreKernel';
 
export type EnvStoreCProps = {
  log?: CoreLogChannel;
  pathOverride?: string;
  envFilePath?: string;
  loadFromLocalEnv?: boolean;
  appName?: string;
  globalFolder?: GlobalFolder[];
};
/**
 * Configuration options for initializing an EnvStoreC instance.
 *
 * The configuration object accepts the following properties:
 *
 * * `log` – A {@link CoreLogChannel} instance used for logging.
 * * `pathOverride` – Optional custom path that overrides the default store location.
 * * `envFilePath` – Optional path to a `.env` file from which environment variables can be loaded.
 * * `loadFromLocalEnv` – Optional boolean indicating whether to load variables from the local environment.
 * * `appName` – Optional name of the application associated with the store.
 *
 * All properties except `log` are optional.  The object is passed to the
 * constructor of EnvStoreC to configure its behavior.
 */
export default class EnvStore implements IStore {
  store: Map<EnvKey, StoreItem>;
 
  log: CoreLogChannel | undefined;
 
  constructor(props?: EnvStoreCProps) {
    this.store = new Map<EnvKey, StoreItem>();
    this.log = props?.log;
    this.initNew(props);
  }
 
  loadLocalEnv() {
    const lEnv: Record<string, any> = {
      ...process.env,
    };
    const lKeyRing = Object.keys(lEnv);
    for (const key of lKeyRing) {
      Eif (key) {
        this.set(key, lEnv[key]);
      }
    }
  }
 
  initNew(props?: EnvStoreCProps): void {
    const { envFilePath, pathOverride, loadFromLocalEnv, appName } =
      props ?? {};
    this.clear();
    const root = envFilePath ?? process.env.npm_config_local_prefix;
    Eif (root) {
      const path = Path.join(root, '.env');
      if (fs.existsSync(path)) {
        this.loadFromFile(path);
      } else {
        this.log?.warn(`Cant load env from ${path}`);
      }
    }
    if (loadFromLocalEnv) {
      this.loadLocalEnv();
    }
    this.setBulk(
      [StoreGlobal.GLOBAL_OS, os.platform()],
      [StoreGlobal.GLOBAL_ARCH, os.arch()],
      [StoreGlobal.GLOBAL_HOME_DIR, os.homedir()],
      [StoreGlobal.GLOBAL_HOST_NAME, os.hostname()],
    );
 
    if (appName) {
      const base = pathOverride
        ? Path.join(pathOverride, appName)
        : Path.join(process.cwd(), 'data', 'config', appName);
      this.setBulk(
        [StoreGlobal.GLOBAL_PATH_HOME, base],
        [StoreGlobal.GLOBAL_PATH_DATA, Path.join(base, 'data')],
        [StoreGlobal.GLOBAL_PATH_DB, Path.join(base, 'db')],
        [StoreGlobal.GLOBAL_PATH_TEMP, Path.join(base, 'temp')],
      );
      props?.globalFolder?.forEach(({ storeKey, folderName, base: b }) => {
        if (b === 'root') {
          this.set(
            `GLOBAL_FOLDER_${storeKey.toUpperCase()}`,
            Path.join(process.cwd(), folderName),
          );
        } else {
          this.set(
            `GLOBAL_FOLDER_${storeKey.toUpperCase()}`,
            Path.join(base, folderName),
          );
        }
      });
    }
    if (process.env.npm_package_version) {
      this.set(StoreGlobal.GLOBAL_APP_VERSION, process.env.npm_package_version);
    } else {
      this.set(StoreGlobal.GLOBAL_APP_VERSION, '0.0.0');
    }
  }
 
  clear(): void {
    this.store = new Map<EnvKey, StoreItem>();
  }
 
  get(key: EnvKey, defaultValue = ''): StoreItem {
    return this.store.get(key) ?? defaultValue;
  }
 
  has(key: EnvKey): boolean {
    return this.store.has(key);
  }
 
  delete(key: EnvKey): boolean {
    return this.store.delete(key);
  }
 
  set(key: EnvKey, value: StoreItem): void {
    this.store.set(key, value);
  }
 
  setBulk(...list: [EnvKey, StoreItem][]): void {
    for (const [key, value] of list) {
      this.store.set(key, value);
    }
  }
 
  getBulk(...list: [EnvKey, string?][]): StoreItem[] {
    return list.map(([key, value]) => this.get(key, value));
  }
 
  /**
   * Retrieves the value of a global folder constant.
   *
   * @param {string} folder - The name of the folder to retrieve.
   * @returns {string} The value of the global folder constant.
   */
  gf(folder: string): string {
    return this.get(`GLOBAL_FOLDER_${folder.toUpperCase()}`)!;
  }
 
  private loadFromFile(path: string) {
    this.log?.log(`Load env from ${path}`);
    const env = fs.readFileSync(path).toString('utf-8');
    const lines = env.split('\n');
    lines.forEach((line) => {
      if (line.startsWith('#')) {
        return;
      }
      const split = line.indexOf('=');
      if (split > 0) {
        const key = line.substring(0, split).trim();
        const value = line.substring(split + 1).trim();
        this.store.set(key, value);
      }
    });
  }
}