@base44-preview/sdk 0.8.40-pr.212.8f46be9 → 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 +7 -0
- package/dist/actor.js +8 -0
- package/dist/client.js +15 -31
- package/dist/client.types.d.ts +0 -10
- package/dist/modules/actors.d.ts +14 -2
- package/dist/modules/actors.js +41 -12
- package/dist/modules/actors.types.d.ts +1 -1
- package/package.json +1 -1
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,
|
|
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: (
|
|
154
|
+
baseURL: (_b = functionsAxiosClient.defaults) === null || _b === void 0 ? void 0 : _b.baseURL,
|
|
165
155
|
}),
|
|
166
156
|
agents: createAgentsModule({
|
|
167
157
|
axios: axiosClient,
|
|
@@ -179,16 +169,10 @@ export function createClient(config) {
|
|
|
179
169
|
appId,
|
|
180
170
|
userAuthModule,
|
|
181
171
|
}),
|
|
182
|
-
actors:
|
|
183
|
-
appId,
|
|
184
|
-
actorsWsUrl: resolvedActorsWsUrl,
|
|
185
|
-
functionsVersion,
|
|
186
|
-
// Same credential as function calls; the platform proxy authenticates the
|
|
187
|
-
// WS connection with it (anonymous when absent) — no pre-connect token mint.
|
|
188
|
-
getAuthToken: () => token || getAccessToken(),
|
|
189
|
-
}),
|
|
172
|
+
actors: actorsModule.module,
|
|
190
173
|
cleanup: () => {
|
|
191
174
|
userModules.analytics.cleanup();
|
|
175
|
+
actorsModule.closeAll();
|
|
192
176
|
if (socket) {
|
|
193
177
|
socket.disconnect();
|
|
194
178
|
}
|
|
@@ -212,7 +196,7 @@ export function createClient(config) {
|
|
|
212
196
|
}
|
|
213
197
|
return headers;
|
|
214
198
|
},
|
|
215
|
-
baseURL: (
|
|
199
|
+
baseURL: (_c = serviceRoleFunctionsAxiosClient.defaults) === null || _c === void 0 ? void 0 : _c.baseURL,
|
|
216
200
|
}),
|
|
217
201
|
agents: createAgentsModule({
|
|
218
202
|
axios: serviceRoleAxiosClient,
|
package/dist/client.types.d.ts
CHANGED
|
@@ -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.
|
package/dist/modules/actors.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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 {};
|
package/dist/modules/actors.js
CHANGED
|
@@ -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.
|
|
31
|
+
host: this.config.host,
|
|
31
32
|
party: this.actorName,
|
|
32
33
|
room: this.instanceId,
|
|
33
34
|
id: connId,
|
|
@@ -53,11 +54,11 @@ class Room {
|
|
|
53
54
|
data = JSON.parse(ev.data);
|
|
54
55
|
}
|
|
55
56
|
catch (_a) {
|
|
56
|
-
return;
|
|
57
|
+
return;
|
|
57
58
|
}
|
|
58
59
|
const msgType = data && typeof data === "object" ? data.type : undefined;
|
|
59
60
|
if (msgType === "__pong")
|
|
60
|
-
return;
|
|
61
|
+
return;
|
|
61
62
|
for (const listener of this.listeners)
|
|
62
63
|
listener(data);
|
|
63
64
|
});
|
|
@@ -68,13 +69,12 @@ class Room {
|
|
|
68
69
|
return;
|
|
69
70
|
}
|
|
70
71
|
try {
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
// idle room's lastMsg goes stale and this watchdog reconnects every DEAD_MS.
|
|
72
|
+
// The deployed shim echoes __ping → __pong (base44-userapp-bundler
|
|
73
|
+
// shim/actor.ts); without that, an idle room reconnects every DEAD_MS.
|
|
74
74
|
ws.send(JSON.stringify({ type: "__ping" }));
|
|
75
75
|
}
|
|
76
76
|
catch (_a) {
|
|
77
|
-
//
|
|
77
|
+
// not open; the watchdog above will reconnect
|
|
78
78
|
}
|
|
79
79
|
}, PING_MS);
|
|
80
80
|
return this;
|
|
@@ -95,7 +95,7 @@ class Room {
|
|
|
95
95
|
this.ws.send(JSON.stringify(data));
|
|
96
96
|
}
|
|
97
97
|
close() {
|
|
98
|
-
var _a;
|
|
98
|
+
var _a, _b;
|
|
99
99
|
if (this.heartbeat) {
|
|
100
100
|
clearInterval(this.heartbeat);
|
|
101
101
|
this.heartbeat = null;
|
|
@@ -103,12 +103,41 @@ class Room {
|
|
|
103
103
|
this.listeners.clear();
|
|
104
104
|
(_a = this.ws) === null || _a === void 0 ? void 0 : _a.close();
|
|
105
105
|
this.ws = null;
|
|
106
|
+
this.connId = null;
|
|
107
|
+
(_b = this.onClose) === null || _b === void 0 ? void 0 : _b.call(this);
|
|
106
108
|
}
|
|
107
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
|
+
}
|
|
108
119
|
export function createActorsModule(config) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
+
};
|
|
112
134
|
},
|
|
113
135
|
});
|
|
136
|
+
return {
|
|
137
|
+
module,
|
|
138
|
+
closeAll: () => {
|
|
139
|
+
for (const room of [...rooms])
|
|
140
|
+
room.close();
|
|
141
|
+
},
|
|
142
|
+
};
|
|
114
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
|
|
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;
|