@base44-preview/sdk 0.8.48-pr.281.97e1467 → 0.8.48-pr.282.2b05017

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 CHANGED
@@ -5,6 +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
9
  import { createFetchWithAuth } from "./utils/fetch-with-auth.js";
9
10
  import { createFunctionsModule } from "./modules/functions.js";
10
11
  import { createAgentsModule } from "./modules/agents.js";
@@ -57,12 +58,24 @@ export function createClient(config) {
57
58
  const { serverUrl = "https://base44.app", appId, analytics, token, serviceToken, requiresAuth = false, appBaseUrl, options, functionsVersion, headers: optionalHeaders, } = config;
58
59
  // Normalize appBaseUrl to always be a string (empty if not provided or invalid)
59
60
  const normalizedAppBaseUrl = typeof appBaseUrl === "string" ? appBaseUrl : "";
61
+ const inBrowser = typeof window !== "undefined";
62
+ // A host platform embeds the app with a one-time token on the iframe URL.
63
+ // It comes off the URL here, before any other code can read it; the
64
+ // exchange that turns it into a session runs once the modules exist.
65
+ const embedOtt = inBrowser ? takeEmbedTokenFromUrl() : null;
66
+ const embedded = embedOtt !== null || (inBrowser && isEmbeddedTab());
67
+ if (embedOtt) {
68
+ markEmbeddedTab();
69
+ }
70
+ let currentToken = token !== null && token !== void 0 ? token : null;
60
71
  const socketConfig = {
61
72
  serverUrl,
62
73
  mountPath: "/ws-user-apps/socket.io/",
63
74
  transports: ["websocket"],
64
75
  appId,
65
- token,
76
+ // null (not undefined): an embedded frame's only identity is the one the
77
+ // platform minted, so the socket must not fall back to a stored token.
78
+ token: embedded ? null : token,
66
79
  };
67
80
  let socket = null;
68
81
  const getSocket = () => {
@@ -125,26 +138,73 @@ export function createClient(config) {
125
138
  appBaseUrl: normalizedAppBaseUrl,
126
139
  serverUrl,
127
140
  token,
141
+ embedded,
128
142
  });
129
143
  // Apply the access token before any module that may issue authenticated
130
144
  // requests during construction (notably analytics, which fires an init
131
145
  // event whose flush calls auth.me()). Without this, the first User/me
132
146
  // request is built before setToken runs and goes out unauthenticated.
133
- if (typeof window !== "undefined") {
147
+ // An embedded load skips this: its session comes from the exchange below,
148
+ // and a token an earlier visitor left in storage must not become it.
149
+ if (inBrowser && !embedded) {
134
150
  const accessToken = token || getAccessToken();
135
151
  if (accessToken) {
152
+ currentToken = accessToken;
136
153
  userAuthModule.setToken(accessToken);
137
154
  }
138
155
  }
156
+ // The one answer every module reads. Follows setToken and logout; outside an
157
+ // embedded frame it still falls back to storage, so a login in another tab is
158
+ // picked up on the next connection, as before.
159
+ const getToken = () => {
160
+ if (userAuthModule.hasToken()) {
161
+ return currentToken;
162
+ }
163
+ return embedded || !inBrowser ? null : getAccessToken();
164
+ };
165
+ const applyToken = (newToken, saveToStorage) => {
166
+ userAuthModule.setToken(newToken, saveToStorage);
167
+ currentToken = newToken;
168
+ socketConfig.token = newToken;
169
+ if (socket) {
170
+ socket.updateConfig({ token: newToken });
171
+ }
172
+ };
173
+ // Resolves once the embedded session is in hand (or known not to be coming);
174
+ // immediately for every other client. The exchanged token is applied without
175
+ // persisting it: the session must not outlive the frame it was minted for.
176
+ const authReady = embedOtt
177
+ ? exchangeEmbedToken({ serverUrl, appId, ott: embedOtt }).then((sessionToken) => {
178
+ if (sessionToken) {
179
+ applyToken(sessionToken, false);
180
+ }
181
+ })
182
+ : Promise.resolve();
183
+ if (embedOtt) {
184
+ // Requests issued while the exchange is in flight wait for it and carry the
185
+ // session it produces. Registered after createAxiosClient's interceptors,
186
+ // so it runs first and the anonymous-visitor header sees the Authorization.
187
+ for (const client of [axiosClient, functionsAxiosClient]) {
188
+ client.interceptors.request.use(async (requestConfig) => {
189
+ await authReady;
190
+ const sessionToken = getToken();
191
+ if (sessionToken && !requestConfig.headers.get("Authorization")) {
192
+ requestConfig.headers.set("Authorization", `Bearer ${sessionToken}`);
193
+ }
194
+ return requestConfig;
195
+ });
196
+ }
197
+ }
139
198
  const actorsModule = createActorsModule({
140
199
  appId,
141
200
  // serverUrl is often relative/empty (same-origin app); the proxy-fallback
142
201
  // URL needs an absolute host, so fall back to the page origin.
143
202
  host: resolveActorsHost(serverUrl, typeof window !== "undefined" ? (_a = window.location) === null || _a === void 0 ? void 0 : _a.origin : undefined),
144
203
  functionsVersion,
145
- getAuthToken: () => token || getAccessToken(),
204
+ getAuthToken: getToken,
146
205
  mintConnectionToken: async (actorName, room, connectionId) => {
147
- const authToken = token || getAccessToken();
206
+ await authReady;
207
+ const authToken = getToken();
148
208
  return await actorsAxiosClient.post(`/apps/${appId}/actors/${encodeURIComponent(actorName)}/connection-token`, { room, connection_id: connectionId }, {
149
209
  headers: {
150
210
  ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),
@@ -171,10 +231,9 @@ export function createClient(config) {
171
231
  functions: createFunctionsModule(functionsAxiosClient, appId, {
172
232
  getAuthHeaders: () => {
173
233
  const headers = {};
174
- // Get current token from storage or initial config
175
- const currentToken = token || getAccessToken();
176
- if (currentToken) {
177
- headers["Authorization"] = `Bearer ${currentToken}`;
234
+ const sessionToken = getToken();
235
+ if (sessionToken) {
236
+ headers["Authorization"] = `Bearer ${sessionToken}`;
178
237
  }
179
238
  return headers;
180
239
  },
@@ -185,9 +244,9 @@ export function createClient(config) {
185
244
  getSocket,
186
245
  appId,
187
246
  serverUrl,
188
- token,
247
+ getToken,
189
248
  }),
190
- aiGateway: createAiGatewayModule({ serverUrl, token, appId }),
249
+ aiGateway: createAiGatewayModule({ serverUrl, getToken, appId }),
191
250
  appLogs: createAppLogsModule(axiosClient, appId),
192
251
  app: createAppModule(axiosClient, appId),
193
252
  users: createUsersModule(axiosClient, appId),
@@ -232,9 +291,13 @@ export function createClient(config) {
232
291
  getSocket,
233
292
  appId,
234
293
  serverUrl,
235
- token,
294
+ getToken: () => token,
295
+ }),
296
+ aiGateway: createAiGatewayModule({
297
+ serverUrl,
298
+ getToken: () => serviceToken,
299
+ appId,
236
300
  }),
237
- aiGateway: createAiGatewayModule({ serverUrl, token: serviceToken, appId }),
238
301
  appLogs: createAppLogsModule(serviceRoleAxiosClient, appId),
239
302
  cleanup: () => {
240
303
  if (socket) {
@@ -274,6 +337,7 @@ export function createClient(config) {
274
337
  * Sets a new authentication token for all subsequent requests.
275
338
  *
276
339
  * @param newToken - The new authentication token
340
+ * @param saveToStorage - Whether to also keep the token in local storage. Defaults to `true`.
277
341
  *
278
342
  * @example
279
343
  * ```typescript
@@ -285,14 +349,8 @@ export function createClient(config) {
285
349
  * base44.setToken(access_token);
286
350
  * ```
287
351
  */
288
- setToken(newToken) {
289
- userModules.auth.setToken(newToken);
290
- if (socket) {
291
- socket.updateConfig({
292
- token: newToken,
293
- });
294
- }
295
- socketConfig.token = newToken;
352
+ setToken(newToken, saveToStorage = true) {
353
+ applyToken(newToken, saveToStorage);
296
354
  },
297
355
  /**
298
356
  * Gets the current client configuration.
@@ -200,8 +200,9 @@ export interface Base44Client {
200
200
  * Updates the token for both HTTP requests and WebSocket connections.
201
201
  *
202
202
  * @param newToken - The new authentication token.
203
+ * @param saveToStorage - Whether to also keep the token in local storage so it survives a page load. Defaults to `true`.
203
204
  */
204
- setToken(newToken: string): void;
205
+ setToken(newToken: string, saveToStorage?: boolean): void;
205
206
  /**
206
207
  * Gets the current client configuration.
207
208
  * @internal
@@ -1,2 +1,2 @@
1
1
  import { AgentsModule, AgentsModuleConfig } from "./agents.types.js";
2
- export declare function createAgentsModule({ axios, getSocket, appId, serverUrl, token, }: AgentsModuleConfig): AgentsModule;
2
+ export declare function createAgentsModule({ axios, getSocket, appId, serverUrl, getToken, }: AgentsModuleConfig): AgentsModule;
@@ -1,5 +1,4 @@
1
- import { getAccessToken } from "../utils/auth-utils.js";
2
- export function createAgentsModule({ axios, getSocket, appId, serverUrl, token, }) {
1
+ export function createAgentsModule({ axios, getSocket, appId, serverUrl, getToken, }) {
3
2
  const baseURL = `/apps/${appId}/agents`;
4
3
  // Track active conversations
5
4
  const currentConversations = {};
@@ -56,7 +55,7 @@ export function createAgentsModule({ axios, getSocket, appId, serverUrl, token,
56
55
  };
57
56
  const getWhatsAppConnectURL = (agentName) => {
58
57
  const baseUrl = `${serverUrl}/api/apps/${appId}/agents/${encodeURIComponent(agentName)}/whatsapp`;
59
- const accessToken = token !== null && token !== void 0 ? token : getAccessToken();
58
+ const accessToken = getToken();
60
59
  if (accessToken) {
61
60
  return `${baseUrl}?token=${accessToken}`;
62
61
  }
@@ -67,7 +66,7 @@ export function createAgentsModule({ axios, getSocket, appId, serverUrl, token,
67
66
  };
68
67
  const getTelegramConnectURL = (agentName) => {
69
68
  const baseUrl = `${serverUrl}/api/apps/${appId}/agents/${encodeURIComponent(agentName)}/telegram`;
70
- const accessToken = token !== null && token !== void 0 ? token : getAccessToken();
69
+ const accessToken = getToken();
71
70
  if (accessToken) {
72
71
  return `${baseUrl}?token=${accessToken}`;
73
72
  }
@@ -160,8 +160,8 @@ export interface AgentsModuleConfig {
160
160
  appId: string;
161
161
  /** Server URL */
162
162
  serverUrl?: string;
163
- /** Authentication token */
164
- token?: string;
163
+ /** Returns the current authentication token, if any */
164
+ getToken: () => string | null | undefined;
165
165
  }
166
166
  /**
167
167
  * Agents module for managing AI agent conversations.
@@ -1,2 +1,2 @@
1
1
  import { AiGatewayModule, AiGatewayModuleConfig } from "./ai-gateway.types.js";
2
- export declare function createAiGatewayModule({ serverUrl, token, appId, }: AiGatewayModuleConfig): AiGatewayModule;
2
+ export declare function createAiGatewayModule({ serverUrl, getToken, appId, }: AiGatewayModuleConfig): AiGatewayModule;
@@ -1,10 +1,9 @@
1
- import { getAccessToken } from "../utils/auth-utils.js";
2
- export function createAiGatewayModule({ serverUrl, token, appId, }) {
1
+ export function createAiGatewayModule({ serverUrl, getToken, appId, }) {
3
2
  const connection = () => {
4
3
  var _a;
5
4
  return ({
6
5
  baseURL: `${serverUrl}/api/apps/${appId}/ai/openai/v1`,
7
- token: (_a = token !== null && token !== void 0 ? token : getAccessToken()) !== null && _a !== void 0 ? _a : "",
6
+ token: (_a = getToken()) !== null && _a !== void 0 ? _a : "",
8
7
  });
9
8
  };
10
9
  return {
@@ -17,8 +17,8 @@ export interface AiGatewayConnection {
17
17
  export interface AiGatewayModuleConfig {
18
18
  /** Server URL */
19
19
  serverUrl?: string;
20
- /** Authentication token */
21
- token?: string;
20
+ /** Returns the current authentication token, if any */
21
+ getToken: () => string | null | undefined;
22
22
  /** Application ID */
23
23
  appId: string;
24
24
  }
@@ -1,4 +1,5 @@
1
1
  import { resetAnalyticsSessionContext } from "./analytics.js";
2
+ import { showEmbedSessionEnded } from "../utils/embed-session.js";
2
3
  function isInsideIframe() {
3
4
  if (typeof window === "undefined")
4
5
  return false;
@@ -86,6 +87,9 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
86
87
  hasToken() {
87
88
  return hasAccessToken;
88
89
  },
90
+ isEmbedded() {
91
+ return Boolean(options.embedded);
92
+ },
89
93
  // Get current user information
90
94
  async me() {
91
95
  const request = pendingMe !== null && pendingMe !== void 0 ? pendingMe : axios.get(`/apps/${appId}/entities/User/me`).finally(() => {
@@ -109,6 +113,12 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
109
113
  if (typeof window === "undefined") {
110
114
  throw new Error("Login method can only be used in a browser environment");
111
115
  }
116
+ // An embedded session can only be renewed by the host platform; the
117
+ // login round trip cannot complete inside its frame.
118
+ if (options.embedded) {
119
+ showEmbedSessionEnded();
120
+ return;
121
+ }
112
122
  // If nextUrl is not provided, use the current URL
113
123
  const redirectUrl = nextUrl
114
124
  ? new URL(nextUrl, window.location.origin).toString()
@@ -98,6 +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
+ */
105
+ embedded?: boolean;
101
106
  }
102
107
  /**
103
108
  * Authentication module for managing user authentication and authorization. The module automatically stores tokens in local storage when available and manages authorization headers for API requests.
@@ -179,6 +184,22 @@ export interface AuthModule {
179
184
  * ```
180
185
  */
181
186
  redirectToLogin(nextUrl: string): void;
187
+ /**
188
+ * Whether the app is running embedded in a host platform.
189
+ *
190
+ * A platform embeds an app in an iframe and signs its user in by minting a one-time token onto the frame's URL; the SDK trades it for a session as the client is created. That session is held in memory only and can be renewed only by the platform, so when it ends there is no login page to send the user to — {@linkcode AuthModule.redirectToLogin | redirectToLogin()} shows a "session ended" notice instead. Use this to render your own notice or to hide sign-in and sign-out controls that cannot work inside the frame.
191
+ *
192
+ * @returns `true` when the app was embedded by a host platform, `false` otherwise.
193
+ *
194
+ * @example
195
+ * ```typescript
196
+ * // Show your own message instead of a login screen
197
+ * if (!user && base44.auth.isEmbedded()) {
198
+ * return <SessionEnded />;
199
+ * }
200
+ * ```
201
+ */
202
+ isEmbedded(): boolean;
182
203
  /**
183
204
  * Redirects the user to a third-party authentication provider's login page.
184
205
  *
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Sessions for apps embedded in a host platform.
3
+ *
4
+ * The platform's server mints a one-time token for one of its users and puts
5
+ * it on the iframe URL as `?ott=`. The client takes it off the URL as it is
6
+ * created, trades it for an app-user session through the OAuth token-exchange
7
+ * grant (RFC 8693), and keeps the result in memory only — never in storage — so
8
+ * the session lives and dies with the frame.
9
+ *
10
+ * @internal
11
+ */
12
+ /**
13
+ * Reads the one-time token off the current URL and strips it, so it is never
14
+ * left in the address bar, in history, or in a shared link.
15
+ *
16
+ * @internal
17
+ */
18
+ export declare function takeEmbedTokenFromUrl(): string | null;
19
+ /**
20
+ * Whether this tab previously redeemed an embed token. Counts only inside a
21
+ * frame: a top-level tab that once carried a token must fall back to the
22
+ * regular login, or it could never sign in again.
23
+ *
24
+ * @internal
25
+ */
26
+ export declare function isEmbeddedTab(): boolean;
27
+ /** @internal */
28
+ export declare function markEmbeddedTab(): void;
29
+ /**
30
+ * Trades a one-time embed token for an app-user session token. Resolves to
31
+ * `null` when the platform refuses the token; never throws.
32
+ *
33
+ * @internal
34
+ */
35
+ export declare function exchangeEmbedToken({ serverUrl, appId, ott, fetchImpl, }: {
36
+ serverUrl: string;
37
+ appId: string;
38
+ ott: string;
39
+ fetchImpl?: typeof fetch;
40
+ }): Promise<string | null>;
41
+ /**
42
+ * Covers the page with a plain "session ended" notice. An embedded session can
43
+ * only be renewed by the host platform (by reloading its own page), so this
44
+ * stands in for the login redirect, which cannot complete inside a frame.
45
+ *
46
+ * @internal
47
+ */
48
+ export declare function showEmbedSessionEnded(): void;
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Sessions for apps embedded in a host platform.
3
+ *
4
+ * The platform's server mints a one-time token for one of its users and puts
5
+ * it on the iframe URL as `?ott=`. The client takes it off the URL as it is
6
+ * created, trades it for an app-user session through the OAuth token-exchange
7
+ * grant (RFC 8693), and keeps the result in memory only — never in storage — so
8
+ * the session lives and dies with the frame.
9
+ *
10
+ * @internal
11
+ */
12
+ const OTT_PARAM = "ott";
13
+ const EMBED_TAB_KEY = "base44_embed_session";
14
+ const GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange";
15
+ const SUBJECT_TOKEN_TYPE = "urn:base44:params:oauth:token-type:embed-ott";
16
+ const RATE_LIMITED = 429;
17
+ const RATE_LIMIT_RETRY_MS = 1000;
18
+ const SESSION_ENDED_ELEMENT_ID = "base44-embed-session-ended";
19
+ /**
20
+ * Reads the one-time token off the current URL and strips it, so it is never
21
+ * left in the address bar, in history, or in a shared link.
22
+ *
23
+ * @internal
24
+ */
25
+ export function takeEmbedTokenFromUrl() {
26
+ if (typeof window === "undefined" || !window.location) {
27
+ return null;
28
+ }
29
+ try {
30
+ const url = new URL(window.location.href);
31
+ const ott = url.searchParams.get(OTT_PARAM);
32
+ if (!ott) {
33
+ return null;
34
+ }
35
+ url.searchParams.delete(OTT_PARAM);
36
+ window.history.replaceState(window.history.state, "", url.toString());
37
+ return ott;
38
+ }
39
+ catch (e) {
40
+ console.error("Error retrieving embed token from URL:", e);
41
+ return null;
42
+ }
43
+ }
44
+ /**
45
+ * Whether this tab previously redeemed an embed token. Counts only inside a
46
+ * frame: a top-level tab that once carried a token must fall back to the
47
+ * regular login, or it could never sign in again.
48
+ *
49
+ * @internal
50
+ */
51
+ export function isEmbeddedTab() {
52
+ if (typeof window === "undefined") {
53
+ return false;
54
+ }
55
+ // A cross-site frame is third-party storage, which some browsers block
56
+ // outright — every access can throw, and the marker is then unavailable.
57
+ try {
58
+ return (window.self !== window.top &&
59
+ window.sessionStorage.getItem(EMBED_TAB_KEY) === "1");
60
+ }
61
+ catch (_a) {
62
+ return false;
63
+ }
64
+ }
65
+ /** @internal */
66
+ export function markEmbeddedTab() {
67
+ try {
68
+ window.sessionStorage.setItem(EMBED_TAB_KEY, "1");
69
+ }
70
+ catch (_a) {
71
+ /* storage blocked */
72
+ }
73
+ }
74
+ /**
75
+ * Trades a one-time embed token for an app-user session token. Resolves to
76
+ * `null` when the platform refuses the token; never throws.
77
+ *
78
+ * @internal
79
+ */
80
+ export async function exchangeEmbedToken({ serverUrl, appId, ott, fetchImpl = fetch, }) {
81
+ const post = () => fetchImpl(`${serverUrl}/api/apps/${appId}/auth/embed/token`, {
82
+ method: "POST",
83
+ headers: { "X-App-Id": String(appId) },
84
+ body: new URLSearchParams({
85
+ grant_type: GRANT_TYPE,
86
+ subject_token: ott,
87
+ subject_token_type: SUBJECT_TOKEN_TYPE,
88
+ }),
89
+ });
90
+ try {
91
+ let response = await post();
92
+ // The limiter refuses before the token is redeemed, so it is still valid.
93
+ if (response.status === RATE_LIMITED) {
94
+ await new Promise((resolve) => setTimeout(resolve, RATE_LIMIT_RETRY_MS));
95
+ response = await post();
96
+ }
97
+ if (!response.ok) {
98
+ return null;
99
+ }
100
+ const { access_token: token } = (await response.json());
101
+ return token || null;
102
+ }
103
+ catch (e) {
104
+ console.error("Embed token exchange failed:", e);
105
+ return null;
106
+ }
107
+ }
108
+ /**
109
+ * Covers the page with a plain "session ended" notice. An embedded session can
110
+ * only be renewed by the host platform (by reloading its own page), so this
111
+ * stands in for the login redirect, which cannot complete inside a frame.
112
+ *
113
+ * @internal
114
+ */
115
+ export function showEmbedSessionEnded() {
116
+ if (typeof document === "undefined" || !document.body) {
117
+ return;
118
+ }
119
+ if (document.getElementById(SESSION_ENDED_ELEMENT_ID)) {
120
+ return;
121
+ }
122
+ const overlay = document.createElement("div");
123
+ overlay.id = SESSION_ENDED_ELEMENT_ID;
124
+ overlay.setAttribute("role", "alert");
125
+ overlay.style.cssText =
126
+ "position:fixed;inset:0;z-index:2147483647;display:flex;align-items:center;" +
127
+ "justify-content:center;background:#fff;color:#0f172a;" +
128
+ "font-family:ui-sans-serif,system-ui,sans-serif;text-align:center;padding:2rem;";
129
+ const title = document.createElement("h1");
130
+ title.textContent = "Session ended";
131
+ title.style.cssText = "font-size:1.5rem;font-weight:700;margin:0 0 .75rem;";
132
+ const body = document.createElement("p");
133
+ body.textContent = "Refresh the page to continue.";
134
+ body.style.cssText = "margin:0;color:#475569;";
135
+ const card = document.createElement("div");
136
+ card.append(title, body);
137
+ overlay.append(card);
138
+ document.body.append(overlay);
139
+ }
@@ -4,7 +4,9 @@ export interface RoomsSocketConfig {
4
4
  mountPath: string;
5
5
  transports: string[];
6
6
  appId: string;
7
- token?: string;
7
+ /** `undefined` means unknown — the socket looks in storage; `null` means
8
+ * known to be anonymous, so storage is never consulted. */
9
+ token?: string | null;
8
10
  }
9
11
  export type TSocketRoom = string;
10
12
  export type TJsonStr = string;
@@ -3,12 +3,11 @@ import { getAccessToken } from "./auth-utils.js";
3
3
  import { getAnalyticsSessionId } from "../modules/analytics.js";
4
4
  const ROOM_LEAVE_GRACE_MS = 250;
5
5
  function initializeSocket(config, handlers) {
6
- var _a;
7
6
  // On unauthenticated clients, send a stable anonymous visitor id on the
8
7
  // handshake so the backend can verify room access for anonymous agent
9
8
  // conversations (mirrors the X-Base44-Anonymous-Id HTTP header). Authenticated
10
9
  // clients are identified by their token instead.
11
- const resolvedToken = (_a = config.token) !== null && _a !== void 0 ? _a : getAccessToken();
10
+ const resolvedToken = config.token === undefined ? getAccessToken() : config.token;
12
11
  const query = {
13
12
  app_id: config.appId,
14
13
  token: resolvedToken,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@base44-preview/sdk",
3
- "version": "0.8.48-pr.281.97e1467",
3
+ "version": "0.8.48-pr.282.2b05017",
4
4
  "description": "JavaScript SDK for Base44 API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",