@base44-preview/sdk 0.8.51-pr.293.e71320c → 0.8.52-pr.286.2dbb896

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.
Files changed (32) hide show
  1. package/README.md +10 -0
  2. package/dist/client.js +18 -2
  3. package/dist/index.d.ts +1 -1
  4. package/dist/modules/ai-gateway.d.ts +1 -1
  5. package/dist/modules/ai-gateway.js +2 -1
  6. package/dist/modules/ai-gateway.types.d.ts +23 -13
  7. package/dist/modules/entities.js +9 -31
  8. package/dist/modules/entities.types.d.ts +47 -85
  9. package/dist/modules/integrations.types.d.ts +2 -2
  10. package/dist/platform/client/client.d.ts +9 -0
  11. package/dist/platform/client/client.js +12 -0
  12. package/dist/platform/client/client.types.d.ts +12 -0
  13. package/dist/platform/client/client.types.js +1 -0
  14. package/dist/platform/client/errors.d.ts +10 -0
  15. package/dist/platform/client/errors.js +10 -0
  16. package/dist/platform/client/errors.types.d.ts +2 -0
  17. package/dist/platform/client/errors.types.js +1 -0
  18. package/dist/platform/client/index.d.ts +7 -0
  19. package/dist/platform/client/index.js +3 -0
  20. package/dist/platform/client/modules/builder-protocol.d.ts +11 -0
  21. package/dist/platform/client/modules/builder-protocol.js +32 -0
  22. package/dist/platform/client/modules/builder-socket.d.ts +41 -0
  23. package/dist/platform/client/modules/builder-socket.js +238 -0
  24. package/dist/platform/client/modules/builder-subscription.d.ts +23 -0
  25. package/dist/platform/client/modules/builder-subscription.js +55 -0
  26. package/dist/platform/client/modules/builder.d.ts +4 -0
  27. package/dist/platform/client/modules/builder.events.types.d.ts +348 -0
  28. package/dist/platform/client/modules/builder.events.types.js +1 -0
  29. package/dist/platform/client/modules/builder.js +7 -0
  30. package/dist/platform/client/modules/builder.types.d.ts +47 -0
  31. package/dist/platform/client/modules/builder.types.js +1 -0
  32. package/package.json +40 -5
package/README.md CHANGED
@@ -151,3 +151,13 @@ npm run create-docs
151
151
  cd docs
152
152
  mintlify dev
153
153
  ```
154
+
155
+ ### Platform browser subscriptions
156
+
157
+ The separate `@base44/sdk/platform/client` entry point watches public builder
158
+ updates for up to eight apps over the platform socket. It supports typed events,
159
+ a snapshot on every join and bounded delivery, using a socket-session token your
160
+ backend opens with its workspace key.
161
+ See [setup, public contract and recovery](platform-docs/client.md) and the
162
+ [TypeScript example](examples/platform-client.ts). White-label sockets must be enabled
163
+ for the workspace; never use an API key in the browser.
package/dist/client.js CHANGED
@@ -77,6 +77,12 @@ export function createClient(config) {
77
77
  ...optionalHeaders,
78
78
  "X-App-Id": String(appId),
79
79
  };
80
+ // Base44-State is the platform's signed proof that the caller passed the
81
+ // workspace IP allowlist; the gateway checks it like any other app route.
82
+ const stateHeader = optionalHeaders === null || optionalHeaders === void 0 ? void 0 : optionalHeaders["Base44-State"];
83
+ const aiGatewayHeaders = stateHeader
84
+ ? { "Base44-State": stateHeader }
85
+ : {};
80
86
  const functionHeaders = functionsVersion
81
87
  ? {
82
88
  ...headers,
@@ -187,7 +193,12 @@ export function createClient(config) {
187
193
  serverUrl,
188
194
  token,
189
195
  }),
190
- aiGateway: createAiGatewayModule({ serverUrl, token, appId }),
196
+ aiGateway: createAiGatewayModule({
197
+ serverUrl,
198
+ token,
199
+ appId,
200
+ headers: aiGatewayHeaders,
201
+ }),
191
202
  appLogs: createAppLogsModule(axiosClient, appId),
192
203
  app: createAppModule(axiosClient, appId),
193
204
  users: createUsersModule(axiosClient, appId),
@@ -234,7 +245,12 @@ export function createClient(config) {
234
245
  serverUrl,
235
246
  token,
236
247
  }),
237
- aiGateway: createAiGatewayModule({ serverUrl, token: serviceToken, appId }),
248
+ aiGateway: createAiGatewayModule({
249
+ serverUrl,
250
+ token: serviceToken,
251
+ appId,
252
+ headers: aiGatewayHeaders,
253
+ }),
238
254
  appLogs: createAppLogsModule(serviceRoleAxiosClient, appId),
239
255
  cleanup: () => {
240
256
  if (socket) {
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, CreateClientAnalyticsConfig, CreateClientConfig, CreateClientOptions, Base44ErrorJSON, };
6
6
  export * from "./types.js";
7
- export type { DeleteManyResult, DeleteResult, EntitiesModule, EntityAggregateResult, EntityAggregateSpec, EntityDateBucketUnit, EntityDistinctOptions, EntityFilterOperators, EntityFilterQuery, EntityFilterValue, EntityHandler, EntityListOptions, EntityArrayOptions, EntityPage, EntityRecord, EntityTypeRegistry, EntityUpsertOptions, EntityUpsertResult, ImportResult, RealtimeEventType, RealtimeEvent, RealtimeCallback, SortField, UpdateManyResult, } from "./modules/entities.types.js";
7
+ export type { DeleteManyResult, DeleteResult, EntitiesModule, EntityAggregateResult, EntityAggregateSpec, EntityDateBucketUnit, EntityDistinctOptions, EntityFilterOperators, EntityFilterQuery, EntityFilterValue, EntityHandler, EntityListOptions, EntityPage, EntityRecord, EntityTypeRegistry, EntityUpsertOptions, EntityUpsertResult, 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";
@@ -1,2 +1,2 @@
1
1
  import { AiGatewayModule, AiGatewayModuleConfig } from "./ai-gateway.types.js";
2
- export declare function createAiGatewayModule({ serverUrl, token, appId, }: AiGatewayModuleConfig): AiGatewayModule;
2
+ export declare function createAiGatewayModule({ serverUrl, token, appId, headers, }: AiGatewayModuleConfig): AiGatewayModule;
@@ -3,7 +3,7 @@ const PROVIDER_PATHS = {
3
3
  openai: "openai",
4
4
  typesafe: "typesafe",
5
5
  };
6
- export function createAiGatewayModule({ serverUrl, token, appId, }) {
6
+ export function createAiGatewayModule({ serverUrl, token, appId, headers = {}, }) {
7
7
  const connection = ({ provider = "openai" } = {}) => {
8
8
  var _a;
9
9
  if (!Object.prototype.hasOwnProperty.call(PROVIDER_PATHS, provider)) {
@@ -13,6 +13,7 @@ export function createAiGatewayModule({ serverUrl, token, appId, }) {
13
13
  return {
14
14
  baseURL: `${serverUrl}/api/apps/${appId}/ai/${providerPath}/v1`,
15
15
  token: (_a = token !== null && token !== void 0 ? token : getAccessToken()) !== null && _a !== void 0 ? _a : "",
16
+ headers: { ...headers },
16
17
  };
17
18
  };
18
19
  return {
@@ -9,6 +9,16 @@ export interface AiGatewayConnection {
9
9
  * unauthenticated.
10
10
  */
11
11
  token: string;
12
+ /**
13
+ * Extra headers to send with every gateway request. Pass them to your client's
14
+ * header option, such as `defaultHeaders` for the `openai` SDK and TanStack AI,
15
+ * or `headers` for the Vercel AI SDK.
16
+ *
17
+ * On a client from {@linkcode createClientFromRequest | createClientFromRequest()},
18
+ * this carries the signed `Base44-State` the platform attached to the request,
19
+ * which a workspace IP allowlist requires. Otherwise it's empty.
20
+ */
21
+ headers: Record<string, string>;
12
22
  }
13
23
  /** Options for selecting an AI Gateway provider. */
14
24
  export interface AiGatewayConnectionOptions {
@@ -26,12 +36,14 @@ export interface AiGatewayModuleConfig {
26
36
  token?: string;
27
37
  /** Application ID */
28
38
  appId: string;
39
+ /** Platform headers to forward on gateway requests */
40
+ headers?: Record<string, string>;
29
41
  }
30
42
  /**
31
43
  * AI Gateway module for calling Base44's managed AI models from your own code.
32
44
  *
33
- * `connection()` hands you a `baseURL` and `token` that authenticate as your
34
- * Base44 app. An OpenAI-compatible client is any library, such as the `openai`
45
+ * `connection()` hands you a `baseURL`, `token`, and `headers` that
46
+ * authenticate as your Base44 app. An OpenAI-compatible client is any library, such as the `openai`
35
47
  * SDK or the Vercel AI SDK, that has the same request and response format
36
48
  * as OpenAI's API and lets you point it at a custom `baseURL`
37
49
  * instead of OpenAI's own servers. Pass `connection()`'s values to one of
@@ -80,18 +92,16 @@ export interface AiGatewayModuleConfig {
80
92
  * app until the quota resets. A request is rejected before the model runs if
81
93
  * the app is out of credits. If you need to cap usage per user, build that
82
94
  * check yourself, for example by tracking calls per user in your own entity.
83
- *
84
- * Streaming responses aren't supported yet, so leave `stream` unset on your requests.
85
95
  */
86
96
  export interface AiGatewayModule {
87
97
  /**
88
98
  * Gets the connection details for the Base44 AI Gateway.
89
99
  *
90
- * Returns the `baseURL` and `token` to pass to the selected provider client.
100
+ * Returns the `baseURL`, `token`, and `headers` to pass to the selected provider client.
91
101
  *
92
102
  * @param options - Provider selection. Omit it to use the OpenAI-compatible gateway.
93
103
  *
94
- * @returns The gateway {@linkcode AiGatewayConnection | connection} (`baseURL` and `token`).
104
+ * @returns The gateway {@linkcode AiGatewayConnection | connection} (`baseURL`, `token`, and `headers`).
95
105
  *
96
106
  * @example
97
107
  * ```typescript
@@ -101,8 +111,8 @@ export interface AiGatewayModule {
101
111
  *
102
112
  * // Runs inside a backend function
103
113
  * const base44 = createClientFromRequest(request);
104
- * const { baseURL, token } = base44.aiGateway.connection();
105
- * const openai = new OpenAI({ baseURL, apiKey: token });
114
+ * const { baseURL, token, headers } = base44.aiGateway.connection();
115
+ * const openai = new OpenAI({ baseURL, apiKey: token, defaultHeaders: headers });
106
116
  *
107
117
  * const response = await openai.chat.completions.create({
108
118
  * model: "automatic",
@@ -123,9 +133,9 @@ export interface AiGatewayModule {
123
133
  * // Runs inside a backend function, reviewing a return request
124
134
  * const base44 = createClientFromRequest(request);
125
135
  * const returnRequest = await base44.entities.ReturnRequest.get(returnId);
126
- * const { baseURL, token } = base44.aiGateway.connection();
127
- * // Point any OpenAI-compatible client at `baseURL` with `apiKey: token`.
128
- * const models = createOpenAICompatible({ name: "base44", baseURL, apiKey: token });
136
+ * const { baseURL, token, headers } = base44.aiGateway.connection();
137
+ * // Point any OpenAI-compatible client at `baseURL` with `apiKey: token` and `headers`.
138
+ * const models = createOpenAICompatible({ name: "base44", baseURL, apiKey: token, headers });
129
139
  *
130
140
  * const agent = new ToolLoopAgent({
131
141
  * model: models("automatic"),
@@ -164,8 +174,8 @@ export interface AiGatewayModule {
164
174
  *
165
175
  * // Runs inside a backend function
166
176
  * const base44 = createClientFromRequest(request);
167
- * const { baseURL, token } = base44.aiGateway.connection();
168
- * const models = createOpenAICompatible({ name: "base44", baseURL, apiKey: token });
177
+ * const { baseURL, token, headers } = base44.aiGateway.connection();
178
+ * const models = createOpenAICompatible({ name: "base44", baseURL, apiKey: token, headers });
169
179
  *
170
180
  * const { image } = await generateImage({
171
181
  * model: models.imageModel("automatic"),
@@ -42,14 +42,8 @@ function parseRealtimeMessage(dataStr) {
42
42
  }
43
43
  }
44
44
  const DEFAULT_PAGE_LIMIT = 100;
45
- // Keys of the object form of list()/filter(). An object passed to list() with none of them is a filter query.
46
- const ARRAY_OPTION_KEYS = ["sort", "limit", "skip", "fields", "filter", "query"];
47
- function isPlainObject(value) {
48
- return typeof value === "object" && value !== null && !Array.isArray(value);
49
- }
50
- // Code written for 0.8.49 reads a page from list()/filter() with a cursor or distinct option; keep it working.
51
- function isPageRequest(value) {
52
- return "cursor" in value || "distinct" in value;
45
+ function isPageOptions(value) {
46
+ return typeof value === "object" && value !== null;
53
47
  }
54
48
  /**
55
49
  * Creates a handler for a specific entity.
@@ -79,7 +73,7 @@ function createEntityHandler(axios, appId, entityName, getSocket) {
79
73
  params.fields = fieldsParam(fields);
80
74
  return axios.get(baseURL, { params });
81
75
  };
82
- // GET /{entity}/v2/list: one cursor page of records or distinct values
76
+ // GET /{entity}/v2/list: one cursor page of records or distinct values, shared by list(options) and filter(query, options)
83
77
  const readPage = (options, query) => {
84
78
  const params = {};
85
79
  if (query)
@@ -99,31 +93,15 @@ function createEntityHandler(axios, appId, entityName, getSocket) {
99
93
  return axios.get(`${baseURL}/v2/list`, { params });
100
94
  };
101
95
  return {
96
+ // list(sort, limit, skip, fields) returns an array; list(options) returns one cursor page.
102
97
  async list(...args) {
103
- var _a;
104
- const [first, ...rest] = args;
105
- if (!isPlainObject(first))
106
- return readArray(first, ...rest);
107
- if (isPageRequest(first))
108
- return readPage(first, first.query);
109
- if (Object.keys(first).some((key) => ARRAY_OPTION_KEYS.includes(key))) {
110
- return readArray(first.sort, first.limit, first.skip, first.fields, (_a = first.filter) !== null && _a !== void 0 ? _a : first.query);
111
- }
112
- // The object took sort's slot: list({}, 5) means limit 5; list({ email }, '-created_date', 100) follows filter()'s order.
113
- return typeof rest[0] === "string"
114
- ? readArray(rest[0], rest[1], rest[2], rest[3], first)
115
- : readArray(undefined, rest[0], rest[1], rest[2], first);
98
+ const [sort, limit, skip, fields] = args;
99
+ return isPageOptions(sort) ? readPage(sort) : readArray(sort, limit, skip, fields);
116
100
  },
101
+ // filter(query, sort, limit, skip, fields) returns an array; filter(query, options) returns one cursor page.
117
102
  async filter(query, ...args) {
118
- const [second, ...rest] = args;
119
- if (!isPlainObject(second))
120
- return readArray(second, rest[0], rest[1], rest[2], query);
121
- if (isPageRequest(second))
122
- return readPage(second, query);
123
- return readArray(second.sort, second.limit, second.skip, second.fields, query);
124
- },
125
- async page(options = {}) {
126
- return readPage(options, options.query);
103
+ const [sort, limit, skip, fields] = args;
104
+ return isPageOptions(sort) ? readPage(sort, query) : readArray(sort, limit, skip, fields, query);
127
105
  },
128
106
  // Get entity by ID
129
107
  async get(id) {
@@ -51,14 +51,13 @@ export interface UpdateManyResult {
51
51
  has_more: boolean;
52
52
  }
53
53
  /**
54
- * Options object accepted by {@linkcode EntityHandler.page | page()} to read one cursor page of records.
54
+ * Options object accepted by {@linkcode EntityHandler.list | list()} and
55
+ * {@linkcode EntityHandler.filter | filter()} to read one cursor page.
55
56
  *
56
57
  * @typeParam T - Entity record type.
57
58
  * @typeParam K - The fields to include in each record.
58
59
  */
59
60
  export interface EntityListOptions<T, K extends keyof T = keyof T> {
60
- /** Filter for the records, in the same form as {@linkcode EntityHandler.filter | filter()}'s query. Defaults to all records. */
61
- query?: EntityFilterQuery<T>;
62
61
  /** Sort parameter, such as `'-created_date'` for descending. Defaults to `'-created_date'`. */
63
62
  sort?: SortField<T>;
64
63
  /** Maximum number of records per page, up to 5,000. Defaults to 100. */
@@ -74,15 +73,14 @@ export interface EntityListOptions<T, K extends keyof T = keyof T> {
74
73
  fields?: K[];
75
74
  }
76
75
  /**
77
- * Options object accepted by {@linkcode EntityHandler.page | page()} to read the distinct values of one field
76
+ * Options object accepted by {@linkcode EntityHandler.list | list()} and
77
+ * {@linkcode EntityHandler.filter | filter()} to read the distinct values of one field
78
78
  * instead of records.
79
79
  *
80
80
  * @typeParam T - Entity record type.
81
81
  * @typeParam K - The field whose distinct values to read.
82
82
  */
83
83
  export interface EntityDistinctOptions<T, K extends keyof T = keyof T> {
84
- /** Filter for the records whose values are read. Defaults to all records. */
85
- query?: EntityFilterQuery<T>;
86
84
  /** Field whose distinct values to return, in ascending order. Array fields contribute each element. */
87
85
  distinct: K;
88
86
  /** Maximum number of values per page, up to 1,000. Defaults to 100. */
@@ -91,26 +89,8 @@ export interface EntityDistinctOptions<T, K extends keyof T = keyof T> {
91
89
  cursor?: string | null;
92
90
  }
93
91
  /**
94
- * Options object accepted by {@linkcode EntityHandler.list | list()} and
95
- * {@linkcode EntityHandler.filter | filter()} in place of positional arguments. Both still return an array.
96
- *
97
- * @typeParam T - Entity record type.
98
- * @typeParam K - The fields to include in each record.
99
- */
100
- export interface EntityArrayOptions<T, K extends keyof T = keyof T> {
101
- /** Sort parameter, such as `'-created_date'` for descending. Defaults to `'-created_date'`. */
102
- sort?: SortField<T>;
103
- /** Maximum number of records to return. Defaults to `5000`. */
104
- limit?: number;
105
- /** Number of records to skip. Defaults to `0`. For loops over many records, use {@linkcode EntityHandler.page | page()}. */
106
- skip?: number;
107
- /** Array of field names to include in each record. Defaults to all fields. */
108
- fields?: K[];
109
- /** Filter for the records. Only read by `list()`; `filter()` takes its query as the first argument. */
110
- filter?: EntityFilterQuery<T>;
111
- }
112
- /**
113
- * One page of records, returned by {@linkcode EntityHandler.page | page()}.
92
+ * One page of records, returned by {@linkcode EntityHandler.list | list()} and
93
+ * {@linkcode EntityHandler.filter | filter()} when called with an options object.
114
94
  *
115
95
  * @typeParam T - Record type of the items.
116
96
  */
@@ -356,19 +336,17 @@ export interface EntityHandler<T = any> {
356
336
  * pagination, and field selection.
357
337
  *
358
338
  * **Note:** The maximum limit is 5,000 items per request. To read more than
359
- * one page, use {@linkcode EntityHandler.page | page()} with a cursor instead of
360
- * `skip`: every page costs the same however deep you are, and records deleted
361
- * between pages never shift the boundary.
362
- *
363
- * The arguments can also be passed as one {@linkcode EntityArrayOptions | options object}
364
- * (`{ sort, limit, skip, fields, filter }`); the result is still an array.
339
+ * one page, pass an {@linkcode EntityListOptions | options object} with a
340
+ * `cursor` instead of `skip`: every page costs the same however deep you are,
341
+ * and records deleted between pages never shift the boundary. `skip` is kept
342
+ * for existing code and is deprecated for loops.
365
343
  *
366
344
  * @typeParam K - The fields to include in the response. Defaults to all fields.
367
345
  * @param sort - Sort parameter, such as `'-created_date'` for descending. Defaults to `'-created_date'`.
368
346
  * @param limit - Maximum number of results to return. Defaults to `5000`.
369
- * @param skip - Number of results to skip for pagination. Defaults to `0`. For loops, use `page()`.
347
+ * @param skip - Number of results to skip for pagination. Defaults to `0`. Deprecated for loops; use a cursor.
370
348
  * @param fields - Array of field names to include in the response. Defaults to all fields.
371
- * @returns Promise resolving to an array of records with selected fields.
349
+ * @returns Promise resolving to an array of records with selected fields. When called with an options object, resolves instead to an {@linkcode EntityPage | EntityPage} with `items`, `next_cursor` and `has_more`; with a `distinct` option the items are the field's values.
372
350
  *
373
351
  * @example
374
352
  * ```typescript
@@ -397,18 +375,25 @@ export interface EntityHandler<T = any> {
397
375
  *
398
376
  * @example
399
377
  * ```typescript
400
- * // The same arguments as one options object; still an array
401
- * const recent = await base44.entities.MyEntity.list({ sort: '-created_date', limit: 10 });
378
+ * // Walk every record with a cursor. Pass an options object instead of
379
+ * // positional arguments to get a page with `next_cursor` and `has_more`.
380
+ * let page = await base44.entities.MyEntity.list({ sort: '-created_date', limit: 1000 });
381
+ * await exportRows(page.items);
382
+ * while (page.has_more) {
383
+ * page = await base44.entities.MyEntity.list({ cursor: page.next_cursor, limit: 1000 });
384
+ * await exportRows(page.items);
385
+ * }
386
+ * ```
387
+ *
388
+ * @example
389
+ * ```typescript
390
+ * // Distinct values of one field, instead of records
391
+ * const { items: categories } = await base44.entities.Product.list({ distinct: 'category' });
402
392
  * ```
403
393
  */
404
394
  list<K extends keyof T = keyof T>(sort?: SortField<T>, limit?: number, skip?: number, fields?: K[]): Promise<Pick<T, K>[]>;
405
- list<K extends keyof T = keyof T>(options: EntityArrayOptions<T, K>): Promise<Pick<T, K>[]>;
406
- /** @deprecated Use {@linkcode EntityHandler.page | page()}. */
407
395
  list<K extends keyof T>(options: EntityDistinctOptions<T, K>): Promise<EntityPage<T[K]>>;
408
- /** @deprecated Use {@linkcode EntityHandler.page | page()}. */
409
- list<K extends keyof T = keyof T>(options: EntityListOptions<T, K> & {
410
- cursor: string | null;
411
- }): Promise<EntityPage<Pick<T, K>>>;
396
+ list<K extends keyof T = keyof T>(options: EntityListOptions<T, K>): Promise<EntityPage<Pick<T, K>>>;
412
397
  /**
413
398
  * Filters records based on a query.
414
399
  *
@@ -416,13 +401,10 @@ export interface EntityHandler<T = any> {
416
401
  * sorting, pagination, and field selection.
417
402
  *
418
403
  * **Note:** The maximum limit is 5,000 items per request. To read more than
419
- * one page, use {@linkcode EntityHandler.page | page()} with a cursor instead of
420
- * `skip`: every page costs the same however deep you are, and records deleted
421
- * between pages never shift the boundary.
422
- *
423
- * The arguments after the query can also be passed as one
424
- * {@linkcode EntityArrayOptions | options object} (`{ sort, limit, skip, fields }`);
425
- * the result is still an array.
404
+ * one page, pass an {@linkcode EntityListOptions | options object} with a
405
+ * `cursor` instead of `skip`: every page costs the same however deep you are,
406
+ * and records deleted between pages never shift the boundary. `skip` is kept
407
+ * for existing code and is deprecated for loops.
426
408
  *
427
409
  * @typeParam K - The fields to include in the response. Defaults to all fields.
428
410
  * @param query - Query object with field-value pairs. Each key should be a field name
@@ -432,9 +414,9 @@ export interface EntityHandler<T = any> {
432
414
  * provided values, or documented MongoDB query operators for advanced filtering.
433
415
  * @param sort - Sort parameter, such as `'-created_date'` for descending. Defaults to `'-created_date'`.
434
416
  * @param limit - Maximum number of results to return. Defaults to `5000`.
435
- * @param skip - Number of results to skip for pagination. Defaults to `0`. For loops, use `page()`.
417
+ * @param skip - Number of results to skip for pagination. Defaults to `0`. Deprecated for loops; use a cursor.
436
418
  * @param fields - Array of field names to include in the response. Defaults to all fields.
437
- * @returns Promise resolving to an array of filtered records with selected fields.
419
+ * @returns Promise resolving to an array of filtered records with selected fields. When called with an options object, resolves instead to an {@linkcode EntityPage | EntityPage} with `items`, `next_cursor` and `has_more`; with a `distinct` option the items are the field's values.
438
420
  *
439
421
  * @example
440
422
  * ```typescript
@@ -514,39 +496,15 @@ export interface EntityHandler<T = any> {
514
496
  *
515
497
  * @example
516
498
  * ```typescript
517
- * // Sort and limit as an options object; still an array
518
- * const open = await base44.entities.Order.filter({ status: 'open' }, { sort: '-created_date', limit: 20 });
519
- * ```
520
- */
521
- filter<K extends keyof T = keyof T>(query: EntityFilterQuery<T>, sort?: SortField<T>, limit?: number, skip?: number, fields?: K[]): Promise<Pick<T, K>[]>;
522
- filter<K extends keyof T = keyof T>(query: EntityFilterQuery<T>, options: EntityArrayOptions<T, K>): Promise<Pick<T, K>[]>;
523
- /** @deprecated Use {@linkcode EntityHandler.page | page()}. */
524
- filter<K extends keyof T>(query: EntityFilterQuery<T>, options: EntityDistinctOptions<T, K>): Promise<EntityPage<T[K]>>;
525
- /** @deprecated Use {@linkcode EntityHandler.page | page()}. */
526
- filter<K extends keyof T = keyof T>(query: EntityFilterQuery<T>, options: EntityListOptions<T, K> & {
527
- cursor: string | null;
528
- }): Promise<EntityPage<Pick<T, K>>>;
529
- /**
530
- * Reads one page of records, or of one field's distinct values, with a cursor.
531
- *
532
- * Use it for "load more", infinite scroll and exports. Every page costs the same
533
- * however deep you are, records deleted between pages never shift the boundary,
534
- * and records that share a sort value are each returned once. Pass `next_cursor`
535
- * from one page as `cursor` on the next; the token carries the query, sort and
536
- * fields, so a later page needs only `cursor` and `limit`. Stop when `has_more`
537
- * is false. Every page holds `limit` items except the last.
538
- *
539
- * @typeParam K - The fields to include in each record, or the field whose values to read.
540
- * @param options - `{ query, sort, limit, fields, cursor }` for records, or `{ query, distinct, limit, cursor }` for distinct values.
541
- * @returns Promise resolving to an {@linkcode EntityPage | EntityPage} with `items`, `next_cursor` and `has_more`.
542
- *
543
- * @example
544
- * ```typescript
545
- * // Walk all matching records
546
- * let page = await base44.entities.Order.page({ query: { status: 'open' }, sort: '-created_date', limit: 1000 });
499
+ * // Walk all matching records with a cursor. Pass an options object as the
500
+ * // second argument to get a page with `next_cursor` and `has_more`.
501
+ * let page = await base44.entities.Order.filter(
502
+ * { status: 'open' },
503
+ * { sort: '-created_date', limit: 1000 }
504
+ * );
547
505
  * await exportRows(page.items);
548
506
  * while (page.has_more) {
549
- * page = await base44.entities.Order.page({ cursor: page.next_cursor, limit: 1000 });
507
+ * page = await base44.entities.Order.filter({ status: 'open' }, { cursor: page.next_cursor, limit: 1000 });
550
508
  * await exportRows(page.items);
551
509
  * }
552
510
  * ```
@@ -554,11 +512,15 @@ export interface EntityHandler<T = any> {
554
512
  * @example
555
513
  * ```typescript
556
514
  * // Distinct values of one field among the matching records
557
- * const { items: agents } = await base44.entities.Order.page({ query: { status: 'open' }, distinct: 'agent_id' });
515
+ * const { items: agents } = await base44.entities.Order.filter(
516
+ * { status: 'open' },
517
+ * { distinct: 'agent_id' }
518
+ * );
558
519
  * ```
559
520
  */
560
- page<K extends keyof T>(options: EntityDistinctOptions<T, K>): Promise<EntityPage<T[K]>>;
561
- page<K extends keyof T = keyof T>(options?: EntityListOptions<T, K>): Promise<EntityPage<Pick<T, K>>>;
521
+ filter<K extends keyof T = keyof T>(query: EntityFilterQuery<T>, sort?: SortField<T>, limit?: number, skip?: number, fields?: K[]): Promise<Pick<T, K>[]>;
522
+ filter<K extends keyof T>(query: EntityFilterQuery<T>, options: EntityDistinctOptions<T, K>): Promise<EntityPage<T[K]>>;
523
+ filter<K extends keyof T = keyof T>(query: EntityFilterQuery<T>, options: EntityListOptions<T, K>): Promise<EntityPage<Pick<T, K>>>;
562
524
  /**
563
525
  * Gets a single record by ID.
564
526
  *
@@ -127,7 +127,7 @@ export interface UploadPrivateFileResult {
127
127
  export interface CreateFileSignedUrlParams {
128
128
  /** URI of the uploaded private file. */
129
129
  file_uri: string;
130
- /** How long the signed URL should be valid for, in seconds.
130
+ /** How long the signed URL should be valid for, in seconds. Must be between `60` (1 minute) and `3600` (1 hour).
131
131
  * @default 300 (5 minutes)
132
132
  */
133
133
  expires_in?: number;
@@ -352,7 +352,7 @@ export interface CoreIntegrations {
352
352
  * // Create a signed URL for a private file
353
353
  * const { signed_url } = await base44.integrations.Core.CreateFileSignedUrl({
354
354
  * file_uri: "private/user123/document.pdf",
355
- * expires_in: 7200 // URL expires in 2 hours
355
+ * expires_in: 1800 // URL expires in 30 minutes
356
356
  * });
357
357
  * console.log(signed_url); // https://...?signature=...
358
358
  * ```
@@ -0,0 +1,9 @@
1
+ import type { PlatformClientOptions } from "./client.types.js";
2
+ import type { BuilderModule } from "./modules/builder.types.js";
3
+ /** Browser platform client. Construction creates no sockets, timers or network requests. */
4
+ export declare class Base44PlatformClient {
5
+ /** Lazy builder subscriptions with independent session lifecycles. */
6
+ readonly builder: BuilderModule;
7
+ /** Configure shared service/auth settings; each module initializes its own resources. */
8
+ constructor(options: PlatformClientOptions);
9
+ }
@@ -0,0 +1,12 @@
1
+ import { createBuilder } from "./modules/builder.js";
2
+ /** Browser platform client. Construction creates no sockets, timers or network requests. */
3
+ export class Base44PlatformClient {
4
+ /** Configure shared service/auth settings; each module initializes its own resources. */
5
+ constructor(options) {
6
+ const url = new URL(options.serverUrl);
7
+ if (!["https:", "http:"].includes(url.protocol) || url.username || url.password || url.search || url.hash || url.pathname !== "/") {
8
+ throw new TypeError("serverUrl must be an HTTP(S) origin without credentials, path, query or fragment");
9
+ }
10
+ this.builder = Object.freeze(createBuilder({ ...options, serverUrl: url.origin }));
11
+ }
12
+ }
@@ -0,0 +1,12 @@
1
+ /** Shared configuration for browser platform modules. Never supply an API key. */
2
+ export interface PlatformClientOptions {
3
+ /** Origin of the platform service, e.g. https://base44.app. No path/query/credentials. */
4
+ serverUrl: string;
5
+ /**
6
+ * Return a socket-session token from your backend, which opens the session with its
7
+ * workspace key (`POST /api/service/socket-sessions`) for the apps this page watches.
8
+ * Called when a builder session first connects, and again only after the server
9
+ * rejects or expires the current session. Sessions last one hour.
10
+ */
11
+ getSessionToken: () => string | Promise<string>;
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ import type { PlatformSocketErrorCode } from "./errors.types.js";
2
+ /** Sanitized failure. Original session-token provider, handler and server exceptions are not retained. */
3
+ export declare class PlatformSocketError extends Error {
4
+ /** Stable machine-readable category. */
5
+ readonly code: PlatformSocketErrorCode;
6
+ /** Associated app, when the server identifies a valid room. */
7
+ readonly appId?: string;
8
+ /** Create a sanitized error with no credential-bearing cause or payload. */
9
+ constructor(code: PlatformSocketErrorCode, appId?: string);
10
+ }
@@ -0,0 +1,10 @@
1
+ /** Sanitized failure. Original session-token provider, handler and server exceptions are not retained. */
2
+ export class PlatformSocketError extends Error {
3
+ /** Create a sanitized error with no credential-bearing cause or payload. */
4
+ constructor(code, appId) {
5
+ super(`Platform socket: ${code}`);
6
+ this.name = "PlatformSocketError";
7
+ this.code = code;
8
+ this.appId = appId;
9
+ }
10
+ }
@@ -0,0 +1,2 @@
1
+ /** Server refusals and client transport/processing failures. */
2
+ export type PlatformSocketErrorCode = "connection_denied" | "connection_failed" | "session_unavailable" | "session_replaced" | "access_denied" | "access_revoked" | "snapshot_unavailable" | "subscription_limit" | "delivery_overflow" | "protocol_error" | "handler_failed" | "client_closed";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ /** Browser platform modules, separate from the runtime and server SDKs. */
2
+ export { Base44PlatformClient } from "./client.js";
3
+ export { PlatformSocketError } from "./errors.js";
4
+ export type { PlatformSocketErrorCode } from "./errors.types.js";
5
+ export type { PlatformClientOptions } from "./client.types.js";
6
+ export type { BuilderModule, BuilderInitOptions, BuilderSession, PlatformSubscription, SubscriptionOptions } from "./modules/builder.types.js";
7
+ export type { AppUpdate, AppStatus, ChatMessage, MessageParams, ToolCall, ToolDisplayProjection, ToolQuestionOption, ToolQuestion, ToolQuestionArguments, ToolSecretField, ToolSecretArguments, ToolPackageOperation, ToolPackageArguments, ToolPlanUpdate, ToolPlanArguments, ToolPrdArguments, ToolMediaArguments, ToolMediaResult, ToolQuestionAnswer, ToolQuestionInput, ToolOutcome, ToolGuardApproval, ToolResult, QueueItem, QueueUpdate, TaskUpdate, ImageReady, Directive, PlatformEventMap, PlatformEvent, Snapshot } from "./modules/builder.events.types.js";
@@ -0,0 +1,3 @@
1
+ /** Browser platform modules, separate from the runtime and server SDKs. */
2
+ export { Base44PlatformClient } from "./client.js";
3
+ export { PlatformSocketError } from "./errors.js";
@@ -0,0 +1,11 @@
1
+ import type { PlatformEvent, PlatformEventMap, Snapshot } from "./builder.events.types.js";
2
+ export declare const eventNames: readonly ["update_model", "directive", "queue_update", "task_update", "image_ready"];
3
+ /** Error frames that name an app room. */
4
+ export declare const roomErrorCodes: readonly ["access_denied", "access_revoked", "snapshot_unavailable"];
5
+ export declare const appPattern: RegExp;
6
+ export declare const roomFor: (appId: string) => string;
7
+ export declare function object(value: unknown): Record<string, unknown>;
8
+ export declare function string(value: unknown): string;
9
+ export declare function appFromRoom(value: unknown): string | undefined;
10
+ export declare function decode(type: keyof PlatformEventMap, appId: string, raw: unknown): PlatformEvent;
11
+ export declare function decodeSnapshot(raw: unknown): Snapshot;
@@ -0,0 +1,32 @@
1
+ export const eventNames = ["update_model", "directive", "queue_update", "task_update", "image_ready"];
2
+ /** Error frames that name an app room. */
3
+ export const roomErrorCodes = ["access_denied", "access_revoked", "snapshot_unavailable"];
4
+ export const appPattern = /^[a-f0-9]{24}$/;
5
+ export const roomFor = (appId) => `/apps/${appId}`;
6
+ export function object(value) {
7
+ if (!value || typeof value !== "object" || Array.isArray(value))
8
+ throw new Error("Invalid frame");
9
+ return value;
10
+ }
11
+ export function string(value) {
12
+ if (typeof value !== "string" || !value)
13
+ throw new Error("Invalid string");
14
+ return value;
15
+ }
16
+ export function appFromRoom(value) {
17
+ return typeof value === "string" && /^\/apps\/[a-f0-9]{24}$/.test(value) ? value.slice(6) : undefined;
18
+ }
19
+ export function decode(type, appId, raw) {
20
+ const frame = object(raw);
21
+ const wrapped = type === "update_model" || type === "task_update" || type === "image_ready";
22
+ const data = wrapped ? object(JSON.parse(string(frame.data))) : frame;
23
+ // Payload schemas are owned by the service; only decode/validate the transport envelope here.
24
+ return { type, appId, data };
25
+ }
26
+ export function decodeSnapshot(raw) {
27
+ const frame = object(raw);
28
+ if (!appFromRoom(frame.room) || !Array.isArray(frame.messages) || (frame.status !== null && typeof frame.status !== "object")) {
29
+ throw new Error("Invalid snapshot");
30
+ }
31
+ return frame;
32
+ }