Skip to content

Commit 9b4212a

Browse files
test: add canonical QueryOptionsV2 and offset() alias tests, update ROADMAP.md
Agent-Logs-Url: https://github.com/objectstack-ai/spec/sessions/d243fae5-51a7-4440-926f-46f040744cb0 Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com>
1 parent 111bbc0 commit 9b4212a

2 files changed

Lines changed: 72 additions & 1 deletion

File tree

ROADMAP.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ObjectStack Protocol — Road Map
22

3-
> **Last Updated:** 2026-02-28
3+
> **Last Updated:** 2026-03-27
44
> **Current Version:** v3.0.11
55
> **Status:** Protocol Specification Complete · Runtime Implementation In Progress
66
@@ -129,6 +129,7 @@ This strategy ensures rapid iteration while maintaining a clear path to producti
129129
| Dispatcher async `getService` bug fix || All `getService`/`getObjectQLService` calls in `http-dispatcher.ts` now properly `await` async service factories. Covers `handleAnalytics`, `handleAuth`, `handleStorage`, `handleAutomation`, `handleMetadata`, `handleUi`, `handlePackages`. All 7 framework adapters (Express, Fastify, Hono, Next.js, SvelteKit, NestJS, Nuxt) updated to use `getServiceAsync()` for auth service resolution. |
130130
| Analytics `getMetadata``getMeta` naming fix || `handleAnalytics` in `http-dispatcher.ts` called `getMetadata({ request })` which didn't match the `IAnalyticsService` contract (`getMeta(cubeName?: string)`). Renamed to `getMeta()` and aligned call signature. Updated test mocks accordingly. |
131131
| Unified ID/audit/tenant field naming || Eliminated `_id`/`modified_at`/`modified_by`/`space_id` from protocol layer. All protocol code uses `id`, `updated_at`, `updated_by`, `tenant_id` per `SystemFieldName`. Storage-layer (NoSQL driver internals) retains `_id` for MongoDB/Mingo compat. |
132+
| **Query syntax canonical unification** || All layers (Client SDK, React Hooks, Studio QueryBuilder, HTTP Dispatcher, docs) unified to Spec canonical field names (`where`/`fields`/`orderBy`/`limit`/`offset`/`expand`). `QueryOptionsV2` interface added. Legacy names (`filter`/`select`/`sort`/`top`/`skip`) accepted with `@deprecated` markers. HTTP Dispatcher normalizes transport params to canonical QueryAST before broker dispatch. |
132133

133134
---
134135

packages/client/src/client.test.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,3 +827,73 @@ describe('ObjectStackClient.automation', () => {
827827
expect(client.capabilities!.search).toBe(true);
828828
});
829829
});
830+
831+
// ==========================================
832+
// QueryOptionsV2 (Canonical Query Syntax) Tests
833+
// ==========================================
834+
835+
describe('QueryOptionsV2 — canonical find()', () => {
836+
it('should accept canonical field names (where, fields, orderBy, limit, offset)', async () => {
837+
const { client, fetchMock } = createMockClient({
838+
success: true,
839+
data: { object: 'account', records: [], total: 0 }
840+
});
841+
842+
await client.data.find('account', {
843+
where: { status: 'active' },
844+
fields: ['name', 'email'],
845+
orderBy: ['-created_at'],
846+
limit: 10,
847+
offset: 5,
848+
});
849+
850+
const url = fetchMock.mock.calls[0][0] as string;
851+
// V2 canonical options are normalized to HTTP transport params
852+
expect(url).toContain('top=10');
853+
expect(url).toContain('skip=5');
854+
expect(url).toContain('select=name%2Cemail');
855+
expect(url).toContain('sort=-created_at');
856+
// where → filter as JSON
857+
expect(url).toContain('status=active');
858+
});
859+
860+
it('should still accept legacy field names (filter, select, sort, top, skip)', async () => {
861+
const { client, fetchMock } = createMockClient({
862+
success: true,
863+
data: { object: 'account', records: [], total: 0 }
864+
});
865+
866+
await client.data.find('account', {
867+
filter: { industry: 'Tech' },
868+
select: ['name'],
869+
sort: ['-revenue'],
870+
top: 20,
871+
skip: 0,
872+
});
873+
874+
const url = fetchMock.mock.calls[0][0] as string;
875+
expect(url).toContain('top=20');
876+
expect(url).toContain('select=name');
877+
expect(url).toContain('sort=-revenue');
878+
expect(url).toContain('industry=Tech');
879+
});
880+
});
881+
882+
describe('QueryBuilder — offset() alias', () => {
883+
it('should set offset via .offset() method', () => {
884+
const q = createQuery('task')
885+
.limit(10)
886+
.offset(20)
887+
.build();
888+
expect(q.limit).toBe(10);
889+
expect(q.offset).toBe(20);
890+
});
891+
892+
it('should set offset via deprecated .skip() method', () => {
893+
const q = createQuery('task')
894+
.limit(10)
895+
.skip(30)
896+
.build();
897+
expect(q.offset).toBe(30);
898+
});
899+
});

0 commit comments

Comments
 (0)