synthesisui 0.16.377 → 0.16.379

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.
@@ -154,10 +154,55 @@ opts = {}) {
154
154
  * medido em 11/08, 12 de 36 componentes têm árvore diferente entre o leitor de 07/08 e o de hoje.
155
155
  */
156
156
  const measuredBy = await censusMeasuredBy(root);
157
- if (behind(measuredBy, cli, READER_SINCE))
157
+ /**
158
+ * A RÉGUA QUE DIZ "VOCÊ ESTÁ ATRÁS" MORA DENTRO DA VERSÃO QUE ESTÁ ATRÁS - e por isso o silêncio
159
+ * daqui nunca significou que estava tudo em dia.
160
+ *
161
+ * O QUE ACONTECIA, medido em 05/09 no repositório do dono: o `connect` prega a versão do dia em
162
+ * que rodou, e os hooks dele chamam `synthesisui@0.16.370` LITERAL. Abrindo o tarball publicado
163
+ * daquela versão, `READER_SINCE = "0.16.367"`. O censo dele foi medido pela 0.16.376, e o leitor
164
+ * de hoje mudou na 0.16.378. Ele ESTÁ atrás - e a comparação acima, feita com a régua congelada de
165
+ * 0.16.367, responde que não: `isOlderCli("0.16.376", "0.16.367")` é falso, e este check cala.
166
+ *
167
+ * É a armadilha inteira: quem prega uma versão passa a medir a própria defasagem com a régua do
168
+ * dia em que pregou. O aviso só existe na versão que ele não tem, e um check que cala lê como
169
+ * "está em dia" - "ausência de evidência não é evidência", agora com a nossa assinatura.
170
+ *
171
+ * O SINAL QUE FUNCIONA OFFLINE, e é o que fecha a porta em vez de vigiá-la: comparar as duas
172
+ * coisas que este processo JÁ conhece. Se o CLI que está rodando é mais velho que o CLI que
173
+ * MEDIU, então ele está julgando uma medição feita por uma versão que sabe mais do que ele - e
174
+ * nada que ele afirme sobre estar em dia pode ser verdade. Ele não precisa saber o que veio
175
+ * depois; ele só precisa saber que veio.
176
+ *
177
+ * É GERAL e não depende de rede: o `align` não faz uma única chamada HTTP, de propósito, e
178
+ * continua sem fazer. As duas versões estão em disco - uma é a que está executando, a outra está
179
+ * carimbada no censo.
180
+ */
181
+ if (measuredBy && cli && isOlderCli(cli, measuredBy))
182
+ out.push({
183
+ /**
184
+ * A FRASE NÃO CITA UMA FRASE QUE A TELA NÃO IMPRIME.
185
+ *
186
+ * A primeira redação dizia que o *"you are up to date"* deste check não é uma resposta - e
187
+ * este comando nunca escreve essa linha. Ele CALA quando não encontra desalinho, e é o
188
+ * silêncio que era falso. Citar uma frase inexistente manda a pessoa procurar na tela algo
189
+ * que não está lá, que é o defeito de 08/08 outra vez em escala menor.
190
+ */
191
+ says: `this check is running on CLI ${cli}, and the measurement in this repo was read by ${measuredBy} - a newer one. It cannot see what changed in the readers after ${cli}, so its silence about the measurement being current is not an answer. ${cli} is the version pinned the day \`connect\` last ran here.`,
192
+ run: "npx synthesisui@latest connect",
193
+ });
194
+ else if (behind(measuredBy, cli, READER_SINCE))
158
195
  out.push({
159
196
  says: `the measurement stored in this repo was read by an older reader, so what the platform knows about your components is what that reader could see. A re-measure is the only thing that reaches it - the fix lives on this machine, not on the server.`,
160
- run: "npx synthesisui sync",
197
+ /**
198
+ * `connect` VEM PRIMEIRO, e a plataforma já dizia isso enquanto esta linha dizia outra coisa.
199
+ *
200
+ * `readerBehind` em `apps/web/src/lib/ds/reader-behind.ts` devolve os DOIS passos, com o
201
+ * motivo escrito: sem o `connect`, o `sync` pode medir com o leitor velho, reenviar o mesmo
202
+ * censo, e ensinar que sincronizar não adianta - "o pior resultado possível de todos". Esta
203
+ * linha mandava só `sync`, e é ela que roda na máquina dele a cada sessão.
204
+ */
205
+ run: "npx synthesisui@latest connect && npx synthesisui sync",
161
206
  });
162
207
  /**
163
208
  * O ESCOPO, que é o desalinho mais caro e o mais silencioso: sem ele o `sync` mede o repo inteiro
@@ -890,7 +890,19 @@ export async function doctor(opts) {
890
890
  * "not drift" era a nossa palavra para o motivo, e ela não diz o motivo. As duas razões reais
891
891
  * cabem na linha: ou nenhum token poderia segurar aquele valor, ou ele já vem de um.
892
892
  */
893
- console.log(body(`${plural(asideTotal, "value")} left out of the count - a token could never hold them, or they already come from one` +
893
+ /**
894
+ * E EM 05/09 ELAS VIRARAM TRÊS, senão esta linha volta a esconder metade.
895
+ *
896
+ * Os dois motivos novos não cabem em nenhum dos dois antigos: um token PODERIA segurar aquele
897
+ * valor, e é justamente por isso que escrevê-lo ali destrói. Uma cor dentro do e-mail que o app
898
+ * monta como texto, ou dentro de um hex escrito numa frase da página, não é pintada pela folha
899
+ * do app - `var()` não resolve num cliente de e-mail, e uma frase não é pintura nenhuma.
900
+ *
901
+ * Esta é a linha que o cliente lê SEM `--verbose`, e ela é o resumo inteiro para quem não pede
902
+ * detalhe. Sem a terceira metade, 61 valores no repositório dele saem da conta sob um motivo
903
+ * que não é o deles.
904
+ */
905
+ console.log(body(`${plural(asideTotal, "value")} left out of the count - a token could never hold them, they already come from one, or the page never paints them` +
894
906
  (skippedProjects.length > 0
895
907
  ? `, and ${plural(skippedProjects.length, "nested project")} that speak their own systems`
896
908
  : "") +
@@ -269,6 +269,97 @@ function fallbackSpans(line, table) {
269
269
  }
270
270
  return out;
271
271
  }
272
+ /**
273
+ * ONDE A CASCATA DO APP NÃO ALCANÇA - e trocar por `var()` ali APAGA a cor.
274
+ *
275
+ * O QUE ACONTECIA: `apps/web/lib/email.ts` monta o e-mail de login como TEXTO, e o `--fix` trocou
276
+ * `background:#6366f1` por `background:var(--color-brand-indigo)` no botão "Sign in". Aquele HTML é
277
+ * entregue a um cliente de e-mail, onde o `:root` do app não existe: a custom property não resolve,
278
+ * a declaração fica inválida, e o `background` volta para `transparent`. O botão de entrar some
279
+ * sobre um card quase preto - e é a PRIMEIRA tela que um usuário novo vê.
280
+ *
281
+ * É A MESMA FAMÍLIA DE `RENDERS_IMAGE`, que já recusa o arquivo que desenha para uma imagem pelo
282
+ * mesmo motivo declarado: "where CSS variables do not exist". Faltava a metade que sai como HTML.
283
+ *
284
+ * O SINAL É DA LINGUAGEM, NÃO DO REPOSITÓRIO: em `.ts`/`.tsx`/`.js`/`.jsx`, `style="..."` com uma
285
+ * declaração dentro NÃO é JSX - o React tipa `style` como objeto (`style={{}}`). Um atributo HTML
286
+ * escrito com aspas só existe ali dentro de uma string, isto é, marcação montada como texto: um
287
+ * e-mail, um PDF, um `srcdoc`, um sanitizador. Nenhum deles herda a folha do app.
288
+ *
289
+ * `.vue` e `.svelte` ficam FORA de propósito: neles `style="..."` é atributo de template real, o
290
+ * elemento é do app, e a cascata alcança. Recusar ali cegaria o produto para dois frameworks
291
+ * inteiros.
292
+ *
293
+ * MEDIDO em 05/09, em 4 escopos: `codelevel-monorepo` 7 linhas (`apps/web/lib/email.ts`, 2 delas já
294
+ * trocadas pelo `--fix` no repositório dele) · `frontend-hub/packages/ui` 3 linhas
295
+ * (`SignalUI/.../Sanitize.ts`, que remonta HTML de editor de texto) · `synthesisui-hub/apps/web`
296
+ * 18 linhas (`src/lib/email.ts` - o nosso próprio produto tem a forma) · `frontend-hub/apps/web-
297
+ * dashboard` 0. Três de quatro: a forma é geral, não é peculiaridade de um repositório.
298
+ */
299
+ const HTML_STYLE_ATTR = /style\s*=\s*"([^"]*)"/g;
300
+ /** Só onde `style="..."` não pode ser um elemento do app - ver `HTML_STYLE_ATTR`. */
301
+ const ASSEMBLES_MARKUP = /\.(?:tsx?|jsx?|mjs|cjs)$/i;
302
+ function markupTextSpans(file, line) {
303
+ if (!ASSEMBLES_MARKUP.test(file))
304
+ return [];
305
+ const out = [];
306
+ for (const m of line.matchAll(HTML_STYLE_ATTR)) {
307
+ if (!m[1].includes(":"))
308
+ continue;
309
+ const start = (m.index ?? 0) + m[0].indexOf('"') + 1;
310
+ out.push([start, start + m[1].length]);
311
+ }
312
+ return out;
313
+ }
314
+ /**
315
+ * O QUE A PÁGINA DIZ NÃO É O QUE ELA PINTA - e o `--fix` reescreveu uma frase.
316
+ *
317
+ * O QUE ACONTECIA: a landing dele demonstra a própria ferramenta num terminal falso, e a frase era
318
+ * `#6b6b78 is already --color-ink-500`. O scanner viu um hex, o sistema nomeia aquele valor, e a
319
+ * troca deixou `var(--color-ink-500) is already --color-ink-500` - uma tautologia, na frase que
320
+ * anuncia o que o produto faz. Não foi estilo mudado: foi CONTEÚDO.
321
+ *
322
+ * A LEI JÁ EXISTIA e esta é a borda dela - `fix` muda VALOR, nunca FORMA. Um literal em posição de
323
+ * conteúdo não é nem valor nem forma: é texto que uma pessoa lê.
324
+ *
325
+ * A REGIÃO É DERIVADA DA MARCAÇÃO, não de nome de arquivo: entre um `>` e o `<` seguinte está o que
326
+ * o elemento mostra. E dentro dela o literal ainda precisa estar CRU - `{x > 1 ? "#fff" : "#000"}`
327
+ * põe valores numa região que se parece com conteúdo, e as aspas são o que os separa de uma frase.
328
+ *
329
+ * UMA EXPRESSÃO NÃO É UMA FRASE, e esta borda custou um achado real. `<style>{`...`}</style>` é a
330
+ * folha que o app injeta de verdade: a cascata ALCANÇA, `var()` resolve, e o `120ms` escrito ali é
331
+ * deriva como qualquer outra. Medido em `apps/web/src/components/dashboard/live-specimen.tsx`, o
332
+ * guard sem esta borda derrubava 24 achados para 23. O sinal é da linguagem: uma chave logo depois
333
+ * do `>` abre uma expressão JSX, e o que vem dentro dela é código.
334
+ *
335
+ * `<style>html,body{...}</style>` DENTRO DE UMA STRING continua sendo conteúdo - lá a chave não
336
+ * encosta no `>`, e o documento é outro (um `srcdoc` de iframe, medido no `frontend-hub`), onde a
337
+ * folha do app também não chega.
338
+ *
339
+ * MEDIDO em 05/09, em 4 escopos: `codelevel-monorepo` 1 (a frase da landing) · `frontend-hub/apps/
340
+ * web-dashboard` 1 (o CSS do iframe) · `synthesisui-hub/apps/web` 14, entre elas a nossa PRÓPRIA
341
+ * demonstração na home, `<Line tone="ok">#2563eb -> --ds-color-semantic-info</Line>` · `frontend-
342
+ * hub/packages/ui` 0. O preço de errar é o produto reescrevendo a página de vendas de quem o
343
+ * instalou - inclusive a nossa.
344
+ */
345
+ function contentSpans(line) {
346
+ const out = [];
347
+ let open = line.indexOf(">");
348
+ while (open !== -1) {
349
+ const close = line.indexOf("<", open + 1);
350
+ if (close === -1)
351
+ break;
352
+ if (close > open + 1 && line[open + 1] !== "{")
353
+ out.push([open + 1, close]);
354
+ open = line.indexOf(">", close + 1);
355
+ }
356
+ return out;
357
+ }
358
+ /** Um valor entre aspas é um valor, mesmo numa região de conteúdo. */
359
+ function quotedAt(line, col, len) {
360
+ const q = /["'`]/;
361
+ return q.test(line[col - 1] ?? "") || q.test(line[col + len] ?? "");
362
+ }
272
363
  /**
273
364
  * The line that DEFINES a token is not a line that drifted from it.
274
365
  *
@@ -403,6 +494,12 @@ function scanCore(file, source, table) {
403
494
  // shadow) is one decision, so it is reported once.
404
495
  const spans = fallbackSpans(line, table);
405
496
  const inFallback = (at) => spans.some(([a, b]) => at >= a && at <= b);
497
+ /** Ver `markupTextSpans`: HTML montado como texto sai do alcance da folha do app. */
498
+ const markup = markupTextSpans(file, line);
499
+ const inMarkup = (at) => markup.some(([a, b]) => at >= a && at <= b);
500
+ /** Ver `contentSpans`: entre `>` e `<` está o que o elemento MOSTRA. */
501
+ const content = contentSpans(line);
502
+ const inContent = (at) => content.some(([a, b]) => at >= a && at <= b);
406
503
  const seen = new Set();
407
504
  // NOT named `at`: that is the line number in this scope, and shadowing it
408
505
  // put column positions into the report as line numbers.
@@ -413,6 +510,14 @@ function scanCore(file, source, table) {
413
510
  setAside("a token's own fallback");
414
511
  return;
415
512
  }
513
+ if (col >= 0 && inMarkup(col)) {
514
+ setAside("markup assembled as text, where the app's variables do not reach");
515
+ return;
516
+ }
517
+ if (col >= 0 && inContent(col) && !quotedAt(line, col, literal.length)) {
518
+ setAside("text the page shows, not a value it paints");
519
+ return;
520
+ }
416
521
  const key = `${kind}:${literal}`;
417
522
  if (seen.has(key))
418
523
  return;
@@ -475,7 +580,14 @@ function scanCore(file, source, table) {
475
580
  // A stack already reading from the system is the point, not a problem.
476
581
  if (stack.startsWith("var("))
477
582
  continue;
478
- push("font", stack);
583
+ /**
584
+ * A COLUNA VIAJA AQUI TAMBÉM - sem ela os guards por posição são cegos para uma família.
585
+ *
586
+ * `font` era o único `push` sem coluna, então um `font-family` dentro de um `style="..."` de
587
+ * e-mail passava pelos dois guards novos e pelo de fallback. Um valor não deixa de estar fora
588
+ * do alcance da folha por ser uma pilha de fontes.
589
+ */
590
+ push("font", stack, (m.index ?? 0) + m[0].indexOf(stack));
479
591
  }
480
592
  for (const m of line.matchAll(MOTION_PROP)) {
481
593
  const value = m[1];
@@ -242,7 +242,15 @@ export const MATERIALISER_SINCE = "0.16.370";
242
242
  * `line 12 oklch(...) -> --color-mint-500`. Um hook pinado antes desta versão aconselha OUTRA
243
243
  * coisa - nada -, que é a régua desta marca.
244
244
  */
245
- export const CHECKER_SINCE = "0.16.367";
245
+ /**
246
+ * 0.16.367 -> 0.16.378 em 05/09: o hook parava de cobrar onde o conselho DESTRÓI. Ele lê `scanSource`
247
+ * direto, então um hook pinado antes olha `style="background:#6366f1"` dentro do e-mail de login que
248
+ * o app monta como texto e manda escrever `var(--color-brand-indigo)` - no cliente de e-mail a
249
+ * custom property não resolve, a declaração fica inválida e o botão "Sign in" perde o fundo. E olha
250
+ * `#6b6b78 is already --color-ink-500` numa frase da landing e manda tokenizar TEXTO. Um hook
251
+ * pinado antes desta versão aconselha OUTRA coisa, que é a régua desta marca.
252
+ */
253
+ export const CHECKER_SINCE = "0.16.378";
246
254
  /**
247
255
  * A ÚLTIMA VERSÃO EM QUE OS LEITORES PASSARAM A PRODUZIR UM CENSO DIFERENTE.
248
256
  *
@@ -665,7 +673,14 @@ export const CHECKER_SINCE = "0.16.367";
665
673
  * `codelevel-ui` e 23 no `frontend-hub`, e ZERO no `observed` das três populações. Um censo tirado
666
674
  * antes disto não tem aqueles valores na régua, e só um `sync` os traz.
667
675
  */
668
- export const READER_SINCE = "0.16.367";
676
+ /**
677
+ * 0.16.367 -> 0.16.378 em 05/09: o censo produzido MUDA. O `import` roda `scanSource` sobre cada
678
+ * arquivo e passa os relatórios por `diagnose`, então duas formas que nunca foram valor de design
679
+ * saem do `observed`: marcação montada como texto (o e-mail, o sanitizador de editor) e literal em
680
+ * posição de conteúdo. Um censo medido antes desta versão conta como deriva o que a folha do app
681
+ * nem alcança - e o número do cliente é a promessa.
682
+ */
683
+ export const READER_SINCE = "0.16.378";
669
684
  /**
670
685
  * O QUE ESTÁ INSTALADO AQUI FICOU PARA TRÁS - e as DUAS condições que fazem isso ser verdade.
671
686
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.377",
3
+ "version": "0.16.379",
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": {