@marginfront/sdk 0.0.1

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,1365 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ApiError: () => ApiError,
34
+ AuthenticationError: () => AuthenticationError,
35
+ AuthorizationError: () => AuthorizationError,
36
+ ConflictError: () => ConflictError,
37
+ InitializationError: () => InitializationError,
38
+ InternalError: () => InternalError,
39
+ MarginFrontClient: () => MarginFrontClient,
40
+ MarginFrontError: () => MarginFrontError,
41
+ NetworkError: () => NetworkError,
42
+ NotFoundError: () => NotFoundError,
43
+ RateLimitError: () => RateLimitError,
44
+ TimeoutError: () => TimeoutError,
45
+ ValidationError: () => ValidationError,
46
+ createErrorFromResponse: () => createErrorFromResponse,
47
+ isPublishableKey: () => isPublishableKey,
48
+ isSecretKey: () => isSecretKey,
49
+ parseApiError: () => parseApiError,
50
+ parseApiKey: () => parseApiKey
51
+ });
52
+ module.exports = __toCommonJS(index_exports);
53
+
54
+ // src/utils/http.ts
55
+ var import_axios = __toESM(require("axios"));
56
+
57
+ // src/errors.ts
58
+ var MarginFrontError = class _MarginFrontError extends Error {
59
+ constructor(message, statusCode = 500, code = "MARGINFRONT_ERROR", details, metadata = {}) {
60
+ super(message);
61
+ this.name = "MarginFrontError";
62
+ this.statusCode = statusCode;
63
+ this.code = code;
64
+ this.details = details;
65
+ this.metadata = metadata;
66
+ this.requestId = metadata.requestId;
67
+ Object.setPrototypeOf(this, _MarginFrontError.prototype);
68
+ if (Error.captureStackTrace) {
69
+ Error.captureStackTrace(this, this.constructor);
70
+ }
71
+ }
72
+ };
73
+ var AuthenticationError = class _AuthenticationError extends MarginFrontError {
74
+ constructor(message = "Invalid or missing API key", metadata = {}) {
75
+ super(message, 401, "AUTHENTICATION_ERROR", void 0, metadata);
76
+ this.name = "AuthenticationError";
77
+ Object.setPrototypeOf(this, _AuthenticationError.prototype);
78
+ }
79
+ };
80
+ var AuthorizationError = class _AuthorizationError extends MarginFrontError {
81
+ constructor(message = "You do not have permission to perform this action", metadata = {}) {
82
+ super(message, 403, "AUTHORIZATION_ERROR", void 0, metadata);
83
+ this.name = "AuthorizationError";
84
+ Object.setPrototypeOf(this, _AuthorizationError.prototype);
85
+ }
86
+ };
87
+ var NotFoundError = class _NotFoundError extends MarginFrontError {
88
+ constructor(message = "Resource not found", resourceType, resourceId, metadata = {}) {
89
+ super(message, 404, "NOT_FOUND_ERROR", void 0, metadata);
90
+ this.name = "NotFoundError";
91
+ this.resourceType = resourceType;
92
+ this.resourceId = resourceId;
93
+ Object.setPrototypeOf(this, _NotFoundError.prototype);
94
+ }
95
+ };
96
+ var ValidationError = class _ValidationError extends MarginFrontError {
97
+ constructor(message = "Validation failed", validationErrors, metadata = {}) {
98
+ super(message, 400, "VALIDATION_ERROR", validationErrors, metadata);
99
+ this.name = "ValidationError";
100
+ this.validationErrors = validationErrors;
101
+ Object.setPrototypeOf(this, _ValidationError.prototype);
102
+ }
103
+ };
104
+ var RateLimitError = class _RateLimitError extends MarginFrontError {
105
+ constructor(message = "Rate limit exceeded", retryAfter, metadata = {}) {
106
+ super(message, 429, "RATE_LIMIT_ERROR", void 0, metadata);
107
+ this.name = "RateLimitError";
108
+ this.retryAfter = retryAfter;
109
+ Object.setPrototypeOf(this, _RateLimitError.prototype);
110
+ }
111
+ };
112
+ var ConflictError = class _ConflictError extends MarginFrontError {
113
+ constructor(message = "Resource conflict", metadata = {}) {
114
+ super(message, 409, "CONFLICT_ERROR", void 0, metadata);
115
+ this.name = "ConflictError";
116
+ Object.setPrototypeOf(this, _ConflictError.prototype);
117
+ }
118
+ };
119
+ var NetworkError = class _NetworkError extends MarginFrontError {
120
+ constructor(message = "Network error occurred", originalError, metadata = {}) {
121
+ super(message, 0, "NETWORK_ERROR", void 0, metadata);
122
+ this.name = "NetworkError";
123
+ this.originalError = originalError;
124
+ Object.setPrototypeOf(this, _NetworkError.prototype);
125
+ }
126
+ };
127
+ var TimeoutError = class _TimeoutError extends MarginFrontError {
128
+ constructor(message = "Request timed out", timeoutMs, metadata = {}) {
129
+ super(message, 0, "TIMEOUT_ERROR", void 0, metadata);
130
+ this.name = "TimeoutError";
131
+ this.timeoutMs = timeoutMs;
132
+ Object.setPrototypeOf(this, _TimeoutError.prototype);
133
+ }
134
+ };
135
+ var InternalError = class _InternalError extends MarginFrontError {
136
+ constructor(message = "Internal server error", metadata = {}) {
137
+ super(message, 500, "INTERNAL_ERROR", void 0, metadata);
138
+ this.name = "InternalError";
139
+ Object.setPrototypeOf(this, _InternalError.prototype);
140
+ }
141
+ };
142
+ var InitializationError = class _InitializationError extends MarginFrontError {
143
+ constructor(message = "Failed to initialize the SDK", metadata = {}) {
144
+ super(message, 0, "INITIALIZATION_ERROR", void 0, metadata);
145
+ this.name = "InitializationError";
146
+ Object.setPrototypeOf(this, _InitializationError.prototype);
147
+ }
148
+ };
149
+ var ApiError = class _ApiError extends MarginFrontError {
150
+ constructor(message = "API request failed", statusCode, errorCode, metadata = {}) {
151
+ super(message, statusCode || 500, errorCode || "API_ERROR", void 0, metadata);
152
+ this.name = "ApiError";
153
+ this.errorCode = errorCode;
154
+ Object.setPrototypeOf(this, _ApiError.prototype);
155
+ }
156
+ };
157
+ function createErrorFromResponse(statusCode, message, details, metadata = {}) {
158
+ switch (statusCode) {
159
+ case 400:
160
+ return new ValidationError(message, details, metadata);
161
+ case 401:
162
+ return new AuthenticationError(message, metadata);
163
+ case 403:
164
+ return new AuthorizationError(message, metadata);
165
+ case 404:
166
+ return new NotFoundError(message, void 0, void 0, metadata);
167
+ case 409:
168
+ return new ConflictError(message, metadata);
169
+ case 422:
170
+ return new ValidationError(message, details, metadata);
171
+ case 429:
172
+ const retryAfter = metadata.retryAfter;
173
+ return new RateLimitError(message, retryAfter, metadata);
174
+ case 500:
175
+ case 502:
176
+ case 503:
177
+ case 504:
178
+ return new InternalError(message, metadata);
179
+ default:
180
+ return new ApiError(message, statusCode, "API_ERROR", metadata);
181
+ }
182
+ }
183
+ function parseApiError(error) {
184
+ const err = error;
185
+ const message = err.message || "Unknown error";
186
+ const statusCode = err.response?.status;
187
+ const responseData = err.response?.data || {};
188
+ const requestId = err.config?.headers?.["X-Request-ID"] || "unknown";
189
+ const metadata = {
190
+ requestId,
191
+ url: err.config?.url,
192
+ method: err.config?.method,
193
+ responseData,
194
+ retryAfter: err.response?.headers?.["retry-after"] ? parseInt(err.response.headers["retry-after"], 10) : void 0
195
+ };
196
+ if (!statusCode) {
197
+ if (err.code === "ECONNABORTED") {
198
+ return new TimeoutError(message, void 0, metadata);
199
+ }
200
+ return new NetworkError(message, err, metadata);
201
+ }
202
+ return createErrorFromResponse(
203
+ statusCode,
204
+ responseData.message || responseData.error || message,
205
+ responseData.errors,
206
+ metadata
207
+ );
208
+ }
209
+
210
+ // src/utils/http.ts
211
+ var DEFAULT_BASE_URL = "https://api-qa.costingly.com/v1";
212
+ var DEFAULT_TIMEOUT = 3e4;
213
+ var DEFAULT_RETRIES = 0;
214
+ var DEFAULT_RETRY_DELAY = 300;
215
+ var Logger = class {
216
+ constructor(config) {
217
+ this.levelPriority = {
218
+ debug: 0,
219
+ info: 1,
220
+ warn: 2,
221
+ error: 3
222
+ };
223
+ this.defaultHandler = (level, message, data) => {
224
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
225
+ const logMessage = `[${timestamp}] [MarginFront] [${level.toUpperCase()}] ${message}`;
226
+ const logFn = level === "error" ? console.error : level === "warn" ? console.warn : console.log;
227
+ if (data) {
228
+ logFn(logMessage, data);
229
+ } else {
230
+ logFn(logMessage);
231
+ }
232
+ };
233
+ this.enabled = config.logging?.enabled ?? config.debug ?? false;
234
+ this.minLevel = config.logging?.level ?? "info";
235
+ this.handler = config.logging?.handler ?? this.defaultHandler;
236
+ }
237
+ shouldLog(level) {
238
+ return this.enabled && this.levelPriority[level] >= this.levelPriority[this.minLevel];
239
+ }
240
+ debug(message, data) {
241
+ if (this.shouldLog("debug")) this.handler("debug", message, data);
242
+ }
243
+ info(message, data) {
244
+ if (this.shouldLog("info")) this.handler("info", message, data);
245
+ }
246
+ warn(message, data) {
247
+ if (this.shouldLog("warn")) this.handler("warn", message, data);
248
+ }
249
+ error(message, data) {
250
+ if (this.shouldLog("error")) this.handler("error", message, data);
251
+ }
252
+ };
253
+ var Telemetry = class {
254
+ constructor(config) {
255
+ this.activeRequests = /* @__PURE__ */ new Map();
256
+ this.requestCount = 0;
257
+ this.successCount = 0;
258
+ this.errorCount = 0;
259
+ this.totalDuration = 0;
260
+ this.errorTypes = /* @__PURE__ */ new Map();
261
+ this.enabled = config.telemetry?.enabled ?? false;
262
+ this.sampleRate = config.telemetry?.sampleRate ?? 1;
263
+ this.handler = config.telemetry?.handler ?? (() => {
264
+ });
265
+ }
266
+ shouldCollect() {
267
+ return this.enabled && Math.random() <= this.sampleRate;
268
+ }
269
+ generateRequestId() {
270
+ return `req_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
271
+ }
272
+ normalizePath(path) {
273
+ if (!path) return "";
274
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
275
+ return normalizedPath.replace(/\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, "/:id").replace(/\/\d+(?=\/|$)/g, "/:id");
276
+ }
277
+ startRequest(method, path, requestId) {
278
+ const shouldTrack = this.shouldCollect();
279
+ const id = requestId || this.generateRequestId();
280
+ if (shouldTrack) {
281
+ const metrics = {
282
+ requestId: id,
283
+ method: method.toUpperCase(),
284
+ path: this.normalizePath(path),
285
+ startTime: Date.now()
286
+ };
287
+ this.activeRequests.set(id, metrics);
288
+ }
289
+ return { requestId: id, isTracked: shouldTrack };
290
+ }
291
+ endRequest(requestId, statusCode, error, retryCount) {
292
+ const metrics = this.activeRequests.get(requestId);
293
+ if (!metrics) return;
294
+ metrics.endTime = Date.now();
295
+ metrics.duration = metrics.endTime - metrics.startTime;
296
+ metrics.statusCode = statusCode;
297
+ metrics.success = !error && (statusCode ? statusCode < 400 : true);
298
+ metrics.retryCount = retryCount || 0;
299
+ if (error) {
300
+ metrics.errorMessage = error.message;
301
+ metrics.errorType = error.name || "UnknownError";
302
+ const currentCount = this.errorTypes.get(metrics.errorType) || 0;
303
+ this.errorTypes.set(metrics.errorType, currentCount + 1);
304
+ this.errorCount++;
305
+ } else {
306
+ this.successCount++;
307
+ }
308
+ this.requestCount++;
309
+ this.totalDuration += metrics.duration;
310
+ this.handler(metrics);
311
+ this.activeRequests.delete(requestId);
312
+ }
313
+ getStats() {
314
+ return {
315
+ requestCount: this.requestCount,
316
+ successCount: this.successCount,
317
+ errorCount: this.errorCount,
318
+ successRate: this.requestCount > 0 ? this.successCount / this.requestCount : 1,
319
+ averageDuration: this.requestCount > 0 ? this.totalDuration / this.requestCount : 0,
320
+ errorBreakdown: Object.fromEntries(this.errorTypes)
321
+ };
322
+ }
323
+ resetStats() {
324
+ this.requestCount = 0;
325
+ this.successCount = 0;
326
+ this.errorCount = 0;
327
+ this.totalDuration = 0;
328
+ this.errorTypes.clear();
329
+ }
330
+ };
331
+ function calculateBackoff(retryCount, initialDelay) {
332
+ return initialDelay * Math.pow(2, retryCount);
333
+ }
334
+ function sleep(ms) {
335
+ return new Promise((resolve) => setTimeout(resolve, ms));
336
+ }
337
+ function isRetryable(error) {
338
+ if (error.response) {
339
+ const status = error.response.status;
340
+ return status === 429 || status >= 500 && status < 600;
341
+ }
342
+ return !error.response;
343
+ }
344
+ var HttpClient = class {
345
+ constructor(config) {
346
+ this.logger = new Logger(config);
347
+ this.telemetry = new Telemetry(config);
348
+ this.retries = config.retries ?? DEFAULT_RETRIES;
349
+ this.retryDelay = config.retryDelay ?? DEFAULT_RETRY_DELAY;
350
+ const baseURL = config.baseUrl || config.baseURL || DEFAULT_BASE_URL;
351
+ const isBrowser = typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
352
+ const headers = {
353
+ "Content-Type": "application/json",
354
+ Accept: "application/json",
355
+ "x-api-key": config.apiKey,
356
+ ...config.headers
357
+ };
358
+ if (!isBrowser) {
359
+ headers["User-Agent"] = "marginfront-node/1.0.0";
360
+ }
361
+ this.client = import_axios.default.create({
362
+ baseURL,
363
+ timeout: config.timeout || DEFAULT_TIMEOUT,
364
+ headers
365
+ });
366
+ this.client.interceptors.request.use(
367
+ (requestConfig) => {
368
+ const customConfig = requestConfig;
369
+ customConfig.metadata = customConfig.metadata || {};
370
+ const { requestId, isTracked } = this.telemetry.startRequest(
371
+ requestConfig.method || "GET",
372
+ requestConfig.url || "",
373
+ customConfig.metadata.requestId
374
+ );
375
+ requestConfig.headers = requestConfig.headers || {};
376
+ requestConfig.headers["X-Request-ID"] = requestId;
377
+ customConfig.metadata.requestId = requestId;
378
+ customConfig.metadata.telemetryTracked = isTracked;
379
+ this.logger.debug(`Request: ${requestConfig.method?.toUpperCase()} ${requestConfig.url}`, {
380
+ requestId
381
+ });
382
+ return requestConfig;
383
+ },
384
+ (error) => Promise.reject(error)
385
+ );
386
+ this.client.interceptors.response.use(
387
+ (response) => {
388
+ const customConfig = response.config;
389
+ const requestId = customConfig.metadata?.requestId;
390
+ const isTracked = customConfig.metadata?.telemetryTracked;
391
+ if (isTracked && requestId) {
392
+ this.telemetry.endRequest(requestId, response.status);
393
+ }
394
+ this.logger.debug(`Response: ${response.status}`, { requestId });
395
+ return response;
396
+ },
397
+ (error) => {
398
+ const customConfig = error.config;
399
+ const requestId = customConfig?.metadata?.requestId;
400
+ const isTracked = customConfig?.metadata?.telemetryTracked;
401
+ const retryCount = customConfig?.metadata?.retryCount || 0;
402
+ if (isTracked && requestId) {
403
+ this.telemetry.endRequest(requestId, error.response?.status, error, retryCount);
404
+ }
405
+ this.logger.error(`Error: ${error.message}`, {
406
+ requestId,
407
+ status: error.response?.status
408
+ });
409
+ return Promise.reject(error);
410
+ }
411
+ );
412
+ }
413
+ /**
414
+ * Make a request with retry logic
415
+ */
416
+ async request(config) {
417
+ let lastError;
418
+ config.metadata = config.metadata || {};
419
+ for (let retry = 0; retry <= this.retries; retry++) {
420
+ try {
421
+ config.metadata.retryCount = retry;
422
+ if (retry > 0) {
423
+ this.logger.info(`Retry attempt ${retry}/${this.retries}`, {
424
+ requestId: config.metadata.requestId
425
+ });
426
+ }
427
+ const response = await this.client.request(config);
428
+ return response.data;
429
+ } catch (error) {
430
+ lastError = error;
431
+ if (retry < this.retries && isRetryable(lastError)) {
432
+ const retryAfterHeader = lastError.response?.headers?.["retry-after"];
433
+ const backoffTime = retryAfterHeader ? parseInt(retryAfterHeader, 10) * 1e3 : calculateBackoff(retry, this.retryDelay);
434
+ this.logger.debug(`Retrying in ${backoffTime}ms...`);
435
+ await sleep(backoffTime);
436
+ continue;
437
+ }
438
+ break;
439
+ }
440
+ }
441
+ throw parseApiError(lastError);
442
+ }
443
+ /**
444
+ * Make a GET request
445
+ */
446
+ async get(path, params) {
447
+ const config = {
448
+ method: "GET",
449
+ url: path
450
+ };
451
+ if (params) {
452
+ config.params = Object.fromEntries(
453
+ Object.entries(params).filter(([, v]) => v !== void 0 && v !== null)
454
+ );
455
+ }
456
+ return this.request(config);
457
+ }
458
+ /**
459
+ * Make a POST request
460
+ */
461
+ async post(path, data) {
462
+ return this.request({
463
+ method: "POST",
464
+ url: path,
465
+ data
466
+ });
467
+ }
468
+ /**
469
+ * Make a PUT request
470
+ */
471
+ async put(path, data) {
472
+ return this.request({
473
+ method: "PUT",
474
+ url: path,
475
+ data
476
+ });
477
+ }
478
+ /**
479
+ * Make a PATCH request
480
+ */
481
+ async patch(path, data) {
482
+ return this.request({
483
+ method: "PATCH",
484
+ url: path,
485
+ data
486
+ });
487
+ }
488
+ /**
489
+ * Make a DELETE request
490
+ */
491
+ async delete(path) {
492
+ return this.request({
493
+ method: "DELETE",
494
+ url: path
495
+ });
496
+ }
497
+ /**
498
+ * Get telemetry statistics
499
+ */
500
+ getTelemetryStats() {
501
+ return this.telemetry.getStats();
502
+ }
503
+ /**
504
+ * Reset telemetry statistics
505
+ */
506
+ resetTelemetryStats() {
507
+ this.telemetry.resetStats();
508
+ }
509
+ };
510
+
511
+ // src/utils/validation.ts
512
+ function validateRequired(value, fieldName) {
513
+ if (value === void 0 || value === null || value === "") {
514
+ throw new ValidationError(`${fieldName} is required`);
515
+ }
516
+ }
517
+ function validateNonNegative(value, fieldName) {
518
+ if (typeof value !== "number" || isNaN(value)) {
519
+ throw new ValidationError(`${fieldName} must be a number`);
520
+ }
521
+ if (value < 0) {
522
+ throw new ValidationError(`${fieldName} must be non-negative`);
523
+ }
524
+ }
525
+ function validateDateString(value, fieldName) {
526
+ const date = value instanceof Date ? value : new Date(value);
527
+ if (isNaN(date.getTime())) {
528
+ throw new ValidationError(`${fieldName} must be a valid ISO date string`);
529
+ }
530
+ }
531
+ function validateUsageRecord(record) {
532
+ validateRequired(record.customerExternalId, "customerExternalId");
533
+ validateRequired(record.agentId, "agentId");
534
+ if (!record.signalName) {
535
+ throw new ValidationError("signalName is required");
536
+ }
537
+ if (record.quantity !== void 0 && record.quantity !== null) {
538
+ validateNonNegative(record.quantity, "quantity");
539
+ }
540
+ if (record.usageDate !== void 0) {
541
+ validateDateString(record.usageDate, "usageDate");
542
+ }
543
+ }
544
+ function validateUsageRecords(records) {
545
+ if (!Array.isArray(records)) {
546
+ throw new ValidationError("records must be an array");
547
+ }
548
+ if (records.length === 0) {
549
+ throw new ValidationError("records array cannot be empty");
550
+ }
551
+ records.forEach((record, index) => {
552
+ try {
553
+ validateUsageRecord(record);
554
+ } catch (error) {
555
+ if (error instanceof ValidationError) {
556
+ throw new ValidationError(`Invalid record at index ${index}: ${error.message}`);
557
+ }
558
+ throw error;
559
+ }
560
+ });
561
+ }
562
+ var KEY_PREFIXES = {
563
+ secret: "mf_sk_",
564
+ publishable: "mf_pk_"
565
+ };
566
+ function parseApiKey(apiKey) {
567
+ if (!apiKey || typeof apiKey !== "string") {
568
+ return null;
569
+ }
570
+ if (apiKey.startsWith(KEY_PREFIXES.secret)) {
571
+ return { type: "secret" };
572
+ }
573
+ if (apiKey.startsWith(KEY_PREFIXES.publishable)) {
574
+ return { type: "publishable" };
575
+ }
576
+ return null;
577
+ }
578
+ function isSecretKey(apiKey) {
579
+ const parsed = parseApiKey(apiKey);
580
+ return parsed !== null && parsed.type === "secret";
581
+ }
582
+ function isPublishableKey(apiKey) {
583
+ const parsed = parseApiKey(apiKey);
584
+ return parsed !== null && parsed.type === "publishable";
585
+ }
586
+ function getMinKeyLength(prefix) {
587
+ return prefix.length + 16;
588
+ }
589
+ function validateApiKey(apiKey) {
590
+ if (!apiKey || typeof apiKey !== "string") {
591
+ throw new ValidationError("API key is required");
592
+ }
593
+ const parsed = parseApiKey(apiKey);
594
+ if (!parsed) {
595
+ throw new ValidationError(
596
+ "Invalid API key format. Expected mf_sk_* or mf_pk_*"
597
+ );
598
+ }
599
+ const prefix = KEY_PREFIXES[parsed.type];
600
+ const minLength = getMinKeyLength(prefix);
601
+ if (apiKey.length < minLength) {
602
+ throw new ValidationError("Invalid API key format. Key is too short");
603
+ }
604
+ }
605
+
606
+ // src/resources/usage.ts
607
+ var UsageResource = class {
608
+ constructor(http) {
609
+ this.http = http;
610
+ }
611
+ /**
612
+ * Track events for a customer - supports both single record and batch operations
613
+ *
614
+ * @param params - Event tracking parameters (single record or batch)
615
+ * @returns Tracked event data
616
+ *
617
+ * @example
618
+ * ```typescript
619
+ * // Single event
620
+ * await client.usage.trackEvent({
621
+ * agentId: 'agent_123',
622
+ * customerExternalId: 'customer_456',
623
+ * signalName: 'api_call',
624
+ * quantity: 1
625
+ * });
626
+ *
627
+ * // Batch tracking
628
+ * await client.usage.trackEvent({
629
+ * records: [
630
+ * { customerExternalId: 'cust_1', agentId: 'agent_123', signalName: 'api_call', quantity: 10 },
631
+ * { customerExternalId: 'cust_2', agentId: 'agent_123', signalName: 'storage', quantity: 100 }
632
+ * ]
633
+ * });
634
+ * ```
635
+ */
636
+ async trackEvent(params) {
637
+ if ("records" in params) {
638
+ const records = params.records.map((record2) => this.normalizeRecord(record2));
639
+ validateUsageRecords(records);
640
+ const response2 = await this.http.post("/sdk/usage/record", {
641
+ records
642
+ });
643
+ return this.convertToBatchResponse(response2);
644
+ }
645
+ const record = this.normalizeRecord(params);
646
+ validateUsageRecord(record);
647
+ const response = await this.http.post("/sdk/usage/record", {
648
+ records: [record]
649
+ });
650
+ if (response.results.success.length === 1) {
651
+ const successResult = response.results.success[0];
652
+ return {
653
+ id: successResult.eventId,
654
+ success: true,
655
+ eventId: successResult.eventId,
656
+ rawEventId: successResult.rawEventId,
657
+ timestamp: successResult.timestamp
658
+ };
659
+ }
660
+ return this.convertToBatchResponse(response);
661
+ }
662
+ /**
663
+ * Record a single usage event
664
+ *
665
+ * @param record - The usage record to track
666
+ * @returns The response containing processing results
667
+ *
668
+ * @example
669
+ * ```typescript
670
+ * const result = await client.usage.record({
671
+ * customerExternalId: 'cust_123',
672
+ * agentId: 'agent_abc',
673
+ * signalName: 'api_call',
674
+ * quantity: 1,
675
+ * metadata: { model: 'gpt-4' }
676
+ * });
677
+ * ```
678
+ */
679
+ async record(record) {
680
+ const normalizedRecord = this.normalizeRecord(record);
681
+ validateUsageRecord(normalizedRecord);
682
+ return this.http.post("/sdk/usage/record", {
683
+ records: [normalizedRecord]
684
+ });
685
+ }
686
+ /**
687
+ * Record multiple usage events in a batch
688
+ *
689
+ * @param records - Array of usage records to track
690
+ * @returns The response containing processing results for all records
691
+ *
692
+ * @example
693
+ * ```typescript
694
+ * const result = await client.usage.recordBatch([
695
+ * { customerExternalId: 'cust_123', agentId: 'agent_abc', signalName: 'api_call', quantity: 5 },
696
+ * { customerExternalId: 'cust_456', agentId: 'agent_abc', signalName: 'api_call', quantity: 3 },
697
+ * ]);
698
+ * console.log(`Processed ${result.successful} of ${result.processed} records`);
699
+ * ```
700
+ */
701
+ async recordBatch(records) {
702
+ const normalizedRecords = records.map((record) => this.normalizeRecord(record));
703
+ validateUsageRecords(normalizedRecords);
704
+ return this.http.post("/sdk/usage/record", {
705
+ records: normalizedRecords
706
+ });
707
+ }
708
+ /**
709
+ * Normalize a usage record
710
+ */
711
+ normalizeRecord(record) {
712
+ const normalized = { ...record };
713
+ if (normalized.quantity === void 0) {
714
+ normalized.quantity = 1;
715
+ }
716
+ if (normalized.usageDate instanceof Date) {
717
+ normalized.usageDate = normalized.usageDate.toISOString();
718
+ }
719
+ return normalized;
720
+ }
721
+ /**
722
+ * Convert native response to BatchEventResponse format
723
+ */
724
+ convertToBatchResponse(response) {
725
+ return {
726
+ success: response.failed === 0,
727
+ totalRecords: response.processed,
728
+ successCount: response.successful,
729
+ failureCount: response.failed,
730
+ results: [
731
+ ...response.results.success.map((result) => ({
732
+ success: true,
733
+ responseData: {
734
+ id: result.eventId,
735
+ success: true,
736
+ eventId: result.eventId,
737
+ rawEventId: result.rawEventId,
738
+ timestamp: result.timestamp
739
+ }
740
+ })),
741
+ ...response.results.failed.map((result) => ({
742
+ success: false,
743
+ error: result.error,
744
+ originalData: result.record
745
+ }))
746
+ ]
747
+ };
748
+ }
749
+ };
750
+
751
+ // src/resources/customers.ts
752
+ var CustomersResource = class {
753
+ constructor(http) {
754
+ this.http = http;
755
+ }
756
+ /**
757
+ * Create a new customer
758
+ *
759
+ * @param params - Customer creation parameters
760
+ * @returns The created customer
761
+ *
762
+ * @example
763
+ * ```typescript
764
+ * const customer = await client.customers.create({
765
+ * name: 'Acme Corp',
766
+ * externalId: 'acme_123',
767
+ * email: 'billing@acme.com',
768
+ * agentId: 'agent_abc' // auto-creates subscription
769
+ * });
770
+ * ```
771
+ */
772
+ async create(params) {
773
+ validateRequired(params.name, "name");
774
+ return this.http.post("/sdk/customers", params);
775
+ }
776
+ /**
777
+ * Get a single customer by ID
778
+ *
779
+ * @param id - The customer's ID
780
+ * @returns The customer with subscriptions
781
+ *
782
+ * @example
783
+ * ```typescript
784
+ * const customer = await client.customers.get('cust_123');
785
+ * console.log(customer.name);
786
+ * ```
787
+ */
788
+ async get(id) {
789
+ validateRequired(id, "id");
790
+ return this.http.get(`/sdk/customers/${id}`);
791
+ }
792
+ /**
793
+ * Update an existing customer
794
+ *
795
+ * @param id - The customer's ID
796
+ * @param params - Fields to update
797
+ * @returns The updated customer
798
+ *
799
+ * @example
800
+ * ```typescript
801
+ * const customer = await client.customers.update('cust_123', {
802
+ * name: 'New Company Name',
803
+ * email: 'new-email@company.com'
804
+ * });
805
+ * ```
806
+ */
807
+ async update(id, params) {
808
+ validateRequired(id, "id");
809
+ return this.http.patch(`/sdk/customers/${id}`, params);
810
+ }
811
+ /**
812
+ * Delete a customer
813
+ *
814
+ * @param id - The customer's ID
815
+ *
816
+ * @example
817
+ * ```typescript
818
+ * await client.customers.delete('cust_123');
819
+ * ```
820
+ */
821
+ async delete(id) {
822
+ validateRequired(id, "id");
823
+ await this.http.delete(`/sdk/customers/${id}`);
824
+ }
825
+ /**
826
+ * List customers with pagination and filtering
827
+ *
828
+ * @param params - List parameters
829
+ * @returns Paginated list of customers
830
+ *
831
+ * @example
832
+ * ```typescript
833
+ * const { data, totalResults, hasMore } = await client.customers.list({
834
+ * limit: 10,
835
+ * page: 1
836
+ * });
837
+ * ```
838
+ */
839
+ async list(params) {
840
+ const response = await this.http.get("/sdk/customers", params);
841
+ if (Array.isArray(response)) {
842
+ return {
843
+ data: response,
844
+ totalResults: response.length,
845
+ page: params?.page || 1,
846
+ limit: params?.limit || response.length,
847
+ hasMore: false
848
+ };
849
+ }
850
+ return response;
851
+ }
852
+ };
853
+
854
+ // src/resources/invoices.ts
855
+ var InvoicesResource = class {
856
+ constructor(http) {
857
+ this.http = http;
858
+ }
859
+ /**
860
+ * List invoices with optional filters
861
+ *
862
+ * @param params - Optional filter parameters
863
+ * @returns Paginated list of invoices
864
+ *
865
+ * @example
866
+ * ```typescript
867
+ * const { invoices, totalResults } = await client.invoices.list({
868
+ * customerId: 'cust_123',
869
+ * status: 'pending',
870
+ * page: 1,
871
+ * limit: 20
872
+ * });
873
+ * ```
874
+ */
875
+ async list(params) {
876
+ return this.http.get("/sdk/invoices", params);
877
+ }
878
+ /**
879
+ * Get a single invoice by ID
880
+ *
881
+ * @param invoiceId - The invoice's ID
882
+ * @returns The invoice with full details including line items and payments
883
+ *
884
+ * @example
885
+ * ```typescript
886
+ * const invoice = await client.invoices.get('inv_abc');
887
+ * console.log(`Invoice ${invoice.invoiceNumber}: ${invoice.totalAmount}`);
888
+ * ```
889
+ */
890
+ async get(invoiceId) {
891
+ validateRequired(invoiceId, "invoiceId");
892
+ return this.http.get(`/sdk/invoices/${invoiceId}`);
893
+ }
894
+ };
895
+
896
+ // src/resources/analytics.ts
897
+ var AnalyticsResource = class {
898
+ constructor(http) {
899
+ this.http = http;
900
+ }
901
+ /**
902
+ * Get usage analytics for a date range
903
+ *
904
+ * @param params - Analytics query parameters
905
+ * @returns Usage analytics with summary and time-series data
906
+ *
907
+ * @example
908
+ * ```typescript
909
+ * const analytics = await client.analytics.usage({
910
+ * startDate: '2024-01-01',
911
+ * endDate: '2024-01-31',
912
+ * groupBy: 'daily',
913
+ * customerId: 'cust_123'
914
+ * });
915
+ *
916
+ * console.log(`Total usage: ${analytics.summary.totalQuantity}`);
917
+ * console.log(`Total cost: $${analytics.summary.totalCost}`);
918
+ *
919
+ * // Time series data
920
+ * analytics.data.forEach(point => {
921
+ * console.log(`${point.date}: ${point.quantity} units, $${point.cost}`);
922
+ * });
923
+ * ```
924
+ */
925
+ async usage(params) {
926
+ validateRequired(params.startDate, "startDate");
927
+ validateRequired(params.endDate, "endDate");
928
+ validateDateString(params.startDate, "startDate");
929
+ validateDateString(params.endDate, "endDate");
930
+ return this.http.get("/sdk/analytics/usage", params);
931
+ }
932
+ };
933
+
934
+ // src/resources/subscriptions.ts
935
+ var SubscriptionsResource = class {
936
+ constructor(http) {
937
+ this.http = http;
938
+ }
939
+ /**
940
+ * List subscriptions with optional filters
941
+ *
942
+ * @param params - Optional filter parameters
943
+ * @returns Paginated list of subscriptions
944
+ *
945
+ * @example
946
+ * ```typescript
947
+ * const { subscriptions, totalResults } = await client.subscriptions.list({
948
+ * status: 'active',
949
+ * customerId: 'cust_123',
950
+ * page: 1,
951
+ * limit: 20
952
+ * });
953
+ * ```
954
+ */
955
+ async list(params) {
956
+ return this.http.get("/sdk/subscriptions", params);
957
+ }
958
+ /**
959
+ * Get a single subscription by ID
960
+ *
961
+ * @param subscriptionId - The subscription's ID
962
+ * @returns The subscription with full details including usage summary
963
+ *
964
+ * @example
965
+ * ```typescript
966
+ * const sub = await client.subscriptions.get('sub_abc');
967
+ * console.log(`Status: ${sub.status}`);
968
+ * console.log(`Usage this period: ${sub.usage.totalQuantity}`);
969
+ * ```
970
+ */
971
+ async get(subscriptionId) {
972
+ validateRequired(subscriptionId, "subscriptionId");
973
+ return this.http.get(`/sdk/subscriptions/${subscriptionId}`);
974
+ }
975
+ };
976
+
977
+ // src/resources/portal-sessions.ts
978
+ var PortalSessionsResource = class {
979
+ constructor(http, assertSecretKey) {
980
+ this.http = http;
981
+ this.assertSecretKey = assertSecretKey;
982
+ }
983
+ /**
984
+ * Create a new portal session
985
+ *
986
+ * Creates a short-lived portal session for a customer.
987
+ * The returned URL can be used to redirect the customer to their billing portal.
988
+ *
989
+ * @param params - Portal session parameters
990
+ * @returns Created portal session with URL and token
991
+ * @throws ValidationError if neither customerId nor customerExternalId is provided
992
+ * @throws AuthenticationError if using a publishable key
993
+ *
994
+ * @example
995
+ * ```typescript
996
+ * // By customer ID
997
+ * const session = await client.portalSessions.create({
998
+ * customerId: 'cust_123',
999
+ * returnUrl: 'https://myapp.com/account'
1000
+ * });
1001
+ *
1002
+ * // By external ID
1003
+ * const session = await client.portalSessions.create({
1004
+ * customerExternalId: 'your_customer_id',
1005
+ * features: ['invoices', 'usage']
1006
+ * });
1007
+ *
1008
+ * console.log(session.url); // Redirect customer here
1009
+ * ```
1010
+ */
1011
+ async create(params) {
1012
+ this.assertSecretKey();
1013
+ if (!params.customerId && !params.customerExternalId) {
1014
+ throw new ValidationError(
1015
+ "Either customerId or customerExternalId is required"
1016
+ );
1017
+ }
1018
+ return this.http.post("/sdk/portal-sessions", params);
1019
+ }
1020
+ /**
1021
+ * Get a portal session by ID
1022
+ *
1023
+ * @param sessionId - Portal session ID
1024
+ * @returns Portal session details
1025
+ *
1026
+ * @example
1027
+ * ```typescript
1028
+ * const session = await client.portalSessions.get('ps_123');
1029
+ * console.log(session.expiresAt);
1030
+ * ```
1031
+ */
1032
+ async get(sessionId) {
1033
+ this.assertSecretKey();
1034
+ if (!sessionId) {
1035
+ throw new ValidationError("sessionId is required");
1036
+ }
1037
+ return this.http.get(`/sdk/portal-sessions/${sessionId}`);
1038
+ }
1039
+ /**
1040
+ * List portal sessions
1041
+ *
1042
+ * @param params - Optional filters
1043
+ * @returns List of portal sessions
1044
+ *
1045
+ * @example
1046
+ * ```typescript
1047
+ * // List all active sessions
1048
+ * const { data } = await client.portalSessions.list();
1049
+ *
1050
+ * // List sessions for a specific customer
1051
+ * const { data } = await client.portalSessions.list({
1052
+ * customerId: 'cust_123',
1053
+ * includeExpired: true
1054
+ * });
1055
+ * ```
1056
+ */
1057
+ async list(params) {
1058
+ this.assertSecretKey();
1059
+ return this.http.get("/sdk/portal-sessions", {
1060
+ customerId: params?.customerId,
1061
+ limit: params?.limit,
1062
+ includeExpired: params?.includeExpired
1063
+ });
1064
+ }
1065
+ /**
1066
+ * Revoke a portal session
1067
+ *
1068
+ * Immediately invalidates the session, preventing further access.
1069
+ *
1070
+ * @param sessionId - Portal session ID to revoke
1071
+ *
1072
+ * @example
1073
+ * ```typescript
1074
+ * await client.portalSessions.revoke('ps_123');
1075
+ * ```
1076
+ */
1077
+ async revoke(sessionId) {
1078
+ this.assertSecretKey();
1079
+ if (!sessionId) {
1080
+ throw new ValidationError("sessionId is required");
1081
+ }
1082
+ await this.http.delete(`/sdk/portal-sessions/${sessionId}`);
1083
+ }
1084
+ };
1085
+
1086
+ // src/client.ts
1087
+ var MarginFrontClient = class {
1088
+ /**
1089
+ * Create a new MarginFront client
1090
+ *
1091
+ * @param apiKeyOrConfig - Either an API key string or a configuration object
1092
+ * @param options - Optional client options (when first param is API key)
1093
+ *
1094
+ * @example
1095
+ * ```typescript
1096
+ * // Simple initialization
1097
+ * const client = new MarginFrontClient('mf_sk_your_secret_key');
1098
+ *
1099
+ * // With options
1100
+ * const client = new MarginFrontClient('mf_sk_your_secret_key', {
1101
+ * timeout: 10000,
1102
+ * retries: 3
1103
+ * });
1104
+ *
1105
+ * // With config object
1106
+ * const client = new MarginFrontClient({
1107
+ * apiKey: 'mf_sk_your_secret_key',
1108
+ * baseUrl: 'https://api.example.com/v1',
1109
+ * debug: true
1110
+ * });
1111
+ * ```
1112
+ */
1113
+ constructor(apiKeyOrConfig, options = {}) {
1114
+ this._orgInfo = null;
1115
+ let config;
1116
+ if (typeof apiKeyOrConfig === "string") {
1117
+ config = {
1118
+ apiKey: apiKeyOrConfig,
1119
+ ...options
1120
+ };
1121
+ } else {
1122
+ config = apiKeyOrConfig;
1123
+ }
1124
+ validateApiKey(config.apiKey);
1125
+ this._apiKey = config.apiKey;
1126
+ const keyInfo = parseApiKey(config.apiKey);
1127
+ if (!keyInfo) {
1128
+ throw new ValidationError("Invalid API key format");
1129
+ }
1130
+ this._keyType = keyInfo.type;
1131
+ this.http = new HttpClient(config);
1132
+ this.usage = new UsageResource(this.http);
1133
+ this.customers = new CustomersResource(this.http);
1134
+ this.invoices = new InvoicesResource(this.http);
1135
+ this.analytics = new AnalyticsResource(this.http);
1136
+ this.subscriptions = new SubscriptionsResource(this.http);
1137
+ this.portalSessions = new PortalSessionsResource(
1138
+ this.http,
1139
+ () => this.assertSecretKey()
1140
+ );
1141
+ }
1142
+ /**
1143
+ * Assert that the current API key is a secret key
1144
+ * @throws ValidationError if using a publishable key
1145
+ */
1146
+ assertSecretKey() {
1147
+ if (this._keyType !== "secret") {
1148
+ throw new ValidationError(
1149
+ "This operation requires a secret key (mf_sk_*). Publishable keys (mf_pk_*) cannot perform this action. Use your secret key on the server side only."
1150
+ );
1151
+ }
1152
+ }
1153
+ /**
1154
+ * Connect to the MarginFront API and verify credentials
1155
+ *
1156
+ * This must be called once before using any API methods that require verification.
1157
+ * Optional but recommended for validating your API key.
1158
+ *
1159
+ * @returns Promise resolving to the client instance for chaining
1160
+ * @throws AuthenticationError if API key is invalid
1161
+ * @throws InitializationError for other initialization errors
1162
+ *
1163
+ * @example
1164
+ * ```typescript
1165
+ * const client = new MarginFrontClient('mf_sk_your_secret_key');
1166
+ *
1167
+ * try {
1168
+ * await client.connect();
1169
+ * console.log('Connected to MarginFront API');
1170
+ * } catch (error) {
1171
+ * console.error('Failed to connect:', error);
1172
+ * }
1173
+ * ```
1174
+ */
1175
+ async connect() {
1176
+ try {
1177
+ const result = await this.http.get("/sdk/verify");
1178
+ if (!result || !result.organization || !result.organization.id) {
1179
+ throw new InitializationError("Unexpected response format from API key verification");
1180
+ }
1181
+ this._orgInfo = result.organization;
1182
+ return this;
1183
+ } catch (error) {
1184
+ const err = error;
1185
+ const statusCode = err.statusCode || err.response?.status;
1186
+ const errorMessage = err.message || "Unknown error";
1187
+ const requestId = err.requestId || "unknown";
1188
+ if (statusCode === 401) {
1189
+ throw new AuthenticationError(
1190
+ "Invalid API key. Please check your API key and try again.",
1191
+ { requestId }
1192
+ );
1193
+ }
1194
+ throw new InitializationError(`Connection failed: ${errorMessage}`, { requestId });
1195
+ }
1196
+ }
1197
+ /**
1198
+ * Verify the API key and get organization details
1199
+ *
1200
+ * @returns Verification result with organization info
1201
+ *
1202
+ * @example
1203
+ * ```typescript
1204
+ * const result = await client.verify();
1205
+ * if (result.verified) {
1206
+ * console.log(`Connected to ${result.organization.name}`);
1207
+ * }
1208
+ * ```
1209
+ */
1210
+ async verify() {
1211
+ const result = await this.http.get("/sdk/verify");
1212
+ if (result.organization && !this._orgInfo) {
1213
+ this._orgInfo = result.organization;
1214
+ }
1215
+ return result;
1216
+ }
1217
+ /**
1218
+ * Get organization information
1219
+ *
1220
+ * @returns Organization information from API key verification
1221
+ * @throws Error if organization info is not available (call connect() first)
1222
+ *
1223
+ * @example
1224
+ * ```typescript
1225
+ * await client.connect();
1226
+ * const org = client.getOrganization();
1227
+ * console.log(`Organization: ${org.name}`);
1228
+ * ```
1229
+ */
1230
+ getOrganization() {
1231
+ if (!this._orgInfo) {
1232
+ throw new Error(
1233
+ "Organization information is not available. Make sure the API key is valid and you have called connect()"
1234
+ );
1235
+ }
1236
+ return { ...this._orgInfo };
1237
+ }
1238
+ /**
1239
+ * Track event for a customer (shorthand method)
1240
+ *
1241
+ * @param params - Event tracking parameters
1242
+ * @returns Tracked event data
1243
+ *
1244
+ * @example
1245
+ * ```typescript
1246
+ * // Simple event tracking
1247
+ * await client.trackEvent({
1248
+ * agentId: 'agent_123',
1249
+ * customerExternalId: 'customer_456',
1250
+ * signalName: 'api_call',
1251
+ * quantity: 1
1252
+ * });
1253
+ *
1254
+ * // Batch tracking
1255
+ * await client.trackEvent({
1256
+ * records: [
1257
+ * { customerExternalId: 'cust_1', agentId: 'agent_123', signalName: 'api_call', quantity: 10 },
1258
+ * { customerExternalId: 'cust_2', agentId: 'agent_123', signalName: 'storage', quantity: 100 }
1259
+ * ]
1260
+ * });
1261
+ * ```
1262
+ */
1263
+ async trackEvent(params) {
1264
+ return this.usage.trackEvent(params);
1265
+ }
1266
+ /**
1267
+ * Re-verify the API key
1268
+ *
1269
+ * Useful if you suspect the API key status might have changed
1270
+ *
1271
+ * @returns Updated organization information
1272
+ */
1273
+ async verifyApiKey() {
1274
+ const result = await this.verify();
1275
+ if (!result.organization) {
1276
+ throw new InitializationError("Unexpected response format from API key verification");
1277
+ }
1278
+ this._orgInfo = result.organization;
1279
+ return { ...this._orgInfo };
1280
+ }
1281
+ /**
1282
+ * Get current telemetry statistics
1283
+ *
1284
+ * @returns Telemetry statistics
1285
+ *
1286
+ * @example
1287
+ * ```typescript
1288
+ * const stats = client.getTelemetryStats();
1289
+ * console.log(`Total Requests: ${stats.requestCount}`);
1290
+ * console.log(`Success Rate: ${(stats.successRate * 100).toFixed(2)}%`);
1291
+ * ```
1292
+ */
1293
+ getTelemetryStats() {
1294
+ return this.http.getTelemetryStats();
1295
+ }
1296
+ /**
1297
+ * Reset telemetry statistics
1298
+ */
1299
+ resetTelemetryStats() {
1300
+ this.http.resetTelemetryStats();
1301
+ }
1302
+ /**
1303
+ * Get the API key type (secret or publishable)
1304
+ *
1305
+ * @returns 'secret' for mf_sk_* keys, 'publishable' for mf_pk_* keys
1306
+ *
1307
+ * @example
1308
+ * ```typescript
1309
+ * if (client.getKeyType() === 'secret') {
1310
+ * const session = await client.portalSessions.create({ ... });
1311
+ * }
1312
+ * ```
1313
+ */
1314
+ getKeyType() {
1315
+ return this._keyType;
1316
+ }
1317
+ /**
1318
+ * Check if the API key is a secret key (mf_sk_*)
1319
+ *
1320
+ * @returns true if the key can perform server-side operations
1321
+ */
1322
+ isSecretKey() {
1323
+ return this._keyType === "secret";
1324
+ }
1325
+ /**
1326
+ * Check if the API key is a publishable key (mf_pk_*)
1327
+ *
1328
+ * @returns true if the key is safe for frontend use
1329
+ */
1330
+ isPublishableKey() {
1331
+ return this._keyType === "publishable";
1332
+ }
1333
+ /**
1334
+ * Get a masked version of the API key for debugging
1335
+ *
1336
+ * @returns Masked API key showing only prefix and last 4 characters
1337
+ */
1338
+ getMaskedApiKey() {
1339
+ if (this._apiKey.length <= 20) {
1340
+ return this._apiKey.substring(0, 8) + "...";
1341
+ }
1342
+ return this._apiKey.substring(0, 16) + "..." + this._apiKey.slice(-4);
1343
+ }
1344
+ };
1345
+ // Annotate the CommonJS export names for ESM import in node:
1346
+ 0 && (module.exports = {
1347
+ ApiError,
1348
+ AuthenticationError,
1349
+ AuthorizationError,
1350
+ ConflictError,
1351
+ InitializationError,
1352
+ InternalError,
1353
+ MarginFrontClient,
1354
+ MarginFrontError,
1355
+ NetworkError,
1356
+ NotFoundError,
1357
+ RateLimitError,
1358
+ TimeoutError,
1359
+ ValidationError,
1360
+ createErrorFromResponse,
1361
+ isPublishableKey,
1362
+ isSecretKey,
1363
+ parseApiError,
1364
+ parseApiKey
1365
+ });