@base44-preview/sdk 0.8.48-pr.282.c69fdcd → 0.8.48-pr.283.2de4c8a
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/README.md +18 -0
- package/dist/client.js +21 -71
- package/dist/client.types.d.ts +1 -2
- package/dist/modules/agents.d.ts +1 -1
- package/dist/modules/agents.js +4 -3
- package/dist/modules/agents.types.d.ts +2 -2
- package/dist/modules/ai-gateway.d.ts +1 -1
- package/dist/modules/ai-gateway.js +3 -2
- package/dist/modules/ai-gateway.types.d.ts +2 -2
- package/dist/modules/auth.js +4 -17
- package/dist/modules/auth.types.d.ts +0 -26
- package/dist/platform/server/client.d.ts +29 -0
- package/dist/platform/server/client.js +57 -0
- package/dist/platform/server/errors.d.ts +28 -0
- package/dist/platform/server/errors.js +20 -0
- package/dist/platform/server/index.d.ts +9 -0
- package/dist/platform/server/index.js +4 -0
- package/dist/platform/server/modules/apps.d.ts +4 -0
- package/dist/platform/server/modules/apps.js +66 -0
- package/dist/platform/server/modules/apps.types.d.ts +87 -0
- package/dist/platform/server/modules/apps.types.js +1 -0
- package/dist/platform/server/modules/users.d.ts +4 -0
- package/dist/platform/server/modules/users.js +40 -0
- package/dist/platform/server/modules/users.types.d.ts +33 -0
- package/dist/platform/server/modules/users.types.js +1 -0
- package/dist/platform/server/token-store.d.ts +11 -0
- package/dist/platform/server/token-store.js +17 -0
- package/dist/platform/server/tokens.d.ts +16 -0
- package/dist/platform/server/tokens.js +80 -0
- package/dist/platform/server/transport.d.ts +10 -0
- package/dist/platform/server/transport.js +69 -0
- package/dist/platform/server/types.d.ts +60 -0
- package/dist/platform/server/types.js +1 -0
- package/dist/platform/server/validation.d.ts +9 -0
- package/dist/platform/server/validation.js +36 -0
- package/dist/utils/auth-utils.d.ts +0 -2
- package/dist/utils/auth-utils.js +0 -11
- package/dist/utils/socket-utils.d.ts +1 -3
- package/dist/utils/socket-utils.js +2 -1
- package/package.json +41 -5
- package/dist/utils/embed-session.d.ts +0 -50
- package/dist/utils/embed-session.js +0 -140
package/README.md
CHANGED
|
@@ -7,6 +7,24 @@ You can use it in two ways:
|
|
|
7
7
|
- **Inside Base44 apps**: When Base44 generates your app, the SDK is already set up and ready to use.
|
|
8
8
|
- **External apps**: Use the SDK to build your own frontend or backend that uses Base44 as a backend service.
|
|
9
9
|
|
|
10
|
+
## Platform SDK (server)
|
|
11
|
+
|
|
12
|
+
The same package includes a separate server entry point for provisioning users and
|
|
13
|
+
managing their apps:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { Base44PlatformClient } from "@base44/sdk/platform/server";
|
|
17
|
+
|
|
18
|
+
const platform = new Base44PlatformClient({ apiKey, workspaceId });
|
|
19
|
+
await platform.users.provision({ externalId: "customer_42" });
|
|
20
|
+
const apps = await platform.asUser("customer_42").apps.list();
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Use this entry point only on your server. `tokenStore` is optional and defaults to
|
|
24
|
+
in-memory caching. See the [platform guide](platform-docs/README.md),
|
|
25
|
+
[complete API reference](platform-docs/api.md), and [token lifecycle](platform-docs/tokens.md).
|
|
26
|
+
The existing `@base44/sdk` root import continues to provide the runtime SDK.
|
|
27
|
+
|
|
10
28
|
## Installation
|
|
11
29
|
|
|
12
30
|
**Inside Base44 apps**: The SDK is already available. No installation needed.
|
package/dist/client.js
CHANGED
|
@@ -5,7 +5,6 @@ import { createAuthModule } from "./modules/auth.js";
|
|
|
5
5
|
import { createSsoModule } from "./modules/sso.js";
|
|
6
6
|
import { createConnectorsModule, createUserConnectorsModule, } from "./modules/connectors.js";
|
|
7
7
|
import { getAccessToken } from "./utils/auth-utils.js";
|
|
8
|
-
import { exchangeEmbedToken, isEmbeddedTab, markEmbeddedTab, takeEmbedTokenFromUrl, } from "./utils/embed-session.js";
|
|
9
8
|
import { createFetchWithAuth } from "./utils/fetch-with-auth.js";
|
|
10
9
|
import { createFunctionsModule } from "./modules/functions.js";
|
|
11
10
|
import { createAgentsModule } from "./modules/agents.js";
|
|
@@ -55,26 +54,15 @@ import { createActorsModule, resolveActorsHost, } from "./modules/actors.js";
|
|
|
55
54
|
*/
|
|
56
55
|
export function createClient(config) {
|
|
57
56
|
var _a, _b, _c, _d;
|
|
58
|
-
const { serverUrl = "https://base44.app", appId, analytics, serviceToken, requiresAuth = false, appBaseUrl, options, functionsVersion, headers: optionalHeaders, } = config;
|
|
57
|
+
const { serverUrl = "https://base44.app", appId, analytics, token, serviceToken, requiresAuth = false, appBaseUrl, options, functionsVersion, headers: optionalHeaders, } = config;
|
|
59
58
|
// Normalize appBaseUrl to always be a string (empty if not provided or invalid)
|
|
60
59
|
const normalizedAppBaseUrl = typeof appBaseUrl === "string" ? appBaseUrl : "";
|
|
61
|
-
const embedOtt = takeEmbedTokenFromUrl();
|
|
62
|
-
if (embedOtt) {
|
|
63
|
-
markEmbeddedTab();
|
|
64
|
-
}
|
|
65
|
-
const embedded = Boolean(embedOtt) || isEmbeddedTab();
|
|
66
|
-
// What an app passes as `token` is whatever getAccessToken() returned as the
|
|
67
|
-
// page loaded. In an embedded frame that is the one-time token itself, or a
|
|
68
|
-
// token an earlier visitor left in storage; neither is this session.
|
|
69
|
-
const token = embedded ? undefined : config.token;
|
|
70
60
|
const socketConfig = {
|
|
71
61
|
serverUrl,
|
|
72
62
|
mountPath: "/ws-user-apps/socket.io/",
|
|
73
63
|
transports: ["websocket"],
|
|
74
64
|
appId,
|
|
75
|
-
|
|
76
|
-
// platform minted, so the socket must not fall back to a stored token.
|
|
77
|
-
token: embedded ? null : token,
|
|
65
|
+
token,
|
|
78
66
|
};
|
|
79
67
|
let socket = null;
|
|
80
68
|
const getSocket = () => {
|
|
@@ -137,66 +125,26 @@ export function createClient(config) {
|
|
|
137
125
|
appBaseUrl: normalizedAppBaseUrl,
|
|
138
126
|
serverUrl,
|
|
139
127
|
token,
|
|
140
|
-
embedded,
|
|
141
128
|
});
|
|
142
129
|
// Apply the access token before any module that may issue authenticated
|
|
143
130
|
// requests during construction (notably analytics, which fires an init
|
|
144
131
|
// event whose flush calls auth.me()). Without this, the first User/me
|
|
145
132
|
// request is built before setToken runs and goes out unauthenticated.
|
|
146
|
-
|
|
147
|
-
// and a token an earlier visitor left in storage must not become it.
|
|
148
|
-
if (typeof window !== "undefined" && !embedded) {
|
|
133
|
+
if (typeof window !== "undefined") {
|
|
149
134
|
const accessToken = token || getAccessToken();
|
|
150
135
|
if (accessToken) {
|
|
151
136
|
userAuthModule.setToken(accessToken);
|
|
152
137
|
}
|
|
153
138
|
}
|
|
154
|
-
// The one answer every module reads. Follows setToken and logout; outside an
|
|
155
|
-
// embedded frame it still falls back to storage, so a login in another tab is
|
|
156
|
-
// picked up on the next connection, as before.
|
|
157
|
-
const getToken = () => { var _a; return (_a = userAuthModule.getToken()) !== null && _a !== void 0 ? _a : (embedded ? null : getAccessToken()); };
|
|
158
|
-
const applyToken = (newToken, saveToStorage) => {
|
|
159
|
-
userAuthModule.setToken(newToken, saveToStorage);
|
|
160
|
-
socketConfig.token = newToken;
|
|
161
|
-
if (socket) {
|
|
162
|
-
socket.updateConfig({ token: newToken });
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
// Resolves once the embedded session is in hand (or known not to be coming);
|
|
166
|
-
// immediately for every other client. The exchanged token is applied without
|
|
167
|
-
// persisting it: the session must not outlive the frame it was minted for.
|
|
168
|
-
const authReady = embedOtt
|
|
169
|
-
? exchangeEmbedToken({ serverUrl, appId, ott: embedOtt }).then((sessionToken) => {
|
|
170
|
-
if (sessionToken) {
|
|
171
|
-
applyToken(sessionToken, false);
|
|
172
|
-
}
|
|
173
|
-
})
|
|
174
|
-
: Promise.resolve();
|
|
175
|
-
if (embedOtt) {
|
|
176
|
-
// Requests issued while the exchange is in flight wait for it and carry the
|
|
177
|
-
// session it produces. Registered after createAxiosClient's interceptors,
|
|
178
|
-
// so it runs first and the anonymous-visitor header sees the Authorization.
|
|
179
|
-
for (const client of [axiosClient, functionsAxiosClient]) {
|
|
180
|
-
client.interceptors.request.use(async (requestConfig) => {
|
|
181
|
-
await authReady;
|
|
182
|
-
const sessionToken = getToken();
|
|
183
|
-
if (sessionToken && !requestConfig.headers.get("Authorization")) {
|
|
184
|
-
requestConfig.headers.set("Authorization", `Bearer ${sessionToken}`);
|
|
185
|
-
}
|
|
186
|
-
return requestConfig;
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
139
|
const actorsModule = createActorsModule({
|
|
191
140
|
appId,
|
|
192
141
|
// serverUrl is often relative/empty (same-origin app); the proxy-fallback
|
|
193
142
|
// URL needs an absolute host, so fall back to the page origin.
|
|
194
143
|
host: resolveActorsHost(serverUrl, typeof window !== "undefined" ? (_a = window.location) === null || _a === void 0 ? void 0 : _a.origin : undefined),
|
|
195
144
|
functionsVersion,
|
|
196
|
-
getAuthToken:
|
|
145
|
+
getAuthToken: () => token || getAccessToken(),
|
|
197
146
|
mintConnectionToken: async (actorName, room, connectionId) => {
|
|
198
|
-
|
|
199
|
-
const authToken = getToken();
|
|
147
|
+
const authToken = token || getAccessToken();
|
|
200
148
|
return await actorsAxiosClient.post(`/apps/${appId}/actors/${encodeURIComponent(actorName)}/connection-token`, { room, connection_id: connectionId }, {
|
|
201
149
|
headers: {
|
|
202
150
|
...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),
|
|
@@ -223,9 +171,10 @@ export function createClient(config) {
|
|
|
223
171
|
functions: createFunctionsModule(functionsAxiosClient, appId, {
|
|
224
172
|
getAuthHeaders: () => {
|
|
225
173
|
const headers = {};
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
174
|
+
// Get current token from storage or initial config
|
|
175
|
+
const currentToken = token || getAccessToken();
|
|
176
|
+
if (currentToken) {
|
|
177
|
+
headers["Authorization"] = `Bearer ${currentToken}`;
|
|
229
178
|
}
|
|
230
179
|
return headers;
|
|
231
180
|
},
|
|
@@ -236,9 +185,9 @@ export function createClient(config) {
|
|
|
236
185
|
getSocket,
|
|
237
186
|
appId,
|
|
238
187
|
serverUrl,
|
|
239
|
-
|
|
188
|
+
token,
|
|
240
189
|
}),
|
|
241
|
-
aiGateway: createAiGatewayModule({ serverUrl,
|
|
190
|
+
aiGateway: createAiGatewayModule({ serverUrl, token, appId }),
|
|
242
191
|
appLogs: createAppLogsModule(axiosClient, appId),
|
|
243
192
|
app: createAppModule(axiosClient, appId),
|
|
244
193
|
users: createUsersModule(axiosClient, appId),
|
|
@@ -283,13 +232,9 @@ export function createClient(config) {
|
|
|
283
232
|
getSocket,
|
|
284
233
|
appId,
|
|
285
234
|
serverUrl,
|
|
286
|
-
|
|
287
|
-
}),
|
|
288
|
-
aiGateway: createAiGatewayModule({
|
|
289
|
-
serverUrl,
|
|
290
|
-
getToken: () => serviceToken,
|
|
291
|
-
appId,
|
|
235
|
+
token,
|
|
292
236
|
}),
|
|
237
|
+
aiGateway: createAiGatewayModule({ serverUrl, token: serviceToken, appId }),
|
|
293
238
|
appLogs: createAppLogsModule(serviceRoleAxiosClient, appId),
|
|
294
239
|
cleanup: () => {
|
|
295
240
|
if (socket) {
|
|
@@ -329,7 +274,6 @@ export function createClient(config) {
|
|
|
329
274
|
* Sets a new authentication token for all subsequent requests.
|
|
330
275
|
*
|
|
331
276
|
* @param newToken - The new authentication token
|
|
332
|
-
* @param saveToStorage - Whether to also keep the token in local storage. Defaults to `true`.
|
|
333
277
|
*
|
|
334
278
|
* @example
|
|
335
279
|
* ```typescript
|
|
@@ -341,8 +285,14 @@ export function createClient(config) {
|
|
|
341
285
|
* base44.setToken(access_token);
|
|
342
286
|
* ```
|
|
343
287
|
*/
|
|
344
|
-
setToken(newToken
|
|
345
|
-
|
|
288
|
+
setToken(newToken) {
|
|
289
|
+
userModules.auth.setToken(newToken);
|
|
290
|
+
if (socket) {
|
|
291
|
+
socket.updateConfig({
|
|
292
|
+
token: newToken,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
socketConfig.token = newToken;
|
|
346
296
|
},
|
|
347
297
|
/**
|
|
348
298
|
* Gets the current client configuration.
|
package/dist/client.types.d.ts
CHANGED
|
@@ -200,9 +200,8 @@ export interface Base44Client {
|
|
|
200
200
|
* Updates the token for both HTTP requests and WebSocket connections.
|
|
201
201
|
*
|
|
202
202
|
* @param newToken - The new authentication token.
|
|
203
|
-
* @param saveToStorage - Whether to also keep the token in local storage so it survives a page load. Defaults to `true`.
|
|
204
203
|
*/
|
|
205
|
-
setToken(newToken: string
|
|
204
|
+
setToken(newToken: string): void;
|
|
206
205
|
/**
|
|
207
206
|
* Gets the current client configuration.
|
|
208
207
|
* @internal
|
package/dist/modules/agents.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { AgentsModule, AgentsModuleConfig } from "./agents.types.js";
|
|
2
|
-
export declare function createAgentsModule({ axios, getSocket, appId, serverUrl,
|
|
2
|
+
export declare function createAgentsModule({ axios, getSocket, appId, serverUrl, token, }: AgentsModuleConfig): AgentsModule;
|
package/dist/modules/agents.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { getAccessToken } from "../utils/auth-utils.js";
|
|
2
|
+
export function createAgentsModule({ axios, getSocket, appId, serverUrl, token, }) {
|
|
2
3
|
const baseURL = `/apps/${appId}/agents`;
|
|
3
4
|
// Track active conversations
|
|
4
5
|
const currentConversations = {};
|
|
@@ -55,7 +56,7 @@ export function createAgentsModule({ axios, getSocket, appId, serverUrl, getToke
|
|
|
55
56
|
};
|
|
56
57
|
const getWhatsAppConnectURL = (agentName) => {
|
|
57
58
|
const baseUrl = `${serverUrl}/api/apps/${appId}/agents/${encodeURIComponent(agentName)}/whatsapp`;
|
|
58
|
-
const accessToken =
|
|
59
|
+
const accessToken = token !== null && token !== void 0 ? token : getAccessToken();
|
|
59
60
|
if (accessToken) {
|
|
60
61
|
return `${baseUrl}?token=${accessToken}`;
|
|
61
62
|
}
|
|
@@ -66,7 +67,7 @@ export function createAgentsModule({ axios, getSocket, appId, serverUrl, getToke
|
|
|
66
67
|
};
|
|
67
68
|
const getTelegramConnectURL = (agentName) => {
|
|
68
69
|
const baseUrl = `${serverUrl}/api/apps/${appId}/agents/${encodeURIComponent(agentName)}/telegram`;
|
|
69
|
-
const accessToken =
|
|
70
|
+
const accessToken = token !== null && token !== void 0 ? token : getAccessToken();
|
|
70
71
|
if (accessToken) {
|
|
71
72
|
return `${baseUrl}?token=${accessToken}`;
|
|
72
73
|
}
|
|
@@ -160,8 +160,8 @@ export interface AgentsModuleConfig {
|
|
|
160
160
|
appId: string;
|
|
161
161
|
/** Server URL */
|
|
162
162
|
serverUrl?: string;
|
|
163
|
-
/**
|
|
164
|
-
|
|
163
|
+
/** Authentication token */
|
|
164
|
+
token?: string;
|
|
165
165
|
}
|
|
166
166
|
/**
|
|
167
167
|
* Agents module for managing AI agent conversations.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { AiGatewayModule, AiGatewayModuleConfig } from "./ai-gateway.types.js";
|
|
2
|
-
export declare function createAiGatewayModule({ serverUrl,
|
|
2
|
+
export declare function createAiGatewayModule({ serverUrl, token, appId, }: AiGatewayModuleConfig): AiGatewayModule;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import { getAccessToken } from "../utils/auth-utils.js";
|
|
2
|
+
export function createAiGatewayModule({ serverUrl, token, appId, }) {
|
|
2
3
|
const connection = () => {
|
|
3
4
|
var _a;
|
|
4
5
|
return ({
|
|
5
6
|
baseURL: `${serverUrl}/api/apps/${appId}/ai/openai/v1`,
|
|
6
|
-
token: (_a =
|
|
7
|
+
token: (_a = token !== null && token !== void 0 ? token : getAccessToken()) !== null && _a !== void 0 ? _a : "",
|
|
7
8
|
});
|
|
8
9
|
};
|
|
9
10
|
return {
|
|
@@ -17,8 +17,8 @@ export interface AiGatewayConnection {
|
|
|
17
17
|
export interface AiGatewayModuleConfig {
|
|
18
18
|
/** Server URL */
|
|
19
19
|
serverUrl?: string;
|
|
20
|
-
/**
|
|
21
|
-
|
|
20
|
+
/** Authentication token */
|
|
21
|
+
token?: string;
|
|
22
22
|
/** Application ID */
|
|
23
23
|
appId: string;
|
|
24
24
|
}
|
package/dist/modules/auth.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { resetAnalyticsSessionContext } from "./analytics.js";
|
|
2
|
-
import { showEmbedSessionEnded } from "../utils/embed-session.js";
|
|
3
2
|
function isInsideIframe() {
|
|
4
3
|
if (typeof window === "undefined")
|
|
5
4
|
return false;
|
|
@@ -82,16 +81,10 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
|
|
|
82
81
|
// Tracked here rather than read off `axios.defaults` so the answer stays tied
|
|
83
82
|
// to the identity transitions below (`setToken`, `logout`) instead of to the
|
|
84
83
|
// header a caller may have set on the instance directly.
|
|
85
|
-
let
|
|
84
|
+
let hasAccessToken = Boolean(options.token);
|
|
86
85
|
return {
|
|
87
86
|
hasToken() {
|
|
88
|
-
return
|
|
89
|
-
},
|
|
90
|
-
getToken() {
|
|
91
|
-
return accessToken;
|
|
92
|
-
},
|
|
93
|
-
isEmbedded() {
|
|
94
|
-
return Boolean(options.embedded);
|
|
87
|
+
return hasAccessToken;
|
|
95
88
|
},
|
|
96
89
|
// Get current user information
|
|
97
90
|
async me() {
|
|
@@ -116,12 +109,6 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
|
|
|
116
109
|
if (typeof window === "undefined") {
|
|
117
110
|
throw new Error("Login method can only be used in a browser environment");
|
|
118
111
|
}
|
|
119
|
-
// An embedded session can only be renewed by the host platform; the
|
|
120
|
-
// login round trip cannot complete inside its frame.
|
|
121
|
-
if (options.embedded) {
|
|
122
|
-
showEmbedSessionEnded();
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
112
|
// If nextUrl is not provided, use the current URL
|
|
126
113
|
const redirectUrl = nextUrl
|
|
127
114
|
? new URL(nextUrl, window.location.origin).toString()
|
|
@@ -164,7 +151,7 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
|
|
|
164
151
|
// flight would otherwise resolve into callers that run after the logout.
|
|
165
152
|
clearPendingMe();
|
|
166
153
|
resetAnalyticsSessionContext();
|
|
167
|
-
|
|
154
|
+
hasAccessToken = false;
|
|
168
155
|
// Only do the rest if in a browser environment
|
|
169
156
|
if (typeof window !== "undefined") {
|
|
170
157
|
// Remove token from localStorage
|
|
@@ -193,7 +180,7 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
|
|
|
193
180
|
// resolved for the previous one must not be handed to later callers.
|
|
194
181
|
clearPendingMe();
|
|
195
182
|
resetAnalyticsSessionContext();
|
|
196
|
-
|
|
183
|
+
hasAccessToken = true;
|
|
197
184
|
// handle token change for axios clients
|
|
198
185
|
axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
|
|
199
186
|
functionsAxiosClient.defaults.headers.common["Authorization"] = `Bearer ${token}`;
|
|
@@ -98,11 +98,6 @@ export interface AuthModuleOptions {
|
|
|
98
98
|
* which is how the server-side SDK reports a token it never sets explicitly.
|
|
99
99
|
*/
|
|
100
100
|
token?: string;
|
|
101
|
-
/**
|
|
102
|
-
* Whether this client runs embedded in a host platform's frame, where a
|
|
103
|
-
* session is minted by the platform and cannot be renewed by a login redirect.
|
|
104
|
-
*/
|
|
105
|
-
embedded?: boolean;
|
|
106
101
|
}
|
|
107
102
|
/**
|
|
108
103
|
* Authentication module for managing user authentication and authorization. The module automatically stores tokens in local storage when available and manages authorization headers for API requests.
|
|
@@ -184,22 +179,6 @@ export interface AuthModule {
|
|
|
184
179
|
* ```
|
|
185
180
|
*/
|
|
186
181
|
redirectToLogin(nextUrl: string): void;
|
|
187
|
-
/**
|
|
188
|
-
* Whether the app is running embedded in a host platform.
|
|
189
|
-
*
|
|
190
|
-
* A platform embeds an app in an iframe and signs its user in by minting a one-time token onto the frame's URL; the SDK trades it for a session as the client is created. That session is held in memory only and can be renewed only by the platform, so when it ends there is no login page to send the user to — {@linkcode AuthModule.redirectToLogin | redirectToLogin()} shows a "session ended" notice instead. Use this to render your own notice or to hide sign-in and sign-out controls that cannot work inside the frame.
|
|
191
|
-
*
|
|
192
|
-
* @returns `true` when the app was embedded by a host platform, `false` otherwise.
|
|
193
|
-
*
|
|
194
|
-
* @example
|
|
195
|
-
* ```typescript
|
|
196
|
-
* // Show your own message instead of a login screen
|
|
197
|
-
* if (!user && base44.auth.isEmbedded()) {
|
|
198
|
-
* return <SessionEnded />;
|
|
199
|
-
* }
|
|
200
|
-
* ```
|
|
201
|
-
*/
|
|
202
|
-
isEmbedded(): boolean;
|
|
203
182
|
/**
|
|
204
183
|
* Redirects the user to a third-party authentication provider's login page.
|
|
205
184
|
*
|
|
@@ -560,9 +539,4 @@ export interface InternalAuthModule extends AuthModule {
|
|
|
560
539
|
* could not succeed without a session, not to decide that one is valid.
|
|
561
540
|
*/
|
|
562
541
|
hasToken(): boolean;
|
|
563
|
-
/**
|
|
564
|
-
* The access token currently set on the client, or `null` when there is none.
|
|
565
|
-
* Follows {@linkcode AuthModule.setToken | setToken} and {@linkcode AuthModule.logout | logout}.
|
|
566
|
-
*/
|
|
567
|
-
getToken(): string | null;
|
|
568
542
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { PlatformClientConfig, TokenRequestOptions } from "./types.js";
|
|
2
|
+
import type { UsersModule } from "./modules/users.types.js";
|
|
3
|
+
import type { AppsModule } from "./modules/apps.types.js";
|
|
4
|
+
/** Immutable server-side view of one provisioned user; never a browser session. */
|
|
5
|
+
export interface PlatformUserClient {
|
|
6
|
+
/** Canonical caller-owned external ID, not the Base44 user ID. */
|
|
7
|
+
readonly externalId: string;
|
|
8
|
+
/** App operations authenticated as this user. */
|
|
9
|
+
readonly apps: AppsModule;
|
|
10
|
+
/** Lazily mint/reuse a server credential. Never send the result to the browser. */
|
|
11
|
+
getAccessToken(options?: TokenRequestOptions): Promise<string>;
|
|
12
|
+
/** Revoke refresh credentials and clear storage even if remote revocation fails. Does not deprovision. */
|
|
13
|
+
revokeToken(): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
/** Server-only client for provisioning users and managing their apps.
|
|
16
|
+
* @example
|
|
17
|
+
* const platform = new Base44PlatformClient({ apiKey, workspaceId });
|
|
18
|
+
* await platform.users.provision({ externalId: "customer_42" });
|
|
19
|
+
* const apps = await platform.asUser("customer_42").apps.list();
|
|
20
|
+
*/
|
|
21
|
+
export declare class Base44PlatformClient {
|
|
22
|
+
#private;
|
|
23
|
+
/** Explicit identity provisioning and offboarding, using the configured workspace API key. */
|
|
24
|
+
readonly users: UsersModule;
|
|
25
|
+
/** Construct without network access. One key must support provisioning and minting. */
|
|
26
|
+
constructor(config: PlatformClientConfig);
|
|
27
|
+
/** Bind a previously provisioned external user ID. Lazy, synchronous and safe to call concurrently. */
|
|
28
|
+
asUser(value: string): PlatformUserClient;
|
|
29
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Base44PlatformError } from "./errors.js";
|
|
2
|
+
import { InMemoryTokenStore } from "./token-store.js";
|
|
3
|
+
import { Tokens } from "./tokens.js";
|
|
4
|
+
import { Transport, checkAbort, waitFor } from "./transport.js";
|
|
5
|
+
import { createUsers } from "./modules/users.js";
|
|
6
|
+
import { createApps } from "./modules/apps.js";
|
|
7
|
+
import { externalId, identifier, invalid, positive } from "./validation.js";
|
|
8
|
+
/** Server-only client for provisioning users and managing their apps.
|
|
9
|
+
* @example
|
|
10
|
+
* const platform = new Base44PlatformClient({ apiKey, workspaceId });
|
|
11
|
+
* await platform.users.provision({ externalId: "customer_42" });
|
|
12
|
+
* const apps = await platform.asUser("customer_42").apps.list();
|
|
13
|
+
*/
|
|
14
|
+
export class Base44PlatformClient {
|
|
15
|
+
/** Explicit identity provisioning and offboarding, using the configured workspace API key. */
|
|
16
|
+
users;
|
|
17
|
+
#transport;
|
|
18
|
+
#tokens;
|
|
19
|
+
#workspaceId;
|
|
20
|
+
#buildTimeoutMs;
|
|
21
|
+
/** Construct without network access. One key must support provisioning and minting. */
|
|
22
|
+
constructor(config) {
|
|
23
|
+
if (typeof window !== "undefined")
|
|
24
|
+
throw new Base44PlatformError("server_only", "The platform client must run on your server.");
|
|
25
|
+
if (typeof config.apiKey !== "string" || !config.apiKey.trim())
|
|
26
|
+
invalid("apiKey is required.");
|
|
27
|
+
identifier(config.workspaceId);
|
|
28
|
+
let url;
|
|
29
|
+
try {
|
|
30
|
+
url = new URL(config.serverUrl ?? "https://base44.app");
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
invalid("serverUrl must be an absolute HTTP(S) URL.");
|
|
34
|
+
}
|
|
35
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash)
|
|
36
|
+
invalid("serverUrl must be an HTTP(S) URL without credentials, query or fragment.");
|
|
37
|
+
const serverUrl = url.toString().replace(/\/+$/, "");
|
|
38
|
+
this.#workspaceId = config.workspaceId;
|
|
39
|
+
this.#buildTimeoutMs = positive(config.buildTimeoutMs ?? 120_000);
|
|
40
|
+
this.#transport = new Transport(serverUrl, config.fetch ?? globalThis.fetch, positive(config.timeoutMs ?? 30_000));
|
|
41
|
+
this.#tokens = new Tokens(this.#transport, config.tokenStore ?? new InMemoryTokenStore(), config.apiKey, { serverUrl, workspaceId: config.workspaceId });
|
|
42
|
+
this.users = Object.freeze(createUsers(this.#transport, this.#tokens, config.apiKey));
|
|
43
|
+
}
|
|
44
|
+
/** Bind a previously provisioned external user ID. Lazy, synchronous and safe to call concurrently. */
|
|
45
|
+
asUser(value) {
|
|
46
|
+
const id = externalId(value);
|
|
47
|
+
return Object.freeze({
|
|
48
|
+
externalId: id,
|
|
49
|
+
apps: Object.freeze(createApps(this.#transport, this.#tokens, id, this.#workspaceId, this.#buildTimeoutMs)),
|
|
50
|
+
getAccessToken: async (options = {}) => {
|
|
51
|
+
checkAbort(options.signal);
|
|
52
|
+
return waitFor(this.#tokens.get(id, options.forceRefresh), options.signal);
|
|
53
|
+
},
|
|
54
|
+
revokeToken: () => this.#tokens.revoke(id),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Stable SDK failure categories; server response prose is never copied into errors. */
|
|
2
|
+
export type PlatformErrorCode = "invalid_argument" | "server_only" | "http_error" | "invalid_response" | "network_error" | "timeout" | "aborted" | "token_store_error";
|
|
3
|
+
/** Safe error representation, suitable for returning to your own application. */
|
|
4
|
+
export interface PlatformErrorJSON {
|
|
5
|
+
/** Always Base44PlatformError. */
|
|
6
|
+
name: "Base44PlatformError";
|
|
7
|
+
/** SDK-defined message containing no request or response bodies. */
|
|
8
|
+
message: string;
|
|
9
|
+
/** HTTP status, or 0 for a local, network, cancellation or storage failure. */
|
|
10
|
+
status: number;
|
|
11
|
+
/** Stable SDK category; inspect status for HTTP failures. */
|
|
12
|
+
code: PlatformErrorCode;
|
|
13
|
+
}
|
|
14
|
+
/** All SDK failures use this error; no raw response, credential or fetch error is retained. */
|
|
15
|
+
export declare class Base44PlatformError extends Error {
|
|
16
|
+
/** SDK failure category. */
|
|
17
|
+
readonly code: PlatformErrorCode;
|
|
18
|
+
/** HTTP status, or 0 if no HTTP failure response was received. */
|
|
19
|
+
readonly status: number;
|
|
20
|
+
/** Create a safe error from an SDK category, message and optional HTTP status. */
|
|
21
|
+
constructor(
|
|
22
|
+
/** SDK failure category. */
|
|
23
|
+
code: PlatformErrorCode, message: string,
|
|
24
|
+
/** HTTP status, or 0 if no HTTP failure response was received. */
|
|
25
|
+
status?: number);
|
|
26
|
+
/** Return safe fields only; excludes stack traces and transport internals. */
|
|
27
|
+
toJSON(): PlatformErrorJSON;
|
|
28
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** All SDK failures use this error; no raw response, credential or fetch error is retained. */
|
|
2
|
+
export class Base44PlatformError extends Error {
|
|
3
|
+
code;
|
|
4
|
+
status;
|
|
5
|
+
/** Create a safe error from an SDK category, message and optional HTTP status. */
|
|
6
|
+
constructor(
|
|
7
|
+
/** SDK failure category. */
|
|
8
|
+
code, message,
|
|
9
|
+
/** HTTP status, or 0 if no HTTP failure response was received. */
|
|
10
|
+
status = 0) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.status = status;
|
|
14
|
+
this.name = "Base44PlatformError";
|
|
15
|
+
}
|
|
16
|
+
/** Return safe fields only; excludes stack traces and transport internals. */
|
|
17
|
+
toJSON() {
|
|
18
|
+
return { name: "Base44PlatformError", message: this.message, status: this.status, code: this.code };
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Server-side Base44 platform SDK. Runtime app entities remain in @base44/sdk. */
|
|
2
|
+
export { Base44PlatformClient } from "./client.js";
|
|
3
|
+
export type { PlatformUserClient } from "./client.js";
|
|
4
|
+
export { Base44PlatformError } from "./errors.js";
|
|
5
|
+
export type { PlatformErrorCode, PlatformErrorJSON } from "./errors.js";
|
|
6
|
+
export { InMemoryTokenStore } from "./token-store.js";
|
|
7
|
+
export type { PlatformClientConfig, RequestOptions, TokenRequestOptions, TokenKey, TokenRecord, TokenStore } from "./types.js";
|
|
8
|
+
export type { UsersModule, ProvisionUserInput, ProvisionedUser, DeprovisionResult } from "./modules/users.types.js";
|
|
9
|
+
export type { AppsModule, PlatformApp, AppState, ListAppsInput, CreateAppInput, PreviewResult, DeployResult } from "./modules/apps.types.js";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Transport } from "../transport.js";
|
|
2
|
+
import { Tokens } from "../tokens.js";
|
|
3
|
+
import type { AppsModule } from "./apps.types.js";
|
|
4
|
+
export declare function createApps(transport: Transport, tokens: Tokens, id: string, workspaceId: string, buildTimeoutMs: number): AppsModule;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { checkAbort, waitFor } from "../transport.js";
|
|
2
|
+
import { object, identifier, requiredString, nullableString, badResponse, invalid } from "../validation.js";
|
|
3
|
+
function projectApp(value) {
|
|
4
|
+
const data = object(value);
|
|
5
|
+
const state = data.status && typeof data.status === "object" ? object(data.status).state : undefined;
|
|
6
|
+
return {
|
|
7
|
+
id: requiredString(data.id), name: nullableString(data.name), slug: nullableString(data.slug),
|
|
8
|
+
state: state === "ready" || state === "processing" || state === "error" ? state : "unknown",
|
|
9
|
+
updatedAt: nullableString(data.updated_date), previewScreenshotUrl: nullableString(data.preview_screenshot_url),
|
|
10
|
+
logoUrl: nullableString(data.logo_url), lastDeployedAt: nullableString(data.last_deployed_at),
|
|
11
|
+
currentRevision: nullableString(data.last_git_commit_hash), deployedRevision: nullableString(data.last_deployed_git_commit_hash),
|
|
12
|
+
hasCustomInstructions: typeof data.custom_instructions === "string" && data.custom_instructions.length > 0,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function createApps(transport, tokens, id, workspaceId, buildTimeoutMs) {
|
|
16
|
+
async function request(path, method, body, options = {}) {
|
|
17
|
+
checkAbort(options.signal);
|
|
18
|
+
const token = await waitFor(tokens.get(id), options.signal);
|
|
19
|
+
return transport.request(path, method, {
|
|
20
|
+
Authorization: `Bearer ${token}`, "X-Active-Workspace-Id": workspaceId, "Content-Type": "application/json",
|
|
21
|
+
}, body, options);
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
async list(input = {}, options) {
|
|
25
|
+
const limit = input.limit ?? 20, skip = input.skip ?? 0;
|
|
26
|
+
if (!Number.isInteger(limit) || limit < 1 || !Number.isInteger(skip) || skip < 0)
|
|
27
|
+
invalid("limit must be positive and skip nonnegative integers.");
|
|
28
|
+
const query = new URLSearchParams({ q: JSON.stringify({ app_type: { $nin: ["user_agent"] } }), sort: "-updated_date", limit: String(limit), skip: String(skip), filter_mode: "all_apps_workspace" });
|
|
29
|
+
if (input.folderId !== undefined) {
|
|
30
|
+
identifier(input.folderId);
|
|
31
|
+
query.set("folder_id", input.folderId);
|
|
32
|
+
}
|
|
33
|
+
const data = await request(`/api/apps?${query}`, "GET", undefined, options);
|
|
34
|
+
return Array.isArray(data) ? data.map(projectApp) : badResponse();
|
|
35
|
+
},
|
|
36
|
+
async create(input, options) {
|
|
37
|
+
if (typeof input.prompt !== "string" || !input.prompt.trim())
|
|
38
|
+
invalid("An initial prompt is required.");
|
|
39
|
+
return projectApp(await request("/api/apps", "POST", {
|
|
40
|
+
name: input.name, user_description: input.prompt, organization_id: workspaceId,
|
|
41
|
+
public_settings: input.publicSettings, prevent_iframe_embedding: input.preventIframeEmbedding,
|
|
42
|
+
custom_instructions: input.customInstructions,
|
|
43
|
+
secrets: input.secrets === undefined ? undefined : Object.fromEntries(Object.entries(input.secrets).map(([key, value]) => [key, { type: "value", value }])),
|
|
44
|
+
initial_message: { content: input.prompt },
|
|
45
|
+
}, { timeoutMs: buildTimeoutMs, ...options }));
|
|
46
|
+
},
|
|
47
|
+
async get(appId, options) { return projectApp(await request(`/api/apps/${identifier(appId)}`, "GET", undefined, options)); },
|
|
48
|
+
async rename(appId, name, options) {
|
|
49
|
+
if (typeof name !== "string" || !name.trim())
|
|
50
|
+
invalid("A nonempty app name is required.");
|
|
51
|
+
return projectApp(await request(`/api/apps/${identifier(appId)}`, "PUT", { name: name.trim() }, options));
|
|
52
|
+
},
|
|
53
|
+
async addToFolder(folderId, appIds, options) {
|
|
54
|
+
appIds.forEach(identifier);
|
|
55
|
+
await request(`/api/app-folders/${identifier(folderId)}/items`, "POST", { app_ids: appIds }, options);
|
|
56
|
+
},
|
|
57
|
+
async getPreviewUrl(appId, options) {
|
|
58
|
+
const data = object(await request(`/api/apps/${identifier(appId)}/sandbox/preview-url`, "GET", undefined, options));
|
|
59
|
+
return { url: requiredString(data.preview_url), token: nullableString(data.preview_token) };
|
|
60
|
+
},
|
|
61
|
+
async deploy(appId, options) {
|
|
62
|
+
const data = object(await request(`/api/apps/${identifier(appId)}/deploy`, "POST", {}, { timeoutMs: buildTimeoutMs, ...options }));
|
|
63
|
+
return { appId: requiredString(data.app_id), checkpointId: nullableString(data.checkpoint_id), revision: requiredString(data.git_commit_hash), deployedAt: requiredString(data.deployed_at) };
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|