@@ -145,6 +145,37 @@ export interface QueryOptions {
145145 groupBy ?: string [ ] ;
146146}
147147
148+ /**
149+ * Canonical query options using Spec protocol field names.
150+ * This is the recommended interface for `data.find()` queries.
151+ *
152+ * Canonical field mapping (QueryAST-aligned):
153+ * - `where` — filter conditions (replaces legacy `filter`/`filters`)
154+ * - `fields` — field selection (replaces legacy `select`)
155+ * - `orderBy` — sort definition (replaces legacy `sort`)
156+ * - `limit` — max records (replaces legacy `top`)
157+ * - `offset` — skip records (replaces legacy `skip`)
158+ * - `expand` — relation loading (replaces legacy `populate`)
159+ */
160+ export interface QueryOptionsV2 {
161+ /** Filter conditions (WHERE clause). Accepts MongoDB-style $op object or FilterCondition AST. */
162+ where ?: Record < string , any > | unknown [ ] ;
163+ /** Fields to retrieve (SELECT clause). */
164+ fields ?: string [ ] ;
165+ /** Sort definition (ORDER BY clause). */
166+ orderBy ?: string | string [ ] | SortNode [ ] ;
167+ /** Maximum number of records to return (LIMIT). */
168+ limit ?: number ;
169+ /** Number of records to skip (OFFSET). */
170+ offset ?: number ;
171+ /** Relations to expand (JOIN / eager-load). */
172+ expand ?: Record < string , any > | string [ ] ;
173+ /** Aggregation functions. */
174+ aggregations ?: AggregationNode [ ] ;
175+ /** Group by fields. */
176+ groupBy ?: string [ ] ;
177+ }
178+
148179export interface PaginatedResult < T = any > {
149180 /** Spec-compliant: array of matching records */
150181 records : T [ ] ;
@@ -1445,34 +1476,52 @@ export class ObjectStackClient {
14451476 * @deprecated Use `data.query()` with standard QueryAST parameters instead.
14461477 * This method uses legacy parameter names. Internally adapts to HTTP GET params.
14471478 */
1448- find : async < T = any > ( object : string , options : QueryOptions = { } ) : Promise < PaginatedResult < T > > => {
1479+ find : async < T = any > ( object : string , options : QueryOptions | QueryOptionsV2 = { } ) : Promise < PaginatedResult < T > > => {
14491480 const route = this . getRoute ( 'data' ) ;
14501481 const queryParams = new URLSearchParams ( ) ;
1451-
1482+
1483+ // ── Normalize V2 canonical options → HTTP transport params ───
1484+ // Detect V2 options by presence of canonical-only keys.
1485+ const v2 = options as QueryOptionsV2 ;
1486+ const normalizedOptions : QueryOptions = { } as QueryOptions ;
1487+ if ( 'where' in options || 'fields' in options || 'orderBy' in options || 'offset' in options ) {
1488+ // V2 canonical options detected — map to legacy HTTP transport keys
1489+ if ( v2 . where ) normalizedOptions . filter = v2 . where as any ;
1490+ if ( v2 . fields ) normalizedOptions . select = v2 . fields ;
1491+ if ( v2 . orderBy ) normalizedOptions . sort = v2 . orderBy as any ;
1492+ if ( v2 . limit != null ) normalizedOptions . top = v2 . limit ;
1493+ if ( v2 . offset != null ) normalizedOptions . skip = v2 . offset ;
1494+ if ( v2 . aggregations ) normalizedOptions . aggregations = v2 . aggregations ;
1495+ if ( v2 . groupBy ) normalizedOptions . groupBy = v2 . groupBy ;
1496+ } else {
1497+ // Legacy QueryOptions — pass through as-is
1498+ Object . assign ( normalizedOptions , options ) ;
1499+ }
1500+
14521501 // 1. Handle Pagination
1453- if ( options . top ) queryParams . set ( 'top' , options . top . toString ( ) ) ;
1454- if ( options . skip ) queryParams . set ( 'skip' , options . skip . toString ( ) ) ;
1502+ if ( normalizedOptions . top ) queryParams . set ( 'top' , normalizedOptions . top . toString ( ) ) ;
1503+ if ( normalizedOptions . skip ) queryParams . set ( 'skip' , normalizedOptions . skip . toString ( ) ) ;
14551504
14561505 // 2. Handle Sort
1457- if ( options . sort ) {
1506+ if ( normalizedOptions . sort ) {
14581507 // Check if it's AST
1459- if ( Array . isArray ( options . sort ) && typeof options . sort [ 0 ] === 'object' ) {
1460- queryParams . set ( 'sort' , JSON . stringify ( options . sort ) ) ;
1508+ if ( Array . isArray ( normalizedOptions . sort ) && typeof normalizedOptions . sort [ 0 ] === 'object' ) {
1509+ queryParams . set ( 'sort' , JSON . stringify ( normalizedOptions . sort ) ) ;
14611510 } else {
1462- const sortVal = Array . isArray ( options . sort ) ? options . sort . join ( ',' ) : options . sort ;
1511+ const sortVal = Array . isArray ( normalizedOptions . sort ) ? normalizedOptions . sort . join ( ',' ) : normalizedOptions . sort ;
14631512 queryParams . set ( 'sort' , sortVal as string ) ;
14641513 }
14651514 }
14661515
14671516 // 3. Handle Select
1468- if ( options . select ) {
1469- queryParams . set ( 'select' , options . select . join ( ',' ) ) ;
1517+ if ( normalizedOptions . select ) {
1518+ queryParams . set ( 'select' , normalizedOptions . select . join ( ',' ) ) ;
14701519 }
14711520
14721521 // 4. Handle Filters (Simple vs AST)
14731522 // Canonical HTTP param name: `filter` (singular). `filters` (plural) is accepted
14741523 // for backward compatibility but `filter` is the standard going forward.
1475- const filterValue = options . filter ?? options . filters ;
1524+ const filterValue = normalizedOptions . filter ?? normalizedOptions . filters ;
14761525 if ( filterValue ) {
14771526 // Detect AST filter format vs simple key-value map. AST filters use an array structure
14781527 // with [field, operator, value] or [logicOp, ...nodes] shape (see isFilterAST from spec).
@@ -1491,11 +1540,11 @@ export class ObjectStackClient {
14911540 }
14921541
14931542 // 5. Handle Aggregations & GroupBy (Pass through as JSON if present)
1494- if ( options . aggregations ) {
1495- queryParams . set ( 'aggregations' , JSON . stringify ( options . aggregations ) ) ;
1543+ if ( normalizedOptions . aggregations ) {
1544+ queryParams . set ( 'aggregations' , JSON . stringify ( normalizedOptions . aggregations ) ) ;
14961545 }
1497- if ( options . groupBy ) {
1498- queryParams . set ( 'groupBy' , options . groupBy . join ( ',' ) ) ;
1546+ if ( normalizedOptions . groupBy ) {
1547+ queryParams . set ( 'groupBy' , normalizedOptions . groupBy . join ( ',' ) ) ;
14991548 }
15001549
15011550 const res = await this . fetch ( `${ this . baseUrl } ${ route } /${ object } ?${ queryParams . toString ( ) } ` ) ;
0 commit comments