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
8 changes: 5 additions & 3 deletions apps/console/src/mocks/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import { ObjectKernel } from '@objectstack/runtime';
import { InMemoryDriver } from '@objectstack/driver-memory';
import { setupWorker } from 'msw/browser';
import appConfig from '../../objectstack.shared';
import appConfig, { sharedConfig } from '../../objectstack.shared';
import { createKernel } from './createKernel';
import { createHandlers } from './handlers';

Expand Down Expand Up @@ -42,8 +42,10 @@ export async function startMockServer() {
// Create MSW handlers that match the response format of HonoServerPlugin
// Include both /api/v1 and legacy /api paths so the ObjectStackClient can
// reach the mock server regardless of which base URL it probes.
const v1Handlers = createHandlers('/api/v1', kernel, driver);
const legacyHandlers = createHandlers('/api', kernel, driver);
// Pass sharedConfig (pre-defineStack) so handlers can enrich object metadata
// with listViews that defineStack's Zod parse strips.
const v1Handlers = createHandlers('/api/v1', kernel, driver, sharedConfig);
const legacyHandlers = createHandlers('/api', kernel, driver, sharedConfig);
const handlers = [...v1Handlers, ...legacyHandlers];

// Start MSW service worker
Expand Down
49 changes: 43 additions & 6 deletions apps/console/src/mocks/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,40 @@ import { ObjectKernel } from '@objectstack/runtime';
import { InMemoryDriver } from '@objectstack/driver-memory';
import { http, HttpResponse } from 'msw';

/** Minimal shape of the stack config used to enrich protocol responses. */
interface StackConfig {
objects?: Array<{ name: string; listViews?: Record<string, unknown> }>;
}

/**
* Create MSW request handlers for a given base URL.
*
* @param baseUrl - URL prefix, e.g. '/api/v1' (browser) or 'http://localhost:3000/api/v1' (node)
* @param kernel - Bootstrapped ObjectKernel with protocol service
* @param driver - InMemoryDriver for direct data access
* @param baseUrl - URL prefix, e.g. '/api/v1' (browser) or 'http://localhost:3000/api/v1' (node)
* @param kernel - Bootstrapped ObjectKernel with protocol service
* @param driver - InMemoryDriver for direct data access
* @param appConfig - Original stack config (used to enrich protocol responses with listViews)
*/
export function createHandlers(baseUrl: string, kernel: ObjectKernel, driver: InMemoryDriver) {
export function createHandlers(baseUrl: string, kernel: ObjectKernel, driver: InMemoryDriver, appConfig?: StackConfig) {
const protocol = kernel.getService('protocol') as any;

// Build a lookup of listViews by object name from the original config.
// The runtime protocol strips listViews from object metadata, so we
// re-attach them here to ensure the console can resolve named views.
const listViewsByObject: Record<string, Record<string, unknown>> = {};
if (appConfig?.objects) {
for (const obj of appConfig.objects) {
if (obj.listViews && Object.keys(obj.listViews).length > 0) {
listViewsByObject[obj.name] = obj.listViews;
}
}
}

/** Merge listViews from config into a single object metadata item. */
function enrichObject(obj: any): any {
const views = listViewsByObject[obj.name];
return views ? { ...obj, listViews: { ...(obj.listViews || {}), ...views } } : obj;
}

// Determine whether we're in a browser (relative paths, wildcard prefix)
// or in Node.js tests (absolute URLs)
const isBrowser = !baseUrl.startsWith('http');
Expand Down Expand Up @@ -56,11 +80,18 @@ export function createHandlers(baseUrl: string, kernel: ObjectKernel, driver: In
http.get(`${prefix}${baseUrl}/meta/:type`, async ({ params }) => {
const metadataType = params.type as string;
const response = await protocol.getMetaItems({ type: metadataType });
// Enrich object metadata with listViews from stack config
if ((metadataType === 'object' || metadataType === 'objects') && response?.items) {
response.items = response.items.map(enrichObject);
}
return HttpResponse.json(response, { status: 200 });
}),
http.get(`${prefix}${baseUrl}/metadata/:type`, async ({ params }) => {
const metadataType = params.type as string;
const response = await protocol.getMetaItems({ type: metadataType });
if ((metadataType === 'object' || metadataType === 'objects') && response?.items) {
response.items = response.items.map(enrichObject);
}
return HttpResponse.json(response, { status: 200 });
}),

Expand All @@ -71,7 +102,10 @@ export function createHandlers(baseUrl: string, kernel: ObjectKernel, driver: In
type: params.type as string,
name: params.name as string
});
const payload = (response && response.item) ? response.item : response;
let payload = (response && response.item) ? response.item : response;
if ((params.type === 'object' || params.type === 'objects') && payload?.name) {
payload = enrichObject(payload);
}
return HttpResponse.json(payload || { error: 'Not found' }, { status: payload ? 200 : 404 });
} catch (e) {
return HttpResponse.json({ error: String(e) }, { status: 500 });
Expand All @@ -83,7 +117,10 @@ export function createHandlers(baseUrl: string, kernel: ObjectKernel, driver: In
type: params.type as string,
name: params.name as string
});
const payload = (response && response.item) ? response.item : response;
let payload = (response && response.item) ? response.item : response;
if ((params.type === 'object' || params.type === 'objects') && payload?.name) {
payload = enrichObject(payload);
}
return HttpResponse.json(payload || { error: 'Not found' }, { status: payload ? 200 : 404 });
} catch (e) {
return HttpResponse.json({ error: String(e) }, { status: 500 });
Expand Down
8 changes: 5 additions & 3 deletions apps/console/src/mocks/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import { ObjectKernel } from '@objectstack/runtime';
import { InMemoryDriver } from '@objectstack/driver-memory';
import { setupServer } from 'msw/node';
import appConfig from '../../objectstack.shared';
import appConfig, { sharedConfig } from '../../objectstack.shared';
import { createKernel } from './createKernel';
import { createHandlers } from './handlers';

Expand All @@ -31,8 +31,10 @@ export async function startMockServer() {
driver = result.driver;

// Create MSW handlers for both paths to ensure compatibility with client defaults
const v1Handlers = createHandlers('http://localhost:3000/api/v1', kernel, driver);
const legacyHandlers = createHandlers('http://localhost:3000/api', kernel, driver);
// Pass sharedConfig (pre-defineStack) so handlers can enrich object metadata
// with listViews that defineStack's Zod parse strips.
const v1Handlers = createHandlers('http://localhost:3000/api/v1', kernel, driver, sharedConfig);
const legacyHandlers = createHandlers('http://localhost:3000/api', kernel, driver, sharedConfig);
const handlers = [...v1Handlers, ...legacyHandlers];

// Setup MSW server for Node.js environment
Expand Down