diff --git a/apps/console/objectstack.config.ts b/apps/console/objectstack.config.ts index fcd3c82a7..ce779e17c 100644 --- a/apps/console/objectstack.config.ts +++ b/apps/console/objectstack.config.ts @@ -4,7 +4,7 @@ const require = createRequire(import.meta.url); // @ts-ignore globalThis.require = require; -import { sharedConfig, appConfigs, setupAppConfig } from './objectstack.shared'; +import { sharedConfig, appConfigs } from './objectstack.shared'; // @ts-ignore import * as MSWPluginPkg from '@objectstack/plugin-msw'; @@ -13,6 +13,10 @@ import * as ObjectQLPluginPkg from '@objectstack/objectql'; // @ts-ignore import * as HonoServerPluginPkg from '@objectstack/plugin-hono-server'; // @ts-ignore +import * as AuthPluginPkg from '@objectstack/plugin-auth'; +// @ts-ignore +import * as SetupPluginPkg from '@objectstack/plugin-setup'; +// @ts-ignore import * as DriverMemoryPkg from '@objectstack/driver-memory'; // @ts-ignore import * as RuntimePkg from '@objectstack/runtime'; @@ -25,6 +29,8 @@ const InMemoryDriver = DriverMemoryPkg.InMemoryDriver || (DriverMemoryPkg as any const DriverPlugin = RuntimePkg.DriverPlugin || (RuntimePkg as any).default?.DriverPlugin || (RuntimePkg as any).default; const AppPlugin = RuntimePkg.AppPlugin || (RuntimePkg as any).default?.AppPlugin || (RuntimePkg as any).default; const HonoServerPlugin = HonoServerPluginPkg.HonoServerPlugin || (HonoServerPluginPkg as any).default?.HonoServerPlugin || (HonoServerPluginPkg as any).default; +const AuthPlugin = AuthPluginPkg.AuthPlugin || (AuthPluginPkg as any).default?.AuthPlugin || (AuthPluginPkg as any).default; +const SetupPlugin = SetupPluginPkg.SetupPlugin || (SetupPluginPkg as any).default?.SetupPlugin || (SetupPluginPkg as any).default; const createMemoryI18n = CorePkg.createMemoryI18n || (CorePkg as any).default?.createMemoryI18n; import { ConsolePlugin } from './plugin'; @@ -70,6 +76,9 @@ class MemoryI18nPlugin { * * MemoryI18nPlugin MUST come before AppPlugin so that the i18n service * exists when AppPlugin.start() → loadTranslations() runs. + * + * SetupPlugin MUST load before AuthPlugin so that the setupNav service + * is registered and available when AuthPlugin.init() tries to contribute menu items. */ const plugins: any[] = [ new MemoryI18nPlugin(), @@ -77,8 +86,13 @@ const plugins: any[] = [ new DriverPlugin(new InMemoryDriver(), 'memory'), // Each example stack loaded as an independent AppPlugin ...appConfigs.map((config: any) => new AppPlugin(config)), - // Setup App registered via AppPlugin so ObjectQLPlugin discovers it - new AppPlugin(setupAppConfig), + // SetupPlugin must come before AuthPlugin (setupNav service dependency) + new SetupPlugin(), + // AuthPlugin contributes to setupNav during init, so it must come AFTER SetupPlugin + new AuthPlugin({ + secret: process.env.AUTH_SECRET || 'objectui-server-secret', + baseUrl: process.env.BASE_URL || 'http://localhost:3000', + }), new HonoServerPlugin({ port: 3000 }), new ConsolePlugin(), ]; diff --git a/apps/console/src/__tests__/i18n-translations.test.ts b/apps/console/src/__tests__/i18n-translations.test.ts index ab8ca8704..e0e91ae75 100644 --- a/apps/console/src/__tests__/i18n-translations.test.ts +++ b/apps/console/src/__tests__/i18n-translations.test.ts @@ -13,7 +13,7 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { setupServer } from 'msw/node'; import { createKernel, type KernelResult } from '../mocks/createKernel'; import { createAuthHandlers } from '../mocks/authHandlers'; -import { appConfigs, setupAppConfig } from '../../objectstack.shared'; +import { appConfigs } from '../../objectstack.shared'; import { crmLocales } from '@object-ui/example-crm'; // Expected values from the CRM i18n bundles — avoid hard-coding in assertions @@ -26,7 +26,8 @@ describe('i18n translations pipeline', () => { beforeAll(async () => { result = await createKernel({ - appConfigs: [...appConfigs, setupAppConfig], + // SetupPlugin is registered in createKernel, so no need for setupAppConfig here + appConfigs: [...appConfigs], persistence: false, mswOptions: { enableBrowser: false, diff --git a/apps/console/src/mocks/browser.ts b/apps/console/src/mocks/browser.ts index 44fb9f69e..31786fd3e 100644 --- a/apps/console/src/mocks/browser.ts +++ b/apps/console/src/mocks/browser.ts @@ -16,7 +16,7 @@ import { setupWorker } from 'msw/browser'; import { ObjectKernel } from '@objectstack/runtime'; import { InMemoryDriver } from '@objectstack/driver-memory'; import type { MSWPlugin } from '@objectstack/plugin-msw'; -import { appConfigs, setupAppConfig, customReportsConfig } from '../../objectstack.shared'; +import { appConfigs, customReportsConfig } from '../../objectstack.shared'; import { createKernel } from './createKernel'; import { createAuthHandlers } from './authHandlers'; @@ -43,7 +43,8 @@ export async function startMockServer() { if (import.meta.env.DEV) console.log('[MSW] Starting ObjectStack Runtime (Browser Mode)...'); const result = await createKernel({ - appConfigs: [...appConfigs, setupAppConfig, customReportsConfig], + // SetupPlugin is registered in createKernel, so no need for setupAppConfig here + appConfigs: [...appConfigs, customReportsConfig], mswOptions: { enableBrowser: false, baseUrl: '/api/v1', diff --git a/apps/console/src/mocks/createKernel.ts b/apps/console/src/mocks/createKernel.ts index 3a8062d0e..1fae23303 100644 --- a/apps/console/src/mocks/createKernel.ts +++ b/apps/console/src/mocks/createKernel.ts @@ -17,6 +17,7 @@ import { ObjectQLPlugin } from '@objectstack/objectql'; import { InMemoryDriver, MemoryAnalyticsService } from '@objectstack/driver-memory'; import { MSWPlugin } from '@objectstack/plugin-msw'; import type { MSWPluginOptions } from '@objectstack/plugin-msw'; +import { AuthPlugin } from '@objectstack/plugin-auth'; import { SetupPlugin } from '@objectstack/plugin-setup'; import type { Cube } from '@objectstack/spec/data'; import { http, HttpResponse } from 'msw'; @@ -317,7 +318,14 @@ export async function createKernel(options: KernelOptions): Promise