better-auth-studio 1.1.3-beta.8 → 1.1.3

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.
Files changed (196) hide show
  1. package/README.md +145 -16
  2. package/dist/adapters/astro.d.ts +0 -1
  3. package/dist/adapters/astro.js +5 -2
  4. package/dist/adapters/cloudflare-workers.d.ts +101 -0
  5. package/dist/adapters/cloudflare-workers.js +494 -0
  6. package/dist/adapters/elysia.d.ts +0 -1
  7. package/dist/adapters/elysia.js +7 -33
  8. package/dist/adapters/express.d.ts +0 -1
  9. package/dist/adapters/express.js +4 -1
  10. package/dist/adapters/hono.d.ts +0 -1
  11. package/dist/adapters/hono.js +7 -27
  12. package/dist/adapters/nextjs.d.ts +0 -1
  13. package/dist/adapters/nextjs.js +5 -2
  14. package/dist/adapters/nuxt.d.ts +0 -1
  15. package/dist/adapters/nuxt.js +5 -2
  16. package/dist/adapters/remix.d.ts +0 -1
  17. package/dist/adapters/remix.js +5 -2
  18. package/dist/adapters/solid-start.d.ts +0 -1
  19. package/dist/adapters/solid-start.js +5 -2
  20. package/dist/adapters/svelte-kit.d.ts +0 -1
  21. package/dist/adapters/svelte-kit.js +5 -2
  22. package/dist/adapters/tanstack-start.d.ts +0 -1
  23. package/dist/adapters/tanstack-start.js +36 -8
  24. package/dist/add-svelte-kit-env-modules.d.ts +0 -1
  25. package/dist/add-svelte-kit-env-modules.js +0 -1
  26. package/dist/auth-adapter.d.ts +5 -2
  27. package/dist/auth-adapter.js +39 -4
  28. package/dist/cli/commands/init.d.ts +0 -1
  29. package/dist/cli/commands/init.js +0 -1
  30. package/dist/cli.d.ts +0 -1
  31. package/dist/cli.js +6 -1
  32. package/dist/config.d.ts +0 -1
  33. package/dist/config.js +0 -1
  34. package/dist/core/handler.d.ts +0 -1
  35. package/dist/core/handler.js +36 -5
  36. package/dist/data.d.ts +10 -4
  37. package/dist/data.js +109 -19
  38. package/dist/geo-service.d.ts +0 -1
  39. package/dist/geo-service.js +0 -1
  40. package/dist/get-tsconfig-info.d.ts +0 -1
  41. package/dist/get-tsconfig-info.js +0 -1
  42. package/dist/index.d.ts +0 -1
  43. package/dist/index.js +0 -1
  44. package/dist/providers/events/helpers.d.ts +3 -2
  45. package/dist/providers/events/helpers.js +303 -93
  46. package/dist/public/assets/main-C-R_HO98.css +1 -0
  47. package/dist/public/assets/main-DDypFItU.js +1302 -0
  48. package/dist/public/index.html +2 -2
  49. package/dist/public/shaders.png +0 -0
  50. package/dist/routes/api-router.d.ts +2 -3
  51. package/dist/routes/api-router.js +0 -1
  52. package/dist/routes.d.ts +2 -3
  53. package/dist/routes.js +813 -172
  54. package/dist/studio.d.ts +0 -1
  55. package/dist/studio.js +6 -4
  56. package/dist/types/events.d.ts +0 -1
  57. package/dist/types/events.js +0 -1
  58. package/dist/types/handler.d.ts +13 -3
  59. package/dist/types/handler.js +0 -1
  60. package/dist/types/index.d.ts +0 -1
  61. package/dist/types/index.js +0 -1
  62. package/dist/utils/access-rules.d.ts +19 -0
  63. package/dist/utils/access-rules.js +126 -0
  64. package/dist/utils/auth-callbacks-injector.d.ts +0 -1
  65. package/dist/utils/auth-callbacks-injector.js +0 -1
  66. package/dist/utils/database-detection.d.ts +0 -1
  67. package/dist/utils/database-detection.js +33 -4
  68. package/dist/utils/event-ingestion.d.ts +0 -1
  69. package/dist/utils/event-ingestion.js +1 -1
  70. package/dist/utils/hook-injector.d.ts +0 -1
  71. package/dist/utils/hook-injector.js +2 -2
  72. package/dist/utils/html-injector.d.ts +1 -7
  73. package/dist/utils/html-injector.js +15 -1
  74. package/dist/utils/org-hooks-injector.d.ts +0 -1
  75. package/dist/utils/org-hooks-injector.js +0 -1
  76. package/dist/utils/package-json.d.ts +1 -1
  77. package/dist/utils/package-json.js +21 -1
  78. package/dist/utils/paths.d.ts +0 -1
  79. package/dist/utils/paths.js +0 -1
  80. package/dist/utils/reason-messages.d.ts +0 -1
  81. package/dist/utils/reason-messages.js +0 -1
  82. package/dist/utils/session.d.ts +0 -1
  83. package/dist/utils/session.js +0 -1
  84. package/dist/utils.d.ts +0 -1
  85. package/dist/utils.js +0 -1
  86. package/package.json +4 -4
  87. package/dist/adapters/astro.d.ts.map +0 -1
  88. package/dist/adapters/astro.js.map +0 -1
  89. package/dist/adapters/elysia.d.ts.map +0 -1
  90. package/dist/adapters/elysia.js.map +0 -1
  91. package/dist/adapters/express.d.ts.map +0 -1
  92. package/dist/adapters/express.js.map +0 -1
  93. package/dist/adapters/hono.d.ts.map +0 -1
  94. package/dist/adapters/hono.js.map +0 -1
  95. package/dist/adapters/nextjs.d.ts.map +0 -1
  96. package/dist/adapters/nextjs.js.map +0 -1
  97. package/dist/adapters/nuxt.d.ts.map +0 -1
  98. package/dist/adapters/nuxt.js.map +0 -1
  99. package/dist/adapters/remix.d.ts.map +0 -1
  100. package/dist/adapters/remix.js.map +0 -1
  101. package/dist/adapters/solid-start.d.ts.map +0 -1
  102. package/dist/adapters/solid-start.js.map +0 -1
  103. package/dist/adapters/svelte-kit.d.ts.map +0 -1
  104. package/dist/adapters/svelte-kit.js.map +0 -1
  105. package/dist/adapters/tanstack-start.d.ts.map +0 -1
  106. package/dist/adapters/tanstack-start.js.map +0 -1
  107. package/dist/add-svelte-kit-env-modules.d.ts.map +0 -1
  108. package/dist/add-svelte-kit-env-modules.js.map +0 -1
  109. package/dist/auth-adapter.d.ts.map +0 -1
  110. package/dist/auth-adapter.js.map +0 -1
  111. package/dist/cli/commands/init.d.ts.map +0 -1
  112. package/dist/cli/commands/init.js.map +0 -1
  113. package/dist/cli.d.ts.map +0 -1
  114. package/dist/cli.js.map +0 -1
  115. package/dist/config.d.ts.map +0 -1
  116. package/dist/config.js.map +0 -1
  117. package/dist/core/handler.d.ts.map +0 -1
  118. package/dist/core/handler.js.map +0 -1
  119. package/dist/data.d.ts.map +0 -1
  120. package/dist/data.js.map +0 -1
  121. package/dist/geo-service.d.ts.map +0 -1
  122. package/dist/geo-service.js.map +0 -1
  123. package/dist/get-tsconfig-info.d.ts.map +0 -1
  124. package/dist/get-tsconfig-info.js.map +0 -1
  125. package/dist/index.d.ts.map +0 -1
  126. package/dist/index.js.map +0 -1
  127. package/dist/providers/events/helpers.d.ts.map +0 -1
  128. package/dist/providers/events/helpers.js.map +0 -1
  129. package/dist/public/assets/main-Bi83bOQp.js +0 -1216
  130. package/dist/public/assets/main-Dx_SZlEy.css +0 -1
  131. package/dist/routes/api-router.d.ts.map +0 -1
  132. package/dist/routes/api-router.js.map +0 -1
  133. package/dist/routes.d.ts.map +0 -1
  134. package/dist/routes.js.map +0 -1
  135. package/dist/studio.d.ts.map +0 -1
  136. package/dist/studio.js.map +0 -1
  137. package/dist/types/events.d.ts.map +0 -1
  138. package/dist/types/events.js.map +0 -1
  139. package/dist/types/handler.d.ts.map +0 -1
  140. package/dist/types/handler.js.map +0 -1
  141. package/dist/types/index.d.ts.map +0 -1
  142. package/dist/types/index.js.map +0 -1
  143. package/dist/utils/auth-callbacks-injector.d.ts.map +0 -1
  144. package/dist/utils/auth-callbacks-injector.js.map +0 -1
  145. package/dist/utils/auth-callbacks-wrapper.d.ts +0 -7
  146. package/dist/utils/auth-callbacks-wrapper.d.ts.map +0 -1
  147. package/dist/utils/auth-callbacks-wrapper.js +0 -133
  148. package/dist/utils/auth-callbacks-wrapper.js.map +0 -1
  149. package/dist/utils/database-detection.d.ts.map +0 -1
  150. package/dist/utils/database-detection.js.map +0 -1
  151. package/dist/utils/database-hook-injector.d.ts +0 -3
  152. package/dist/utils/database-hook-injector.d.ts.map +0 -1
  153. package/dist/utils/database-hook-injector.js +0 -145
  154. package/dist/utils/database-hook-injector.js.map +0 -1
  155. package/dist/utils/email-otp-hooks-injector.d.ts +0 -45
  156. package/dist/utils/email-otp-hooks-injector.d.ts.map +0 -1
  157. package/dist/utils/email-otp-hooks-injector.js +0 -141
  158. package/dist/utils/email-otp-hooks-injector.js.map +0 -1
  159. package/dist/utils/event-ingestion.d.ts.map +0 -1
  160. package/dist/utils/event-ingestion.js.map +0 -1
  161. package/dist/utils/hook-injector.d.ts.map +0 -1
  162. package/dist/utils/hook-injector.js.map +0 -1
  163. package/dist/utils/html-injector.d.ts.map +0 -1
  164. package/dist/utils/html-injector.js.map +0 -1
  165. package/dist/utils/org-hooks-injector.d.ts.map +0 -1
  166. package/dist/utils/org-hooks-injector.js.map +0 -1
  167. package/dist/utils/org-hooks-wrapper.d.ts +0 -80
  168. package/dist/utils/org-hooks-wrapper.d.ts.map +0 -1
  169. package/dist/utils/org-hooks-wrapper.js +0 -807
  170. package/dist/utils/org-hooks-wrapper.js.map +0 -1
  171. package/dist/utils/organization-hooks-wrapper.d.ts +0 -44
  172. package/dist/utils/organization-hooks-wrapper.d.ts.map +0 -1
  173. package/dist/utils/organization-hooks-wrapper.js +0 -345
  174. package/dist/utils/organization-hooks-wrapper.js.map +0 -1
  175. package/dist/utils/package-json.d.ts.map +0 -1
  176. package/dist/utils/package-json.js.map +0 -1
  177. package/dist/utils/paths.d.ts.map +0 -1
  178. package/dist/utils/paths.js.map +0 -1
  179. package/dist/utils/reason-messages.d.ts.map +0 -1
  180. package/dist/utils/reason-messages.js.map +0 -1
  181. package/dist/utils/server-init.d.ts +0 -10
  182. package/dist/utils/server-init.d.ts.map +0 -1
  183. package/dist/utils/server-init.js +0 -40
  184. package/dist/utils/server-init.js.map +0 -1
  185. package/dist/utils/session.d.ts.map +0 -1
  186. package/dist/utils/session.js.map +0 -1
  187. package/dist/utils.d.ts.map +0 -1
  188. package/dist/utils.js.map +0 -1
  189. package/public/assets/main-Bi83bOQp.js +0 -1216
  190. package/public/assets/main-Dx_SZlEy.css +0 -1
  191. package/public/favicon.svg +0 -6
  192. package/public/index.html +0 -14
  193. package/public/logo.png +0 -0
  194. package/public/vite.svg +0 -5
  195. package/scripts/download-geolite2.js +0 -42
  196. package/scripts/generate-default-db.js +0 -462
@@ -0,0 +1,494 @@
1
+ const DEFAULT_METADATA = {
2
+ title: "Better Auth Studio",
3
+ logo: "",
4
+ favicon: "",
5
+ company: {
6
+ name: "",
7
+ website: "",
8
+ supportEmail: "",
9
+ },
10
+ theme: "dark",
11
+ };
12
+ const STATIC_FILE_NAMES = new Set(["/vite.svg", "/favicon.svg", "/logo.png", "/shaders.png"]);
13
+ const IP_HEADER_CANDIDATES = [
14
+ "x-forwarded-for",
15
+ "cf-connecting-ip",
16
+ "x-real-ip",
17
+ "x-client-ip",
18
+ "true-client-ip",
19
+ ];
20
+ /**
21
+ * Cloudflare Workers adapter for Better Auth Studio.
22
+ *
23
+ * This entrypoint avoids Node-only imports at module load time. It can serve Studio
24
+ * UI assets from a Workers Assets binding or an in-memory asset map and delegates
25
+ * API routes to an edge-compatible handler supplied by the host app.
26
+ */
27
+ export function betterAuthStudio(config) {
28
+ return async (request, env, ctx) => {
29
+ try {
30
+ const route = normalizeRoute(request, config.basePath);
31
+ const accessDecision = evaluateEdgeAccess(config.access, request);
32
+ if (!accessDecision.allowed) {
33
+ return jsonResponse({
34
+ success: false,
35
+ message: accessDecision.message,
36
+ reason: accessDecision.reason,
37
+ ...(accessDecision.ipAddress ? { ipAddress: accessDecision.ipAddress } : {}),
38
+ }, 403, request);
39
+ }
40
+ if (isStaticAssetPath(route.path)) {
41
+ return handleAssetRequest(request, route, config, env, ctx);
42
+ }
43
+ const apiPath = getApiPath(request, route, config);
44
+ if (apiPath) {
45
+ return handleApiRequest(request, route, apiPath, config, env, ctx);
46
+ }
47
+ return handleIndexRequest(request, route, config, env, ctx);
48
+ }
49
+ catch (error) {
50
+ console.error("Cloudflare Studio handler error:", error);
51
+ return jsonResponse({ error: "Internal server error" }, 500, request);
52
+ }
53
+ };
54
+ }
55
+ function normalizeBasePath(basePath) {
56
+ if (!basePath || basePath === "/")
57
+ return "";
58
+ const withLeadingSlash = basePath.startsWith("/") ? basePath : `/${basePath}`;
59
+ return withLeadingSlash.endsWith("/") ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
60
+ }
61
+ function normalizeRoute(request, basePath) {
62
+ const url = new URL(request.url);
63
+ const normalizedBasePath = normalizeBasePath(basePath);
64
+ let path = url.pathname || "/";
65
+ if (normalizedBasePath) {
66
+ if (path === normalizedBasePath || path === `${normalizedBasePath}/`) {
67
+ path = "/";
68
+ }
69
+ else if (path.startsWith(`${normalizedBasePath}/`)) {
70
+ path = path.slice(normalizedBasePath.length) || "/";
71
+ }
72
+ }
73
+ return {
74
+ path: path || "/",
75
+ originalPath: url.pathname || "/",
76
+ basePath: normalizedBasePath,
77
+ search: url.search,
78
+ };
79
+ }
80
+ function isStaticAssetPath(path) {
81
+ return path.startsWith("/assets/") || STATIC_FILE_NAMES.has(path);
82
+ }
83
+ function wantsJsonResponse(request) {
84
+ const accept = request.headers.get("accept") || "";
85
+ return accept.includes("application/json") || accept === "*/*" || !accept.includes("text/html");
86
+ }
87
+ function getApiPath(request, route, config) {
88
+ if (route.path === "/api" || route.path.startsWith("/api/")) {
89
+ return route.path;
90
+ }
91
+ if (route.path === "/auth" || route.path.startsWith("/auth/")) {
92
+ return `/api${route.path}`;
93
+ }
94
+ if (config.basePath && route.path !== "/" && wantsJsonResponse(request)) {
95
+ return `/api${route.path}`;
96
+ }
97
+ return null;
98
+ }
99
+ async function handleApiRequest(request, route, apiPath, config, env, ctx) {
100
+ if (apiPath === "/api/health") {
101
+ return jsonResponse({
102
+ status: "ok",
103
+ environment: "cloudflare-workers",
104
+ timestamp: new Date().toISOString(),
105
+ }, 200, request);
106
+ }
107
+ const delegatedRequest = rewriteRequestPath(request, apiPath, route.search);
108
+ const context = {
109
+ env,
110
+ ctx,
111
+ path: apiPath,
112
+ originalPath: route.originalPath,
113
+ basePath: route.basePath,
114
+ config,
115
+ };
116
+ const apiResponse = await config.apiHandler?.(delegatedRequest, context);
117
+ if (apiResponse) {
118
+ return finalizeResponse(request, apiResponse);
119
+ }
120
+ if (apiPath.startsWith("/api/auth/") && typeof config.auth?.handler === "function") {
121
+ const authResponse = await config.auth.handler(delegatedRequest);
122
+ return finalizeResponse(request, authResponse);
123
+ }
124
+ return jsonResponse({
125
+ error: "Cloudflare Workers API handler not configured",
126
+ message: "The Cloudflare Workers adapter is edge-safe and can serve the Studio shell, but the built-in Studio API still depends on Node-only modules. Provide apiHandler to handle /api/* routes in your Worker.",
127
+ path: apiPath,
128
+ }, 501, request);
129
+ }
130
+ async function handleAssetRequest(request, route, config, env, _ctx) {
131
+ const source = await resolveAssetSource(config, env);
132
+ const response = source ? await readAsset(source, request, route.path, route.search) : null;
133
+ if (response && response.status !== 404) {
134
+ return finalizeResponse(request, response);
135
+ }
136
+ return new Response(null, {
137
+ status: 404,
138
+ headers: {
139
+ "Cache-Control": "no-cache",
140
+ },
141
+ });
142
+ }
143
+ async function handleIndexRequest(request, route, config, env, ctx) {
144
+ const configuredIndex = await readConfiguredIndex(config, env, ctx, route.path);
145
+ if (configuredIndex) {
146
+ return htmlResponse(request, await responseToHtml(configuredIndex), config);
147
+ }
148
+ const source = await resolveAssetSource(config, env);
149
+ const assetIndex = source ? await readAsset(source, request, "/index.html", route.search) : null;
150
+ if (assetIndex && assetIndex.status >= 200 && assetIndex.status < 300) {
151
+ return htmlResponse(request, await assetIndex.text(), config);
152
+ }
153
+ return htmlResponse(request, getMissingAssetsHtml(config), config, 503);
154
+ }
155
+ async function readConfiguredIndex(config, env, ctx, path) {
156
+ if (!config.indexHtml)
157
+ return null;
158
+ if (typeof config.indexHtml === "string") {
159
+ return config.indexHtml;
160
+ }
161
+ const value = await config.indexHtml({
162
+ env,
163
+ ctx,
164
+ path,
165
+ config,
166
+ });
167
+ return value || null;
168
+ }
169
+ async function responseToHtml(value) {
170
+ if (typeof value === "string")
171
+ return value;
172
+ return value.text();
173
+ }
174
+ async function resolveAssetSource(config, env) {
175
+ if (typeof config.assets === "function") {
176
+ return (await config.assets(env)) || null;
177
+ }
178
+ if (config.assets) {
179
+ return config.assets;
180
+ }
181
+ const defaultBinding = getDefaultAssetsBinding(env);
182
+ return defaultBinding || null;
183
+ }
184
+ function getDefaultAssetsBinding(env) {
185
+ const maybeAssets = env?.ASSETS;
186
+ if (isAssetBinding(maybeAssets)) {
187
+ return maybeAssets;
188
+ }
189
+ return null;
190
+ }
191
+ async function readAsset(source, request, path, search) {
192
+ if (isAssetBinding(source)) {
193
+ return source.fetch(rewriteRequestPath(request, path, search));
194
+ }
195
+ const asset = findAssetInMap(source, path);
196
+ if (!asset)
197
+ return null;
198
+ if (asset instanceof Response)
199
+ return asset.clone();
200
+ return new Response(asset, {
201
+ status: 200,
202
+ headers: {
203
+ "Content-Type": getContentType(path, typeof asset === "string"),
204
+ "Cache-Control": getCacheControl(path),
205
+ },
206
+ });
207
+ }
208
+ function isAssetBinding(value) {
209
+ return !!value && typeof value === "object" && typeof value.fetch === "function";
210
+ }
211
+ function findAssetInMap(source, path) {
212
+ const normalizedPath = path === "/" ? "/index.html" : path;
213
+ const candidates = [
214
+ normalizedPath,
215
+ normalizedPath.startsWith("/") ? normalizedPath.slice(1) : `/${normalizedPath}`,
216
+ ];
217
+ for (const candidate of candidates) {
218
+ const asset = source[candidate];
219
+ if (asset)
220
+ return asset;
221
+ }
222
+ return null;
223
+ }
224
+ function rewriteRequestPath(request, path, search) {
225
+ const url = new URL(request.url);
226
+ url.pathname = path;
227
+ url.search = search;
228
+ return new Request(url.toString(), request);
229
+ }
230
+ function prepareFrontendConfig(config) {
231
+ const metadata = {
232
+ ...DEFAULT_METADATA,
233
+ ...config.metadata,
234
+ company: {
235
+ ...DEFAULT_METADATA.company,
236
+ ...config.metadata?.company,
237
+ },
238
+ };
239
+ const liveMarqueeConfig = config.events?.liveMarquee;
240
+ const shouldIncludeLiveMarquee = !!liveMarqueeConfig || !!config.events?.enabled;
241
+ return {
242
+ basePath: normalizeBasePath(config.basePath),
243
+ metadata,
244
+ liveMarquee: shouldIncludeLiveMarquee
245
+ ? {
246
+ enabled: liveMarqueeConfig?.enabled !== false,
247
+ pollInterval: liveMarqueeConfig?.pollInterval || 2000,
248
+ speed: liveMarqueeConfig?.speed ?? 0.5,
249
+ pauseOnHover: liveMarqueeConfig?.pauseOnHover ?? true,
250
+ limit: liveMarqueeConfig?.limit ?? 50,
251
+ sort: liveMarqueeConfig?.sort ?? "desc",
252
+ colors: liveMarqueeConfig?.colors || undefined,
253
+ timeWindow: liveMarqueeConfig?.timeWindow || undefined,
254
+ }
255
+ : undefined,
256
+ lastSeenAt: config.lastSeenAt && typeof config.lastSeenAt === "object"
257
+ ? {
258
+ enabled: !!config.lastSeenAt.enabled,
259
+ columnName: config.lastSeenAt.columnName,
260
+ }
261
+ : undefined,
262
+ tools: config.tools && Array.isArray(config.tools.exclude) && config.tools.exclude.length > 0
263
+ ? { exclude: config.tools.exclude }
264
+ : undefined,
265
+ };
266
+ }
267
+ function injectConfig(html, config) {
268
+ const frontendConfig = prepareFrontendConfig(config);
269
+ const safeJson = JSON.stringify(frontendConfig)
270
+ .replace(/</g, "\\u003c")
271
+ .replace(/>/g, "\\u003e")
272
+ .replace(/&/g, "\\u0026");
273
+ const escapedTitle = escapeHtml(frontendConfig.metadata.title);
274
+ let modifiedHtml = html.replace(/<title>.*?<\/title>/i, `<title>${escapedTitle}</title>`);
275
+ if (frontendConfig.metadata.favicon) {
276
+ const favicon = escapeHtml(frontendConfig.metadata.favicon);
277
+ const faviconTag = `<link rel="icon" type="${getContentType(favicon)}" href="${favicon}" />`;
278
+ modifiedHtml = modifiedHtml.replace(/<link[^>]*rel=["'](icon|shortcut icon)["'][^>]*>/gi, faviconTag);
279
+ if (!modifiedHtml.includes('rel="icon"') && !modifiedHtml.includes("rel='icon'")) {
280
+ modifiedHtml = modifiedHtml.replace("</head>", ` ${faviconTag}\n</head>`);
281
+ }
282
+ }
283
+ if (frontendConfig.basePath) {
284
+ modifiedHtml = modifiedHtml
285
+ .replace(/href="\/assets\//g, `href="${frontendConfig.basePath}/assets/`)
286
+ .replace(/src="\/assets\//g, `src="${frontendConfig.basePath}/assets/`)
287
+ .replace(/href="\/vite\.svg"/g, `href="${frontendConfig.basePath}/vite.svg"`)
288
+ .replace(/href="\/favicon\.svg"/g, `href="${frontendConfig.basePath}/favicon.svg"`)
289
+ .replace(/href="\/logo\.png"/g, `href="${frontendConfig.basePath}/logo.png"`)
290
+ .replace(/src="\/logo\.png"/g, `src="${frontendConfig.basePath}/logo.png"`);
291
+ }
292
+ const script = `
293
+ <script>
294
+ const __BAS_THEME_KEY__ = "better-auth-studio-theme";
295
+ window.__STUDIO_CONFIG__ = ${safeJson};
296
+ Object.freeze(window.__STUDIO_CONFIG__);
297
+ try {
298
+ const configuredTheme = window.__STUDIO_CONFIG__?.metadata?.theme === "light" ? "light" : "dark";
299
+ const storedTheme = window.localStorage.getItem(__BAS_THEME_KEY__);
300
+ const activeTheme = storedTheme === "light" || storedTheme === "dark" ? storedTheme : configuredTheme;
301
+ document.documentElement.dataset.theme = activeTheme;
302
+ document.documentElement.style.colorScheme = activeTheme;
303
+ document.documentElement.classList.remove("light", "dark");
304
+ document.documentElement.classList.add(activeTheme);
305
+ } catch {
306
+ document.documentElement.dataset.theme = window.__STUDIO_CONFIG__?.metadata?.theme === "light" ? "light" : "dark";
307
+ document.documentElement.style.colorScheme = document.documentElement.dataset.theme;
308
+ document.documentElement.classList.remove("light", "dark");
309
+ document.documentElement.classList.add(document.documentElement.dataset.theme);
310
+ }
311
+ if (window.__STUDIO_CONFIG__?.metadata?.title) {
312
+ document.title = window.__STUDIO_CONFIG__.metadata.title;
313
+ }
314
+ </script>
315
+ `;
316
+ if (modifiedHtml.includes("</head>")) {
317
+ return modifiedHtml.replace("</head>", `${script}</head>`);
318
+ }
319
+ return `${script}${modifiedHtml}`;
320
+ }
321
+ function htmlResponse(request, html, config, status = 200) {
322
+ const body = request.method === "HEAD" ? null : injectConfig(html, config);
323
+ return new Response(body, {
324
+ status,
325
+ headers: {
326
+ "Content-Type": "text/html; charset=utf-8",
327
+ "Cache-Control": "no-cache",
328
+ },
329
+ });
330
+ }
331
+ function jsonResponse(data, status, request) {
332
+ return new Response(request.method === "HEAD" ? null : JSON.stringify(data), {
333
+ status,
334
+ headers: {
335
+ "Content-Type": "application/json",
336
+ "Cache-Control": "no-cache",
337
+ },
338
+ });
339
+ }
340
+ function finalizeResponse(request, response) {
341
+ if (request.method !== "HEAD")
342
+ return response;
343
+ return new Response(null, {
344
+ status: response.status,
345
+ statusText: response.statusText,
346
+ headers: response.headers,
347
+ });
348
+ }
349
+ function getMissingAssetsHtml(config) {
350
+ const basePath = normalizeBasePath(config.basePath) || "/";
351
+ return `<!DOCTYPE html>
352
+ <html>
353
+ <head>
354
+ <title>Better Auth Studio</title>
355
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
356
+ <style>
357
+ body { font-family: system-ui, sans-serif; background: #050505; color: #f5f5f5; max-width: 720px; margin: 56px auto; padding: 0 24px; line-height: 1.6; }
358
+ code { background: #181818; border: 1px solid #2a2a2a; padding: 2px 6px; }
359
+ pre { background: #101010; border: 1px solid #2a2a2a; padding: 16px; overflow-x: auto; }
360
+ </style>
361
+ </head>
362
+ <body>
363
+ <h1>Studio assets are not configured</h1>
364
+ <p>The Cloudflare Workers adapter is running at <code>${escapeHtml(basePath)}</code>, but it could not find Studio UI assets.</p>
365
+ <p>Bind Workers Assets as <code>ASSETS</code>, pass an <code>assets</code> binding, or provide <code>indexHtml</code> when creating the handler.</p>
366
+ <pre>import { betterAuthStudio } from "better-auth-studio/cloudflare-workers";
367
+
368
+ const studio = betterAuthStudio({
369
+ basePath: "${escapeHtml(basePath === "/" ? "" : basePath)}",
370
+ });
371
+
372
+ export default {
373
+ fetch: (request, env, ctx) => studio(request, env, ctx),
374
+ };</pre>
375
+ </body>
376
+ </html>`;
377
+ }
378
+ function evaluateEdgeAccess(accessConfig, request) {
379
+ const ipAddress = extractClientIp(request.headers);
380
+ const allowIpAddresses = accessConfig?.allowIpAddresses?.filter((value) => value.trim().length);
381
+ if (allowIpAddresses?.length) {
382
+ if (!ipAddress || !allowIpAddresses.some((rule) => ipMatchesRule(ipAddress, rule))) {
383
+ return {
384
+ allowed: false,
385
+ ipAddress,
386
+ reason: "ip_not_allowed",
387
+ message: "Access denied. This IP address is not in the allowed list.",
388
+ };
389
+ }
390
+ }
391
+ const blockIpAddresses = accessConfig?.blockIpAddresses?.filter((value) => value.trim().length);
392
+ if (blockIpAddresses?.length && ipAddress) {
393
+ const blocked = blockIpAddresses.some((rule) => ipMatchesRule(ipAddress, rule));
394
+ if (blocked) {
395
+ return {
396
+ allowed: false,
397
+ ipAddress,
398
+ reason: "ip_blocked",
399
+ message: "Access denied. This IP address is blocked.",
400
+ };
401
+ }
402
+ }
403
+ return { allowed: true, ipAddress };
404
+ }
405
+ function extractClientIp(headers) {
406
+ for (const header of IP_HEADER_CANDIDATES) {
407
+ const value = headers.get(header);
408
+ if (!value)
409
+ continue;
410
+ const ip = normalizeIpToken(value.split(",")[0]);
411
+ if (ip)
412
+ return ip;
413
+ }
414
+ const forwarded = headers.get("forwarded");
415
+ if (forwarded) {
416
+ const entries = forwarded.split(",");
417
+ for (const entry of entries) {
418
+ const match = entry.match(/for=("?\[?[a-fA-F0-9:.]+\]?"?)/i);
419
+ if (!match)
420
+ continue;
421
+ const ip = normalizeIpToken(match[1]);
422
+ if (ip)
423
+ return ip;
424
+ }
425
+ }
426
+ return null;
427
+ }
428
+ function normalizeIpToken(raw) {
429
+ if (!raw)
430
+ return null;
431
+ let value = raw.trim();
432
+ if (!value || value.toLowerCase() === "unknown")
433
+ return null;
434
+ if (value.toLowerCase().startsWith("for="))
435
+ value = value.slice(4).trim();
436
+ value = value.replace(/^"+|"+$/g, "");
437
+ if (value.startsWith("[")) {
438
+ const end = value.indexOf("]");
439
+ if (end > 0)
440
+ value = value.slice(1, end);
441
+ }
442
+ if (/^\d{1,3}(?:\.\d{1,3}){3}:\d+$/.test(value))
443
+ value = value.split(":")[0] || value;
444
+ if (value.startsWith("::ffff:"))
445
+ value = value.slice(7);
446
+ return value || null;
447
+ }
448
+ function ipMatchesRule(ipAddress, rule) {
449
+ const trimmedRule = rule.trim();
450
+ if (!trimmedRule)
451
+ return false;
452
+ if (!trimmedRule.includes("*")) {
453
+ return ipAddress === normalizeIpToken(trimmedRule);
454
+ }
455
+ const pattern = `^${escapeRegExp(trimmedRule).replace(/\\\*/g, ".*")}$`;
456
+ return new RegExp(pattern).test(ipAddress);
457
+ }
458
+ function escapeRegExp(value) {
459
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
460
+ }
461
+ function escapeHtml(value) {
462
+ return value
463
+ .replace(/&/g, "&amp;")
464
+ .replace(/</g, "&lt;")
465
+ .replace(/>/g, "&gt;")
466
+ .replace(/"/g, "&quot;")
467
+ .replace(/'/g, "&#39;");
468
+ }
469
+ function getContentType(path, preferHtml = false) {
470
+ const cleanPath = path.split("?")[0] || "";
471
+ const ext = cleanPath.slice(cleanPath.lastIndexOf(".")).toLowerCase();
472
+ const types = {
473
+ ".html": "text/html; charset=utf-8",
474
+ ".js": "application/javascript",
475
+ ".css": "text/css",
476
+ ".json": "application/json",
477
+ ".png": "image/png",
478
+ ".jpg": "image/jpeg",
479
+ ".jpeg": "image/jpeg",
480
+ ".svg": "image/svg+xml",
481
+ ".ico": "image/x-icon",
482
+ ".webp": "image/webp",
483
+ ".woff": "font/woff",
484
+ ".woff2": "font/woff2",
485
+ ".ttf": "font/ttf",
486
+ };
487
+ return types[ext] || (preferHtml ? "text/html; charset=utf-8" : "application/octet-stream");
488
+ }
489
+ function getCacheControl(path) {
490
+ if (path.match(/\.(js|css|png|jpg|jpeg|svg|webp|woff|woff2|ttf)$/)) {
491
+ return "public, max-age=31536000, immutable";
492
+ }
493
+ return "no-cache";
494
+ }
@@ -4,4 +4,3 @@ import type { StudioConfig } from "../types/handler.js";
4
4
  * Elysia adapter for Better Auth Studio
5
5
  */
6
6
  export declare function betterAuthStudio(config: StudioConfig): (context: Context) => Promise<any>;
7
- //# sourceMappingURL=elysia.d.ts.map
@@ -70,38 +70,12 @@ async function convertElysiaToUniversal(context) {
70
70
  };
71
71
  }
72
72
  function sendElysiaResponse(context, universal) {
73
- context.set.status = universal.status;
74
- Object.entries(universal.headers).forEach(([key, value]) => {
75
- context.set.headers[key] = value;
73
+ const headers = new Headers(universal.headers);
74
+ universal.setCookies?.forEach((cookie) => {
75
+ headers.append("Set-Cookie", cookie);
76
+ });
77
+ return new Response(universal.body, {
78
+ status: universal.status,
79
+ headers,
76
80
  });
77
- if (Buffer.isBuffer(universal.body)) {
78
- return new Response(universal.body, {
79
- status: universal.status,
80
- headers: universal.headers,
81
- });
82
- }
83
- else if (typeof universal.body === "string") {
84
- const contentType = universal.headers["content-type"] || universal.headers["Content-Type"] || "";
85
- if (contentType.includes("application/json")) {
86
- try {
87
- return JSON.parse(universal.body);
88
- }
89
- catch {
90
- return universal.body;
91
- }
92
- }
93
- else if (contentType.includes("text/html")) {
94
- return new Response(universal.body, {
95
- status: universal.status,
96
- headers: universal.headers,
97
- });
98
- }
99
- else {
100
- return universal.body;
101
- }
102
- }
103
- else {
104
- return String(universal.body);
105
- }
106
81
  }
107
- //# sourceMappingURL=elysia.js.map
@@ -4,4 +4,3 @@ import type { StudioConfig } from "../types/handler.js";
4
4
  * Express adapter for Better Auth Studio
5
5
  */
6
6
  export declare function betterAuthStudio(config: StudioConfig): ExpressRouter;
7
- //# sourceMappingURL=express.d.ts.map
@@ -28,6 +28,7 @@ function convertExpressToUniversal(req) {
28
28
  url: req.originalUrl,
29
29
  method: req.method,
30
30
  headers: req.headers,
31
+ ip: req.ip,
31
32
  body: req.body,
32
33
  };
33
34
  }
@@ -36,6 +37,9 @@ function sendExpressResponse(res, universal) {
36
37
  Object.entries(universal.headers).forEach(([key, value]) => {
37
38
  res.setHeader(key, value);
38
39
  });
40
+ if (universal.setCookies?.length) {
41
+ res.setHeader("Set-Cookie", universal.setCookies);
42
+ }
39
43
  if (Buffer.isBuffer(universal.body)) {
40
44
  res.end(universal.body);
41
45
  }
@@ -43,4 +47,3 @@ function sendExpressResponse(res, universal) {
43
47
  res.send(universal.body);
44
48
  }
45
49
  }
46
- //# sourceMappingURL=express.js.map
@@ -4,4 +4,3 @@ import type { StudioConfig } from "../types/handler.js";
4
4
  * Hono adapter for Better Auth Studio
5
5
  */
6
6
  export declare function betterAuthStudio(config: StudioConfig): (c: Context) => Promise<Response>;
7
- //# sourceMappingURL=hono.d.ts.map
@@ -53,32 +53,12 @@ async function convertHonoToUniversal(c) {
53
53
  };
54
54
  }
55
55
  function sendHonoResponse(c, universal) {
56
- c.status(universal.status);
57
- Object.entries(universal.headers).forEach(([key, value]) => {
58
- c.header(key, value);
56
+ const headers = new Headers(universal.headers);
57
+ universal.setCookies?.forEach((cookie) => {
58
+ headers.append("Set-Cookie", cookie);
59
+ });
60
+ return new Response(universal.body, {
61
+ status: universal.status,
62
+ headers,
59
63
  });
60
- if (Buffer.isBuffer(universal.body)) {
61
- return c.body(universal.body);
62
- }
63
- else if (typeof universal.body === "string") {
64
- const contentType = universal.headers["content-type"] || universal.headers["Content-Type"] || "";
65
- if (contentType.includes("application/json")) {
66
- try {
67
- return c.json(JSON.parse(universal.body));
68
- }
69
- catch {
70
- return c.text(universal.body);
71
- }
72
- }
73
- else if (contentType.includes("text/html")) {
74
- return c.html(universal.body);
75
- }
76
- else {
77
- return c.text(universal.body);
78
- }
79
- }
80
- else {
81
- return c.text(String(universal.body));
82
- }
83
64
  }
84
- //# sourceMappingURL=hono.js.map
@@ -1,3 +1,2 @@
1
1
  import type { StudioConfig } from "../types/handler.js";
2
2
  export declare function betterAuthStudio(config: StudioConfig): (request: Request) => Promise<Response>;
3
- //# sourceMappingURL=nextjs.d.ts.map
@@ -52,9 +52,12 @@ async function requestToUniversal(req) {
52
52
  };
53
53
  }
54
54
  function universalToResponse(res) {
55
+ const headers = new Headers(res.headers);
56
+ res.setCookies?.forEach((cookie) => {
57
+ headers.append("Set-Cookie", cookie);
58
+ });
55
59
  return new Response(res.body, {
56
60
  status: res.status,
57
- headers: res.headers,
61
+ headers,
58
62
  });
59
63
  }
60
- //# sourceMappingURL=nextjs.js.map
@@ -16,4 +16,3 @@ import type { StudioConfig } from "../types/handler.js";
16
16
  * auto-imports enabled for h3 utilities.
17
17
  */
18
18
  export declare function betterAuthStudio(config: StudioConfig): (event: any) => Promise<Response | any>;
19
- //# sourceMappingURL=nuxt.d.ts.map
@@ -112,9 +112,12 @@ function getRequestURL(event) {
112
112
  function universalToResponse(res) {
113
113
  // Simply return a Response object - Nuxt/h3 will handle it properly
114
114
  // Nuxt will handle client disconnections automatically
115
+ const headers = new Headers(res.headers);
116
+ res.setCookies?.forEach((cookie) => {
117
+ headers.append("Set-Cookie", cookie);
118
+ });
115
119
  return new Response(res.body, {
116
120
  status: res.status,
117
- headers: res.headers,
121
+ headers,
118
122
  });
119
123
  }
120
- //# sourceMappingURL=nuxt.js.map
@@ -23,4 +23,3 @@ import type { StudioConfig } from "../types/handler.js";
23
23
  export declare function betterAuthStudio(config: StudioConfig): ({ request }: {
24
24
  request: Request;
25
25
  }) => Promise<Response>;
26
- //# sourceMappingURL=remix.d.ts.map
@@ -96,9 +96,12 @@ async function convertRemixToUniversal({ request }, config) {
96
96
  };
97
97
  }
98
98
  function universalToResponse(res) {
99
+ const headers = new Headers(res.headers);
100
+ res.setCookies?.forEach((cookie) => {
101
+ headers.append("Set-Cookie", cookie);
102
+ });
99
103
  return new Response(res.body, {
100
104
  status: res.status,
101
- headers: res.headers,
105
+ headers,
102
106
  });
103
107
  }
104
- //# sourceMappingURL=remix.js.map