@commet/node 4.5.0 → 5.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.mjs CHANGED
@@ -1,54 +1,50 @@
1
- // src/customer.ts
2
- var CustomerContext = class {
3
- constructor(customerId, resources) {
4
- this.features = {
5
- get: (code, options) => this.featuresResource.get({ code, customerId: this.customerId }, options),
6
- check: (code, options) => this.featuresResource.check(
7
- { code, customerId: this.customerId },
8
- options
9
- ),
10
- canUse: (code, options) => this.featuresResource.canUse(
11
- { code, customerId: this.customerId },
12
- options
13
- ),
14
- list: (options) => this.featuresResource.list(this.customerId, options)
15
- };
16
- this.seats = {
17
- add: (featureCode, count = 1, options) => this.seatsResource.add(
18
- { customerId: this.customerId, featureCode, count },
19
- options
20
- ),
21
- remove: (featureCode, count = 1, options) => this.seatsResource.remove(
22
- { customerId: this.customerId, featureCode, count },
23
- options
24
- ),
25
- set: (featureCode, count, options) => this.seatsResource.set(
26
- { customerId: this.customerId, featureCode, count },
27
- options
28
- ),
29
- getBalance: (featureCode) => this.seatsResource.getBalance({
30
- customerId: this.customerId,
31
- featureCode
32
- })
33
- };
34
- this.usage = {
35
- track: (feature, value, properties, options) => this.usageResource.track(
36
- { customerId: this.customerId, feature, value, properties },
37
- options
38
- )
39
- };
40
- this.subscription = {
41
- get: () => this.subscriptionsResource.get(this.customerId)
42
- };
43
- this.portal = {
44
- getUrl: (options) => this.portalResource.getUrl({ customerId: this.customerId }, options)
45
- };
46
- this.customerId = customerId;
47
- this.featuresResource = resources.features;
48
- this.seatsResource = resources.seats;
49
- this.usageResource = resources.usage;
50
- this.subscriptionsResource = resources.subscriptions;
51
- this.portalResource = resources.portal;
1
+ // src/resources/addons.ts
2
+ var AddonsResource = class {
3
+ constructor(httpClient) {
4
+ this.httpClient = httpClient;
5
+ }
6
+ async listActive(params, options) {
7
+ return this.httpClient.get(
8
+ "/addons/active",
9
+ { customerId: params.customerId },
10
+ options
11
+ );
12
+ }
13
+ async list(params, options) {
14
+ return this.httpClient.get("/addons", params, options);
15
+ }
16
+ async get(params, options) {
17
+ const { id } = params;
18
+ return this.httpClient.get(`/addons/${id}`, void 0, options);
19
+ }
20
+ async create(params, options) {
21
+ return this.httpClient.post("/addons", params, options);
22
+ }
23
+ async update(params, options) {
24
+ const { id, ...body } = params;
25
+ return this.httpClient.put(`/addons/${id}`, body, options);
26
+ }
27
+ /** Fails if addon has active subscriptions. */
28
+ async delete(params, options) {
29
+ const { id } = params;
30
+ return this.httpClient.delete(`/addons/${id}`, void 0, options);
31
+ }
32
+ };
33
+
34
+ // src/resources/api-keys.ts
35
+ var ApiKeysResource = class {
36
+ constructor(httpClient) {
37
+ this.httpClient = httpClient;
38
+ }
39
+ async list(params) {
40
+ return this.httpClient.get("/api-keys", params);
41
+ }
42
+ /** Response includes full `apiKey` which is only returned once. */
43
+ async create(params, options) {
44
+ return this.httpClient.post("/api-keys", params, options);
45
+ }
46
+ async delete(params, options) {
47
+ return this.httpClient.delete(`/api-keys/${params.id}`, void 0, options);
52
48
  }
53
49
  };
54
50
 
@@ -57,18 +53,20 @@ var CreditPacksResource = class {
57
53
  constructor(httpClient) {
58
54
  this.httpClient = httpClient;
59
55
  }
60
- /**
61
- * List all active credit packs
62
- *
63
- * @example
64
- * ```typescript
65
- * const packs = await commet.creditPacks.list();
66
- * console.log(packs.data); // [{ id: "cp_xxx", name: "100 Credits", ... }]
67
- * ```
68
- */
69
56
  async list() {
70
57
  return this.httpClient.get("/credit-packs");
71
58
  }
59
+ async create(params, options) {
60
+ return this.httpClient.post("/credit-packs/manage", params, options);
61
+ }
62
+ async update(params, options) {
63
+ const { id, ...body } = params;
64
+ return this.httpClient.put(`/credit-packs/${id}`, body, options);
65
+ }
66
+ async delete(params, options) {
67
+ const { id } = params;
68
+ return this.httpClient.delete(`/credit-packs/${id}`, void 0, options);
69
+ }
72
70
  };
73
71
 
74
72
  // src/resources/customers.ts
@@ -76,15 +74,13 @@ var CustomersResource = class {
76
74
  constructor(httpClient) {
77
75
  this.httpClient = httpClient;
78
76
  }
79
- /**
80
- * Create a customer (idempotent when id is provided)
81
- */
77
+ /** Idempotent when `id` is provided — returns existing customer instead of duplicating. */
82
78
  async create(params, options) {
83
79
  return this.httpClient.post(
84
80
  "/customers",
85
81
  {
86
82
  billingEmail: params.email,
87
- externalId: params.id,
83
+ id: params.id,
88
84
  fullName: params.fullName,
89
85
  domain: params.domain,
90
86
  website: params.website,
@@ -97,13 +93,10 @@ var CustomersResource = class {
97
93
  options
98
94
  );
99
95
  }
100
- /**
101
- * Create multiple customers in batch
102
- */
103
96
  async createBatch(params, options) {
104
97
  const customers = params.customers.map((c) => ({
105
98
  billingEmail: c.email,
106
- externalId: c.id,
99
+ id: c.id,
107
100
  fullName: c.fullName,
108
101
  domain: c.domain,
109
102
  website: c.website,
@@ -115,18 +108,12 @@ var CustomersResource = class {
115
108
  }));
116
109
  return this.httpClient.post("/customers/batch", { customers }, options);
117
110
  }
118
- /**
119
- * Get a customer by ID
120
- */
121
- async get(customerId) {
122
- return this.httpClient.get(`/customers/${customerId}`);
111
+ async get(params) {
112
+ return this.httpClient.get(`/customers/${params.id}`);
123
113
  }
124
- /**
125
- * Update a customer
126
- */
127
114
  async update(params, options) {
128
115
  return this.httpClient.put(
129
- `/customers/${params.customerId}`,
116
+ `/customers/${params.id}`,
130
117
  {
131
118
  billingEmail: params.email,
132
119
  fullName: params.fullName,
@@ -141,9 +128,6 @@ var CustomersResource = class {
141
128
  options
142
129
  );
143
130
  }
144
- /**
145
- * List customers with optional filters
146
- */
147
131
  async list(params) {
148
132
  return this.httpClient.get("/customers", params);
149
133
  }
@@ -161,34 +145,113 @@ var FeaturesResource = class {
161
145
  options
162
146
  );
163
147
  }
164
- async check(params, options) {
165
- const result = await this.httpClient.get(
148
+ /** Checks if the customer can consume one more unit — returns billing impact and reason if blocked. */
149
+ async canUse(params, options) {
150
+ return this.httpClient.get(
166
151
  `/features/${params.code}`,
167
- { customerId: params.customerId },
152
+ { customerId: params.customerId, action: "canUse" },
168
153
  options
169
154
  );
170
- if (!result.success || !result.data) {
171
- return {
172
- success: false,
173
- code: result.code,
174
- message: result.message,
175
- details: result.details
176
- };
177
- }
178
- return {
179
- success: true,
180
- data: { allowed: result.data.allowed }
181
- };
182
155
  }
183
- async canUse(params, options) {
156
+ async list(params, options) {
184
157
  return this.httpClient.get(
185
- `/features/${params.code}`,
186
- { customerId: params.customerId, action: "canUse" },
158
+ "/features",
159
+ { customerId: params.customerId },
160
+ options
161
+ );
162
+ }
163
+ async create(params, options) {
164
+ return this.httpClient.post("/features/manage", params, options);
165
+ }
166
+ async update(params, options) {
167
+ const { code, ...body } = params;
168
+ return this.httpClient.put(`/features/${code}/manage`, body, options);
169
+ }
170
+ /** Fails if feature is attached to active plans or has an active addon. */
171
+ async delete(params, options) {
172
+ const { code } = params;
173
+ return this.httpClient.delete(
174
+ `/features/${code}/manage`,
175
+ void 0,
187
176
  options
188
177
  );
189
178
  }
190
- async list(customerId, options) {
191
- return this.httpClient.get("/features", { customerId }, options);
179
+ };
180
+
181
+ // src/resources/invoices.ts
182
+ var InvoicesResource = class {
183
+ constructor(httpClient) {
184
+ this.httpClient = httpClient;
185
+ }
186
+ async list(params) {
187
+ return this.httpClient.get("/invoices", params);
188
+ }
189
+ async get(params) {
190
+ return this.httpClient.get(`/invoices/${params.id}`);
191
+ }
192
+ /** Negative amount creates a credit. */
193
+ async createAdjustment(params, options) {
194
+ return this.httpClient.post("/invoices", params, options);
195
+ }
196
+ /** Signed URL, expires after 7 days. */
197
+ async getDownloadUrl(params) {
198
+ return this.httpClient.get(`/invoices/${params.id}/download`);
199
+ }
200
+ async send(params, options) {
201
+ return this.httpClient.post(`/invoices/${params.id}/send`, {}, options);
202
+ }
203
+ /** Only outstanding invoices can be changed to paid or void. */
204
+ async updateStatus(params, options) {
205
+ const { id, ...body } = params;
206
+ return this.httpClient.put(`/invoices/${id}/status`, body, options);
207
+ }
208
+ };
209
+
210
+ // src/resources/plan-groups.ts
211
+ var PlanGroupsResource = class {
212
+ constructor(httpClient) {
213
+ this.httpClient = httpClient;
214
+ }
215
+ async list(params) {
216
+ return this.httpClient.get("/plan-groups", params);
217
+ }
218
+ async get(params) {
219
+ return this.httpClient.get(`/plan-groups/${params.id}`);
220
+ }
221
+ async create(params, options) {
222
+ return this.httpClient.post("/plan-groups", params, options);
223
+ }
224
+ async update(params, options) {
225
+ const { id, ...body } = params;
226
+ return this.httpClient.put(`/plan-groups/${id}`, body, options);
227
+ }
228
+ /** Plans in the group are unlinked, not deleted. */
229
+ async delete(params, options) {
230
+ return this.httpClient.delete(
231
+ `/plan-groups/${params.id}`,
232
+ void 0,
233
+ options
234
+ );
235
+ }
236
+ async addPlan(params, options) {
237
+ const { id, ...body } = params;
238
+ return this.httpClient.post(`/plan-groups/${id}/plans`, body, options);
239
+ }
240
+ async removePlan(params, options) {
241
+ const { id, planId } = params;
242
+ return this.httpClient.delete(
243
+ `/plan-groups/${id}/plans/${planId}`,
244
+ void 0,
245
+ options
246
+ );
247
+ }
248
+ async reorderPlans(params, options) {
249
+ const { id, ...body } = params;
250
+ return this.httpClient.put(
251
+ `/plan-groups/${id}/plans/reorder`,
252
+ body,
253
+ options
254
+ );
192
255
  }
193
256
  };
194
257
 
@@ -197,33 +260,93 @@ var PlansResource = class {
197
260
  constructor(httpClient) {
198
261
  this.httpClient = httpClient;
199
262
  }
200
- /**
201
- * List all available plans
202
- *
203
- * @example
204
- * ```typescript
205
- * // List public plans
206
- * const plans = await commet.plans.list();
207
- *
208
- * // Include private plans
209
- * const allPlans = await commet.plans.list({ includePrivate: true });
210
- * ```
211
- */
212
263
  async list(params) {
213
264
  return this.httpClient.get("/plans", params);
214
265
  }
215
- /**
216
- * Get a specific plan by code
217
- *
218
- * @example
219
- * ```typescript
220
- * const plan = await commet.plans.get('pro');
221
- * console.log(plan.data.name); // "Pro"
222
- * console.log(plan.data.prices); // [{ billingInterval: 'monthly', price: 9900 }]
223
- * ```
224
- */
225
- async get(planCode) {
226
- return this.httpClient.get(`/plans/${planCode}`);
266
+ async get(params) {
267
+ return this.httpClient.get(`/plans/${params.id}`);
268
+ }
269
+ async create(params, options) {
270
+ return this.httpClient.post("/plans/manage", params, options);
271
+ }
272
+ async update(params, options) {
273
+ const { id, ...body } = params;
274
+ return this.httpClient.put(`/plans/${id}/manage`, body, options);
275
+ }
276
+ async delete(params, options) {
277
+ return this.httpClient.delete(
278
+ `/plans/${params.id}/manage`,
279
+ void 0,
280
+ options
281
+ );
282
+ }
283
+ async setVisibility(params, options) {
284
+ const { id, ...body } = params;
285
+ return this.httpClient.put(`/plans/${id}/visibility`, body, options);
286
+ }
287
+ async addFeature(params, options) {
288
+ const { planId, ...body } = params;
289
+ return this.httpClient.post(`/plans/${planId}/features`, body, options);
290
+ }
291
+ async updateFeature(params, options) {
292
+ const { planId, featureId, ...body } = params;
293
+ return this.httpClient.put(
294
+ `/plans/${planId}/features/${featureId}`,
295
+ body,
296
+ options
297
+ );
298
+ }
299
+ async removeFeature(params, options) {
300
+ const { planId, featureId } = params;
301
+ return this.httpClient.delete(
302
+ `/plans/${planId}/features/${featureId}`,
303
+ void 0,
304
+ options
305
+ );
306
+ }
307
+ async addPrice(params, options) {
308
+ const { planId, ...body } = params;
309
+ return this.httpClient.post(`/plans/${planId}/prices`, body, options);
310
+ }
311
+ async updatePrice(params, options) {
312
+ const { planId, priceId, ...body } = params;
313
+ return this.httpClient.put(
314
+ `/plans/${planId}/prices/${priceId}`,
315
+ body,
316
+ options
317
+ );
318
+ }
319
+ async deletePrice(params, options) {
320
+ const { planId, priceId } = params;
321
+ return this.httpClient.delete(
322
+ `/plans/${planId}/prices/${priceId}`,
323
+ void 0,
324
+ options
325
+ );
326
+ }
327
+ async setDefaultPrice(params, options) {
328
+ const { planId, priceId } = params;
329
+ return this.httpClient.put(
330
+ `/plans/${planId}/prices/${priceId}/default`,
331
+ {},
332
+ options
333
+ );
334
+ }
335
+ async setRegionalPrices(params, options) {
336
+ const { planId, priceId, ...body } = params;
337
+ return this.httpClient.put(
338
+ `/plans/${planId}/prices/${priceId}/regional`,
339
+ body,
340
+ options
341
+ );
342
+ }
343
+ async deleteRegionalPrices(params, options) {
344
+ const { planId, priceId } = params;
345
+ return this.httpClient.delete(
346
+ `/plans/${planId}/prices/${priceId}/regional`,
347
+ void 0,
348
+ options
349
+ );
227
350
  }
228
351
  };
229
352
 
@@ -237,39 +360,63 @@ var PortalResource = class {
237
360
  }
238
361
  };
239
362
 
240
- // src/resources/seats.ts
241
- function resolveCode(params) {
242
- const code = params.featureCode ?? params.seatType;
243
- if (!code) {
244
- throw new Error("Either featureCode or seatType must be provided");
363
+ // src/resources/promo-codes.ts
364
+ var PromoCodesResource = class {
365
+ constructor(httpClient) {
366
+ this.httpClient = httpClient;
245
367
  }
246
- return code;
247
- }
368
+ async list(params) {
369
+ return this.httpClient.get("/promo-codes", params);
370
+ }
371
+ async get(params) {
372
+ return this.httpClient.get(`/promo-codes/${params.id}`);
373
+ }
374
+ async create(params, options) {
375
+ return this.httpClient.post("/promo-codes", params, options);
376
+ }
377
+ async update(params, options) {
378
+ const { id, ...body } = params;
379
+ return this.httpClient.put(`/promo-codes/${id}`, body, options);
380
+ }
381
+ };
382
+
383
+ // src/resources/seats.ts
248
384
  var SeatsResource = class {
249
385
  constructor(httpClient) {
250
386
  this.httpClient = httpClient;
251
387
  }
388
+ /** Prorates charges for the current billing period. */
252
389
  async add(params, options) {
253
- const code = resolveCode(params);
254
390
  return this.httpClient.post(
255
391
  "/seats",
256
- { customerId: params.customerId, seatType: code, count: params.count },
392
+ {
393
+ customerId: params.customerId,
394
+ featureCode: params.featureCode,
395
+ count: params.count ?? 1
396
+ },
257
397
  options
258
398
  );
259
399
  }
400
+ /** Removal takes effect at the end of the billing period. */
260
401
  async remove(params, options) {
261
- const code = resolveCode(params);
262
402
  return this.httpClient.delete(
263
403
  "/seats",
264
- { customerId: params.customerId, seatType: code, count: params.count },
404
+ {
405
+ customerId: params.customerId,
406
+ featureCode: params.featureCode,
407
+ count: params.count ?? 1
408
+ },
265
409
  options
266
410
  );
267
411
  }
268
412
  async set(params, options) {
269
- const code = resolveCode(params);
270
413
  return this.httpClient.put(
271
414
  "/seats",
272
- { customerId: params.customerId, seatType: code, count: params.count },
415
+ {
416
+ customerId: params.customerId,
417
+ featureCode: params.featureCode,
418
+ count: params.count
419
+ },
273
420
  options
274
421
  );
275
422
  }
@@ -277,10 +424,9 @@ var SeatsResource = class {
277
424
  return this.httpClient.put("/seats/bulk", params, options);
278
425
  }
279
426
  async getBalance(params) {
280
- const code = resolveCode(params);
281
427
  return this.httpClient.get("/seats/balance", {
282
428
  customerId: params.customerId,
283
- seatType: code
429
+ featureCode: params.featureCode
284
430
  });
285
431
  }
286
432
  async getAllBalances(params) {
@@ -295,67 +441,109 @@ var SubscriptionsResource = class {
295
441
  constructor(httpClient) {
296
442
  this.httpClient = httpClient;
297
443
  }
298
- /**
299
- * Create a subscription with a plan
300
- *
301
- * @example
302
- * ```typescript
303
- * await commet.subscriptions.create({
304
- * externalId: 'user_123',
305
- * planCode: 'pro', // autocomplete works after `commet pull`
306
- * billingInterval: 'yearly',
307
- * initialSeats: { editor: 5 }
308
- * });
309
- * ```
310
- */
444
+ /** Returns a `checkoutUrl` when payment is required before activation. */
311
445
  async create(params, options) {
312
446
  return this.httpClient.post("/subscriptions", params, options);
313
447
  }
314
- /**
315
- * Get the active subscription for a customer
316
- *
317
- * @example
318
- * ```typescript
319
- * const sub = await commet.subscriptions.get('user_123');
320
- * ```
321
- */
322
- async get(customerId) {
323
- return this.httpClient.get("/subscriptions/active", { customerId });
448
+ async getActive(params) {
449
+ return this.httpClient.get("/subscriptions/active", {
450
+ customerId: params.customerId
451
+ });
324
452
  }
325
- /**
326
- * Cancel a subscription
327
- *
328
- * @example
329
- * ```typescript
330
- * await commet.subscriptions.cancel({
331
- * subscriptionId: 'sub_xxx',
332
- * reason: 'switched_to_competitor'
333
- * });
334
- * ```
335
- */
453
+ /** Schedules cancellation at period end by default. Set `immediate: true` to cancel now. */
336
454
  async cancel(params, options) {
455
+ const { id, ...body } = params;
456
+ return this.httpClient.post(`/subscriptions/${id}/cancel`, body, options);
457
+ }
458
+ /** Only works on subscriptions with a pending cancellation — cannot revert already-canceled. */
459
+ async uncancel(params, options) {
337
460
  return this.httpClient.post(
338
- `/subscriptions/${params.subscriptionId}/cancel`,
339
- params || {},
461
+ `/subscriptions/${params.id}/uncancel`,
462
+ {},
340
463
  options
341
464
  );
342
465
  }
343
- /**
344
- * Revert a scheduled cancellation
345
- *
346
- * Only works on subscriptions with a pending cancellation (canceledAt is set
347
- * but status is not yet "canceled"). Cannot revert already-canceled subscriptions.
348
- *
349
- * @example
350
- * ```typescript
351
- * await commet.subscriptions.uncancel({
352
- * subscriptionId: 'sub_xxx',
353
- * });
354
- * ```
355
- */
356
- async uncancel(params, options) {
466
+ /** Upgrades execute immediately with proration. Downgrades are scheduled for end of period. */
467
+ async changePlan(params, options) {
468
+ const { id, ...body } = params;
469
+ return this.httpClient.post(
470
+ `/subscriptions/${id}/change-plan`,
471
+ body,
472
+ options
473
+ );
474
+ }
475
+ async list(params) {
476
+ return this.httpClient.get("/subscriptions", params);
477
+ }
478
+ /** Dry-run: returns proration details without applying the change. */
479
+ async previewChange(params, options) {
480
+ const { id, ...body } = params;
481
+ return this.httpClient.post(
482
+ `/subscriptions/${id}/preview-change`,
483
+ body,
484
+ options
485
+ );
486
+ }
487
+ /** Prorated charge for the current billing period. */
488
+ async activateAddon(params, options) {
489
+ const { id, ...body } = params;
490
+ return this.httpClient.post(`/subscriptions/${id}/addons`, body, options);
491
+ }
492
+ async deactivateAddon(params, options) {
493
+ const { id, addonId } = params;
494
+ return this.httpClient.delete(
495
+ `/subscriptions/${id}/addons/${addonId}`,
496
+ void 0,
497
+ options
498
+ );
499
+ }
500
+ /** Positive amount adds, negative subtracts. */
501
+ async adjustBalance(params, options) {
502
+ const { id, ...body } = params;
503
+ return this.httpClient.post(
504
+ `/subscriptions/${id}/balance/adjust`,
505
+ body,
506
+ options
507
+ );
508
+ }
509
+ /** Charges the customer's payment method. */
510
+ async topupBalance(params, options) {
511
+ const { id, ...body } = params;
512
+ return this.httpClient.post(
513
+ `/subscriptions/${id}/balance/topup`,
514
+ body,
515
+ options
516
+ );
517
+ }
518
+ async purchaseCredits(params, options) {
519
+ const { id, ...body } = params;
520
+ return this.httpClient.post(`/subscriptions/${id}/credits`, body, options);
521
+ }
522
+ };
523
+
524
+ // src/resources/transactions.ts
525
+ var TransactionsResource = class {
526
+ constructor(httpClient) {
527
+ this.httpClient = httpClient;
528
+ }
529
+ async list(params) {
530
+ return this.httpClient.get("/transactions", params);
531
+ }
532
+ async get(params) {
533
+ return this.httpClient.get(`/transactions/${params.id}`);
534
+ }
535
+ /** Full refund only. */
536
+ async refund(params, options) {
357
537
  return this.httpClient.post(
358
- `/subscriptions/${params.subscriptionId}/uncancel`,
538
+ `/transactions/${params.id}/refund`,
539
+ {},
540
+ options
541
+ );
542
+ }
543
+ /** Creates a new invoice and initiates a new payment attempt. */
544
+ async retry(params, options) {
545
+ return this.httpClient.post(
546
+ `/transactions/${params.id}/retry`,
359
547
  {},
360
548
  options
361
549
  );
@@ -367,6 +555,7 @@ var UsageResource = class {
367
555
  constructor(httpClient) {
368
556
  this.httpClient = httpClient;
369
557
  }
558
+ /** Deducts from balance/credits if the plan uses consumption. Duplicate `idempotencyKey` is rejected. */
370
559
  async track(params, options) {
371
560
  const eventData = {
372
561
  feature: params.feature,
@@ -393,44 +582,19 @@ var UsageResource = class {
393
582
  }
394
583
  return this.httpClient.post("/usage/events", eventData, options);
395
584
  }
585
+ /** Dry-run: checks if a usage event would be allowed without actually tracking it. */
586
+ async check(params, options) {
587
+ return this.httpClient.post("/usage/check", params, options);
588
+ }
396
589
  };
397
590
 
398
591
  // src/resources/webhooks.ts
399
- import crypto from "crypto";
592
+ import crypto2 from "crypto";
400
593
  var Webhooks = class {
401
- /**
402
- * Verify HMAC-SHA256 webhook signature
403
- *
404
- * Use this method to verify that webhooks are authentically from Commet.
405
- * The signature is included in the `X-Commet-Signature` header.
406
- *
407
- * @param payload - Raw request body as string (IMPORTANT: Do not parse JSON first)
408
- * @param signature - Value from X-Commet-Signature header
409
- * @param secret - Your webhook secret from Commet dashboard
410
- * @returns true if signature is valid, false otherwise
411
- *
412
- * @example
413
- * ```typescript
414
- * // Next.js API route example
415
- * export async function POST(request: Request) {
416
- * const rawBody = await request.text();
417
- * const signature = request.headers.get('x-commet-signature');
418
- *
419
- * const isValid = commet.webhooks.verify(
420
- * rawBody,
421
- * signature,
422
- * process.env.COMMET_WEBHOOK_SECRET
423
- * );
424
- *
425
- * if (!isValid) {
426
- * return new Response('Invalid signature', { status: 401 });
427
- * }
428
- *
429
- * const payload = JSON.parse(rawBody);
430
- * // Handle webhook event...
431
- * }
432
- * ```
433
- */
594
+ constructor(httpClient) {
595
+ this.httpClient = httpClient;
596
+ }
597
+ /** HMAC-SHA256 verification. Payload must be the raw request body string, not parsed JSON. */
434
598
  verify(params) {
435
599
  const { payload, signature, secret } = params;
436
600
  if (!signature || !secret || !payload) {
@@ -438,7 +602,7 @@ var Webhooks = class {
438
602
  }
439
603
  try {
440
604
  const expectedSignature = this.generateSignature({ payload, secret });
441
- return crypto.timingSafeEqual(
605
+ return crypto2.timingSafeEqual(
442
606
  Buffer.from(signature, "hex"),
443
607
  Buffer.from(expectedSignature, "hex")
444
608
  );
@@ -446,35 +610,11 @@ var Webhooks = class {
446
610
  return false;
447
611
  }
448
612
  }
449
- /**
450
- * Generate HMAC-SHA256 signature (internal use)
451
- * @internal
452
- */
453
613
  generateSignature(params) {
454
614
  const { payload, secret } = params;
455
- return crypto.createHmac("sha256", secret).update(payload).digest("hex");
615
+ return crypto2.createHmac("sha256", secret).update(payload).digest("hex");
456
616
  }
457
- /**
458
- * Parse and verify webhook payload in one step
459
- *
460
- * @example
461
- * ```typescript
462
- * const payload = commet.webhooks.verifyAndParse({
463
- * rawBody,
464
- * signature,
465
- * secret: process.env.COMMET_WEBHOOK_SECRET
466
- * });
467
- *
468
- * if (!payload) {
469
- * return new Response('Invalid signature', { status: 401 });
470
- * }
471
- *
472
- * // payload is typed and validated
473
- * if (payload.event === 'subscription.activated') {
474
- * // Handle activation...
475
- * }
476
- * ```
477
- */
617
+ /** Verifies signature and parses JSON in one step. Returns null if invalid. */
478
618
  verifyAndParse(params) {
479
619
  const { rawBody, signature, secret } = params;
480
620
  if (!this.verify({ payload: rawBody, signature, secret })) {
@@ -486,6 +626,21 @@ var Webhooks = class {
486
626
  return null;
487
627
  }
488
628
  }
629
+ async list(params, options) {
630
+ return this.httpClient.get("/webhooks", params, options);
631
+ }
632
+ /** Response includes `secretKey` which is only returned once. */
633
+ async create(params, options) {
634
+ return this.httpClient.post("/webhooks", params, options);
635
+ }
636
+ async delete(params, options) {
637
+ const { id } = params;
638
+ return this.httpClient.delete(`/webhooks/${id}`, void 0, options);
639
+ }
640
+ async test(params, options) {
641
+ const { id } = params;
642
+ return this.httpClient.post(`/webhooks/${id}/test`, void 0, options);
643
+ }
489
644
  };
490
645
 
491
646
  // src/types/common.ts
@@ -499,12 +654,15 @@ var CommetError = class extends Error {
499
654
  }
500
655
  };
501
656
  var CommetAPIError = class extends CommetError {
502
- constructor(message, statusCode, code, details) {
657
+ constructor(message, statusCode, code, details, errorDetail) {
503
658
  super(message, code, statusCode, details);
504
659
  this.statusCode = statusCode;
505
660
  this.code = code;
506
661
  this.details = details;
507
662
  this.name = "CommetAPIError";
663
+ this.type = errorDetail?.type;
664
+ this.param = errorDetail?.param;
665
+ this.docUrl = errorDetail?.doc_url;
508
666
  }
509
667
  };
510
668
  var CommetValidationError = class extends CommetError {
@@ -516,8 +674,8 @@ var CommetValidationError = class extends CommetError {
516
674
  };
517
675
 
518
676
  // src/version.ts
519
- var API_VERSION = "2026-05-18";
520
- var SDK_VERSION = "4.5.0";
677
+ var API_VERSION = "2026-05-25";
678
+ var SDK_VERSION = "5.0.0";
521
679
 
522
680
  // src/utils/telemetry.ts
523
681
  var registeredIntegrations = /* @__PURE__ */ new Set();
@@ -581,7 +739,7 @@ var DEFAULT_RETRY_CONFIG = {
581
739
  // 8s
582
740
  retryableStatusCodes: [408, 429, 500, 502, 503, 504]
583
741
  };
584
- var CommetHTTPClient = class {
742
+ var _CommetHTTPClient = class _CommetHTTPClient {
585
743
  constructor(config) {
586
744
  this.lastRequestMetrics = null;
587
745
  this.config = config;
@@ -592,7 +750,13 @@ var CommetHTTPClient = class {
592
750
  };
593
751
  }
594
752
  async get(endpoint, params, options) {
595
- return this.request("GET", endpoint, void 0, options, params);
753
+ return this.request(
754
+ "GET",
755
+ endpoint,
756
+ void 0,
757
+ options,
758
+ params
759
+ );
596
760
  }
597
761
  async post(endpoint, data, options) {
598
762
  return this.request("POST", endpoint, data, options);
@@ -608,6 +772,9 @@ var CommetHTTPClient = class {
608
772
  }
609
773
  async request(method, endpoint, data, options, params) {
610
774
  const url = this.buildURL(endpoint, params);
775
+ if (_CommetHTTPClient.BODY_METHODS.has(method) && this.retryConfig.maxRetries > 0 && !options?.idempotencyKey) {
776
+ options = { ...options, idempotencyKey: this.generateIdempotencyKey() };
777
+ }
611
778
  return this.executeRequest(method, url, data, options);
612
779
  }
613
780
  /**
@@ -633,8 +800,6 @@ var CommetHTTPClient = class {
633
800
  }
634
801
  if (options?.idempotencyKey) {
635
802
  headers["Idempotency-Key"] = options.idempotencyKey;
636
- } else if (method === "POST" && data) {
637
- headers["Idempotency-Key"] = this.generateIdempotencyKey();
638
803
  }
639
804
  const requestConfig = {
640
805
  method,
@@ -703,26 +868,30 @@ var CommetHTTPClient = class {
703
868
  JSON.stringify(responseData, null, 2)
704
869
  );
705
870
  }
706
- const isErrorResponse = (data2) => {
707
- return typeof data2 === "object" && data2 !== null;
871
+ const parsed = typeof responseData === "object" && responseData !== null ? responseData : {};
872
+ const errorObj = typeof parsed.error === "object" && parsed.error !== null ? parsed.error : {};
873
+ const errorDetail = {
874
+ type: errorObj.type ?? "api_error",
875
+ code: errorObj.code ?? "unknown",
876
+ message: errorObj.message ?? `Request failed with status ${response.status}`,
877
+ param: errorObj.param,
878
+ details: errorObj.details,
879
+ doc_url: errorObj.doc_url
708
880
  };
709
- const errorData = isErrorResponse(responseData) ? responseData : {};
710
- if (errorData.code === "validation_error" && Array.isArray(errorData.details)) {
881
+ if (errorDetail.code === "validation_error" && Array.isArray(errorDetail.details)) {
711
882
  const errors = {};
712
- for (const detail of errorData.details) {
883
+ for (const detail of errorDetail.details) {
713
884
  if (!errors[detail.field]) errors[detail.field] = [];
714
885
  errors[detail.field].push(detail.message);
715
886
  }
716
- throw new CommetValidationError(
717
- errorData.message || "Validation failed",
718
- errors
719
- );
887
+ throw new CommetValidationError(errorDetail.message, errors);
720
888
  }
721
889
  throw new CommetAPIError(
722
- errorData.message || `Request failed with status ${response.status}`,
890
+ errorDetail.message,
723
891
  response.status,
724
- errorData.code,
725
- errorData.details
892
+ errorDetail.code,
893
+ errorDetail.details,
894
+ errorDetail
726
895
  );
727
896
  }
728
897
  if (this.config.debug) {
@@ -780,11 +949,8 @@ var CommetHTTPClient = class {
780
949
  }
781
950
  return finalUrl;
782
951
  }
783
- /**
784
- * Generate idempotency key
785
- */
786
952
  generateIdempotencyKey() {
787
- return `sdk_${Date.now()}_${Math.random().toString(36).substring(2)}`;
953
+ return `commet-node-retry-${crypto.randomUUID()}`;
788
954
  }
789
955
  /**
790
956
  * Sleep for specified milliseconds
@@ -793,6 +959,8 @@ var CommetHTTPClient = class {
793
959
  return new Promise((resolve) => setTimeout(resolve, ms));
794
960
  }
795
961
  };
962
+ _CommetHTTPClient.BODY_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH"]);
963
+ var CommetHTTPClient = _CommetHTTPClient;
796
964
 
797
965
  // src/client.ts
798
966
  var Commet = class {
@@ -806,41 +974,21 @@ var Commet = class {
806
974
  );
807
975
  }
808
976
  this.httpClient = new CommetHTTPClient(config);
809
- this.customers = new CustomersResource(this.httpClient);
977
+ this.addons = new AddonsResource(this.httpClient);
978
+ this.apiKeys = new ApiKeysResource(this.httpClient);
810
979
  this.creditPacks = new CreditPacksResource(this.httpClient);
980
+ this.customers = new CustomersResource(this.httpClient);
981
+ this.features = new FeaturesResource(this.httpClient);
982
+ this.invoices = new InvoicesResource(this.httpClient);
983
+ this.planGroups = new PlanGroupsResource(this.httpClient);
811
984
  this.plans = new PlansResource(this.httpClient);
812
- this.usage = new UsageResource(this.httpClient);
985
+ this.portal = new PortalResource(this.httpClient);
986
+ this.promoCodes = new PromoCodesResource(this.httpClient);
813
987
  this.seats = new SeatsResource(this.httpClient);
814
988
  this.subscriptions = new SubscriptionsResource(this.httpClient);
815
- this.portal = new PortalResource(this.httpClient);
816
- this.features = new FeaturesResource(this.httpClient);
817
- this.webhooks = new Webhooks();
818
- if (config.debug) {
819
- console.log("[Commet SDK] Initialized");
820
- console.log("API Key:", `${config.apiKey.substring(0, 12)}...`);
821
- }
822
- }
823
- /**
824
- * Create a customer-scoped context for cleaner API usage
825
- *
826
- * @example
827
- * ```typescript
828
- * const customer = commet.customer("user_123");
829
- *
830
- * // All operations are now scoped to this customer
831
- * const seats = await customer.features.get("team_members");
832
- * await customer.seats.add("member");
833
- * await customer.usage.track("api_call");
834
- * ```
835
- */
836
- customer(customerId) {
837
- return new CustomerContext(customerId, {
838
- features: this.features,
839
- seats: this.seats,
840
- usage: this.usage,
841
- subscriptions: this.subscriptions,
842
- portal: this.portal
843
- });
989
+ this.transactions = new TransactionsResource(this.httpClient);
990
+ this.usage = new UsageResource(this.httpClient);
991
+ this.webhooks = new Webhooks(this.httpClient);
844
992
  }
845
993
  };
846
994
  function createCommet(_billingConfig, options) {
@@ -860,7 +1008,6 @@ export {
860
1008
  CommetAPIError,
861
1009
  CommetError,
862
1010
  CommetValidationError,
863
- CustomerContext,
864
1011
  SDK_VERSION,
865
1012
  Webhooks,
866
1013
  createCommet,