@base44-preview/sdk 0.8.44-pr.270.91862eb → 0.8.46-pr.272.2f95f82
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 +7 -7
- package/dist/actor.js +6 -6
- package/dist/client.d.ts +2 -2
- package/dist/client.js +7 -5
- package/dist/client.types.d.ts +27 -19
- package/dist/index.d.ts +3 -2
- package/dist/modules/actors.d.ts +1 -1
- package/dist/modules/actors.js +5 -5
- package/dist/modules/actors.types.d.ts +6 -6
- package/dist/modules/analytics.d.ts +3 -13
- package/dist/modules/analytics.js +17 -135
- package/dist/modules/analytics.types.d.ts +0 -91
- package/dist/modules/app.d.ts +11 -0
- package/dist/modules/app.js +15 -0
- package/dist/modules/app.types.d.ts +53 -1
- package/dist/modules/auth.js +1 -1
- package/dist/modules/auth.types.d.ts +2 -2
- package/dist/modules/connectors.js +2 -2
- package/dist/modules/connectors.types.d.ts +4 -4
- package/dist/modules/entities.js +1 -1
- package/dist/utils/common.js +1 -1
- package/package.json +1 -1
package/dist/actor.d.ts
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
* export class MyActor extends Actor { ... }
|
|
7
7
|
*
|
|
8
8
|
* At deploy time the bundler replaces this import with the compiled
|
|
9
|
-
* Cloudflare Durable Object implementation
|
|
9
|
+
* Cloudflare Durable Object implementation. This file provides types only.
|
|
10
10
|
*/
|
|
11
11
|
import type { Base44Client } from "./client";
|
|
12
12
|
/**
|
|
13
13
|
* A single client connection. `Send` is the message type this connection accepts
|
|
14
|
-
* via {@link send}
|
|
14
|
+
* via {@link send}, the actor's *outgoing* (server→client) messages.
|
|
15
15
|
*/
|
|
16
16
|
export interface Conn<Send = unknown> {
|
|
17
17
|
/** Unique per-connection id (one per socket/tab), the same value the client
|
|
@@ -33,9 +33,9 @@ export interface Storage {
|
|
|
33
33
|
* Base class for an Actor.
|
|
34
34
|
*
|
|
35
35
|
* @typeParam Incoming - messages this actor *receives* from clients
|
|
36
|
-
* (`handleMessage`'s `msg`)
|
|
36
|
+
* (`handleMessage`'s `msg`), the schema's `toServer` section.
|
|
37
37
|
* @typeParam Outgoing - messages this actor *sends* to clients
|
|
38
|
-
* (`conn.send`/`broadcast`)
|
|
38
|
+
* (`conn.send`/`broadcast`), the schema's `toClient` section.
|
|
39
39
|
*
|
|
40
40
|
* With a generated `schema.jsonc`, wire both from the registry so they can't drift
|
|
41
41
|
* from the client's types:
|
|
@@ -51,7 +51,7 @@ export declare abstract class Actor<Incoming = unknown, Outgoing = unknown> {
|
|
|
51
51
|
abstract handleTick(): void | Promise<void>;
|
|
52
52
|
/**
|
|
53
53
|
* Optional wake hook: runs once when the instance starts, before any
|
|
54
|
-
* connection is handled
|
|
54
|
+
* connection is handled. Safe to load persisted state here.
|
|
55
55
|
*/
|
|
56
56
|
handleStart(): void | Promise<void>;
|
|
57
57
|
/**
|
|
@@ -71,7 +71,7 @@ export declare abstract class Actor<Incoming = unknown, Outgoing = unknown> {
|
|
|
71
71
|
/**
|
|
72
72
|
* Managed ticker (opt-in). Override {@link shouldTick} and the platform runs
|
|
73
73
|
* {@link handleTick} on a timer of {@link tickIntervalMs} while it returns true,
|
|
74
|
-
* and stops (letting the Durable Object hibernate
|
|
74
|
+
* and stops (letting the Durable Object hibernate at no compute cost) when it
|
|
75
75
|
* returns false. The platform owns scheduling, rescheduling, self-heal, and
|
|
76
76
|
* error-safety.
|
|
77
77
|
*
|
|
@@ -85,7 +85,7 @@ export declare abstract class Actor<Incoming = unknown, Outgoing = unknown> {
|
|
|
85
85
|
protected get instanceId(): string;
|
|
86
86
|
protected get storage(): Storage;
|
|
87
87
|
/**
|
|
88
|
-
* Anonymous Base44 client scoped to this actor instance
|
|
88
|
+
* Anonymous Base44 client scoped to this actor instance, with no user or service
|
|
89
89
|
* auth, so entity access is RLS-gated (same as a logged-out visitor). Always
|
|
90
90
|
* operates on production data: an actor runs server-side with no per-connection
|
|
91
91
|
* identity, so a Test DB preview selected in the editor does not apply here.
|
package/dist/actor.js
CHANGED
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
* export class MyActor extends Actor { ... }
|
|
7
7
|
*
|
|
8
8
|
* At deploy time the bundler replaces this import with the compiled
|
|
9
|
-
* Cloudflare Durable Object implementation
|
|
9
|
+
* Cloudflare Durable Object implementation. This file provides types only.
|
|
10
10
|
*/
|
|
11
11
|
/**
|
|
12
12
|
* Base class for an Actor.
|
|
13
13
|
*
|
|
14
14
|
* @typeParam Incoming - messages this actor *receives* from clients
|
|
15
|
-
* (`handleMessage`'s `msg`)
|
|
15
|
+
* (`handleMessage`'s `msg`), the schema's `toServer` section.
|
|
16
16
|
* @typeParam Outgoing - messages this actor *sends* to clients
|
|
17
|
-
* (`conn.send`/`broadcast`)
|
|
17
|
+
* (`conn.send`/`broadcast`), the schema's `toClient` section.
|
|
18
18
|
*
|
|
19
19
|
* With a generated `schema.jsonc`, wire both from the registry so they can't drift
|
|
20
20
|
* from the client's types:
|
|
@@ -28,7 +28,7 @@ export class Actor {
|
|
|
28
28
|
/**
|
|
29
29
|
* Managed ticker (opt-in). Override {@link shouldTick} and the platform runs
|
|
30
30
|
* {@link handleTick} on a timer of {@link tickIntervalMs} while it returns true,
|
|
31
|
-
* and stops (letting the Durable Object hibernate
|
|
31
|
+
* and stops (letting the Durable Object hibernate at no compute cost) when it
|
|
32
32
|
* returns false. The platform owns scheduling, rescheduling, self-heal, and
|
|
33
33
|
* error-safety.
|
|
34
34
|
*
|
|
@@ -39,7 +39,7 @@ export class Actor {
|
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
41
|
* Optional wake hook: runs once when the instance starts, before any
|
|
42
|
-
* connection is handled
|
|
42
|
+
* connection is handled. Safe to load persisted state here.
|
|
43
43
|
*/
|
|
44
44
|
handleStart() { }
|
|
45
45
|
/**
|
|
@@ -73,7 +73,7 @@ export class Actor {
|
|
|
73
73
|
throw new Error("Actor.storage is only available inside a deployed actor");
|
|
74
74
|
}
|
|
75
75
|
/**
|
|
76
|
-
* Anonymous Base44 client scoped to this actor instance
|
|
76
|
+
* Anonymous Base44 client scoped to this actor instance, with no user or service
|
|
77
77
|
* auth, so entity access is RLS-gated (same as a logged-out visitor). Always
|
|
78
78
|
* operates on production data: an actor runs server-side with no per-connection
|
|
79
79
|
* identity, so a Test DB preview selected in the editor does not apply here.
|
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Base44Client, CreateClientConfig, CreateClientOptions } from "./client.types.js";
|
|
2
|
-
export type { Base44Client, CreateClientConfig, CreateClientOptions };
|
|
1
|
+
import type { Base44Client, CreateClientAnalyticsConfig, CreateClientConfig, CreateClientOptions } from "./client.types.js";
|
|
2
|
+
export type { Base44Client, CreateClientAnalyticsConfig, CreateClientConfig, CreateClientOptions, };
|
|
3
3
|
/**
|
|
4
4
|
* Creates a Base44 client.
|
|
5
5
|
*
|
package/dist/client.js
CHANGED
|
@@ -9,6 +9,7 @@ import { createFunctionsModule } from "./modules/functions.js";
|
|
|
9
9
|
import { createAgentsModule } from "./modules/agents.js";
|
|
10
10
|
import { createAiGatewayModule } from "./modules/ai-gateway.js";
|
|
11
11
|
import { createAppLogsModule } from "./modules/app-logs.js";
|
|
12
|
+
import { createAppModule } from "./modules/app.js";
|
|
12
13
|
import { createUsersModule } from "./modules/users.js";
|
|
13
14
|
import { RoomsSocket } from "./utils/socket-utils.js";
|
|
14
15
|
import { createAnalyticsModule } from "./modules/analytics.js";
|
|
@@ -51,8 +52,8 @@ import { createActorsModule, resolveActorsHost, } from "./modules/actors.js";
|
|
|
51
52
|
* ```
|
|
52
53
|
*/
|
|
53
54
|
export function createClient(config) {
|
|
54
|
-
var _a, _b, _c;
|
|
55
|
-
const { serverUrl = "https://base44.app", appId, token, serviceToken, requiresAuth = false, appBaseUrl, options,
|
|
55
|
+
var _a, _b, _c, _d;
|
|
56
|
+
const { serverUrl = "https://base44.app", appId, analytics, token, serviceToken, requiresAuth = false, appBaseUrl, options, functionsVersion, headers: optionalHeaders, } = config;
|
|
56
57
|
// Normalize appBaseUrl to always be a string (empty if not provided or invalid)
|
|
57
58
|
const normalizedAppBaseUrl = typeof appBaseUrl === "string" ? appBaseUrl : "";
|
|
58
59
|
const socketConfig = {
|
|
@@ -112,7 +113,7 @@ export function createClient(config) {
|
|
|
112
113
|
});
|
|
113
114
|
// Dedicated client for actor connection-token mints: no onError (a legacy
|
|
114
115
|
// actor answers every mint with an expected 409 before the proxy fallback,
|
|
115
|
-
// which must not reach the app's error handler
|
|
116
|
+
// which must not reach the app's error handler. The actors module forwards
|
|
116
117
|
// genuine failures itself via onMintError) and no constructor token
|
|
117
118
|
// (auth is per-request so a login/logout is picked up on every reconnect).
|
|
118
119
|
const actorsAxiosClient = createAxiosClient({
|
|
@@ -187,13 +188,14 @@ export function createClient(config) {
|
|
|
187
188
|
}),
|
|
188
189
|
aiGateway: createAiGatewayModule({ serverUrl, token, appId }),
|
|
189
190
|
appLogs: createAppLogsModule(axiosClient, appId),
|
|
191
|
+
app: createAppModule(axiosClient, appId),
|
|
190
192
|
users: createUsersModule(axiosClient, appId),
|
|
191
193
|
analytics: createAnalyticsModule({
|
|
192
194
|
axiosClient,
|
|
193
195
|
serverUrl,
|
|
194
196
|
appId,
|
|
195
197
|
userAuthModule,
|
|
196
|
-
|
|
198
|
+
enabled: (_c = analytics === null || analytics === void 0 ? void 0 : analytics.enabled) !== null && _c !== void 0 ? _c : true,
|
|
197
199
|
}),
|
|
198
200
|
actors: actorsModule.module,
|
|
199
201
|
cleanup: () => {
|
|
@@ -222,7 +224,7 @@ export function createClient(config) {
|
|
|
222
224
|
}
|
|
223
225
|
return headers;
|
|
224
226
|
},
|
|
225
|
-
baseURL: (
|
|
227
|
+
baseURL: (_d = serviceRoleFunctionsAxiosClient.defaults) === null || _d === void 0 ? void 0 : _d.baseURL,
|
|
226
228
|
}),
|
|
227
229
|
agents: createAgentsModule({
|
|
228
230
|
axios: serviceRoleAxiosClient,
|
package/dist/client.types.d.ts
CHANGED
|
@@ -7,7 +7,8 @@ import type { FunctionsModule } from "./modules/functions.types.js";
|
|
|
7
7
|
import type { AgentsModule } from "./modules/agents.types.js";
|
|
8
8
|
import type { AiGatewayModule } from "./modules/ai-gateway.types.js";
|
|
9
9
|
import type { AppLogsModule } from "./modules/app-logs.types.js";
|
|
10
|
-
import type {
|
|
10
|
+
import type { AppModule } from "./modules/app.types.js";
|
|
11
|
+
import type { AnalyticsModule } from "./modules/analytics.types.js";
|
|
11
12
|
import type { ActorsModule } from "./modules/actors.types.js";
|
|
12
13
|
/**
|
|
13
14
|
* Options for creating a Base44 client.
|
|
@@ -17,19 +18,34 @@ export interface CreateClientOptions {
|
|
|
17
18
|
* Optional error handler that will be called whenever an API error occurs.
|
|
18
19
|
*
|
|
19
20
|
* Also receives {@link ActorsModule | actors} connection failures. Errors
|
|
20
|
-
* are usually {@linkcode Base44Error} instances
|
|
21
|
+
* are usually {@linkcode Base44Error} instances, so check `error.status`.
|
|
21
22
|
*/
|
|
22
23
|
onError?: (error: Error) => void;
|
|
23
24
|
/**
|
|
24
25
|
* Forces the actors transport. `"auto"` (default) connects directly to the
|
|
25
26
|
* actor and falls back to the platform proxy when the app's actors don't
|
|
26
27
|
* support direct connections; `"proxy"` always uses the platform proxy
|
|
27
|
-
* (ops rollback
|
|
28
|
+
* (ops rollback, with no connection-token calls); `"direct"` disables the
|
|
28
29
|
* fallback (validation environments).
|
|
29
30
|
* @internal
|
|
30
31
|
*/
|
|
31
32
|
actorsTransport?: "auto" | "proxy" | "direct";
|
|
32
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Configuration for the SDK's app analytics module.
|
|
36
|
+
*/
|
|
37
|
+
export interface CreateClientAnalyticsConfig {
|
|
38
|
+
/**
|
|
39
|
+
* Whether app analytics is enabled for this client.
|
|
40
|
+
*
|
|
41
|
+
* When disabled, automatic analytics and calls to `analytics.track()` are
|
|
42
|
+
* no-ops. The SDK does not create an analytics session identifier, start
|
|
43
|
+
* heartbeat timers, or send analytics requests.
|
|
44
|
+
*
|
|
45
|
+
* @defaultValue `true`
|
|
46
|
+
*/
|
|
47
|
+
enabled: boolean;
|
|
48
|
+
}
|
|
33
49
|
/**
|
|
34
50
|
* Configuration for creating a Base44 client.
|
|
35
51
|
*/
|
|
@@ -56,6 +72,12 @@ export interface CreateClientConfig {
|
|
|
56
72
|
* It's the string between `/apps/` and `/editor/`.
|
|
57
73
|
*/
|
|
58
74
|
appId: string;
|
|
75
|
+
/**
|
|
76
|
+
* Controls app analytics for this client.
|
|
77
|
+
*
|
|
78
|
+
* Omit this option to preserve the default analytics behavior.
|
|
79
|
+
*/
|
|
80
|
+
analytics?: CreateClientAnalyticsConfig;
|
|
59
81
|
/**
|
|
60
82
|
* User authentication token. Used to authenticate as a specific user.
|
|
61
83
|
*
|
|
@@ -82,22 +104,6 @@ export interface CreateClientConfig {
|
|
|
82
104
|
* @internal
|
|
83
105
|
*/
|
|
84
106
|
headers?: Record<string, string>;
|
|
85
|
-
/**
|
|
86
|
-
* Analytics configuration for this client.
|
|
87
|
-
*
|
|
88
|
-
* By default, analytics is enabled and starts as soon as the client is created: a persistent visitor ID is stored in `localStorage` and automatic events are sent.
|
|
89
|
-
*
|
|
90
|
-
* Set `consent: "pending"` to keep analytics dormant until the visitor makes a consent decision, then call {@linkcode AnalyticsModule.optIn | analytics.optIn()} or {@linkcode AnalyticsModule.optOut | analytics.optOut()}. Set `enabled: false` to turn the analytics module off entirely.
|
|
91
|
-
*
|
|
92
|
-
* @example
|
|
93
|
-
* ```typescript
|
|
94
|
-
* const base44 = createClient({
|
|
95
|
-
* appId: 'my-app-id',
|
|
96
|
-
* analytics: { consent: 'pending' }
|
|
97
|
-
* });
|
|
98
|
-
* ```
|
|
99
|
-
*/
|
|
100
|
-
analytics?: CreateClientAnalyticsOptions;
|
|
101
107
|
/**
|
|
102
108
|
* Additional client options.
|
|
103
109
|
*/
|
|
@@ -117,6 +123,8 @@ export interface Base44Client {
|
|
|
117
123
|
analytics: AnalyticsModule;
|
|
118
124
|
/** {@link AppLogsModule | App logs module} for tracking app usage. */
|
|
119
125
|
appLogs: AppLogsModule;
|
|
126
|
+
/** {@link AppModule | App module} for reading the app's own public configuration. */
|
|
127
|
+
app: AppModule;
|
|
120
128
|
/** {@link ActorsModule | Actors module} for subscribing to and sending messages via Cloudflare Durable Object-backed Actors. */
|
|
121
129
|
actors: ActorsModule;
|
|
122
130
|
/** {@link AuthModule | Auth module} for user authentication and management. */
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { createClient, createClientFromRequest, type Base44Client, type CreateClientConfig, type CreateClientOptions } from "./client.js";
|
|
1
|
+
import { createClient, createClientFromRequest, type Base44Client, type CreateClientAnalyticsConfig, type CreateClientConfig, type CreateClientOptions } from "./client.js";
|
|
2
2
|
import { Base44Error, type Base44ErrorJSON } from "./utils/axios-client.js";
|
|
3
3
|
import { getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl } from "./utils/auth-utils.js";
|
|
4
4
|
export { createClient, createClientFromRequest, Base44Error, getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl, };
|
|
5
|
-
export type { Base44Client, CreateClientConfig, CreateClientOptions, Base44ErrorJSON, };
|
|
5
|
+
export type { Base44Client, CreateClientAnalyticsConfig, CreateClientConfig, CreateClientOptions, Base44ErrorJSON, };
|
|
6
6
|
export * from "./types.js";
|
|
7
7
|
export type { DeleteManyResult, DeleteResult, EntitiesModule, EntityFilterOperators, EntityFilterQuery, EntityFilterValue, EntityHandler, EntityRecord, EntityTypeRegistry, 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";
|
|
@@ -11,6 +11,7 @@ export type { FunctionsModule, FunctionName, FunctionNameRegistry, } from "./mod
|
|
|
11
11
|
export type { AgentsModule, AgentName, AgentNameRegistry, AgentConversation, AgentMessage, AgentMessageReasoning, AgentMessageToolCall, AgentMessageUsage, AgentMessageCustomContext, AgentMessageMetadata, CreateConversationParams, } from "./modules/agents.types.js";
|
|
12
12
|
export type { AiGatewayModule, AiGatewayConnection, } from "./modules/ai-gateway.types.js";
|
|
13
13
|
export type { AppLogsModule } from "./modules/app-logs.types.js";
|
|
14
|
+
export type { AppModule, AppPublicSettings, AppPublicSettingsResponse, } from "./modules/app.types.js";
|
|
14
15
|
export type { ActorsModule, ActorClient, ActorRef, Connection, ActorSubscription, ActorConnectOptions, ActorNameRegistry, ActorRegistry, } from "./modules/actors.types.js";
|
|
15
16
|
export type { SsoModule, SsoAccessTokenResponse } from "./modules/sso.types.js";
|
|
16
17
|
export { Actor, type Conn } from "./actor.js";
|
package/dist/modules/actors.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ interface ActorsConfig {
|
|
|
35
35
|
* The legacy platform-proxy URL, byte-for-byte what PartySocket built before
|
|
36
36
|
* the direct path existed: same scheme swap (including its localhost-needs-a-
|
|
37
37
|
* port quirk), case-preserved party segment, `_pk` first in the query. The
|
|
38
|
-
* `handler` param is load-bearing
|
|
38
|
+
* `handler` param is load-bearing, because the proxy reads it for the actor name.
|
|
39
39
|
*/
|
|
40
40
|
export declare function buildProxyActorUrl(rawHost: string, actorName: string, instanceId: string, connectionId: string, appId: string, token: string | null | undefined, functionsVersion?: string): string;
|
|
41
41
|
/**
|
package/dist/modules/actors.js
CHANGED
|
@@ -8,12 +8,12 @@ const DEAD_MS = 3000;
|
|
|
8
8
|
// 422 = no principal (e.g. anonymous outside a browser) or an id/room only the
|
|
9
9
|
// proxy's looser validation accepts, 405 = a backend that predates the mint
|
|
10
10
|
// endpoint (its actor deploy routes catch the path via `{handler_name:path}`
|
|
11
|
-
// but not the POST method
|
|
11
|
+
// but not the POST method, and the real endpoint never 405s a POST). The
|
|
12
12
|
// proxy serves migrated actors too, so falling back is always safe.
|
|
13
13
|
const PROXY_FALLBACK_STATUSES = new Set([405, 409, 422, 503]);
|
|
14
14
|
// Mint responses no retry can fix (bad request / forbidden / not found): the
|
|
15
15
|
// connection closes instead of re-minting forever; a fresh connect() re-probes.
|
|
16
|
-
// 401 is deliberately absent
|
|
16
|
+
// 401 is deliberately absent. The auth token is re-read on every attempt, so a
|
|
17
17
|
// login recovers on the next retry. Disjoint from PROXY_FALLBACK_STATUSES.
|
|
18
18
|
const TERMINAL_MINT_STATUSES = new Set([400, 403, 404]);
|
|
19
19
|
/** The mint's rejection can be anything; a `Base44Error` carries a numeric
|
|
@@ -29,7 +29,7 @@ function toError(err) {
|
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
31
|
* A live connection to an actor instance. Only obtainable from
|
|
32
|
-
* {@link ActorRef.connect}, so `subscribe`/`send` are always valid
|
|
32
|
+
* {@link ActorRef.connect}, so `subscribe`/`send` are always valid. The socket
|
|
33
33
|
* exists for this object's whole lifetime.
|
|
34
34
|
*/
|
|
35
35
|
class Connection {
|
|
@@ -44,7 +44,7 @@ class Connection {
|
|
|
44
44
|
// mint answers with a fallback status the choice is sticky for this
|
|
45
45
|
// socket's lifetime (a fresh connect() after close() probes direct again,
|
|
46
46
|
// picking up actors migrated in the meantime). Any other mint failure
|
|
47
|
-
// rejects, which ReconnectingWebSocket retries with backoff
|
|
47
|
+
// rejects, which ReconnectingWebSocket retries with backoff, except the
|
|
48
48
|
// terminal statuses, which close this connection for good.
|
|
49
49
|
let useProxy = config.transport === "proxy";
|
|
50
50
|
const urlProvider = async () => {
|
|
@@ -172,7 +172,7 @@ function makeActorRef(actorName, instanceId, config, connections) {
|
|
|
172
172
|
* The legacy platform-proxy URL, byte-for-byte what PartySocket built before
|
|
173
173
|
* the direct path existed: same scheme swap (including its localhost-needs-a-
|
|
174
174
|
* port quirk), case-preserved party segment, `_pk` first in the query. The
|
|
175
|
-
* `handler` param is load-bearing
|
|
175
|
+
* `handler` param is load-bearing, because the proxy reads it for the actor name.
|
|
176
176
|
*/
|
|
177
177
|
export function buildProxyActorUrl(rawHost, actorName, instanceId, connectionId, appId, token, functionsVersion) {
|
|
178
178
|
let host = rawHost.replace(/^(http|https|ws|wss):\/\//, "");
|
|
@@ -21,7 +21,7 @@ export interface ActorRegistry {
|
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
23
|
* Auto-populated by `base44 types generate` with the names of your deployed actors.
|
|
24
|
-
* Do not edit this interface manually
|
|
24
|
+
* Do not edit this interface manually. Use {@link ActorRegistry} for message types.
|
|
25
25
|
*/
|
|
26
26
|
export interface ActorNameRegistry {
|
|
27
27
|
}
|
|
@@ -35,7 +35,7 @@ type ToServerFor<N extends string> = N extends keyof ActorRegistry ? ActorRegist
|
|
|
35
35
|
/** Options for {@link ActorRef.connect}. */
|
|
36
36
|
export interface ActorConnectOptions {
|
|
37
37
|
/**
|
|
38
|
-
* The connection id
|
|
38
|
+
* The connection id, which becomes the actor's `conn.id`. Supply a stable value
|
|
39
39
|
* (e.g. persisted per tab) so a reconnect reuses the same server-side
|
|
40
40
|
* identity; omit for an auto-generated per-connection id.
|
|
41
41
|
*/
|
|
@@ -48,7 +48,7 @@ export interface ActorSubscription {
|
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
50
|
* A live connection to an actor instance, returned by {@link ActorRef.connect}.
|
|
51
|
-
* `subscribe`/`send` are always valid
|
|
51
|
+
* `subscribe`/`send` are always valid. You only get a `Connection` once the
|
|
52
52
|
* socket has been opened, so there's no pre-connect state to guard against.
|
|
53
53
|
*/
|
|
54
54
|
export interface Connection<N extends string = string> {
|
|
@@ -61,13 +61,13 @@ export interface Connection<N extends string = string> {
|
|
|
61
61
|
send(data: ToServerFor<N>): void;
|
|
62
62
|
/**
|
|
63
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
|
|
64
|
+
* than once. A connection also closes itself when it fails permanently.
|
|
65
65
|
* see {@link ActorRef.connect}.
|
|
66
66
|
*/
|
|
67
67
|
close(): void;
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
70
|
-
* A handle to one actor instance
|
|
70
|
+
* A handle to one actor instance, obtained from `base44.actors.MyActor(id)`. Call
|
|
71
71
|
* {@link connect} to open the socket and get a {@link Connection}.
|
|
72
72
|
*/
|
|
73
73
|
export interface ActorRef<N extends string = string> {
|
|
@@ -83,7 +83,7 @@ export interface ActorRef<N extends string = string> {
|
|
|
83
83
|
connect(options?: ActorConnectOptions): Connection<N>;
|
|
84
84
|
}
|
|
85
85
|
/**
|
|
86
|
-
* Client for a single named Actor
|
|
86
|
+
* Client for a single named Actor. Call it with an instance id to get an
|
|
87
87
|
* {@link ActorRef}. Typed automatically when the actor is registered in
|
|
88
88
|
* {@link ActorRegistry}.
|
|
89
89
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AxiosInstance } from "axios";
|
|
2
|
-
import { TrackEventParams, AnalyticsModuleOptions
|
|
2
|
+
import { TrackEventParams, AnalyticsModuleOptions } from "./analytics.types";
|
|
3
3
|
import type { InternalAuthModule } from "./auth.types";
|
|
4
4
|
export declare const USER_HEARTBEAT_EVENT_NAME = "__user_heartbeat_event__";
|
|
5
5
|
export declare const ANALYTICS_INITIALIZATION_EVENT_NAME = "__initialization_event__";
|
|
@@ -11,20 +11,10 @@ export interface AnalyticsModuleArgs {
|
|
|
11
11
|
serverUrl: string;
|
|
12
12
|
appId: string;
|
|
13
13
|
userAuthModule: InternalAuthModule;
|
|
14
|
-
|
|
14
|
+
enabled: boolean;
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
* The effective analytics consent status. `"granted"` when no client set one
|
|
18
|
-
* explicitly, preserving the legacy always-on behavior.
|
|
19
|
-
*
|
|
20
|
-
* @internal
|
|
21
|
-
*/
|
|
22
|
-
export declare function getAnalyticsConsentStatus(): AnalyticsConsentStatus;
|
|
23
|
-
export declare const createAnalyticsModule: ({ axiosClient, serverUrl, appId, userAuthModule, options, }: AnalyticsModuleArgs) => {
|
|
16
|
+
export declare const createAnalyticsModule: ({ axiosClient, serverUrl, appId, userAuthModule, enabled, }: AnalyticsModuleArgs) => {
|
|
24
17
|
track: (params: TrackEventParams) => void;
|
|
25
|
-
optIn: () => void;
|
|
26
|
-
optOut: () => void;
|
|
27
|
-
getConsentStatus: typeof getAnalyticsConsentStatus;
|
|
28
18
|
cleanup: () => void;
|
|
29
19
|
};
|
|
30
20
|
/**
|
|
@@ -25,84 +25,25 @@ const analyticsSharedState = getSharedInstance(ANALYTICS_SHARED_STATE_NAME, () =
|
|
|
25
25
|
wasInitializationTracked: false,
|
|
26
26
|
sessionContext: null,
|
|
27
27
|
sessionStartTime: null,
|
|
28
|
-
// Memoized session id for when `localStorage` can't persist one
|
|
28
|
+
// Memoized session id for when `localStorage` can't persist one. See
|
|
29
29
|
// getAnalyticsSessionId.
|
|
30
30
|
fallbackSessionId: null,
|
|
31
|
-
// Consent status shared by every client on the page. `null` means no
|
|
32
|
-
// client set one explicitly, which keeps the legacy behavior (granted).
|
|
33
|
-
consent: null,
|
|
34
31
|
config: {
|
|
35
32
|
...defaultConfiguration,
|
|
36
33
|
...getAnalyticsConfigFromUrlParams(),
|
|
37
34
|
},
|
|
38
35
|
}));
|
|
39
|
-
|
|
40
|
-
// multiple clients created on the same page: the shared state (and therefore
|
|
41
|
-
// the shared persistent id) can only honor one status, so the most
|
|
42
|
-
// restrictive explicitly-configured one wins.
|
|
43
|
-
const CONSENT_RESTRICTIVENESS = {
|
|
44
|
-
denied: 0,
|
|
45
|
-
pending: 1,
|
|
46
|
-
granted: 2,
|
|
47
|
-
};
|
|
48
|
-
function applyInitialConsent(consent) {
|
|
49
|
-
if (!consent)
|
|
50
|
-
return;
|
|
51
|
-
const current = analyticsSharedState.consent;
|
|
52
|
-
if (current === null ||
|
|
53
|
-
CONSENT_RESTRICTIVENESS[consent] < CONSENT_RESTRICTIVENESS[current]) {
|
|
54
|
-
analyticsSharedState.consent = consent;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* The effective analytics consent status. `"granted"` when no client set one
|
|
59
|
-
* explicitly, preserving the legacy always-on behavior.
|
|
60
|
-
*
|
|
61
|
-
* @internal
|
|
62
|
-
*/
|
|
63
|
-
export function getAnalyticsConsentStatus() {
|
|
64
|
-
var _a;
|
|
65
|
-
return (_a = analyticsSharedState.consent) !== null && _a !== void 0 ? _a : "granted";
|
|
66
|
-
}
|
|
67
|
-
function clearPersistedAnalyticsSessionId() {
|
|
68
|
-
if (typeof window === "undefined")
|
|
69
|
-
return;
|
|
70
|
-
try {
|
|
71
|
-
localStorage.removeItem(ANALYTICS_SESSION_ID_LOCAL_STORAGE_KEY);
|
|
72
|
-
}
|
|
73
|
-
catch (_a) {
|
|
74
|
-
// Storage unavailable — nothing was persisted, so nothing to clear.
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
export const createAnalyticsModule = ({ axiosClient, serverUrl, appId, userAuthModule, options, }) => {
|
|
36
|
+
export const createAnalyticsModule = ({ axiosClient, serverUrl, appId, userAuthModule, enabled, }) => {
|
|
78
37
|
var _a;
|
|
79
|
-
// Consent gates more than this module: getAnalyticsSessionId() also backs
|
|
80
|
-
// the anonymous-id HTTP header and the socket handshake, so the client's
|
|
81
|
-
// consent choice must be recorded even when the early returns below make
|
|
82
|
-
// the module itself a no-op.
|
|
83
|
-
applyInitialConsent(options === null || options === void 0 ? void 0 : options.consent);
|
|
84
38
|
// prevent overflow of events //
|
|
85
39
|
const { maxQueueSize, throttleTime, batchSize } = analyticsSharedState.config;
|
|
86
40
|
// Disable analytics on React Native. It defines `window` but not `document`,
|
|
87
41
|
// so the per-callsite `typeof window` guards below aren't enough to keep it
|
|
88
42
|
// from touching `document` (e.g. `document.referrer` on init). Node/SSR is
|
|
89
43
|
// still handled by those `window` guards, so this doesn't affect it.
|
|
90
|
-
if (!((_a = analyticsSharedState.config) === null || _a === void 0 ? void 0 : _a.enabled) ||
|
|
91
|
-
(options === null || options === void 0 ? void 0 : options.enabled) === false ||
|
|
92
|
-
isReactNative) {
|
|
44
|
+
if (!enabled || !((_a = analyticsSharedState.config) === null || _a === void 0 ? void 0 : _a.enabled) || isReactNative) {
|
|
93
45
|
return {
|
|
94
46
|
track: () => { },
|
|
95
|
-
// Consent still matters with the event pipeline off: it decides whether
|
|
96
|
-
// the persistent id may back the anonymous-id header and socket
|
|
97
|
-
// handshake, so opting in/out has to work here too.
|
|
98
|
-
optIn: () => {
|
|
99
|
-
analyticsSharedState.consent = "granted";
|
|
100
|
-
},
|
|
101
|
-
optOut: () => {
|
|
102
|
-
analyticsSharedState.consent = "denied";
|
|
103
|
-
clearPersistedAnalyticsSessionId();
|
|
104
|
-
},
|
|
105
|
-
getConsentStatus: getAnalyticsConsentStatus,
|
|
106
47
|
cleanup: () => { },
|
|
107
48
|
};
|
|
108
49
|
}
|
|
@@ -149,12 +90,6 @@ export const createAnalyticsModule = ({ axiosClient, serverUrl, appId, userAuthM
|
|
|
149
90
|
});
|
|
150
91
|
};
|
|
151
92
|
const track = (params) => {
|
|
152
|
-
const consent = getAnalyticsConsentStatus();
|
|
153
|
-
// Denied: drop. Pending: buffer in memory (no network, no storage) so the
|
|
154
|
-
// events can be delivered if the visitor opts in later.
|
|
155
|
-
if (consent === "denied") {
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
93
|
if (analyticsSharedState.requestsQueue.length >= maxQueueSize) {
|
|
159
94
|
return;
|
|
160
95
|
}
|
|
@@ -163,9 +98,7 @@ export const createAnalyticsModule = ({ axiosClient, serverUrl, appId, userAuthM
|
|
|
163
98
|
...params,
|
|
164
99
|
...intrinsicData,
|
|
165
100
|
});
|
|
166
|
-
|
|
167
|
-
startProcessing();
|
|
168
|
-
}
|
|
101
|
+
startProcessing();
|
|
169
102
|
};
|
|
170
103
|
const onDocVisible = () => {
|
|
171
104
|
startAnalyticsProcessor(flush, {
|
|
@@ -193,66 +126,25 @@ export const createAnalyticsModule = ({ axiosClient, serverUrl, appId, userAuthM
|
|
|
193
126
|
onDocVisible();
|
|
194
127
|
}
|
|
195
128
|
};
|
|
196
|
-
|
|
197
|
-
// automatic events, timers, network — starts in activate(), so a client
|
|
198
|
-
// created with consent "pending" or "denied" stays fully dormant until the
|
|
199
|
-
// visitor opts in.
|
|
200
|
-
let isActive = false;
|
|
201
|
-
const activate = () => {
|
|
202
|
-
if (isActive)
|
|
203
|
-
return;
|
|
204
|
-
isActive = true;
|
|
205
|
-
// start the flusing process ///
|
|
206
|
-
startProcessing();
|
|
207
|
-
// start the heart beat processor //
|
|
208
|
-
clearHeartBeatProcessor = startHeartBeatProcessor(track);
|
|
209
|
-
// track the referrer event //
|
|
210
|
-
trackInitializationEvent(track);
|
|
211
|
-
// start the visibility change listener //
|
|
212
|
-
if (typeof window !== "undefined") {
|
|
213
|
-
window.addEventListener("visibilitychange", onVisibilityChange);
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
const deactivate = () => {
|
|
217
|
-
if (!isActive)
|
|
218
|
-
return;
|
|
219
|
-
isActive = false;
|
|
129
|
+
const cleanup = () => {
|
|
220
130
|
stopAnalyticsProcessor();
|
|
221
131
|
clearHeartBeatProcessor === null || clearHeartBeatProcessor === void 0 ? void 0 : clearHeartBeatProcessor();
|
|
222
|
-
clearHeartBeatProcessor = undefined;
|
|
223
132
|
if (typeof window !== "undefined") {
|
|
224
133
|
window.removeEventListener("visibilitychange", onVisibilityChange);
|
|
225
134
|
}
|
|
226
135
|
};
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
analyticsSharedState.consent = "denied";
|
|
237
|
-
deactivate();
|
|
238
|
-
// Drop anything buffered while consent was pending, and forget the
|
|
239
|
-
// identity: both the persisted id and the memoized session context.
|
|
240
|
-
analyticsSharedState.requestsQueue.length = 0;
|
|
241
|
-
analyticsSharedState.sessionStartTime = null;
|
|
242
|
-
resetAnalyticsSessionContext();
|
|
243
|
-
clearPersistedAnalyticsSessionId();
|
|
244
|
-
};
|
|
245
|
-
const cleanup = () => {
|
|
246
|
-
deactivate();
|
|
247
|
-
};
|
|
248
|
-
if (getAnalyticsConsentStatus() === "granted") {
|
|
249
|
-
activate();
|
|
136
|
+
// start the flusing process ///
|
|
137
|
+
startProcessing();
|
|
138
|
+
// start the heart beat processor //
|
|
139
|
+
clearHeartBeatProcessor = startHeartBeatProcessor(track);
|
|
140
|
+
// track the referrer event //
|
|
141
|
+
trackInitializationEvent(track);
|
|
142
|
+
// start the visibility change listener //
|
|
143
|
+
if (typeof window !== "undefined") {
|
|
144
|
+
window.addEventListener("visibilitychange", onVisibilityChange);
|
|
250
145
|
}
|
|
251
146
|
return {
|
|
252
147
|
track,
|
|
253
|
-
optIn,
|
|
254
|
-
optOut,
|
|
255
|
-
getConsentStatus: getAnalyticsConsentStatus,
|
|
256
148
|
cleanup,
|
|
257
149
|
};
|
|
258
150
|
};
|
|
@@ -363,7 +255,7 @@ async function getSessionContext(userAuthModule) {
|
|
|
363
255
|
// With no token there is no identity to resolve: `me()` can only answer 401,
|
|
364
256
|
// which the browser logs to the console before any handler here sees it. On
|
|
365
257
|
// a public page that request is the sole reason an error appears, so skip
|
|
366
|
-
// it. This is not memoized
|
|
258
|
+
// it. This is not memoized, because a visitor who logs in later must still resolve.
|
|
367
259
|
if (!userAuthModule.hasToken()) {
|
|
368
260
|
return { user_id: null, session_id: getAnalyticsSessionId() };
|
|
369
261
|
}
|
|
@@ -422,29 +314,19 @@ function getFallbackSessionId() {
|
|
|
422
314
|
return ((_a = analyticsSharedState.fallbackSessionId) !== null && _a !== void 0 ? _a : (analyticsSharedState.fallbackSessionId = generateUuid()));
|
|
423
315
|
}
|
|
424
316
|
export function getAnalyticsSessionId() {
|
|
425
|
-
var _a;
|
|
426
317
|
if (typeof window === "undefined") {
|
|
427
318
|
return getFallbackSessionId();
|
|
428
319
|
}
|
|
429
|
-
// Until consent is granted, never read or write the persistent id — hand out
|
|
430
|
-
// a per-page-load ephemeral id instead. The anonymous-id HTTP header and the
|
|
431
|
-
// socket handshake resolve their id through here too, so this single gate
|
|
432
|
-
// covers every place a persistent identifier could be minted pre-consent.
|
|
433
|
-
if (getAnalyticsConsentStatus() !== "granted") {
|
|
434
|
-
return getFallbackSessionId();
|
|
435
|
-
}
|
|
436
320
|
try {
|
|
437
321
|
const sessionId = localStorage.getItem(ANALYTICS_SESSION_ID_LOCAL_STORAGE_KEY);
|
|
438
322
|
if (!sessionId) {
|
|
439
|
-
|
|
440
|
-
// visitor keeps a single identity across the consent grant.
|
|
441
|
-
const newSessionId = (_a = analyticsSharedState.fallbackSessionId) !== null && _a !== void 0 ? _a : generateUuid();
|
|
323
|
+
const newSessionId = generateUuid();
|
|
442
324
|
localStorage.setItem(ANALYTICS_SESSION_ID_LOCAL_STORAGE_KEY, newSessionId);
|
|
443
325
|
return newSessionId;
|
|
444
326
|
}
|
|
445
327
|
return sessionId;
|
|
446
328
|
}
|
|
447
|
-
catch (
|
|
329
|
+
catch (_a) {
|
|
448
330
|
return getFallbackSessionId();
|
|
449
331
|
}
|
|
450
332
|
}
|
|
@@ -67,41 +67,6 @@ export type AnalyticsModuleOptions = {
|
|
|
67
67
|
batchSize?: number;
|
|
68
68
|
heartBeatInterval?: number;
|
|
69
69
|
};
|
|
70
|
-
/**
|
|
71
|
-
* Consent status for analytics tracking.
|
|
72
|
-
*
|
|
73
|
-
* - `"granted"`: Analytics is fully active. The SDK persists a visitor ID in `localStorage`, sends automatic events, and delivers tracked events to the server.
|
|
74
|
-
* - `"pending"`: Analytics is dormant while waiting for a consent decision. No visitor ID is persisted and nothing is sent to the server. Events passed to {@linkcode AnalyticsModule.track | track()} are buffered in memory and delivered if consent is later granted with {@linkcode AnalyticsModule.optIn | optIn()}.
|
|
75
|
-
* - `"denied"`: Analytics is off. No visitor ID is persisted, nothing is sent to the server, and tracked events are discarded.
|
|
76
|
-
*/
|
|
77
|
-
export type AnalyticsConsentStatus = "granted" | "denied" | "pending";
|
|
78
|
-
/**
|
|
79
|
-
* Analytics configuration for {@linkcode createClient | createClient()}.
|
|
80
|
-
*
|
|
81
|
-
* Controls whether analytics runs and whether it waits for a consent decision before persisting a visitor ID or sending events.
|
|
82
|
-
*/
|
|
83
|
-
export type CreateClientAnalyticsOptions = {
|
|
84
|
-
/**
|
|
85
|
-
* Whether the analytics module is enabled.
|
|
86
|
-
*
|
|
87
|
-
* When `false`, {@linkcode AnalyticsModule.track | track()} is a no-op and no automatic events are sent, regardless of consent status.
|
|
88
|
-
*
|
|
89
|
-
* @defaultValue `true`
|
|
90
|
-
*/
|
|
91
|
-
enabled?: boolean;
|
|
92
|
-
/**
|
|
93
|
-
* Initial consent status for analytics.
|
|
94
|
-
*
|
|
95
|
-
* Defaults to `"granted"`, which preserves the SDK's original behavior: analytics starts as soon as the client is created.
|
|
96
|
-
*
|
|
97
|
-
* Set to `"pending"` when your app needs a consent decision (for example, from a cookie banner) before tracking starts. The client is still created immediately, but analytics stays dormant — no visitor ID is written to `localStorage`, no automatic events fire, and tracked events are buffered in memory. Call {@linkcode AnalyticsModule.optIn | optIn()} once consent is granted, or {@linkcode AnalyticsModule.optOut | optOut()} if it's refused.
|
|
98
|
-
*
|
|
99
|
-
* When multiple clients are created on the same page, the most restrictive explicitly-configured consent status wins (`"denied"` over `"pending"` over `"granted"`).
|
|
100
|
-
*
|
|
101
|
-
* @defaultValue `"granted"`
|
|
102
|
-
*/
|
|
103
|
-
consent?: AnalyticsConsentStatus;
|
|
104
|
-
};
|
|
105
70
|
/**
|
|
106
71
|
* Analytics module for tracking custom events in your app.
|
|
107
72
|
*
|
|
@@ -116,10 +81,6 @@ export type CreateClientAnalyticsOptions = {
|
|
|
116
81
|
* - Choose clear, descriptive event names in snake_case like `signup_button_click` or `purchase_completed` rather than generic names like `click`.
|
|
117
82
|
* - Include relevant context in your properties such as identifiers like `product_id`, measurements like `price`, and flags like `is_first_purchase`.
|
|
118
83
|
*
|
|
119
|
-
* ## Consent
|
|
120
|
-
*
|
|
121
|
-
* Apps that need a consent decision (for example, from a cookie banner) before tracking starts can create the client with `analytics: { consent: 'pending' }` and then call {@linkcode optIn | optIn()} or {@linkcode optOut | optOut()} once the visitor decides. See {@linkcode CreateClientAnalyticsOptions} for details.
|
|
122
|
-
*
|
|
123
84
|
* ## Authentication Modes
|
|
124
85
|
*
|
|
125
86
|
* This module is only available in user authentication mode (`base44.analytics`).
|
|
@@ -158,56 +119,4 @@ export interface AnalyticsModule {
|
|
|
158
119
|
* ```
|
|
159
120
|
*/
|
|
160
121
|
track(params: TrackEventParams): void;
|
|
161
|
-
/**
|
|
162
|
-
* Grants analytics consent and activates tracking.
|
|
163
|
-
*
|
|
164
|
-
* Use this after the visitor accepts analytics in your consent flow (for example, a cookie banner). It sets the consent status to `"granted"`, persists the visitor ID, starts automatic events, and delivers any events buffered while consent was `"pending"`.
|
|
165
|
-
*
|
|
166
|
-
* The visitor keeps a single identity across the consent grant: the temporary in-memory ID used before consent is adopted as the persistent ID.
|
|
167
|
-
*
|
|
168
|
-
* Calling this when consent is already `"granted"` has no effect.
|
|
169
|
-
*
|
|
170
|
-
* @example
|
|
171
|
-
* ```typescript
|
|
172
|
-
* // Create the client without tracking, then activate it once the
|
|
173
|
-
* // visitor accepts analytics in your consent banner.
|
|
174
|
-
* const base44 = createClient({
|
|
175
|
-
* appId: 'my-app-id',
|
|
176
|
-
* analytics: { consent: 'pending' }
|
|
177
|
-
* });
|
|
178
|
-
*
|
|
179
|
-
* onConsentBannerAccept(() => {
|
|
180
|
-
* base44.analytics.optIn();
|
|
181
|
-
* });
|
|
182
|
-
* ```
|
|
183
|
-
*/
|
|
184
|
-
optIn(): void;
|
|
185
|
-
/**
|
|
186
|
-
* Revokes analytics consent and deactivates tracking.
|
|
187
|
-
*
|
|
188
|
-
* Use this when the visitor declines analytics in your consent flow, or withdraws consent later. It sets the consent status to `"denied"`, stops automatic events, discards any buffered events, and removes the persistent visitor ID from `localStorage`.
|
|
189
|
-
*
|
|
190
|
-
* Tracking can be re-enabled later with {@linkcode optIn | optIn()}.
|
|
191
|
-
*
|
|
192
|
-
* @example
|
|
193
|
-
* ```typescript
|
|
194
|
-
* onConsentBannerDecline(() => {
|
|
195
|
-
* base44.analytics.optOut();
|
|
196
|
-
* });
|
|
197
|
-
* ```
|
|
198
|
-
*/
|
|
199
|
-
optOut(): void;
|
|
200
|
-
/**
|
|
201
|
-
* Gets the current analytics consent status.
|
|
202
|
-
*
|
|
203
|
-
* @returns The current consent status: `"granted"`, `"denied"`, or `"pending"`.
|
|
204
|
-
*
|
|
205
|
-
* @example
|
|
206
|
-
* ```typescript
|
|
207
|
-
* if (base44.analytics.getConsentStatus() === 'pending') {
|
|
208
|
-
* showConsentBanner();
|
|
209
|
-
* }
|
|
210
|
-
* ```
|
|
211
|
-
*/
|
|
212
|
-
getConsentStatus(): AnalyticsConsentStatus;
|
|
213
122
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AxiosInstance } from "axios";
|
|
2
|
+
import { AppModule } from "./app.types";
|
|
3
|
+
/**
|
|
4
|
+
* Creates the app module for the Base44 SDK.
|
|
5
|
+
*
|
|
6
|
+
* @param axios - Axios instance
|
|
7
|
+
* @param appId - Application ID
|
|
8
|
+
* @returns App module for reading the app's own configuration
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
export declare function createAppModule(axios: AxiosInstance, appId: string): AppModule;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates the app module for the Base44 SDK.
|
|
3
|
+
*
|
|
4
|
+
* @param axios - Axios instance
|
|
5
|
+
* @param appId - Application ID
|
|
6
|
+
* @returns App module for reading the app's own configuration
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export function createAppModule(axios, appId) {
|
|
10
|
+
return {
|
|
11
|
+
async getPublicSettings() {
|
|
12
|
+
return axios.get(`/apps/public/prod/public-settings/by-id/${appId}`);
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -1,3 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The app's access policy: whether the app is reachable without an account, and
|
|
3
|
+
* who may sign in.
|
|
4
|
+
*/
|
|
5
|
+
export type AppPublicSettings = "private_with_login" | "public_with_login" | "public_without_login" | "workspace_with_login" | string;
|
|
6
|
+
/**
|
|
7
|
+
* The app's public configuration, as returned by {@link AppModule.getPublicSettings}.
|
|
8
|
+
*/
|
|
9
|
+
export interface AppPublicSettingsResponse {
|
|
10
|
+
/** The app's ID. */
|
|
11
|
+
id: string;
|
|
12
|
+
/** The app's access policy. */
|
|
13
|
+
public_settings: AppPublicSettings;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* App module for reading the app's own public configuration.
|
|
17
|
+
*
|
|
18
|
+
* Use it to discover how the app is gated before rendering it, so a private app
|
|
19
|
+
* can send the visitor to login instead of rendering an empty shell.
|
|
20
|
+
*
|
|
21
|
+
* ## Authentication Modes
|
|
22
|
+
*
|
|
23
|
+
* This module is available to use with a client in all authentication modes. The
|
|
24
|
+
* client's token, when it has one, is sent with the request, so a signed-in visitor
|
|
25
|
+
* who has no access to the app is reported differently from an anonymous one.
|
|
26
|
+
*/
|
|
27
|
+
export interface AppModule {
|
|
28
|
+
/**
|
|
29
|
+
* Get the app's public configuration.
|
|
30
|
+
*
|
|
31
|
+
* Rejects with a {@linkcode Base44Error} when the visitor may not open the app:
|
|
32
|
+
* `status` is `403` and `data.extra_data.reason` says why. It is
|
|
33
|
+
* `"auth_required"` when the visitor must sign in, and
|
|
34
|
+
* `"user_not_registered"` when the signed-in visitor has no access to this app.
|
|
35
|
+
*
|
|
36
|
+
* @returns Promise resolving to the app's ID and access policy.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* // Decide what to render before the app boots
|
|
41
|
+
* try {
|
|
42
|
+
* const { public_settings } = await base44.app.getPublicSettings();
|
|
43
|
+
* console.log('App access policy:', public_settings);
|
|
44
|
+
* } catch (error) {
|
|
45
|
+
* if (error.status === 403) {
|
|
46
|
+
* console.log('Blocked because:', error.data?.extra_data?.reason);
|
|
47
|
+
* }
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
getPublicSettings(): Promise<AppPublicSettingsResponse>;
|
|
52
|
+
}
|
|
1
53
|
/**
|
|
2
54
|
* @internal
|
|
3
55
|
*/
|
|
@@ -62,7 +114,7 @@ export interface AppLike {
|
|
|
62
114
|
agents?: Record<string, any>;
|
|
63
115
|
logo_url?: string;
|
|
64
116
|
slug?: string;
|
|
65
|
-
public_settings?:
|
|
117
|
+
public_settings?: AppPublicSettings;
|
|
66
118
|
is_blocked?: boolean;
|
|
67
119
|
github_repo_url?: string;
|
|
68
120
|
main_page?: string;
|
package/dist/modules/auth.js
CHANGED
|
@@ -70,7 +70,7 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
|
|
|
70
70
|
// two identical GETs, so the second pays the first's full latency on every
|
|
71
71
|
// cold load.
|
|
72
72
|
//
|
|
73
|
-
// This shares the pending promise only
|
|
73
|
+
// This shares the pending promise only, and it is cleared as soon as the request
|
|
74
74
|
// settles, so no resolved user is ever retained. Caching the user across
|
|
75
75
|
// requests would leave the app rendering a stale identity after logout or a
|
|
76
76
|
// session swap.
|
|
@@ -526,7 +526,7 @@ export interface AuthModule {
|
|
|
526
526
|
/**
|
|
527
527
|
* The auth module as constructed internally, before it is narrowed to
|
|
528
528
|
* {@link AuthModule} on the public client. Not exported from the package
|
|
529
|
-
* index
|
|
529
|
+
* index. SDK consumers see only {@link AuthModule}.
|
|
530
530
|
*
|
|
531
531
|
* @internal
|
|
532
532
|
*/
|
|
@@ -534,7 +534,7 @@ export interface InternalAuthModule extends AuthModule {
|
|
|
534
534
|
/**
|
|
535
535
|
* Whether an access token is currently set on the client.
|
|
536
536
|
*
|
|
537
|
-
* Reports only the presence of a token, never its validity
|
|
537
|
+
* Reports only the presence of a token, never its validity. An expired or
|
|
538
538
|
* revoked token still reads as `true`. Callers use this to skip requests that
|
|
539
539
|
* could not succeed without a session, not to decide that one is valid.
|
|
540
540
|
*/
|
|
@@ -93,8 +93,8 @@ function assertNonEmptyString(value, label) {
|
|
|
93
93
|
* POST a request to the connector proxy and normalize the response.
|
|
94
94
|
*
|
|
95
95
|
* The proxy reports upstream outcomes in the body rather than as HTTP status, so
|
|
96
|
-
* a provider 4xx/5xx arrives here as a resolved response with `success: false
|
|
97
|
-
*
|
|
96
|
+
* a provider 4xx/5xx arrives here as a resolved response with `success: false`.
|
|
97
|
+
* Only Base44-side failures reject through the axios error interceptor.
|
|
98
98
|
*
|
|
99
99
|
* @internal
|
|
100
100
|
*/
|
|
@@ -87,7 +87,7 @@ export interface ConnectorApiResponse<T = unknown> {
|
|
|
87
87
|
status: number | null;
|
|
88
88
|
/**
|
|
89
89
|
* The parsed upstream response body, or proxy error details when no response
|
|
90
|
-
* was received. `null` when the response was binary
|
|
90
|
+
* was received. `null` when the response was binary. See {@link dataBase64}.
|
|
91
91
|
*/
|
|
92
92
|
data: T | null;
|
|
93
93
|
/**
|
|
@@ -148,9 +148,9 @@ export interface ConnectorProxyRawResponse {
|
|
|
148
148
|
*
|
|
149
149
|
* ## Metered connectors
|
|
150
150
|
*
|
|
151
|
-
* A few [platform connectors](#shared-connectors) are backed by paid third-party APIs that charge Base44 per call. For those, the OAuth token is **not** available to your code
|
|
151
|
+
* A few [platform connectors](#shared-connectors) are backed by paid third-party APIs that charge Base44 per call. For those, the OAuth token is **not** available to your code, and {@linkcode getConnection | getConnection()} rejects with a `403`. Call them with {@linkcode callApi | callApi()} instead: Base44 attaches the credential server-side, forwards the request, and bills your workspace's integration credits for the call.
|
|
152
152
|
*
|
|
153
|
-
* This applies to platform connectors only. A workspace-registered or app user connector runs on **your own** OAuth app, so the provider invoices you directly and there is nothing for Base44 to meter
|
|
153
|
+
* This applies to platform connectors only. A workspace-registered or app user connector runs on **your own** OAuth app, so the provider invoices you directly and there is nothing for Base44 to meter, so those keep normal token access via {@linkcode getWorkspaceConnection | getWorkspaceConnection()} and {@linkcode getCurrentAppUserConnection | getCurrentAppUserConnection()}.
|
|
154
154
|
*
|
|
155
155
|
* Two things to keep in mind when writing against a metered connector:
|
|
156
156
|
*
|
|
@@ -422,7 +422,7 @@ export interface ConnectorsModule {
|
|
|
422
422
|
*
|
|
423
423
|
* @param integrationType - The type of integration, such as `'x'`. See [Available connectors](#available-connectors).
|
|
424
424
|
* @param request - The upstream request to forward. See {@link ConnectorApiRequest}.
|
|
425
|
-
* @returns Promise resolving to a {@link ConnectorApiResponse}. Note that an upstream error is reported in `success` and `status`, not thrown
|
|
425
|
+
* @returns Promise resolving to a {@link ConnectorApiResponse}. Note that an upstream error is reported in `success` and `status`, not thrown. Only Base44-side failures reject.
|
|
426
426
|
*
|
|
427
427
|
* @example
|
|
428
428
|
* ```typescript
|
package/dist/modules/entities.js
CHANGED
|
@@ -143,7 +143,7 @@ function createEntityHandler(axios, appId, entityName, getSocket) {
|
|
|
143
143
|
// developer console so they know to fetch the full record on
|
|
144
144
|
// demand (e.g. a follow-up entities.X.get(id) call) instead of
|
|
145
145
|
// rendering the slimmed payload directly. Skip on delete events
|
|
146
|
-
//
|
|
146
|
+
// because the record no longer exists.
|
|
147
147
|
if (event.type !== "delete" && ((_a = event.data) === null || _a === void 0 ? void 0 : _a._oversize)) {
|
|
148
148
|
console.error(`[Base44 SDK] Realtime broadcast for ${entityName}#${event.id} was oversize and got slimmed for transport. ` +
|
|
149
149
|
`Fields >10 KB are empty and the rest of the record may be a stub. ` +
|
package/dist/utils/common.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const isNode = typeof window === "undefined";
|
|
2
2
|
export const isInIFrame = !isNode && window.self !== window.top;
|
|
3
3
|
// React Native defines `window` (so `isNode` is false there) but not `document`.
|
|
4
|
-
// Browser-only code paths gated on `window`/`isNode` alone would run
|
|
4
|
+
// Browser-only code paths gated on `window`/`isNode` alone would run, and crash,
|
|
5
5
|
// on React Native. Node (no `window`) is already handled by those `window` guards;
|
|
6
6
|
// this flags the window-without-a-DOM case that isn't.
|
|
7
7
|
export const isReactNative = !isNode && typeof document === "undefined";
|