@base44-preview/sdk 0.8.53-pr.289.39e1f1f → 0.8.53-pr.289.545edf7

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/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, EntityPage, EntityRecord, EntityTypeRegistry, EntityUpsertOptions, EntityUpsertResult, ImportResult, RealtimeEventType, RealtimeEvent, RealtimeCallback, SortField, UpdateManyResult, } from "./modules/entities.types.js";
7
+ export type { DeleteManyResult, DeleteResult, EntitiesModule, EntityAggregateResult, EntityAggregateSpec, EntityDateBucket, 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,13 +1,14 @@
1
1
  /**
2
2
  * Maps actor names to their incoming and outgoing message types.
3
3
  *
4
- * Extend this interface through module augmentation when you want typed actor
5
- * messages without generating types with the CLI. For each actor, `toServer`
6
- * defines incoming messages that a client sends to the actor. `toClient` defines
7
- * outgoing messages that the actor sends to connected clients.
4
+ * Extend this interface when you want typed actor
5
+ * messages without generating types with the CLI:
6
+ * - `toServer`: Defines incoming messages that a client sends to the actor.
7
+ * - `toClient`: Defines outgoing messages that the actor sends to connected clients.
8
8
  *
9
- * To generate types from deployed actors instead, use the
9
+ * To generate types from deployed actors, use the
10
10
  * [`types generate`](/developers/references/cli/commands/types-generate) CLI command.
11
+ *
11
12
  * To learn how incoming and outgoing messages work, see
12
13
  * [message types](/developers/backend/resources/actors/overview#message-types).
13
14
  *
@@ -31,7 +32,7 @@ export interface ActorRegistry {
31
32
  * with [`types generate`](/developers/references/cli/commands/types-generate).
32
33
  *
33
34
  * The generated names provide autocomplete for deployed actors. To define
34
- * incoming and outgoing message types manually, augment [ActorRegistry](#actorregistry).
35
+ * incoming and outgoing message types manually, augment {@linkcode ActorRegistry}.
35
36
  */
36
37
  export interface ActorNameRegistry {
37
38
  }
@@ -43,24 +44,21 @@ type ToServerFor<N extends string> = N extends keyof ActorRegistry ? ActorRegist
43
44
  toServer: infer O;
44
45
  } ? O : unknown : unknown;
45
46
  /**
46
- * Configures the connection that [ActorRef.connect](#connect) opens.
47
+ * Configures the connection that {@linkcode ActorRef.connect | connect()} opens.
47
48
  */
48
49
  export interface ActorConnectOptions {
49
50
  /**
50
51
  * Connection ID that the actor receives as `conn.id`.
51
52
  *
52
- * To let the actor recognize the same client if it reconnects, use a stable
53
+ * To let the actor recognize a client when it reconnects, use a stable
53
54
  * value, such as an ID stored per browser tab. If you omit this property, the
54
- * SDK generates a connection ID.
55
- *
56
- * For more about connection IDs, see
57
- * [connections](/developers/backend/resources/actors/reference#connections).
55
+ * SDK generates a new connection ID.
58
56
  */
59
57
  id?: string;
60
58
  }
61
59
  /**
62
60
  * Represents a listener for messages from the actor, registered with
63
- * [Connection.subscribe](#subscribe).
61
+ * {@linkcode Connection.subscribe | subscribe()}.
64
62
  */
65
63
  export interface ActorSubscription {
66
64
  /**
@@ -75,10 +73,11 @@ export interface ActorSubscription {
75
73
  unsubscribe(): void;
76
74
  }
77
75
  /**
78
- * Represents a client's WebSocket connection to an actor session.
76
+ * Represents a client's WebSocket connection to an actor session, returned after calling
77
+ * [`connect()`](#connect). The socket queues messages you send before it opens.
79
78
  *
80
- * [ActorRef.connect](#connect) returns this object. The socket buffers messages
81
- * you send before it opens.
79
+ * Learn more about
80
+ * [connections](/developers/backend/resources/actors/reference#connections).
82
81
  */
83
82
  export interface Connection<N extends string = string> {
84
83
  /** Connection ID that the actor receives as `conn.id`. */
@@ -106,10 +105,10 @@ export interface Connection<N extends string = string> {
106
105
  /**
107
106
  * Sends a message to the actor.
108
107
  *
109
- * The socket buffers messages until it opens. After you call [close](#close),
110
- * the socket drops further sends.
108
+ * The socket queues messages until it opens. When you call {@linkcode Connection.close | close()},
109
+ * the socket drops any further sent messages.
111
110
  *
112
- * @param data - Message to send to the actor. The type comes from [ActorRegistry](#actorregistry) when you register the actor there.
111
+ * @param data - Message to send to the actor. The type comes from {@linkcode ActorRegistry} when you register the actor there.
113
112
  *
114
113
  * @example
115
114
  * ```typescript
@@ -122,8 +121,7 @@ export interface Connection<N extends string = string> {
122
121
  * Closes the connection and removes all listeners.
123
122
  *
124
123
  * You can call this method more than once. A connection also closes itself
125
- * when it fails permanently. To open a new connection, call
126
- * [ActorRef.connect](#connect) again.
124
+ * when it fails permanently.
127
125
  *
128
126
  * @example
129
127
  * ```typescript
@@ -136,22 +134,20 @@ export interface Connection<N extends string = string> {
136
134
  /**
137
135
  * Represents a reference to an actor session, identified by actor name and session ID.
138
136
  *
139
- * Call [connect](#connect) to open the WebSocket and get a [Connection](#connection).
137
+ * Call {@linkcode ActorRef.connect | connect()} to open the WebSocket and get a [Connection](#returns).
140
138
  */
141
139
  export interface ActorRef<N extends string = string> {
142
140
  /**
143
- * Creates or returns the [Connection](#connection) for this session.
141
+ * Creates or returns the [Connection](#returns) for this session.
144
142
  *
145
- * Repeated calls return the same connection until it closes. If the connection
146
- * fails permanently, for example because the actor doesn't exist or the actor
147
- * denies the connection, fix the cause and call `connect()` again. Then
148
- * subscribe again on the new connection.
143
+ * Calling `connect()` again on the same session reference returns the same
144
+ * connection until it closes.
149
145
  *
150
146
  * For a sample flow, see
151
147
  * [connect a client to a session](/developers/backend/resources/actors/sample-flows#connect-a-client-to-a-session).
152
148
  *
153
149
  * @param options - Optional connection settings, such as a stable connection ID.
154
- * @returns The [Connection](#connection) for this actor session.
150
+ * @returns The [Connection](#returns) for this actor session.
155
151
  *
156
152
  * @example
157
153
  * ```typescript
@@ -165,7 +161,7 @@ export interface ActorRef<N extends string = string> {
165
161
  * Selects a session for a named actor.
166
162
  *
167
163
  * TypeScript infers message types when you register the actor in
168
- * [ActorRegistry](#actorregistry). [ActorNameRegistry](#actornameregistry)
164
+ * {@linkcode ActorRegistry}. {@linkcode ActorNameRegistry}
169
165
  * provides autocomplete for actor names only.
170
166
  */
171
167
  export interface ActorClient<N extends string = string> {
@@ -186,33 +182,32 @@ export interface ActorClient<N extends string = string> {
186
182
  (instanceId: string): ActorRef<N>;
187
183
  }
188
184
  /**
189
- * Connects your frontend to [actor sessions](/developers/backend/resources/actors/overview),
190
- * shared live backend processes where clients can exchange messages in realtime.
185
+ * Actors module for connecting a client to an [actor](/developers/backend/resources/actors/overview) sessions, managing connections, and exchanging messages.
186
+ *
187
+ * An actor is a long-running backend process that multiple clients connect to simultaneously. A session
188
+ * is a running instance of an actor, identified by the actor name and a session ID. Each
189
+ * session manages its own state, storage, and client connections independently.
190
+ *
191
+ * The actors module supports several objects with the following functionality:
191
192
  *
192
- * The following table lists what you can do with the actors module:
193
+ * - {@linkcode ActorRef.connect | connect()}: Open a WebSocket connection to a session.
194
+ * - {@linkcode Connection.subscribe | subscribe()}: Receive messages from the actor.
195
+ * - {@linkcode ActorSubscription.unsubscribe | unsubscribe()}: Stop receiving messages from the actor without closing the connection.
196
+ * - {@linkcode Connection.send | send()}: Send messages to the actor.
197
+ * - {@linkcode Connection.close | close()}: Close the WebSocket connection to the actor.
193
198
  *
194
- * | Member | Purpose |
195
- * |---|---|
196
- * | [`connect()`](#connect) | Opens a connection to a session. Clients that use the same actor name and session ID join the same session. |
197
- * | [`subscribe()`](#subscribe) | Receives messages from an actor. |
198
- * | [`send()`](#send) | Sends a message to an actor. |
199
- * | [`ActorRegistry`](#actorregistry) | Defines message types for autocomplete and compile-time safety. |
199
+ * For a sample flow, see
200
+ * [connect a client to a session](/developers/backend/resources/actors/sample-flows#connect-a-client-to-a-session).
200
201
  *
201
202
  * ## Authentication modes
202
203
  *
203
- * This module is available in anonymous and user authentication modes.
204
- * Apps that require login can reject anonymous connections in the actor's `handleConnect()` method.
205
- * To learn more, see [manage client connections](/developers/backend/resources/actors/sample-flows#manage-client-connections).
204
+ * This module is available to use with a client in anonymous or user authentication mode. Access it
205
+ * through `base44.actors`. It isn't available in
206
+ * [service role authentication mode](/developers/references/sdk/getting-started/client#service-role).
206
207
  *
207
- * @example
208
- * ```typescript
209
- * // Connect, subscribe, send, and close
210
- * const conn = base44.actors.chatRoom("session-1").connect({ id: "tab-1" });
211
- * const sub = conn.subscribe((msg) => console.log(msg));
212
- * conn.send({ type: "message", text: "hi" });
213
- * sub.unsubscribe();
214
- * conn.close();
215
- * ```
208
+ * The actor receives each client's [identity](/developers/backend/resources/actors/reference#connections)
209
+ * when the client connects. A client that hasn't logged in connects as anonymous, and a client that
210
+ * has [logged in](/developers/references/sdk/docs/interfaces/auth) connects as authenticated.
216
211
  */
217
212
  export type ActorsModule = {
218
213
  [K in AllActorNames]: K extends keyof ActorRegistry ? ActorClient<string & K> : ActorClient;
@@ -58,15 +58,15 @@ export interface UpdateManyResult {
58
58
  * @typeParam K - The fields to include in each record.
59
59
  */
60
60
  export interface EntityListOptions<T, K extends keyof T = keyof T> {
61
- /** Sort parameter, such as `'-created_date'` for descending. Defaults to `'-created_date'`. */
61
+ /** Sort parameter, such as `'-priority'` for descending. Defaults to `'-created_date'`. */
62
62
  sort?: SortField<T>;
63
63
  /** Maximum number of records per page, up to 5,000. Defaults to 100. */
64
64
  limit?: number;
65
65
  /**
66
- * `next_cursor` from the previous page. Omit or pass `null` for the first page.
66
+ * The `next_cursor` from the previous page. Omit or pass `null` for the first page.
67
67
  *
68
- * The token carries the query, sort and fields of the walk, so a later page needs only
69
- * `cursor` and `limit`. Passing a different query, sort or fields with a cursor is an error.
68
+ * The token carries this walk's parameters, so a later page needs only
69
+ * `cursor` and `limit`. Passing different parameters with a cursor is an error.
70
70
  */
71
71
  cursor?: string | null;
72
72
  /** Array of field names to include in each record. Defaults to all fields. */
@@ -81,49 +81,52 @@ export interface EntityListOptions<T, K extends keyof T = keyof T> {
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
- /** Field whose distinct values to return, in ascending order. Array fields contribute each element. */
84
+ /** Field whose distinct values to return, in ascending order. For an array field like `tags`, this returns the distinct individual tags used across records, not the distinct arrays. */
85
85
  distinct: K;
86
86
  /** Maximum number of values per page, up to 1,000. Defaults to 100. */
87
87
  limit?: number;
88
- /** `next_cursor` from the previous page. Omit or pass `null` for the first page. The token carries the query and field. */
88
+ /** The `next_cursor` from the previous page. Omit or pass `null` for the first page. The token carries this lookup's parameters. */
89
89
  cursor?: string | null;
90
90
  }
91
91
  /**
92
- * One page of records, returned by {@linkcode EntityHandler.list | list()} and
93
- * {@linkcode EntityHandler.filter | filter()} when called with an options object.
92
+ * One page of items, with a cursor to continue.
94
93
  *
95
- * @typeParam T - Record type of the items.
94
+ * @typeParam T - Type of the items.
96
95
  */
97
96
  export interface EntityPage<T> {
98
- /** The page's records in the requested sort order, or the distinct values in ascending order. */
97
+ /** The page's items. Without `distinct`, these are records in the requested sort order. With `distinct`, these are that field's distinct values instead of records, in ascending order. */
99
98
  items: T[];
100
- /** Pass as `cursor` to get the next page. `null` on the last page. */
99
+ /** A cursor for the next page, or `null` on the last page. Pass it as `cursor` to keep paging. */
101
100
  next_cursor: string | null;
102
101
  /** Whether records remain after this page. */
103
102
  has_more: boolean;
104
103
  }
105
104
  /**
106
- * Time unit for {@linkcode EntityAggregateSpec.dateBucket | dateBucket}.
105
+ * A time bucket to group a date field by, for {@linkcode EntityAggregateSpec.dateBucket | dateBucket}.
106
+ *
107
+ * @typeParam T - Entity record type.
107
108
  */
108
- export type EntityDateBucketUnit = "day" | "week" | "month" | "year";
109
+ export interface EntityDateBucket<T> {
110
+ /** The date field to bucket by: `created_date`, `updated_date`, or a date field of your schema. */
111
+ field: keyof T & string;
112
+ /** Every date field supports `day`, `month` and `year`. The `created_date` and `updated_date` fields also support `week`. */
113
+ unit: "day" | "week" | "month" | "year";
114
+ }
109
115
  /**
110
116
  * Describes what {@linkcode EntityHandler.aggregate | aggregate()} computes.
111
117
  *
112
- * Name the fields to group by and the measures to compute; the server does the work and
118
+ * Name the fields to group by and the measures to compute, and the server does the work and
113
119
  * returns one row per group. Field names are the entity's own field names.
114
120
  *
115
121
  * @typeParam T - Entity record type.
116
122
  */
117
123
  export interface EntityAggregateSpec<T> {
118
- /** Filter applied before grouping, in the same form {@linkcode EntityHandler.filter | filter()} accepts. Defaults to all records. */
124
+ /** Filter applied before grouping, matching {@linkcode EntityFilterQuery}. Defaults to all records. */
119
125
  query?: EntityFilterQuery<T>;
120
126
  /** Field, or up to four fields, to group by. Omit to get one total row. */
121
127
  groupBy?: (keyof T & string) | (keyof T & string)[];
122
- /** Group by a time bucket of a date field. `created_date` and `updated_date` support every unit; date fields of your schema support `day`, `month` and `year`. */
123
- dateBucket?: {
124
- field: keyof T & string;
125
- unit: EntityDateBucketUnit;
126
- };
128
+ /** Group by a time bucket of a date field. */
129
+ dateBucket?: EntityDateBucket<T>;
127
130
  /** Whether to include the number of records per group as `count`. Defaults to `true`. */
128
131
  count?: boolean;
129
132
  /** Field, or fields, to sum. Each appears in the rows as `sum_<field>`. */
@@ -134,9 +137,9 @@ export interface EntityAggregateSpec<T> {
134
137
  min?: (keyof T & string) | (keyof T & string)[];
135
138
  /** Field, or fields, to take the maximum of. Each appears in the rows as `max_<field>`. */
136
139
  max?: (keyof T & string) | (keyof T & string)[];
137
- /** Field whose distinct values to count per group, returned as `count_distinct_<field>`. */
140
+ /** Field whose distinct values to count per group, returned as `count_distinct_<field>`. Unlike `distinct` on `list()`/`filter()`, an array field here counts each whole array as one value, not its individual elements. */
138
141
  countDistinct?: keyof T & string;
139
- /** Filter on the computed fields, applied after grouping. For example `{ count: { $gt: 1 } }` keeps only duplicated groups. */
142
+ /** Filter applied to the computed fields after grouping, not to raw or `groupBy` fields. Supports `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`, and `$nin`. With more than one key, a row is kept only when every key's condition holds. For example, grouped by `external_id`, `having: { count: { $gt: 1 } }` keeps only the external ids that appear in more than one record. */
140
143
  having?: Record<string, any>;
141
144
  /** Computed or group field to sort the rows by, with a `-` prefix for descending. For example `'-count'`. */
142
145
  sort?: string;
@@ -147,9 +150,9 @@ export interface EntityAggregateSpec<T> {
147
150
  * Rows returned by {@linkcode EntityHandler.aggregate | aggregate()}.
148
151
  */
149
152
  export interface EntityAggregateResult {
150
- /** One row per group: the group fields by name, then `count`, `sum_<field>`, `avg_<field>`, `min_<field>`, `max_<field>` or `count_distinct_<field>`. */
153
+ /** One row per group. Each row has the group fields by name, then `count`, `sum_<field>`, `avg_<field>`, `min_<field>`, `max_<field>` or `count_distinct_<field>`. */
151
154
  rows: Record<string, any>[];
152
- /** `true` when more groups exist than `limit` allowed. */
155
+ /** Set to `true` when more groups exist than `limit` allowed. */
153
156
  truncated: boolean;
154
157
  }
155
158
  /**
@@ -158,7 +161,7 @@ export interface EntityAggregateResult {
158
161
  * @typeParam T - Entity record type.
159
162
  */
160
163
  export interface EntityUpsertOptions<T> {
161
- /** Field, or fields, that identify a record. A record whose key values match an existing record updates it; any other record is created. */
164
+ /** Field, or fields, that identify a record. A record whose key values match an existing record updates it, and any other record is created. */
162
165
  key: (keyof T & string) | (keyof T & string)[];
163
166
  }
164
167
  /**
@@ -208,19 +211,6 @@ export interface ImportResult<T = any> {
208
211
  * ```
209
212
  */
210
213
  export type SortField<T> = (keyof T & string) | `+${keyof T & string}` | `-${keyof T & string}`;
211
- /**
212
- * Entity filter query type system.
213
- *
214
- * `EntityFilterQuery<T>` keeps field names tied to the entity schema while
215
- * allowing Base44's documented filtering syntax. Each field can use an exact
216
- * value, `null`, an array shorthand for matching any listed value, or a
217
- * field-level operator object. Root-level `$and`, `$or`, and `$nor` combine
218
- * nested filter queries.
219
- *
220
- * Operator values are typed from the field they filter where possible. For
221
- * example, numeric fields accept numeric comparison values, string fields
222
- * accept `$regex`, and array fields accept `$all` and `$size`.
223
- */
224
214
  /**
225
215
  * Value accepted when filtering an entity field.
226
216
  *
@@ -262,12 +252,30 @@ type EntityFilterArrayOperators<T> = [
262
252
  $size?: number;
263
253
  };
264
254
  /**
265
- * Query object accepted by entity filtering methods.
255
+ * Query object accepted by {@linkcode EntityHandler.filter | filter()},
256
+ * {@linkcode EntityHandler.count | count()}, {@linkcode EntityHandler.deleteMany | deleteMany()},
257
+ * {@linkcode EntityHandler.updateMany | updateMany()}, and the `query` field of
258
+ * `EntityAggregateSpec`.
259
+ *
260
+ * Field keys are typed from the entity schema. Each field can use an exact value, `null`,
261
+ * an array shorthand for matching any of the listed values, or a field-level operator
262
+ * object. Root-level `$and`, `$or`, and `$nor` combine nested filter queries.
266
263
  *
267
- * Field keys are typed from the entity schema. `$and`, `$or`, and `$nor`
268
- * combine nested filter queries at the root level.
264
+ * Operator values are typed from the field they filter where possible. For example,
265
+ * numeric fields accept numeric comparison values, string fields accept `$regex`, and
266
+ * array fields accept `$all` and `$size`.
269
267
  *
270
268
  * @typeParam T - Entity record type.
269
+ *
270
+ * @example
271
+ * ```typescript
272
+ * // Exact match, a comparison operator, and a nested $or
273
+ * const query: EntityFilterQuery<Task> = {
274
+ * status: 'open',
275
+ * priority: { $gte: 3 },
276
+ * $or: [{ assignee: 'me' }, { team: 'core' }]
277
+ * };
278
+ * ```
271
279
  */
272
280
  export type EntityFilterQuery<T> = {
273
281
  [K in keyof T]?: EntityFilterValue<T[K]>;
@@ -330,23 +338,81 @@ export type EntityRecord = {
330
338
  */
331
339
  export interface EntityHandler<T = any> {
332
340
  /**
333
- * Lists records with optional pagination and sorting.
341
+ * Lists one cursor page of records, sorted and optionally field-selected.
342
+ *
343
+ * Pass `cursor` from the previous page's `next_cursor` to keep paging, or omit it
344
+ * for the first page. Records added or deleted between pages never shift the boundary.
345
+ *
346
+ * @typeParam K - The fields to include in each record. Defaults to all fields.
347
+ * @param options - Paging options.
348
+ * @returns Promise resolving to a page of records.
349
+ *
350
+ * @example
351
+ * ```typescript
352
+ * // Get one page of records
353
+ * const page = await base44.entities.MyEntity.list({ limit: 10 });
354
+ * console.log(page.items);
355
+ * ```
356
+ *
357
+ * @example
358
+ * ```typescript
359
+ * // Sort records
360
+ * const page = await base44.entities.MyEntity.list({ sort: '-priority', limit: 10 });
361
+ * ```
362
+ *
363
+ * @example
364
+ * ```typescript
365
+ * // Only return specific fields
366
+ * const page = await base44.entities.MyEntity.list({ fields: ['name', 'status'], limit: 10 });
367
+ * ```
368
+ *
369
+ * @example
370
+ * ```typescript
371
+ * // Walk every record with a cursor
372
+ * const allItems = [];
373
+ * let page = await base44.entities.MyEntity.list({ sort: '-created_date', limit: 1000 });
374
+ * allItems.push(...page.items);
375
+ * while (page.has_more) {
376
+ * page = await base44.entities.MyEntity.list({ cursor: page.next_cursor, limit: 1000 });
377
+ * allItems.push(...page.items);
378
+ * }
379
+ * ```
380
+ */
381
+ list<K extends keyof T = keyof T>(options: EntityListOptions<T, K>): Promise<EntityPage<Pick<T, K>>>;
382
+ /**
383
+ * Lists one cursor page of a single field's distinct values, instead of records.
334
384
  *
335
- * Retrieves all records of this type with support for sorting,
336
- * pagination, and field selection.
385
+ * @typeParam K - The field whose distinct values to read.
386
+ * @param options - Paging options naming the field to read.
387
+ * @returns Promise resolving to a page of distinct values.
337
388
  *
338
- * **Note:** The maximum limit is 5,000 items per request. To read more than
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.
389
+ * @example
390
+ * ```typescript
391
+ * // Regular field
392
+ * const { items: categories } = await base44.entities.Product.list({ distinct: 'category' });
393
+ * // categories: ['books', 'electronics', 'toys']
394
+ * ```
395
+ *
396
+ * @example
397
+ * ```typescript
398
+ * // Array field
399
+ * // Each tag counts once, not each array
400
+ * const { items: tags } = await base44.entities.Product.list({ distinct: 'tags' });
401
+ * // tags: ['bestseller', 'clearance', 'new']
402
+ * ```
403
+ */
404
+ list<K extends keyof T>(options: EntityDistinctOptions<T, K>): Promise<EntityPage<T[K]>>;
405
+ /**
406
+ * Lists records as an array, using `skip` for pagination.
407
+ *
408
+ * Kept for existing code. Prefer a cursor for pagination instead.
343
409
  *
344
410
  * @typeParam K - The fields to include in the response. Defaults to all fields.
345
- * @param sort - Sort parameter, such as `'-created_date'` for descending. Defaults to `'-created_date'`.
411
+ * @param sort - Sort parameter, such as `'-priority'` for descending. Defaults to `'-created_date'`.
346
412
  * @param limit - Maximum number of results to return. Defaults to `5000`.
347
- * @param skip - Number of results to skip for pagination. Defaults to `0`. Deprecated for loops; use a cursor.
413
+ * @param skip - Number of results to skip for pagination. Defaults to `0`. Prefer a cursor for loops instead.
348
414
  * @param fields - Array of field names to include in the response. Defaults to all 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.
415
+ * @returns Promise resolving to an array of records with selected fields.
350
416
  *
351
417
  * @example
352
418
  * ```typescript
@@ -372,51 +438,101 @@ export interface EntityHandler<T = any> {
372
438
  * // Get only specific fields
373
439
  * const fields = await base44.entities.MyEntity.list('-created_date', 10, 0, ['name', 'status']);
374
440
  * ```
441
+ */
442
+ list<K extends keyof T = keyof T>(sort?: SortField<T>, limit?: number, skip?: number, fields?: K[]): Promise<Pick<T, K>[]>;
443
+ /**
444
+ * Filters and returns one cursor page of matching records, sorted and
445
+ * optionally field-selected.
446
+ *
447
+ * Pass `cursor` from the previous page's `next_cursor` to keep paging, or omit it
448
+ * for the first page. Records added or deleted between pages never shift the boundary.
449
+ *
450
+ * @typeParam K - The fields to include in each record. Defaults to all fields.
451
+ * @param query - Query matching {@linkcode EntityFilterQuery}. Field names are
452
+ * case-sensitive, and records matching every field are returned.
453
+ * @param options - Paging options.
454
+ * @returns Promise resolving to a page of matching records.
375
455
  *
376
456
  * @example
377
457
  * ```typescript
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);
458
+ * // Get one page of matching records
459
+ * const page = await base44.entities.Order.filter({ status: 'open' }, { limit: 10 });
460
+ * console.log(page.items);
461
+ * ```
462
+ *
463
+ * @example
464
+ * ```typescript
465
+ * // Sort matching records
466
+ * const page = await base44.entities.Order.filter({ status: 'open' }, { sort: '-priority', limit: 10 });
467
+ * ```
468
+ *
469
+ * @example
470
+ * ```typescript
471
+ * // Only return specific fields
472
+ * const page = await base44.entities.Order.filter({ status: 'open' }, { fields: ['status', 'region'], limit: 10 });
473
+ * ```
474
+ *
475
+ * @example
476
+ * ```typescript
477
+ * // Walk all matching records with a cursor
478
+ * const allOrders = [];
479
+ * let page = await base44.entities.Order.filter(
480
+ * { status: 'open' },
481
+ * { sort: '-created_date', limit: 1000 }
482
+ * );
483
+ * allOrders.push(...page.items);
382
484
  * while (page.has_more) {
383
- * page = await base44.entities.MyEntity.list({ cursor: page.next_cursor, limit: 1000 });
384
- * await exportRows(page.items);
485
+ * page = await base44.entities.Order.filter({ status: 'open' }, { cursor: page.next_cursor, limit: 1000 });
486
+ * allOrders.push(...page.items);
385
487
  * }
386
488
  * ```
489
+ */
490
+ filter<K extends keyof T = keyof T>(query: EntityFilterQuery<T>, options: EntityListOptions<T, K>): Promise<EntityPage<Pick<T, K>>>;
491
+ /**
492
+ * Filters and returns one cursor page of a single field's distinct values
493
+ * among matching records.
494
+ *
495
+ * @typeParam K - The field whose distinct values to read.
496
+ * @param query - Query matching {@linkcode EntityFilterQuery}. Field names are
497
+ * case-sensitive, and records matching every field are returned.
498
+ * @param options - Paging options naming the field to read.
499
+ * @returns Promise resolving to a page of distinct values.
387
500
  *
388
501
  * @example
389
502
  * ```typescript
390
- * // Distinct values of one field, instead of records
391
- * const { items: categories } = await base44.entities.Product.list({ distinct: 'category' });
503
+ * // Regular field
504
+ * const { items: regions } = await base44.entities.Order.filter(
505
+ * { status: 'open' },
506
+ * { distinct: 'region' }
507
+ * );
508
+ * // regions: ['east', 'north', 'west']
509
+ * ```
510
+ *
511
+ * @example
512
+ * ```typescript
513
+ * // Array field
514
+ * // Each tag counts once, not each array
515
+ * const { items: tags } = await base44.entities.Order.filter(
516
+ * { status: 'open' },
517
+ * { distinct: 'tags' }
518
+ * );
519
+ * // tags: ['gift', 'international', 'rush']
392
520
  * ```
393
521
  */
394
- list<K extends keyof T = keyof T>(sort?: SortField<T>, limit?: number, skip?: number, fields?: K[]): Promise<Pick<T, K>[]>;
395
- list<K extends keyof T>(options: EntityDistinctOptions<T, K>): Promise<EntityPage<T[K]>>;
396
- list<K extends keyof T = keyof T>(options: EntityListOptions<T, K>): Promise<EntityPage<Pick<T, K>>>;
522
+ filter<K extends keyof T>(query: EntityFilterQuery<T>, options: EntityDistinctOptions<T, K>): Promise<EntityPage<T[K]>>;
397
523
  /**
398
- * Filters records based on a query.
399
- *
400
- * Retrieves records that match specific criteria with support for
401
- * sorting, pagination, and field selection.
524
+ * Filters records as an array, using `skip` for pagination.
402
525
  *
403
- * **Note:** The maximum limit is 5,000 items per request. To read more than
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.
526
+ * Kept for existing code. Prefer a cursor for pagination instead.
408
527
  *
409
528
  * @typeParam K - The fields to include in the response. Defaults to all fields.
410
- * @param query - Query object with field-value pairs. Each key should be a field name
411
- * from your entity schema, and each value is the criteria to match. Records matching all
412
- * specified criteria are returned. Field names are case-sensitive. Use field-value pairs
413
- * for exact matches, `null` for null values, arrays as shorthand for matching any of the
414
- * provided values, or documented MongoDB query operators for advanced filtering.
415
- * @param sort - Sort parameter, such as `'-created_date'` for descending. Defaults to `'-created_date'`.
529
+ * @param query - Query matching {@linkcode EntityFilterQuery}. Field names are
530
+ * case-sensitive, and records matching every field are returned.
531
+ * @param sort - Sort parameter, such as `'-priority'` for descending. Defaults to `'-created_date'`.
416
532
  * @param limit - Maximum number of results to return. Defaults to `5000`.
417
- * @param skip - Number of results to skip for pagination. Defaults to `0`. Deprecated for loops; use a cursor.
533
+ * @param skip - Number of results to skip for pagination. Defaults to `0`. Prefer a cursor for loops instead.
418
534
  * @param fields - Array of field names to include in the response. Defaults to all 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.
535
+ * @returns Promise resolving to an array of filtered records with selected fields.
420
536
  *
421
537
  * @example
422
538
  * ```typescript
@@ -493,34 +609,8 @@ export interface EntityHandler<T = any> {
493
609
  * ['name', 'priority']
494
610
  * );
495
611
  * ```
496
- *
497
- * @example
498
- * ```typescript
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
- * );
505
- * await exportRows(page.items);
506
- * while (page.has_more) {
507
- * page = await base44.entities.Order.filter({ status: 'open' }, { cursor: page.next_cursor, limit: 1000 });
508
- * await exportRows(page.items);
509
- * }
510
- * ```
511
- *
512
- * @example
513
- * ```typescript
514
- * // Distinct values of one field among the matching records
515
- * const { items: agents } = await base44.entities.Order.filter(
516
- * { status: 'open' },
517
- * { distinct: 'agent_id' }
518
- * );
519
- * ```
520
612
  */
521
613
  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>>>;
524
614
  /**
525
615
  * Gets a single record by ID.
526
616
  *
@@ -612,9 +702,7 @@ export interface EntityHandler<T = any> {
612
702
  *
613
703
  * Permanently removes all records that match the provided query.
614
704
  *
615
- * @param query - Query object with field-value pairs. Each key should be a field name
616
- * from your entity schema, and each value is the criteria to match. Records matching all
617
- * specified criteria will be deleted. Field names are case-sensitive.
705
+ * @param query - Query matching {@linkcode EntityFilterQuery}. Every matching record is deleted.
618
706
  * @returns Promise resolving to the deletion result.
619
707
  *
620
708
  * @example
@@ -664,12 +752,7 @@ export interface EntityHandler<T = any> {
664
752
  * To update a single record by ID, use {@linkcode update | update()} instead. To update
665
753
  * multiple specific records with different data each, use {@linkcode bulkUpdate | bulkUpdate()}.
666
754
  *
667
- * @param query - Query object to filter which records to update. Use field-value
668
- * pairs for exact matches, or
669
- * [MongoDB query operators](https://www.mongodb.com/docs/manual/reference/operator/query/)
670
- * for advanced filtering. Supported query operators include `$eq`, `$ne`, `$gt`,
671
- * `$gte`, `$lt`, `$lte`, `$in`, `$nin`, `$and`, `$or`, `$not`, `$nor`,
672
- * `$exists`, `$regex`, `$all`, `$elemMatch`, and `$size`.
755
+ * @param query - Query matching {@linkcode EntityFilterQuery}, selecting which records to update.
673
756
  * @param data - Update operation object containing one or more
674
757
  * [MongoDB update operators](https://www.mongodb.com/docs/manual/reference/operator/update/).
675
758
  * Each field may only appear in one operator per call.
@@ -731,23 +814,30 @@ export interface EntityHandler<T = any> {
731
814
  * Counts the records that match a query.
732
815
  *
733
816
  * Returns the number of records the current user can read, without fetching them.
734
- * Use it for totals, badges and "page N of M" instead of listing records and
735
- * measuring the array.
817
+ * Use it for totals, badges and "page N of M".
736
818
  *
737
- * @param query - Filter query, in the same form {@linkcode filter | filter()} accepts. Defaults to all records.
819
+ * @param query - Query matching {@linkcode EntityFilterQuery}. Defaults to all records.
738
820
  * @returns Promise resolving to the number of matching records.
739
821
  *
740
822
  * @example
741
823
  * ```typescript
742
- * // How many tasks are still open?
824
+ * // Count matching records
743
825
  * const open = await base44.entities.Task.count({ status: 'open' });
744
826
  * ```
745
827
  *
746
828
  * @example
747
829
  * ```typescript
748
- * // Total records in the entity
830
+ * // Count all records
749
831
  * const total = await base44.entities.Task.count();
750
832
  * ```
833
+ *
834
+ * @example
835
+ * ```typescript
836
+ * // Page N of M
837
+ * const pageSize = 20;
838
+ * const total = await base44.entities.Task.count({ status: 'open' });
839
+ * const totalPages = Math.ceil(total / pageSize);
840
+ * ```
751
841
  */
752
842
  count(query?: EntityFilterQuery<T>): Promise<number>;
753
843
  /**
@@ -757,7 +847,7 @@ export interface EntityHandler<T = any> {
757
847
  * and adding up in the browser. The server groups the records you can read and
758
848
  * returns one row per group, up to 1,000 rows.
759
849
  *
760
- * @param spec - What to group by and what to compute. See {@linkcode EntityAggregateSpec | EntityAggregateSpec}.
850
+ * @param spec - What to group by and what to compute.
761
851
  * @returns Promise resolving to the rows and a `truncated` flag.
762
852
  *
763
853
  * @example
@@ -769,58 +859,78 @@ export interface EntityHandler<T = any> {
769
859
  * sum: 'amount',
770
860
  * sort: '-sum_amount'
771
861
  * });
772
- * // rows: [{ agent_id: 'a1', count: 42, sum_amount: 18250 }, ...]
862
+ * // rows: [
863
+ * // { agent_id: 'a1', count: 42, sum_amount: 18250 },
864
+ * // { agent_id: 'a2', count: 37, sum_amount: 15400 },
865
+ * // ...
866
+ * // ]
773
867
  * ```
774
868
  *
775
869
  * @example
776
870
  * ```typescript
777
- * // Records created per day
778
- * const { rows } = await base44.entities.Visit.aggregate({
871
+ * // Tasks created per day
872
+ * const { rows } = await base44.entities.Task.aggregate({
779
873
  * dateBucket: { field: 'created_date', unit: 'day' }
780
874
  * });
875
+ * // rows: [
876
+ * // { created_date: '2026-09-01', count: 8 },
877
+ * // { created_date: '2026-09-02', count: 11 },
878
+ * // ...
879
+ * // ]
781
880
  * ```
782
881
  *
783
882
  * @example
784
883
  * ```typescript
785
884
  * // Find duplicated external ids
786
- * const { rows } = await base44.entities.Contact.aggregate({
885
+ * const { rows } = await base44.entities.Order.aggregate({
787
886
  * groupBy: 'external_id',
788
887
  * having: { count: { $gt: 1 } }
789
888
  * });
889
+ * // rows: [
890
+ * // { external_id: 'ext-42', count: 3 },
891
+ * // { external_id: 'ext-77', count: 2 }
892
+ * // ]
790
893
  * ```
791
894
  *
792
895
  * @example
793
896
  * ```typescript
794
- * // Unique visitors per page
795
- * const { rows } = await base44.entities.PageView.aggregate({
796
- * groupBy: 'path',
797
- * countDistinct: 'session_id'
897
+ * // Unique customers per product
898
+ * const { rows } = await base44.entities.Order.aggregate({
899
+ * groupBy: 'product_id',
900
+ * countDistinct: 'customer_id'
798
901
  * });
902
+ * // rows: [
903
+ * // { product_id: 'p1', count_distinct_customer_id: 86 },
904
+ * // { product_id: 'p2', count_distinct_customer_id: 34 }
905
+ * // ]
799
906
  * ```
800
907
  */
801
908
  aggregate(spec: EntityAggregateSpec<T>): Promise<EntityAggregateResult>;
802
909
  /**
803
- * Creates or updates records by a key of your own.
910
+ * Creates or updates records by a key you define, instead of by `id`.
804
911
  *
805
- * Use this when you sync data from another system: name the field, or fields, that
806
- * identify a record, and the server updates the records whose key already exists and
807
- * creates the rest, in one call. You no longer need to list existing records to check
808
- * for duplicates before writing.
912
+ * Use this whenever records have a natural key, such as an ID from another system or a
913
+ * one-per-user record keyed by `user_id`, so you don't have to look up each record first
914
+ * to decide between create and update. Name the field, or fields, that identify a record,
915
+ * and the server updates the records whose key already exists and creates the rest, in
916
+ * one call.
809
917
  *
810
918
  * You can upsert up to 500 records per request. When two records in one call share a
811
919
  * key, the last one wins. Updates merge the given fields into the existing record, like
812
920
  * {@linkcode update | update()}.
813
921
  *
814
- * @param records - Array of record data objects. Each must carry a value for every key field.
815
- * @param options - The key field or fields. See {@linkcode EntityUpsertOptions | EntityUpsertOptions}.
922
+ * @param records - Array of record data objects. Each must carry a value, not an object or
923
+ * array, for every field named in `options.key`, to find or create by. Other fields are
924
+ * optional, and only the ones you include are merged into a matched record, like `update()`.
925
+ * @param options - The key field or fields.
816
926
  * @returns Promise resolving to the counts and the written records.
817
927
  *
818
928
  * @example
819
929
  * ```typescript
820
- * // Sync contacts from a CRM by their CRM id
821
- * const result = await base44.entities.Contact.upsert(
822
- * crmContacts.map(c => ({ crm_id: c.id, name: c.name, email: c.email })),
823
- * { key: 'crm_id' }
930
+ * // Sync by sku
931
+ * const result = await base44.entities.Product.upsert(
932
+ * supplierCatalog.map(p => ({ sku: p.sku, name: p.name, price: p.price })),
933
+ * { key: 'sku' }
824
934
  * );
825
935
  * console.log(`${result.created} new, ${result.updated} updated`);
826
936
  * ```
@@ -828,7 +938,11 @@ export interface EntityHandler<T = any> {
828
938
  * @example
829
939
  * ```typescript
830
940
  * // Compound key
831
- * await base44.entities.Inventory.upsert(rows, { key: ['sku', 'warehouse'] });
941
+ * const result = await base44.entities.Inventory.upsert(
942
+ * warehouseFeed.map(row => ({ sku: row.sku, warehouse: row.warehouseCode, quantity: row.qty })),
943
+ * { key: ['sku', 'warehouse'] }
944
+ * );
945
+ * console.log(`${result.created} new, ${result.updated} updated`);
832
946
  * ```
833
947
  */
834
948
  upsert(records: Partial<T>[], options: EntityUpsertOptions<T>): Promise<EntityUpsertResult<T>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@base44-preview/sdk",
3
- "version": "0.8.53-pr.289.39e1f1f",
3
+ "version": "0.8.53-pr.289.545edf7",
4
4
  "description": "JavaScript SDK for Base44 API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",