@usequota/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.
@@ -0,0 +1,477 @@
1
+ export { Q as QuotaError, a as QuotaInsufficientCreditsError, b as QuotaNotConnectedError, d as QuotaRateLimitError, c as QuotaTokenExpiredError, e as errorFromResponse } from './errors-CmNx3kSz.mjs';
2
+ import { QuotaUser, CreditPackage } from '@usequota/types';
3
+
4
+ /**
5
+ * @usequota/nextjs - Token Storage Adapter
6
+ *
7
+ * Defines the interface for pluggable token storage backends.
8
+ * The default implementation uses httpOnly cookies, but apps can
9
+ * provide their own (e.g., Supabase, Redis, a database).
10
+ */
11
+ /**
12
+ * Pluggable token storage adapter for the Quota SDK.
13
+ *
14
+ * Implement this interface to store Quota OAuth tokens in your own backend
15
+ * (database, Redis, etc.) instead of the default httpOnly cookies.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * import { QuotaTokenStorage } from '@usequota/nextjs/server';
20
+ * import { createClient } from '@supabase/supabase-js';
21
+ *
22
+ * const supabaseTokenStorage: QuotaTokenStorage = {
23
+ * async getTokens(request) {
24
+ * const supabase = createClient(...);
25
+ * const userId = await getUserId(request);
26
+ * const { data } = await supabase
27
+ * .from('quota_accounts')
28
+ * .select('access_token, refresh_token')
29
+ * .eq('user_id', userId)
30
+ * .single();
31
+ * if (!data) return null;
32
+ * return { accessToken: data.access_token, refreshToken: data.refresh_token };
33
+ * },
34
+ *
35
+ * async setTokens(tokens, request) {
36
+ * const supabase = createClient(...);
37
+ * const userId = await getUserId(request);
38
+ * await supabase.from('quota_accounts').upsert({
39
+ * user_id: userId,
40
+ * access_token: tokens.accessToken,
41
+ * refresh_token: tokens.refreshToken,
42
+ * });
43
+ * },
44
+ *
45
+ * async deleteTokens(request) {
46
+ * const supabase = createClient(...);
47
+ * const userId = await getUserId(request);
48
+ * await supabase.from('quota_accounts').delete().eq('user_id', userId);
49
+ * },
50
+ * };
51
+ * ```
52
+ */
53
+ interface QuotaTokenStorage {
54
+ /**
55
+ * Retrieve stored tokens for the current request's user.
56
+ * Return null if no tokens are stored.
57
+ */
58
+ getTokens(request: Request): Promise<{
59
+ accessToken: string;
60
+ refreshToken?: string;
61
+ } | null>;
62
+ /**
63
+ * Persist tokens for the current request's user.
64
+ * Called after OAuth callback and after token refresh.
65
+ *
66
+ * For storage backends that modify the Response (like cookies),
67
+ * return the modified Response. For backends that don't modify
68
+ * the Response (like databases), return void.
69
+ */
70
+ setTokens(tokens: {
71
+ accessToken: string;
72
+ refreshToken?: string;
73
+ expiresIn?: number;
74
+ }, request: Request, response?: Response): Promise<Response | void>;
75
+ /**
76
+ * Delete all stored tokens for the current request's user.
77
+ * Called on disconnect.
78
+ *
79
+ * For storage backends that modify the Response (like cookies),
80
+ * return the modified Response. For backends that don't modify
81
+ * the Response (like databases), return void.
82
+ */
83
+ deleteTokens(request: Request, response?: Response): Promise<Response | void>;
84
+ /**
85
+ * Get the external user ID for hosted mode.
86
+ * Only required if you use hosted storage mode alongside tokenStorage.
87
+ */
88
+ getExternalUserId?(request: Request): Promise<string | null>;
89
+ }
90
+
91
+ /**
92
+ * @usequota/nextjs - Route Handler Factory
93
+ *
94
+ * Creates all necessary Next.js App Router route handlers for Quota integration.
95
+ * Similar pattern to NextAuth's auth() handler.
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * // lib/quota.ts
100
+ * import { createQuotaRouteHandlers } from '@usequota/nextjs/server';
101
+ *
102
+ * export const quotaHandlers = createQuotaRouteHandlers({
103
+ * clientId: process.env.QUOTA_CLIENT_ID!,
104
+ * clientSecret: process.env.QUOTA_CLIENT_SECRET!,
105
+ * });
106
+ *
107
+ * // app/api/quota/authorize/route.ts
108
+ * export { authorize as GET } from '@/lib/quota';
109
+ * // OR: export const GET = quotaHandlers.authorize;
110
+ *
111
+ * // app/api/quota/callback/route.ts
112
+ * export { callback as GET } from '@/lib/quota';
113
+ *
114
+ * // app/api/quota/status/route.ts
115
+ * export { status as GET } from '@/lib/quota';
116
+ *
117
+ * // app/api/quota/packages/route.ts
118
+ * export { packages as GET } from '@/lib/quota';
119
+ *
120
+ * // app/api/quota/checkout/route.ts
121
+ * export { checkout as POST } from '@/lib/quota';
122
+ *
123
+ * // app/api/quota/disconnect/route.ts
124
+ * export { disconnect as POST } from '@/lib/quota';
125
+ * ```
126
+ */
127
+
128
+ interface QuotaRouteHandlerConfig {
129
+ /**
130
+ * OAuth client ID from Quota dashboard
131
+ */
132
+ clientId: string;
133
+ /**
134
+ * OAuth client secret (keep this secure, server-side only)
135
+ */
136
+ clientSecret: string;
137
+ /**
138
+ * Quota API base URL
139
+ * @default 'https://api.usequota.app'
140
+ */
141
+ baseUrl?: string;
142
+ /**
143
+ * Cookie name prefix
144
+ * @default 'quota'
145
+ */
146
+ cookiePrefix?: string;
147
+ /**
148
+ * Cookie max age in seconds
149
+ * @default 604800 (7 days)
150
+ */
151
+ cookieMaxAge?: number;
152
+ /**
153
+ * Token storage mode
154
+ * - 'client': Tokens stored in httpOnly cookies (default)
155
+ * - 'hosted': Tokens stored server-side by Quota
156
+ * @default 'client'
157
+ */
158
+ storageMode?: "client" | "hosted";
159
+ /**
160
+ * Function to get the external user ID (for hosted mode)
161
+ * Receives the Request object; return the ID of the currently logged-in user
162
+ * in your own auth system.
163
+ */
164
+ getExternalUserId?: (request: Request) => string | Promise<string>;
165
+ /**
166
+ * Pluggable token storage adapter.
167
+ * When provided, tokens are stored/retrieved using this adapter
168
+ * instead of httpOnly cookies.
169
+ */
170
+ tokenStorage?: QuotaTokenStorage;
171
+ /**
172
+ * OAuth callback path
173
+ * @default '/api/quota/callback'
174
+ */
175
+ callbackPath?: string;
176
+ /**
177
+ * URL to redirect after successful connection
178
+ * @default '/'
179
+ */
180
+ successRedirect?: string;
181
+ /**
182
+ * URL to redirect after errors
183
+ * @default '/'
184
+ */
185
+ errorRedirect?: string;
186
+ /**
187
+ * Callbacks for lifecycle events
188
+ */
189
+ callbacks?: {
190
+ /**
191
+ * Called after a user successfully connects their Quota account.
192
+ * Use this to save tokens, update your database, etc.
193
+ */
194
+ onConnect?: (data: {
195
+ user: QuotaUser;
196
+ accessToken: string;
197
+ refreshToken: string;
198
+ expiresIn: number;
199
+ request: Request;
200
+ }) => void | Promise<void>;
201
+ /**
202
+ * Called when a user disconnects their Quota account.
203
+ */
204
+ onDisconnect?: (request: Request) => void | Promise<void>;
205
+ };
206
+ }
207
+ interface QuotaRouteHandlers {
208
+ /** GET handler - initiates OAuth authorization flow */
209
+ authorize: (request: Request) => Promise<Response>;
210
+ /** GET handler - handles OAuth callback, exchanges code for tokens */
211
+ callback: (request: Request) => Promise<Response>;
212
+ /** GET handler - returns user connection status and balance */
213
+ status: (request: Request) => Promise<Response>;
214
+ /** GET handler - returns available credit packages */
215
+ packages: (request: Request) => Promise<Response>;
216
+ /** POST handler - creates a Stripe checkout session */
217
+ checkout: (request: Request) => Promise<Response>;
218
+ /** POST handler - disconnects the user's Quota account */
219
+ disconnect: (request: Request) => Promise<Response>;
220
+ }
221
+ /**
222
+ * Creates all needed API route handlers for Quota integration.
223
+ *
224
+ * Returns an object with handlers for: authorize, callback, status, packages, checkout, disconnect.
225
+ * Each handler is a standard Next.js App Router route handler function.
226
+ */
227
+ declare function createQuotaRouteHandlers(config: QuotaRouteHandlerConfig): QuotaRouteHandlers;
228
+
229
+ /**
230
+ * @usequota/nextjs - withQuotaAuth API route wrapper
231
+ *
232
+ * Wraps a Next.js API route handler to automatically check Quota authentication,
233
+ * fetch user info, and pass it to the handler. Returns proper error responses
234
+ * for common failure modes.
235
+ *
236
+ * @example
237
+ * ```typescript
238
+ * // app/api/summarize/route.ts
239
+ * import { withQuotaAuth } from '@usequota/nextjs/server';
240
+ *
241
+ * export const POST = withQuotaAuth(
242
+ * {
243
+ * clientId: process.env.QUOTA_CLIENT_ID!,
244
+ * clientSecret: process.env.QUOTA_CLIENT_SECRET!,
245
+ * },
246
+ * async (request, { user, accessToken }) => {
247
+ * // user is guaranteed to exist and be connected
248
+ * // accessToken can be used to proxy AI requests through Quota
249
+ * const client = new Anthropic({
250
+ * authToken: accessToken,
251
+ * baseURL: 'https://api.usequota.app',
252
+ * });
253
+ *
254
+ * const result = await client.messages.create({ ... });
255
+ * return Response.json(result);
256
+ * }
257
+ * );
258
+ * ```
259
+ */
260
+
261
+ interface WithQuotaAuthConfig {
262
+ /**
263
+ * OAuth client ID
264
+ */
265
+ clientId: string;
266
+ /**
267
+ * OAuth client secret
268
+ */
269
+ clientSecret: string;
270
+ /**
271
+ * Quota API base URL
272
+ * @default 'https://api.usequota.app'
273
+ */
274
+ baseUrl?: string;
275
+ /**
276
+ * Cookie prefix (must match middleware/route handler config)
277
+ * @default 'quota'
278
+ */
279
+ cookiePrefix?: string;
280
+ /**
281
+ * Cookie max age in seconds
282
+ * @default 604800 (7 days)
283
+ */
284
+ cookieMaxAge?: number;
285
+ /**
286
+ * Token storage mode
287
+ * @default 'client'
288
+ */
289
+ storageMode?: "client" | "hosted";
290
+ /**
291
+ * Function to get external user ID (required for hosted mode)
292
+ */
293
+ getExternalUserId?: (request: Request) => string | Promise<string>;
294
+ /**
295
+ * Pluggable token storage adapter.
296
+ * When provided, tokens are read/refreshed using this adapter
297
+ * instead of httpOnly cookies.
298
+ */
299
+ tokenStorage?: QuotaTokenStorage;
300
+ }
301
+ interface QuotaAuthContext {
302
+ /** The authenticated Quota user */
303
+ user: QuotaUser;
304
+ /** The OAuth access token (empty string in hosted mode) */
305
+ accessToken: string;
306
+ }
307
+ type QuotaAuthHandler = (request: Request, context: QuotaAuthContext) => Promise<Response>;
308
+ /**
309
+ * Wraps an API route handler with Quota authentication.
310
+ *
311
+ * The wrapper:
312
+ * 1. Checks if the user has a Quota access token
313
+ * 2. Fetches the user's info from Quota
314
+ * 3. If the token is expired, attempts to refresh it
315
+ * 4. Passes the user and token to the handler
316
+ * 5. Catches typed Quota errors and returns proper HTTP responses
317
+ */
318
+ declare function withQuotaAuth(config: WithQuotaAuthConfig, handler: QuotaAuthHandler): (request: Request) => Promise<Response>;
319
+
320
+ /**
321
+ * @usequota/nextjs/server - Server-side utilities for Quota SDK
322
+ *
323
+ * Provides utilities for accessing Quota from Next.js server components and API routes.
324
+ */
325
+
326
+ interface QuotaServerConfig {
327
+ /**
328
+ * OAuth client ID from Quota dashboard
329
+ */
330
+ clientId: string;
331
+ /**
332
+ * OAuth client secret (keep this secure, server-side only)
333
+ */
334
+ clientSecret: string;
335
+ /**
336
+ * Quota API base URL
337
+ * @default 'https://api.usequota.app'
338
+ */
339
+ baseUrl?: string;
340
+ /**
341
+ * Cookie prefix (must match middleware config)
342
+ * @default 'quota'
343
+ */
344
+ cookiePrefix?: string;
345
+ /**
346
+ * Cookie max age in seconds
347
+ * @default 604800 (7 days)
348
+ */
349
+ cookieMaxAge?: number;
350
+ /**
351
+ * Token storage mode
352
+ * - 'client': Tokens stored in httpOnly cookies (default)
353
+ * - 'hosted': Tokens stored server-side by Quota
354
+ * @default 'client'
355
+ */
356
+ storageMode?: "client" | "hosted";
357
+ /**
358
+ * Function to get external user ID for hosted mode
359
+ * Only required when storageMode is 'hosted'
360
+ */
361
+ getExternalUserId?: () => string | Promise<string>;
362
+ }
363
+ /**
364
+ * Gets the current authenticated user from server-side
365
+ *
366
+ * @example
367
+ * ```typescript
368
+ * // app/api/quota/me/route.ts
369
+ * import { getQuotaUser } from '@usequota/nextjs/server';
370
+ *
371
+ * export async function GET() {
372
+ * const user = await getQuotaUser({
373
+ * clientId: process.env.QUOTA_CLIENT_ID!,
374
+ * clientSecret: process.env.QUOTA_CLIENT_SECRET!,
375
+ * });
376
+ *
377
+ * if (!user) {
378
+ * return new Response('Unauthorized', { status: 401 });
379
+ * }
380
+ *
381
+ * return Response.json(user);
382
+ * }
383
+ * ```
384
+ */
385
+ declare function getQuotaUser(config: QuotaServerConfig): Promise<QuotaUser | null>;
386
+ /**
387
+ * Requires authentication, redirects to login if not authenticated
388
+ *
389
+ * @example
390
+ * ```typescript
391
+ * // app/dashboard/page.tsx
392
+ * import { requireQuotaAuth } from '@usequota/nextjs/server';
393
+ *
394
+ * export default async function DashboardPage() {
395
+ * const user = await requireQuotaAuth({
396
+ * clientId: process.env.QUOTA_CLIENT_ID!,
397
+ * clientSecret: process.env.QUOTA_CLIENT_SECRET!,
398
+ * });
399
+ *
400
+ * return <div>Welcome, {user.email}!</div>;
401
+ * }
402
+ * ```
403
+ */
404
+ declare function requireQuotaAuth(config: QuotaServerConfig): Promise<QuotaUser>;
405
+ /**
406
+ * Fetches available credit packages
407
+ *
408
+ * @example
409
+ * ```typescript
410
+ * // app/api/packages/route.ts
411
+ * import { getQuotaPackages } from '@usequota/nextjs/server';
412
+ *
413
+ * export async function GET() {
414
+ * const packages = await getQuotaPackages({
415
+ * baseUrl: process.env.NEXT_PUBLIC_QUOTA_BASE_URL,
416
+ * });
417
+ *
418
+ * return Response.json(packages);
419
+ * }
420
+ * ```
421
+ */
422
+ declare function getQuotaPackages(config?: {
423
+ baseUrl?: string;
424
+ }): Promise<CreditPackage[]>;
425
+ /**
426
+ * Creates a Stripe checkout session for purchasing credits
427
+ *
428
+ * @example
429
+ * ```typescript
430
+ * // app/api/checkout/route.ts
431
+ * import { createQuotaCheckout } from '@usequota/nextjs/server';
432
+ *
433
+ * export async function POST(request: Request) {
434
+ * const { packageId } = await request.json();
435
+ *
436
+ * const checkoutUrl = await createQuotaCheckout({
437
+ * clientId: process.env.QUOTA_CLIENT_ID!,
438
+ * clientSecret: process.env.QUOTA_CLIENT_SECRET!,
439
+ * packageId,
440
+ * successUrl: `${process.env.NEXT_PUBLIC_APP_URL}/success`,
441
+ * cancelUrl: `${process.env.NEXT_PUBLIC_APP_URL}/cancel`,
442
+ * });
443
+ *
444
+ * return Response.json({ url: checkoutUrl });
445
+ * }
446
+ * ```
447
+ */
448
+ declare function createQuotaCheckout(config: {
449
+ clientId: string;
450
+ clientSecret: string;
451
+ packageId: string;
452
+ successUrl: string;
453
+ cancelUrl: string;
454
+ baseUrl?: string;
455
+ cookiePrefix?: string;
456
+ storageMode?: "client" | "hosted";
457
+ getExternalUserId?: () => string | Promise<string>;
458
+ }): Promise<string>;
459
+ /**
460
+ * Clears authentication cookies (logout)
461
+ *
462
+ * @example
463
+ * ```typescript
464
+ * // app/api/quota/logout/route.ts
465
+ * import { clearQuotaAuth } from '@usequota/nextjs/server';
466
+ *
467
+ * export async function POST() {
468
+ * await clearQuotaAuth();
469
+ * return Response.json({ success: true });
470
+ * }
471
+ * ```
472
+ */
473
+ declare function clearQuotaAuth(config?: {
474
+ cookiePrefix?: string;
475
+ }): Promise<void>;
476
+
477
+ export { type QuotaAuthContext, type QuotaRouteHandlerConfig, type QuotaRouteHandlers, type QuotaServerConfig, type QuotaTokenStorage, type WithQuotaAuthConfig, clearQuotaAuth, createQuotaCheckout, createQuotaRouteHandlers, getQuotaPackages, getQuotaUser, requireQuotaAuth, withQuotaAuth };