youiyoux 0.1.2 → 0.1.4

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.
Files changed (212) hide show
  1. package/README.md +10 -0
  2. package/dist/chunks/{chunk-S7UV34H5.js → chunk-42YJX3W5.js} +1 -3
  3. package/dist/chunks/{chunk-FUB7B3VK.js → chunk-4BBYLTZX.js} +10 -10
  4. package/dist/chunks/chunk-52KOKYY6.js +8710 -0
  5. package/dist/chunks/chunk-FJZFCAL5.js +129 -0
  6. package/dist/chunks/chunk-FVVL5JCY.js +24 -0
  7. package/dist/chunks/{chunk-UHPK62Q5.js → chunk-M5XZWCPS.js} +240 -60
  8. package/dist/chunks/{chunk-ZLRFD5JI.js → chunk-NYDJYMID.js} +263 -16
  9. package/dist/chunks/{src-7ENJHYG4.js → chunk-RDTHHRZN.js} +319 -165
  10. package/dist/chunks/{chunk-E4HNK7VF.js → chunk-WNTS7ZO4.js} +1 -1
  11. package/dist/chunks/chunk-XAEMVEYX.js +188 -0
  12. package/dist/chunks/{chunk-CLNMVQZJ.js → chunk-YFCMTTXS.js} +708 -494
  13. package/dist/chunks/chunk-YJ7WMBTT.js +13761 -0
  14. package/dist/chunks/src-5IP4NGKT.js +115 -0
  15. package/dist/chunks/src-6CJR547Y.js +124 -0
  16. package/dist/chunks/{src-WQWYJ6IQ.js → src-BPETAFLW.js} +175 -76
  17. package/dist/chunks/{src-B742AYFC.js → src-LLV7IY6W.js} +55 -16
  18. package/dist/chunks/src-RHNZELGE.js +306 -0
  19. package/dist/chunks/{src-4KFNL3MY.js → src-SFKFERZ2.js} +18 -8
  20. package/dist/chunks/{src-VEHGMKYV.js → src-VL2ZYO6X.js} +4 -4
  21. package/dist/hub/assets/Badge-CUJUit4d.js +1 -0
  22. package/dist/hub/assets/CandidatePanel-CnbK4S0j.js +1 -0
  23. package/dist/hub/assets/CatalogScreen-B5OVL-AR.js +1 -0
  24. package/dist/hub/assets/ChangesScreen-GwGqJwxN.js +1 -0
  25. package/dist/hub/assets/CheatSheet-BLSOq-xa.js +1 -0
  26. package/dist/hub/assets/CodeBlock-CsiGFgvI.js +1 -0
  27. package/dist/hub/assets/CommandPalette-CAWQfdXJ.js +1 -0
  28. package/dist/hub/assets/CopyButton-DE8drl4P.js +1 -0
  29. package/dist/hub/assets/Disclosure-B1kTgPgk.js +1 -0
  30. package/dist/hub/assets/DiscoverScreen-Tl_Z6mgY.js +2 -0
  31. package/dist/hub/assets/DraftEditor-yIDapXTs.js +5 -0
  32. package/dist/hub/assets/Field-LwrJXoOs.js +1 -0
  33. package/dist/hub/assets/GapsScreen-DXZAlm6v.js +1 -0
  34. package/dist/hub/assets/HomeScreen-CEpiVhOa.js +1 -0
  35. package/dist/hub/assets/InventoryScreen-CEG3Pmw_.js +1 -0
  36. package/dist/hub/assets/KindIcon-BJ6bz-ny.js +1 -0
  37. package/dist/hub/assets/LibraryScreen-IaVnxrX9.js +1 -0
  38. package/dist/hub/assets/LicenceBadge-CLCUMMHE.js +1 -0
  39. package/dist/hub/assets/Opened-DJyf4Hnj.js +1 -0
  40. package/dist/hub/assets/Preview-C069zVMP.js +1 -0
  41. package/dist/hub/assets/RuleDetail-B4g2-TRe.js +3 -0
  42. package/dist/hub/assets/RulesScreen-CAaXIWjO.js +3 -0
  43. package/dist/hub/assets/ScreenImage-DcI7_G3O.js +1 -0
  44. package/dist/hub/assets/ScreensScreen-DwczCjZD.js +1 -0
  45. package/dist/hub/assets/SearchField-DaCpNAj6.js +1 -0
  46. package/dist/hub/assets/Section-BTnvUq_W.js +1 -0
  47. package/dist/hub/assets/Segmented-Dzi1x49S.js +1 -0
  48. package/dist/hub/assets/ServiceDown-Bv-BPXro.js +1 -0
  49. package/dist/hub/assets/SpecimenCapture-DlsjshVE.js +1 -0
  50. package/dist/hub/assets/Specimens-C-lHp7sr.js +2 -0
  51. package/dist/hub/assets/StackScreen-DkmELsqR.js +1 -0
  52. package/dist/hub/assets/StubScreens-Bt7lWLVK.js +1 -0
  53. package/dist/hub/assets/StyleScreen-tq1tbRBo.js +1 -0
  54. package/dist/hub/assets/Swatch-KDz4qxdM.js +1 -0
  55. package/dist/hub/assets/Tabs-1QQDS0I6.js +1 -0
  56. package/dist/hub/assets/TokenDetail-DvCw6GqC.js +1 -0
  57. package/dist/hub/assets/TokenValue-CsPRc2ai.js +1 -0
  58. package/dist/hub/assets/TokensScreen-DoENV8Xy.js +1 -0
  59. package/dist/hub/assets/arrow-right-DcbNDzqs.js +1 -0
  60. package/dist/hub/assets/bot-B3gC9oUR.js +1 -0
  61. package/dist/hub/assets/button-6mvMVtpb.js +1 -0
  62. package/dist/hub/assets/button.demo-DuU0DQbv.js +1 -0
  63. package/dist/hub/assets/card.demo-CufAxMAA.js +1 -0
  64. package/dist/hub/assets/chevron-down-CzN07vMi.js +1 -0
  65. package/dist/hub/assets/circle-alert-DT2LC4yf.js +1 -0
  66. package/dist/hub/assets/circle-check-DkwBMT62.js +1 -0
  67. package/dist/hub/assets/client-D7whEyep.js +1 -0
  68. package/dist/hub/assets/clsx-DB0hHKMi.js +1 -0
  69. package/dist/hub/assets/compass-BkKOZPYe.js +1 -0
  70. package/dist/hub/assets/contrast-BkNHUAPa.js +1 -0
  71. package/dist/hub/assets/createLucideIcon-CGDJWtKO.js +1 -0
  72. package/dist/hub/assets/discover-XjJEViwk.js +1 -0
  73. package/dist/hub/assets/dist-BYQYaPBV.js +1 -0
  74. package/dist/hub/assets/dist-Bk4srtDn.js +41 -0
  75. package/dist/hub/assets/dist-BogNzeK9.js +1 -0
  76. package/dist/hub/assets/dist-D0z7Bvsy.js +1 -0
  77. package/dist/hub/assets/download-BQI-Si9R.js +1 -0
  78. package/dist/hub/assets/engine-CMmt9yEz.js +1 -0
  79. package/dist/hub/assets/esm-NPpg23M8.js +1 -0
  80. package/dist/hub/assets/family-tiles-CyTdy_cP.js +1 -0
  81. package/dist/hub/assets/format-DxAYNTN_.js +1 -0
  82. package/dist/hub/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  83. package/dist/hub/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  84. package/dist/hub/assets/geist-latin-core-vkBVTr4s.woff2 +0 -0
  85. package/dist/hub/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  86. package/dist/hub/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  87. package/dist/hub/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
  88. package/dist/hub/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
  89. package/dist/hub/assets/geist-mono-latin-core-DccGGzmg.woff2 +0 -0
  90. package/dist/hub/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
  91. package/dist/hub/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  92. package/dist/hub/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
  93. package/dist/hub/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
  94. package/dist/hub/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  95. package/dist/hub/assets/git-pull-request-arrow-RqUUIRCU.js +1 -0
  96. package/dist/hub/assets/input.demo-238gqWpz.js +1 -0
  97. package/dist/hub/assets/inventory-BI8QBdar.js +1 -0
  98. package/dist/hub/assets/inventory-source-lZYzcA8O.js +1 -0
  99. package/dist/hub/assets/jsx-runtime-D6zNwnaE.js +9 -0
  100. package/dist/hub/assets/layers-DfzdK_wG.js +1 -0
  101. package/dist/hub/assets/layout-grid-xFY9L9Oc.js +1 -0
  102. package/dist/hub/assets/library-W5R8GwvG.js +1 -0
  103. package/dist/hub/assets/main-7snyIdoi.css +2 -0
  104. package/dist/hub/assets/main-B9CIkk5m.js +2 -0
  105. package/dist/hub/assets/minus-BDifFhOo.js +1 -0
  106. package/dist/hub/assets/monitor-X_lELkcU.js +1 -0
  107. package/dist/hub/assets/mouse-pointer-click-CVJ6hWg8.js +1 -0
  108. package/dist/hub/assets/paintbrush-DOkEgSTO.js +1 -0
  109. package/dist/hub/assets/plural-BbGQPQKA.js +1 -0
  110. package/dist/hub/assets/preview-BA8CPsKb.css +2 -0
  111. package/dist/hub/assets/preview-DUm13vr1.js +3 -0
  112. package/dist/hub/assets/protocol-caP6umNL.js +1 -0
  113. package/dist/hub/assets/react-DB-4Zxce.js +1 -0
  114. package/dist/hub/assets/shield-check-0ka0kQct.js +1 -0
  115. package/dist/hub/assets/trash-B7aEfJps.js +1 -0
  116. package/dist/hub/assets/triangle-alert-Dkdi9eDR.js +1 -0
  117. package/dist/hub/assets/try-live-zX3CSd3P.js +1 -0
  118. package/dist/hub/assets/utils-BoaXfs8y.js +1 -0
  119. package/dist/hub/assets/values-DzxR_TlI.js +1 -0
  120. package/dist/hub/assets/worker-Bh-wSp42.js +1 -0
  121. package/dist/hub/assets/workflow-jpV8wh-U.js +1 -0
  122. package/dist/hub/assets/x-MLbFVa4v.js +1 -0
  123. package/dist/hub/favicon.svg +1 -0
  124. package/dist/hub/index.html +35 -0
  125. package/dist/hub/kit/candidates.json +1 -0
  126. package/dist/hub/kit/chunks/chunk-2NYFUOIV.js +14 -0
  127. package/dist/hub/kit/chunks/chunk-DCANE7XH.js +1 -0
  128. package/dist/hub/kit/chunks/chunk-FTGHCY4I.js +14 -0
  129. package/dist/hub/kit/chunks/chunk-IBF3UTJF.js +33 -0
  130. package/dist/hub/kit/chunks/chunk-LLXY4VSH.js +1 -0
  131. package/dist/hub/kit/chunks/chunk-PUPZMW4P.js +14 -0
  132. package/dist/hub/kit/chunks/chunk-PWLNZQET.js +1 -0
  133. package/dist/hub/kit/chunks/chunk-ULXWVOKJ.js +1 -0
  134. package/dist/hub/kit/chunks/chunk-WM4BGGMK.js +1 -0
  135. package/dist/hub/kit/chunks/chunk-ZQUNRK72.js +1 -0
  136. package/dist/hub/kit/class-variance-authority.js +1 -0
  137. package/dist/hub/kit/clsx.js +1 -0
  138. package/dist/hub/kit/fonts/geist-cyrillic-ext-wght-normal.woff2 +0 -0
  139. package/dist/hub/kit/fonts/geist-cyrillic-wght-normal.woff2 +0 -0
  140. package/dist/hub/kit/fonts/geist-latin-ext-wght-normal.woff2 +0 -0
  141. package/dist/hub/kit/fonts/geist-latin-wght-normal.woff2 +0 -0
  142. package/dist/hub/kit/fonts/geist-vietnamese-wght-normal.woff2 +0 -0
  143. package/dist/hub/kit/frame.css.src +73 -0
  144. package/dist/hub/kit/frame.js +2 -0
  145. package/dist/hub/kit/lucide-react.js +1878 -0
  146. package/dist/hub/kit/preview.html +13 -0
  147. package/dist/hub/kit/radix-ui-react-dialog.js +41 -0
  148. package/dist/hub/kit/radix-ui-react-label.js +1 -0
  149. package/dist/hub/kit/radix-ui-react-separator.js +1 -0
  150. package/dist/hub/kit/radix-ui-react-slot.js +1 -0
  151. package/dist/hub/kit/radix-ui-react-switch.js +1 -0
  152. package/dist/hub/kit/radix-ui-react-tabs.js +1 -0
  153. package/dist/hub/kit/react-dom-client.js +1 -0
  154. package/dist/hub/kit/react-dom.js +1 -0
  155. package/dist/hub/kit/react-jsx-runtime.js +1 -0
  156. package/dist/hub/kit/react.js +1 -0
  157. package/dist/hub/kit/tailwind-merge.js +1 -0
  158. package/dist/hub/kit/tailwindcss/index.css +950 -0
  159. package/dist/hub/kit/tailwindcss/preflight.css +398 -0
  160. package/dist/hub/kit/tailwindcss/theme.css +510 -0
  161. package/dist/hub/kit/tailwindcss/utilities.css +1 -0
  162. package/dist/hub-demo/frame/button.demo.js +1 -0
  163. package/dist/hub-demo/frame/card.demo.js +1 -0
  164. package/dist/hub-demo/frame/demos.js +6 -0
  165. package/dist/hub-demo/frame/frame.css +770 -0
  166. package/dist/hub-demo/frame/input.demo.js +1 -0
  167. package/dist/hub-demo/preview/components/button.demo.tsx +47 -0
  168. package/dist/hub-demo/preview/components/button.tsx +31 -0
  169. package/dist/hub-demo/preview/components/card.demo.tsx +26 -0
  170. package/dist/hub-demo/preview/components/card.tsx +20 -0
  171. package/dist/hub-demo/preview/components/input.demo.tsx +39 -0
  172. package/dist/hub-demo/preview/components/input.tsx +19 -0
  173. package/dist/hub-demo/preview/components/utils.ts +5 -0
  174. package/dist/hub-demo/preview/system.css +161 -0
  175. package/dist/hub-demo/public/data/demo-app.usage.bedd9c82fe.json +1 -0
  176. package/dist/hub-demo/public/data/demo.catalog.a605de2281.json +1 -0
  177. package/dist/hub-demo/public/data/gaps.129a93538d.json +1 -0
  178. package/dist/hub-demo/public/data/inventory.165a4f6408.json +1 -0
  179. package/dist/hub-demo/public/data/inventory.console.badge.ad0238784e.json +1 -0
  180. package/dist/hub-demo/public/data/inventory.console.button.3249daff35.json +1 -0
  181. package/dist/hub-demo/public/data/inventory.console.card.9ea88708ac.json +1 -0
  182. package/dist/hub-demo/public/data/inventory.console.dialog.8b6574d93a.json +1 -0
  183. package/dist/hub-demo/public/data/inventory.console.input.bdffb756a7.json +1 -0
  184. package/dist/hub-demo/public/data/inventory.console.link-as-button.1ce7faa1f3.json +1 -0
  185. package/dist/hub-demo/public/data/inventory.console.tabs.813e694c44.json +1 -0
  186. package/dist/hub-demo/public/data/inventory.extras.e5d06c6ff4.json +1 -0
  187. package/dist/hub-demo/public/data/inventory.site.badge.047a68f644.json +1 -0
  188. package/dist/hub-demo/public/data/inventory.site.button.720938384e.json +1 -0
  189. package/dist/hub-demo/public/data/inventory.site.card.d8df5f6873.json +1 -0
  190. package/dist/hub-demo/public/data/inventory.site.link-as-button.9c1f8a55bf.json +1 -0
  191. package/dist/hub-demo/public/data/inventory.site.tabs.59bd0200b9.json +1 -0
  192. package/dist/hub-demo/public/data/manifest.json +39 -0
  193. package/dist/hub-demo/public/data/specimens.console.dark.046471907d.css +786 -0
  194. package/dist/hub-demo/public/data/specimens.console.light.aaffa2d213.css +786 -0
  195. package/dist/hub-demo/public/data/specimens.site.dark.94a6706908.css +92 -0
  196. package/dist/hub-demo/public/data/specimens.site.light.3ddbc8d28d.css +92 -0
  197. package/dist/hub-demo/public/previews/button.dark.webp +0 -0
  198. package/dist/hub-demo/public/previews/button.light.webp +0 -0
  199. package/dist/hub-demo/public/previews/card.dark.webp +0 -0
  200. package/dist/hub-demo/public/previews/card.light.webp +0 -0
  201. package/dist/hub-demo/public/previews/input.dark.webp +0 -0
  202. package/dist/hub-demo/public/previews/input.light.webp +0 -0
  203. package/dist/hub-previews.js +173 -0
  204. package/dist/hub-server.js +6329 -0
  205. package/dist/hub-snapshot.js +573 -0
  206. package/dist/schema/youi.gaps.schema.json +235 -0
  207. package/dist/schema/youi.request.schema.json +229 -0
  208. package/dist/tailwind-worker.js +3 -3
  209. package/dist/youi.js +10 -8
  210. package/dist/youiyoux.js +10 -8
  211. package/package.json +4 -4
  212. package/dist/chunks/chunk-3ZHGONNA.js +0 -21797
@@ -1,12 +1,14 @@
1
- import { createRequire as __youiRequire } from "node:module"; const require = __youiRequire(import.meta.url);
1
+ import { createRequire as __youiRequire } from "node:module"; import { dirname as __youiDir } from "node:path"; import { fileURLToPath as __youiPath } from "node:url"; const require = __youiRequire(import.meta.url); const __youiDirname = __youiDir(__youiPath(import.meta.url));
2
2
  import {
3
3
  LIBRARY_SOURCE_KINDS,
4
4
  LibraryEntry,
5
5
  SHAPE_DIMENSIONS,
6
+ copyleft,
6
7
  external_exports,
7
8
  sha256,
9
+ shortHash,
8
10
  writeFileAtomic
9
- } from "./chunk-ZLRFD5JI.js";
11
+ } from "./chunk-NYDJYMID.js";
10
12
 
11
13
  // ../discover/src/aliases.ts
12
14
  var CONCEPTS = [
@@ -55,9 +57,6 @@ var CONCEPTS = [
55
57
  { id: "marquee", names: ["marquee", "ticker", "scrolling text"], packages: [] }
56
58
  ];
57
59
  var BY_ID = new Map(CONCEPTS.map((c) => [c.id, c]));
58
- function conceptById(id) {
59
- return BY_ID.get(id);
60
- }
61
60
  function normalize(text) {
62
61
  return text.normalize("NFC").replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
63
62
  }
@@ -112,7 +111,7 @@ function cleanData(value) {
112
111
  }
113
112
 
114
113
  // ../discover/src/sources.ts
115
- var CURATED = ["@coss", "@kibo-ui", "@magicui", "@origin", "@aceternity"];
114
+ var CURATED = ["@coss", "@kibo-ui", "@magicui", "@aceternity"];
116
115
  var SHADCN = {
117
116
  name: "@shadcn",
118
117
  homepage: "https://ui.shadcn.com",
@@ -215,7 +214,7 @@ function importedPackages(source) {
215
214
  const found = /* @__PURE__ */ new Set();
216
215
  for (const match of source.matchAll(IMPORT)) {
217
216
  const spec = match[1];
218
- if (/^(\.|\/|@\/|~\/|#|node:)/.test(spec) || spec.startsWith("@/")) continue;
217
+ if (/^(\.|\/|@\/|~\/|#|node:)/.test(spec)) continue;
219
218
  const parts = spec.split("/");
220
219
  const name = spec.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
221
220
  if (name && !AMBIENT.has(name)) found.add(name);
@@ -508,7 +507,7 @@ var json = (value) => JSON.stringify(value);
508
507
  var parse = (value) => typeof value === "string" ? JSON.parse(value) : value;
509
508
  var int = (value) => Number(value ?? 0);
510
509
  var marks = (n) => Array.from({ length: n }, () => "?").join(", ");
511
- var clampLimit = pageInteger;
510
+ var batches = (items) => Array.from({ length: Math.ceil(items.length / MAX_PARAMS) }, (_, i) => items.slice(i * MAX_PARAMS, (i + 1) * MAX_PARAMS));
512
511
  var SqlStore = class {
513
512
  db;
514
513
  lock = new Lock();
@@ -603,10 +602,7 @@ var SqlStore = class {
603
602
  const update = columns.slice(1).map((c) => `${c} = excluded.${c}`).join(", ");
604
603
  await this.insert("discover_items", columns, items.map((i) => [i.id, i.scope, i.ref, i.name, i.title, i.summary, i.kind, i.sourceKind, i.sourceClass, i.registry, i.licence, i.style, i.downloads, i.stars, i.archived ? 1 : 0, i.searchText, json(i.entry), i.builtAt]), `ON CONFLICT (id) DO UPDATE SET ${update}`);
605
604
  const ids = items.map((i) => i.id);
606
- for (let i = 0; i < ids.length; i += MAX_PARAMS) {
607
- const batch = ids.slice(i, i + MAX_PARAMS);
608
- await this.db.run(`DELETE FROM discover_roles WHERE item_id IN (${marks(batch.length)})`, batch);
609
- }
605
+ for (const batch of batches(ids)) await this.db.run(`DELETE FROM discover_roles WHERE item_id IN (${marks(batch.length)})`, batch);
610
606
  await this.insert("discover_roles", ["item_id", "role"], items.flatMap((i) => [...new Set(i.roles)].map((role) => [i.id, role])), "ON CONFLICT (item_id, role) DO NOTHING");
611
607
  }
612
608
  async replaceDiscoverItems(scope, items) {
@@ -659,15 +655,14 @@ var SqlStore = class {
659
655
  );
660
656
  const entries = /* @__PURE__ */ new Map();
661
657
  const ids = ranked.map((r) => r.id);
662
- for (let i = 0; i < ids.length; i += MAX_PARAMS) {
663
- const batch = ids.slice(i, i + MAX_PARAMS);
658
+ for (const batch of batches(ids)) {
664
659
  for (const row of await this.db.all(`SELECT id, entry FROM discover_items WHERE id IN (${marks(batch.length)})`, batch)) entries.set(row.id, row.entry);
665
660
  }
666
661
  return ranked.map((r) => ({ id: r.id, sourceClass: r.source_class, entry: parse(entries.get(r.id)) }));
667
662
  }
668
663
  async listDiscover(filter, page = {}) {
669
664
  const where = this.filterSql(filter);
670
- const limit = clampLimit(page.limit, 20);
665
+ const limit = pageInteger(page.limit, 20);
671
666
  const offset = pageInteger(page.offset, 0, 2147483647);
672
667
  const rows = await this.db.all(
673
668
  `SELECT d.id, d.source_class, d.entry FROM discover_items d WHERE ${where.sql} ORDER BY CASE d.source_class WHEN 'official' THEN 0 ELSE 1 END, COALESCE(d.downloads, -1) DESC, d.id LIMIT ? OFFSET ?`,
@@ -698,8 +693,7 @@ var SqlStore = class {
698
693
  }
699
694
  async discoverRoles(itemIds) {
700
695
  const out = {};
701
- for (let i = 0; i < itemIds.length; i += MAX_PARAMS) {
702
- const batch = itemIds.slice(i, i + MAX_PARAMS);
696
+ for (const batch of batches(itemIds)) {
703
697
  for (const row of await this.db.all(`SELECT item_id, role FROM discover_roles WHERE item_id IN (${marks(batch.length)}) ORDER BY item_id, role`, batch)) (out[row.item_id] ??= []).push(row.role);
704
698
  }
705
699
  return out;
@@ -745,7 +739,7 @@ var SqlStore = class {
745
739
  }
746
740
  async history(filter = {}) {
747
741
  const where = filter.project ? `WHERE project = ?` : "";
748
- const rows = await this.db.all(`SELECT * FROM scans ${where} ORDER BY scanned_at DESC, project LIMIT ? OFFSET ?`, [...filter.project ? [filter.project] : [], clampLimit(filter.limit, 20), pageInteger(filter.offset, 0, 2147483647)]);
742
+ const rows = await this.db.all(`SELECT * FROM scans ${where} ORDER BY scanned_at DESC, project LIMIT ? OFFSET ?`, [...filter.project ? [filter.project] : [], pageInteger(filter.limit, 20), pageInteger(filter.offset, 0, 2147483647)]);
749
743
  return rows.map((r) => ({ id: String(r.id), run: String(r.run), source: String(r.source), project: String(r.project), root: String(r.root), commit: r.commit_sha ?? null, scannedAt: String(r.scanned_at), status: String(r.status), files: int(r.files), looks: int(r.looks), families: int(r.families), nearDuplicates: int(r.near_duplicates), roles: parse(r.roles) }));
750
744
  }
751
745
  async latestRun(source) {
@@ -758,7 +752,7 @@ var SqlStore = class {
758
752
  const where = `run = ?${filter.role ? " AND role = ?" : ""}`;
759
753
  const params = [run, ...filter.role ? [filter.role] : []];
760
754
  const total = int((await this.db.all(`SELECT CAST(COUNT(*) AS INTEGER) AS n FROM families WHERE ${where}`, params))[0]?.n);
761
- const rows = await this.db.all(`SELECT * FROM families WHERE ${where} ORDER BY role, looks DESC, id LIMIT ? OFFSET ?`, [...params, clampLimit(filter.limit, 50), pageInteger(filter.offset, 0, 2147483647)]);
755
+ const rows = await this.db.all(`SELECT * FROM families WHERE ${where} ORDER BY role, looks DESC, id LIMIT ? OFFSET ?`, [...params, pageInteger(filter.limit, 50), pageInteger(filter.offset, 0, 2147483647)]);
762
756
  return { run, total, rows: rows.map((r) => ({ id: String(r.id), role: String(r.role), looks: int(r.looks), nearDuplicate: int(r.near_duplicate) === 1, projects: parse(r.projects), kits: parse(r.kits), shape: parse(r.shape) })) };
763
757
  }
764
758
  async looks(filter) {
@@ -767,7 +761,7 @@ var SqlStore = class {
767
761
  const where = `run = ?${filter.family ? " AND family = ?" : ""}${filter.role ? " AND role = ?" : ""}`;
768
762
  const params = [run, ...filter.family ? [filter.family] : [], ...filter.role ? [filter.role] : []];
769
763
  const total = int((await this.db.all(`SELECT CAST(COUNT(*) AS INTEGER) AS n FROM looks WHERE ${where}`, params))[0]?.n);
770
- const rows = await this.db.all(`SELECT * FROM looks WHERE ${where} ORDER BY project, file, line, col, id LIMIT ? OFFSET ?`, [...params, clampLimit(filter.limit, 50), pageInteger(filter.offset, 0, 2147483647)]);
764
+ const rows = await this.db.all(`SELECT * FROM looks WHERE ${where} ORDER BY project, file, line, col, id LIMIT ? OFFSET ?`, [...params, pageInteger(filter.limit, 50), pageInteger(filter.offset, 0, 2147483647)]);
771
765
  return { run, total, rows: rows.map((r) => ({ id: String(r.id), project: String(r.project), role: String(r.role), file: String(r.file), line: int(r.line), column: int(r.col), tag: String(r.tag), kit: r.kit ?? null, family: r.family ?? null, routes: parse(r.routes), shape: parse(r.shape) })) };
772
766
  }
773
767
  /* --------------------------------------------------------------------- graph */
@@ -1108,10 +1102,8 @@ async function openStore(options = {}) {
1108
1102
  }
1109
1103
 
1110
1104
  // ../store/src/derive.ts
1111
- import { createHash } from "node:crypto";
1112
- var short = (text) => createHash("sha256").update(text).digest("hex").slice(0, 12);
1113
1105
  var nodeId = {
1114
- family: (inventory, id, project = "") => `family:${short(inventory)}/${encodeURIComponent(project)}/${id}`,
1106
+ family: (inventory, id, project = "") => `family:${shortHash(inventory)}/${encodeURIComponent(project)}/${id}`,
1115
1107
  file: (project, file) => `file:${project}:${file}`,
1116
1108
  route: (project, route) => `route:${project}:${route}`,
1117
1109
  project: (id) => `project:${id}`,
@@ -1158,7 +1150,7 @@ function nearDuplicatePairs(families, tolerance) {
1158
1150
  return pairs;
1159
1151
  }
1160
1152
  function inventoryRun(report, source) {
1161
- const run = `${report.generatedAt}#${short(`${source}\0${report.generatedAt}`).slice(0, 8)}`;
1153
+ const run = `${report.generatedAt}#${shortHash(`${source}\0${report.generatedAt}`).slice(0, 8)}`;
1162
1154
  const g = new GraphBuilder();
1163
1155
  const familyNode = (id, project) => g.node({ id: nodeId.family(source, id, project), kind: "family", ref: id, project });
1164
1156
  for (const family of report.families) for (const project of family.projects) familyNode(family.id, project);
@@ -1266,7 +1258,6 @@ var DiscoverIndex = external_exports.object({
1266
1258
  categories: external_exports.record(external_exports.string(), external_exports.array(external_exports.string())),
1267
1259
  entries: external_exports.array(LibraryEntry)
1268
1260
  });
1269
- var EMPTY_SIGNALS = { npm: {}, github: {}, registryRepos: {} };
1270
1261
  async function readJson(file) {
1271
1262
  try {
1272
1263
  return JSON.parse(await readFile(file, "utf8"));
@@ -1373,7 +1364,7 @@ function slimResponse(url, body) {
1373
1364
  }))
1374
1365
  })) };
1375
1366
  }
1376
- async function writeHttpCache(cacheDir, cache, _previous) {
1367
+ async function writeHttpCache(cacheDir, cache) {
1377
1368
  const keep = /* @__PURE__ */ new Set();
1378
1369
  for (const [url, entry] of cache) {
1379
1370
  let data;
@@ -1511,7 +1502,7 @@ var PoliteClient = class {
1511
1502
  this.stats.cached++;
1512
1503
  return { ok: true, url, status: 200, body: cached.body, sha256: cached.sha256, fetchedAt: cached.fetchedAt, revalidated: true, headers: new Headers() };
1513
1504
  }
1514
- const bucket = npm ? "npm" : this.options.bucketFor?.(parsed) ?? githubBucketFor(parsed);
1505
+ const bucket = npm ? "npm" : githubBucketFor(parsed);
1515
1506
  let host = this.hosts.get(parsed.host);
1516
1507
  if (!host) this.hosts.set(parsed.host, host = new Gate(this.options.perHost ?? 2));
1517
1508
  const gate = host;
@@ -1720,6 +1711,9 @@ function playsRole(role, f) {
1720
1711
  const n = normalize(role);
1721
1712
  return hasPhrase(f.name, n) || f.categories.some((c) => hasPhrase(c, n));
1722
1713
  }
1714
+ function licenceOf(entry) {
1715
+ return entry.license && entry.license !== "unknown" ? entry.license.spdx : "unknown";
1716
+ }
1723
1717
  function hitOf(entry, score, why) {
1724
1718
  return {
1725
1719
  id: entry.id,
@@ -1728,7 +1722,7 @@ function hitOf(entry, score, why) {
1728
1722
  kind: entry.kind,
1729
1723
  summary: entry.summary,
1730
1724
  source: { kind: entry.source.kind ?? null, name: entry.source.name, ref: entry.source.ref ?? null, url: entry.source.url ?? null },
1731
- license: entry.license && entry.license !== "unknown" ? entry.license.spdx : "unknown",
1725
+ license: licenceOf(entry),
1732
1726
  signals: { ...entry.signals?.via ? { via: entry.signals.via } : {}, weeklyDownloads: entry.signals?.weeklyDownloads ?? null, stars: entry.signals?.stars ?? null, pushedAt: entry.signals?.pushedAt ?? null, archived: entry.signals?.archived ?? null },
1733
1727
  install: entry.install ? { command: entry.install.command, npm: entry.install.npm, registryDeps: entry.install.registryDeps, implicitDeps: entry.install.implicitDeps } : null,
1734
1728
  score: Math.round(score * 100) / 100,
@@ -1796,7 +1790,7 @@ function showCandidate(index, query) {
1796
1790
 
1797
1791
  // ../discover/src/signals.ts
1798
1792
  var HOUR = 36e5;
1799
- var SIGNAL_TTL = { npmMs: 24 * HOUR, githubMs: 7 * 24 * HOUR, registryRepoMs: 30 * 24 * HOUR };
1793
+ var SIGNAL_TTL = { npmMs: 24 * HOUR, githubMs: 7 * 24 * HOUR, registryRepoMs: 30 * 24 * HOUR, registryRepoMissMs: 24 * HOUR };
1800
1794
  function defaultBudgets(token) {
1801
1795
  return token ? { npmPackages: 400, githubRepos: 600, githubSearches: 30 } : { npmPackages: 200, githubRepos: 50, githubSearches: 10 };
1802
1796
  }
@@ -1810,18 +1804,39 @@ function registryOf(entry) {
1810
1804
  return entry.source.ref.slice(0, entry.source.ref.indexOf("/"));
1811
1805
  }
1812
1806
  var fresh = (at, ttl, now) => Boolean(at) && now.getTime() - Date.parse(at) < ttl;
1807
+ function homeParts(value) {
1808
+ try {
1809
+ const url = new URL(value.includes("://") ? value : `https://${value}`);
1810
+ return { host: url.hostname.replace(/^www\./, "").toLowerCase(), path: url.pathname.replace(/\/+$/, "").toLowerCase() };
1811
+ } catch {
1812
+ return null;
1813
+ }
1814
+ }
1813
1815
  function sameHomepage(a, b) {
1814
- const key = (value) => {
1815
- try {
1816
- const url = new URL(value.includes("://") ? value : `https://${value}`);
1817
- return `${url.hostname.replace(/^www\./, "")}${url.pathname.replace(/\/+$/, "")}`.toLowerCase();
1818
- } catch {
1819
- return null;
1820
- }
1821
- };
1822
1816
  if (!a || !b) return false;
1823
- const ka = key(a);
1824
- return ka !== null && ka === key(b);
1817
+ const pa = homeParts(a);
1818
+ const pb = homeParts(b);
1819
+ return pa !== null && pb !== null && `${pa.host}${pa.path}` === `${pb.host}${pb.path}`;
1820
+ }
1821
+ var PATH_TENANTS = /* @__PURE__ */ new Set(["github.com", "gitlab.com", "bitbucket.org", "codeberg.org", "npmjs.com", "codesandbox.io", "stackblitz.com", "gitbook.io", "medium.com", "notion.site"]);
1822
+ function sameSite(repository, registry) {
1823
+ if (!repository || !registry) return false;
1824
+ const repo = homeParts(repository);
1825
+ const home = homeParts(registry);
1826
+ if (!repo || !home || repo.host !== home.host) return false;
1827
+ if (PATH_TENANTS.has(repo.host)) return repo.path === home.path && repo.path !== "";
1828
+ return repo.path === "" || home.path === repo.path || home.path.startsWith(`${repo.path}/`);
1829
+ }
1830
+ var NOT_OWNERS = /* @__PURE__ */ new Set(["about", "apps", "collections", "contact", "enterprise", "explore", "features", "login", "marketplace", "orgs", "pricing", "settings", "site", "sponsors", "topics"]);
1831
+ function linkedRepos(html) {
1832
+ const names = [];
1833
+ for (const match of html.matchAll(/https?:\/\/(?:www\.)?github\.com\/([A-Za-z0-9-]+)\/([A-Za-z0-9_.-]+)/g)) {
1834
+ const owner = match[1];
1835
+ const repo = match[2].replace(/\.git$/, "").replace(/\.+$/, "");
1836
+ if (NOT_OWNERS.has(owner.toLowerCase()) || !repo || repo.startsWith(".")) continue;
1837
+ names.push(`${owner}/${repo}`);
1838
+ }
1839
+ return byUse(names);
1825
1840
  }
1826
1841
  var GithubRepoBody = external_exports.object({
1827
1842
  full_name: external_exports.string(),
@@ -1886,9 +1901,19 @@ async function refreshSignals(entries, registries, previous, client, now, budget
1886
1901
  npm[name] = { weeklyDownloads: typeof count2 === "number" ? count2 : null, license: spdxOrNull(meta?.license), repo: githubRepo(typeof repository === "string" ? repository : null), fetchedAt: at };
1887
1902
  });
1888
1903
  registryRepos[SHADCN.name] = { repo: SHADCN.repo, checkedAt: at };
1889
- const unsearched = [...registries].filter((r) => r.name !== SHADCN.name && r.homepage && !fresh(registryRepos[r.name]?.checkedAt, SIGNAL_TTL.registryRepoMs, now)).sort((a, b) => b.items - a.items || a.name.localeCompare(b.name));
1890
- for (const registry of unsearched.slice(0, budgets.githubSearches)) {
1891
- if (!client.hasBudget("github-search")) break;
1904
+ const known = (name) => {
1905
+ const prior = registryRepos[name];
1906
+ return fresh(prior?.checkedAt, prior?.repo ? SIGNAL_TTL.registryRepoMs : SIGNAL_TTL.registryRepoMissMs, now);
1907
+ };
1908
+ const unsearched = [...registries].filter((r) => r.name !== SHADCN.name && r.homepage && !known(r.name)).sort((a, b) => b.items - a.items || a.name.localeCompare(b.name));
1909
+ const spend = { repoCalls: budgets.githubRepos };
1910
+ for (const registry of unsearched) {
1911
+ const linked = await homepageRepo(registry.homepage, client, github, at, report, spend);
1912
+ if (linked) {
1913
+ registryRepos[registry.name] = { repo: linked, checkedAt: at };
1914
+ continue;
1915
+ }
1916
+ if (report.github.searches >= budgets.githubSearches || !client.hasBudget("github-search")) continue;
1892
1917
  const query = `${sourceSlug(registry.name).replace(/-ui$/, "")} in:name`;
1893
1918
  const result = await client.json(`https://api.github.com/search/repositories?q=${encodeURIComponent(query)}&per_page=10`);
1894
1919
  report.github.searches++;
@@ -1915,7 +1940,7 @@ async function refreshSignals(entries, registries, previous, client, now, budget
1915
1940
  }
1916
1941
  return true;
1917
1942
  });
1918
- const repoTodo = repoStale.slice(0, budgets.githubRepos);
1943
+ const repoTodo = repoStale.slice(0, spend.repoCalls);
1919
1944
  await pool(repoTodo, async (repo) => {
1920
1945
  if (!client.hasBudget("github-core")) return;
1921
1946
  const result = await client.json(`https://api.github.com/repos/${repo}`);
@@ -1931,12 +1956,35 @@ async function refreshSignals(entries, registries, previous, client, now, budget
1931
1956
  report.github.failed++;
1932
1957
  return;
1933
1958
  }
1934
- const body = parsed.data;
1935
- github[repo] = { found: true, repo: body.full_name, htmlUrl: body.html_url, stars: body.stargazers_count, pushedAt: body.pushed_at, archived: body.archived, spdx: spdxOrNull(body.license?.spdx_id), fetchedAt: at };
1959
+ github[repo] = githubSignal(parsed.data, at);
1936
1960
  });
1937
1961
  report.github.leftUnknown = repos.filter((repo) => !github[repo]).length;
1938
1962
  return { signals: { npm, github, registryRepos }, report };
1939
1963
  }
1964
+ function githubSignal(body, at) {
1965
+ return { found: true, repo: body.full_name, htmlUrl: body.html_url, stars: body.stargazers_count, pushedAt: body.pushed_at, archived: body.archived, spdx: spdxOrNull(body.license?.spdx_id), fetchedAt: at };
1966
+ }
1967
+ var HOMEPAGE_REPOS = 3;
1968
+ async function homepageRepo(homepage, client, github, at, report, spend) {
1969
+ const page = await client.get(homepage, "text/html");
1970
+ client.cache.delete(homepage);
1971
+ if (!page.ok) return null;
1972
+ for (const repo of linkedRepos(page.body).slice(0, HOMEPAGE_REPOS)) {
1973
+ if (spend.repoCalls <= 0 || !client.hasBudget("github-core")) return null;
1974
+ spend.repoCalls--;
1975
+ const result = await client.json(`https://api.github.com/repos/${repo}`);
1976
+ report.github.looked++;
1977
+ if (!result.ok) {
1978
+ if (result.reason === "rate-limited") report.github.rateLimited = true;
1979
+ continue;
1980
+ }
1981
+ const parsed = GithubRepoBody.safeParse(result.value);
1982
+ if (!parsed.success || !sameSite(parsed.data.homepage, homepage)) continue;
1983
+ github[parsed.data.full_name] = githubSignal(parsed.data, at);
1984
+ return parsed.data.full_name;
1985
+ }
1986
+ return null;
1987
+ }
1940
1988
  function repoOf(entry, signals) {
1941
1989
  const registry = registryOf(entry);
1942
1990
  if (registry) return signals.registryRepos[registry]?.repo ?? null;
@@ -2021,7 +2069,6 @@ async function syncIndex(options) {
2021
2069
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
2022
2070
  const builtAt = now().toISOString();
2023
2071
  const cache = await readHttpCache(options.cacheDir);
2024
- const previousBodies = new Map([...cache].map(([url, c]) => [url, c.sha256]));
2025
2072
  const client = new PoliteClient({
2026
2073
  ...options.fetcher ? { fetcher: options.fetcher } : {},
2027
2074
  ...options.token ? { token: options.token } : {},
@@ -2029,7 +2076,6 @@ async function syncIndex(options) {
2029
2076
  perHost: options.perHost ?? 2,
2030
2077
  timeoutMs: options.timeoutMs ?? 15e3,
2031
2078
  buckets: githubBuckets(options.token),
2032
- bucketFor: githubBucketFor,
2033
2079
  cache,
2034
2080
  now,
2035
2081
  ...options.sleep ? { sleep: options.sleep } : {}
@@ -2055,7 +2101,7 @@ async function syncIndex(options) {
2055
2101
  failure = "could not be parsed";
2056
2102
  }
2057
2103
  }
2058
- sources.push(failure === null && result.ok ? { id: `headless:${catalog.id}`, url: catalog.url, outcome: result.revalidated ? "revalidated" : "fetched", reason: null, httpStatus: result.status, items: names.length, health: null } : { id: `headless:${catalog.id}`, url: catalog.url, outcome: "failed", reason: failure, httpStatus: result.ok ? result.status : result.status, items: 0, health: null });
2104
+ sources.push(failure === null && result.ok ? { id: `headless:${catalog.id}`, url: catalog.url, outcome: result.revalidated ? "revalidated" : "fetched", reason: null, httpStatus: result.status, items: names.length, health: null } : { id: `headless:${catalog.id}`, url: catalog.url, outcome: "failed", reason: failure, httpStatus: result.status, items: 0, health: null });
2059
2105
  return { id: catalog.id, names };
2060
2106
  })
2061
2107
  );
@@ -2086,6 +2132,8 @@ async function syncIndex(options) {
2086
2132
  else if (options.maxRegistries !== void 0 && toFetch.length >= options.maxRegistries) sources.push({ id: registry.name, url: registryUrl(registry.template), outcome: "skipped", reason: "over --max-registries", httpStatus: null, items: 0, health: healthOf(registry) });
2087
2133
  else toFetch.push(registry);
2088
2134
  }
2135
+ const missing = directory2.ok ? selected.filter((name) => !listed.some((r) => r.name === name)) : [];
2136
+ for (const name of missing) sources.push({ id: name, url: SHADCN.directory, outcome: "skipped", reason: "not in the registry directory", httpStatus: null, items: 0, health: null });
2089
2137
  const community = await Promise.all(
2090
2138
  toFetch.map(async (registry) => {
2091
2139
  const url = registryUrl(registry.template);
@@ -2138,7 +2186,7 @@ async function syncIndex(options) {
2138
2186
  const index = cleanData({ schema: "youi.discover-index/v1", format: 2, builtAt, sources, categories: Object.fromEntries(categories), entries: final });
2139
2187
  await writeJson(join3(options.cacheDir, SIGNALS_FILE), signals);
2140
2188
  await writeJson(join3(options.cacheDir, HEALTH_FILE), cleanData({ checkedAt: builtAt, sources }));
2141
- await writeHttpCache(options.cacheDir, client.cache, previousBodies);
2189
+ await writeHttpCache(options.cacheDir, client.cache);
2142
2190
  await writeJson(join3(options.cacheDir, INDEX_FILE2), index);
2143
2191
  const registryStatuses = sources.filter((s) => s.health !== null);
2144
2192
  const known = final.filter((e) => e.license && e.license !== "unknown").length;
@@ -2147,7 +2195,7 @@ async function syncIndex(options) {
2147
2195
  catalogBytes: await catalogBytes(options.cacheDir),
2148
2196
  selectedSources: selected.length,
2149
2197
  index: { bytes: Buffer.byteLength(JSON.stringify(index) + "\n"), entries: final.length, mirrorsCollapsed, iconRegistriesSkipped: sources.filter((s) => s.reason === iconReason).length },
2150
- warnings: client.warnings,
2198
+ warnings: [...client.warnings, ...missing.map((name) => `${name} is chosen but the registry directory no longer lists it: \`youiyoux discover sources remove ${name}\``)],
2151
2199
  builtAt,
2152
2200
  durationMs: Math.round(performance.now() - started),
2153
2201
  entries: { total: final.length, official: final.filter((e) => e.source.ref?.startsWith(`${SHADCN.name}/`)).length, community: final.filter((e) => e.source.kind === "shadcn-registry" && !e.source.ref?.startsWith(`${SHADCN.name}/`)).length, npm: final.filter((e) => e.source.kind === "npm").length },
@@ -2197,7 +2245,7 @@ async function expandAll(cacheDir, options = {}) {
2197
2245
  const entries2 = parseRegistryItems(response.value).items.filter(isCandidate).map((item) => registryEntry({ name: r.name, homepage: r.homepage, url, fetchedAt: response.result.fetchedAt, official: false, health: r.health }, item, categories));
2198
2246
  return !options.icons && entries2.length && entries2.filter(isIconEntry).length / entries2.length >= 0.8 ? [] : entries2.map((entry) => applySignals(entry, signals));
2199
2247
  }));
2200
- await writeHttpCache(cacheDir, cache, /* @__PURE__ */ new Map());
2248
+ await writeHttpCache(cacheDir, cache);
2201
2249
  const chosen = new Set(index.entries.map((e) => e.id));
2202
2250
  const entries = dedupe([...index.entries, ...extra.flat()]).entries.filter((e) => !chosen.has(e.id));
2203
2251
  const next = `${JSON.stringify(cleanData({ schema: index.schema, format: 2, builtAt: index.builtAt, sources: [], categories: index.categories, entries }))}
@@ -2259,9 +2307,6 @@ function rolesOf(entry) {
2259
2307
  const f = fieldsOf(entry);
2260
2308
  return CONCEPTS.filter((concept) => conceptScore(concept, f, []) > 0).map((c) => c.id);
2261
2309
  }
2262
- function licenceOf(entry) {
2263
- return entry.license && entry.license !== "unknown" ? entry.license.spdx : "unknown";
2264
- }
2265
2310
  function searchTextOf(entry) {
2266
2311
  const f = fieldsOf(entry);
2267
2312
  return [f.name, f.name, f.name, f.title, f.title, f.description, ...f.categories, ...f.packages.map(normalize)].filter(Boolean).join(" ");
@@ -2388,7 +2433,7 @@ function needTerms(need) {
2388
2433
  }
2389
2434
  async function searchCatalog(store, need, options = {}) {
2390
2435
  const filter = filterOf(options);
2391
- const rows = await store.matchDiscover({ ...filter, terms: needTerms(need), perClass: options.perClass ?? CANDIDATES_PER_CLASS });
2436
+ const rows = await store.matchDiscover({ ...filter, terms: needTerms(need), perClass: CANDIDATES_PER_CLASS });
2392
2437
  const ranked = rankEntries(rows.map((r) => r.entry), need, options);
2393
2438
  const offset = pageInteger(options.offset, 0, 2147483647);
2394
2439
  const page = ranked.scored.slice(offset, offset + pageInteger(options.limit, 10));
@@ -2598,7 +2643,7 @@ async function preparePreview(cacheDir, id, options = {}) {
2598
2643
  await assertNoLinks(dir, file);
2599
2644
  await writeFileAtomic(join6(dir, file), text);
2600
2645
  }
2601
- const packages = [...new Set([...rewritten.values()].flatMap((t) => importedPackages(t)))].filter((p) => !p.startsWith("/")).sort();
2646
+ const packages = [...new Set([...rewritten.values()].flatMap((t) => importedPackages(t)))].sort();
2602
2647
  const declared = [...new Set([...source.dependencies ?? entry.install?.npm ?? [], ...partDeps].map(packageName))].sort();
2603
2648
  const sources = [...rewritten.entries()].filter(([f]) => /\.(?:tsx|jsx|ts|js)$/.test(f)).map(([, t]) => t);
2604
2649
  return {
@@ -2618,9 +2663,138 @@ async function preparePreview(cacheDir, id, options = {}) {
2618
2663
  };
2619
2664
  }
2620
2665
 
2666
+ // ../discover/src/gaps.ts
2667
+ import { existsSync, readFileSync } from "node:fs";
2668
+ import { join as join7 } from "node:path";
2669
+ var STANDARD_KIT = ["button", "input", "card", "badge", "dialog", "alert", "drawer", "menu", "popover", "tooltip", "tabs", "avatar", "accordion", "table", "carousel", "slider", "switch", "checkbox", "radio", "select", "otp", "progress", "skeleton", "spinner", "sidebar", "navigation", "breadcrumb", "pagination", "command", "toast", "date-picker", "combobox", "chart"];
2670
+ var ONLY_EXACT = /* @__PURE__ */ new Set(["hint", "tile", "range", "loading", "picker", "tree", "graph", "plot", "editor", "cta", "notice", "steps", "tag", "toggle", "meter", "command", "banner", "popup", "pager", "loader", "navigation", "chip", "pill"]);
2671
+ var ROLE_CONCEPT2 = { button: "button", "link-as-button": "button", input: "input", card: "card", badge: "badge", dialog: "dialog", tabs: "tabs" };
2672
+ var CANDIDATES = 3;
2673
+ var DEFINITIONS_SHOWN = 5;
2674
+ function conceptOfName(name) {
2675
+ const words2 = normalize(name.replace(/\.[a-z]+$/, ""));
2676
+ if (!words2) return void 0;
2677
+ const phrases = CONCEPTS.flatMap((concept) => concept.names.map((n) => ({ concept, name: normalize(n) }))).sort((a, b) => b.name.length - a.name.length);
2678
+ return phrases.find((p) => words2 === p.name || !ONLY_EXACT.has(p.name) && ` ${words2}`.endsWith(` ${p.name}`))?.concept;
2679
+ }
2680
+ function conceptOfPackage(pkg) {
2681
+ const listed = CONCEPTS.find((c) => c.packages.includes(pkg));
2682
+ if (listed) return listed;
2683
+ const last = pkg.split("/").at(-1).replace(/^react-/, "");
2684
+ return conceptForRole(last);
2685
+ }
2686
+ function declaredPackages(roots) {
2687
+ const found = {};
2688
+ for (const [id, root] of roots) {
2689
+ const file = join7(root, "package.json");
2690
+ if (!existsSync(file)) continue;
2691
+ try {
2692
+ const pkg = JSON.parse(readFileSync(file, "utf8"));
2693
+ found[id] = [...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {})].sort();
2694
+ } catch {
2695
+ }
2696
+ }
2697
+ return found;
2698
+ }
2699
+ var fresh2 = () => ({ system: /* @__PURE__ */ new Set(), packages: /* @__PURE__ */ new Set(), definitions: [], uses: 0, files: /* @__PURE__ */ new Set() });
2700
+ async function candidatesFor(store, concept) {
2701
+ const { hits } = await searchCatalog(store, concept.names[0], { role: concept.id, limit: 12 });
2702
+ return hits.filter((hit) => hit.license !== "unknown").map((hit) => ({ id: hit.id, name: hit.name, source: hit.source.name, licence: hit.license, copyleft: copyleft(hit.license), install: hit.install?.command ?? null })).sort((a, b) => Number(a.copyleft) - Number(b.copyleft)).slice(0, CANDIDATES);
2703
+ }
2704
+ var ORDER = { "hand-built": 0, missing: 1, library: 2, system: 3 };
2705
+ var KIT_ORDER = new Map(STANDARD_KIT.map((id, i) => [id, i]));
2706
+ async function buildGaps(source, store) {
2707
+ const { inventory } = source;
2708
+ const systemByConcept = /* @__PURE__ */ new Map();
2709
+ for (const name of source.system?.components ?? []) {
2710
+ const concept = conceptOfName(name);
2711
+ if (concept) systemByConcept.set(concept.id, [...systemByConcept.get(concept.id) ?? [], name]);
2712
+ }
2713
+ const candidates = /* @__PURE__ */ new Map();
2714
+ const offer = (concept) => {
2715
+ if (!store) return null;
2716
+ let found = candidates.get(concept.id);
2717
+ if (!found) candidates.set(concept.id, found = candidatesFor(store, concept));
2718
+ return found;
2719
+ };
2720
+ const projects = [];
2721
+ for (const project of inventory.projects) {
2722
+ const tallies = /* @__PURE__ */ new Map();
2723
+ const tally2 = (id) => {
2724
+ let t = tallies.get(id);
2725
+ if (!t) tallies.set(id, t = fresh2());
2726
+ return t;
2727
+ };
2728
+ for (const [id, names] of systemByConcept) for (const name of names) tally2(id).system.add(name);
2729
+ for (const primitive of inventory.primitives.filter((p) => p.project === project.id)) {
2730
+ const concept = conceptOfPackage(primitive.package);
2731
+ if (concept) tally2(concept.id).packages.add(primitive.package);
2732
+ }
2733
+ for (const pkg of source.dependencies[project.id] ?? []) {
2734
+ const concept = CONCEPTS.find((c) => c.packages.includes(pkg));
2735
+ if (concept) tally2(concept.id).packages.add(pkg);
2736
+ }
2737
+ const ownConcept = /* @__PURE__ */ new Map();
2738
+ for (const definition of inventory.definitions.filter((d) => d.project === project.id)) {
2739
+ const concept = conceptOfName(definition.name);
2740
+ if (!concept) continue;
2741
+ ownConcept.set(definition.id, concept.id);
2742
+ const t = tally2(concept.id);
2743
+ t.definitions.push({ name: definition.name, file: definition.file, line: definition.line });
2744
+ t.files.add(definition.file);
2745
+ }
2746
+ for (const look of inventory.looks.filter((l) => l.project === project.id && !l.story)) {
2747
+ const id = look.definition ? ownConcept.get(look.definition) : look.source === "intrinsic" ? ROLE_CONCEPT2[look.role] : void 0;
2748
+ if (!id) continue;
2749
+ const t = tally2(id);
2750
+ t.uses += 1;
2751
+ t.files.add(look.file);
2752
+ }
2753
+ const concepts = [];
2754
+ for (const concept of CONCEPTS) {
2755
+ const t = tallies.get(concept.id) ?? fresh2();
2756
+ const hand = t.definitions.length > 0 || t.uses > 0;
2757
+ const status2 = t.system.size ? "system" : t.packages.size ? "library" : hand ? "hand-built" : KIT_ORDER.has(concept.id) ? "missing" : null;
2758
+ if (!status2) continue;
2759
+ const offered = status2 === "hand-built" || status2 === "missing" ? offer(concept) : null;
2760
+ concepts.push({
2761
+ concept: concept.id,
2762
+ name: concept.names[0],
2763
+ status: status2,
2764
+ system: [...t.system].sort(),
2765
+ packages: [...t.packages].sort(),
2766
+ definitions: t.definitions.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line).slice(0, DEFINITIONS_SHOWN),
2767
+ count: t.definitions.length,
2768
+ uses: t.uses,
2769
+ files: t.files.size,
2770
+ candidates: offered ? await offered : status2 === "hand-built" || status2 === "missing" ? null : []
2771
+ });
2772
+ }
2773
+ concepts.sort((a, b) => ORDER[a.status] - ORDER[b.status] || (a.status === "hand-built" ? b.uses - a.uses || b.count - a.count : 0) || (KIT_ORDER.get(a.concept) ?? 99) - (KIT_ORDER.get(b.concept) ?? 99) || a.concept.localeCompare(b.concept));
2774
+ projects.push({ id: project.id, complete: project.status === "complete", dependencies: source.dependencies[project.id] ? "read" : "unread", concepts });
2775
+ }
2776
+ const across = /* @__PURE__ */ new Map();
2777
+ for (const project of projects) {
2778
+ for (const c of project.concepts.filter((c2) => c2.status === "hand-built")) {
2779
+ const entry = across.get(c.concept) ?? { name: c.name, projects: [], uses: 0 };
2780
+ entry.projects.push(project.id);
2781
+ entry.uses += c.uses;
2782
+ across.set(c.concept, entry);
2783
+ }
2784
+ }
2785
+ return {
2786
+ schema: "youi.gaps/v1",
2787
+ generatedAt: (source.now ?? /* @__PURE__ */ new Date()).toISOString(),
2788
+ system: source.system?.name ?? null,
2789
+ discover: store ? "synced" : "not-synced",
2790
+ kit: [...STANDARD_KIT],
2791
+ projects,
2792
+ across: [...across].filter(([, e]) => e.projects.length > 1).map(([concept, e]) => ({ concept, ...e })).sort((a, b) => b.uses - a.uses || a.concept.localeCompare(b.concept))
2793
+ };
2794
+ }
2795
+
2621
2796
  export {
2622
2797
  CONCEPTS,
2623
- conceptById,
2624
2798
  normalize,
2625
2799
  hasPhrase,
2626
2800
  readNeed,
@@ -2669,7 +2843,6 @@ export {
2669
2843
  discoverIndexPath,
2670
2844
  SourceStatus,
2671
2845
  DiscoverIndex,
2672
- EMPTY_SIGNALS,
2673
2846
  writeJson,
2674
2847
  MissingIndexError,
2675
2848
  readIndex,
@@ -2696,6 +2869,7 @@ export {
2696
2869
  registryName,
2697
2870
  passes,
2698
2871
  playsRole,
2872
+ licenceOf,
2699
2873
  hitOf,
2700
2874
  isIconEntry,
2701
2875
  rankEntries,
@@ -2706,6 +2880,8 @@ export {
2706
2880
  keyPackage,
2707
2881
  registryOf,
2708
2882
  sameHomepage,
2883
+ sameSite,
2884
+ linkedRepos,
2709
2885
  refreshSignals,
2710
2886
  applySignals,
2711
2887
  dedupe,
@@ -2718,7 +2894,6 @@ export {
2718
2894
  LINKS_PER_ROLE,
2719
2895
  sourceClassOf,
2720
2896
  rolesOf,
2721
- licenceOf,
2722
2897
  searchTextOf,
2723
2898
  itemOf,
2724
2899
  ingestCatalog,
@@ -2736,5 +2911,10 @@ export {
2736
2911
  safeRelative,
2737
2912
  exportNames,
2738
2913
  importedNames,
2739
- preparePreview
2914
+ preparePreview,
2915
+ STANDARD_KIT,
2916
+ conceptOfName,
2917
+ conceptOfPackage,
2918
+ declaredPackages,
2919
+ buildGaps
2740
2920
  };