Created
November 11, 2024 01:14
-
-
Save nadeesha/0df80eebf27fd1056d57084e4e39c7c4 to your computer and use it in GitHub Desktop.
A straightforward and type-safe .netrc reader/writer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as fs from 'fs'; | |
import * as os from 'os'; | |
import * as path from 'path'; | |
interface NetrcEntry { | |
machine: string; | |
login: string; | |
password: string; | |
account?: string; | |
} | |
interface NetrcData { | |
[key: string]: NetrcEntry; | |
} | |
export class NetrcUtils { | |
private netrcPath: string; | |
constructor(customPath?: string) { | |
this.netrcPath = customPath || path.join(os.homedir(), '.netrc'); | |
} | |
/** | |
* Read and parse the .netrc file | |
* @returns Promise<NetrcData> Parsed netrc data as JSON | |
*/ | |
async read(): Promise<NetrcData> { | |
try { | |
const content = await fs.promises.readFile(this.netrcPath, 'utf8'); | |
return this.parseNetrc(content); | |
} catch (error) { | |
if ((error as NodeJS.ErrnoException).code === 'ENOENT') { | |
return {}; | |
} | |
throw error; | |
} | |
} | |
/** | |
* Write data to the .netrc file | |
* @param data NetrcData to write | |
*/ | |
async write(data: NetrcData): Promise<void> { | |
const content = this.stringifyNetrc(data); | |
await fs.promises.writeFile(this.netrcPath, content, { | |
mode: 0o600, // Set file permissions to be readable/writable only by owner | |
}); | |
} | |
/** | |
* Parse netrc content into JSON format | |
* @param content Raw netrc file content | |
* @returns Parsed NetrcData | |
*/ | |
private parseNetrc(content: string): NetrcData { | |
const result: NetrcData = {}; | |
const lines = content.split('\n'); | |
let currentMachine: string | null = null; | |
let currentEntry: Partial<NetrcEntry> = {}; | |
for (const line of lines) { | |
const tokens = line.trim().split(/\s+/); | |
for (let i = 0; i < tokens.length; i++) { | |
const token = tokens[i]; | |
switch (token) { | |
case 'machine': | |
if (currentMachine && Object.keys(currentEntry).length) { | |
result[currentMachine] = currentEntry as NetrcEntry; | |
} | |
currentMachine = tokens[++i]; | |
currentEntry = { machine: currentMachine }; | |
break; | |
case 'login': | |
currentEntry.login = tokens[++i]; | |
break; | |
case 'password': | |
currentEntry.password = tokens[++i]; | |
break; | |
case 'account': | |
currentEntry.account = tokens[++i]; | |
break; | |
} | |
} | |
} | |
// Add the last entry | |
if (currentMachine && Object.keys(currentEntry).length) { | |
result[currentMachine] = currentEntry as NetrcEntry; | |
} | |
return result; | |
} | |
/** | |
* Convert NetrcData back to netrc file format | |
* @param data NetrcData to convert | |
* @returns Formatted netrc content | |
*/ | |
private stringifyNetrc(data: NetrcData): string { | |
return Object.entries(data) | |
.map(([_, entry]) => { | |
let result = `machine ${entry.machine}\n login ${entry.login}\n password ${entry.password}`; | |
if (entry.account) { | |
result += `\n account ${entry.account}`; | |
} | |
return result; | |
}) | |
.join('\n\n'); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment