envapt 4.1.0 → 5.0.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/CHANGELOG.md ADDED
@@ -0,0 +1,328 @@
1
+ # envapt
2
+
3
+ ## 5.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 9711554: **BREAKING:** array converters now use a phantom-branded `Converters.array({ of?, delimiter? })` builder instead of the `{ delimiter, type }` config object. inference survives variable indirection and union-widening, and bad element values in the raw env value throw `EnvaptError` (`ArrayElementConversionFailed`, code 206) instead of silently substituting the raw string into a wrong-typed array.
8
+
9
+ Migration:
10
+
11
+ ```ts
12
+ // before
13
+ @Envapt('PORTS', { converter: { delimiter: ',', type: Converters.Number }, fallback: [] })
14
+ @Envapt('TAGS', { converter: Converters.Array, fallback: [] })
15
+
16
+ // after
17
+ @Envapt('PORTS', { converter: Converters.array({ of: Converters.Number }), fallback: [] })
18
+ @Envapt('TAGS', { converter: Converters.array(), fallback: [] })
19
+ ```
20
+
21
+ **BREAKING:** `Converters` migrates from a TS `enum` to an `as const` object so envapt source stays compatible with `erasableSyntaxOnly` / Node's native TS execution. call sites are unchanged (`Converters.Number === 'number'` still holds), but `Converters` is no longer usable as a type. use `ConverterToken` instead.
22
+
23
+ **BREAKING:** `Converters.Array` token is gone (use `Converters.array()`). the `ArrayConverter` and `ValidArrayConverterBuiltInType` types are gone (replaced by `ArrayOf<TElement>` and `ArrayElement`).
24
+
25
+ new: `of:` accepts a custom `(raw: string) => T` function. the array element type is inferred from the function's return type, so `Converters.array({ of: (raw) => User.parse(raw) })` types the property as `User[]`.
26
+
27
+ new: `Converters.array({ of: Converters.Time })` accepts `TimeFallback[]` (e.g. `['5s', '10m']`) as a fallback, matching the existing scalar `Converters.Time` asymmetry. string fallbacks are coerced to milliseconds at resolve time.
28
+
29
+ `Envapter.getUsing` now routes through the parser whenever a fallback is provided, so `TimeFallback` / `TimeFallback[]` fallbacks are coerced consistently with the `@Envapt` decorator path. fixes a pre-existing inconsistency where `Envapter.getUsing('MISSING', Converters.Time, '10s')` returned `'10s'` instead of `10000`.
30
+
31
+ - 9711554: Add `Envapter.debug` three-level log toggle. Removes the `debug` key from the env-file options object.
32
+ - New `Envapter.debug = 'silent' | 'warn' | 'verbose'` (default `silent`). Output goes to stderr prefixed with `[envapt]`.
33
+ - New `ENVAPT_DEBUG` env var. Read lazily on first access if the setter was never called; the setter wins after that.
34
+ - `warn` level: failed `.env` reads, unresolved `${VAR}` templates (non-strict path), fallback values used in place of missing env.
35
+ - `verbose` level: everything in `warn` plus effective `.env` paths during cache rebuild, the cache-cleared notice, per-file key counts, and per-key load lines.
36
+ - New `DebugLevel` type re-exported from the package root.
37
+ - Invalid setter values throw `EnvaptError(InvalidUserDefinedConfig)` with a list of the accepted levels.
38
+ - **Breaking**: the `debug` key on the env-file options object is removed. Use `Envapter.debug = 'verbose'` (or `ENVAPT_DEBUG=verbose`) instead. The corresponding `[dotenv]`-prefixed lines are gone; all debug output now flows through the unified `[envapt]` surface.
39
+
40
+ - 9711554: **BREAKING:** dropped `dotenv` as a runtime dependency. envapt now has **zero runtime deps**. A small internal `.env` parser ships inline (`src/Dotenv.ts`) that handles the subset of dotenv semantics envapt actually relies on: KEY=VALUE pairs, `export KEY=...` prefix, single / double / backtick quotes (with `\n`, `\r`, `\t`, `\\`, `\"` escape interpretation for double quotes), multi-line quoted values, inline `# comments`, multiple paths with first-wins (or `override: true`), and `encoding` for non-UTF8 files. End-user behavior is unchanged: the existing test suite (357 tests) passes against the new parser.
41
+
42
+ **BREAKING:** `Envapter.envFileOptions` no longer accepts `quiet` or `DOTENV_KEY`.
43
+ - `quiet` existed only to suppress dotenv's marketing tips. envapt never prints anything from the loader, so the option is meaningless. The default `_userDefinedDotenvConfig` is now `{}` instead of `{ quiet: true }`.
44
+ - `DOTENV_KEY` was for `dotenv-vault` encrypted `.env` files. Not exercised by any envapt test and out of scope for envapt's internal parser. If you need encrypted env files, decrypt them externally and pass the resulting `.env` path to `Envapter.envPaths`.
45
+
46
+ Passing either key to `Envapter.envFileOptions = { ... }` now throws `InvalidUserDefinedConfig` (302). The remaining allowed keys are `encoding` and `override`.
47
+
48
+ **BREAKING:** the env-file options accessor was renamed from `Envapter.dotenvConfig` to `Envapter.envFileOptions` (type `DotenvConfigOptions` to `EnvFileOptions`), since it no longer relates to the dropped `dotenv` package.
49
+
50
+ - 9711554: Add global `Envapter.strict` flag, `required: true` option, and `Envapter.require()` for boot-time existence checks.
51
+ - New `Envapter.strict` flag (default `false`). When enabled: whitespace-only values are treated as missing on read; empty / whitespace items in array converters throw `EmptyArrayElement (207)` instead of being silently filtered; unresolved `${VAR}` placeholders in cached values and `Envapter.resolve` tagged templates throw `MissingEnvValue (305)` instead of being preserved as literal text. Toggling the flag refreshes the cache.
52
+ - New `@Envapt(key, { required: true })` decorator option. Throws `MissingEnvValue` on first access if the env value is missing or empty (post-trim). Independent of global `strict`. Mutually exclusive with `fallback`: combining them fails to match any overload at compile time, and the runtime Validator throws `InvalidUserDefinedConfig (302)` for dynamic objects that bypass the types.
53
+ - New functional options-bag form: `Envapter.getUsing(key, { converter, required: true })` and `Envapter.getWith(key, { converter, required: true })` return the converter's narrowed type (no `| undefined`) and throw `MissingEnvValue` on missing/empty. Positional `(key, converter, fallback?)` form unchanged.
54
+ - New `Envapter.require(...keys)` existence-check helper. Variadic rest signature, always returns `void`. At least one key required (compile-time error via `[string, ...string[]]` tuple if zero args). Collects every missing key into a single error instead of failing one at a time. Resolves templates before checking.
55
+ - New error codes: `EmptyArrayElement (207)` for strict-mode array empties; `MissingEnvValue (305)` for required-key absences, `require()` failures, and strict-mode unresolved templates.
56
+
57
+ ### Minor Changes
58
+
59
+ - 9711554: Add `Envapter.baseDir` to anchor `.env` resolution to a directory instead of `process.cwd()`. The auto-cascade, `configureProfiles` paths, and relative `envPaths` resolve against it; absolute paths bypass it. It accepts a directory path or a module location: `import.meta.url` / `import.meta.dirname` (ESM) or `__dirname` (CJS). Left unset, paths resolve against `process.cwd()` as before.
60
+
61
+ This covers monorepos where the process starts from the repository root rather than the package directory, so a package-local `.env` resolves regardless of the working directory.
62
+
63
+ - 9711554: Deprecate the classic positional `@Envapt('KEY', fallback, Converter)` form. It now carries a `@deprecated` JSDoc tag and will be removed in v6; it still works throughout v5. Use the options object: `@Envapt('KEY', { converter, fallback })`.
64
+ - 9711554: Add the `envapt/config` side-effect entry, a drop-in for `dotenv/config`. `import 'envapt/config'` (or `node --import envapt/config`, or `node -r envapt/config` in CommonJS) loads envapt's per-environment `.env` cascade and mirrors every loaded key into `process.env`, with zero dependencies. Also adds `Envapter.load()` to eagerly load the cascade on demand instead of lazily on first read.
65
+ - 9711554: Lowered the Node engine floor from `>=24.0.0` to `>=20.0.0`. The Node 24 pin was originally there for `util.parseEnv`, which envapt no longer uses now that the internal `.env` parser ships in `src/Dotenv.ts`. Node 20 LTS users (the bulk of the production ecosystem) can install envapt cleanly again. `bun >=1.3.0` and `deno >=2.5.0` engine floors are unchanged.
66
+ - 9711554: new **profiles** support. envapt now auto-loads conventional dotenv-flow files based on the active environment: `.env`, `.env.local`, `.env.${env}`, `.env.${env}.local` (most-specific wins, matches Vite). zero config for the common case.
67
+
68
+ new `Envapter.configureProfiles({...})` for non-conventional path mappings per environment. configured paths layer on top of the cascade with higher precedence; pass `useDefaults: false` to skip the cascade entirely.
69
+
70
+ new `Envapter.resetProfiles()` clears any profile / envPaths configuration back to the cascade default. useful in tests.
71
+
72
+ existing `Envapter.envPaths = '...'` still works as the lowest-level override and takes absolute precedence when explicitly set.
73
+
74
+ - 9711554: Add Standard Schema v1 adapter (zod, valibot, arktype, hand-rolled).
75
+ - New `schema:` option on `@Envapt`. Synchronous schemas only.
76
+ - New `Envapter.parse(key, schema, fallback?)` static + instance methods.
77
+ - New `StandardSchemaV1` interface inlined verbatim from <https://standardschema.dev>; `InferSchemaInput<S>` / `InferSchemaOutput<S>` helpers exported from the package root. Zero runtime peer dependencies.
78
+ - New error codes: `SchemaValidationFailed (208)` populates `err.issues` with the schema's `~standard.validate` issue array; `SchemaThrew (209)` chains the underlying throw via `Error.cause`.
79
+ - Schema slot is mutually exclusive with `converter`: combining them fails to match any overload at compile time, and the runtime Validator throws `InvalidUserDefinedConfig` for dynamic objects that bypass the types.
80
+ - Async-validating schemas resolve the type slot to the `SchemaMustBeSync` brand; the Parser also throws if a Promise leaks past the type check.
81
+ - Missing env + fallback returns the fallback as-is without re-validating it through the schema.
82
+
83
+ - 9711554: Add per-type shorthand decorators: `@EnvNum`, `@EnvStr`, `@EnvBool`, `@EnvUrl`, `@EnvTime`.
84
+
85
+ Each is a thin wrapper over `@Envapt` with a fixed converter, so the call site is the key and an optional fallback (`@EnvNum('PORT', 3000)`). The fallback is typed to the converter: `@EnvUrl` takes a `URL`, `@EnvTime` a millisecond number or a time string, the rest their primitive. They accept the ordered-key array form and resolve through the same cache, getter install, and strict-mode path as `@Envapt`. For `required`, a `schema`, an array, or a custom converter, use `@Envapt` with the options object.
86
+
87
+ - 9711554: Add `Envapter.syncProcessEnv` opt-in to mirror dotenv-loaded keys back to `process.env`.
88
+ - New `Envapter.syncProcessEnv = boolean` (default `false`). Symmetric with `Envapter.strict` / `Envapter.debug`.
89
+ - When `true`, after every cache (re)build envapt writes the keys it loaded from `.env` files into the real `process.env`. Only the keys the loader actually wrote into the isolated env are mirrored, so first-wins (`override: false`, default) leaves a pre-existing `process.env` value alone and `envFileOptions.override = true` lets the file value win in both the cache and the mirror.
90
+ - Flipping `false` to `true` after the cache is already populated mirrors the existing tracked delta immediately (no cache refresh). Flipping `true` to `false` is one-way: previously mirrored keys remain in `process.env` until the process exits.
91
+ - Invalid setter values (non-boolean) throw `EnvaptError(InvalidUserDefinedConfig)`.
92
+ - Verbose debug emits per-key `mirrored KEY to process.env` lines plus a summary `mirrored N keys to process.env` after each mirror.
93
+
94
+ - 9711554: `Converters.Time` fallbacks now accept a time-string in addition to a number: `fallback: '10s'` is the same as `fallback: 10000`. also adds `d` (days) and `w` (weeks) to the supported units. `TimeFallback` is exported if you want to type the fallback yourself.
95
+
96
+ malformed time-string fallbacks (like `'1.5h'` or `'1500'`, where the runtime expects an integer with an explicit unit) now throw `EnvaptErrorCodes.MalformedTimeFallback` instead of the generic `FallbackConverterTypeMismatch`. number fallbacks keep working the same way.
97
+
98
+ ### Patch Changes
99
+
100
+ - 9711554: Add cross-runtime integration test layer under `packages/envapt/tests/integration/`.
101
+ - Hand-rolled `node:assert`-based smoke (6 portable suites + 1 Deno-only suite, ~30 assertions): basic get, every built-in converter, fallbacks, missing-file recovery, the `@Envapt` decorator's runtime install path, the v5 features (strict, debug, syncProcessEnv, require), and `@Envapt` syntax compiled by the host runtime's TS transpiler.
102
+ - Runs identically under Node, Bun, and Deno; consumes only the built `dist/index.mjs`.
103
+ - New `test:integration` package script for local Node runs.
104
+ - New GitHub Actions workflow `cross-runtime.yml`: build once, fanout across Node `[20, 22, 24]` on ubuntu plus Node 22 on macos and windows, plus Bun on ubuntu and Deno on ubuntu. Branch protection should gate on the aggregator `cross-runtime ok` job.
105
+
106
+ **Known limitation: Bun direct-`.ts` execution with `@Envapt` syntax.** Bun 1.3.10+ ignores the `experimentalDecorators` tsconfig flag and emits TC39 Stage 3 decorators ([bun#27575](https://github.com/oven-sh/bun/issues/27575)); envapt's `@Envapt` is a legacy TypeScript decorator and the call shapes are incompatible. Bun users who want the decorator API should precompile with `tsc` / `tsdown` / Vite first, then run the compiled output with Bun. The functional API (`Envapter.get`, `getNumber`, etc.) works without any build step under direct-`.ts` execution.
107
+
108
+ No public API change.
109
+
110
+ - 9711554: Instance `get` now narrows its return type on a fallback. A redundant overload made `env.get('KEY', 'fallback')` resolve to `string | undefined` instead of `string`; removing it makes instance `get` match static `get`.
111
+ - 9711554: Minify the published build and mark the package side-effect-free except the `envapt/config` entry. The
112
+ `dist` output is now minified (roughly halving the npm install size), and the `sideEffects` field lets
113
+ bundlers tree-shake the parts of the surface a consumer does not import. No API or behavior change.
114
+ - 9711554: Rewrite the README for v5. The package README is now a short, docs-first landing page: hero, the
115
+ `process.env` to typed-value pitch, install for npm/pnpm/yarn/bun/Deno (JSR), and one quick start each
116
+ for the functional and decorator APIs, an agent-skill install line, with the full reference linked at
117
+ the docs site. Removes the stale v4 surface (the old converter enum, `dotenvConfig`, Node `>=22`, "dotenv bundled").
118
+
119
+ Also refresh the npm `description` and `keywords` for registry and search discoverability (adds
120
+ `typescript`, `type-safe`, `deno`, `bun`, `zod`, `valibot`, `arktype`, `validation`, `decorator`,
121
+ `cross-runtime`, and related terms), and fix the `@Envapt` url-converter `@example` to pass a `URL`
122
+ instance for the fallback instead of a string (the previous example would throw at runtime, since the
123
+ url converter validates the fallback as `instanceof URL`).
124
+
125
+ ## 4.1.1
126
+
127
+ ### Patch Changes
128
+
129
+ - 1244fb1: bump deps
130
+
131
+ ## 4.1.0
132
+
133
+ ### Minor Changes
134
+
135
+ - a87c8e5: allow passing a list of env as the first arg to Envapt or Envapter methods. envapt will look for the env left-to-right and pick the first available one. passing a string still works and everything that worked before will work the same right now
136
+
137
+ ## 4.0.2
138
+
139
+ ### Patch Changes
140
+
141
+ - 9c7b728: bump versions
142
+ - 9c7b728: add examples for each overload of the Envapt decorator
143
+
144
+ ## 4.0.1
145
+
146
+ ### Patch Changes
147
+
148
+ - bump deps
149
+
150
+ ## 4.0.0
151
+
152
+ ### Major Changes
153
+
154
+ - **BREAKING:** Simplify exports and type definitions for ESM and CJS. Replaced the nested exports map with flat fields import, require, and default. Types are now only at ./dist/index.d.ts. Removed the build step that renamed index.d.cts to index.d.mts.
155
+
156
+ ## 3.0.2
157
+
158
+ ### Patch Changes
159
+
160
+ - 063703c: bump deps
161
+
162
+ ## 3.0.1
163
+
164
+ ### Patch Changes
165
+
166
+ - bump deps and refactor regexes to use String.raw and RegExp obj in BuiltInConverters to avoid disabling eslint rules
167
+
168
+ ## 3.0.0
169
+
170
+ ### Major Changes
171
+
172
+ - 482eb6d: **BREAKING:** Custom Converters will now execute even if an env variable is not present in the source env file(s). This allows for using `@Envapt` to validate the existence of variables by throwing errors inside the user-defined Custom Converters
173
+
174
+ ### Patch Changes
175
+
176
+ - 5d1df34: update README to include new things and better navigation
177
+
178
+ ## 2.2.10
179
+
180
+ ### Patch Changes
181
+
182
+ - remove codecov config
183
+
184
+ ## 2.2.9
185
+
186
+ ### Patch Changes
187
+
188
+ - b263044: Ensure expected behavior to reject non ISO strings for Date converters
189
+ - 953bcb2: Improve test coverage by ignoring unreachable defensive programming blocks
190
+ - 8f0722d: Linting for TSDoc
191
+ - bc945d4: Safety when attempting to coerce to Symbol
192
+ - a21de8c: Augment ImportMeta instead of exporting a method
193
+ - dbd99b9: Cleanup files and configure vitest extension
194
+
195
+ ## 2.2.8
196
+
197
+ ### Patch Changes
198
+
199
+ - e9e359e: Port tests to vitest
200
+
201
+ ## 2.2.7
202
+
203
+ ### Patch Changes
204
+
205
+ - Fix readme badge for tests CI and make file ext consistent in workflows dir
206
+
207
+ ## 2.2.6
208
+
209
+ ### Patch Changes
210
+
211
+ - 01b1ba0: Fix deno missing mocha types error by explicitly importing mocha types in test files
212
+ - 0be2af4: Better test names and some badge formatting in README. Also condensed NOTICE and the other license file into one NOTICE.md file.
213
+
214
+ ## 2.2.5
215
+
216
+ ### Patch Changes
217
+
218
+ - 7bb1923: Fixing types and removing `Error.captureStackTrace` to follow ECMAScript specifications
219
+
220
+ ## 2.2.4
221
+
222
+ ### Patch Changes
223
+
224
+ - 435a738: Add jsr publishing support and a test for the previous version's type-error patch
225
+ - f17ca67: Bump deps: @types/node
226
+ - 6d47364: Bump deps: @typescript-eslint/parser
227
+ - fe12794: Bump deps: typescript-eslint
228
+
229
+ ## 2.2.3
230
+
231
+ ### Patch Changes
232
+
233
+ - 73162d8: `undefined` as fallback causing type errors when provided with a BuiltIn or Array Converter
234
+ - d56fe89: Fix incorrect example in the README for URL converter and add some comments for `@Envapt` overloads
235
+
236
+ ## 2.2.2
237
+
238
+ ### Patch Changes
239
+
240
+ - 6c1caaa: Fix grammar mistake in README
241
+
242
+ ## 2.2.1
243
+
244
+ ### Patch Changes
245
+
246
+ - f1c81cc: Make clear that Envapt can be used with JavaScript too
247
+
248
+ ## 2.2.0
249
+
250
+ ### Minor Changes
251
+
252
+ - a74e97d: Tagged Template resolver for a easily "one-lining" multiple parsed envs in a string literal. It also supports template variables like `${VAR}`! Check README for usage and examples.
253
+
254
+ ### Patch Changes
255
+
256
+ - c479505: Missing export for `EnvaptError`
257
+ - f15ddc9: Fix cache collisions on Envapt used on a static property in one class, and an instance property in another class with the property name being the same for both
258
+ - 447aaa4: Fix incorrect "main" export in package.json
259
+
260
+ ## 2.1.1
261
+
262
+ ### Patch Changes
263
+
264
+ - "boilerplate of parsing" → "boilerplate of transforming parsed" in README
265
+
266
+ ## 2.1.0
267
+
268
+ ### Minor Changes
269
+
270
+ - Fix missing `.mjs` build
271
+
272
+ ## 2.0.0
273
+
274
+ ### Major Changes
275
+
276
+ - 7e3a440: Strict Runtime Validation
277
+ - **BREAKING:** Runtime type validation between converter return types and fallback values exists now... and it's strict!
278
+ - But only for built-in and array converters. You are free to do what you want with custom converters.
279
+ - EnvaptError codes actually make sense now instead of the random numbers they were before
280
+ - Added primitive type coercion validation and better error handling
281
+
282
+ Since this didn't exist before, it will break existing code that was previously passing incorrect types.
283
+
284
+ - 7e3a440: Major Type Inference Improvements
285
+ - **BREAKING:** Improved type inference for `@Envapt` decorator with better type safety
286
+ - Fallbacks and Converters are also validated against each other
287
+ - Fallback always decides the type and converter has to match it, except when using a Primitive constructor.
288
+ - Fixed incorrect type inference in **many** cases
289
+ - **BREAKING:** `@Envapt` won't allow you to use its Classic API for any custom converters anymore. Please use the decorator's Modern API, or the Functional API instead.
290
+ - I updated the overloads for `@Envapt` which fixed most of the type inference issues and also the point above.
291
+
292
+ Improved type checking may break existing code that was previously passing incorrect types. Typecheck your files after you update pls.
293
+
294
+ Decorators don't exactly set the value they return to the property they decorate, so the inferred type you see on hover for `@Envapt` will be the type of the converter rather than the type of the property it'll set. Although, for the functional API, the type will be the type of the property it'll set.
295
+
296
+ ### Minor Changes
297
+
298
+ - 7e3a440: Enums for Built-in Converters
299
+ - Added `Converters` enum because they look better than string literals and provide better DX
300
+ - Allows using `Converters.String`, `Converters.Number`, etc. instead of string literals
301
+ - Maintains backward compatibility with string literal converter names
302
+
303
+ - 7e3a440: Customize Dotenv Configs
304
+ - Change how dotenv loads your env files. (Excludes the `path` and `processEnv` options because Envapter handles those)
305
+ - `Envapter.dotenvConfig` property for setting encoding, debug, override, and other dotenv options
306
+ - Now validates the file paths you provide to ensure they exist
307
+
308
+ - 7e3a440: New Functional API Methods
309
+ - Added `getUsing()` method for using built-in converters functionally
310
+ - Type overrides in `getUsing<T>()` if you need to specify a different type than the converter's inferred return type
311
+ - Added `getWith()` method for using custom converter functions functionally
312
+ - Functional API actually knows that a value won't be undefined if you pass a fallback value now
313
+
314
+ ### Patch Changes
315
+
316
+ - 7e3a440: Dev and Testing Improvements
317
+ - Coverage for codecov
318
+ - Tests for BigInt and Symbol types
319
+ - Tests for primitive type coercion and multi-line environment variables
320
+ - LOTS more tests I don't remember
321
+
322
+ - 7e3a440: Refactored Some Code
323
+ - Envapter went over 400 lines and eslint started crying. It was a sign.
324
+ - It's basically a mixin using inheritance now. Nothing changed for the user though.
325
+ - Some Types were removed from the public API because they didn't have any use outside of internal code.
326
+
327
+ - 7e3a440: Make README.md pretty 🙏🏻
328
+ - Also shorten some scripts in package.json and update files that use these scripts