@orxataguy/tyr 1.0.7 → 1.0.9
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.
- package/package.json +2 -1
- package/src/core/Kernel.ts +15 -0
- package/src/core/sys/help.ts +149 -0
- package/src/lib/MongoManager.ts +176 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orxataguy/tyr",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"tyr": "./bin/tyr.js"
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"find-config": "^1.0.0",
|
|
50
50
|
"inquirer": "^13.2.1",
|
|
51
51
|
"js-yaml": "^4.1.1",
|
|
52
|
+
"mongodb": "^7.2.0",
|
|
52
53
|
"mssql": "^12.2.0",
|
|
53
54
|
"tsx": "^4.21.0"
|
|
54
55
|
},
|
package/src/core/Kernel.ts
CHANGED
|
@@ -11,6 +11,7 @@ import rem from './sys/rem';
|
|
|
11
11
|
import doc from './sys/doc';
|
|
12
12
|
import ai from './sys/ai';
|
|
13
13
|
import config from './sys/config';
|
|
14
|
+
import help from './sys/help';
|
|
14
15
|
|
|
15
16
|
import { TyrError } from './TyrError';
|
|
16
17
|
|
|
@@ -126,6 +127,20 @@ export class Kernel {
|
|
|
126
127
|
return;
|
|
127
128
|
}
|
|
128
129
|
|
|
130
|
+
// --help / -h: lista todos los comandos disponibles con su documentación
|
|
131
|
+
if (commandName === '--help' || commandName === '-h') {
|
|
132
|
+
const helpContext = {
|
|
133
|
+
...this.container.get(),
|
|
134
|
+
frameworkRoot: this.frameworkRoot,
|
|
135
|
+
userRoot: this.userRoot,
|
|
136
|
+
run: async () => {},
|
|
137
|
+
task: async <T>(_: string, action: () => Promise<T> | T) => action(),
|
|
138
|
+
fail: (msg: string) => { throw new Error(msg); },
|
|
139
|
+
} as any;
|
|
140
|
+
await help(helpContext)(args.slice(1));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
129
144
|
const runInternal = async (cmd: string, cmdArgs: string[] = []) => {
|
|
130
145
|
await this.handle([cmd, ...cmdArgs]);
|
|
131
146
|
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { TyrContext } from '../Kernel';
|
|
4
|
+
|
|
5
|
+
interface CommandDoc {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
usage: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Extrae el primer bloque JSDoc de un archivo .tyr.ts y lo parsea
|
|
13
|
+
* en descripción y ejemplos de uso.
|
|
14
|
+
*/
|
|
15
|
+
function parseCommandDoc(filePath: string): CommandDoc {
|
|
16
|
+
const fileName = path.basename(filePath, '.tyr.ts');
|
|
17
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
18
|
+
|
|
19
|
+
const match = content.match(/\/\*\*([\s\S]*?)\*\//);
|
|
20
|
+
if (!match) {
|
|
21
|
+
return { name: fileName, description: '', usage: '' };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Limpiar cada línea: eliminar el * inicial y espacios
|
|
25
|
+
const lines = match[1]
|
|
26
|
+
.split('\n')
|
|
27
|
+
.map(line => line.replace(/^\s*\*\s?/, '').trimEnd());
|
|
28
|
+
|
|
29
|
+
// Separar en descripción y bloque "Uso:"
|
|
30
|
+
const usoIndex = lines.findIndex(l => /^uso:/i.test(l.trim()));
|
|
31
|
+
|
|
32
|
+
let description = '';
|
|
33
|
+
let usage = '';
|
|
34
|
+
|
|
35
|
+
if (usoIndex !== -1) {
|
|
36
|
+
description = lines
|
|
37
|
+
.slice(0, usoIndex)
|
|
38
|
+
.filter(l => l.trim() !== '')
|
|
39
|
+
.join('\n')
|
|
40
|
+
.trim();
|
|
41
|
+
|
|
42
|
+
usage = lines
|
|
43
|
+
.slice(usoIndex + 1)
|
|
44
|
+
.filter(l => l.trim() !== '')
|
|
45
|
+
.map(l => l.trim())
|
|
46
|
+
.join('\n')
|
|
47
|
+
.trim();
|
|
48
|
+
} else {
|
|
49
|
+
description = lines.filter(l => l.trim() !== '').join('\n').trim();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return { name: fileName, description, usage };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default function help({ userRoot }: TyrContext) {
|
|
56
|
+
return async (_args: string[]) => {
|
|
57
|
+
const commandsDir = path.join(userRoot, 'commands');
|
|
58
|
+
|
|
59
|
+
// ── ANSI ──────────────────────────────────────────────────────────
|
|
60
|
+
const reset = '\x1b[0m';
|
|
61
|
+
const bold = '\x1b[1m';
|
|
62
|
+
const dim = '\x1b[2m';
|
|
63
|
+
const cyan = '\x1b[36m';
|
|
64
|
+
const green = '\x1b[32m';
|
|
65
|
+
const yellow = '\x1b[33m';
|
|
66
|
+
const gray = '\x1b[90m';
|
|
67
|
+
const white = '\x1b[37m';
|
|
68
|
+
// ──────────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
const separator = `${gray} ${'─'.repeat(50)}${reset}`;
|
|
71
|
+
|
|
72
|
+
console.log('');
|
|
73
|
+
console.log(` ${bold}${cyan}tyr${reset} ${white}Comandos disponibles${reset}`);
|
|
74
|
+
console.log(separator);
|
|
75
|
+
console.log('');
|
|
76
|
+
|
|
77
|
+
// Flags y comandos built-in del framework
|
|
78
|
+
const builtins = [
|
|
79
|
+
{ name: '--help', description: 'Muestra este listado de comandos.', usage: 'tyr --help' },
|
|
80
|
+
{ name: '--version', description: 'Muestra la versión instalada de tyr.', usage: 'tyr --version' },
|
|
81
|
+
{ name: '--config', description: 'Configura tyr por primera vez.', usage: 'tyr --config' },
|
|
82
|
+
{ name: '--update', description: 'Actualiza ~/.tyr desde el repositorio git.', usage: 'tyr --update' },
|
|
83
|
+
{ name: '--upgrade', description: 'Actualiza el paquete npm de tyr.', usage: 'tyr --upgrade' },
|
|
84
|
+
{ name: 'gen', description: 'Genera un nuevo comando a partir de una descripción con IA.', usage: 'tyr gen <nombre> "<descripción>"' },
|
|
85
|
+
{ name: 'doc', description: 'Levanta la documentación del framework en el navegador.', usage: 'tyr doc' },
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
console.log(` ${bold}${yellow}Framework${reset}`);
|
|
89
|
+
console.log('');
|
|
90
|
+
|
|
91
|
+
for (const cmd of builtins) {
|
|
92
|
+
console.log(` ${bold}${green}${cmd.name.padEnd(14)}${reset}${dim}${cmd.description}${reset}`);
|
|
93
|
+
console.log(` ${' '.repeat(14)}${gray}${cmd.usage}${reset}`);
|
|
94
|
+
console.log('');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Comandos de usuario en ~/.tyr/commands/
|
|
98
|
+
if (!fs.existsSync(commandsDir)) {
|
|
99
|
+
console.log(separator);
|
|
100
|
+
console.log(` ${yellow}No se encontró la carpeta de comandos: ${commandsDir}${reset}`);
|
|
101
|
+
console.log('');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const files = fs.readdirSync(commandsDir)
|
|
106
|
+
.filter(f => f.endsWith('.tyr.ts'))
|
|
107
|
+
.sort();
|
|
108
|
+
|
|
109
|
+
if (files.length === 0) {
|
|
110
|
+
console.log(separator);
|
|
111
|
+
console.log(` ${dim}No hay comandos en ${commandsDir}${reset}`);
|
|
112
|
+
console.log('');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
console.log(separator);
|
|
117
|
+
console.log('');
|
|
118
|
+
console.log(` ${bold}${yellow}Comandos de usuario${reset} ${gray}(~/.tyr/commands/)${reset}`);
|
|
119
|
+
console.log('');
|
|
120
|
+
|
|
121
|
+
for (const file of files) {
|
|
122
|
+
const doc = parseCommandDoc(path.join(commandsDir, file));
|
|
123
|
+
|
|
124
|
+
console.log(` ${bold}${green}${doc.name}${reset}`);
|
|
125
|
+
|
|
126
|
+
if (doc.description) {
|
|
127
|
+
for (const line of doc.description.split('\n')) {
|
|
128
|
+
console.log(` ${dim}${line}${reset}`);
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
console.log(` ${gray}Sin descripción${reset}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (doc.usage) {
|
|
135
|
+
console.log('');
|
|
136
|
+
console.log(` ${gray} Uso:${reset}`);
|
|
137
|
+
for (const line of doc.usage.split('\n')) {
|
|
138
|
+
console.log(` ${cyan} ${line}${reset}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
console.log('');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
console.log(separator);
|
|
146
|
+
console.log(` ${dim}Genera un comando nuevo con ${cyan}tyr gen <nombre> "<qué debe hacer>"${reset}`);
|
|
147
|
+
console.log('');
|
|
148
|
+
};
|
|
149
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { MongoClient, Db, Document, Filter, UpdateFilter, InsertOneResult, InsertManyResult, UpdateResult, DeleteResult, WithId, OptionalUnlessRequiredId, FindOptions } from 'mongodb';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @class MongoManager
|
|
5
|
+
* @description Conector con MongoDB que gestiona el ciclo de vida de la conexión y expone un CRUD genérico.
|
|
6
|
+
*/
|
|
7
|
+
export class MongoManager {
|
|
8
|
+
private client!: MongoClient;
|
|
9
|
+
private db!: Db;
|
|
10
|
+
private connected = false;
|
|
11
|
+
|
|
12
|
+
constructor() {}
|
|
13
|
+
|
|
14
|
+
private async init(): Promise<void> {
|
|
15
|
+
if (!this.connected) {
|
|
16
|
+
const uri = process.env.MONGO_URI || 'mongodb://localhost:27017';
|
|
17
|
+
const dbName = process.env.MONGO_DATABASE || '';
|
|
18
|
+
|
|
19
|
+
this.client = new MongoClient(uri);
|
|
20
|
+
await this.client.connect();
|
|
21
|
+
this.db = this.client.db(dbName);
|
|
22
|
+
this.connected = true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private async close(): Promise<void> {
|
|
27
|
+
if (this.connected && this.client) {
|
|
28
|
+
await this.client.close();
|
|
29
|
+
this.connected = false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @method insertOne
|
|
35
|
+
* @description Inserta un documento en la colección indicada.
|
|
36
|
+
* @param {string} collection - Nombre de la colección.
|
|
37
|
+
* @param {Document} document - Documento a insertar.
|
|
38
|
+
* @returns {Promise<InsertOneResult>} Resultado de la inserción.
|
|
39
|
+
* @example
|
|
40
|
+
* const result = await mongo.insertOne('users', { name: 'Ana', age: 30 });
|
|
41
|
+
*/
|
|
42
|
+
public async insertOne<T extends Document>(collection: string, document: OptionalUnlessRequiredId<T>): Promise<InsertOneResult<T>> {
|
|
43
|
+
await this.init();
|
|
44
|
+
const result = await this.db.collection<T>(collection).insertOne(document);
|
|
45
|
+
await this.close();
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @method insertMany
|
|
51
|
+
* @description Inserta múltiples documentos en la colección indicada.
|
|
52
|
+
* @param {string} collection - Nombre de la colección.
|
|
53
|
+
* @param {Document[]} documents - Array de documentos a insertar.
|
|
54
|
+
* @returns {Promise<InsertManyResult>} Resultado de la inserción.
|
|
55
|
+
* @example
|
|
56
|
+
* const result = await mongo.insertMany('users', [{ name: 'Ana' }, { name: 'Luis' }]);
|
|
57
|
+
*/
|
|
58
|
+
public async insertMany<T extends Document>(collection: string, documents: OptionalUnlessRequiredId<T>[]): Promise<InsertManyResult<T>> {
|
|
59
|
+
await this.init();
|
|
60
|
+
const result = await this.db.collection<T>(collection).insertMany(documents);
|
|
61
|
+
await this.close();
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @method findOne
|
|
67
|
+
* @description Busca el primer documento que coincida con el filtro.
|
|
68
|
+
* @param {string} collection - Nombre de la colección.
|
|
69
|
+
* @param {Filter<Document>} filter - Filtro de búsqueda.
|
|
70
|
+
* @param {FindOptions} [options] - Opciones adicionales (proyección, etc.).
|
|
71
|
+
* @returns {Promise<WithId<T> | null>} El documento encontrado o null.
|
|
72
|
+
* @example
|
|
73
|
+
* const user = await mongo.findOne('users', { name: 'Ana' });
|
|
74
|
+
*/
|
|
75
|
+
public async findOne<T extends Document>(collection: string, filter: Filter<T>, options?: FindOptions): Promise<WithId<T> | null> {
|
|
76
|
+
await this.init();
|
|
77
|
+
const result = await this.db.collection<T>(collection).findOne(filter, options);
|
|
78
|
+
await this.close();
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @method find
|
|
84
|
+
* @description Busca todos los documentos que coincidan con el filtro.
|
|
85
|
+
* @param {string} collection - Nombre de la colección.
|
|
86
|
+
* @param {Filter<Document>} filter - Filtro de búsqueda. Usa {} para traer todos los documentos.
|
|
87
|
+
* @param {FindOptions} [options] - Opciones adicionales (proyección, límite, etc.).
|
|
88
|
+
* @returns {Promise<WithId<T>[]>} Array de documentos encontrados.
|
|
89
|
+
* @example
|
|
90
|
+
* const users = await mongo.find('users', { age: { $gte: 18 } });
|
|
91
|
+
*/
|
|
92
|
+
public async find<T extends Document>(collection: string, filter: Filter<T>, options?: FindOptions): Promise<WithId<T>[]> {
|
|
93
|
+
await this.init();
|
|
94
|
+
const result = await this.db.collection<T>(collection).find(filter, options).toArray();
|
|
95
|
+
await this.close();
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @method updateOne
|
|
101
|
+
* @description Actualiza el primer documento que coincida con el filtro.
|
|
102
|
+
* @param {string} collection - Nombre de la colección.
|
|
103
|
+
* @param {Filter<Document>} filter - Filtro para identificar el documento.
|
|
104
|
+
* @param {UpdateFilter<Document>} update - Operación de actualización (ej: { $set: { field: value } }).
|
|
105
|
+
* @returns {Promise<UpdateResult>} Resultado de la actualización.
|
|
106
|
+
* @example
|
|
107
|
+
* const result = await mongo.updateOne('users', { name: 'Ana' }, { $set: { age: 31 } });
|
|
108
|
+
*/
|
|
109
|
+
public async updateOne<T extends Document>(collection: string, filter: Filter<T>, update: UpdateFilter<T>): Promise<UpdateResult<T>> {
|
|
110
|
+
await this.init();
|
|
111
|
+
const result = await this.db.collection<T>(collection).updateOne(filter, update);
|
|
112
|
+
await this.close();
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @method updateMany
|
|
118
|
+
* @description Actualiza todos los documentos que coincidan con el filtro.
|
|
119
|
+
* @param {string} collection - Nombre de la colección.
|
|
120
|
+
* @param {Filter<Document>} filter - Filtro para identificar los documentos.
|
|
121
|
+
* @param {UpdateFilter<Document>} update - Operación de actualización.
|
|
122
|
+
* @returns {Promise<UpdateResult>} Resultado de la actualización.
|
|
123
|
+
* @example
|
|
124
|
+
* const result = await mongo.updateMany('users', { active: false }, { $set: { active: true } });
|
|
125
|
+
*/
|
|
126
|
+
public async updateMany<T extends Document>(collection: string, filter: Filter<T>, update: UpdateFilter<T>): Promise<UpdateResult<T>> {
|
|
127
|
+
await this.init();
|
|
128
|
+
const result = await this.db.collection<T>(collection).updateMany(filter, update);
|
|
129
|
+
await this.close();
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @method deleteOne
|
|
135
|
+
* @description Elimina el primer documento que coincida con el filtro.
|
|
136
|
+
* @param {string} collection - Nombre de la colección.
|
|
137
|
+
* @param {Filter<Document>} filter - Filtro para identificar el documento.
|
|
138
|
+
* @returns {Promise<DeleteResult>} Resultado de la eliminación.
|
|
139
|
+
* @example
|
|
140
|
+
* const result = await mongo.deleteOne('users', { name: 'Ana' });
|
|
141
|
+
*/
|
|
142
|
+
public async deleteOne<T extends Document>(collection: string, filter: Filter<T>): Promise<DeleteResult> {
|
|
143
|
+
await this.init();
|
|
144
|
+
const result = await this.db.collection<T>(collection).deleteOne(filter);
|
|
145
|
+
await this.close();
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @method deleteMany
|
|
151
|
+
* @description Elimina todos los documentos que coincidan con el filtro.
|
|
152
|
+
* @param {string} collection - Nombre de la colección.
|
|
153
|
+
* @param {Filter<Document>} filter - Filtro para identificar los documentos.
|
|
154
|
+
* @returns {Promise<DeleteResult>} Resultado de la eliminación.
|
|
155
|
+
* @example
|
|
156
|
+
* const result = await mongo.deleteMany('users', { active: false });
|
|
157
|
+
*/
|
|
158
|
+
public async deleteMany<T extends Document>(collection: string, filter: Filter<T>): Promise<DeleteResult> {
|
|
159
|
+
await this.init();
|
|
160
|
+
const result = await this.db.collection<T>(collection).deleteMany(filter);
|
|
161
|
+
await this.close();
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* @object MongoManagerTests
|
|
168
|
+
* @description Parámetros de pruebas para validar la funcionalidad de MongoManager.
|
|
169
|
+
*/
|
|
170
|
+
export const MongoManagerTests = {
|
|
171
|
+
// insertOne: { collection: 'test', document: { name: 'test_doc', value: 1 } },
|
|
172
|
+
// findOne: { collection: 'test', filter: { name: 'test_doc' } },
|
|
173
|
+
// find: { collection: 'test', filter: {} },
|
|
174
|
+
// updateOne: { collection: 'test', filter: { name: 'test_doc' }, update: { $set: { value: 2 } } },
|
|
175
|
+
// deleteOne: { collection: 'test', filter: { name: 'test_doc' } },
|
|
176
|
+
};
|