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 <poldsberg@gmail.com>
Made-with: Cursor
This commit is contained in:
Patrik Oldsberg
2026-03-16 14:43:15 +01:00
parent e04955b861
commit da11157729
2 changed files with 11 additions and 39 deletions
@@ -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'),
@@ -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(