@objectstack/client 2.0.0 → 2.0.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/README.md CHANGED
@@ -11,6 +11,8 @@ The official TypeScript client for ObjectStack.
11
11
  - **Batch Operations**: Efficient bulk create/update/delete with transaction support.
12
12
  - **View Storage**: Save, load, and share custom UI view configurations.
13
13
  - **Standardized Errors**: Machine-readable error codes with retry guidance.
14
+ - **Full Protocol Coverage**: Implements all 13 API namespaces defined in `@objectstack/spec`
15
+ - **95+ Methods**: Complete implementation of discovery, metadata, data, auth, workflow, realtime, AI, and more.
14
16
 
15
17
  ## 🤖 AI Development Context
16
18
 
@@ -219,3 +221,130 @@ const data = await retryableRequest(() =>
219
221
  );
220
222
  ```
221
223
 
224
+ ## Protocol Compliance
225
+
226
+ The `@objectstack/client` SDK implements all 13 API namespaces defined in the `@objectstack/spec` protocol specification:
227
+
228
+ | Namespace | Purpose | Status |
229
+ |-----------|---------|:------:|
230
+ | `discovery` | API version & capabilities detection | ✅ |
231
+ | `meta` | Metadata operations (objects, plugins, etc.) | ✅ |
232
+ | `data` | CRUD & query operations | ✅ |
233
+ | `auth` | Authentication & user management | ✅ |
234
+ | `packages` | Plugin/package lifecycle management | ✅ |
235
+ | `views` | UI view definitions | ✅ |
236
+ | `workflow` | Workflow state transitions | ✅ |
237
+ | `analytics` | Analytics queries | ✅ |
238
+ | `automation` | Automation triggers | ✅ |
239
+ | `i18n` | Internationalization | ✅ |
240
+ | `notifications` | Push notifications | ✅ |
241
+ | `realtime` | WebSocket subscriptions | ✅ |
242
+ | `ai` | AI services (NLQ, chat, insights) | ✅ |
243
+
244
+ For detailed compliance verification, see [CLIENT_SPEC_COMPLIANCE.md](./CLIENT_SPEC_COMPLIANCE.md).
245
+
246
+ ## Available Namespaces
247
+
248
+ ### Complete API Coverage
249
+
250
+ ```typescript
251
+ const client = new ObjectStackClient({ baseUrl: 'http://localhost:3000' });
252
+ await client.connect();
253
+
254
+ // Discovery & Metadata
255
+ await client.meta.getTypes(); // List metadata types
256
+ await client.meta.getItems('object'); // List all objects
257
+ await client.meta.getItem('object', 'contact'); // Get specific object
258
+
259
+ // Data Operations
260
+ await client.data.find('contact', { filters: { status: 'active' } });
261
+ await client.data.create('contact', { name: 'John' });
262
+ await client.data.update('contact', id, { status: 'inactive' });
263
+ await client.data.delete('contact', id);
264
+ await client.data.batch('contact', batchRequest);
265
+
266
+ // Authentication
267
+ await client.auth.login({ email: 'user@example.com', password: 'pass' });
268
+ await client.auth.register({ email: 'new@example.com', password: 'pass' });
269
+ await client.auth.me();
270
+ await client.auth.logout();
271
+ await client.auth.refreshToken('refresh-token-string');
272
+
273
+ // Package Management
274
+ await client.packages.list();
275
+ await client.packages.install({
276
+ name: 'vendor_plugin',
277
+ label: 'Vendor Plugin',
278
+ version: '1.0.0',
279
+ });
280
+ await client.packages.enable('plugin-id');
281
+
282
+ // Permissions
283
+ await client.permissions.check({ object: 'contact', action: 'create' });
284
+ await client.permissions.getObjectPermissions('contact');
285
+ await client.permissions.getEffectivePermissions();
286
+
287
+ // Workflow
288
+ await client.workflow.getConfig('approval');
289
+ await client.workflow.getState('approval', recordId);
290
+ await client.workflow.transition({ object: 'approval', recordId, transition: 'submit' });
291
+ await client.workflow.approve({ object: 'approval', recordId });
292
+ await client.workflow.reject({ object: 'approval', recordId, reason: 'Incomplete' });
293
+
294
+ // Realtime
295
+ await client.realtime.connect({ protocol: 'websocket' });
296
+ await client.realtime.subscribe({ channel: 'contact', event: 'update' });
297
+ await client.realtime.setPresence({ status: 'online' });
298
+
299
+ // Notifications
300
+ await client.notifications.registerDevice({ token: 'device-token', platform: 'ios' });
301
+ await client.notifications.list({ unreadOnly: true });
302
+ await client.notifications.markRead(['notif-1', 'notif-2']);
303
+
304
+ // AI Services
305
+ await client.ai.nlq({ query: 'Show me all active contacts' });
306
+ await client.ai.chat({ message: 'Summarize this project', context: projectId });
307
+ await client.ai.suggest({ object: 'contact', field: 'industry' });
308
+ await client.ai.insights({ object: 'sales', recordId: dealId });
309
+
310
+ // Internationalization
311
+ await client.i18n.getLocales();
312
+ await client.i18n.getTranslations('zh-CN');
313
+ await client.i18n.getFieldLabels('contact', 'zh-CN');
314
+
315
+ // Analytics
316
+ await client.analytics.query({ object: 'sales', aggregations: ['sum:amount'] });
317
+ await client.analytics.meta('sales');
318
+
319
+ // Automation
320
+ await client.automation.trigger('send_welcome_email', { userId });
321
+
322
+ // File Storage
323
+ await client.storage.upload(fileData, 'user');
324
+ await client.storage.getDownloadUrl('file-123');
325
+ ```
326
+
327
+ ## Testing
328
+
329
+ ### Unit Tests
330
+
331
+ ```bash
332
+ pnpm test
333
+ ```
334
+
335
+ ### Integration Tests
336
+
337
+ **Note:** Integration tests require a running ObjectStack server. The server is provided by a separate repository and must be set up independently.
338
+
339
+ ```bash
340
+ # Start test server (in the ObjectStack server repository)
341
+ # Follow that project's documentation for test server setup
342
+ # Example: cd /path/to/objectstack-server && pnpm dev:test
343
+
344
+ # Run integration tests (in this repository)
345
+ cd packages/client
346
+ pnpm test:integration
347
+ ```
348
+
349
+ See [CLIENT_SERVER_INTEGRATION_TESTS.md](./CLIENT_SERVER_INTEGRATION_TESTS.md) for detailed test specifications.
350
+
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { FilterCondition, QueryAST, SortNode, AggregationNode } from '@objectstack/spec/data';
2
- import { GetDiscoveryResponse, StandardErrorCode, ErrorCategory, GetMetaTypesResponse, GetMetaItemsResponse, MetadataCacheRequest, MetadataCacheResponse, LoginRequest, SessionResponse, FileUploadResponse, BatchUpdateRequest, BatchUpdateResponse, BatchOptions } from '@objectstack/spec/api';
3
- export { BatchOperationResult, BatchOptions, BatchRecord, BatchUpdateRequest, BatchUpdateResponse, DeleteManyRequest, ErrorCategory, GetDiscoveryResponse, GetMetaItemsResponse, GetMetaTypesResponse, MetadataCacheRequest, MetadataCacheResponse, StandardErrorCode, UpdateManyRequest } from '@objectstack/spec/api';
2
+ import { GetDiscoveryResponse, StandardErrorCode, ErrorCategory, GetMetaTypesResponse, GetMetaItemsResponse, MetadataCacheRequest, MetadataCacheResponse, LoginRequest, SessionResponse, RegisterRequest, FileUploadResponse, CheckPermissionRequest, CheckPermissionResponse, GetObjectPermissionsResponse, GetEffectivePermissionsResponse, RealtimeConnectRequest, RealtimeConnectResponse, RealtimeSubscribeRequest, RealtimeSubscribeResponse, SetPresenceRequest, GetPresenceResponse, GetWorkflowConfigResponse, GetWorkflowStateResponse, WorkflowTransitionRequest, WorkflowTransitionResponse, WorkflowApproveRequest, WorkflowApproveResponse, WorkflowRejectRequest, WorkflowRejectResponse, ListViewsResponse, GetViewResponse, CreateViewRequest, CreateViewResponse, UpdateViewRequest, UpdateViewResponse, DeleteViewResponse, RegisterDeviceRequest, RegisterDeviceResponse, UnregisterDeviceResponse, GetNotificationPreferencesResponse, UpdateNotificationPreferencesRequest, UpdateNotificationPreferencesResponse, ListNotificationsResponse, MarkNotificationsReadResponse, MarkAllNotificationsReadResponse, AiNlqRequest, AiNlqResponse, AiChatRequest, AiChatResponse, AiSuggestRequest, AiSuggestResponse, AiInsightsRequest, AiInsightsResponse, GetLocalesResponse, GetTranslationsResponse, GetFieldLabelsResponse, BatchUpdateRequest, BatchUpdateResponse, BatchOptions } from '@objectstack/spec/api';
3
+ export { AiChatRequest, AiChatResponse, AiInsightsRequest, AiInsightsResponse, AiNlqRequest, AiNlqResponse, AiSuggestRequest, AiSuggestResponse, BatchOperationResult, BatchOptions, BatchRecord, BatchUpdateRequest, BatchUpdateResponse, CheckPermissionRequest, CheckPermissionResponse, CreateViewResponse, DeleteManyRequest, DeleteViewResponse, ErrorCategory, GetDiscoveryResponse, GetEffectivePermissionsResponse, GetFieldLabelsResponse, GetLocalesResponse, GetMetaItemsResponse, GetMetaTypesResponse, GetObjectPermissionsResponse, GetPresenceResponse, GetTranslationsResponse, GetViewResponse, GetWorkflowConfigResponse, GetWorkflowStateResponse, ListNotificationsResponse, ListViewsResponse, MetadataCacheRequest, MetadataCacheResponse, RealtimeConnectRequest, RealtimeConnectResponse, RealtimeSubscribeRequest, RealtimeSubscribeResponse, RefreshTokenRequest, RegisterDeviceRequest, RegisterDeviceResponse, RegisterRequest, StandardErrorCode, UpdateManyRequest, UpdateViewResponse, WorkflowApproveRequest, WorkflowApproveResponse, WorkflowRejectRequest, WorkflowRejectResponse, WorkflowTransitionRequest, WorkflowTransitionResponse } from '@objectstack/spec/api';
4
4
  import { Logger } from '@objectstack/core';
5
5
 
6
6
  /**
@@ -62,6 +62,26 @@ declare class FilterBuilder<T = any> {
62
62
  * IS NOT NULL filter: field IS NOT NULL
63
63
  */
64
64
  isNotNull<K extends keyof T>(field: K): this;
65
+ /**
66
+ * BETWEEN filter: field BETWEEN min AND max
67
+ */
68
+ between<K extends keyof T>(field: K, min: T[K], max: T[K]): this;
69
+ /**
70
+ * CONTAINS filter: field contains value (case-insensitive LIKE %value%)
71
+ */
72
+ contains<K extends keyof T>(field: K, value: string): this;
73
+ /**
74
+ * STARTS WITH filter: field starts with value (LIKE value%)
75
+ */
76
+ startsWith<K extends keyof T>(field: K, value: string): this;
77
+ /**
78
+ * ENDS WITH filter: field ends with value (LIKE %value)
79
+ */
80
+ endsWith<K extends keyof T>(field: K, value: string): this;
81
+ /**
82
+ * EXISTS filter: field is not null (alias for isNotNull)
83
+ */
84
+ exists<K extends keyof T>(field: K): this;
65
85
  /**
66
86
  * Build the filter condition
67
87
  */
@@ -110,6 +130,25 @@ declare class QueryBuilder<T = any> {
110
130
  * Group by fields
111
131
  */
112
132
  groupBy<K extends keyof T>(...fields: K[]): this;
133
+ /**
134
+ * Expand (eager-load) a related object with an optional sub-query
135
+ */
136
+ expand(relation: string, subQuery?: Partial<QueryAST>): this;
137
+ /**
138
+ * Add full-text search
139
+ */
140
+ search(query: string, options?: {
141
+ fields?: string[];
142
+ fuzzy?: boolean;
143
+ }): this;
144
+ /**
145
+ * Set cursor for keyset pagination
146
+ */
147
+ cursor(cursor: Record<string, any>): this;
148
+ /**
149
+ * Enable SELECT DISTINCT
150
+ */
151
+ distinct(): this;
113
152
  /**
114
153
  * Build the final query AST
115
154
  */
@@ -218,33 +257,31 @@ declare class ObjectStackClient {
218
257
  version: string;
219
258
  apiName: string;
220
259
  capabilities?: {
260
+ graphql: boolean;
221
261
  search: boolean;
262
+ websockets: boolean;
222
263
  files: boolean;
223
- graphql: boolean;
224
- notifications: boolean;
225
264
  analytics: boolean;
226
- hub: boolean;
227
265
  ai: boolean;
228
- i18n: boolean;
229
266
  workflow: boolean;
230
- websockets: boolean;
267
+ notifications: boolean;
268
+ i18n: boolean;
231
269
  } | undefined;
232
270
  endpoints?: {
233
271
  data: string;
234
272
  metadata: string;
235
- auth: string;
236
- graphql?: string | undefined;
237
- notifications?: string | undefined;
238
- storage?: string | undefined;
273
+ ui?: string | undefined;
274
+ auth?: string | undefined;
239
275
  automation?: string | undefined;
276
+ storage?: string | undefined;
240
277
  analytics?: string | undefined;
241
- hub?: string | undefined;
278
+ graphql?: string | undefined;
279
+ packages?: string | undefined;
280
+ workflow?: string | undefined;
242
281
  realtime?: string | undefined;
282
+ notifications?: string | undefined;
243
283
  ai?: string | undefined;
244
284
  i18n?: string | undefined;
245
- ui?: string | undefined;
246
- workflow?: string | undefined;
247
- packages?: string | undefined;
248
285
  } | undefined;
249
286
  }>;
250
287
  /**
@@ -298,18 +335,6 @@ declare class ObjectStackClient {
298
335
  meta: (cube: string) => Promise<any>;
299
336
  explain: (payload: any) => Promise<any>;
300
337
  };
301
- /**
302
- * Hub Management Services
303
- */
304
- hub: {
305
- spaces: {
306
- list: () => Promise<any>;
307
- create: (payload: any) => Promise<any>;
308
- };
309
- plugins: {
310
- install: (pkg: string, version?: string) => Promise<any>;
311
- };
312
- };
313
338
  /**
314
339
  * Package Management Services
315
340
  *
@@ -384,6 +409,14 @@ declare class ObjectStackClient {
384
409
  login: (request: LoginRequest) => Promise<SessionResponse>;
385
410
  logout: () => Promise<void>;
386
411
  me: () => Promise<SessionResponse>;
412
+ /**
413
+ * Register a new user account
414
+ */
415
+ register: (request: RegisterRequest) => Promise<SessionResponse>;
416
+ /**
417
+ * Refresh an authentication token
418
+ */
419
+ refreshToken: (refreshToken: string) => Promise<SessionResponse>;
387
420
  };
388
421
  /**
389
422
  * Storage Services
@@ -398,6 +431,181 @@ declare class ObjectStackClient {
398
431
  automation: {
399
432
  trigger: (triggerName: string, payload: any) => Promise<any>;
400
433
  };
434
+ /**
435
+ * Permissions Services
436
+ */
437
+ permissions: {
438
+ /**
439
+ * Check if current user has permission for an action on an object
440
+ */
441
+ check: (request: CheckPermissionRequest) => Promise<CheckPermissionResponse>;
442
+ /**
443
+ * Get all permissions for a specific object
444
+ */
445
+ getObjectPermissions: (object: string) => Promise<GetObjectPermissionsResponse>;
446
+ /**
447
+ * Get effective permissions for the current user
448
+ */
449
+ getEffectivePermissions: () => Promise<GetEffectivePermissionsResponse>;
450
+ };
451
+ /**
452
+ * Realtime Services
453
+ */
454
+ realtime: {
455
+ /**
456
+ * Establish a realtime connection
457
+ */
458
+ connect: (request?: RealtimeConnectRequest) => Promise<RealtimeConnectResponse>;
459
+ /**
460
+ * Disconnect from realtime services
461
+ */
462
+ disconnect: () => Promise<void>;
463
+ /**
464
+ * Subscribe to a channel
465
+ */
466
+ subscribe: (request: RealtimeSubscribeRequest) => Promise<RealtimeSubscribeResponse>;
467
+ /**
468
+ * Unsubscribe from a channel
469
+ */
470
+ unsubscribe: (subscriptionId: string) => Promise<void>;
471
+ /**
472
+ * Set presence state on a channel
473
+ */
474
+ setPresence: (channel: string, state: SetPresenceRequest["state"]) => Promise<void>;
475
+ /**
476
+ * Get presence information for a channel
477
+ */
478
+ getPresence: (channel: string) => Promise<GetPresenceResponse>;
479
+ };
480
+ /**
481
+ * Workflow Services
482
+ */
483
+ workflow: {
484
+ /**
485
+ * Get workflow configuration for an object
486
+ */
487
+ getConfig: (object: string) => Promise<GetWorkflowConfigResponse>;
488
+ /**
489
+ * Get current workflow state for a record
490
+ */
491
+ getState: (object: string, recordId: string) => Promise<GetWorkflowStateResponse>;
492
+ /**
493
+ * Execute a workflow state transition
494
+ */
495
+ transition: (request: WorkflowTransitionRequest) => Promise<WorkflowTransitionResponse>;
496
+ /**
497
+ * Approve a workflow step
498
+ */
499
+ approve: (request: WorkflowApproveRequest) => Promise<WorkflowApproveResponse>;
500
+ /**
501
+ * Reject a workflow step
502
+ */
503
+ reject: (request: WorkflowRejectRequest) => Promise<WorkflowRejectResponse>;
504
+ };
505
+ /**
506
+ * Views CRUD Services
507
+ */
508
+ views: {
509
+ /**
510
+ * List views for an object
511
+ */
512
+ list: (object: string, type?: "list" | "form") => Promise<ListViewsResponse>;
513
+ /**
514
+ * Get a specific view
515
+ */
516
+ get: (object: string, viewId: string) => Promise<GetViewResponse>;
517
+ /**
518
+ * Create a new view
519
+ */
520
+ create: (object: string, data: CreateViewRequest["data"]) => Promise<CreateViewResponse>;
521
+ /**
522
+ * Update an existing view
523
+ */
524
+ update: (object: string, viewId: string, data: UpdateViewRequest["data"]) => Promise<UpdateViewResponse>;
525
+ /**
526
+ * Delete a view
527
+ */
528
+ delete: (object: string, viewId: string) => Promise<DeleteViewResponse>;
529
+ };
530
+ /**
531
+ * Notification Services
532
+ */
533
+ notifications: {
534
+ /**
535
+ * Register a device for push notifications
536
+ */
537
+ registerDevice: (request: RegisterDeviceRequest) => Promise<RegisterDeviceResponse>;
538
+ /**
539
+ * Unregister a device from push notifications
540
+ */
541
+ unregisterDevice: (deviceId: string) => Promise<UnregisterDeviceResponse>;
542
+ /**
543
+ * Get notification preferences for the current user
544
+ */
545
+ getPreferences: () => Promise<GetNotificationPreferencesResponse>;
546
+ /**
547
+ * Update notification preferences
548
+ */
549
+ updatePreferences: (preferences: UpdateNotificationPreferencesRequest["preferences"]) => Promise<UpdateNotificationPreferencesResponse>;
550
+ /**
551
+ * List notifications for the current user
552
+ */
553
+ list: (options?: {
554
+ read?: boolean;
555
+ type?: string;
556
+ limit?: number;
557
+ cursor?: string;
558
+ }) => Promise<ListNotificationsResponse>;
559
+ /**
560
+ * Mark specific notifications as read
561
+ */
562
+ markRead: (ids: string[]) => Promise<MarkNotificationsReadResponse>;
563
+ /**
564
+ * Mark all notifications as read
565
+ */
566
+ markAllRead: () => Promise<MarkAllNotificationsReadResponse>;
567
+ };
568
+ /**
569
+ * AI Services
570
+ */
571
+ ai: {
572
+ /**
573
+ * Natural language query — converts natural language to structured query
574
+ */
575
+ nlq: (request: AiNlqRequest) => Promise<AiNlqResponse>;
576
+ /**
577
+ * Multi-turn AI chat
578
+ */
579
+ chat: (request: AiChatRequest) => Promise<AiChatResponse>;
580
+ /**
581
+ * AI-powered field value suggestions
582
+ */
583
+ suggest: (request: AiSuggestRequest) => Promise<AiSuggestResponse>;
584
+ /**
585
+ * AI-powered data insights
586
+ */
587
+ insights: (request: AiInsightsRequest) => Promise<AiInsightsResponse>;
588
+ };
589
+ /**
590
+ * Internationalization Services
591
+ */
592
+ i18n: {
593
+ /**
594
+ * Get available locales
595
+ */
596
+ getLocales: () => Promise<GetLocalesResponse>;
597
+ /**
598
+ * Get translations for a locale
599
+ */
600
+ getTranslations: (locale: string, options?: {
601
+ namespace?: string;
602
+ keys?: string[];
603
+ }) => Promise<GetTranslationsResponse>;
604
+ /**
605
+ * Get translated field labels for an object
606
+ */
607
+ getFieldLabels: (object: string, locale: string) => Promise<GetFieldLabelsResponse>;
608
+ };
401
609
  /**
402
610
  * Data Operations
403
611
  */