estela 0.1.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/README.md +138 -0
- package/dist/billing/amortize.d.ts +44 -0
- package/dist/billing/amortize.d.ts.map +1 -0
- package/dist/billing/amortize.js +83 -0
- package/dist/billing/amortize.js.map +1 -0
- package/dist/billing/amortize.test.d.ts +2 -0
- package/dist/billing/amortize.test.d.ts.map +1 -0
- package/dist/billing/amortize.test.js +121 -0
- package/dist/billing/amortize.test.js.map +1 -0
- package/dist/billing/format.test.d.ts +2 -0
- package/dist/billing/format.test.d.ts.map +1 -0
- package/dist/billing/format.test.js +110 -0
- package/dist/billing/format.test.js.map +1 -0
- package/dist/billing/invoice.d.ts +52 -0
- package/dist/billing/invoice.d.ts.map +1 -0
- package/dist/billing/invoice.js +148 -0
- package/dist/billing/invoice.js.map +1 -0
- package/dist/billing/invoice.test.d.ts +2 -0
- package/dist/billing/invoice.test.d.ts.map +1 -0
- package/dist/billing/invoice.test.js +200 -0
- package/dist/billing/invoice.test.js.map +1 -0
- package/dist/billing/sessionize.d.ts +104 -0
- package/dist/billing/sessionize.d.ts.map +1 -0
- package/dist/billing/sessionize.js +0 -0
- package/dist/billing/sessionize.js.map +1 -0
- package/dist/billing/sessionize.test.d.ts +2 -0
- package/dist/billing/sessionize.test.d.ts.map +1 -0
- package/dist/billing/sessionize.test.js +67 -0
- package/dist/billing/sessionize.test.js.map +1 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1091 -0
- package/dist/cli.js.map +1 -0
- package/dist/cloud/auth.d.ts +4 -0
- package/dist/cloud/auth.d.ts.map +1 -0
- package/dist/cloud/auth.js +98 -0
- package/dist/cloud/auth.js.map +1 -0
- package/dist/cloud/client.d.ts +12 -0
- package/dist/cloud/client.d.ts.map +1 -0
- package/dist/cloud/client.js +34 -0
- package/dist/cloud/client.js.map +1 -0
- package/dist/db/schema.d.ts +11 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +424 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/db/schema.test.d.ts +2 -0
- package/dist/db/schema.test.d.ts.map +1 -0
- package/dist/db/schema.test.js +335 -0
- package/dist/db/schema.test.js.map +1 -0
- package/dist/db/store.d.ts +113 -0
- package/dist/db/store.d.ts.map +1 -0
- package/dist/db/store.js +550 -0
- package/dist/db/store.js.map +1 -0
- package/dist/doctor.d.ts +24 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +229 -0
- package/dist/doctor.js.map +1 -0
- package/dist/export/csv.d.ts +9 -0
- package/dist/export/csv.d.ts.map +1 -0
- package/dist/export/csv.js +81 -0
- package/dist/export/csv.js.map +1 -0
- package/dist/export/invoice-pdf.d.ts +21 -0
- package/dist/export/invoice-pdf.d.ts.map +1 -0
- package/dist/export/invoice-pdf.js +135 -0
- package/dist/export/invoice-pdf.js.map +1 -0
- package/dist/export/panel.d.ts +78 -0
- package/dist/export/panel.d.ts.map +1 -0
- package/dist/export/panel.js +693 -0
- package/dist/export/panel.js.map +1 -0
- package/dist/export/panel.test.d.ts +2 -0
- package/dist/export/panel.test.d.ts.map +1 -0
- package/dist/export/panel.test.js +273 -0
- package/dist/export/panel.test.js.map +1 -0
- package/dist/export/pdf.d.ts +32 -0
- package/dist/export/pdf.d.ts.map +1 -0
- package/dist/export/pdf.js +229 -0
- package/dist/export/pdf.js.map +1 -0
- package/dist/export/pdf.test.d.ts +2 -0
- package/dist/export/pdf.test.d.ts.map +1 -0
- package/dist/export/pdf.test.js +151 -0
- package/dist/export/pdf.test.js.map +1 -0
- package/dist/export/share.d.ts +42 -0
- package/dist/export/share.d.ts.map +1 -0
- package/dist/export/share.js +181 -0
- package/dist/export/share.js.map +1 -0
- package/dist/export/share.test.d.ts +2 -0
- package/dist/export/share.test.d.ts.map +1 -0
- package/dist/export/share.test.js +116 -0
- package/dist/export/share.test.js.map +1 -0
- package/dist/metrics/index.d.ts +101 -0
- package/dist/metrics/index.d.ts.map +1 -0
- package/dist/metrics/index.js +221 -0
- package/dist/metrics/index.js.map +1 -0
- package/dist/metrics/metrics.test.d.ts +2 -0
- package/dist/metrics/metrics.test.d.ts.map +1 -0
- package/dist/metrics/metrics.test.js +162 -0
- package/dist/metrics/metrics.test.js.map +1 -0
- package/dist/metrics/people.d.ts +46 -0
- package/dist/metrics/people.d.ts.map +1 -0
- package/dist/metrics/people.js +126 -0
- package/dist/metrics/people.js.map +1 -0
- package/dist/metrics/people.test.d.ts +2 -0
- package/dist/metrics/people.test.d.ts.map +1 -0
- package/dist/metrics/people.test.js +84 -0
- package/dist/metrics/people.test.js.map +1 -0
- package/dist/metrics/team.d.ts +41 -0
- package/dist/metrics/team.d.ts.map +1 -0
- package/dist/metrics/team.js +0 -0
- package/dist/metrics/team.js.map +1 -0
- package/dist/metrics/team.test.d.ts +2 -0
- package/dist/metrics/team.test.d.ts.map +1 -0
- package/dist/metrics/team.test.js +90 -0
- package/dist/metrics/team.test.js.map +1 -0
- package/dist/pricing/catalog.d.ts +39 -0
- package/dist/pricing/catalog.d.ts.map +1 -0
- package/dist/pricing/catalog.js +59 -0
- package/dist/pricing/catalog.js.map +1 -0
- package/dist/pricing/cost.d.ts +25 -0
- package/dist/pricing/cost.d.ts.map +1 -0
- package/dist/pricing/cost.js +50 -0
- package/dist/pricing/cost.js.map +1 -0
- package/dist/pricing/cost.test.d.ts +2 -0
- package/dist/pricing/cost.test.d.ts.map +1 -0
- package/dist/pricing/cost.test.js +74 -0
- package/dist/pricing/cost.test.js.map +1 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +1141 -0
- package/dist/server.js.map +1 -0
- package/dist/setup.d.ts +70 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +223 -0
- package/dist/setup.js.map +1 -0
- package/dist/setup.test.d.ts +2 -0
- package/dist/setup.test.d.ts.map +1 -0
- package/dist/setup.test.js +131 -0
- package/dist/setup.test.js.map +1 -0
- package/dist/watchers/claude.d.ts +34 -0
- package/dist/watchers/claude.d.ts.map +1 -0
- package/dist/watchers/claude.js +235 -0
- package/dist/watchers/claude.js.map +1 -0
- package/dist/watchers/claude.test.d.ts +2 -0
- package/dist/watchers/claude.test.d.ts.map +1 -0
- package/dist/watchers/claude.test.js +180 -0
- package/dist/watchers/claude.test.js.map +1 -0
- package/dist/watchers/git.d.ts +42 -0
- package/dist/watchers/git.d.ts.map +1 -0
- package/dist/watchers/git.js +218 -0
- package/dist/watchers/git.js.map +1 -0
- package/dist/watchers/git.test.d.ts +2 -0
- package/dist/watchers/git.test.d.ts.map +1 -0
- package/dist/watchers/git.test.js +73 -0
- package/dist/watchers/git.test.js.map +1 -0
- package/dist/watchers/identity.d.ts +34 -0
- package/dist/watchers/identity.d.ts.map +1 -0
- package/dist/watchers/identity.js +85 -0
- package/dist/watchers/identity.js.map +1 -0
- package/dist/watchers/identity.test.d.ts +2 -0
- package/dist/watchers/identity.test.d.ts.map +1 -0
- package/dist/watchers/identity.test.js +48 -0
- package/dist/watchers/identity.test.js.map +1 -0
- package/dist/web-ui.test.d.ts +2 -0
- package/dist/web-ui.test.d.ts.map +1 -0
- package/dist/web-ui.test.js +312 -0
- package/dist/web-ui.test.js.map +1 -0
- package/node_modules/@estela/shared/dist/index.d.ts +3 -0
- package/node_modules/@estela/shared/dist/index.d.ts.map +1 -0
- package/node_modules/@estela/shared/dist/index.js +19 -0
- package/node_modules/@estela/shared/dist/index.js.map +1 -0
- package/node_modules/@estela/shared/dist/money.d.ts +71 -0
- package/node_modules/@estela/shared/dist/money.d.ts.map +1 -0
- package/node_modules/@estela/shared/dist/money.js +171 -0
- package/node_modules/@estela/shared/dist/money.js.map +1 -0
- package/node_modules/@estela/shared/dist/types.d.ts +365 -0
- package/node_modules/@estela/shared/dist/types.d.ts.map +1 -0
- package/node_modules/@estela/shared/dist/types.js +24 -0
- package/node_modules/@estela/shared/dist/types.js.map +1 -0
- package/node_modules/@estela/shared/package.json +5 -0
- package/package.json +41 -0
- package/web/app.css +749 -0
- package/web/app.js +1257 -0
- package/web/apple-touch-icon.png +0 -0
- package/web/favicon.png +0 -0
- package/web/index.html +132 -0
- package/web/lib.js +94 -0
- package/web/logo-mark.png +0 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readCommits = readCommits;
|
|
4
|
+
exports.repoRoot = repoRoot;
|
|
5
|
+
exports.mergedBranches = mergedBranches;
|
|
6
|
+
exports.integrationBranch = integrationBranch;
|
|
7
|
+
exports.currentBranch = currentBranch;
|
|
8
|
+
exports.repoAuthors = repoAuthors;
|
|
9
|
+
exports.gitUserEmail = gitUserEmail;
|
|
10
|
+
const node_child_process_1 = require("node:child_process");
|
|
11
|
+
const node_util_1 = require("node:util");
|
|
12
|
+
const run = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
13
|
+
/**
|
|
14
|
+
* Lector de git.
|
|
15
|
+
*
|
|
16
|
+
* Solo observa: no instala hooks, no escribe trailers, no toca los mensajes de
|
|
17
|
+
* commit. `core.hooksPath` es global y pisaría husky o lefthook del equipo, y un
|
|
18
|
+
* identificador inyectado en el historial es irreversible una vez subido. Leer
|
|
19
|
+
* `git log` no tiene ninguno de esos costes.
|
|
20
|
+
*/
|
|
21
|
+
const SEP = "\x1f";
|
|
22
|
+
const RECORD = "\x1e";
|
|
23
|
+
async function readCommits(repoPath, options = {}) {
|
|
24
|
+
const root = await repoRoot(repoPath);
|
|
25
|
+
if (!root)
|
|
26
|
+
return [];
|
|
27
|
+
// %D son las referencias que APUNTAN al commit; no dicen a qué rama pertenece.
|
|
28
|
+
// Para eso se usa --source, que hace que git diga por cuál de las referencias
|
|
29
|
+
// recorridas llegó a cada commit. Sin esto, `branch` guardaba la rama actual
|
|
30
|
+
// del repositorio para todos los commits y cualquier métrica por feature
|
|
31
|
+
// salía mal.
|
|
32
|
+
// El separador va DELANTE de la cabecera, no detrás. Con `--numstat` git
|
|
33
|
+
// imprime la cabecera y a continuación las líneas de estadísticas: si el
|
|
34
|
+
// separador cierra la cabecera, esas líneas quedan pegadas al principio del
|
|
35
|
+
// commit siguiente y todos los recuentos salen a cero.
|
|
36
|
+
// El separador de registro va SIEMPRE el primero. Si se cuela un campo antes,
|
|
37
|
+
// ese campo queda pegado al final del commit anterior al partir por él.
|
|
38
|
+
const format = RECORD + ["%S", "%H", "%aI", "%ae", "%an", "%s"].join(SEP);
|
|
39
|
+
const args = ["log", "--source", "--all", `--pretty=format:${format}`,
|
|
40
|
+
"--numstat", "--no-merges"];
|
|
41
|
+
if (options.since)
|
|
42
|
+
args.push(`--since=${options.since.toISOString()}`);
|
|
43
|
+
// Varios --author en git se combinan con OR, que es justo lo que hace falta:
|
|
44
|
+
// en el repo de un cliente commiteas con el correo que te dieron, y en los
|
|
45
|
+
// tuyos con el propio. Filtrar por uno solo pierde casi todo tu trabajo.
|
|
46
|
+
const authors = options.authorEmails?.length
|
|
47
|
+
? options.authorEmails
|
|
48
|
+
: options.authorEmail ? [options.authorEmail] : [];
|
|
49
|
+
// Sin ignorar mayúsculas, configurar un nombre ("JDev Leon") en vez de un
|
|
50
|
+
// correo no casaría con nada y el proyecto se quedaría sin commits.
|
|
51
|
+
if (authors.length)
|
|
52
|
+
args.push("--regexp-ignore-case");
|
|
53
|
+
for (const email of authors)
|
|
54
|
+
args.push(`--author=${email}`);
|
|
55
|
+
let stdout;
|
|
56
|
+
try {
|
|
57
|
+
({ stdout } = await run("git", args, { cwd: root, maxBuffer: 64 * 1024 * 1024 }));
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Un repo sin commits, o sin git instalado, no debe tumbar el escaneo.
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
const commits = [];
|
|
64
|
+
for (const chunk of stdout.split(RECORD)) {
|
|
65
|
+
const trimmed = chunk.trim();
|
|
66
|
+
if (!trimmed)
|
|
67
|
+
continue;
|
|
68
|
+
const newlineAt = trimmed.indexOf("\n");
|
|
69
|
+
const header = newlineAt === -1 ? trimmed : trimmed.slice(0, newlineAt);
|
|
70
|
+
const body = newlineAt === -1 ? "" : trimmed.slice(newlineAt + 1);
|
|
71
|
+
const [source, hash, iso, authorEmail, authorName, subject] = header.split(SEP);
|
|
72
|
+
if (!hash || !iso || !authorEmail)
|
|
73
|
+
continue;
|
|
74
|
+
// --source devuelve "refs/heads/feature/x"; nos quedamos con el nombre.
|
|
75
|
+
const branch = source
|
|
76
|
+
? source.replace(/^refs\/(heads|remotes\/[^/]+)\//, "").trim() || null
|
|
77
|
+
: null;
|
|
78
|
+
const at = new Date(iso);
|
|
79
|
+
if (Number.isNaN(at.getTime()))
|
|
80
|
+
continue;
|
|
81
|
+
let linesAdded = 0;
|
|
82
|
+
let linesDeleted = 0;
|
|
83
|
+
const files = [];
|
|
84
|
+
for (const line of body.split("\n")) {
|
|
85
|
+
const [added, deleted, file] = line.split("\t");
|
|
86
|
+
// "-" en --numstat significa archivo binario: no cuenta como líneas.
|
|
87
|
+
if (added && added !== "-")
|
|
88
|
+
linesAdded += Number(added) || 0;
|
|
89
|
+
if (deleted && deleted !== "-")
|
|
90
|
+
linesDeleted += Number(deleted) || 0;
|
|
91
|
+
// Los ficheros habilitan medir reescritura: sin ellos no se puede saber
|
|
92
|
+
// qué se está tocando una y otra vez.
|
|
93
|
+
if (file)
|
|
94
|
+
files.push(file.trim());
|
|
95
|
+
}
|
|
96
|
+
commits.push({
|
|
97
|
+
repoPath: root, hash, at, authorEmail, authorName: authorName ?? "",
|
|
98
|
+
branch, subject: subject ?? "", linesAdded, linesDeleted, files,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return commits;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Raíz del repositorio.
|
|
105
|
+
*
|
|
106
|
+
* `git rev-parse --show-toplevel` en vez de comprobar si existe un directorio
|
|
107
|
+
* `.git`: en un `git worktree` el `.git` es un *archivo*, no un directorio, y la
|
|
108
|
+
* comprobación ingenua devuelve falso en un repo perfectamente válido.
|
|
109
|
+
*/
|
|
110
|
+
async function repoRoot(path) {
|
|
111
|
+
try {
|
|
112
|
+
const { stdout } = await run("git", ["rev-parse", "--show-toplevel"], { cwd: path });
|
|
113
|
+
return stdout.trim() || null;
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Ramas ya integradas en la rama de integración.
|
|
121
|
+
*
|
|
122
|
+
* Lo que queda fuera es trabajo abierto, que es la señal de riesgo que un jefe
|
|
123
|
+
* de proyecto no tiene hoy: no la ve hasta que estalla al integrar.
|
|
124
|
+
*
|
|
125
|
+
* La rama base se detecta porque varía por equipo —unos integran en `main`,
|
|
126
|
+
* otros en `develop`— y equivocarse la marcaría todo como abierto.
|
|
127
|
+
*/
|
|
128
|
+
async function mergedBranches(path) {
|
|
129
|
+
const root = await repoRoot(path);
|
|
130
|
+
if (!root)
|
|
131
|
+
return new Set();
|
|
132
|
+
const base = await integrationBranch(root);
|
|
133
|
+
if (!base)
|
|
134
|
+
return new Set();
|
|
135
|
+
const merged = new Set([base]);
|
|
136
|
+
for (const ref of ["--merged"]) {
|
|
137
|
+
try {
|
|
138
|
+
const { stdout } = await run("git", ["branch", "-a", ref, base], { cwd: root });
|
|
139
|
+
for (const line of stdout.split("\n")) {
|
|
140
|
+
const name = line.replace(/^[*+]?\s*/, "").trim();
|
|
141
|
+
if (!name || name.includes("->"))
|
|
142
|
+
continue;
|
|
143
|
+
merged.add(name.replace(/^remotes\/[^/]+\//, ""));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
// Un repo recién creado no tiene aún la rama base.
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return merged;
|
|
151
|
+
}
|
|
152
|
+
/** Rama donde el equipo integra. `develop` gana a `main` si existe. */
|
|
153
|
+
async function integrationBranch(root) {
|
|
154
|
+
for (const candidate of ["develop", "main", "master"]) {
|
|
155
|
+
try {
|
|
156
|
+
await run("git", ["rev-parse", "--verify", `refs/heads/${candidate}`], { cwd: root });
|
|
157
|
+
return candidate;
|
|
158
|
+
}
|
|
159
|
+
catch { /* probar la siguiente */ }
|
|
160
|
+
}
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
/** Rama activa del repositorio. Ya no se usa para etiquetar commits. */
|
|
164
|
+
async function currentBranch(root) {
|
|
165
|
+
try {
|
|
166
|
+
const { stdout } = await run("git", ["branch", "--show-current"], { cwd: root });
|
|
167
|
+
return stdout.trim() || null;
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Autores que han commiteado en un repositorio, de más a menos reciente.
|
|
175
|
+
*
|
|
176
|
+
* Sirve para preguntar "¿cuál de estos eres tú?" en vez de adivinarlo: en el
|
|
177
|
+
* repositorio de un cliente hay cinco compañeros y tu correo global no aparece
|
|
178
|
+
* por ningún lado.
|
|
179
|
+
*/
|
|
180
|
+
async function repoAuthors(path, limit = 12) {
|
|
181
|
+
const root = await repoRoot(path);
|
|
182
|
+
if (!root)
|
|
183
|
+
return [];
|
|
184
|
+
try {
|
|
185
|
+
const { stdout } = await run("git", ["log", "--pretty=format:%ae\t%aI", "--no-merges"], { cwd: root, maxBuffer: 32 * 1024 * 1024 });
|
|
186
|
+
const seen = new Map();
|
|
187
|
+
for (const line of stdout.split("\n")) {
|
|
188
|
+
const [email, at] = line.split("\t");
|
|
189
|
+
if (!email)
|
|
190
|
+
continue;
|
|
191
|
+
const entry = seen.get(email);
|
|
192
|
+
if (entry) {
|
|
193
|
+
entry.commits++;
|
|
194
|
+
if (at && at > entry.lastAt)
|
|
195
|
+
entry.lastAt = at;
|
|
196
|
+
}
|
|
197
|
+
else
|
|
198
|
+
seen.set(email, { commits: 1, lastAt: at ?? "" });
|
|
199
|
+
}
|
|
200
|
+
return [...seen.entries()]
|
|
201
|
+
.map(([email, v]) => ({ email, commits: v.commits, lastAt: v.lastAt.slice(0, 10) }))
|
|
202
|
+
.sort((a, b) => b.commits - a.commits)
|
|
203
|
+
.slice(0, limit);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
return [];
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async function gitUserEmail(path) {
|
|
210
|
+
try {
|
|
211
|
+
const { stdout } = await run("git", ["config", "user.email"], { cwd: path });
|
|
212
|
+
return stdout.trim() || null;
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/watchers/git.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.test.d.ts","sourceRoot":"","sources":["../../src/watchers/git.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
|
+
const node_child_process_1 = require("node:child_process");
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_os_1 = require("node:os");
|
|
10
|
+
const node_path_1 = require("node:path");
|
|
11
|
+
const node_test_1 = require("node:test");
|
|
12
|
+
const git_js_1 = require("./git.js");
|
|
13
|
+
/** Repo de verdad: lo que falla aquí falla contra git real, no contra un mock. */
|
|
14
|
+
function makeRepo() {
|
|
15
|
+
const dir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "estela-git-"));
|
|
16
|
+
const git = (...args) => (0, node_child_process_1.execFileSync)("git", args, { cwd: dir, stdio: "pipe", env: {
|
|
17
|
+
...process.env,
|
|
18
|
+
GIT_AUTHOR_NAME: "Prueba", GIT_AUTHOR_EMAIL: "prueba@ejemplo.com",
|
|
19
|
+
GIT_COMMITTER_NAME: "Prueba", GIT_COMMITTER_EMAIL: "prueba@ejemplo.com",
|
|
20
|
+
} });
|
|
21
|
+
git("init", "-q");
|
|
22
|
+
git("config", "user.email", "prueba@ejemplo.com");
|
|
23
|
+
git("config", "user.name", "Prueba");
|
|
24
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, "a.txt"), Array.from({ length: 10 }, (_, i) => `linea ${i}`).join("\n") + "\n");
|
|
25
|
+
git("add", "-A");
|
|
26
|
+
git("commit", "-q", "-m", "feat: primer commit con diez líneas");
|
|
27
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, "b.txt"), "una\ndos\ntres\n");
|
|
28
|
+
git("add", "-A");
|
|
29
|
+
git("commit", "-q", "-m", "feat(b): añade tres líneas más");
|
|
30
|
+
return dir;
|
|
31
|
+
}
|
|
32
|
+
(0, node_test_1.test)("cuenta las líneas añadidas y borradas de cada commit", async () => {
|
|
33
|
+
// El fallo que esto previene: con el separador de registro DETRÁS de la
|
|
34
|
+
// cabecera, las líneas de --numstat se pegaban al commit siguiente y todos
|
|
35
|
+
// los recuentos salían +0 −0.
|
|
36
|
+
const dir = makeRepo();
|
|
37
|
+
const commits = await (0, git_js_1.readCommits)(dir);
|
|
38
|
+
strict_1.default.equal(commits.length, 2);
|
|
39
|
+
const total = commits.reduce((s, c) => s + c.linesAdded, 0);
|
|
40
|
+
strict_1.default.equal(total, 13, "10 líneas del primer commit + 3 del segundo");
|
|
41
|
+
const primero = commits.find((c) => c.subject.includes("primer"));
|
|
42
|
+
strict_1.default.ok(primero);
|
|
43
|
+
strict_1.default.equal(primero.linesAdded, 10);
|
|
44
|
+
strict_1.default.equal(primero.linesDeleted, 0);
|
|
45
|
+
});
|
|
46
|
+
(0, node_test_1.test)("lee hash, fecha, autor y asunto", async () => {
|
|
47
|
+
const dir = makeRepo();
|
|
48
|
+
const commits = await (0, git_js_1.readCommits)(dir);
|
|
49
|
+
const c = commits[0];
|
|
50
|
+
strict_1.default.match(c.hash, /^[0-9a-f]{40}$/);
|
|
51
|
+
strict_1.default.equal(c.authorEmail, "prueba@ejemplo.com");
|
|
52
|
+
strict_1.default.ok(c.subject.length > 0);
|
|
53
|
+
strict_1.default.ok(!Number.isNaN(c.at.getTime()));
|
|
54
|
+
});
|
|
55
|
+
(0, node_test_1.test)("filtra por autor", async () => {
|
|
56
|
+
const dir = makeRepo();
|
|
57
|
+
strict_1.default.equal((await (0, git_js_1.readCommits)(dir, { authorEmail: "prueba@ejemplo.com" })).length, 2);
|
|
58
|
+
strict_1.default.equal((await (0, git_js_1.readCommits)(dir, { authorEmail: "otro@ejemplo.com" })).length, 0);
|
|
59
|
+
});
|
|
60
|
+
(0, node_test_1.test)("un directorio que no es repo devuelve vacío en vez de lanzar", async () => {
|
|
61
|
+
const dir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "estela-norepo-"));
|
|
62
|
+
strict_1.default.equal(await (0, git_js_1.repoRoot)(dir), null);
|
|
63
|
+
strict_1.default.deepEqual(await (0, git_js_1.readCommits)(dir), []);
|
|
64
|
+
});
|
|
65
|
+
(0, node_test_1.test)("repoRoot funciona donde la comprobación de un .git como directorio fallaría", async () => {
|
|
66
|
+
// En un `git worktree` el .git es un archivo, no un directorio. Por eso se
|
|
67
|
+
// usa `git rev-parse --show-toplevel` y no `existsSync(join(dir, '.git'))`.
|
|
68
|
+
const dir = makeRepo();
|
|
69
|
+
const root = await (0, git_js_1.repoRoot)(dir);
|
|
70
|
+
strict_1.default.ok(root);
|
|
71
|
+
strict_1.default.ok(root.endsWith(dir.split("/").pop()));
|
|
72
|
+
});
|
|
73
|
+
//# sourceMappingURL=git.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.test.js","sourceRoot":"","sources":["../../src/watchers/git.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import type { CommitRecord } from "@estela/shared";
|
|
3
|
+
/**
|
|
4
|
+
* Qué commits son tuyos.
|
|
5
|
+
*
|
|
6
|
+
* Desde que existe la vista de equipo, `commits` guarda a **todos** los autores
|
|
7
|
+
* del repositorio: es lo que permite enseñar el trabajo del equipo sin que cada
|
|
8
|
+
* compañero instale nada. Pero eso convierte una tabla que antes solo contenía
|
|
9
|
+
* trabajo tuyo en una que contiene el de seis personas.
|
|
10
|
+
*
|
|
11
|
+
* Todo lo que imputa horas tiene que filtrar por aquí antes de sessionizar. Si
|
|
12
|
+
* no, `sessionizeCommits()` derivaría bloques de los commits de tus compañeros
|
|
13
|
+
* y te los imputaría como tiempo tuyo: acabarías facturándole a un cliente el
|
|
14
|
+
* trabajo de otra persona. Es el peor fallo que este sistema puede cometer, así
|
|
15
|
+
* que la decisión vive en un solo sitio y la CLI y el servidor lo comparten.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Correos con los que commiteas en cada repositorio, indexados por su raíz.
|
|
19
|
+
*
|
|
20
|
+
* Manda lo configurado con `estela author`. Solo si un repositorio no tiene
|
|
21
|
+
* autores declarados se cae a `git config user.email`, que es la suposición que
|
|
22
|
+
* en el repositorio de un cliente casi nunca acierta — ahí commiteas con el
|
|
23
|
+
* correo de la empresa, no con el tuyo.
|
|
24
|
+
*/
|
|
25
|
+
export declare function myEmailsByRepo(db: DatabaseSync, repoPaths: Iterable<string>): Promise<Map<string, Set<string>>>;
|
|
26
|
+
/**
|
|
27
|
+
* Se queda solo con tus commits.
|
|
28
|
+
*
|
|
29
|
+
* Un repositorio sin ningún correo conocido no aporta nada: preferimos perder
|
|
30
|
+
* horas a imputarte las de otro. Quedarse corto se arregla con
|
|
31
|
+
* `estela author`; haber facturado trabajo ajeno, no.
|
|
32
|
+
*/
|
|
33
|
+
export declare function onlyMine(commits: readonly CommitRecord[], mine: ReadonlyMap<string, Set<string>>): CommitRecord[];
|
|
34
|
+
//# sourceMappingURL=identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/watchers/identity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAKnD;;;;;;;;;;;;;GAaG;AAEH;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,EAAE,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,GAC5C,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAanC;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,SAAS,YAAY,EAAE,EAAE,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,GACvE,YAAY,EAAE,CAEhB"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.myEmailsByRepo = myEmailsByRepo;
|
|
37
|
+
exports.onlyMine = onlyMine;
|
|
38
|
+
const store = __importStar(require("../db/store.js"));
|
|
39
|
+
const git_js_1 = require("./git.js");
|
|
40
|
+
/**
|
|
41
|
+
* Qué commits son tuyos.
|
|
42
|
+
*
|
|
43
|
+
* Desde que existe la vista de equipo, `commits` guarda a **todos** los autores
|
|
44
|
+
* del repositorio: es lo que permite enseñar el trabajo del equipo sin que cada
|
|
45
|
+
* compañero instale nada. Pero eso convierte una tabla que antes solo contenía
|
|
46
|
+
* trabajo tuyo en una que contiene el de seis personas.
|
|
47
|
+
*
|
|
48
|
+
* Todo lo que imputa horas tiene que filtrar por aquí antes de sessionizar. Si
|
|
49
|
+
* no, `sessionizeCommits()` derivaría bloques de los commits de tus compañeros
|
|
50
|
+
* y te los imputaría como tiempo tuyo: acabarías facturándole a un cliente el
|
|
51
|
+
* trabajo de otra persona. Es el peor fallo que este sistema puede cometer, así
|
|
52
|
+
* que la decisión vive en un solo sitio y la CLI y el servidor lo comparten.
|
|
53
|
+
*/
|
|
54
|
+
/**
|
|
55
|
+
* Correos con los que commiteas en cada repositorio, indexados por su raíz.
|
|
56
|
+
*
|
|
57
|
+
* Manda lo configurado con `estela author`. Solo si un repositorio no tiene
|
|
58
|
+
* autores declarados se cae a `git config user.email`, que es la suposición que
|
|
59
|
+
* en el repositorio de un cliente casi nunca acierta — ahí commiteas con el
|
|
60
|
+
* correo de la empresa, no con el tuyo.
|
|
61
|
+
*/
|
|
62
|
+
async function myEmailsByRepo(db, repoPaths) {
|
|
63
|
+
const configured = store.authorsByRepo(db);
|
|
64
|
+
const mine = new Map();
|
|
65
|
+
for (const path of repoPaths) {
|
|
66
|
+
const root = await (0, git_js_1.repoRoot)(path);
|
|
67
|
+
if (!root || mine.has(root))
|
|
68
|
+
continue;
|
|
69
|
+
const declared = configured.get(root) ?? configured.get(path) ?? [];
|
|
70
|
+
const emails = declared.length ? declared : [await (0, git_js_1.gitUserEmail)(root)].filter(Boolean);
|
|
71
|
+
mine.set(root, new Set(emails));
|
|
72
|
+
}
|
|
73
|
+
return mine;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Se queda solo con tus commits.
|
|
77
|
+
*
|
|
78
|
+
* Un repositorio sin ningún correo conocido no aporta nada: preferimos perder
|
|
79
|
+
* horas a imputarte las de otro. Quedarse corto se arregla con
|
|
80
|
+
* `estela author`; haber facturado trabajo ajeno, no.
|
|
81
|
+
*/
|
|
82
|
+
function onlyMine(commits, mine) {
|
|
83
|
+
return commits.filter((c) => mine.get(c.repoPath)?.has(c.authorEmail) ?? false);
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/watchers/identity.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,wCAeC;AASD,4BAIC;AAtDD,sDAAwC;AACxC,qCAAkD;AAElD;;;;;;;;;;;;;GAaG;AAEH;;;;;;;GAOG;AACI,KAAK,UAAU,cAAc,CAClC,EAAgB,EAAE,SAA2B;IAE7C,MAAM,UAAU,GAAG,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAuB,CAAC;IAE5C,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,MAAM,IAAA,iBAAQ,EAAC,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QAEtC,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACpE,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,IAAA,qBAAY,EAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACvF,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,MAAkB,CAAC,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,QAAQ,CACtB,OAAgC,EAAE,IAAsC;IAExE,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,CAAC;AAClF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.test.d.ts","sourceRoot":"","sources":["../../src/watchers/identity.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
|
+
const node_test_1 = require("node:test");
|
|
8
|
+
const sessionize_js_1 = require("../billing/sessionize.js");
|
|
9
|
+
const identity_js_1 = require("./identity.js");
|
|
10
|
+
function commit(email, iso, repo = "/repo") {
|
|
11
|
+
return {
|
|
12
|
+
repoPath: repo, hash: `${email}-${iso}`, at: new Date(iso),
|
|
13
|
+
authorEmail: email, authorName: email.split("@")[0], branch: "main",
|
|
14
|
+
subject: "trabajo", linesAdded: 1, linesDeleted: 0, files: [],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
const MINE = new Map([["/repo", new Set(["yo@ejemplo.com"])]]);
|
|
18
|
+
(0, node_test_1.test)("el trabajo de un compañero nunca se convierte en horas tuyas", () => {
|
|
19
|
+
// La regresión más cara que este sistema puede sufrir: desde que `commits`
|
|
20
|
+
// guarda a todo el equipo, un filtro que falle te haría facturarle a un
|
|
21
|
+
// cliente el trabajo de otra persona.
|
|
22
|
+
const commits = [
|
|
23
|
+
commit("yo@ejemplo.com", "2026-08-20T09:00:00Z"),
|
|
24
|
+
commit("ana@empresa.com", "2026-08-20T10:00:00Z"),
|
|
25
|
+
commit("ana@empresa.com", "2026-08-21T10:00:00Z"),
|
|
26
|
+
];
|
|
27
|
+
const mine = (0, identity_js_1.onlyMine)(commits, MINE);
|
|
28
|
+
strict_1.default.equal(mine.length, 1);
|
|
29
|
+
strict_1.default.equal(mine[0].authorEmail, "yo@ejemplo.com");
|
|
30
|
+
const blocks = (0, sessionize_js_1.sessionizeCommits)(mine);
|
|
31
|
+
strict_1.default.equal(blocks.length, 1, "solo tu commit produce un bloque");
|
|
32
|
+
strict_1.default.ok(blocks.every((b) => b.commits.every((c) => c.authorEmail === "yo@ejemplo.com")));
|
|
33
|
+
});
|
|
34
|
+
(0, node_test_1.test)("un repositorio sin autores conocidos no aporta horas", () => {
|
|
35
|
+
// Preferimos perder horas a imputarte las de otro: quedarse corto se corrige
|
|
36
|
+
// con `estela author`, haber facturado trabajo ajeno no.
|
|
37
|
+
const commits = [commit("quien.sea@empresa.com", "2026-08-20T09:00:00Z", "/otro")];
|
|
38
|
+
strict_1.default.equal((0, identity_js_1.onlyMine)(commits, MINE).length, 0);
|
|
39
|
+
strict_1.default.equal((0, sessionize_js_1.sessionizeCommits)((0, identity_js_1.onlyMine)(commits, MINE)).length, 0);
|
|
40
|
+
});
|
|
41
|
+
(0, node_test_1.test)("los correos de un repositorio no valen en otro", () => {
|
|
42
|
+
// Commiteas con un correo distinto en cada cliente. Si el filtro no
|
|
43
|
+
// distinguiera el repositorio, capturarías commits ajenos allí donde otra
|
|
44
|
+
// persona use el correo que tú usas aquí.
|
|
45
|
+
const commits = [commit("yo@ejemplo.com", "2026-08-20T09:00:00Z", "/ajeno")];
|
|
46
|
+
strict_1.default.equal((0, identity_js_1.onlyMine)(commits, MINE).length, 0);
|
|
47
|
+
});
|
|
48
|
+
//# sourceMappingURL=identity.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.test.js","sourceRoot":"","sources":["../../src/watchers/identity.test.ts"],"names":[],"mappings":";;;;;AAAA,gEAAwC;AACxC,yCAAiC;AAIjC,4DAA6D;AAC7D,+CAAyC;AAEzC,SAAS,MAAM,CAAC,KAAa,EAAE,GAAW,EAAE,IAAI,GAAG,OAAO;IACxD,OAAO;QACL,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,KAAK,IAAI,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC;QAC1D,WAAW,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,EAAE,MAAM,EAAE,MAAM;QACpE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE;KAC9D,CAAC;AACJ,CAAC;AAED,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,GAAG,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE/D,IAAA,gBAAI,EAAC,8DAA8D,EAAE,GAAG,EAAE;IACxE,2EAA2E;IAC3E,wEAAwE;IACxE,sCAAsC;IACtC,MAAM,OAAO,GAAG;QACd,MAAM,CAAC,gBAAgB,EAAE,sBAAsB,CAAC;QAChD,MAAM,CAAC,iBAAiB,EAAE,sBAAsB,CAAC;QACjD,MAAM,CAAC,iBAAiB,EAAE,sBAAsB,CAAC;KAClD,CAAC;IAEF,MAAM,IAAI,GAAG,IAAA,sBAAQ,EAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACrC,gBAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC7B,gBAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,gBAAgB,CAAC,CAAC;IAErD,MAAM,MAAM,GAAG,IAAA,iCAAiB,EAAC,IAAI,CAAC,CAAC;IACvC,gBAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,kCAAkC,CAAC,CAAC;IACnE,gBAAM,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,gBAAgB,CAAC,CAAC,CAAC,CAAC;AAC7F,CAAC,CAAC,CAAC;AAEH,IAAA,gBAAI,EAAC,sDAAsD,EAAE,GAAG,EAAE;IAChE,6EAA6E;IAC7E,yDAAyD;IACzD,MAAM,OAAO,GAAG,CAAC,MAAM,CAAC,uBAAuB,EAAE,sBAAsB,EAAE,OAAO,CAAC,CAAC,CAAC;IACnF,gBAAM,CAAC,KAAK,CAAC,IAAA,sBAAQ,EAAC,OAAO,EAAE,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAChD,gBAAM,CAAC,KAAK,CAAC,IAAA,iCAAiB,EAAC,IAAA,sBAAQ,EAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;AACrE,CAAC,CAAC,CAAC;AAEH,IAAA,gBAAI,EAAC,gDAAgD,EAAE,GAAG,EAAE;IAC1D,oEAAoE;IACpE,0EAA0E;IAC1E,0CAA0C;IAC1C,MAAM,OAAO,GAAG,CAAC,MAAM,CAAC,gBAAgB,EAAE,sBAAsB,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC7E,gBAAM,CAAC,KAAK,CAAC,IAAA,sBAAQ,EAAC,OAAO,EAAE,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;AAClD,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-ui.test.d.ts","sourceRoot":"","sources":["../src/web-ui.test.ts"],"names":[],"mappings":""}
|