From f94161463cc82cc7bae45c3fbfdbe0c5f247204a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 06:37:23 +0000 Subject: [PATCH 1/3] Initial plan From 77ffd05fff83bf61bc1735077694d7be3f88fda0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 06:59:50 +0000 Subject: [PATCH 2/3] fix: resolve all CI build and test errors - Fix TS2345 errors in objectql/protocol.ts: cast 'name' to 'any' in SchemaRegistry.registerItem calls (lines 208, 222, 257, 269) - Fix loadMetaFromDb to use registerObject for object types instead of registerItem (fixes getItem/registerItem type mismatch for 'object') - Fix discovery endpoint in hono, sveltekit, nuxt, nextjs, fastify adapters: route discovery to prefix root instead of /discovery subpath - Fix client feed namespace to use 'data' route instead of 'feed' route (feed is a sub-resource of data: /api/data/{object}/{recordId}/feed) Agent-Logs-Url: https://github.com/objectstack-ai/spec/sessions/7eed517d-0606-4b0b-b9f5-3aa8e3ac5fe8 Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com> --- packages/adapters/fastify/src/index.ts | 4 ++-- packages/adapters/hono/src/index.ts | 4 ++-- packages/adapters/nextjs/src/index.ts | 4 ++-- packages/adapters/nuxt/src/index.ts | 4 ++-- packages/adapters/sveltekit/src/index.ts | 2 +- packages/client/src/index.ts | 28 ++++++++++++------------ packages/objectql/src/protocol.ts | 14 +++++++----- 7 files changed, 32 insertions(+), 28 deletions(-) diff --git a/packages/adapters/fastify/src/index.ts b/packages/adapters/fastify/src/index.ts index 0917697150..3c1a6d4be2 100644 --- a/packages/adapters/fastify/src/index.ts +++ b/packages/adapters/fastify/src/index.ts @@ -76,13 +76,13 @@ export async function objectStackPlugin(fastify: FastifyInstance, options: Fasti // ─── Explicit routes (framework-specific handling required) ──────────────── // --- Discovery --- - fastify.get(`${prefix}/discovery`, async (_request: FastifyRequest, reply: FastifyReply) => { + fastify.get(prefix, async (_request: FastifyRequest, reply: FastifyReply) => { return reply.send({ data: await dispatcher.getDiscoveryInfo(prefix) }); }); // --- .well-known --- fastify.get('/.well-known/objectstack', async (_request: FastifyRequest, reply: FastifyReply) => { - return reply.redirect(`${prefix}/discovery`); + return reply.redirect(prefix); }); // --- Auth (needs auth service integration) --- diff --git a/packages/adapters/hono/src/index.ts b/packages/adapters/hono/src/index.ts index 970db2f8a6..cbe633b30c 100644 --- a/packages/adapters/hono/src/index.ts +++ b/packages/adapters/hono/src/index.ts @@ -81,13 +81,13 @@ export function createHonoApp(options: ObjectStackHonoOptions): Hono { // ─── Explicit routes (framework-specific handling required) ──────────────── // --- Discovery --- - app.get(`${prefix}/discovery`, async (c) => { + app.get(prefix, async (c) => { return c.json({ data: await dispatcher.getDiscoveryInfo(prefix) }); }); // --- .well-known --- app.get('/.well-known/objectstack', (c) => { - return c.redirect(`${prefix}/discovery`); + return c.redirect(prefix); }); // --- Auth (needs auth service integration) --- diff --git a/packages/adapters/nextjs/src/index.ts b/packages/adapters/nextjs/src/index.ts index db87eeb6c1..9f7a6f6fd0 100644 --- a/packages/adapters/nextjs/src/index.ts +++ b/packages/adapters/nextjs/src/index.ts @@ -62,7 +62,7 @@ export function createRouteHandler(options: NextAdapterOptions) { const method = req.method; // --- 0. Discovery Endpoint --- - if (segments.length === 1 && segments[0] === 'discovery' && method === 'GET') { + if (segments.length === 0 && method === 'GET') { return NextResponse.json({ data: await dispatcher.getDiscoveryInfo(options.prefix || '/api') }); } @@ -152,7 +152,7 @@ export function createDiscoveryHandler(options: NextAdapterOptions) { return async function discoveryHandler(req: NextRequest) { const apiPath = options.prefix || '/api'; const url = new URL(req.url); - const targetUrl = new URL(`${apiPath}/discovery`, url.origin); + const targetUrl = new URL(apiPath, url.origin); return NextResponse.redirect(targetUrl); } } diff --git a/packages/adapters/nuxt/src/index.ts b/packages/adapters/nuxt/src/index.ts index 7b8492935a..108cea599d 100644 --- a/packages/adapters/nuxt/src/index.ts +++ b/packages/adapters/nuxt/src/index.ts @@ -89,7 +89,7 @@ export function createH3Router(options: NuxtAdapterOptions): Router { // --- Discovery --- router.get( - `${prefix}/discovery`, + prefix, defineEventHandler(async () => { return { data: await dispatcher.getDiscoveryInfo(prefix) }; }), @@ -99,7 +99,7 @@ export function createH3Router(options: NuxtAdapterOptions): Router { router.get( '/.well-known/objectstack', defineEventHandler((event) => { - return sendRedirect(event, `${prefix}/discovery`); + return sendRedirect(event, prefix); }), ); diff --git a/packages/adapters/sveltekit/src/index.ts b/packages/adapters/sveltekit/src/index.ts index e873adc2f1..578b871a78 100644 --- a/packages/adapters/sveltekit/src/index.ts +++ b/packages/adapters/sveltekit/src/index.ts @@ -99,7 +99,7 @@ export function createRequestHandler(options: SvelteKitAdapterOptions) { const segments = path.split('/').filter(Boolean); // --- Discovery --- - if (segments.length === 1 && segments[0] === 'discovery' && method === 'GET') { + if (segments.length === 0 && method === 'GET') { return new Response(JSON.stringify({ data: await dispatcher.getDiscoveryInfo(prefix) }), { status: 200, headers: { 'Content-Type': 'application/json' }, diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 842bc2259a..27c2c31758 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -1294,7 +1294,7 @@ export class ObjectStackClient { * List feed items for a record */ list: async (object: string, recordId: string, options?: { type?: string; limit?: number; cursor?: string }): Promise => { - const route = this.getRoute('feed'); + const route = this.getRoute('data'); const params = new URLSearchParams(); if (options?.type) params.set('type', options.type); if (options?.limit) params.set('limit', String(options.limit)); @@ -1308,7 +1308,7 @@ export class ObjectStackClient { * Create a new feed item (comment, note, task, etc.) */ create: async (object: string, recordId: string, data: { type: string; body?: string; mentions?: any[]; parentId?: string; visibility?: string }): Promise => { - const route = this.getRoute('feed'); + const route = this.getRoute('data'); const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed`, { method: 'POST', body: JSON.stringify(data) @@ -1320,7 +1320,7 @@ export class ObjectStackClient { * Update an existing feed item */ update: async (object: string, recordId: string, feedId: string, data: { body?: string; mentions?: any[]; visibility?: string }): Promise => { - const route = this.getRoute('feed'); + const route = this.getRoute('data'); const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}`, { method: 'PUT', body: JSON.stringify(data) @@ -1332,7 +1332,7 @@ export class ObjectStackClient { * Delete a feed item */ delete: async (object: string, recordId: string, feedId: string): Promise => { - const route = this.getRoute('feed'); + const route = this.getRoute('data'); const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}`, { method: 'DELETE' }); @@ -1343,7 +1343,7 @@ export class ObjectStackClient { * Add an emoji reaction to a feed item */ addReaction: async (object: string, recordId: string, feedId: string, emoji: string): Promise => { - const route = this.getRoute('feed'); + const route = this.getRoute('data'); const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/reactions`, { method: 'POST', body: JSON.stringify({ emoji }) @@ -1355,7 +1355,7 @@ export class ObjectStackClient { * Remove an emoji reaction from a feed item */ removeReaction: async (object: string, recordId: string, feedId: string, emoji: string): Promise => { - const route = this.getRoute('feed'); + const route = this.getRoute('data'); const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/reactions/${encodeURIComponent(emoji)}`, { method: 'DELETE' }); @@ -1366,7 +1366,7 @@ export class ObjectStackClient { * Pin a feed item to the top of the timeline */ pin: async (object: string, recordId: string, feedId: string): Promise => { - const route = this.getRoute('feed'); + const route = this.getRoute('data'); const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/pin`, { method: 'POST' }); @@ -1377,7 +1377,7 @@ export class ObjectStackClient { * Unpin a feed item */ unpin: async (object: string, recordId: string, feedId: string): Promise => { - const route = this.getRoute('feed'); + const route = this.getRoute('data'); const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/pin`, { method: 'DELETE' }); @@ -1388,7 +1388,7 @@ export class ObjectStackClient { * Star (bookmark) a feed item */ star: async (object: string, recordId: string, feedId: string): Promise => { - const route = this.getRoute('feed'); + const route = this.getRoute('data'); const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/star`, { method: 'POST' }); @@ -1399,7 +1399,7 @@ export class ObjectStackClient { * Unstar a feed item */ unstar: async (object: string, recordId: string, feedId: string): Promise => { - const route = this.getRoute('feed'); + const route = this.getRoute('data'); const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/star`, { method: 'DELETE' }); @@ -1410,7 +1410,7 @@ export class ObjectStackClient { * Search feed items */ search: async (object: string, recordId: string, query: string, options?: { type?: string; actorId?: string; dateFrom?: string; dateTo?: string; limit?: number; cursor?: string }): Promise => { - const route = this.getRoute('feed'); + const route = this.getRoute('data'); const params = new URLSearchParams(); params.set('query', query); if (options?.type) params.set('type', options.type); @@ -1427,7 +1427,7 @@ export class ObjectStackClient { * Get field-level changelog for a record */ getChangelog: async (object: string, recordId: string, options?: { field?: string; actorId?: string; dateFrom?: string; dateTo?: string; limit?: number; cursor?: string }): Promise => { - const route = this.getRoute('feed'); + const route = this.getRoute('data'); const params = new URLSearchParams(); if (options?.field) params.set('field', options.field); if (options?.actorId) params.set('actorId', options.actorId); @@ -1444,7 +1444,7 @@ export class ObjectStackClient { * Subscribe to record notifications */ subscribe: async (object: string, recordId: string, options?: { events?: string[]; channels?: string[] }): Promise => { - const route = this.getRoute('feed'); + const route = this.getRoute('data'); const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/subscribe`, { method: 'POST', body: JSON.stringify(options || {}) @@ -1456,7 +1456,7 @@ export class ObjectStackClient { * Unsubscribe from record notifications */ unsubscribe: async (object: string, recordId: string): Promise => { - const route = this.getRoute('feed'); + const route = this.getRoute('data'); const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/subscribe`, { method: 'DELETE' }); diff --git a/packages/objectql/src/protocol.ts b/packages/objectql/src/protocol.ts index 181e04bc86..dac7bd3901 100644 --- a/packages/objectql/src/protocol.ts +++ b/packages/objectql/src/protocol.ts @@ -205,7 +205,7 @@ export class ObjectStackProtocolImplementation implements ObjectStackProtocol { ? JSON.parse(record.metadata) : record.metadata; // Hydrate back into registry - SchemaRegistry.registerItem(request.type, data, 'name'); + SchemaRegistry.registerItem(request.type, data, 'name' as any); return data; }); } else { @@ -219,7 +219,7 @@ export class ObjectStackProtocolImplementation implements ObjectStackProtocol { const data = typeof record.metadata === 'string' ? JSON.parse(record.metadata) : record.metadata; - SchemaRegistry.registerItem(request.type, data, 'name'); + SchemaRegistry.registerItem(request.type, data, 'name' as any); return data; }); } @@ -254,7 +254,7 @@ export class ObjectStackProtocolImplementation implements ObjectStackProtocol { ? JSON.parse(record.metadata) : record.metadata; // Hydrate back into registry for next time - SchemaRegistry.registerItem(request.type, item, 'name'); + SchemaRegistry.registerItem(request.type, item, 'name' as any); } else { // Try alternate type name const alt = request.type.endsWith('s') ? request.type.slice(0, -1) : request.type + 's'; @@ -266,7 +266,7 @@ export class ObjectStackProtocolImplementation implements ObjectStackProtocol { ? JSON.parse(altRecord.metadata) : altRecord.metadata; // Hydrate back into registry for next time - SchemaRegistry.registerItem(request.type, item, 'name'); + SchemaRegistry.registerItem(request.type, item, 'name' as any); } } } catch { @@ -980,7 +980,11 @@ export class ObjectStackProtocolImplementation implements ObjectStackProtocol { const data = typeof record.metadata === 'string' ? JSON.parse(record.metadata) : record.metadata; - SchemaRegistry.registerItem(record.type, data, 'name'); + if (record.type === 'object') { + SchemaRegistry.registerObject(data as any, record.packageId || 'sys_metadata'); + } else { + SchemaRegistry.registerItem(record.type, data, 'name' as any); + } loaded++; } catch (e) { errors++; From 321cd281f16c48b16a9c4fcbe771d4d3450a5520 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 07:04:44 +0000 Subject: [PATCH 3/3] docs: update CHANGELOG.md with CI fix details Agent-Logs-Url: https://github.com/objectstack-ai/spec/sessions/7eed517d-0606-4b0b-b9f5-3aa8e3ac5fe8 Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com> --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f22affc895..3a5e00a850 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed +- **ObjectQL build failure** — Fixed TypeScript TS2345 errors in `packages/objectql/src/protocol.ts` + where `SchemaRegistry.registerItem()` calls failed type checking for the `keyField` parameter. + Applied `'name' as any` cast consistent with the established codebase pattern. +- **ObjectQL `loadMetaFromDb`** — Fixed metadata hydration for `object` type records to use + `SchemaRegistry.registerObject()` instead of `registerItem()`, resolving a mismatch where + objects registered via `registerItem` could not be retrieved via `getItem('object', ...)`. +- **Adapter discovery endpoints** — Fixed discovery route in Hono, SvelteKit, Nuxt, Next.js, + and Fastify adapters to serve discovery info at the API prefix root (e.g., `GET /api`) + instead of a `/discovery` subpath. Updated `.well-known/objectstack` redirects accordingly. +- **Client feed namespace routing** — Fixed `ObjectStackClient.feed` methods to use the `data` + route (`/api/v1/data/{object}/{recordId}/feed`) instead of a separate `/api/v1/feed` route, + matching the actual server-side routing where feed is a sub-resource of data. + ### Added - **`@objectstack/service-ai` — Unified AI capability service plugin** — New kernel plugin providing standardized AI service integration: