configre 2.1.5 → 2.1.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/README.md CHANGED
@@ -18,11 +18,10 @@ Create `config/index.cjs` with your defaults:
18
18
  module.exports = {
19
19
  db: {
20
20
  host: "localhost",
21
- port: 5432,
22
- password: ""
21
+ port: 5432
23
22
  },
24
23
  api: {
25
- key: ""
24
+ url: "https://api.example.com"
26
25
  }
27
26
  };
28
27
  ```
@@ -73,11 +72,10 @@ The resulting configuration contains:
73
72
  db: {
74
73
  host: "db.internal",
75
74
  port: 5432,
76
- password: "",
77
75
  ssl: true
78
76
  },
79
77
  api: {
80
- key: ""
78
+ url: "https://api.example.com"
81
79
  }
82
80
  }
83
81
  ```
@@ -110,7 +108,7 @@ config/
110
108
  production.secret.cjs # Production secrets, edited locally
111
109
  ```
112
110
 
113
- A secret file exports only the fields it needs to supply. Your application still reads `cfg.db.password` and `cfg.api.key`; there is no separate secrets API.
111
+ A secret file exports only the fields it needs to supply. Your application reads them through ordinary properties such as `cfg.api.key`; there is no separate secrets API. Define sensitive fields in secret files; public configuration does not need placeholders for them.
114
112
 
115
113
  For `--config=production`, the merge order is:
116
114
 
@@ -132,17 +130,17 @@ module.exports = {
132
130
  };
133
131
  ```
134
132
 
135
- For a production database credential, create `config/production.secret.cjs`:
133
+ For a production API key, create `config/production.secret.cjs`:
136
134
 
137
135
  ```javascript
138
136
  module.exports = {
139
- db: {
140
- password: ""
137
+ api: {
138
+ key: ""
141
139
  }
142
140
  };
143
141
  ```
144
142
 
145
- Fill in the values in these local files. Configre generates `secrets.enc.json` and adds Git exclusions for the editable secret files when your application runs.
143
+ Fill in the values in these local files. The production key overrides the shared key when that profile is selected, while `api.url` still comes from the public configuration. Configre generates `secrets.enc.json` and adds Git exclusions for the editable secret files when your application runs.
146
144
 
147
145
  ### Share with a server
148
146
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "configre",
3
- "version": "2.1.5",
3
+ "version": "2.1.6",
4
4
  "description": "🔧 Effortlessly Tailor Your Settings",
5
5
  "type": "module",
6
6
  "engines": {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: configre
3
- description: Set up and manage Configre configuration in Node.js projects, including hostname/profile overrides, encrypted shared secrets, recipient registration, and configuration printing with secret fields omitted. Use when adopting Configre or working on its configuration files, secret sharing, or print() API.
3
+ description: Set up or edit Configre configuration in Node.js projects, including profile overrides, encrypted secrets, safe printing, and explicit environment-variable application.
4
4
  metadata:
5
5
  category: configuration
6
6
  tags: [nodejs, config, environment, settings, secrets]
@@ -8,123 +8,39 @@ metadata:
8
8
 
9
9
  # Configre
10
10
 
11
- Environment-specific configuration manager for Node.js. Merges public defaults, hostname or profile overrides, and optional secrets synchronously using its own deep-merge implementation.
11
+ Load public defaults, extend or overwrite them by profile, and merge optional secrets synchronously.
12
12
 
13
- ## Instructions
13
+ ## Setup and loading
14
14
 
15
- ### Step 1: Install the package
16
-
17
- Requires Node.js 22.13 or later. Configre is native ESM and supports synchronous CommonJS consumers through the same implementation.
18
-
19
- ```bash
20
- npm install configre --save
21
- ```
22
-
23
- ### Step 2: Create the config directory
24
-
25
- Config files **must always use the `.cjs` extension**. This works in both CommonJS and ESM projects and allows using `module.exports`.
26
-
27
- ```
28
- project/
29
- ├── config/
30
- │ ├── index.cjs # Default settings (required)
31
- │ ├── myhostname.cjs # Host-specific overrides (optional)
32
- │ └── myhostname.dev.cjs # Dev override for host (optional)
33
- ```
34
-
35
- ### Step 3: Write the default config (`config/index.cjs`)
36
-
37
- ```javascript
38
- module.exports = {
39
- db: {
40
- host: 'localhost',
41
- port: 5432,
42
- user: 'dev',
43
- password: ""
44
- },
45
- api: {
46
- key: "",
47
- url: 'http://localhost:3000'
48
- }
49
- };
50
- ```
51
-
52
- Keep credentials in `.secret.cjs` files. Public settings should contain explicit empty placeholders, not real credentials or automatic environment-variable fallbacks.
53
-
54
- ### Step 4: Write host/profile overrides
55
-
56
- Create `config/<hostname>.cjs` with only the keys that differ — they are deep-merged over defaults:
57
-
58
- ```javascript
59
- module.exports = {
60
- db: {
61
- user: 'prod-user'
62
- }
63
- };
64
- ```
65
-
66
- ### Step 5: Load the configuration
15
+ Install with `npm install configre`; requires Node.js 22.13 or later.
16
+ Configuration files use `.cjs` and `module.exports`, including in ESM projects.
17
+ The path is required. Anchor it to the module; avoid deriving it from `process.cwd()`.
67
18
 
68
19
  ```javascript
69
20
  import Configre from "configre";
70
21
  import { join } from "node:path";
71
22
 
72
23
  const cfg = Configre(join(import.meta.dirname, "config"));
73
-
74
- console.log(cfg.db.host); // from default
75
- console.log(cfg.db.user); // from host override
76
24
  ```
77
25
 
78
- CommonJS consumers can still use `const Configre = require("configre")` and `Configre(path.join(__dirname, "config"))`, without `.default` or `await`. Configuration files remain `.cjs` in either module system.
26
+ Create `config/index.cjs` for defaults and `config/production.cjs` for additions or overrides.
27
+ Keep sensitive public placeholders as `""`; put actual values in secret files.
28
+ CommonJS supports `require("configre")` directly, without `.default` or `await`.
29
+ `Configre(path)` returns configuration; `new Configre(path)` exposes `.get()` and `.print()`.
79
30
 
80
- The path argument is required. Prefer an absolute path anchored to the module. Do not recommend paths derived from `process.cwd()`, such as `path.join(process.cwd(), "config")`: they can point somewhere else when the process is launched from a different directory.
31
+ ## Profiles and merge behavior
81
32
 
82
- To use a different config directory:
33
+ - Select with `--config=production` anywhere in the application arguments; otherwise use `os.hostname()`.
34
+ - Prefer `<profile>.dev.cjs` over `<profile>.cjs` when present, independently of `NODE_ENV`.
35
+ - Select one public profile; dev and regular variants do not merge. Missing profiles use defaults.
36
+ - Objects merge recursively: preserve unspecified fields, override matching values, and add new fields.
37
+ - Arrays merge by index and retain trailing entries; they are not replaced wholesale.
38
+ - Merge order: public base → public profile → secret base → secret profile.
39
+ - Independently prefer `<profile>.dev.secret.cjs` over `<profile>.secret.cjs`.
83
40
 
84
- ```javascript
85
- const cfg = Configre(join(import.meta.dirname, "settings"));
86
- ```
87
-
88
- ## Profile resolution
89
-
90
- Configre determines the active profile by looking for a `--config=<profile>` argument anywhere in `process.argv`, or falls back to `os.hostname()`. It then checks for overrides in this order (first match wins):
41
+ ## Secrets
91
42
 
92
- 1. `config/<profile>.dev.cjs` — dev override
93
- 2. `config/<profile>.cjs` — production override
94
- 3. No match — uses public defaults; available secrets still apply
95
-
96
- To force a specific profile at runtime:
97
-
98
- ```bash
99
- node app.js --config=staging
100
- node app.js --port=3000 --config=production --debug
101
- ```
102
-
103
- Using `--config=` (instead of a positional argument) avoids conflicts with other CLI flags.
104
-
105
- ## Shared secrets
106
-
107
- ### Activation and file layout
108
-
109
- Secret support activates when the base config or selected profile has a corresponding `.secret.cjs` file, or when an encrypted file already exists. There is no `secrets` option. Without either condition, Configre loads public settings without accessing an identity or creating secret artifacts. Configre never creates editable `.secret.cjs` modules itself; create them only when secret support is wanted.
110
-
111
- For a config directory:
112
-
113
- ```text
114
- config/
115
- ├── index.cjs
116
- ├── production.cjs
117
- ├── index.secret.cjs # Administrator-only base secrets
118
- ├── production.secret.cjs # Optional administrator-only profile secrets
119
- ├── production.dev.secret.cjs # Optional preferred secret profile variant
120
- ├── secrets.enc.json # Generated ciphertext; share through Git
121
- ├── recipients/*.pub # Public recipient keys; share through Git
122
- └── .gitignore # Generated exclusions for editable secrets
123
- ```
124
-
125
- For an individual `settings.cjs` config file, sidecars are `settings.secret.cjs`, `settings.cjs.secrets.enc.json`, and `settings.cjs.recipients/`, beside the config file. An extensionless path resolving to that file uses the same sidecars. File mode selects the base secret module, not profile secret modules.
126
-
127
- Secret modules export a plain object containing only JSON-compatible values. For example, create `config/index.secret.cjs` with empty values for the administrator to fill privately:
43
+ Create `index.secret.cjs` for shared secrets and `<profile>.secret.cjs` for profile secrets:
128
44
 
129
45
  ```javascript
130
46
  module.exports = {
@@ -133,74 +49,52 @@ module.exports = {
133
49
  };
134
50
  ```
135
51
 
136
- Empty strings remain empty. Configre does not fill values from the environment or populate `process.env`. Functions, `undefined`, accessors, symbols, custom objects, circular references, non-finite numbers, sparse arrays, and keys named `__proto__`, `constructor`, or `prototype` are rejected in secret modules.
137
-
138
- ### Merge order
139
-
140
- For `--config=production`, later layers override earlier ones:
141
-
142
- ```text
143
- index.cjs → selected public profile → index.secret.cjs → selected secret profile
144
- ```
145
-
146
- Public selection prefers `production.dev.cjs` over `production.cjs`. Independently, secret selection prefers `production.dev.secret.cjs` over `production.secret.cjs`; dev and regular variants do not merge together. A selected profile secret can activate secrets without `index.secret.cjs`. Once activated, directory mode encrypts all local `.secret.cjs` files, including inactive profiles, while only base and selected profile values enter the returned config.
147
-
148
- ### Administrator and server workflow
149
-
150
- 1. On the administrator, create the needed `.secret.cjs` files and load Configre. It creates or reuses the machine identity, prepares Git exclusions and `recipients/`, and writes `secrets.enc.json`. Editable secrets must not already be tracked by Git; Configre refuses to proceed if they are. It does not untrack files or rewrite history.
151
- 2. Share the public configuration, generated `.gitignore`, recipient public keys, and ciphertext through Git. Keep editable secret modules and the private identity local. A server with ciphertext and no editable secret modules decrypts the bundle without executing secret modules.
152
- 3. On an unauthorized server, loading Configre attempts to publish `recipients/<profile>.pub` through Git. New registration requires a branch matching its upstream, Git author identity, and credentials that work without an interactive prompt. Profile names must start with a letter or digit and contain only letters, digits, dots, underscores, or hyphens.
153
- 4. On the administrator, pull the public-key registration, reload Configre, and publish the updated ciphertext. The server then pulls it and reloads Configre. Publishing a public key alone does not authorize decryption. Any authorized recipient can decrypt the whole bundle, including other profiles; profiles are not access-control boundaries.
154
-
155
- Loading an unauthorized server config can fetch, create a public-key commit, and push it. Account for these side effects before using application startup as a verification command; use isolated fixtures for local checks unless the requested work authorizes live registration. Configre preserves unrelated staged and unstaged changes through an isolated index, runs no commit or pre-push hooks, and does not merge, rebase, force-push, or publish local tags. Already published matching keys do not create another registration commit. Conflicting keys for the same profile are not overwritten. Failed registration is retried on a later load; each registration Git command has a 30-second timeout.
156
-
157
- While authorization is pending, Configre warns and returns public settings only: public placeholders remain and encrypted-only fields are absent. Registration failure also permits public-only startup. Do not assume successful application startup proves secrets were loaded; verify required credentials without printing their values. Invalid secret modules, damaged identities, malformed ciphertext, and authentication failures stop loading instead of silently resetting data.
158
-
159
- ### Updates, revocation, and identity
160
-
161
- - The administrator's local `.secret.cjs` files are authoritative for values; `recipients/` is authoritative for additional recipients. Reloading updates ciphertext when values or recipients change. Unchanged inputs do not rewrite it; existing editable modules are never overwritten. Deleting every local secret module while retaining ciphertext switches to consumer behavior rather than clearing the bundle.
162
- - To revoke a recipient, remove its public-key file on the administrator, reload, and distribute the new ciphertext. The current administrator is always included. Remove repository write access too if the machine must not register again. Old ciphertext and previously obtained credentials remain usable; rotate affected credentials when revocation requires it.
163
- - Identity files live at `~/.config/configre/identity.pem` (private) and `identity.pub` (public). Share only the public key. Back up editable secrets and the private identity securely. On POSIX, the identity directory must have owner-only permissions (`0700`) and the private key owner-only permissions (`0600`). Private identities and secret input files cannot be symlinks.
164
- - A missing public identity can be regenerated from a valid private key. A corrupt private identity, or a missing private identity with an existing public key, is not silently replaced. Restore the identity or deliberately register a new one. Losing all authorized private keys makes existing ciphertext unrecoverable.
165
- - Configre uses AES-256-GCM and RSA-3072/OAEP-SHA-256 for encryption and recipient key wrapping. Ciphertext is replaced atomically under a writer lock. If a stale lock is reported after a crash, confirm its writer has stopped before removing it; do not reset ciphertext or identities as a retry strategy.
166
-
167
- ## Print configuration without secret fields
168
-
169
- Use `print()` instead of logging the merged config object, which contains decrypted secrets:
52
+ - Secret modules supply only the needed fields; consumers read ordinary properties such as `cfg.api.key`.
53
+ - Activate when the base or selected profile has a secret counterpart, or ciphertext already exists.
54
+ - Without either condition, load public settings without identity access or secret artifacts.
55
+ - Configre never creates editable secret modules. There is no `secrets` option.
56
+ - Export plain objects containing JSON-compatible values; empty strings remain empty.
57
+ - Reject functions, undefined, accessors, symbols, custom objects, cycles, sparse arrays, non-finite numbers, and keys named `__proto__`, `constructor`, or `prototype`.
58
+ - Once active, encrypt all local profile secrets together; merge only the base and selected profile.
59
+ - Every authorized identity can decrypt every profile. Profiles organize values, not access.
60
+ - Editable secrets are authoritative for values; `recipients/` is authoritative for additional recipients.
61
+ - With ciphertext and no editable secret modules, load as a consumer; deleting local modules does not clear secrets.
62
+ - Individual `settings.cjs` files use `settings.secret.cjs`, `settings.cjs.secrets.enc.json`, and `settings.cjs.recipients/`; file mode has no secret profiles.
63
+
64
+ ### Sharing and updates
65
+
66
+ 1. Local: create secret files and run; Configre generates ciphertext and Git exclusions. Push the encrypted file.
67
+ 2. Server: pull and run; Configre automatically commits and pushes `recipients/<profile>.pub`.
68
+ 3. Local: pull, run to incorporate the public key, and push the updated ciphertext.
69
+ 4. Server: pull and restart. Later secret updates repeat the local run/push and server pull/restart.
70
+
71
+ Registration needs a branch matching its upstream, Git author identity, and non-interactive push access.
72
+ Pending authorization or failed registration returns public settings only; malformed secrets, identities, and ciphertext stop loading.
73
+ Use isolated fixtures for verification unless live registration is authorized: loading can fetch, commit, and push.
74
+ Registration preserves unrelated changes, skips hooks, rejects key-name collisions, and retries failures on later loads.
75
+ Never commit editable secret modules or private identities; Configre rejects already tracked secret inputs.
76
+ Identity files live under `~/.config/configre/`: `identity.pem` is private and `identity.pub` is public.
77
+ Back up editable secrets and private identities; never reset damaged identities or ciphertext as a retry.
78
+ To revoke access, remove the recipient key, reload locally, and publish; prevent re-registration and rotate previously shared credentials when required.
79
+
80
+ ## Print without secret fields
81
+
82
+ Use `cfg.print()` with `DEBUG=Configre:*`, rather than logging the merged configuration.
83
+ It logs current values without mutation or automatic printing on load; public siblings remain visible.
84
+ Omit loaded secret-file fields recursively, including encrypted inputs; omit secret arrays entirely.
85
+ Classification follows secret-file fields, not names: secrets copied elsewhere are not automatically detected.
86
+ The method is non-enumerable; spreads and JSON still contain secrets and do not preserve the method.
87
+ An existing `print` field is preserved; use `new Configre(configPath).print()` in that case.
88
+
89
+ ## Apply environment variables
170
90
 
171
91
  ```javascript
172
- const cfg = Configre(join(import.meta.dirname, "config"));
173
- cfg.print();
92
+ import Configre, { applyConfigEnv } from "configre";
93
+ const cfg = Configre(configPath);
94
+ applyConfigEnv(cfg.env);
174
95
  ```
175
96
 
176
- `print()` calls Configre's LemonLog `log.debug`. Enable output with `DEBUG=Configre:*`; the `--debug` argument alone does not enable these logs. File creation, ciphertext updates, and public-key publication use `log.info`; authorization failures use `log.warn`, in the same namespace.
177
-
178
- - `cfg.print()` prints current values, including edits made after loading, without modifying `cfg` or logging automatically on load.
179
- - Fields present in loaded base or selected profile secret modules are omitted, including when read from ciphertext. Public sibling fields remain visible; arrays supplied by secrets are omitted entirely and emptied secret objects are removed.
180
- - Classification is by secret-file fields, not names such as `password` or `token`. Sensitive values placed only in public settings or copied to another field are not automatically detected. Keep them in secret modules and do not treat `print()` as a general-purpose redactor.
181
- - The method is non-enumerable: it does not appear in `Object.keys(cfg)`, object spreads, or JSON output. Those operations still include secret data, so do not use them as redaction. A spread or JSON round trip does not preserve the method.
182
- - `new Configre(configPath).print()` is also supported and prints the instance's merged settings. `.get()` still returns plain configuration data. If a configuration already has its own `print` field, that value is preserved; use the constructor's `print()` method instead.
183
-
184
- ## Examples
185
-
186
- **Example 1: Basic setup**
187
- User says: "Add configuration management to my Node.js project"
188
- Actions: install configre, create `config/index.cjs` with project defaults, import Configre and load with `Configre(join(import.meta.dirname, "config"))`
189
- Result: merged config object ready to use
190
-
191
- **Example 2: Multi-environment**
192
- User says: "I need different database settings per server"
193
- Actions: create `config/index.cjs` with defaults, create `config/<hostname>.cjs` per server with db overrides
194
- Result: each server automatically loads its own config based on hostname
195
-
196
- **Example 3: Custom profile via CLI**
197
- User says: "I want to run my app with a staging config"
198
- Actions: create `config/staging.cjs`, run app with `node app.js --config=staging`
199
- Result: staging overrides are merged over defaults, without conflicting with other CLI arguments
200
-
201
- ## Key behaviors
202
-
203
- - **Deep merge**: nested plain objects merge recursively using Configre's own implementation; arrays merge by index and retain existing trailing entries rather than being replaced wholesale
204
- - **Config files must use the `.cjs` extension** (`.js` is not accepted; works in both CommonJS and ESM projects)
205
- - **Required path**: always pass the config directory or file path; prefer an absolute module-relative path over a process-relative path
206
- - **Function vs constructor**: `Configre(path)` returns the merged config with non-enumerable `print()` when that field is available; `new Configre(path)` returns the instance with `.get()` and `.print()`
97
+ Call explicitly after loading. Set only undefined `process.env` variables using `String(value)`; preserve existing values, including `""`.
98
+ Omitted `cfg.env` does nothing; skip null/undefined entries. Reject null, arrays, non-objects, empty keys, and object values.
99
+ Invalid entries throw without rolling back earlier assignments. CommonJS: `const { applyConfigEnv } = require("configre")`.
100
+ For encryption, identity permissions, and recovery details, consult the [advanced reference](https://github.com/clasen/Configre#advanced-reference).