@base44-preview/sdk 0.8.35-pr.220.75109b8 → 0.8.35-pr.221.5933e35
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 +6 -1
- package/dist/client.types.d.ts +23 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/modules/analytics.d.ts +3 -1
- package/dist/modules/analytics.js +7 -2
- package/dist/modules/auth.js +5 -0
- package/dist/server.d.ts +111 -0
- package/dist/server.js +157 -0
- package/dist/utils/auth-utils.d.ts +33 -0
- package/dist/utils/auth-utils.js +66 -0
- package/dist/utils/axios-client.d.ts +3 -1
- package/dist/utils/axios-client.js +2 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -50,7 +50,7 @@ import { createAnalyticsModule } from "./modules/analytics.js";
|
|
|
50
50
|
*/
|
|
51
51
|
export function createClient(config) {
|
|
52
52
|
var _a, _b;
|
|
53
|
-
const { serverUrl = "https://base44.app", appId, token, serviceToken, requiresAuth = false, appBaseUrl, options, functionsVersion, headers: optionalHeaders, } = config;
|
|
53
|
+
const { serverUrl = "https://base44.app", appId, token, serviceToken, requiresAuth = false, appBaseUrl, options, functionsVersion, headers: optionalHeaders, disableAnalytics = false, adapter, } = config;
|
|
54
54
|
// Normalize appBaseUrl to always be a string (empty if not provided or invalid)
|
|
55
55
|
const normalizedAppBaseUrl = typeof appBaseUrl === "string" ? appBaseUrl : "";
|
|
56
56
|
const socketConfig = {
|
|
@@ -84,6 +84,7 @@ export function createClient(config) {
|
|
|
84
84
|
headers,
|
|
85
85
|
token,
|
|
86
86
|
onError: options === null || options === void 0 ? void 0 : options.onError,
|
|
87
|
+
adapter,
|
|
87
88
|
});
|
|
88
89
|
const functionsAxiosClient = createAxiosClient({
|
|
89
90
|
baseURL: `${serverUrl}/api`,
|
|
@@ -91,6 +92,7 @@ export function createClient(config) {
|
|
|
91
92
|
token,
|
|
92
93
|
interceptResponses: false,
|
|
93
94
|
onError: options === null || options === void 0 ? void 0 : options.onError,
|
|
95
|
+
adapter,
|
|
94
96
|
});
|
|
95
97
|
const serviceRoleHeaders = {
|
|
96
98
|
...headers,
|
|
@@ -101,12 +103,14 @@ export function createClient(config) {
|
|
|
101
103
|
headers: serviceRoleHeaders,
|
|
102
104
|
token: serviceToken,
|
|
103
105
|
onError: options === null || options === void 0 ? void 0 : options.onError,
|
|
106
|
+
adapter,
|
|
104
107
|
});
|
|
105
108
|
const serviceRoleFunctionsAxiosClient = createAxiosClient({
|
|
106
109
|
baseURL: `${serverUrl}/api`,
|
|
107
110
|
headers: functionHeaders,
|
|
108
111
|
token: serviceToken,
|
|
109
112
|
interceptResponses: false,
|
|
113
|
+
adapter,
|
|
110
114
|
});
|
|
111
115
|
const userAuthModule = createAuthModule(axiosClient, functionsAxiosClient, appId, {
|
|
112
116
|
appBaseUrl: normalizedAppBaseUrl,
|
|
@@ -157,6 +161,7 @@ export function createClient(config) {
|
|
|
157
161
|
serverUrl,
|
|
158
162
|
appId,
|
|
159
163
|
userAuthModule,
|
|
164
|
+
disabled: disableAnalytics,
|
|
160
165
|
}),
|
|
161
166
|
cleanup: () => {
|
|
162
167
|
userModules.analytics.cleanup();
|
package/dist/client.types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AxiosRequestConfig } from "axios";
|
|
1
2
|
import type { EntitiesModule } from "./modules/entities.types.js";
|
|
2
3
|
import type { IntegrationsModule } from "./modules/integrations.types.js";
|
|
3
4
|
import type { AuthModule } from "./modules/auth.types.js";
|
|
@@ -68,6 +69,28 @@ export interface CreateClientConfig {
|
|
|
68
69
|
* @internal
|
|
69
70
|
*/
|
|
70
71
|
headers?: Record<string, string>;
|
|
72
|
+
/**
|
|
73
|
+
* Disables the {@link AnalyticsModule | analytics module} entirely.
|
|
74
|
+
*
|
|
75
|
+
* When `true`, `base44.analytics.track()` becomes a no-op and no background
|
|
76
|
+
* processing or heartbeat timers are started. Set this for server-side
|
|
77
|
+
* clients (SSR, edge runtimes) where background timers must not run.
|
|
78
|
+
* {@linkcode createServerClient | createServerClient()} sets this automatically.
|
|
79
|
+
*
|
|
80
|
+
* Analytics is also automatically disabled outside a browser environment.
|
|
81
|
+
*
|
|
82
|
+
* @defaultValue `false`
|
|
83
|
+
*/
|
|
84
|
+
disableAnalytics?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Axios adapter override for HTTP requests.
|
|
87
|
+
*
|
|
88
|
+
* By default, axios picks an adapter based on the runtime. Set this to
|
|
89
|
+
* `'fetch'` in edge runtimes such as Cloudflare Workers, where the default
|
|
90
|
+
* adapter detection can pick the wrong adapter.
|
|
91
|
+
* {@linkcode createServerClient | createServerClient()} sets this automatically.
|
|
92
|
+
*/
|
|
93
|
+
adapter?: AxiosRequestConfig["adapter"];
|
|
71
94
|
/**
|
|
72
95
|
* Additional client options.
|
|
73
96
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createClient, createClientFromRequest, type Base44Client, type CreateClientConfig, type CreateClientOptions } from "./client.js";
|
|
2
|
+
import { createServerClient, type CreateServerClientOptions } from "./server.js";
|
|
2
3
|
import { Base44Error, type Base44ErrorJSON } from "./utils/axios-client.js";
|
|
3
4
|
import { getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl } from "./utils/auth-utils.js";
|
|
4
|
-
export { createClient, createClientFromRequest, Base44Error, getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl, };
|
|
5
|
-
export type { Base44Client, CreateClientConfig, CreateClientOptions, Base44ErrorJSON, };
|
|
5
|
+
export { createClient, createClientFromRequest, createServerClient, Base44Error, getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl, };
|
|
6
|
+
export type { Base44Client, CreateClientConfig, CreateClientOptions, CreateServerClientOptions, Base44ErrorJSON, };
|
|
6
7
|
export * from "./types.js";
|
|
7
8
|
export type { DeleteManyResult, DeleteResult, EntitiesModule, EntityFilterOperators, EntityFilterQuery, EntityFilterValue, EntityHandler, EntityRecord, EntityTypeRegistry, ImportResult, RealtimeEventType, RealtimeEvent, RealtimeCallback, SortField, UpdateManyResult, } from "./modules/entities.types.js";
|
|
8
9
|
export type { AuthModule, LoginResponse, RegisterParams, VerifyOtpParams, ChangePasswordParams, ResetPasswordParams, User, } from "./modules/auth.types.js";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createClient, createClientFromRequest, } from "./client.js";
|
|
2
|
+
import { createServerClient, } from "./server.js";
|
|
2
3
|
import { Base44Error } from "./utils/axios-client.js";
|
|
3
4
|
import { getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl, } from "./utils/auth-utils.js";
|
|
4
|
-
export { createClient, createClientFromRequest, Base44Error, getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl, };
|
|
5
|
+
export { createClient, createClientFromRequest, createServerClient, Base44Error, getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl, };
|
|
5
6
|
export * from "./types.js";
|
|
@@ -11,8 +11,10 @@ export interface AnalyticsModuleArgs {
|
|
|
11
11
|
serverUrl: string;
|
|
12
12
|
appId: string;
|
|
13
13
|
userAuthModule: AuthModule;
|
|
14
|
+
/** Skips all analytics processing when true (e.g. for server-side clients). */
|
|
15
|
+
disabled?: boolean;
|
|
14
16
|
}
|
|
15
|
-
export declare const createAnalyticsModule: ({ axiosClient, serverUrl, appId, userAuthModule, }: AnalyticsModuleArgs) => {
|
|
17
|
+
export declare const createAnalyticsModule: ({ axiosClient, serverUrl, appId, userAuthModule, disabled, }: AnalyticsModuleArgs) => {
|
|
16
18
|
track: (params: TrackEventParams) => void;
|
|
17
19
|
cleanup: () => void;
|
|
18
20
|
};
|
|
@@ -30,11 +30,16 @@ const analyticsSharedState = getSharedInstance(ANALYTICS_SHARED_STATE_NAME, () =
|
|
|
30
30
|
...getAnalyticsConfigFromUrlParams(),
|
|
31
31
|
},
|
|
32
32
|
}));
|
|
33
|
-
export const createAnalyticsModule = ({ axiosClient, serverUrl, appId, userAuthModule, }) => {
|
|
33
|
+
export const createAnalyticsModule = ({ axiosClient, serverUrl, appId, userAuthModule, disabled = false, }) => {
|
|
34
34
|
var _a;
|
|
35
35
|
// prevent overflow of events //
|
|
36
36
|
const { maxQueueSize, throttleTime, batchSize } = analyticsSharedState.config;
|
|
37
|
-
|
|
37
|
+
// Analytics is browser-only. Outside a browser (SSR, Cloudflare Workers,
|
|
38
|
+
// Node) timers would leak — or throw at global scope in Workers — so the
|
|
39
|
+
// module becomes a no-op there, and when explicitly disabled.
|
|
40
|
+
if (disabled ||
|
|
41
|
+
typeof window === "undefined" ||
|
|
42
|
+
!((_a = analyticsSharedState.config) === null || _a === void 0 ? void 0 : _a.enabled)) {
|
|
38
43
|
return {
|
|
39
44
|
track: () => { },
|
|
40
45
|
cleanup: () => { },
|
package/dist/modules/auth.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { setAccessTokenCookie, clearAccessTokenCookie, } from "../utils/auth-utils.js";
|
|
1
2
|
function isInsideIframe() {
|
|
2
3
|
if (typeof window === "undefined")
|
|
3
4
|
return false;
|
|
@@ -129,6 +130,8 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
|
|
|
129
130
|
console.error("Failed to remove token from localStorage:", e);
|
|
130
131
|
}
|
|
131
132
|
}
|
|
133
|
+
// Clear the cookie that mirrors the token for SSR
|
|
134
|
+
clearAccessTokenCookie();
|
|
132
135
|
// Determine the from_url parameter
|
|
133
136
|
const fromUrl = redirectUrl || window.location.href;
|
|
134
137
|
// Redirect to server-side logout endpoint to clear HTTP-only cookies
|
|
@@ -155,6 +158,8 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
|
|
|
155
158
|
catch (e) {
|
|
156
159
|
console.error("Failed to save token to localStorage:", e);
|
|
157
160
|
}
|
|
161
|
+
// Mirror the token into a cookie so document requests carry it to SSR
|
|
162
|
+
setAccessTokenCookie(token);
|
|
158
163
|
}
|
|
159
164
|
},
|
|
160
165
|
// Login using username and password
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { Base44Client } from "./client.types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Options for creating a server-side Base44 client with {@linkcode createServerClient | createServerClient()}.
|
|
4
|
+
*/
|
|
5
|
+
export interface CreateServerClientOptions {
|
|
6
|
+
/**
|
|
7
|
+
* The incoming Fetch API `Request`.
|
|
8
|
+
*
|
|
9
|
+
* Used to resolve configuration from `Base44-*` headers, the user token from
|
|
10
|
+
* the `Authorization` header, and the `base44_access_token` cookie set by the
|
|
11
|
+
* browser SDK.
|
|
12
|
+
*/
|
|
13
|
+
request: Request;
|
|
14
|
+
/**
|
|
15
|
+
* Environment variables record, such as a Cloudflare Worker `env` binding or
|
|
16
|
+
* Node's `process.env`.
|
|
17
|
+
*
|
|
18
|
+
* Recognized variables: `BASE44_APP_ID`, `BASE44_API_URL`,
|
|
19
|
+
* `BASE44_SERVICE_TOKEN`, and `BASE44_FUNCTIONS_VERSION`.
|
|
20
|
+
*/
|
|
21
|
+
env?: Record<string, string | undefined>;
|
|
22
|
+
/**
|
|
23
|
+
* The Base44 app ID. Takes precedence over `env.BASE44_APP_ID` and the
|
|
24
|
+
* `Base44-App-Id` request header.
|
|
25
|
+
*/
|
|
26
|
+
appId?: string;
|
|
27
|
+
/**
|
|
28
|
+
* The Base44 server URL. Must be an absolute URL. Takes precedence over
|
|
29
|
+
* `env.BASE44_API_URL` and the `Base44-Api-Url` request header.
|
|
30
|
+
*
|
|
31
|
+
* @defaultValue `"https://base44.app"`
|
|
32
|
+
*/
|
|
33
|
+
serverUrl?: string;
|
|
34
|
+
/**
|
|
35
|
+
* User authentication token. Takes precedence over the request's
|
|
36
|
+
* `Authorization: Bearer` header and the `base44_access_token` cookie.
|
|
37
|
+
*/
|
|
38
|
+
token?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Service role authentication token. Takes precedence over
|
|
41
|
+
* `env.BASE44_SERVICE_TOKEN` and the `Base44-Service-Authorization` request
|
|
42
|
+
* header.
|
|
43
|
+
*/
|
|
44
|
+
serviceToken?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Version string for the functions API. Takes precedence over
|
|
47
|
+
* `env.BASE44_FUNCTIONS_VERSION` and the `Base44-Functions-Version` request
|
|
48
|
+
* header.
|
|
49
|
+
* @internal
|
|
50
|
+
*/
|
|
51
|
+
functionsVersion?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Creates a Base44 client for server-side rendering (SSR) and edge runtimes.
|
|
55
|
+
*
|
|
56
|
+
* Use this function in Cloudflare Workers, framework loaders, and other
|
|
57
|
+
* server environments that handle Fetch API requests. Unlike
|
|
58
|
+
* {@linkcode createClient | createClient()}, the returned client is safe to
|
|
59
|
+
* create per request outside a browser: analytics is fully disabled (no
|
|
60
|
+
* background timers), HTTP requests use the `fetch` adapter, and no browser
|
|
61
|
+
* storage is touched.
|
|
62
|
+
*
|
|
63
|
+
* Each configuration value is resolved in order from: the explicit option,
|
|
64
|
+
* the `env` record (`BASE44_APP_ID`, `BASE44_API_URL`, `BASE44_SERVICE_TOKEN`,
|
|
65
|
+
* `BASE44_FUNCTIONS_VERSION`), and finally the request headers — the same
|
|
66
|
+
* `Base44-*` headers read by
|
|
67
|
+
* {@linkcode createClientFromRequest | createClientFromRequest()}. The user
|
|
68
|
+
* token is resolved from the explicit option, then the request's
|
|
69
|
+
* `Authorization: Bearer` header, then the `base44_access_token` cookie that
|
|
70
|
+
* the browser SDK mirrors from localStorage.
|
|
71
|
+
*
|
|
72
|
+
* @param options - Server client options, including the incoming request and optional environment record.
|
|
73
|
+
* @returns A configured Base44 client instance scoped to the incoming request.
|
|
74
|
+
* @throws {Error} When no app ID can be resolved from the options, environment, or request headers.
|
|
75
|
+
* @throws {Error} When the resolved server URL isn't an absolute URL.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* // Cloudflare Worker fetch handler
|
|
80
|
+
* import { createServerClient } from '@base44/sdk';
|
|
81
|
+
*
|
|
82
|
+
* export default {
|
|
83
|
+
* async fetch(request, env) {
|
|
84
|
+
* const base44 = createServerClient({ request, env });
|
|
85
|
+
*
|
|
86
|
+
* // Reads data as the user identified by the request's cookie or
|
|
87
|
+
* // Authorization header (anonymous when neither is present)
|
|
88
|
+
* const products = await base44.entities.Products.list();
|
|
89
|
+
*
|
|
90
|
+
* return Response.json({ products });
|
|
91
|
+
* }
|
|
92
|
+
* };
|
|
93
|
+
* ```
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* // Framework loader (React Router, Remix, and similar)
|
|
98
|
+
* import { createServerClient } from '@base44/sdk';
|
|
99
|
+
*
|
|
100
|
+
* export async function loader({ request }) {
|
|
101
|
+
* const base44 = createServerClient({
|
|
102
|
+
* request,
|
|
103
|
+
* appId: 'my-app-id'
|
|
104
|
+
* });
|
|
105
|
+
*
|
|
106
|
+
* const user = await base44.auth.me().catch(() => null);
|
|
107
|
+
* return { user };
|
|
108
|
+
* }
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
export declare function createServerClient(options: CreateServerClientOptions): Base44Client;
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { createClient } from "./client.js";
|
|
2
|
+
const ACCESS_TOKEN_COOKIE_NAME = "base44_access_token";
|
|
3
|
+
/**
|
|
4
|
+
* Returns the first truthy value, treating empty strings the same as unset.
|
|
5
|
+
*/
|
|
6
|
+
function firstNonEmpty(...values) {
|
|
7
|
+
for (const value of values) {
|
|
8
|
+
if (value) {
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Extracts the token from a `Bearer <token>` authorization header value.
|
|
16
|
+
* Returns undefined when the header is missing or malformed.
|
|
17
|
+
*/
|
|
18
|
+
function parseBearerToken(header) {
|
|
19
|
+
if (!header) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const parts = header.split(" ");
|
|
23
|
+
if (parts.length === 2 && parts[0] === "Bearer" && parts[1]) {
|
|
24
|
+
return parts[1];
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Minimal cookie parser (no dependency): reads a single cookie value from a
|
|
30
|
+
* `Cookie` request header, handling quoted values and URL-encoding.
|
|
31
|
+
*/
|
|
32
|
+
function getCookieValue(cookieHeader, name) {
|
|
33
|
+
if (!cookieHeader) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
for (const part of cookieHeader.split(";")) {
|
|
37
|
+
const separatorIndex = part.indexOf("=");
|
|
38
|
+
if (separatorIndex === -1) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (part.slice(0, separatorIndex).trim() !== name) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
let value = part.slice(separatorIndex + 1).trim();
|
|
45
|
+
if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
|
|
46
|
+
value = value.slice(1, -1);
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
return decodeURIComponent(value);
|
|
50
|
+
}
|
|
51
|
+
catch (_a) {
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
function isAbsoluteUrl(url) {
|
|
58
|
+
try {
|
|
59
|
+
new URL(url);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
catch (_a) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Creates a Base44 client for server-side rendering (SSR) and edge runtimes.
|
|
68
|
+
*
|
|
69
|
+
* Use this function in Cloudflare Workers, framework loaders, and other
|
|
70
|
+
* server environments that handle Fetch API requests. Unlike
|
|
71
|
+
* {@linkcode createClient | createClient()}, the returned client is safe to
|
|
72
|
+
* create per request outside a browser: analytics is fully disabled (no
|
|
73
|
+
* background timers), HTTP requests use the `fetch` adapter, and no browser
|
|
74
|
+
* storage is touched.
|
|
75
|
+
*
|
|
76
|
+
* Each configuration value is resolved in order from: the explicit option,
|
|
77
|
+
* the `env` record (`BASE44_APP_ID`, `BASE44_API_URL`, `BASE44_SERVICE_TOKEN`,
|
|
78
|
+
* `BASE44_FUNCTIONS_VERSION`), and finally the request headers — the same
|
|
79
|
+
* `Base44-*` headers read by
|
|
80
|
+
* {@linkcode createClientFromRequest | createClientFromRequest()}. The user
|
|
81
|
+
* token is resolved from the explicit option, then the request's
|
|
82
|
+
* `Authorization: Bearer` header, then the `base44_access_token` cookie that
|
|
83
|
+
* the browser SDK mirrors from localStorage.
|
|
84
|
+
*
|
|
85
|
+
* @param options - Server client options, including the incoming request and optional environment record.
|
|
86
|
+
* @returns A configured Base44 client instance scoped to the incoming request.
|
|
87
|
+
* @throws {Error} When no app ID can be resolved from the options, environment, or request headers.
|
|
88
|
+
* @throws {Error} When the resolved server URL isn't an absolute URL.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* // Cloudflare Worker fetch handler
|
|
93
|
+
* import { createServerClient } from '@base44/sdk';
|
|
94
|
+
*
|
|
95
|
+
* export default {
|
|
96
|
+
* async fetch(request, env) {
|
|
97
|
+
* const base44 = createServerClient({ request, env });
|
|
98
|
+
*
|
|
99
|
+
* // Reads data as the user identified by the request's cookie or
|
|
100
|
+
* // Authorization header (anonymous when neither is present)
|
|
101
|
+
* const products = await base44.entities.Products.list();
|
|
102
|
+
*
|
|
103
|
+
* return Response.json({ products });
|
|
104
|
+
* }
|
|
105
|
+
* };
|
|
106
|
+
* ```
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* // Framework loader (React Router, Remix, and similar)
|
|
111
|
+
* import { createServerClient } from '@base44/sdk';
|
|
112
|
+
*
|
|
113
|
+
* export async function loader({ request }) {
|
|
114
|
+
* const base44 = createServerClient({
|
|
115
|
+
* request,
|
|
116
|
+
* appId: 'my-app-id'
|
|
117
|
+
* });
|
|
118
|
+
*
|
|
119
|
+
* const user = await base44.auth.me().catch(() => null);
|
|
120
|
+
* return { user };
|
|
121
|
+
* }
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
export function createServerClient(options) {
|
|
125
|
+
var _a;
|
|
126
|
+
const { request, env = {} } = options;
|
|
127
|
+
const headers = request.headers;
|
|
128
|
+
const appId = firstNonEmpty(options.appId, env.BASE44_APP_ID, headers.get("Base44-App-Id"));
|
|
129
|
+
if (!appId) {
|
|
130
|
+
throw new Error("createServerClient: unable to resolve an app ID. Pass appId explicitly, set the BASE44_APP_ID environment variable, or forward the Base44-App-Id request header.");
|
|
131
|
+
}
|
|
132
|
+
const serverUrl = (_a = firstNonEmpty(options.serverUrl, env.BASE44_API_URL, headers.get("Base44-Api-Url"))) !== null && _a !== void 0 ? _a : "https://base44.app";
|
|
133
|
+
if (!isAbsoluteUrl(serverUrl)) {
|
|
134
|
+
throw new Error(`createServerClient: serverUrl must be an absolute URL, got "${serverUrl}"`);
|
|
135
|
+
}
|
|
136
|
+
const token = firstNonEmpty(options.token, parseBearerToken(headers.get("Authorization")), getCookieValue(headers.get("Cookie"), ACCESS_TOKEN_COOKIE_NAME));
|
|
137
|
+
const serviceToken = firstNonEmpty(options.serviceToken, env.BASE44_SERVICE_TOKEN, parseBearerToken(headers.get("Base44-Service-Authorization")));
|
|
138
|
+
const functionsVersion = firstNonEmpty(options.functionsVersion, env.BASE44_FUNCTIONS_VERSION, headers.get("Base44-Functions-Version"));
|
|
139
|
+
// Propagate Base44-State like createClientFromRequest, so the server client
|
|
140
|
+
// degrades gracefully behind the existing Base44 proxy
|
|
141
|
+
const stateHeader = headers.get("Base44-State");
|
|
142
|
+
const additionalHeaders = {};
|
|
143
|
+
if (stateHeader) {
|
|
144
|
+
additionalHeaders["Base44-State"] = stateHeader;
|
|
145
|
+
}
|
|
146
|
+
return createClient({
|
|
147
|
+
serverUrl,
|
|
148
|
+
appId,
|
|
149
|
+
token,
|
|
150
|
+
serviceToken,
|
|
151
|
+
functionsVersion,
|
|
152
|
+
headers: additionalHeaders,
|
|
153
|
+
requiresAuth: false,
|
|
154
|
+
disableAnalytics: true,
|
|
155
|
+
adapter: "fetch",
|
|
156
|
+
});
|
|
157
|
+
}
|
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
import { GetAccessTokenOptions, SaveAccessTokenOptions, RemoveAccessTokenOptions, GetLoginUrlOptions } from "./auth-utils.types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Builds the cookie string that mirrors the access token so subsequent
|
|
4
|
+
* document requests carry it to server-side rendering (SSR) code.
|
|
5
|
+
*
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildAccessTokenCookie(token: string, { name, secure, }?: {
|
|
9
|
+
name?: string;
|
|
10
|
+
secure?: boolean;
|
|
11
|
+
}): string;
|
|
12
|
+
/**
|
|
13
|
+
* Builds the cookie string that clears the mirrored access token cookie.
|
|
14
|
+
*
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildClearAccessTokenCookie({ name, secure, }?: {
|
|
18
|
+
name?: string;
|
|
19
|
+
secure?: boolean;
|
|
20
|
+
}): string;
|
|
21
|
+
/**
|
|
22
|
+
* Mirrors the access token into a cookie (in addition to localStorage) so
|
|
23
|
+
* subsequent document requests carry the token to SSR. No-op outside a
|
|
24
|
+
* browser environment.
|
|
25
|
+
*
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
28
|
+
export declare function setAccessTokenCookie(token: string, name?: string): void;
|
|
29
|
+
/**
|
|
30
|
+
* Clears the mirrored access token cookie. No-op outside a browser environment.
|
|
31
|
+
*
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
34
|
+
export declare function clearAccessTokenCookie(name?: string): void;
|
|
2
35
|
/**
|
|
3
36
|
* Retrieves an access token from URL parameters or local storage.
|
|
4
37
|
*
|
package/dist/utils/auth-utils.js
CHANGED
|
@@ -1,3 +1,65 @@
|
|
|
1
|
+
const ACCESS_TOKEN_COOKIE_ATTRIBUTES = "path=/; SameSite=Lax";
|
|
2
|
+
/**
|
|
3
|
+
* Builds the cookie string that mirrors the access token so subsequent
|
|
4
|
+
* document requests carry it to server-side rendering (SSR) code.
|
|
5
|
+
*
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
export function buildAccessTokenCookie(token, { name = "base44_access_token", secure = false, } = {}) {
|
|
9
|
+
return `${name}=${encodeURIComponent(token)}; ${ACCESS_TOKEN_COOKIE_ATTRIBUTES}${secure ? "; Secure" : ""}`;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Builds the cookie string that clears the mirrored access token cookie.
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
export function buildClearAccessTokenCookie({ name = "base44_access_token", secure = false, } = {}) {
|
|
17
|
+
return `${name}=; ${ACCESS_TOKEN_COOKIE_ATTRIBUTES}; Max-Age=0${secure ? "; Secure" : ""}`;
|
|
18
|
+
}
|
|
19
|
+
function isHttpsPage() {
|
|
20
|
+
var _a;
|
|
21
|
+
return (typeof window !== "undefined" && ((_a = window.location) === null || _a === void 0 ? void 0 : _a.protocol) === "https:");
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Mirrors the access token into a cookie (in addition to localStorage) so
|
|
25
|
+
* subsequent document requests carry the token to SSR. No-op outside a
|
|
26
|
+
* browser environment.
|
|
27
|
+
*
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
export function setAccessTokenCookie(token, name) {
|
|
31
|
+
if (typeof document === "undefined" || !token) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
document.cookie = buildAccessTokenCookie(token, {
|
|
36
|
+
name,
|
|
37
|
+
secure: isHttpsPage(),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
console.error("Error saving token cookie:", e);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Clears the mirrored access token cookie. No-op outside a browser environment.
|
|
46
|
+
*
|
|
47
|
+
* @internal
|
|
48
|
+
*/
|
|
49
|
+
export function clearAccessTokenCookie(name) {
|
|
50
|
+
if (typeof document === "undefined") {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
document.cookie = buildClearAccessTokenCookie({
|
|
55
|
+
name,
|
|
56
|
+
secure: isHttpsPage(),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
console.error("Error removing token cookie:", e);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
1
63
|
/**
|
|
2
64
|
* Retrieves an access token from URL parameters or local storage.
|
|
3
65
|
*
|
|
@@ -112,6 +174,8 @@ export function saveAccessToken(token, options) {
|
|
|
112
174
|
window.localStorage.setItem(storageKey, token);
|
|
113
175
|
// Set "token" that is set by the built-in SDK of platform version 2
|
|
114
176
|
window.localStorage.setItem("token", token);
|
|
177
|
+
// Mirror the token into a cookie so document requests carry it to SSR
|
|
178
|
+
setAccessTokenCookie(token, storageKey);
|
|
115
179
|
return true;
|
|
116
180
|
}
|
|
117
181
|
catch (e) {
|
|
@@ -150,6 +214,8 @@ export function removeAccessToken(options) {
|
|
|
150
214
|
}
|
|
151
215
|
try {
|
|
152
216
|
window.localStorage.removeItem(storageKey);
|
|
217
|
+
// Clear the cookie that mirrors the token for SSR
|
|
218
|
+
clearAccessTokenCookie(storageKey);
|
|
153
219
|
return true;
|
|
154
220
|
}
|
|
155
221
|
catch (e) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AxiosRequestConfig } from "axios";
|
|
1
2
|
import type { Base44ErrorJSON } from "./axios-client.types.js";
|
|
2
3
|
/**
|
|
3
4
|
* Custom error class for Base44 SDK errors.
|
|
@@ -90,11 +91,12 @@ export declare class Base44Error extends Error {
|
|
|
90
91
|
* @returns Configured axios instance
|
|
91
92
|
* @internal
|
|
92
93
|
*/
|
|
93
|
-
export declare function createAxiosClient({ baseURL, headers, token, interceptResponses, onError, }: {
|
|
94
|
+
export declare function createAxiosClient({ baseURL, headers, token, interceptResponses, onError, adapter, }: {
|
|
94
95
|
baseURL: string;
|
|
95
96
|
headers?: Record<string, string>;
|
|
96
97
|
token?: string;
|
|
97
98
|
interceptResponses?: boolean;
|
|
98
99
|
onError?: (error: Error) => void;
|
|
100
|
+
adapter?: AxiosRequestConfig["adapter"];
|
|
99
101
|
}): import("axios").AxiosInstance;
|
|
100
102
|
export type { Base44ErrorJSON } from "./axios-client.types.js";
|
|
@@ -115,9 +115,10 @@ function safeErrorLog(prefix, error) {
|
|
|
115
115
|
* @returns Configured axios instance
|
|
116
116
|
* @internal
|
|
117
117
|
*/
|
|
118
|
-
export function createAxiosClient({ baseURL, headers = {}, token, interceptResponses = true, onError, }) {
|
|
118
|
+
export function createAxiosClient({ baseURL, headers = {}, token, interceptResponses = true, onError, adapter, }) {
|
|
119
119
|
const client = axios.create({
|
|
120
120
|
baseURL,
|
|
121
|
+
...(adapter !== undefined ? { adapter } : {}),
|
|
121
122
|
headers: {
|
|
122
123
|
"Content-Type": "application/json",
|
|
123
124
|
Accept: "application/json",
|