@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 +2 -2
- package/dist/env.d.mts.map +1 -1
- package/dist/env.mjs +22 -3
- package/dist/env.mjs.map +1 -1
- package/package.json +1 -1
- package/src/env.test.ts +41 -2
- package/src/env.ts +28 -6
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
|
package/dist/env.d.mts.map
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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\
|
|
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
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
|
-
|
|
26
|
+
let parsed: T | undefined
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
if (
|
|
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
|
|
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
|
}
|