@depup/base44__sdk 0.8.43-depup.0 → 0.8.45-depup.0
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 +4 -4
- package/changes.json +3 -3
- package/dist/client.js +29 -3
- package/dist/client.types.d.ts +15 -0
- package/dist/index.d.ts +2 -1
- package/dist/modules/actors.d.ts +33 -7
- package/dist/modules/actors.js +116 -23
- package/dist/modules/actors.types.d.ts +16 -3
- package/dist/modules/analytics.js +14 -3
- package/dist/modules/app.d.ts +11 -0
- package/dist/modules/app.js +15 -0
- package/dist/modules/app.types.d.ts +53 -1
- package/dist/modules/auth.types.d.ts +9 -7
- package/dist/modules/connectors.js +67 -7
- package/dist/modules/connectors.types.d.ts +122 -0
- package/dist/modules/integrations.types.d.ts +2 -2
- package/dist/utils/axios-client.js +6 -4
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -13,8 +13,8 @@ npm install @depup/base44__sdk
|
|
|
13
13
|
|
|
14
14
|
| Field | Value |
|
|
15
15
|
|-------|-------|
|
|
16
|
-
| Original | [@base44/sdk](https://www.npmjs.com/package/@base44/sdk) @ 0.8.
|
|
17
|
-
| Processed | 2026-
|
|
16
|
+
| Original | [@base44/sdk](https://www.npmjs.com/package/@base44/sdk) @ 0.8.45 |
|
|
17
|
+
| Processed | 2026-09-02 |
|
|
18
18
|
| Smoke test | passed |
|
|
19
19
|
| Deps updated | 3 |
|
|
20
20
|
|
|
@@ -22,9 +22,9 @@ npm install @depup/base44__sdk
|
|
|
22
22
|
|
|
23
23
|
| Dependency | From | To |
|
|
24
24
|
|------------|------|-----|
|
|
25
|
-
| axios | ^1.18.1 | ^1.
|
|
25
|
+
| axios | ^1.18.1 | ^1.20.0 |
|
|
26
26
|
| partysocket | ^0.0.23 | ^1.3.0 |
|
|
27
|
-
| uuid | ^13.0.2 | ^14.0.
|
|
27
|
+
| uuid | ^13.0.2 | ^14.0.2 |
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
package/changes.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"bumped": {
|
|
3
3
|
"axios": {
|
|
4
4
|
"from": "^1.18.1",
|
|
5
|
-
"to": "^1.
|
|
5
|
+
"to": "^1.20.0"
|
|
6
6
|
},
|
|
7
7
|
"partysocket": {
|
|
8
8
|
"from": "^0.0.23",
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
},
|
|
11
11
|
"uuid": {
|
|
12
12
|
"from": "^13.0.2",
|
|
13
|
-
"to": "^14.0.
|
|
13
|
+
"to": "^14.0.2"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
|
-
"timestamp": "2026-
|
|
16
|
+
"timestamp": "2026-09-02T08:12:09.333Z",
|
|
17
17
|
"totalUpdated": 3
|
|
18
18
|
}
|
package/dist/client.js
CHANGED
|
@@ -9,10 +9,11 @@ import { createFunctionsModule } from "./modules/functions.js";
|
|
|
9
9
|
import { createAgentsModule } from "./modules/agents.js";
|
|
10
10
|
import { createAiGatewayModule } from "./modules/ai-gateway.js";
|
|
11
11
|
import { createAppLogsModule } from "./modules/app-logs.js";
|
|
12
|
+
import { createAppModule } from "./modules/app.js";
|
|
12
13
|
import { createUsersModule } from "./modules/users.js";
|
|
13
14
|
import { RoomsSocket } from "./utils/socket-utils.js";
|
|
14
15
|
import { createAnalyticsModule } from "./modules/analytics.js";
|
|
15
|
-
import { createActorsModule, resolveActorsHost } from "./modules/actors.js";
|
|
16
|
+
import { createActorsModule, resolveActorsHost, } from "./modules/actors.js";
|
|
16
17
|
/**
|
|
17
18
|
* Creates a Base44 client.
|
|
18
19
|
*
|
|
@@ -110,6 +111,15 @@ export function createClient(config) {
|
|
|
110
111
|
token: serviceToken,
|
|
111
112
|
interceptResponses: false,
|
|
112
113
|
});
|
|
114
|
+
// Dedicated client for actor connection-token mints: no onError (a legacy
|
|
115
|
+
// actor answers every mint with an expected 409 before the proxy fallback,
|
|
116
|
+
// which must not reach the app's error handler — the actors module forwards
|
|
117
|
+
// genuine failures itself via onMintError) and no constructor token
|
|
118
|
+
// (auth is per-request so a login/logout is picked up on every reconnect).
|
|
119
|
+
const actorsAxiosClient = createAxiosClient({
|
|
120
|
+
baseURL: `${serverUrl}/api`,
|
|
121
|
+
headers,
|
|
122
|
+
});
|
|
113
123
|
const userAuthModule = createAuthModule(axiosClient, functionsAxiosClient, appId, {
|
|
114
124
|
appBaseUrl: normalizedAppBaseUrl,
|
|
115
125
|
serverUrl,
|
|
@@ -127,11 +137,26 @@ export function createClient(config) {
|
|
|
127
137
|
}
|
|
128
138
|
const actorsModule = createActorsModule({
|
|
129
139
|
appId,
|
|
130
|
-
// serverUrl is often relative/empty (same-origin app);
|
|
131
|
-
// absolute host, so fall back to the page origin.
|
|
140
|
+
// serverUrl is often relative/empty (same-origin app); the proxy-fallback
|
|
141
|
+
// URL needs an absolute host, so fall back to the page origin.
|
|
132
142
|
host: resolveActorsHost(serverUrl, typeof window !== "undefined" ? (_a = window.location) === null || _a === void 0 ? void 0 : _a.origin : undefined),
|
|
133
143
|
functionsVersion,
|
|
134
144
|
getAuthToken: () => token || getAccessToken(),
|
|
145
|
+
mintConnectionToken: async (actorName, room, connectionId) => {
|
|
146
|
+
const authToken = token || getAccessToken();
|
|
147
|
+
return await actorsAxiosClient.post(`/apps/${appId}/actors/${encodeURIComponent(actorName)}/connection-token`, { room, connection_id: connectionId }, {
|
|
148
|
+
headers: {
|
|
149
|
+
...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),
|
|
150
|
+
// The mint endpoint resolves draft vs published from this header;
|
|
151
|
+
// only the functions axios clients send it by default.
|
|
152
|
+
...(functionsVersion
|
|
153
|
+
? { "Base44-Functions-Version": functionsVersion }
|
|
154
|
+
: {}),
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
transport: options === null || options === void 0 ? void 0 : options.actorsTransport,
|
|
159
|
+
onMintError: options === null || options === void 0 ? void 0 : options.onError,
|
|
135
160
|
});
|
|
136
161
|
const userModules = {
|
|
137
162
|
entities: createEntitiesModule({
|
|
@@ -163,6 +188,7 @@ export function createClient(config) {
|
|
|
163
188
|
}),
|
|
164
189
|
aiGateway: createAiGatewayModule({ serverUrl, token, appId }),
|
|
165
190
|
appLogs: createAppLogsModule(axiosClient, appId),
|
|
191
|
+
app: createAppModule(axiosClient, appId),
|
|
166
192
|
users: createUsersModule(axiosClient, appId),
|
|
167
193
|
analytics: createAnalyticsModule({
|
|
168
194
|
axiosClient,
|
package/dist/client.types.d.ts
CHANGED
|
@@ -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 { AiGatewayModule } from "./modules/ai-gateway.types.js";
|
|
9
9
|
import type { AppLogsModule } from "./modules/app-logs.types.js";
|
|
10
|
+
import type { AppModule } from "./modules/app.types.js";
|
|
10
11
|
import type { AnalyticsModule } from "./modules/analytics.types.js";
|
|
11
12
|
import type { ActorsModule } from "./modules/actors.types.js";
|
|
12
13
|
/**
|
|
@@ -15,8 +16,20 @@ import type { ActorsModule } from "./modules/actors.types.js";
|
|
|
15
16
|
export interface CreateClientOptions {
|
|
16
17
|
/**
|
|
17
18
|
* Optional error handler that will be called whenever an API error occurs.
|
|
19
|
+
*
|
|
20
|
+
* Also receives {@link ActorsModule | actors} connection failures. Errors
|
|
21
|
+
* are usually {@linkcode Base44Error} instances — check `error.status`.
|
|
18
22
|
*/
|
|
19
23
|
onError?: (error: Error) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Forces the actors transport. `"auto"` (default) connects directly to the
|
|
26
|
+
* actor and falls back to the platform proxy when the app's actors don't
|
|
27
|
+
* support direct connections; `"proxy"` always uses the platform proxy
|
|
28
|
+
* (ops rollback — no connection-token calls); `"direct"` disables the
|
|
29
|
+
* fallback (validation environments).
|
|
30
|
+
* @internal
|
|
31
|
+
*/
|
|
32
|
+
actorsTransport?: "auto" | "proxy" | "direct";
|
|
20
33
|
}
|
|
21
34
|
/**
|
|
22
35
|
* Configuration for creating a Base44 client.
|
|
@@ -89,6 +102,8 @@ export interface Base44Client {
|
|
|
89
102
|
analytics: AnalyticsModule;
|
|
90
103
|
/** {@link AppLogsModule | App logs module} for tracking app usage. */
|
|
91
104
|
appLogs: AppLogsModule;
|
|
105
|
+
/** {@link AppModule | App module} for reading the app's own public configuration. */
|
|
106
|
+
app: AppModule;
|
|
92
107
|
/** {@link ActorsModule | Actors module} for subscribing to and sending messages via Cloudflare Durable Object-backed Actors. */
|
|
93
108
|
actors: ActorsModule;
|
|
94
109
|
/** {@link AuthModule | Auth module} for user authentication and management. */
|
package/dist/index.d.ts
CHANGED
|
@@ -11,9 +11,10 @@ export type { FunctionsModule, FunctionName, FunctionNameRegistry, } from "./mod
|
|
|
11
11
|
export type { AgentsModule, AgentName, AgentNameRegistry, AgentConversation, AgentMessage, AgentMessageReasoning, AgentMessageToolCall, AgentMessageUsage, AgentMessageCustomContext, AgentMessageMetadata, CreateConversationParams, } from "./modules/agents.types.js";
|
|
12
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 { AppModule, AppPublicSettings, AppPublicSettingsResponse, } from "./modules/app.types.js";
|
|
14
15
|
export type { ActorsModule, ActorClient, ActorRef, Connection, ActorSubscription, ActorConnectOptions, ActorNameRegistry, ActorRegistry, } from "./modules/actors.types.js";
|
|
15
16
|
export type { SsoModule, SsoAccessTokenResponse } from "./modules/sso.types.js";
|
|
16
17
|
export { Actor, type Conn } from "./actor.js";
|
|
17
|
-
export type { ConnectorsModule, UserConnectorsModule, } from "./modules/connectors.types.js";
|
|
18
|
+
export type { ConnectorsModule, UserConnectorsModule, ConnectorApiRequest, ConnectorApiResponse, ConnectorApiResponsePhase, } from "./modules/connectors.types.js";
|
|
18
19
|
export type { CustomIntegrationsModule, CustomIntegrationCallParams, CustomIntegrationCallResponse, } from "./modules/custom-integrations.types.js";
|
|
19
20
|
export type { GetAccessTokenOptions, SaveAccessTokenOptions, RemoveAccessTokenOptions, GetLoginUrlOptions, } from "./utils/auth-utils.types.js";
|
package/dist/modules/actors.d.ts
CHANGED
|
@@ -1,21 +1,47 @@
|
|
|
1
1
|
import type { ActorRef } from "./actors.types.js";
|
|
2
|
+
/** Credentials minted by the platform for one direct actor connection. */
|
|
3
|
+
export interface ActorConnectionCredentials {
|
|
4
|
+
/** Direct actor endpoint, already carrying `?_pk=<connectionId>`. */
|
|
5
|
+
websocket_url: string;
|
|
6
|
+
/** Short-lived JWT bound to (app, actor, room, connectionId); appended to
|
|
7
|
+
* the URL as `token=` since browsers can't set WebSocket headers. */
|
|
8
|
+
token: string;
|
|
9
|
+
}
|
|
2
10
|
interface ActorsConfig {
|
|
3
11
|
appId: string;
|
|
4
|
-
/** Current user access token, if authenticated. Rides the WS query
|
|
5
|
-
* platform proxy can authenticate the connection;
|
|
12
|
+
/** Current user access token, if authenticated. Rides the WS query on the
|
|
13
|
+
* proxy-fallback path so the platform proxy can authenticate the connection;
|
|
14
|
+
* anonymous connects omit it. */
|
|
6
15
|
getAuthToken(): string | null | undefined;
|
|
7
16
|
/** Same semantics as function calls: editors with a non-prod version get the
|
|
8
17
|
* draft actor script; everyone else gets the published one. */
|
|
9
18
|
functionsVersion?: string;
|
|
10
|
-
/** Absolute host
|
|
19
|
+
/** Absolute host for the proxy-fallback URL (scheme is swapped to wss, ws
|
|
11
20
|
* for localhost). Resolved by {@link resolveActorsHost}. */
|
|
12
21
|
host: string;
|
|
22
|
+
/** Mints a direct-connect credential for one (actor, room, connection).
|
|
23
|
+
* Called per connection attempt: the token's expiry is checked at upgrade,
|
|
24
|
+
* so every reconnect needs a fresh one. */
|
|
25
|
+
mintConnectionToken(actorName: string, room: string, connectionId: string): Promise<ActorConnectionCredentials>;
|
|
26
|
+
/** @internal Ops escape hatch: "proxy" never mints (legacy path only),
|
|
27
|
+
* "direct" never falls back. Default "auto". */
|
|
28
|
+
transport?: "auto" | "proxy" | "direct";
|
|
29
|
+
/** Called when a mint fails for a reason other than the expected
|
|
30
|
+
* direct→proxy fallback (which recovers by itself). Wired to the client's
|
|
31
|
+
* `options.onError`. */
|
|
32
|
+
onMintError?: (error: Error) => void;
|
|
13
33
|
}
|
|
14
34
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
35
|
+
* The legacy platform-proxy URL, byte-for-byte what PartySocket built before
|
|
36
|
+
* the direct path existed: same scheme swap (including its localhost-needs-a-
|
|
37
|
+
* port quirk), case-preserved party segment, `_pk` first in the query. The
|
|
38
|
+
* `handler` param is load-bearing — the proxy reads it for the actor name.
|
|
39
|
+
*/
|
|
40
|
+
export declare function buildProxyActorUrl(rawHost: string, actorName: string, instanceId: string, connectionId: string, appId: string, token: string | null | undefined, functionsVersion?: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* Absolute host for the proxy-fallback actor URL. A relative/empty `serverUrl`
|
|
43
|
+
* can't be dialed (same-origin apps use a relative `/api`, so `serverUrl` is
|
|
44
|
+
* often `""`), so fall back to the page origin.
|
|
19
45
|
*/
|
|
20
46
|
export declare function resolveActorsHost(serverUrl: string, browserOrigin?: string): string;
|
|
21
47
|
export declare function createActorsModule(config: ActorsConfig): {
|
package/dist/modules/actors.js
CHANGED
|
@@ -1,8 +1,32 @@
|
|
|
1
|
-
import
|
|
2
|
-
// Heartbeat / half-open detection:
|
|
1
|
+
import { WebSocket as ReconnectingWebSocket } from "partysocket";
|
|
2
|
+
// Heartbeat / half-open detection: the socket only reconnects on a close/error
|
|
3
3
|
// event, so ping periodically and force a reconnect if nothing returns in DEAD_MS.
|
|
4
4
|
const PING_MS = 1000;
|
|
5
5
|
const DEAD_MS = 3000;
|
|
6
|
+
// Mint responses that mean "direct can't serve this connection, the proxy can":
|
|
7
|
+
// 409 = legacy-family actor script, 503 = direct connections not provisioned,
|
|
8
|
+
// 422 = no principal (e.g. anonymous outside a browser) or an id/room only the
|
|
9
|
+
// proxy's looser validation accepts, 405 = a backend that predates the mint
|
|
10
|
+
// endpoint (its actor deploy routes catch the path via `{handler_name:path}`
|
|
11
|
+
// but not the POST method — and the real endpoint never 405s a POST). The
|
|
12
|
+
// proxy serves migrated actors too, so falling back is always safe.
|
|
13
|
+
const PROXY_FALLBACK_STATUSES = new Set([405, 409, 422, 503]);
|
|
14
|
+
// Mint responses no retry can fix (bad request / forbidden / not found): the
|
|
15
|
+
// connection closes instead of re-minting forever; a fresh connect() re-probes.
|
|
16
|
+
// 401 is deliberately absent — the auth token is re-read on every attempt, so a
|
|
17
|
+
// login recovers on the next retry. Disjoint from PROXY_FALLBACK_STATUSES.
|
|
18
|
+
const TERMINAL_MINT_STATUSES = new Set([400, 403, 404]);
|
|
19
|
+
/** The mint's rejection can be anything; a `Base44Error` carries a numeric
|
|
20
|
+
* `.status` (absent for network failures). */
|
|
21
|
+
function mintErrorStatus(err) {
|
|
22
|
+
const status = err && typeof err === "object"
|
|
23
|
+
? err.status
|
|
24
|
+
: undefined;
|
|
25
|
+
return typeof status === "number" ? status : undefined;
|
|
26
|
+
}
|
|
27
|
+
function toError(err) {
|
|
28
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
29
|
+
}
|
|
6
30
|
/**
|
|
7
31
|
* A live connection to an actor instance. Only obtainable from
|
|
8
32
|
* {@link ActorRef.connect}, so `subscribe`/`send` are always valid — the socket
|
|
@@ -14,23 +38,54 @@ class Connection {
|
|
|
14
38
|
this.onClose = onClose;
|
|
15
39
|
this.listeners = new Set();
|
|
16
40
|
this.heartbeat = null;
|
|
41
|
+
this.closed = false;
|
|
17
42
|
this.id = (_a = options === null || options === void 0 ? void 0 : options.id) !== null && _a !== void 0 ? _a : crypto.randomUUID();
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
43
|
+
// Direct-first with proxy fallback, decided per connection attempt. Once a
|
|
44
|
+
// mint answers with a fallback status the choice is sticky for this
|
|
45
|
+
// socket's lifetime (a fresh connect() after close() probes direct again,
|
|
46
|
+
// picking up actors migrated in the meantime). Any other mint failure
|
|
47
|
+
// rejects, which ReconnectingWebSocket retries with backoff — except the
|
|
48
|
+
// terminal statuses, which close this connection for good.
|
|
49
|
+
let useProxy = config.transport === "proxy";
|
|
50
|
+
const urlProvider = async () => {
|
|
51
|
+
var _a;
|
|
52
|
+
if (this.closed)
|
|
53
|
+
throw new Error("Actor connection is closed");
|
|
54
|
+
if (!useProxy) {
|
|
55
|
+
try {
|
|
56
|
+
const { websocket_url, token } = await config.mintConnectionToken(actorName, instanceId, this.id);
|
|
57
|
+
const sep = websocket_url.includes("?") ? "&" : "?";
|
|
58
|
+
return `${websocket_url}${sep}token=${encodeURIComponent(token)}`;
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
const status = mintErrorStatus(err);
|
|
62
|
+
const isFallback = config.transport !== "direct" &&
|
|
63
|
+
status !== undefined &&
|
|
64
|
+
PROXY_FALLBACK_STATUSES.has(status);
|
|
65
|
+
if (!isFallback) {
|
|
66
|
+
if (status !== undefined && TERMINAL_MINT_STATUSES.has(status)) {
|
|
67
|
+
// close() before notifying: ws.close() stops the redial the
|
|
68
|
+
// rethrow below would otherwise schedule, and a handler that
|
|
69
|
+
// immediately calls connect() gets a clean new connection.
|
|
70
|
+
this.close();
|
|
71
|
+
}
|
|
72
|
+
// Reported from here because the socket's error event only
|
|
73
|
+
// preserves `err.message`, never `.status`.
|
|
74
|
+
try {
|
|
75
|
+
(_a = config.onMintError) === null || _a === void 0 ? void 0 : _a.call(config, toError(err));
|
|
76
|
+
}
|
|
77
|
+
catch (_b) {
|
|
78
|
+
// an app handler must not break the dial loop or mask `err`
|
|
79
|
+
}
|
|
80
|
+
throw err;
|
|
81
|
+
}
|
|
82
|
+
useProxy = true;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Rebuilt per attempt so a login/logout is picked up on reconnect.
|
|
86
|
+
return buildProxyActorUrl(config.host, actorName, instanceId, this.id, config.appId, config.getAuthToken(), config.functionsVersion);
|
|
87
|
+
};
|
|
88
|
+
const ws = new ReconnectingWebSocket(urlProvider);
|
|
34
89
|
this.ws = ws;
|
|
35
90
|
let lastMsg = Date.now();
|
|
36
91
|
const bumpAlive = () => { lastMsg = Date.now(); };
|
|
@@ -53,7 +108,11 @@ class Connection {
|
|
|
53
108
|
this.heartbeat = setInterval(() => {
|
|
54
109
|
if (Date.now() - lastMsg > DEAD_MS) {
|
|
55
110
|
bumpAlive(); // avoid a reconnect storm while the new socket comes up
|
|
56
|
-
|
|
111
|
+
// Only kick a half-open socket (OPEN but silent). When it isn't open
|
|
112
|
+
// the socket is already redialing with backoff, and reconnect() would
|
|
113
|
+
// reset that backoff into a mint call every DEAD_MS.
|
|
114
|
+
if (ws.readyState === ws.OPEN)
|
|
115
|
+
ws.reconnect();
|
|
57
116
|
return;
|
|
58
117
|
}
|
|
59
118
|
try {
|
|
@@ -73,9 +132,15 @@ class Connection {
|
|
|
73
132
|
};
|
|
74
133
|
}
|
|
75
134
|
send(data) {
|
|
135
|
+
// after close() the socket would buffer forever (unbounded enqueue)
|
|
136
|
+
if (this.closed)
|
|
137
|
+
return;
|
|
76
138
|
this.ws.send(JSON.stringify(data));
|
|
77
139
|
}
|
|
78
140
|
close() {
|
|
141
|
+
if (this.closed)
|
|
142
|
+
return;
|
|
143
|
+
this.closed = true;
|
|
79
144
|
if (this.heartbeat) {
|
|
80
145
|
clearInterval(this.heartbeat);
|
|
81
146
|
this.heartbeat = null;
|
|
@@ -104,10 +169,38 @@ function makeActorRef(actorName, instanceId, config, connections) {
|
|
|
104
169
|
};
|
|
105
170
|
}
|
|
106
171
|
/**
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
172
|
+
* The legacy platform-proxy URL, byte-for-byte what PartySocket built before
|
|
173
|
+
* the direct path existed: same scheme swap (including its localhost-needs-a-
|
|
174
|
+
* port quirk), case-preserved party segment, `_pk` first in the query. The
|
|
175
|
+
* `handler` param is load-bearing — the proxy reads it for the actor name.
|
|
176
|
+
*/
|
|
177
|
+
export function buildProxyActorUrl(rawHost, actorName, instanceId, connectionId, appId, token, functionsVersion) {
|
|
178
|
+
let host = rawHost.replace(/^(http|https|ws|wss):\/\//, "");
|
|
179
|
+
if (host.endsWith("/"))
|
|
180
|
+
host = host.slice(0, -1);
|
|
181
|
+
const insecure = host.startsWith("localhost:") ||
|
|
182
|
+
host.startsWith("127.0.0.1:") ||
|
|
183
|
+
host.startsWith("192.168.") ||
|
|
184
|
+
host.startsWith("10.") ||
|
|
185
|
+
(host.startsWith("172.") &&
|
|
186
|
+
host.split(".")[1] >= "16" &&
|
|
187
|
+
host.split(".")[1] <= "31") ||
|
|
188
|
+
host.startsWith("[::ffff:7f00:1]:");
|
|
189
|
+
const query = new URLSearchParams([
|
|
190
|
+
["_pk", connectionId],
|
|
191
|
+
["app_id", appId],
|
|
192
|
+
["handler", actorName],
|
|
193
|
+
]);
|
|
194
|
+
if (token)
|
|
195
|
+
query.append("token", token);
|
|
196
|
+
if (functionsVersion)
|
|
197
|
+
query.append("fv", functionsVersion);
|
|
198
|
+
return `${insecure ? "ws" : "wss"}://${host}/parties/${actorName}/${instanceId}?${query}`;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Absolute host for the proxy-fallback actor URL. A relative/empty `serverUrl`
|
|
202
|
+
* can't be dialed (same-origin apps use a relative `/api`, so `serverUrl` is
|
|
203
|
+
* often `""`), so fall back to the page origin.
|
|
111
204
|
*/
|
|
112
205
|
export function resolveActorsHost(serverUrl, browserOrigin) {
|
|
113
206
|
return serverUrl && !serverUrl.startsWith("/") ? serverUrl : browserOrigin !== null && browserOrigin !== void 0 ? browserOrigin : serverUrl;
|
|
@@ -56,9 +56,14 @@ export interface Connection<N extends string = string> {
|
|
|
56
56
|
readonly id: string;
|
|
57
57
|
/** Register a message listener. Multiple are allowed; returns a per-listener unsubscribe. */
|
|
58
58
|
subscribe(callback: (data: ToClientFor<N>) => void): ActorSubscription;
|
|
59
|
-
/** Send a message. Buffered by the socket until it's open
|
|
59
|
+
/** Send a message. Buffered by the socket until it's open; dropped after
|
|
60
|
+
* {@link close}. */
|
|
60
61
|
send(data: ToServerFor<N>): void;
|
|
61
|
-
/**
|
|
62
|
+
/**
|
|
63
|
+
* Tear down the socket, heartbeat, and all listeners. Safe to call more
|
|
64
|
+
* than once. A connection also closes itself when it fails permanently —
|
|
65
|
+
* see {@link ActorRef.connect}.
|
|
66
|
+
*/
|
|
62
67
|
close(): void;
|
|
63
68
|
}
|
|
64
69
|
/**
|
|
@@ -66,7 +71,15 @@ export interface Connection<N extends string = string> {
|
|
|
66
71
|
* {@link connect} to open the socket and get a {@link Connection}.
|
|
67
72
|
*/
|
|
68
73
|
export interface ActorRef<N extends string = string> {
|
|
69
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* Open the WebSocket and return the {@link Connection}. Idempotent while the
|
|
76
|
+
* connection is open.
|
|
77
|
+
*
|
|
78
|
+
* A connection that fails permanently (for example, the actor doesn't exist
|
|
79
|
+
* or the caller isn't allowed to connect) closes itself and reports the
|
|
80
|
+
* error to the client's `onError` handler. Call `connect()` again after
|
|
81
|
+
* fixing the cause to get a fresh {@link Connection}, and re-subscribe.
|
|
82
|
+
*/
|
|
70
83
|
connect(options?: ActorConnectOptions): Connection<N>;
|
|
71
84
|
}
|
|
72
85
|
/**
|
|
@@ -25,6 +25,9 @@ const analyticsSharedState = getSharedInstance(ANALYTICS_SHARED_STATE_NAME, () =
|
|
|
25
25
|
wasInitializationTracked: false,
|
|
26
26
|
sessionContext: null,
|
|
27
27
|
sessionStartTime: null,
|
|
28
|
+
// Memoized session id for when `localStorage` can't persist one — see
|
|
29
|
+
// getAnalyticsSessionId.
|
|
30
|
+
fallbackSessionId: null,
|
|
28
31
|
config: {
|
|
29
32
|
...defaultConfiguration,
|
|
30
33
|
...getAnalyticsConfigFromUrlParams(),
|
|
@@ -216,9 +219,11 @@ function trackSessionDurationEvent(track) {
|
|
|
216
219
|
});
|
|
217
220
|
}
|
|
218
221
|
function getEventIntrinsicData() {
|
|
222
|
+
var _a, _b;
|
|
219
223
|
return {
|
|
220
224
|
timestamp: new Date().toISOString(),
|
|
221
|
-
|
|
225
|
+
// `window.location` is absent on React Native, so read it optionally.
|
|
226
|
+
pageUrl: typeof window !== "undefined" ? (_b = (_a = window.location) === null || _a === void 0 ? void 0 : _a.pathname) !== null && _b !== void 0 ? _b : null : null,
|
|
222
227
|
};
|
|
223
228
|
}
|
|
224
229
|
function transformEventDataToApiRequestData(sessionContext) {
|
|
@@ -302,9 +307,15 @@ export function getAnalyticsConfigFromUrlParams() {
|
|
|
302
307
|
// return the config object //
|
|
303
308
|
return { enabled: analyticsEnable === "true" };
|
|
304
309
|
}
|
|
310
|
+
// When the id can't be persisted (React Native has no `localStorage`), keep
|
|
311
|
+
// it stable for the process instead of minting a fresh one per call.
|
|
312
|
+
function getFallbackSessionId() {
|
|
313
|
+
var _a;
|
|
314
|
+
return ((_a = analyticsSharedState.fallbackSessionId) !== null && _a !== void 0 ? _a : (analyticsSharedState.fallbackSessionId = generateUuid()));
|
|
315
|
+
}
|
|
305
316
|
export function getAnalyticsSessionId() {
|
|
306
317
|
if (typeof window === "undefined") {
|
|
307
|
-
return
|
|
318
|
+
return getFallbackSessionId();
|
|
308
319
|
}
|
|
309
320
|
try {
|
|
310
321
|
const sessionId = localStorage.getItem(ANALYTICS_SESSION_ID_LOCAL_STORAGE_KEY);
|
|
@@ -316,6 +327,6 @@ export function getAnalyticsSessionId() {
|
|
|
316
327
|
return sessionId;
|
|
317
328
|
}
|
|
318
329
|
catch (_a) {
|
|
319
|
-
return
|
|
330
|
+
return getFallbackSessionId();
|
|
320
331
|
}
|
|
321
332
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AxiosInstance } from "axios";
|
|
2
|
+
import { AppModule } from "./app.types";
|
|
3
|
+
/**
|
|
4
|
+
* Creates the app module for the Base44 SDK.
|
|
5
|
+
*
|
|
6
|
+
* @param axios - Axios instance
|
|
7
|
+
* @param appId - Application ID
|
|
8
|
+
* @returns App module for reading the app's own configuration
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
export declare function createAppModule(axios: AxiosInstance, appId: string): AppModule;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates the app module for the Base44 SDK.
|
|
3
|
+
*
|
|
4
|
+
* @param axios - Axios instance
|
|
5
|
+
* @param appId - Application ID
|
|
6
|
+
* @returns App module for reading the app's own configuration
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export function createAppModule(axios, appId) {
|
|
10
|
+
return {
|
|
11
|
+
async getPublicSettings() {
|
|
12
|
+
return axios.get(`/apps/public/prod/public-settings/by-id/${appId}`);
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -1,3 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The app's access policy: whether the app is reachable without an account, and
|
|
3
|
+
* who may sign in.
|
|
4
|
+
*/
|
|
5
|
+
export type AppPublicSettings = "private_with_login" | "public_with_login" | "public_without_login" | "workspace_with_login" | string;
|
|
6
|
+
/**
|
|
7
|
+
* The app's public configuration, as returned by {@link AppModule.getPublicSettings}.
|
|
8
|
+
*/
|
|
9
|
+
export interface AppPublicSettingsResponse {
|
|
10
|
+
/** The app's ID. */
|
|
11
|
+
id: string;
|
|
12
|
+
/** The app's access policy. */
|
|
13
|
+
public_settings: AppPublicSettings;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* App module for reading the app's own public configuration.
|
|
17
|
+
*
|
|
18
|
+
* Use it to discover how the app is gated before rendering it, so a private app
|
|
19
|
+
* can send the visitor to login instead of rendering an empty shell.
|
|
20
|
+
*
|
|
21
|
+
* ## Authentication Modes
|
|
22
|
+
*
|
|
23
|
+
* This module is available to use with a client in all authentication modes. The
|
|
24
|
+
* client's token, when it has one, is sent with the request — a signed-in visitor
|
|
25
|
+
* who has no access to the app is reported differently from an anonymous one.
|
|
26
|
+
*/
|
|
27
|
+
export interface AppModule {
|
|
28
|
+
/**
|
|
29
|
+
* Get the app's public configuration.
|
|
30
|
+
*
|
|
31
|
+
* Rejects with a {@linkcode Base44Error} when the visitor may not open the app:
|
|
32
|
+
* `status` is `403` and `data.extra_data.reason` says why — `"auth_required"`
|
|
33
|
+
* when the visitor must sign in, `"user_not_registered"` when the signed-in
|
|
34
|
+
* visitor has no access to this app.
|
|
35
|
+
*
|
|
36
|
+
* @returns Promise resolving to the app's ID and access policy.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* // Decide what to render before the app boots
|
|
41
|
+
* try {
|
|
42
|
+
* const { public_settings } = await base44.app.getPublicSettings();
|
|
43
|
+
* console.log('App access policy:', public_settings);
|
|
44
|
+
* } catch (error) {
|
|
45
|
+
* if (error.status === 403) {
|
|
46
|
+
* console.log('Blocked because:', error.data?.extra_data?.reason);
|
|
47
|
+
* }
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
getPublicSettings(): Promise<AppPublicSettingsResponse>;
|
|
52
|
+
}
|
|
1
53
|
/**
|
|
2
54
|
* @internal
|
|
3
55
|
*/
|
|
@@ -62,7 +114,7 @@ export interface AppLike {
|
|
|
62
114
|
agents?: Record<string, any>;
|
|
63
115
|
logo_url?: string;
|
|
64
116
|
slug?: string;
|
|
65
|
-
public_settings?:
|
|
117
|
+
public_settings?: AppPublicSettings;
|
|
66
118
|
is_blocked?: boolean;
|
|
67
119
|
github_repo_url?: string;
|
|
68
120
|
main_page?: string;
|
|
@@ -134,12 +134,15 @@ export interface AuthModule {
|
|
|
134
134
|
/**
|
|
135
135
|
* Updates the current authenticated user's information.
|
|
136
136
|
*
|
|
137
|
-
* You can update
|
|
138
|
-
* User entity schema.
|
|
139
|
-
*
|
|
137
|
+
* You can update any [custom fields](/developers/backend/resources/entities/user-schema#custom-fields)
|
|
138
|
+
* defined in your User entity schema.
|
|
139
|
+
*
|
|
140
|
+
* Updating `role` requires editor access on the app.
|
|
141
|
+
*
|
|
140
142
|
* <Note>
|
|
141
|
-
*
|
|
142
|
-
* `id`, `email`, `full_name`, `created_date`, `updated_date`,
|
|
143
|
+
* These fields can't be changed with this method:
|
|
144
|
+
* `id`, `email`, `full_name`, `created_date`, `updated_date`, `created_by`,
|
|
145
|
+
* and `collaborator_role`.
|
|
143
146
|
* </Note>
|
|
144
147
|
*
|
|
145
148
|
* @param data - Object containing the fields to update.
|
|
@@ -147,9 +150,8 @@ export interface AuthModule {
|
|
|
147
150
|
*
|
|
148
151
|
* @example
|
|
149
152
|
* ```typescript
|
|
150
|
-
* // Update
|
|
153
|
+
* // Update custom fields defined in your User entity
|
|
151
154
|
* await base44.auth.updateMe({
|
|
152
|
-
* role: 'admin',
|
|
153
155
|
* bio: 'Software developer',
|
|
154
156
|
* preferences: { theme: 'dark' }
|
|
155
157
|
* });
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
const CONNECTOR_API_METHODS = new Set([
|
|
2
|
+
"GET",
|
|
3
|
+
"POST",
|
|
4
|
+
"PUT",
|
|
5
|
+
"PATCH",
|
|
6
|
+
"DELETE",
|
|
7
|
+
"HEAD",
|
|
8
|
+
]);
|
|
1
9
|
/**
|
|
2
10
|
* Creates the Connectors module for the Base44 SDK.
|
|
3
11
|
*
|
|
@@ -17,7 +25,7 @@ export function createConnectorsModule(axios, appId) {
|
|
|
17
25
|
if (!integrationType || typeof integrationType !== "string") {
|
|
18
26
|
throw new Error("Integration type is required and must be a string");
|
|
19
27
|
}
|
|
20
|
-
const response = await axios.get(`/apps/${appId}/external-auth/tokens/${integrationType}`);
|
|
28
|
+
const response = await axios.get(`/apps/${appId}/external-auth/tokens/${encodeURIComponent(integrationType)}`);
|
|
21
29
|
// @ts-expect-error
|
|
22
30
|
return response.access_token;
|
|
23
31
|
},
|
|
@@ -26,7 +34,7 @@ export function createConnectorsModule(axios, appId) {
|
|
|
26
34
|
if (!integrationType || typeof integrationType !== "string") {
|
|
27
35
|
throw new Error("Integration type is required and must be a string");
|
|
28
36
|
}
|
|
29
|
-
const response = await axios.get(`/apps/${appId}/external-auth/tokens/${integrationType}`);
|
|
37
|
+
const response = await axios.get(`/apps/${appId}/external-auth/tokens/${encodeURIComponent(integrationType)}`);
|
|
30
38
|
const data = response;
|
|
31
39
|
return {
|
|
32
40
|
accessToken: data.access_token,
|
|
@@ -38,7 +46,7 @@ export function createConnectorsModule(axios, appId) {
|
|
|
38
46
|
if (!connectorId || typeof connectorId !== "string") {
|
|
39
47
|
throw new Error("Connector ID is required and must be a string");
|
|
40
48
|
}
|
|
41
|
-
const response = await axios.get(`/apps/${appId}/external-auth/tokens/connectors/${connectorId}`);
|
|
49
|
+
const response = await axios.get(`/apps/${appId}/external-auth/tokens/connectors/${encodeURIComponent(connectorId)}`);
|
|
42
50
|
const data = response;
|
|
43
51
|
return {
|
|
44
52
|
accessToken: data.access_token,
|
|
@@ -52,7 +60,7 @@ export function createConnectorsModule(axios, appId) {
|
|
|
52
60
|
if (!connectorId || typeof connectorId !== "string") {
|
|
53
61
|
throw new Error("Connector ID is required and must be a string");
|
|
54
62
|
}
|
|
55
|
-
const response = await axios.get(`/apps/${appId}/app-user-auth/connectors/${connectorId}/token`);
|
|
63
|
+
const response = await axios.get(`/apps/${appId}/app-user-auth/connectors/${encodeURIComponent(connectorId)}/token`);
|
|
56
64
|
const data = response;
|
|
57
65
|
return data.access_token;
|
|
58
66
|
},
|
|
@@ -61,13 +69,65 @@ export function createConnectorsModule(axios, appId) {
|
|
|
61
69
|
if (!connectorId || typeof connectorId !== "string") {
|
|
62
70
|
throw new Error("Connector ID is required and must be a string");
|
|
63
71
|
}
|
|
64
|
-
const response = await axios.get(`/apps/${appId}/app-user-auth/connectors/${connectorId}/token`);
|
|
72
|
+
const response = await axios.get(`/apps/${appId}/app-user-auth/connectors/${encodeURIComponent(connectorId)}/token`);
|
|
65
73
|
const data = response;
|
|
66
74
|
return {
|
|
67
75
|
accessToken: data.access_token,
|
|
68
76
|
connectionConfig: (_a = data.connection_config) !== null && _a !== void 0 ? _a : null,
|
|
69
77
|
};
|
|
70
78
|
},
|
|
79
|
+
async callApi(integrationType, request) {
|
|
80
|
+
assertNonEmptyString(integrationType, "Integration type");
|
|
81
|
+
// Encoded so a runtime-built identifier can only ever select a
|
|
82
|
+
// connector, never re-target another route under this token.
|
|
83
|
+
return proxyCall(axios, `/apps/${appId}/connectors/${encodeURIComponent(integrationType)}/call`, request);
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function assertNonEmptyString(value, label) {
|
|
88
|
+
if (!value || typeof value !== "string") {
|
|
89
|
+
throw new Error(`${label} is required and must be a string`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* POST a request to the connector proxy and normalize the response.
|
|
94
|
+
*
|
|
95
|
+
* The proxy reports upstream outcomes in the body rather than as HTTP status, so
|
|
96
|
+
* a provider 4xx/5xx arrives here as a resolved response with `success: false` —
|
|
97
|
+
* only Base44-side failures reject through the axios error interceptor.
|
|
98
|
+
*
|
|
99
|
+
* @internal
|
|
100
|
+
*/
|
|
101
|
+
async function proxyCall(axios, url, request) {
|
|
102
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
103
|
+
if (!request || typeof request !== "object") {
|
|
104
|
+
throw new Error("Request is required and must be an object");
|
|
105
|
+
}
|
|
106
|
+
assertNonEmptyString(request.path, "Request path");
|
|
107
|
+
const method = (_a = request.method) !== null && _a !== void 0 ? _a : "GET";
|
|
108
|
+
if (!CONNECTOR_API_METHODS.has(method)) {
|
|
109
|
+
throw new Error("Request method must be one of GET, POST, PUT, PATCH, DELETE, or HEAD");
|
|
110
|
+
}
|
|
111
|
+
const response = await axios.post(url, {
|
|
112
|
+
method,
|
|
113
|
+
// Omitted when unset (undefined or null, since untyped callers write
|
|
114
|
+
// either) so the proxy applies the connector's declared default host.
|
|
115
|
+
...(request.host == null ? {} : { host: request.host }),
|
|
116
|
+
path: request.path,
|
|
117
|
+
query: (_b = request.query) !== null && _b !== void 0 ? _b : {},
|
|
118
|
+
headers: (_c = request.headers) !== null && _c !== void 0 ? _c : {},
|
|
119
|
+
body: (_d = request.body) !== null && _d !== void 0 ? _d : null,
|
|
120
|
+
});
|
|
121
|
+
const data = response;
|
|
122
|
+
return {
|
|
123
|
+
success: data.success,
|
|
124
|
+
phase: data.phase,
|
|
125
|
+
status: (_e = data.status_code) !== null && _e !== void 0 ? _e : null,
|
|
126
|
+
data: data.data,
|
|
127
|
+
dataBase64: (_f = data.data_base64) !== null && _f !== void 0 ? _f : null,
|
|
128
|
+
contentType: (_g = data.content_type) !== null && _g !== void 0 ? _g : null,
|
|
129
|
+
headers: (_h = data.headers) !== null && _h !== void 0 ? _h : {},
|
|
130
|
+
creditsCharged: (_j = data.credits_charged) !== null && _j !== void 0 ? _j : 0,
|
|
71
131
|
};
|
|
72
132
|
}
|
|
73
133
|
/**
|
|
@@ -84,7 +144,7 @@ export function createUserConnectorsModule(axios, appId) {
|
|
|
84
144
|
if (!connectorId || typeof connectorId !== "string") {
|
|
85
145
|
throw new Error("Connector ID is required and must be a string");
|
|
86
146
|
}
|
|
87
|
-
const response = await axios.post(`/apps/${appId}/app-user-auth/connectors/${connectorId}/initiate`);
|
|
147
|
+
const response = await axios.post(`/apps/${appId}/app-user-auth/connectors/${encodeURIComponent(connectorId)}/initiate`);
|
|
88
148
|
const data = response;
|
|
89
149
|
return data.redirect_url;
|
|
90
150
|
},
|
|
@@ -92,7 +152,7 @@ export function createUserConnectorsModule(axios, appId) {
|
|
|
92
152
|
if (!connectorId || typeof connectorId !== "string") {
|
|
93
153
|
throw new Error("Connector ID is required and must be a string");
|
|
94
154
|
}
|
|
95
|
-
await axios.delete(`/apps/${appId}/app-user-auth/connectors/${connectorId}`);
|
|
155
|
+
await axios.delete(`/apps/${appId}/app-user-auth/connectors/${encodeURIComponent(connectorId)}`);
|
|
96
156
|
},
|
|
97
157
|
};
|
|
98
158
|
}
|
|
@@ -41,6 +41,81 @@ export interface AppUserConnectorConnectionResponse {
|
|
|
41
41
|
/** Key-value configuration for the connection, or `null` if the connector does not provide one. */
|
|
42
42
|
connectionConfig: Record<string, string> | null;
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* How far a metered connector call progressed through the Base44 proxy.
|
|
46
|
+
*
|
|
47
|
+
* Only `not_sent` proves that the provider did not execute the request.
|
|
48
|
+
* `timed_out` and `sent_unconfirmed` may have executed upstream, so do not
|
|
49
|
+
* automatically retry non-idempotent requests based on those phases.
|
|
50
|
+
*/
|
|
51
|
+
export type ConnectorApiResponsePhase = "not_sent" | "responded" | "timed_out" | "sent_unconfirmed";
|
|
52
|
+
/**
|
|
53
|
+
* A request to forward to a metered connector's API through the Base44 proxy.
|
|
54
|
+
*/
|
|
55
|
+
export interface ConnectorApiRequest {
|
|
56
|
+
/** HTTP method for the upstream request. Defaults to `'GET'`. */
|
|
57
|
+
method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD";
|
|
58
|
+
/**
|
|
59
|
+
* Which of the connector's API hosts to call, by the name it declares.
|
|
60
|
+
* Omit for its default host (the first one declared). Only relevant for
|
|
61
|
+
* connectors that expose more than one host.
|
|
62
|
+
*/
|
|
63
|
+
host?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Path relative to the connector's API root, starting with `/`, such as `'/2/tweets'`.
|
|
66
|
+
*
|
|
67
|
+
* Must not be an absolute URL. Query parameters may be included here or passed
|
|
68
|
+
* separately as {@link query}; either way they are forwarded and priced identically.
|
|
69
|
+
*/
|
|
70
|
+
path: string;
|
|
71
|
+
/** Query parameters. Merged into the request URL alongside any already present in {@link path}. */
|
|
72
|
+
query?: Record<string, string | number | boolean | Array<string | number>>;
|
|
73
|
+
/** Extra request headers. Only headers the connector explicitly allows are forwarded; the rest are dropped. */
|
|
74
|
+
headers?: Record<string, string>;
|
|
75
|
+
/** JSON request body. Ignored for `GET` and `HEAD`. */
|
|
76
|
+
body?: unknown;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The upstream API's response, as returned by the Base44 connector proxy.
|
|
80
|
+
*/
|
|
81
|
+
export interface ConnectorApiResponse<T = unknown> {
|
|
82
|
+
/** `true` only when the upstream API returned a 2xx status. Proxy and upstream errors are `false`. */
|
|
83
|
+
success: boolean;
|
|
84
|
+
/** How far the call progressed. Only `not_sent` proves the provider did not execute it. */
|
|
85
|
+
phase: ConnectorApiResponsePhase;
|
|
86
|
+
/** The upstream HTTP status code, or `null` when no response was received. */
|
|
87
|
+
status: number | null;
|
|
88
|
+
/**
|
|
89
|
+
* The parsed upstream response body, or proxy error details when no response
|
|
90
|
+
* was received. `null` when the response was binary — see {@link dataBase64}.
|
|
91
|
+
*/
|
|
92
|
+
data: T | null;
|
|
93
|
+
/**
|
|
94
|
+
* The response body base64-encoded, for the media types the connector declares
|
|
95
|
+
* as binary (images, PDFs). Set instead of {@link data}, never alongside it.
|
|
96
|
+
*/
|
|
97
|
+
dataBase64: string | null;
|
|
98
|
+
/** The response media type, set only alongside {@link dataBase64}. */
|
|
99
|
+
contentType: string | null;
|
|
100
|
+
/** The subset of upstream response headers the connector exposes, typically rate-limit counters. */
|
|
101
|
+
headers: Record<string, string>;
|
|
102
|
+
/** Integration credits billed to the workspace for this call. */
|
|
103
|
+
creditsCharged: number;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Raw proxy response shape. Mapped to {@link ConnectorApiResponse} before being returned.
|
|
107
|
+
* @internal
|
|
108
|
+
*/
|
|
109
|
+
export interface ConnectorProxyRawResponse {
|
|
110
|
+
success: boolean;
|
|
111
|
+
phase: ConnectorApiResponsePhase;
|
|
112
|
+
status_code: number | null;
|
|
113
|
+
data: unknown;
|
|
114
|
+
data_base64: string | null;
|
|
115
|
+
content_type: string | null;
|
|
116
|
+
headers: Record<string, string>;
|
|
117
|
+
credits_charged: number;
|
|
118
|
+
}
|
|
44
119
|
/**
|
|
45
120
|
* Connectors module for managing OAuth tokens for external services.
|
|
46
121
|
*
|
|
@@ -71,6 +146,18 @@ export interface AppUserConnectorConnectionResponse {
|
|
|
71
146
|
* 3. In a backend function, call {@linkcode getCurrentAppUserConnection | getCurrentAppUserConnection()} using the service role client (`base44.asServiceRole.connectors`) with the connector ID to retrieve the app user's token.
|
|
72
147
|
* 4. Use the returned `accessToken` to call the external service's API directly. Some connectors also return a `connectionConfig` with additional values such as a subdomain for building the API URL.
|
|
73
148
|
*
|
|
149
|
+
* ## Metered connectors
|
|
150
|
+
*
|
|
151
|
+
* A few [platform connectors](#shared-connectors) are backed by paid third-party APIs that charge Base44 per call. For those, the OAuth token is **not** available to your code — {@linkcode getConnection | getConnection()} rejects with a `403`. Call them with {@linkcode callApi | callApi()} instead: Base44 attaches the credential server-side, forwards the request, and bills your workspace's integration credits for the call.
|
|
152
|
+
*
|
|
153
|
+
* This applies to platform connectors only. A workspace-registered or app user connector runs on **your own** OAuth app, so the provider invoices you directly and there is nothing for Base44 to meter — those keep normal token access via {@linkcode getWorkspaceConnection | getWorkspaceConnection()} and {@linkcode getCurrentAppUserConnection | getCurrentAppUserConnection()}.
|
|
154
|
+
*
|
|
155
|
+
* Two things to keep in mind when writing against a metered connector:
|
|
156
|
+
*
|
|
157
|
+
* - **Cost varies by endpoint, sometimes sharply.** The same connector can charge two orders of magnitude more for one endpoint than another, so avoid putting an expensive call inside a loop and batch wherever the provider supports it. Each response reports what it actually cost as `creditsCharged`.
|
|
158
|
+
* - **Provider and transport outcomes are returned, not thrown.** A provider `4xx`/`5xx` or a connection failure comes back as `success: false` with its `phase`; authorization, quota, and invalid proxy requests reject the promise.
|
|
159
|
+
* - **Only `phase: 'not_sent'` proves the provider did not execute the request.** A timeout or in-flight failure may have executed upstream, so do not automatically retry a non-idempotent call unless the provider supports an idempotency key.
|
|
160
|
+
*
|
|
74
161
|
* ## Available connectors
|
|
75
162
|
*
|
|
76
163
|
* The connectors below can be used as shared connectors or as app user connectors. For a shared platform connector, pass the integration type string to {@linkcode getConnection | getConnection()}. For a connector you register in Workspace Settings with your own OAuth app, use the connector ID with {@linkcode getWorkspaceConnection | getWorkspaceConnection()} for a shared token, or with {@linkcode getCurrentAppUserConnection | getCurrentAppUserConnection()} for a per-user token.
|
|
@@ -328,6 +415,41 @@ export interface ConnectorsModule {
|
|
|
328
415
|
* ```
|
|
329
416
|
*/
|
|
330
417
|
getCurrentAppUserConnection(connectorId: string): Promise<AppUserConnectorConnectionResponse>;
|
|
418
|
+
/**
|
|
419
|
+
* Calls a [metered connector's](#metered-connectors) API through the Base44 proxy.
|
|
420
|
+
*
|
|
421
|
+
* Use this for a shared platform connector identified by an integration type. Base44 adds the OAuth credential to the outgoing request, forwards it, and bills the workspace for the call, so you never handle the token yourself.
|
|
422
|
+
*
|
|
423
|
+
* @param integrationType - The type of integration, such as `'x'`. See [Available connectors](#available-connectors).
|
|
424
|
+
* @param request - The upstream request to forward. See {@link ConnectorApiRequest}.
|
|
425
|
+
* @returns Promise resolving to a {@link ConnectorApiResponse}. Note that an upstream error is reported in `success` and `status`, not thrown — only Base44-side failures reject.
|
|
426
|
+
*
|
|
427
|
+
* @example
|
|
428
|
+
* ```typescript
|
|
429
|
+
* // Post to X
|
|
430
|
+
* const res = await base44.asServiceRole.connectors.callApi('x', {
|
|
431
|
+
* method: 'POST',
|
|
432
|
+
* path: '/2/tweets',
|
|
433
|
+
* body: { text: 'Shipped!' },
|
|
434
|
+
* });
|
|
435
|
+
*
|
|
436
|
+
* if (!res.success) {
|
|
437
|
+
* console.error('X rejected the post', res.status, res.data);
|
|
438
|
+
* }
|
|
439
|
+
* ```
|
|
440
|
+
*
|
|
441
|
+
* @example
|
|
442
|
+
* ```typescript
|
|
443
|
+
* // Read, with query parameters and a look at what the call cost
|
|
444
|
+
* const res = await base44.asServiceRole.connectors.callApi('x', {
|
|
445
|
+
* path: '/2/tweets/search/recent',
|
|
446
|
+
* query: { query: 'base44', max_results: 10 },
|
|
447
|
+
* });
|
|
448
|
+
*
|
|
449
|
+
* console.log(`${res.creditsCharged} credits`, res.data);
|
|
450
|
+
* ```
|
|
451
|
+
*/
|
|
452
|
+
callApi<T = unknown>(integrationType: ConnectorIntegrationType, request: ConnectorApiRequest): Promise<ConnectorApiResponse<T>>;
|
|
331
453
|
}
|
|
332
454
|
/**
|
|
333
455
|
* User-scoped connectors module for managing app user OAuth connections.
|
|
@@ -43,9 +43,9 @@ export interface InvokeLLMParams {
|
|
|
43
43
|
prompt: string;
|
|
44
44
|
/** Optionally specify a model to override the app-level model setting for this specific call.
|
|
45
45
|
*
|
|
46
|
-
* Options: `"gpt_5_mini"`, `"gemini_3_flash"`, `"gpt_5_4"`, `"
|
|
46
|
+
* Options: `"gpt_5_mini"`, `"gemini_3_flash"`, `"gpt_5_4"`, `"gpt_5_6_sol"`, `"gpt_5_6_luna"`, `"gemini_3_1_pro"`, `"claude_sonnet_4_6"`, `"claude_opus_4_6"`, `"claude_opus_4_7"`, `"claude_opus_4_8"`, `"claude-sonnet-5"`
|
|
47
47
|
*/
|
|
48
|
-
model?: 'gpt_5_mini' | 'gemini_3_flash' | 'gpt_5_4' | '
|
|
48
|
+
model?: 'gpt_5_mini' | 'gemini_3_flash' | 'gpt_5_4' | 'gpt_5_6_sol' | 'gpt_5_6_luna' | 'gemini_3_1_pro' | 'claude_sonnet_4_6' | 'claude_opus_4_6' | 'claude_opus_4_7' | 'claude_opus_4_8' | 'claude-sonnet-5';
|
|
49
49
|
/** If set to `true`, the LLM will use Google Search, Maps, and News to gather real-time context before answering.
|
|
50
50
|
* @default false
|
|
51
51
|
*/
|
|
@@ -132,8 +132,10 @@ export function createAxiosClient({ baseURL, headers = {}, token, interceptRespo
|
|
|
132
132
|
client.interceptors.request.use((config) => {
|
|
133
133
|
// `window.location` is absent on React Native (where `window` still exists),
|
|
134
134
|
// so guard on it before reading `.href`.
|
|
135
|
-
if (typeof window !== "undefined"
|
|
136
|
-
|
|
135
|
+
if (typeof window !== "undefined") {
|
|
136
|
+
if (window.location) {
|
|
137
|
+
config.headers.set("X-Origin-URL", window.location.href);
|
|
138
|
+
}
|
|
137
139
|
// On unauthenticated requests, attach a stable anonymous visitor id so the
|
|
138
140
|
// backend can support anonymous agent access (conversation grouping + ownership).
|
|
139
141
|
// Authenticated requests are identified by their Authorization header instead.
|
|
@@ -185,11 +187,11 @@ export function createAxiosClient({ baseURL, headers = {}, token, interceptRespo
|
|
|
185
187
|
}
|
|
186
188
|
return response.data;
|
|
187
189
|
}, (error) => {
|
|
188
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
190
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
189
191
|
const message = ((_b = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.message) ||
|
|
190
192
|
((_d = (_c = error.response) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d.detail) ||
|
|
191
193
|
error.message;
|
|
192
|
-
const base44Error = new Base44Error(message, (_e = error.response) === null || _e === void 0 ? void 0 : _e.status, (_g = (_f = error.response) === null || _f === void 0 ? void 0 : _f.data) === null || _g === void 0 ? void 0 : _g.code, (
|
|
194
|
+
const base44Error = new Base44Error(message, (_e = error.response) === null || _e === void 0 ? void 0 : _e.status, (_m = (_h = (_g = (_f = error.response) === null || _f === void 0 ? void 0 : _f.data) === null || _g === void 0 ? void 0 : _g.code) !== null && _h !== void 0 ? _h : (_l = (_k = (_j = error.response) === null || _j === void 0 ? void 0 : _j.headers) === null || _k === void 0 ? void 0 : _k.get) === null || _l === void 0 ? void 0 : _l.call(_k, "x-base44-connector-error")) !== null && _m !== void 0 ? _m : (_p = (_o = error.response) === null || _o === void 0 ? void 0 : _o.headers) === null || _p === void 0 ? void 0 : _p["x-base44-connector-error"], (_q = error.response) === null || _q === void 0 ? void 0 : _q.data, error);
|
|
193
195
|
// Log errors in development
|
|
194
196
|
if (process.env.NODE_ENV !== "production") {
|
|
195
197
|
safeErrorLog("[Base44 SDK Error]", base44Error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@depup/base44__sdk",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.45-depup.0",
|
|
4
4
|
"description": "JavaScript SDK for Base44 API (with updated dependencies)",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
"create-docs:process": "node scripts/mintlify-post-processing/file-processing/file-processing.js"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"axios": "^1.
|
|
30
|
+
"axios": "^1.20.0",
|
|
31
31
|
"partysocket": "^1.3.0",
|
|
32
32
|
"socket.io-client": "^4.8.3",
|
|
33
|
-
"uuid": "^14.0.
|
|
33
|
+
"uuid": "^14.0.2"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/hast": "^3.0.4",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"changes": {
|
|
76
76
|
"axios": {
|
|
77
77
|
"from": "^1.18.1",
|
|
78
|
-
"to": "^1.
|
|
78
|
+
"to": "^1.20.0"
|
|
79
79
|
},
|
|
80
80
|
"partysocket": {
|
|
81
81
|
"from": "^0.0.23",
|
|
@@ -83,13 +83,13 @@
|
|
|
83
83
|
},
|
|
84
84
|
"uuid": {
|
|
85
85
|
"from": "^13.0.2",
|
|
86
|
-
"to": "^14.0.
|
|
86
|
+
"to": "^14.0.2"
|
|
87
87
|
}
|
|
88
88
|
},
|
|
89
89
|
"depsUpdated": 3,
|
|
90
90
|
"originalPackage": "@base44/sdk",
|
|
91
|
-
"originalVersion": "0.8.
|
|
92
|
-
"processedAt": "2026-
|
|
91
|
+
"originalVersion": "0.8.45",
|
|
92
|
+
"processedAt": "2026-09-02T08:12:22.602Z",
|
|
93
93
|
"smokeTest": "passed"
|
|
94
94
|
}
|
|
95
95
|
}
|