@base44-preview/sdk 0.8.28-pr.173.186369a → 0.8.28-pr.174.6d8a9cd
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 +6 -10
- package/dist/client.types.d.ts +1 -5
- package/dist/index.d.ts +1 -1
- package/dist/modules/auth.types.d.ts +8 -14
- package/dist/modules/entities.d.ts +1 -4
- package/dist/modules/entities.js +35 -231
- package/dist/modules/entities.types.d.ts +0 -36
- package/dist/utils/socket-utils.js +1 -3
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -122,19 +122,11 @@ export function createClient(config) {
|
|
|
122
122
|
userAuthModule.setToken(accessToken);
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
|
-
const userAnalyticsModule = createAnalyticsModule({
|
|
126
|
-
axiosClient,
|
|
127
|
-
serverUrl,
|
|
128
|
-
appId,
|
|
129
|
-
userAuthModule,
|
|
130
|
-
});
|
|
131
125
|
const userModules = {
|
|
132
126
|
entities: createEntitiesModule({
|
|
133
127
|
axios: axiosClient,
|
|
134
128
|
appId,
|
|
135
129
|
getSocket,
|
|
136
|
-
subscriptionOptions: options === null || options === void 0 ? void 0 : options.entitySubscriptions,
|
|
137
|
-
trackSubscriptionEvent: userAnalyticsModule.track,
|
|
138
130
|
}),
|
|
139
131
|
integrations: createIntegrationsModule(axiosClient, appId),
|
|
140
132
|
connectors: createUserConnectorsModule(axiosClient, appId),
|
|
@@ -160,7 +152,12 @@ export function createClient(config) {
|
|
|
160
152
|
}),
|
|
161
153
|
appLogs: createAppLogsModule(axiosClient, appId),
|
|
162
154
|
users: createUsersModule(axiosClient, appId),
|
|
163
|
-
analytics:
|
|
155
|
+
analytics: createAnalyticsModule({
|
|
156
|
+
axiosClient,
|
|
157
|
+
serverUrl,
|
|
158
|
+
appId,
|
|
159
|
+
userAuthModule,
|
|
160
|
+
}),
|
|
164
161
|
cleanup: () => {
|
|
165
162
|
userModules.analytics.cleanup();
|
|
166
163
|
if (socket) {
|
|
@@ -173,7 +170,6 @@ export function createClient(config) {
|
|
|
173
170
|
axios: serviceRoleAxiosClient,
|
|
174
171
|
appId,
|
|
175
172
|
getSocket,
|
|
176
|
-
subscriptionOptions: options === null || options === void 0 ? void 0 : options.entitySubscriptions,
|
|
177
173
|
}),
|
|
178
174
|
integrations: createIntegrationsModule(serviceRoleAxiosClient, appId),
|
|
179
175
|
sso: createSsoModule(serviceRoleAxiosClient, appId),
|
package/dist/client.types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EntitiesModule
|
|
1
|
+
import type { EntitiesModule } from "./modules/entities.types.js";
|
|
2
2
|
import type { IntegrationsModule } from "./modules/integrations.types.js";
|
|
3
3
|
import type { AuthModule } from "./modules/auth.types.js";
|
|
4
4
|
import type { SsoModule } from "./modules/sso.types.js";
|
|
@@ -15,10 +15,6 @@ export interface CreateClientOptions {
|
|
|
15
15
|
* Optional error handler that will be called whenever an API error occurs.
|
|
16
16
|
*/
|
|
17
17
|
onError?: (error: Error) => void;
|
|
18
|
-
/**
|
|
19
|
-
* Client-side controls for realtime entity subscriptions.
|
|
20
|
-
*/
|
|
21
|
-
entitySubscriptions?: EntitySubscriptionOptions;
|
|
22
18
|
}
|
|
23
19
|
/**
|
|
24
20
|
* Configuration for creating a Base44 client.
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl } from
|
|
|
4
4
|
export { createClient, createClientFromRequest, Base44Error, getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl, };
|
|
5
5
|
export type { Base44Client, CreateClientConfig, CreateClientOptions, Base44ErrorJSON, };
|
|
6
6
|
export * from "./types.js";
|
|
7
|
-
export type { DeleteManyResult, DeleteResult, EntitiesModule, EntityFilterOperators, EntityFilterQuery, EntityFilterValue, EntityHandler, EntityRecord,
|
|
7
|
+
export type { DeleteManyResult, DeleteResult, EntitiesModule, EntityFilterOperators, EntityFilterQuery, EntityFilterValue, EntityHandler, EntityRecord, EntityTypeRegistry, ImportResult, RealtimeEventType, RealtimeEvent, RealtimeCallback, SortField, UpdateManyResult, } from "./modules/entities.types.js";
|
|
8
8
|
export type { AuthModule, LoginResponse, RegisterParams, VerifyOtpParams, ChangePasswordParams, ResetPasswordParams, User, } from "./modules/auth.types.js";
|
|
9
9
|
export type { IntegrationsModule, IntegrationEndpointFunction, CoreIntegrations, InvokeLLMParams, GenerateImageParams, GenerateImageResult, UploadFileParams, UploadFileResult, SendEmailParams, SendEmailResult, ExtractDataFromUploadedFileParams, ExtractDataFromUploadedFileResult, UploadPrivateFileParams, UploadPrivateFileResult, CreateFileSignedUrlParams, CreateFileSignedUrlResult, } from "./modules/integrations.types.js";
|
|
10
10
|
export type { FunctionsModule, FunctionName, FunctionNameRegistry, } from "./modules/functions.types.js";
|
|
@@ -128,32 +128,26 @@ export interface AuthModule {
|
|
|
128
128
|
/**
|
|
129
129
|
* Updates the current authenticated user's information.
|
|
130
130
|
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
131
|
+
* You can update `role` and any custom fields defined in your
|
|
132
|
+
* [User entity schema](https://docs.base44.com/developers/backend/resources/entities/user-schema).
|
|
133
|
+
* The `role` value must match one of the options defined in your User entity schema.
|
|
134
|
+
* The following fields are read-only and can't be changed with this method:
|
|
135
|
+
* `id`, `email`, `full_name`, `created_date`, `updated_date`, and `created_by`.
|
|
133
136
|
*
|
|
134
137
|
* @param data - Object containing the fields to update.
|
|
135
138
|
* @returns Promise resolving to the updated user data.
|
|
136
139
|
*
|
|
137
140
|
* @example
|
|
138
141
|
* ```typescript
|
|
139
|
-
* // Update
|
|
140
|
-
* const updatedUser = await base44.auth.updateMe({
|
|
141
|
-
* full_name: 'John Doe'
|
|
142
|
-
* });
|
|
143
|
-
* console.log(`Updated user: ${updatedUser.full_name}`);
|
|
144
|
-
* ```
|
|
145
|
-
*
|
|
146
|
-
* @example
|
|
147
|
-
* ```typescript
|
|
148
|
-
* // Update custom fields defined in your User entity
|
|
142
|
+
* // Update role and custom fields defined in your User entity
|
|
149
143
|
* await base44.auth.updateMe({
|
|
144
|
+
* role: 'admin',
|
|
150
145
|
* bio: 'Software developer',
|
|
151
|
-
* phone: '+1234567890',
|
|
152
146
|
* preferences: { theme: 'dark' }
|
|
153
147
|
* });
|
|
154
148
|
* ```
|
|
155
149
|
*/
|
|
156
|
-
updateMe(data:
|
|
150
|
+
updateMe(data: Record<string, any>): Promise<User>;
|
|
157
151
|
/**
|
|
158
152
|
* Redirects the user to the app's login page.
|
|
159
153
|
*
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { AxiosInstance } from "axios";
|
|
2
|
-
import { EntitiesModule
|
|
3
|
-
import type { TrackEventParams } from "./analytics.types";
|
|
2
|
+
import { EntitiesModule } from "./entities.types";
|
|
4
3
|
import { RoomsSocket } from "../utils/socket-utils.js";
|
|
5
4
|
/**
|
|
6
5
|
* Configuration for the entities module.
|
|
@@ -10,8 +9,6 @@ export interface EntitiesModuleConfig {
|
|
|
10
9
|
axios: AxiosInstance;
|
|
11
10
|
appId: string;
|
|
12
11
|
getSocket: () => ReturnType<typeof RoomsSocket>;
|
|
13
|
-
subscriptionOptions?: EntitySubscriptionOptions;
|
|
14
|
-
trackSubscriptionEvent?: (params: TrackEventParams) => void;
|
|
15
12
|
}
|
|
16
13
|
/**
|
|
17
14
|
* Creates the entities module for the Base44 SDK.
|
package/dist/modules/entities.js
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
const DEFAULT_MAX_ACTIVE_ENTITY_SUBSCRIPTIONS = 100;
|
|
2
|
-
const DEFAULT_SUBSCRIPTION_CHURN_WARNING_THRESHOLD = 20;
|
|
3
|
-
const DEFAULT_SUBSCRIPTION_CHURN_WINDOW_MS = 60000;
|
|
4
|
-
const DEFAULT_EMPTY_ROOM_GRACE_MS = 1000;
|
|
5
|
-
const ENTITY_SUBSCRIPTION_WARNING_EVENT_NAME = "__entity_subscription_warning__";
|
|
6
1
|
/**
|
|
7
2
|
* Creates the entities module for the Base44 SDK.
|
|
8
3
|
*
|
|
@@ -12,13 +7,6 @@ const ENTITY_SUBSCRIPTION_WARNING_EVENT_NAME = "__entity_subscription_warning__"
|
|
|
12
7
|
*/
|
|
13
8
|
export function createEntitiesModule(config) {
|
|
14
9
|
const { axios, appId, getSocket } = config;
|
|
15
|
-
const entityHandlers = new Map();
|
|
16
|
-
const subscriptionManager = createEntitySubscriptionManager({
|
|
17
|
-
appId,
|
|
18
|
-
getSocket,
|
|
19
|
-
options: config.subscriptionOptions,
|
|
20
|
-
trackSubscriptionEvent: config.trackSubscriptionEvent,
|
|
21
|
-
});
|
|
22
10
|
// Using Proxy to dynamically handle entity names
|
|
23
11
|
return new Proxy({}, {
|
|
24
12
|
get(target, entityName) {
|
|
@@ -28,14 +16,8 @@ export function createEntitiesModule(config) {
|
|
|
28
16
|
entityName.startsWith("_")) {
|
|
29
17
|
return undefined;
|
|
30
18
|
}
|
|
31
|
-
const cachedHandler = entityHandlers.get(entityName);
|
|
32
|
-
if (cachedHandler) {
|
|
33
|
-
return cachedHandler;
|
|
34
|
-
}
|
|
35
19
|
// Create entity handler
|
|
36
|
-
|
|
37
|
-
entityHandlers.set(entityName, handler);
|
|
38
|
-
return handler;
|
|
20
|
+
return createEntityHandler(axios, appId, entityName, getSocket);
|
|
39
21
|
},
|
|
40
22
|
});
|
|
41
23
|
}
|
|
@@ -59,226 +41,17 @@ function parseRealtimeMessage(dataStr) {
|
|
|
59
41
|
return null;
|
|
60
42
|
}
|
|
61
43
|
}
|
|
62
|
-
function normalizeEntitySubscriptionOptions(options) {
|
|
63
|
-
return {
|
|
64
|
-
maxActiveSubscriptions: normalizePositiveInteger(options === null || options === void 0 ? void 0 : options.maxActiveSubscriptions, DEFAULT_MAX_ACTIVE_ENTITY_SUBSCRIPTIONS),
|
|
65
|
-
churnWarningThreshold: normalizePositiveInteger(options === null || options === void 0 ? void 0 : options.churnWarningThreshold, DEFAULT_SUBSCRIPTION_CHURN_WARNING_THRESHOLD),
|
|
66
|
-
churnWindowMs: normalizePositiveInteger(options === null || options === void 0 ? void 0 : options.churnWindowMs, DEFAULT_SUBSCRIPTION_CHURN_WINDOW_MS),
|
|
67
|
-
emptyRoomGraceMs: normalizeNonNegativeInteger(options === null || options === void 0 ? void 0 : options.emptyRoomGraceMs, DEFAULT_EMPTY_ROOM_GRACE_MS),
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
function normalizePositiveInteger(value, fallback) {
|
|
71
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
72
|
-
return fallback;
|
|
73
|
-
}
|
|
74
|
-
return Math.floor(value);
|
|
75
|
-
}
|
|
76
|
-
function normalizeNonNegativeInteger(value, fallback) {
|
|
77
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
78
|
-
return fallback;
|
|
79
|
-
}
|
|
80
|
-
return Math.floor(value);
|
|
81
|
-
}
|
|
82
|
-
function createEntitySubscriptionManager({ appId, getSocket, options, trackSubscriptionEvent, }) {
|
|
83
|
-
const normalizedOptions = normalizeEntitySubscriptionOptions(options);
|
|
84
|
-
const activeSubscriptions = new Map();
|
|
85
|
-
const churnByRoom = new Map();
|
|
86
|
-
const roomsWarnedForCap = new Set();
|
|
87
|
-
let nextCallbackId = 1;
|
|
88
|
-
function makeRoom(entityName) {
|
|
89
|
-
return `entities:${appId}:${entityName}`;
|
|
90
|
-
}
|
|
91
|
-
function emitWarning(message, properties) {
|
|
92
|
-
console.warn(message);
|
|
93
|
-
try {
|
|
94
|
-
trackSubscriptionEvent === null || trackSubscriptionEvent === void 0 ? void 0 : trackSubscriptionEvent({
|
|
95
|
-
eventName: ENTITY_SUBSCRIPTION_WARNING_EVENT_NAME,
|
|
96
|
-
properties,
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
catch (_a) {
|
|
100
|
-
// Diagnostics should never break application code.
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
function recordSubscriptionActivity(room, entityName, action) {
|
|
104
|
-
var _a;
|
|
105
|
-
const now = Date.now();
|
|
106
|
-
const churnState = (_a = churnByRoom.get(room)) !== null && _a !== void 0 ? _a : {
|
|
107
|
-
events: [],
|
|
108
|
-
lastWarningAt: null,
|
|
109
|
-
};
|
|
110
|
-
churnState.events = churnState.events.filter((event) => now - event.timestamp <= normalizedOptions.churnWindowMs);
|
|
111
|
-
churnState.events.push({ action, timestamp: now });
|
|
112
|
-
churnByRoom.set(room, churnState);
|
|
113
|
-
const subscribeCount = churnState.events.filter((event) => event.action === "subscribe").length;
|
|
114
|
-
const unsubscribeCount = churnState.events.length - subscribeCount;
|
|
115
|
-
if (churnState.events.length < normalizedOptions.churnWarningThreshold ||
|
|
116
|
-
subscribeCount === 0 ||
|
|
117
|
-
unsubscribeCount === 0 ||
|
|
118
|
-
(churnState.lastWarningAt !== null &&
|
|
119
|
-
now - churnState.lastWarningAt < normalizedOptions.churnWindowMs)) {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
churnState.lastWarningAt = now;
|
|
123
|
-
emitWarning(`[Base44 SDK] entities.${entityName}.subscribe() is being created and cleaned up repeatedly ` +
|
|
124
|
-
`(${churnState.events.length} subscribe/unsubscribe operations in ` +
|
|
125
|
-
`${normalizedOptions.churnWindowMs}ms). Keep realtime subscriptions in a stable lifecycle to avoid socket churn.`, {
|
|
126
|
-
reason: "subscription_churn",
|
|
127
|
-
app_id: appId,
|
|
128
|
-
entity: entityName,
|
|
129
|
-
room,
|
|
130
|
-
action,
|
|
131
|
-
activity_count: churnState.events.length,
|
|
132
|
-
subscribe_count: subscribeCount,
|
|
133
|
-
unsubscribe_count: unsubscribeCount,
|
|
134
|
-
churn_window_ms: normalizedOptions.churnWindowMs,
|
|
135
|
-
empty_room_grace_ms: normalizedOptions.emptyRoomGraceMs,
|
|
136
|
-
churn_warning_threshold: normalizedOptions.churnWarningThreshold,
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
function warnForSubscriptionCap(room, entityName) {
|
|
140
|
-
if (roomsWarnedForCap.has(room)) {
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
roomsWarnedForCap.add(room);
|
|
144
|
-
emitWarning(`[Base44 SDK] Realtime entity subscription cap reached ` +
|
|
145
|
-
`(${normalizedOptions.maxActiveSubscriptions} active entities per SDK client/tab). ` +
|
|
146
|
-
`Skipping entities.${entityName}.subscribe(). Unsubscribe from unused entity subscriptions before subscribing to more entities.`, {
|
|
147
|
-
reason: "active_subscription_cap",
|
|
148
|
-
app_id: appId,
|
|
149
|
-
entity: entityName,
|
|
150
|
-
room,
|
|
151
|
-
active_subscription_count: activeSubscriptions.size,
|
|
152
|
-
max_active_subscriptions: normalizedOptions.maxActiveSubscriptions,
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
function closeRoomSubscription(state) {
|
|
156
|
-
clearPendingClose(state);
|
|
157
|
-
state.unsubscribeFromRoom();
|
|
158
|
-
activeSubscriptions.delete(state.room);
|
|
159
|
-
if (activeSubscriptions.size < normalizedOptions.maxActiveSubscriptions) {
|
|
160
|
-
roomsWarnedForCap.clear();
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
function clearPendingClose(state) {
|
|
164
|
-
if (!state.closeTimer) {
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
clearTimeout(state.closeTimer);
|
|
168
|
-
state.closeTimer = null;
|
|
169
|
-
}
|
|
170
|
-
function scheduleRoomClose(state) {
|
|
171
|
-
var _a;
|
|
172
|
-
if (state.closeTimer) {
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
if (normalizedOptions.emptyRoomGraceMs === 0) {
|
|
176
|
-
closeRoomSubscription(state);
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
const closeTimer = setTimeout(() => {
|
|
180
|
-
state.closeTimer = null;
|
|
181
|
-
if (state.callbacks.size === 0 &&
|
|
182
|
-
activeSubscriptions.get(state.room) === state) {
|
|
183
|
-
closeRoomSubscription(state);
|
|
184
|
-
}
|
|
185
|
-
}, normalizedOptions.emptyRoomGraceMs);
|
|
186
|
-
(_a = closeTimer.unref) === null || _a === void 0 ? void 0 : _a.call(closeTimer);
|
|
187
|
-
state.closeTimer = closeTimer;
|
|
188
|
-
}
|
|
189
|
-
function dispatchRealtimeMessage(state, dataStr) {
|
|
190
|
-
var _a;
|
|
191
|
-
if (state.callbacks.size === 0) {
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
const event = parseRealtimeMessage(dataStr);
|
|
195
|
-
if (!event) {
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
// Server signals oversize broadcasts with `_oversize: true` on
|
|
199
|
-
// `data`. The wire payload was slimmed to fit under the realtime
|
|
200
|
-
// transport cap, so big string fields arrive as empty strings (or
|
|
201
|
-
// the whole record collapses to a stub). Surface this to the
|
|
202
|
-
// developer console so they know to fetch the full record on
|
|
203
|
-
// demand (e.g. a follow-up entities.X.get(id) call) instead of
|
|
204
|
-
// rendering the slimmed payload directly. Skip on delete events
|
|
205
|
-
// — the record no longer exists.
|
|
206
|
-
if (event.type !== "delete" && ((_a = event.data) === null || _a === void 0 ? void 0 : _a._oversize)) {
|
|
207
|
-
console.error(`[Base44 SDK] Realtime broadcast for ${state.entityName}#${event.id} was oversize and got slimmed for transport. ` +
|
|
208
|
-
`Fields >10 KB are empty and the rest of the record may be a stub. ` +
|
|
209
|
-
`Call \`entities.${state.entityName}.get("${event.id}")\` to fetch the full record.`);
|
|
210
|
-
}
|
|
211
|
-
Array.from(state.callbacks.values()).forEach((callback) => {
|
|
212
|
-
try {
|
|
213
|
-
callback(event);
|
|
214
|
-
}
|
|
215
|
-
catch (error) {
|
|
216
|
-
console.error("[Base44 SDK] Subscription callback error:", error);
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
function openRoomSubscription(entityName, room) {
|
|
221
|
-
const state = {
|
|
222
|
-
room,
|
|
223
|
-
entityName,
|
|
224
|
-
callbacks: new Map(),
|
|
225
|
-
unsubscribeFromRoom: () => { },
|
|
226
|
-
closeTimer: null,
|
|
227
|
-
};
|
|
228
|
-
const socket = getSocket();
|
|
229
|
-
state.unsubscribeFromRoom = socket.subscribeToRoom(room, {
|
|
230
|
-
update_model: (msg) => {
|
|
231
|
-
dispatchRealtimeMessage(state, msg.data);
|
|
232
|
-
},
|
|
233
|
-
});
|
|
234
|
-
activeSubscriptions.set(room, state);
|
|
235
|
-
return state;
|
|
236
|
-
}
|
|
237
|
-
return {
|
|
238
|
-
subscribe(entityName, callback) {
|
|
239
|
-
const room = makeRoom(entityName);
|
|
240
|
-
recordSubscriptionActivity(room, entityName, "subscribe");
|
|
241
|
-
let state = activeSubscriptions.get(room);
|
|
242
|
-
if (!state) {
|
|
243
|
-
if (activeSubscriptions.size >=
|
|
244
|
-
normalizedOptions.maxActiveSubscriptions) {
|
|
245
|
-
warnForSubscriptionCap(room, entityName);
|
|
246
|
-
return () => { };
|
|
247
|
-
}
|
|
248
|
-
state = openRoomSubscription(entityName, room);
|
|
249
|
-
}
|
|
250
|
-
else {
|
|
251
|
-
clearPendingClose(state);
|
|
252
|
-
}
|
|
253
|
-
const callbackId = nextCallbackId++;
|
|
254
|
-
state.callbacks.set(callbackId, callback);
|
|
255
|
-
let unsubscribed = false;
|
|
256
|
-
return () => {
|
|
257
|
-
if (unsubscribed) {
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
unsubscribed = true;
|
|
261
|
-
recordSubscriptionActivity(room, entityName, "unsubscribe");
|
|
262
|
-
state.callbacks.delete(callbackId);
|
|
263
|
-
if (state.callbacks.size === 0 &&
|
|
264
|
-
activeSubscriptions.get(room) === state) {
|
|
265
|
-
scheduleRoomClose(state);
|
|
266
|
-
}
|
|
267
|
-
};
|
|
268
|
-
},
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
44
|
/**
|
|
272
45
|
* Creates a handler for a specific entity.
|
|
273
46
|
*
|
|
274
47
|
* @param axios - Axios instance
|
|
275
48
|
* @param appId - Application ID
|
|
276
49
|
* @param entityName - Entity name
|
|
277
|
-
* @param
|
|
50
|
+
* @param getSocket - Function to get the socket instance
|
|
278
51
|
* @returns Entity handler with CRUD methods
|
|
279
52
|
* @internal
|
|
280
53
|
*/
|
|
281
|
-
function createEntityHandler(axios, appId, entityName,
|
|
54
|
+
function createEntityHandler(axios, appId, entityName, getSocket) {
|
|
282
55
|
const baseURL = `/apps/${appId}/entities/${entityName}`;
|
|
283
56
|
return {
|
|
284
57
|
// List entities with optional pagination and sorting
|
|
@@ -353,7 +126,38 @@ function createEntityHandler(axios, appId, entityName, subscriptionManager) {
|
|
|
353
126
|
},
|
|
354
127
|
// Subscribe to realtime updates
|
|
355
128
|
subscribe(callback) {
|
|
356
|
-
|
|
129
|
+
const room = `entities:${appId}:${entityName}`;
|
|
130
|
+
// Get the socket and subscribe to the room
|
|
131
|
+
const socket = getSocket();
|
|
132
|
+
const unsubscribe = socket.subscribeToRoom(room, {
|
|
133
|
+
update_model: (msg) => {
|
|
134
|
+
var _a;
|
|
135
|
+
const event = parseRealtimeMessage(msg.data);
|
|
136
|
+
if (!event) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
// Server signals oversize broadcasts with `_oversize: true` on
|
|
140
|
+
// `data`. The wire payload was slimmed to fit under the realtime
|
|
141
|
+
// transport cap, so big string fields arrive as empty strings (or
|
|
142
|
+
// the whole record collapses to a stub). Surface this to the
|
|
143
|
+
// developer console so they know to fetch the full record on
|
|
144
|
+
// demand (e.g. a follow-up entities.X.get(id) call) instead of
|
|
145
|
+
// rendering the slimmed payload directly. Skip on delete events
|
|
146
|
+
// — the record no longer exists.
|
|
147
|
+
if (event.type !== "delete" && ((_a = event.data) === null || _a === void 0 ? void 0 : _a._oversize)) {
|
|
148
|
+
console.error(`[Base44 SDK] Realtime broadcast for ${entityName}#${event.id} was oversize and got slimmed for transport. ` +
|
|
149
|
+
`Fields >10 KB are empty and the rest of the record may be a stub. ` +
|
|
150
|
+
`Call \`entities.${entityName}.get("${event.id}")\` to fetch the full record.`);
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
callback(event);
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
console.error("[Base44 SDK] Subscription callback error:", error);
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
return unsubscribe;
|
|
357
161
|
},
|
|
358
162
|
};
|
|
359
163
|
}
|
|
@@ -23,42 +23,6 @@ export interface RealtimeEvent<T = any> {
|
|
|
23
23
|
* @typeParam T - The entity type for the event data. Defaults to `any`.
|
|
24
24
|
*/
|
|
25
25
|
export type RealtimeCallback<T = any> = (event: RealtimeEvent<T>) => void;
|
|
26
|
-
/**
|
|
27
|
-
* Client-side controls for realtime entity subscriptions.
|
|
28
|
-
*/
|
|
29
|
-
export interface EntitySubscriptionOptions {
|
|
30
|
-
/**
|
|
31
|
-
* Maximum number of distinct active entity realtime subscriptions allowed
|
|
32
|
-
* for one SDK client instance. Repeated subscriptions to the same entity
|
|
33
|
-
* share one active entity subscription and do not count again.
|
|
34
|
-
*
|
|
35
|
-
* @defaultValue `100`
|
|
36
|
-
*/
|
|
37
|
-
maxActiveSubscriptions?: number;
|
|
38
|
-
/**
|
|
39
|
-
* Number of subscribe/unsubscribe operations for the same entity within
|
|
40
|
-
* `churnWindowMs` before the SDK logs a warning and emits diagnostic
|
|
41
|
-
* telemetry.
|
|
42
|
-
*
|
|
43
|
-
* @defaultValue `20`
|
|
44
|
-
*/
|
|
45
|
-
churnWarningThreshold?: number;
|
|
46
|
-
/**
|
|
47
|
-
* Time window, in milliseconds, used to detect repeated subscribe/unsubscribe
|
|
48
|
-
* churn for one entity.
|
|
49
|
-
*
|
|
50
|
-
* @defaultValue `60000`
|
|
51
|
-
*/
|
|
52
|
-
churnWindowMs?: number;
|
|
53
|
-
/**
|
|
54
|
-
* Grace period, in milliseconds, before the SDK leaves an entity realtime
|
|
55
|
-
* room after its last local callback unsubscribes. A new subscription to
|
|
56
|
-
* the same entity during this window reuses the existing room membership.
|
|
57
|
-
*
|
|
58
|
-
* @defaultValue `1000`
|
|
59
|
-
*/
|
|
60
|
-
emptyRoomGraceMs?: number;
|
|
61
|
-
}
|
|
62
26
|
/**
|
|
63
27
|
* Result returned when deleting a single entity.
|
|
64
28
|
*/
|
|
@@ -88,8 +88,7 @@ export function RoomsSocket({ config }) {
|
|
|
88
88
|
return (_a = handlers.update_model) === null || _a === void 0 ? void 0 : _a.call(handlers, { room, data: dataStr });
|
|
89
89
|
}
|
|
90
90
|
function getListeners(room) {
|
|
91
|
-
|
|
92
|
-
return (_a = roomsToListeners[room]) !== null && _a !== void 0 ? _a : [];
|
|
91
|
+
return roomsToListeners[room];
|
|
93
92
|
}
|
|
94
93
|
const subscribeToRoom = (room, handlers) => {
|
|
95
94
|
if (!roomsToListeners[room]) {
|
|
@@ -103,7 +102,6 @@ export function RoomsSocket({ config }) {
|
|
|
103
102
|
(_b = (_a = roomsToListeners[room]) === null || _a === void 0 ? void 0 : _a.filter((listener) => listener !== handlers)) !== null && _b !== void 0 ? _b : [];
|
|
104
103
|
if (roomsToListeners[room].length === 0) {
|
|
105
104
|
leaveRoom(room);
|
|
106
|
-
delete roomsToListeners[room];
|
|
107
105
|
}
|
|
108
106
|
};
|
|
109
107
|
};
|