@mitralab.io/platform-sdk 1.0.8 → 1.1.0-beta.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 +49 -0
- package/LICENSE +21 -0
- package/README.md +251 -108
- package/dist/index.cjs +1835 -0
- package/dist/index.d.cts +966 -0
- package/dist/index.d.ts +409 -102
- package/dist/index.js +1238 -160
- package/package.json +29 -11
- package/dist/index.d.mts +0 -659
- package/dist/index.mjs +0 -706
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1835 @@
|
|
|
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
|
+
callIntegrationMitra: () => import_mitra_interactions_sdk14.callIntegrationMitra,
|
|
25
|
+
configureSdkMitra: () => import_mitra_interactions_sdk3.configureSdkMitra,
|
|
26
|
+
createClient: () => createClient,
|
|
27
|
+
createMitraInstance: () => import_mitra_interactions_sdk4.createMitraInstance,
|
|
28
|
+
createRecordMitra: () => import_mitra_interactions_sdk18.createRecordMitra,
|
|
29
|
+
createRecordsBatchMitra: () => import_mitra_interactions_sdk19.createRecordsBatchMitra,
|
|
30
|
+
deleteRecordMitra: () => import_mitra_interactions_sdk22.deleteRecordMitra,
|
|
31
|
+
exchangeSsoCodeMitra: () => exchangeSsoCodeMitra,
|
|
32
|
+
executePublicServerFunctionAsyncMitra: () => import_mitra_interactions_sdk11.executePublicServerFunctionAsyncMitra,
|
|
33
|
+
executePublicServerFunctionMitra: () => import_mitra_interactions_sdk10.executePublicServerFunctionMitra,
|
|
34
|
+
executeServerFunctionAsyncMitra: () => import_mitra_interactions_sdk9.executeServerFunctionAsyncMitra,
|
|
35
|
+
executeServerFunctionMitra: () => import_mitra_interactions_sdk8.executeServerFunctionMitra,
|
|
36
|
+
getAgentTaskMitra: () => import_mitra_interactions_sdk23.getAgentTaskMitra,
|
|
37
|
+
getConfig: () => import_mitra_interactions_sdk5.getConfig,
|
|
38
|
+
getPublicServerFunctionExecutionMitra: () => import_mitra_interactions_sdk12.getPublicServerFunctionExecutionMitra,
|
|
39
|
+
getRecordMitra: () => import_mitra_interactions_sdk17.getRecordMitra,
|
|
40
|
+
listIntegrationsMitra: () => import_mitra_interactions_sdk15.listIntegrationsMitra,
|
|
41
|
+
listRecordsMitra: () => import_mitra_interactions_sdk16.listRecordsMitra,
|
|
42
|
+
loginMitra: () => loginMitra,
|
|
43
|
+
loginWithGoogleMitra: () => loginWithGoogleMitra,
|
|
44
|
+
loginWithMicrosoftMitra: () => loginWithMicrosoftMitra,
|
|
45
|
+
manageAgentChatMitra: () => import_mitra_interactions_sdk24.manageAgentChatMitra,
|
|
46
|
+
manageAgentCredentialMitra: () => import_mitra_interactions_sdk25.manageAgentCredentialMitra,
|
|
47
|
+
patchRecordMitra: () => import_mitra_interactions_sdk21.patchRecordMitra,
|
|
48
|
+
refreshTokenSilently: () => import_mitra_interactions_sdk7.refreshTokenSilently,
|
|
49
|
+
resolveProjectId: () => import_mitra_interactions_sdk6.resolveProjectId,
|
|
50
|
+
stopServerFunctionExecutionMitra: () => import_mitra_interactions_sdk13.stopServerFunctionExecutionMitra,
|
|
51
|
+
updateRecordMitra: () => import_mitra_interactions_sdk20.updateRecordMitra
|
|
52
|
+
});
|
|
53
|
+
module.exports = __toCommonJS(index_exports);
|
|
54
|
+
|
|
55
|
+
// src/utils/url.ts
|
|
56
|
+
function stripTrailingSlashes(value) {
|
|
57
|
+
let end = value.length;
|
|
58
|
+
while (end > 0 && value.codePointAt(end - 1) === 47) end -= 1;
|
|
59
|
+
return value.slice(0, end);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/client.ts
|
|
63
|
+
var import_sdk_core9 = require("@mitralab.io/sdk-core");
|
|
64
|
+
|
|
65
|
+
// src/utils/http-client.ts
|
|
66
|
+
var bearerCredentialPattern = /(Bearer\s+)\S+/gi;
|
|
67
|
+
var sensitiveDetailKeyPattern = /token|authorization|password|secret|api.?key|credential|private.?key/i;
|
|
68
|
+
function isSensitiveDetailKey(key) {
|
|
69
|
+
return sensitiveDetailKeyPattern.test(key);
|
|
70
|
+
}
|
|
71
|
+
function redactText(value, currentToken) {
|
|
72
|
+
const withoutBearerCredentials = value.replace(bearerCredentialPattern, "$1[REDACTED]");
|
|
73
|
+
return currentToken ? withoutBearerCredentials.split(currentToken).join("[REDACTED]") : withoutBearerCredentials;
|
|
74
|
+
}
|
|
75
|
+
function redactDetails(value, currentToken) {
|
|
76
|
+
if (typeof value === "string") return redactText(value, currentToken);
|
|
77
|
+
if (Array.isArray(value)) return value.map((item) => redactDetails(item, currentToken));
|
|
78
|
+
if (value && typeof value === "object") {
|
|
79
|
+
return Object.fromEntries(
|
|
80
|
+
Object.entries(value).map(([key, entry]) => {
|
|
81
|
+
const redactedKey = redactText(key, currentToken);
|
|
82
|
+
return [
|
|
83
|
+
redactedKey,
|
|
84
|
+
isSensitiveDetailKey(redactedKey) ? "[REDACTED]" : redactDetails(entry, currentToken)
|
|
85
|
+
];
|
|
86
|
+
})
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
function asErrorPayload(value) {
|
|
92
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
93
|
+
return value;
|
|
94
|
+
}
|
|
95
|
+
function optionalString(value) {
|
|
96
|
+
return typeof value === "string" ? value : void 0;
|
|
97
|
+
}
|
|
98
|
+
function buildRequestUrl(baseUrl, path, params) {
|
|
99
|
+
const url = `${baseUrl}${path}`;
|
|
100
|
+
if (!params) return url;
|
|
101
|
+
const searchParams = new URLSearchParams();
|
|
102
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
103
|
+
if (value !== void 0) searchParams.append(key, String(value));
|
|
104
|
+
});
|
|
105
|
+
const queryString = searchParams.toString();
|
|
106
|
+
return queryString ? `${url}?${queryString}` : url;
|
|
107
|
+
}
|
|
108
|
+
var HttpClient = class {
|
|
109
|
+
baseUrl;
|
|
110
|
+
tokenGetter;
|
|
111
|
+
beforeAuthenticatedRequest;
|
|
112
|
+
onUnauthorized;
|
|
113
|
+
onError;
|
|
114
|
+
defaultHeaders;
|
|
115
|
+
constructor(config) {
|
|
116
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
117
|
+
this.tokenGetter = config.getToken ?? (() => null);
|
|
118
|
+
this.beforeAuthenticatedRequest = config.beforeAuthenticatedRequest;
|
|
119
|
+
this.onUnauthorized = config.onUnauthorized;
|
|
120
|
+
this.onError = config.onError;
|
|
121
|
+
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Returns the current authentication token.
|
|
125
|
+
* @returns The JWT token if authenticated, null otherwise
|
|
126
|
+
*/
|
|
127
|
+
getToken() {
|
|
128
|
+
return this.tokenGetter();
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Makes an HTTP request with automatic JSON handling and authentication.
|
|
132
|
+
*
|
|
133
|
+
* @param path - API endpoint path (e.g., '/users')
|
|
134
|
+
* @param options - Request options including method, body, headers, and params
|
|
135
|
+
* @returns Promise resolving to the parsed JSON response
|
|
136
|
+
* @throws {MitraApiError} When the API returns an error response
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```typescript
|
|
140
|
+
* const result = await client.request<User>('/users/123', {
|
|
141
|
+
* method: 'PUT',
|
|
142
|
+
* body: { name: 'Updated Name' },
|
|
143
|
+
* });
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
async request(path, options = {}) {
|
|
147
|
+
const { method = "GET", body, headers = {}, params, isRetry } = options;
|
|
148
|
+
const url = buildRequestUrl(this.baseUrl, path, params);
|
|
149
|
+
if (!isRetry && this.tokenGetter() && this.beforeAuthenticatedRequest) {
|
|
150
|
+
await this.beforeAuthenticatedRequest();
|
|
151
|
+
}
|
|
152
|
+
const requestHeaders = {
|
|
153
|
+
"Content-Type": "application/json",
|
|
154
|
+
...this.defaultHeaders,
|
|
155
|
+
...headers
|
|
156
|
+
};
|
|
157
|
+
const token = this.tokenGetter();
|
|
158
|
+
if (token) {
|
|
159
|
+
requestHeaders["Authorization"] = `Bearer ${token}`;
|
|
160
|
+
}
|
|
161
|
+
const response = await fetch(url, {
|
|
162
|
+
method,
|
|
163
|
+
headers: requestHeaders,
|
|
164
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
165
|
+
redirect: "manual"
|
|
166
|
+
});
|
|
167
|
+
if (response.redirected || response.type === "opaqueredirect") {
|
|
168
|
+
const error = new MitraApiError(
|
|
169
|
+
"Redirected responses are not allowed",
|
|
170
|
+
response.status,
|
|
171
|
+
"REDIRECT_NOT_ALLOWED"
|
|
172
|
+
);
|
|
173
|
+
this.onError?.(error);
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
if (!response.ok) {
|
|
177
|
+
if (response.status === 401 && !isRetry && this.onUnauthorized) {
|
|
178
|
+
const refreshed = await this.onUnauthorized(token);
|
|
179
|
+
if (refreshed) {
|
|
180
|
+
return this.request(path, { ...options, isRetry: true });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const error = await this.errorFromResponse(response, token);
|
|
184
|
+
this.onError?.(error);
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
return this.parseResponseBody(response, path);
|
|
188
|
+
}
|
|
189
|
+
async errorFromResponse(response, token) {
|
|
190
|
+
const errorBody = await response.json().catch(() => ({}));
|
|
191
|
+
const errorPayload = asErrorPayload(errorBody);
|
|
192
|
+
const rawMessage = optionalString(errorPayload.message);
|
|
193
|
+
const rawCode = optionalString(errorPayload.error_code);
|
|
194
|
+
return new MitraApiError(
|
|
195
|
+
redactText(rawMessage || `Request failed with status ${response.status}`, token),
|
|
196
|
+
response.status,
|
|
197
|
+
rawCode === void 0 ? void 0 : redactText(rawCode, token),
|
|
198
|
+
redactDetails(errorBody, token)
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
async parseResponseBody(response, path) {
|
|
202
|
+
if (response.status === 204) {
|
|
203
|
+
return void 0;
|
|
204
|
+
}
|
|
205
|
+
if (typeof response.text === "function") {
|
|
206
|
+
const responseText = await response.text();
|
|
207
|
+
if (responseText.length === 0) {
|
|
208
|
+
return void 0;
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
return JSON.parse(responseText);
|
|
212
|
+
} catch {
|
|
213
|
+
const error = new MitraApiError(
|
|
214
|
+
`Response from ${path} is not valid JSON`,
|
|
215
|
+
response.status,
|
|
216
|
+
"INVALID_RESPONSE"
|
|
217
|
+
);
|
|
218
|
+
this.onError?.(error);
|
|
219
|
+
throw error;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return response.json();
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Makes a GET request.
|
|
226
|
+
*
|
|
227
|
+
* @param path - API endpoint path
|
|
228
|
+
* @param params - Optional query parameters
|
|
229
|
+
* @returns Promise resolving to the parsed JSON response
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* ```typescript
|
|
233
|
+
* const users = await client.get<User[]>('/users', { limit: 10 });
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
get(path, params) {
|
|
237
|
+
return this.request(path, { method: "GET", params });
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Makes a POST request.
|
|
241
|
+
*
|
|
242
|
+
* @param path - API endpoint path
|
|
243
|
+
* @param body - Request body (will be JSON stringified)
|
|
244
|
+
* @returns Promise resolving to the parsed JSON response
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* ```typescript
|
|
248
|
+
* const user = await client.post<User>('/users', { name: 'John', email: 'john@example.com' });
|
|
249
|
+
* ```
|
|
250
|
+
*/
|
|
251
|
+
post(path, body) {
|
|
252
|
+
return this.request(path, { method: "POST", body });
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Makes a PUT request.
|
|
256
|
+
*
|
|
257
|
+
* @param path - API endpoint path
|
|
258
|
+
* @param body - Request body (will be JSON stringified)
|
|
259
|
+
* @returns Promise resolving to the parsed JSON response
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* ```typescript
|
|
263
|
+
* const user = await client.put<User>('/users/123', { name: 'Updated Name' });
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
put(path, body) {
|
|
267
|
+
return this.request(path, { method: "PUT", body });
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Makes a DELETE request.
|
|
271
|
+
*
|
|
272
|
+
* @param path - API endpoint path
|
|
273
|
+
* @param params - Optional query parameters
|
|
274
|
+
* @returns Promise resolving to the parsed JSON response (or undefined for 204 responses)
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```typescript
|
|
278
|
+
* await client.delete('/users/123');
|
|
279
|
+
* ```
|
|
280
|
+
*/
|
|
281
|
+
delete(path, params) {
|
|
282
|
+
return this.request(path, { method: "DELETE", params });
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
var MitraApiError = class extends Error {
|
|
286
|
+
constructor(message, status, code, details) {
|
|
287
|
+
super(message);
|
|
288
|
+
this.status = status;
|
|
289
|
+
this.code = code;
|
|
290
|
+
this.details = details;
|
|
291
|
+
this.name = "MitraApiError";
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
// src/core-errors.ts
|
|
296
|
+
var coreErrors = {
|
|
297
|
+
configuration: (message) => new MitraApiError(message, 0, "INVALID_CONFIGURATION"),
|
|
298
|
+
invalidResponse: (message) => new MitraApiError(message, 200, "INVALID_RESPONSE")
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
// src/legacy/bridge.ts
|
|
302
|
+
var import_mitra_interactions_sdk = require("mitra-interactions-sdk");
|
|
303
|
+
|
|
304
|
+
// src/modules/auth-page-url.ts
|
|
305
|
+
function resolveAuthPageUrl(apiUrl, configuredAuthPageUrl, browserWindow = globalThis.window) {
|
|
306
|
+
const injectedAuthPageUrl = browserWindow?.__mitraEnv?.authPageUrl;
|
|
307
|
+
try {
|
|
308
|
+
const candidate = configuredAuthPageUrl ?? (typeof injectedAuthPageUrl === "string" && injectedAuthPageUrl.trim() ? injectedAuthPageUrl : new URL("/sdk-auth.html", apiUrl).toString());
|
|
309
|
+
const url = new URL(candidate);
|
|
310
|
+
if (url.protocol !== "https:" && url.protocol !== "http:")
|
|
311
|
+
throw new Error("authPageUrl must be an absolute HTTP or HTTPS URL.");
|
|
312
|
+
return url;
|
|
313
|
+
} catch {
|
|
314
|
+
throw new Error("authPageUrl must be an absolute HTTP or HTTPS URL.");
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// src/legacy/bridge.ts
|
|
319
|
+
function stripBearer(token) {
|
|
320
|
+
return token.replace(/^\s*(?:bearer\s+)+/i, "").trim();
|
|
321
|
+
}
|
|
322
|
+
var LegacySessionBridge = class {
|
|
323
|
+
auth;
|
|
324
|
+
appId;
|
|
325
|
+
apiUrl;
|
|
326
|
+
configuredAuthPageUrl;
|
|
327
|
+
legacyUrl;
|
|
328
|
+
unsubscribeFromAuth = null;
|
|
329
|
+
constructor(auth, appId, apiUrl, authPageUrl) {
|
|
330
|
+
this.auth = auth;
|
|
331
|
+
this.appId = appId;
|
|
332
|
+
this.apiUrl = apiUrl;
|
|
333
|
+
this.configuredAuthPageUrl = authPageUrl;
|
|
334
|
+
const gatewayUrl = stripTrailingSlashes(apiUrl);
|
|
335
|
+
this.legacyUrl = gatewayUrl ? `${gatewayUrl}/legacy` : "";
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Configures the legacy SDK and hands it the session this SDK already holds.
|
|
339
|
+
*
|
|
340
|
+
* Called once per client. Failures are swallowed: an unusable legacy SDK must
|
|
341
|
+
* not stop the new client from being created.
|
|
342
|
+
*/
|
|
343
|
+
connect() {
|
|
344
|
+
this.disconnect();
|
|
345
|
+
this.syncToLegacy(this.auth.readSessionTokens());
|
|
346
|
+
this.unsubscribeFromAuth = this.auth.onSessionChange((session) => {
|
|
347
|
+
this.syncToLegacy(session);
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
/** Stops this bridge from changing the process-wide legacy SDK singleton. */
|
|
351
|
+
disconnect() {
|
|
352
|
+
this.unsubscribeFromAuth?.();
|
|
353
|
+
this.unsubscribeFromAuth = null;
|
|
354
|
+
}
|
|
355
|
+
syncToLegacy(session) {
|
|
356
|
+
try {
|
|
357
|
+
const authPageUrl = resolveAuthPageUrl(
|
|
358
|
+
this.apiUrl,
|
|
359
|
+
this.configuredAuthPageUrl
|
|
360
|
+
).toString();
|
|
361
|
+
(0, import_mitra_interactions_sdk.configureSdkMitra)({
|
|
362
|
+
baseURL: this.legacyUrl,
|
|
363
|
+
authUrl: this.legacyUrl,
|
|
364
|
+
authPageUrl,
|
|
365
|
+
projectId: this.appId,
|
|
366
|
+
onTokenRefresh: (session2) => this.adopt(session2),
|
|
367
|
+
...session.token ? { token: session.token } : {},
|
|
368
|
+
...session.refreshToken ? { refreshToken: session.refreshToken } : {}
|
|
369
|
+
});
|
|
370
|
+
const legacyConfig = (0, import_mitra_interactions_sdk.getConfig)();
|
|
371
|
+
if (!session.token) delete legacyConfig.token;
|
|
372
|
+
if (!session.refreshToken) delete legacyConfig.refreshToken;
|
|
373
|
+
} catch {
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Stores a session produced by the legacy SDK. The AuthModule subscription
|
|
378
|
+
* then writes that session back to the process-wide legacy config and
|
|
379
|
+
* reinstates the refresh hook removed by legacy login.
|
|
380
|
+
*/
|
|
381
|
+
adopt(session) {
|
|
382
|
+
const adopted = this.auth.adoptSession({
|
|
383
|
+
token: stripBearer(session.token),
|
|
384
|
+
refreshToken: session.refreshToken ?? null
|
|
385
|
+
});
|
|
386
|
+
if (!adopted) this.syncToLegacy(this.auth.readSessionTokens());
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
var activeBridge = null;
|
|
390
|
+
function setActiveBridge(bridge) {
|
|
391
|
+
activeBridge?.disconnect();
|
|
392
|
+
activeBridge = bridge;
|
|
393
|
+
}
|
|
394
|
+
function adoptLegacySession(session) {
|
|
395
|
+
activeBridge?.adopt(session);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// src/modules/auth.ts
|
|
399
|
+
var import_sdk_core2 = require("@mitralab.io/sdk-core");
|
|
400
|
+
|
|
401
|
+
// src/modules/google-auth.ts
|
|
402
|
+
var import_sdk_core = require("@mitralab.io/sdk-core");
|
|
403
|
+
var RESULT_TYPE = "mitra-oauth-result";
|
|
404
|
+
var PROVIDER_LABELS = {
|
|
405
|
+
google: "Google",
|
|
406
|
+
microsoft: "Microsoft"
|
|
407
|
+
};
|
|
408
|
+
var POPUP_WIDTH = 480;
|
|
409
|
+
var POPUP_HEIGHT = 600;
|
|
410
|
+
var POPUP_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
411
|
+
var POPUP_CLOSED_POLL_MS = 500;
|
|
412
|
+
function expectAuthTokenResponse(value) {
|
|
413
|
+
const response = (0, import_sdk_core.expectObject)(
|
|
414
|
+
value,
|
|
415
|
+
"Authentication token response",
|
|
416
|
+
coreErrors
|
|
417
|
+
);
|
|
418
|
+
for (const field of ["accessToken", "refreshToken", "tokenType"]) {
|
|
419
|
+
if (typeof response[field] !== "string" || !response[field].trim()) {
|
|
420
|
+
throw coreErrors.invalidResponse(
|
|
421
|
+
`Authentication token response has an invalid ${field} field`
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return {
|
|
426
|
+
accessToken: response.accessToken,
|
|
427
|
+
refreshToken: response.refreshToken,
|
|
428
|
+
tokenType: response.tokenType
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
var GoogleAuthFlow = class {
|
|
432
|
+
appId;
|
|
433
|
+
apiUrl;
|
|
434
|
+
configuredAuthPageUrl;
|
|
435
|
+
client;
|
|
436
|
+
provider;
|
|
437
|
+
providerLabel;
|
|
438
|
+
redirectStorageKey;
|
|
439
|
+
popupPromise = null;
|
|
440
|
+
constructor(config) {
|
|
441
|
+
this.appId = config.appId;
|
|
442
|
+
this.apiUrl = stripTrailingSlashes(config.apiUrl);
|
|
443
|
+
this.configuredAuthPageUrl = config.authPageUrl;
|
|
444
|
+
this.client = config.client;
|
|
445
|
+
this.provider = config.provider ?? "google";
|
|
446
|
+
this.providerLabel = PROVIDER_LABELS[this.provider];
|
|
447
|
+
this.redirectStorageKey = `mitra_${this.provider}_redirect_${config.appId}`;
|
|
448
|
+
}
|
|
449
|
+
signIn(options = {}) {
|
|
450
|
+
const browserWindow = this.requireBrowser();
|
|
451
|
+
if (options.mode === "redirect") {
|
|
452
|
+
return this.startRedirect(browserWindow);
|
|
453
|
+
}
|
|
454
|
+
if (options.mode !== void 0 && options.mode !== "popup") {
|
|
455
|
+
return Promise.reject(new Error(`Unsupported ${this.providerLabel} sign-in mode: ${String(options.mode)}`));
|
|
456
|
+
}
|
|
457
|
+
if (this.popupPromise) return this.popupPromise;
|
|
458
|
+
this.popupPromise = this.startPopup(browserWindow).finally(() => {
|
|
459
|
+
this.popupPromise = null;
|
|
460
|
+
});
|
|
461
|
+
return this.popupPromise;
|
|
462
|
+
}
|
|
463
|
+
async completeRedirect() {
|
|
464
|
+
const browserWindow = this.requireBrowser();
|
|
465
|
+
const params = new URLSearchParams(browserWindow.location.hash.replace(/^#/, ""));
|
|
466
|
+
const code = params.get("codeMitra");
|
|
467
|
+
const state = params.get("stateMitra");
|
|
468
|
+
const error = params.get("errorMitra");
|
|
469
|
+
if (code === null && state === null && error === null) return null;
|
|
470
|
+
const context = this.readRedirectContext(browserWindow);
|
|
471
|
+
if (!state?.trim()) {
|
|
472
|
+
throw new Error(`${this.providerLabel} sign-in redirect is missing state.`);
|
|
473
|
+
}
|
|
474
|
+
if (context?.state !== state) {
|
|
475
|
+
throw new Error(`Invalid ${this.providerLabel} sign-in state (possible CSRF).`);
|
|
476
|
+
}
|
|
477
|
+
const expectedRedirectUri = this.getRedirectUri(
|
|
478
|
+
resolveAuthPageUrl(this.apiUrl, this.configuredAuthPageUrl, browserWindow)
|
|
479
|
+
);
|
|
480
|
+
if (context.redirectUri !== expectedRedirectUri) {
|
|
481
|
+
throw new Error(`${this.providerLabel} sign-in redirect context is invalid.`);
|
|
482
|
+
}
|
|
483
|
+
this.cleanRedirectFragment(browserWindow);
|
|
484
|
+
this.clearRedirectContext(browserWindow);
|
|
485
|
+
if (code === "error" || error !== null) {
|
|
486
|
+
throw new Error(error || `${this.providerLabel} sign-in failed.`);
|
|
487
|
+
}
|
|
488
|
+
if (!code?.trim()) {
|
|
489
|
+
throw new Error(`${this.providerLabel} sign-in redirect is missing code.`);
|
|
490
|
+
}
|
|
491
|
+
return this.exchangeCode(code, context.redirectUri);
|
|
492
|
+
}
|
|
493
|
+
async startPopup(browserWindow) {
|
|
494
|
+
const state = this.generateState();
|
|
495
|
+
const authPageUrl = resolveAuthPageUrl(
|
|
496
|
+
this.apiUrl,
|
|
497
|
+
this.configuredAuthPageUrl,
|
|
498
|
+
browserWindow
|
|
499
|
+
);
|
|
500
|
+
const popup = this.openPopup(browserWindow, this.buildStartUrl(browserWindow, authPageUrl, state));
|
|
501
|
+
const result = await this.waitForPopupResult(browserWindow, popup, authPageUrl.origin, state);
|
|
502
|
+
if (result.code) return this.exchangeCode(result.code, this.getRedirectUri(authPageUrl));
|
|
503
|
+
return expectAuthTokenResponse(result.token);
|
|
504
|
+
}
|
|
505
|
+
startRedirect(browserWindow) {
|
|
506
|
+
const state = this.generateState();
|
|
507
|
+
const authPageUrl = resolveAuthPageUrl(
|
|
508
|
+
this.apiUrl,
|
|
509
|
+
this.configuredAuthPageUrl,
|
|
510
|
+
browserWindow
|
|
511
|
+
);
|
|
512
|
+
const context = {
|
|
513
|
+
state,
|
|
514
|
+
redirectUri: this.getRedirectUri(authPageUrl)
|
|
515
|
+
};
|
|
516
|
+
this.persistRedirectContext(browserWindow, context);
|
|
517
|
+
const startUrl = this.buildStartUrl(browserWindow, authPageUrl, state);
|
|
518
|
+
browserWindow.location.assign(startUrl.toString());
|
|
519
|
+
return new Promise(() => void 0);
|
|
520
|
+
}
|
|
521
|
+
async exchangeCode(code, redirectUri) {
|
|
522
|
+
const response = await this.client.post(`/api/v1/auth/${this.provider}`, {
|
|
523
|
+
appId: this.appId,
|
|
524
|
+
code,
|
|
525
|
+
redirectUri
|
|
526
|
+
});
|
|
527
|
+
return expectAuthTokenResponse(response);
|
|
528
|
+
}
|
|
529
|
+
buildStartUrl(browserWindow, authPageUrl, state) {
|
|
530
|
+
const startUrl = new URL(authPageUrl);
|
|
531
|
+
startUrl.searchParams.set("provider", this.provider);
|
|
532
|
+
startUrl.searchParams.set("state", state);
|
|
533
|
+
startUrl.searchParams.set("appId", this.appId);
|
|
534
|
+
startUrl.searchParams.set("apiUrl", this.apiUrl);
|
|
535
|
+
startUrl.searchParams.set("origin", browserWindow.location.origin);
|
|
536
|
+
startUrl.searchParams.set("responseType", "code");
|
|
537
|
+
return startUrl;
|
|
538
|
+
}
|
|
539
|
+
getRedirectUri(authPageUrl) {
|
|
540
|
+
return `${authPageUrl.origin}${authPageUrl.pathname}`;
|
|
541
|
+
}
|
|
542
|
+
generateState() {
|
|
543
|
+
if (!globalThis.crypto?.getRandomValues) {
|
|
544
|
+
throw new Error(`${this.providerLabel} sign-in requires crypto.getRandomValues.`);
|
|
545
|
+
}
|
|
546
|
+
const bytes = new Uint8Array(16);
|
|
547
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
548
|
+
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
549
|
+
}
|
|
550
|
+
openPopup(browserWindow, url) {
|
|
551
|
+
const outerWidth = browserWindow.outerWidth || browserWindow.screen.width;
|
|
552
|
+
const outerHeight = browserWindow.outerHeight || browserWindow.screen.height;
|
|
553
|
+
const left = Math.max(0, (browserWindow.screenX || 0) + (outerWidth - POPUP_WIDTH) / 2);
|
|
554
|
+
const top = Math.max(0, (browserWindow.screenY || 0) + (outerHeight - POPUP_HEIGHT) / 2);
|
|
555
|
+
const popup = browserWindow.open(
|
|
556
|
+
url.toString(),
|
|
557
|
+
"mitra-google-oauth",
|
|
558
|
+
`width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},menubar=no,toolbar=no,status=no`
|
|
559
|
+
);
|
|
560
|
+
if (!popup) {
|
|
561
|
+
throw new Error(`${this.providerLabel} sign-in popup was blocked by the browser.`);
|
|
562
|
+
}
|
|
563
|
+
return popup;
|
|
564
|
+
}
|
|
565
|
+
waitForPopupResult(browserWindow, popup, expectedOrigin, expectedState) {
|
|
566
|
+
return new Promise((resolve, reject) => {
|
|
567
|
+
const timeout = globalThis.setTimeout(() => {
|
|
568
|
+
cleanup();
|
|
569
|
+
reject(new Error(`${this.providerLabel} sign-in timed out.`));
|
|
570
|
+
}, POPUP_TIMEOUT_MS);
|
|
571
|
+
const closedPoll = globalThis.setInterval(() => {
|
|
572
|
+
if (popup.closed) {
|
|
573
|
+
cleanup();
|
|
574
|
+
reject(new Error(`${this.providerLabel} sign-in was cancelled.`));
|
|
575
|
+
}
|
|
576
|
+
}, POPUP_CLOSED_POLL_MS);
|
|
577
|
+
const onMessage = (event) => {
|
|
578
|
+
if (event.origin !== expectedOrigin || event.source !== popup) return;
|
|
579
|
+
if (!event.data || typeof event.data !== "object") return;
|
|
580
|
+
const data = event.data;
|
|
581
|
+
if (data.type !== RESULT_TYPE) return;
|
|
582
|
+
if (data.state !== expectedState) {
|
|
583
|
+
cleanup();
|
|
584
|
+
reject(new Error(`Invalid ${this.providerLabel} sign-in state (possible CSRF).`));
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
if (data.success !== true) {
|
|
588
|
+
cleanup();
|
|
589
|
+
reject(new Error(typeof data.error === "string" && data.error.trim() ? data.error : `${this.providerLabel} sign-in failed.`));
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
const code = typeof data.code === "string" && data.code.trim() ? data.code : void 0;
|
|
593
|
+
if (!code && data.token === void 0) {
|
|
594
|
+
cleanup();
|
|
595
|
+
reject(new Error("Google auth page returned neither code nor token."));
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
cleanup();
|
|
599
|
+
resolve({ ...code ? { code } : {}, ...data.token !== void 0 ? { token: data.token } : {} });
|
|
600
|
+
};
|
|
601
|
+
const cleanup = () => {
|
|
602
|
+
globalThis.clearTimeout(timeout);
|
|
603
|
+
globalThis.clearInterval(closedPoll);
|
|
604
|
+
browserWindow.removeEventListener("message", onMessage);
|
|
605
|
+
if (!popup.closed) popup.close();
|
|
606
|
+
};
|
|
607
|
+
browserWindow.addEventListener("message", onMessage);
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
persistRedirectContext(browserWindow, context) {
|
|
611
|
+
try {
|
|
612
|
+
browserWindow.sessionStorage.setItem(this.redirectStorageKey, JSON.stringify(context));
|
|
613
|
+
} catch {
|
|
614
|
+
throw new Error(`${this.providerLabel} sign-in redirect requires sessionStorage.`);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
readRedirectContext(browserWindow) {
|
|
618
|
+
try {
|
|
619
|
+
const raw = browserWindow.sessionStorage.getItem(this.redirectStorageKey);
|
|
620
|
+
if (!raw) return null;
|
|
621
|
+
const value = JSON.parse(raw);
|
|
622
|
+
if (typeof value.state !== "string" || typeof value.redirectUri !== "string") return null;
|
|
623
|
+
return {
|
|
624
|
+
state: value.state,
|
|
625
|
+
redirectUri: value.redirectUri
|
|
626
|
+
};
|
|
627
|
+
} catch {
|
|
628
|
+
return null;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
clearRedirectContext(browserWindow) {
|
|
632
|
+
try {
|
|
633
|
+
browserWindow.sessionStorage.removeItem(this.redirectStorageKey);
|
|
634
|
+
} catch {
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
cleanRedirectFragment(browserWindow) {
|
|
638
|
+
browserWindow.history.replaceState(
|
|
639
|
+
{},
|
|
640
|
+
"",
|
|
641
|
+
`${browserWindow.location.pathname}${browserWindow.location.search}`
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
requireBrowser() {
|
|
645
|
+
if (globalThis.window === void 0) {
|
|
646
|
+
throw new Error(`${this.providerLabel} sign-in is only available in a browser.`);
|
|
647
|
+
}
|
|
648
|
+
return globalThis.window;
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
|
|
652
|
+
// src/modules/auth.ts
|
|
653
|
+
var DEFAULT_TOKEN_VALIDITY_MS = 3e4;
|
|
654
|
+
var sessionPorts = /* @__PURE__ */ new WeakMap();
|
|
655
|
+
function getAuthSessionPort(auth) {
|
|
656
|
+
const port = sessionPorts.get(auth);
|
|
657
|
+
if (!port) throw new Error("Auth session port is unavailable.");
|
|
658
|
+
return port;
|
|
659
|
+
}
|
|
660
|
+
function decodeJwtPayload(token) {
|
|
661
|
+
try {
|
|
662
|
+
const part = token.replace(/^Bearer\s+/i, "").split(".")[1];
|
|
663
|
+
if (!part || typeof globalThis.atob !== "function") return null;
|
|
664
|
+
const base64 = part.replaceAll("-", "+").replaceAll("_", "/");
|
|
665
|
+
const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
|
|
666
|
+
const value = JSON.parse(globalThis.atob(padded));
|
|
667
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
668
|
+
return value;
|
|
669
|
+
} catch {
|
|
670
|
+
return null;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
function belongsToApp(token, appId) {
|
|
674
|
+
const payload = decodeJwtPayload(token);
|
|
675
|
+
if (!payload) return true;
|
|
676
|
+
return payload.app_id === appId;
|
|
677
|
+
}
|
|
678
|
+
function isTokenExpiring(token, minValidityMs) {
|
|
679
|
+
const exp = decodeJwtPayload(token)?.exp;
|
|
680
|
+
if (typeof exp !== "number" || !Number.isFinite(exp)) return false;
|
|
681
|
+
return exp * 1e3 - Date.now() < minValidityMs;
|
|
682
|
+
}
|
|
683
|
+
function isDefinitiveRefreshFailure(error) {
|
|
684
|
+
return error instanceof MitraApiError && error.status >= 400 && error.status < 500 && error.status !== 408 && error.status !== 429;
|
|
685
|
+
}
|
|
686
|
+
var AuthModule = class {
|
|
687
|
+
appId;
|
|
688
|
+
_currentUser = null;
|
|
689
|
+
#accessToken = null;
|
|
690
|
+
#refreshToken = null;
|
|
691
|
+
sessionGeneration = 0;
|
|
692
|
+
refreshFlight = null;
|
|
693
|
+
transientRefreshFailureGeneration = null;
|
|
694
|
+
listeners = /* @__PURE__ */ new Set();
|
|
695
|
+
sessionListeners = /* @__PURE__ */ new Set();
|
|
696
|
+
storageKey;
|
|
697
|
+
publicClient;
|
|
698
|
+
authedClient;
|
|
699
|
+
currentUserApi;
|
|
700
|
+
googleAuth;
|
|
701
|
+
microsoftAuth;
|
|
702
|
+
constructor(appId, iamBaseUrl, options = {}) {
|
|
703
|
+
this.appId = appId;
|
|
704
|
+
const trimmedIamBaseUrl = stripTrailingSlashes(iamBaseUrl);
|
|
705
|
+
const apiUrl = stripTrailingSlashes(
|
|
706
|
+
options.apiUrl ?? (trimmedIamBaseUrl.endsWith("/iam") ? trimmedIamBaseUrl.slice(0, -"/iam".length) : trimmedIamBaseUrl)
|
|
707
|
+
);
|
|
708
|
+
this.storageKey = `mitra_auth_${appId}`;
|
|
709
|
+
this.publicClient = new HttpClient({ baseUrl: iamBaseUrl, getToken: () => null });
|
|
710
|
+
this.authedClient = new HttpClient({
|
|
711
|
+
baseUrl: iamBaseUrl,
|
|
712
|
+
getToken: () => this.#accessToken,
|
|
713
|
+
beforeAuthenticatedRequest: () => this.ensureFreshSession().then(() => void 0),
|
|
714
|
+
onUnauthorized: (requestToken) => this.handleUnauthorized(requestToken)
|
|
715
|
+
});
|
|
716
|
+
this.currentUserApi = (0, import_sdk_core2.createAuthModule)(this.authedClient, coreErrors);
|
|
717
|
+
this.googleAuth = new GoogleAuthFlow({
|
|
718
|
+
appId,
|
|
719
|
+
apiUrl,
|
|
720
|
+
authPageUrl: options.authPageUrl,
|
|
721
|
+
client: this.publicClient
|
|
722
|
+
});
|
|
723
|
+
this.microsoftAuth = new GoogleAuthFlow({
|
|
724
|
+
appId,
|
|
725
|
+
apiUrl,
|
|
726
|
+
authPageUrl: options.authPageUrl,
|
|
727
|
+
client: this.publicClient,
|
|
728
|
+
provider: "microsoft"
|
|
729
|
+
});
|
|
730
|
+
this.loadFromStorage();
|
|
731
|
+
const readAccessToken = () => this.#accessToken;
|
|
732
|
+
sessionPorts.set(this, {
|
|
733
|
+
get accessToken() {
|
|
734
|
+
return readAccessToken();
|
|
735
|
+
},
|
|
736
|
+
ensureFreshSession: (minValidityMs) => this.ensureFreshSession(minValidityMs),
|
|
737
|
+
handleUnauthorized: (requestToken) => this.handleUnauthorized(requestToken),
|
|
738
|
+
readSessionTokens: () => this.readSessionTokens(),
|
|
739
|
+
onSessionChange: (callback) => this.onSessionChange(callback),
|
|
740
|
+
adoptSession: (session) => this.adoptSession(session)
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
/** The currently authenticated user, or null. */
|
|
744
|
+
get currentUser() {
|
|
745
|
+
return this._currentUser;
|
|
746
|
+
}
|
|
747
|
+
/** The current JWT access token, or null. */
|
|
748
|
+
get accessToken() {
|
|
749
|
+
return this.#accessToken;
|
|
750
|
+
}
|
|
751
|
+
/** Whether a user is currently authenticated (local check, not server-validated). */
|
|
752
|
+
get isAuthenticated() {
|
|
753
|
+
return this._currentUser !== null && this.#accessToken !== null;
|
|
754
|
+
}
|
|
755
|
+
/** @deprecated Email/password authentication is not implemented by IAM. Use Google or Microsoft SSO. */
|
|
756
|
+
async signIn(_credentials) {
|
|
757
|
+
throw new MitraApiError(
|
|
758
|
+
"Email/password authentication is not available. Use signInWithGoogle() or signInWithMicrosoft().",
|
|
759
|
+
0,
|
|
760
|
+
"UNSUPPORTED_AUTH_METHOD"
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Signs in with Google SSO.
|
|
765
|
+
*
|
|
766
|
+
* Popup mode is used by default. Redirect mode stores a one-time CSRF context
|
|
767
|
+
* in `sessionStorage` and navigates to the configured `sdk-auth.html` page.
|
|
768
|
+
* Call {@link completeGoogleSignInRedirect} during application startup to
|
|
769
|
+
* finish a redirect response.
|
|
770
|
+
*
|
|
771
|
+
* The auth page is resolved from `createClient({ authPageUrl })`, then
|
|
772
|
+
* `window.__mitraEnv.authPageUrl`, and finally `/sdk-auth.html` on the API
|
|
773
|
+
* gateway origin.
|
|
774
|
+
*
|
|
775
|
+
* @param options - Popup or redirect mode.
|
|
776
|
+
* @returns The authenticated and hydrated user in popup mode.
|
|
777
|
+
* @throws {MitraApiError} When IAM rejects the authorization code.
|
|
778
|
+
* @throws {Error} When the browser blocks or cancels the popup, the flow times
|
|
779
|
+
* out, or the OAuth response fails origin, source, state, or shape validation.
|
|
780
|
+
*
|
|
781
|
+
* @example
|
|
782
|
+
* ```typescript
|
|
783
|
+
* const user = await mitra.auth.signInWithGoogle();
|
|
784
|
+
* ```
|
|
785
|
+
*
|
|
786
|
+
* @example
|
|
787
|
+
* ```typescript
|
|
788
|
+
* await mitra.auth.signInWithGoogle({ mode: 'redirect' });
|
|
789
|
+
* ```
|
|
790
|
+
*/
|
|
791
|
+
async signInWithGoogle(options = {}) {
|
|
792
|
+
return this.establishSession(await this.googleAuth.signIn(options));
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Completes a Google redirect response from `#codeMitra` and `#stateMitra`.
|
|
796
|
+
*
|
|
797
|
+
* The method consumes and clears the fragment and stored CSRF context, sends
|
|
798
|
+
* the single-use code directly to IAM, persists both tokens, calls `auth.me()`,
|
|
799
|
+
* and notifies auth-state listeners. It returns `null` when the current URL is
|
|
800
|
+
* not a Google SSO redirect. Redirect errors must carry the same `stateMitra`
|
|
801
|
+
* stored at the start of the flow before their message is exposed or consumed.
|
|
802
|
+
*
|
|
803
|
+
* @returns The authenticated user, or `null` when no redirect result is present.
|
|
804
|
+
*
|
|
805
|
+
* @example
|
|
806
|
+
* ```typescript
|
|
807
|
+
* const redirectedUser = await mitra.auth.completeGoogleSignInRedirect();
|
|
808
|
+
* if (redirectedUser) console.log(redirectedUser.email);
|
|
809
|
+
* ```
|
|
810
|
+
*/
|
|
811
|
+
async completeGoogleSignInRedirect() {
|
|
812
|
+
const tokenResponse = await this.googleAuth.completeRedirect();
|
|
813
|
+
return tokenResponse ? this.establishSession(tokenResponse) : null;
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Signs in with Microsoft SSO: the same auth-page handshake as Google, exchanged
|
|
817
|
+
* at IAM's `/auth/microsoft`. Popup by default; redirect mode navigates away.
|
|
818
|
+
*
|
|
819
|
+
* @example
|
|
820
|
+
* ```typescript
|
|
821
|
+
* const user = await mitra.auth.signInWithMicrosoft();
|
|
822
|
+
* ```
|
|
823
|
+
*/
|
|
824
|
+
async signInWithMicrosoft(options = {}) {
|
|
825
|
+
return this.establishSession(await this.microsoftAuth.signIn(options));
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Completes a Microsoft redirect response from `#codeMitra` and `#stateMitra`.
|
|
829
|
+
* Mirrors {@link completeGoogleSignInRedirect}; returns `null` when the current
|
|
830
|
+
* URL is not a Microsoft SSO redirect.
|
|
831
|
+
*/
|
|
832
|
+
async completeMicrosoftSignInRedirect() {
|
|
833
|
+
const tokenResponse = await this.microsoftAuth.completeRedirect();
|
|
834
|
+
return tokenResponse ? this.establishSession(tokenResponse) : null;
|
|
835
|
+
}
|
|
836
|
+
/** @deprecated Email/password registration is not implemented by IAM. Use Google or Microsoft SSO. */
|
|
837
|
+
async signUp(_data) {
|
|
838
|
+
throw new MitraApiError(
|
|
839
|
+
"Email/password registration is not available. Use signInWithGoogle() or signInWithMicrosoft().",
|
|
840
|
+
0,
|
|
841
|
+
"UNSUPPORTED_AUTH_METHOD"
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* Signs out the current user, clearing all auth state and localStorage.
|
|
846
|
+
*
|
|
847
|
+
* @param redirectUrl - Optional URL to navigate to after sign-out.
|
|
848
|
+
*
|
|
849
|
+
* @example
|
|
850
|
+
* ```typescript
|
|
851
|
+
* mitra.auth.signOut();
|
|
852
|
+
* mitra.auth.signOut('/login');
|
|
853
|
+
* ```
|
|
854
|
+
*/
|
|
855
|
+
signOut(redirectUrl) {
|
|
856
|
+
this.clearAuthState();
|
|
857
|
+
if (globalThis.window !== void 0 && redirectUrl) {
|
|
858
|
+
globalThis.window.location.href = redirectUrl;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Refreshes the session using the stored refresh token.
|
|
863
|
+
*
|
|
864
|
+
* Called automatically before requests whose JWT is close to expiry and on
|
|
865
|
+
* `401` responses. Can also be called manually. Multiple proactive, reactive,
|
|
866
|
+
* and manual calls are deduplicated into one refresh request.
|
|
867
|
+
*
|
|
868
|
+
* @returns `true` if refresh succeeded, `false` otherwise.
|
|
869
|
+
*
|
|
870
|
+
* @example
|
|
871
|
+
* ```typescript
|
|
872
|
+
* const ok = await mitra.auth.refreshSession();
|
|
873
|
+
* if (!ok) mitra.auth.redirectToLogin();
|
|
874
|
+
* ```
|
|
875
|
+
*/
|
|
876
|
+
async refreshSession() {
|
|
877
|
+
if (!this.#refreshToken) return false;
|
|
878
|
+
if (!this.hasValidAppIdentity()) {
|
|
879
|
+
this.clearAuthState();
|
|
880
|
+
return false;
|
|
881
|
+
}
|
|
882
|
+
const generation = this.sessionGeneration;
|
|
883
|
+
if (this.refreshFlight?.generation === generation) {
|
|
884
|
+
return this.refreshFlight.promise;
|
|
885
|
+
}
|
|
886
|
+
this.transientRefreshFailureGeneration = null;
|
|
887
|
+
const flight = {
|
|
888
|
+
generation,
|
|
889
|
+
promise: this.doRefresh(generation, this.#refreshToken)
|
|
890
|
+
};
|
|
891
|
+
this.refreshFlight = flight;
|
|
892
|
+
try {
|
|
893
|
+
return await flight.promise;
|
|
894
|
+
} finally {
|
|
895
|
+
if (this.refreshFlight === flight) {
|
|
896
|
+
this.refreshFlight = null;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Resolves a 401 against the credential that actually reached the server.
|
|
902
|
+
* A newer session is retried as-is instead of being refreshed because of an
|
|
903
|
+
* older request. A signed-out session is neither refreshed nor retried.
|
|
904
|
+
*
|
|
905
|
+
* @param requestToken - Access token attached to the rejected request.
|
|
906
|
+
* @returns Whether the request should be retried once with the current token.
|
|
907
|
+
*
|
|
908
|
+
* @internal
|
|
909
|
+
*/
|
|
910
|
+
async handleUnauthorized(requestToken) {
|
|
911
|
+
const currentToken = this.#accessToken;
|
|
912
|
+
if (!currentToken) return false;
|
|
913
|
+
if (currentToken !== requestToken) return true;
|
|
914
|
+
return this.refreshSession();
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* Fetches the current user from the server and updates local state.
|
|
918
|
+
*
|
|
919
|
+
* Clears auth state on a definitive 401. When the request reaches 401 after
|
|
920
|
+
* IAM refresh failed due to a network error, 408, 429, or 5xx response, the
|
|
921
|
+
* retained session is preserved and this method returns null.
|
|
922
|
+
*
|
|
923
|
+
* @returns The user if authenticated, `null` otherwise.
|
|
924
|
+
*
|
|
925
|
+
* @example
|
|
926
|
+
* ```typescript
|
|
927
|
+
* const user = await mitra.auth.me();
|
|
928
|
+
* if (!user) console.log('Not authenticated');
|
|
929
|
+
* ```
|
|
930
|
+
*/
|
|
931
|
+
async me() {
|
|
932
|
+
if (!this.#accessToken) return null;
|
|
933
|
+
const generation = this.sessionGeneration;
|
|
934
|
+
try {
|
|
935
|
+
const user = await this.getCurrentUser();
|
|
936
|
+
if (generation !== this.sessionGeneration) return null;
|
|
937
|
+
this._currentUser = user;
|
|
938
|
+
this.transientRefreshFailureGeneration = null;
|
|
939
|
+
this.saveToStorage();
|
|
940
|
+
this.notifyListeners();
|
|
941
|
+
return user;
|
|
942
|
+
} catch (error) {
|
|
943
|
+
if (error instanceof MitraApiError && error.status === 401 && this.#accessToken && generation === this.sessionGeneration && this.transientRefreshFailureGeneration !== this.sessionGeneration) {
|
|
944
|
+
this.clearAuthState();
|
|
945
|
+
}
|
|
946
|
+
return null;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Validates the current session with the server.
|
|
951
|
+
*
|
|
952
|
+
* @returns `true` if the session is valid, `false` otherwise.
|
|
953
|
+
*
|
|
954
|
+
* @example
|
|
955
|
+
* ```typescript
|
|
956
|
+
* const valid = await mitra.auth.checkAuth();
|
|
957
|
+
* if (!valid) mitra.auth.redirectToLogin();
|
|
958
|
+
* ```
|
|
959
|
+
*/
|
|
960
|
+
async checkAuth() {
|
|
961
|
+
return await this.me() !== null;
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Sets the access token manually (e.g., from SSO/OAuth callback).
|
|
965
|
+
*
|
|
966
|
+
* Call `me()` afterwards to fetch the associated user data.
|
|
967
|
+
*
|
|
968
|
+
* @param token - JWT access token.
|
|
969
|
+
* @param saveToStorage - Whether to persist to localStorage (default: true).
|
|
970
|
+
*
|
|
971
|
+
* @example
|
|
972
|
+
* ```typescript
|
|
973
|
+
* mitra.auth.setToken(tokenFromCallback);
|
|
974
|
+
* await mitra.auth.me();
|
|
975
|
+
* ```
|
|
976
|
+
*/
|
|
977
|
+
setToken(token, saveToStorage = true) {
|
|
978
|
+
if (!this.belongsToConfiguredApp(token)) {
|
|
979
|
+
this.clearAuthState();
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
this.invalidatePendingRefreshes();
|
|
983
|
+
this.#accessToken = token;
|
|
984
|
+
if (saveToStorage) {
|
|
985
|
+
this.saveToStorage();
|
|
986
|
+
}
|
|
987
|
+
this.notifySessionListeners();
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* Reads the tokens currently held by this module.
|
|
991
|
+
*
|
|
992
|
+
* Used by the legacy session bridge to hand a session persisted by this SDK
|
|
993
|
+
* over to `mitra-interactions-sdk` on startup.
|
|
994
|
+
*
|
|
995
|
+
* @returns The access and refresh tokens, each `null` when absent.
|
|
996
|
+
*
|
|
997
|
+
* @internal
|
|
998
|
+
*/
|
|
999
|
+
readSessionTokens() {
|
|
1000
|
+
return { token: this.#accessToken, refreshToken: this.#refreshToken };
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Adopts an app-scoped session received from a trusted platform boundary.
|
|
1004
|
+
*
|
|
1005
|
+
* This preserves the access and refresh tokens together so the normal refresh
|
|
1006
|
+
* lifecycle continues after an embedded preview hands its session to the app.
|
|
1007
|
+
* Call {@link checkAuth} afterward to validate the token and hydrate the user.
|
|
1008
|
+
*/
|
|
1009
|
+
setSession(session) {
|
|
1010
|
+
return this.adoptSession({
|
|
1011
|
+
token: session.accessToken,
|
|
1012
|
+
refreshToken: session.refreshToken ?? null
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
/**
|
|
1016
|
+
* Ensures the current access token has enough remaining validity.
|
|
1017
|
+
*
|
|
1018
|
+
* JWT decoding is used only as a scheduling heuristic. Opaque tokens and JWTs
|
|
1019
|
+
* without a numeric `exp` claim proceed unchanged and remain server-authoritative.
|
|
1020
|
+
* Multiple proactive and reactive callers share the same refresh request.
|
|
1021
|
+
* Transient refresh failures preserve the current session so the caller can
|
|
1022
|
+
* continue and rely on the normal one-time `401` refresh fallback.
|
|
1023
|
+
*
|
|
1024
|
+
* This method is suitable for authenticated HTTP, WebSocket, and Server-Sent
|
|
1025
|
+
* Events boundaries that need a fresh token before connecting.
|
|
1026
|
+
*
|
|
1027
|
+
* @param minValidityMs - Minimum remaining token lifetime. Defaults to 30 seconds.
|
|
1028
|
+
* @returns `true` when no refresh is needed or refresh succeeds. Returns
|
|
1029
|
+
* `false` when a required refresh fails, even when a transient failure keeps
|
|
1030
|
+
* the current session available for a reactive server-authoritative fallback.
|
|
1031
|
+
*/
|
|
1032
|
+
async ensureFreshSession(minValidityMs = DEFAULT_TOKEN_VALIDITY_MS) {
|
|
1033
|
+
if (!Number.isFinite(minValidityMs) || minValidityMs < 0) {
|
|
1034
|
+
throw new RangeError("minValidityMs must be a finite non-negative number");
|
|
1035
|
+
}
|
|
1036
|
+
if (!this.#accessToken) return false;
|
|
1037
|
+
if (!this.hasValidAppIdentity()) {
|
|
1038
|
+
this.clearAuthState();
|
|
1039
|
+
return false;
|
|
1040
|
+
}
|
|
1041
|
+
if (!isTokenExpiring(this.#accessToken, minValidityMs)) return true;
|
|
1042
|
+
return this.refreshSession();
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Subscribes an internal boundary to token changes without triggering login
|
|
1046
|
+
* or refresh. Unlike auth-state listeners, this callback is not invoked
|
|
1047
|
+
* immediately and does not depend on the user being hydrated.
|
|
1048
|
+
*
|
|
1049
|
+
* @param callback - Receives the current access and refresh tokens.
|
|
1050
|
+
* @returns A function that removes the callback.
|
|
1051
|
+
*
|
|
1052
|
+
* @internal
|
|
1053
|
+
*/
|
|
1054
|
+
onSessionChange(callback) {
|
|
1055
|
+
this.sessionListeners.add(callback);
|
|
1056
|
+
return () => this.sessionListeners.delete(callback);
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Adopts a session produced outside this module, such as a legacy SSO login.
|
|
1060
|
+
*
|
|
1061
|
+
* Replaces the in-memory tokens and persists them under the same storage key
|
|
1062
|
+
* the rest of the module uses. The current user is left untouched because the
|
|
1063
|
+
* legacy SDK does not return one; call `me()` to hydrate it.
|
|
1064
|
+
*
|
|
1065
|
+
* @param session - Access token and, when the issuer returned one, refresh token.
|
|
1066
|
+
*
|
|
1067
|
+
* @internal
|
|
1068
|
+
*/
|
|
1069
|
+
adoptSession(session) {
|
|
1070
|
+
if (!this.belongsToConfiguredApp(session.token) || typeof session.refreshToken === "string" && !this.belongsToConfiguredApp(session.refreshToken)) {
|
|
1071
|
+
this.clearAuthState();
|
|
1072
|
+
return false;
|
|
1073
|
+
}
|
|
1074
|
+
this.invalidatePendingRefreshes();
|
|
1075
|
+
this.#accessToken = session.token;
|
|
1076
|
+
if (session.refreshToken !== void 0) {
|
|
1077
|
+
this.#refreshToken = session.refreshToken;
|
|
1078
|
+
}
|
|
1079
|
+
this.saveToStorage();
|
|
1080
|
+
this.notifySessionListeners();
|
|
1081
|
+
return true;
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* Redirects to `/login?returnUrl=...` for unauthenticated users.
|
|
1085
|
+
*
|
|
1086
|
+
* @param returnUrl - URL to return to after login (default: '/').
|
|
1087
|
+
*
|
|
1088
|
+
* @example
|
|
1089
|
+
* ```typescript
|
|
1090
|
+
* if (!mitra.auth.isAuthenticated) {
|
|
1091
|
+
* mitra.auth.redirectToLogin(window.location.pathname);
|
|
1092
|
+
* }
|
|
1093
|
+
* ```
|
|
1094
|
+
*/
|
|
1095
|
+
redirectToLogin(returnUrl = "/") {
|
|
1096
|
+
if (globalThis.window === void 0) return;
|
|
1097
|
+
globalThis.window.location.href = `/login?returnUrl=${encodeURIComponent(returnUrl)}`;
|
|
1098
|
+
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Registers a callback for auth state changes.
|
|
1101
|
+
*
|
|
1102
|
+
* Called immediately with the current state, then on every sign-in/sign-out.
|
|
1103
|
+
*
|
|
1104
|
+
* @param callback - Receives the User on login, null on logout.
|
|
1105
|
+
* @returns Unsubscribe function.
|
|
1106
|
+
*
|
|
1107
|
+
* @example
|
|
1108
|
+
* ```typescript
|
|
1109
|
+
* useEffect(() => {
|
|
1110
|
+
* const unsub = mitra.auth.onAuthStateChange((user) => {
|
|
1111
|
+
* setUser(user);
|
|
1112
|
+
* setLoading(false);
|
|
1113
|
+
* });
|
|
1114
|
+
* return unsub;
|
|
1115
|
+
* }, []);
|
|
1116
|
+
* ```
|
|
1117
|
+
*/
|
|
1118
|
+
onAuthStateChange(callback) {
|
|
1119
|
+
this.listeners.add(callback);
|
|
1120
|
+
callback(this._currentUser);
|
|
1121
|
+
return () => {
|
|
1122
|
+
this.listeners.delete(callback);
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
async doRefresh(generation, refreshToken) {
|
|
1126
|
+
let tokenResponse;
|
|
1127
|
+
try {
|
|
1128
|
+
tokenResponse = expectAuthTokenResponse(
|
|
1129
|
+
await this.publicClient.post(
|
|
1130
|
+
"/api/v1/auth/refresh-token",
|
|
1131
|
+
{ refreshToken }
|
|
1132
|
+
)
|
|
1133
|
+
);
|
|
1134
|
+
} catch (error) {
|
|
1135
|
+
if (generation !== this.sessionGeneration) return false;
|
|
1136
|
+
if (isDefinitiveRefreshFailure(error)) {
|
|
1137
|
+
this.clearAuthState();
|
|
1138
|
+
} else {
|
|
1139
|
+
this.transientRefreshFailureGeneration = generation;
|
|
1140
|
+
}
|
|
1141
|
+
return false;
|
|
1142
|
+
}
|
|
1143
|
+
if (generation !== this.sessionGeneration) return false;
|
|
1144
|
+
if (!this.belongsToConfiguredApp(tokenResponse.accessToken) || !this.belongsToConfiguredApp(tokenResponse.refreshToken)) {
|
|
1145
|
+
this.clearAuthState();
|
|
1146
|
+
return false;
|
|
1147
|
+
}
|
|
1148
|
+
this.#accessToken = tokenResponse.accessToken;
|
|
1149
|
+
this.#refreshToken = tokenResponse.refreshToken;
|
|
1150
|
+
this.transientRefreshFailureGeneration = null;
|
|
1151
|
+
this.saveToStorage();
|
|
1152
|
+
this.notifySessionListeners();
|
|
1153
|
+
return true;
|
|
1154
|
+
}
|
|
1155
|
+
async establishSession(tokenResponse) {
|
|
1156
|
+
if (!this.belongsToConfiguredApp(tokenResponse.accessToken) || !this.belongsToConfiguredApp(tokenResponse.refreshToken)) {
|
|
1157
|
+
this.clearAuthState();
|
|
1158
|
+
throw coreErrors.invalidResponse("Authentication returned a token for a different app");
|
|
1159
|
+
}
|
|
1160
|
+
this.invalidatePendingRefreshes();
|
|
1161
|
+
const generation = this.sessionGeneration;
|
|
1162
|
+
this.#accessToken = tokenResponse.accessToken;
|
|
1163
|
+
this.#refreshToken = tokenResponse.refreshToken;
|
|
1164
|
+
try {
|
|
1165
|
+
const user = await this.getCurrentUser();
|
|
1166
|
+
if (generation !== this.sessionGeneration) {
|
|
1167
|
+
throw new Error("Authentication session was superseded");
|
|
1168
|
+
}
|
|
1169
|
+
this.setAuthState(user, this.#accessToken, this.#refreshToken);
|
|
1170
|
+
return user;
|
|
1171
|
+
} catch (error) {
|
|
1172
|
+
if (generation === this.sessionGeneration) {
|
|
1173
|
+
this.clearAuthState();
|
|
1174
|
+
}
|
|
1175
|
+
throw error;
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
setAuthState(user, token, refreshToken) {
|
|
1179
|
+
this._currentUser = user;
|
|
1180
|
+
this.#accessToken = token;
|
|
1181
|
+
this.#refreshToken = refreshToken;
|
|
1182
|
+
this.saveToStorage();
|
|
1183
|
+
this.notifySessionListeners();
|
|
1184
|
+
this.notifyListeners();
|
|
1185
|
+
}
|
|
1186
|
+
async getCurrentUser() {
|
|
1187
|
+
const user = await this.currentUserApi.me();
|
|
1188
|
+
return { ...user, tenantId: user.tenant.id };
|
|
1189
|
+
}
|
|
1190
|
+
clearAuthState() {
|
|
1191
|
+
const hadAuthState = this._currentUser !== null || this.#accessToken !== null || this.#refreshToken !== null;
|
|
1192
|
+
this.invalidatePendingRefreshes();
|
|
1193
|
+
this._currentUser = null;
|
|
1194
|
+
this.#accessToken = null;
|
|
1195
|
+
this.#refreshToken = null;
|
|
1196
|
+
this.removeFromStorage();
|
|
1197
|
+
if (hadAuthState) {
|
|
1198
|
+
this.notifySessionListeners();
|
|
1199
|
+
this.notifyListeners();
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
invalidatePendingRefreshes() {
|
|
1203
|
+
this.sessionGeneration += 1;
|
|
1204
|
+
this.transientRefreshFailureGeneration = null;
|
|
1205
|
+
}
|
|
1206
|
+
hasValidAppIdentity() {
|
|
1207
|
+
return (!this.#accessToken || this.belongsToConfiguredApp(this.#accessToken)) && (!this.#refreshToken || this.belongsToConfiguredApp(this.#refreshToken));
|
|
1208
|
+
}
|
|
1209
|
+
belongsToConfiguredApp(token) {
|
|
1210
|
+
return belongsToApp(token, this.appId);
|
|
1211
|
+
}
|
|
1212
|
+
notifySessionListeners() {
|
|
1213
|
+
const session = this.readSessionTokens();
|
|
1214
|
+
this.sessionListeners.forEach((callback) => {
|
|
1215
|
+
try {
|
|
1216
|
+
callback(session);
|
|
1217
|
+
} catch {
|
|
1218
|
+
}
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
notifyListeners() {
|
|
1222
|
+
this.listeners.forEach((callback) => {
|
|
1223
|
+
try {
|
|
1224
|
+
callback(this._currentUser);
|
|
1225
|
+
} catch (error) {
|
|
1226
|
+
console.error("Auth state change listener error:", error);
|
|
1227
|
+
}
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
saveToStorage() {
|
|
1231
|
+
if (typeof localStorage === "undefined") return;
|
|
1232
|
+
try {
|
|
1233
|
+
localStorage.setItem(
|
|
1234
|
+
this.storageKey,
|
|
1235
|
+
JSON.stringify({
|
|
1236
|
+
user: this._currentUser,
|
|
1237
|
+
token: this.#accessToken,
|
|
1238
|
+
refreshToken: this.#refreshToken
|
|
1239
|
+
})
|
|
1240
|
+
);
|
|
1241
|
+
} catch {
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
loadFromStorage() {
|
|
1245
|
+
if (typeof localStorage === "undefined") return;
|
|
1246
|
+
try {
|
|
1247
|
+
const stored = localStorage.getItem(this.storageKey);
|
|
1248
|
+
if (stored) {
|
|
1249
|
+
const { user, token, refreshToken } = JSON.parse(stored);
|
|
1250
|
+
if (typeof token !== "string" || refreshToken !== null && refreshToken !== void 0 && typeof refreshToken !== "string" || !this.belongsToConfiguredApp(token) || typeof refreshToken === "string" && !this.belongsToConfiguredApp(refreshToken)) {
|
|
1251
|
+
this.clearAuthState();
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
this._currentUser = user;
|
|
1255
|
+
this.#accessToken = token;
|
|
1256
|
+
this.#refreshToken = refreshToken ?? null;
|
|
1257
|
+
this.invalidatePendingRefreshes();
|
|
1258
|
+
}
|
|
1259
|
+
} catch {
|
|
1260
|
+
this.removeFromStorage();
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
removeFromStorage() {
|
|
1264
|
+
if (typeof localStorage === "undefined") return;
|
|
1265
|
+
try {
|
|
1266
|
+
localStorage.removeItem(this.storageKey);
|
|
1267
|
+
} catch {
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1271
|
+
|
|
1272
|
+
// src/modules/entities.ts
|
|
1273
|
+
var import_sdk_core3 = require("@mitralab.io/sdk-core");
|
|
1274
|
+
var EntitiesModule = class _EntitiesModule {
|
|
1275
|
+
constructor(httpClient, _dataSourceId) {
|
|
1276
|
+
this.httpClient = httpClient;
|
|
1277
|
+
this.core = (0, import_sdk_core3.createEntitiesModule)(httpClient, coreErrors);
|
|
1278
|
+
}
|
|
1279
|
+
core;
|
|
1280
|
+
static createProxy(httpClient, dataSourceId) {
|
|
1281
|
+
const instance = new _EntitiesModule(httpClient, dataSourceId);
|
|
1282
|
+
return new Proxy(instance, {
|
|
1283
|
+
get(target, property, receiver) {
|
|
1284
|
+
if (typeof property !== "string" || property in target) {
|
|
1285
|
+
return Reflect.get(target, property, receiver);
|
|
1286
|
+
}
|
|
1287
|
+
return target.getTable(property);
|
|
1288
|
+
}
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
/**
|
|
1292
|
+
* Preserved for Platform SDK 1.x compatibility.
|
|
1293
|
+
* Records now resolve the app from authenticated context instead of a data source path.
|
|
1294
|
+
*/
|
|
1295
|
+
setDataSourceId(_dataSourceId) {
|
|
1296
|
+
this.core = (0, import_sdk_core3.createEntitiesModule)(this.httpClient, coreErrors);
|
|
1297
|
+
}
|
|
1298
|
+
getTable(tableName) {
|
|
1299
|
+
return this.core.getTable(tableName);
|
|
1300
|
+
}
|
|
1301
|
+
};
|
|
1302
|
+
|
|
1303
|
+
// src/modules/functions.ts
|
|
1304
|
+
var import_sdk_core4 = require("@mitralab.io/sdk-core");
|
|
1305
|
+
var FunctionsModule = class {
|
|
1306
|
+
core;
|
|
1307
|
+
constructor(httpClient) {
|
|
1308
|
+
this.core = (0, import_sdk_core4.createFunctionsModule)(
|
|
1309
|
+
httpClient,
|
|
1310
|
+
{ emptyInput: "omit-body", executeInvocationType: "sync" },
|
|
1311
|
+
coreErrors
|
|
1312
|
+
);
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* Executes a Function synchronously and waits for its terminal result.
|
|
1316
|
+
*/
|
|
1317
|
+
async execute(functionId, input) {
|
|
1318
|
+
return this.core.execute(functionId, input);
|
|
1319
|
+
}
|
|
1320
|
+
/** Queues a Function and returns its initial execution record. */
|
|
1321
|
+
async executeAsync(functionId, input) {
|
|
1322
|
+
return this.core.executeAsync(functionId, input);
|
|
1323
|
+
}
|
|
1324
|
+
/** Reads the current state of an asynchronous Function execution. */
|
|
1325
|
+
async getExecution(executionId) {
|
|
1326
|
+
return this.core.getExecution(executionId);
|
|
1327
|
+
}
|
|
1328
|
+
/** Requests cancellation of a queued or running Function execution. */
|
|
1329
|
+
cancelExecution(executionId) {
|
|
1330
|
+
return this.core.cancelExecution(executionId);
|
|
1331
|
+
}
|
|
1332
|
+
};
|
|
1333
|
+
|
|
1334
|
+
// src/modules/integration.ts
|
|
1335
|
+
var import_sdk_core5 = require("@mitralab.io/sdk-core");
|
|
1336
|
+
var IntegrationModule = class {
|
|
1337
|
+
core;
|
|
1338
|
+
configs;
|
|
1339
|
+
constructor(httpClient) {
|
|
1340
|
+
this.core = (0, import_sdk_core5.createIntegrationModule)(httpClient, coreErrors);
|
|
1341
|
+
this.configs = (0, import_sdk_core5.createIntegrationAdminModule)(httpClient, coreErrors);
|
|
1342
|
+
}
|
|
1343
|
+
/** Lists the current app's integration configs without exposing admin mutations. */
|
|
1344
|
+
list(options) {
|
|
1345
|
+
return this.configs.list(options);
|
|
1346
|
+
}
|
|
1347
|
+
executeResource(resourceId, params) {
|
|
1348
|
+
return this.core.executeResource(resourceId, params);
|
|
1349
|
+
}
|
|
1350
|
+
execute(configId, request) {
|
|
1351
|
+
return this.core.execute(configId, request);
|
|
1352
|
+
}
|
|
1353
|
+
/** Executes a saved integration config selected by its app-scoped alias. */
|
|
1354
|
+
executeByAlias(alias, request) {
|
|
1355
|
+
return this.core.executeByAlias(alias, request);
|
|
1356
|
+
}
|
|
1357
|
+
};
|
|
1358
|
+
|
|
1359
|
+
// src/modules/queries.ts
|
|
1360
|
+
var import_sdk_core6 = require("@mitralab.io/sdk-core");
|
|
1361
|
+
var QueriesModule = class {
|
|
1362
|
+
core;
|
|
1363
|
+
constructor(httpClient) {
|
|
1364
|
+
this.core = (0, import_sdk_core6.createQueriesModule)(httpClient, coreErrors);
|
|
1365
|
+
}
|
|
1366
|
+
/**
|
|
1367
|
+
* @deprecated Preserved for Platform SDK 1.x source compatibility. Data Manager now resolves
|
|
1368
|
+
* the Data Source from the authenticated app.
|
|
1369
|
+
*/
|
|
1370
|
+
setDataSourceId(_dataSourceId) {
|
|
1371
|
+
}
|
|
1372
|
+
async execute(id, parameters) {
|
|
1373
|
+
return this.core.execute(id, parameters);
|
|
1374
|
+
}
|
|
1375
|
+
};
|
|
1376
|
+
|
|
1377
|
+
// src/modules/agent-tasks.ts
|
|
1378
|
+
var import_sdk_core7 = require("@mitralab.io/sdk-core");
|
|
1379
|
+
|
|
1380
|
+
// src/modules/agent-session.ts
|
|
1381
|
+
var CONNECT_TIMEOUT_MS = 15e3;
|
|
1382
|
+
function stripBearer2(token) {
|
|
1383
|
+
return token.replace(/^Bearer\s+/i, "");
|
|
1384
|
+
}
|
|
1385
|
+
function asObject(value) {
|
|
1386
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
1387
|
+
}
|
|
1388
|
+
function expectEvent(value) {
|
|
1389
|
+
const event = asObject(value);
|
|
1390
|
+
if (!event || typeof event.type !== "string" || !event.type) return null;
|
|
1391
|
+
if (typeof event.timestamp !== "number" || !Number.isFinite(event.timestamp)) return null;
|
|
1392
|
+
if (event.sequence !== void 0 && (typeof event.sequence !== "number" || !Number.isSafeInteger(event.sequence) || event.sequence < 0)) return null;
|
|
1393
|
+
return {
|
|
1394
|
+
type: event.type,
|
|
1395
|
+
payload: event.payload,
|
|
1396
|
+
timestamp: event.timestamp,
|
|
1397
|
+
...typeof event.sequence === "number" ? { sequence: event.sequence } : {}
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
function parseEvent(raw) {
|
|
1401
|
+
if (typeof raw !== "string") return expectEvent(raw);
|
|
1402
|
+
try {
|
|
1403
|
+
return expectEvent(JSON.parse(raw));
|
|
1404
|
+
} catch {
|
|
1405
|
+
return null;
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
function isNormalClose(event) {
|
|
1409
|
+
return event.code === 1e3 || event.code === 4409;
|
|
1410
|
+
}
|
|
1411
|
+
var BrowserAgentTaskEventSource = class {
|
|
1412
|
+
constructor(auth, apiUrl) {
|
|
1413
|
+
this.auth = auth;
|
|
1414
|
+
this.apiUrl = stripTrailingSlashes(apiUrl);
|
|
1415
|
+
}
|
|
1416
|
+
apiUrl;
|
|
1417
|
+
sseFallbackTasks = /* @__PURE__ */ new Set();
|
|
1418
|
+
async open(taskId, observer, signal, transport = "auto") {
|
|
1419
|
+
if (transport === "http") return this.openSse(taskId, observer, signal);
|
|
1420
|
+
if (transport === "websocket") return this.openWebSocket(taskId, observer, signal);
|
|
1421
|
+
if (this.sseFallbackTasks.has(taskId)) {
|
|
1422
|
+
return this.wrapAutoConnection(taskId, await this.openSse(taskId, observer, signal));
|
|
1423
|
+
}
|
|
1424
|
+
try {
|
|
1425
|
+
const connection = await this.openWebSocket(taskId, {
|
|
1426
|
+
...observer,
|
|
1427
|
+
onDisconnect: (error) => {
|
|
1428
|
+
this.sseFallbackTasks.add(taskId);
|
|
1429
|
+
observer.onDisconnect(error);
|
|
1430
|
+
}
|
|
1431
|
+
}, signal);
|
|
1432
|
+
return this.wrapAutoConnection(taskId, connection);
|
|
1433
|
+
} catch {
|
|
1434
|
+
this.sseFallbackTasks.add(taskId);
|
|
1435
|
+
return this.wrapAutoConnection(taskId, await this.openSse(taskId, observer, signal));
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
wrapAutoConnection(taskId, connection) {
|
|
1439
|
+
return {
|
|
1440
|
+
close: () => {
|
|
1441
|
+
this.sseFallbackTasks.delete(taskId);
|
|
1442
|
+
connection.close();
|
|
1443
|
+
}
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1446
|
+
async requireFreshToken() {
|
|
1447
|
+
const fresh = await this.auth.ensureFreshSession();
|
|
1448
|
+
const token = this.auth.accessToken;
|
|
1449
|
+
if (!fresh || !token) {
|
|
1450
|
+
throw new Error("A fresh authenticated app session is required for Agent streaming.");
|
|
1451
|
+
}
|
|
1452
|
+
return token;
|
|
1453
|
+
}
|
|
1454
|
+
async openWebSocket(taskId, observer, signal) {
|
|
1455
|
+
if (typeof globalThis.WebSocket !== "function") {
|
|
1456
|
+
throw new TypeError("WebSocket is not available.");
|
|
1457
|
+
}
|
|
1458
|
+
if (signal?.aborted) throw signal.reason ?? new Error("Agent WebSocket connection aborted.");
|
|
1459
|
+
const token = await this.requireFreshToken();
|
|
1460
|
+
const url = `${this.apiUrl.replace(/^http/i, "ws")}/copilot/ws/tasks/${encodeURIComponent(taskId)}?token=${encodeURIComponent(stripBearer2(token))}`;
|
|
1461
|
+
return new Promise((resolve, reject) => {
|
|
1462
|
+
const socket = new globalThis.WebSocket(url);
|
|
1463
|
+
let opened = false;
|
|
1464
|
+
let intentionalClose = false;
|
|
1465
|
+
let settled = false;
|
|
1466
|
+
const removeAbortListener = () => signal?.removeEventListener("abort", onAbort);
|
|
1467
|
+
const clearHandshake = () => {
|
|
1468
|
+
globalThis.clearTimeout(timer);
|
|
1469
|
+
removeAbortListener();
|
|
1470
|
+
};
|
|
1471
|
+
const rejectHandshake = (error) => {
|
|
1472
|
+
if (settled) return;
|
|
1473
|
+
settled = true;
|
|
1474
|
+
intentionalClose = true;
|
|
1475
|
+
clearHandshake();
|
|
1476
|
+
socket.close();
|
|
1477
|
+
reject(error);
|
|
1478
|
+
};
|
|
1479
|
+
const close = () => {
|
|
1480
|
+
if (intentionalClose) return;
|
|
1481
|
+
intentionalClose = true;
|
|
1482
|
+
removeAbortListener();
|
|
1483
|
+
socket.close(1e3, "Client closed");
|
|
1484
|
+
};
|
|
1485
|
+
const onAbort = () => {
|
|
1486
|
+
if (!opened) {
|
|
1487
|
+
rejectHandshake(signal?.reason instanceof Error ? signal.reason : new Error("Agent WebSocket connection aborted."));
|
|
1488
|
+
return;
|
|
1489
|
+
}
|
|
1490
|
+
close();
|
|
1491
|
+
};
|
|
1492
|
+
const timer = globalThis.setTimeout(() => {
|
|
1493
|
+
rejectHandshake(new Error("Timed out connecting to the Agent WebSocket."));
|
|
1494
|
+
}, CONNECT_TIMEOUT_MS);
|
|
1495
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1496
|
+
socket.onopen = () => {
|
|
1497
|
+
if (settled) return;
|
|
1498
|
+
opened = true;
|
|
1499
|
+
settled = true;
|
|
1500
|
+
globalThis.clearTimeout(timer);
|
|
1501
|
+
resolve({ close });
|
|
1502
|
+
};
|
|
1503
|
+
socket.onerror = () => {
|
|
1504
|
+
if (!opened) rejectHandshake(new Error("Failed to connect to the Agent WebSocket."));
|
|
1505
|
+
};
|
|
1506
|
+
socket.onmessage = (message) => {
|
|
1507
|
+
const event = parseEvent(message.data);
|
|
1508
|
+
if (event) observer.onEvent(event);
|
|
1509
|
+
};
|
|
1510
|
+
socket.onclose = (event) => {
|
|
1511
|
+
globalThis.clearTimeout(timer);
|
|
1512
|
+
removeAbortListener();
|
|
1513
|
+
if (!opened) {
|
|
1514
|
+
rejectHandshake(new Error(`Agent WebSocket closed during handshake (${event.code}).`));
|
|
1515
|
+
return;
|
|
1516
|
+
}
|
|
1517
|
+
if (!intentionalClose && !isNormalClose(event)) {
|
|
1518
|
+
observer.onDisconnect(new Error(`Agent WebSocket disconnected (${event.code}).`));
|
|
1519
|
+
}
|
|
1520
|
+
};
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
async openSse(taskId, observer, signal) {
|
|
1524
|
+
if (signal?.aborted) throw signal.reason ?? new Error("Agent SSE connection aborted.");
|
|
1525
|
+
const requestToken = await this.requireFreshToken();
|
|
1526
|
+
const abort = new AbortController();
|
|
1527
|
+
let intentionalClose = false;
|
|
1528
|
+
let disconnected = false;
|
|
1529
|
+
const url = `${this.apiUrl}/copilot/api/v1/tasks/${encodeURIComponent(taskId)}/events`;
|
|
1530
|
+
const request = (token) => globalThis.fetch(url, {
|
|
1531
|
+
headers: { Accept: "text/event-stream", Authorization: `Bearer ${stripBearer2(token)}` },
|
|
1532
|
+
signal: abort.signal
|
|
1533
|
+
});
|
|
1534
|
+
const onAbort = () => {
|
|
1535
|
+
intentionalClose = true;
|
|
1536
|
+
abort.abort(signal?.reason);
|
|
1537
|
+
};
|
|
1538
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1539
|
+
let response = await request(requestToken);
|
|
1540
|
+
if (response.status === 401 && await this.auth.handleUnauthorized(requestToken)) {
|
|
1541
|
+
const currentToken = this.auth.accessToken;
|
|
1542
|
+
if (currentToken) response = await request(currentToken);
|
|
1543
|
+
}
|
|
1544
|
+
if (!response.ok || !response.body) {
|
|
1545
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1546
|
+
abort.abort();
|
|
1547
|
+
throw new Error(`Agent SSE connection failed (${response.status}).`);
|
|
1548
|
+
}
|
|
1549
|
+
const disconnect = (error) => {
|
|
1550
|
+
if (disconnected || intentionalClose || signal?.aborted) return;
|
|
1551
|
+
disconnected = true;
|
|
1552
|
+
observer.onDisconnect(error);
|
|
1553
|
+
};
|
|
1554
|
+
void this.readSse(response.body, observer, abort.signal).then(() => disconnect()).catch((error) => disconnect(error)).finally(() => signal?.removeEventListener("abort", onAbort));
|
|
1555
|
+
return {
|
|
1556
|
+
close: () => {
|
|
1557
|
+
if (intentionalClose) return;
|
|
1558
|
+
intentionalClose = true;
|
|
1559
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1560
|
+
abort.abort();
|
|
1561
|
+
}
|
|
1562
|
+
};
|
|
1563
|
+
}
|
|
1564
|
+
async readSse(body, observer, signal) {
|
|
1565
|
+
const reader = body.getReader();
|
|
1566
|
+
const decoder = new TextDecoder();
|
|
1567
|
+
let buffer = "";
|
|
1568
|
+
try {
|
|
1569
|
+
while (!signal.aborted) {
|
|
1570
|
+
const { done, value } = await reader.read();
|
|
1571
|
+
if (done) break;
|
|
1572
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1573
|
+
let separator = /\r?\n\r?\n/.exec(buffer);
|
|
1574
|
+
while (separator?.index !== void 0) {
|
|
1575
|
+
const block = buffer.slice(0, separator.index);
|
|
1576
|
+
buffer = buffer.slice(separator.index + separator[0].length);
|
|
1577
|
+
const data = block.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
|
|
1578
|
+
const event = data ? parseEvent(data) : null;
|
|
1579
|
+
if (event) observer.onEvent(event);
|
|
1580
|
+
separator = /\r?\n\r?\n/.exec(buffer);
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
} finally {
|
|
1584
|
+
reader.releaseLock();
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
};
|
|
1588
|
+
|
|
1589
|
+
// src/modules/agent-tasks.ts
|
|
1590
|
+
function createBrowserAgentTasksModule(httpClient, auth, apiUrl) {
|
|
1591
|
+
const tasks = (0, import_sdk_core7.createAgentTasksModule)(httpClient, coreErrors);
|
|
1592
|
+
const manager = (0, import_sdk_core7.createAgentTaskSessionManager)({
|
|
1593
|
+
tasks,
|
|
1594
|
+
eventSource: new BrowserAgentTaskEventSource(auth, apiUrl)
|
|
1595
|
+
});
|
|
1596
|
+
return (0, import_sdk_core7.withAgentTaskSessions)(tasks, manager);
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
// src/modules/agent-credentials.ts
|
|
1600
|
+
var import_sdk_core8 = require("@mitralab.io/sdk-core");
|
|
1601
|
+
function requireProvider(provider, allowed, flow) {
|
|
1602
|
+
if (!allowed.includes(provider)) {
|
|
1603
|
+
throw new TypeError(`${flow} does not support provider ${provider}.`);
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
function createBrowserAgentCredentialsModule(httpClient) {
|
|
1607
|
+
const core = (0, import_sdk_core8.createAgentCredentialsModule)(httpClient, coreErrors);
|
|
1608
|
+
return {
|
|
1609
|
+
list: () => core.list(),
|
|
1610
|
+
listModels: (agentId) => core.listModels(agentId),
|
|
1611
|
+
saveApiKey: (provider, apiKey) => {
|
|
1612
|
+
requireProvider(provider, ["ANTHROPIC", "OPENAI"], "API key authentication");
|
|
1613
|
+
return core.saveApiKey(provider, apiKey);
|
|
1614
|
+
},
|
|
1615
|
+
remove: (provider) => {
|
|
1616
|
+
requireProvider(provider, ["ANTHROPIC", "OPENAI"], "Credential removal");
|
|
1617
|
+
return core.remove(provider);
|
|
1618
|
+
},
|
|
1619
|
+
startOAuth: (provider) => {
|
|
1620
|
+
requireProvider(provider, ["ANTHROPIC"], "OAuth");
|
|
1621
|
+
return core.startOAuth(provider);
|
|
1622
|
+
},
|
|
1623
|
+
exchangeOAuth: (provider, input) => {
|
|
1624
|
+
requireProvider(provider, ["ANTHROPIC"], "OAuth");
|
|
1625
|
+
return core.exchangeOAuth(provider, input);
|
|
1626
|
+
},
|
|
1627
|
+
startDeviceAuthorization: (provider) => {
|
|
1628
|
+
requireProvider(provider, ["OPENAI"], "Device authorization");
|
|
1629
|
+
return core.startDeviceAuthorization(provider);
|
|
1630
|
+
},
|
|
1631
|
+
pollDeviceAuthorization: (provider, deviceAuthId) => {
|
|
1632
|
+
requireProvider(provider, ["OPENAI"], "Device authorization");
|
|
1633
|
+
return core.pollDeviceAuthorization(provider, deviceAuthId);
|
|
1634
|
+
}
|
|
1635
|
+
};
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
// src/client.ts
|
|
1639
|
+
function expectAppInfoResponse(value) {
|
|
1640
|
+
const response = (0, import_sdk_core9.expectObject)(
|
|
1641
|
+
value,
|
|
1642
|
+
"App info response",
|
|
1643
|
+
coreErrors
|
|
1644
|
+
);
|
|
1645
|
+
if (response.dataSourceId !== null && (typeof response.dataSourceId !== "string" || !response.dataSourceId.trim())) {
|
|
1646
|
+
throw coreErrors.invalidResponse(
|
|
1647
|
+
"App info response has an invalid dataSourceId field"
|
|
1648
|
+
);
|
|
1649
|
+
}
|
|
1650
|
+
if (typeof response.allowSignup !== "boolean") {
|
|
1651
|
+
throw coreErrors.invalidResponse(
|
|
1652
|
+
"App info response has an invalid allowSignup field"
|
|
1653
|
+
);
|
|
1654
|
+
}
|
|
1655
|
+
return {
|
|
1656
|
+
dataSourceId: response.dataSourceId,
|
|
1657
|
+
allowSignup: response.allowSignup
|
|
1658
|
+
};
|
|
1659
|
+
}
|
|
1660
|
+
function createClient(config) {
|
|
1661
|
+
const { appId, apiUrl, authPageUrl, onError } = config;
|
|
1662
|
+
const gatewayUrl = stripTrailingSlashes(apiUrl);
|
|
1663
|
+
const iamUrl = `${gatewayUrl}/iam`;
|
|
1664
|
+
const dataManagerUrl = `${gatewayUrl}/data-manager`;
|
|
1665
|
+
const functionsUrl = `${gatewayUrl}/functions`;
|
|
1666
|
+
const integrationUrl = `${gatewayUrl}/integration`;
|
|
1667
|
+
const codeStudioUrl = `${gatewayUrl}/code-studio`;
|
|
1668
|
+
const copilotUrl = `${gatewayUrl}/copilot`;
|
|
1669
|
+
const authModule = new AuthModule(appId, iamUrl, { apiUrl: gatewayUrl, authPageUrl });
|
|
1670
|
+
const authSession = getAuthSessionPort(authModule);
|
|
1671
|
+
const onUnauthorized = (requestToken) => authSession.handleUnauthorized(requestToken);
|
|
1672
|
+
const beforeAuthenticatedRequest = () => authModule.ensureFreshSession().then(() => void 0);
|
|
1673
|
+
const defaultHeaders = { "X-App-Id": appId };
|
|
1674
|
+
const httpClient = new HttpClient({
|
|
1675
|
+
baseUrl: dataManagerUrl,
|
|
1676
|
+
getToken: () => authModule.accessToken,
|
|
1677
|
+
beforeAuthenticatedRequest,
|
|
1678
|
+
onUnauthorized,
|
|
1679
|
+
onError,
|
|
1680
|
+
defaultHeaders
|
|
1681
|
+
});
|
|
1682
|
+
const entitiesModule = EntitiesModule.createProxy(httpClient, "");
|
|
1683
|
+
const functionsHttpClient = new HttpClient({
|
|
1684
|
+
baseUrl: functionsUrl,
|
|
1685
|
+
getToken: () => authModule.accessToken,
|
|
1686
|
+
beforeAuthenticatedRequest,
|
|
1687
|
+
onUnauthorized,
|
|
1688
|
+
onError,
|
|
1689
|
+
defaultHeaders
|
|
1690
|
+
});
|
|
1691
|
+
const functionsModule = new FunctionsModule(functionsHttpClient);
|
|
1692
|
+
const publicFunctionsModule = (0, import_sdk_core9.createPublicFunctionsModule)(
|
|
1693
|
+
new HttpClient({ baseUrl: functionsUrl, getToken: () => null }),
|
|
1694
|
+
coreErrors
|
|
1695
|
+
);
|
|
1696
|
+
const copilotHttpClient = new HttpClient({
|
|
1697
|
+
baseUrl: copilotUrl,
|
|
1698
|
+
getToken: () => authModule.accessToken,
|
|
1699
|
+
beforeAuthenticatedRequest,
|
|
1700
|
+
onUnauthorized,
|
|
1701
|
+
onError,
|
|
1702
|
+
defaultHeaders
|
|
1703
|
+
});
|
|
1704
|
+
const agentTasksModule = createBrowserAgentTasksModule(
|
|
1705
|
+
copilotHttpClient,
|
|
1706
|
+
authSession,
|
|
1707
|
+
gatewayUrl
|
|
1708
|
+
);
|
|
1709
|
+
const agentCredentialsModule = createBrowserAgentCredentialsModule(copilotHttpClient);
|
|
1710
|
+
const integrationHttpClient = new HttpClient({
|
|
1711
|
+
baseUrl: integrationUrl,
|
|
1712
|
+
getToken: () => authModule.accessToken,
|
|
1713
|
+
beforeAuthenticatedRequest,
|
|
1714
|
+
onUnauthorized,
|
|
1715
|
+
onError,
|
|
1716
|
+
defaultHeaders
|
|
1717
|
+
});
|
|
1718
|
+
const integrationModule = new IntegrationModule(integrationHttpClient);
|
|
1719
|
+
const queriesModule = new QueriesModule(httpClient);
|
|
1720
|
+
const legacyBridge = new LegacySessionBridge(authSession, appId, apiUrl, authPageUrl);
|
|
1721
|
+
setActiveBridge(legacyBridge);
|
|
1722
|
+
legacyBridge.connect();
|
|
1723
|
+
let initialized = false;
|
|
1724
|
+
let allowSignup = true;
|
|
1725
|
+
async function init() {
|
|
1726
|
+
if (initialized) return;
|
|
1727
|
+
const publicClient = new HttpClient({
|
|
1728
|
+
baseUrl: codeStudioUrl,
|
|
1729
|
+
getToken: () => null
|
|
1730
|
+
});
|
|
1731
|
+
const appInfo = expectAppInfoResponse(
|
|
1732
|
+
await publicClient.get(
|
|
1733
|
+
`/api/v1/apps/${(0, import_sdk_core9.encodePathSegment)(appId, "appId", coreErrors)}/info`
|
|
1734
|
+
)
|
|
1735
|
+
);
|
|
1736
|
+
if (appInfo.dataSourceId) {
|
|
1737
|
+
entitiesModule.setDataSourceId(appInfo.dataSourceId);
|
|
1738
|
+
}
|
|
1739
|
+
allowSignup = appInfo.allowSignup;
|
|
1740
|
+
initialized = true;
|
|
1741
|
+
}
|
|
1742
|
+
return {
|
|
1743
|
+
init,
|
|
1744
|
+
auth: authModule,
|
|
1745
|
+
entities: entitiesModule,
|
|
1746
|
+
functions: functionsModule,
|
|
1747
|
+
publicFunctions: publicFunctionsModule,
|
|
1748
|
+
agentTasks: agentTasksModule,
|
|
1749
|
+
agentCredentials: agentCredentialsModule,
|
|
1750
|
+
integration: integrationModule,
|
|
1751
|
+
queries: queriesModule,
|
|
1752
|
+
get allowSignup() {
|
|
1753
|
+
return allowSignup;
|
|
1754
|
+
},
|
|
1755
|
+
config
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
// src/legacy/index.ts
|
|
1760
|
+
var import_mitra_interactions_sdk2 = require("mitra-interactions-sdk");
|
|
1761
|
+
var import_mitra_interactions_sdk3 = require("mitra-interactions-sdk");
|
|
1762
|
+
var import_mitra_interactions_sdk4 = require("mitra-interactions-sdk");
|
|
1763
|
+
var import_mitra_interactions_sdk5 = require("mitra-interactions-sdk");
|
|
1764
|
+
var import_mitra_interactions_sdk6 = require("mitra-interactions-sdk");
|
|
1765
|
+
var import_mitra_interactions_sdk7 = require("mitra-interactions-sdk");
|
|
1766
|
+
var import_mitra_interactions_sdk8 = require("mitra-interactions-sdk");
|
|
1767
|
+
var import_mitra_interactions_sdk9 = require("mitra-interactions-sdk");
|
|
1768
|
+
var import_mitra_interactions_sdk10 = require("mitra-interactions-sdk");
|
|
1769
|
+
var import_mitra_interactions_sdk11 = require("mitra-interactions-sdk");
|
|
1770
|
+
var import_mitra_interactions_sdk12 = require("mitra-interactions-sdk");
|
|
1771
|
+
var import_mitra_interactions_sdk13 = require("mitra-interactions-sdk");
|
|
1772
|
+
var import_mitra_interactions_sdk14 = require("mitra-interactions-sdk");
|
|
1773
|
+
var import_mitra_interactions_sdk15 = require("mitra-interactions-sdk");
|
|
1774
|
+
var import_mitra_interactions_sdk16 = require("mitra-interactions-sdk");
|
|
1775
|
+
var import_mitra_interactions_sdk17 = require("mitra-interactions-sdk");
|
|
1776
|
+
var import_mitra_interactions_sdk18 = require("mitra-interactions-sdk");
|
|
1777
|
+
var import_mitra_interactions_sdk19 = require("mitra-interactions-sdk");
|
|
1778
|
+
var import_mitra_interactions_sdk20 = require("mitra-interactions-sdk");
|
|
1779
|
+
var import_mitra_interactions_sdk21 = require("mitra-interactions-sdk");
|
|
1780
|
+
var import_mitra_interactions_sdk22 = require("mitra-interactions-sdk");
|
|
1781
|
+
var import_mitra_interactions_sdk23 = require("mitra-interactions-sdk");
|
|
1782
|
+
var import_mitra_interactions_sdk24 = require("mitra-interactions-sdk");
|
|
1783
|
+
var import_mitra_interactions_sdk25 = require("mitra-interactions-sdk");
|
|
1784
|
+
var loginMitra = async (method, options) => {
|
|
1785
|
+
const session = await (0, import_mitra_interactions_sdk2.loginMitra)(method, options);
|
|
1786
|
+
adoptLegacySession(session);
|
|
1787
|
+
return session;
|
|
1788
|
+
};
|
|
1789
|
+
var loginWithGoogleMitra = async (options) => {
|
|
1790
|
+
const session = await (0, import_mitra_interactions_sdk2.loginWithGoogleMitra)(options);
|
|
1791
|
+
adoptLegacySession(session);
|
|
1792
|
+
return session;
|
|
1793
|
+
};
|
|
1794
|
+
var loginWithMicrosoftMitra = async (options) => {
|
|
1795
|
+
const session = await (0, import_mitra_interactions_sdk2.loginWithMicrosoftMitra)(options);
|
|
1796
|
+
adoptLegacySession(session);
|
|
1797
|
+
return session;
|
|
1798
|
+
};
|
|
1799
|
+
var exchangeSsoCodeMitra = async (options) => {
|
|
1800
|
+
const session = await (0, import_mitra_interactions_sdk2.exchangeSsoCodeMitra)(options);
|
|
1801
|
+
adoptLegacySession(session);
|
|
1802
|
+
return session;
|
|
1803
|
+
};
|
|
1804
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1805
|
+
0 && (module.exports = {
|
|
1806
|
+
MitraApiError,
|
|
1807
|
+
callIntegrationMitra,
|
|
1808
|
+
configureSdkMitra,
|
|
1809
|
+
createClient,
|
|
1810
|
+
createMitraInstance,
|
|
1811
|
+
createRecordMitra,
|
|
1812
|
+
createRecordsBatchMitra,
|
|
1813
|
+
deleteRecordMitra,
|
|
1814
|
+
exchangeSsoCodeMitra,
|
|
1815
|
+
executePublicServerFunctionAsyncMitra,
|
|
1816
|
+
executePublicServerFunctionMitra,
|
|
1817
|
+
executeServerFunctionAsyncMitra,
|
|
1818
|
+
executeServerFunctionMitra,
|
|
1819
|
+
getAgentTaskMitra,
|
|
1820
|
+
getConfig,
|
|
1821
|
+
getPublicServerFunctionExecutionMitra,
|
|
1822
|
+
getRecordMitra,
|
|
1823
|
+
listIntegrationsMitra,
|
|
1824
|
+
listRecordsMitra,
|
|
1825
|
+
loginMitra,
|
|
1826
|
+
loginWithGoogleMitra,
|
|
1827
|
+
loginWithMicrosoftMitra,
|
|
1828
|
+
manageAgentChatMitra,
|
|
1829
|
+
manageAgentCredentialMitra,
|
|
1830
|
+
patchRecordMitra,
|
|
1831
|
+
refreshTokenSilently,
|
|
1832
|
+
resolveProjectId,
|
|
1833
|
+
stopServerFunctionExecutionMitra,
|
|
1834
|
+
updateRecordMitra
|
|
1835
|
+
});
|