mirror of
https://github.com/openclaw/clawhub.git
synced 2026-08-14 17:02:11 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b192be11c | ||
|
|
077a5f9d27 |
@@ -1309,6 +1309,97 @@ describe("public skill list deterministic cursors", () => {
|
||||
expect(result.items[0]).toMatchObject({ latestVersion: null });
|
||||
});
|
||||
|
||||
it("falls back to the general trending snapshot during non-suspicious rollout", async () => {
|
||||
const digest = makeSearchDigest();
|
||||
const requestedKinds: string[] = [];
|
||||
const ctx = {
|
||||
db: {
|
||||
query: vi.fn((table: string) => {
|
||||
if (table === "skillLeaderboards") {
|
||||
return {
|
||||
withIndex: vi.fn(
|
||||
(
|
||||
_indexName: string,
|
||||
builder: (q: { eq: (field: string, value: string) => unknown }) => unknown,
|
||||
) => {
|
||||
const queryBuilder = {
|
||||
eq: (_field: string, value: string) => {
|
||||
requestedKinds.push(value);
|
||||
return queryBuilder;
|
||||
},
|
||||
};
|
||||
builder(queryBuilder);
|
||||
return {
|
||||
order: () => ({
|
||||
first: async () =>
|
||||
requestedKinds.at(-1) === "trending_non_suspicious"
|
||||
? null
|
||||
: { items: [{ skillId: "skills:demo" }] },
|
||||
}),
|
||||
};
|
||||
},
|
||||
),
|
||||
};
|
||||
}
|
||||
if (table === "skillSearchDigest") {
|
||||
return {
|
||||
withIndex: () => ({
|
||||
unique: async () => digest,
|
||||
}),
|
||||
};
|
||||
}
|
||||
throw new Error(`unexpected table ${table}`);
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
const result = await listPublicTrendingPageHandler(ctx as never, {
|
||||
limit: 10,
|
||||
nonSuspiciousOnly: true,
|
||||
});
|
||||
|
||||
expect(requestedKinds).toEqual(["trending_non_suspicious", "trending"]);
|
||||
expect(result.items).toHaveLength(1);
|
||||
expect(result.items[0]).toMatchObject({ skill: { slug: "demo" } });
|
||||
});
|
||||
|
||||
it("warms trending with recent public skills before the first snapshot exists", async () => {
|
||||
const digest = makeSearchDigest({ updatedAt: 12 });
|
||||
const ctx = {
|
||||
db: {
|
||||
query: vi.fn((table: string) => {
|
||||
if (table === "skillLeaderboards") {
|
||||
return {
|
||||
withIndex: () => ({
|
||||
order: () => ({
|
||||
first: async () => null,
|
||||
}),
|
||||
}),
|
||||
};
|
||||
}
|
||||
if (table === "skillSearchDigest") {
|
||||
return {
|
||||
withIndex: () => ({
|
||||
order: () => ({
|
||||
take: async () => [digest],
|
||||
}),
|
||||
}),
|
||||
};
|
||||
}
|
||||
throw new Error(`unexpected table ${table}`);
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
const result = await listPublicTrendingPageHandler(ctx as never, {
|
||||
limit: 10,
|
||||
nonSuspiciousOnly: true,
|
||||
});
|
||||
|
||||
expect(result.items).toHaveLength(1);
|
||||
expect(result.items[0]).toMatchObject({ skill: { slug: "demo" } });
|
||||
});
|
||||
|
||||
it("keeps verified legacy trending latest versions without owner markers", async () => {
|
||||
const legacyDigest = makeSearchDigest({
|
||||
latestVersionSkillId: undefined,
|
||||
|
||||
+31
-2
@@ -5868,13 +5868,42 @@ export const listPublicTrendingPage = query({
|
||||
const kind = args.nonSuspiciousOnly
|
||||
? TRENDING_NON_SUSPICIOUS_LEADERBOARD_KIND
|
||||
: TRENDING_LEADERBOARD_KIND;
|
||||
const leaderboard = await ctx.db
|
||||
let leaderboard = await ctx.db
|
||||
.query("skillLeaderboards")
|
||||
.withIndex("by_kind", (q) => q.eq("kind", kind))
|
||||
.order("desc")
|
||||
.first();
|
||||
|
||||
if (!leaderboard) return { items: [], nextCursor: null };
|
||||
// Older deployments may have the general snapshot but not the
|
||||
// non-suspicious snapshot yet. Keep trending populated during rollout.
|
||||
if (!leaderboard && args.nonSuspiciousOnly) {
|
||||
leaderboard = await ctx.db
|
||||
.query("skillLeaderboards")
|
||||
.withIndex("by_kind", (q) => q.eq("kind", TRENDING_LEADERBOARD_KIND))
|
||||
.order("desc")
|
||||
.first();
|
||||
}
|
||||
|
||||
if (!leaderboard) {
|
||||
// The first leaderboard snapshot may not exist yet after deployment.
|
||||
// Use a bounded recent catalog warm-up instead of rendering an empty page.
|
||||
const fallbackDigests = await ctx.db
|
||||
.query("skillSearchDigest")
|
||||
.withIndex("by_active_updated", (q) => q.eq("softDeletedAt", undefined))
|
||||
.order("desc")
|
||||
.take(Math.min(Math.max(limit * 8, limit), 200));
|
||||
const fallbackItems: PublicSkillEntry[] = [];
|
||||
for (const digest of fallbackDigests) {
|
||||
if (args.nonSuspiciousOnly && digest.isSuspicious) continue;
|
||||
if (categorySlug && !resolveStoredSkillCategories(digest).includes(categorySlug)) continue;
|
||||
if (topic && !getCatalogTopicSlugs(digest.topics).includes(topic)) continue;
|
||||
const item = await buildPublicSkillEntryFromDigest(ctx, digest);
|
||||
if (!item) continue;
|
||||
fallbackItems.push(item);
|
||||
if (fallbackItems.length >= limit) break;
|
||||
}
|
||||
return { items: fallbackItems, nextCursor: null };
|
||||
}
|
||||
|
||||
const items: PublicSkillEntry[] = [];
|
||||
for (const entry of leaderboard.items) {
|
||||
|
||||
Reference in New Issue
Block a user