@aamini/lib 0.0.10 → 0.0.11

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/env.d.mts CHANGED
@@ -1,8 +1,8 @@
1
1
  //#region src/env.d.ts
2
- type EnvSchema<T> = {
2
+ type EnvSchema<T extends object> = {
3
3
  parse(input: unknown): T;
4
4
  };
5
- declare function createEnv<T>(schema: EnvSchema<T>): T;
5
+ declare function createEnv<T extends object>(schema: EnvSchema<T>): T;
6
6
  //#endregion
7
7
  export { createEnv };
8
8
  //# sourceMappingURL=env.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"env.d.mts","names":[],"sources":["../src/env.ts"],"mappings":";KAEK,SAAA;EACJ,KAAA,CAAM,KAAA,YAAiB,CAAA;AAAA;AAAA,iBAGR,SAAA,GAAA,CAAa,MAAA,EAAQ,SAAA,CAAU,CAAA,IAAK,CAAA"}
1
+ {"version":3,"file":"env.d.mts","names":[],"sources":["../src/env.ts"],"mappings":";KAEK,SAAA;EACJ,KAAA,CAAM,KAAA,YAAiB,CAAA;AAAA;AAAA,iBAGR,SAAA,kBAAA,CAA4B,MAAA,EAAQ,SAAA,CAAU,CAAA,IAAK,CAAA"}
package/dist/env.mjs CHANGED
@@ -15,9 +15,28 @@ function createEnv(schema) {
15
15
  override: true,
16
16
  processEnv: fileEnvironment
17
17
  });
18
- const env = { ...fileEnvironment };
19
- for (const [key, value] of Object.entries(process.env)) if (value !== void 0) env[key] = value;
20
- return schema.parse(env);
18
+ let parsed;
19
+ function parseEnv() {
20
+ if (parsed !== void 0) return parsed;
21
+ const env = { ...fileEnvironment };
22
+ for (const [key, value] of Object.entries(process.env)) if (value !== void 0) env[key] = value;
23
+ parsed = schema.parse(env);
24
+ return parsed;
25
+ }
26
+ return new Proxy({}, {
27
+ get(_target, property, receiver) {
28
+ return Reflect.get(parseEnv(), property, receiver);
29
+ },
30
+ has(_target, property) {
31
+ return property in parseEnv();
32
+ },
33
+ ownKeys() {
34
+ return Reflect.ownKeys(parseEnv());
35
+ },
36
+ getOwnPropertyDescriptor(_target, property) {
37
+ return Object.getOwnPropertyDescriptor(parseEnv(), property);
38
+ }
39
+ });
21
40
  }
22
41
  //#endregion
23
42
  export { createEnv };
package/dist/env.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"env.mjs","names":[],"sources":["../src/env.ts"],"sourcesContent":["import { config } from 'dotenv'\n\ntype EnvSchema<T> = {\n\tparse(input: unknown): T\n}\n\nexport function createEnv<T>(schema: EnvSchema<T>): T {\n\tconst raw =\n\t\tprocess.env.RAILWAY_ENVIRONMENT_NAME ??\n\t\t(process.env.NODE_ENV === 'production' ? 'production' : 'development')\n\tconst environmentName = /(?:^|-)pr-\\d+$/.test(raw) ? 'staging' : raw\n\tconst fileEnvironment: NodeJS.ProcessEnv = {}\n\n\tconfig({\n\t\tpath: [\n\t\t\t'.env',\n\t\t\t'.env.local',\n\t\t\t`.env.${environmentName}`,\n\t\t\t`.env.${environmentName}.local`,\n\t\t],\n\t\tquiet: true,\n\t\toverride: true,\n\t\tprocessEnv: fileEnvironment,\n\t})\n\n\tconst env: NodeJS.ProcessEnv = { ...fileEnvironment }\n\n\tfor (const [key, value] of Object.entries(process.env)) {\n\t\tif (value !== undefined) env[key] = value\n\t}\n\n\treturn schema.parse(env)\n}\n"],"mappings":";;AAMA,SAAgB,UAAa,QAAyB;CACrD,MAAM,MACL,QAAQ,IAAI,6BACX,QAAQ,IAAI,aAAa,eAAe,eAAe;CACzD,MAAM,kBAAkB,iBAAiB,KAAK,IAAI,GAAG,YAAY;CACjE,MAAM,kBAAqC,EAAE;CAE7C,OAAO;EACN,MAAM;GACL;GACA;GACA,QAAQ;GACR,QAAQ,gBAAgB;GACxB;EACD,OAAO;EACP,UAAU;EACV,YAAY;EACZ,CAAC;CAEF,MAAM,MAAyB,EAAE,GAAG,iBAAiB;CAErD,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,IAAI,EACrD,IAAI,UAAU,KAAA,GAAW,IAAI,OAAO;CAGrC,OAAO,OAAO,MAAM,IAAI"}
1
+ {"version":3,"file":"env.mjs","names":[],"sources":["../src/env.ts"],"sourcesContent":["import { config } from 'dotenv'\n\ntype EnvSchema<T extends object> = {\n\tparse(input: unknown): T\n}\n\nexport function createEnv<T extends object>(schema: EnvSchema<T>): T {\n\tconst raw =\n\t\tprocess.env.RAILWAY_ENVIRONMENT_NAME ??\n\t\t(process.env.NODE_ENV === 'production' ? 'production' : 'development')\n\tconst environmentName = /(?:^|-)pr-\\d+$/.test(raw) ? 'staging' : raw\n\tconst fileEnvironment: NodeJS.ProcessEnv = {}\n\n\tconfig({\n\t\tpath: [\n\t\t\t'.env',\n\t\t\t'.env.local',\n\t\t\t`.env.${environmentName}`,\n\t\t\t`.env.${environmentName}.local`,\n\t\t],\n\t\tquiet: true,\n\t\toverride: true,\n\t\tprocessEnv: fileEnvironment,\n\t})\n\n\tlet parsed: T | undefined\n\n\tfunction parseEnv() {\n\t\tif (parsed !== undefined) return parsed\n\n\t\tconst env: NodeJS.ProcessEnv = { ...fileEnvironment }\n\n\t\tfor (const [key, value] of Object.entries(process.env)) {\n\t\t\tif (value !== undefined) env[key] = value\n\t\t}\n\n\t\tparsed = schema.parse(env)\n\t\treturn parsed\n\t}\n\n\treturn new Proxy({} as Record<PropertyKey, unknown>, {\n\t\tget(_target, property, receiver) {\n\t\t\treturn Reflect.get(parseEnv() as object, property, receiver)\n\t\t},\n\t\thas(_target, property) {\n\t\t\treturn property in (parseEnv() as object)\n\t\t},\n\t\townKeys() {\n\t\t\treturn Reflect.ownKeys(parseEnv() as object)\n\t\t},\n\t\tgetOwnPropertyDescriptor(_target, property) {\n\t\t\treturn Object.getOwnPropertyDescriptor(parseEnv() as object, property)\n\t\t},\n\t}) as T\n}\n"],"mappings":";;AAMA,SAAgB,UAA4B,QAAyB;CACpE,MAAM,MACL,QAAQ,IAAI,6BACX,QAAQ,IAAI,aAAa,eAAe,eAAe;CACzD,MAAM,kBAAkB,iBAAiB,KAAK,IAAI,GAAG,YAAY;CACjE,MAAM,kBAAqC,EAAE;CAE7C,OAAO;EACN,MAAM;GACL;GACA;GACA,QAAQ;GACR,QAAQ,gBAAgB;GACxB;EACD,OAAO;EACP,UAAU;EACV,YAAY;EACZ,CAAC;CAEF,IAAI;CAEJ,SAAS,WAAW;EACnB,IAAI,WAAW,KAAA,GAAW,OAAO;EAEjC,MAAM,MAAyB,EAAE,GAAG,iBAAiB;EAErD,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,IAAI,EACrD,IAAI,UAAU,KAAA,GAAW,IAAI,OAAO;EAGrC,SAAS,OAAO,MAAM,IAAI;EAC1B,OAAO;;CAGR,OAAO,IAAI,MAAM,EAAE,EAAkC;EACpD,IAAI,SAAS,UAAU,UAAU;GAChC,OAAO,QAAQ,IAAI,UAAU,EAAY,UAAU,SAAS;;EAE7D,IAAI,SAAS,UAAU;GACtB,OAAO,YAAa,UAAU;;EAE/B,UAAU;GACT,OAAO,QAAQ,QAAQ,UAAU,CAAW;;EAE7C,yBAAyB,SAAS,UAAU;GAC3C,OAAO,OAAO,yBAAyB,UAAU,EAAY,SAAS;;EAEvE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aamini/lib",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "repository": {
package/src/env.test.ts CHANGED
@@ -14,9 +14,7 @@ describe.sequential('createEnv', () => {
14
14
  beforeEach(() => {
15
15
  tempDir = mkdtempSync(join(tmpdir(), 'aamini-lib-env-'))
16
16
  process.chdir(tempDir)
17
- writeFileSync(join(tempDir, '.env'), 'DATABASE_URL=postgres://from-file\n')
18
17
  process.env.RAILWAY_ENVIRONMENT_NAME = 'production'
19
- process.env.DATABASE_URL = 'postgres://from-railway'
20
18
  })
21
19
 
22
20
  afterEach(() => {
@@ -30,6 +28,9 @@ describe.sequential('createEnv', () => {
30
28
  })
31
29
 
32
30
  it('keeps injected env values ahead of dotenv files', () => {
31
+ writeFileSync(join(tempDir, '.env'), 'DATABASE_URL=postgres://from-file\n')
32
+ process.env.DATABASE_URL = 'postgres://from-railway'
33
+
33
34
  const env = createEnv(
34
35
  z.object({
35
36
  DATABASE_URL: z.string(),
@@ -40,4 +41,42 @@ describe.sequential('createEnv', () => {
40
41
  expect(process.env.DATABASE_URL).toBe('postgres://from-railway')
41
42
  expect(readFileSync(join(tempDir, '.env'), 'utf8')).toContain('from-file')
42
43
  })
44
+
45
+ it('loads dotenv files from lowest to highest precedence', () => {
46
+ delete process.env.DATABASE_URL
47
+ writeFileSync(join(tempDir, '.env'), 'DATABASE_URL=postgres://from-env\n')
48
+ writeFileSync(
49
+ join(tempDir, '.env.local'),
50
+ 'DATABASE_URL=postgres://from-local\n',
51
+ )
52
+ writeFileSync(
53
+ join(tempDir, '.env.production'),
54
+ 'DATABASE_URL=postgres://from-production\n',
55
+ )
56
+ writeFileSync(
57
+ join(tempDir, '.env.production.local'),
58
+ 'DATABASE_URL=postgres://from-production-local\n',
59
+ )
60
+
61
+ const env = createEnv(
62
+ z.object({
63
+ DATABASE_URL: z.string(),
64
+ }),
65
+ )
66
+
67
+ expect(env.DATABASE_URL).toBe('postgres://from-production-local')
68
+ })
69
+
70
+ it('does not validate until an env value is read', () => {
71
+ delete process.env.DATABASE_URL
72
+
73
+ const env = createEnv(
74
+ z.object({
75
+ DATABASE_URL: z.string(),
76
+ }),
77
+ )
78
+
79
+ expect(() => env).not.toThrow()
80
+ expect(() => env.DATABASE_URL).toThrow(/expected string/)
81
+ })
43
82
  })
package/src/env.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { config } from 'dotenv'
2
2
 
3
- type EnvSchema<T> = {
3
+ type EnvSchema<T extends object> = {
4
4
  parse(input: unknown): T
5
5
  }
6
6
 
7
- export function createEnv<T>(schema: EnvSchema<T>): T {
7
+ export function createEnv<T extends object>(schema: EnvSchema<T>): T {
8
8
  const raw =
9
9
  process.env.RAILWAY_ENVIRONMENT_NAME ??
10
10
  (process.env.NODE_ENV === 'production' ? 'production' : 'development')
@@ -23,11 +23,33 @@ export function createEnv<T>(schema: EnvSchema<T>): T {
23
23
  processEnv: fileEnvironment,
24
24
  })
25
25
 
26
- const env: NodeJS.ProcessEnv = { ...fileEnvironment }
26
+ let parsed: T | undefined
27
27
 
28
- for (const [key, value] of Object.entries(process.env)) {
29
- if (value !== undefined) env[key] = value
28
+ function parseEnv() {
29
+ if (parsed !== undefined) return parsed
30
+
31
+ const env: NodeJS.ProcessEnv = { ...fileEnvironment }
32
+
33
+ for (const [key, value] of Object.entries(process.env)) {
34
+ if (value !== undefined) env[key] = value
35
+ }
36
+
37
+ parsed = schema.parse(env)
38
+ return parsed
30
39
  }
31
40
 
32
- return schema.parse(env)
41
+ return new Proxy({} as Record<PropertyKey, unknown>, {
42
+ get(_target, property, receiver) {
43
+ return Reflect.get(parseEnv() as object, property, receiver)
44
+ },
45
+ has(_target, property) {
46
+ return property in (parseEnv() as object)
47
+ },
48
+ ownKeys() {
49
+ return Reflect.ownKeys(parseEnv() as object)
50
+ },
51
+ getOwnPropertyDescriptor(_target, property) {
52
+ return Object.getOwnPropertyDescriptor(parseEnv() as object, property)
53
+ },
54
+ }) as T
33
55
  }