@mitralab.io/platform-sdk 1.0.7 → 1.0.9

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.
@@ -1,4 +1,69 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ MitraApiError: () => MitraApiError,
24
+ createClient: () => createClient
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/client.ts
29
+ var import_sdk_core6 = require("@mitralab.io/sdk-core");
30
+
1
31
  // src/utils/http-client.ts
32
+ var bearerCredentialPattern = /(Bearer\s+)\S+/gi;
33
+ function redactText(value, currentToken) {
34
+ const withoutBearerCredentials = value.replace(bearerCredentialPattern, "$1[REDACTED]");
35
+ return currentToken ? withoutBearerCredentials.split(currentToken).join("[REDACTED]") : withoutBearerCredentials;
36
+ }
37
+ function redactDetails(value, currentToken) {
38
+ if (typeof value === "string") return redactText(value, currentToken);
39
+ if (Array.isArray(value)) return value.map((item) => redactDetails(item, currentToken));
40
+ if (value && typeof value === "object") {
41
+ return Object.fromEntries(
42
+ Object.entries(value).map(([key, entry]) => [
43
+ redactText(key, currentToken),
44
+ redactDetails(entry, currentToken)
45
+ ])
46
+ );
47
+ }
48
+ return value;
49
+ }
50
+ function asErrorPayload(value) {
51
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
52
+ return value;
53
+ }
54
+ function optionalString(value) {
55
+ return typeof value === "string" ? value : void 0;
56
+ }
57
+ function buildRequestUrl(baseUrl, path, params) {
58
+ const url = `${baseUrl}${path}`;
59
+ if (!params) return url;
60
+ const searchParams = new URLSearchParams();
61
+ Object.entries(params).forEach(([key, value]) => {
62
+ if (value !== void 0) searchParams.append(key, String(value));
63
+ });
64
+ const queryString = searchParams.toString();
65
+ return queryString ? `${url}?${queryString}` : url;
66
+ }
2
67
  var HttpClient = class {
3
68
  baseUrl;
4
69
  tokenGetter;
@@ -37,19 +102,7 @@ var HttpClient = class {
37
102
  */
38
103
  async request(path, options = {}) {
39
104
  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
- }
105
+ const url = buildRequestUrl(this.baseUrl, path, params);
53
106
  const requestHeaders = {
54
107
  "Content-Type": "application/json",
55
108
  ...this.defaultHeaders,
@@ -62,8 +115,18 @@ var HttpClient = class {
62
115
  const response = await fetch(url, {
63
116
  method,
64
117
  headers: requestHeaders,
65
- body: body ? JSON.stringify(body) : void 0
118
+ body: body ? JSON.stringify(body) : void 0,
119
+ redirect: "manual"
66
120
  });
121
+ if (response.redirected || response.type === "opaqueredirect") {
122
+ const error = new MitraApiError(
123
+ "Redirected responses are not allowed",
124
+ response.status,
125
+ "REDIRECT_NOT_ALLOWED"
126
+ );
127
+ this.onError?.(error);
128
+ throw error;
129
+ }
67
130
  if (!response.ok) {
68
131
  if (response.status === 401 && !isRetry && this.onUnauthorized) {
69
132
  const refreshed = await this.onUnauthorized();
@@ -72,11 +135,14 @@ var HttpClient = class {
72
135
  }
73
136
  }
74
137
  const errorBody = await response.json().catch(() => ({}));
138
+ const errorPayload = asErrorPayload(errorBody);
139
+ const rawMessage = optionalString(errorPayload.message);
140
+ const rawCode = optionalString(errorPayload.error_code);
75
141
  const error = new MitraApiError(
76
- errorBody.message || `Request failed with status ${response.status}`,
142
+ redactText(rawMessage || `Request failed with status ${response.status}`, token),
77
143
  response.status,
78
- errorBody.error_code,
79
- errorBody
144
+ rawCode === void 0 ? void 0 : redactText(rawCode, token),
145
+ redactDetails(errorBody, token)
80
146
  );
81
147
  this.onError?.(error);
82
148
  throw error;
@@ -157,22 +223,31 @@ var MitraApiError = class extends Error {
157
223
  }
158
224
  };
159
225
 
226
+ // src/core-errors.ts
227
+ var coreErrors = {
228
+ configuration: (message) => new MitraApiError(message, 0, "INVALID_CONFIGURATION"),
229
+ invalidResponse: (message) => new MitraApiError(message, 200, "INVALID_RESPONSE")
230
+ };
231
+
160
232
  // src/modules/auth.ts
233
+ var import_sdk_core = require("@mitralab.io/sdk-core");
161
234
  var AuthModule = class {
162
235
  appId;
163
236
  _currentUser = null;
164
- _accessToken = null;
165
- _refreshToken = null;
237
+ #accessToken = null;
238
+ #refreshToken = null;
166
239
  refreshPromise = null;
167
240
  listeners = /* @__PURE__ */ new Set();
168
241
  storageKey;
169
242
  publicClient;
170
243
  authedClient;
244
+ currentUserApi;
171
245
  constructor(appId, iamBaseUrl) {
172
246
  this.appId = appId;
173
247
  this.storageKey = `mitra_auth_${appId}`;
174
248
  this.publicClient = new HttpClient({ baseUrl: iamBaseUrl, getToken: () => null });
175
- this.authedClient = new HttpClient({ baseUrl: iamBaseUrl, getToken: () => this._accessToken });
249
+ this.authedClient = new HttpClient({ baseUrl: iamBaseUrl, getToken: () => this.#accessToken });
250
+ this.currentUserApi = (0, import_sdk_core.createAuthModule)(this.authedClient, coreErrors);
176
251
  this.loadFromStorage();
177
252
  }
178
253
  /** The currently authenticated user, or null. */
@@ -181,11 +256,11 @@ var AuthModule = class {
181
256
  }
182
257
  /** The current JWT access token, or null. */
183
258
  get accessToken() {
184
- return this._accessToken;
259
+ return this.#accessToken;
185
260
  }
186
261
  /** Whether a user is currently authenticated (local check, not server-validated). */
187
262
  get isAuthenticated() {
188
- return this._currentUser !== null && this._accessToken !== null;
263
+ return this._currentUser !== null && this.#accessToken !== null;
189
264
  }
190
265
  /**
191
266
  * Signs in a user with email and password.
@@ -210,9 +285,9 @@ var AuthModule = class {
210
285
  "/api/v1/auth/login",
211
286
  { ...credentials, appId: this.appId }
212
287
  );
213
- this._accessToken = tokenResponse.accessToken;
214
- this._refreshToken = tokenResponse.refreshToken;
215
- const user = await this.authedClient.get("/api/v1/auth/me");
288
+ this.#accessToken = tokenResponse.accessToken;
289
+ this.#refreshToken = tokenResponse.refreshToken;
290
+ const user = await this.getCurrentUser();
216
291
  this.setAuthState(user, tokenResponse.accessToken, tokenResponse.refreshToken);
217
292
  return user;
218
293
  }
@@ -272,7 +347,7 @@ var AuthModule = class {
272
347
  * ```
273
348
  */
274
349
  async refreshSession() {
275
- if (!this._refreshToken) return false;
350
+ if (!this.#refreshToken) return false;
276
351
  if (this.refreshPromise) return this.refreshPromise;
277
352
  this.refreshPromise = this.doRefresh();
278
353
  try {
@@ -296,9 +371,9 @@ var AuthModule = class {
296
371
  * ```
297
372
  */
298
373
  async me() {
299
- if (!this._accessToken) return null;
374
+ if (!this.#accessToken) return null;
300
375
  try {
301
- const user = await this.authedClient.get("/api/v1/auth/me");
376
+ const user = await this.getCurrentUser();
302
377
  this._currentUser = user;
303
378
  this.saveToStorage();
304
379
  this.notifyListeners();
@@ -339,7 +414,7 @@ var AuthModule = class {
339
414
  * ```
340
415
  */
341
416
  setToken(token, saveToStorage = true) {
342
- this._accessToken = token;
417
+ this.#accessToken = token;
343
418
  if (saveToStorage) {
344
419
  this.saveToStorage();
345
420
  }
@@ -390,11 +465,11 @@ var AuthModule = class {
390
465
  try {
391
466
  const tokenResponse = await this.publicClient.post(
392
467
  "/api/v1/auth/refresh-token",
393
- { refreshToken: this._refreshToken }
468
+ { refreshToken: this.#refreshToken }
394
469
  );
395
- this._accessToken = tokenResponse.accessToken;
396
- this._refreshToken = tokenResponse.refreshToken;
397
- const user = await this.authedClient.get("/api/v1/auth/me");
470
+ this.#accessToken = tokenResponse.accessToken;
471
+ this.#refreshToken = tokenResponse.refreshToken;
472
+ const user = await this.getCurrentUser();
398
473
  this.setAuthState(user, tokenResponse.accessToken, tokenResponse.refreshToken);
399
474
  return true;
400
475
  } catch {
@@ -404,15 +479,19 @@ var AuthModule = class {
404
479
  }
405
480
  setAuthState(user, token, refreshToken) {
406
481
  this._currentUser = user;
407
- this._accessToken = token;
408
- this._refreshToken = refreshToken;
482
+ this.#accessToken = token;
483
+ this.#refreshToken = refreshToken;
409
484
  this.saveToStorage();
410
485
  this.notifyListeners();
411
486
  }
487
+ async getCurrentUser() {
488
+ const user = await this.currentUserApi.me();
489
+ return { ...user, tenantId: user.tenant.id };
490
+ }
412
491
  clearAuthState() {
413
492
  this._currentUser = null;
414
- this._accessToken = null;
415
- this._refreshToken = null;
493
+ this.#accessToken = null;
494
+ this.#refreshToken = null;
416
495
  this.removeFromStorage();
417
496
  this.notifyListeners();
418
497
  }
@@ -432,8 +511,8 @@ var AuthModule = class {
432
511
  this.storageKey,
433
512
  JSON.stringify({
434
513
  user: this._currentUser,
435
- token: this._accessToken,
436
- refreshToken: this._refreshToken
514
+ token: this.#accessToken,
515
+ refreshToken: this.#refreshToken
437
516
  })
438
517
  );
439
518
  } catch {
@@ -446,8 +525,8 @@ var AuthModule = class {
446
525
  if (stored) {
447
526
  const { user, token, refreshToken } = JSON.parse(stored);
448
527
  this._currentUser = user;
449
- this._accessToken = token;
450
- this._refreshToken = refreshToken ?? null;
528
+ this.#accessToken = token;
529
+ this.#refreshToken = refreshToken ?? null;
451
530
  }
452
531
  } catch {
453
532
  this.removeFromStorage();
@@ -463,208 +542,113 @@ var AuthModule = class {
463
542
  };
464
543
 
465
544
  // src/modules/entities.ts
545
+ var import_sdk_core2 = require("@mitralab.io/sdk-core");
466
546
  var EntitiesModule = class _EntitiesModule {
467
- httpClient;
468
- dataSourceId;
469
- tableProxies = /* @__PURE__ */ new Map();
470
- constructor(httpClient, dataSourceId) {
547
+ constructor(httpClient, _dataSourceId) {
471
548
  this.httpClient = httpClient;
472
- this.dataSourceId = dataSourceId;
549
+ this.core = (0, import_sdk_core2.createEntitiesModule)(httpClient, coreErrors);
473
550
  }
551
+ core;
474
552
  static createProxy(httpClient, dataSourceId) {
475
553
  const instance = new _EntitiesModule(httpClient, dataSourceId);
476
554
  return new Proxy(instance, {
477
- get: (target, prop) => {
478
- if (prop in target) {
479
- return target[prop];
555
+ get(target, property, receiver) {
556
+ if (typeof property !== "string" || property in target) {
557
+ return Reflect.get(target, property, receiver);
480
558
  }
481
- return target.getTable(prop);
559
+ return target.getTable(property);
482
560
  }
483
561
  });
484
562
  }
485
- setDataSourceId(dataSourceId) {
486
- this.dataSourceId = dataSourceId;
487
- this.tableProxies.clear();
563
+ /**
564
+ * Preserved for Platform SDK 1.x compatibility.
565
+ * Records now resolve the app from authenticated context instead of a data source path.
566
+ */
567
+ setDataSourceId(_dataSourceId) {
568
+ this.core = (0, import_sdk_core2.createEntitiesModule)(this.httpClient, coreErrors);
488
569
  }
489
570
  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
- };
571
+ return this.core.getTable(tableName);
550
572
  }
551
573
  };
552
574
 
553
575
  // src/modules/functions.ts
576
+ var import_sdk_core3 = require("@mitralab.io/sdk-core");
554
577
  var FunctionsModule = class {
555
- httpClient;
578
+ core;
556
579
  constructor(httpClient) {
557
- this.httpClient = httpClient;
580
+ this.core = (0, import_sdk_core3.createFunctionsModule)(httpClient, { emptyInput: "omit-body" }, coreErrors);
558
581
  }
559
582
  /**
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
- * ```
583
+ * Executes a Function using the Platform SDK 1.x server-default invocation semantics.
584
+ * The runtime SDK uses an explicit invocation header instead.
574
585
  */
575
586
  async execute(functionId, input) {
576
- return this.httpClient.post(
577
- `/api/v1/functions/${functionId}/execute`,
578
- input ? { input } : void 0
579
- );
587
+ const execution = await this.core.execute(functionId, input);
588
+ if (execution.input === null) {
589
+ throw coreErrors.invalidResponse(
590
+ "Function execution response has an invalid input field"
591
+ );
592
+ }
593
+ return { ...execution, input: execution.input };
580
594
  }
581
595
  };
582
596
 
583
597
  // src/modules/integration.ts
598
+ var import_sdk_core4 = require("@mitralab.io/sdk-core");
584
599
  var IntegrationModule = class {
585
- httpClient;
600
+ core;
586
601
  constructor(httpClient) {
587
- this.httpClient = httpClient;
602
+ this.core = (0, import_sdk_core4.createIntegrationModule)(httpClient, coreErrors);
588
603
  }
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
- );
604
+ executeResource(resourceId, params) {
605
+ return this.core.executeResource(resourceId, params);
614
606
  }
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
- );
607
+ execute(configId, request) {
608
+ return this.core.execute(configId, request);
631
609
  }
632
610
  };
633
611
 
634
612
  // src/modules/queries.ts
613
+ var import_sdk_core5 = require("@mitralab.io/sdk-core");
635
614
  var QueriesModule = class {
636
- httpClient;
637
615
  dataSourceId = "";
616
+ core;
638
617
  constructor(httpClient) {
639
- this.httpClient = httpClient;
618
+ this.core = (0, import_sdk_core5.createQueriesModule)(httpClient, () => this.dataSourceId, coreErrors);
640
619
  }
641
- /** @internal Called by client.init() to set the resolved data source. */
620
+ /** Called by `client.init()` to set the app's resolved data source. */
642
621
  setDataSourceId(dataSourceId) {
643
622
  this.dataSourceId = dataSourceId;
644
623
  }
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
624
  async execute(id, parameters) {
660
- return this.httpClient.post(
661
- `/api/v1/custom-queries/${id}/execute`,
662
- { datasourceId: this.dataSourceId, parameters }
663
- );
625
+ const result = await this.core.execute(id, parameters);
626
+ return { ...result, affectedRows: result.affectedRows ?? null };
664
627
  }
665
628
  };
666
629
 
667
630
  // src/client.ts
631
+ function expectAppInfoResponse(value) {
632
+ const response = (0, import_sdk_core6.expectObject)(
633
+ value,
634
+ "App info response",
635
+ coreErrors
636
+ );
637
+ if (typeof response.dataSourceId !== "string" || !response.dataSourceId.trim()) {
638
+ throw coreErrors.invalidResponse(
639
+ "App info response has an invalid dataSourceId field"
640
+ );
641
+ }
642
+ if (typeof response.allowSignup !== "boolean") {
643
+ throw coreErrors.invalidResponse(
644
+ "App info response has an invalid allowSignup field"
645
+ );
646
+ }
647
+ return {
648
+ dataSourceId: response.dataSourceId,
649
+ allowSignup: response.allowSignup
650
+ };
651
+ }
668
652
  function createClient(config) {
669
653
  const { appId, apiUrl, onError } = config;
670
654
  const iamUrl = `${apiUrl}/iam`;
@@ -708,8 +692,10 @@ function createClient(config) {
708
692
  baseUrl: codeStudioUrl,
709
693
  getToken: () => null
710
694
  });
711
- const appInfo = await publicClient.get(
712
- `/api/v1/apps/${appId}/info`
695
+ const appInfo = expectAppInfoResponse(
696
+ await publicClient.get(
697
+ `/api/v1/apps/${(0, import_sdk_core6.encodePathSegment)(appId, "appId", coreErrors)}/info`
698
+ )
713
699
  );
714
700
  entitiesModule.setDataSourceId(appInfo.dataSourceId);
715
701
  queriesModule.setDataSourceId(appInfo.dataSourceId);
@@ -729,7 +715,8 @@ function createClient(config) {
729
715
  config
730
716
  };
731
717
  }
732
- export {
718
+ // Annotate the CommonJS export names for ESM import in node:
719
+ 0 && (module.exports = {
733
720
  MitraApiError,
734
721
  createClient
735
- };
722
+ });