synthesisui 0.16.461 → 0.16.463

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.
@@ -1,5 +1,5 @@
1
1
  import { execFile } from "node:child_process";
2
- import { stat } from "node:fs/promises";
2
+ import { readdir, stat } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
4
  import { promisify } from "node:util";
5
5
  const run = promisify(execFile);
@@ -25,8 +25,44 @@ async function listed(root) {
25
25
  if (line.trim())
26
26
  out.push(line.trim());
27
27
  }
28
+ if (out.length === 0 && !(await isGitRepo(root)))
29
+ return walked(root);
28
30
  return [...new Set(out)];
29
31
  }
32
+ /**
33
+ * FORA DO GIT, A PASTA É A LISTA - rodada 3 do refinamento, 27/09. O leigo-3 nasceu sem repositório, e
34
+ * as duas perguntas ao git voltavam vazias: o fim do turno não checava arquivo nenhum, nunca - o ledger
35
+ * sem uma linha `hook`, o envio com `count: 0`, o dashboard inteiro do agente sem checagem. Sem git, a
36
+ * resposta é andar pela pasta; o filtro pelo relógio do turno (em `changedSince`) continua valendo.
37
+ */
38
+ const WALK_SKIP = new Set(["node_modules", ".next", "dist", "build", "out", "_synthesisui", "coverage"]);
39
+ const WALK_MAX = 5000;
40
+ async function isGitRepo(root) {
41
+ const { stdout } = await run("git", ["rev-parse", "--is-inside-work-tree"], { cwd: root }).catch(() => ({
42
+ stdout: "",
43
+ }));
44
+ return stdout.trim() === "true";
45
+ }
46
+ async function walked(root) {
47
+ const out = [];
48
+ const go = async (rel) => {
49
+ if (out.length >= WALK_MAX)
50
+ return;
51
+ for (const entry of await readdir(join(root, rel), { withFileTypes: true }).catch(() => [])) {
52
+ if (entry.name.startsWith(".") || WALK_SKIP.has(entry.name))
53
+ continue;
54
+ const next = rel ? `${rel}/${entry.name}` : entry.name;
55
+ if (entry.isDirectory())
56
+ await go(next);
57
+ else if (entry.isFile())
58
+ out.push(next);
59
+ if (out.length >= WALK_MAX)
60
+ return;
61
+ }
62
+ };
63
+ await go("");
64
+ return out;
65
+ }
30
66
  /**
31
67
  * OS ARQUIVOS INTERESSANTES QUE MUDARAM DESDE `since`, do mais recente para o mais antigo.
32
68
  *
@@ -199,7 +199,9 @@ export async function add(slug, opts) {
199
199
  * Falha calada de propósito: um repositório sem folha de estilo, ou sem build, devolve o que veio
200
200
  * do servidor. A folha nunca fica pior do que estava.
201
201
  */
202
- const theirVars = await pointTokensAtTheirNames(projectRoot, payload);
202
+ const theirVars = await pointTokensAtTheirNames(projectRoot, payload,
203
+ /** O que um build anterior provou - ver `provenBefore`. Sem build aqui, é a melhor prova que há. */
204
+ (prev?.tokenMap ?? []).map((p) => p.theirs));
203
205
  /**
204
206
  * O `theme.css` ALINHA ONDE HÁ O QUE ALINHAR - ver `onlyWhatMatched`.
205
207
  *
@@ -619,6 +621,12 @@ export async function add(slug, opts) {
619
621
  */
620
622
  if (theirVars.cycles > 0)
621
623
  console.log(` ${theirVars.cycles} ${theirVars.cycles === 1 ? "value" : "values"} already had the bridge pointing the other way, so ${theirVars.cycles === 1 ? "it was" : "they were"} left alone - your own utility still resolves through this system, and pointing back would have cancelled both.`);
624
+ /**
625
+ * SEM BUILD, A TELA DIZ DE ONDE VEIO A PROVA - 27/09. Antes, o mapa sumia e a única pista era "0
626
+ * Tailwind utilities now point at your own decisions"; o dono passou uma tarde sem saber por quê.
627
+ */
628
+ if (theirVars.provedBy === "before")
629
+ console.log(` No build found here (no CSS in .next, dist, build or out), so the ${theirVars.pairs.length} name${theirVars.pairs.length === 1 ? "" : "s"} an earlier build proved ${theirVars.pairs.length === 1 ? "was" : "were"} kept. Run your build and \`connect\` again to prove them on this machine.`);
622
630
  if (theirVars.pointed > 0)
623
631
  console.log(` ${theirVars.pointed} value${theirVars.pointed === 1 ? "" : "s"} in tokens.css now point at the name YOUR code already gives ${theirVars.pointed === 1 ? "it" : "them"} - change yours and the system follows${theirVars.pruned > 0 ? `; ${theirVars.pruned} matched but your build does not emit ${theirVars.pruned === 1 ? "that name" : "those names"}, so ${theirVars.pruned === 1 ? "it keeps" : "they keep"} the value` : ""}`);
624
632
  /**
@@ -1,3 +1,4 @@
1
+ import { armTurnClock } from "./hook.js";
1
2
  import { mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
2
3
  import { dirname, join } from "node:path";
3
4
  import { codexPinBefore, exists, wireAgent } from "../agent-wiring.js";
@@ -477,6 +478,9 @@ export async function connect(opts) {
477
478
  ? say("✓ what the check records is sent to your dashboard at the end of each turn - your system changes only from a commit on a clean main")
478
479
  : say("· what the check records stays on this machine - run `npx synthesisui sync` to send it"));
479
480
  }
481
+ /** O relógio do turno nasce aqui - ver `armTurnClock`: o primeiro turno depois do `connect` é checado. */
482
+ if (want.hook && wired.hook !== "skipped")
483
+ await armTurnClock(root);
480
484
  if (want.hook && wired.hook !== "skipped") {
481
485
  row(wired.hook !== "already there", (() => {
482
486
  return wired.hook === "added"
@@ -88,6 +88,17 @@ const startClock = (root) => writeFile(ledgerPath(root), "", { flag: "a" }).catc
88
88
  const TURN_CLOCK = "_synthesisui/.turn-clock";
89
89
  const turnClockOf = (root) => stat(join(root, TURN_CLOCK)).then((s) => s.mtimeMs, () => 0);
90
90
  const moveTurnClock = (root) => writeFile(join(root, TURN_CLOCK), "", "utf8").catch(() => { });
91
+ /**
92
+ * O RELÓGIO NASCE NO `connect`, E NÃO NO PRIMEIRO TURNO - rodada 3 do refinamento, 27/09.
93
+ *
94
+ * Sem relógio, o primeiro fim de turno só o acertava e calava (ver `startClock`). Isso protegia o
95
+ * repositório com trabalho antigo em aberto, e cobrava um preço que ninguém tinha medido: num projeto
96
+ * NOVO o primeiro turno é o trabalho inteiro. O dashboard do leigo-3 - cinco arquivos - e as telas das
97
+ * rodadas 1 e 2 nunca foram checados: o `ledger.jsonl` tinha zero linhas `hook` e o envio marcava
98
+ * `count: 0`. Armado na instalação, o relógio deixa de fora o que veio antes do `connect` (a intenção
99
+ * de antes) e vê tudo o que veio depois. `flag: "wx"`: um relógio que já existe não recua.
100
+ */
101
+ export const armTurnClock = (root) => writeFile(join(root, TURN_CLOCK), "", { encoding: "utf8", flag: "wx" }).catch(() => { });
91
102
  /**
92
103
  * QUANTOS ARQUIVOS UM COMANDO DE SHELL PODE FAZER O PRODUTO RELATAR DE UMA VEZ.
93
104
  *
@@ -367,12 +367,30 @@ function propsType(axes, tag, base, withAs = false) {
367
367
  const extras = axes.map((a) => a.boolean
368
368
  ? ` ${a.prop}?: boolean;`
369
369
  : ` ${a.prop}?: ${a.options.map((o) => `"${o}"`).join(" | ")};`);
370
- if (withAs)
371
- extras.push(" /** Render as a different element (th, dt, h2, li…) - the recipe is", " * styling only, so the document's semantics stay yours. */", " as?: ElementType;");
370
+ /**
371
+ * COM `as`, AS PROPS SEGUEM O ELEMENTO ESCOLHIDO - rodada 1 do refinamento, 27/09.
372
+ *
373
+ * O tipo era `ComponentProps<"button"> & { as?: ElementType }`: aceitava `as={Link}` e recusava o
374
+ * `href` que o `Link` exige - o `npm run build` do leigo quebrou em "Property 'href' does not exist",
375
+ * e ele contornou com `{...{ href }}`. Genérico no elemento, com o padrão sendo a tag da receita: sem
376
+ * `as`, nada muda para quem chama.
377
+ */
378
+ if (withAs) {
379
+ extras.push(" /** Render as a different element (th, dt, h2, li, a Link…) - the recipe is", " * styling only, so the document's semantics stay yours, and the props follow it. */", " as?: T;");
380
+ /**
381
+ * SOBREPOSIÇÃO, e não troca: o tipo da tag da receita continua escrito por inteiro, e o elemento
382
+ * escolhido só ACRESCENTA o que ele tem a mais (o `href` do `Link`). Sem `as`, o `Omit` é vazio e o
383
+ * tipo é o de antes - e quem lê a raiz no texto (`ComponentProps<"button">`) continua lendo.
384
+ */
385
+ const own = ["as", ...axes.map((a) => a.prop)].map((k) => `"${k}"`).join(" | ");
386
+ return `${base}<"${tag}"> & Omit<${base}<T>, keyof ${base}<"${tag}"> | ${own}> & {\n${extras.join("\n")}\n}`;
387
+ }
372
388
  if (extras.length === 0)
373
389
  return `${base}<"${tag}">`;
374
390
  return `${base}<"${tag}"> & {\n${extras.join("\n")}\n}`;
375
391
  }
392
+ /** O parâmetro de tipo que acompanha `propsType(..., withAs)` - vazio quando não há `as`. */
393
+ const asGeneric = (tag, withAs) => withAs ? `<T extends ElementType = "${tag}">` : "";
376
394
  /**
377
395
  * THE ELEMENT IS THE CALLER'S DECISION (1kro2o, 29/07).
378
396
  *
@@ -385,7 +403,7 @@ function propsType(axes, tag, base, withAs = false) {
385
403
  * offered takes the same attributes in practice - the pattern is the exact
386
404
  * one the field agent wrote by hand.
387
405
  */
388
- const asTagLine = (tag) => ` const Tag = (as ?? "${tag}") as "${tag}";`;
406
+ const asTagLine = (tag) => ` const Tag = (as ?? "${tag}") as ElementType;`;
389
407
  /** The render pieces for an element that offers `as`. `type="button"` must
390
408
  * follow the RENDERED tag - a Button rendered as an <a> with type="button"
391
409
  * is invalid HTML wearing a straight face. Void elements never offer `as`:
@@ -612,8 +630,22 @@ export class ThemeVocab extends Set {
612
630
  }
613
631
  const sameValue = (a, b) => a.trim().toLowerCase().replace(/\s+/g, " ") === b.trim().toLowerCase().replace(/\s+/g, " ");
614
632
  /** A chave de cor que ele escreve: pelo `ThemeVocab` quando há valores, pelo nome quando não. */
633
+ /**
634
+ * O DEGRAU DA PALETA PELO NOME - `{color.coral.700}` -> `coral-700` - rodada 3 do refinamento, 27/09.
635
+ *
636
+ * O badge do Zephyr pede degraus (`{color.coral.700}`, `{color.teal.700}`…), o projeto do leigo-3 declara
637
+ * `--color-coral-700` com o mesmo valor, e o badge saía com `#c2410c` cru: pelo nome só os papéis
638
+ * semânticos eram traduzidos, e pelo valor só quando UMA variável segura aquele valor - ali duas seguravam.
639
+ * Só vale pelo `ThemeVocab`, que confere o valor.
640
+ */
641
+ const paletteKey = (v) => {
642
+ const m = /^\{color\.([a-z0-9-]+)\.([a-z0-9-]+)\}$/.exec(v.trim());
643
+ if (!m || m[1] === "semantic" || m[1] === "series")
644
+ return null;
645
+ return `${m[1]}-${m[2]}`;
646
+ };
615
647
  const theirColorKey = (value, themeVars) => {
616
- const key = colorKey(value);
648
+ const key = colorKey(value) ?? (themeVars instanceof ThemeVocab ? paletteKey(value) : null);
617
649
  if (themeVars instanceof ThemeVocab)
618
650
  return themeVars.nameFor(value, "color", key);
619
651
  return key && minted(themeVars, `--color-${key}`) ? key : null;
@@ -1324,7 +1356,7 @@ localName = name) {
1324
1356
  ].join(", ");
1325
1357
  return `
1326
1358
  /** Part "${partName}" of ${comp}${formOf.get(partName) ? ` (${formOf.get(partName)})` : ""} - compose it inside <${comp}>. */
1327
- export function ${partComp}({ ${partDestructure} }: ${propsType(partAxes, partTag, props, partEl.offersAs)}) {
1359
+ export function ${partComp}${asGeneric(partTag, partEl.offersAs)}({ ${partDestructure} }: ${propsType(partAxes, partTag, props, partEl.offersAs)}) {
1328
1360
  ${partEl.setup} return (
1329
1361
  <${partEl.jsxTag}${partEl.jsxAttrs}
1330
1362
  className={${joinCls([`"${partClassName(name, partName, convention)}"`, "className"])}}
@@ -1339,10 +1371,10 @@ import "./${localName}.css";
1339
1371
 
1340
1372
  import type { ${needsElementType ? `ElementType, ${props}` : props} } from "react";
1341
1373
 
1342
- type ${comp}Props = ${propsType(axes, tag, props, el.offersAs)};
1374
+ type ${comp}Props${asGeneric(tag, el.offersAs)} = ${propsType(axes, tag, props, el.offersAs)};
1343
1375
 
1344
1376
  ${compositionHint(comp, name, recipe, voidEl)}
1345
- export function ${comp}({ ${destructure} }: ${comp}Props) {
1377
+ export function ${comp}${asGeneric(tag, el.offersAs)}({ ${destructure} }: ${comp}Props${el.offersAs ? "<T>" : ""}) {
1346
1378
  ${el.setup} return (
1347
1379
  ${rootJsx}
1348
1380
  );
@@ -1454,10 +1486,10 @@ import { cn } from "../cn";
1454
1486
  const BASE = ${JSON.stringify([needsGroup ? "group" : "", tailwindClassList(recipe, resolved, themeVars, excluded)].filter(Boolean).join(" "))};
1455
1487
  ${[...variantConsts, ...booleanConsts].join("\n")}
1456
1488
 
1457
- type ${comp}Props = ${propsType(axes, tag, props, el.offersAs)};
1489
+ type ${comp}Props${asGeneric(tag, el.offersAs)} = ${propsType(axes, tag, props, el.offersAs)};
1458
1490
 
1459
1491
  ${compositionHint(comp, name, recipe, voidEl)}
1460
- export function ${comp}({ ${destructure} }: ${comp}Props) {
1492
+ export function ${comp}${asGeneric(tag, el.offersAs)}({ ${destructure} }: ${comp}Props${el.offersAs ? "<T>" : ""}) {
1461
1493
  ${el.setup} return (
1462
1494
  ${hasShape
1463
1495
  ? `${rootOpen} {...props}
@@ -1525,7 +1557,7 @@ ${orderedParts
1525
1557
  ].join(", ");
1526
1558
  return `
1527
1559
  /** Part "${partName}" of ${comp}${formOf.get(partName) ? ` (${formOf.get(partName)})` : ""} - compose it inside <${comp}>. */
1528
- export function ${partComp}({ ${partDestructure} }: ${propsType(partAxes, partTag, props, partEl.offersAs)}) {
1560
+ export function ${partComp}${asGeneric(partTag, partEl.offersAs)}({ ${partDestructure} }: ${propsType(partAxes, partTag, props, partEl.offersAs)}) {
1529
1561
  ${partEl.setup} return (
1530
1562
  <${partEl.jsxTag}${partEl.jsxAttrs}
1531
1563
  className={${resolveCls([JSON.stringify(partCls), "className"])}}
package/dist/fonts.js CHANGED
@@ -248,9 +248,15 @@ export function nextFontSnippet(input) {
248
248
  * Nenhuma linha aqui menciona o sistema, o slug ou uma variável nossa: é a fonte dele, ligada ao
249
249
  * Tailwind dele.
250
250
  */
251
+ /**
252
+ * `@theme inline`, e não `@theme` - rodada 1 do refinamento, 27/09. O Tailwind escreve um `@theme`
253
+ * comum no `:root`, e a variável do `next/font` mora na classe do `<body>`: no `:root` ela não existe,
254
+ * e `font-display` caía na fonte genérica. `inline` faz a utility ler a variável no próprio elemento,
255
+ * que é o padrão do `create-next-app`.
256
+ */
251
257
  const css = [
252
258
  `/* ${appDir}/globals.css - in your own @theme */`,
253
- `@theme {`,
259
+ `@theme inline {`,
254
260
  ...emitted.map((role) => ` --font-${role}: var(${roleVar(role)});`),
255
261
  `}`,
256
262
  ];
package/dist/guarantee.js CHANGED
@@ -1,5 +1,5 @@
1
- import { readFile, writeFile } from "node:fs/promises";
2
- import { join } from "node:path";
1
+ import { readFile, stat, writeFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
3
  import { hasHook } from "./agent-wiring.js";
4
4
  import { ungovernedIn } from "./governed.js";
5
5
  /**
@@ -104,7 +104,14 @@ system) {
104
104
  * invisível: um arquivo novo nunca apagou nada, então a checagem cala com razão e a pessoa lê
105
105
  * esse silêncio como aprovação.
106
106
  */
107
- lines.push(plain("What a file no longer has is measured against your last commit. A file git has never seen has no before, so nothing is reported as removed there."));
107
+ /**
108
+ * SEM GIT, A METADE DO "ANTES" NÃO EXISTE - rodada 3 do refinamento, 27/09. O leigo-3 nasceu sem
109
+ * repositório (o `create-next-app` dele não criou um), e a frase de baixo falava de "your last commit"
110
+ * numa pasta que não tem commit nenhum. Dizer é o que transforma o silêncio em um passo.
111
+ */
112
+ lines.push(plain((await inGitRepo(root))
113
+ ? "What a file no longer has is measured against your last commit. A file git has never seen has no before, so nothing is reported as removed there."
114
+ : "This folder is not in a git repository, so nothing can be measured against a last commit - what a file lost goes unseen. `git init` and a first commit turn that on."));
108
115
  lines.push(system.utilities.size > 0
109
116
  ? {
110
117
  text: "A value counts as coming from your system in two spellings: `var(--token)`, and the {n} utility names your own theme generates - `bg-primary`, `p-6`, `rounded-lg`.",
@@ -169,3 +176,16 @@ export async function guaranteeIfNew(root, system) {
169
176
  await writeFile(join(root, SAID), `${answer.fingerprint}\n`, "utf8").catch(() => { });
170
177
  return answer.lines;
171
178
  }
179
+ /** Esta pasta, ou uma acima dela, tem `.git`? - um monorepo tem o `.git` na raiz, não no app. */
180
+ async function inGitRepo(root) {
181
+ let at = root;
182
+ for (let i = 0; i < 40; i += 1) {
183
+ if (await stat(join(at, ".git")).then(() => true, () => false))
184
+ return true;
185
+ const up = dirname(at);
186
+ if (up === at)
187
+ return false;
188
+ at = up;
189
+ }
190
+ return false;
191
+ }
package/dist/guide.js CHANGED
@@ -841,8 +841,9 @@ ${pageBaseSection}${pendingSection}${keyframeLines.length > 0
841
841
  ## Motion vocabulary
842
842
 
843
843
  The base is QUIET: nothing moves until someone asks. When they do, animation is **selection
844
- from this list**, not improvisation - these are the system's own animations, already compiled
845
- into ${hasTailwind && hasAnimateKeys ? "`theme.css` as ready utilities" : `\`tokens.css\` as \`@keyframes ds-${slug}-<name>\``}, each on the clock its intent calls for:
844
+ from this list**, not improvisation - these are the system's own animations, each on the clock its
845
+ intent calls for. They are written in ${hasTailwind && hasAnimateKeys ? "`theme.css`" : "`tokens.css`"} next to this guide, which your app does not load -
846
+ nothing of ours is imported. To use one, copy its \`@keyframes\` block${hasTailwind && hasAnimateKeys ? " and its `--animate-<name>` line into your own `@theme`, and the utility below starts to exist" : " into your own stylesheet, and write the `animation` below"}:
846
847
 
847
848
  ${keyframeLines.join("\n")}
848
849
  ${patternLines.length > 0
@@ -258,7 +258,18 @@
258
258
  * o nome dele pelo valor quando a receita pede um degrau - rodada 1 do refinamento. Um componente escrito
259
259
  * antes carrega o valor cru onde o projeto tem nome.
260
260
  */
261
- export const MATERIALISER_SINCE = "0.16.459";
261
+ /**
262
+ * 0.16.459 -> 0.16.462 em 27/09, e o passo 1 dá **SIM**: o `GUIDE.md` que cai na pasta passa a entregar
263
+ * as fontes num `@theme inline` (no `@theme` comum a fonte não carregava) e a dizer de onde vem cada
264
+ * animação, e os componentes com `as` passam a ter o tipo genérico - rodada 1 do refinamento. Uma pasta
265
+ * escrita antes ensina o agente a colar uma fonte que não carrega.
266
+ */
267
+ /**
268
+ * 0.16.462 -> 0.16.463 em 27/09, e o passo 1 dá **SIM**: o gerador que o `upgrade` usa passa a achar pelo
269
+ * nome os degraus da paleta que o projeto declara (`{color.coral.700}` -> `bg-coral-700`) - o badge do
270
+ * Zephyr saía com quatro cores cruas (rodada 3 do refinamento).
271
+ */
272
+ export const MATERIALISER_SINCE = "0.16.463";
262
273
  /**
263
274
  * A ÚLTIMA VERSÃO EM QUE O QUE O HOOK RODA MUDOU.
264
275
  *
@@ -414,7 +425,13 @@ export const COUNTED_DIFFERENTLY = "this run counts a value as named only when Y
414
425
  * hand" no `@theme` que o leigo copiou do GUIDE). Um hook fixado antes continua acusando o vocabulário do
415
426
  * projeto como valor à mão, em todo turno.
416
427
  */
417
- export const CHECKER_SINCE = "0.16.458";
428
+ /**
429
+ * 0.16.458 -> 0.16.463 em 27/09, e o passo 1 dá **SIM**: o fim do turno passa a checar o primeiro turno
430
+ * depois do `connect` (o relógio nasce na instalação) e a enxergar os arquivos de uma pasta sem git -
431
+ * rodada 3 do refinamento: o dashboard do leigo-3 inteiro nunca foi checado. Um hook anterior segue calado
432
+ * nos dois casos.
433
+ */
434
+ export const CHECKER_SINCE = "0.16.463";
418
435
  /**
419
436
  * A ÚLTIMA VERSÃO EM QUE OS LEITORES PASSARAM A PRODUZIR UM CENSO DIFERENTE.
420
437
  *
package/dist/registry.js CHANGED
@@ -53,7 +53,13 @@ export async function fetchDesignSystem(base, slug, version) {
53
53
  url.searchParams.set("version", String(version));
54
54
  const res = await request(url.toString());
55
55
  if (res.status === 404) {
56
- throw new RegistryError(`No design system published with slug "${slug}"${version != null ? ` at version v${version}` : ""}. Run \`synthesisui list\` to see what's available.`);
56
+ throw new RegistryError(
57
+ /**
58
+ * A CONTA É A PRIMEIRA SUSPEITA - rodada 3 do refinamento, 27/09. O fork `zephyr` do dono estava na
59
+ * conta do navegador, e o terminal, logado em outra: o registry só entrega um sistema que não é
60
+ * público à conta dona, e a frase dizia "no design system published". Ele parou ali.
61
+ */
62
+ `No design system "${slug}"${version != null ? ` at version v${version}` : ""} that this machine can see. If it is a system in your account, this machine may be signed in to another one - run \`npx synthesisui@latest login\` with the account that owns it. Otherwise, \`synthesisui list\` shows what is public.`);
57
63
  }
58
64
  if (!res.ok) {
59
65
  throw new RegistryError(`Registry responded ${res.status} while fetching "${slug}".`);
@@ -180,13 +180,14 @@ const FAMILY_KIND = {
180
180
  "--ds-motion-durations-": "motion",
181
181
  "--ds-motion-easings-": "motion",
182
182
  };
183
+ export async function pointTokensAtTheirNames(root, payload,
183
184
  /**
184
- * A JORNADA INTEIRA, num chamador só - varrer, medir o build, reescrever.
185
- *
186
- * Recebe o payload em vez do CSS solto porque um argumento opcional a menos é uma chamada pela
187
- * metade a menos: quem chama não tem como esquecer de passar a folha certa.
185
+ * Os nomes DELE que um build anterior provou - `theirs` do `tokenMap` do `.lock`. Usados só quando
186
+ * esta máquina não tem build: antes de 27/09, sem build o mapa saía vazio e o `connect` apagava 36
187
+ * pares provados no CodeLevel, sem dizer por quê. O cruzamento é refeito contra o CSS dele de hoje,
188
+ * então um nome que sumiu do código continua não casando.
188
189
  */
189
- export async function pointTokensAtTheirNames(root, payload) {
190
+ provenBefore = []) {
190
191
  const css = payload.artifacts["tokens.css"] ?? "";
191
192
  if (!css)
192
193
  return {
@@ -196,12 +197,20 @@ export async function pointTokensAtTheirNames(root, payload) {
196
197
  compiledAway: 0,
197
198
  cycles: 0,
198
199
  pairs: [],
200
+ provedBy: "nothing",
199
201
  };
200
- const [theirs, resolvable] = await Promise.all([
202
+ const [theirs, built] = await Promise.all([
201
203
  harvestTheirCss(root),
202
204
  resolvableVars(root),
203
205
  ]);
204
- return pointAtTheirNames(css, theirs, resolvable);
206
+ if (built)
207
+ return { ...pointAtTheirNames(css, theirs, built), provedBy: "build" };
208
+ if (provenBefore.length > 0)
209
+ return {
210
+ ...pointAtTheirNames(css, theirs, new Set(provenBefore)),
211
+ provedBy: "before",
212
+ };
213
+ return { ...pointAtTheirNames(css, theirs, null), provedBy: "nothing" };
205
214
  }
206
215
  /** As folhas de estilo DELE, e nenhuma da nossa pasta. Gêmeo de `harvestOwnTokens` no doctor. */
207
216
  async function harvestTheirCss(root) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.461",
3
+ "version": "0.16.463",
4
4
  "description": "Bring SynthesisUI design systems into any project - tokens, typed components, whole pages and an agent-ready CLAUDE.md manifest.",
5
5
  "type": "module",
6
6
  "bin": {