@onchaindb/sdk 0.4.5 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/.claude/settings.local.json +10 -2
  2. package/README.md +422 -355
  3. package/dist/batch.d.ts +1 -10
  4. package/dist/batch.d.ts.map +1 -1
  5. package/dist/batch.js +4 -26
  6. package/dist/batch.js.map +1 -1
  7. package/dist/client.d.ts +31 -46
  8. package/dist/client.d.ts.map +1 -1
  9. package/dist/client.js +222 -357
  10. package/dist/client.js.map +1 -1
  11. package/dist/database.d.ts +14 -131
  12. package/dist/database.d.ts.map +1 -1
  13. package/dist/database.js +35 -131
  14. package/dist/database.js.map +1 -1
  15. package/dist/index.d.ts +10 -13
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +4 -18
  18. package/dist/index.js.map +1 -1
  19. package/dist/query-sdk/ConditionBuilder.d.ts +3 -11
  20. package/dist/query-sdk/ConditionBuilder.d.ts.map +1 -1
  21. package/dist/query-sdk/ConditionBuilder.js +10 -48
  22. package/dist/query-sdk/ConditionBuilder.js.map +1 -1
  23. package/dist/query-sdk/NestedBuilders.d.ts +33 -30
  24. package/dist/query-sdk/NestedBuilders.d.ts.map +1 -1
  25. package/dist/query-sdk/NestedBuilders.js +46 -43
  26. package/dist/query-sdk/NestedBuilders.js.map +1 -1
  27. package/{src/query-sdk/dist/OnChainDB.d.ts → dist/query-sdk/OnDB.d.ts} +10 -2
  28. package/dist/query-sdk/OnDB.d.ts.map +1 -0
  29. package/{src/query-sdk/dist/OnChainDB.js → dist/query-sdk/OnDB.js} +86 -18
  30. package/dist/query-sdk/OnDB.js.map +1 -0
  31. package/dist/query-sdk/QueryBuilder.d.ts +4 -2
  32. package/dist/query-sdk/QueryBuilder.d.ts.map +1 -1
  33. package/dist/query-sdk/QueryBuilder.js +47 -169
  34. package/dist/query-sdk/QueryBuilder.js.map +1 -1
  35. package/dist/query-sdk/QueryResult.d.ts +0 -38
  36. package/dist/query-sdk/QueryResult.d.ts.map +1 -1
  37. package/dist/query-sdk/QueryResult.js +1 -227
  38. package/dist/query-sdk/QueryResult.js.map +1 -1
  39. package/dist/query-sdk/index.d.ts +2 -2
  40. package/dist/query-sdk/index.d.ts.map +1 -1
  41. package/dist/query-sdk/index.js +3 -3
  42. package/dist/query-sdk/index.js.map +1 -1
  43. package/dist/query-sdk/operators.d.ts +32 -28
  44. package/dist/query-sdk/operators.d.ts.map +1 -1
  45. package/dist/query-sdk/operators.js +45 -155
  46. package/dist/query-sdk/operators.js.map +1 -1
  47. package/dist/types.d.ts +159 -36
  48. package/dist/types.d.ts.map +1 -1
  49. package/dist/types.js +8 -8
  50. package/dist/types.js.map +1 -1
  51. package/dist/x402/types.d.ts +1 -1
  52. package/dist/x402/types.d.ts.map +1 -1
  53. package/dist/x402/utils.js +2 -2
  54. package/dist/x402/utils.js.map +1 -1
  55. package/jest.config.js +4 -0
  56. package/package.json +1 -1
  57. package/skills.md +0 -1
  58. package/src/batch.d.ts +3 -3
  59. package/src/batch.js +1 -1
  60. package/src/client.ts +287 -823
  61. package/src/database.d.ts +1 -1
  62. package/src/database.js +4 -4
  63. package/src/database.ts +71 -494
  64. package/src/index.d.ts +18 -18
  65. package/src/index.js +16 -16
  66. package/src/index.ts +44 -198
  67. package/src/query-sdk/ConditionBuilder.ts +37 -89
  68. package/src/query-sdk/NestedBuilders.ts +90 -92
  69. package/src/query-sdk/{OnChainDB.ts → OnDB.ts} +1 -1
  70. package/src/query-sdk/QueryBuilder.ts +59 -218
  71. package/src/query-sdk/QueryResult.ts +4 -330
  72. package/src/query-sdk/README.md +218 -587
  73. package/src/query-sdk/index.ts +2 -2
  74. package/src/query-sdk/operators.ts +91 -200
  75. package/src/query-sdk/tests/FieldConditionBuilder.test.ts +70 -71
  76. package/src/query-sdk/tests/LogicalOperator.test.ts +43 -82
  77. package/src/query-sdk/tests/NestedBuilders.test.ts +229 -309
  78. package/src/query-sdk/tests/QueryBuilder.test.ts +5 -5
  79. package/src/query-sdk/tests/QueryResult.test.ts +41 -435
  80. package/src/query-sdk/tests/comprehensive.test.ts +4 -185
  81. package/src/tests/client-requests.test.ts +280 -0
  82. package/src/tests/client-validation.test.ts +80 -0
  83. package/src/types.d.ts +6 -6
  84. package/src/types.js +8 -8
  85. package/src/types.ts +239 -54
  86. package/src/x402/types.ts +3 -3
  87. package/src/x402/utils.ts +3 -3
  88. package/examples/blob-upload-example.ts +0 -140
  89. package/src/batch.ts +0 -257
  90. package/src/query-sdk/dist/ConditionBuilder.d.ts +0 -22
  91. package/src/query-sdk/dist/ConditionBuilder.js +0 -90
  92. package/src/query-sdk/dist/FieldConditionBuilder.d.ts +0 -1
  93. package/src/query-sdk/dist/FieldConditionBuilder.js +0 -6
  94. package/src/query-sdk/dist/NestedBuilders.d.ts +0 -43
  95. package/src/query-sdk/dist/NestedBuilders.js +0 -144
  96. package/src/query-sdk/dist/QueryBuilder.d.ts +0 -70
  97. package/src/query-sdk/dist/QueryBuilder.js +0 -295
  98. package/src/query-sdk/dist/QueryResult.d.ts +0 -52
  99. package/src/query-sdk/dist/QueryResult.js +0 -293
  100. package/src/query-sdk/dist/SelectionBuilder.d.ts +0 -20
  101. package/src/query-sdk/dist/SelectionBuilder.js +0 -80
  102. package/src/query-sdk/dist/adapters/HttpClientAdapter.d.ts +0 -27
  103. package/src/query-sdk/dist/adapters/HttpClientAdapter.js +0 -170
  104. package/src/query-sdk/dist/index.d.ts +0 -36
  105. package/src/query-sdk/dist/index.js +0 -27
  106. package/src/query-sdk/dist/operators.d.ts +0 -56
  107. package/src/query-sdk/dist/operators.js +0 -289
  108. package/src/query-sdk/dist/tests/setup.d.ts +0 -15
  109. package/src/query-sdk/dist/tests/setup.js +0 -46
  110. package/src/query-sdk/jest.config.js +0 -25
  111. package/src/query-sdk/package.json +0 -46
  112. package/src/query-sdk/tests/aggregations.test.ts +0 -653
  113. package/src/query-sdk/tests/integration.test.ts +0 -608
  114. package/src/query-sdk/tests/operators.test.ts +0 -327
  115. package/src/query-sdk/tests/unit.test.ts +0 -794
  116. package/src/query-sdk/tsconfig.json +0 -26
  117. package/src/query-sdk/yarn.lock +0 -3092
@@ -1,248 +1,156 @@
1
- # OnChainDB Query SDK
1
+ # OnDB Query SDK
2
2
 
3
- The Query SDK provides a powerful fluent API for building queries with server-side JOINs, complex logical operators, field conditions, and result processing utilities.
3
+ Fluent API for building queries with server-side JOINs, logical operators, field conditions, and aggregations.
4
4
 
5
5
  ## Table of Contents
6
6
 
7
- - [Quick Start](#quick-start)
8
7
  - [QueryBuilder](#querybuilder)
9
- - [Aggregations](#aggregations)
10
- - [Server-Side JOINs](#server-side-joins)
11
- - [Logical Operators](#logical-operators)
12
8
  - [Field Operators](#field-operators)
9
+ - [Logical Operators](#logical-operators)
10
+ - [Server-Side JOINs](#server-side-joins)
11
+ - [Aggregations](#aggregations)
13
12
  - [Selection](#selection)
14
- - [QueryResult Utilities](#queryresult-utilities)
15
13
  - [Nested Field Queries](#nested-field-queries)
16
- - [OnChainDB Static Class](#onchaindb-static-class)
14
+ - [QueryResult](#queryresult)
17
15
  - [HTTP Adapters](#http-adapters)
18
16
  - [API Reference](#api-reference)
19
17
 
20
- ## Quick Start
18
+ ## QueryBuilder
21
19
 
22
20
  ```typescript
23
- import { createClient } from '@onchaindb/sdk';
24
-
25
- const db = createClient({
26
- endpoint: 'http://localhost:9092',
27
- appId: 'my_app'
28
- });
29
-
30
- // Simple query
31
21
  const result = await db.queryBuilder()
32
- .collection('users')
22
+ .collection('users') // Target collection
33
23
  .whereField('status').equals('active')
34
- .selectFields(['id', 'name', 'email'])
35
- .limit(10)
24
+ .selectFields(['id', 'name'])
25
+ .orderBy('created_at', 'DESC')
26
+ .limit(50)
27
+ .offset(100)
36
28
  .execute();
37
-
38
- console.log(result.records);
39
- ```
40
-
41
- ## QueryBuilder
42
-
43
- The QueryBuilder provides a fluent interface for constructing queries.
44
-
45
- ### Basic Methods
46
-
47
- ```typescript
48
- const query = db.queryBuilder()
49
- .collection('users') // Set target collection
50
- .whereField('status').equals('active') // Add filter condition
51
- .selectFields(['id', 'name']) // Select specific fields
52
- .selectAll() // Or select all fields
53
- .limit(50) // Limit results
54
- .offset(100) // Skip results (pagination)
55
- .orderBy('created_at') // Sort results
56
- .includeHistory(true) // Include historical versions
57
- .execute(); // Execute the query
58
29
  ```
59
30
 
60
31
  ### executeUnique()
61
32
 
62
- Returns the latest record by metadata timestamp (`updatedAt` or `createdAt`). Useful for finding a single record when multiple versions may exist.
33
+ Returns the latest record by `updatedAt` / `createdAt` timestamp. Returns `null` if nothing matches.
63
34
 
64
35
  ```typescript
65
- // Find the latest user record by email
66
36
  const user = await db.queryBuilder()
67
37
  .collection('users')
68
38
  .whereField('email').equals('alice@example.com')
69
39
  .executeUnique<User>();
40
+ ```
41
+
42
+ ### buildRawQuery / clone / isValid
43
+
44
+ ```typescript
45
+ // Inspect the raw query object
46
+ const raw = db.queryBuilder()
47
+ .collection('tweets')
48
+ .whereField('author').equals('alice')
49
+ .buildRawQuery();
50
+
51
+ // Clone a base query and diverge
52
+ const base = db.queryBuilder().collection('users').selectAll();
53
+ const active = base.clone().whereField('active').isTrue();
54
+ const inactive = base.clone().whereField('active').isFalse();
70
55
 
71
- // Returns: User | null (latest version by timestamp)
56
+ // Check if a query has conditions set
57
+ queryBuilder.isValid(); // false until whereField / find is called
72
58
  ```
73
59
 
74
- ### WhereField Operators
60
+ ## Field Operators
75
61
 
76
- All operators available on `whereField()`:
62
+ All operators available on `whereField()` and `FieldConditionBuilder`:
77
63
 
78
64
  ```typescript
79
65
  // Comparison
80
- .equals(value)
81
- .notEquals(value)
82
- .greaterThan(value)
83
- .greaterThanOrEqual(value)
84
- .lessThan(value)
85
- .lessThanOrEqual(value)
86
- .between(min, max)
66
+ .equals(value) .notEquals(value)
67
+ .greaterThan(value) .greaterThanOrEqual(value)
68
+ .lessThan(value) .lessThanOrEqual(value)
69
+ .between(min, max) // betweenOp: { from, to }
87
70
 
88
71
  // String
89
- .contains(value)
90
- .startsWith(value)
91
- .endsWith(value)
72
+ .contains(value) // includes
73
+ .startsWith(value) .endsWith(value)
92
74
  .regExpMatches(pattern)
93
75
  .includesCaseInsensitive(value)
94
76
  .startsWithCaseInsensitive(value)
95
77
  .endsWithCaseInsensitive(value)
96
78
 
97
79
  // Array
98
- .in(values)
99
- .notIn(values)
80
+ .in(values) .notIn(values)
100
81
 
101
82
  // Existence
102
- .exists()
103
- .notExists()
104
- .isNull()
105
- .isNotNull()
106
- .isTrue()
107
- .isFalse()
108
-
109
- // IP Address
110
- .isLocalIp()
111
- .isExternalIp()
112
- .inCountry(countryCode)
113
- .cidr(cidrRange)
114
-
115
- // Special
116
- .b64(value)
117
- .inDataset(dataset)
83
+ .exists() .notExists() .isNull() .isNotNull()
84
+ .isTrue() .isFalse()
85
+
86
+ // Network / security
87
+ .isLocalIp() .isExternalIp()
88
+ .inCountry(countryCode) .cidr(cidrRange)
89
+ .b64(value) .inDataset(dataset)
118
90
  ```
119
91
 
120
- ### Debugging
92
+ ### FieldConditionBuilder directly
121
93
 
122
94
  ```typescript
123
- // Get raw query object for debugging
124
- const rawQuery = db.queryBuilder()
125
- .collection('tweets')
126
- .whereField('author').equals('alice')
127
- .selectAll()
128
- .buildRawQuery();
95
+ import { FieldConditionBuilder } from '@ondb/sdk';
129
96
 
130
- console.log(JSON.stringify(rawQuery, null, 2));
131
-
132
- // Clone a query builder
133
- const baseQuery = db.queryBuilder().collection('users').selectAll();
134
- const activeUsers = baseQuery.clone().whereField('active').isTrue();
135
- const inactiveUsers = baseQuery.clone().whereField('active').isFalse();
97
+ const condition = new FieldConditionBuilder('user.profile.bio').contains('Developer');
98
+ condition.toComposable(); // { user: { profile: { bio: { includes: 'Developer' } } } }
136
99
  ```
137
100
 
138
- ## Aggregations
139
-
140
- QueryBuilder provides built-in aggregation methods for counting, summing, averaging, and grouping data.
141
-
142
- ### Basic Aggregations
101
+ ## Logical Operators
143
102
 
144
103
  ```typescript
145
- // Count records
146
- const activeUsers = await db.queryBuilder()
147
- .collection('users')
148
- .whereField('active').equals(true)
149
- .count();
150
- // Returns: number
104
+ import { LogicalOperator } from '@ondb/sdk';
151
105
 
152
- // Sum a numeric field
153
- const totalRevenue = await db.queryBuilder()
154
- .collection('orders')
155
- .whereField('status').equals('completed')
156
- .sumBy('amount');
157
- // Returns: number
158
-
159
- // Calculate average
160
- const avgPrice = await db.queryBuilder()
161
- .collection('products')
162
- .whereField('category').equals('electronics')
163
- .avgBy('price');
164
- // Returns: number
165
-
166
- // Find maximum value
167
- const highestPrice = await db.queryBuilder()
168
- .collection('products')
169
- .maxBy('price');
170
- // Returns: T | null
171
-
172
- // Find minimum value
173
- const lowestPrice = await db.queryBuilder()
174
- .collection('products')
175
- .minBy('price');
176
- // Returns: T | null
177
-
178
- // Get distinct values
179
- const categories = await db.queryBuilder()
180
- .collection('products')
181
- .distinctBy('category');
182
- // Returns: string[]
183
-
184
- // Count distinct values
185
- const uniqueCategories = await db.queryBuilder()
186
- .collection('products')
187
- .countDistinct('category');
188
- // Returns: number
106
+ const result = await db.queryBuilder()
107
+ .collection('posts')
108
+ .find(builder =>
109
+ LogicalOperator.And([
110
+ builder.field('published').isTrue(),
111
+ LogicalOperator.Or([
112
+ builder.field('category').equals('tech'),
113
+ builder.field('views').greaterThan(1000)
114
+ ]),
115
+ LogicalOperator.Not([
116
+ builder.field('archived').isTrue()
117
+ ])
118
+ ])
119
+ )
120
+ .selectAll()
121
+ .execute();
189
122
  ```
190
123
 
191
- ### Grouped Aggregations
124
+ `builder.field(name)` returns a `FieldConditionBuilder`. Calling any operator on it (`.equals()`, `.greaterThan()`, etc.) returns a `LogicalOperator` directly — no extra wrapping needed.
192
125
 
193
- Use `groupBy()` to perform aggregations on groups of records:
126
+ ### Instance chain methods
194
127
 
195
128
  ```typescript
196
- // Count users by country
197
- const usersByCountry = await db.queryBuilder()
198
- .collection('users')
199
- .groupBy('country')
200
- .count();
201
- // Returns: { "USA": 150, "UK": 75, "Germany": 50 }
202
-
203
- // Sum order amounts by category
204
- const salesByCategory = await db.queryBuilder()
205
- .collection('orders')
206
- .whereField('status').equals('completed')
207
- .groupBy('category')
208
- .sumBy('amount');
209
- // Returns: { "electronics": 50000, "clothing": 25000 }
210
-
211
- // Average rating by product
212
- const avgRatingByProduct = await db.queryBuilder()
213
- .collection('reviews')
214
- .groupBy('productId')
215
- .avgBy('rating');
216
- // Returns: { "prod_1": 4.5, "prod_2": 3.8 }
217
-
218
- // Max/Min by group
219
- const maxPriceByCategory = await db.queryBuilder()
220
- .collection('products')
221
- .groupBy('category')
222
- .maxBy('price');
223
- // Returns: { "electronics": 999, "books": 49 }
129
+ const condition = new FieldConditionBuilder('age').greaterThan(18)
130
+ .and(new FieldConditionBuilder('active').isTrue());
131
+ // type: 'and', conditions: [age > 18, active = true]
224
132
  ```
225
133
 
226
- ### Nested Field Grouping
227
-
228
- GroupBy supports nested field paths:
134
+ ### ConditionBuilder (inside find())
229
135
 
230
136
  ```typescript
231
- // Group by nested field
232
- const ordersByRegion = await db.queryBuilder()
233
- .collection('orders')
234
- .groupBy('customer.address.region')
235
- .sumBy('total');
236
- // Returns: { "West": 10000, "East": 8500 }
137
+ const result = await db.queryBuilder()
138
+ .collection('users')
139
+ .find(builder => {
140
+ const isAdult = builder.field('age').greaterThan(18);
141
+ const isActive = builder.field('status').equals('active');
142
+ return builder.and(isAdult, isActive);
143
+ })
144
+ .execute();
237
145
  ```
238
146
 
239
147
  ## Server-Side JOINs
240
148
 
241
- Server-side JOINs execute on the backend in a single request. Use `$data.fieldname` to reference parent record fields.
149
+ JOINs run on the backend in a single request. Use `$data.fieldname` to reference fields from the parent record.
242
150
 
243
- ### joinOne (One-to-One)
151
+ ### joinOne (one-to-one)
244
152
 
245
- Returns a single object or null. Use when you expect at most one related record.
153
+ Returns a single object or `null`.
246
154
 
247
155
  ```typescript
248
156
  const result = await db.queryBuilder()
@@ -254,12 +162,12 @@ const result = await db.queryBuilder()
254
162
  .selectAll()
255
163
  .execute();
256
164
 
257
- // Result: { id, content, author, author_info: { display_name, ... } | null }
165
+ // record.author_info => { display_name, avatar_url, verified } | null
258
166
  ```
259
167
 
260
- ### joinMany (One-to-Many)
168
+ ### joinMany (one-to-many)
261
169
 
262
- Returns an array of related records. Use when you expect multiple related records.
170
+ Returns an array.
263
171
 
264
172
  ```typescript
265
173
  const result = await db.queryBuilder()
@@ -271,22 +179,7 @@ const result = await db.queryBuilder()
271
179
  .selectAll()
272
180
  .execute();
273
181
 
274
- // Result: { address, name, tweets: [{ id, content, ... }, ...] }
275
- ```
276
-
277
- ### joinWith (Default Behavior)
278
-
279
- Returns an array (same as joinMany but without explicit many flag).
280
-
281
- ```typescript
282
- const result = await db.queryBuilder()
283
- .collection('posts')
284
- .joinWith('comments', 'comments')
285
- .onField('post_id').equals('$data.id')
286
- .selectAll()
287
- .build()
288
- .selectAll()
289
- .execute();
182
+ // record.tweets => [{ id, content, created_at }, ...]
290
183
  ```
291
184
 
292
185
  ### Multiple JOINs
@@ -295,17 +188,14 @@ const result = await db.queryBuilder()
295
188
  const result = await db.queryBuilder()
296
189
  .collection('tweets')
297
190
  .whereField('reply_to_id').isNull()
298
- // Author profile (one-to-one)
299
191
  .joinOne('author_info', 'users')
300
192
  .onField('address').equals('$data.author')
301
193
  .selectFields(['display_name', 'avatar_url', 'verified'])
302
194
  .build()
303
- // All likes (one-to-many)
304
195
  .joinMany('likes', 'likes')
305
196
  .onField('tweet_id').equals('$data.id')
306
197
  .selectFields(['user', 'created_at'])
307
198
  .build()
308
- // All replies (one-to-many)
309
199
  .joinMany('replies', 'tweets')
310
200
  .onField('reply_to_id').equals('$data.id')
311
201
  .selectFields(['id', 'author', 'content'])
@@ -317,18 +207,15 @@ const result = await db.queryBuilder()
317
207
 
318
208
  ### Nested JOINs
319
209
 
320
- Chain JOINs before calling `.build()` to create nested relationships:
210
+ Chain JOINs before `.build()` to create nested relationships:
321
211
 
322
212
  ```typescript
323
213
  const result = await db.queryBuilder()
324
214
  .collection('tweets')
325
- .whereField('id').equals(tweetId)
326
- // Get replies with their authors
327
215
  .joinMany('replies', 'tweets')
328
216
  .onField('reply_to_id').equals('$data.id')
329
217
  .selectAll()
330
- // Nested: get author for each reply
331
- .joinOne('author_info', 'users')
218
+ .joinOne('author_info', 'users') // Nested: author for each reply
332
219
  .onField('address').equals('$data.author')
333
220
  .selectFields(['display_name', 'avatar_url'])
334
221
  .build()
@@ -337,272 +224,114 @@ const result = await db.queryBuilder()
337
224
  .execute();
338
225
  ```
339
226
 
340
- ### Self-Referential JOINs
227
+ ### JoinBuilder Operators (on `onField()`)
341
228
 
342
229
  ```typescript
343
- // Get tweets with quoted tweet info
344
- const result = await db.queryBuilder()
345
- .collection('tweets')
346
- .whereField('quote_tweet_id').isNotNull()
347
- .joinOne('quote_tweet', 'tweets')
348
- .onField('id').equals('$data.quote_tweet_id')
349
- .selectFields(['id', 'content', 'author', 'created_at'])
350
- .joinOne('author_info', 'users')
351
- .onField('address').equals('$data.author')
352
- .selectFields(['display_name', 'avatar_url'])
353
- .build()
354
- .build()
355
- .selectAll()
356
- .execute();
230
+ .equals(value) .in(values)
231
+ .greaterThan(v) .lessThan(v)
232
+ .isNull() .isNotNull()
357
233
  ```
358
234
 
359
- ### JoinBuilder Operators
235
+ ## Aggregations
360
236
 
361
- Available on `onField()`:
237
+ All aggregations execute server-side.
362
238
 
363
- ```typescript
364
- .equals(value)
365
- .in(values)
366
- .greaterThan(value)
367
- .lessThan(value)
368
- .isNull()
369
- .isNotNull()
370
- ```
239
+ ### Basic Aggregations
371
240
 
372
- ## Logical Operators
241
+ ```typescript
242
+ // Count
243
+ const n = await db.queryBuilder()
244
+ .collection('users')
245
+ .whereField('active').isTrue()
246
+ .count();
373
247
 
374
- Build complex conditions with AND, OR, and NOT:
248
+ // Sum / average / max / min
249
+ const total = await db.queryBuilder().collection('orders').sumBy('amount');
250
+ const avg = await db.queryBuilder().collection('orders').avgBy('amount');
251
+ const max = await db.queryBuilder().collection('products').maxBy('price');
252
+ const min = await db.queryBuilder().collection('products').minBy('price');
375
253
 
376
- ```typescript
377
- import { LogicalOperator } from '@onchaindb/sdk';
254
+ // Distinct values
255
+ const categories = await db.queryBuilder().collection('products').distinctBy('category');
378
256
 
379
- const result = await db.queryBuilder()
380
- .collection('posts')
381
- .find(builder =>
382
- LogicalOperator.And([
383
- LogicalOperator.Condition(builder.field('published').equals(true)),
384
- LogicalOperator.Or([
385
- LogicalOperator.Condition(builder.field('category').equals('tech')),
386
- LogicalOperator.Condition(builder.field('views').greaterThan(1000))
387
- ]),
388
- LogicalOperator.Not([
389
- LogicalOperator.Condition(builder.field('archived').equals(true))
390
- ])
391
- ])
392
- )
393
- .selectAll()
394
- .execute();
257
+ // Count distinct
258
+ const n = await db.queryBuilder().collection('products').countDistinct('category');
395
259
  ```
396
260
 
397
- ### ConditionBuilder Methods
261
+ ### Grouped Aggregations
262
+
263
+ `groupBy(field)` returns a `GroupByQueryBuilder` with the same aggregate methods:
398
264
 
399
265
  ```typescript
400
- import { ConditionBuilder, LogicalOperator } from '@onchaindb/sdk';
401
-
402
- const builder = new ConditionBuilder();
403
-
404
- // Create field conditions
405
- builder.field('name').equals('John')
406
- builder.field('age').greaterThan(18)
407
-
408
- // Group conditions
409
- builder.andGroup(() => [...conditions])
410
- builder.orGroup(() => [...conditions])
411
- builder.notGroup(() => [...conditions])
412
-
413
- // Nested field conditions (ORM-like)
414
- builder.nested('user', nested =>
415
- nested.andGroup(() => [
416
- LogicalOperator.Condition(nested.field('profile').field('name').equals('John')),
417
- LogicalOperator.Condition(nested.field('settings').field('theme').equals('dark'))
418
- ])
419
- )
420
- ```
266
+ // Count per group
267
+ const byCountry = await db.queryBuilder()
268
+ .collection('users')
269
+ .groupBy('country')
270
+ .count();
271
+ // { "USA": 150, "UK": 75, "Germany": 50 }
421
272
 
422
- ## Field Operators
273
+ // Sum per group
274
+ const salesByCategory = await db.queryBuilder()
275
+ .collection('orders')
276
+ .whereField('status').equals('completed')
277
+ .groupBy('category')
278
+ .sumBy('amount');
279
+ // { "electronics": 50000, "clothing": 25000 }
423
280
 
424
- ### FieldConditionBuilder
281
+ // Supports nested field paths
282
+ const byRegion = await db.queryBuilder()
283
+ .collection('orders')
284
+ .groupBy('customer.address.region')
285
+ .sumBy('total');
286
+ ```
287
+
288
+ ## Selection
425
289
 
426
- All available operators:
290
+ ### selectFields / selectAll
427
291
 
428
292
  ```typescript
429
- import { FieldConditionBuilder } from '@onchaindb/sdk';
430
-
431
- const field = new FieldConditionBuilder('fieldName');
432
-
433
- // Base operators
434
- field.equals(value) // is
435
- field.notEquals(value) // isNot
436
- field.in(values) // in
437
- field.notIn(values) // notIn
438
- field.isNull() // isNull: true
439
- field.isNotNull() // isNull: false
440
- field.exists() // exists: true
441
- field.notExists() // exists: false
442
-
443
- // String operators
444
- field.startsWith(value)
445
- field.endsWith(value)
446
- field.contains(value) // includes
447
- field.regExpMatches(pattern)
448
- field.includesCaseInsensitive(value)
449
- field.startsWithCaseInsensitive(value)
450
- field.endsWithCaseInsensitive(value)
451
-
452
- // Number operators
453
- field.greaterThan(value)
454
- field.lessThan(value)
455
- field.greaterThanOrEqual(value)
456
- field.lessThanOrEqual(value)
457
- field.between(min, max) // betweenOp: { from, to }
458
-
459
- // IP operators
460
- field.isLocalIp()
461
- field.isExternalIp()
462
- field.inCountry(countryCode)
463
- field.cidr(cidrRange)
464
-
465
- // Misc operators
466
- field.b64(value) // Base64 comparison
467
- field.inDataset(dataset) // Dataset membership
468
-
469
- // Convenience
470
- field.isTrue() // equals(true)
471
- field.isFalse() // equals(false)
293
+ db.queryBuilder().collection('users').selectFields(['id', 'name', 'email']).execute();
294
+ db.queryBuilder().collection('users').selectAll().execute();
472
295
  ```
473
296
 
474
- ## Selection
475
-
476
297
  ### SelectionBuilder
477
298
 
478
299
  ```typescript
479
- import { SelectionBuilder } from '@onchaindb/sdk';
300
+ import { SelectionBuilder } from '@ondb/sdk';
480
301
 
481
- // Select specific fields
482
- const selection = new SelectionBuilder()
483
- .field('id')
484
- .field('name')
485
- .field('email')
486
- .build();
302
+ // Field-by-field
303
+ const sel = new SelectionBuilder().field('id').field('name').build();
487
304
 
488
- // Select multiple fields at once
489
- new SelectionBuilder()
490
- .fields(['id', 'name', 'email', 'created_at'])
491
- .build();
305
+ // Multiple at once
306
+ new SelectionBuilder().fields(['id', 'name', 'email']).build();
492
307
 
493
- // Nested field selection
308
+ // Nested selection
494
309
  new SelectionBuilder()
495
310
  .field('id')
496
- .nested('profile', nested => nested.field('bio').field('avatar'))
311
+ .nested('profile', n => n.field('bio').field('avatar'))
497
312
  .build();
498
313
 
499
314
  // Exclude fields
500
- new SelectionBuilder()
501
- .excludeFields(['password', 'secret_key'])
502
- .build();
315
+ new SelectionBuilder().excludeFields(['password', 'secret']).build();
503
316
 
504
- // Select all fields
505
- SelectionBuilder.all(); // Returns {}
317
+ // Select all
318
+ SelectionBuilder.all(); // {}
506
319
  ```
507
320
 
508
- ### Using with QueryBuilder
321
+ ### select() with builder
509
322
 
510
323
  ```typescript
511
- // Method 1: selectFields()
512
- db.queryBuilder()
513
- .collection('users')
514
- .selectFields(['id', 'name', 'email'])
515
- .execute();
516
-
517
- // Method 2: selectAll()
518
- db.queryBuilder()
519
- .collection('users')
520
- .selectAll()
521
- .execute();
522
-
523
- // Method 3: select() with builder
524
324
  db.queryBuilder()
525
325
  .collection('users')
526
326
  .select(s => s.field('id').field('name').nested('profile', n => n.field('bio')))
527
327
  .execute();
528
328
  ```
529
329
 
530
- ## QueryResult Utilities
531
-
532
- Process query results with built-in utilities:
533
-
534
- ```typescript
535
- import { createQueryResult, QueryResult } from '@onchaindb/sdk';
536
-
537
- const result = createQueryResult(response.records);
538
-
539
- // Basic utilities
540
- result.len() // Count of records
541
- result.isEmpty() // Check if empty
542
- result.first() // First record or undefined
543
- result.last() // Last record or undefined
544
- result.get(5) // Get record at index
545
-
546
- // Iteration
547
- result.any(r => r.active) // Any match predicate?
548
- result.all(r => r.verified) // All match predicate?
549
- result.find(r => r.id === '123') // Find first matching
550
- result.filter(r => r.status === 'active')
551
- result.map(r => r.name)
552
- result.forEach((r, i) => console.log(i, r))
553
- result.reduce((acc, r) => acc + r.amount, 0)
554
-
555
- // Aggregation
556
- result.groupBy('department') // { 'sales': [...], 'engineering': [...] }
557
- result.pluck('name') // ['Alice', 'Bob', ...]
558
- result.pluckStrings('name') // Type-safe string pluck
559
- result.pluckNumbers('age') // Type-safe number pluck
560
- result.countBy('status') // { 'active': 10, 'inactive': 3 }
561
-
562
- // Numeric summary
563
- const stats = result.summarizeNumeric('salary');
564
- // { count, sum, mean, median, min, max, standardDeviation, variance }
565
-
566
- // Sorting
567
- result.sortBy('name') // Ascending
568
- result.sortBy('created_at', false) // Descending
569
- result.sortByMultiple([
570
- { field: 'department', ascending: true },
571
- { field: 'salary', ascending: false }
572
- ])
573
-
574
- // Pagination
575
- result.paginate(2, 20) // Page 2, 20 items per page
576
- result.chunk(10) // Split into chunks of 10
577
-
578
- // Distinct
579
- result.distinctBy('email') // Unique by field
580
- result.unique() // Unique records
581
-
582
- // Joining (client-side)
583
- const departments = createQueryResult(deptData);
584
- result.innerJoin(departments, 'dept_id', 'id')
585
- result.leftJoin(departments, 'dept_id', 'id')
586
-
587
- // Export
588
- result.toCsv() // CSV string
589
- result.toCsv(';') // Custom delimiter
590
- result.toJSON() // JSON string
591
- result.toArray() // Plain array
592
-
593
- // Debugging
594
- result.inspect() // Log to console
595
- result.inspect(5) // Log first 5 records
596
- ```
597
-
598
330
  ## Nested Field Queries
599
331
 
600
- Query nested objects with dot notation or ORM-like builders:
601
-
602
- ### Dot Notation
332
+ ### Dot notation
603
333
 
604
334
  ```typescript
605
- // Query nested fields with dot notation
606
335
  const result = await db.queryBuilder()
607
336
  .collection('users')
608
337
  .whereField('profile.settings.theme').equals('dark')
@@ -610,99 +339,68 @@ const result = await db.queryBuilder()
610
339
  .execute();
611
340
  ```
612
341
 
613
- ### ORM-like Nested Builder
342
+ ### NestedConditionBuilder
614
343
 
615
344
  ```typescript
616
- import { NestedConditionBuilder, LogicalOperator } from '@onchaindb/sdk';
345
+ import { NestedConditionBuilder } from '@ondb/sdk';
617
346
 
618
347
  const result = await db.queryBuilder()
619
348
  .collection('users')
620
349
  .find(builder =>
621
350
  builder.nested('user', nested =>
622
- nested.andGroup(() => [
623
- LogicalOperator.Condition(
624
- nested.field('profile').field('name').equals('John')
625
- ),
626
- LogicalOperator.Condition(
627
- nested.field('settings').field('notifications').isTrue()
628
- )
351
+ nested.andGroup(b => [
352
+ b.field('name').equals('John'),
353
+ b.field('status').equals('active')
629
354
  ])
630
355
  )
631
356
  )
632
357
  .execute();
633
358
  ```
634
359
 
635
- ## OnChainDB Static Class
360
+ ## QueryResult
636
361
 
637
- For standalone query building without client initialization:
362
+ A lightweight wrapper around query response data for convenient access. Aggregation, sorting, filtering, and joining are handled server-side via `QueryBuilder`.
638
363
 
639
364
  ```typescript
640
- import { OnChainDB, FetchHttpClient } from '@onchaindb/sdk';
641
-
642
- // Configure globally
643
- OnChainDB.configure(new FetchHttpClient(), 'http://localhost:9092', 'api_key');
644
-
645
- // Build queries
646
- const query = OnChainDB.query()
647
- .collection('users')
648
- .selectAll();
649
-
650
- // Execute raw queries
651
- const result = await OnChainDB.rawQuery('{"find": {}, "select": {}}');
365
+ import { createQueryResult } from '@ondb/sdk';
652
366
 
653
- // Query from JSON object
654
- const result = await OnChainDB.queryFromJson({ find: {}, select: {} });
655
-
656
- // Health check
657
- const isHealthy = await OnChainDB.healthCheck();
658
-
659
- // Create indexes
660
- await OnChainDB.createIndex('my_app', 'users', 'email');
661
- await OnChainDB.createUniqueIndex('my_app', 'users', 'id');
367
+ const result = createQueryResult(response.records);
662
368
 
663
- // Reset configuration
664
- OnChainDB.reset();
369
+ result.len() // Total record count
370
+ result.length() // Alias for len()
371
+ result.isEmpty() // true if no records
372
+ result.first() // First record or undefined
373
+ result.last() // Last record or undefined
374
+ result.get(2) // Record at index or undefined
375
+ result.toArray() // Plain array copy
376
+ result.toJSON() // JSON string
377
+ result.cast<User>() // Re-type without copying
665
378
  ```
666
379
 
667
380
  ## HTTP Adapters
668
381
 
669
- ### Built-in Adapters
670
-
671
382
  ```typescript
672
383
  import {
673
- FetchHttpClient,
674
- AxiosHttpClient,
675
- NodeHttpClient,
676
- createHttpClient
677
- } from '@onchaindb/sdk';
678
-
679
- // Browser (Fetch API)
680
- const fetchClient = new FetchHttpClient();
681
-
682
- // Axios (browser or Node.js)
683
- const axiosClient = new AxiosHttpClient();
684
-
685
- // Node.js native HTTP
686
- const nodeClient = new NodeHttpClient();
687
-
688
- // Auto-detect best client for environment
689
- const client = createHttpClient();
384
+ FetchHttpClient, // Browser Fetch API
385
+ AxiosHttpClient, // Axios (browser or Node.js)
386
+ NodeHttpClient, // Node.js native HTTP
387
+ createHttpClient // Auto-detect best for environment
388
+ } from '@ondb/sdk';
690
389
  ```
691
390
 
692
391
  ### Custom HTTP Client
693
392
 
694
393
  ```typescript
695
- import { HttpClient } from '@onchaindb/sdk';
394
+ import { HttpClient } from '@ondb/sdk';
696
395
 
697
- class CustomHttpClient implements HttpClient {
396
+ class MyHttpClient implements HttpClient {
698
397
  async post(url: string, data: any, headers?: Record<string, string>): Promise<any> {
699
- // Your implementation
700
- const response = await fetch(url, {
398
+ const res = await fetch(url, {
701
399
  method: 'POST',
702
400
  headers: { 'Content-Type': 'application/json', ...headers },
703
401
  body: JSON.stringify(data)
704
402
  });
705
- return response.json();
403
+ return res.json();
706
404
  }
707
405
  }
708
406
  ```
@@ -714,68 +412,61 @@ class CustomHttpClient implements HttpClient {
714
412
  | Method | Description |
715
413
  |--------|-------------|
716
414
  | `collection(name)` | Set target collection |
717
- | `whereField(name)` | Start a field condition |
415
+ | `whereField(name)` | Start a field condition (returns `WhereClause`) |
718
416
  | `find(builderFn)` | Set complex logical conditions |
719
- | `select(builderFn)` | Configure selection with builder |
417
+ | `select(builderFn)` | Configure selection with `SelectionBuilder` |
720
418
  | `selectFields(fields)` | Select specific fields |
721
419
  | `selectAll()` | Select all fields |
722
- | `joinOne(alias, model)` | One-to-one JOIN (returns object/null) |
723
- | `joinMany(alias, model)` | One-to-many JOIN (returns array) |
724
- | `joinWith(alias, model)` | JOIN with default behavior |
725
- | `serverJoin(alias, model, resolve)` | Low-level server JOIN |
420
+ | `joinOne(alias, model)` | One-to-one server JOIN |
421
+ | `joinMany(alias, model)` | One-to-many server JOIN |
422
+ | `joinWith(alias, model)` | Server JOIN with default array behavior |
423
+ | `orderBy(field, dir?)` | Sort (`'ASC'` or `'DESC'`) |
726
424
  | `limit(n)` | Limit result count |
727
- | `offset(n)` | Skip results (pagination) |
728
- | `orderBy(field)` | Sort results |
729
- | `includeHistory(bool)` | Include historical versions |
730
- | `withFieldMap(map)` | Set field mapping |
731
- | `execute()` | Execute query |
732
- | `executeUnique()` | Execute and return latest record by metadata |
733
- | `executeWithPayment(quoteId, txHash, network?)` | Execute with x402 payment |
425
+ | `offset(n)` | Skip results |
426
+ | `includeHistory(bool)` | Include all historical versions |
427
+ | `execute()` | Execute and return records |
428
+ | `executeUnique()` | Return latest single record or `null` |
734
429
  | `count()` | Count matching records |
735
430
  | `sumBy(field)` | Sum numeric field values |
736
- | `avgBy(field)` | Calculate average of numeric field |
737
- | `maxBy(field)` | Find maximum value |
738
- | `minBy(field)` | Find minimum value |
739
- | `distinctBy(field)` | Get distinct field values |
431
+ | `avgBy(field)` | Average numeric field values |
432
+ | `maxBy(field)` | Maximum field value |
433
+ | `minBy(field)` | Minimum field value |
434
+ | `distinctBy(field)` | Distinct field values |
740
435
  | `countDistinct(field)` | Count distinct values |
741
436
  | `groupBy(field)` | Start grouped aggregation |
742
- | `buildRawQuery()` | Get raw query object |
437
+ | `buildRawQuery()` | Inspect raw query object |
743
438
  | `clone()` | Clone the builder |
744
- | `isValid()` | Check if query has conditions |
439
+ | `isValid()` | `true` if query has conditions |
745
440
 
746
441
  ### JoinBuilder
747
442
 
748
443
  | Method | Description |
749
444
  |--------|-------------|
750
445
  | `onField(name)` | Start JOIN condition |
751
- | `on(builderFn)` | Complex JOIN condition |
752
- | `selectFields(fields)` | Select fields from joined collection |
753
- | `selectAll()` | Select all fields |
754
- | `selecting(builderFn)` | Configure selection with builder |
446
+ | `selectFields(fields)` | Select from joined collection |
447
+ | `selectAll()` | Select all from joined collection |
448
+ | `selecting(builderFn)` | Selection via `SelectionBuilder` |
755
449
  | `joinOne(alias, model)` | Nested one-to-one JOIN |
756
450
  | `joinMany(alias, model)` | Nested one-to-many JOIN |
757
- | `build()` | Complete JOIN and return to parent |
451
+ | `build()` | Complete JOIN, return to parent |
758
452
 
759
453
  ### GroupByQueryBuilder
760
454
 
761
- Returned by `queryBuilder.groupBy(field)`:
762
-
763
455
  | Method | Description |
764
456
  |--------|-------------|
765
- | `count()` | Count records per group |
766
- | `sumBy(field)` | Sum field values per group |
767
- | `avgBy(field)` | Average field values per group |
768
- | `maxBy(field)` | Max field value per group |
769
- | `minBy(field)` | Min field value per group |
457
+ | `count()` | Count per group |
458
+ | `sumBy(field)` | Sum per group |
459
+ | `avgBy(field)` | Average per group |
460
+ | `maxBy(field)` | Max per group |
461
+ | `minBy(field)` | Min per group |
770
462
 
771
- ### LogicalOperator
463
+ ### LogicalOperator (static)
772
464
 
773
- | Static Method | Description |
774
- |---------------|-------------|
775
- | `And(conditions)` | Combine with AND logic |
776
- | `Or(conditions)` | Combine with OR logic |
777
- | `Not(conditions)` | Negate conditions |
778
- | `Condition(cond)` | Wrap a field condition |
465
+ | Method | Description |
466
+ |--------|-------------|
467
+ | `And(conditions)` | AND of conditions |
468
+ | `Or(conditions)` | OR of conditions |
469
+ | `Not(conditions)` | NOT of conditions |
779
470
 
780
471
  ### SelectionBuilder
781
472
 
@@ -783,83 +474,23 @@ Returned by `queryBuilder.groupBy(field)`:
783
474
  |--------|-------------|
784
475
  | `field(name)` | Include a field |
785
476
  | `fields(names)` | Include multiple fields |
786
- | `nested(name, builderFn)` | Nested selection |
477
+ | `nested(name, fn)` | Nested field selection |
787
478
  | `exclude(name)` | Exclude a field |
788
479
  | `excludeFields(names)` | Exclude multiple fields |
789
480
  | `build()` | Get selection map |
790
- | `clear()` | Reset builder |
791
- | `static all()` | Select all fields |
481
+ | `static all()` | Select all fields (`{}`) |
792
482
 
793
483
  ### QueryResult
794
484
 
795
485
  | Method | Description |
796
486
  |--------|-------------|
797
487
  | `len()` / `length()` | Record count |
798
- | `isEmpty()` | Check if empty |
799
- | `first()` / `last()` / `get(i)` | Access records |
800
- | `any(predicate)` | Any match? |
801
- | `all(predicate)` | All match? |
802
- | `find(predicate)` | Find first matching |
803
- | `filter(predicate)` | Filter records |
804
- | `map(transform)` | Transform records |
805
- | `forEach(callback)` | Iterate records |
806
- | `reduce(callback, initial)` | Reduce records |
807
- | `groupBy(field)` | Group by field value |
808
- | `pluck(field)` | Extract field values |
809
- | `summarizeNumeric(field)` | Statistical summary |
810
- | `sortBy(field, asc?)` | Sort by field |
811
- | `sortByMultiple(specs)` | Multi-field sort |
812
- | `paginate(page, size)` | Paginate results |
813
- | `chunk(size)` | Split into chunks |
814
- | `distinctBy(field)` | Unique by field |
815
- | `count(predicate?)` | Count records |
816
- | `countBy(field)` | Count by field value |
817
- | `innerJoin(other, thisKey, otherKey)` | Inner join |
818
- | `leftJoin(other, thisKey, otherKey)` | Left join |
819
- | `toCsv(delimiter?)` | Export to CSV |
820
- | `toJSON()` | Export to JSON |
821
- | `toArray()` | Get plain array |
822
- | `inspect(limit?)` | Debug output |
823
-
824
- ## Types
825
-
826
- ```typescript
827
- // Query types
828
- interface QueryRequest {
829
- find: any;
830
- select: SelectionMap;
831
- include_history?: boolean;
832
- limit?: number;
833
- offset?: number;
834
- sort?: string[];
835
- }
836
-
837
- interface QueryResponse<T = any> {
838
- records: T[];
839
- total?: number;
840
- error?: string;
841
- }
842
-
843
- // Value types
844
- type Val = string | number | boolean | null | Val[] | Date | RegExp;
845
-
846
- interface BetweenValue {
847
- min: Val;
848
- max: Val;
849
- }
850
-
851
- // Numeric summary
852
- interface NumericSummary {
853
- count: number;
854
- sum: number;
855
- mean: number;
856
- median: number;
857
- min: number;
858
- max: number;
859
- standardDeviation: number;
860
- variance: number;
861
- }
862
- ```
488
+ | `isEmpty()` | `true` if no records |
489
+ | `first()` / `last()` | First or last record |
490
+ | `get(index)` | Record at index |
491
+ | `toArray()` | Plain array copy |
492
+ | `toJSON()` | JSON string |
493
+ | `cast<T>()` | Re-type the result |
863
494
 
864
495
  ---
865
496