{"_id":"@emdzej/config-provider","name":"@emdzej/config-provider","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@emdzej/config-provider","version":"0.1.0","description":"Spring-style externalized configuration provider with resolver chain, schema validation, and type-safe property access","license":"MIT","repository":{"type":"git","url":"git+https://github.com/emdzej/config.git","directory":"packages/config-provider"},"keywords":["config","provider","spring","externalized","env","yaml","json-schema"],"main":"dist/index.js","types":"dist/index.d.ts","dependencies":{"js-yaml":"^4.1.0","@emdzej/config-resolver":"0.1.0"},"devDependencies":{"@types/js-yaml":"^4.0.9","@types/node":"^20.11.25","typescript":"^5.4.2","vitest":"^4.1.4"},"engines":{"node":">=22"},"scripts":{"build":"tsc","test":"vitest run","check-types":"tsc --noEmit"},"_id":"@emdzej/config-provider@0.1.0","bugs":{"url":"https://github.com/emdzej/config/issues"},"homepage":"https://github.com/emdzej/config#readme","_integrity":"sha512-NvoqYfFtnaHk1unhq+11zu2rxPKQPMQotvQvKQ+smt6y+075gx3ESdn8MkWsiO9VuQU/rVXxq74/7BCqyWnFSA==","_resolved":"/private/var/folders/7h/x_w_580x4s9dq3sq11tpvzkwy3nbj8/T/9a4e1b26cf00d9a51fbd96c5c3ab908b/emdzej-config-provider-0.1.0.tgz","_from":"file:emdzej-config-provider-0.1.0.tgz","_nodeVersion":"22.22.1","_npmVersion":"10.9.4","dist":{"integrity":"sha512-NvoqYfFtnaHk1unhq+11zu2rxPKQPMQotvQvKQ+smt6y+075gx3ESdn8MkWsiO9VuQU/rVXxq74/7BCqyWnFSA==","shasum":"12eff5580bc426d66bf11121a0c5041757e486b4","tarball":"https://registry.npmjs.org/@emdzej/config-provider/-/config-provider-0.1.0.tgz","fileCount":30,"unpackedSize":48633,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIGULbvXJr/GU7bTi8SvR6c69xJgTwu4MyVKXaAIgFdS7AiAo2vYEcQm4YHMoZBWcPvUgWC3A0WyB7nCZDr3dq2fHvA=="}]},"_npmUser":{"name":"emdzej","email":"michal@jaskolski.pro"},"directories":{},"maintainers":[{"name":"emdzej","email":"michal@jaskolski.pro"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/config-provider_0.1.0_1776342088210_0.7795331996364927"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-16T12:21:28.132Z","0.1.0":"2026-04-16T12:21:28.387Z","modified":"2026-04-16T12:21:28.577Z"},"maintainers":[{"name":"emdzej","email":"michal@jaskolski.pro"}],"description":"Spring-style externalized configuration provider with resolver chain, schema validation, and type-safe property access","homepage":"https://github.com/emdzej/config#readme","keywords":["config","provider","spring","externalized","env","yaml","json-schema"],"repository":{"type":"git","url":"git+https://github.com/emdzej/config.git","directory":"packages/config-provider"},"bugs":{"url":"https://github.com/emdzej/config/issues"},"license":"MIT","readme":"# @emdzej/config-provider\n\nSpring-style externalized configuration provider for Node.js. Resolves properties through an ordered chain of sources — in-memory overrides, environment variables, and config files (JSON/YAML) — with JSON Schema validation and placeholder resolution.\n\nBuilt on top of [`@emdzej/config-resolver`](../config-resolver).\n\n## Installation\n\n```bash\nnpm install @emdzej/config-provider\n```\n\n## Features\n\n- **Strongly typed** — all core functions are generic, preserving your config types through the resolver chain\n- **Resolver chain** — `memory > env > files` priority (matches Spring Boot)\n- **Dot-notation property access** — `getProperty(\"db.host\")` traverses nested objects\n- **In-memory overrides** — `setProperty()` writes to a transient overlay that wins over all other sources\n- **JSON & YAML config files** — loaded and merged in order, with `${PLACEHOLDER:default}` resolution\n- **Environment variable resolver** — relaxed binding (`DB_HOST` → `db.host`), type coercion, optional prefix\n- **JSON Schema validation** — `setProperty()` validates the full merged tree before accepting a change\n- **Schema composition** — pass multiple schemas and they're combined via `allOf`\n- **Hot reload** — `reload()` re-reads files from disk without restarting\n\n## Quick Start\n\n```typescript\nimport { ConfigProvider } from \"@emdzej/config-provider\";\n\ninterface AppConfig {\n  db: { host: string; port: number };\n  features: { darkMode: boolean };\n}\n\nconst provider = new ConfigProvider({\n  files: [\"./config/base.json\", \"./config/app.yaml\"],\n  env: process.env,\n  envPrefix: \"myapp\",            // only MYAPP_* env vars are considered\n  schema: {\n    type: \"object\",\n    properties: {\n      db: {\n        type: \"object\",\n        properties: {\n          host: { type: \"string\" },\n          port: { type: \"number\" },\n        },\n        required: [\"host\", \"port\"],\n      },\n    },\n  },\n});\n\n// Read properties — generic parameter provides strong typing\nconst host = provider.getProperty<string>(\"db.host\");       // string | undefined\nconst port = provider.getRequiredProperty<number>(\"db.port\"); // number (throws if missing)\n\n// Override at runtime (in-memory only, validated against schema)\nprovider.setProperty(\"db.host\", \"override-host\");\n\n// Get the full merged config tree — typed\nconst all = provider.getAll<AppConfig>();                    // AppConfig\nall.db.host;      // string ✓\nall.features;     // { darkMode: boolean } ✓\n\n// Clear all in-memory overrides\nprovider.resetOverrides();\n\n// Reload files from disk\nprovider.reload();\n```\n\n## API\n\n### `ConfigProvider`\n\n#### `constructor(options?: ConfigProviderOptions)`\n\nCreates a new provider. Options:\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `files` | `string[]` | — | Config file paths (JSON, YAML). Loaded in order; later files override earlier |\n| `env` | `Record<string, string \\| undefined>` | `process.env` | Environment variables map |\n| `envPrefix` | `string` | — | Only consider env vars with this prefix (e.g. `\"app\"` → `APP_DB_HOST` maps to `db.host`) |\n| `schema` | `object \\| object[] \\| ValidateFunction` | — | JSON Schema for validation. Array of schemas composed via `allOf` |\n| `resolvers` | `PropertyResolver[]` | — | Custom resolver chain (overrides default memory/env/file setup) |\n| `resolvePlaceholders` | `boolean` | `true` | Resolve `${...}` placeholders in file values |\n| `strict` | `boolean` | `true` | Throw on unresolved placeholders |\n\n#### `getProperty<T extends JsonValue = JsonValue>(key: string): T | undefined`\n\nReturns the value at the dot-notation key, resolved through the chain. Returns `undefined` if not found.\n\n#### `getRequiredProperty<T extends JsonValue = JsonValue>(key: string): T`\n\nLike `getProperty`, but throws `PropertyNotFoundError` if the key is missing.\n\n#### `setProperty<T extends JsonValue = JsonValue>(key: string, value: T): void`\n\nSets an in-memory override. If a schema is configured, validates the full merged tree with the new value before accepting it. Throws `PropertyValidationError` if validation fails.\n\n#### `resetOverrides(): void`\n\nClears all in-memory overrides. Subsequent reads fall back to env/file sources.\n\n#### `getAll<T extends Record<string, JsonValue> = Record<string, JsonValue>>(): T`\n\nReturns the fully merged config tree from all resolvers. The generic parameter lets you type the result as your config interface.\n\n#### `reload(): void`\n\nReloads file-based config sources from disk.\n\n### Resolvers\n\nThe default chain is `[MemoryResolver, EnvResolver, FileResolver]`. You can also construct them individually:\n\n```typescript\nimport { MemoryResolver, EnvResolver, FileResolver } from \"@emdzej/config-provider\";\n```\n\n#### `MemoryResolver`\n\nIn-memory key-value store. Highest priority in the default chain.\n\n#### `EnvResolver(env?, prefix?)`\n\nResolves from environment variables with relaxed binding and type coercion (booleans, numbers, null, JSON objects/arrays).\n\n#### `FileResolver(options)`\n\nLoads and merges JSON/YAML files with optional `${...}` placeholder resolution.\n\n### Schema Utilities\n\n```typescript\nimport { composeSchemas, prepareSchema } from \"@emdzej/config-provider\";\n\n// Combine multiple schemas\nconst combined = composeSchemas([baseSchema, appSchema]);\n\n// Prepare a ValidateFunction from any input form\nconst validateFn = prepareSchema(schemaOrArrayOrFunction);\n```\n\n### Custom Resolvers\n\nImplement the `PropertyResolver` interface to add custom sources:\n\n```typescript\nimport type { PropertyResolver } from \"@emdzej/config-provider\";\nimport type { JsonValue } from \"@emdzej/config-resolver\";\n\nclass VaultResolver implements PropertyResolver {\n  readonly name = \"vault\";\n  get<T extends JsonValue = JsonValue>(key: string): T | undefined { /* ... */ }\n  getAll<T extends Record<string, JsonValue> = Record<string, JsonValue>>(): T { return {} as T; }\n}\n\nconst provider = new ConfigProvider({\n  resolvers: [new MemoryResolver(), new VaultResolver(), new FileResolver({ files: [\"app.json\"] })],\n});\n```\n\n### Error Classes\n\n- **`PropertyNotFoundError`** — thrown by `getRequiredProperty()` when the key doesn't exist in any resolver\n- **`PropertyValidationError`** — thrown by `setProperty()` when the new value would make the merged tree fail schema validation\n\n## Resolver Priority\n\n| Priority | Source | Description |\n|---|---|---|\n| 1 (highest) | Memory | `setProperty()` overrides |\n| 2 | Environment | Env vars with relaxed binding |\n| 3 (lowest) | Files | JSON/YAML files with placeholder resolution |\n\nThis matches Spring Boot's property source ordering. The first resolver to return a value for a key wins.\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-5aa4473c1799ceb84bd81e49464dba03"}