@objectstack/client 0.9.1 → 1.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.
- package/CHANGELOG.md +23 -0
- package/README.md +11 -0
- package/dist/index.d.ts +61 -4
- package/dist/index.js +156 -4
- package/package.json +4 -3
- package/src/client.test.ts +3 -3
- package/src/index.ts +178 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# @objectstack/client
|
|
2
2
|
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- Major version release for ObjectStack Protocol v1.0.
|
|
8
|
+
- Stabilized Protocol Definitions
|
|
9
|
+
- Enhanced Runtime Plugin Support
|
|
10
|
+
- Fixed Type Compliance across Monorepo
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- Updated dependencies
|
|
15
|
+
- @objectstack/spec@1.0.0
|
|
16
|
+
- @objectstack/core@1.0.0
|
|
17
|
+
|
|
18
|
+
## 0.9.2
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- Updated dependencies
|
|
23
|
+
- @objectstack/spec@0.9.2
|
|
24
|
+
- @objectstack/core@0.9.2
|
|
25
|
+
|
|
3
26
|
## 0.9.1
|
|
4
27
|
|
|
5
28
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -12,6 +12,17 @@ The official TypeScript client for ObjectStack.
|
|
|
12
12
|
- **View Storage**: Save, load, and share custom UI view configurations.
|
|
13
13
|
- **Standardized Errors**: Machine-readable error codes with retry guidance.
|
|
14
14
|
|
|
15
|
+
## 🤖 AI Development Context
|
|
16
|
+
|
|
17
|
+
**Role**: Browser/Node Client SDK
|
|
18
|
+
**Usage**:
|
|
19
|
+
- Use this in Frontend Apps (React, etc.) or external Node scripts.
|
|
20
|
+
- Interacts with `packages/runtime` over HTTP.
|
|
21
|
+
|
|
22
|
+
**Key namespaces**:
|
|
23
|
+
- `client.meta`: Metadata operations.
|
|
24
|
+
- `client.data`: Data operations.
|
|
25
|
+
|
|
15
26
|
## Installation
|
|
16
27
|
|
|
17
28
|
```bash
|
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, GetDiscoveryResponse, GetMetaTypesResponse, GetMetaItemsResponse } from '@objectstack/spec/api';
|
|
2
|
+
import { BatchUpdateRequest, BatchUpdateResponse, BatchOptions, MetadataCacheRequest, MetadataCacheResponse, StandardErrorCode, ErrorCategory, GetDiscoveryResponse, GetMetaTypesResponse, GetMetaItemsResponse, LoginRequest, SessionResponse, FileUploadResponse } from '@objectstack/spec/api';
|
|
3
3
|
import { Logger } from '@objectstack/core';
|
|
4
4
|
export interface ClientConfig {
|
|
5
5
|
baseUrl: string;
|
|
@@ -56,8 +56,24 @@ export declare class ObjectStackClient {
|
|
|
56
56
|
connect(): Promise<{
|
|
57
57
|
version: string;
|
|
58
58
|
apiName: string;
|
|
59
|
-
capabilities?:
|
|
60
|
-
|
|
59
|
+
capabilities?: {
|
|
60
|
+
search: boolean;
|
|
61
|
+
files: boolean;
|
|
62
|
+
graphql: boolean;
|
|
63
|
+
analytics: boolean;
|
|
64
|
+
hub: boolean;
|
|
65
|
+
websockets: boolean;
|
|
66
|
+
} | undefined;
|
|
67
|
+
endpoints?: {
|
|
68
|
+
data: string;
|
|
69
|
+
metadata: string;
|
|
70
|
+
auth: string;
|
|
71
|
+
graphql?: string | undefined;
|
|
72
|
+
storage?: string | undefined;
|
|
73
|
+
automation?: string | undefined;
|
|
74
|
+
analytics?: string | undefined;
|
|
75
|
+
hub?: string | undefined;
|
|
76
|
+
} | undefined;
|
|
61
77
|
}>;
|
|
62
78
|
/**
|
|
63
79
|
* Metadata Operations
|
|
@@ -92,6 +108,47 @@ export declare class ObjectStackClient {
|
|
|
92
108
|
getCached: (name: string, cacheOptions?: MetadataCacheRequest) => Promise<MetadataCacheResponse>;
|
|
93
109
|
getView: (object: string, type?: "list" | "form") => Promise<any>;
|
|
94
110
|
};
|
|
111
|
+
/**
|
|
112
|
+
* Analytics Services
|
|
113
|
+
*/
|
|
114
|
+
analytics: {
|
|
115
|
+
query: (payload: any) => Promise<any>;
|
|
116
|
+
meta: (cube: string) => Promise<any>;
|
|
117
|
+
explain: (payload: any) => Promise<any>;
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Hub Management Services
|
|
121
|
+
*/
|
|
122
|
+
hub: {
|
|
123
|
+
spaces: {
|
|
124
|
+
list: () => Promise<any>;
|
|
125
|
+
create: (payload: any) => Promise<any>;
|
|
126
|
+
};
|
|
127
|
+
plugins: {
|
|
128
|
+
install: (pkg: string, version?: string) => Promise<any>;
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Authentication Services
|
|
133
|
+
*/
|
|
134
|
+
auth: {
|
|
135
|
+
login: (request: LoginRequest) => Promise<SessionResponse>;
|
|
136
|
+
logout: () => Promise<void>;
|
|
137
|
+
me: () => Promise<SessionResponse>;
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Storage Services
|
|
141
|
+
*/
|
|
142
|
+
storage: {
|
|
143
|
+
upload: (file: any, scope?: string) => Promise<FileUploadResponse>;
|
|
144
|
+
getDownloadUrl: (fileId: string) => Promise<string>;
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* Automation Services
|
|
148
|
+
*/
|
|
149
|
+
automation: {
|
|
150
|
+
trigger: (triggerName: string, payload: any) => Promise<any>;
|
|
151
|
+
};
|
|
95
152
|
/**
|
|
96
153
|
* Data Operations
|
|
97
154
|
*/
|
|
@@ -134,7 +191,7 @@ export declare class ObjectStackClient {
|
|
|
134
191
|
private fetch;
|
|
135
192
|
/**
|
|
136
193
|
* Get the conventional route path for a given API endpoint type
|
|
137
|
-
* ObjectStack uses standard conventions: /api/v1/data, /api/v1/
|
|
194
|
+
* ObjectStack uses standard conventions: /api/v1/data, /api/v1/metadata, /api/v1/ui
|
|
138
195
|
*/
|
|
139
196
|
private getRoute;
|
|
140
197
|
}
|
package/dist/index.js
CHANGED
|
@@ -88,6 +88,148 @@ export class ObjectStackClient {
|
|
|
88
88
|
return res.json();
|
|
89
89
|
}
|
|
90
90
|
};
|
|
91
|
+
/**
|
|
92
|
+
* Analytics Services
|
|
93
|
+
*/
|
|
94
|
+
this.analytics = {
|
|
95
|
+
query: async (payload) => {
|
|
96
|
+
const route = this.getRoute('analytics');
|
|
97
|
+
const res = await this.fetch(`${this.baseUrl}${route}/query`, {
|
|
98
|
+
method: 'POST',
|
|
99
|
+
body: JSON.stringify(payload)
|
|
100
|
+
});
|
|
101
|
+
return res.json();
|
|
102
|
+
},
|
|
103
|
+
meta: async (cube) => {
|
|
104
|
+
const route = this.getRoute('analytics');
|
|
105
|
+
const res = await this.fetch(`${this.baseUrl}${route}/meta/${cube}`);
|
|
106
|
+
return res.json();
|
|
107
|
+
},
|
|
108
|
+
explain: async (payload) => {
|
|
109
|
+
const route = this.getRoute('analytics');
|
|
110
|
+
const res = await this.fetch(`${this.baseUrl}${route}/explain`, {
|
|
111
|
+
method: 'POST',
|
|
112
|
+
body: JSON.stringify(payload)
|
|
113
|
+
});
|
|
114
|
+
return res.json();
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Hub Management Services
|
|
119
|
+
*/
|
|
120
|
+
this.hub = {
|
|
121
|
+
spaces: {
|
|
122
|
+
list: async () => {
|
|
123
|
+
const route = this.getRoute('hub');
|
|
124
|
+
const res = await this.fetch(`${this.baseUrl}${route}/spaces`);
|
|
125
|
+
return res.json();
|
|
126
|
+
},
|
|
127
|
+
create: async (payload) => {
|
|
128
|
+
const route = this.getRoute('hub');
|
|
129
|
+
const res = await this.fetch(`${this.baseUrl}${route}/spaces`, {
|
|
130
|
+
method: 'POST',
|
|
131
|
+
body: JSON.stringify(payload)
|
|
132
|
+
});
|
|
133
|
+
return res.json();
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
plugins: {
|
|
137
|
+
install: async (pkg, version) => {
|
|
138
|
+
const route = this.getRoute('hub');
|
|
139
|
+
const res = await this.fetch(`${this.baseUrl}${route}/plugins/install`, {
|
|
140
|
+
method: 'POST',
|
|
141
|
+
body: JSON.stringify({ pkg, version })
|
|
142
|
+
});
|
|
143
|
+
return res.json();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Authentication Services
|
|
149
|
+
*/
|
|
150
|
+
this.auth = {
|
|
151
|
+
login: async (request) => {
|
|
152
|
+
const route = this.getRoute('auth');
|
|
153
|
+
const res = await this.fetch(`${this.baseUrl}${route}/login`, {
|
|
154
|
+
method: 'POST',
|
|
155
|
+
body: JSON.stringify(request)
|
|
156
|
+
});
|
|
157
|
+
const data = await res.json();
|
|
158
|
+
// Auto-set token if present in response
|
|
159
|
+
if (data.data?.token) {
|
|
160
|
+
this.token = data.data.token;
|
|
161
|
+
}
|
|
162
|
+
return data;
|
|
163
|
+
},
|
|
164
|
+
logout: async () => {
|
|
165
|
+
const route = this.getRoute('auth');
|
|
166
|
+
await this.fetch(`${this.baseUrl}${route}/logout`, { method: 'POST' });
|
|
167
|
+
this.token = undefined;
|
|
168
|
+
},
|
|
169
|
+
me: async () => {
|
|
170
|
+
const route = this.getRoute('auth');
|
|
171
|
+
const res = await this.fetch(`${this.baseUrl}${route}/me`);
|
|
172
|
+
return res.json();
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Storage Services
|
|
177
|
+
*/
|
|
178
|
+
this.storage = {
|
|
179
|
+
upload: async (file, scope = 'user') => {
|
|
180
|
+
// 1. Get Presigned URL
|
|
181
|
+
const presignedReq = {
|
|
182
|
+
filename: file.name,
|
|
183
|
+
mimeType: file.type,
|
|
184
|
+
size: file.size,
|
|
185
|
+
scope
|
|
186
|
+
};
|
|
187
|
+
const route = this.getRoute('storage');
|
|
188
|
+
const presignedRes = await this.fetch(`${this.baseUrl}${route}/upload/presigned`, {
|
|
189
|
+
method: 'POST',
|
|
190
|
+
body: JSON.stringify(presignedReq)
|
|
191
|
+
});
|
|
192
|
+
const { data: presigned } = await presignedRes.json();
|
|
193
|
+
// 2. Upload to Cloud directly (Bypass API Middleware to avoid Auth headers if using S3)
|
|
194
|
+
// Use fetchImpl directly
|
|
195
|
+
const uploadRes = await this.fetchImpl(presigned.uploadUrl, {
|
|
196
|
+
method: presigned.method,
|
|
197
|
+
headers: presigned.headers,
|
|
198
|
+
body: file
|
|
199
|
+
});
|
|
200
|
+
if (!uploadRes.ok) {
|
|
201
|
+
throw new Error(`Storage Upload Failed: ${uploadRes.statusText}`);
|
|
202
|
+
}
|
|
203
|
+
// 3. Complete Upload
|
|
204
|
+
const completeReq = {
|
|
205
|
+
fileId: presigned.fileId
|
|
206
|
+
};
|
|
207
|
+
const completeRes = await this.fetch(`${this.baseUrl}${route}/upload/complete`, {
|
|
208
|
+
method: 'POST',
|
|
209
|
+
body: JSON.stringify(completeReq)
|
|
210
|
+
});
|
|
211
|
+
return completeRes.json();
|
|
212
|
+
},
|
|
213
|
+
getDownloadUrl: async (fileId) => {
|
|
214
|
+
const route = this.getRoute('storage');
|
|
215
|
+
const res = await this.fetch(`${this.baseUrl}${route}/files/${fileId}/url`);
|
|
216
|
+
const data = await res.json();
|
|
217
|
+
return data.url;
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
/**
|
|
221
|
+
* Automation Services
|
|
222
|
+
*/
|
|
223
|
+
this.automation = {
|
|
224
|
+
trigger: async (triggerName, payload) => {
|
|
225
|
+
const route = this.getRoute('automation');
|
|
226
|
+
const res = await this.fetch(`${this.baseUrl}${route}/trigger/${triggerName}`, {
|
|
227
|
+
method: 'POST',
|
|
228
|
+
body: JSON.stringify(payload)
|
|
229
|
+
});
|
|
230
|
+
return res.json();
|
|
231
|
+
}
|
|
232
|
+
};
|
|
91
233
|
/**
|
|
92
234
|
* Data Operations
|
|
93
235
|
*/
|
|
@@ -325,15 +467,25 @@ export class ObjectStackClient {
|
|
|
325
467
|
}
|
|
326
468
|
/**
|
|
327
469
|
* Get the conventional route path for a given API endpoint type
|
|
328
|
-
* ObjectStack uses standard conventions: /api/v1/data, /api/v1/
|
|
470
|
+
* ObjectStack uses standard conventions: /api/v1/data, /api/v1/metadata, /api/v1/ui
|
|
329
471
|
*/
|
|
330
472
|
getRoute(type) {
|
|
331
|
-
// Use
|
|
473
|
+
// 1. Use discovered routes if available
|
|
474
|
+
// Note: Spec uses 'endpoints', mapped dynamically
|
|
475
|
+
if (this.discoveryInfo?.endpoints && this.discoveryInfo.endpoints[type]) {
|
|
476
|
+
return this.discoveryInfo.endpoints[type];
|
|
477
|
+
}
|
|
478
|
+
// 2. Fallback to conventions
|
|
479
|
+
// Note: HttpDispatcher expects /metadata, not /meta
|
|
332
480
|
const routeMap = {
|
|
333
481
|
data: '/api/v1/data',
|
|
334
|
-
metadata: '/api/v1/
|
|
482
|
+
metadata: '/api/v1/metadata',
|
|
335
483
|
ui: '/api/v1/ui',
|
|
336
|
-
auth: '/api/v1/auth'
|
|
484
|
+
auth: '/api/v1/auth',
|
|
485
|
+
analytics: '/api/v1/analytics',
|
|
486
|
+
hub: '/api/v1/hub',
|
|
487
|
+
storage: '/api/v1/storage',
|
|
488
|
+
automation: '/api/v1/automation'
|
|
337
489
|
};
|
|
338
490
|
return routeMap[type] || `/api/v1/${type}`;
|
|
339
491
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "Apache-2.0",
|
|
4
5
|
"description": "Official Client SDK for ObjectStack Protocol",
|
|
5
6
|
"main": "dist/index.js",
|
|
6
7
|
"types": "dist/index.d.ts",
|
|
7
8
|
"dependencies": {
|
|
8
|
-
"@objectstack/spec": "0.
|
|
9
|
-
"@objectstack/core": "0.
|
|
9
|
+
"@objectstack/spec": "1.0.0",
|
|
10
|
+
"@objectstack/core": "1.0.0"
|
|
10
11
|
},
|
|
11
12
|
"devDependencies": {
|
|
12
13
|
"typescript": "^5.0.0",
|
package/src/client.test.ts
CHANGED
|
@@ -46,7 +46,7 @@ describe('ObjectStackClient', () => {
|
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
const result = await client.meta.getTypes();
|
|
49
|
-
expect(fetchMock).toHaveBeenCalledWith('http://localhost:3000/api/v1/
|
|
49
|
+
expect(fetchMock).toHaveBeenCalledWith('http://localhost:3000/api/v1/metadata', expect.any(Object));
|
|
50
50
|
expect(result.types).toEqual(['object', 'plugin', 'view']);
|
|
51
51
|
});
|
|
52
52
|
|
|
@@ -65,7 +65,7 @@ describe('ObjectStackClient', () => {
|
|
|
65
65
|
});
|
|
66
66
|
|
|
67
67
|
const result = await client.meta.getItems('object');
|
|
68
|
-
expect(fetchMock).toHaveBeenCalledWith('http://localhost:3000/api/v1/
|
|
68
|
+
expect(fetchMock).toHaveBeenCalledWith('http://localhost:3000/api/v1/metadata/object', expect.any(Object));
|
|
69
69
|
expect(result.type).toBe('object');
|
|
70
70
|
expect(result.items).toHaveLength(2);
|
|
71
71
|
});
|
|
@@ -85,7 +85,7 @@ describe('ObjectStackClient', () => {
|
|
|
85
85
|
});
|
|
86
86
|
|
|
87
87
|
const result = await client.meta.getItem('object', 'customer');
|
|
88
|
-
expect(fetchMock).toHaveBeenCalledWith('http://localhost:3000/api/v1/
|
|
88
|
+
expect(fetchMock).toHaveBeenCalledWith('http://localhost:3000/api/v1/metadata/object/customer', expect.any(Object));
|
|
89
89
|
expect(result.name).toBe('customer');
|
|
90
90
|
});
|
|
91
91
|
});
|
package/src/index.ts
CHANGED
|
@@ -11,7 +11,13 @@ import {
|
|
|
11
11
|
ErrorCategory,
|
|
12
12
|
GetDiscoveryResponse,
|
|
13
13
|
GetMetaTypesResponse,
|
|
14
|
-
GetMetaItemsResponse
|
|
14
|
+
GetMetaItemsResponse,
|
|
15
|
+
LoginRequest,
|
|
16
|
+
SessionResponse,
|
|
17
|
+
GetPresignedUrlRequest,
|
|
18
|
+
PresignedUrlResponse,
|
|
19
|
+
CompleteUploadRequest,
|
|
20
|
+
FileUploadResponse
|
|
15
21
|
} from '@objectstack/spec/api';
|
|
16
22
|
import { Logger, createLogger } from '@objectstack/core';
|
|
17
23
|
|
|
@@ -208,6 +214,161 @@ export class ObjectStackClient {
|
|
|
208
214
|
}
|
|
209
215
|
};
|
|
210
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Analytics Services
|
|
219
|
+
*/
|
|
220
|
+
analytics = {
|
|
221
|
+
query: async (payload: any) => {
|
|
222
|
+
const route = this.getRoute('analytics');
|
|
223
|
+
const res = await this.fetch(`${this.baseUrl}${route}/query`, {
|
|
224
|
+
method: 'POST',
|
|
225
|
+
body: JSON.stringify(payload)
|
|
226
|
+
});
|
|
227
|
+
return res.json();
|
|
228
|
+
},
|
|
229
|
+
meta: async (cube: string) => {
|
|
230
|
+
const route = this.getRoute('analytics');
|
|
231
|
+
const res = await this.fetch(`${this.baseUrl}${route}/meta/${cube}`);
|
|
232
|
+
return res.json();
|
|
233
|
+
},
|
|
234
|
+
explain: async (payload: any) => {
|
|
235
|
+
const route = this.getRoute('analytics');
|
|
236
|
+
const res = await this.fetch(`${this.baseUrl}${route}/explain`, {
|
|
237
|
+
method: 'POST',
|
|
238
|
+
body: JSON.stringify(payload)
|
|
239
|
+
});
|
|
240
|
+
return res.json();
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Hub Management Services
|
|
246
|
+
*/
|
|
247
|
+
hub = {
|
|
248
|
+
spaces: {
|
|
249
|
+
list: async () => {
|
|
250
|
+
const route = this.getRoute('hub');
|
|
251
|
+
const res = await this.fetch(`${this.baseUrl}${route}/spaces`);
|
|
252
|
+
return res.json();
|
|
253
|
+
},
|
|
254
|
+
create: async (payload: any) => {
|
|
255
|
+
const route = this.getRoute('hub');
|
|
256
|
+
const res = await this.fetch(`${this.baseUrl}${route}/spaces`, {
|
|
257
|
+
method: 'POST',
|
|
258
|
+
body: JSON.stringify(payload)
|
|
259
|
+
});
|
|
260
|
+
return res.json();
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
plugins: {
|
|
264
|
+
install: async (pkg: string, version?: string) => {
|
|
265
|
+
const route = this.getRoute('hub');
|
|
266
|
+
const res = await this.fetch(`${this.baseUrl}${route}/plugins/install`, {
|
|
267
|
+
method: 'POST',
|
|
268
|
+
body: JSON.stringify({ pkg, version })
|
|
269
|
+
});
|
|
270
|
+
return res.json();
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Authentication Services
|
|
277
|
+
*/
|
|
278
|
+
auth = {
|
|
279
|
+
login: async (request: LoginRequest): Promise<SessionResponse> => {
|
|
280
|
+
const route = this.getRoute('auth');
|
|
281
|
+
const res = await this.fetch(`${this.baseUrl}${route}/login`, {
|
|
282
|
+
method: 'POST',
|
|
283
|
+
body: JSON.stringify(request)
|
|
284
|
+
});
|
|
285
|
+
const data = await res.json();
|
|
286
|
+
// Auto-set token if present in response
|
|
287
|
+
if (data.data?.token) {
|
|
288
|
+
this.token = data.data.token;
|
|
289
|
+
}
|
|
290
|
+
return data;
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
logout: async () => {
|
|
294
|
+
const route = this.getRoute('auth');
|
|
295
|
+
await this.fetch(`${this.baseUrl}${route}/logout`, { method: 'POST' });
|
|
296
|
+
this.token = undefined;
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
me: async (): Promise<SessionResponse> => {
|
|
300
|
+
const route = this.getRoute('auth');
|
|
301
|
+
const res = await this.fetch(`${this.baseUrl}${route}/me`);
|
|
302
|
+
return res.json();
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Storage Services
|
|
308
|
+
*/
|
|
309
|
+
storage = {
|
|
310
|
+
upload: async (file: any, scope: string = 'user'): Promise<FileUploadResponse> => {
|
|
311
|
+
// 1. Get Presigned URL
|
|
312
|
+
const presignedReq: GetPresignedUrlRequest = {
|
|
313
|
+
filename: file.name,
|
|
314
|
+
mimeType: file.type,
|
|
315
|
+
size: file.size,
|
|
316
|
+
scope
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
const route = this.getRoute('storage');
|
|
320
|
+
const presignedRes = await this.fetch(`${this.baseUrl}${route}/upload/presigned`, {
|
|
321
|
+
method: 'POST',
|
|
322
|
+
body: JSON.stringify(presignedReq)
|
|
323
|
+
});
|
|
324
|
+
const { data: presigned } = await presignedRes.json() as { data: PresignedUrlResponse['data'] };
|
|
325
|
+
|
|
326
|
+
// 2. Upload to Cloud directly (Bypass API Middleware to avoid Auth headers if using S3)
|
|
327
|
+
// Use fetchImpl directly
|
|
328
|
+
const uploadRes = await this.fetchImpl(presigned.uploadUrl, {
|
|
329
|
+
method: presigned.method,
|
|
330
|
+
headers: presigned.headers,
|
|
331
|
+
body: file
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
if (!uploadRes.ok) {
|
|
335
|
+
throw new Error(`Storage Upload Failed: ${uploadRes.statusText}`);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// 3. Complete Upload
|
|
339
|
+
const completeReq: CompleteUploadRequest = {
|
|
340
|
+
fileId: presigned.fileId
|
|
341
|
+
};
|
|
342
|
+
const completeRes = await this.fetch(`${this.baseUrl}${route}/upload/complete`, {
|
|
343
|
+
method: 'POST',
|
|
344
|
+
body: JSON.stringify(completeReq)
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
return completeRes.json();
|
|
348
|
+
},
|
|
349
|
+
|
|
350
|
+
getDownloadUrl: async (fileId: string): Promise<string> => {
|
|
351
|
+
const route = this.getRoute('storage');
|
|
352
|
+
const res = await this.fetch(`${this.baseUrl}${route}/files/${fileId}/url`);
|
|
353
|
+
const data = await res.json();
|
|
354
|
+
return data.url;
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Automation Services
|
|
360
|
+
*/
|
|
361
|
+
automation = {
|
|
362
|
+
trigger: async (triggerName: string, payload: any) => {
|
|
363
|
+
const route = this.getRoute('automation');
|
|
364
|
+
const res = await this.fetch(`${this.baseUrl}${route}/trigger/${triggerName}`, {
|
|
365
|
+
method: 'POST',
|
|
366
|
+
body: JSON.stringify(payload)
|
|
367
|
+
});
|
|
368
|
+
return res.json();
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
|
|
211
372
|
/**
|
|
212
373
|
* Data Operations
|
|
213
374
|
*/
|
|
@@ -443,15 +604,26 @@ export class ObjectStackClient {
|
|
|
443
604
|
|
|
444
605
|
/**
|
|
445
606
|
* Get the conventional route path for a given API endpoint type
|
|
446
|
-
* ObjectStack uses standard conventions: /api/v1/data, /api/v1/
|
|
607
|
+
* ObjectStack uses standard conventions: /api/v1/data, /api/v1/metadata, /api/v1/ui
|
|
447
608
|
*/
|
|
448
|
-
private getRoute(type: 'data' | 'metadata' | 'ui' | 'auth'): string {
|
|
449
|
-
// Use
|
|
609
|
+
private getRoute(type: 'data' | 'metadata' | 'ui' | 'auth' | 'analytics' | 'hub' | 'storage' | 'automation'): string {
|
|
610
|
+
// 1. Use discovered routes if available
|
|
611
|
+
// Note: Spec uses 'endpoints', mapped dynamically
|
|
612
|
+
if (this.discoveryInfo?.endpoints && (this.discoveryInfo.endpoints as any)[type]) {
|
|
613
|
+
return (this.discoveryInfo.endpoints as any)[type];
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// 2. Fallback to conventions
|
|
617
|
+
// Note: HttpDispatcher expects /metadata, not /meta
|
|
450
618
|
const routeMap: Record<string, string> = {
|
|
451
619
|
data: '/api/v1/data',
|
|
452
|
-
metadata: '/api/v1/
|
|
620
|
+
metadata: '/api/v1/metadata',
|
|
453
621
|
ui: '/api/v1/ui',
|
|
454
|
-
auth: '/api/v1/auth'
|
|
622
|
+
auth: '/api/v1/auth',
|
|
623
|
+
analytics: '/api/v1/analytics',
|
|
624
|
+
hub: '/api/v1/hub',
|
|
625
|
+
storage: '/api/v1/storage',
|
|
626
|
+
automation: '/api/v1/automation'
|
|
455
627
|
};
|
|
456
628
|
|
|
457
629
|
return routeMap[type] || `/api/v1/${type}`;
|