@mitralab.io/platform-sdk 1.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/README.md +147 -0
- package/dist/index.d.mts +1140 -0
- package/dist/index.d.ts +1140 -0
- package/dist/index.js +763 -0
- package/dist/index.mjs +735 -0
- package/package.json +57 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,735 @@
|
|
|
1
|
+
// src/utils/http-client.ts
|
|
2
|
+
var HttpClient = class {
|
|
3
|
+
baseUrl;
|
|
4
|
+
tokenGetter;
|
|
5
|
+
onUnauthorized;
|
|
6
|
+
onError;
|
|
7
|
+
defaultHeaders;
|
|
8
|
+
constructor(config) {
|
|
9
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
10
|
+
this.tokenGetter = config.getToken ?? (() => null);
|
|
11
|
+
this.onUnauthorized = config.onUnauthorized;
|
|
12
|
+
this.onError = config.onError;
|
|
13
|
+
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Returns the current authentication token.
|
|
17
|
+
* @returns The JWT token if authenticated, null otherwise
|
|
18
|
+
*/
|
|
19
|
+
getToken() {
|
|
20
|
+
return this.tokenGetter();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Makes an HTTP request with automatic JSON handling and authentication.
|
|
24
|
+
*
|
|
25
|
+
* @param path - API endpoint path (e.g., '/users')
|
|
26
|
+
* @param options - Request options including method, body, headers, and params
|
|
27
|
+
* @returns Promise resolving to the parsed JSON response
|
|
28
|
+
* @throws {MitraApiError} When the API returns an error response
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const result = await client.request<User>('/users/123', {
|
|
33
|
+
* method: 'PUT',
|
|
34
|
+
* body: { name: 'Updated Name' },
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
async request(path, options = {}) {
|
|
39
|
+
const { method = "GET", body, headers = {}, params, isRetry } = options;
|
|
40
|
+
let url = `${this.baseUrl}${path}`;
|
|
41
|
+
if (params) {
|
|
42
|
+
const searchParams = new URLSearchParams();
|
|
43
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
44
|
+
if (value !== void 0) {
|
|
45
|
+
searchParams.append(key, String(value));
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
const queryString = searchParams.toString();
|
|
49
|
+
if (queryString) {
|
|
50
|
+
url += `?${queryString}`;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const requestHeaders = {
|
|
54
|
+
"Content-Type": "application/json",
|
|
55
|
+
...this.defaultHeaders,
|
|
56
|
+
...headers
|
|
57
|
+
};
|
|
58
|
+
const token = this.tokenGetter();
|
|
59
|
+
if (token) {
|
|
60
|
+
requestHeaders["Authorization"] = `Bearer ${token}`;
|
|
61
|
+
}
|
|
62
|
+
const response = await fetch(url, {
|
|
63
|
+
method,
|
|
64
|
+
headers: requestHeaders,
|
|
65
|
+
body: body ? JSON.stringify(body) : void 0
|
|
66
|
+
});
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
if (response.status === 401 && !isRetry && this.onUnauthorized) {
|
|
69
|
+
const refreshed = await this.onUnauthorized();
|
|
70
|
+
if (refreshed) {
|
|
71
|
+
return this.request(path, { ...options, isRetry: true });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const errorBody = await response.json().catch(() => ({}));
|
|
75
|
+
const error = new MitraApiError(
|
|
76
|
+
errorBody.message || `Request failed with status ${response.status}`,
|
|
77
|
+
response.status,
|
|
78
|
+
errorBody.error_code,
|
|
79
|
+
errorBody
|
|
80
|
+
);
|
|
81
|
+
this.onError?.(error);
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
if (response.status === 204) {
|
|
85
|
+
return void 0;
|
|
86
|
+
}
|
|
87
|
+
return response.json();
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Makes a GET request.
|
|
91
|
+
*
|
|
92
|
+
* @param path - API endpoint path
|
|
93
|
+
* @param params - Optional query parameters
|
|
94
|
+
* @returns Promise resolving to the parsed JSON response
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* const users = await client.get<User[]>('/users', { limit: 10 });
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
get(path, params) {
|
|
102
|
+
return this.request(path, { method: "GET", params });
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Makes a POST request.
|
|
106
|
+
*
|
|
107
|
+
* @param path - API endpoint path
|
|
108
|
+
* @param body - Request body (will be JSON stringified)
|
|
109
|
+
* @returns Promise resolving to the parsed JSON response
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* const user = await client.post<User>('/users', { name: 'John', email: 'john@example.com' });
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
post(path, body) {
|
|
117
|
+
return this.request(path, { method: "POST", body });
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Makes a PUT request.
|
|
121
|
+
*
|
|
122
|
+
* @param path - API endpoint path
|
|
123
|
+
* @param body - Request body (will be JSON stringified)
|
|
124
|
+
* @returns Promise resolving to the parsed JSON response
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* const user = await client.put<User>('/users/123', { name: 'Updated Name' });
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
put(path, body) {
|
|
132
|
+
return this.request(path, { method: "PUT", body });
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Makes a DELETE request.
|
|
136
|
+
*
|
|
137
|
+
* @param path - API endpoint path
|
|
138
|
+
* @param params - Optional query parameters
|
|
139
|
+
* @returns Promise resolving to the parsed JSON response (or undefined for 204 responses)
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```typescript
|
|
143
|
+
* await client.delete('/users/123');
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
delete(path, params) {
|
|
147
|
+
return this.request(path, { method: "DELETE", params });
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
var MitraApiError = class extends Error {
|
|
151
|
+
constructor(message, status, code, details) {
|
|
152
|
+
super(message);
|
|
153
|
+
this.status = status;
|
|
154
|
+
this.code = code;
|
|
155
|
+
this.details = details;
|
|
156
|
+
this.name = "MitraApiError";
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// src/modules/auth.ts
|
|
161
|
+
var AuthModule = class {
|
|
162
|
+
appId;
|
|
163
|
+
_currentUser = null;
|
|
164
|
+
_accessToken = null;
|
|
165
|
+
_refreshToken = null;
|
|
166
|
+
refreshPromise = null;
|
|
167
|
+
listeners = /* @__PURE__ */ new Set();
|
|
168
|
+
storageKey;
|
|
169
|
+
publicClient;
|
|
170
|
+
authedClient;
|
|
171
|
+
constructor(appId, iamBaseUrl) {
|
|
172
|
+
this.appId = appId;
|
|
173
|
+
this.storageKey = `mitra_auth_${appId}`;
|
|
174
|
+
this.publicClient = new HttpClient({ baseUrl: iamBaseUrl, getToken: () => null });
|
|
175
|
+
this.authedClient = new HttpClient({ baseUrl: iamBaseUrl, getToken: () => this._accessToken });
|
|
176
|
+
this.loadFromStorage();
|
|
177
|
+
}
|
|
178
|
+
/** The currently authenticated user, or null. */
|
|
179
|
+
get currentUser() {
|
|
180
|
+
return this._currentUser;
|
|
181
|
+
}
|
|
182
|
+
/** The current JWT access token, or null. */
|
|
183
|
+
get accessToken() {
|
|
184
|
+
return this._accessToken;
|
|
185
|
+
}
|
|
186
|
+
/** Whether a user is currently authenticated (local check, not server-validated). */
|
|
187
|
+
get isAuthenticated() {
|
|
188
|
+
return this._currentUser !== null && this._accessToken !== null;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Signs in a user with email and password.
|
|
192
|
+
*
|
|
193
|
+
* On success, stores access token, refresh token, and user data.
|
|
194
|
+
* Subsequent API requests use the token automatically.
|
|
195
|
+
*
|
|
196
|
+
* @param credentials - Email and password.
|
|
197
|
+
* @returns The authenticated user.
|
|
198
|
+
* @throws {MitraApiError} On invalid credentials (401).
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* ```typescript
|
|
202
|
+
* const user = await mitra.auth.signIn({
|
|
203
|
+
* email: 'user@example.com',
|
|
204
|
+
* password: 'password123',
|
|
205
|
+
* });
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
async signIn(credentials) {
|
|
209
|
+
const tokenResponse = await this.publicClient.post(
|
|
210
|
+
"/api/v1/auth/tokens",
|
|
211
|
+
credentials
|
|
212
|
+
);
|
|
213
|
+
this._accessToken = tokenResponse.accessToken;
|
|
214
|
+
this._refreshToken = tokenResponse.refreshToken;
|
|
215
|
+
const user = await this.authedClient.get("/api/v1/auth/me");
|
|
216
|
+
this.setAuthState(user, tokenResponse.accessToken, tokenResponse.refreshToken);
|
|
217
|
+
return user;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Registers a new user and signs them in automatically.
|
|
221
|
+
*
|
|
222
|
+
* @param data - Email, password, and optional name.
|
|
223
|
+
* @returns The newly created and authenticated user.
|
|
224
|
+
* @throws {MitraApiError} On duplicate email (409) or validation error (400).
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```typescript
|
|
228
|
+
* const user = await mitra.auth.signUp({
|
|
229
|
+
* email: 'new@example.com',
|
|
230
|
+
* password: 'securepassword',
|
|
231
|
+
* name: 'Jane Doe',
|
|
232
|
+
* });
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
async signUp(data) {
|
|
236
|
+
await this.publicClient.post("/api/v1/auth/users/register", {
|
|
237
|
+
...data,
|
|
238
|
+
appId: this.appId
|
|
239
|
+
});
|
|
240
|
+
return this.signIn({ email: data.email, password: data.password });
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Signs out the current user, clearing all auth state and localStorage.
|
|
244
|
+
*
|
|
245
|
+
* @param redirectUrl - Optional URL to navigate to after sign-out.
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* ```typescript
|
|
249
|
+
* mitra.auth.signOut();
|
|
250
|
+
* mitra.auth.signOut('/login');
|
|
251
|
+
* ```
|
|
252
|
+
*/
|
|
253
|
+
signOut(redirectUrl) {
|
|
254
|
+
this.clearAuthState();
|
|
255
|
+
if (globalThis.window !== void 0 && redirectUrl) {
|
|
256
|
+
globalThis.window.location.href = redirectUrl;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Refreshes the session using the stored refresh token.
|
|
261
|
+
*
|
|
262
|
+
* Called automatically by the SDK on 401 responses. Can also be called
|
|
263
|
+
* manually. Multiple concurrent calls are deduplicated (only one refresh
|
|
264
|
+
* request is made).
|
|
265
|
+
*
|
|
266
|
+
* @returns `true` if refresh succeeded, `false` otherwise.
|
|
267
|
+
*
|
|
268
|
+
* @example
|
|
269
|
+
* ```typescript
|
|
270
|
+
* const ok = await mitra.auth.refreshSession();
|
|
271
|
+
* if (!ok) mitra.auth.redirectToLogin();
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
async refreshSession() {
|
|
275
|
+
if (!this._refreshToken) return false;
|
|
276
|
+
if (this.refreshPromise) return this.refreshPromise;
|
|
277
|
+
this.refreshPromise = this.doRefresh();
|
|
278
|
+
try {
|
|
279
|
+
return await this.refreshPromise;
|
|
280
|
+
} finally {
|
|
281
|
+
this.refreshPromise = null;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Fetches the current user from the server and updates local state.
|
|
286
|
+
*
|
|
287
|
+
* Only clears auth state on 401 (expired/invalid token).
|
|
288
|
+
* Transient errors (500, network) return null without clearing the session.
|
|
289
|
+
*
|
|
290
|
+
* @returns The user if authenticated, `null` otherwise.
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```typescript
|
|
294
|
+
* const user = await mitra.auth.me();
|
|
295
|
+
* if (!user) console.log('Not authenticated');
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
async me() {
|
|
299
|
+
if (!this._accessToken) return null;
|
|
300
|
+
try {
|
|
301
|
+
const user = await this.authedClient.get("/api/v1/auth/me");
|
|
302
|
+
this._currentUser = user;
|
|
303
|
+
this.saveToStorage();
|
|
304
|
+
this.notifyListeners();
|
|
305
|
+
return user;
|
|
306
|
+
} catch (error) {
|
|
307
|
+
if (error instanceof MitraApiError && error.status === 401) {
|
|
308
|
+
this.clearAuthState();
|
|
309
|
+
}
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Validates the current session with the server.
|
|
315
|
+
*
|
|
316
|
+
* @returns `true` if the session is valid, `false` otherwise.
|
|
317
|
+
*
|
|
318
|
+
* @example
|
|
319
|
+
* ```typescript
|
|
320
|
+
* const valid = await mitra.auth.checkAuth();
|
|
321
|
+
* if (!valid) mitra.auth.redirectToLogin();
|
|
322
|
+
* ```
|
|
323
|
+
*/
|
|
324
|
+
async checkAuth() {
|
|
325
|
+
return await this.me() !== null;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Sets the access token manually (e.g., from SSO/OAuth callback).
|
|
329
|
+
*
|
|
330
|
+
* Call `me()` afterwards to fetch the associated user data.
|
|
331
|
+
*
|
|
332
|
+
* @param token - JWT access token.
|
|
333
|
+
* @param saveToStorage - Whether to persist to localStorage (default: true).
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```typescript
|
|
337
|
+
* mitra.auth.setToken(tokenFromCallback);
|
|
338
|
+
* await mitra.auth.me();
|
|
339
|
+
* ```
|
|
340
|
+
*/
|
|
341
|
+
setToken(token, saveToStorage = true) {
|
|
342
|
+
this._accessToken = token;
|
|
343
|
+
if (saveToStorage) {
|
|
344
|
+
this.saveToStorage();
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Redirects to `/login?returnUrl=...` for unauthenticated users.
|
|
349
|
+
*
|
|
350
|
+
* @param returnUrl - URL to return to after login (default: '/').
|
|
351
|
+
*
|
|
352
|
+
* @example
|
|
353
|
+
* ```typescript
|
|
354
|
+
* if (!mitra.auth.isAuthenticated) {
|
|
355
|
+
* mitra.auth.redirectToLogin(window.location.pathname);
|
|
356
|
+
* }
|
|
357
|
+
* ```
|
|
358
|
+
*/
|
|
359
|
+
redirectToLogin(returnUrl = "/") {
|
|
360
|
+
if (globalThis.window === void 0) return;
|
|
361
|
+
globalThis.window.location.href = `/login?returnUrl=${encodeURIComponent(returnUrl)}`;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Registers a callback for auth state changes.
|
|
365
|
+
*
|
|
366
|
+
* Called immediately with the current state, then on every sign-in/sign-out.
|
|
367
|
+
*
|
|
368
|
+
* @param callback - Receives the User on login, null on logout.
|
|
369
|
+
* @returns Unsubscribe function.
|
|
370
|
+
*
|
|
371
|
+
* @example
|
|
372
|
+
* ```typescript
|
|
373
|
+
* useEffect(() => {
|
|
374
|
+
* const unsub = mitra.auth.onAuthStateChange((user) => {
|
|
375
|
+
* setUser(user);
|
|
376
|
+
* setLoading(false);
|
|
377
|
+
* });
|
|
378
|
+
* return unsub;
|
|
379
|
+
* }, []);
|
|
380
|
+
* ```
|
|
381
|
+
*/
|
|
382
|
+
onAuthStateChange(callback) {
|
|
383
|
+
this.listeners.add(callback);
|
|
384
|
+
callback(this._currentUser);
|
|
385
|
+
return () => {
|
|
386
|
+
this.listeners.delete(callback);
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
async doRefresh() {
|
|
390
|
+
try {
|
|
391
|
+
const tokenResponse = await this.publicClient.post(
|
|
392
|
+
"/api/v1/auth/tokens/refresh",
|
|
393
|
+
{ refreshToken: this._refreshToken }
|
|
394
|
+
);
|
|
395
|
+
this._accessToken = tokenResponse.accessToken;
|
|
396
|
+
this._refreshToken = tokenResponse.refreshToken;
|
|
397
|
+
const user = await this.authedClient.get("/api/v1/auth/me");
|
|
398
|
+
this.setAuthState(user, tokenResponse.accessToken, tokenResponse.refreshToken);
|
|
399
|
+
return true;
|
|
400
|
+
} catch {
|
|
401
|
+
this.clearAuthState();
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
setAuthState(user, token, refreshToken) {
|
|
406
|
+
this._currentUser = user;
|
|
407
|
+
this._accessToken = token;
|
|
408
|
+
this._refreshToken = refreshToken;
|
|
409
|
+
this.saveToStorage();
|
|
410
|
+
this.notifyListeners();
|
|
411
|
+
}
|
|
412
|
+
clearAuthState() {
|
|
413
|
+
this._currentUser = null;
|
|
414
|
+
this._accessToken = null;
|
|
415
|
+
this._refreshToken = null;
|
|
416
|
+
this.removeFromStorage();
|
|
417
|
+
this.notifyListeners();
|
|
418
|
+
}
|
|
419
|
+
notifyListeners() {
|
|
420
|
+
this.listeners.forEach((callback) => {
|
|
421
|
+
try {
|
|
422
|
+
callback(this._currentUser);
|
|
423
|
+
} catch (error) {
|
|
424
|
+
console.error("Auth state change listener error:", error);
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
saveToStorage() {
|
|
429
|
+
if (typeof localStorage === "undefined") return;
|
|
430
|
+
try {
|
|
431
|
+
localStorage.setItem(
|
|
432
|
+
this.storageKey,
|
|
433
|
+
JSON.stringify({
|
|
434
|
+
user: this._currentUser,
|
|
435
|
+
token: this._accessToken,
|
|
436
|
+
refreshToken: this._refreshToken
|
|
437
|
+
})
|
|
438
|
+
);
|
|
439
|
+
} catch {
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
loadFromStorage() {
|
|
443
|
+
if (typeof localStorage === "undefined") return;
|
|
444
|
+
try {
|
|
445
|
+
const stored = localStorage.getItem(this.storageKey);
|
|
446
|
+
if (stored) {
|
|
447
|
+
const { user, token, refreshToken } = JSON.parse(stored);
|
|
448
|
+
this._currentUser = user;
|
|
449
|
+
this._accessToken = token;
|
|
450
|
+
this._refreshToken = refreshToken ?? null;
|
|
451
|
+
}
|
|
452
|
+
} catch {
|
|
453
|
+
this.removeFromStorage();
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
removeFromStorage() {
|
|
457
|
+
if (typeof localStorage === "undefined") return;
|
|
458
|
+
try {
|
|
459
|
+
localStorage.removeItem(this.storageKey);
|
|
460
|
+
} catch {
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
// src/modules/entities.ts
|
|
466
|
+
var EntitiesModule = class _EntitiesModule {
|
|
467
|
+
httpClient;
|
|
468
|
+
dataSourceId;
|
|
469
|
+
tableProxies = /* @__PURE__ */ new Map();
|
|
470
|
+
constructor(httpClient, dataSourceId) {
|
|
471
|
+
this.httpClient = httpClient;
|
|
472
|
+
this.dataSourceId = dataSourceId;
|
|
473
|
+
}
|
|
474
|
+
static createProxy(httpClient, dataSourceId) {
|
|
475
|
+
const instance = new _EntitiesModule(httpClient, dataSourceId);
|
|
476
|
+
return new Proxy(instance, {
|
|
477
|
+
get: (target, prop) => {
|
|
478
|
+
if (prop in target) {
|
|
479
|
+
return target[prop];
|
|
480
|
+
}
|
|
481
|
+
return target.getTable(prop);
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
setDataSourceId(dataSourceId) {
|
|
486
|
+
this.dataSourceId = dataSourceId;
|
|
487
|
+
this.tableProxies.clear();
|
|
488
|
+
}
|
|
489
|
+
getTable(tableName) {
|
|
490
|
+
if (!this.tableProxies.has(tableName)) {
|
|
491
|
+
this.tableProxies.set(tableName, this.createTableAccessor(tableName));
|
|
492
|
+
}
|
|
493
|
+
return this.tableProxies.get(tableName);
|
|
494
|
+
}
|
|
495
|
+
createTableAccessor(tableName) {
|
|
496
|
+
const basePath = `/api/v1/data-sources/${this.dataSourceId}/tables/${tableName}/records`;
|
|
497
|
+
return {
|
|
498
|
+
list: async (sortOrOptions, limit, skip, fields) => {
|
|
499
|
+
let params;
|
|
500
|
+
if (typeof sortOrOptions === "string" || sortOrOptions === void 0) {
|
|
501
|
+
params = {
|
|
502
|
+
sort: sortOrOptions,
|
|
503
|
+
limit,
|
|
504
|
+
skip,
|
|
505
|
+
fields: fields?.join(",")
|
|
506
|
+
};
|
|
507
|
+
} else {
|
|
508
|
+
params = {
|
|
509
|
+
sort: sortOrOptions.sort,
|
|
510
|
+
limit: sortOrOptions.limit,
|
|
511
|
+
skip: sortOrOptions.skip,
|
|
512
|
+
fields: sortOrOptions.fields?.join(",")
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
const response = await this.httpClient.get(basePath, params);
|
|
516
|
+
return response.data;
|
|
517
|
+
},
|
|
518
|
+
filter: async (query, sort, limit, skip, fields) => {
|
|
519
|
+
const params = {
|
|
520
|
+
q: JSON.stringify(query),
|
|
521
|
+
sort,
|
|
522
|
+
limit,
|
|
523
|
+
skip,
|
|
524
|
+
fields: fields?.join(",")
|
|
525
|
+
};
|
|
526
|
+
const response = await this.httpClient.get(basePath, params);
|
|
527
|
+
return response.data;
|
|
528
|
+
},
|
|
529
|
+
get: async (id) => {
|
|
530
|
+
return this.httpClient.get(`${basePath}/${id}`);
|
|
531
|
+
},
|
|
532
|
+
create: async (data) => {
|
|
533
|
+
return this.httpClient.post(basePath, data);
|
|
534
|
+
},
|
|
535
|
+
update: async (id, data) => {
|
|
536
|
+
return this.httpClient.put(`${basePath}/${id}`, data);
|
|
537
|
+
},
|
|
538
|
+
delete: async (id) => {
|
|
539
|
+
return this.httpClient.delete(`${basePath}/${id}`);
|
|
540
|
+
},
|
|
541
|
+
deleteMany: async (query) => {
|
|
542
|
+
return this.httpClient.delete(basePath, {
|
|
543
|
+
q: JSON.stringify(query)
|
|
544
|
+
});
|
|
545
|
+
},
|
|
546
|
+
bulkCreate: async (data) => {
|
|
547
|
+
return this.httpClient.post(`${basePath}/bulk`, data);
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
// src/modules/functions.ts
|
|
554
|
+
var FunctionsModule = class {
|
|
555
|
+
httpClient;
|
|
556
|
+
constructor(httpClient) {
|
|
557
|
+
this.httpClient = httpClient;
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Executes a serverless function by ID.
|
|
561
|
+
*
|
|
562
|
+
* Triggers the function's current published version with the provided input.
|
|
563
|
+
*
|
|
564
|
+
* @param functionId - UUID of the function to execute.
|
|
565
|
+
* @param input - Input data to pass to the function.
|
|
566
|
+
* @returns The execution result with status, output, and metadata.
|
|
567
|
+
* @throws {MitraApiError} On function not found (404) or unauthorized (401).
|
|
568
|
+
*
|
|
569
|
+
* @example
|
|
570
|
+
* ```typescript
|
|
571
|
+
* const execution = await mitra.functions.execute('fn-id', { key: 'value' });
|
|
572
|
+
* console.log(execution.status, execution.output);
|
|
573
|
+
* ```
|
|
574
|
+
*/
|
|
575
|
+
async execute(functionId, input) {
|
|
576
|
+
return this.httpClient.post(
|
|
577
|
+
`/api/v1/functions/${functionId}/execute`,
|
|
578
|
+
input ? { input } : void 0
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
// src/modules/integration.ts
|
|
584
|
+
var IntegrationModule = class {
|
|
585
|
+
httpClient;
|
|
586
|
+
constructor(httpClient) {
|
|
587
|
+
this.httpClient = httpClient;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Executes a pre-defined integration resource by ID.
|
|
591
|
+
*
|
|
592
|
+
* The resource's endpoint, method, and body are resolved server-side
|
|
593
|
+
* using the provided parameters. Only declared parameters can be passed.
|
|
594
|
+
*
|
|
595
|
+
* @param resourceId - UUID of the integration resource.
|
|
596
|
+
* @param params - Named parameters declared in the resource's params schema.
|
|
597
|
+
* @returns Proxy result with status, headers, body, and execution metadata.
|
|
598
|
+
* @throws {MitraApiError} On resource not found (404) or external API failure.
|
|
599
|
+
*
|
|
600
|
+
* @example
|
|
601
|
+
* ```typescript
|
|
602
|
+
* const result = await mitra.integration.executeResource('resource-id', {
|
|
603
|
+
* descricao: 'Notebook',
|
|
604
|
+
* limit: 10,
|
|
605
|
+
* });
|
|
606
|
+
* console.log(result.body);
|
|
607
|
+
* ```
|
|
608
|
+
*/
|
|
609
|
+
async executeResource(resourceId, params) {
|
|
610
|
+
return this.httpClient.post(
|
|
611
|
+
`/api/v1/proxy/resources/${resourceId}/execute`,
|
|
612
|
+
{ params }
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Executes a proxied HTTP request through an integration config.
|
|
617
|
+
*
|
|
618
|
+
* The Mitra server handles authentication and injects credentials automatically.
|
|
619
|
+
* Note: integrations configured with RESOURCE_ONLY mode will block direct proxy access.
|
|
620
|
+
*
|
|
621
|
+
* @param configId - UUID of the integration config.
|
|
622
|
+
* @param request - The HTTP request to proxy (method, endpoint, body, etc.).
|
|
623
|
+
* @returns Proxy result with status, headers, body, and execution metadata.
|
|
624
|
+
* @throws {MitraApiError} On config not found (404) or external API failure.
|
|
625
|
+
*/
|
|
626
|
+
async execute(configId, request) {
|
|
627
|
+
return this.httpClient.post(
|
|
628
|
+
`/api/v1/proxy/template-configs/${configId}/execute`,
|
|
629
|
+
{ ...request, source: "SDK" }
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
// src/modules/queries.ts
|
|
635
|
+
var QueriesModule = class {
|
|
636
|
+
httpClient;
|
|
637
|
+
dataSourceId = "";
|
|
638
|
+
constructor(httpClient) {
|
|
639
|
+
this.httpClient = httpClient;
|
|
640
|
+
}
|
|
641
|
+
/** @internal Called by client.init() to set the resolved data source. */
|
|
642
|
+
setDataSourceId(dataSourceId) {
|
|
643
|
+
this.dataSourceId = dataSourceId;
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Executes a named query.
|
|
647
|
+
*
|
|
648
|
+
* @param id - UUID of the custom query.
|
|
649
|
+
* @param parameters - Named parameters for the prepared statement.
|
|
650
|
+
* @returns Query result with rows and affected row count.
|
|
651
|
+
* @throws {MitraApiError} On query not found (404).
|
|
652
|
+
*
|
|
653
|
+
* @example
|
|
654
|
+
* ```typescript
|
|
655
|
+
* const result = await mitra.queries.execute('query-id', { status: 'active' });
|
|
656
|
+
* console.log(`Found ${result.rows.length} rows`);
|
|
657
|
+
* ```
|
|
658
|
+
*/
|
|
659
|
+
async execute(id, parameters) {
|
|
660
|
+
return this.httpClient.post(
|
|
661
|
+
`/api/v1/custom-queries/${id}/execute`,
|
|
662
|
+
{ datasourceId: this.dataSourceId, parameters }
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
// src/client.ts
|
|
668
|
+
function createClient(config) {
|
|
669
|
+
const { appId, apiUrl, onError } = config;
|
|
670
|
+
const iamUrl = `${apiUrl}/iam`;
|
|
671
|
+
const dataManagerUrl = `${apiUrl}/data-manager`;
|
|
672
|
+
const functionsUrl = `${apiUrl}/functions`;
|
|
673
|
+
const integrationUrl = `${apiUrl}/integration`;
|
|
674
|
+
const codeStudioUrl = `${apiUrl}/code-studio`;
|
|
675
|
+
const authModule = new AuthModule(appId, iamUrl);
|
|
676
|
+
const onUnauthorized = () => authModule.refreshSession();
|
|
677
|
+
const defaultHeaders = { "X-App-Id": appId };
|
|
678
|
+
const httpClient = new HttpClient({
|
|
679
|
+
baseUrl: dataManagerUrl,
|
|
680
|
+
getToken: () => authModule.accessToken,
|
|
681
|
+
onUnauthorized,
|
|
682
|
+
onError,
|
|
683
|
+
defaultHeaders
|
|
684
|
+
});
|
|
685
|
+
const entitiesModule = EntitiesModule.createProxy(httpClient, "");
|
|
686
|
+
const functionsHttpClient = new HttpClient({
|
|
687
|
+
baseUrl: functionsUrl,
|
|
688
|
+
getToken: () => authModule.accessToken,
|
|
689
|
+
onUnauthorized,
|
|
690
|
+
onError,
|
|
691
|
+
defaultHeaders
|
|
692
|
+
});
|
|
693
|
+
const functionsModule = new FunctionsModule(functionsHttpClient);
|
|
694
|
+
const integrationHttpClient = new HttpClient({
|
|
695
|
+
baseUrl: integrationUrl,
|
|
696
|
+
getToken: () => authModule.accessToken,
|
|
697
|
+
onUnauthorized,
|
|
698
|
+
onError,
|
|
699
|
+
defaultHeaders
|
|
700
|
+
});
|
|
701
|
+
const integrationModule = new IntegrationModule(integrationHttpClient);
|
|
702
|
+
const queriesModule = new QueriesModule(httpClient);
|
|
703
|
+
let initialized = false;
|
|
704
|
+
let allowSignup = true;
|
|
705
|
+
async function init() {
|
|
706
|
+
if (initialized) return;
|
|
707
|
+
const publicClient = new HttpClient({
|
|
708
|
+
baseUrl: codeStudioUrl,
|
|
709
|
+
getToken: () => null
|
|
710
|
+
});
|
|
711
|
+
const appInfo = await publicClient.get(
|
|
712
|
+
`/api/v1/apps/${appId}/info`
|
|
713
|
+
);
|
|
714
|
+
entitiesModule.setDataSourceId(appInfo.dataSourceId);
|
|
715
|
+
queriesModule.setDataSourceId(appInfo.dataSourceId);
|
|
716
|
+
allowSignup = appInfo.allowSignup;
|
|
717
|
+
initialized = true;
|
|
718
|
+
}
|
|
719
|
+
return {
|
|
720
|
+
init,
|
|
721
|
+
auth: authModule,
|
|
722
|
+
entities: entitiesModule,
|
|
723
|
+
functions: functionsModule,
|
|
724
|
+
integration: integrationModule,
|
|
725
|
+
queries: queriesModule,
|
|
726
|
+
get allowSignup() {
|
|
727
|
+
return allowSignup;
|
|
728
|
+
},
|
|
729
|
+
config
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
export {
|
|
733
|
+
MitraApiError,
|
|
734
|
+
createClient
|
|
735
|
+
};
|