@clipboard-health/config 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.
- package/README.md +96 -0
- package/package.json +26 -0
- package/src/index.d.ts +2 -0
- package/src/index.js +6 -0
- package/src/index.js.map +1 -0
- package/src/lib/config.d.ts +40 -0
- package/src/lib/config.js +54 -0
- package/src/lib/config.js.map +1 -0
- package/src/lib/internal/deepFreeze.d.ts +9 -0
- package/src/lib/internal/deepFreeze.js +27 -0
- package/src/lib/internal/deepFreeze.js.map +1 -0
- package/src/lib/internal/isDefined.d.ts +4 -0
- package/src/lib/internal/isDefined.js +10 -0
- package/src/lib/internal/isDefined.js.map +1 -0
- package/src/lib/internal/resolver.d.ts +10 -0
- package/src/lib/internal/resolver.js +51 -0
- package/src/lib/internal/resolver.js.map +1 -0
- package/src/lib/types.d.ts +44 -0
- package/src/lib/types.js +3 -0
- package/src/lib/types.js.map +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# @clipboard-health/config <!-- omit from toc -->
|
|
2
|
+
|
|
3
|
+
Type-safe static configuration management: a pure function to resolve, validate against a Zod schema, and freeze configuration values.
|
|
4
|
+
|
|
5
|
+
## Table of contents <!-- omit from toc -->
|
|
6
|
+
|
|
7
|
+
- [Install](#install)
|
|
8
|
+
- [Usage](#usage)
|
|
9
|
+
- [Type-safe configuration](#type-safe-configuration)
|
|
10
|
+
- [Local development commands](#local-development-commands)
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @clipboard-health/config
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
### Type-safe configuration
|
|
21
|
+
|
|
22
|
+
For additional detail, see the `createConfig` documentation.
|
|
23
|
+
|
|
24
|
+
<!-- prettier-ignore -->
|
|
25
|
+
```ts
|
|
26
|
+
// ./examples/config.ts
|
|
27
|
+
|
|
28
|
+
import { ok } from "node:assert/strict";
|
|
29
|
+
|
|
30
|
+
import { createConfig } from "@clipboard-health/config";
|
|
31
|
+
import { z } from "zod";
|
|
32
|
+
|
|
33
|
+
const allowed = ["local", "development", "production"] as const;
|
|
34
|
+
type Allowed = (typeof allowed)[number];
|
|
35
|
+
|
|
36
|
+
function createEnvironmentConfig(current: Allowed) {
|
|
37
|
+
return createConfig({
|
|
38
|
+
config: {
|
|
39
|
+
baseUrl: {
|
|
40
|
+
defaultValue: "http://localhost:3000",
|
|
41
|
+
description: "Base URL for API requests",
|
|
42
|
+
overrides: {
|
|
43
|
+
development: "https://dev.example.com",
|
|
44
|
+
production: "https://api.example.com",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
database: {
|
|
48
|
+
port: {
|
|
49
|
+
defaultValue: 5432,
|
|
50
|
+
description: "Database port",
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
environment: { allowed, current },
|
|
55
|
+
schema: z.object({
|
|
56
|
+
baseUrl: z.string().url(),
|
|
57
|
+
database: z.object({
|
|
58
|
+
// Use `z.coerce` to override with environment variables.
|
|
59
|
+
port: z.coerce.number().min(1024).max(65_535),
|
|
60
|
+
}),
|
|
61
|
+
}),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
{
|
|
66
|
+
// Uses default values.
|
|
67
|
+
const config = createEnvironmentConfig("local");
|
|
68
|
+
ok(config.baseUrl === "http://localhost:3000");
|
|
69
|
+
ok(config.database.port === 5432);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
{
|
|
73
|
+
// Uses baseUrl environment override.
|
|
74
|
+
const config = createEnvironmentConfig("development");
|
|
75
|
+
ok(config.baseUrl === "https://dev.example.com");
|
|
76
|
+
ok(config.database.port === 5432);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Uses environment variable overrides.
|
|
80
|
+
const original = { ...process.env };
|
|
81
|
+
try {
|
|
82
|
+
process.env["BASE_URL"] = "https://staging.example.com";
|
|
83
|
+
process.env["DATABASE_PORT"] = "54320";
|
|
84
|
+
|
|
85
|
+
const config = createEnvironmentConfig("local");
|
|
86
|
+
ok(config.baseUrl === "https://staging.example.com");
|
|
87
|
+
ok(config.database.port === 54_320);
|
|
88
|
+
} finally {
|
|
89
|
+
process.env = { ...original };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Local development commands
|
|
95
|
+
|
|
96
|
+
See [`package.json`](./package.json) `scripts` for a list of commands.
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@clipboard-health/config",
|
|
3
|
+
"description": "Type-safe static configuration management: a pure function to resolve, validate against a Zod schema, and freeze configuration values.",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"decamelize": "5.0.1",
|
|
7
|
+
"tslib": "2.8.0",
|
|
8
|
+
"zod": "3.23.8",
|
|
9
|
+
"zod-validation-error": "3.4.0"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@clipboard-health/contract-core": "0.2.0"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"main": "./src/index.js",
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "restricted"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"embed": "embedme README.md"
|
|
22
|
+
},
|
|
23
|
+
"type": "commonjs",
|
|
24
|
+
"typings": "./src/index.d.ts",
|
|
25
|
+
"types": "./src/index.d.ts"
|
|
26
|
+
}
|
package/src/index.d.ts
ADDED
package/src/index.js
ADDED
package/src/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/config/src/index.ts"],"names":[],"mappings":";;;AAAA,uDAA6B;AAC7B,sDAA4B"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type ConfigParams } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Type-safe static configuration management: a pure function to resolve, validate against a Zod
|
|
4
|
+
* schema, and freeze configuration values.
|
|
5
|
+
*
|
|
6
|
+
* Configuration values resolve in order from highest precedence to lowest:
|
|
7
|
+
* 1. Environment variables
|
|
8
|
+
* - Resolved converting configuration path from camelCase to UPPER_SNAKE. For example, the `{
|
|
9
|
+
* myApi: { port: 3000 } }` configuration resolves to `MY_API_PORT`.
|
|
10
|
+
* 2. Environment-specific overrides, {@link ConfigValue.overrides}
|
|
11
|
+
* 3. Default values, {@link ConfigValue.defaultValue}
|
|
12
|
+
*
|
|
13
|
+
* Supported configuration value types:
|
|
14
|
+
* - bigint
|
|
15
|
+
* - boolean
|
|
16
|
+
* - date
|
|
17
|
+
* - number
|
|
18
|
+
* - string
|
|
19
|
+
* - arrays and nested objects using the above types
|
|
20
|
+
*
|
|
21
|
+
* To override arrays with environment variables, use stringified JSON arrays, e.g. `["a","b"]`.
|
|
22
|
+
*
|
|
23
|
+
* **IMPORTANT**: To avoid runtime errors:
|
|
24
|
+
* 1. Environment variables are strings, so use `z.coerce` Zod types for those you plan to override.
|
|
25
|
+
* Note that `z.coerce.boolean()` coerces any truthy value to `true`. To restrict to `"true" |
|
|
26
|
+
* "false"`, use the
|
|
27
|
+
* {@link https://github.com/ClipboardHealth/core-utils/blob/main/packages/contract-core/src/lib/schemas/booleanString.ts
|
|
28
|
+
* `booleanString` schema} from `@clipboard-health/contract-core`.
|
|
29
|
+
* 2. The resulting configuration is deeply frozen and will throw a runtime error if you attempt to
|
|
30
|
+
* modify it. The actual return type is `ReadonlyDeep<SchemaT>`, but the library returns a
|
|
31
|
+
* `Readonly<SchemaT>` because the former prevents clients from passing configuration values to
|
|
32
|
+
* functions that don't explicitly accept `readonly` types.
|
|
33
|
+
*
|
|
34
|
+
* @includeExample ./packages/config/examples/config.ts
|
|
35
|
+
* @see [Usage example](../../examples/config.ts)
|
|
36
|
+
*
|
|
37
|
+
* @throws {Error} When configuration values fail schema validation
|
|
38
|
+
* @returns A deeply frozen configuration object matching the provided schema
|
|
39
|
+
*/
|
|
40
|
+
export declare function createConfig<const SchemaT extends Record<string, unknown>, const EnvironmentT extends readonly string[]>(params: Readonly<ConfigParams<SchemaT, EnvironmentT>>): Readonly<SchemaT>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createConfig = createConfig;
|
|
4
|
+
const zod_validation_error_1 = require("zod-validation-error");
|
|
5
|
+
const deepFreeze_1 = require("./internal/deepFreeze");
|
|
6
|
+
const resolver_1 = require("./internal/resolver");
|
|
7
|
+
/**
|
|
8
|
+
* Type-safe static configuration management: a pure function to resolve, validate against a Zod
|
|
9
|
+
* schema, and freeze configuration values.
|
|
10
|
+
*
|
|
11
|
+
* Configuration values resolve in order from highest precedence to lowest:
|
|
12
|
+
* 1. Environment variables
|
|
13
|
+
* - Resolved converting configuration path from camelCase to UPPER_SNAKE. For example, the `{
|
|
14
|
+
* myApi: { port: 3000 } }` configuration resolves to `MY_API_PORT`.
|
|
15
|
+
* 2. Environment-specific overrides, {@link ConfigValue.overrides}
|
|
16
|
+
* 3. Default values, {@link ConfigValue.defaultValue}
|
|
17
|
+
*
|
|
18
|
+
* Supported configuration value types:
|
|
19
|
+
* - bigint
|
|
20
|
+
* - boolean
|
|
21
|
+
* - date
|
|
22
|
+
* - number
|
|
23
|
+
* - string
|
|
24
|
+
* - arrays and nested objects using the above types
|
|
25
|
+
*
|
|
26
|
+
* To override arrays with environment variables, use stringified JSON arrays, e.g. `["a","b"]`.
|
|
27
|
+
*
|
|
28
|
+
* **IMPORTANT**: To avoid runtime errors:
|
|
29
|
+
* 1. Environment variables are strings, so use `z.coerce` Zod types for those you plan to override.
|
|
30
|
+
* Note that `z.coerce.boolean()` coerces any truthy value to `true`. To restrict to `"true" |
|
|
31
|
+
* "false"`, use the
|
|
32
|
+
* {@link https://github.com/ClipboardHealth/core-utils/blob/main/packages/contract-core/src/lib/schemas/booleanString.ts
|
|
33
|
+
* `booleanString` schema} from `@clipboard-health/contract-core`.
|
|
34
|
+
* 2. The resulting configuration is deeply frozen and will throw a runtime error if you attempt to
|
|
35
|
+
* modify it. The actual return type is `ReadonlyDeep<SchemaT>`, but the library returns a
|
|
36
|
+
* `Readonly<SchemaT>` because the former prevents clients from passing configuration values to
|
|
37
|
+
* functions that don't explicitly accept `readonly` types.
|
|
38
|
+
*
|
|
39
|
+
* @includeExample ./packages/config/examples/config.ts
|
|
40
|
+
* @see [Usage example](../../examples/config.ts)
|
|
41
|
+
*
|
|
42
|
+
* @throws {Error} When configuration values fail schema validation
|
|
43
|
+
* @returns A deeply frozen configuration object matching the provided schema
|
|
44
|
+
*/
|
|
45
|
+
function createConfig(params) {
|
|
46
|
+
const { config, environment, schema } = params;
|
|
47
|
+
const { current } = environment;
|
|
48
|
+
const result = schema.safeParse((0, resolver_1.resolve)({ config, environment: current, path: [], schema }));
|
|
49
|
+
if (!result.success) {
|
|
50
|
+
throw new Error((0, zod_validation_error_1.fromZodError)(result.error).toString(), { cause: result.error });
|
|
51
|
+
}
|
|
52
|
+
return (0, deepFreeze_1.deepFreeze)(result.data);
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../../packages/config/src/lib/config.ts"],"names":[],"mappings":";;AA4CA,oCAaC;AAzDD,+DAAoD;AAEpD,sDAAmD;AACnD,kDAA8C;AAG9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,SAAgB,YAAY,CAG1B,MAAqD;IACrD,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAC/C,MAAM,EAAE,OAAO,EAAE,GAAG,WAAW,CAAC;IAEhC,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,IAAA,kBAAO,EAAC,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAC7F,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,IAAA,mCAAY,EAAC,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IAClF,CAAC;IAED,OAAO,IAAA,uBAAU,EAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recursively freezes an object and all its nested properties.
|
|
3
|
+
*
|
|
4
|
+
* @template T - Type of the object to freeze
|
|
5
|
+
* @param value - The object to freeze
|
|
6
|
+
* @param seen - Internal parameter to track circular references
|
|
7
|
+
* @returns A deeply frozen version of the input object.
|
|
8
|
+
*/
|
|
9
|
+
export declare function deepFreeze<T extends object>(value: T, seen?: WeakSet<WeakKey>): Readonly<T>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deepFreeze = deepFreeze;
|
|
4
|
+
/**
|
|
5
|
+
* Recursively freezes an object and all its nested properties.
|
|
6
|
+
*
|
|
7
|
+
* @template T - Type of the object to freeze
|
|
8
|
+
* @param value - The object to freeze
|
|
9
|
+
* @param seen - Internal parameter to track circular references
|
|
10
|
+
* @returns A deeply frozen version of the input object.
|
|
11
|
+
*/
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
13
|
+
function deepFreeze(value, seen = new WeakSet()) {
|
|
14
|
+
if (!value || typeof value !== "object" || seen.has(value)) {
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
seen.add(value);
|
|
18
|
+
Reflect.ownKeys(value).forEach((key) => {
|
|
19
|
+
const property = value[key];
|
|
20
|
+
if (property && typeof property === "object" && !Object.isFrozen(property)) {
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
22
|
+
deepFreeze(property, seen);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return Object.freeze(value);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=deepFreeze.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deepFreeze.js","sourceRoot":"","sources":["../../../../../../packages/config/src/lib/internal/deepFreeze.ts"],"names":[],"mappings":";;AASA,gCAeC;AAxBD;;;;;;;GAOG;AACH,wDAAwD;AACxD,SAAgB,UAAU,CAAmB,KAAQ,EAAE,IAAI,GAAG,IAAI,OAAO,EAAE;IACzE,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3D,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACf,OAAO,CAAC,OAAO,CAAC,KAAK,CAAoB,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;QACzD,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3E,wDAAwD;YACxD,UAAU,CAA2B,QAAQ,EAAE,IAAI,CAAC,CAAC;QACvD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isDefined = isDefined;
|
|
4
|
+
/**
|
|
5
|
+
* Type guard that checks if a value is neither null nor undefined.
|
|
6
|
+
*/
|
|
7
|
+
function isDefined(value) {
|
|
8
|
+
return value !== null && value !== undefined;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=isDefined.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isDefined.js","sourceRoot":"","sources":["../../../../../../packages/config/src/lib/internal/isDefined.ts"],"names":[],"mappings":";;AAGA,8BAEC;AALD;;GAEG;AACH,SAAgB,SAAS,CAAI,KAAQ;IACnC,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type ConfigValueMap } from "../types";
|
|
3
|
+
interface ResolveParams<SchemaT extends Record<string, unknown>> {
|
|
4
|
+
config: Readonly<ConfigValueMap<SchemaT, readonly string[]>>;
|
|
5
|
+
environment: string;
|
|
6
|
+
path: readonly string[];
|
|
7
|
+
schema: z.ZodType<SchemaT>;
|
|
8
|
+
}
|
|
9
|
+
export declare function resolve<SchemaT extends Record<string, unknown>>(params: Readonly<ResolveParams<SchemaT>>): Record<string, unknown>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolve = resolve;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const decamelize_1 = tslib_1.__importDefault(require("decamelize"));
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
const isDefined_1 = require("./isDefined");
|
|
8
|
+
function resolve(params) {
|
|
9
|
+
const { config, path, ...rest } = params;
|
|
10
|
+
return Object.fromEntries(Object.entries(config).map(([key, value]) => [
|
|
11
|
+
key,
|
|
12
|
+
isConfigValue(value)
|
|
13
|
+
? resolveConfigValue({ ...rest, path: [...path, key], value })
|
|
14
|
+
: resolve({ ...rest, config: value, path: [...path, key] }),
|
|
15
|
+
]));
|
|
16
|
+
}
|
|
17
|
+
function isConfigValue(value) {
|
|
18
|
+
return (typeof value === "object" &&
|
|
19
|
+
(0, isDefined_1.isDefined)(value) &&
|
|
20
|
+
"description" in value &&
|
|
21
|
+
"defaultValue" in value);
|
|
22
|
+
}
|
|
23
|
+
function resolveConfigValue(params) {
|
|
24
|
+
const { environment, path, schema, value } = params;
|
|
25
|
+
const variable = process.env[(0, decamelize_1.default)(path.join("_")).toUpperCase()];
|
|
26
|
+
if ((0, isDefined_1.isDefined)(variable)) {
|
|
27
|
+
return parseEnvironmentVariable(variable, getSchema(path, schema));
|
|
28
|
+
}
|
|
29
|
+
const override = value.overrides?.[environment];
|
|
30
|
+
if ((0, isDefined_1.isDefined)(override)) {
|
|
31
|
+
return override;
|
|
32
|
+
}
|
|
33
|
+
return value.defaultValue;
|
|
34
|
+
}
|
|
35
|
+
function parseEnvironmentVariable(value, schema) {
|
|
36
|
+
if (schema instanceof zod_1.z.ZodArray && typeof value === "string") {
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(value);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
function getSchema(path, schema) {
|
|
47
|
+
return path.reduce((result, key) =>
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
49
|
+
result instanceof zod_1.z.ZodObject ? result.shape[key] : /* istanbul ignore next */ result, schema);
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../../../../../packages/config/src/lib/internal/resolver.ts"],"names":[],"mappings":";;AAoBA,0BAeC;;AAnCD,oEAAoC;AACpC,6BAAwB;AAGxB,2CAAwC;AAgBxC,SAAgB,OAAO,CACrB,MAAwC;IAExC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;IAEzC,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CACxB,CAAC,CAAC,GAAG,EAAE,KAAK,CAAuD,EAAE,EAAE,CAAC;QACtE,GAAG;QACH,aAAa,CAAC,KAAK,CAAC;YAClB,CAAC,CAAC,kBAAkB,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC;YAC9D,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;KAC9D,CACF,CACF,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,IAAA,qBAAS,EAAC,KAAK,CAAC;QAChB,aAAa,IAAI,KAAK;QACtB,cAAc,IAAI,KAAK,CACxB,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CAAC,MAAgC;IAC1D,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;IAEpD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,IAAA,oBAAU,EAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IACvE,IAAI,IAAA,qBAAS,EAAC,QAAQ,CAAC,EAAE,CAAC;QACxB,OAAO,wBAAwB,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IACrE,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC,WAAW,CAAC,CAAC;IAChD,IAAI,IAAA,qBAAS,EAAC,QAAQ,CAAC,EAAE,CAAC;QACxB,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,OAAO,KAAK,CAAC,YAAY,CAAC;AAC5B,CAAC;AAED,SAAS,wBAAwB,CAAC,KAAc,EAAE,MAA0B;IAC1E,IAAI,MAAM,YAAY,OAAC,CAAC,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9D,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,SAAS,CAAC,IAAuB,EAAE,MAA0B;IACpE,OAAO,IAAI,CAAC,MAAM,CAChB,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;IACd,+DAA+D;IAC/D,MAAM,YAAY,OAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,0BAA0B,CAAC,MAAM,EACvF,MAAM,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Represents a single configuration value with metadata.
|
|
4
|
+
*/
|
|
5
|
+
export interface ConfigValue<SchemaT, EnvironmentT extends readonly string[]> {
|
|
6
|
+
/** Default value used when no override or environment variable is present */
|
|
7
|
+
defaultValue: SchemaT;
|
|
8
|
+
/** Human-readable description of the configuration value */
|
|
9
|
+
description: string;
|
|
10
|
+
/**
|
|
11
|
+
* Optional environment-specific overrides.
|
|
12
|
+
* @example { development: "dev-host", production: "prod-host" }
|
|
13
|
+
*/
|
|
14
|
+
overrides?: Readonly<Partial<Record<EnvironmentT[number], SchemaT>>>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Maps configuration schema to configuration values with metadata.
|
|
18
|
+
*/
|
|
19
|
+
export type ConfigValueMap<SchemaT, EnvironmentT extends readonly string[]> = {
|
|
20
|
+
[K in keyof SchemaT]: ConfigValueForType<SchemaT[K], EnvironmentT>;
|
|
21
|
+
};
|
|
22
|
+
type ConfigValueForType<T, EnvironmentT extends readonly string[]> = T extends unknown[] ? ConfigValue<T, EnvironmentT> : T extends Date ? ConfigValue<Date, EnvironmentT> : T extends bigint ? ConfigValue<bigint, EnvironmentT> : T extends z.EnumValues ? ConfigValue<T, EnvironmentT> : T extends Record<string, unknown> ? ConfigValueMap<T, EnvironmentT> : ConfigValue<T, EnvironmentT>;
|
|
23
|
+
/**
|
|
24
|
+
* Parameters for creating a configuration instance.
|
|
25
|
+
*/
|
|
26
|
+
export interface ConfigParams<SchemaT extends Record<string, unknown>, EnvironmentT extends readonly string[]> {
|
|
27
|
+
/**
|
|
28
|
+
* Configuration values resolved in order of precedence. @see {@link createConfig}.
|
|
29
|
+
*/
|
|
30
|
+
config: Readonly<ConfigValueMap<SchemaT, EnvironmentT>>;
|
|
31
|
+
/**
|
|
32
|
+
* The current environment and list of allowed environments.
|
|
33
|
+
* @example { current: "development", allowed: ["development", "production"] as const }
|
|
34
|
+
*/
|
|
35
|
+
environment: {
|
|
36
|
+
allowed: EnvironmentT;
|
|
37
|
+
current: EnvironmentT[number];
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Zod schema defining the configuration shape and validation rules.
|
|
41
|
+
*/
|
|
42
|
+
schema: z.ZodType<SchemaT>;
|
|
43
|
+
}
|
|
44
|
+
export {};
|
package/src/lib/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../packages/config/src/lib/types.ts"],"names":[],"mappings":""}
|