youiyoux 0.1.3 → 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.
- package/README.md +10 -0
- package/dist/chunks/{chunk-S7UV34H5.js → chunk-42YJX3W5.js} +1 -3
- package/dist/chunks/{chunk-74KBRI2H.js → chunk-4BBYLTZX.js} +10 -10
- package/dist/chunks/chunk-52KOKYY6.js +8710 -0
- package/dist/chunks/chunk-FJZFCAL5.js +129 -0
- package/dist/chunks/chunk-FVVL5JCY.js +24 -0
- package/dist/chunks/{chunk-UEB6MJDX.js → chunk-M5XZWCPS.js} +176 -61
- package/dist/chunks/{chunk-C3TSTYP7.js → chunk-NYDJYMID.js} +212 -26
- package/dist/chunks/{chunk-OIHNEGWH.js → chunk-RDTHHRZN.js} +96 -142
- package/dist/chunks/{chunk-E4HNK7VF.js → chunk-WNTS7ZO4.js} +1 -1
- package/dist/chunks/chunk-XAEMVEYX.js +188 -0
- package/dist/chunks/{chunk-RTOTNSOV.js → chunk-YFCMTTXS.js} +600 -506
- package/dist/chunks/chunk-YJ7WMBTT.js +13761 -0
- package/dist/chunks/src-5IP4NGKT.js +115 -0
- package/dist/chunks/{src-U5NTDPUX.js → src-6CJR547Y.js} +11 -35
- package/dist/chunks/{src-YGEZCUAO.js → src-BPETAFLW.js} +134 -77
- package/dist/chunks/{src-ZS76UD7E.js → src-LLV7IY6W.js} +27 -16
- package/dist/chunks/src-RHNZELGE.js +306 -0
- package/dist/chunks/{src-YUC5PIDK.js → src-SFKFERZ2.js} +14 -8
- package/dist/chunks/{src-DS43LCRA.js → src-VL2ZYO6X.js} +4 -4
- package/dist/hub/assets/Badge-CUJUit4d.js +1 -0
- package/dist/hub/assets/CandidatePanel-CnbK4S0j.js +1 -0
- package/dist/hub/assets/CatalogScreen-B5OVL-AR.js +1 -0
- package/dist/hub/assets/ChangesScreen-GwGqJwxN.js +1 -0
- package/dist/hub/assets/CheatSheet-BLSOq-xa.js +1 -0
- package/dist/hub/assets/CodeBlock-CsiGFgvI.js +1 -0
- package/dist/hub/assets/CommandPalette-CAWQfdXJ.js +1 -0
- package/dist/hub/assets/CopyButton-DE8drl4P.js +1 -0
- package/dist/hub/assets/Disclosure-B1kTgPgk.js +1 -0
- package/dist/hub/assets/DiscoverScreen-Tl_Z6mgY.js +2 -0
- package/dist/hub/assets/DraftEditor-yIDapXTs.js +5 -0
- package/dist/hub/assets/Field-LwrJXoOs.js +1 -0
- package/dist/hub/assets/GapsScreen-DXZAlm6v.js +1 -0
- package/dist/hub/assets/HomeScreen-CEpiVhOa.js +1 -0
- package/dist/hub/assets/InventoryScreen-CEG3Pmw_.js +1 -0
- package/dist/hub/assets/KindIcon-BJ6bz-ny.js +1 -0
- package/dist/hub/assets/LibraryScreen-IaVnxrX9.js +1 -0
- package/dist/hub/assets/LicenceBadge-CLCUMMHE.js +1 -0
- package/dist/hub/assets/Opened-DJyf4Hnj.js +1 -0
- package/dist/hub/assets/Preview-C069zVMP.js +1 -0
- package/dist/hub/assets/RuleDetail-B4g2-TRe.js +3 -0
- package/dist/hub/assets/RulesScreen-CAaXIWjO.js +3 -0
- package/dist/hub/assets/ScreenImage-DcI7_G3O.js +1 -0
- package/dist/hub/assets/ScreensScreen-DwczCjZD.js +1 -0
- package/dist/hub/assets/SearchField-DaCpNAj6.js +1 -0
- package/dist/hub/assets/Section-BTnvUq_W.js +1 -0
- package/dist/hub/assets/Segmented-Dzi1x49S.js +1 -0
- package/dist/hub/assets/ServiceDown-Bv-BPXro.js +1 -0
- package/dist/hub/assets/SpecimenCapture-DlsjshVE.js +1 -0
- package/dist/hub/assets/Specimens-C-lHp7sr.js +2 -0
- package/dist/hub/assets/StackScreen-DkmELsqR.js +1 -0
- package/dist/hub/assets/StubScreens-Bt7lWLVK.js +1 -0
- package/dist/hub/assets/StyleScreen-tq1tbRBo.js +1 -0
- package/dist/hub/assets/Swatch-KDz4qxdM.js +1 -0
- package/dist/hub/assets/Tabs-1QQDS0I6.js +1 -0
- package/dist/hub/assets/TokenDetail-DvCw6GqC.js +1 -0
- package/dist/hub/assets/TokenValue-CsPRc2ai.js +1 -0
- package/dist/hub/assets/TokensScreen-DoENV8Xy.js +1 -0
- package/dist/hub/assets/arrow-right-DcbNDzqs.js +1 -0
- package/dist/hub/assets/bot-B3gC9oUR.js +1 -0
- package/dist/hub/assets/button-6mvMVtpb.js +1 -0
- package/dist/hub/assets/button.demo-DuU0DQbv.js +1 -0
- package/dist/hub/assets/card.demo-CufAxMAA.js +1 -0
- package/dist/hub/assets/chevron-down-CzN07vMi.js +1 -0
- package/dist/hub/assets/circle-alert-DT2LC4yf.js +1 -0
- package/dist/hub/assets/circle-check-DkwBMT62.js +1 -0
- package/dist/hub/assets/client-D7whEyep.js +1 -0
- package/dist/hub/assets/clsx-DB0hHKMi.js +1 -0
- package/dist/hub/assets/compass-BkKOZPYe.js +1 -0
- package/dist/hub/assets/contrast-BkNHUAPa.js +1 -0
- package/dist/hub/assets/createLucideIcon-CGDJWtKO.js +1 -0
- package/dist/hub/assets/discover-XjJEViwk.js +1 -0
- package/dist/hub/assets/dist-BYQYaPBV.js +1 -0
- package/dist/hub/assets/dist-Bk4srtDn.js +41 -0
- package/dist/hub/assets/dist-BogNzeK9.js +1 -0
- package/dist/hub/assets/dist-D0z7Bvsy.js +1 -0
- package/dist/hub/assets/download-BQI-Si9R.js +1 -0
- package/dist/hub/assets/engine-CMmt9yEz.js +1 -0
- package/dist/hub/assets/esm-NPpg23M8.js +1 -0
- package/dist/hub/assets/family-tiles-CyTdy_cP.js +1 -0
- package/dist/hub/assets/format-DxAYNTN_.js +1 -0
- package/dist/hub/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/hub/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/hub/assets/geist-latin-core-vkBVTr4s.woff2 +0 -0
- package/dist/hub/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/hub/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/hub/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/dist/hub/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/dist/hub/assets/geist-mono-latin-core-DccGGzmg.woff2 +0 -0
- package/dist/hub/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/dist/hub/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/dist/hub/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/dist/hub/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/dist/hub/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/hub/assets/git-pull-request-arrow-RqUUIRCU.js +1 -0
- package/dist/hub/assets/input.demo-238gqWpz.js +1 -0
- package/dist/hub/assets/inventory-BI8QBdar.js +1 -0
- package/dist/hub/assets/inventory-source-lZYzcA8O.js +1 -0
- package/dist/hub/assets/jsx-runtime-D6zNwnaE.js +9 -0
- package/dist/hub/assets/layers-DfzdK_wG.js +1 -0
- package/dist/hub/assets/layout-grid-xFY9L9Oc.js +1 -0
- package/dist/hub/assets/library-W5R8GwvG.js +1 -0
- package/dist/hub/assets/main-7snyIdoi.css +2 -0
- package/dist/hub/assets/main-B9CIkk5m.js +2 -0
- package/dist/hub/assets/minus-BDifFhOo.js +1 -0
- package/dist/hub/assets/monitor-X_lELkcU.js +1 -0
- package/dist/hub/assets/mouse-pointer-click-CVJ6hWg8.js +1 -0
- package/dist/hub/assets/paintbrush-DOkEgSTO.js +1 -0
- package/dist/hub/assets/plural-BbGQPQKA.js +1 -0
- package/dist/hub/assets/preview-BA8CPsKb.css +2 -0
- package/dist/hub/assets/preview-DUm13vr1.js +3 -0
- package/dist/hub/assets/protocol-caP6umNL.js +1 -0
- package/dist/hub/assets/react-DB-4Zxce.js +1 -0
- package/dist/hub/assets/shield-check-0ka0kQct.js +1 -0
- package/dist/hub/assets/trash-B7aEfJps.js +1 -0
- package/dist/hub/assets/triangle-alert-Dkdi9eDR.js +1 -0
- package/dist/hub/assets/try-live-zX3CSd3P.js +1 -0
- package/dist/hub/assets/utils-BoaXfs8y.js +1 -0
- package/dist/hub/assets/values-DzxR_TlI.js +1 -0
- package/dist/hub/assets/worker-Bh-wSp42.js +1 -0
- package/dist/hub/assets/workflow-jpV8wh-U.js +1 -0
- package/dist/hub/assets/x-MLbFVa4v.js +1 -0
- package/dist/hub/favicon.svg +1 -0
- package/dist/hub/index.html +35 -0
- package/dist/hub/kit/candidates.json +1 -0
- package/dist/hub/kit/chunks/chunk-2NYFUOIV.js +14 -0
- package/dist/hub/kit/chunks/chunk-DCANE7XH.js +1 -0
- package/dist/hub/kit/chunks/chunk-FTGHCY4I.js +14 -0
- package/dist/hub/kit/chunks/chunk-IBF3UTJF.js +33 -0
- package/dist/hub/kit/chunks/chunk-LLXY4VSH.js +1 -0
- package/dist/hub/kit/chunks/chunk-PUPZMW4P.js +14 -0
- package/dist/hub/kit/chunks/chunk-PWLNZQET.js +1 -0
- package/dist/hub/kit/chunks/chunk-ULXWVOKJ.js +1 -0
- package/dist/hub/kit/chunks/chunk-WM4BGGMK.js +1 -0
- package/dist/hub/kit/chunks/chunk-ZQUNRK72.js +1 -0
- package/dist/hub/kit/class-variance-authority.js +1 -0
- package/dist/hub/kit/clsx.js +1 -0
- package/dist/hub/kit/fonts/geist-cyrillic-ext-wght-normal.woff2 +0 -0
- package/dist/hub/kit/fonts/geist-cyrillic-wght-normal.woff2 +0 -0
- package/dist/hub/kit/fonts/geist-latin-ext-wght-normal.woff2 +0 -0
- package/dist/hub/kit/fonts/geist-latin-wght-normal.woff2 +0 -0
- package/dist/hub/kit/fonts/geist-vietnamese-wght-normal.woff2 +0 -0
- package/dist/hub/kit/frame.css.src +73 -0
- package/dist/hub/kit/frame.js +2 -0
- package/dist/hub/kit/lucide-react.js +1878 -0
- package/dist/hub/kit/preview.html +13 -0
- package/dist/hub/kit/radix-ui-react-dialog.js +41 -0
- package/dist/hub/kit/radix-ui-react-label.js +1 -0
- package/dist/hub/kit/radix-ui-react-separator.js +1 -0
- package/dist/hub/kit/radix-ui-react-slot.js +1 -0
- package/dist/hub/kit/radix-ui-react-switch.js +1 -0
- package/dist/hub/kit/radix-ui-react-tabs.js +1 -0
- package/dist/hub/kit/react-dom-client.js +1 -0
- package/dist/hub/kit/react-dom.js +1 -0
- package/dist/hub/kit/react-jsx-runtime.js +1 -0
- package/dist/hub/kit/react.js +1 -0
- package/dist/hub/kit/tailwind-merge.js +1 -0
- package/dist/hub/kit/tailwindcss/index.css +950 -0
- package/dist/hub/kit/tailwindcss/preflight.css +398 -0
- package/dist/hub/kit/tailwindcss/theme.css +510 -0
- package/dist/hub/kit/tailwindcss/utilities.css +1 -0
- package/dist/hub-demo/frame/button.demo.js +1 -0
- package/dist/hub-demo/frame/card.demo.js +1 -0
- package/dist/hub-demo/frame/demos.js +6 -0
- package/dist/hub-demo/frame/frame.css +770 -0
- package/dist/hub-demo/frame/input.demo.js +1 -0
- package/dist/hub-demo/preview/components/button.demo.tsx +47 -0
- package/dist/hub-demo/preview/components/button.tsx +31 -0
- package/dist/hub-demo/preview/components/card.demo.tsx +26 -0
- package/dist/hub-demo/preview/components/card.tsx +20 -0
- package/dist/hub-demo/preview/components/input.demo.tsx +39 -0
- package/dist/hub-demo/preview/components/input.tsx +19 -0
- package/dist/hub-demo/preview/components/utils.ts +5 -0
- package/dist/hub-demo/preview/system.css +161 -0
- package/dist/hub-demo/public/data/demo-app.usage.bedd9c82fe.json +1 -0
- package/dist/hub-demo/public/data/demo.catalog.a605de2281.json +1 -0
- package/dist/hub-demo/public/data/gaps.129a93538d.json +1 -0
- package/dist/hub-demo/public/data/inventory.165a4f6408.json +1 -0
- package/dist/hub-demo/public/data/inventory.console.badge.ad0238784e.json +1 -0
- package/dist/hub-demo/public/data/inventory.console.button.3249daff35.json +1 -0
- package/dist/hub-demo/public/data/inventory.console.card.9ea88708ac.json +1 -0
- package/dist/hub-demo/public/data/inventory.console.dialog.8b6574d93a.json +1 -0
- package/dist/hub-demo/public/data/inventory.console.input.bdffb756a7.json +1 -0
- package/dist/hub-demo/public/data/inventory.console.link-as-button.1ce7faa1f3.json +1 -0
- package/dist/hub-demo/public/data/inventory.console.tabs.813e694c44.json +1 -0
- package/dist/hub-demo/public/data/inventory.extras.e5d06c6ff4.json +1 -0
- package/dist/hub-demo/public/data/inventory.site.badge.047a68f644.json +1 -0
- package/dist/hub-demo/public/data/inventory.site.button.720938384e.json +1 -0
- package/dist/hub-demo/public/data/inventory.site.card.d8df5f6873.json +1 -0
- package/dist/hub-demo/public/data/inventory.site.link-as-button.9c1f8a55bf.json +1 -0
- package/dist/hub-demo/public/data/inventory.site.tabs.59bd0200b9.json +1 -0
- package/dist/hub-demo/public/data/manifest.json +39 -0
- package/dist/hub-demo/public/data/specimens.console.dark.046471907d.css +786 -0
- package/dist/hub-demo/public/data/specimens.console.light.aaffa2d213.css +786 -0
- package/dist/hub-demo/public/data/specimens.site.dark.94a6706908.css +92 -0
- package/dist/hub-demo/public/data/specimens.site.light.3ddbc8d28d.css +92 -0
- package/dist/hub-demo/public/previews/button.dark.webp +0 -0
- package/dist/hub-demo/public/previews/button.light.webp +0 -0
- package/dist/hub-demo/public/previews/card.dark.webp +0 -0
- package/dist/hub-demo/public/previews/card.light.webp +0 -0
- package/dist/hub-demo/public/previews/input.dark.webp +0 -0
- package/dist/hub-demo/public/previews/input.light.webp +0 -0
- package/dist/hub-previews.js +173 -0
- package/dist/hub-server.js +6329 -0
- package/dist/hub-snapshot.js +573 -0
- package/dist/schema/youi.gaps.schema.json +235 -0
- package/dist/tailwind-worker.js +3 -3
- package/dist/youi.js +10 -8
- package/dist/youiyoux.js +10 -8
- package/package.json +4 -4
- package/dist/chunks/chunk-L5BV4PI7.js +0 -22159
|
@@ -0,0 +1,129 @@
|
|
|
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
|
+
isLocalHost,
|
|
4
|
+
loadPackage,
|
|
5
|
+
readTryIndex,
|
|
6
|
+
send
|
|
7
|
+
} from "./chunk-XAEMVEYX.js";
|
|
8
|
+
import {
|
|
9
|
+
writeFileAtomic
|
|
10
|
+
} from "./chunk-NYDJYMID.js";
|
|
11
|
+
|
|
12
|
+
// ../../apps/web/scripts/discover-thumbs.ts
|
|
13
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
14
|
+
import { readdir, rm } from "node:fs/promises";
|
|
15
|
+
import { join, resolve } from "node:path";
|
|
16
|
+
var THUMBS_PATH = "/__youi/discover-thumbs";
|
|
17
|
+
var THUMBS_DIR = "thumbs";
|
|
18
|
+
var THUMBS_INDEX = "index.json";
|
|
19
|
+
var THUMBS_SCHEMA = "youiyoux.discover-thumbs/v1";
|
|
20
|
+
var THUMB_SCHEMES = ["light", "dark"];
|
|
21
|
+
var PROJECT = /^[a-z0-9][a-z0-9._-]{0,80}$/i;
|
|
22
|
+
var PICTURE = /^([0-9a-f]{16})\.(light|dark)\.webp$/;
|
|
23
|
+
var safeProject = (project) => PROJECT.test(project) && project !== "." && project !== ".." ? project : null;
|
|
24
|
+
function thumbsDir(cache, project) {
|
|
25
|
+
const safe = safeProject(project);
|
|
26
|
+
if (!safe) throw new Error(`refused: "${project.slice(0, 60)}" is not a project id`);
|
|
27
|
+
return join(resolve(cache), THUMBS_DIR, safe);
|
|
28
|
+
}
|
|
29
|
+
var emptyIndex = (project) => ({ schema: THUMBS_SCHEMA, project, items: {}, failed: {} });
|
|
30
|
+
function readThumbIndex(cache, project) {
|
|
31
|
+
const file = join(thumbsDir(cache, project), THUMBS_INDEX);
|
|
32
|
+
if (!existsSync(file)) return emptyIndex(project);
|
|
33
|
+
try {
|
|
34
|
+
const value = JSON.parse(readFileSync(file, "utf8"));
|
|
35
|
+
if (value.schema !== THUMBS_SCHEMA) return emptyIndex(project);
|
|
36
|
+
return { schema: THUMBS_SCHEMA, project, items: { ...value.items }, failed: { ...value.failed } };
|
|
37
|
+
} catch {
|
|
38
|
+
return emptyIndex(project);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async function writeThumbIndex(cache, index) {
|
|
42
|
+
await writeFileAtomic(join(thumbsDir(cache, index.project), THUMBS_INDEX), `${JSON.stringify(index, null, 1)}
|
|
43
|
+
`);
|
|
44
|
+
}
|
|
45
|
+
function needsCapture(index, id, sha, force = false) {
|
|
46
|
+
return force || index.items[id]?.sha !== sha && index.failed[id]?.sha !== sha;
|
|
47
|
+
}
|
|
48
|
+
function currentThumbs(index, current, url) {
|
|
49
|
+
const items = {};
|
|
50
|
+
for (const [id, thumb] of Object.entries(index.items)) {
|
|
51
|
+
if (current.get(id) !== thumb.sha || !/^[0-9a-f]{16}$/.test(thumb.key)) continue;
|
|
52
|
+
items[id] = { light: url(`${thumb.key}.light.webp`), dark: url(`${thumb.key}.dark.webp`), width: thumb.width, height: thumb.height, capturedAt: thumb.capturedAt };
|
|
53
|
+
}
|
|
54
|
+
const failed = {};
|
|
55
|
+
for (const [id, failure] of Object.entries(index.failed)) if (current.get(id) === failure.sha && !items[id]) failed[id] = failure.reason;
|
|
56
|
+
return { project: index.project, items, failed };
|
|
57
|
+
}
|
|
58
|
+
async function prunePictures(cache, index) {
|
|
59
|
+
const dir = thumbsDir(cache, index.project);
|
|
60
|
+
const keep = new Set(Object.values(index.items).map((t) => t.key));
|
|
61
|
+
let removed = 0;
|
|
62
|
+
for (const name of await readdir(dir).catch(() => [])) {
|
|
63
|
+
const match = PICTURE.exec(name);
|
|
64
|
+
if (!match || keep.has(match[1])) continue;
|
|
65
|
+
await rm(join(dir, name), { force: true });
|
|
66
|
+
removed++;
|
|
67
|
+
}
|
|
68
|
+
return removed;
|
|
69
|
+
}
|
|
70
|
+
function selectCandidates(entries, selection) {
|
|
71
|
+
const chosen = entries.filter((e) => {
|
|
72
|
+
if (e.source.kind !== "shadcn-registry" || !e.source.ref || typeof e.provenance?.sha256 !== "string") return false;
|
|
73
|
+
if (selection.registry && !e.source.ref.startsWith(`${selection.registry}/`)) return false;
|
|
74
|
+
return !selection.role || (e.categories ?? []).includes(selection.role) || e.source.ref.endsWith(`/${selection.role}`);
|
|
75
|
+
});
|
|
76
|
+
return selection.limit === null ? chosen : chosen.slice(0, selection.limit);
|
|
77
|
+
}
|
|
78
|
+
function listingDigests(cache) {
|
|
79
|
+
const digests = /* @__PURE__ */ new Map();
|
|
80
|
+
for (const name of ["index.json", "index-all.json"]) {
|
|
81
|
+
const file = join(cache, name);
|
|
82
|
+
if (!existsSync(file)) continue;
|
|
83
|
+
try {
|
|
84
|
+
const value = JSON.parse(readFileSync(file, "utf8"));
|
|
85
|
+
for (const entry of value.entries ?? []) if (typeof entry.id === "string" && typeof entry.provenance?.sha256 === "string") digests.set(entry.id, entry.provenance.sha256);
|
|
86
|
+
} catch {
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return digests;
|
|
90
|
+
}
|
|
91
|
+
function discoverThumbsMiddleware(snapshot, base = "/", options = {}, cacheDir = async () => (await loadPackage("discover")).discoverCacheDir(void 0, options.env ?? process.env)) {
|
|
92
|
+
const projects = () => Object.keys(readTryIndex(snapshot)?.projects ?? {});
|
|
93
|
+
return async (req, res, next) => {
|
|
94
|
+
const url = new URL((req.url ?? "/").replace(base, "/"), "http://hub.local");
|
|
95
|
+
if (!url.pathname.startsWith(THUMBS_PATH) || req.method !== "GET") return next();
|
|
96
|
+
if (!isLocalHost(req.headers.host)) return send(res, 403, { error: "Thumbnails answer this machine only (Host must be localhost)." });
|
|
97
|
+
const cache = resolve(options.cache ?? await cacheDir());
|
|
98
|
+
const picture = /^\/__youi\/discover-thumbs\/([^/]+)\/([^/]+)$/.exec(url.pathname);
|
|
99
|
+
if (picture) {
|
|
100
|
+
const project2 = safeProject(decodeURIComponent(picture[1]));
|
|
101
|
+
if (!project2 || !PICTURE.test(picture[2])) return send(res, 400, { error: "not a thumbnail" });
|
|
102
|
+
const file = join(thumbsDir(cache, project2), picture[2]);
|
|
103
|
+
if (!existsSync(file)) return send(res, 404, { error: "no such thumbnail" });
|
|
104
|
+
res.setHeader("Content-Type", "image/webp");
|
|
105
|
+
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
|
106
|
+
res.end(readFileSync(file));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (url.pathname !== THUMBS_PATH) return send(res, 404, { error: "not found" });
|
|
110
|
+
const known = projects();
|
|
111
|
+
const wanted = url.searchParams.get("project");
|
|
112
|
+
const project = wanted ? known.includes(wanted) ? wanted : null : known[0] ?? null;
|
|
113
|
+
if (!project || !safeProject(project)) return send(res, 404, { error: wanted ? `The hub has no project "${wanted.slice(0, 60)}".` : "The hub has no project: open it with youiyoux ui --project." });
|
|
114
|
+
const index = readThumbIndex(cache, project);
|
|
115
|
+
send(res, 200, currentThumbs(index, listingDigests(cache), (file) => `${base}${THUMBS_PATH.slice(1)}/${encodeURIComponent(project)}/${file}`));
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export {
|
|
120
|
+
THUMBS_PATH,
|
|
121
|
+
THUMB_SCHEMES,
|
|
122
|
+
thumbsDir,
|
|
123
|
+
readThumbIndex,
|
|
124
|
+
writeThumbIndex,
|
|
125
|
+
needsCapture,
|
|
126
|
+
prunePictures,
|
|
127
|
+
selectCandidates,
|
|
128
|
+
discoverThumbsMiddleware
|
|
129
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
|
|
3
|
+
// src/node20.js
|
|
4
|
+
if (!Map.groupBy) {
|
|
5
|
+
Map.groupBy = (items, key) => {
|
|
6
|
+
const groups = /* @__PURE__ */ new Map();
|
|
7
|
+
let i = 0;
|
|
8
|
+
for (const item of items) {
|
|
9
|
+
const k = key(item, i++);
|
|
10
|
+
const list = groups.get(k);
|
|
11
|
+
if (list) list.push(item);
|
|
12
|
+
else groups.set(k, [item]);
|
|
13
|
+
}
|
|
14
|
+
return groups;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
if (!Object.groupBy) Object.groupBy = (items, key) => Object.fromEntries(Map.groupBy(items, key));
|
|
18
|
+
if (!Promise.withResolvers) {
|
|
19
|
+
Promise.withResolvers = () => {
|
|
20
|
+
let resolve, reject;
|
|
21
|
+
const promise = new Promise((yes, no) => (resolve = yes, reject = no));
|
|
22
|
+
return { promise, resolve, reject };
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -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-
|
|
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
|
}
|
|
@@ -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)
|
|
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
|
|
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 (
|
|
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 (
|
|
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 =
|
|
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 (
|
|
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] : [],
|
|
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,
|
|
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,
|
|
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:${
|
|
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}#${
|
|
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
|
|
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" :
|
|
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
|
|
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,
|
|
@@ -1810,32 +1804,25 @@ 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
|
|
1824
|
-
|
|
1817
|
+
const pa = homeParts(a);
|
|
1818
|
+
const pb = homeParts(b);
|
|
1819
|
+
return pa !== null && pb !== null && `${pa.host}${pa.path}` === `${pb.host}${pb.path}`;
|
|
1825
1820
|
}
|
|
1826
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"]);
|
|
1827
1822
|
function sameSite(repository, registry) {
|
|
1828
|
-
const parts = (value) => {
|
|
1829
|
-
try {
|
|
1830
|
-
const url = new URL(value.includes("://") ? value : `https://${value}`);
|
|
1831
|
-
return { host: url.hostname.replace(/^www\./, "").toLowerCase(), path: url.pathname.replace(/\/+$/, "").toLowerCase() };
|
|
1832
|
-
} catch {
|
|
1833
|
-
return null;
|
|
1834
|
-
}
|
|
1835
|
-
};
|
|
1836
1823
|
if (!repository || !registry) return false;
|
|
1837
|
-
const repo =
|
|
1838
|
-
const home =
|
|
1824
|
+
const repo = homeParts(repository);
|
|
1825
|
+
const home = homeParts(registry);
|
|
1839
1826
|
if (!repo || !home || repo.host !== home.host) return false;
|
|
1840
1827
|
if (PATH_TENANTS.has(repo.host)) return repo.path === home.path && repo.path !== "";
|
|
1841
1828
|
return repo.path === "" || home.path === repo.path || home.path.startsWith(`${repo.path}/`);
|
|
@@ -2082,7 +2069,6 @@ async function syncIndex(options) {
|
|
|
2082
2069
|
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
2083
2070
|
const builtAt = now().toISOString();
|
|
2084
2071
|
const cache = await readHttpCache(options.cacheDir);
|
|
2085
|
-
const previousBodies = new Map([...cache].map(([url, c]) => [url, c.sha256]));
|
|
2086
2072
|
const client = new PoliteClient({
|
|
2087
2073
|
...options.fetcher ? { fetcher: options.fetcher } : {},
|
|
2088
2074
|
...options.token ? { token: options.token } : {},
|
|
@@ -2090,7 +2076,6 @@ async function syncIndex(options) {
|
|
|
2090
2076
|
perHost: options.perHost ?? 2,
|
|
2091
2077
|
timeoutMs: options.timeoutMs ?? 15e3,
|
|
2092
2078
|
buckets: githubBuckets(options.token),
|
|
2093
|
-
bucketFor: githubBucketFor,
|
|
2094
2079
|
cache,
|
|
2095
2080
|
now,
|
|
2096
2081
|
...options.sleep ? { sleep: options.sleep } : {}
|
|
@@ -2116,7 +2101,7 @@ async function syncIndex(options) {
|
|
|
2116
2101
|
failure = "could not be parsed";
|
|
2117
2102
|
}
|
|
2118
2103
|
}
|
|
2119
|
-
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.
|
|
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 });
|
|
2120
2105
|
return { id: catalog.id, names };
|
|
2121
2106
|
})
|
|
2122
2107
|
);
|
|
@@ -2201,7 +2186,7 @@ async function syncIndex(options) {
|
|
|
2201
2186
|
const index = cleanData({ schema: "youi.discover-index/v1", format: 2, builtAt, sources, categories: Object.fromEntries(categories), entries: final });
|
|
2202
2187
|
await writeJson(join3(options.cacheDir, SIGNALS_FILE), signals);
|
|
2203
2188
|
await writeJson(join3(options.cacheDir, HEALTH_FILE), cleanData({ checkedAt: builtAt, sources }));
|
|
2204
|
-
await writeHttpCache(options.cacheDir, client.cache
|
|
2189
|
+
await writeHttpCache(options.cacheDir, client.cache);
|
|
2205
2190
|
await writeJson(join3(options.cacheDir, INDEX_FILE2), index);
|
|
2206
2191
|
const registryStatuses = sources.filter((s) => s.health !== null);
|
|
2207
2192
|
const known = final.filter((e) => e.license && e.license !== "unknown").length;
|
|
@@ -2260,7 +2245,7 @@ async function expandAll(cacheDir, options = {}) {
|
|
|
2260
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));
|
|
2261
2246
|
return !options.icons && entries2.length && entries2.filter(isIconEntry).length / entries2.length >= 0.8 ? [] : entries2.map((entry) => applySignals(entry, signals));
|
|
2262
2247
|
}));
|
|
2263
|
-
await writeHttpCache(cacheDir, cache
|
|
2248
|
+
await writeHttpCache(cacheDir, cache);
|
|
2264
2249
|
const chosen = new Set(index.entries.map((e) => e.id));
|
|
2265
2250
|
const entries = dedupe([...index.entries, ...extra.flat()]).entries.filter((e) => !chosen.has(e.id));
|
|
2266
2251
|
const next = `${JSON.stringify(cleanData({ schema: index.schema, format: 2, builtAt: index.builtAt, sources: [], categories: index.categories, entries }))}
|
|
@@ -2322,9 +2307,6 @@ function rolesOf(entry) {
|
|
|
2322
2307
|
const f = fieldsOf(entry);
|
|
2323
2308
|
return CONCEPTS.filter((concept) => conceptScore(concept, f, []) > 0).map((c) => c.id);
|
|
2324
2309
|
}
|
|
2325
|
-
function licenceOf(entry) {
|
|
2326
|
-
return entry.license && entry.license !== "unknown" ? entry.license.spdx : "unknown";
|
|
2327
|
-
}
|
|
2328
2310
|
function searchTextOf(entry) {
|
|
2329
2311
|
const f = fieldsOf(entry);
|
|
2330
2312
|
return [f.name, f.name, f.name, f.title, f.title, f.description, ...f.categories, ...f.packages.map(normalize)].filter(Boolean).join(" ");
|
|
@@ -2451,7 +2433,7 @@ function needTerms(need) {
|
|
|
2451
2433
|
}
|
|
2452
2434
|
async function searchCatalog(store, need, options = {}) {
|
|
2453
2435
|
const filter = filterOf(options);
|
|
2454
|
-
const rows = await store.matchDiscover({ ...filter, terms: needTerms(need), perClass:
|
|
2436
|
+
const rows = await store.matchDiscover({ ...filter, terms: needTerms(need), perClass: CANDIDATES_PER_CLASS });
|
|
2455
2437
|
const ranked = rankEntries(rows.map((r) => r.entry), need, options);
|
|
2456
2438
|
const offset = pageInteger(options.offset, 0, 2147483647);
|
|
2457
2439
|
const page = ranked.scored.slice(offset, offset + pageInteger(options.limit, 10));
|
|
@@ -2661,7 +2643,7 @@ async function preparePreview(cacheDir, id, options = {}) {
|
|
|
2661
2643
|
await assertNoLinks(dir, file);
|
|
2662
2644
|
await writeFileAtomic(join6(dir, file), text);
|
|
2663
2645
|
}
|
|
2664
|
-
const packages = [...new Set([...rewritten.values()].flatMap((t) => importedPackages(t)))].
|
|
2646
|
+
const packages = [...new Set([...rewritten.values()].flatMap((t) => importedPackages(t)))].sort();
|
|
2665
2647
|
const declared = [...new Set([...source.dependencies ?? entry.install?.npm ?? [], ...partDeps].map(packageName))].sort();
|
|
2666
2648
|
const sources = [...rewritten.entries()].filter(([f]) => /\.(?:tsx|jsx|ts|js)$/.test(f)).map(([, t]) => t);
|
|
2667
2649
|
return {
|
|
@@ -2681,9 +2663,138 @@ async function preparePreview(cacheDir, id, options = {}) {
|
|
|
2681
2663
|
};
|
|
2682
2664
|
}
|
|
2683
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
|
+
|
|
2684
2796
|
export {
|
|
2685
2797
|
CONCEPTS,
|
|
2686
|
-
conceptById,
|
|
2687
2798
|
normalize,
|
|
2688
2799
|
hasPhrase,
|
|
2689
2800
|
readNeed,
|
|
@@ -2732,7 +2843,6 @@ export {
|
|
|
2732
2843
|
discoverIndexPath,
|
|
2733
2844
|
SourceStatus,
|
|
2734
2845
|
DiscoverIndex,
|
|
2735
|
-
EMPTY_SIGNALS,
|
|
2736
2846
|
writeJson,
|
|
2737
2847
|
MissingIndexError,
|
|
2738
2848
|
readIndex,
|
|
@@ -2759,6 +2869,7 @@ export {
|
|
|
2759
2869
|
registryName,
|
|
2760
2870
|
passes,
|
|
2761
2871
|
playsRole,
|
|
2872
|
+
licenceOf,
|
|
2762
2873
|
hitOf,
|
|
2763
2874
|
isIconEntry,
|
|
2764
2875
|
rankEntries,
|
|
@@ -2783,7 +2894,6 @@ export {
|
|
|
2783
2894
|
LINKS_PER_ROLE,
|
|
2784
2895
|
sourceClassOf,
|
|
2785
2896
|
rolesOf,
|
|
2786
|
-
licenceOf,
|
|
2787
2897
|
searchTextOf,
|
|
2788
2898
|
itemOf,
|
|
2789
2899
|
ingestCatalog,
|
|
@@ -2801,5 +2911,10 @@ export {
|
|
|
2801
2911
|
safeRelative,
|
|
2802
2912
|
exportNames,
|
|
2803
2913
|
importedNames,
|
|
2804
|
-
preparePreview
|
|
2914
|
+
preparePreview,
|
|
2915
|
+
STANDARD_KIT,
|
|
2916
|
+
conceptOfName,
|
|
2917
|
+
conceptOfPackage,
|
|
2918
|
+
declaredPackages,
|
|
2919
|
+
buildGaps
|
|
2805
2920
|
};
|