Revert "Merge pull request 'Complete Housekeeping migration and /ase cutover' (#52) from codex/housekeeping-complete into main"
This reverts commit488b6e57c4, reversing changes made tob506b4499a.
This commit is contained in:
1 parent
488b6e57c4
commit
b1ddda66ff
802 files changed
+61370
-76659
No files matched your search
@@ -1,20 +1,9 @@
|
||||
import { PERMS } from "@/lib/permission-slugs";
|
||||
import { HOUSEKEEPING_DOMAIN_IDS } from "../migration/types";
|
||||
import { satisfiesCapability } from "./capability-context";
|
||||
import type {
|
||||
CapabilityRequirement,
|
||||
HousekeepingCapabilityContext,
|
||||
HousekeepingDomainManifest,
|
||||
HousekeepingErrorCode,
|
||||
HousekeepingWidgetDefinition,
|
||||
} from "./contracts";
|
||||
import { ok } from "./contracts";
|
||||
import { reconcilePreferences } from "./preferences/reconcile";
|
||||
import type { HousekeepingPreferences } from "./preferences/schema";
|
||||
import {
|
||||
type HousekeepingProvider,
|
||||
runProvider,
|
||||
} from "./providers/run-provider";
|
||||
|
||||
const approvedDomainIds = new Set<string>(HOUSEKEEPING_DOMAIN_IDS);
|
||||
const knownCapabilitySlugs = new Set<string>(Object.values(PERMS));
|
||||
@@ -28,86 +17,13 @@ export interface HousekeepingRegistry {
|
||||
domains: readonly HousekeepingDomainManifest[];
|
||||
}
|
||||
|
||||
export const WIDGET_POLICY = Object.freeze({ timeoutMs: 2_000 } as const);
|
||||
|
||||
export interface HousekeepingWidgetLoadResult {
|
||||
readonly widgets: readonly { readonly id: string; readonly data: unknown }[];
|
||||
readonly errors: readonly {
|
||||
readonly widgetId: string;
|
||||
readonly code: HousekeepingErrorCode;
|
||||
}[];
|
||||
}
|
||||
|
||||
function widgetProvider(
|
||||
widget: HousekeepingWidgetDefinition,
|
||||
): HousekeepingProvider<unknown> {
|
||||
return {
|
||||
id: widget.id,
|
||||
capability: widget.capability,
|
||||
async run(context, signal) {
|
||||
const result = await widget.load(context, signal);
|
||||
return result.ok ? ok([result.data], result.correlationId) : result;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export async function loadHousekeepingWidgets(
|
||||
registry: HousekeepingRegistry,
|
||||
preferences: HousekeepingPreferences,
|
||||
context: HousekeepingCapabilityContext,
|
||||
timeoutMs: number = WIDGET_POLICY.timeoutMs,
|
||||
): Promise<HousekeepingWidgetLoadResult> {
|
||||
const reconciled = reconcilePreferences(preferences, registry, context);
|
||||
const enabledOptionalIds = new Set(reconciled.enabledOptionalWidgetIds);
|
||||
const selected = registry.domains
|
||||
.filter((domain) => satisfiesCapability(context, domain.capability))
|
||||
.flatMap((domain) => domain.widgets)
|
||||
.filter(
|
||||
(widget) =>
|
||||
satisfiesCapability(context, widget.capability) &&
|
||||
(widget.kind === "mandatory" || enabledOptionalIds.has(widget.id)),
|
||||
);
|
||||
const selectedById = new Map(selected.map((widget) => [widget.id, widget]));
|
||||
const orderedIds = reconciled.widgetOrder.filter((id) =>
|
||||
selectedById.has(id),
|
||||
);
|
||||
for (const widget of selected) {
|
||||
if (!orderedIds.includes(widget.id)) orderedIds.push(widget.id);
|
||||
}
|
||||
const ordered = orderedIds.flatMap((id) => {
|
||||
const widget = selectedById.get(id);
|
||||
return widget ? [widget] : [];
|
||||
});
|
||||
const outcomes = await Promise.all(
|
||||
ordered.map((widget) =>
|
||||
runProvider(widgetProvider(widget), context, timeoutMs),
|
||||
),
|
||||
);
|
||||
|
||||
return {
|
||||
widgets: outcomes.flatMap((outcome) =>
|
||||
outcome.error === undefined
|
||||
? [{ id: outcome.providerId, data: outcome.items[0] }]
|
||||
: [],
|
||||
),
|
||||
errors: outcomes.flatMap((outcome) =>
|
||||
outcome.error === undefined
|
||||
? []
|
||||
: [{ widgetId: outcome.providerId, code: outcome.error }],
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
export function createHousekeepingRegistry(
|
||||
manifests: readonly HousekeepingDomainManifest[],
|
||||
): HousekeepingRegistry {
|
||||
const domainIds = new Set<string>();
|
||||
const routeIds = new Set<string>();
|
||||
const routeHrefs = new Set<string>();
|
||||
const routeShapes = new Set<string>();
|
||||
const searchProviderIds = new Set<string>();
|
||||
const inboxSourceIds = new Set<string>();
|
||||
const widgetIds = new Set<string>();
|
||||
const registryEntryIds = new Set<string>();
|
||||
|
||||
for (const manifest of manifests) {
|
||||
if (!approvedDomainIds.has(manifest.id)) {
|
||||
@@ -118,11 +34,8 @@ export function createHousekeepingRegistry(
|
||||
}
|
||||
domainIds.add(manifest.id);
|
||||
|
||||
if (
|
||||
manifest.canonicalHref !==
|
||||
(manifest.id === "operations" ? "/ase" : `/ase/${manifest.id}`)
|
||||
) {
|
||||
throw new Error(`invalid canonical href: ${manifest.canonicalHref}`);
|
||||
if (manifest.previewHref !== `/admin-next/${manifest.id}`) {
|
||||
throw new Error(`invalid preview href: ${manifest.previewHref}`);
|
||||
}
|
||||
validateNonEmpty(manifest.labelKey, "label key");
|
||||
validateNonEmpty(manifest.descriptionKey, "description key");
|
||||
@@ -131,19 +44,19 @@ export function createHousekeepingRegistry(
|
||||
manifest.searchProviders,
|
||||
manifest.id,
|
||||
"search provider",
|
||||
searchProviderIds,
|
||||
registryEntryIds,
|
||||
);
|
||||
validateOwnedRegistryEntries(
|
||||
manifest.inboxSources,
|
||||
manifest.id,
|
||||
"inbox source",
|
||||
inboxSourceIds,
|
||||
registryEntryIds,
|
||||
);
|
||||
validateOwnedRegistryEntries(
|
||||
manifest.widgets,
|
||||
manifest.id,
|
||||
"widget",
|
||||
widgetIds,
|
||||
registryEntryIds,
|
||||
);
|
||||
for (const widget of manifest.widgets) {
|
||||
if (widget.kind !== "mandatory" && widget.kind !== "optional") {
|
||||
@@ -159,20 +72,11 @@ export function createHousekeepingRegistry(
|
||||
routeIds.add(route.id);
|
||||
|
||||
validateNonEmpty(route.href, "route href");
|
||||
if (route.href !== "/ase" && !route.href.startsWith("/ase/")) {
|
||||
throw new Error(`invalid canonical route href: ${route.href}`);
|
||||
}
|
||||
if (routeHrefs.has(route.href)) {
|
||||
throw new Error(`duplicate route href: ${route.href}`);
|
||||
}
|
||||
routeHrefs.add(route.href);
|
||||
|
||||
const routeShape = dynamicRouteShape(route.href);
|
||||
if (routeShape && routeShapes.has(routeShape)) {
|
||||
throw new Error(`duplicate dynamic route shape: ${routeShape}`);
|
||||
}
|
||||
if (routeShape) routeShapes.add(routeShape);
|
||||
|
||||
validateNonEmpty(route.labelKey, "route label key");
|
||||
validateCapability(route.capability);
|
||||
}
|
||||
@@ -181,20 +85,6 @@ export function createHousekeepingRegistry(
|
||||
return { domains: Object.freeze([...manifests]) };
|
||||
}
|
||||
|
||||
function dynamicRouteShape(href: string): string | null {
|
||||
let hasDynamicSegment = false;
|
||||
const shape = href
|
||||
.split("/")
|
||||
.map((segment) => {
|
||||
if (!segment.startsWith(":")) return segment;
|
||||
hasDynamicSegment = true;
|
||||
return ":";
|
||||
})
|
||||
.join("/");
|
||||
|
||||
return hasDynamicSegment ? shape : null;
|
||||
}
|
||||
|
||||
function validateOwnedRegistryEntries(
|
||||
entries: readonly OwnedRegistryEntry[],
|
||||
owner: HousekeepingDomainManifest["id"],
|
||||
|
||||
Reference in new issue
Block a user