@@ -31,11 +31,15 @@ function parseEnvLine(line) {
3131 return { key, value } ;
3232}
3333
34+ function getEnvPath ( ) {
35+ return process . env . CCR_ENV_PATH || DEFAULT_ENV_PATH ;
36+ }
37+
3438function loadDotenv ( ) {
3539 if ( envLoaded ) return ;
3640 envLoaded = true ;
3741
38- const envPath = process . env . CCR_ENV_PATH || DEFAULT_ENV_PATH ;
42+ const envPath = getEnvPath ( ) ;
3943 if ( ! fs . existsSync ( envPath ) ) return ;
4044
4145 const content = fs . readFileSync ( envPath , 'utf8' ) ;
@@ -62,6 +66,73 @@ function resolveEnv(value) {
6266 } ) ;
6367}
6468
69+ function readEnvFile ( ) {
70+ const envPath = getEnvPath ( ) ;
71+ if ( ! fs . existsSync ( envPath ) ) {
72+ return { path : envPath , entries : { } } ;
73+ }
74+
75+ const content = fs . readFileSync ( envPath , 'utf8' ) ;
76+ const entries = { } ;
77+ content . split ( / \r ? \n / ) . forEach ( ( line ) => {
78+ const parsed = parseEnvLine ( line ) ;
79+ if ( ! parsed ) return ;
80+ entries [ parsed . key ] = parsed . value ;
81+ } ) ;
82+
83+ return { path : envPath , entries } ;
84+ }
85+
86+ function formatEnvValue ( value ) {
87+ const safe = / ^ [ A - Z a - z 0 - 9 _ . / : @ - ] + $ / . test ( value ) ;
88+ if ( safe ) return value ;
89+ const escaped = value
90+ . replace ( / \\ / g, '\\\\' )
91+ . replace ( / " / g, '\\"' )
92+ . replace ( / \n / g, '\\n' ) ;
93+ return `"${ escaped } "` ;
94+ }
95+
96+ function writeEnvValue ( key , value ) {
97+ const envPath = getEnvPath ( ) ;
98+ const normalizedKey = String ( key || '' ) . trim ( ) ;
99+ if ( ! / ^ [ A - Z a - z 0 - 9 _ ] + $ / . test ( normalizedKey ) ) {
100+ throw new Error ( 'Invalid environment key' ) ;
101+ }
102+
103+ const normalizedValue = value === undefined || value === null ? '' : String ( value ) ;
104+ const formattedValue = formatEnvValue ( normalizedValue ) ;
105+
106+ let lines = [ ] ;
107+ let updated = false ;
108+
109+ if ( fs . existsSync ( envPath ) ) {
110+ const content = fs . readFileSync ( envPath , 'utf8' ) ;
111+ lines = content . split ( / \r ? \n / ) ;
112+ }
113+
114+ lines = lines . map ( ( line ) => {
115+ const parsed = parseEnvLine ( line ) ;
116+ if ( ! parsed || parsed . key !== normalizedKey ) return line ;
117+ const prefix = line . trim ( ) . startsWith ( 'export ' ) ? 'export ' : '' ;
118+ updated = true ;
119+ return `${ prefix } ${ normalizedKey } =${ formattedValue } ` ;
120+ } ) ;
121+
122+ if ( ! updated ) {
123+ lines . push ( `${ normalizedKey } =${ formattedValue } ` ) ;
124+ }
125+
126+ const output = lines . filter ( ( line , index , arr ) => {
127+ if ( index === arr . length - 1 ) return true ;
128+ return line !== '' || arr [ index + 1 ] !== '' ;
129+ } ) ;
130+
131+ fs . writeFileSync ( envPath , `${ output . join ( '\n' ) } \n` , 'utf8' ) ;
132+
133+ return { path : envPath , updated : true } ;
134+ }
135+
65136function resolveConfigValue ( value , key ) {
66137 if ( Array . isArray ( value ) ) {
67138 return value . map ( ( item ) => resolveConfigValue ( item , key ) ) ;
@@ -125,6 +196,9 @@ function resolveProviderKey(provider) {
125196
126197module . exports = {
127198 loadConfig,
199+ getEnvPath,
200+ readEnvFile,
201+ writeEnvValue,
128202 getConfigPath,
129203 getConfigDir,
130204 resolveProviderKey
0 commit comments