@ciqol/resenv 0.1.0-beta.1 → 0.1.0-beta.2

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/CONTRIBUTING.md CHANGED
@@ -6,9 +6,10 @@ and [repository instructions](https://github.com/ciqol/resenv/blob/beta/AGENTS.m
6
6
  The [README](README.md) covers using resenv in an application. This guide covers
7
7
  changing resenv itself and verifying those changes.
8
8
 
9
- Releases start at `0.1.0-beta.1` on the sole branch `beta`, using npm channel
10
- `beta`. Semantic-release's official commit-analysis and release-notes plugins
11
- select releasable changes and generate notes; actual beta tags supply the next
9
+ Releases start at `0.1.0-beta.1` on the sole branch `beta`, using npm tags
10
+ `beta` and `latest` for the same tested version. Semantic-release's official
11
+ commit-analysis and release-notes plugins select releasable changes and generate
12
+ notes; actual beta tags supply the next
12
13
  prerelease counter. The root and ordinary local builds remain private prototypes.
13
14
  CI generates the publishable manifest before packing and testing it, then stages
14
15
  the exact archive through npm OIDC for maintainer approval. See
@@ -25,6 +26,14 @@ npm ci
25
26
  npm run verify
26
27
  ```
27
28
 
29
+ The committed `.npmrc` omits automatically installed peer dependencies. The
30
+ release planner calls the two official plugins directly, so their full engine
31
+ peer and its publisher dependencies are intentionally not installed. Audit and
32
+ planning enforce that absence. `npm ls` still reports the declared missing
33
+ engine peers; standalone plugin behavior is covered directly by the release
34
+ specs. Keep this install policy when refreshing the lockfile; do not use force
35
+ or legacy peer resolution. See [the dependency review](docs/dependencies.md).
36
+
28
37
  `npm run verify` is the complete local check: lint, strict source/spec type
29
38
  checks, build, unit/process tests and installed-package checks. To build without
30
39
  running the checks, use `npm run build`.
@@ -60,14 +69,22 @@ shipping them in JavaScript or declarations. Do not maintain separate handwritte
60
69
  ```sh
61
70
  npm run test:unit # colocated Vitest specs, no build required
62
71
  npm run test:watch # watch unit specs while editing
63
- npm run test:coverage # source unit coverage, text and lcov output
72
+ npm run test:coverage # source unit coverage, text, lcov and Cobertura output
64
73
  npm run test:integration # process checks, requires a build
65
74
  ```
66
75
 
67
76
  `npm test` runs unit and process tests. Mock or inject provider calls, credential
68
- helpers, browser launchers and process control in unit tests. Keep checks that
69
- spawn actual commands or temporary applications in `test/`. The unit coverage
77
+ lookup and process control in unit tests. Keep checks that spawn actual commands
78
+ or temporary applications in `test/`. The unit coverage
70
79
  report measures source and is separate from packed-package and process checks.
80
+ `npm run verify` enforces a 90% global floor for source statements, branches,
81
+ functions and lines. Worker and preload entry files stay in the denominator;
82
+ the CLI is checked separately by process and packed-package tests. CI retains
83
+ LCOV and Cobertura reports for seven days in the `resenv-coverage` artifact.
84
+ The official `actions/upload-code-coverage` action uploads Cobertura in a separate
85
+ job; fork PRs retain the local gate and reports but cannot upload. CLI lookup
86
+ checks use injected helpers and temporary dummy executables; they never access
87
+ a developer credential store or authenticated provider session.
71
88
 
72
89
  Use normal npm lifecycle behavior locally and in CI. Review install hooks,
73
90
  including implicit native builds, when changing dependencies; record any
@@ -87,6 +104,8 @@ Pass an existing archive to test the same build CI uses:
87
104
  `npm run test:package -- /absolute/path/to/package.tgz`. For a final CI candidate,
88
105
  also pass its `release.json` as the second argument. The supplied package's
89
106
  name, version, executable mode and manifest are checked before runtime probes.
107
+ The README inside the archive must match the checkout, install from npm's
108
+ `beta` channel and avoid unpublished or local-archive installation guidance.
90
109
  `npm run verify` runs lint, typecheck, build and tests; `npm run release:check`
91
110
  also previews the package contents. Neither publishes or creates a tag.
92
111
 
@@ -127,22 +146,20 @@ local. Leave its manifest, lockfile and CI configuration unchanged.
127
146
 
128
147
  Rebuild resenv after source, metadata or documentation changes and restart the
129
148
  application. An application `npm ci` can remove the link; recreate it when needed.
130
- If you only created the package link, run its CLI directly:
149
+ Authenticate with the official Doppler CLI from the application's directory:
131
150
 
132
151
  ```sh
133
- node node_modules/@ciqol/resenv/bin/resenv.cjs login doppler
152
+ doppler login
134
153
  ```
135
154
 
136
- For login from the resenv checkout, use
137
- `node dist/bin/resenv.cjs login doppler`. Real login is a manual integration
138
- check; automated tests must use injected or mocked credentials.
155
+ resenv reuses that login at runtime. Automated tests use injected or mocked
156
+ credentials; real login is a manual integration check owned by Doppler's CLI.
139
157
 
140
158
  ## Optional integration checks
141
159
 
142
160
  `npm run bench` measures fresh-process startup, a plain 200-variable environment,
143
161
  and a mocked provider with eight distinct lookups. It needs a build first and
144
162
  does not contact a provider. Timings are observations, not CI pass/fail thresholds.
145
- See [the review](docs/security-performance-review.md) for the recorded comparison.
146
163
 
147
164
  With an existing Nx installation:
148
165
 
@@ -168,7 +185,8 @@ enable that option; the CLI passes the returned copy to its child process.
168
185
 
169
186
  Add regression tests for changed behavior and avoid real provider calls. Use
170
187
  Conventional Commit subjects, for example `fix(resolver): preserve empty values`.
171
- Dependency changes require an updated [review record](docs/dependencies.md).
188
+ Review dependency changes under the [dependency policy](docs/dependencies.md)
189
+ and put the evidence in the PR. Update the guide when a lasting choice changes.
172
190
 
173
191
  CODEOWNERS identifies the maintainer. Keep bug reports and pull requests focused
174
192
  on observable behavior and include the checks run. Weekly dependency updates are
@@ -177,6 +195,8 @@ grouped, with major version updates reviewed separately; bots cannot merge them.
177
195
  Keep the README focused on installation, authentication, application integration,
178
196
  API behavior and troubleshooting. Put contributor tooling, test commands and
179
197
  local development instructions here; detailed internals belong in `docs/`.
198
+ Keep docs current rather than appending session logs, dated audit reports or
199
+ test transcripts. Record run results in PRs and CI.
180
200
 
181
201
  Security reports follow [SECURITY.md](SECURITY.md). Publication and repository
182
202
  visibility changes require a separate maintainer decision; see the
package/README.md CHANGED
@@ -20,45 +20,58 @@ Resolution happens once at startup. It does not watch the provider for changes;
20
20
  restart the application to fetch updated values. No filename is passed to
21
21
  resenv, and dotenv files are never rewritten.
22
22
 
23
- Requires Node.js 20 or later. This is an experimental beta, currently distributed
24
- as a package archive; it has not been published to npm.
23
+ Requires Node.js 20 or later. This is an experimental beta, published on npm's
24
+ `beta` channel.
25
25
 
26
26
  ## Install
27
27
 
28
- Install the archive supplied by the maintainer in your application project:
28
+ Install the beta in your application project:
29
29
 
30
30
  ```sh
31
- npm install --save-exact /path/to/ciqol-resenv-0.1.0-beta.1.tgz
31
+ npm install --save-exact @ciqol/resenv@beta
32
32
  ```
33
33
 
34
- This records resenv in your project's manifest and lockfile. Keep the archive
35
- available wherever that project installs dependencies. Once the beta is
36
- published to npm, you will be able to install `@ciqol/resenv@beta` instead.
34
+ This records the selected beta version in your project's manifest and lockfile.
35
+ The `latest` tag points to the same beta; omitting `@beta` installs it too.
37
36
 
38
37
  The CLI examples below use `npx --no-install` to run the copy installed in your
39
38
  project. In an npm script, use `resenv` directly.
40
39
 
41
40
  ## Use with Doppler
42
41
 
42
+ Doppler manages application secrets. If you're new to it, start with
43
+ [Doppler's introduction](https://docs.doppler.com/docs/start).
44
+
43
45
  ### 1. Authenticate locally
44
46
 
45
- After installing the package, run:
47
+ Follow the [official Doppler CLI installation guide](https://docs.doppler.com/docs/cli#installation)
48
+ for your operating system, then log in from your application directory:
46
49
 
47
50
  ```sh
48
- npx --no-install resenv login doppler
51
+ doppler login
49
52
  ```
50
53
 
51
- Each developer authenticates with their own Doppler access. Secret reads use
52
- the HTTPS API. Credentials come from `DOPPLER_TOKEN`, the saved OS credential,
53
- or an existing Doppler CLI login, in that order. Startup reports missing access
54
- and stops; it does not open a login prompt.
54
+ Each developer authenticates with their own Doppler access.
55
+
56
+ > [!WARNING]
57
+ > Authentication and credential storage depend on the Doppler CLI's implementation
58
+ > and may vary by platform or CLI version. resenv only reuses its existing token;
59
+ > it does not manage login, logout or credential storage, or access the OS
60
+ > credential store directly.
61
+
62
+ At startup, resenv uses `DOPPLER_TOKEN` when supplied; otherwise it reads the
63
+ existing Doppler CLI token for the application's working directory. It then
64
+ fetches only referenced secrets through the HTTPS API. Missing credentials or
65
+ access stop startup without opening a browser or prompting for login.
66
+
67
+ For CI and other unattended environments, supply `DOPPLER_TOKEN` through your
68
+ runner's secret facility; the CLI is not needed in that mode. See
69
+ [Doppler authentication and platform details](docs/doppler.md), including
70
+ migration from the earlier resenv-managed login.
55
71
 
56
- Native login uses the browser flow observed in Doppler's official MCP client
57
- and saves the token in macOS Keychain or Linux Secret Service. The CLI is a
58
- fallback; the browser protocol is not a public OAuth contract and fresh native
59
- login is not yet verified end to end. Windows needs `DOPPLER_TOKEN` because
60
- native login/storage is not implemented there. See
61
- [Doppler login and platform details](docs/doppler.md).
72
+ Need another secrets provider, operating system or CLI installation method?
73
+ [Request support](https://github.com/ciqol/resenv/issues/new) with your use case
74
+ and platform details.
62
75
 
63
76
  ### 2. Declare references in the environment your app already loads
64
77
 
@@ -94,7 +107,8 @@ npx --no-install resenv run -- node app.mjs
94
107
 
95
108
  The wrapper supplies resolved values to the child process. It inherits
96
109
  stdin/stdout/stderr, preserves exit status and forwards SIGINT, SIGTERM and
97
- SIGHUP to that child.
110
+ SIGHUP to that child. On POSIX, the child runs in its own session so terminal
111
+ signals reach it only through the wrapper.
98
112
 
99
113
  ## Nx integration
100
114
 
package/SECURITY.md CHANGED
@@ -9,22 +9,24 @@ any credentials supplied to it.
9
9
 
10
10
  Doppler mode uses a fixed HTTPS API origin, disables redirects and fetches only
11
11
  referenced secret names. It does not execute shell expressions. Credentials
12
- come from an explicit token, resenv's OS credential store or a fixed Doppler CLI
13
- helper. An API rejection never triggers a switch to another identity. Native
14
- browser login uses endpoints observed in Doppler's official MCP client rather
15
- than a public third-party OAuth contract. See [Doppler details](docs/doppler.md)
16
- for fallback rules, credential scope and platform limits.
12
+ come from an explicit `DOPPLER_TOKEN` or the current directory's existing
13
+ Doppler CLI login. A fixed CLI helper captures that token privately; resenv
14
+ does not parse Doppler's config files. An API rejection never triggers a switch
15
+ to another identity. The official Doppler CLI owns browser authorization,
16
+ token storage, login and logout. See [Doppler details](docs/doppler.md)
17
+ for lookup rules, credential scope, migration and platform limits.
17
18
  The canonical `resolve` preload and `run` command select Doppler only for
18
19
  `doppler://` values. They do not infer shell execution from `$()` values.
19
- Startup does not initiate login; the separate login command rejects CI and
20
- noninteractive sessions.
20
+ Startup does not initiate login. Use `doppler login` separately for local
21
+ development or supply a suitably scoped token for unattended runs.
21
22
 
22
- The local credential store does not prevent code running as the same user from
23
- requesting credentials. resenv does not automatically remove an explicitly
24
- supplied `DOPPLER_TOKEN` from the application environment. Interactive login
25
- displays a temporary human authorization code; tokens and the polling code are
26
- not logged. The Node preload's private worker exchanges credentials and results
27
- over captured pipes, never process arguments or dotenv files.
23
+ Doppler's credential storage does not isolate credentials from other code
24
+ running as the same user with access to them. resenv does not read or write the
25
+ OS credential store directly or persist login tokens or fetched secrets.
26
+ It does not automatically remove an explicitly supplied `DOPPLER_TOKEN` from
27
+ the application environment. Tokens, captured CLI output and provider response
28
+ bodies are not logged. The Node preload's private worker exchanges credentials
29
+ and results over captured pipes, never process arguments or dotenv files.
28
30
 
29
31
  Command mode executes trusted configuration with the current user's
30
32
  permissions. Enable it only for configuration you have reviewed. Commands can
@@ -57,17 +59,15 @@ all changed properties; read-only properties, setters and Proxy targets are
57
59
  rejected before any values are committed. Use `resolve()` to obtain a copy
58
60
  when the input is not a writable environment record.
59
61
 
60
- This experimental package has not had an independent security audit. Validation
61
- evidence and platform limits are recorded in [docs/validation.md](docs/validation.md).
62
+ This experimental package has not had an independent security audit. See
63
+ [verification](CONTRIBUTING.md) and [Doppler platform limits](docs/doppler.md).
62
64
 
63
65
  ## Reporting a vulnerability
64
66
 
65
67
  Do not open a public issue containing a vulnerability, credentials or sensitive
66
- output. Open the repository's Security tab and use "Report a vulnerability"
67
- when available. If that option is unavailable, contact the maintainer through
68
- an existing private channel. Before the public transition is complete,
69
- maintainers must enable GitHub private vulnerability reporting and verify that
70
- it is available.
68
+ output. [Report a vulnerability privately](https://github.com/ciqol/resenv/security/advisories/new)
69
+ through GitHub. If that option is unavailable, contact the maintainer through
70
+ an existing private channel.
71
71
 
72
72
  Include affected versions, platform, impact and a minimal reproduction using
73
73
  dummy data. Never include production tokens, private keys or environment dumps.
package/bin/resenv.cjs CHANGED
@@ -6,32 +6,15 @@ var node_os = require('node:os');
6
6
  var assertSafeDiagnostics = require('../assert-safe-diagnostics.cjs');
7
7
  var preload = require('../preload.cjs');
8
8
  var index = require('../index.cjs');
9
- var auth = require('../doppler/auth.cjs');
10
- var system = require('../doppler/system.cjs');
11
- var references = require('../doppler/references.cjs');
12
9
  var values = require('../values.cjs');
13
10
 
14
- const usage = 'Usage: resenv run [--commands] -- <command> [arguments...]\n resenv login [doppler] [--cli] [--no-browser]\n resenv logout doppler\nInfer secret providers from values already in the environment.\n--doppler remains accepted for compatibility.\n--commands enables whole-value $(command) expressions using /bin/sh.\n';
15
- async function main() {
11
+ const usage = 'Usage: resenv run [--commands] -- <command> [arguments...]\nInfer secret providers from values already in the environment.\nManage authentication with doppler login and doppler logout, or supply DOPPLER_TOKEN.\n--doppler remains accepted for compatibility.\n--commands enables whole-value $(command) expressions using /bin/sh.\n';
12
+ function main() {
16
13
  const args = process.argv.slice(2);
17
14
  if (args.length === 1 && ['--help', '-h'].includes(args[0] || '')) {
18
15
  process.stdout.write(usage);
19
16
  return;
20
17
  }
21
- const explicitProvider = args[1] === 'doppler';
22
- if (args[0] === 'login' && args.slice(explicitProvider ? 2 : 1).every(arg => ['--cli', '--no-browser'].includes(arg))) {
23
- if (!explicitProvider && !references.references(process.env).length) {
24
- process.stderr.write('Nothing to handle: no cloud secret references in the environment. Load your target environment first, or use resenv login doppler.\n');
25
- return;
26
- }
27
- await auth.login({ cli: args.includes('--cli'), noBrowser: args.includes('--no-browser') });
28
- return;
29
- }
30
- if (args.length === 2 && args[0] === 'logout' && args[1] === 'doppler') {
31
- system.createSystem().remove();
32
- process.stderr.write('Removed the resenv credential. Doppler CLI credentials and DOPPLER_TOKEN are unchanged; revoke the token in Doppler to invalidate it.\n');
33
- return;
34
- }
35
18
  let commands = false;
36
19
  while (args[0] === 'run' && ['--commands', '--doppler'].includes(args[1] || '')) {
37
20
  if (args[1] === '--commands')
@@ -55,11 +38,23 @@ async function main() {
55
38
  return;
56
39
  }
57
40
  assertSafeDiagnostics.assertSafeDiagnostics();
58
- const child = node_child_process.spawn(command, commandArgs, { env, stdio: 'inherit', shell: false });
41
+ // Isolate the child from terminal group signals; the wrapper forwards them once.
42
+ const child = node_child_process.spawn(command, commandArgs, { env, stdio: 'inherit', shell: false, detached: process.platform !== 'win32' });
59
43
  const signals = ['SIGINT', 'SIGTERM', 'SIGHUP'];
60
44
  const handlers = new Map();
61
45
  for (const signal of signals) {
62
- const handler = () => { child.kill(signal); };
46
+ const handler = () => {
47
+ try {
48
+ if (process.platform !== 'win32' && child.pid)
49
+ process.kill(-child.pid, signal);
50
+ else
51
+ child.kill(signal);
52
+ }
53
+ catch (err) {
54
+ if (!values.hasErrorCode(err, 'ESRCH'))
55
+ throw err;
56
+ }
57
+ };
63
58
  handlers.set(signal, handler);
64
59
  process.on(signal, handler);
65
60
  }
@@ -85,7 +80,10 @@ async function main() {
85
80
  }
86
81
  });
87
82
  }
88
- void main().catch(err => {
83
+ try {
84
+ main();
85
+ }
86
+ catch (err) {
89
87
  process.stderr.write(`${index.formatError(err)}\n`);
90
88
  process.exitCode = 1;
91
- });
89
+ }
package/docs/build.md CHANGED
@@ -71,7 +71,7 @@ Public options use type aliases and readonly properties. `Environment` stays
71
71
  mutable for optional in-place replacement. `resolve()` always returns a new
72
72
  `Environment`, with input replacement enabled only by `{ replace: true }`. It
73
73
  does not preserve a generic input type: resolved strings cannot retain a
74
- literal reference type. This intentionally tightens the unpublished API.
74
+ literal reference type.
75
75
  ESLint uses type information and enforces the selected style rules. The compiler
76
76
  checks all library/CLI source, colocated unit specs and both consumer formats with `strict` and
77
77
  `noUncheckedIndexedAccess`. Provider responses and parsed worker input start as
@@ -92,9 +92,9 @@ and docs are copied into the distribution and included in the archive.
92
92
 
93
93
  Ordinary local builds retain `private: true`. The CI build accepts a validated
94
94
  release plan and sets its computed version, removing `private` before the final
95
- archive is packed and tested. The development root stays private. Earlier
96
- builds kept both manifests private while the package was an unpublished
97
- prototype. There is no publishing workflow.
95
+ archive is packed and tested. The development root stays private. Only the
96
+ manual release workflow stages that tested archive for npm approval; see
97
+ [releasing](releasing.md).
98
98
 
99
99
  `npm run pack:local` packs the generated directory. `npm run test:package`
100
100
  instead packs into a temporary directory and installs it offline into a clean
@@ -122,15 +122,14 @@ package, extracts the same build
122
122
  for the process tests, and never recompiles it. The matrix checks Node 20
123
123
  and 22 on Linux plus the `.nvmrc` version on Linux and macOS.
124
124
 
125
- Jobs have read-only repository permissions and receive no application or
125
+ Build and test jobs have read-only repository permissions and receive no application or
126
126
  registry secrets. Missing artifacts and digest mismatches fail the run. The
127
- archive is retained for seven days for maintainer review. The workflow has no
128
- publish or deploy step. Hosted results must be observed before treating the
129
- workflow definition as a passing CI run.
130
-
131
- Checkout and setup/download actions use full commit pins. The GitHub-owned
132
- upload action uses the Tashtit exception for a complete release tag; major tags
133
- and third-party movable refs are not permitted. Installs use normal npm lifecycle
134
- behavior, and dependency updates require an install-hook review. Build commands
135
- run explicitly.
136
- See the [maturity evaluation](maturity.md) for the static catalog results.
127
+ archive is retained for seven days for maintainer review. A separate release
128
+ job requires manual dispatch on `beta`, the protected `release` environment and
129
+ all candidate checks. Hosted results must be observed before treating the workflow definition
130
+ as a passing CI run.
131
+
132
+ Official `actions/*` and `github/*` actions use exact release tags under the
133
+ repository's GitHub publisher trust policy. Other remote actions require full
134
+ commit SHAs. Installs use normal npm lifecycle behavior, and dependency updates
135
+ require an install-hook review. Build commands run explicitly.