@objectstack/client 0.8.1 → 0.9.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @objectstack/client
2
2
 
3
+ ## 0.8.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [555e6a7]
8
+ - @objectstack/spec@0.8.2
9
+ - @objectstack/core@0.8.2
10
+
3
11
  ## 0.8.1
4
12
 
5
13
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { QueryAST, SortNode, AggregationNode } from '@objectstack/spec/data';
2
- import { BatchUpdateRequest, BatchUpdateResponse, BatchOptions, MetadataCacheRequest, MetadataCacheResponse, StandardErrorCode, ErrorCategory, CreateViewRequest, UpdateViewRequest, ListViewsRequest, ListViewsResponse, ViewResponse } from '@objectstack/spec/api';
2
+ import { BatchUpdateRequest, BatchUpdateResponse, BatchOptions, MetadataCacheRequest, MetadataCacheResponse, StandardErrorCode, ErrorCategory, GetDiscoveryResponse, GetMetaTypesResponse, GetMetaItemsResponse } from '@objectstack/spec/api';
3
3
  import { Logger } from '@objectstack/core';
4
4
  export interface ClientConfig {
5
5
  baseUrl: string;
@@ -17,16 +17,11 @@ export interface ClientConfig {
17
17
  */
18
18
  debug?: boolean;
19
19
  }
20
- export interface DiscoveryResult {
21
- routes: {
22
- discovery: string;
23
- metadata: string;
24
- data: string;
25
- auth: string;
26
- ui: string;
27
- };
28
- capabilities?: Record<string, boolean>;
29
- }
20
+ /**
21
+ * Discovery Result
22
+ * Re-export from @objectstack/spec/api for convenience
23
+ */
24
+ export type DiscoveryResult = GetDiscoveryResponse;
30
25
  export interface QueryOptions {
31
26
  select?: string[];
32
27
  filters?: Record<string, any>;
@@ -52,18 +47,44 @@ export declare class ObjectStackClient {
52
47
  private baseUrl;
53
48
  private token?;
54
49
  private fetchImpl;
55
- private routes?;
50
+ private discoveryInfo?;
56
51
  private logger;
57
52
  constructor(config: ClientConfig);
58
53
  /**
59
- * Initialize the client by discovering server capabilities and routes.
54
+ * Initialize the client by discovering server capabilities.
60
55
  */
61
- connect(): Promise<DiscoveryResult>;
56
+ connect(): Promise<{
57
+ version: string;
58
+ apiName: string;
59
+ capabilities?: string[] | undefined;
60
+ endpoints?: Record<string, string> | undefined;
61
+ }>;
62
62
  /**
63
63
  * Metadata Operations
64
64
  */
65
65
  meta: {
66
+ /**
67
+ * Get all available metadata types
68
+ * Returns types like 'object', 'plugin', 'view', etc.
69
+ */
70
+ getTypes: () => Promise<GetMetaTypesResponse>;
71
+ /**
72
+ * Get all items of a specific metadata type
73
+ * @param type - Metadata type name (e.g., 'object', 'plugin')
74
+ */
75
+ getItems: (type: string) => Promise<GetMetaItemsResponse>;
76
+ /**
77
+ * Get a specific object definition by name
78
+ * @deprecated Use `getItem('object', name)` instead for consistency with spec protocol
79
+ * @param name - Object name (snake_case identifier)
80
+ */
66
81
  getObject: (name: string) => Promise<any>;
82
+ /**
83
+ * Get a specific metadata item by type and name
84
+ * @param type - Metadata type (e.g., 'object', 'plugin')
85
+ * @param name - Item name (snake_case identifier)
86
+ */
87
+ getItem: (type: string, name: string) => Promise<any>;
67
88
  /**
68
89
  * Get object metadata with cache support
69
90
  * Supports ETag-based conditional requests for efficient caching
@@ -106,48 +127,16 @@ export declare class ObjectStackClient {
106
127
  */
107
128
  deleteMany: (object: string, ids: string[], options?: BatchOptions) => Promise<BatchUpdateResponse>;
108
129
  };
109
- /**
110
- * View Storage Operations
111
- * Save, load, and manage UI view configurations
112
- */
113
- views: {
114
- /**
115
- * Create a new saved view
116
- */
117
- create: (request: CreateViewRequest) => Promise<ViewResponse>;
118
- /**
119
- * Get a saved view by ID
120
- */
121
- get: (id: string) => Promise<ViewResponse>;
122
- /**
123
- * List saved views with optional filters
124
- */
125
- list: (request?: ListViewsRequest) => Promise<ListViewsResponse>;
126
- /**
127
- * Update an existing view
128
- */
129
- update: (request: UpdateViewRequest) => Promise<ViewResponse>;
130
- /**
131
- * Delete a saved view
132
- */
133
- delete: (id: string) => Promise<{
134
- success: boolean;
135
- }>;
136
- /**
137
- * Share a view with users/teams
138
- */
139
- share: (id: string, userIds: string[]) => Promise<ViewResponse>;
140
- /**
141
- * Set a view as default for an object
142
- */
143
- setDefault: (id: string, object: string) => Promise<ViewResponse>;
144
- };
145
130
  /**
146
131
  * Private Helpers
147
132
  */
148
133
  private isFilterAST;
149
134
  private fetch;
135
+ /**
136
+ * Get the conventional route path for a given API endpoint type
137
+ * ObjectStack uses standard conventions: /api/v1/data, /api/v1/meta, /api/v1/ui
138
+ */
150
139
  private getRoute;
151
140
  }
152
141
  export { QueryBuilder, FilterBuilder, createQuery, createFilter } from './query-builder';
153
- export type { BatchUpdateRequest, BatchUpdateResponse, UpdateManyRequest, DeleteManyRequest, BatchOptions, BatchRecord, BatchOperationResult, MetadataCacheRequest, MetadataCacheResponse, StandardErrorCode, ErrorCategory, CreateViewRequest, UpdateViewRequest, ListViewsRequest, SavedView, ViewResponse, ListViewsResponse, ViewType, ViewVisibility, ViewColumn, ViewLayout } from '@objectstack/spec/api';
142
+ export type { BatchUpdateRequest, BatchUpdateResponse, UpdateManyRequest, DeleteManyRequest, BatchOptions, BatchRecord, BatchOperationResult, MetadataCacheRequest, MetadataCacheResponse, StandardErrorCode, ErrorCategory, GetDiscoveryResponse, GetMetaTypesResponse, GetMetaItemsResponse } from '@objectstack/spec/api';
package/dist/index.js CHANGED
@@ -5,11 +5,44 @@ export class ObjectStackClient {
5
5
  * Metadata Operations
6
6
  */
7
7
  this.meta = {
8
+ /**
9
+ * Get all available metadata types
10
+ * Returns types like 'object', 'plugin', 'view', etc.
11
+ */
12
+ getTypes: async () => {
13
+ const route = this.getRoute('metadata');
14
+ const res = await this.fetch(`${this.baseUrl}${route}`);
15
+ return res.json();
16
+ },
17
+ /**
18
+ * Get all items of a specific metadata type
19
+ * @param type - Metadata type name (e.g., 'object', 'plugin')
20
+ */
21
+ getItems: async (type) => {
22
+ const route = this.getRoute('metadata');
23
+ const res = await this.fetch(`${this.baseUrl}${route}/${type}`);
24
+ return res.json();
25
+ },
26
+ /**
27
+ * Get a specific object definition by name
28
+ * @deprecated Use `getItem('object', name)` instead for consistency with spec protocol
29
+ * @param name - Object name (snake_case identifier)
30
+ */
8
31
  getObject: async (name) => {
9
32
  const route = this.getRoute('metadata');
10
33
  const res = await this.fetch(`${this.baseUrl}${route}/object/${name}`);
11
34
  return res.json();
12
35
  },
36
+ /**
37
+ * Get a specific metadata item by type and name
38
+ * @param type - Metadata type (e.g., 'object', 'plugin')
39
+ * @param name - Item name (snake_case identifier)
40
+ */
41
+ getItem: async (type, name) => {
42
+ const route = this.getRoute('metadata');
43
+ const res = await this.fetch(`${this.baseUrl}${route}/${type}/${name}`);
44
+ return res.json();
45
+ },
13
46
  /**
14
47
  * Get object metadata with cache support
15
48
  * Supports ETag-based conditional requests for efficient caching
@@ -201,101 +234,6 @@ export class ObjectStackClient {
201
234
  return res.json();
202
235
  }
203
236
  };
204
- /**
205
- * View Storage Operations
206
- * Save, load, and manage UI view configurations
207
- */
208
- this.views = {
209
- /**
210
- * Create a new saved view
211
- */
212
- create: async (request) => {
213
- const route = this.getRoute('ui');
214
- const res = await this.fetch(`${this.baseUrl}${route}/views`, {
215
- method: 'POST',
216
- body: JSON.stringify(request)
217
- });
218
- return res.json();
219
- },
220
- /**
221
- * Get a saved view by ID
222
- */
223
- get: async (id) => {
224
- const route = this.getRoute('ui');
225
- const res = await this.fetch(`${this.baseUrl}${route}/views/${id}`);
226
- return res.json();
227
- },
228
- /**
229
- * List saved views with optional filters
230
- */
231
- list: async (request) => {
232
- const route = this.getRoute('ui');
233
- const queryParams = new URLSearchParams();
234
- if (request?.object)
235
- queryParams.set('object', request.object);
236
- if (request?.type)
237
- queryParams.set('type', request.type);
238
- if (request?.visibility)
239
- queryParams.set('visibility', request.visibility);
240
- if (request?.createdBy)
241
- queryParams.set('createdBy', request.createdBy);
242
- if (request?.isDefault !== undefined)
243
- queryParams.set('isDefault', String(request.isDefault));
244
- if (request?.limit)
245
- queryParams.set('limit', String(request.limit));
246
- if (request?.offset)
247
- queryParams.set('offset', String(request.offset));
248
- const url = queryParams.toString()
249
- ? `${this.baseUrl}${route}/views?${queryParams.toString()}`
250
- : `${this.baseUrl}${route}/views`;
251
- const res = await this.fetch(url);
252
- return res.json();
253
- },
254
- /**
255
- * Update an existing view
256
- */
257
- update: async (request) => {
258
- const route = this.getRoute('ui');
259
- const { id, ...updateData } = request;
260
- const res = await this.fetch(`${this.baseUrl}${route}/views/${id}`, {
261
- method: 'PATCH',
262
- body: JSON.stringify(updateData)
263
- });
264
- return res.json();
265
- },
266
- /**
267
- * Delete a saved view
268
- */
269
- delete: async (id) => {
270
- const route = this.getRoute('ui');
271
- const res = await this.fetch(`${this.baseUrl}${route}/views/${id}`, {
272
- method: 'DELETE'
273
- });
274
- return res.json();
275
- },
276
- /**
277
- * Share a view with users/teams
278
- */
279
- share: async (id, userIds) => {
280
- const route = this.getRoute('ui');
281
- const res = await this.fetch(`${this.baseUrl}${route}/views/${id}/share`, {
282
- method: 'POST',
283
- body: JSON.stringify({ sharedWith: userIds })
284
- });
285
- return res.json();
286
- },
287
- /**
288
- * Set a view as default for an object
289
- */
290
- setDefault: async (id, object) => {
291
- const route = this.getRoute('ui');
292
- const res = await this.fetch(`${this.baseUrl}${route}/views/${id}/set-default`, {
293
- method: 'POST',
294
- body: JSON.stringify({ object })
295
- });
296
- return res.json();
297
- }
298
- };
299
237
  this.baseUrl = config.baseUrl.replace(/\/$/, ''); // Remove trailing slash
300
238
  this.token = config.token;
301
239
  this.fetchImpl = config.fetch || globalThis.fetch.bind(globalThis);
@@ -307,18 +245,18 @@ export class ObjectStackClient {
307
245
  this.logger.debug('ObjectStack client created', { baseUrl: this.baseUrl });
308
246
  }
309
247
  /**
310
- * Initialize the client by discovering server capabilities and routes.
248
+ * Initialize the client by discovering server capabilities.
311
249
  */
312
250
  async connect() {
313
251
  this.logger.debug('Connecting to ObjectStack server', { baseUrl: this.baseUrl });
314
252
  try {
315
- // Connect to the discovery endpoint
316
- // During boot, we might not know routes, so we check convention /api/v1 first
253
+ // Connect to the discovery endpoint at /api/v1
317
254
  const res = await this.fetch(`${this.baseUrl}/api/v1`);
318
255
  const data = await res.json();
319
- this.routes = data.routes;
256
+ this.discoveryInfo = data;
320
257
  this.logger.info('Connected to ObjectStack server', {
321
- routes: Object.keys(data.routes || {}),
258
+ version: data.version,
259
+ apiName: data.apiName,
322
260
  capabilities: data.capabilities
323
261
  });
324
262
  return data;
@@ -385,16 +323,19 @@ export class ObjectStackClient {
385
323
  }
386
324
  return res;
387
325
  }
388
- getRoute(key) {
389
- if (!this.routes) {
390
- // Fallback for strictness, but we allow bootstrapping
391
- this.logger.warn('Accessing route before connect()', {
392
- route: key,
393
- fallback: `/api/v1/${key}`
394
- });
395
- return `/api/v1/${key}`;
396
- }
397
- return this.routes[key] || `/api/v1/${key}`;
326
+ /**
327
+ * Get the conventional route path for a given API endpoint type
328
+ * ObjectStack uses standard conventions: /api/v1/data, /api/v1/meta, /api/v1/ui
329
+ */
330
+ getRoute(type) {
331
+ // Use conventional ObjectStack API paths
332
+ const routeMap = {
333
+ data: '/api/v1/data',
334
+ metadata: '/api/v1/meta',
335
+ ui: '/api/v1/ui',
336
+ auth: '/api/v1/auth'
337
+ };
338
+ return routeMap[type] || `/api/v1/${type}`;
398
339
  }
399
340
  }
400
341
  // Re-export type-safe query builder
package/package.json CHANGED
@@ -1,17 +1,19 @@
1
1
  {
2
2
  "name": "@objectstack/client",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "Official Client SDK for ObjectStack Protocol",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "dependencies": {
8
- "@objectstack/spec": "0.8.1",
9
- "@objectstack/core": "0.8.1"
8
+ "@objectstack/spec": "0.9.0",
9
+ "@objectstack/core": "0.9.0"
10
10
  },
11
11
  "devDependencies": {
12
- "typescript": "^5.0.0"
12
+ "typescript": "^5.0.0",
13
+ "vitest": "^4.0.18"
13
14
  },
14
15
  "scripts": {
15
- "build": "tsc"
16
+ "build": "tsc",
17
+ "test": "vitest run"
16
18
  }
17
19
  }
@@ -0,0 +1,91 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { ObjectStackClient } from './index';
3
+
4
+ describe('ObjectStackClient', () => {
5
+ it('should initialize with correct configuration', () => {
6
+ const client = new ObjectStackClient({ baseUrl: 'http://localhost:3000' });
7
+ expect(client).toBeDefined();
8
+ });
9
+
10
+ it('should normalize base URL', () => {
11
+ const client: any = new ObjectStackClient({ baseUrl: 'http://localhost:3000/' });
12
+ expect(client.baseUrl).toBe('http://localhost:3000');
13
+ });
14
+
15
+ it('should make discovery request on connect', async () => {
16
+ const fetchMock = vi.fn().mockResolvedValue({
17
+ ok: true,
18
+ json: async () => ({
19
+ version: 'v1',
20
+ apiName: 'ObjectStack',
21
+ capabilities: ['metadata', 'data', 'ui'],
22
+ endpoints: {}
23
+ })
24
+ });
25
+
26
+ const client = new ObjectStackClient({
27
+ baseUrl: 'http://localhost:3000',
28
+ fetch: fetchMock
29
+ });
30
+
31
+ await client.connect();
32
+ expect(fetchMock).toHaveBeenCalledWith('http://localhost:3000/api/v1', expect.any(Object));
33
+ });
34
+
35
+ it('should get metadata types', async () => {
36
+ const fetchMock = vi.fn().mockResolvedValue({
37
+ ok: true,
38
+ json: async () => ({
39
+ types: ['object', 'plugin', 'view']
40
+ })
41
+ });
42
+
43
+ const client = new ObjectStackClient({
44
+ baseUrl: 'http://localhost:3000',
45
+ fetch: fetchMock
46
+ });
47
+
48
+ const result = await client.meta.getTypes();
49
+ expect(fetchMock).toHaveBeenCalledWith('http://localhost:3000/api/v1/meta', expect.any(Object));
50
+ expect(result.types).toEqual(['object', 'plugin', 'view']);
51
+ });
52
+
53
+ it('should get metadata items by type', async () => {
54
+ const fetchMock = vi.fn().mockResolvedValue({
55
+ ok: true,
56
+ json: async () => ({
57
+ type: 'object',
58
+ items: [{ name: 'customer' }, { name: 'order' }]
59
+ })
60
+ });
61
+
62
+ const client = new ObjectStackClient({
63
+ baseUrl: 'http://localhost:3000',
64
+ fetch: fetchMock
65
+ });
66
+
67
+ const result = await client.meta.getItems('object');
68
+ expect(fetchMock).toHaveBeenCalledWith('http://localhost:3000/api/v1/meta/object', expect.any(Object));
69
+ expect(result.type).toBe('object');
70
+ expect(result.items).toHaveLength(2);
71
+ });
72
+
73
+ it('should get metadata item by type and name', async () => {
74
+ const fetchMock = vi.fn().mockResolvedValue({
75
+ ok: true,
76
+ json: async () => ({
77
+ name: 'customer',
78
+ fields: []
79
+ })
80
+ });
81
+
82
+ const client = new ObjectStackClient({
83
+ baseUrl: 'http://localhost:3000',
84
+ fetch: fetchMock
85
+ });
86
+
87
+ const result = await client.meta.getItem('object', 'customer');
88
+ expect(fetchMock).toHaveBeenCalledWith('http://localhost:3000/api/v1/meta/object/customer', expect.any(Object));
89
+ expect(result.name).toBe('customer');
90
+ });
91
+ });
package/src/index.ts CHANGED
@@ -9,12 +9,9 @@ import {
9
9
  MetadataCacheResponse,
10
10
  StandardErrorCode,
11
11
  ErrorCategory,
12
- CreateViewRequest,
13
- UpdateViewRequest,
14
- ListViewsRequest,
15
- SavedView,
16
- ListViewsResponse,
17
- ViewResponse
12
+ GetDiscoveryResponse,
13
+ GetMetaTypesResponse,
14
+ GetMetaItemsResponse
18
15
  } from '@objectstack/spec/api';
19
16
  import { Logger, createLogger } from '@objectstack/core';
20
17
 
@@ -35,16 +32,11 @@ export interface ClientConfig {
35
32
  debug?: boolean;
36
33
  }
37
34
 
38
- export interface DiscoveryResult {
39
- routes: {
40
- discovery: string;
41
- metadata: string;
42
- data: string;
43
- auth: string;
44
- ui: string;
45
- };
46
- capabilities?: Record<string, boolean>;
47
- }
35
+ /**
36
+ * Discovery Result
37
+ * Re-export from @objectstack/spec/api for convenience
38
+ */
39
+ export type DiscoveryResult = GetDiscoveryResponse;
48
40
 
49
41
  export interface QueryOptions {
50
42
  select?: string[]; // Simplified Selection
@@ -75,7 +67,7 @@ export class ObjectStackClient {
75
67
  private baseUrl: string;
76
68
  private token?: string;
77
69
  private fetchImpl: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
78
- private routes?: DiscoveryResult['routes'];
70
+ private discoveryInfo?: DiscoveryResult;
79
71
  private logger: Logger;
80
72
 
81
73
  constructor(config: ClientConfig) {
@@ -93,21 +85,21 @@ export class ObjectStackClient {
93
85
  }
94
86
 
95
87
  /**
96
- * Initialize the client by discovering server capabilities and routes.
88
+ * Initialize the client by discovering server capabilities.
97
89
  */
98
90
  async connect() {
99
91
  this.logger.debug('Connecting to ObjectStack server', { baseUrl: this.baseUrl });
100
92
 
101
93
  try {
102
- // Connect to the discovery endpoint
103
- // During boot, we might not know routes, so we check convention /api/v1 first
94
+ // Connect to the discovery endpoint at /api/v1
104
95
  const res = await this.fetch(`${this.baseUrl}/api/v1`);
105
96
 
106
97
  const data = await res.json();
107
- this.routes = data.routes;
98
+ this.discoveryInfo = data;
108
99
 
109
100
  this.logger.info('Connected to ObjectStack server', {
110
- routes: Object.keys(data.routes || {}),
101
+ version: data.version,
102
+ apiName: data.apiName,
111
103
  capabilities: data.capabilities
112
104
  });
113
105
 
@@ -122,11 +114,47 @@ export class ObjectStackClient {
122
114
  * Metadata Operations
123
115
  */
124
116
  meta = {
117
+ /**
118
+ * Get all available metadata types
119
+ * Returns types like 'object', 'plugin', 'view', etc.
120
+ */
121
+ getTypes: async (): Promise<GetMetaTypesResponse> => {
122
+ const route = this.getRoute('metadata');
123
+ const res = await this.fetch(`${this.baseUrl}${route}`);
124
+ return res.json();
125
+ },
126
+
127
+ /**
128
+ * Get all items of a specific metadata type
129
+ * @param type - Metadata type name (e.g., 'object', 'plugin')
130
+ */
131
+ getItems: async (type: string): Promise<GetMetaItemsResponse> => {
132
+ const route = this.getRoute('metadata');
133
+ const res = await this.fetch(`${this.baseUrl}${route}/${type}`);
134
+ return res.json();
135
+ },
136
+
137
+ /**
138
+ * Get a specific object definition by name
139
+ * @deprecated Use `getItem('object', name)` instead for consistency with spec protocol
140
+ * @param name - Object name (snake_case identifier)
141
+ */
125
142
  getObject: async (name: string) => {
126
143
  const route = this.getRoute('metadata');
127
144
  const res = await this.fetch(`${this.baseUrl}${route}/object/${name}`);
128
145
  return res.json();
129
146
  },
147
+
148
+ /**
149
+ * Get a specific metadata item by type and name
150
+ * @param type - Metadata type (e.g., 'object', 'plugin')
151
+ * @param name - Item name (snake_case identifier)
152
+ */
153
+ getItem: async (type: string, name: string) => {
154
+ const route = this.getRoute('metadata');
155
+ const res = await this.fetch(`${this.baseUrl}${route}/${type}/${name}`);
156
+ return res.json();
157
+ },
130
158
 
131
159
  /**
132
160
  * Get object metadata with cache support
@@ -342,103 +370,7 @@ export class ObjectStackClient {
342
370
  }
343
371
  };
344
372
 
345
- /**
346
- * View Storage Operations
347
- * Save, load, and manage UI view configurations
348
- */
349
- views = {
350
- /**
351
- * Create a new saved view
352
- */
353
- create: async (request: CreateViewRequest): Promise<ViewResponse> => {
354
- const route = this.getRoute('ui');
355
- const res = await this.fetch(`${this.baseUrl}${route}/views`, {
356
- method: 'POST',
357
- body: JSON.stringify(request)
358
- });
359
- return res.json();
360
- },
361
373
 
362
- /**
363
- * Get a saved view by ID
364
- */
365
- get: async (id: string): Promise<ViewResponse> => {
366
- const route = this.getRoute('ui');
367
- const res = await this.fetch(`${this.baseUrl}${route}/views/${id}`);
368
- return res.json();
369
- },
370
-
371
- /**
372
- * List saved views with optional filters
373
- */
374
- list: async (request?: ListViewsRequest): Promise<ListViewsResponse> => {
375
- const route = this.getRoute('ui');
376
- const queryParams = new URLSearchParams();
377
-
378
- if (request?.object) queryParams.set('object', request.object);
379
- if (request?.type) queryParams.set('type', request.type);
380
- if (request?.visibility) queryParams.set('visibility', request.visibility);
381
- if (request?.createdBy) queryParams.set('createdBy', request.createdBy);
382
- if (request?.isDefault !== undefined) queryParams.set('isDefault', String(request.isDefault));
383
- if (request?.limit) queryParams.set('limit', String(request.limit));
384
- if (request?.offset) queryParams.set('offset', String(request.offset));
385
-
386
- const url = queryParams.toString()
387
- ? `${this.baseUrl}${route}/views?${queryParams.toString()}`
388
- : `${this.baseUrl}${route}/views`;
389
-
390
- const res = await this.fetch(url);
391
- return res.json();
392
- },
393
-
394
- /**
395
- * Update an existing view
396
- */
397
- update: async (request: UpdateViewRequest): Promise<ViewResponse> => {
398
- const route = this.getRoute('ui');
399
- const { id, ...updateData } = request;
400
- const res = await this.fetch(`${this.baseUrl}${route}/views/${id}`, {
401
- method: 'PATCH',
402
- body: JSON.stringify(updateData)
403
- });
404
- return res.json();
405
- },
406
-
407
- /**
408
- * Delete a saved view
409
- */
410
- delete: async (id: string): Promise<{ success: boolean }> => {
411
- const route = this.getRoute('ui');
412
- const res = await this.fetch(`${this.baseUrl}${route}/views/${id}`, {
413
- method: 'DELETE'
414
- });
415
- return res.json();
416
- },
417
-
418
- /**
419
- * Share a view with users/teams
420
- */
421
- share: async (id: string, userIds: string[]): Promise<ViewResponse> => {
422
- const route = this.getRoute('ui');
423
- const res = await this.fetch(`${this.baseUrl}${route}/views/${id}/share`, {
424
- method: 'POST',
425
- body: JSON.stringify({ sharedWith: userIds })
426
- });
427
- return res.json();
428
- },
429
-
430
- /**
431
- * Set a view as default for an object
432
- */
433
- setDefault: async (id: string, object: string): Promise<ViewResponse> => {
434
- const route = this.getRoute('ui');
435
- const res = await this.fetch(`${this.baseUrl}${route}/views/${id}/set-default`, {
436
- method: 'POST',
437
- body: JSON.stringify({ object })
438
- });
439
- return res.json();
440
- }
441
- };
442
374
 
443
375
  /**
444
376
  * Private Helpers
@@ -509,16 +441,20 @@ export class ObjectStackClient {
509
441
  return res;
510
442
  }
511
443
 
512
- private getRoute(key: keyof DiscoveryResult['routes']): string {
513
- if (!this.routes) {
514
- // Fallback for strictness, but we allow bootstrapping
515
- this.logger.warn('Accessing route before connect()', {
516
- route: key,
517
- fallback: `/api/v1/${key}`
518
- });
519
- return `/api/v1/${key}`;
520
- }
521
- return this.routes[key] || `/api/v1/${key}`;
444
+ /**
445
+ * Get the conventional route path for a given API endpoint type
446
+ * ObjectStack uses standard conventions: /api/v1/data, /api/v1/meta, /api/v1/ui
447
+ */
448
+ private getRoute(type: 'data' | 'metadata' | 'ui' | 'auth'): string {
449
+ // Use conventional ObjectStack API paths
450
+ const routeMap: Record<string, string> = {
451
+ data: '/api/v1/data',
452
+ metadata: '/api/v1/meta',
453
+ ui: '/api/v1/ui',
454
+ auth: '/api/v1/auth'
455
+ };
456
+
457
+ return routeMap[type] || `/api/v1/${type}`;
522
458
  }
523
459
  }
524
460
 
@@ -538,14 +474,7 @@ export type {
538
474
  MetadataCacheResponse,
539
475
  StandardErrorCode,
540
476
  ErrorCategory,
541
- CreateViewRequest,
542
- UpdateViewRequest,
543
- ListViewsRequest,
544
- SavedView,
545
- ViewResponse,
546
- ListViewsResponse,
547
- ViewType,
548
- ViewVisibility,
549
- ViewColumn,
550
- ViewLayout
477
+ GetDiscoveryResponse,
478
+ GetMetaTypesResponse,
479
+ GetMetaItemsResponse
551
480
  } from '@objectstack/spec/api';
package/tsconfig.json CHANGED
@@ -9,5 +9,6 @@
9
9
  "esModuleInterop": true,
10
10
  "skipLibCheck": true
11
11
  },
12
- "include": ["src/**/*"]
12
+ "include": ["src/**/*"],
13
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
13
14
  }