@base44-preview/sdk 0.8.48-pr.289.c93657a → 0.8.49-pr.288.a18e427
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 +2 -2
- package/dist/modules/actors.types.d.ts +37 -132
- package/dist/modules/ai-gateway.js +12 -4
- package/dist/modules/ai-gateway.types.d.ts +15 -4
- package/dist/modules/entities.js +64 -27
- package/dist/modules/entities.types.d.ts +291 -8
- package/dist/utils/axios-client.js +4 -5
- 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
|
-
/** Wipe the
|
|
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,12 +4,12 @@ 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, EntityFilterOperators, EntityFilterQuery, EntityFilterValue, EntityHandler, EntityRecord, EntityTypeRegistry, ImportResult, RealtimeEventType, RealtimeEvent, RealtimeCallback, SortField, UpdateManyResult, } from "./modules/entities.types.js";
|
|
7
|
+
export type { DeleteManyResult, DeleteResult, EntitiesModule, EntityAggregateResult, EntityAggregateSpec, EntityDateBucketUnit, EntityDistinctOptions, EntityFilterOperators, EntityFilterQuery, EntityFilterValue, EntityHandler, EntityListOptions, EntityPage, EntityRecord, EntityTypeRegistry, EntityUpsertOptions, EntityUpsertResult, ImportResult, RealtimeEventType, RealtimeEvent, RealtimeCallback, SortField, UpdateManyResult, } from "./modules/entities.types.js";
|
|
8
8
|
export type { AuthModule, LoginResponse, RegisterParams, VerifyOtpParams, ChangePasswordParams, ResetPasswordParams, User, } from "./modules/auth.types.js";
|
|
9
9
|
export type { IntegrationsModule, IntegrationEndpointFunction, CoreIntegrations, InvokeLLMParams, GenerateImageParams, GenerateImageResult, UploadFileParams, UploadFileResult, SendEmailParams, SendEmailResult, ExtractDataFromUploadedFileParams, ExtractDataFromUploadedFileResult, UploadPrivateFileParams, UploadPrivateFileResult, CreateFileSignedUrlParams, CreateFileSignedUrlResult, } from "./modules/integrations.types.js";
|
|
10
10
|
export type { FunctionsModule, FunctionName, FunctionNameRegistry, } from "./modules/functions.types.js";
|
|
11
11
|
export type { AgentsModule, AgentName, AgentNameRegistry, AgentConversation, AgentMessage, AgentMessageReasoning, AgentMessageToolCall, AgentMessageUsage, AgentMessageCustomContext, AgentMessageMetadata, CreateConversationParams, } from "./modules/agents.types.js";
|
|
12
|
-
export type { AiGatewayModule, AiGatewayConnection, } from "./modules/ai-gateway.types.js";
|
|
12
|
+
export type { AiGatewayModule, AiGatewayConnection, AiGatewayConnectionOptions, } from "./modules/ai-gateway.types.js";
|
|
13
13
|
export type { AppLogsModule } from "./modules/app-logs.types.js";
|
|
14
14
|
export type { AppModule, AppPublicSettings, AppPublicSettingsResponse, } from "./modules/app.types.js";
|
|
15
15
|
export type { ActorsModule, ActorClient, ActorRef, Connection, ActorSubscription, ActorConnectOptions, ActorNameRegistry, ActorRegistry, } from "./modules/actors.types.js";
|
|
@@ -1,10 +1,9 @@
|
|
|
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.
|
|
5
|
+
* This is separate from {@link ActorNameRegistry} (which is auto-generated
|
|
6
|
+
* by `base44 types generate`), so there are no conflicts.
|
|
8
7
|
*
|
|
9
8
|
* @example
|
|
10
9
|
* ```typescript
|
|
@@ -21,10 +20,8 @@
|
|
|
21
20
|
export interface ActorRegistry {
|
|
22
21
|
}
|
|
23
22
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* The generated names provide autocomplete for deployed actors. To define
|
|
27
|
-
* incoming and outgoing message types manually, augment {@link 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.
|
|
28
25
|
*/
|
|
29
26
|
export interface ActorNameRegistry {
|
|
30
27
|
}
|
|
@@ -35,163 +32,71 @@ type ToClientFor<N extends string> = N extends keyof ActorRegistry ? ActorRegist
|
|
|
35
32
|
type ToServerFor<N extends string> = N extends keyof ActorRegistry ? ActorRegistry[N] extends {
|
|
36
33
|
toServer: infer O;
|
|
37
34
|
} ? O : unknown : unknown;
|
|
38
|
-
/**
|
|
39
|
-
* Options for {@link ActorRef.connect}.
|
|
40
|
-
*/
|
|
35
|
+
/** Options for {@link ActorRef.connect}. */
|
|
41
36
|
export interface ActorConnectOptions {
|
|
42
37
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* reconnections from that client reuse the same connection ID. Omit this property
|
|
47
|
-
* to generate an ID.
|
|
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.
|
|
48
41
|
*/
|
|
49
42
|
id?: string;
|
|
50
43
|
}
|
|
51
|
-
/**
|
|
52
|
-
* Represents an outgoing-message listener registered with
|
|
53
|
-
* {@link Connection.subscribe}.
|
|
54
|
-
*/
|
|
44
|
+
/** Handle for one listener registered via {@link Connection.subscribe}. */
|
|
55
45
|
export interface ActorSubscription {
|
|
56
|
-
/**
|
|
57
|
-
* Removes this listener. Other listeners and the socket stay open.
|
|
58
|
-
*
|
|
59
|
-
* @example
|
|
60
|
-
* ```typescript
|
|
61
|
-
* const sub = conn.subscribe((msg) => console.log(msg));
|
|
62
|
-
* sub.unsubscribe();
|
|
63
|
-
* ```
|
|
64
|
-
*/
|
|
46
|
+
/** Remove this listener; other listeners and the socket stay live. */
|
|
65
47
|
unsubscribe(): void;
|
|
66
48
|
}
|
|
67
49
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* The socket buffers messages sent during connection setup until 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.
|
|
72
53
|
*/
|
|
73
54
|
export interface Connection<N extends string = string> {
|
|
74
|
-
/**
|
|
55
|
+
/** The connection id (the value the actor sees as `conn.id`). */
|
|
75
56
|
readonly id: string;
|
|
76
|
-
/**
|
|
77
|
-
* Registers a listener for outgoing messages from the actor.
|
|
78
|
-
*
|
|
79
|
-
* You can register multiple listeners on the same connection.
|
|
80
|
-
*
|
|
81
|
-
* @param callback - Called with each outgoing message sent to this connection.
|
|
82
|
-
* @returns A handle you can use to remove this listener without closing the socket.
|
|
83
|
-
*
|
|
84
|
-
* @example
|
|
85
|
-
* ```typescript
|
|
86
|
-
* const conn = base44.actors.Chat("session-1").connect();
|
|
87
|
-
* const sub = conn.subscribe((msg) => {
|
|
88
|
-
* if (msg.type === "message") console.log(msg.text);
|
|
89
|
-
* });
|
|
90
|
-
* ```
|
|
91
|
-
*/
|
|
57
|
+
/** Register a message listener. Multiple are allowed; returns a per-listener unsubscribe. */
|
|
92
58
|
subscribe(callback: (data: ToClientFor<N>) => void): ActorSubscription;
|
|
93
|
-
/**
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* The socket buffers messages until it opens. After {@link close}, the socket
|
|
97
|
-
* drops further sends.
|
|
98
|
-
*
|
|
99
|
-
* @param data - Incoming message to send. Typed through {@link ActorRegistry} when configured.
|
|
100
|
-
*
|
|
101
|
-
* @example
|
|
102
|
-
* ```typescript
|
|
103
|
-
* conn.send({ type: "message", text: "Hello" });
|
|
104
|
-
* ```
|
|
105
|
-
*/
|
|
59
|
+
/** Send a message. Buffered by the socket until it's open; dropped after
|
|
60
|
+
* {@link close}. */
|
|
106
61
|
send(data: ToServerFor<N>): void;
|
|
107
62
|
/**
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
* permanently. See {@link ActorRef.connect} for how to open a fresh connection.
|
|
112
|
-
*
|
|
113
|
-
* @example
|
|
114
|
-
* ```typescript
|
|
115
|
-
* conn.close();
|
|
116
|
-
* ```
|
|
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}.
|
|
117
66
|
*/
|
|
118
67
|
close(): void;
|
|
119
68
|
}
|
|
120
69
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
* Call {@link connect} to open the WebSocket and get a {@link 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}.
|
|
124
72
|
*/
|
|
125
73
|
export interface ActorRef<N extends string = string> {
|
|
126
74
|
/**
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
* Repeated calls return the same connection until it closes. After a permanent
|
|
130
|
-
* failure, such as a missing actor or denied connection, fix the cause and call
|
|
131
|
-
* `connect()` again. Add subscriptions to the new connection.
|
|
75
|
+
* Open the WebSocket and return the {@link Connection}. Idempotent while the
|
|
76
|
+
* connection is open.
|
|
132
77
|
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* @
|
|
137
|
-
* ```typescript
|
|
138
|
-
* const conn = base44.actors.Chat("session-1").connect({ id: "tab-abc" });
|
|
139
|
-
* conn.subscribe((msg) => console.log(msg));
|
|
140
|
-
* ```
|
|
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.
|
|
141
82
|
*/
|
|
142
83
|
connect(options?: ActorConnectOptions): Connection<N>;
|
|
143
84
|
}
|
|
144
85
|
/**
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
* {@link ActorNameRegistry}.
|
|
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}.
|
|
149
89
|
*/
|
|
150
90
|
export interface ActorClient<N extends string = string> {
|
|
151
|
-
/**
|
|
152
|
-
* Gets a reference to an actor session.
|
|
153
|
-
*
|
|
154
|
-
* Clients that specify the same actor name and session ID join the same session.
|
|
155
|
-
*
|
|
156
|
-
* @param instanceId - Session ID that identifies which session to connect to.
|
|
157
|
-
* @returns A reference to the actor session.
|
|
158
|
-
*
|
|
159
|
-
* @example
|
|
160
|
-
* ```typescript
|
|
161
|
-
* const session = base44.actors.Chat("lobby-1");
|
|
162
|
-
* const conn = session.connect();
|
|
163
|
-
* ```
|
|
164
|
-
*/
|
|
165
91
|
(instanceId: string): ActorRef<N>;
|
|
166
92
|
}
|
|
167
93
|
/**
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
* Select an actor by name and session ID, then connect a client to the
|
|
171
|
-
* session. Clients in the same session can exchange realtime messages through the
|
|
172
|
-
* actor. The client works in the browser and in Node.
|
|
173
|
-
*
|
|
174
|
-
* ## Connection flow
|
|
175
|
-
*
|
|
176
|
-
* - Connect to a session with `base44.actors.<ActorName>(sessionId).connect()`.
|
|
177
|
-
* - Subscribe to outgoing messages with {@link Connection.subscribe}.
|
|
178
|
-
* - Send incoming messages with {@link Connection.send}.
|
|
179
|
-
* - Close the connection with {@link Connection.close}.
|
|
94
|
+
* The actors module provides access to Cloudflare Durable Object-backed
|
|
95
|
+
* Actors deployed by the Base44 platform.
|
|
180
96
|
*
|
|
181
|
-
* See [Actors Overview](/developers/backend/resources/actors/overview)
|
|
182
|
-
* for actor concepts and terminology, and the [Actor Class Reference](/developers/backend/resources/actors/reference)
|
|
183
|
-
* for the backend class API.
|
|
184
|
-
*
|
|
185
|
-
* ## Authentication modes
|
|
186
|
-
*
|
|
187
|
-
* This module is available in anonymous or user authentication mode
|
|
188
|
-
* (`base44.actors`). It isn't available with service role authentication. Apps that
|
|
189
|
-
* require login can reject anonymous connections in the actor's `handleConnect()` method.
|
|
190
|
-
*
|
|
191
|
-
* @example
|
|
192
97
|
* ```typescript
|
|
193
|
-
* const conn = base44.actors.
|
|
194
|
-
* 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
|
|
195
100
|
* conn.send({ type: "message", text: "hi" });
|
|
196
101
|
* sub.unsubscribe();
|
|
197
102
|
* conn.close();
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { getAccessToken } from "../utils/auth-utils.js";
|
|
2
|
+
const PROVIDER_PATHS = {
|
|
3
|
+
openai: "openai",
|
|
4
|
+
typesafe: "typesafe",
|
|
5
|
+
};
|
|
2
6
|
export function createAiGatewayModule({ serverUrl, token, appId, }) {
|
|
3
|
-
const connection = () => {
|
|
7
|
+
const connection = ({ provider = "openai" } = {}) => {
|
|
4
8
|
var _a;
|
|
5
|
-
|
|
6
|
-
|
|
9
|
+
if (!Object.prototype.hasOwnProperty.call(PROVIDER_PATHS, provider)) {
|
|
10
|
+
throw new Error(`Unsupported AI Gateway provider: ${provider}`);
|
|
11
|
+
}
|
|
12
|
+
const providerPath = PROVIDER_PATHS[provider];
|
|
13
|
+
return {
|
|
14
|
+
baseURL: `${serverUrl}/api/apps/${appId}/ai/${providerPath}/v1`,
|
|
7
15
|
token: (_a = token !== null && token !== void 0 ? token : getAccessToken()) !== null && _a !== void 0 ? _a : "",
|
|
8
|
-
}
|
|
16
|
+
};
|
|
9
17
|
};
|
|
10
18
|
return {
|
|
11
19
|
connection,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Connection details for the Base44 AI Gateway.
|
|
3
3
|
*/
|
|
4
4
|
export interface AiGatewayConnection {
|
|
5
|
-
/** Base URL of the gateway
|
|
5
|
+
/** Base URL of the selected gateway provider. */
|
|
6
6
|
baseURL: string;
|
|
7
7
|
/**
|
|
8
8
|
* Bearer token that authenticates the request. Empty string when the caller is
|
|
@@ -10,6 +10,11 @@ export interface AiGatewayConnection {
|
|
|
10
10
|
*/
|
|
11
11
|
token: string;
|
|
12
12
|
}
|
|
13
|
+
/** Options for selecting an AI Gateway provider. */
|
|
14
|
+
export interface AiGatewayConnectionOptions {
|
|
15
|
+
/** Gateway provider to connect to. Defaults to `openai`. */
|
|
16
|
+
provider?: "openai" | "typesafe";
|
|
17
|
+
}
|
|
13
18
|
/**
|
|
14
19
|
* Configuration for the AI Gateway module.
|
|
15
20
|
* @internal
|
|
@@ -34,13 +39,17 @@ export interface AiGatewayModuleConfig {
|
|
|
34
39
|
* against the provider directly, no separate account, API key, or billing
|
|
35
40
|
* setup with the underlying model provider required.
|
|
36
41
|
*
|
|
42
|
+
* By default, `connection()` uses the OpenAI-compatible provider. Pass
|
|
43
|
+
* `{ provider: "typesafe" }` to connect to TypeSafe for structured evaluations
|
|
44
|
+
* with `@ai-sdk/typesafe-ai`.
|
|
45
|
+
*
|
|
37
46
|
* Call `connection()` from a backend function rather than the browser. This
|
|
38
47
|
* keeps your instructions, tools, and business logic server-side, and lets
|
|
39
48
|
* you enforce your own auth, rate, and spend limits around the call. The
|
|
40
49
|
* `token` it returns is the caller's regular session token, the same one
|
|
41
50
|
* used for every other SDK call.
|
|
42
51
|
*
|
|
43
|
-
* ##
|
|
52
|
+
* ## OpenAI-compatible models
|
|
44
53
|
*
|
|
45
54
|
* You can use any of the [models available through `InvokeLLM`](/developers/references/sdk/docs/type-aliases/integrations#invokellm).
|
|
46
55
|
* Pass `'automatic'` to let Base44 choose one, or pin a specific model such
|
|
@@ -69,7 +78,9 @@ export interface AiGatewayModule {
|
|
|
69
78
|
/**
|
|
70
79
|
* Gets the connection details for the Base44 AI Gateway.
|
|
71
80
|
*
|
|
72
|
-
* Returns the `baseURL` and `token` to pass to
|
|
81
|
+
* Returns the `baseURL` and `token` to pass to the selected provider client.
|
|
82
|
+
*
|
|
83
|
+
* @param options - Provider selection. Omit it to use the OpenAI-compatible gateway.
|
|
73
84
|
*
|
|
74
85
|
* @returns The gateway {@linkcode AiGatewayConnection | connection} (`baseURL` and `token`).
|
|
75
86
|
*
|
|
@@ -135,5 +146,5 @@ export interface AiGatewayModule {
|
|
|
135
146
|
* await agent.generate({ prompt: `Review this return request: ${JSON.stringify(returnRequest)}` });
|
|
136
147
|
* ```
|
|
137
148
|
*/
|
|
138
|
-
connection(): AiGatewayConnection;
|
|
149
|
+
connection(options?: AiGatewayConnectionOptions): AiGatewayConnection;
|
|
139
150
|
}
|
package/dist/modules/entities.js
CHANGED
|
@@ -41,6 +41,10 @@ function parseRealtimeMessage(dataStr) {
|
|
|
41
41
|
return null;
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
+
const DEFAULT_PAGE_LIMIT = 100;
|
|
45
|
+
function isPageOptions(value) {
|
|
46
|
+
return typeof value === "object" && value !== null;
|
|
47
|
+
}
|
|
44
48
|
/**
|
|
45
49
|
* Creates a handler for a specific entity.
|
|
46
50
|
*
|
|
@@ -53,34 +57,51 @@ function parseRealtimeMessage(dataStr) {
|
|
|
53
57
|
*/
|
|
54
58
|
function createEntityHandler(axios, appId, entityName, getSocket) {
|
|
55
59
|
const baseURL = `/apps/${appId}/entities/${entityName}`;
|
|
60
|
+
const fieldsParam = (fields) => Array.isArray(fields) ? fields.join(",") : fields;
|
|
61
|
+
// GET /{entity}: the array form shared by list() and filter()
|
|
62
|
+
const readArray = (sort, limit, skip, fields, query) => {
|
|
63
|
+
const params = {};
|
|
64
|
+
if (query)
|
|
65
|
+
params.q = JSON.stringify(query);
|
|
66
|
+
if (sort)
|
|
67
|
+
params.sort = sort;
|
|
68
|
+
if (limit)
|
|
69
|
+
params.limit = limit;
|
|
70
|
+
if (skip)
|
|
71
|
+
params.skip = skip;
|
|
72
|
+
if (fields)
|
|
73
|
+
params.fields = fieldsParam(fields);
|
|
74
|
+
return axios.get(baseURL, { params });
|
|
75
|
+
};
|
|
76
|
+
// GET /{entity}/v2/list: one cursor page of records or distinct values, shared by list(options) and filter(query, options)
|
|
77
|
+
const readPage = (options, query) => {
|
|
78
|
+
const params = {};
|
|
79
|
+
if (query)
|
|
80
|
+
params.q = JSON.stringify(query);
|
|
81
|
+
params.limit = options.limit || DEFAULT_PAGE_LIMIT;
|
|
82
|
+
if (options.cursor)
|
|
83
|
+
params.cursor = options.cursor;
|
|
84
|
+
if ("distinct" in options) {
|
|
85
|
+
params.distinct = options.distinct;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
if (options.sort)
|
|
89
|
+
params.sort = options.sort;
|
|
90
|
+
if (options.fields)
|
|
91
|
+
params.fields = fieldsParam(options.fields);
|
|
92
|
+
}
|
|
93
|
+
return axios.get(`${baseURL}/v2/list`, { params });
|
|
94
|
+
};
|
|
56
95
|
return {
|
|
57
|
-
//
|
|
58
|
-
async list(
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (fields)
|
|
67
|
-
params.fields = Array.isArray(fields) ? fields.join(",") : fields;
|
|
68
|
-
return axios.get(baseURL, { params });
|
|
69
|
-
},
|
|
70
|
-
// Filter entities based on query
|
|
71
|
-
async filter(query, sort, limit, skip, fields) {
|
|
72
|
-
const params = {
|
|
73
|
-
q: JSON.stringify(query),
|
|
74
|
-
};
|
|
75
|
-
if (sort)
|
|
76
|
-
params.sort = sort;
|
|
77
|
-
if (limit)
|
|
78
|
-
params.limit = limit;
|
|
79
|
-
if (skip)
|
|
80
|
-
params.skip = skip;
|
|
81
|
-
if (fields)
|
|
82
|
-
params.fields = Array.isArray(fields) ? fields.join(",") : fields;
|
|
83
|
-
return axios.get(baseURL, { params });
|
|
96
|
+
// list(sort, limit, skip, fields) returns an array; list(options) returns one cursor page.
|
|
97
|
+
async list(...args) {
|
|
98
|
+
const [sort, limit, skip, fields] = args;
|
|
99
|
+
return isPageOptions(sort) ? readPage(sort) : readArray(sort, limit, skip, fields);
|
|
100
|
+
},
|
|
101
|
+
// filter(query, sort, limit, skip, fields) returns an array; filter(query, options) returns one cursor page.
|
|
102
|
+
async filter(query, ...args) {
|
|
103
|
+
const [sort, limit, skip, fields] = args;
|
|
104
|
+
return isPageOptions(sort) ? readPage(sort, query) : readArray(sort, limit, skip, fields, query);
|
|
84
105
|
},
|
|
85
106
|
// Get entity by ID
|
|
86
107
|
async get(id) {
|
|
@@ -110,6 +131,22 @@ function createEntityHandler(axios, appId, entityName, getSocket) {
|
|
|
110
131
|
async updateMany(query, data) {
|
|
111
132
|
return axios.patch(`${baseURL}/update-many`, { query, data });
|
|
112
133
|
},
|
|
134
|
+
// Count entities matching a query
|
|
135
|
+
async count(query) {
|
|
136
|
+
const params = {};
|
|
137
|
+
if (query)
|
|
138
|
+
params.q = JSON.stringify(query);
|
|
139
|
+
const result = await axios.get(`${baseURL}/count`, { params });
|
|
140
|
+
return result.count;
|
|
141
|
+
},
|
|
142
|
+
// Server-side group-by aggregation
|
|
143
|
+
async aggregate(spec) {
|
|
144
|
+
return axios.post(`${baseURL}/aggregate`, spec);
|
|
145
|
+
},
|
|
146
|
+
// Create or update by a natural key
|
|
147
|
+
async upsert(records, options) {
|
|
148
|
+
return axios.post(`${baseURL}/upsert`, { records, key: options.key });
|
|
149
|
+
},
|
|
113
150
|
// Update multiple entities by ID, each with its own update data
|
|
114
151
|
async bulkUpdate(data) {
|
|
115
152
|
return axios.put(`${baseURL}/bulk`, data);
|
|
@@ -50,6 +50,130 @@ export interface UpdateManyResult {
|
|
|
50
50
|
/** Whether there are more entities matching the query that were not updated in this batch. When `true`, call `updateMany` again with the same query to update the next batch. */
|
|
51
51
|
has_more: boolean;
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Options object accepted by {@linkcode EntityHandler.list | list()} and
|
|
55
|
+
* {@linkcode EntityHandler.filter | filter()} to read one cursor page.
|
|
56
|
+
*
|
|
57
|
+
* @typeParam T - Entity record type.
|
|
58
|
+
* @typeParam K - The fields to include in each record.
|
|
59
|
+
*/
|
|
60
|
+
export interface EntityListOptions<T, K extends keyof T = keyof T> {
|
|
61
|
+
/** Sort parameter, such as `'-created_date'` for descending. Defaults to `'-created_date'`. */
|
|
62
|
+
sort?: SortField<T>;
|
|
63
|
+
/** Maximum number of records per page, up to 5,000. Defaults to 100. */
|
|
64
|
+
limit?: number;
|
|
65
|
+
/**
|
|
66
|
+
* `next_cursor` from the previous page. Omit or pass `null` for the first page.
|
|
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.
|
|
70
|
+
*/
|
|
71
|
+
cursor?: string | null;
|
|
72
|
+
/** Array of field names to include in each record. Defaults to all fields. */
|
|
73
|
+
fields?: K[];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Options object accepted by {@linkcode EntityHandler.list | list()} and
|
|
77
|
+
* {@linkcode EntityHandler.filter | filter()} to read the distinct values of one field
|
|
78
|
+
* instead of records.
|
|
79
|
+
*
|
|
80
|
+
* @typeParam T - Entity record type.
|
|
81
|
+
* @typeParam K - The field whose distinct values to read.
|
|
82
|
+
*/
|
|
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. */
|
|
85
|
+
distinct: K;
|
|
86
|
+
/** Maximum number of values per page, up to 1,000. Defaults to 100. */
|
|
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. */
|
|
89
|
+
cursor?: string | null;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* One page of records, returned by {@linkcode EntityHandler.list | list()} and
|
|
93
|
+
* {@linkcode EntityHandler.filter | filter()} when called with an options object.
|
|
94
|
+
*
|
|
95
|
+
* @typeParam T - Record type of the items.
|
|
96
|
+
*/
|
|
97
|
+
export interface EntityPage<T> {
|
|
98
|
+
/** The page's records in the requested sort order, or the distinct values in ascending order. */
|
|
99
|
+
items: T[];
|
|
100
|
+
/** Pass as `cursor` to get the next page. `null` on the last page. */
|
|
101
|
+
next_cursor: string | null;
|
|
102
|
+
/** Whether records remain after this page. */
|
|
103
|
+
has_more: boolean;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Time unit for {@linkcode EntityAggregateSpec.dateBucket | dateBucket}.
|
|
107
|
+
*/
|
|
108
|
+
export type EntityDateBucketUnit = "day" | "week" | "month" | "year";
|
|
109
|
+
/**
|
|
110
|
+
* Describes what {@linkcode EntityHandler.aggregate | aggregate()} computes.
|
|
111
|
+
*
|
|
112
|
+
* Name the fields to group by and the measures to compute; the server does the work and
|
|
113
|
+
* returns one row per group. Field names are the entity's own field names.
|
|
114
|
+
*
|
|
115
|
+
* @typeParam T - Entity record type.
|
|
116
|
+
*/
|
|
117
|
+
export interface EntityAggregateSpec<T> {
|
|
118
|
+
/** Filter applied before grouping, in the same form {@linkcode EntityHandler.filter | filter()} accepts. Defaults to all records. */
|
|
119
|
+
query?: EntityFilterQuery<T>;
|
|
120
|
+
/** Field, or up to four fields, to group by. Omit to get one total row. */
|
|
121
|
+
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
|
+
};
|
|
127
|
+
/** Whether to include the number of records per group as `count`. Defaults to `true`. */
|
|
128
|
+
count?: boolean;
|
|
129
|
+
/** Field, or fields, to sum. Each appears in the rows as `sum_<field>`. */
|
|
130
|
+
sum?: (keyof T & string) | (keyof T & string)[];
|
|
131
|
+
/** Field, or fields, to average. Each appears in the rows as `avg_<field>`. */
|
|
132
|
+
avg?: (keyof T & string) | (keyof T & string)[];
|
|
133
|
+
/** Field, or fields, to take the minimum of. Each appears in the rows as `min_<field>`. */
|
|
134
|
+
min?: (keyof T & string) | (keyof T & string)[];
|
|
135
|
+
/** Field, or fields, to take the maximum of. Each appears in the rows as `max_<field>`. */
|
|
136
|
+
max?: (keyof T & string) | (keyof T & string)[];
|
|
137
|
+
/** Field whose distinct values to count per group, returned as `count_distinct_<field>`. */
|
|
138
|
+
countDistinct?: keyof T & string;
|
|
139
|
+
/** Filter on the computed fields, applied after grouping. For example `{ count: { $gt: 1 } }` keeps only duplicated groups. */
|
|
140
|
+
having?: Record<string, any>;
|
|
141
|
+
/** Computed or group field to sort the rows by, with a `-` prefix for descending. For example `'-count'`. */
|
|
142
|
+
sort?: string;
|
|
143
|
+
/** Maximum number of rows, up to 1,000. Defaults to 1,000. */
|
|
144
|
+
limit?: number;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Rows returned by {@linkcode EntityHandler.aggregate | aggregate()}.
|
|
148
|
+
*/
|
|
149
|
+
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>`. */
|
|
151
|
+
rows: Record<string, any>[];
|
|
152
|
+
/** `true` when more groups exist than `limit` allowed. */
|
|
153
|
+
truncated: boolean;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Options for {@linkcode EntityHandler.upsert | upsert()}.
|
|
157
|
+
*
|
|
158
|
+
* @typeParam T - Entity record type.
|
|
159
|
+
*/
|
|
160
|
+
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. */
|
|
162
|
+
key: (keyof T & string) | (keyof T & string)[];
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Result returned by {@linkcode EntityHandler.upsert | upsert()}.
|
|
166
|
+
*
|
|
167
|
+
* @typeParam T - Entity record type.
|
|
168
|
+
*/
|
|
169
|
+
export interface EntityUpsertResult<T = any> {
|
|
170
|
+
/** Number of records that were created. */
|
|
171
|
+
created: number;
|
|
172
|
+
/** Number of existing records that were updated. */
|
|
173
|
+
updated: number;
|
|
174
|
+
/** The written records, created and updated, as they now exist. */
|
|
175
|
+
records: T[];
|
|
176
|
+
}
|
|
53
177
|
/**
|
|
54
178
|
* Result returned when importing entities from a file.
|
|
55
179
|
*
|
|
@@ -211,14 +335,18 @@ export interface EntityHandler<T = any> {
|
|
|
211
335
|
* Retrieves all records of this type with support for sorting,
|
|
212
336
|
* pagination, and field selection.
|
|
213
337
|
*
|
|
214
|
-
* **Note:** The maximum limit is 5,000 items per request.
|
|
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.
|
|
215
343
|
*
|
|
216
344
|
* @typeParam K - The fields to include in the response. Defaults to all fields.
|
|
217
345
|
* @param sort - Sort parameter, such as `'-created_date'` for descending. Defaults to `'-created_date'`.
|
|
218
|
-
* @param limit - Maximum number of results to return. Defaults to `
|
|
219
|
-
* @param skip - Number of results to skip for pagination. Defaults to `0`.
|
|
346
|
+
* @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.
|
|
220
348
|
* @param fields - Array of field names to include in the response. Defaults to all fields.
|
|
221
|
-
* @returns Promise resolving to an array of records with selected fields.
|
|
349
|
+
* @returns Promise resolving to an array of records with selected fields. When called with an options object, resolves instead to an {@linkcode EntityPage | EntityPage} with `items`, `next_cursor` and `has_more`; with a `distinct` option the items are the field's values.
|
|
222
350
|
*
|
|
223
351
|
* @example
|
|
224
352
|
* ```typescript
|
|
@@ -244,15 +372,39 @@ export interface EntityHandler<T = any> {
|
|
|
244
372
|
* // Get only specific fields
|
|
245
373
|
* const fields = await base44.entities.MyEntity.list('-created_date', 10, 0, ['name', 'status']);
|
|
246
374
|
* ```
|
|
375
|
+
*
|
|
376
|
+
* @example
|
|
377
|
+
* ```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);
|
|
382
|
+
* while (page.has_more) {
|
|
383
|
+
* page = await base44.entities.MyEntity.list({ cursor: page.next_cursor, limit: 1000 });
|
|
384
|
+
* await exportRows(page.items);
|
|
385
|
+
* }
|
|
386
|
+
* ```
|
|
387
|
+
*
|
|
388
|
+
* @example
|
|
389
|
+
* ```typescript
|
|
390
|
+
* // Distinct values of one field, instead of records
|
|
391
|
+
* const { items: categories } = await base44.entities.Product.list({ distinct: 'category' });
|
|
392
|
+
* ```
|
|
247
393
|
*/
|
|
248
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>>>;
|
|
249
397
|
/**
|
|
250
398
|
* Filters records based on a query.
|
|
251
399
|
*
|
|
252
400
|
* Retrieves records that match specific criteria with support for
|
|
253
401
|
* sorting, pagination, and field selection.
|
|
254
402
|
*
|
|
255
|
-
* **Note:** The maximum limit is 5,000 items per request.
|
|
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.
|
|
256
408
|
*
|
|
257
409
|
* @typeParam K - The fields to include in the response. Defaults to all fields.
|
|
258
410
|
* @param query - Query object with field-value pairs. Each key should be a field name
|
|
@@ -261,10 +413,10 @@ export interface EntityHandler<T = any> {
|
|
|
261
413
|
* for exact matches, `null` for null values, arrays as shorthand for matching any of the
|
|
262
414
|
* provided values, or documented MongoDB query operators for advanced filtering.
|
|
263
415
|
* @param sort - Sort parameter, such as `'-created_date'` for descending. Defaults to `'-created_date'`.
|
|
264
|
-
* @param limit - Maximum number of results to return. Defaults to `
|
|
265
|
-
* @param skip - Number of results to skip for pagination. Defaults to `0`.
|
|
416
|
+
* @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.
|
|
266
418
|
* @param fields - Array of field names to include in the response. Defaults to all fields.
|
|
267
|
-
* @returns Promise resolving to an array of filtered records with selected fields.
|
|
419
|
+
* @returns Promise resolving to an array of filtered records with selected fields. When called with an options object, resolves instead to an {@linkcode EntityPage | EntityPage} with `items`, `next_cursor` and `has_more`; with a `distinct` option the items are the field's values.
|
|
268
420
|
*
|
|
269
421
|
* @example
|
|
270
422
|
* ```typescript
|
|
@@ -341,8 +493,34 @@ export interface EntityHandler<T = any> {
|
|
|
341
493
|
* ['name', 'priority']
|
|
342
494
|
* );
|
|
343
495
|
* ```
|
|
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
|
+
* ```
|
|
344
520
|
*/
|
|
345
521
|
filter<K extends keyof T = keyof T>(query: EntityFilterQuery<T>, sort?: SortField<T>, limit?: number, skip?: number, fields?: K[]): Promise<Pick<T, K>[]>;
|
|
522
|
+
filter<K extends keyof T>(query: EntityFilterQuery<T>, options: EntityDistinctOptions<T, K>): Promise<EntityPage<T[K]>>;
|
|
523
|
+
filter<K extends keyof T = keyof T>(query: EntityFilterQuery<T>, options: EntityListOptions<T, K>): Promise<EntityPage<Pick<T, K>>>;
|
|
346
524
|
/**
|
|
347
525
|
* Gets a single record by ID.
|
|
348
526
|
*
|
|
@@ -549,6 +727,111 @@ export interface EntityHandler<T = any> {
|
|
|
549
727
|
* ```
|
|
550
728
|
*/
|
|
551
729
|
updateMany(query: Partial<T>, data: Record<string, Record<string, any>>): Promise<UpdateManyResult>;
|
|
730
|
+
/**
|
|
731
|
+
* Counts the records that match a query.
|
|
732
|
+
*
|
|
733
|
+
* 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.
|
|
736
|
+
*
|
|
737
|
+
* @param query - Filter query, in the same form {@linkcode filter | filter()} accepts. Defaults to all records.
|
|
738
|
+
* @returns Promise resolving to the number of matching records.
|
|
739
|
+
*
|
|
740
|
+
* @example
|
|
741
|
+
* ```typescript
|
|
742
|
+
* // How many tasks are still open?
|
|
743
|
+
* const open = await base44.entities.Task.count({ status: 'open' });
|
|
744
|
+
* ```
|
|
745
|
+
*
|
|
746
|
+
* @example
|
|
747
|
+
* ```typescript
|
|
748
|
+
* // Total records in the entity
|
|
749
|
+
* const total = await base44.entities.Task.count();
|
|
750
|
+
* ```
|
|
751
|
+
*/
|
|
752
|
+
count(query?: EntityFilterQuery<T>): Promise<number>;
|
|
753
|
+
/**
|
|
754
|
+
* Computes counts, sums, averages, minimums, maximums or distinct counts, grouped by fields.
|
|
755
|
+
*
|
|
756
|
+
* Use it for dashboards, leaderboards and reports instead of loading every record
|
|
757
|
+
* and adding up in the browser. The server groups the records you can read and
|
|
758
|
+
* returns one row per group, up to 1,000 rows.
|
|
759
|
+
*
|
|
760
|
+
* @param spec - What to group by and what to compute. See {@linkcode EntityAggregateSpec | EntityAggregateSpec}.
|
|
761
|
+
* @returns Promise resolving to the rows and a `truncated` flag.
|
|
762
|
+
*
|
|
763
|
+
* @example
|
|
764
|
+
* ```typescript
|
|
765
|
+
* // Sales per agent this month, biggest first
|
|
766
|
+
* const { rows } = await base44.entities.Sale.aggregate({
|
|
767
|
+
* query: { sale_date: { $gte: '2026-09-01' } },
|
|
768
|
+
* groupBy: 'agent_id',
|
|
769
|
+
* sum: 'amount',
|
|
770
|
+
* sort: '-sum_amount'
|
|
771
|
+
* });
|
|
772
|
+
* // rows: [{ agent_id: 'a1', count: 42, sum_amount: 18250 }, ...]
|
|
773
|
+
* ```
|
|
774
|
+
*
|
|
775
|
+
* @example
|
|
776
|
+
* ```typescript
|
|
777
|
+
* // Records created per day
|
|
778
|
+
* const { rows } = await base44.entities.Visit.aggregate({
|
|
779
|
+
* dateBucket: { field: 'created_date', unit: 'day' }
|
|
780
|
+
* });
|
|
781
|
+
* ```
|
|
782
|
+
*
|
|
783
|
+
* @example
|
|
784
|
+
* ```typescript
|
|
785
|
+
* // Find duplicated external ids
|
|
786
|
+
* const { rows } = await base44.entities.Contact.aggregate({
|
|
787
|
+
* groupBy: 'external_id',
|
|
788
|
+
* having: { count: { $gt: 1 } }
|
|
789
|
+
* });
|
|
790
|
+
* ```
|
|
791
|
+
*
|
|
792
|
+
* @example
|
|
793
|
+
* ```typescript
|
|
794
|
+
* // Unique visitors per page
|
|
795
|
+
* const { rows } = await base44.entities.PageView.aggregate({
|
|
796
|
+
* groupBy: 'path',
|
|
797
|
+
* countDistinct: 'session_id'
|
|
798
|
+
* });
|
|
799
|
+
* ```
|
|
800
|
+
*/
|
|
801
|
+
aggregate(spec: EntityAggregateSpec<T>): Promise<EntityAggregateResult>;
|
|
802
|
+
/**
|
|
803
|
+
* Creates or updates records by a key of your own.
|
|
804
|
+
*
|
|
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.
|
|
809
|
+
*
|
|
810
|
+
* You can upsert up to 500 records per request. When two records in one call share a
|
|
811
|
+
* key, the last one wins. Updates merge the given fields into the existing record, like
|
|
812
|
+
* {@linkcode update | update()}.
|
|
813
|
+
*
|
|
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}.
|
|
816
|
+
* @returns Promise resolving to the counts and the written records.
|
|
817
|
+
*
|
|
818
|
+
* @example
|
|
819
|
+
* ```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' }
|
|
824
|
+
* );
|
|
825
|
+
* console.log(`${result.created} new, ${result.updated} updated`);
|
|
826
|
+
* ```
|
|
827
|
+
*
|
|
828
|
+
* @example
|
|
829
|
+
* ```typescript
|
|
830
|
+
* // Compound key
|
|
831
|
+
* await base44.entities.Inventory.upsert(rows, { key: ['sku', 'warehouse'] });
|
|
832
|
+
* ```
|
|
833
|
+
*/
|
|
834
|
+
upsert(records: Partial<T>[], options: EntityUpsertOptions<T>): Promise<EntityUpsertResult<T>>;
|
|
552
835
|
/**
|
|
553
836
|
* Updates the specified records in a single request, each with its own data.
|
|
554
837
|
*
|
|
@@ -187,11 +187,10 @@ export function createAxiosClient({ baseURL, headers = {}, token, interceptRespo
|
|
|
187
187
|
}
|
|
188
188
|
return response.data;
|
|
189
189
|
}, (error) => {
|
|
190
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const base44Error = new Base44Error(message, (_e = error.response) === null || _e === void 0 ? void 0 : _e.status, (_m = (_h = (_g = (_f = error.response) === null || _f === void 0 ? void 0 : _f.data) === null || _g === void 0 ? void 0 : _g.code) !== null && _h !== void 0 ? _h : (_l = (_k = (_j = error.response) === null || _j === void 0 ? void 0 : _j.headers) === null || _k === void 0 ? void 0 : _k.get) === null || _l === void 0 ? void 0 : _l.call(_k, "x-base44-connector-error")) !== null && _m !== void 0 ? _m : (_p = (_o = error.response) === null || _o === void 0 ? void 0 : _o.headers) === null || _p === void 0 ? void 0 : _p["x-base44-connector-error"], (_q = error.response) === null || _q === void 0 ? void 0 : _q.data, error);
|
|
190
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
191
|
+
const data = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data;
|
|
192
|
+
const message = ((_b = data === null || data === void 0 ? void 0 : data.error) === null || _b === void 0 ? void 0 : _b.message) || (data === null || data === void 0 ? void 0 : data.message) || (data === null || data === void 0 ? void 0 : data.detail) || error.message;
|
|
193
|
+
const base44Error = new Base44Error(message, (_c = error.response) === null || _c === void 0 ? void 0 : _c.status, (_k = (_f = (_e = (_d = data === null || data === void 0 ? void 0 : data.error) === null || _d === void 0 ? void 0 : _d.code) !== null && _e !== void 0 ? _e : data === null || data === void 0 ? void 0 : data.code) !== null && _f !== void 0 ? _f : (_j = (_h = (_g = error.response) === null || _g === void 0 ? void 0 : _g.headers) === null || _h === void 0 ? void 0 : _h.get) === null || _j === void 0 ? void 0 : _j.call(_h, "x-base44-connector-error")) !== null && _k !== void 0 ? _k : (_m = (_l = error.response) === null || _l === void 0 ? void 0 : _l.headers) === null || _m === void 0 ? void 0 : _m["x-base44-connector-error"], data, error);
|
|
195
194
|
// Log errors in development
|
|
196
195
|
if (process.env.NODE_ENV !== "production") {
|
|
197
196
|
safeErrorLog("[Base44 SDK Error]", base44Error);
|