@orxataguy/tyr 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +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
+ };
@@ -0,0 +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
+ };