@commet/node 4.6.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,79 +1,51 @@
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;
52
- }
53
- };
54
-
55
1
  // src/resources/addons.ts
56
2
  var AddonsResource = class {
57
3
  constructor(httpClient) {
58
4
  this.httpClient = httpClient;
59
5
  }
60
- /**
61
- * Get active addons for a customer's subscription
62
- *
63
- * @example
64
- * ```typescript
65
- * const { data } = await commet.addons.getActive({
66
- * customerId: 'cus_xxx',
67
- * });
68
- * ```
69
- */
70
- async getActive(params, options) {
6
+ async listActive(params, options) {
71
7
  return this.httpClient.get(
72
8
  "/addons/active",
73
9
  { customerId: params.customerId },
74
10
  options
75
11
  );
76
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);
48
+ }
77
49
  };
78
50
 
79
51
  // src/resources/credit-packs.ts
@@ -81,18 +53,20 @@ var CreditPacksResource = class {
81
53
  constructor(httpClient) {
82
54
  this.httpClient = httpClient;
83
55
  }
84
- /**
85
- * List all active credit packs
86
- *
87
- * @example
88
- * ```typescript
89
- * const packs = await commet.creditPacks.list();
90
- * console.log(packs.data); // [{ id: "cp_xxx", name: "100 Credits", ... }]
91
- * ```
92
- */
93
56
  async list() {
94
57
  return this.httpClient.get("/credit-packs");
95
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
+ }
96
70
  };
97
71
 
98
72
  // src/resources/customers.ts
@@ -100,15 +74,13 @@ var CustomersResource = class {
100
74
  constructor(httpClient) {
101
75
  this.httpClient = httpClient;
102
76
  }
103
- /**
104
- * Create a customer (idempotent when id is provided)
105
- */
77
+ /** Idempotent when `id` is provided — returns existing customer instead of duplicating. */
106
78
  async create(params, options) {
107
79
  return this.httpClient.post(
108
80
  "/customers",
109
81
  {
110
82
  billingEmail: params.email,
111
- externalId: params.id,
83
+ id: params.id,
112
84
  fullName: params.fullName,
113
85
  domain: params.domain,
114
86
  website: params.website,
@@ -121,13 +93,10 @@ var CustomersResource = class {
121
93
  options
122
94
  );
123
95
  }
124
- /**
125
- * Create multiple customers in batch
126
- */
127
96
  async createBatch(params, options) {
128
97
  const customers = params.customers.map((c) => ({
129
98
  billingEmail: c.email,
130
- externalId: c.id,
99
+ id: c.id,
131
100
  fullName: c.fullName,
132
101
  domain: c.domain,
133
102
  website: c.website,
@@ -139,18 +108,12 @@ var CustomersResource = class {
139
108
  }));
140
109
  return this.httpClient.post("/customers/batch", { customers }, options);
141
110
  }
142
- /**
143
- * Get a customer by ID
144
- */
145
- async get(customerId) {
146
- return this.httpClient.get(`/customers/${customerId}`);
111
+ async get(params) {
112
+ return this.httpClient.get(`/customers/${params.id}`);
147
113
  }
148
- /**
149
- * Update a customer
150
- */
151
114
  async update(params, options) {
152
115
  return this.httpClient.put(
153
- `/customers/${params.customerId}`,
116
+ `/customers/${params.id}`,
154
117
  {
155
118
  billingEmail: params.email,
156
119
  fullName: params.fullName,
@@ -165,9 +128,6 @@ var CustomersResource = class {
165
128
  options
166
129
  );
167
130
  }
168
- /**
169
- * List customers with optional filters
170
- */
171
131
  async list(params) {
172
132
  return this.httpClient.get("/customers", params);
173
133
  }
@@ -185,34 +145,113 @@ var FeaturesResource = class {
185
145
  options
186
146
  );
187
147
  }
188
- async check(params, options) {
189
- 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(
190
151
  `/features/${params.code}`,
191
- { customerId: params.customerId },
152
+ { customerId: params.customerId, action: "canUse" },
192
153
  options
193
154
  );
194
- if (!result.success || !result.data) {
195
- return {
196
- success: false,
197
- code: result.code,
198
- message: result.message,
199
- details: result.details
200
- };
201
- }
202
- return {
203
- success: true,
204
- data: { allowed: result.data.allowed }
205
- };
206
155
  }
207
- async canUse(params, options) {
156
+ async list(params, options) {
208
157
  return this.httpClient.get(
209
- `/features/${params.code}`,
210
- { 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,
211
176
  options
212
177
  );
213
178
  }
214
- async list(customerId, options) {
215
- 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
+ );
216
255
  }
217
256
  };
218
257
 
@@ -221,33 +260,93 @@ var PlansResource = class {
221
260
  constructor(httpClient) {
222
261
  this.httpClient = httpClient;
223
262
  }
224
- /**
225
- * List all available plans
226
- *
227
- * @example
228
- * ```typescript
229
- * // List public plans
230
- * const plans = await commet.plans.list();
231
- *
232
- * // Include private plans
233
- * const allPlans = await commet.plans.list({ includePrivate: true });
234
- * ```
235
- */
236
263
  async list(params) {
237
264
  return this.httpClient.get("/plans", params);
238
265
  }
239
- /**
240
- * Get a specific plan by code
241
- *
242
- * @example
243
- * ```typescript
244
- * const plan = await commet.plans.get('pro');
245
- * console.log(plan.data.name); // "Pro"
246
- * console.log(plan.data.prices); // [{ billingInterval: 'monthly', price: 9900 }]
247
- * ```
248
- */
249
- async get(planCode) {
250
- 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
+ );
251
350
  }
252
351
  };
253
352
 
@@ -261,39 +360,63 @@ var PortalResource = class {
261
360
  }
262
361
  };
263
362
 
264
- // src/resources/seats.ts
265
- function resolveCode(params) {
266
- const code = params.featureCode ?? params.seatType;
267
- if (!code) {
268
- 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;
269
367
  }
270
- return code;
271
- }
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
272
384
  var SeatsResource = class {
273
385
  constructor(httpClient) {
274
386
  this.httpClient = httpClient;
275
387
  }
388
+ /** Prorates charges for the current billing period. */
276
389
  async add(params, options) {
277
- const code = resolveCode(params);
278
390
  return this.httpClient.post(
279
391
  "/seats",
280
- { customerId: params.customerId, seatType: code, count: params.count },
392
+ {
393
+ customerId: params.customerId,
394
+ featureCode: params.featureCode,
395
+ count: params.count ?? 1
396
+ },
281
397
  options
282
398
  );
283
399
  }
400
+ /** Removal takes effect at the end of the billing period. */
284
401
  async remove(params, options) {
285
- const code = resolveCode(params);
286
402
  return this.httpClient.delete(
287
403
  "/seats",
288
- { customerId: params.customerId, seatType: code, count: params.count },
404
+ {
405
+ customerId: params.customerId,
406
+ featureCode: params.featureCode,
407
+ count: params.count ?? 1
408
+ },
289
409
  options
290
410
  );
291
411
  }
292
412
  async set(params, options) {
293
- const code = resolveCode(params);
294
413
  return this.httpClient.put(
295
414
  "/seats",
296
- { customerId: params.customerId, seatType: code, count: params.count },
415
+ {
416
+ customerId: params.customerId,
417
+ featureCode: params.featureCode,
418
+ count: params.count
419
+ },
297
420
  options
298
421
  );
299
422
  }
@@ -301,10 +424,9 @@ var SeatsResource = class {
301
424
  return this.httpClient.put("/seats/bulk", params, options);
302
425
  }
303
426
  async getBalance(params) {
304
- const code = resolveCode(params);
305
427
  return this.httpClient.get("/seats/balance", {
306
428
  customerId: params.customerId,
307
- seatType: code
429
+ featureCode: params.featureCode
308
430
  });
309
431
  }
310
432
  async getAllBalances(params) {
@@ -319,92 +441,113 @@ var SubscriptionsResource = class {
319
441
  constructor(httpClient) {
320
442
  this.httpClient = httpClient;
321
443
  }
322
- /**
323
- * Create a subscription with a plan
324
- *
325
- * @example
326
- * ```typescript
327
- * await commet.subscriptions.create({
328
- * externalId: 'user_123',
329
- * planCode: 'pro', // autocomplete works after `commet pull`
330
- * billingInterval: 'yearly',
331
- * initialSeats: { editor: 5 }
332
- * });
333
- * ```
334
- */
444
+ /** Returns a `checkoutUrl` when payment is required before activation. */
335
445
  async create(params, options) {
336
446
  return this.httpClient.post("/subscriptions", params, options);
337
447
  }
338
- /**
339
- * Get the active subscription for a customer
340
- *
341
- * @example
342
- * ```typescript
343
- * const sub = await commet.subscriptions.get('user_123');
344
- * ```
345
- */
346
- async get(customerId) {
347
- return this.httpClient.get("/subscriptions/active", { customerId });
448
+ async getActive(params) {
449
+ return this.httpClient.get("/subscriptions/active", {
450
+ customerId: params.customerId
451
+ });
348
452
  }
349
- /**
350
- * Cancel a subscription
351
- *
352
- * @example
353
- * ```typescript
354
- * await commet.subscriptions.cancel({
355
- * subscriptionId: 'sub_xxx',
356
- * reason: 'switched_to_competitor'
357
- * });
358
- * ```
359
- */
453
+ /** Schedules cancellation at period end by default. Set `immediate: true` to cancel now. */
360
454
  async cancel(params, options) {
361
- return this.httpClient.post(
362
- `/subscriptions/${params.subscriptionId}/cancel`,
363
- params || {},
364
- options
365
- );
455
+ const { id, ...body } = params;
456
+ return this.httpClient.post(`/subscriptions/${id}/cancel`, body, options);
366
457
  }
367
- /**
368
- * Revert a scheduled cancellation
369
- *
370
- * Only works on subscriptions with a pending cancellation (canceledAt is set
371
- * but status is not yet "canceled"). Cannot revert already-canceled subscriptions.
372
- *
373
- * @example
374
- * ```typescript
375
- * await commet.subscriptions.uncancel({
376
- * subscriptionId: 'sub_xxx',
377
- * });
378
- * ```
379
- */
458
+ /** Only works on subscriptions with a pending cancellation — cannot revert already-canceled. */
380
459
  async uncancel(params, options) {
381
460
  return this.httpClient.post(
382
- `/subscriptions/${params.subscriptionId}/uncancel`,
461
+ `/subscriptions/${params.id}/uncancel`,
383
462
  {},
384
463
  options
385
464
  );
386
465
  }
387
- /**
388
- * Change the plan of a subscription (upgrade/downgrade)
389
- *
390
- * Upgrades execute immediately. Downgrades are scheduled for end of period.
391
- *
392
- * @example
393
- * ```typescript
394
- * await commet.subscriptions.changePlan({
395
- * subscriptionId: 'sub_xxx',
396
- * newPlanId: 'pln_xxx',
397
- * });
398
- * ```
399
- */
466
+ /** Upgrades execute immediately with proration. Downgrades are scheduled for end of period. */
400
467
  async changePlan(params, options) {
401
- const { subscriptionId, ...body } = params;
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;
402
481
  return this.httpClient.post(
403
- `/subscriptions/${subscriptionId}/change-plan`,
482
+ `/subscriptions/${id}/preview-change`,
404
483
  body,
405
484
  options
406
485
  );
407
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) {
537
+ return this.httpClient.post(
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`,
547
+ {},
548
+ options
549
+ );
550
+ }
408
551
  };
409
552
 
410
553
  // src/resources/usage.ts
@@ -412,6 +555,7 @@ var UsageResource = class {
412
555
  constructor(httpClient) {
413
556
  this.httpClient = httpClient;
414
557
  }
558
+ /** Deducts from balance/credits if the plan uses consumption. Duplicate `idempotencyKey` is rejected. */
415
559
  async track(params, options) {
416
560
  const eventData = {
417
561
  feature: params.feature,
@@ -438,62 +582,19 @@ var UsageResource = class {
438
582
  }
439
583
  return this.httpClient.post("/usage/events", eventData, options);
440
584
  }
441
- /**
442
- * Check if a usage event would be allowed before tracking it
443
- *
444
- * @example
445
- * ```typescript
446
- * const { data } = await commet.usage.check({
447
- * customerId: 'cus_xxx',
448
- * featureCode: 'api_calls',
449
- * quantity: 1,
450
- * });
451
- * if (data.allowed) {
452
- * // proceed with the operation
453
- * }
454
- * ```
455
- */
585
+ /** Dry-run: checks if a usage event would be allowed without actually tracking it. */
456
586
  async check(params, options) {
457
587
  return this.httpClient.post("/usage/check", params, options);
458
588
  }
459
589
  };
460
590
 
461
591
  // src/resources/webhooks.ts
462
- import crypto from "crypto";
592
+ import crypto2 from "crypto";
463
593
  var Webhooks = class {
464
- /**
465
- * Verify HMAC-SHA256 webhook signature
466
- *
467
- * Use this method to verify that webhooks are authentically from Commet.
468
- * The signature is included in the `X-Commet-Signature` header.
469
- *
470
- * @param payload - Raw request body as string (IMPORTANT: Do not parse JSON first)
471
- * @param signature - Value from X-Commet-Signature header
472
- * @param secret - Your webhook secret from Commet dashboard
473
- * @returns true if signature is valid, false otherwise
474
- *
475
- * @example
476
- * ```typescript
477
- * // Next.js API route example
478
- * export async function POST(request: Request) {
479
- * const rawBody = await request.text();
480
- * const signature = request.headers.get('x-commet-signature');
481
- *
482
- * const isValid = commet.webhooks.verify(
483
- * rawBody,
484
- * signature,
485
- * process.env.COMMET_WEBHOOK_SECRET
486
- * );
487
- *
488
- * if (!isValid) {
489
- * return new Response('Invalid signature', { status: 401 });
490
- * }
491
- *
492
- * const payload = JSON.parse(rawBody);
493
- * // Handle webhook event...
494
- * }
495
- * ```
496
- */
594
+ constructor(httpClient) {
595
+ this.httpClient = httpClient;
596
+ }
597
+ /** HMAC-SHA256 verification. Payload must be the raw request body string, not parsed JSON. */
497
598
  verify(params) {
498
599
  const { payload, signature, secret } = params;
499
600
  if (!signature || !secret || !payload) {
@@ -501,7 +602,7 @@ var Webhooks = class {
501
602
  }
502
603
  try {
503
604
  const expectedSignature = this.generateSignature({ payload, secret });
504
- return crypto.timingSafeEqual(
605
+ return crypto2.timingSafeEqual(
505
606
  Buffer.from(signature, "hex"),
506
607
  Buffer.from(expectedSignature, "hex")
507
608
  );
@@ -509,35 +610,11 @@ var Webhooks = class {
509
610
  return false;
510
611
  }
511
612
  }
512
- /**
513
- * Generate HMAC-SHA256 signature (internal use)
514
- * @internal
515
- */
516
613
  generateSignature(params) {
517
614
  const { payload, secret } = params;
518
- return crypto.createHmac("sha256", secret).update(payload).digest("hex");
615
+ return crypto2.createHmac("sha256", secret).update(payload).digest("hex");
519
616
  }
520
- /**
521
- * Parse and verify webhook payload in one step
522
- *
523
- * @example
524
- * ```typescript
525
- * const payload = commet.webhooks.verifyAndParse({
526
- * rawBody,
527
- * signature,
528
- * secret: process.env.COMMET_WEBHOOK_SECRET
529
- * });
530
- *
531
- * if (!payload) {
532
- * return new Response('Invalid signature', { status: 401 });
533
- * }
534
- *
535
- * // payload is typed and validated
536
- * if (payload.event === 'subscription.activated') {
537
- * // Handle activation...
538
- * }
539
- * ```
540
- */
617
+ /** Verifies signature and parses JSON in one step. Returns null if invalid. */
541
618
  verifyAndParse(params) {
542
619
  const { rawBody, signature, secret } = params;
543
620
  if (!this.verify({ payload: rawBody, signature, secret })) {
@@ -549,6 +626,21 @@ var Webhooks = class {
549
626
  return null;
550
627
  }
551
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
+ }
552
644
  };
553
645
 
554
646
  // src/types/common.ts
@@ -562,12 +654,15 @@ var CommetError = class extends Error {
562
654
  }
563
655
  };
564
656
  var CommetAPIError = class extends CommetError {
565
- constructor(message, statusCode, code, details) {
657
+ constructor(message, statusCode, code, details, errorDetail) {
566
658
  super(message, code, statusCode, details);
567
659
  this.statusCode = statusCode;
568
660
  this.code = code;
569
661
  this.details = details;
570
662
  this.name = "CommetAPIError";
663
+ this.type = errorDetail?.type;
664
+ this.param = errorDetail?.param;
665
+ this.docUrl = errorDetail?.doc_url;
571
666
  }
572
667
  };
573
668
  var CommetValidationError = class extends CommetError {
@@ -579,8 +674,8 @@ var CommetValidationError = class extends CommetError {
579
674
  };
580
675
 
581
676
  // src/version.ts
582
- var API_VERSION = "2026-05-18";
583
- var SDK_VERSION = "4.6.0";
677
+ var API_VERSION = "2026-05-25";
678
+ var SDK_VERSION = "5.0.0";
584
679
 
585
680
  // src/utils/telemetry.ts
586
681
  var registeredIntegrations = /* @__PURE__ */ new Set();
@@ -644,7 +739,7 @@ var DEFAULT_RETRY_CONFIG = {
644
739
  // 8s
645
740
  retryableStatusCodes: [408, 429, 500, 502, 503, 504]
646
741
  };
647
- var CommetHTTPClient = class {
742
+ var _CommetHTTPClient = class _CommetHTTPClient {
648
743
  constructor(config) {
649
744
  this.lastRequestMetrics = null;
650
745
  this.config = config;
@@ -655,7 +750,13 @@ var CommetHTTPClient = class {
655
750
  };
656
751
  }
657
752
  async get(endpoint, params, options) {
658
- 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
+ );
659
760
  }
660
761
  async post(endpoint, data, options) {
661
762
  return this.request("POST", endpoint, data, options);
@@ -671,6 +772,9 @@ var CommetHTTPClient = class {
671
772
  }
672
773
  async request(method, endpoint, data, options, params) {
673
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
+ }
674
778
  return this.executeRequest(method, url, data, options);
675
779
  }
676
780
  /**
@@ -696,8 +800,6 @@ var CommetHTTPClient = class {
696
800
  }
697
801
  if (options?.idempotencyKey) {
698
802
  headers["Idempotency-Key"] = options.idempotencyKey;
699
- } else if (method === "POST" && data) {
700
- headers["Idempotency-Key"] = this.generateIdempotencyKey();
701
803
  }
702
804
  const requestConfig = {
703
805
  method,
@@ -766,26 +868,30 @@ var CommetHTTPClient = class {
766
868
  JSON.stringify(responseData, null, 2)
767
869
  );
768
870
  }
769
- const isErrorResponse = (data2) => {
770
- 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
771
880
  };
772
- const errorData = isErrorResponse(responseData) ? responseData : {};
773
- if (errorData.code === "validation_error" && Array.isArray(errorData.details)) {
881
+ if (errorDetail.code === "validation_error" && Array.isArray(errorDetail.details)) {
774
882
  const errors = {};
775
- for (const detail of errorData.details) {
883
+ for (const detail of errorDetail.details) {
776
884
  if (!errors[detail.field]) errors[detail.field] = [];
777
885
  errors[detail.field].push(detail.message);
778
886
  }
779
- throw new CommetValidationError(
780
- errorData.message || "Validation failed",
781
- errors
782
- );
887
+ throw new CommetValidationError(errorDetail.message, errors);
783
888
  }
784
889
  throw new CommetAPIError(
785
- errorData.message || `Request failed with status ${response.status}`,
890
+ errorDetail.message,
786
891
  response.status,
787
- errorData.code,
788
- errorData.details
892
+ errorDetail.code,
893
+ errorDetail.details,
894
+ errorDetail
789
895
  );
790
896
  }
791
897
  if (this.config.debug) {
@@ -843,11 +949,8 @@ var CommetHTTPClient = class {
843
949
  }
844
950
  return finalUrl;
845
951
  }
846
- /**
847
- * Generate idempotency key
848
- */
849
952
  generateIdempotencyKey() {
850
- return `sdk_${Date.now()}_${Math.random().toString(36).substring(2)}`;
953
+ return `commet-node-retry-${crypto.randomUUID()}`;
851
954
  }
852
955
  /**
853
956
  * Sleep for specified milliseconds
@@ -856,6 +959,8 @@ var CommetHTTPClient = class {
856
959
  return new Promise((resolve) => setTimeout(resolve, ms));
857
960
  }
858
961
  };
962
+ _CommetHTTPClient.BODY_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH"]);
963
+ var CommetHTTPClient = _CommetHTTPClient;
859
964
 
860
965
  // src/client.ts
861
966
  var Commet = class {
@@ -870,41 +975,20 @@ var Commet = class {
870
975
  }
871
976
  this.httpClient = new CommetHTTPClient(config);
872
977
  this.addons = new AddonsResource(this.httpClient);
873
- this.customers = new CustomersResource(this.httpClient);
978
+ this.apiKeys = new ApiKeysResource(this.httpClient);
874
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);
875
984
  this.plans = new PlansResource(this.httpClient);
876
- this.usage = new UsageResource(this.httpClient);
985
+ this.portal = new PortalResource(this.httpClient);
986
+ this.promoCodes = new PromoCodesResource(this.httpClient);
877
987
  this.seats = new SeatsResource(this.httpClient);
878
988
  this.subscriptions = new SubscriptionsResource(this.httpClient);
879
- this.portal = new PortalResource(this.httpClient);
880
- this.features = new FeaturesResource(this.httpClient);
881
- this.webhooks = new Webhooks();
882
- if (config.debug) {
883
- console.log("[Commet SDK] Initialized");
884
- console.log("API Key:", `${config.apiKey.substring(0, 12)}...`);
885
- }
886
- }
887
- /**
888
- * Create a customer-scoped context for cleaner API usage
889
- *
890
- * @example
891
- * ```typescript
892
- * const customer = commet.customer("user_123");
893
- *
894
- * // All operations are now scoped to this customer
895
- * const seats = await customer.features.get("team_members");
896
- * await customer.seats.add("member");
897
- * await customer.usage.track("api_call");
898
- * ```
899
- */
900
- customer(customerId) {
901
- return new CustomerContext(customerId, {
902
- features: this.features,
903
- seats: this.seats,
904
- usage: this.usage,
905
- subscriptions: this.subscriptions,
906
- portal: this.portal
907
- });
989
+ this.transactions = new TransactionsResource(this.httpClient);
990
+ this.usage = new UsageResource(this.httpClient);
991
+ this.webhooks = new Webhooks(this.httpClient);
908
992
  }
909
993
  };
910
994
  function createCommet(_billingConfig, options) {
@@ -924,7 +1008,6 @@ export {
924
1008
  CommetAPIError,
925
1009
  CommetError,
926
1010
  CommetValidationError,
927
- CustomerContext,
928
1011
  SDK_VERSION,
929
1012
  Webhooks,
930
1013
  createCommet,