@base44-preview/sdk 0.8.36-pr.212.71a10c2 → 0.8.36-pr.223.4029308
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 +1 -28
- package/dist/client.types.d.ts +0 -22
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -1
- package/dist/modules/analytics.js +10 -3
- package/dist/utils/axios-client.js +3 -1
- package/dist/utils/common.d.ts +1 -0
- package/dist/utils/common.js +5 -0
- package/package.json +1 -2
- package/dist/modules/realtime.d.ts +0 -21
- package/dist/modules/realtime.js +0 -90
- package/dist/modules/realtime.types.d.ts +0 -77
- package/dist/modules/realtime.types.js +0 -1
- package/dist/realtime-handler.d.ts +0 -85
- package/dist/realtime-handler.js +0 -78
package/dist/client.js
CHANGED
|
@@ -12,7 +12,6 @@ import { createAppLogsModule } from "./modules/app-logs.js";
|
|
|
12
12
|
import { createUsersModule } from "./modules/users.js";
|
|
13
13
|
import { RoomsSocket } from "./utils/socket-utils.js";
|
|
14
14
|
import { createAnalyticsModule } from "./modules/analytics.js";
|
|
15
|
-
import { createRealtimeModule } from "./modules/realtime.js";
|
|
16
15
|
/**
|
|
17
16
|
* Creates a Base44 client.
|
|
18
17
|
*
|
|
@@ -52,19 +51,9 @@ import { createRealtimeModule } from "./modules/realtime.js";
|
|
|
52
51
|
*/
|
|
53
52
|
export function createClient(config) {
|
|
54
53
|
var _a, _b;
|
|
55
|
-
const { serverUrl = "https://base44.app", appId, token, serviceToken, requiresAuth = false, appBaseUrl, options, functionsVersion, headers: optionalHeaders,
|
|
54
|
+
const { serverUrl = "https://base44.app", appId, token, serviceToken, requiresAuth = false, appBaseUrl, options, functionsVersion, headers: optionalHeaders, } = config;
|
|
56
55
|
// Normalize appBaseUrl to always be a string (empty if not provided or invalid)
|
|
57
56
|
const normalizedAppBaseUrl = typeof appBaseUrl === "string" ? appBaseUrl : "";
|
|
58
|
-
// Derive the dispatcher WebSocket URL from serverUrl if not explicitly provided.
|
|
59
|
-
// Convert https:// → wss:// (or http:// → ws://) and strip trailing slash.
|
|
60
|
-
const resolvedDispatcherWsUrl = (() => {
|
|
61
|
-
if (dispatcherWsUrl)
|
|
62
|
-
return dispatcherWsUrl.replace(/\/$/, "");
|
|
63
|
-
return serverUrl
|
|
64
|
-
.replace(/\/$/, "")
|
|
65
|
-
.replace(/^https:\/\//, "wss://")
|
|
66
|
-
.replace(/^http:\/\//, "ws://");
|
|
67
|
-
})();
|
|
68
57
|
const socketConfig = {
|
|
69
58
|
serverUrl,
|
|
70
59
|
mountPath: "/ws-user-apps/socket.io/",
|
|
@@ -171,22 +160,6 @@ export function createClient(config) {
|
|
|
171
160
|
appId,
|
|
172
161
|
userAuthModule,
|
|
173
162
|
}),
|
|
174
|
-
realtime: createRealtimeModule({
|
|
175
|
-
appId,
|
|
176
|
-
dispatcherWsUrl: resolvedDispatcherWsUrl,
|
|
177
|
-
webSocketImpl,
|
|
178
|
-
getToken: async (handlerName, instanceId, connId) => {
|
|
179
|
-
// axiosClient interceptors unwrap response.data, so the result is the body directly.
|
|
180
|
-
// conn_id rides inside the signed token (not a WS query param) so it survives
|
|
181
|
-
// proxies that strip params; the dispatcher forwards it as the handler's conn.id.
|
|
182
|
-
// Base44-Functions-Version rides along (like function calls) so live apps get
|
|
183
|
-
// tokens for the *published* realtime script and previews get the draft.
|
|
184
|
-
const data = await axiosClient.post(`/apps/${appId}/realtime-token`, { handler_name: handlerName, instance_id: instanceId, conn_id: connId }, functionsVersion
|
|
185
|
-
? { headers: { "Base44-Functions-Version": functionsVersion } }
|
|
186
|
-
: undefined);
|
|
187
|
-
return data.token;
|
|
188
|
-
},
|
|
189
|
-
}),
|
|
190
163
|
cleanup: () => {
|
|
191
164
|
userModules.analytics.cleanup();
|
|
192
165
|
if (socket) {
|
package/dist/client.types.d.ts
CHANGED
|
@@ -8,7 +8,6 @@ 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
10
|
import type { AnalyticsModule } from "./modules/analytics.types.js";
|
|
11
|
-
import type { RealtimeModule } from "./modules/realtime.types.js";
|
|
12
11
|
/**
|
|
13
12
|
* Options for creating a Base44 client.
|
|
14
13
|
*/
|
|
@@ -74,25 +73,6 @@ export interface CreateClientConfig {
|
|
|
74
73
|
* Additional client options.
|
|
75
74
|
*/
|
|
76
75
|
options?: CreateClientOptions;
|
|
77
|
-
/**
|
|
78
|
-
* Base WebSocket URL for the Cloudflare Durable Object dispatcher.
|
|
79
|
-
*
|
|
80
|
-
* Defaults to the `serverUrl` with `https://` replaced by `wss://` (or `http://` by `ws://`).
|
|
81
|
-
* Override when the dispatcher lives at a different host than the API.
|
|
82
|
-
*/
|
|
83
|
-
dispatcherWsUrl?: string;
|
|
84
|
-
/**
|
|
85
|
-
* WebSocket implementation for realtime subscriptions in environments
|
|
86
|
-
* without a global `WebSocket` (Node.js < 22). Browsers and Node ≥ 22
|
|
87
|
-
* don't need this.
|
|
88
|
-
*
|
|
89
|
-
* @example
|
|
90
|
-
* ```typescript
|
|
91
|
-
* import WS from "ws";
|
|
92
|
-
* const base44 = createClient({ appId, webSocketImpl: WS });
|
|
93
|
-
* ```
|
|
94
|
-
*/
|
|
95
|
-
webSocketImpl?: unknown;
|
|
96
76
|
}
|
|
97
77
|
/**
|
|
98
78
|
* The Base44 client instance.
|
|
@@ -108,8 +88,6 @@ export interface Base44Client {
|
|
|
108
88
|
analytics: AnalyticsModule;
|
|
109
89
|
/** {@link AppLogsModule | App logs module} for tracking app usage. */
|
|
110
90
|
appLogs: AppLogsModule;
|
|
111
|
-
/** {@link RealtimeModule | Realtime module} for subscribing to and sending messages via Cloudflare Durable Object-backed RealtimeHandlers. */
|
|
112
|
-
realtime: RealtimeModule;
|
|
113
91
|
/** {@link AuthModule | Auth module} for user authentication and management. */
|
|
114
92
|
auth: AuthModule;
|
|
115
93
|
/** {@link UserConnectorsModule | Connectors module} for app-user OAuth flows. */
|
package/dist/index.d.ts
CHANGED
|
@@ -11,9 +11,7 @@ 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 { RealtimeModule, RealtimeHandlerClient, RealtimeHandlerNameRegistry, RealtimeHandlerRegistry, } from "./modules/realtime.types.js";
|
|
15
14
|
export type { SsoModule, SsoAccessTokenResponse } from "./modules/sso.types.js";
|
|
16
|
-
export { RealtimeHandler, type Conn } from "./realtime-handler.js";
|
|
17
15
|
export type { ConnectorsModule, UserConnectorsModule, } from "./modules/connectors.types.js";
|
|
18
16
|
export type { CustomIntegrationsModule, CustomIntegrationCallParams, CustomIntegrationCallResponse, } from "./modules/custom-integrations.types.js";
|
|
19
17
|
export type { GetAccessTokenOptions, SaveAccessTokenOptions, RemoveAccessTokenOptions, GetLoginUrlOptions, } from "./utils/auth-utils.types.js";
|
package/dist/index.js
CHANGED
|
@@ -3,4 +3,3 @@ import { Base44Error } from "./utils/axios-client.js";
|
|
|
3
3
|
import { getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl, } from "./utils/auth-utils.js";
|
|
4
4
|
export { createClient, createClientFromRequest, Base44Error, getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl, };
|
|
5
5
|
export * from "./types.js";
|
|
6
|
-
export { RealtimeHandler } from "./realtime-handler.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getSharedInstance } from "../utils/sharedInstance.js";
|
|
2
|
-
import { generateUuid } from "../utils/common.js";
|
|
2
|
+
import { generateUuid, isReactNative } from "../utils/common.js";
|
|
3
3
|
export const USER_HEARTBEAT_EVENT_NAME = "__user_heartbeat_event__";
|
|
4
4
|
export const ANALYTICS_INITIALIZATION_EVENT_NAME = "__initialization_event__";
|
|
5
5
|
export const ANALYTICS_SESSION_DURATION_EVENT_NAME = "__session_duration_event__";
|
|
@@ -34,7 +34,11 @@ export const createAnalyticsModule = ({ axiosClient, serverUrl, appId, userAuthM
|
|
|
34
34
|
var _a;
|
|
35
35
|
// prevent overflow of events //
|
|
36
36
|
const { maxQueueSize, throttleTime, batchSize } = analyticsSharedState.config;
|
|
37
|
-
|
|
37
|
+
// Disable analytics on React Native. It defines `window` but not `document`,
|
|
38
|
+
// so the per-callsite `typeof window` guards below aren't enough to keep it
|
|
39
|
+
// from touching `document` (e.g. `document.referrer` on init). Node/SSR is
|
|
40
|
+
// still handled by those `window` guards, so this doesn't affect it.
|
|
41
|
+
if (!((_a = analyticsSharedState.config) === null || _a === void 0 ? void 0 : _a.enabled) || isReactNative) {
|
|
38
42
|
return {
|
|
39
43
|
track: () => { },
|
|
40
44
|
cleanup: () => { },
|
|
@@ -242,7 +246,10 @@ async function getSessionContext(userAuthModule) {
|
|
|
242
246
|
return analyticsSharedState.sessionContext;
|
|
243
247
|
}
|
|
244
248
|
export function getAnalyticsConfigFromUrlParams() {
|
|
245
|
-
|
|
249
|
+
// `window.location` is absent on React Native. This runs at module load (via
|
|
250
|
+
// the shared-state factory), so an unguarded `window.location.search` would
|
|
251
|
+
// throw on import there.
|
|
252
|
+
if (typeof window === "undefined" || !window.location)
|
|
246
253
|
return undefined;
|
|
247
254
|
const urlParams = new URLSearchParams(window.location.search);
|
|
248
255
|
const analyticsEnable = urlParams.get(ANALYTICS_CONFIG_ENABLE_URL_PARAM_KEY);
|
|
@@ -130,7 +130,9 @@ export function createAxiosClient({ baseURL, headers = {}, token, interceptRespo
|
|
|
130
130
|
}
|
|
131
131
|
// Add origin URL in browser environment
|
|
132
132
|
client.interceptors.request.use((config) => {
|
|
133
|
-
|
|
133
|
+
// `window.location` is absent on React Native (where `window` still exists),
|
|
134
|
+
// so guard on it before reading `.href`.
|
|
135
|
+
if (typeof window !== "undefined" && window.location) {
|
|
134
136
|
config.headers.set("X-Origin-URL", window.location.href);
|
|
135
137
|
// On unauthenticated requests, attach a stable anonymous visitor id so the
|
|
136
138
|
// backend can support anonymous agent access (conversation grouping + ownership).
|
package/dist/utils/common.d.ts
CHANGED
package/dist/utils/common.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
export const isNode = typeof window === "undefined";
|
|
2
2
|
export const isInIFrame = !isNode && window.self !== window.top;
|
|
3
|
+
// React Native defines `window` (so `isNode` is false there) but not `document`.
|
|
4
|
+
// Browser-only code paths gated on `window`/`isNode` alone would run — and crash —
|
|
5
|
+
// on React Native. Node (no `window`) is already handled by those `window` guards;
|
|
6
|
+
// this flags the window-without-a-DOM case that isn't.
|
|
7
|
+
export const isReactNative = !isNode && typeof document === "undefined";
|
|
3
8
|
export const generateUuid = () => {
|
|
4
9
|
return (Math.random().toString(36).substring(2, 15) +
|
|
5
10
|
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.36-pr.
|
|
3
|
+
"version": "0.8.36-pr.223.4029308",
|
|
4
4
|
"description": "JavaScript SDK for Base44 API",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -27,7 +27,6 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"axios": "^1.17.0",
|
|
30
|
-
"partysocket": "^0.0.23",
|
|
31
30
|
"socket.io-client": "^4.8.3",
|
|
32
31
|
"uuid": "^13.0.2"
|
|
33
32
|
},
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export declare function createRealtimeModule(config: {
|
|
2
|
-
appId: string;
|
|
3
|
-
getToken(handlerName: string, instanceId: string, connId: string): Promise<string>;
|
|
4
|
-
dispatcherWsUrl: string;
|
|
5
|
-
/** WebSocket implementation for runtimes without a global one (Node < 22). */
|
|
6
|
-
webSocketImpl?: unknown;
|
|
7
|
-
}): Record<string, RealtimeHandler>;
|
|
8
|
-
/** Handle for an active realtime subscription. */
|
|
9
|
-
interface RealtimeSubscription {
|
|
10
|
-
/** This connection's id — the same value the handler receives as `conn.id`. */
|
|
11
|
-
id: string;
|
|
12
|
-
/** Close the subscription and its underlying socket. */
|
|
13
|
-
unsubscribe(): void;
|
|
14
|
-
}
|
|
15
|
-
interface RealtimeHandler {
|
|
16
|
-
subscribe(instanceId: string, callback: (data: unknown) => void, options?: {
|
|
17
|
-
id?: string;
|
|
18
|
-
}): RealtimeSubscription;
|
|
19
|
-
send(instanceId: string, data: unknown): void;
|
|
20
|
-
}
|
|
21
|
-
export {};
|
package/dist/modules/realtime.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import PartySocket from "partysocket";
|
|
2
|
-
// Module-level map: "HandlerName:instanceId" → active socket
|
|
3
|
-
const activeSockets = new Map();
|
|
4
|
-
function socketKey(handlerName, instanceId) {
|
|
5
|
-
return `${handlerName}:${instanceId}`;
|
|
6
|
-
}
|
|
7
|
-
export function createRealtimeModule(config) {
|
|
8
|
-
return new Proxy({}, {
|
|
9
|
-
get(_, handlerName) {
|
|
10
|
-
return {
|
|
11
|
-
subscribe(instanceId, callback, options) {
|
|
12
|
-
var _a, _b;
|
|
13
|
-
const key = socketKey(handlerName, instanceId);
|
|
14
|
-
// close existing if any
|
|
15
|
-
(_a = activeSockets.get(key)) === null || _a === void 0 ? void 0 : _a.close();
|
|
16
|
-
// Connection id: caller-supplied (stable — reuse across reconnects/tabs as
|
|
17
|
-
// you see fit) or auto-generated per subscription. It travels INSIDE the
|
|
18
|
-
// signed realtime token (never as a WS query param, which proxies strip);
|
|
19
|
-
// the dispatcher forwards the verified claim as partyserver's _pk, so the
|
|
20
|
-
// handler sees this exact value as conn.id. Reconnects re-mint the token
|
|
21
|
-
// with the same id, so conn.id is stable across reconnects.
|
|
22
|
-
const connId = (_b = options === null || options === void 0 ? void 0 : options.id) !== null && _b !== void 0 ? _b : crypto.randomUUID();
|
|
23
|
-
// query as async fn: called on every (re)connect, fetches a fresh token each time
|
|
24
|
-
const ws = new PartySocket({
|
|
25
|
-
host: config.dispatcherWsUrl,
|
|
26
|
-
party: handlerName,
|
|
27
|
-
room: instanceId,
|
|
28
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
-
...(config.webSocketImpl ? { WebSocket: config.webSocketImpl } : {}),
|
|
30
|
-
query: () => config.getToken(handlerName, instanceId, connId).then((token) => ({ token })),
|
|
31
|
-
});
|
|
32
|
-
activeSockets.set(key, ws);
|
|
33
|
-
// Heartbeat / half-open detection. PartySocket only reconnects on a
|
|
34
|
-
// browser close/error event, so a silently-dead connection (TCP alive,
|
|
35
|
-
// no data — common behind proxies/LBs) hangs until the OS idle timeout
|
|
36
|
-
// (~60s). We ping periodically and force a reconnect if nothing comes
|
|
37
|
-
// back within DEAD_MS, cutting detection from ~60s to a few seconds.
|
|
38
|
-
const PING_MS = 1000;
|
|
39
|
-
const DEAD_MS = 3000;
|
|
40
|
-
let lastMsg = Date.now();
|
|
41
|
-
const bumpAlive = () => { lastMsg = Date.now(); };
|
|
42
|
-
ws.addEventListener("open", bumpAlive);
|
|
43
|
-
ws.addEventListener("message", (ev) => {
|
|
44
|
-
bumpAlive();
|
|
45
|
-
let data;
|
|
46
|
-
try {
|
|
47
|
-
data = JSON.parse(ev.data);
|
|
48
|
-
}
|
|
49
|
-
catch (_a) {
|
|
50
|
-
return; // ignore malformed
|
|
51
|
-
}
|
|
52
|
-
// Swallow platform messages — never surface them to the app.
|
|
53
|
-
const msgType = data && typeof data === "object" ? data.type : undefined;
|
|
54
|
-
if (msgType === "__pong")
|
|
55
|
-
return;
|
|
56
|
-
callback(data);
|
|
57
|
-
});
|
|
58
|
-
const heartbeat = setInterval(() => {
|
|
59
|
-
if (Date.now() - lastMsg > DEAD_MS) {
|
|
60
|
-
bumpAlive(); // avoid a reconnect storm while the new socket comes up
|
|
61
|
-
ws.reconnect();
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
try {
|
|
65
|
-
ws.send(JSON.stringify({ type: "__ping" }));
|
|
66
|
-
}
|
|
67
|
-
catch (_a) {
|
|
68
|
-
// socket not open; the watchdog above will force a reconnect
|
|
69
|
-
}
|
|
70
|
-
}, PING_MS);
|
|
71
|
-
return {
|
|
72
|
-
id: connId, // the connection id (same value the handler sees as conn.id)
|
|
73
|
-
unsubscribe() {
|
|
74
|
-
clearInterval(heartbeat);
|
|
75
|
-
activeSockets.delete(key);
|
|
76
|
-
ws.close();
|
|
77
|
-
},
|
|
78
|
-
};
|
|
79
|
-
},
|
|
80
|
-
send(instanceId, data) {
|
|
81
|
-
const key = socketKey(handlerName, instanceId);
|
|
82
|
-
const ws = activeSockets.get(key);
|
|
83
|
-
if (!ws)
|
|
84
|
-
throw new Error(`No active subscription for ${handlerName}:${instanceId}`);
|
|
85
|
-
ws.send(JSON.stringify(data));
|
|
86
|
-
},
|
|
87
|
-
};
|
|
88
|
-
},
|
|
89
|
-
});
|
|
90
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Extend this interface to add typed `subscribe` callbacks and `send` payloads
|
|
3
|
-
* for your deployed RealtimeHandlers.
|
|
4
|
-
*
|
|
5
|
-
* This is separate from {@link RealtimeHandlerNameRegistry} (which is auto-generated
|
|
6
|
-
* by `base44 types generate`), so there are no conflicts.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```typescript
|
|
10
|
-
* declare module "@base44/sdk" {
|
|
11
|
-
* interface RealtimeHandlerRegistry {
|
|
12
|
-
* ChatRoom: {
|
|
13
|
-
* toClient: { type: "joined" | "left" | "message"; userId?: string; from?: string; text?: string };
|
|
14
|
-
* toServer: { type: "message"; text: string };
|
|
15
|
-
* };
|
|
16
|
-
* }
|
|
17
|
-
* }
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
export interface RealtimeHandlerRegistry {
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Auto-populated by `base44 types generate` with the names of your deployed handlers.
|
|
24
|
-
* Do not edit this interface manually — use {@link RealtimeHandlerRegistry} for message types.
|
|
25
|
-
*/
|
|
26
|
-
export interface RealtimeHandlerNameRegistry {
|
|
27
|
-
}
|
|
28
|
-
type AllHandlerNames = keyof RealtimeHandlerRegistry | keyof RealtimeHandlerNameRegistry;
|
|
29
|
-
type ToClientFor<N extends string> = N extends keyof RealtimeHandlerRegistry ? RealtimeHandlerRegistry[N] extends {
|
|
30
|
-
toClient: infer I;
|
|
31
|
-
} ? I : unknown : unknown;
|
|
32
|
-
type ToServerFor<N extends string> = N extends keyof RealtimeHandlerRegistry ? RealtimeHandlerRegistry[N] extends {
|
|
33
|
-
toServer: infer O;
|
|
34
|
-
} ? O : unknown : unknown;
|
|
35
|
-
/**
|
|
36
|
-
* Client for a single named RealtimeHandler.
|
|
37
|
-
* Typed automatically when the handler is registered in {@link RealtimeHandlerRegistry}.
|
|
38
|
-
*/
|
|
39
|
-
export interface RealtimeHandlerClient<N extends string = string> {
|
|
40
|
-
/**
|
|
41
|
-
* Open a WebSocket subscription. Returns a {@link RealtimeSubscription} with the
|
|
42
|
-
* connection `id` (same value the handler sees as `conn.id`) and an `unsubscribe()` method.
|
|
43
|
-
*
|
|
44
|
-
* Pass `options.id` to control the connection id (e.g. a stable per-tab id so a
|
|
45
|
-
* reconnect reuses the same server-side connection); omit it for an auto-generated
|
|
46
|
-
* per-connection id.
|
|
47
|
-
*/
|
|
48
|
-
subscribe(instanceId: string, callback: (data: ToClientFor<N>) => void, options?: {
|
|
49
|
-
id?: string;
|
|
50
|
-
}): RealtimeSubscription;
|
|
51
|
-
/** Send a message over the open socket. Throws if not subscribed. */
|
|
52
|
-
send(instanceId: string, data: ToServerFor<N>): void;
|
|
53
|
-
}
|
|
54
|
-
/** Handle for an active realtime subscription. */
|
|
55
|
-
export interface RealtimeSubscription {
|
|
56
|
-
/** This connection's id — the same value the handler receives as `conn.id`. */
|
|
57
|
-
id: string;
|
|
58
|
-
/** Close the subscription and its underlying socket. */
|
|
59
|
-
unsubscribe(): void;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* The realtime module provides access to Cloudflare Durable Object-backed
|
|
63
|
-
* RealtimeHandlers deployed by the Base44 platform.
|
|
64
|
-
*
|
|
65
|
-
* Handler names are accessed as dynamic properties on this module:
|
|
66
|
-
* ```typescript
|
|
67
|
-
* const sub = await base44.realtime.MyHandler.subscribe("room-1", (msg) => {
|
|
68
|
-
* console.log(msg); // typed if MyHandler is in RealtimeHandlerRegistry
|
|
69
|
-
* });
|
|
70
|
-
* const { id, unsubscribe } = sub;
|
|
71
|
-
* unsubscribe();
|
|
72
|
-
* ```
|
|
73
|
-
*/
|
|
74
|
-
export type RealtimeModule = {
|
|
75
|
-
[K in AllHandlerNames]: K extends keyof RealtimeHandlerRegistry ? RealtimeHandlerClient<string & K> : RealtimeHandlerClient;
|
|
76
|
-
} & Record<string, RealtimeHandlerClient>;
|
|
77
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Type-only base class for Realtime Handlers.
|
|
3
|
-
*
|
|
4
|
-
* Import and extend this in your handler files:
|
|
5
|
-
* import { RealtimeHandler } from "@base44/sdk";
|
|
6
|
-
* export class MyHandler extends RealtimeHandler { ... }
|
|
7
|
-
*
|
|
8
|
-
* At deploy time the bundler replaces this import with the compiled
|
|
9
|
-
* Cloudflare Durable Object implementation — this file provides types only.
|
|
10
|
-
*/
|
|
11
|
-
import type { Base44Client } from "./client.types.js";
|
|
12
|
-
/**
|
|
13
|
-
* A single client connection. `Send` is the message type this connection accepts
|
|
14
|
-
* via {@link send} — the handler's *outgoing* (server→client) messages.
|
|
15
|
-
*/
|
|
16
|
-
export interface Conn<Send = unknown> {
|
|
17
|
-
/** Unique per-connection id (one per socket/tab), the same value the client
|
|
18
|
-
* receives from `subscribe()`. Use this — not userId — to identify a distinct
|
|
19
|
-
* client, so multiple tabs of the same user are separate connections. */
|
|
20
|
-
id: string;
|
|
21
|
-
userId: string;
|
|
22
|
-
appId: string;
|
|
23
|
-
instanceId: string;
|
|
24
|
-
send(data: Send): void;
|
|
25
|
-
reject(code: number, reason: string): void;
|
|
26
|
-
}
|
|
27
|
-
export interface Storage {
|
|
28
|
-
get<T>(key: string): Promise<T | undefined>;
|
|
29
|
-
put(key: string, value: unknown): Promise<void>;
|
|
30
|
-
delete(key: string): Promise<boolean>;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Base class for a Realtime Handler.
|
|
34
|
-
*
|
|
35
|
-
* @typeParam Incoming - messages this handler *receives* from clients
|
|
36
|
-
* (`handleMessage`'s `msg`) — the schema's `toServer` section.
|
|
37
|
-
* @typeParam Outgoing - messages this handler *sends* to clients
|
|
38
|
-
* (`conn.send`/`broadcast`) — the schema's `toClient` section.
|
|
39
|
-
*
|
|
40
|
-
* With a generated `schema.jsonc`, wire both from the registry so they can't drift
|
|
41
|
-
* from the client's types:
|
|
42
|
-
* ```ts
|
|
43
|
-
* type Reg = RealtimeHandlerRegistry["MyHandler"];
|
|
44
|
-
* class MyHandler extends RealtimeHandler<Reg["toServer"], Reg["toClient"]> { ... }
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
|
-
export declare abstract class RealtimeHandler<Incoming = unknown, Outgoing = unknown> {
|
|
48
|
-
abstract handleConnect(conn: Conn<Outgoing>): void | Promise<void>;
|
|
49
|
-
abstract handleMessage(conn: Conn<Outgoing>, msg: Incoming): void | Promise<void>;
|
|
50
|
-
abstract handleClose(conn: Conn<Outgoing>): void | Promise<void>;
|
|
51
|
-
abstract handleTick(): void | Promise<void>;
|
|
52
|
-
onStart(): void | Promise<void>;
|
|
53
|
-
/**
|
|
54
|
-
* Managed ticker (opt-in). Override {@link shouldTick} and the platform runs
|
|
55
|
-
* {@link handleTick} on a timer of {@link tickIntervalMs} while it returns true,
|
|
56
|
-
* and stops (letting the Durable Object hibernate — no compute cost) when it
|
|
57
|
-
* returns false. The platform owns scheduling, rescheduling, self-heal, and
|
|
58
|
-
* error-safety — you don't call {@link startLoop}/{@link stopLoop}.
|
|
59
|
-
*
|
|
60
|
-
* Re-evaluated after every connect/message/close and on every tick, so keep it
|
|
61
|
-
* cheap and pure (no async, no side effects). Example: `return this.players >= 2`.
|
|
62
|
-
*/
|
|
63
|
-
protected tickIntervalMs: number;
|
|
64
|
-
protected shouldTick?(): boolean;
|
|
65
|
-
protected broadcast(_data: Outgoing): void;
|
|
66
|
-
protected getConnections(): Conn<Outgoing>[];
|
|
67
|
-
protected startLoop(_ms: number): Promise<void>;
|
|
68
|
-
protected stopLoop(): Promise<void>;
|
|
69
|
-
protected get instanceId(): string;
|
|
70
|
-
protected get storage(): Storage;
|
|
71
|
-
/**
|
|
72
|
-
* SDK client acting **as the connected user** — every entity call respects
|
|
73
|
-
* the app's row-level security, evaluated as that user at call time. The
|
|
74
|
-
* default wherever a `conn` is in scope (connect/message/close).
|
|
75
|
-
*
|
|
76
|
-
* Throws for anonymous connections (no signed-in user to act as).
|
|
77
|
-
*/
|
|
78
|
-
protected createUserClient(conn: Conn): Base44Client;
|
|
79
|
-
/**
|
|
80
|
-
* Service-role SDK client — bypasses RLS. For work with **no user in scope**
|
|
81
|
-
* (tick, alarm, onStart). Inside handleMessage/handleConnect prefer
|
|
82
|
-
* `createUserClient(conn)`.
|
|
83
|
-
*/
|
|
84
|
-
protected createServiceClient(): Base44Client;
|
|
85
|
-
}
|
package/dist/realtime-handler.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Type-only base class for Realtime Handlers.
|
|
3
|
-
*
|
|
4
|
-
* Import and extend this in your handler files:
|
|
5
|
-
* import { RealtimeHandler } from "@base44/sdk";
|
|
6
|
-
* export class MyHandler extends RealtimeHandler { ... }
|
|
7
|
-
*
|
|
8
|
-
* At deploy time the bundler replaces this import with the compiled
|
|
9
|
-
* Cloudflare Durable Object implementation — this file provides types only.
|
|
10
|
-
*/
|
|
11
|
-
/**
|
|
12
|
-
* Base class for a Realtime Handler.
|
|
13
|
-
*
|
|
14
|
-
* @typeParam Incoming - messages this handler *receives* from clients
|
|
15
|
-
* (`handleMessage`'s `msg`) — the schema's `toServer` section.
|
|
16
|
-
* @typeParam Outgoing - messages this handler *sends* to clients
|
|
17
|
-
* (`conn.send`/`broadcast`) — the schema's `toClient` section.
|
|
18
|
-
*
|
|
19
|
-
* With a generated `schema.jsonc`, wire both from the registry so they can't drift
|
|
20
|
-
* from the client's types:
|
|
21
|
-
* ```ts
|
|
22
|
-
* type Reg = RealtimeHandlerRegistry["MyHandler"];
|
|
23
|
-
* class MyHandler extends RealtimeHandler<Reg["toServer"], Reg["toClient"]> { ... }
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
export class RealtimeHandler {
|
|
27
|
-
constructor() {
|
|
28
|
-
/**
|
|
29
|
-
* Managed ticker (opt-in). Override {@link shouldTick} and the platform runs
|
|
30
|
-
* {@link handleTick} on a timer of {@link tickIntervalMs} while it returns true,
|
|
31
|
-
* and stops (letting the Durable Object hibernate — no compute cost) when it
|
|
32
|
-
* returns false. The platform owns scheduling, rescheduling, self-heal, and
|
|
33
|
-
* error-safety — you don't call {@link startLoop}/{@link stopLoop}.
|
|
34
|
-
*
|
|
35
|
-
* Re-evaluated after every connect/message/close and on every tick, so keep it
|
|
36
|
-
* cheap and pure (no async, no side effects). Example: `return this.players >= 2`.
|
|
37
|
-
*/
|
|
38
|
-
this.tickIntervalMs = 100;
|
|
39
|
-
}
|
|
40
|
-
onStart() { }
|
|
41
|
-
broadcast(_data) {
|
|
42
|
-
throw new Error("RealtimeHandler.broadcast() is only available inside a deployed handler");
|
|
43
|
-
}
|
|
44
|
-
getConnections() {
|
|
45
|
-
throw new Error("RealtimeHandler.getConnections() is only available inside a deployed handler");
|
|
46
|
-
}
|
|
47
|
-
startLoop(_ms) {
|
|
48
|
-
throw new Error("RealtimeHandler.startLoop() is only available inside a deployed handler");
|
|
49
|
-
}
|
|
50
|
-
stopLoop() {
|
|
51
|
-
throw new Error("RealtimeHandler.stopLoop() is only available inside a deployed handler");
|
|
52
|
-
}
|
|
53
|
-
get instanceId() {
|
|
54
|
-
throw new Error("RealtimeHandler.instanceId is only available inside a deployed handler");
|
|
55
|
-
}
|
|
56
|
-
get storage() {
|
|
57
|
-
throw new Error("RealtimeHandler.storage is only available inside a deployed handler");
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* SDK client acting **as the connected user** — every entity call respects
|
|
61
|
-
* the app's row-level security, evaluated as that user at call time. The
|
|
62
|
-
* default wherever a `conn` is in scope (connect/message/close).
|
|
63
|
-
*
|
|
64
|
-
* Throws for anonymous connections (no signed-in user to act as).
|
|
65
|
-
*/
|
|
66
|
-
createUserClient(conn) {
|
|
67
|
-
void conn;
|
|
68
|
-
throw new Error("RealtimeHandler.createUserClient() is only available inside a deployed handler");
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Service-role SDK client — bypasses RLS. For work with **no user in scope**
|
|
72
|
-
* (tick, alarm, onStart). Inside handleMessage/handleConnect prefer
|
|
73
|
-
* `createUserClient(conn)`.
|
|
74
|
-
*/
|
|
75
|
-
createServiceClient() {
|
|
76
|
-
throw new Error("RealtimeHandler.createServiceClient() is only available inside a deployed handler");
|
|
77
|
-
}
|
|
78
|
-
}
|