meterflow 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.js ADDED
@@ -0,0 +1,358 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AuthError: () => AuthError,
24
+ ConflictError: () => ConflictError,
25
+ InsufficientCreditsError: () => InsufficientCreditsError,
26
+ MeterFlow: () => MeterFlow,
27
+ MeterFlowError: () => MeterFlowError,
28
+ NotFoundError: () => NotFoundError,
29
+ RateLimitError: () => RateLimitError,
30
+ ServerError: () => ServerError,
31
+ ValidationError: () => ValidationError,
32
+ verifyWebhook: () => verifyWebhook
33
+ });
34
+ module.exports = __toCommonJS(index_exports);
35
+
36
+ // src/errors.ts
37
+ var MeterFlowError = class extends Error {
38
+ errorType;
39
+ requestId;
40
+ statusCode;
41
+ retryable;
42
+ constructor(message, errorType, retryable2, requestId, statusCode) {
43
+ super(message);
44
+ this.name = "MeterFlowError";
45
+ this.errorType = errorType;
46
+ this.retryable = retryable2;
47
+ this.requestId = requestId;
48
+ this.statusCode = statusCode;
49
+ }
50
+ };
51
+ var AuthError = class extends MeterFlowError {
52
+ constructor(message, requestId, statusCode = 401) {
53
+ super(message, "auth_error", false, requestId, statusCode);
54
+ this.name = "AuthError";
55
+ }
56
+ };
57
+ var NotFoundError = class extends MeterFlowError {
58
+ constructor(message, requestId) {
59
+ super(message, "not_found", false, requestId, 404);
60
+ this.name = "NotFoundError";
61
+ }
62
+ };
63
+ var InsufficientCreditsError = class extends MeterFlowError {
64
+ constructor(message, requestId) {
65
+ super(message, "insufficient_credits", false, requestId, 402);
66
+ this.name = "InsufficientCreditsError";
67
+ }
68
+ };
69
+ var ConflictError = class extends MeterFlowError {
70
+ constructor(message, requestId) {
71
+ super(message, "conflict", false, requestId, 409);
72
+ this.name = "ConflictError";
73
+ }
74
+ };
75
+ var ValidationError = class extends MeterFlowError {
76
+ constructor(message, requestId) {
77
+ super(message, "validation_error", false, requestId, 422);
78
+ this.name = "ValidationError";
79
+ }
80
+ };
81
+ var RateLimitError = class extends MeterFlowError {
82
+ retryAfter;
83
+ constructor(message, retryAfter, requestId) {
84
+ super(message, "rate_limit", true, requestId, 429);
85
+ this.name = "RateLimitError";
86
+ this.retryAfter = retryAfter;
87
+ }
88
+ };
89
+ var ServerError = class extends MeterFlowError {
90
+ constructor(message, requestId, statusCode = 500) {
91
+ super(message, "server_error", true, requestId, statusCode);
92
+ this.name = "ServerError";
93
+ }
94
+ };
95
+
96
+ // src/utils/retry.ts
97
+ function sleep(ms) {
98
+ return new Promise((resolve) => setTimeout(resolve, ms));
99
+ }
100
+ async function retryable(fn, opts, shouldRetry = () => true) {
101
+ let lastError;
102
+ for (let attempt = 0; attempt <= opts.retries; attempt++) {
103
+ try {
104
+ return await fn();
105
+ } catch (err) {
106
+ lastError = err;
107
+ const isLast = attempt === opts.retries;
108
+ if (isLast || !shouldRetry(err)) {
109
+ throw err;
110
+ }
111
+ let delayMs;
112
+ if (err instanceof RateLimitError && err.retryAfter != null) {
113
+ delayMs = err.retryAfter * 1e3;
114
+ } else {
115
+ const baseMs = Math.min(1e3 * Math.pow(opts.factor, attempt), opts.maxDelayMs);
116
+ delayMs = opts.jitter ? baseMs * (0.75 + Math.random() * 0.5) : baseMs;
117
+ }
118
+ await sleep(delayMs);
119
+ }
120
+ }
121
+ throw lastError;
122
+ }
123
+
124
+ // src/utils/http.ts
125
+ var SDK_VERSION = "0.1.0";
126
+ function buildUrl(baseUrl, path, query) {
127
+ const url = new URL(path, baseUrl.endsWith("/") ? baseUrl : baseUrl + "/");
128
+ if (query) {
129
+ for (const [key, value] of Object.entries(query)) {
130
+ url.searchParams.set(key, String(value));
131
+ }
132
+ }
133
+ return url.toString();
134
+ }
135
+ function buildHeaders(options, opts) {
136
+ const headers = {
137
+ Authorization: `Bearer ${options.apiKey}`,
138
+ "User-Agent": `meterflow-node/${SDK_VERSION}`,
139
+ Accept: "application/json"
140
+ };
141
+ if (opts?.body !== void 0) {
142
+ headers["Content-Type"] = "application/json";
143
+ }
144
+ if (opts?.idempotencyKey) {
145
+ headers["Idempotency-Key"] = opts.idempotencyKey;
146
+ }
147
+ return headers;
148
+ }
149
+ async function parseErrorBody(response) {
150
+ try {
151
+ const json = await response.json();
152
+ if (typeof json["detail"] === "string") return json["detail"];
153
+ return JSON.stringify(json);
154
+ } catch {
155
+ return response.statusText || `HTTP ${response.status}`;
156
+ }
157
+ }
158
+ function mapResponseError(status, message, requestId, response) {
159
+ if (status === 401 || status === 403) return new AuthError(message, requestId, status);
160
+ if (status === 402) return new InsufficientCreditsError(message, requestId);
161
+ if (status === 404) return new NotFoundError(message, requestId);
162
+ if (status === 409) return new ConflictError(message, requestId);
163
+ if (status === 422) return new ValidationError(message, requestId);
164
+ if (status === 429) {
165
+ const retryAfterRaw = response.headers.get("Retry-After");
166
+ const retryAfter = retryAfterRaw != null ? parseInt(retryAfterRaw, 10) : void 0;
167
+ return new RateLimitError(message, Number.isFinite(retryAfter) ? retryAfter : void 0, requestId);
168
+ }
169
+ if (status >= 500) return new ServerError(message, requestId, status);
170
+ return new MeterFlowError(message, "request_error", false, requestId, status);
171
+ }
172
+ async function httpRequest(options, method, path, opts) {
173
+ const url = buildUrl(options.baseUrl, path, opts?.query);
174
+ const headers = buildHeaders(options, opts);
175
+ const body = opts?.body !== void 0 ? JSON.stringify(opts.body) : void 0;
176
+ const timeoutMs = opts?.timeout ?? options.timeout;
177
+ return retryable(
178
+ async () => {
179
+ const signal = AbortSignal.timeout(timeoutMs);
180
+ const fetchInit = { method, headers, signal };
181
+ if (body !== void 0) fetchInit.body = body;
182
+ const response = await options.fetch(url, fetchInit);
183
+ const requestId = response.headers.get("x-request-id") ?? void 0;
184
+ if (!response.ok) {
185
+ const message = await parseErrorBody(response);
186
+ throw mapResponseError(response.status, message, requestId, response);
187
+ }
188
+ if (response.status === 204 || response.headers.get("content-length") === "0") {
189
+ return void 0;
190
+ }
191
+ return await response.json();
192
+ },
193
+ { retries: options.retries, factor: 2, jitter: true, maxDelayMs: 1e4 },
194
+ (err) => {
195
+ if (err instanceof MeterFlowError) return err.retryable;
196
+ return true;
197
+ }
198
+ );
199
+ }
200
+
201
+ // src/resources/credits.ts
202
+ var CreditsResource = class {
203
+ constructor(client) {
204
+ this.client = client;
205
+ }
206
+ grant(body, opts) {
207
+ return this.client.request("POST", "credits/grant", {
208
+ body,
209
+ ...opts?.idempotencyKey !== void 0 && { idempotencyKey: opts.idempotencyKey }
210
+ });
211
+ }
212
+ deduct(body, opts) {
213
+ return this.client.request("POST", "credits/deduct", {
214
+ body,
215
+ ...opts?.idempotencyKey !== void 0 && { idempotencyKey: opts.idempotencyKey }
216
+ });
217
+ }
218
+ balance(customerId) {
219
+ return this.client.request("GET", `credits/${encodeURIComponent(customerId)}/balance`);
220
+ }
221
+ transactions(customerId, query) {
222
+ return this.client.request("GET", `credits/${encodeURIComponent(customerId)}/transactions`, {
223
+ ...query !== void 0 && { query }
224
+ });
225
+ }
226
+ };
227
+
228
+ // src/resources/usage.ts
229
+ var UsageResource = class {
230
+ constructor(client) {
231
+ this.client = client;
232
+ }
233
+ record(body, opts) {
234
+ return this.client.request("POST", "usage/events", {
235
+ body,
236
+ ...opts?.idempotencyKey !== void 0 && { idempotencyKey: opts.idempotencyKey }
237
+ });
238
+ }
239
+ recordBatch(events, opts) {
240
+ return this.client.request("POST", "usage/events/batch", {
241
+ body: { events },
242
+ ...opts?.idempotencyKey !== void 0 && { idempotencyKey: opts.idempotencyKey }
243
+ });
244
+ }
245
+ summary(customerId, query) {
246
+ return this.client.request("GET", `usage/${encodeURIComponent(customerId)}`, {
247
+ ...query !== void 0 && { query }
248
+ });
249
+ }
250
+ };
251
+
252
+ // src/resources/subscriptions.ts
253
+ var SubscriptionsResource = class {
254
+ constructor(client) {
255
+ this.client = client;
256
+ }
257
+ create(body, opts) {
258
+ return this.client.request("POST", "subscriptions", {
259
+ body,
260
+ ...opts?.idempotencyKey !== void 0 && { idempotencyKey: opts.idempotencyKey }
261
+ });
262
+ }
263
+ list(query) {
264
+ return this.client.request("GET", "subscriptions", {
265
+ ...query !== void 0 && { query }
266
+ });
267
+ }
268
+ get(subscriptionId) {
269
+ return this.client.request("GET", `subscriptions/${encodeURIComponent(subscriptionId)}`);
270
+ }
271
+ update(subscriptionId, body, opts) {
272
+ return this.client.request("PATCH", `subscriptions/${encodeURIComponent(subscriptionId)}`, {
273
+ body,
274
+ ...opts?.idempotencyKey !== void 0 && { idempotencyKey: opts.idempotencyKey }
275
+ });
276
+ }
277
+ delete(subscriptionId) {
278
+ return this.client.request("DELETE", `subscriptions/${encodeURIComponent(subscriptionId)}`);
279
+ }
280
+ };
281
+
282
+ // src/resources/plans.ts
283
+ var PlansResource = class {
284
+ constructor(client) {
285
+ this.client = client;
286
+ }
287
+ list(query) {
288
+ return this.client.request("GET", "plans", {
289
+ ...query !== void 0 && { query }
290
+ });
291
+ }
292
+ get(planId, query) {
293
+ return this.client.request("GET", `plans/${encodeURIComponent(planId)}`, {
294
+ ...query !== void 0 && { query }
295
+ });
296
+ }
297
+ };
298
+
299
+ // src/client.ts
300
+ var MeterFlow = class {
301
+ options;
302
+ _credits;
303
+ _usage;
304
+ _subscriptions;
305
+ _plans;
306
+ constructor(options) {
307
+ if (!options.apiKey.startsWith("mf_live_") && !options.apiKey.startsWith("mf_test_")) {
308
+ throw new Error("apiKey must start with mf_live_ or mf_test_");
309
+ }
310
+ this.options = {
311
+ apiKey: options.apiKey,
312
+ baseUrl: options.baseUrl ?? "https://api.meterflow.com/api/v1",
313
+ timeout: options.timeout ?? 3e4,
314
+ retries: options.retries ?? 3,
315
+ fetch: options.fetch ?? globalThis.fetch
316
+ };
317
+ }
318
+ get credits() {
319
+ return this._credits ??= new CreditsResource(this);
320
+ }
321
+ get usage() {
322
+ return this._usage ??= new UsageResource(this);
323
+ }
324
+ get subscriptions() {
325
+ return this._subscriptions ??= new SubscriptionsResource(this);
326
+ }
327
+ get plans() {
328
+ return this._plans ??= new PlansResource(this);
329
+ }
330
+ /** @internal — used by resource classes only; not part of the public API */
331
+ request(method, path, opts) {
332
+ return httpRequest(this.options, method, path, opts);
333
+ }
334
+ };
335
+
336
+ // src/utils/webhook-verify.ts
337
+ var import_crypto = require("crypto");
338
+ function verifyWebhook(rawBody, signature, secret) {
339
+ const body = typeof rawBody === "string" ? Buffer.from(rawBody, "utf8") : rawBody;
340
+ const expected = (0, import_crypto.createHmac)("sha256", secret).update(body).digest("hex");
341
+ const expectedBuf = Buffer.from(expected, "utf8");
342
+ const actualBuf = Buffer.from(signature, "utf8");
343
+ if (expectedBuf.length !== actualBuf.length) return false;
344
+ return (0, import_crypto.timingSafeEqual)(expectedBuf, actualBuf);
345
+ }
346
+ // Annotate the CommonJS export names for ESM import in node:
347
+ 0 && (module.exports = {
348
+ AuthError,
349
+ ConflictError,
350
+ InsufficientCreditsError,
351
+ MeterFlow,
352
+ MeterFlowError,
353
+ NotFoundError,
354
+ RateLimitError,
355
+ ServerError,
356
+ ValidationError,
357
+ verifyWebhook
358
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,322 @@
1
+ // src/errors.ts
2
+ var MeterFlowError = class extends Error {
3
+ errorType;
4
+ requestId;
5
+ statusCode;
6
+ retryable;
7
+ constructor(message, errorType, retryable2, requestId, statusCode) {
8
+ super(message);
9
+ this.name = "MeterFlowError";
10
+ this.errorType = errorType;
11
+ this.retryable = retryable2;
12
+ this.requestId = requestId;
13
+ this.statusCode = statusCode;
14
+ }
15
+ };
16
+ var AuthError = class extends MeterFlowError {
17
+ constructor(message, requestId, statusCode = 401) {
18
+ super(message, "auth_error", false, requestId, statusCode);
19
+ this.name = "AuthError";
20
+ }
21
+ };
22
+ var NotFoundError = class extends MeterFlowError {
23
+ constructor(message, requestId) {
24
+ super(message, "not_found", false, requestId, 404);
25
+ this.name = "NotFoundError";
26
+ }
27
+ };
28
+ var InsufficientCreditsError = class extends MeterFlowError {
29
+ constructor(message, requestId) {
30
+ super(message, "insufficient_credits", false, requestId, 402);
31
+ this.name = "InsufficientCreditsError";
32
+ }
33
+ };
34
+ var ConflictError = class extends MeterFlowError {
35
+ constructor(message, requestId) {
36
+ super(message, "conflict", false, requestId, 409);
37
+ this.name = "ConflictError";
38
+ }
39
+ };
40
+ var ValidationError = class extends MeterFlowError {
41
+ constructor(message, requestId) {
42
+ super(message, "validation_error", false, requestId, 422);
43
+ this.name = "ValidationError";
44
+ }
45
+ };
46
+ var RateLimitError = class extends MeterFlowError {
47
+ retryAfter;
48
+ constructor(message, retryAfter, requestId) {
49
+ super(message, "rate_limit", true, requestId, 429);
50
+ this.name = "RateLimitError";
51
+ this.retryAfter = retryAfter;
52
+ }
53
+ };
54
+ var ServerError = class extends MeterFlowError {
55
+ constructor(message, requestId, statusCode = 500) {
56
+ super(message, "server_error", true, requestId, statusCode);
57
+ this.name = "ServerError";
58
+ }
59
+ };
60
+
61
+ // src/utils/retry.ts
62
+ function sleep(ms) {
63
+ return new Promise((resolve) => setTimeout(resolve, ms));
64
+ }
65
+ async function retryable(fn, opts, shouldRetry = () => true) {
66
+ let lastError;
67
+ for (let attempt = 0; attempt <= opts.retries; attempt++) {
68
+ try {
69
+ return await fn();
70
+ } catch (err) {
71
+ lastError = err;
72
+ const isLast = attempt === opts.retries;
73
+ if (isLast || !shouldRetry(err)) {
74
+ throw err;
75
+ }
76
+ let delayMs;
77
+ if (err instanceof RateLimitError && err.retryAfter != null) {
78
+ delayMs = err.retryAfter * 1e3;
79
+ } else {
80
+ const baseMs = Math.min(1e3 * Math.pow(opts.factor, attempt), opts.maxDelayMs);
81
+ delayMs = opts.jitter ? baseMs * (0.75 + Math.random() * 0.5) : baseMs;
82
+ }
83
+ await sleep(delayMs);
84
+ }
85
+ }
86
+ throw lastError;
87
+ }
88
+
89
+ // src/utils/http.ts
90
+ var SDK_VERSION = "0.1.0";
91
+ function buildUrl(baseUrl, path, query) {
92
+ const url = new URL(path, baseUrl.endsWith("/") ? baseUrl : baseUrl + "/");
93
+ if (query) {
94
+ for (const [key, value] of Object.entries(query)) {
95
+ url.searchParams.set(key, String(value));
96
+ }
97
+ }
98
+ return url.toString();
99
+ }
100
+ function buildHeaders(options, opts) {
101
+ const headers = {
102
+ Authorization: `Bearer ${options.apiKey}`,
103
+ "User-Agent": `meterflow-node/${SDK_VERSION}`,
104
+ Accept: "application/json"
105
+ };
106
+ if (opts?.body !== void 0) {
107
+ headers["Content-Type"] = "application/json";
108
+ }
109
+ if (opts?.idempotencyKey) {
110
+ headers["Idempotency-Key"] = opts.idempotencyKey;
111
+ }
112
+ return headers;
113
+ }
114
+ async function parseErrorBody(response) {
115
+ try {
116
+ const json = await response.json();
117
+ if (typeof json["detail"] === "string") return json["detail"];
118
+ return JSON.stringify(json);
119
+ } catch {
120
+ return response.statusText || `HTTP ${response.status}`;
121
+ }
122
+ }
123
+ function mapResponseError(status, message, requestId, response) {
124
+ if (status === 401 || status === 403) return new AuthError(message, requestId, status);
125
+ if (status === 402) return new InsufficientCreditsError(message, requestId);
126
+ if (status === 404) return new NotFoundError(message, requestId);
127
+ if (status === 409) return new ConflictError(message, requestId);
128
+ if (status === 422) return new ValidationError(message, requestId);
129
+ if (status === 429) {
130
+ const retryAfterRaw = response.headers.get("Retry-After");
131
+ const retryAfter = retryAfterRaw != null ? parseInt(retryAfterRaw, 10) : void 0;
132
+ return new RateLimitError(message, Number.isFinite(retryAfter) ? retryAfter : void 0, requestId);
133
+ }
134
+ if (status >= 500) return new ServerError(message, requestId, status);
135
+ return new MeterFlowError(message, "request_error", false, requestId, status);
136
+ }
137
+ async function httpRequest(options, method, path, opts) {
138
+ const url = buildUrl(options.baseUrl, path, opts?.query);
139
+ const headers = buildHeaders(options, opts);
140
+ const body = opts?.body !== void 0 ? JSON.stringify(opts.body) : void 0;
141
+ const timeoutMs = opts?.timeout ?? options.timeout;
142
+ return retryable(
143
+ async () => {
144
+ const signal = AbortSignal.timeout(timeoutMs);
145
+ const fetchInit = { method, headers, signal };
146
+ if (body !== void 0) fetchInit.body = body;
147
+ const response = await options.fetch(url, fetchInit);
148
+ const requestId = response.headers.get("x-request-id") ?? void 0;
149
+ if (!response.ok) {
150
+ const message = await parseErrorBody(response);
151
+ throw mapResponseError(response.status, message, requestId, response);
152
+ }
153
+ if (response.status === 204 || response.headers.get("content-length") === "0") {
154
+ return void 0;
155
+ }
156
+ return await response.json();
157
+ },
158
+ { retries: options.retries, factor: 2, jitter: true, maxDelayMs: 1e4 },
159
+ (err) => {
160
+ if (err instanceof MeterFlowError) return err.retryable;
161
+ return true;
162
+ }
163
+ );
164
+ }
165
+
166
+ // src/resources/credits.ts
167
+ var CreditsResource = class {
168
+ constructor(client) {
169
+ this.client = client;
170
+ }
171
+ grant(body, opts) {
172
+ return this.client.request("POST", "credits/grant", {
173
+ body,
174
+ ...opts?.idempotencyKey !== void 0 && { idempotencyKey: opts.idempotencyKey }
175
+ });
176
+ }
177
+ deduct(body, opts) {
178
+ return this.client.request("POST", "credits/deduct", {
179
+ body,
180
+ ...opts?.idempotencyKey !== void 0 && { idempotencyKey: opts.idempotencyKey }
181
+ });
182
+ }
183
+ balance(customerId) {
184
+ return this.client.request("GET", `credits/${encodeURIComponent(customerId)}/balance`);
185
+ }
186
+ transactions(customerId, query) {
187
+ return this.client.request("GET", `credits/${encodeURIComponent(customerId)}/transactions`, {
188
+ ...query !== void 0 && { query }
189
+ });
190
+ }
191
+ };
192
+
193
+ // src/resources/usage.ts
194
+ var UsageResource = class {
195
+ constructor(client) {
196
+ this.client = client;
197
+ }
198
+ record(body, opts) {
199
+ return this.client.request("POST", "usage/events", {
200
+ body,
201
+ ...opts?.idempotencyKey !== void 0 && { idempotencyKey: opts.idempotencyKey }
202
+ });
203
+ }
204
+ recordBatch(events, opts) {
205
+ return this.client.request("POST", "usage/events/batch", {
206
+ body: { events },
207
+ ...opts?.idempotencyKey !== void 0 && { idempotencyKey: opts.idempotencyKey }
208
+ });
209
+ }
210
+ summary(customerId, query) {
211
+ return this.client.request("GET", `usage/${encodeURIComponent(customerId)}`, {
212
+ ...query !== void 0 && { query }
213
+ });
214
+ }
215
+ };
216
+
217
+ // src/resources/subscriptions.ts
218
+ var SubscriptionsResource = class {
219
+ constructor(client) {
220
+ this.client = client;
221
+ }
222
+ create(body, opts) {
223
+ return this.client.request("POST", "subscriptions", {
224
+ body,
225
+ ...opts?.idempotencyKey !== void 0 && { idempotencyKey: opts.idempotencyKey }
226
+ });
227
+ }
228
+ list(query) {
229
+ return this.client.request("GET", "subscriptions", {
230
+ ...query !== void 0 && { query }
231
+ });
232
+ }
233
+ get(subscriptionId) {
234
+ return this.client.request("GET", `subscriptions/${encodeURIComponent(subscriptionId)}`);
235
+ }
236
+ update(subscriptionId, body, opts) {
237
+ return this.client.request("PATCH", `subscriptions/${encodeURIComponent(subscriptionId)}`, {
238
+ body,
239
+ ...opts?.idempotencyKey !== void 0 && { idempotencyKey: opts.idempotencyKey }
240
+ });
241
+ }
242
+ delete(subscriptionId) {
243
+ return this.client.request("DELETE", `subscriptions/${encodeURIComponent(subscriptionId)}`);
244
+ }
245
+ };
246
+
247
+ // src/resources/plans.ts
248
+ var PlansResource = class {
249
+ constructor(client) {
250
+ this.client = client;
251
+ }
252
+ list(query) {
253
+ return this.client.request("GET", "plans", {
254
+ ...query !== void 0 && { query }
255
+ });
256
+ }
257
+ get(planId, query) {
258
+ return this.client.request("GET", `plans/${encodeURIComponent(planId)}`, {
259
+ ...query !== void 0 && { query }
260
+ });
261
+ }
262
+ };
263
+
264
+ // src/client.ts
265
+ var MeterFlow = class {
266
+ options;
267
+ _credits;
268
+ _usage;
269
+ _subscriptions;
270
+ _plans;
271
+ constructor(options) {
272
+ if (!options.apiKey.startsWith("mf_live_") && !options.apiKey.startsWith("mf_test_")) {
273
+ throw new Error("apiKey must start with mf_live_ or mf_test_");
274
+ }
275
+ this.options = {
276
+ apiKey: options.apiKey,
277
+ baseUrl: options.baseUrl ?? "https://api.meterflow.com/api/v1",
278
+ timeout: options.timeout ?? 3e4,
279
+ retries: options.retries ?? 3,
280
+ fetch: options.fetch ?? globalThis.fetch
281
+ };
282
+ }
283
+ get credits() {
284
+ return this._credits ??= new CreditsResource(this);
285
+ }
286
+ get usage() {
287
+ return this._usage ??= new UsageResource(this);
288
+ }
289
+ get subscriptions() {
290
+ return this._subscriptions ??= new SubscriptionsResource(this);
291
+ }
292
+ get plans() {
293
+ return this._plans ??= new PlansResource(this);
294
+ }
295
+ /** @internal — used by resource classes only; not part of the public API */
296
+ request(method, path, opts) {
297
+ return httpRequest(this.options, method, path, opts);
298
+ }
299
+ };
300
+
301
+ // src/utils/webhook-verify.ts
302
+ import { createHmac, timingSafeEqual } from "crypto";
303
+ function verifyWebhook(rawBody, signature, secret) {
304
+ const body = typeof rawBody === "string" ? Buffer.from(rawBody, "utf8") : rawBody;
305
+ const expected = createHmac("sha256", secret).update(body).digest("hex");
306
+ const expectedBuf = Buffer.from(expected, "utf8");
307
+ const actualBuf = Buffer.from(signature, "utf8");
308
+ if (expectedBuf.length !== actualBuf.length) return false;
309
+ return timingSafeEqual(expectedBuf, actualBuf);
310
+ }
311
+ export {
312
+ AuthError,
313
+ ConflictError,
314
+ InsufficientCreditsError,
315
+ MeterFlow,
316
+ MeterFlowError,
317
+ NotFoundError,
318
+ RateLimitError,
319
+ ServerError,
320
+ ValidationError,
321
+ verifyWebhook
322
+ };
@@ -0,0 +1,26 @@
1
+ import type { MeterFlow } from "../client";
2
+ import type { components } from "../types/openapi";
3
+ type CreditGrantRequest = components["schemas"]["CreditGrantRequest"];
4
+ type CreditDeductRequest = components["schemas"]["CreditDeductRequest"];
5
+ type CreditTransactionResponse = components["schemas"]["CreditTransactionResponse"];
6
+ type CreditBalanceResponse = components["schemas"]["CreditBalanceResponse"];
7
+ export interface GrantOptions {
8
+ idempotencyKey?: string;
9
+ }
10
+ export interface DeductOptions {
11
+ idempotencyKey?: string;
12
+ }
13
+ export interface TransactionsQuery {
14
+ page?: number;
15
+ limit?: number;
16
+ }
17
+ export declare class CreditsResource {
18
+ private readonly client;
19
+ constructor(client: MeterFlow);
20
+ grant(body: CreditGrantRequest, opts?: GrantOptions): Promise<CreditTransactionResponse>;
21
+ deduct(body: CreditDeductRequest, opts?: DeductOptions): Promise<CreditTransactionResponse>;
22
+ balance(customerId: string): Promise<CreditBalanceResponse>;
23
+ transactions(customerId: string, query?: TransactionsQuery): Promise<CreditTransactionResponse[]>;
24
+ }
25
+ export {};
26
+ //# sourceMappingURL=credits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credits.d.ts","sourceRoot":"","sources":["../../../src/resources/credits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,KAAK,kBAAkB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,oBAAoB,CAAC,CAAC;AACtE,KAAK,mBAAmB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,qBAAqB,CAAC,CAAC;AACxE,KAAK,yBAAyB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,2BAA2B,CAAC,CAAC;AACpF,KAAK,qBAAqB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,uBAAuB,CAAC,CAAC;AAE5E,MAAM,WAAW,YAAY;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,SAAS;IAE9C,KAAK,CAAC,IAAI,EAAE,kBAAkB,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,yBAAyB,CAAC;IAOxF,MAAM,CAAC,IAAI,EAAE,mBAAmB,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,yBAAyB,CAAC;IAO3F,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAI3D,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,yBAAyB,EAAE,CAAC;CAKlG"}