@objectql/sdk 3.0.1 → 4.0.1

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @objectql/sdk
2
2
 
3
+ ## 4.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - **Release Version 4.0.1**
8
+
9
+ This patch release includes the latest repository improvements and infrastructure updates:
10
+ - Added comprehensive GitHub workflows for CI/CD, testing, and quality assurance
11
+ - Enhanced documentation and developer experience
12
+ - Improved build and release processes with Changesets
13
+ - Added Excel driver for reading/writing Excel files as data sources
14
+ - Repository structure and tooling improvements
15
+ - Bug fixes and stability enhancements
16
+
17
+ - Updated dependencies
18
+ - @objectql/types@4.0.1
19
+
3
20
  ## 3.0.1
4
21
 
5
22
  ### Patch Changes
@@ -7,7 +24,6 @@
7
24
  - 79d04e1: Patch release for January 2026 updates
8
25
 
9
26
  This patch includes minor improvements and maintenance updates:
10
-
11
27
  - Enhanced type safety across core packages
12
28
  - Improved error handling in drivers
13
29
  - Documentation updates
@@ -30,7 +46,6 @@
30
46
  This is a coordinated major release that unifies all ObjectQL packages to version 2.0.0, establishing a synchronized versioning strategy across the entire ecosystem.
31
47
 
32
48
  ### 🎯 Key Changes
33
-
34
49
  - **Unified Versioning**: All core packages now share the same version number (2.0.0)
35
50
  - **Fixed Group Management**: Updated changeset configuration to include all @objectql packages in the fixed versioning group
36
51
  - **Simplified Maintenance**: Future releases will automatically maintain version consistency across the entire monorepo
@@ -38,7 +53,6 @@
38
53
  ### 📦 Packages Included
39
54
 
40
55
  All ObjectQL packages are now synchronized at version 2.0.0:
41
-
42
56
  - Foundation: `@objectql/types`, `@objectql/core`, `@objectql/platform-node`
43
57
  - Drivers: `@objectql/driver-sql`, `@objectql/driver-mongo`, `@objectql/driver-redis`, `@objectql/driver-fs`, `@objectql/driver-memory`, `@objectql/driver-localstorage`, `@objectql/driver-excel`, `@objectql/sdk`
44
58
  - Runtime: `@objectql/server`
package/README.md CHANGED
@@ -12,10 +12,14 @@ The `@objectql/sdk` package provides a type-safe HTTP client for ObjectQL server
12
12
  ## ✨ Features
13
13
 
14
14
  * **🌍 Universal Runtime** - Works in browsers, Node.js, Deno, and edge environments
15
- * **📦 Zero Dependencies** - Only depends on `@objectql/types` for type definitions
15
+ * **📦 Zero Dependencies** - Only depends on `@objectql/types` and `@objectstack/spec` for type definitions
16
16
  * **🔒 Type-Safe** - Full TypeScript support with generics
17
17
  * **🚀 Modern APIs** - Uses native `fetch` API available in all modern JavaScript runtimes
18
18
  * **🎯 RESTful Interface** - Clean, predictable API design
19
+ * **✅ DriverInterface v4.0** - Fully compliant with ObjectStack protocol specification
20
+ * **🔐 Authentication** - Built-in support for Bearer tokens and API keys
21
+ * **🔄 Retry Logic** - Automatic retry with exponential backoff for network resilience
22
+ * **📊 Request Logging** - Optional request/response logging for debugging
19
23
 
20
24
  ---
21
25
 
@@ -290,6 +294,274 @@ const actions = await metadataClient.listActions('users');
290
294
 
291
295
  ---
292
296
 
297
+ ### RemoteDriver (DriverInterface v4.0)
298
+
299
+ Client for connecting to a remote ObjectQL server via HTTP. Implements the standard `DriverInterface` from `@objectstack/spec`.
300
+
301
+ #### Constructor
302
+
303
+ ```typescript
304
+ // Legacy constructor
305
+ new RemoteDriver(baseUrl: string, rpcPath?: string)
306
+
307
+ // New config-based constructor (recommended)
308
+ new RemoteDriver(config: SdkConfig)
309
+ ```
310
+
311
+ **Config Options:**
312
+ * `baseUrl` (string, required) - Base URL of the ObjectQL server
313
+ * `rpcPath` (string, optional) - JSON-RPC endpoint path (default: /api/objectql)
314
+ * `queryPath` (string, optional) - Query endpoint path (default: /api/query)
315
+ * `commandPath` (string, optional) - Command endpoint path (default: /api/command)
316
+ * `executePath` (string, optional) - Custom execute endpoint path (default: /api/execute)
317
+ * `token` (string, optional) - Authentication token (Bearer)
318
+ * `apiKey` (string, optional) - API key for authentication
319
+ * `headers` (Record<string, string>, optional) - Custom HTTP headers
320
+ * `timeout` (number, optional) - Request timeout in milliseconds (default: 30000)
321
+ * `enableRetry` (boolean, optional) - Enable automatic retry on failure (default: false)
322
+ * `maxRetries` (number, optional) - Maximum retry attempts (default: 3)
323
+ * `enableLogging` (boolean, optional) - Enable request/response logging (default: false)
324
+
325
+ #### Methods
326
+
327
+ ##### `executeQuery(ast: QueryAST, options?: any): Promise<{ value: any[]; count?: number }>`
328
+
329
+ Execute a query using QueryAST format (DriverInterface v4.0).
330
+
331
+ ```typescript
332
+ import { RemoteDriver } from '@objectql/sdk';
333
+
334
+ const driver = new RemoteDriver({
335
+ baseUrl: 'http://localhost:3000',
336
+ token: 'your-auth-token',
337
+ enableRetry: true,
338
+ maxRetries: 3
339
+ });
340
+
341
+ // Execute a QueryAST
342
+ const result = await driver.executeQuery({
343
+ object: 'users',
344
+ fields: ['name', 'email', 'status'],
345
+ filters: {
346
+ type: 'comparison',
347
+ field: 'status',
348
+ operator: '=',
349
+ value: 'active'
350
+ },
351
+ sort: [{ field: 'created_at', order: 'desc' }],
352
+ top: 10,
353
+ skip: 0
354
+ });
355
+
356
+ console.log(result.value); // Array of users
357
+ console.log(result.count); // Total count
358
+ ```
359
+
360
+ ##### `executeCommand(command: Command, options?: any): Promise<CommandResult>`
361
+
362
+ Execute a command for mutation operations (create, update, delete, bulk operations).
363
+
364
+ ```typescript
365
+ // Create a record
366
+ const createResult = await driver.executeCommand({
367
+ type: 'create',
368
+ object: 'users',
369
+ data: {
370
+ name: 'Alice',
371
+ email: 'alice@example.com',
372
+ status: 'active'
373
+ }
374
+ });
375
+
376
+ console.log(createResult.success); // true
377
+ console.log(createResult.data); // Created record
378
+ console.log(createResult.affected); // 1
379
+
380
+ // Update a record
381
+ const updateResult = await driver.executeCommand({
382
+ type: 'update',
383
+ object: 'users',
384
+ id: 'user_123',
385
+ data: { status: 'inactive' }
386
+ });
387
+
388
+ // Delete a record
389
+ const deleteResult = await driver.executeCommand({
390
+ type: 'delete',
391
+ object: 'users',
392
+ id: 'user_123'
393
+ });
394
+
395
+ // Bulk create
396
+ const bulkCreateResult = await driver.executeCommand({
397
+ type: 'bulkCreate',
398
+ object: 'users',
399
+ records: [
400
+ { name: 'Bob', email: 'bob@example.com' },
401
+ { name: 'Charlie', email: 'charlie@example.com' }
402
+ ]
403
+ });
404
+
405
+ // Bulk update
406
+ const bulkUpdateResult = await driver.executeCommand({
407
+ type: 'bulkUpdate',
408
+ object: 'users',
409
+ updates: [
410
+ { id: 'user_1', data: { status: 'active' } },
411
+ { id: 'user_2', data: { status: 'inactive' } }
412
+ ]
413
+ });
414
+
415
+ // Bulk delete
416
+ const bulkDeleteResult = await driver.executeCommand({
417
+ type: 'bulkDelete',
418
+ object: 'users',
419
+ ids: ['user_1', 'user_2', 'user_3']
420
+ });
421
+ ```
422
+
423
+ ##### `execute(endpoint?: string, payload?: any, options?: any): Promise<any>`
424
+
425
+ Execute a custom operation on the remote server.
426
+
427
+ ```typescript
428
+ // Execute a custom workflow
429
+ const workflowResult = await driver.execute('/api/workflows/approve', {
430
+ workflowId: 'wf_123',
431
+ comment: 'Approved by manager'
432
+ });
433
+
434
+ // Use default execute endpoint
435
+ const customResult = await driver.execute(undefined, {
436
+ action: 'calculateMetrics',
437
+ params: { year: 2024, quarter: 'Q1' }
438
+ });
439
+
440
+ // Call a custom action
441
+ const actionResult = await driver.execute('/api/actions/send-email', {
442
+ to: 'user@example.com',
443
+ subject: 'Welcome',
444
+ body: 'Welcome to our platform!'
445
+ });
446
+ ```
447
+
448
+ #### Legacy CRUD Methods
449
+
450
+ The RemoteDriver also supports legacy CRUD methods for backward compatibility:
451
+
452
+ ```typescript
453
+ // Find records
454
+ const users = await driver.find('users', {
455
+ filters: [['status', '=', 'active']],
456
+ sort: [['name', 'asc']],
457
+ limit: 10
458
+ });
459
+
460
+ // Find one record
461
+ const user = await driver.findOne('users', 'user_123');
462
+
463
+ // Create a record
464
+ const newUser = await driver.create('users', {
465
+ name: 'Alice',
466
+ email: 'alice@example.com'
467
+ });
468
+
469
+ // Update a record
470
+ const updated = await driver.update('users', 'user_123', {
471
+ status: 'inactive'
472
+ });
473
+
474
+ // Delete a record
475
+ await driver.delete('users', 'user_123');
476
+
477
+ // Count records
478
+ const count = await driver.count('users', {
479
+ filters: [['status', '=', 'active']]
480
+ });
481
+ ```
482
+
483
+ ### Authentication Examples
484
+
485
+ ```typescript
486
+ // Bearer token authentication
487
+ const driverWithToken = new RemoteDriver({
488
+ baseUrl: 'http://localhost:3000',
489
+ token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
490
+ });
491
+
492
+ // API key authentication
493
+ const driverWithApiKey = new RemoteDriver({
494
+ baseUrl: 'http://localhost:3000',
495
+ apiKey: 'sk-1234567890abcdef'
496
+ });
497
+
498
+ // Both token and API key
499
+ const driverWithBoth = new RemoteDriver({
500
+ baseUrl: 'http://localhost:3000',
501
+ token: 'jwt-token',
502
+ apiKey: 'api-key'
503
+ });
504
+
505
+ // Custom headers
506
+ const driverWithHeaders = new RemoteDriver({
507
+ baseUrl: 'http://localhost:3000',
508
+ headers: {
509
+ 'X-Tenant-ID': 'tenant_123',
510
+ 'X-Request-ID': crypto.randomUUID()
511
+ }
512
+ });
513
+ ```
514
+
515
+ ### Error Handling and Retry
516
+
517
+ ```typescript
518
+ import { ObjectQLError, ApiErrorCode } from '@objectql/types';
519
+
520
+ // Enable retry with exponential backoff
521
+ const driver = new RemoteDriver({
522
+ baseUrl: 'http://localhost:3000',
523
+ enableRetry: true,
524
+ maxRetries: 3,
525
+ timeout: 10000
526
+ });
527
+
528
+ try {
529
+ const result = await driver.executeQuery({ object: 'users' });
530
+ } catch (error) {
531
+ if (error instanceof ObjectQLError) {
532
+ switch (error.code) {
533
+ case ApiErrorCode.UNAUTHORIZED:
534
+ console.error('Authentication required');
535
+ break;
536
+ case ApiErrorCode.VALIDATION_ERROR:
537
+ console.error('Validation failed:', error.details);
538
+ break;
539
+ case ApiErrorCode.NOT_FOUND:
540
+ console.error('Resource not found');
541
+ break;
542
+ default:
543
+ console.error('API error:', error.message);
544
+ }
545
+ }
546
+ }
547
+ ```
548
+
549
+ ### Request Logging
550
+
551
+ ```typescript
552
+ // Enable logging for debugging
553
+ const driver = new RemoteDriver({
554
+ baseUrl: 'http://localhost:3000',
555
+ enableLogging: true
556
+ });
557
+
558
+ // Logs will be printed to console:
559
+ // [RemoteDriver] executeQuery { endpoint: '...', ast: {...} }
560
+ // [RemoteDriver] executeQuery response { value: [...], count: 10 }
561
+ ```
562
+
563
+ ---
564
+
293
565
  ## 🌐 Browser Compatibility
294
566
 
295
567
  The SDK uses modern JavaScript APIs available in all current browsers:
package/dist/index.d.ts CHANGED
@@ -1,3 +1,12 @@
1
+ import { Data } from '@objectstack/spec';
2
+ type QueryAST = Data.QueryAST;
3
+ /**
4
+ * ObjectQL
5
+ * Copyright (c) 2026-present ObjectStack Inc.
6
+ *
7
+ * This source code is licensed under the MIT license found in the
8
+ * LICENSE file in the root directory of this source tree.
9
+ */
1
10
  /**
2
11
  * @objectql/sdk - Universal HTTP Client for ObjectQL
3
12
  *
@@ -14,15 +23,231 @@
14
23
  *
15
24
  * @packageDocumentation
16
25
  */
17
- import { Driver, IDataApiClient, IMetadataApiClient, DataApiClientConfig, MetadataApiClientConfig, DataApiListParams, DataApiListResponse, DataApiItemResponse, DataApiCreateRequest, DataApiCreateManyRequest, DataApiUpdateRequest, DataApiBulkUpdateRequest, DataApiBulkDeleteRequest, DataApiCountResponse, DataApiDeleteResponse, DataApiResponse, MetadataApiObjectListResponse, MetadataApiObjectDetailResponse, FieldMetadataResponse, MetadataApiActionsResponse, MetadataApiListResponse, MetadataApiResponse, FilterExpression } from '@objectql/types';
26
+ import { Driver, IDataApiClient, IMetadataApiClient, DataApiClientConfig, MetadataApiClientConfig, DataApiListParams, DataApiListResponse, DataApiItemResponse, DataApiCreateRequest, DataApiCreateManyRequest, DataApiUpdateRequest, DataApiBulkUpdateRequest, DataApiBulkDeleteRequest, DataApiCountResponse, DataApiDeleteResponse, DataApiResponse, MetadataApiObjectListResponse, MetadataApiObjectDetailResponse, FieldMetadataResponse, MetadataApiActionsResponse, MetadataApiListResponse, MetadataApiResponse, Filter } from '@objectql/types';
27
+ /**
28
+ * Command interface for executeCommand method
29
+ * Defines the structure of mutation commands sent to the remote API
30
+ */
31
+ export interface Command {
32
+ type: 'create' | 'update' | 'delete' | 'bulkCreate' | 'bulkUpdate' | 'bulkDelete';
33
+ object: string;
34
+ data?: any;
35
+ id?: string | number;
36
+ ids?: Array<string | number>;
37
+ records?: any[];
38
+ updates?: Array<{
39
+ id: string | number;
40
+ data: any;
41
+ }>;
42
+ options?: any;
43
+ }
44
+ /**
45
+ * Command result interface
46
+ * Standard response format for command execution
47
+ */
48
+ export interface CommandResult {
49
+ success: boolean;
50
+ data?: any;
51
+ affected: number;
52
+ error?: string;
53
+ }
54
+ /**
55
+ * SDK Configuration for the RemoteDriver
56
+ */
57
+ export interface SdkConfig {
58
+ /** Base URL of the remote ObjectQL server */
59
+ baseUrl: string;
60
+ /** RPC endpoint path (default: /api/objectql) */
61
+ rpcPath?: string;
62
+ /** Query endpoint path (default: /api/query) */
63
+ queryPath?: string;
64
+ /** Command endpoint path (default: /api/command) */
65
+ commandPath?: string;
66
+ /** Custom execute endpoint path (default: /api/execute) */
67
+ executePath?: string;
68
+ /** Authentication token */
69
+ token?: string;
70
+ /** API key for authentication */
71
+ apiKey?: string;
72
+ /** Custom headers */
73
+ headers?: Record<string, string>;
74
+ /** Request timeout in milliseconds (default: 30000) */
75
+ timeout?: number;
76
+ /** Enable retry on failure (default: false) */
77
+ enableRetry?: boolean;
78
+ /** Maximum number of retry attempts (default: 3) */
79
+ maxRetries?: number;
80
+ /** Enable request/response logging (default: false) */
81
+ enableLogging?: boolean;
82
+ }
18
83
  /**
19
84
  * Legacy Driver implementation that uses JSON-RPC style API
85
+ *
86
+ * Implements both the legacy Driver interface from @objectql/types and
87
+ * the standard DriverInterface from @objectstack/spec for compatibility
88
+ * with the new kernel-based plugin system.
89
+ *
90
+ * @version 4.0.0 - DriverInterface compliant
20
91
  */
21
92
  export declare class RemoteDriver implements Driver {
22
- private baseUrl;
93
+ readonly name = "RemoteDriver";
94
+ readonly version = "4.0.0";
95
+ readonly supports: {
96
+ transactions: boolean;
97
+ joins: boolean;
98
+ fullTextSearch: boolean;
99
+ jsonFields: boolean;
100
+ arrayFields: boolean;
101
+ queryFilters: boolean;
102
+ queryAggregations: boolean;
103
+ querySorting: boolean;
104
+ queryPagination: boolean;
105
+ queryWindowFunctions: boolean;
106
+ querySubqueries: boolean;
107
+ };
23
108
  private rpcPath;
24
- constructor(baseUrl: string, rpcPath?: string);
109
+ private queryPath;
110
+ private commandPath;
111
+ private executePath;
112
+ private baseUrl;
113
+ private token?;
114
+ private apiKey?;
115
+ private headers;
116
+ private timeout;
117
+ private enableRetry;
118
+ private maxRetries;
119
+ private enableLogging;
120
+ constructor(baseUrlOrConfig: string | SdkConfig, rpcPath?: string);
121
+ /**
122
+ * Build full endpoint URL
123
+ * @private
124
+ */
125
+ private buildEndpoint;
126
+ /**
127
+ * Get authentication headers
128
+ * @private
129
+ */
130
+ private getAuthHeaders;
131
+ /**
132
+ * Handle HTTP errors and convert to ObjectQLError
133
+ * @private
134
+ */
135
+ private handleHttpError;
136
+ /**
137
+ * Retry logic with exponential backoff
138
+ * @private
139
+ */
140
+ private retryWithBackoff;
141
+ /**
142
+ * Log request/response if logging is enabled
143
+ * @private
144
+ */
145
+ private log;
146
+ /**
147
+ * Connect to the remote server (for DriverInterface compatibility)
148
+ */
149
+ connect(): Promise<void>;
150
+ /**
151
+ * Check remote server connection health
152
+ */
153
+ checkHealth(): Promise<boolean>;
154
+ /**
155
+ * Execute a query using QueryAST format (DriverInterface v4.0)
156
+ *
157
+ * Sends a QueryAST to the remote server's /api/query endpoint
158
+ * and returns the query results.
159
+ *
160
+ * @param ast - The QueryAST to execute
161
+ * @param options - Optional execution options
162
+ * @returns Query result with value array and optional count
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * const result = await driver.executeQuery({
167
+ * object: 'users',
168
+ * fields: ['name', 'email'],
169
+ * filters: {
170
+ * type: 'comparison',
171
+ * field: 'status',
172
+ * operator: '=',
173
+ * value: 'active'
174
+ * },
175
+ * sort: [{ field: 'created_at', order: 'desc' }],
176
+ * top: 10
177
+ * });
178
+ * ```
179
+ */
180
+ executeQuery(ast: QueryAST, options?: any): Promise<{
181
+ value: any[];
182
+ count?: number;
183
+ }>;
184
+ /**
185
+ * Execute a command using Command format (DriverInterface v4.0)
186
+ *
187
+ * Sends a Command to the remote server's /api/command endpoint
188
+ * for mutation operations (create, update, delete, bulk operations).
189
+ *
190
+ * @param command - The command to execute
191
+ * @param options - Optional execution options
192
+ * @returns Command execution result
193
+ *
194
+ * @example
195
+ * ```typescript
196
+ * // Create a record
197
+ * const result = await driver.executeCommand({
198
+ * type: 'create',
199
+ * object: 'users',
200
+ * data: { name: 'Alice', email: 'alice@example.com' }
201
+ * });
202
+ *
203
+ * // Bulk update
204
+ * const bulkResult = await driver.executeCommand({
205
+ * type: 'bulkUpdate',
206
+ * object: 'users',
207
+ * updates: [
208
+ * { id: '1', data: { status: 'active' } },
209
+ * { id: '2', data: { status: 'inactive' } }
210
+ * ]
211
+ * });
212
+ * ```
213
+ */
214
+ executeCommand(command: Command, options?: any): Promise<CommandResult>;
215
+ /**
216
+ * Execute a custom operation on the remote server
217
+ *
218
+ * Allows calling custom HTTP endpoints with flexible parameters.
219
+ * Useful for custom actions, workflows, or specialized operations.
220
+ *
221
+ * @param endpoint - Optional custom endpoint path (defaults to /api/execute)
222
+ * @param payload - Request payload
223
+ * @param options - Optional execution options
224
+ * @returns Execution result
225
+ *
226
+ * @example
227
+ * ```typescript
228
+ * // Execute a custom workflow
229
+ * const result = await driver.execute('/api/workflows/approve', {
230
+ * workflowId: 'wf_123',
231
+ * comment: 'Approved'
232
+ * });
233
+ *
234
+ * // Use default execute endpoint
235
+ * const result = await driver.execute(undefined, {
236
+ * action: 'calculateMetrics',
237
+ * params: { year: 2024 }
238
+ * });
239
+ * ```
240
+ */
241
+ execute(endpoint?: string, payload?: any, options?: any): Promise<any>;
25
242
  private request;
243
+ /**
244
+ * Normalizes query format to support both legacy UnifiedQuery and QueryAST formats.
245
+ * This ensures backward compatibility while supporting the new @objectstack/spec interface.
246
+ *
247
+ * QueryAST format uses 'top' for limit, while UnifiedQuery uses 'limit'.
248
+ * QueryAST sort is array of {field, order}, while UnifiedQuery is array of [field, order].
249
+ */
250
+ private normalizeQuery;
26
251
  find(objectName: string, query: any, options?: any): Promise<any[]>;
27
252
  findOne(objectName: string, id: string | number, query?: any, options?: any): Promise<any>;
28
253
  create(objectName: string, data: any, options?: any): Promise<any>;
@@ -79,7 +304,7 @@ export declare class DataApiClient implements IDataApiClient {
79
304
  updateMany(objectName: string, request: DataApiBulkUpdateRequest): Promise<DataApiResponse>;
80
305
  delete(objectName: string, id: string | number): Promise<DataApiDeleteResponse>;
81
306
  deleteMany(objectName: string, request: DataApiBulkDeleteRequest): Promise<DataApiDeleteResponse>;
82
- count(objectName: string, filters?: FilterExpression): Promise<DataApiCountResponse>;
307
+ count(objectName: string, filters?: Filter): Promise<DataApiCountResponse>;
83
308
  }
84
309
  /**
85
310
  * REST-based Metadata API Client
@@ -123,3 +348,4 @@ export declare class MetadataApiClient implements IMetadataApiClient {
123
348
  listByType<T = unknown>(metadataType: string): Promise<MetadataApiListResponse<T>>;
124
349
  getMetadata<T = unknown>(metadataType: string, id: string): Promise<MetadataApiResponse<T>>;
125
350
  }
351
+ export {};