feat: consolidate shared UI and harden browser authority for release

This commit is contained in:
2026-09-08 01:35:05 +02:00
parent ac40774785
commit b75ca34295
143 changed files with 8664 additions and 752 deletions
+39
View File
@@ -3,6 +3,7 @@ import type {
DashboardWidgetsUiCapability,
OrganizationFunctionActionContext,
OrganizationFunctionActionContribution,
PlatformNavItem,
PlatformWebModule
} from "../src/types";
import {
@@ -18,6 +19,7 @@ import {
visibleRoutesForProjection
} from "../src/platform/views";
import { groupNavigationItems } from "../src/platform/productAreas";
import { inheritedNavigationLayout, materializeNavigationLayout, moveNavigationEntry, navigationEditorOrder } from "../src/components/navigationPreferenceLayout";
import {
composeProductSurfaces,
projectProductNavigation
@@ -197,6 +199,24 @@ assert(
"flat navigation should retain every authorized destination"
);
const railItems = [
{ to: "/files", label: "Files", surfaceId: "files", navigationId: "files", navigationCustomLayout: true, navigationLayoutSource: "tenant", navigationSection: { id: "separator:a", label: "A" } },
{ to: "/mail", label: "Mail", surfaceId: "mail", navigationId: "messages", navigationLocked: true, navigationCustomLayout: true, navigationLayoutSource: "tenant", navigationSection: { id: "separator:b", label: "B" } }
];
assert(groupNavigationItems(railItems, []).map((group) => group.label).join() === "A,B", "explicit separators survive module-default grouping and filtering");
const viewNavigation = { contract_version: "1" as const, order: ["messages", "separator:view", "files"], hidden: ["mail"], separators: [{ id: "separator:view", label: "View" }] };
const viewGroups = groupNavigationItems(railItems, [], { navigation: viewNavigation });
assert(viewGroups[0]?.items[0]?.to === "/mail", "View layout supports navigation aliases and cannot hide locked module");
assert(viewGroups[1]?.label === "View" && viewGroups.flatMap((group) => group.items).length === 2, "View separators retain every authorized item once");
assert(groupNavigationItems(railItems.map((item) => ({ ...item, navigationLayoutSource: "user" })), [], { navigation: viewNavigation })[0]?.items[0]?.to === "/files", "personal layout takes precedence over View presentation order");
assert(groupNavigationItems(railItems.map((item) => ({ ...item, navigationVisibilitySource: "user" })), [], { navigation: { ...viewNavigation, hidden: ["files"] } }).flatMap((group) => group.items).some((item) => item.to === "/files"), "legacy personal visibility-only preferences also take precedence over View navigation hiding");
const inheritedRail = inheritedNavigationLayout(railItems, "system", []);
assert(inheritedRail.order.join() === "files,messages", "system baseline ignores current tenant grouping");
const materialized = materializeNavigationLayout({ contract_version: "1", order: ["mail", "files"], hidden: [] }, { contract_version: "1", order: ["separator:work", "files", "mail"], hidden: [], separators: [{ id: "separator:work", label: "Work" }] });
assert(materialized.order.join() === "separator:work,mail,files", "legacy drafts gain inherited separator without losing their item order");
assert(moveNavigationEntry(["a", "separator:b", "c"], "c", "a").join() === "c,a,separator:b", "drag reorder treats separators as ordinary ordered entries");
assert(navigationEditorOrder(railItems, { contract_version: "1", order: ["files", "messages"], hidden: ["files"], separators: [] }).join() === "messages", "removed modules leave editor list while remaining available for re-add");
const productSurfaceExplanation = {
reason: "authorization" as const,
title: "Messages are unavailable",
@@ -293,6 +313,25 @@ const mailOnlyNavigation = projectProductNavigation(
messageSurfaceModules,
{ scopes: ["mail:mailbox:read"] } as AuthInfo
);
const reorderedOwners: PlatformNavItem[] = [
{ to: "/postbox", label: "Postbox", order: 0, navigationId: "postbox.navigation.postbox", surfaceId: "postbox.navigation.postbox", anyOf: ["postbox:message:read"], navigationLocked: true, navigationLockSource: "system", navigationCustomLayout: true, navigationSection: { id: "separator:locked", label: "Locked group" } },
{ to: "/files", label: "Files", order: 1 },
{ to: "/mail", label: "Mail", order: 2, navigationId: "mail.navigation.mail", surfaceId: "mail.navigation.mail", anyOf: ["mail:mailbox:read"] }
];
const reorderedProductNavigation = projectProductNavigation(reorderedOwners, messageSurfaceModules, { scopes: ["mail:mailbox:read", "postbox:message:read"] } as AuthInfo);
const reorderedMessages = reorderedProductNavigation.primaryItems[0];
assert(reorderedProductNavigation.primaryItems.map((item) => item.to).join() === "/messages,/files", "composed placement follows earliest effective owner order rather than fixed contributor priority");
assert(reorderedMessages.navigationSection?.id === "separator:locked", "composed entry keeps the earliest owner separator");
assert(reorderedMessages.navigationAliases?.includes("mail.navigation.mail") && reorderedMessages.navigationAliases?.includes("postbox.navigation.postbox"), "composition retains every consumed owner navigation ID for layout aliases");
assert(reorderedMessages.navigationLocked && reorderedMessages.navigationLockSource === "system", "a secondary owner's system lock survives composition with an unlocked primary owner");
assert(composeProductSurfaces(messageSurfaceModules)[0].contributors[0].moduleId === "mail", "custom rail placement never changes operational contributor priority");
const lockedAfterView = groupNavigationItems(reorderedProductNavigation.primaryItems, [], { navigation: { contract_version: "1", order: [], hidden: ["communication.messages", "mail.navigation.mail", "postbox.navigation.postbox"], separators: [] } });
assert(lockedAfterView.flatMap((group) => group.items).some((item) => item.to === "/messages"), "View aliases cannot hide a composed entry locked by any consumed authorized owner");
const aliasOrdered = groupNavigationItems(reorderedProductNavigation.primaryItems, [], { navigation: { contract_version: "1", order: ["/files", "separator:view", "mail.navigation.mail"], hidden: [], separators: [{ id: "separator:view", label: "View group" }] } });
assert(aliasOrdered[0]?.items[0]?.to === "/files" && aliasOrdered[1]?.items[0]?.to === "/messages" && aliasOrdered[1]?.label === "View group", "View ordering recognizes non-placement owner aliases and assigns the requested separator exactly once");
const unauthorizedLockedOwner = projectProductNavigation(reorderedOwners, messageSurfaceModules, { scopes: ["mail:mailbox:read"] } as AuthInfo).primaryItems.find((item) => item.to === "/messages");
assert(unauthorizedLockedOwner?.navigationLocked === false && !unauthorizedLockedOwner.navigationAliases?.includes("postbox.navigation.postbox"), "unavailable optional owners contribute neither aliases nor lock metadata");
assert(
mailOnlyNavigation.primaryItems.map((item) => item.to).join(",") === "/messages",
"composition should remain stable when only one optional contributor is authorized"