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
package/dist/db/store.js
ADDED
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.upsertClient = upsertClient;
|
|
4
|
+
exports.upsertProject = upsertProject;
|
|
5
|
+
exports.addRatePeriod = addRatePeriod;
|
|
6
|
+
exports.getClient = getClient;
|
|
7
|
+
exports.getProject = getProject;
|
|
8
|
+
exports.listProjects = listProjects;
|
|
9
|
+
exports.listClients = listClients;
|
|
10
|
+
exports.getRates = getRates;
|
|
11
|
+
exports.getProjectAuthors = getProjectAuthors;
|
|
12
|
+
exports.setProjectAuthors = setProjectAuthors;
|
|
13
|
+
exports.recordPublication = recordPublication;
|
|
14
|
+
exports.listPublications = listPublications;
|
|
15
|
+
exports.authorsByRepo = authorsByRepo;
|
|
16
|
+
exports.projectForRepo = projectForRepo;
|
|
17
|
+
exports.saveTurns = saveTurns;
|
|
18
|
+
exports.saveCommits = saveCommits;
|
|
19
|
+
exports.logScan = logScan;
|
|
20
|
+
exports.saveTimeEntry = saveTimeEntry;
|
|
21
|
+
exports.getTimeEntries = getTimeEntries;
|
|
22
|
+
exports.listEntriesBetween = listEntriesBetween;
|
|
23
|
+
exports.saveInvoice = saveInvoice;
|
|
24
|
+
exports.nextInvoiceNumber = nextInvoiceNumber;
|
|
25
|
+
exports.readInvoiceLines = readInvoiceLines;
|
|
26
|
+
exports.upsertSubscription = upsertSubscription;
|
|
27
|
+
exports.listSubscriptions = listSubscriptions;
|
|
28
|
+
exports.totalConsumptionByMonth = totalConsumptionByMonth;
|
|
29
|
+
exports.consumptionByProjectMonth = consumptionByProjectMonth;
|
|
30
|
+
exports.getCloudAccount = getCloudAccount;
|
|
31
|
+
exports.setCloudAccount = setCloudAccount;
|
|
32
|
+
exports.clearCloudAccount = clearCloudAccount;
|
|
33
|
+
exports.getProjectSync = getProjectSync;
|
|
34
|
+
exports.setProjectSync = setProjectSync;
|
|
35
|
+
exports.listTeamSyncedProjects = listTeamSyncedProjects;
|
|
36
|
+
exports.listPersonalSyncCandidates = listPersonalSyncCandidates;
|
|
37
|
+
exports.getAiConsent = getAiConsent;
|
|
38
|
+
exports.setAiConsent = setAiConsent;
|
|
39
|
+
const node_crypto_1 = require("node:crypto");
|
|
40
|
+
const shared_1 = require("@estela/shared");
|
|
41
|
+
const cost_js_1 = require("../pricing/cost.js");
|
|
42
|
+
const iso = (d) => d.toISOString();
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Clientes y proyectos
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
function upsertClient(db, client) {
|
|
47
|
+
db.prepare(`
|
|
48
|
+
INSERT INTO clients (id, name, currency, tax_id, email, address)
|
|
49
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
50
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
51
|
+
name = excluded.name, currency = excluded.currency,
|
|
52
|
+
tax_id = excluded.tax_id, email = excluded.email, address = excluded.address
|
|
53
|
+
`).run(client.id, client.name, client.currency, client.taxId ?? null, client.email ?? null, client.address ?? null);
|
|
54
|
+
}
|
|
55
|
+
function upsertProject(db, project) {
|
|
56
|
+
db.prepare(`
|
|
57
|
+
INSERT INTO projects (id, client_id, name, billable, rounding_minutes, ai_cost_policy, kind)
|
|
58
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
59
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
60
|
+
client_id = excluded.client_id, name = excluded.name,
|
|
61
|
+
billable = excluded.billable, rounding_minutes = excluded.rounding_minutes,
|
|
62
|
+
ai_cost_policy = excluded.ai_cost_policy, kind = excluded.kind
|
|
63
|
+
`).run(project.id, project.clientId, project.name, project.billable ? 1 : 0, project.roundingMinutes, project.aiCostPolicy, project.kind);
|
|
64
|
+
const link = db.prepare("INSERT OR IGNORE INTO project_repos (project_id, repo_path) VALUES (?, ?)");
|
|
65
|
+
for (const repo of project.repoPaths)
|
|
66
|
+
link.run(project.id, repo);
|
|
67
|
+
}
|
|
68
|
+
function addRatePeriod(db, rate) {
|
|
69
|
+
// Cerrar la tarifa abierta anterior en la fecha en que empieza la nueva.
|
|
70
|
+
db.prepare(`
|
|
71
|
+
UPDATE rate_periods SET effective_to = ?
|
|
72
|
+
WHERE project_id = ? AND effective_to IS NULL AND effective_from < ?
|
|
73
|
+
`).run(iso(rate.effectiveFrom), rate.projectId, iso(rate.effectiveFrom));
|
|
74
|
+
db.prepare(`
|
|
75
|
+
INSERT INTO rate_periods (project_id, hourly_minor, currency, effective_from, effective_to)
|
|
76
|
+
VALUES (?, ?, ?, ?, ?)
|
|
77
|
+
`).run(rate.projectId, rate.hourlyRate.amount, rate.hourlyRate.currency, iso(rate.effectiveFrom), rate.effectiveTo ? iso(rate.effectiveTo) : null);
|
|
78
|
+
}
|
|
79
|
+
function getClient(db, id) {
|
|
80
|
+
const row = db.prepare("SELECT * FROM clients WHERE id = ?").get(id);
|
|
81
|
+
if (!row)
|
|
82
|
+
return null;
|
|
83
|
+
return {
|
|
84
|
+
id: row["id"],
|
|
85
|
+
name: row["name"],
|
|
86
|
+
currency: row["currency"],
|
|
87
|
+
...(row["tax_id"] ? { taxId: row["tax_id"] } : {}),
|
|
88
|
+
...(row["email"] ? { email: row["email"] } : {}),
|
|
89
|
+
...(row["address"] ? { address: row["address"] } : {}),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function getProject(db, id) {
|
|
93
|
+
const row = db.prepare("SELECT * FROM projects WHERE id = ?").get(id);
|
|
94
|
+
if (!row)
|
|
95
|
+
return null;
|
|
96
|
+
const repos = db.prepare("SELECT repo_path FROM project_repos WHERE project_id = ?")
|
|
97
|
+
.all(id);
|
|
98
|
+
return {
|
|
99
|
+
id: row["id"],
|
|
100
|
+
clientId: row["client_id"],
|
|
101
|
+
name: row["name"],
|
|
102
|
+
repoPaths: repos.map((r) => r.repo_path),
|
|
103
|
+
billable: Boolean(row["billable"]),
|
|
104
|
+
roundingMinutes: row["rounding_minutes"],
|
|
105
|
+
aiCostPolicy: row["ai_cost_policy"],
|
|
106
|
+
kind: row["kind"] ?? "client",
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function listProjects(db) {
|
|
110
|
+
const rows = db.prepare("SELECT id FROM projects ORDER BY name").all();
|
|
111
|
+
return rows.map((r) => getProject(db, r.id)).filter(Boolean);
|
|
112
|
+
}
|
|
113
|
+
function listClients(db) {
|
|
114
|
+
const rows = db.prepare("SELECT id FROM clients ORDER BY name").all();
|
|
115
|
+
return rows.map((r) => getClient(db, r.id)).filter(Boolean);
|
|
116
|
+
}
|
|
117
|
+
function getRates(db, projectId) {
|
|
118
|
+
const rows = db.prepare("SELECT * FROM rate_periods WHERE project_id = ? ORDER BY effective_from").all(projectId);
|
|
119
|
+
return rows.map((r) => ({
|
|
120
|
+
projectId: r["project_id"],
|
|
121
|
+
hourlyRate: { amount: r["hourly_minor"], currency: r["currency"] },
|
|
122
|
+
effectiveFrom: new Date(r["effective_from"]),
|
|
123
|
+
effectiveTo: r["effective_to"] ? new Date(r["effective_to"]) : null,
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Correos con los que commiteas en un proyecto.
|
|
128
|
+
*
|
|
129
|
+
* Vacío significa "sin configurar", y entonces se cae a la configuración de
|
|
130
|
+
* git. Es un mal apaño pero mejor que capturar los commits de todo el equipo:
|
|
131
|
+
* imputarte el trabajo de un compañero es peor que no imputar nada.
|
|
132
|
+
*/
|
|
133
|
+
function getProjectAuthors(db, projectId) {
|
|
134
|
+
return db.prepare("SELECT author_email FROM project_authors WHERE project_id = ?")
|
|
135
|
+
.all(projectId).map((r) => r.author_email);
|
|
136
|
+
}
|
|
137
|
+
function setProjectAuthors(db, projectId, emails) {
|
|
138
|
+
db.exec("BEGIN");
|
|
139
|
+
try {
|
|
140
|
+
db.prepare("DELETE FROM project_authors WHERE project_id = ?").run(projectId);
|
|
141
|
+
const add = db.prepare("INSERT OR IGNORE INTO project_authors (project_id, author_email) VALUES (?, ?)");
|
|
142
|
+
for (const email of emails) {
|
|
143
|
+
// Sin normalizar a minúsculas: aquí puede haber un nombre de autor, no
|
|
144
|
+
// solo un correo, y git lo compara tal cual.
|
|
145
|
+
const clean = email.trim();
|
|
146
|
+
if (clean)
|
|
147
|
+
add.run(projectId, clean);
|
|
148
|
+
}
|
|
149
|
+
db.exec("COMMIT");
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
db.exec("ROLLBACK");
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function recordPublication(db, projectId, token, baseUrl, at = new Date()) {
|
|
157
|
+
db.prepare(`
|
|
158
|
+
INSERT INTO publications (project_id, token, published_at, base_url)
|
|
159
|
+
VALUES (?, ?, ?, ?)
|
|
160
|
+
ON CONFLICT(project_id) DO UPDATE SET
|
|
161
|
+
token = excluded.token, published_at = excluded.published_at,
|
|
162
|
+
base_url = COALESCE(excluded.base_url, publications.base_url)
|
|
163
|
+
`).run(projectId, token, iso(at), baseUrl);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Estado de las publicaciones.
|
|
167
|
+
*
|
|
168
|
+
* `staleBlocks` cuenta el trabajo posterior a la última publicación. Es lo que
|
|
169
|
+
* de verdad envejece un panel: los días transcurridos no importan si en ese
|
|
170
|
+
* tiempo no tocaste el proyecto.
|
|
171
|
+
*/
|
|
172
|
+
function listPublications(db) {
|
|
173
|
+
const rows = db.prepare("SELECT * FROM publications").all();
|
|
174
|
+
return rows.map((r) => {
|
|
175
|
+
const projectId = r["project_id"];
|
|
176
|
+
const publishedAt = r["published_at"];
|
|
177
|
+
const stale = db.prepare(`
|
|
178
|
+
SELECT COUNT(*) AS n, MAX(updated_at) AS last
|
|
179
|
+
FROM time_entries WHERE project_id = ? AND updated_at > ?
|
|
180
|
+
`).get(projectId, publishedAt);
|
|
181
|
+
return {
|
|
182
|
+
projectId,
|
|
183
|
+
token: r["token"],
|
|
184
|
+
publishedAt: new Date(publishedAt),
|
|
185
|
+
baseUrl: r["base_url"] ?? null,
|
|
186
|
+
staleBlocks: stale.n,
|
|
187
|
+
lastWorkAt: stale.last ? new Date(stale.last) : null,
|
|
188
|
+
};
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/** Todos los correos configurados, por repositorio, para filtrar al importar. */
|
|
192
|
+
function authorsByRepo(db) {
|
|
193
|
+
const rows = db.prepare(`
|
|
194
|
+
SELECT r.repo_path, a.author_email
|
|
195
|
+
FROM project_repos r JOIN project_authors a ON a.project_id = r.project_id
|
|
196
|
+
`).all();
|
|
197
|
+
const map = new Map();
|
|
198
|
+
for (const row of rows) {
|
|
199
|
+
const list = map.get(row.repo_path);
|
|
200
|
+
if (list)
|
|
201
|
+
list.push(row.author_email);
|
|
202
|
+
else
|
|
203
|
+
map.set(row.repo_path, [row.author_email]);
|
|
204
|
+
}
|
|
205
|
+
return map;
|
|
206
|
+
}
|
|
207
|
+
/** Encuentra el proyecto al que pertenece una ruta de repositorio. */
|
|
208
|
+
function projectForRepo(db, repoPath) {
|
|
209
|
+
const rows = db.prepare("SELECT project_id, repo_path FROM project_repos")
|
|
210
|
+
.all();
|
|
211
|
+
// El prefijo más largo gana: permite anidar un subproyecto dentro de un monorepo.
|
|
212
|
+
const match = rows
|
|
213
|
+
.filter((r) => repoPath === r.repo_path || repoPath.startsWith(r.repo_path + "/"))
|
|
214
|
+
.sort((a, b) => b.repo_path.length - a.repo_path.length)[0];
|
|
215
|
+
return match?.project_id ?? null;
|
|
216
|
+
}
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
// Captura
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
/** Persiste turnos. `INSERT OR IGNORE` sobre la PK hace la dedup permanente. */
|
|
221
|
+
function saveTurns(db, turns) {
|
|
222
|
+
const stmt = db.prepare(`
|
|
223
|
+
INSERT OR IGNORE INTO agent_turns (
|
|
224
|
+
turn_id, agent, session_id, at, model, repo_path, branch,
|
|
225
|
+
tok_input, tok_output, tok_cache_read, tok_cache_w5m, tok_cache_w1h,
|
|
226
|
+
cost_micro_usd, producer_version
|
|
227
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
228
|
+
`);
|
|
229
|
+
let inserted = 0;
|
|
230
|
+
db.exec("BEGIN");
|
|
231
|
+
try {
|
|
232
|
+
for (const t of turns) {
|
|
233
|
+
const cost = (0, cost_js_1.costOfTurn)(t.tokens, t.model, t.at);
|
|
234
|
+
const result = stmt.run(t.turnId, t.agent, t.sessionId, iso(t.at), t.model, t.repoPath, t.branch, t.tokens.input, t.tokens.output, t.tokens.cacheRead, t.tokens.cacheWrite5m, t.tokens.cacheWrite1h, cost ? cost.microUsd : null, t.producerVersion);
|
|
235
|
+
inserted += Number(result.changes);
|
|
236
|
+
}
|
|
237
|
+
db.exec("COMMIT");
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
db.exec("ROLLBACK");
|
|
241
|
+
throw error;
|
|
242
|
+
}
|
|
243
|
+
return inserted;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Guarda commits.
|
|
247
|
+
*
|
|
248
|
+
* El commit es inmutable, pero **nuestra lectura de él no lo es**: si un día se
|
|
249
|
+
* corrige el parser, reimportar tiene que poder rehacer los campos derivados.
|
|
250
|
+
* Con `INSERT OR IGNORE` los datos mal parseados se quedarían para siempre, sin
|
|
251
|
+
* forma de arreglarlos salvo borrando la base entera.
|
|
252
|
+
*
|
|
253
|
+
* Por eso hash y fecha se conservan (son la identidad) y las estadísticas se
|
|
254
|
+
* sobrescriben.
|
|
255
|
+
*/
|
|
256
|
+
function saveCommits(db, commits) {
|
|
257
|
+
const stmt = db.prepare(`
|
|
258
|
+
INSERT INTO commits
|
|
259
|
+
(repo_path, hash, at, author_email, author_name, branch, subject,
|
|
260
|
+
lines_added, lines_deleted, files)
|
|
261
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
262
|
+
ON CONFLICT(repo_path, hash) DO UPDATE SET
|
|
263
|
+
subject = excluded.subject,
|
|
264
|
+
branch = excluded.branch,
|
|
265
|
+
author_name = excluded.author_name,
|
|
266
|
+
lines_added = excluded.lines_added,
|
|
267
|
+
lines_deleted = excluded.lines_deleted,
|
|
268
|
+
files = excluded.files
|
|
269
|
+
`);
|
|
270
|
+
let inserted = 0;
|
|
271
|
+
db.exec("BEGIN");
|
|
272
|
+
try {
|
|
273
|
+
for (const c of commits) {
|
|
274
|
+
const result = stmt.run(c.repoPath, c.hash, iso(c.at), c.authorEmail, c.authorName, c.branch, c.subject, c.linesAdded, c.linesDeleted, c.files.join("\n"));
|
|
275
|
+
inserted += Number(result.changes);
|
|
276
|
+
}
|
|
277
|
+
db.exec("COMMIT");
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
db.exec("ROLLBACK");
|
|
281
|
+
throw error;
|
|
282
|
+
}
|
|
283
|
+
return inserted;
|
|
284
|
+
}
|
|
285
|
+
function logScan(db, source, report) {
|
|
286
|
+
db.prepare(`
|
|
287
|
+
INSERT INTO scan_log (
|
|
288
|
+
ran_at, source, files_read, records_seen, turns_accepted,
|
|
289
|
+
duplicates_dropped, unknown_records, malformed_records,
|
|
290
|
+
producer_versions, warnings
|
|
291
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
292
|
+
`).run(iso(new Date()), source, report.filesRead, report.recordsSeen, report.turnsAccepted, report.duplicatesDropped, report.unknownRecords, report.malformedRecords, report.producerVersions.join(","), report.warnings.join(" | "));
|
|
293
|
+
}
|
|
294
|
+
// ---------------------------------------------------------------------------
|
|
295
|
+
// Imputaciones y facturas
|
|
296
|
+
// ---------------------------------------------------------------------------
|
|
297
|
+
/**
|
|
298
|
+
* Guarda una imputación.
|
|
299
|
+
*
|
|
300
|
+
* `at` es la hora de escritura, no la del trabajo. Se puede pasar para que los
|
|
301
|
+
* tests no dependan del reloj: publicar y escribir en el mismo milisegundo hace
|
|
302
|
+
* que la comparación de desfase falle por empate.
|
|
303
|
+
*/
|
|
304
|
+
function saveTimeEntry(db, entry, at = new Date()) {
|
|
305
|
+
const id = entry.id ?? `te_${(0, node_crypto_1.randomUUID)()}`;
|
|
306
|
+
db.prepare(`
|
|
307
|
+
INSERT INTO time_entries (
|
|
308
|
+
id, project_id, started_at, local_date, ended_at, seconds, description,
|
|
309
|
+
billable, approved, invoice_id, ai_micro_usd, agent_seconds,
|
|
310
|
+
commit_hashes, agents, source, kind, branch, updated_at
|
|
311
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
312
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
313
|
+
started_at = excluded.started_at, local_date = excluded.local_date,
|
|
314
|
+
ended_at = excluded.ended_at,
|
|
315
|
+
seconds = excluded.seconds, description = excluded.description,
|
|
316
|
+
billable = excluded.billable, ai_micro_usd = excluded.ai_micro_usd,
|
|
317
|
+
-- Los commits también se rehacen. Sin esto, un bloque creado cuando
|
|
318
|
+
-- todavía no se capturaban los commits del proyecto se quedaba sin
|
|
319
|
+
-- ellos para siempre: la descripción se corregía al reimportar, pero
|
|
320
|
+
-- el respaldo que la justifica no aparecía nunca.
|
|
321
|
+
commit_hashes = excluded.commit_hashes,
|
|
322
|
+
agents = excluded.agents,
|
|
323
|
+
agent_seconds = excluded.agent_seconds,
|
|
324
|
+
-- La procedencia también se rehace: un bloque deducido de commits pasa a
|
|
325
|
+
-- 'agent' en cuanto aparece el transcript que lo cubre, y al revés. Sin
|
|
326
|
+
-- esto, una fila mal etiquetada lo seguiría estando para siempre.
|
|
327
|
+
source = excluded.source,
|
|
328
|
+
branch = excluded.branch,
|
|
329
|
+
-- Solo se marca como tocada si algo cambió de verdad: un reimport que
|
|
330
|
+
-- reescribe los mismos valores no debería envejecer un panel publicado.
|
|
331
|
+
updated_at = CASE
|
|
332
|
+
WHEN time_entries.seconds <> excluded.seconds
|
|
333
|
+
OR time_entries.description <> excluded.description
|
|
334
|
+
OR time_entries.commit_hashes <> excluded.commit_hashes
|
|
335
|
+
THEN excluded.updated_at ELSE time_entries.updated_at END
|
|
336
|
+
-- Reimportar rehace lo deducido de los agentes. Lo escrito a mano es tuyo:
|
|
337
|
+
-- un import no puede borrarte una reunión de dos horas que apuntaste ayer.
|
|
338
|
+
WHERE time_entries.source IN ('agent', 'commit')
|
|
339
|
+
`).run(id, entry.projectId, iso(entry.startedAt), (0, shared_1.localDate)(entry.startedAt), iso(entry.endedAt), entry.seconds, entry.description, entry.billable ? 1 : 0, entry.invoiceId, entry.aiCost.microUsd, entry.agentSeconds, entry.commitHashes.join(","), entry.agents.join(","), entry.source, entry.kind, entry.branch, iso(at));
|
|
340
|
+
return id;
|
|
341
|
+
}
|
|
342
|
+
function getTimeEntries(db, projectId) {
|
|
343
|
+
const rows = db.prepare("SELECT * FROM time_entries WHERE project_id = ? ORDER BY started_at").all(projectId);
|
|
344
|
+
return rows.map(rowToEntry);
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Imputaciones de un rango de fechas.
|
|
348
|
+
*
|
|
349
|
+
* Filtra por `local_date` y no por `started_at`: la fecha en que trabajaste es
|
|
350
|
+
* la de tu reloj, no la de Greenwich. Un bloque de las 23:20 pertenece a hoy
|
|
351
|
+
* aunque en UTC ya sea mañana.
|
|
352
|
+
*/
|
|
353
|
+
function listEntriesBetween(db, from, to) {
|
|
354
|
+
const rows = db.prepare("SELECT * FROM time_entries WHERE local_date >= ? AND local_date <= ? ORDER BY started_at").all(from, to);
|
|
355
|
+
return rows.map(rowToEntry);
|
|
356
|
+
}
|
|
357
|
+
function rowToEntry(r) {
|
|
358
|
+
return ({
|
|
359
|
+
id: r["id"],
|
|
360
|
+
projectId: r["project_id"],
|
|
361
|
+
startedAt: new Date(r["started_at"]),
|
|
362
|
+
endedAt: new Date(r["ended_at"]),
|
|
363
|
+
seconds: r["seconds"],
|
|
364
|
+
description: r["description"],
|
|
365
|
+
billable: Boolean(r["billable"]),
|
|
366
|
+
invoiceId: r["invoice_id"] ?? null,
|
|
367
|
+
aiCost: { microUsd: r["ai_micro_usd"] },
|
|
368
|
+
agentSeconds: r["agent_seconds"],
|
|
369
|
+
commitHashes: String(r["commit_hashes"] || "").split(",").filter(Boolean),
|
|
370
|
+
agents: String(r["agents"] || "").split(",").filter(Boolean),
|
|
371
|
+
source: r["source"] ?? "agent",
|
|
372
|
+
kind: r["kind"] ?? "development",
|
|
373
|
+
branch: r["branch"] ?? null,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
/** Guarda la factura y marca sus imputaciones. Todo o nada. */
|
|
377
|
+
function saveInvoice(db, invoice, entryIds) {
|
|
378
|
+
db.exec("BEGIN");
|
|
379
|
+
try {
|
|
380
|
+
db.prepare(`
|
|
381
|
+
INSERT INTO invoices (
|
|
382
|
+
id, number, client_id, project_id, issued_at, cutoff_at, period_start,
|
|
383
|
+
currency, subtotal_minor, total_minor, total_seconds, ai_micro_usd,
|
|
384
|
+
usd_fx_rate, ai_billed_minor, ai_amort_minor, ai_amort_cur, notes, lines_json
|
|
385
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
386
|
+
`).run(invoice.id, invoice.number, invoice.clientId, invoice.projectId, iso(invoice.issuedAt), iso(invoice.cutoffAt), iso(invoice.periodStart), invoice.currency, invoice.subtotal.amount, invoice.total.amount, invoice.totalSeconds, invoice.aiCost.microUsd, invoice.usdFxRate, invoice.aiCostBilled?.amount ?? null, invoice.aiAmortized?.amount ?? null, invoice.aiAmortized?.currency ?? null, invoice.notes ?? null, JSON.stringify(invoice.lines));
|
|
387
|
+
const mark = db.prepare("UPDATE time_entries SET invoice_id = ? WHERE id = ?");
|
|
388
|
+
for (const id of entryIds)
|
|
389
|
+
mark.run(invoice.id, id);
|
|
390
|
+
db.exec("COMMIT");
|
|
391
|
+
}
|
|
392
|
+
catch (error) {
|
|
393
|
+
db.exec("ROLLBACK");
|
|
394
|
+
throw error;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
function nextInvoiceNumber(db, prefix) {
|
|
398
|
+
const year = new Date().getFullYear();
|
|
399
|
+
const row = db.prepare("SELECT COUNT(*) AS n FROM invoices WHERE number LIKE ?").get(`${prefix}-${year}-%`);
|
|
400
|
+
return `${prefix}-${year}-${String(row.n + 1).padStart(3, "0")}`;
|
|
401
|
+
}
|
|
402
|
+
function readInvoiceLines(json) {
|
|
403
|
+
return JSON.parse(json);
|
|
404
|
+
}
|
|
405
|
+
// ---------------------------------------------------------------------------
|
|
406
|
+
// Suscripciones
|
|
407
|
+
// ---------------------------------------------------------------------------
|
|
408
|
+
function upsertSubscription(db, sub) {
|
|
409
|
+
db.prepare(`
|
|
410
|
+
INSERT INTO subscriptions (id, name, fee_minor, currency, effective_from, effective_to)
|
|
411
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
412
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
413
|
+
name = excluded.name, fee_minor = excluded.fee_minor,
|
|
414
|
+
currency = excluded.currency, effective_from = excluded.effective_from,
|
|
415
|
+
effective_to = excluded.effective_to
|
|
416
|
+
`).run(sub.id, sub.name, sub.monthlyFee.amount, sub.monthlyFee.currency, iso(sub.effectiveFrom), sub.effectiveTo ? iso(sub.effectiveTo) : null);
|
|
417
|
+
}
|
|
418
|
+
function listSubscriptions(db) {
|
|
419
|
+
const rows = db.prepare("SELECT * FROM subscriptions ORDER BY effective_from")
|
|
420
|
+
.all();
|
|
421
|
+
return rows.map((r) => ({
|
|
422
|
+
id: r["id"],
|
|
423
|
+
name: r["name"],
|
|
424
|
+
monthlyFee: { amount: r["fee_minor"], currency: r["currency"] },
|
|
425
|
+
effectiveFrom: new Date(r["effective_from"]),
|
|
426
|
+
effectiveTo: r["effective_to"] ? new Date(r["effective_to"]) : null,
|
|
427
|
+
}));
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Consumo total del mes en toda la máquina, esté asignado a un proyecto o no.
|
|
431
|
+
*
|
|
432
|
+
* Es el denominador correcto para repartir una cuota fija: la pagaste entera,
|
|
433
|
+
* la usaras donde la usaras. Se lee de `agent_turns` y no de `time_entries`
|
|
434
|
+
* porque ahí está *todo* tu consumo, incluidos los repos que aún no configuraste.
|
|
435
|
+
*/
|
|
436
|
+
function totalConsumptionByMonth(db) {
|
|
437
|
+
const rows = db.prepare(`
|
|
438
|
+
SELECT substr(at, 1, 7) AS month, SUM(COALESCE(cost_micro_usd, 0)) AS micro
|
|
439
|
+
FROM agent_turns
|
|
440
|
+
GROUP BY month
|
|
441
|
+
`).all();
|
|
442
|
+
return new Map(rows.map((r) => [r.month, { microUsd: r.micro ?? 0 }]));
|
|
443
|
+
}
|
|
444
|
+
/** Consumo por proyecto y mes, sobre las imputaciones ya asignadas. */
|
|
445
|
+
function consumptionByProjectMonth(db) {
|
|
446
|
+
const rows = db.prepare(`
|
|
447
|
+
SELECT project_id, substr(started_at, 1, 7) AS month, SUM(ai_micro_usd) AS micro
|
|
448
|
+
FROM time_entries
|
|
449
|
+
GROUP BY project_id, month
|
|
450
|
+
`).all();
|
|
451
|
+
return rows.map((r) => ({
|
|
452
|
+
projectId: r.project_id,
|
|
453
|
+
month: r.month,
|
|
454
|
+
consumption: { microUsd: r.micro ?? 0 },
|
|
455
|
+
}));
|
|
456
|
+
}
|
|
457
|
+
// ---------------------------------------------------------------------------
|
|
458
|
+
// Cuenta cloud, alcance de sincronización y consentimiento de IA
|
|
459
|
+
// ---------------------------------------------------------------------------
|
|
460
|
+
/** La cuenta cloud vinculada a esta máquina, o null si todo sigue local. */
|
|
461
|
+
function getCloudAccount(db) {
|
|
462
|
+
const row = db.prepare("SELECT * FROM cloud_account WHERE id = 'local'")
|
|
463
|
+
.get();
|
|
464
|
+
if (!row)
|
|
465
|
+
return null;
|
|
466
|
+
return {
|
|
467
|
+
accountId: row["account_id"],
|
|
468
|
+
email: row["email"],
|
|
469
|
+
plan: row["plan"],
|
|
470
|
+
deviceToken: row["device_token"],
|
|
471
|
+
apiBaseUrl: row["api_base_url"],
|
|
472
|
+
linkedAt: new Date(row["linked_at"]),
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
function setCloudAccount(db, account) {
|
|
476
|
+
db.prepare(`
|
|
477
|
+
INSERT INTO cloud_account (id, account_id, email, plan, device_token, api_base_url, linked_at)
|
|
478
|
+
VALUES ('local', ?, ?, ?, ?, ?, ?)
|
|
479
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
480
|
+
account_id = excluded.account_id, email = excluded.email, plan = excluded.plan,
|
|
481
|
+
device_token = excluded.device_token, api_base_url = excluded.api_base_url,
|
|
482
|
+
linked_at = excluded.linked_at
|
|
483
|
+
`).run(account.accountId, account.email, account.plan, account.deviceToken, account.apiBaseUrl, iso(account.linkedAt));
|
|
484
|
+
}
|
|
485
|
+
/** `estela logout`. Borra el vínculo cloud; los datos locales no se tocan. */
|
|
486
|
+
function clearCloudAccount(db) {
|
|
487
|
+
db.prepare("DELETE FROM cloud_account WHERE id = 'local'").run();
|
|
488
|
+
}
|
|
489
|
+
function getProjectSync(db, projectId) {
|
|
490
|
+
const row = db.prepare("SELECT * FROM project_sync WHERE project_id = ?")
|
|
491
|
+
.get(projectId);
|
|
492
|
+
if (!row)
|
|
493
|
+
return null;
|
|
494
|
+
return {
|
|
495
|
+
projectId: row["project_id"],
|
|
496
|
+
scope: row["scope"],
|
|
497
|
+
remoteProjectId: row["remote_project_id"],
|
|
498
|
+
remoteOrgId: row["remote_org_id"] ?? null,
|
|
499
|
+
inviteToken: row["invite_token"] ?? null,
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
function setProjectSync(db, sync) {
|
|
503
|
+
db.prepare(`
|
|
504
|
+
INSERT INTO project_sync (project_id, scope, remote_project_id, remote_org_id, invite_token)
|
|
505
|
+
VALUES (?, ?, ?, ?, ?)
|
|
506
|
+
ON CONFLICT(project_id) DO UPDATE SET
|
|
507
|
+
scope = excluded.scope, remote_project_id = excluded.remote_project_id,
|
|
508
|
+
remote_org_id = excluded.remote_org_id, invite_token = excluded.invite_token
|
|
509
|
+
`).run(sync.projectId, sync.scope, sync.remoteProjectId, sync.remoteOrgId, sync.inviteToken);
|
|
510
|
+
}
|
|
511
|
+
/** Proyectos de equipo enlazados en esta máquina — lo único que sube `sync/team.ts`. */
|
|
512
|
+
function listTeamSyncedProjects(db) {
|
|
513
|
+
const rows = db.prepare("SELECT project_id FROM project_sync WHERE scope = 'team'")
|
|
514
|
+
.all();
|
|
515
|
+
return rows.map((r) => getProjectSync(db, r.project_id)).filter(Boolean);
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Proyectos candidatos al sync personal (Pro): todos menos los que ya están
|
|
519
|
+
* enlazados a un equipo. Es la guardia anti-fuga: un proyecto de equipo nunca
|
|
520
|
+
* puede colarse también como sync personal, porque `project_sync.project_id`
|
|
521
|
+
* es su clave primaria — un proyecto tiene un único alcance.
|
|
522
|
+
*/
|
|
523
|
+
function listPersonalSyncCandidates(db) {
|
|
524
|
+
const rows = db.prepare(`
|
|
525
|
+
SELECT id FROM projects
|
|
526
|
+
WHERE id NOT IN (SELECT project_id FROM project_sync WHERE scope = 'team')
|
|
527
|
+
`).all();
|
|
528
|
+
return rows.map((r) => r.id);
|
|
529
|
+
}
|
|
530
|
+
function getAiConsent(db, projectId) {
|
|
531
|
+
const row = db.prepare("SELECT * FROM ai_consent WHERE project_id = ?")
|
|
532
|
+
.get(projectId);
|
|
533
|
+
if (!row)
|
|
534
|
+
return null;
|
|
535
|
+
return {
|
|
536
|
+
projectId: row["project_id"],
|
|
537
|
+
requested: Boolean(row["requested"]),
|
|
538
|
+
decision: row["decision"],
|
|
539
|
+
decidedAt: row["decided_at"] ? new Date(row["decided_at"]) : null,
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
function setAiConsent(db, projectId, requested, decision) {
|
|
543
|
+
db.prepare(`
|
|
544
|
+
INSERT INTO ai_consent (project_id, requested, decision, decided_at)
|
|
545
|
+
VALUES (?, ?, ?, ?)
|
|
546
|
+
ON CONFLICT(project_id) DO UPDATE SET
|
|
547
|
+
requested = excluded.requested, decision = excluded.decision, decided_at = excluded.decided_at
|
|
548
|
+
`).run(projectId, requested ? 1 : 0, decision, decision === "pending" ? null : iso(new Date()));
|
|
549
|
+
}
|
|
550
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/db/store.ts"],"names":[],"mappings":"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"}
|
package/dist/doctor.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
2
|
+
/**
|
|
3
|
+
* Revisión de los datos antes de publicar.
|
|
4
|
+
*
|
|
5
|
+
* Existe porque el fallo que arruina una demo no es una gráfica fea: es una
|
|
6
|
+
* cifra que no cuadra delante del cliente. Cada comprobación de aquí
|
|
7
|
+
* corresponde a un fallo que ya ocurrió de verdad — un proyecto sin autor
|
|
8
|
+
* configurado capturando 3 commits de 1697, filas duplicadas por mezclar UTC
|
|
9
|
+
* con hora local, modelos sin precio sumando cero en silencio.
|
|
10
|
+
*
|
|
11
|
+
* Todo lo que avisa tiene arreglo y dice cuál. Un diagnóstico sin remedio solo
|
|
12
|
+
* genera ansiedad.
|
|
13
|
+
*/
|
|
14
|
+
export type Severity = "error" | "warning" | "info";
|
|
15
|
+
export interface Finding {
|
|
16
|
+
readonly severity: Severity;
|
|
17
|
+
readonly title: string;
|
|
18
|
+
readonly detail: string;
|
|
19
|
+
/** Qué hacer. Sin esto, avisar solo preocupa. */
|
|
20
|
+
readonly fix: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function diagnose(db: DatabaseSync): Finding[];
|
|
23
|
+
export declare function renderFindings(findings: readonly Finding[]): string;
|
|
24
|
+
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMhD;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAEpD,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iDAAiD;IACjD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,QAAQ,CAAC,EAAE,EAAE,YAAY,GAAG,OAAO,EAAE,CA6LpD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAyBnE"}
|