@commet/better-auth 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1081 @@
1
+ // src/hooks/customer.ts
2
+ import { APIError } from "better-auth/api";
3
+ var onBeforeUserCreate = (options) => async (user, context) => {
4
+ if (!context || !options.createCustomerOnSignUp) {
5
+ return;
6
+ }
7
+ try {
8
+ const customParams = options.getCustomerCreateParams ? await options.getCustomerCreateParams({ user }, context.request) : {};
9
+ if (!user.email) {
10
+ throw new APIError("BAD_REQUEST", {
11
+ message: "An email is required to create a customer"
12
+ });
13
+ }
14
+ const existingCustomers = await options.client.customers.list({
15
+ search: user.email
16
+ });
17
+ const existingCustomer = existingCustomers.data?.find(
18
+ (c) => c.billingEmail === user.email
19
+ );
20
+ if (!existingCustomer) {
21
+ await options.client.customers.create({
22
+ email: user.email,
23
+ legalName: customParams.legalName ?? user.name,
24
+ displayName: customParams.displayName,
25
+ domain: customParams.domain,
26
+ metadata: customParams.metadata
27
+ });
28
+ }
29
+ } catch (e) {
30
+ if (e instanceof APIError) {
31
+ throw e;
32
+ }
33
+ if (e instanceof Error) {
34
+ throw new APIError("INTERNAL_SERVER_ERROR", {
35
+ message: `Commet customer creation failed: ${e.message}`
36
+ });
37
+ }
38
+ throw new APIError("INTERNAL_SERVER_ERROR", {
39
+ message: "Commet customer creation failed"
40
+ });
41
+ }
42
+ };
43
+ var onAfterUserCreate = (options) => async (user, context) => {
44
+ if (!context || !options.createCustomerOnSignUp) {
45
+ return;
46
+ }
47
+ try {
48
+ const existingCustomers = await options.client.customers.list({
49
+ search: user.email
50
+ });
51
+ const existingCustomer = existingCustomers.data?.find(
52
+ (c) => c.billingEmail === user.email
53
+ );
54
+ if (existingCustomer && existingCustomer.externalId !== user.id) {
55
+ await options.client.customers.update({
56
+ customerId: existingCustomer.id,
57
+ externalId: user.id
58
+ });
59
+ }
60
+ } catch (e) {
61
+ if (e instanceof Error) {
62
+ throw new APIError("INTERNAL_SERVER_ERROR", {
63
+ message: `Commet customer update failed: ${e.message}`
64
+ });
65
+ }
66
+ throw new APIError("INTERNAL_SERVER_ERROR", {
67
+ message: "Commet customer update failed"
68
+ });
69
+ }
70
+ };
71
+ var onUserUpdate = (options) => async (user, context) => {
72
+ if (!context || !options.createCustomerOnSignUp) {
73
+ return;
74
+ }
75
+ try {
76
+ const existingCustomers = await options.client.customers.list({
77
+ externalId: user.id
78
+ });
79
+ const existingCustomer = existingCustomers.data?.[0];
80
+ if (existingCustomer) {
81
+ await options.client.customers.update({
82
+ customerId: existingCustomer.id,
83
+ email: user.email,
84
+ legalName: user.name ?? void 0
85
+ });
86
+ }
87
+ } catch (e) {
88
+ if (e instanceof Error) {
89
+ context.context.logger.error(
90
+ `Commet customer update failed: ${e.message}`
91
+ );
92
+ } else {
93
+ context.context.logger.error("Commet customer update failed");
94
+ }
95
+ }
96
+ };
97
+ var onUserDelete = (options) => async (user, context) => {
98
+ if (!context || !options.createCustomerOnSignUp) {
99
+ return;
100
+ }
101
+ try {
102
+ const existingCustomers = await options.client.customers.list({
103
+ externalId: user.id
104
+ });
105
+ const existingCustomer = existingCustomers.data?.[0];
106
+ if (existingCustomer) {
107
+ await options.client.customers.archive(existingCustomer.id);
108
+ }
109
+ } catch (e) {
110
+ if (e instanceof Error) {
111
+ context?.context.logger.error(
112
+ `Commet customer archive failed: ${e.message}`
113
+ );
114
+ } else {
115
+ context?.context.logger.error("Commet customer archive failed");
116
+ }
117
+ }
118
+ };
119
+
120
+ // src/client.ts
121
+ var commetClient = () => {
122
+ return {
123
+ id: "commet-client",
124
+ $InferServerPlugin: {},
125
+ getActions: ($fetch) => {
126
+ return {
127
+ // Customer Portal
128
+ customer: {
129
+ /**
130
+ * Redirect to the Commet customer portal
131
+ */
132
+ portal: async (fetchOptions) => {
133
+ const res = await $fetch("/commet/portal", {
134
+ method: "GET",
135
+ ...fetchOptions
136
+ });
137
+ if (res.error) {
138
+ throw new Error(res.error.message);
139
+ }
140
+ const data = res.data;
141
+ if (data.redirect && typeof window !== "undefined") {
142
+ window.location.href = data.url;
143
+ }
144
+ return data;
145
+ }
146
+ },
147
+ // Subscription management
148
+ subscription: {
149
+ /**
150
+ * Get the current subscription for the authenticated user
151
+ */
152
+ get: async (fetchOptions) => {
153
+ return $fetch("/commet/subscription", {
154
+ method: "GET",
155
+ ...fetchOptions
156
+ });
157
+ },
158
+ /**
159
+ * Change the subscription plan (upgrade/downgrade)
160
+ */
161
+ changePlan: async (data, fetchOptions) => {
162
+ return $fetch("/commet/subscription/change-plan", {
163
+ method: "POST",
164
+ body: data,
165
+ ...fetchOptions
166
+ });
167
+ },
168
+ /**
169
+ * Cancel the subscription
170
+ */
171
+ cancel: async (data, fetchOptions) => {
172
+ return $fetch("/commet/subscription/cancel", {
173
+ method: "POST",
174
+ body: data ?? {},
175
+ ...fetchOptions
176
+ });
177
+ }
178
+ },
179
+ // Feature access
180
+ features: {
181
+ /**
182
+ * List all features for the authenticated user
183
+ */
184
+ list: async (fetchOptions) => {
185
+ return $fetch("/commet/features", {
186
+ method: "GET",
187
+ ...fetchOptions
188
+ });
189
+ },
190
+ /**
191
+ * Get a specific feature's access/usage
192
+ */
193
+ get: async (data, fetchOptions) => {
194
+ return $fetch(`/commet/features/${data.code}`, {
195
+ method: "GET",
196
+ ...fetchOptions
197
+ });
198
+ },
199
+ /**
200
+ * Check if a feature is enabled (boolean check)
201
+ */
202
+ check: async (data, fetchOptions) => {
203
+ return $fetch(`/features/${data.code}/check`, {
204
+ method: "GET",
205
+ ...fetchOptions
206
+ });
207
+ },
208
+ /**
209
+ * Check if user can use one more unit of a feature
210
+ * Returns { allowed: boolean, willBeCharged: boolean }
211
+ */
212
+ canUse: async (data, fetchOptions) => {
213
+ return $fetch(`/features/${data.code}/can-use`, {
214
+ method: "GET",
215
+ ...fetchOptions
216
+ });
217
+ }
218
+ },
219
+ // Usage tracking
220
+ usage: {
221
+ /**
222
+ * Track a usage event for the authenticated user
223
+ */
224
+ track: async (data, fetchOptions) => {
225
+ return $fetch("/commet/usage/track", {
226
+ method: "POST",
227
+ body: data,
228
+ ...fetchOptions
229
+ });
230
+ }
231
+ },
232
+ // Seat management
233
+ seats: {
234
+ /**
235
+ * List all seat balances for the authenticated user
236
+ */
237
+ list: async (fetchOptions) => {
238
+ return $fetch("/commet/seats", {
239
+ method: "GET",
240
+ ...fetchOptions
241
+ });
242
+ },
243
+ /**
244
+ * Add seats of a specific type
245
+ */
246
+ add: async (data, fetchOptions) => {
247
+ return $fetch("/commet/seats/add", {
248
+ method: "POST",
249
+ body: data,
250
+ ...fetchOptions
251
+ });
252
+ },
253
+ /**
254
+ * Remove seats of a specific type
255
+ */
256
+ remove: async (data, fetchOptions) => {
257
+ return $fetch("/commet/seats/remove", {
258
+ method: "POST",
259
+ body: data,
260
+ ...fetchOptions
261
+ });
262
+ },
263
+ /**
264
+ * Set seats to a specific count
265
+ */
266
+ set: async (data, fetchOptions) => {
267
+ return $fetch("/commet/seats/set", {
268
+ method: "POST",
269
+ body: data,
270
+ ...fetchOptions
271
+ });
272
+ },
273
+ /**
274
+ * Set all seat types at once
275
+ */
276
+ setAll: async (data, fetchOptions) => {
277
+ return $fetch("/commet/seats/set-all", {
278
+ method: "POST",
279
+ body: data,
280
+ ...fetchOptions
281
+ });
282
+ }
283
+ }
284
+ };
285
+ }
286
+ };
287
+ };
288
+
289
+ // src/plugins/portal.ts
290
+ import { APIError as APIError2, sessionMiddleware } from "better-auth/api";
291
+ import { createAuthEndpoint } from "better-auth/plugins";
292
+ var portal = ({ returnUrl } = {}) => (commet2) => {
293
+ return {
294
+ portal: createAuthEndpoint(
295
+ "/commet/portal",
296
+ {
297
+ method: "GET",
298
+ use: [sessionMiddleware]
299
+ },
300
+ async (ctx) => {
301
+ const userId = ctx.context.session?.user.id;
302
+ if (!userId) {
303
+ throw new APIError2("UNAUTHORIZED", {
304
+ message: "You must be logged in to access the customer portal"
305
+ });
306
+ }
307
+ try {
308
+ const portalAccess = await commet2.portal.getUrl({
309
+ externalId: userId
310
+ });
311
+ if (!portalAccess.success || !portalAccess.data) {
312
+ throw new APIError2("INTERNAL_SERVER_ERROR", {
313
+ message: portalAccess.message || "Failed to generate portal URL"
314
+ });
315
+ }
316
+ let portalUrl = portalAccess.data.portalUrl;
317
+ if (returnUrl) {
318
+ const url = new URL(portalUrl);
319
+ url.searchParams.set("return_url", returnUrl);
320
+ portalUrl = url.toString();
321
+ }
322
+ return ctx.json({
323
+ url: portalUrl,
324
+ redirect: true
325
+ });
326
+ } catch (e) {
327
+ if (e instanceof APIError2) {
328
+ throw e;
329
+ }
330
+ if (e instanceof Error) {
331
+ ctx.context.logger.error(
332
+ `Commet portal access failed: ${e.message}`
333
+ );
334
+ }
335
+ throw new APIError2("INTERNAL_SERVER_ERROR", {
336
+ message: "Failed to access customer portal"
337
+ });
338
+ }
339
+ }
340
+ )
341
+ };
342
+ };
343
+
344
+ // src/plugins/subscriptions.ts
345
+ import { APIError as APIError3, sessionMiddleware as sessionMiddleware2 } from "better-auth/api";
346
+ import { createAuthEndpoint as createAuthEndpoint2 } from "better-auth/plugins";
347
+ import { z } from "zod";
348
+ var ChangePlanSchema = z.object({
349
+ planId: z.string().optional(),
350
+ slug: z.string().optional(),
351
+ billingInterval: z.enum(["monthly", "quarterly", "yearly"]).optional()
352
+ });
353
+ var CancelSchema = z.object({
354
+ reason: z.string().optional(),
355
+ immediate: z.boolean().optional()
356
+ });
357
+ var subscriptions = (config = {}) => (commet2) => {
358
+ return {
359
+ getSubscription: createAuthEndpoint2(
360
+ "/commet/subscription",
361
+ {
362
+ method: "GET",
363
+ use: [sessionMiddleware2]
364
+ },
365
+ async (ctx) => {
366
+ const userId = ctx.context.session?.user.id;
367
+ if (!userId) {
368
+ throw new APIError3("UNAUTHORIZED", {
369
+ message: "You must be logged in to view subscription"
370
+ });
371
+ }
372
+ try {
373
+ const subscription = await commet2.subscriptions.get(userId);
374
+ if (!subscription.success) {
375
+ throw new APIError3("INTERNAL_SERVER_ERROR", {
376
+ message: subscription.message || "Failed to retrieve subscription"
377
+ });
378
+ }
379
+ return ctx.json(subscription.data ?? null);
380
+ } catch (e) {
381
+ if (e instanceof APIError3) {
382
+ throw e;
383
+ }
384
+ if (e instanceof Error) {
385
+ ctx.context.logger.error(
386
+ `Commet subscription get failed: ${e.message}`
387
+ );
388
+ }
389
+ throw new APIError3("INTERNAL_SERVER_ERROR", {
390
+ message: "Failed to retrieve subscription"
391
+ });
392
+ }
393
+ }
394
+ ),
395
+ changePlan: createAuthEndpoint2(
396
+ "/commet/subscription/change-plan",
397
+ {
398
+ method: "POST",
399
+ body: ChangePlanSchema,
400
+ use: [sessionMiddleware2]
401
+ },
402
+ async (ctx) => {
403
+ const userId = ctx.context.session?.user.id;
404
+ if (!userId) {
405
+ throw new APIError3("UNAUTHORIZED", {
406
+ message: "You must be logged in to change plan"
407
+ });
408
+ }
409
+ let planId = ctx.body.planId;
410
+ if (ctx.body.slug && !planId) {
411
+ const plan = config.plans?.find((p) => p.slug === ctx.body.slug);
412
+ if (!plan) {
413
+ throw new APIError3("BAD_REQUEST", {
414
+ message: `Plan with slug "${ctx.body.slug}" not found`
415
+ });
416
+ }
417
+ planId = plan.planId;
418
+ }
419
+ if (!planId) {
420
+ throw new APIError3("BAD_REQUEST", {
421
+ message: "Either planId or slug must be provided"
422
+ });
423
+ }
424
+ try {
425
+ const currentSub = await commet2.subscriptions.get(userId);
426
+ if (!currentSub.success || !currentSub.data) {
427
+ throw new APIError3("BAD_REQUEST", {
428
+ message: "No active subscription found"
429
+ });
430
+ }
431
+ const result = await commet2.subscriptions.changePlan({
432
+ subscriptionId: currentSub.data.id,
433
+ planId,
434
+ billingInterval: ctx.body.billingInterval
435
+ });
436
+ if (!result.success) {
437
+ throw new APIError3("INTERNAL_SERVER_ERROR", {
438
+ message: result.message || "Failed to change plan"
439
+ });
440
+ }
441
+ return ctx.json(result.data ?? null);
442
+ } catch (e) {
443
+ if (e instanceof APIError3) {
444
+ throw e;
445
+ }
446
+ if (e instanceof Error) {
447
+ ctx.context.logger.error(
448
+ `Commet plan change failed: ${e.message}`
449
+ );
450
+ }
451
+ throw new APIError3("INTERNAL_SERVER_ERROR", {
452
+ message: "Failed to change plan"
453
+ });
454
+ }
455
+ }
456
+ ),
457
+ cancelSubscription: createAuthEndpoint2(
458
+ "/commet/subscription/cancel",
459
+ {
460
+ method: "POST",
461
+ body: CancelSchema.optional(),
462
+ use: [sessionMiddleware2]
463
+ },
464
+ async (ctx) => {
465
+ const userId = ctx.context.session?.user.id;
466
+ if (!userId) {
467
+ throw new APIError3("UNAUTHORIZED", {
468
+ message: "You must be logged in to cancel subscription"
469
+ });
470
+ }
471
+ try {
472
+ const currentSub = await commet2.subscriptions.get(userId);
473
+ if (!currentSub.success || !currentSub.data) {
474
+ throw new APIError3("BAD_REQUEST", {
475
+ message: "No active subscription found"
476
+ });
477
+ }
478
+ const result = await commet2.subscriptions.cancel({
479
+ subscriptionId: currentSub.data.id,
480
+ reason: ctx.body?.reason,
481
+ immediate: ctx.body?.immediate
482
+ });
483
+ if (!result.success) {
484
+ throw new APIError3("INTERNAL_SERVER_ERROR", {
485
+ message: result.message || "Failed to cancel subscription"
486
+ });
487
+ }
488
+ return ctx.json(result.data ?? null);
489
+ } catch (e) {
490
+ if (e instanceof APIError3) {
491
+ throw e;
492
+ }
493
+ if (e instanceof Error) {
494
+ ctx.context.logger.error(
495
+ `Commet subscription cancel failed: ${e.message}`
496
+ );
497
+ }
498
+ throw new APIError3("INTERNAL_SERVER_ERROR", {
499
+ message: "Failed to cancel subscription"
500
+ });
501
+ }
502
+ }
503
+ )
504
+ };
505
+ };
506
+
507
+ // src/plugins/features.ts
508
+ import { APIError as APIError4, sessionMiddleware as sessionMiddleware3 } from "better-auth/api";
509
+ import { createAuthEndpoint as createAuthEndpoint3 } from "better-auth/plugins";
510
+ var features = (_config = {}) => (commet2) => {
511
+ return {
512
+ listFeatures: createAuthEndpoint3(
513
+ "/commet/features",
514
+ {
515
+ method: "GET",
516
+ use: [sessionMiddleware3]
517
+ },
518
+ async (ctx) => {
519
+ const userId = ctx.context.session?.user.id;
520
+ if (!userId) {
521
+ throw new APIError4("UNAUTHORIZED", {
522
+ message: "You must be logged in to view features"
523
+ });
524
+ }
525
+ try {
526
+ const result = await commet2.features.list(userId);
527
+ if (!result.success) {
528
+ throw new APIError4("INTERNAL_SERVER_ERROR", {
529
+ message: result.message || "Failed to list features"
530
+ });
531
+ }
532
+ return ctx.json(result.data ?? null);
533
+ } catch (e) {
534
+ if (e instanceof APIError4) {
535
+ throw e;
536
+ }
537
+ if (e instanceof Error) {
538
+ ctx.context.logger.error(
539
+ `Commet features list failed: ${e.message}`
540
+ );
541
+ }
542
+ throw new APIError4("INTERNAL_SERVER_ERROR", {
543
+ message: "Failed to list features"
544
+ });
545
+ }
546
+ }
547
+ ),
548
+ getFeature: createAuthEndpoint3(
549
+ "/commet/features/:code",
550
+ {
551
+ method: "GET",
552
+ use: [sessionMiddleware3]
553
+ },
554
+ async (ctx) => {
555
+ const userId = ctx.context.session?.user.id;
556
+ const code = ctx.params?.code;
557
+ if (!userId) {
558
+ throw new APIError4("UNAUTHORIZED", {
559
+ message: "You must be logged in to view feature"
560
+ });
561
+ }
562
+ if (!code) {
563
+ throw new APIError4("BAD_REQUEST", {
564
+ message: "Feature code is required"
565
+ });
566
+ }
567
+ try {
568
+ const result = await commet2.features.get({
569
+ externalId: userId,
570
+ code
571
+ });
572
+ if (!result.success) {
573
+ throw new APIError4("INTERNAL_SERVER_ERROR", {
574
+ message: result.message || "Failed to get feature"
575
+ });
576
+ }
577
+ return ctx.json(result.data ?? null);
578
+ } catch (e) {
579
+ if (e instanceof APIError4) {
580
+ throw e;
581
+ }
582
+ if (e instanceof Error) {
583
+ ctx.context.logger.error(
584
+ `Commet feature get failed: ${e.message}`
585
+ );
586
+ }
587
+ throw new APIError4("INTERNAL_SERVER_ERROR", {
588
+ message: "Failed to get feature"
589
+ });
590
+ }
591
+ }
592
+ ),
593
+ checkFeature: createAuthEndpoint3(
594
+ "/commet/features/:code/check",
595
+ {
596
+ method: "GET",
597
+ use: [sessionMiddleware3]
598
+ },
599
+ async (ctx) => {
600
+ const userId = ctx.context.session?.user.id;
601
+ const code = ctx.params?.code;
602
+ if (!userId) {
603
+ throw new APIError4("UNAUTHORIZED", {
604
+ message: "You must be logged in to check feature"
605
+ });
606
+ }
607
+ if (!code) {
608
+ throw new APIError4("BAD_REQUEST", {
609
+ message: "Feature code is required"
610
+ });
611
+ }
612
+ try {
613
+ const result = await commet2.features.check({
614
+ externalId: userId,
615
+ code
616
+ });
617
+ if (!result.success) {
618
+ throw new APIError4("INTERNAL_SERVER_ERROR", {
619
+ message: result.message || "Failed to check feature"
620
+ });
621
+ }
622
+ return ctx.json(result.data ?? null);
623
+ } catch (e) {
624
+ if (e instanceof APIError4) {
625
+ throw e;
626
+ }
627
+ if (e instanceof Error) {
628
+ ctx.context.logger.error(
629
+ `Commet feature check failed: ${e.message}`
630
+ );
631
+ }
632
+ throw new APIError4("INTERNAL_SERVER_ERROR", {
633
+ message: "Failed to check feature"
634
+ });
635
+ }
636
+ }
637
+ ),
638
+ canUseFeature: createAuthEndpoint3(
639
+ "/commet/features/:code/can-use",
640
+ {
641
+ method: "GET",
642
+ use: [sessionMiddleware3]
643
+ },
644
+ async (ctx) => {
645
+ const userId = ctx.context.session?.user.id;
646
+ const code = ctx.params?.code;
647
+ if (!userId) {
648
+ throw new APIError4("UNAUTHORIZED", {
649
+ message: "You must be logged in to check feature usage"
650
+ });
651
+ }
652
+ if (!code) {
653
+ throw new APIError4("BAD_REQUEST", {
654
+ message: "Feature code is required"
655
+ });
656
+ }
657
+ try {
658
+ const result = await commet2.features.canUse({
659
+ externalId: userId,
660
+ code
661
+ });
662
+ if (!result.success) {
663
+ throw new APIError4("INTERNAL_SERVER_ERROR", {
664
+ message: result.message || "Failed to check feature usage"
665
+ });
666
+ }
667
+ return ctx.json(result.data ?? null);
668
+ } catch (e) {
669
+ if (e instanceof APIError4) {
670
+ throw e;
671
+ }
672
+ if (e instanceof Error) {
673
+ ctx.context.logger.error(
674
+ `Commet feature canUse failed: ${e.message}`
675
+ );
676
+ }
677
+ throw new APIError4("INTERNAL_SERVER_ERROR", {
678
+ message: "Failed to check feature usage"
679
+ });
680
+ }
681
+ }
682
+ )
683
+ };
684
+ };
685
+
686
+ // src/plugins/usage.ts
687
+ import { APIError as APIError5, sessionMiddleware as sessionMiddleware4 } from "better-auth/api";
688
+ import { createAuthEndpoint as createAuthEndpoint4 } from "better-auth/plugins";
689
+ import { z as z2 } from "zod";
690
+ var TrackEventSchema = z2.object({
691
+ eventType: z2.string(),
692
+ value: z2.number().optional(),
693
+ idempotencyKey: z2.string().optional(),
694
+ properties: z2.record(z2.string(), z2.string()).optional()
695
+ });
696
+ var usage = (_config = {}) => (commet2) => {
697
+ return {
698
+ trackUsage: createAuthEndpoint4(
699
+ "/commet/usage/track",
700
+ {
701
+ method: "POST",
702
+ body: TrackEventSchema,
703
+ use: [sessionMiddleware4]
704
+ },
705
+ async (ctx) => {
706
+ const userId = ctx.context.session?.user.id;
707
+ if (!userId) {
708
+ throw new APIError5("UNAUTHORIZED", {
709
+ message: "You must be logged in to track usage"
710
+ });
711
+ }
712
+ try {
713
+ const result = await commet2.usage.track(
714
+ {
715
+ externalId: userId,
716
+ eventType: ctx.body.eventType,
717
+ idempotencyKey: ctx.body.idempotencyKey,
718
+ properties: ctx.body.properties
719
+ },
720
+ {}
721
+ );
722
+ if (!result.success) {
723
+ throw new APIError5("INTERNAL_SERVER_ERROR", {
724
+ message: result.message || "Failed to track usage"
725
+ });
726
+ }
727
+ return ctx.json(result.data ?? null);
728
+ } catch (e) {
729
+ if (e instanceof APIError5) {
730
+ throw e;
731
+ }
732
+ if (e instanceof Error) {
733
+ ctx.context.logger.error(
734
+ `Commet usage track failed: ${e.message}`
735
+ );
736
+ }
737
+ throw new APIError5("INTERNAL_SERVER_ERROR", {
738
+ message: "Failed to track usage"
739
+ });
740
+ }
741
+ }
742
+ )
743
+ };
744
+ };
745
+
746
+ // src/plugins/seats.ts
747
+ import { APIError as APIError6, sessionMiddleware as sessionMiddleware5 } from "better-auth/api";
748
+ import { createAuthEndpoint as createAuthEndpoint5 } from "better-auth/plugins";
749
+ import { z as z3 } from "zod";
750
+ var SeatOperationSchema = z3.object({
751
+ seatType: z3.string(),
752
+ count: z3.number().min(1)
753
+ });
754
+ var SetAllSeatsSchema = z3.object({
755
+ seats: z3.record(z3.string(), z3.number())
756
+ });
757
+ var seats = (_config = {}) => (commet2) => {
758
+ return {
759
+ listSeats: createAuthEndpoint5(
760
+ "/commet/seats",
761
+ {
762
+ method: "GET",
763
+ use: [sessionMiddleware5]
764
+ },
765
+ async (ctx) => {
766
+ const userId = ctx.context.session?.user.id;
767
+ if (!userId) {
768
+ throw new APIError6("UNAUTHORIZED", {
769
+ message: "You must be logged in to view seats"
770
+ });
771
+ }
772
+ try {
773
+ const result = await commet2.seats.getAllBalances({
774
+ externalId: userId
775
+ });
776
+ if (!result.success) {
777
+ throw new APIError6("INTERNAL_SERVER_ERROR", {
778
+ message: result.message || "Failed to list seats"
779
+ });
780
+ }
781
+ return ctx.json(result.data ?? null);
782
+ } catch (e) {
783
+ if (e instanceof APIError6) {
784
+ throw e;
785
+ }
786
+ if (e instanceof Error) {
787
+ ctx.context.logger.error(
788
+ `Commet seats list failed: ${e.message}`
789
+ );
790
+ }
791
+ throw new APIError6("INTERNAL_SERVER_ERROR", {
792
+ message: "Failed to list seats"
793
+ });
794
+ }
795
+ }
796
+ ),
797
+ addSeats: createAuthEndpoint5(
798
+ "/commet/seats/add",
799
+ {
800
+ method: "POST",
801
+ body: SeatOperationSchema,
802
+ use: [sessionMiddleware5]
803
+ },
804
+ async (ctx) => {
805
+ const userId = ctx.context.session?.user.id;
806
+ if (!userId) {
807
+ throw new APIError6("UNAUTHORIZED", {
808
+ message: "You must be logged in to add seats"
809
+ });
810
+ }
811
+ try {
812
+ const result = await commet2.seats.add(
813
+ {
814
+ externalId: userId,
815
+ seatType: ctx.body.seatType,
816
+ count: ctx.body.count
817
+ },
818
+ {}
819
+ );
820
+ if (!result.success) {
821
+ throw new APIError6("INTERNAL_SERVER_ERROR", {
822
+ message: result.message || "Failed to add seats"
823
+ });
824
+ }
825
+ return ctx.json(result.data ?? null);
826
+ } catch (e) {
827
+ if (e instanceof APIError6) {
828
+ throw e;
829
+ }
830
+ if (e instanceof Error) {
831
+ ctx.context.logger.error(`Commet seats add failed: ${e.message}`);
832
+ }
833
+ throw new APIError6("INTERNAL_SERVER_ERROR", {
834
+ message: "Failed to add seats"
835
+ });
836
+ }
837
+ }
838
+ ),
839
+ removeSeats: createAuthEndpoint5(
840
+ "/commet/seats/remove",
841
+ {
842
+ method: "POST",
843
+ body: SeatOperationSchema,
844
+ use: [sessionMiddleware5]
845
+ },
846
+ async (ctx) => {
847
+ const userId = ctx.context.session?.user.id;
848
+ if (!userId) {
849
+ throw new APIError6("UNAUTHORIZED", {
850
+ message: "You must be logged in to remove seats"
851
+ });
852
+ }
853
+ try {
854
+ const result = await commet2.seats.remove(
855
+ {
856
+ externalId: userId,
857
+ seatType: ctx.body.seatType,
858
+ count: ctx.body.count
859
+ },
860
+ {}
861
+ );
862
+ if (!result.success) {
863
+ throw new APIError6("INTERNAL_SERVER_ERROR", {
864
+ message: result.message || "Failed to remove seats"
865
+ });
866
+ }
867
+ return ctx.json(result.data ?? null);
868
+ } catch (e) {
869
+ if (e instanceof APIError6) {
870
+ throw e;
871
+ }
872
+ if (e instanceof Error) {
873
+ ctx.context.logger.error(
874
+ `Commet seats remove failed: ${e.message}`
875
+ );
876
+ }
877
+ throw new APIError6("INTERNAL_SERVER_ERROR", {
878
+ message: "Failed to remove seats"
879
+ });
880
+ }
881
+ }
882
+ ),
883
+ setSeats: createAuthEndpoint5(
884
+ "/commet/seats/set",
885
+ {
886
+ method: "POST",
887
+ body: SeatOperationSchema,
888
+ use: [sessionMiddleware5]
889
+ },
890
+ async (ctx) => {
891
+ const userId = ctx.context.session?.user.id;
892
+ if (!userId) {
893
+ throw new APIError6("UNAUTHORIZED", {
894
+ message: "You must be logged in to set seats"
895
+ });
896
+ }
897
+ try {
898
+ const result = await commet2.seats.set(
899
+ {
900
+ externalId: userId,
901
+ seatType: ctx.body.seatType,
902
+ count: ctx.body.count
903
+ },
904
+ {}
905
+ );
906
+ if (!result.success) {
907
+ throw new APIError6("INTERNAL_SERVER_ERROR", {
908
+ message: result.message || "Failed to set seats"
909
+ });
910
+ }
911
+ return ctx.json(result.data ?? null);
912
+ } catch (e) {
913
+ if (e instanceof APIError6) {
914
+ throw e;
915
+ }
916
+ if (e instanceof Error) {
917
+ ctx.context.logger.error(`Commet seats set failed: ${e.message}`);
918
+ }
919
+ throw new APIError6("INTERNAL_SERVER_ERROR", {
920
+ message: "Failed to set seats"
921
+ });
922
+ }
923
+ }
924
+ ),
925
+ setAllSeats: createAuthEndpoint5(
926
+ "/commet/seats/set-all",
927
+ {
928
+ method: "POST",
929
+ body: SetAllSeatsSchema,
930
+ use: [sessionMiddleware5]
931
+ },
932
+ async (ctx) => {
933
+ const userId = ctx.context.session?.user.id;
934
+ if (!userId) {
935
+ throw new APIError6("UNAUTHORIZED", {
936
+ message: "You must be logged in to set seats"
937
+ });
938
+ }
939
+ try {
940
+ const result = await commet2.seats.setAll(
941
+ {
942
+ externalId: userId,
943
+ seats: ctx.body.seats
944
+ },
945
+ {}
946
+ );
947
+ if (!result.success) {
948
+ throw new APIError6("INTERNAL_SERVER_ERROR", {
949
+ message: result.message || "Failed to set all seats"
950
+ });
951
+ }
952
+ return ctx.json(result.data ?? null);
953
+ } catch (e) {
954
+ if (e instanceof APIError6) {
955
+ throw e;
956
+ }
957
+ if (e instanceof Error) {
958
+ ctx.context.logger.error(
959
+ `Commet seats set-all failed: ${e.message}`
960
+ );
961
+ }
962
+ throw new APIError6("INTERNAL_SERVER_ERROR", {
963
+ message: "Failed to set all seats"
964
+ });
965
+ }
966
+ }
967
+ )
968
+ };
969
+ };
970
+
971
+ // src/plugins/webhooks.ts
972
+ import { APIError as APIError7, createAuthEndpoint as createAuthEndpoint6 } from "better-auth/api";
973
+ var EVENT_HANDLER_MAP = {
974
+ "subscription.created": "onSubscriptionCreated",
975
+ "subscription.activated": "onSubscriptionActivated",
976
+ "subscription.canceled": "onSubscriptionCanceled",
977
+ "subscription.updated": "onSubscriptionUpdated"
978
+ };
979
+ var webhooks = (config) => (commet2) => {
980
+ return {
981
+ commetWebhooks: createAuthEndpoint6(
982
+ "/commet/webhooks",
983
+ {
984
+ method: "POST",
985
+ metadata: {
986
+ isAction: false
987
+ },
988
+ cloneRequest: true
989
+ },
990
+ async (ctx) => {
991
+ if (!ctx.request?.body) {
992
+ throw new APIError7("BAD_REQUEST", {
993
+ message: "Request body is required"
994
+ });
995
+ }
996
+ const rawBody = await ctx.request.text();
997
+ const signature = ctx.request.headers.get("x-commet-signature");
998
+ const payload = commet2.webhooks.verifyAndParse({
999
+ rawBody,
1000
+ signature,
1001
+ secret: config.secret
1002
+ });
1003
+ if (!payload) {
1004
+ ctx.context.logger.error("Invalid webhook signature");
1005
+ throw new APIError7("UNAUTHORIZED", {
1006
+ message: "Invalid webhook signature"
1007
+ });
1008
+ }
1009
+ try {
1010
+ const handlerKey = EVENT_HANDLER_MAP[payload.event];
1011
+ if (handlerKey) {
1012
+ const handler = config[handlerKey];
1013
+ if (handler) {
1014
+ await handler(payload);
1015
+ }
1016
+ }
1017
+ if (config.onPayload) {
1018
+ await config.onPayload(payload);
1019
+ }
1020
+ return ctx.json({ received: true });
1021
+ } catch (e) {
1022
+ if (e instanceof Error) {
1023
+ ctx.context.logger.error(
1024
+ `Commet webhook handler error: ${e.message}`
1025
+ );
1026
+ } else {
1027
+ ctx.context.logger.error("Commet webhook handler error");
1028
+ }
1029
+ throw new APIError7("INTERNAL_SERVER_ERROR", {
1030
+ message: "Webhook handler error"
1031
+ });
1032
+ }
1033
+ }
1034
+ )
1035
+ };
1036
+ };
1037
+
1038
+ // src/index.ts
1039
+ var commet = (options) => {
1040
+ const plugins = options.use.map((use) => use(options.client)).reduce((acc, plugin) => {
1041
+ Object.assign(acc, plugin);
1042
+ return acc;
1043
+ }, {});
1044
+ return {
1045
+ id: "commet",
1046
+ endpoints: {
1047
+ ...plugins
1048
+ },
1049
+ init() {
1050
+ return {
1051
+ options: {
1052
+ databaseHooks: {
1053
+ user: {
1054
+ create: {
1055
+ before: onBeforeUserCreate(options),
1056
+ after: onAfterUserCreate(options)
1057
+ },
1058
+ update: {
1059
+ after: onUserUpdate(options)
1060
+ },
1061
+ delete: {
1062
+ after: onUserDelete(options)
1063
+ }
1064
+ }
1065
+ }
1066
+ }
1067
+ };
1068
+ }
1069
+ };
1070
+ };
1071
+ export {
1072
+ commet,
1073
+ commetClient,
1074
+ features,
1075
+ portal,
1076
+ seats,
1077
+ subscriptions,
1078
+ usage,
1079
+ webhooks
1080
+ };
1081
+ //# sourceMappingURL=index.js.map