mirror of
https://github.com/openclaw/clawhub.git
synced 2026-08-15 01:12:11 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93f2187e2c |
+2
-1
@@ -384,7 +384,8 @@ const souls = defineTable({
|
||||
.index("by_slug", ["slug"])
|
||||
.index("by_owner", ["ownerUserId"])
|
||||
.index("by_owner_publisher", ["ownerPublisherId"])
|
||||
.index("by_updated", ["updatedAt"]);
|
||||
.index("by_updated", ["updatedAt"])
|
||||
.index("by_active_updated", ["softDeletedAt", "updatedAt"]);
|
||||
|
||||
const skillVersions = defineTable({
|
||||
skillId: v.id("skills"),
|
||||
|
||||
+74
-1
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { getSoulBySlugInternal, insertVersion } from "./souls";
|
||||
import { getSoulBySlugInternal, insertVersion, list } from "./souls";
|
||||
|
||||
type WrappedHandler<TArgs> = {
|
||||
_handler: (ctx: unknown, args: TArgs) => Promise<unknown>;
|
||||
@@ -10,6 +10,7 @@ const insertVersionHandler = (insertVersion as unknown as WrappedHandler<Record<
|
||||
const getSoulBySlugInternalHandler = (
|
||||
getSoulBySlugInternal as unknown as WrappedHandler<{ slug: string }>
|
||||
)._handler;
|
||||
const listHandler = (list as unknown as WrappedHandler<{ ownerUserId?: string; limit?: number }>)._handler;
|
||||
|
||||
describe("souls.insertVersion", () => {
|
||||
it("throws a soul-specific ownership error for non-owners", async () => {
|
||||
@@ -139,3 +140,75 @@ describe("souls.insertVersion", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("souls.list", () => {
|
||||
it("uses the active browse index and only takes the requested limit", async () => {
|
||||
let requestedIndex: string | null = null;
|
||||
let requestedSoftDeletedAt: number | undefined;
|
||||
let requestedLimit: number | null = null;
|
||||
|
||||
const result = await listHandler(
|
||||
{
|
||||
db: {
|
||||
query: vi.fn((table: string) => {
|
||||
if (table !== "souls") throw new Error(`unexpected table ${table}`);
|
||||
return {
|
||||
withIndex: (
|
||||
name: string,
|
||||
build:
|
||||
| ((q: { eq: (field: string, value: undefined) => unknown }) => unknown)
|
||||
| undefined,
|
||||
) => {
|
||||
requestedIndex = name;
|
||||
const q = {
|
||||
eq: (field: string, value: undefined) => {
|
||||
if (field !== "softDeletedAt") throw new Error(`unexpected field ${field}`);
|
||||
requestedSoftDeletedAt = value;
|
||||
return q;
|
||||
},
|
||||
};
|
||||
build?.(q);
|
||||
return {
|
||||
order: () => ({
|
||||
take: async (limit: number) => {
|
||||
requestedLimit = limit;
|
||||
return [
|
||||
{
|
||||
_id: "souls:1",
|
||||
_creationTime: 1,
|
||||
slug: "demo-soul",
|
||||
displayName: "Demo Soul",
|
||||
summary: "A demo soul",
|
||||
ownerUserId: "users:owner",
|
||||
ownerPublisherId: undefined,
|
||||
latestVersionId: undefined,
|
||||
tags: {},
|
||||
softDeletedAt: undefined,
|
||||
stats: { downloads: 1, stars: 2, versions: 3, comments: 4 },
|
||||
createdAt: 1,
|
||||
updatedAt: 2,
|
||||
},
|
||||
];
|
||||
},
|
||||
}),
|
||||
};
|
||||
},
|
||||
};
|
||||
}),
|
||||
},
|
||||
} as never,
|
||||
{ limit: 7 } as never,
|
||||
);
|
||||
|
||||
expect(requestedIndex).toBe("by_active_updated");
|
||||
expect(requestedSoftDeletedAt).toBeUndefined();
|
||||
expect(requestedLimit).toBe(7);
|
||||
expect(result).toEqual([
|
||||
expect.objectContaining({
|
||||
_id: "souls:1",
|
||||
slug: "demo-soul",
|
||||
displayName: "Demo Soul",
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
+2
-3
@@ -138,11 +138,10 @@ export const list = query({
|
||||
}
|
||||
const entries = await ctx.db
|
||||
.query("souls")
|
||||
.withIndex("by_active_updated", (q) => q.eq("softDeletedAt", undefined))
|
||||
.order("desc")
|
||||
.take(limit * 5);
|
||||
.take(limit);
|
||||
return entries
|
||||
.filter((soul) => !soul.softDeletedAt)
|
||||
.slice(0, limit)
|
||||
.map((soul) => toPublicSoul(soul))
|
||||
.filter((soul): soul is NonNullable<typeof soul> => Boolean(soul));
|
||||
},
|
||||
|
||||
@@ -61,17 +61,17 @@ export function CliAuth({ navigate = (url: string) => window.location.assign(url
|
||||
hash.set("token", result.token);
|
||||
hash.set("registry", registry);
|
||||
hash.set("state", state);
|
||||
const callbackUrl = `${redirectUri}#${hash.toString()}`;
|
||||
const redirectUrl = `${redirectUri}#${hash.toString()}`;
|
||||
// Render the fallback token before attempting navigation so it is
|
||||
// always visible if the browser blocks or fails the http:// redirect
|
||||
// (e.g. ERR_CONNECTION_REFUSED when the CLI server has already shut
|
||||
// down, or Chrome's HTTPS-first mode interfering with localhost).
|
||||
flushSync(() => {
|
||||
setToken(result.token);
|
||||
setCallbackUrl(callbackUrl);
|
||||
setCallbackUrl(redirectUrl);
|
||||
setStatus("Redirecting to CLI…");
|
||||
});
|
||||
navigate(callbackUrl);
|
||||
navigate(redirectUrl);
|
||||
};
|
||||
|
||||
void run().catch((error) => {
|
||||
|
||||
Reference in New Issue
Block a user