mirror of
https://github.com/openclaw/clawhub.git
synced 2026-08-14 00:47:57 +00:00
fix(claws): preserve discovery cursor sort
This commit is contained in:
@@ -1825,7 +1825,10 @@ function makePluginExportIndexKey(row: Record<string, unknown>) {
|
||||
return [row.softDeletedAt, row.family, row.updatedAt, row._creationTime, row._id] as unknown[];
|
||||
}
|
||||
|
||||
function makePluginExportCtx(digests: Array<Record<string, unknown>>) {
|
||||
function makePluginExportCtx(
|
||||
digests: Array<Record<string, unknown>>,
|
||||
options?: { recommendationScoresMissing?: () => boolean },
|
||||
) {
|
||||
const packagesById = new Map(
|
||||
digests.map((digest) => [
|
||||
String(digest.packageId),
|
||||
@@ -1851,6 +1854,15 @@ function makePluginExportCtx(digests: Array<Record<string, unknown>>) {
|
||||
db: {
|
||||
get: vi.fn(async (id: string) => packagesById.get(id) ?? null),
|
||||
query: vi.fn((table: string) => {
|
||||
if (table === "packages") {
|
||||
return {
|
||||
withIndex: vi.fn(() => ({
|
||||
first: vi.fn(async () =>
|
||||
options?.recommendationScoresMissing?.() ? { _id: "packages:missing-score" } : null,
|
||||
),
|
||||
})),
|
||||
};
|
||||
}
|
||||
if (table !== "packageSearchDigest") throw new Error(`Unexpected table ${table}`);
|
||||
return {
|
||||
withIndex: vi.fn(
|
||||
@@ -17008,6 +17020,36 @@ describe("restorePackageInternal", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps the cursor's effective sort when recommendation backfill completes between pages", async () => {
|
||||
const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS;
|
||||
delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS;
|
||||
let recommendationScoresMissing = true;
|
||||
const ctx = makePluginExportCtx(
|
||||
[
|
||||
makeDigest("newer-plugin", { updatedAt: 2, _creationTime: 2 }),
|
||||
makeDigest("older-plugin", { updatedAt: 1, _creationTime: 1 }),
|
||||
],
|
||||
{ recommendationScoresMissing: () => recommendationScoresMissing },
|
||||
);
|
||||
|
||||
try {
|
||||
const first = await listPublicPageHandler(ctx, {
|
||||
sort: "recommended",
|
||||
paginationOpts: { cursor: null, numItems: 1 },
|
||||
});
|
||||
recommendationScoresMissing = false;
|
||||
const second = await listPublicPageHandler(ctx, {
|
||||
sort: "recommended",
|
||||
paginationOpts: { cursor: first.continueCursor, numItems: 1 },
|
||||
});
|
||||
expect(first.page.map((entry) => entry.name)).toEqual(["newer-plugin"]);
|
||||
expect(second.page.map((entry) => entry.name)).toEqual(["older-plugin"]);
|
||||
} finally {
|
||||
if (previous === undefined) delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS;
|
||||
else process.env.CLAWHUB_EXPERIMENTAL_CLAWS = previous;
|
||||
}
|
||||
});
|
||||
|
||||
it("does not let disabled Claws starve unfiltered public search", async () => {
|
||||
const previous = process.env.CLAWHUB_EXPERIMENTAL_CLAWS;
|
||||
delete process.env.CLAWHUB_EXPERIMENTAL_CLAWS;
|
||||
|
||||
+27
-6
@@ -3476,6 +3476,23 @@ function encodeStablePackageDiscoveryCursor(state: StablePackageDiscoveryCursorS
|
||||
})}`;
|
||||
}
|
||||
|
||||
function readStablePackageDiscoveryCursorSort(
|
||||
raw: string | null | undefined,
|
||||
): "updated" | "recommended" | null {
|
||||
if (!raw?.startsWith(STABLE_PACKAGE_DISCOVERY_CURSOR_PREFIX)) return null;
|
||||
try {
|
||||
const parsed = JSON.parse(raw.slice(STABLE_PACKAGE_DISCOVERY_CURSOR_PREFIX.length)) as {
|
||||
v?: unknown;
|
||||
sort?: unknown;
|
||||
};
|
||||
return parsed.v === 1 && (parsed.sort === "updated" || parsed.sort === "recommended")
|
||||
? parsed.sort
|
||||
: null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function decodeStablePackageDiscoveryCursor(
|
||||
raw: string | null | undefined,
|
||||
sort: StablePackageDiscoveryCursorState["sort"],
|
||||
@@ -3930,17 +3947,21 @@ async function listStablePackageDiscoveryPage(
|
||||
Math.min(args.paginationOpts.numItems, MAX_PUBLIC_LIST_PAGE_SIZE),
|
||||
);
|
||||
const requestedSort = args.sort ?? "updated";
|
||||
const sort =
|
||||
requestedSort === "recommended" &&
|
||||
(
|
||||
const cursorSort =
|
||||
requestedSort === "recommended"
|
||||
? readStablePackageDiscoveryCursorSort(args.paginationOpts.cursor)
|
||||
: null;
|
||||
let sort = cursorSort ?? requestedSort;
|
||||
if (!cursorSort && requestedSort === "recommended") {
|
||||
const recommendationScoresMissing = (
|
||||
await Promise.all(
|
||||
STABLE_PACKAGE_FAMILIES.map(
|
||||
async (family) => await hasMissingPackageRecommendedScore(ctx, family),
|
||||
),
|
||||
)
|
||||
).some(Boolean)
|
||||
? "updated"
|
||||
: requestedSort;
|
||||
).some(Boolean);
|
||||
if (recommendationScoresMissing) sort = "updated";
|
||||
}
|
||||
const cursor = decodeStablePackageDiscoveryCursor(args.paginationOpts.cursor, sort);
|
||||
const scanLimit = Math.min(MAX_PUBLIC_LIST_PAGE_SIZE, Math.max(targetCount * 5, 50));
|
||||
const loadFamily = async (family: StablePackageFamily) => {
|
||||
|
||||
Reference in New Issue
Block a user