synthesisui 0.16.177 → 0.16.179
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/dist/commands/add.js +35 -11
- package/dist/commands/align.js +7 -2
- package/dist/commands/sync.js +6 -1
- package/dist/doctor/ledger.js +49 -0
- package/package.json +1 -1
package/dist/commands/add.js
CHANGED
|
@@ -25,21 +25,45 @@ import { detectStack } from "../stack.js";
|
|
|
25
25
|
*
|
|
26
26
|
* Escrito uma vez e nunca reescrito: um arquivo que alguém ajustou é decisão dele.
|
|
27
27
|
*/
|
|
28
|
+
/**
|
|
29
|
+
* O que NÃO é do repositório: a medição e o registro local. Ambos são reescritos por inteiro a cada
|
|
30
|
+
* rodada e conflitariam em todo merge; o `.ledger-sent` descreve este clone e mais nada.
|
|
31
|
+
*/
|
|
32
|
+
const IGNORED = [
|
|
33
|
+
"census.json",
|
|
34
|
+
"ledger.jsonl",
|
|
35
|
+
".ledger-sent",
|
|
36
|
+
"not-expressed.md",
|
|
37
|
+
];
|
|
38
|
+
const IGNORE_HEADER = "# Managed by synthesisui. The identity and the CSS are committed so a fresh\n" +
|
|
39
|
+
"# clone is governed; the measurement and the local record are not, because\n" +
|
|
40
|
+
"# both are rewritten in full on every run and would conflict on every merge.\n";
|
|
28
41
|
async function writeGovernanceIgnore(projectRoot) {
|
|
29
42
|
const path = join(projectRoot, "_synthesisui", ".gitignore");
|
|
30
|
-
|
|
31
|
-
|
|
43
|
+
const existing = await readFile(path, "utf8").catch(() => null);
|
|
44
|
+
/**
|
|
45
|
+
* O ARQUIVO DIZ "Managed by synthesisui", E ELE PRECISA SER.
|
|
46
|
+
*
|
|
47
|
+
* Isto saía na primeira linha quando o arquivo já existia, então ele era escrito UMA vez e nunca
|
|
48
|
+
* mais. A consequência aparece na primeira vez que a gente acrescenta um registro local: em 07/08
|
|
49
|
+
* nasceu o `.ledger-sent`, e todo repositório que já tinha o `.gitignore` passaria a commitar um
|
|
50
|
+
* cursor que descreve a máquina de UMA pessoa. Um arquivo que se declara gerenciado e não é
|
|
51
|
+
* reconciliado é uma promessa quebrada com o time de alguém.
|
|
52
|
+
*
|
|
53
|
+
* ACRESCENTA, NUNCA REMOVE: uma linha que a pessoa escreveu ali é decisão dela, e some no dia em
|
|
54
|
+
* que a gente reescrevesse o arquivo inteiro. E só grava quando falta algo - um comando que sempre
|
|
55
|
+
* produz diff é um comando que o time para de rodar.
|
|
56
|
+
*/
|
|
57
|
+
if (existing == null) {
|
|
58
|
+
await writeFile(path, `${IGNORE_HEADER}${IGNORED.map((l) => `${l}\n`).join("")}`, "utf8");
|
|
32
59
|
return;
|
|
33
60
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"census.json\n" +
|
|
41
|
-
"ledger.jsonl\n" +
|
|
42
|
-
"not-expressed.md\n", "utf8");
|
|
61
|
+
const lines = new Set(existing.split("\n").map((l) => l.trim()));
|
|
62
|
+
const missing = IGNORED.filter((l) => !lines.has(l));
|
|
63
|
+
if (missing.length === 0)
|
|
64
|
+
return;
|
|
65
|
+
const sep = existing.endsWith("\n") ? "" : "\n";
|
|
66
|
+
await writeFile(path, `${existing}${sep}${missing.map((l) => `${l}\n`).join("")}`, "utf8");
|
|
43
67
|
}
|
|
44
68
|
/**
|
|
45
69
|
* APAGA A PROSA QUE ESTA VERSÃO DEIXOU DE ESCREVER.
|
package/dist/commands/align.js
CHANGED
|
@@ -2,7 +2,7 @@ import { readdir, readFile } from "node:fs/promises";
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
4
|
import { readToken, resolveRegistry } from "../config.js";
|
|
5
|
-
import {
|
|
5
|
+
import { unsentEvents } from "../doctor/ledger.js";
|
|
6
6
|
import { measuredScope } from "../measured-scope.js";
|
|
7
7
|
import { READER } from "../reader-version.js";
|
|
8
8
|
/**
|
|
@@ -124,7 +124,12 @@ opts = {}) {
|
|
|
124
124
|
*/
|
|
125
125
|
run: "npx synthesisui sync",
|
|
126
126
|
});
|
|
127
|
-
|
|
127
|
+
/**
|
|
128
|
+
* SÓ O QUE NÃO SUBIU - ver `unsentEvents`. Isto contava o arquivo inteiro, e como o ledger é
|
|
129
|
+
* append-only e o `sync` manda tudo (a plataforma deduplica), a linha nunca mais saía da tela e o
|
|
130
|
+
* número só crescia.
|
|
131
|
+
*/
|
|
132
|
+
const events = await unsentEvents(root).catch(() => []);
|
|
128
133
|
if (events.length > 0)
|
|
129
134
|
out.push({
|
|
130
135
|
says: `${events.length} checked write${events.length === 1 ? "" : "s"} recorded here and not sent - the platform is scoring this repo on older evidence.`,
|
package/dist/commands/sync.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readdir, readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
import { readToken, resolveRegistry } from "../config.js";
|
|
4
|
-
import { readEvents } from "../doctor/ledger.js";
|
|
4
|
+
import { markSent, readEvents } from "../doctor/ledger.js";
|
|
5
5
|
import { checkableName, closeRequest, readRequests, verifyAndCloseRequests, } from "../doctor/requests.js";
|
|
6
6
|
import { measuredScope, rememberScope } from "../measured-scope.js";
|
|
7
7
|
import { body, paint, section, snippet } from "../output.js";
|
|
@@ -114,6 +114,11 @@ export async function sync(opts) {
|
|
|
114
114
|
console.log(body(err.message ?? `The registry answered ${res.status}.`));
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* O QUE SUBIU, ANOTADO - senão o `align` continua pedindo o envio do que acabou de ser enviado.
|
|
119
|
+
* Depois da resposta OK, e nunca antes: marcar um envio que falhou perderia os eventos de vez.
|
|
120
|
+
*/
|
|
121
|
+
await markSent(root, events);
|
|
117
122
|
const out = (await res.json());
|
|
118
123
|
console.log(section("Sync"));
|
|
119
124
|
console.log(body(`${out.eventsReceived} checks sent, ${out.eventsNew} new. ${out.requestsNow} open request${out.requestsNow === 1 ? "" : "s"}.`));
|
package/dist/doctor/ledger.js
CHANGED
|
@@ -46,6 +46,55 @@ export async function appendEvent(root, event) {
|
|
|
46
46
|
// is a ledger that gets the hook uninstalled.
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* ATÉ ONDE ESTE REPO JÁ MANDOU - e a ausência disto fazia o `align` mentir para sempre.
|
|
51
|
+
*
|
|
52
|
+
* O ledger é APPEND-ONLY e o `sync` manda o arquivo inteiro; a plataforma deduplica (em 07/08 uma
|
|
53
|
+
* rodada reportou "6 checks sent, 1 new"). Só que o `align` contava TODAS as linhas e dizia "6
|
|
54
|
+
* checked writes recorded here and not sent" - sobre cinco que tinham subido minutos antes. O número
|
|
55
|
+
* só crescia, então aquela linha nunca mais sairia da tela, e um aviso permanente é um aviso que a
|
|
56
|
+
* pessoa aprende a pular (dono, 07/08: *"quer apostar que você vai achar algo errado?"*).
|
|
57
|
+
*
|
|
58
|
+
* A CONTAGEM E A HORA, as duas: a contagem responde sozinha enquanto o arquivo só cresce, e a hora
|
|
59
|
+
* dá como perceber que ele foi trocado por outro - aí a contagem antiga não vale mais nada.
|
|
60
|
+
*/
|
|
61
|
+
const SENT_FILE = ".ledger-sent";
|
|
62
|
+
const sentPath = (root) => join(root, "_synthesisui", SENT_FILE);
|
|
63
|
+
export async function readSent(root) {
|
|
64
|
+
const raw = await readFile(sentPath(root), "utf8").catch(() => "");
|
|
65
|
+
if (!raw)
|
|
66
|
+
return { count: 0 };
|
|
67
|
+
try {
|
|
68
|
+
const v = JSON.parse(raw);
|
|
69
|
+
return {
|
|
70
|
+
count: typeof v.count === "number" && v.count >= 0 ? v.count : 0,
|
|
71
|
+
...(typeof v.at === "string" ? { at: v.at } : {}),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return { count: 0 };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export async function markSent(root, events) {
|
|
79
|
+
const last = events[events.length - 1]?.at;
|
|
80
|
+
await writeFile(sentPath(root), `${JSON.stringify({ count: events.length, ...(last ? { at: last } : {}) })}\n`, "utf8").catch(() => {
|
|
81
|
+
/** O registro nunca pode custar o envio: o pior caso é o aviso reaparecer uma vez. */
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Os eventos que este repo ainda não mandou.
|
|
86
|
+
*
|
|
87
|
+
* Se a contagem gravada é maior que o arquivo, alguém o trocou ou apagou - e aí a marca não descreve
|
|
88
|
+
* mais este arquivo. Contar tudo de novo é a leitura honesta: um envio a mais custa nada, e a
|
|
89
|
+
* plataforma deduplica.
|
|
90
|
+
*/
|
|
91
|
+
export async function unsentEvents(root) {
|
|
92
|
+
const all = await readEvents(root);
|
|
93
|
+
const { count } = await readSent(root);
|
|
94
|
+
if (count <= 0 || count > all.length)
|
|
95
|
+
return all;
|
|
96
|
+
return all.slice(count);
|
|
97
|
+
}
|
|
49
98
|
export async function readEvents(root) {
|
|
50
99
|
const raw = await readFile(ledgerPath(root), "utf8").catch(() => "");
|
|
51
100
|
if (!raw)
|
package/package.json
CHANGED