@orxataguy/tyr 1.5.0 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,118 +1,118 @@
1
- import { execa } from 'execa';
2
- import { resolve } from 'path';
3
- import { homedir } from 'os';
4
- import inquirer from 'inquirer';
5
-
6
- import { TyrError } from '../core/TyrError.js';
7
-
8
- /**
9
- * @class ShellManager
10
- * @description Terminal command executor. Maintains the working directory (CWD) state to chain commands in specific folders.
11
- */
12
- export class ShellManager {
13
- private cwd: string;
14
-
15
- constructor() {
16
- this.cwd = process.cwd();
17
- }
18
-
19
- /**
20
- * @method exec
21
- * @description Executes a command in the system shell and returns the standard output.
22
- * @param {string} command - The full command to execute.
23
- * @returns {Promise<string>} The command output (stdout) trimmed of extra whitespace.
24
- * @example
25
- * const version = await shell.exec('node -v');
26
- */
27
- public async exec(command: string): Promise<string> {
28
- try {
29
- const result = await execa(command, { shell: true, cwd: this.cwd });
30
- return result.stdout.trim();
31
- } catch (e) {
32
- throw new TyrError(`An error occurred while executing the command: ${command}`, e);
33
- }
34
- }
35
-
36
- /**
37
- * @method showLoader
38
- * @description Displays a spinner loader in the terminal.
39
- * @param {string} message - Informational text to show alongside the spinner.
40
- * @returns {void}
41
- * @example
42
- * shell.showLoader('Loading...');
43
- */
44
- public showLoader = (message: string): { stop: () => void } => {
45
- const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
46
- let i = 0;
47
- let stopped = false;
48
-
49
- const interval = setInterval(() => {
50
- if (!stopped) {
51
- process.stdout.write(`\r${frames[i]} ${message}`);
52
- i = (i + 1) % frames.length;
53
- }
54
- }, 80);
55
-
56
- return {
57
- stop: () => {
58
- stopped = true;
59
- clearInterval(interval);
60
- process.stdout.write('\r');
61
- }
62
- };
63
- };
64
-
65
- /**
66
- * @method input
67
- * @description Prompts the user for a value via CLI.
68
- * @param {string} question - Informational text shown as the prompt.
69
- * @returns {Promise<string>} The value entered by the user.
70
- * @example
71
- * const name = await shell.input("What's your name?");
72
- */
73
- public async input(question: string): Promise<string> {
74
- try {
75
- const result = await inquirer.prompt([
76
- {
77
- type: 'input',
78
- name: 'value',
79
- message: question,
80
- },
81
- ]);
82
-
83
- return result.value.trim();
84
- } catch (e) {
85
- throw new TyrError(`An error occurred while prompting the question: ${question}`, e);
86
- }
87
- }
88
-
89
- /**
90
- * @method cd
91
- * @description Changes the internal working directory for subsequent commands executed by this instance.
92
- * @param {string} path - Absolute or relative path to change to.
93
- * @example
94
- * shell.cd('./backend');
95
- * await shell.exec('npm install'); // Runs inside /backend
96
- */
97
- public cd(path: string): void {
98
- let expandedPath = path;
99
- if (path.startsWith('~/')) {
100
- expandedPath = path.replace('~', homedir());
101
- } else if (path === '~') {
102
- expandedPath = homedir();
103
- }
104
-
105
- this.cwd = resolve(this.cwd, expandedPath);
106
- }
107
- }
108
-
109
- /**
110
- * @object ShellManagerTests
111
- * @description Test parameters to validate ShellManager functionality.
112
- */
113
- export const ShellManagerTests = {
114
- exec: { command: 'node -v' },
115
- cd: { path: '/tmp' },
116
- input: { question: 'Enter a test value:' },
117
- showLoader: { message: 'Loading test...' }
1
+ import { execa } from 'execa';
2
+ import { resolve } from 'path';
3
+ import { homedir } from 'os';
4
+ import inquirer from 'inquirer';
5
+
6
+ import { TyrError } from '../core/TyrError.js';
7
+
8
+ /**
9
+ * @class ShellManager
10
+ * @description Terminal command executor. Maintains the working directory (CWD) state to chain commands in specific folders.
11
+ */
12
+ export class ShellManager {
13
+ private cwd: string;
14
+
15
+ constructor() {
16
+ this.cwd = process.cwd();
17
+ }
18
+
19
+ /**
20
+ * @method exec
21
+ * @description Executes a command in the system shell and returns the standard output.
22
+ * @param {string} command - The full command to execute.
23
+ * @returns {Promise<string>} The command output (stdout) trimmed of extra whitespace.
24
+ * @example
25
+ * const version = await shell.exec('node -v');
26
+ */
27
+ public async exec(command: string): Promise<string> {
28
+ try {
29
+ const result = await execa(command, { shell: true, cwd: this.cwd });
30
+ return result.stdout.trim();
31
+ } catch (e) {
32
+ throw new TyrError(`An error occurred while executing the command: ${command}`, e);
33
+ }
34
+ }
35
+
36
+ /**
37
+ * @method showLoader
38
+ * @description Displays a spinner loader in the terminal.
39
+ * @param {string} message - Informational text to show alongside the spinner.
40
+ * @returns {void}
41
+ * @example
42
+ * shell.showLoader('Loading...');
43
+ */
44
+ public showLoader = (message: string): { stop: () => void } => {
45
+ const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
46
+ let i = 0;
47
+ let stopped = false;
48
+
49
+ const interval = setInterval(() => {
50
+ if (!stopped) {
51
+ process.stdout.write(`\r${frames[i]} ${message}`);
52
+ i = (i + 1) % frames.length;
53
+ }
54
+ }, 80);
55
+
56
+ return {
57
+ stop: () => {
58
+ stopped = true;
59
+ clearInterval(interval);
60
+ process.stdout.write('\r');
61
+ }
62
+ };
63
+ };
64
+
65
+ /**
66
+ * @method input
67
+ * @description Prompts the user for a value via CLI.
68
+ * @param {string} question - Informational text shown as the prompt.
69
+ * @returns {Promise<string>} The value entered by the user.
70
+ * @example
71
+ * const name = await shell.input("What's your name?");
72
+ */
73
+ public async input(question: string): Promise<string> {
74
+ try {
75
+ const result = await inquirer.prompt([
76
+ {
77
+ type: 'input',
78
+ name: 'value',
79
+ message: question,
80
+ },
81
+ ]);
82
+
83
+ return result.value.trim();
84
+ } catch (e) {
85
+ throw new TyrError(`An error occurred while prompting the question: ${question}`, e);
86
+ }
87
+ }
88
+
89
+ /**
90
+ * @method cd
91
+ * @description Changes the internal working directory for subsequent commands executed by this instance.
92
+ * @param {string} path - Absolute or relative path to change to.
93
+ * @example
94
+ * shell.cd('./backend');
95
+ * await shell.exec('npm install'); // Runs inside /backend
96
+ */
97
+ public cd(path: string): void {
98
+ let expandedPath = path;
99
+ if (path.startsWith('~/')) {
100
+ expandedPath = path.replace('~', homedir());
101
+ } else if (path === '~') {
102
+ expandedPath = homedir();
103
+ }
104
+
105
+ this.cwd = resolve(this.cwd, expandedPath);
106
+ }
107
+ }
108
+
109
+ /**
110
+ * @object ShellManagerTests
111
+ * @description Test parameters to validate ShellManager functionality.
112
+ */
113
+ export const ShellManagerTests = {
114
+ exec: { command: 'node -v' },
115
+ cd: { path: '/tmp' },
116
+ input: { question: 'Enter a test value:' },
117
+ showLoader: { message: 'Loading test...' }
118
118
  };
@@ -1,83 +1,83 @@
1
- import { ShellManager } from './ShellManager.js';
2
- import { Logger } from '../core/Logger.js';
3
- import { TyrError } from '../core/TyrError.js';
4
-
5
- /**
6
- * @class SystemManager
7
- * @description General OS maintenance and cleanup utilities.
8
- */
9
- export class SystemManager {
10
- private shell: ShellManager;
11
- private logger: Logger;
12
-
13
- constructor(shell: ShellManager, logger: Logger) {
14
- this.shell = shell;
15
- this.logger = logger;
16
- }
17
-
18
- /**
19
- * @method killPort
20
- * @description Identifies which process is occupying a port and force-kills it.
21
- * @param {number|string} port - The port to free (e.g. 3000).
22
- * @returns {Promise<boolean>} True if a process was killed, False if the port was already free.
23
- * @example
24
- * await sys.killPort(8080);
25
- */
26
- public async killPort(port: number | string): Promise<boolean> {
27
- try {
28
- let pid: string | null = null;
29
-
30
- if (process.platform === 'win32') {
31
- const result = await this.shell.exec(`netstat -ano | findstr :${port}`).catch(() => null);
32
- if (!result?.trim()) return false;
33
- const lines = result.trim().split('\n').filter(l => l.includes(`LISTENING`));
34
- if (!lines.length) return false;
35
- pid = lines[0].trim().split(/\s+/).pop() ?? null;
36
- } else {
37
- pid = await this.shell.exec(`lsof -t -i:${port}`).catch(() => null);
38
- }
39
-
40
- if (!pid?.trim()) return false;
41
-
42
- this.logger.warn(`Port ${port} blocked by PID ${pid.trim()}. Killing...`);
43
-
44
- if (process.platform === 'win32') {
45
- await this.shell.exec(`taskkill /F /PID ${pid.trim()}`);
46
- } else {
47
- await this.shell.exec(`kill -9 ${pid.trim()}`);
48
- }
49
-
50
- this.logger.success(`Port ${port} freed.`);
51
- return true;
52
- } catch (e) {
53
- throw new TyrError(`Could not free port: ${port}`, e, 'Check that you have the necessary permissions.');
54
- }
55
- }
56
-
57
- /**
58
- * @method nukeNodeModules
59
- * @description Removes node_modules and package-lock.json from the current directory.
60
- * Useful for fixing corrupted NPM installations.
61
- * @example
62
- * await sys.nukeNodeModules();
63
- */
64
- public async nukeNodeModules(): Promise<void> {
65
- this.logger.info('Cleaning up dependencies...');
66
- try {
67
- if (process.platform === 'win32') {
68
- await this.shell.exec('if exist node_modules rmdir /s /q node_modules');
69
- await this.shell.exec('if exist package-lock.json del /f /q package-lock.json');
70
- } else {
71
- await this.shell.exec('rm -rf node_modules package-lock.json');
72
- }
73
- this.logger.success('node_modules and package-lock.json removed.');
74
- } catch (e) {
75
- if (e instanceof TyrError) throw e;
76
- throw new TyrError('Could not remove node_modules.', e, 'Check write permissions on the current directory.');
77
- }
78
- }
79
- }
80
-
81
- export const SystemManagerTests = {
82
- killPort: { port: 3000 },
83
- };
1
+ import { ShellManager } from './ShellManager.js';
2
+ import { Logger } from '../core/Logger.js';
3
+ import { TyrError } from '../core/TyrError.js';
4
+
5
+ /**
6
+ * @class SystemManager
7
+ * @description General OS maintenance and cleanup utilities.
8
+ */
9
+ export class SystemManager {
10
+ private shell: ShellManager;
11
+ private logger: Logger;
12
+
13
+ constructor(shell: ShellManager, logger: Logger) {
14
+ this.shell = shell;
15
+ this.logger = logger;
16
+ }
17
+
18
+ /**
19
+ * @method killPort
20
+ * @description Identifies which process is occupying a port and force-kills it.
21
+ * @param {number|string} port - The port to free (e.g. 3000).
22
+ * @returns {Promise<boolean>} True if a process was killed, False if the port was already free.
23
+ * @example
24
+ * await sys.killPort(8080);
25
+ */
26
+ public async killPort(port: number | string): Promise<boolean> {
27
+ try {
28
+ let pid: string | null = null;
29
+
30
+ if (process.platform === 'win32') {
31
+ const result = await this.shell.exec(`netstat -ano | findstr :${port}`).catch(() => null);
32
+ if (!result?.trim()) return false;
33
+ const lines = result.trim().split('\n').filter(l => l.includes(`LISTENING`));
34
+ if (!lines.length) return false;
35
+ pid = lines[0].trim().split(/\s+/).pop() ?? null;
36
+ } else {
37
+ pid = await this.shell.exec(`lsof -t -i:${port}`).catch(() => null);
38
+ }
39
+
40
+ if (!pid?.trim()) return false;
41
+
42
+ this.logger.warn(`Port ${port} blocked by PID ${pid.trim()}. Killing...`);
43
+
44
+ if (process.platform === 'win32') {
45
+ await this.shell.exec(`taskkill /F /PID ${pid.trim()}`);
46
+ } else {
47
+ await this.shell.exec(`kill -9 ${pid.trim()}`);
48
+ }
49
+
50
+ this.logger.success(`Port ${port} freed.`);
51
+ return true;
52
+ } catch (e) {
53
+ throw new TyrError(`Could not free port: ${port}`, e, 'Check that you have the necessary permissions.');
54
+ }
55
+ }
56
+
57
+ /**
58
+ * @method nukeNodeModules
59
+ * @description Removes node_modules and package-lock.json from the current directory.
60
+ * Useful for fixing corrupted NPM installations.
61
+ * @example
62
+ * await sys.nukeNodeModules();
63
+ */
64
+ public async nukeNodeModules(): Promise<void> {
65
+ this.logger.info('Cleaning up dependencies...');
66
+ try {
67
+ if (process.platform === 'win32') {
68
+ await this.shell.exec('if exist node_modules rmdir /s /q node_modules');
69
+ await this.shell.exec('if exist package-lock.json del /f /q package-lock.json');
70
+ } else {
71
+ await this.shell.exec('rm -rf node_modules package-lock.json');
72
+ }
73
+ this.logger.success('node_modules and package-lock.json removed.');
74
+ } catch (e) {
75
+ if (e instanceof TyrError) throw e;
76
+ throw new TyrError('Could not remove node_modules.', e, 'Check write permissions on the current directory.');
77
+ }
78
+ }
79
+ }
80
+
81
+ export const SystemManagerTests = {
82
+ killPort: { port: 3000 },
83
+ };
@@ -1,62 +1,62 @@
1
- import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
2
- import * as cheerio from 'cheerio';
3
-
4
- import { Logger } from '../core/Logger.js';
5
- import { TyrError } from '../core/TyrError.js';
6
-
7
- /**
8
- * @class WebManager
9
- * @description Utilities for accessing APIs and the Internet.
10
- */
11
- export class WebManager {
12
- private logger: Logger;
13
-
14
- constructor(logger: Logger) {
15
- this.logger = logger;
16
- }
17
-
18
- private async fetch(url: string, config?: AxiosRequestConfig): Promise<cheerio.CheerioAPI> {
19
- try {
20
- const response: AxiosResponse<string> = await axios.get(url, config);
21
- return cheerio.load(response.data);
22
- } catch (e) {
23
- throw new TyrError(`Could not fetch URL: ${url}`, e, 'Check your internet connection and that the URL is valid.');
24
- }
25
- }
26
-
27
- /**
28
- * @method selectFromWeb
29
- * @description Selects elements from a web page using a CSS selector.
30
- * @param {string} url - URL of the page to scrape.
31
- * @param {Function} selector - CSS selector function.
32
- * @returns {Promise<string[]>} List of extracted text values.
33
- * @example
34
- * const titles = await web.selectFromWeb('https://example.com', ($) => $('h1'));
35
- */
36
- public async selectFromWeb(url: string, selector: ($: cheerio.CheerioAPI) => cheerio.Cheerio<any>): Promise<string[]> {
37
- try {
38
- const $ = await this.fetch(url);
39
- const results: string[] = [];
40
- const selection = selector($);
41
-
42
- if (typeof selection === 'string') return [selection];
43
-
44
- selection.each((_, element) => {
45
- const text = $(element).text().trim();
46
- if (text) results.push(text);
47
- });
48
-
49
- return results;
50
- } catch (e) {
51
- if (e instanceof TyrError) throw e;
52
- throw new TyrError(`Could not select content from: ${url}`, e);
53
- }
54
- }
55
- }
56
-
57
- export const WebManagerTests = {
58
- selectFromWeb: {
59
- url: 'https://www.w3schools.com/',
60
- selector: ($: any) => $('title'),
61
- },
62
- };
1
+ import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
2
+ import * as cheerio from 'cheerio';
3
+
4
+ import { Logger } from '../core/Logger.js';
5
+ import { TyrError } from '../core/TyrError.js';
6
+
7
+ /**
8
+ * @class WebManager
9
+ * @description Utilities for accessing APIs and the Internet.
10
+ */
11
+ export class WebManager {
12
+ private logger: Logger;
13
+
14
+ constructor(logger: Logger) {
15
+ this.logger = logger;
16
+ }
17
+
18
+ private async fetch(url: string, config?: AxiosRequestConfig): Promise<cheerio.CheerioAPI> {
19
+ try {
20
+ const response: AxiosResponse<string> = await axios.get(url, config);
21
+ return cheerio.load(response.data);
22
+ } catch (e) {
23
+ throw new TyrError(`Could not fetch URL: ${url}`, e, 'Check your internet connection and that the URL is valid.');
24
+ }
25
+ }
26
+
27
+ /**
28
+ * @method selectFromWeb
29
+ * @description Selects elements from a web page using a CSS selector.
30
+ * @param {string} url - URL of the page to scrape.
31
+ * @param {Function} selector - CSS selector function.
32
+ * @returns {Promise<string[]>} List of extracted text values.
33
+ * @example
34
+ * const titles = await web.selectFromWeb('https://example.com', ($) => $('h1'));
35
+ */
36
+ public async selectFromWeb(url: string, selector: ($: cheerio.CheerioAPI) => cheerio.Cheerio<any>): Promise<string[]> {
37
+ try {
38
+ const $ = await this.fetch(url);
39
+ const results: string[] = [];
40
+ const selection = selector($);
41
+
42
+ if (typeof selection === 'string') return [selection];
43
+
44
+ selection.each((_, element) => {
45
+ const text = $(element).text().trim();
46
+ if (text) results.push(text);
47
+ });
48
+
49
+ return results;
50
+ } catch (e) {
51
+ if (e instanceof TyrError) throw e;
52
+ throw new TyrError(`Could not select content from: ${url}`, e);
53
+ }
54
+ }
55
+ }
56
+
57
+ export const WebManagerTests = {
58
+ selectFromWeb: {
59
+ url: 'https://www.w3schools.com/',
60
+ selector: ($: any) => $('title'),
61
+ },
62
+ };