@clocknext/sdk 0.1.0 → 0.4.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/README.md +140 -23
- package/dist/index.cjs +602 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +915 -62
- package/dist/index.d.ts +915 -62
- package/dist/index.js +603 -58
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
// src/config.ts
|
|
4
|
-
var DEFAULT_BASE_URL = "https://
|
|
4
|
+
var DEFAULT_BASE_URL = "https://payments.clocknext.com";
|
|
5
5
|
function num(value, fallback, min) {
|
|
6
6
|
return typeof value === "number" && Number.isFinite(value) && value >= min ? value : fallback;
|
|
7
7
|
}
|
|
@@ -123,7 +123,8 @@ var Flusher = class {
|
|
|
123
123
|
method: "POST",
|
|
124
124
|
path: "/api/v1/usage",
|
|
125
125
|
body: item.body,
|
|
126
|
-
|
|
126
|
+
// Best-effort metering opts into retries on transient failures.
|
|
127
|
+
retry: true
|
|
127
128
|
});
|
|
128
129
|
return true;
|
|
129
130
|
} catch (error) {
|
|
@@ -146,15 +147,12 @@ var Flusher = class {
|
|
|
146
147
|
var ClockNextError = class extends Error {
|
|
147
148
|
/** HTTP status, when the failure came from a server response. */
|
|
148
149
|
status;
|
|
149
|
-
/** The idempotency key of the offending signal, when applicable. */
|
|
150
|
-
idempotencyKey;
|
|
151
150
|
/** Whether the SDK considers this failure worth retrying. */
|
|
152
151
|
retryable;
|
|
153
152
|
constructor(message, opts = {}) {
|
|
154
153
|
super(message);
|
|
155
154
|
this.name = new.target.name;
|
|
156
155
|
this.status = opts.status;
|
|
157
|
-
this.idempotencyKey = opts.idempotencyKey;
|
|
158
156
|
this.retryable = opts.retryable ?? false;
|
|
159
157
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
160
158
|
}
|
|
@@ -174,28 +172,29 @@ var ConflictError = class extends ClockNextError {
|
|
|
174
172
|
var RateLimitError = class extends ClockNextError {
|
|
175
173
|
retryAfterMs;
|
|
176
174
|
constructor(message, opts = {}) {
|
|
177
|
-
super(message, { status: 429,
|
|
175
|
+
super(message, { status: 429, retryable: true });
|
|
178
176
|
this.retryAfterMs = opts.retryAfterMs;
|
|
179
177
|
}
|
|
180
178
|
};
|
|
181
179
|
var ServerError = class extends ClockNextError {
|
|
182
180
|
};
|
|
183
181
|
var NetworkError = class extends ClockNextError {
|
|
184
|
-
constructor(message
|
|
185
|
-
super(message, {
|
|
182
|
+
constructor(message) {
|
|
183
|
+
super(message, { retryable: true });
|
|
186
184
|
}
|
|
187
185
|
};
|
|
188
|
-
function errorFromResponse(status, message,
|
|
186
|
+
function errorFromResponse(status, message, retryAfterMs) {
|
|
189
187
|
const msg = message || `Request failed with status ${status}.`;
|
|
190
|
-
const opts = { status
|
|
188
|
+
const opts = { status };
|
|
191
189
|
if (status === 400) return new ValidationError(msg, opts);
|
|
192
190
|
if (status === 401) return new AuthError(msg, opts);
|
|
193
191
|
if (status === 404) return new NotFoundError(msg, opts);
|
|
192
|
+
if (status === 408) return new ClockNextError(msg, { ...opts, retryable: true });
|
|
194
193
|
if (status === 409) return new ConflictError(msg, { ...opts, retryable: true });
|
|
195
194
|
if (status === 422) {
|
|
196
195
|
return /insufficient/i.test(msg) ? new AllowanceError(msg, opts) : new PlanError(msg, opts);
|
|
197
196
|
}
|
|
198
|
-
if (status === 429) return new RateLimitError(msg, {
|
|
197
|
+
if (status === 429) return new RateLimitError(msg, { retryAfterMs });
|
|
199
198
|
if (status >= 500) return new ServerError(msg, { ...opts, retryable: true });
|
|
200
199
|
return new ClockNextError(msg, opts);
|
|
201
200
|
}
|
|
@@ -230,7 +229,7 @@ function sleep(ms) {
|
|
|
230
229
|
}
|
|
231
230
|
|
|
232
231
|
// src/http.ts
|
|
233
|
-
var SDK_VERSION = "0.
|
|
232
|
+
var SDK_VERSION = "0.4.0" ;
|
|
234
233
|
var Transport = class {
|
|
235
234
|
constructor(cfg) {
|
|
236
235
|
this.cfg = cfg;
|
|
@@ -238,8 +237,9 @@ var Transport = class {
|
|
|
238
237
|
cfg;
|
|
239
238
|
async request(opts) {
|
|
240
239
|
const url = this.buildUrl(opts.path, opts.query);
|
|
241
|
-
const replaySafe = opts.method === "GET" || opts.method === "
|
|
242
|
-
const
|
|
240
|
+
const replaySafe = opts.method === "GET" || opts.method === "PUT" || opts.method === "DELETE";
|
|
241
|
+
const allowRetry = opts.retry ?? replaySafe;
|
|
242
|
+
const maxAttempts = allowRetry ? this.cfg.retry.maxAttempts : 1;
|
|
243
243
|
let attempt = 0;
|
|
244
244
|
while (true) {
|
|
245
245
|
attempt++;
|
|
@@ -273,7 +273,7 @@ var Transport = class {
|
|
|
273
273
|
});
|
|
274
274
|
} catch (err) {
|
|
275
275
|
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)}`;
|
|
276
|
-
throw new NetworkError(message2
|
|
276
|
+
throw new NetworkError(message2);
|
|
277
277
|
} finally {
|
|
278
278
|
clearTimeout(timer);
|
|
279
279
|
}
|
|
@@ -293,24 +293,14 @@ var Transport = class {
|
|
|
293
293
|
return obj.result ?? {};
|
|
294
294
|
}
|
|
295
295
|
const message2 = detail?.message ?? `Request failed with status ${res.status}.`;
|
|
296
|
-
throw errorFromResponse(
|
|
297
|
-
res.status,
|
|
298
|
-
message2,
|
|
299
|
-
opts.idempotencyKey,
|
|
300
|
-
parseRetryAfter(res.headers)
|
|
301
|
-
);
|
|
296
|
+
throw errorFromResponse(res.status, message2, parseRetryAfter(res.headers));
|
|
302
297
|
}
|
|
303
298
|
const envelope = obj;
|
|
304
299
|
if (res.ok && envelope.ok !== false) {
|
|
305
300
|
return envelope;
|
|
306
301
|
}
|
|
307
302
|
const message = envelope.error ?? text ?? `Request failed with status ${res.status}.`;
|
|
308
|
-
throw errorFromResponse(
|
|
309
|
-
res.status,
|
|
310
|
-
message,
|
|
311
|
-
opts.idempotencyKey,
|
|
312
|
-
parseRetryAfter(res.headers)
|
|
313
|
-
);
|
|
303
|
+
throw errorFromResponse(res.status, message, parseRetryAfter(res.headers));
|
|
314
304
|
}
|
|
315
305
|
headers(opts) {
|
|
316
306
|
const h = {
|
|
@@ -318,7 +308,6 @@ var Transport = class {
|
|
|
318
308
|
"User-Agent": `clocknext-sdk-js/${SDK_VERSION}`
|
|
319
309
|
};
|
|
320
310
|
if (opts.body !== void 0) h["Content-Type"] = "application/json";
|
|
321
|
-
if (opts.idempotencyKey) h["Idempotency-Key"] = opts.idempotencyKey;
|
|
322
311
|
return h;
|
|
323
312
|
}
|
|
324
313
|
buildUrl(path, query) {
|
|
@@ -335,6 +324,63 @@ var Transport = class {
|
|
|
335
324
|
}
|
|
336
325
|
};
|
|
337
326
|
|
|
327
|
+
// src/resources/credits.ts
|
|
328
|
+
var Credits = class {
|
|
329
|
+
constructor(transport) {
|
|
330
|
+
this.transport = transport;
|
|
331
|
+
}
|
|
332
|
+
transport;
|
|
333
|
+
/** List credit types. Pass `{ active: true }` to only return active ones. */
|
|
334
|
+
async list(params = {}) {
|
|
335
|
+
const res = await this.transport.request({
|
|
336
|
+
method: "GET",
|
|
337
|
+
path: "/api/v1/credits",
|
|
338
|
+
query: { active: params.active === void 0 ? void 0 : String(params.active) }
|
|
339
|
+
});
|
|
340
|
+
return res.credits;
|
|
341
|
+
}
|
|
342
|
+
/** Create a credit type. `POST /api/v1/credits`. */
|
|
343
|
+
async create(input) {
|
|
344
|
+
await this.transport.request({
|
|
345
|
+
method: "POST",
|
|
346
|
+
path: "/api/v1/credits",
|
|
347
|
+
body: input
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
/** Fetch one credit type with usage stats. `GET /api/v1/credits/:id`. */
|
|
351
|
+
async get(id) {
|
|
352
|
+
const res = await this.transport.request({
|
|
353
|
+
method: "GET",
|
|
354
|
+
path: `/api/v1/credits/${encodeURIComponent(id)}`
|
|
355
|
+
});
|
|
356
|
+
return res.credit;
|
|
357
|
+
}
|
|
358
|
+
/** Update a credit type's definition. `PATCH /api/v1/credits/:id`. */
|
|
359
|
+
async update(id, input) {
|
|
360
|
+
await this.transport.request({
|
|
361
|
+
method: "PATCH",
|
|
362
|
+
path: `/api/v1/credits/${encodeURIComponent(id)}`,
|
|
363
|
+
body: input
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
/** Activate or deactivate a credit type. */
|
|
367
|
+
async setActive(id, isActive) {
|
|
368
|
+
const res = await this.transport.request({
|
|
369
|
+
method: "PATCH",
|
|
370
|
+
path: `/api/v1/credits/${encodeURIComponent(id)}`,
|
|
371
|
+
body: { isActive }
|
|
372
|
+
});
|
|
373
|
+
return res.credit;
|
|
374
|
+
}
|
|
375
|
+
/** Delete a credit type. Refused while a customer is linked to it. */
|
|
376
|
+
async delete(id) {
|
|
377
|
+
await this.transport.request({
|
|
378
|
+
method: "DELETE",
|
|
379
|
+
path: `/api/v1/credits/${encodeURIComponent(id)}`
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
|
|
338
384
|
// src/resources/customers.ts
|
|
339
385
|
var Customers = class {
|
|
340
386
|
constructor(transport) {
|
|
@@ -426,6 +472,269 @@ var Customers = class {
|
|
|
426
472
|
query: { range: params.range }
|
|
427
473
|
});
|
|
428
474
|
}
|
|
475
|
+
// --- Members -------------------------------------------------------------
|
|
476
|
+
/** List a customer's members. `GET …/:id/members`. */
|
|
477
|
+
async listMembers(id) {
|
|
478
|
+
const res = await this.transport.request({
|
|
479
|
+
method: "GET",
|
|
480
|
+
path: `/api/v1/customers/${encodeURIComponent(id)}/members`
|
|
481
|
+
});
|
|
482
|
+
return res.members;
|
|
483
|
+
}
|
|
484
|
+
/** Add a member to a customer. Duplicate `employeeId` → ConflictError. */
|
|
485
|
+
async addMember(id, input) {
|
|
486
|
+
const res = await this.transport.request({
|
|
487
|
+
method: "POST",
|
|
488
|
+
path: `/api/v1/customers/${encodeURIComponent(id)}/members`,
|
|
489
|
+
body: input
|
|
490
|
+
});
|
|
491
|
+
return res.member;
|
|
492
|
+
}
|
|
493
|
+
/** Update a member's profile (role is fixed). `PATCH …/:id/members/:memberId`. */
|
|
494
|
+
async updateMember(id, memberId, input) {
|
|
495
|
+
const res = await this.transport.request({
|
|
496
|
+
method: "PATCH",
|
|
497
|
+
path: `/api/v1/customers/${encodeURIComponent(id)}/members/${encodeURIComponent(memberId)}`,
|
|
498
|
+
body: input
|
|
499
|
+
});
|
|
500
|
+
return res.member;
|
|
501
|
+
}
|
|
502
|
+
/** Remove a member from a customer. `DELETE …/:id/members/:memberId`. */
|
|
503
|
+
async removeMember(id, memberId) {
|
|
504
|
+
await this.transport.request({
|
|
505
|
+
method: "DELETE",
|
|
506
|
+
path: `/api/v1/customers/${encodeURIComponent(id)}/members/${encodeURIComponent(memberId)}`
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
// --- Wallet & billing config --------------------------------------------
|
|
510
|
+
/** The customer's wallet balance + transactions. `GET …/:id/wallet`. */
|
|
511
|
+
wallet(id, params = {}) {
|
|
512
|
+
return this.transport.request({
|
|
513
|
+
method: "GET",
|
|
514
|
+
path: `/api/v1/customers/${encodeURIComponent(id)}/wallet`,
|
|
515
|
+
query: { limit: params.limit }
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Record a manual wallet entry (top-up or deduction). `amount` is always
|
|
520
|
+
* positive; the sign comes from `type`. `POST …/:id/wallet`.
|
|
521
|
+
*/
|
|
522
|
+
async addWalletEntry(id, input) {
|
|
523
|
+
return this.transport.request({
|
|
524
|
+
method: "POST",
|
|
525
|
+
path: `/api/v1/customers/${encodeURIComponent(id)}/wallet`,
|
|
526
|
+
body: input
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
// Note: a customer's currency is set through `update(id, { currencyCode })`
|
|
530
|
+
// (PATCH …/:id) — there is no separate currency endpoint. Credit-type
|
|
531
|
+
// assignment has no public API.
|
|
532
|
+
// --- Balance adjustments -------------------------------------------------
|
|
533
|
+
/** Grant or claw back a credit balance. `POST …/:id/credits/adjust`. */
|
|
534
|
+
async adjustCredit(id, input) {
|
|
535
|
+
await this.transport.request({
|
|
536
|
+
method: "POST",
|
|
537
|
+
path: `/api/v1/customers/${encodeURIComponent(id)}/credits/adjust`,
|
|
538
|
+
body: input
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
/** Grant or claw back an outcome balance. Returns the remaining balance. */
|
|
542
|
+
async adjustOutcome(id, input) {
|
|
543
|
+
const res = await this.transport.request({
|
|
544
|
+
method: "POST",
|
|
545
|
+
path: `/api/v1/customers/${encodeURIComponent(id)}/outcomes/adjust`,
|
|
546
|
+
body: input
|
|
547
|
+
});
|
|
548
|
+
return { remaining: res.remaining };
|
|
549
|
+
}
|
|
550
|
+
/** Grant or claw back a unit balance. Returns the remaining balance. */
|
|
551
|
+
async adjustUnit(id, input) {
|
|
552
|
+
const res = await this.transport.request({
|
|
553
|
+
method: "POST",
|
|
554
|
+
path: `/api/v1/customers/${encodeURIComponent(id)}/units/adjust`,
|
|
555
|
+
body: input
|
|
556
|
+
});
|
|
557
|
+
return { remaining: res.remaining };
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
// src/resources/invoices.ts
|
|
562
|
+
var Invoices = class {
|
|
563
|
+
constructor(transport) {
|
|
564
|
+
this.transport = transport;
|
|
565
|
+
}
|
|
566
|
+
transport;
|
|
567
|
+
/** List invoices, most-recent first. Filter by customer and/or status. */
|
|
568
|
+
async list(params = {}) {
|
|
569
|
+
const res = await this.transport.request({
|
|
570
|
+
method: "GET",
|
|
571
|
+
path: "/api/v1/invoices",
|
|
572
|
+
query: {
|
|
573
|
+
customerId: params.customerId,
|
|
574
|
+
status: params.status,
|
|
575
|
+
limit: params.limit
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
return res.invoices;
|
|
579
|
+
}
|
|
580
|
+
/** Fetch one full invoice + its plan snapshot. `GET /api/v1/invoices/:id`. */
|
|
581
|
+
get(id) {
|
|
582
|
+
return this.transport.request({
|
|
583
|
+
method: "GET",
|
|
584
|
+
path: `/api/v1/invoices/${encodeURIComponent(id)}`
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Mint a hosted pay link for an OPEN invoice. PAID/VOID invoices are rejected
|
|
589
|
+
* with a `ConflictError`. `GET /api/v1/invoices/:id/pay-link`.
|
|
590
|
+
*/
|
|
591
|
+
payLink(id) {
|
|
592
|
+
return this.transport.request({
|
|
593
|
+
method: "GET",
|
|
594
|
+
path: `/api/v1/invoices/${encodeURIComponent(id)}/pay-link`
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
|
|
599
|
+
// src/resources/outcomes.ts
|
|
600
|
+
var Outcomes = class {
|
|
601
|
+
constructor(transport) {
|
|
602
|
+
this.transport = transport;
|
|
603
|
+
}
|
|
604
|
+
transport;
|
|
605
|
+
/** List outcome types. Pass `{ active: true }` to only return active ones. */
|
|
606
|
+
async list(params = {}) {
|
|
607
|
+
const res = await this.transport.request({
|
|
608
|
+
method: "GET",
|
|
609
|
+
path: "/api/v1/outcomes",
|
|
610
|
+
query: { active: params.active === void 0 ? void 0 : String(params.active) }
|
|
611
|
+
});
|
|
612
|
+
return res.outcomes;
|
|
613
|
+
}
|
|
614
|
+
/** Create an outcome type with its steps. `POST /api/v1/outcomes`. */
|
|
615
|
+
async create(input) {
|
|
616
|
+
const res = await this.transport.request({
|
|
617
|
+
method: "POST",
|
|
618
|
+
path: "/api/v1/outcomes",
|
|
619
|
+
body: input
|
|
620
|
+
});
|
|
621
|
+
return res.outcome;
|
|
622
|
+
}
|
|
623
|
+
/** Fetch one outcome type with steps + stats. `GET /api/v1/outcomes/:id`. */
|
|
624
|
+
async get(id) {
|
|
625
|
+
const res = await this.transport.request({
|
|
626
|
+
method: "GET",
|
|
627
|
+
path: `/api/v1/outcomes/${encodeURIComponent(id)}`
|
|
628
|
+
});
|
|
629
|
+
return res.outcome;
|
|
630
|
+
}
|
|
631
|
+
/** Update an outcome type's definition. `PATCH /api/v1/outcomes/:id`. */
|
|
632
|
+
async update(id, input) {
|
|
633
|
+
const res = await this.transport.request({
|
|
634
|
+
method: "PATCH",
|
|
635
|
+
path: `/api/v1/outcomes/${encodeURIComponent(id)}`,
|
|
636
|
+
body: input
|
|
637
|
+
});
|
|
638
|
+
return res.outcome;
|
|
639
|
+
}
|
|
640
|
+
/** Activate or deactivate an outcome type. */
|
|
641
|
+
async setActive(id, isActive) {
|
|
642
|
+
await this.transport.request({
|
|
643
|
+
method: "PATCH",
|
|
644
|
+
path: `/api/v1/outcomes/${encodeURIComponent(id)}`,
|
|
645
|
+
body: { isActive }
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
/** Delete an outcome type. */
|
|
649
|
+
async delete(id) {
|
|
650
|
+
await this.transport.request({
|
|
651
|
+
method: "DELETE",
|
|
652
|
+
path: `/api/v1/outcomes/${encodeURIComponent(id)}`
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
// src/resources/payments.ts
|
|
658
|
+
var Payments = class {
|
|
659
|
+
constructor(transport) {
|
|
660
|
+
this.transport = transport;
|
|
661
|
+
}
|
|
662
|
+
transport;
|
|
663
|
+
/** List payments (paid invoices), most-recent first. Filter by customer. */
|
|
664
|
+
async list(params = {}) {
|
|
665
|
+
const res = await this.transport.request({
|
|
666
|
+
method: "GET",
|
|
667
|
+
path: "/api/v1/payments",
|
|
668
|
+
query: { customerId: params.customerId, limit: params.limit }
|
|
669
|
+
});
|
|
670
|
+
return res.payments;
|
|
671
|
+
}
|
|
672
|
+
/** Fetch one payment + its plan snapshot. `GET /api/v1/payments/:id`. */
|
|
673
|
+
get(id) {
|
|
674
|
+
return this.transport.request({
|
|
675
|
+
method: "GET",
|
|
676
|
+
path: `/api/v1/payments/${encodeURIComponent(id)}`
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
|
|
681
|
+
// src/resources/plans.ts
|
|
682
|
+
var Plans = class {
|
|
683
|
+
constructor(transport) {
|
|
684
|
+
this.transport = transport;
|
|
685
|
+
}
|
|
686
|
+
transport;
|
|
687
|
+
/** List plans, oldest-first. Pass `{ active: true }` to only return active plans. */
|
|
688
|
+
async list(params = {}) {
|
|
689
|
+
const res = await this.transport.request({
|
|
690
|
+
method: "GET",
|
|
691
|
+
path: "/api/v1/plans",
|
|
692
|
+
query: { active: params.active === void 0 ? void 0 : String(params.active) }
|
|
693
|
+
});
|
|
694
|
+
return res.plans;
|
|
695
|
+
}
|
|
696
|
+
/** Create a plan. `POST /api/v1/plans`. */
|
|
697
|
+
async create(input) {
|
|
698
|
+
const res = await this.transport.request({
|
|
699
|
+
method: "POST",
|
|
700
|
+
path: "/api/v1/plans",
|
|
701
|
+
body: input
|
|
702
|
+
});
|
|
703
|
+
return res.plan;
|
|
704
|
+
}
|
|
705
|
+
/** Fetch one plan with aggregate subscriber/invoice stats. `GET /api/v1/plans/:id`. */
|
|
706
|
+
async get(id) {
|
|
707
|
+
const res = await this.transport.request({
|
|
708
|
+
method: "GET",
|
|
709
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}`
|
|
710
|
+
});
|
|
711
|
+
return res.plan;
|
|
712
|
+
}
|
|
713
|
+
/** Replace a plan's definition (components + settings). `PATCH /api/v1/plans/:id`. */
|
|
714
|
+
async update(id, input) {
|
|
715
|
+
const res = await this.transport.request({
|
|
716
|
+
method: "PATCH",
|
|
717
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}`,
|
|
718
|
+
body: input
|
|
719
|
+
});
|
|
720
|
+
return res.plan;
|
|
721
|
+
}
|
|
722
|
+
/** Activate or deactivate a plan without rewriting it. */
|
|
723
|
+
async setActive(id, isActive) {
|
|
724
|
+
const res = await this.transport.request({
|
|
725
|
+
method: "PATCH",
|
|
726
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}`,
|
|
727
|
+
body: { isActive }
|
|
728
|
+
});
|
|
729
|
+
return res.plan;
|
|
730
|
+
}
|
|
731
|
+
/** Delete a plan. Refused once any customer has purchased it. */
|
|
732
|
+
async delete(id) {
|
|
733
|
+
await this.transport.request({
|
|
734
|
+
method: "DELETE",
|
|
735
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}`
|
|
736
|
+
});
|
|
737
|
+
}
|
|
429
738
|
};
|
|
430
739
|
|
|
431
740
|
// src/resources/portal.ts
|
|
@@ -452,20 +761,71 @@ var Portal = class {
|
|
|
452
761
|
}
|
|
453
762
|
};
|
|
454
763
|
|
|
764
|
+
// src/resources/purchases.ts
|
|
765
|
+
var Purchases = class {
|
|
766
|
+
constructor(transport) {
|
|
767
|
+
this.transport = transport;
|
|
768
|
+
}
|
|
769
|
+
transport;
|
|
770
|
+
/** List purchases, most-recent first. Filter by customer and/or status. */
|
|
771
|
+
async list(params = {}) {
|
|
772
|
+
const res = await this.transport.request({
|
|
773
|
+
method: "GET",
|
|
774
|
+
path: "/api/v1/purchases",
|
|
775
|
+
query: {
|
|
776
|
+
customerId: params.customerId,
|
|
777
|
+
status: params.status,
|
|
778
|
+
limit: params.limit
|
|
779
|
+
}
|
|
780
|
+
});
|
|
781
|
+
return res.purchases;
|
|
782
|
+
}
|
|
783
|
+
/** Subscribe a customer to a plan. `POST /api/v1/purchases`. */
|
|
784
|
+
async create(input) {
|
|
785
|
+
const res = await this.transport.request({
|
|
786
|
+
method: "POST",
|
|
787
|
+
path: "/api/v1/purchases",
|
|
788
|
+
body: input
|
|
789
|
+
});
|
|
790
|
+
return res.purchase;
|
|
791
|
+
}
|
|
792
|
+
/** Fetch one purchase. `GET /api/v1/purchases/:id`. */
|
|
793
|
+
async get(id) {
|
|
794
|
+
const res = await this.transport.request({
|
|
795
|
+
method: "GET",
|
|
796
|
+
path: `/api/v1/purchases/${encodeURIComponent(id)}`
|
|
797
|
+
});
|
|
798
|
+
return res.purchase;
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* Turn automatic billing on or off for a purchase. When pausing, an optional
|
|
802
|
+
* `pauseReason` is recorded. `PATCH /api/v1/purchases/:id`.
|
|
803
|
+
*/
|
|
804
|
+
async setAutoPayment(id, autoPayment, opts = {}) {
|
|
805
|
+
return this.transport.request({
|
|
806
|
+
method: "PATCH",
|
|
807
|
+
path: `/api/v1/purchases/${encodeURIComponent(id)}`,
|
|
808
|
+
body: { autoPayment, pauseReason: opts.pauseReason }
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
/** Cancel a purchase (voids any open invoice). `POST /api/v1/purchases/:id/cancel`. */
|
|
812
|
+
async cancel(id) {
|
|
813
|
+
await this.transport.request({
|
|
814
|
+
method: "POST",
|
|
815
|
+
path: `/api/v1/purchases/${encodeURIComponent(id)}/cancel`
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
|
|
455
820
|
// src/idempotency.ts
|
|
456
|
-
function
|
|
457
|
-
const
|
|
458
|
-
if (
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
}
|
|
465
|
-
b[6] = b[6] & 15 | 64;
|
|
466
|
-
b[8] = b[8] & 63 | 128;
|
|
467
|
-
const hex = Array.from(b, (n) => n.toString(16).padStart(2, "0")).join("");
|
|
468
|
-
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
821
|
+
function newKey() {
|
|
822
|
+
const c = globalThis.crypto;
|
|
823
|
+
if (typeof c?.randomUUID === "function") return c.randomUUID();
|
|
824
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (ch) => {
|
|
825
|
+
const r = Math.random() * 16 | 0;
|
|
826
|
+
const v = ch === "x" ? r : r & 3 | 8;
|
|
827
|
+
return v.toString(16);
|
|
828
|
+
});
|
|
469
829
|
}
|
|
470
830
|
|
|
471
831
|
// src/resources/signals.ts
|
|
@@ -476,18 +836,15 @@ function signalToWire(signal) {
|
|
|
476
836
|
model: signal.model
|
|
477
837
|
};
|
|
478
838
|
if (signal.member) body.member = signal.member;
|
|
479
|
-
if (signal.status) body.status = signal.status;
|
|
480
839
|
if (signal.custom) body.custom = signal.custom;
|
|
481
|
-
if (signal.idempotencyKey) body.idempotencyKey = signal.idempotencyKey;
|
|
482
840
|
const t = signal.tokens;
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
if (t.cache != null) body.cacheTokens = t.cache;
|
|
487
|
-
}
|
|
841
|
+
body.inputTokens = t.input;
|
|
842
|
+
body.outputTokens = t.output;
|
|
843
|
+
if (t.cache != null) body.cacheTokens = t.cache;
|
|
488
844
|
if (signal.type === "credit" || signal.type === "outcome") {
|
|
489
|
-
body.
|
|
845
|
+
body.agentKey = signal.agentKey;
|
|
490
846
|
}
|
|
847
|
+
if (signal.idempotencyKey) body.idempotencyKey = signal.idempotencyKey;
|
|
491
848
|
return body;
|
|
492
849
|
}
|
|
493
850
|
var Signals = class {
|
|
@@ -500,25 +857,42 @@ var Signals = class {
|
|
|
500
857
|
flusher;
|
|
501
858
|
cfg;
|
|
502
859
|
async track(signal, opts = {}) {
|
|
503
|
-
const
|
|
504
|
-
|
|
860
|
+
const body = signalToWire(signal);
|
|
861
|
+
if (body.idempotencyKey == null) body.idempotencyKey = newKey();
|
|
505
862
|
const sendNow = opts.wait || this.cfg.mode === "sync";
|
|
506
863
|
if (!sendNow) {
|
|
507
|
-
this.flusher.enqueue({ body,
|
|
508
|
-
return {
|
|
864
|
+
this.flusher.enqueue({ body, signal });
|
|
865
|
+
return { queued: true };
|
|
509
866
|
}
|
|
510
867
|
const res = await this.transport.request({
|
|
511
868
|
method: "POST",
|
|
512
869
|
path: "/api/v1/usage",
|
|
513
870
|
body,
|
|
514
|
-
|
|
871
|
+
// Best-effort metering opts into retries on transient failures.
|
|
872
|
+
retry: true
|
|
515
873
|
});
|
|
516
874
|
return {
|
|
517
|
-
idempotencyKey,
|
|
518
875
|
queued: false,
|
|
519
876
|
usageLog: res.usageLog ?? null
|
|
520
877
|
};
|
|
521
878
|
}
|
|
879
|
+
/**
|
|
880
|
+
* Validate + price a signal WITHOUT recording it (server `dryRun`). Returns
|
|
881
|
+
* the projected usage log so an integration can preflight before sending real
|
|
882
|
+
* traffic — no inbox row, wallet / outcome writes, or webhooks fire.
|
|
883
|
+
*/
|
|
884
|
+
async verify(signal) {
|
|
885
|
+
const body = signalToWire(signal);
|
|
886
|
+
body.dryRun = true;
|
|
887
|
+
const res = await this.transport.request({
|
|
888
|
+
method: "POST",
|
|
889
|
+
path: "/api/v1/usage",
|
|
890
|
+
body,
|
|
891
|
+
// A dry run writes nothing, so it is always safe to retry.
|
|
892
|
+
retry: true
|
|
893
|
+
});
|
|
894
|
+
return res.usageLog ?? null;
|
|
895
|
+
}
|
|
522
896
|
/** Meter a named credit. */
|
|
523
897
|
credit(input, opts) {
|
|
524
898
|
return this.track({ ...input, type: "credit" }, opts);
|
|
@@ -540,16 +914,178 @@ var Signals = class {
|
|
|
540
914
|
});
|
|
541
915
|
return { customer: res.customer, totals: res.totals, logs: res.logs };
|
|
542
916
|
}
|
|
917
|
+
/**
|
|
918
|
+
* Record one unit-metered event (a fixed-price meter with no tokens),
|
|
919
|
+
* addressed to a catalog unit by its `agentKey`. Sent immediately — unit
|
|
920
|
+
* events are not buffered. `POST /api/v1/units`.
|
|
921
|
+
*/
|
|
922
|
+
async unit(input) {
|
|
923
|
+
return this.transport.request({
|
|
924
|
+
method: "POST",
|
|
925
|
+
path: "/api/v1/units",
|
|
926
|
+
body: {
|
|
927
|
+
customerId: input.customerId,
|
|
928
|
+
agentKey: input.agentKey,
|
|
929
|
+
...input.member ? { member: input.member } : {}
|
|
930
|
+
},
|
|
931
|
+
// Best-effort metering opts into retries on transient failures.
|
|
932
|
+
retry: true
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
/** A customer's unit balances + recent unit events. `GET /api/v1/units`. */
|
|
936
|
+
unitUsage(params) {
|
|
937
|
+
return this.transport.request({
|
|
938
|
+
method: "GET",
|
|
939
|
+
path: "/api/v1/units",
|
|
940
|
+
query: { customerId: params.customerId, limit: params.limit }
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
};
|
|
944
|
+
|
|
945
|
+
// src/resources/units.ts
|
|
946
|
+
var Units = class {
|
|
947
|
+
constructor(transport) {
|
|
948
|
+
this.transport = transport;
|
|
949
|
+
}
|
|
950
|
+
transport;
|
|
951
|
+
/** List unit types. Pass `{ active: true }` to only return active ones. */
|
|
952
|
+
async list(params = {}) {
|
|
953
|
+
const res = await this.transport.request({
|
|
954
|
+
method: "GET",
|
|
955
|
+
path: "/api/v1/units-catalog",
|
|
956
|
+
query: { active: params.active === void 0 ? void 0 : String(params.active) }
|
|
957
|
+
});
|
|
958
|
+
return res.units;
|
|
959
|
+
}
|
|
960
|
+
/** Create a unit type. `POST /api/v1/units-catalog`. */
|
|
961
|
+
async create(input) {
|
|
962
|
+
const res = await this.transport.request({
|
|
963
|
+
method: "POST",
|
|
964
|
+
path: "/api/v1/units-catalog",
|
|
965
|
+
body: input
|
|
966
|
+
});
|
|
967
|
+
return res.unit;
|
|
968
|
+
}
|
|
969
|
+
/** Fetch one unit type with stats. `GET /api/v1/units-catalog/:id`. */
|
|
970
|
+
async get(id) {
|
|
971
|
+
const res = await this.transport.request({
|
|
972
|
+
method: "GET",
|
|
973
|
+
path: `/api/v1/units-catalog/${encodeURIComponent(id)}`
|
|
974
|
+
});
|
|
975
|
+
return res.unit;
|
|
976
|
+
}
|
|
977
|
+
/** Update a unit type's definition. `PATCH /api/v1/units-catalog/:id`. */
|
|
978
|
+
async update(id, input) {
|
|
979
|
+
const res = await this.transport.request({
|
|
980
|
+
method: "PATCH",
|
|
981
|
+
path: `/api/v1/units-catalog/${encodeURIComponent(id)}`,
|
|
982
|
+
body: input
|
|
983
|
+
});
|
|
984
|
+
return res.unit;
|
|
985
|
+
}
|
|
986
|
+
/** Activate or deactivate a unit type. */
|
|
987
|
+
async setActive(id, isActive) {
|
|
988
|
+
const res = await this.transport.request({
|
|
989
|
+
method: "PATCH",
|
|
990
|
+
path: `/api/v1/units-catalog/${encodeURIComponent(id)}`,
|
|
991
|
+
body: { isActive }
|
|
992
|
+
});
|
|
993
|
+
return res.unit;
|
|
994
|
+
}
|
|
995
|
+
/** Delete a unit type. */
|
|
996
|
+
async delete(id) {
|
|
997
|
+
await this.transport.request({
|
|
998
|
+
method: "DELETE",
|
|
999
|
+
path: `/api/v1/units-catalog/${encodeURIComponent(id)}`
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
};
|
|
1003
|
+
|
|
1004
|
+
// src/resources/webhooks.ts
|
|
1005
|
+
var Webhooks = class {
|
|
1006
|
+
constructor(transport) {
|
|
1007
|
+
this.transport = transport;
|
|
1008
|
+
}
|
|
1009
|
+
transport;
|
|
1010
|
+
/** List configured webhooks. `GET /api/v1/webhooks`. */
|
|
1011
|
+
async list() {
|
|
1012
|
+
const res = await this.transport.request({
|
|
1013
|
+
method: "GET",
|
|
1014
|
+
path: "/api/v1/webhooks"
|
|
1015
|
+
});
|
|
1016
|
+
return res.webhooks;
|
|
1017
|
+
}
|
|
1018
|
+
/** Create or replace the webhook for a kind. `POST /api/v1/webhooks`. */
|
|
1019
|
+
async upsert(input) {
|
|
1020
|
+
const res = await this.transport.request({
|
|
1021
|
+
method: "POST",
|
|
1022
|
+
path: "/api/v1/webhooks",
|
|
1023
|
+
body: input
|
|
1024
|
+
});
|
|
1025
|
+
return res.webhook;
|
|
1026
|
+
}
|
|
1027
|
+
/** Delete the webhook for a kind (idempotent). `DELETE /api/v1/webhooks/:kind`. */
|
|
1028
|
+
async delete(kind) {
|
|
1029
|
+
await this.transport.request({
|
|
1030
|
+
method: "DELETE",
|
|
1031
|
+
path: `/api/v1/webhooks/${encodeURIComponent(kind)}`
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
};
|
|
1035
|
+
|
|
1036
|
+
// src/resources/workspace.ts
|
|
1037
|
+
var Workspace = class {
|
|
1038
|
+
constructor(transport) {
|
|
1039
|
+
this.transport = transport;
|
|
1040
|
+
}
|
|
1041
|
+
transport;
|
|
1042
|
+
/** Identify the org + key behind the request, incl. `sandbox` | `live`. */
|
|
1043
|
+
me() {
|
|
1044
|
+
return this.transport.request({
|
|
1045
|
+
method: "GET",
|
|
1046
|
+
path: "/api/v1/me"
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
/** The org's enabled models with live prices. `{ active: true }` returns only
|
|
1050
|
+
* the models that can be metered right now. */
|
|
1051
|
+
async models(params = {}) {
|
|
1052
|
+
const res = await this.transport.request({
|
|
1053
|
+
method: "GET",
|
|
1054
|
+
path: "/api/v1/models",
|
|
1055
|
+
query: {
|
|
1056
|
+
active: params.active === void 0 ? void 0 : String(params.active)
|
|
1057
|
+
}
|
|
1058
|
+
});
|
|
1059
|
+
return res.models;
|
|
1060
|
+
}
|
|
543
1061
|
};
|
|
544
1062
|
|
|
545
1063
|
// src/client.ts
|
|
546
1064
|
var ClockNext = class {
|
|
547
|
-
/** Record + read usage signals. */
|
|
1065
|
+
/** Record + read usage signals (credit / wallet / outcome / unit). */
|
|
548
1066
|
signals;
|
|
549
|
-
/** Manage customers
|
|
1067
|
+
/** Manage customers, their members, wallet, and balances. */
|
|
550
1068
|
customers;
|
|
1069
|
+
/** Define and manage billing plans. */
|
|
1070
|
+
plans;
|
|
1071
|
+
/** Subscribe customers to plans (purchases). */
|
|
1072
|
+
purchases;
|
|
1073
|
+
/** Read invoices and mint hosted pay links. */
|
|
1074
|
+
invoices;
|
|
1075
|
+
/** Read payments (paid invoices). */
|
|
1076
|
+
payments;
|
|
1077
|
+
/** Manage the credit-type catalog. */
|
|
1078
|
+
credits;
|
|
1079
|
+
/** Manage the outcome-type catalog. */
|
|
1080
|
+
outcomes;
|
|
1081
|
+
/** Manage the unit-type catalog. */
|
|
1082
|
+
units;
|
|
1083
|
+
/** Configure threshold webhooks. */
|
|
1084
|
+
webhooks;
|
|
551
1085
|
/** Mint customer-portal embed tokens. */
|
|
552
1086
|
portal;
|
|
1087
|
+
/** Identify the workspace (sandbox/live) + list its models. `/me`, `/models`. */
|
|
1088
|
+
workspace;
|
|
553
1089
|
cfg;
|
|
554
1090
|
flusher;
|
|
555
1091
|
constructor(config) {
|
|
@@ -563,7 +1099,16 @@ var ClockNext = class {
|
|
|
563
1099
|
this.flusher = new Flusher(transport, this.cfg);
|
|
564
1100
|
this.signals = new Signals(transport, this.flusher, this.cfg);
|
|
565
1101
|
this.customers = new Customers(transport);
|
|
1102
|
+
this.plans = new Plans(transport);
|
|
1103
|
+
this.purchases = new Purchases(transport);
|
|
1104
|
+
this.invoices = new Invoices(transport);
|
|
1105
|
+
this.payments = new Payments(transport);
|
|
1106
|
+
this.credits = new Credits(transport);
|
|
1107
|
+
this.outcomes = new Outcomes(transport);
|
|
1108
|
+
this.units = new Units(transport);
|
|
1109
|
+
this.webhooks = new Webhooks(transport);
|
|
566
1110
|
this.portal = new Portal(transport);
|
|
1111
|
+
this.workspace = new Workspace(transport);
|
|
567
1112
|
}
|
|
568
1113
|
/** Number of signals currently buffered (async mode). */
|
|
569
1114
|
get pending() {
|
|
@@ -590,7 +1135,6 @@ exports.PlanError = PlanError;
|
|
|
590
1135
|
exports.RateLimitError = RateLimitError;
|
|
591
1136
|
exports.ServerError = ServerError;
|
|
592
1137
|
exports.ValidationError = ValidationError;
|
|
593
|
-
exports.newIdempotencyKey = newIdempotencyKey;
|
|
594
1138
|
exports.signalToWire = signalToWire;
|
|
595
1139
|
//# sourceMappingURL=index.cjs.map
|
|
596
1140
|
//# sourceMappingURL=index.cjs.map
|