Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions apps/console/objectstack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Expand All @@ -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';
Expand Down Expand Up @@ -70,15 +76,23 @@ 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(),
new ObjectQLPlugin(),
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(),
];
Expand Down
5 changes: 3 additions & 2 deletions apps/console/src/__tests__/i18n-translations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down
5 changes: 3 additions & 2 deletions apps/console/src/mocks/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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',
Expand Down
8 changes: 8 additions & 0 deletions apps/console/src/mocks/createKernel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -317,7 +318,14 @@ export async function createKernel(options: KernelOptions): Promise<KernelResult
for (const config of configs) {
await kernel.use(new AppPlugin(config));
}
// SetupPlugin MUST load before AuthPlugin so that the setupNav service
// is registered and available when AuthPlugin.init() tries to contribute menu items.
await kernel.use(new SetupPlugin() as unknown as Plugin);
// AuthPlugin contributes to setupNav during init, so it must come AFTER SetupPlugin.
await kernel.use(new AuthPlugin({
secret: 'objectui-demo-secret',
baseUrl: 'http://localhost:5173', // Vite dev server default
}) as unknown as Plugin);

// Register MemoryAnalyticsService so that HttpDispatcher can serve
// /api/v1/analytics/* endpoints in demo/MSW/dev environments.
Expand Down
5 changes: 3 additions & 2 deletions apps/console/src/mocks/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { ObjectKernel } from '@objectstack/runtime';
import { InMemoryDriver } from '@objectstack/driver-memory';
import { setupServer } from 'msw/node';
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';

Expand All @@ -34,7 +34,8 @@ export async function startMockServer() {
console.log('[MSW] Starting ObjectStack Runtime (Test Mode)...');

const result = await createKernel({
appConfigs: [...appConfigs, setupAppConfig, customReportsConfig],
// SetupPlugin is registered in createKernel, so no need for setupAppConfig here
appConfigs: [...appConfigs, customReportsConfig],
persistence: false,
mswOptions: {
enableBrowser: false,
Expand Down
10 changes: 4 additions & 6 deletions objectstack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,14 @@ export default {
new AppPlugin(prepareConfig(crmConfig)),
new AppPlugin(prepareConfig(todoConfig)),
new AppPlugin(prepareConfig(kitchenSinkConfig)),
// AuthPlugin before SetupPlugin: both use namespace 'sys', and the
// ObjectQL registry requires the package that owns objects (AuthPlugin →
// com.objectstack.system) to register first.
// SetupPlugin MUST load before AuthPlugin so that the setupNav service
// is registered and available when AuthPlugin.init() tries to contribute menu items.
new SetupPlugin(),
// AuthPlugin contributes to setupNav during init, so it must come AFTER SetupPlugin.
new AuthPlugin({
secret: process.env.AUTH_SECRET || 'objectui-dev-secret',
baseUrl: 'http://localhost:3000',
}),
// SetupPlugin registers setupNav during init and the merged Setup app
// during start. Must come after AuthPlugin to avoid sys namespace collision.
new SetupPlugin(),
new HonoServerPlugin({ port: 3000 }),
new ConsolePlugin(),
],
Expand Down
Loading