@objectql/sdk 1.8.1 β†’ 1.8.2

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,4 +1,33 @@
1
- # @objectql/driver-remote
1
+ # @objectql/sdk
2
+
3
+ ## 1.8.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Patch release v1.8.2 - Small version update with latest improvements
8
+ - **Enhanced Browser Compatibility:**
9
+ - Added comprehensive README with browser usage examples
10
+ - Added `browser` field to package.json for better bundler support
11
+ - Added `sideEffects: false` for tree-shaking optimization
12
+ - Added repository, bugs, and homepage metadata
13
+ - Expanded keywords for better discoverability (frontend, react, vue, angular, esm)
14
+ - **Built-in polyfill for `AbortSignal.timeout()`** - SDK now works in older browsers automatically
15
+ - Improved timeout handling with fallback mechanism for universal compatibility
16
+ - **Documentation Improvements:**
17
+ - Added detailed browser compatibility notes (now works in Chrome 90+, Firefox 90+, Safari 15+)
18
+ - Removed need for manual polyfills - SDK includes automatic polyfill
19
+ - Added React and Vue.js hook examples
20
+ - Added browser example HTML file with interactive demo
21
+ - Added Node.js example demonstrating cross-runtime compatibility
22
+ - Added package-level documentation comments
23
+ - Updated examples to use CDN imports for browser usage
24
+ - **Examples:**
25
+ - Added `examples/browser/` with standalone HTML example
26
+ - Added `examples/browser/example-node.ts` for Node.js usage
27
+ - Demonstrates browser usage without build tools
28
+ - Shows SDK works identically across all JavaScript runtimes
29
+ - Updated dependencies
30
+ - @objectql/types@1.8.2
2
31
 
3
32
  ## 1.8.1
4
33
 
package/README.md ADDED
@@ -0,0 +1,499 @@
1
+ # @objectql/sdk
2
+
3
+ > **Remote HTTP Driver for ObjectQL** - Universal client for browser, Node.js, and edge runtimes
4
+
5
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](../../LICENSE)
6
+ [![TypeScript](https://img.shields.io/badge/written%20in-TypeScript-3178C6.svg)](https://www.typescriptlang.org/)
7
+
8
+ The `@objectql/sdk` package provides a type-safe HTTP client for ObjectQL servers. It works seamlessly in browsers, Node.js, Deno, and edge runtimes like Cloudflare Workers.
9
+
10
+ ---
11
+
12
+ ## ✨ Features
13
+
14
+ * **🌍 Universal Runtime** - Works in browsers, Node.js, Deno, and edge environments
15
+ * **πŸ“¦ Zero Dependencies** - Only depends on `@objectql/types` for type definitions
16
+ * **πŸ”’ Type-Safe** - Full TypeScript support with generics
17
+ * **πŸš€ Modern APIs** - Uses native `fetch` API available in all modern JavaScript runtimes
18
+ * **🎯 RESTful Interface** - Clean, predictable API design
19
+
20
+ ---
21
+
22
+ ## πŸ“¦ Installation
23
+
24
+ ```bash
25
+ npm install @objectql/sdk @objectql/types
26
+ ```
27
+
28
+ For frontend projects:
29
+ ```bash
30
+ # Using npm
31
+ npm install @objectql/sdk @objectql/types
32
+
33
+ # Using yarn
34
+ yarn add @objectql/sdk @objectql/types
35
+
36
+ # Using pnpm
37
+ pnpm add @objectql/sdk @objectql/types
38
+ ```
39
+
40
+ ---
41
+
42
+ ## πŸš€ Quick Start
43
+
44
+ ### Browser Usage (ES Modules)
45
+
46
+ ```html
47
+ <!DOCTYPE html>
48
+ <html>
49
+ <head>
50
+ <title>ObjectQL SDK Browser Example</title>
51
+ </head>
52
+ <body>
53
+ <h1>ObjectQL Browser Client</h1>
54
+ <div id="users"></div>
55
+
56
+ <script type="module">
57
+ // Option 1: Using unpkg CDN
58
+ import { DataApiClient } from 'https://unpkg.com/@objectql/sdk/dist/index.js';
59
+
60
+ // Option 2: Using a bundler (Vite, Webpack, etc.)
61
+ // import { DataApiClient } from '@objectql/sdk';
62
+
63
+ const client = new DataApiClient({
64
+ baseUrl: 'http://localhost:3000',
65
+ token: 'your-auth-token' // Optional
66
+ });
67
+
68
+ // Fetch and display users
69
+ async function loadUsers() {
70
+ const response = await client.list('users', {
71
+ filter: [['status', '=', 'active']],
72
+ limit: 10
73
+ });
74
+
75
+ const usersDiv = document.getElementById('users');
76
+ usersDiv.innerHTML = response.items
77
+ .map(user => `<p>${user.name} - ${user.email}</p>`)
78
+ .join('');
79
+ }
80
+
81
+ loadUsers().catch(console.error);
82
+ </script>
83
+ </body>
84
+ </html>
85
+ ```
86
+
87
+ ### React / Vue / Angular
88
+
89
+ ```typescript
90
+ import { DataApiClient, MetadataApiClient } from '@objectql/sdk';
91
+
92
+ // Initialize clients
93
+ const dataClient = new DataApiClient({
94
+ baseUrl: process.env.REACT_APP_API_URL || 'http://localhost:3000',
95
+ token: localStorage.getItem('auth_token')
96
+ });
97
+
98
+ const metadataClient = new MetadataApiClient({
99
+ baseUrl: process.env.REACT_APP_API_URL || 'http://localhost:3000'
100
+ });
101
+
102
+ // Use in your components
103
+ async function fetchUsers() {
104
+ const response = await dataClient.list('users', {
105
+ filter: [['status', '=', 'active']],
106
+ sort: [['created_at', 'desc']]
107
+ });
108
+ return response.items;
109
+ }
110
+ ```
111
+
112
+ ### Node.js
113
+
114
+ ```javascript
115
+ const { DataApiClient } = require('@objectql/sdk');
116
+
117
+ const client = new DataApiClient({
118
+ baseUrl: 'http://localhost:3000'
119
+ });
120
+
121
+ async function main() {
122
+ const users = await client.list('users');
123
+ console.log(users.items);
124
+ }
125
+
126
+ main();
127
+ ```
128
+
129
+ ---
130
+
131
+ ## πŸ“š API Reference
132
+
133
+ ### DataApiClient
134
+
135
+ Client for CRUD operations on data records.
136
+
137
+ #### Constructor
138
+
139
+ ```typescript
140
+ new DataApiClient(config: DataApiClientConfig)
141
+ ```
142
+
143
+ **Config Options:**
144
+ * `baseUrl` (string, required) - Base URL of the ObjectQL server
145
+ * `token` (string, optional) - Authentication token
146
+ * `headers` (Record<string, string>, optional) - Additional HTTP headers
147
+ * `timeout` (number, optional) - Request timeout in milliseconds (default: 30000)
148
+
149
+ #### Methods
150
+
151
+ ##### `list<T>(objectName: string, params?: DataApiListParams): Promise<DataApiListResponse<T>>`
152
+
153
+ List records with optional filtering, sorting, and pagination.
154
+
155
+ ```typescript
156
+ const users = await client.list('users', {
157
+ filter: [['status', '=', 'active']],
158
+ sort: [['name', 'asc']],
159
+ limit: 20,
160
+ skip: 0,
161
+ fields: ['name', 'email', 'status']
162
+ });
163
+ ```
164
+
165
+ ##### `get<T>(objectName: string, id: string | number): Promise<DataApiItemResponse<T>>`
166
+
167
+ Get a single record by ID.
168
+
169
+ ```typescript
170
+ const user = await client.get('users', 'user_123');
171
+ ```
172
+
173
+ ##### `create<T>(objectName: string, data: DataApiCreateRequest): Promise<DataApiItemResponse<T>>`
174
+
175
+ Create a new record.
176
+
177
+ ```typescript
178
+ const newUser = await client.create('users', {
179
+ name: 'Alice',
180
+ email: 'alice@example.com',
181
+ status: 'active'
182
+ });
183
+ ```
184
+
185
+ ##### `createMany<T>(objectName: string, data: DataApiCreateManyRequest): Promise<DataApiListResponse<T>>`
186
+
187
+ Create multiple records at once.
188
+
189
+ ```typescript
190
+ const newUsers = await client.createMany('users', [
191
+ { name: 'Bob', email: 'bob@example.com' },
192
+ { name: 'Charlie', email: 'charlie@example.com' }
193
+ ]);
194
+ ```
195
+
196
+ ##### `update<T>(objectName: string, id: string | number, data: DataApiUpdateRequest): Promise<DataApiItemResponse<T>>`
197
+
198
+ Update an existing record.
199
+
200
+ ```typescript
201
+ const updated = await client.update('users', 'user_123', {
202
+ status: 'inactive'
203
+ });
204
+ ```
205
+
206
+ ##### `updateMany(objectName: string, request: DataApiBulkUpdateRequest): Promise<DataApiResponse>`
207
+
208
+ Update multiple records matching filters.
209
+
210
+ ```typescript
211
+ await client.updateMany('users', {
212
+ filters: [['status', '=', 'pending']],
213
+ data: { status: 'active' }
214
+ });
215
+ ```
216
+
217
+ ##### `delete(objectName: string, id: string | number): Promise<DataApiDeleteResponse>`
218
+
219
+ Delete a record by ID.
220
+
221
+ ```typescript
222
+ await client.delete('users', 'user_123');
223
+ ```
224
+
225
+ ##### `deleteMany(objectName: string, request: DataApiBulkDeleteRequest): Promise<DataApiDeleteResponse>`
226
+
227
+ Delete multiple records matching filters.
228
+
229
+ ```typescript
230
+ await client.deleteMany('users', {
231
+ filters: [['created_at', '<', '2023-01-01']]
232
+ });
233
+ ```
234
+
235
+ ##### `count(objectName: string, filters?: FilterExpression): Promise<DataApiCountResponse>`
236
+
237
+ Count records matching filters.
238
+
239
+ ```typescript
240
+ const result = await client.count('users', [['status', '=', 'active']]);
241
+ console.log(result.count);
242
+ ```
243
+
244
+ ---
245
+
246
+ ### MetadataApiClient
247
+
248
+ Client for reading object schemas and metadata.
249
+
250
+ #### Constructor
251
+
252
+ ```typescript
253
+ new MetadataApiClient(config: MetadataApiClientConfig)
254
+ ```
255
+
256
+ #### Methods
257
+
258
+ ##### `listObjects(): Promise<MetadataApiObjectListResponse>`
259
+
260
+ List all available objects.
261
+
262
+ ```typescript
263
+ const objects = await metadataClient.listObjects();
264
+ ```
265
+
266
+ ##### `getObject(objectName: string): Promise<MetadataApiObjectDetailResponse>`
267
+
268
+ Get detailed schema for an object.
269
+
270
+ ```typescript
271
+ const userSchema = await metadataClient.getObject('users');
272
+ console.log(userSchema.fields);
273
+ ```
274
+
275
+ ##### `getField(objectName: string, fieldName: string): Promise<FieldMetadataResponse>`
276
+
277
+ Get metadata for a specific field.
278
+
279
+ ```typescript
280
+ const emailField = await metadataClient.getField('users', 'email');
281
+ ```
282
+
283
+ ##### `listActions(objectName: string): Promise<MetadataApiActionsResponse>`
284
+
285
+ List actions available for an object.
286
+
287
+ ```typescript
288
+ const actions = await metadataClient.listActions('users');
289
+ ```
290
+
291
+ ---
292
+
293
+ ## 🌐 Browser Compatibility
294
+
295
+ The SDK uses modern JavaScript APIs available in all current browsers:
296
+
297
+ * **fetch API** - Available in all modern browsers
298
+ * **Promises/async-await** - ES2017+
299
+ * **AbortSignal.timeout()** - Chrome 103+, Firefox 100+, Safari 16.4+
300
+
301
+ ### Automatic Polyfill
302
+
303
+ The SDK **automatically includes a polyfill** for `AbortSignal.timeout()` that activates when running in older browsers. You don't need to add any polyfills manually - the SDK works universally out of the box!
304
+
305
+ The polyfill is lightweight and only adds the missing functionality when needed, ensuring compatibility with:
306
+ - Chrome 90+
307
+ - Firefox 90+
308
+ - Safari 15+
309
+ - Edge 90+
310
+
311
+ For even older browsers, you may need to add polyfills for:
312
+ - `fetch` API (via `whatwg-fetch`)
313
+ - `AbortController` (via `abort-controller` package)
314
+
315
+ ---
316
+
317
+ ## πŸ”§ Advanced Usage
318
+
319
+ ### Custom Headers
320
+
321
+ ```typescript
322
+ const client = new DataApiClient({
323
+ baseUrl: 'http://localhost:3000',
324
+ headers: {
325
+ 'X-Custom-Header': 'value',
326
+ 'X-Request-ID': crypto.randomUUID()
327
+ }
328
+ });
329
+ ```
330
+
331
+ ### Dynamic Token Updates
332
+
333
+ ```typescript
334
+ class AuthenticatedClient {
335
+ private client: DataApiClient;
336
+
337
+ constructor(baseUrl: string) {
338
+ this.client = new DataApiClient({ baseUrl });
339
+ }
340
+
341
+ setToken(token: string) {
342
+ this.client = new DataApiClient({
343
+ baseUrl: this.client['baseUrl'],
344
+ token
345
+ });
346
+ }
347
+
348
+ async fetchData() {
349
+ return this.client.list('users');
350
+ }
351
+ }
352
+ ```
353
+
354
+ ### Error Handling
355
+
356
+ ```typescript
357
+ import { ObjectQLError, ApiErrorCode } from '@objectql/types';
358
+
359
+ try {
360
+ await client.create('users', { email: 'invalid' });
361
+ } catch (error) {
362
+ if (error instanceof ObjectQLError) {
363
+ console.error('ObjectQL Error:', error.code, error.message);
364
+
365
+ if (error.code === ApiErrorCode.VALIDATION_ERROR) {
366
+ console.log('Validation failed:', error.details);
367
+ }
368
+ }
369
+ }
370
+ ```
371
+
372
+ ---
373
+
374
+ ## πŸ“– Examples
375
+
376
+ ### React Hook
377
+
378
+ ```typescript
379
+ import { useState, useEffect } from 'react';
380
+ import { DataApiClient } from '@objectql/sdk';
381
+
382
+ const client = new DataApiClient({
383
+ baseUrl: process.env.REACT_APP_API_URL
384
+ });
385
+
386
+ export function useObjectData<T>(objectName: string, params?: any) {
387
+ const [data, setData] = useState<T[]>([]);
388
+ const [loading, setLoading] = useState(true);
389
+ const [error, setError] = useState<Error | null>(null);
390
+
391
+ useEffect(() => {
392
+ async function fetchData() {
393
+ try {
394
+ setLoading(true);
395
+ const response = await client.list<T>(objectName, params);
396
+ setData(response.items || []);
397
+ } catch (err) {
398
+ setError(err as Error);
399
+ } finally {
400
+ setLoading(false);
401
+ }
402
+ }
403
+
404
+ fetchData();
405
+ }, [objectName, JSON.stringify(params)]);
406
+
407
+ return { data, loading, error };
408
+ }
409
+ ```
410
+
411
+ ### Vue Composable
412
+
413
+ ```typescript
414
+ import { ref, watchEffect } from 'vue';
415
+ import { DataApiClient } from '@objectql/sdk';
416
+
417
+ const client = new DataApiClient({
418
+ baseUrl: import.meta.env.VITE_API_URL
419
+ });
420
+
421
+ export function useObjectData<T>(objectName: string, params?: any) {
422
+ const data = ref<T[]>([]);
423
+ const loading = ref(true);
424
+ const error = ref<Error | null>(null);
425
+
426
+ watchEffect(async () => {
427
+ try {
428
+ loading.value = true;
429
+ const response = await client.list<T>(objectName, params);
430
+ data.value = response.items || [];
431
+ } catch (err) {
432
+ error.value = err as Error;
433
+ } finally {
434
+ loading.value = false;
435
+ }
436
+ });
437
+
438
+ return { data, loading, error };
439
+ }
440
+ ```
441
+
442
+ ---
443
+
444
+ ## πŸ—οΈ Architecture
445
+
446
+ The SDK is designed with the ObjectQL "Trinity" architecture:
447
+
448
+ 1. **@objectql/types** (The Contract) - Pure TypeScript interfaces
449
+ 2. **@objectql/sdk** (The Client) - HTTP communication layer
450
+ 3. **ObjectQL Server** (The Backend) - Data processing and storage
451
+
452
+ ```
453
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
454
+ β”‚ Frontend β”‚
455
+ β”‚ (Browser/App) β”‚
456
+ β”‚ β”‚
457
+ β”‚ @objectql/sdk β”‚
458
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
459
+ β”‚ HTTP/REST
460
+ β”‚
461
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”
462
+ β”‚ ObjectQL Server β”‚
463
+ β”‚ β”‚
464
+ β”‚ @objectql/core β”‚
465
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
466
+ β”‚
467
+ β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”
468
+ β”‚ SQL/Mongoβ”‚
469
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
470
+ ```
471
+
472
+ ---
473
+
474
+ ## πŸ“„ License
475
+
476
+ MIT License - see [LICENSE](../../LICENSE) file for details.
477
+
478
+ ---
479
+
480
+ ## πŸ”— Related Packages
481
+
482
+ * **[@objectql/types](../../foundation/types)** - TypeScript type definitions
483
+ * **[@objectql/core](../../foundation/core)** - Core ObjectQL engine
484
+ * **[@objectql/server](../../runtime/server)** - HTTP server implementation
485
+
486
+ ---
487
+
488
+ ## 🀝 Contributing
489
+
490
+ We welcome contributions! Please see the main [repository README](../../../README.md) for guidelines.
491
+
492
+ ---
493
+
494
+ ## πŸ“š Documentation
495
+
496
+ For complete documentation, visit:
497
+ * [Client SDK Guide](../../../docs/api/client-sdk.md)
498
+ * [REST API Reference](../../../docs/api/rest.md)
499
+ * [ObjectQL Documentation](https://objectql.org)
package/dist/index.d.ts CHANGED
@@ -1,4 +1,23 @@
1
- import { Driver } from '@objectql/types';
1
+ /**
2
+ * @objectql/sdk - Universal HTTP Client for ObjectQL
3
+ *
4
+ * This package provides type-safe HTTP clients for ObjectQL servers.
5
+ * It works seamlessly in browsers, Node.js, Deno, and edge runtimes.
6
+ *
7
+ * ## Browser Compatibility
8
+ *
9
+ * The SDK uses modern JavaScript APIs:
10
+ * - fetch API (universal)
11
+ * - AbortSignal.timeout() (Chrome 103+, Firefox 100+, Safari 16.4+)
12
+ *
13
+ * For older browsers, a polyfill is automatically applied if AbortSignal.timeout is not available.
14
+ *
15
+ * @packageDocumentation
16
+ */
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';
18
+ /**
19
+ * Legacy Driver implementation that uses JSON-RPC style API
20
+ */
2
21
  export declare class RemoteDriver implements Driver {
3
22
  private baseUrl;
4
23
  constructor(baseUrl: string);
@@ -13,3 +32,85 @@ export declare class RemoteDriver implements Driver {
13
32
  updateMany(objectName: string, filters: any, data: any, options?: any): Promise<any>;
14
33
  deleteMany(objectName: string, filters: any, options?: any): Promise<any>;
15
34
  }
35
+ /**
36
+ * REST-based Data API Client
37
+ *
38
+ * Implements type-safe client for ObjectQL Data API endpoints.
39
+ * Uses the RESTful interface described in docs/api/rest.md
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * const client = new DataApiClient({ baseUrl: 'http://localhost:3000' });
44
+ *
45
+ * // List users
46
+ * const response = await client.list('users', {
47
+ * filter: [['status', '=', 'active']],
48
+ * sort: [['created_at', 'desc']],
49
+ * limit: 20
50
+ * });
51
+ *
52
+ * // Get single user
53
+ * const user = await client.get('users', 'user_123');
54
+ *
55
+ * // Create user
56
+ * const newUser = await client.create('users', {
57
+ * name: 'Alice',
58
+ * email: 'alice@example.com'
59
+ * });
60
+ * ```
61
+ */
62
+ export declare class DataApiClient implements IDataApiClient {
63
+ private baseUrl;
64
+ private token?;
65
+ private headers;
66
+ private timeout;
67
+ constructor(config: DataApiClientConfig);
68
+ private request;
69
+ list<T = unknown>(objectName: string, params?: DataApiListParams): Promise<DataApiListResponse<T>>;
70
+ get<T = unknown>(objectName: string, id: string | number): Promise<DataApiItemResponse<T>>;
71
+ create<T = unknown>(objectName: string, data: DataApiCreateRequest): Promise<DataApiItemResponse<T>>;
72
+ createMany<T = unknown>(objectName: string, data: DataApiCreateManyRequest): Promise<DataApiListResponse<T>>;
73
+ update<T = unknown>(objectName: string, id: string | number, data: DataApiUpdateRequest): Promise<DataApiItemResponse<T>>;
74
+ updateMany(objectName: string, request: DataApiBulkUpdateRequest): Promise<DataApiResponse>;
75
+ delete(objectName: string, id: string | number): Promise<DataApiDeleteResponse>;
76
+ deleteMany(objectName: string, request: DataApiBulkDeleteRequest): Promise<DataApiDeleteResponse>;
77
+ count(objectName: string, filters?: FilterExpression): Promise<DataApiCountResponse>;
78
+ }
79
+ /**
80
+ * REST-based Metadata API Client
81
+ *
82
+ * Implements type-safe client for ObjectQL Metadata API endpoints.
83
+ * Uses the interface described in docs/api/metadata.md
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * const client = new MetadataApiClient({ baseUrl: 'http://localhost:3000' });
88
+ *
89
+ * // List all objects
90
+ * const objects = await client.listObjects();
91
+ *
92
+ * // Get detailed object metadata
93
+ * const userSchema = await client.getObject('users');
94
+ * console.log(userSchema.fields);
95
+ *
96
+ * // Get field metadata
97
+ * const emailField = await client.getField('users', 'email');
98
+ *
99
+ * // List actions
100
+ * const actions = await client.listActions('users');
101
+ * ```
102
+ */
103
+ export declare class MetadataApiClient implements IMetadataApiClient {
104
+ private baseUrl;
105
+ private token?;
106
+ private headers;
107
+ private timeout;
108
+ constructor(config: MetadataApiClientConfig);
109
+ private request;
110
+ listObjects(): Promise<MetadataApiObjectListResponse>;
111
+ getObject(objectName: string): Promise<MetadataApiObjectDetailResponse>;
112
+ getField(objectName: string, fieldName: string): Promise<FieldMetadataResponse>;
113
+ listActions(objectName: string): Promise<MetadataApiActionsResponse>;
114
+ listByType<T = unknown>(metadataType: string): Promise<MetadataApiListResponse<T>>;
115
+ getMetadata<T = unknown>(metadataType: string, id: string): Promise<MetadataApiResponse<T>>;
116
+ }