@base44-preview/sdk 0.8.35-pr.221.5933e35 → 0.8.36-pr.212.0d64c77

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
@@ -7,10 +7,12 @@ import { createConnectorsModule, createUserConnectorsModule, } from "./modules/c
7
7
  import { getAccessToken } from "./utils/auth-utils.js";
8
8
  import { createFunctionsModule } from "./modules/functions.js";
9
9
  import { createAgentsModule } from "./modules/agents.js";
10
+ import { createAiGatewayModule } from "./modules/ai-gateway.js";
10
11
  import { createAppLogsModule } from "./modules/app-logs.js";
11
12
  import { createUsersModule } from "./modules/users.js";
12
13
  import { RoomsSocket } from "./utils/socket-utils.js";
13
14
  import { createAnalyticsModule } from "./modules/analytics.js";
15
+ import { createRealtimeModule, pushUserTokenToActiveSockets } from "./modules/realtime.js";
14
16
  /**
15
17
  * Creates a Base44 client.
16
18
  *
@@ -50,9 +52,19 @@ import { createAnalyticsModule } from "./modules/analytics.js";
50
52
  */
51
53
  export function createClient(config) {
52
54
  var _a, _b;
53
- const { serverUrl = "https://base44.app", appId, token, serviceToken, requiresAuth = false, appBaseUrl, options, functionsVersion, headers: optionalHeaders, disableAnalytics = false, adapter, } = config;
55
+ const { serverUrl = "https://base44.app", appId, token, serviceToken, requiresAuth = false, appBaseUrl, options, functionsVersion, headers: optionalHeaders, dispatcherWsUrl, webSocketImpl, } = config;
54
56
  // Normalize appBaseUrl to always be a string (empty if not provided or invalid)
55
57
  const normalizedAppBaseUrl = typeof appBaseUrl === "string" ? appBaseUrl : "";
58
+ // Derive the dispatcher WebSocket URL from serverUrl if not explicitly provided.
59
+ // Convert https:// → wss:// (or http:// → ws://) and strip trailing slash.
60
+ const resolvedDispatcherWsUrl = (() => {
61
+ if (dispatcherWsUrl)
62
+ return dispatcherWsUrl.replace(/\/$/, "");
63
+ return serverUrl
64
+ .replace(/\/$/, "")
65
+ .replace(/^https:\/\//, "wss://")
66
+ .replace(/^http:\/\//, "ws://");
67
+ })();
56
68
  const socketConfig = {
57
69
  serverUrl,
58
70
  mountPath: "/ws-user-apps/socket.io/",
@@ -84,7 +96,6 @@ export function createClient(config) {
84
96
  headers,
85
97
  token,
86
98
  onError: options === null || options === void 0 ? void 0 : options.onError,
87
- adapter,
88
99
  });
89
100
  const functionsAxiosClient = createAxiosClient({
90
101
  baseURL: `${serverUrl}/api`,
@@ -92,7 +103,6 @@ export function createClient(config) {
92
103
  token,
93
104
  interceptResponses: false,
94
105
  onError: options === null || options === void 0 ? void 0 : options.onError,
95
- adapter,
96
106
  });
97
107
  const serviceRoleHeaders = {
98
108
  ...headers,
@@ -103,19 +113,32 @@ export function createClient(config) {
103
113
  headers: serviceRoleHeaders,
104
114
  token: serviceToken,
105
115
  onError: options === null || options === void 0 ? void 0 : options.onError,
106
- adapter,
107
116
  });
108
117
  const serviceRoleFunctionsAxiosClient = createAxiosClient({
109
118
  baseURL: `${serverUrl}/api`,
110
119
  headers: functionHeaders,
111
120
  token: serviceToken,
112
121
  interceptResponses: false,
113
- adapter,
114
122
  });
115
123
  const userAuthModule = createAuthModule(axiosClient, functionsAxiosClient, appId, {
116
124
  appBaseUrl: normalizedAppBaseUrl,
117
125
  serverUrl,
118
126
  });
127
+ // Current user session token (axios defaults are the single source of truth —
128
+ // createClient({token}) and every setToken() land there). Used for in-band
129
+ // realtime auth; read lazily so refreshes are always picked up.
130
+ const getUserToken = () => {
131
+ var _a;
132
+ const h = (_a = axiosClient.defaults.headers.common) === null || _a === void 0 ? void 0 : _a["Authorization"];
133
+ return typeof h === "string" && h.startsWith("Bearer ") ? h.slice(7) : null;
134
+ };
135
+ // Login / token refresh must reach long-lived realtime sockets too, so the
136
+ // handler-side credential never goes stale mid-connection.
137
+ const originalSetToken = userAuthModule.setToken.bind(userAuthModule);
138
+ userAuthModule.setToken = (newToken, saveToStorage) => {
139
+ originalSetToken(newToken, saveToStorage);
140
+ pushUserTokenToActiveSockets(newToken);
141
+ };
119
142
  // Apply the access token before any module that may issue authenticated
120
143
  // requests during construction (notably analytics, which fires an init
121
144
  // event whose flush calls auth.me()). Without this, the first User/me
@@ -154,6 +177,7 @@ export function createClient(config) {
154
177
  serverUrl,
155
178
  token,
156
179
  }),
180
+ aiGateway: createAiGatewayModule({ serverUrl, token }),
157
181
  appLogs: createAppLogsModule(axiosClient, appId),
158
182
  users: createUsersModule(axiosClient, appId),
159
183
  analytics: createAnalyticsModule({
@@ -161,7 +185,31 @@ export function createClient(config) {
161
185
  serverUrl,
162
186
  appId,
163
187
  userAuthModule,
164
- disabled: disableAnalytics,
188
+ }),
189
+ realtime: createRealtimeModule({
190
+ appId,
191
+ dispatcherWsUrl: resolvedDispatcherWsUrl,
192
+ webSocketImpl,
193
+ getUserToken,
194
+ getToken: async (handlerName, instanceId, connId) => {
195
+ // axiosClient interceptors unwrap response.data, so the result is the body directly.
196
+ // conn_id rides inside the signed token (not a WS query param) so it survives
197
+ // proxies that strip params; the dispatcher forwards it as the handler's conn.id.
198
+ // Base44-Functions-Version rides along (like function calls) so live apps get
199
+ // tokens for the *published* realtime script and previews get the draft.
200
+ const data = await axiosClient.post(`/apps/${appId}/realtime-token`, {
201
+ handler_name: handlerName,
202
+ instance_id: instanceId,
203
+ conn_id: connId,
204
+ // Declares "an __auth message follows right after connect" — the
205
+ // handler delays handleConnect until it arrives (signed into the
206
+ // token so old SDKs, which never send __auth, are never waited on).
207
+ supports_inband_auth: getUserToken() != null,
208
+ }, functionsVersion
209
+ ? { headers: { "Base44-Functions-Version": functionsVersion } }
210
+ : undefined);
211
+ return data.token;
212
+ },
165
213
  }),
166
214
  cleanup: () => {
167
215
  userModules.analytics.cleanup();
@@ -197,6 +245,7 @@ export function createClient(config) {
197
245
  serverUrl,
198
246
  token,
199
247
  }),
248
+ aiGateway: createAiGatewayModule({ serverUrl, token: serviceToken }),
200
249
  appLogs: createAppLogsModule(serviceRoleAxiosClient, appId),
201
250
  cleanup: () => {
202
251
  if (socket) {
@@ -1,4 +1,3 @@
1
- import type { AxiosRequestConfig } from "axios";
2
1
  import type { EntitiesModule } from "./modules/entities.types.js";
3
2
  import type { IntegrationsModule } from "./modules/integrations.types.js";
4
3
  import type { AuthModule } from "./modules/auth.types.js";
@@ -6,8 +5,10 @@ import type { SsoModule } from "./modules/sso.types.js";
6
5
  import type { ConnectorsModule, UserConnectorsModule } from "./modules/connectors.types.js";
7
6
  import type { FunctionsModule } from "./modules/functions.types.js";
8
7
  import type { AgentsModule } from "./modules/agents.types.js";
8
+ import type { AiGatewayModule } from "./modules/ai-gateway.types.js";
9
9
  import type { AppLogsModule } from "./modules/app-logs.types.js";
10
10
  import type { AnalyticsModule } from "./modules/analytics.types.js";
11
+ import type { RealtimeModule } from "./modules/realtime.types.js";
11
12
  /**
12
13
  * Options for creating a Base44 client.
13
14
  */
@@ -70,31 +71,28 @@ export interface CreateClientConfig {
70
71
  */
71
72
  headers?: Record<string, string>;
72
73
  /**
73
- * Disables the {@link AnalyticsModule | analytics module} entirely.
74
- *
75
- * When `true`, `base44.analytics.track()` becomes a no-op and no background
76
- * processing or heartbeat timers are started. Set this for server-side
77
- * clients (SSR, edge runtimes) where background timers must not run.
78
- * {@linkcode createServerClient | createServerClient()} sets this automatically.
79
- *
80
- * Analytics is also automatically disabled outside a browser environment.
81
- *
82
- * @defaultValue `false`
74
+ * Additional client options.
83
75
  */
84
- disableAnalytics?: boolean;
76
+ options?: CreateClientOptions;
85
77
  /**
86
- * Axios adapter override for HTTP requests.
78
+ * Base WebSocket URL for the Cloudflare Durable Object dispatcher.
87
79
  *
88
- * By default, axios picks an adapter based on the runtime. Set this to
89
- * `'fetch'` in edge runtimes such as Cloudflare Workers, where the default
90
- * adapter detection can pick the wrong adapter.
91
- * {@linkcode createServerClient | createServerClient()} sets this automatically.
80
+ * Defaults to the `serverUrl` with `https://` replaced by `wss://` (or `http://` by `ws://`).
81
+ * Override when the dispatcher lives at a different host than the API.
92
82
  */
93
- adapter?: AxiosRequestConfig["adapter"];
83
+ dispatcherWsUrl?: string;
94
84
  /**
95
- * Additional client options.
85
+ * WebSocket implementation for realtime subscriptions in environments
86
+ * without a global `WebSocket` (Node.js < 22). Browsers and Node ≥ 22
87
+ * don't need this.
88
+ *
89
+ * @example
90
+ * ```typescript
91
+ * import WS from "ws";
92
+ * const base44 = createClient({ appId, webSocketImpl: WS });
93
+ * ```
96
94
  */
97
- options?: CreateClientOptions;
95
+ webSocketImpl?: unknown;
98
96
  }
99
97
  /**
100
98
  * The Base44 client instance.
@@ -104,10 +102,14 @@ export interface CreateClientConfig {
104
102
  export interface Base44Client {
105
103
  /** {@link AgentsModule | Agents module} for managing AI agent conversations. */
106
104
  agents: AgentsModule;
105
+ /** {@link AiGatewayModule | AI Gateway module} for connecting to the Base44 AI Gateway with your own SDK. */
106
+ aiGateway: AiGatewayModule;
107
107
  /** {@link AnalyticsModule | Analytics module} for tracking custom events in your app. */
108
108
  analytics: AnalyticsModule;
109
109
  /** {@link AppLogsModule | App logs module} for tracking app usage. */
110
110
  appLogs: AppLogsModule;
111
+ /** {@link RealtimeModule | Realtime module} for subscribing to and sending messages via Cloudflare Durable Object-backed RealtimeHandlers. */
112
+ realtime: RealtimeModule;
111
113
  /** {@link AuthModule | Auth module} for user authentication and management. */
112
114
  auth: AuthModule;
113
115
  /** {@link UserConnectorsModule | Connectors module} for app-user OAuth flows. */
@@ -147,6 +149,8 @@ export interface Base44Client {
147
149
  readonly asServiceRole: {
148
150
  /** {@link AgentsModule | Agents module} with elevated permissions. */
149
151
  agents: AgentsModule;
152
+ /** {@link AiGatewayModule | AI Gateway module} with the service-role token. */
153
+ aiGateway: AiGatewayModule;
150
154
  /** {@link AppLogsModule | App logs module} with elevated permissions. */
151
155
  appLogs: AppLogsModule;
152
156
  /** {@link ConnectorsModule | Connectors module} for OAuth token retrieval. */
package/dist/index.d.ts CHANGED
@@ -1,17 +1,19 @@
1
1
  import { createClient, createClientFromRequest, type Base44Client, type CreateClientConfig, type CreateClientOptions } from "./client.js";
2
- import { createServerClient, type CreateServerClientOptions } from "./server.js";
3
2
  import { Base44Error, type Base44ErrorJSON } from "./utils/axios-client.js";
4
3
  import { getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl } from "./utils/auth-utils.js";
5
- export { createClient, createClientFromRequest, createServerClient, Base44Error, getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl, };
6
- export type { Base44Client, CreateClientConfig, CreateClientOptions, CreateServerClientOptions, Base44ErrorJSON, };
4
+ export { createClient, createClientFromRequest, Base44Error, getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl, };
5
+ export type { Base44Client, CreateClientConfig, CreateClientOptions, Base44ErrorJSON, };
7
6
  export * from "./types.js";
8
7
  export type { DeleteManyResult, DeleteResult, EntitiesModule, EntityFilterOperators, EntityFilterQuery, EntityFilterValue, EntityHandler, EntityRecord, EntityTypeRegistry, ImportResult, RealtimeEventType, RealtimeEvent, RealtimeCallback, SortField, UpdateManyResult, } from "./modules/entities.types.js";
9
8
  export type { AuthModule, LoginResponse, RegisterParams, VerifyOtpParams, ChangePasswordParams, ResetPasswordParams, User, } from "./modules/auth.types.js";
10
9
  export type { IntegrationsModule, IntegrationEndpointFunction, CoreIntegrations, InvokeLLMParams, GenerateImageParams, GenerateImageResult, UploadFileParams, UploadFileResult, SendEmailParams, SendEmailResult, ExtractDataFromUploadedFileParams, ExtractDataFromUploadedFileResult, UploadPrivateFileParams, UploadPrivateFileResult, CreateFileSignedUrlParams, CreateFileSignedUrlResult, } from "./modules/integrations.types.js";
11
10
  export type { FunctionsModule, FunctionName, FunctionNameRegistry, } from "./modules/functions.types.js";
12
11
  export type { AgentsModule, AgentName, AgentNameRegistry, AgentConversation, AgentMessage, AgentMessageReasoning, AgentMessageToolCall, AgentMessageUsage, AgentMessageCustomContext, AgentMessageMetadata, CreateConversationParams, } from "./modules/agents.types.js";
12
+ export type { AiGatewayModule, AiGatewayConnection, } from "./modules/ai-gateway.types.js";
13
13
  export type { AppLogsModule } from "./modules/app-logs.types.js";
14
+ export type { RealtimeModule, RealtimeHandlerClient, RealtimeHandlerNameRegistry, RealtimeHandlerRegistry, } from "./modules/realtime.types.js";
14
15
  export type { SsoModule, SsoAccessTokenResponse } from "./modules/sso.types.js";
16
+ export { RealtimeHandler, type Conn } from "./realtime-handler.js";
15
17
  export type { ConnectorsModule, UserConnectorsModule, } from "./modules/connectors.types.js";
16
18
  export type { CustomIntegrationsModule, CustomIntegrationCallParams, CustomIntegrationCallResponse, } from "./modules/custom-integrations.types.js";
17
19
  export type { GetAccessTokenOptions, SaveAccessTokenOptions, RemoveAccessTokenOptions, GetLoginUrlOptions, } from "./utils/auth-utils.types.js";
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createClient, createClientFromRequest, } from "./client.js";
2
- import { createServerClient, } from "./server.js";
3
2
  import { Base44Error } from "./utils/axios-client.js";
4
3
  import { getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl, } from "./utils/auth-utils.js";
5
- export { createClient, createClientFromRequest, createServerClient, Base44Error, getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl, };
4
+ export { createClient, createClientFromRequest, Base44Error, getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl, };
6
5
  export * from "./types.js";
6
+ export { RealtimeHandler } from "./realtime-handler.js";
@@ -0,0 +1,2 @@
1
+ import { AiGatewayModule, AiGatewayModuleConfig } from "./ai-gateway.types.js";
2
+ export declare function createAiGatewayModule({ serverUrl, token, }: AiGatewayModuleConfig): AiGatewayModule;
@@ -0,0 +1,13 @@
1
+ import { getAccessToken } from "../utils/auth-utils.js";
2
+ export function createAiGatewayModule({ serverUrl, token, }) {
3
+ const connection = () => {
4
+ var _a;
5
+ return ({
6
+ baseURL: `${serverUrl}/api/ai/openai/v1`,
7
+ token: (_a = token !== null && token !== void 0 ? token : getAccessToken()) !== null && _a !== void 0 ? _a : "",
8
+ });
9
+ };
10
+ return {
11
+ connection,
12
+ };
13
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * A connection to the Base44 AI Gateway.
3
+ *
4
+ * Contains the base URL and bearer token to use with any OpenAI-compatible client
5
+ * (OpenAI SDK, Mastra, Vercel AI SDK, and others) pointed at the Base44 AI
6
+ * Gateway.
7
+ */
8
+ export interface AiGatewayConnection {
9
+ /** Base URL of the gateway's OpenAI-compatible endpoint. */
10
+ baseURL: string;
11
+ /** Bearer token used to authenticate requests to the gateway. */
12
+ token: string;
13
+ }
14
+ /**
15
+ * Configuration for the AI Gateway module.
16
+ * @internal
17
+ */
18
+ export interface AiGatewayModuleConfig {
19
+ /** Server URL */
20
+ serverUrl?: string;
21
+ /** Authentication token */
22
+ token?: string;
23
+ }
24
+ /**
25
+ * AI Gateway module for calling Base44's managed AI models from your own code.
26
+ *
27
+ * The gateway exposes an OpenAI-compatible Chat Completions endpoint, so any
28
+ * OpenAI-compatible SDK works against it:
29
+ * - Build custom AI agents with agent SDKs such as Mastra or the Vercel AI SDK
30
+ * - Uses your app's models, billing, and credit quota, no API key to manage
31
+ *
32
+ * Available in user authentication mode (`base44.aiGateway`) and with the
33
+ * service-role token via `base44.asServiceRole.aiGateway`.
34
+ */
35
+ export interface AiGatewayModule {
36
+ /**
37
+ * Gets the connection details for the Base44 AI Gateway.
38
+ *
39
+ * Returns the `baseURL` and `token` to pass to any OpenAI-compatible client.
40
+ *
41
+ * The `token` is the current caller's bearer token: the app user's token for
42
+ * `base44.aiGateway`, or the service-role token for `base44.asServiceRole.aiGateway`.
43
+ * When the caller is unauthenticated, `token` is an empty string.
44
+ *
45
+ * @returns The gateway {@linkcode AiGatewayConnection | connection} (`baseURL` and `token`).
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * // Build an AI agent with Mastra on top of the gateway, inside a backend function
50
+ * import { createClientFromRequest } from 'npm:@base44/sdk';
51
+ * import { Agent } from 'npm:@mastra/core/agent';
52
+ * import { createTool } from 'npm:@mastra/core/tools';
53
+ * import { createOpenAICompatible } from 'npm:@ai-sdk/openai-compatible';
54
+ * import { z } from 'npm:zod';
55
+ *
56
+ * Deno.serve(async (req) => {
57
+ * const base44 = createClientFromRequest(req);
58
+ * const { baseURL, token } = base44.aiGateway.connection();
59
+ * const models = createOpenAICompatible({ name: 'base44', baseURL, apiKey: token });
60
+ *
61
+ * const agent = new Agent({
62
+ * id: 'order-helper',
63
+ * name: 'order-helper',
64
+ * instructions: 'Help the user with their orders.',
65
+ * model: models('claude_sonnet_4_6'),
66
+ * tools: {
67
+ * lookupOrder: createTool({
68
+ * id: 'lookup-order',
69
+ * description: 'Fetch an order by id',
70
+ * inputSchema: z.object({ orderId: z.string() }),
71
+ * execute: async ({ orderId }) => base44.entities.Order.get(orderId),
72
+ * }),
73
+ * },
74
+ * });
75
+ *
76
+ * const { text } = await agent.generate('Where is order 123?');
77
+ * return Response.json({ text });
78
+ * });
79
+ * ```
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * // Call a model directly with the OpenAI SDK
84
+ * import { createClientFromRequest } from 'npm:@base44/sdk';
85
+ * import OpenAI from 'npm:openai';
86
+ *
87
+ * Deno.serve(async (req) => {
88
+ * const base44 = createClientFromRequest(req);
89
+ * const { baseURL, token } = base44.aiGateway.connection();
90
+ *
91
+ * const openai = new OpenAI({ baseURL, apiKey: token });
92
+ * const res = await openai.chat.completions.create({
93
+ * model: 'claude_sonnet_4_6',
94
+ * messages: [{ role: 'user', content: 'Hello!' }],
95
+ * });
96
+ * return Response.json({ text: res.choices[0].message.content });
97
+ * });
98
+ * ```
99
+ */
100
+ connection(): AiGatewayConnection;
101
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -11,10 +11,8 @@ export interface AnalyticsModuleArgs {
11
11
  serverUrl: string;
12
12
  appId: string;
13
13
  userAuthModule: AuthModule;
14
- /** Skips all analytics processing when true (e.g. for server-side clients). */
15
- disabled?: boolean;
16
14
  }
17
- export declare const createAnalyticsModule: ({ axiosClient, serverUrl, appId, userAuthModule, disabled, }: AnalyticsModuleArgs) => {
15
+ export declare const createAnalyticsModule: ({ axiosClient, serverUrl, appId, userAuthModule, }: AnalyticsModuleArgs) => {
18
16
  track: (params: TrackEventParams) => void;
19
17
  cleanup: () => void;
20
18
  };
@@ -30,16 +30,11 @@ const analyticsSharedState = getSharedInstance(ANALYTICS_SHARED_STATE_NAME, () =
30
30
  ...getAnalyticsConfigFromUrlParams(),
31
31
  },
32
32
  }));
33
- export const createAnalyticsModule = ({ axiosClient, serverUrl, appId, userAuthModule, disabled = false, }) => {
33
+ export const createAnalyticsModule = ({ axiosClient, serverUrl, appId, userAuthModule, }) => {
34
34
  var _a;
35
35
  // prevent overflow of events //
36
36
  const { maxQueueSize, throttleTime, batchSize } = analyticsSharedState.config;
37
- // Analytics is browser-only. Outside a browser (SSR, Cloudflare Workers,
38
- // Node) timers would leak — or throw at global scope in Workers — so the
39
- // module becomes a no-op there, and when explicitly disabled.
40
- if (disabled ||
41
- typeof window === "undefined" ||
42
- !((_a = analyticsSharedState.config) === null || _a === void 0 ? void 0 : _a.enabled)) {
37
+ if (!((_a = analyticsSharedState.config) === null || _a === void 0 ? void 0 : _a.enabled)) {
43
38
  return {
44
39
  track: () => { },
45
40
  cleanup: () => { },
@@ -1,4 +1,3 @@
1
- import { setAccessTokenCookie, clearAccessTokenCookie, } from "../utils/auth-utils.js";
2
1
  function isInsideIframe() {
3
2
  if (typeof window === "undefined")
4
3
  return false;
@@ -130,8 +129,6 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
130
129
  console.error("Failed to remove token from localStorage:", e);
131
130
  }
132
131
  }
133
- // Clear the cookie that mirrors the token for SSR
134
- clearAccessTokenCookie();
135
132
  // Determine the from_url parameter
136
133
  const fromUrl = redirectUrl || window.location.href;
137
134
  // Redirect to server-side logout endpoint to clear HTTP-only cookies
@@ -158,8 +155,6 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
158
155
  catch (e) {
159
156
  console.error("Failed to save token to localStorage:", e);
160
157
  }
161
- // Mirror the token into a cookie so document requests carry it to SSR
162
- setAccessTokenCookie(token);
163
158
  }
164
159
  },
165
160
  // Login using username and password
@@ -0,0 +1,28 @@
1
+ /** Push a (new) user session token to every open realtime socket — called on
2
+ * login/refresh so long-lived connections keep a valid credential server-side. */
3
+ export declare function pushUserTokenToActiveSockets(token: string): void;
4
+ export declare function createRealtimeModule(config: {
5
+ appId: string;
6
+ getToken(handlerName: string, instanceId: string, connId: string): Promise<string>;
7
+ /** Current user session token, if signed in. Sent in-band ({type:"__auth"})
8
+ * right after every socket open — never in the URL — so the handler can act
9
+ * as this user (createUserClient / RLS). */
10
+ getUserToken?: () => string | null;
11
+ dispatcherWsUrl: string;
12
+ /** WebSocket implementation for runtimes without a global one (Node < 22). */
13
+ webSocketImpl?: unknown;
14
+ }): Record<string, RealtimeHandler>;
15
+ /** Handle for an active realtime subscription. */
16
+ interface RealtimeSubscription {
17
+ /** This connection's id — the same value the handler receives as `conn.id`. */
18
+ id: string;
19
+ /** Close the subscription and its underlying socket. */
20
+ unsubscribe(): void;
21
+ }
22
+ interface RealtimeHandler {
23
+ subscribe(instanceId: string, callback: (data: unknown) => void, options?: {
24
+ id?: string;
25
+ }): RealtimeSubscription;
26
+ send(instanceId: string, data: unknown): void;
27
+ }
28
+ export {};
@@ -0,0 +1,124 @@
1
+ import PartySocket from "partysocket";
2
+ // Module-level map: "HandlerName:instanceId" → active socket
3
+ const activeSockets = new Map();
4
+ function socketKey(handlerName, instanceId) {
5
+ return `${handlerName}:${instanceId}`;
6
+ }
7
+ /** Push a (new) user session token to every open realtime socket — called on
8
+ * login/refresh so long-lived connections keep a valid credential server-side. */
9
+ export function pushUserTokenToActiveSockets(token) {
10
+ if (!token)
11
+ return;
12
+ const payload = JSON.stringify({ type: "__auth", token });
13
+ for (const ws of activeSockets.values()) {
14
+ try {
15
+ ws.send(payload);
16
+ }
17
+ catch ( /* not open — the open handler will send */_a) { /* not open — the open handler will send */ }
18
+ }
19
+ }
20
+ export function createRealtimeModule(config) {
21
+ return new Proxy({}, {
22
+ get(_, handlerName) {
23
+ return {
24
+ subscribe(instanceId, callback, options) {
25
+ var _a, _b;
26
+ const key = socketKey(handlerName, instanceId);
27
+ // close existing if any
28
+ (_a = activeSockets.get(key)) === null || _a === void 0 ? void 0 : _a.close();
29
+ // Connection id: caller-supplied (stable — reuse across reconnects/tabs as
30
+ // you see fit) or auto-generated per subscription. It travels INSIDE the
31
+ // signed realtime token (never as a WS query param, which proxies strip);
32
+ // the dispatcher forwards the verified claim as partyserver's _pk, so the
33
+ // handler sees this exact value as conn.id. Reconnects re-mint the token
34
+ // with the same id, so conn.id is stable across reconnects.
35
+ const connId = (_b = options === null || options === void 0 ? void 0 : options.id) !== null && _b !== void 0 ? _b : crypto.randomUUID();
36
+ // query as async fn: called on every (re)connect, fetches a fresh token each time
37
+ const ws = new PartySocket({
38
+ host: config.dispatcherWsUrl,
39
+ party: handlerName,
40
+ room: instanceId,
41
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
42
+ ...(config.webSocketImpl ? { WebSocket: config.webSocketImpl } : {}),
43
+ query: () => config.getToken(handlerName, instanceId, connId).then((token) => ({ token })),
44
+ });
45
+ activeSockets.set(key, ws);
46
+ // In-band credential delivery (Supabase-style): the user token rides the
47
+ // open socket, never the URL. Sent on every open (incl. reconnects); the
48
+ // server may also nudge with {type:"__auth_required"} (e.g. just before
49
+ // the held token expires) and we answer with the current one.
50
+ const sendAuth = () => {
51
+ var _a;
52
+ const t = (_a = config.getUserToken) === null || _a === void 0 ? void 0 : _a.call(config);
53
+ if (t) {
54
+ try {
55
+ ws.send(JSON.stringify({ type: "__auth", token: t }));
56
+ }
57
+ catch ( /* not open */_b) { /* not open */ }
58
+ }
59
+ };
60
+ ws.addEventListener("open", sendAuth);
61
+ // Heartbeat / half-open detection. PartySocket only reconnects on a
62
+ // browser close/error event, so a silently-dead connection (TCP alive,
63
+ // no data — common behind proxies/LBs) hangs until the OS idle timeout
64
+ // (~60s). We ping periodically and force a reconnect if nothing comes
65
+ // back within DEAD_MS, cutting detection from ~60s to a few seconds.
66
+ // Pairs with the handler's setWebSocketAutoResponse("__ping"→"__pong"),
67
+ // so idle handlers (no app broadcasts) still keep the connection proven.
68
+ const PING_MS = 1000;
69
+ const DEAD_MS = 3000;
70
+ let lastMsg = Date.now();
71
+ const bumpAlive = () => { lastMsg = Date.now(); };
72
+ ws.addEventListener("open", bumpAlive);
73
+ ws.addEventListener("message", (ev) => {
74
+ bumpAlive();
75
+ let data;
76
+ try {
77
+ data = JSON.parse(ev.data);
78
+ }
79
+ catch (_a) {
80
+ return; // ignore malformed
81
+ }
82
+ // Swallow platform messages — never surface them to the app.
83
+ const msgType = data && typeof data === "object" ? data.type : undefined;
84
+ if (msgType === "__pong")
85
+ return;
86
+ if (msgType === "__auth_required") {
87
+ sendAuth();
88
+ return;
89
+ }
90
+ callback(data);
91
+ });
92
+ const heartbeat = setInterval(() => {
93
+ if (Date.now() - lastMsg > DEAD_MS) {
94
+ bumpAlive(); // avoid a reconnect storm while the new socket comes up
95
+ ws.reconnect();
96
+ return;
97
+ }
98
+ try {
99
+ ws.send(JSON.stringify({ type: "__ping" }));
100
+ }
101
+ catch (_a) {
102
+ // socket not open; the watchdog above will force a reconnect
103
+ }
104
+ }, PING_MS);
105
+ return {
106
+ id: connId, // the connection id (same value the handler sees as conn.id)
107
+ unsubscribe() {
108
+ clearInterval(heartbeat);
109
+ activeSockets.delete(key);
110
+ ws.close();
111
+ },
112
+ };
113
+ },
114
+ send(instanceId, data) {
115
+ const key = socketKey(handlerName, instanceId);
116
+ const ws = activeSockets.get(key);
117
+ if (!ws)
118
+ throw new Error(`No active subscription for ${handlerName}:${instanceId}`);
119
+ ws.send(JSON.stringify(data));
120
+ },
121
+ };
122
+ },
123
+ });
124
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Extend this interface to add typed `subscribe` callbacks and `send` payloads
3
+ * for your deployed RealtimeHandlers.
4
+ *
5
+ * This is separate from {@link RealtimeHandlerNameRegistry} (which is auto-generated
6
+ * by `base44 types generate`), so there are no conflicts.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * declare module "@base44/sdk" {
11
+ * interface RealtimeHandlerRegistry {
12
+ * ChatRoom: {
13
+ * toClient: { type: "joined" | "left" | "message"; userId?: string; from?: string; text?: string };
14
+ * toServer: { type: "message"; text: string };
15
+ * };
16
+ * }
17
+ * }
18
+ * ```
19
+ */
20
+ export interface RealtimeHandlerRegistry {
21
+ }
22
+ /**
23
+ * Auto-populated by `base44 types generate` with the names of your deployed handlers.
24
+ * Do not edit this interface manually — use {@link RealtimeHandlerRegistry} for message types.
25
+ */
26
+ export interface RealtimeHandlerNameRegistry {
27
+ }
28
+ type AllHandlerNames = keyof RealtimeHandlerRegistry | keyof RealtimeHandlerNameRegistry;
29
+ type ToClientFor<N extends string> = N extends keyof RealtimeHandlerRegistry ? RealtimeHandlerRegistry[N] extends {
30
+ toClient: infer I;
31
+ } ? I : unknown : unknown;
32
+ type ToServerFor<N extends string> = N extends keyof RealtimeHandlerRegistry ? RealtimeHandlerRegistry[N] extends {
33
+ toServer: infer O;
34
+ } ? O : unknown : unknown;
35
+ /**
36
+ * Client for a single named RealtimeHandler.
37
+ * Typed automatically when the handler is registered in {@link RealtimeHandlerRegistry}.
38
+ */
39
+ export interface RealtimeHandlerClient<N extends string = string> {
40
+ /**
41
+ * Open a WebSocket subscription. Returns a {@link RealtimeSubscription} with the
42
+ * connection `id` (same value the handler sees as `conn.id`) and an `unsubscribe()` method.
43
+ *
44
+ * Pass `options.id` to control the connection id (e.g. a stable per-tab id so a
45
+ * reconnect reuses the same server-side connection); omit it for an auto-generated
46
+ * per-connection id.
47
+ */
48
+ subscribe(instanceId: string, callback: (data: ToClientFor<N>) => void, options?: {
49
+ id?: string;
50
+ }): RealtimeSubscription;
51
+ /** Send a message over the open socket. Throws if not subscribed. */
52
+ send(instanceId: string, data: ToServerFor<N>): void;
53
+ }
54
+ /** Handle for an active realtime subscription. */
55
+ export interface RealtimeSubscription {
56
+ /** This connection's id — the same value the handler receives as `conn.id`. */
57
+ id: string;
58
+ /** Close the subscription and its underlying socket. */
59
+ unsubscribe(): void;
60
+ }
61
+ /**
62
+ * The realtime module provides access to Cloudflare Durable Object-backed
63
+ * RealtimeHandlers deployed by the Base44 platform.
64
+ *
65
+ * Handler names are accessed as dynamic properties on this module:
66
+ * ```typescript
67
+ * const sub = await base44.realtime.MyHandler.subscribe("room-1", (msg) => {
68
+ * console.log(msg); // typed if MyHandler is in RealtimeHandlerRegistry
69
+ * });
70
+ * const { id, unsubscribe } = sub;
71
+ * unsubscribe();
72
+ * ```
73
+ */
74
+ export type RealtimeModule = {
75
+ [K in AllHandlerNames]: K extends keyof RealtimeHandlerRegistry ? RealtimeHandlerClient<string & K> : RealtimeHandlerClient;
76
+ } & Record<string, RealtimeHandlerClient>;
77
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,5 @@
1
1
  export * from "./app.types.js";
2
2
  export * from "./agents.types.js";
3
+ export * from "./ai-gateway.types.js";
3
4
  export * from "./connectors.types.js";
4
5
  export * from "./analytics.types.js";
@@ -1,4 +1,5 @@
1
1
  export * from "./app.types.js";
2
2
  export * from "./agents.types.js";
3
+ export * from "./ai-gateway.types.js";
3
4
  export * from "./connectors.types.js";
4
5
  export * from "./analytics.types.js";
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Type-only base class for Realtime Handlers.
3
+ *
4
+ * Import and extend this in your handler files:
5
+ * import { RealtimeHandler } from "@base44/sdk";
6
+ * export class MyHandler extends RealtimeHandler { ... }
7
+ *
8
+ * At deploy time the bundler replaces this import with the compiled
9
+ * Cloudflare Durable Object implementation — this file provides types only.
10
+ */
11
+ import type { Base44Client } from "./client.types.js";
12
+ /**
13
+ * A single client connection. `Send` is the message type this connection accepts
14
+ * via {@link send} — the handler's *outgoing* (server→client) messages.
15
+ */
16
+ export interface Conn<Send = unknown> {
17
+ /** Unique per-connection id (one per socket/tab), the same value the client
18
+ * receives from `subscribe()`. Use this — not userId — to identify a distinct
19
+ * client, so multiple tabs of the same user are separate connections. */
20
+ id: string;
21
+ userId: string;
22
+ appId: string;
23
+ instanceId: string;
24
+ send(data: Send): void;
25
+ reject(code: number, reason: string): void;
26
+ }
27
+ export interface Storage {
28
+ get<T>(key: string): Promise<T | undefined>;
29
+ put(key: string, value: unknown): Promise<void>;
30
+ delete(key: string): Promise<boolean>;
31
+ }
32
+ /**
33
+ * Base class for a Realtime Handler.
34
+ *
35
+ * @typeParam Incoming - messages this handler *receives* from clients
36
+ * (`handleMessage`'s `msg`) — the schema's `toServer` section.
37
+ * @typeParam Outgoing - messages this handler *sends* to clients
38
+ * (`conn.send`/`broadcast`) — the schema's `toClient` section.
39
+ *
40
+ * With a generated `schema.jsonc`, wire both from the registry so they can't drift
41
+ * from the client's types:
42
+ * ```ts
43
+ * type Reg = RealtimeHandlerRegistry["MyHandler"];
44
+ * class MyHandler extends RealtimeHandler<Reg["toServer"], Reg["toClient"]> { ... }
45
+ * ```
46
+ */
47
+ export declare abstract class RealtimeHandler<Incoming = unknown, Outgoing = unknown> {
48
+ abstract handleConnect(conn: Conn<Outgoing>): void | Promise<void>;
49
+ abstract handleMessage(conn: Conn<Outgoing>, msg: Incoming): void | Promise<void>;
50
+ abstract handleClose(conn: Conn<Outgoing>): void | Promise<void>;
51
+ abstract handleTick(): void | Promise<void>;
52
+ onStart(): void | Promise<void>;
53
+ /**
54
+ * Managed ticker (opt-in). Override {@link shouldTick} and the platform runs
55
+ * {@link handleTick} on a timer of {@link tickIntervalMs} while it returns true,
56
+ * and stops (letting the Durable Object hibernate — no compute cost) when it
57
+ * returns false. The platform owns scheduling, rescheduling, self-heal, and
58
+ * error-safety — you don't call {@link startLoop}/{@link stopLoop}.
59
+ *
60
+ * Re-evaluated after every connect/message/close and on every tick, so keep it
61
+ * cheap and pure (no async, no side effects). Example: `return this.players >= 2`.
62
+ */
63
+ protected tickIntervalMs: number;
64
+ protected shouldTick?(): boolean;
65
+ protected broadcast(_data: Outgoing): void;
66
+ protected getConnections(): Conn<Outgoing>[];
67
+ protected startLoop(_ms: number): Promise<void>;
68
+ protected stopLoop(): Promise<void>;
69
+ protected get instanceId(): string;
70
+ protected get storage(): Storage;
71
+ /**
72
+ * SDK client acting **as the connected user** — every entity call respects
73
+ * the app's row-level security, evaluated as that user at call time. The
74
+ * default wherever a `conn` is in scope (connect/message/close).
75
+ *
76
+ * Throws if the connection carries no user credential (anonymous visitor,
77
+ * signed-out session, or an app SDK that predates in-band auth).
78
+ */
79
+ protected createUserClient(conn: Conn): Base44Client;
80
+ /**
81
+ * Service-role SDK client — bypasses RLS. For work with **no user in scope**
82
+ * (tick, alarm, onStart). Inside handleMessage/handleConnect prefer
83
+ * `createUserClient(conn)`.
84
+ */
85
+ protected createServiceClient(): Base44Client;
86
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Type-only base class for Realtime Handlers.
3
+ *
4
+ * Import and extend this in your handler files:
5
+ * import { RealtimeHandler } from "@base44/sdk";
6
+ * export class MyHandler extends RealtimeHandler { ... }
7
+ *
8
+ * At deploy time the bundler replaces this import with the compiled
9
+ * Cloudflare Durable Object implementation — this file provides types only.
10
+ */
11
+ /**
12
+ * Base class for a Realtime Handler.
13
+ *
14
+ * @typeParam Incoming - messages this handler *receives* from clients
15
+ * (`handleMessage`'s `msg`) — the schema's `toServer` section.
16
+ * @typeParam Outgoing - messages this handler *sends* to clients
17
+ * (`conn.send`/`broadcast`) — the schema's `toClient` section.
18
+ *
19
+ * With a generated `schema.jsonc`, wire both from the registry so they can't drift
20
+ * from the client's types:
21
+ * ```ts
22
+ * type Reg = RealtimeHandlerRegistry["MyHandler"];
23
+ * class MyHandler extends RealtimeHandler<Reg["toServer"], Reg["toClient"]> { ... }
24
+ * ```
25
+ */
26
+ export class RealtimeHandler {
27
+ constructor() {
28
+ /**
29
+ * Managed ticker (opt-in). Override {@link shouldTick} and the platform runs
30
+ * {@link handleTick} on a timer of {@link tickIntervalMs} while it returns true,
31
+ * and stops (letting the Durable Object hibernate — no compute cost) when it
32
+ * returns false. The platform owns scheduling, rescheduling, self-heal, and
33
+ * error-safety — you don't call {@link startLoop}/{@link stopLoop}.
34
+ *
35
+ * Re-evaluated after every connect/message/close and on every tick, so keep it
36
+ * cheap and pure (no async, no side effects). Example: `return this.players >= 2`.
37
+ */
38
+ this.tickIntervalMs = 100;
39
+ }
40
+ onStart() { }
41
+ broadcast(_data) {
42
+ throw new Error("RealtimeHandler.broadcast() is only available inside a deployed handler");
43
+ }
44
+ getConnections() {
45
+ throw new Error("RealtimeHandler.getConnections() is only available inside a deployed handler");
46
+ }
47
+ startLoop(_ms) {
48
+ throw new Error("RealtimeHandler.startLoop() is only available inside a deployed handler");
49
+ }
50
+ stopLoop() {
51
+ throw new Error("RealtimeHandler.stopLoop() is only available inside a deployed handler");
52
+ }
53
+ get instanceId() {
54
+ throw new Error("RealtimeHandler.instanceId is only available inside a deployed handler");
55
+ }
56
+ get storage() {
57
+ throw new Error("RealtimeHandler.storage is only available inside a deployed handler");
58
+ }
59
+ /**
60
+ * SDK client acting **as the connected user** — every entity call respects
61
+ * the app's row-level security, evaluated as that user at call time. The
62
+ * default wherever a `conn` is in scope (connect/message/close).
63
+ *
64
+ * Throws if the connection carries no user credential (anonymous visitor,
65
+ * signed-out session, or an app SDK that predates in-band auth).
66
+ */
67
+ createUserClient(conn) {
68
+ void conn;
69
+ throw new Error("RealtimeHandler.createUserClient() is only available inside a deployed handler");
70
+ }
71
+ /**
72
+ * Service-role SDK client — bypasses RLS. For work with **no user in scope**
73
+ * (tick, alarm, onStart). Inside handleMessage/handleConnect prefer
74
+ * `createUserClient(conn)`.
75
+ */
76
+ createServiceClient() {
77
+ throw new Error("RealtimeHandler.createServiceClient() is only available inside a deployed handler");
78
+ }
79
+ }
@@ -1,37 +1,4 @@
1
1
  import { GetAccessTokenOptions, SaveAccessTokenOptions, RemoveAccessTokenOptions, GetLoginUrlOptions } from "./auth-utils.types.js";
2
- /**
3
- * Builds the cookie string that mirrors the access token so subsequent
4
- * document requests carry it to server-side rendering (SSR) code.
5
- *
6
- * @internal
7
- */
8
- export declare function buildAccessTokenCookie(token: string, { name, secure, }?: {
9
- name?: string;
10
- secure?: boolean;
11
- }): string;
12
- /**
13
- * Builds the cookie string that clears the mirrored access token cookie.
14
- *
15
- * @internal
16
- */
17
- export declare function buildClearAccessTokenCookie({ name, secure, }?: {
18
- name?: string;
19
- secure?: boolean;
20
- }): string;
21
- /**
22
- * Mirrors the access token into a cookie (in addition to localStorage) so
23
- * subsequent document requests carry the token to SSR. No-op outside a
24
- * browser environment.
25
- *
26
- * @internal
27
- */
28
- export declare function setAccessTokenCookie(token: string, name?: string): void;
29
- /**
30
- * Clears the mirrored access token cookie. No-op outside a browser environment.
31
- *
32
- * @internal
33
- */
34
- export declare function clearAccessTokenCookie(name?: string): void;
35
2
  /**
36
3
  * Retrieves an access token from URL parameters or local storage.
37
4
  *
@@ -1,65 +1,3 @@
1
- const ACCESS_TOKEN_COOKIE_ATTRIBUTES = "path=/; SameSite=Lax";
2
- /**
3
- * Builds the cookie string that mirrors the access token so subsequent
4
- * document requests carry it to server-side rendering (SSR) code.
5
- *
6
- * @internal
7
- */
8
- export function buildAccessTokenCookie(token, { name = "base44_access_token", secure = false, } = {}) {
9
- return `${name}=${encodeURIComponent(token)}; ${ACCESS_TOKEN_COOKIE_ATTRIBUTES}${secure ? "; Secure" : ""}`;
10
- }
11
- /**
12
- * Builds the cookie string that clears the mirrored access token cookie.
13
- *
14
- * @internal
15
- */
16
- export function buildClearAccessTokenCookie({ name = "base44_access_token", secure = false, } = {}) {
17
- return `${name}=; ${ACCESS_TOKEN_COOKIE_ATTRIBUTES}; Max-Age=0${secure ? "; Secure" : ""}`;
18
- }
19
- function isHttpsPage() {
20
- var _a;
21
- return (typeof window !== "undefined" && ((_a = window.location) === null || _a === void 0 ? void 0 : _a.protocol) === "https:");
22
- }
23
- /**
24
- * Mirrors the access token into a cookie (in addition to localStorage) so
25
- * subsequent document requests carry the token to SSR. No-op outside a
26
- * browser environment.
27
- *
28
- * @internal
29
- */
30
- export function setAccessTokenCookie(token, name) {
31
- if (typeof document === "undefined" || !token) {
32
- return;
33
- }
34
- try {
35
- document.cookie = buildAccessTokenCookie(token, {
36
- name,
37
- secure: isHttpsPage(),
38
- });
39
- }
40
- catch (e) {
41
- console.error("Error saving token cookie:", e);
42
- }
43
- }
44
- /**
45
- * Clears the mirrored access token cookie. No-op outside a browser environment.
46
- *
47
- * @internal
48
- */
49
- export function clearAccessTokenCookie(name) {
50
- if (typeof document === "undefined") {
51
- return;
52
- }
53
- try {
54
- document.cookie = buildClearAccessTokenCookie({
55
- name,
56
- secure: isHttpsPage(),
57
- });
58
- }
59
- catch (e) {
60
- console.error("Error removing token cookie:", e);
61
- }
62
- }
63
1
  /**
64
2
  * Retrieves an access token from URL parameters or local storage.
65
3
  *
@@ -174,8 +112,6 @@ export function saveAccessToken(token, options) {
174
112
  window.localStorage.setItem(storageKey, token);
175
113
  // Set "token" that is set by the built-in SDK of platform version 2
176
114
  window.localStorage.setItem("token", token);
177
- // Mirror the token into a cookie so document requests carry it to SSR
178
- setAccessTokenCookie(token, storageKey);
179
115
  return true;
180
116
  }
181
117
  catch (e) {
@@ -214,8 +150,6 @@ export function removeAccessToken(options) {
214
150
  }
215
151
  try {
216
152
  window.localStorage.removeItem(storageKey);
217
- // Clear the cookie that mirrors the token for SSR
218
- clearAccessTokenCookie(storageKey);
219
153
  return true;
220
154
  }
221
155
  catch (e) {
@@ -1,4 +1,3 @@
1
- import { AxiosRequestConfig } from "axios";
2
1
  import type { Base44ErrorJSON } from "./axios-client.types.js";
3
2
  /**
4
3
  * Custom error class for Base44 SDK errors.
@@ -91,12 +90,11 @@ export declare class Base44Error extends Error {
91
90
  * @returns Configured axios instance
92
91
  * @internal
93
92
  */
94
- export declare function createAxiosClient({ baseURL, headers, token, interceptResponses, onError, adapter, }: {
93
+ export declare function createAxiosClient({ baseURL, headers, token, interceptResponses, onError, }: {
95
94
  baseURL: string;
96
95
  headers?: Record<string, string>;
97
96
  token?: string;
98
97
  interceptResponses?: boolean;
99
98
  onError?: (error: Error) => void;
100
- adapter?: AxiosRequestConfig["adapter"];
101
99
  }): import("axios").AxiosInstance;
102
100
  export type { Base44ErrorJSON } from "./axios-client.types.js";
@@ -115,10 +115,9 @@ function safeErrorLog(prefix, error) {
115
115
  * @returns Configured axios instance
116
116
  * @internal
117
117
  */
118
- export function createAxiosClient({ baseURL, headers = {}, token, interceptResponses = true, onError, adapter, }) {
118
+ export function createAxiosClient({ baseURL, headers = {}, token, interceptResponses = true, onError, }) {
119
119
  const client = axios.create({
120
120
  baseURL,
121
- ...(adapter !== undefined ? { adapter } : {}),
122
121
  headers: {
123
122
  "Content-Type": "application/json",
124
123
  Accept: "application/json",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@base44-preview/sdk",
3
- "version": "0.8.35-pr.221.5933e35",
3
+ "version": "0.8.36-pr.212.0d64c77",
4
4
  "description": "JavaScript SDK for Base44 API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -27,6 +27,7 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "axios": "^1.17.0",
30
+ "partysocket": "^0.0.23",
30
31
  "socket.io-client": "^4.8.3",
31
32
  "uuid": "^13.0.2"
32
33
  },
package/dist/server.d.ts DELETED
@@ -1,111 +0,0 @@
1
- import type { Base44Client } from "./client.types.js";
2
- /**
3
- * Options for creating a server-side Base44 client with {@linkcode createServerClient | createServerClient()}.
4
- */
5
- export interface CreateServerClientOptions {
6
- /**
7
- * The incoming Fetch API `Request`.
8
- *
9
- * Used to resolve configuration from `Base44-*` headers, the user token from
10
- * the `Authorization` header, and the `base44_access_token` cookie set by the
11
- * browser SDK.
12
- */
13
- request: Request;
14
- /**
15
- * Environment variables record, such as a Cloudflare Worker `env` binding or
16
- * Node's `process.env`.
17
- *
18
- * Recognized variables: `BASE44_APP_ID`, `BASE44_API_URL`,
19
- * `BASE44_SERVICE_TOKEN`, and `BASE44_FUNCTIONS_VERSION`.
20
- */
21
- env?: Record<string, string | undefined>;
22
- /**
23
- * The Base44 app ID. Takes precedence over `env.BASE44_APP_ID` and the
24
- * `Base44-App-Id` request header.
25
- */
26
- appId?: string;
27
- /**
28
- * The Base44 server URL. Must be an absolute URL. Takes precedence over
29
- * `env.BASE44_API_URL` and the `Base44-Api-Url` request header.
30
- *
31
- * @defaultValue `"https://base44.app"`
32
- */
33
- serverUrl?: string;
34
- /**
35
- * User authentication token. Takes precedence over the request's
36
- * `Authorization: Bearer` header and the `base44_access_token` cookie.
37
- */
38
- token?: string;
39
- /**
40
- * Service role authentication token. Takes precedence over
41
- * `env.BASE44_SERVICE_TOKEN` and the `Base44-Service-Authorization` request
42
- * header.
43
- */
44
- serviceToken?: string;
45
- /**
46
- * Version string for the functions API. Takes precedence over
47
- * `env.BASE44_FUNCTIONS_VERSION` and the `Base44-Functions-Version` request
48
- * header.
49
- * @internal
50
- */
51
- functionsVersion?: string;
52
- }
53
- /**
54
- * Creates a Base44 client for server-side rendering (SSR) and edge runtimes.
55
- *
56
- * Use this function in Cloudflare Workers, framework loaders, and other
57
- * server environments that handle Fetch API requests. Unlike
58
- * {@linkcode createClient | createClient()}, the returned client is safe to
59
- * create per request outside a browser: analytics is fully disabled (no
60
- * background timers), HTTP requests use the `fetch` adapter, and no browser
61
- * storage is touched.
62
- *
63
- * Each configuration value is resolved in order from: the explicit option,
64
- * the `env` record (`BASE44_APP_ID`, `BASE44_API_URL`, `BASE44_SERVICE_TOKEN`,
65
- * `BASE44_FUNCTIONS_VERSION`), and finally the request headers — the same
66
- * `Base44-*` headers read by
67
- * {@linkcode createClientFromRequest | createClientFromRequest()}. The user
68
- * token is resolved from the explicit option, then the request's
69
- * `Authorization: Bearer` header, then the `base44_access_token` cookie that
70
- * the browser SDK mirrors from localStorage.
71
- *
72
- * @param options - Server client options, including the incoming request and optional environment record.
73
- * @returns A configured Base44 client instance scoped to the incoming request.
74
- * @throws {Error} When no app ID can be resolved from the options, environment, or request headers.
75
- * @throws {Error} When the resolved server URL isn't an absolute URL.
76
- *
77
- * @example
78
- * ```typescript
79
- * // Cloudflare Worker fetch handler
80
- * import { createServerClient } from '@base44/sdk';
81
- *
82
- * export default {
83
- * async fetch(request, env) {
84
- * const base44 = createServerClient({ request, env });
85
- *
86
- * // Reads data as the user identified by the request's cookie or
87
- * // Authorization header (anonymous when neither is present)
88
- * const products = await base44.entities.Products.list();
89
- *
90
- * return Response.json({ products });
91
- * }
92
- * };
93
- * ```
94
- *
95
- * @example
96
- * ```typescript
97
- * // Framework loader (React Router, Remix, and similar)
98
- * import { createServerClient } from '@base44/sdk';
99
- *
100
- * export async function loader({ request }) {
101
- * const base44 = createServerClient({
102
- * request,
103
- * appId: 'my-app-id'
104
- * });
105
- *
106
- * const user = await base44.auth.me().catch(() => null);
107
- * return { user };
108
- * }
109
- * ```
110
- */
111
- export declare function createServerClient(options: CreateServerClientOptions): Base44Client;
package/dist/server.js DELETED
@@ -1,157 +0,0 @@
1
- import { createClient } from "./client.js";
2
- const ACCESS_TOKEN_COOKIE_NAME = "base44_access_token";
3
- /**
4
- * Returns the first truthy value, treating empty strings the same as unset.
5
- */
6
- function firstNonEmpty(...values) {
7
- for (const value of values) {
8
- if (value) {
9
- return value;
10
- }
11
- }
12
- return undefined;
13
- }
14
- /**
15
- * Extracts the token from a `Bearer <token>` authorization header value.
16
- * Returns undefined when the header is missing or malformed.
17
- */
18
- function parseBearerToken(header) {
19
- if (!header) {
20
- return undefined;
21
- }
22
- const parts = header.split(" ");
23
- if (parts.length === 2 && parts[0] === "Bearer" && parts[1]) {
24
- return parts[1];
25
- }
26
- return undefined;
27
- }
28
- /**
29
- * Minimal cookie parser (no dependency): reads a single cookie value from a
30
- * `Cookie` request header, handling quoted values and URL-encoding.
31
- */
32
- function getCookieValue(cookieHeader, name) {
33
- if (!cookieHeader) {
34
- return undefined;
35
- }
36
- for (const part of cookieHeader.split(";")) {
37
- const separatorIndex = part.indexOf("=");
38
- if (separatorIndex === -1) {
39
- continue;
40
- }
41
- if (part.slice(0, separatorIndex).trim() !== name) {
42
- continue;
43
- }
44
- let value = part.slice(separatorIndex + 1).trim();
45
- if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
46
- value = value.slice(1, -1);
47
- }
48
- try {
49
- return decodeURIComponent(value);
50
- }
51
- catch (_a) {
52
- return value;
53
- }
54
- }
55
- return undefined;
56
- }
57
- function isAbsoluteUrl(url) {
58
- try {
59
- new URL(url);
60
- return true;
61
- }
62
- catch (_a) {
63
- return false;
64
- }
65
- }
66
- /**
67
- * Creates a Base44 client for server-side rendering (SSR) and edge runtimes.
68
- *
69
- * Use this function in Cloudflare Workers, framework loaders, and other
70
- * server environments that handle Fetch API requests. Unlike
71
- * {@linkcode createClient | createClient()}, the returned client is safe to
72
- * create per request outside a browser: analytics is fully disabled (no
73
- * background timers), HTTP requests use the `fetch` adapter, and no browser
74
- * storage is touched.
75
- *
76
- * Each configuration value is resolved in order from: the explicit option,
77
- * the `env` record (`BASE44_APP_ID`, `BASE44_API_URL`, `BASE44_SERVICE_TOKEN`,
78
- * `BASE44_FUNCTIONS_VERSION`), and finally the request headers — the same
79
- * `Base44-*` headers read by
80
- * {@linkcode createClientFromRequest | createClientFromRequest()}. The user
81
- * token is resolved from the explicit option, then the request's
82
- * `Authorization: Bearer` header, then the `base44_access_token` cookie that
83
- * the browser SDK mirrors from localStorage.
84
- *
85
- * @param options - Server client options, including the incoming request and optional environment record.
86
- * @returns A configured Base44 client instance scoped to the incoming request.
87
- * @throws {Error} When no app ID can be resolved from the options, environment, or request headers.
88
- * @throws {Error} When the resolved server URL isn't an absolute URL.
89
- *
90
- * @example
91
- * ```typescript
92
- * // Cloudflare Worker fetch handler
93
- * import { createServerClient } from '@base44/sdk';
94
- *
95
- * export default {
96
- * async fetch(request, env) {
97
- * const base44 = createServerClient({ request, env });
98
- *
99
- * // Reads data as the user identified by the request's cookie or
100
- * // Authorization header (anonymous when neither is present)
101
- * const products = await base44.entities.Products.list();
102
- *
103
- * return Response.json({ products });
104
- * }
105
- * };
106
- * ```
107
- *
108
- * @example
109
- * ```typescript
110
- * // Framework loader (React Router, Remix, and similar)
111
- * import { createServerClient } from '@base44/sdk';
112
- *
113
- * export async function loader({ request }) {
114
- * const base44 = createServerClient({
115
- * request,
116
- * appId: 'my-app-id'
117
- * });
118
- *
119
- * const user = await base44.auth.me().catch(() => null);
120
- * return { user };
121
- * }
122
- * ```
123
- */
124
- export function createServerClient(options) {
125
- var _a;
126
- const { request, env = {} } = options;
127
- const headers = request.headers;
128
- const appId = firstNonEmpty(options.appId, env.BASE44_APP_ID, headers.get("Base44-App-Id"));
129
- if (!appId) {
130
- throw new Error("createServerClient: unable to resolve an app ID. Pass appId explicitly, set the BASE44_APP_ID environment variable, or forward the Base44-App-Id request header.");
131
- }
132
- const serverUrl = (_a = firstNonEmpty(options.serverUrl, env.BASE44_API_URL, headers.get("Base44-Api-Url"))) !== null && _a !== void 0 ? _a : "https://base44.app";
133
- if (!isAbsoluteUrl(serverUrl)) {
134
- throw new Error(`createServerClient: serverUrl must be an absolute URL, got "${serverUrl}"`);
135
- }
136
- const token = firstNonEmpty(options.token, parseBearerToken(headers.get("Authorization")), getCookieValue(headers.get("Cookie"), ACCESS_TOKEN_COOKIE_NAME));
137
- const serviceToken = firstNonEmpty(options.serviceToken, env.BASE44_SERVICE_TOKEN, parseBearerToken(headers.get("Base44-Service-Authorization")));
138
- const functionsVersion = firstNonEmpty(options.functionsVersion, env.BASE44_FUNCTIONS_VERSION, headers.get("Base44-Functions-Version"));
139
- // Propagate Base44-State like createClientFromRequest, so the server client
140
- // degrades gracefully behind the existing Base44 proxy
141
- const stateHeader = headers.get("Base44-State");
142
- const additionalHeaders = {};
143
- if (stateHeader) {
144
- additionalHeaders["Base44-State"] = stateHeader;
145
- }
146
- return createClient({
147
- serverUrl,
148
- appId,
149
- token,
150
- serviceToken,
151
- functionsVersion,
152
- headers: additionalHeaders,
153
- requiresAuth: false,
154
- disableAnalytics: true,
155
- adapter: "fetch",
156
- });
157
- }