synthesisui 0.16.188 → 0.16.190
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/anatomy-from-sketch.js +47 -1
- package/dist/commands/import.js +12 -2
- package/dist/frontier-kind.js +8 -1
- package/dist/index.js +103 -1
- package/dist/repo-state.js +19 -1
- package/package.json +1 -1
|
@@ -237,6 +237,33 @@ const MEMBER_FORM = {
|
|
|
237
237
|
* already the recipe's `base`, and emitting it again would draw the component
|
|
238
238
|
* inside a copy of itself.
|
|
239
239
|
*/
|
|
240
|
+
/**
|
|
241
|
+
* COMO A PEÇA DA BIBLIOTECA FOI USADA, verbatim - e ela nunca atravessava.
|
|
242
|
+
*
|
|
243
|
+
* O sketch já lia `behaviourProps` no ponto de chamada (`open`, `onOpenChange`, `disabled`) e o
|
|
244
|
+
* contrato já tinha o campo; entre um e outro não havia ninguém carregando. Medido no repo do dono em
|
|
245
|
+
* 09/08: 15 nós carregavam as props na leitura e ZERO chegavam na receita.
|
|
246
|
+
*
|
|
247
|
+
* É a metade que decide se um agente consegue reproduzir o componente sem inventar: `Collapsible.Root`
|
|
248
|
+
* sozinho não diz se o disclosure é controlado; `Collapsible.Root` com `open` e `onOpenChange` diz.
|
|
249
|
+
*
|
|
250
|
+
* Teto de 12 props e 200 caracteres cada: isto viaja no censo e uma expressão de chamada pode ser uma
|
|
251
|
+
* linha inteira de código. O que passa disso é ruído no lugar onde a receita precisa ser lida.
|
|
252
|
+
*/
|
|
253
|
+
function behaviour(node) {
|
|
254
|
+
const raw = node.behaviourProps;
|
|
255
|
+
if (!raw || typeof raw !== "object")
|
|
256
|
+
return {};
|
|
257
|
+
const out = {};
|
|
258
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
259
|
+
if (Object.keys(out).length >= 12)
|
|
260
|
+
break;
|
|
261
|
+
if (typeof value !== "string" || !key)
|
|
262
|
+
continue;
|
|
263
|
+
out[key] = value.slice(0, 200);
|
|
264
|
+
}
|
|
265
|
+
return Object.keys(out).length > 0 ? { behaviourProps: out } : {};
|
|
266
|
+
}
|
|
240
267
|
export function anatomyFromSketch(sketch, defines,
|
|
241
268
|
/**
|
|
242
269
|
* Their manifest: a package name → the range they pinned. This is what tells
|
|
@@ -335,6 +362,7 @@ versionOf) {
|
|
|
335
362
|
as: held.length > 0 ? "stack" : "text",
|
|
336
363
|
from: String(node.from ?? tag),
|
|
337
364
|
refName: tag,
|
|
365
|
+
...behaviour(node),
|
|
338
366
|
/** Its own index, so its guard and its props are still reachable - see `at`. */
|
|
339
367
|
at: index,
|
|
340
368
|
...(held.length > 0 ? { children: held } : {}),
|
|
@@ -352,6 +380,8 @@ versionOf) {
|
|
|
352
380
|
{
|
|
353
381
|
as: "component",
|
|
354
382
|
ref: tag,
|
|
383
|
+
/** Como ELE configurou o próprio componente é receita igual - ver `behaviour`. */
|
|
384
|
+
...behaviour(node),
|
|
355
385
|
/**
|
|
356
386
|
* THE INDEX IS THE NODE'S IDENTITY ON THE SKETCH, not a pointer to its classes.
|
|
357
387
|
*
|
|
@@ -380,6 +410,16 @@ versionOf) {
|
|
|
380
410
|
{
|
|
381
411
|
as: "external",
|
|
382
412
|
from: String(node.from ?? tag),
|
|
413
|
+
/**
|
|
414
|
+
* QUAL PEÇA, e não só de qual pacote - o ramo headless já dizia isto e o opaco não.
|
|
415
|
+
*
|
|
416
|
+
* `Collapsible.Root` e `Collapsible.Trigger` vêm do mesmo especificador e não são a mesma
|
|
417
|
+
* coisa: quem gera código a partir da receita tem de saber qual das duas escrever. Medido
|
|
418
|
+
* no repo do dono em 09/08: dos 119 nós de fronteira, 42 diziam a peça e 77 diziam apenas
|
|
419
|
+
* o pacote - e os 77 eram exatamente este ramo.
|
|
420
|
+
*/
|
|
421
|
+
refName: tag,
|
|
422
|
+
...behaviour(node),
|
|
383
423
|
at: index,
|
|
384
424
|
...(children.length > 0 ? { children: descend() } : {}),
|
|
385
425
|
},
|
|
@@ -513,8 +553,14 @@ versionOf) {
|
|
|
513
553
|
* props are copied into the snippet and can never fire, because the element unmounts
|
|
514
554
|
* before anything animates. Headless-only dropped it (dono, 04/08).
|
|
515
555
|
*/
|
|
556
|
+
/**
|
|
557
|
+
* Uma primitiva headless DESENHADA continua sendo daquela biblioteca, e as props do ponto de
|
|
558
|
+
* chamada são o que dizem se ela é controlada. Medido em 09/08: `Collapsible.Root`,
|
|
559
|
+
* `Popover.Root`, `Switch.Root`, `BaseDialog.Root` e `Field.Root` chegavam aqui e perdiam
|
|
560
|
+
* `open`/`onOpenChange`/`checked` - 11 dos 15 nós que a leitura media.
|
|
561
|
+
*/
|
|
516
562
|
const fromLib = (kind === "headless" || kind === "motion") && node.from
|
|
517
|
-
? { from: String(node.from), refName: tag }
|
|
563
|
+
? { from: String(node.from), refName: tag, ...behaviour(node) }
|
|
518
564
|
: {};
|
|
519
565
|
const built = {
|
|
520
566
|
as,
|
package/dist/commands/import.js
CHANGED
|
@@ -32,7 +32,7 @@ import { MUI_DEFAULT_SPACING, readStyledComponents, spacingOf, } from "../doctor
|
|
|
32
32
|
import { buildTable } from "../doctor/tokens.js";
|
|
33
33
|
import { definitionSpan, readInlineStyle, rootClasses, rootTag, transcribe, } from "../doctor/transcribe.js";
|
|
34
34
|
import { transcribeVariants } from "../doctor/variant-read.js";
|
|
35
|
-
import { frontierKind } from "../frontier-kind.js";
|
|
35
|
+
import { frontierKind, packageRoot } from "../frontier-kind.js";
|
|
36
36
|
import { withLibraryStructure } from "../library-structure.js";
|
|
37
37
|
import { body, paint, section } from "../output.js";
|
|
38
38
|
import { phase, startProgress } from "../progress.js";
|
|
@@ -2478,7 +2478,17 @@ export async function resolveReadParts(census, root) {
|
|
|
2478
2478
|
.filter((lib) => !resolved.external.some((e) => e.from === lib.from))
|
|
2479
2479
|
.map((lib) => ({
|
|
2480
2480
|
from: lib.from,
|
|
2481
|
-
|
|
2481
|
+
/**
|
|
2482
|
+
* PELA RAIZ DO PACOTE, e não pelo especificador inteiro.
|
|
2483
|
+
*
|
|
2484
|
+
* O `package.json` declara `@base-ui/react`; o código importa
|
|
2485
|
+
* `@base-ui/react/collapsible`. Consultar o manifesto com o segundo acerta só quando os
|
|
2486
|
+
* dois coincidem - medido no repo do dono em 09/08, 95 de 119 nós de fronteira chegavam
|
|
2487
|
+
* sem versão, e uma receita de biblioteca headless sem versão é um palpite datado.
|
|
2488
|
+
*/
|
|
2489
|
+
...(pinned[packageRoot(lib.from)]
|
|
2490
|
+
? { version: pinned[packageRoot(lib.from)] }
|
|
2491
|
+
: {}),
|
|
2482
2492
|
/**
|
|
2483
2493
|
* EVERY KIND, not the two the rule sentence happened to need.
|
|
2484
2494
|
*
|
package/dist/frontier-kind.js
CHANGED
|
@@ -118,7 +118,14 @@ export function frontierKind(pkg) {
|
|
|
118
118
|
return "opaque";
|
|
119
119
|
}
|
|
120
120
|
/** `@scope/name/sub` → `@scope/name`; `name/sub` → `name`. */
|
|
121
|
-
|
|
121
|
+
/**
|
|
122
|
+
* O NOME DO PACOTE, do especificador inteiro: `@base-ui/react/collapsible` -> `@base-ui/react`.
|
|
123
|
+
*
|
|
124
|
+
* Exportado em 09/08 porque o carimbo de VERSÃO consultava o manifesto com o especificador cru, e o
|
|
125
|
+
* `package.json` declara a raiz. Resultado medido no repo do dono: 95 de 119 nós de fronteira
|
|
126
|
+
* chegavam sem versão, e os 24 que chegavam eram justamente os que importam da raiz.
|
|
127
|
+
*/
|
|
128
|
+
export function packageRoot(pkg) {
|
|
122
129
|
const parts = pkg.split("/");
|
|
123
130
|
return pkg.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
|
|
124
131
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
3
4
|
import { absorb } from "./commands/absorb.js";
|
|
4
5
|
import { add } from "./commands/add.js";
|
|
5
6
|
import { adopt } from "./commands/adopt.js";
|
|
@@ -26,6 +27,7 @@ import { sync } from "./commands/sync.js";
|
|
|
26
27
|
import { template } from "./commands/template.js";
|
|
27
28
|
import { upgrade } from "./commands/upgrade.js";
|
|
28
29
|
import { use } from "./commands/use.js";
|
|
30
|
+
import { appendEvent } from "./doctor/ledger.js";
|
|
29
31
|
import { RegistryError } from "./registry.js";
|
|
30
32
|
/** Our own version, for pinning the hook and MCP commands we write into a
|
|
31
33
|
* project. Read from the package we are running out of, so a pinned command
|
|
@@ -602,7 +604,107 @@ async function main() {
|
|
|
602
604
|
process.exitCode = 1;
|
|
603
605
|
}
|
|
604
606
|
}
|
|
605
|
-
|
|
607
|
+
/**
|
|
608
|
+
* TODA EXECUÇÃO DEIXA UMA LINHA - e ela é escrita AQUI, nunca dentro de um comando.
|
|
609
|
+
*
|
|
610
|
+
* Metade dos defeitos de 09/08 era comportamento da ferramenta: um `upgrade` que rematerializava sem
|
|
611
|
+
* medir, uma barra que entupia o clipboard, um aviso de versão invertido. Nenhum virava número em
|
|
612
|
+
* lugar nenhum, e os três foram achados pelo dono lendo a saída no terminal. A tela de causas era
|
|
613
|
+
* sólida sobre o que a gente PRODUZ e cega sobre o que a ferramenta FAZ.
|
|
614
|
+
*
|
|
615
|
+
* NO PONTO DE ENTRADA, por uma razão que já custou caro duas vezes hoje: um comando pode esquecer de
|
|
616
|
+
* se registrar, e um caminho novo dentro dele nasce esquecido. A porta não esquece.
|
|
617
|
+
*
|
|
618
|
+
* E NO `finally`, porque a execução que mais importa registrar é a que NÃO terminou. Uma queda que
|
|
619
|
+
* não deixa rastro é exatamente "perder uma entrada".
|
|
620
|
+
*
|
|
621
|
+
* O registro nunca derruba o comando nem muda o código de saída: ele é a nossa ferramenta de
|
|
622
|
+
* diagnóstico, e perder o trabalho de alguém para gravar uma linha dela seria a pior troca da esteira.
|
|
623
|
+
*/
|
|
624
|
+
/**
|
|
625
|
+
* A MENSAGEM SEM O DISCO DELE - o que este registro pode carregar, e o que ele nunca pode.
|
|
626
|
+
*
|
|
627
|
+
* O spec que executa o CLI pegou o vazamento na primeira rodada: a mensagem de erro trazia o
|
|
628
|
+
* argumento digitado, e mensagens de Node trazem caminho absoluto o tempo todo (`ENOENT: … open
|
|
629
|
+
* '/home/alguem/projeto/src/App.tsx'`). Um registro que carrega o caminho do projeto de alguém é um
|
|
630
|
+
* registro que ninguém deixa ligado - e aí a esteira perde o sinal inteiro, não só a linha.
|
|
631
|
+
*
|
|
632
|
+
* O que sobra é a FORMA do erro, que é o que dá para consertar a partir daqui. Caminho vira `<path>`;
|
|
633
|
+
* o resto passa.
|
|
634
|
+
*/
|
|
635
|
+
function scrub(message) {
|
|
636
|
+
return message
|
|
637
|
+
.replace(/[A-Za-z]:\\[^\s'"`)]+/g, "<path>")
|
|
638
|
+
.replace(/(?:\.{0,2}\/)[^\s'"`)]{2,}/g, "<path>")
|
|
639
|
+
.trim();
|
|
640
|
+
}
|
|
641
|
+
async function recordRun(command, started, ok, error, bytes) {
|
|
642
|
+
try {
|
|
643
|
+
const root = resolve(typeof parseFlags(process.argv.slice(2)).flags.dir === "string"
|
|
644
|
+
? parseFlags(process.argv.slice(2)).flags.dir
|
|
645
|
+
: process.cwd());
|
|
646
|
+
const message = scrub(error instanceof Error ? error.message : error ? String(error) : "");
|
|
647
|
+
await appendEvent(root, {
|
|
648
|
+
kind: "run",
|
|
649
|
+
at: new Date().toISOString(),
|
|
650
|
+
command,
|
|
651
|
+
ok,
|
|
652
|
+
ms: Date.now() - started,
|
|
653
|
+
/** Só a primeira linha, cortada: uma stack carrega caminhos do projeto de alguém. */
|
|
654
|
+
...(message ? { error: message.split("\n")[0].slice(0, 200) } : {}),
|
|
655
|
+
...(typeof bytes === "number" && bytes > 0 ? { bytes } : {}),
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
catch {
|
|
659
|
+
/** Sem ledger, sem `_synthesisui`, disco cheio: nada disso é um comando que falhou. */
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* QUANTO O COMANDO ESCREVEU NA TELA - o segundo defeito de 09/08, e ele É medível.
|
|
664
|
+
*
|
|
665
|
+
* A barra de progresso se redesenha com `\r`: na tela é uma linha, no clipboard são todos os quadros.
|
|
666
|
+
* O dono tentou copiar a saída de um `sync` para mostrar um problema e levou junto 2 172 quadros -
|
|
667
|
+
* 195 879 bytes -, e o relatório que ele queria ficou enterrado. Aquilo foi achado por ele tentando
|
|
668
|
+
* copiar; não havia número em lugar nenhum.
|
|
669
|
+
*
|
|
670
|
+
* Contar bytes é a coisa mais simples que pega essa família inteira: uma saída de 200 KB não é uma
|
|
671
|
+
* saída, é um despejo, e a diferença entre 12 KB e 200 KB não depende de ninguém perceber.
|
|
672
|
+
*
|
|
673
|
+
* O contador NÃO altera a saída - ele soma e delega. Um wrapper que muda o que é impresso seria uma
|
|
674
|
+
* ferramenta mentindo sobre si mesma no exato lugar onde a gente foi conferir a verdade.
|
|
675
|
+
*/
|
|
676
|
+
function countingStdout() {
|
|
677
|
+
const original = process.stdout.write.bind(process.stdout);
|
|
678
|
+
let bytes = 0;
|
|
679
|
+
process.stdout.write = ((chunk, ...rest) => {
|
|
680
|
+
bytes +=
|
|
681
|
+
typeof chunk === "string"
|
|
682
|
+
? Buffer.byteLength(chunk)
|
|
683
|
+
: Buffer.isBuffer(chunk)
|
|
684
|
+
? chunk.length
|
|
685
|
+
: 0;
|
|
686
|
+
return original(chunk, ...rest);
|
|
687
|
+
});
|
|
688
|
+
return () => bytes;
|
|
689
|
+
}
|
|
690
|
+
async function guarded() {
|
|
691
|
+
const started = Date.now();
|
|
692
|
+
const command = parseFlags(process.argv.slice(2)).positionals[0] ?? "";
|
|
693
|
+
/** `help` e a invocação vazia não são trabalho sobre um projeto, e registrá-las seria só ruído. */
|
|
694
|
+
const worth = command && command !== "help";
|
|
695
|
+
const written = worth ? countingStdout() : () => 0;
|
|
696
|
+
try {
|
|
697
|
+
await main();
|
|
698
|
+
if (worth)
|
|
699
|
+
await recordRun(command, started, process.exitCode !== 1, undefined, written());
|
|
700
|
+
}
|
|
701
|
+
catch (err) {
|
|
702
|
+
if (worth)
|
|
703
|
+
await recordRun(command, started, false, err, written());
|
|
704
|
+
throw err;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
guarded().catch((err) => {
|
|
606
708
|
if (err instanceof RegistryError) {
|
|
607
709
|
console.error(`error: ${err.message}`);
|
|
608
710
|
}
|
package/dist/repo-state.js
CHANGED
|
@@ -39,7 +39,24 @@ export async function repoStateOf(projectRoot, slug, cli) {
|
|
|
39
39
|
* própria aqui seria uma segunda implementação do formato do registro - e a primeira coisa que
|
|
40
40
|
* ela faria é discordar da outra sobre o que é uma linha válida.
|
|
41
41
|
*/
|
|
42
|
-
const
|
|
42
|
+
const events = await readEvents(projectRoot);
|
|
43
|
+
/**
|
|
44
|
+
* AS ÚLTIMAS 20, e a janela é deliberada: o que interessa é a sessão de agora, não o histórico.
|
|
45
|
+
* Uma pessoa que roda vinte comandos sem sincronizar está no meio de um problema, e é dessa vintena
|
|
46
|
+
* que sai a resposta.
|
|
47
|
+
*/
|
|
48
|
+
const runs = events
|
|
49
|
+
.filter((e) => e.kind === "run" && e.command && e.at)
|
|
50
|
+
.slice(-20)
|
|
51
|
+
.map((e) => ({
|
|
52
|
+
command: String(e.command),
|
|
53
|
+
at: String(e.at),
|
|
54
|
+
...(e.ok === false ? { ok: false } : {}),
|
|
55
|
+
...(typeof e.ms === "number" ? { ms: Math.round(e.ms) } : {}),
|
|
56
|
+
...(e.error ? { error: String(e.error).slice(0, 200) } : {}),
|
|
57
|
+
...(typeof e.bytes === "number" ? { bytes: Math.round(e.bytes) } : {}),
|
|
58
|
+
}));
|
|
59
|
+
const doctor = events
|
|
43
60
|
.filter((e) => e.kind === "doctor" && e.at)
|
|
44
61
|
.sort((a, b) => String(b.at).localeCompare(String(a.at)))[0];
|
|
45
62
|
const payload = {
|
|
@@ -56,6 +73,7 @@ export async function repoStateOf(projectRoot, slug, cli) {
|
|
|
56
73
|
...(doctor?.matched && doctor.matched.length > 0
|
|
57
74
|
? { matched: doctor.matched }
|
|
58
75
|
: {}),
|
|
76
|
+
...(runs.length > 0 ? { runs } : {}),
|
|
59
77
|
...(typeof lock?.fetchedAt === "string"
|
|
60
78
|
? { installedAt: lock.fetchedAt }
|
|
61
79
|
: {}),
|
package/package.json
CHANGED