@neondatabase/neon-js 0.1.0-alpha.1 → 0.1.0-alpha.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.
package/dist/index.js DELETED
@@ -1,1360 +0,0 @@
1
- import { AuthApiError, AuthError, isAuthError } from "@supabase/auth-js";
2
- import { StackClientApp, StackServerApp } from "@stackframe/js";
3
- import { z } from "zod";
4
- import { PostgrestClient } from "@supabase/postgrest-js";
5
-
6
- //#region src/auth/adapters/stack-auth/stack-auth-schemas.ts
7
- const accessTokenSchema = z.object({
8
- exp: z.number(),
9
- iat: z.number(),
10
- sub: z.string(),
11
- email: z.string().nullable()
12
- });
13
-
14
- //#endregion
15
- //#region src/auth/adapters/stack-auth/stack-auth-helpers.ts
16
- /**
17
- * Environment detection helpers for Stack Auth adapter
18
- * Based on Supabase's auth-js implementation patterns
19
- */
20
- /**
21
- * Checks if the code is running in a browser environment
22
- * @returns true if in browser, false otherwise (e.g., Node.js)
23
- */
24
- const isBrowser = () => {
25
- return typeof window !== "undefined" && typeof document !== "undefined";
26
- };
27
- /**
28
- * Checks if BroadcastChannel API is available
29
- * Used for cross-tab authentication state synchronization
30
- * @returns true if BroadcastChannel is available (browser only)
31
- */
32
- const supportsBroadcastChannel = () => {
33
- return isBrowser() && typeof globalThis.BroadcastChannel !== "undefined";
34
- };
35
-
36
- //#endregion
37
- //#region src/auth/adapters/stack-auth/stack-auth-adapter.ts
38
- /**
39
- * Type guard to check if Stack Auth user has internal session access
40
- * Stack Auth exposes _internalSession with caching methods that we can leverage
41
- */
42
- function hasInternalSession(user) {
43
- return user !== null && user !== void 0 && typeof user === "object" && "_internalSession" in user && user._internalSession !== null && user._internalSession !== void 0 && typeof user._internalSession === "object" && "getAccessTokenIfNotExpiredYet" in user._internalSession && typeof user._internalSession.getAccessTokenIfNotExpiredYet === "function" && "_refreshToken" in user._internalSession;
44
- }
45
- /**
46
- * Helper to convert signedUpAt (string or Date) to ISO string
47
- * Stack Auth SDK may return dates as strings or Date objects depending on context
48
- */
49
- function toISOString(date) {
50
- if (!date) return (/* @__PURE__ */ new Date()).toISOString();
51
- if (typeof date === "string") return date;
52
- if (typeof date === "number") return new Date(date).toISOString();
53
- return date.toISOString();
54
- }
55
- /**
56
- * Map Stack Auth errors to Supabase error format
57
- * This is Stack Auth-specific logic
58
- */
59
- function normalizeStackAuthError(error) {
60
- if (error !== null && error !== void 0 && typeof error === "object" && "status" in error && error.status === "error" && "error" in error && error.error !== null && error.error !== void 0 && typeof error.error === "object" && "message" in error.error) {
61
- const stackError = error;
62
- const message = stackError.error.message || "Authentication failed";
63
- let status = stackError.httpStatus || 400;
64
- let code = "unknown_error";
65
- if (status === 429) code = "over_request_rate_limit";
66
- else if (status === 422) code = "user_already_exists";
67
- else if (status === 404) code = "user_not_found";
68
- else if (status === 401) code = "bad_jwt";
69
- else if (message.includes("Invalid login credentials") || message.includes("incorrect")) {
70
- code = "invalid_credentials";
71
- status = 400;
72
- } else if (message.includes("already exists") || message.includes("already registered")) {
73
- code = "user_already_exists";
74
- status = 422;
75
- } else if (message.includes("not found")) {
76
- code = "user_not_found";
77
- status = 404;
78
- } else if (message.includes("token") && message.includes("invalid")) {
79
- code = "bad_jwt";
80
- status = 401;
81
- } else if (message.includes("token") && message.includes("expired")) {
82
- code = "bad_jwt";
83
- status = 401;
84
- } else if (message.includes("rate limit") || message.includes("Too many requests")) {
85
- code = "over_request_rate_limit";
86
- status = 429;
87
- } else if (message.includes("email") && message.includes("invalid")) {
88
- code = "email_address_invalid";
89
- status = 400;
90
- }
91
- return new AuthApiError(message, status, code);
92
- }
93
- if (error instanceof Error) {
94
- const message = error.message;
95
- let status = 500;
96
- let code = "unexpected_failure";
97
- if (message.includes("already exists") || message.includes("already registered")) {
98
- status = 422;
99
- code = "user_already_exists";
100
- } else if (message.includes("Invalid login credentials") || message.includes("incorrect")) {
101
- status = 400;
102
- code = "invalid_credentials";
103
- } else if (message.includes("not found")) {
104
- status = 404;
105
- code = "user_not_found";
106
- } else if (message.includes("token") && message.includes("invalid")) {
107
- status = 401;
108
- code = "bad_jwt";
109
- } else if (message.includes("token") && message.includes("expired")) {
110
- status = 401;
111
- code = "bad_jwt";
112
- } else if (message.includes("rate limit") || message.includes("Too many requests") || message.includes("too many requests")) {
113
- status = 429;
114
- code = "over_request_rate_limit";
115
- } else if (message.includes("email") && message.includes("invalid")) {
116
- status = 400;
117
- code = "email_address_invalid";
118
- }
119
- if (status !== 500) return new AuthApiError(message, status, code);
120
- return new AuthError(message, status, code);
121
- }
122
- return new AuthError("An unexpected error occurred", 500, "unexpected_failure");
123
- }
124
- /**
125
- * Stack Auth adapter implementing the AuthClient interface
126
- */
127
- var StackAuthAdapter = class {
128
- stackAuth;
129
- stateChangeEmitters = /* @__PURE__ */ new Map();
130
- broadcastChannel = null;
131
- tokenRefreshCheckInterval = null;
132
- config = {
133
- enableTokenRefreshDetection: true,
134
- tokenRefreshCheckInterval: 3e4
135
- };
136
- constructor(params, config) {
137
- if (config) this.config = {
138
- ...this.config,
139
- ...config
140
- };
141
- this.stackAuth = params.secretServerKey ? new StackServerApp(params) : new StackClientApp(params);
142
- }
143
- admin = void 0;
144
- mfa = void 0;
145
- initialize = async () => {
146
- try {
147
- const session = await this.getSession();
148
- return {
149
- data: session.data,
150
- error: session.error
151
- };
152
- } catch (error) {
153
- return {
154
- data: { session: null },
155
- error: normalizeStackAuthError(error)
156
- };
157
- }
158
- };
159
- signUp = async (credentials) => {
160
- try {
161
- if ("email" in credentials && credentials.email && credentials.password) {
162
- const verificationCallbackUrl = credentials.options?.emailRedirectTo || this.stackAuth.urls.emailVerification;
163
- const result = await this.stackAuth.signUpWithCredential({
164
- email: credentials.email,
165
- password: credentials.password,
166
- noRedirect: true,
167
- verificationCallbackUrl
168
- });
169
- if (result.status === "error") return {
170
- data: {
171
- user: null,
172
- session: null
173
- },
174
- error: normalizeStackAuthError(result)
175
- };
176
- if (credentials.options?.data) {
177
- const user = await this.stackAuth.getUser();
178
- if (user) await user.update({ clientMetadata: credentials.options.data });
179
- }
180
- const sessionResult = await this._fetchFreshSession();
181
- if (!sessionResult.data.session?.user) return {
182
- data: {
183
- user: null,
184
- session: null
185
- },
186
- error: new AuthError("Failed to retrieve user session", 500, "unexpected_failure")
187
- };
188
- const data = {
189
- user: sessionResult.data.session.user,
190
- session: sessionResult.data.session
191
- };
192
- await this.notifyAllSubscribers("SIGNED_IN", sessionResult.data.session);
193
- return {
194
- data,
195
- error: null
196
- };
197
- } else if ("phone" in credentials && credentials.phone) return {
198
- data: {
199
- user: null,
200
- session: null
201
- },
202
- error: new AuthError("Phone sign-up not supported", 501, "phone_provider_disabled")
203
- };
204
- else return {
205
- data: {
206
- user: null,
207
- session: null
208
- },
209
- error: new AuthError("Invalid credentials format", 400, "validation_failed")
210
- };
211
- } catch (error) {
212
- return {
213
- data: {
214
- user: null,
215
- session: null
216
- },
217
- error: normalizeStackAuthError(error)
218
- };
219
- }
220
- };
221
- signInAnonymously = async () => {
222
- return {
223
- data: {
224
- user: null,
225
- session: null
226
- },
227
- error: new AuthError("Anonymous sign-in is not supported by Stack Auth", 501, "anonymous_provider_disabled")
228
- };
229
- };
230
- signInWithPassword = async (credentials) => {
231
- try {
232
- if ("email" in credentials && credentials.email) {
233
- const result = await this.stackAuth.signInWithCredential({
234
- email: credentials.email,
235
- password: credentials.password,
236
- noRedirect: true
237
- });
238
- if (result.status === "error") return {
239
- data: {
240
- user: null,
241
- session: null
242
- },
243
- error: normalizeStackAuthError(result)
244
- };
245
- const sessionResult = await this._fetchFreshSession();
246
- if (!sessionResult.data.session?.user) return {
247
- data: {
248
- user: null,
249
- session: null
250
- },
251
- error: new AuthError("Failed to retrieve user session", 500, "unexpected_failure")
252
- };
253
- const data = {
254
- user: sessionResult.data.session.user,
255
- session: sessionResult.data.session
256
- };
257
- await this.notifyAllSubscribers("SIGNED_IN", sessionResult.data.session);
258
- return {
259
- data,
260
- error: null
261
- };
262
- } else if ("phone" in credentials && credentials.phone) return {
263
- data: {
264
- user: null,
265
- session: null
266
- },
267
- error: new AuthError("Phone sign-in not supported", 501, "phone_provider_disabled")
268
- };
269
- else return {
270
- data: {
271
- user: null,
272
- session: null
273
- },
274
- error: new AuthError("Invalid credentials format", 400, "validation_failed")
275
- };
276
- } catch (error) {
277
- return {
278
- data: {
279
- user: null,
280
- session: null
281
- },
282
- error: normalizeStackAuthError(error)
283
- };
284
- }
285
- };
286
- signInWithOAuth = async (credentials) => {
287
- try {
288
- const { provider, options } = credentials;
289
- await this.stackAuth.signInWithOAuth(provider, { returnTo: options?.redirectTo });
290
- return {
291
- data: {
292
- provider,
293
- url: options?.redirectTo || ""
294
- },
295
- error: null
296
- };
297
- } catch (error) {
298
- return {
299
- data: {
300
- provider: credentials.provider,
301
- url: null
302
- },
303
- error: normalizeStackAuthError(error)
304
- };
305
- }
306
- };
307
- signInWithOtp = async (credentials) => {
308
- try {
309
- if ("email" in credentials && credentials.email) {
310
- const callbackUrl = credentials.options?.emailRedirectTo || this.stackAuth.urls.magicLinkCallback;
311
- const result = await this.stackAuth.sendMagicLinkEmail(credentials.email, { callbackUrl });
312
- if (result.status === "error") return {
313
- data: {
314
- user: null,
315
- session: null,
316
- messageId: void 0
317
- },
318
- error: normalizeStackAuthError(result)
319
- };
320
- return {
321
- data: {
322
- user: null,
323
- session: null,
324
- messageId: void 0
325
- },
326
- error: null
327
- };
328
- } else if ("phone" in credentials && credentials.phone) return {
329
- data: {
330
- user: null,
331
- session: null,
332
- messageId: void 0
333
- },
334
- error: new AuthError("Phone OTP not supported", 501, "phone_provider_disabled")
335
- };
336
- else return {
337
- data: {
338
- user: null,
339
- session: null,
340
- messageId: void 0
341
- },
342
- error: new AuthError("Invalid credentials format", 400, "validation_failed")
343
- };
344
- } catch (error) {
345
- return {
346
- data: {
347
- user: null,
348
- session: null,
349
- messageId: void 0
350
- },
351
- error: normalizeStackAuthError(error)
352
- };
353
- }
354
- };
355
- signInWithIdToken = async (credentials) => {
356
- /**
357
- * Stack Auth does not support direct OIDC ID token authentication.
358
- *
359
- * Supabase's signInWithIdToken accepts pre-existing OIDC ID tokens from providers like:
360
- * - Google, Apple, Azure, Facebook, Kakao, Keycloak
361
- * - Validates the ID token server-side
362
- * - Can handle tokens with at_hash (requires access_token) and nonce claims
363
- *
364
- * Stack Auth uses OAuth authorization code flow with redirects instead:
365
- * - Requires redirecting users to the OAuth provider
366
- * - Handles the OAuth callback to exchange authorization code for tokens
367
- * - Does not accept pre-existing ID tokens directly
368
- *
369
- * For OAuth providers, use signInWithOAuth instead:
370
- * ```
371
- * await authAdapter.signInWithOAuth({ provider: 'google', options: { redirectTo: '...' } });
372
- * ```
373
- */
374
- const attemptedProvider = credentials.provider;
375
- const hasAccessToken = !!credentials.access_token;
376
- const hasNonce = !!credentials.nonce;
377
- return {
378
- data: {
379
- user: null,
380
- session: null
381
- },
382
- error: new AuthError(`Stack Auth does not support OIDC ID token authentication. Attempted with provider: ${attemptedProvider}${hasAccessToken ? " (with access_token)" : ""}${hasNonce ? " (with nonce)" : ""}. Stack Auth uses OAuth authorization code flow and does not accept pre-existing ID tokens. Please use signInWithOAuth() to redirect users to the OAuth provider for authentication.`, 501, "id_token_provider_disabled")
383
- };
384
- };
385
- signInWithSSO = async (params) => {
386
- return {
387
- data: null,
388
- error: new AuthError(`Stack Auth does not support enterprise SAML SSO. Attempted with ${"providerId" in params ? `provider ID: ${params.providerId}` : `domain: ${"domain" in params ? params.domain : "unknown"}`}. Stack Auth only supports OAuth social providers (Google, GitHub, Microsoft, etc.). Please use signInWithOAuth() for OAuth providers instead.`, 501, "sso_provider_disabled")
389
- };
390
- };
391
- signInWithWeb3 = async (credentials) => {
392
- /**
393
- * Stack Auth does not support Web3/crypto wallet authentication (Ethereum, Solana, etc.)
394
- *
395
- * Supabase's signInWithWeb3 enables authentication with crypto wallets like:
396
- * - Ethereum: MetaMask, WalletConnect, Coinbase Wallet (using EIP-1193)
397
- * - Solana: Phantom, Solflare (using Sign-In with Solana standard)
398
- *
399
- * Stack Auth only supports:
400
- * - OAuth social providers (Google, GitHub, Microsoft, etc.)
401
- * - Email/Password credentials
402
- * - Magic link (passwordless email)
403
- * - Passkey/WebAuthn
404
- * - Anonymous sign-in
405
- *
406
- * For OAuth providers, use signInWithOAuth instead:
407
- * ```
408
- * await authAdapter.signInWithOAuth({ provider: 'google', options: { redirectTo: '...' } });
409
- * ```
410
- */
411
- const attemptedChain = credentials.chain;
412
- return {
413
- data: {
414
- user: null,
415
- session: null
416
- },
417
- error: new AuthError(`Stack Auth does not support Web3 authentication. Attempted with chain: ${attemptedChain}. Stack Auth does not support crypto wallet sign-in (Ethereum, Solana, etc.). Supported authentication methods: OAuth, email/password, magic link, passkey, or anonymous. For social authentication, please use signInWithOAuth() instead.`, 501, "web3_provider_disabled")
418
- };
419
- };
420
- signOut = async () => {
421
- try {
422
- const internalSession = await this._getSessionFromStackAuthInternals();
423
- if (!internalSession) throw new AuthError("No session found", 401, "session_not_found");
424
- await this.stackAuth._interface.signOut(internalSession);
425
- await this.notifyAllSubscribers("SIGNED_OUT", null);
426
- return { error: null };
427
- } catch (error) {
428
- return { error: normalizeStackAuthError(error) };
429
- }
430
- };
431
- verifyOtp = async (params) => {
432
- try {
433
- if ("email" in params && params.email) {
434
- const { token, type } = params;
435
- if (type === "magiclink" || type === "email") {
436
- let internalSession = await this._getSessionFromStackAuthInternals();
437
- if (!internalSession) internalSession = this.stackAuth._interface.createSession({
438
- refreshToken: null,
439
- accessToken: null
440
- });
441
- const result = await this.stackAuth._interface.signInWithMagicLink(token, internalSession);
442
- if (result.status === "error") return {
443
- data: {
444
- user: null,
445
- session: null
446
- },
447
- error: normalizeStackAuthError(result)
448
- };
449
- const sessionResult = await this.getSession();
450
- if (!sessionResult.data.session) return {
451
- data: {
452
- user: null,
453
- session: null
454
- },
455
- error: new AuthError("Failed to retrieve session after OTP verification", 500, "unexpected_failure")
456
- };
457
- await this.notifyAllSubscribers("SIGNED_IN", sessionResult.data.session);
458
- return {
459
- data: {
460
- user: sessionResult.data.session.user,
461
- session: sessionResult.data.session
462
- },
463
- error: null
464
- };
465
- }
466
- if (type === "signup" || type === "invite") {
467
- const result = await this.stackAuth._interface.verifyEmail(token);
468
- if (result.status === "error") return {
469
- data: {
470
- user: null,
471
- session: null
472
- },
473
- error: normalizeStackAuthError(result)
474
- };
475
- const sessionResult = await this.getSession();
476
- return {
477
- data: {
478
- user: sessionResult.data.session?.user ?? null,
479
- session: sessionResult.data.session
480
- },
481
- error: null
482
- };
483
- }
484
- if (type === "recovery") {
485
- const result = await this.stackAuth._interface.resetPassword({
486
- code: token,
487
- onlyVerifyCode: true
488
- });
489
- if (result.status === "error") return {
490
- data: {
491
- user: null,
492
- session: null
493
- },
494
- error: normalizeStackAuthError(result)
495
- };
496
- return {
497
- data: {
498
- user: null,
499
- session: null
500
- },
501
- error: null
502
- };
503
- }
504
- if (type === "email_change") {
505
- const result = await this.stackAuth._interface.verifyEmail(token);
506
- if (result.status === "error") return {
507
- data: {
508
- user: null,
509
- session: null
510
- },
511
- error: normalizeStackAuthError(result)
512
- };
513
- const sessionResult = await this.getSession();
514
- await this.notifyAllSubscribers("USER_UPDATED", sessionResult.data.session);
515
- return {
516
- data: {
517
- user: sessionResult.data.session?.user ?? null,
518
- session: sessionResult.data.session
519
- },
520
- error: null
521
- };
522
- }
523
- return {
524
- data: {
525
- user: null,
526
- session: null
527
- },
528
- error: new AuthError(`Unsupported email OTP type: ${type}`, 400, "validation_failed")
529
- };
530
- }
531
- if ("phone" in params && params.phone) return {
532
- data: {
533
- user: null,
534
- session: null
535
- },
536
- error: new AuthError("Phone OTP verification not supported by Stack Auth", 501, "phone_provider_disabled")
537
- };
538
- if ("token_hash" in params && params.token_hash) {
539
- const { token_hash, type } = params;
540
- if (type === "magiclink" || type === "email") {
541
- let internalSession = await this._getSessionFromStackAuthInternals();
542
- if (!internalSession) internalSession = this.stackAuth._interface.createSession({
543
- refreshToken: null,
544
- accessToken: null
545
- });
546
- const result = await this.stackAuth._interface.signInWithMagicLink(token_hash, internalSession);
547
- if (result.status === "error") return {
548
- data: {
549
- user: null,
550
- session: null
551
- },
552
- error: normalizeStackAuthError(result)
553
- };
554
- const sessionResult = await this.getSession();
555
- if (!sessionResult.data.session) return {
556
- data: {
557
- user: null,
558
- session: null
559
- },
560
- error: new AuthError("Failed to retrieve session after token hash verification", 500, "unexpected_failure")
561
- };
562
- await this.notifyAllSubscribers("SIGNED_IN", sessionResult.data.session);
563
- return {
564
- data: {
565
- user: sessionResult.data.session.user,
566
- session: sessionResult.data.session
567
- },
568
- error: null
569
- };
570
- }
571
- if (type === "signup" || type === "invite") {
572
- const result = await this.stackAuth._interface.verifyEmail(token_hash);
573
- if (result.status === "error") return {
574
- data: {
575
- user: null,
576
- session: null
577
- },
578
- error: normalizeStackAuthError(result)
579
- };
580
- const sessionResult = await this.getSession();
581
- return {
582
- data: {
583
- user: sessionResult.data.session?.user ?? null,
584
- session: sessionResult.data.session
585
- },
586
- error: null
587
- };
588
- }
589
- if (type === "recovery") {
590
- const result = await this.stackAuth._interface.resetPassword({
591
- code: token_hash,
592
- onlyVerifyCode: true
593
- });
594
- if (result.status === "error") return {
595
- data: {
596
- user: null,
597
- session: null
598
- },
599
- error: normalizeStackAuthError(result)
600
- };
601
- return {
602
- data: {
603
- user: null,
604
- session: null
605
- },
606
- error: null
607
- };
608
- }
609
- if (type === "email_change") {
610
- const result = await this.stackAuth._interface.verifyEmail(token_hash);
611
- if (result.status === "error") return {
612
- data: {
613
- user: null,
614
- session: null
615
- },
616
- error: normalizeStackAuthError(result)
617
- };
618
- const sessionResult = await this.getSession();
619
- await this.notifyAllSubscribers("USER_UPDATED", sessionResult.data.session);
620
- return {
621
- data: {
622
- user: sessionResult.data.session?.user ?? null,
623
- session: sessionResult.data.session
624
- },
625
- error: null
626
- };
627
- }
628
- return {
629
- data: {
630
- user: null,
631
- session: null
632
- },
633
- error: new AuthError(`Unsupported token hash OTP type: ${type}`, 400, "validation_failed")
634
- };
635
- }
636
- return {
637
- data: {
638
- user: null,
639
- session: null
640
- },
641
- error: new AuthError("Invalid OTP verification parameters", 400, "validation_failed")
642
- };
643
- } catch (error) {
644
- return {
645
- data: {
646
- user: null,
647
- session: null
648
- },
649
- error: normalizeStackAuthError(error)
650
- };
651
- }
652
- };
653
- /**
654
- * Fetches fresh session data from Stack Auth API.
655
- * Always makes a network request to get the latest user metadata.
656
- * Used when we need fresh data (after setSession, refreshSession, etc.)
657
- */
658
- async _fetchFreshSession() {
659
- try {
660
- const user = await this.stackAuth.getUser();
661
- if (user) {
662
- const tokens = await user.currentSession.getTokens();
663
- if (tokens.accessToken) {
664
- const payload = accessTokenSchema.parse(JSON.parse(atob(tokens.accessToken.split(".")[1])));
665
- return {
666
- data: { session: {
667
- access_token: tokens.accessToken,
668
- refresh_token: tokens.refreshToken ?? "",
669
- expires_at: payload.exp,
670
- expires_in: Math.max(0, payload.exp - Math.floor(Date.now() / 1e3)),
671
- token_type: "bearer",
672
- user: {
673
- id: user.id,
674
- email: user.primaryEmail || "",
675
- email_confirmed_at: user.primaryEmailVerified ? toISOString(user.signedUpAt) : void 0,
676
- last_sign_in_at: toISOString(user.signedUpAt),
677
- created_at: toISOString(user.signedUpAt),
678
- updated_at: toISOString(user.signedUpAt),
679
- aud: "authenticated",
680
- role: "authenticated",
681
- app_metadata: user.clientReadOnlyMetadata,
682
- user_metadata: {
683
- displayName: user.displayName,
684
- profileImageUrl: user.profileImageUrl,
685
- ...user.clientMetadata
686
- },
687
- identities: []
688
- }
689
- } },
690
- error: null
691
- };
692
- }
693
- }
694
- return {
695
- data: { session: null },
696
- error: null
697
- };
698
- } catch (error) {
699
- console.error("Error fetching fresh session:", error);
700
- return {
701
- data: { session: null },
702
- error: normalizeStackAuthError(error)
703
- };
704
- }
705
- }
706
- getSession = async () => {
707
- try {
708
- const cachedTokens = await this._getCachedTokensFromStackAuthInternals();
709
- if (cachedTokens?.accessToken) {
710
- const payload = accessTokenSchema.parse(JSON.parse(atob(cachedTokens.accessToken.split(".")[1])));
711
- return {
712
- data: { session: {
713
- access_token: cachedTokens.accessToken,
714
- refresh_token: cachedTokens.refreshToken ?? "",
715
- expires_at: payload.exp,
716
- expires_in: Math.max(0, payload.exp - Math.floor(Date.now() / 1e3)),
717
- token_type: "bearer",
718
- user: {
719
- id: payload.sub,
720
- email: payload.email || "",
721
- created_at: (/* @__PURE__ */ new Date(payload.iat * 1e3)).toISOString(),
722
- aud: "authenticated",
723
- role: "authenticated",
724
- app_metadata: {},
725
- user_metadata: {}
726
- }
727
- } },
728
- error: null
729
- };
730
- }
731
- return await this._fetchFreshSession();
732
- } catch (error) {
733
- console.error("Error getting session:", error);
734
- return {
735
- data: { session: null },
736
- error: normalizeStackAuthError(error)
737
- };
738
- }
739
- };
740
- refreshSession = async () => {
741
- try {
742
- const sessionResult = await this.getSession();
743
- if (sessionResult.error) return {
744
- data: {
745
- user: null,
746
- session: null
747
- },
748
- error: sessionResult.error
749
- };
750
- return {
751
- data: {
752
- user: sessionResult.data.session?.user ?? null,
753
- session: sessionResult.data.session
754
- },
755
- error: null
756
- };
757
- } catch (error) {
758
- return {
759
- data: {
760
- user: null,
761
- session: null
762
- },
763
- error: normalizeStackAuthError(error)
764
- };
765
- }
766
- };
767
- setSession = async () => {
768
- return {
769
- data: {
770
- user: null,
771
- session: null
772
- },
773
- error: new AuthError("Setting external sessions is not supported by Stack Auth", 501, "not_implemented")
774
- };
775
- };
776
- getUser = async () => {
777
- try {
778
- const user = await this.stackAuth.getUser();
779
- if (!user) return {
780
- data: { user: null },
781
- error: new AuthError("No user session found", 401, "session_not_found")
782
- };
783
- return {
784
- data: { user: {
785
- id: user.id,
786
- aud: "authenticated",
787
- role: "authenticated",
788
- email: user.primaryEmail || "",
789
- email_confirmed_at: user.primaryEmailVerified ? toISOString(user.signedUpAt) : void 0,
790
- phone: void 0,
791
- confirmed_at: user.primaryEmailVerified ? toISOString(user.signedUpAt) : void 0,
792
- last_sign_in_at: toISOString(user.signedUpAt),
793
- app_metadata: {},
794
- user_metadata: {
795
- ...user.clientMetadata,
796
- ...user.displayName ? { displayName: user.displayName } : {},
797
- ...user.profileImageUrl ? { profileImageUrl: user.profileImageUrl } : {}
798
- },
799
- identities: [],
800
- created_at: toISOString(user.signedUpAt),
801
- updated_at: toISOString(user.signedUpAt)
802
- } },
803
- error: null
804
- };
805
- } catch (error) {
806
- return {
807
- data: { user: null },
808
- error: normalizeStackAuthError(error)
809
- };
810
- }
811
- };
812
- getClaims = async () => {
813
- try {
814
- const user = await this.stackAuth.getUser();
815
- if (!user) return {
816
- data: null,
817
- error: new AuthError("No user session found", 401, "session_not_found")
818
- };
819
- let accessToken = null;
820
- if (hasInternalSession(user)) accessToken = user._internalSession.getAccessTokenIfNotExpiredYet(0)?.token ?? null;
821
- if (!accessToken) accessToken = (await user.currentSession.getTokens()).accessToken;
822
- if (!accessToken) return {
823
- data: null,
824
- error: new AuthError("No access token found", 401, "session_not_found")
825
- };
826
- const tokenParts = accessToken.split(".");
827
- if (tokenParts.length !== 3) return {
828
- data: null,
829
- error: new AuthError("Invalid token format", 401, "bad_jwt")
830
- };
831
- try {
832
- return {
833
- data: JSON.parse(atob(tokenParts[1])),
834
- error: null
835
- };
836
- } catch {
837
- return {
838
- data: null,
839
- error: new AuthError("Failed to decode token", 401, "bad_jwt")
840
- };
841
- }
842
- } catch (error) {
843
- return {
844
- data: null,
845
- error: normalizeStackAuthError(error)
846
- };
847
- }
848
- };
849
- updateUser = async (attributes) => {
850
- try {
851
- const user = await this.stackAuth.getUser();
852
- if (!user) return {
853
- data: { user: null },
854
- error: new AuthError("No user session found", 401, "session_not_found")
855
- };
856
- if (attributes.password) return {
857
- data: { user: null },
858
- error: new AuthError("Password updates require reauthentication. Stack Auth does not support the nonce-based reauthentication flow (reauthenticate() method). For password changes, users must: 1) Sign out, 2) Use \"Forgot Password\" flow (resetPasswordForEmail), or 3) Use Stack Auth directly with updatePassword({ oldPassword, newPassword }).", 400, "feature_not_supported")
859
- };
860
- const updateData = {};
861
- if (attributes.data) {
862
- const data$1 = attributes.data;
863
- if (data$1 && "displayName" in data$1 && typeof data$1.displayName === "string") updateData.displayName = data$1.displayName;
864
- if (data$1 && "profileImageUrl" in data$1 && typeof data$1.profileImageUrl === "string") updateData.profileImageUrl = data$1.profileImageUrl;
865
- updateData.clientMetadata = {
866
- ...user.clientMetadata,
867
- ...attributes.data
868
- };
869
- }
870
- await user.update(updateData);
871
- if (attributes.email) console.warn("Email updates require server-side Stack Auth configuration");
872
- const updatedUser = await this.stackAuth.getUser();
873
- if (!updatedUser) throw new Error("Failed to retrieve updated user");
874
- const user_metadata = {
875
- ...updatedUser.clientMetadata,
876
- ...updatedUser.displayName ? { displayName: updatedUser.displayName } : {},
877
- ...updatedUser.profileImageUrl ? { profileImageUrl: updatedUser.profileImageUrl } : {}
878
- };
879
- const data = { user: {
880
- id: updatedUser.id,
881
- aud: "authenticated",
882
- role: "authenticated",
883
- email: updatedUser.primaryEmail || "",
884
- email_confirmed_at: updatedUser.primaryEmailVerified ? toISOString(updatedUser.signedUpAt) : void 0,
885
- phone: void 0,
886
- confirmed_at: updatedUser.primaryEmailVerified ? toISOString(updatedUser.signedUpAt) : void 0,
887
- last_sign_in_at: toISOString(updatedUser.signedUpAt),
888
- app_metadata: {},
889
- user_metadata,
890
- identities: [],
891
- created_at: toISOString(updatedUser.signedUpAt),
892
- updated_at: toISOString(updatedUser.signedUpAt)
893
- } };
894
- const sessionResult = await this.getSession();
895
- await this.notifyAllSubscribers("USER_UPDATED", sessionResult.data.session);
896
- return {
897
- data,
898
- error: null
899
- };
900
- } catch (error) {
901
- return {
902
- data: { user: null },
903
- error: normalizeStackAuthError(error)
904
- };
905
- }
906
- };
907
- getUserIdentities = async () => {
908
- try {
909
- const user = await this.stackAuth.getUser();
910
- if (!user) return {
911
- data: null,
912
- error: new AuthError("No user session found", 401, "session_not_found")
913
- };
914
- return {
915
- data: { identities: (await user.listOAuthProviders()).map((provider) => ({
916
- id: provider.id,
917
- user_id: user.id,
918
- identity_id: provider.id,
919
- provider: provider.type,
920
- identity_data: {
921
- email: provider.email || null,
922
- account_id: provider.accountId || null,
923
- provider_type: provider.type,
924
- user_id: provider.userId,
925
- allow_sign_in: provider.allowSignIn,
926
- allow_connected_accounts: provider.allowConnectedAccounts
927
- },
928
- created_at: toISOString(user.signedUpAt),
929
- last_sign_in_at: toISOString(user.signedUpAt),
930
- updated_at: toISOString(user.signedUpAt)
931
- })) },
932
- error: null
933
- };
934
- } catch (error) {
935
- return {
936
- data: null,
937
- error: normalizeStackAuthError(error)
938
- };
939
- }
940
- };
941
- linkIdentity = async (credentials) => {
942
- try {
943
- const user = await this.stackAuth.getUser();
944
- if (!user) return {
945
- data: {
946
- provider: credentials.provider,
947
- url: null
948
- },
949
- error: new AuthError("No user session found", 401, "session_not_found")
950
- };
951
- const scopes = credentials.options?.scopes ? credentials.options.scopes.split(" ") : void 0;
952
- await user.getConnectedAccount(credentials.provider, {
953
- or: "redirect",
954
- scopes
955
- });
956
- return {
957
- data: {
958
- provider: credentials.provider,
959
- url: credentials.options?.redirectTo || ""
960
- },
961
- error: null
962
- };
963
- } catch (error) {
964
- return {
965
- data: {
966
- provider: credentials.provider,
967
- url: null
968
- },
969
- error: normalizeStackAuthError(error)
970
- };
971
- }
972
- };
973
- unlinkIdentity = async (identity) => {
974
- try {
975
- const user = await this.stackAuth.getUser();
976
- if (!user) return {
977
- data: null,
978
- error: new AuthError("No user session found", 401, "session_not_found")
979
- };
980
- const provider = await user.getOAuthProvider(identity.identity_id);
981
- if (!provider) return {
982
- data: null,
983
- error: new AuthError(`OAuth provider with ID ${identity.identity_id} not found`, 404, "identity_not_found")
984
- };
985
- await provider.delete();
986
- const sessionResult = await this.getSession();
987
- await this.notifyAllSubscribers("USER_UPDATED", sessionResult.data.session);
988
- return {
989
- data: {},
990
- error: null
991
- };
992
- } catch (error) {
993
- return {
994
- data: null,
995
- error: normalizeStackAuthError(error)
996
- };
997
- }
998
- };
999
- resetPasswordForEmail = async (email, options) => {
1000
- try {
1001
- const result = await this.stackAuth.sendForgotPasswordEmail(email, { callbackUrl: options?.redirectTo });
1002
- if (result.status === "error") return {
1003
- data: null,
1004
- error: normalizeStackAuthError(result)
1005
- };
1006
- return {
1007
- data: {},
1008
- error: null
1009
- };
1010
- } catch (error) {
1011
- return {
1012
- data: null,
1013
- error: normalizeStackAuthError(error)
1014
- };
1015
- }
1016
- };
1017
- reauthenticate = async () => {
1018
- return {
1019
- data: {
1020
- user: null,
1021
- session: null
1022
- },
1023
- error: new AuthError("Stack Auth does not support nonce-based reauthentication. For password changes, use the password reset flow (resetPasswordForEmail) or access Stack Auth directly.", 400, "feature_not_supported")
1024
- };
1025
- };
1026
- resend = async (credentials) => {
1027
- try {
1028
- if ("email" in credentials) {
1029
- const { email, type, options } = credentials;
1030
- if (type === "signup") {
1031
- const user = await this.stackAuth.getUser();
1032
- if (user && user.primaryEmail === email) await user.sendVerificationEmail();
1033
- else {
1034
- const result = await this.stackAuth.sendMagicLinkEmail(email, { callbackUrl: options?.emailRedirectTo });
1035
- if (result.status === "error") return {
1036
- data: {
1037
- user: null,
1038
- session: null
1039
- },
1040
- error: normalizeStackAuthError(result)
1041
- };
1042
- }
1043
- return {
1044
- data: {
1045
- user: null,
1046
- session: null
1047
- },
1048
- error: null
1049
- };
1050
- }
1051
- if (type === "email_change") {
1052
- const user = await this.stackAuth.getUser();
1053
- if (!user) return {
1054
- data: {
1055
- user: null,
1056
- session: null
1057
- },
1058
- error: new AuthError("No user session found", 401, "session_not_found")
1059
- };
1060
- const targetChannel = (await user.listContactChannels()).find((ch) => ch.value === email && ch.type === "email");
1061
- if (!targetChannel) return {
1062
- data: {
1063
- user: null,
1064
- session: null
1065
- },
1066
- error: new AuthError("Email not found in user contact channels", 404, "email_not_found")
1067
- };
1068
- await targetChannel.sendVerificationEmail({ callbackUrl: options?.emailRedirectTo });
1069
- return {
1070
- data: {
1071
- user: null,
1072
- session: null
1073
- },
1074
- error: null
1075
- };
1076
- }
1077
- return {
1078
- data: {
1079
- user: null,
1080
- session: null
1081
- },
1082
- error: new AuthError(`Unsupported resend type: ${type}`, 400, "validation_failed")
1083
- };
1084
- }
1085
- if ("phone" in credentials) return {
1086
- data: {
1087
- user: null,
1088
- session: null
1089
- },
1090
- error: new AuthError("Phone OTP resend not supported by Stack Auth", 501, "phone_provider_disabled")
1091
- };
1092
- return {
1093
- data: {
1094
- user: null,
1095
- session: null
1096
- },
1097
- error: new AuthError("Invalid credentials format", 400, "validation_failed")
1098
- };
1099
- } catch (error) {
1100
- return {
1101
- data: {
1102
- user: null,
1103
- session: null
1104
- },
1105
- error: normalizeStackAuthError(error)
1106
- };
1107
- }
1108
- };
1109
- onAuthStateChange = (callback) => {
1110
- const id = crypto.randomUUID();
1111
- const subscription = {
1112
- id,
1113
- callback,
1114
- unsubscribe: () => {
1115
- this.stateChangeEmitters.delete(id);
1116
- if (this.stateChangeEmitters.size === 0) {
1117
- this.stopTokenRefreshDetection();
1118
- this.closeBroadcastChannel();
1119
- }
1120
- }
1121
- };
1122
- this.stateChangeEmitters.set(id, subscription);
1123
- if (this.stateChangeEmitters.size === 1) {
1124
- this.initializeBroadcastChannel();
1125
- this.startTokenRefreshDetection();
1126
- }
1127
- this.emitInitialSession(callback);
1128
- return { data: { subscription: {
1129
- id,
1130
- callback,
1131
- unsubscribe: subscription.unsubscribe
1132
- } } };
1133
- };
1134
- async _getSessionFromStackAuthInternals() {
1135
- const tokenStore = await this.stackAuth._getOrCreateTokenStore(await this.stackAuth._createCookieHelper());
1136
- return this.stackAuth._getSessionFromTokenStore(tokenStore);
1137
- }
1138
- async _getCachedTokensFromStackAuthInternals() {
1139
- try {
1140
- const session = await this._getSessionFromStackAuthInternals();
1141
- const accessToken = session?.getAccessTokenIfNotExpiredYet(0);
1142
- if (!accessToken) return null;
1143
- return {
1144
- accessToken: accessToken.token,
1145
- refreshToken: session?._refreshToken?.token ?? null
1146
- };
1147
- } catch {
1148
- return null;
1149
- }
1150
- }
1151
- async emitInitialSession(callback) {
1152
- try {
1153
- const { data, error } = await this.getSession();
1154
- if (error) {
1155
- await callback("INITIAL_SESSION", null);
1156
- return;
1157
- }
1158
- await callback("INITIAL_SESSION", data.session);
1159
- } catch (error) {
1160
- await callback("INITIAL_SESSION", null);
1161
- }
1162
- }
1163
- async notifyAllSubscribers(event, session, broadcast = true) {
1164
- if (broadcast && this.broadcastChannel) try {
1165
- this.broadcastChannel.postMessage({
1166
- event,
1167
- session,
1168
- timestamp: Date.now()
1169
- });
1170
- } catch (error) {
1171
- console.warn("BroadcastChannel postMessage failed:", error);
1172
- }
1173
- const promises = Array.from(this.stateChangeEmitters.values()).map((subscription) => {
1174
- try {
1175
- return Promise.resolve(subscription.callback(event, session));
1176
- } catch (error) {
1177
- console.error("Auth state change callback error:", error);
1178
- return Promise.resolve();
1179
- }
1180
- });
1181
- await Promise.allSettled(promises);
1182
- }
1183
- initializeBroadcastChannel() {
1184
- if (!supportsBroadcastChannel()) return;
1185
- if (!this.broadcastChannel) try {
1186
- this.broadcastChannel = new BroadcastChannel("stack-auth-state-changes");
1187
- this.broadcastChannel.onmessage = async (event) => {
1188
- const { event: authEvent, session } = event.data;
1189
- await this.notifyAllSubscribers(authEvent, session, false);
1190
- };
1191
- } catch (error) {
1192
- console.error("Failed to create BroadcastChannel, cross-tab sync will not be available:", error);
1193
- }
1194
- }
1195
- closeBroadcastChannel() {
1196
- if (this.broadcastChannel) {
1197
- this.broadcastChannel.close();
1198
- this.broadcastChannel = null;
1199
- }
1200
- }
1201
- startTokenRefreshDetection() {
1202
- if (!this.config.enableTokenRefreshDetection) return;
1203
- if (this.tokenRefreshCheckInterval) return;
1204
- this.tokenRefreshCheckInterval = setInterval(async () => {
1205
- try {
1206
- const sessionResult = await this.getSession();
1207
- if (!sessionResult.data.session) return;
1208
- const session = sessionResult.data.session;
1209
- const now = Math.floor(Date.now() / 1e3);
1210
- const expiresInSeconds = (session.expires_at ?? now) - now;
1211
- if (expiresInSeconds <= 0) {
1212
- await this.notifyAllSubscribers("SIGNED_OUT", null);
1213
- return;
1214
- }
1215
- if (expiresInSeconds <= 90 && expiresInSeconds > 0) await this.notifyAllSubscribers("TOKEN_REFRESHED", session);
1216
- } catch (error) {
1217
- console.error("Token refresh detection error:", error);
1218
- }
1219
- }, this.config.tokenRefreshCheckInterval);
1220
- }
1221
- stopTokenRefreshDetection() {
1222
- if (this.tokenRefreshCheckInterval) {
1223
- clearInterval(this.tokenRefreshCheckInterval);
1224
- this.tokenRefreshCheckInterval = null;
1225
- }
1226
- }
1227
- /**
1228
- * Exchange an OAuth authorization code for a session.
1229
- *
1230
- * Note: Stack Auth handles OAuth callbacks automatically via callOAuthCallback().
1231
- * This method delegates to Stack Auth's internal flow which:
1232
- * - Retrieves the code and state from the current URL
1233
- * - Retrieves the PKCE verifier from cookies (stored during signInWithOAuth)
1234
- * - Exchanges the code for access/refresh tokens
1235
- * - Creates and stores the user session
1236
- *
1237
- * @param authCode - The authorization code (Stack Auth reads this from URL automatically)
1238
- * @returns Session data or error
1239
- */
1240
- exchangeCodeForSession = async (_authCode) => {
1241
- try {
1242
- if (await this.stackAuth.callOAuthCallback()) {
1243
- const sessionResult = await this.getSession();
1244
- if (sessionResult.data.session) {
1245
- await this.notifyAllSubscribers("SIGNED_IN", sessionResult.data.session);
1246
- return {
1247
- data: {
1248
- session: sessionResult.data.session,
1249
- user: sessionResult.data.session.user
1250
- },
1251
- error: null
1252
- };
1253
- }
1254
- }
1255
- return {
1256
- data: {
1257
- session: null,
1258
- user: null
1259
- },
1260
- error: new AuthError("OAuth callback completed but no session was created", 500, "oauth_callback_failed")
1261
- };
1262
- } catch (error) {
1263
- return {
1264
- data: {
1265
- session: null,
1266
- user: null
1267
- },
1268
- error: normalizeStackAuthError(error)
1269
- };
1270
- }
1271
- };
1272
- startAutoRefresh = async () => {
1273
- return Promise.resolve();
1274
- };
1275
- stopAutoRefresh = async () => {
1276
- return Promise.resolve();
1277
- };
1278
- };
1279
-
1280
- //#endregion
1281
- //#region src/client/neon-client.ts
1282
- var NeonClient = class extends PostgrestClient {
1283
- auth;
1284
- constructor({ url, options }) {
1285
- super(url, {
1286
- headers: options?.global?.headers,
1287
- fetch: options?.global?.fetch,
1288
- schema: options?.db?.schema
1289
- });
1290
- }
1291
- };
1292
-
1293
- //#endregion
1294
- //#region src/client/fetch-with-auth.ts
1295
- /**
1296
- * Error thrown when authentication is required but no session exists
1297
- */
1298
- var AuthRequiredError = class extends Error {
1299
- constructor(message = "Authentication required. User must be signed in to access Neon database.") {
1300
- super(message);
1301
- this.name = "AuthRequiredError";
1302
- }
1303
- };
1304
- /**
1305
- * Creates a fetch wrapper that injects auth headers into every request
1306
- *
1307
- * Unlike Supabase, Neon requires authentication - requests without a valid
1308
- * session will throw an AuthRequiredError.
1309
- *
1310
- * @param getAccessToken - Async function that returns current access token
1311
- * @param customFetch - Optional custom fetch implementation
1312
- * @returns Wrapped fetch function with auth headers
1313
- */
1314
- function fetchWithAuth(getAccessToken, customFetch) {
1315
- const baseFetch = customFetch ?? fetch;
1316
- return async (input, init) => {
1317
- const accessToken = await getAccessToken();
1318
- if (!accessToken) throw new AuthRequiredError();
1319
- const headers = new Headers(init?.headers);
1320
- if (!headers.has("Authorization")) headers.set("Authorization", `Bearer ${accessToken}`);
1321
- return baseFetch(input, {
1322
- ...init,
1323
- headers
1324
- });
1325
- };
1326
- }
1327
-
1328
- //#endregion
1329
- //#region src/client/client-factory.ts
1330
- /**
1331
- * Factory function to create NeonClient with seamless auth integration
1332
- *
1333
- * @param options - Configuration options
1334
- * @returns NeonClient instance with auth-aware fetch wrapper
1335
- * @throws AuthRequiredError when making requests without authentication
1336
- */
1337
- function createClient({ url, auth: authOptions, options }) {
1338
- const auth = new StackAuthAdapter(authOptions);
1339
- const getAccessToken = async () => {
1340
- const { data, error } = await auth.getSession();
1341
- if (error || !data.session) return null;
1342
- return data.session.access_token;
1343
- };
1344
- const authFetch = fetchWithAuth(getAccessToken, options?.global?.fetch);
1345
- const client = new NeonClient({
1346
- url,
1347
- options: {
1348
- ...options,
1349
- global: {
1350
- ...options?.global,
1351
- fetch: authFetch
1352
- }
1353
- }
1354
- });
1355
- client.auth = auth;
1356
- return client;
1357
- }
1358
-
1359
- //#endregion
1360
- export { AuthApiError, AuthError, NeonClient, StackAuthAdapter, createClient, isAuthError };