@clocknext/sdk 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/config.ts
2
- var DEFAULT_BASE_URL = "https://app.clocknext.com";
2
+ var DEFAULT_BASE_URL = "https://payments.clocknext.com";
3
3
  function num(value, fallback, min) {
4
4
  return typeof value === "number" && Number.isFinite(value) && value >= min ? value : fallback;
5
5
  }
@@ -121,7 +121,8 @@ var Flusher = class {
121
121
  method: "POST",
122
122
  path: "/api/v1/usage",
123
123
  body: item.body,
124
- idempotencyKey: item.idempotencyKey
124
+ // Best-effort metering opts into retries on transient failures.
125
+ retry: true
125
126
  });
126
127
  return true;
127
128
  } catch (error) {
@@ -144,15 +145,12 @@ var Flusher = class {
144
145
  var ClockNextError = class extends Error {
145
146
  /** HTTP status, when the failure came from a server response. */
146
147
  status;
147
- /** The idempotency key of the offending signal, when applicable. */
148
- idempotencyKey;
149
148
  /** Whether the SDK considers this failure worth retrying. */
150
149
  retryable;
151
150
  constructor(message, opts = {}) {
152
151
  super(message);
153
152
  this.name = new.target.name;
154
153
  this.status = opts.status;
155
- this.idempotencyKey = opts.idempotencyKey;
156
154
  this.retryable = opts.retryable ?? false;
157
155
  Object.setPrototypeOf(this, new.target.prototype);
158
156
  }
@@ -172,28 +170,29 @@ var ConflictError = class extends ClockNextError {
172
170
  var RateLimitError = class extends ClockNextError {
173
171
  retryAfterMs;
174
172
  constructor(message, opts = {}) {
175
- super(message, { status: 429, idempotencyKey: opts.idempotencyKey, retryable: true });
173
+ super(message, { status: 429, retryable: true });
176
174
  this.retryAfterMs = opts.retryAfterMs;
177
175
  }
178
176
  };
179
177
  var ServerError = class extends ClockNextError {
180
178
  };
181
179
  var NetworkError = class extends ClockNextError {
182
- constructor(message, opts = {}) {
183
- super(message, { idempotencyKey: opts.idempotencyKey, retryable: true });
180
+ constructor(message) {
181
+ super(message, { retryable: true });
184
182
  }
185
183
  };
186
- function errorFromResponse(status, message, idempotencyKey, retryAfterMs) {
184
+ function errorFromResponse(status, message, retryAfterMs) {
187
185
  const msg = message || `Request failed with status ${status}.`;
188
- const opts = { status, idempotencyKey };
186
+ const opts = { status };
189
187
  if (status === 400) return new ValidationError(msg, opts);
190
188
  if (status === 401) return new AuthError(msg, opts);
191
189
  if (status === 404) return new NotFoundError(msg, opts);
190
+ if (status === 408) return new ClockNextError(msg, { ...opts, retryable: true });
192
191
  if (status === 409) return new ConflictError(msg, { ...opts, retryable: true });
193
192
  if (status === 422) {
194
193
  return /insufficient/i.test(msg) ? new AllowanceError(msg, opts) : new PlanError(msg, opts);
195
194
  }
196
- if (status === 429) return new RateLimitError(msg, { idempotencyKey, retryAfterMs });
195
+ if (status === 429) return new RateLimitError(msg, { retryAfterMs });
197
196
  if (status >= 500) return new ServerError(msg, { ...opts, retryable: true });
198
197
  return new ClockNextError(msg, opts);
199
198
  }
@@ -228,7 +227,7 @@ function sleep(ms) {
228
227
  }
229
228
 
230
229
  // src/http.ts
231
- var SDK_VERSION = "0.1.0";
230
+ var SDK_VERSION = "0.3.0" ;
232
231
  var Transport = class {
233
232
  constructor(cfg) {
234
233
  this.cfg = cfg;
@@ -236,8 +235,9 @@ var Transport = class {
236
235
  cfg;
237
236
  async request(opts) {
238
237
  const url = this.buildUrl(opts.path, opts.query);
239
- const replaySafe = opts.method === "GET" || opts.method === "DELETE" || opts.idempotencyKey != null;
240
- const maxAttempts = opts.retry === false || !replaySafe ? 1 : this.cfg.retry.maxAttempts;
238
+ const replaySafe = opts.method === "GET" || opts.method === "PUT" || opts.method === "DELETE";
239
+ const allowRetry = opts.retry ?? replaySafe;
240
+ const maxAttempts = allowRetry ? this.cfg.retry.maxAttempts : 1;
241
241
  let attempt = 0;
242
242
  while (true) {
243
243
  attempt++;
@@ -271,7 +271,7 @@ var Transport = class {
271
271
  });
272
272
  } catch (err) {
273
273
  const message2 = controller.signal.aborted ? `Request to ${opts.path} timed out after ${this.cfg.timeoutMs}ms.` : `Network error calling ${opts.path}: ${err instanceof Error ? err.message : String(err)}`;
274
- throw new NetworkError(message2, { idempotencyKey: opts.idempotencyKey });
274
+ throw new NetworkError(message2);
275
275
  } finally {
276
276
  clearTimeout(timer);
277
277
  }
@@ -291,24 +291,14 @@ var Transport = class {
291
291
  return obj.result ?? {};
292
292
  }
293
293
  const message2 = detail?.message ?? `Request failed with status ${res.status}.`;
294
- throw errorFromResponse(
295
- res.status,
296
- message2,
297
- opts.idempotencyKey,
298
- parseRetryAfter(res.headers)
299
- );
294
+ throw errorFromResponse(res.status, message2, parseRetryAfter(res.headers));
300
295
  }
301
296
  const envelope = obj;
302
297
  if (res.ok && envelope.ok !== false) {
303
298
  return envelope;
304
299
  }
305
300
  const message = envelope.error ?? text ?? `Request failed with status ${res.status}.`;
306
- throw errorFromResponse(
307
- res.status,
308
- message,
309
- opts.idempotencyKey,
310
- parseRetryAfter(res.headers)
311
- );
301
+ throw errorFromResponse(res.status, message, parseRetryAfter(res.headers));
312
302
  }
313
303
  headers(opts) {
314
304
  const h = {
@@ -316,7 +306,6 @@ var Transport = class {
316
306
  "User-Agent": `clocknext-sdk-js/${SDK_VERSION}`
317
307
  };
318
308
  if (opts.body !== void 0) h["Content-Type"] = "application/json";
319
- if (opts.idempotencyKey) h["Idempotency-Key"] = opts.idempotencyKey;
320
309
  return h;
321
310
  }
322
311
  buildUrl(path, query) {
@@ -333,6 +322,63 @@ var Transport = class {
333
322
  }
334
323
  };
335
324
 
325
+ // src/resources/credits.ts
326
+ var Credits = class {
327
+ constructor(transport) {
328
+ this.transport = transport;
329
+ }
330
+ transport;
331
+ /** List credit types. Pass `{ active: true }` to only return active ones. */
332
+ async list(params = {}) {
333
+ const res = await this.transport.request({
334
+ method: "GET",
335
+ path: "/api/v1/credits",
336
+ query: { active: params.active === void 0 ? void 0 : String(params.active) }
337
+ });
338
+ return res.credits;
339
+ }
340
+ /** Create a credit type. `POST /api/v1/credits`. */
341
+ async create(input) {
342
+ await this.transport.request({
343
+ method: "POST",
344
+ path: "/api/v1/credits",
345
+ body: input
346
+ });
347
+ }
348
+ /** Fetch one credit type with usage stats. `GET /api/v1/credits/:id`. */
349
+ async get(id) {
350
+ const res = await this.transport.request({
351
+ method: "GET",
352
+ path: `/api/v1/credits/${encodeURIComponent(id)}`
353
+ });
354
+ return res.credit;
355
+ }
356
+ /** Update a credit type's definition. `PATCH /api/v1/credits/:id`. */
357
+ async update(id, input) {
358
+ await this.transport.request({
359
+ method: "PATCH",
360
+ path: `/api/v1/credits/${encodeURIComponent(id)}`,
361
+ body: input
362
+ });
363
+ }
364
+ /** Activate or deactivate a credit type. */
365
+ async setActive(id, isActive) {
366
+ const res = await this.transport.request({
367
+ method: "PATCH",
368
+ path: `/api/v1/credits/${encodeURIComponent(id)}`,
369
+ body: { isActive }
370
+ });
371
+ return res.credit;
372
+ }
373
+ /** Delete a credit type. Refused while a customer is linked to it. */
374
+ async delete(id) {
375
+ await this.transport.request({
376
+ method: "DELETE",
377
+ path: `/api/v1/credits/${encodeURIComponent(id)}`
378
+ });
379
+ }
380
+ };
381
+
336
382
  // src/resources/customers.ts
337
383
  var Customers = class {
338
384
  constructor(transport) {
@@ -424,6 +470,282 @@ var Customers = class {
424
470
  query: { range: params.range }
425
471
  });
426
472
  }
473
+ // --- Members -------------------------------------------------------------
474
+ /** List a customer's members. `GET …/:id/members`. */
475
+ async listMembers(id) {
476
+ const res = await this.transport.request({
477
+ method: "GET",
478
+ path: `/api/v1/customers/${encodeURIComponent(id)}/members`
479
+ });
480
+ return res.members;
481
+ }
482
+ /** Add a member to a customer. Duplicate `employeeId` → ConflictError. */
483
+ async addMember(id, input) {
484
+ const res = await this.transport.request({
485
+ method: "POST",
486
+ path: `/api/v1/customers/${encodeURIComponent(id)}/members`,
487
+ body: input
488
+ });
489
+ return res.member;
490
+ }
491
+ /** Update a member's profile (role is fixed). `PATCH …/:id/members/:memberId`. */
492
+ async updateMember(id, memberId, input) {
493
+ const res = await this.transport.request({
494
+ method: "PATCH",
495
+ path: `/api/v1/customers/${encodeURIComponent(id)}/members/${encodeURIComponent(memberId)}`,
496
+ body: input
497
+ });
498
+ return res.member;
499
+ }
500
+ /** Remove a member from a customer. `DELETE …/:id/members/:memberId`. */
501
+ async removeMember(id, memberId) {
502
+ await this.transport.request({
503
+ method: "DELETE",
504
+ path: `/api/v1/customers/${encodeURIComponent(id)}/members/${encodeURIComponent(memberId)}`
505
+ });
506
+ }
507
+ // --- Wallet & billing config --------------------------------------------
508
+ /** The customer's wallet balance + transactions. `GET …/:id/wallet`. */
509
+ wallet(id, params = {}) {
510
+ return this.transport.request({
511
+ method: "GET",
512
+ path: `/api/v1/customers/${encodeURIComponent(id)}/wallet`,
513
+ query: { limit: params.limit }
514
+ });
515
+ }
516
+ /**
517
+ * Record a manual wallet entry (top-up or deduction). `amount` is always
518
+ * positive; the sign comes from `type`. `POST …/:id/wallet`.
519
+ */
520
+ async addWalletEntry(id, input) {
521
+ return this.transport.request({
522
+ method: "POST",
523
+ path: `/api/v1/customers/${encodeURIComponent(id)}/wallet`,
524
+ body: input
525
+ });
526
+ }
527
+ /** Set the customer's currency. `PUT …/:id/currency`. */
528
+ async setCurrency(id, currencyCode) {
529
+ await this.transport.request({
530
+ method: "PUT",
531
+ path: `/api/v1/customers/${encodeURIComponent(id)}/currency`,
532
+ body: { currencyCode }
533
+ });
534
+ }
535
+ /** Set which credit types the customer is billed against. `PUT …/:id/credits`. */
536
+ async setCreditTypes(id, creditIds) {
537
+ await this.transport.request({
538
+ method: "PUT",
539
+ path: `/api/v1/customers/${encodeURIComponent(id)}/credits`,
540
+ body: { creditIds }
541
+ });
542
+ }
543
+ // --- Balance adjustments -------------------------------------------------
544
+ /** Grant or claw back a credit balance. `POST …/:id/credits/adjust`. */
545
+ async adjustCredit(id, input) {
546
+ await this.transport.request({
547
+ method: "POST",
548
+ path: `/api/v1/customers/${encodeURIComponent(id)}/credits/adjust`,
549
+ body: input
550
+ });
551
+ }
552
+ /** Grant or claw back an outcome balance. Returns the remaining balance. */
553
+ async adjustOutcome(id, input) {
554
+ const res = await this.transport.request({
555
+ method: "POST",
556
+ path: `/api/v1/customers/${encodeURIComponent(id)}/outcomes/adjust`,
557
+ body: input
558
+ });
559
+ return { remaining: res.remaining };
560
+ }
561
+ /** Grant or claw back a unit balance. Returns the remaining balance. */
562
+ async adjustUnit(id, input) {
563
+ const res = await this.transport.request({
564
+ method: "POST",
565
+ path: `/api/v1/customers/${encodeURIComponent(id)}/units/adjust`,
566
+ body: input
567
+ });
568
+ return { remaining: res.remaining };
569
+ }
570
+ };
571
+
572
+ // src/resources/invoices.ts
573
+ var Invoices = class {
574
+ constructor(transport) {
575
+ this.transport = transport;
576
+ }
577
+ transport;
578
+ /** List invoices, most-recent first. Filter by customer and/or status. */
579
+ async list(params = {}) {
580
+ const res = await this.transport.request({
581
+ method: "GET",
582
+ path: "/api/v1/invoices",
583
+ query: {
584
+ customerId: params.customerId,
585
+ status: params.status,
586
+ limit: params.limit
587
+ }
588
+ });
589
+ return res.invoices;
590
+ }
591
+ /** Fetch one full invoice + its plan snapshot. `GET /api/v1/invoices/:id`. */
592
+ get(id) {
593
+ return this.transport.request({
594
+ method: "GET",
595
+ path: `/api/v1/invoices/${encodeURIComponent(id)}`
596
+ });
597
+ }
598
+ /**
599
+ * Mint a hosted pay link for an OPEN invoice. PAID/VOID invoices are rejected
600
+ * with a `ConflictError`. `GET /api/v1/invoices/:id/pay-link`.
601
+ */
602
+ payLink(id) {
603
+ return this.transport.request({
604
+ method: "GET",
605
+ path: `/api/v1/invoices/${encodeURIComponent(id)}/pay-link`
606
+ });
607
+ }
608
+ };
609
+
610
+ // src/resources/outcomes.ts
611
+ var Outcomes = class {
612
+ constructor(transport) {
613
+ this.transport = transport;
614
+ }
615
+ transport;
616
+ /** List outcome types. Pass `{ active: true }` to only return active ones. */
617
+ async list(params = {}) {
618
+ const res = await this.transport.request({
619
+ method: "GET",
620
+ path: "/api/v1/outcomes",
621
+ query: { active: params.active === void 0 ? void 0 : String(params.active) }
622
+ });
623
+ return res.outcomes;
624
+ }
625
+ /** Create an outcome type with its steps. `POST /api/v1/outcomes`. */
626
+ async create(input) {
627
+ const res = await this.transport.request({
628
+ method: "POST",
629
+ path: "/api/v1/outcomes",
630
+ body: input
631
+ });
632
+ return res.outcome;
633
+ }
634
+ /** Fetch one outcome type with steps + stats. `GET /api/v1/outcomes/:id`. */
635
+ async get(id) {
636
+ const res = await this.transport.request({
637
+ method: "GET",
638
+ path: `/api/v1/outcomes/${encodeURIComponent(id)}`
639
+ });
640
+ return res.outcome;
641
+ }
642
+ /** Update an outcome type's definition. `PATCH /api/v1/outcomes/:id`. */
643
+ async update(id, input) {
644
+ const res = await this.transport.request({
645
+ method: "PATCH",
646
+ path: `/api/v1/outcomes/${encodeURIComponent(id)}`,
647
+ body: input
648
+ });
649
+ return res.outcome;
650
+ }
651
+ /** Activate or deactivate an outcome type. */
652
+ async setActive(id, isActive) {
653
+ await this.transport.request({
654
+ method: "PATCH",
655
+ path: `/api/v1/outcomes/${encodeURIComponent(id)}`,
656
+ body: { isActive }
657
+ });
658
+ }
659
+ /** Delete an outcome type. */
660
+ async delete(id) {
661
+ await this.transport.request({
662
+ method: "DELETE",
663
+ path: `/api/v1/outcomes/${encodeURIComponent(id)}`
664
+ });
665
+ }
666
+ };
667
+
668
+ // src/resources/payments.ts
669
+ var Payments = class {
670
+ constructor(transport) {
671
+ this.transport = transport;
672
+ }
673
+ transport;
674
+ /** List payments (paid invoices), most-recent first. Filter by customer. */
675
+ async list(params = {}) {
676
+ const res = await this.transport.request({
677
+ method: "GET",
678
+ path: "/api/v1/payments",
679
+ query: { customerId: params.customerId, limit: params.limit }
680
+ });
681
+ return res.payments;
682
+ }
683
+ /** Fetch one payment + its plan snapshot. `GET /api/v1/payments/:id`. */
684
+ get(id) {
685
+ return this.transport.request({
686
+ method: "GET",
687
+ path: `/api/v1/payments/${encodeURIComponent(id)}`
688
+ });
689
+ }
690
+ };
691
+
692
+ // src/resources/plans.ts
693
+ var Plans = class {
694
+ constructor(transport) {
695
+ this.transport = transport;
696
+ }
697
+ transport;
698
+ /** List plans, oldest-first. Pass `{ active: true }` to only return active plans. */
699
+ async list(params = {}) {
700
+ const res = await this.transport.request({
701
+ method: "GET",
702
+ path: "/api/v1/plans",
703
+ query: { active: params.active === void 0 ? void 0 : String(params.active) }
704
+ });
705
+ return res.plans;
706
+ }
707
+ /** Create a plan. `POST /api/v1/plans`. */
708
+ async create(input) {
709
+ const res = await this.transport.request({
710
+ method: "POST",
711
+ path: "/api/v1/plans",
712
+ body: input
713
+ });
714
+ return res.plan;
715
+ }
716
+ /** Fetch one plan with aggregate subscriber/invoice stats. `GET /api/v1/plans/:id`. */
717
+ async get(id) {
718
+ const res = await this.transport.request({
719
+ method: "GET",
720
+ path: `/api/v1/plans/${encodeURIComponent(id)}`
721
+ });
722
+ return res.plan;
723
+ }
724
+ /** Replace a plan's definition (components + settings). `PATCH /api/v1/plans/:id`. */
725
+ async update(id, input) {
726
+ const res = await this.transport.request({
727
+ method: "PATCH",
728
+ path: `/api/v1/plans/${encodeURIComponent(id)}`,
729
+ body: input
730
+ });
731
+ return res.plan;
732
+ }
733
+ /** Activate or deactivate a plan without rewriting it. */
734
+ async setActive(id, isActive) {
735
+ const res = await this.transport.request({
736
+ method: "PATCH",
737
+ path: `/api/v1/plans/${encodeURIComponent(id)}`,
738
+ body: { isActive }
739
+ });
740
+ return res.plan;
741
+ }
742
+ /** Delete a plan. Refused once any customer has purchased it. */
743
+ async delete(id) {
744
+ await this.transport.request({
745
+ method: "DELETE",
746
+ path: `/api/v1/plans/${encodeURIComponent(id)}`
747
+ });
748
+ }
427
749
  };
428
750
 
429
751
  // src/resources/portal.ts
@@ -450,21 +772,61 @@ var Portal = class {
450
772
  }
451
773
  };
452
774
 
453
- // src/idempotency.ts
454
- function newIdempotencyKey() {
455
- const webcrypto = globalThis.crypto;
456
- if (webcrypto?.randomUUID) return webcrypto.randomUUID();
457
- const b = new Uint8Array(16);
458
- if (webcrypto?.getRandomValues) {
459
- webcrypto.getRandomValues(b);
460
- } else {
461
- for (let i = 0; i < 16; i++) b[i] = Math.floor(Math.random() * 256);
462
- }
463
- b[6] = b[6] & 15 | 64;
464
- b[8] = b[8] & 63 | 128;
465
- const hex = Array.from(b, (n) => n.toString(16).padStart(2, "0")).join("");
466
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
467
- }
775
+ // src/resources/purchases.ts
776
+ var Purchases = class {
777
+ constructor(transport) {
778
+ this.transport = transport;
779
+ }
780
+ transport;
781
+ /** List purchases, most-recent first. Filter by customer and/or status. */
782
+ async list(params = {}) {
783
+ const res = await this.transport.request({
784
+ method: "GET",
785
+ path: "/api/v1/purchases",
786
+ query: {
787
+ customerId: params.customerId,
788
+ status: params.status,
789
+ limit: params.limit
790
+ }
791
+ });
792
+ return res.purchases;
793
+ }
794
+ /** Subscribe a customer to a plan. `POST /api/v1/purchases`. */
795
+ async create(input) {
796
+ const res = await this.transport.request({
797
+ method: "POST",
798
+ path: "/api/v1/purchases",
799
+ body: input
800
+ });
801
+ return res.purchase;
802
+ }
803
+ /** Fetch one purchase. `GET /api/v1/purchases/:id`. */
804
+ async get(id) {
805
+ const res = await this.transport.request({
806
+ method: "GET",
807
+ path: `/api/v1/purchases/${encodeURIComponent(id)}`
808
+ });
809
+ return res.purchase;
810
+ }
811
+ /**
812
+ * Turn automatic billing on or off for a purchase. When pausing, an optional
813
+ * `pauseReason` is recorded. `PATCH /api/v1/purchases/:id`.
814
+ */
815
+ async setAutoPayment(id, autoPayment, opts = {}) {
816
+ return this.transport.request({
817
+ method: "PATCH",
818
+ path: `/api/v1/purchases/${encodeURIComponent(id)}`,
819
+ body: { autoPayment, pauseReason: opts.pauseReason }
820
+ });
821
+ }
822
+ /** Cancel a purchase (voids any open invoice). `POST /api/v1/purchases/:id/cancel`. */
823
+ async cancel(id) {
824
+ await this.transport.request({
825
+ method: "POST",
826
+ path: `/api/v1/purchases/${encodeURIComponent(id)}/cancel`
827
+ });
828
+ }
829
+ };
468
830
 
469
831
  // src/resources/signals.ts
470
832
  function signalToWire(signal) {
@@ -476,7 +838,6 @@ function signalToWire(signal) {
476
838
  if (signal.member) body.member = signal.member;
477
839
  if (signal.status) body.status = signal.status;
478
840
  if (signal.custom) body.custom = signal.custom;
479
- if (signal.idempotencyKey) body.idempotencyKey = signal.idempotencyKey;
480
841
  const t = signal.tokens;
481
842
  if (t) {
482
843
  if (t.input != null) body.inputTokens = t.input;
@@ -484,7 +845,7 @@ function signalToWire(signal) {
484
845
  if (t.cache != null) body.cacheTokens = t.cache;
485
846
  }
486
847
  if (signal.type === "credit" || signal.type === "outcome") {
487
- body.key = signal.key;
848
+ body.agentKey = signal.agentKey;
488
849
  }
489
850
  return body;
490
851
  }
@@ -498,21 +859,20 @@ var Signals = class {
498
859
  flusher;
499
860
  cfg;
500
861
  async track(signal, opts = {}) {
501
- const idempotencyKey = signal.idempotencyKey ?? newIdempotencyKey();
502
- const body = signalToWire({ ...signal, idempotencyKey });
862
+ const body = signalToWire(signal);
503
863
  const sendNow = opts.wait || this.cfg.mode === "sync";
504
864
  if (!sendNow) {
505
- this.flusher.enqueue({ body, idempotencyKey, signal });
506
- return { idempotencyKey, queued: true };
865
+ this.flusher.enqueue({ body, signal });
866
+ return { queued: true };
507
867
  }
508
868
  const res = await this.transport.request({
509
869
  method: "POST",
510
870
  path: "/api/v1/usage",
511
871
  body,
512
- idempotencyKey
872
+ // Best-effort metering opts into retries on transient failures.
873
+ retry: true
513
874
  });
514
875
  return {
515
- idempotencyKey,
516
876
  queued: false,
517
877
  usageLog: res.usageLog ?? null
518
878
  };
@@ -538,14 +898,147 @@ var Signals = class {
538
898
  });
539
899
  return { customer: res.customer, totals: res.totals, logs: res.logs };
540
900
  }
901
+ /**
902
+ * Record one unit-metered event (a fixed-price meter with no tokens), matched
903
+ * to a catalog unit by name. Sent immediately — unit events are not buffered.
904
+ * `POST /api/v1/units`.
905
+ */
906
+ async unit(input) {
907
+ return this.transport.request({
908
+ method: "POST",
909
+ path: "/api/v1/units",
910
+ body: {
911
+ customerId: input.customerId,
912
+ unit: input.unit,
913
+ ...input.member ? { member: input.member } : {}
914
+ },
915
+ // Best-effort metering opts into retries on transient failures.
916
+ retry: true
917
+ });
918
+ }
919
+ /** A customer's unit balances + recent unit events. `GET /api/v1/units`. */
920
+ unitUsage(params) {
921
+ return this.transport.request({
922
+ method: "GET",
923
+ path: "/api/v1/units",
924
+ query: { customerId: params.customerId, limit: params.limit }
925
+ });
926
+ }
927
+ };
928
+
929
+ // src/resources/units.ts
930
+ var Units = class {
931
+ constructor(transport) {
932
+ this.transport = transport;
933
+ }
934
+ transport;
935
+ /** List unit types. Pass `{ active: true }` to only return active ones. */
936
+ async list(params = {}) {
937
+ const res = await this.transport.request({
938
+ method: "GET",
939
+ path: "/api/v1/units-catalog",
940
+ query: { active: params.active === void 0 ? void 0 : String(params.active) }
941
+ });
942
+ return res.units;
943
+ }
944
+ /** Create a unit type. `POST /api/v1/units-catalog`. */
945
+ async create(input) {
946
+ const res = await this.transport.request({
947
+ method: "POST",
948
+ path: "/api/v1/units-catalog",
949
+ body: input
950
+ });
951
+ return res.unit;
952
+ }
953
+ /** Fetch one unit type with stats. `GET /api/v1/units-catalog/:id`. */
954
+ async get(id) {
955
+ const res = await this.transport.request({
956
+ method: "GET",
957
+ path: `/api/v1/units-catalog/${encodeURIComponent(id)}`
958
+ });
959
+ return res.unit;
960
+ }
961
+ /** Update a unit type's definition. `PATCH /api/v1/units-catalog/:id`. */
962
+ async update(id, input) {
963
+ const res = await this.transport.request({
964
+ method: "PATCH",
965
+ path: `/api/v1/units-catalog/${encodeURIComponent(id)}`,
966
+ body: input
967
+ });
968
+ return res.unit;
969
+ }
970
+ /** Activate or deactivate a unit type. */
971
+ async setActive(id, isActive) {
972
+ const res = await this.transport.request({
973
+ method: "PATCH",
974
+ path: `/api/v1/units-catalog/${encodeURIComponent(id)}`,
975
+ body: { isActive }
976
+ });
977
+ return res.unit;
978
+ }
979
+ /** Delete a unit type. */
980
+ async delete(id) {
981
+ await this.transport.request({
982
+ method: "DELETE",
983
+ path: `/api/v1/units-catalog/${encodeURIComponent(id)}`
984
+ });
985
+ }
986
+ };
987
+
988
+ // src/resources/webhooks.ts
989
+ var Webhooks = class {
990
+ constructor(transport) {
991
+ this.transport = transport;
992
+ }
993
+ transport;
994
+ /** List configured webhooks. `GET /api/v1/webhooks`. */
995
+ async list() {
996
+ const res = await this.transport.request({
997
+ method: "GET",
998
+ path: "/api/v1/webhooks"
999
+ });
1000
+ return res.webhooks;
1001
+ }
1002
+ /** Create or replace the webhook for a kind. `POST /api/v1/webhooks`. */
1003
+ async upsert(input) {
1004
+ const res = await this.transport.request({
1005
+ method: "POST",
1006
+ path: "/api/v1/webhooks",
1007
+ body: input
1008
+ });
1009
+ return res.webhook;
1010
+ }
1011
+ /** Delete the webhook for a kind (idempotent). `DELETE /api/v1/webhooks/:kind`. */
1012
+ async delete(kind) {
1013
+ await this.transport.request({
1014
+ method: "DELETE",
1015
+ path: `/api/v1/webhooks/${encodeURIComponent(kind)}`
1016
+ });
1017
+ }
541
1018
  };
542
1019
 
543
1020
  // src/client.ts
544
1021
  var ClockNext = class {
545
- /** Record + read usage signals. */
1022
+ /** Record + read usage signals (credit / wallet / outcome / unit). */
546
1023
  signals;
547
- /** Manage customers and read their insights. */
1024
+ /** Manage customers, their members, wallet, and balances. */
548
1025
  customers;
1026
+ /** Define and manage billing plans. */
1027
+ plans;
1028
+ /** Subscribe customers to plans (purchases). */
1029
+ purchases;
1030
+ /** Read invoices and mint hosted pay links. */
1031
+ invoices;
1032
+ /** Read payments (paid invoices). */
1033
+ payments;
1034
+ /** Manage the credit-type catalog. */
1035
+ credits;
1036
+ /** Manage the outcome-type catalog. */
1037
+ outcomes;
1038
+ /** Manage the unit-type catalog. */
1039
+ units;
1040
+ /** Configure threshold webhooks. */
1041
+ webhooks;
549
1042
  /** Mint customer-portal embed tokens. */
550
1043
  portal;
551
1044
  cfg;
@@ -561,6 +1054,14 @@ var ClockNext = class {
561
1054
  this.flusher = new Flusher(transport, this.cfg);
562
1055
  this.signals = new Signals(transport, this.flusher, this.cfg);
563
1056
  this.customers = new Customers(transport);
1057
+ this.plans = new Plans(transport);
1058
+ this.purchases = new Purchases(transport);
1059
+ this.invoices = new Invoices(transport);
1060
+ this.payments = new Payments(transport);
1061
+ this.credits = new Credits(transport);
1062
+ this.outcomes = new Outcomes(transport);
1063
+ this.units = new Units(transport);
1064
+ this.webhooks = new Webhooks(transport);
564
1065
  this.portal = new Portal(transport);
565
1066
  }
566
1067
  /** Number of signals currently buffered (async mode). */
@@ -577,6 +1078,6 @@ var ClockNext = class {
577
1078
  }
578
1079
  };
579
1080
 
580
- export { AllowanceError, AuthError, ClockNext, ClockNextError, ConflictError, NetworkError, NotFoundError, PlanError, RateLimitError, ServerError, ValidationError, newIdempotencyKey, signalToWire };
1081
+ export { AllowanceError, AuthError, ClockNext, ClockNextError, ConflictError, NetworkError, NotFoundError, PlanError, RateLimitError, ServerError, ValidationError, signalToWire };
581
1082
  //# sourceMappingURL=index.js.map
582
1083
  //# sourceMappingURL=index.js.map