@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,585 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import * as react from 'react';
4
+ import { ReactNode } from 'react';
5
+ import { QuotaUser } from '@usequota/types';
6
+ export { ChatCompletionRequest, ChatCompletionResponse, ChatMessage, CreditPackage, QuotaError as QuotaErrorResponse, QuotaMetadata, QuotaSession, QuotaUser, Tool, ToolCall } from '@usequota/types';
7
+ export { Q as QuotaError, a as QuotaInsufficientCreditsError, b as QuotaNotConnectedError, d as QuotaRateLimitError, c as QuotaTokenExpiredError } from './errors-CmNx3kSz.js';
8
+
9
+ /**
10
+ * @usequota/nextjs - Next.js middleware for OAuth callback handling
11
+ *
12
+ * This middleware handles the OAuth callback route and manages token storage.
13
+ * For client-side storage, tokens are stored in httpOnly cookies.
14
+ * For hosted storage, the link is established server-side via external_user_id.
15
+ */
16
+
17
+ interface QuotaConfig {
18
+ /**
19
+ * OAuth client ID from Quota dashboard
20
+ */
21
+ clientId: string;
22
+ /**
23
+ * OAuth client secret (keep this secure, server-side only)
24
+ */
25
+ clientSecret: string;
26
+ /**
27
+ * Quota API base URL
28
+ * @default 'https://api.usequota.app'
29
+ */
30
+ baseUrl?: string;
31
+ /**
32
+ * OAuth callback path
33
+ * @default '/api/quota/callback'
34
+ */
35
+ callbackPath?: string;
36
+ /**
37
+ * Token storage mode
38
+ * - 'client': Store tokens in httpOnly cookies (default)
39
+ * - 'hosted': Use hosted token storage (tokens stored server-side by Quota)
40
+ * @default 'client'
41
+ */
42
+ storageMode?: "client" | "hosted";
43
+ /**
44
+ * Function to get external user ID for hosted mode
45
+ * Only required when storageMode is 'hosted'
46
+ */
47
+ getExternalUserId?: (request: NextRequest) => string | Promise<string>;
48
+ /**
49
+ * Cookie settings
50
+ */
51
+ cookie?: {
52
+ /**
53
+ * Cookie name prefix
54
+ * @default 'quota'
55
+ */
56
+ prefix?: string;
57
+ /**
58
+ * Cookie domain
59
+ */
60
+ domain?: string;
61
+ /**
62
+ * Cookie path
63
+ * @default '/'
64
+ */
65
+ path?: string;
66
+ /**
67
+ * Cookie max age in seconds
68
+ * @default 604800 (7 days)
69
+ */
70
+ maxAge?: number;
71
+ };
72
+ }
73
+ /**
74
+ * Creates Next.js middleware to handle OAuth callback
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * // middleware.ts
79
+ * import { createQuotaMiddleware } from '@usequota/nextjs';
80
+ *
81
+ * export const middleware = createQuotaMiddleware({
82
+ * clientId: process.env.QUOTA_CLIENT_ID!,
83
+ * clientSecret: process.env.QUOTA_CLIENT_SECRET!,
84
+ * });
85
+ *
86
+ * export const config = {
87
+ * matcher: '/api/quota/callback',
88
+ * };
89
+ * ```
90
+ */
91
+ declare function createQuotaMiddleware(config: QuotaConfig): (request: NextRequest) => Promise<NextResponse<unknown>>;
92
+
93
+ interface QuotaContextValue {
94
+ /**
95
+ * Current authenticated user, or null if not logged in
96
+ */
97
+ user: QuotaUser | null;
98
+ /**
99
+ * Whether the user data is currently loading
100
+ */
101
+ isLoading: boolean;
102
+ /**
103
+ * Error that occurred during authentication or user fetch
104
+ */
105
+ error: Error | null;
106
+ /**
107
+ * Redirects to OAuth login flow
108
+ */
109
+ login: () => void;
110
+ /**
111
+ * Logs out the current user
112
+ */
113
+ logout: () => Promise<void>;
114
+ /**
115
+ * Refetches user data from the server
116
+ */
117
+ refetch: () => Promise<void>;
118
+ }
119
+ declare const QuotaContext: react.Context<QuotaContextValue | null>;
120
+ interface QuotaProviderProps {
121
+ children: ReactNode;
122
+ /**
123
+ * OAuth client ID from Quota dashboard
124
+ */
125
+ clientId: string;
126
+ /**
127
+ * Quota API base URL
128
+ * @default 'https://api.usequota.app'
129
+ */
130
+ baseUrl?: string;
131
+ /**
132
+ * OAuth callback path (must match middleware config)
133
+ * @default '/api/quota/callback'
134
+ */
135
+ callbackPath?: string;
136
+ /**
137
+ * Path to your Next.js API route that fetches user data
138
+ * @default '/api/quota/me'
139
+ */
140
+ apiPath?: string;
141
+ }
142
+ /**
143
+ * React context provider for Quota authentication
144
+ *
145
+ * @example
146
+ * ```tsx
147
+ * // app/layout.tsx
148
+ * import { QuotaProvider } from '@usequota/nextjs';
149
+ *
150
+ * export default function RootLayout({ children }) {
151
+ * return (
152
+ * <html>
153
+ * <body>
154
+ * <QuotaProvider clientId={process.env.NEXT_PUBLIC_QUOTA_CLIENT_ID!}>
155
+ * {children}
156
+ * </QuotaProvider>
157
+ * </body>
158
+ * </html>
159
+ * );
160
+ * }
161
+ * ```
162
+ */
163
+ declare function QuotaProvider({ children, clientId, baseUrl, callbackPath, apiPath, }: QuotaProviderProps): react_jsx_runtime.JSX.Element;
164
+ /**
165
+ * Hook to access Quota authentication context
166
+ *
167
+ * @example
168
+ * ```tsx
169
+ * 'use client';
170
+ * import { useQuota } from '@usequota/nextjs';
171
+ *
172
+ * export function MyComponent() {
173
+ * const { user, isLoading, login } = useQuota();
174
+ *
175
+ * if (isLoading) return <div>Loading...</div>;
176
+ * if (!user) return <button onClick={login}>Sign in</button>;
177
+ *
178
+ * return <div>Welcome, {user.email}!</div>;
179
+ * }
180
+ * ```
181
+ */
182
+ declare function useQuota(): QuotaContextValue;
183
+
184
+ /**
185
+ * @usequota/nextjs - React hooks for Quota SDK
186
+ *
187
+ * Provides convenient hooks for accessing Quota features.
188
+ */
189
+
190
+ /**
191
+ * Hook to get the current authenticated user
192
+ *
193
+ * @example
194
+ * ```tsx
195
+ * 'use client';
196
+ * import { useQuotaUser } from '@usequota/nextjs';
197
+ *
198
+ * export function UserProfile() {
199
+ * const user = useQuotaUser();
200
+ *
201
+ * if (!user) return <div>Not logged in</div>;
202
+ *
203
+ * return (
204
+ * <div>
205
+ * <p>Email: {user.email}</p>
206
+ * <p>Balance: {user.balance} credits</p>
207
+ * </div>
208
+ * );
209
+ * }
210
+ * ```
211
+ */
212
+ declare function useQuotaUser(): QuotaUser | null;
213
+ /**
214
+ * Hook to check if user is authenticated
215
+ *
216
+ * @example
217
+ * ```tsx
218
+ * 'use client';
219
+ * import { useQuotaAuth } from '@usequota/nextjs';
220
+ *
221
+ * export function ProtectedContent() {
222
+ * const { isAuthenticated, isLoading, login } = useQuotaAuth();
223
+ *
224
+ * if (isLoading) return <div>Loading...</div>;
225
+ * if (!isAuthenticated) return <button onClick={login}>Sign in</button>;
226
+ *
227
+ * return <div>Protected content</div>;
228
+ * }
229
+ * ```
230
+ */
231
+ declare function useQuotaAuth(): {
232
+ isAuthenticated: boolean;
233
+ isLoading: boolean;
234
+ login: () => void;
235
+ logout: () => Promise<void>;
236
+ };
237
+ /**
238
+ * Hook to get user's credit balance
239
+ *
240
+ * @example
241
+ * ```tsx
242
+ * 'use client';
243
+ * import { useQuotaBalance } from '@usequota/nextjs';
244
+ *
245
+ * export function CreditDisplay() {
246
+ * const { balance, isLoading } = useQuotaBalance();
247
+ *
248
+ * if (isLoading) return <div>Loading...</div>;
249
+ * if (balance === null) return <div>Not logged in</div>;
250
+ *
251
+ * return (
252
+ * <div>
253
+ * Balance: {balance} credits (${(balance / 100).toFixed(2)})
254
+ * </div>
255
+ * );
256
+ * }
257
+ * ```
258
+ */
259
+ declare function useQuotaBalance(): {
260
+ balance: number | null;
261
+ isLoading: boolean;
262
+ error: Error | null;
263
+ refetch: () => Promise<void>;
264
+ };
265
+
266
+ interface QuotaConnectButtonProps {
267
+ /**
268
+ * Custom button content when not logged in
269
+ * @default 'Connect Wallet'
270
+ */
271
+ children?: ReactNode;
272
+ /**
273
+ * Additional CSS class names for styling customization
274
+ */
275
+ className?: string;
276
+ /**
277
+ * Callback fired when OAuth login completes successfully
278
+ */
279
+ onSuccess?: () => void;
280
+ /**
281
+ * Callback fired when an error occurs during login
282
+ */
283
+ onError?: (error: Error) => void;
284
+ /**
285
+ * Button variant style
286
+ * @default 'primary'
287
+ */
288
+ variant?: "primary" | "secondary" | "ghost";
289
+ /**
290
+ * Show loading spinner during authentication
291
+ * @default true
292
+ */
293
+ showLoadingState?: boolean;
294
+ /**
295
+ * If true, shows user info when logged in instead of hiding
296
+ * @default false
297
+ */
298
+ showWhenConnected?: boolean;
299
+ }
300
+ /**
301
+ * OAuth login button for Quota authentication
302
+ *
303
+ * @example
304
+ * ```tsx
305
+ * // Basic usage
306
+ * <QuotaConnectButton />
307
+ *
308
+ * // With custom text
309
+ * <QuotaConnectButton>Sign in with Quota</QuotaConnectButton>
310
+ *
311
+ * // With callbacks
312
+ * <QuotaConnectButton
313
+ * onSuccess={() => console.log('Connected!')}
314
+ * onError={(err) => console.error(err)}
315
+ * />
316
+ * ```
317
+ */
318
+ declare function QuotaConnectButton({ children, className, onSuccess, onError, variant, showLoadingState, showWhenConnected, }: QuotaConnectButtonProps): react_jsx_runtime.JSX.Element | null;
319
+
320
+ /**
321
+ * @usequota/nextjs - QuotaBalance
322
+ *
323
+ * Display component for showing the user's credit balance.
324
+ * Supports both credit and dollar formatting.
325
+ */
326
+ interface QuotaBalanceProps {
327
+ /**
328
+ * Display format for the balance
329
+ * - 'credits': Shows raw credit count (e.g., "1,250 credits")
330
+ * - 'dollars': Shows dollar equivalent (e.g., "$12.50")
331
+ * @default 'credits'
332
+ */
333
+ format?: "credits" | "dollars";
334
+ /**
335
+ * Whether to show the coin/dollar icon
336
+ * @default true
337
+ */
338
+ showIcon?: boolean;
339
+ /**
340
+ * Additional CSS class names for styling customization
341
+ */
342
+ className?: string;
343
+ /**
344
+ * Label text for accessibility
345
+ * @default 'Credit balance'
346
+ */
347
+ ariaLabel?: string;
348
+ /**
349
+ * Show a refresh button to manually refetch balance
350
+ * @default false
351
+ */
352
+ showRefresh?: boolean;
353
+ /**
354
+ * Callback when balance is clicked (useful for linking to buy credits)
355
+ */
356
+ onClick?: () => void;
357
+ }
358
+ /**
359
+ * Display component for user's Quota credit balance
360
+ *
361
+ * @example
362
+ * ```tsx
363
+ * // Basic usage - shows credits
364
+ * <QuotaBalance />
365
+ *
366
+ * // Show as dollars
367
+ * <QuotaBalance format="dollars" />
368
+ *
369
+ * // Clickable balance
370
+ * <QuotaBalance onClick={() => router.push('/buy-credits')} />
371
+ * ```
372
+ */
373
+ declare function QuotaBalance({ format, showIcon, className, ariaLabel, showRefresh, onClick, }: QuotaBalanceProps): react_jsx_runtime.JSX.Element;
374
+
375
+ interface QuotaBuyCreditsProps {
376
+ /**
377
+ * Specific package ID to purchase (from /v1/packages)
378
+ */
379
+ packageId?: string;
380
+ /**
381
+ * Desired credit amount (used to auto-select best package)
382
+ * Ignored if packageId is provided
383
+ */
384
+ amount?: number;
385
+ /**
386
+ * Custom button content
387
+ * @default 'Buy Credits'
388
+ */
389
+ children?: ReactNode;
390
+ /**
391
+ * Additional CSS class names for styling customization
392
+ */
393
+ className?: string;
394
+ /**
395
+ * Callback fired when checkout session is created successfully
396
+ */
397
+ onSuccess?: () => void;
398
+ /**
399
+ * Callback fired when an error occurs
400
+ */
401
+ onError?: (error: Error) => void;
402
+ /**
403
+ * Button variant style
404
+ * @default 'primary'
405
+ */
406
+ variant?: "primary" | "secondary" | "ghost";
407
+ /**
408
+ * Path to the checkout API endpoint
409
+ * @default '/api/quota/checkout'
410
+ */
411
+ checkoutPath?: string;
412
+ /**
413
+ * Disable the button
414
+ * @default false
415
+ */
416
+ disabled?: boolean;
417
+ }
418
+ /**
419
+ * Credit purchase button that redirects to Stripe checkout
420
+ *
421
+ * @example
422
+ * ```tsx
423
+ * // Basic usage - shows package selection
424
+ * <QuotaBuyCredits />
425
+ *
426
+ * // Specific package
427
+ * <QuotaBuyCredits packageId="pack_1000" />
428
+ *
429
+ * // Custom amount (finds best matching package)
430
+ * <QuotaBuyCredits amount={5000}>Get 5000 Credits</QuotaBuyCredits>
431
+ * ```
432
+ */
433
+ declare function QuotaBuyCredits({ packageId, amount, children, className, onSuccess, onError, variant, checkoutPath, disabled, }: QuotaBuyCreditsProps): react_jsx_runtime.JSX.Element;
434
+
435
+ /**
436
+ * @usequota/nextjs - QuotaUserMenu
437
+ *
438
+ * Dropdown menu component showing user avatar, balance, and account actions.
439
+ * Uses headless UI patterns with no external dependencies.
440
+ */
441
+ interface QuotaUserMenuProps {
442
+ /**
443
+ * Additional CSS class names for styling customization
444
+ */
445
+ className?: string;
446
+ /**
447
+ * Callback when "Buy Credits" is clicked
448
+ */
449
+ onBuyCredits?: () => void;
450
+ /**
451
+ * Callback when logout completes
452
+ */
453
+ onLogout?: () => void;
454
+ /**
455
+ * Show the buy credits menu item
456
+ * @default true
457
+ */
458
+ showBuyCredits?: boolean;
459
+ /**
460
+ * Custom content to render in the dropdown
461
+ */
462
+ children?: React.ReactNode;
463
+ }
464
+ /**
465
+ * User account dropdown menu with balance display
466
+ *
467
+ * @example
468
+ * ```tsx
469
+ * // Basic usage
470
+ * <QuotaUserMenu />
471
+ *
472
+ * // With callbacks
473
+ * <QuotaUserMenu
474
+ * onBuyCredits={() => router.push('/buy-credits')}
475
+ * onLogout={() => router.push('/')}
476
+ * />
477
+ *
478
+ * // With custom menu items
479
+ * <QuotaUserMenu>
480
+ * <button onClick={() => router.push('/settings')}>Settings</button>
481
+ * </QuotaUserMenu>
482
+ * ```
483
+ */
484
+ declare function QuotaUserMenu({ className, onBuyCredits, onLogout, showBuyCredits, children, }: QuotaUserMenuProps): react_jsx_runtime.JSX.Element | null;
485
+
486
+ /**
487
+ * Webhook event types sent by Quota
488
+ */
489
+ interface WebhookEvent {
490
+ /** Unique event identifier */
491
+ id: string;
492
+ /** Event type */
493
+ type: "user.connected" | "user.disconnected" | "balance.updated" | "balance.low" | "usage.completed";
494
+ /** ISO 8601 timestamp when event was created */
495
+ created_at: string;
496
+ /** Event-specific data payload */
497
+ data: Record<string, unknown>;
498
+ }
499
+ /**
500
+ * Options for verifying webhook signatures
501
+ */
502
+ interface VerifyWebhookOptions {
503
+ /** Raw webhook payload (string or Buffer) */
504
+ payload: string | Buffer;
505
+ /** Signature from X-Quota-Signature header */
506
+ signature: string;
507
+ /** Webhook secret from Quota dashboard */
508
+ secret: string;
509
+ }
510
+ /**
511
+ * Verify a webhook signature from Quota using HMAC-SHA256
512
+ *
513
+ * @param options - Verification options
514
+ * @returns true if signature is valid, false otherwise
515
+ *
516
+ * @example
517
+ * ```typescript
518
+ * const isValid = verifyWebhookSignature({
519
+ * payload: rawBody,
520
+ * signature: request.headers.get('x-quota-signature')!,
521
+ * secret: process.env.QUOTA_WEBHOOK_SECRET!
522
+ * });
523
+ * ```
524
+ */
525
+ declare function verifyWebhookSignature({ payload, signature, secret, }: VerifyWebhookOptions): boolean;
526
+ /**
527
+ * Parse and verify a webhook request
528
+ * For use in Next.js API routes (App Router)
529
+ *
530
+ * @param req - Next.js Request object
531
+ * @param secret - Webhook secret from Quota dashboard
532
+ * @returns Parsed and verified webhook event
533
+ * @throws Error if signature is missing or invalid
534
+ *
535
+ * @example
536
+ * ```typescript
537
+ * // app/api/quota/webhook/route.ts
538
+ * export async function POST(req: Request) {
539
+ * try {
540
+ * const event = await parseWebhook(req, process.env.QUOTA_WEBHOOK_SECRET!);
541
+ *
542
+ * if (event.type === 'balance.low') {
543
+ * await sendLowBalanceEmail(event.data);
544
+ * }
545
+ *
546
+ * return Response.json({ received: true });
547
+ * } catch (error) {
548
+ * return Response.json({ error: error.message }, { status: 400 });
549
+ * }
550
+ * }
551
+ * ```
552
+ */
553
+ declare function parseWebhook(req: Request, secret: string): Promise<WebhookEvent>;
554
+ /**
555
+ * Create a webhook handler for Next.js App Router
556
+ * Automatically verifies signatures and routes events to handlers
557
+ *
558
+ * @param secret - Webhook secret from Quota dashboard
559
+ * @param handlers - Map of event types to handler functions
560
+ * @returns Next.js route handler function
561
+ *
562
+ * @example
563
+ * ```typescript
564
+ * // app/api/quota/webhook/route.ts
565
+ * import { createWebhookHandler } from '@usequota/nextjs';
566
+ *
567
+ * export const POST = createWebhookHandler(process.env.QUOTA_WEBHOOK_SECRET!, {
568
+ * 'balance.low': async (event) => {
569
+ * // Send email notification
570
+ * await sendLowBalanceEmail(event.data.user_id, event.data.current_balance);
571
+ * },
572
+ * 'user.connected': async (event) => {
573
+ * // Track new user connection
574
+ * await analytics.track('user_connected', event.data);
575
+ * },
576
+ * 'usage.completed': async (event) => {
577
+ * // Log usage for analytics
578
+ * await logUsage(event.data);
579
+ * }
580
+ * });
581
+ * ```
582
+ */
583
+ declare function createWebhookHandler(secret: string, handlers: Partial<Record<WebhookEvent["type"], (event: WebhookEvent) => Promise<void>>>): (req: Request) => Promise<Response>;
584
+
585
+ export { QuotaBalance, type QuotaBalanceProps, QuotaBuyCredits, type QuotaBuyCreditsProps, type QuotaConfig, QuotaConnectButton, type QuotaConnectButtonProps, QuotaContext, type QuotaContextValue, QuotaProvider, type QuotaProviderProps, QuotaUserMenu, type QuotaUserMenuProps, type VerifyWebhookOptions, type WebhookEvent, createQuotaMiddleware, createWebhookHandler, parseWebhook, useQuota, useQuotaAuth, useQuotaBalance, useQuotaUser, verifyWebhookSignature };