@commet/next 0.3.2 → 0.3.4

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 CHANGED
@@ -13,13 +13,29 @@
13
13
 
14
14
  <br/>
15
15
 
16
+ Ready-to-use Next.js route handlers for [Commet](https://commet.co) webhooks and customer portal. Drop them into your App Router and you're set.
17
+
16
18
  ## Installation
17
19
 
18
20
  ```bash
19
- npm install @commet/next
21
+ npm install @commet/next @commet/node
20
22
  ```
21
23
 
22
- ## Webhooks
24
+ ## Getting Started
25
+
26
+ ### 1. Set up your environment
27
+
28
+ Get your API key and webhook secret from your [Commet dashboard](https://commet.co).
29
+
30
+ ```bash
31
+ # .env
32
+ COMMET_API_KEY=ck_...
33
+ COMMET_WEBHOOK_SECRET=whsec_...
34
+ ```
35
+
36
+ ### 2. Add webhook handler
37
+
38
+ Create a route handler to receive billing events from Commet:
23
39
 
24
40
  ```typescript
25
41
  // app/api/webhooks/commet/route.ts
@@ -28,12 +44,28 @@ import { Webhooks } from "@commet/next";
28
44
  export const POST = Webhooks({
29
45
  webhookSecret: process.env.COMMET_WEBHOOK_SECRET!,
30
46
  onSubscriptionActivated: async (payload) => {
31
- // Grant access
47
+ // Grant access to your product
48
+ },
49
+ onSubscriptionCanceled: async (payload) => {
50
+ // Revoke access
51
+ },
52
+ onSubscriptionCreated: async (payload) => {
53
+ // Handle new subscriptions
54
+ },
55
+ onSubscriptionUpdated: async (payload) => {
56
+ // Handle plan changes
57
+ },
58
+ onPayload: async (payload) => {
59
+ // Catch-all for any event
32
60
  },
33
61
  });
34
62
  ```
35
63
 
36
- ## Customer Portal
64
+ Then register the URL `https://yourapp.com/api/webhooks/commet` in your Commet dashboard under **Settings → Webhooks**.
65
+
66
+ ### 3. Add customer portal
67
+
68
+ Let your customers manage their billing (update payment method, view invoices, cancel) without leaving your app:
37
69
 
38
70
  ```typescript
39
71
  // app/api/commet/portal/route.ts
@@ -49,20 +81,29 @@ export const GET = CustomerPortal({
49
81
  });
50
82
  ```
51
83
 
84
+ Then link to it from your dashboard:
85
+
52
86
  ```tsx
53
- // Use in component
54
87
  <Button asChild>
55
88
  <Link href="/api/commet/portal">Manage Billing</Link>
56
89
  </Button>
57
90
  ```
58
91
 
92
+ ## Note About Webhooks
93
+
94
+ Webhooks are optional in Commet. You can always query the current state directly using [`@commet/node`](https://www.npmjs.com/package/@commet/node):
95
+
96
+ ```typescript
97
+ const subscription = await commet.subscriptions.get({ externalId: userId });
98
+ const features = await commet.features.list({ externalId: userId });
99
+ ```
100
+
101
+ Webhooks are useful when you want to react immediately to changes — send emails, update your database, revoke access, etc.
102
+
59
103
  ## Documentation
60
104
 
61
- - [Webhooks](https://commet.co/docs)
62
- - [Customer Portal](https://commet.co/docs)
63
- - [SDK Reference](https://commet.co/docs)
105
+ Visit [commet.co/docs](https://commet.co/docs) for the full guide on webhooks, portal customization, and more.
64
106
 
65
107
  ## License
66
108
 
67
109
  MIT
68
-
package/dist/index.d.mts CHANGED
@@ -1,7 +1,56 @@
1
1
  import { NextRequest, NextResponse } from 'next/server';
2
- import { WebhookPayload, BillingInterval } from '@commet/node';
2
+ import { BillingInterval, WebhookPayload } from '@commet/node';
3
3
  export { WebhookData, WebhookEvent, WebhookPayload } from '@commet/node';
4
4
 
5
+ interface CustomerPortalConfig {
6
+ apiKey: string;
7
+ getCustomerId: (req: NextRequest) => Promise<string | null>;
8
+ environment?: "sandbox" | "production";
9
+ onError?: (error: Error) => void;
10
+ }
11
+ /**
12
+ * Creates Next.js route handler for Customer Portal access
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * // app/api/commet/portal/route.ts
17
+ * import { CustomerPortal } from "@commet/next";
18
+ * import { auth } from "@/lib/auth";
19
+ *
20
+ * export const GET = CustomerPortal({
21
+ * apiKey: process.env.COMMET_API_KEY!,
22
+ * getCustomerId: async (req) => {
23
+ * const session = await auth.api.getSession({ headers: req.headers });
24
+ * return session?.user.id ?? null;
25
+ * },
26
+ * });
27
+ * ```
28
+ *
29
+ * @example
30
+ * ```tsx
31
+ * // Use in component
32
+ * export function BillingButton() {
33
+ * return (
34
+ * <Button asChild>
35
+ * <Link href="/api/commet/portal">Manage Billing</Link>
36
+ * </Button>
37
+ * );
38
+ * }
39
+ * ```
40
+ */
41
+ declare const CustomerPortal: ({ apiKey, environment, getCustomerId, onError, }: CustomerPortalConfig) => (req: NextRequest) => Promise<NextResponse<unknown>>;
42
+
43
+ interface PricingMarkdownConfig {
44
+ apiKey: string;
45
+ title: string;
46
+ description?: string;
47
+ includeCreditPacks?: boolean;
48
+ billingIntervals?: BillingInterval[];
49
+ cacheMaxAge?: number;
50
+ onError?: (error: Error) => void;
51
+ }
52
+ declare const PricingMarkdown: ({ apiKey, title, description, includeCreditPacks, billingIntervals, cacheMaxAge, onError, }: PricingMarkdownConfig) => (_req: NextRequest) => Promise<NextResponse<unknown>>;
53
+
5
54
  /**
6
55
  * Configuration for the Commet webhook handler
7
56
  */
@@ -146,53 +195,4 @@ declare const Webhooks: (config: WebhooksConfig) => (request: NextRequest) => Pr
146
195
  received: boolean;
147
196
  }>>;
148
197
 
149
- interface CustomerPortalConfig {
150
- apiKey: string;
151
- getCustomerId: (req: NextRequest) => Promise<string | null>;
152
- environment?: "sandbox" | "production";
153
- onError?: (error: Error) => void;
154
- }
155
- /**
156
- * Creates Next.js route handler for Customer Portal access
157
- *
158
- * @example
159
- * ```typescript
160
- * // app/api/commet/portal/route.ts
161
- * import { CustomerPortal } from "@commet/next";
162
- * import { auth } from "@/lib/auth";
163
- *
164
- * export const GET = CustomerPortal({
165
- * apiKey: process.env.COMMET_API_KEY!,
166
- * getCustomerId: async (req) => {
167
- * const session = await auth.api.getSession({ headers: req.headers });
168
- * return session?.user.id ?? null;
169
- * },
170
- * });
171
- * ```
172
- *
173
- * @example
174
- * ```tsx
175
- * // Use in component
176
- * export function BillingButton() {
177
- * return (
178
- * <Button asChild>
179
- * <Link href="/api/commet/portal">Manage Billing</Link>
180
- * </Button>
181
- * );
182
- * }
183
- * ```
184
- */
185
- declare const CustomerPortal: ({ apiKey, environment, getCustomerId, onError, }: CustomerPortalConfig) => (req: NextRequest) => Promise<NextResponse<unknown>>;
186
-
187
- interface PricingMarkdownConfig {
188
- apiKey: string;
189
- title: string;
190
- description?: string;
191
- includeCreditPacks?: boolean;
192
- billingIntervals?: BillingInterval[];
193
- cacheMaxAge?: number;
194
- onError?: (error: Error) => void;
195
- }
196
- declare const PricingMarkdown: ({ apiKey, title, description, includeCreditPacks, billingIntervals, cacheMaxAge, onError, }: PricingMarkdownConfig) => (_req: NextRequest) => Promise<NextResponse<unknown>>;
197
-
198
198
  export { CustomerPortal, type CustomerPortalConfig, PricingMarkdown, type PricingMarkdownConfig, Webhooks, type WebhooksConfig };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,56 @@
1
1
  import { NextRequest, NextResponse } from 'next/server';
2
- import { WebhookPayload, BillingInterval } from '@commet/node';
2
+ import { BillingInterval, WebhookPayload } from '@commet/node';
3
3
  export { WebhookData, WebhookEvent, WebhookPayload } from '@commet/node';
4
4
 
5
+ interface CustomerPortalConfig {
6
+ apiKey: string;
7
+ getCustomerId: (req: NextRequest) => Promise<string | null>;
8
+ environment?: "sandbox" | "production";
9
+ onError?: (error: Error) => void;
10
+ }
11
+ /**
12
+ * Creates Next.js route handler for Customer Portal access
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * // app/api/commet/portal/route.ts
17
+ * import { CustomerPortal } from "@commet/next";
18
+ * import { auth } from "@/lib/auth";
19
+ *
20
+ * export const GET = CustomerPortal({
21
+ * apiKey: process.env.COMMET_API_KEY!,
22
+ * getCustomerId: async (req) => {
23
+ * const session = await auth.api.getSession({ headers: req.headers });
24
+ * return session?.user.id ?? null;
25
+ * },
26
+ * });
27
+ * ```
28
+ *
29
+ * @example
30
+ * ```tsx
31
+ * // Use in component
32
+ * export function BillingButton() {
33
+ * return (
34
+ * <Button asChild>
35
+ * <Link href="/api/commet/portal">Manage Billing</Link>
36
+ * </Button>
37
+ * );
38
+ * }
39
+ * ```
40
+ */
41
+ declare const CustomerPortal: ({ apiKey, environment, getCustomerId, onError, }: CustomerPortalConfig) => (req: NextRequest) => Promise<NextResponse<unknown>>;
42
+
43
+ interface PricingMarkdownConfig {
44
+ apiKey: string;
45
+ title: string;
46
+ description?: string;
47
+ includeCreditPacks?: boolean;
48
+ billingIntervals?: BillingInterval[];
49
+ cacheMaxAge?: number;
50
+ onError?: (error: Error) => void;
51
+ }
52
+ declare const PricingMarkdown: ({ apiKey, title, description, includeCreditPacks, billingIntervals, cacheMaxAge, onError, }: PricingMarkdownConfig) => (_req: NextRequest) => Promise<NextResponse<unknown>>;
53
+
5
54
  /**
6
55
  * Configuration for the Commet webhook handler
7
56
  */
@@ -146,53 +195,4 @@ declare const Webhooks: (config: WebhooksConfig) => (request: NextRequest) => Pr
146
195
  received: boolean;
147
196
  }>>;
148
197
 
149
- interface CustomerPortalConfig {
150
- apiKey: string;
151
- getCustomerId: (req: NextRequest) => Promise<string | null>;
152
- environment?: "sandbox" | "production";
153
- onError?: (error: Error) => void;
154
- }
155
- /**
156
- * Creates Next.js route handler for Customer Portal access
157
- *
158
- * @example
159
- * ```typescript
160
- * // app/api/commet/portal/route.ts
161
- * import { CustomerPortal } from "@commet/next";
162
- * import { auth } from "@/lib/auth";
163
- *
164
- * export const GET = CustomerPortal({
165
- * apiKey: process.env.COMMET_API_KEY!,
166
- * getCustomerId: async (req) => {
167
- * const session = await auth.api.getSession({ headers: req.headers });
168
- * return session?.user.id ?? null;
169
- * },
170
- * });
171
- * ```
172
- *
173
- * @example
174
- * ```tsx
175
- * // Use in component
176
- * export function BillingButton() {
177
- * return (
178
- * <Button asChild>
179
- * <Link href="/api/commet/portal">Manage Billing</Link>
180
- * </Button>
181
- * );
182
- * }
183
- * ```
184
- */
185
- declare const CustomerPortal: ({ apiKey, environment, getCustomerId, onError, }: CustomerPortalConfig) => (req: NextRequest) => Promise<NextResponse<unknown>>;
186
-
187
- interface PricingMarkdownConfig {
188
- apiKey: string;
189
- title: string;
190
- description?: string;
191
- includeCreditPacks?: boolean;
192
- billingIntervals?: BillingInterval[];
193
- cacheMaxAge?: number;
194
- onError?: (error: Error) => void;
195
- }
196
- declare const PricingMarkdown: ({ apiKey, title, description, includeCreditPacks, billingIntervals, cacheMaxAge, onError, }: PricingMarkdownConfig) => (_req: NextRequest) => Promise<NextResponse<unknown>>;
197
-
198
198
  export { CustomerPortal, type CustomerPortalConfig, PricingMarkdown, type PricingMarkdownConfig, Webhooks, type WebhooksConfig };
package/dist/index.js CHANGED
@@ -26,132 +26,16 @@ __export(index_exports, {
26
26
  });
27
27
  module.exports = __toCommonJS(index_exports);
28
28
 
29
- // src/webhooks.ts
29
+ // src/portal.ts
30
30
  var import_node = require("@commet/node");
31
31
  var import_server = require("next/server");
32
- var Webhooks = (config) => {
33
- const {
34
- webhookSecret,
35
- onSubscriptionActivated,
36
- onSubscriptionCanceled,
37
- onSubscriptionCreated,
38
- onSubscriptionUpdated,
39
- onPayload,
40
- onError
41
- } = config;
42
- const webhooks = new import_node.Webhooks();
43
- return async (request) => {
44
- try {
45
- const rawBody = await request.text();
46
- const signature = request.headers.get("x-commet-signature");
47
- const isValid = webhooks.verify({
48
- payload: rawBody,
49
- signature,
50
- secret: webhookSecret
51
- });
52
- if (!isValid) {
53
- console.error("[Commet Webhook] Invalid signature");
54
- return import_server.NextResponse.json(
55
- { received: false, error: "Invalid signature" },
56
- { status: 403 }
57
- );
58
- }
59
- let payload;
60
- try {
61
- payload = JSON.parse(rawBody);
62
- } catch (parseError) {
63
- console.error("[Commet Webhook] Failed to parse payload:", parseError);
64
- if (onError) {
65
- await onError(
66
- parseError instanceof Error ? parseError : new Error("Failed to parse webhook payload"),
67
- rawBody
68
- );
69
- }
70
- return import_server.NextResponse.json(
71
- { received: false, error: "Invalid payload" },
72
- { status: 400 }
73
- );
74
- }
75
- const promises = [];
76
- if (onPayload) {
77
- promises.push(onPayload(payload));
78
- }
79
- switch (payload.event) {
80
- case "subscription.activated":
81
- if (onSubscriptionActivated) {
82
- promises.push(onSubscriptionActivated(payload));
83
- }
84
- break;
85
- case "subscription.canceled":
86
- if (onSubscriptionCanceled) {
87
- promises.push(onSubscriptionCanceled(payload));
88
- }
89
- break;
90
- case "subscription.created":
91
- if (onSubscriptionCreated) {
92
- promises.push(onSubscriptionCreated(payload));
93
- }
94
- break;
95
- case "subscription.updated":
96
- if (onSubscriptionUpdated) {
97
- promises.push(onSubscriptionUpdated(payload));
98
- }
99
- break;
100
- default:
101
- console.log(`[Commet Webhook] Unhandled event: ${payload.event}`);
102
- }
103
- try {
104
- await Promise.all(promises);
105
- } catch (handlerError) {
106
- console.error(
107
- "[Commet Webhook] Handler error:",
108
- handlerError instanceof Error ? handlerError.message : handlerError
109
- );
110
- if (onError) {
111
- await onError(
112
- handlerError instanceof Error ? handlerError : new Error("Handler execution failed"),
113
- payload
114
- );
115
- }
116
- return import_server.NextResponse.json(
117
- { received: true, warning: "Handler failed" },
118
- { status: 200 }
119
- );
120
- }
121
- return import_server.NextResponse.json({ received: true }, { status: 200 });
122
- } catch (error) {
123
- console.error("[Commet Webhook] Unexpected error:", error);
124
- if (onError) {
125
- try {
126
- await onError(
127
- error instanceof Error ? error : new Error("Unexpected error"),
128
- void 0
129
- );
130
- } catch (errorHandlerError) {
131
- console.error(
132
- "[Commet Webhook] Error handler failed:",
133
- errorHandlerError
134
- );
135
- }
136
- }
137
- return import_server.NextResponse.json(
138
- { received: false, error: "Internal error" },
139
- { status: 500 }
140
- );
141
- }
142
- };
143
- };
144
-
145
- // src/portal.ts
146
- var import_node2 = require("@commet/node");
147
- var import_server2 = require("next/server");
148
32
  var CustomerPortal = ({
149
33
  apiKey,
150
34
  environment = "production",
151
35
  getCustomerId,
152
36
  onError
153
37
  }) => {
154
- const commet = new import_node2.Commet({
38
+ const commet = new import_node.Commet({
155
39
  apiKey,
156
40
  environment
157
41
  });
@@ -159,23 +43,23 @@ var CustomerPortal = ({
159
43
  try {
160
44
  const customerId = await getCustomerId(req);
161
45
  if (!customerId) {
162
- return import_server2.NextResponse.json(
46
+ return import_server.NextResponse.json(
163
47
  { error: "Customer not authenticated" },
164
48
  { status: 401 }
165
49
  );
166
50
  }
167
51
  const result = await commet.portal.getUrl({
168
- externalId: customerId
52
+ customerId
169
53
  });
170
54
  if (!result.success || !result.data) {
171
55
  throw new Error(result.message || "Failed to create portal session");
172
56
  }
173
- return import_server2.NextResponse.redirect(result.data.portalUrl);
57
+ return import_server.NextResponse.redirect(result.data.portalUrl);
174
58
  } catch (error) {
175
59
  const errorObj = error instanceof Error ? error : new Error(String(error));
176
60
  onError?.(errorObj);
177
61
  console.error("[Commet Portal]", errorObj);
178
- return import_server2.NextResponse.json(
62
+ return import_server.NextResponse.json(
179
63
  { error: "Failed to access customer portal" },
180
64
  { status: 500 }
181
65
  );
@@ -184,8 +68,8 @@ var CustomerPortal = ({
184
68
  };
185
69
 
186
70
  // src/pricing-markdown.ts
187
- var import_node3 = require("@commet/node");
188
- var import_server3 = require("next/server");
71
+ var import_node2 = require("@commet/node");
72
+ var import_server2 = require("next/server");
189
73
  var INTERVAL_SHORT = {
190
74
  monthly: "/mo",
191
75
  quarterly: "/qtr",
@@ -200,7 +84,7 @@ var PricingMarkdown = ({
200
84
  cacheMaxAge = 3600,
201
85
  onError
202
86
  }) => {
203
- const commet = new import_node3.Commet({ apiKey });
87
+ const commet = new import_node2.Commet({ apiKey });
204
88
  return async (_req) => {
205
89
  try {
206
90
  const plansResult = await commet.plans.list();
@@ -218,7 +102,7 @@ var PricingMarkdown = ({
218
102
  creditPacks,
219
103
  billingIntervals
220
104
  );
221
- return new import_server3.NextResponse(markdown, {
105
+ return new import_server2.NextResponse(markdown, {
222
106
  status: 200,
223
107
  headers: {
224
108
  "Content-Type": "text/markdown; charset=utf-8",
@@ -229,7 +113,7 @@ var PricingMarkdown = ({
229
113
  const errorObj = error instanceof Error ? error : new Error(String(error));
230
114
  onError?.(errorObj);
231
115
  console.error("[Commet PricingMarkdown]", errorObj);
232
- return import_server3.NextResponse.json(
116
+ return import_server2.NextResponse.json(
233
117
  { error: "Failed to generate pricing markdown" },
234
118
  { status: 500 }
235
119
  );
@@ -417,6 +301,122 @@ function formatRatePrice(rate) {
417
301
  function formatNumber(n) {
418
302
  return n.toLocaleString("en-US");
419
303
  }
304
+
305
+ // src/webhooks.ts
306
+ var import_node3 = require("@commet/node");
307
+ var import_server3 = require("next/server");
308
+ var Webhooks = (config) => {
309
+ const {
310
+ webhookSecret,
311
+ onSubscriptionActivated,
312
+ onSubscriptionCanceled,
313
+ onSubscriptionCreated,
314
+ onSubscriptionUpdated,
315
+ onPayload,
316
+ onError
317
+ } = config;
318
+ const webhooks = new import_node3.Webhooks();
319
+ return async (request) => {
320
+ try {
321
+ const rawBody = await request.text();
322
+ const signature = request.headers.get("x-commet-signature");
323
+ const isValid = webhooks.verify({
324
+ payload: rawBody,
325
+ signature,
326
+ secret: webhookSecret
327
+ });
328
+ if (!isValid) {
329
+ console.error("[Commet Webhook] Invalid signature");
330
+ return import_server3.NextResponse.json(
331
+ { received: false, error: "Invalid signature" },
332
+ { status: 403 }
333
+ );
334
+ }
335
+ let payload;
336
+ try {
337
+ payload = JSON.parse(rawBody);
338
+ } catch (parseError) {
339
+ console.error("[Commet Webhook] Failed to parse payload:", parseError);
340
+ if (onError) {
341
+ await onError(
342
+ parseError instanceof Error ? parseError : new Error("Failed to parse webhook payload"),
343
+ rawBody
344
+ );
345
+ }
346
+ return import_server3.NextResponse.json(
347
+ { received: false, error: "Invalid payload" },
348
+ { status: 400 }
349
+ );
350
+ }
351
+ const promises = [];
352
+ if (onPayload) {
353
+ promises.push(onPayload(payload));
354
+ }
355
+ switch (payload.event) {
356
+ case "subscription.activated":
357
+ if (onSubscriptionActivated) {
358
+ promises.push(onSubscriptionActivated(payload));
359
+ }
360
+ break;
361
+ case "subscription.canceled":
362
+ if (onSubscriptionCanceled) {
363
+ promises.push(onSubscriptionCanceled(payload));
364
+ }
365
+ break;
366
+ case "subscription.created":
367
+ if (onSubscriptionCreated) {
368
+ promises.push(onSubscriptionCreated(payload));
369
+ }
370
+ break;
371
+ case "subscription.updated":
372
+ if (onSubscriptionUpdated) {
373
+ promises.push(onSubscriptionUpdated(payload));
374
+ }
375
+ break;
376
+ default:
377
+ console.log(`[Commet Webhook] Unhandled event: ${payload.event}`);
378
+ }
379
+ try {
380
+ await Promise.all(promises);
381
+ } catch (handlerError) {
382
+ console.error(
383
+ "[Commet Webhook] Handler error:",
384
+ handlerError instanceof Error ? handlerError.message : handlerError
385
+ );
386
+ if (onError) {
387
+ await onError(
388
+ handlerError instanceof Error ? handlerError : new Error("Handler execution failed"),
389
+ payload
390
+ );
391
+ }
392
+ return import_server3.NextResponse.json(
393
+ { received: true, warning: "Handler failed" },
394
+ { status: 200 }
395
+ );
396
+ }
397
+ return import_server3.NextResponse.json({ received: true }, { status: 200 });
398
+ } catch (error) {
399
+ console.error("[Commet Webhook] Unexpected error:", error);
400
+ if (onError) {
401
+ try {
402
+ await onError(
403
+ error instanceof Error ? error : new Error("Unexpected error"),
404
+ void 0
405
+ );
406
+ } catch (errorHandlerError) {
407
+ console.error(
408
+ "[Commet Webhook] Error handler failed:",
409
+ errorHandlerError
410
+ );
411
+ }
412
+ }
413
+ return import_server3.NextResponse.json(
414
+ { received: false, error: "Internal error" },
415
+ { status: 500 }
416
+ );
417
+ }
418
+ };
419
+ };
420
420
  // Annotate the CommonJS export names for ESM import in node:
421
421
  0 && (module.exports = {
422
422
  CustomerPortal,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/webhooks.ts","../src/portal.ts","../src/pricing-markdown.ts"],"sourcesContent":["/**\n * @commet/next - Next.js integration for Commet\n *\n * @example\n * Webhooks\n * ```typescript\n * import { Webhooks } from \"@commet/next\";\n *\n * export const POST = Webhooks({\n * webhookSecret: process.env.COMMET_WEBHOOK_SECRET!,\n * onSubscriptionActivated: async (payload) => {\n * // Handle activation\n * },\n * });\n * ```\n *\n * @example\n * Customer Portal\n * ```typescript\n * import { CustomerPortal } from \"@commet/next\";\n *\n * export const GET = CustomerPortal({\n * apiKey: process.env.COMMET_API_KEY!,\n * getCustomerId: async (req) => {\n * const session = await auth.api.getSession({ headers: req.headers });\n * return session?.user.id;\n * },\n * });\n * ```\n *\n * @packageDocumentation\n */\n\nexport { Webhooks } from \"./webhooks\";\nexport type {\n WebhooksConfig,\n WebhookPayload,\n WebhookData,\n WebhookEvent,\n} from \"./types\";\n\nexport { CustomerPortal } from \"./portal\";\nexport type { CustomerPortalConfig } from \"./portal\";\n\nexport { PricingMarkdown } from \"./pricing-markdown\";\nexport type { PricingMarkdownConfig } from \"./pricing-markdown\";\n","import { Webhooks as CommetWebhooks } from \"@commet/node\";\nimport type { WebhookPayload } from \"@commet/node\";\nimport { type NextRequest, NextResponse } from \"next/server\";\nimport type { WebhooksConfig } from \"./types\";\n\n/**\n * Create a Next.js webhook handler for Commet events\n *\n * Automatically verifies signatures, routes events to handlers, and returns proper responses.\n *\n * @param config - Webhook configuration with secret and event handlers\n * @returns Next.js route handler function\n *\n * @example\n * ```typescript\n * // app/api/webhooks/commet/route.ts\n * import { Webhooks } from \"@commet/next\";\n *\n * export const POST = Webhooks({\n * webhookSecret: process.env.COMMET_WEBHOOK_SECRET!,\n * onSubscriptionActivated: async (payload) => {\n * // Grant access to user\n * },\n * onSubscriptionCanceled: async (payload) => {\n * // Revoke access from user\n * },\n * });\n * ```\n */\nexport const Webhooks = (config: WebhooksConfig) => {\n const {\n webhookSecret,\n onSubscriptionActivated,\n onSubscriptionCanceled,\n onSubscriptionCreated,\n onSubscriptionUpdated,\n onPayload,\n onError,\n } = config;\n\n // Create webhook verifier instance\n const webhooks = new CommetWebhooks();\n\n return async (request: NextRequest) => {\n try {\n // 1. Read raw request body\n const rawBody = await request.text();\n\n // 2. Extract signature from headers\n const signature = request.headers.get(\"x-commet-signature\");\n\n // 3. Verify signature\n const isValid = webhooks.verify({\n payload: rawBody,\n signature,\n secret: webhookSecret,\n });\n\n if (!isValid) {\n console.error(\"[Commet Webhook] Invalid signature\");\n return NextResponse.json(\n { received: false, error: \"Invalid signature\" },\n { status: 403 },\n );\n }\n\n // 4. Parse payload\n let payload: WebhookPayload;\n try {\n payload = JSON.parse(rawBody) as WebhookPayload;\n } catch (parseError) {\n console.error(\"[Commet Webhook] Failed to parse payload:\", parseError);\n if (onError) {\n await onError(\n parseError instanceof Error\n ? parseError\n : new Error(\"Failed to parse webhook payload\"),\n rawBody,\n );\n }\n return NextResponse.json(\n { received: false, error: \"Invalid payload\" },\n { status: 400 },\n );\n }\n\n // 5. Collect promises for parallel execution\n const promises: Promise<void>[] = [];\n\n // Call catch-all handler if provided\n if (onPayload) {\n promises.push(onPayload(payload));\n }\n\n // 6. Route to specific event handler\n switch (payload.event) {\n case \"subscription.activated\":\n if (onSubscriptionActivated) {\n promises.push(onSubscriptionActivated(payload));\n }\n break;\n\n case \"subscription.canceled\":\n if (onSubscriptionCanceled) {\n promises.push(onSubscriptionCanceled(payload));\n }\n break;\n\n case \"subscription.created\":\n if (onSubscriptionCreated) {\n promises.push(onSubscriptionCreated(payload));\n }\n break;\n\n case \"subscription.updated\":\n if (onSubscriptionUpdated) {\n promises.push(onSubscriptionUpdated(payload));\n }\n break;\n\n default:\n console.log(`[Commet Webhook] Unhandled event: ${payload.event}`);\n }\n\n // 7. Execute all handlers in parallel\n try {\n await Promise.all(promises);\n } catch (handlerError) {\n console.error(\n \"[Commet Webhook] Handler error:\",\n handlerError instanceof Error ? handlerError.message : handlerError,\n );\n if (onError) {\n await onError(\n handlerError instanceof Error\n ? handlerError\n : new Error(\"Handler execution failed\"),\n payload,\n );\n }\n // Still return 200 to prevent retries for handler errors\n return NextResponse.json(\n { received: true, warning: \"Handler failed\" },\n { status: 200 },\n );\n }\n\n // 8. Success response\n return NextResponse.json({ received: true }, { status: 200 });\n } catch (error) {\n console.error(\"[Commet Webhook] Unexpected error:\", error);\n if (onError) {\n try {\n await onError(\n error instanceof Error ? error : new Error(\"Unexpected error\"),\n undefined,\n );\n } catch (errorHandlerError) {\n console.error(\n \"[Commet Webhook] Error handler failed:\",\n errorHandlerError,\n );\n }\n }\n return NextResponse.json(\n { received: false, error: \"Internal error\" },\n { status: 500 },\n );\n }\n };\n};\n","import { Commet } from \"@commet/node\";\nimport { type NextRequest, NextResponse } from \"next/server\";\n\nexport interface CustomerPortalConfig {\n apiKey: string;\n getCustomerId: (req: NextRequest) => Promise<string | null>;\n environment?: \"sandbox\" | \"production\";\n onError?: (error: Error) => void;\n}\n\n/**\n * Creates Next.js route handler for Customer Portal access\n *\n * @example\n * ```typescript\n * // app/api/commet/portal/route.ts\n * import { CustomerPortal } from \"@commet/next\";\n * import { auth } from \"@/lib/auth\";\n *\n * export const GET = CustomerPortal({\n * apiKey: process.env.COMMET_API_KEY!,\n * getCustomerId: async (req) => {\n * const session = await auth.api.getSession({ headers: req.headers });\n * return session?.user.id ?? null;\n * },\n * });\n * ```\n *\n * @example\n * ```tsx\n * // Use in component\n * export function BillingButton() {\n * return (\n * <Button asChild>\n * <Link href=\"/api/commet/portal\">Manage Billing</Link>\n * </Button>\n * );\n * }\n * ```\n */\nexport const CustomerPortal = ({\n apiKey,\n environment = \"production\",\n getCustomerId,\n onError,\n}: CustomerPortalConfig) => {\n const commet = new Commet({\n apiKey,\n environment,\n });\n\n return async (req: NextRequest) => {\n try {\n // Get customer identifier from request\n const customerId = await getCustomerId(req);\n\n if (!customerId) {\n return NextResponse.json(\n { error: \"Customer not authenticated\" },\n { status: 401 },\n );\n }\n\n // Get portal URL from Commet\n const result = await commet.portal.getUrl({\n externalId: customerId,\n });\n\n if (!result.success || !result.data) {\n throw new Error(result.message || \"Failed to create portal session\");\n }\n\n // Redirect to customer portal\n return NextResponse.redirect(result.data.portalUrl);\n } catch (error) {\n const errorObj =\n error instanceof Error ? error : new Error(String(error));\n\n // Call custom error handler if provided\n onError?.(errorObj);\n\n // Log error\n console.error(\"[Commet Portal]\", errorObj);\n\n // Return error response\n return NextResponse.json(\n { error: \"Failed to access customer portal\" },\n { status: 500 },\n );\n }\n };\n};\n","import { Commet } from \"@commet/node\";\nimport type {\n BillingInterval,\n CreditPack,\n Plan,\n PlanFeature,\n} from \"@commet/node\";\nimport { type NextRequest, NextResponse } from \"next/server\";\n\nexport interface PricingMarkdownConfig {\n apiKey: string;\n title: string;\n description?: string;\n includeCreditPacks?: boolean;\n billingIntervals?: BillingInterval[];\n cacheMaxAge?: number;\n onError?: (error: Error) => void;\n}\n\nconst INTERVAL_SHORT: Record<BillingInterval, string> = {\n monthly: \"/mo\",\n quarterly: \"/qtr\",\n yearly: \"/yr\",\n};\n\nexport const PricingMarkdown = ({\n apiKey,\n title,\n description,\n includeCreditPacks = false,\n billingIntervals,\n cacheMaxAge = 3600,\n onError,\n}: PricingMarkdownConfig) => {\n const commet = new Commet({ apiKey });\n\n return async (_req: NextRequest) => {\n try {\n const plansResult = await commet.plans.list();\n\n if (!plansResult.success || !plansResult.data) {\n throw new Error(plansResult.message ?? \"Failed to fetch plans\");\n }\n\n const publicPlans = plansResult.data.filter((plan) => plan.isPublic);\n\n const creditPacks = includeCreditPacks\n ? await commet.creditPacks\n .list()\n .then((result) =>\n result.success && result.data ? result.data : [],\n )\n : [];\n\n const markdown = generateMarkdown(\n title,\n description,\n publicPlans,\n creditPacks,\n billingIntervals,\n );\n\n return new NextResponse(markdown, {\n status: 200,\n headers: {\n \"Content-Type\": \"text/markdown; charset=utf-8\",\n \"Cache-Control\": `public, s-maxage=${cacheMaxAge}, stale-while-revalidate=${cacheMaxAge * 2}`,\n },\n });\n } catch (error) {\n const errorObj =\n error instanceof Error ? error : new Error(String(error));\n\n onError?.(errorObj);\n console.error(\"[Commet PricingMarkdown]\", errorObj);\n\n return NextResponse.json(\n { error: \"Failed to generate pricing markdown\" },\n { status: 500 },\n );\n }\n };\n};\n\nfunction generateMarkdown(\n title: string,\n description: string | undefined,\n plans: Plan[],\n creditPacks: CreditPack[],\n billingIntervals?: BillingInterval[],\n): string {\n const sections: string[] = [`# ${title}`];\n\n if (description) {\n sections.push(description);\n }\n\n if (plans.length > 0) {\n sections.push(renderPlansTable(plans, billingIntervals));\n\n const featuresSection = renderFeatures(plans);\n if (featuresSection) {\n sections.push(featuresSection);\n }\n }\n\n if (creditPacks.length > 0) {\n sections.push(renderCreditPacks(creditPacks));\n }\n\n return sections.join(\"\\n\\n\");\n}\n\nfunction renderPlansTable(\n plans: Plan[],\n filterIntervals?: BillingInterval[],\n): string {\n const usageFeatures = collectUsageFeatures(plans);\n\n const headerCols = [\"Plan\", \"Price\"];\n for (const { name, unitName } of usageFeatures) {\n headerCols.push(`${name}/${unitName ?? \"unit\"}`);\n headerCols.push(\"Overage\");\n }\n\n const rows = plans.map((plan) => {\n const priceCell = renderPriceCell(plan, filterIntervals);\n const cols = [plan.name, priceCell];\n\n const featuresByCode = new Map(\n plan.features.map((feature) => [feature.code, feature]),\n );\n\n for (const { code } of usageFeatures) {\n const feature = featuresByCode.get(code);\n\n if (!feature) {\n cols.push(\"—\", \"—\");\n continue;\n }\n\n if (feature.unlimited) {\n cols.push(\"Unlimited\", \"—\");\n continue;\n }\n\n const included = feature.includedAmount;\n cols.push(included ? formatNumber(included) : \"—\");\n\n const overagePrice =\n feature.overageEnabled && feature.overageUnitPrice !== undefined\n ? formatRatePrice(feature.overageUnitPrice)\n : \"—\";\n cols.push(overagePrice);\n }\n\n return cols;\n });\n\n const separator = headerCols.map(() => \"---\");\n const lines = [\n `| ${headerCols.join(\" | \")} |`,\n `| ${separator.join(\" | \")} |`,\n ...rows.map((cols) => `| ${cols.join(\" | \")} |`),\n ];\n\n const footnotes: string[] = [];\n\n const trialPlans = plans.filter((plan) =>\n plan.prices.some((price) => price.trialDays > 0),\n );\n for (const plan of trialPlans) {\n const trialPrice = plan.prices.find((price) => price.trialDays > 0)!;\n footnotes.push(\n `*${plan.name} plan includes a ${trialPrice.trialDays}-day free trial.*`,\n );\n }\n\n if (usageFeatures.length > 0) {\n footnotes.push(\n \"The overage rate applies only to usage beyond the included volume.\",\n );\n }\n\n return footnotes.length > 0\n ? `## Plans\\n\\n${lines.join(\"\\n\")}\\n\\n${footnotes.join(\"\\n\\n\")}`\n : `## Plans\\n\\n${lines.join(\"\\n\")}`;\n}\n\nfunction renderPriceCell(\n plan: Plan,\n filterIntervals?: BillingInterval[],\n): string {\n if (plan.isFree) return \"Free\";\n\n const prices = filterIntervals\n ? plan.prices.filter((price) =>\n filterIntervals.includes(price.billingInterval),\n )\n : plan.prices;\n\n if (prices.length === 0) return \"—\";\n\n return prices\n .map((price) => {\n if (price.price === 0) return \"Free\";\n return `${formatSettlementPrice(price.price)}${INTERVAL_SHORT[price.billingInterval]}`;\n })\n .join(\" / \");\n}\n\nfunction renderFeatures(plans: Plan[]): string | null {\n const allBooleanCodes = new Set<string>();\n for (const plan of plans) {\n for (const feature of plan.features) {\n if (feature.type === \"boolean\") {\n allBooleanCodes.add(feature.code);\n }\n }\n }\n\n if (allBooleanCodes.size === 0) return null;\n\n const enabledOnAll = new Set<string>();\n for (const code of allBooleanCodes) {\n const enabledOnEveryPlan = plans.every((plan) => {\n const feature = plan.features.find((f) => f.code === code);\n return feature?.enabled;\n });\n if (enabledOnEveryPlan) {\n enabledOnAll.add(code);\n }\n }\n\n const perPlanLines: string[] = [];\n for (const plan of plans) {\n const uniqueFeatures = plan.features\n .filter(\n (feature) =>\n feature.type === \"boolean\" &&\n feature.enabled &&\n !enabledOnAll.has(feature.code),\n )\n .map((feature) => feature.name);\n\n if (uniqueFeatures.length > 0) {\n perPlanLines.push(`- **${plan.name}**: ${uniqueFeatures.join(\", \")}`);\n }\n }\n\n const commonFeatures = plans[0]?.features\n .filter(\n (feature) => feature.type === \"boolean\" && enabledOnAll.has(feature.code),\n )\n .map((feature) => feature.name);\n\n const parts: string[] = [];\n\n if (perPlanLines.length > 0) {\n parts.push(perPlanLines.join(\"\\n\"));\n }\n\n if (commonFeatures && commonFeatures.length > 0) {\n parts.push(`All plans include: ${commonFeatures.join(\", \")}.`);\n }\n\n return parts.length > 0 ? `## Features\\n\\n${parts.join(\"\\n\\n\")}` : null;\n}\n\nfunction renderCreditPacks(packs: CreditPack[]): string {\n const lines = [\n \"| Pack | Credits | Price |\",\n \"| --- | --- | --- |\",\n ...packs.map(\n (pack) =>\n `| ${pack.name} | ${formatNumber(pack.credits)} credits | ${formatSettlementPrice(pack.price)} |`,\n ),\n ];\n\n return `## Credit Packs\\n\\n${lines.join(\"\\n\")}`;\n}\n\ninterface UsageFeatureColumn {\n code: string;\n name: string;\n unitName: string | null;\n}\n\nfunction collectUsageFeatures(plans: Plan[]): UsageFeatureColumn[] {\n const seen = new Map<string, UsageFeatureColumn>();\n for (const plan of plans) {\n for (const feature of plan.features) {\n if (\n (feature.type === \"metered\" || feature.type === \"seats\") &&\n !seen.has(feature.code) &&\n hasUsageData(feature)\n ) {\n seen.set(feature.code, {\n code: feature.code,\n name: feature.name,\n unitName: feature.unitName,\n });\n }\n }\n }\n return Array.from(seen.values());\n}\n\nfunction hasUsageData(feature: PlanFeature): boolean {\n return (\n feature.unlimited === true ||\n (feature.includedAmount !== undefined && feature.includedAmount > 0) ||\n (feature.overageEnabled === true && feature.overageUnitPrice !== undefined)\n );\n}\n\nfunction formatSettlementPrice(cents: number): string {\n if (cents === 0) return \"Free\";\n const dollars = cents / 100;\n const [whole, decimal] = dollars.toFixed(2).split(\".\");\n return `$${Number(whole).toLocaleString(\"en-US\")}.${decimal}`;\n}\n\nfunction formatRatePrice(rate: number): string {\n const dollars = rate / 10000;\n\n if (dollars === Math.floor(dollars)) {\n return `$${dollars.toFixed(2)}`;\n }\n\n const formatted = dollars.toFixed(4).replace(/0+$/, \"\");\n const decimalPlaces = formatted.split(\".\")[1]?.length ?? 0;\n if (decimalPlaces < 2) {\n return `$${dollars.toFixed(2)}`;\n }\n\n return `$${formatted}`;\n}\n\nfunction formatNumber(n: number): string {\n return n.toLocaleString(\"en-US\");\n}\n"],"mappings":"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","names":["CommetWebhooks","import_node","import_server","import_node","import_server"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/portal.ts","../src/pricing-markdown.ts","../src/webhooks.ts"],"sourcesContent":["/**\n * @commet/next - Next.js integration for Commet\n *\n * @example\n * Webhooks\n * ```typescript\n * import { Webhooks } from \"@commet/next\";\n *\n * export const POST = Webhooks({\n * webhookSecret: process.env.COMMET_WEBHOOK_SECRET!,\n * onSubscriptionActivated: async (payload) => {\n * // Handle activation\n * },\n * });\n * ```\n *\n * @example\n * Customer Portal\n * ```typescript\n * import { CustomerPortal } from \"@commet/next\";\n *\n * export const GET = CustomerPortal({\n * apiKey: process.env.COMMET_API_KEY!,\n * getCustomerId: async (req) => {\n * const session = await auth.api.getSession({ headers: req.headers });\n * return session?.user.id;\n * },\n * });\n * ```\n *\n * @packageDocumentation\n */\n\nexport type { CustomerPortalConfig } from \"./portal\";\nexport { CustomerPortal } from \"./portal\";\nexport type { PricingMarkdownConfig } from \"./pricing-markdown\";\nexport { PricingMarkdown } from \"./pricing-markdown\";\nexport type {\n WebhookData,\n WebhookEvent,\n WebhookPayload,\n WebhooksConfig,\n} from \"./types\";\nexport { Webhooks } from \"./webhooks\";\n","import { Commet } from \"@commet/node\";\nimport { type NextRequest, NextResponse } from \"next/server\";\n\nexport interface CustomerPortalConfig {\n apiKey: string;\n getCustomerId: (req: NextRequest) => Promise<string | null>;\n environment?: \"sandbox\" | \"production\";\n onError?: (error: Error) => void;\n}\n\n/**\n * Creates Next.js route handler for Customer Portal access\n *\n * @example\n * ```typescript\n * // app/api/commet/portal/route.ts\n * import { CustomerPortal } from \"@commet/next\";\n * import { auth } from \"@/lib/auth\";\n *\n * export const GET = CustomerPortal({\n * apiKey: process.env.COMMET_API_KEY!,\n * getCustomerId: async (req) => {\n * const session = await auth.api.getSession({ headers: req.headers });\n * return session?.user.id ?? null;\n * },\n * });\n * ```\n *\n * @example\n * ```tsx\n * // Use in component\n * export function BillingButton() {\n * return (\n * <Button asChild>\n * <Link href=\"/api/commet/portal\">Manage Billing</Link>\n * </Button>\n * );\n * }\n * ```\n */\nexport const CustomerPortal = ({\n apiKey,\n environment = \"production\",\n getCustomerId,\n onError,\n}: CustomerPortalConfig) => {\n const commet = new Commet({\n apiKey,\n environment,\n });\n\n return async (req: NextRequest) => {\n try {\n // Get customer identifier from request\n const customerId = await getCustomerId(req);\n\n if (!customerId) {\n return NextResponse.json(\n { error: \"Customer not authenticated\" },\n { status: 401 },\n );\n }\n\n // Get portal URL from Commet\n const result = await commet.portal.getUrl({\n customerId,\n });\n\n if (!result.success || !result.data) {\n throw new Error(result.message || \"Failed to create portal session\");\n }\n\n // Redirect to customer portal\n return NextResponse.redirect(result.data.portalUrl);\n } catch (error) {\n const errorObj =\n error instanceof Error ? error : new Error(String(error));\n\n // Call custom error handler if provided\n onError?.(errorObj);\n\n // Log error\n console.error(\"[Commet Portal]\", errorObj);\n\n // Return error response\n return NextResponse.json(\n { error: \"Failed to access customer portal\" },\n { status: 500 },\n );\n }\n };\n};\n","import type {\n BillingInterval,\n CreditPack,\n Plan,\n PlanFeature,\n} from \"@commet/node\";\nimport { Commet } from \"@commet/node\";\nimport { type NextRequest, NextResponse } from \"next/server\";\n\nexport interface PricingMarkdownConfig {\n apiKey: string;\n title: string;\n description?: string;\n includeCreditPacks?: boolean;\n billingIntervals?: BillingInterval[];\n cacheMaxAge?: number;\n onError?: (error: Error) => void;\n}\n\nconst INTERVAL_SHORT: Record<BillingInterval, string> = {\n monthly: \"/mo\",\n quarterly: \"/qtr\",\n yearly: \"/yr\",\n};\n\nexport const PricingMarkdown = ({\n apiKey,\n title,\n description,\n includeCreditPacks = false,\n billingIntervals,\n cacheMaxAge = 3600,\n onError,\n}: PricingMarkdownConfig) => {\n const commet = new Commet({ apiKey });\n\n return async (_req: NextRequest) => {\n try {\n const plansResult = await commet.plans.list();\n\n if (!plansResult.success || !plansResult.data) {\n throw new Error(plansResult.message ?? \"Failed to fetch plans\");\n }\n\n const publicPlans = plansResult.data.filter((plan) => plan.isPublic);\n\n const creditPacks = includeCreditPacks\n ? await commet.creditPacks\n .list()\n .then((result) =>\n result.success && result.data ? result.data : [],\n )\n : [];\n\n const markdown = generateMarkdown(\n title,\n description,\n publicPlans,\n creditPacks,\n billingIntervals,\n );\n\n return new NextResponse(markdown, {\n status: 200,\n headers: {\n \"Content-Type\": \"text/markdown; charset=utf-8\",\n \"Cache-Control\": `public, s-maxage=${cacheMaxAge}, stale-while-revalidate=${cacheMaxAge * 2}`,\n },\n });\n } catch (error) {\n const errorObj =\n error instanceof Error ? error : new Error(String(error));\n\n onError?.(errorObj);\n console.error(\"[Commet PricingMarkdown]\", errorObj);\n\n return NextResponse.json(\n { error: \"Failed to generate pricing markdown\" },\n { status: 500 },\n );\n }\n };\n};\n\nfunction generateMarkdown(\n title: string,\n description: string | undefined,\n plans: Plan[],\n creditPacks: CreditPack[],\n billingIntervals?: BillingInterval[],\n): string {\n const sections: string[] = [`# ${title}`];\n\n if (description) {\n sections.push(description);\n }\n\n if (plans.length > 0) {\n sections.push(renderPlansTable(plans, billingIntervals));\n\n const featuresSection = renderFeatures(plans);\n if (featuresSection) {\n sections.push(featuresSection);\n }\n }\n\n if (creditPacks.length > 0) {\n sections.push(renderCreditPacks(creditPacks));\n }\n\n return sections.join(\"\\n\\n\");\n}\n\nfunction renderPlansTable(\n plans: Plan[],\n filterIntervals?: BillingInterval[],\n): string {\n const usageFeatures = collectUsageFeatures(plans);\n\n const headerCols = [\"Plan\", \"Price\"];\n for (const { name, unitName } of usageFeatures) {\n headerCols.push(`${name}/${unitName ?? \"unit\"}`);\n headerCols.push(\"Overage\");\n }\n\n const rows = plans.map((plan) => {\n const priceCell = renderPriceCell(plan, filterIntervals);\n const cols = [plan.name, priceCell];\n\n const featuresByCode = new Map(\n plan.features.map((feature) => [feature.code, feature]),\n );\n\n for (const { code } of usageFeatures) {\n const feature = featuresByCode.get(code);\n\n if (!feature) {\n cols.push(\"—\", \"—\");\n continue;\n }\n\n if (feature.unlimited) {\n cols.push(\"Unlimited\", \"—\");\n continue;\n }\n\n const included = feature.includedAmount;\n cols.push(included ? formatNumber(included) : \"—\");\n\n const overagePrice =\n feature.overageEnabled && feature.overageUnitPrice !== undefined\n ? formatRatePrice(feature.overageUnitPrice)\n : \"—\";\n cols.push(overagePrice);\n }\n\n return cols;\n });\n\n const separator = headerCols.map(() => \"---\");\n const lines = [\n `| ${headerCols.join(\" | \")} |`,\n `| ${separator.join(\" | \")} |`,\n ...rows.map((cols) => `| ${cols.join(\" | \")} |`),\n ];\n\n const footnotes: string[] = [];\n\n const trialPlans = plans.filter((plan) =>\n plan.prices.some((price) => price.trialDays > 0),\n );\n for (const plan of trialPlans) {\n const trialPrice = plan.prices.find((price) => price.trialDays > 0)!;\n footnotes.push(\n `*${plan.name} plan includes a ${trialPrice.trialDays}-day free trial.*`,\n );\n }\n\n if (usageFeatures.length > 0) {\n footnotes.push(\n \"The overage rate applies only to usage beyond the included volume.\",\n );\n }\n\n return footnotes.length > 0\n ? `## Plans\\n\\n${lines.join(\"\\n\")}\\n\\n${footnotes.join(\"\\n\\n\")}`\n : `## Plans\\n\\n${lines.join(\"\\n\")}`;\n}\n\nfunction renderPriceCell(\n plan: Plan,\n filterIntervals?: BillingInterval[],\n): string {\n if (plan.isFree) return \"Free\";\n\n const prices = filterIntervals\n ? plan.prices.filter((price) =>\n filterIntervals.includes(price.billingInterval),\n )\n : plan.prices;\n\n if (prices.length === 0) return \"—\";\n\n return prices\n .map((price) => {\n if (price.price === 0) return \"Free\";\n return `${formatSettlementPrice(price.price)}${INTERVAL_SHORT[price.billingInterval]}`;\n })\n .join(\" / \");\n}\n\nfunction renderFeatures(plans: Plan[]): string | null {\n const allBooleanCodes = new Set<string>();\n for (const plan of plans) {\n for (const feature of plan.features) {\n if (feature.type === \"boolean\") {\n allBooleanCodes.add(feature.code);\n }\n }\n }\n\n if (allBooleanCodes.size === 0) return null;\n\n const enabledOnAll = new Set<string>();\n for (const code of allBooleanCodes) {\n const enabledOnEveryPlan = plans.every((plan) => {\n const feature = plan.features.find((f) => f.code === code);\n return feature?.enabled;\n });\n if (enabledOnEveryPlan) {\n enabledOnAll.add(code);\n }\n }\n\n const perPlanLines: string[] = [];\n for (const plan of plans) {\n const uniqueFeatures = plan.features\n .filter(\n (feature) =>\n feature.type === \"boolean\" &&\n feature.enabled &&\n !enabledOnAll.has(feature.code),\n )\n .map((feature) => feature.name);\n\n if (uniqueFeatures.length > 0) {\n perPlanLines.push(`- **${plan.name}**: ${uniqueFeatures.join(\", \")}`);\n }\n }\n\n const commonFeatures = plans[0]?.features\n .filter(\n (feature) => feature.type === \"boolean\" && enabledOnAll.has(feature.code),\n )\n .map((feature) => feature.name);\n\n const parts: string[] = [];\n\n if (perPlanLines.length > 0) {\n parts.push(perPlanLines.join(\"\\n\"));\n }\n\n if (commonFeatures && commonFeatures.length > 0) {\n parts.push(`All plans include: ${commonFeatures.join(\", \")}.`);\n }\n\n return parts.length > 0 ? `## Features\\n\\n${parts.join(\"\\n\\n\")}` : null;\n}\n\nfunction renderCreditPacks(packs: CreditPack[]): string {\n const lines = [\n \"| Pack | Credits | Price |\",\n \"| --- | --- | --- |\",\n ...packs.map(\n (pack) =>\n `| ${pack.name} | ${formatNumber(pack.credits)} credits | ${formatSettlementPrice(pack.price)} |`,\n ),\n ];\n\n return `## Credit Packs\\n\\n${lines.join(\"\\n\")}`;\n}\n\ninterface UsageFeatureColumn {\n code: string;\n name: string;\n unitName: string | null;\n}\n\nfunction collectUsageFeatures(plans: Plan[]): UsageFeatureColumn[] {\n const seen = new Map<string, UsageFeatureColumn>();\n for (const plan of plans) {\n for (const feature of plan.features) {\n if (\n (feature.type === \"metered\" || feature.type === \"seats\") &&\n !seen.has(feature.code) &&\n hasUsageData(feature)\n ) {\n seen.set(feature.code, {\n code: feature.code,\n name: feature.name,\n unitName: feature.unitName,\n });\n }\n }\n }\n return Array.from(seen.values());\n}\n\nfunction hasUsageData(feature: PlanFeature): boolean {\n return (\n feature.unlimited === true ||\n (feature.includedAmount !== undefined && feature.includedAmount > 0) ||\n (feature.overageEnabled === true && feature.overageUnitPrice !== undefined)\n );\n}\n\nfunction formatSettlementPrice(cents: number): string {\n if (cents === 0) return \"Free\";\n const dollars = cents / 100;\n const [whole, decimal] = dollars.toFixed(2).split(\".\");\n return `$${Number(whole).toLocaleString(\"en-US\")}.${decimal}`;\n}\n\nfunction formatRatePrice(rate: number): string {\n const dollars = rate / 10000;\n\n if (dollars === Math.floor(dollars)) {\n return `$${dollars.toFixed(2)}`;\n }\n\n const formatted = dollars.toFixed(4).replace(/0+$/, \"\");\n const decimalPlaces = formatted.split(\".\")[1]?.length ?? 0;\n if (decimalPlaces < 2) {\n return `$${dollars.toFixed(2)}`;\n }\n\n return `$${formatted}`;\n}\n\nfunction formatNumber(n: number): string {\n return n.toLocaleString(\"en-US\");\n}\n","import type { WebhookPayload } from \"@commet/node\";\nimport { Webhooks as CommetWebhooks } from \"@commet/node\";\nimport { type NextRequest, NextResponse } from \"next/server\";\nimport type { WebhooksConfig } from \"./types\";\n\n/**\n * Create a Next.js webhook handler for Commet events\n *\n * Automatically verifies signatures, routes events to handlers, and returns proper responses.\n *\n * @param config - Webhook configuration with secret and event handlers\n * @returns Next.js route handler function\n *\n * @example\n * ```typescript\n * // app/api/webhooks/commet/route.ts\n * import { Webhooks } from \"@commet/next\";\n *\n * export const POST = Webhooks({\n * webhookSecret: process.env.COMMET_WEBHOOK_SECRET!,\n * onSubscriptionActivated: async (payload) => {\n * // Grant access to user\n * },\n * onSubscriptionCanceled: async (payload) => {\n * // Revoke access from user\n * },\n * });\n * ```\n */\nexport const Webhooks = (config: WebhooksConfig) => {\n const {\n webhookSecret,\n onSubscriptionActivated,\n onSubscriptionCanceled,\n onSubscriptionCreated,\n onSubscriptionUpdated,\n onPayload,\n onError,\n } = config;\n\n // Create webhook verifier instance\n const webhooks = new CommetWebhooks();\n\n return async (request: NextRequest) => {\n try {\n // 1. Read raw request body\n const rawBody = await request.text();\n\n // 2. Extract signature from headers\n const signature = request.headers.get(\"x-commet-signature\");\n\n // 3. Verify signature\n const isValid = webhooks.verify({\n payload: rawBody,\n signature,\n secret: webhookSecret,\n });\n\n if (!isValid) {\n console.error(\"[Commet Webhook] Invalid signature\");\n return NextResponse.json(\n { received: false, error: \"Invalid signature\" },\n { status: 403 },\n );\n }\n\n // 4. Parse payload\n let payload: WebhookPayload;\n try {\n payload = JSON.parse(rawBody) as WebhookPayload;\n } catch (parseError) {\n console.error(\"[Commet Webhook] Failed to parse payload:\", parseError);\n if (onError) {\n await onError(\n parseError instanceof Error\n ? parseError\n : new Error(\"Failed to parse webhook payload\"),\n rawBody,\n );\n }\n return NextResponse.json(\n { received: false, error: \"Invalid payload\" },\n { status: 400 },\n );\n }\n\n // 5. Collect promises for parallel execution\n const promises: Promise<void>[] = [];\n\n // Call catch-all handler if provided\n if (onPayload) {\n promises.push(onPayload(payload));\n }\n\n // 6. Route to specific event handler\n switch (payload.event) {\n case \"subscription.activated\":\n if (onSubscriptionActivated) {\n promises.push(onSubscriptionActivated(payload));\n }\n break;\n\n case \"subscription.canceled\":\n if (onSubscriptionCanceled) {\n promises.push(onSubscriptionCanceled(payload));\n }\n break;\n\n case \"subscription.created\":\n if (onSubscriptionCreated) {\n promises.push(onSubscriptionCreated(payload));\n }\n break;\n\n case \"subscription.updated\":\n if (onSubscriptionUpdated) {\n promises.push(onSubscriptionUpdated(payload));\n }\n break;\n\n default:\n console.log(`[Commet Webhook] Unhandled event: ${payload.event}`);\n }\n\n // 7. Execute all handlers in parallel\n try {\n await Promise.all(promises);\n } catch (handlerError) {\n console.error(\n \"[Commet Webhook] Handler error:\",\n handlerError instanceof Error ? handlerError.message : handlerError,\n );\n if (onError) {\n await onError(\n handlerError instanceof Error\n ? handlerError\n : new Error(\"Handler execution failed\"),\n payload,\n );\n }\n // Still return 200 to prevent retries for handler errors\n return NextResponse.json(\n { received: true, warning: \"Handler failed\" },\n { status: 200 },\n );\n }\n\n // 8. Success response\n return NextResponse.json({ received: true }, { status: 200 });\n } catch (error) {\n console.error(\"[Commet Webhook] Unexpected error:\", error);\n if (onError) {\n try {\n await onError(\n error instanceof Error ? error : new Error(\"Unexpected error\"),\n undefined,\n );\n } catch (errorHandlerError) {\n console.error(\n \"[Commet Webhook] Error handler failed:\",\n errorHandlerError,\n );\n }\n }\n return NextResponse.json(\n { received: false, error: \"Internal error\" },\n { status: 500 },\n );\n }\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAAuB;AACvB,oBAA+C;AAuCxC,IAAM,iBAAiB,CAAC;AAAA,EAC7B;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AACF,MAA4B;AAC1B,QAAM,SAAS,IAAI,mBAAO;AAAA,IACxB;AAAA,IACA;AAAA,EACF,CAAC;AAED,SAAO,OAAO,QAAqB;AACjC,QAAI;AAEF,YAAM,aAAa,MAAM,cAAc,GAAG;AAE1C,UAAI,CAAC,YAAY;AACf,eAAO,2BAAa;AAAA,UAClB,EAAE,OAAO,6BAA6B;AAAA,UACtC,EAAE,QAAQ,IAAI;AAAA,QAChB;AAAA,MACF;AAGA,YAAM,SAAS,MAAM,OAAO,OAAO,OAAO;AAAA,QACxC;AAAA,MACF,CAAC;AAED,UAAI,CAAC,OAAO,WAAW,CAAC,OAAO,MAAM;AACnC,cAAM,IAAI,MAAM,OAAO,WAAW,iCAAiC;AAAA,MACrE;AAGA,aAAO,2BAAa,SAAS,OAAO,KAAK,SAAS;AAAA,IACpD,SAAS,OAAO;AACd,YAAM,WACJ,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAG1D,gBAAU,QAAQ;AAGlB,cAAQ,MAAM,mBAAmB,QAAQ;AAGzC,aAAO,2BAAa;AAAA,QAClB,EAAE,OAAO,mCAAmC;AAAA,QAC5C,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AACF;;;ACrFA,IAAAA,eAAuB;AACvB,IAAAC,iBAA+C;AAY/C,IAAM,iBAAkD;AAAA,EACtD,SAAS;AAAA,EACT,WAAW;AAAA,EACX,QAAQ;AACV;AAEO,IAAM,kBAAkB,CAAC;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA,qBAAqB;AAAA,EACrB;AAAA,EACA,cAAc;AAAA,EACd;AACF,MAA6B;AAC3B,QAAM,SAAS,IAAI,oBAAO,EAAE,OAAO,CAAC;AAEpC,SAAO,OAAO,SAAsB;AAClC,QAAI;AACF,YAAM,cAAc,MAAM,OAAO,MAAM,KAAK;AAE5C,UAAI,CAAC,YAAY,WAAW,CAAC,YAAY,MAAM;AAC7C,cAAM,IAAI,MAAM,YAAY,WAAW,uBAAuB;AAAA,MAChE;AAEA,YAAM,cAAc,YAAY,KAAK,OAAO,CAAC,SAAS,KAAK,QAAQ;AAEnE,YAAM,cAAc,qBAChB,MAAM,OAAO,YACV,KAAK,EACL;AAAA,QAAK,CAAC,WACL,OAAO,WAAW,OAAO,OAAO,OAAO,OAAO,CAAC;AAAA,MACjD,IACF,CAAC;AAEL,YAAM,WAAW;AAAA,QACf;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,aAAO,IAAI,4BAAa,UAAU;AAAA,QAChC,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,iBAAiB,oBAAoB,WAAW,4BAA4B,cAAc,CAAC;AAAA,QAC7F;AAAA,MACF,CAAC;AAAA,IACH,SAAS,OAAO;AACd,YAAM,WACJ,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAE1D,gBAAU,QAAQ;AAClB,cAAQ,MAAM,4BAA4B,QAAQ;AAElD,aAAO,4BAAa;AAAA,QAClB,EAAE,OAAO,sCAAsC;AAAA,QAC/C,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,iBACP,OACA,aACA,OACA,aACA,kBACQ;AACR,QAAM,WAAqB,CAAC,KAAK,KAAK,EAAE;AAExC,MAAI,aAAa;AACf,aAAS,KAAK,WAAW;AAAA,EAC3B;AAEA,MAAI,MAAM,SAAS,GAAG;AACpB,aAAS,KAAK,iBAAiB,OAAO,gBAAgB,CAAC;AAEvD,UAAM,kBAAkB,eAAe,KAAK;AAC5C,QAAI,iBAAiB;AACnB,eAAS,KAAK,eAAe;AAAA,IAC/B;AAAA,EACF;AAEA,MAAI,YAAY,SAAS,GAAG;AAC1B,aAAS,KAAK,kBAAkB,WAAW,CAAC;AAAA,EAC9C;AAEA,SAAO,SAAS,KAAK,MAAM;AAC7B;AAEA,SAAS,iBACP,OACA,iBACQ;AACR,QAAM,gBAAgB,qBAAqB,KAAK;AAEhD,QAAM,aAAa,CAAC,QAAQ,OAAO;AACnC,aAAW,EAAE,MAAM,SAAS,KAAK,eAAe;AAC9C,eAAW,KAAK,GAAG,IAAI,IAAI,YAAY,MAAM,EAAE;AAC/C,eAAW,KAAK,SAAS;AAAA,EAC3B;AAEA,QAAM,OAAO,MAAM,IAAI,CAAC,SAAS;AAC/B,UAAM,YAAY,gBAAgB,MAAM,eAAe;AACvD,UAAM,OAAO,CAAC,KAAK,MAAM,SAAS;AAElC,UAAM,iBAAiB,IAAI;AAAA,MACzB,KAAK,SAAS,IAAI,CAAC,YAAY,CAAC,QAAQ,MAAM,OAAO,CAAC;AAAA,IACxD;AAEA,eAAW,EAAE,KAAK,KAAK,eAAe;AACpC,YAAM,UAAU,eAAe,IAAI,IAAI;AAEvC,UAAI,CAAC,SAAS;AACZ,aAAK,KAAK,UAAK,QAAG;AAClB;AAAA,MACF;AAEA,UAAI,QAAQ,WAAW;AACrB,aAAK,KAAK,aAAa,QAAG;AAC1B;AAAA,MACF;AAEA,YAAM,WAAW,QAAQ;AACzB,WAAK,KAAK,WAAW,aAAa,QAAQ,IAAI,QAAG;AAEjD,YAAM,eACJ,QAAQ,kBAAkB,QAAQ,qBAAqB,SACnD,gBAAgB,QAAQ,gBAAgB,IACxC;AACN,WAAK,KAAK,YAAY;AAAA,IACxB;AAEA,WAAO;AAAA,EACT,CAAC;AAED,QAAM,YAAY,WAAW,IAAI,MAAM,KAAK;AAC5C,QAAM,QAAQ;AAAA,IACZ,KAAK,WAAW,KAAK,KAAK,CAAC;AAAA,IAC3B,KAAK,UAAU,KAAK,KAAK,CAAC;AAAA,IAC1B,GAAG,KAAK,IAAI,CAAC,SAAS,KAAK,KAAK,KAAK,KAAK,CAAC,IAAI;AAAA,EACjD;AAEA,QAAM,YAAsB,CAAC;AAE7B,QAAM,aAAa,MAAM;AAAA,IAAO,CAAC,SAC/B,KAAK,OAAO,KAAK,CAAC,UAAU,MAAM,YAAY,CAAC;AAAA,EACjD;AACA,aAAW,QAAQ,YAAY;AAC7B,UAAM,aAAa,KAAK,OAAO,KAAK,CAAC,UAAU,MAAM,YAAY,CAAC;AAClE,cAAU;AAAA,MACR,IAAI,KAAK,IAAI,oBAAoB,WAAW,SAAS;AAAA,IACvD;AAAA,EACF;AAEA,MAAI,cAAc,SAAS,GAAG;AAC5B,cAAU;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,SAAO,UAAU,SAAS,IACtB;AAAA;AAAA,EAAe,MAAM,KAAK,IAAI,CAAC;AAAA;AAAA,EAAO,UAAU,KAAK,MAAM,CAAC,KAC5D;AAAA;AAAA,EAAe,MAAM,KAAK,IAAI,CAAC;AACrC;AAEA,SAAS,gBACP,MACA,iBACQ;AACR,MAAI,KAAK,OAAQ,QAAO;AAExB,QAAM,SAAS,kBACX,KAAK,OAAO;AAAA,IAAO,CAAC,UAClB,gBAAgB,SAAS,MAAM,eAAe;AAAA,EAChD,IACA,KAAK;AAET,MAAI,OAAO,WAAW,EAAG,QAAO;AAEhC,SAAO,OACJ,IAAI,CAAC,UAAU;AACd,QAAI,MAAM,UAAU,EAAG,QAAO;AAC9B,WAAO,GAAG,sBAAsB,MAAM,KAAK,CAAC,GAAG,eAAe,MAAM,eAAe,CAAC;AAAA,EACtF,CAAC,EACA,KAAK,KAAK;AACf;AAEA,SAAS,eAAe,OAA8B;AACpD,QAAM,kBAAkB,oBAAI,IAAY;AACxC,aAAW,QAAQ,OAAO;AACxB,eAAW,WAAW,KAAK,UAAU;AACnC,UAAI,QAAQ,SAAS,WAAW;AAC9B,wBAAgB,IAAI,QAAQ,IAAI;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AAEA,MAAI,gBAAgB,SAAS,EAAG,QAAO;AAEvC,QAAM,eAAe,oBAAI,IAAY;AACrC,aAAW,QAAQ,iBAAiB;AAClC,UAAM,qBAAqB,MAAM,MAAM,CAAC,SAAS;AAC/C,YAAM,UAAU,KAAK,SAAS,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;AACzD,aAAO,SAAS;AAAA,IAClB,CAAC;AACD,QAAI,oBAAoB;AACtB,mBAAa,IAAI,IAAI;AAAA,IACvB;AAAA,EACF;AAEA,QAAM,eAAyB,CAAC;AAChC,aAAW,QAAQ,OAAO;AACxB,UAAM,iBAAiB,KAAK,SACzB;AAAA,MACC,CAAC,YACC,QAAQ,SAAS,aACjB,QAAQ,WACR,CAAC,aAAa,IAAI,QAAQ,IAAI;AAAA,IAClC,EACC,IAAI,CAAC,YAAY,QAAQ,IAAI;AAEhC,QAAI,eAAe,SAAS,GAAG;AAC7B,mBAAa,KAAK,OAAO,KAAK,IAAI,OAAO,eAAe,KAAK,IAAI,CAAC,EAAE;AAAA,IACtE;AAAA,EACF;AAEA,QAAM,iBAAiB,MAAM,CAAC,GAAG,SAC9B;AAAA,IACC,CAAC,YAAY,QAAQ,SAAS,aAAa,aAAa,IAAI,QAAQ,IAAI;AAAA,EAC1E,EACC,IAAI,CAAC,YAAY,QAAQ,IAAI;AAEhC,QAAM,QAAkB,CAAC;AAEzB,MAAI,aAAa,SAAS,GAAG;AAC3B,UAAM,KAAK,aAAa,KAAK,IAAI,CAAC;AAAA,EACpC;AAEA,MAAI,kBAAkB,eAAe,SAAS,GAAG;AAC/C,UAAM,KAAK,sBAAsB,eAAe,KAAK,IAAI,CAAC,GAAG;AAAA,EAC/D;AAEA,SAAO,MAAM,SAAS,IAAI;AAAA;AAAA,EAAkB,MAAM,KAAK,MAAM,CAAC,KAAK;AACrE;AAEA,SAAS,kBAAkB,OAA6B;AACtD,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA;AAAA,IACA,GAAG,MAAM;AAAA,MACP,CAAC,SACC,KAAK,KAAK,IAAI,MAAM,aAAa,KAAK,OAAO,CAAC,cAAc,sBAAsB,KAAK,KAAK,CAAC;AAAA,IACjG;AAAA,EACF;AAEA,SAAO;AAAA;AAAA,EAAsB,MAAM,KAAK,IAAI,CAAC;AAC/C;AAQA,SAAS,qBAAqB,OAAqC;AACjE,QAAM,OAAO,oBAAI,IAAgC;AACjD,aAAW,QAAQ,OAAO;AACxB,eAAW,WAAW,KAAK,UAAU;AACnC,WACG,QAAQ,SAAS,aAAa,QAAQ,SAAS,YAChD,CAAC,KAAK,IAAI,QAAQ,IAAI,KACtB,aAAa,OAAO,GACpB;AACA,aAAK,IAAI,QAAQ,MAAM;AAAA,UACrB,MAAM,QAAQ;AAAA,UACd,MAAM,QAAQ;AAAA,UACd,UAAU,QAAQ;AAAA,QACpB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACA,SAAO,MAAM,KAAK,KAAK,OAAO,CAAC;AACjC;AAEA,SAAS,aAAa,SAA+B;AACnD,SACE,QAAQ,cAAc,QACrB,QAAQ,mBAAmB,UAAa,QAAQ,iBAAiB,KACjE,QAAQ,mBAAmB,QAAQ,QAAQ,qBAAqB;AAErE;AAEA,SAAS,sBAAsB,OAAuB;AACpD,MAAI,UAAU,EAAG,QAAO;AACxB,QAAM,UAAU,QAAQ;AACxB,QAAM,CAAC,OAAO,OAAO,IAAI,QAAQ,QAAQ,CAAC,EAAE,MAAM,GAAG;AACrD,SAAO,IAAI,OAAO,KAAK,EAAE,eAAe,OAAO,CAAC,IAAI,OAAO;AAC7D;AAEA,SAAS,gBAAgB,MAAsB;AAC7C,QAAM,UAAU,OAAO;AAEvB,MAAI,YAAY,KAAK,MAAM,OAAO,GAAG;AACnC,WAAO,IAAI,QAAQ,QAAQ,CAAC,CAAC;AAAA,EAC/B;AAEA,QAAM,YAAY,QAAQ,QAAQ,CAAC,EAAE,QAAQ,OAAO,EAAE;AACtD,QAAM,gBAAgB,UAAU,MAAM,GAAG,EAAE,CAAC,GAAG,UAAU;AACzD,MAAI,gBAAgB,GAAG;AACrB,WAAO,IAAI,QAAQ,QAAQ,CAAC,CAAC;AAAA,EAC/B;AAEA,SAAO,IAAI,SAAS;AACtB;AAEA,SAAS,aAAa,GAAmB;AACvC,SAAO,EAAE,eAAe,OAAO;AACjC;;;ACpVA,IAAAC,eAA2C;AAC3C,IAAAC,iBAA+C;AA2BxC,IAAM,WAAW,CAAC,WAA2B;AAClD,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAGJ,QAAM,WAAW,IAAI,aAAAC,SAAe;AAEpC,SAAO,OAAO,YAAyB;AACrC,QAAI;AAEF,YAAM,UAAU,MAAM,QAAQ,KAAK;AAGnC,YAAM,YAAY,QAAQ,QAAQ,IAAI,oBAAoB;AAG1D,YAAM,UAAU,SAAS,OAAO;AAAA,QAC9B,SAAS;AAAA,QACT;AAAA,QACA,QAAQ;AAAA,MACV,CAAC;AAED,UAAI,CAAC,SAAS;AACZ,gBAAQ,MAAM,oCAAoC;AAClD,eAAO,4BAAa;AAAA,UAClB,EAAE,UAAU,OAAO,OAAO,oBAAoB;AAAA,UAC9C,EAAE,QAAQ,IAAI;AAAA,QAChB;AAAA,MACF;AAGA,UAAI;AACJ,UAAI;AACF,kBAAU,KAAK,MAAM,OAAO;AAAA,MAC9B,SAAS,YAAY;AACnB,gBAAQ,MAAM,6CAA6C,UAAU;AACrE,YAAI,SAAS;AACX,gBAAM;AAAA,YACJ,sBAAsB,QAClB,aACA,IAAI,MAAM,iCAAiC;AAAA,YAC/C;AAAA,UACF;AAAA,QACF;AACA,eAAO,4BAAa;AAAA,UAClB,EAAE,UAAU,OAAO,OAAO,kBAAkB;AAAA,UAC5C,EAAE,QAAQ,IAAI;AAAA,QAChB;AAAA,MACF;AAGA,YAAM,WAA4B,CAAC;AAGnC,UAAI,WAAW;AACb,iBAAS,KAAK,UAAU,OAAO,CAAC;AAAA,MAClC;AAGA,cAAQ,QAAQ,OAAO;AAAA,QACrB,KAAK;AACH,cAAI,yBAAyB;AAC3B,qBAAS,KAAK,wBAAwB,OAAO,CAAC;AAAA,UAChD;AACA;AAAA,QAEF,KAAK;AACH,cAAI,wBAAwB;AAC1B,qBAAS,KAAK,uBAAuB,OAAO,CAAC;AAAA,UAC/C;AACA;AAAA,QAEF,KAAK;AACH,cAAI,uBAAuB;AACzB,qBAAS,KAAK,sBAAsB,OAAO,CAAC;AAAA,UAC9C;AACA;AAAA,QAEF,KAAK;AACH,cAAI,uBAAuB;AACzB,qBAAS,KAAK,sBAAsB,OAAO,CAAC;AAAA,UAC9C;AACA;AAAA,QAEF;AACE,kBAAQ,IAAI,qCAAqC,QAAQ,KAAK,EAAE;AAAA,MACpE;AAGA,UAAI;AACF,cAAM,QAAQ,IAAI,QAAQ;AAAA,MAC5B,SAAS,cAAc;AACrB,gBAAQ;AAAA,UACN;AAAA,UACA,wBAAwB,QAAQ,aAAa,UAAU;AAAA,QACzD;AACA,YAAI,SAAS;AACX,gBAAM;AAAA,YACJ,wBAAwB,QACpB,eACA,IAAI,MAAM,0BAA0B;AAAA,YACxC;AAAA,UACF;AAAA,QACF;AAEA,eAAO,4BAAa;AAAA,UAClB,EAAE,UAAU,MAAM,SAAS,iBAAiB;AAAA,UAC5C,EAAE,QAAQ,IAAI;AAAA,QAChB;AAAA,MACF;AAGA,aAAO,4BAAa,KAAK,EAAE,UAAU,KAAK,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC9D,SAAS,OAAO;AACd,cAAQ,MAAM,sCAAsC,KAAK;AACzD,UAAI,SAAS;AACX,YAAI;AACF,gBAAM;AAAA,YACJ,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,kBAAkB;AAAA,YAC7D;AAAA,UACF;AAAA,QACF,SAAS,mBAAmB;AAC1B,kBAAQ;AAAA,YACN;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AACA,aAAO,4BAAa;AAAA,QAClB,EAAE,UAAU,OAAO,OAAO,iBAAiB;AAAA,QAC3C,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AACF;","names":["import_node","import_server","import_node","import_server","CommetWebhooks"]}
package/dist/index.mjs CHANGED
@@ -1,122 +1,6 @@
1
- // src/webhooks.ts
2
- import { Webhooks as CommetWebhooks } from "@commet/node";
3
- import { NextResponse } from "next/server";
4
- var Webhooks = (config) => {
5
- const {
6
- webhookSecret,
7
- onSubscriptionActivated,
8
- onSubscriptionCanceled,
9
- onSubscriptionCreated,
10
- onSubscriptionUpdated,
11
- onPayload,
12
- onError
13
- } = config;
14
- const webhooks = new CommetWebhooks();
15
- return async (request) => {
16
- try {
17
- const rawBody = await request.text();
18
- const signature = request.headers.get("x-commet-signature");
19
- const isValid = webhooks.verify({
20
- payload: rawBody,
21
- signature,
22
- secret: webhookSecret
23
- });
24
- if (!isValid) {
25
- console.error("[Commet Webhook] Invalid signature");
26
- return NextResponse.json(
27
- { received: false, error: "Invalid signature" },
28
- { status: 403 }
29
- );
30
- }
31
- let payload;
32
- try {
33
- payload = JSON.parse(rawBody);
34
- } catch (parseError) {
35
- console.error("[Commet Webhook] Failed to parse payload:", parseError);
36
- if (onError) {
37
- await onError(
38
- parseError instanceof Error ? parseError : new Error("Failed to parse webhook payload"),
39
- rawBody
40
- );
41
- }
42
- return NextResponse.json(
43
- { received: false, error: "Invalid payload" },
44
- { status: 400 }
45
- );
46
- }
47
- const promises = [];
48
- if (onPayload) {
49
- promises.push(onPayload(payload));
50
- }
51
- switch (payload.event) {
52
- case "subscription.activated":
53
- if (onSubscriptionActivated) {
54
- promises.push(onSubscriptionActivated(payload));
55
- }
56
- break;
57
- case "subscription.canceled":
58
- if (onSubscriptionCanceled) {
59
- promises.push(onSubscriptionCanceled(payload));
60
- }
61
- break;
62
- case "subscription.created":
63
- if (onSubscriptionCreated) {
64
- promises.push(onSubscriptionCreated(payload));
65
- }
66
- break;
67
- case "subscription.updated":
68
- if (onSubscriptionUpdated) {
69
- promises.push(onSubscriptionUpdated(payload));
70
- }
71
- break;
72
- default:
73
- console.log(`[Commet Webhook] Unhandled event: ${payload.event}`);
74
- }
75
- try {
76
- await Promise.all(promises);
77
- } catch (handlerError) {
78
- console.error(
79
- "[Commet Webhook] Handler error:",
80
- handlerError instanceof Error ? handlerError.message : handlerError
81
- );
82
- if (onError) {
83
- await onError(
84
- handlerError instanceof Error ? handlerError : new Error("Handler execution failed"),
85
- payload
86
- );
87
- }
88
- return NextResponse.json(
89
- { received: true, warning: "Handler failed" },
90
- { status: 200 }
91
- );
92
- }
93
- return NextResponse.json({ received: true }, { status: 200 });
94
- } catch (error) {
95
- console.error("[Commet Webhook] Unexpected error:", error);
96
- if (onError) {
97
- try {
98
- await onError(
99
- error instanceof Error ? error : new Error("Unexpected error"),
100
- void 0
101
- );
102
- } catch (errorHandlerError) {
103
- console.error(
104
- "[Commet Webhook] Error handler failed:",
105
- errorHandlerError
106
- );
107
- }
108
- }
109
- return NextResponse.json(
110
- { received: false, error: "Internal error" },
111
- { status: 500 }
112
- );
113
- }
114
- };
115
- };
116
-
117
1
  // src/portal.ts
118
2
  import { Commet } from "@commet/node";
119
- import { NextResponse as NextResponse2 } from "next/server";
3
+ import { NextResponse } from "next/server";
120
4
  var CustomerPortal = ({
121
5
  apiKey,
122
6
  environment = "production",
@@ -131,23 +15,23 @@ var CustomerPortal = ({
131
15
  try {
132
16
  const customerId = await getCustomerId(req);
133
17
  if (!customerId) {
134
- return NextResponse2.json(
18
+ return NextResponse.json(
135
19
  { error: "Customer not authenticated" },
136
20
  { status: 401 }
137
21
  );
138
22
  }
139
23
  const result = await commet.portal.getUrl({
140
- externalId: customerId
24
+ customerId
141
25
  });
142
26
  if (!result.success || !result.data) {
143
27
  throw new Error(result.message || "Failed to create portal session");
144
28
  }
145
- return NextResponse2.redirect(result.data.portalUrl);
29
+ return NextResponse.redirect(result.data.portalUrl);
146
30
  } catch (error) {
147
31
  const errorObj = error instanceof Error ? error : new Error(String(error));
148
32
  onError?.(errorObj);
149
33
  console.error("[Commet Portal]", errorObj);
150
- return NextResponse2.json(
34
+ return NextResponse.json(
151
35
  { error: "Failed to access customer portal" },
152
36
  { status: 500 }
153
37
  );
@@ -157,7 +41,7 @@ var CustomerPortal = ({
157
41
 
158
42
  // src/pricing-markdown.ts
159
43
  import { Commet as Commet2 } from "@commet/node";
160
- import { NextResponse as NextResponse3 } from "next/server";
44
+ import { NextResponse as NextResponse2 } from "next/server";
161
45
  var INTERVAL_SHORT = {
162
46
  monthly: "/mo",
163
47
  quarterly: "/qtr",
@@ -190,7 +74,7 @@ var PricingMarkdown = ({
190
74
  creditPacks,
191
75
  billingIntervals
192
76
  );
193
- return new NextResponse3(markdown, {
77
+ return new NextResponse2(markdown, {
194
78
  status: 200,
195
79
  headers: {
196
80
  "Content-Type": "text/markdown; charset=utf-8",
@@ -201,7 +85,7 @@ var PricingMarkdown = ({
201
85
  const errorObj = error instanceof Error ? error : new Error(String(error));
202
86
  onError?.(errorObj);
203
87
  console.error("[Commet PricingMarkdown]", errorObj);
204
- return NextResponse3.json(
88
+ return NextResponse2.json(
205
89
  { error: "Failed to generate pricing markdown" },
206
90
  { status: 500 }
207
91
  );
@@ -389,6 +273,122 @@ function formatRatePrice(rate) {
389
273
  function formatNumber(n) {
390
274
  return n.toLocaleString("en-US");
391
275
  }
276
+
277
+ // src/webhooks.ts
278
+ import { Webhooks as CommetWebhooks } from "@commet/node";
279
+ import { NextResponse as NextResponse3 } from "next/server";
280
+ var Webhooks = (config) => {
281
+ const {
282
+ webhookSecret,
283
+ onSubscriptionActivated,
284
+ onSubscriptionCanceled,
285
+ onSubscriptionCreated,
286
+ onSubscriptionUpdated,
287
+ onPayload,
288
+ onError
289
+ } = config;
290
+ const webhooks = new CommetWebhooks();
291
+ return async (request) => {
292
+ try {
293
+ const rawBody = await request.text();
294
+ const signature = request.headers.get("x-commet-signature");
295
+ const isValid = webhooks.verify({
296
+ payload: rawBody,
297
+ signature,
298
+ secret: webhookSecret
299
+ });
300
+ if (!isValid) {
301
+ console.error("[Commet Webhook] Invalid signature");
302
+ return NextResponse3.json(
303
+ { received: false, error: "Invalid signature" },
304
+ { status: 403 }
305
+ );
306
+ }
307
+ let payload;
308
+ try {
309
+ payload = JSON.parse(rawBody);
310
+ } catch (parseError) {
311
+ console.error("[Commet Webhook] Failed to parse payload:", parseError);
312
+ if (onError) {
313
+ await onError(
314
+ parseError instanceof Error ? parseError : new Error("Failed to parse webhook payload"),
315
+ rawBody
316
+ );
317
+ }
318
+ return NextResponse3.json(
319
+ { received: false, error: "Invalid payload" },
320
+ { status: 400 }
321
+ );
322
+ }
323
+ const promises = [];
324
+ if (onPayload) {
325
+ promises.push(onPayload(payload));
326
+ }
327
+ switch (payload.event) {
328
+ case "subscription.activated":
329
+ if (onSubscriptionActivated) {
330
+ promises.push(onSubscriptionActivated(payload));
331
+ }
332
+ break;
333
+ case "subscription.canceled":
334
+ if (onSubscriptionCanceled) {
335
+ promises.push(onSubscriptionCanceled(payload));
336
+ }
337
+ break;
338
+ case "subscription.created":
339
+ if (onSubscriptionCreated) {
340
+ promises.push(onSubscriptionCreated(payload));
341
+ }
342
+ break;
343
+ case "subscription.updated":
344
+ if (onSubscriptionUpdated) {
345
+ promises.push(onSubscriptionUpdated(payload));
346
+ }
347
+ break;
348
+ default:
349
+ console.log(`[Commet Webhook] Unhandled event: ${payload.event}`);
350
+ }
351
+ try {
352
+ await Promise.all(promises);
353
+ } catch (handlerError) {
354
+ console.error(
355
+ "[Commet Webhook] Handler error:",
356
+ handlerError instanceof Error ? handlerError.message : handlerError
357
+ );
358
+ if (onError) {
359
+ await onError(
360
+ handlerError instanceof Error ? handlerError : new Error("Handler execution failed"),
361
+ payload
362
+ );
363
+ }
364
+ return NextResponse3.json(
365
+ { received: true, warning: "Handler failed" },
366
+ { status: 200 }
367
+ );
368
+ }
369
+ return NextResponse3.json({ received: true }, { status: 200 });
370
+ } catch (error) {
371
+ console.error("[Commet Webhook] Unexpected error:", error);
372
+ if (onError) {
373
+ try {
374
+ await onError(
375
+ error instanceof Error ? error : new Error("Unexpected error"),
376
+ void 0
377
+ );
378
+ } catch (errorHandlerError) {
379
+ console.error(
380
+ "[Commet Webhook] Error handler failed:",
381
+ errorHandlerError
382
+ );
383
+ }
384
+ }
385
+ return NextResponse3.json(
386
+ { received: false, error: "Internal error" },
387
+ { status: 500 }
388
+ );
389
+ }
390
+ };
391
+ };
392
392
  export {
393
393
  CustomerPortal,
394
394
  PricingMarkdown,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/webhooks.ts","../src/portal.ts","../src/pricing-markdown.ts"],"sourcesContent":["import { Webhooks as CommetWebhooks } from \"@commet/node\";\nimport type { WebhookPayload } from \"@commet/node\";\nimport { type NextRequest, NextResponse } from \"next/server\";\nimport type { WebhooksConfig } from \"./types\";\n\n/**\n * Create a Next.js webhook handler for Commet events\n *\n * Automatically verifies signatures, routes events to handlers, and returns proper responses.\n *\n * @param config - Webhook configuration with secret and event handlers\n * @returns Next.js route handler function\n *\n * @example\n * ```typescript\n * // app/api/webhooks/commet/route.ts\n * import { Webhooks } from \"@commet/next\";\n *\n * export const POST = Webhooks({\n * webhookSecret: process.env.COMMET_WEBHOOK_SECRET!,\n * onSubscriptionActivated: async (payload) => {\n * // Grant access to user\n * },\n * onSubscriptionCanceled: async (payload) => {\n * // Revoke access from user\n * },\n * });\n * ```\n */\nexport const Webhooks = (config: WebhooksConfig) => {\n const {\n webhookSecret,\n onSubscriptionActivated,\n onSubscriptionCanceled,\n onSubscriptionCreated,\n onSubscriptionUpdated,\n onPayload,\n onError,\n } = config;\n\n // Create webhook verifier instance\n const webhooks = new CommetWebhooks();\n\n return async (request: NextRequest) => {\n try {\n // 1. Read raw request body\n const rawBody = await request.text();\n\n // 2. Extract signature from headers\n const signature = request.headers.get(\"x-commet-signature\");\n\n // 3. Verify signature\n const isValid = webhooks.verify({\n payload: rawBody,\n signature,\n secret: webhookSecret,\n });\n\n if (!isValid) {\n console.error(\"[Commet Webhook] Invalid signature\");\n return NextResponse.json(\n { received: false, error: \"Invalid signature\" },\n { status: 403 },\n );\n }\n\n // 4. Parse payload\n let payload: WebhookPayload;\n try {\n payload = JSON.parse(rawBody) as WebhookPayload;\n } catch (parseError) {\n console.error(\"[Commet Webhook] Failed to parse payload:\", parseError);\n if (onError) {\n await onError(\n parseError instanceof Error\n ? parseError\n : new Error(\"Failed to parse webhook payload\"),\n rawBody,\n );\n }\n return NextResponse.json(\n { received: false, error: \"Invalid payload\" },\n { status: 400 },\n );\n }\n\n // 5. Collect promises for parallel execution\n const promises: Promise<void>[] = [];\n\n // Call catch-all handler if provided\n if (onPayload) {\n promises.push(onPayload(payload));\n }\n\n // 6. Route to specific event handler\n switch (payload.event) {\n case \"subscription.activated\":\n if (onSubscriptionActivated) {\n promises.push(onSubscriptionActivated(payload));\n }\n break;\n\n case \"subscription.canceled\":\n if (onSubscriptionCanceled) {\n promises.push(onSubscriptionCanceled(payload));\n }\n break;\n\n case \"subscription.created\":\n if (onSubscriptionCreated) {\n promises.push(onSubscriptionCreated(payload));\n }\n break;\n\n case \"subscription.updated\":\n if (onSubscriptionUpdated) {\n promises.push(onSubscriptionUpdated(payload));\n }\n break;\n\n default:\n console.log(`[Commet Webhook] Unhandled event: ${payload.event}`);\n }\n\n // 7. Execute all handlers in parallel\n try {\n await Promise.all(promises);\n } catch (handlerError) {\n console.error(\n \"[Commet Webhook] Handler error:\",\n handlerError instanceof Error ? handlerError.message : handlerError,\n );\n if (onError) {\n await onError(\n handlerError instanceof Error\n ? handlerError\n : new Error(\"Handler execution failed\"),\n payload,\n );\n }\n // Still return 200 to prevent retries for handler errors\n return NextResponse.json(\n { received: true, warning: \"Handler failed\" },\n { status: 200 },\n );\n }\n\n // 8. Success response\n return NextResponse.json({ received: true }, { status: 200 });\n } catch (error) {\n console.error(\"[Commet Webhook] Unexpected error:\", error);\n if (onError) {\n try {\n await onError(\n error instanceof Error ? error : new Error(\"Unexpected error\"),\n undefined,\n );\n } catch (errorHandlerError) {\n console.error(\n \"[Commet Webhook] Error handler failed:\",\n errorHandlerError,\n );\n }\n }\n return NextResponse.json(\n { received: false, error: \"Internal error\" },\n { status: 500 },\n );\n }\n };\n};\n","import { Commet } from \"@commet/node\";\nimport { type NextRequest, NextResponse } from \"next/server\";\n\nexport interface CustomerPortalConfig {\n apiKey: string;\n getCustomerId: (req: NextRequest) => Promise<string | null>;\n environment?: \"sandbox\" | \"production\";\n onError?: (error: Error) => void;\n}\n\n/**\n * Creates Next.js route handler for Customer Portal access\n *\n * @example\n * ```typescript\n * // app/api/commet/portal/route.ts\n * import { CustomerPortal } from \"@commet/next\";\n * import { auth } from \"@/lib/auth\";\n *\n * export const GET = CustomerPortal({\n * apiKey: process.env.COMMET_API_KEY!,\n * getCustomerId: async (req) => {\n * const session = await auth.api.getSession({ headers: req.headers });\n * return session?.user.id ?? null;\n * },\n * });\n * ```\n *\n * @example\n * ```tsx\n * // Use in component\n * export function BillingButton() {\n * return (\n * <Button asChild>\n * <Link href=\"/api/commet/portal\">Manage Billing</Link>\n * </Button>\n * );\n * }\n * ```\n */\nexport const CustomerPortal = ({\n apiKey,\n environment = \"production\",\n getCustomerId,\n onError,\n}: CustomerPortalConfig) => {\n const commet = new Commet({\n apiKey,\n environment,\n });\n\n return async (req: NextRequest) => {\n try {\n // Get customer identifier from request\n const customerId = await getCustomerId(req);\n\n if (!customerId) {\n return NextResponse.json(\n { error: \"Customer not authenticated\" },\n { status: 401 },\n );\n }\n\n // Get portal URL from Commet\n const result = await commet.portal.getUrl({\n externalId: customerId,\n });\n\n if (!result.success || !result.data) {\n throw new Error(result.message || \"Failed to create portal session\");\n }\n\n // Redirect to customer portal\n return NextResponse.redirect(result.data.portalUrl);\n } catch (error) {\n const errorObj =\n error instanceof Error ? error : new Error(String(error));\n\n // Call custom error handler if provided\n onError?.(errorObj);\n\n // Log error\n console.error(\"[Commet Portal]\", errorObj);\n\n // Return error response\n return NextResponse.json(\n { error: \"Failed to access customer portal\" },\n { status: 500 },\n );\n }\n };\n};\n","import { Commet } from \"@commet/node\";\nimport type {\n BillingInterval,\n CreditPack,\n Plan,\n PlanFeature,\n} from \"@commet/node\";\nimport { type NextRequest, NextResponse } from \"next/server\";\n\nexport interface PricingMarkdownConfig {\n apiKey: string;\n title: string;\n description?: string;\n includeCreditPacks?: boolean;\n billingIntervals?: BillingInterval[];\n cacheMaxAge?: number;\n onError?: (error: Error) => void;\n}\n\nconst INTERVAL_SHORT: Record<BillingInterval, string> = {\n monthly: \"/mo\",\n quarterly: \"/qtr\",\n yearly: \"/yr\",\n};\n\nexport const PricingMarkdown = ({\n apiKey,\n title,\n description,\n includeCreditPacks = false,\n billingIntervals,\n cacheMaxAge = 3600,\n onError,\n}: PricingMarkdownConfig) => {\n const commet = new Commet({ apiKey });\n\n return async (_req: NextRequest) => {\n try {\n const plansResult = await commet.plans.list();\n\n if (!plansResult.success || !plansResult.data) {\n throw new Error(plansResult.message ?? \"Failed to fetch plans\");\n }\n\n const publicPlans = plansResult.data.filter((plan) => plan.isPublic);\n\n const creditPacks = includeCreditPacks\n ? await commet.creditPacks\n .list()\n .then((result) =>\n result.success && result.data ? result.data : [],\n )\n : [];\n\n const markdown = generateMarkdown(\n title,\n description,\n publicPlans,\n creditPacks,\n billingIntervals,\n );\n\n return new NextResponse(markdown, {\n status: 200,\n headers: {\n \"Content-Type\": \"text/markdown; charset=utf-8\",\n \"Cache-Control\": `public, s-maxage=${cacheMaxAge}, stale-while-revalidate=${cacheMaxAge * 2}`,\n },\n });\n } catch (error) {\n const errorObj =\n error instanceof Error ? error : new Error(String(error));\n\n onError?.(errorObj);\n console.error(\"[Commet PricingMarkdown]\", errorObj);\n\n return NextResponse.json(\n { error: \"Failed to generate pricing markdown\" },\n { status: 500 },\n );\n }\n };\n};\n\nfunction generateMarkdown(\n title: string,\n description: string | undefined,\n plans: Plan[],\n creditPacks: CreditPack[],\n billingIntervals?: BillingInterval[],\n): string {\n const sections: string[] = [`# ${title}`];\n\n if (description) {\n sections.push(description);\n }\n\n if (plans.length > 0) {\n sections.push(renderPlansTable(plans, billingIntervals));\n\n const featuresSection = renderFeatures(plans);\n if (featuresSection) {\n sections.push(featuresSection);\n }\n }\n\n if (creditPacks.length > 0) {\n sections.push(renderCreditPacks(creditPacks));\n }\n\n return sections.join(\"\\n\\n\");\n}\n\nfunction renderPlansTable(\n plans: Plan[],\n filterIntervals?: BillingInterval[],\n): string {\n const usageFeatures = collectUsageFeatures(plans);\n\n const headerCols = [\"Plan\", \"Price\"];\n for (const { name, unitName } of usageFeatures) {\n headerCols.push(`${name}/${unitName ?? \"unit\"}`);\n headerCols.push(\"Overage\");\n }\n\n const rows = plans.map((plan) => {\n const priceCell = renderPriceCell(plan, filterIntervals);\n const cols = [plan.name, priceCell];\n\n const featuresByCode = new Map(\n plan.features.map((feature) => [feature.code, feature]),\n );\n\n for (const { code } of usageFeatures) {\n const feature = featuresByCode.get(code);\n\n if (!feature) {\n cols.push(\"—\", \"—\");\n continue;\n }\n\n if (feature.unlimited) {\n cols.push(\"Unlimited\", \"—\");\n continue;\n }\n\n const included = feature.includedAmount;\n cols.push(included ? formatNumber(included) : \"—\");\n\n const overagePrice =\n feature.overageEnabled && feature.overageUnitPrice !== undefined\n ? formatRatePrice(feature.overageUnitPrice)\n : \"—\";\n cols.push(overagePrice);\n }\n\n return cols;\n });\n\n const separator = headerCols.map(() => \"---\");\n const lines = [\n `| ${headerCols.join(\" | \")} |`,\n `| ${separator.join(\" | \")} |`,\n ...rows.map((cols) => `| ${cols.join(\" | \")} |`),\n ];\n\n const footnotes: string[] = [];\n\n const trialPlans = plans.filter((plan) =>\n plan.prices.some((price) => price.trialDays > 0),\n );\n for (const plan of trialPlans) {\n const trialPrice = plan.prices.find((price) => price.trialDays > 0)!;\n footnotes.push(\n `*${plan.name} plan includes a ${trialPrice.trialDays}-day free trial.*`,\n );\n }\n\n if (usageFeatures.length > 0) {\n footnotes.push(\n \"The overage rate applies only to usage beyond the included volume.\",\n );\n }\n\n return footnotes.length > 0\n ? `## Plans\\n\\n${lines.join(\"\\n\")}\\n\\n${footnotes.join(\"\\n\\n\")}`\n : `## Plans\\n\\n${lines.join(\"\\n\")}`;\n}\n\nfunction renderPriceCell(\n plan: Plan,\n filterIntervals?: BillingInterval[],\n): string {\n if (plan.isFree) return \"Free\";\n\n const prices = filterIntervals\n ? plan.prices.filter((price) =>\n filterIntervals.includes(price.billingInterval),\n )\n : plan.prices;\n\n if (prices.length === 0) return \"—\";\n\n return prices\n .map((price) => {\n if (price.price === 0) return \"Free\";\n return `${formatSettlementPrice(price.price)}${INTERVAL_SHORT[price.billingInterval]}`;\n })\n .join(\" / \");\n}\n\nfunction renderFeatures(plans: Plan[]): string | null {\n const allBooleanCodes = new Set<string>();\n for (const plan of plans) {\n for (const feature of plan.features) {\n if (feature.type === \"boolean\") {\n allBooleanCodes.add(feature.code);\n }\n }\n }\n\n if (allBooleanCodes.size === 0) return null;\n\n const enabledOnAll = new Set<string>();\n for (const code of allBooleanCodes) {\n const enabledOnEveryPlan = plans.every((plan) => {\n const feature = plan.features.find((f) => f.code === code);\n return feature?.enabled;\n });\n if (enabledOnEveryPlan) {\n enabledOnAll.add(code);\n }\n }\n\n const perPlanLines: string[] = [];\n for (const plan of plans) {\n const uniqueFeatures = plan.features\n .filter(\n (feature) =>\n feature.type === \"boolean\" &&\n feature.enabled &&\n !enabledOnAll.has(feature.code),\n )\n .map((feature) => feature.name);\n\n if (uniqueFeatures.length > 0) {\n perPlanLines.push(`- **${plan.name}**: ${uniqueFeatures.join(\", \")}`);\n }\n }\n\n const commonFeatures = plans[0]?.features\n .filter(\n (feature) => feature.type === \"boolean\" && enabledOnAll.has(feature.code),\n )\n .map((feature) => feature.name);\n\n const parts: string[] = [];\n\n if (perPlanLines.length > 0) {\n parts.push(perPlanLines.join(\"\\n\"));\n }\n\n if (commonFeatures && commonFeatures.length > 0) {\n parts.push(`All plans include: ${commonFeatures.join(\", \")}.`);\n }\n\n return parts.length > 0 ? `## Features\\n\\n${parts.join(\"\\n\\n\")}` : null;\n}\n\nfunction renderCreditPacks(packs: CreditPack[]): string {\n const lines = [\n \"| Pack | Credits | Price |\",\n \"| --- | --- | --- |\",\n ...packs.map(\n (pack) =>\n `| ${pack.name} | ${formatNumber(pack.credits)} credits | ${formatSettlementPrice(pack.price)} |`,\n ),\n ];\n\n return `## Credit Packs\\n\\n${lines.join(\"\\n\")}`;\n}\n\ninterface UsageFeatureColumn {\n code: string;\n name: string;\n unitName: string | null;\n}\n\nfunction collectUsageFeatures(plans: Plan[]): UsageFeatureColumn[] {\n const seen = new Map<string, UsageFeatureColumn>();\n for (const plan of plans) {\n for (const feature of plan.features) {\n if (\n (feature.type === \"metered\" || feature.type === \"seats\") &&\n !seen.has(feature.code) &&\n hasUsageData(feature)\n ) {\n seen.set(feature.code, {\n code: feature.code,\n name: feature.name,\n unitName: feature.unitName,\n });\n }\n }\n }\n return Array.from(seen.values());\n}\n\nfunction hasUsageData(feature: PlanFeature): boolean {\n return (\n feature.unlimited === true ||\n (feature.includedAmount !== undefined && feature.includedAmount > 0) ||\n (feature.overageEnabled === true && feature.overageUnitPrice !== undefined)\n );\n}\n\nfunction formatSettlementPrice(cents: number): string {\n if (cents === 0) return \"Free\";\n const dollars = cents / 100;\n const [whole, decimal] = dollars.toFixed(2).split(\".\");\n return `$${Number(whole).toLocaleString(\"en-US\")}.${decimal}`;\n}\n\nfunction formatRatePrice(rate: number): string {\n const dollars = rate / 10000;\n\n if (dollars === Math.floor(dollars)) {\n return `$${dollars.toFixed(2)}`;\n }\n\n const formatted = dollars.toFixed(4).replace(/0+$/, \"\");\n const decimalPlaces = formatted.split(\".\")[1]?.length ?? 0;\n if (decimalPlaces < 2) {\n return `$${dollars.toFixed(2)}`;\n }\n\n return `$${formatted}`;\n}\n\nfunction formatNumber(n: number): string {\n return n.toLocaleString(\"en-US\");\n}\n"],"mappings":"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","names":["NextResponse","Commet","NextResponse"]}
1
+ {"version":3,"sources":["../src/portal.ts","../src/pricing-markdown.ts","../src/webhooks.ts"],"sourcesContent":["import { Commet } from \"@commet/node\";\nimport { type NextRequest, NextResponse } from \"next/server\";\n\nexport interface CustomerPortalConfig {\n apiKey: string;\n getCustomerId: (req: NextRequest) => Promise<string | null>;\n environment?: \"sandbox\" | \"production\";\n onError?: (error: Error) => void;\n}\n\n/**\n * Creates Next.js route handler for Customer Portal access\n *\n * @example\n * ```typescript\n * // app/api/commet/portal/route.ts\n * import { CustomerPortal } from \"@commet/next\";\n * import { auth } from \"@/lib/auth\";\n *\n * export const GET = CustomerPortal({\n * apiKey: process.env.COMMET_API_KEY!,\n * getCustomerId: async (req) => {\n * const session = await auth.api.getSession({ headers: req.headers });\n * return session?.user.id ?? null;\n * },\n * });\n * ```\n *\n * @example\n * ```tsx\n * // Use in component\n * export function BillingButton() {\n * return (\n * <Button asChild>\n * <Link href=\"/api/commet/portal\">Manage Billing</Link>\n * </Button>\n * );\n * }\n * ```\n */\nexport const CustomerPortal = ({\n apiKey,\n environment = \"production\",\n getCustomerId,\n onError,\n}: CustomerPortalConfig) => {\n const commet = new Commet({\n apiKey,\n environment,\n });\n\n return async (req: NextRequest) => {\n try {\n // Get customer identifier from request\n const customerId = await getCustomerId(req);\n\n if (!customerId) {\n return NextResponse.json(\n { error: \"Customer not authenticated\" },\n { status: 401 },\n );\n }\n\n // Get portal URL from Commet\n const result = await commet.portal.getUrl({\n customerId,\n });\n\n if (!result.success || !result.data) {\n throw new Error(result.message || \"Failed to create portal session\");\n }\n\n // Redirect to customer portal\n return NextResponse.redirect(result.data.portalUrl);\n } catch (error) {\n const errorObj =\n error instanceof Error ? error : new Error(String(error));\n\n // Call custom error handler if provided\n onError?.(errorObj);\n\n // Log error\n console.error(\"[Commet Portal]\", errorObj);\n\n // Return error response\n return NextResponse.json(\n { error: \"Failed to access customer portal\" },\n { status: 500 },\n );\n }\n };\n};\n","import type {\n BillingInterval,\n CreditPack,\n Plan,\n PlanFeature,\n} from \"@commet/node\";\nimport { Commet } from \"@commet/node\";\nimport { type NextRequest, NextResponse } from \"next/server\";\n\nexport interface PricingMarkdownConfig {\n apiKey: string;\n title: string;\n description?: string;\n includeCreditPacks?: boolean;\n billingIntervals?: BillingInterval[];\n cacheMaxAge?: number;\n onError?: (error: Error) => void;\n}\n\nconst INTERVAL_SHORT: Record<BillingInterval, string> = {\n monthly: \"/mo\",\n quarterly: \"/qtr\",\n yearly: \"/yr\",\n};\n\nexport const PricingMarkdown = ({\n apiKey,\n title,\n description,\n includeCreditPacks = false,\n billingIntervals,\n cacheMaxAge = 3600,\n onError,\n}: PricingMarkdownConfig) => {\n const commet = new Commet({ apiKey });\n\n return async (_req: NextRequest) => {\n try {\n const plansResult = await commet.plans.list();\n\n if (!plansResult.success || !plansResult.data) {\n throw new Error(plansResult.message ?? \"Failed to fetch plans\");\n }\n\n const publicPlans = plansResult.data.filter((plan) => plan.isPublic);\n\n const creditPacks = includeCreditPacks\n ? await commet.creditPacks\n .list()\n .then((result) =>\n result.success && result.data ? result.data : [],\n )\n : [];\n\n const markdown = generateMarkdown(\n title,\n description,\n publicPlans,\n creditPacks,\n billingIntervals,\n );\n\n return new NextResponse(markdown, {\n status: 200,\n headers: {\n \"Content-Type\": \"text/markdown; charset=utf-8\",\n \"Cache-Control\": `public, s-maxage=${cacheMaxAge}, stale-while-revalidate=${cacheMaxAge * 2}`,\n },\n });\n } catch (error) {\n const errorObj =\n error instanceof Error ? error : new Error(String(error));\n\n onError?.(errorObj);\n console.error(\"[Commet PricingMarkdown]\", errorObj);\n\n return NextResponse.json(\n { error: \"Failed to generate pricing markdown\" },\n { status: 500 },\n );\n }\n };\n};\n\nfunction generateMarkdown(\n title: string,\n description: string | undefined,\n plans: Plan[],\n creditPacks: CreditPack[],\n billingIntervals?: BillingInterval[],\n): string {\n const sections: string[] = [`# ${title}`];\n\n if (description) {\n sections.push(description);\n }\n\n if (plans.length > 0) {\n sections.push(renderPlansTable(plans, billingIntervals));\n\n const featuresSection = renderFeatures(plans);\n if (featuresSection) {\n sections.push(featuresSection);\n }\n }\n\n if (creditPacks.length > 0) {\n sections.push(renderCreditPacks(creditPacks));\n }\n\n return sections.join(\"\\n\\n\");\n}\n\nfunction renderPlansTable(\n plans: Plan[],\n filterIntervals?: BillingInterval[],\n): string {\n const usageFeatures = collectUsageFeatures(plans);\n\n const headerCols = [\"Plan\", \"Price\"];\n for (const { name, unitName } of usageFeatures) {\n headerCols.push(`${name}/${unitName ?? \"unit\"}`);\n headerCols.push(\"Overage\");\n }\n\n const rows = plans.map((plan) => {\n const priceCell = renderPriceCell(plan, filterIntervals);\n const cols = [plan.name, priceCell];\n\n const featuresByCode = new Map(\n plan.features.map((feature) => [feature.code, feature]),\n );\n\n for (const { code } of usageFeatures) {\n const feature = featuresByCode.get(code);\n\n if (!feature) {\n cols.push(\"—\", \"—\");\n continue;\n }\n\n if (feature.unlimited) {\n cols.push(\"Unlimited\", \"—\");\n continue;\n }\n\n const included = feature.includedAmount;\n cols.push(included ? formatNumber(included) : \"—\");\n\n const overagePrice =\n feature.overageEnabled && feature.overageUnitPrice !== undefined\n ? formatRatePrice(feature.overageUnitPrice)\n : \"—\";\n cols.push(overagePrice);\n }\n\n return cols;\n });\n\n const separator = headerCols.map(() => \"---\");\n const lines = [\n `| ${headerCols.join(\" | \")} |`,\n `| ${separator.join(\" | \")} |`,\n ...rows.map((cols) => `| ${cols.join(\" | \")} |`),\n ];\n\n const footnotes: string[] = [];\n\n const trialPlans = plans.filter((plan) =>\n plan.prices.some((price) => price.trialDays > 0),\n );\n for (const plan of trialPlans) {\n const trialPrice = plan.prices.find((price) => price.trialDays > 0)!;\n footnotes.push(\n `*${plan.name} plan includes a ${trialPrice.trialDays}-day free trial.*`,\n );\n }\n\n if (usageFeatures.length > 0) {\n footnotes.push(\n \"The overage rate applies only to usage beyond the included volume.\",\n );\n }\n\n return footnotes.length > 0\n ? `## Plans\\n\\n${lines.join(\"\\n\")}\\n\\n${footnotes.join(\"\\n\\n\")}`\n : `## Plans\\n\\n${lines.join(\"\\n\")}`;\n}\n\nfunction renderPriceCell(\n plan: Plan,\n filterIntervals?: BillingInterval[],\n): string {\n if (plan.isFree) return \"Free\";\n\n const prices = filterIntervals\n ? plan.prices.filter((price) =>\n filterIntervals.includes(price.billingInterval),\n )\n : plan.prices;\n\n if (prices.length === 0) return \"—\";\n\n return prices\n .map((price) => {\n if (price.price === 0) return \"Free\";\n return `${formatSettlementPrice(price.price)}${INTERVAL_SHORT[price.billingInterval]}`;\n })\n .join(\" / \");\n}\n\nfunction renderFeatures(plans: Plan[]): string | null {\n const allBooleanCodes = new Set<string>();\n for (const plan of plans) {\n for (const feature of plan.features) {\n if (feature.type === \"boolean\") {\n allBooleanCodes.add(feature.code);\n }\n }\n }\n\n if (allBooleanCodes.size === 0) return null;\n\n const enabledOnAll = new Set<string>();\n for (const code of allBooleanCodes) {\n const enabledOnEveryPlan = plans.every((plan) => {\n const feature = plan.features.find((f) => f.code === code);\n return feature?.enabled;\n });\n if (enabledOnEveryPlan) {\n enabledOnAll.add(code);\n }\n }\n\n const perPlanLines: string[] = [];\n for (const plan of plans) {\n const uniqueFeatures = plan.features\n .filter(\n (feature) =>\n feature.type === \"boolean\" &&\n feature.enabled &&\n !enabledOnAll.has(feature.code),\n )\n .map((feature) => feature.name);\n\n if (uniqueFeatures.length > 0) {\n perPlanLines.push(`- **${plan.name}**: ${uniqueFeatures.join(\", \")}`);\n }\n }\n\n const commonFeatures = plans[0]?.features\n .filter(\n (feature) => feature.type === \"boolean\" && enabledOnAll.has(feature.code),\n )\n .map((feature) => feature.name);\n\n const parts: string[] = [];\n\n if (perPlanLines.length > 0) {\n parts.push(perPlanLines.join(\"\\n\"));\n }\n\n if (commonFeatures && commonFeatures.length > 0) {\n parts.push(`All plans include: ${commonFeatures.join(\", \")}.`);\n }\n\n return parts.length > 0 ? `## Features\\n\\n${parts.join(\"\\n\\n\")}` : null;\n}\n\nfunction renderCreditPacks(packs: CreditPack[]): string {\n const lines = [\n \"| Pack | Credits | Price |\",\n \"| --- | --- | --- |\",\n ...packs.map(\n (pack) =>\n `| ${pack.name} | ${formatNumber(pack.credits)} credits | ${formatSettlementPrice(pack.price)} |`,\n ),\n ];\n\n return `## Credit Packs\\n\\n${lines.join(\"\\n\")}`;\n}\n\ninterface UsageFeatureColumn {\n code: string;\n name: string;\n unitName: string | null;\n}\n\nfunction collectUsageFeatures(plans: Plan[]): UsageFeatureColumn[] {\n const seen = new Map<string, UsageFeatureColumn>();\n for (const plan of plans) {\n for (const feature of plan.features) {\n if (\n (feature.type === \"metered\" || feature.type === \"seats\") &&\n !seen.has(feature.code) &&\n hasUsageData(feature)\n ) {\n seen.set(feature.code, {\n code: feature.code,\n name: feature.name,\n unitName: feature.unitName,\n });\n }\n }\n }\n return Array.from(seen.values());\n}\n\nfunction hasUsageData(feature: PlanFeature): boolean {\n return (\n feature.unlimited === true ||\n (feature.includedAmount !== undefined && feature.includedAmount > 0) ||\n (feature.overageEnabled === true && feature.overageUnitPrice !== undefined)\n );\n}\n\nfunction formatSettlementPrice(cents: number): string {\n if (cents === 0) return \"Free\";\n const dollars = cents / 100;\n const [whole, decimal] = dollars.toFixed(2).split(\".\");\n return `$${Number(whole).toLocaleString(\"en-US\")}.${decimal}`;\n}\n\nfunction formatRatePrice(rate: number): string {\n const dollars = rate / 10000;\n\n if (dollars === Math.floor(dollars)) {\n return `$${dollars.toFixed(2)}`;\n }\n\n const formatted = dollars.toFixed(4).replace(/0+$/, \"\");\n const decimalPlaces = formatted.split(\".\")[1]?.length ?? 0;\n if (decimalPlaces < 2) {\n return `$${dollars.toFixed(2)}`;\n }\n\n return `$${formatted}`;\n}\n\nfunction formatNumber(n: number): string {\n return n.toLocaleString(\"en-US\");\n}\n","import type { WebhookPayload } from \"@commet/node\";\nimport { Webhooks as CommetWebhooks } from \"@commet/node\";\nimport { type NextRequest, NextResponse } from \"next/server\";\nimport type { WebhooksConfig } from \"./types\";\n\n/**\n * Create a Next.js webhook handler for Commet events\n *\n * Automatically verifies signatures, routes events to handlers, and returns proper responses.\n *\n * @param config - Webhook configuration with secret and event handlers\n * @returns Next.js route handler function\n *\n * @example\n * ```typescript\n * // app/api/webhooks/commet/route.ts\n * import { Webhooks } from \"@commet/next\";\n *\n * export const POST = Webhooks({\n * webhookSecret: process.env.COMMET_WEBHOOK_SECRET!,\n * onSubscriptionActivated: async (payload) => {\n * // Grant access to user\n * },\n * onSubscriptionCanceled: async (payload) => {\n * // Revoke access from user\n * },\n * });\n * ```\n */\nexport const Webhooks = (config: WebhooksConfig) => {\n const {\n webhookSecret,\n onSubscriptionActivated,\n onSubscriptionCanceled,\n onSubscriptionCreated,\n onSubscriptionUpdated,\n onPayload,\n onError,\n } = config;\n\n // Create webhook verifier instance\n const webhooks = new CommetWebhooks();\n\n return async (request: NextRequest) => {\n try {\n // 1. Read raw request body\n const rawBody = await request.text();\n\n // 2. Extract signature from headers\n const signature = request.headers.get(\"x-commet-signature\");\n\n // 3. Verify signature\n const isValid = webhooks.verify({\n payload: rawBody,\n signature,\n secret: webhookSecret,\n });\n\n if (!isValid) {\n console.error(\"[Commet Webhook] Invalid signature\");\n return NextResponse.json(\n { received: false, error: \"Invalid signature\" },\n { status: 403 },\n );\n }\n\n // 4. Parse payload\n let payload: WebhookPayload;\n try {\n payload = JSON.parse(rawBody) as WebhookPayload;\n } catch (parseError) {\n console.error(\"[Commet Webhook] Failed to parse payload:\", parseError);\n if (onError) {\n await onError(\n parseError instanceof Error\n ? parseError\n : new Error(\"Failed to parse webhook payload\"),\n rawBody,\n );\n }\n return NextResponse.json(\n { received: false, error: \"Invalid payload\" },\n { status: 400 },\n );\n }\n\n // 5. Collect promises for parallel execution\n const promises: Promise<void>[] = [];\n\n // Call catch-all handler if provided\n if (onPayload) {\n promises.push(onPayload(payload));\n }\n\n // 6. Route to specific event handler\n switch (payload.event) {\n case \"subscription.activated\":\n if (onSubscriptionActivated) {\n promises.push(onSubscriptionActivated(payload));\n }\n break;\n\n case \"subscription.canceled\":\n if (onSubscriptionCanceled) {\n promises.push(onSubscriptionCanceled(payload));\n }\n break;\n\n case \"subscription.created\":\n if (onSubscriptionCreated) {\n promises.push(onSubscriptionCreated(payload));\n }\n break;\n\n case \"subscription.updated\":\n if (onSubscriptionUpdated) {\n promises.push(onSubscriptionUpdated(payload));\n }\n break;\n\n default:\n console.log(`[Commet Webhook] Unhandled event: ${payload.event}`);\n }\n\n // 7. Execute all handlers in parallel\n try {\n await Promise.all(promises);\n } catch (handlerError) {\n console.error(\n \"[Commet Webhook] Handler error:\",\n handlerError instanceof Error ? handlerError.message : handlerError,\n );\n if (onError) {\n await onError(\n handlerError instanceof Error\n ? handlerError\n : new Error(\"Handler execution failed\"),\n payload,\n );\n }\n // Still return 200 to prevent retries for handler errors\n return NextResponse.json(\n { received: true, warning: \"Handler failed\" },\n { status: 200 },\n );\n }\n\n // 8. Success response\n return NextResponse.json({ received: true }, { status: 200 });\n } catch (error) {\n console.error(\"[Commet Webhook] Unexpected error:\", error);\n if (onError) {\n try {\n await onError(\n error instanceof Error ? error : new Error(\"Unexpected error\"),\n undefined,\n );\n } catch (errorHandlerError) {\n console.error(\n \"[Commet Webhook] Error handler failed:\",\n errorHandlerError,\n );\n }\n }\n return NextResponse.json(\n { received: false, error: \"Internal error\" },\n { status: 500 },\n );\n }\n };\n};\n"],"mappings":"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","names":["Commet","NextResponse","NextResponse"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commet/next",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "Next.js integration for Commet billing platform",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -26,11 +26,11 @@
26
26
  "author": "Commet Team",
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
- "@commet/node": "1.7.0"
29
+ "@commet/node": "1.8.0"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "24.10.1",
33
- "next": "16.1.6",
33
+ "next": "16.2.1",
34
34
  "tsup": "8.5.1",
35
35
  "typescript": "5.9.3",
36
36
  "vitest": "4.0.13"