New:Socket for Asana Is Now Available.Learn more
Get Started

@itsthw/envguard

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@itsthw/envguard - npm Package Compare versions

Comparing version
2.1.0
to
2.1.1
+94
-80
dist/envguard.cjs

@@ -28,26 +28,58 @@ #!/usr/bin/env node

var import_commander = require("commander");
var import_dotenv = __toESM(require("dotenv"), 1);
var import_fs = __toESM(require("fs"), 1);
var import_node_fs2 = __toESM(require("fs"), 1);
var import_node_path2 = __toESM(require("path"), 1);
// src/validate.ts
function parse(value, type) {
if (type === "string") return value;
if (type === "number") {
const n = Number(value);
return Number.isFinite(n) ? n : null;
// src/scan.ts
var import_node_fs = __toESM(require("fs"), 1);
var import_node_path = __toESM(require("path"), 1);
function scanProjectForEnvKeys(rootDir) {
const exts = /* @__PURE__ */ new Set([".js", ".ts", ".jsx", ".tsx", ".mjs", ".cjs"]);
const ignoreDirs = /* @__PURE__ */ new Set([
"node_modules",
".git",
"dist",
"build",
".next"
]);
const keys = /* @__PURE__ */ new Set();
walk(rootDir);
return keys;
function walk(dir) {
for (const entry of import_node_fs.default.readdirSync(dir, { withFileTypes: true })) {
const full = import_node_path.default.join(dir, entry.name);
if (entry.isDirectory()) {
if (!ignoreDirs.has(entry.name)) walk(full);
continue;
}
if (!exts.has(import_node_path.default.extname(entry.name))) continue;
const content = safeRead(full);
if (!content) continue;
for (const m of content.matchAll(/\bprocess\.env\.([A-Z0-9_]+)\b/g)) {
keys.add(m[1]);
}
for (const m of content.matchAll(
/\bprocess\.env\[\s*["']([A-Z0-9_]+)["']\s*\]/g
)) {
keys.add(m[1]);
}
for (const m of content.matchAll(
/\bimport\.meta\.env\.([A-Z0-9_]+)\b/g
)) {
keys.add(m[1]);
}
}
}
if (type === "boolean") {
if (value === "true") return true;
if (value === "false") return false;
return null;
function safeRead(file) {
try {
return import_node_fs.default.readFileSync(file, "utf8");
} catch {
return null;
}
}
if (typeof type === "object" && "enum" in type) {
return type.enum.includes(value) ? value : null;
}
return null;
}
function validateEnv(schema) {
// src/validate.ts
function validateProcessEnv(schema) {
const errors = [];
const values = {};
for (const key in schema) {
for (const [key, type] of Object.entries(schema)) {
const raw = process.env[key];

@@ -58,75 +90,57 @@ if (!raw) {

}
const parsed = parse(raw, schema[key]);
if (parsed === null) {
errors.push(`Invalid env: ${key}=${raw}`);
continue;
if (type === "number" && !Number.isFinite(Number(raw))) {
errors.push(`Invalid number: ${key}="${raw}"`);
}
values[key] = parsed;
}
if (errors.length) {
throw new Error(errors.join("\n"));
}
return values;
}
// src/example.ts
function placeholder(type) {
if (type === "number") return "3000";
if (type === "boolean") return "true";
if (typeof type === "object") return type.enum[0];
return "value";
}
function generateExample(schema) {
return Object.entries(schema).map(([k, t]) => `${k}=${placeholder(t)}`).join("\n") + "\n";
}
// src/infer.ts
function inferSchema(env) {
const schema = {};
for (const [key, value] of Object.entries(env)) {
if (!value) continue;
if (key.startsWith("npm_") || key.startsWith("NODE_") || key.startsWith("PATH") || key.startsWith("HOME")) {
continue;
if (type === "boolean" && !(raw === "true" || raw === "false")) {
errors.push(`Invalid boolean: ${key}="${raw}"`);
}
schema[key] = inferType(value);
}
return schema;
return { ok: errors.length === 0, errors };
}
function inferType(value) {
if (/^-?\d+$/.test(value)) return "number";
if (value === "true" || value === "false") return "boolean";
return "string";
}
// src/envguard.ts
import_dotenv.default.config();
var program = new import_commander.Command();
var SCHEMA_FILE = "envguard.schema.json";
var EXAMPLE_FILE = ".env.example";
function loadSchema() {
return JSON.parse(import_fs.default.readFileSync(SCHEMA_FILE, "utf8"));
}
program.name("envguard").description("Ultra-light env validation").version("1.0.0");
program.command("init").description("Auto-generate envguard.schema.json and .env.example").action(() => {
const schema = inferSchema(process.env);
if (Object.keys(schema).length === 0) {
console.error("\u274C No environment variables found to infer schema.");
var DEFAULT_SCHEMA = "envguard.schema.json";
var DEFAULT_ENV_FILE = ".env";
program.name("envguard").description("Lightweight env discovery & validation tool").version("1.0.0");
program.command("init").option("--root <dir>", "Project root to scan", ".").option("--env <file>", "Env file to generate", DEFAULT_ENV_FILE).option("--schema <file>", "Schema file to generate", DEFAULT_SCHEMA).option("--force", "Overwrite files if they exist").description("Scan project and generate env files").action((opts) => {
const root = import_node_path2.default.resolve(process.cwd(), opts.root);
const envPath = import_node_path2.default.resolve(process.cwd(), opts.env);
const schemaPath = import_node_path2.default.resolve(process.cwd(), opts.schema);
const keys = scanProjectForEnvKeys(root);
if (keys.size === 0) {
console.error("\u274C No environment variables found in project source.");
process.exit(1);
}
import_fs.default.writeFileSync(SCHEMA_FILE, JSON.stringify(schema, null, 2));
import_fs.default.writeFileSync(EXAMPLE_FILE, generateExample(schema));
console.log("\u2705 envguard initialized");
console.log(` - ${SCHEMA_FILE}`);
console.log(` - ${EXAMPLE_FILE}`);
if (!opts.force) {
if (import_node_fs2.default.existsSync(envPath) || import_node_fs2.default.existsSync(schemaPath)) {
console.error("\u274C Files already exist. Use --force to overwrite.");
process.exit(1);
}
}
const schema = {};
for (const key of keys) schema[key] = "string";
import_node_fs2.default.writeFileSync(schemaPath, JSON.stringify(schema, null, 2));
const envContent = [...keys].map((k) => `${k}=`).join("\n") + "\n";
import_node_fs2.default.writeFileSync(envPath, envContent);
console.log("\u2705 EnvGuard initialized");
console.log(` - ${opts.env}`);
console.log(` - ${opts.schema}`);
console.log(` - ${keys.size} env vars discovered`);
});
program.command("check").description("Validate process.env").action(() => {
try {
validateEnv(loadSchema());
console.log("\u2705 env OK");
} catch (e) {
console.error("\u274C env invalid");
console.error(e.message);
program.command("check").option("--schema <file>", "Schema file", DEFAULT_SCHEMA).description("Validate process.env against schema").action((opts) => {
const schemaPath = import_node_path2.default.resolve(process.cwd(), opts.schema);
if (!import_node_fs2.default.existsSync(schemaPath)) {
console.error("\u274C Schema file not found. Run `envguard init` first.");
process.exit(1);
}
const schema = JSON.parse(import_node_fs2.default.readFileSync(schemaPath, "utf8"));
const res = validateProcessEnv(schema);
if (!res.ok) {
console.error("\u274C Env validation failed:");
for (const e of res.errors) console.error(" -", e);
process.exit(1);
}
console.log("\u2705 Env OK");
});
program.parse();
program.parse(process.argv);
"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;

@@ -18,2 +20,10 @@ var __export = (target, all) => {

};
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
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

@@ -24,28 +34,60 @@

__export(index_exports, {
generateExample: () => generateExample,
validateEnv: () => validateEnv
scanProjectForEnvKeys: () => scanProjectForEnvKeys,
validateProcessEnv: () => validateProcessEnv
});
module.exports = __toCommonJS(index_exports);
// src/validate.ts
function parse(value, type) {
if (type === "string") return value;
if (type === "number") {
const n = Number(value);
return Number.isFinite(n) ? n : null;
// src/scan.ts
var import_node_fs = __toESM(require("fs"), 1);
var import_node_path = __toESM(require("path"), 1);
function scanProjectForEnvKeys(rootDir) {
const exts = /* @__PURE__ */ new Set([".js", ".ts", ".jsx", ".tsx", ".mjs", ".cjs"]);
const ignoreDirs = /* @__PURE__ */ new Set([
"node_modules",
".git",
"dist",
"build",
".next"
]);
const keys = /* @__PURE__ */ new Set();
walk(rootDir);
return keys;
function walk(dir) {
for (const entry of import_node_fs.default.readdirSync(dir, { withFileTypes: true })) {
const full = import_node_path.default.join(dir, entry.name);
if (entry.isDirectory()) {
if (!ignoreDirs.has(entry.name)) walk(full);
continue;
}
if (!exts.has(import_node_path.default.extname(entry.name))) continue;
const content = safeRead(full);
if (!content) continue;
for (const m of content.matchAll(/\bprocess\.env\.([A-Z0-9_]+)\b/g)) {
keys.add(m[1]);
}
for (const m of content.matchAll(
/\bprocess\.env\[\s*["']([A-Z0-9_]+)["']\s*\]/g
)) {
keys.add(m[1]);
}
for (const m of content.matchAll(
/\bimport\.meta\.env\.([A-Z0-9_]+)\b/g
)) {
keys.add(m[1]);
}
}
}
if (type === "boolean") {
if (value === "true") return true;
if (value === "false") return false;
return null;
function safeRead(file) {
try {
return import_node_fs.default.readFileSync(file, "utf8");
} catch {
return null;
}
}
if (typeof type === "object" && "enum" in type) {
return type.enum.includes(value) ? value : null;
}
return null;
}
function validateEnv(schema) {
// src/validate.ts
function validateProcessEnv(schema) {
const errors = [];
const values = {};
for (const key in schema) {
for (const [key, type] of Object.entries(schema)) {
const raw = process.env[key];

@@ -56,29 +98,15 @@ if (!raw) {

}
const parsed = parse(raw, schema[key]);
if (parsed === null) {
errors.push(`Invalid env: ${key}=${raw}`);
continue;
if (type === "number" && !Number.isFinite(Number(raw))) {
errors.push(`Invalid number: ${key}="${raw}"`);
}
values[key] = parsed;
if (type === "boolean" && !(raw === "true" || raw === "false")) {
errors.push(`Invalid boolean: ${key}="${raw}"`);
}
}
if (errors.length) {
throw new Error(errors.join("\n"));
}
return values;
return { ok: errors.length === 0, errors };
}
// src/example.ts
function placeholder(type) {
if (type === "number") return "3000";
if (type === "boolean") return "true";
if (typeof type === "object") return type.enum[0];
return "value";
}
function generateExample(schema) {
return Object.entries(schema).map(([k, t]) => `${k}=${placeholder(t)}`).join("\n") + "\n";
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
generateExample,
validateEnv
scanProjectForEnvKeys,
validateProcessEnv
});

@@ -1,10 +0,19 @@

type EnvType = "string" | "number" | "boolean" | {
enum: string[];
/**
* Scan project source code to find env usage.
* Supports:
* - process.env.MY_KEY
* - process.env["MY_KEY"]
* - import.meta.env.MY_KEY
*/
declare function scanProjectForEnvKeys(rootDir: string): Set<string>;
type Schema = Record<string, "string" | "number" | "boolean">;
/**
* Validate process.env against schema.
*/
declare function validateProcessEnv(schema: Schema): {
ok: boolean;
errors: string[];
};
type EnvSchema = Record<string, EnvType>;
declare function validateEnv(schema: EnvSchema): Record<string, any>;
declare function generateExample(schema: EnvSchema): string;
export { type EnvSchema, type EnvType, generateExample, validateEnv };
export { scanProjectForEnvKeys, validateProcessEnv };
{
"name": "@itsthw/envguard",
"version": "2.1.0",
"version": "2.1.1",
"type": "module",

@@ -12,4 +12,3 @@ "bin": {

"dependencies": {
"commander": "^12.0.0",
"dotenv": "^16.4.5"
"commander": "^12.0.0"
},

@@ -16,0 +15,0 @@ "scripts": {