@geekmidas/envkit 0.2.0 → 0.3.0

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.
Files changed (73) hide show
  1. package/dist/{EnvironmentBuilder-DfmYRBm-.mjs → EnvironmentBuilder-BSuHZm0y.mjs} +2 -4
  2. package/dist/EnvironmentBuilder-BSuHZm0y.mjs.map +1 -0
  3. package/dist/EnvironmentBuilder-DHfDXJUm.d.mts.map +1 -0
  4. package/dist/{EnvironmentBuilder-W2wku49g.cjs → EnvironmentBuilder-Djr1VsWM.cjs} +2 -4
  5. package/dist/EnvironmentBuilder-Djr1VsWM.cjs.map +1 -0
  6. package/dist/EnvironmentBuilder-Xuf2Dd9u.d.cts.map +1 -0
  7. package/dist/EnvironmentBuilder.cjs +1 -1
  8. package/dist/EnvironmentBuilder.mjs +1 -1
  9. package/dist/EnvironmentParser-Bt246UeP.cjs.map +1 -1
  10. package/dist/{EnvironmentParser-CVWU1ooT.d.mts → EnvironmentParser-CY8TosTN.d.mts} +2 -1
  11. package/dist/EnvironmentParser-CY8TosTN.d.mts.map +1 -0
  12. package/dist/{EnvironmentParser-tV-JjCg7.d.cts → EnvironmentParser-DtOL86NU.d.cts} +2 -1
  13. package/dist/EnvironmentParser-DtOL86NU.d.cts.map +1 -0
  14. package/dist/EnvironmentParser-c06agx31.mjs.map +1 -1
  15. package/dist/EnvironmentParser.d.cts +1 -1
  16. package/dist/EnvironmentParser.d.mts +1 -1
  17. package/dist/SnifferEnvironmentParser.cjs.map +1 -1
  18. package/dist/SnifferEnvironmentParser.d.cts +3 -2
  19. package/dist/SnifferEnvironmentParser.d.cts.map +1 -0
  20. package/dist/SnifferEnvironmentParser.d.mts +3 -2
  21. package/dist/SnifferEnvironmentParser.d.mts.map +1 -0
  22. package/dist/SnifferEnvironmentParser.mjs.map +1 -1
  23. package/dist/{SstEnvironmentBuilder-DEa3lTUB.mjs → SstEnvironmentBuilder-BEBFSUYr.mjs} +2 -2
  24. package/dist/SstEnvironmentBuilder-BEBFSUYr.mjs.map +1 -0
  25. package/dist/SstEnvironmentBuilder-CjURMGjW.d.mts.map +1 -0
  26. package/dist/SstEnvironmentBuilder-D4oSo_KX.d.cts.map +1 -0
  27. package/dist/{SstEnvironmentBuilder-BuFw1hCe.cjs → SstEnvironmentBuilder-wFnN2M5O.cjs} +2 -2
  28. package/dist/SstEnvironmentBuilder-wFnN2M5O.cjs.map +1 -0
  29. package/dist/SstEnvironmentBuilder.cjs +2 -2
  30. package/dist/SstEnvironmentBuilder.mjs +2 -2
  31. package/dist/credentials.cjs +66 -0
  32. package/dist/credentials.cjs.map +1 -0
  33. package/dist/credentials.d.cts +31 -0
  34. package/dist/credentials.d.cts.map +1 -0
  35. package/dist/credentials.d.mts +31 -0
  36. package/dist/credentials.d.mts.map +1 -0
  37. package/dist/credentials.mjs +62 -0
  38. package/dist/credentials.mjs.map +1 -0
  39. package/dist/index.cjs +1 -1
  40. package/dist/index.d.cts +1 -1
  41. package/dist/index.d.mts +1 -1
  42. package/dist/index.mjs +1 -1
  43. package/dist/sst.cjs +2 -2
  44. package/dist/sst.cjs.map +1 -1
  45. package/dist/sst.d.cts +1 -0
  46. package/dist/sst.d.cts.map +1 -0
  47. package/dist/sst.d.mts +1 -0
  48. package/dist/sst.d.mts.map +1 -0
  49. package/dist/sst.mjs +2 -2
  50. package/dist/sst.mjs.map +1 -1
  51. package/examples/basic-usage.ts +329 -333
  52. package/package.json +6 -1
  53. package/src/EnvironmentBuilder.ts +76 -80
  54. package/src/EnvironmentParser.ts +231 -231
  55. package/src/SnifferEnvironmentParser.ts +178 -178
  56. package/src/SstEnvironmentBuilder.ts +127 -127
  57. package/src/__tests__/ConfigParser.spec.ts +388 -388
  58. package/src/__tests__/EnvironmentBuilder.spec.ts +245 -265
  59. package/src/__tests__/EnvironmentParser.spec.ts +828 -828
  60. package/src/__tests__/SnifferEnvironmentParser.spec.ts +380 -326
  61. package/src/__tests__/SstEnvironmentBuilder.spec.ts +347 -367
  62. package/src/__tests__/credentials.integration.spec.ts +239 -0
  63. package/src/__tests__/credentials.spec.ts +136 -0
  64. package/src/__tests__/sst.spec.ts +390 -413
  65. package/src/credentials.ts +99 -0
  66. package/src/index.ts +11 -11
  67. package/src/sst.ts +24 -24
  68. package/sst-env.d.ts +0 -1
  69. package/tsconfig.json +9 -0
  70. package/dist/EnvironmentBuilder-DfmYRBm-.mjs.map +0 -1
  71. package/dist/EnvironmentBuilder-W2wku49g.cjs.map +0 -1
  72. package/dist/SstEnvironmentBuilder-BuFw1hCe.cjs.map +0 -1
  73. package/dist/SstEnvironmentBuilder-DEa3lTUB.mjs.map +0 -1
@@ -3,272 +3,252 @@ import { describe, expect, it, vi } from 'vitest';
3
3
  import { EnvironmentBuilder, environmentCase } from '../EnvironmentBuilder';
4
4
 
5
5
  describe('environmentCase', () => {
6
- it('should convert camelCase to UPPER_SNAKE_CASE', () => {
7
- expect(environmentCase('myVariable')).toBe('MY_VARIABLE');
8
- expect(environmentCase('apiUrl')).toBe('API_URL');
9
- expect(environmentCase('databaseName')).toBe('DATABASE_NAME');
10
- });
11
-
12
- it('should handle already snake_case', () => {
13
- expect(environmentCase('my_variable')).toBe('MY_VARIABLE');
14
- expect(environmentCase('api_url')).toBe('API_URL');
15
- });
16
-
17
- it('should remove underscore directly before numbers', () => {
18
- // The regex /_\d+/g only removes underscores that are directly followed by digits
19
- expect(environmentCase('api_v2')).toBe('API_V2');
20
- expect(environmentCase('value_123')).toBe('VALUE123');
21
- expect(environmentCase('my_var_2')).toBe('MY_VAR2');
22
- });
23
-
24
- it('should handle single words', () => {
25
- expect(environmentCase('name')).toBe('NAME');
26
- expect(environmentCase('port')).toBe('PORT');
27
- });
6
+ it('should convert camelCase to UPPER_SNAKE_CASE', () => {
7
+ expect(environmentCase('myVariable')).toBe('MY_VARIABLE');
8
+ expect(environmentCase('apiUrl')).toBe('API_URL');
9
+ expect(environmentCase('databaseName')).toBe('DATABASE_NAME');
10
+ });
11
+
12
+ it('should handle already snake_case', () => {
13
+ expect(environmentCase('my_variable')).toBe('MY_VARIABLE');
14
+ expect(environmentCase('api_url')).toBe('API_URL');
15
+ });
16
+
17
+ it('should remove underscore directly before numbers', () => {
18
+ // The regex /_\d+/g only removes underscores that are directly followed by digits
19
+ expect(environmentCase('api_v2')).toBe('API_V2');
20
+ expect(environmentCase('value_123')).toBe('VALUE123');
21
+ expect(environmentCase('my_var_2')).toBe('MY_VAR2');
22
+ });
23
+
24
+ it('should handle single words', () => {
25
+ expect(environmentCase('name')).toBe('NAME');
26
+ expect(environmentCase('port')).toBe('PORT');
27
+ });
28
28
  });
29
29
 
30
30
  describe('EnvironmentBuilder', () => {
31
- describe('basic functionality', () => {
32
- it('should pass through plain string values with key transformation', () => {
33
- const env = new EnvironmentBuilder(
34
- {
35
- appName: 'my-app',
36
- nodeEnv: 'production',
37
- },
38
- {},
39
- ).build();
40
-
41
- expect(env).toEqual({
42
- APP_NAME: 'my-app',
43
- NODE_ENV: 'production',
44
- });
45
- });
46
-
47
- it('should resolve object values using type-based resolvers', () => {
48
- const env = new EnvironmentBuilder(
49
- {
50
- apiKey: { type: 'secret', value: 'xyz' },
51
- },
52
- {
53
- secret: (key, value: { type: string; value: string }) => ({
54
- [key]: value.value,
55
- }),
56
- },
57
- ).build();
58
-
59
- expect(env).toEqual({
60
- API_KEY: 'xyz',
61
- });
62
- });
63
-
64
- it('should transform resolver output keys to UPPER_SNAKE_CASE', () => {
65
- const env = new EnvironmentBuilder(
66
- {
67
- auth: { type: 'auth0', domain: 'example.auth0.com', clientId: 'abc' },
68
- },
69
- {
70
- auth0: (
71
- key,
72
- value: { type: string; domain: string; clientId: string },
73
- ) => ({
74
- [`${key}Domain`]: value.domain,
75
- [`${key}ClientId`]: value.clientId,
76
- }),
77
- },
78
- ).build();
79
-
80
- expect(env).toEqual({
81
- AUTH_DOMAIN: 'example.auth0.com',
82
- AUTH_CLIENT_ID: 'abc',
83
- });
84
- });
85
-
86
- it('should handle mixed string and object values', () => {
87
- const env = new EnvironmentBuilder(
88
- {
89
- appName: 'my-app',
90
- apiKey: { type: 'secret', value: 'secret-key' },
91
- nodeEnv: 'production',
92
- },
93
- {
94
- secret: (key, value: { type: string; value: string }) => ({
95
- [key]: value.value,
96
- }),
97
- },
98
- ).build();
99
-
100
- expect(env).toEqual({
101
- APP_NAME: 'my-app',
102
- API_KEY: 'secret-key',
103
- NODE_ENV: 'production',
104
- });
105
- });
106
- });
107
-
108
- describe('nested values', () => {
109
- it('should support nested object values', () => {
110
- const env = new EnvironmentBuilder(
111
- {
112
- database: {
113
- type: 'multi-db',
114
- primary: 'pg://primary',
115
- replica: 'pg://replica',
116
- },
117
- },
118
- {
119
- 'multi-db': (
120
- key,
121
- value: { type: string; primary: string; replica: string },
122
- ) => ({
123
- [key]: {
124
- primary: value.primary,
125
- replica: value.replica,
126
- },
127
- }),
128
- },
129
- ).build();
130
-
131
- expect(env).toEqual({
132
- DATABASE: {
133
- primary: 'pg://primary',
134
- replica: 'pg://replica',
135
- },
136
- });
137
- });
138
-
139
- it('should not transform nested object keys', () => {
140
- const env = new EnvironmentBuilder(
141
- {
142
- config: { type: 'nested', camelCase: 'value', snake_case: 'value2' },
143
- },
144
- {
145
- nested: (
146
- key,
147
- value: { type: string; camelCase: string; snake_case: string },
148
- ) => ({
149
- [key]: {
150
- camelCase: value.camelCase,
151
- snake_case: value.snake_case,
152
- },
153
- }),
154
- },
155
- ).build();
156
-
157
- expect(env).toEqual({
158
- CONFIG: {
159
- camelCase: 'value',
160
- snake_case: 'value2',
161
- },
162
- });
163
- });
164
- });
165
-
166
- describe('unmatched values', () => {
167
- it('should call onUnmatchedValue for objects without matching resolver', () => {
168
- const onUnmatchedValue = vi.fn();
169
-
170
- new EnvironmentBuilder(
171
- {
172
- unknown: { type: 'unknown-type', data: 'test' },
173
- },
174
- {},
175
- { onUnmatchedValue },
176
- ).build();
177
-
178
- expect(onUnmatchedValue).toHaveBeenCalledWith('unknown', {
179
- type: 'unknown-type',
180
- data: 'test',
181
- });
182
- });
183
-
184
- it('should default to console.warn for unmatched values', () => {
185
- const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
186
-
187
- new EnvironmentBuilder(
188
- {
189
- unknown: { type: 'unknown-type', data: 'test' },
190
- },
191
- {},
192
- ).build();
193
-
194
- expect(warnSpy).toHaveBeenCalledWith(
195
- 'No resolver found for key "unknown":',
196
- {
197
- value: { type: 'unknown-type', data: 'test' },
198
- },
199
- );
200
-
201
- warnSpy.mockRestore();
202
- });
203
- });
204
-
205
- describe('value types', () => {
206
- it('should support number values', () => {
207
- const env = new EnvironmentBuilder(
208
- {
209
- port: { type: 'port', value: 3000 },
210
- },
211
- {
212
- port: (key, value: { type: string; value: number }) => ({
213
- [key]: value.value,
214
- }),
215
- },
216
- ).build();
217
-
218
- expect(env).toEqual({
219
- PORT: 3000,
220
- });
221
- });
222
-
223
- it('should support boolean values', () => {
224
- const env = new EnvironmentBuilder(
225
- {
226
- enabled: { type: 'flag', value: true },
227
- },
228
- {
229
- flag: (key, value: { type: string; value: boolean }) => ({
230
- [key]: value.value,
231
- }),
232
- },
233
- ).build();
234
-
235
- expect(env).toEqual({
236
- ENABLED: true,
237
- });
238
- });
239
- });
240
-
241
- describe('multiple resolvers', () => {
242
- it('should use the correct resolver for each type', () => {
243
- const env = new EnvironmentBuilder(
244
- {
245
- secret: { type: 'secret', value: 'my-secret' },
246
- database: { type: 'postgres', host: 'localhost', port: 5432 },
247
- bucket: { type: 'bucket', name: 'my-bucket' },
248
- },
249
- {
250
- secret: (key, value: { type: string; value: string }) => ({
251
- [key]: value.value,
252
- }),
253
- postgres: (
254
- key,
255
- value: { type: string; host: string; port: number },
256
- ) => ({
257
- [`${key}Host`]: value.host,
258
- [`${key}Port`]: value.port,
259
- }),
260
- bucket: (key, value: { type: string; name: string }) => ({
261
- [`${key}Name`]: value.name,
262
- }),
263
- },
264
- ).build();
265
-
266
- expect(env).toEqual({
267
- SECRET: 'my-secret',
268
- DATABASE_HOST: 'localhost',
269
- DATABASE_PORT: 5432,
270
- BUCKET_NAME: 'my-bucket',
271
- });
272
- });
273
- });
31
+ describe('basic functionality', () => {
32
+ it('should pass through plain string values with key transformation', () => {
33
+ const env = new EnvironmentBuilder(
34
+ {
35
+ appName: 'my-app',
36
+ nodeEnv: 'production',
37
+ },
38
+ {},
39
+ ).build();
40
+
41
+ expect(env).toEqual({
42
+ APP_NAME: 'my-app',
43
+ NODE_ENV: 'production',
44
+ });
45
+ });
46
+
47
+ it('should resolve object values using type-based resolvers', () => {
48
+ const env = new EnvironmentBuilder(
49
+ {
50
+ apiKey: { type: 'secret', value: 'xyz' },
51
+ },
52
+ {
53
+ secret: (key, value: { type: string; value: string }) => ({
54
+ [key]: value.value,
55
+ }),
56
+ },
57
+ ).build();
58
+
59
+ expect(env).toEqual({
60
+ API_KEY: 'xyz',
61
+ });
62
+ });
63
+
64
+ it('should transform resolver output keys to UPPER_SNAKE_CASE', () => {
65
+ const env = new EnvironmentBuilder(
66
+ {
67
+ auth: { type: 'auth0', domain: 'example.auth0.com', clientId: 'abc' },
68
+ },
69
+ {
70
+ auth0: (
71
+ key,
72
+ value: { type: string; domain: string; clientId: string },
73
+ ) => ({
74
+ [`${key}Domain`]: value.domain,
75
+ [`${key}ClientId`]: value.clientId,
76
+ }),
77
+ },
78
+ ).build();
79
+
80
+ expect(env).toEqual({
81
+ AUTH_DOMAIN: 'example.auth0.com',
82
+ AUTH_CLIENT_ID: 'abc',
83
+ });
84
+ });
85
+
86
+ it('should handle mixed string and object values', () => {
87
+ const env = new EnvironmentBuilder(
88
+ {
89
+ appName: 'my-app',
90
+ apiKey: { type: 'secret', value: 'secret-key' },
91
+ nodeEnv: 'production',
92
+ },
93
+ {
94
+ secret: (key, value: { type: string; value: string }) => ({
95
+ [key]: value.value,
96
+ }),
97
+ },
98
+ ).build();
99
+
100
+ expect(env).toEqual({
101
+ APP_NAME: 'my-app',
102
+ API_KEY: 'secret-key',
103
+ NODE_ENV: 'production',
104
+ });
105
+ });
106
+ });
107
+
108
+ describe('nested values', () => {
109
+ it('should support nested object values', () => {
110
+ const env = new EnvironmentBuilder(
111
+ {
112
+ database: {
113
+ type: 'multi-db',
114
+ primary: 'pg://primary',
115
+ replica: 'pg://replica',
116
+ },
117
+ },
118
+ {
119
+ 'multi-db': (
120
+ key,
121
+ value: { type: string; primary: string; replica: string },
122
+ ) => ({
123
+ [key]: {
124
+ primary: value.primary,
125
+ replica: value.replica,
126
+ },
127
+ }),
128
+ },
129
+ ).build();
130
+
131
+ expect(env).toEqual({
132
+ DATABASE: {
133
+ primary: 'pg://primary',
134
+ replica: 'pg://replica',
135
+ },
136
+ });
137
+ });
138
+
139
+ it('should not transform nested object keys', () => {
140
+ const env = new EnvironmentBuilder(
141
+ {
142
+ config: { type: 'nested', camelCase: 'value', snake_case: 'value2' },
143
+ },
144
+ {
145
+ nested: (
146
+ key,
147
+ value: { type: string; camelCase: string; snake_case: string },
148
+ ) => ({
149
+ [key]: {
150
+ camelCase: value.camelCase,
151
+ snake_case: value.snake_case,
152
+ },
153
+ }),
154
+ },
155
+ ).build();
156
+
157
+ expect(env).toEqual({
158
+ CONFIG: {
159
+ camelCase: 'value',
160
+ snake_case: 'value2',
161
+ },
162
+ });
163
+ });
164
+ });
165
+
166
+ describe('unmatched values', () => {
167
+ it('should call onUnmatchedValue for objects without matching resolver', () => {
168
+ const onUnmatchedValue = vi.fn();
169
+
170
+ new EnvironmentBuilder(
171
+ {
172
+ unknown: { type: 'unknown-type', data: 'test' },
173
+ },
174
+ {},
175
+ { onUnmatchedValue },
176
+ ).build();
177
+
178
+ expect(onUnmatchedValue).toHaveBeenCalledWith('unknown', {
179
+ type: 'unknown-type',
180
+ data: 'test',
181
+ });
182
+ });
183
+ });
184
+
185
+ describe('value types', () => {
186
+ it('should support number values', () => {
187
+ const env = new EnvironmentBuilder(
188
+ {
189
+ port: { type: 'port', value: 3000 },
190
+ },
191
+ {
192
+ port: (key, value: { type: string; value: number }) => ({
193
+ [key]: value.value,
194
+ }),
195
+ },
196
+ ).build();
197
+
198
+ expect(env).toEqual({
199
+ PORT: 3000,
200
+ });
201
+ });
202
+
203
+ it('should support boolean values', () => {
204
+ const env = new EnvironmentBuilder(
205
+ {
206
+ enabled: { type: 'flag', value: true },
207
+ },
208
+ {
209
+ flag: (key, value: { type: string; value: boolean }) => ({
210
+ [key]: value.value,
211
+ }),
212
+ },
213
+ ).build();
214
+
215
+ expect(env).toEqual({
216
+ ENABLED: true,
217
+ });
218
+ });
219
+ });
220
+
221
+ describe('multiple resolvers', () => {
222
+ it('should use the correct resolver for each type', () => {
223
+ const env = new EnvironmentBuilder(
224
+ {
225
+ secret: { type: 'secret', value: 'my-secret' },
226
+ database: { type: 'postgres', host: 'localhost', port: 5432 },
227
+ bucket: { type: 'bucket', name: 'my-bucket' },
228
+ },
229
+ {
230
+ secret: (key, value: { type: string; value: string }) => ({
231
+ [key]: value.value,
232
+ }),
233
+ postgres: (
234
+ key,
235
+ value: { type: string; host: string; port: number },
236
+ ) => ({
237
+ [`${key}Host`]: value.host,
238
+ [`${key}Port`]: value.port,
239
+ }),
240
+ bucket: (key, value: { type: string; name: string }) => ({
241
+ [`${key}Name`]: value.name,
242
+ }),
243
+ },
244
+ ).build();
245
+
246
+ expect(env).toEqual({
247
+ SECRET: 'my-secret',
248
+ DATABASE_HOST: 'localhost',
249
+ DATABASE_PORT: 5432,
250
+ BUCKET_NAME: 'my-bucket',
251
+ });
252
+ });
253
+ });
274
254
  });