@hexaijs/utils 0.1.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 (35) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +325 -0
  3. package/dist/config/config-spec.d.ts +49 -0
  4. package/dist/config/config-spec.d.ts.map +1 -0
  5. package/dist/config/config-spec.js +10 -0
  6. package/dist/config/config-spec.js.map +1 -0
  7. package/dist/config/database-config.d.ts +22 -0
  8. package/dist/config/database-config.d.ts.map +1 -0
  9. package/dist/config/database-config.js +3 -0
  10. package/dist/config/database-config.js.map +1 -0
  11. package/dist/config/define-config.d.ts +45 -0
  12. package/dist/config/define-config.d.ts.map +1 -0
  13. package/dist/config/define-config.js +70 -0
  14. package/dist/config/define-config.js.map +1 -0
  15. package/dist/config/env-spec.d.ts +78 -0
  16. package/dist/config/env-spec.d.ts.map +1 -0
  17. package/dist/config/env-spec.js +123 -0
  18. package/dist/config/env-spec.js.map +1 -0
  19. package/dist/config/env-spec.spec.d.ts +2 -0
  20. package/dist/config/env-spec.spec.d.ts.map +1 -0
  21. package/dist/config/env-spec.spec.js +184 -0
  22. package/dist/config/env-spec.spec.js.map +1 -0
  23. package/dist/config/errors.d.ts +9 -0
  24. package/dist/config/errors.d.ts.map +1 -0
  25. package/dist/config/errors.js +17 -0
  26. package/dist/config/errors.js.map +1 -0
  27. package/dist/config/index.d.ts +7 -0
  28. package/dist/config/index.d.ts.map +1 -0
  29. package/dist/config/index.js +34 -0
  30. package/dist/config/index.js.map +1 -0
  31. package/dist/config/load-env-files.d.ts +5 -0
  32. package/dist/config/load-env-files.d.ts.map +1 -0
  33. package/dist/config/load-env-files.js +34 -0
  34. package/dist/config/load-env-files.js.map +1 -0
  35. package/package.json +47 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Sangwoo Hyun
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,325 @@
1
+ # @hexaijs/utils
2
+
3
+ > Configuration management utilities for hexai applications
4
+
5
+ ## Overview
6
+
7
+ `@hexaijs/utils` provides utilities for managing application configuration in a type-safe, validated manner. The package currently focuses on configuration management, offering a declarative approach to reading and validating environment variables.
8
+
9
+ The configuration system solves several common problems:
10
+
11
+ 1. **Type Safety** - Environment variables are strings, but your application needs typed values (numbers, booleans, objects). The config system handles transformation and inference automatically.
12
+ 2. **Validation at Startup** - Missing or invalid configuration is detected immediately when your application starts, not when the code path is first executed.
13
+ 3. **Singleton Management** - Configuration is resolved once and cached, with special handling for test environments.
14
+ 4. **Extensibility** - Custom config specs allow database packages and other infrastructure to integrate seamlessly.
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install @hexaijs/utils
20
+ ```
21
+
22
+ ## Core Concepts
23
+
24
+ ### defineConfig
25
+
26
+ `defineConfig` creates a typed configuration getter that resolves and validates configuration on first access.
27
+
28
+ ```typescript
29
+ import {
30
+ defineConfig,
31
+ env,
32
+ envOptional,
33
+ envNumber,
34
+ envBoolean,
35
+ } from "@hexaijs/utils/config";
36
+
37
+ export const getConfig = defineConfig({
38
+ // Required string
39
+ apiKey: env("API_KEY"),
40
+
41
+ // Optional with default
42
+ logLevel: envOptional("LOG_LEVEL", "info"),
43
+
44
+ // Required number
45
+ port: envNumber("PORT"),
46
+
47
+ // Boolean with default
48
+ debug: envBoolean("DEBUG", false),
49
+
50
+ // Static values are also supported
51
+ appName: "my-app",
52
+ maxRetries: 3,
53
+ });
54
+
55
+ // Usage
56
+ const config = getConfig();
57
+ config.apiKey; // string
58
+ config.logLevel; // string | undefined
59
+ config.port; // number
60
+ config.debug; // boolean
61
+ config.appName; // "my-app" (literal type preserved)
62
+ config.maxRetries; // 3 (literal type preserved)
63
+ ```
64
+
65
+ The returned getter function caches the resolved config (singleton pattern). In test environments (`NODE_ENV=test`), the config is recreated on each call to reflect environment changes between tests.
66
+
67
+ ### Environment Variable Specs
68
+
69
+ Builder functions for different data types:
70
+
71
+ | Function | Type | Description |
72
+ |----------|------|-------------|
73
+ | `env(key)` | `string` | Required string |
74
+ | `envOptional(key, default?)` | `string \| undefined` | Optional string with default |
75
+ | `envNumber(key)` | `number` | Required number |
76
+ | `envNumberOptional(key, default?)` | `number \| undefined` | Optional number |
77
+ | `envBoolean(key, default)` | `boolean` | Boolean (`"true"`, `"1"` → true) |
78
+ | `envJson<T>(key)` | `T` | Required JSON-parsed object |
79
+ | `envJsonOptional<T>(key, default?)` | `T \| undefined` | Optional JSON object |
80
+
81
+ ```typescript
82
+ import {
83
+ defineConfig,
84
+ env,
85
+ envNumber,
86
+ envBoolean,
87
+ envJson,
88
+ } from "@hexaijs/utils/config";
89
+
90
+ const getConfig = defineConfig({
91
+ // Required - throws ConfigValidationError if missing
92
+ databaseUrl: env("DATABASE_URL"),
93
+
94
+ // Number transformation
95
+ port: envNumber("PORT"),
96
+
97
+ // Boolean - recognizes "true" and "1"
98
+ enableMetrics: envBoolean("ENABLE_METRICS", false),
99
+
100
+ // JSON parsing with type inference
101
+ rateLimits: envJson<{ requests: number; window: number }>("RATE_LIMITS"),
102
+ });
103
+ ```
104
+
105
+ ### ConfigValidationError
106
+
107
+ When required environment variables are missing or transformation fails, `defineConfig` collects all errors and throws a single `ConfigValidationError`.
108
+
109
+ ```typescript
110
+ import { defineConfig, env, ConfigValidationError } from "@hexaijs/utils/config";
111
+
112
+ const getConfig = defineConfig({
113
+ apiKey: env("API_KEY"),
114
+ secretKey: env("SECRET_KEY"),
115
+ databaseUrl: env("DATABASE_URL"),
116
+ });
117
+
118
+ try {
119
+ const config = getConfig();
120
+ } catch (e) {
121
+ if (e instanceof ConfigValidationError) {
122
+ console.error("Configuration errors:");
123
+ e.errors.forEach(err => console.error(` - ${err}`));
124
+ // Config validation failed:
125
+ // - Missing required env: API_KEY
126
+ // - Missing required env: SECRET_KEY
127
+ // - Missing required env: DATABASE_URL
128
+ process.exit(1);
129
+ }
130
+ throw e;
131
+ }
132
+ ```
133
+
134
+ All validation errors are collected before throwing, so you see every missing variable at once rather than fixing them one at a time.
135
+
136
+ ### Custom ConfigSpec
137
+
138
+ The `ConfigSpec` interface allows you to create custom configuration resolvers. Infrastructure packages like `@hexaijs/postgres` use this to provide database configuration specs.
139
+
140
+ ```typescript
141
+ import type { ConfigSpec } from "@hexaijs/utils/config";
142
+
143
+ // Example: Custom config spec for Redis
144
+ class RedisSpec implements ConfigSpec<RedisConfig> {
145
+ readonly _type = "redis";
146
+
147
+ constructor(private prefix: string) {}
148
+
149
+ resolve(errors: string[]): RedisConfig | undefined {
150
+ const host = process.env[`${this.prefix}_HOST`];
151
+ const port = process.env[`${this.prefix}_PORT`];
152
+
153
+ if (!host) {
154
+ errors.push(`Missing required env: ${this.prefix}_HOST`);
155
+ return undefined;
156
+ }
157
+
158
+ return {
159
+ host,
160
+ port: port ? parseInt(port) : 6379,
161
+ };
162
+ }
163
+ }
164
+
165
+ // Usage with defineConfig
166
+ const getConfig = defineConfig({
167
+ redis: new RedisSpec("REDIS"),
168
+ apiKey: env("API_KEY"),
169
+ });
170
+ ```
171
+
172
+ The `resolve` method should push error messages to the `errors` array instead of throwing, allowing all errors to be collected.
173
+
174
+ ### DatabaseConfig Interface
175
+
176
+ A common interface that database configuration classes implement, enabling polymorphic handling of different databases.
177
+
178
+ ```typescript
179
+ import type { DatabaseConfig } from "@hexaijs/utils/config";
180
+
181
+ // PostgresConfig from @hexaijs/postgres implements this interface
182
+ interface DatabaseConfig {
183
+ readonly host: string;
184
+ readonly port: number;
185
+ readonly database: string;
186
+ readonly user: string;
187
+ readonly password?: string;
188
+ toString(): string; // Returns connection URL
189
+ }
190
+ ```
191
+
192
+ This interface is used by database packages to provide type-safe configuration. See `@hexaijs/postgres` for a complete implementation.
193
+
194
+ ### Loading .env Files
195
+
196
+ The `loadEnvFiles` function loads environment files following a standard precedence order.
197
+
198
+ ```typescript
199
+ import { loadEnvFiles, defineConfig, env } from "@hexaijs/utils/config";
200
+
201
+ // Option 1: Load env files before defining config
202
+ loadEnvFiles();
203
+
204
+ const getConfig = defineConfig({
205
+ apiKey: env("API_KEY"),
206
+ });
207
+
208
+ // Option 2: Use loadEnv option in defineConfig
209
+ const getConfig = defineConfig(
210
+ { apiKey: env("API_KEY") },
211
+ { loadEnv: true }
212
+ );
213
+ ```
214
+
215
+ **File loading order** (later files override earlier):
216
+ 1. `.env` - Default values
217
+ 2. `.env.local` - Local overrides (gitignored)
218
+ 3. `.env.{NODE_ENV}` - Environment-specific (e.g., `.env.test`)
219
+ 4. `.env.{NODE_ENV}.local` - Local environment overrides
220
+
221
+ ```typescript
222
+ // Custom base path and environment
223
+ loadEnvFiles({
224
+ basePath: "/app/config",
225
+ nodeEnv: "staging",
226
+ });
227
+ ```
228
+
229
+ ## Usage
230
+
231
+ ### Application Configuration Pattern
232
+
233
+ A typical pattern for hexai applications:
234
+
235
+ ```typescript
236
+ // config.ts
237
+ import { defineConfig, env, envBoolean, envNumber } from "@hexaijs/utils/config";
238
+ import { postgresConfig } from "@hexaijs/postgres";
239
+
240
+ export const getConfig = defineConfig(
241
+ {
242
+ db: postgresConfig("ORDER_DB"), // ConfigSpec - errors collected properly
243
+ port: envNumber("PORT"),
244
+ debug: envBoolean("DEBUG", false),
245
+ apiKey: env("API_KEY"),
246
+ },
247
+ { loadEnv: true }
248
+ );
249
+
250
+ // application-context.ts
251
+ import { getConfig } from "./config";
252
+
253
+ export class OrderApplicationContext {
254
+ private readonly config = getConfig();
255
+
256
+ getDatabase() {
257
+ return this.config.db;
258
+ }
259
+
260
+ getPort() {
261
+ return this.config.port;
262
+ }
263
+ }
264
+ ```
265
+
266
+ ### Testing with Environment Changes
267
+
268
+ In test environments, the config is recreated on each call:
269
+
270
+ ```typescript
271
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
272
+ import { defineConfig, env } from "@hexaijs/utils/config";
273
+
274
+ describe("MyService", () => {
275
+ const originalEnv = process.env;
276
+
277
+ beforeEach(() => {
278
+ process.env = { ...originalEnv, NODE_ENV: "test" };
279
+ });
280
+
281
+ afterEach(() => {
282
+ process.env = originalEnv;
283
+ });
284
+
285
+ it("uses API_KEY from environment", () => {
286
+ process.env.API_KEY = "test-key";
287
+
288
+ const getConfig = defineConfig({ apiKey: env("API_KEY") });
289
+
290
+ expect(getConfig().apiKey).toBe("test-key");
291
+ });
292
+
293
+ it("reflects environment changes", () => {
294
+ process.env.API_KEY = "first-key";
295
+ const getConfig = defineConfig({ apiKey: env("API_KEY") });
296
+
297
+ expect(getConfig().apiKey).toBe("first-key");
298
+
299
+ process.env.API_KEY = "second-key";
300
+ expect(getConfig().apiKey).toBe("second-key");
301
+ });
302
+ });
303
+ ```
304
+
305
+ ## API Highlights
306
+
307
+ | Export | Description |
308
+ |--------|-------------|
309
+ | `defineConfig(schema, options?)` | Creates typed config getter with validation |
310
+ | `env(key)` | Required string environment variable |
311
+ | `envOptional(key, default?)` | Optional string with default |
312
+ | `envNumber(key)` | Required number (transforms string) |
313
+ | `envNumberOptional(key, default?)` | Optional number |
314
+ | `envBoolean(key, default)` | Boolean (`"true"`, `"1"` → true) |
315
+ | `envJson<T>(key)` | Required JSON-parsed value |
316
+ | `envJsonOptional<T>(key, default?)` | Optional JSON value |
317
+ | `ConfigSpec<T>` | Interface for custom config specs |
318
+ | `ConfigValidationError` | Error containing all validation failures |
319
+ | `DatabaseConfig` | Common interface for database configs |
320
+ | `loadEnvFiles(options?)` | Loads .env files with NODE_ENV awareness |
321
+
322
+ ## See Also
323
+
324
+ - [@hexaijs/postgres](../postgres/README.md) - PostgresConfig implements DatabaseConfig, `postgresConfig()` helper for use with defineConfig
325
+ - [@hexaijs/application](../application/README.md) - Application layer that uses configuration
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Base interface for all config specs.
3
+ * Implement this interface to create custom config specs that can be used with defineConfig.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * class RedisSpec implements ConfigSpec<RedisConfig> {
8
+ * readonly _type = "redis";
9
+ *
10
+ * constructor(private prefix: string) {}
11
+ *
12
+ * resolve(errors: string[]): RedisConfig | undefined {
13
+ * try {
14
+ * return RedisConfig.fromEnv(this.prefix);
15
+ * } catch (e) {
16
+ * errors.push((e as Error).message);
17
+ * return undefined;
18
+ * }
19
+ * }
20
+ * }
21
+ * ```
22
+ */
23
+ export interface ConfigSpec<T> {
24
+ /**
25
+ * Unique type identifier for this spec.
26
+ * Used for debugging and error messages.
27
+ */
28
+ readonly _type: string;
29
+ /**
30
+ * Resolve the config value from environment.
31
+ * Should push error messages to the errors array instead of throwing.
32
+ *
33
+ * @param errors - Array to collect validation errors
34
+ * @returns The resolved value, or undefined if resolution failed
35
+ */
36
+ resolve(errors: string[]): T | undefined;
37
+ }
38
+ /**
39
+ * Type helper to infer result type from a ConfigSpec.
40
+ */
41
+ export type InferSpecType<S> = S extends ConfigSpec<infer T> ? T : S;
42
+ /**
43
+ * Type helper to infer config object type from schema.
44
+ */
45
+ export type InferConfigType<S extends Record<string, unknown>> = {
46
+ readonly [K in keyof S]: InferSpecType<S[K]>;
47
+ };
48
+ export declare function isConfigSpec(value: unknown): value is ConfigSpec<unknown>;
49
+ //# sourceMappingURL=config-spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-spec.d.ts","sourceRoot":"","sources":["../../src/config/config-spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,UAAU,CAAC,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;CAC5C;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,CAAC,SAAS,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAErE;;GAEG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;IAC7D,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC/C,CAAC;AAGF,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAAC,OAAO,CAAC,CAOzE"}
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isConfigSpec = isConfigSpec;
4
+ function isConfigSpec(value) {
5
+ return (typeof value === "object" &&
6
+ value !== null &&
7
+ "resolve" in value &&
8
+ typeof value.resolve === "function");
9
+ }
10
+ //# sourceMappingURL=config-spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-spec.js","sourceRoot":"","sources":["../../src/config/config-spec.ts"],"names":[],"mappings":";;AAoDA,oCAOC;AAPD,SAAgB,YAAY,CAAC,KAAc;IACvC,OAAO,CACH,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,SAAS,IAAI,KAAK;QAClB,OAAQ,KAA6B,CAAC,OAAO,KAAK,UAAU,CAC/D,CAAC;AACN,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Common interface for all database configurations.
3
+ *
4
+ * This interface defines the minimal contract that all database config
5
+ * implementations (PostgresConfig, MySQLConfig, etc.) must satisfy.
6
+ *
7
+ * Note: Builder methods (with*) are intentionally NOT included here.
8
+ * Each implementation returns its own concrete type for better type inference.
9
+ */
10
+ export interface DatabaseConfig {
11
+ readonly host: string;
12
+ readonly port: number;
13
+ readonly database: string;
14
+ readonly user: string;
15
+ readonly password?: string;
16
+ /**
17
+ * Returns the connection URL string representation.
18
+ * Format varies by database type (e.g., postgres://, mysql://)
19
+ */
20
+ toString(): string;
21
+ }
22
+ //# sourceMappingURL=database-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-config.d.ts","sourceRoot":"","sources":["../../src/config/database-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,QAAQ,IAAI,MAAM,CAAC;CACtB"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=database-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-config.js","sourceRoot":"","sources":["../../src/config/database-config.ts"],"names":[],"mappings":""}
@@ -0,0 +1,45 @@
1
+ import { type InferConfigType } from "./config-spec";
2
+ export interface ConfigOptions {
3
+ /**
4
+ * Whether to load .env files before resolving config.
5
+ * @default false
6
+ */
7
+ loadEnv?: boolean;
8
+ /**
9
+ * Custom env loader function. Called when loadEnv is true.
10
+ * If not provided and loadEnv is true, uses dotenv.config().
11
+ */
12
+ envLoader?: () => void;
13
+ }
14
+ /**
15
+ * Defines a typed configuration with automatic validation and singleton management.
16
+ *
17
+ * @param schema - Configuration schema defining all config fields
18
+ * @param options - Optional configuration options
19
+ * @returns A function that returns the resolved config (singleton)
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * import { defineConfig, env, envBoolean } from "@hexaijs/core";
24
+ * import { postgres } from "@hexaijs/postgres";
25
+ *
26
+ * export const getConfig = defineConfig({
27
+ * db: postgres("ORDER_DB"),
28
+ * apiKey: env("API_KEY"),
29
+ * debug: envBoolean("DEBUG", false),
30
+ * // Primitive values are also supported
31
+ * appName: "my-app",
32
+ * maxRetries: 3,
33
+ * });
34
+ *
35
+ * // Usage
36
+ * const config = getConfig();
37
+ * config.db.host; // PostgresConfig property
38
+ * config.apiKey; // string
39
+ * config.debug; // boolean
40
+ * config.appName; // "my-app" (literal type)
41
+ * config.maxRetries; // 3 (literal type)
42
+ * ```
43
+ */
44
+ export declare function defineConfig<S extends Record<string, unknown>>(schema: S, options?: ConfigOptions): () => InferConfigType<S>;
45
+ //# sourceMappingURL=define-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-config.d.ts","sourceRoot":"","sources":["../../src/config/define-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAgB,MAAM,eAAe,CAAC;AAInE,MAAM,WAAW,aAAa;IAC1B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1D,MAAM,EAAE,CAAC,EACT,OAAO,CAAC,EAAE,aAAa,GACxB,MAAM,eAAe,CAAC,CAAC,CAAC,CAoC1B"}
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defineConfig = defineConfig;
4
+ const config_spec_1 = require("./config-spec");
5
+ const errors_1 = require("./errors");
6
+ const load_env_files_1 = require("./load-env-files");
7
+ /**
8
+ * Defines a typed configuration with automatic validation and singleton management.
9
+ *
10
+ * @param schema - Configuration schema defining all config fields
11
+ * @param options - Optional configuration options
12
+ * @returns A function that returns the resolved config (singleton)
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { defineConfig, env, envBoolean } from "@hexaijs/core";
17
+ * import { postgres } from "@hexaijs/postgres";
18
+ *
19
+ * export const getConfig = defineConfig({
20
+ * db: postgres("ORDER_DB"),
21
+ * apiKey: env("API_KEY"),
22
+ * debug: envBoolean("DEBUG", false),
23
+ * // Primitive values are also supported
24
+ * appName: "my-app",
25
+ * maxRetries: 3,
26
+ * });
27
+ *
28
+ * // Usage
29
+ * const config = getConfig();
30
+ * config.db.host; // PostgresConfig property
31
+ * config.apiKey; // string
32
+ * config.debug; // boolean
33
+ * config.appName; // "my-app" (literal type)
34
+ * config.maxRetries; // 3 (literal type)
35
+ * ```
36
+ */
37
+ function defineConfig(schema, options) {
38
+ let instance = null;
39
+ return () => {
40
+ // In test environment, always recreate to reflect env changes
41
+ if (instance && process.env.NODE_ENV !== "test") {
42
+ return instance;
43
+ }
44
+ // Load env files if requested
45
+ if (options?.loadEnv) {
46
+ if (options.envLoader) {
47
+ options.envLoader();
48
+ }
49
+ else {
50
+ (0, load_env_files_1.loadEnvFiles)();
51
+ }
52
+ }
53
+ const config = {};
54
+ const errors = [];
55
+ for (const [key, value] of Object.entries(schema)) {
56
+ if ((0, config_spec_1.isConfigSpec)(value)) {
57
+ config[key] = value.resolve(errors);
58
+ }
59
+ else {
60
+ config[key] = value;
61
+ }
62
+ }
63
+ if (errors.length > 0) {
64
+ throw new errors_1.ConfigValidationError(errors);
65
+ }
66
+ instance = config;
67
+ return instance;
68
+ };
69
+ }
70
+ //# sourceMappingURL=define-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-config.js","sourceRoot":"","sources":["../../src/config/define-config.ts"],"names":[],"mappings":";;AAgDA,oCAuCC;AAvFD,+CAAmE;AACnE,qCAAiD;AACjD,qDAAgD;AAgBhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,SAAgB,YAAY,CACxB,MAAS,EACT,OAAuB;IAEvB,IAAI,QAAQ,GAA8B,IAAI,CAAC;IAE/C,OAAO,GAAG,EAAE;QACR,8DAA8D;QAC9D,IAAI,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;YAC9C,OAAO,QAAQ,CAAC;QACpB,CAAC;QAED,8BAA8B;QAC9B,IAAI,OAAO,EAAE,OAAO,EAAE,CAAC;YACnB,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;gBACpB,OAAO,CAAC,SAAS,EAAE,CAAC;YACxB,CAAC;iBAAM,CAAC;gBACJ,IAAA,6BAAY,GAAE,CAAC;YACnB,CAAC;QACL,CAAC;QAED,MAAM,MAAM,GAAG,EAA6B,CAAC;QAC7C,MAAM,MAAM,GAAa,EAAE,CAAC;QAE5B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAChD,IAAI,IAAA,0BAAY,EAAC,KAAK,CAAC,EAAE,CAAC;gBACtB,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACxC,CAAC;iBAAM,CAAC;gBACJ,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YACxB,CAAC;QACL,CAAC;QAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,IAAI,8BAAqB,CAAC,MAAM,CAAC,CAAC;QAC5C,CAAC;QAED,QAAQ,GAAG,MAA4B,CAAC;QACxC,OAAO,QAAQ,CAAC;IACpB,CAAC,CAAC;AACN,CAAC"}
@@ -0,0 +1,78 @@
1
+ import type { ConfigSpec } from "./config-spec";
2
+ /**
3
+ * Config spec for environment variables.
4
+ * Use the builder functions (env, envOptional, envNumber, etc.) instead of instantiating directly.
5
+ */
6
+ export declare class EnvSpec<T> implements ConfigSpec<T> {
7
+ private readonly envKey;
8
+ private readonly required;
9
+ private readonly defaultValue?;
10
+ private readonly transform?;
11
+ readonly _type = "env";
12
+ constructor(envKey: string, required: boolean, defaultValue?: T | undefined, transform?: ((value: string) => T) | undefined);
13
+ resolve(errors: string[]): T | undefined;
14
+ }
15
+ /**
16
+ * Required string environment variable.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const getConfig = defineConfig({
21
+ * apiKey: env("API_KEY"),
22
+ * });
23
+ * ```
24
+ */
25
+ export declare function env(key: string): EnvSpec<string>;
26
+ /**
27
+ * Optional string environment variable with default value.
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * const getConfig = defineConfig({
32
+ * logLevel: envOptional("LOG_LEVEL", "info"),
33
+ * });
34
+ * ```
35
+ */
36
+ export declare function envOptional(key: string, defaultValue?: string): EnvSpec<string | undefined>;
37
+ /**
38
+ * Required number environment variable.
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * const getConfig = defineConfig({
43
+ * port: envNumber("PORT"),
44
+ * });
45
+ * ```
46
+ */
47
+ export declare function envNumber(key: string): EnvSpec<number>;
48
+ /**
49
+ * Optional number environment variable with default value.
50
+ */
51
+ export declare function envNumberOptional(key: string, defaultValue?: number): EnvSpec<number | undefined>;
52
+ /**
53
+ * Boolean environment variable. Recognizes "true", "1" as true.
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * const getConfig = defineConfig({
58
+ * debug: envBoolean("DEBUG", false),
59
+ * });
60
+ * ```
61
+ */
62
+ export declare function envBoolean(key: string, defaultValue?: boolean): EnvSpec<boolean>;
63
+ /**
64
+ * JSON environment variable. Parses JSON string into object.
65
+ *
66
+ * @example
67
+ * ```typescript
68
+ * const getConfig = defineConfig({
69
+ * prompts: envJson<{ system: string }>("PROMPTS"),
70
+ * });
71
+ * ```
72
+ */
73
+ export declare function envJson<T>(key: string): EnvSpec<T>;
74
+ /**
75
+ * Optional JSON environment variable with default value.
76
+ */
77
+ export declare function envJsonOptional<T>(key: string, defaultValue?: T): EnvSpec<T | undefined>;
78
+ //# sourceMappingURL=env-spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-spec.d.ts","sourceRoot":"","sources":["../../src/config/env-spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD;;;GAGG;AACH,qBAAa,OAAO,CAAC,CAAC,CAAE,YAAW,UAAU,CAAC,CAAC,CAAC;IAIxC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;IAN/B,QAAQ,CAAC,KAAK,SAAS;gBAGF,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,OAAO,EACjB,YAAY,CAAC,EAAE,CAAC,YAAA,EAChB,SAAS,CAAC,GAAE,CAAC,KAAK,EAAE,MAAM,KAAK,CAAC,aAAA;IAGrD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS;CAmB3C;AAED;;;;;;;;;GASG;AACH,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEhD;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAE3F;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEtD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAEjG;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,UAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAE9E;AAED;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAElD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAExF"}
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EnvSpec = void 0;
4
+ exports.env = env;
5
+ exports.envOptional = envOptional;
6
+ exports.envNumber = envNumber;
7
+ exports.envNumberOptional = envNumberOptional;
8
+ exports.envBoolean = envBoolean;
9
+ exports.envJson = envJson;
10
+ exports.envJsonOptional = envJsonOptional;
11
+ /**
12
+ * Config spec for environment variables.
13
+ * Use the builder functions (env, envOptional, envNumber, etc.) instead of instantiating directly.
14
+ */
15
+ class EnvSpec {
16
+ envKey;
17
+ required;
18
+ defaultValue;
19
+ transform;
20
+ _type = "env";
21
+ constructor(envKey, required, defaultValue, transform) {
22
+ this.envKey = envKey;
23
+ this.required = required;
24
+ this.defaultValue = defaultValue;
25
+ this.transform = transform;
26
+ }
27
+ resolve(errors) {
28
+ const rawValue = process.env[this.envKey];
29
+ if (!rawValue && this.required) {
30
+ errors.push(`Missing required env: ${this.envKey}`);
31
+ return undefined;
32
+ }
33
+ if (rawValue !== undefined) {
34
+ try {
35
+ return this.transform ? this.transform(rawValue) : rawValue;
36
+ }
37
+ catch (e) {
38
+ errors.push(`Failed to transform ${this.envKey}: ${e.message}`);
39
+ return undefined;
40
+ }
41
+ }
42
+ return this.defaultValue;
43
+ }
44
+ }
45
+ exports.EnvSpec = EnvSpec;
46
+ /**
47
+ * Required string environment variable.
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * const getConfig = defineConfig({
52
+ * apiKey: env("API_KEY"),
53
+ * });
54
+ * ```
55
+ */
56
+ function env(key) {
57
+ return new EnvSpec(key, true);
58
+ }
59
+ /**
60
+ * Optional string environment variable with default value.
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * const getConfig = defineConfig({
65
+ * logLevel: envOptional("LOG_LEVEL", "info"),
66
+ * });
67
+ * ```
68
+ */
69
+ function envOptional(key, defaultValue) {
70
+ return new EnvSpec(key, false, defaultValue);
71
+ }
72
+ /**
73
+ * Required number environment variable.
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * const getConfig = defineConfig({
78
+ * port: envNumber("PORT"),
79
+ * });
80
+ * ```
81
+ */
82
+ function envNumber(key) {
83
+ return new EnvSpec(key, true, undefined, Number);
84
+ }
85
+ /**
86
+ * Optional number environment variable with default value.
87
+ */
88
+ function envNumberOptional(key, defaultValue) {
89
+ return new EnvSpec(key, false, defaultValue, (v) => (v ? Number(v) : undefined));
90
+ }
91
+ /**
92
+ * Boolean environment variable. Recognizes "true", "1" as true.
93
+ *
94
+ * @example
95
+ * ```typescript
96
+ * const getConfig = defineConfig({
97
+ * debug: envBoolean("DEBUG", false),
98
+ * });
99
+ * ```
100
+ */
101
+ function envBoolean(key, defaultValue = false) {
102
+ return new EnvSpec(key, false, defaultValue, (v) => v === "true" || v === "1");
103
+ }
104
+ /**
105
+ * JSON environment variable. Parses JSON string into object.
106
+ *
107
+ * @example
108
+ * ```typescript
109
+ * const getConfig = defineConfig({
110
+ * prompts: envJson<{ system: string }>("PROMPTS"),
111
+ * });
112
+ * ```
113
+ */
114
+ function envJson(key) {
115
+ return new EnvSpec(key, true, undefined, JSON.parse);
116
+ }
117
+ /**
118
+ * Optional JSON environment variable with default value.
119
+ */
120
+ function envJsonOptional(key, defaultValue) {
121
+ return new EnvSpec(key, false, defaultValue, (v) => (v ? JSON.parse(v) : undefined));
122
+ }
123
+ //# sourceMappingURL=env-spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-spec.js","sourceRoot":"","sources":["../../src/config/env-spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=env-spec.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-spec.spec.d.ts","sourceRoot":"","sources":["../../src/config/env-spec.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,184 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const env_spec_1 = require("./env-spec");
5
+ const define_config_1 = require("./define-config");
6
+ const errors_1 = require("./errors");
7
+ (0, vitest_1.describe)("EnvSpec", () => {
8
+ const originalEnv = process.env;
9
+ (0, vitest_1.beforeEach)(() => {
10
+ process.env = { ...originalEnv, NODE_ENV: "test" };
11
+ });
12
+ (0, vitest_1.afterEach)(() => {
13
+ process.env = originalEnv;
14
+ });
15
+ (0, vitest_1.describe)("env()", () => {
16
+ (0, vitest_1.it)("should resolve required string env var", () => {
17
+ process.env.API_KEY = "secret-key";
18
+ const getConfig = (0, define_config_1.defineConfig)({ apiKey: (0, env_spec_1.env)("API_KEY") });
19
+ (0, vitest_1.expect)(getConfig().apiKey).toBe("secret-key");
20
+ });
21
+ (0, vitest_1.it)("should throw ConfigValidationError for missing required env", () => {
22
+ const getConfig = (0, define_config_1.defineConfig)({ apiKey: (0, env_spec_1.env)("MISSING_KEY") });
23
+ (0, vitest_1.expect)(() => getConfig()).toThrow(errors_1.ConfigValidationError);
24
+ (0, vitest_1.expect)(() => getConfig()).toThrow("Missing required env: MISSING_KEY");
25
+ });
26
+ (0, vitest_1.it)("should collect all missing env vars in error", () => {
27
+ const getConfig = (0, define_config_1.defineConfig)({
28
+ key1: (0, env_spec_1.env)("MISSING_1"),
29
+ key2: (0, env_spec_1.env)("MISSING_2"),
30
+ key3: (0, env_spec_1.env)("MISSING_3"),
31
+ });
32
+ try {
33
+ getConfig();
34
+ vitest_1.expect.fail("Should have thrown");
35
+ }
36
+ catch (e) {
37
+ (0, vitest_1.expect)(e).toBeInstanceOf(errors_1.ConfigValidationError);
38
+ const error = e;
39
+ (0, vitest_1.expect)(error.errors).toHaveLength(3);
40
+ (0, vitest_1.expect)(error.errors).toContain("Missing required env: MISSING_1");
41
+ (0, vitest_1.expect)(error.errors).toContain("Missing required env: MISSING_2");
42
+ (0, vitest_1.expect)(error.errors).toContain("Missing required env: MISSING_3");
43
+ }
44
+ });
45
+ });
46
+ (0, vitest_1.describe)("envOptional()", () => {
47
+ (0, vitest_1.it)("should resolve optional env var", () => {
48
+ process.env.LOG_LEVEL = "debug";
49
+ const getConfig = (0, define_config_1.defineConfig)({ logLevel: (0, env_spec_1.envOptional)("LOG_LEVEL") });
50
+ (0, vitest_1.expect)(getConfig().logLevel).toBe("debug");
51
+ });
52
+ (0, vitest_1.it)("should use default value when env is not set", () => {
53
+ const getConfig = (0, define_config_1.defineConfig)({ logLevel: (0, env_spec_1.envOptional)("LOG_LEVEL", "info") });
54
+ (0, vitest_1.expect)(getConfig().logLevel).toBe("info");
55
+ });
56
+ (0, vitest_1.it)("should return undefined when no default and env not set", () => {
57
+ const getConfig = (0, define_config_1.defineConfig)({ logLevel: (0, env_spec_1.envOptional)("LOG_LEVEL") });
58
+ (0, vitest_1.expect)(getConfig().logLevel).toBeUndefined();
59
+ });
60
+ });
61
+ (0, vitest_1.describe)("envNumber()", () => {
62
+ (0, vitest_1.it)("should resolve and transform to number", () => {
63
+ process.env.PORT = "3000";
64
+ const getConfig = (0, define_config_1.defineConfig)({ port: (0, env_spec_1.envNumber)("PORT") });
65
+ (0, vitest_1.expect)(getConfig().port).toBe(3000);
66
+ (0, vitest_1.expect)(typeof getConfig().port).toBe("number");
67
+ });
68
+ (0, vitest_1.it)("should throw for missing required number env", () => {
69
+ const getConfig = (0, define_config_1.defineConfig)({ port: (0, env_spec_1.envNumber)("PORT") });
70
+ (0, vitest_1.expect)(() => getConfig()).toThrow("Missing required env: PORT");
71
+ });
72
+ });
73
+ (0, vitest_1.describe)("envNumberOptional()", () => {
74
+ (0, vitest_1.it)("should resolve optional number with default", () => {
75
+ const getConfig = (0, define_config_1.defineConfig)({ timeout: (0, env_spec_1.envNumberOptional)("TIMEOUT", 5000) });
76
+ (0, vitest_1.expect)(getConfig().timeout).toBe(5000);
77
+ });
78
+ (0, vitest_1.it)("should parse env value when set", () => {
79
+ process.env.TIMEOUT = "10000";
80
+ const getConfig = (0, define_config_1.defineConfig)({ timeout: (0, env_spec_1.envNumberOptional)("TIMEOUT", 5000) });
81
+ (0, vitest_1.expect)(getConfig().timeout).toBe(10000);
82
+ });
83
+ });
84
+ (0, vitest_1.describe)("envBoolean()", () => {
85
+ (0, vitest_1.it)("should parse 'true' as true", () => {
86
+ process.env.DEBUG = "true";
87
+ const getConfig = (0, define_config_1.defineConfig)({ debug: (0, env_spec_1.envBoolean)("DEBUG") });
88
+ (0, vitest_1.expect)(getConfig().debug).toBe(true);
89
+ });
90
+ (0, vitest_1.it)("should parse '1' as true", () => {
91
+ process.env.DEBUG = "1";
92
+ const getConfig = (0, define_config_1.defineConfig)({ debug: (0, env_spec_1.envBoolean)("DEBUG") });
93
+ (0, vitest_1.expect)(getConfig().debug).toBe(true);
94
+ });
95
+ (0, vitest_1.it)("should parse other values as false", () => {
96
+ process.env.DEBUG = "false";
97
+ const getConfig = (0, define_config_1.defineConfig)({ debug: (0, env_spec_1.envBoolean)("DEBUG") });
98
+ (0, vitest_1.expect)(getConfig().debug).toBe(false);
99
+ });
100
+ (0, vitest_1.it)("should use default value when not set", () => {
101
+ const getConfig = (0, define_config_1.defineConfig)({ debug: (0, env_spec_1.envBoolean)("DEBUG", true) });
102
+ (0, vitest_1.expect)(getConfig().debug).toBe(true);
103
+ });
104
+ });
105
+ (0, vitest_1.describe)("envJson()", () => {
106
+ (0, vitest_1.it)("should parse JSON env var", () => {
107
+ process.env.CONFIG = JSON.stringify({ key: "value", num: 42 });
108
+ const getConfig = (0, define_config_1.defineConfig)({
109
+ config: (0, env_spec_1.envJson)("CONFIG"),
110
+ });
111
+ (0, vitest_1.expect)(getConfig().config).toEqual({ key: "value", num: 42 });
112
+ });
113
+ (0, vitest_1.it)("should throw for invalid JSON", () => {
114
+ process.env.CONFIG = "not-valid-json";
115
+ const getConfig = (0, define_config_1.defineConfig)({ config: (0, env_spec_1.envJson)("CONFIG") });
116
+ (0, vitest_1.expect)(() => getConfig()).toThrow("Failed to transform CONFIG");
117
+ });
118
+ (0, vitest_1.it)("should throw for missing required JSON env", () => {
119
+ const getConfig = (0, define_config_1.defineConfig)({ config: (0, env_spec_1.envJson)("CONFIG") });
120
+ (0, vitest_1.expect)(() => getConfig()).toThrow("Missing required env: CONFIG");
121
+ });
122
+ });
123
+ (0, vitest_1.describe)("envJsonOptional()", () => {
124
+ (0, vitest_1.it)("should return default when not set", () => {
125
+ const defaultConfig = { level: "info" };
126
+ const getConfig = (0, define_config_1.defineConfig)({ config: (0, env_spec_1.envJsonOptional)("CONFIG", defaultConfig) });
127
+ (0, vitest_1.expect)(getConfig().config).toEqual(defaultConfig);
128
+ });
129
+ (0, vitest_1.it)("should parse JSON when set", () => {
130
+ process.env.CONFIG = JSON.stringify({ level: "debug" });
131
+ const getConfig = (0, define_config_1.defineConfig)({ config: (0, env_spec_1.envJsonOptional)("CONFIG", { level: "info" }) });
132
+ (0, vitest_1.expect)(getConfig().config).toEqual({ level: "debug" });
133
+ });
134
+ });
135
+ (0, vitest_1.describe)("singleton behavior", () => {
136
+ (0, vitest_1.it)("should return same instance in non-test environment", () => {
137
+ process.env.NODE_ENV = "production";
138
+ process.env.API_KEY = "key1";
139
+ const getConfig = (0, define_config_1.defineConfig)({ apiKey: (0, env_spec_1.env)("API_KEY") });
140
+ const config1 = getConfig();
141
+ process.env.API_KEY = "key2";
142
+ const config2 = getConfig();
143
+ (0, vitest_1.expect)(config1).toBe(config2);
144
+ (0, vitest_1.expect)(config1.apiKey).toBe("key1");
145
+ });
146
+ (0, vitest_1.it)("should recreate config in test environment", () => {
147
+ process.env.NODE_ENV = "test";
148
+ process.env.API_KEY = "key1";
149
+ const getConfig = (0, define_config_1.defineConfig)({ apiKey: (0, env_spec_1.env)("API_KEY") });
150
+ const config1 = getConfig();
151
+ (0, vitest_1.expect)(config1.apiKey).toBe("key1");
152
+ process.env.API_KEY = "key2";
153
+ const config2 = getConfig();
154
+ (0, vitest_1.expect)(config2.apiKey).toBe("key2");
155
+ });
156
+ });
157
+ (0, vitest_1.describe)("type inference", () => {
158
+ (0, vitest_1.it)("should infer correct types", () => {
159
+ process.env.API_KEY = "secret";
160
+ process.env.PORT = "3000";
161
+ process.env.DEBUG = "true";
162
+ process.env.CONFIG = "{}";
163
+ const getConfig = (0, define_config_1.defineConfig)({
164
+ apiKey: (0, env_spec_1.env)("API_KEY"),
165
+ port: (0, env_spec_1.envNumber)("PORT"),
166
+ debug: (0, env_spec_1.envBoolean)("DEBUG"),
167
+ optional: (0, env_spec_1.envOptional)("OPTIONAL"),
168
+ json: (0, env_spec_1.envJson)("CONFIG"),
169
+ });
170
+ const config = getConfig();
171
+ // Type assertions - these would fail at compile time if types are wrong
172
+ const _apiKey = config.apiKey;
173
+ const _port = config.port;
174
+ const _debug = config.debug;
175
+ const _optional = config.optional;
176
+ const _json = config.json;
177
+ (0, vitest_1.expect)(_apiKey).toBeDefined();
178
+ (0, vitest_1.expect)(_port).toBeDefined();
179
+ (0, vitest_1.expect)(_debug).toBeDefined();
180
+ (0, vitest_1.expect)(_json).toBeDefined();
181
+ });
182
+ });
183
+ });
184
+ //# sourceMappingURL=env-spec.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-spec.spec.js","sourceRoot":"","sources":["../../src/config/env-spec.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Error thrown when config validation fails.
3
+ * Contains all validation errors collected during config resolution.
4
+ */
5
+ export declare class ConfigValidationError extends Error {
6
+ readonly errors: string[];
7
+ constructor(errors: string[]);
8
+ }
9
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/config/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;aAChB,MAAM,EAAE,MAAM,EAAE;gBAAhB,MAAM,EAAE,MAAM,EAAE;CAI/C"}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConfigValidationError = void 0;
4
+ /**
5
+ * Error thrown when config validation fails.
6
+ * Contains all validation errors collected during config resolution.
7
+ */
8
+ class ConfigValidationError extends Error {
9
+ errors;
10
+ constructor(errors) {
11
+ super(`Config validation failed:\n - ${errors.join("\n - ")}`);
12
+ this.errors = errors;
13
+ this.name = "ConfigValidationError";
14
+ }
15
+ }
16
+ exports.ConfigValidationError = ConfigValidationError;
17
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/config/errors.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,MAAa,qBAAsB,SAAQ,KAAK;IAChB;IAA5B,YAA4B,MAAgB;QACxC,KAAK,CAAC,kCAAkC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QADzC,WAAM,GAAN,MAAM,CAAU;QAExC,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACxC,CAAC;CACJ;AALD,sDAKC"}
@@ -0,0 +1,7 @@
1
+ export { type ConfigSpec, type InferSpecType, type InferConfigType, isConfigSpec, } from "./config-spec";
2
+ export { ConfigValidationError } from "./errors";
3
+ export { defineConfig, type ConfigOptions } from "./define-config";
4
+ export * from "./load-env-files";
5
+ export { EnvSpec, env, envOptional, envNumber, envNumberOptional, envBoolean, envJson, envJsonOptional, } from "./env-spec";
6
+ export { DatabaseConfig } from "./database-config";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,YAAY,GACf,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAEjD,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACnE,cAAc,kBAAkB,CAAC;AAEjC,OAAO,EACH,OAAO,EACP,GAAG,EACH,WAAW,EACX,SAAS,EACT,iBAAiB,EACjB,UAAU,EACV,OAAO,EACP,eAAe,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.envJsonOptional = exports.envJson = exports.envBoolean = exports.envNumberOptional = exports.envNumber = exports.envOptional = exports.env = exports.EnvSpec = exports.defineConfig = exports.ConfigValidationError = exports.isConfigSpec = void 0;
18
+ var config_spec_1 = require("./config-spec");
19
+ Object.defineProperty(exports, "isConfigSpec", { enumerable: true, get: function () { return config_spec_1.isConfigSpec; } });
20
+ var errors_1 = require("./errors");
21
+ Object.defineProperty(exports, "ConfigValidationError", { enumerable: true, get: function () { return errors_1.ConfigValidationError; } });
22
+ var define_config_1 = require("./define-config");
23
+ Object.defineProperty(exports, "defineConfig", { enumerable: true, get: function () { return define_config_1.defineConfig; } });
24
+ __exportStar(require("./load-env-files"), exports);
25
+ var env_spec_1 = require("./env-spec");
26
+ Object.defineProperty(exports, "EnvSpec", { enumerable: true, get: function () { return env_spec_1.EnvSpec; } });
27
+ Object.defineProperty(exports, "env", { enumerable: true, get: function () { return env_spec_1.env; } });
28
+ Object.defineProperty(exports, "envOptional", { enumerable: true, get: function () { return env_spec_1.envOptional; } });
29
+ Object.defineProperty(exports, "envNumber", { enumerable: true, get: function () { return env_spec_1.envNumber; } });
30
+ Object.defineProperty(exports, "envNumberOptional", { enumerable: true, get: function () { return env_spec_1.envNumberOptional; } });
31
+ Object.defineProperty(exports, "envBoolean", { enumerable: true, get: function () { return env_spec_1.envBoolean; } });
32
+ Object.defineProperty(exports, "envJson", { enumerable: true, get: function () { return env_spec_1.envJson; } });
33
+ Object.defineProperty(exports, "envJsonOptional", { enumerable: true, get: function () { return env_spec_1.envJsonOptional; } });
34
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,6CAKuB;AADnB,2GAAA,YAAY,OAAA;AAGhB,mCAAiD;AAAxC,+GAAA,qBAAqB,OAAA;AAE9B,iDAAmE;AAA1D,6GAAA,YAAY,OAAA;AACrB,mDAAiC;AAEjC,uCASoB;AARhB,mGAAA,OAAO,OAAA;AACP,+FAAA,GAAG,OAAA;AACH,uGAAA,WAAW,OAAA;AACX,qGAAA,SAAS,OAAA;AACT,6GAAA,iBAAiB,OAAA;AACjB,sGAAA,UAAU,OAAA;AACV,mGAAA,OAAO,OAAA;AACP,2GAAA,eAAe,OAAA"}
@@ -0,0 +1,5 @@
1
+ export declare function loadEnvFiles({ basePath, nodeEnv, }?: {
2
+ basePath?: string;
3
+ nodeEnv?: string;
4
+ }): void;
5
+ //# sourceMappingURL=load-env-files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-env-files.d.ts","sourceRoot":"","sources":["../../src/config/load-env-files.ts"],"names":[],"mappings":"AAKA,wBAAgB,YAAY,CAAC,EACzB,QAAQ,EACR,OAAO,GACV,GAAE;IACC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,IAAI,CA0BZ"}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loadEnvFiles = loadEnvFiles;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const dotenv_1 = __importDefault(require("dotenv"));
10
+ function loadEnvFiles({ basePath, nodeEnv, } = {}) {
11
+ const basePath_ = basePath ?? process.cwd();
12
+ const nodeEnv_ = nodeEnv ?? process.env.NODE_ENV ?? "development";
13
+ // List of environment files in the order of precedence
14
+ const envFiles = [
15
+ ".env", // Default
16
+ ".env.local", // Local overrides
17
+ `.env.${nodeEnv_}`, // Environment-specific settings
18
+ `.env.${nodeEnv_}.local`, // Local overrides of environment-specific settings
19
+ ];
20
+ // Function to load a single env file
21
+ const loadEnvFile = (filePath) => {
22
+ if (node_fs_1.default.existsSync(filePath)) {
23
+ dotenv_1.default.config({
24
+ path: filePath,
25
+ });
26
+ }
27
+ };
28
+ // Load env files in order of precedence
29
+ envFiles.reverse().forEach((file) => {
30
+ const filePath = node_path_1.default.resolve(basePath_, file);
31
+ loadEnvFile(filePath);
32
+ });
33
+ }
34
+ //# sourceMappingURL=load-env-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-env-files.js","sourceRoot":"","sources":["../../src/config/load-env-files.ts"],"names":[],"mappings":";;;;;AAKA,oCAgCC;AArCD,sDAAyB;AACzB,0DAA6B;AAE7B,oDAA4B;AAE5B,SAAgB,YAAY,CAAC,EACzB,QAAQ,EACR,OAAO,MAIP,EAAE;IACF,MAAM,SAAS,GAAG,QAAQ,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAC5C,MAAM,QAAQ,GAAG,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa,CAAC;IAElE,uDAAuD;IACvD,MAAM,QAAQ,GAAG;QACb,MAAM,EAAE,UAAU;QAClB,YAAY,EAAE,kBAAkB;QAChC,QAAQ,QAAQ,EAAE,EAAE,gCAAgC;QACpD,QAAQ,QAAQ,QAAQ,EAAE,mDAAmD;KAChF,CAAC;IAEF,qCAAqC;IACrC,MAAM,WAAW,GAAG,CAAC,QAAgB,EAAE,EAAE;QACrC,IAAI,iBAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,gBAAM,CAAC,MAAM,CAAC;gBACV,IAAI,EAAE,QAAQ;aACjB,CAAC,CAAC;QACP,CAAC;IACL,CAAC,CAAC;IAEF,wCAAwC;IACxC,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAChC,MAAM,QAAQ,GAAG,mBAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QAC/C,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACP,CAAC"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@hexaijs/utils",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "version": "0.1.0",
7
+ "description": "A collection of utility functions and value objects for hexai projects",
8
+ "license": "MIT",
9
+ "author": "Sangwoo Hyun <wkdny.hyun@gmail.com>",
10
+ "contributors": [
11
+ "Seungcheol Baek <victoryiron.baek@gmail.com>",
12
+ "Donghyun Lee <edonghyun@naver.com>"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/workingdanny911/hexai.git",
17
+ "directory": "packages/utils"
18
+ },
19
+ "homepage": "https://github.com/workingdanny911/hexai#readme",
20
+ "bugs": {
21
+ "url": "https://github.com/workingdanny911/hexai/issues"
22
+ },
23
+ "keywords": [
24
+ "hexai",
25
+ "hexagonal",
26
+ "clean-architecture",
27
+ "utilities",
28
+ "typescript"
29
+ ],
30
+ "files": [
31
+ "dist",
32
+ "LICENSE"
33
+ ],
34
+ "exports": {
35
+ "./config": {
36
+ "types": "./dist/config/index.d.ts",
37
+ "import": "./dist/config/index.js",
38
+ "require": "./dist/config/index.js"
39
+ }
40
+ },
41
+ "dependencies": {},
42
+ "devDependencies": {},
43
+ "scripts": {
44
+ "test": "vitest run",
45
+ "build": "tsc && tsc-alias"
46
+ }
47
+ }