@clocknext/sdk 0.1.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 +161 -0
- package/dist/index.cjs +596 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +580 -0
- package/dist/index.d.ts +580 -0
- package/dist/index.js +582 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types for the ClockNext SDK.
|
|
3
|
+
*
|
|
4
|
+
* The signal types mirror the server's `recordSchema` (the body of
|
|
5
|
+
* `POST /api/v1/usage`) as a discriminated union, so the per-type required
|
|
6
|
+
* fields the server enforces with Zod refinements are enforced here at compile
|
|
7
|
+
* time instead of surfacing as runtime 422s.
|
|
8
|
+
*/
|
|
9
|
+
type SignalType = "wallet" | "credit" | "outcome";
|
|
10
|
+
type SignalStatus = "SUCCESS" | "FAILURE" | "PENDING";
|
|
11
|
+
/** Token counts for a signal. All default to 0. */
|
|
12
|
+
interface Tokens {
|
|
13
|
+
input?: number;
|
|
14
|
+
output?: number;
|
|
15
|
+
cache?: number;
|
|
16
|
+
}
|
|
17
|
+
interface SignalBase {
|
|
18
|
+
/** The customer this usage belongs to (required). */
|
|
19
|
+
customerId: string;
|
|
20
|
+
/**
|
|
21
|
+
* The model's catalog id — must be enabled in your ClockNext org
|
|
22
|
+
* (Settings → Models). Matched case-insensitively against `OrgModel.modelId`.
|
|
23
|
+
*/
|
|
24
|
+
model: string;
|
|
25
|
+
/** Token counts for this call. */
|
|
26
|
+
tokens?: Tokens;
|
|
27
|
+
/** Optional member (email) this usage is attributed to. */
|
|
28
|
+
member?: string;
|
|
29
|
+
/** Defaults to SUCCESS. */
|
|
30
|
+
status?: SignalStatus;
|
|
31
|
+
/** Free-form metadata. Credit rules can target `custom.<key>` paths. */
|
|
32
|
+
custom?: Record<string, unknown>;
|
|
33
|
+
/**
|
|
34
|
+
* Stable, unique id for this logical event. Generated automatically if
|
|
35
|
+
* omitted. Pass a deterministic value to dedupe across process restarts.
|
|
36
|
+
*/
|
|
37
|
+
idempotencyKey?: string;
|
|
38
|
+
}
|
|
39
|
+
/** Meters a credit, identified by the credit's stable `key`. */
|
|
40
|
+
interface CreditSignal extends SignalBase {
|
|
41
|
+
type: "credit";
|
|
42
|
+
/** The Credit's stable key (assigned when the credit was created). */
|
|
43
|
+
key: string;
|
|
44
|
+
}
|
|
45
|
+
/** Debits the customer's wallet in USD at the model's cost. */
|
|
46
|
+
interface WalletSignal extends SignalBase {
|
|
47
|
+
type: "wallet";
|
|
48
|
+
}
|
|
49
|
+
/** Advances one step of a multi-call outcome workflow, by the step's `key`. */
|
|
50
|
+
interface OutcomeSignal extends SignalBase {
|
|
51
|
+
type: "outcome";
|
|
52
|
+
/** The OutcomeStep's stable key — identifies the outcome AND the step. */
|
|
53
|
+
key: string;
|
|
54
|
+
}
|
|
55
|
+
type Signal = CreditSignal | WalletSignal | OutcomeSignal;
|
|
56
|
+
/** Helper input shapes for the per-type convenience methods. */
|
|
57
|
+
type CreditInput = Omit<CreditSignal, "type">;
|
|
58
|
+
type WalletInput = Omit<WalletSignal, "type">;
|
|
59
|
+
type OutcomeInput = Omit<OutcomeSignal, "type">;
|
|
60
|
+
interface SerializedRuleApplication {
|
|
61
|
+
ruleId: string | null;
|
|
62
|
+
ruleName: string;
|
|
63
|
+
creditsApplied: number;
|
|
64
|
+
}
|
|
65
|
+
interface SerializedOutcomeAttribution {
|
|
66
|
+
name: string;
|
|
67
|
+
step: string;
|
|
68
|
+
completed: boolean;
|
|
69
|
+
progressSteps: string[];
|
|
70
|
+
totalSteps: number;
|
|
71
|
+
}
|
|
72
|
+
interface SerializedUsageLog {
|
|
73
|
+
id: string;
|
|
74
|
+
customerId: string;
|
|
75
|
+
modelName: string | null;
|
|
76
|
+
creditName: string | null;
|
|
77
|
+
member: string | null;
|
|
78
|
+
inputTokens: number;
|
|
79
|
+
outputTokens: number;
|
|
80
|
+
cacheTokens: number;
|
|
81
|
+
/** Total credits deducted: the call's real per-model provider cost as a
|
|
82
|
+
* fraction of the credit's base price (i.e. cost + your margin), plus any
|
|
83
|
+
* credit-rule add-ons; 0 for wallet-money calls. */
|
|
84
|
+
credit: number;
|
|
85
|
+
providedCost: number;
|
|
86
|
+
customerCost: number;
|
|
87
|
+
status: SignalStatus;
|
|
88
|
+
appliedRules: SerializedRuleApplication[];
|
|
89
|
+
outcome: SerializedOutcomeAttribution | null;
|
|
90
|
+
createdAt: string;
|
|
91
|
+
}
|
|
92
|
+
/** Result of a single signal send (sync mode, or `{ wait: true }`). */
|
|
93
|
+
interface TrackResult {
|
|
94
|
+
/** The idempotency key the event was sent with. */
|
|
95
|
+
idempotencyKey: string;
|
|
96
|
+
/** True when the signal was buffered (async mode) and not yet sent. */
|
|
97
|
+
queued: boolean;
|
|
98
|
+
/** The computed usage log — present only once the signal has been sent. */
|
|
99
|
+
usageLog?: SerializedUsageLog | null;
|
|
100
|
+
}
|
|
101
|
+
interface UsageList {
|
|
102
|
+
customer: {
|
|
103
|
+
id: string;
|
|
104
|
+
name: string;
|
|
105
|
+
creditAllowance: number;
|
|
106
|
+
};
|
|
107
|
+
totals: {
|
|
108
|
+
count: number;
|
|
109
|
+
cost: number;
|
|
110
|
+
revenue: number;
|
|
111
|
+
profit: number;
|
|
112
|
+
creditsUsed: number;
|
|
113
|
+
};
|
|
114
|
+
logs: SerializedUsageLog[];
|
|
115
|
+
}
|
|
116
|
+
interface ApiCustomer {
|
|
117
|
+
id: string;
|
|
118
|
+
slug: string;
|
|
119
|
+
name: string;
|
|
120
|
+
description: string | null;
|
|
121
|
+
email: string | null;
|
|
122
|
+
website: string | null;
|
|
123
|
+
phone: string | null;
|
|
124
|
+
legalName: string | null;
|
|
125
|
+
addressLine1: string | null;
|
|
126
|
+
addressLine2: string | null;
|
|
127
|
+
city: string | null;
|
|
128
|
+
state: string | null;
|
|
129
|
+
country: string | null;
|
|
130
|
+
pincode: string | null;
|
|
131
|
+
taxId: string | null;
|
|
132
|
+
notes: string | null;
|
|
133
|
+
logoUrl: string | null;
|
|
134
|
+
currencyCode: string;
|
|
135
|
+
creditAllowance: number;
|
|
136
|
+
createdAt: string;
|
|
137
|
+
updatedAt: string;
|
|
138
|
+
}
|
|
139
|
+
/** Writable customer profile fields. `null` clears a column; omit to leave it. */
|
|
140
|
+
interface CustomerProfileInput {
|
|
141
|
+
name?: string;
|
|
142
|
+
description?: string | null;
|
|
143
|
+
email?: string | null;
|
|
144
|
+
website?: string | null;
|
|
145
|
+
phone?: string | null;
|
|
146
|
+
legalName?: string | null;
|
|
147
|
+
addressLine1?: string | null;
|
|
148
|
+
addressLine2?: string | null;
|
|
149
|
+
city?: string | null;
|
|
150
|
+
state?: string | null;
|
|
151
|
+
country?: string | null;
|
|
152
|
+
pincode?: string | null;
|
|
153
|
+
taxId?: string | null;
|
|
154
|
+
notes?: string | null;
|
|
155
|
+
logoUrl?: string | null;
|
|
156
|
+
creditAllowance?: number;
|
|
157
|
+
/** ISO 4217 (3 letters); must be enabled for the org. */
|
|
158
|
+
currencyCode?: string;
|
|
159
|
+
}
|
|
160
|
+
/** `name` is required on create. */
|
|
161
|
+
type CreateCustomerInput = CustomerProfileInput & {
|
|
162
|
+
name: string;
|
|
163
|
+
};
|
|
164
|
+
interface CustomerList {
|
|
165
|
+
customers: ApiCustomer[];
|
|
166
|
+
nextCursor: string | null;
|
|
167
|
+
}
|
|
168
|
+
type RevenueRange = "30d" | "6m" | "1y" | "all";
|
|
169
|
+
/** Loosely typed insight payloads (revenue/balances/plan) — server shapes the
|
|
170
|
+
* detail; we surface the whole envelope minus the `ok` flag. */
|
|
171
|
+
type CustomerBalances = {
|
|
172
|
+
customerId: string;
|
|
173
|
+
currency: {
|
|
174
|
+
code: string;
|
|
175
|
+
rateUsd: number;
|
|
176
|
+
};
|
|
177
|
+
hasPlan: boolean;
|
|
178
|
+
wallet: {
|
|
179
|
+
balanceUsd: number;
|
|
180
|
+
} | null;
|
|
181
|
+
credits: unknown[];
|
|
182
|
+
outcomes: unknown[];
|
|
183
|
+
units: unknown[];
|
|
184
|
+
};
|
|
185
|
+
type CustomerPlan = {
|
|
186
|
+
customerId: string;
|
|
187
|
+
currency: {
|
|
188
|
+
code: string;
|
|
189
|
+
rateUsd: number;
|
|
190
|
+
};
|
|
191
|
+
plan: Record<string, unknown> | null;
|
|
192
|
+
};
|
|
193
|
+
type CustomerRevenue = {
|
|
194
|
+
customerId: string;
|
|
195
|
+
range: RevenueRange;
|
|
196
|
+
window: unknown;
|
|
197
|
+
bucket: unknown;
|
|
198
|
+
currency: {
|
|
199
|
+
code: string;
|
|
200
|
+
rateUsd: number;
|
|
201
|
+
};
|
|
202
|
+
revenue: number;
|
|
203
|
+
cost: number;
|
|
204
|
+
profit: number;
|
|
205
|
+
grossMargin: number;
|
|
206
|
+
deltas: Record<string, unknown>;
|
|
207
|
+
trend: unknown[];
|
|
208
|
+
};
|
|
209
|
+
interface PortalTokenInput {
|
|
210
|
+
customerId: string;
|
|
211
|
+
ttlSeconds?: number;
|
|
212
|
+
memberEmail?: string;
|
|
213
|
+
}
|
|
214
|
+
interface PortalToken {
|
|
215
|
+
token: string;
|
|
216
|
+
/** Unix seconds. */
|
|
217
|
+
expiresAt: number;
|
|
218
|
+
/** Seconds until expiry. */
|
|
219
|
+
expiresIn: number;
|
|
220
|
+
}
|
|
221
|
+
type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
|
|
222
|
+
interface Logger {
|
|
223
|
+
debug?: (...args: unknown[]) => void;
|
|
224
|
+
warn?: (...args: unknown[]) => void;
|
|
225
|
+
error?: (...args: unknown[]) => void;
|
|
226
|
+
}
|
|
227
|
+
type DropReason = "queue_full" | "send_failed";
|
|
228
|
+
interface ClockNextConfig {
|
|
229
|
+
/** The org API key (`cnk_…`). Required. */
|
|
230
|
+
apiKey: string;
|
|
231
|
+
/** API base URL. Defaults to https://app.clocknext.com */
|
|
232
|
+
baseUrl?: string;
|
|
233
|
+
/**
|
|
234
|
+
* "async" (default) buffers signals and flushes in the background.
|
|
235
|
+
* "sync" awaits every send inside `track()`.
|
|
236
|
+
*/
|
|
237
|
+
mode?: "async" | "sync";
|
|
238
|
+
/** Per-request timeout (ms). Default 10_000. */
|
|
239
|
+
timeoutMs?: number;
|
|
240
|
+
/** Background batching config (async mode). */
|
|
241
|
+
batch?: {
|
|
242
|
+
/** Flush when this many signals are buffered. Default 20. */
|
|
243
|
+
maxSize?: number;
|
|
244
|
+
/** Flush at least this often (ms). Default 2000. */
|
|
245
|
+
maxIntervalMs?: number;
|
|
246
|
+
/** Max concurrent in-flight sends per flush. Default 5. */
|
|
247
|
+
maxConcurrency?: number;
|
|
248
|
+
/** Drop new signals once the buffer reaches this size. Default 10_000. */
|
|
249
|
+
maxQueueSize?: number;
|
|
250
|
+
};
|
|
251
|
+
/** Retry policy for transient failures. */
|
|
252
|
+
retry?: {
|
|
253
|
+
/** Total attempts including the first. Default 5. */
|
|
254
|
+
maxAttempts?: number;
|
|
255
|
+
/** Base backoff (ms). Default 200. */
|
|
256
|
+
baseDelayMs?: number;
|
|
257
|
+
/** Max backoff (ms). Default 10_000. */
|
|
258
|
+
maxDelayMs?: number;
|
|
259
|
+
};
|
|
260
|
+
/** Injectable fetch (defaults to global fetch). */
|
|
261
|
+
fetch?: FetchLike;
|
|
262
|
+
logger?: Logger;
|
|
263
|
+
/** Called when a signal permanently fails (after retries / non-retryable). */
|
|
264
|
+
onError?: (error: unknown, signal?: Signal) => void;
|
|
265
|
+
/** Called after each successful background flush with the sent count. */
|
|
266
|
+
onFlush?: (sentCount: number) => void;
|
|
267
|
+
/** Called before each retry attempt. */
|
|
268
|
+
onRetry?: (info: {
|
|
269
|
+
attempt: number;
|
|
270
|
+
delayMs: number;
|
|
271
|
+
error: unknown;
|
|
272
|
+
}) => void;
|
|
273
|
+
/** Called when a signal is dropped without being sent. */
|
|
274
|
+
onDrop?: (signal: Signal, reason: DropReason) => void;
|
|
275
|
+
}
|
|
276
|
+
/** Fully-resolved config with defaults applied. */
|
|
277
|
+
interface ResolvedConfig {
|
|
278
|
+
apiKey: string;
|
|
279
|
+
baseUrl: string;
|
|
280
|
+
mode: "async" | "sync";
|
|
281
|
+
timeoutMs: number;
|
|
282
|
+
batch: {
|
|
283
|
+
maxSize: number;
|
|
284
|
+
maxIntervalMs: number;
|
|
285
|
+
maxConcurrency: number;
|
|
286
|
+
maxQueueSize: number;
|
|
287
|
+
};
|
|
288
|
+
retry: {
|
|
289
|
+
maxAttempts: number;
|
|
290
|
+
baseDelayMs: number;
|
|
291
|
+
maxDelayMs: number;
|
|
292
|
+
};
|
|
293
|
+
fetch: FetchLike;
|
|
294
|
+
logger: Logger;
|
|
295
|
+
onError?: (error: unknown, signal?: Signal) => void;
|
|
296
|
+
onFlush?: (sentCount: number) => void;
|
|
297
|
+
onRetry?: (info: {
|
|
298
|
+
attempt: number;
|
|
299
|
+
delayMs: number;
|
|
300
|
+
error: unknown;
|
|
301
|
+
}) => void;
|
|
302
|
+
onDrop?: (signal: Signal, reason: DropReason) => void;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
interface RequestOptions {
|
|
306
|
+
method: "GET" | "POST" | "PATCH" | "DELETE";
|
|
307
|
+
path: string;
|
|
308
|
+
query?: Record<string, string | number | undefined>;
|
|
309
|
+
body?: unknown;
|
|
310
|
+
/** Reused across retries — what makes a retried write safe. */
|
|
311
|
+
idempotencyKey?: string;
|
|
312
|
+
/** Disable retries for this call (rarely needed). */
|
|
313
|
+
retry?: boolean;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* The single HTTP seam. Builds the authenticated request, enforces a timeout,
|
|
317
|
+
* unwraps the `{ ok, ... }` envelope, maps failures to typed errors, and retries
|
|
318
|
+
* transient ones with backoff + jitter — reusing the same idempotency key on
|
|
319
|
+
* every attempt.
|
|
320
|
+
*/
|
|
321
|
+
declare class Transport {
|
|
322
|
+
private readonly cfg;
|
|
323
|
+
constructor(cfg: ResolvedConfig);
|
|
324
|
+
request<T = unknown>(opts: RequestOptions): Promise<T>;
|
|
325
|
+
/** One attempt: fetch + timeout + envelope/status handling. */
|
|
326
|
+
private once;
|
|
327
|
+
private headers;
|
|
328
|
+
private buildUrl;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* The `customers` resource — full CRUD over the org's customers plus the
|
|
333
|
+
* read-only insight endpoints (usage / balances / plan / revenue). Every call
|
|
334
|
+
* is scoped to the API key's organisation server-side; a customer in another
|
|
335
|
+
* org resolves as a NotFoundError.
|
|
336
|
+
*/
|
|
337
|
+
declare class Customers {
|
|
338
|
+
private readonly transport;
|
|
339
|
+
constructor(transport: Transport);
|
|
340
|
+
/** Create a customer. `POST /api/v1/customers`. */
|
|
341
|
+
create(input: CreateCustomerInput): Promise<ApiCustomer>;
|
|
342
|
+
/** List customers, most-recent first (cursor-paginated). `GET /api/v1/customers`. */
|
|
343
|
+
list(params?: {
|
|
344
|
+
limit?: number;
|
|
345
|
+
q?: string;
|
|
346
|
+
cursor?: string;
|
|
347
|
+
}): Promise<CustomerList>;
|
|
348
|
+
/**
|
|
349
|
+
* Async iterator over ALL customers, transparently following the cursor.
|
|
350
|
+
* `for await (const c of cnk.customers.iterate()) { … }`
|
|
351
|
+
*/
|
|
352
|
+
iterate(params?: {
|
|
353
|
+
limit?: number;
|
|
354
|
+
q?: string;
|
|
355
|
+
}): AsyncGenerator<ApiCustomer>;
|
|
356
|
+
/** Fetch one customer. `GET /api/v1/customers/:id`. */
|
|
357
|
+
get(id: string): Promise<ApiCustomer>;
|
|
358
|
+
/** Update a customer (partial). `PATCH /api/v1/customers/:id`. */
|
|
359
|
+
update(id: string, input: CustomerProfileInput): Promise<ApiCustomer>;
|
|
360
|
+
/** Delete a customer (members, invitations, usage logs cascade). */
|
|
361
|
+
delete(id: string): Promise<void>;
|
|
362
|
+
/** A customer's usage history. `GET /api/v1/customers/:id/usage`. */
|
|
363
|
+
usage(id: string, params?: {
|
|
364
|
+
limit?: number;
|
|
365
|
+
}): Promise<UsageList>;
|
|
366
|
+
/** Wallet / credit / outcome / unit balances. `GET …/:id/balances`. */
|
|
367
|
+
balances(id: string): Promise<CustomerBalances>;
|
|
368
|
+
/** The customer's current plan. `GET …/:id/plan`. */
|
|
369
|
+
plan(id: string): Promise<CustomerPlan>;
|
|
370
|
+
/** Revenue / cost / profit / margin over a window. `GET …/:id/revenue`. */
|
|
371
|
+
revenue(id: string, params?: {
|
|
372
|
+
range?: RevenueRange;
|
|
373
|
+
}): Promise<CustomerRevenue>;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* The `portal` resource — mint a short-lived embed token for one of your
|
|
378
|
+
* customers, then render `<iframe src=".../portal/embed?token=<token>">`. The
|
|
379
|
+
* HS256 signing secret never leaves ClockNext; your backend only needs its API
|
|
380
|
+
* key. `POST /api/v1/portal/token`.
|
|
381
|
+
*/
|
|
382
|
+
declare class Portal {
|
|
383
|
+
private readonly transport;
|
|
384
|
+
constructor(transport: Transport);
|
|
385
|
+
createToken(input: PortalTokenInput): Promise<PortalToken>;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/** One buffered, wire-ready signal awaiting a background flush. */
|
|
389
|
+
interface QueuedSignal {
|
|
390
|
+
/** The exact JSON body to POST to /api/v1/usage. */
|
|
391
|
+
body: Record<string, unknown>;
|
|
392
|
+
/** Reused across retries. */
|
|
393
|
+
idempotencyKey: string;
|
|
394
|
+
/** The original signal — passed to onError/onDrop hooks. */
|
|
395
|
+
signal: Signal;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Pluggable buffer. The default is in-memory; a host that needs to survive a
|
|
399
|
+
* crash *before* send can implement this against disk/Redis/SQLite.
|
|
400
|
+
*/
|
|
401
|
+
interface QueueStore {
|
|
402
|
+
push(item: QueuedSignal): void;
|
|
403
|
+
/** Remove and return up to `n` items (FIFO). */
|
|
404
|
+
take(n: number): QueuedSignal[];
|
|
405
|
+
size(): number;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Background send engine for async-mode signals. Buffers wire-ready signals and
|
|
410
|
+
* flushes them when the buffer reaches `maxSize` OR every `maxIntervalMs`,
|
|
411
|
+
* whichever comes first — the Segment/Amberflo pattern. The Transport owns
|
|
412
|
+
* retries; `close()` drains and awaits in-flight sends so a serverless process
|
|
413
|
+
* can flush before it freezes.
|
|
414
|
+
*/
|
|
415
|
+
declare class Flusher {
|
|
416
|
+
private readonly transport;
|
|
417
|
+
private readonly cfg;
|
|
418
|
+
private readonly queue;
|
|
419
|
+
private timer;
|
|
420
|
+
private draining;
|
|
421
|
+
private closed;
|
|
422
|
+
constructor(transport: Transport, cfg: ResolvedConfig);
|
|
423
|
+
/** Buffer one signal. Drops (with onDrop) when closed or the queue is full. */
|
|
424
|
+
enqueue(item: QueuedSignal): void;
|
|
425
|
+
/** Arm the interval timer if it isn't already running. */
|
|
426
|
+
private arm;
|
|
427
|
+
/**
|
|
428
|
+
* Send everything currently buffered. Overlapping calls share the single
|
|
429
|
+
* in-progress drain, so total in-flight sends never exceed `maxConcurrency`.
|
|
430
|
+
*/
|
|
431
|
+
flush(): Promise<void>;
|
|
432
|
+
/** Drain the buffer in concurrency-bounded waves until it's empty. */
|
|
433
|
+
private drain;
|
|
434
|
+
/** Send one item; never throws — failures surface via onError/onDrop. */
|
|
435
|
+
private send;
|
|
436
|
+
get size(): number;
|
|
437
|
+
/** Flush remaining signals, then refuse further enqueues. */
|
|
438
|
+
close(): Promise<void>;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/** Builds the `POST /api/v1/usage` body from a typed Signal. */
|
|
442
|
+
declare function signalToWire(signal: Signal): Record<string, unknown>;
|
|
443
|
+
/**
|
|
444
|
+
* The `signals` resource: record usage signals and read them back.
|
|
445
|
+
*
|
|
446
|
+
* In async mode (default), `track()` buffers the signal and returns immediately
|
|
447
|
+
* with `{ queued: true }`. Pass `{ wait: true }` (or construct the client in
|
|
448
|
+
* `sync` mode) to await the send and receive the computed `usageLog`.
|
|
449
|
+
*/
|
|
450
|
+
declare class Signals {
|
|
451
|
+
private readonly transport;
|
|
452
|
+
private readonly flusher;
|
|
453
|
+
private readonly cfg;
|
|
454
|
+
constructor(transport: Transport, flusher: Flusher, cfg: ResolvedConfig);
|
|
455
|
+
track(signal: Signal, opts?: {
|
|
456
|
+
wait?: boolean;
|
|
457
|
+
}): Promise<TrackResult>;
|
|
458
|
+
/** Meter a named credit. */
|
|
459
|
+
credit(input: CreditInput, opts?: {
|
|
460
|
+
wait?: boolean;
|
|
461
|
+
}): Promise<TrackResult>;
|
|
462
|
+
/** Debit the customer's wallet at model cost. */
|
|
463
|
+
wallet(input: WalletInput, opts?: {
|
|
464
|
+
wait?: boolean;
|
|
465
|
+
}): Promise<TrackResult>;
|
|
466
|
+
/** Advance one step of an outcome workflow. */
|
|
467
|
+
outcome(input: OutcomeInput, opts?: {
|
|
468
|
+
wait?: boolean;
|
|
469
|
+
}): Promise<TrackResult>;
|
|
470
|
+
/** Recent usage logs + totals for a customer. `GET /api/v1/usage`. */
|
|
471
|
+
list(params: {
|
|
472
|
+
customerId: string;
|
|
473
|
+
limit?: number;
|
|
474
|
+
}): Promise<UsageList>;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* The ClockNext client.
|
|
479
|
+
*
|
|
480
|
+
* ```ts
|
|
481
|
+
* const cnk = new ClockNext({ apiKey: process.env.CLOCKNEXT_API_KEY! });
|
|
482
|
+
*
|
|
483
|
+
* await cnk.signals.credit({
|
|
484
|
+
* customerId: "cus_123",
|
|
485
|
+
* model: "gpt-4o", // catalog model id
|
|
486
|
+
* key: "api_credit", // the credit's stable key
|
|
487
|
+
* tokens: { input: 1200, output: 340 },
|
|
488
|
+
* });
|
|
489
|
+
*
|
|
490
|
+
* // before a serverless function returns / on shutdown:
|
|
491
|
+
* await cnk.flush();
|
|
492
|
+
* ```
|
|
493
|
+
*/
|
|
494
|
+
declare class ClockNext {
|
|
495
|
+
/** Record + read usage signals. */
|
|
496
|
+
readonly signals: Signals;
|
|
497
|
+
/** Manage customers and read their insights. */
|
|
498
|
+
readonly customers: Customers;
|
|
499
|
+
/** Mint customer-portal embed tokens. */
|
|
500
|
+
readonly portal: Portal;
|
|
501
|
+
private readonly cfg;
|
|
502
|
+
private readonly flusher;
|
|
503
|
+
constructor(config: ClockNextConfig);
|
|
504
|
+
/** Number of signals currently buffered (async mode). */
|
|
505
|
+
get pending(): number;
|
|
506
|
+
/** Force-send all buffered signals now. Await before a serverless return. */
|
|
507
|
+
flush(): Promise<void>;
|
|
508
|
+
/** Flush and stop the background timer. Call on graceful shutdown. */
|
|
509
|
+
close(): Promise<void>;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Typed error hierarchy. The server returns `{ ok: false, error }` with a
|
|
514
|
+
* meaningful HTTP status; we map that status to a class so callers can branch
|
|
515
|
+
* on the *kind* of failure (e.g. allowance exhausted ⇒ upsell; network ⇒ ignore,
|
|
516
|
+
* it's queued) rather than string-matching messages.
|
|
517
|
+
*/
|
|
518
|
+
declare class ClockNextError extends Error {
|
|
519
|
+
/** HTTP status, when the failure came from a server response. */
|
|
520
|
+
readonly status?: number;
|
|
521
|
+
/** The idempotency key of the offending signal, when applicable. */
|
|
522
|
+
readonly idempotencyKey?: string;
|
|
523
|
+
/** Whether the SDK considers this failure worth retrying. */
|
|
524
|
+
readonly retryable: boolean;
|
|
525
|
+
constructor(message: string, opts?: {
|
|
526
|
+
status?: number;
|
|
527
|
+
idempotencyKey?: string;
|
|
528
|
+
retryable?: boolean;
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
/** 401 — missing / invalid / expired API key. */
|
|
532
|
+
declare class AuthError extends ClockNextError {
|
|
533
|
+
}
|
|
534
|
+
/** 400 — malformed request / schema validation failure. */
|
|
535
|
+
declare class ValidationError extends ClockNextError {
|
|
536
|
+
}
|
|
537
|
+
/** 404 — unknown customer (also returned for cross-org access). */
|
|
538
|
+
declare class NotFoundError extends ClockNextError {
|
|
539
|
+
}
|
|
540
|
+
/** 422 — no active plan, meter not in plan, model not enabled, member missing. */
|
|
541
|
+
declare class PlanError extends ClockNextError {
|
|
542
|
+
}
|
|
543
|
+
/** 422 — insufficient credits / units / outcomes for the call. */
|
|
544
|
+
declare class AllowanceError extends ClockNextError {
|
|
545
|
+
}
|
|
546
|
+
/** 409 — conflict (e.g. idempotency-key reuse with a different payload). */
|
|
547
|
+
declare class ConflictError extends ClockNextError {
|
|
548
|
+
}
|
|
549
|
+
/** 429 — rate limited. */
|
|
550
|
+
declare class RateLimitError extends ClockNextError {
|
|
551
|
+
readonly retryAfterMs?: number;
|
|
552
|
+
constructor(message: string, opts?: {
|
|
553
|
+
idempotencyKey?: string;
|
|
554
|
+
retryAfterMs?: number;
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
/** 5xx — server-side error. */
|
|
558
|
+
declare class ServerError extends ClockNextError {
|
|
559
|
+
}
|
|
560
|
+
/** fetch threw / request timed out — request never completed. */
|
|
561
|
+
declare class NetworkError extends ClockNextError {
|
|
562
|
+
constructor(message: string, opts?: {
|
|
563
|
+
idempotencyKey?: string;
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Generates a stable idempotency key for one logical event. Generated ONCE at
|
|
569
|
+
* enqueue/track time and reused across every retry of that event — regenerating
|
|
570
|
+
* per HTTP attempt would defeat dedup (the documented foot-gun in most usage
|
|
571
|
+
* SDKs).
|
|
572
|
+
*
|
|
573
|
+
* Uses the Web Crypto global (`globalThis.crypto`), which is present on Node 18+
|
|
574
|
+
* and every edge runtime — and, unlike a static `node:crypto` import, never
|
|
575
|
+
* fails to load on runtimes that lack Node built-ins. Degrades through
|
|
576
|
+
* `getRandomValues` to `Math.random` so it always returns a v4 UUID.
|
|
577
|
+
*/
|
|
578
|
+
declare function newIdempotencyKey(): string;
|
|
579
|
+
|
|
580
|
+
export { AllowanceError, type ApiCustomer, AuthError, ClockNext, type ClockNextConfig, ClockNextError, ConflictError, type CreateCustomerInput, type CreditInput, type CreditSignal, type CustomerBalances, type CustomerList, type CustomerPlan, type CustomerProfileInput, type CustomerRevenue, type DropReason, type FetchLike, type Logger, NetworkError, NotFoundError, type OutcomeInput, type OutcomeSignal, PlanError, type PortalToken, type PortalTokenInput, type QueueStore, type QueuedSignal, RateLimitError, type RevenueRange, type SerializedOutcomeAttribution, type SerializedRuleApplication, type SerializedUsageLog, ServerError, type Signal, type SignalStatus, type SignalType, type Tokens, type TrackResult, type UsageList, ValidationError, type WalletInput, type WalletSignal, newIdempotencyKey, signalToWire };
|