@clocknext/sdk 0.13.0 → 0.15.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.cjs CHANGED
@@ -77,7 +77,8 @@ function translateFieldPaths(message) {
77
77
  }
78
78
  return out;
79
79
  }
80
- var ALLOWANCE_MESSAGE = /\binsufficient\b|\bno allowance left\b|\bno slots left\b|\boutcomes remaining\b|\btop up the (?:allowance|balance)\b/i;
80
+ var ALLOWANCE_MESSAGE = /\binsufficient\b|\bno allowance left\b|\bno slots left\b|\boutcomes remaining\b|\btop up the (?:allowance|balance)\b|\bbelow zero\b/i;
81
+ var IDEMPOTENCY_KEY_REUSED = /\bidempotency key was already used\b/i;
81
82
  function isAllowanceFailure(message, code) {
82
83
  if (code) return code === "ALLOWANCE_EXHAUSTED";
83
84
  return ALLOWANCE_MESSAGE.test(message);
@@ -101,6 +102,9 @@ function errorFromResponse(status, message, retryAfterMs, meta = {}) {
101
102
  });
102
103
  }
103
104
  if (status === 422) {
105
+ if (IDEMPOTENCY_KEY_REUSED.test(raw)) {
106
+ return new ConflictError(raw, { ...opts, retryable: false });
107
+ }
104
108
  return isAllowanceFailure(raw, code) ? new AllowanceError(raw, opts) : new PlanError(raw, opts);
105
109
  }
106
110
  if (status === 429) return new RateLimitError(raw, { retryAfterMs, idempotencyKey });
@@ -328,7 +332,7 @@ function sleep(ms) {
328
332
  }
329
333
 
330
334
  // src/http.ts
331
- var SDK_VERSION = "0.13.0" ;
335
+ var SDK_VERSION = "0.15.0" ;
332
336
  var Transport = class {
333
337
  constructor(cfg) {
334
338
  this.cfg = cfg;
@@ -432,105 +436,17 @@ var Transport = class {
432
436
  }
433
437
  };
434
438
 
435
- // src/filters.ts
436
- function activeQuery(active) {
437
- return active === true ? "true" : void 0;
438
- }
439
- function filterByActive(rows, active) {
440
- return active === false ? rows.filter((row) => !row.isActive) : rows;
439
+ // src/idempotency.ts
440
+ function newKey() {
441
+ const c = globalThis.crypto;
442
+ if (typeof c?.randomUUID === "function") return c.randomUUID();
443
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (ch) => {
444
+ const r = Math.random() * 16 | 0;
445
+ const v = ch === "x" ? r : r & 3 | 8;
446
+ return v.toString(16);
447
+ });
441
448
  }
442
449
 
443
- // src/resources/composites.ts
444
- var Composites = class {
445
- constructor(transport) {
446
- this.transport = transport;
447
- }
448
- transport;
449
- /** List composites. Pass `{ active: true }` for only the ones a signal can
450
- * still be tagged with — the default list INCLUDES archived ones, which
451
- * keep every row they already own.
452
- * `{ active: false }` returns only archived ones; omitting it returns
453
- * both. See `activeQuery` — the API itself only supports `true`. */
454
- async list(params = {}) {
455
- const res = await this.transport.request({
456
- method: "GET",
457
- path: "/api/v1/composites",
458
- query: { active: activeQuery(params.active) }
459
- });
460
- return filterByActive(res.composites, params.active);
461
- }
462
- /** Create a composite. `POST /api/v1/composites`. */
463
- async create(input) {
464
- const res = await this.transport.request({
465
- method: "POST",
466
- path: "/api/v1/composites",
467
- body: input
468
- });
469
- return res.composite;
470
- }
471
- };
472
-
473
- // src/resources/credits.ts
474
- var Credits = class {
475
- constructor(transport) {
476
- this.transport = transport;
477
- }
478
- transport;
479
- /** List credit types. Pass `{ active: true }` to only return active ones.
480
- * `{ active: false }` returns only archived ones; omitting it returns
481
- * both. See `activeQuery` — the API itself only supports `true`. */
482
- async list(params = {}) {
483
- const res = await this.transport.request({
484
- method: "GET",
485
- path: "/api/v1/credits",
486
- query: { active: activeQuery(params.active) }
487
- });
488
- return filterByActive(res.credits, params.active);
489
- }
490
- /** Create a credit type; returns the created credit. `POST /api/v1/credits`. */
491
- async create(input) {
492
- const res = await this.transport.request({
493
- method: "POST",
494
- path: "/api/v1/credits",
495
- body: input
496
- });
497
- return res.credit;
498
- }
499
- /** Fetch one credit type with usage stats. `GET /api/v1/credits/:id`. */
500
- async get(id) {
501
- const res = await this.transport.request({
502
- method: "GET",
503
- path: `/api/v1/credits/${encodeURIComponent(id)}`
504
- });
505
- return res.credit;
506
- }
507
- /** Update a credit type's definition; returns the updated credit. `PATCH /api/v1/credits/:id`. */
508
- async update(id, input) {
509
- const res = await this.transport.request({
510
- method: "PATCH",
511
- path: `/api/v1/credits/${encodeURIComponent(id)}`,
512
- body: input
513
- });
514
- return res.credit;
515
- }
516
- /** Activate or deactivate a credit type. */
517
- async setActive(id, isActive) {
518
- const res = await this.transport.request({
519
- method: "PATCH",
520
- path: `/api/v1/credits/${encodeURIComponent(id)}`,
521
- body: { isActive }
522
- });
523
- return res.credit;
524
- }
525
- /** Delete a credit type. Refused while a customer is linked to it. */
526
- async delete(id) {
527
- await this.transport.request({
528
- method: "DELETE",
529
- path: `/api/v1/credits/${encodeURIComponent(id)}`
530
- });
531
- }
532
- };
533
-
534
450
  // src/resources/customers.ts
535
451
  var Customers = class {
536
452
  constructor(transport) {
@@ -584,6 +500,36 @@ var Customers = class {
584
500
  });
585
501
  return res.customer;
586
502
  }
503
+ /**
504
+ * Fetch one customer by YOUR id for them (its `externalId`), instead of
505
+ * ours. Same result as {@link get}. `GET /api/v1/customers/ext/:externalId`.
506
+ * An unknown external id is a `NotFoundError`; only active customers match.
507
+ */
508
+ async getByExternalId(externalId) {
509
+ const res = await this.transport.request({
510
+ method: "GET",
511
+ path: `/api/v1/customers/ext/${encodeURIComponent(externalId)}`
512
+ });
513
+ return res.customer;
514
+ }
515
+ /** Update a customer (partial), addressed by its `externalId`. Same body as
516
+ * {@link update}. `PATCH /api/v1/customers/ext/:externalId`. */
517
+ async updateByExternalId(externalId, input) {
518
+ const res = await this.transport.request({
519
+ method: "PATCH",
520
+ path: `/api/v1/customers/ext/${encodeURIComponent(externalId)}`,
521
+ body: input
522
+ });
523
+ return res.customer;
524
+ }
525
+ /** {@link delete}, addressing the customer by its `externalId`.
526
+ * `DELETE /api/v1/customers/ext/:externalId`. */
527
+ async deleteByExternalId(externalId) {
528
+ await this.transport.request({
529
+ method: "DELETE",
530
+ path: `/api/v1/customers/ext/${encodeURIComponent(externalId)}`
531
+ });
532
+ }
587
533
  /** Delete a customer (members, invitations, usage logs cascade). */
588
534
  async delete(id) {
589
535
  await this.transport.request({
@@ -591,15 +537,6 @@ var Customers = class {
591
537
  path: `/api/v1/customers/${encodeURIComponent(id)}`
592
538
  });
593
539
  }
594
- /** A customer's usage history. `GET /api/v1/usage?customerId=`. */
595
- async usage(id, params = {}) {
596
- const res = await this.transport.request({
597
- method: "GET",
598
- path: `/api/v1/usage`,
599
- query: { customerId: id, limit: params.limit }
600
- });
601
- return { customer: res.customer, totals: res.totals, logs: res.logs };
602
- }
603
540
  /** Wallet / credit / outcome / unit balances. `GET …/:id/balances`. */
604
541
  balances(id) {
605
542
  return this.transport.request({
@@ -607,6 +544,14 @@ var Customers = class {
607
544
  path: `/api/v1/customers/${encodeURIComponent(id)}/balances`
608
545
  });
609
546
  }
547
+ /** {@link balances}, addressing the customer by its `externalId`.
548
+ * `GET /api/v1/customers/ext/:externalId/balances`. */
549
+ balancesByExternalId(externalId) {
550
+ return this.transport.request({
551
+ method: "GET",
552
+ path: `/api/v1/customers/ext/${encodeURIComponent(externalId)}/balances`
553
+ });
554
+ }
610
555
  /**
611
556
  * One credit balance for a customer, matched by `creditId` or `creditName`;
612
557
  * `null` when the customer has no plan or no matching credit. Convenience
@@ -645,6 +590,14 @@ var Customers = class {
645
590
  path: `/api/v1/customers/${encodeURIComponent(id)}/plan`
646
591
  });
647
592
  }
593
+ /** {@link plan}, addressing the customer by its `externalId`.
594
+ * `GET /api/v1/customers/ext/:externalId/plan`. */
595
+ planByExternalId(externalId) {
596
+ return this.transport.request({
597
+ method: "GET",
598
+ path: `/api/v1/customers/ext/${encodeURIComponent(externalId)}/plan`
599
+ });
600
+ }
648
601
  // --- Members -------------------------------------------------------------
649
602
  /** List a customer's members. `GET …/:id/members`. */
650
603
  async listMembers(id) {
@@ -680,72 +633,129 @@ var Customers = class {
680
633
  });
681
634
  return res.member;
682
635
  }
683
- // --- Wallet & billing config --------------------------------------------
684
- /** The customer's wallet balance + transactions. `GET …/:id/wallet`. */
685
- wallet(id, params = {}) {
686
- return this.transport.request({
687
- method: "GET",
688
- path: `/api/v1/customers/${encodeURIComponent(id)}/wallet`,
689
- query: { limit: params.limit }
690
- });
691
- }
692
- /**
693
- * Record a manual wallet entry (top-up or deduction). `amount` is always
694
- * positive; the sign comes from `type`. `POST …/:id/wallet`.
695
- */
696
- async addWalletEntry(id, input) {
697
- return this.transport.request({
698
- method: "POST",
699
- path: `/api/v1/customers/${encodeURIComponent(id)}/wallet`,
700
- body: input
701
- });
702
- }
636
+ // --- Billing config -----------------------------------------------------
703
637
  // Note: a customer's currency is set through `update(id, { currencyCode })`
704
638
  // (PATCH …/:id) — there is no separate currency endpoint. Credit-type
705
639
  // assignment has no public API.
706
640
  // --- Balance adjustments -------------------------------------------------
707
- /** Grant or claw back a credit balance. Returns the remaining balance.
708
- * `POST …/:id/credits/adjust`. */
709
- async adjustCredit(id, input) {
710
- const res = await this.transport.request({
711
- method: "POST",
712
- path: `/api/v1/customers/${encodeURIComponent(id)}/credits/adjust`,
713
- body: input
714
- });
715
- return { remaining: res.remaining };
641
+ /** Grant or claw back a credit balance. The credit must be a prepaid part of
642
+ * the customer's current plan. Pass `idempotencyKey` to make it safe to
643
+ * retry. `POST …/:id/credits/adjust`. */
644
+ adjustCredit(id, input) {
645
+ return this.adjust(`/api/v1/customers/${encodeURIComponent(id)}/credits/adjust`, input);
646
+ }
647
+ /** Grant or claw back an outcome balance. The outcome must be a prepaid part
648
+ * of the customer's current plan. `POST …/:id/outcomes/adjust`. */
649
+ adjustOutcome(id, input) {
650
+ return this.adjust(`/api/v1/customers/${encodeURIComponent(id)}/outcomes/adjust`, input);
651
+ }
652
+ /** Grant or claw back a unit balance. The unit must be a prepaid part of the
653
+ * customer's current plan. `POST …/:id/units/adjust`. */
654
+ adjustUnit(id, input) {
655
+ return this.adjust(`/api/v1/customers/${encodeURIComponent(id)}/units/adjust`, input);
656
+ }
657
+ /** Grant or claw back slots on a customer's ADVANCE composite pool for the
658
+ * current billing cycle. `remaining` is the resulting slot count.
659
+ * `POST …/:id/composites/adjust`. */
660
+ adjustComposite(id, input) {
661
+ return this.adjust(`/api/v1/customers/${encodeURIComponent(id)}/composites/adjust`, input);
716
662
  }
717
- /** Grant or claw back an outcome balance. Returns the remaining balance. */
718
- async adjustOutcome(id, input) {
663
+ /**
664
+ * Top up (positive `delta`) or draw down (negative `delta`) a customer's
665
+ * wallet, in USD — allowed whatever the customer's plan. `remaining` is the
666
+ * resulting USD balance. No `agentKey` — a customer has exactly one wallet.
667
+ * Pass `pool: "RESERVED"` to move the Reserved Wallet instead (then
668
+ * `remaining` is its balance). `POST …/:id/wallet/adjust`.
669
+ */
670
+ adjustWallet(id, input) {
671
+ return this.adjust(`/api/v1/customers/${encodeURIComponent(id)}/wallet/adjust`, input);
672
+ }
673
+ /** {@link adjustCredit}, addressing the customer by its `externalId` (`id`
674
+ * here). `POST /api/v1/customers/ext/:externalId/credits/adjust`. */
675
+ adjustCreditByExternalId(id, input) {
676
+ return this.adjust(`/api/v1/customers/ext/${encodeURIComponent(id)}/credits/adjust`, input);
677
+ }
678
+ /** {@link adjustOutcome}, addressing the customer by its `externalId` (`id`
679
+ * here). `POST /api/v1/customers/ext/:externalId/outcomes/adjust`. */
680
+ adjustOutcomeByExternalId(id, input) {
681
+ return this.adjust(`/api/v1/customers/ext/${encodeURIComponent(id)}/outcomes/adjust`, input);
682
+ }
683
+ /** {@link adjustUnit}, addressing the customer by its `externalId` (`id`
684
+ * here). `POST /api/v1/customers/ext/:externalId/units/adjust`. */
685
+ adjustUnitByExternalId(id, input) {
686
+ return this.adjust(`/api/v1/customers/ext/${encodeURIComponent(id)}/units/adjust`, input);
687
+ }
688
+ /** {@link adjustComposite}, addressing the customer by its `externalId`
689
+ * (`id` here). `POST /api/v1/customers/ext/:externalId/composites/adjust`. */
690
+ adjustCompositeByExternalId(id, input) {
691
+ return this.adjust(`/api/v1/customers/ext/${encodeURIComponent(id)}/composites/adjust`, input);
692
+ }
693
+ /** {@link adjustWallet}, addressing the customer by its `externalId` (`id`
694
+ * here). `POST /api/v1/customers/ext/:externalId/wallet/adjust`. */
695
+ adjustWalletByExternalId(id, input) {
696
+ return this.adjust(`/api/v1/customers/ext/${encodeURIComponent(id)}/wallet/adjust`, input);
697
+ }
698
+ /**
699
+ * The one body behind all ten adjust calls.
700
+ *
701
+ * Every adjustment carries an idempotency key — the caller's, or one
702
+ * generated here ONCE per call, exactly like signals. The server
703
+ * deduplicates on it, so a transient failure is safe to retry: the same key
704
+ * rides every attempt, and a retry of one that already landed comes back as
705
+ * `replayed: true` instead of adjusting again. The key is also copied onto
706
+ * any thrown error (`error.idempotencyKey`) so the caller can replay safely.
707
+ */
708
+ async adjust(path, input) {
709
+ const idempotencyKey = input.idempotencyKey ?? newKey();
719
710
  const res = await this.transport.request({
720
711
  method: "POST",
721
- path: `/api/v1/customers/${encodeURIComponent(id)}/outcomes/adjust`,
722
- body: input
712
+ path,
713
+ idempotencyKey,
714
+ body: { ...input, idempotencyKey },
715
+ retry: true
723
716
  });
724
- return { remaining: res.remaining };
717
+ const result = {
718
+ remaining: res.remaining,
719
+ replayed: res.replayed === true,
720
+ customerArchived: res.customerArchived === true
721
+ };
722
+ if (result.replayed && typeof res.adjustedAt === "string") {
723
+ result.adjustedAt = res.adjustedAt;
724
+ }
725
+ return result;
725
726
  }
726
- /** Grant or claw back a unit balance. Returns the remaining balance. */
727
- async adjustUnit(id, input) {
727
+ // --- Recurring grants ----------------------------------------------------
728
+ /**
729
+ * Change how much a customer is granted every billing cycle (wallet top-up,
730
+ * credits, outcomes, units or a composite pool). Any increase or decrease
731
+ * applies to their current balance immediately. `PUT …/:id/recurring-grant`.
732
+ */
733
+ async updateRecurringGrant(id, input) {
728
734
  const res = await this.transport.request({
729
- method: "POST",
730
- path: `/api/v1/customers/${encodeURIComponent(id)}/units/adjust`,
735
+ method: "PUT",
736
+ path: `/api/v1/customers/${encodeURIComponent(id)}/recurring-grant`,
731
737
  body: input
732
738
  });
733
- return { remaining: res.remaining };
739
+ return toRecurringGrantResult(res);
734
740
  }
735
- /**
736
- * Grant or claw back slots on a customer's ADVANCE composite pool for the
737
- * current billing cycle. Returns the resulting `remaining` slot count.
738
- * `POST …/:id/composites/adjust`.
739
- */
740
- async adjustComposite(id, input) {
741
+ /** {@link updateRecurringGrant}, addressing the customer by its
742
+ * `externalId`. `PUT /api/v1/customers/ext/:externalId/recurring-grant`. */
743
+ async updateRecurringGrantByExternalId(externalId, input) {
741
744
  const res = await this.transport.request({
742
- method: "POST",
743
- path: `/api/v1/customers/${encodeURIComponent(id)}/composites/adjust`,
745
+ method: "PUT",
746
+ path: `/api/v1/customers/ext/${encodeURIComponent(externalId)}/recurring-grant`,
744
747
  body: input
745
748
  });
746
- return { remaining: res.remaining };
749
+ return toRecurringGrantResult(res);
747
750
  }
748
751
  };
752
+ function toRecurringGrantResult(res) {
753
+ return {
754
+ kind: res.kind,
755
+ quantity: res.quantity,
756
+ ...res.balanceUsd !== void 0 ? { balanceUsd: res.balanceUsd } : {}
757
+ };
758
+ }
749
759
 
750
760
  // src/resources/invoices.ts
751
761
  var Invoices = class {
@@ -774,7 +784,7 @@ var Invoices = class {
774
784
  });
775
785
  }
776
786
  /**
777
- * Mint a hosted pay link for an OPEN invoice. PAID/VOID invoices are rejected
787
+ * Mint a hosted pay link for an OPEN invoice. PAID/VOID/AUTO_CLOSED invoices are rejected
778
788
  * with a `ConflictError`. `GET /api/v1/invoices/:id/pay-link`.
779
789
  */
780
790
  payLink(id) {
@@ -785,67 +795,6 @@ var Invoices = class {
785
795
  }
786
796
  };
787
797
 
788
- // src/resources/outcomes.ts
789
- var Outcomes = class {
790
- constructor(transport) {
791
- this.transport = transport;
792
- }
793
- transport;
794
- /** List outcome types. Pass `{ active: true }` to only return active ones.
795
- * `{ active: false }` returns only archived ones; omitting it returns
796
- * both. See `activeQuery` — the API itself only supports `true`. */
797
- async list(params = {}) {
798
- const res = await this.transport.request({
799
- method: "GET",
800
- path: "/api/v1/outcomes",
801
- query: { active: activeQuery(params.active) }
802
- });
803
- return filterByActive(res.outcomes, params.active);
804
- }
805
- /** Create an outcome type with its steps. `POST /api/v1/outcomes`. */
806
- async create(input) {
807
- const res = await this.transport.request({
808
- method: "POST",
809
- path: "/api/v1/outcomes",
810
- body: input
811
- });
812
- return res.outcome;
813
- }
814
- /** Fetch one outcome type with steps + stats. `GET /api/v1/outcomes/:id`. */
815
- async get(id) {
816
- const res = await this.transport.request({
817
- method: "GET",
818
- path: `/api/v1/outcomes/${encodeURIComponent(id)}`
819
- });
820
- return res.outcome;
821
- }
822
- /** Update an outcome type's definition. `PATCH /api/v1/outcomes/:id`. */
823
- async update(id, input) {
824
- const res = await this.transport.request({
825
- method: "PATCH",
826
- path: `/api/v1/outcomes/${encodeURIComponent(id)}`,
827
- body: input
828
- });
829
- return res.outcome;
830
- }
831
- /** Activate or deactivate an outcome type; returns the updated outcome. */
832
- async setActive(id, isActive) {
833
- const res = await this.transport.request({
834
- method: "PATCH",
835
- path: `/api/v1/outcomes/${encodeURIComponent(id)}`,
836
- body: { isActive }
837
- });
838
- return res.outcome;
839
- }
840
- /** Delete an outcome type. */
841
- async delete(id) {
842
- await this.transport.request({
843
- method: "DELETE",
844
- path: `/api/v1/outcomes/${encodeURIComponent(id)}`
845
- });
846
- }
847
- };
848
-
849
798
  // src/resources/payments.ts
850
799
  var Payments = class {
851
800
  constructor(transport) {
@@ -870,6 +819,14 @@ var Payments = class {
870
819
  }
871
820
  };
872
821
 
822
+ // src/filters.ts
823
+ function activeQuery(active) {
824
+ return active === true ? "true" : void 0;
825
+ }
826
+ function filterByActive(rows, active) {
827
+ return active === false ? rows.filter((row) => !row.isActive) : rows;
828
+ }
829
+
873
830
  // src/resources/plans.ts
874
831
  var Plans = class {
875
832
  constructor(transport) {
@@ -887,48 +844,6 @@ var Plans = class {
887
844
  });
888
845
  return filterByActive(res.plans, params.active);
889
846
  }
890
- /** Create a plan. `POST /api/v1/plans`. */
891
- async create(input) {
892
- const res = await this.transport.request({
893
- method: "POST",
894
- path: "/api/v1/plans",
895
- body: input
896
- });
897
- return res.plan;
898
- }
899
- /** Fetch one plan with aggregate subscriber/invoice stats. `GET /api/v1/plans/:id`. */
900
- async get(id) {
901
- const res = await this.transport.request({
902
- method: "GET",
903
- path: `/api/v1/plans/${encodeURIComponent(id)}`
904
- });
905
- return res.plan;
906
- }
907
- /** Replace a plan's definition (components + settings). `PATCH /api/v1/plans/:id`. */
908
- async update(id, input) {
909
- const res = await this.transport.request({
910
- method: "PATCH",
911
- path: `/api/v1/plans/${encodeURIComponent(id)}`,
912
- body: input
913
- });
914
- return res.plan;
915
- }
916
- /** Activate or deactivate a plan without rewriting it. */
917
- async setActive(id, isActive) {
918
- const res = await this.transport.request({
919
- method: "PATCH",
920
- path: `/api/v1/plans/${encodeURIComponent(id)}`,
921
- body: { isActive }
922
- });
923
- return res.plan;
924
- }
925
- /** Delete a plan. Refused once any customer has purchased it. */
926
- async delete(id) {
927
- await this.transport.request({
928
- method: "DELETE",
929
- path: `/api/v1/plans/${encodeURIComponent(id)}`
930
- });
931
- }
932
847
  };
933
848
 
934
849
  // src/resources/portal.ts
@@ -1011,15 +926,12 @@ var Purchases = class {
1011
926
  }
1012
927
  };
1013
928
 
1014
- // src/idempotency.ts
1015
- function newKey() {
1016
- const c = globalThis.crypto;
1017
- if (typeof c?.randomUUID === "function") return c.randomUUID();
1018
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (ch) => {
1019
- const r = Math.random() * 16 | 0;
1020
- const v = ch === "x" ? r : r & 3 | 8;
1021
- return v.toString(16);
1022
- });
929
+ // src/customer-ref.ts
930
+ function customerRefToWire(ref) {
931
+ return {
932
+ ...ref.customerId != null ? { customerId: ref.customerId } : {},
933
+ ...ref.externalId != null ? { externalId: ref.externalId } : {}
934
+ };
1023
935
  }
1024
936
 
1025
937
  // src/resources/signals.ts
@@ -1031,10 +943,11 @@ var SIGNAL_PATHS = {
1031
943
  var UNIT_PATH = "/api/v1/signal/unit";
1032
944
  var COMPOSITE_COMPLETE_PATH = "/api/v1/signal/composite/complete";
1033
945
  var OUTCOME_COMPLETE_PATH = "/api/v1/signal/outcome/complete";
946
+ var USAGE_LOG_PATH = "/api/v1/signal/usage-log";
1034
947
  function signalToWire(signal) {
1035
948
  const t = signal.tokens;
1036
949
  const body = {
1037
- customerId: signal.customerId,
950
+ ...customerRefToWire(signal),
1038
951
  // `usage` is required on all three model-priced kinds. input/output are
1039
952
  // always sent; `cacheTokens` only when provided (the server defaults 0).
1040
953
  usage: {
@@ -1145,9 +1058,8 @@ var Signals = class {
1145
1058
  path: OUTCOME_COMPLETE_PATH,
1146
1059
  idempotencyKey,
1147
1060
  body: {
1148
- customerId: input.customerId,
1061
+ ...customerRefToWire(input),
1149
1062
  runId: input.runId,
1150
- complete: true,
1151
1063
  ...input.member ? { member: input.member } : {},
1152
1064
  idempotencyKey
1153
1065
  },
@@ -1162,23 +1074,6 @@ var Signals = class {
1162
1074
  ...handles(res)
1163
1075
  };
1164
1076
  }
1165
- /**
1166
- * Recent usage logs + totals for a customer. `GET /api/v1/usage`.
1167
- *
1168
- * Still the legacy path, deliberately. The `/api/v1/signal/*` family is
1169
- * POST-only — it replaced how usage is INGESTED, not how it is read — so
1170
- * `GET /api/v1/usage` has no successor and is not deprecated. It is also the
1171
- * only way to prove a signal landed, which matters more now that ingestion
1172
- * answers `202` for everything.
1173
- */
1174
- async list(params) {
1175
- const res = await this.transport.request({
1176
- method: "GET",
1177
- path: "/api/v1/usage",
1178
- query: { customerId: params.customerId, limit: params.limit }
1179
- });
1180
- return { customer: res.customer, totals: res.totals, logs: res.logs };
1181
- }
1182
1077
  /**
1183
1078
  * Record one unit-metered event (a fixed-price meter with no tokens),
1184
1079
  * addressed to a catalog unit by its `agentKey`. Sent immediately — unit
@@ -1198,7 +1093,7 @@ var Signals = class {
1198
1093
  path: UNIT_PATH,
1199
1094
  idempotencyKey,
1200
1095
  body: {
1201
- customerId: input.customerId,
1096
+ ...customerRefToWire(input),
1202
1097
  agentKey: input.agentKey,
1203
1098
  ...input.quantity != null ? { quantity: input.quantity } : {},
1204
1099
  ...input.member ? { member: input.member } : {},
@@ -1255,7 +1150,7 @@ var Signals = class {
1255
1150
  path: COMPOSITE_COMPLETE_PATH,
1256
1151
  idempotencyKey,
1257
1152
  body: {
1258
- customerId: input.customerId,
1153
+ ...customerRefToWire(input),
1259
1154
  composite: { [input.composite.ref]: input.composite.value },
1260
1155
  // Auto-assigned like the others. A retry here could not double-BILL
1261
1156
  // even without it — closing a closed occurrence is a server-side
@@ -1277,147 +1172,45 @@ var Signals = class {
1277
1172
  };
1278
1173
  }
1279
1174
  /**
1280
- * A customer's unit balances + recent unit events. `GET /api/v1/units`.
1175
+ * One customer's usage history, merged from every meter kind — credit,
1176
+ * outcome, unit, wallet, and completed composite occurrences — into one
1177
+ * feed sorted by time. `GET /api/v1/signal/usage-log`. Exactly the same
1178
+ * rows the customer portal's own "Usage logs" table shows, so a caller can
1179
+ * build against this instead of the portal UI.
1180
+ *
1181
+ * Cursor-paginated, NOT page-numbered: `limit` + `cursor` in, `nextCursor`
1182
+ * out. Omit `cursor` for the first page; pass the previous page's
1183
+ * `nextCursor` back to fetch the next one, and stop once it comes back
1184
+ * `null`. Unlike offset pagination, the cost of a page never grows with how
1185
+ * deep you've paged — each page only asks for rows strictly before the
1186
+ * cursor's position.
1281
1187
  *
1282
- * Legacy path for the same reason as {@link list}: the signal family is
1283
- * POST-only, so the read side has no successor and is not deprecated.
1188
+ * Read-only; unlike {@link track}/{@link unit}/etc., there is nothing to
1189
+ * buffer or retry here.
1284
1190
  */
1285
- unitUsage(params) {
1191
+ usageLog(params) {
1286
1192
  return this.transport.request({
1287
1193
  method: "GET",
1288
- path: "/api/v1/units",
1289
- query: { customerId: params.customerId, limit: params.limit }
1290
- });
1291
- }
1292
- };
1293
-
1294
- // src/resources/units.ts
1295
- var Units = class {
1296
- constructor(transport) {
1297
- this.transport = transport;
1298
- }
1299
- transport;
1300
- /** List unit types. Pass `{ active: true }` to only return active ones.
1301
- * `{ active: false }` returns only archived ones; omitting it returns
1302
- * both. See `activeQuery` — the API itself only supports `true`. */
1303
- async list(params = {}) {
1304
- const res = await this.transport.request({
1305
- method: "GET",
1306
- path: "/api/v1/units-catalog",
1307
- query: { active: activeQuery(params.active) }
1308
- });
1309
- return filterByActive(res.units, params.active);
1310
- }
1311
- /** Create a unit type. `POST /api/v1/units-catalog`. */
1312
- async create(input) {
1313
- const res = await this.transport.request({
1314
- method: "POST",
1315
- path: "/api/v1/units-catalog",
1316
- body: input
1317
- });
1318
- return res.unit;
1319
- }
1320
- /** Fetch one unit type with stats. `GET /api/v1/units-catalog/:id`. */
1321
- async get(id) {
1322
- const res = await this.transport.request({
1323
- method: "GET",
1324
- path: `/api/v1/units-catalog/${encodeURIComponent(id)}`
1325
- });
1326
- return res.unit;
1327
- }
1328
- /** Update a unit type's definition. `PATCH /api/v1/units-catalog/:id`. */
1329
- async update(id, input) {
1330
- const res = await this.transport.request({
1331
- method: "PATCH",
1332
- path: `/api/v1/units-catalog/${encodeURIComponent(id)}`,
1333
- body: input
1334
- });
1335
- return res.unit;
1336
- }
1337
- /** Activate or deactivate a unit type. */
1338
- async setActive(id, isActive) {
1339
- const res = await this.transport.request({
1340
- method: "PATCH",
1341
- path: `/api/v1/units-catalog/${encodeURIComponent(id)}`,
1342
- body: { isActive }
1343
- });
1344
- return res.unit;
1345
- }
1346
- /** Delete a unit type. */
1347
- async delete(id) {
1348
- await this.transport.request({
1349
- method: "DELETE",
1350
- path: `/api/v1/units-catalog/${encodeURIComponent(id)}`
1351
- });
1352
- }
1353
- };
1354
-
1355
- // src/resources/webhooks.ts
1356
- var Webhooks = class {
1357
- constructor(transport) {
1358
- this.transport = transport;
1359
- }
1360
- transport;
1361
- /** List configured webhooks. `GET /api/v1/webhooks`. */
1362
- async list() {
1363
- const res = await this.transport.request({
1364
- method: "GET",
1365
- path: "/api/v1/webhooks"
1366
- });
1367
- return res.webhooks;
1368
- }
1369
- /** Create or replace the webhook for a kind. `POST /api/v1/webhooks`. */
1370
- async upsert(input) {
1371
- const res = await this.transport.request({
1372
- method: "POST",
1373
- path: "/api/v1/webhooks",
1374
- body: input
1375
- });
1376
- return res.webhook;
1377
- }
1378
- /** Delete the webhook for a kind (idempotent). `DELETE /api/v1/webhooks/:kind`. */
1379
- async delete(kind) {
1380
- await this.transport.request({
1381
- method: "DELETE",
1382
- path: `/api/v1/webhooks/${encodeURIComponent(kind)}`
1383
- });
1384
- }
1385
- };
1386
-
1387
- // src/resources/workspace.ts
1388
- var Workspace = class {
1389
- constructor(transport) {
1390
- this.transport = transport;
1391
- }
1392
- transport;
1393
- /** Identify the org + key behind the request, incl. `sandbox` | `live`. */
1394
- me() {
1395
- return this.transport.request({
1396
- method: "GET",
1397
- path: "/api/v1/me"
1398
- });
1399
- }
1400
- /** The org's enabled models with live prices. `{ active: true }` returns only
1401
- * the models that can be metered right now.
1402
- * `{ active: false }` returns only archived ones; omitting it returns
1403
- * both. See `activeQuery` — the API itself only supports `true`. */
1404
- async models(params = {}) {
1405
- const res = await this.transport.request({
1406
- method: "GET",
1407
- path: "/api/v1/models",
1408
- query: { active: activeQuery(params.active) }
1194
+ path: USAGE_LOG_PATH,
1195
+ query: {
1196
+ customerId: params.customerId,
1197
+ limit: params.limit,
1198
+ cursor: params.cursor,
1199
+ period: params.period,
1200
+ from: params.from,
1201
+ to: params.to
1202
+ }
1409
1203
  });
1410
- return filterByActive(res.models, params.active);
1411
1204
  }
1412
1205
  };
1413
1206
 
1414
1207
  // src/client.ts
1415
1208
  var ClockNext = class {
1416
- /** Record + read usage signals (credit / wallet / outcome / unit). */
1209
+ /** Record usage signals (credit / wallet / outcome / unit) and read the usage log. */
1417
1210
  signals;
1418
- /** Manage customers, their members, wallet, and balances. */
1211
+ /** Manage customers, their members, and balances. */
1419
1212
  customers;
1420
- /** Define and manage billing plans. */
1213
+ /** List billing plans. */
1421
1214
  plans;
1422
1215
  /** Subscribe customers to plans (purchases). */
1423
1216
  purchases;
@@ -1425,20 +1218,8 @@ var ClockNext = class {
1425
1218
  invoices;
1426
1219
  /** Read payments (paid invoices). */
1427
1220
  payments;
1428
- /** Manage the credit-type catalog. */
1429
- credits;
1430
- /** Manage the composite catalog — bundles billed as one thing. */
1431
- composites;
1432
- /** Manage the outcome-type catalog. */
1433
- outcomes;
1434
- /** Manage the unit-type catalog. */
1435
- units;
1436
- /** Configure threshold webhooks. */
1437
- webhooks;
1438
1221
  /** Mint customer-portal embed tokens. */
1439
1222
  portal;
1440
- /** Identify the workspace (sandbox/live) + list its models. `/me`, `/models`. */
1441
- workspace;
1442
1223
  cfg;
1443
1224
  flusher;
1444
1225
  constructor(config) {
@@ -1456,13 +1237,7 @@ var ClockNext = class {
1456
1237
  this.purchases = new Purchases(transport);
1457
1238
  this.invoices = new Invoices(transport);
1458
1239
  this.payments = new Payments(transport);
1459
- this.credits = new Credits(transport);
1460
- this.composites = new Composites(transport);
1461
- this.outcomes = new Outcomes(transport);
1462
- this.units = new Units(transport);
1463
- this.webhooks = new Webhooks(transport);
1464
1240
  this.portal = new Portal(transport);
1465
- this.workspace = new Workspace(transport);
1466
1241
  }
1467
1242
  /** Number of signals currently buffered (async mode). */
1468
1243
  get pending() {