@bintvn/lite-env 1.0.1 → 1.0.3

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # lite-env
1
+ # @bintvn/lite-env
2
2
 
3
- `lite-env` is a small TypeScript-first environment loader for Node.js. It reads `.env` and `.env.{NODE_ENV}`, merges them, parses values into runtime types, and returns a type-safe object inferred from your schema.
3
+ `@bintvn/lite-env` is a small TypeScript-first environment loader for Node.js. It reads `.env` and `.env.{NODE_ENV}`, merges them, parses values into runtime types, and returns a type-safe object inferred from your schema.
4
4
 
5
5
  ## Features
6
6
 
@@ -13,7 +13,7 @@
13
13
  ## Installation
14
14
 
15
15
  ```bash
16
- npm install lite-env
16
+ npm install @bintvn/lite-env
17
17
  ```
18
18
 
19
19
  ## Quick Start
@@ -22,7 +22,7 @@ Create your env files:
22
22
 
23
23
  ```env
24
24
  # .env
25
- APP_NAME=lite-env
25
+ APP_NAME=@bintvn/lite-env
26
26
  PORT=3000
27
27
  DEBUG=false
28
28
  ```
@@ -36,7 +36,7 @@ TAGS=api,dev,local
36
36
  Use `loadEnv` in your app:
37
37
 
38
38
  ```ts
39
- import { loadEnv } from 'lite-env'
39
+ import { loadEnv } from '@bintvn/lite-env'
40
40
 
41
41
  const env = loadEnv({
42
42
  APP_NAME: 'string',
@@ -113,7 +113,7 @@ const env = loadEnv({
113
113
 
114
114
  ## Supported Types
115
115
 
116
- `lite-env` currently supports the following schema kinds:
116
+ `@bintvn/lite-env` currently supports the following schema kinds:
117
117
 
118
118
  | Schema value | Result type | Notes |
119
119
  | --- | --- | --- |
@@ -127,7 +127,7 @@ const env = loadEnv({
127
127
 
128
128
  ## Type Inference
129
129
 
130
- The main goal of `lite-env` is type-safe inference from the input schema.
130
+ The main goal of `@bintvn/lite-env` is type-safe inference from the input schema.
131
131
 
132
132
  ```ts
133
133
  const env = loadEnv({
package/dist/index.cjs CHANGED
@@ -156,8 +156,9 @@ function loadEnv(allowedEnvKeys = {}, bypassUnknownEnvKeys = true, NODE_ENV) {
156
156
  } else {
157
157
  if (!process.env.NODE_ENV) {
158
158
  process.env.NODE_ENV = "production";
159
+ } else {
160
+ NODE_ENV = process.env.NODE_ENV;
159
161
  }
160
- NODE_ENV = process.env.NODE_ENV;
161
162
  }
162
163
  const defaultEnvPath = import_path.default.join(process.cwd(), ".env");
163
164
  const nodeEnvPath = import_path.default.join(process.cwd(), `.env.${NODE_ENV}`);
@@ -190,6 +191,10 @@ function loadEnv(allowedEnvKeys = {}, bypassUnknownEnvKeys = true, NODE_ENV) {
190
191
  ...process.env,
191
192
  ...parsedEnv.data
192
193
  };
194
+ parsedEnv.data = {
195
+ ...parsedEnv.data,
196
+ NODE_ENV
197
+ };
193
198
  console.log("Loaded env file", `${defaultEnvPath}, ${nodeEnvPath}`);
194
199
  return parsedEnv.data;
195
200
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/helper.ts"],"sourcesContent":["import path from 'path'\r\nimport { existsSync } from 'fs'\r\nimport { formatError, getUnknownEnvKeys, readEnvFile, safeParse } from './helper.js'\r\nimport { AllowedEnvKeys, LoadedEnv } from \"./types.js\";\r\n\r\nexport type { AllowedEnvKeys, EnvKind, EnvKindMap, LoadedEnv } from \"./types.js\"\r\n\r\ndeclare global {\r\n var liteEnv: unknown\r\n}\r\n\r\nexport function loadEnv<TSchema extends AllowedEnvKeys = {}>(\r\n allowedEnvKeys: TSchema = {} as TSchema,\r\n bypassUnknownEnvKeys: boolean = true,\r\n NODE_ENV?: string\r\n): LoadedEnv<TSchema> {\r\n if (globalThis.liteEnv)\r\n return globalThis.liteEnv as LoadedEnv<TSchema>\r\n\r\n if (NODE_ENV) {\r\n process.env.NODE_ENV = NODE_ENV\r\n } else {\r\n if (!process.env.NODE_ENV) {\r\n process.env.NODE_ENV = 'production'\r\n }\r\n\r\n NODE_ENV = process.env.NODE_ENV\r\n }\r\n\r\n const defaultEnvPath = path.join(process.cwd(), '.env')\r\n const nodeEnvPath = path.join(process.cwd(), `.env.${NODE_ENV}`)\r\n\r\n if (!existsSync(defaultEnvPath))\r\n throw new Error('Default Environment file not found')\r\n\r\n if (!existsSync(nodeEnvPath))\r\n throw new Error(`Environment file not found: .env.${NODE_ENV}`)\r\n\r\n const defaultEnvSource = readEnvFile(defaultEnvPath)\r\n const nodeEnvSource = readEnvFile(nodeEnvPath)\r\n\r\n const mergedEnv: Record<string, string> = {}\r\n\r\n if (defaultEnvSource)\r\n Object.assign(mergedEnv, defaultEnvSource)\r\n if (nodeEnvSource)\r\n Object.assign(mergedEnv, nodeEnvSource)\r\n\r\n if (!bypassUnknownEnvKeys) {\r\n const unknownKeys = [...new Set([...getUnknownEnvKeys(mergedEnv, new Set(Object.keys(allowedEnvKeys)))])]\r\n\r\n if (unknownKeys.length > 0)\r\n throw new Error(`Unknown environment variables in env files: ${unknownKeys.join(', ')}`)\r\n } else {\r\n process.env = {\r\n ...process.env,\r\n ...mergedEnv\r\n }\r\n }\r\n\r\n const parsedEnv = safeParse(allowedEnvKeys, mergedEnv)\r\n\r\n if (!parsedEnv.success)\r\n throw new Error(`Environment validation failed: ${formatError(parsedEnv.error)}`)\r\n\r\n globalThis.liteEnv = parsedEnv.data\r\n\r\n process.env = {\r\n ...process.env,\r\n ...parsedEnv.data\r\n }\r\n\r\n console.log('Loaded env file', `${defaultEnvPath}, ${nodeEnvPath}`)\r\n\r\n return parsedEnv.data as LoadedEnv<TSchema>\r\n}\r\n","import { existsSync, readFileSync } from \"fs\"\nimport { AllowedEnvKeys, EnvKind, EnvKindMap, LoadedEnv } from \"./types.js\"\n\r\nexport function toString(value: string): string {\r\n return String(value)\r\n}\r\n\r\nexport function toNumber(value: string): number {\n const parsed = Number(value)\n if (Number.isNaN(parsed))\n throw new Error('Value is not supported for number')\n\n return parsed\n}\n\r\nexport function toArray(value: string): string[] {\r\n if (!value.includes(','))\r\n throw new Error('Valur is not supported for array')\r\n\r\n return value.split(',')\r\n}\r\n\r\nexport function toBoolean(value: string): boolean {\n if (value === 'true' || value === '1')\n return true\n\n if (value === 'false' || value === '0')\n return false\n\n throw new Error('Value is not supported for boolean')\n}\n\r\nexport function toObject<T>(value: string): T {\r\n try {\r\n return JSON.parse(value)\r\n } catch (error) {\r\n throw new Error('Value is not supported for object')\r\n }\r\n}\r\n\r\nexport function toBuffer(value: string, encoding?: BufferEncoding): Buffer {\r\n try {\r\n return Buffer.from(value, encoding || 'base64')\r\n } catch (error) {\r\n throw new Error('Value is not supported for buffer')\r\n }\r\n}\r\n\r\nexport function toDate(value: string): Date {\n const parsed = new Date(value)\n if (Number.isNaN(parsed.getTime()))\n throw new Error('Value is not supported for date')\n\n return parsed\n}\n\nfunction parseEnvValue<TKind extends EnvKind>(kind: TKind, value: string): EnvKindMap[TKind] {\n switch (kind) {\n case \"string\":\n return toString(value) as EnvKindMap[TKind]\n case \"number\":\n return toNumber(value) as EnvKindMap[TKind]\n case \"boolean\":\n return toBoolean(value) as EnvKindMap[TKind]\n case \"array\":\n return toArray(value) as EnvKindMap[TKind]\n case \"object\":\n return toObject(value) as EnvKindMap[TKind]\n case \"buffer\":\n return toBuffer(value) as EnvKindMap[TKind]\n case \"date\":\n return toDate(value) as EnvKindMap[TKind]\n default: {\n const exhaustiveCheck: never = kind\n throw new Error(`Unsupported env kind: ${exhaustiveCheck}`)\n }\n }\n}\n\nexport function safeParse<TSchema extends AllowedEnvKeys>(allowedEnvKeys: TSchema, values: Record<string, string>) {\n const data: Partial<LoadedEnv<TSchema>> = {}\n const error: string[] = []\n\n for (const key of Object.keys(allowedEnvKeys) as Array<keyof TSchema>) {\n const value = values[key as string]\n if (value === undefined) continue\n\n try {\n data[key] = parseEnvValue(allowedEnvKeys[key], value)\n } catch (e) {\n if (e instanceof Error)\n error.push(`${String(key)}: ${e.message}`)\n else\n error.push(`${String(key)}: Unknown error occurred`)\n }\n }\n\n return {\n success: error.length === 0,\n data: data as LoadedEnv<TSchema>,\n error\n }\n}\n\r\nexport function formatError(message: string[]): string {\r\n return message.join('\\n')\r\n}\r\n\r\nexport function parseRawEnv(rawEnv: string): Record<string, string> {\r\n const result: Record<string, string> = {}\r\n const lines = rawEnv.split(/\\r?\\n/)\r\n\r\n for (const line of lines) {\r\n const normalized = line.trim()\r\n if (!normalized || normalized.startsWith('#')) continue\r\n\r\n const separatorIndex = normalized.indexOf('=')\r\n if (separatorIndex <= 0) continue\r\n\r\n const key = normalized.slice(0, separatorIndex).trim()\r\n const value = normalized.slice(separatorIndex + 1).trim()\r\n if (!key) continue\r\n\r\n result[key] = value\r\n }\r\n\r\n return result\r\n}\r\n\r\nexport function readEnvFile(filePath: string): false | Record<string, string> {\r\n if (!existsSync(filePath)) return false\r\n return parseRawEnv(readFileSync(filePath, 'utf-8'))\r\n}\r\n\r\nexport function getUnknownEnvKeys(source: Record<string, string>, allowedEnvKeys: Set<string>): string[] {\r\n return Object.keys(source).filter((key) => !allowedEnvKeys.has(key))\r\n}\r\n"],"mappings":"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","names":["import_fs","path"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/helper.ts"],"sourcesContent":["import path from 'path'\r\nimport { existsSync } from 'fs'\r\nimport { formatError, getUnknownEnvKeys, readEnvFile, safeParse } from './helper.js'\r\nimport { AllowedEnvKeys, LoadedEnv } from \"./types.js\";\r\n\r\nexport type { AllowedEnvKeys, EnvKind, EnvKindMap, LoadedEnv } from \"./types.js\"\r\n\r\ndeclare global {\r\n var liteEnv: unknown\r\n}\r\n\r\nexport function loadEnv<TSchema extends AllowedEnvKeys = {}>(\r\n allowedEnvKeys: TSchema = {} as TSchema,\r\n bypassUnknownEnvKeys: boolean = true,\r\n NODE_ENV?: string\r\n): LoadedEnv<TSchema> {\r\n if (globalThis.liteEnv)\r\n return globalThis.liteEnv as LoadedEnv<TSchema>\r\n\r\n if (NODE_ENV) {\r\n process.env.NODE_ENV = NODE_ENV\r\n } else {\r\n if (!process.env.NODE_ENV) {\r\n process.env.NODE_ENV = 'production'\r\n } else {\r\n NODE_ENV = process.env.NODE_ENV\r\n }\r\n }\r\n\r\n const defaultEnvPath = path.join(process.cwd(), '.env')\r\n const nodeEnvPath = path.join(process.cwd(), `.env.${NODE_ENV}`)\r\n\r\n if (!existsSync(defaultEnvPath))\r\n throw new Error('Default Environment file not found')\r\n\r\n if (!existsSync(nodeEnvPath))\r\n throw new Error(`Environment file not found: .env.${NODE_ENV}`)\r\n\r\n const defaultEnvSource = readEnvFile(defaultEnvPath)\r\n const nodeEnvSource = readEnvFile(nodeEnvPath)\r\n\r\n const mergedEnv: Record<string, string> = {}\r\n\r\n if (defaultEnvSource)\r\n Object.assign(mergedEnv, defaultEnvSource)\r\n if (nodeEnvSource)\r\n Object.assign(mergedEnv, nodeEnvSource)\r\n\r\n if (!bypassUnknownEnvKeys) {\r\n const unknownKeys = [...new Set([...getUnknownEnvKeys(mergedEnv, new Set(Object.keys(allowedEnvKeys)))])]\r\n\r\n if (unknownKeys.length > 0)\r\n throw new Error(`Unknown environment variables in env files: ${unknownKeys.join(', ')}`)\r\n } else {\r\n process.env = {\r\n ...process.env,\r\n ...mergedEnv\r\n }\r\n }\r\n\r\n const parsedEnv = safeParse(allowedEnvKeys, mergedEnv)\r\n\r\n if (!parsedEnv.success)\r\n throw new Error(`Environment validation failed: ${formatError(parsedEnv.error)}`)\r\n\r\n globalThis.liteEnv = parsedEnv.data\r\n\r\n process.env = {\r\n ...process.env,\r\n ...parsedEnv.data\r\n }\r\n\r\n parsedEnv.data = {\r\n ...parsedEnv.data,\r\n NODE_ENV\r\n }\r\n\r\n console.log('Loaded env file', `${defaultEnvPath}, ${nodeEnvPath}`)\r\n\r\n return parsedEnv.data as LoadedEnv<TSchema>\r\n}\r\n","import { existsSync, readFileSync } from \"fs\"\nimport { AllowedEnvKeys, EnvKind, EnvKindMap, LoadedEnv } from \"./types.js\"\n\r\nexport function toString(value: string): string {\r\n return String(value)\r\n}\r\n\r\nexport function toNumber(value: string): number {\n const parsed = Number(value)\n if (Number.isNaN(parsed))\n throw new Error('Value is not supported for number')\n\n return parsed\n}\n\r\nexport function toArray(value: string): string[] {\r\n if (!value.includes(','))\r\n throw new Error('Valur is not supported for array')\r\n\r\n return value.split(',')\r\n}\r\n\r\nexport function toBoolean(value: string): boolean {\n if (value === 'true' || value === '1')\n return true\n\n if (value === 'false' || value === '0')\n return false\n\n throw new Error('Value is not supported for boolean')\n}\n\r\nexport function toObject<T>(value: string): T {\r\n try {\r\n return JSON.parse(value)\r\n } catch (error) {\r\n throw new Error('Value is not supported for object')\r\n }\r\n}\r\n\r\nexport function toBuffer(value: string, encoding?: BufferEncoding): Buffer {\r\n try {\r\n return Buffer.from(value, encoding || 'base64')\r\n } catch (error) {\r\n throw new Error('Value is not supported for buffer')\r\n }\r\n}\r\n\r\nexport function toDate(value: string): Date {\n const parsed = new Date(value)\n if (Number.isNaN(parsed.getTime()))\n throw new Error('Value is not supported for date')\n\n return parsed\n}\n\nfunction parseEnvValue<TKind extends EnvKind>(kind: TKind, value: string): EnvKindMap[TKind] {\n switch (kind) {\n case \"string\":\n return toString(value) as EnvKindMap[TKind]\n case \"number\":\n return toNumber(value) as EnvKindMap[TKind]\n case \"boolean\":\n return toBoolean(value) as EnvKindMap[TKind]\n case \"array\":\n return toArray(value) as EnvKindMap[TKind]\n case \"object\":\n return toObject(value) as EnvKindMap[TKind]\n case \"buffer\":\n return toBuffer(value) as EnvKindMap[TKind]\n case \"date\":\n return toDate(value) as EnvKindMap[TKind]\n default: {\n const exhaustiveCheck: never = kind\n throw new Error(`Unsupported env kind: ${exhaustiveCheck}`)\n }\n }\n}\n\nexport function safeParse<TSchema extends AllowedEnvKeys>(allowedEnvKeys: TSchema, values: Record<string, string>) {\n const data: Partial<LoadedEnv<TSchema>> = {}\n const error: string[] = []\n\n for (const key of Object.keys(allowedEnvKeys) as Array<keyof TSchema>) {\n const value = values[key as string]\n if (value === undefined) continue\n\n try {\n data[key] = parseEnvValue(allowedEnvKeys[key], value)\n } catch (e) {\n if (e instanceof Error)\n error.push(`${String(key)}: ${e.message}`)\n else\n error.push(`${String(key)}: Unknown error occurred`)\n }\n }\n\n return {\n success: error.length === 0,\n data: data as LoadedEnv<TSchema>,\n error\n }\n}\n\r\nexport function formatError(message: string[]): string {\r\n return message.join('\\n')\r\n}\r\n\r\nexport function parseRawEnv(rawEnv: string): Record<string, string> {\r\n const result: Record<string, string> = {}\r\n const lines = rawEnv.split(/\\r?\\n/)\r\n\r\n for (const line of lines) {\r\n const normalized = line.trim()\r\n if (!normalized || normalized.startsWith('#')) continue\r\n\r\n const separatorIndex = normalized.indexOf('=')\r\n if (separatorIndex <= 0) continue\r\n\r\n const key = normalized.slice(0, separatorIndex).trim()\r\n const value = normalized.slice(separatorIndex + 1).trim()\r\n if (!key) continue\r\n\r\n result[key] = value\r\n }\r\n\r\n return result\r\n}\r\n\r\nexport function readEnvFile(filePath: string): false | Record<string, string> {\r\n if (!existsSync(filePath)) return false\r\n return parseRawEnv(readFileSync(filePath, 'utf-8'))\r\n}\r\n\r\nexport function getUnknownEnvKeys(source: Record<string, string>, allowedEnvKeys: Set<string>): string[] {\r\n return Object.keys(source).filter((key) => !allowedEnvKeys.has(key))\r\n}\r\n"],"mappings":"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","names":["import_fs","path"]}
package/dist/index.js CHANGED
@@ -123,8 +123,9 @@ function loadEnv(allowedEnvKeys = {}, bypassUnknownEnvKeys = true, NODE_ENV) {
123
123
  } else {
124
124
  if (!process.env.NODE_ENV) {
125
125
  process.env.NODE_ENV = "production";
126
+ } else {
127
+ NODE_ENV = process.env.NODE_ENV;
126
128
  }
127
- NODE_ENV = process.env.NODE_ENV;
128
129
  }
129
130
  const defaultEnvPath = path.join(process.cwd(), ".env");
130
131
  const nodeEnvPath = path.join(process.cwd(), `.env.${NODE_ENV}`);
@@ -157,6 +158,10 @@ function loadEnv(allowedEnvKeys = {}, bypassUnknownEnvKeys = true, NODE_ENV) {
157
158
  ...process.env,
158
159
  ...parsedEnv.data
159
160
  };
161
+ parsedEnv.data = {
162
+ ...parsedEnv.data,
163
+ NODE_ENV
164
+ };
160
165
  console.log("Loaded env file", `${defaultEnvPath}, ${nodeEnvPath}`);
161
166
  return parsedEnv.data;
162
167
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/helper.ts"],"sourcesContent":["import path from 'path'\r\nimport { existsSync } from 'fs'\r\nimport { formatError, getUnknownEnvKeys, readEnvFile, safeParse } from './helper.js'\r\nimport { AllowedEnvKeys, LoadedEnv } from \"./types.js\";\r\n\r\nexport type { AllowedEnvKeys, EnvKind, EnvKindMap, LoadedEnv } from \"./types.js\"\r\n\r\ndeclare global {\r\n var liteEnv: unknown\r\n}\r\n\r\nexport function loadEnv<TSchema extends AllowedEnvKeys = {}>(\r\n allowedEnvKeys: TSchema = {} as TSchema,\r\n bypassUnknownEnvKeys: boolean = true,\r\n NODE_ENV?: string\r\n): LoadedEnv<TSchema> {\r\n if (globalThis.liteEnv)\r\n return globalThis.liteEnv as LoadedEnv<TSchema>\r\n\r\n if (NODE_ENV) {\r\n process.env.NODE_ENV = NODE_ENV\r\n } else {\r\n if (!process.env.NODE_ENV) {\r\n process.env.NODE_ENV = 'production'\r\n }\r\n\r\n NODE_ENV = process.env.NODE_ENV\r\n }\r\n\r\n const defaultEnvPath = path.join(process.cwd(), '.env')\r\n const nodeEnvPath = path.join(process.cwd(), `.env.${NODE_ENV}`)\r\n\r\n if (!existsSync(defaultEnvPath))\r\n throw new Error('Default Environment file not found')\r\n\r\n if (!existsSync(nodeEnvPath))\r\n throw new Error(`Environment file not found: .env.${NODE_ENV}`)\r\n\r\n const defaultEnvSource = readEnvFile(defaultEnvPath)\r\n const nodeEnvSource = readEnvFile(nodeEnvPath)\r\n\r\n const mergedEnv: Record<string, string> = {}\r\n\r\n if (defaultEnvSource)\r\n Object.assign(mergedEnv, defaultEnvSource)\r\n if (nodeEnvSource)\r\n Object.assign(mergedEnv, nodeEnvSource)\r\n\r\n if (!bypassUnknownEnvKeys) {\r\n const unknownKeys = [...new Set([...getUnknownEnvKeys(mergedEnv, new Set(Object.keys(allowedEnvKeys)))])]\r\n\r\n if (unknownKeys.length > 0)\r\n throw new Error(`Unknown environment variables in env files: ${unknownKeys.join(', ')}`)\r\n } else {\r\n process.env = {\r\n ...process.env,\r\n ...mergedEnv\r\n }\r\n }\r\n\r\n const parsedEnv = safeParse(allowedEnvKeys, mergedEnv)\r\n\r\n if (!parsedEnv.success)\r\n throw new Error(`Environment validation failed: ${formatError(parsedEnv.error)}`)\r\n\r\n globalThis.liteEnv = parsedEnv.data\r\n\r\n process.env = {\r\n ...process.env,\r\n ...parsedEnv.data\r\n }\r\n\r\n console.log('Loaded env file', `${defaultEnvPath}, ${nodeEnvPath}`)\r\n\r\n return parsedEnv.data as LoadedEnv<TSchema>\r\n}\r\n","import { existsSync, readFileSync } from \"fs\"\nimport { AllowedEnvKeys, EnvKind, EnvKindMap, LoadedEnv } from \"./types.js\"\n\r\nexport function toString(value: string): string {\r\n return String(value)\r\n}\r\n\r\nexport function toNumber(value: string): number {\n const parsed = Number(value)\n if (Number.isNaN(parsed))\n throw new Error('Value is not supported for number')\n\n return parsed\n}\n\r\nexport function toArray(value: string): string[] {\r\n if (!value.includes(','))\r\n throw new Error('Valur is not supported for array')\r\n\r\n return value.split(',')\r\n}\r\n\r\nexport function toBoolean(value: string): boolean {\n if (value === 'true' || value === '1')\n return true\n\n if (value === 'false' || value === '0')\n return false\n\n throw new Error('Value is not supported for boolean')\n}\n\r\nexport function toObject<T>(value: string): T {\r\n try {\r\n return JSON.parse(value)\r\n } catch (error) {\r\n throw new Error('Value is not supported for object')\r\n }\r\n}\r\n\r\nexport function toBuffer(value: string, encoding?: BufferEncoding): Buffer {\r\n try {\r\n return Buffer.from(value, encoding || 'base64')\r\n } catch (error) {\r\n throw new Error('Value is not supported for buffer')\r\n }\r\n}\r\n\r\nexport function toDate(value: string): Date {\n const parsed = new Date(value)\n if (Number.isNaN(parsed.getTime()))\n throw new Error('Value is not supported for date')\n\n return parsed\n}\n\nfunction parseEnvValue<TKind extends EnvKind>(kind: TKind, value: string): EnvKindMap[TKind] {\n switch (kind) {\n case \"string\":\n return toString(value) as EnvKindMap[TKind]\n case \"number\":\n return toNumber(value) as EnvKindMap[TKind]\n case \"boolean\":\n return toBoolean(value) as EnvKindMap[TKind]\n case \"array\":\n return toArray(value) as EnvKindMap[TKind]\n case \"object\":\n return toObject(value) as EnvKindMap[TKind]\n case \"buffer\":\n return toBuffer(value) as EnvKindMap[TKind]\n case \"date\":\n return toDate(value) as EnvKindMap[TKind]\n default: {\n const exhaustiveCheck: never = kind\n throw new Error(`Unsupported env kind: ${exhaustiveCheck}`)\n }\n }\n}\n\nexport function safeParse<TSchema extends AllowedEnvKeys>(allowedEnvKeys: TSchema, values: Record<string, string>) {\n const data: Partial<LoadedEnv<TSchema>> = {}\n const error: string[] = []\n\n for (const key of Object.keys(allowedEnvKeys) as Array<keyof TSchema>) {\n const value = values[key as string]\n if (value === undefined) continue\n\n try {\n data[key] = parseEnvValue(allowedEnvKeys[key], value)\n } catch (e) {\n if (e instanceof Error)\n error.push(`${String(key)}: ${e.message}`)\n else\n error.push(`${String(key)}: Unknown error occurred`)\n }\n }\n\n return {\n success: error.length === 0,\n data: data as LoadedEnv<TSchema>,\n error\n }\n}\n\r\nexport function formatError(message: string[]): string {\r\n return message.join('\\n')\r\n}\r\n\r\nexport function parseRawEnv(rawEnv: string): Record<string, string> {\r\n const result: Record<string, string> = {}\r\n const lines = rawEnv.split(/\\r?\\n/)\r\n\r\n for (const line of lines) {\r\n const normalized = line.trim()\r\n if (!normalized || normalized.startsWith('#')) continue\r\n\r\n const separatorIndex = normalized.indexOf('=')\r\n if (separatorIndex <= 0) continue\r\n\r\n const key = normalized.slice(0, separatorIndex).trim()\r\n const value = normalized.slice(separatorIndex + 1).trim()\r\n if (!key) continue\r\n\r\n result[key] = value\r\n }\r\n\r\n return result\r\n}\r\n\r\nexport function readEnvFile(filePath: string): false | Record<string, string> {\r\n if (!existsSync(filePath)) return false\r\n return parseRawEnv(readFileSync(filePath, 'utf-8'))\r\n}\r\n\r\nexport function getUnknownEnvKeys(source: Record<string, string>, allowedEnvKeys: Set<string>): string[] {\r\n return Object.keys(source).filter((key) => !allowedEnvKeys.has(key))\r\n}\r\n"],"mappings":"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","names":["existsSync","existsSync"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/helper.ts"],"sourcesContent":["import path from 'path'\r\nimport { existsSync } from 'fs'\r\nimport { formatError, getUnknownEnvKeys, readEnvFile, safeParse } from './helper.js'\r\nimport { AllowedEnvKeys, LoadedEnv } from \"./types.js\";\r\n\r\nexport type { AllowedEnvKeys, EnvKind, EnvKindMap, LoadedEnv } from \"./types.js\"\r\n\r\ndeclare global {\r\n var liteEnv: unknown\r\n}\r\n\r\nexport function loadEnv<TSchema extends AllowedEnvKeys = {}>(\r\n allowedEnvKeys: TSchema = {} as TSchema,\r\n bypassUnknownEnvKeys: boolean = true,\r\n NODE_ENV?: string\r\n): LoadedEnv<TSchema> {\r\n if (globalThis.liteEnv)\r\n return globalThis.liteEnv as LoadedEnv<TSchema>\r\n\r\n if (NODE_ENV) {\r\n process.env.NODE_ENV = NODE_ENV\r\n } else {\r\n if (!process.env.NODE_ENV) {\r\n process.env.NODE_ENV = 'production'\r\n } else {\r\n NODE_ENV = process.env.NODE_ENV\r\n }\r\n }\r\n\r\n const defaultEnvPath = path.join(process.cwd(), '.env')\r\n const nodeEnvPath = path.join(process.cwd(), `.env.${NODE_ENV}`)\r\n\r\n if (!existsSync(defaultEnvPath))\r\n throw new Error('Default Environment file not found')\r\n\r\n if (!existsSync(nodeEnvPath))\r\n throw new Error(`Environment file not found: .env.${NODE_ENV}`)\r\n\r\n const defaultEnvSource = readEnvFile(defaultEnvPath)\r\n const nodeEnvSource = readEnvFile(nodeEnvPath)\r\n\r\n const mergedEnv: Record<string, string> = {}\r\n\r\n if (defaultEnvSource)\r\n Object.assign(mergedEnv, defaultEnvSource)\r\n if (nodeEnvSource)\r\n Object.assign(mergedEnv, nodeEnvSource)\r\n\r\n if (!bypassUnknownEnvKeys) {\r\n const unknownKeys = [...new Set([...getUnknownEnvKeys(mergedEnv, new Set(Object.keys(allowedEnvKeys)))])]\r\n\r\n if (unknownKeys.length > 0)\r\n throw new Error(`Unknown environment variables in env files: ${unknownKeys.join(', ')}`)\r\n } else {\r\n process.env = {\r\n ...process.env,\r\n ...mergedEnv\r\n }\r\n }\r\n\r\n const parsedEnv = safeParse(allowedEnvKeys, mergedEnv)\r\n\r\n if (!parsedEnv.success)\r\n throw new Error(`Environment validation failed: ${formatError(parsedEnv.error)}`)\r\n\r\n globalThis.liteEnv = parsedEnv.data\r\n\r\n process.env = {\r\n ...process.env,\r\n ...parsedEnv.data\r\n }\r\n\r\n parsedEnv.data = {\r\n ...parsedEnv.data,\r\n NODE_ENV\r\n }\r\n\r\n console.log('Loaded env file', `${defaultEnvPath}, ${nodeEnvPath}`)\r\n\r\n return parsedEnv.data as LoadedEnv<TSchema>\r\n}\r\n","import { existsSync, readFileSync } from \"fs\"\nimport { AllowedEnvKeys, EnvKind, EnvKindMap, LoadedEnv } from \"./types.js\"\n\r\nexport function toString(value: string): string {\r\n return String(value)\r\n}\r\n\r\nexport function toNumber(value: string): number {\n const parsed = Number(value)\n if (Number.isNaN(parsed))\n throw new Error('Value is not supported for number')\n\n return parsed\n}\n\r\nexport function toArray(value: string): string[] {\r\n if (!value.includes(','))\r\n throw new Error('Valur is not supported for array')\r\n\r\n return value.split(',')\r\n}\r\n\r\nexport function toBoolean(value: string): boolean {\n if (value === 'true' || value === '1')\n return true\n\n if (value === 'false' || value === '0')\n return false\n\n throw new Error('Value is not supported for boolean')\n}\n\r\nexport function toObject<T>(value: string): T {\r\n try {\r\n return JSON.parse(value)\r\n } catch (error) {\r\n throw new Error('Value is not supported for object')\r\n }\r\n}\r\n\r\nexport function toBuffer(value: string, encoding?: BufferEncoding): Buffer {\r\n try {\r\n return Buffer.from(value, encoding || 'base64')\r\n } catch (error) {\r\n throw new Error('Value is not supported for buffer')\r\n }\r\n}\r\n\r\nexport function toDate(value: string): Date {\n const parsed = new Date(value)\n if (Number.isNaN(parsed.getTime()))\n throw new Error('Value is not supported for date')\n\n return parsed\n}\n\nfunction parseEnvValue<TKind extends EnvKind>(kind: TKind, value: string): EnvKindMap[TKind] {\n switch (kind) {\n case \"string\":\n return toString(value) as EnvKindMap[TKind]\n case \"number\":\n return toNumber(value) as EnvKindMap[TKind]\n case \"boolean\":\n return toBoolean(value) as EnvKindMap[TKind]\n case \"array\":\n return toArray(value) as EnvKindMap[TKind]\n case \"object\":\n return toObject(value) as EnvKindMap[TKind]\n case \"buffer\":\n return toBuffer(value) as EnvKindMap[TKind]\n case \"date\":\n return toDate(value) as EnvKindMap[TKind]\n default: {\n const exhaustiveCheck: never = kind\n throw new Error(`Unsupported env kind: ${exhaustiveCheck}`)\n }\n }\n}\n\nexport function safeParse<TSchema extends AllowedEnvKeys>(allowedEnvKeys: TSchema, values: Record<string, string>) {\n const data: Partial<LoadedEnv<TSchema>> = {}\n const error: string[] = []\n\n for (const key of Object.keys(allowedEnvKeys) as Array<keyof TSchema>) {\n const value = values[key as string]\n if (value === undefined) continue\n\n try {\n data[key] = parseEnvValue(allowedEnvKeys[key], value)\n } catch (e) {\n if (e instanceof Error)\n error.push(`${String(key)}: ${e.message}`)\n else\n error.push(`${String(key)}: Unknown error occurred`)\n }\n }\n\n return {\n success: error.length === 0,\n data: data as LoadedEnv<TSchema>,\n error\n }\n}\n\r\nexport function formatError(message: string[]): string {\r\n return message.join('\\n')\r\n}\r\n\r\nexport function parseRawEnv(rawEnv: string): Record<string, string> {\r\n const result: Record<string, string> = {}\r\n const lines = rawEnv.split(/\\r?\\n/)\r\n\r\n for (const line of lines) {\r\n const normalized = line.trim()\r\n if (!normalized || normalized.startsWith('#')) continue\r\n\r\n const separatorIndex = normalized.indexOf('=')\r\n if (separatorIndex <= 0) continue\r\n\r\n const key = normalized.slice(0, separatorIndex).trim()\r\n const value = normalized.slice(separatorIndex + 1).trim()\r\n if (!key) continue\r\n\r\n result[key] = value\r\n }\r\n\r\n return result\r\n}\r\n\r\nexport function readEnvFile(filePath: string): false | Record<string, string> {\r\n if (!existsSync(filePath)) return false\r\n return parseRawEnv(readFileSync(filePath, 'utf-8'))\r\n}\r\n\r\nexport function getUnknownEnvKeys(source: Record<string, string>, allowedEnvKeys: Set<string>): string[] {\r\n return Object.keys(source).filter((key) => !allowedEnvKeys.has(key))\r\n}\r\n"],"mappings":"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","names":["existsSync","existsSync"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bintvn/lite-env",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "TypeScript based environment variable loader.",
5
5
  "keywords": [
6
6
  "util",