@base44-preview/sdk 0.8.40-pr.212.8d7ec0a → 0.8.40-pr.212.ae9a5d0

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/actor.d.ts CHANGED
@@ -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";
11
12
  /**
12
13
  * A single client connection. `Send` is the message type this connection accepts
13
14
  * via {@link send} — the actor's *outgoing* (server→client) messages.
@@ -74,4 +75,10 @@ export declare abstract class Actor<Incoming = unknown, Outgoing = unknown> {
74
75
  protected stopLoop(): Promise<void>;
75
76
  protected get instanceId(): string;
76
77
  protected get storage(): Storage;
78
+ /**
79
+ * Anonymous Base44 client scoped to this actor instance — no user or service
80
+ * auth, so entity access is RLS-gated (same as a logged-out visitor).
81
+ * Example: `const rows = await this.client.entities.Score.list();`
82
+ */
83
+ protected get client(): Base44Client;
77
84
  }
package/dist/actor.js CHANGED
@@ -60,4 +60,12 @@ export class Actor {
60
60
  get storage() {
61
61
  throw new Error("Actor.storage is only available inside a deployed actor");
62
62
  }
63
+ /**
64
+ * Anonymous Base44 client scoped to this actor instance — no user or service
65
+ * auth, so entity access is RLS-gated (same as a logged-out visitor).
66
+ * Example: `const rows = await this.client.entities.Score.list();`
67
+ */
68
+ get client() {
69
+ throw new Error("Actor.client is only available inside a deployed actor");
70
+ }
63
71
  }
package/dist/client.js CHANGED
@@ -12,7 +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 } from "./modules/actors.js";
15
+ import { createActorsModule, resolveActorsHost } from "./modules/actors.js";
16
16
  /**
17
17
  * Creates a Base44 client.
18
18
  *
@@ -51,28 +51,10 @@ import { createActorsModule } from "./modules/actors.js";
51
51
  * ```
52
52
  */
53
53
  export function createClient(config) {
54
- var _a, _b;
55
- const { serverUrl = "https://base44.app", appId, token, serviceToken, requiresAuth = false, appBaseUrl, options, functionsVersion, headers: optionalHeaders, actorsWsUrl, } = config;
54
+ var _a, _b, _c;
55
+ const { serverUrl = "https://base44.app", appId, token, serviceToken, requiresAuth = false, appBaseUrl, options, functionsVersion, headers: optionalHeaders, } = config;
56
56
  // Normalize appBaseUrl to always be a string (empty if not provided or invalid)
57
57
  const normalizedAppBaseUrl = typeof appBaseUrl === "string" ? appBaseUrl : "";
58
- // Derive the Actor WebSocket URL if not explicitly provided. Default to
59
- // the app's OWN origin (the app URL proxies /parties to the backend) so the
60
- // socket is same-origin with the running app, not the API host: prefer an
61
- // explicit appBaseUrl, then the browser origin, then fall back to serverUrl
62
- // (Node/SSR, where there's no window). Convert https:// → wss:// (http → ws)
63
- // and strip the trailing slash.
64
- const resolvedActorsWsUrl = (() => {
65
- var _a, _b;
66
- if (actorsWsUrl)
67
- return actorsWsUrl.replace(/\/$/, "");
68
- const appOrigin = normalizedAppBaseUrl ||
69
- // React Native has a bare `window` with no `location`, so guard both.
70
- (typeof window !== "undefined" ? (_b = (_a = window.location) === null || _a === void 0 ? void 0 : _a.origin) !== null && _b !== void 0 ? _b : "" : "");
71
- return (appOrigin || serverUrl)
72
- .replace(/\/$/, "")
73
- .replace(/^https:\/\//, "wss://")
74
- .replace(/^http:\/\//, "ws://");
75
- })();
76
58
  const socketConfig = {
77
59
  serverUrl,
78
60
  mountPath: "/ws-user-apps/socket.io/",
@@ -142,6 +124,14 @@ export function createClient(config) {
142
124
  userAuthModule.setToken(accessToken);
143
125
  }
144
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
+ });
145
135
  const userModules = {
146
136
  entities: createEntitiesModule({
147
137
  axios: axiosClient,
@@ -161,7 +151,7 @@ export function createClient(config) {
161
151
  }
162
152
  return headers;
163
153
  },
164
- 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,
165
155
  }),
166
156
  agents: createAgentsModule({
167
157
  axios: axiosClient,
@@ -179,14 +169,10 @@ export function createClient(config) {
179
169
  appId,
180
170
  userAuthModule,
181
171
  }),
182
- actors: createActorsModule({
183
- appId,
184
- actorsWsUrl: resolvedActorsWsUrl,
185
- functionsVersion,
186
- getAuthToken: () => token || getAccessToken(),
187
- }),
172
+ actors: actorsModule.module,
188
173
  cleanup: () => {
189
174
  userModules.analytics.cleanup();
175
+ actorsModule.closeAll();
190
176
  if (socket) {
191
177
  socket.disconnect();
192
178
  }
@@ -210,7 +196,7 @@ export function createClient(config) {
210
196
  }
211
197
  return headers;
212
198
  },
213
- 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,
214
200
  }),
215
201
  agents: createAgentsModule({
216
202
  axios: serviceRoleAxiosClient,
@@ -74,16 +74,6 @@ export interface CreateClientConfig {
74
74
  * Additional client options.
75
75
  */
76
76
  options?: CreateClientOptions;
77
- /**
78
- * Base WebSocket URL for Actor connections.
79
- *
80
- * Defaults to the app's own origin (`appBaseUrl`, else the browser's
81
- * `window.location.origin`, else `serverUrl`) with `https://` replaced by
82
- * `wss://` (or `http://` by `ws://`) — so the Actor socket is same-origin
83
- * with the running app, which proxies `/parties` to the backend dispatcher.
84
- * Override only when the Actor host differs from the app origin.
85
- */
86
- actorsWsUrl?: string;
87
77
  }
88
78
  /**
89
79
  * The Base44 client instance.
@@ -7,7 +7,19 @@ interface ActorsConfig {
7
7
  /** Same semantics as function calls: editors with a non-prod version get the
8
8
  * draft actor script; everyone else gets the published one. */
9
9
  functionsVersion?: string;
10
- actorsWsUrl: string;
10
+ /** Absolute host PartySocket dials (it strips the scheme and connects wss, ws
11
+ * for localhost). Resolved by {@link resolveActorsHost}. */
12
+ host: string;
11
13
  }
12
- export declare function createActorsModule(config: ActorsConfig): Record<string, (instanceId: string) => ActorRoom>;
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) => ActorRoom>;
23
+ closeAll: () => void;
24
+ };
13
25
  export {};
@@ -4,10 +4,11 @@ import PartySocket from "partysocket";
4
4
  const PING_MS = 1000;
5
5
  const DEAD_MS = 3000;
6
6
  class Room {
7
- constructor(actorName, instanceId, config) {
7
+ constructor(actorName, instanceId, config, onClose) {
8
8
  this.actorName = actorName;
9
9
  this.instanceId = instanceId;
10
10
  this.config = config;
11
+ this.onClose = onClose;
11
12
  this.ws = null;
12
13
  this.listeners = new Set();
13
14
  this.heartbeat = null;
@@ -27,7 +28,7 @@ class Room {
27
28
  const connId = (_a = options === null || options === void 0 ? void 0 : options.id) !== null && _a !== void 0 ? _a : crypto.randomUUID();
28
29
  this.connId = connId;
29
30
  const ws = new PartySocket({
30
- host: this.config.actorsWsUrl,
31
+ host: this.config.host,
31
32
  party: this.actorName,
32
33
  room: this.instanceId,
33
34
  id: connId,
@@ -94,7 +95,7 @@ class Room {
94
95
  this.ws.send(JSON.stringify(data));
95
96
  }
96
97
  close() {
97
- var _a;
98
+ var _a, _b;
98
99
  if (this.heartbeat) {
99
100
  clearInterval(this.heartbeat);
100
101
  this.heartbeat = null;
@@ -102,12 +103,41 @@ class Room {
102
103
  this.listeners.clear();
103
104
  (_a = this.ws) === null || _a === void 0 ? void 0 : _a.close();
104
105
  this.ws = null;
106
+ this.connId = null;
107
+ (_b = this.onClose) === null || _b === void 0 ? void 0 : _b.call(this);
105
108
  }
106
109
  }
110
+ /**
111
+ * Absolute host for the actor WebSocket. PartySocket needs an absolute host and
112
+ * can't resolve a relative/empty `serverUrl` (same-origin apps use a relative
113
+ * `/api`, so `serverUrl` is often `""`), so fall back to the page origin.
114
+ * PartySocket handles the scheme (https→wss, ws for localhost).
115
+ */
116
+ export function resolveActorsHost(serverUrl, browserOrigin) {
117
+ return serverUrl && !serverUrl.startsWith("/") ? serverUrl : browserOrigin !== null && browserOrigin !== void 0 ? browserOrigin : serverUrl;
118
+ }
107
119
  export function createActorsModule(config) {
108
- return new Proxy({}, {
109
- get(_, actorName) {
110
- return (instanceId) => new Room(actorName, instanceId, config);
120
+ // Live rooms this client opened, so client.cleanup() can reclaim any the app
121
+ // forgot to close() (each room removes itself here on close).
122
+ const rooms = new Set();
123
+ const module = new Proxy({}, {
124
+ get(_, key) {
125
+ // Symbols and `then` resolve to undefined (so the module isn't mistaken
126
+ // for a thenable when awaited); any string key is an actor name.
127
+ if (typeof key !== "string" || key === "then")
128
+ return undefined;
129
+ return (instanceId) => {
130
+ const room = new Room(key, instanceId, config, () => rooms.delete(room));
131
+ rooms.add(room);
132
+ return room;
133
+ };
111
134
  },
112
135
  });
136
+ return {
137
+ module,
138
+ closeAll: () => {
139
+ for (const room of [...rooms])
140
+ room.close();
141
+ },
142
+ };
113
143
  }
@@ -58,7 +58,7 @@ export interface ActorRoom<N extends string = string> {
58
58
  connect(options?: ActorConnectOptions): this;
59
59
  /** Register a message listener. Multiple are allowed; returns a per-listener unsubscribe. */
60
60
  subscribe(callback: (data: ToClientFor<N>) => void): ActorSubscription;
61
- /** Send a message. Throws before {@link connect}; buffered by the socket until open. */
61
+ /** Send a message. Throws before {@link connect}; buffered by the socket once connecting. */
62
62
  send(data: ToServerFor<N>): void;
63
63
  /** Tear down the socket, heartbeat, and all listeners. */
64
64
  close(): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@base44-preview/sdk",
3
- "version": "0.8.40-pr.212.8d7ec0a",
3
+ "version": "0.8.40-pr.212.ae9a5d0",
4
4
  "description": "JavaScript SDK for Base44 API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",