@@ -6,6 +6,51 @@ import { http, HttpResponse } from 'msw';
66import { ObjectStackClient } from '@objectstack/client' ;
77import studioConfig from '../../objectstack.config' ;
88
9+ /**
10+ * Parse query parameters from a request URL into an ObjectQL query options object.
11+ * Supports: top, skip, sort, select, filter (JSON), and individual key-value filters.
12+ */
13+ function parseQueryOptions ( url : URL ) : Record < string , any > {
14+ const query : Record < string , any > = { } ;
15+ const where : Record < string , any > = { } ;
16+
17+ url . searchParams . forEach ( ( val , key ) => {
18+ switch ( key ) {
19+ case 'top' :
20+ case '$top' :
21+ query . limit = parseInt ( val , 10 ) ;
22+ break ;
23+ case 'skip' :
24+ case '$skip' :
25+ query . offset = parseInt ( val , 10 ) ;
26+ break ;
27+ case 'sort' :
28+ case '$sort' : {
29+ // JSON array or comma-separated string
30+ try { query . orderBy = JSON . parse ( val ) ; } catch {
31+ query . orderBy = val . split ( ',' ) . map ( ( s : string ) => s . trim ( ) ) ;
32+ }
33+ break ;
34+ }
35+ case 'select' :
36+ case '$select' :
37+ query . fields = val . split ( ',' ) . map ( ( s : string ) => s . trim ( ) ) ;
38+ break ;
39+ case 'filter' :
40+ case '$filter' :
41+ try { Object . assign ( where , JSON . parse ( val ) ) ; } catch { /* ignore malformed */ }
42+ break ;
43+ default :
44+ // Individual key-value filter params (e.g. id=abc)
45+ where [ key ] = val ;
46+ break ;
47+ }
48+ } ) ;
49+
50+ if ( Object . keys ( where ) . length > 0 ) query . where = where ;
51+ return query ;
52+ }
53+
954/**
1055 * Creates a Realistic Browser Simulation
1156 *
@@ -28,6 +73,7 @@ export async function simulateBrowser() {
2873 // 2. Define Network Handlers (The "Virtual Router")
2974 // These map HTTP requests -> Kernel ObjectQL service actions
3075 const ql = ( kernel as any ) . context ?. getService ( 'objectql' ) ;
76+ const protocol = ( kernel as any ) . context ?. getService ( 'protocol' ) ;
3177 const handlers = [
3278 // Discovery
3379 http . get ( 'http://localhost:3000/.well-known/objectstack' , ( ) => {
@@ -47,17 +93,14 @@ export async function simulateBrowser() {
4793 } ) ;
4894 } ) ,
4995
50- // Query / Find
96+ // Query / Find — parse query params for filtering, pagination, sorting, field selection
5197 http . get ( 'http://localhost:3000/api/v1/data/:object' , async ( { params, request } ) => {
5298 const url = new URL ( request . url ) ;
53- const filters = { } ;
54- url . searchParams . forEach ( ( val , key ) => {
55- ( filters as any ) [ key ] = val ;
56- } ) ;
57- console . log ( `[VirtualNetwork] GET /data/${ params . object } ` , filters ) ;
99+ const queryOpts = parseQueryOptions ( url ) ;
100+ console . log ( `[VirtualNetwork] GET /data/${ params . object } ` , queryOpts ) ;
58101
59102 try {
60- let all = await ql . find ( params . object ) ;
103+ let all = await ql . find ( params . object , queryOpts ) ;
61104 if ( ! Array . isArray ( all ) && all && ( all as any ) . value ) all = ( all as any ) . value ;
62105 if ( ! all ) all = [ ] ;
63106 const result = { object : params . object , records : all , total : all . length } ;
@@ -122,12 +165,12 @@ export async function simulateBrowser() {
122165 }
123166 } ) ,
124167
125- // Metadata - Get all types (base route returns types)
168+ // Metadata - Get all types (merges SchemaRegistry + MetadataService types)
126169 http . get ( 'http://localhost:3000/api/v1/meta' , async ( ) => {
127170 console . log ( '[VirtualNetwork] GET /meta (types)' ) ;
128171 try {
129- const types = ql ?. registry ?. getRegisteredTypes ?. ( ) || [ ] ;
130- return HttpResponse . json ( { success : true , data : { types } } ) ;
172+ const result = await protocol ?. getMetaTypes ?. ( ) ;
173+ return HttpResponse . json ( { success : true , data : result || { types : [ ] } } ) ;
131174 } catch ( err : any ) {
132175 return HttpResponse . json ( { error : err . message } , { status : 500 } ) ;
133176 }
@@ -137,43 +180,44 @@ export async function simulateBrowser() {
137180 http . get ( 'http://localhost:3000/api/v1/meta/object' , async ( ) => {
138181 console . log ( '[VirtualNetwork] GET /meta/object' ) ;
139182 try {
140- const objects = ql ?. getObjects ?.( ) || { } ;
141- return HttpResponse . json ( { success : true , data : objects } ) ;
183+ const result = await protocol ?. getMetaItems ?.( { type : 'object' } ) ;
184+ return HttpResponse . json ( { success : true , data : result || { type : 'object' , items : [ ] } } ) ;
142185 } catch ( err : any ) {
143186 return HttpResponse . json ( { error : err . message } , { status : 500 } ) ;
144187 }
145188 } ) ,
146189 http . get ( 'http://localhost:3000/api/v1/meta/objects' , async ( ) => {
147190 console . log ( '[VirtualNetwork] GET /meta/objects' ) ;
148191 try {
149- const objects = ql ?. getObjects ?.( ) || { } ;
150- return HttpResponse . json ( { success : true , data : objects } ) ;
192+ const result = await protocol ?. getMetaItems ?.( { type : 'object' } ) ;
193+ return HttpResponse . json ( { success : true , data : result || { type : 'object' , items : [ ] } } ) ;
151194 } catch ( err : any ) {
152195 return HttpResponse . json ( { error : err . message } , { status : 500 } ) ;
153196 }
154197 } ) ,
155198
156199 // Metadata - Object Detail (Singular & Plural support)
200+ // Returns the raw ServiceObject directly (with name, fields, etc.)
157201 http . get ( 'http://localhost:3000/api/v1/meta/object/:name' , async ( { params } ) => {
158202 console . log ( `[VirtualNetwork] GET /meta/object/${ params . name } ` ) ;
159203 try {
160- const result = ql ?. registry ?. getObject ?. ( params . name ) ;
161- if ( ! result ) {
204+ const result = await protocol ?. getMetaItem ?. ( { type : 'object' , name : params . name as string } ) ;
205+ if ( ! result ?. item ) {
162206 return HttpResponse . json ( { error : 'Not Found' } , { status : 404 } ) ;
163207 }
164- return HttpResponse . json ( { success : true , data : result } ) ;
208+ return HttpResponse . json ( { success : true , data : result . item } ) ;
165209 } catch ( err : any ) {
166210 return HttpResponse . json ( { error : err . message } , { status : 500 } ) ;
167211 }
168212 } ) ,
169213 http . get ( 'http://localhost:3000/api/v1/meta/objects/:name' , async ( { params } ) => {
170214 console . log ( `[VirtualNetwork] GET /meta/objects/${ params . name } ` ) ;
171215 try {
172- const result = ql ?. registry ?. getObject ?. ( params . name ) ;
173- if ( ! result ) {
216+ const result = await protocol ?. getMetaItem ?. ( { type : 'object' , name : params . name as string } ) ;
217+ if ( ! result ?. item ) {
174218 return HttpResponse . json ( { error : 'Not Found' } , { status : 404 } ) ;
175219 }
176- return HttpResponse . json ( { success : true , data : result } ) ;
220+ return HttpResponse . json ( { success : true , data : result . item } ) ;
177221 } catch ( err : any ) {
178222 return HttpResponse . json ( { error : err . message } , { status : 500 } ) ;
179223 }
@@ -186,8 +230,8 @@ export async function simulateBrowser() {
186230 }
187231 console . log ( `[VirtualNetwork] GET /meta/${ params . type } ` ) ;
188232 try {
189- const items = ql ?. registry ?. listItems ?. ( params . type ) || [ ] ;
190- return HttpResponse . json ( { success : true , data : items } ) ;
233+ const result = await protocol ?. getMetaItems ?. ( { type : params . type as string } ) ;
234+ return HttpResponse . json ( { success : true , data : result || { type : params . type , items : [ ] } } ) ;
191235 } catch ( err : any ) {
192236 return HttpResponse . json ( { error : err . message } , { status : 500 } ) ;
193237 }
0 commit comments