@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 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: userAnalyticsModule,
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),
@@ -1,4 +1,4 @@
1
- import type { EntitiesModule, EntitySubscriptionOptions } from "./modules/entities.types.js";
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, EntitySubscriptionOptions, EntityTypeRegistry, ImportResult, RealtimeEventType, RealtimeEvent, RealtimeCallback, SortField, UpdateManyResult, } from "./modules/entities.types.js";
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
- * Only the fields included in the data object will be updated.
132
- * Commonly updated fields include `full_name` and custom profile fields.
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 specific fields
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: Partial<Omit<User, "id" | "created_date" | "updated_date">>): Promise<User>;
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, EntitySubscriptionOptions } from "./entities.types";
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.
@@ -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
- const handler = createEntityHandler(axios, appId, entityName, subscriptionManager);
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 subscriptionManager - Shared realtime subscription manager
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, subscriptionManager) {
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
- return subscriptionManager.subscribe(entityName, callback);
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
- var _a;
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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@base44-preview/sdk",
3
- "version": "0.8.28-pr.173.186369a",
3
+ "version": "0.8.28-pr.174.6d8a9cd",
4
4
  "description": "JavaScript SDK for Base44 API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",