@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/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
+ };