envapt 8.2.0 → 8.2.1

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.
Files changed (124) hide show
  1. package/CHANGELOG.md +12 -15
  2. package/dist/node/config.cjs.map +1 -1
  3. package/dist/node/config.mjs.map +1 -1
  4. package/dist/node/converters/BuiltInConverters.cjs.map +1 -1
  5. package/dist/node/converters/BuiltInConverters.mjs.map +1 -1
  6. package/dist/node/converters/Converters.cjs.map +1 -1
  7. package/dist/node/converters/Converters.mjs.map +1 -1
  8. package/dist/node/converters/ListOfBuiltInConverters.cjs.map +1 -1
  9. package/dist/node/converters/ListOfBuiltInConverters.mjs.map +1 -1
  10. package/dist/node/converters/ValueConverter.cjs.map +1 -1
  11. package/dist/node/converters/ValueConverter.mjs.map +1 -1
  12. package/dist/node/core/AdvancedMethods.cjs.map +1 -1
  13. package/dist/node/core/AdvancedMethods.mjs.map +1 -1
  14. package/dist/node/core/EnvapterBase.cjs.map +1 -1
  15. package/dist/node/core/EnvapterBase.mjs.map +1 -1
  16. package/dist/node/core/EnvironmentMethods.cjs.map +1 -1
  17. package/dist/node/core/EnvironmentMethods.mjs.map +1 -1
  18. package/dist/node/core/PrimitiveMethods.cjs.map +1 -1
  19. package/dist/node/core/PrimitiveMethods.mjs.map +1 -1
  20. package/dist/node/core/engine.cjs.map +1 -1
  21. package/dist/node/core/engine.mjs.map +1 -1
  22. package/dist/node/core/missing.cjs.map +1 -1
  23. package/dist/node/core/missing.mjs.map +1 -1
  24. package/dist/node/core/paths.cjs.map +1 -1
  25. package/dist/node/core/paths.mjs.map +1 -1
  26. package/dist/node/core/state.cjs.map +1 -1
  27. package/dist/node/core/state.mjs.map +1 -1
  28. package/dist/node/decorators/legacy/Envapt.cjs.map +1 -1
  29. package/dist/node/decorators/legacy/Envapt.mjs.map +1 -1
  30. package/dist/node/decorators/legacy/SugarDecorators.cjs.map +1 -1
  31. package/dist/node/decorators/legacy/SugarDecorators.mjs.map +1 -1
  32. package/dist/node/decorators/modern/Envapt.cjs.map +1 -1
  33. package/dist/node/decorators/modern/Envapt.mjs.map +1 -1
  34. package/dist/node/decorators/modern/SugarDecorators.cjs.map +1 -1
  35. package/dist/node/decorators/modern/SugarDecorators.mjs.map +1 -1
  36. package/dist/node/decorators/modern/createAccessorDecorator.cjs.map +1 -1
  37. package/dist/node/decorators/modern/createAccessorDecorator.mjs.map +1 -1
  38. package/dist/node/decorators/resolveDecoratorValue.cjs.map +1 -1
  39. package/dist/node/decorators/resolveDecoratorValue.mjs.map +1 -1
  40. package/dist/node/engine/Envapter.cjs.map +1 -1
  41. package/dist/node/engine/Envapter.mjs.map +1 -1
  42. package/dist/node/engine/NodeEnvapter.cjs.map +1 -1
  43. package/dist/node/engine/NodeEnvapter.mjs.map +1 -1
  44. package/dist/node/engine/TemplateResolver.cjs.map +1 -1
  45. package/dist/node/engine/TemplateResolver.mjs.map +1 -1
  46. package/dist/node/engine/Validators.cjs.map +1 -1
  47. package/dist/node/engine/Validators.mjs.map +1 -1
  48. package/dist/node/infra/Debug.cjs.map +1 -1
  49. package/dist/node/infra/Debug.mjs.map +1 -1
  50. package/dist/node/infra/Dotenv.cjs.map +1 -1
  51. package/dist/node/infra/Dotenv.mjs.map +1 -1
  52. package/dist/node/infra/Error.cjs.map +1 -1
  53. package/dist/node/infra/Error.mjs.map +1 -1
  54. package/dist/node/infra/recase.cjs.map +1 -1
  55. package/dist/node/infra/recase.mjs.map +1 -1
  56. package/dist/node/infra/runtime.cjs.map +1 -1
  57. package/dist/node/infra/runtime.mjs.map +1 -1
  58. package/dist/node/sources/FileSource.cjs.map +1 -1
  59. package/dist/node/sources/FileSource.mjs.map +1 -1
  60. package/dist/node/sources/PortableSource.cjs.map +1 -1
  61. package/dist/node/sources/PortableSource.mjs.map +1 -1
  62. package/dist/node/sources/UnboundSource.cjs.map +1 -1
  63. package/dist/node/sources/UnboundSource.mjs.map +1 -1
  64. package/dist/node/sources/coerce.cjs.map +1 -1
  65. package/dist/node/sources/coerce.mjs.map +1 -1
  66. package/dist/node/sources/merge.cjs.map +1 -1
  67. package/dist/node/sources/merge.mjs.map +1 -1
  68. package/dist/portable/converters/BuiltInConverters.mjs.map +1 -1
  69. package/dist/portable/converters/Converters.mjs.map +1 -1
  70. package/dist/portable/converters/ListOfBuiltInConverters.mjs.map +1 -1
  71. package/dist/portable/converters/ValueConverter.mjs.map +1 -1
  72. package/dist/portable/core/AdvancedMethods.mjs.map +1 -1
  73. package/dist/portable/core/EnvapterBase.mjs.map +1 -1
  74. package/dist/portable/core/EnvironmentMethods.mjs.map +1 -1
  75. package/dist/portable/core/PrimitiveMethods.mjs.map +1 -1
  76. package/dist/portable/core/engine.mjs.map +1 -1
  77. package/dist/portable/core/missing.mjs.map +1 -1
  78. package/dist/portable/core/paths.mjs.map +1 -1
  79. package/dist/portable/core/state.mjs.map +1 -1
  80. package/dist/portable/decorators/legacy/Envapt.mjs.map +1 -1
  81. package/dist/portable/decorators/legacy/SugarDecorators.mjs.map +1 -1
  82. package/dist/portable/decorators/modern/Envapt.mjs.map +1 -1
  83. package/dist/portable/decorators/modern/SugarDecorators.mjs.map +1 -1
  84. package/dist/portable/decorators/modern/createAccessorDecorator.mjs.map +1 -1
  85. package/dist/portable/decorators/resolveDecoratorValue.mjs.map +1 -1
  86. package/dist/portable/engine/Envapter.mjs.map +1 -1
  87. package/dist/portable/engine/PortableEnvapter.mjs.map +1 -1
  88. package/dist/portable/engine/TemplateResolver.mjs.map +1 -1
  89. package/dist/portable/engine/Validators.mjs.map +1 -1
  90. package/dist/portable/engine/fileApiStub.mjs.map +1 -1
  91. package/dist/portable/infra/Debug.mjs.map +1 -1
  92. package/dist/portable/infra/Dotenv.mjs.map +1 -1
  93. package/dist/portable/infra/Error.mjs.map +1 -1
  94. package/dist/portable/infra/recase.mjs.map +1 -1
  95. package/dist/portable/infra/runtime.mjs.map +1 -1
  96. package/dist/portable/sources/PortableSource.mjs.map +1 -1
  97. package/dist/portable/sources/UnboundSource.mjs.map +1 -1
  98. package/dist/portable/sources/coerce.mjs.map +1 -1
  99. package/dist/portable/sources/merge.mjs.map +1 -1
  100. package/dist/types/converters/Converters.d.mts +4 -5
  101. package/dist/types/core/AdvancedMethods.d.mts +6 -12
  102. package/dist/types/core/EnvapterBase.d.mts +9 -12
  103. package/dist/types/core/Environment.d.mts +1 -2
  104. package/dist/types/core/EnvironmentMethods.d.mts +1 -6
  105. package/dist/types/core/PrimitiveMethods.d.mts +1 -5
  106. package/dist/types/decorators/legacy/Envapt.d.mts +17 -19
  107. package/dist/types/decorators/legacy/SugarDecorators.d.mts +12 -13
  108. package/dist/types/decorators/modern/Envapt.d.mts +17 -19
  109. package/dist/types/decorators/modern/SugarDecorators.d.mts +12 -13
  110. package/dist/types/engine/Envapter.d.mts +2 -2
  111. package/dist/types/engine/NodeEnvapter.d.mts +5 -6
  112. package/dist/types/engine/PortableEnvapter.d.mts +5 -7
  113. package/dist/types/infra/Debug.d.mts +3 -5
  114. package/dist/types/infra/Dotenv.d.mts +2 -4
  115. package/dist/types/infra/Error.d.mts +4 -5
  116. package/dist/types/infra/StandardSchema.d.mts +5 -7
  117. package/dist/types/sources/FileSource.d.mts +1 -2
  118. package/dist/types/sources/PortableSource.d.mts +7 -8
  119. package/dist/types/sources/merge.d.mts +1 -2
  120. package/dist/types/types/Conversion.d.mts +0 -4
  121. package/dist/types/types/Decorator.d.mts +2 -3
  122. package/dist/types/types/Options.d.mts +7 -7
  123. package/dist/types/types/Schema.d.mts +1 -1
  124. package/package.json +9 -11
@@ -1 +1 @@
1
- {"version":3,"file":"TemplateResolver.mjs","names":[],"sources":["../../../src/engine/TemplateResolver.ts"],"sourcesContent":["import { isMissing } from '../core/missing';\nimport { state } from '../core/state';\nimport { debugWarn } from '../infra/Debug';\nimport { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\n\nimport type { EnvapterService } from '../types/Env';\n\n/**\n * Resolve `${VAR}` template references in environment values, guarding against circular\n * references and missing variables.\n * @internal\n */\nexport class TemplateResolver {\n private readonly TEMPLATE_REGEX = /\\${\\w*}/g;\n\n constructor(private readonly envService: EnvapterService) {}\n\n resolveTemplate(key: string, value: string, stack: Set<string> = new Set<string>()): string {\n stack.add(key);\n const strict = state.strict;\n\n const out = value.replace(this.TEMPLATE_REGEX, (template) => {\n const variable = template.slice(2, -1);\n\n if (stack.has(variable)) return template; // cycle, preserve\n\n const raw = this.envService.getRaw(variable);\n if (raw === undefined || isMissing(raw)) {\n if (strict) {\n throw new EnvaptError(\n EnvaptErrorCodes.MissingEnvValue,\n `Cannot resolve template variable \"\\${${variable}}\": value is missing or empty.`\n );\n }\n debugWarn(`unresolved template \\${${variable}} preserved as literal`);\n return template; // missing or empty, preserve\n }\n\n const resolved = this.resolveTemplate(variable, raw, new Set(stack));\n\n // If resolution still references the current key, skip replacement (indirect cycle)\n if (resolved.includes(`\\${${key}}`)) return template;\n\n // If nothing changed (unresolved placeholders stayed), also preserve original template\n if (resolved === raw && /\\$\\{[^}]*\\}/.test(resolved)) return template;\n\n return resolved;\n });\n\n stack.delete(key);\n return out;\n }\n}\n"],"mappings":"0LAYA,IAAa,EAAb,KAA8B,CAGG,WAF7B,eAAkC,WAElC,YAAY,EAA8C,CAA7B,KAAA,WAAA,CAA8B,CAE3D,gBAAgB,EAAa,EAAe,EAAqB,IAAI,IAAuB,CACxF,EAAM,IAAI,CAAG,EACb,IAAM,EAAS,EAAM,OAEf,EAAM,EAAM,QAAQ,KAAK,eAAiB,GAAa,CACzD,IAAM,EAAW,EAAS,MAAM,EAAG,EAAE,EAErC,GAAI,EAAM,IAAI,CAAQ,EAAG,OAAO,EAEhC,IAAM,EAAM,KAAK,WAAW,OAAO,CAAQ,EAC3C,GAAI,IAAQ,IAAA,IAAa,EAAU,CAAG,EAAG,CACrC,GAAI,EACA,MAAM,IAAI,EAAA,IAEN,wCAAwC,EAAS,+BACrD,EAGJ,OADA,EAAU,0BAA0B,EAAS,uBAAuB,EAC7D,CACX,CAEA,IAAM,EAAW,KAAK,gBAAgB,EAAU,EAAK,IAAI,IAAI,CAAK,CAAC,EAQnE,OALI,EAAS,SAAS,MAAM,EAAI,EAAE,GAG9B,IAAa,GAAO,cAAc,KAAK,CAAQ,EAAU,EAEtD,CACX,CAAC,EAGD,OADA,EAAM,OAAO,CAAG,EACT,CACX,CACJ"}
1
+ {"version":3,"file":"TemplateResolver.mjs","names":[],"sources":["../../../src/engine/TemplateResolver.ts"],"sourcesContent":["import { isMissing } from '../core/missing';\nimport { state } from '../core/state';\nimport { debugWarn } from '../infra/Debug';\nimport { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\n\nimport type { EnvapterService } from '../types/Env';\n\nexport class TemplateResolver {\n private readonly TEMPLATE_REGEX = /\\${\\w*}/g;\n\n constructor(private readonly envService: EnvapterService) {}\n\n resolveTemplate(key: string, value: string, stack: Set<string> = new Set<string>()): string {\n stack.add(key);\n const strict = state.strict;\n\n const out = value.replace(this.TEMPLATE_REGEX, (template) => {\n const variable = template.slice(2, -1);\n\n if (stack.has(variable)) return template; // cycle, preserve\n\n const raw = this.envService.getRaw(variable);\n if (raw === undefined || isMissing(raw)) {\n if (strict) {\n throw new EnvaptError(\n EnvaptErrorCodes.MissingEnvValue,\n `Cannot resolve template variable \"\\${${variable}}\": value is missing or empty.`\n );\n }\n debugWarn(`unresolved template \\${${variable}} preserved as literal`);\n return template; // missing or empty, preserve\n }\n\n const resolved = this.resolveTemplate(variable, raw, new Set(stack));\n\n // indirect cycle\n if (resolved.includes(`\\${${key}}`)) return template;\n\n // the nested value still has unresolved placeholders\n if (resolved === raw && /\\$\\{[^}]*\\}/.test(resolved)) return template;\n\n return resolved;\n });\n\n stack.delete(key);\n return out;\n }\n}\n"],"mappings":"0LAOA,IAAa,EAAb,KAA8B,CAGG,WAF7B,eAAkC,WAElC,YAAY,EAA8C,CAA7B,KAAA,WAAA,CAA8B,CAE3D,gBAAgB,EAAa,EAAe,EAAqB,IAAI,IAAuB,CACxF,EAAM,IAAI,CAAG,EACb,IAAM,EAAS,EAAM,OAEf,EAAM,EAAM,QAAQ,KAAK,eAAiB,GAAa,CACzD,IAAM,EAAW,EAAS,MAAM,EAAG,EAAE,EAErC,GAAI,EAAM,IAAI,CAAQ,EAAG,OAAO,EAEhC,IAAM,EAAM,KAAK,WAAW,OAAO,CAAQ,EAC3C,GAAI,IAAQ,IAAA,IAAa,EAAU,CAAG,EAAG,CACrC,GAAI,EACA,MAAM,IAAI,EAAA,IAEN,wCAAwC,EAAS,+BACrD,EAGJ,OADA,EAAU,0BAA0B,EAAS,uBAAuB,EAC7D,CACX,CAEA,IAAM,EAAW,KAAK,gBAAgB,EAAU,EAAK,IAAI,IAAI,CAAK,CAAC,EAQnE,OALI,EAAS,SAAS,MAAM,EAAI,EAAE,GAG9B,IAAa,GAAO,cAAc,KAAK,CAAQ,EAAU,EAEtD,CACX,CAAC,EAGD,OADA,EAAM,OAAO,CAAG,EACT,CACX,CACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"Validators.mjs","names":[],"sources":["../../../src/engine/Validators.ts"],"sourcesContent":["// Import the converter modules directly, not via the `./converters` barrel: that barrel pulls in\n// ValueConverter, which imports this Validator, so a barrel import here would cycle.\nimport { isArrayOf } from '../converters/Converters';\nimport { ListOfBuiltInConverters, BuiltInConverterTypeCheckers } from '../converters/ListOfBuiltInConverters';\nimport { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\n\nimport type { ArrayOf, ConverterToken } from '../converters/Converters';\nimport type { EnvFileOptions } from '../infra/Dotenv';\nimport type { StandardSchemaV1 } from '../infra/StandardSchema';\nimport type { BuiltInConverter, ConverterFunction, EnvaptConverter, FileApiMode } from '../types';\n\n// eslint-disable-next-line @typescript-eslint/no-extraneous-class -- cohesive dispatch of stateless type guards, same shape as BuiltInConverters\nexport class Validator {\n /**\n * Check if a value is a built-in scalar converter token\n */\n static isBuiltInConverter<TFallback>(value: EnvaptConverter<TFallback>): value is BuiltInConverter {\n if (typeof value === 'string') return ListOfBuiltInConverters.includes(value);\n return false;\n }\n\n /**\n * Check if a value is an `ArrayOf<...>` token produced by {@link Converters.array}.\n */\n static isArrayConverter(value: unknown): value is ArrayOf {\n return isArrayOf(value);\n }\n\n // Structural check: `version === 1` + callable `validate` is the minimum dispatchable\n // shape per the Standard Schema spec.\n static isStandardSchema(value: unknown): value is StandardSchemaV1 {\n if (typeof value !== 'object' || value === null) return false;\n if (!('~standard' in value)) return false;\n const slot = value['~standard'];\n if (typeof slot !== 'object' || slot === null) return false;\n const props = slot as { version?: unknown; validate?: unknown };\n return props.version === 1 && typeof props.validate === 'function';\n }\n\n static customConvertor<TFallback>(\n converter: EnvaptConverter<TFallback>\n ): asserts converter is ConverterFunction<TFallback> {\n if (typeof converter !== 'function') {\n throw new EnvaptError(\n EnvaptErrorCodes.InvalidCustomConverter,\n `Custom converter must be a function, got ${typeof converter}.`\n );\n }\n }\n\n /**\n * Runtime validation that the `ArrayOf<...>` configuration is well-formed.\n */\n static arrayConverter(value: unknown): asserts value is ArrayOf {\n if (!isArrayOf(value)) {\n throw new EnvaptError(\n EnvaptErrorCodes.InvalidArrayConverterType,\n 'Expected an ArrayOf<...> token produced by Converters.array(...)'\n );\n }\n\n if (typeof value.delimiter !== 'string' || value.delimiter.length === 0) {\n throw new EnvaptError(\n EnvaptErrorCodes.MissingDelimiter,\n `ArrayOf<...> requires a non-empty string delimiter, got ${typeof value.delimiter}`\n );\n }\n\n const elementOf = value.of;\n const isScalar = typeof elementOf === 'string' && ListOfBuiltInConverters.includes(elementOf);\n const isCustomFn = typeof elementOf === 'function';\n if (!isScalar && !isCustomFn) {\n throw new EnvaptError(\n EnvaptErrorCodes.InvalidArrayConverterType,\n `ArrayOf<...> element (\"of\") must be a built-in scalar token or a function, got ${typeof elementOf}`\n );\n }\n }\n\n /**\n * Validate that a string is a valid built-in scalar converter token\n */\n static builtInConverter(value: unknown): asserts value is BuiltInConverter {\n if (typeof value !== 'string') {\n throw new EnvaptError(EnvaptErrorCodes.InvalidConverterType, `Expected string, got ${typeof value}`);\n }\n\n if (!ListOfBuiltInConverters.includes(value as ConverterToken)) {\n throw new EnvaptError(\n EnvaptErrorCodes.InvalidBuiltInConverter,\n `\"${value}\" is not a valid converter type. Valid types are: ${ListOfBuiltInConverters.join(',')}`\n );\n }\n }\n\n /**\n * Validate that fallback type matches the converter's return type for built-in converters\n */\n static validateBuiltInConverterFallback(converter: BuiltInConverter, fallback: unknown): void {\n const typeChecker = BuiltInConverterTypeCheckers[converter];\n if (!typeChecker(fallback)) {\n throw new EnvaptError(\n EnvaptErrorCodes.FallbackConverterTypeMismatch,\n `Fallback type does not match converter \"${converter}\". Expected ${converter} compatible type.`\n );\n }\n }\n\n /**\n * Validate that all elements in an array fallback have consistent types\n */\n static validateArrayFallbackElementTypes(fallback: unknown[]): void {\n if (fallback.length === 0) return;\n\n const firstElementType = typeof fallback[0];\n const hasInconsistentTypes = fallback.some((element, index) => {\n if (index === 0) return false;\n return typeof element !== firstElementType;\n });\n\n if (hasInconsistentTypes) {\n throw new EnvaptError(\n EnvaptErrorCodes.ArrayFallbackElementTypeMismatch,\n `All elements in array fallback must have the same type. Found mixed types.`\n );\n }\n }\n\n /**\n * Validate that an `ArrayOf<...>` element converter matches the runtime types of its\n * fallback elements. For `Converters.Time` arrays the element-time-string format is also\n * checked here.\n */\n static validateArrayConverterElementTypeMatch(elementOf: ArrayOf['of'], fallback: unknown[]): void {\n if (fallback.length === 0) return;\n\n if (typeof elementOf === 'function') {\n // Custom element converters can return anything; we can't statically validate the fallback shape.\n return;\n }\n\n const elementToken = elementOf;\n\n // Time array fallbacks may be number[] OR string[] (TimeFallback[]).\n if (elementToken === 'time') {\n const everyNumber = fallback.every((v) => typeof v === 'number');\n const everyString = fallback.every((v) => typeof v === 'string');\n if (!everyNumber && !everyString) {\n throw new EnvaptError(\n EnvaptErrorCodes.ArrayFallbackElementTypeMismatch,\n 'Time array fallback must be all numbers or all time-string entries.'\n );\n }\n return;\n }\n\n const typeChecker = BuiltInConverterTypeCheckers[elementToken];\n const firstElement = fallback[0];\n if (!typeChecker(firstElement)) {\n throw new EnvaptError(\n EnvaptErrorCodes.ArrayFallbackElementTypeMismatch,\n `Array converter type \"${elementToken}\" does not match fallback element type. Expected ${elementToken} compatible elements.`\n );\n }\n }\n\n /**\n * Check if a value is a primitive constructor\n */\n static isPrimitiveConstructor(\n value: unknown\n ): value is typeof String | typeof Number | typeof Boolean | typeof BigInt | typeof Symbol {\n return value === String || value === Number || value === Boolean || value === BigInt || value === Symbol;\n }\n\n /**\n * Safely coerce a fallback value using a primitive constructor\n */\n static coercePrimitiveFallback<CoercedType>(\n converter: typeof String | typeof Number | typeof Boolean | typeof BigInt | typeof Symbol,\n fallback: unknown\n ): CoercedType {\n if (this.isCorrectPrimitiveType(converter, fallback)) return fallback as CoercedType;\n return this.performPrimitiveCoercion<CoercedType>(converter, fallback);\n }\n\n private static isCorrectPrimitiveType(\n converter: typeof String | typeof Number | typeof Boolean | typeof BigInt | typeof Symbol,\n fallback: unknown\n ): boolean {\n if (converter === String && typeof fallback === 'string') return true;\n if (converter === Number && typeof fallback === 'number') return true;\n if (converter === Boolean && typeof fallback === 'boolean') return true;\n if (converter === BigInt && typeof fallback === 'bigint') return true;\n if (converter === Symbol && typeof fallback === 'symbol') return true;\n return false;\n }\n\n private static performPrimitiveCoercion<CoercedType>(\n converter: typeof String | typeof Number | typeof Boolean | typeof BigInt | typeof Symbol,\n fallback: unknown\n ): CoercedType {\n try {\n if (converter === String) return String(fallback) as CoercedType;\n if (converter === Number) return Number(fallback) as CoercedType;\n if (converter === Boolean) return Boolean(fallback) as CoercedType;\n if (converter === BigInt) return BigInt(fallback as string | number | bigint) as CoercedType;\n /* v8 ignore next -- @preserve */\n if (converter === Symbol) return Symbol.for(String(fallback)) as CoercedType;\n } catch (error) {\n throw new EnvaptError(\n EnvaptErrorCodes.PrimitiveCoercionFailed,\n `Failed to coerce fallback value using ${converter.name}: ${(error as Error).message}`\n );\n }\n\n /* v8 ignore next -- @preserve */\n throw new EnvaptError(\n EnvaptErrorCodes.PrimitiveCoercionFailed,\n `Unknown primitive converter: ${converter.name}`\n );\n }\n\n /**\n * Reject non-boolean inputs to `Envapter.syncProcessEnv` so a truthy typo\n * (`'true'`, `1`, etc.) does not silently enable the mirror.\n */\n static validateSyncProcessEnv(value: unknown): asserts value is boolean {\n if (typeof value !== 'boolean') {\n throw new EnvaptError(\n EnvaptErrorCodes.InvalidUserDefinedConfig,\n `Envapter.syncProcessEnv must be a boolean, got ${typeof value}.`\n );\n }\n }\n\n static validateFileApiMode(value: unknown): asserts value is FileApiMode {\n if (value !== 'warn' && value !== 'throw') {\n const got = typeof value === 'string' ? `'${value}'` : typeof value;\n throw new EnvaptError(\n EnvaptErrorCodes.InvalidUserDefinedConfig,\n `Envapter.fileApiMode must be 'warn' or 'throw', got ${got}.`\n );\n }\n }\n\n /**\n * Make sure the user hasn't provided prohibited options in their dotenv config\n */\n static validateEnvFileOptions(config: object): config is EnvFileOptions {\n const validKeys = new Set(['encoding', 'override']);\n const invalidKeys = Object.keys(config).filter((key) => !validKeys.has(key));\n\n if (invalidKeys.length > 0) {\n throw new EnvaptError(\n EnvaptErrorCodes.InvalidUserDefinedConfig,\n `Invalid envFileOptions: ${invalidKeys.join(', ')}. Allowed options: ${Array.from(validKeys).join(', ')}. For debug output, use Envapter.debug or the ENVAPT_DEBUG env var.`\n );\n }\n\n return true;\n }\n\n // Existence is probed through the caller-supplied `fileExists` (backed by the bound source) so\n // this stays free of `node:fs`.\n static validateEnvFilesExist(paths: string[], fileExists: (path: string) => boolean): void {\n const missing = paths.filter((p) => !fileExists(p));\n\n if (missing.length > 0) {\n throw new EnvaptError(\n EnvaptErrorCodes.EnvFilesNotFound,\n `Environment file not found at path: ${missing.join(', ')}`\n );\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Validators.mjs","names":[],"sources":["../../../src/engine/Validators.ts"],"sourcesContent":["// the ./converters barrel imports ValueConverter, which imports this file\nimport { isArrayOf } from '../converters/Converters';\nimport { ListOfBuiltInConverters, BuiltInConverterTypeCheckers } from '../converters/ListOfBuiltInConverters';\nimport { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\n\nimport type { ArrayOf, ConverterToken } from '../converters/Converters';\nimport type { EnvFileOptions } from '../infra/Dotenv';\nimport type { StandardSchemaV1 } from '../infra/StandardSchema';\nimport type { BuiltInConverter, ConverterFunction, EnvaptConverter, FileApiMode } from '../types';\n\n// eslint-disable-next-line @typescript-eslint/no-extraneous-class -- cohesive dispatch of stateless type guards, same shape as BuiltInConverters\nexport class Validator {\n static isBuiltInConverter<TFallback>(value: EnvaptConverter<TFallback>): value is BuiltInConverter {\n if (typeof value === 'string') return ListOfBuiltInConverters.includes(value);\n return false;\n }\n\n static isArrayConverter(value: unknown): value is ArrayOf {\n return isArrayOf(value);\n }\n\n static isStandardSchema(value: unknown): value is StandardSchemaV1 {\n if (typeof value !== 'object' || value === null) return false;\n if (!('~standard' in value)) return false;\n const slot = value['~standard'];\n if (typeof slot !== 'object' || slot === null) return false;\n const props = slot as { version?: unknown; validate?: unknown };\n return props.version === 1 && typeof props.validate === 'function';\n }\n\n static customConvertor<TFallback>(\n converter: EnvaptConverter<TFallback>\n ): asserts converter is ConverterFunction<TFallback> {\n if (typeof converter !== 'function') {\n throw new EnvaptError(\n EnvaptErrorCodes.InvalidCustomConverter,\n `Custom converter must be a function, got ${typeof converter}.`\n );\n }\n }\n\n static arrayConverter(value: unknown): asserts value is ArrayOf {\n if (!isArrayOf(value)) {\n throw new EnvaptError(\n EnvaptErrorCodes.InvalidArrayConverterType,\n 'Expected an ArrayOf<...> token produced by Converters.array(...)'\n );\n }\n\n if (typeof value.delimiter !== 'string' || value.delimiter.length === 0) {\n throw new EnvaptError(\n EnvaptErrorCodes.MissingDelimiter,\n `ArrayOf<...> requires a non-empty string delimiter, got ${typeof value.delimiter}`\n );\n }\n\n const elementOf = value.of;\n const isScalar = typeof elementOf === 'string' && ListOfBuiltInConverters.includes(elementOf);\n const isCustomFn = typeof elementOf === 'function';\n if (!isScalar && !isCustomFn) {\n throw new EnvaptError(\n EnvaptErrorCodes.InvalidArrayConverterType,\n `ArrayOf<...> element (\"of\") must be a built-in scalar token or a function, got ${typeof elementOf}`\n );\n }\n }\n\n static builtInConverter(value: unknown): asserts value is BuiltInConverter {\n if (typeof value !== 'string') {\n throw new EnvaptError(EnvaptErrorCodes.InvalidConverterType, `Expected string, got ${typeof value}`);\n }\n\n if (!ListOfBuiltInConverters.includes(value as ConverterToken)) {\n throw new EnvaptError(\n EnvaptErrorCodes.InvalidBuiltInConverter,\n `\"${value}\" is not a valid converter type. Valid types are: ${ListOfBuiltInConverters.join(',')}`\n );\n }\n }\n\n static validateBuiltInConverterFallback(converter: BuiltInConverter, fallback: unknown): void {\n const typeChecker = BuiltInConverterTypeCheckers[converter];\n if (!typeChecker(fallback)) {\n throw new EnvaptError(\n EnvaptErrorCodes.FallbackConverterTypeMismatch,\n `Fallback type does not match converter \"${converter}\". Expected ${converter} compatible type.`\n );\n }\n }\n\n static validateArrayFallbackElementTypes(fallback: unknown[]): void {\n if (fallback.length === 0) return;\n\n const firstElementType = typeof fallback[0];\n const hasInconsistentTypes = fallback.some((element, index) => {\n if (index === 0) return false;\n return typeof element !== firstElementType;\n });\n\n if (hasInconsistentTypes) {\n throw new EnvaptError(\n EnvaptErrorCodes.ArrayFallbackElementTypeMismatch,\n `All elements in array fallback must have the same type. Found mixed types.`\n );\n }\n }\n\n static validateArrayConverterElementTypeMatch(elementOf: ArrayOf['of'], fallback: unknown[]): void {\n if (fallback.length === 0) return;\n\n if (typeof elementOf === 'function') {\n // a custom element converter can return any type\n return;\n }\n\n const elementToken = elementOf;\n\n if (elementToken === 'time') {\n const everyNumber = fallback.every((v) => typeof v === 'number');\n const everyString = fallback.every((v) => typeof v === 'string');\n if (!everyNumber && !everyString) {\n throw new EnvaptError(\n EnvaptErrorCodes.ArrayFallbackElementTypeMismatch,\n 'Time array fallback must be all numbers or all time-string entries.'\n );\n }\n return;\n }\n\n const typeChecker = BuiltInConverterTypeCheckers[elementToken];\n const firstElement = fallback[0];\n if (!typeChecker(firstElement)) {\n throw new EnvaptError(\n EnvaptErrorCodes.ArrayFallbackElementTypeMismatch,\n `Array converter type \"${elementToken}\" does not match fallback element type. Expected ${elementToken} compatible elements.`\n );\n }\n }\n\n static isPrimitiveConstructor(\n value: unknown\n ): value is typeof String | typeof Number | typeof Boolean | typeof BigInt | typeof Symbol {\n return value === String || value === Number || value === Boolean || value === BigInt || value === Symbol;\n }\n\n static coercePrimitiveFallback<CoercedType>(\n converter: typeof String | typeof Number | typeof Boolean | typeof BigInt | typeof Symbol,\n fallback: unknown\n ): CoercedType {\n if (this.isCorrectPrimitiveType(converter, fallback)) return fallback as CoercedType;\n return this.performPrimitiveCoercion<CoercedType>(converter, fallback);\n }\n\n private static isCorrectPrimitiveType(\n converter: typeof String | typeof Number | typeof Boolean | typeof BigInt | typeof Symbol,\n fallback: unknown\n ): boolean {\n if (converter === String && typeof fallback === 'string') return true;\n if (converter === Number && typeof fallback === 'number') return true;\n if (converter === Boolean && typeof fallback === 'boolean') return true;\n if (converter === BigInt && typeof fallback === 'bigint') return true;\n if (converter === Symbol && typeof fallback === 'symbol') return true;\n return false;\n }\n\n private static performPrimitiveCoercion<CoercedType>(\n converter: typeof String | typeof Number | typeof Boolean | typeof BigInt | typeof Symbol,\n fallback: unknown\n ): CoercedType {\n try {\n if (converter === String) return String(fallback) as CoercedType;\n if (converter === Number) return Number(fallback) as CoercedType;\n if (converter === Boolean) return Boolean(fallback) as CoercedType;\n if (converter === BigInt) return BigInt(fallback as string | number | bigint) as CoercedType;\n /* v8 ignore next -- @preserve */\n if (converter === Symbol) return Symbol.for(String(fallback)) as CoercedType;\n } catch (error) {\n throw new EnvaptError(\n EnvaptErrorCodes.PrimitiveCoercionFailed,\n `Failed to coerce fallback value using ${converter.name}: ${(error as Error).message}`\n );\n }\n\n /* v8 ignore next -- @preserve */\n throw new EnvaptError(\n EnvaptErrorCodes.PrimitiveCoercionFailed,\n `Unknown primitive converter: ${converter.name}`\n );\n }\n\n // a truthy typo like 'true' or 1 would turn the mirror on\n static validateSyncProcessEnv(value: unknown): asserts value is boolean {\n if (typeof value !== 'boolean') {\n throw new EnvaptError(\n EnvaptErrorCodes.InvalidUserDefinedConfig,\n `Envapter.syncProcessEnv must be a boolean, got ${typeof value}.`\n );\n }\n }\n\n static validateFileApiMode(value: unknown): asserts value is FileApiMode {\n if (value !== 'warn' && value !== 'throw') {\n const got = typeof value === 'string' ? `'${value}'` : typeof value;\n throw new EnvaptError(\n EnvaptErrorCodes.InvalidUserDefinedConfig,\n `Envapter.fileApiMode must be 'warn' or 'throw', got ${got}.`\n );\n }\n }\n\n static validateEnvFileOptions(config: object): config is EnvFileOptions {\n const validKeys = new Set(['encoding', 'override']);\n const invalidKeys = Object.keys(config).filter((key) => !validKeys.has(key));\n\n if (invalidKeys.length > 0) {\n throw new EnvaptError(\n EnvaptErrorCodes.InvalidUserDefinedConfig,\n `Invalid envFileOptions: ${invalidKeys.join(', ')}. Allowed options: ${Array.from(validKeys).join(', ')}. For debug output, use Envapter.debug or the ENVAPT_DEBUG env var.`\n );\n }\n\n return true;\n }\n\n // fileExists comes from the caller because this file must not import node:fs\n static validateEnvFilesExist(paths: string[], fileExists: (path: string) => boolean): void {\n const missing = paths.filter((p) => !fileExists(p));\n\n if (missing.length > 0) {\n throw new EnvaptError(\n EnvaptErrorCodes.EnvFilesNotFound,\n `Environment file not found at path: ${missing.join(', ')}`\n );\n }\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"fileApiStub.mjs","names":[],"sources":["../../../src/engine/fileApiStub.ts"],"sourcesContent":["import { EnvapterBase } from '../core/EnvapterBase';\nimport { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\nimport { writeRuntimeLine } from '../infra/runtime';\n\n// EnvapterBase must never import this module back, or the chain cycles.\nconst warned = new Set<string>();\n\nfunction fileApiUnsupported(api: string): never {\n throw new EnvaptError(\n EnvaptErrorCodes.FileApiUnsupported,\n `Envapter.${api} requires a filesystem-backed source and is not available in this build.`\n );\n}\n\n// warns regardless of Envapter.debug (debugWarn would gate on it)\nfunction warnOnce(api: string): void {\n if (warned.has(api)) return;\n warned.add(api);\n writeRuntimeLine(\n `[envapt] Envapter.${api} is a filesystem-only API and has no effect in the portable build. ` +\n `Set Envapter.fileApiMode = 'throw' to throw instead.`\n );\n}\n\nexport function portableFileApiRead<Value>(api: string, fallback: Value): Value {\n if (EnvapterBase.fileApiMode === 'throw') fileApiUnsupported(api);\n warnOnce(api);\n // warn mode no-ops, so the read returns the getter's empty default rather than undefined\n return fallback;\n}\n\nexport function portableFileApiWrite(api: string): void {\n if (EnvapterBase.fileApiMode === 'throw') fileApiUnsupported(api);\n warnOnce(api);\n}\n"],"mappings":"iKAKA,MAAM,EAAS,IAAI,IAEnB,SAAS,EAAmB,EAAoB,CAC5C,MAAM,IAAI,EAAA,IAEN,YAAY,EAAI,yEACpB,CACJ,CAGA,SAAS,EAAS,EAAmB,CAC7B,EAAO,IAAI,CAAG,IAClB,EAAO,IAAI,CAAG,EACd,EACI,qBAAqB,EAAI,wHAE7B,EACJ,CAEA,SAAgB,EAA2B,EAAa,EAAwB,CAI5E,OAHI,EAAa,cAAgB,SAAS,EAAmB,CAAG,EAChE,EAAS,CAAG,EAEL,CACX,CAEA,SAAgB,EAAqB,EAAmB,CAChD,EAAa,cAAgB,SAAS,EAAmB,CAAG,EAChE,EAAS,CAAG,CAChB"}
1
+ {"version":3,"file":"fileApiStub.mjs","names":[],"sources":["../../../src/engine/fileApiStub.ts"],"sourcesContent":["import { EnvapterBase } from '../core/EnvapterBase';\nimport { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\nimport { writeRuntimeLine } from '../infra/runtime';\n\n// importing this from EnvapterBase would create an import cycle\nconst warned = new Set<string>();\n\nfunction fileApiUnsupported(api: string): never {\n throw new EnvaptError(\n EnvaptErrorCodes.FileApiUnsupported,\n `Envapter.${api} requires a filesystem-backed source and is not available in this build.`\n );\n}\n\n// writes even when Envapter.debug is silent\nfunction warnOnce(api: string): void {\n if (warned.has(api)) return;\n warned.add(api);\n writeRuntimeLine(\n `[envapt] Envapter.${api} is a filesystem-only API and has no effect in the portable build. ` +\n `Set Envapter.fileApiMode = 'throw' to throw instead.`\n );\n}\n\nexport function portableFileApiRead<Value>(api: string, fallback: Value): Value {\n if (EnvapterBase.fileApiMode === 'throw') fileApiUnsupported(api);\n warnOnce(api);\n return fallback;\n}\n\nexport function portableFileApiWrite(api: string): void {\n if (EnvapterBase.fileApiMode === 'throw') fileApiUnsupported(api);\n warnOnce(api);\n}\n"],"mappings":"iKAKA,MAAM,EAAS,IAAI,IAEnB,SAAS,EAAmB,EAAoB,CAC5C,MAAM,IAAI,EAAA,IAEN,YAAY,EAAI,yEACpB,CACJ,CAGA,SAAS,EAAS,EAAmB,CAC7B,EAAO,IAAI,CAAG,IAClB,EAAO,IAAI,CAAG,EACd,EACI,qBAAqB,EAAI,wHAE7B,EACJ,CAEA,SAAgB,EAA2B,EAAa,EAAwB,CAG5E,OAFI,EAAa,cAAgB,SAAS,EAAmB,CAAG,EAChE,EAAS,CAAG,EACL,CACX,CAEA,SAAgB,EAAqB,EAAmB,CAChD,EAAa,cAAgB,SAAS,EAAmB,CAAG,EAChE,EAAS,CAAG,CAChB"}
@@ -1 +1 @@
1
- {"version":3,"file":"Debug.mjs","names":[],"sources":["../../../src/infra/Debug.ts"],"sourcesContent":["import { EnvaptError, EnvaptErrorCodes } from './Error';\nimport { readRuntimeVar, writeRuntimeLine } from './runtime';\n\n/**\n * Debug log levels for {@link Envapter.debug}. `silent` (default) emits nothing.\n * `warn` covers signals that might indicate misconfiguration, failed file reads,\n * unresolved templates (when not strict), and any read of a missing or empty variable\n * (whether it returns a fallback or `undefined`). `verbose` adds every loaded file,\n * per-file key count, per-key load lines, and effective-paths / cache-rebuild notices.\n * @public\n * @see {@link https://envapt.materwelon.dev/docs/configuration#debug-logging}\n */\nexport type DebugLevel = 'silent' | 'warn' | 'verbose';\n\nconst VALID_LEVELS: ReadonlySet<string> = new Set<DebugLevel>(['silent', 'warn', 'verbose']);\n\nlet currentLevel: DebugLevel = 'silent';\nlet initialized = false;\n\nfunction isDebugLevel(value: string | undefined): value is DebugLevel {\n return value !== undefined && VALID_LEVELS.has(value);\n}\n\n// Reads `ENVAPT_DEBUG` lazily on first access. Setter wins after that.\nfunction resolveLevel(): DebugLevel {\n if (!initialized) {\n const fromEnv = readRuntimeVar('ENVAPT_DEBUG');\n currentLevel = isDebugLevel(fromEnv) ? fromEnv : 'silent';\n initialized = true;\n }\n return currentLevel;\n}\n\n/** @internal */\nexport function setDebugLevel(level: DebugLevel): void {\n if (!isDebugLevel(level)) {\n throw new EnvaptError(\n EnvaptErrorCodes.InvalidUserDefinedConfig,\n `Invalid debug level \"${String(level)}\". Expected 'silent' | 'warn' | 'verbose'.`\n );\n }\n currentLevel = level;\n initialized = true;\n}\n\n/** @internal */\nexport function getDebugLevel(): DebugLevel {\n return resolveLevel();\n}\n\n/** @internal */\nexport function resetDebugForTesting(): void {\n currentLevel = 'silent';\n initialized = false;\n}\n\n/** @internal */\nexport function debugWarn(message: string): void {\n const level = resolveLevel();\n if (level === 'warn' || level === 'verbose') {\n writeRuntimeLine(`[envapt] ${message}`);\n }\n}\n\n/** @internal */\nexport function debugVerbose(message: string): void {\n if (resolveLevel() === 'verbose') {\n writeRuntimeLine(`[envapt] ${message}`);\n }\n}\n"],"mappings":"+GAcA,MAAM,EAAoC,IAAI,IAAgB,CAAC,SAAU,OAAQ,SAAS,CAAC,EAE3F,IAAI,EAA2B,SAC3B,EAAc,GAElB,SAAS,EAAa,EAAgD,CAClE,OAAO,IAAU,IAAA,IAAa,EAAa,IAAI,CAAK,CACxD,CAGA,SAAS,GAA2B,CAChC,GAAI,CAAC,EAAa,CACd,IAAM,EAAU,EAAe,cAAc,EAC7C,EAAe,EAAa,CAAO,EAAI,EAAU,SACjD,EAAc,EAClB,CACA,OAAO,CACX,CAGA,SAAgB,EAAc,EAAyB,CACnD,GAAI,CAAC,EAAa,CAAK,EACnB,MAAM,IAAI,EAAA,IAEN,wBAAwB,OAAO,CAAK,EAAE,2CAC1C,EAEJ,EAAe,EACf,EAAc,EAClB,CAGA,SAAgB,GAA4B,CACxC,OAAO,EAAa,CACxB,CASA,SAAgB,EAAU,EAAuB,CAC7C,IAAM,EAAQ,EAAa,GACvB,IAAU,QAAU,IAAU,YAC9B,EAAiB,YAAY,GAAS,CAE9C,CAGA,SAAgB,EAAa,EAAuB,CAC5C,EAAa,IAAM,WACnB,EAAiB,YAAY,GAAS,CAE9C"}
1
+ {"version":3,"file":"Debug.mjs","names":[],"sources":["../../../src/infra/Debug.ts"],"sourcesContent":["import { EnvaptError, EnvaptErrorCodes } from './Error';\nimport { readRuntimeVar, writeRuntimeLine } from './runtime';\n\n/**\n * Debug log levels for {@link Envapter.debug}. `silent` (default) emits nothing.\n * `warn` logs failed file reads, unresolved templates (when not strict), and any read of a\n * missing or empty variable, whether it returns a fallback or `undefined`. `verbose` adds every loaded file,\n * per-file key count, per-key load lines, and effective-paths / cache-rebuild notices.\n * @public\n * @see {@link https://envapt.materwelon.dev/docs/configuration#debug-logging}\n */\nexport type DebugLevel = 'silent' | 'warn' | 'verbose';\n\nconst VALID_LEVELS: ReadonlySet<string> = new Set<DebugLevel>(['silent', 'warn', 'verbose']);\n\nlet currentLevel: DebugLevel = 'silent';\nlet initialized = false;\n\nfunction isDebugLevel(value: string | undefined): value is DebugLevel {\n return value !== undefined && VALID_LEVELS.has(value);\n}\n\n// Reads `ENVAPT_DEBUG` lazily on first access. Setter wins after that.\nfunction resolveLevel(): DebugLevel {\n if (!initialized) {\n const fromEnv = readRuntimeVar('ENVAPT_DEBUG');\n currentLevel = isDebugLevel(fromEnv) ? fromEnv : 'silent';\n initialized = true;\n }\n return currentLevel;\n}\n\nexport function setDebugLevel(level: DebugLevel): void {\n if (!isDebugLevel(level)) {\n throw new EnvaptError(\n EnvaptErrorCodes.InvalidUserDefinedConfig,\n `Invalid debug level \"${String(level)}\". Expected 'silent' | 'warn' | 'verbose'.`\n );\n }\n currentLevel = level;\n initialized = true;\n}\n\nexport function getDebugLevel(): DebugLevel {\n return resolveLevel();\n}\n\nexport function resetDebugForTesting(): void {\n currentLevel = 'silent';\n initialized = false;\n}\n\nexport function debugWarn(message: string): void {\n const level = resolveLevel();\n if (level === 'warn' || level === 'verbose') {\n writeRuntimeLine(`[envapt] ${message}`);\n }\n}\n\nexport function debugVerbose(message: string): void {\n if (resolveLevel() === 'verbose') {\n writeRuntimeLine(`[envapt] ${message}`);\n }\n}\n"],"mappings":"+GAaA,MAAM,EAAoC,IAAI,IAAgB,CAAC,SAAU,OAAQ,SAAS,CAAC,EAE3F,IAAI,EAA2B,SAC3B,EAAc,GAElB,SAAS,EAAa,EAAgD,CAClE,OAAO,IAAU,IAAA,IAAa,EAAa,IAAI,CAAK,CACxD,CAGA,SAAS,GAA2B,CAChC,GAAI,CAAC,EAAa,CACd,IAAM,EAAU,EAAe,cAAc,EAC7C,EAAe,EAAa,CAAO,EAAI,EAAU,SACjD,EAAc,EAClB,CACA,OAAO,CACX,CAEA,SAAgB,EAAc,EAAyB,CACnD,GAAI,CAAC,EAAa,CAAK,EACnB,MAAM,IAAI,EAAA,IAEN,wBAAwB,OAAO,CAAK,EAAE,2CAC1C,EAEJ,EAAe,EACf,EAAc,EAClB,CAEA,SAAgB,GAA4B,CACxC,OAAO,EAAa,CACxB,CAOA,SAAgB,EAAU,EAAuB,CAC7C,IAAM,EAAQ,EAAa,GACvB,IAAU,QAAU,IAAU,YAC9B,EAAiB,YAAY,GAAS,CAE9C,CAEA,SAAgB,EAAa,EAAuB,CAC5C,EAAa,IAAM,WACnB,EAAiB,YAAY,GAAS,CAE9C"}
@@ -1 +1 @@
1
- {"version":3,"file":"Dotenv.mjs","names":[],"sources":["../../../src/infra/Dotenv.ts"],"sourcesContent":["import { debugVerbose, debugWarn } from './Debug';\n\n/**\n * Public options for the internal `.env` loader. Mirrors the subset of dotenv's\n * `config()` options that envapt actually supports (no DOTENV_KEY, no quiet).\n * For debug output, use `Envapter.debug` (or the `ENVAPT_DEBUG` env var).\n *\n * @public\n * @see {@link https://envapt.materwelon.dev/docs/configuration#which-files-load}\n */\nexport interface EnvFileOptions {\n /** Encoding for reading .env files. Defaults to 'utf8'. */\n encoding?: string;\n /** When true, later files override earlier ones (and existing processEnv values). Default false (first-wins). */\n override?: boolean;\n}\n\n/**\n * Internal call signature used by `EnvapterBase`. The `path` and `processEnv`\n * fields are managed by envapt and never user-supplied.\n * @internal\n */\nexport interface LoadDotenvInput extends EnvFileOptions {\n path: string | string[];\n processEnv: Record<string, string>;\n // Injected so the loader stays free of `node:fs`. Returns `undefined` when the file is absent.\n readFile(path: string, encoding: string): string | undefined;\n}\n\n// Matches: optional `export`, KEY name, optional whitespace, `=`, optional whitespace, value tail.\n// Multi-line quoted values are handled by re-buffering subsequent lines below.\n// Bounded by anchors with linear-time quantifiers; no catastrophic backtracking risk -- justified\n// eslint-disable-next-line security/detect-unsafe-regex\nconst KEY_LINE_RE = /^\\s*(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=(.*)$/u;\n\n/**\n * Parse a `.env` text blob into a `Map<string, string>`.\n *\n * Supported syntax:\n * - `KEY=value` and `export KEY=value`\n * - blank lines and full-line `# comments`\n * - single-quoted `'literal'` values (no escape interpretation)\n * - double-quoted `\"value\"` values (interprets `\\n`, `\\r`, `\\t`, `\\\\`, `\\\"`)\n * - backtick-quoted values (literal, like single quotes)\n * - quoted values may span multiple lines\n * - inline `# comment` after an unquoted value (requires whitespace before `#`)\n * - empty values resolve to `\"\"`\n *\n * Mirrors dotenv's quirk where unescaped inner quotes inside a quoted value\n * are tolerated by greedy-matching to the rightmost matching quote on the\n * (possibly multi-line) value buffer. This is what lets a value like\n * `JSON=\"{\"name\":\"x\"}\"` round-trip without escaping.\n *\n * Does NOT perform `${VAR}` expansion — envapt's `Parser` handles that downstream.\n * @internal\n */\nexport function parseDotenv(src: string): Map<string, string> {\n const out = new Map<string, string>();\n const lines = src.split(/\\r?\\n/u);\n\n for (let i = 0; i < lines.length; i++) {\n const consumed = parseEntry(lines, i);\n i = consumed.endLine;\n if (consumed.entry) out.set(consumed.entry.key, consumed.entry.value);\n }\n\n return out;\n}\n\ninterface ParsedEntry {\n key: string;\n value: string;\n}\n\nfunction parseEntry(lines: string[], start: number): { entry: ParsedEntry | undefined; endLine: number } {\n /* v8 ignore next -- @preserve caller bounds-checks start so this never falls back */\n const line = lines[start] ?? '';\n const trimmedLine = line.trim();\n if (trimmedLine === '' || trimmedLine.startsWith('#')) return { entry: undefined, endLine: start };\n\n const match = line.match(KEY_LINE_RE);\n if (!match) return { entry: undefined, endLine: start };\n\n const key = match[1] as string;\n // Drop only leading whitespace before the value; trailing whitespace and inline comments\n // are dealt with below depending on whether the value is quoted.\n /* v8 ignore next -- @preserve regex group 2 always matches via `(.*)`, fallback is defensive */\n const rest = (match[2] ?? '').replace(/^\\s+/u, '');\n const firstChar = rest[0];\n\n if (firstChar === '\"' || firstChar === \"'\" || firstChar === '`') {\n const consumed = consumeQuotedValue(rest, lines, start, firstChar);\n return { entry: { key, value: consumed.value }, endLine: consumed.endLine };\n }\n\n return { entry: { key, value: stripInlineComment(rest) }, endLine: start };\n}\n\nfunction consumeQuotedValue(\n firstLineRest: string,\n lines: string[],\n startLine: number,\n quote: string\n): { value: string; endLine: number } {\n let buffer = firstLineRest;\n let cursor = startLine;\n let closeIdx = findLastUnescapedQuote(buffer, quote);\n\n while (closeIdx < 0 && cursor < lines.length - 1) {\n cursor++;\n /* v8 ignore next -- @preserve cursor is bounded by the while condition above */\n buffer += `\\n${lines[cursor] ?? ''}`;\n closeIdx = findLastUnescapedQuote(buffer, quote);\n }\n\n const inner = closeIdx > 0 ? buffer.slice(1, closeIdx) : buffer.slice(1);\n return { value: quote === '\"' ? unescapeDouble(inner) : inner, endLine: cursor };\n}\n\nfunction stripInlineComment(value: string): string {\n const hashIdx = findInlineCommentStart(value);\n const stripped = hashIdx === -1 ? value : value.slice(0, hashIdx);\n return stripped.trimEnd();\n}\n\nfunction findInlineCommentStart(value: string): number {\n for (let i = 0; i < value.length; i++) {\n if (value[i] === '#' && (i === 0 || /\\s/u.test(value[i - 1] as string))) return i;\n }\n return -1;\n}\n\n/**\n * Find the rightmost matching closing quote in `buffer` (skipping index 0 which is the\n * opening quote). For `\"`, a quote preceded by an odd number of backslashes is escaped\n * and ignored. Single and backtick quotes do not unescape.\n */\nfunction findLastUnescapedQuote(buffer: string, quote: string): number {\n for (let i = buffer.length - 1; i > 0; i--) {\n if (buffer[i] !== quote) continue;\n if (quote === '\"') {\n let backslashes = 0;\n let k = i - 1;\n while (k >= 0 && buffer[k] === '\\\\') {\n backslashes++;\n k--;\n }\n if (backslashes % 2 === 1) continue; // escaped\n }\n return i;\n }\n return -1;\n}\n\nfunction unescapeDouble(raw: string): string {\n let out = '';\n for (let i = 0; i < raw.length; i++) {\n const ch = raw[i];\n if (ch !== '\\\\' || i === raw.length - 1) {\n out += ch;\n continue;\n }\n const next = raw[i + 1];\n i++;\n switch (next) {\n case 'n':\n out += '\\n';\n break;\n case 'r':\n out += '\\r';\n break;\n case 't':\n out += '\\t';\n break;\n case '\\\\':\n out += '\\\\';\n break;\n case '\"':\n out += '\"';\n break;\n default:\n /* v8 ignore next -- @preserve `next` is always defined when this branch is reached (guarded above) */\n out += `\\\\${next ?? ''}`;\n }\n }\n return out;\n}\n\n/**\n * Load one or more `.env` files into a target `processEnv` map. Mirrors the\n * subset of dotenv's `config()` semantics that envapt depends on: first-wins\n * across multiple paths by default, optional `override: true`, optional\n * non-UTF8 encoding, missing files are skipped silently.\n *\n * Returns the set of keys actually written into `input.processEnv`. Skipped\n * collisions (under default `override: false`) are NOT included.\n * @internal\n */\nexport function loadDotenv(input: LoadDotenvInput): Set<string> {\n const paths = Array.isArray(input.path) ? input.path : [input.path];\n const encoding = input.encoding ?? 'utf8';\n const override = input.override ?? false;\n const written = new Set<string>();\n\n for (const filePath of paths) {\n const src = input.readFile(filePath, encoding);\n if (src === undefined) {\n debugWarn(`could not read ${filePath}`);\n continue;\n }\n\n const parsed = parseDotenv(src);\n debugVerbose(`loaded ${filePath}: ${parsed.size} ${parsed.size === 1 ? 'key' : 'keys'}`);\n for (const [key, value] of parsed) {\n const exists = Object.prototype.hasOwnProperty.call(input.processEnv, key);\n if (!exists || override) {\n input.processEnv[key] = value;\n written.add(key);\n debugVerbose(`${filePath} -> ${key}`);\n }\n }\n }\n\n return written;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Dotenv.mjs","names":[],"sources":["../../../src/infra/Dotenv.ts"],"sourcesContent":["import { debugVerbose, debugWarn } from './Debug';\n\n/**\n * Options for the `.env` loader, matching the dotenv `config()` options of the same name.\n * For debug output, use `Envapter.debug` (or the `ENVAPT_DEBUG` env var).\n *\n * @public\n * @see {@link https://envapt.materwelon.dev/docs/configuration#which-files-load}\n */\nexport interface EnvFileOptions {\n /** Encoding for reading .env files. Defaults to 'utf8'. */\n encoding?: string;\n /** When true, later files override earlier ones (and existing processEnv values). Default false (first-wins). */\n override?: boolean;\n}\n\nexport interface LoadDotenvInput extends EnvFileOptions {\n path: string | string[];\n processEnv: Record<string, string>;\n // passed in because this file must not import node:fs\n readFile(path: string, encoding: string): string | undefined;\n}\n\n// the anchors and linear quantifiers rule out catastrophic backtracking\n// eslint-disable-next-line security/detect-unsafe-regex\nconst KEY_LINE_RE = /^\\s*(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=(.*)$/u;\n\n// does not expand `${VAR}`, TemplateResolver does that on read\nexport function parseDotenv(src: string): Map<string, string> {\n const out = new Map<string, string>();\n const lines = src.split(/\\r?\\n/u);\n\n for (let i = 0; i < lines.length; i++) {\n const consumed = parseEntry(lines, i);\n i = consumed.endLine;\n if (consumed.entry) out.set(consumed.entry.key, consumed.entry.value);\n }\n\n return out;\n}\n\ninterface ParsedEntry {\n key: string;\n value: string;\n}\n\nfunction parseEntry(lines: string[], start: number): { entry: ParsedEntry | undefined; endLine: number } {\n /* v8 ignore next -- @preserve caller bounds-checks start so this never falls back */\n const line = lines[start] ?? '';\n const trimmedLine = line.trim();\n if (trimmedLine === '' || trimmedLine.startsWith('#')) return { entry: undefined, endLine: start };\n\n const match = line.match(KEY_LINE_RE);\n if (!match) return { entry: undefined, endLine: start };\n\n const key = match[1] as string;\n /* v8 ignore next -- @preserve regex group 2 always matches via `(.*)` */\n const rest = (match[2] ?? '').replace(/^\\s+/u, '');\n const firstChar = rest[0];\n\n if (firstChar === '\"' || firstChar === \"'\" || firstChar === '`') {\n const consumed = consumeQuotedValue(rest, lines, start, firstChar);\n return { entry: { key, value: consumed.value }, endLine: consumed.endLine };\n }\n\n return { entry: { key, value: stripInlineComment(rest) }, endLine: start };\n}\n\nfunction consumeQuotedValue(\n firstLineRest: string,\n lines: string[],\n startLine: number,\n quote: string\n): { value: string; endLine: number } {\n let buffer = firstLineRest;\n let cursor = startLine;\n let closeIdx = findLastUnescapedQuote(buffer, quote);\n\n while (closeIdx < 0 && cursor < lines.length - 1) {\n cursor++;\n /* v8 ignore next -- @preserve cursor is bounded by the while condition above */\n buffer += `\\n${lines[cursor] ?? ''}`;\n closeIdx = findLastUnescapedQuote(buffer, quote);\n }\n\n const inner = closeIdx > 0 ? buffer.slice(1, closeIdx) : buffer.slice(1);\n return { value: quote === '\"' ? unescapeDouble(inner) : inner, endLine: cursor };\n}\n\nfunction stripInlineComment(value: string): string {\n const hashIdx = findInlineCommentStart(value);\n const stripped = hashIdx === -1 ? value : value.slice(0, hashIdx);\n return stripped.trimEnd();\n}\n\nfunction findInlineCommentStart(value: string): number {\n for (let i = 0; i < value.length; i++) {\n if (value[i] === '#' && (i === 0 || /\\s/u.test(value[i - 1] as string))) return i;\n }\n return -1;\n}\n\n// closes on the last quote, like dotenv. JSON=\"{\"name\":\"x\"}\" parses without escaping.\nfunction findLastUnescapedQuote(buffer: string, quote: string): number {\n for (let i = buffer.length - 1; i > 0; i--) {\n if (buffer[i] !== quote) continue;\n if (quote === '\"') {\n let backslashes = 0;\n let k = i - 1;\n while (k >= 0 && buffer[k] === '\\\\') {\n backslashes++;\n k--;\n }\n if (backslashes % 2 === 1) continue; // escaped\n }\n return i;\n }\n return -1;\n}\n\nfunction unescapeDouble(raw: string): string {\n let out = '';\n for (let i = 0; i < raw.length; i++) {\n const ch = raw[i];\n if (ch !== '\\\\' || i === raw.length - 1) {\n out += ch;\n continue;\n }\n const next = raw[i + 1];\n i++;\n switch (next) {\n case 'n':\n out += '\\n';\n break;\n case 'r':\n out += '\\r';\n break;\n case 't':\n out += '\\t';\n break;\n case '\\\\':\n out += '\\\\';\n break;\n case '\"':\n out += '\"';\n break;\n default:\n /* v8 ignore next -- @preserve `next` is always defined when this branch is reached (guarded above) */\n out += `\\\\${next ?? ''}`;\n }\n }\n return out;\n}\n\nexport function loadDotenv(input: LoadDotenvInput): Set<string> {\n const paths = Array.isArray(input.path) ? input.path : [input.path];\n const encoding = input.encoding ?? 'utf8';\n const override = input.override ?? false;\n const written = new Set<string>();\n\n for (const filePath of paths) {\n const src = input.readFile(filePath, encoding);\n if (src === undefined) {\n debugWarn(`could not read ${filePath}`);\n continue;\n }\n\n const parsed = parseDotenv(src);\n debugVerbose(`loaded ${filePath}: ${parsed.size} ${parsed.size === 1 ? 'key' : 'keys'}`);\n for (const [key, value] of parsed) {\n const exists = Object.prototype.hasOwnProperty.call(input.processEnv, key);\n if (!exists || override) {\n input.processEnv[key] = value;\n written.add(key);\n debugVerbose(`${filePath} -> ${key}`);\n }\n }\n }\n\n return written;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"Error.mjs","names":[],"sources":["../../../src/infra/Error.ts"],"sourcesContent":["/* eslint-disable no-magic-numbers */\nimport type { StandardSchemaV1 } from './StandardSchema';\n\n/**\n * Numeric codes carried by {@link EnvaptError.code}, grouped by fallback (1xx), converter (2xx),\n * and configuration (3xx) failures.\n * @see {@link https://envapt.materwelon.dev/docs/errors#codes}\n */\nexport enum EnvaptErrorCodes {\n // Fallback related errors\n /** Thrown when an invalid fallback value is provided */\n InvalidFallback = 101,\n /** Thrown when fallback value type doesn't match expected converter type */\n InvalidFallbackType = 102,\n /** Thrown when array fallback contains elements of wrong type */\n ArrayFallbackElementTypeMismatch = 103,\n /** Thrown when fallback type doesn't match the specified converter */\n FallbackConverterTypeMismatch = 104,\n /** Thrown when a time-string fallback is malformed (does not match the required `<number><unit>` format) */\n MalformedTimeFallback = 105,\n\n // Converter related errors\n /** Thrown when invalid array converter configuration is provided */\n InvalidArrayConverterType = 201,\n /** Thrown when an invalid built-in converter is specified */\n InvalidBuiltInConverter = 202,\n /** Thrown when a custom converter is not a function */\n InvalidCustomConverter = 203,\n /** Thrown when converter type is not recognized */\n InvalidConverterType = 204,\n /** Thrown when primitive type coercion on fallback value fails */\n PrimitiveCoercionFailed = 205,\n /** Thrown when an array element fails to convert to the configured element type */\n ArrayElementConversionFailed = 206,\n /** Thrown under strict mode when an array element is empty or whitespace only */\n EmptyArrayElement = 207,\n /** Thrown when a Standard Schema returns issues for a non-empty env value */\n SchemaValidationFailed = 208,\n /** Thrown when a Standard Schema's `validate` itself throws (e.g. a refinement that crashes) */\n SchemaThrew = 209,\n\n // Other errors\n /** Thrown when delimiter is missing in array converter configuration */\n // This doesn't happen because 203 is thrown when object without delimiter is passed\n MissingDelimiter = 301,\n /** Thrown when invalid user-defined configuration is provided */\n InvalidUserDefinedConfig = 302,\n /** Thrown when specified environment files don't exist */\n EnvFilesNotFound = 303,\n /** Thrown when no valid environment key is provided */\n InvalidKeyInput = 304,\n /** Thrown when a required environment value is missing or empty (post-trim) */\n MissingEnvValue = 305,\n /** Thrown when a file-based API (envPaths, baseDir, configureProfiles) is used on a source without filesystem support */\n FileApiUnsupported = 306,\n /** Thrown when an environment value is read before a source is bound via Envapter.useSource */\n NoSourceBound = 307,\n /** Thrown when `merge` is called with no members, or with more than one filesystem-backed source */\n InvalidMergedSource = 308\n}\n\ninterface EnvaptErrorOptions {\n issues?: readonly StandardSchemaV1.Issue[];\n cause?: unknown;\n}\n\n/**\n * Custom error for better DX and debugging when using Envapt.\n *\n * @example\n * ```ts\n * throw new EnvaptError(EnvaptErrorCodes.InvalidFallback, \"Invalid fallback value provided for environment variable.\");\n * ```\n * @see {@link https://envapt.materwelon.dev/docs/errors#the-error-shape}\n */\nexport class EnvaptError extends Error {\n /** The {@link EnvaptErrorCodes} value identifying what failed. */\n public readonly code: EnvaptErrorCodes;\n /**\n * Populated only for {@link EnvaptErrorCodes.SchemaValidationFailed} (208). For every other\n * code this is `undefined`. Lets callers do `if (err.code === 208) err.issues?.forEach(...)`\n * without a type cast.\n */\n public readonly issues: readonly StandardSchemaV1.Issue[] | undefined;\n\n /** Create an error carrying an {@link EnvaptErrorCodes} code, a message, and optional `cause` / `issues`. */\n constructor(code: EnvaptErrorCodes, message: string, options?: EnvaptErrorOptions) {\n super(message, options?.cause !== undefined ? { cause: options.cause } : undefined);\n this.name = `EnvaptError [${code}]`;\n this.code = code;\n this.issues = options?.issues;\n }\n}\n"],"mappings":"AAQA,IAAY,EAAL,SAAA,EAAA,OAGH,GAAA,EAAA,gBAAA,KAAA,kBAEA,EAAA,EAAA,oBAAA,KAAA,sBAEA,EAAA,EAAA,iCAAA,KAAA,mCAEA,EAAA,EAAA,8BAAA,KAAA,gCAEA,EAAA,EAAA,sBAAA,KAAA,wBAIA,EAAA,EAAA,0BAAA,KAAA,4BAEA,EAAA,EAAA,wBAAA,KAAA,0BAEA,EAAA,EAAA,uBAAA,KAAA,yBAEA,EAAA,EAAA,qBAAA,KAAA,uBAEA,EAAA,EAAA,wBAAA,KAAA,0BAEA,EAAA,EAAA,6BAAA,KAAA,+BAEA,EAAA,EAAA,kBAAA,KAAA,oBAEA,EAAA,EAAA,uBAAA,KAAA,yBAEA,EAAA,EAAA,YAAA,KAAA,cAKA,EAAA,EAAA,iBAAA,KAAA,mBAEA,EAAA,EAAA,yBAAA,KAAA,2BAEA,EAAA,EAAA,iBAAA,KAAA,mBAEA,EAAA,EAAA,gBAAA,KAAA,kBAEA,EAAA,EAAA,gBAAA,KAAA,kBAEA,EAAA,EAAA,mBAAA,KAAA,qBAEA,EAAA,EAAA,cAAA,KAAA,gBAEA,EAAA,EAAA,oBAAA,KAAA,uBACJ,EAAA,CAAA,CAAA,EAgBA,IAAa,EAAb,cAAiC,KAAM,CAEnC,KAMA,OAGA,YAAY,EAAwB,EAAiB,EAA8B,CAC/E,MAAM,EAAS,GAAS,QAAU,IAAA,GAAuC,IAAA,GAA3B,CAAE,MAAO,EAAQ,KAAM,CAAa,EAClF,KAAK,KAAO,gBAAgB,EAAK,GACjC,KAAK,KAAO,EACZ,KAAK,OAAS,GAAS,MAC3B,CACJ"}
1
+ {"version":3,"file":"Error.mjs","names":[],"sources":["../../../src/infra/Error.ts"],"sourcesContent":["/* eslint-disable no-magic-numbers */\nimport type { StandardSchemaV1 } from './StandardSchema';\n\n/**\n * Numeric codes carried by {@link EnvaptError.code}, grouped by fallback (1xx), converter (2xx),\n * and configuration (3xx) failures.\n * @see {@link https://envapt.materwelon.dev/docs/errors#codes}\n */\nexport enum EnvaptErrorCodes {\n // Fallback related errors\n /** Thrown when an invalid fallback value is provided */\n InvalidFallback = 101,\n /** Thrown when fallback value type doesn't match expected converter type */\n InvalidFallbackType = 102,\n /** Thrown when array fallback contains elements of wrong type */\n ArrayFallbackElementTypeMismatch = 103,\n /** Thrown when fallback type doesn't match the specified converter */\n FallbackConverterTypeMismatch = 104,\n /** Thrown when a time-string fallback is malformed (does not match the required `<number><unit>` format) */\n MalformedTimeFallback = 105,\n\n // Converter related errors\n /** Thrown when invalid array converter configuration is provided */\n InvalidArrayConverterType = 201,\n /** Thrown when an invalid built-in converter is specified */\n InvalidBuiltInConverter = 202,\n /** Thrown when a custom converter is not a function */\n InvalidCustomConverter = 203,\n /** Thrown when converter type is not recognized */\n InvalidConverterType = 204,\n /** Thrown when primitive type coercion on fallback value fails */\n PrimitiveCoercionFailed = 205,\n /** Thrown when an array element fails to convert to the configured element type */\n ArrayElementConversionFailed = 206,\n /** Thrown under strict mode when an array element is empty or whitespace only */\n EmptyArrayElement = 207,\n /** Thrown when a Standard Schema returns issues for a non-empty env value */\n SchemaValidationFailed = 208,\n /** Thrown when a Standard Schema's `validate` itself throws (e.g. a refinement that crashes) */\n SchemaThrew = 209,\n\n // Other errors\n /** Thrown when delimiter is missing in array converter configuration */\n MissingDelimiter = 301,\n /** Thrown when invalid user-defined configuration is provided */\n InvalidUserDefinedConfig = 302,\n /** Thrown when specified environment files don't exist */\n EnvFilesNotFound = 303,\n /** Thrown when no valid environment key is provided */\n InvalidKeyInput = 304,\n /** Thrown when a required environment value is missing or empty */\n MissingEnvValue = 305,\n /** Thrown when a file-based API (envPaths, baseDir, configureProfiles) is used on a source without filesystem support */\n FileApiUnsupported = 306,\n /** Thrown when an environment value is read before a source is bound via Envapter.useSource */\n NoSourceBound = 307,\n /** Thrown when `merge` is called with no members, or with more than one filesystem-backed source */\n InvalidMergedSource = 308\n}\n\ninterface EnvaptErrorOptions {\n issues?: readonly StandardSchemaV1.Issue[];\n cause?: unknown;\n}\n\n/**\n * The error envapt throws. Its `code` identifies the failure.\n *\n * @example\n * ```ts\n * throw new EnvaptError(EnvaptErrorCodes.InvalidFallback, \"Invalid fallback value provided for environment variable.\");\n * ```\n * @see {@link https://envapt.materwelon.dev/docs/errors#the-error-shape}\n */\nexport class EnvaptError extends Error {\n /** The {@link EnvaptErrorCodes} value identifying what failed. */\n public readonly code: EnvaptErrorCodes;\n /**\n * Populated only for {@link EnvaptErrorCodes.SchemaValidationFailed} (208). For every other\n * code this is `undefined`. Lets callers do `if (err.code === 208) err.issues?.forEach(...)`\n * without a type cast.\n */\n public readonly issues: readonly StandardSchemaV1.Issue[] | undefined;\n\n /** Create an error carrying an {@link EnvaptErrorCodes} code, a message, and optional `cause` / `issues`. */\n constructor(code: EnvaptErrorCodes, message: string, options?: EnvaptErrorOptions) {\n super(message, options?.cause !== undefined ? { cause: options.cause } : undefined);\n this.name = `EnvaptError [${code}]`;\n this.code = code;\n this.issues = options?.issues;\n }\n}\n"],"mappings":"AAQA,IAAY,EAAL,SAAA,EAAA,OAGH,GAAA,EAAA,gBAAA,KAAA,kBAEA,EAAA,EAAA,oBAAA,KAAA,sBAEA,EAAA,EAAA,iCAAA,KAAA,mCAEA,EAAA,EAAA,8BAAA,KAAA,gCAEA,EAAA,EAAA,sBAAA,KAAA,wBAIA,EAAA,EAAA,0BAAA,KAAA,4BAEA,EAAA,EAAA,wBAAA,KAAA,0BAEA,EAAA,EAAA,uBAAA,KAAA,yBAEA,EAAA,EAAA,qBAAA,KAAA,uBAEA,EAAA,EAAA,wBAAA,KAAA,0BAEA,EAAA,EAAA,6BAAA,KAAA,+BAEA,EAAA,EAAA,kBAAA,KAAA,oBAEA,EAAA,EAAA,uBAAA,KAAA,yBAEA,EAAA,EAAA,YAAA,KAAA,cAIA,EAAA,EAAA,iBAAA,KAAA,mBAEA,EAAA,EAAA,yBAAA,KAAA,2BAEA,EAAA,EAAA,iBAAA,KAAA,mBAEA,EAAA,EAAA,gBAAA,KAAA,kBAEA,EAAA,EAAA,gBAAA,KAAA,kBAEA,EAAA,EAAA,mBAAA,KAAA,qBAEA,EAAA,EAAA,cAAA,KAAA,gBAEA,EAAA,EAAA,oBAAA,KAAA,uBACJ,EAAA,CAAA,CAAA,EAgBA,IAAa,EAAb,cAAiC,KAAM,CAEnC,KAMA,OAGA,YAAY,EAAwB,EAAiB,EAA8B,CAC/E,MAAM,EAAS,GAAS,QAAU,IAAA,GAAuC,IAAA,GAA3B,CAAE,MAAO,EAAQ,KAAM,CAAa,EAClF,KAAK,KAAO,gBAAgB,EAAK,GACjC,KAAK,KAAO,EACZ,KAAK,OAAS,GAAS,MAC3B,CACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"recase.mjs","names":[],"sources":["../../../src/infra/recase.ts"],"sourcesContent":["import type { KeyCasing } from '../types';\n\n// underscore-delimited words, kept in step with the RecaseKey type transforms in types/Casing.ts.\nexport function recase(name: string, casing?: KeyCasing): string {\n if (casing === undefined) return name;\n const words = name.split('_').filter((word) => word.length > 0);\n if (casing === 'kebab-case') return words.map((word) => word.toLowerCase()).join('-');\n const pascal = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join('');\n return casing === 'PascalCase' ? pascal : pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n"],"mappings":"AAGA,SAAgB,EAAO,EAAc,EAA4B,CAC7D,GAAI,IAAW,IAAA,GAAW,OAAO,EACjC,IAAM,EAAQ,EAAK,MAAM,GAAG,CAAC,CAAC,OAAQ,GAAS,EAAK,OAAS,CAAC,EAC9D,GAAI,IAAW,aAAc,OAAO,EAAM,IAAK,GAAS,EAAK,YAAY,CAAC,CAAC,CAAC,KAAK,GAAG,EACpF,IAAM,EAAS,EAAM,IAAK,GAAS,EAAK,OAAO,CAAC,CAAC,CAAC,YAAY,EAAI,EAAK,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,EAAE,EACtG,OAAO,IAAW,aAAe,EAAS,EAAO,OAAO,CAAC,CAAC,CAAC,YAAY,EAAI,EAAO,MAAM,CAAC,CAC7F"}
1
+ {"version":3,"file":"recase.mjs","names":[],"sources":["../../../src/infra/recase.ts"],"sourcesContent":["import type { KeyCasing } from '../types';\n\n// if you change this, change the RecaseKey type in types/Casing.ts to match\nexport function recase(name: string, casing?: KeyCasing): string {\n if (casing === undefined) return name;\n const words = name.split('_').filter((word) => word.length > 0);\n if (casing === 'kebab-case') return words.map((word) => word.toLowerCase()).join('-');\n const pascal = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join('');\n return casing === 'PascalCase' ? pascal : pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n"],"mappings":"AAGA,SAAgB,EAAO,EAAc,EAA4B,CAC7D,GAAI,IAAW,IAAA,GAAW,OAAO,EACjC,IAAM,EAAQ,EAAK,MAAM,GAAG,CAAC,CAAC,OAAQ,GAAS,EAAK,OAAS,CAAC,EAC9D,GAAI,IAAW,aAAc,OAAO,EAAM,IAAK,GAAS,EAAK,YAAY,CAAC,CAAC,CAAC,KAAK,GAAG,EACpF,IAAM,EAAS,EAAM,IAAK,GAAS,EAAK,OAAO,CAAC,CAAC,CAAC,YAAY,EAAI,EAAK,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,EAAE,EACtG,OAAO,IAAW,aAAe,EAAS,EAAO,OAAO,CAAC,CAAC,CAAC,YAAY,EAAI,EAAO,MAAM,CAAC,CAC7F"}
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.mjs","names":[],"sources":["../../../src/infra/runtime.ts"],"sourcesContent":["import type { Source } from '../types';\n\n// importing Debug back into EnvapterBase would cycle, so the source and sink are injected via the setters below.\n/* v8 ignore start -- @preserve replaced at load on Node so the Node suite never runs these defaults */\nlet sink: (line: string) => void = (line) => {\n // eslint-disable-next-line no-console -- the off-Node fallback log sink\n console.error(line);\n};\nlet envReader: () => Record<string, string> = () => ({});\nlet varReader: ((key: string) => string | undefined) | undefined;\n/* v8 ignore stop */\n\nexport function setRuntimeSink(fn: (line: string) => void): void {\n sink = fn;\n}\n\nexport function writeRuntimeLine(line: string): void {\n sink(line);\n}\n\n// check snapshot first, then the source's per-key reader, so a reader source (empty snapshot) still resolves a key\nexport function readRuntimeVar(key: string): string | undefined {\n const fromSnapshot = envReader()[key];\n if (fromSnapshot !== undefined) return fromSnapshot;\n // ENVAPT_DEBUG resolves through here, so a throwing reader must not abort the config load that triggered it\n try {\n return varReader?.(key);\n } catch {\n return undefined;\n }\n}\n\nexport function bindRuntimeFromSource(source: Source): void {\n envReader = (): Record<string, string> => source.readVars();\n const readVar = source.readVar;\n varReader = typeof readVar === 'function' ? readVar.bind(source) : undefined;\n}\n"],"mappings":"AAIA,IAAI,EAAgC,GAAS,CAEzC,QAAQ,MAAM,CAAI,CACtB,EACI,OAAiD,CAAC,GAClD,EAOJ,SAAgB,EAAiB,EAAoB,CACjD,EAAK,CAAI,CACb,CAGA,SAAgB,EAAe,EAAiC,CAC5D,IAAM,EAAe,EAAU,CAAC,CAAC,GACjC,GAAI,IAAiB,IAAA,GAAW,OAAO,EAEvC,GAAI,CACA,OAAO,IAAY,CAAG,CAC1B,MAAQ,CACJ,MACJ,CACJ,CAEA,SAAgB,EAAsB,EAAsB,CACxD,MAA0C,EAAO,SAAS,EAC1D,IAAM,EAAU,EAAO,QACvB,EAAY,OAAO,GAAY,WAAa,EAAQ,KAAK,CAAM,EAAI,IAAA,EACvE"}
1
+ {"version":3,"file":"runtime.mjs","names":[],"sources":["../../../src/infra/runtime.ts"],"sourcesContent":["import type { Source } from '../types';\n\n/* v8 ignore start -- @preserve replaced at load on Node so the Node suite never runs these defaults */\nlet sink: (line: string) => void = (line) => {\n // eslint-disable-next-line no-console -- the off-Node fallback log sink\n console.error(line);\n};\nlet envReader: () => Record<string, string> = () => ({});\nlet varReader: ((key: string) => string | undefined) | undefined;\n/* v8 ignore stop */\n\nexport function setRuntimeSink(fn: (line: string) => void): void {\n sink = fn;\n}\n\nexport function writeRuntimeLine(line: string): void {\n sink(line);\n}\n\n// a reader source has an empty snapshot\nexport function readRuntimeVar(key: string): string | undefined {\n const fromSnapshot = envReader()[key];\n if (fromSnapshot !== undefined) return fromSnapshot;\n // ENVAPT_DEBUG reads go through here and must not throw during a config load\n try {\n return varReader?.(key);\n } catch {\n return undefined;\n }\n}\n\nexport function bindRuntimeFromSource(source: Source): void {\n envReader = (): Record<string, string> => source.readVars();\n const readVar = source.readVar;\n varReader = typeof readVar === 'function' ? readVar.bind(source) : undefined;\n}\n"],"mappings":"AAGA,IAAI,EAAgC,GAAS,CAEzC,QAAQ,MAAM,CAAI,CACtB,EACI,OAAiD,CAAC,GAClD,EAOJ,SAAgB,EAAiB,EAAoB,CACjD,EAAK,CAAI,CACb,CAGA,SAAgB,EAAe,EAAiC,CAC5D,IAAM,EAAe,EAAU,CAAC,CAAC,GACjC,GAAI,IAAiB,IAAA,GAAW,OAAO,EAEvC,GAAI,CACA,OAAO,IAAY,CAAG,CAC1B,MAAQ,CACJ,MACJ,CACJ,CAEA,SAAgB,EAAsB,EAAsB,CACxD,MAA0C,EAAO,SAAS,EAC1D,IAAM,EAAU,EAAO,QACvB,EAAY,OAAO,GAAY,WAAa,EAAQ,KAAK,CAAM,EAAI,IAAA,EACvE"}
@@ -1 +1 @@
1
- {"version":3,"file":"PortableSource.mjs","names":[],"sources":["../../../src/sources/PortableSource.ts"],"sourcesContent":["import { coerceToStringRecord } from './coerce';\n\nimport type { BareSource } from '../types';\n\n/**\n * Environment source seeded from an object you provide, for every runtime without a filesystem. The\n * config your bundler injects into a browser build (Vite's `import.meta.env`, a webpack `DefinePlugin`\n * global), the Cloudflare `env` binding, a Vercel Edge or Fastly config object, a test fixture, or any\n * plain object. Pass it straight through. Non-string values are JSON-stringified so the converters\n * still apply, which means they must be JSON-serializable. Without a filesystem the `.env` cascade and\n * file APIs do not apply.\n * @public\n * @see {@link https://envapt.materwelon.dev/docs/sources#the-providers}\n */\nexport class PortableSource implements BareSource {\n /** Always `false`. With no filesystem, the `.env` cascade and file APIs do not apply. */\n readonly supportsFiles = false;\n private readonly vars: Record<string, string>;\n\n /** Seed the source from `vars`. Non-string values are JSON-stringified so the converters still apply. */\n constructor(vars: object) {\n this.vars = coerceToStringRecord(vars);\n }\n\n /** Returns a snapshot clone of the provided object as plain strings. */\n readVars(): Record<string, string> {\n return { ...this.vars };\n }\n}\n"],"mappings":"oDAcA,IAAa,EAAb,KAAkD,CAE9C,cAAyB,GACzB,KAGA,YAAY,EAAc,CACtB,KAAK,KAAO,EAAqB,CAAI,CACzC,CAGA,UAAmC,CAC/B,MAAO,CAAE,GAAG,KAAK,IAAK,CAC1B,CACJ"}
1
+ {"version":3,"file":"PortableSource.mjs","names":[],"sources":["../../../src/sources/PortableSource.ts"],"sourcesContent":["import { coerceToStringRecord } from './coerce';\n\nimport type { BareSource } from '../types';\n\n/**\n * Environment source seeded from an object you provide, for every runtime without a filesystem. Pass\n * the object straight through, such as the config your bundler injects into a browser build (Vite's\n * `import.meta.env`, a webpack `DefinePlugin` global), the Cloudflare `env` binding, a Vercel Edge or\n * Fastly config object, a test fixture, or any plain object. Non-string values are JSON-stringified so\n * the converters still apply. Those values must be JSON-serializable. Without a filesystem the `.env`\n * cascade and file APIs do not apply.\n * @public\n * @see {@link https://envapt.materwelon.dev/docs/sources#the-providers}\n */\nexport class PortableSource implements BareSource {\n /** Always `false`. With no filesystem, the `.env` cascade and file APIs do not apply. */\n readonly supportsFiles = false;\n private readonly vars: Record<string, string>;\n\n /** Seed the source from `vars`. Non-string values are JSON-stringified so the converters still apply. */\n constructor(vars: object) {\n this.vars = coerceToStringRecord(vars);\n }\n\n /** Returns a snapshot clone of the provided object as plain strings. */\n readVars(): Record<string, string> {\n return { ...this.vars };\n }\n}\n"],"mappings":"oDAcA,IAAa,EAAb,KAAkD,CAE9C,cAAyB,GACzB,KAGA,YAAY,EAAc,CACtB,KAAK,KAAO,EAAqB,CAAI,CACzC,CAGA,UAAmC,CAC/B,MAAO,CAAE,GAAG,KAAK,IAAK,CAC1B,CACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"UnboundSource.mjs","names":[],"sources":["../../../src/sources/UnboundSource.ts"],"sourcesContent":["import { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\n\nimport type { BareSource } from '../types';\n\n/**\n * The default source until one is bound. Every read throws NoSourceBound so a forgotten `useSource`\n * surfaces at read time instead of silently yielding nothing. The Node entry binds {@link FileSource}\n * at load, browser and Workers bundles must call `Envapter.useSource(...)` before reading.\n * @internal\n */\nexport class UnboundSource implements BareSource {\n readonly supportsFiles = false;\n\n readVars(): Record<string, string> {\n throw new EnvaptError(\n EnvaptErrorCodes.NoSourceBound,\n 'No environment source is bound. Call Envapter.useSource(...) with a source such as new PortableSource({...}) before reading.'\n );\n }\n}\n"],"mappings":"iDAUA,IAAa,EAAb,KAAiD,CAC7C,cAAyB,GAEzB,UAAmC,CAC/B,MAAM,IAAI,EAAA,IAEN,8HACJ,CACJ,CACJ"}
1
+ {"version":3,"file":"UnboundSource.mjs","names":[],"sources":["../../../src/sources/UnboundSource.ts"],"sourcesContent":["import { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\n\nimport type { BareSource } from '../types';\n\nexport class UnboundSource implements BareSource {\n readonly supportsFiles = false;\n\n readVars(): Record<string, string> {\n throw new EnvaptError(\n EnvaptErrorCodes.NoSourceBound,\n 'No environment source is bound. Call Envapter.useSource(...) with a source such as new PortableSource({...}) before reading.'\n );\n }\n}\n"],"mappings":"iDAIA,IAAa,EAAb,KAAiD,CAC7C,cAAyB,GAEzB,UAAmC,CAC/B,MAAM,IAAI,EAAA,IAEN,8HACJ,CACJ,CACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"coerce.mjs","names":[],"sources":["../../../src/sources/coerce.ts"],"sourcesContent":["// The returned record is fresh, so a caller mutating its object\n// later cannot leak into the source, and non-string values are JSON-stringified.\nexport function coerceToStringRecord(env: object): Record<string, string> {\n const snapshot: Record<string, string> = {};\n // `object` so a Cloudflare `Env` (an interface with no index signature) is accepted. cast to a record\n // to read entries as `unknown` rather than `any`.\n for (const [key, value] of Object.entries(env as Record<string, unknown>)) {\n if (typeof value === 'string') {\n snapshot[key] = value;\n continue;\n }\n const encoded = JSON.stringify(value);\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- TS lib mistypes JSON.stringify's return as always-string when it is string | undefined at runtime\n if (encoded !== undefined) snapshot[key] = encoded;\n }\n return snapshot;\n}\n"],"mappings":"AAEA,SAAgB,EAAqB,EAAqC,CACtE,IAAM,EAAmC,CAAC,EAG1C,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,CAA8B,EAAG,CACvE,GAAI,OAAO,GAAU,SAAU,CAC3B,EAAS,GAAO,EAChB,QACJ,CACA,IAAM,EAAU,KAAK,UAAU,CAAK,EAEhC,IAAY,IAAA,KAAW,EAAS,GAAO,EAC/C,CACA,OAAO,CACX"}
1
+ {"version":3,"file":"coerce.mjs","names":[],"sources":["../../../src/sources/coerce.ts"],"sourcesContent":["// copies the entries so a later change to the caller's object does not reach the source\nexport function coerceToStringRecord(env: object): Record<string, string> {\n const snapshot: Record<string, string> = {};\n // takes `object` because a Cloudflare Env interface has no index signature\n for (const [key, value] of Object.entries(env as Record<string, unknown>)) {\n if (typeof value === 'string') {\n snapshot[key] = value;\n continue;\n }\n const encoded = JSON.stringify(value);\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- TS lib mistypes JSON.stringify's return as always-string when it is string | undefined at runtime\n if (encoded !== undefined) snapshot[key] = encoded;\n }\n return snapshot;\n}\n"],"mappings":"AACA,SAAgB,EAAqB,EAAqC,CACtE,IAAM,EAAmC,CAAC,EAE1C,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,CAA8B,EAAG,CACvE,GAAI,OAAO,GAAU,SAAU,CAC3B,EAAS,GAAO,EAChB,QACJ,CACA,IAAM,EAAU,KAAK,UAAU,CAAK,EAEhC,IAAY,IAAA,KAAW,EAAS,GAAO,EAC/C,CACA,OAAO,CACX"}
@@ -1 +1 @@
1
- {"version":3,"file":"merge.mjs","names":[],"sources":["../../../src/sources/merge.ts"],"sourcesContent":["import { normalizeSource } from './normalizeSource';\nimport { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\n\nimport type { BareSource, FileCapableSource, Source } from '../types';\n\n/**\n * Compose several sources into one, read last-wins. A later member's `readVars()` value overrides an\n * earlier member's for the same key. A member may be a `(key) => string | undefined` reader for a\n * source that reads one key at a time, and it fills a key missing from every snapshot. Bind the result\n * with `Envapter.useSource`. Throws {@link EnvaptErrorCodes.InvalidMergedSource} with no members or with\n * more than one file-backed member.\n * @public\n * @see {@link https://envapt.materwelon.dev/docs/sources#combining-sources}\n */\nexport function merge(...members: (Source | ((key: string) => string | undefined))[]): Source {\n if (members.length === 0) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidMergedSource, 'merge requires at least one source.');\n }\n\n const sources = members.map(normalizeSource);\n\n const fileMembers = sources.filter((m): m is FileCapableSource => m.supportsFiles === true);\n if (fileMembers.length > 1) {\n throw new EnvaptError(\n EnvaptErrorCodes.InvalidMergedSource,\n 'merge accepts at most one filesystem-backed source, so the .env cascade and file APIs route to a single member.'\n );\n }\n\n const readVars = (): Record<string, string> => {\n const merged: Record<string, string> = {};\n for (const m of sources) Object.assign(merged, m.readVars());\n return merged;\n };\n\n const readers = sources.filter(\n (m): m is Source & { readVar: (key: string) => string | undefined } => typeof m.readVar === 'function'\n );\n const readVar =\n readers.length === 0\n ? undefined\n : (key: string): string | undefined => {\n // last reader that answers wins, so scan from the end and return the first defined value\n for (let i = readers.length - 1; i >= 0; i--) {\n const found = readers[i]?.readVar(key);\n if (found !== undefined) return found;\n }\n return undefined;\n };\n\n const fileMember = fileMembers[0];\n if (!fileMember) {\n const bare: BareSource = { readVars };\n if (readVar) bare.readVar = readVar;\n return bare;\n }\n\n const fileCapable: FileCapableSource = {\n readVars,\n supportsFiles: true,\n readFile: (path, encoding) => fileMember.readFile(path, encoding),\n resolvePath: (baseDir, candidate) => fileMember.resolvePath(baseDir, candidate),\n normalizeBaseDir: (value) => fileMember.normalizeBaseDir(value),\n writeVars: (vars) => fileMember.writeVars(vars)\n };\n if (readVar) fileCapable.readVar = readVar;\n return fileCapable;\n}\n"],"mappings":"yGAcA,SAAgB,EAAM,GAAG,EAAqE,CAC1F,GAAI,EAAQ,SAAW,EACnB,MAAM,IAAI,EAAA,IAAkD,qCAAqC,EAGrG,IAAM,EAAU,EAAQ,IAAI,CAAe,EAErC,EAAc,EAAQ,OAAQ,GAA8B,EAAE,gBAAkB,EAAI,EAC1F,GAAI,EAAY,OAAS,EACrB,MAAM,IAAI,EAAA,IAEN,iHACJ,EAGJ,IAAM,MAAyC,CAC3C,IAAM,EAAiC,CAAC,EACxC,IAAK,IAAM,KAAK,EAAS,OAAO,OAAO,EAAQ,EAAE,SAAS,CAAC,EAC3D,OAAO,CACX,EAEM,EAAU,EAAQ,OACnB,GAAsE,OAAO,EAAE,SAAY,UAChG,EACM,EACF,EAAQ,SAAW,EACb,IAAA,GACC,GAAoC,CAEjC,IAAK,IAAI,EAAI,EAAQ,OAAS,EAAG,GAAK,EAAG,IAAK,CAC1C,IAAM,EAAQ,EAAQ,EAAE,EAAE,QAAQ,CAAG,EACrC,GAAI,IAAU,IAAA,GAAW,OAAO,CACpC,CAEJ,EAEJ,EAAa,EAAY,GAC/B,GAAI,CAAC,EAAY,CACb,IAAM,EAAmB,CAAE,UAAS,EAEpC,OADI,IAAS,EAAK,QAAU,GACrB,CACX,CAEA,IAAM,EAAiC,CACnC,WACA,cAAe,GACf,UAAW,EAAM,IAAa,EAAW,SAAS,EAAM,CAAQ,EAChE,aAAc,EAAS,IAAc,EAAW,YAAY,EAAS,CAAS,EAC9E,iBAAmB,GAAU,EAAW,iBAAiB,CAAK,EAC9D,UAAY,GAAS,EAAW,UAAU,CAAI,CAClD,EAEA,OADI,IAAS,EAAY,QAAU,GAC5B,CACX"}
1
+ {"version":3,"file":"merge.mjs","names":[],"sources":["../../../src/sources/merge.ts"],"sourcesContent":["import { normalizeSource } from './normalizeSource';\nimport { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\n\nimport type { BareSource, FileCapableSource, Source } from '../types';\n\n/**\n * Compose several sources into one, read last-wins. A later member's `readVars()` value overrides an\n * earlier member's for the same key. A member may be a `(key) => string | undefined` reader for a\n * source that reads one key at a time, and it fills a key missing from every snapshot. Bind the result\n * with `Envapter.useSource`. Throws {@link EnvaptErrorCodes.InvalidMergedSource} with no members or with\n * more than one file-backed member.\n * @public\n * @see {@link https://envapt.materwelon.dev/docs/sources#combining-sources}\n */\nexport function merge(...members: (Source | ((key: string) => string | undefined))[]): Source {\n if (members.length === 0) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidMergedSource, 'merge requires at least one source.');\n }\n\n const sources = members.map(normalizeSource);\n\n const fileMembers = sources.filter((m): m is FileCapableSource => m.supportsFiles === true);\n if (fileMembers.length > 1) {\n throw new EnvaptError(\n EnvaptErrorCodes.InvalidMergedSource,\n 'merge accepts at most one filesystem-backed source, so the .env cascade and file APIs route to a single member.'\n );\n }\n\n const readVars = (): Record<string, string> => {\n const merged: Record<string, string> = {};\n for (const m of sources) Object.assign(merged, m.readVars());\n return merged;\n };\n\n const readers = sources.filter(\n (m): m is Source & { readVar: (key: string) => string | undefined } => typeof m.readVar === 'function'\n );\n const readVar =\n readers.length === 0\n ? undefined\n : (key: string): string | undefined => {\n // the last reader with a value wins\n for (let i = readers.length - 1; i >= 0; i--) {\n const found = readers[i]?.readVar(key);\n if (found !== undefined) return found;\n }\n return undefined;\n };\n\n const fileMember = fileMembers[0];\n if (!fileMember) {\n const bare: BareSource = { readVars };\n if (readVar) bare.readVar = readVar;\n return bare;\n }\n\n const fileCapable: FileCapableSource = {\n readVars,\n supportsFiles: true,\n readFile: (path, encoding) => fileMember.readFile(path, encoding),\n resolvePath: (baseDir, candidate) => fileMember.resolvePath(baseDir, candidate),\n normalizeBaseDir: (value) => fileMember.normalizeBaseDir(value),\n writeVars: (vars) => fileMember.writeVars(vars)\n };\n if (readVar) fileCapable.readVar = readVar;\n return fileCapable;\n}\n"],"mappings":"yGAcA,SAAgB,EAAM,GAAG,EAAqE,CAC1F,GAAI,EAAQ,SAAW,EACnB,MAAM,IAAI,EAAA,IAAkD,qCAAqC,EAGrG,IAAM,EAAU,EAAQ,IAAI,CAAe,EAErC,EAAc,EAAQ,OAAQ,GAA8B,EAAE,gBAAkB,EAAI,EAC1F,GAAI,EAAY,OAAS,EACrB,MAAM,IAAI,EAAA,IAEN,iHACJ,EAGJ,IAAM,MAAyC,CAC3C,IAAM,EAAiC,CAAC,EACxC,IAAK,IAAM,KAAK,EAAS,OAAO,OAAO,EAAQ,EAAE,SAAS,CAAC,EAC3D,OAAO,CACX,EAEM,EAAU,EAAQ,OACnB,GAAsE,OAAO,EAAE,SAAY,UAChG,EACM,EACF,EAAQ,SAAW,EACb,IAAA,GACC,GAAoC,CAEjC,IAAK,IAAI,EAAI,EAAQ,OAAS,EAAG,GAAK,EAAG,IAAK,CAC1C,IAAM,EAAQ,EAAQ,EAAE,EAAE,QAAQ,CAAG,EACrC,GAAI,IAAU,IAAA,GAAW,OAAO,CACpC,CAEJ,EAEJ,EAAa,EAAY,GAC/B,GAAI,CAAC,EAAY,CACb,IAAM,EAAmB,CAAE,UAAS,EAEpC,OADI,IAAS,EAAK,QAAU,GACrB,CACX,CAEA,IAAM,EAAiC,CACnC,WACA,cAAe,GACf,UAAW,EAAM,IAAa,EAAW,SAAS,EAAM,CAAQ,EAChE,aAAc,EAAS,IAAc,EAAW,YAAY,EAAS,CAAS,EAC9E,iBAAmB,GAAU,EAAW,iBAAiB,CAAK,EAC9D,UAAY,GAAS,EAAW,UAAU,CAAI,CAClD,EAEA,OADI,IAAS,EAAY,QAAU,GAC5B,CACX"}
@@ -15,16 +15,16 @@ declare const SCALAR: {
15
15
  readonly Port: "port";
16
16
  readonly Email: "email";
17
17
  };
18
- type ConverterToken = (typeof SCALAR)[keyof typeof SCALAR];
18
+ export type ConverterToken = (typeof SCALAR)[keyof typeof SCALAR];
19
19
  /**
20
20
  * Custom element converter for use inside {@link Converters.array}. Receives the trimmed,
21
21
  * non-empty raw string for one array slot and returns the parsed value.
22
22
  * @public
23
23
  * @see {@link https://envapt.materwelon.dev/docs/converters#custom-converters}
24
24
  */
25
- type CustomElementConverter<TReturn = unknown> = (raw: string) => TReturn;
25
+ export type CustomElementConverter<TReturn = unknown> = (raw: string) => TReturn;
26
26
  type ArrayElement = Exclude<ConverterToken, 'json' | 'regexp'> | CustomElementConverter;
27
- interface ArrayOf<TElement extends ArrayElement = ArrayElement> {
27
+ export interface ArrayOf<TElement extends ArrayElement = ArrayElement> {
28
28
  readonly __envaptKind: 'array';
29
29
  readonly of: TElement;
30
30
  readonly delimiter: string;
@@ -57,7 +57,7 @@ declare function buildArrayConverter(opts?: {
57
57
  * @public
58
58
  * @see {@link https://envapt.materwelon.dev/docs/converters#built-in-tokens}
59
59
  */
60
- declare const Converters: {
60
+ export declare const Converters: {
61
61
  readonly array: typeof buildArrayConverter;
62
62
  readonly String: "string";
63
63
  readonly Number: "number";
@@ -75,5 +75,4 @@ declare const Converters: {
75
75
  readonly Email: "email";
76
76
  };
77
77
  //#endregion
78
- export { ArrayElement, ArrayOf, ConverterToken, Converters, CustomElementConverter };
79
78
  //# sourceMappingURL=Converters.d.mts.map
@@ -6,11 +6,7 @@ import { SchemaConstraint } from "../types/Schema.mjs";
6
6
  import { EnvKeyInput } from "../types/Env.mjs";
7
7
  import { PrimitiveMethods } from "./PrimitiveMethods.mjs";
8
8
  //#region src/core/AdvancedMethods.d.ts
9
- /**
10
- * Mixin for advanced methods for environment variable conversion using built-in and custom converters
11
- * @internal
12
- */
13
- declare class AdvancedMethods extends PrimitiveMethods {
9
+ export declare class AdvancedMethods extends PrimitiveMethods {
14
10
  /**
15
11
  * Get an environment variable using a built-in converter.
16
12
  *
@@ -59,8 +55,8 @@ declare class AdvancedMethods extends PrimitiveMethods {
59
55
  * converter (a token, an `array()` token, or a custom parser), and the returned record holds
60
56
  * every converted value, all non-undefined. Collects every missing or empty key and throws one
61
57
  * `MissingEnvValue` listing them all. Pass a `casing` (`'camelCase'`, `'PascalCase'`, or
62
- * `'kebab-case'`) to rename the record keys, splitting on underscores, which assumes the
63
- * conventional SCREAMING_SNAKE env-var names. With no casing the keys stay as-is.
58
+ * `'kebab-case'`) to rename the record keys. The rename splits each key on underscores, as in
59
+ * SCREAMING_SNAKE env-var names. With no casing the keys stay as-is.
64
60
  * @see {@link https://envapt.materwelon.dev/docs/envapter#fail-fast-on-missing-values}
65
61
  * @see {@link https://envapt.materwelon.dev/docs/converters#require-a-converted-value}
66
62
  */
@@ -72,11 +68,10 @@ declare class AdvancedMethods extends PrimitiveMethods {
72
68
  /**
73
69
  * Validate an environment variable through a {@link StandardSchemaV1}-conformant schema
74
70
  * (zod, valibot, arktype, etc). Throws `MissingEnvValue` if the env value is absent and
75
- * no fallback is provided. The fallback, when provided, is returned as-is on missing.
76
- * It does NOT pass through the schema, mirroring custom-converter behavior.
71
+ * no fallback is provided. When the value is missing, the fallback is returned as-is and does
72
+ * not pass through the schema.
77
73
  *
78
- * Synchronous schemas only. A Promise-returning `validate` triggers an
79
- * `InvalidUserDefinedConfig` throw at the call site.
74
+ * Synchronous schemas only. A `validate` that returns a Promise throws `InvalidUserDefinedConfig`.
80
75
  *
81
76
  * @example
82
77
  * ```ts
@@ -92,5 +87,4 @@ declare class AdvancedMethods extends PrimitiveMethods {
92
87
  parse<Schema extends StandardSchemaV1>(key: EnvKeyInput, schema: SchemaConstraint<Schema>, fallback?: InferSchemaOutput<Schema>): InferSchemaOutput<Schema>;
93
88
  }
94
89
  //#endregion
95
- export { AdvancedMethods };
96
90
  //# sourceMappingURL=AdvancedMethods.d.mts.map
@@ -3,11 +3,9 @@ import { FileApiMode } from "../types/Options.mjs";
3
3
  import { EnvKeyInput } from "../types/Env.mjs";
4
4
  import { Source } from "../types/Source.mjs";
5
5
  //#region src/core/EnvapterBase.d.ts
6
- /** @internal */
7
- declare abstract class EnvapterBase {
6
+ export declare abstract class EnvapterBase {
8
7
  /**
9
- * Enable or disable strict mode. Default `false`. Setting refreshes the cache so
10
- * previously-cached converted values get re-evaluated under the new rule.
8
+ * Enable or disable strict mode. Default `false`. Setting it clears and rebuilds the cache.
11
9
  * @see {@link https://envapt.materwelon.dev/docs/strict-mode#what-strict-mode-changes}
12
10
  */
13
11
  static set strict(value: boolean);
@@ -23,13 +21,13 @@ declare abstract class EnvapterBase {
23
21
  /**
24
22
  * Opt-in mirror of dotenv-loaded keys back to `process.env`. Default `false`.
25
23
  *
26
- * Only keys the loader actually wrote are mirrored, so collision behavior follows
27
- * `envFileOptions.override`: with the default `false`, pre-existing `process.env` values
28
- * are preserved. With `true`, the file value wins in both the cache and the mirror.
24
+ * `envFileOptions.override` controls collisions because only keys the loader wrote are mirrored.
25
+ * With the default `false`, existing `process.env` values stay. With `true`, the file value
26
+ * wins in both the cache and the mirror.
29
27
  *
30
- * Flipping `false → true` mirrors the existing tracked delta immediately (no cache
31
- * refresh). Flipping `true → false` is one-way: previously mirrored keys remain in
32
- * `process.env` until the process exits.
28
+ * Flipping `false → true` mirrors the loaded keys immediately without a cache refresh.
29
+ * Flipping `true → false` does not remove anything. Mirrored keys stay in `process.env`
30
+ * until the process exits.
33
31
  * @see {@link https://envapt.materwelon.dev/docs/configuration#mirroring-to-processenv}
34
32
  */
35
33
  static set syncProcessEnv(value: boolean);
@@ -37,7 +35,7 @@ declare abstract class EnvapterBase {
37
35
  /**
38
36
  * On the portable build, this controls the filesystem-only config APIs (`envPaths`, `baseDir`,
39
37
  * `envFileOptions`, `configureProfiles`, `resetProfiles`). `'warn'` (the default) warns once and
40
- * no-ops, `'throw'` throws {@link EnvaptError} `FileApiUnsupported`. The node build runs these
38
+ * no-ops. `'throw'` throws {@link EnvaptError} `FileApiUnsupported`. The node build runs these
41
39
  * APIs normally and this value has no effect there.
42
40
  * @see {@link https://envapt.materwelon.dev/docs/compatibility#binding-by-runtime}
43
41
  */
@@ -65,5 +63,4 @@ declare abstract class EnvapterBase {
65
63
  getRaw(key: EnvKeyInput): string | undefined;
66
64
  }
67
65
  //#endregion
68
- export { EnvapterBase };
69
66
  //# sourceMappingURL=EnvapterBase.d.mts.map
@@ -11,7 +11,7 @@
11
11
  * @public
12
12
  * @see {@link https://envapt.materwelon.dev/docs/environment#detecting-the-environment}
13
13
  */
14
- declare enum Environment {
14
+ export declare enum Environment {
15
15
  /** The default when no environment variable names a known environment. */
16
16
  Development = 0,
17
17
  /** Selected when an environment variable reads `staging`. */
@@ -22,5 +22,4 @@ declare enum Environment {
22
22
  Test = 3
23
23
  }
24
24
  //#endregion
25
- export { Environment };
26
25
  //# sourceMappingURL=Environment.d.mts.map
@@ -1,11 +1,7 @@
1
1
  import { Environment } from "./Environment.mjs";
2
2
  import { EnvapterBase } from "./EnvapterBase.mjs";
3
3
  //#region src/core/EnvironmentMethods.d.ts
4
- /**
5
- * Mixin for environment detection and checking methods
6
- * @internal
7
- */
8
- declare class EnvironmentMethods extends EnvapterBase {
4
+ export declare class EnvironmentMethods extends EnvapterBase {
9
5
  /**
10
6
  * Get the current application environment
11
7
  * @see {@link https://envapt.materwelon.dev/docs/environment#detecting-the-environment}
@@ -62,5 +58,4 @@ declare class EnvironmentMethods extends EnvapterBase {
62
58
  get isTest(): boolean;
63
59
  }
64
60
  //#endregion
65
- export { EnvironmentMethods };
66
61
  //# sourceMappingURL=EnvironmentMethods.d.mts.map
@@ -2,10 +2,7 @@ import { ConditionalReturn } from "../types/Conversion.mjs";
2
2
  import { EnvKeyInput } from "../types/Env.mjs";
3
3
  import { EnvironmentMethods } from "./EnvironmentMethods.mjs";
4
4
  //#region src/core/PrimitiveMethods.d.ts
5
- /**
6
- * @internal
7
- */
8
- declare class PrimitiveMethods extends EnvironmentMethods {
5
+ export declare class PrimitiveMethods extends EnvironmentMethods {
9
6
  /**
10
7
  * Get a string environment variable with optional fallback.
11
8
  * Supports template variable resolution using `${VAR}` syntax.
@@ -63,5 +60,4 @@ declare class PrimitiveMethods extends EnvironmentMethods {
63
60
  getSymbol<Default extends symbol | undefined = undefined>(key: EnvKeyInput, def?: Default): ConditionalReturn<symbol, Default>;
64
61
  }
65
62
  //#endregion
66
- export { PrimitiveMethods };
67
63
  //# sourceMappingURL=PrimitiveMethods.d.mts.map
@@ -31,11 +31,11 @@ import { EnvaptFieldDecorator } from "../../types/Decorator.mjs";
31
31
  * }
32
32
  * ```
33
33
  */
34
- declare function Envapt(key: EnvKeyInput, options?: {
34
+ export declare function Envapt(key: EnvKeyInput, options?: {
35
35
  fallback: undefined;
36
36
  converter?: undefined;
37
37
  }): EnvaptFieldDecorator<string | undefined>;
38
- declare function Envapt<TFallback>(key: EnvKeyInput, options: {
38
+ export declare function Envapt<TFallback>(key: EnvKeyInput, options: {
39
39
  converter: (raw: string | undefined, fallback: TFallback) => TFallback;
40
40
  fallback: TFallback;
41
41
  } | {
@@ -66,7 +66,7 @@ declare function Envapt<TFallback>(key: EnvKeyInput, options: {
66
66
  * }
67
67
  * ```
68
68
  */
69
- declare function Envapt<TReturnType>(key: EnvKeyInput, options: {
69
+ export declare function Envapt<TReturnType>(key: EnvKeyInput, options: {
70
70
  converter: ConverterFunction<TReturnType>;
71
71
  required?: false;
72
72
  } | {
@@ -89,11 +89,11 @@ declare function Envapt<TReturnType>(key: EnvKeyInput, options: {
89
89
  * \@Envapt('APP_PORT', { converter: Converters.Number, fallback: 3000 })
90
90
  * static readonly port: number;
91
91
  *
92
- * // the Url fallback is a URL instance, not a string
92
+ * // the Url fallback is a URL instance
93
93
  * \@Envapt('APP_URL', { converter: Converters.Url, fallback: new URL('http://localhost:3000') })
94
94
  * static readonly url: URL;
95
95
  *
96
- * // prefers CANARY_URL when present, otherwise APP_URL
96
+ * // reads CANARY_URL when present, otherwise APP_URL
97
97
  * \@Envapt(['CANARY_URL', 'APP_URL'], { converter: Converters.Url })
98
98
  * static readonly canaryUrl: URL | undefined;
99
99
  *
@@ -112,7 +112,7 @@ declare function Envapt<TReturnType>(key: EnvKeyInput, options: {
112
112
  * }
113
113
  * ```
114
114
  */
115
- declare function Envapt<TConverter extends BuiltInConverter | ArrayOf>(key: EnvKeyInput, options: {
115
+ export declare function Envapt<TConverter extends BuiltInConverter | ArrayOf>(key: EnvKeyInput, options: {
116
116
  converter: TConverter;
117
117
  fallback: InferConverterFallbackType<TConverter>;
118
118
  required?: false;
@@ -120,7 +120,7 @@ declare function Envapt<TConverter extends BuiltInConverter | ArrayOf>(key: EnvK
120
120
  converter: TConverter;
121
121
  required: true;
122
122
  }): EnvaptFieldDecorator<InferConverterReturnType<TConverter>>;
123
- declare function Envapt<TConverter extends BuiltInConverter | ArrayOf>(key: EnvKeyInput, options: {
123
+ export declare function Envapt<TConverter extends BuiltInConverter | ArrayOf>(key: EnvKeyInput, options: {
124
124
  converter: TConverter;
125
125
  fallback?: undefined;
126
126
  required?: false;
@@ -142,7 +142,7 @@ declare function Envapt<TConverter extends BuiltInConverter | ArrayOf>(key: EnvK
142
142
  * }
143
143
  * ```
144
144
  */
145
- declare function Envapt<TConstructor extends PrimitiveConstructor>(key: EnvKeyInput, options: {
145
+ export declare function Envapt<TConstructor extends PrimitiveConstructor>(key: EnvKeyInput, options: {
146
146
  converter: TConstructor;
147
147
  fallback: InferPrimitiveReturnType<TConstructor>;
148
148
  required?: false;
@@ -150,16 +150,15 @@ declare function Envapt<TConstructor extends PrimitiveConstructor>(key: EnvKeyIn
150
150
  converter: TConstructor;
151
151
  required: true;
152
152
  }): EnvaptFieldDecorator<InferPrimitiveReturnType<TConstructor>>;
153
- declare function Envapt<TConstructor extends PrimitiveConstructor>(key: EnvKeyInput, options: {
153
+ export declare function Envapt<TConstructor extends PrimitiveConstructor>(key: EnvKeyInput, options: {
154
154
  converter: TConstructor;
155
155
  fallback?: undefined;
156
156
  required?: false;
157
157
  }): EnvaptFieldDecorator<InferPrimitiveReturnType<TConstructor> | undefined>;
158
158
  /**
159
159
  * Required, no converter (raw string). Throws `MissingEnvValue` on first access when the env
160
- * value is missing or empty after trimming, independent of the global `Envapter.strict` flag.
161
- * Pairing `required: true` with `fallback` matches no overload at compile time, and the runtime
162
- * Validator rejects dynamic objects that bypass the types.
160
+ * value is missing or empty. Pairing `required: true` with `fallback` matches no overload at
161
+ * compile time, and a call that bypasses the types throws `InvalidUserDefinedConfig` at runtime.
163
162
  *
164
163
  * @param key - Environment variable name(s) to load
165
164
  * @param options - `{ required: true }`
@@ -172,17 +171,17 @@ declare function Envapt<TConstructor extends PrimitiveConstructor>(key: EnvKeyIn
172
171
  * }
173
172
  * ```
174
173
  */
175
- declare function Envapt(key: EnvKeyInput, options: {
174
+ export declare function Envapt(key: EnvKeyInput, options: {
176
175
  required: true;
177
176
  }): EnvaptFieldDecorator<string>;
178
177
  /**
179
- * A Standard Schema v1 adapter (zod, valibot, arktype, hand-rolled). Synchronous schemas only,
180
- * so a Promise-returning `validate` throws `InvalidUserDefinedConfig` at runtime. Pairing
181
- * `schema` with `converter` matches no overload at compile time, and the runtime Validator
182
- * rejects dynamic objects that bypass the types.
178
+ * A Standard Schema v1 adapter (zod, valibot, arktype, hand-rolled). Synchronous schemas only.
179
+ * A `validate` that returns a Promise throws `InvalidUserDefinedConfig` at runtime. Pairing
180
+ * `schema` with `converter` matches no overload at compile time, and a call that bypasses the
181
+ * types throws `InvalidUserDefinedConfig` at runtime.
183
182
  * @public
184
183
  */
185
- declare function Envapt<Schema extends StandardSchemaV1>(key: EnvKeyInput, options: {
184
+ export declare function Envapt<Schema extends StandardSchemaV1>(key: EnvKeyInput, options: {
186
185
  schema: SchemaConstraint<Schema>;
187
186
  fallback?: InferSchemaOutput<Schema>;
188
187
  required?: false;
@@ -191,5 +190,4 @@ declare function Envapt<Schema extends StandardSchemaV1>(key: EnvKeyInput, optio
191
190
  required: true;
192
191
  }): EnvaptFieldDecorator<InferSchemaOutput<Schema>>;
193
192
  //#endregion
194
- export { Envapt };
195
193
  //# sourceMappingURL=Envapt.d.mts.map