@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.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/config.ts
|
|
2
|
-
var DEFAULT_BASE_URL = "https://
|
|
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
|
-
|
|
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,
|
|
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
|
|
183
|
-
super(message, {
|
|
180
|
+
constructor(message) {
|
|
181
|
+
super(message, { retryable: true });
|
|
184
182
|
}
|
|
185
183
|
};
|
|
186
|
-
function errorFromResponse(status, message,
|
|
184
|
+
function errorFromResponse(status, message, retryAfterMs) {
|
|
187
185
|
const msg = message || `Request failed with status ${status}.`;
|
|
188
|
-
const opts = { status
|
|
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, {
|
|
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.
|
|
230
|
+
var SDK_VERSION = "0.4.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 === "
|
|
240
|
-
const
|
|
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
|
|
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,269 @@ 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
|
+
// Note: a customer's currency is set through `update(id, { currencyCode })`
|
|
528
|
+
// (PATCH …/:id) — there is no separate currency endpoint. Credit-type
|
|
529
|
+
// assignment has no public API.
|
|
530
|
+
// --- Balance adjustments -------------------------------------------------
|
|
531
|
+
/** Grant or claw back a credit balance. `POST …/:id/credits/adjust`. */
|
|
532
|
+
async adjustCredit(id, input) {
|
|
533
|
+
await this.transport.request({
|
|
534
|
+
method: "POST",
|
|
535
|
+
path: `/api/v1/customers/${encodeURIComponent(id)}/credits/adjust`,
|
|
536
|
+
body: input
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
/** Grant or claw back an outcome balance. Returns the remaining balance. */
|
|
540
|
+
async adjustOutcome(id, input) {
|
|
541
|
+
const res = await this.transport.request({
|
|
542
|
+
method: "POST",
|
|
543
|
+
path: `/api/v1/customers/${encodeURIComponent(id)}/outcomes/adjust`,
|
|
544
|
+
body: input
|
|
545
|
+
});
|
|
546
|
+
return { remaining: res.remaining };
|
|
547
|
+
}
|
|
548
|
+
/** Grant or claw back a unit balance. Returns the remaining balance. */
|
|
549
|
+
async adjustUnit(id, input) {
|
|
550
|
+
const res = await this.transport.request({
|
|
551
|
+
method: "POST",
|
|
552
|
+
path: `/api/v1/customers/${encodeURIComponent(id)}/units/adjust`,
|
|
553
|
+
body: input
|
|
554
|
+
});
|
|
555
|
+
return { remaining: res.remaining };
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
// src/resources/invoices.ts
|
|
560
|
+
var Invoices = class {
|
|
561
|
+
constructor(transport) {
|
|
562
|
+
this.transport = transport;
|
|
563
|
+
}
|
|
564
|
+
transport;
|
|
565
|
+
/** List invoices, most-recent first. Filter by customer and/or status. */
|
|
566
|
+
async list(params = {}) {
|
|
567
|
+
const res = await this.transport.request({
|
|
568
|
+
method: "GET",
|
|
569
|
+
path: "/api/v1/invoices",
|
|
570
|
+
query: {
|
|
571
|
+
customerId: params.customerId,
|
|
572
|
+
status: params.status,
|
|
573
|
+
limit: params.limit
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
return res.invoices;
|
|
577
|
+
}
|
|
578
|
+
/** Fetch one full invoice + its plan snapshot. `GET /api/v1/invoices/:id`. */
|
|
579
|
+
get(id) {
|
|
580
|
+
return this.transport.request({
|
|
581
|
+
method: "GET",
|
|
582
|
+
path: `/api/v1/invoices/${encodeURIComponent(id)}`
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Mint a hosted pay link for an OPEN invoice. PAID/VOID invoices are rejected
|
|
587
|
+
* with a `ConflictError`. `GET /api/v1/invoices/:id/pay-link`.
|
|
588
|
+
*/
|
|
589
|
+
payLink(id) {
|
|
590
|
+
return this.transport.request({
|
|
591
|
+
method: "GET",
|
|
592
|
+
path: `/api/v1/invoices/${encodeURIComponent(id)}/pay-link`
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
// src/resources/outcomes.ts
|
|
598
|
+
var Outcomes = class {
|
|
599
|
+
constructor(transport) {
|
|
600
|
+
this.transport = transport;
|
|
601
|
+
}
|
|
602
|
+
transport;
|
|
603
|
+
/** List outcome types. Pass `{ active: true }` to only return active ones. */
|
|
604
|
+
async list(params = {}) {
|
|
605
|
+
const res = await this.transport.request({
|
|
606
|
+
method: "GET",
|
|
607
|
+
path: "/api/v1/outcomes",
|
|
608
|
+
query: { active: params.active === void 0 ? void 0 : String(params.active) }
|
|
609
|
+
});
|
|
610
|
+
return res.outcomes;
|
|
611
|
+
}
|
|
612
|
+
/** Create an outcome type with its steps. `POST /api/v1/outcomes`. */
|
|
613
|
+
async create(input) {
|
|
614
|
+
const res = await this.transport.request({
|
|
615
|
+
method: "POST",
|
|
616
|
+
path: "/api/v1/outcomes",
|
|
617
|
+
body: input
|
|
618
|
+
});
|
|
619
|
+
return res.outcome;
|
|
620
|
+
}
|
|
621
|
+
/** Fetch one outcome type with steps + stats. `GET /api/v1/outcomes/:id`. */
|
|
622
|
+
async get(id) {
|
|
623
|
+
const res = await this.transport.request({
|
|
624
|
+
method: "GET",
|
|
625
|
+
path: `/api/v1/outcomes/${encodeURIComponent(id)}`
|
|
626
|
+
});
|
|
627
|
+
return res.outcome;
|
|
628
|
+
}
|
|
629
|
+
/** Update an outcome type's definition. `PATCH /api/v1/outcomes/:id`. */
|
|
630
|
+
async update(id, input) {
|
|
631
|
+
const res = await this.transport.request({
|
|
632
|
+
method: "PATCH",
|
|
633
|
+
path: `/api/v1/outcomes/${encodeURIComponent(id)}`,
|
|
634
|
+
body: input
|
|
635
|
+
});
|
|
636
|
+
return res.outcome;
|
|
637
|
+
}
|
|
638
|
+
/** Activate or deactivate an outcome type. */
|
|
639
|
+
async setActive(id, isActive) {
|
|
640
|
+
await this.transport.request({
|
|
641
|
+
method: "PATCH",
|
|
642
|
+
path: `/api/v1/outcomes/${encodeURIComponent(id)}`,
|
|
643
|
+
body: { isActive }
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
/** Delete an outcome type. */
|
|
647
|
+
async delete(id) {
|
|
648
|
+
await this.transport.request({
|
|
649
|
+
method: "DELETE",
|
|
650
|
+
path: `/api/v1/outcomes/${encodeURIComponent(id)}`
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
// src/resources/payments.ts
|
|
656
|
+
var Payments = class {
|
|
657
|
+
constructor(transport) {
|
|
658
|
+
this.transport = transport;
|
|
659
|
+
}
|
|
660
|
+
transport;
|
|
661
|
+
/** List payments (paid invoices), most-recent first. Filter by customer. */
|
|
662
|
+
async list(params = {}) {
|
|
663
|
+
const res = await this.transport.request({
|
|
664
|
+
method: "GET",
|
|
665
|
+
path: "/api/v1/payments",
|
|
666
|
+
query: { customerId: params.customerId, limit: params.limit }
|
|
667
|
+
});
|
|
668
|
+
return res.payments;
|
|
669
|
+
}
|
|
670
|
+
/** Fetch one payment + its plan snapshot. `GET /api/v1/payments/:id`. */
|
|
671
|
+
get(id) {
|
|
672
|
+
return this.transport.request({
|
|
673
|
+
method: "GET",
|
|
674
|
+
path: `/api/v1/payments/${encodeURIComponent(id)}`
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
// src/resources/plans.ts
|
|
680
|
+
var Plans = class {
|
|
681
|
+
constructor(transport) {
|
|
682
|
+
this.transport = transport;
|
|
683
|
+
}
|
|
684
|
+
transport;
|
|
685
|
+
/** List plans, oldest-first. Pass `{ active: true }` to only return active plans. */
|
|
686
|
+
async list(params = {}) {
|
|
687
|
+
const res = await this.transport.request({
|
|
688
|
+
method: "GET",
|
|
689
|
+
path: "/api/v1/plans",
|
|
690
|
+
query: { active: params.active === void 0 ? void 0 : String(params.active) }
|
|
691
|
+
});
|
|
692
|
+
return res.plans;
|
|
693
|
+
}
|
|
694
|
+
/** Create a plan. `POST /api/v1/plans`. */
|
|
695
|
+
async create(input) {
|
|
696
|
+
const res = await this.transport.request({
|
|
697
|
+
method: "POST",
|
|
698
|
+
path: "/api/v1/plans",
|
|
699
|
+
body: input
|
|
700
|
+
});
|
|
701
|
+
return res.plan;
|
|
702
|
+
}
|
|
703
|
+
/** Fetch one plan with aggregate subscriber/invoice stats. `GET /api/v1/plans/:id`. */
|
|
704
|
+
async get(id) {
|
|
705
|
+
const res = await this.transport.request({
|
|
706
|
+
method: "GET",
|
|
707
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}`
|
|
708
|
+
});
|
|
709
|
+
return res.plan;
|
|
710
|
+
}
|
|
711
|
+
/** Replace a plan's definition (components + settings). `PATCH /api/v1/plans/:id`. */
|
|
712
|
+
async update(id, input) {
|
|
713
|
+
const res = await this.transport.request({
|
|
714
|
+
method: "PATCH",
|
|
715
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}`,
|
|
716
|
+
body: input
|
|
717
|
+
});
|
|
718
|
+
return res.plan;
|
|
719
|
+
}
|
|
720
|
+
/** Activate or deactivate a plan without rewriting it. */
|
|
721
|
+
async setActive(id, isActive) {
|
|
722
|
+
const res = await this.transport.request({
|
|
723
|
+
method: "PATCH",
|
|
724
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}`,
|
|
725
|
+
body: { isActive }
|
|
726
|
+
});
|
|
727
|
+
return res.plan;
|
|
728
|
+
}
|
|
729
|
+
/** Delete a plan. Refused once any customer has purchased it. */
|
|
730
|
+
async delete(id) {
|
|
731
|
+
await this.transport.request({
|
|
732
|
+
method: "DELETE",
|
|
733
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}`
|
|
734
|
+
});
|
|
735
|
+
}
|
|
427
736
|
};
|
|
428
737
|
|
|
429
738
|
// src/resources/portal.ts
|
|
@@ -450,20 +759,71 @@ var Portal = class {
|
|
|
450
759
|
}
|
|
451
760
|
};
|
|
452
761
|
|
|
762
|
+
// src/resources/purchases.ts
|
|
763
|
+
var Purchases = class {
|
|
764
|
+
constructor(transport) {
|
|
765
|
+
this.transport = transport;
|
|
766
|
+
}
|
|
767
|
+
transport;
|
|
768
|
+
/** List purchases, most-recent first. Filter by customer and/or status. */
|
|
769
|
+
async list(params = {}) {
|
|
770
|
+
const res = await this.transport.request({
|
|
771
|
+
method: "GET",
|
|
772
|
+
path: "/api/v1/purchases",
|
|
773
|
+
query: {
|
|
774
|
+
customerId: params.customerId,
|
|
775
|
+
status: params.status,
|
|
776
|
+
limit: params.limit
|
|
777
|
+
}
|
|
778
|
+
});
|
|
779
|
+
return res.purchases;
|
|
780
|
+
}
|
|
781
|
+
/** Subscribe a customer to a plan. `POST /api/v1/purchases`. */
|
|
782
|
+
async create(input) {
|
|
783
|
+
const res = await this.transport.request({
|
|
784
|
+
method: "POST",
|
|
785
|
+
path: "/api/v1/purchases",
|
|
786
|
+
body: input
|
|
787
|
+
});
|
|
788
|
+
return res.purchase;
|
|
789
|
+
}
|
|
790
|
+
/** Fetch one purchase. `GET /api/v1/purchases/:id`. */
|
|
791
|
+
async get(id) {
|
|
792
|
+
const res = await this.transport.request({
|
|
793
|
+
method: "GET",
|
|
794
|
+
path: `/api/v1/purchases/${encodeURIComponent(id)}`
|
|
795
|
+
});
|
|
796
|
+
return res.purchase;
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Turn automatic billing on or off for a purchase. When pausing, an optional
|
|
800
|
+
* `pauseReason` is recorded. `PATCH /api/v1/purchases/:id`.
|
|
801
|
+
*/
|
|
802
|
+
async setAutoPayment(id, autoPayment, opts = {}) {
|
|
803
|
+
return this.transport.request({
|
|
804
|
+
method: "PATCH",
|
|
805
|
+
path: `/api/v1/purchases/${encodeURIComponent(id)}`,
|
|
806
|
+
body: { autoPayment, pauseReason: opts.pauseReason }
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
/** Cancel a purchase (voids any open invoice). `POST /api/v1/purchases/:id/cancel`. */
|
|
810
|
+
async cancel(id) {
|
|
811
|
+
await this.transport.request({
|
|
812
|
+
method: "POST",
|
|
813
|
+
path: `/api/v1/purchases/${encodeURIComponent(id)}/cancel`
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
|
|
453
818
|
// src/idempotency.ts
|
|
454
|
-
function
|
|
455
|
-
const
|
|
456
|
-
if (
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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)}`;
|
|
819
|
+
function newKey() {
|
|
820
|
+
const c = globalThis.crypto;
|
|
821
|
+
if (typeof c?.randomUUID === "function") return c.randomUUID();
|
|
822
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (ch) => {
|
|
823
|
+
const r = Math.random() * 16 | 0;
|
|
824
|
+
const v = ch === "x" ? r : r & 3 | 8;
|
|
825
|
+
return v.toString(16);
|
|
826
|
+
});
|
|
467
827
|
}
|
|
468
828
|
|
|
469
829
|
// src/resources/signals.ts
|
|
@@ -474,18 +834,15 @@ function signalToWire(signal) {
|
|
|
474
834
|
model: signal.model
|
|
475
835
|
};
|
|
476
836
|
if (signal.member) body.member = signal.member;
|
|
477
|
-
if (signal.status) body.status = signal.status;
|
|
478
837
|
if (signal.custom) body.custom = signal.custom;
|
|
479
|
-
if (signal.idempotencyKey) body.idempotencyKey = signal.idempotencyKey;
|
|
480
838
|
const t = signal.tokens;
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
if (t.cache != null) body.cacheTokens = t.cache;
|
|
485
|
-
}
|
|
839
|
+
body.inputTokens = t.input;
|
|
840
|
+
body.outputTokens = t.output;
|
|
841
|
+
if (t.cache != null) body.cacheTokens = t.cache;
|
|
486
842
|
if (signal.type === "credit" || signal.type === "outcome") {
|
|
487
|
-
body.
|
|
843
|
+
body.agentKey = signal.agentKey;
|
|
488
844
|
}
|
|
845
|
+
if (signal.idempotencyKey) body.idempotencyKey = signal.idempotencyKey;
|
|
489
846
|
return body;
|
|
490
847
|
}
|
|
491
848
|
var Signals = class {
|
|
@@ -498,25 +855,42 @@ var Signals = class {
|
|
|
498
855
|
flusher;
|
|
499
856
|
cfg;
|
|
500
857
|
async track(signal, opts = {}) {
|
|
501
|
-
const
|
|
502
|
-
|
|
858
|
+
const body = signalToWire(signal);
|
|
859
|
+
if (body.idempotencyKey == null) body.idempotencyKey = newKey();
|
|
503
860
|
const sendNow = opts.wait || this.cfg.mode === "sync";
|
|
504
861
|
if (!sendNow) {
|
|
505
|
-
this.flusher.enqueue({ body,
|
|
506
|
-
return {
|
|
862
|
+
this.flusher.enqueue({ body, signal });
|
|
863
|
+
return { queued: true };
|
|
507
864
|
}
|
|
508
865
|
const res = await this.transport.request({
|
|
509
866
|
method: "POST",
|
|
510
867
|
path: "/api/v1/usage",
|
|
511
868
|
body,
|
|
512
|
-
|
|
869
|
+
// Best-effort metering opts into retries on transient failures.
|
|
870
|
+
retry: true
|
|
513
871
|
});
|
|
514
872
|
return {
|
|
515
|
-
idempotencyKey,
|
|
516
873
|
queued: false,
|
|
517
874
|
usageLog: res.usageLog ?? null
|
|
518
875
|
};
|
|
519
876
|
}
|
|
877
|
+
/**
|
|
878
|
+
* Validate + price a signal WITHOUT recording it (server `dryRun`). Returns
|
|
879
|
+
* the projected usage log so an integration can preflight before sending real
|
|
880
|
+
* traffic — no inbox row, wallet / outcome writes, or webhooks fire.
|
|
881
|
+
*/
|
|
882
|
+
async verify(signal) {
|
|
883
|
+
const body = signalToWire(signal);
|
|
884
|
+
body.dryRun = true;
|
|
885
|
+
const res = await this.transport.request({
|
|
886
|
+
method: "POST",
|
|
887
|
+
path: "/api/v1/usage",
|
|
888
|
+
body,
|
|
889
|
+
// A dry run writes nothing, so it is always safe to retry.
|
|
890
|
+
retry: true
|
|
891
|
+
});
|
|
892
|
+
return res.usageLog ?? null;
|
|
893
|
+
}
|
|
520
894
|
/** Meter a named credit. */
|
|
521
895
|
credit(input, opts) {
|
|
522
896
|
return this.track({ ...input, type: "credit" }, opts);
|
|
@@ -538,16 +912,178 @@ var Signals = class {
|
|
|
538
912
|
});
|
|
539
913
|
return { customer: res.customer, totals: res.totals, logs: res.logs };
|
|
540
914
|
}
|
|
915
|
+
/**
|
|
916
|
+
* Record one unit-metered event (a fixed-price meter with no tokens),
|
|
917
|
+
* addressed to a catalog unit by its `agentKey`. Sent immediately — unit
|
|
918
|
+
* events are not buffered. `POST /api/v1/units`.
|
|
919
|
+
*/
|
|
920
|
+
async unit(input) {
|
|
921
|
+
return this.transport.request({
|
|
922
|
+
method: "POST",
|
|
923
|
+
path: "/api/v1/units",
|
|
924
|
+
body: {
|
|
925
|
+
customerId: input.customerId,
|
|
926
|
+
agentKey: input.agentKey,
|
|
927
|
+
...input.member ? { member: input.member } : {}
|
|
928
|
+
},
|
|
929
|
+
// Best-effort metering opts into retries on transient failures.
|
|
930
|
+
retry: true
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
/** A customer's unit balances + recent unit events. `GET /api/v1/units`. */
|
|
934
|
+
unitUsage(params) {
|
|
935
|
+
return this.transport.request({
|
|
936
|
+
method: "GET",
|
|
937
|
+
path: "/api/v1/units",
|
|
938
|
+
query: { customerId: params.customerId, limit: params.limit }
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
};
|
|
942
|
+
|
|
943
|
+
// src/resources/units.ts
|
|
944
|
+
var Units = class {
|
|
945
|
+
constructor(transport) {
|
|
946
|
+
this.transport = transport;
|
|
947
|
+
}
|
|
948
|
+
transport;
|
|
949
|
+
/** List unit types. Pass `{ active: true }` to only return active ones. */
|
|
950
|
+
async list(params = {}) {
|
|
951
|
+
const res = await this.transport.request({
|
|
952
|
+
method: "GET",
|
|
953
|
+
path: "/api/v1/units-catalog",
|
|
954
|
+
query: { active: params.active === void 0 ? void 0 : String(params.active) }
|
|
955
|
+
});
|
|
956
|
+
return res.units;
|
|
957
|
+
}
|
|
958
|
+
/** Create a unit type. `POST /api/v1/units-catalog`. */
|
|
959
|
+
async create(input) {
|
|
960
|
+
const res = await this.transport.request({
|
|
961
|
+
method: "POST",
|
|
962
|
+
path: "/api/v1/units-catalog",
|
|
963
|
+
body: input
|
|
964
|
+
});
|
|
965
|
+
return res.unit;
|
|
966
|
+
}
|
|
967
|
+
/** Fetch one unit type with stats. `GET /api/v1/units-catalog/:id`. */
|
|
968
|
+
async get(id) {
|
|
969
|
+
const res = await this.transport.request({
|
|
970
|
+
method: "GET",
|
|
971
|
+
path: `/api/v1/units-catalog/${encodeURIComponent(id)}`
|
|
972
|
+
});
|
|
973
|
+
return res.unit;
|
|
974
|
+
}
|
|
975
|
+
/** Update a unit type's definition. `PATCH /api/v1/units-catalog/:id`. */
|
|
976
|
+
async update(id, input) {
|
|
977
|
+
const res = await this.transport.request({
|
|
978
|
+
method: "PATCH",
|
|
979
|
+
path: `/api/v1/units-catalog/${encodeURIComponent(id)}`,
|
|
980
|
+
body: input
|
|
981
|
+
});
|
|
982
|
+
return res.unit;
|
|
983
|
+
}
|
|
984
|
+
/** Activate or deactivate a unit type. */
|
|
985
|
+
async setActive(id, isActive) {
|
|
986
|
+
const res = await this.transport.request({
|
|
987
|
+
method: "PATCH",
|
|
988
|
+
path: `/api/v1/units-catalog/${encodeURIComponent(id)}`,
|
|
989
|
+
body: { isActive }
|
|
990
|
+
});
|
|
991
|
+
return res.unit;
|
|
992
|
+
}
|
|
993
|
+
/** Delete a unit type. */
|
|
994
|
+
async delete(id) {
|
|
995
|
+
await this.transport.request({
|
|
996
|
+
method: "DELETE",
|
|
997
|
+
path: `/api/v1/units-catalog/${encodeURIComponent(id)}`
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
1001
|
+
|
|
1002
|
+
// src/resources/webhooks.ts
|
|
1003
|
+
var Webhooks = class {
|
|
1004
|
+
constructor(transport) {
|
|
1005
|
+
this.transport = transport;
|
|
1006
|
+
}
|
|
1007
|
+
transport;
|
|
1008
|
+
/** List configured webhooks. `GET /api/v1/webhooks`. */
|
|
1009
|
+
async list() {
|
|
1010
|
+
const res = await this.transport.request({
|
|
1011
|
+
method: "GET",
|
|
1012
|
+
path: "/api/v1/webhooks"
|
|
1013
|
+
});
|
|
1014
|
+
return res.webhooks;
|
|
1015
|
+
}
|
|
1016
|
+
/** Create or replace the webhook for a kind. `POST /api/v1/webhooks`. */
|
|
1017
|
+
async upsert(input) {
|
|
1018
|
+
const res = await this.transport.request({
|
|
1019
|
+
method: "POST",
|
|
1020
|
+
path: "/api/v1/webhooks",
|
|
1021
|
+
body: input
|
|
1022
|
+
});
|
|
1023
|
+
return res.webhook;
|
|
1024
|
+
}
|
|
1025
|
+
/** Delete the webhook for a kind (idempotent). `DELETE /api/v1/webhooks/:kind`. */
|
|
1026
|
+
async delete(kind) {
|
|
1027
|
+
await this.transport.request({
|
|
1028
|
+
method: "DELETE",
|
|
1029
|
+
path: `/api/v1/webhooks/${encodeURIComponent(kind)}`
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
};
|
|
1033
|
+
|
|
1034
|
+
// src/resources/workspace.ts
|
|
1035
|
+
var Workspace = class {
|
|
1036
|
+
constructor(transport) {
|
|
1037
|
+
this.transport = transport;
|
|
1038
|
+
}
|
|
1039
|
+
transport;
|
|
1040
|
+
/** Identify the org + key behind the request, incl. `sandbox` | `live`. */
|
|
1041
|
+
me() {
|
|
1042
|
+
return this.transport.request({
|
|
1043
|
+
method: "GET",
|
|
1044
|
+
path: "/api/v1/me"
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
/** The org's enabled models with live prices. `{ active: true }` returns only
|
|
1048
|
+
* the models that can be metered right now. */
|
|
1049
|
+
async models(params = {}) {
|
|
1050
|
+
const res = await this.transport.request({
|
|
1051
|
+
method: "GET",
|
|
1052
|
+
path: "/api/v1/models",
|
|
1053
|
+
query: {
|
|
1054
|
+
active: params.active === void 0 ? void 0 : String(params.active)
|
|
1055
|
+
}
|
|
1056
|
+
});
|
|
1057
|
+
return res.models;
|
|
1058
|
+
}
|
|
541
1059
|
};
|
|
542
1060
|
|
|
543
1061
|
// src/client.ts
|
|
544
1062
|
var ClockNext = class {
|
|
545
|
-
/** Record + read usage signals. */
|
|
1063
|
+
/** Record + read usage signals (credit / wallet / outcome / unit). */
|
|
546
1064
|
signals;
|
|
547
|
-
/** Manage customers
|
|
1065
|
+
/** Manage customers, their members, wallet, and balances. */
|
|
548
1066
|
customers;
|
|
1067
|
+
/** Define and manage billing plans. */
|
|
1068
|
+
plans;
|
|
1069
|
+
/** Subscribe customers to plans (purchases). */
|
|
1070
|
+
purchases;
|
|
1071
|
+
/** Read invoices and mint hosted pay links. */
|
|
1072
|
+
invoices;
|
|
1073
|
+
/** Read payments (paid invoices). */
|
|
1074
|
+
payments;
|
|
1075
|
+
/** Manage the credit-type catalog. */
|
|
1076
|
+
credits;
|
|
1077
|
+
/** Manage the outcome-type catalog. */
|
|
1078
|
+
outcomes;
|
|
1079
|
+
/** Manage the unit-type catalog. */
|
|
1080
|
+
units;
|
|
1081
|
+
/** Configure threshold webhooks. */
|
|
1082
|
+
webhooks;
|
|
549
1083
|
/** Mint customer-portal embed tokens. */
|
|
550
1084
|
portal;
|
|
1085
|
+
/** Identify the workspace (sandbox/live) + list its models. `/me`, `/models`. */
|
|
1086
|
+
workspace;
|
|
551
1087
|
cfg;
|
|
552
1088
|
flusher;
|
|
553
1089
|
constructor(config) {
|
|
@@ -561,7 +1097,16 @@ var ClockNext = class {
|
|
|
561
1097
|
this.flusher = new Flusher(transport, this.cfg);
|
|
562
1098
|
this.signals = new Signals(transport, this.flusher, this.cfg);
|
|
563
1099
|
this.customers = new Customers(transport);
|
|
1100
|
+
this.plans = new Plans(transport);
|
|
1101
|
+
this.purchases = new Purchases(transport);
|
|
1102
|
+
this.invoices = new Invoices(transport);
|
|
1103
|
+
this.payments = new Payments(transport);
|
|
1104
|
+
this.credits = new Credits(transport);
|
|
1105
|
+
this.outcomes = new Outcomes(transport);
|
|
1106
|
+
this.units = new Units(transport);
|
|
1107
|
+
this.webhooks = new Webhooks(transport);
|
|
564
1108
|
this.portal = new Portal(transport);
|
|
1109
|
+
this.workspace = new Workspace(transport);
|
|
565
1110
|
}
|
|
566
1111
|
/** Number of signals currently buffered (async mode). */
|
|
567
1112
|
get pending() {
|
|
@@ -577,6 +1122,6 @@ var ClockNext = class {
|
|
|
577
1122
|
}
|
|
578
1123
|
};
|
|
579
1124
|
|
|
580
|
-
export { AllowanceError, AuthError, ClockNext, ClockNextError, ConflictError, NetworkError, NotFoundError, PlanError, RateLimitError, ServerError, ValidationError,
|
|
1125
|
+
export { AllowanceError, AuthError, ClockNext, ClockNextError, ConflictError, NetworkError, NotFoundError, PlanError, RateLimitError, ServerError, ValidationError, signalToWire };
|
|
581
1126
|
//# sourceMappingURL=index.js.map
|
|
582
1127
|
//# sourceMappingURL=index.js.map
|