@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 +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/modules/actors.types.d.ts +38 -150
- package/dist/modules/entities.types.d.ts +255 -141
- package/package.json +1 -1
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
|
-
/**
|
|
29
|
-
*
|
|
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,
|
|
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
|
-
*
|
|
2
|
+
* Extend this interface to add typed `subscribe` callbacks and `send` payloads
|
|
3
|
+
* for your deployed Actors.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
31
|
-
*
|
|
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
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
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
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
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
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
* @
|
|
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
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
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
|
-
*
|
|
190
|
-
*
|
|
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
|
-
*
|
|
210
|
-
* const
|
|
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 `'-
|
|
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
|
|
69
|
-
* `cursor` and `limit`. Passing
|
|
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.
|
|
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
|
|
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
|
|
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 -
|
|
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
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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,
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
*
|
|
268
|
-
*
|
|
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
|
|
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
|
-
*
|
|
336
|
-
*
|
|
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
|
-
*
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
*
|
|
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 `'-
|
|
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`.
|
|
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.
|
|
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
|
-
* //
|
|
379
|
-
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
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.
|
|
384
|
-
*
|
|
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
|
-
* //
|
|
391
|
-
* const { items:
|
|
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
|
-
|
|
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
|
|
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
|
-
*
|
|
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
|
|
411
|
-
*
|
|
412
|
-
*
|
|
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`.
|
|
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.
|
|
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
|
|
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
|
|
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"
|
|
735
|
-
* measuring the array.
|
|
817
|
+
* Use it for totals, badges and "page N of M".
|
|
736
818
|
*
|
|
737
|
-
* @param query -
|
|
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
|
-
* //
|
|
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
|
-
* //
|
|
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.
|
|
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: [
|
|
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
|
-
* //
|
|
778
|
-
* const { rows } = await base44.entities.
|
|
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.
|
|
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
|
|
795
|
-
* const { rows } = await base44.entities.
|
|
796
|
-
* groupBy: '
|
|
797
|
-
* countDistinct: '
|
|
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
|
|
910
|
+
* Creates or updates records by a key you define, instead of by `id`.
|
|
804
911
|
*
|
|
805
|
-
* Use this
|
|
806
|
-
*
|
|
807
|
-
*
|
|
808
|
-
*
|
|
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
|
|
815
|
-
*
|
|
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
|
|
821
|
-
* const result = await base44.entities.
|
|
822
|
-
*
|
|
823
|
-
* { key: '
|
|
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(
|
|
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>>;
|