feat(housekeeping): personalize command deck

This commit is contained in:
2026-08-30 16:55:10 +02:00
parent 300ac0ef95
commit 5574e601bb
12 changed files with 1549 additions and 1 deletions
@@ -30,13 +30,19 @@ import {
anyCapability,
type CanonicalHousekeepingHref,
type CapabilityRequirement,
type HousekeepingCapabilityContext,
type HousekeepingDomainManifest,
type HousekeepingInboxSource,
type HousekeepingSearchProvider,
type HousekeepingWidgetDefinition,
ok,
} from "./contracts";
import { createHousekeepingRegistry } from "./registry";
import { defaultHousekeepingPreferences } from "./preferences/schema";
import {
createHousekeepingRegistry,
loadHousekeepingWidgets,
WIDGET_POLICY,
} from "./registry";
const manifest = (
id: HousekeepingDomainId,
@@ -565,6 +571,88 @@ describe("housekeeping registry", () => {
).toThrow("invalid widget kind: people.queue");
});
it("loads only authorized mandatory and enabled optional widgets with isolated timeouts", async () => {
const context: HousekeepingCapabilityContext = {
actor: { id: 42, username: "operator", rank: 7 },
isSuperAdmin: false,
has: (slug) => slug === PERMS.USERS_VIEW,
hasAny: (...slugs) => slugs.includes(PERMS.USERS_VIEW),
hasAll: (...slugs) => slugs.every((slug) => slug === PERMS.USERS_VIEW),
};
let deniedCalls = 0;
const base = {
...manifest("people"),
capability: anyCapability(PERMS.USERS_VIEW),
};
const registry = createHousekeepingRegistry([
{
...base,
widgets: [
{
...widget("people.required"),
kind: "mandatory",
load: async () => ok("required", "required"),
},
{
...widget("people.optional"),
load: async () => ok("optional", "optional"),
},
{
...widget(
"people.denied",
"people",
anyCapability(PERMS.BANS_VIEW),
),
load: async () => {
deniedCalls += 1;
return ok("denied", "denied");
},
},
{
...widget("people.slow"),
load: async (_context, signal) =>
new Promise((resolve) => {
signal.addEventListener("abort", () =>
resolve(ok("late", "late")),
);
}),
},
],
},
]);
const preferences = {
...defaultHousekeepingPreferences(),
widgetOrder: [
"stale.widget",
"people.optional",
"people.required",
"people.slow",
],
enabledOptionalWidgetIds: [
"people.optional",
"people.denied",
"people.slow",
],
};
const result = await loadHousekeepingWidgets(
registry,
preferences,
context,
5,
);
expect(WIDGET_POLICY.timeoutMs).toBe(2_000);
expect(result.widgets).toEqual([
{ id: "people.optional", data: "optional" },
{ id: "people.required", data: "required" },
]);
expect(result.errors).toEqual([
{ widgetId: "people.slow", code: "TIMEOUT" },
]);
expect(deniedCalls).toBe(0);
});
it("rejects empty domain and route capability requirements", () => {
expect(() =>
createHousekeepingRegistry([