@webtwizz/cloud 0.4.0 → 0.5.0
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/AI_GUIDE.md +63 -5
- package/CHANGELOG.md +26 -0
- package/README.md +4 -2
- package/dist/chunk-4PKQUQZD.js +6 -0
- package/dist/{chunk-G54JBUO3.js → chunk-WX3OU7ED.js} +17 -4
- package/dist/client.d.ts +13 -4
- package/dist/client.js +68 -770
- package/dist/db.js +1 -1
- package/dist/files.js +1 -1
- package/dist/index.js +1 -1
- package/dist/server.d.ts +29 -5
- package/dist/server.js +494 -1428
- package/package.json +7 -10
- package/dist/chunk-7EXXDRMF.js +0 -376
package/dist/client.js
CHANGED
|
@@ -1,793 +1,91 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
NEON_AUTH_POPUP_CALLBACK_ROUTE,
|
|
5
|
-
NEON_AUTH_POPUP_PARAM_NAME,
|
|
6
|
-
NEON_AUTH_SESSION_VERIFIER_PARAM_NAME,
|
|
7
|
-
OAUTH_POPUP_MESSAGE_TYPE,
|
|
8
|
-
SESSION_CACHE_TTL_MS,
|
|
9
|
-
normalizeBetterAuthError
|
|
10
|
-
} from "./chunk-7EXXDRMF.js";
|
|
2
|
+
FRAME_HEADER
|
|
3
|
+
} from "./chunk-4PKQUQZD.js";
|
|
11
4
|
|
|
12
|
-
//
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
var
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
*
|
|
28
|
-
* @param key - Unique identifier for this request (e.g., "getSession")
|
|
29
|
-
* @param fn - Async function to execute (only called if no in-flight request exists)
|
|
30
|
-
* @returns Promise that resolves to the function result
|
|
31
|
-
*
|
|
32
|
-
* @example
|
|
33
|
-
* ```typescript
|
|
34
|
-
* // First call: Executes fetchSession(), tracks Promise
|
|
35
|
-
* const result1 = await manager.deduplicate('getSession', fetchSession);
|
|
36
|
-
*
|
|
37
|
-
* // Concurrent call: Returns existing Promise (no fetchSession() call)
|
|
38
|
-
* const result2 = await manager.deduplicate('getSession', fetchSession);
|
|
39
|
-
*
|
|
40
|
-
* // Both results are identical (same object reference)
|
|
41
|
-
* console.log(result1 === result2); // true
|
|
42
|
-
* ```
|
|
43
|
-
*/
|
|
44
|
-
async deduplicate(key, fn) {
|
|
45
|
-
const existing = this.inFlightRequests.get(key);
|
|
46
|
-
if (existing) return existing;
|
|
47
|
-
const promise = fn().finally(() => {
|
|
48
|
-
this.inFlightRequests.delete(key);
|
|
49
|
-
});
|
|
50
|
-
this.inFlightRequests.set(key, promise);
|
|
51
|
-
return promise;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Clear specific in-flight request.
|
|
55
|
-
*
|
|
56
|
-
* Useful for forced refresh or cache invalidation scenarios.
|
|
57
|
-
* Next call with same key will execute fresh request.
|
|
58
|
-
*
|
|
59
|
-
* @param key - Request key to clear
|
|
60
|
-
*/
|
|
61
|
-
clear(key) {
|
|
62
|
-
this.inFlightRequests.delete(key);
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Clear all in-flight requests.
|
|
66
|
-
*
|
|
67
|
-
* Useful for cleanup on sign-out or reset scenarios.
|
|
68
|
-
*/
|
|
69
|
-
clearAll() {
|
|
70
|
-
this.inFlightRequests.clear();
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Check if request is in-flight.
|
|
74
|
-
*
|
|
75
|
-
* @param key - Request key to check
|
|
76
|
-
* @returns True if request is currently in-flight
|
|
77
|
-
*/
|
|
78
|
-
has(key) {
|
|
79
|
-
return this.inFlightRequests.has(key);
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Get count of in-flight requests (for debugging/testing).
|
|
83
|
-
*
|
|
84
|
-
* @returns Number of currently tracked requests
|
|
85
|
-
*/
|
|
86
|
-
size() {
|
|
87
|
-
return this.inFlightRequests.size;
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
function getJwtExpiration(jwt) {
|
|
91
|
-
try {
|
|
92
|
-
const parts = jwt.split(".");
|
|
93
|
-
if (parts.length !== 3) return null;
|
|
94
|
-
const exp = JSON.parse(atob(parts[1])).exp;
|
|
95
|
-
return typeof exp === "number" ? exp : null;
|
|
96
|
-
} catch {
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
var TokenCache = class {
|
|
101
|
-
cache = null;
|
|
102
|
-
/**
|
|
103
|
-
* Get cached data if not expired.
|
|
104
|
-
* Returns null if cache is empty or expired.
|
|
105
|
-
*/
|
|
106
|
-
get() {
|
|
107
|
-
if (!this.cache) return null;
|
|
108
|
-
if (Date.now() > this.cache.expiresAt) {
|
|
109
|
-
this.cache = null;
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
return this.cache.data;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Set cached data with TTL.
|
|
116
|
-
* If jwt is provided, TTL is calculated from its expiration.
|
|
117
|
-
* Otherwise, uses default SESSION_CACHE_TTL_MS.
|
|
118
|
-
*/
|
|
119
|
-
set(data, jwt) {
|
|
120
|
-
const ttl = this.calculateTTL(jwt);
|
|
121
|
-
this.cache = {
|
|
122
|
-
data,
|
|
123
|
-
expiresAt: Date.now() + ttl
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Clear the cache.
|
|
128
|
-
*/
|
|
129
|
-
clear() {
|
|
130
|
-
this.cache = null;
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Check if cache has valid (non-expired) data.
|
|
134
|
-
*/
|
|
135
|
-
has() {
|
|
136
|
-
return this.get() !== null;
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Calculate cache TTL from JWT expiration.
|
|
140
|
-
* Falls back to default TTL if JWT is invalid or missing.
|
|
141
|
-
*/
|
|
142
|
-
calculateTTL(jwt) {
|
|
143
|
-
if (!jwt) return SESSION_CACHE_TTL_MS;
|
|
144
|
-
const exp = getJwtExpiration(jwt);
|
|
145
|
-
if (!exp) return SESSION_CACHE_TTL_MS;
|
|
146
|
-
const now = Date.now();
|
|
147
|
-
const ttl = exp * 1e3 - now - CLOCK_SKEW_BUFFER_MS;
|
|
148
|
-
return Math.max(ttl, 1e3);
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
var SessionCacheManager = class {
|
|
152
|
-
cache = new TokenCache();
|
|
153
|
-
lastSessionData = null;
|
|
154
|
-
invalidated = false;
|
|
155
|
-
/**
|
|
156
|
-
* Get cached session if valid and not expired.
|
|
157
|
-
* Returns null if cache is invalid, expired, or doesn't exist.
|
|
158
|
-
*/
|
|
159
|
-
getCachedSession() {
|
|
160
|
-
if (this.invalidated) return null;
|
|
161
|
-
return this.cache.get();
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Set cached session with JWT-based TTL.
|
|
165
|
-
* Skips caching if cache was invalidated (sign-out scenario).
|
|
166
|
-
*/
|
|
167
|
-
setCachedSession(data) {
|
|
168
|
-
if (this.invalidated) return;
|
|
169
|
-
this.lastSessionData = this.cache.get();
|
|
170
|
-
this.cache.set(data, data.session.token);
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Invalidate cache (marks as invalid but doesn't clear).
|
|
174
|
-
* Useful for sign-out scenarios where in-flight requests should not cache.
|
|
175
|
-
*/
|
|
176
|
-
invalidateSessionCache() {
|
|
177
|
-
this.invalidated = true;
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Clear cache completely.
|
|
181
|
-
*/
|
|
182
|
-
clearSessionCache() {
|
|
183
|
-
this.cache.clear();
|
|
184
|
-
this.lastSessionData = null;
|
|
185
|
-
this.invalidated = false;
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Check if token was refreshed by comparing tokens with previous session.
|
|
189
|
-
* Returns true if tokens differ (token was refreshed), false otherwise.
|
|
190
|
-
*/
|
|
191
|
-
wasTokenRefreshed(data) {
|
|
192
|
-
if (!this.lastSessionData?.session?.token || !data?.session?.token) return false;
|
|
193
|
-
return this.lastSessionData.session.token !== data.session.token;
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
var AnonymousTokenCacheManager = class {
|
|
197
|
-
cache = new TokenCache();
|
|
198
|
-
/**
|
|
199
|
-
* Get cached anonymous token response if not expired.
|
|
200
|
-
* Returns null if cache is empty or expired.
|
|
201
|
-
*/
|
|
202
|
-
getCachedResponse() {
|
|
203
|
-
return this.cache.get();
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Set cached anonymous token response with JWT-based TTL.
|
|
207
|
-
* TTL is automatically calculated from the JWT expiration.
|
|
208
|
-
*/
|
|
209
|
-
setCachedResponse(data) {
|
|
210
|
-
this.cache.set(data, data.token);
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Clear the cache.
|
|
214
|
-
*/
|
|
215
|
-
clearCache() {
|
|
216
|
-
this.cache.clear();
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Check if cache has a valid (non-expired) response.
|
|
220
|
-
*/
|
|
221
|
-
hasCachedResponse() {
|
|
222
|
-
return this.cache.has();
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
async function openOAuthPopup(url) {
|
|
226
|
-
const timeout = 12e4;
|
|
227
|
-
const pollInterval = 500;
|
|
228
|
-
return new Promise((resolve, reject) => {
|
|
229
|
-
const popup = globalThis.open(url, "neon_oauth_popup", "width=500,height=700,popup=yes");
|
|
230
|
-
if (!popup || popup.closed) {
|
|
231
|
-
reject(/* @__PURE__ */ new Error("Popup blocked. Please allow popups for this site."));
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
const timeoutId = setTimeout(() => {
|
|
235
|
-
cleanup();
|
|
236
|
-
try {
|
|
237
|
-
popup.close();
|
|
238
|
-
} catch {
|
|
239
|
-
}
|
|
240
|
-
reject(/* @__PURE__ */ new Error("OAuth popup timed out. Please try again."));
|
|
241
|
-
}, timeout);
|
|
242
|
-
const pollId = setInterval(() => {
|
|
5
|
+
// src/client.ts
|
|
6
|
+
import { adminClient, emailOTPClient } from "better-auth/client/plugins";
|
|
7
|
+
import { createAuthClient as create } from "better-auth/react";
|
|
8
|
+
var POPUP_MESSAGE = "webtwizz-auth:return";
|
|
9
|
+
var failure = (code, status = 0, message = "") => ({ data: null, error: { code, status, statusText: "", message } });
|
|
10
|
+
function settle(promise) {
|
|
11
|
+
return promise.catch((failed) => failure(failed?.code ?? "NETWORK_ERROR", failed?.status ?? 0, failed?.message ?? ""));
|
|
12
|
+
}
|
|
13
|
+
function neverThrows(target) {
|
|
14
|
+
return new Proxy(target, {
|
|
15
|
+
get(object, key, receiver) {
|
|
16
|
+
const value = Reflect.get(object, key, receiver);
|
|
17
|
+
return typeof value === "function" ? neverThrows(value) : value;
|
|
18
|
+
},
|
|
19
|
+
apply(method, self, args) {
|
|
243
20
|
try {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
} catch {
|
|
21
|
+
const result = Reflect.apply(method, self, args);
|
|
22
|
+
return result instanceof Promise ? settle(result) : result;
|
|
23
|
+
} catch (error) {
|
|
24
|
+
return settle(Promise.reject(error));
|
|
249
25
|
}
|
|
250
|
-
}, pollInterval);
|
|
251
|
-
function cleanup() {
|
|
252
|
-
clearTimeout(timeoutId);
|
|
253
|
-
clearInterval(pollId);
|
|
254
|
-
globalThis.removeEventListener("message", handleMessage);
|
|
255
26
|
}
|
|
256
|
-
function handleMessage(event) {
|
|
257
|
-
if (event.origin !== globalThis.location.origin) return;
|
|
258
|
-
if (event.data?.type !== OAUTH_POPUP_MESSAGE_TYPE) return;
|
|
259
|
-
cleanup();
|
|
260
|
-
resolve({ verifier: event.data.verifier || null });
|
|
261
|
-
}
|
|
262
|
-
globalThis.addEventListener("message", handleMessage);
|
|
263
27
|
});
|
|
264
28
|
}
|
|
265
|
-
var
|
|
266
|
-
return globalThis.window !== void 0 && typeof document !== "undefined";
|
|
267
|
-
};
|
|
268
|
-
var isIframe = () => {
|
|
269
|
-
if (!isBrowser()) return false;
|
|
29
|
+
var inFrame = () => {
|
|
270
30
|
try {
|
|
271
|
-
return
|
|
31
|
+
return typeof window !== "undefined" && window.self !== window.top;
|
|
272
32
|
} catch {
|
|
273
33
|
return true;
|
|
274
34
|
}
|
|
275
35
|
};
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
return { getAnonymousToken: async (fetchOptions) => {
|
|
287
|
-
return await $fetch(ANONYMOUS_TOKEN_ENDPOINT, {
|
|
288
|
-
method: "GET",
|
|
289
|
-
...fetchOptions
|
|
290
|
-
});
|
|
291
|
-
} };
|
|
292
|
-
}
|
|
293
|
-
};
|
|
294
|
-
};
|
|
295
|
-
var CURRENT_TAB_CLIENT_ID = crypto.randomUUID();
|
|
296
|
-
var BETTER_AUTH_METHODS_IN_FLIGHT_REQUESTS = new InFlightRequestManager();
|
|
297
|
-
var BETTER_AUTH_METHODS_CACHE = new SessionCacheManager();
|
|
298
|
-
var BETTER_AUTH_ANONYMOUS_TOKEN_CACHE = new AnonymousTokenCacheManager();
|
|
299
|
-
var BETTER_AUTH_ENDPOINTS = {
|
|
300
|
-
signUp: "/sign-up",
|
|
301
|
-
signIn: "/sign-in",
|
|
302
|
-
signOut: "/sign-out",
|
|
303
|
-
updateUser: "/update-user",
|
|
304
|
-
getSession: "/get-session",
|
|
305
|
-
token: "/token",
|
|
306
|
-
anonymousSignIn: "/sign-in/anonymous",
|
|
307
|
-
anonymousToken: "/token/anonymous"
|
|
308
|
-
};
|
|
309
|
-
async function handleSocialSignInViaPopup(input, init) {
|
|
310
|
-
const body = JSON.parse(init?.body || "{}");
|
|
311
|
-
const originalCallbackURL = body.callbackURL || "/";
|
|
312
|
-
const popupCallbackUrl = new URL(NEON_AUTH_POPUP_CALLBACK_ROUTE, globalThis.location.origin);
|
|
313
|
-
popupCallbackUrl.searchParams.set(NEON_AUTH_POPUP_PARAM_NAME, "1");
|
|
314
|
-
popupCallbackUrl.searchParams.set(NEON_AUTH_POPUP_CALLBACK_PARAM_NAME, originalCallbackURL);
|
|
315
|
-
body.callbackURL = popupCallbackUrl.toString();
|
|
316
|
-
body.disableRedirect = true;
|
|
317
|
-
const response = await fetch(input, {
|
|
318
|
-
...init,
|
|
319
|
-
body: JSON.stringify(body)
|
|
320
|
-
});
|
|
321
|
-
const data = await response.json();
|
|
322
|
-
const oauthUrl = data.url;
|
|
323
|
-
if (!oauthUrl) throw new Error("Failed to get OAuth URL");
|
|
324
|
-
const popupResult = await openOAuthPopup(oauthUrl);
|
|
325
|
-
if (!popupResult.verifier) throw new Error("OAuth completed but no session verifier received");
|
|
326
|
-
const navigationUrl = new URL(originalCallbackURL, globalThis.location.origin);
|
|
327
|
-
navigationUrl.searchParams.set(NEON_AUTH_SESSION_VERIFIER_PARAM_NAME, popupResult.verifier);
|
|
328
|
-
globalThis.location.href = navigationUrl.toString();
|
|
329
|
-
return Response.json(data, { status: response.status });
|
|
330
|
-
}
|
|
331
|
-
var BETTER_AUTH_METHODS_HOOKS = {
|
|
332
|
-
signUp: {
|
|
333
|
-
onRequest: () => {
|
|
334
|
-
},
|
|
335
|
-
onSuccess: (responseData) => {
|
|
336
|
-
if (isSessionResponseData(responseData)) {
|
|
337
|
-
const sessionData = {
|
|
338
|
-
session: responseData.session,
|
|
339
|
-
user: responseData.user
|
|
340
|
-
};
|
|
341
|
-
BETTER_AUTH_METHODS_CACHE.setCachedSession(sessionData);
|
|
342
|
-
emitAuthEvent({
|
|
343
|
-
type: "SIGN_IN",
|
|
344
|
-
data: sessionData
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
},
|
|
349
|
-
signIn: {
|
|
350
|
-
beforeFetch: (input, init) => {
|
|
351
|
-
if (!(typeof input === "string" ? input : input.toString()).includes("/sign-in/social") || !isIframe()) return null;
|
|
352
|
-
return handleSocialSignInViaPopup(input, init);
|
|
353
|
-
},
|
|
354
|
-
onRequest: () => {
|
|
355
|
-
},
|
|
356
|
-
onSuccess: (responseData) => {
|
|
357
|
-
if (isSessionResponseData(responseData)) {
|
|
358
|
-
const sessionData = {
|
|
359
|
-
session: responseData.session,
|
|
360
|
-
user: responseData.user
|
|
361
|
-
};
|
|
362
|
-
BETTER_AUTH_METHODS_CACHE.setCachedSession(sessionData);
|
|
363
|
-
emitAuthEvent({
|
|
364
|
-
type: "SIGN_IN",
|
|
365
|
-
data: sessionData
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
},
|
|
370
|
-
signOut: {
|
|
371
|
-
onRequest: () => {
|
|
372
|
-
BETTER_AUTH_METHODS_CACHE.invalidateSessionCache();
|
|
373
|
-
BETTER_AUTH_METHODS_IN_FLIGHT_REQUESTS.clearAll();
|
|
374
|
-
},
|
|
375
|
-
onSuccess: () => {
|
|
376
|
-
BETTER_AUTH_METHODS_CACHE.clearSessionCache();
|
|
377
|
-
emitAuthEvent({ type: "SIGN_OUT" });
|
|
378
|
-
}
|
|
379
|
-
},
|
|
380
|
-
updateUser: {
|
|
381
|
-
onRequest: () => {
|
|
382
|
-
},
|
|
383
|
-
onSuccess: (responseData) => {
|
|
384
|
-
if (isSessionResponseData(responseData)) {
|
|
385
|
-
const sessionData = {
|
|
386
|
-
session: responseData.session,
|
|
387
|
-
user: responseData.user
|
|
388
|
-
};
|
|
389
|
-
BETTER_AUTH_METHODS_CACHE.setCachedSession(sessionData);
|
|
390
|
-
emitAuthEvent({
|
|
391
|
-
type: "USER_UPDATE",
|
|
392
|
-
data: sessionData
|
|
393
|
-
});
|
|
394
|
-
} else {
|
|
395
|
-
BETTER_AUTH_METHODS_CACHE.clearSessionCache();
|
|
396
|
-
emitAuthEvent({ type: "USER_UPDATE" });
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
},
|
|
400
|
-
getSession: {
|
|
401
|
-
beforeFetch: () => {
|
|
402
|
-
if (isBrowser()) {
|
|
403
|
-
if (new URLSearchParams(globalThis.window.location.search).has(NEON_AUTH_SESSION_VERIFIER_PARAM_NAME)) return null;
|
|
36
|
+
function popupSignIn(social) {
|
|
37
|
+
const start = social;
|
|
38
|
+
return ((params, options) => {
|
|
39
|
+
const popup = window.open("about:blank", "webtwizz-sign-in", "width=500,height=700,popup=yes");
|
|
40
|
+
if (!popup) return Promise.resolve(failure("POPUP_BLOCKED"));
|
|
41
|
+
return start({ ...params, disableRedirect: true }, options).then((result) => {
|
|
42
|
+
const url = result.data?.url;
|
|
43
|
+
if (result.error || !url) {
|
|
44
|
+
popup.close();
|
|
45
|
+
return result;
|
|
404
46
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
const neonAuthSessionVerifierParam = new URLSearchParams(globalThis.window.location.search).get(NEON_AUTH_SESSION_VERIFIER_PARAM_NAME);
|
|
412
|
-
if (neonAuthSessionVerifierParam) {
|
|
413
|
-
const url = typeof ctx.url === "string" ? new URL(ctx.url) : ctx.url;
|
|
414
|
-
url.searchParams.set(NEON_AUTH_SESSION_VERIFIER_PARAM_NAME, neonAuthSessionVerifierParam);
|
|
415
|
-
return {
|
|
416
|
-
...ctx,
|
|
417
|
-
url
|
|
47
|
+
popup.location.href = url;
|
|
48
|
+
return new Promise((resolve) => {
|
|
49
|
+
const done = (outcome) => {
|
|
50
|
+
clearInterval(watch);
|
|
51
|
+
window.removeEventListener("message", listen);
|
|
52
|
+
resolve(outcome);
|
|
418
53
|
};
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
user: responseData.user
|
|
54
|
+
const listen = (event) => {
|
|
55
|
+
if (event.origin !== window.location.origin || event.data?.type !== POPUP_MESSAGE || typeof event.data.url !== "string") return;
|
|
56
|
+
const next = new URL(event.data.url, window.location.origin);
|
|
57
|
+
if (next.origin !== window.location.origin) return;
|
|
58
|
+
window.location.href = next.toString();
|
|
59
|
+
done(result);
|
|
426
60
|
};
|
|
427
|
-
const
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
if (isBrowser()) {
|
|
434
|
-
const url = new URL(globalThis.window.location.href);
|
|
435
|
-
if (url.searchParams.get(NEON_AUTH_SESSION_VERIFIER_PARAM_NAME)) {
|
|
436
|
-
url.searchParams.delete(NEON_AUTH_SESSION_VERIFIER_PARAM_NAME);
|
|
437
|
-
history.replaceState(history.state, "", url.href);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
},
|
|
443
|
-
anonymousToken: {
|
|
444
|
-
beforeFetch: () => {
|
|
445
|
-
const cachedResponse = BETTER_AUTH_ANONYMOUS_TOKEN_CACHE.getCachedResponse();
|
|
446
|
-
if (!cachedResponse) return null;
|
|
447
|
-
return Response.json(cachedResponse, { status: 200 });
|
|
448
|
-
},
|
|
449
|
-
onRequest: () => {
|
|
450
|
-
},
|
|
451
|
-
onSuccess: (responseData) => {
|
|
452
|
-
const parsed = anonymousTokenResponseSchema.safeParse(responseData);
|
|
453
|
-
if (parsed.success) BETTER_AUTH_ANONYMOUS_TOKEN_CACHE.setCachedResponse(parsed.data);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
};
|
|
457
|
-
async function emitAuthEvent(event) {
|
|
458
|
-
const eventType = mapToEventType(event);
|
|
459
|
-
const sessionData = "data" in event ? event.data : null;
|
|
460
|
-
const trigger = mapToTrigger(event);
|
|
461
|
-
if (trigger) getGlobalBroadcastChannel().post({
|
|
462
|
-
event: "session",
|
|
463
|
-
data: { trigger },
|
|
464
|
-
clientId: CURRENT_TAB_CLIENT_ID
|
|
465
|
-
});
|
|
466
|
-
getGlobalBroadcastChannel().post({
|
|
467
|
-
event: "session",
|
|
468
|
-
data: {
|
|
469
|
-
trigger: eventType,
|
|
470
|
-
sessionData
|
|
471
|
-
},
|
|
472
|
-
clientId: CURRENT_TAB_CLIENT_ID
|
|
473
|
-
});
|
|
474
|
-
}
|
|
475
|
-
function mapToEventType(event) {
|
|
476
|
-
switch (event.type) {
|
|
477
|
-
case "SIGN_IN":
|
|
478
|
-
return "SIGNED_IN";
|
|
479
|
-
case "SIGN_OUT":
|
|
480
|
-
return "SIGNED_OUT";
|
|
481
|
-
case "TOKEN_REFRESH":
|
|
482
|
-
return "TOKEN_REFRESHED";
|
|
483
|
-
case "USER_UPDATE":
|
|
484
|
-
return "USER_UPDATED";
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
function mapToTrigger(event) {
|
|
488
|
-
switch (event.type) {
|
|
489
|
-
case "SIGN_OUT":
|
|
490
|
-
return "signout";
|
|
491
|
-
case "TOKEN_REFRESH":
|
|
492
|
-
return null;
|
|
493
|
-
case "USER_UPDATE":
|
|
494
|
-
return "updateUser";
|
|
495
|
-
case "SIGN_IN":
|
|
496
|
-
return null;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
function isSessionResponseData(responseData) {
|
|
500
|
-
return Boolean(responseData && typeof responseData === "object" && "session" in responseData && "user" in responseData && responseData.session !== null && responseData.user !== null);
|
|
501
|
-
}
|
|
502
|
-
function deriveBetterAuthMethodFromUrl(url) {
|
|
503
|
-
if (url.includes("/sign-in/anonymous")) return "anonymousSignIn";
|
|
504
|
-
if (url.includes(BETTER_AUTH_ENDPOINTS.anonymousToken)) return "anonymousToken";
|
|
505
|
-
if (url.includes(BETTER_AUTH_ENDPOINTS.signIn)) return "signIn";
|
|
506
|
-
if (url.includes(BETTER_AUTH_ENDPOINTS.signUp)) return "signUp";
|
|
507
|
-
if (url.includes(BETTER_AUTH_ENDPOINTS.signOut)) return "signOut";
|
|
508
|
-
if (url.includes(BETTER_AUTH_ENDPOINTS.updateUser)) return "updateUser";
|
|
509
|
-
if (url.includes(BETTER_AUTH_ENDPOINTS.getSession) || url.includes(BETTER_AUTH_ENDPOINTS.token)) return "getSession";
|
|
510
|
-
}
|
|
511
|
-
function initBroadcastChannel() {
|
|
512
|
-
if (isBrowser() && globalThis.opener && globalThis.opener !== globalThis) {
|
|
513
|
-
const params = new URLSearchParams(globalThis.location.search);
|
|
514
|
-
if (params.has(NEON_AUTH_POPUP_PARAM_NAME)) {
|
|
515
|
-
const verifier = params.get(NEON_AUTH_SESSION_VERIFIER_PARAM_NAME);
|
|
516
|
-
const originalCallback = params.get(NEON_AUTH_POPUP_CALLBACK_PARAM_NAME);
|
|
517
|
-
globalThis.opener.postMessage({
|
|
518
|
-
type: OAUTH_POPUP_MESSAGE_TYPE,
|
|
519
|
-
verifier,
|
|
520
|
-
originalCallback
|
|
521
|
-
}, "*");
|
|
522
|
-
globalThis.close();
|
|
523
|
-
return;
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
getGlobalBroadcastChannel().subscribe((message) => {
|
|
527
|
-
if (message.clientId === CURRENT_TAB_CLIENT_ID) return;
|
|
528
|
-
const trigger = message.data?.trigger;
|
|
529
|
-
if (trigger === "signout" || trigger === "updateUser" || trigger === "getSession") BETTER_AUTH_METHODS_CACHE.clearSessionCache();
|
|
61
|
+
const watch = setInterval(() => {
|
|
62
|
+
if (popup.closed) done(failure("POPUP_CLOSED"));
|
|
63
|
+
}, 500);
|
|
64
|
+
window.addEventListener("message", listen);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
530
67
|
});
|
|
531
68
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
}
|
|
548
|
-
function getClientInfo(sdkName, sdkVersion) {
|
|
549
|
-
const base = {
|
|
550
|
-
sdk: sdkName,
|
|
551
|
-
version: sdkVersion,
|
|
552
|
-
runtime: "unknown",
|
|
553
|
-
runtimeVersion: "unknown",
|
|
554
|
-
platform: "unknown",
|
|
555
|
-
arch: "unknown"
|
|
556
|
-
};
|
|
557
|
-
let result;
|
|
558
|
-
if (typeof process !== "undefined" && process.versions?.node) result = {
|
|
559
|
-
...base,
|
|
560
|
-
runtime: "node",
|
|
561
|
-
runtimeVersion: process.versions.node,
|
|
562
|
-
platform: process.platform,
|
|
563
|
-
arch: process.arch
|
|
564
|
-
};
|
|
565
|
-
else if (typeof Deno !== "undefined") result = {
|
|
566
|
-
...base,
|
|
567
|
-
runtime: "deno",
|
|
568
|
-
runtimeVersion: Deno.version?.deno ?? "unknown",
|
|
569
|
-
platform: Deno.build?.os ?? "unknown",
|
|
570
|
-
arch: Deno.build?.arch ?? "unknown"
|
|
571
|
-
};
|
|
572
|
-
else if (typeof Bun !== "undefined") result = {
|
|
573
|
-
...base,
|
|
574
|
-
runtime: "bun",
|
|
575
|
-
runtimeVersion: Bun.version ?? "unknown",
|
|
576
|
-
platform: process?.platform ?? "unknown",
|
|
577
|
-
arch: process?.arch ?? "unknown"
|
|
578
|
-
};
|
|
579
|
-
else if (typeof EdgeRuntime !== "undefined" || typeof process !== "undefined" && !process.versions?.node && typeof globalThis.window === "undefined" && typeof document === "undefined") result = {
|
|
580
|
-
...base,
|
|
581
|
-
runtime: "edge"
|
|
582
|
-
};
|
|
583
|
-
else if (globalThis.window !== void 0 && typeof document !== "undefined") result = {
|
|
584
|
-
...base,
|
|
585
|
-
runtime: "browser",
|
|
586
|
-
runtimeVersion: "unknown",
|
|
587
|
-
platform: "web",
|
|
588
|
-
arch: "unknown"
|
|
589
|
-
};
|
|
590
|
-
else result = base;
|
|
591
|
-
const framework = detectFramework();
|
|
592
|
-
if (framework) result.framework = framework;
|
|
593
|
-
return result;
|
|
594
|
-
}
|
|
595
|
-
function createClientInfoInjector(defaultSdkName, defaultSdkVersion) {
|
|
596
|
-
const cachedClientInfo = JSON.stringify(getClientInfo(defaultSdkName, defaultSdkVersion));
|
|
597
|
-
return function injectClientInfo$1(headers, sdkOverride) {
|
|
598
|
-
const result = new Headers(headers);
|
|
599
|
-
if (result.has(X_NEON_CLIENT_INFO_HEADER)) return result;
|
|
600
|
-
const clientInfoString = sdkOverride ? JSON.stringify(getClientInfo(sdkOverride.name, sdkOverride.version)) : cachedClientInfo;
|
|
601
|
-
result.set(X_NEON_CLIENT_INFO_HEADER, clientInfoString);
|
|
602
|
-
return result;
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
var injectClientInfo = createClientInfoInjector(name, version);
|
|
606
|
-
var FORCE_FETCH_HEADER = "X-Force-Fetch";
|
|
607
|
-
var supportedBetterAuthClientPlugins = [
|
|
608
|
-
jwtClient(),
|
|
609
|
-
adminClient(),
|
|
610
|
-
organizationClient(),
|
|
611
|
-
emailOTPClient(),
|
|
612
|
-
magicLinkClient(),
|
|
613
|
-
phoneNumberClient(),
|
|
614
|
-
anonymousTokenClient()
|
|
615
|
-
];
|
|
616
|
-
var NeonAuthAdapterCore = class {
|
|
617
|
-
betterAuthOptions;
|
|
618
|
-
/**
|
|
619
|
-
* Better Auth adapter implementing the NeonAuthClient interface.
|
|
620
|
-
* See CLAUDE.md for architecture details and API mappings.
|
|
621
|
-
*/
|
|
622
|
-
constructor(betterAuthClientOptions) {
|
|
623
|
-
const userOnSuccess = betterAuthClientOptions.fetchOptions?.onSuccess;
|
|
624
|
-
const userOnRequest = betterAuthClientOptions.fetchOptions?.onRequest;
|
|
625
|
-
this.betterAuthOptions = {
|
|
626
|
-
...betterAuthClientOptions,
|
|
627
|
-
plugins: supportedBetterAuthClientPlugins,
|
|
628
|
-
fetchOptions: {
|
|
629
|
-
...betterAuthClientOptions.fetchOptions,
|
|
630
|
-
throw: false,
|
|
631
|
-
onRequest: (request) => {
|
|
632
|
-
const url = request.url;
|
|
633
|
-
const method = deriveBetterAuthMethodFromUrl(url.toString());
|
|
634
|
-
if (method) BETTER_AUTH_METHODS_HOOKS[method].onRequest(request);
|
|
635
|
-
userOnRequest?.(request);
|
|
636
|
-
},
|
|
637
|
-
customFetchImpl: async (url, init) => {
|
|
638
|
-
const headers = injectClientInfo(init?.headers);
|
|
639
|
-
if (headers.has(FORCE_FETCH_HEADER)) {
|
|
640
|
-
headers.delete(FORCE_FETCH_HEADER);
|
|
641
|
-
const response$1 = await fetch(url, {
|
|
642
|
-
...init,
|
|
643
|
-
headers
|
|
644
|
-
});
|
|
645
|
-
if (!response$1.ok) {
|
|
646
|
-
const body = await response$1.clone().json().catch(() => ({}));
|
|
647
|
-
throw normalizeBetterAuthError({
|
|
648
|
-
status: response$1.status,
|
|
649
|
-
statusText: response$1.statusText,
|
|
650
|
-
message: body.message || `HTTP ${response$1.status} ${response$1.statusText}`,
|
|
651
|
-
code: body.code,
|
|
652
|
-
body
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
return response$1;
|
|
656
|
-
}
|
|
657
|
-
const betterAuthMethod = deriveBetterAuthMethodFromUrl(url.toString());
|
|
658
|
-
if (betterAuthMethod) {
|
|
659
|
-
const response$1 = await BETTER_AUTH_METHODS_HOOKS[betterAuthMethod].beforeFetch?.(url, init);
|
|
660
|
-
if (response$1) return response$1;
|
|
661
|
-
}
|
|
662
|
-
const key = `${init?.method || "GET"}:${url}:${init?.body || ""}`;
|
|
663
|
-
const response = await BETTER_AUTH_METHODS_IN_FLIGHT_REQUESTS.deduplicate(key, () => fetch(url, {
|
|
664
|
-
...init,
|
|
665
|
-
headers
|
|
666
|
-
}));
|
|
667
|
-
if (!response.ok) {
|
|
668
|
-
const errorBody = await response.clone().json().catch(() => ({}));
|
|
669
|
-
throw normalizeBetterAuthError({
|
|
670
|
-
status: response.status,
|
|
671
|
-
statusText: response.statusText,
|
|
672
|
-
message: errorBody.message || `HTTP ${response.status} ${response.statusText}`,
|
|
673
|
-
code: errorBody.code,
|
|
674
|
-
body: errorBody
|
|
675
|
-
});
|
|
676
|
-
}
|
|
677
|
-
return response.clone();
|
|
678
|
-
},
|
|
679
|
-
onSuccess: async (ctx) => {
|
|
680
|
-
const jwt = ctx.response.headers.get("set-auth-jwt");
|
|
681
|
-
if (jwt) if (ctx.data?.session) ctx.data.session.token = jwt;
|
|
682
|
-
else console.warn("[onSuccess] JWT found but no session data to inject into!");
|
|
683
|
-
const url = ctx.request.url.toString();
|
|
684
|
-
const responseData = ctx.data;
|
|
685
|
-
const method = deriveBetterAuthMethodFromUrl(url);
|
|
686
|
-
if (method) BETTER_AUTH_METHODS_HOOKS[method].onSuccess(responseData);
|
|
687
|
-
await userOnSuccess?.(ctx);
|
|
69
|
+
function createAuthClient(options = {}) {
|
|
70
|
+
const client = neverThrows(create({
|
|
71
|
+
baseURL: options.baseURL,
|
|
72
|
+
plugins: [adminClient(), emailOTPClient()],
|
|
73
|
+
fetchOptions: inFrame() ? { headers: { [FRAME_HEADER]: "1" } } : void 0
|
|
74
|
+
}));
|
|
75
|
+
if (!inFrame()) return client;
|
|
76
|
+
const social = popupSignIn(client.signIn.social);
|
|
77
|
+
return new Proxy(client, {
|
|
78
|
+
get(target, key, receiver) {
|
|
79
|
+
const value = Reflect.get(target, key, receiver);
|
|
80
|
+
if (key !== "signIn") return value;
|
|
81
|
+
return new Proxy(value, {
|
|
82
|
+
get(signIn, method, signInReceiver) {
|
|
83
|
+
return method === "social" ? social : Reflect.get(signIn, method, signInReceiver);
|
|
688
84
|
}
|
|
689
|
-
}
|
|
690
|
-
};
|
|
691
|
-
initBroadcastChannel();
|
|
692
|
-
}
|
|
693
|
-
/**
|
|
694
|
-
* Get JWT token for authenticated or anonymous access.
|
|
695
|
-
* Single source of truth for token retrieval logic.
|
|
696
|
-
*
|
|
697
|
-
* @param allowAnonymous - When true, fetches anonymous token if no session exists
|
|
698
|
-
* @returns JWT token string or null if unavailable
|
|
699
|
-
*/
|
|
700
|
-
async getJWTToken(allowAnonymous) {
|
|
701
|
-
const client = this.getBetterAuthInstance();
|
|
702
|
-
const session = await client.getSession();
|
|
703
|
-
if (session.data?.session?.token) return session.data.session.token;
|
|
704
|
-
if (allowAnonymous) return (await client.getAnonymousToken()).data?.token ?? null;
|
|
705
|
-
return null;
|
|
706
|
-
}
|
|
707
|
-
};
|
|
708
|
-
|
|
709
|
-
// node_modules/@neondatabase/auth/dist/better-auth-react-adapter-DMizSZtB.mjs
|
|
710
|
-
import { createAuthClient } from "better-auth/react";
|
|
711
|
-
var BetterAuthReactAdapterImpl = class extends NeonAuthAdapterCore {
|
|
712
|
-
_betterAuth;
|
|
713
|
-
constructor(betterAuthClientOptions) {
|
|
714
|
-
super(betterAuthClientOptions);
|
|
715
|
-
this._betterAuth = createAuthClient(this.betterAuthOptions);
|
|
716
|
-
}
|
|
717
|
-
getBetterAuthInstance() {
|
|
718
|
-
return this._betterAuth;
|
|
719
|
-
}
|
|
720
|
-
};
|
|
721
|
-
function BetterAuthReactAdapter(options) {
|
|
722
|
-
return (url, fetchOptions) => new BetterAuthReactAdapterImpl({
|
|
723
|
-
baseURL: url,
|
|
724
|
-
...options,
|
|
725
|
-
fetchOptions: {
|
|
726
|
-
...options?.fetchOptions,
|
|
727
|
-
headers: {
|
|
728
|
-
...options?.fetchOptions?.headers,
|
|
729
|
-
...fetchOptions?.headers
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
});
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
// node_modules/@neondatabase/auth/dist/supabase-adapter-CKXlmI3i.mjs
|
|
736
|
-
import { createAuthClient as createAuthClient2, getGlobalBroadcastChannel as getGlobalBroadcastChannel2 } from "better-auth/client";
|
|
737
|
-
import "@supabase/auth-js";
|
|
738
|
-
import { base64url, decodeJwt, decodeProtectedHeader } from "jose";
|
|
739
|
-
var BetterAuthVanillaAdapterImpl = class extends NeonAuthAdapterCore {
|
|
740
|
-
_betterAuth;
|
|
741
|
-
constructor(betterAuthClientOptions) {
|
|
742
|
-
super(betterAuthClientOptions);
|
|
743
|
-
this._betterAuth = createAuthClient2(this.betterAuthOptions);
|
|
744
|
-
}
|
|
745
|
-
getBetterAuthInstance() {
|
|
746
|
-
return this._betterAuth;
|
|
747
|
-
}
|
|
748
|
-
};
|
|
749
|
-
function BetterAuthVanillaAdapter(options) {
|
|
750
|
-
return (url, fetchOptions) => new BetterAuthVanillaAdapterImpl({
|
|
751
|
-
baseURL: url,
|
|
752
|
-
...options,
|
|
753
|
-
fetchOptions: {
|
|
754
|
-
...options?.fetchOptions,
|
|
755
|
-
headers: {
|
|
756
|
-
...options?.fetchOptions?.headers,
|
|
757
|
-
...fetchOptions?.headers
|
|
758
|
-
}
|
|
85
|
+
});
|
|
759
86
|
}
|
|
760
87
|
});
|
|
761
88
|
}
|
|
762
|
-
|
|
763
|
-
// node_modules/@neondatabase/auth/dist/neon-auth-BLCDc-bu.mjs
|
|
764
|
-
function createInternalNeonAuth(url, config) {
|
|
765
|
-
const adapterBuilder = config?.adapter ?? BetterAuthVanillaAdapter();
|
|
766
|
-
const { fetchOptions } = config ?? {};
|
|
767
|
-
const adapter = adapterBuilder(url, fetchOptions);
|
|
768
|
-
const allowAnonymous = config?.allowAnonymous ?? false;
|
|
769
|
-
if (!(typeof adapter.initialize === "function")) return {
|
|
770
|
-
getJWTToken: () => adapter.getJWTToken(allowAnonymous),
|
|
771
|
-
adapter: adapter.getBetterAuthInstance()
|
|
772
|
-
};
|
|
773
|
-
return {
|
|
774
|
-
getJWTToken: () => adapter.getJWTToken(allowAnonymous),
|
|
775
|
-
adapter
|
|
776
|
-
};
|
|
777
|
-
}
|
|
778
|
-
function createAuthClient3(url, config) {
|
|
779
|
-
return createInternalNeonAuth(url, config).adapter;
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
// node_modules/@neondatabase/auth/dist/next/index.mjs
|
|
783
|
-
function createAuthClient4() {
|
|
784
|
-
return createAuthClient3(void 0, { adapter: BetterAuthReactAdapter() });
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
// src/client.ts
|
|
788
|
-
function createAuthClient5() {
|
|
789
|
-
return createAuthClient4();
|
|
790
|
-
}
|
|
791
89
|
export {
|
|
792
|
-
|
|
90
|
+
createAuthClient
|
|
793
91
|
};
|