@geekmidas/envkit 0.0.4 → 0.0.6
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/{EnvironmentParser-Bo2CCl_K.cjs → EnvironmentParser-BDPDLv6i.cjs} +7 -7
- package/dist/EnvironmentParser-C-arQEHQ.d.mts +108 -0
- package/dist/{EnvironmentParser-jKrGMBhP.mjs → EnvironmentParser-CQUOGqc0.mjs} +7 -7
- package/dist/EnvironmentParser-X4h2Vp4r.d.cts +108 -0
- package/dist/EnvironmentParser.cjs +1 -1
- package/dist/EnvironmentParser.d.cts +2 -0
- package/dist/EnvironmentParser.d.mts +2 -0
- package/dist/EnvironmentParser.mjs +1 -1
- package/dist/__tests__/ConfigParser.spec.cjs +1 -1
- package/dist/__tests__/ConfigParser.spec.d.cts +1 -0
- package/dist/__tests__/ConfigParser.spec.d.mts +1 -0
- package/dist/__tests__/ConfigParser.spec.mjs +1 -1
- package/dist/__tests__/EnvironmentParser.spec.cjs +1 -1
- package/dist/__tests__/EnvironmentParser.spec.d.cts +1 -0
- package/dist/__tests__/EnvironmentParser.spec.d.mts +1 -0
- package/dist/__tests__/EnvironmentParser.spec.mjs +1 -1
- package/dist/__tests__/sst.spec.cjs +305 -0
- package/dist/__tests__/sst.spec.d.cts +1 -0
- package/dist/__tests__/sst.spec.d.mts +1 -0
- package/dist/__tests__/sst.spec.mjs +304 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +1 -1
- package/dist/sst-BSxwaAdz.cjs +146 -0
- package/dist/sst-CQhO0S6y.mjs +128 -0
- package/dist/sst.cjs +4 -130
- package/dist/sst.d.cts +107 -0
- package/dist/sst.d.mts +107 -0
- package/dist/sst.mjs +1 -126
- package/package.json +5 -5
- package/src/EnvironmentParser.ts +10 -10
- package/src/__tests__/sst.spec.ts +415 -0
- package/src/sst.ts +9 -9
package/dist/sst.mjs
CHANGED
|
@@ -1,128 +1,3 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ResourceType, environmentCase, normalizeResourceEnv } from "./sst-CQhO0S6y.mjs";
|
|
2
2
|
|
|
3
|
-
//#region src/sst.ts
|
|
4
|
-
/**
|
|
5
|
-
* Converts a string to environment variable case format (UPPER_SNAKE_CASE).
|
|
6
|
-
* Numbers following underscores are preserved without the underscore.
|
|
7
|
-
*
|
|
8
|
-
* @param name - The string to convert
|
|
9
|
-
* @returns The converted string in environment variable format
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* environmentCase('myVariable') // 'MY_VARIABLE'
|
|
13
|
-
* environmentCase('api_v2') // 'APIV2'
|
|
14
|
-
*/
|
|
15
|
-
function environmentCase(name) {
|
|
16
|
-
return snakecase(name).toUpperCase().replace(/_\d+/g, (r) => {
|
|
17
|
-
return r.replace("_", "");
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Enumeration of supported SST (Serverless Stack Toolkit) resource types.
|
|
22
|
-
* Used to identify and process different AWS and SST resources.
|
|
23
|
-
*/
|
|
24
|
-
let ResourceType = /* @__PURE__ */ function(ResourceType$1) {
|
|
25
|
-
ResourceType$1["ApiGatewayV2"] = "sst.aws.ApiGatewayV2";
|
|
26
|
-
ResourceType$1["Postgres"] = "sst.aws.Postgres";
|
|
27
|
-
ResourceType$1["Function"] = "sst.aws.Function";
|
|
28
|
-
ResourceType$1["Bucket"] = "sst.aws.Bucket";
|
|
29
|
-
ResourceType$1["Vpc"] = "sst.aws.Vpc";
|
|
30
|
-
ResourceType$1["Secret"] = "sst.sst.Secret";
|
|
31
|
-
ResourceType$1["SSTSecret"] = "sst:sst:Secret";
|
|
32
|
-
ResourceType$1["SSTFunction"] = "sst:sst:Function";
|
|
33
|
-
ResourceType$1["SSTApiGatewayV2"] = "sst:aws:ApiGatewayV2";
|
|
34
|
-
ResourceType$1["SSTPostgres"] = "sst:aws:Postgres";
|
|
35
|
-
ResourceType$1["SSTBucket"] = "sst:aws:Bucket";
|
|
36
|
-
return ResourceType$1;
|
|
37
|
-
}({});
|
|
38
|
-
/**
|
|
39
|
-
* Processes a Secret resource into environment variables.
|
|
40
|
-
*
|
|
41
|
-
* @param name - The resource name
|
|
42
|
-
* @param value - The Secret resource
|
|
43
|
-
* @returns Object with environment variable mappings
|
|
44
|
-
*/
|
|
45
|
-
const secret = (name, value) => ({ [environmentCase(name)]: value.value });
|
|
46
|
-
/**
|
|
47
|
-
* Processes a Postgres database resource into environment variables.
|
|
48
|
-
* Creates multiple environment variables for database connection details.
|
|
49
|
-
*
|
|
50
|
-
* @param key - The resource key
|
|
51
|
-
* @param value - The Postgres resource
|
|
52
|
-
* @returns Object with database connection environment variables
|
|
53
|
-
*/
|
|
54
|
-
const postgres = (key, value) => {
|
|
55
|
-
const prefix = `${environmentCase(key)}`;
|
|
56
|
-
return {
|
|
57
|
-
[`${prefix}_NAME`]: value.database,
|
|
58
|
-
[`${prefix}_HOST`]: value.host,
|
|
59
|
-
[`${prefix}_PASSWORD`]: value.password,
|
|
60
|
-
[`${prefix}_PORT`]: value.port,
|
|
61
|
-
[`${prefix}_USERNAME`]: value.username
|
|
62
|
-
};
|
|
63
|
-
};
|
|
64
|
-
/**
|
|
65
|
-
* Processes a Bucket resource into environment variables.
|
|
66
|
-
*
|
|
67
|
-
* @param name - The resource name
|
|
68
|
-
* @param value - The Bucket resource
|
|
69
|
-
* @returns Object with bucket name environment variable
|
|
70
|
-
*/
|
|
71
|
-
const bucket = (name, value) => {
|
|
72
|
-
const prefix = `${environmentCase(name)}`;
|
|
73
|
-
return { [`${prefix}_NAME`]: value.name };
|
|
74
|
-
};
|
|
75
|
-
/**
|
|
76
|
-
* No-operation processor for resources that don't require environment variables.
|
|
77
|
-
*
|
|
78
|
-
* @param name - The resource name (unused)
|
|
79
|
-
* @param value - The resource value (unused)
|
|
80
|
-
* @returns Empty object
|
|
81
|
-
*/
|
|
82
|
-
const noop = (name, value) => ({});
|
|
83
|
-
/**
|
|
84
|
-
* Map of resource types to their corresponding processor functions.
|
|
85
|
-
* Each processor converts resource data into environment variables.
|
|
86
|
-
*/
|
|
87
|
-
const processors = {
|
|
88
|
-
[ResourceType.ApiGatewayV2]: noop,
|
|
89
|
-
[ResourceType.Function]: noop,
|
|
90
|
-
[ResourceType.Vpc]: noop,
|
|
91
|
-
[ResourceType.Secret]: secret,
|
|
92
|
-
[ResourceType.Postgres]: postgres,
|
|
93
|
-
[ResourceType.Bucket]: bucket,
|
|
94
|
-
[ResourceType.SSTSecret]: secret,
|
|
95
|
-
[ResourceType.SSTBucket]: bucket,
|
|
96
|
-
[ResourceType.SSTFunction]: noop,
|
|
97
|
-
[ResourceType.SSTPostgres]: postgres,
|
|
98
|
-
[ResourceType.SSTApiGatewayV2]: noop
|
|
99
|
-
};
|
|
100
|
-
/**
|
|
101
|
-
* Normalizes SST resources and plain strings into environment variables.
|
|
102
|
-
* Processes resources based on their type and converts names to environment case.
|
|
103
|
-
*
|
|
104
|
-
* @param record - Object containing resources and/or string values
|
|
105
|
-
* @returns Normalized environment variables object
|
|
106
|
-
*
|
|
107
|
-
* @example
|
|
108
|
-
* normalizeResourceEnv({
|
|
109
|
-
* apiUrl: 'https://api.example.com',
|
|
110
|
-
* database: { type: ResourceType.Postgres, ... }
|
|
111
|
-
* })
|
|
112
|
-
*/
|
|
113
|
-
function normalizeResourceEnv(record) {
|
|
114
|
-
const env = {};
|
|
115
|
-
for (const [k, value] of Object.entries(record)) {
|
|
116
|
-
if (typeof value === "string") {
|
|
117
|
-
env[environmentCase(k)] = value;
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
const processor = processors[value.type];
|
|
121
|
-
if (processor) Object.assign(env, processor(k, value));
|
|
122
|
-
else console.warn(`No processor found for resource type: `, { value });
|
|
123
|
-
}
|
|
124
|
-
return env;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
//#endregion
|
|
128
3
|
export { ResourceType, environmentCase, normalizeResourceEnv };
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geekmidas/envkit",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
8
9
|
"import": "./dist/index.mjs",
|
|
9
|
-
"require": "./dist/index.cjs"
|
|
10
|
-
"types": "./src/index.ts"
|
|
10
|
+
"require": "./dist/index.cjs"
|
|
11
11
|
},
|
|
12
12
|
"./sst": {
|
|
13
|
+
"types": "./dist/sst.d.ts",
|
|
13
14
|
"import": "./dist/sst.mjs",
|
|
14
|
-
"require": "./dist/sst.cjs"
|
|
15
|
-
"types": "./src/sst.ts"
|
|
15
|
+
"require": "./dist/sst.cjs"
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
"publishConfig": {
|
package/src/EnvironmentParser.ts
CHANGED
|
@@ -5,13 +5,13 @@ import { z } from 'zod/v4';
|
|
|
5
5
|
/**
|
|
6
6
|
* Parses and validates configuration objects against Zod schemas.
|
|
7
7
|
* Handles nested configurations and aggregates validation errors.
|
|
8
|
-
*
|
|
8
|
+
*
|
|
9
9
|
* @template TResponse - The shape of the configuration object
|
|
10
10
|
*/
|
|
11
11
|
export class ConfigParser<TResponse extends EmptyObject> {
|
|
12
12
|
/**
|
|
13
13
|
* Creates a new ConfigParser instance.
|
|
14
|
-
*
|
|
14
|
+
*
|
|
15
15
|
* @param config - The configuration object to parse
|
|
16
16
|
*/
|
|
17
17
|
constructor(private readonly config: TResponse) {}
|
|
@@ -71,9 +71,9 @@ export class ConfigParser<TResponse extends EmptyObject> {
|
|
|
71
71
|
* Parses environment variables with type-safe validation using Zod schemas.
|
|
72
72
|
* Provides a fluent API for defining environment variable schemas with automatic
|
|
73
73
|
* error context enrichment.
|
|
74
|
-
*
|
|
74
|
+
*
|
|
75
75
|
* @template T - The type of the configuration object (typically process.env)
|
|
76
|
-
*
|
|
76
|
+
*
|
|
77
77
|
* @example
|
|
78
78
|
* ```typescript
|
|
79
79
|
* const config = new EnvironmentParser(process.env)
|
|
@@ -89,7 +89,7 @@ export class ConfigParser<TResponse extends EmptyObject> {
|
|
|
89
89
|
export class EnvironmentParser<T extends EmptyObject> {
|
|
90
90
|
/**
|
|
91
91
|
* Creates a new EnvironmentParser instance.
|
|
92
|
-
*
|
|
92
|
+
*
|
|
93
93
|
* @param config - The configuration object to parse (typically process.env)
|
|
94
94
|
*/
|
|
95
95
|
constructor(private readonly config: T) {}
|
|
@@ -97,7 +97,7 @@ export class EnvironmentParser<T extends EmptyObject> {
|
|
|
97
97
|
/**
|
|
98
98
|
* Wraps a Zod schema to intercept parse/safeParse calls and enrich error messages
|
|
99
99
|
* with environment variable context.
|
|
100
|
-
*
|
|
100
|
+
*
|
|
101
101
|
* @param schema - The Zod schema to wrap
|
|
102
102
|
* @param name - The environment variable name for error context
|
|
103
103
|
* @returns A wrapped Zod schema with enhanced error reporting
|
|
@@ -172,7 +172,7 @@ export class EnvironmentParser<T extends EmptyObject> {
|
|
|
172
172
|
/**
|
|
173
173
|
* Creates a proxied version of the Zod object that wraps all schema creators
|
|
174
174
|
* to provide enhanced error messages with environment variable context.
|
|
175
|
-
*
|
|
175
|
+
*
|
|
176
176
|
* @param name - The environment variable name
|
|
177
177
|
* @returns A proxied Zod object with wrapped schema creators
|
|
178
178
|
*/
|
|
@@ -234,7 +234,7 @@ export class EnvironmentParser<T extends EmptyObject> {
|
|
|
234
234
|
/**
|
|
235
235
|
* Infers the TypeScript type of a configuration object based on its Zod schemas.
|
|
236
236
|
* Recursively processes nested objects and extracts types from Zod schemas.
|
|
237
|
-
*
|
|
237
|
+
*
|
|
238
238
|
* @template T - The configuration object type
|
|
239
239
|
*/
|
|
240
240
|
export type InferConfig<T extends EmptyObject> = {
|
|
@@ -248,7 +248,7 @@ export type InferConfig<T extends EmptyObject> = {
|
|
|
248
248
|
/**
|
|
249
249
|
* Function type for fetching environment variables with Zod validation.
|
|
250
250
|
* Returns a Zod object scoped to a specific environment variable.
|
|
251
|
-
*
|
|
251
|
+
*
|
|
252
252
|
* @template TPath - The environment variable path type
|
|
253
253
|
* @param name - The environment variable name
|
|
254
254
|
* @returns A Zod object for defining the schema
|
|
@@ -260,7 +260,7 @@ export type EnvFetcher<TPath extends string = string> = (
|
|
|
260
260
|
/**
|
|
261
261
|
* Function type for building environment configuration objects.
|
|
262
262
|
* Takes an EnvFetcher and returns a configuration object with Zod schemas.
|
|
263
|
-
*
|
|
263
|
+
*
|
|
264
264
|
* @template TResponse - The response configuration object type
|
|
265
265
|
* @param get - The environment variable fetcher function
|
|
266
266
|
* @returns The configuration object with Zod schemas
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
type ApiGatewayV2,
|
|
4
|
+
type Bucket,
|
|
5
|
+
type Function,
|
|
6
|
+
type Postgres,
|
|
7
|
+
ResourceType,
|
|
8
|
+
type Secret,
|
|
9
|
+
type Vpc,
|
|
10
|
+
environmentCase,
|
|
11
|
+
normalizeResourceEnv,
|
|
12
|
+
} from '../sst';
|
|
13
|
+
|
|
14
|
+
describe('sst', () => {
|
|
15
|
+
describe('environmentCase', () => {
|
|
16
|
+
it('should convert camelCase to UPPER_SNAKE_CASE', () => {
|
|
17
|
+
expect(environmentCase('myVariable')).toBe('MY_VARIABLE');
|
|
18
|
+
expect(environmentCase('somePropertyName')).toBe('SOME_PROPERTY_NAME');
|
|
19
|
+
expect(environmentCase('APIKey')).toBe('API_KEY');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should convert snake_case to UPPER_SNAKE_CASE', () => {
|
|
23
|
+
expect(environmentCase('my_variable')).toBe('MY_VARIABLE');
|
|
24
|
+
expect(environmentCase('some_property_name')).toBe('SOME_PROPERTY_NAME');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should handle numbers correctly', () => {
|
|
28
|
+
// The function only removes underscore before digits
|
|
29
|
+
expect(environmentCase('api_v2')).toBe('API_V2');
|
|
30
|
+
expect(environmentCase('value_123')).toBe('VALUE123');
|
|
31
|
+
expect(environmentCase('test_1_thing')).toBe('TEST1_THING');
|
|
32
|
+
expect(environmentCase('api_2')).toBe('API2');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should handle already uppercase strings', () => {
|
|
36
|
+
expect(environmentCase('ALREADY_UPPER')).toBe('ALREADY_UPPER');
|
|
37
|
+
expect(environmentCase('TEST')).toBe('TEST');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should handle kebab-case', () => {
|
|
41
|
+
expect(environmentCase('my-variable')).toBe('MY_VARIABLE');
|
|
42
|
+
expect(environmentCase('kebab-case-example')).toBe('KEBAB_CASE_EXAMPLE');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should handle mixed cases', () => {
|
|
46
|
+
expect(environmentCase('MixedCASE')).toBe('MIXED_CASE');
|
|
47
|
+
expect(environmentCase('XMLParser')).toBe('XML_PARSER');
|
|
48
|
+
expect(environmentCase('IOStream')).toBe('IO_STREAM');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should handle empty string', () => {
|
|
52
|
+
expect(environmentCase('')).toBe('');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should handle single character', () => {
|
|
56
|
+
expect(environmentCase('a')).toBe('A');
|
|
57
|
+
expect(environmentCase('A')).toBe('A');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should handle strings with special characters', () => {
|
|
61
|
+
expect(environmentCase('my.variable')).toBe('MY_VARIABLE');
|
|
62
|
+
expect(environmentCase('my@variable')).toBe('MY_VARIABLE');
|
|
63
|
+
expect(environmentCase('my#variable')).toBe('MY_VARIABLE');
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe('normalizeResourceEnv', () => {
|
|
68
|
+
describe('string values', () => {
|
|
69
|
+
it('should convert string values to environment case', () => {
|
|
70
|
+
const input = {
|
|
71
|
+
apiUrl: 'https://api.example.com',
|
|
72
|
+
secretKey: 'my-secret-key',
|
|
73
|
+
NODE_ENV: 'production',
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const result = normalizeResourceEnv(input);
|
|
77
|
+
|
|
78
|
+
expect(result).toEqual({
|
|
79
|
+
API_URL: 'https://api.example.com',
|
|
80
|
+
SECRET_KEY: 'my-secret-key',
|
|
81
|
+
NODE_ENV: 'production',
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should handle empty object', () => {
|
|
86
|
+
expect(normalizeResourceEnv({})).toEqual({});
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('Secret resource', () => {
|
|
91
|
+
it('should process Secret resource correctly', () => {
|
|
92
|
+
const secret: Secret = {
|
|
93
|
+
type: ResourceType.Secret,
|
|
94
|
+
value: 'super-secret-value',
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const result = normalizeResourceEnv({
|
|
98
|
+
mySecret: secret,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
expect(result).toEqual({
|
|
102
|
+
MY_SECRET: 'super-secret-value',
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should process SSTSecret resource correctly', () => {
|
|
107
|
+
const secret = {
|
|
108
|
+
type: ResourceType.SSTSecret as ResourceType.SSTSecret,
|
|
109
|
+
value: 'another-secret',
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const result = normalizeResourceEnv({
|
|
113
|
+
appSecret: secret as any,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
expect(result).toEqual({
|
|
117
|
+
APP_SECRET: 'another-secret',
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe('Postgres resource', () => {
|
|
123
|
+
it('should process Postgres resource correctly', () => {
|
|
124
|
+
const postgres: Postgres = {
|
|
125
|
+
type: ResourceType.Postgres,
|
|
126
|
+
database: 'myapp',
|
|
127
|
+
host: 'localhost',
|
|
128
|
+
password: 'password123',
|
|
129
|
+
port: 5432,
|
|
130
|
+
username: 'postgres',
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const result = normalizeResourceEnv({
|
|
134
|
+
database: postgres,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
expect(result).toEqual({
|
|
138
|
+
DATABASE_NAME: 'myapp',
|
|
139
|
+
DATABASE_HOST: 'localhost',
|
|
140
|
+
DATABASE_PASSWORD: 'password123',
|
|
141
|
+
DATABASE_PORT: 5432,
|
|
142
|
+
DATABASE_USERNAME: 'postgres',
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('should process SSTPostgres resource correctly', () => {
|
|
147
|
+
const postgres = {
|
|
148
|
+
type: ResourceType.SSTPostgres as ResourceType.SSTPostgres,
|
|
149
|
+
database: 'prod_db',
|
|
150
|
+
host: 'prod.example.com',
|
|
151
|
+
password: 'prod-password',
|
|
152
|
+
port: 5433,
|
|
153
|
+
username: 'prod_user',
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const result = normalizeResourceEnv({
|
|
157
|
+
mainDb: postgres as any,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
expect(result).toEqual({
|
|
161
|
+
MAIN_DB_NAME: 'prod_db',
|
|
162
|
+
MAIN_DB_HOST: 'prod.example.com',
|
|
163
|
+
MAIN_DB_PASSWORD: 'prod-password',
|
|
164
|
+
MAIN_DB_PORT: 5433,
|
|
165
|
+
MAIN_DB_USERNAME: 'prod_user',
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe('Bucket resource', () => {
|
|
171
|
+
it('should process Bucket resource correctly', () => {
|
|
172
|
+
const bucket: Bucket = {
|
|
173
|
+
type: ResourceType.Bucket,
|
|
174
|
+
name: 'my-s3-bucket',
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const result = normalizeResourceEnv({
|
|
178
|
+
uploadBucket: bucket,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
expect(result).toEqual({
|
|
182
|
+
UPLOAD_BUCKET_NAME: 'my-s3-bucket',
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('should process SSTBucket resource correctly', () => {
|
|
187
|
+
const bucket = {
|
|
188
|
+
type: ResourceType.SSTBucket as ResourceType.SSTBucket,
|
|
189
|
+
name: 'assets-bucket-prod',
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const result = normalizeResourceEnv({
|
|
193
|
+
assetStorage: bucket as any,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
expect(result).toEqual({
|
|
197
|
+
ASSET_STORAGE_NAME: 'assets-bucket-prod',
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
describe('noop resources', () => {
|
|
203
|
+
it('should not add environment variables for ApiGatewayV2', () => {
|
|
204
|
+
const api: ApiGatewayV2 = {
|
|
205
|
+
type: ResourceType.ApiGatewayV2,
|
|
206
|
+
url: 'https://api.example.com',
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const result = normalizeResourceEnv({
|
|
210
|
+
api: api,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
expect(result).toEqual({});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('should not add environment variables for Function', () => {
|
|
217
|
+
const fn: Function = {
|
|
218
|
+
type: ResourceType.Function,
|
|
219
|
+
name: 'my-lambda',
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const result = normalizeResourceEnv({
|
|
223
|
+
handler: fn,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
expect(result).toEqual({});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('should not add environment variables for Vpc', () => {
|
|
230
|
+
const vpc: Vpc = {
|
|
231
|
+
type: ResourceType.Vpc,
|
|
232
|
+
bastion: 'bastion-host',
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const result = normalizeResourceEnv({
|
|
236
|
+
network: vpc,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
expect(result).toEqual({});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('should handle all SST noop resource types', () => {
|
|
243
|
+
const api = {
|
|
244
|
+
type: ResourceType.SSTApiGatewayV2 as ResourceType.SSTApiGatewayV2,
|
|
245
|
+
url: 'https://api.example.com',
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const fn = {
|
|
249
|
+
type: ResourceType.SSTFunction as ResourceType.SSTFunction,
|
|
250
|
+
name: 'my-function',
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const result = normalizeResourceEnv({
|
|
254
|
+
api: api as any,
|
|
255
|
+
function: fn as any,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
expect(result).toEqual({});
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
describe('mixed resources', () => {
|
|
263
|
+
it('should handle mix of strings and resources', () => {
|
|
264
|
+
const postgres: Postgres = {
|
|
265
|
+
type: ResourceType.Postgres,
|
|
266
|
+
database: 'app_db',
|
|
267
|
+
host: 'db.example.com',
|
|
268
|
+
password: 'db-pass',
|
|
269
|
+
port: 5432,
|
|
270
|
+
username: 'app_user',
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const secret: Secret = {
|
|
274
|
+
type: ResourceType.Secret,
|
|
275
|
+
value: 'jwt-secret',
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const bucket: Bucket = {
|
|
279
|
+
type: ResourceType.Bucket,
|
|
280
|
+
name: 'uploads-bucket',
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const result = normalizeResourceEnv({
|
|
284
|
+
nodeEnv: 'production',
|
|
285
|
+
appName: 'My App',
|
|
286
|
+
database: postgres,
|
|
287
|
+
jwtSecret: secret,
|
|
288
|
+
uploads: bucket,
|
|
289
|
+
apiVersion: 'v2',
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
expect(result).toEqual({
|
|
293
|
+
NODE_ENV: 'production',
|
|
294
|
+
APP_NAME: 'My App',
|
|
295
|
+
DATABASE_NAME: 'app_db',
|
|
296
|
+
DATABASE_HOST: 'db.example.com',
|
|
297
|
+
DATABASE_PASSWORD: 'db-pass',
|
|
298
|
+
DATABASE_PORT: 5432,
|
|
299
|
+
DATABASE_USERNAME: 'app_user',
|
|
300
|
+
JWT_SECRET: 'jwt-secret',
|
|
301
|
+
UPLOADS_NAME: 'uploads-bucket',
|
|
302
|
+
API_VERSION: 'v2',
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
describe('edge cases', () => {
|
|
308
|
+
it('should warn for unknown resource types', () => {
|
|
309
|
+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
310
|
+
|
|
311
|
+
const unknownResource = {
|
|
312
|
+
type: 'unknown.resource.Type' as any,
|
|
313
|
+
value: 'something',
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const result = normalizeResourceEnv({
|
|
317
|
+
unknown: unknownResource,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
expect(result).toEqual({});
|
|
321
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
|
322
|
+
'No processor found for resource type: ',
|
|
323
|
+
{ value: unknownResource }
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
consoleWarnSpy.mockRestore();
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it('should handle resources with special characters in keys', () => {
|
|
330
|
+
const secret: Secret = {
|
|
331
|
+
type: ResourceType.Secret,
|
|
332
|
+
value: 'value',
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const result = normalizeResourceEnv({
|
|
336
|
+
'my-secret-key': secret,
|
|
337
|
+
'another.secret': secret,
|
|
338
|
+
'secret@123': secret,
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
expect(result).toEqual({
|
|
342
|
+
MY_SECRET_KEY: 'value',
|
|
343
|
+
ANOTHER_SECRET: 'value',
|
|
344
|
+
SECRET123: 'value',
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('should handle numeric values in postgres port', () => {
|
|
349
|
+
const postgres: Postgres = {
|
|
350
|
+
type: ResourceType.Postgres,
|
|
351
|
+
database: 'test',
|
|
352
|
+
host: 'localhost',
|
|
353
|
+
password: 'pass',
|
|
354
|
+
port: 5432,
|
|
355
|
+
username: 'user',
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const result = normalizeResourceEnv({
|
|
359
|
+
db: postgres,
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
expect(result.DB_PORT).toBe(5432);
|
|
363
|
+
expect(typeof result.DB_PORT).toBe('number');
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it('should handle very long keys', () => {
|
|
367
|
+
const secret: Secret = {
|
|
368
|
+
type: ResourceType.Secret,
|
|
369
|
+
value: 'value',
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const result = normalizeResourceEnv({
|
|
373
|
+
thisIsAVeryLongKeyNameThatShouldBeConvertedProperly: secret,
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
expect(result).toEqual({
|
|
377
|
+
THIS_IS_A_VERY_LONG_KEY_NAME_THAT_SHOULD_BE_CONVERTED_PROPERLY: 'value',
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it('should handle multiple underscores and numbers', () => {
|
|
382
|
+
const bucket: Bucket = {
|
|
383
|
+
type: ResourceType.Bucket,
|
|
384
|
+
name: 'test-bucket',
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
const result = normalizeResourceEnv({
|
|
388
|
+
's3_bucket_v2_1': bucket,
|
|
389
|
+
'bucket_123_456': bucket,
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
expect(result).toEqual({
|
|
393
|
+
S3_BUCKET_V21_NAME: 'test-bucket',
|
|
394
|
+
BUCKET123456_NAME: 'test-bucket',
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
describe('ResourceType enum', () => {
|
|
401
|
+
it('should have all expected resource types', () => {
|
|
402
|
+
expect(ResourceType.ApiGatewayV2).toBe('sst.aws.ApiGatewayV2');
|
|
403
|
+
expect(ResourceType.Postgres).toBe('sst.aws.Postgres');
|
|
404
|
+
expect(ResourceType.Function).toBe('sst.aws.Function');
|
|
405
|
+
expect(ResourceType.Bucket).toBe('sst.aws.Bucket');
|
|
406
|
+
expect(ResourceType.Vpc).toBe('sst.aws.Vpc');
|
|
407
|
+
expect(ResourceType.Secret).toBe('sst.sst.Secret');
|
|
408
|
+
expect(ResourceType.SSTSecret).toBe('sst:sst:Secret');
|
|
409
|
+
expect(ResourceType.SSTFunction).toBe('sst:sst:Function');
|
|
410
|
+
expect(ResourceType.SSTApiGatewayV2).toBe('sst:aws:ApiGatewayV2');
|
|
411
|
+
expect(ResourceType.SSTPostgres).toBe('sst:aws:Postgres');
|
|
412
|
+
expect(ResourceType.SSTBucket).toBe('sst:aws:Bucket');
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
});
|