@getparallel/parx 0.4.4 → 0.6.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.
- package/CHANGELOG.md +16 -0
- package/README.md +21 -6
- package/dist/actualizar.js +2 -1
- package/dist/agente.js +82 -2
- package/dist/api.js +32 -0
- package/dist/cli.js +385 -36
- package/dist/comandos.js +71 -0
- package/dist/config.js +4 -0
- package/dist/consejos.js +2 -1
- package/dist/estimacion.js +118 -0
- package/dist/firmas.js +55 -0
- package/dist/marca.js +56 -19
- package/dist/menciones.js +85 -0
- package/dist/par.js +355 -0
- package/dist/remoto.js +284 -0
- package/dist/tar.js +99 -0
- package/dist/textos.js +95 -6
- package/dist/tui.js +132 -15
- package/dist/ui.js +35 -2
- package/dist/versiones.js +341 -0
- package/package.json +1 -1
package/dist/config.js
CHANGED
|
@@ -96,6 +96,10 @@ export function leerAjustes(dir = carpetaDeConfiguracion()) {
|
|
|
96
96
|
out.raton = a.raton;
|
|
97
97
|
if (typeof a.animacion === 'boolean')
|
|
98
98
|
out.animacion = a.animacion;
|
|
99
|
+
if (typeof a.autoactualizar === 'boolean')
|
|
100
|
+
out.autoactualizar = a.autoactualizar;
|
|
101
|
+
if (typeof a.remoto === 'boolean')
|
|
102
|
+
out.remoto = a.remoto;
|
|
99
103
|
const k = a.consejos;
|
|
100
104
|
if (k && typeof k === 'object') {
|
|
101
105
|
const n = (x) => (typeof x === 'number' && Number.isFinite(x) && x >= 0 ? Math.floor(x) : 0);
|
package/dist/consejos.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { capacidades } from "./marca.js";
|
|
2
2
|
import { t } from "./textos.js";
|
|
3
|
-
export const CONSEJOS = ['menu', 'modo', 'una', 'bash', 'imagen', 'historial', 'pegar', 'deshacer', 'modelo', 'carriles', 'parar', 'cola', 'bandeja', 'raton', 'informe', 'mision', 'ensenar', 'saldo', 'cuenta', 'agents'];
|
|
3
|
+
export const CONSEJOS = ['menu', 'modo', 'una', 'bash', 'imagen', 'historial', 'pegar', 'deshacer', 'modelo', 'carriles', 'parar', 'cola', 'bandeja', 'raton', 'informe', 'mision', 'ensenar', 'saldo', 'cuenta', 'agents', 'arroba'];
|
|
4
4
|
export function consejo(id) {
|
|
5
5
|
const u = capacidades().unicode;
|
|
6
6
|
const tecla = {
|
|
@@ -24,6 +24,7 @@ export function consejo(id) {
|
|
|
24
24
|
bash: '!',
|
|
25
25
|
bandeja: '/inbox',
|
|
26
26
|
raton: 'Click',
|
|
27
|
+
arroba: '@',
|
|
27
28
|
};
|
|
28
29
|
return { tecla: tecla[id], texto: t(`tip.${id}`) };
|
|
29
30
|
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { carpetaDeConfiguracion } from "./config.js";
|
|
4
|
+
import { MODELOS, ORDEN_DE_MODELOS, nombreDeModelo } from "./modelos.js";
|
|
5
|
+
import { t } from "./textos.js";
|
|
6
|
+
export const PERFILES = {
|
|
7
|
+
pregunta: { vueltas: 2, crecimiento: 700, salidaPorVuelta: 80, salidaFinal: 350 },
|
|
8
|
+
pequena: { vueltas: 4, crecimiento: 900, salidaPorVuelta: 350, salidaFinal: 200 },
|
|
9
|
+
media: { vueltas: 8, crecimiento: 1200, salidaPorVuelta: 550, salidaFinal: 250 },
|
|
10
|
+
grande: { vueltas: 16, crecimiento: 1500, salidaPorVuelta: 650, salidaFinal: 350 },
|
|
11
|
+
};
|
|
12
|
+
const GRANDE = /\b(refactor|refactoriz|migrat|migra[r]?\b|rewrite|reescrib|rework|whole (project|app|codebase|repo)|entire|todo el (proyecto|código|repo)|toda la (app|aplicación)|all (the )?files|todos los ficheros|architecture|arquitectura|from scratch|desde cero|full[- ]stack|authentication|autenticaci|database|base de datos|deploy|despliega|integrat|integra[r]?\b|test suite|end[- ]to[- ]end|e2e|multi[- ]?page|varias páginas|dashboard|backend|api rest|microservic)/i;
|
|
13
|
+
const CREA = /\b(create|build|make|write|generate|implement|crea[r]?|construye|monta|escribe|genera|implementa|haz(me)?|new|nuev[oa])\b/i;
|
|
14
|
+
const SUSTANCIAL = /\b(page|página|pagina|website|web ?site|site|web|app|form|formulario|component|componente|feature|funcionalidad|endpoint|screen|pantalla|module|módulo|modulo|api|script|server|servidor|service|servicio|table|tabla|model|modelo|view|vista)\b/i;
|
|
15
|
+
const PEQUENA = /\b(typo|errata|rename|renombra|one[- ]line|una línea|small|pequeñ|minor|mínim|quick|rápid|simple|sencill|tiny|just|solo|sólo|only|fix the|arregla el|arregla la|change the|cambia el|cambia la|add a |añade un|añade una|remove the|quita el|quita la|update the|actualiza el)\b/i;
|
|
16
|
+
const PREGUNTA = /^(what|why|how|where|which|who|when|is|are|does|do|can|could|should|explain|describe|tell me|show me|list|qué|que|por qué|cómo|como|dónde|donde|cuál|cual|quién|quien|cuándo|explica|describe|dime|enséñame|lista|hay)\b/i;
|
|
17
|
+
function palabras(s) {
|
|
18
|
+
return s.trim().split(/\s+/).filter(Boolean).length;
|
|
19
|
+
}
|
|
20
|
+
export function clasificarTarea(pedido) {
|
|
21
|
+
const s = pedido.replace(/\s+/g, ' ').trim();
|
|
22
|
+
const n = palabras(s);
|
|
23
|
+
if (!s)
|
|
24
|
+
return 'pequena';
|
|
25
|
+
if (GRANDE.test(s) || n > 90)
|
|
26
|
+
return 'grande';
|
|
27
|
+
const pregunta = PREGUNTA.test(s) || (s.endsWith('?') && !CREA.test(s));
|
|
28
|
+
if (pregunta && !CREA.test(s))
|
|
29
|
+
return 'pregunta';
|
|
30
|
+
const creaAlgo = CREA.test(s) || SUSTANCIAL.test(s);
|
|
31
|
+
if (n <= 10 && !creaAlgo)
|
|
32
|
+
return 'pequena';
|
|
33
|
+
if (PEQUENA.test(s) && !creaAlgo)
|
|
34
|
+
return 'pequena';
|
|
35
|
+
return 'media';
|
|
36
|
+
}
|
|
37
|
+
export function tokensDe(texto) {
|
|
38
|
+
const chars = typeof texto === 'number' ? texto : texto.length;
|
|
39
|
+
return Math.ceil(chars / 3.8);
|
|
40
|
+
}
|
|
41
|
+
export function accionesEnBruto(clase, baseTokens, tarifa) {
|
|
42
|
+
const p = PERFILES[clase];
|
|
43
|
+
let entrada = 0;
|
|
44
|
+
let salida = 0;
|
|
45
|
+
for (let i = 0; i < p.vueltas; i++) {
|
|
46
|
+
entrada += baseTokens + i * p.crecimiento;
|
|
47
|
+
salida += i === p.vueltas - 1 ? p.salidaFinal : p.salidaPorVuelta;
|
|
48
|
+
}
|
|
49
|
+
return (entrada * tarifa.entrada + salida * tarifa.salida) / 1000;
|
|
50
|
+
}
|
|
51
|
+
export function redondear(n) {
|
|
52
|
+
if (n < 1)
|
|
53
|
+
return 1;
|
|
54
|
+
if (n < 10)
|
|
55
|
+
return Math.round(n);
|
|
56
|
+
const magnitud = 10 ** (Math.floor(Math.log10(n)) - 1);
|
|
57
|
+
return Math.round(n / magnitud) * magnitud;
|
|
58
|
+
}
|
|
59
|
+
export function estimarTarea(e) {
|
|
60
|
+
const clase = clasificarTarea(e.pedido);
|
|
61
|
+
const base = tokensDe(e.contextoChars) + tokensDe(e.pedido);
|
|
62
|
+
const acciones = {};
|
|
63
|
+
for (const m of ORDEN_DE_MODELOS) {
|
|
64
|
+
const bruto = accionesEnBruto(clase, base, e.tarifas[MODELOS[m].servidor]);
|
|
65
|
+
acciones[m] = redondear(bruto * factorDeCalibracion(e.calibracion, clase, m));
|
|
66
|
+
}
|
|
67
|
+
return { clase, acciones };
|
|
68
|
+
}
|
|
69
|
+
export function lineaDeEstimacion(est, modelo) {
|
|
70
|
+
const actual = est.acciones[modelo];
|
|
71
|
+
let sugerido = null;
|
|
72
|
+
if (modelo === 'summit' && est.clase !== 'grande')
|
|
73
|
+
sugerido = 'stride';
|
|
74
|
+
if ((modelo === 'summit' || modelo === 'stride') && (est.clase === 'pregunta' || est.clase === 'pequena'))
|
|
75
|
+
sugerido = 'swift';
|
|
76
|
+
if (sugerido && est.acciones[sugerido] <= actual * 0.6 && actual >= 5) {
|
|
77
|
+
return t('estimacion.con', { n: actual.toLocaleString('en-US'), modelo: nombreDeModelo(sugerido).replace(/ \d+(\.\d+)?$/, ''), otro: est.acciones[sugerido].toLocaleString('en-US') });
|
|
78
|
+
}
|
|
79
|
+
return t('estimacion.linea', { n: actual.toLocaleString('en-US') });
|
|
80
|
+
}
|
|
81
|
+
const FICHERO = 'estimaciones.json';
|
|
82
|
+
const PESO = 0.3;
|
|
83
|
+
export function leerCalibracion(dir = carpetaDeConfiguracion()) {
|
|
84
|
+
try {
|
|
85
|
+
const c = JSON.parse(readFileSync(join(dir, FICHERO), 'utf8'));
|
|
86
|
+
return c && typeof c === 'object' ? c : {};
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return {};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function guardarCalibracion(c, dir) {
|
|
93
|
+
try {
|
|
94
|
+
if (!existsSync(dir))
|
|
95
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
96
|
+
writeFileSync(join(dir, FICHERO), JSON.stringify(c, null, 2), { mode: 0o600 });
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export function factorDeCalibracion(c, clase, modelo) {
|
|
102
|
+
const f = c?.[clase]?.[modelo]?.factor;
|
|
103
|
+
if (typeof f !== 'number' || !Number.isFinite(f) || f <= 0)
|
|
104
|
+
return 1;
|
|
105
|
+
return Math.min(2, Math.max(0.5, f));
|
|
106
|
+
}
|
|
107
|
+
export function aprenderDeLaTarea(o) {
|
|
108
|
+
const dir = o.dir ?? carpetaDeConfiguracion();
|
|
109
|
+
const c = o.calibracion ?? leerCalibracion(dir);
|
|
110
|
+
if (!(o.estimada > 0) || !(o.real > 0))
|
|
111
|
+
return c;
|
|
112
|
+
const ratio = Math.min(4, Math.max(0.25, o.real / o.estimada));
|
|
113
|
+
const antes = c[o.clase]?.[o.modelo];
|
|
114
|
+
const factor = antes ? antes.factor * (1 - PESO) + ratio * PESO : ratio;
|
|
115
|
+
c[o.clase] = { ...(c[o.clase] ?? {}), [o.modelo]: { factor: Number(factor.toFixed(3)), n: (antes?.n ?? 0) + 1 } };
|
|
116
|
+
guardarCalibracion(c, dir);
|
|
117
|
+
return c;
|
|
118
|
+
}
|
package/dist/firmas.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { createHash, createVerify } from 'node:crypto';
|
|
2
|
+
export const CLAVES_DE_NPM = [
|
|
3
|
+
{
|
|
4
|
+
keyid: 'SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U',
|
|
5
|
+
key: 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEY6Ya7W++7aUPzvMTrezH6Ycx3c+HOKYCcNGybJZSCJq/fd7Qa8uuAKtdIkUQtQiEKERhAmE5lMMJhP8OkDOa2g==',
|
|
6
|
+
},
|
|
7
|
+
];
|
|
8
|
+
const REGISTRO_DE_NPM = 'https://registry.npmjs.org';
|
|
9
|
+
export function keyidDe(spkiBase64) {
|
|
10
|
+
return `SHA256:${createHash('sha256').update(Buffer.from(spkiBase64, 'base64')).digest('base64').replace(/=+$/, '')}`;
|
|
11
|
+
}
|
|
12
|
+
export function clavesDeConfianza(env = process.env) {
|
|
13
|
+
const claves = [...CLAVES_DE_NPM];
|
|
14
|
+
const registro = (env.PARX_REGISTRY ?? '').replace(/\/+$/, '');
|
|
15
|
+
if (registro && registro !== REGISTRO_DE_NPM && env.PARX_CLAVES_DE_FIRMA) {
|
|
16
|
+
for (const k of env.PARX_CLAVES_DE_FIRMA.split(',').map((x) => x.trim()).filter(Boolean))
|
|
17
|
+
claves.push({ keyid: keyidDe(k), key: k });
|
|
18
|
+
}
|
|
19
|
+
return claves;
|
|
20
|
+
}
|
|
21
|
+
export function mensajeFirmado(nombre, version, integrity) {
|
|
22
|
+
return `${nombre}@${version}:${integrity}`;
|
|
23
|
+
}
|
|
24
|
+
export function firmaValida(d, claves = CLAVES_DE_NPM) {
|
|
25
|
+
if (!Array.isArray(d.firmas) || !d.firmas.length)
|
|
26
|
+
return false;
|
|
27
|
+
if (!/^sha512-[A-Za-z0-9+/]+={0,2}$/.test(d.integrity))
|
|
28
|
+
return false;
|
|
29
|
+
const mensaje = mensajeFirmado(d.nombre, d.version, d.integrity);
|
|
30
|
+
for (const f of d.firmas) {
|
|
31
|
+
if (!f || typeof f !== 'object')
|
|
32
|
+
continue;
|
|
33
|
+
const { keyid, sig } = f;
|
|
34
|
+
if (typeof keyid !== 'string' || typeof sig !== 'string')
|
|
35
|
+
continue;
|
|
36
|
+
const clave = claves.find((k) => k.keyid === keyid);
|
|
37
|
+
if (!clave)
|
|
38
|
+
continue;
|
|
39
|
+
try {
|
|
40
|
+
const v = createVerify('SHA256');
|
|
41
|
+
v.update(mensaje);
|
|
42
|
+
if (v.verify(`-----BEGIN PUBLIC KEY-----\n${clave.key}\n-----END PUBLIC KEY-----`, sig, 'base64'))
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
export function integridadDe(contenido) {
|
|
51
|
+
return `sha512-${createHash('sha512').update(contenido).digest('base64')}`;
|
|
52
|
+
}
|
|
53
|
+
export function integridadCoincide(contenido, integrity) {
|
|
54
|
+
return integridadDe(contenido) === integrity;
|
|
55
|
+
}
|
package/dist/marca.js
CHANGED
|
@@ -125,27 +125,64 @@ export function logo() {
|
|
|
125
125
|
const tonos = [c.marca, c.claro, c.claro, c.violeta];
|
|
126
126
|
return filas.map((f, i) => tonos[i](f));
|
|
127
127
|
}
|
|
128
|
-
export const ANCHO_DE_PAR =
|
|
129
|
-
|
|
130
|
-
|
|
128
|
+
export const ANCHO_DE_PAR = 8;
|
|
129
|
+
function miniPar(gesto) {
|
|
130
|
+
const ojo = gesto === 'error' ? 'R' : 'E';
|
|
131
|
+
const fila1 = ['.', '.', 'B', 'B', '.', 'B', 'B', '.'];
|
|
132
|
+
if (gesto === 'pregunta') {
|
|
133
|
+
fila1.splice(0, 8, '.', '.', ojo, ojo, '.', ojo, ojo, '.');
|
|
134
|
+
}
|
|
135
|
+
else if (gesto !== 'parpadeo') {
|
|
136
|
+
const izquierda = gesto === 1 || gesto === 2;
|
|
137
|
+
const a = izquierda ? 2 : 3;
|
|
138
|
+
fila1[a] = ojo;
|
|
139
|
+
fila1[a + 3] = ojo;
|
|
140
|
+
}
|
|
141
|
+
return [
|
|
142
|
+
['.', '.', '.', 'B', 'B', '.', 'B', 'B'],
|
|
143
|
+
fila1,
|
|
144
|
+
['.', 'B', 'B', '.', 'B', 'B', '.', '.'],
|
|
145
|
+
['B', 'B', '.', 'B', 'B', '.', '.', '.'],
|
|
146
|
+
];
|
|
131
147
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
148
|
+
const RGB_DE_PAR = { B: [139, 112, 255], E: [246, 243, 255], R: [255, 107, 107] };
|
|
149
|
+
const C256_DE_PAR = { B: 141, E: 255, R: 203 };
|
|
150
|
+
function filaDePar(arriba, abajo) {
|
|
151
|
+
let out = '';
|
|
152
|
+
for (let x = 0; x < ANCHO_DE_PAR; x++) {
|
|
153
|
+
const a = arriba[x];
|
|
154
|
+
const b = abajo[x];
|
|
155
|
+
if (!caps.unicode || !caps.color) {
|
|
156
|
+
const hay = a !== '.' || b !== '.';
|
|
157
|
+
const ojo = a === 'E' || a === 'R' || b === 'E' || b === 'R';
|
|
158
|
+
out += ojo ? 'o' : hay ? (caps.unicode ? '█' : '/') : ' ';
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (a === '.' && b === '.') {
|
|
162
|
+
out += ' ';
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const fg = a === '.' ? null : a;
|
|
166
|
+
const bg = b === '.' ? null : b;
|
|
167
|
+
const color = (p, fondo) => caps.color === 3 ? `\x1b[${fondo ? 48 : 38};2;${RGB_DE_PAR[p].join(';')}m` : caps.color === 2 ? `\x1b[${fondo ? 48 : 38};5;${C256_DE_PAR[p]}m` : p === 'B' ? `\x1b[${fondo ? 45 : 35}m` : p === 'R' ? `\x1b[${fondo ? 41 : 31}m` : `\x1b[${fondo ? 47 : 97}m`;
|
|
168
|
+
if (fg && bg && fg === bg)
|
|
169
|
+
out += `${color(fg, false)}█\x1b[39m`;
|
|
170
|
+
else if (fg && bg)
|
|
171
|
+
out += `${color(fg, false)}${color(bg, true)}▀\x1b[39;49m`;
|
|
172
|
+
else if (fg)
|
|
173
|
+
out += `${color(fg, false)}▀\x1b[39m`;
|
|
174
|
+
else
|
|
175
|
+
out += `${color(bg, false)}▄\x1b[39m`;
|
|
147
176
|
}
|
|
148
|
-
return
|
|
177
|
+
return out;
|
|
178
|
+
}
|
|
179
|
+
export function cabezaDePar(gesto = 'quieto') {
|
|
180
|
+
const p = miniPar(gesto);
|
|
181
|
+
return filaDePar(p[0], p[1]);
|
|
182
|
+
}
|
|
183
|
+
export function caraDePar(gesto = 'quieto') {
|
|
184
|
+
const p = miniPar(gesto);
|
|
185
|
+
return filaDePar(p[2], p[3]);
|
|
149
186
|
}
|
|
150
187
|
export const ANCHO_DE_ANIMACION = 8;
|
|
151
188
|
export function animacion(tipo, paso) {
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export const LARGO_MAXIMO_CONSULTA = 30;
|
|
2
|
+
export function normalizar(texto) {
|
|
3
|
+
return texto.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase();
|
|
4
|
+
}
|
|
5
|
+
export function detectarArroba(texto, cursor) {
|
|
6
|
+
if (cursor < 1 || cursor > texto.length)
|
|
7
|
+
return null;
|
|
8
|
+
const antes = texto.slice(0, cursor);
|
|
9
|
+
const arroba = antes.lastIndexOf('@');
|
|
10
|
+
if (arroba < 0)
|
|
11
|
+
return null;
|
|
12
|
+
const previo = arroba === 0 ? '' : antes[arroba - 1];
|
|
13
|
+
if (previo !== '' && !/\s/.test(previo))
|
|
14
|
+
return null;
|
|
15
|
+
const consulta = antes.slice(arroba + 1);
|
|
16
|
+
if (consulta.length > LARGO_MAXIMO_CONSULTA)
|
|
17
|
+
return null;
|
|
18
|
+
if (/\n|\s\s|^\s/.test(consulta))
|
|
19
|
+
return null;
|
|
20
|
+
return { inicio: arroba, consulta };
|
|
21
|
+
}
|
|
22
|
+
export function mencionTerminada(consulta, conexiones) {
|
|
23
|
+
if (!/\s$/.test(consulta))
|
|
24
|
+
return false;
|
|
25
|
+
const q = normalizar(consulta.trim());
|
|
26
|
+
return conexiones.some((c) => normalizar(c.nombre) === q || normalizar(c.provider) === q);
|
|
27
|
+
}
|
|
28
|
+
export function filtrarConexiones(conexiones, consulta) {
|
|
29
|
+
return puntuar(conexiones, consulta)
|
|
30
|
+
.sort((a, b) => a.p - b.p || a.c.nombre.localeCompare(b.c.nombre))
|
|
31
|
+
.map((x) => x.c);
|
|
32
|
+
}
|
|
33
|
+
function puntuar(conexiones, consulta) {
|
|
34
|
+
const q = normalizar(consulta.trim());
|
|
35
|
+
const puntuados = [];
|
|
36
|
+
for (const c of conexiones) {
|
|
37
|
+
const nombre = normalizar(c.nombre);
|
|
38
|
+
const clave = normalizar(c.provider);
|
|
39
|
+
let p = -1;
|
|
40
|
+
if (!q || nombre.startsWith(q) || clave.startsWith(q))
|
|
41
|
+
p = 0;
|
|
42
|
+
else if (nombre.split(/[\s._-]+/).some((w) => w.startsWith(q)) || clave.split('_').some((w) => w.startsWith(q)))
|
|
43
|
+
p = 1;
|
|
44
|
+
else if (nombre.includes(q) || clave.includes(q))
|
|
45
|
+
p = 2;
|
|
46
|
+
if (p >= 0)
|
|
47
|
+
puntuados.push({ c, p });
|
|
48
|
+
}
|
|
49
|
+
return puntuados;
|
|
50
|
+
}
|
|
51
|
+
export function conexionesQueSalen(conexiones, consulta, max = 40) {
|
|
52
|
+
return puntuar(conexiones, consulta)
|
|
53
|
+
.sort((a, b) => a.p - b.p || Number(b.c.conectada) - Number(a.c.conectada) || a.c.nombre.localeCompare(b.c.nombre))
|
|
54
|
+
.slice(0, max)
|
|
55
|
+
.map((x) => x.c);
|
|
56
|
+
}
|
|
57
|
+
export function mencionEnLaCaja(unidades, cursor) {
|
|
58
|
+
if (unidades[0] === '!')
|
|
59
|
+
return null;
|
|
60
|
+
const antes = unidades.slice(0, Math.max(0, Math.min(cursor, unidades.length)));
|
|
61
|
+
let arroba = -1;
|
|
62
|
+
for (let i = antes.length - 1; i >= 0; i--) {
|
|
63
|
+
const u = antes[i];
|
|
64
|
+
if (u === '@') {
|
|
65
|
+
arroba = i;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
if (u.length !== 1 || antes.length - i > LARGO_MAXIMO_CONSULTA + 1)
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
if (arroba < 0)
|
|
72
|
+
return null;
|
|
73
|
+
const previo = arroba === 0 ? '' : antes[arroba - 1];
|
|
74
|
+
if (previo !== '' && (previo.length !== 1 || !/\s/.test(previo)))
|
|
75
|
+
return null;
|
|
76
|
+
const consulta = antes.slice(arroba + 1).join('');
|
|
77
|
+
return detectarArroba(`@${consulta}`, consulta.length + 1) ? { inicio: arroba, consulta } : null;
|
|
78
|
+
}
|
|
79
|
+
export function insertarMencion(unidades, inicio, cursor, nombre) {
|
|
80
|
+
const despues = unidades.slice(cursor);
|
|
81
|
+
const separador = despues[0] === ' ' ? [] : [' '];
|
|
82
|
+
const etiqueta = [...Array.from(`@${nombre}`), ...separador];
|
|
83
|
+
const nuevas = [...unidades.slice(0, inicio), ...etiqueta, ...despues];
|
|
84
|
+
return { unidades: nuevas, cursor: inicio + etiqueta.length + (separador.length ? 0 : 1) };
|
|
85
|
+
}
|