mbase-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/dist/index.cjs ADDED
@@ -0,0 +1,589 @@
1
+ 'use strict';
2
+
3
+ // src/errors.ts
4
+ var MeterbaseError = class extends Error {
5
+ constructor(message, options) {
6
+ super(message, options);
7
+ this.name = new.target.name;
8
+ }
9
+ };
10
+ var APIError = class extends MeterbaseError {
11
+ status;
12
+ code;
13
+ /** From `X-Request-Id`, when the engine sends one. */
14
+ requestId;
15
+ body;
16
+ constructor(args) {
17
+ super(args.message);
18
+ this.status = args.status;
19
+ this.code = args.code;
20
+ this.requestId = args.requestId;
21
+ this.body = args.body;
22
+ }
23
+ };
24
+ var AuthenticationError = class extends APIError {
25
+ };
26
+ var PermissionDeniedError = class extends APIError {
27
+ };
28
+ var NotFoundError = class extends APIError {
29
+ };
30
+ var ConflictError = class extends APIError {
31
+ };
32
+ var IdempotencyConflictError = class extends ConflictError {
33
+ /** The event that key already recorded. Undefined only if the engine sent a
34
+ * body this could not be read out of. */
35
+ existingEventId;
36
+ constructor(args) {
37
+ super(args);
38
+ this.existingEventId = existingEventId(args.body);
39
+ }
40
+ };
41
+ var InvalidRequestError = class extends APIError {
42
+ };
43
+ var RateLimitError = class extends APIError {
44
+ /** Seconds to wait, from `Retry-After`, when the engine sends one. */
45
+ retryAfter;
46
+ constructor(args) {
47
+ super(args);
48
+ this.retryAfter = args.retryAfter;
49
+ }
50
+ };
51
+ var ServerError = class extends APIError {
52
+ };
53
+ var ConnectionError = class extends MeterbaseError {
54
+ };
55
+ var TimeoutError = class extends ConnectionError {
56
+ };
57
+ function errorFromResponse(args) {
58
+ switch (args.status) {
59
+ case 401:
60
+ return new AuthenticationError(args);
61
+ case 403:
62
+ return new PermissionDeniedError(args);
63
+ case 404:
64
+ return new NotFoundError(args);
65
+ case 409:
66
+ return args.code === "idempotency_conflict" ? new IdempotencyConflictError(args) : new ConflictError(args);
67
+ case 422:
68
+ return new InvalidRequestError(args);
69
+ case 429:
70
+ return new RateLimitError(args);
71
+ default:
72
+ return args.status >= 500 ? new ServerError(args) : new APIError(args);
73
+ }
74
+ }
75
+ function existingEventId(body) {
76
+ if (typeof body !== "object" || body === null) return void 0;
77
+ const error = body.error;
78
+ if (typeof error !== "object" || error === null) return void 0;
79
+ const id = error.existing_event_id;
80
+ return typeof id === "string" ? id : void 0;
81
+ }
82
+
83
+ // src/client.ts
84
+ var DEFAULT_BASE_URL = "https://api.meterbase.tech";
85
+ var DEFAULT_TIMEOUT = 1e4;
86
+ var DEFAULT_MAX_RETRIES = 2;
87
+ var MAX_BACKOFF = 8e3;
88
+ var Client = class {
89
+ #apiKey;
90
+ #baseUrl;
91
+ #timeout;
92
+ #maxRetries;
93
+ #fetch;
94
+ constructor(options) {
95
+ if (!options.apiKey) {
96
+ throw new MeterbaseError(
97
+ "An API key is required: new Meterbase({ apiKey: 'mb_sk_live_\u2026' })."
98
+ );
99
+ }
100
+ this.#apiKey = options.apiKey;
101
+ this.#baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
102
+ this.#timeout = options.timeout ?? DEFAULT_TIMEOUT;
103
+ this.#maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
104
+ this.#fetch = options.fetch ?? globalThis.fetch;
105
+ if (typeof this.#fetch !== "function") {
106
+ throw new MeterbaseError(
107
+ "No fetch implementation: pass one as `fetch`, or run on Node 18+."
108
+ );
109
+ }
110
+ }
111
+ async request(req) {
112
+ const maxRetries = req.maxRetries ?? this.#maxRetries;
113
+ const retryable = req.method === "GET" || req.method === "DELETE" || req.idempotent === true;
114
+ let lastError;
115
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
116
+ if (attempt > 0) await sleep(backoff(attempt, lastError));
117
+ try {
118
+ const response = await this.#send(req);
119
+ if (response.ok) return await parseBody(response);
120
+ const error = await errorFrom(response);
121
+ if (retryable && attempt < maxRetries && shouldRetry(response.status)) {
122
+ lastError = error;
123
+ continue;
124
+ }
125
+ throw error;
126
+ } catch (cause) {
127
+ if (cause instanceof MeterbaseError && !(cause instanceof ConnectionError)) {
128
+ throw cause;
129
+ }
130
+ if (!retryable || attempt >= maxRetries) throw cause;
131
+ lastError = cause;
132
+ }
133
+ }
134
+ throw lastError;
135
+ }
136
+ async #send(req) {
137
+ const url = new URL(this.#baseUrl + req.path);
138
+ for (const [key, value] of Object.entries(req.query ?? {})) {
139
+ if (value !== void 0) url.searchParams.set(key, String(value));
140
+ }
141
+ const headers = {
142
+ authorization: `Bearer ${this.#apiKey}`,
143
+ accept: "application/json"
144
+ };
145
+ if (req.body !== void 0) headers["content-type"] = "application/json";
146
+ const timeout = req.timeout ?? this.#timeout;
147
+ const controller = new AbortController();
148
+ const onAbort = () => controller.abort(req.signal?.reason);
149
+ req.signal?.addEventListener("abort", onAbort, { once: true });
150
+ let timedOut = false;
151
+ const timer = setTimeout(() => {
152
+ timedOut = true;
153
+ controller.abort();
154
+ }, timeout);
155
+ try {
156
+ return await this.#fetch(url, {
157
+ method: req.method,
158
+ headers,
159
+ body: req.body === void 0 ? void 0 : JSON.stringify(req.body),
160
+ signal: controller.signal
161
+ });
162
+ } catch (cause) {
163
+ if (timedOut) {
164
+ throw new TimeoutError(`Request timed out after ${timeout}ms.`, {
165
+ cause
166
+ });
167
+ }
168
+ if (req.signal?.aborted) throw cause;
169
+ throw new ConnectionError(`Could not reach ${url.origin}.`, { cause });
170
+ } finally {
171
+ clearTimeout(timer);
172
+ req.signal?.removeEventListener("abort", onAbort);
173
+ }
174
+ }
175
+ };
176
+ function shouldRetry(status) {
177
+ return status === 408 || status === 429 || status >= 500;
178
+ }
179
+ function backoff(attempt, lastError) {
180
+ const retryAfter = lastError && typeof lastError === "object" && "retryAfter" in lastError ? lastError.retryAfter : void 0;
181
+ if (retryAfter !== void 0) return Math.min(retryAfter * 1e3, MAX_BACKOFF);
182
+ const ceiling = Math.min(500 * 2 ** (attempt - 1), MAX_BACKOFF);
183
+ return Math.random() * ceiling;
184
+ }
185
+ function sleep(ms) {
186
+ return new Promise((resolve) => setTimeout(resolve, ms));
187
+ }
188
+ async function parseBody(response) {
189
+ if (response.status === 204) return void 0;
190
+ const text = await response.text();
191
+ if (text === "") return void 0;
192
+ try {
193
+ return JSON.parse(text);
194
+ } catch (cause) {
195
+ throw new MeterbaseError("The engine returned a body that is not JSON.", {
196
+ cause
197
+ });
198
+ }
199
+ }
200
+ async function errorFrom(response) {
201
+ const body = await response.text();
202
+ const parsed = parseErrorBody(body);
203
+ const retryAfterHeader = response.headers.get("retry-after");
204
+ const retryAfter = retryAfterHeader ? Number(retryAfterHeader) : void 0;
205
+ return errorFromResponse({
206
+ status: response.status,
207
+ code: parsed?.code ?? `http_${response.status}`,
208
+ message: parsed?.message ?? `The engine responded ${response.status}.`,
209
+ requestId: response.headers.get("x-request-id") ?? void 0,
210
+ retryAfter: retryAfter !== void 0 && Number.isFinite(retryAfter) ? retryAfter : void 0,
211
+ body: body === "" ? void 0 : safeParse(body) ?? body
212
+ });
213
+ }
214
+ function parseErrorBody(body) {
215
+ const parsed = safeParse(body);
216
+ const error = parsed?.error;
217
+ if (!error?.code || !error.message) return void 0;
218
+ return { code: error.code, message: error.message };
219
+ }
220
+ function safeParse(body) {
221
+ try {
222
+ return JSON.parse(body);
223
+ } catch {
224
+ return void 0;
225
+ }
226
+ }
227
+
228
+ // src/resources/customer-allowances.ts
229
+ var CustomerAllowances = class {
230
+ #client;
231
+ constructor(client) {
232
+ this.#client = client;
233
+ }
234
+ grant(customerId, params, options) {
235
+ return this.#client.request({
236
+ ...options,
237
+ method: "POST",
238
+ path: `/v1/customers/${encodeURIComponent(customerId)}/allowances`,
239
+ body: params
240
+ });
241
+ }
242
+ /** Open grants only, unless `include_closed`. */
243
+ list(customerId, params = {}, options) {
244
+ return this.#client.request({
245
+ ...options,
246
+ method: "GET",
247
+ path: `/v1/customers/${encodeURIComponent(customerId)}/allowances`,
248
+ query: {
249
+ meter_id: params.meter_id,
250
+ include_closed: params.include_closed
251
+ }
252
+ });
253
+ }
254
+ retrieve(customerId, allowanceId, options) {
255
+ return this.#client.request({
256
+ ...options,
257
+ method: "GET",
258
+ path: `/v1/customers/${encodeURIComponent(customerId)}/allowances/${encodeURIComponent(allowanceId)}`
259
+ });
260
+ }
261
+ /** Withdraws what is left without erasing what was consumed. Idempotent. */
262
+ revoke(customerId, allowanceId, options) {
263
+ return this.#client.request({
264
+ ...options,
265
+ method: "DELETE",
266
+ path: `/v1/customers/${encodeURIComponent(customerId)}/allowances/${encodeURIComponent(allowanceId)}`
267
+ });
268
+ }
269
+ };
270
+
271
+ // src/resources/customer-plan.ts
272
+ var CustomerPlan = class {
273
+ #client;
274
+ constructor(client) {
275
+ this.#client = client;
276
+ }
277
+ /** Returns the instruction as recorded, which is where to read when it lands. */
278
+ assign(customerId, params, options) {
279
+ return this.#client.request({
280
+ ...options,
281
+ method: "POST",
282
+ path: `/v1/customers/${encodeURIComponent(customerId)}/plan`,
283
+ body: params
284
+ });
285
+ }
286
+ /**
287
+ * The plan in force now, which is not always the newest instruction: a
288
+ * change dated ahead does not govern yet. Throws NotFoundError when the
289
+ * customer holds no plan.
290
+ */
291
+ retrieve(customerId, options) {
292
+ return this.#client.request({
293
+ ...options,
294
+ method: "GET",
295
+ path: `/v1/customers/${encodeURIComponent(customerId)}/plan`
296
+ });
297
+ }
298
+ /** Every instruction, superseded ones included: it is the whole trail. */
299
+ history(customerId, options) {
300
+ return this.#client.request({
301
+ ...options,
302
+ method: "GET",
303
+ path: `/v1/customers/${encodeURIComponent(customerId)}/plan/history`
304
+ });
305
+ }
306
+ };
307
+
308
+ // src/resources/customers.ts
309
+ var Customers = class {
310
+ /** The plan they hold, and the trail of instructions that got them there. */
311
+ plan;
312
+ /** Grants, which sit on top of whatever the plan gives. */
313
+ allowances;
314
+ #client;
315
+ constructor(client) {
316
+ this.#client = client;
317
+ this.plan = new CustomerPlan(client);
318
+ this.allowances = new CustomerAllowances(client);
319
+ }
320
+ create(params, options) {
321
+ return this.#client.request({
322
+ ...options,
323
+ method: "POST",
324
+ path: "/v1/customers",
325
+ body: params
326
+ });
327
+ }
328
+ list(params = {}, options) {
329
+ return this.#client.request({
330
+ ...options,
331
+ method: "GET",
332
+ path: "/v1/customers",
333
+ query: { include_deleted: params.include_deleted }
334
+ });
335
+ }
336
+ retrieve(id, options) {
337
+ return this.#client.request({
338
+ ...options,
339
+ method: "GET",
340
+ path: `/v1/customers/${encodeURIComponent(id)}`
341
+ });
342
+ }
343
+ /**
344
+ * Resolves the tenant's own identifier, or null. The engine answers this as
345
+ * a filtered list, so a miss is an empty collection rather than a 404.
346
+ */
347
+ async retrieveByExternalId(externalId, options) {
348
+ const { data } = await this.#client.request({
349
+ ...options,
350
+ method: "GET",
351
+ path: "/v1/customers",
352
+ query: { external_id: externalId }
353
+ });
354
+ return data[0] ?? null;
355
+ }
356
+ update(id, params, options) {
357
+ return this.#client.request({
358
+ ...options,
359
+ method: "PATCH",
360
+ path: `/v1/customers/${encodeURIComponent(id)}`,
361
+ body: params
362
+ });
363
+ }
364
+ /** Soft delete: usage and assignments keep referencing the customer. */
365
+ delete(id, options) {
366
+ return this.#client.request({
367
+ ...options,
368
+ method: "DELETE",
369
+ path: `/v1/customers/${encodeURIComponent(id)}`
370
+ });
371
+ }
372
+ };
373
+
374
+ // src/resources/meters.ts
375
+ var Meters = class {
376
+ #client;
377
+ constructor(client) {
378
+ this.#client = client;
379
+ }
380
+ create(params, options) {
381
+ return this.#client.request({
382
+ ...options,
383
+ method: "POST",
384
+ path: "/v1/meters",
385
+ body: params
386
+ });
387
+ }
388
+ list(params = {}, options) {
389
+ return this.#client.request({
390
+ ...options,
391
+ method: "GET",
392
+ path: "/v1/meters",
393
+ query: { include_archived: params.include_archived }
394
+ });
395
+ }
396
+ retrieve(id, options) {
397
+ return this.#client.request({
398
+ ...options,
399
+ method: "GET",
400
+ path: `/v1/meters/${encodeURIComponent(id)}`
401
+ });
402
+ }
403
+ update(id, params, options) {
404
+ return this.#client.request({
405
+ ...options,
406
+ method: "PATCH",
407
+ path: `/v1/meters/${encodeURIComponent(id)}`,
408
+ body: params
409
+ });
410
+ }
411
+ /** Soft delete: plans and usage keep referencing the meter. Idempotent. */
412
+ archive(id, options) {
413
+ return this.#client.request({
414
+ ...options,
415
+ method: "DELETE",
416
+ path: `/v1/meters/${encodeURIComponent(id)}`
417
+ });
418
+ }
419
+ };
420
+
421
+ // src/resources/plans.ts
422
+ var PlanAllowances = class {
423
+ #client;
424
+ constructor(client) {
425
+ this.#client = client;
426
+ }
427
+ set(planId, params, options) {
428
+ return this.#client.request({
429
+ ...options,
430
+ method: "POST",
431
+ path: `/v1/plans/${encodeURIComponent(planId)}/allowances`,
432
+ body: params
433
+ });
434
+ }
435
+ /** Every version of every meter, newest first — history, not just current. */
436
+ list(planId, params = {}, options) {
437
+ return this.#client.request({
438
+ ...options,
439
+ method: "GET",
440
+ path: `/v1/plans/${encodeURIComponent(planId)}/allowances`,
441
+ query: { meter_id: params.meter_id }
442
+ });
443
+ }
444
+ };
445
+ var Plans = class {
446
+ allowances;
447
+ #client;
448
+ constructor(client) {
449
+ this.#client = client;
450
+ this.allowances = new PlanAllowances(client);
451
+ }
452
+ /** A new plan grants nothing; attach entitlement with `allowances.set`. */
453
+ create(params, options) {
454
+ return this.#client.request({
455
+ ...options,
456
+ method: "POST",
457
+ path: "/v1/plans",
458
+ body: params
459
+ });
460
+ }
461
+ list(params = {}, options) {
462
+ return this.#client.request({
463
+ ...options,
464
+ method: "GET",
465
+ path: "/v1/plans",
466
+ query: { include_archived: params.include_archived }
467
+ });
468
+ }
469
+ retrieve(id, options) {
470
+ return this.#client.request({
471
+ ...options,
472
+ method: "GET",
473
+ path: `/v1/plans/${encodeURIComponent(id)}`
474
+ });
475
+ }
476
+ update(id, params, options) {
477
+ return this.#client.request({
478
+ ...options,
479
+ method: "PATCH",
480
+ path: `/v1/plans/${encodeURIComponent(id)}`,
481
+ body: params
482
+ });
483
+ }
484
+ /**
485
+ * Stops new assignments without withdrawing capacity from anyone already on
486
+ * the plan, which is why an archived plan still resolves by id. Idempotent.
487
+ */
488
+ archive(id, options) {
489
+ return this.#client.request({
490
+ ...options,
491
+ method: "DELETE",
492
+ path: `/v1/plans/${encodeURIComponent(id)}`
493
+ });
494
+ }
495
+ };
496
+
497
+ // src/index.ts
498
+ var Meterbase = class {
499
+ customers;
500
+ meters;
501
+ plans;
502
+ #client;
503
+ constructor(options) {
504
+ this.#client = new Client(options);
505
+ this.customers = new Customers(this.#client);
506
+ this.meters = new Meters(this.#client);
507
+ this.plans = new Plans(this.#client);
508
+ }
509
+ /**
510
+ * Asks whether a customer may consume `quantity` of a meter, named by the
511
+ * tenant's own external id and the meter's key. Absence of entitlement is
512
+ * not permission: a customer with no plan and no grant is denied.
513
+ *
514
+ * This is the gate, and the only call that ever refuses. Run it before the
515
+ * work; record the work with `track` afterwards.
516
+ */
517
+ check(params, options) {
518
+ return this.#client.request({
519
+ ...options,
520
+ method: "POST",
521
+ path: "/v1/check",
522
+ body: params
523
+ });
524
+ }
525
+ /**
526
+ * Records usage that already happened — the tokens were spent, the image
527
+ * was generated — so it never refuses on capacity. A quantity beyond the
528
+ * customer's capacity is recorded, drives `available` to 0, and the next
529
+ * `check` says no. The answer is a receipt, not a verdict.
530
+ *
531
+ * `idempotency_key` is generated when omitted, so retrying this call — the
532
+ * SDK's own retries included — replays the original rather than counting
533
+ * the same work twice.
534
+ */
535
+ // `async` so that generating the key cannot throw synchronously out of a
536
+ // method that otherwise only ever rejects: one call, one way to fail.
537
+ async track(params, options) {
538
+ return this.#client.request({
539
+ ...options,
540
+ method: "POST",
541
+ path: "/v1/usage/track",
542
+ // Fixed here, once, before the retry loop is entered: every attempt of
543
+ // this call carries the same key, which is what makes replaying it safe.
544
+ body: {
545
+ ...params,
546
+ idempotency_key: params.idempotency_key ?? idempotencyKey()
547
+ },
548
+ idempotent: true
549
+ });
550
+ }
551
+ /** Reports which workspace this key acts for. */
552
+ whoami(options) {
553
+ return this.#client.request({
554
+ ...options,
555
+ method: "GET",
556
+ path: "/v1/whoami"
557
+ });
558
+ }
559
+ };
560
+ function idempotencyKey() {
561
+ const webcrypto = globalThis.crypto;
562
+ if (typeof webcrypto?.randomUUID === "function") {
563
+ return webcrypto.randomUUID();
564
+ }
565
+ if (typeof webcrypto?.getRandomValues === "function") {
566
+ const bytes = webcrypto.getRandomValues(new Uint8Array(16));
567
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
568
+ }
569
+ throw new MeterbaseError(
570
+ "No crypto to generate an idempotency key with: pass `idempotency_key` yourself, or run somewhere `crypto.getRandomValues` exists."
571
+ );
572
+ }
573
+
574
+ exports.APIError = APIError;
575
+ exports.AuthenticationError = AuthenticationError;
576
+ exports.ConflictError = ConflictError;
577
+ exports.ConnectionError = ConnectionError;
578
+ exports.IdempotencyConflictError = IdempotencyConflictError;
579
+ exports.InvalidRequestError = InvalidRequestError;
580
+ exports.Meterbase = Meterbase;
581
+ exports.MeterbaseError = MeterbaseError;
582
+ exports.NotFoundError = NotFoundError;
583
+ exports.PermissionDeniedError = PermissionDeniedError;
584
+ exports.RateLimitError = RateLimitError;
585
+ exports.ServerError = ServerError;
586
+ exports.TimeoutError = TimeoutError;
587
+ exports.errorFromResponse = errorFromResponse;
588
+ //# sourceMappingURL=index.cjs.map
589
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/resources/customer-allowances.ts","../src/resources/customer-plan.ts","../src/resources/customers.ts","../src/resources/meters.ts","../src/resources/plans.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["// Every failure is a MeterbaseError, so a caller can catch one type and still\n// switch on the specific one. `code` is stable; `message` may change.\n\nexport class MeterbaseError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options)\n this.name = new.target.name\n }\n}\n\nexport class APIError extends MeterbaseError {\n readonly status: number\n readonly code: string\n /** From `X-Request-Id`, when the engine sends one. */\n readonly requestId: string | undefined\n readonly body: unknown\n\n constructor(args: {\n status: number\n code: string\n message: string\n requestId?: string | undefined\n body?: unknown\n }) {\n super(args.message)\n this.status = args.status\n this.code = args.code\n this.requestId = args.requestId\n this.body = args.body\n }\n}\n\nexport class AuthenticationError extends APIError {}\nexport class PermissionDeniedError extends APIError {}\nexport class NotFoundError extends APIError {}\nexport class ConflictError extends APIError {}\n\n/**\n * `409 idempotency_conflict`: the key is in use for a different meter or\n * quantity. A caller told their key is taken asks what it recorded next, so\n * the engine names the original and this carries it.\n *\n * A `ConflictError` still, so `catch (e) { if (e instanceof ConflictError) }`\n * keeps working.\n */\nexport class IdempotencyConflictError extends ConflictError {\n /** The event that key already recorded. Undefined only if the engine sent a\n * body this could not be read out of. */\n readonly existingEventId: string | undefined\n\n constructor(args: ConstructorParameters<typeof APIError>[0]) {\n super(args)\n this.existingEventId = existingEventId(args.body)\n }\n}\n\nexport class InvalidRequestError extends APIError {}\nexport class RateLimitError extends APIError {\n /** Seconds to wait, from `Retry-After`, when the engine sends one. */\n readonly retryAfter: number | undefined\n\n constructor(\n args: ConstructorParameters<typeof APIError>[0] & {\n retryAfter?: number | undefined\n },\n ) {\n super(args)\n this.retryAfter = args.retryAfter\n }\n}\nexport class ServerError extends APIError {}\n\n// The request never produced an answer: DNS, TLS, a reset, an abort.\nexport class ConnectionError extends MeterbaseError {}\nexport class TimeoutError extends ConnectionError {}\n\nexport function errorFromResponse(args: {\n status: number\n code: string\n message: string\n requestId?: string | undefined\n retryAfter?: number | undefined\n body?: unknown\n}): APIError {\n switch (args.status) {\n case 401:\n return new AuthenticationError(args)\n case 403:\n return new PermissionDeniedError(args)\n case 404:\n return new NotFoundError(args)\n case 409:\n return args.code === \"idempotency_conflict\"\n ? new IdempotencyConflictError(args)\n : new ConflictError(args)\n case 422:\n return new InvalidRequestError(args)\n case 429:\n return new RateLimitError(args)\n default:\n return args.status >= 500 ? new ServerError(args) : new APIError(args)\n }\n}\n\n/** The engine carries it beside `code` and `message`, inside `error`. */\nfunction existingEventId(body: unknown): string | undefined {\n if (typeof body !== \"object\" || body === null) return undefined\n\n const error = (body as { error?: unknown }).error\n if (typeof error !== \"object\" || error === null) return undefined\n\n const id = (error as { existing_event_id?: unknown }).existing_event_id\n return typeof id === \"string\" ? id : undefined\n}\n","// Everything goes through `request`, so authentication, timeouts, retries and\n// error mapping are defined once.\n\nimport {\n ConnectionError,\n errorFromResponse,\n MeterbaseError,\n TimeoutError,\n} from \"./errors.js\"\n\nexport type MeterbaseOptions = {\n /** A secret key. It names its own workspace. */\n apiKey: string\n /** Defaults to the hosted engine. */\n baseUrl?: string\n /** Per attempt, not per call. */\n timeout?: number\n maxRetries?: number\n fetch?: typeof globalThis.fetch\n}\n\nexport type RequestOptions = {\n signal?: AbortSignal\n timeout?: number\n maxRetries?: number\n}\n\ntype HttpMethod = \"GET\" | \"POST\" | \"PATCH\" | \"DELETE\"\n\ntype Request = RequestOptions & {\n method: HttpMethod\n path: string\n query?: Record<string, string | number | boolean | undefined>\n body?: unknown\n /**\n * Replay this call even though its method is not safe. Only a route that\n * carries an idempotency key may set it, and the key has to be fixed before\n * `request` is called — the loop re-sends one body, so a key generated\n * per attempt would count the same usage twice.\n */\n idempotent?: boolean\n}\n\nconst DEFAULT_BASE_URL = \"https://api.meterbase.tech\"\nconst DEFAULT_TIMEOUT = 10_000\nconst DEFAULT_MAX_RETRIES = 2\nconst MAX_BACKOFF = 8_000\n\nexport class Client {\n readonly #apiKey: string\n readonly #baseUrl: string\n readonly #timeout: number\n readonly #maxRetries: number\n readonly #fetch: typeof globalThis.fetch\n\n constructor(options: MeterbaseOptions) {\n if (!options.apiKey) {\n throw new MeterbaseError(\n \"An API key is required: new Meterbase({ apiKey: 'mb_sk_live_…' }).\",\n )\n }\n\n this.#apiKey = options.apiKey\n this.#baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\")\n this.#timeout = options.timeout ?? DEFAULT_TIMEOUT\n this.#maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES\n this.#fetch = options.fetch ?? globalThis.fetch\n\n if (typeof this.#fetch !== \"function\") {\n throw new MeterbaseError(\n \"No fetch implementation: pass one as `fetch`, or run on Node 18+.\",\n )\n }\n }\n\n async request<T>(req: Request): Promise<T> {\n const maxRetries = req.maxRetries ?? this.#maxRetries\n // Safe methods, plus the routes that carry an idempotency key and say so.\n // Any other POST or PATCH could create a second row on a replay.\n const retryable =\n req.method === \"GET\" || req.method === \"DELETE\" || req.idempotent === true\n\n let lastError: unknown\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n if (attempt > 0) await sleep(backoff(attempt, lastError))\n\n try {\n const response = await this.#send(req)\n\n if (response.ok) return (await parseBody(response)) as T\n\n const error = await errorFrom(response)\n if (retryable && attempt < maxRetries && shouldRetry(response.status)) {\n lastError = error\n continue\n }\n throw error\n } catch (cause) {\n if (\n cause instanceof MeterbaseError &&\n !(cause instanceof ConnectionError)\n ) {\n throw cause\n }\n // A connection failure proves nothing about whether the server acted,\n // so it is replayed under the same idempotency rule.\n if (!retryable || attempt >= maxRetries) throw cause\n lastError = cause\n }\n }\n\n /* c8 ignore next 2 -- the loop always returns or throws */\n throw lastError\n }\n\n async #send(req: Request): Promise<Response> {\n const url = new URL(this.#baseUrl + req.path)\n for (const [key, value] of Object.entries(req.query ?? {})) {\n if (value !== undefined) url.searchParams.set(key, String(value))\n }\n\n const headers: Record<string, string> = {\n authorization: `Bearer ${this.#apiKey}`,\n accept: \"application/json\",\n }\n if (req.body !== undefined) headers[\"content-type\"] = \"application/json\"\n\n const timeout = req.timeout ?? this.#timeout\n const controller = new AbortController()\n const onAbort = () => controller.abort(req.signal?.reason)\n req.signal?.addEventListener(\"abort\", onAbort, { once: true })\n\n let timedOut = false\n const timer = setTimeout(() => {\n timedOut = true\n controller.abort()\n }, timeout)\n\n try {\n return await this.#fetch(url, {\n method: req.method,\n headers,\n body: req.body === undefined ? undefined : JSON.stringify(req.body),\n signal: controller.signal,\n })\n } catch (cause) {\n if (timedOut) {\n throw new TimeoutError(`Request timed out after ${timeout}ms.`, {\n cause,\n })\n }\n // A caller's own abort is theirs to handle, not ours to reclassify.\n if (req.signal?.aborted) throw cause\n throw new ConnectionError(`Could not reach ${url.origin}.`, { cause })\n } finally {\n clearTimeout(timer)\n req.signal?.removeEventListener(\"abort\", onAbort)\n }\n }\n}\n\nfunction shouldRetry(status: number): boolean {\n return status === 408 || status === 429 || status >= 500\n}\n\n/** Exponential backoff with full jitter, unless the engine named a delay. */\nfunction backoff(attempt: number, lastError: unknown): number {\n const retryAfter =\n lastError && typeof lastError === \"object\" && \"retryAfter\" in lastError\n ? (lastError as { retryAfter?: number }).retryAfter\n : undefined\n if (retryAfter !== undefined) return Math.min(retryAfter * 1000, MAX_BACKOFF)\n\n const ceiling = Math.min(500 * 2 ** (attempt - 1), MAX_BACKOFF)\n return Math.random() * ceiling\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nasync function parseBody(response: Response): Promise<unknown> {\n if (response.status === 204) return undefined\n\n const text = await response.text()\n if (text === \"\") return undefined\n\n try {\n return JSON.parse(text)\n } catch (cause) {\n throw new MeterbaseError(\"The engine returned a body that is not JSON.\", {\n cause,\n })\n }\n}\n\nasync function errorFrom(response: Response) {\n const body = await response.text()\n const parsed = parseErrorBody(body)\n const retryAfterHeader = response.headers.get(\"retry-after\")\n const retryAfter = retryAfterHeader ? Number(retryAfterHeader) : undefined\n\n return errorFromResponse({\n status: response.status,\n code: parsed?.code ?? `http_${response.status}`,\n message: parsed?.message ?? `The engine responded ${response.status}.`,\n requestId: response.headers.get(\"x-request-id\") ?? undefined,\n retryAfter:\n retryAfter !== undefined && Number.isFinite(retryAfter)\n ? retryAfter\n : undefined,\n body: body === \"\" ? undefined : (safeParse(body) ?? body),\n })\n}\n\nfunction parseErrorBody(body: string) {\n const parsed = safeParse(body) as\n { error?: { code?: string; message?: string } } | undefined\n const error = parsed?.error\n if (!error?.code || !error.message) return undefined\n\n return { code: error.code, message: error.message }\n}\n\nfunction safeParse(body: string): unknown {\n try {\n return JSON.parse(body)\n } catch {\n return undefined\n }\n}\n","import type { Client, RequestOptions } from \"../client.js\"\nimport type {\n Allowance,\n AllowanceGrantParams,\n AllowanceListParams,\n List,\n} from \"../types.js\"\n\n/**\n * Capacity handed to one customer on top of their plan. A grant with\n * `source: \"plan\"` is the engine's own, minted when a plan is assigned, and\n * cannot be created here.\n */\nexport class CustomerAllowances {\n readonly #client: Client\n\n constructor(client: Client) {\n this.#client = client\n }\n\n grant(\n customerId: string,\n params: AllowanceGrantParams,\n options?: RequestOptions,\n ): Promise<Allowance> {\n return this.#client.request({\n ...options,\n method: \"POST\",\n path: `/v1/customers/${encodeURIComponent(customerId)}/allowances`,\n body: params,\n })\n }\n\n /** Open grants only, unless `include_closed`. */\n list(\n customerId: string,\n params: AllowanceListParams = {},\n options?: RequestOptions,\n ): Promise<List<Allowance>> {\n return this.#client.request({\n ...options,\n method: \"GET\",\n path: `/v1/customers/${encodeURIComponent(customerId)}/allowances`,\n query: {\n meter_id: params.meter_id,\n include_closed: params.include_closed,\n },\n })\n }\n\n retrieve(\n customerId: string,\n allowanceId: string,\n options?: RequestOptions,\n ): Promise<Allowance> {\n return this.#client.request({\n ...options,\n method: \"GET\",\n path: `/v1/customers/${encodeURIComponent(customerId)}/allowances/${encodeURIComponent(allowanceId)}`,\n })\n }\n\n /** Withdraws what is left without erasing what was consumed. Idempotent. */\n revoke(\n customerId: string,\n allowanceId: string,\n options?: RequestOptions,\n ): Promise<Allowance> {\n return this.#client.request({\n ...options,\n method: \"DELETE\",\n path: `/v1/customers/${encodeURIComponent(customerId)}/allowances/${encodeURIComponent(allowanceId)}`,\n })\n }\n}\n","import type { Client, RequestOptions } from \"../client.js\"\nimport type { AssignPlanParams, Assignment, List } from \"../types.js\"\n\n/**\n * Which plan a customer holds. The history is append-only: there is no patch\n * and no delete, and assigning a plan to a customer who already has one is\n * the same call as their first.\n */\nexport class CustomerPlan {\n readonly #client: Client\n\n constructor(client: Client) {\n this.#client = client\n }\n\n /** Returns the instruction as recorded, which is where to read when it lands. */\n assign(\n customerId: string,\n params: AssignPlanParams,\n options?: RequestOptions,\n ): Promise<Assignment> {\n return this.#client.request({\n ...options,\n method: \"POST\",\n path: `/v1/customers/${encodeURIComponent(customerId)}/plan`,\n body: params,\n })\n }\n\n /**\n * The plan in force now, which is not always the newest instruction: a\n * change dated ahead does not govern yet. Throws NotFoundError when the\n * customer holds no plan.\n */\n retrieve(customerId: string, options?: RequestOptions): Promise<Assignment> {\n return this.#client.request({\n ...options,\n method: \"GET\",\n path: `/v1/customers/${encodeURIComponent(customerId)}/plan`,\n })\n }\n\n /** Every instruction, superseded ones included: it is the whole trail. */\n history(\n customerId: string,\n options?: RequestOptions,\n ): Promise<List<Assignment>> {\n return this.#client.request({\n ...options,\n method: \"GET\",\n path: `/v1/customers/${encodeURIComponent(customerId)}/plan/history`,\n })\n }\n}\n","import type { Client, RequestOptions } from \"../client.js\"\nimport type {\n Customer,\n CustomerCreateParams,\n CustomerListParams,\n CustomerUpdateParams,\n List,\n} from \"../types.js\"\nimport { CustomerAllowances } from \"./customer-allowances.js\"\nimport { CustomerPlan } from \"./customer-plan.js\"\n\nexport class Customers {\n /** The plan they hold, and the trail of instructions that got them there. */\n readonly plan: CustomerPlan\n /** Grants, which sit on top of whatever the plan gives. */\n readonly allowances: CustomerAllowances\n\n readonly #client: Client\n\n constructor(client: Client) {\n this.#client = client\n this.plan = new CustomerPlan(client)\n this.allowances = new CustomerAllowances(client)\n }\n\n create(\n params: CustomerCreateParams,\n options?: RequestOptions,\n ): Promise<Customer> {\n return this.#client.request({\n ...options,\n method: \"POST\",\n path: \"/v1/customers\",\n body: params,\n })\n }\n\n list(\n params: CustomerListParams = {},\n options?: RequestOptions,\n ): Promise<List<Customer>> {\n return this.#client.request({\n ...options,\n method: \"GET\",\n path: \"/v1/customers\",\n query: { include_deleted: params.include_deleted },\n })\n }\n\n retrieve(id: string, options?: RequestOptions): Promise<Customer> {\n return this.#client.request({\n ...options,\n method: \"GET\",\n path: `/v1/customers/${encodeURIComponent(id)}`,\n })\n }\n\n /**\n * Resolves the tenant's own identifier, or null. The engine answers this as\n * a filtered list, so a miss is an empty collection rather than a 404.\n */\n async retrieveByExternalId(\n externalId: string,\n options?: RequestOptions,\n ): Promise<Customer | null> {\n const { data } = await this.#client.request<List<Customer>>({\n ...options,\n method: \"GET\",\n path: \"/v1/customers\",\n query: { external_id: externalId },\n })\n\n return data[0] ?? null\n }\n\n update(\n id: string,\n params: CustomerUpdateParams,\n options?: RequestOptions,\n ): Promise<Customer> {\n return this.#client.request({\n ...options,\n method: \"PATCH\",\n path: `/v1/customers/${encodeURIComponent(id)}`,\n body: params,\n })\n }\n\n /** Soft delete: usage and assignments keep referencing the customer. */\n delete(id: string, options?: RequestOptions): Promise<Customer> {\n return this.#client.request({\n ...options,\n method: \"DELETE\",\n path: `/v1/customers/${encodeURIComponent(id)}`,\n })\n }\n}\n","import type { Client, RequestOptions } from \"../client.js\"\nimport type {\n List,\n Meter,\n MeterCreateParams,\n MeterListParams,\n MeterUpdateParams,\n} from \"../types.js\"\n\nexport class Meters {\n readonly #client: Client\n\n constructor(client: Client) {\n this.#client = client\n }\n\n create(params: MeterCreateParams, options?: RequestOptions): Promise<Meter> {\n return this.#client.request({\n ...options,\n method: \"POST\",\n path: \"/v1/meters\",\n body: params,\n })\n }\n\n list(\n params: MeterListParams = {},\n options?: RequestOptions,\n ): Promise<List<Meter>> {\n return this.#client.request({\n ...options,\n method: \"GET\",\n path: \"/v1/meters\",\n query: { include_archived: params.include_archived },\n })\n }\n\n retrieve(id: string, options?: RequestOptions): Promise<Meter> {\n return this.#client.request({\n ...options,\n method: \"GET\",\n path: `/v1/meters/${encodeURIComponent(id)}`,\n })\n }\n\n update(\n id: string,\n params: MeterUpdateParams,\n options?: RequestOptions,\n ): Promise<Meter> {\n return this.#client.request({\n ...options,\n method: \"PATCH\",\n path: `/v1/meters/${encodeURIComponent(id)}`,\n body: params,\n })\n }\n\n /** Soft delete: plans and usage keep referencing the meter. Idempotent. */\n archive(id: string, options?: RequestOptions): Promise<Meter> {\n return this.#client.request({\n ...options,\n method: \"DELETE\",\n path: `/v1/meters/${encodeURIComponent(id)}`,\n })\n }\n}\n","import type { Client, RequestOptions } from \"../client.js\"\nimport type {\n List,\n Plan,\n PlanAllowance,\n PlanAllowanceListParams,\n PlanAllowanceSetParams,\n PlanCreateParams,\n PlanListParams,\n PlanUpdateParams,\n} from \"../types.js\"\n\n/**\n * A plan's entitlement, versioned. There is no update and no delete: an edit\n * appends the next version, so which amount applied when stays derivable.\n */\nexport class PlanAllowances {\n readonly #client: Client\n\n constructor(client: Client) {\n this.#client = client\n }\n\n set(\n planId: string,\n params: PlanAllowanceSetParams,\n options?: RequestOptions,\n ): Promise<PlanAllowance> {\n return this.#client.request({\n ...options,\n method: \"POST\",\n path: `/v1/plans/${encodeURIComponent(planId)}/allowances`,\n body: params,\n })\n }\n\n /** Every version of every meter, newest first — history, not just current. */\n list(\n planId: string,\n params: PlanAllowanceListParams = {},\n options?: RequestOptions,\n ): Promise<List<PlanAllowance>> {\n return this.#client.request({\n ...options,\n method: \"GET\",\n path: `/v1/plans/${encodeURIComponent(planId)}/allowances`,\n query: { meter_id: params.meter_id },\n })\n }\n}\n\nexport class Plans {\n readonly allowances: PlanAllowances\n\n readonly #client: Client\n\n constructor(client: Client) {\n this.#client = client\n this.allowances = new PlanAllowances(client)\n }\n\n /** A new plan grants nothing; attach entitlement with `allowances.set`. */\n create(params: PlanCreateParams, options?: RequestOptions): Promise<Plan> {\n return this.#client.request({\n ...options,\n method: \"POST\",\n path: \"/v1/plans\",\n body: params,\n })\n }\n\n list(\n params: PlanListParams = {},\n options?: RequestOptions,\n ): Promise<List<Plan>> {\n return this.#client.request({\n ...options,\n method: \"GET\",\n path: \"/v1/plans\",\n query: { include_archived: params.include_archived },\n })\n }\n\n retrieve(id: string, options?: RequestOptions): Promise<Plan> {\n return this.#client.request({\n ...options,\n method: \"GET\",\n path: `/v1/plans/${encodeURIComponent(id)}`,\n })\n }\n\n update(\n id: string,\n params: PlanUpdateParams,\n options?: RequestOptions,\n ): Promise<Plan> {\n return this.#client.request({\n ...options,\n method: \"PATCH\",\n path: `/v1/plans/${encodeURIComponent(id)}`,\n body: params,\n })\n }\n\n /**\n * Stops new assignments without withdrawing capacity from anyone already on\n * the plan, which is why an archived plan still resolves by id. Idempotent.\n */\n archive(id: string, options?: RequestOptions): Promise<Plan> {\n return this.#client.request({\n ...options,\n method: \"DELETE\",\n path: `/v1/plans/${encodeURIComponent(id)}`,\n })\n }\n}\n","import { Client, type MeterbaseOptions, type RequestOptions } from \"./client.js\"\nimport { MeterbaseError } from \"./errors.js\"\nimport type { CustomerAllowances } from \"./resources/customer-allowances.js\"\nimport type { CustomerPlan } from \"./resources/customer-plan.js\"\nimport { Customers } from \"./resources/customers.js\"\nimport { Meters } from \"./resources/meters.js\"\nimport { Plans, type PlanAllowances } from \"./resources/plans.js\"\nimport type {\n CheckParams,\n CheckResult,\n TrackParams,\n TrackResult,\n WhoAmI,\n} from \"./types.js\"\n\nexport class Meterbase {\n readonly customers: Customers\n readonly meters: Meters\n readonly plans: Plans\n\n readonly #client: Client\n\n constructor(options: MeterbaseOptions) {\n this.#client = new Client(options)\n this.customers = new Customers(this.#client)\n this.meters = new Meters(this.#client)\n this.plans = new Plans(this.#client)\n }\n\n /**\n * Asks whether a customer may consume `quantity` of a meter, named by the\n * tenant's own external id and the meter's key. Absence of entitlement is\n * not permission: a customer with no plan and no grant is denied.\n *\n * This is the gate, and the only call that ever refuses. Run it before the\n * work; record the work with `track` afterwards.\n */\n check(params: CheckParams, options?: RequestOptions): Promise<CheckResult> {\n return this.#client.request({\n ...options,\n method: \"POST\",\n path: \"/v1/check\",\n body: params,\n })\n }\n\n /**\n * Records usage that already happened — the tokens were spent, the image\n * was generated — so it never refuses on capacity. A quantity beyond the\n * customer's capacity is recorded, drives `available` to 0, and the next\n * `check` says no. The answer is a receipt, not a verdict.\n *\n * `idempotency_key` is generated when omitted, so retrying this call — the\n * SDK's own retries included — replays the original rather than counting\n * the same work twice.\n */\n // `async` so that generating the key cannot throw synchronously out of a\n // method that otherwise only ever rejects: one call, one way to fail.\n async track(\n params: TrackParams,\n options?: RequestOptions,\n ): Promise<TrackResult> {\n return this.#client.request({\n ...options,\n method: \"POST\",\n path: \"/v1/usage/track\",\n // Fixed here, once, before the retry loop is entered: every attempt of\n // this call carries the same key, which is what makes replaying it safe.\n body: {\n ...params,\n idempotency_key: params.idempotency_key ?? idempotencyKey(),\n },\n idempotent: true,\n })\n }\n\n /** Reports which workspace this key acts for. */\n whoami(options?: RequestOptions): Promise<WhoAmI> {\n return this.#client.request({\n ...options,\n method: \"GET\",\n path: \"/v1/whoami\",\n })\n }\n}\n\n// Type-only: none of them is constructible without the unexported Client.\nexport type {\n CustomerAllowances,\n CustomerPlan,\n Customers,\n MeterbaseOptions,\n Meters,\n PlanAllowances,\n Plans,\n RequestOptions,\n}\nexport * from \"./errors.js\"\nexport type * from \"./types.js\"\n\n/**\n * The engine requires a key, and a caller made to invent one on every call\n * eventually will not — which is the moment a retry counts twice.\n */\nfunction idempotencyKey(): string {\n // Structurally, not as `Crypto`: that lib type is the DOM's, and this\n // compiles without it so the SDK stays as portable as `fetch` is.\n const webcrypto = globalThis.crypto as\n | {\n randomUUID?: () => string\n getRandomValues?: (array: Uint8Array) => Uint8Array\n }\n | undefined\n\n if (typeof webcrypto?.randomUUID === \"function\") {\n return webcrypto.randomUUID()\n }\n // `randomUUID` needs a secure context; `getRandomValues` does not, so a\n // page served over plain http still gets 128 random bits.\n if (typeof webcrypto?.getRandomValues === \"function\") {\n const bytes = webcrypto.getRandomValues(new Uint8Array(16))\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\")\n }\n\n throw new MeterbaseError(\n \"No crypto to generate an idempotency key with: pass `idempotency_key` \" +\n \"yourself, or run somewhere `crypto.getRandomValues` exists.\",\n )\n}\n"]}