@usequota/nextjs 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.d.mts +2 -101
- package/dist/index.d.ts +2 -101
- package/dist/index.js +13 -158
- package/dist/index.mjs +15 -73
- package/dist/server.d.mts +3 -89
- package/dist/server.d.ts +3 -89
- package/dist/server.js +79 -183
- package/dist/server.mjs +59 -49
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @usequota/nextjs
|
|
2
2
|
|
|
3
|
-
Next.js SDK for [Quota](https://usequota.
|
|
3
|
+
Next.js SDK for [Quota](https://usequota.ai) - AI credit wallet and multi-provider inference API.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -409,7 +409,7 @@ export const POST = withQuotaAuth(
|
|
|
409
409
|
// user is guaranteed to exist
|
|
410
410
|
// accessToken can proxy requests through Quota
|
|
411
411
|
const response = await fetch(
|
|
412
|
-
"https://api.usequota.
|
|
412
|
+
"https://api.usequota.ai/v1/chat/completions",
|
|
413
413
|
{
|
|
414
414
|
method: "POST",
|
|
415
415
|
headers: {
|
package/dist/index.d.mts
CHANGED
|
@@ -4,7 +4,7 @@ import * as react from 'react';
|
|
|
4
4
|
import { ReactNode } from 'react';
|
|
5
5
|
import { QuotaUser, CreditPackage } from '@usequota/types';
|
|
6
6
|
export { ChatCompletionRequest, ChatCompletionResponse, ChatMessage, CreditPackage, QuotaError as QuotaErrorResponse, QuotaMetadata, QuotaSession, QuotaUser, Tool, ToolCall } from '@usequota/types';
|
|
7
|
-
export {
|
|
7
|
+
export { QuotaError, QuotaInsufficientCreditsError, QuotaNotConnectedError, QuotaRateLimitError, QuotaTokenExpiredError, VerifyWebhookOptions, WebhookEvent, createWebhookHandler, parseWebhook, verifyWebhookSignature } from '@usequota/core';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @usequota/nextjs - Next.js middleware for OAuth callback handling
|
|
@@ -541,103 +541,4 @@ interface QuotaUserMenuProps {
|
|
|
541
541
|
*/
|
|
542
542
|
declare function QuotaUserMenu({ className, onBuyCredits, onLogout, showBuyCredits, children, }: QuotaUserMenuProps): react_jsx_runtime.JSX.Element | null;
|
|
543
543
|
|
|
544
|
-
|
|
545
|
-
* Webhook event types sent by Quota
|
|
546
|
-
*/
|
|
547
|
-
interface WebhookEvent {
|
|
548
|
-
/** Unique event identifier */
|
|
549
|
-
id: string;
|
|
550
|
-
/** Event type */
|
|
551
|
-
type: "user.connected" | "user.disconnected" | "balance.updated" | "balance.low" | "usage.completed";
|
|
552
|
-
/** ISO 8601 timestamp when event was created */
|
|
553
|
-
created_at: string;
|
|
554
|
-
/** Event-specific data payload */
|
|
555
|
-
data: Record<string, unknown>;
|
|
556
|
-
}
|
|
557
|
-
/**
|
|
558
|
-
* Options for verifying webhook signatures
|
|
559
|
-
*/
|
|
560
|
-
interface VerifyWebhookOptions {
|
|
561
|
-
/** Raw webhook payload (string or Buffer) */
|
|
562
|
-
payload: string | Buffer;
|
|
563
|
-
/** Signature from X-Quota-Signature header */
|
|
564
|
-
signature: string;
|
|
565
|
-
/** Webhook secret from Quota dashboard */
|
|
566
|
-
secret: string;
|
|
567
|
-
}
|
|
568
|
-
/**
|
|
569
|
-
* Verify a webhook signature from Quota using HMAC-SHA256
|
|
570
|
-
*
|
|
571
|
-
* @param options - Verification options
|
|
572
|
-
* @returns true if signature is valid, false otherwise
|
|
573
|
-
*
|
|
574
|
-
* @example
|
|
575
|
-
* ```typescript
|
|
576
|
-
* const isValid = verifyWebhookSignature({
|
|
577
|
-
* payload: rawBody,
|
|
578
|
-
* signature: request.headers.get('x-quota-signature')!,
|
|
579
|
-
* secret: process.env.QUOTA_WEBHOOK_SECRET!
|
|
580
|
-
* });
|
|
581
|
-
* ```
|
|
582
|
-
*/
|
|
583
|
-
declare function verifyWebhookSignature({ payload, signature, secret, }: VerifyWebhookOptions): boolean;
|
|
584
|
-
/**
|
|
585
|
-
* Parse and verify a webhook request
|
|
586
|
-
* For use in Next.js API routes (App Router)
|
|
587
|
-
*
|
|
588
|
-
* @param req - Next.js Request object
|
|
589
|
-
* @param secret - Webhook secret from Quota dashboard
|
|
590
|
-
* @returns Parsed and verified webhook event
|
|
591
|
-
* @throws Error if signature is missing or invalid
|
|
592
|
-
*
|
|
593
|
-
* @example
|
|
594
|
-
* ```typescript
|
|
595
|
-
* // app/api/quota/webhook/route.ts
|
|
596
|
-
* export async function POST(req: Request) {
|
|
597
|
-
* try {
|
|
598
|
-
* const event = await parseWebhook(req, process.env.QUOTA_WEBHOOK_SECRET!);
|
|
599
|
-
*
|
|
600
|
-
* if (event.type === 'balance.low') {
|
|
601
|
-
* await sendLowBalanceEmail(event.data);
|
|
602
|
-
* }
|
|
603
|
-
*
|
|
604
|
-
* return Response.json({ received: true });
|
|
605
|
-
* } catch (error) {
|
|
606
|
-
* return Response.json({ error: error.message }, { status: 400 });
|
|
607
|
-
* }
|
|
608
|
-
* }
|
|
609
|
-
* ```
|
|
610
|
-
*/
|
|
611
|
-
declare function parseWebhook(req: Request, secret: string): Promise<WebhookEvent>;
|
|
612
|
-
/**
|
|
613
|
-
* Create a webhook handler for Next.js App Router
|
|
614
|
-
* Automatically verifies signatures and routes events to handlers
|
|
615
|
-
*
|
|
616
|
-
* @param secret - Webhook secret from Quota dashboard
|
|
617
|
-
* @param handlers - Map of event types to handler functions
|
|
618
|
-
* @returns Next.js route handler function
|
|
619
|
-
*
|
|
620
|
-
* @example
|
|
621
|
-
* ```typescript
|
|
622
|
-
* // app/api/quota/webhook/route.ts
|
|
623
|
-
* import { createWebhookHandler } from '@usequota/nextjs';
|
|
624
|
-
*
|
|
625
|
-
* export const POST = createWebhookHandler(process.env.QUOTA_WEBHOOK_SECRET!, {
|
|
626
|
-
* 'balance.low': async (event) => {
|
|
627
|
-
* // Send email notification
|
|
628
|
-
* await sendLowBalanceEmail(event.data.user_id, event.data.current_balance);
|
|
629
|
-
* },
|
|
630
|
-
* 'user.connected': async (event) => {
|
|
631
|
-
* // Track new user connection
|
|
632
|
-
* await analytics.track('user_connected', event.data);
|
|
633
|
-
* },
|
|
634
|
-
* 'usage.completed': async (event) => {
|
|
635
|
-
* // Log usage for analytics
|
|
636
|
-
* await logUsage(event.data);
|
|
637
|
-
* }
|
|
638
|
-
* });
|
|
639
|
-
* ```
|
|
640
|
-
*/
|
|
641
|
-
declare function createWebhookHandler(secret: string, handlers: Partial<Record<WebhookEvent["type"], (event: WebhookEvent) => Promise<void>>>): (req: Request) => Promise<Response>;
|
|
642
|
-
|
|
643
|
-
export { QuotaBalance, type QuotaBalanceProps, QuotaBuyCredits, type QuotaBuyCreditsProps, type QuotaConfig, QuotaConnectButton, type QuotaConnectButtonProps, QuotaContext, type QuotaContextValue, type QuotaFetchStrategy, QuotaProvider, type QuotaProviderProps, QuotaUserMenu, type QuotaUserMenuProps, type VerifyWebhookOptions, type WebhookEvent, createQuotaMiddleware, createWebhookHandler, parseWebhook, useQuota, useQuotaAuth, useQuotaBalance, useQuotaPackages, useQuotaUser, verifyWebhookSignature };
|
|
544
|
+
export { QuotaBalance, type QuotaBalanceProps, QuotaBuyCredits, type QuotaBuyCreditsProps, type QuotaConfig, QuotaConnectButton, type QuotaConnectButtonProps, QuotaContext, type QuotaContextValue, type QuotaFetchStrategy, QuotaProvider, type QuotaProviderProps, QuotaUserMenu, type QuotaUserMenuProps, createQuotaMiddleware, useQuota, useQuotaAuth, useQuotaBalance, useQuotaPackages, useQuotaUser };
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import * as react from 'react';
|
|
|
4
4
|
import { ReactNode } from 'react';
|
|
5
5
|
import { QuotaUser, CreditPackage } from '@usequota/types';
|
|
6
6
|
export { ChatCompletionRequest, ChatCompletionResponse, ChatMessage, CreditPackage, QuotaError as QuotaErrorResponse, QuotaMetadata, QuotaSession, QuotaUser, Tool, ToolCall } from '@usequota/types';
|
|
7
|
-
export {
|
|
7
|
+
export { QuotaError, QuotaInsufficientCreditsError, QuotaNotConnectedError, QuotaRateLimitError, QuotaTokenExpiredError, VerifyWebhookOptions, WebhookEvent, createWebhookHandler, parseWebhook, verifyWebhookSignature } from '@usequota/core';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @usequota/nextjs - Next.js middleware for OAuth callback handling
|
|
@@ -541,103 +541,4 @@ interface QuotaUserMenuProps {
|
|
|
541
541
|
*/
|
|
542
542
|
declare function QuotaUserMenu({ className, onBuyCredits, onLogout, showBuyCredits, children, }: QuotaUserMenuProps): react_jsx_runtime.JSX.Element | null;
|
|
543
543
|
|
|
544
|
-
|
|
545
|
-
* Webhook event types sent by Quota
|
|
546
|
-
*/
|
|
547
|
-
interface WebhookEvent {
|
|
548
|
-
/** Unique event identifier */
|
|
549
|
-
id: string;
|
|
550
|
-
/** Event type */
|
|
551
|
-
type: "user.connected" | "user.disconnected" | "balance.updated" | "balance.low" | "usage.completed";
|
|
552
|
-
/** ISO 8601 timestamp when event was created */
|
|
553
|
-
created_at: string;
|
|
554
|
-
/** Event-specific data payload */
|
|
555
|
-
data: Record<string, unknown>;
|
|
556
|
-
}
|
|
557
|
-
/**
|
|
558
|
-
* Options for verifying webhook signatures
|
|
559
|
-
*/
|
|
560
|
-
interface VerifyWebhookOptions {
|
|
561
|
-
/** Raw webhook payload (string or Buffer) */
|
|
562
|
-
payload: string | Buffer;
|
|
563
|
-
/** Signature from X-Quota-Signature header */
|
|
564
|
-
signature: string;
|
|
565
|
-
/** Webhook secret from Quota dashboard */
|
|
566
|
-
secret: string;
|
|
567
|
-
}
|
|
568
|
-
/**
|
|
569
|
-
* Verify a webhook signature from Quota using HMAC-SHA256
|
|
570
|
-
*
|
|
571
|
-
* @param options - Verification options
|
|
572
|
-
* @returns true if signature is valid, false otherwise
|
|
573
|
-
*
|
|
574
|
-
* @example
|
|
575
|
-
* ```typescript
|
|
576
|
-
* const isValid = verifyWebhookSignature({
|
|
577
|
-
* payload: rawBody,
|
|
578
|
-
* signature: request.headers.get('x-quota-signature')!,
|
|
579
|
-
* secret: process.env.QUOTA_WEBHOOK_SECRET!
|
|
580
|
-
* });
|
|
581
|
-
* ```
|
|
582
|
-
*/
|
|
583
|
-
declare function verifyWebhookSignature({ payload, signature, secret, }: VerifyWebhookOptions): boolean;
|
|
584
|
-
/**
|
|
585
|
-
* Parse and verify a webhook request
|
|
586
|
-
* For use in Next.js API routes (App Router)
|
|
587
|
-
*
|
|
588
|
-
* @param req - Next.js Request object
|
|
589
|
-
* @param secret - Webhook secret from Quota dashboard
|
|
590
|
-
* @returns Parsed and verified webhook event
|
|
591
|
-
* @throws Error if signature is missing or invalid
|
|
592
|
-
*
|
|
593
|
-
* @example
|
|
594
|
-
* ```typescript
|
|
595
|
-
* // app/api/quota/webhook/route.ts
|
|
596
|
-
* export async function POST(req: Request) {
|
|
597
|
-
* try {
|
|
598
|
-
* const event = await parseWebhook(req, process.env.QUOTA_WEBHOOK_SECRET!);
|
|
599
|
-
*
|
|
600
|
-
* if (event.type === 'balance.low') {
|
|
601
|
-
* await sendLowBalanceEmail(event.data);
|
|
602
|
-
* }
|
|
603
|
-
*
|
|
604
|
-
* return Response.json({ received: true });
|
|
605
|
-
* } catch (error) {
|
|
606
|
-
* return Response.json({ error: error.message }, { status: 400 });
|
|
607
|
-
* }
|
|
608
|
-
* }
|
|
609
|
-
* ```
|
|
610
|
-
*/
|
|
611
|
-
declare function parseWebhook(req: Request, secret: string): Promise<WebhookEvent>;
|
|
612
|
-
/**
|
|
613
|
-
* Create a webhook handler for Next.js App Router
|
|
614
|
-
* Automatically verifies signatures and routes events to handlers
|
|
615
|
-
*
|
|
616
|
-
* @param secret - Webhook secret from Quota dashboard
|
|
617
|
-
* @param handlers - Map of event types to handler functions
|
|
618
|
-
* @returns Next.js route handler function
|
|
619
|
-
*
|
|
620
|
-
* @example
|
|
621
|
-
* ```typescript
|
|
622
|
-
* // app/api/quota/webhook/route.ts
|
|
623
|
-
* import { createWebhookHandler } from '@usequota/nextjs';
|
|
624
|
-
*
|
|
625
|
-
* export const POST = createWebhookHandler(process.env.QUOTA_WEBHOOK_SECRET!, {
|
|
626
|
-
* 'balance.low': async (event) => {
|
|
627
|
-
* // Send email notification
|
|
628
|
-
* await sendLowBalanceEmail(event.data.user_id, event.data.current_balance);
|
|
629
|
-
* },
|
|
630
|
-
* 'user.connected': async (event) => {
|
|
631
|
-
* // Track new user connection
|
|
632
|
-
* await analytics.track('user_connected', event.data);
|
|
633
|
-
* },
|
|
634
|
-
* 'usage.completed': async (event) => {
|
|
635
|
-
* // Log usage for analytics
|
|
636
|
-
* await logUsage(event.data);
|
|
637
|
-
* }
|
|
638
|
-
* });
|
|
639
|
-
* ```
|
|
640
|
-
*/
|
|
641
|
-
declare function createWebhookHandler(secret: string, handlers: Partial<Record<WebhookEvent["type"], (event: WebhookEvent) => Promise<void>>>): (req: Request) => Promise<Response>;
|
|
642
|
-
|
|
643
|
-
export { QuotaBalance, type QuotaBalanceProps, QuotaBuyCredits, type QuotaBuyCreditsProps, type QuotaConfig, QuotaConnectButton, type QuotaConnectButtonProps, QuotaContext, type QuotaContextValue, type QuotaFetchStrategy, QuotaProvider, type QuotaProviderProps, QuotaUserMenu, type QuotaUserMenuProps, type VerifyWebhookOptions, type WebhookEvent, createQuotaMiddleware, createWebhookHandler, parseWebhook, useQuota, useQuotaAuth, useQuotaBalance, useQuotaPackages, useQuotaUser, verifyWebhookSignature };
|
|
544
|
+
export { QuotaBalance, type QuotaBalanceProps, QuotaBuyCredits, type QuotaBuyCreditsProps, type QuotaConfig, QuotaConnectButton, type QuotaConnectButtonProps, QuotaContext, type QuotaContextValue, type QuotaFetchStrategy, QuotaProvider, type QuotaProviderProps, QuotaUserMenu, type QuotaUserMenuProps, createQuotaMiddleware, useQuota, useQuotaAuth, useQuotaBalance, useQuotaPackages, useQuotaUser };
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
6
|
var __export = (target, all) => {
|
|
9
7
|
for (var name in all)
|
|
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
15
|
}
|
|
18
16
|
return to;
|
|
19
17
|
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
19
|
|
|
30
20
|
// src/index.ts
|
|
@@ -34,22 +24,22 @@ __export(index_exports, {
|
|
|
34
24
|
QuotaBuyCredits: () => QuotaBuyCredits,
|
|
35
25
|
QuotaConnectButton: () => QuotaConnectButton,
|
|
36
26
|
QuotaContext: () => QuotaContext,
|
|
37
|
-
QuotaError: () => QuotaError,
|
|
38
|
-
QuotaInsufficientCreditsError: () => QuotaInsufficientCreditsError,
|
|
39
|
-
QuotaNotConnectedError: () => QuotaNotConnectedError,
|
|
27
|
+
QuotaError: () => import_core.QuotaError,
|
|
28
|
+
QuotaInsufficientCreditsError: () => import_core.QuotaInsufficientCreditsError,
|
|
29
|
+
QuotaNotConnectedError: () => import_core.QuotaNotConnectedError,
|
|
40
30
|
QuotaProvider: () => QuotaProvider,
|
|
41
|
-
QuotaRateLimitError: () => QuotaRateLimitError,
|
|
42
|
-
QuotaTokenExpiredError: () => QuotaTokenExpiredError,
|
|
31
|
+
QuotaRateLimitError: () => import_core.QuotaRateLimitError,
|
|
32
|
+
QuotaTokenExpiredError: () => import_core.QuotaTokenExpiredError,
|
|
43
33
|
QuotaUserMenu: () => QuotaUserMenu,
|
|
44
34
|
createQuotaMiddleware: () => createQuotaMiddleware,
|
|
45
|
-
createWebhookHandler: () => createWebhookHandler,
|
|
46
|
-
parseWebhook: () => parseWebhook,
|
|
35
|
+
createWebhookHandler: () => import_core2.createWebhookHandler,
|
|
36
|
+
parseWebhook: () => import_core2.parseWebhook,
|
|
47
37
|
useQuota: () => useQuota,
|
|
48
38
|
useQuotaAuth: () => useQuotaAuth,
|
|
49
39
|
useQuotaBalance: () => useQuotaBalance,
|
|
50
40
|
useQuotaPackages: () => useQuotaPackages,
|
|
51
41
|
useQuotaUser: () => useQuotaUser,
|
|
52
|
-
verifyWebhookSignature: () => verifyWebhookSignature
|
|
42
|
+
verifyWebhookSignature: () => import_core2.verifyWebhookSignature
|
|
53
43
|
});
|
|
54
44
|
module.exports = __toCommonJS(index_exports);
|
|
55
45
|
|
|
@@ -120,7 +110,8 @@ function createQuotaMiddleware(config) {
|
|
|
120
110
|
headers: {
|
|
121
111
|
"Content-Type": "application/json"
|
|
122
112
|
},
|
|
123
|
-
body: JSON.stringify(tokenBody)
|
|
113
|
+
body: JSON.stringify(tokenBody),
|
|
114
|
+
signal: AbortSignal.timeout(1e4)
|
|
124
115
|
});
|
|
125
116
|
if (!tokenResponse.ok) {
|
|
126
117
|
const errorData = await tokenResponse.json();
|
|
@@ -137,16 +128,6 @@ function createQuotaMiddleware(config) {
|
|
|
137
128
|
redirectUrl.searchParams.set("quota_success", "true");
|
|
138
129
|
const response = import_server.NextResponse.redirect(redirectUrl);
|
|
139
130
|
if ("access_token" in tokenData) {
|
|
140
|
-
const cookieOptions = [
|
|
141
|
-
`Path=${cookiePath}`,
|
|
142
|
-
"HttpOnly",
|
|
143
|
-
"Secure",
|
|
144
|
-
"SameSite=Lax",
|
|
145
|
-
`Max-Age=${cookieMaxAge}`
|
|
146
|
-
];
|
|
147
|
-
if (cookieDomain) {
|
|
148
|
-
cookieOptions.push(`Domain=${cookieDomain}`);
|
|
149
|
-
}
|
|
150
131
|
response.cookies.set(
|
|
151
132
|
`${cookiePrefix}_access_token`,
|
|
152
133
|
tokenData.access_token,
|
|
@@ -965,135 +946,9 @@ function LogoutIcon() {
|
|
|
965
946
|
);
|
|
966
947
|
}
|
|
967
948
|
|
|
968
|
-
// src/
|
|
969
|
-
var
|
|
970
|
-
|
|
971
|
-
code;
|
|
972
|
-
/** HTTP status code associated with this error */
|
|
973
|
-
statusCode;
|
|
974
|
-
/** Optional hint for resolving the error */
|
|
975
|
-
hint;
|
|
976
|
-
constructor(message, code, statusCode, hint) {
|
|
977
|
-
super(message);
|
|
978
|
-
this.name = "QuotaError";
|
|
979
|
-
this.code = code;
|
|
980
|
-
this.statusCode = statusCode;
|
|
981
|
-
this.hint = hint;
|
|
982
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
983
|
-
}
|
|
984
|
-
};
|
|
985
|
-
var QuotaInsufficientCreditsError = class extends QuotaError {
|
|
986
|
-
/** Current balance (if available) */
|
|
987
|
-
balance;
|
|
988
|
-
/** Credits required for the operation (if available) */
|
|
989
|
-
required;
|
|
990
|
-
constructor(message, options) {
|
|
991
|
-
super(
|
|
992
|
-
message ?? "Insufficient credits to complete this operation",
|
|
993
|
-
"insufficient_credits",
|
|
994
|
-
402,
|
|
995
|
-
"Purchase more credits or reduce usage"
|
|
996
|
-
);
|
|
997
|
-
this.name = "QuotaInsufficientCreditsError";
|
|
998
|
-
this.balance = options?.balance;
|
|
999
|
-
this.required = options?.required;
|
|
1000
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
1001
|
-
}
|
|
1002
|
-
};
|
|
1003
|
-
var QuotaNotConnectedError = class extends QuotaError {
|
|
1004
|
-
constructor(message) {
|
|
1005
|
-
super(
|
|
1006
|
-
message ?? "User has not connected a Quota account",
|
|
1007
|
-
"not_connected",
|
|
1008
|
-
401,
|
|
1009
|
-
"Connect your Quota account to use this feature"
|
|
1010
|
-
);
|
|
1011
|
-
this.name = "QuotaNotConnectedError";
|
|
1012
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
1013
|
-
}
|
|
1014
|
-
};
|
|
1015
|
-
var QuotaTokenExpiredError = class extends QuotaError {
|
|
1016
|
-
constructor(message) {
|
|
1017
|
-
super(
|
|
1018
|
-
message ?? "Quota access token has expired and could not be refreshed",
|
|
1019
|
-
"token_expired",
|
|
1020
|
-
401,
|
|
1021
|
-
"Reconnect your Quota account"
|
|
1022
|
-
);
|
|
1023
|
-
this.name = "QuotaTokenExpiredError";
|
|
1024
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
1025
|
-
}
|
|
1026
|
-
};
|
|
1027
|
-
var QuotaRateLimitError = class extends QuotaError {
|
|
1028
|
-
/** Seconds until the rate limit resets */
|
|
1029
|
-
retryAfter;
|
|
1030
|
-
constructor(message, retryAfter) {
|
|
1031
|
-
super(
|
|
1032
|
-
message ?? "Rate limit exceeded",
|
|
1033
|
-
"rate_limit_exceeded",
|
|
1034
|
-
429,
|
|
1035
|
-
"Wait before retrying"
|
|
1036
|
-
);
|
|
1037
|
-
this.name = "QuotaRateLimitError";
|
|
1038
|
-
this.retryAfter = retryAfter ?? 60;
|
|
1039
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
1040
|
-
}
|
|
1041
|
-
};
|
|
1042
|
-
|
|
1043
|
-
// src/webhooks.ts
|
|
1044
|
-
var import_crypto = __toESM(require("crypto"));
|
|
1045
|
-
function verifyWebhookSignature({
|
|
1046
|
-
payload,
|
|
1047
|
-
signature,
|
|
1048
|
-
secret
|
|
1049
|
-
}) {
|
|
1050
|
-
const payloadString = typeof payload === "string" ? payload : payload.toString("utf8");
|
|
1051
|
-
const hmac = import_crypto.default.createHmac("sha256", secret);
|
|
1052
|
-
hmac.update(payloadString);
|
|
1053
|
-
const expectedSignature = hmac.digest("hex");
|
|
1054
|
-
try {
|
|
1055
|
-
return import_crypto.default.timingSafeEqual(
|
|
1056
|
-
Buffer.from(signature, "hex"),
|
|
1057
|
-
Buffer.from(expectedSignature, "hex")
|
|
1058
|
-
);
|
|
1059
|
-
} catch {
|
|
1060
|
-
return false;
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
async function parseWebhook(req, secret) {
|
|
1064
|
-
const signature = req.headers.get("x-quota-signature");
|
|
1065
|
-
if (!signature) {
|
|
1066
|
-
throw new Error("Missing X-Quota-Signature header");
|
|
1067
|
-
}
|
|
1068
|
-
const payload = await req.text();
|
|
1069
|
-
if (!verifyWebhookSignature({ payload, signature, secret })) {
|
|
1070
|
-
throw new Error("Invalid webhook signature");
|
|
1071
|
-
}
|
|
1072
|
-
return JSON.parse(payload);
|
|
1073
|
-
}
|
|
1074
|
-
function createWebhookHandler(secret, handlers) {
|
|
1075
|
-
return async (req) => {
|
|
1076
|
-
try {
|
|
1077
|
-
const event = await parseWebhook(req, secret);
|
|
1078
|
-
const handler = handlers[event.type];
|
|
1079
|
-
if (handler) {
|
|
1080
|
-
await handler(event);
|
|
1081
|
-
}
|
|
1082
|
-
return new Response(JSON.stringify({ received: true }), {
|
|
1083
|
-
status: 200,
|
|
1084
|
-
headers: { "Content-Type": "application/json" }
|
|
1085
|
-
});
|
|
1086
|
-
} catch (error) {
|
|
1087
|
-
console.error("Webhook error:", error);
|
|
1088
|
-
return new Response(
|
|
1089
|
-
JSON.stringify({
|
|
1090
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
1091
|
-
}),
|
|
1092
|
-
{ status: 400, headers: { "Content-Type": "application/json" } }
|
|
1093
|
-
);
|
|
1094
|
-
}
|
|
1095
|
-
};
|
|
1096
|
-
}
|
|
949
|
+
// src/index.ts
|
|
950
|
+
var import_core = require("@usequota/core");
|
|
951
|
+
var import_core2 = require("@usequota/core");
|
|
1097
952
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1098
953
|
0 && (module.exports = {
|
|
1099
954
|
QuotaBalance,
|
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
QuotaError,
|
|
3
|
-
QuotaInsufficientCreditsError,
|
|
4
|
-
QuotaNotConnectedError,
|
|
5
|
-
QuotaRateLimitError,
|
|
6
|
-
QuotaTokenExpiredError
|
|
7
|
-
} from "./chunk-BMI3VFWV.mjs";
|
|
8
|
-
|
|
9
1
|
// src/middleware.ts
|
|
10
2
|
import { NextResponse } from "next/server";
|
|
11
3
|
var DEFAULT_BASE_URL = "https://api.usequota.app";
|
|
@@ -73,7 +65,8 @@ function createQuotaMiddleware(config) {
|
|
|
73
65
|
headers: {
|
|
74
66
|
"Content-Type": "application/json"
|
|
75
67
|
},
|
|
76
|
-
body: JSON.stringify(tokenBody)
|
|
68
|
+
body: JSON.stringify(tokenBody),
|
|
69
|
+
signal: AbortSignal.timeout(1e4)
|
|
77
70
|
});
|
|
78
71
|
if (!tokenResponse.ok) {
|
|
79
72
|
const errorData = await tokenResponse.json();
|
|
@@ -90,16 +83,6 @@ function createQuotaMiddleware(config) {
|
|
|
90
83
|
redirectUrl.searchParams.set("quota_success", "true");
|
|
91
84
|
const response = NextResponse.redirect(redirectUrl);
|
|
92
85
|
if ("access_token" in tokenData) {
|
|
93
|
-
const cookieOptions = [
|
|
94
|
-
`Path=${cookiePath}`,
|
|
95
|
-
"HttpOnly",
|
|
96
|
-
"Secure",
|
|
97
|
-
"SameSite=Lax",
|
|
98
|
-
`Max-Age=${cookieMaxAge}`
|
|
99
|
-
];
|
|
100
|
-
if (cookieDomain) {
|
|
101
|
-
cookieOptions.push(`Domain=${cookieDomain}`);
|
|
102
|
-
}
|
|
103
86
|
response.cookies.set(
|
|
104
87
|
`${cookiePrefix}_access_token`,
|
|
105
88
|
tokenData.access_token,
|
|
@@ -929,60 +912,19 @@ function LogoutIcon() {
|
|
|
929
912
|
);
|
|
930
913
|
}
|
|
931
914
|
|
|
932
|
-
// src/
|
|
933
|
-
import
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
Buffer.from(signature, "hex"),
|
|
946
|
-
Buffer.from(expectedSignature, "hex")
|
|
947
|
-
);
|
|
948
|
-
} catch {
|
|
949
|
-
return false;
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
async function parseWebhook(req, secret) {
|
|
953
|
-
const signature = req.headers.get("x-quota-signature");
|
|
954
|
-
if (!signature) {
|
|
955
|
-
throw new Error("Missing X-Quota-Signature header");
|
|
956
|
-
}
|
|
957
|
-
const payload = await req.text();
|
|
958
|
-
if (!verifyWebhookSignature({ payload, signature, secret })) {
|
|
959
|
-
throw new Error("Invalid webhook signature");
|
|
960
|
-
}
|
|
961
|
-
return JSON.parse(payload);
|
|
962
|
-
}
|
|
963
|
-
function createWebhookHandler(secret, handlers) {
|
|
964
|
-
return async (req) => {
|
|
965
|
-
try {
|
|
966
|
-
const event = await parseWebhook(req, secret);
|
|
967
|
-
const handler = handlers[event.type];
|
|
968
|
-
if (handler) {
|
|
969
|
-
await handler(event);
|
|
970
|
-
}
|
|
971
|
-
return new Response(JSON.stringify({ received: true }), {
|
|
972
|
-
status: 200,
|
|
973
|
-
headers: { "Content-Type": "application/json" }
|
|
974
|
-
});
|
|
975
|
-
} catch (error) {
|
|
976
|
-
console.error("Webhook error:", error);
|
|
977
|
-
return new Response(
|
|
978
|
-
JSON.stringify({
|
|
979
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
980
|
-
}),
|
|
981
|
-
{ status: 400, headers: { "Content-Type": "application/json" } }
|
|
982
|
-
);
|
|
983
|
-
}
|
|
984
|
-
};
|
|
985
|
-
}
|
|
915
|
+
// src/index.ts
|
|
916
|
+
import {
|
|
917
|
+
QuotaError,
|
|
918
|
+
QuotaInsufficientCreditsError,
|
|
919
|
+
QuotaNotConnectedError,
|
|
920
|
+
QuotaTokenExpiredError,
|
|
921
|
+
QuotaRateLimitError
|
|
922
|
+
} from "@usequota/core";
|
|
923
|
+
import {
|
|
924
|
+
verifyWebhookSignature,
|
|
925
|
+
parseWebhook,
|
|
926
|
+
createWebhookHandler
|
|
927
|
+
} from "@usequota/core";
|
|
986
928
|
export {
|
|
987
929
|
QuotaBalance,
|
|
988
930
|
QuotaBuyCredits,
|
package/dist/server.d.mts
CHANGED
|
@@ -1,93 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { QuotaTokenStorage } from '@usequota/core';
|
|
2
|
+
export { InMemoryTokenStorage, OAuthTokenResponse, QuotaClient, QuotaClientConfig, QuotaError, QuotaInsufficientCreditsError, QuotaNotConnectedError, QuotaRateLimitError, QuotaTokenExpiredError, QuotaTokenStorage, VerifyWebhookOptions, WebhookEvent, createWebhookHandler, errorFromResponse, exchangeCodeForToken, parseSSEStream, parseWebhook, refreshAccessToken, verifyWebhookSignature } from '@usequota/core';
|
|
2
3
|
import { QuotaUser, CreditPackage } from '@usequota/types';
|
|
3
4
|
|
|
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
5
|
/**
|
|
92
6
|
* @usequota/nextjs - Route Handler Factory
|
|
93
7
|
*
|
|
@@ -474,4 +388,4 @@ declare function clearQuotaAuth(config?: {
|
|
|
474
388
|
cookiePrefix?: string;
|
|
475
389
|
}): Promise<void>;
|
|
476
390
|
|
|
477
|
-
export { type QuotaAuthContext, type QuotaRouteHandlerConfig, type QuotaRouteHandlers, type QuotaServerConfig, type
|
|
391
|
+
export { type QuotaAuthContext, type QuotaRouteHandlerConfig, type QuotaRouteHandlers, type QuotaServerConfig, type WithQuotaAuthConfig, clearQuotaAuth, createQuotaCheckout, createQuotaRouteHandlers, getQuotaPackages, getQuotaUser, requireQuotaAuth, withQuotaAuth };
|