@base44-preview/sdk 0.8.43-pr.260.d28232f → 0.8.43-pr.261.f8cbcf8

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
@@ -12,7 +12,7 @@ import { createAppLogsModule } from "./modules/app-logs.js";
12
12
  import { createUsersModule } from "./modules/users.js";
13
13
  import { RoomsSocket } from "./utils/socket-utils.js";
14
14
  import { createAnalyticsModule } from "./modules/analytics.js";
15
- import { createActorsModule, resolveActorsHost, } from "./modules/actors.js";
15
+ import { createActorsModule, resolveActorsHost } from "./modules/actors.js";
16
16
  /**
17
17
  * Creates a Base44 client.
18
18
  *
@@ -110,15 +110,6 @@ export function createClient(config) {
110
110
  token: serviceToken,
111
111
  interceptResponses: false,
112
112
  });
113
- // Dedicated client for actor connection-token mints: no onError (a legacy
114
- // actor answers every mint with an expected 409 before the proxy fallback,
115
- // which must not reach the app's error handler — the actors module forwards
116
- // genuine failures itself via onMintError) and no constructor token
117
- // (auth is per-request so a login/logout is picked up on every reconnect).
118
- const actorsAxiosClient = createAxiosClient({
119
- baseURL: `${serverUrl}/api`,
120
- headers,
121
- });
122
113
  const userAuthModule = createAuthModule(axiosClient, functionsAxiosClient, appId, {
123
114
  appBaseUrl: normalizedAppBaseUrl,
124
115
  serverUrl,
@@ -136,26 +127,11 @@ export function createClient(config) {
136
127
  }
137
128
  const actorsModule = createActorsModule({
138
129
  appId,
139
- // serverUrl is often relative/empty (same-origin app); the proxy-fallback
140
- // URL needs an absolute host, so fall back to the page origin.
130
+ // serverUrl is often relative/empty (same-origin app); PartySocket needs an
131
+ // absolute host, so fall back to the page origin.
141
132
  host: resolveActorsHost(serverUrl, typeof window !== "undefined" ? (_a = window.location) === null || _a === void 0 ? void 0 : _a.origin : undefined),
142
133
  functionsVersion,
143
134
  getAuthToken: () => token || getAccessToken(),
144
- mintConnectionToken: async (actorName, room, connectionId) => {
145
- const authToken = token || getAccessToken();
146
- return await actorsAxiosClient.post(`/apps/${appId}/actors/${encodeURIComponent(actorName)}/connection-token`, { room, connection_id: connectionId }, {
147
- headers: {
148
- ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),
149
- // The mint endpoint resolves draft vs published from this header;
150
- // only the functions axios clients send it by default.
151
- ...(functionsVersion
152
- ? { "Base44-Functions-Version": functionsVersion }
153
- : {}),
154
- },
155
- });
156
- },
157
- transport: options === null || options === void 0 ? void 0 : options.actorsTransport,
158
- onMintError: options === null || options === void 0 ? void 0 : options.onError,
159
135
  });
160
136
  const userModules = {
161
137
  entities: createEntitiesModule({
@@ -15,20 +15,8 @@ import type { ActorsModule } from "./modules/actors.types.js";
15
15
  export interface CreateClientOptions {
16
16
  /**
17
17
  * Optional error handler that will be called whenever an API error occurs.
18
- *
19
- * Also receives {@link ActorsModule | actors} connection failures. Errors
20
- * are usually {@linkcode Base44Error} instances — check `error.status`.
21
18
  */
22
19
  onError?: (error: Error) => void;
23
- /**
24
- * Forces the actors transport. `"auto"` (default) connects directly to the
25
- * actor and falls back to the platform proxy when the app's actors don't
26
- * support direct connections; `"proxy"` always uses the platform proxy
27
- * (ops rollback — no connection-token calls); `"direct"` disables the
28
- * fallback (validation environments).
29
- * @internal
30
- */
31
- actorsTransport?: "auto" | "proxy" | "direct";
32
20
  }
33
21
  /**
34
22
  * Configuration for creating a Base44 client.
@@ -1,47 +1,21 @@
1
1
  import type { ActorRef } from "./actors.types.js";
2
- /** Credentials minted by the platform for one direct actor connection. */
3
- export interface ActorConnectionCredentials {
4
- /** Direct actor endpoint, already carrying `?_pk=<connectionId>`. */
5
- websocket_url: string;
6
- /** Short-lived JWT bound to (app, actor, room, connectionId); appended to
7
- * the URL as `token=` since browsers can't set WebSocket headers. */
8
- token: string;
9
- }
10
2
  interface ActorsConfig {
11
3
  appId: string;
12
- /** Current user access token, if authenticated. Rides the WS query on the
13
- * proxy-fallback path so the platform proxy can authenticate the connection;
14
- * anonymous connects omit it. */
4
+ /** Current user access token, if authenticated. Rides the WS query so the
5
+ * platform proxy can authenticate the connection; anonymous connects omit it. */
15
6
  getAuthToken(): string | null | undefined;
16
7
  /** Same semantics as function calls: editors with a non-prod version get the
17
8
  * draft actor script; everyone else gets the published one. */
18
9
  functionsVersion?: string;
19
- /** Absolute host for the proxy-fallback URL (scheme is swapped to wss, ws
10
+ /** Absolute host PartySocket dials (it strips the scheme and connects wss, ws
20
11
  * for localhost). Resolved by {@link resolveActorsHost}. */
21
12
  host: string;
22
- /** Mints a direct-connect credential for one (actor, room, connection).
23
- * Called per connection attempt: the token's expiry is checked at upgrade,
24
- * so every reconnect needs a fresh one. */
25
- mintConnectionToken(actorName: string, room: string, connectionId: string): Promise<ActorConnectionCredentials>;
26
- /** @internal Ops escape hatch: "proxy" never mints (legacy path only),
27
- * "direct" never falls back. Default "auto". */
28
- transport?: "auto" | "proxy" | "direct";
29
- /** Called when a mint fails for a reason other than the expected
30
- * direct→proxy fallback (which recovers by itself). Wired to the client's
31
- * `options.onError`. */
32
- onMintError?: (error: Error) => void;
33
13
  }
34
14
  /**
35
- * The legacy platform-proxy URL, byte-for-byte what PartySocket built before
36
- * the direct path existed: same scheme swap (including its localhost-needs-a-
37
- * port quirk), case-preserved party segment, `_pk` first in the query. The
38
- * `handler` param is load-bearing — the proxy reads it for the actor name.
39
- */
40
- export declare function buildProxyActorUrl(rawHost: string, actorName: string, instanceId: string, connectionId: string, appId: string, token: string | null | undefined, functionsVersion?: string): string;
41
- /**
42
- * Absolute host for the proxy-fallback actor URL. A relative/empty `serverUrl`
43
- * can't be dialed (same-origin apps use a relative `/api`, so `serverUrl` is
44
- * often `""`), so fall back to the page origin.
15
+ * Absolute host for the actor WebSocket. PartySocket needs an absolute host and
16
+ * can't resolve a relative/empty `serverUrl` (same-origin apps use a relative
17
+ * `/api`, so `serverUrl` is often `""`), so fall back to the page origin.
18
+ * PartySocket handles the scheme (https→wss, ws for localhost).
45
19
  */
46
20
  export declare function resolveActorsHost(serverUrl: string, browserOrigin?: string): string;
47
21
  export declare function createActorsModule(config: ActorsConfig): {
@@ -1,32 +1,8 @@
1
- import { WebSocket as ReconnectingWebSocket } from "partysocket";
2
- // Heartbeat / half-open detection: the socket only reconnects on a close/error
1
+ import PartySocket from "partysocket";
2
+ // Heartbeat / half-open detection: PartySocket only reconnects on a close/error
3
3
  // event, so ping periodically and force a reconnect if nothing returns in DEAD_MS.
4
4
  const PING_MS = 1000;
5
5
  const DEAD_MS = 3000;
6
- // Mint responses that mean "direct can't serve this connection, the proxy can":
7
- // 409 = legacy-family actor script, 503 = direct connections not provisioned,
8
- // 422 = no principal (e.g. anonymous outside a browser) or an id/room only the
9
- // proxy's looser validation accepts, 405 = a backend that predates the mint
10
- // endpoint (its actor deploy routes catch the path via `{handler_name:path}`
11
- // but not the POST method — and the real endpoint never 405s a POST). The
12
- // proxy serves migrated actors too, so falling back is always safe.
13
- const PROXY_FALLBACK_STATUSES = new Set([405, 409, 422, 503]);
14
- // Mint responses no retry can fix (bad request / forbidden / not found): the
15
- // connection closes instead of re-minting forever; a fresh connect() re-probes.
16
- // 401 is deliberately absent — the auth token is re-read on every attempt, so a
17
- // login recovers on the next retry. Disjoint from PROXY_FALLBACK_STATUSES.
18
- const TERMINAL_MINT_STATUSES = new Set([400, 403, 404]);
19
- /** The mint's rejection can be anything; a `Base44Error` carries a numeric
20
- * `.status` (absent for network failures). */
21
- function mintErrorStatus(err) {
22
- const status = err && typeof err === "object"
23
- ? err.status
24
- : undefined;
25
- return typeof status === "number" ? status : undefined;
26
- }
27
- function toError(err) {
28
- return err instanceof Error ? err : new Error(String(err));
29
- }
30
6
  /**
31
7
  * A live connection to an actor instance. Only obtainable from
32
8
  * {@link ActorRef.connect}, so `subscribe`/`send` are always valid — the socket
@@ -38,54 +14,23 @@ class Connection {
38
14
  this.onClose = onClose;
39
15
  this.listeners = new Set();
40
16
  this.heartbeat = null;
41
- this.closed = false;
42
17
  this.id = (_a = options === null || options === void 0 ? void 0 : options.id) !== null && _a !== void 0 ? _a : crypto.randomUUID();
43
- // Direct-first with proxy fallback, decided per connection attempt. Once a
44
- // mint answers with a fallback status the choice is sticky for this
45
- // socket's lifetime (a fresh connect() after close() probes direct again,
46
- // picking up actors migrated in the meantime). Any other mint failure
47
- // rejects, which ReconnectingWebSocket retries with backoff — except the
48
- // terminal statuses, which close this connection for good.
49
- let useProxy = config.transport === "proxy";
50
- const urlProvider = async () => {
51
- var _a;
52
- if (this.closed)
53
- throw new Error("Actor connection is closed");
54
- if (!useProxy) {
55
- try {
56
- const { websocket_url, token } = await config.mintConnectionToken(actorName, instanceId, this.id);
57
- const sep = websocket_url.includes("?") ? "&" : "?";
58
- return `${websocket_url}${sep}token=${encodeURIComponent(token)}`;
59
- }
60
- catch (err) {
61
- const status = mintErrorStatus(err);
62
- const isFallback = config.transport !== "direct" &&
63
- status !== undefined &&
64
- PROXY_FALLBACK_STATUSES.has(status);
65
- if (!isFallback) {
66
- if (status !== undefined && TERMINAL_MINT_STATUSES.has(status)) {
67
- // close() before notifying: ws.close() stops the redial the
68
- // rethrow below would otherwise schedule, and a handler that
69
- // immediately calls connect() gets a clean new connection.
70
- this.close();
71
- }
72
- // Reported from here because the socket's error event only
73
- // preserves `err.message`, never `.status`.
74
- try {
75
- (_a = config.onMintError) === null || _a === void 0 ? void 0 : _a.call(config, toError(err));
76
- }
77
- catch (_b) {
78
- // an app handler must not break the dial loop or mask `err`
79
- }
80
- throw err;
81
- }
82
- useProxy = true;
83
- }
84
- }
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);
87
- };
88
- const ws = new ReconnectingWebSocket(urlProvider);
18
+ const ws = new PartySocket({
19
+ host: config.host,
20
+ party: actorName,
21
+ room: instanceId,
22
+ id: this.id,
23
+ // Re-read on every (re)connect so a login/logout is picked up.
24
+ query: () => {
25
+ const token = config.getAuthToken();
26
+ return {
27
+ app_id: config.appId,
28
+ handler: actorName,
29
+ ...(token ? { token } : {}),
30
+ ...(config.functionsVersion ? { fv: config.functionsVersion } : {}),
31
+ };
32
+ },
33
+ });
89
34
  this.ws = ws;
90
35
  let lastMsg = Date.now();
91
36
  const bumpAlive = () => { lastMsg = Date.now(); };
@@ -108,11 +53,7 @@ class Connection {
108
53
  this.heartbeat = setInterval(() => {
109
54
  if (Date.now() - lastMsg > DEAD_MS) {
110
55
  bumpAlive(); // avoid a reconnect storm while the new socket comes up
111
- // Only kick a half-open socket (OPEN but silent). When it isn't open
112
- // the socket is already redialing with backoff, and reconnect() would
113
- // reset that backoff into a mint call every DEAD_MS.
114
- if (ws.readyState === ws.OPEN)
115
- ws.reconnect();
56
+ ws.reconnect();
116
57
  return;
117
58
  }
118
59
  try {
@@ -132,15 +73,9 @@ class Connection {
132
73
  };
133
74
  }
134
75
  send(data) {
135
- // after close() the socket would buffer forever (unbounded enqueue)
136
- if (this.closed)
137
- return;
138
76
  this.ws.send(JSON.stringify(data));
139
77
  }
140
78
  close() {
141
- if (this.closed)
142
- return;
143
- this.closed = true;
144
79
  if (this.heartbeat) {
145
80
  clearInterval(this.heartbeat);
146
81
  this.heartbeat = null;
@@ -169,38 +104,10 @@ function makeActorRef(actorName, instanceId, config, connections) {
169
104
  };
170
105
  }
171
106
  /**
172
- * The legacy platform-proxy URL, byte-for-byte what PartySocket built before
173
- * the direct path existed: same scheme swap (including its localhost-needs-a-
174
- * port quirk), case-preserved party segment, `_pk` first in the query. The
175
- * `handler` param is load-bearing — the proxy reads it for the actor name.
176
- */
177
- export function buildProxyActorUrl(rawHost, actorName, instanceId, connectionId, appId, token, functionsVersion) {
178
- let host = rawHost.replace(/^(http|https|ws|wss):\/\//, "");
179
- if (host.endsWith("/"))
180
- host = host.slice(0, -1);
181
- const insecure = host.startsWith("localhost:") ||
182
- host.startsWith("127.0.0.1:") ||
183
- host.startsWith("192.168.") ||
184
- host.startsWith("10.") ||
185
- (host.startsWith("172.") &&
186
- host.split(".")[1] >= "16" &&
187
- host.split(".")[1] <= "31") ||
188
- host.startsWith("[::ffff:7f00:1]:");
189
- const query = new URLSearchParams([
190
- ["_pk", connectionId],
191
- ["app_id", appId],
192
- ["handler", actorName],
193
- ]);
194
- if (token)
195
- query.append("token", token);
196
- if (functionsVersion)
197
- query.append("fv", functionsVersion);
198
- return `${insecure ? "ws" : "wss"}://${host}/parties/${actorName}/${instanceId}?${query}`;
199
- }
200
- /**
201
- * Absolute host for the proxy-fallback actor URL. A relative/empty `serverUrl`
202
- * can't be dialed (same-origin apps use a relative `/api`, so `serverUrl` is
203
- * often `""`), so fall back to the page origin.
107
+ * Absolute host for the actor WebSocket. PartySocket needs an absolute host and
108
+ * can't resolve a relative/empty `serverUrl` (same-origin apps use a relative
109
+ * `/api`, so `serverUrl` is often `""`), so fall back to the page origin.
110
+ * PartySocket handles the scheme (https→wss, ws for localhost).
204
111
  */
205
112
  export function resolveActorsHost(serverUrl, browserOrigin) {
206
113
  return serverUrl && !serverUrl.startsWith("/") ? serverUrl : browserOrigin !== null && browserOrigin !== void 0 ? browserOrigin : serverUrl;
@@ -56,14 +56,9 @@ export interface Connection<N extends string = string> {
56
56
  readonly id: string;
57
57
  /** Register a message listener. Multiple are allowed; returns a per-listener unsubscribe. */
58
58
  subscribe(callback: (data: ToClientFor<N>) => void): ActorSubscription;
59
- /** Send a message. Buffered by the socket until it's open; dropped after
60
- * {@link close}. */
59
+ /** Send a message. Buffered by the socket until it's open. */
61
60
  send(data: ToServerFor<N>): void;
62
- /**
63
- * Tear down the socket, heartbeat, and all listeners. Safe to call more
64
- * than once. A connection also closes itself when it fails permanently —
65
- * see {@link ActorRef.connect}.
66
- */
61
+ /** Tear down the socket, heartbeat, and all listeners. */
67
62
  close(): void;
68
63
  }
69
64
  /**
@@ -71,15 +66,7 @@ export interface Connection<N extends string = string> {
71
66
  * {@link connect} to open the socket and get a {@link Connection}.
72
67
  */
73
68
  export interface ActorRef<N extends string = string> {
74
- /**
75
- * Open the WebSocket and return the {@link Connection}. Idempotent while the
76
- * connection is open.
77
- *
78
- * A connection that fails permanently (for example, the actor doesn't exist
79
- * or the caller isn't allowed to connect) closes itself and reports the
80
- * error to the client's `onError` handler. Call `connect()` again after
81
- * fixing the cause to get a fresh {@link Connection}, and re-subscribe.
82
- */
69
+ /** Open the WebSocket and return the {@link Connection}. Idempotent. */
83
70
  connect(options?: ActorConnectOptions): Connection<N>;
84
71
  }
85
72
  /**
@@ -25,9 +25,6 @@ const analyticsSharedState = getSharedInstance(ANALYTICS_SHARED_STATE_NAME, () =
25
25
  wasInitializationTracked: false,
26
26
  sessionContext: null,
27
27
  sessionStartTime: null,
28
- // Memoized session id for when `localStorage` can't persist one — see
29
- // getAnalyticsSessionId.
30
- fallbackSessionId: null,
31
28
  config: {
32
29
  ...defaultConfiguration,
33
30
  ...getAnalyticsConfigFromUrlParams(),
@@ -219,11 +216,9 @@ function trackSessionDurationEvent(track) {
219
216
  });
220
217
  }
221
218
  function getEventIntrinsicData() {
222
- var _a, _b;
223
219
  return {
224
220
  timestamp: new Date().toISOString(),
225
- // `window.location` is absent on React Native, so read it optionally.
226
- pageUrl: typeof window !== "undefined" ? (_b = (_a = window.location) === null || _a === void 0 ? void 0 : _a.pathname) !== null && _b !== void 0 ? _b : null : null,
221
+ pageUrl: typeof window !== "undefined" ? window.location.pathname : null,
227
222
  };
228
223
  }
229
224
  function transformEventDataToApiRequestData(sessionContext) {
@@ -307,15 +302,9 @@ export function getAnalyticsConfigFromUrlParams() {
307
302
  // return the config object //
308
303
  return { enabled: analyticsEnable === "true" };
309
304
  }
310
- // When the id can't be persisted (React Native has no `localStorage`), keep
311
- // it stable for the process instead of minting a fresh one per call.
312
- function getFallbackSessionId() {
313
- var _a;
314
- return ((_a = analyticsSharedState.fallbackSessionId) !== null && _a !== void 0 ? _a : (analyticsSharedState.fallbackSessionId = generateUuid()));
315
- }
316
305
  export function getAnalyticsSessionId() {
317
306
  if (typeof window === "undefined") {
318
- return getFallbackSessionId();
307
+ return generateUuid();
319
308
  }
320
309
  try {
321
310
  const sessionId = localStorage.getItem(ANALYTICS_SESSION_ID_LOCAL_STORAGE_KEY);
@@ -327,6 +316,6 @@ export function getAnalyticsSessionId() {
327
316
  return sessionId;
328
317
  }
329
318
  catch (_a) {
330
- return getFallbackSessionId();
319
+ return generateUuid();
331
320
  }
332
321
  }
@@ -25,7 +25,7 @@ export function createConnectorsModule(axios, appId) {
25
25
  if (!integrationType || typeof integrationType !== "string") {
26
26
  throw new Error("Integration type is required and must be a string");
27
27
  }
28
- const response = await axios.get(`/apps/${appId}/external-auth/tokens/${integrationType}`);
28
+ const response = await axios.get(`/apps/${appId}/external-auth/tokens/${encodeURIComponent(integrationType)}`);
29
29
  // @ts-expect-error
30
30
  return response.access_token;
31
31
  },
@@ -34,7 +34,7 @@ export function createConnectorsModule(axios, appId) {
34
34
  if (!integrationType || typeof integrationType !== "string") {
35
35
  throw new Error("Integration type is required and must be a string");
36
36
  }
37
- const response = await axios.get(`/apps/${appId}/external-auth/tokens/${integrationType}`);
37
+ const response = await axios.get(`/apps/${appId}/external-auth/tokens/${encodeURIComponent(integrationType)}`);
38
38
  const data = response;
39
39
  return {
40
40
  accessToken: data.access_token,
@@ -46,7 +46,7 @@ export function createConnectorsModule(axios, appId) {
46
46
  if (!connectorId || typeof connectorId !== "string") {
47
47
  throw new Error("Connector ID is required and must be a string");
48
48
  }
49
- const response = await axios.get(`/apps/${appId}/external-auth/tokens/connectors/${connectorId}`);
49
+ const response = await axios.get(`/apps/${appId}/external-auth/tokens/connectors/${encodeURIComponent(connectorId)}`);
50
50
  const data = response;
51
51
  return {
52
52
  accessToken: data.access_token,
@@ -60,7 +60,7 @@ export function createConnectorsModule(axios, appId) {
60
60
  if (!connectorId || typeof connectorId !== "string") {
61
61
  throw new Error("Connector ID is required and must be a string");
62
62
  }
63
- const response = await axios.get(`/apps/${appId}/app-user-auth/connectors/${connectorId}/token`);
63
+ const response = await axios.get(`/apps/${appId}/app-user-auth/connectors/${encodeURIComponent(connectorId)}/token`);
64
64
  const data = response;
65
65
  return data.access_token;
66
66
  },
@@ -69,7 +69,7 @@ export function createConnectorsModule(axios, appId) {
69
69
  if (!connectorId || typeof connectorId !== "string") {
70
70
  throw new Error("Connector ID is required and must be a string");
71
71
  }
72
- const response = await axios.get(`/apps/${appId}/app-user-auth/connectors/${connectorId}/token`);
72
+ const response = await axios.get(`/apps/${appId}/app-user-auth/connectors/${encodeURIComponent(connectorId)}/token`);
73
73
  const data = response;
74
74
  return {
75
75
  accessToken: data.access_token,
@@ -78,7 +78,9 @@ export function createConnectorsModule(axios, appId) {
78
78
  },
79
79
  async callApi(integrationType, request) {
80
80
  assertNonEmptyString(integrationType, "Integration type");
81
- return proxyCall(axios, `/apps/${appId}/connectors/${integrationType}/call`, request);
81
+ // Encoded so a runtime-built identifier can only ever select a
82
+ // connector, never re-target another route under this token.
83
+ return proxyCall(axios, `/apps/${appId}/connectors/${encodeURIComponent(integrationType)}/call`, request);
82
84
  },
83
85
  };
84
86
  }
@@ -108,9 +110,9 @@ async function proxyCall(axios, url, request) {
108
110
  }
109
111
  const response = await axios.post(url, {
110
112
  method,
111
- // Omitted rather than sent as null so the proxy applies the connector's
112
- // declared default host.
113
- ...(request.host === undefined ? {} : { host: request.host }),
113
+ // Omitted when unset (undefined or null, since untyped callers write
114
+ // either) so the proxy applies the connector's declared default host.
115
+ ...(request.host == null ? {} : { host: request.host }),
114
116
  path: request.path,
115
117
  query: (_b = request.query) !== null && _b !== void 0 ? _b : {},
116
118
  headers: (_c = request.headers) !== null && _c !== void 0 ? _c : {},
@@ -142,7 +144,7 @@ export function createUserConnectorsModule(axios, appId) {
142
144
  if (!connectorId || typeof connectorId !== "string") {
143
145
  throw new Error("Connector ID is required and must be a string");
144
146
  }
145
- const response = await axios.post(`/apps/${appId}/app-user-auth/connectors/${connectorId}/initiate`);
147
+ const response = await axios.post(`/apps/${appId}/app-user-auth/connectors/${encodeURIComponent(connectorId)}/initiate`);
146
148
  const data = response;
147
149
  return data.redirect_url;
148
150
  },
@@ -150,7 +152,7 @@ export function createUserConnectorsModule(axios, appId) {
150
152
  if (!connectorId || typeof connectorId !== "string") {
151
153
  throw new Error("Connector ID is required and must be a string");
152
154
  }
153
- await axios.delete(`/apps/${appId}/app-user-auth/connectors/${connectorId}`);
155
+ await axios.delete(`/apps/${appId}/app-user-auth/connectors/${encodeURIComponent(connectorId)}`);
154
156
  },
155
157
  };
156
158
  }
@@ -89,7 +89,7 @@ export interface ConnectorApiResponse<T = unknown> {
89
89
  * The parsed upstream response body, or proxy error details when no response
90
90
  * was received. `null` when the response was binary — see {@link dataBase64}.
91
91
  */
92
- data: T;
92
+ data: T | null;
93
93
  /**
94
94
  * The response body base64-encoded, for the media types the connector declares
95
95
  * as binary (images, PDFs). Set instead of {@link data}, never alongside it.
@@ -132,10 +132,8 @@ export function createAxiosClient({ baseURL, headers = {}, token, interceptRespo
132
132
  client.interceptors.request.use((config) => {
133
133
  // `window.location` is absent on React Native (where `window` still exists),
134
134
  // so guard on it before reading `.href`.
135
- if (typeof window !== "undefined") {
136
- if (window.location) {
137
- config.headers.set("X-Origin-URL", window.location.href);
138
- }
135
+ if (typeof window !== "undefined" && window.location) {
136
+ config.headers.set("X-Origin-URL", window.location.href);
139
137
  // On unauthenticated requests, attach a stable anonymous visitor id so the
140
138
  // backend can support anonymous agent access (conversation grouping + ownership).
141
139
  // Authenticated requests are identified by their Authorization header instead.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@base44-preview/sdk",
3
- "version": "0.8.43-pr.260.d28232f",
3
+ "version": "0.8.43-pr.261.f8cbcf8",
4
4
  "description": "JavaScript SDK for Base44 API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",