synthesisui 0.16.199 → 0.16.200

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.
@@ -172,6 +172,29 @@ async function sheetWithImports(path, seen = new Set(), depth = 0) {
172
172
  async function exists(path) {
173
173
  return await readFile(path, "utf8").then(() => true, () => false);
174
174
  }
175
+ /**
176
+ * SOB QUE NOME O ESCOPO É IMPORTADO - a única forma de reconhecer a linha da própria biblioteca.
177
+ *
178
+ * O app dele importa `@frontend-hub/ui`; o `packages/ui/package.json` declara esse nome. É a mesma
179
+ * regra que a lei 13 já usa para desempatar `workspace:` contra semver, um passo antes: o MANIFESTO
180
+ * responde, e não o nome do componente nem o formato do caminho.
181
+ *
182
+ * Ausente quando o escopo não é um pacote - e aí a evidência só é somada quando o nome chega de UMA
183
+ * origem interna, que é a mesma resposta por outro caminho.
184
+ */
185
+ async function publishedAs(root) {
186
+ const raw = await readFile(join(root, "package.json"), "utf8").catch(() => null);
187
+ if (!raw)
188
+ return null;
189
+ try {
190
+ const name = JSON.parse(raw).name;
191
+ return typeof name === "string" && name ? name : null;
192
+ }
193
+ catch {
194
+ /** Um manifesto ilegível custa a evidência daquele escopo, nunca a medição. */
195
+ return null;
196
+ }
197
+ }
175
198
  async function harvestOwnCss(root,
176
199
  /**
177
200
  * O LEDGER TAMBÉM COME AQUI, e por um motivo de promessa: este é o único laço que vê TODO
@@ -1065,7 +1088,18 @@ export async function takeCensus(root, opts) {
1065
1088
  */
1066
1089
  const turnedAway = new Set(skips.map((s) => s.name));
1067
1090
  const inventory = allComposed.filter((c) => !turnedAway.has(c.name));
1068
- const composed = new Set(inventory.map((c) => c.name));
1091
+ /**
1092
+ * COMPOSTO POR ELE, e não por qualquer um que use esse nome - a mesma lei, o terceiro sítio.
1093
+ *
1094
+ * Este conjunto responde "preciso criar uma linha a partir da DECLARAÇÃO, ou já existe uma linha
1095
+ * de uso?". Com uma linha por nome, "existe" era inequívoco. Com uma linha por nome+origem, um
1096
+ * `Toggle` do `@base-ui/react` respondia que sim - e o `Toggle` que ele declara em
1097
+ * `packages/ui/atoms` ficava SEM LINHA NENHUMA, que é o mesmo desaparecimento do test50 chegando
1098
+ * por outra porta.
1099
+ *
1100
+ * A linha de terceiro não responde por ele: só conta como composto o que veio de dentro.
1101
+ */
1102
+ const composed = new Set(inventory.filter((c) => !c.from).map((c) => c.name));
1069
1103
  const fromTypes = defined
1070
1104
  .filter((d) => !composed.has(d.name))
1071
1105
  .map((d) => ({
@@ -1160,8 +1194,64 @@ export async function takeCensus(root, opts) {
1160
1194
  * the laws read, and it is the reason this pass exists: three files agreeing is
1161
1195
  * what makes a habit, and a library has one file per component.
1162
1196
  */
1163
- const own = new Set(scoped.map((c) => c.name));
1164
- const evidence = new Map(usedThere.filter((c) => !c.from).map((c) => [c.name, c]));
1197
+ /**
1198
+ * A ORIGEM DECIDE, E AUSÊNCIA DE ORIGEM NÃO É UMA ORIGEM - lei 13, no segundo sítio.
1199
+ *
1200
+ * `tallyToInventory` já separa as linhas por nome+origem, e o censo mostra isso funcionando em 8
1201
+ * nomes (`Tooltip`, `Popover.*`, `AnimatePresence`, `Toggle`). Aqui a evidência voltava a casar por
1202
+ * NOME, e o filtro era `!c.from` - que junta três coisas diferentes numa só: o que é dele, o que
1203
+ * tem origem misturada, e o que não tem origem NENHUMA.
1204
+ *
1205
+ * O que isso produziu, medido no repo do dono em 10/08: o `Button` DELE publicava
1206
+ * `variant = ["ocean", "outlined", "contained"]`. `outlined` e `contained` são do MUI, e os
1207
+ * `declaredAxes` dele não têm nenhum dos dois - as opções que ele declara são `ocean`, `royal`,
1208
+ * `ai-outline`, `danger` e mais cinco. Os oito arquivos que importam o Button do MUI já eram
1209
+ * separados pelo `from`; o vazamento veio de um arquivo com 25 usos de `<Button>` e NENHUM import
1210
+ * de `Button` - origem desconhecida, tratada como dele.
1211
+ *
1212
+ * E "DE DENTRO" NÃO ERA UMA ORIGEM, era uma vizinhança: `OWN` valia para todo caminho interno,
1213
+ * então o `Button` da biblioteca dele e o `Button` do Chakra re-exportado atrás do alias `@/` eram
1214
+ * a MESMA linha. Exigir só `own` trocava o vazamento de terceiro por um vazamento de vizinho, e
1215
+ * pior: seis leis - `enlarge`, `on`, `hover`, `click`, `asChild`, `onlyBorder` - escritas sobre um
1216
+ * componente dele que não tem nenhuma dessas props. A origem agora é o especificador inteiro, e a
1217
+ * evidência casa com a linha que veio da PRÓPRIA biblioteca.
1218
+ *
1219
+ * Sem conseguir dizer QUAL linha é a da biblioteca, ninguém soma nada: a direção conservadora é a
1220
+ * mesma dos dois lados - deixar de somar evidência custa uma contagem, somar a errada custa uma
1221
+ * LEI escrita sobre a biblioteca de outra pessoa.
1222
+ */
1223
+ /** Os nomes que a biblioteca declara - a pergunta aqui é de PERTENCIMENTO, não de origem. */
1224
+ const inScope = new Set(scoped.map((c) => c.name));
1225
+ /** `root` aqui já É a pasta do escopo - `runImport` resolve `--scope` antes de chamar. */
1226
+ const published = await publishedAs(root);
1227
+ const evidence = new Map();
1228
+ /** Os nomes que chegam de mais de um lugar de dentro - a lacuna, contada para ser dita. */
1229
+ const ambiguous = [];
1230
+ const byName = new Map();
1231
+ for (const c of usedThere) {
1232
+ if (!c.own)
1233
+ continue;
1234
+ byName.set(c.name, [...(byName.get(c.name) ?? []), c]);
1235
+ }
1236
+ for (const [name, rows] of byName) {
1237
+ /**
1238
+ * A LINHA DA BIBLIOTECA, quando dá para nomeá-la: o app importa o pacote que o escopo publica.
1239
+ * É a resposta exata, e ela não depende de resolver caminho nenhum.
1240
+ */
1241
+ const mine = published
1242
+ ? rows.filter((r) => r.origin === published || r.origin?.startsWith(`${published}/`))
1243
+ : [];
1244
+ if (mine.length === 1) {
1245
+ evidence.set(name, mine[0]);
1246
+ continue;
1247
+ }
1248
+ /** Sem pacote para casar, uma origem interna só também é resposta: não há o que confundir. */
1249
+ if (mine.length === 0 && rows.length === 1) {
1250
+ evidence.set(name, rows[0]);
1251
+ continue;
1252
+ }
1253
+ ambiguous.push(name);
1254
+ }
1165
1255
  const merged = scoped.map((c) => {
1166
1256
  const e = evidence.get(c.name);
1167
1257
  if (!e)
@@ -1198,7 +1288,15 @@ export async function takeCensus(root, opts) {
1198
1288
  : {}),
1199
1289
  };
1200
1290
  });
1201
- const theirsOnly = usedThere.filter((c) => !c.from && !own.has(c.name));
1291
+ /**
1292
+ * O QUE O APP TEM E A BIBLIOTECA NÃO - e a mesma lei: só entra o que tem origem DELE.
1293
+ *
1294
+ * A frase que sai daqui diz "estes são componentes seus que vivem só no app". Sem `own`, um nome
1295
+ * de origem desconhecida entrava na lista, e a tela oferecia ao cliente um componente que pode ser
1296
+ * de terceiro - a mesma acusação que o `<Button variant="contained">` sem import produzia do
1297
+ * outro lado.
1298
+ */
1299
+ const theirsOnly = usedThere.filter((c) => c.own && !inScope.has(c.name));
1202
1300
  // The verdict travels WITH the payload: the platform reads one reading rather
1203
1301
  // than computing a second opinion from the same numbers, which is how two
1204
1302
  // implementations of the same judgement start disagreeing.
@@ -1211,8 +1309,19 @@ export async function takeCensus(root, opts) {
1211
1309
  * itself. See `isLibrary`.
1212
1310
  */
1213
1311
  const library = isLibrary(scoped);
1312
+ /**
1313
+ * O VEREDITO É DE UMA LINHA, NÃO DE UM NOME - o quarto sítio da mesma lei.
1314
+ *
1315
+ * `crosswalk` já julga linha a linha e pula quem tem `from`: o `Tooltip` do `recharts` sai dele
1316
+ * SEM veredito, que é o certo. Mas o mapa era por NOME, então o veredito do `Tooltip` DELE -
1317
+ * `exclusive`, um componente que ele escreveu - era colado também na linha do recharts. Medido em
1318
+ * 10/08: `recharts` e `@base-ui/react` chegavam ao censo como componentes exclusivos dele.
1319
+ *
1320
+ * A chave é nome+origem, que é a mesma identidade que o censo passou a carregar.
1321
+ */
1322
+ const idOf = (c) => `${c.name}\u0000${c.origin ?? ""}`;
1214
1323
  const verdicts = new Map(crosswalk(merged, { library }).map((r) => [
1215
- r.component.name,
1324
+ idOf(r.component),
1216
1325
  { bucket: r.bucket, canonical: r.canonical, because: r.because },
1217
1326
  ]));
1218
1327
  if (usedThere.length > 0) {
@@ -1515,7 +1624,7 @@ export async function takeCensus(root, opts) {
1515
1624
  }
1516
1625
  const components = merged.map((c) => ({
1517
1626
  ...c,
1518
- ...(verdicts.get(c.name) ?? {}),
1627
+ ...(verdicts.get(idOf(c)) ?? {}),
1519
1628
  ...(laws.has(c.name) ? { laws: laws.get(c.name) } : {}),
1520
1629
  ...(runtimeOf.has(c.name) ? { runtime: runtimeOf.get(c.name) } : {}),
1521
1630
  ...(companionsOf.has(c.name)
@@ -2341,10 +2450,31 @@ function sayReach(c) {
2341
2450
  * shadow one of their own names. `declared` is a DEFINITION of theirs, which is what makes
2342
2451
  * this a tie-break rather than a preference.
2343
2452
  */
2453
+ /**
2454
+ * O DESEMPATE QUE DEIXOU DE SER NECESSÁRIO - e que virou uma reivindicação falsa.
2455
+ *
2456
+ * Ele nasceu quando uma linha era um NOME: o `Pill` deles fazia
2457
+ * `import { Toggle } from "@base-ui/react/toggle"`, e esse nome colidia com o `Toggle` que eles
2458
+ * escreveram uma pasta ao lado - a linha saía carregando `from: "@base-ui/react/toggle"`, e `from` é
2459
+ * o que faz o crosswalk pular um componente. O Toggle DELES sumia do sistema inteiro (test50,
2460
+ * 04/08). Apagar o `from` era a única saída, porque não havia duas linhas para escolher.
2461
+ *
2462
+ * Agora há: a origem entra na chave, então o `Toggle` deles e o do `@base-ui/react` são duas linhas.
2463
+ * Continuar apagando o `from` da linha de terceiro faz a esteira reivindicar o componente ALHEIO -
2464
+ * medido em 10/08 no repo do dono: o `Tooltip` do `recharts` e o `Toggle` do `@base-ui/react`
2465
+ * chegavam ao censo como `exclusive`, ou seja, como componentes que ele teria escrito. É exatamente
2466
+ * o caso que a lei 13 nomeia.
2467
+ *
2468
+ * A regra passa a olhar a ORIGEM: um `from` só se apaga quando não há origem para distinguir as
2469
+ * duas - o mundo antigo, e o único em que o desempate ainda é honesto.
2470
+ */
2344
2471
  export function declaredWins(component, declared) {
2345
- return component.from && declared
2346
- ? { ...component, from: undefined }
2347
- : component;
2472
+ if (!component.from || !declared)
2473
+ return component;
2474
+ /** Com origem, a linha dele existe à parte e esta aqui é de quem a exportou. */
2475
+ if (component.origin)
2476
+ return component;
2477
+ return { ...component, from: undefined };
2348
2478
  }
2349
2479
  function rootPackage(tag, sketch) {
2350
2480
  if (!tag || !/^[A-Z]/.test(tag))
@@ -235,8 +235,19 @@ function importedNames(source, internal = []) {
235
235
  if (!part || part === "*")
236
236
  continue;
237
237
  const local = (part.split(/\s+as\s+/).pop() ?? "").trim();
238
+ /**
239
+ * O ESPECIFICADOR VIAJA MESMO QUANDO É DE DENTRO - e `own` vira uma resposta à parte.
240
+ *
241
+ * `OWN` era um balde só para TODO caminho interno, então o `Button` da biblioteca dele e o
242
+ * `Button` do Chakra re-exportado atrás do alias `@/` eram a mesma linha. Medido no repo do
243
+ * dono em 10/08: `Button` chega de 10 alvos internos diferentes, 6 deles dentro dos apps, e a
244
+ * união deles escreveu seis leis - `enlarge`, `on`, `hover`, `click`, `asChild`, `onlyBorder` -
245
+ * sobre um componente que não tem nenhuma dessas props.
246
+ *
247
+ * A lei 13 diz que a origem decide. "De dentro" não é uma origem, é uma vizinhança.
248
+ */
238
249
  if (/^[A-Z]/.test(local))
239
- out.set(local, own ? OWN : spec);
250
+ out.set(local, { spec, own });
240
251
  }
241
252
  }
242
253
  return out;
@@ -333,7 +344,7 @@ internal = []) {
333
344
  * causa do `own: true` valer em 7 de 128: uma linha que mistura duas origens nunca tem TODAS
334
345
  * as origens de dentro.
335
346
  */
336
- const key = `${name}\u0000${owner ?? ""}`;
347
+ const key = `${name}\u0000${owner?.spec ?? ""}`;
337
348
  let hit = tally.get(key);
338
349
  if (!hit) {
339
350
  hit = {
@@ -341,14 +352,18 @@ internal = []) {
341
352
  files: new Set(),
342
353
  props: new Map(),
343
354
  propFiles: new Map(),
344
- ...(owner && owner !== OWN ? { from: owner } : {}),
355
+ ...(owner && !owner.own ? { from: owner.spec } : {}),
345
356
  origins: new Set(),
346
357
  };
347
358
  tally.set(key, hit);
348
359
  }
349
- /** TODA origem vista, e não só a primeira - ver `origins` no tipo. */
360
+ /**
361
+ * A ORIGEM AGORA ESTÁ INTEIRA NA CHAVE, então esta linha tem uma só. `origins` continua
362
+ * existindo porque é ele que separa "veio de um lugar que eu sei ler" de "não veio de lugar
363
+ * nenhum" - e é a segunda que a lei 13 manda não atribuir a ninguém.
364
+ */
350
365
  if (owner)
351
- hit.origins.add(owner);
366
+ hit.origins.add(owner.own ? OWN : owner.spec);
352
367
  hit.count += 1;
353
368
  hit.files.add(file);
354
369
  LITERAL_PROP.lastIndex = 0;
@@ -393,6 +408,14 @@ export function tallyToInventory(tally, max = 80) {
393
408
  .map(([key, v]) => ({
394
409
  /** A chave carrega a origem para separar as linhas - o nome é a primeira metade dela. */
395
410
  name: key.split("\u0000")[0],
411
+ /**
412
+ * E A SEGUNDA METADE VIAJA, que é o que permite a quem lê perguntar QUAL. Sem isto o censo
413
+ * separava as linhas e devolvia só o nome - foi exatamente assim que a evidência de uso voltou
414
+ * a casar por NOME um passo depois desta função, em `runImport`.
415
+ */
416
+ ...(key.slice(key.indexOf("\u0000") + 1)
417
+ ? { origin: key.slice(key.indexOf("\u0000") + 1) }
418
+ : {}),
396
419
  ...(v.from ? { from: v.from } : {}),
397
420
  /** Só dele quando TODA aparição veio de dentro - ver `origins` e `OWN`. */
398
421
  ...(v.origins.size > 0 && [...v.origins].every((o) => o === OWN)
@@ -60,8 +60,36 @@ export function checkContracts(used, documents) {
60
60
  }
61
61
  if (axes.size === 0)
62
62
  return [];
63
+ /**
64
+ * E O NOME QUE CHEGA DE MAIS DE UM LUGAR DE DENTRO NÃO SE COBRA DE NINGUÉM.
65
+ *
66
+ * A terceira forma da mesma lei, e a que faltava. `own` responde "veio de dentro do projeto", que
67
+ * é uma VIZINHANÇA e não uma origem: um `Button` do Chakra re-exportado atrás do alias `@/` é tão
68
+ * "de dentro" quanto o da biblioteca dele.
69
+ *
70
+ * Medido no repo do dono em 10/08, no `apps/web-dashboard`: `Button` chega de DEZ lugares, cinco
71
+ * deles de dentro - `@frontend-hub/ui` (34 arquivos), `@/components/ui/_legacy/_chakra-ui/button`
72
+ * (25), `@/components/ui/_legacy/_general/Button` (19), `@/components/ui/Button` (3) e
73
+ * `../Button` (1). A ÚNICA acusação que o doctor fazia contra o sistema dele era
74
+ * `variant="outline"`, que é do Chakra: as cinco variantes que o Button DELE recebe estão todas
75
+ * entre as nove que ele declara.
76
+ *
77
+ * Uma acusação falsa custa mais que uma acusação perdida: ela ensina a pessoa a não acreditar na
78
+ * próxima. Enquanto a identidade não se divide de verdade, o mínimo honesto é a esteira SABER que
79
+ * é ambíguo e calar sobre esse nome.
80
+ */
81
+ const insideOrigins = new Map();
82
+ for (const c of used) {
83
+ if (!c.own)
84
+ continue;
85
+ const seen = insideOrigins.get(c.name) ?? new Set();
86
+ seen.add(c.origin ?? "");
87
+ insideOrigins.set(c.name, seen);
88
+ }
63
89
  const out = [];
64
90
  for (const c of used) {
91
+ if ((insideOrigins.get(c.name)?.size ?? 0) > 1)
92
+ continue;
65
93
  // A third party's component answers to its own package, not to this system.
66
94
  if (c.from)
67
95
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.199",
3
+ "version": "0.16.200",
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": {