refactor(console-web): centralize page lifecycle and UI composition
Declare navigation, page metadata, lazy loaders, and keep-alive policy in one registry. Retain primary iframe pages until a continuous inactive TTL expires and expose typed show, hide, and unload events through the Page SDK. Move business-aware components out of the UI primitive directory, migrate views and Host dialogs to variant primitives and local Tailwind classes, and remove the global component style layer. Keep native dialogs inside the overlay flex context so they remain centered. Cover iframe expiration, lifecycle dispatch, UI dependency direction, and global style ownership with focused tests.
This commit is contained in:
@@ -4,7 +4,7 @@ import { FrameCache } from "../src/host/frame-cache.ts";
|
||||
|
||||
test("retains opted-in iframe identities across navigation", () => {
|
||||
const cache = new FrameCache("services", {
|
||||
maxKeepAliveEntries: 2,
|
||||
inactiveTtlMs: 1_000,
|
||||
shouldKeepAlive: (view) => view === "services" || view === "settings",
|
||||
});
|
||||
const services = cache.snapshot()[0];
|
||||
@@ -26,7 +26,7 @@ test("retains opted-in iframe identities across navigation", () => {
|
||||
|
||||
test("recreates non-keep-alive documents after leaving them", () => {
|
||||
const cache = new FrameCache("overview", {
|
||||
maxKeepAliveEntries: 2,
|
||||
inactiveTtlMs: 1_000,
|
||||
shouldKeepAlive: () => false,
|
||||
});
|
||||
const firstInstance = cache.snapshot()[0].instance;
|
||||
@@ -41,40 +41,72 @@ test("recreates non-keep-alive documents after leaving them", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("evicts inactive keep-alive documents in LRU order", () => {
|
||||
test("expires a keep-alive document after its continuous hidden TTL", () => {
|
||||
let now = 5_000;
|
||||
const cache = new FrameCache("services", {
|
||||
maxKeepAliveEntries: 2,
|
||||
inactiveTtlMs: 1_000,
|
||||
shouldKeepAlive: () => true,
|
||||
now: () => now,
|
||||
});
|
||||
const servicesInstance = cache.snapshot()[0].instance;
|
||||
|
||||
// Time spent visible does not consume the inactive TTL.
|
||||
now = 50_000;
|
||||
cache.activate("settings");
|
||||
const overflow = cache.activate("instances");
|
||||
now = 50_999;
|
||||
assert.equal(cache.timeUntilExpiration(), 1);
|
||||
assert.deepEqual(cache.expireInactive().removed, []);
|
||||
|
||||
now = 51_000;
|
||||
const expiration = cache.expireInactive();
|
||||
assert.deepEqual(
|
||||
overflow.removed.map((entry) => entry.view),
|
||||
expiration.removed.map((entry) => entry.view),
|
||||
["services"],
|
||||
);
|
||||
assert.deepEqual(
|
||||
overflow.entries.map((entry) => entry.view),
|
||||
["settings", "instances"],
|
||||
expiration.entries.map((entry) => entry.view),
|
||||
["settings"],
|
||||
);
|
||||
|
||||
const recreated = cache.activate("services");
|
||||
assert.notEqual(recreated.activated.instance, servicesInstance);
|
||||
});
|
||||
|
||||
test("revisiting a hidden page before its TTL preserves and resets it", () => {
|
||||
let now = 0;
|
||||
const cache = new FrameCache("services", {
|
||||
inactiveTtlMs: 1_000,
|
||||
shouldKeepAlive: () => true,
|
||||
now: () => now,
|
||||
});
|
||||
const services = cache.snapshot()[0];
|
||||
|
||||
cache.activate("settings");
|
||||
now = 999;
|
||||
const revisited = cache.activate("services");
|
||||
assert.equal(revisited.activated, services);
|
||||
|
||||
now = 1_500;
|
||||
assert.deepEqual(cache.expireInactive().removed, []);
|
||||
assert.equal(cache.timeUntilExpiration(), 499);
|
||||
|
||||
now = 1_999;
|
||||
const expiration = cache.expireInactive();
|
||||
assert.deepEqual(
|
||||
recreated.removed.map((entry) => entry.view),
|
||||
expiration.removed.map((entry) => entry.view),
|
||||
["settings"],
|
||||
);
|
||||
});
|
||||
|
||||
test("rejects invalid keep-alive limits", () => {
|
||||
assert.throws(
|
||||
() =>
|
||||
new FrameCache("overview", {
|
||||
maxKeepAliveEntries: -1,
|
||||
shouldKeepAlive: () => true,
|
||||
}),
|
||||
RangeError,
|
||||
);
|
||||
test("rejects invalid inactive TTL values", () => {
|
||||
for (const inactiveTtlMs of [0, -1, Number.POSITIVE_INFINITY, Number.NaN]) {
|
||||
assert.throws(
|
||||
() =>
|
||||
new FrameCache("overview", {
|
||||
inactiveTtlMs,
|
||||
shouldKeepAlive: () => true,
|
||||
}),
|
||||
RangeError,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user