@ascendkit/nextjs 0.1.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.
Files changed (95) hide show
  1. package/LICENSE +21 -0
  2. package/dist/client/hooks.d.ts +109 -0
  3. package/dist/client/hooks.d.ts.map +1 -0
  4. package/dist/client/hooks.js +372 -0
  5. package/dist/client/index.d.ts +4 -0
  6. package/dist/client/index.d.ts.map +1 -0
  7. package/dist/client/index.js +3 -0
  8. package/dist/client/provider.d.ts +66 -0
  9. package/dist/client/provider.d.ts.map +1 -0
  10. package/dist/client/provider.js +284 -0
  11. package/dist/client/use-analytics.d.ts +27 -0
  12. package/dist/client/use-analytics.d.ts.map +1 -0
  13. package/dist/client/use-analytics.js +133 -0
  14. package/dist/components/auth-card.d.ts +20 -0
  15. package/dist/components/auth-card.d.ts.map +1 -0
  16. package/dist/components/auth-card.js +128 -0
  17. package/dist/components/auth-modal.d.ts +9 -0
  18. package/dist/components/auth-modal.d.ts.map +1 -0
  19. package/dist/components/auth-modal.js +110 -0
  20. package/dist/components/branding-badge.d.ts +2 -0
  21. package/dist/components/branding-badge.d.ts.map +1 -0
  22. package/dist/components/branding-badge.js +9 -0
  23. package/dist/components/email-verification.d.ts +2 -0
  24. package/dist/components/email-verification.d.ts.map +1 -0
  25. package/dist/components/email-verification.js +48 -0
  26. package/dist/components/forgot-password.d.ts +6 -0
  27. package/dist/components/forgot-password.d.ts.map +1 -0
  28. package/dist/components/forgot-password.js +37 -0
  29. package/dist/components/index.d.ts +13 -0
  30. package/dist/components/index.d.ts.map +1 -0
  31. package/dist/components/index.js +12 -0
  32. package/dist/components/login.d.ts +9 -0
  33. package/dist/components/login.d.ts.map +1 -0
  34. package/dist/components/login.js +48 -0
  35. package/dist/components/reset-password.d.ts +6 -0
  36. package/dist/components/reset-password.d.ts.map +1 -0
  37. package/dist/components/reset-password.js +47 -0
  38. package/dist/components/sign-in-button.d.ts +19 -0
  39. package/dist/components/sign-in-button.d.ts.map +1 -0
  40. package/dist/components/sign-in-button.js +27 -0
  41. package/dist/components/sign-up-button.d.ts +19 -0
  42. package/dist/components/sign-up-button.d.ts.map +1 -0
  43. package/dist/components/sign-up-button.js +27 -0
  44. package/dist/components/signup.d.ts +9 -0
  45. package/dist/components/signup.d.ts.map +1 -0
  46. package/dist/components/signup.js +60 -0
  47. package/dist/components/social-button.d.ts +8 -0
  48. package/dist/components/social-button.d.ts.map +1 -0
  49. package/dist/components/social-button.js +10 -0
  50. package/dist/components/user-button.d.ts +11 -0
  51. package/dist/components/user-button.d.ts.map +1 -0
  52. package/dist/components/user-button.js +14 -0
  53. package/dist/index.d.ts +6 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/index.js +8 -0
  56. package/dist/server/access-token.d.ts +39 -0
  57. package/dist/server/access-token.d.ts.map +1 -0
  58. package/dist/server/access-token.js +74 -0
  59. package/dist/server/adapter.d.ts +6 -0
  60. package/dist/server/adapter.d.ts.map +1 -0
  61. package/dist/server/adapter.js +57 -0
  62. package/dist/server/analytics.d.ts +61 -0
  63. package/dist/server/analytics.d.ts.map +1 -0
  64. package/dist/server/analytics.js +117 -0
  65. package/dist/server/ascendkit-auth.d.ts +122 -0
  66. package/dist/server/ascendkit-auth.d.ts.map +1 -0
  67. package/dist/server/ascendkit-auth.js +146 -0
  68. package/dist/server/auth-runtime.d.ts +12 -0
  69. package/dist/server/auth-runtime.d.ts.map +1 -0
  70. package/dist/server/auth-runtime.js +123 -0
  71. package/dist/server/config-fetcher.d.ts +22 -0
  72. package/dist/server/config-fetcher.d.ts.map +1 -0
  73. package/dist/server/config-fetcher.js +26 -0
  74. package/dist/server/email-sender.d.ts +29 -0
  75. package/dist/server/email-sender.d.ts.map +1 -0
  76. package/dist/server/email-sender.js +58 -0
  77. package/dist/server/index.d.ts +10 -0
  78. package/dist/server/index.d.ts.map +1 -0
  79. package/dist/server/index.js +7 -0
  80. package/dist/server/oauth-proxy-plugin.d.ts +10 -0
  81. package/dist/server/oauth-proxy-plugin.d.ts.map +1 -0
  82. package/dist/server/oauth-proxy-plugin.js +156 -0
  83. package/dist/server/social-providers.d.ts +12 -0
  84. package/dist/server/social-providers.d.ts.map +1 -0
  85. package/dist/server/social-providers.js +15 -0
  86. package/dist/server/webhooks.d.ts +43 -0
  87. package/dist/server/webhooks.d.ts.map +1 -0
  88. package/dist/server/webhooks.js +83 -0
  89. package/dist/shared/http-client.d.ts +17 -0
  90. package/dist/shared/http-client.d.ts.map +1 -0
  91. package/dist/shared/http-client.js +52 -0
  92. package/dist/shared/types.d.ts +49 -0
  93. package/dist/shared/types.d.ts.map +1 -0
  94. package/dist/shared/types.js +1 -0
  95. package/package.json +49 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,aAAa,EACb,0BAA0B,EAC1B,sBAAsB,EACtB,wBAAwB,EACxB,SAAS,GACV,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAG5G,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG/J,OAAO,EACL,KAAK,EACL,MAAM,EACN,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,aAAa,GACd,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ // Server
2
+ export { AscendKitAuth, createAscendKitAuthRuntime, createAscendKitAdapter, createAccessTokenHandler, Analytics, } from "./server";
3
+ // Client
4
+ export { AscendKitProvider, useAscendKitContext, useAscendKit, useAuthModal, useAccessToken, useSessions, useUser, useAnalytics, verifyEmail } from "./client";
5
+ // Components
6
+ export { Login, SignUp, EmailVerification, ForgotPassword, ResetPassword, SocialButton, AscendKitAuthCard, AuthModal, SignInButton, SignUpButton, AscendKitUserButton, BrandingBadge, } from "./components";
7
+ // Re-exports from Better Auth UI (convenience)
8
+ export { SignedIn, SignedOut, AuthLoading } from "@daveyplate/better-auth-ui";
@@ -0,0 +1,39 @@
1
+ import type { AscendKitAuthRuntime } from "./auth-runtime";
2
+ interface AccessTokenHandlerOptions {
3
+ /** Dynamic auth runtime returned by createAscendKitAuthRuntime(). */
4
+ authRuntime: AscendKitAuthRuntime;
5
+ /** AscendKit environment public key (default: ASCENDKIT_ENV_KEY env var). */
6
+ publicKey?: string;
7
+ /** AscendKit API URL (default: ASCENDKIT_API_URL env var, then https://api.ascendkit.com). */
8
+ apiUrl?: string;
9
+ }
10
+ /**
11
+ * Create a Next.js route handler that exchanges a Better Auth session
12
+ * for a short-lived RS256 access token.
13
+ *
14
+ * The access token can be verified by any backend using AscendKit's JWKS
15
+ * endpoint — no shared secrets needed.
16
+ *
17
+ * Usage:
18
+ * ```ts
19
+ * // lib/auth.ts
20
+ * import { createAscendKitAuthRuntime } from "@ascendkit/nextjs/server";
21
+ * export const authRuntime = createAscendKitAuthRuntime();
22
+ *
23
+ * // app/api/access-token/route.ts
24
+ * import { authRuntime } from "@/lib/auth";
25
+ * import { createAccessTokenHandler } from "@ascendkit/nextjs/server";
26
+ *
27
+ * export const GET = createAccessTokenHandler({ authRuntime });
28
+ * ```
29
+ *
30
+ * Frontend usage:
31
+ * ```ts
32
+ * const { getAccessToken } = useAccessToken();
33
+ * const token = await getAccessToken();
34
+ * fetch("/api/data", { headers: { Authorization: `Bearer ${token}` } });
35
+ * ```
36
+ */
37
+ export declare function createAccessTokenHandler(options: AccessTokenHandlerOptions): (req: Request) => Promise<Response>;
38
+ export {};
39
+ //# sourceMappingURL=access-token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access-token.d.ts","sourceRoot":"","sources":["../../src/server/access-token.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,UAAU,yBAAyB;IACjC,qEAAqE;IACrE,WAAW,EAAE,oBAAoB,CAAC;IAClC,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8FAA8F;IAC9F,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAQD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,IAO/C,KAAK,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CA+D3D"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Create a Next.js route handler that exchanges a Better Auth session
3
+ * for a short-lived RS256 access token.
4
+ *
5
+ * The access token can be verified by any backend using AscendKit's JWKS
6
+ * endpoint — no shared secrets needed.
7
+ *
8
+ * Usage:
9
+ * ```ts
10
+ * // lib/auth.ts
11
+ * import { createAscendKitAuthRuntime } from "@ascendkit/nextjs/server";
12
+ * export const authRuntime = createAscendKitAuthRuntime();
13
+ *
14
+ * // app/api/access-token/route.ts
15
+ * import { authRuntime } from "@/lib/auth";
16
+ * import { createAccessTokenHandler } from "@ascendkit/nextjs/server";
17
+ *
18
+ * export const GET = createAccessTokenHandler({ authRuntime });
19
+ * ```
20
+ *
21
+ * Frontend usage:
22
+ * ```ts
23
+ * const { getAccessToken } = useAccessToken();
24
+ * const token = await getAccessToken();
25
+ * fetch("/api/data", { headers: { Authorization: `Bearer ${token}` } });
26
+ * ```
27
+ */
28
+ export function createAccessTokenHandler(options) {
29
+ const { authRuntime, publicKey = process.env.ASCENDKIT_ENV_KEY ?? "", apiUrl = process.env.ASCENDKIT_API_URL || "https://api.ascendkit.com", } = options;
30
+ return async function GET(req) {
31
+ try {
32
+ if (!publicKey || publicKey === "undefined" || publicKey === "null") {
33
+ return Response.json({
34
+ data: null,
35
+ error: "Server misconfiguration: missing AscendKit public key in createAccessTokenHandler",
36
+ }, { status: 500 });
37
+ }
38
+ // Read session via Better Auth (handles cookies internally)
39
+ let session;
40
+ try {
41
+ session = await authRuntime.getSession(req.headers);
42
+ }
43
+ catch (err) {
44
+ console.error("[AscendKit] getSession() failed:", err instanceof Error ? err.message : err);
45
+ return Response.json({ data: null, error: "Failed to read session" }, { status: 401 });
46
+ }
47
+ if (!session?.session?.token) {
48
+ return Response.json({ data: null, error: "Unauthorized" }, { status: 401 });
49
+ }
50
+ // Exchange session token for RS256 access token
51
+ const res = await fetch(`${apiUrl}/api/auth/access-token`, {
52
+ method: "POST",
53
+ headers: {
54
+ "X-AscendKit-Public-Key": publicKey,
55
+ Authorization: `Bearer ${session.session.token}`,
56
+ "Content-Type": "application/json",
57
+ },
58
+ });
59
+ if (!res.ok) {
60
+ const body = await res.json().catch(() => ({}));
61
+ return Response.json({
62
+ data: null,
63
+ error: body.error || body.detail || body.message || "Failed to get access token",
64
+ }, { status: res.status });
65
+ }
66
+ const json = (await res.json());
67
+ return Response.json({ data: json.data });
68
+ }
69
+ catch (err) {
70
+ console.error("[AscendKit] Access token handler error:", err);
71
+ return Response.json({ data: null, error: "Internal error in access token handler" }, { status: 500 });
72
+ }
73
+ };
74
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Creates a Better Auth database adapter that routes all CRUD operations
3
+ * through the AscendKit backend API, scoped by public key.
4
+ */
5
+ export declare function createAscendKitAdapter(publicKey: string, apiUrl: string): import("better-auth/adapters").AdapterFactory;
6
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/server/adapter.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,iDA+DvE"}
@@ -0,0 +1,57 @@
1
+ import { createAdapterFactory } from "better-auth/adapters";
2
+ import { createHttpClient } from "../shared/http-client";
3
+ /**
4
+ * Creates a Better Auth database adapter that routes all CRUD operations
5
+ * through the AscendKit backend API, scoped by public key.
6
+ */
7
+ export function createAscendKitAdapter(publicKey, apiUrl) {
8
+ return createAdapterFactory({
9
+ config: {
10
+ adapterId: "ascendkit",
11
+ disableIdGeneration: true,
12
+ supportsJSON: true,
13
+ supportsDates: false,
14
+ supportsBooleans: true,
15
+ },
16
+ adapter: () => {
17
+ const http = createHttpClient({ publicKey, apiUrl });
18
+ return {
19
+ async create({ model, data, select }) {
20
+ return await http.post("/api/auth/db/create", { model, data, select });
21
+ },
22
+ async findOne({ model, where, select }) {
23
+ return await http.post("/api/auth/db/find-one", { model, where, select });
24
+ },
25
+ async findMany({ model, where, limit, select, sortBy, offset, }) {
26
+ return await http.post("/api/auth/db/find-many", {
27
+ model, where, select, limit, offset, sortBy,
28
+ });
29
+ },
30
+ async update({ model, where, update }) {
31
+ return await http.post("/api/auth/db/update", { model, where, update });
32
+ },
33
+ async updateMany({ model, where, update }) {
34
+ const result = await http.post("/api/auth/db/update-many", {
35
+ model, where, update,
36
+ });
37
+ return result?.count ?? 0;
38
+ },
39
+ async delete({ model, where }) {
40
+ await http.post("/api/auth/db/delete", { model, where });
41
+ },
42
+ async deleteMany({ model, where }) {
43
+ const result = await http.post("/api/auth/db/delete-many", {
44
+ model, where,
45
+ });
46
+ return result?.count ?? 0;
47
+ },
48
+ async count({ model, where }) {
49
+ const result = await http.post("/api/auth/db/count", {
50
+ model, where,
51
+ });
52
+ return result?.count ?? 0;
53
+ },
54
+ };
55
+ },
56
+ });
57
+ }
@@ -0,0 +1,61 @@
1
+ interface AnalyticsOptions {
2
+ /** Your project's secret key (sk_prod_...). */
3
+ secretKey: string;
4
+ /** Your project's public key. */
5
+ publicKey: string;
6
+ /** AscendKit API base URL. Defaults to https://api.ascendkit.com. */
7
+ apiUrl?: string;
8
+ /** Milliseconds between automatic flushes. Defaults to 30000. */
9
+ flushIntervalMs?: number;
10
+ /** Max events before auto-flush. Defaults to 10. */
11
+ batchSize?: number;
12
+ }
13
+ /**
14
+ * Server-side analytics client for AscendKit.
15
+ *
16
+ * Tracks events from your backend with trusted identity (secret key auth).
17
+ * Events are queued in-memory and flushed in batches via a background timer.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import { Analytics } from "@ascendkit/nextjs/server";
22
+ *
23
+ * const analytics = new Analytics({
24
+ * secretKey: process.env.ASCENDKIT_SECRET_KEY!,
25
+ * publicKey: process.env.ASCENDKIT_ENV_KEY!,
26
+ * });
27
+ *
28
+ * analytics.track("usr_456", "checkout.completed", { orderId: "ord_789" });
29
+ *
30
+ * // Events flush automatically every 30s or when batch size (10) is reached.
31
+ * // Call shutdown() for graceful cleanup (e.g. in process.on("beforeExit")).
32
+ * ```
33
+ */
34
+ export declare class Analytics {
35
+ private queue;
36
+ private http;
37
+ private batchSize;
38
+ private flushTimer;
39
+ private flushing;
40
+ constructor(options: AnalyticsOptions);
41
+ /**
42
+ * Queue a server-side event for a user.
43
+ *
44
+ * @param userId - The user ID (usr_ prefixed).
45
+ * @param event - Event name (e.g. "checkout.completed").
46
+ * @param properties - Optional event properties.
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * analytics.track("usr_456", "checkout.completed", { total: 99.99 });
51
+ * ```
52
+ */
53
+ track(userId: string, event: string, properties?: Record<string, unknown>): void;
54
+ /** Manually flush the event queue. */
55
+ flush(): Promise<void>;
56
+ /** Gracefully shut down: stop the flush timer and flush remaining events. */
57
+ shutdown(): Promise<void>;
58
+ private sendBatch;
59
+ }
60
+ export {};
61
+ //# sourceMappingURL=analytics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/server/analytics.ts"],"names":[],"mappings":"AAcA,UAAU,gBAAgB;IACxB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,KAAK,CAAwB;IACrC,OAAO,CAAC,IAAI,CAAsC;IAClD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAiC;IACnD,OAAO,CAAC,QAAQ,CAAS;gBAEb,OAAO,EAAE,gBAAgB;IAqBrC;;;;;;;;;;;OAWG;IACH,KAAK,CACH,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,IAAI;IAcP,sCAAsC;IAChC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB5B,6EAA6E;IACvE,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YASjB,SAAS;CAuBxB"}
@@ -0,0 +1,117 @@
1
+ import { createHttpClient } from "../shared/http-client";
2
+ const SDK_VERSION = "0.1.0";
3
+ const DEFAULT_FLUSH_INTERVAL_MS = 30_000;
4
+ const DEFAULT_BATCH_SIZE = 10;
5
+ const MAX_RETRY_ATTEMPTS = 3;
6
+ /**
7
+ * Server-side analytics client for AscendKit.
8
+ *
9
+ * Tracks events from your backend with trusted identity (secret key auth).
10
+ * Events are queued in-memory and flushed in batches via a background timer.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { Analytics } from "@ascendkit/nextjs/server";
15
+ *
16
+ * const analytics = new Analytics({
17
+ * secretKey: process.env.ASCENDKIT_SECRET_KEY!,
18
+ * publicKey: process.env.ASCENDKIT_ENV_KEY!,
19
+ * });
20
+ *
21
+ * analytics.track("usr_456", "checkout.completed", { orderId: "ord_789" });
22
+ *
23
+ * // Events flush automatically every 30s or when batch size (10) is reached.
24
+ * // Call shutdown() for graceful cleanup (e.g. in process.on("beforeExit")).
25
+ * ```
26
+ */
27
+ export class Analytics {
28
+ queue = [];
29
+ http;
30
+ batchSize;
31
+ flushTimer;
32
+ flushing = false;
33
+ constructor(options) {
34
+ const apiUrl = options.apiUrl ?? "https://api.ascendkit.com";
35
+ this.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;
36
+ this.http = createHttpClient({
37
+ publicKey: options.publicKey,
38
+ apiUrl,
39
+ secretKey: options.secretKey,
40
+ });
41
+ this.flushTimer = setInterval(() => void this.flush(), options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS);
42
+ // Unref so the timer doesn't keep the process alive
43
+ if (typeof this.flushTimer === "object" && "unref" in this.flushTimer) {
44
+ this.flushTimer.unref();
45
+ }
46
+ }
47
+ /**
48
+ * Queue a server-side event for a user.
49
+ *
50
+ * @param userId - The user ID (usr_ prefixed).
51
+ * @param event - Event name (e.g. "checkout.completed").
52
+ * @param properties - Optional event properties.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * analytics.track("usr_456", "checkout.completed", { total: 99.99 });
57
+ * ```
58
+ */
59
+ track(userId, event, properties) {
60
+ const prefixedEvent = event.startsWith("app.") ? event : `app.${event}`;
61
+ this.queue.push({
62
+ event: prefixedEvent,
63
+ userId,
64
+ properties,
65
+ timestamp: new Date().toISOString(),
66
+ });
67
+ if (this.queue.length >= this.batchSize) {
68
+ void this.flush();
69
+ }
70
+ }
71
+ /** Manually flush the event queue. */
72
+ async flush() {
73
+ if (this.flushing || this.queue.length === 0)
74
+ return;
75
+ this.flushing = true;
76
+ const batch = this.queue.splice(0);
77
+ try {
78
+ await this.sendBatch(batch, 0);
79
+ }
80
+ catch {
81
+ // Put events back at the front of the queue
82
+ this.queue.unshift(...batch);
83
+ }
84
+ finally {
85
+ this.flushing = false;
86
+ }
87
+ }
88
+ /** Gracefully shut down: stop the flush timer and flush remaining events. */
89
+ async shutdown() {
90
+ clearInterval(this.flushTimer);
91
+ await this.flush();
92
+ }
93
+ // ---------------------------------------------------------------------------
94
+ // Internal
95
+ // ---------------------------------------------------------------------------
96
+ async sendBatch(batch, attempt) {
97
+ try {
98
+ await this.http.post("/api/v1/events", {
99
+ batch: batch.map((e) => ({
100
+ event: e.event,
101
+ userId: e.userId,
102
+ properties: e.properties,
103
+ timestamp: e.timestamp,
104
+ context: { sdk: "js", sdkVersion: SDK_VERSION },
105
+ })),
106
+ });
107
+ }
108
+ catch (err) {
109
+ if (attempt < MAX_RETRY_ATTEMPTS) {
110
+ const delay = 2 ** attempt * 1000; // 1s, 2s, 4s
111
+ await new Promise((r) => setTimeout(r, delay));
112
+ return this.sendBatch(batch, attempt + 1);
113
+ }
114
+ throw err;
115
+ }
116
+ }
117
+ }
@@ -0,0 +1,122 @@
1
+ import type { AscendKitAuthOptions, AuthConfig } from "../shared/types";
2
+ import type { BetterAuthPlugin } from "better-auth";
3
+ /**
4
+ * Pre-configured Better Auth setup for AscendKit.
5
+ *
6
+ * NOTE: This helper builds a static auth instance. For runtime hot-reload
7
+ * behavior (no server restarts when auth settings change), use
8
+ * createAscendKitAuthRuntime() instead.
9
+ *
10
+ * Fetches auth config (enabled providers, OAuth credentials, features)
11
+ * from the AscendKit backend and returns a fully configured Better Auth instance.
12
+ * Email callbacks route through AscendKit's content service + SES for delivery.
13
+ *
14
+ * Usage:
15
+ * ```ts
16
+ * // lib/auth.ts — zero-config (reads from env vars automatically)
17
+ * import { AscendKitAuth } from "@ascendkit/nextjs/server";
18
+ * export const auth = await AscendKitAuth();
19
+ * ```
20
+ *
21
+ * Environment variables (set by `ascendkit init` + `ascendkit set-env`):
22
+ * - ASCENDKIT_ENV_KEY — public key (server-side)
23
+ * - ASCENDKIT_SECRET_KEY — secret key (server-side)
24
+ * - ASCENDKIT_API_URL — backend URL (default: https://api.ascendkit.com)
25
+ *
26
+ * ```ts
27
+ * // app/api/auth/[...all]/route.ts
28
+ * import { auth } from "@/lib/auth";
29
+ * import { toNextJsHandler } from "@ascendkit/nextjs/server";
30
+ * export const { GET, POST } = toNextJsHandler(auth.handler);
31
+ * ```
32
+ */
33
+ export declare function AscendKitAuth(options?: AscendKitAuthOptions): Promise<import("better-auth").Auth<{
34
+ database: import("better-auth/adapters").AdapterFactory;
35
+ emailAndPassword: {
36
+ enabled: false;
37
+ };
38
+ }> | import("better-auth").Auth<{
39
+ plugins?: BetterAuthPlugin[] | undefined;
40
+ socialProviders: Record<string, {
41
+ clientId: string;
42
+ clientSecret: string;
43
+ }>;
44
+ session: {
45
+ expiresIn: number;
46
+ cookieCache: {
47
+ enabled: true;
48
+ maxAge: number;
49
+ };
50
+ };
51
+ emailVerification?: {
52
+ sendVerificationEmail: (data: {
53
+ user: {
54
+ name: string;
55
+ email: string;
56
+ };
57
+ url: string;
58
+ }) => Promise<void>;
59
+ sendOnSignUp: boolean;
60
+ } | undefined;
61
+ database: import("better-auth/adapters").AdapterFactory;
62
+ emailAndPassword: {
63
+ sendResetPassword?: ((data: {
64
+ user: {
65
+ name: string;
66
+ email: string;
67
+ };
68
+ url: string;
69
+ }) => Promise<void>) | undefined;
70
+ requireEmailVerification?: boolean | undefined;
71
+ enabled: boolean;
72
+ };
73
+ }>>;
74
+ export declare function createDisabledAuth(publicKey: string, apiUrl: string): import("better-auth").Auth<{
75
+ database: import("better-auth/adapters").AdapterFactory;
76
+ emailAndPassword: {
77
+ enabled: false;
78
+ };
79
+ }>;
80
+ export declare function buildAscendKitAuthFromConfig(args: {
81
+ publicKey: string;
82
+ apiUrl: string;
83
+ config: AuthConfig;
84
+ waitlistRedirectPath?: string;
85
+ rejectedRedirectPath?: string;
86
+ }): Promise<import("better-auth").Auth<{
87
+ plugins?: BetterAuthPlugin[] | undefined;
88
+ socialProviders: Record<string, {
89
+ clientId: string;
90
+ clientSecret: string;
91
+ }>;
92
+ session: {
93
+ expiresIn: number;
94
+ cookieCache: {
95
+ enabled: true;
96
+ maxAge: number;
97
+ };
98
+ };
99
+ emailVerification?: {
100
+ sendVerificationEmail: (data: {
101
+ user: {
102
+ name: string;
103
+ email: string;
104
+ };
105
+ url: string;
106
+ }) => Promise<void>;
107
+ sendOnSignUp: boolean;
108
+ } | undefined;
109
+ database: import("better-auth/adapters").AdapterFactory;
110
+ emailAndPassword: {
111
+ sendResetPassword?: ((data: {
112
+ user: {
113
+ name: string;
114
+ email: string;
115
+ };
116
+ url: string;
117
+ }) => Promise<void>) | undefined;
118
+ requireEmailVerification?: boolean | undefined;
119
+ enabled: boolean;
120
+ };
121
+ }>>;
122
+ //# sourceMappingURL=ascendkit-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ascendkit-auth.d.ts","sourceRoot":"","sources":["../../src/server/ascendkit-auth.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAsB,aAAa,CAAC,OAAO,GAAE,oBAAyB;;;;;;;;;;;;;;;;;;;sCAuG1B;YAAE,IAAI,EAAE;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,KAAK,EAAE,MAAM,CAAA;aAAE,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE;;;;;oCARxD;YAAE,IAAI,EAAE;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,KAAK,EAAE,MAAM,CAAA;aAAE,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE;;;;IA1E/F;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;GAMnE;AAED,wBAAsB,4BAA4B,CAAC,IAAI,EAAE;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,CAAC;IACnB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;;;;;;;;;;;;;;sCAkE2C;YAAE,IAAI,EAAE;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,KAAK,EAAE,MAAM,CAAA;aAAE,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE;;;;;oCARxD;YAAE,IAAI,EAAE;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,KAAK,EAAE,MAAM,CAAA;aAAE,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE;;;;IAwB/F"}
@@ -0,0 +1,146 @@
1
+ import { betterAuth } from "better-auth";
2
+ import { createAscendKitAdapter } from "./adapter";
3
+ import { fetchAuthConfig } from "./config-fetcher";
4
+ import { buildSocialProviders } from "./social-providers";
5
+ import { createEmailSender } from "./email-sender";
6
+ import { ascendkitOAuthProxyPlugin } from "./oauth-proxy-plugin";
7
+ /**
8
+ * Pre-configured Better Auth setup for AscendKit.
9
+ *
10
+ * NOTE: This helper builds a static auth instance. For runtime hot-reload
11
+ * behavior (no server restarts when auth settings change), use
12
+ * createAscendKitAuthRuntime() instead.
13
+ *
14
+ * Fetches auth config (enabled providers, OAuth credentials, features)
15
+ * from the AscendKit backend and returns a fully configured Better Auth instance.
16
+ * Email callbacks route through AscendKit's content service + SES for delivery.
17
+ *
18
+ * Usage:
19
+ * ```ts
20
+ * // lib/auth.ts — zero-config (reads from env vars automatically)
21
+ * import { AscendKitAuth } from "@ascendkit/nextjs/server";
22
+ * export const auth = await AscendKitAuth();
23
+ * ```
24
+ *
25
+ * Environment variables (set by `ascendkit init` + `ascendkit set-env`):
26
+ * - ASCENDKIT_ENV_KEY — public key (server-side)
27
+ * - ASCENDKIT_SECRET_KEY — secret key (server-side)
28
+ * - ASCENDKIT_API_URL — backend URL (default: https://api.ascendkit.com)
29
+ *
30
+ * ```ts
31
+ * // app/api/auth/[...all]/route.ts
32
+ * import { auth } from "@/lib/auth";
33
+ * import { toNextJsHandler } from "@ascendkit/nextjs/server";
34
+ * export const { GET, POST } = toNextJsHandler(auth.handler);
35
+ * ```
36
+ */
37
+ export async function AscendKitAuth(options = {}) {
38
+ const publicKey = options.publicKey || process.env.ASCENDKIT_ENV_KEY || "";
39
+ const secretKey = options.secretKey || process.env.ASCENDKIT_SECRET_KEY || "";
40
+ const apiUrl = options.apiUrl || process.env.ASCENDKIT_API_URL || "https://api.ascendkit.com";
41
+ let config;
42
+ try {
43
+ config = await fetchAuthConfig(publicKey, secretKey, apiUrl);
44
+ }
45
+ catch (err) {
46
+ console.error("[AscendKit] Failed to fetch auth config:", err instanceof Error ? err.message : err);
47
+ console.error("[AscendKit] Auth routes will operate with no providers until config is available.");
48
+ return createDisabledAuth(publicKey, apiUrl);
49
+ }
50
+ return buildAscendKitAuthFromConfig({
51
+ publicKey,
52
+ apiUrl,
53
+ config,
54
+ waitlistRedirectPath: options.waitlistRedirectPath,
55
+ rejectedRedirectPath: options.rejectedRedirectPath,
56
+ });
57
+ }
58
+ export function createDisabledAuth(publicKey, apiUrl) {
59
+ const adapter = createAscendKitAdapter(publicKey, apiUrl);
60
+ return betterAuth({
61
+ database: adapter,
62
+ emailAndPassword: { enabled: false },
63
+ });
64
+ }
65
+ export async function buildAscendKitAuthFromConfig(args) {
66
+ const { publicKey, apiUrl, config, waitlistRedirectPath, rejectedRedirectPath, } = args;
67
+ const adapter = createAscendKitAdapter(publicKey, apiUrl);
68
+ const proxyProviders = Object.entries(config.oauth)
69
+ .filter(([, creds]) => creds.flowType === "proxy")
70
+ .map(([provider]) => provider);
71
+ const socialProviders = buildSocialProviders(config);
72
+ const email = createEmailSender(publicKey, apiUrl, {
73
+ waitlistEnabled: config.features.waitlist,
74
+ });
75
+ // Build plugins array based on feature flags
76
+ const plugins = [];
77
+ if (proxyProviders.length > 0) {
78
+ plugins.push(ascendkitOAuthProxyPlugin({
79
+ publicKey,
80
+ apiUrl,
81
+ proxyProviders,
82
+ waitlistRedirectPath: config.features.waitlist ? waitlistRedirectPath : undefined,
83
+ rejectedRedirectPath: config.features.waitlist ? rejectedRedirectPath : undefined,
84
+ }));
85
+ }
86
+ const magicLinkEnabled = config.providers.includes("magic-link");
87
+ if (magicLinkEnabled) {
88
+ const { magicLink } = await import("better-auth/plugins/magic-link");
89
+ plugins.push(magicLink({
90
+ sendMagicLink: async (data) => {
91
+ await email.sendMagicLinkEmail(data);
92
+ },
93
+ }));
94
+ }
95
+ if (config.features.requireUsername) {
96
+ const { username } = await import("better-auth/plugins/username");
97
+ plugins.push(username());
98
+ }
99
+ // Parse session duration (e.g. "7d", "24h") to seconds
100
+ const sessionMaxAge = parseSessionDuration(config.sessionDuration);
101
+ const credentialsEnabled = config.providers.includes("credentials") && !magicLinkEnabled;
102
+ return betterAuth({
103
+ database: adapter,
104
+ emailAndPassword: {
105
+ enabled: credentialsEnabled,
106
+ ...(credentialsEnabled && {
107
+ requireEmailVerification: config.features.emailVerification,
108
+ ...(config.features.passwordReset && {
109
+ sendResetPassword: async (data) => {
110
+ await email.sendResetPasswordEmail(data);
111
+ },
112
+ }),
113
+ }),
114
+ },
115
+ ...(credentialsEnabled && {
116
+ emailVerification: {
117
+ sendVerificationEmail: async (data) => {
118
+ await email.sendVerificationEmail(data);
119
+ },
120
+ sendOnSignUp: config.features.emailVerification,
121
+ },
122
+ }),
123
+ socialProviders,
124
+ session: {
125
+ expiresIn: sessionMaxAge,
126
+ cookieCache: {
127
+ enabled: true,
128
+ maxAge: 5 * 60, // 5 minutes — auth'd requests skip the adapter
129
+ },
130
+ },
131
+ ...(plugins.length > 0 && { plugins }),
132
+ });
133
+ }
134
+ function parseSessionDuration(duration) {
135
+ const match = duration.match(/^(\d+)([dhms])$/);
136
+ if (!match)
137
+ return 7 * 24 * 60 * 60; // default 7 days
138
+ const value = parseInt(match[1], 10);
139
+ switch (match[2]) {
140
+ case "d": return value * 24 * 60 * 60;
141
+ case "h": return value * 60 * 60;
142
+ case "m": return value * 60;
143
+ case "s": return value;
144
+ default: return 7 * 24 * 60 * 60;
145
+ }
146
+ }
@@ -0,0 +1,12 @@
1
+ import { betterAuth } from "better-auth";
2
+ import type { AscendKitAuthOptions } from "../shared/types";
3
+ type BetterAuthInstance = ReturnType<typeof betterAuth>;
4
+ type SessionResult = Awaited<ReturnType<BetterAuthInstance["api"]["getSession"]>>;
5
+ export interface AscendKitAuthRuntime {
6
+ handler: (request: Request) => Promise<Response>;
7
+ getSession: (headers: HeadersInit) => Promise<SessionResult>;
8
+ getAuth: () => Promise<BetterAuthInstance>;
9
+ }
10
+ export declare function createAscendKitAuthRuntime(options?: AscendKitAuthOptions): AscendKitAuthRuntime;
11
+ export {};
12
+ //# sourceMappingURL=auth-runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-runtime.d.ts","sourceRoot":"","sources":["../../src/server/auth-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAI5D,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AACxD,KAAK,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;AAOlF,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACjD,UAAU,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IAC7D,OAAO,EAAE,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAAC;CAC5C;AA8BD,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,oBAAyB,GAAG,oBAAoB,CA0HnG"}