From 221494eec9b29b7c104509dbdb796a4bcfcdc527 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 15 Feb 2026 01:52:29 +0000
Subject: [PATCH 1/3] Initial plan
From 003aca61b43075f6a4e9f633bcc346c5dbcdf791 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 15 Feb 2026 02:15:41 +0000
Subject: [PATCH 2/3] Fix MSW metadata handlers to use dynamic :type routes
matching client URLs
The @objectstack/client sends metadata requests using singular type names
(e.g. GET /api/v1/meta/app, /meta/object) but the MSW handlers only
registered plural routes (/meta/apps, /meta/objects), causing all metadata
fetches to fail with 500 errors on the demo site.
Replace hardcoded plural-only handlers with dynamic /meta/:type and
/metadata/:type/:name routes that match both singular and plural forms.
Also fix RootRedirect to show an error message instead of an infinite
loading screen when no apps are available.
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
---
apps/console/src/App.tsx | 10 +++-
apps/console/src/mocks/handlers.ts | 86 +++++++-----------------------
2 files changed, 26 insertions(+), 70 deletions(-)
diff --git a/apps/console/src/App.tsx b/apps/console/src/App.tsx
index 3e7766a71..f107638e0 100644
--- a/apps/console/src/App.tsx
+++ b/apps/console/src/App.tsx
@@ -368,7 +368,7 @@ function findFirstRoute(items: any[]): string {
// Redirect root to default app
function RootRedirect() {
- const { apps, loading } = useMetadata();
+ const { apps, loading, error } = useMetadata();
const activeApps = apps.filter((a: any) => a.active !== false);
const defaultApp = activeApps.find((a: any) => a.isDefault === true) || activeApps[0];
@@ -376,7 +376,13 @@ function RootRedirect() {
if (defaultApp) {
return ;
}
- return ;
+ return (
+
+
+ {error ? 'Failed to Load Configuration' : 'No Apps Configured'}
+
+
+ );
}
export function App() {
diff --git a/apps/console/src/mocks/handlers.ts b/apps/console/src/mocks/handlers.ts
index d0c04698c..6afe54ae8 100644
--- a/apps/console/src/mocks/handlers.ts
+++ b/apps/console/src/mocks/handlers.ts
@@ -48,98 +48,48 @@ export function createHandlers(baseUrl: string, kernel: ObjectKernel, driver: In
return HttpResponse.json(response, { status: 200 });
}),
- // ── Metadata: list objects ───────────────────────────────────────────
- http.get(`${prefix}${baseUrl}/meta/objects`, async () => {
- const response = await protocol.getMetaItems({ type: 'object' });
+ // ── Metadata: list items by type ────────────────────────────────────
+ // The client sends GET /meta/ where is singular (e.g. app,
+ // object, dashboard, report, page). We also keep the legacy plural
+ // routes (/meta/apps, /meta/objects, …) for backward compatibility.
+ // A single dynamic handler covers both forms.
+ http.get(`${prefix}${baseUrl}/meta/:type`, async ({ params }) => {
+ const raw = params.type as string;
+ const response = await protocol.getMetaItems({ type: raw });
return HttpResponse.json(response, { status: 200 });
}),
- http.get(`${prefix}${baseUrl}/metadata/objects`, async () => {
- const response = await protocol.getMetaItems({ type: 'object' });
+ http.get(`${prefix}${baseUrl}/metadata/:type`, async ({ params }) => {
+ const raw = params.type as string;
+ const response = await protocol.getMetaItems({ type: raw });
return HttpResponse.json(response, { status: 200 });
}),
- // ── Metadata: single object (legacy /meta/objects/:name) ─────────────
- http.get(`${prefix}${baseUrl}/meta/objects/:objectName`, async ({ params }) => {
+ // ── Metadata: single item by type + name ─────────────────────────────
+ http.get(`${prefix}${baseUrl}/meta/:type/:name`, async ({ params }) => {
try {
const response = await protocol.getMetaItem({
- type: 'object',
- name: params.objectName as string
- });
- return HttpResponse.json(response || { error: 'Not found' }, { status: response ? 200 : 404 });
- } catch (e) {
- return HttpResponse.json({ error: String(e) }, { status: 500 });
- }
- }),
-
- // ── Metadata: single object (/meta/object/:name & /metadata/object/:name)
- http.get(`${prefix}${baseUrl}/meta/object/:objectName`, async ({ params }) => {
- try {
- const response = await protocol.getMetaItem({
- type: 'object',
- name: params.objectName as string
+ type: params.type as string,
+ name: params.name as string
});
const payload = (response && response.item) ? response.item : response;
return HttpResponse.json(payload || { error: 'Not found' }, { status: payload ? 200 : 404 });
} catch (e) {
- console.error('[MSW] error getting meta item', e);
return HttpResponse.json({ error: String(e) }, { status: 500 });
}
}),
-
- http.get(`${prefix}${baseUrl}/metadata/object/:objectName`, async ({ params }) => {
+ http.get(`${prefix}${baseUrl}/metadata/:type/:name`, async ({ params }) => {
try {
const response = await protocol.getMetaItem({
- type: 'object',
- name: params.objectName as string
+ type: params.type as string,
+ name: params.name as string
});
const payload = (response && response.item) ? response.item : response;
return HttpResponse.json(payload || { error: 'Not found' }, { status: payload ? 200 : 404 });
} catch (e) {
- console.error('[MSW] error getting meta item', e);
return HttpResponse.json({ error: String(e) }, { status: 500 });
}
}),
- // ── Metadata: apps ──────────────────────────────────────────────────
- http.get(`${prefix}${baseUrl}/meta/apps`, async () => {
- const response = await protocol.getMetaItems({ type: 'app' });
- return HttpResponse.json(response, { status: 200 });
- }),
- http.get(`${prefix}${baseUrl}/metadata/apps`, async () => {
- const response = await protocol.getMetaItems({ type: 'app' });
- return HttpResponse.json(response, { status: 200 });
- }),
-
- // ── Metadata: dashboards ────────────────────────────────────────────
- http.get(`${prefix}${baseUrl}/meta/dashboards`, async () => {
- const response = await protocol.getMetaItems({ type: 'dashboard' });
- return HttpResponse.json(response, { status: 200 });
- }),
- http.get(`${prefix}${baseUrl}/metadata/dashboards`, async () => {
- const response = await protocol.getMetaItems({ type: 'dashboard' });
- return HttpResponse.json(response, { status: 200 });
- }),
-
- // ── Metadata: reports ───────────────────────────────────────────────
- http.get(`${prefix}${baseUrl}/meta/reports`, async () => {
- const response = await protocol.getMetaItems({ type: 'report' });
- return HttpResponse.json(response, { status: 200 });
- }),
- http.get(`${prefix}${baseUrl}/metadata/reports`, async () => {
- const response = await protocol.getMetaItems({ type: 'report' });
- return HttpResponse.json(response, { status: 200 });
- }),
-
- // ── Metadata: pages ─────────────────────────────────────────────────
- http.get(`${prefix}${baseUrl}/meta/pages`, async () => {
- const response = await protocol.getMetaItems({ type: 'page' });
- return HttpResponse.json(response, { status: 200 });
- }),
- http.get(`${prefix}${baseUrl}/metadata/pages`, async () => {
- const response = await protocol.getMetaItems({ type: 'page' });
- return HttpResponse.json(response, { status: 200 });
- }),
-
// ── Data: find all ──────────────────────────────────────────────────
http.get(`${prefix}${baseUrl}/data/:objectName`, async ({ params, request }) => {
const url = new URL(request.url);
From 7628557ac1037728de91d300f4a624a7a110dcb7 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 15 Feb 2026 02:18:39 +0000
Subject: [PATCH 3/3] Address code review: improve variable naming and error
messages
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
---
apps/console/src/App.tsx | 5 ++++-
apps/console/src/mocks/handlers.ts | 8 ++++----
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/apps/console/src/App.tsx b/apps/console/src/App.tsx
index f107638e0..25468ddaf 100644
--- a/apps/console/src/App.tsx
+++ b/apps/console/src/App.tsx
@@ -1,7 +1,7 @@
import { BrowserRouter, Routes, Route, Navigate, useNavigate, useLocation, useSearchParams } from 'react-router-dom';
import { useState, useEffect, lazy, Suspense, useMemo, type ReactNode } from 'react';
import { ObjectForm } from '@object-ui/plugin-form';
-import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Empty, EmptyTitle } from '@object-ui/components';
+import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Empty, EmptyTitle, EmptyDescription } from '@object-ui/components';
import { toast } from 'sonner';
import { SchemaRendererProvider } from '@object-ui/react';
import type { ConnectionState } from './dataSource';
@@ -380,6 +380,9 @@ function RootRedirect() {
{error ? 'Failed to Load Configuration' : 'No Apps Configured'}
+
+ {error ? error.message : 'No applications have been registered. Check your ObjectStack configuration.'}
+
);
diff --git a/apps/console/src/mocks/handlers.ts b/apps/console/src/mocks/handlers.ts
index 6afe54ae8..88d11c2f5 100644
--- a/apps/console/src/mocks/handlers.ts
+++ b/apps/console/src/mocks/handlers.ts
@@ -54,13 +54,13 @@ export function createHandlers(baseUrl: string, kernel: ObjectKernel, driver: In
// routes (/meta/apps, /meta/objects, …) for backward compatibility.
// A single dynamic handler covers both forms.
http.get(`${prefix}${baseUrl}/meta/:type`, async ({ params }) => {
- const raw = params.type as string;
- const response = await protocol.getMetaItems({ type: raw });
+ const metadataType = params.type as string;
+ const response = await protocol.getMetaItems({ type: metadataType });
return HttpResponse.json(response, { status: 200 });
}),
http.get(`${prefix}${baseUrl}/metadata/:type`, async ({ params }) => {
- const raw = params.type as string;
- const response = await protocol.getMetaItems({ type: raw });
+ const metadataType = params.type as string;
+ const response = await protocol.getMetaItems({ type: metadataType });
return HttpResponse.json(response, { status: 200 });
}),