itismyskillmarket 1.3.7 → 1.3.9
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/dist/index.js +70 -30
- package/gui/index.html +1 -1
- package/gui/style.css +3 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1528,6 +1528,31 @@ import { fileURLToPath as fileURLToPath3 } from "url";
|
|
|
1528
1528
|
var __filename2 = fileURLToPath3(import.meta.url);
|
|
1529
1529
|
var __dirname2 = dirname(__filename2);
|
|
1530
1530
|
var guiDir = join4(__dirname2, "..", "gui");
|
|
1531
|
+
var cache = /* @__PURE__ */ new Map();
|
|
1532
|
+
function getCached(key) {
|
|
1533
|
+
const entry = cache.get(key);
|
|
1534
|
+
if (!entry) return null;
|
|
1535
|
+
if (Date.now() > entry.expiry) {
|
|
1536
|
+
cache.delete(key);
|
|
1537
|
+
return null;
|
|
1538
|
+
}
|
|
1539
|
+
return entry.data;
|
|
1540
|
+
}
|
|
1541
|
+
function setCache(key, data, ttlMs = 6e4) {
|
|
1542
|
+
cache.set(key, { data, expiry: Date.now() + ttlMs });
|
|
1543
|
+
}
|
|
1544
|
+
async function throttledMap(items, fn, concurrency = 3) {
|
|
1545
|
+
const results = [];
|
|
1546
|
+
for (let i = 0; i < items.length; i += concurrency) {
|
|
1547
|
+
const batch = items.slice(i, i + concurrency);
|
|
1548
|
+
const batchResults = await Promise.all(batch.map((item, idx) => fn(item, i + idx)));
|
|
1549
|
+
results.push(...batchResults);
|
|
1550
|
+
if (i + concurrency < items.length) {
|
|
1551
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
return results;
|
|
1555
|
+
}
|
|
1531
1556
|
var MIME_TYPES = {
|
|
1532
1557
|
".html": "text/html; charset=utf-8",
|
|
1533
1558
|
".js": "application/javascript; charset=utf-8",
|
|
@@ -1570,36 +1595,46 @@ API_ROUTES.GET["/api/skills"] = async (_req, res, url) => {
|
|
|
1570
1595
|
const page = Math.max(1, parseInt(url.searchParams.get("page") || "1"));
|
|
1571
1596
|
const limit = Math.min(100, Math.max(1, parseInt(url.searchParams.get("limit") || "20")));
|
|
1572
1597
|
const search = url.searchParams.get("search") || "";
|
|
1573
|
-
const
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
description: pkg?.description || "",
|
|
1593
|
-
platforms: meta?.platforms || [],
|
|
1594
|
-
author: info.author?.name || pkg?.author?.name || "",
|
|
1595
|
-
homepage: pkg?.homepage || "",
|
|
1596
|
-
repository: pkg?.repository?.url || ""
|
|
1597
|
-
};
|
|
1598
|
-
} catch {
|
|
1599
|
-
return null;
|
|
1598
|
+
const cacheKey = `search:${search}:limit:${limit}`;
|
|
1599
|
+
let searchResult = getCached(cacheKey);
|
|
1600
|
+
if (!searchResult) {
|
|
1601
|
+
searchResult = await searchSkillmarketPackages({
|
|
1602
|
+
from: 0,
|
|
1603
|
+
size: 100,
|
|
1604
|
+
// 一次拉取更多,避免分页
|
|
1605
|
+
keyword: search || void 0
|
|
1606
|
+
});
|
|
1607
|
+
setCache(cacheKey, searchResult, 3e4);
|
|
1608
|
+
}
|
|
1609
|
+
const { packages, total } = searchResult;
|
|
1610
|
+
const skillDetails = await throttledMap(packages, async (pkgName) => {
|
|
1611
|
+
try {
|
|
1612
|
+
const pkgCacheKey = `pkg:${pkgName}`;
|
|
1613
|
+
let info = getCached(pkgCacheKey);
|
|
1614
|
+
if (!info) {
|
|
1615
|
+
info = await fetchNpmPackage(pkgName);
|
|
1616
|
+
if (info) setCache(pkgCacheKey, info, 3e4);
|
|
1600
1617
|
}
|
|
1601
|
-
|
|
1602
|
-
|
|
1618
|
+
if (!info) return null;
|
|
1619
|
+
const latestVersion = info["dist-tags"]?.latest || "unknown";
|
|
1620
|
+
const pkg = info.versions?.[latestVersion];
|
|
1621
|
+
const meta = pkg?.skillmarket;
|
|
1622
|
+
return {
|
|
1623
|
+
id: meta?.id || info.name.replace(/^@[^/]+\//, ""),
|
|
1624
|
+
name: info.name,
|
|
1625
|
+
displayName: meta?.displayName || info.name,
|
|
1626
|
+
version: latestVersion,
|
|
1627
|
+
description: pkg?.description || "",
|
|
1628
|
+
platforms: meta?.platforms || [],
|
|
1629
|
+
author: info.author?.name || pkg?.author?.name || "",
|
|
1630
|
+
homepage: pkg?.homepage || "",
|
|
1631
|
+
repository: pkg?.repository?.url || ""
|
|
1632
|
+
};
|
|
1633
|
+
} catch {
|
|
1634
|
+
return null;
|
|
1635
|
+
}
|
|
1636
|
+
}, 3);
|
|
1637
|
+
const skills = skillDetails.filter(Boolean);
|
|
1603
1638
|
const totalPages = Math.ceil(total / limit) || 1;
|
|
1604
1639
|
jsonResponse(res, 200, { skills, page, totalPages, total });
|
|
1605
1640
|
} catch (err) {
|
|
@@ -1654,7 +1689,12 @@ API_ROUTES.GET["/api/skill-info"] = async (_req, res, url) => {
|
|
|
1654
1689
|
jsonResponse(res, 400, { error: 'Missing "skill" query parameter' });
|
|
1655
1690
|
return;
|
|
1656
1691
|
}
|
|
1657
|
-
const
|
|
1692
|
+
const cacheKey = `skill-info:${skillName}`;
|
|
1693
|
+
let info = getCached(cacheKey);
|
|
1694
|
+
if (!info) {
|
|
1695
|
+
info = await fetchNpmPackage(skillName);
|
|
1696
|
+
if (info) setCache(cacheKey, info, 3e4);
|
|
1697
|
+
}
|
|
1658
1698
|
if (!info) {
|
|
1659
1699
|
jsonResponse(res, 404, { error: `Skill "${skillName}" not found` });
|
|
1660
1700
|
return;
|
package/gui/index.html
CHANGED
package/gui/style.css
CHANGED
|
@@ -109,6 +109,7 @@ nav {
|
|
|
109
109
|
.main-content {
|
|
110
110
|
flex: 1;
|
|
111
111
|
overflow-y: auto;
|
|
112
|
+
min-height: 0; /* flex 子项必须显式允许收缩 */
|
|
112
113
|
padding: 30px;
|
|
113
114
|
}
|
|
114
115
|
|
|
@@ -118,6 +119,8 @@ nav {
|
|
|
118
119
|
|
|
119
120
|
.view.active {
|
|
120
121
|
display: block;
|
|
122
|
+
overflow-y: auto;
|
|
123
|
+
min-height: 0;
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
.view-header {
|