frontend-app-api: stop resetting the tree for api discovery

Collect bootstrap and deferred API factories without mutating app node instances, so conditional API roots stay deferred and visible API instances can survive finalization unchanged.

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
This commit is contained in:
Patrik Oldsberg
2026-03-15 19:38:35 +01:00
parent 268f8f8d9f
commit f919c714fc
@@ -1366,6 +1366,75 @@ describe('createSpecializedApp', () => {
).resolves.toBeInTheDocument();
});
it('should defer conditional api roots without resetting visible api instances', () => {
const featureFlagsApi = {
isActive: jest.fn((name: string) => name === 'test-flag'),
registerFlag: jest.fn(),
getRegisteredFlags: () => [],
save: jest.fn(),
} as unknown as typeof featureFlagsApiRef.T;
const visibleApiExtension = ApiBlueprint.make({
name: 'visible-api',
params: defineParams =>
defineParams({
api: createApiRef<{ value: string }>({ id: 'test.visible-api' }),
deps: {},
factory: () => ({ value: 'visible' }),
}),
});
const deferredApiExtension = ApiBlueprint.make({
name: 'deferred-api',
params: defineParams =>
defineParams({
api: createApiRef<{ value: string }>({ id: 'test.deferred-api' }),
deps: {},
factory: () => ({ value: 'deferred' }),
}),
}).override({
if: { featureFlags: { $contains: 'test-flag' } },
});
const preparedApp = prepareSpecializedApp({
features: [
makeAppPlugin(),
createFrontendPlugin({
pluginId: 'test',
featureFlags: [{ name: 'test-flag' }],
extensions: [
visibleApiExtension,
deferredApiExtension,
ApiBlueprint.make({
name: 'feature-flags',
params: defineParams =>
defineParams({
api: featureFlagsApiRef,
deps: {},
factory: () => featureFlagsApi,
}),
}),
],
}),
],
});
const bootstrapTree = preparedApp.getBootstrapApp().tree;
const apiNodes = bootstrapTree.root.edges.attachments.get('apis') ?? [];
const visibleApiNode = apiNodes.find(
node => node.spec.id === 'api:test/visible-api',
);
const deferredApiNode = apiNodes.find(
node => node.spec.id === 'api:test/deferred-api',
);
expect(visibleApiNode?.instance).toBeDefined();
expect(deferredApiNode?.instance).toBeUndefined();
const visibleApiInstance = visibleApiNode?.instance;
preparedApp.finalize();
expect(visibleApiNode?.instance).toBe(visibleApiInstance);
expect(deferredApiNode?.instance).toBeDefined();
});
it('should defer app root children until finalize', async () => {
const identityApi = {
getProfileInfo: async () => ({ displayName: 'Test User' }),