@base44-preview/sdk 0.8.35-pr.212.2fe0031 → 0.8.35-pr.212.9dba072
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/index.d.ts +1 -1
- package/dist/modules/realtime.d.ts +10 -4
- package/dist/modules/realtime.js +37 -16
- package/dist/modules/realtime.types.d.ts +55 -22
- package/dist/realtime-handler.d.ts +10 -0
- package/dist/realtime-handler.js +10 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export type { IntegrationsModule, IntegrationEndpointFunction, CoreIntegrations,
|
|
|
10
10
|
export type { FunctionsModule, FunctionName, FunctionNameRegistry, } from "./modules/functions.types.js";
|
|
11
11
|
export type { AgentsModule, AgentName, AgentNameRegistry, AgentConversation, AgentMessage, AgentMessageReasoning, AgentMessageToolCall, AgentMessageUsage, AgentMessageCustomContext, AgentMessageMetadata, CreateConversationParams, } from "./modules/agents.types.js";
|
|
12
12
|
export type { AppLogsModule } from "./modules/app-logs.types.js";
|
|
13
|
-
export type { RealtimeModule, RealtimeHandlerClient,
|
|
13
|
+
export type { RealtimeModule, RealtimeHandlerClient, RealtimeHandlerNameRegistry, RealtimeHandlerRegistry, } from "./modules/realtime.types.js";
|
|
14
14
|
export type { SsoModule, SsoAccessTokenResponse } from "./modules/sso.types.js";
|
|
15
15
|
export { RealtimeHandler, type Conn } from "./realtime-handler.js";
|
|
16
16
|
export type { ConnectorsModule, UserConnectorsModule, } from "./modules/connectors.types.js";
|
|
@@ -3,11 +3,17 @@ export declare function createRealtimeModule(config: {
|
|
|
3
3
|
getToken(handlerName: string, instanceId: string): Promise<string>;
|
|
4
4
|
dispatcherWsUrl: string;
|
|
5
5
|
}): Record<string, RealtimeHandler>;
|
|
6
|
+
/** Handle for an active realtime subscription. */
|
|
7
|
+
interface RealtimeSubscription {
|
|
8
|
+
/** This connection's id — the same value the handler receives as `conn.id`. */
|
|
9
|
+
id: string;
|
|
10
|
+
/** Close the subscription and its socket. */
|
|
11
|
+
close(): void;
|
|
12
|
+
}
|
|
6
13
|
interface RealtimeHandler {
|
|
7
|
-
subscribe(instanceId: string, callback: (data: unknown) => void
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}>;
|
|
14
|
+
subscribe(instanceId: string, callback: (data: unknown) => void, options?: {
|
|
15
|
+
id?: string;
|
|
16
|
+
}): RealtimeSubscription;
|
|
11
17
|
send(instanceId: string, data: unknown): void;
|
|
12
18
|
}
|
|
13
19
|
export {};
|
package/dist/modules/realtime.js
CHANGED
|
@@ -8,44 +8,65 @@ export function createRealtimeModule(config) {
|
|
|
8
8
|
return new Proxy({}, {
|
|
9
9
|
get(_, handlerName) {
|
|
10
10
|
return {
|
|
11
|
-
|
|
11
|
+
subscribe(instanceId, callback, options) {
|
|
12
12
|
var _a;
|
|
13
13
|
const key = socketKey(handlerName, instanceId);
|
|
14
14
|
// close existing if any
|
|
15
15
|
(_a = activeSockets.get(key)) === null || _a === void 0 ? void 0 : _a.close();
|
|
16
|
-
|
|
16
|
+
// query as async fn: called on every (re)connect, fetches a fresh token each time
|
|
17
17
|
const ws = new PartySocket({
|
|
18
18
|
host: config.dispatcherWsUrl,
|
|
19
19
|
party: handlerName,
|
|
20
20
|
room: instanceId,
|
|
21
|
-
|
|
21
|
+
// Connection id: caller-supplied (stable — reuse across reconnects/tabs as
|
|
22
|
+
// you see fit) or auto-generated per connection. Server sees it as conn.id.
|
|
23
|
+
id: options === null || options === void 0 ? void 0 : options.id,
|
|
24
|
+
query: () => config.getToken(handlerName, instanceId).then((token) => ({ token })),
|
|
22
25
|
});
|
|
23
26
|
activeSockets.set(key, ws);
|
|
27
|
+
// Heartbeat / half-open detection. PartySocket only reconnects on a
|
|
28
|
+
// browser close/error event, so a silently-dead connection (TCP alive,
|
|
29
|
+
// no data — common behind proxies/LBs) hangs until the OS idle timeout
|
|
30
|
+
// (~60s). We ping periodically and force a reconnect if nothing comes
|
|
31
|
+
// back within DEAD_MS, cutting detection from ~60s to a few seconds.
|
|
32
|
+
// Pairs with the handler's setWebSocketAutoResponse("__ping"→"__pong"),
|
|
33
|
+
// so idle handlers (no app broadcasts) still keep the connection proven.
|
|
34
|
+
const PING_MS = 1000;
|
|
35
|
+
const DEAD_MS = 3000;
|
|
36
|
+
let lastMsg = Date.now();
|
|
37
|
+
const bumpAlive = () => { lastMsg = Date.now(); };
|
|
38
|
+
ws.addEventListener("open", bumpAlive);
|
|
24
39
|
ws.addEventListener("message", (ev) => {
|
|
40
|
+
bumpAlive();
|
|
41
|
+
let data;
|
|
25
42
|
try {
|
|
26
|
-
|
|
43
|
+
data = JSON.parse(ev.data);
|
|
27
44
|
}
|
|
28
45
|
catch (_a) {
|
|
29
|
-
// ignore malformed
|
|
46
|
+
return; // ignore malformed
|
|
30
47
|
}
|
|
48
|
+
// Swallow heartbeat acks — never surface them to the app.
|
|
49
|
+
if (data && typeof data === "object" && data.type === "__pong")
|
|
50
|
+
return;
|
|
51
|
+
callback(data);
|
|
31
52
|
});
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
53
|
+
const heartbeat = setInterval(() => {
|
|
54
|
+
if (Date.now() - lastMsg > DEAD_MS) {
|
|
55
|
+
bumpAlive(); // avoid a reconnect storm while the new socket comes up
|
|
56
|
+
ws.reconnect();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
36
59
|
try {
|
|
37
|
-
|
|
38
|
-
ws.updateProperties({ query: { token: newToken } });
|
|
60
|
+
ws.send(JSON.stringify({ type: "__ping" }));
|
|
39
61
|
}
|
|
40
62
|
catch (_a) {
|
|
41
|
-
//
|
|
63
|
+
// socket not open; the watchdog above will force a reconnect
|
|
42
64
|
}
|
|
43
|
-
});
|
|
65
|
+
}, PING_MS);
|
|
44
66
|
return {
|
|
45
|
-
|
|
46
|
-
ws.send(JSON.stringify(data));
|
|
47
|
-
},
|
|
67
|
+
id: ws.id, // the connection id (same value the handler sees as conn.id)
|
|
48
68
|
close() {
|
|
69
|
+
clearInterval(heartbeat);
|
|
49
70
|
activeSockets.delete(key);
|
|
50
71
|
ws.close();
|
|
51
72
|
},
|
|
@@ -1,32 +1,62 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
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
|
+
* inbound: { type: "joined" | "left" | "message"; userId?: string; from?: string; text?: string };
|
|
14
|
+
* outbound: { text: string };
|
|
15
|
+
* };
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
3
19
|
*/
|
|
4
|
-
export interface
|
|
5
|
-
/** Send a message to all subscribers of this instance. */
|
|
6
|
-
send(data: unknown): void;
|
|
7
|
-
/** Close the WebSocket connection and remove the subscription. */
|
|
8
|
-
close(): void;
|
|
20
|
+
export interface RealtimeHandlerRegistry {
|
|
9
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 InboundFor<N extends string> = N extends keyof RealtimeHandlerRegistry ? RealtimeHandlerRegistry[N] extends {
|
|
30
|
+
inbound: infer I;
|
|
31
|
+
} ? I : unknown : unknown;
|
|
32
|
+
type OutboundFor<N extends string> = N extends keyof RealtimeHandlerRegistry ? RealtimeHandlerRegistry[N] extends {
|
|
33
|
+
outbound: infer O;
|
|
34
|
+
} ? O : unknown : unknown;
|
|
10
35
|
/**
|
|
11
36
|
* Client for a single named RealtimeHandler.
|
|
37
|
+
* Typed automatically when the handler is registered in {@link RealtimeHandlerRegistry}.
|
|
12
38
|
*/
|
|
13
|
-
export interface RealtimeHandlerClient {
|
|
14
|
-
/**
|
|
15
|
-
* Subscribe to messages from a specific RealtimeHandler instance.
|
|
16
|
-
*
|
|
17
|
-
* @param instanceId - The instance ID of the Durable Object.
|
|
18
|
-
* @param callback - Called with each parsed message payload.
|
|
19
|
-
* @returns A subscription handle with `send` and `close` methods.
|
|
20
|
-
*/
|
|
21
|
-
subscribe(instanceId: string, callback: (data: unknown) => void): Promise<RealtimeSubscription>;
|
|
39
|
+
export interface RealtimeHandlerClient<N extends string = string> {
|
|
22
40
|
/**
|
|
23
|
-
*
|
|
41
|
+
* Open a WebSocket subscription. Returns a {@link RealtimeSubscription} with the
|
|
42
|
+
* connection `id` (same value the handler sees as `conn.id`) and a `close()` method.
|
|
24
43
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
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.
|
|
28
47
|
*/
|
|
29
|
-
|
|
48
|
+
subscribe(instanceId: string, callback: (data: InboundFor<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: OutboundFor<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
|
+
close(): void;
|
|
30
60
|
}
|
|
31
61
|
/**
|
|
32
62
|
* The realtime module provides access to Cloudflare Durable Object-backed
|
|
@@ -35,10 +65,13 @@ export interface RealtimeHandlerClient {
|
|
|
35
65
|
* Handler names are accessed as dynamic properties on this module:
|
|
36
66
|
* ```typescript
|
|
37
67
|
* const sub = await base44.realtime.MyHandler.subscribe("room-1", (msg) => {
|
|
38
|
-
* console.log(msg);
|
|
68
|
+
* console.log(msg); // typed if MyHandler is in RealtimeHandlerRegistry
|
|
39
69
|
* });
|
|
40
70
|
* sub.send({ text: "hello" });
|
|
41
71
|
* sub.close();
|
|
42
72
|
* ```
|
|
43
73
|
*/
|
|
44
|
-
export type RealtimeModule =
|
|
74
|
+
export type RealtimeModule = {
|
|
75
|
+
[K in AllHandlerNames]: K extends keyof RealtimeHandlerRegistry ? RealtimeHandlerClient<string & K> : RealtimeHandlerClient;
|
|
76
|
+
} & Record<string, RealtimeHandlerClient>;
|
|
77
|
+
export {};
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* At deploy time the bundler replaces this import with the compiled
|
|
9
9
|
* Cloudflare Durable Object implementation — this file provides types only.
|
|
10
10
|
*/
|
|
11
|
+
import type { Base44Client } from "./client.types.js";
|
|
11
12
|
export interface Conn {
|
|
12
13
|
userId: string;
|
|
13
14
|
appId: string;
|
|
@@ -15,13 +16,22 @@ export interface Conn {
|
|
|
15
16
|
send(data: unknown): void;
|
|
16
17
|
reject(code: number, reason: string): void;
|
|
17
18
|
}
|
|
19
|
+
export interface Storage {
|
|
20
|
+
get<T>(key: string): Promise<T | undefined>;
|
|
21
|
+
put(key: string, value: unknown): Promise<void>;
|
|
22
|
+
delete(key: string): Promise<boolean>;
|
|
23
|
+
}
|
|
18
24
|
export declare abstract class RealtimeHandler<_State = unknown, Message = unknown> {
|
|
19
25
|
abstract handleConnect(conn: Conn): void | Promise<void>;
|
|
20
26
|
abstract handleMessage(conn: Conn, msg: Message): void | Promise<void>;
|
|
21
27
|
abstract handleClose(conn: Conn): void | Promise<void>;
|
|
22
28
|
abstract handleTick(): void | Promise<void>;
|
|
29
|
+
onStart(): void | Promise<void>;
|
|
23
30
|
protected broadcast(_data: unknown): void;
|
|
24
31
|
protected getConnections(): Conn[];
|
|
25
32
|
protected startLoop(_ms: number): Promise<void>;
|
|
26
33
|
protected stopLoop(): Promise<void>;
|
|
34
|
+
protected get instanceId(): string;
|
|
35
|
+
protected get storage(): Storage;
|
|
36
|
+
protected createServiceClient(): Base44Client;
|
|
27
37
|
}
|
package/dist/realtime-handler.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* Cloudflare Durable Object implementation — this file provides types only.
|
|
10
10
|
*/
|
|
11
11
|
export class RealtimeHandler {
|
|
12
|
+
onStart() { }
|
|
12
13
|
broadcast(_data) {
|
|
13
14
|
throw new Error("RealtimeHandler.broadcast() is only available inside a deployed handler");
|
|
14
15
|
}
|
|
@@ -21,4 +22,13 @@ export class RealtimeHandler {
|
|
|
21
22
|
stopLoop() {
|
|
22
23
|
throw new Error("RealtimeHandler.stopLoop() is only available inside a deployed handler");
|
|
23
24
|
}
|
|
25
|
+
get instanceId() {
|
|
26
|
+
throw new Error("RealtimeHandler.instanceId is only available inside a deployed handler");
|
|
27
|
+
}
|
|
28
|
+
get storage() {
|
|
29
|
+
throw new Error("RealtimeHandler.storage is only available inside a deployed handler");
|
|
30
|
+
}
|
|
31
|
+
createServiceClient() {
|
|
32
|
+
throw new Error("RealtimeHandler.createServiceClient() is only available inside a deployed handler");
|
|
33
|
+
}
|
|
24
34
|
}
|