@base44-preview/sdk 0.8.40-pr.242.b74cef0 → 0.8.41-pr.243.98f858c

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.
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Type-only base class for Actors.
3
+ *
4
+ * Import and extend this in your actor files:
5
+ * import { Actor } from "@base44/sdk";
6
+ * export class MyActor extends Actor { ... }
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";
12
+ /**
13
+ * A single client connection. `Send` is the message type this connection accepts
14
+ * via {@link send} — the actor'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()`. Identifies a distinct client, so multiple
19
+ * tabs are separate connections. */
20
+ id: string;
21
+ send(data: Send): void;
22
+ reject(code: number, reason: string): void;
23
+ }
24
+ export interface Storage {
25
+ get<T>(key: string): Promise<T | undefined>;
26
+ put(key: string, value: unknown): Promise<void>;
27
+ delete(key: string): Promise<boolean>;
28
+ /** Wipe the room's entire persisted storage (match-end cleanup). Safe: a
29
+ * later rejoin re-bootstraps exactly like a brand-new room. */
30
+ deleteAll(): Promise<void>;
31
+ }
32
+ /**
33
+ * Base class for an Actor.
34
+ *
35
+ * @typeParam Incoming - messages this actor *receives* from clients
36
+ * (`handleMessage`'s `msg`) — the schema's `toServer` section.
37
+ * @typeParam Outgoing - messages this actor *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 = ActorRegistry["MyActor"];
44
+ * class MyActor extends Actor<Reg["toServer"], Reg["toClient"]> { ... }
45
+ * ```
46
+ */
47
+ export declare abstract class Actor<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
+ /**
53
+ * Optional wake hook: runs once when the instance starts, before any
54
+ * connection is handled — safe to load persisted state here.
55
+ */
56
+ handleStart(): void | Promise<void>;
57
+ /**
58
+ * Optional handler for scheduled wakes. Runs when a timer armed via
59
+ * {@link schedule} comes due, receiving the same `key` that was scheduled.
60
+ * The schedule is one-shot: it fires once and is cleared before this runs.
61
+ */
62
+ protected handleWake(_key: string): void | Promise<void>;
63
+ /**
64
+ * Arm a one-shot wake at `at` (epoch ms or a `Date`), identified by `key`.
65
+ * When it comes due the platform calls {@link handleWake} with this `key`.
66
+ * Scheduling the same `key` again reschedules it.
67
+ */
68
+ protected schedule(_key: string, _at: number | Date): Promise<void>;
69
+ /** Cancel a pending wake previously armed with {@link schedule}. */
70
+ protected cancelSchedule(_key: string): Promise<void>;
71
+ /**
72
+ * Managed ticker (opt-in). Override {@link shouldTick} and the platform runs
73
+ * {@link handleTick} on a timer of {@link tickIntervalMs} while it returns true,
74
+ * and stops (letting the Durable Object hibernate — no compute cost) when it
75
+ * returns false. The platform owns scheduling, rescheduling, self-heal, and
76
+ * error-safety.
77
+ *
78
+ * Re-evaluated after every connect/message/close and on every tick, so keep it
79
+ * cheap and pure (no async, no side effects). Example: `return this.players >= 2`.
80
+ */
81
+ protected tickIntervalMs: number;
82
+ protected shouldTick?(): boolean;
83
+ protected broadcast(_data: Outgoing): void;
84
+ protected getConnections(): Conn<Outgoing>[];
85
+ protected get instanceId(): string;
86
+ protected get storage(): Storage;
87
+ /**
88
+ * Anonymous Base44 client scoped to this actor instance — no user or service
89
+ * auth, so entity access is RLS-gated (same as a logged-out visitor). Always
90
+ * operates on production data: an actor runs server-side with no per-connection
91
+ * identity, so a Test DB preview selected in the editor does not apply here.
92
+ * Example: `const rows = await this.client.entities.Score.list();`
93
+ */
94
+ protected get client(): Base44Client;
95
+ }
package/dist/actor.js ADDED
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Type-only base class for Actors.
3
+ *
4
+ * Import and extend this in your actor files:
5
+ * import { Actor } from "@base44/sdk";
6
+ * export class MyActor extends Actor { ... }
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 an Actor.
13
+ *
14
+ * @typeParam Incoming - messages this actor *receives* from clients
15
+ * (`handleMessage`'s `msg`) — the schema's `toServer` section.
16
+ * @typeParam Outgoing - messages this actor *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 = ActorRegistry["MyActor"];
23
+ * class MyActor extends Actor<Reg["toServer"], Reg["toClient"]> { ... }
24
+ * ```
25
+ */
26
+ export class Actor {
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.
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
+ /**
41
+ * Optional wake hook: runs once when the instance starts, before any
42
+ * connection is handled — safe to load persisted state here.
43
+ */
44
+ handleStart() { }
45
+ /**
46
+ * Optional handler for scheduled wakes. Runs when a timer armed via
47
+ * {@link schedule} comes due, receiving the same `key` that was scheduled.
48
+ * The schedule is one-shot: it fires once and is cleared before this runs.
49
+ */
50
+ handleWake(_key) { }
51
+ /**
52
+ * Arm a one-shot wake at `at` (epoch ms or a `Date`), identified by `key`.
53
+ * When it comes due the platform calls {@link handleWake} with this `key`.
54
+ * Scheduling the same `key` again reschedules it.
55
+ */
56
+ schedule(_key, _at) {
57
+ throw new Error("Actor.schedule() is only available inside a deployed actor");
58
+ }
59
+ /** Cancel a pending wake previously armed with {@link schedule}. */
60
+ cancelSchedule(_key) {
61
+ throw new Error("Actor.cancelSchedule() is only available inside a deployed actor");
62
+ }
63
+ broadcast(_data) {
64
+ throw new Error("Actor.broadcast() is only available inside a deployed actor");
65
+ }
66
+ getConnections() {
67
+ throw new Error("Actor.getConnections() is only available inside a deployed actor");
68
+ }
69
+ get instanceId() {
70
+ throw new Error("Actor.instanceId is only available inside a deployed actor");
71
+ }
72
+ get storage() {
73
+ throw new Error("Actor.storage is only available inside a deployed actor");
74
+ }
75
+ /**
76
+ * Anonymous Base44 client scoped to this actor instance — no user or service
77
+ * auth, so entity access is RLS-gated (same as a logged-out visitor). Always
78
+ * operates on production data: an actor runs server-side with no per-connection
79
+ * identity, so a Test DB preview selected in the editor does not apply here.
80
+ * Example: `const rows = await this.client.entities.Score.list();`
81
+ */
82
+ get client() {
83
+ throw new Error("Actor.client is only available inside a deployed actor");
84
+ }
85
+ }
package/dist/client.js CHANGED
@@ -12,6 +12,7 @@ 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 { createActorsModule, resolveActorsHost } from "./modules/actors.js";
15
16
  /**
16
17
  * Creates a Base44 client.
17
18
  *
@@ -50,7 +51,7 @@ import { createAnalyticsModule } from "./modules/analytics.js";
50
51
  * ```
51
52
  */
52
53
  export function createClient(config) {
53
- var _a, _b;
54
+ var _a, _b, _c;
54
55
  const { serverUrl = "https://base44.app", appId, token, serviceToken, requiresAuth = false, appBaseUrl, options, functionsVersion, headers: optionalHeaders, } = config;
55
56
  // Normalize appBaseUrl to always be a string (empty if not provided or invalid)
56
57
  const normalizedAppBaseUrl = typeof appBaseUrl === "string" ? appBaseUrl : "";
@@ -123,6 +124,14 @@ export function createClient(config) {
123
124
  userAuthModule.setToken(accessToken);
124
125
  }
125
126
  }
127
+ const actorsModule = createActorsModule({
128
+ appId,
129
+ // serverUrl is often relative/empty (same-origin app); PartySocket needs an
130
+ // absolute host, so fall back to the page origin.
131
+ host: resolveActorsHost(serverUrl, typeof window !== "undefined" ? (_a = window.location) === null || _a === void 0 ? void 0 : _a.origin : undefined),
132
+ functionsVersion,
133
+ getAuthToken: () => token || getAccessToken(),
134
+ });
126
135
  const userModules = {
127
136
  entities: createEntitiesModule({
128
137
  axios: axiosClient,
@@ -142,7 +151,7 @@ export function createClient(config) {
142
151
  }
143
152
  return headers;
144
153
  },
145
- baseURL: (_a = functionsAxiosClient.defaults) === null || _a === void 0 ? void 0 : _a.baseURL,
154
+ baseURL: (_b = functionsAxiosClient.defaults) === null || _b === void 0 ? void 0 : _b.baseURL,
146
155
  }),
147
156
  agents: createAgentsModule({
148
157
  axios: axiosClient,
@@ -160,8 +169,10 @@ export function createClient(config) {
160
169
  appId,
161
170
  userAuthModule,
162
171
  }),
172
+ actors: actorsModule.module,
163
173
  cleanup: () => {
164
174
  userModules.analytics.cleanup();
175
+ actorsModule.closeAll();
165
176
  if (socket) {
166
177
  socket.disconnect();
167
178
  }
@@ -185,7 +196,7 @@ export function createClient(config) {
185
196
  }
186
197
  return headers;
187
198
  },
188
- baseURL: (_b = serviceRoleFunctionsAxiosClient.defaults) === null || _b === void 0 ? void 0 : _b.baseURL,
199
+ baseURL: (_c = serviceRoleFunctionsAxiosClient.defaults) === null || _c === void 0 ? void 0 : _c.baseURL,
189
200
  }),
190
201
  agents: createAgentsModule({
191
202
  axios: serviceRoleAxiosClient,
@@ -8,6 +8,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
10
  import type { AnalyticsModule } from "./modules/analytics.types.js";
11
+ import type { ActorsModule } from "./modules/actors.types.js";
11
12
  /**
12
13
  * Options for creating a Base44 client.
13
14
  */
@@ -88,6 +89,8 @@ export interface Base44Client {
88
89
  analytics: AnalyticsModule;
89
90
  /** {@link AppLogsModule | App logs module} for tracking app usage. */
90
91
  appLogs: AppLogsModule;
92
+ /** {@link ActorsModule | Actors module} for subscribing to and sending messages via Cloudflare Durable Object-backed Actors. */
93
+ actors: ActorsModule;
91
94
  /** {@link AuthModule | Auth module} for user authentication and management. */
92
95
  auth: AuthModule;
93
96
  /** {@link UserConnectorsModule | Connectors module} for app-user OAuth flows. */
package/dist/index.d.ts CHANGED
@@ -11,7 +11,9 @@ 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 { ActorsModule, ActorClient, ActorRef, Connection, ActorSubscription, ActorConnectOptions, ActorNameRegistry, ActorRegistry, } from "./modules/actors.types.js";
14
15
  export type { SsoModule, SsoAccessTokenResponse } from "./modules/sso.types.js";
16
+ export { Actor, type Conn } from "./actor.js";
15
17
  export type { ConnectorsModule, UserConnectorsModule, } from "./modules/connectors.types.js";
16
18
  export type { CustomIntegrationsModule, CustomIntegrationCallParams, CustomIntegrationCallResponse, } from "./modules/custom-integrations.types.js";
17
19
  export type { GetAccessTokenOptions, SaveAccessTokenOptions, RemoveAccessTokenOptions, GetLoginUrlOptions, } from "./utils/auth-utils.types.js";
package/dist/index.js CHANGED
@@ -3,3 +3,4 @@ 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 { Actor } from "./actor.js";
@@ -0,0 +1,25 @@
1
+ import type { ActorRef } from "./actors.types.js";
2
+ interface ActorsConfig {
3
+ appId: string;
4
+ /** Current user access token, if authenticated. Rides the WS query so the
5
+ * platform proxy can authenticate the connection; anonymous connects omit it. */
6
+ getAuthToken(): string | null | undefined;
7
+ /** Same semantics as function calls: editors with a non-prod version get the
8
+ * draft actor script; everyone else gets the published one. */
9
+ functionsVersion?: string;
10
+ /** Absolute host PartySocket dials (it strips the scheme and connects wss, ws
11
+ * for localhost). Resolved by {@link resolveActorsHost}. */
12
+ host: string;
13
+ }
14
+ /**
15
+ * Absolute host for the actor WebSocket. PartySocket needs an absolute host and
16
+ * can't resolve a relative/empty `serverUrl` (same-origin apps use a relative
17
+ * `/api`, so `serverUrl` is often `""`), so fall back to the page origin.
18
+ * PartySocket handles the scheme (https→wss, ws for localhost).
19
+ */
20
+ export declare function resolveActorsHost(serverUrl: string, browserOrigin?: string): string;
21
+ export declare function createActorsModule(config: ActorsConfig): {
22
+ module: Record<string, (instanceId: string) => ActorRef>;
23
+ closeAll: () => void;
24
+ };
25
+ export {};
@@ -0,0 +1,135 @@
1
+ import PartySocket from "partysocket";
2
+ // Heartbeat / half-open detection: PartySocket only reconnects on a close/error
3
+ // event, so ping periodically and force a reconnect if nothing returns in DEAD_MS.
4
+ const PING_MS = 1000;
5
+ const DEAD_MS = 3000;
6
+ /**
7
+ * A live connection to an actor instance. Only obtainable from
8
+ * {@link ActorRef.connect}, so `subscribe`/`send` are always valid — the socket
9
+ * exists for this object's whole lifetime.
10
+ */
11
+ class Connection {
12
+ constructor(actorName, instanceId, config, options, onClose) {
13
+ var _a;
14
+ this.onClose = onClose;
15
+ this.listeners = new Set();
16
+ this.heartbeat = null;
17
+ this.id = (_a = options === null || options === void 0 ? void 0 : options.id) !== null && _a !== void 0 ? _a : crypto.randomUUID();
18
+ const ws = new PartySocket({
19
+ host: config.host,
20
+ party: actorName,
21
+ room: instanceId,
22
+ id: this.id,
23
+ // Re-read on every (re)connect so a login/logout is picked up.
24
+ query: () => {
25
+ const token = config.getAuthToken();
26
+ return {
27
+ app_id: config.appId,
28
+ handler: actorName,
29
+ ...(token ? { token } : {}),
30
+ ...(config.functionsVersion ? { fv: config.functionsVersion } : {}),
31
+ };
32
+ },
33
+ });
34
+ this.ws = ws;
35
+ let lastMsg = Date.now();
36
+ const bumpAlive = () => { lastMsg = Date.now(); };
37
+ ws.addEventListener("open", bumpAlive);
38
+ ws.addEventListener("message", (ev) => {
39
+ bumpAlive();
40
+ let data;
41
+ try {
42
+ data = JSON.parse(ev.data);
43
+ }
44
+ catch (_a) {
45
+ return;
46
+ }
47
+ const msgType = data && typeof data === "object" ? data.type : undefined;
48
+ if (msgType === "__pong")
49
+ return;
50
+ for (const listener of this.listeners)
51
+ listener(data);
52
+ });
53
+ this.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
+ }
59
+ try {
60
+ // The deployed shim echoes __ping → __pong (base44-userapp-bundler
61
+ // shim/actor.ts); without that, an idle room reconnects every DEAD_MS.
62
+ ws.send(JSON.stringify({ type: "__ping" }));
63
+ }
64
+ catch (_a) {
65
+ // not open; the watchdog above will reconnect
66
+ }
67
+ }, PING_MS);
68
+ }
69
+ subscribe(callback) {
70
+ this.listeners.add(callback);
71
+ return {
72
+ unsubscribe: () => { this.listeners.delete(callback); },
73
+ };
74
+ }
75
+ send(data) {
76
+ this.ws.send(JSON.stringify(data));
77
+ }
78
+ close() {
79
+ if (this.heartbeat) {
80
+ clearInterval(this.heartbeat);
81
+ this.heartbeat = null;
82
+ }
83
+ this.listeners.clear();
84
+ this.ws.close();
85
+ this.onClose();
86
+ }
87
+ }
88
+ /** Handle for one actor instance: `connect()` opens the socket (idempotent). */
89
+ function makeActorRef(actorName, instanceId, config, connections) {
90
+ let conn = null;
91
+ return {
92
+ connect(options) {
93
+ if (conn)
94
+ return conn;
95
+ const c = new Connection(actorName, instanceId, config, options, () => {
96
+ connections.delete(c);
97
+ if (conn === c)
98
+ conn = null; // allow a fresh connect() after close
99
+ });
100
+ conn = c;
101
+ connections.add(c);
102
+ return c;
103
+ },
104
+ };
105
+ }
106
+ /**
107
+ * Absolute host for the actor WebSocket. PartySocket needs an absolute host and
108
+ * can't resolve a relative/empty `serverUrl` (same-origin apps use a relative
109
+ * `/api`, so `serverUrl` is often `""`), so fall back to the page origin.
110
+ * PartySocket handles the scheme (https→wss, ws for localhost).
111
+ */
112
+ export function resolveActorsHost(serverUrl, browserOrigin) {
113
+ return serverUrl && !serverUrl.startsWith("/") ? serverUrl : browserOrigin !== null && browserOrigin !== void 0 ? browserOrigin : serverUrl;
114
+ }
115
+ export function createActorsModule(config) {
116
+ // Live connections this client opened, so client.cleanup() can reclaim any the
117
+ // app forgot to close() (each connection removes itself here on close).
118
+ const connections = new Set();
119
+ const module = new Proxy({}, {
120
+ get(_, key) {
121
+ // Symbols and `then` resolve to undefined (so the module isn't mistaken
122
+ // for a thenable when awaited); any string key is an actor name.
123
+ if (typeof key !== "string" || key === "then")
124
+ return undefined;
125
+ return (instanceId) => makeActorRef(key, instanceId, config, connections);
126
+ },
127
+ });
128
+ return {
129
+ module,
130
+ closeAll: () => {
131
+ for (const c of [...connections])
132
+ c.close();
133
+ },
134
+ };
135
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Extend this interface to add typed `subscribe` callbacks and `send` payloads
3
+ * for your deployed Actors.
4
+ *
5
+ * This is separate from {@link ActorNameRegistry} (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 ActorRegistry {
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 ActorRegistry {
21
+ }
22
+ /**
23
+ * Auto-populated by `base44 types generate` with the names of your deployed actors.
24
+ * Do not edit this interface manually — use {@link ActorRegistry} for message types.
25
+ */
26
+ export interface ActorNameRegistry {
27
+ }
28
+ type AllActorNames = keyof ActorRegistry | keyof ActorNameRegistry;
29
+ type ToClientFor<N extends string> = N extends keyof ActorRegistry ? ActorRegistry[N] extends {
30
+ toClient: infer I;
31
+ } ? I : unknown : unknown;
32
+ type ToServerFor<N extends string> = N extends keyof ActorRegistry ? ActorRegistry[N] extends {
33
+ toServer: infer O;
34
+ } ? O : unknown : unknown;
35
+ /** Options for {@link ActorRef.connect}. */
36
+ export interface ActorConnectOptions {
37
+ /**
38
+ * The connection id — becomes the actor's `conn.id`. Supply a stable value
39
+ * (e.g. persisted per tab) so a reconnect reuses the same server-side
40
+ * identity; omit for an auto-generated per-connection id.
41
+ */
42
+ id?: string;
43
+ }
44
+ /** Handle for one listener registered via {@link Connection.subscribe}. */
45
+ export interface ActorSubscription {
46
+ /** Remove this listener; other listeners and the socket stay live. */
47
+ unsubscribe(): void;
48
+ }
49
+ /**
50
+ * A live connection to an actor instance, returned by {@link ActorRef.connect}.
51
+ * `subscribe`/`send` are always valid — you only get a `Connection` once the
52
+ * socket has been opened, so there's no pre-connect state to guard against.
53
+ */
54
+ export interface Connection<N extends string = string> {
55
+ /** The connection id (the value the actor sees as `conn.id`). */
56
+ readonly id: string;
57
+ /** Register a message listener. Multiple are allowed; returns a per-listener unsubscribe. */
58
+ subscribe(callback: (data: ToClientFor<N>) => void): ActorSubscription;
59
+ /** Send a message. Buffered by the socket until it's open. */
60
+ send(data: ToServerFor<N>): void;
61
+ /** Tear down the socket, heartbeat, and all listeners. */
62
+ close(): void;
63
+ }
64
+ /**
65
+ * A handle to one actor instance — `base44.actors.MyActor(id)`. Call
66
+ * {@link connect} to open the socket and get a {@link Connection}.
67
+ */
68
+ export interface ActorRef<N extends string = string> {
69
+ /** Open the WebSocket and return the {@link Connection}. Idempotent. */
70
+ connect(options?: ActorConnectOptions): Connection<N>;
71
+ }
72
+ /**
73
+ * Client for a single named Actor — call it with an instance id to get an
74
+ * {@link ActorRef}. Typed automatically when the actor is registered in
75
+ * {@link ActorRegistry}.
76
+ */
77
+ export interface ActorClient<N extends string = string> {
78
+ (instanceId: string): ActorRef<N>;
79
+ }
80
+ /**
81
+ * The actors module provides access to Cloudflare Durable Object-backed
82
+ * Actors deployed by the Base44 platform.
83
+ *
84
+ * ```typescript
85
+ * const conn = base44.actors.MyActor("room-1").connect();
86
+ * const sub = conn.subscribe((msg) => console.log(msg)); // typed via ActorRegistry
87
+ * conn.send({ type: "message", text: "hi" });
88
+ * sub.unsubscribe();
89
+ * conn.close();
90
+ * ```
91
+ */
92
+ export type ActorsModule = {
93
+ [K in AllActorNames]: K extends keyof ActorRegistry ? ActorClient<string & K> : ActorClient;
94
+ } & Record<string, ActorClient>;
95
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,3 @@
1
- import { getLoginUrl } from "../utils/auth-utils.js";
2
1
  function isInsideIframe() {
3
2
  if (typeof window === "undefined")
4
3
  return false;
@@ -83,11 +82,8 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
83
82
  const redirectUrl = nextUrl
84
83
  ? new URL(nextUrl, window.location.origin).toString()
85
84
  : window.location.href;
86
- // A foreign origin (e.g. local dev) can't resolve the app by Host, so
87
- // the login URL must carry the app id; same-origin keeps the bare path.
88
- const loginUrl = options.appBaseUrl
89
- ? getLoginUrl(redirectUrl, { serverUrl: options.appBaseUrl, appId })
90
- : `/login?from_url=${encodeURIComponent(redirectUrl)}`;
85
+ // Build the login URL
86
+ const loginUrl = `${options.appBaseUrl}/login?from_url=${encodeURIComponent(redirectUrl)}`;
91
87
  // Redirect to the login page
92
88
  window.location.href = loginUrl;
93
89
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@base44-preview/sdk",
3
- "version": "0.8.40-pr.242.b74cef0",
3
+ "version": "0.8.41-pr.243.98f858c",
4
4
  "description": "JavaScript SDK for Base44 API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -27,6 +27,7 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "axios": "^1.18.1",
30
+ "partysocket": "^0.0.23",
30
31
  "socket.io-client": "^4.8.3",
31
32
  "uuid": "^13.0.2"
32
33
  },