@base44-preview/sdk 0.8.30-pr.160.b403af6 → 0.8.31-pr.178.f25a7cd

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
@@ -9,6 +9,7 @@ import { createFunctionsModule } from "./modules/functions.js";
9
9
  import { createAgentsModule } from "./modules/agents.js";
10
10
  import { createAppLogsModule } from "./modules/app-logs.js";
11
11
  import { createUsersModule } from "./modules/users.js";
12
+ import { createAccountsModule } from "./modules/accounts.js";
12
13
  import { RoomsSocket } from "./utils/socket-utils.js";
13
14
  import { createAnalyticsModule } from "./modules/analytics.js";
14
15
  /**
@@ -83,12 +84,14 @@ export function createClient(config) {
83
84
  baseURL: `${serverUrl}/api`,
84
85
  headers,
85
86
  token,
87
+ appId: String(appId),
86
88
  onError: options === null || options === void 0 ? void 0 : options.onError,
87
89
  });
88
90
  const functionsAxiosClient = createAxiosClient({
89
91
  baseURL: `${serverUrl}/api`,
90
92
  headers: functionHeaders,
91
93
  token,
94
+ appId: String(appId),
92
95
  interceptResponses: false,
93
96
  onError: options === null || options === void 0 ? void 0 : options.onError,
94
97
  });
@@ -100,12 +103,14 @@ export function createClient(config) {
100
103
  baseURL: `${serverUrl}/api`,
101
104
  headers: serviceRoleHeaders,
102
105
  token: serviceToken,
106
+ appId: String(appId),
103
107
  onError: options === null || options === void 0 ? void 0 : options.onError,
104
108
  });
105
109
  const serviceRoleFunctionsAxiosClient = createAxiosClient({
106
110
  baseURL: `${serverUrl}/api`,
107
111
  headers: functionHeaders,
108
112
  token: serviceToken,
113
+ appId: String(appId),
109
114
  interceptResponses: false,
110
115
  });
111
116
  const userAuthModule = createAuthModule(axiosClient, functionsAxiosClient, appId, {
@@ -152,6 +157,7 @@ export function createClient(config) {
152
157
  }),
153
158
  appLogs: createAppLogsModule(axiosClient, appId),
154
159
  users: createUsersModule(axiosClient, appId),
160
+ accounts: createAccountsModule(axiosClient, appId),
155
161
  analytics: createAnalyticsModule({
156
162
  axiosClient,
157
163
  serverUrl,
@@ -7,6 +7,7 @@ import type { FunctionsModule } from "./modules/functions.types.js";
7
7
  import type { AgentsModule } from "./modules/agents.types.js";
8
8
  import type { AppLogsModule } from "./modules/app-logs.types.js";
9
9
  import type { AnalyticsModule } from "./modules/analytics.types.js";
10
+ import type { AccountsModule } from "./modules/accounts.types.js";
10
11
  /**
11
12
  * Options for creating a Base44 client.
12
13
  */
@@ -79,6 +80,8 @@ export interface CreateClientConfig {
79
80
  * Provides access to all SDK modules for interacting with the app.
80
81
  */
81
82
  export interface Base44Client {
83
+ /** {@link AccountsModule | Accounts module} for multi-tenancy (accounts, members, billing). */
84
+ accounts: AccountsModule;
82
85
  /** {@link AgentsModule | Agents module} for managing AI agent conversations. */
83
86
  agents: AgentsModule;
84
87
  /** {@link AnalyticsModule | Analytics module} for tracking custom events in your app. */
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@ export type { IntegrationsModule, IntegrationEndpointFunction, CoreIntegrations,
10
10
  export type { FunctionsModule, FunctionName, FunctionNameRegistry, } from "./modules/functions.types.js";
11
11
  export type { AgentsModule, AgentName, AgentNameRegistry, AgentConversation, AgentMessage, AgentMessageReasoning, AgentMessageToolCall, AgentMessageUsage, AgentMessageCustomContext, AgentMessageMetadata, CreateConversationParams, } from "./modules/agents.types.js";
12
12
  export type { AppLogsModule } from "./modules/app-logs.types.js";
13
+ export type { AccountsModule, Account, AccountMembership, AccountPlan, AccountRole, AssignableAccountRole, AccountStatus, AccountMembershipStatus, MyAccountsResponse, CheckoutSession, } from "./modules/accounts.types.js";
13
14
  export type { SsoModule, SsoAccessTokenResponse } from "./modules/sso.types.js";
14
15
  export type { ConnectorsModule, UserConnectorsModule, } from "./modules/connectors.types.js";
15
16
  export type { CustomIntegrationsModule, CustomIntegrationCallParams, CustomIntegrationCallResponse, } from "./modules/custom-integrations.types.js";
@@ -0,0 +1,11 @@
1
+ import { AxiosInstance } from "axios";
2
+ import type { AccountsModule } from "./accounts.types.js";
3
+ /**
4
+ * Creates the accounts module (multi-tenancy) for the Base44 SDK.
5
+ *
6
+ * @param axios - Axios instance (responses are unwrapped to data).
7
+ * @param appId - Application ID.
8
+ * @returns The accounts module.
9
+ * @internal
10
+ */
11
+ export declare function createAccountsModule(axios: AxiosInstance, appId: string): AccountsModule;
@@ -0,0 +1,64 @@
1
+ import { getStoredActiveAccountId, setStoredActiveAccountId, } from "../utils/common.js";
2
+ /**
3
+ * Creates the accounts module (multi-tenancy) for the Base44 SDK.
4
+ *
5
+ * @param axios - Axios instance (responses are unwrapped to data).
6
+ * @param appId - Application ID.
7
+ * @returns The accounts module.
8
+ * @internal
9
+ */
10
+ export function createAccountsModule(axios, appId) {
11
+ const base = `/apps/${appId}/accounts`;
12
+ const enc = encodeURIComponent;
13
+ return {
14
+ getActiveAccountId() {
15
+ return getStoredActiveAccountId(appId);
16
+ },
17
+ switchAccount(accountId) {
18
+ setStoredActiveAccountId(appId, accountId);
19
+ if (typeof window === "undefined")
20
+ return;
21
+ window.location.reload();
22
+ },
23
+ clearActiveAccount() {
24
+ setStoredActiveAccountId(appId, null);
25
+ },
26
+ async listMine() {
27
+ return axios.get(`${base}/me`);
28
+ },
29
+ async create(params) {
30
+ return axios.post(base, params);
31
+ },
32
+ async update(accountId, params) {
33
+ return axios.patch(`${base}/${accountId}`, params);
34
+ },
35
+ async listMembers(accountId) {
36
+ return axios.get(`${base}/${accountId}/members`);
37
+ },
38
+ async invite(accountId, email, role = "member") {
39
+ return axios.post(`${base}/${accountId}/invites`, { email, role });
40
+ },
41
+ async acceptInvite(accountId) {
42
+ return axios.post(`${base}/${accountId}/accept`, {});
43
+ },
44
+ async changeMemberRole(accountId, email, role) {
45
+ return axios.patch(`${base}/${accountId}/members/${enc(email)}/role`, {
46
+ role,
47
+ });
48
+ },
49
+ async removeMember(accountId, email) {
50
+ return axios.delete(`${base}/${accountId}/members/${enc(email)}`);
51
+ },
52
+ async transferOwnership(accountId, email) {
53
+ return axios.post(`${base}/${accountId}/transfer-ownership`, { email });
54
+ },
55
+ billing: {
56
+ async listPlans(accountId) {
57
+ return axios.get(`${base}/${accountId}/billing/plans`);
58
+ },
59
+ async startCheckout(accountId, params) {
60
+ return axios.post(`${base}/${accountId}/billing/checkout`, params);
61
+ },
62
+ },
63
+ };
64
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Types for the {@link AccountsModule | accounts} module (multi-tenancy).
3
+ *
4
+ * An Account groups the app's end-users into an isolated tenant (a company,
5
+ * team, or organization). Users join accounts via membership and act inside one
6
+ * active account at a time. Account-scoped entities are transparently isolated
7
+ * to the active account (carried by the `X-Active-Account-Id` header, read from
8
+ * stored client state in localStorage, keyed per app).
9
+ */
10
+ /** Account-management role. Distinct from the app's business roles. */
11
+ export type AccountRole = "owner" | "admin" | "member";
12
+ /** Assignable (non-owner) role used for invites/role changes. */
13
+ export type AssignableAccountRole = "admin" | "member";
14
+ export type AccountStatus = "active" | "suspended";
15
+ export type AccountMembershipStatus = "pending" | "active";
16
+ /** An account (tenant) within the app. */
17
+ export interface Account {
18
+ id: string;
19
+ app_id: string;
20
+ name: string;
21
+ status: AccountStatus;
22
+ plan_id?: string | null;
23
+ billing_status?: string;
24
+ /** The current user's role in this account (present on `listMine()` results). */
25
+ my_role?: AccountRole;
26
+ /** Builder-defined custom fields. */
27
+ data?: Record<string, unknown>;
28
+ created_date?: string;
29
+ }
30
+ /** The accounts the current user belongs to, plus the active one. */
31
+ export interface MyAccountsResponse {
32
+ accounts: Account[];
33
+ active_account_id: string | null;
34
+ }
35
+ /** A user's membership in an account. */
36
+ export interface AccountMembership {
37
+ id: string;
38
+ account_id: string;
39
+ email: string;
40
+ role: AccountRole;
41
+ status: AccountMembershipStatus;
42
+ }
43
+ /** A subscription plan/tier offered to accounts. */
44
+ export interface AccountPlan {
45
+ id: string;
46
+ name: string;
47
+ description?: string | null;
48
+ price_amount: number;
49
+ currency: string;
50
+ interval: "month" | "year";
51
+ is_active: boolean;
52
+ }
53
+ /** A provider checkout session. */
54
+ export interface CheckoutSession {
55
+ url: string;
56
+ session_id: string;
57
+ }
58
+ /**
59
+ * The accounts module — manage multi-tenancy ("Accounts") from inside the app.
60
+ *
61
+ * Access via `base44.accounts`. Available when the app has multi-tenancy enabled.
62
+ */
63
+ export interface AccountsModule {
64
+ /** The active account id, read from stored client state (or `undefined`). */
65
+ getActiveAccountId(): string | undefined;
66
+ /**
67
+ * Switch the active account by persisting it to stored client state and
68
+ * reloading the page so all data follows the new account.
69
+ * @param accountId - The account to switch to.
70
+ */
71
+ switchAccount(accountId: string): void;
72
+ /** Clear the stored active account (the backend falls back to the default). */
73
+ clearActiveAccount(): void;
74
+ /** List the accounts the current user belongs to, plus the active one. */
75
+ listMine(): Promise<MyAccountsResponse>;
76
+ /** Create a new account; the current user becomes its owner. */
77
+ create(params: {
78
+ name: string;
79
+ data?: Record<string, unknown>;
80
+ }): Promise<Account>;
81
+ /** Rename and/or update an account's custom fields (managers only). */
82
+ update(accountId: string, params: {
83
+ name?: string;
84
+ data?: Record<string, unknown>;
85
+ }): Promise<Account>;
86
+ /** List an account's members (any active member). */
87
+ listMembers(accountId: string): Promise<AccountMembership[]>;
88
+ /** Invite a user by email to an account (managers only). */
89
+ invite(accountId: string, email: string, role?: AssignableAccountRole): Promise<AccountMembership>;
90
+ /** Accept a pending invite to an account for the current user. */
91
+ acceptInvite(accountId: string): Promise<AccountMembership>;
92
+ /** Change a member's role (managers only; not for the owner). */
93
+ changeMemberRole(accountId: string, email: string, role: AssignableAccountRole): Promise<AccountMembership>;
94
+ /** Remove a member from an account (managers only; not the owner). */
95
+ removeMember(accountId: string, email: string): Promise<{
96
+ removed: boolean;
97
+ }>;
98
+ /** Transfer ownership to another active member (owner only). */
99
+ transferOwnership(accountId: string, email: string): Promise<{
100
+ transferred: boolean;
101
+ }>;
102
+ /** Per-account billing. */
103
+ billing: {
104
+ /** List the active plans available to this account. */
105
+ listPlans(accountId: string): Promise<AccountPlan[]>;
106
+ /** Start a subscription checkout session for a plan. */
107
+ startCheckout(accountId: string, params: {
108
+ plan_id: string;
109
+ success_url: string;
110
+ cancel_url: string;
111
+ }): Promise<CheckoutSession>;
112
+ };
113
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Types for the {@link AccountsModule | accounts} module (multi-tenancy).
3
+ *
4
+ * An Account groups the app's end-users into an isolated tenant (a company,
5
+ * team, or organization). Users join accounts via membership and act inside one
6
+ * active account at a time. Account-scoped entities are transparently isolated
7
+ * to the active account (carried by the `X-Active-Account-Id` header, read from
8
+ * stored client state in localStorage, keyed per app).
9
+ */
10
+ export {};
@@ -21,25 +21,24 @@ export function createConnectorsModule(axios, appId) {
21
21
  // @ts-expect-error
22
22
  return response.access_token;
23
23
  },
24
- async getConnection(arg) {
24
+ async getConnection(integrationType) {
25
25
  var _a;
26
- let url;
27
- if (typeof arg === "string") {
28
- if (!arg) {
29
- throw new Error("Integration type is required and must be a string");
30
- }
31
- url = `/apps/${appId}/external-auth/tokens/${arg}`;
32
- }
33
- else if (arg && typeof arg === "object" && typeof arg.connectorId === "string") {
34
- if (!arg.connectorId) {
35
- throw new Error("Connector ID is required and must be a string");
36
- }
37
- url = `/apps/${appId}/external-auth/tokens/by-connector/${arg.connectorId}`;
26
+ if (!integrationType || typeof integrationType !== "string") {
27
+ throw new Error("Integration type is required and must be a string");
38
28
  }
39
- else {
40
- throw new Error("getConnection requires either an integration type string or an object with a connectorId string");
29
+ const response = await axios.get(`/apps/${appId}/external-auth/tokens/${integrationType}`);
30
+ const data = response;
31
+ return {
32
+ accessToken: data.access_token,
33
+ connectionConfig: (_a = data.connection_config) !== null && _a !== void 0 ? _a : null,
34
+ };
35
+ },
36
+ async getWorkspaceConnection(connectorId) {
37
+ var _a;
38
+ if (!connectorId || typeof connectorId !== "string") {
39
+ throw new Error("Connector ID is required and must be a string");
41
40
  }
42
- const response = await axios.get(url);
41
+ const response = await axios.get(`/apps/${appId}/external-auth/tokens/connectors/${connectorId}`);
43
42
  const data = response;
44
43
  return {
45
44
  accessToken: data.access_token,
@@ -255,30 +255,28 @@ export interface ConnectorsModule {
255
255
  * Retrieves the OAuth access token and connection configuration for a **workspace-registered** connector
256
256
  * (a connector backed by an OAuth app registered in the workspace, consented to once by the app builder).
257
257
  *
258
- * Use this overload when the app's backend function needs to use a connector identified by its
258
+ * Use this method when the app's backend function needs to use a connector identified by its
259
259
  * workspace-connector ID rather than a platform integration type. The token returned represents
260
260
  * the app builder's consent against the workspace's OAuth app and is shared across all app users
261
- * of the app — identical semantics to the platform-shared `getConnection(integrationType)` form,
261
+ * of the app — identical semantics to the platform-shared {@link getConnection} form,
262
262
  * differing only in which OAuth app was used to produce the token.
263
263
  *
264
- * @param opts - An object with `connectorId` — the ID of the workspace connector (the `OrganizationConnector` database ID) as surfaced in the builder chat context.
264
+ * @param connectorId - The ID of the workspace connector (the `OrganizationConnector` database ID) as surfaced in the builder chat context.
265
265
  * @returns Promise resolving to a {@link ConnectorConnectionResponse} with `accessToken` and `connectionConfig`.
266
266
  *
267
267
  * @example
268
268
  * ```typescript
269
269
  * // Get the connection for a workspace-registered connector
270
- * const { accessToken, connectionConfig } = await base44.asServiceRole.connectors.getConnection({
271
- * connectorId: 'abc123def',
272
- * });
270
+ * const { accessToken, connectionConfig } = await base44.asServiceRole.connectors.getWorkspaceConnection(
271
+ * 'abc123def',
272
+ * );
273
273
  *
274
274
  * const response = await fetch(`https://${connectionConfig?.subdomain}.snowflakecomputing.com/api/v2/statements`, {
275
275
  * headers: { Authorization: `Bearer ${accessToken}` },
276
276
  * });
277
277
  * ```
278
278
  */
279
- getConnection(opts: {
280
- connectorId: string;
281
- }): Promise<ConnectorConnectionResponse>;
279
+ getWorkspaceConnection(connectorId: string): Promise<ConnectorConnectionResponse>;
282
280
  /**
283
281
  * @internal
284
282
  * @deprecated Use {@link getCurrentAppUserConnection} instead.
@@ -90,10 +90,11 @@ export declare class Base44Error extends Error {
90
90
  * @returns Configured axios instance
91
91
  * @internal
92
92
  */
93
- export declare function createAxiosClient({ baseURL, headers, token, interceptResponses, onError, }: {
93
+ export declare function createAxiosClient({ baseURL, headers, token, appId, interceptResponses, onError, }: {
94
94
  baseURL: string;
95
95
  headers?: Record<string, string>;
96
96
  token?: string;
97
+ appId?: string;
97
98
  interceptResponses?: boolean;
98
99
  onError?: (error: Error) => void;
99
100
  }): import("axios").AxiosInstance;
@@ -1,5 +1,5 @@
1
1
  import axios from "axios";
2
- import { isInIFrame } from "./common.js";
2
+ import { getStoredActiveAccountId, isInIFrame } from "./common.js";
3
3
  import { v4 as uuidv4 } from "uuid";
4
4
  /**
5
5
  * Custom error class for Base44 SDK errors.
@@ -114,7 +114,7 @@ function safeErrorLog(prefix, error) {
114
114
  * @returns Configured axios instance
115
115
  * @internal
116
116
  */
117
- export function createAxiosClient({ baseURL, headers = {}, token, interceptResponses = true, onError, }) {
117
+ export function createAxiosClient({ baseURL, headers = {}, token, appId, interceptResponses = true, onError, }) {
118
118
  const client = axios.create({
119
119
  baseURL,
120
120
  headers: {
@@ -131,6 +131,16 @@ export function createAxiosClient({ baseURL, headers = {}, token, interceptRespo
131
131
  client.interceptors.request.use((config) => {
132
132
  if (typeof window !== "undefined") {
133
133
  config.headers.set("X-Origin-URL", window.location.href);
134
+ // Multi-tenancy: forward the active account from stored client state
135
+ // (localStorage, keyed per app) on every request so account-scoped
136
+ // reads/writes stay isolated to the current tenant. No-op when unset —
137
+ // the backend then defaults to the user's sole active account.
138
+ const activeAccountId = appId
139
+ ? getStoredActiveAccountId(appId)
140
+ : undefined;
141
+ if (activeAccountId) {
142
+ config.headers.set("X-Active-Account-Id", activeAccountId);
143
+ }
134
144
  }
135
145
  const requestId = uuidv4();
136
146
  config.requestId = requestId;
@@ -1,3 +1,19 @@
1
1
  export declare const isNode: boolean;
2
2
  export declare const isInIFrame: boolean;
3
+ /**
4
+ * The active account id from stored client state, or undefined.
5
+ *
6
+ * Browser-only: reads `localStorage['base44:active_account:<appId>']` and returns
7
+ * it when it's a valid 24-hex account id, else undefined. Returns undefined in
8
+ * non-browser environments or if storage access throws.
9
+ */
10
+ export declare function getStoredActiveAccountId(appId: string): string | undefined;
11
+ /**
12
+ * Persist (or clear) the active account id in stored client state.
13
+ *
14
+ * Browser-only: writes `localStorage['base44:active_account:<appId>']` when
15
+ * `accountId` is a valid 24-hex id, and removes the key when it is null or
16
+ * invalid. No-op in non-browser environments or if storage access throws.
17
+ */
18
+ export declare function setStoredActiveAccountId(appId: string, accountId: string | null): void;
3
19
  export declare const generateUuid: () => string;
@@ -1,5 +1,53 @@
1
1
  export const isNode = typeof window === "undefined";
2
2
  export const isInIFrame = !isNode && window.self !== window.top;
3
+ // Multi-tenancy: the active account is explicit client state, not the URL path.
4
+ // It is persisted in localStorage keyed per app (`base44:active_account:<appId>`)
5
+ // so it survives reloads and works under any base path (e.g. the sandbox/preview
6
+ // where the app is served under a non-account base path). When unset, no header
7
+ // is sent and the backend defaults to the user's sole active account.
8
+ const ACCOUNT_ID_RE = /^[a-f0-9]{24}$/;
9
+ const activeAccountStorageKey = (appId) => `base44:active_account:${appId}`;
10
+ /**
11
+ * The active account id from stored client state, or undefined.
12
+ *
13
+ * Browser-only: reads `localStorage['base44:active_account:<appId>']` and returns
14
+ * it when it's a valid 24-hex account id, else undefined. Returns undefined in
15
+ * non-browser environments or if storage access throws.
16
+ */
17
+ export function getStoredActiveAccountId(appId) {
18
+ if (isNode)
19
+ return undefined;
20
+ try {
21
+ const stored = window.localStorage.getItem(activeAccountStorageKey(appId));
22
+ return stored && ACCOUNT_ID_RE.test(stored) ? stored : undefined;
23
+ }
24
+ catch (_a) {
25
+ return undefined;
26
+ }
27
+ }
28
+ /**
29
+ * Persist (or clear) the active account id in stored client state.
30
+ *
31
+ * Browser-only: writes `localStorage['base44:active_account:<appId>']` when
32
+ * `accountId` is a valid 24-hex id, and removes the key when it is null or
33
+ * invalid. No-op in non-browser environments or if storage access throws.
34
+ */
35
+ export function setStoredActiveAccountId(appId, accountId) {
36
+ if (isNode)
37
+ return;
38
+ try {
39
+ const key = activeAccountStorageKey(appId);
40
+ if (accountId && ACCOUNT_ID_RE.test(accountId)) {
41
+ window.localStorage.setItem(key, accountId);
42
+ }
43
+ else {
44
+ window.localStorage.removeItem(key);
45
+ }
46
+ }
47
+ catch (_a) {
48
+ /* storage unavailable — ignore */
49
+ }
50
+ }
3
51
  export const generateUuid = () => {
4
52
  return (Math.random().toString(36).substring(2, 15) +
5
53
  Math.random().toString(36).substring(2, 15));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@base44-preview/sdk",
3
- "version": "0.8.30-pr.160.b403af6",
3
+ "version": "0.8.31-pr.178.f25a7cd",
4
4
  "description": "JavaScript SDK for Base44 API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",