@base44-preview/sdk 0.8.48-pr.282.6cbc158 → 0.8.48-pr.282.6ea0687
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 +43 -72
- package/dist/modules/actors.d.ts +3 -2
- package/dist/modules/agents.types.d.ts +7 -3
- package/dist/modules/auth.types.d.ts +8 -16
- package/dist/utils/embed-session.d.ts +6 -2
- package/dist/utils/embed-session.js +8 -24
- package/dist/utils/fetch-with-auth.d.ts +0 -1
- package/dist/utils/socket-utils.d.ts +0 -1
- 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,
|
|
8
|
+
import { exchangeEmbedToken, 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";
|
|
@@ -58,16 +58,31 @@ export function createClient(config) {
|
|
|
58
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
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
61
|
+
const embedOtt = takeEmbedTokenFromUrl();
|
|
62
|
+
// A declaration, not a const: this block sits above the auth module.
|
|
63
|
+
function getToken() {
|
|
64
|
+
var _a;
|
|
65
|
+
return (_a = userAuthModule.getToken()) !== null && _a !== void 0 ? _a : (embedOtt ? null : getAccessToken());
|
|
66
|
+
}
|
|
67
|
+
const socketConfig = {
|
|
68
|
+
serverUrl,
|
|
69
|
+
mountPath: "/ws-user-apps/socket.io/",
|
|
70
|
+
transports: ["websocket"],
|
|
71
|
+
appId,
|
|
72
|
+
getToken,
|
|
73
|
+
};
|
|
74
|
+
let socket = null;
|
|
75
|
+
const getSocket = () => {
|
|
76
|
+
if (!socket) {
|
|
77
|
+
socket = RoomsSocket({
|
|
78
|
+
config: socketConfig,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return socket;
|
|
82
|
+
};
|
|
83
|
+
// Apps pass getAccessToken() in as `token`, which in a frame is the OTT —
|
|
84
|
+
// what the exchange trades for a session, never a bearer itself.
|
|
85
|
+
const token = embedOtt ? undefined : config.token;
|
|
71
86
|
const headers = {
|
|
72
87
|
...optionalHeaders,
|
|
73
88
|
"X-App-Id": String(appId),
|
|
@@ -116,54 +131,31 @@ export function createClient(config) {
|
|
|
116
131
|
baseURL: `${serverUrl}/api`,
|
|
117
132
|
headers,
|
|
118
133
|
});
|
|
119
|
-
// Declared before the auth module so `onSessionChange` can reach it; the
|
|
120
|
-
// socket itself is still only built on first use, below.
|
|
121
|
-
let socket = null;
|
|
122
134
|
const userAuthModule = createAuthModule(axiosClient, functionsAxiosClient, appId, {
|
|
123
135
|
appBaseUrl: normalizedAppBaseUrl,
|
|
124
136
|
serverUrl,
|
|
125
137
|
token,
|
|
126
|
-
embedded,
|
|
127
|
-
// The socket carries its token on the handshake, so
|
|
128
|
-
//
|
|
129
|
-
// dropping the one still running as the user who just left.
|
|
138
|
+
embedded: Boolean(embedOtt),
|
|
139
|
+
// The socket carries its token on the handshake, so it can only pick a
|
|
140
|
+
// new one up by redialling — or, on logout, by dropping what it has.
|
|
130
141
|
onSessionChange: (hasSession) => hasSession ? socket === null || socket === void 0 ? void 0 : socket.reconnect() : socket === null || socket === void 0 ? void 0 : socket.disconnect(),
|
|
131
142
|
});
|
|
132
143
|
// Apply the access token before any module that may issue authenticated
|
|
133
144
|
// requests during construction (notably analytics, which fires an init
|
|
134
145
|
// event whose flush calls auth.me()). Without this, the first User/me
|
|
135
146
|
// request is built before setToken runs and goes out unauthenticated.
|
|
136
|
-
//
|
|
137
|
-
if (typeof window !== "undefined" && !
|
|
147
|
+
// Not in a frame: a stored token there belongs to an earlier visitor.
|
|
148
|
+
if (typeof window !== "undefined" && !embedOtt) {
|
|
138
149
|
const accessToken = token || getAccessToken();
|
|
139
150
|
if (accessToken) {
|
|
140
151
|
userAuthModule.setToken(accessToken);
|
|
141
152
|
}
|
|
142
153
|
}
|
|
143
|
-
// Live token for every module; outside a frame it still falls back to storage.
|
|
144
|
-
const getToken = () => { var _a; return (_a = userAuthModule.getToken()) !== null && _a !== void 0 ? _a : (embedded ? null : getAccessToken()); };
|
|
145
|
-
const socketConfig = {
|
|
146
|
-
serverUrl,
|
|
147
|
-
mountPath: "/ws-user-apps/socket.io/",
|
|
148
|
-
transports: ["websocket"],
|
|
149
|
-
appId,
|
|
150
|
-
getToken,
|
|
151
|
-
};
|
|
152
|
-
const getSocket = () => {
|
|
153
|
-
if (!socket) {
|
|
154
|
-
socket = RoomsSocket({ config: socketConfig });
|
|
155
|
-
}
|
|
156
|
-
return socket;
|
|
157
|
-
};
|
|
158
|
-
// The exchange belongs to the frame, not to one client: the token is off the
|
|
159
|
-
// URL after the first `createClient`, so a second one in the same document
|
|
160
|
-
// joins the session already being negotiated rather than going anonymous.
|
|
161
154
|
const session = embedOtt
|
|
162
|
-
?
|
|
163
|
-
:
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
// must not turn into a rejection on each of them.
|
|
155
|
+
? exchangeEmbedToken({ serverUrl, appId, ott: embedOtt })
|
|
156
|
+
: null;
|
|
157
|
+
// Never rejects: every request waits on it, so one failure here must not
|
|
158
|
+
// become a rejection on each of them.
|
|
167
159
|
const authReady = session
|
|
168
160
|
? session
|
|
169
161
|
.then((sessionToken) => {
|
|
@@ -172,8 +164,6 @@ export function createClient(config) {
|
|
|
172
164
|
userAuthModule.setToken(sessionToken, false);
|
|
173
165
|
return;
|
|
174
166
|
}
|
|
175
|
-
// The app is about to run anonymous. Say why, once, instead of
|
|
176
|
-
// leaving only the 401s that follow.
|
|
177
167
|
const error = new Error("Base44: the embed token was refused, so this app is not signed in.");
|
|
178
168
|
console.error(error.message);
|
|
179
169
|
(_a = options === null || options === void 0 ? void 0 : options.onError) === null || _a === void 0 ? void 0 : _a.call(options, error);
|
|
@@ -182,24 +172,9 @@ export function createClient(config) {
|
|
|
182
172
|
console.error("Base44: applying the embedded session failed:", e);
|
|
183
173
|
})
|
|
184
174
|
: Promise.resolve();
|
|
185
|
-
// Everything that can wait for the session does. `aiGateway.connection()`
|
|
186
|
-
// and the agents connect URLs cannot: they hand back a value, not a promise,
|
|
187
|
-
// and a value read now would stay wrong after the session arrives. They warn
|
|
188
|
-
// instead of failing quietly.
|
|
189
|
-
let warnedEarlyRead = false;
|
|
190
|
-
const getTokenNow = () => {
|
|
191
|
-
const sessionToken = getToken();
|
|
192
|
-
if (sessionToken === null && session && !warnedEarlyRead) {
|
|
193
|
-
warnedEarlyRead = true;
|
|
194
|
-
console.warn("Base44: read a token before the embedded session was ready, so it is " +
|
|
195
|
-
"empty. Await a call such as base44.auth.me() before building a " +
|
|
196
|
-
"client or a URL that keeps the token.");
|
|
197
|
-
}
|
|
198
|
-
return sessionToken;
|
|
199
|
-
};
|
|
200
175
|
if (session) {
|
|
201
|
-
//
|
|
202
|
-
//
|
|
176
|
+
// Registered after createAxiosClient's so it runs first (axios unshifts),
|
|
177
|
+
// letting the anonymous-visitor header see the Authorization we just set.
|
|
203
178
|
for (const client of [axiosClient, functionsAxiosClient]) {
|
|
204
179
|
client.interceptors.request.use(async (requestConfig) => {
|
|
205
180
|
await authReady;
|
|
@@ -264,13 +239,10 @@ export function createClient(config) {
|
|
|
264
239
|
getSocket,
|
|
265
240
|
appId,
|
|
266
241
|
serverUrl,
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
aiGateway: createAiGatewayModule({
|
|
270
|
-
serverUrl,
|
|
271
|
-
getToken: getTokenNow,
|
|
272
|
-
appId,
|
|
242
|
+
// Sync, unlike everything else: these return a URL, not a promise.
|
|
243
|
+
getToken,
|
|
273
244
|
}),
|
|
245
|
+
aiGateway: createAiGatewayModule({ serverUrl, getToken, appId }),
|
|
274
246
|
appLogs: createAppLogsModule(axiosClient, appId),
|
|
275
247
|
app: createAppModule(axiosClient, appId),
|
|
276
248
|
users: createUsersModule(axiosClient, appId),
|
|
@@ -315,10 +287,9 @@ export function createClient(config) {
|
|
|
315
287
|
getSocket,
|
|
316
288
|
appId,
|
|
317
289
|
serverUrl,
|
|
318
|
-
// The user's token,
|
|
319
|
-
//
|
|
320
|
-
|
|
321
|
-
getToken: getTokenNow,
|
|
290
|
+
// The user's token, deliberately: this is read only for the `?token=` on
|
|
291
|
+
// a channel URL handed to that user. Never the service credential.
|
|
292
|
+
getToken,
|
|
322
293
|
}),
|
|
323
294
|
aiGateway: createAiGatewayModule({
|
|
324
295
|
serverUrl,
|
package/dist/modules/actors.d.ts
CHANGED
|
@@ -11,8 +11,9 @@ interface ActorsConfig {
|
|
|
11
11
|
appId: string;
|
|
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
|
-
* anonymous connects omit it.
|
|
15
|
-
|
|
14
|
+
* anonymous connects omit it. Awaited per dial, so a session still being
|
|
15
|
+
* exchanged is in hand before the URL is built. */
|
|
16
|
+
getAuthToken(): Promise<string | null | undefined>;
|
|
16
17
|
/** Same semantics as function calls: editors with a non-prod version get the
|
|
17
18
|
* draft actor script; everyone else gets the published one. */
|
|
18
19
|
functionsVersion?: string;
|
|
@@ -161,7 +161,7 @@ export interface AgentsModuleConfig {
|
|
|
161
161
|
/** Server URL */
|
|
162
162
|
serverUrl?: string;
|
|
163
163
|
/** Returns the current authentication token, if any */
|
|
164
|
-
getToken: () => string | null
|
|
164
|
+
getToken: () => string | null;
|
|
165
165
|
}
|
|
166
166
|
/**
|
|
167
167
|
* Agents module for managing AI agent conversations.
|
|
@@ -360,7 +360,9 @@ export interface AgentsModule {
|
|
|
360
360
|
* Gets WhatsApp connection URL for an agent.
|
|
361
361
|
*
|
|
362
362
|
* Generates a URL that users can use to connect with the agent through WhatsApp.
|
|
363
|
-
* The URL includes authentication if a token is available.
|
|
363
|
+
* The URL includes authentication if a token is available. In an app a
|
|
364
|
+
* platform has embedded, that is only once the session has been exchanged —
|
|
365
|
+
* await a call such as `base44.auth.me()` before building the URL.
|
|
364
366
|
*
|
|
365
367
|
* @param agentName - The name of the agent.
|
|
366
368
|
* @returns WhatsApp connection URL.
|
|
@@ -378,7 +380,9 @@ export interface AgentsModule {
|
|
|
378
380
|
* Gets Telegram connection URL for an agent.
|
|
379
381
|
*
|
|
380
382
|
* Generates a URL that users can use to connect with the agent through Telegram.
|
|
381
|
-
* The URL includes authentication if a token is available.
|
|
383
|
+
* The URL includes authentication if a token is available. In an app a
|
|
384
|
+
* platform has embedded, that is only once the session has been exchanged —
|
|
385
|
+
* await a call such as `base44.auth.me()` before building the URL. When the user opens
|
|
382
386
|
* this URL, they are redirected to the agent's Telegram bot with an activation
|
|
383
387
|
* code that securely links their account.
|
|
384
388
|
*
|
|
@@ -98,16 +98,11 @@ export interface AuthModuleOptions {
|
|
|
98
98
|
* which is how the server-side SDK reports a token it never sets explicitly.
|
|
99
99
|
*/
|
|
100
100
|
token?: string;
|
|
101
|
-
/**
|
|
102
|
-
* Whether this client runs embedded in a host platform's frame, where a
|
|
103
|
-
* session is minted by the platform and cannot be renewed by a login redirect.
|
|
104
|
-
*/
|
|
101
|
+
/** Whether a host platform embedded this client in a frame. */
|
|
105
102
|
embedded?: boolean;
|
|
106
103
|
/**
|
|
107
|
-
* Called
|
|
108
|
-
*
|
|
109
|
-
* the realtime socket, which carries a token on the handshake and so has to
|
|
110
|
-
* open a new connection to pick one up, or drop the one it has.
|
|
104
|
+
* Called when the identity changes: `true` on `setToken`, `false` on
|
|
105
|
+
* `logout`. Lets the client redial the socket, which holds its own copy.
|
|
111
106
|
*/
|
|
112
107
|
onSessionChange?: (hasSession: boolean) => void;
|
|
113
108
|
}
|
|
@@ -192,13 +187,13 @@ export interface AuthModule {
|
|
|
192
187
|
*/
|
|
193
188
|
redirectToLogin(nextUrl: string): void;
|
|
194
189
|
/**
|
|
195
|
-
* Whether
|
|
190
|
+
* Whether a host platform embedded this app and signed its user in.
|
|
196
191
|
*
|
|
197
|
-
*
|
|
192
|
+
* Only the platform can renew that session, so {@linkcode AuthModule.redirectToLogin | redirectToLogin()} and {@linkcode AuthModule.logout | logout()} show a "session ended" notice rather than a login page. Use this to render your own notice, or to hide sign-in controls that cannot work in the frame.
|
|
198
193
|
*
|
|
199
|
-
*
|
|
194
|
+
* The session lives in memory, so a reload inside the frame ends it and returns `false` here. Prefer client-side navigation.
|
|
200
195
|
*
|
|
201
|
-
* @returns `true` when
|
|
196
|
+
* @returns `true` when a host platform embedded this app.
|
|
202
197
|
*
|
|
203
198
|
* @example
|
|
204
199
|
* ```typescript
|
|
@@ -569,9 +564,6 @@ export interface InternalAuthModule extends AuthModule {
|
|
|
569
564
|
* could not succeed without a session, not to decide that one is valid.
|
|
570
565
|
*/
|
|
571
566
|
hasToken(): boolean;
|
|
572
|
-
/**
|
|
573
|
-
* The access token currently set on the client, or `null` when there is none.
|
|
574
|
-
* Follows {@linkcode AuthModule.setToken | setToken} and {@linkcode AuthModule.logout | logout}.
|
|
575
|
-
*/
|
|
567
|
+
/** The token currently set on the client, or `null`. */
|
|
576
568
|
getToken(): string | null;
|
|
577
569
|
}
|
|
@@ -7,6 +7,12 @@
|
|
|
7
7
|
* grant (RFC 8693), and keeps the result in memory only — never in storage — so
|
|
8
8
|
* the session lives and dies with the frame.
|
|
9
9
|
*
|
|
10
|
+
* Taking the token off the URL is unconditional — a one-time token must not be
|
|
11
|
+
* left in the address bar, in history, or in a shared link — but it is only
|
|
12
|
+
* reported back inside a frame, the only place one is redeemed. It is gone once
|
|
13
|
+
* the first client has taken it, so the session belongs to that client — an app
|
|
14
|
+
* creates one.
|
|
15
|
+
*
|
|
10
16
|
* The exchange endpoint is rate limited per app, and its limiter refuses before
|
|
11
17
|
* the one-time token is redeemed — so a 429 leaves the token still valid and is
|
|
12
18
|
* worth retrying once.
|
|
@@ -15,8 +21,6 @@
|
|
|
15
21
|
*/
|
|
16
22
|
export declare const EMBED_TOKEN_PARAM = "ott";
|
|
17
23
|
/** @internal */
|
|
18
|
-
export declare function frameSession(appId: string, start?: () => Promise<string | null>): Promise<string | null> | null;
|
|
19
|
-
/** @internal */
|
|
20
24
|
export declare function takeEmbedTokenFromUrl(): string | null;
|
|
21
25
|
/** @internal */
|
|
22
26
|
export declare function isFramed(): boolean;
|
|
@@ -7,6 +7,12 @@
|
|
|
7
7
|
* grant (RFC 8693), and keeps the result in memory only — never in storage — so
|
|
8
8
|
* the session lives and dies with the frame.
|
|
9
9
|
*
|
|
10
|
+
* Taking the token off the URL is unconditional — a one-time token must not be
|
|
11
|
+
* left in the address bar, in history, or in a shared link — but it is only
|
|
12
|
+
* reported back inside a frame, the only place one is redeemed. It is gone once
|
|
13
|
+
* the first client has taken it, so the session belongs to that client — an app
|
|
14
|
+
* creates one.
|
|
15
|
+
*
|
|
10
16
|
* The exchange endpoint is rate limited per app, and its limiter refuses before
|
|
11
17
|
* the one-time token is redeemed — so a 429 leaves the token still valid and is
|
|
12
18
|
* worth retrying once.
|
|
@@ -20,20 +26,6 @@ const RATE_LIMITED = 429;
|
|
|
20
26
|
const RATE_LIMIT_RETRY_MS = 1000;
|
|
21
27
|
const EXCHANGE_TIMEOUT_MS = 15000;
|
|
22
28
|
const SESSION_ENDED_ELEMENT_ID = "base44-embed-session-ended";
|
|
23
|
-
const FRAME_SESSIONS_KEY = "__base44EmbedSessions";
|
|
24
|
-
/** @internal */
|
|
25
|
-
export function frameSession(appId, start) {
|
|
26
|
-
var _a, _b;
|
|
27
|
-
var _c;
|
|
28
|
-
if (typeof window === "undefined") {
|
|
29
|
-
return start ? start() : null;
|
|
30
|
-
}
|
|
31
|
-
const sessions = ((_a = (_c = window)[FRAME_SESSIONS_KEY]) !== null && _a !== void 0 ? _a : (_c[FRAME_SESSIONS_KEY] = {}));
|
|
32
|
-
if (!sessions[appId] && start) {
|
|
33
|
-
sessions[appId] = start();
|
|
34
|
-
}
|
|
35
|
-
return (_b = sessions[appId]) !== null && _b !== void 0 ? _b : null;
|
|
36
|
-
}
|
|
37
29
|
/** @internal */
|
|
38
30
|
export function takeEmbedTokenFromUrl() {
|
|
39
31
|
if (typeof window === "undefined" || !window.location) {
|
|
@@ -52,19 +44,11 @@ export function takeEmbedTokenFromUrl() {
|
|
|
52
44
|
catch (e) {
|
|
53
45
|
console.error("Error retrieving embed token from URL:", e);
|
|
54
46
|
}
|
|
55
|
-
return ott;
|
|
47
|
+
return isFramed() ? ott : null;
|
|
56
48
|
}
|
|
57
49
|
/** @internal */
|
|
58
50
|
export function isFramed() {
|
|
59
|
-
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
try {
|
|
63
|
-
return window.self !== window.top;
|
|
64
|
-
}
|
|
65
|
-
catch (_a) {
|
|
66
|
-
return true;
|
|
67
|
-
}
|
|
51
|
+
return typeof window !== "undefined" && window.self !== window.top;
|
|
68
52
|
}
|
|
69
53
|
/** @internal */
|
|
70
54
|
export async function exchangeEmbedToken({ serverUrl, appId, ott, fetchImpl = fetch, }) {
|
|
@@ -40,6 +40,5 @@ export declare function createFetchWithAuth({ axios, serviceRoleAxios, appId, se
|
|
|
40
40
|
serverUrl: string;
|
|
41
41
|
functionsVersion?: string;
|
|
42
42
|
platformHeaders?: Record<string, string>;
|
|
43
|
-
/** Resolves once the client's session is settled. */
|
|
44
43
|
waitForAuth?: () => Promise<void>;
|
|
45
44
|
}): (path: string, init?: FetchWithAuthInit) => Promise<Response>;
|