@base44-preview/sdk 0.8.48-pr.282.8affbe6 → 0.8.48-pr.282.a87ae54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.js +78 -62
- package/dist/modules/actors.d.ts +1 -1
- package/dist/modules/actors.js +1 -1
- package/dist/modules/auth.js +12 -7
- package/dist/modules/auth.types.d.ts +5 -0
- package/dist/modules/functions.js +4 -0
- package/dist/modules/functions.types.d.ts +6 -0
- package/dist/utils/embed-session.d.ts +11 -2
- package/dist/utils/embed-session.js +32 -8
- package/dist/utils/fetch-with-auth.d.ts +3 -1
- package/dist/utils/fetch-with-auth.js +4 -1
- package/dist/utils/socket-utils.d.ts +3 -4
- package/dist/utils/socket-utils.js +5 -10
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -5,7 +5,7 @@ import { createAuthModule } from "./modules/auth.js";
|
|
|
5
5
|
import { createSsoModule } from "./modules/sso.js";
|
|
6
6
|
import { createConnectorsModule, createUserConnectorsModule, } from "./modules/connectors.js";
|
|
7
7
|
import { getAccessToken } from "./utils/auth-utils.js";
|
|
8
|
-
import { exchangeEmbedToken, isEmbeddedTab, markEmbeddedTab, takeEmbedTokenFromUrl, } from "./utils/embed-session.js";
|
|
8
|
+
import { exchangeEmbedToken, isEmbeddedTab, isFramed, markEmbeddedTab, takeEmbedTokenFromUrl, } from "./utils/embed-session.js";
|
|
9
9
|
import { createFetchWithAuth } from "./utils/fetch-with-auth.js";
|
|
10
10
|
import { createFunctionsModule } from "./modules/functions.js";
|
|
11
11
|
import { createAgentsModule } from "./modules/agents.js";
|
|
@@ -55,41 +55,22 @@ import { createActorsModule, resolveActorsHost, } from "./modules/actors.js";
|
|
|
55
55
|
*/
|
|
56
56
|
export function createClient(config) {
|
|
57
57
|
var _a, _b, _c, _d;
|
|
58
|
-
const { serverUrl = "https://base44.app", appId, analytics,
|
|
58
|
+
const { serverUrl = "https://base44.app", appId, analytics, serviceToken, requiresAuth = false, appBaseUrl, options, functionsVersion, headers: optionalHeaders, } = config;
|
|
59
59
|
// Normalize appBaseUrl to always be a string (empty if not provided or invalid)
|
|
60
60
|
const normalizedAppBaseUrl = typeof appBaseUrl === "string" ? appBaseUrl : "";
|
|
61
|
-
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
const
|
|
61
|
+
// Always taken off the URL, even outside a frame: a one-time token must not
|
|
62
|
+
// be left in the address bar, in history, or in a shared link.
|
|
63
|
+
const urlOtt = takeEmbedTokenFromUrl();
|
|
64
|
+
// Only redeemed inside a frame. Opened as a normal tab, the same URL falls
|
|
65
|
+
// back to the app's own login, which works there and cannot work in a frame.
|
|
66
|
+
const embedOtt = isFramed() ? urlOtt : null;
|
|
67
67
|
if (embedOtt) {
|
|
68
68
|
markEmbeddedTab();
|
|
69
69
|
}
|
|
70
|
-
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
const
|
|
74
|
-
let currentToken = configToken !== null && configToken !== void 0 ? configToken : null;
|
|
75
|
-
const socketConfig = {
|
|
76
|
-
serverUrl,
|
|
77
|
-
mountPath: "/ws-user-apps/socket.io/",
|
|
78
|
-
transports: ["websocket"],
|
|
79
|
-
appId,
|
|
80
|
-
// null (not undefined): an embedded frame's only identity is the one the
|
|
81
|
-
// platform minted, so the socket must not fall back to a stored token.
|
|
82
|
-
token: embedded ? null : configToken,
|
|
83
|
-
};
|
|
84
|
-
let socket = null;
|
|
85
|
-
const getSocket = () => {
|
|
86
|
-
if (!socket) {
|
|
87
|
-
socket = RoomsSocket({
|
|
88
|
-
config: socketConfig,
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
return socket;
|
|
92
|
-
};
|
|
70
|
+
const embedded = Boolean(embedOtt) || isEmbeddedTab();
|
|
71
|
+
// The passed token is the OTT itself whenever one came in on the URL, and a
|
|
72
|
+
// stale stored one in a frame. Neither of those is this session.
|
|
73
|
+
const token = urlOtt !== null || embedded ? undefined : config.token;
|
|
93
74
|
const headers = {
|
|
94
75
|
...optionalHeaders,
|
|
95
76
|
"X-App-Id": String(appId),
|
|
@@ -103,19 +84,19 @@ export function createClient(config) {
|
|
|
103
84
|
const axiosClient = createAxiosClient({
|
|
104
85
|
baseURL: `${serverUrl}/api`,
|
|
105
86
|
headers,
|
|
106
|
-
token
|
|
87
|
+
token,
|
|
107
88
|
onError: options === null || options === void 0 ? void 0 : options.onError,
|
|
108
89
|
});
|
|
109
90
|
const functionsAxiosClient = createAxiosClient({
|
|
110
91
|
baseURL: `${serverUrl}/api`,
|
|
111
92
|
headers: functionHeaders,
|
|
112
|
-
token
|
|
93
|
+
token,
|
|
113
94
|
interceptResponses: false,
|
|
114
95
|
onError: options === null || options === void 0 ? void 0 : options.onError,
|
|
115
96
|
});
|
|
116
97
|
const serviceRoleHeaders = {
|
|
117
98
|
...headers,
|
|
118
|
-
...(
|
|
99
|
+
...(token ? { "on-behalf-of": `Bearer ${token}` } : {}),
|
|
119
100
|
};
|
|
120
101
|
const serviceRoleAxiosClient = createAxiosClient({
|
|
121
102
|
baseURL: `${serverUrl}/api`,
|
|
@@ -141,53 +122,79 @@ export function createClient(config) {
|
|
|
141
122
|
const userAuthModule = createAuthModule(axiosClient, functionsAxiosClient, appId, {
|
|
142
123
|
appBaseUrl: normalizedAppBaseUrl,
|
|
143
124
|
serverUrl,
|
|
144
|
-
token
|
|
125
|
+
token,
|
|
145
126
|
embedded,
|
|
146
127
|
});
|
|
147
128
|
// Apply the access token before any module that may issue authenticated
|
|
148
129
|
// requests during construction (notably analytics, which fires an init
|
|
149
130
|
// event whose flush calls auth.me()). Without this, the first User/me
|
|
150
131
|
// request is built before setToken runs and goes out unauthenticated.
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const accessToken = configToken || getAccessToken();
|
|
132
|
+
// Skipped in a frame: the session comes from the exchange below.
|
|
133
|
+
if (typeof window !== "undefined" && !embedded) {
|
|
134
|
+
const accessToken = token || getAccessToken();
|
|
155
135
|
if (accessToken) {
|
|
156
|
-
currentToken = accessToken;
|
|
157
136
|
userAuthModule.setToken(accessToken);
|
|
158
137
|
}
|
|
159
138
|
}
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
139
|
+
// Live token for every module; outside a frame it still falls back to storage.
|
|
140
|
+
const getToken = () => { var _a; return (_a = userAuthModule.getToken()) !== null && _a !== void 0 ? _a : (embedded ? null : getAccessToken()); };
|
|
141
|
+
const socketConfig = {
|
|
142
|
+
serverUrl,
|
|
143
|
+
mountPath: "/ws-user-apps/socket.io/",
|
|
144
|
+
transports: ["websocket"],
|
|
145
|
+
appId,
|
|
146
|
+
getToken,
|
|
147
|
+
};
|
|
148
|
+
let socket = null;
|
|
149
|
+
const getSocket = () => {
|
|
150
|
+
if (!socket) {
|
|
151
|
+
socket = RoomsSocket({ config: socketConfig });
|
|
166
152
|
}
|
|
167
|
-
return
|
|
153
|
+
return socket;
|
|
168
154
|
};
|
|
169
155
|
const applyToken = (newToken, saveToStorage) => {
|
|
170
156
|
userAuthModule.setToken(newToken, saveToStorage);
|
|
171
|
-
|
|
172
|
-
socketConfig.token = newToken;
|
|
173
|
-
if (socket) {
|
|
174
|
-
socket.updateConfig({ token: newToken });
|
|
175
|
-
}
|
|
157
|
+
socket === null || socket === void 0 ? void 0 : socket.reconnect();
|
|
176
158
|
};
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
//
|
|
159
|
+
// Settles once the exchanged session is applied (memory only); at once
|
|
160
|
+
// otherwise. Never rejects: every request waits on it, so a failure here
|
|
161
|
+
// must not turn into a rejection on each of them.
|
|
180
162
|
const authReady = embedOtt
|
|
181
|
-
? exchangeEmbedToken({ serverUrl, appId, ott: embedOtt })
|
|
163
|
+
? exchangeEmbedToken({ serverUrl, appId, ott: embedOtt })
|
|
164
|
+
.then((sessionToken) => {
|
|
165
|
+
var _a;
|
|
182
166
|
if (sessionToken) {
|
|
183
167
|
applyToken(sessionToken, false);
|
|
168
|
+
return;
|
|
184
169
|
}
|
|
170
|
+
// The app is about to run anonymous. Say why, once, instead of
|
|
171
|
+
// leaving only the 401s that follow.
|
|
172
|
+
const error = new Error("Base44: the embed token was refused, so this app is not signed in.");
|
|
173
|
+
console.error(error.message);
|
|
174
|
+
(_a = options === null || options === void 0 ? void 0 : options.onError) === null || _a === void 0 ? void 0 : _a.call(options, error);
|
|
175
|
+
})
|
|
176
|
+
.catch((e) => {
|
|
177
|
+
console.error("Base44: applying the embedded session failed:", e);
|
|
185
178
|
})
|
|
186
179
|
: Promise.resolve();
|
|
180
|
+
// Everything that can wait for the session does. `aiGateway.connection()`
|
|
181
|
+
// and the agents connect URLs cannot: they hand back a value, not a promise,
|
|
182
|
+
// and a value read now would stay wrong after the session arrives. They warn
|
|
183
|
+
// instead of failing quietly.
|
|
184
|
+
let warnedEarlyRead = false;
|
|
185
|
+
const getTokenNow = () => {
|
|
186
|
+
const sessionToken = getToken();
|
|
187
|
+
if (sessionToken === null && embedOtt && !warnedEarlyRead) {
|
|
188
|
+
warnedEarlyRead = true;
|
|
189
|
+
console.warn("Base44: read a token before the embedded session was ready, so it is " +
|
|
190
|
+
"empty. Await a call such as base44.auth.me() before building a " +
|
|
191
|
+
"client or a URL that keeps the token.");
|
|
192
|
+
}
|
|
193
|
+
return sessionToken;
|
|
194
|
+
};
|
|
187
195
|
if (embedOtt) {
|
|
188
|
-
// Requests issued
|
|
189
|
-
//
|
|
190
|
-
// so it runs first and the anonymous-visitor header sees the Authorization.
|
|
196
|
+
// Requests issued during the exchange wait for it. Registered after createAxiosClient's
|
|
197
|
+
// interceptors so it runs first and the anonymous-visitor header sees the Authorization.
|
|
191
198
|
for (const client of [axiosClient, functionsAxiosClient]) {
|
|
192
199
|
client.interceptors.request.use(async (requestConfig) => {
|
|
193
200
|
await authReady;
|
|
@@ -205,7 +212,10 @@ export function createClient(config) {
|
|
|
205
212
|
// URL needs an absolute host, so fall back to the page origin.
|
|
206
213
|
host: resolveActorsHost(serverUrl, typeof window !== "undefined" ? (_a = window.location) === null || _a === void 0 ? void 0 : _a.origin : undefined),
|
|
207
214
|
functionsVersion,
|
|
208
|
-
getAuthToken:
|
|
215
|
+
getAuthToken: async () => {
|
|
216
|
+
await authReady;
|
|
217
|
+
return getToken();
|
|
218
|
+
},
|
|
209
219
|
mintConnectionToken: async (actorName, room, connectionId) => {
|
|
210
220
|
await authReady;
|
|
211
221
|
const authToken = getToken();
|
|
@@ -233,6 +243,7 @@ export function createClient(config) {
|
|
|
233
243
|
connectors: createUserConnectorsModule(axiosClient, appId),
|
|
234
244
|
auth: userAuthModule,
|
|
235
245
|
functions: createFunctionsModule(functionsAxiosClient, appId, {
|
|
246
|
+
waitForAuth: () => authReady,
|
|
236
247
|
getAuthHeaders: () => {
|
|
237
248
|
const headers = {};
|
|
238
249
|
const sessionToken = getToken();
|
|
@@ -248,9 +259,13 @@ export function createClient(config) {
|
|
|
248
259
|
getSocket,
|
|
249
260
|
appId,
|
|
250
261
|
serverUrl,
|
|
251
|
-
getToken,
|
|
262
|
+
getToken: getTokenNow,
|
|
263
|
+
}),
|
|
264
|
+
aiGateway: createAiGatewayModule({
|
|
265
|
+
serverUrl,
|
|
266
|
+
getToken: getTokenNow,
|
|
267
|
+
appId,
|
|
252
268
|
}),
|
|
253
|
-
aiGateway: createAiGatewayModule({ serverUrl, getToken, appId }),
|
|
254
269
|
appLogs: createAppLogsModule(axiosClient, appId),
|
|
255
270
|
app: createAppModule(axiosClient, appId),
|
|
256
271
|
users: createUsersModule(axiosClient, appId),
|
|
@@ -336,6 +351,7 @@ export function createClient(config) {
|
|
|
336
351
|
serverUrl,
|
|
337
352
|
functionsVersion,
|
|
338
353
|
platformHeaders: optionalHeaders,
|
|
354
|
+
waitForAuth: () => authReady,
|
|
339
355
|
}),
|
|
340
356
|
/**
|
|
341
357
|
* Sets a new authentication token for all subsequent requests.
|
package/dist/modules/actors.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ interface ActorsConfig {
|
|
|
12
12
|
/** Current user access token, if authenticated. Rides the WS query on the
|
|
13
13
|
* proxy-fallback path so the platform proxy can authenticate the connection;
|
|
14
14
|
* anonymous connects omit it. */
|
|
15
|
-
getAuthToken(): string | null | undefined
|
|
15
|
+
getAuthToken(): string | null | undefined | Promise<string | null | undefined>;
|
|
16
16
|
/** Same semantics as function calls: editors with a non-prod version get the
|
|
17
17
|
* draft actor script; everyone else gets the published one. */
|
|
18
18
|
functionsVersion?: string;
|
package/dist/modules/actors.js
CHANGED
|
@@ -83,7 +83,7 @@ class Connection {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
// Rebuilt per attempt so a login/logout is picked up on reconnect.
|
|
86
|
-
return buildProxyActorUrl(config.host, actorName, instanceId, this.id, config.appId, config.getAuthToken(), config.functionsVersion);
|
|
86
|
+
return buildProxyActorUrl(config.host, actorName, instanceId, this.id, config.appId, await config.getAuthToken(), config.functionsVersion);
|
|
87
87
|
};
|
|
88
88
|
const ws = new ReconnectingWebSocket(urlProvider);
|
|
89
89
|
this.ws = ws;
|
package/dist/modules/auth.js
CHANGED
|
@@ -82,10 +82,13 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
|
|
|
82
82
|
// Tracked here rather than read off `axios.defaults` so the answer stays tied
|
|
83
83
|
// to the identity transitions below (`setToken`, `logout`) instead of to the
|
|
84
84
|
// header a caller may have set on the instance directly.
|
|
85
|
-
let
|
|
85
|
+
let accessToken = options.token || null;
|
|
86
86
|
return {
|
|
87
87
|
hasToken() {
|
|
88
|
-
return
|
|
88
|
+
return accessToken !== null;
|
|
89
|
+
},
|
|
90
|
+
getToken() {
|
|
91
|
+
return accessToken;
|
|
89
92
|
},
|
|
90
93
|
isEmbedded() {
|
|
91
94
|
return Boolean(options.embedded);
|
|
@@ -161,7 +164,7 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
|
|
|
161
164
|
// flight would otherwise resolve into callers that run after the logout.
|
|
162
165
|
clearPendingMe();
|
|
163
166
|
resetAnalyticsSessionContext();
|
|
164
|
-
|
|
167
|
+
accessToken = null;
|
|
165
168
|
// Only do the rest if in a browser environment
|
|
166
169
|
if (typeof window !== "undefined") {
|
|
167
170
|
// Remove token from localStorage
|
|
@@ -186,18 +189,20 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
|
|
|
186
189
|
setToken(token, saveToStorage = true) {
|
|
187
190
|
if (!token)
|
|
188
191
|
return;
|
|
192
|
+
// An embedded session belongs to the frame the platform minted it for.
|
|
193
|
+
// Persisting it would let it outlive that frame and be picked up as the
|
|
194
|
+
// identity on a later top-level visit, so storage is refused outright.
|
|
195
|
+
const persist = saveToStorage && !options.embedded;
|
|
189
196
|
// Same reasoning as in `logout`: the identity changes here, so anything
|
|
190
197
|
// resolved for the previous one must not be handed to later callers.
|
|
191
198
|
clearPendingMe();
|
|
192
199
|
resetAnalyticsSessionContext();
|
|
193
|
-
|
|
200
|
+
accessToken = token;
|
|
194
201
|
// handle token change for axios clients
|
|
195
202
|
axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
|
|
196
203
|
functionsAxiosClient.defaults.headers.common["Authorization"] = `Bearer ${token}`;
|
|
197
204
|
// Save token to localStorage if requested
|
|
198
|
-
if (
|
|
199
|
-
typeof window !== "undefined" &&
|
|
200
|
-
window.localStorage) {
|
|
205
|
+
if (persist && typeof window !== "undefined" && window.localStorage) {
|
|
201
206
|
try {
|
|
202
207
|
window.localStorage.setItem("base44_access_token", token);
|
|
203
208
|
// Set "token" that is set by the built-in SDK of platform version 2
|
|
@@ -560,4 +560,9 @@ export interface InternalAuthModule extends AuthModule {
|
|
|
560
560
|
* could not succeed without a session, not to decide that one is valid.
|
|
561
561
|
*/
|
|
562
562
|
hasToken(): boolean;
|
|
563
|
+
/**
|
|
564
|
+
* The access token currently set on the client, or `null` when there is none.
|
|
565
|
+
* Follows {@linkcode AuthModule.setToken | setToken} and {@linkcode AuthModule.logout | logout}.
|
|
566
|
+
*/
|
|
567
|
+
getToken(): string | null;
|
|
563
568
|
}
|
|
@@ -65,8 +65,12 @@ export function createFunctionsModule(axios, appId, config) {
|
|
|
65
65
|
},
|
|
66
66
|
// Fetch a backend function endpoint directly.
|
|
67
67
|
async fetch(path, init = {}) {
|
|
68
|
+
var _a;
|
|
68
69
|
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
69
70
|
const primaryPath = `/functions${normalizedPath}`;
|
|
71
|
+
// Headers are read after this: a session still being negotiated must be
|
|
72
|
+
// in hand before the Authorization is built, not after.
|
|
73
|
+
await ((_a = config === null || config === void 0 ? void 0 : config.waitForAuth) === null || _a === void 0 ? void 0 : _a.call(config));
|
|
70
74
|
const headers = toHeaders(init.headers);
|
|
71
75
|
const requestInit = {
|
|
72
76
|
...init,
|
|
@@ -29,6 +29,12 @@ export type FunctionsFetchInit = RequestInit;
|
|
|
29
29
|
export interface FunctionsModuleConfig {
|
|
30
30
|
getAuthHeaders?: () => Record<string, string>;
|
|
31
31
|
baseURL?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Resolves once the client's session is settled. `fetch` builds its headers
|
|
34
|
+
* by hand rather than through axios, so without this it would miss the gate
|
|
35
|
+
* every other request goes through.
|
|
36
|
+
*/
|
|
37
|
+
waitForAuth?: () => Promise<void>;
|
|
32
38
|
}
|
|
33
39
|
/**
|
|
34
40
|
* Functions module for invoking custom backend functions.
|
|
@@ -18,6 +18,14 @@ export declare const EMBED_TOKEN_PARAM = "ott";
|
|
|
18
18
|
* @internal
|
|
19
19
|
*/
|
|
20
20
|
export declare function takeEmbedTokenFromUrl(): string | null;
|
|
21
|
+
/**
|
|
22
|
+
* Whether this document is rendered inside a frame. Read at call time, not at
|
|
23
|
+
* import: a module-level answer would be fixed before a host (or a test) has
|
|
24
|
+
* set the window up.
|
|
25
|
+
*
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
28
|
+
export declare function isFramed(): boolean;
|
|
21
29
|
/**
|
|
22
30
|
* Whether this tab previously redeemed an embed token. Counts only inside a
|
|
23
31
|
* frame: a top-level tab that once carried a token must fall back to the
|
|
@@ -42,8 +50,9 @@ export declare function exchangeEmbedToken({ serverUrl, appId, ott, fetchImpl, }
|
|
|
42
50
|
}): Promise<string | null>;
|
|
43
51
|
/**
|
|
44
52
|
* Covers the page with a plain "session ended" notice. An embedded session can
|
|
45
|
-
* only be renewed by the host platform
|
|
46
|
-
*
|
|
53
|
+
* only be renewed by the host platform, so this stands in for the login
|
|
54
|
+
* redirect, which cannot complete inside a frame. The copy points at the host
|
|
55
|
+
* page: reloading the frame itself carries no token and lands here again.
|
|
47
56
|
*
|
|
48
57
|
* @internal
|
|
49
58
|
*/
|
|
@@ -27,19 +27,43 @@ export function takeEmbedTokenFromUrl() {
|
|
|
27
27
|
if (typeof window === "undefined" || !window.location) {
|
|
28
28
|
return null;
|
|
29
29
|
}
|
|
30
|
+
let ott = null;
|
|
30
31
|
try {
|
|
31
32
|
const url = new URL(window.location.href);
|
|
32
|
-
|
|
33
|
+
ott = url.searchParams.get(EMBED_TOKEN_PARAM);
|
|
33
34
|
if (!ott) {
|
|
34
35
|
return null;
|
|
35
36
|
}
|
|
36
37
|
url.searchParams.delete(EMBED_TOKEN_PARAM);
|
|
37
38
|
window.history.replaceState(window.history.state, "", url.toString());
|
|
38
|
-
return ott;
|
|
39
39
|
}
|
|
40
40
|
catch (e) {
|
|
41
|
+
// The token was read but the URL could not be rewritten (history blocked,
|
|
42
|
+
// a sandboxed frame, replaceState rate-limited). Return it anyway: it is
|
|
43
|
+
// this load's identity, and dropping it here would leave `?ott=` on the
|
|
44
|
+
// URL for getAccessToken to hand back as if it were a session token.
|
|
41
45
|
console.error("Error retrieving embed token from URL:", e);
|
|
42
|
-
|
|
46
|
+
}
|
|
47
|
+
return ott;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Whether this document is rendered inside a frame. Read at call time, not at
|
|
51
|
+
* import: a module-level answer would be fixed before a host (or a test) has
|
|
52
|
+
* set the window up.
|
|
53
|
+
*
|
|
54
|
+
* @internal
|
|
55
|
+
*/
|
|
56
|
+
export function isFramed() {
|
|
57
|
+
if (typeof window === "undefined") {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
return window.self !== window.top;
|
|
62
|
+
}
|
|
63
|
+
catch (_a) {
|
|
64
|
+
// Reaching the top window can be refused outright; if we cannot see it,
|
|
65
|
+
// we are certainly not it.
|
|
66
|
+
return true;
|
|
43
67
|
}
|
|
44
68
|
}
|
|
45
69
|
/**
|
|
@@ -56,8 +80,7 @@ export function isEmbeddedTab() {
|
|
|
56
80
|
// A cross-site frame is third-party storage, which some browsers block
|
|
57
81
|
// outright — every access can throw, and the marker is then unavailable.
|
|
58
82
|
try {
|
|
59
|
-
return (
|
|
60
|
-
window.sessionStorage.getItem(EMBED_TAB_KEY) === "1");
|
|
83
|
+
return isFramed() && window.sessionStorage.getItem(EMBED_TAB_KEY) === "1";
|
|
61
84
|
}
|
|
62
85
|
catch (_a) {
|
|
63
86
|
return false;
|
|
@@ -108,8 +131,9 @@ export async function exchangeEmbedToken({ serverUrl, appId, ott, fetchImpl = fe
|
|
|
108
131
|
}
|
|
109
132
|
/**
|
|
110
133
|
* Covers the page with a plain "session ended" notice. An embedded session can
|
|
111
|
-
* only be renewed by the host platform
|
|
112
|
-
*
|
|
134
|
+
* only be renewed by the host platform, so this stands in for the login
|
|
135
|
+
* redirect, which cannot complete inside a frame. The copy points at the host
|
|
136
|
+
* page: reloading the frame itself carries no token and lands here again.
|
|
113
137
|
*
|
|
114
138
|
* @internal
|
|
115
139
|
*/
|
|
@@ -131,7 +155,7 @@ export function showEmbedSessionEnded() {
|
|
|
131
155
|
title.textContent = "Session ended";
|
|
132
156
|
title.style.cssText = "font-size:1.5rem;font-weight:700;margin:0 0 .75rem;";
|
|
133
157
|
const body = document.createElement("p");
|
|
134
|
-
body.textContent = "
|
|
158
|
+
body.textContent = "Reload this page in your browser to start a new session.";
|
|
135
159
|
body.style.cssText = "margin:0;color:#475569;";
|
|
136
160
|
const card = document.createElement("div");
|
|
137
161
|
card.append(title, body);
|
|
@@ -33,11 +33,13 @@ export interface FetchWithAuthInit extends RequestInit {
|
|
|
33
33
|
* has none, so nothing is sent.
|
|
34
34
|
* @internal
|
|
35
35
|
*/
|
|
36
|
-
export declare function createFetchWithAuth({ axios, serviceRoleAxios, appId, serverUrl, functionsVersion, platformHeaders, }: {
|
|
36
|
+
export declare function createFetchWithAuth({ axios, serviceRoleAxios, appId, serverUrl, functionsVersion, platformHeaders, waitForAuth, }: {
|
|
37
37
|
axios: AxiosInstance;
|
|
38
38
|
serviceRoleAxios: AxiosInstance;
|
|
39
39
|
appId: string;
|
|
40
40
|
serverUrl: string;
|
|
41
41
|
functionsVersion?: string;
|
|
42
42
|
platformHeaders?: Record<string, string>;
|
|
43
|
+
/** Resolves once the client's session is settled. */
|
|
44
|
+
waitForAuth?: () => Promise<void>;
|
|
43
45
|
}): (path: string, init?: FetchWithAuthInit) => Promise<Response>;
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* has none, so nothing is sent.
|
|
22
22
|
* @internal
|
|
23
23
|
*/
|
|
24
|
-
export function createFetchWithAuth({ axios, serviceRoleAxios, appId, serverUrl, functionsVersion, platformHeaders, }) {
|
|
24
|
+
export function createFetchWithAuth({ axios, serviceRoleAxios, appId, serverUrl, functionsVersion, platformHeaders, waitForAuth, }) {
|
|
25
25
|
const inherited = new Headers(platformHeaders);
|
|
26
26
|
const bearer = (client) => {
|
|
27
27
|
const header = client.defaults.headers.common["Authorization"];
|
|
@@ -31,6 +31,9 @@ export function createFetchWithAuth({ axios, serviceRoleAxios, appId, serverUrl,
|
|
|
31
31
|
};
|
|
32
32
|
return async function fetchWithAuth(path, init = {}) {
|
|
33
33
|
assertOwnOriginPath(path);
|
|
34
|
+
// The Authorization below is read off the axios defaults, which a session
|
|
35
|
+
// still being negotiated has not written yet.
|
|
36
|
+
await (waitForAuth === null || waitForAuth === void 0 ? void 0 : waitForAuth());
|
|
34
37
|
const { fetch: transport = fetch, ...requestInit } = init;
|
|
35
38
|
const headers = new Headers(init.headers);
|
|
36
39
|
// A caller-supplied value always wins, so a route can hand the callee a
|
|
@@ -4,9 +4,8 @@ export interface RoomsSocketConfig {
|
|
|
4
4
|
mountPath: string;
|
|
5
5
|
transports: string[];
|
|
6
6
|
appId: string;
|
|
7
|
-
/**
|
|
8
|
-
|
|
9
|
-
token?: string | null;
|
|
7
|
+
/** Asked on every connect, so the socket always carries the current session. */
|
|
8
|
+
getToken: () => string | null;
|
|
10
9
|
}
|
|
11
10
|
export type TSocketRoom = string;
|
|
12
11
|
export type TJsonStr = string;
|
|
@@ -42,7 +41,7 @@ export declare function RoomsSocket({ config }: {
|
|
|
42
41
|
leave: (room: string) => void;
|
|
43
42
|
}>;
|
|
44
43
|
subscribeToRoom: (room: TSocketRoom, handlers: Partial<{ [k in TEvent]: THandler<k>; }>) => () => void;
|
|
45
|
-
|
|
44
|
+
reconnect: () => void;
|
|
46
45
|
updateModel: (room: string, data: any) => Promise<void>;
|
|
47
46
|
disconnect: () => void;
|
|
48
47
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { io } from "socket.io-client";
|
|
2
|
-
import { getAccessToken } from "./auth-utils.js";
|
|
3
2
|
import { getAnalyticsSessionId } from "../modules/analytics.js";
|
|
4
3
|
const ROOM_LEAVE_GRACE_MS = 250;
|
|
5
4
|
function initializeSocket(config, handlers) {
|
|
@@ -7,7 +6,7 @@ function initializeSocket(config, handlers) {
|
|
|
7
6
|
// handshake so the backend can verify room access for anonymous agent
|
|
8
7
|
// conversations (mirrors the X-Base44-Anonymous-Id HTTP header). Authenticated
|
|
9
8
|
// clients are identified by their token instead.
|
|
10
|
-
const resolvedToken = config.
|
|
9
|
+
const resolvedToken = config.getToken();
|
|
11
10
|
const query = {
|
|
12
11
|
app_id: config.appId,
|
|
13
12
|
token: resolvedToken,
|
|
@@ -41,7 +40,6 @@ function initializeSocket(config, handlers) {
|
|
|
41
40
|
return socket;
|
|
42
41
|
}
|
|
43
42
|
export function RoomsSocket({ config }) {
|
|
44
|
-
let currentConfig = { ...config };
|
|
45
43
|
const roomsToListeners = {};
|
|
46
44
|
const pendingRoomLeaves = {};
|
|
47
45
|
const handlers = {
|
|
@@ -83,13 +81,10 @@ export function RoomsSocket({ config }) {
|
|
|
83
81
|
socket.disconnect();
|
|
84
82
|
}
|
|
85
83
|
}
|
|
86
|
-
|
|
84
|
+
/** Drops the connection and opens a new one with the current token. */
|
|
85
|
+
function reconnect() {
|
|
87
86
|
cleanup();
|
|
88
|
-
|
|
89
|
-
...currentConfig,
|
|
90
|
-
...config,
|
|
91
|
-
};
|
|
92
|
-
socket = initializeSocket(currentConfig, handlers);
|
|
87
|
+
socket = initializeSocket(config, handlers);
|
|
93
88
|
}
|
|
94
89
|
function joinRoom(room) {
|
|
95
90
|
socket.emit("join", room);
|
|
@@ -162,7 +157,7 @@ export function RoomsSocket({ config }) {
|
|
|
162
157
|
return {
|
|
163
158
|
socket,
|
|
164
159
|
subscribeToRoom,
|
|
165
|
-
|
|
160
|
+
reconnect,
|
|
166
161
|
updateModel,
|
|
167
162
|
disconnect,
|
|
168
163
|
};
|