@base44-preview/sdk 0.8.48-pr.280.cc2ee4a → 0.8.48-pr.281.97e1467
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/client.js +5 -18
- package/dist/client.types.d.ts +3 -6
- package/dist/index.d.ts +0 -1
- package/dist/modules/analytics.d.ts +0 -2
- package/dist/modules/analytics.js +3 -11
- package/dist/modules/auth.js +1 -30
- package/dist/modules/auth.types.d.ts +0 -13
- package/package.json +1 -1
- package/dist/modules/experiment-exposures.d.ts +0 -16
- package/dist/modules/experiment-exposures.js +0 -45
- package/dist/modules/experiments-runtime.types.d.ts +0 -19
- package/dist/modules/experiments-runtime.types.js +0 -7
- package/dist/modules/experiments.d.ts +0 -12
- package/dist/modules/experiments.js +0 -150
- package/dist/modules/experiments.types.d.ts +0 -96
- package/dist/modules/experiments.types.js +0 -1
package/dist/client.js
CHANGED
|
@@ -14,8 +14,6 @@ import { createAppModule } from "./modules/app.js";
|
|
|
14
14
|
import { createUsersModule } from "./modules/users.js";
|
|
15
15
|
import { RoomsSocket } from "./utils/socket-utils.js";
|
|
16
16
|
import { createAnalyticsModule } from "./modules/analytics.js";
|
|
17
|
-
import { createExperimentsModule } from "./modules/experiments.js";
|
|
18
|
-
import { createExposureTracker } from "./modules/experiment-exposures.js";
|
|
19
17
|
import { createActorsModule, resolveActorsHost, } from "./modules/actors.js";
|
|
20
18
|
/**
|
|
21
19
|
* Creates a Base44 client.
|
|
@@ -55,7 +53,7 @@ import { createActorsModule, resolveActorsHost, } from "./modules/actors.js";
|
|
|
55
53
|
* ```
|
|
56
54
|
*/
|
|
57
55
|
export function createClient(config) {
|
|
58
|
-
var _a, _b, _c, _d
|
|
56
|
+
var _a, _b, _c, _d;
|
|
59
57
|
const { serverUrl = "https://base44.app", appId, analytics, token, serviceToken, requiresAuth = false, appBaseUrl, options, functionsVersion, headers: optionalHeaders, } = config;
|
|
60
58
|
// Normalize appBaseUrl to always be a string (empty if not provided or invalid)
|
|
61
59
|
const normalizedAppBaseUrl = typeof appBaseUrl === "string" ? appBaseUrl : "";
|
|
@@ -123,19 +121,10 @@ export function createClient(config) {
|
|
|
123
121
|
baseURL: `${serverUrl}/api`,
|
|
124
122
|
headers,
|
|
125
123
|
});
|
|
126
|
-
const experiments = createExperimentsModule({
|
|
127
|
-
getAuth: () => userAuthModule,
|
|
128
|
-
trackExposure: createExposureTracker({
|
|
129
|
-
axiosClient,
|
|
130
|
-
appId,
|
|
131
|
-
enabled: (_a = analytics === null || analytics === void 0 ? void 0 : analytics.enabled) !== null && _a !== void 0 ? _a : true,
|
|
132
|
-
}).track,
|
|
133
|
-
});
|
|
134
124
|
const userAuthModule = createAuthModule(axiosClient, functionsAxiosClient, appId, {
|
|
135
125
|
appBaseUrl: normalizedAppBaseUrl,
|
|
136
126
|
serverUrl,
|
|
137
127
|
token,
|
|
138
|
-
onAuthStateChange: experiments.onAuthStateChange,
|
|
139
128
|
});
|
|
140
129
|
// Apply the access token before any module that may issue authenticated
|
|
141
130
|
// requests during construction (notably analytics, which fires an init
|
|
@@ -151,7 +140,7 @@ export function createClient(config) {
|
|
|
151
140
|
appId,
|
|
152
141
|
// serverUrl is often relative/empty (same-origin app); the proxy-fallback
|
|
153
142
|
// URL needs an absolute host, so fall back to the page origin.
|
|
154
|
-
host: resolveActorsHost(serverUrl, typeof window !== "undefined" ? (
|
|
143
|
+
host: resolveActorsHost(serverUrl, typeof window !== "undefined" ? (_a = window.location) === null || _a === void 0 ? void 0 : _a.origin : undefined),
|
|
155
144
|
functionsVersion,
|
|
156
145
|
getAuthToken: () => token || getAccessToken(),
|
|
157
146
|
mintConnectionToken: async (actorName, room, connectionId) => {
|
|
@@ -179,7 +168,6 @@ export function createClient(config) {
|
|
|
179
168
|
integrations: createIntegrationsModule(axiosClient, appId),
|
|
180
169
|
connectors: createUserConnectorsModule(axiosClient, appId),
|
|
181
170
|
auth: userAuthModule,
|
|
182
|
-
experiments: experiments.module,
|
|
183
171
|
functions: createFunctionsModule(functionsAxiosClient, appId, {
|
|
184
172
|
getAuthHeaders: () => {
|
|
185
173
|
const headers = {};
|
|
@@ -190,7 +178,7 @@ export function createClient(config) {
|
|
|
190
178
|
}
|
|
191
179
|
return headers;
|
|
192
180
|
},
|
|
193
|
-
baseURL: (
|
|
181
|
+
baseURL: (_b = functionsAxiosClient.defaults) === null || _b === void 0 ? void 0 : _b.baseURL,
|
|
194
182
|
}),
|
|
195
183
|
agents: createAgentsModule({
|
|
196
184
|
axios: axiosClient,
|
|
@@ -208,12 +196,11 @@ export function createClient(config) {
|
|
|
208
196
|
serverUrl,
|
|
209
197
|
appId,
|
|
210
198
|
userAuthModule,
|
|
211
|
-
enabled: (
|
|
199
|
+
enabled: (_c = analytics === null || analytics === void 0 ? void 0 : analytics.enabled) !== null && _c !== void 0 ? _c : true,
|
|
212
200
|
}),
|
|
213
201
|
actors: actorsModule.module,
|
|
214
202
|
cleanup: () => {
|
|
215
203
|
userModules.analytics.cleanup();
|
|
216
|
-
experiments.cleanup();
|
|
217
204
|
actorsModule.closeAll();
|
|
218
205
|
if (socket) {
|
|
219
206
|
socket.disconnect();
|
|
@@ -238,7 +225,7 @@ export function createClient(config) {
|
|
|
238
225
|
}
|
|
239
226
|
return headers;
|
|
240
227
|
},
|
|
241
|
-
baseURL: (
|
|
228
|
+
baseURL: (_d = serviceRoleFunctionsAxiosClient.defaults) === null || _d === void 0 ? void 0 : _d.baseURL,
|
|
242
229
|
}),
|
|
243
230
|
agents: createAgentsModule({
|
|
244
231
|
axios: serviceRoleAxiosClient,
|
package/dist/client.types.d.ts
CHANGED
|
@@ -9,7 +9,6 @@ import type { AiGatewayModule } from "./modules/ai-gateway.types.js";
|
|
|
9
9
|
import type { AppLogsModule } from "./modules/app-logs.types.js";
|
|
10
10
|
import type { AppModule } from "./modules/app.types.js";
|
|
11
11
|
import type { AnalyticsModule } from "./modules/analytics.types.js";
|
|
12
|
-
import type { ExperimentsModule } from "./modules/experiments.types.js";
|
|
13
12
|
import type { ActorsModule } from "./modules/actors.types.js";
|
|
14
13
|
import type { FetchWithAuthInit } from "./utils/fetch-with-auth.js";
|
|
15
14
|
/**
|
|
@@ -40,9 +39,9 @@ export interface CreateClientAnalyticsConfig {
|
|
|
40
39
|
/**
|
|
41
40
|
* Whether app analytics is enabled for this client.
|
|
42
41
|
*
|
|
43
|
-
* When disabled, automatic analytics
|
|
44
|
-
*
|
|
45
|
-
*
|
|
42
|
+
* When disabled, automatic analytics and calls to `analytics.track()` are
|
|
43
|
+
* no-ops. The SDK does not create an analytics session identifier, start
|
|
44
|
+
* heartbeat timers, or send analytics requests.
|
|
46
45
|
*
|
|
47
46
|
* @defaultValue `true`
|
|
48
47
|
*/
|
|
@@ -135,8 +134,6 @@ export interface Base44Client {
|
|
|
135
134
|
connectors: UserConnectorsModule;
|
|
136
135
|
/** {@link EntitiesModule | Entities module} for CRUD operations on your data models. */
|
|
137
136
|
entities: EntitiesModule;
|
|
138
|
-
/** {@link ExperimentsModule | Experiments module} for browser feature flags and exposures. */
|
|
139
|
-
experiments: ExperimentsModule;
|
|
140
137
|
/** {@link FunctionsModule | Functions module} for invoking custom backend functions. */
|
|
141
138
|
functions: FunctionsModule;
|
|
142
139
|
/** {@link IntegrationsModule | Integrations module} for calling pre-built integration endpoints. */
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,6 @@ 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 { ExperimentsModule, ExperimentsSnapshot, } from "./modules/experiments.types.js";
|
|
8
7
|
export type { DeleteManyResult, DeleteResult, EntitiesModule, EntityFilterOperators, EntityFilterQuery, EntityFilterValue, EntityHandler, EntityRecord, EntityTypeRegistry, ImportResult, RealtimeEventType, RealtimeEvent, RealtimeCallback, SortField, UpdateManyResult, } from "./modules/entities.types.js";
|
|
9
8
|
export type { AuthModule, LoginResponse, RegisterParams, VerifyOtpParams, ChangePasswordParams, ResetPasswordParams, User, } from "./modules/auth.types.js";
|
|
10
9
|
export type { IntegrationsModule, IntegrationEndpointFunction, CoreIntegrations, InvokeLLMParams, GenerateImageParams, GenerateImageResult, UploadFileParams, UploadFileResult, SendEmailParams, SendEmailResult, ExtractDataFromUploadedFileParams, ExtractDataFromUploadedFileResult, UploadPrivateFileParams, UploadPrivateFileResult, CreateFileSignedUrlParams, CreateFileSignedUrlResult, } from "./modules/integrations.types.js";
|
|
@@ -13,8 +13,6 @@ export interface AnalyticsModuleArgs {
|
|
|
13
13
|
userAuthModule: InternalAuthModule;
|
|
14
14
|
enabled: boolean;
|
|
15
15
|
}
|
|
16
|
-
/** @internal */
|
|
17
|
-
export declare function isAnalyticsEnabled(enabled: boolean): boolean;
|
|
18
16
|
export declare const createAnalyticsModule: ({ axiosClient, serverUrl, appId, userAuthModule, enabled, }: AnalyticsModuleArgs) => {
|
|
19
17
|
track: (params: TrackEventParams) => void;
|
|
20
18
|
cleanup: () => void;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { getSharedInstance } from "../utils/sharedInstance.js";
|
|
2
2
|
import { generateUuid, isReactNative } from "../utils/common.js";
|
|
3
|
-
import { getExperimentsRuntime } from "./experiments-runtime.types.js";
|
|
4
3
|
export const USER_HEARTBEAT_EVENT_NAME = "__user_heartbeat_event__";
|
|
5
4
|
export const ANALYTICS_INITIALIZATION_EVENT_NAME = "__initialization_event__";
|
|
6
5
|
export const ANALYTICS_SESSION_DURATION_EVENT_NAME = "__session_duration_event__";
|
|
@@ -34,18 +33,15 @@ const analyticsSharedState = getSharedInstance(ANALYTICS_SHARED_STATE_NAME, () =
|
|
|
34
33
|
...getAnalyticsConfigFromUrlParams(),
|
|
35
34
|
},
|
|
36
35
|
}));
|
|
37
|
-
/** @internal */
|
|
38
|
-
export function isAnalyticsEnabled(enabled) {
|
|
39
|
-
return enabled && analyticsSharedState.config.enabled && !isReactNative;
|
|
40
|
-
}
|
|
41
36
|
export const createAnalyticsModule = ({ axiosClient, serverUrl, appId, userAuthModule, enabled, }) => {
|
|
37
|
+
var _a;
|
|
42
38
|
// prevent overflow of events //
|
|
43
39
|
const { maxQueueSize, throttleTime, batchSize } = analyticsSharedState.config;
|
|
44
40
|
// Disable analytics on React Native. It defines `window` but not `document`,
|
|
45
41
|
// so the per-callsite `typeof window` guards below aren't enough to keep it
|
|
46
42
|
// from touching `document` (e.g. `document.referrer` on init). Node/SSR is
|
|
47
43
|
// still handled by those `window` guards, so this doesn't affect it.
|
|
48
|
-
if (!
|
|
44
|
+
if (!enabled || !((_a = analyticsSharedState.config) === null || _a === void 0 ? void 0 : _a.enabled) || isReactNative) {
|
|
49
45
|
return {
|
|
50
46
|
track: () => { },
|
|
51
47
|
cleanup: () => { },
|
|
@@ -318,10 +314,6 @@ function getFallbackSessionId() {
|
|
|
318
314
|
return ((_a = analyticsSharedState.fallbackSessionId) !== null && _a !== void 0 ? _a : (analyticsSharedState.fallbackSessionId = generateUuid()));
|
|
319
315
|
}
|
|
320
316
|
export function getAnalyticsSessionId() {
|
|
321
|
-
var _a;
|
|
322
|
-
const visitorId = (_a = getExperimentsRuntime()) === null || _a === void 0 ? void 0 : _a.visitorId;
|
|
323
|
-
if (visitorId && visitorId !== "anon")
|
|
324
|
-
return visitorId;
|
|
325
317
|
if (typeof window === "undefined") {
|
|
326
318
|
return getFallbackSessionId();
|
|
327
319
|
}
|
|
@@ -334,7 +326,7 @@ export function getAnalyticsSessionId() {
|
|
|
334
326
|
}
|
|
335
327
|
return sessionId;
|
|
336
328
|
}
|
|
337
|
-
catch (
|
|
329
|
+
catch (_a) {
|
|
338
330
|
return getFallbackSessionId();
|
|
339
331
|
}
|
|
340
332
|
}
|
package/dist/modules/auth.js
CHANGED
|
@@ -75,47 +75,20 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
|
|
|
75
75
|
// requests would leave the app rendering a stale identity after logout or a
|
|
76
76
|
// session swap.
|
|
77
77
|
let pendingMe = null;
|
|
78
|
-
let identityGeneration = 0;
|
|
79
78
|
const clearPendingMe = () => {
|
|
80
|
-
identityGeneration += 1;
|
|
81
79
|
pendingMe = null;
|
|
82
80
|
};
|
|
83
81
|
// Tracked here rather than read off `axios.defaults` so the answer stays tied
|
|
84
82
|
// to the identity transitions below (`setToken`, `logout`) instead of to the
|
|
85
83
|
// header a caller may have set on the instance directly.
|
|
86
84
|
let hasAccessToken = Boolean(options.token);
|
|
87
|
-
const notifyAuthState = (state) => {
|
|
88
|
-
var _a;
|
|
89
|
-
try {
|
|
90
|
-
(_a = options.onAuthStateChange) === null || _a === void 0 ? void 0 : _a.call(options, state);
|
|
91
|
-
}
|
|
92
|
-
catch (_b) {
|
|
93
|
-
// Optional observers must not interrupt authentication or logout redirects.
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
85
|
return {
|
|
97
86
|
hasToken() {
|
|
98
87
|
return hasAccessToken;
|
|
99
88
|
},
|
|
100
89
|
// Get current user information
|
|
101
90
|
async me() {
|
|
102
|
-
const
|
|
103
|
-
const request = pendingMe !== null && pendingMe !== void 0 ? pendingMe : axios.get(`/apps/${appId}/entities/User/me`).then((user) => {
|
|
104
|
-
if (generation === identityGeneration) {
|
|
105
|
-
notifyAuthState({ status: "authenticated", userId: user.id });
|
|
106
|
-
}
|
|
107
|
-
return user;
|
|
108
|
-
}, (error) => {
|
|
109
|
-
var _a, _b;
|
|
110
|
-
if (generation === identityGeneration) {
|
|
111
|
-
const authError = error;
|
|
112
|
-
const status = (_a = authError === null || authError === void 0 ? void 0 : authError.status) !== null && _a !== void 0 ? _a : (_b = authError === null || authError === void 0 ? void 0 : authError.response) === null || _b === void 0 ? void 0 : _b.status;
|
|
113
|
-
notifyAuthState({
|
|
114
|
-
status: status === 401 || status === 403 ? "anonymous" : "error",
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
throw error;
|
|
118
|
-
}).finally(() => {
|
|
91
|
+
const request = pendingMe !== null && pendingMe !== void 0 ? pendingMe : axios.get(`/apps/${appId}/entities/User/me`).finally(() => {
|
|
119
92
|
// Only retire this request if it is still the shared one. An identity
|
|
120
93
|
// change mid-flight clears `pendingMe` and the next caller starts a
|
|
121
94
|
// fresh request; an unconditional clear here would retire that newer
|
|
@@ -179,7 +152,6 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
|
|
|
179
152
|
clearPendingMe();
|
|
180
153
|
resetAnalyticsSessionContext();
|
|
181
154
|
hasAccessToken = false;
|
|
182
|
-
notifyAuthState({ status: "anonymous" });
|
|
183
155
|
// Only do the rest if in a browser environment
|
|
184
156
|
if (typeof window !== "undefined") {
|
|
185
157
|
// Remove token from localStorage
|
|
@@ -212,7 +184,6 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
|
|
|
212
184
|
// handle token change for axios clients
|
|
213
185
|
axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
|
|
214
186
|
functionsAxiosClient.defaults.headers.common["Authorization"] = `Bearer ${token}`;
|
|
215
|
-
notifyAuthState({ status: "pending" });
|
|
216
187
|
// Save token to localStorage if requested
|
|
217
188
|
if (saveToStorage &&
|
|
218
189
|
typeof window !== "undefined" &&
|
|
@@ -84,17 +84,6 @@ export interface ResetPasswordParams {
|
|
|
84
84
|
/** New password to set. */
|
|
85
85
|
newPassword: string;
|
|
86
86
|
}
|
|
87
|
-
/** @internal */
|
|
88
|
-
export type AuthState = {
|
|
89
|
-
status: "pending";
|
|
90
|
-
} | {
|
|
91
|
-
status: "anonymous";
|
|
92
|
-
} | {
|
|
93
|
-
status: "authenticated";
|
|
94
|
-
userId: string;
|
|
95
|
-
} | {
|
|
96
|
-
status: "error";
|
|
97
|
-
};
|
|
98
87
|
/**
|
|
99
88
|
* Configuration options for the auth module.
|
|
100
89
|
*/
|
|
@@ -109,8 +98,6 @@ export interface AuthModuleOptions {
|
|
|
109
98
|
* which is how the server-side SDK reports a token it never sets explicitly.
|
|
110
99
|
*/
|
|
111
100
|
token?: string;
|
|
112
|
-
/** @internal */
|
|
113
|
-
onAuthStateChange?: (state: AuthState) => void;
|
|
114
101
|
}
|
|
115
102
|
/**
|
|
116
103
|
* Authentication module for managing user authentication and authorization. The module automatically stores tokens in local storage when available and manages authorization headers for API requests.
|
package/package.json
CHANGED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { AxiosInstance } from "axios";
|
|
2
|
-
/** @internal */
|
|
3
|
-
export declare function createExposureTracker({ axiosClient, appId, enabled, }: {
|
|
4
|
-
axiosClient: AxiosInstance;
|
|
5
|
-
appId: string;
|
|
6
|
-
enabled: boolean;
|
|
7
|
-
}): {
|
|
8
|
-
track(assignment: {
|
|
9
|
-
experiment_id: string;
|
|
10
|
-
run_version: number;
|
|
11
|
-
variant_key: string;
|
|
12
|
-
}, identity: {
|
|
13
|
-
visitorId: string;
|
|
14
|
-
userId: string | null;
|
|
15
|
-
}): void;
|
|
16
|
-
};
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { isAnalyticsEnabled } from "./analytics.js";
|
|
2
|
-
/** @internal */
|
|
3
|
-
export function createExposureTracker({ axiosClient, appId, enabled, }) {
|
|
4
|
-
const exposed = new Set();
|
|
5
|
-
return {
|
|
6
|
-
track(assignment, identity) {
|
|
7
|
-
var _a;
|
|
8
|
-
if (typeof window === "undefined" || !isAnalyticsEnabled(enabled))
|
|
9
|
-
return;
|
|
10
|
-
const { experiment_id, run_version, variant_key } = assignment;
|
|
11
|
-
const key = JSON.stringify([
|
|
12
|
-
experiment_id,
|
|
13
|
-
run_version,
|
|
14
|
-
variant_key,
|
|
15
|
-
identity.userId,
|
|
16
|
-
identity.visitorId,
|
|
17
|
-
]);
|
|
18
|
-
if (exposed.has(key))
|
|
19
|
-
return;
|
|
20
|
-
exposed.add(key);
|
|
21
|
-
// Pin the event's auth before an account change can alter Axios defaults.
|
|
22
|
-
const authorization = identity.userId
|
|
23
|
-
? ((_a = axiosClient.defaults.headers.common.Authorization) !== null && _a !== void 0 ? _a : null)
|
|
24
|
-
: null;
|
|
25
|
-
void axiosClient
|
|
26
|
-
.request({
|
|
27
|
-
method: "POST",
|
|
28
|
-
url: `/apps/${appId}/analytics/track/batch`,
|
|
29
|
-
headers: { Authorization: authorization },
|
|
30
|
-
data: {
|
|
31
|
-
events: [
|
|
32
|
-
{
|
|
33
|
-
event_name: "__experiment_exposure__",
|
|
34
|
-
timestamp: new Date().toISOString(),
|
|
35
|
-
session_id: identity.visitorId,
|
|
36
|
-
page_url: window.location.pathname,
|
|
37
|
-
properties: { experiment_id, run_version, variant_key },
|
|
38
|
-
},
|
|
39
|
-
],
|
|
40
|
-
},
|
|
41
|
-
})
|
|
42
|
-
.catch(() => exposed.delete(key));
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/** @internal */
|
|
2
|
-
export interface ExperimentAssignment {
|
|
3
|
-
experiment_id: string;
|
|
4
|
-
flag_key: string;
|
|
5
|
-
run_version: number;
|
|
6
|
-
variant_key: string;
|
|
7
|
-
preview: boolean;
|
|
8
|
-
}
|
|
9
|
-
/** @internal */
|
|
10
|
-
export interface ExperimentsRuntime {
|
|
11
|
-
flags: Record<string, boolean>;
|
|
12
|
-
assignments: ExperimentAssignment[];
|
|
13
|
-
visitorId: string;
|
|
14
|
-
userId: string | null;
|
|
15
|
-
pendingUser: boolean;
|
|
16
|
-
setUser(id: string | null): void;
|
|
17
|
-
}
|
|
18
|
-
/** @internal */
|
|
19
|
-
export declare function getExperimentsRuntime(): ExperimentsRuntime | undefined;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { AuthState, InternalAuthModule } from "./auth.types.js";
|
|
2
|
-
import type { ExperimentsModule } from "./experiments.types.js";
|
|
3
|
-
import type { createExposureTracker } from "./experiment-exposures.js";
|
|
4
|
-
/** @internal */
|
|
5
|
-
export declare function createExperimentsModule({ getAuth, trackExposure, }: {
|
|
6
|
-
getAuth: () => InternalAuthModule;
|
|
7
|
-
trackExposure: ReturnType<typeof createExposureTracker>["track"];
|
|
8
|
-
}): {
|
|
9
|
-
module: ExperimentsModule;
|
|
10
|
-
onAuthStateChange: (next: AuthState) => void;
|
|
11
|
-
cleanup(): void;
|
|
12
|
-
};
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import { getExperimentsRuntime, } from "./experiments-runtime.types.js";
|
|
2
|
-
const EMPTY = Object.freeze({
|
|
3
|
-
flags: Object.freeze({}),
|
|
4
|
-
isLoading: false,
|
|
5
|
-
});
|
|
6
|
-
/** @internal */
|
|
7
|
-
export function createExperimentsModule({ getAuth, trackExposure, }) {
|
|
8
|
-
let runtime;
|
|
9
|
-
let state;
|
|
10
|
-
let snapshot = EMPTY;
|
|
11
|
-
let active = false;
|
|
12
|
-
let disposed = false;
|
|
13
|
-
let generation = 0;
|
|
14
|
-
let pending;
|
|
15
|
-
const listeners = new Set();
|
|
16
|
-
const readyWaiters = new Set();
|
|
17
|
-
function settleReady() {
|
|
18
|
-
if (snapshot.isLoading)
|
|
19
|
-
return;
|
|
20
|
-
for (const resolve of readyWaiters)
|
|
21
|
-
resolve(snapshot);
|
|
22
|
-
readyWaiters.clear();
|
|
23
|
-
}
|
|
24
|
-
function publish() {
|
|
25
|
-
const isLoading = !!runtime && (state === null || state === void 0 ? void 0 : state.status) === "pending";
|
|
26
|
-
const flags = runtime &&
|
|
27
|
-
((state === null || state === void 0 ? void 0 : state.status) === "authenticated" || (state === null || state === void 0 ? void 0 : state.status) === "anonymous")
|
|
28
|
-
? runtime.flags
|
|
29
|
-
: EMPTY.flags;
|
|
30
|
-
if (snapshot.isLoading === isLoading &&
|
|
31
|
-
Object.keys(snapshot.flags).length === Object.keys(flags).length &&
|
|
32
|
-
Object.keys(flags).every((key) => Object.prototype.hasOwnProperty.call(snapshot.flags, key) &&
|
|
33
|
-
snapshot.flags[key] === flags[key])) {
|
|
34
|
-
settleReady();
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
snapshot = Object.freeze({ flags: Object.freeze({ ...flags }), isLoading });
|
|
38
|
-
settleReady();
|
|
39
|
-
for (const listener of listeners) {
|
|
40
|
-
try {
|
|
41
|
-
listener();
|
|
42
|
-
}
|
|
43
|
-
catch (_a) {
|
|
44
|
-
/* Observers must not interrupt authentication. */
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
function applyIdentity() {
|
|
49
|
-
if (runtime) {
|
|
50
|
-
const userId = (state === null || state === void 0 ? void 0 : state.status) === "authenticated" ? state.userId : null;
|
|
51
|
-
if (runtime.userId !== userId || runtime.pendingUser)
|
|
52
|
-
runtime.setUser(userId);
|
|
53
|
-
}
|
|
54
|
-
publish();
|
|
55
|
-
}
|
|
56
|
-
function resolveIdentity() {
|
|
57
|
-
if (!runtime || pending || disposed)
|
|
58
|
-
return;
|
|
59
|
-
state = { status: "pending" };
|
|
60
|
-
applyIdentity();
|
|
61
|
-
const currentGeneration = generation;
|
|
62
|
-
pending = getAuth()
|
|
63
|
-
.me()
|
|
64
|
-
.then(() => { }, () => { })
|
|
65
|
-
.finally(() => {
|
|
66
|
-
if (currentGeneration === generation)
|
|
67
|
-
pending = undefined;
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
function activate() {
|
|
71
|
-
if (disposed)
|
|
72
|
-
return;
|
|
73
|
-
active = true;
|
|
74
|
-
runtime = getExperimentsRuntime();
|
|
75
|
-
if (!runtime) {
|
|
76
|
-
publish();
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
if (!state)
|
|
80
|
-
state = getAuth().hasToken()
|
|
81
|
-
? { status: "pending" }
|
|
82
|
-
: { status: "anonymous" };
|
|
83
|
-
applyIdentity();
|
|
84
|
-
if (state.status === "pending")
|
|
85
|
-
resolveIdentity();
|
|
86
|
-
}
|
|
87
|
-
function onAuthStateChange(next) {
|
|
88
|
-
if (disposed)
|
|
89
|
-
return;
|
|
90
|
-
state = next;
|
|
91
|
-
if (next.status === "pending" || next.status === "anonymous") {
|
|
92
|
-
generation++;
|
|
93
|
-
pending = undefined;
|
|
94
|
-
}
|
|
95
|
-
if (!active)
|
|
96
|
-
return;
|
|
97
|
-
runtime = getExperimentsRuntime();
|
|
98
|
-
applyIdentity();
|
|
99
|
-
if (next.status === "pending")
|
|
100
|
-
resolveIdentity();
|
|
101
|
-
}
|
|
102
|
-
const module = {
|
|
103
|
-
isEnabled(flagKey, fallback = false) {
|
|
104
|
-
activate();
|
|
105
|
-
if (!Object.prototype.hasOwnProperty.call(snapshot.flags, flagKey))
|
|
106
|
-
return fallback;
|
|
107
|
-
const assignment = runtime === null || runtime === void 0 ? void 0 : runtime.assignments.find((item) => item.flag_key === flagKey && !item.preview);
|
|
108
|
-
if (runtime && assignment)
|
|
109
|
-
trackExposure(assignment, runtime);
|
|
110
|
-
return snapshot.flags[flagKey];
|
|
111
|
-
},
|
|
112
|
-
getSnapshot() {
|
|
113
|
-
activate();
|
|
114
|
-
return snapshot;
|
|
115
|
-
},
|
|
116
|
-
subscribe(listener) {
|
|
117
|
-
activate();
|
|
118
|
-
if (!disposed)
|
|
119
|
-
listeners.add(listener);
|
|
120
|
-
return () => {
|
|
121
|
-
listeners.delete(listener);
|
|
122
|
-
};
|
|
123
|
-
},
|
|
124
|
-
async ready() {
|
|
125
|
-
activate();
|
|
126
|
-
if ((state === null || state === void 0 ? void 0 : state.status) === "error") {
|
|
127
|
-
// Wait for auth.me() to release its shared, failed request before retrying.
|
|
128
|
-
await pending;
|
|
129
|
-
if ((state === null || state === void 0 ? void 0 : state.status) === "error")
|
|
130
|
-
resolveIdentity();
|
|
131
|
-
}
|
|
132
|
-
if (snapshot.isLoading)
|
|
133
|
-
return new Promise((resolve) => readyWaiters.add(resolve));
|
|
134
|
-
return snapshot;
|
|
135
|
-
},
|
|
136
|
-
};
|
|
137
|
-
return {
|
|
138
|
-
module,
|
|
139
|
-
onAuthStateChange,
|
|
140
|
-
cleanup() {
|
|
141
|
-
disposed = true;
|
|
142
|
-
generation++;
|
|
143
|
-
pending = undefined;
|
|
144
|
-
runtime = undefined;
|
|
145
|
-
snapshot = EMPTY;
|
|
146
|
-
settleReady();
|
|
147
|
-
listeners.clear();
|
|
148
|
-
},
|
|
149
|
-
};
|
|
150
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
/** A stable, read-only view of the browser's current feature flags. */
|
|
2
|
-
export interface ExperimentsSnapshot {
|
|
3
|
-
/** Resolved flags. Empty when the runtime is absent or identity is unresolved. */
|
|
4
|
-
readonly flags: Readonly<Record<string, boolean>>;
|
|
5
|
-
/** Whether the SDK is resolving the signed-in user's identity. */
|
|
6
|
-
readonly isLoading: boolean;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Reads feature flags evaluated by the Base44 browser runtime.
|
|
10
|
-
*
|
|
11
|
-
* - Reads flags and reports experiment exposures when a flag is used.
|
|
12
|
-
* - Synchronizes assignments with this client's SDK login, token changes, and logout.
|
|
13
|
-
* - Provides readiness and subscriptions without requiring React.
|
|
14
|
-
*
|
|
15
|
-
* Available as `base44.experiments` for anonymous and signed-in app visitors,
|
|
16
|
-
* not in service role mode. Use one client for the app whose runtime is on the page.
|
|
17
|
-
* The platform must inject the Experiments runtime before this module can evaluate
|
|
18
|
-
* flags. Without it, including on servers and Workers, reads return their fallback;
|
|
19
|
-
* this module does not provide server-side evaluation or hydration guarantees.
|
|
20
|
-
* Goal conversions use the existing {@link AnalyticsModule | analytics module}.
|
|
21
|
-
* Visitor-keyed conversions share the injected runtime's visitor ID. When browser
|
|
22
|
-
* storage is blocked, the platform must supply a unique per-page ID; attribution
|
|
23
|
-
* then lasts for that page only, not across reloads or tabs.
|
|
24
|
-
*/
|
|
25
|
-
export interface ExperimentsModule {
|
|
26
|
-
/**
|
|
27
|
-
* Reads a flag and reports a best-effort exposure for its current assignment.
|
|
28
|
-
*
|
|
29
|
-
* The first use resolves identity through {@link AuthModule.me | auth.me()}
|
|
30
|
-
* when the client has a token. Reads return the fallback while identity is
|
|
31
|
-
* pending or could not be resolved. Await {@link ExperimentsModule.ready | ready()}
|
|
32
|
-
* or subscribe to updates before displaying authenticated variants.
|
|
33
|
-
*
|
|
34
|
-
* Call only where the feature is used: a read counts as exposure, not proof of
|
|
35
|
-
* visibility. Preview overrides and flags without an assignment are not tracked.
|
|
36
|
-
* Exposures respect the client's analytics setting, are deduplicated per client,
|
|
37
|
-
* experiment run, variant and identity, and retry only on a later read after failure.
|
|
38
|
-
*
|
|
39
|
-
* @param flagKey - Feature flag key defined in your app.
|
|
40
|
-
* @param fallback - Value for an unavailable flag or unresolved identity. Defaults to `false`.
|
|
41
|
-
* @returns The evaluated boolean, or the fallback when unavailable.
|
|
42
|
-
* @example
|
|
43
|
-
* ```typescript
|
|
44
|
-
* await base44.experiments.ready();
|
|
45
|
-
* const showNewCheckout = base44.experiments.isEnabled('new_checkout');
|
|
46
|
-
* ```
|
|
47
|
-
*/
|
|
48
|
-
isEnabled(flagKey: string, fallback?: boolean): boolean;
|
|
49
|
-
/**
|
|
50
|
-
* Returns the current flags and identity-loading state without tracking exposures.
|
|
51
|
-
*
|
|
52
|
-
* Starts lazy identity resolution if needed. The returned object retains its
|
|
53
|
-
* reference until its values change, for use with external-store subscriptions.
|
|
54
|
-
* Use {@link ExperimentsModule.isEnabled | isEnabled()} at the feature boundary
|
|
55
|
-
* to record exposure rather than displaying a variant directly from this snapshot.
|
|
56
|
-
*
|
|
57
|
-
* @returns A stable, read-only snapshot.
|
|
58
|
-
* @example
|
|
59
|
-
* ```typescript
|
|
60
|
-
* const { isLoading } = base44.experiments.getSnapshot();
|
|
61
|
-
* ```
|
|
62
|
-
*/
|
|
63
|
-
getSnapshot(): ExperimentsSnapshot;
|
|
64
|
-
/**
|
|
65
|
-
* Listens for flag or loading-state changes caused by this client's SDK auth flows.
|
|
66
|
-
*
|
|
67
|
-
* Does not poll for platform configuration changes or observe token writes outside
|
|
68
|
-
* the SDK. {@link Base44Client.cleanup | cleanup()} removes all listeners.
|
|
69
|
-
*
|
|
70
|
-
* @param listener - Callback invoked when the snapshot changes.
|
|
71
|
-
* @returns A function that removes the listener.
|
|
72
|
-
* @example
|
|
73
|
-
* ```typescript
|
|
74
|
-
* const unsubscribe = base44.experiments.subscribe(() => {
|
|
75
|
-
* renderCheckout(base44.experiments.isEnabled('new_checkout'));
|
|
76
|
-
* });
|
|
77
|
-
* unsubscribe();
|
|
78
|
-
* ```
|
|
79
|
-
*/
|
|
80
|
-
subscribe(listener: () => void): () => void;
|
|
81
|
-
/**
|
|
82
|
-
* Waits for the current identity lookup, including a token change during that lookup.
|
|
83
|
-
*
|
|
84
|
-
* Resolves with empty flags after an identity lookup failure; calling again retries
|
|
85
|
-
* the lookup. Missing runtimes resolve immediately. This does not wait for a future
|
|
86
|
-
* runtime injection or for exposure delivery, and never records an exposure itself.
|
|
87
|
-
*
|
|
88
|
-
* @returns A snapshot after the current identity lookup settles.
|
|
89
|
-
* @example
|
|
90
|
-
* ```typescript
|
|
91
|
-
* await base44.experiments.ready();
|
|
92
|
-
* renderCheckout(base44.experiments.isEnabled('new_checkout'));
|
|
93
|
-
* ```
|
|
94
|
-
*/
|
|
95
|
-
ready(): Promise<ExperimentsSnapshot>;
|
|
96
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|