@base44-preview/sdk 0.8.53-pr.289.39e1f1f → 0.8.53-pr.308.2df7905

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/actor.d.ts CHANGED
@@ -25,8 +25,8 @@ export interface Storage {
25
25
  get<T>(key: string): Promise<T | undefined>;
26
26
  put(key: string, value: unknown): Promise<void>;
27
27
  delete(key: string): Promise<boolean>;
28
- /** Deletes all persisted storage for the session. A later connection starts
29
- * with empty storage, the same as a new session. */
28
+ /** Wipe the room's entire persisted storage (match-end cleanup). Safe: a
29
+ * later rejoin re-bootstraps exactly like a brand-new room. */
30
30
  deleteAll(): Promise<void>;
31
31
  }
32
32
  /**
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,22 +1,15 @@
1
1
  /**
2
- * Maps actor names to their incoming and outgoing message types.
2
+ * Extend this interface to add typed `subscribe` callbacks and `send` payloads
3
+ * for your deployed Actors.
3
4
  *
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.
8
- *
9
- * To generate types from deployed actors instead, use the
10
- * [`types generate`](/developers/references/cli/commands/types-generate) CLI command.
11
- * To learn how incoming and outgoing messages work, see
12
- * [message types](/developers/backend/resources/actors/overview#message-types).
5
+ * This is separate from {@link ActorNameRegistry} (which is auto-generated
6
+ * by `base44 types generate`), so there are no conflicts.
13
7
  *
14
8
  * @example
15
9
  * ```typescript
16
- * // Type messages for an actor
17
10
  * declare module "@base44/sdk" {
18
11
  * interface ActorRegistry {
19
- * chatRoom: {
12
+ * ChatRoom: {
20
13
  * toClient: { type: "joined" | "left" | "message"; userId?: string; from?: string; text?: string };
21
14
  * toServer: { type: "message"; text: string };
22
15
  * };
@@ -27,11 +20,8 @@
27
20
  export interface ActorRegistry {
28
21
  }
29
22
  /**
30
- * Lists actor names when your project includes types generated by the CLI
31
- * with [`types generate`](/developers/references/cli/commands/types-generate).
32
- *
33
- * The generated names provide autocomplete for deployed actors. To define
34
- * incoming and outgoing message types manually, augment [ActorRegistry](#actorregistry).
23
+ * Auto-populated by `base44 types generate` with the names of your deployed actors.
24
+ * Do not edit this interface manually — use {@link ActorRegistry} for message types.
35
25
  */
36
26
  export interface ActorNameRegistry {
37
27
  }
@@ -42,173 +32,71 @@ type ToClientFor<N extends string> = N extends keyof ActorRegistry ? ActorRegist
42
32
  type ToServerFor<N extends string> = N extends keyof ActorRegistry ? ActorRegistry[N] extends {
43
33
  toServer: infer O;
44
34
  } ? O : unknown : unknown;
45
- /**
46
- * Configures the connection that [ActorRef.connect](#connect) opens.
47
- */
35
+ /** Options for {@link ActorRef.connect}. */
48
36
  export interface ActorConnectOptions {
49
37
  /**
50
- * Connection ID that the actor receives as `conn.id`.
51
- *
52
- * To let the actor recognize the same client if it reconnects, use a stable
53
- * 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).
38
+ * The connection id — becomes the actor's `conn.id`. Supply a stable value
39
+ * (e.g. persisted per tab) so a reconnect reuses the same server-side
40
+ * identity; omit for an auto-generated per-connection id.
58
41
  */
59
42
  id?: string;
60
43
  }
61
- /**
62
- * Represents a listener for messages from the actor, registered with
63
- * [Connection.subscribe](#subscribe).
64
- */
44
+ /** Handle for one listener registered via {@link Connection.subscribe}. */
65
45
  export interface ActorSubscription {
66
- /**
67
- * Removes this listener. Other listeners and the socket stay open.
68
- *
69
- * @example
70
- * ```typescript
71
- * // Remove a listener
72
- * sub.unsubscribe();
73
- * ```
74
- */
46
+ /** Remove this listener; other listeners and the socket stay live. */
75
47
  unsubscribe(): void;
76
48
  }
77
49
  /**
78
- * Represents a client's WebSocket connection to an actor session.
79
- *
80
- * [ActorRef.connect](#connect) returns this object. The socket buffers messages
81
- * you send before it opens.
50
+ * A live connection to an actor instance, returned by {@link ActorRef.connect}.
51
+ * `subscribe`/`send` are always valid — you only get a `Connection` once the
52
+ * socket has been opened, so there's no pre-connect state to guard against.
82
53
  */
83
54
  export interface Connection<N extends string = string> {
84
- /** Connection ID that the actor receives as `conn.id`. */
55
+ /** The connection id (the value the actor sees as `conn.id`). */
85
56
  readonly id: string;
86
- /**
87
- * Registers a listener for messages from the actor.
88
- *
89
- * You can register multiple listeners on the same connection.
90
- *
91
- * @param callback - Callback that runs for each message the actor sends to this connection.
92
- * @returns A subscription handle. Call `unsubscribe()` on it to remove this listener without closing the socket.
93
- *
94
- * @example
95
- * ```typescript
96
- * // Listen for messages from the actor
97
- * const sub = conn.subscribe((msg) => {
98
- * console.log(msg);
99
- * });
100
- *
101
- * // Stop listening without closing the socket.
102
- * sub.unsubscribe();
103
- * ```
104
- */
57
+ /** Register a message listener. Multiple are allowed; returns a per-listener unsubscribe. */
105
58
  subscribe(callback: (data: ToClientFor<N>) => void): ActorSubscription;
106
- /**
107
- * Sends a message to the actor.
108
- *
109
- * The socket buffers messages until it opens. After you call [close](#close),
110
- * the socket drops further sends.
111
- *
112
- * @param data - Message to send to the actor. The type comes from [ActorRegistry](#actorregistry) when you register the actor there.
113
- *
114
- * @example
115
- * ```typescript
116
- * // Send a message to the actor
117
- * conn.send({ type: "message", text: "Hello" });
118
- * ```
119
- */
59
+ /** Send a message. Buffered by the socket until it's open; dropped after
60
+ * {@link close}. */
120
61
  send(data: ToServerFor<N>): void;
121
62
  /**
122
- * Closes the connection and removes all listeners.
123
- *
124
- * 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.
127
- *
128
- * @example
129
- * ```typescript
130
- * // Close the connection
131
- * conn.close();
132
- * ```
63
+ * Tear down the socket, heartbeat, and all listeners. Safe to call more
64
+ * than once. A connection also closes itself when it fails permanently —
65
+ * see {@link ActorRef.connect}.
133
66
  */
134
67
  close(): void;
135
68
  }
136
69
  /**
137
- * Represents a reference to an actor session, identified by actor name and session ID.
138
- *
139
- * Call [connect](#connect) to open the WebSocket and get a [Connection](#connection).
70
+ * A handle to one actor instance — `base44.actors.MyActor(id)`. Call
71
+ * {@link connect} to open the socket and get a {@link Connection}.
140
72
  */
141
73
  export interface ActorRef<N extends string = string> {
142
74
  /**
143
- * Creates or returns the [Connection](#connection) for this session.
144
- *
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.
149
- *
150
- * For a sample flow, see
151
- * [connect a client to a session](/developers/backend/resources/actors/sample-flows#connect-a-client-to-a-session).
75
+ * Open the WebSocket and return the {@link Connection}. Idempotent while the
76
+ * connection is open.
152
77
  *
153
- * @param options - Optional connection settings, such as a stable connection ID.
154
- * @returns The [Connection](#connection) for this actor session.
155
- *
156
- * @example
157
- * ```typescript
158
- * // Connect to a session
159
- * const conn = base44.actors.chatRoom("session-1").connect({ id: "tab-1" });
160
- * ```
78
+ * A connection that fails permanently (for example, the actor doesn't exist
79
+ * or the caller isn't allowed to connect) closes itself and reports the
80
+ * error to the client's `onError` handler. Call `connect()` again after
81
+ * fixing the cause to get a fresh {@link Connection}, and re-subscribe.
161
82
  */
162
83
  connect(options?: ActorConnectOptions): Connection<N>;
163
84
  }
164
85
  /**
165
- * Selects a session for a named actor.
166
- *
167
- * TypeScript infers message types when you register the actor in
168
- * [ActorRegistry](#actorregistry). [ActorNameRegistry](#actornameregistry)
169
- * provides autocomplete for actor names only.
86
+ * Client for a single named Actor — call it with an instance id to get an
87
+ * {@link ActorRef}. Typed automatically when the actor is registered in
88
+ * {@link ActorRegistry}.
170
89
  */
171
90
  export interface ActorClient<N extends string = string> {
172
- /**
173
- * Gets a reference to an actor session.
174
- *
175
- * Clients that specify the same actor name and session ID join the same session.
176
- *
177
- * @param instanceId - Session ID that identifies which session to connect to.
178
- * @returns A reference to the actor session.
179
- *
180
- * @example
181
- * ```typescript
182
- * // Select a session
183
- * const session = base44.actors.chatRoom("session-1");
184
- * ```
185
- */
186
91
  (instanceId: string): ActorRef<N>;
187
92
  }
188
93
  /**
189
- * Connects your frontend to [actor sessions](/developers/backend/resources/actors/overview),
190
- * shared live backend processes where clients can exchange messages in realtime.
94
+ * The actors module provides access to Cloudflare Durable Object-backed
95
+ * Actors deployed by the Base44 platform.
191
96
  *
192
- * The following table lists what you can do with the actors module:
193
- *
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. |
200
- *
201
- * ## Authentication modes
202
- *
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).
206
- *
207
- * @example
208
97
  * ```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));
98
+ * const conn = base44.actors.MyActor("room-1").connect();
99
+ * const sub = conn.subscribe((msg) => console.log(msg)); // typed via ActorRegistry
212
100
  * conn.send({ type: "message", text: "hi" });
213
101
  * sub.unsubscribe();
214
102
  * conn.close();
@@ -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.308.2df7905",
4
4
  "description": "JavaScript SDK for Base44 API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",