@oxog/environment-detector
Version:
Comprehensive, zero-dependency environment detection for Node.js
1,457 lines (1,433 loc) • 41 kB
JavaScript
#!C:/Program Files/Git/usr/bin/env node/n
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
// src/detectors/os.ts
var os = __toESM(require("os"));
// src/core/constants.ts
var DEFAULT_CACHE_TIMEOUT = 6e4;
var CI_ENV_VARS = Object.freeze({
CI: "CI",
CONTINUOUS_INTEGRATION: "CONTINUOUS_INTEGRATION",
BUILD_NUMBER: "BUILD_NUMBER",
CI_NAME: "CI_NAME"
});
var CI_PROVIDER_ENV_VARS = Object.freeze({
GITHUB_ACTIONS: "GITHUB_ACTIONS",
GITLAB_CI: "GITLAB_CI",
TRAVIS: "TRAVIS",
CIRCLECI: "CIRCLECI",
JENKINS_URL: "JENKINS_URL",
BITBUCKET_BUILD_NUMBER: "BITBUCKET_BUILD_NUMBER",
TEAMCITY_VERSION: "TEAMCITY_VERSION",
APPVEYOR: "APPVEYOR",
CODEBUILD_BUILD_ID: "CODEBUILD_BUILD_ID",
TF_BUILD: "TF_BUILD"
// Azure Pipelines
});
var CLOUD_ENV_VARS = Object.freeze({
AWS_LAMBDA_FUNCTION_NAME: "AWS_LAMBDA_FUNCTION_NAME",
AWS_EXECUTION_ENV: "AWS_EXECUTION_ENV",
AWS_REGION: "AWS_REGION",
FUNCTION_NAME: "FUNCTION_NAME",
// Google Cloud Functions
K_SERVICE: "K_SERVICE",
// Google Cloud Run
WEBSITE_SITE_NAME: "WEBSITE_SITE_NAME",
// Azure Functions
VERCEL: "VERCEL",
VERCEL_ENV: "VERCEL_ENV",
NETLIFY: "NETLIFY",
CF_WORKER: "CF_WORKER"
// Cloudflare Workers
});
var CONTAINER_FILES = Object.freeze({
DOCKER_ENV: "/.dockerenv",
DOCKER_INIT: "/.dockerinit",
WSL_INTEROP: "/run/WSL",
KUBERNETES_SERVICE: "/var/run/secrets/kubernetes.io"
});
var WSL_INDICATORS = Object.freeze({
ENV_VAR: "WSL_DISTRO_NAME",
PROC_VERSION: "Microsoft",
PROC_SYS: "/proc/sys/fs/binfmt_misc/WSLInterop"
});
var NODE_ENV_VALUES = Object.freeze({
DEVELOPMENT: "development",
PRODUCTION: "production",
TEST: "test",
STAGING: "staging"
});
// src/core/cache.ts
var Cache = class _Cache {
constructor() {
this.store = /* @__PURE__ */ new Map();
this.enabled = true;
}
static getInstance() {
if (!_Cache.instance) {
_Cache.instance = new _Cache();
}
return _Cache.instance;
}
get(key) {
if (!this.enabled) {
return void 0;
}
const entry = this.store.get(key);
if (!entry) {
return void 0;
}
const now = Date.now();
if (now - entry.timestamp > entry.timeout) {
this.store.delete(key);
return void 0;
}
return entry.value;
}
set(key, value, timeout = DEFAULT_CACHE_TIMEOUT) {
if (!this.enabled) {
return;
}
this.store.set(key, {
value,
timestamp: Date.now(),
timeout
});
}
has(key) {
if (!this.enabled) {
return false;
}
const entry = this.store.get(key);
if (!entry) {
return false;
}
const now = Date.now();
if (now - entry.timestamp > entry.timeout) {
this.store.delete(key);
return false;
}
return true;
}
delete(key) {
return this.store.delete(key);
}
clear() {
this.store.clear();
}
enable() {
this.enabled = true;
}
disable() {
this.enabled = false;
this.clear();
}
isEnabled() {
return this.enabled;
}
size() {
return this.store.size;
}
keys() {
return Array.from(this.store.keys());
}
};
// src/core/detector.ts
var BaseDetector = class {
constructor(options = {}) {
this.cache = Cache.getInstance();
this.options = {
cache: true,
cacheTimeout: 6e4,
async: false,
...options
};
}
detect() {
if (this.options.async) {
return this.detectAsync();
}
return this.detectSync();
}
reset() {
this.cache.delete(this.getCacheKey());
}
detectSync() {
const cacheKey = this.getCacheKey();
if (this.options.cache) {
const cached = this.cache.get(cacheKey);
if (cached !== void 0) {
return cached;
}
}
const result = this.performDetection();
if (this.options.cache) {
this.cache.set(cacheKey, result, this.options.cacheTimeout);
}
return result;
}
async detectAsync() {
const cacheKey = this.getCacheKey();
if (this.options.cache) {
const cached = this.cache.get(cacheKey);
if (cached !== void 0) {
return cached;
}
}
const result = await this.performAsyncDetection();
if (this.options.cache) {
this.cache.set(cacheKey, result, this.options.cacheTimeout);
}
return result;
}
async performAsyncDetection() {
return this.performDetection();
}
getCacheKey() {
return `detector:${this.name}`;
}
};
// src/detectors/os.ts
var OSDetector = class extends BaseDetector {
constructor() {
super(...arguments);
this.name = "os";
}
performDetection() {
try {
const platform5 = os.platform();
const release2 = os.release();
const version2 = os.version ? os.version() : release2;
const arch3 = os.arch();
let type = "unknown";
let isWindows = false;
let isMacOS = false;
let isLinux = false;
switch (platform5) {
case "win32":
type = "windows";
isWindows = true;
break;
case "darwin":
type = "macos";
isMacOS = true;
break;
case "linux":
type = "linux";
isLinux = true;
break;
}
return {
platform: platform5,
type,
version: version2,
release: release2,
arch: arch3,
isWindows,
isMacOS,
isLinux
};
} catch {
return {
platform: "linux",
type: "unknown",
arch: "unknown",
release: "unknown",
version: "unknown",
isWindows: false,
isMacOS: false,
isLinux: false
};
}
}
};
// src/detectors/container.ts
var os3 = __toESM(require("os"));
// src/utils/file.ts
var fs = __toESM(require("fs"));
function fileExists(path2) {
try {
fs.accessSync(path2, fs.constants.F_OK);
return true;
} catch {
return false;
}
}
function readFile(path2) {
try {
return fs.readFileSync(path2, "utf8");
} catch {
return null;
}
}
function isDirectory(path2) {
try {
const stats = fs.statSync(path2);
return stats.isDirectory();
} catch {
return false;
}
}
// src/utils/process.ts
var child_process = __toESM(require("child_process"));
var os2 = __toESM(require("os"));
function getEnv(key) {
return process.env[key];
}
function hasEnv(key) {
return key in process.env;
}
function getEnvBoolean(key, defaultValue = false) {
const value = getEnv(key);
if (!value) {
return defaultValue;
}
return value.toLowerCase() === "true" || value === "1";
}
function execSync2(command) {
try {
return child_process.execSync(command, {
encoding: "utf8",
stdio: ["pipe", "pipe", "ignore"]
}).trim();
} catch {
return null;
}
}
function getUID() {
if (process.getuid) {
return process.getuid();
}
return void 0;
}
function getGID() {
if (process.getgid) {
return process.getgid();
}
return void 0;
}
function getUsername() {
try {
return os2.userInfo().username;
} catch {
return void 0;
}
}
function isRoot() {
return getUID() === 0;
}
function parseNodeVersion() {
const version2 = process.version;
const [major, minor, patch] = version2.slice(1).split(".").map((v) => parseInt(v, 10));
return {
version: version2,
major: major || 0,
minor: minor || 0,
patch: patch || 0
};
}
// src/detectors/container.ts
var ContainerDetector = class extends BaseDetector {
constructor() {
super(...arguments);
this.name = "container";
}
performDetection() {
const isDocker = this.detectDocker();
const wslInfo = this.detectWSL();
const isKubernetes = this.detectKubernetes();
const isContainer = isDocker || wslInfo.isWSL || isKubernetes;
let containerType;
if (isDocker) {
containerType = "docker";
} else if (wslInfo.isWSL) {
containerType = "wsl";
} else if (isKubernetes) {
containerType = "kubernetes";
}
return {
isContainer,
isDocker,
isWSL: wslInfo.isWSL,
isKubernetes,
containerType,
wslVersion: wslInfo.version,
wslDistro: wslInfo.distro
};
}
detectDocker() {
if (fileExists(CONTAINER_FILES.DOCKER_ENV) || fileExists(CONTAINER_FILES.DOCKER_INIT)) {
return true;
}
const cgroupPath = "/proc/self/cgroup";
const cgroupContent = readFile(cgroupPath);
if (cgroupContent && (cgroupContent.includes("docker") || cgroupContent.includes("containerd"))) {
return true;
}
const initCgroupContent = readFile("/proc/1/cgroup");
if (initCgroupContent && (initCgroupContent.includes("docker") || initCgroupContent.includes("containerd"))) {
return true;
}
const mountInfo = readFile("/proc/self/mountinfo");
if (mountInfo && mountInfo.includes("docker")) {
return true;
}
return false;
}
detectWSL() {
if (os3.platform() === "win32") {
return { isWSL: false };
}
const wslDistro = getEnv(WSL_INDICATORS.ENV_VAR);
if (wslDistro) {
return {
isWSL: true,
version: this.getWSLVersion(),
distro: wslDistro
};
}
const procVersion = readFile("/proc/version");
if (procVersion && procVersion.toLowerCase().includes(WSL_INDICATORS.PROC_VERSION.toLowerCase())) {
return {
isWSL: true,
version: this.getWSLVersion(),
distro: this.getWSLDistro()
};
}
if (fileExists(WSL_INDICATORS.PROC_SYS) || isDirectory(CONTAINER_FILES.WSL_INTEROP)) {
return {
isWSL: true,
version: this.getWSLVersion(),
distro: this.getWSLDistro()
};
}
if (hasEnv("WSLENV") || hasEnv("WSL_INTEROP")) {
return {
isWSL: true,
version: this.getWSLVersion(),
distro: this.getWSLDistro()
};
}
return { isWSL: false };
}
detectKubernetes() {
if (isDirectory(CONTAINER_FILES.KUBERNETES_SERVICE)) {
return true;
}
if (hasEnv("KUBERNETES_SERVICE_HOST") || hasEnv("KUBERNETES_PORT")) {
return true;
}
const hostname2 = os3.hostname();
if (hostname2 && /^[a-z0-9]+-[a-z0-9]+-[a-z0-9]+(-[a-z0-9]+)*$/.test(hostname2)) {
if (fileExists("/var/run/secrets/kubernetes.io/serviceaccount/token")) {
return true;
}
}
return false;
}
getWSLVersion() {
const procVersion = readFile("/proc/version");
if (procVersion) {
if (procVersion.includes("WSL2") || procVersion.includes("microsoft-standard-WSL2")) {
return 2;
}
if (procVersion.includes("Microsoft") || procVersion.includes("WSL")) {
const kernelMatch = procVersion.match(/(\d+)\.(\d+)\.(\d+)/);
if (kernelMatch) {
const majorVersion = parseInt(kernelMatch[1] || "0", 10);
if (majorVersion >= 5) {
return 2;
}
}
return 1;
}
}
return void 0;
}
getWSLDistro() {
const envDistro = getEnv(WSL_INDICATORS.ENV_VAR);
if (envDistro) {
return envDistro;
}
const osRelease = readFile("/etc/os-release");
if (osRelease) {
const nameMatch = osRelease.match(/^NAME="?(.+?)"?$/m);
if (nameMatch) {
return nameMatch[1];
}
}
return void 0;
}
};
// src/detectors/ci.ts
var CIDetector = class extends BaseDetector {
constructor() {
super(...arguments);
this.name = "ci";
}
performDetection() {
const isCI = this.detectCI();
if (!isCI) {
return { isCI: false };
}
const provider = this.detectCIProvider();
const name = this.getCIName(provider);
const isPR = this.detectPullRequest(provider);
return {
isCI,
name,
isPR,
provider
};
}
detectCI() {
if (getEnvBoolean(CI_ENV_VARS.CI) || getEnvBoolean(CI_ENV_VARS.CONTINUOUS_INTEGRATION) || hasEnv(CI_ENV_VARS.BUILD_NUMBER) || hasEnv(CI_ENV_VARS.CI_NAME)) {
return true;
}
for (const envVar of Object.values(CI_PROVIDER_ENV_VARS)) {
if (hasEnv(envVar)) {
return true;
}
}
return false;
}
detectCIProvider() {
if (getEnvBoolean(CI_PROVIDER_ENV_VARS.GITHUB_ACTIONS)) {
return "github-actions";
}
if (getEnvBoolean(CI_PROVIDER_ENV_VARS.GITLAB_CI)) {
return "gitlab-ci";
}
if (getEnvBoolean(CI_PROVIDER_ENV_VARS.TRAVIS)) {
return "travis-ci";
}
if (getEnvBoolean(CI_PROVIDER_ENV_VARS.CIRCLECI)) {
return "circleci";
}
if (hasEnv(CI_PROVIDER_ENV_VARS.JENKINS_URL)) {
return "jenkins";
}
if (getEnvBoolean(CI_PROVIDER_ENV_VARS.TF_BUILD)) {
return "azure-pipelines";
}
if (hasEnv(CI_PROVIDER_ENV_VARS.BITBUCKET_BUILD_NUMBER)) {
return "bitbucket-pipelines";
}
if (hasEnv(CI_PROVIDER_ENV_VARS.TEAMCITY_VERSION)) {
return "teamcity";
}
if (getEnvBoolean(CI_PROVIDER_ENV_VARS.APPVEYOR)) {
return "appveyor";
}
if (hasEnv(CI_PROVIDER_ENV_VARS.CODEBUILD_BUILD_ID)) {
return "codebuild";
}
return "unknown";
}
getCIName(provider) {
switch (provider) {
case "github-actions":
return "GitHub Actions";
case "gitlab-ci":
return "GitLab CI";
case "travis-ci":
return "Travis CI";
case "circleci":
return "CircleCI";
case "jenkins":
return "Jenkins";
case "azure-pipelines":
return "Azure Pipelines";
case "bitbucket-pipelines":
return "Bitbucket Pipelines";
case "teamcity":
return "TeamCity";
case "appveyor":
return "AppVeyor";
case "codebuild":
return "AWS CodeBuild";
default:
return getEnv(CI_ENV_VARS.CI_NAME) || "Unknown CI";
}
}
detectPullRequest(provider) {
var _a;
switch (provider) {
case "github-actions":
return getEnv("GITHUB_EVENT_NAME") === "pull_request";
case "gitlab-ci":
return hasEnv("CI_MERGE_REQUEST_ID") || hasEnv("CI_EXTERNAL_PULL_REQUEST_IID");
case "travis-ci":
return getEnv("TRAVIS_PULL_REQUEST") !== "false";
case "circleci":
return hasEnv("CIRCLE_PULL_REQUEST") || hasEnv("CIRCLE_PR_NUMBER");
case "jenkins":
return hasEnv("CHANGE_ID") || hasEnv("ghprbPullId");
case "azure-pipelines":
return hasEnv("SYSTEM_PULLREQUEST_PULLREQUESTID");
case "bitbucket-pipelines":
return hasEnv("BITBUCKET_PR_ID");
case "teamcity":
return hasEnv("TEAMCITY_PULL_REQUEST_NUMBER");
case "appveyor":
return hasEnv("APPVEYOR_PULL_REQUEST_NUMBER");
case "codebuild":
return hasEnv("CODEBUILD_WEBHOOK_HEAD_REF") && ((_a = getEnv("CODEBUILD_WEBHOOK_HEAD_REF")) == null ? void 0 : _a.startsWith("refs/pull/")) || false;
default:
return hasEnv("PR_NUMBER") || hasEnv("PULL_REQUEST_ID") || hasEnv("PULL_REQUEST");
}
}
};
// src/detectors/cloud.ts
var CloudDetector = class extends BaseDetector {
constructor() {
super(...arguments);
this.name = "cloud";
}
performDetection() {
const provider = this.detectCloudProvider();
const isCloud = provider !== void 0;
const isServerless = this.isServerlessEnvironment(provider);
const functionName = this.getFunctionName(provider);
const region = this.getRegion(provider);
return {
isCloud,
provider,
isServerless,
functionName,
region
};
}
detectCloudProvider() {
if (hasEnv(CLOUD_ENV_VARS.AWS_LAMBDA_FUNCTION_NAME) || hasEnv(CLOUD_ENV_VARS.AWS_EXECUTION_ENV)) {
return "aws-lambda";
}
if (hasEnv(CLOUD_ENV_VARS.FUNCTION_NAME) || hasEnv(CLOUD_ENV_VARS.K_SERVICE)) {
return "google-cloud-functions";
}
if (hasEnv(CLOUD_ENV_VARS.WEBSITE_SITE_NAME) && hasEnv("FUNCTIONS_EXTENSION_VERSION")) {
return "azure-functions";
}
if (hasEnv(CLOUD_ENV_VARS.VERCEL) || hasEnv(CLOUD_ENV_VARS.VERCEL_ENV)) {
return "vercel";
}
if (hasEnv(CLOUD_ENV_VARS.NETLIFY) || hasEnv("NETLIFY_BUILD_BASE")) {
return "netlify";
}
if (hasEnv(CLOUD_ENV_VARS.CF_WORKER) || this.detectCloudflareWorkers()) {
return "cloudflare-workers";
}
if (this.detectAWS()) {
return "aws-lambda";
}
return void 0;
}
isServerlessEnvironment(provider) {
if (!provider) {
return false;
}
switch (provider) {
case "aws-lambda":
case "google-cloud-functions":
case "azure-functions":
case "cloudflare-workers":
return true;
case "vercel":
case "netlify":
return hasEnv("VERCEL_FUNCTION") || hasEnv("NETLIFY_FUNCTION_NAME") || hasEnv("NETLIFY_DEV");
default:
return false;
}
}
getFunctionName(provider) {
if (!provider) {
return void 0;
}
switch (provider) {
case "aws-lambda":
return getEnv(CLOUD_ENV_VARS.AWS_LAMBDA_FUNCTION_NAME);
case "google-cloud-functions":
return getEnv(CLOUD_ENV_VARS.FUNCTION_NAME) || getEnv(CLOUD_ENV_VARS.K_SERVICE);
case "azure-functions":
return getEnv(CLOUD_ENV_VARS.WEBSITE_SITE_NAME);
case "vercel":
return getEnv("VERCEL_FUNCTION");
case "netlify":
return getEnv("NETLIFY_FUNCTION_NAME");
case "cloudflare-workers":
return getEnv("CF_WORKER_NAME") || getEnv("WORKER_NAME");
default:
return void 0;
}
}
getRegion(provider) {
if (!provider) {
return void 0;
}
switch (provider) {
case "aws-lambda":
return getEnv(CLOUD_ENV_VARS.AWS_REGION) || getEnv("AWS_DEFAULT_REGION");
case "google-cloud-functions":
return getEnv("FUNCTION_REGION") || getEnv("GCP_REGION");
case "azure-functions":
return getEnv("REGION_NAME") || getEnv("AZURE_REGION");
case "vercel":
return getEnv("VERCEL_REGION");
case "cloudflare-workers":
return getEnv("CF_REGION");
default:
return void 0;
}
}
detectAWS() {
if (hasEnv("AWS_EXECUTION_ENV") || hasEnv("AWS_LAMBDA_RUNTIME_API") || hasEnv("_HANDLER") || hasEnv("LAMBDA_TASK_ROOT") || hasEnv("LAMBDA_RUNTIME_DIR")) {
return true;
}
if (hasEnv("ECS_CONTAINER_METADATA_URI") || hasEnv("ECS_CONTAINER_METADATA_URI_V4")) {
return true;
}
if (fileExists("/sys/hypervisor/uuid")) {
return true;
}
return false;
}
detectCloudflareWorkers() {
if (typeof global !== "undefined" && "caches" in global && "default" in global.caches) {
return true;
}
try {
const globalAny = global;
if (globalAny.CloudflareWorker || globalAny.MINIFLARE || globalAny.navigator && globalAny.navigator.userAgent === "Cloudflare-Workers") {
return true;
}
} catch {
}
return false;
}
};
// src/detectors/node.ts
var os4 = __toESM(require("os"));
var NodeDetector = class extends BaseDetector {
constructor() {
super(...arguments);
this.name = "node";
}
performDetection() {
const { version: version2, major, minor, patch } = parseNodeVersion();
const arch3 = os4.arch();
const platform5 = os4.platform();
return {
version: version2,
major,
minor,
patch,
arch: arch3,
platform: platform5
};
}
};
var EnvironmentModeDetector = class extends BaseDetector {
constructor() {
super(...arguments);
this.name = "mode";
}
performDetection() {
var _a;
const nodeEnv = (_a = getEnv("NODE_ENV")) == null ? void 0 : _a.toLowerCase();
switch (nodeEnv) {
case NODE_ENV_VALUES.PRODUCTION:
return "production";
case NODE_ENV_VALUES.DEVELOPMENT:
return "development";
case NODE_ENV_VALUES.TEST:
return "test";
case NODE_ENV_VALUES.STAGING:
return "staging";
default:
return "development";
}
}
};
// src/detectors/privileges.ts
var os5 = __toESM(require("os"));
var PrivilegeDetector = class extends BaseDetector {
constructor() {
super(...arguments);
this.name = "privileges";
}
performDetection() {
const platform5 = os5.platform();
const uid = getUID();
const gid = getGID();
const username = getUsername();
const isRootUser = isRoot();
const isAdminUser = this.isAdmin(platform5);
const isElevated = isRootUser || isAdminUser;
return {
isElevated,
isRoot: isRootUser,
isAdmin: isAdminUser,
uid,
gid,
username
};
}
isAdmin(platform5) {
switch (platform5) {
case "win32":
return this.isWindowsAdmin();
case "darwin":
case "linux":
return this.isUnixAdmin();
default:
return false;
}
}
isWindowsAdmin() {
try {
const result = execSync2("net session 2>nul");
return result !== null;
} catch {
try {
const whoami = execSync2("whoami /groups");
if (whoami) {
return whoami.toLowerCase().includes("s-1-5-32-544") || whoami.toLowerCase().includes("administrators");
}
} catch {
}
try {
const systemDir = execSync2('dir "%SystemRoot%\\System32\\config" 2>nul');
return systemDir !== null;
} catch {
return false;
}
}
}
isUnixAdmin() {
if (isRoot()) {
return true;
}
try {
const groups = execSync2("groups");
if (groups) {
const groupList = groups.toLowerCase();
return groupList.includes("admin") || groupList.includes("sudo") || groupList.includes("wheel") || groupList.includes("root");
}
} catch {
try {
const sudoCheck = execSync2("sudo -n true 2>/dev/null");
return sudoCheck !== null;
} catch {
return false;
}
}
return false;
}
};
// src/plugins/base.ts
var PluginContextImpl = class {
constructor() {
this.detectors = /* @__PURE__ */ new Map();
this.eventHandlers = /* @__PURE__ */ new Map();
}
registerDetector(detector) {
this.detectors.set(detector.name, detector);
}
unregisterDetector(name) {
this.detectors.delete(name);
}
getDetector(name) {
return this.detectors.get(name);
}
getAllDetectors() {
return Array.from(this.detectors.values());
}
emit(event, data) {
const handlers = this.eventHandlers.get(event);
if (handlers) {
for (const handler of handlers) {
try {
const result = handler(data);
if (result instanceof Promise) {
result.catch((error) => {
console.error(`Error in event handler for '${event}':`, error);
});
}
} catch (error) {
console.error(`Error in event handler for '${event}':`, error);
}
}
}
}
on(event, handler) {
if (!this.eventHandlers.has(event)) {
this.eventHandlers.set(event, []);
}
this.eventHandlers.get(event).push(handler);
}
off(event, handler) {
const handlers = this.eventHandlers.get(event);
if (handlers) {
const index = handlers.indexOf(handler);
if (index > -1) {
handlers.splice(index, 1);
}
}
}
};
// src/plugins/manager.ts
var PluginManagerImpl = class _PluginManagerImpl {
constructor() {
this.plugins = /* @__PURE__ */ new Map();
this.context = new PluginContextImpl();
}
static getInstance() {
if (!_PluginManagerImpl.instance) {
_PluginManagerImpl.instance = new _PluginManagerImpl();
}
return _PluginManagerImpl.instance;
}
async use(plugin) {
var _a;
if (this.plugins.has(plugin.name)) {
throw new Error(`Plugin '${plugin.name}' is already installed`);
}
try {
await ((_a = plugin.install) == null ? void 0 : _a.call(plugin, this.context));
this.plugins.set(plugin.name, plugin);
this.context.emit("plugin:installed", { plugin });
} catch (error) {
this.context.emit("plugin:error", { plugin, error });
throw new Error(`Failed to install plugin '${plugin.name}': ${error}`);
}
}
async remove(pluginName) {
var _a;
const plugin = this.plugins.get(pluginName);
if (!plugin) {
throw new Error(`Plugin '${pluginName}' is not installed`);
}
try {
for (const detector of plugin.detectors) {
this.context.unregisterDetector(detector.name);
}
await ((_a = plugin.uninstall) == null ? void 0 : _a.call(plugin));
this.plugins.delete(pluginName);
this.context.emit("plugin:removed", { plugin });
} catch (error) {
this.context.emit("plugin:error", { plugin, error });
throw new Error(`Failed to remove plugin '${pluginName}': ${error}`);
}
}
get(pluginName) {
return this.plugins.get(pluginName);
}
getAll() {
return Array.from(this.plugins.values());
}
clear() {
for (const pluginName of this.plugins.keys()) {
try {
this.remove(pluginName);
} catch (error) {
console.error(`Error removing plugin '${pluginName}':`, error);
}
}
}
getContext() {
return this.context;
}
};
// src/version.ts
var VERSION = "1.0.0";
var BUILD_DATE = (/* @__PURE__ */ new Date()).toISOString();
var NODE_VERSION = process.version;
// src/index.ts
var EnvironmentDetector = class {
constructor(options) {
this.osDetector = new OSDetector();
this.containerDetector = new ContainerDetector();
this.ciDetector = new CIDetector();
this.cloudDetector = new CloudDetector();
this.nodeDetector = new NodeDetector();
this.modeDetector = new EnvironmentModeDetector();
this.privilegeDetector = new PrivilegeDetector();
this.pluginManager = PluginManagerImpl.getInstance();
this.cache = Cache.getInstance();
if (options) {
this.osDetector = new OSDetector(options);
this.containerDetector = new ContainerDetector(options);
this.ciDetector = new CIDetector(options);
this.cloudDetector = new CloudDetector(options);
this.nodeDetector = new NodeDetector(options);
this.modeDetector = new EnvironmentModeDetector(options);
this.privilegeDetector = new PrivilegeDetector(options);
if (options.cache === false) {
this.cache.disable();
}
}
}
getEnvironmentInfo() {
return {
os: this.osDetector.detect(),
container: this.containerDetector.detect(),
ci: this.ciDetector.detect(),
cloud: this.cloudDetector.detect(),
node: this.nodeDetector.detect(),
privileges: this.privilegeDetector.detect(),
mode: this.modeDetector.detect()
};
}
async getEnvironmentInfoAsync() {
const [os6, container, ci, cloud, node, privileges, mode] = await Promise.all([
Promise.resolve(this.osDetector.detect()),
Promise.resolve(this.containerDetector.detect()),
Promise.resolve(this.ciDetector.detect()),
Promise.resolve(this.cloudDetector.detect()),
Promise.resolve(this.nodeDetector.detect()),
Promise.resolve(this.privilegeDetector.detect()),
Promise.resolve(this.modeDetector.detect())
]);
return { os: os6, container, ci, cloud, node, privileges, mode };
}
// Convenience getters for individual detections
get isWindows() {
return this.osDetector.detect().isWindows;
}
get isMacOS() {
return this.osDetector.detect().isMacOS;
}
get isLinux() {
return this.osDetector.detect().isLinux;
}
get isWSL() {
return this.containerDetector.detect().isWSL;
}
get isDocker() {
return this.containerDetector.detect().isDocker;
}
get isKubernetes() {
return this.containerDetector.detect().isKubernetes;
}
get isContainer() {
return this.containerDetector.detect().isContainer;
}
get isCI() {
return this.ciDetector.detect().isCI;
}
get isCloud() {
return this.cloudDetector.detect().isCloud;
}
get isServerless() {
return this.cloudDetector.detect().isServerless;
}
get isElevated() {
return this.privilegeDetector.detect().isElevated;
}
get isRoot() {
return this.privilegeDetector.detect().isRoot;
}
get isAdmin() {
return this.privilegeDetector.detect().isAdmin;
}
// Plugin system
use(plugin) {
this.pluginManager.use(plugin);
}
removePlugin(pluginName) {
this.pluginManager.remove(pluginName);
}
// Cache management
clearCache() {
this.cache.clear();
}
enableCache() {
this.cache.enable();
}
disableCache() {
this.cache.disable();
}
// Reset all detectors
reset() {
this.osDetector.reset();
this.containerDetector.reset();
this.ciDetector.reset();
this.cloudDetector.reset();
this.nodeDetector.reset();
this.modeDetector.reset();
this.privilegeDetector.reset();
}
// Get version information
getVersion() {
return VERSION;
}
// Get detailed package information
getPackageInfo() {
return {
version: VERSION,
detectors: ["os", "container", "ci", "cloud", "node", "privileges", "mode"],
cacheEnabled: this.cache.isEnabled()
};
}
// Quick environment summary
getEnvironmentSummary() {
const info = this.getEnvironmentInfo();
const parts = [info.os.type];
if (info.container.isContainer) {
parts.push(info.container.containerType || "container");
}
if (info.ci.isCI) {
parts.push("ci");
}
if (info.cloud.isCloud) {
parts.push("cloud");
}
if (info.privileges.isElevated) {
parts.push("elevated");
}
return parts.join("+");
}
};
var env = new EnvironmentDetector();
// cli/index.ts
var fs2 = __toESM(require("fs"));
var path = __toESM(require("path"));
var CLI = class {
constructor() {
this.env = new EnvironmentDetector();
this.options = {};
}
async run() {
this.parseArgs();
if (this.options.help) {
this.showHelp();
return;
}
if (this.options.version) {
this.showVersion();
return;
}
if (this.options.list) {
this.listDetectors();
return;
}
if (this.options.plugin) {
await this.loadPlugin(this.options.plugin);
}
if (this.options.benchmark) {
await this.runBenchmark();
return;
}
if (this.options.check) {
await this.checkEnvironment(this.options.check);
return;
}
await this.showEnvironmentInfo();
}
parseArgs() {
const args = process.argv.slice(2);
for (let i = 0; i < args.length; i++) {
const arg = args[i];
switch (arg) {
case "--json":
this.options.json = true;
break;
case "--check":
this.options.check = args[++i];
break;
case "--list":
this.options.list = true;
break;
case "--plugin":
this.options.plugin = args[++i];
break;
case "--verbose":
this.options.verbose = true;
break;
case "--benchmark":
this.options.benchmark = true;
break;
case "--help":
case "-h":
this.options.help = true;
break;
case "--version":
case "-v":
this.options.version = true;
break;
default:
console.error(`Unknown option: ${arg}`);
process.exit(1);
}
}
}
showHelp() {
console.log(`
/environment-detector CLI
Usage: environment-detector [options]
Options:
--json Output full environment info as JSON
--check <env> Check specific environment (returns exit code)
--list List all available detectors
--plugin <path> Load custom plugin
--verbose Detailed detection process
--benchmark Run performance benchmarks
--help, -h Show this help message
--version, -v Show version number
Examples:
environment-detector --json
environment-detector --check docker
environment-detector --check wsl
environment-detector --list
environment-detector --plugin ./my-plugin.js
environment-detector --benchmark
Check environment values:
windows, macos, linux, wsl, docker, kubernetes, container,
ci, cloud, serverless, elevated, root, admin
Exit codes:
0 - Success (environment detected or general success)
1 - Failure (environment not detected or error)
`);
}
showVersion() {
try {
const packageJson = JSON.parse(
fs2.readFileSync(path.join(__dirname, "..", "package.json"), "utf8")
);
console.log(packageJson.version);
} catch {
console.log("Unknown version");
}
}
listDetectors() {
const detectors = [
"os - Operating system detection",
"container - Container environment detection (Docker, WSL, Kubernetes)",
"ci - CI/CD environment detection",
"cloud - Cloud platform detection",
"node - Node.js version and runtime info",
"privileges - User privilege detection",
"mode - Environment mode (development, production, etc.)"
];
console.log("Available detectors:");
detectors.forEach((detector) => console.log(` ${detector}`));
}
async loadPlugin(pluginPath) {
try {
const resolvedPath = path.resolve(pluginPath);
const plugin = await import(resolvedPath);
if (plugin.default) {
await this.env.use(plugin.default);
if (this.options.verbose) {
console.log(`Loaded plugin: ${plugin.default.name}`);
}
} else {
throw new Error("Plugin must export a default plugin object");
}
} catch (error) {
console.error(`Failed to load plugin: ${error}`);
process.exit(1);
}
}
async runBenchmark() {
console.log("Running performance benchmarks...\n");
const benchmarks = [
{ name: "OS Detection", fn: () => this.env.isWindows },
{ name: "Container Detection", fn: () => this.env.isContainer },
{ name: "CI Detection", fn: () => this.env.isCI },
{ name: "Cloud Detection", fn: () => this.env.isCloud },
{ name: "Privilege Detection", fn: () => this.env.isElevated },
{ name: "Full Environment Info", fn: () => this.env.getEnvironmentInfo() }
];
for (const benchmark of benchmarks) {
const iterations = 1e3;
const start = process.hrtime.bigint();
for (let i = 0; i < iterations; i++) {
benchmark.fn();
}
const end = process.hrtime.bigint();
const totalMs = Number(end - start) / 1e6;
const avgMs = totalMs / iterations;
console.log(`${benchmark.name}:`);
console.log(` Total: ${totalMs.toFixed(2)}ms`);
console.log(` Average: ${avgMs.toFixed(4)}ms per call`);
console.log(` Rate: ${(1e3 / avgMs).toFixed(0)} calls/second
`);
}
const memUsage = process.memoryUsage();
console.log("Memory Usage:");
console.log(` RSS: ${(memUsage.rss / 1024 / 1024).toFixed(2)} MB`);
console.log(` Heap Used: ${(memUsage.heapUsed / 1024 / 1024).toFixed(2)} MB`);
console.log(` Heap Total: ${(memUsage.heapTotal / 1024 / 1024).toFixed(2)} MB`);
}
async checkEnvironment(envCheck) {
const env2 = envCheck.toLowerCase();
let result = false;
let description = "";
switch (env2) {
case "windows":
result = this.env.isWindows;
description = "Windows operating system";
break;
case "macos":
result = this.env.isMacOS;
description = "macOS operating system";
break;
case "linux":
result = this.env.isLinux;
description = "Linux operating system";
break;
case "wsl":
result = this.env.isWSL;
description = "Windows Subsystem for Linux";
break;
case "docker":
result = this.env.isDocker;
description = "Docker container";
break;
case "kubernetes":
result = this.env.isKubernetes;
description = "Kubernetes pod";
break;
case "container":
result = this.env.isContainer;
description = "Any container environment";
break;
case "ci":
result = this.env.isCI;
description = "CI/CD environment";
break;
case "cloud":
result = this.env.isCloud;
description = "Cloud platform";
break;
case "serverless":
result = this.env.isServerless;
description = "Serverless environment";
break;
case "elevated":
result = this.env.isElevated;
description = "Elevated privileges";
break;
case "root":
result = this.env.isRoot;
description = "Root user";
break;
case "admin":
result = this.env.isAdmin;
description = "Administrator user";
break;
default:
console.error(`Unknown environment check: ${envCheck}`);
process.exit(1);
}
if (this.options.verbose) {
console.log(`Checking for ${description}...`);
}
if (this.options.json) {
console.log(JSON.stringify({ [env2]: result }, null, 2));
} else if (this.options.verbose) {
console.log(result ? `\u2713 ${description} detected` : `\u2717 ${description} not detected`);
} else {
console.log(result.toString());
}
process.exit(result ? 0 : 1);
}
async showEnvironmentInfo() {
const info = this.env.getEnvironmentInfo();
if (this.options.json) {
console.log(JSON.stringify(info, null, 2));
} else {
this.printHumanReadable(info);
}
}
printHumanReadable(info) {
console.log("Environment Detection Results");
console.log("============================\n");
console.log("Operating System:");
console.log(` Platform: ${info.os.type} (${info.os.platform})`);
console.log(` Version: ${info.os.version}`);
console.log(` Architecture: ${info.os.arch}
`);
console.log("Container Environment:");
if (info.container.isContainer) {
console.log(` Type: ${info.container.containerType}`);
if (info.container.isWSL) {
console.log(` WSL Version: ${info.container.wslVersion}`);
if (info.container.wslDistro) {
console.log(` WSL Distribution: ${info.container.wslDistro}`);
}
}
} else {
console.log(" No container environment detected");
}
console.log();
console.log("CI/CD Environment:");
if (info.ci.isCI) {
console.log(` Provider: ${info.ci.name} (${info.ci.provider})`);
console.log(` Pull Request: ${info.ci.isPR ? "Yes" : "No"}`);
} else {
console.log(" No CI/CD environment detected");
}
console.log();
console.log("Cloud Environment:");
if (info.cloud.isCloud) {
console.log(` Provider: ${info.cloud.provider}`);
console.log(` Serverless: ${info.cloud.isServerless ? "Yes" : "No"}`);
if (info.cloud.functionName) {
console.log(` Function: ${info.cloud.functionName}`);
}
if (info.cloud.region) {
console.log(` Region: ${info.cloud.region}`);
}
} else {
console.log(" No cloud environment detected");
}
console.log();
console.log("Node.js Runtime:");
console.log(` Version: ${info.node.version}`);
console.log(` Architecture: ${info.node.arch}`);
console.log(` Platform: ${info.node.platform}
`);
console.log("User Privileges:");
console.log(` Elevated: ${info.privileges.isElevated ? "Yes" : "No"}`);
console.log(` Root: ${info.privileges.isRoot ? "Yes" : "No"}`);
console.log(` Admin: ${info.privileges.isAdmin ? "Yes" : "No"}`);
if (info.privileges.username) {
console.log(` Username: ${info.privileges.username}`);
}
if (info.privileges.uid !== void 0) {
console.log(` UID: ${info.privileges.uid}`);
}
console.log();
console.log(`Environment Mode: ${info.mode}`);
}
};
var cli = new CLI();
cli.run().catch((error) => {
console.error("CLI Error:", error);
process.exit(1);
});
//# sourceMappingURL=index.js.map