youiyoux 0.1.13 → 0.1.14

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 (77) hide show
  1. package/dist/chunks/{chunk-ZWVWENSB.js → chunk-A2IZOTJG.js} +1 -1
  2. package/dist/chunks/{chunk-5I2VHSEN.js → chunk-ALQVHSF2.js} +1 -1
  3. package/dist/chunks/{chunk-EV62TUDV.js → chunk-EN64OSUT.js} +230 -288
  4. package/dist/chunks/{chunk-DX2NKY2M.js → chunk-F3KCL226.js} +4 -4
  5. package/dist/chunks/chunk-FTF4MBV5.js +110 -0
  6. package/dist/chunks/{chunk-PCLXMWZ2.js → chunk-HII2I2PI.js} +3 -3
  7. package/dist/chunks/{chunk-XOXF2ILO.js → chunk-HIXGUALJ.js} +1 -1
  8. package/dist/chunks/{chunk-N5Z5A637.js → chunk-JGEVNQ5R.js} +2 -2
  9. package/dist/chunks/{chunk-PWGZRP3C.js → chunk-MIUG2Z43.js} +180 -8
  10. package/dist/chunks/{chunk-NF4OJYKC.js → chunk-S7UN5KQK.js} +1 -1
  11. package/dist/chunks/{chunk-ZOQW6LNH.js → chunk-TPP4HLDB.js} +446 -29
  12. package/dist/chunks/ingest-OF4Z33L2.js +52 -0
  13. package/dist/chunks/{src-HVYMF4S2.js → src-3LEBH7E3.js} +7 -7
  14. package/dist/chunks/{src-IKDQ2BZO.js → src-4BOFDAZD.js} +1 -1
  15. package/dist/chunks/{src-4QOHR5GC.js → src-6JO73FOS.js} +3 -3
  16. package/dist/chunks/{src-E6ZPFKPR.js → src-7LETTRRF.js} +23 -3
  17. package/dist/chunks/{src-PBM76LFQ.js → src-7SHBAJEO.js} +2 -2
  18. package/dist/chunks/{src-R7JGKOYI.js → src-CCGTRT5J.js} +18 -4
  19. package/dist/chunks/{src-624TVB6T.js → src-LUPO3O76.js} +2 -2
  20. package/dist/chunks/{src-YC64NNQ3.js → src-WMGLZKYK.js} +2 -2
  21. package/dist/hub/assets/{CandidatePanel-DfntneYT.js → CandidatePanel-BbE2xCN2.js} +1 -1
  22. package/dist/hub/assets/CatalogScreen-CBlaW1-N.js +1 -0
  23. package/dist/hub/assets/ChangesScreen-WaA9Y7zq.js +1 -0
  24. package/dist/hub/assets/{CheatSheet-B1dXTYTW.js → CheatSheet-CGfTS7zw.js} +1 -1
  25. package/dist/hub/assets/{CommandPalette-LBZHJ659.js → CommandPalette-D4WigyuD.js} +1 -1
  26. package/dist/hub/assets/{DiscoverScreen-Yd3gVUG4.js → DiscoverScreen-CVtcQCM4.js} +2 -2
  27. package/dist/hub/assets/DraftEditor-Mp1bT8-o.js +5 -0
  28. package/dist/hub/assets/{GapsScreen-DPYGlNnA.js → GapsScreen-DzB0TfH3.js} +1 -1
  29. package/dist/hub/assets/{HomeScreen-BCPPk7mz.js → HomeScreen-BVxjYDtc.js} +1 -1
  30. package/dist/hub/assets/InventoryScreen-D33_YchG.js +1 -0
  31. package/dist/hub/assets/{LibraryScreen-BVXhOVPU.js → LibraryScreen-B8lAJBGW.js} +1 -1
  32. package/dist/hub/assets/{Opened-CJyMbxqV.js → Opened-DgNa-hGA.js} +1 -1
  33. package/dist/hub/assets/{OrgApp-Dxs_7CxZ.js → OrgApp-Q8YNFE-z.js} +1 -1
  34. package/dist/hub/assets/{Preview-DWFP0BIe.js → Preview-yu3LaZKL.js} +1 -1
  35. package/dist/hub/assets/{RuleDetail-C2fTlLJL.js → RuleDetail-BqVdc20s.js} +1 -1
  36. package/dist/hub/assets/{RulesScreen-BABeAGJg.js → RulesScreen-mb3OUlOB.js} +1 -1
  37. package/dist/hub/assets/{ScreenImage-CyhKm_nj.js → ScreenImage-DUMSgBJL.js} +1 -1
  38. package/dist/hub/assets/{ScreensScreen-8Ih8byGy.js → ScreensScreen-DjN5mwSP.js} +1 -1
  39. package/dist/hub/assets/{SearchField-hF-6-YDM.js → SearchField-ByftoJws.js} +1 -1
  40. package/dist/hub/assets/{Segmented-Dzi1x49S.js → Segmented-CTMaLPgh.js} +1 -1
  41. package/dist/hub/assets/{ServiceDown-S4ya0Cob.js → ServiceDown-BghtjpTn.js} +1 -1
  42. package/dist/hub/assets/{SpecimenCapture-e_pgT4Sp.js → SpecimenCapture-CglZ4bn-.js} +1 -1
  43. package/dist/hub/assets/{Specimens-D_9hjlJH.js → Specimens-D5ehj5-O.js} +2 -2
  44. package/dist/hub/assets/{StackScreen-J2FQilLW.js → StackScreen-w5hCLj_R.js} +1 -1
  45. package/dist/hub/assets/{StubScreens-DAkMph3i.js → StubScreens-DfF4aAbp.js} +1 -1
  46. package/dist/hub/assets/{StyleScreen-C5WytSZU.js → StyleScreen-DKkerxmC.js} +1 -1
  47. package/dist/hub/assets/{Tabs-DU-fFRDV.js → Tabs-B1IFs-wv.js} +1 -1
  48. package/dist/hub/assets/TokenDetail-BPTZ9o6J.js +1 -0
  49. package/dist/hub/assets/TokensScreen-CKnQuVaT.js +1 -0
  50. package/dist/hub/assets/{WorkspaceScreen-CzZ16fPZ.js → WorkspaceScreen-DwjGW6eY.js} +1 -1
  51. package/dist/hub/assets/{client-Bdgr12LP.js → client-uMxZXiDl.js} +1 -1
  52. package/dist/hub/assets/{discover-va2KFuK_.js → discover-Yfb0eB14.js} +1 -1
  53. package/dist/hub/assets/{dist-CC_p8DIj.js → dist-BvRlRM3Z.js} +1 -1
  54. package/dist/hub/assets/{dist-C8tf4g8c.js → dist-CL3qgCbi.js} +1 -1
  55. package/dist/hub/assets/{dist-YTWqg5sS.js → dist-tjVfXRHc.js} +1 -1
  56. package/dist/hub/assets/{family-tiles-qaK1-_c8.js → family-tiles-dZqjsxro.js} +1 -1
  57. package/dist/hub/assets/link-2-dzvhLwTC.js +1 -0
  58. package/dist/hub/assets/main-DNLr0KzQ.css +2 -0
  59. package/dist/hub/assets/main-Djqho539.js +2 -0
  60. package/dist/hub/assets/{try-live-Iys3nCBG.js → try-live-Dxsjf62v.js} +1 -1
  61. package/dist/hub/assets/{workflow-C53UBmRZ.js → workflow-Bv814-L8.js} +1 -1
  62. package/dist/hub/index.html +2 -2
  63. package/dist/hub-previews.js +6 -6
  64. package/dist/hub-server.js +3 -3
  65. package/dist/hub-snapshot.js +1 -1
  66. package/dist/schema/youi.library.schema.json +3 -1
  67. package/dist/youi.js +8 -7
  68. package/dist/youiyoux.js +8 -7
  69. package/package.json +4 -4
  70. package/dist/hub/assets/CatalogScreen-kpFYZqX3.js +0 -1
  71. package/dist/hub/assets/ChangesScreen-Chu-Ui1B.js +0 -1
  72. package/dist/hub/assets/DraftEditor-QH9Sa_Uc.js +0 -5
  73. package/dist/hub/assets/InventoryScreen-SleB733C.js +0 -1
  74. package/dist/hub/assets/TokenDetail-CDY1xxb9.js +0 -1
  75. package/dist/hub/assets/TokensScreen-Sx4aICWk.js +0 -1
  76. package/dist/hub/assets/main-cvOFSjox.js +0 -2
  77. package/dist/hub/assets/main-pNY4iNRo.css +0 -2
@@ -6,15 +6,19 @@ import {
6
6
  openStore,
7
7
  pageInteger,
8
8
  searchTerms
9
- } from "./chunk-ZWVWENSB.js";
9
+ } from "./chunk-A2IZOTJG.js";
10
10
  import {
11
11
  LIBRARY_SOURCE_KINDS,
12
12
  LibraryEntry,
13
13
  copyleft,
14
14
  external_exports,
15
+ require_dist,
15
16
  sha256,
16
17
  writeFileAtomic
17
- } from "./chunk-NF4OJYKC.js";
18
+ } from "./chunk-S7UN5KQK.js";
19
+ import {
20
+ __toESM
21
+ } from "./chunk-WNTS7ZO4.js";
18
22
 
19
23
  // ../discover/src/aliases.ts
20
24
  var CONCEPTS = [
@@ -63,11 +67,11 @@ var CONCEPTS = [
63
67
  { id: "marquee", names: ["marquee", "ticker", "scrolling text"], packages: [] }
64
68
  ];
65
69
  var BY_ID = new Map(CONCEPTS.map((c) => [c.id, c]));
66
- function normalize(text) {
67
- return text.normalize("NFC").replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
70
+ function normalize(text2) {
71
+ return text2.normalize("NFC").replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
68
72
  }
69
- function hasPhrase(text, phrase) {
70
- return ` ${text} `.includes(` ${phrase} `);
73
+ function hasPhrase(text2, phrase) {
74
+ return ` ${text2} `.includes(` ${phrase} `);
71
75
  }
72
76
  var STOP = /* @__PURE__ */ new Set(["a", "an", "the", "for", "with", "and", "or", "of", "to", "in", "component", "components", "ui", "react"]);
73
77
  function readNeed(need) {
@@ -106,8 +110,8 @@ function publicHttps(value) {
106
110
  }
107
111
 
108
112
  // ../discover/src/text.ts
109
- function cleanText(text) {
110
- return text.replace(/(?:\x1b\]|\x9d)[\s\S]*?(?:\x07|\x1b\\|\x9c|$)/g, "").replace(/(?:\x1b[PX^_]|[\x90\x98\x9e\x9f])[\s\S]*?(?:\x1b\\|\x9c|$)/g, "").replace(/(?:\x1b\[|\x9b)[0-?]*[ -/]*[@-~]/g, "").replace(/\x1b[ -/]*[0-~]/g, "").replace(/[\x00-\x08\x0b-\x1f\x7f-\x9f]/g, "");
113
+ function cleanText(text2) {
114
+ return text2.replace(/(?:\x1b\]|\x9d)[\s\S]*?(?:\x07|\x1b\\|\x9c|$)/g, "").replace(/(?:\x1b[PX^_]|[\x90\x98\x9e\x9f])[\s\S]*?(?:\x1b\\|\x9c|$)/g, "").replace(/(?:\x1b\[|\x9b)[0-?]*[ -/]*[@-~]/g, "").replace(/\x1b[ -/]*[0-~]/g, "").replace(/[\x00-\x08\x0b-\x1f\x7f-\x9f]/g, "");
111
115
  }
112
116
  function cleanData(value) {
113
117
  if (typeof value === "string") return cleanText(value);
@@ -245,8 +249,8 @@ function nameSlug(name) {
245
249
  return slug || "item";
246
250
  }
247
251
  function summaryOf(...texts) {
248
- const text = texts.map((t) => cleanText(t ?? "").replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim()).find((t) => t.length > 0) ?? "";
249
- return text.length > 200 ? `${text.slice(0, 199)}\u2026` : text;
252
+ const text2 = texts.map((t) => cleanText(t ?? "").replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim()).find((t) => t.length > 0) ?? "";
253
+ return text2.length > 200 ? `${text2.slice(0, 199)}\u2026` : text2;
250
254
  }
251
255
  function categoriesOf(item, headless) {
252
256
  const own = (item.categories ?? []).map((c) => c.toLowerCase());
@@ -347,8 +351,8 @@ function githubRepo(url) {
347
351
  return match ? `${match[1]}/${match[2]}` : null;
348
352
  }
349
353
  function answers(object, concept) {
350
- const text = normalize([object.package.name, object.package.description ?? "", ...object.package.keywords ?? []].join(" "));
351
- return concept.packages.includes(object.package.name) || concept.names.some((name) => hasPhrase(text, normalize(name)));
354
+ const text2 = normalize([object.package.name, object.package.description ?? "", ...object.package.keywords ?? []].join(" "));
355
+ return concept.packages.includes(object.package.name) || concept.names.some((name) => hasPhrase(text2, normalize(name)));
352
356
  }
353
357
  var MIN_WEEKLY_DOWNLOADS = 1e3;
354
358
  function npmCandidates(concept, objects) {
@@ -624,9 +628,10 @@ var PoliteClient = class {
624
628
  return this.options.now?.() ?? /* @__PURE__ */ new Date();
625
629
  }
626
630
  /** Replace the token wherever it appears, so no message, log line or file can carry it. */
627
- redact(text) {
628
- const token = this.options.token;
629
- return cleanText(token ? text.split(token).join("***") : text);
631
+ redact(text2) {
632
+ let clean = text2;
633
+ for (const token of [this.options.token, this.options.figmaToken]) if (token) clean = clean.split(token).join("***");
634
+ return cleanText(clean);
630
635
  }
631
636
  /** True when the bucket still has room in its window; a spent bucket stays spent for this run. */
632
637
  take(bucket) {
@@ -670,7 +675,7 @@ var PoliteClient = class {
670
675
  const gate = host;
671
676
  const request = () => this.gate.run(
672
677
  () => gate.run(async () => {
673
- if (npm && this.npmStarted) await (this.options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))))(500);
678
+ if (npm && this.npmStarted) await (this.options.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms))))(500);
674
679
  if (npm) this.npmStarted = true;
675
680
  if (!this.take(bucket)) {
676
681
  this.stats.rateLimited++;
@@ -680,7 +685,7 @@ var PoliteClient = class {
680
685
  const result = await this.send(url, parsed, bucket, accept);
681
686
  if (!npm || result.ok || result.status !== 429) return result;
682
687
  if (attempt === 4) return result;
683
- await (this.options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))))(Math.max(result.retryAfterMs ?? 0, Math.min(3e4, 1e3 * 2 ** attempt * (0.5 + (this.options.random ?? Math.random)()))));
688
+ await (this.options.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms))))(Math.max(result.retryAfterMs ?? 0, Math.min(3e4, 1e3 * 2 ** attempt * (0.5 + (this.options.random ?? Math.random)()))));
684
689
  }
685
690
  })
686
691
  );
@@ -692,6 +697,7 @@ var PoliteClient = class {
692
697
  if (cached?.etag) headers["if-none-match"] = cached.etag;
693
698
  if (cached?.lastModified) headers["if-modified-since"] = cached.lastModified;
694
699
  if (this.options.token && parsed.hostname === "api.github.com") headers.authorization = `Bearer ${this.options.token}`;
700
+ if (this.options.figmaToken && parsed.hostname === "api.figma.com") headers["x-figma-token"] = this.options.figmaToken;
695
701
  this.stats.sent++;
696
702
  this.inFlight++;
697
703
  this.stats.maxInFlight = Math.max(this.stats.maxInFlight, this.inFlight);
@@ -912,10 +918,10 @@ function rankEntries(entries, need, options = {}) {
912
918
  const f = fieldsOf(entry);
913
919
  if (options.role && !playsRole(options.role, f)) continue;
914
920
  const why = [];
915
- let text = 0;
916
- for (const concept of read.concepts) text += conceptScore(concept, f, why);
917
- for (const word of read.words) text += wordScore(word, f, why);
918
- if (text <= 0) continue;
921
+ let text2 = 0;
922
+ for (const concept of read.concepts) text2 += conceptScore(concept, f, why);
923
+ for (const word of read.words) text2 += wordScore(word, f, why);
924
+ if (text2 <= 0) continue;
919
925
  let suitability = 1;
920
926
  if (isIconEntry(entry)) {
921
927
  suitability *= 0.3;
@@ -926,7 +932,7 @@ function rankEntries(entries, need, options = {}) {
926
932
  suitability *= 0.3;
927
933
  why.push(`framework ${framework}`);
928
934
  }
929
- scored.push({ entry, score: (text * KIND_WEIGHT[entry.kind] + signalBoost(entry)) * suitability, why });
935
+ scored.push({ entry, score: (text2 * KIND_WEIGHT[entry.kind] + signalBoost(entry)) * suitability, why });
930
936
  }
931
937
  scored.sort((a, b) => b.score - a.score || a.entry.id.localeCompare(b.entry.id));
932
938
  return { need, concepts: read.concepts.map((c) => c.id), scored };
@@ -1751,8 +1757,8 @@ async function preparePreview(cacheDir, id, options = {}) {
1751
1757
  const queue = [...own];
1752
1758
  while (queue.length > 0) {
1753
1759
  const file = queue.shift();
1754
- const text = texts.get(file);
1755
- for (const match of text.matchAll(SPECIFIER)) {
1760
+ const text2 = texts.get(file);
1761
+ for (const match of text2.matchAll(SPECIFIER)) {
1756
1762
  const spec = match[3];
1757
1763
  if (!spec.startsWith("@/registry/") || resolved.has(spec)) continue;
1758
1764
  const local = [...texts.keys()].find((f) => f.replace(/\.(?:tsx?|jsx?)$/, "") === spec.slice(2) || f.replace(/\/index\.(?:tsx?|jsx?)$/, "") === spec.slice(2));
@@ -1793,17 +1799,17 @@ async function preparePreview(cacheDir, id, options = {}) {
1793
1799
  }
1794
1800
  }
1795
1801
  const rewritten = /* @__PURE__ */ new Map();
1796
- for (const [file, text] of texts) {
1797
- rewritten.set(file, text.replace(SPECIFIER, (all, lead, quote, spec) => {
1802
+ for (const [file, text2] of texts) {
1803
+ rewritten.set(file, text2.replace(SPECIFIER, (all, lead, quote, spec) => {
1798
1804
  const target = resolved.get(spec);
1799
1805
  if (!target || target === spec) return all;
1800
1806
  const next = target.startsWith("/") || /^[a-z]:[\\/]/i.test(target) ? target : relativeSpec(file, target);
1801
1807
  return `${lead}${quote}${next}${quote}`;
1802
1808
  }));
1803
1809
  }
1804
- for (const [file, text] of rewritten) {
1810
+ for (const [file, text2] of rewritten) {
1805
1811
  await assertNoLinks(dir, file);
1806
- await writeFileAtomic(join5(dir, file), text);
1812
+ await writeFileAtomic(join5(dir, file), text2);
1807
1813
  }
1808
1814
  const packages = [...new Set([...rewritten.values()].flatMap((t) => importedPackages(t)))].sort();
1809
1815
  const declared = [...new Set([...source.dependencies ?? entry.install?.npm ?? [], ...partDeps].map(packageName))].sort();
@@ -1955,6 +1961,407 @@ async function buildGaps(source, store) {
1955
1961
  };
1956
1962
  }
1957
1963
 
1964
+ // ../discover/src/ingest.ts
1965
+ import { readFile as readFile3 } from "node:fs/promises";
1966
+ import { basename as basename2, resolve } from "node:path";
1967
+
1968
+ // ../discover/src/ingest-design-md.ts
1969
+ var import_yaml = __toESM(require_dist(), 1);
1970
+ import { basename } from "node:path";
1971
+ var GROUP_TITLE = { colors: "Colors", typography: "Typography", rounded: "Corner radius", spacing: "Spacing", elevation: "Elevation", shadows: "Shadows", motion: "Motion" };
1972
+ var NOT_TOKENS = /* @__PURE__ */ new Set(["name", "version", "description", "components", "$schema"]);
1973
+ var SAMPLES = 4;
1974
+ var title = (key) => key.split(/[-_\s]+/).filter(Boolean).map((w) => w[0].toUpperCase() + w.slice(1)).join(" ");
1975
+ function sample(name, value) {
1976
+ if (value && typeof value === "object") {
1977
+ const v = value;
1978
+ return `${name} ${[v.fontSize, v.lineHeight, v.fontWeight].filter((x) => x !== void 0).join("/")}`.trim();
1979
+ }
1980
+ return `${name} ${String(value)}`;
1981
+ }
1982
+ function bodyComponents(body) {
1983
+ const lines = body.split(/\r?\n/);
1984
+ const found = [];
1985
+ let level = 0;
1986
+ for (let i = 0; i < lines.length; i++) {
1987
+ const heading = /^(#{1,6})\s+(.+?)\s*#*$/.exec(lines[i]);
1988
+ if (heading) {
1989
+ const depth = heading[1].length;
1990
+ if (/\bcomponents?\b/i.test(heading[2])) {
1991
+ level = depth;
1992
+ continue;
1993
+ }
1994
+ if (level > 0 && depth <= level) level = 0;
1995
+ else if (level > 0 && depth > level) {
1996
+ const text2 = lines.slice(i + 1).find((l) => l.trim() && !l.startsWith("#") && !l.startsWith("|"));
1997
+ found.push({ name: heading[2].replace(/[`*_]/g, ""), summary: text2?.trim() ?? "" });
1998
+ }
1999
+ continue;
2000
+ }
2001
+ if (level === 0) continue;
2002
+ const row = /^\|(.+)\|\s*$/.exec(lines[i]);
2003
+ if (!row || /^\|[\s:-]+\|/.test(lines[i])) continue;
2004
+ const cells = row[1].split("|").map((c) => c.trim());
2005
+ const name = cells[0].replace(/[`*_]/g, "");
2006
+ if (!name || /^(component|name|element)s?$/i.test(name)) continue;
2007
+ found.push({ name, summary: cells.slice(1).filter((c) => c && c !== "\u2014" && !c.startsWith("`")).at(-1) ?? "" });
2008
+ }
2009
+ return found;
2010
+ }
2011
+ async function readDesignMd(target, client, fetchedAt, readLocal) {
2012
+ const text2 = target.local ? await readLocal(target.location).then((body2) => ({ body: body2, sha256: digestOf(body2) }), () => {
2013
+ throw new IngestError(`could not read ${basename(target.location)}`);
2014
+ }) : await fetchText(client, target.location, "the DESIGN.md");
2015
+ const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(text2.body);
2016
+ let front = {};
2017
+ if (match) {
2018
+ try {
2019
+ const parsed = (0, import_yaml.parse)(match[1]);
2020
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) front = parsed;
2021
+ } catch (error) {
2022
+ throw new IngestError(`the DESIGN.md's frontmatter is not YAML: ${error instanceof Error ? error.message.split("\n")[0] : String(error)}`);
2023
+ }
2024
+ }
2025
+ const body = match ? match[2] : text2.body;
2026
+ const heading = /^#\s+(.+)$/m.exec(body)?.[1]?.replace(/^Design System:\s*/i, "").trim();
2027
+ const name = typeof front.name === "string" && front.name.trim() || heading || (target.local ? basename(target.location, ".md") : new URL(target.location).host);
2028
+ const url = target.local ? null : target.location;
2029
+ const source = { kind: "design-md", name, url };
2030
+ const file = target.local ? basename(target.location) : new URL(target.location).pathname.split("/").at(-1) ?? "DESIGN.md";
2031
+ const inputs = [];
2032
+ for (const [group, value] of Object.entries(front)) {
2033
+ if (NOT_TOKENS.has(group) || !value || typeof value !== "object" || Array.isArray(value)) continue;
2034
+ const tokens = Object.entries(value);
2035
+ if (tokens.length === 0) continue;
2036
+ const samples = tokens.slice(0, SAMPLES).map(([k, v]) => sample(k, v)).join(", ");
2037
+ inputs.push({ kind: "token", name: GROUP_TITLE[group] ?? title(group), summary: `${tokens.length} ${group} token${tokens.length === 1 ? "" : "s"}: ${samples}${tokens.length > SAMPLES ? ", \u2026" : ""}`, ref: `${file}#${group}`, url, digest: digestOf(value), categories: [group] });
2038
+ }
2039
+ const components = /* @__PURE__ */ new Map();
2040
+ const keys = Object.keys(front.components ?? {});
2041
+ for (const key of keys) {
2042
+ const base = keys.filter((other) => other !== key && key.startsWith(`${other}-`)).sort((a, b) => a.length - b.length)[0] ?? key;
2043
+ const entry = components.get(base.toLowerCase()) ?? { name: title(base), summary: "", digest: digestOf(front.components[base]) };
2044
+ if (base !== key) entry.summary = `${entry.summary ? `${entry.summary}, ` : "Variants: "}${key.slice(base.length + 1)}`;
2045
+ components.set(base.toLowerCase(), entry);
2046
+ }
2047
+ for (const found of bodyComponents(body)) {
2048
+ const key = found.name.toLowerCase().replace(/\s+/g, "-");
2049
+ const known = components.get(key);
2050
+ components.set(key, { name: known?.name ?? found.name, summary: found.summary || known?.summary || "", digest: known?.digest ?? digestOf(found) });
2051
+ }
2052
+ for (const [key, c] of components) inputs.push({ kind: "component", name: c.name, summary: c.summary || `${c.name}, as ${name}'s DESIGN.md describes it`, ref: `${file}#${key}`, url, digest: c.digest });
2053
+ if (inputs.length === 0) throw new IngestError(`the DESIGN.md names no token groups in its frontmatter and no components: nothing to read`);
2054
+ return {
2055
+ source,
2056
+ license: "unknown",
2057
+ entries: itemsOf(source, "unknown", fetchedAt, inputs),
2058
+ skipped: [],
2059
+ notes: ["licence unknown: a DESIGN.md does not say one", ...target.local ? ["read from a local file: its path is not written into the items"] : []]
2060
+ };
2061
+ }
2062
+
2063
+ // ../discover/src/ingest-figma.ts
2064
+ var API = "https://api.figma.com/v1";
2065
+ var STYLE_GROUP = { FILL: "Color styles", TEXT: "Text styles", EFFECT: "Effect styles", GRID: "Grid styles" };
2066
+ var SAMPLES2 = 5;
2067
+ var listOf = (value, field) => {
2068
+ const list = value.meta?.[field];
2069
+ return Array.isArray(list) ? list : [];
2070
+ };
2071
+ var text = (value) => typeof value === "string" ? value.trim() : "";
2072
+ async function readFigma(target, client, fetchedAt) {
2073
+ const file = await fetchJson(client, `${API}/files/${target.key}?depth=1`, "the Figma file").catch((error) => {
2074
+ throw new IngestError(`${error instanceof Error ? error.message : String(error)}: is FIGMA_TOKEN allowed to read this file?`);
2075
+ });
2076
+ const fileName = text(file.name) || target.key;
2077
+ const [sets, components, styles] = await Promise.all([
2078
+ fetchJson(client, `${API}/files/${target.key}/component_sets`, "its component sets"),
2079
+ fetchJson(client, `${API}/files/${target.key}/components`, "its components"),
2080
+ fetchJson(client, `${API}/files/${target.key}/styles`, "its styles")
2081
+ ]);
2082
+ const at = (node) => `https://www.figma.com/design/${target.key}?node-id=${encodeURIComponent(String(node).replace(/:/g, "-"))}`;
2083
+ const source = { kind: "figma", name: `${fileName} (Figma)`, url: `https://www.figma.com/design/${target.key}` };
2084
+ const inputs = [];
2085
+ const page = (p) => [text(p.containing_frame?.pageName), text(p.containing_frame?.name)].filter(Boolean);
2086
+ for (const set of listOf(sets, "component_sets")) {
2087
+ if (!text(set.name)) continue;
2088
+ inputs.push({ kind: "component", name: text(set.name), summary: text(set.description) || `${text(set.name)}, a component set in ${fileName}`, ref: text(set.key) || String(set.node_id), url: at(set.node_id), digest: digestOf(set), categories: page(set) });
2089
+ }
2090
+ const loose = listOf(components, "components").filter((c) => !c.containing_frame?.containingComponentSet && text(c.name));
2091
+ for (const component of loose) inputs.push({ kind: "component", name: text(component.name), summary: text(component.description) || `${text(component.name)}, a component in ${fileName}`, ref: text(component.key) || String(component.node_id), url: at(component.node_id), digest: digestOf(component), categories: page(component) });
2092
+ const byType = /* @__PURE__ */ new Map();
2093
+ for (const style of listOf(styles, "styles")) {
2094
+ const type = text(style.style_type);
2095
+ if (type) byType.set(type, [...byType.get(type) ?? [], style]);
2096
+ }
2097
+ for (const [type, list] of byType) {
2098
+ const names = list.map((s) => text(s.name)).filter(Boolean);
2099
+ inputs.push({ kind: "token", name: STYLE_GROUP[type] ?? `${type.toLowerCase()} styles`, summary: `${names.length} ${(STYLE_GROUP[type] ?? type).toLowerCase()} in ${fileName}: ${names.slice(0, SAMPLES2).join(", ")}${names.length > SAMPLES2 ? ", \u2026" : ""}`, ref: `${target.key}#${type.toLowerCase()}`, url: source.url, digest: digestOf(list), categories: [type.toLowerCase()] });
2100
+ }
2101
+ if (inputs.length === 0) throw new IngestError(`${fileName} has no components or styles published to a team library: Figma lists only published ones, so publish the library (Assets > Libraries) and ingest it again`);
2102
+ return {
2103
+ source,
2104
+ license: "unknown",
2105
+ entries: itemsOf(source, "unknown", fetchedAt, inputs),
2106
+ skipped: [],
2107
+ notes: [`a Figma file: ${fileName}, ${listOf(sets, "component_sets").length} component sets, ${loose.length} other components, ${byType.size} kinds of style`, "only what is published to a team library is listed", "licence unknown: a Figma file does not say one"]
2108
+ };
2109
+ }
2110
+
2111
+ // ../discover/src/ingest-github.ts
2112
+ var API2 = "https://api.github.com";
2113
+ var COMPONENT_FILE = /\.(tsx|jsx|vue|svelte|astro)$/;
2114
+ var NOT_A_COMPONENT = /(\.(test|spec|stories|story|d)\.[^.]+$)|(^|\/)(index|types?|utils?|helpers?|constants?)\.[^.]+$|(^|\/)(__tests__|__mocks__|stories|examples?|demo|docs?)\//;
2115
+ var CONTRACTS_READ = 100;
2116
+ async function licenseOf(client, repo, fetchedAt) {
2117
+ const got = await client.json(`${API2}/repos/${repo}/license`, false);
2118
+ if (!got.ok) return "unknown";
2119
+ const value = got.value;
2120
+ const spdx = value.license?.spdx_id;
2121
+ if (typeof spdx !== "string" || spdx === "NOASSERTION" || typeof value.html_url !== "string") return "unknown";
2122
+ return { spdx, evidenceUrl: value.html_url, verifiedAt: fetchedAt };
2123
+ }
2124
+ async function readGithub(target, client, fetchedAt) {
2125
+ const repo = `${target.owner}/${target.repo}`;
2126
+ const meta = await fetchJson(client, `${API2}/repos/${repo}`, `the repository ${repo}`);
2127
+ const ref = target.ref ?? meta.default_branch ?? "main";
2128
+ const commit = (await fetchJson(client, `${API2}/repos/${repo}/commits/${encodeURIComponent(ref)}`, `${repo} at ${ref}`)).sha;
2129
+ if (typeof commit !== "string" || !/^[0-9a-f]{40}$/.test(commit)) throw new IngestError(`${repo} has no commit at ${ref}`);
2130
+ const tree = await fetchJson(client, `${API2}/repos/${repo}/git/trees/${commit}?recursive=1`, `${repo}'s files`);
2131
+ const scope = target.path ? `${target.path.replace(/\/+$/, "")}/` : "";
2132
+ const blobs = new Map((tree.tree ?? []).filter((e) => e.type === "blob" && e.path.startsWith(scope)).map((e) => [e.path, e.sha ?? null]));
2133
+ const files = [...blobs.keys()];
2134
+ const license = await licenseOf(client, repo, fetchedAt);
2135
+ const source = { kind: "github", name: repo, url: `https://github.com/${repo}${target.ref || target.path ? `/tree/${ref}${target.path ? `/${target.path}` : ""}` : ""}` };
2136
+ const blob = (path) => `https://github.com/${repo}/blob/${commit}/${path}`;
2137
+ const raw = (path) => `https://raw.githubusercontent.com/${repo}/${commit}/${path}`;
2138
+ const notes = [
2139
+ `read at ${commit.slice(0, 7)} (${ref})`,
2140
+ license === "unknown" ? "licence unknown: GitHub names none for this repository, so its items cannot be adopted until one is found" : `licence ${license.spdx}, from ${license.evidenceUrl}`,
2141
+ ...tree.truncated ? ["GitHub listed only part of this repository's files: name a folder (https://github.com/<owner>/<repo>/tree/<ref>/<folder>) to read the rest"] : []
2142
+ ];
2143
+ const byDepth = (a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b);
2144
+ const registry = files.filter((f) => /(^|\/)registry\.json$/.test(f) && !f.includes("node_modules/")).sort(byDepth)[0];
2145
+ if (registry) {
2146
+ const text2 = await fetchText(client, raw(registry), `${repo}'s ${registry}`);
2147
+ let value;
2148
+ try {
2149
+ value = JSON.parse(text2.body);
2150
+ } catch {
2151
+ throw new IngestError(`${repo}'s ${registry} is not JSON`);
2152
+ }
2153
+ const { items, invalid } = parseRegistryItems(value);
2154
+ const built = new Set(files.filter((f) => /(^|\/)public\/r\/[^/]+\.json$/.test(f)));
2155
+ const inputs2 = items.filter(isCandidate).map((item) => {
2156
+ const servedAt = [...built].find((f) => f.endsWith(`/r/${item.name}.json`));
2157
+ return {
2158
+ kind: item.type === "registry:block" ? "pattern" : "component",
2159
+ name: item.name,
2160
+ ...item.title ? { title: item.title } : {},
2161
+ summary: item.description ?? "",
2162
+ ref: `${registry}#${item.name}`,
2163
+ url: blob(registry),
2164
+ digest: item.itemSha256 ?? digestOf(item),
2165
+ files: (item.files ?? []).map((f) => ({ path: f.path, sha256: f.sha256 ?? null })),
2166
+ categories: item.categories ?? [],
2167
+ ...servedAt ? { install: { command: `npx shadcn add ${raw(servedAt)}`, npm: [...new Set((item.dependencies ?? []).map(packageName))], registryDeps: [...new Set(item.registryDependencies ?? [])], implicitDeps: [] } } : {}
2168
+ };
2169
+ });
2170
+ const others = items.filter((i) => !isCandidate(i));
2171
+ return {
2172
+ source,
2173
+ license,
2174
+ entries: itemsOf(source, license, fetchedAt, inputs2),
2175
+ skipped: [...others.map((i) => ({ name: i.name, reason: `${i.type}: not a component or block` })), ...invalid > 0 ? [{ name: `${invalid} item${invalid === 1 ? "" : "s"}`, reason: "not valid registry items" }] : []],
2176
+ notes: [`a shadcn registry: ${registry}`, ...notes]
2177
+ };
2178
+ }
2179
+ const config = files.filter((f) => /(^|\/)youi\.config\.json$/.test(f)).sort(byDepth)[0];
2180
+ if (config) {
2181
+ const contracts = files.filter((f) => f.endsWith(".contract.json")).sort();
2182
+ const inputs2 = [];
2183
+ const skipped = [];
2184
+ for (const path of contracts.slice(0, CONTRACTS_READ)) {
2185
+ const text2 = await fetchText(client, raw(path), path).catch((error) => {
2186
+ skipped.push({ name: path, reason: error instanceof Error ? error.message : String(error) });
2187
+ return null;
2188
+ });
2189
+ if (!text2) continue;
2190
+ try {
2191
+ const contract = JSON.parse(text2.body);
2192
+ if (typeof contract.name !== "string") throw new Error("no name");
2193
+ inputs2.push({ kind: "component", name: contract.name, summary: typeof contract.summary === "string" ? contract.summary : "", ref: path, url: blob(path), digest: text2.sha256, files: [{ path, sha256: text2.sha256 }], categories: ["contract", ...typeof contract.status === "string" ? [contract.status] : []] });
2194
+ } catch (error) {
2195
+ skipped.push({ name: path, reason: `not a component contract: ${error instanceof Error ? error.message : String(error)}` });
2196
+ }
2197
+ }
2198
+ return { source, license, entries: itemsOf(source, license, fetchedAt, inputs2), skipped, notes: [`a youiyoux design repo: ${config}`, ...notes, ...contracts.length > CONTRACTS_READ ? [`${contracts.length - CONTRACTS_READ} more contracts not read: at most ${CONTRACTS_READ}`] : []] };
2199
+ }
2200
+ const components = files.filter((f) => /(^|\/)components?\//.test(f) && COMPONENT_FILE.test(f) && !NOT_A_COMPONENT.test(f) && !f.includes("node_modules/")).sort();
2201
+ if (components.length === 0) throw new IngestError(`${repo}${scope ? `/${scope}` : ""} has no registry.json, no youi.config.json and no components folder: nothing to read as Library items`);
2202
+ const inputs = components.map((path) => ({ kind: "component", name: displayName(path), summary: cleanText(`${displayName(path)}, in ${path}`), ref: path, url: blob(path), digest: digestOf({ path, blob: blobs.get(path) }), categories: [] }));
2203
+ return { source, license, entries: itemsOf(source, license, fetchedAt, inputs), skipped: [], notes: [`component files under a components folder, by name: their code was not read`, ...notes] };
2204
+ }
2205
+
2206
+ // ../discover/src/ingest-storybook.ts
2207
+ var PATTERN = /^(pages?|templates?|layouts?|patterns?|screens?|examples?)$/i;
2208
+ var STORIES_LISTED = 6;
2209
+ async function readStorybook(target, client, fetchedAt) {
2210
+ let index = null;
2211
+ let entries = {};
2212
+ for (const file of ["index.json", "stories.json"]) {
2213
+ const url = `${target.base}/${file}`;
2214
+ const got = await client.json(url, false);
2215
+ if (!got.ok) continue;
2216
+ const value = got.value;
2217
+ const found = value.entries ?? value.stories;
2218
+ if (found && typeof found === "object" && !Array.isArray(found)) {
2219
+ entries = found;
2220
+ index = { body: got.result.body, url, sha256: got.result.sha256 };
2221
+ break;
2222
+ }
2223
+ }
2224
+ if (!index) throw new IngestError(`${target.base} is not a GitHub repository, a Figma file or a DESIGN.md, and serves no Storybook index (index.json or stories.json): nothing to read`);
2225
+ const byTitle = /* @__PURE__ */ new Map();
2226
+ for (const [key, raw] of Object.entries(entries)) {
2227
+ const title2 = typeof raw.title === "string" ? raw.title : typeof raw.kind === "string" ? raw.kind : null;
2228
+ const id = typeof raw.id === "string" ? raw.id : key;
2229
+ if (!title2) continue;
2230
+ const item = byTitle.get(title2) ?? { stories: [], first: null, docs: null };
2231
+ if (raw.type === "docs") item.docs ??= id;
2232
+ else {
2233
+ item.first ??= id;
2234
+ if (typeof raw.name === "string") item.stories.push(raw.name);
2235
+ }
2236
+ byTitle.set(title2, item);
2237
+ }
2238
+ const host = new URL(target.base).host;
2239
+ const source = { kind: "storybook", name: host, url: target.base };
2240
+ const inputs = [...byTitle].map(([title2, item]) => {
2241
+ const parts = title2.split("/").map((p) => p.trim()).filter(Boolean);
2242
+ const name = parts.at(-1) ?? title2;
2243
+ const folders = parts.slice(0, -1);
2244
+ const page = item.docs ? `?path=/docs/${item.docs}` : item.first ? `?path=/story/${item.first}` : "";
2245
+ const stories = item.stories.length === 0 ? "docs only" : `${item.stories.length} ${item.stories.length === 1 ? "story" : "stories"}: ${item.stories.slice(0, STORIES_LISTED).join(", ")}${item.stories.length > STORIES_LISTED ? ", \u2026" : ""}`;
2246
+ return {
2247
+ kind: folders.some((f) => PATTERN.test(f)) ? "pattern" : "component",
2248
+ name,
2249
+ title: title2,
2250
+ summary: `${title2} in ${host}'s Storybook, ${stories}`,
2251
+ ref: title2,
2252
+ url: `${target.base}/${page}`,
2253
+ digest: index.sha256,
2254
+ categories: folders
2255
+ };
2256
+ });
2257
+ return {
2258
+ source,
2259
+ license: "unknown",
2260
+ entries: itemsOf(source, "unknown", fetchedAt, inputs),
2261
+ skipped: [],
2262
+ notes: [`a Storybook: ${index.url}, ${byTitle.size} component title${byTitle.size === 1 ? "" : "s"}`, "licence unknown: a Storybook does not say one, so its items cannot be adopted until the code's licence is found"]
2263
+ };
2264
+ }
2265
+
2266
+ // ../discover/src/ingest.ts
2267
+ var INGEST_CAP = 300;
2268
+ var IngestError = class extends Error {
2269
+ };
2270
+ var GITHUB_SHORT = /^github:([\w.-]+)\/([\w.-]+?)(?:\.git)?(?:#([^\s]+))?$/;
2271
+ function classifyLink(link, options = {}) {
2272
+ const text2 = link.trim();
2273
+ const short = GITHUB_SHORT.exec(text2);
2274
+ if (short) return { kind: "github", owner: short[1], repo: short[2], ref: short[3] ?? null, path: null };
2275
+ let url = null;
2276
+ try {
2277
+ url = new URL(text2);
2278
+ } catch {
2279
+ url = null;
2280
+ }
2281
+ if (!url) {
2282
+ if (/(^|\/)[^/]*\.md$/i.test(text2)) {
2283
+ if (!options.allowLocal) throw new IngestError(`${text2} is a local file: only the CLI reads those (youiyoux library ingest ${text2})`);
2284
+ return { kind: "design-md", location: resolve(text2), local: true };
2285
+ }
2286
+ throw new IngestError(`"${text2}" is not a link: give a GitHub repository (github:owner/repo or its https URL), a Storybook's https URL, a Figma file's URL, or a DESIGN.md (a path or an https URL)`);
2287
+ }
2288
+ if (url.protocol !== "https:") throw new IngestError(`${url.protocol}//${url.host} is not https: ingestion reads public https links only`);
2289
+ const host = url.hostname.replace(/^www\./, "");
2290
+ const parts = url.pathname.split("/").filter(Boolean);
2291
+ if (host === "github.com") {
2292
+ if (parts.length < 2) throw new IngestError(`${text2} names no repository: expected https://github.com/<owner>/<repo>`);
2293
+ const [owner, repo, kind, ref, ...rest] = parts;
2294
+ if (kind === "blob" && ref && /\.md$/i.test(rest.at(-1) ?? "")) return { kind: "design-md", location: `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${rest.join("/")}`, local: false };
2295
+ return { kind: "github", owner, repo: repo.replace(/\.git$/, ""), ref: kind === "tree" && ref ? ref : null, path: kind === "tree" && rest.length > 0 ? rest.join("/") : null };
2296
+ }
2297
+ if (host === "figma.com" || host.endsWith(".figma.com")) {
2298
+ const at = parts.findIndex((p) => p === "design" || p === "file" || p === "proto");
2299
+ const key = at >= 0 ? parts[at + 1] : void 0;
2300
+ if (!key || !/^[A-Za-z0-9]{10,64}$/.test(key)) throw new IngestError(`${text2} names no Figma file: expected https://www.figma.com/design/<file key>/\u2026`);
2301
+ return { kind: "figma", key };
2302
+ }
2303
+ if (/\.md$/i.test(url.pathname)) return { kind: "design-md", location: url.href, local: false };
2304
+ const base = `${url.origin}${url.pathname.replace(/\/(?:index\.html|iframe\.html)?$/, "")}`.replace(/\/+$/, "");
2305
+ return { kind: "storybook", base };
2306
+ }
2307
+ function itemsOf(source, license, fetchedAt, inputs) {
2308
+ const used = /* @__PURE__ */ new Set();
2309
+ const slug = sourceSlug(source.name.replace(/\//g, "-"));
2310
+ return inputs.map((input) => {
2311
+ const base = `lib:${slug}/${input.kind}/${nameSlug(input.name)}`;
2312
+ let id = base;
2313
+ for (let n = 2; used.has(id); n++) id = `${base}-${n}`;
2314
+ used.add(id);
2315
+ const draft = {
2316
+ schema: "youi.library/v1",
2317
+ id,
2318
+ name: input.name,
2319
+ ...input.title && input.title !== input.name ? { title: input.title } : {},
2320
+ kind: input.kind,
2321
+ stage: "library",
2322
+ summary: summaryOf(input.summary, `${input.name} from ${source.name}`),
2323
+ source: { name: source.name, ...input.url ? { url: input.url } : {}, ingestedAt: fetchedAt, kind: source.kind, ref: input.ref },
2324
+ categories: [...new Set((input.categories ?? []).map((c) => c.toLowerCase()).filter(Boolean))],
2325
+ swapsWith: [],
2326
+ ...input.url ? { provenance: { fetchedAt, url: input.url, sha256: input.digest, files: [...input.files ?? []], registryHealth: null } } : {},
2327
+ license,
2328
+ ...input.install ? { install: input.install } : {}
2329
+ };
2330
+ const named = readNeed(input.name).concepts.length > 0 ? readNeed(input.name).concepts : readNeed(input.name.replace(/[^A-Za-z0-9]+/g, "").toLowerCase()).concepts;
2331
+ const roles = input.kind === "token" ? [] : (named.length > 0 ? named : input.title ? readNeed(input.title).concepts : []).map((c) => c.id);
2332
+ return LibraryEntry.parse({ ...draft, categories: [.../* @__PURE__ */ new Set([...roles, ...draft.categories])] });
2333
+ });
2334
+ }
2335
+ function displayName(file) {
2336
+ const stem = basename2(file).replace(/\.[^.]+$/, "");
2337
+ return stem.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[-_\s]+/).filter(Boolean).map((w) => w[0].toUpperCase() + w.slice(1)).join(" ");
2338
+ }
2339
+ var digestOf = (value) => sha256(typeof value === "string" ? value : JSON.stringify(value));
2340
+ async function fetchJson(client, url, what) {
2341
+ const got = await client.json(url, false);
2342
+ if (!got.ok) throw new IngestError(`could not read ${what} (${url}): ${got.status === 404 ? "not found" : got.message}`);
2343
+ return got.value;
2344
+ }
2345
+ async function fetchText(client, url, what) {
2346
+ const got = await client.get(url, "text/plain, text/markdown, */*");
2347
+ if (!got.ok) throw new IngestError(`could not read ${what} (${url}): ${got.status === 404 ? "not found" : got.message}`);
2348
+ return { body: got.body, sha256: got.sha256, fetchedAt: got.fetchedAt };
2349
+ }
2350
+ async function ingestLink(link, options) {
2351
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
2352
+ const target = classifyLink(link, options);
2353
+ const fetchedAt = now().toISOString();
2354
+ const result = await (target.kind === "github" ? readGithub(target, options.client, fetchedAt) : target.kind === "storybook" ? readStorybook(target, options.client, fetchedAt) : target.kind === "figma" ? options.figma ? readFigma(target, options.client, fetchedAt) : Promise.reject(new IngestError("a Figma file is read through Figma's API: set FIGMA_TOKEN to a personal access token that can read the file (Figma > Settings > Security > Personal access tokens)")) : readDesignMd(target, options.client, fetchedAt, (path) => readFile3(path, "utf8")));
2355
+ const over = result.entries.length - INGEST_CAP;
2356
+ return {
2357
+ ...result,
2358
+ link: link.trim(),
2359
+ fetchedAt,
2360
+ entries: result.entries.slice(0, INGEST_CAP),
2361
+ notes: [...result.notes, ...over > 0 ? [`${over} more item${over === 1 ? "" : "s"} not read: at most ${INGEST_CAP} come from one link`] : []]
2362
+ };
2363
+ }
2364
+
1958
2365
  export {
1959
2366
  CONCEPTS,
1960
2367
  normalize,
@@ -1977,6 +2384,7 @@ export {
1977
2384
  implicitDeps,
1978
2385
  sourceSlug,
1979
2386
  nameSlug,
2387
+ summaryOf,
1980
2388
  docsLinks,
1981
2389
  registryEntry,
1982
2390
  HEADLESS,
@@ -2072,5 +2480,14 @@ export {
2072
2480
  conceptOfName,
2073
2481
  conceptOfPackage,
2074
2482
  declaredPackages,
2075
- buildGaps
2483
+ buildGaps,
2484
+ INGEST_CAP,
2485
+ IngestError,
2486
+ classifyLink,
2487
+ itemsOf,
2488
+ displayName,
2489
+ digestOf,
2490
+ fetchJson,
2491
+ fetchText,
2492
+ ingestLink
2076
2493
  };
@@ -0,0 +1,52 @@
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
+ import {
3
+ padEnd,
4
+ plural,
5
+ style,
6
+ workflowStateDir
7
+ } from "./chunk-FTF4MBV5.js";
8
+ import "./chunk-HII2I2PI.js";
9
+ import "./chunk-42YJX3W5.js";
10
+ import "./chunk-HIXGUALJ.js";
11
+ import "./chunk-S7UN5KQK.js";
12
+ import "./chunk-WNTS7ZO4.js";
13
+
14
+ // ../cli/src/ingest.ts
15
+ import { userInfo } from "node:os";
16
+ async function readLink(link, options) {
17
+ const { githubBuckets, ingestLink, PoliteClient } = await import("./src-7LETTRRF.js");
18
+ const env = options.env ?? process.env;
19
+ const token = env.GITHUB_TOKEN || void 0;
20
+ const figmaToken = env.FIGMA_TOKEN || void 0;
21
+ const client = new PoliteClient({ buckets: githubBuckets(token), ...token ? { token } : {}, ...figmaToken ? { figmaToken } : {} });
22
+ const result = await ingestLink(link, { client, allowLocal: options.allowLocal, figma: Boolean(figmaToken) });
23
+ return { link: result.link, source: result.source, license: result.license, fetchedAt: result.fetchedAt, entries: result.entries, skipped: result.skipped, notes: result.notes };
24
+ }
25
+ async function fileIngestDraft(project, reading, state) {
26
+ const { FileStore, RuleWorkflow } = await import("./src-CCGTRT5J.js");
27
+ const workflow = new RuleWorkflow({ projectRoot: project.root, store: new FileStore(workflowStateDir(project, state)), ...project.configFile ? { configPath: project.configFile } : {}, ...project.designOverride ? { designRoot: project.repo.root } : {}, ...project.rootOverride ? { productRoot: project.root } : {}, ingest: async () => reading });
28
+ const draft = await workflow.create({ kind: "person", name: userInfo().username }, { kind: "ingest", link: reading.link });
29
+ return { id: draft.id, problems: draft.problems };
30
+ }
31
+ function renderIngest(reading, filed) {
32
+ const licence = reading.license === "unknown" ? style.warn("licence unknown") : `licence ${reading.license.spdx}`;
33
+ const lines = [`${style.bold(reading.source.name)} ${style.dim(`\xB7 ${reading.source.kind}${reading.source.url ? ` \xB7 ${reading.source.url}` : ""}`)} \xB7 ${plural(reading.entries.length, "item")} \xB7 ${licence}`, ""];
34
+ const width = Math.min(28, Math.max(0, ...reading.entries.map((e) => e.name.length)));
35
+ for (const kind of ["component", "pattern", "token"]) {
36
+ const items = reading.entries.filter((e) => e.kind === kind);
37
+ if (items.length === 0) continue;
38
+ lines.push(style.bold(`${kind}s`));
39
+ for (const e of items) lines.push(` ${padEnd(e.name.length > width ? `${e.name.slice(0, width - 1)}\u2026` : e.name, width)} ${style.dim(e.categories.length ? e.categories.join(", ") : "no role")} ${e.summary}`);
40
+ lines.push("");
41
+ }
42
+ const SKIPPED_SHOWN = 10;
43
+ if (reading.skipped.length > 0) lines.push(style.bold(`not read (${reading.skipped.length})`), ...reading.skipped.slice(0, SKIPPED_SHOWN).map((s) => ` ${s.name} ${style.dim(s.reason)}`), ...reading.skipped.length > SKIPPED_SHOWN ? [style.dim(` \u2026 ${reading.skipped.length - SKIPPED_SHOWN} more (--json lists them all)`)] : [], "");
44
+ lines.push(...reading.notes.map((n) => style.dim(`\xB7 ${n}`)), "");
45
+ lines.push(filed ? `${style.ok("filed")} as draft ${filed.id}: keep or drop each item, then approve, in youiyoux ui > Changes` : `Next: youiyoux library ingest ${reading.link} --draft files it for review (nothing is written to the Library until a person approves)`);
46
+ return lines.join("\n");
47
+ }
48
+ export {
49
+ fileIngestDraft,
50
+ readLink,
51
+ renderIngest
52
+ };