From da11157729f8241af800bb78c34d9dade183336b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 16 Mar 2026 14:43:15 +0100 Subject: [PATCH] frontend-app-api: move session reuse to prepare time Remove the late-bound finalize session override so prepared apps accept reusable session state in one place. This keeps bootstrap and finalization semantics aligned and simplifies the prepared app API. Signed-off-by: Patrik Oldsberg Made-with: Cursor --- .../src/wiring/createSpecializedApp.test.tsx | 26 ------------------- .../src/wiring/prepareSpecializedApp.tsx | 24 ++++++++--------- 2 files changed, 11 insertions(+), 39 deletions(-) diff --git a/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx b/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx index 3d5cedbf30..cd1356d4cb 100644 --- a/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx +++ b/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx @@ -941,32 +941,6 @@ describe('createSpecializedApp', () => { ); }); - it('should reject finalize after selecting onFinalized', () => { - const preparedApp = prepareSpecializedApp({ - features: [makeAppPlugin()], - }); - - const unsubscribe = preparedApp.onFinalized(() => {}); - - expect(() => preparedApp.finalize()).toThrow( - 'prepareSpecializedApp only supports using either onFinalized() or finalize(), not both', - ); - - unsubscribe(); - }); - - it('should reject onFinalized after selecting finalize', () => { - const preparedApp = prepareSpecializedApp({ - features: [makeAppPlugin()], - }); - - preparedApp.finalize(); - - expect(() => preparedApp.onFinalized(() => {})).toThrow( - 'prepareSpecializedApp only supports using either onFinalized() or finalize(), not both', - ); - }); - it('should synchronously finalize feature flag predicates without sign-in', async () => { const featureFlagsApi = { isActive: jest.fn((name: string) => name === 'test-flag'), diff --git a/packages/frontend-app-api/src/wiring/prepareSpecializedApp.tsx b/packages/frontend-app-api/src/wiring/prepareSpecializedApp.tsx index 546ca4200c..13b5c8e8f0 100644 --- a/packages/frontend-app-api/src/wiring/prepareSpecializedApp.tsx +++ b/packages/frontend-app-api/src/wiring/prepareSpecializedApp.tsx @@ -246,9 +246,7 @@ export type PrepareSpecializedAppOptions = { export type PreparedSpecializedApp = { getBootstrapApp(): BootstrapSpecializedApp; onFinalized(callback: (app: FinalizedSpecializedApp) => void): () => void; - finalize(options?: { - sessionState?: SpecializedAppSessionState; - }): FinalizedSpecializedApp; + finalize(): FinalizedSpecializedApp; }; // Internal options type, not exported in the public API @@ -591,25 +589,25 @@ export function prepareSpecializedApp( subscribed = false; }; }, - finalize(finalizeOptions?: { sessionState?: SpecializedAppSessionState }) { + finalize() { finalization.selectMode('finalize'); if (finalized) { return finalized; } - if (!finalizeOptions?.sessionState) { + if (!providedSessionState) { // finalize() still depends on bootstrap classification and sign-in - // discovery, so we make sure the bootstrap tree has been prepared first. + // discovery unless a reusable session was supplied up front, so we make + // sure the bootstrap tree has been prepared first. getBootstrapApp(); } // Direct finalization never waits for async session preparation. Callers - // must either supply sessionState or invoke finalize() only when the - // predicate context is already available synchronously. - const finalizedSessionState = - finalizeOptions?.sessionState ?? - (signInRuntime?.requiresSignIn - ? undefined - : getSynchronousSessionState()); + // must either provide sessionState during prepareSpecializedApp() or + // invoke finalize() only when the predicate context is already available + // synchronously. + const finalizedSessionState = signInRuntime?.requiresSignIn + ? undefined + : getSynchronousSessionState(); if (!finalizedSessionState) { if (signInRuntime?.requiresSignIn) { throw new Error(