@licensechain/javascript-sdk 1.0.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.
@@ -0,0 +1,1484 @@
1
+ import crypto, { createHash } from 'crypto';
2
+ import * as jose from 'jose';
3
+
4
+ class Configuration {
5
+ constructor(options) {
6
+ this.apiKey = options.apiKey;
7
+ this.baseUrl = options.baseUrl || 'https://api.licensechain.app/v1';
8
+ this.timeout = options.timeout || 30000; // 30 seconds
9
+ this.retries = options.retries || 3;
10
+ }
11
+ getApiKey() {
12
+ return this.apiKey;
13
+ }
14
+ getBaseUrl() {
15
+ return this.baseUrl;
16
+ }
17
+ getTimeout() {
18
+ return this.timeout;
19
+ }
20
+ getRetries() {
21
+ return this.retries;
22
+ }
23
+ isValid() {
24
+ return !!this.apiKey && this.apiKey.length > 0;
25
+ }
26
+ setApiKey(apiKey) {
27
+ this.apiKey = apiKey;
28
+ return this;
29
+ }
30
+ setBaseUrl(baseUrl) {
31
+ this.baseUrl = baseUrl;
32
+ return this;
33
+ }
34
+ setTimeout(timeout) {
35
+ this.timeout = timeout;
36
+ return this;
37
+ }
38
+ setRetries(retries) {
39
+ this.retries = retries;
40
+ return this;
41
+ }
42
+ toObject() {
43
+ return {
44
+ apiKey: this.apiKey,
45
+ baseUrl: this.baseUrl,
46
+ timeout: this.timeout,
47
+ retries: this.retries
48
+ };
49
+ }
50
+ static fromObject(config) {
51
+ return new Configuration(config);
52
+ }
53
+ static fromEnvironment() {
54
+ return new Configuration({
55
+ apiKey: process.env.LICENSECHAIN_API_KEY || '',
56
+ baseUrl: process.env.LICENSECHAIN_BASE_URL || 'https://api.licensechain.app/v1',
57
+ timeout: parseInt(process.env.LICENSECHAIN_TIMEOUT || '30000'),
58
+ retries: parseInt(process.env.LICENSECHAIN_RETRIES || '3')
59
+ });
60
+ }
61
+ }
62
+
63
+ function validateEmail(email) {
64
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
65
+ return emailRegex.test(email);
66
+ }
67
+ function validateLicenseKey(licenseKey) {
68
+ return licenseKey.length === 32 && /^[A-Z0-9]+$/.test(licenseKey);
69
+ }
70
+ function validateUuid(uuid) {
71
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
72
+ return uuidRegex.test(uuid);
73
+ }
74
+ function validateAmount(amount) {
75
+ return typeof amount === 'number' && amount > 0 && isFinite(amount);
76
+ }
77
+ function validateCurrency(currency) {
78
+ const validCurrencies = ['USD', 'EUR', 'GBP', 'CAD', 'AUD', 'JPY', 'CHF', 'CNY'];
79
+ return validCurrencies.includes(currency.toUpperCase());
80
+ }
81
+ function validateStatus(status, allowedStatuses) {
82
+ return allowedStatuses.includes(status);
83
+ }
84
+ function sanitizeInput(input) {
85
+ if (typeof input !== 'string') {
86
+ return String(input);
87
+ }
88
+ return input
89
+ .replace(/&/g, '&')
90
+ .replace(/</g, '&lt;')
91
+ .replace(/>/g, '&gt;')
92
+ .replace(/"/g, '&quot;')
93
+ .replace(/'/g, '&#x27;');
94
+ }
95
+ function sanitizeMetadata(metadata) {
96
+ if (typeof metadata !== 'object' || metadata === null) {
97
+ return metadata;
98
+ }
99
+ const sanitized = {};
100
+ for (const [key, value] of Object.entries(metadata)) {
101
+ if (typeof value === 'string') {
102
+ sanitized[key] = sanitizeInput(value);
103
+ }
104
+ else if (Array.isArray(value)) {
105
+ sanitized[key] = value.map(item => typeof item === 'string' ? sanitizeInput(item) : item);
106
+ }
107
+ else if (typeof value === 'object' && value !== null) {
108
+ sanitized[key] = sanitizeMetadata(value);
109
+ }
110
+ else {
111
+ sanitized[key] = value;
112
+ }
113
+ }
114
+ return sanitized;
115
+ }
116
+ function generateLicenseKey() {
117
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
118
+ let result = '';
119
+ for (let i = 0; i < 32; i++) {
120
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
121
+ }
122
+ return result;
123
+ }
124
+ function generateUuid() {
125
+ return crypto.randomUUID();
126
+ }
127
+ function formatTimestamp(timestamp) {
128
+ return new Date(timestamp * 1000).toISOString();
129
+ }
130
+ function parseTimestamp(timestamp) {
131
+ return Math.floor(new Date(timestamp).getTime() / 1000);
132
+ }
133
+ function validatePagination(page, limit) {
134
+ const validPage = Math.max(page || 1, 1);
135
+ const validLimit = Math.min(Math.max(limit || 10, 1), 100);
136
+ return [validPage, validLimit];
137
+ }
138
+ function validateDateRange(startDate, endDate) {
139
+ const startTime = parseTimestamp(startDate);
140
+ const endTime = parseTimestamp(endDate);
141
+ if (startTime > endTime) {
142
+ throw new Error('Start date must be before or equal to end date');
143
+ }
144
+ }
145
+ function createWebhookSignature(payload, secret) {
146
+ return crypto.createHmac('sha256', secret).update(payload).digest('hex');
147
+ }
148
+ function verifyWebhookSignature(payload, signature, secret) {
149
+ const expectedSignature = createWebhookSignature(payload, secret);
150
+ return crypto.timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(expectedSignature, 'hex'));
151
+ }
152
+ function retryWithBackoff(fn, maxRetries = 3, initialDelay = 1000) {
153
+ return new Promise((resolve, reject) => {
154
+ let attempts = 0;
155
+ const attempt = async () => {
156
+ try {
157
+ const result = await fn();
158
+ resolve(result);
159
+ }
160
+ catch (error) {
161
+ attempts++;
162
+ if (attempts >= maxRetries) {
163
+ reject(error);
164
+ }
165
+ else {
166
+ setTimeout(attempt, initialDelay * Math.pow(2, attempts - 1));
167
+ }
168
+ }
169
+ };
170
+ attempt();
171
+ });
172
+ }
173
+ function formatBytes(bytes) {
174
+ const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
175
+ let size = bytes;
176
+ let unitIndex = 0;
177
+ while (size >= 1024 && unitIndex < units.length - 1) {
178
+ size /= 1024;
179
+ unitIndex++;
180
+ }
181
+ return `${size.toFixed(1)} ${units[unitIndex]}`;
182
+ }
183
+ function formatDuration(seconds) {
184
+ if (seconds < 60) {
185
+ return `${Math.floor(seconds)}s`;
186
+ }
187
+ else if (seconds < 3600) {
188
+ const minutes = Math.floor(seconds / 60);
189
+ const remainingSeconds = Math.floor(seconds % 60);
190
+ return `${minutes}m ${remainingSeconds}s`;
191
+ }
192
+ else if (seconds < 86400) {
193
+ const hours = Math.floor(seconds / 3600);
194
+ const minutes = Math.floor((seconds % 3600) / 60);
195
+ return `${hours}h ${minutes}m`;
196
+ }
197
+ else {
198
+ const days = Math.floor(seconds / 86400);
199
+ const hours = Math.floor((seconds % 86400) / 3600);
200
+ return `${days}d ${hours}h`;
201
+ }
202
+ }
203
+ function capitalizeFirst(text) {
204
+ if (!text)
205
+ return text;
206
+ return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase();
207
+ }
208
+ function toSnakeCase(text) {
209
+ return text.replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, '');
210
+ }
211
+ function toPascalCase(text) {
212
+ return text.split('_').map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join('');
213
+ }
214
+ function truncateString(text, maxLength) {
215
+ if (text.length <= maxLength) {
216
+ return text;
217
+ }
218
+ return text.slice(0, maxLength - 3) + '...';
219
+ }
220
+ function removeSpecialChars(text) {
221
+ return text.replace(/[^a-zA-Z0-9\s]/g, '');
222
+ }
223
+ function slugify(text) {
224
+ return text
225
+ .toLowerCase()
226
+ .replace(/[^a-z0-9\s-]/g, '')
227
+ .replace(/\s+/g, '-')
228
+ .replace(/-+/g, '-')
229
+ .replace(/^-|-$/g, '');
230
+ }
231
+ function validateNotEmpty(value, fieldName) {
232
+ if (!value || !value.trim()) {
233
+ throw new Error(`${fieldName} cannot be empty`);
234
+ }
235
+ }
236
+ function validatePositive(value, fieldName) {
237
+ if (value <= 0) {
238
+ throw new Error(`${fieldName} must be positive`);
239
+ }
240
+ }
241
+ function validateRange(value, min, max, fieldName) {
242
+ if (value < min || value > max) {
243
+ throw new Error(`${fieldName} must be between ${min} and ${max}`);
244
+ }
245
+ }
246
+ function jsonSerialize(obj) {
247
+ return JSON.stringify(obj, null, 2);
248
+ }
249
+ function jsonDeserialize(jsonString) {
250
+ return JSON.parse(jsonString);
251
+ }
252
+ function deepMerge(target, source) {
253
+ const result = { ...target };
254
+ for (const [key, value] of Object.entries(source)) {
255
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
256
+ result[key] = deepMerge(result[key] || {}, value);
257
+ }
258
+ else {
259
+ result[key] = value;
260
+ }
261
+ }
262
+ return result;
263
+ }
264
+ function chunkArray(array, chunkSize) {
265
+ const chunks = [];
266
+ for (let i = 0; i < array.length; i += chunkSize) {
267
+ chunks.push(array.slice(i, i + chunkSize));
268
+ }
269
+ return chunks;
270
+ }
271
+ function flattenObject(obj, separator = '.') {
272
+ const result = {};
273
+ for (const [key, value] of Object.entries(obj)) {
274
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
275
+ const flattened = flattenObject(value, separator);
276
+ for (const [subKey, subValue] of Object.entries(flattened)) {
277
+ result[`${key}${separator}${subKey}`] = subValue;
278
+ }
279
+ }
280
+ else {
281
+ result[key] = value;
282
+ }
283
+ }
284
+ return result;
285
+ }
286
+ function unflattenObject(obj, separator = '.') {
287
+ const result = {};
288
+ for (const [key, value] of Object.entries(obj)) {
289
+ const keys = key.split(separator);
290
+ let current = result;
291
+ for (let i = 0; i < keys.length - 1; i++) {
292
+ if (!current[keys[i]]) {
293
+ current[keys[i]] = {};
294
+ }
295
+ current = current[keys[i]];
296
+ }
297
+ current[keys[keys.length - 1]] = value;
298
+ }
299
+ return result;
300
+ }
301
+ function generateRandomString(length, characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') {
302
+ let result = '';
303
+ for (let i = 0; i < length; i++) {
304
+ result += characters.charAt(Math.floor(Math.random() * characters.length));
305
+ }
306
+ return result;
307
+ }
308
+ function generateRandomBytes(length) {
309
+ return crypto.randomBytes(length);
310
+ }
311
+ function sha256(data) {
312
+ return crypto.createHash('sha256').update(data).digest('hex');
313
+ }
314
+ function sha1(data) {
315
+ return crypto.createHash('sha1').update(data).digest('hex');
316
+ }
317
+ function md5(data) {
318
+ return crypto.createHash('md5').update(data).digest('hex');
319
+ }
320
+ function base64Encode(data) {
321
+ return Buffer.from(data).toString('base64');
322
+ }
323
+ function base64Decode(data) {
324
+ return Buffer.from(data, 'base64').toString('utf8');
325
+ }
326
+ function urlEncode(data) {
327
+ return encodeURIComponent(data);
328
+ }
329
+ function urlDecode(data) {
330
+ return decodeURIComponent(data);
331
+ }
332
+ function isValidJson(jsonString) {
333
+ try {
334
+ JSON.parse(jsonString);
335
+ return true;
336
+ }
337
+ catch {
338
+ return false;
339
+ }
340
+ }
341
+ function isValidUrl(url) {
342
+ try {
343
+ new URL(url);
344
+ return true;
345
+ }
346
+ catch {
347
+ return false;
348
+ }
349
+ }
350
+ function isValidIp(ip) {
351
+ const ipRegex = /^(\d{1,3}\.){3}\d{1,3}$/;
352
+ return ipRegex.test(ip);
353
+ }
354
+ function isValidEmail(email) {
355
+ return validateEmail(email);
356
+ }
357
+ function getCurrentTimestamp() {
358
+ return Math.floor(Date.now() / 1000);
359
+ }
360
+ function getCurrentDate() {
361
+ return new Date().toISOString();
362
+ }
363
+ function getCurrentDateFormatted(format) {
364
+ const date = new Date();
365
+ const year = date.getFullYear();
366
+ const month = String(date.getMonth() + 1).padStart(2, '0');
367
+ const day = String(date.getDate()).padStart(2, '0');
368
+ const hours = String(date.getHours()).padStart(2, '0');
369
+ const minutes = String(date.getMinutes()).padStart(2, '0');
370
+ const seconds = String(date.getSeconds()).padStart(2, '0');
371
+ return format
372
+ .replace('YYYY', year.toString())
373
+ .replace('MM', month)
374
+ .replace('DD', day)
375
+ .replace('HH', hours)
376
+ .replace('mm', minutes)
377
+ .replace('ss', seconds);
378
+ }
379
+ function sleep(ms) {
380
+ return new Promise(resolve => setTimeout(resolve, ms));
381
+ }
382
+ function debounce(func, wait) {
383
+ let timeout;
384
+ return (...args) => {
385
+ clearTimeout(timeout);
386
+ timeout = setTimeout(() => func(...args), wait);
387
+ };
388
+ }
389
+ function throttle(func, limit) {
390
+ let inThrottle;
391
+ return (...args) => {
392
+ if (!inThrottle) {
393
+ func(...args);
394
+ inThrottle = true;
395
+ setTimeout(() => (inThrottle = false), limit);
396
+ }
397
+ };
398
+ }
399
+
400
+ /**
401
+ * LicenseChain JavaScript SDK Exceptions
402
+ *
403
+ * Custom exceptions for the LicenseChain JavaScript SDK.
404
+ */
405
+ class LicenseChainException extends Error {
406
+ constructor(message = 'LicenseChain error occurred', code, statusCode, details) {
407
+ super(message);
408
+ this.name = 'LicenseChainException';
409
+ this.code = code;
410
+ this.statusCode = statusCode;
411
+ this.details = details;
412
+ // Maintains proper stack trace for where our error was thrown
413
+ if (Error.captureStackTrace) {
414
+ Error.captureStackTrace(this, LicenseChainException);
415
+ }
416
+ }
417
+ }
418
+ class AuthenticationError extends LicenseChainException {
419
+ constructor(message = 'Authentication failed', code, statusCode, details) {
420
+ super(message, code, statusCode, details);
421
+ this.name = 'AuthenticationError';
422
+ }
423
+ }
424
+ class ValidationError extends LicenseChainException {
425
+ constructor(message = 'Validation failed', code, statusCode, details) {
426
+ super(message, code, statusCode, details);
427
+ this.name = 'ValidationError';
428
+ }
429
+ }
430
+ class NotFoundError extends LicenseChainException {
431
+ constructor(message = 'Resource not found', code, statusCode, details) {
432
+ super(message, code, statusCode, details);
433
+ this.name = 'NotFoundError';
434
+ }
435
+ }
436
+ class RateLimitError extends LicenseChainException {
437
+ constructor(message = 'Rate limit exceeded', code, statusCode, details, retryAfter, limit, remaining, reset) {
438
+ super(message, code, statusCode, details);
439
+ this.name = 'RateLimitError';
440
+ this.retryAfter = retryAfter;
441
+ this.limit = limit;
442
+ this.remaining = remaining;
443
+ this.reset = reset;
444
+ }
445
+ }
446
+ class ServerError extends LicenseChainException {
447
+ constructor(message = 'Server error', code, statusCode, details) {
448
+ super(message, code, statusCode, details);
449
+ this.name = 'ServerError';
450
+ }
451
+ }
452
+ class NetworkError extends LicenseChainException {
453
+ constructor(message = 'Network error', code, statusCode, details) {
454
+ super(message, code, statusCode, details);
455
+ this.name = 'NetworkError';
456
+ }
457
+ }
458
+ class WebhookVerificationError extends LicenseChainException {
459
+ constructor(message = 'Webhook verification failed', code, statusCode, details) {
460
+ super(message, code, statusCode, details);
461
+ this.name = 'WebhookVerificationError';
462
+ }
463
+ }
464
+ class LicenseValidationError extends LicenseChainException {
465
+ constructor(message = 'License validation failed', code, statusCode, details) {
466
+ super(message, code, statusCode, details);
467
+ this.name = 'LicenseValidationError';
468
+ }
469
+ }
470
+ class ConfigurationError extends LicenseChainException {
471
+ constructor(message = 'Configuration error', code, statusCode, details) {
472
+ super(message, code, statusCode, details);
473
+ this.name = 'ConfigurationError';
474
+ }
475
+ }
476
+ class TimeoutError extends LicenseChainException {
477
+ constructor(message = 'Request timeout', code, statusCode, details) {
478
+ super(message, code, statusCode, details);
479
+ this.name = 'TimeoutError';
480
+ }
481
+ }
482
+ // Backward-compatible aliases used by existing SDK modules.
483
+ const NetworkException = NetworkError;
484
+ const ServerException = ServerError;
485
+ const ValidationException = ValidationError;
486
+ const AuthenticationException = AuthenticationError;
487
+ const NotFoundException = NotFoundError;
488
+ const RateLimitException = RateLimitError;
489
+ const ConfigurationException = ConfigurationError;
490
+ /**
491
+ * Error factory for creating appropriate exceptions based on HTTP status codes
492
+ */
493
+ function createErrorFromResponse(statusCode, message, code, details) {
494
+ switch (statusCode) {
495
+ case 400:
496
+ return new ValidationError(message, code, statusCode, details);
497
+ case 401:
498
+ case 403:
499
+ return new AuthenticationError(message, code, statusCode, details);
500
+ case 404:
501
+ return new NotFoundError(message, code, statusCode, details);
502
+ case 429:
503
+ return new RateLimitError(message, code, statusCode, details);
504
+ case 408:
505
+ return new TimeoutError(message, code, statusCode, details);
506
+ case 500:
507
+ case 502:
508
+ case 503:
509
+ case 504:
510
+ return new ServerError(message, code, statusCode, details);
511
+ default:
512
+ if (statusCode >= 400 && statusCode < 500) {
513
+ return new ValidationError(message, code, statusCode, details);
514
+ }
515
+ else if (statusCode >= 500) {
516
+ return new ServerError(message, code, statusCode, details);
517
+ }
518
+ else {
519
+ return new LicenseChainException(message, code, statusCode, details);
520
+ }
521
+ }
522
+ }
523
+ /**
524
+ * Check if an error is a LicenseChain exception
525
+ */
526
+ function isLicenseChainError(error) {
527
+ return error instanceof LicenseChainException;
528
+ }
529
+ /**
530
+ * Check if an error is a retryable error
531
+ */
532
+ function isRetryableError(error) {
533
+ if (!isLicenseChainError(error)) {
534
+ return false;
535
+ }
536
+ // Retry on network errors, server errors, and rate limits
537
+ return (error instanceof NetworkError ||
538
+ error instanceof ServerError ||
539
+ error instanceof RateLimitError ||
540
+ error instanceof TimeoutError);
541
+ }
542
+
543
+ class LicenseChainHttpCore {
544
+ constructor(runtime) {
545
+ this.runtime = runtime;
546
+ this.baseUrl = runtime.baseUrl.replace(/\/$/, '');
547
+ }
548
+ async get(endpoint, params) {
549
+ return this.makeRequest('GET', endpoint, undefined, params);
550
+ }
551
+ async post(endpoint, data) {
552
+ return this.makeRequest('POST', endpoint, data);
553
+ }
554
+ async put(endpoint, data) {
555
+ return this.makeRequest('PUT', endpoint, data);
556
+ }
557
+ async patch(endpoint, data) {
558
+ return this.makeRequest('PATCH', endpoint, data);
559
+ }
560
+ async delete(endpoint, data) {
561
+ return this.makeRequest('DELETE', endpoint, data);
562
+ }
563
+ async makeRequest(method, endpoint, data, params) {
564
+ const url = this.buildUrl(endpoint, params);
565
+ const requestOptions = this.buildRequestOptions(method, data);
566
+ return this.runtime.retryWithBackoff(async () => {
567
+ return this.sendRequest(url, requestOptions);
568
+ }, this.runtime.retries);
569
+ }
570
+ buildUrl(endpoint, params) {
571
+ const baseHasV1 = this.baseUrl.endsWith('/v1');
572
+ const normalizedEndpoint = endpoint.startsWith('/v1/')
573
+ ? (baseHasV1 ? endpoint.slice(3) : endpoint)
574
+ : endpoint.startsWith('/')
575
+ ? (baseHasV1 ? endpoint : `/v1${endpoint}`)
576
+ : (baseHasV1 ? `/${endpoint}` : `/v1/${endpoint}`);
577
+ let url = `${this.baseUrl}${normalizedEndpoint}`;
578
+ if (params && Object.keys(params).length > 0) {
579
+ const searchParams = new URLSearchParams();
580
+ for (const [key, value] of Object.entries(params)) {
581
+ if (value !== undefined && value !== null) {
582
+ searchParams.append(key, String(value));
583
+ }
584
+ }
585
+ url += `?${searchParams.toString()}`;
586
+ }
587
+ return url;
588
+ }
589
+ buildRequestOptions(method, data) {
590
+ const options = {
591
+ method,
592
+ headers: {
593
+ Authorization: `Bearer ${this.runtime.apiKey}`,
594
+ 'Content-Type': 'application/json',
595
+ 'User-Agent': this.runtime.userAgent,
596
+ 'X-API-Version': '1.0',
597
+ 'X-Platform': this.runtime.platform,
598
+ },
599
+ };
600
+ if (data !== undefined) {
601
+ options.body = this.runtime.serialize(data);
602
+ }
603
+ return options;
604
+ }
605
+ async sendRequest(url, options) {
606
+ try {
607
+ const response = await fetch(url, {
608
+ ...options,
609
+ signal: AbortSignal.timeout(this.runtime.timeout),
610
+ });
611
+ if (response.ok) {
612
+ const text = await response.text();
613
+ return text ? this.runtime.deserialize(text) : {};
614
+ }
615
+ const errorText = await response.text();
616
+ let errorMessage = 'Unknown error';
617
+ try {
618
+ const errorData = this.runtime.deserialize(errorText);
619
+ errorMessage = errorData.error || errorData.message || errorMessage;
620
+ }
621
+ catch {
622
+ errorMessage = errorText || errorMessage;
623
+ }
624
+ this.handleHttpError(response.status, errorMessage);
625
+ }
626
+ catch (error) {
627
+ if (error instanceof Error) {
628
+ if (error.name === 'AbortError') {
629
+ throw this.runtime.exceptions.network('Request timeout');
630
+ }
631
+ throw this.runtime.exceptions.network(error.message);
632
+ }
633
+ throw error;
634
+ }
635
+ }
636
+ handleHttpError(statusCode, message) {
637
+ switch (statusCode) {
638
+ case 400:
639
+ throw this.runtime.exceptions.validation(`Bad Request: ${message}`);
640
+ case 401:
641
+ case 403:
642
+ throw this.runtime.exceptions.authentication(`Unauthorized: ${message}`);
643
+ case 404:
644
+ throw this.runtime.exceptions.notFound(`Not Found: ${message}`);
645
+ case 429:
646
+ throw this.runtime.exceptions.rateLimit(`Rate Limited: ${message}`);
647
+ case 500:
648
+ case 502:
649
+ case 503:
650
+ case 504:
651
+ throw this.runtime.exceptions.server(`Server Error: ${message}`);
652
+ default:
653
+ throw this.runtime.exceptions.server(`Unexpected response: ${statusCode} ${message}`);
654
+ }
655
+ }
656
+ }
657
+
658
+ class ApiClient {
659
+ constructor(config) {
660
+ this.core = new LicenseChainHttpCore({
661
+ apiKey: config.getApiKey(),
662
+ baseUrl: config.getBaseUrl(),
663
+ deserialize: jsonDeserialize,
664
+ exceptions: {
665
+ authentication: (message) => new AuthenticationError(message),
666
+ network: (message) => new NetworkError(message),
667
+ notFound: (message) => new NotFoundError(message),
668
+ rateLimit: (message) => new RateLimitError(message),
669
+ server: (message) => new ServerError(message),
670
+ validation: (message) => new ValidationError(message),
671
+ },
672
+ platform: 'javascript-sdk',
673
+ retries: config.getRetries(),
674
+ retryWithBackoff,
675
+ serialize: jsonSerialize,
676
+ timeout: config.getTimeout(),
677
+ userAgent: 'LicenseChain-JavaScript-SDK/1.0.0',
678
+ });
679
+ }
680
+ async get(endpoint, params) {
681
+ return this.core.get(endpoint, params);
682
+ }
683
+ async post(endpoint, data) {
684
+ return this.core.post(endpoint, data);
685
+ }
686
+ async put(endpoint, data) {
687
+ return this.core.put(endpoint, data);
688
+ }
689
+ async patch(endpoint, data) {
690
+ return this.core.patch(endpoint, data);
691
+ }
692
+ async delete(endpoint, data) {
693
+ return this.core.delete(endpoint, data);
694
+ }
695
+ async ping() {
696
+ return this.get('/health');
697
+ }
698
+ async health() {
699
+ return this.get('/health');
700
+ }
701
+ }
702
+
703
+ class LicenseService {
704
+ constructor(client) {
705
+ this.client = client;
706
+ }
707
+ async create(request) {
708
+ const appId = request.app_id;
709
+ if (!appId) {
710
+ throw new ValidationException('app_id is required for API v1 license creation');
711
+ }
712
+ const data = {
713
+ appId,
714
+ plan: request.plan || 'FREE',
715
+ issuedEmail: request.issued_email,
716
+ expiresAt: request.expires_at
717
+ };
718
+ const response = await this.client.post(`/apps/${appId}/licenses`, data);
719
+ return this.extractLicenseResponse(response);
720
+ }
721
+ async get(licenseId) {
722
+ validateNotEmpty(licenseId, 'license_id');
723
+ const response = await this.client.get(`/licenses/${licenseId}`);
724
+ return this.extractLicenseResponse(response);
725
+ }
726
+ async update(licenseId, updates) {
727
+ validateNotEmpty(licenseId, 'license_id');
728
+ const response = await this.client.patch(`/licenses/${licenseId}`, sanitizeMetadata(updates));
729
+ return this.extractLicenseResponse(response);
730
+ }
731
+ async revoke(licenseId) {
732
+ validateNotEmpty(licenseId, 'license_id');
733
+ await this.client.delete(`/licenses/${licenseId}`);
734
+ }
735
+ async validate(licenseKey, hwuid) {
736
+ validateNotEmpty(licenseKey, 'license_key');
737
+ const body = { key: licenseKey };
738
+ body.hwuid = hwuid != null && String(hwuid).trim() !== '' ? hwuid.trim() : this.defaultHwuid();
739
+ const response = await this.client.post('/licenses/verify', body);
740
+ return response.valid || false;
741
+ }
742
+ /** Full POST /v1/licenses/verify JSON (optional license_token / license_jwks_uri). */
743
+ async verifyWithDetails(licenseKey, hwuid) {
744
+ validateNotEmpty(licenseKey, 'license_key');
745
+ const body = { key: licenseKey };
746
+ body.hwuid = hwuid != null && String(hwuid).trim() !== '' ? hwuid.trim() : this.defaultHwuid();
747
+ return this.client.post('/licenses/verify', body);
748
+ }
749
+ async listUserLicenses(userId, page, limit) {
750
+ validateNotEmpty(userId, 'user_id');
751
+ const [validPage, validLimit] = validatePagination(page, limit);
752
+ const response = await this.client.get('/licenses', {
753
+ page: validPage,
754
+ limit: validLimit
755
+ });
756
+ const allLicenses = response.licenses || response.data || [];
757
+ const filtered = allLicenses.filter((license) => license?.issuedEmail === userId ||
758
+ license?.email === userId ||
759
+ license?.user_id === userId);
760
+ return {
761
+ data: filtered,
762
+ total: filtered.length,
763
+ page: validPage,
764
+ limit: validLimit
765
+ };
766
+ }
767
+ async stats() {
768
+ const response = await this.client.get('/licenses/stats');
769
+ return response.data || response;
770
+ }
771
+ extractLicenseResponse(response) {
772
+ return response?.data ? response.data : response;
773
+ }
774
+ defaultHwuid() {
775
+ const g = globalThis;
776
+ if (g?.navigator) {
777
+ return createHash('sha256')
778
+ .update(`licensechain|js-browser|${g.navigator.userAgent}|${g.navigator.language}|${g.navigator.platform || 'unknown'}`)
779
+ .digest('hex');
780
+ }
781
+ if (typeof process !== 'undefined') {
782
+ return createHash('sha256')
783
+ .update(`licensechain|js-node|${process.platform}|${process.arch}|${process.version}`)
784
+ .digest('hex');
785
+ }
786
+ return createHash('sha256').update('licensechain|js|unknown|unknown|unknown').digest('hex');
787
+ }
788
+ }
789
+
790
+ class UserService {
791
+ constructor(client) {
792
+ this.client = client;
793
+ }
794
+ async create(request) {
795
+ this.validateEmail(request.email);
796
+ const data = {
797
+ email: request.email,
798
+ password: request.password || 'ChangeMe123!',
799
+ name: request.name,
800
+ metadata: sanitizeMetadata(request.metadata || {})
801
+ };
802
+ const response = await this.client.post('/auth/register', data);
803
+ return (response.user || response.data);
804
+ }
805
+ async get(userId) {
806
+ validateNotEmpty(userId, 'user_id');
807
+ const response = await this.client.get('/auth/me');
808
+ return (response.data || response);
809
+ }
810
+ async update(userId, updates) {
811
+ validateNotEmpty(userId, 'user_id');
812
+ throw new ValidationException('User update endpoint is not available in API v1');
813
+ }
814
+ async delete(userId) {
815
+ validateNotEmpty(userId, 'user_id');
816
+ throw new ValidationException('User delete endpoint is not available in API v1');
817
+ }
818
+ async list(page, limit) {
819
+ const [validPage, validLimit] = validatePagination(page, limit);
820
+ return {
821
+ data: [],
822
+ total: 0,
823
+ page: validPage,
824
+ limit: validLimit
825
+ };
826
+ }
827
+ async stats() {
828
+ return { total: 0, active: 0, inactive: 0 };
829
+ }
830
+ validateEmail(email) {
831
+ validateNotEmpty(email, 'email');
832
+ if (!validateEmail(email)) {
833
+ throw new ValidationException('Invalid email format');
834
+ }
835
+ }
836
+ }
837
+
838
+ class ProductService {
839
+ constructor(client) {
840
+ this.client = client;
841
+ }
842
+ async create(request) {
843
+ this.validateRequiredParams(request.name, request.price, request.currency);
844
+ throw new ValidationException('Product endpoints are not available in API v1');
845
+ }
846
+ async get(productId) {
847
+ validateNotEmpty(productId, 'product_id');
848
+ throw new ValidationException('Product endpoints are not available in API v1');
849
+ }
850
+ async update(productId, updates) {
851
+ validateNotEmpty(productId, 'product_id');
852
+ throw new ValidationException('Product endpoints are not available in API v1');
853
+ }
854
+ async delete(productId) {
855
+ validateNotEmpty(productId, 'product_id');
856
+ throw new ValidationException('Product endpoints are not available in API v1');
857
+ }
858
+ async list(page, limit) {
859
+ const [validPage, validLimit] = validatePagination(page, limit);
860
+ return {
861
+ data: [],
862
+ total: 0,
863
+ page: validPage,
864
+ limit: validLimit
865
+ };
866
+ }
867
+ async stats() {
868
+ return { total: 0, active: 0, revenue: 0 };
869
+ }
870
+ validateRequiredParams(name, price, currency) {
871
+ validateNotEmpty(name, 'name');
872
+ if (price !== undefined) {
873
+ validatePositive(price, 'price');
874
+ }
875
+ if (!validateCurrency(currency)) {
876
+ throw new ValidationException('Invalid currency');
877
+ }
878
+ }
879
+ }
880
+
881
+ class WebhookService {
882
+ constructor(client) {
883
+ this.client = client;
884
+ }
885
+ async create(request) {
886
+ this.validateWebhookParams(request.url, request.events);
887
+ const data = {
888
+ url: request.url,
889
+ events: request.events,
890
+ secret: request.secret
891
+ };
892
+ const response = await this.client.post('/webhooks', data);
893
+ return this.normalizeWebhookPayload((response.data || response));
894
+ }
895
+ async get(webhookId) {
896
+ this.validateUuid(webhookId, 'webhook_id');
897
+ const response = await this.client.get(`/webhooks/${webhookId}`);
898
+ return this.normalizeWebhookPayload((response.data || response));
899
+ }
900
+ async update(webhookId, updates) {
901
+ this.validateUuid(webhookId, 'webhook_id');
902
+ const response = await this.client.put(`/webhooks/${webhookId}`, sanitizeMetadata(updates));
903
+ return this.normalizeWebhookPayload((response.data || response));
904
+ }
905
+ async delete(webhookId) {
906
+ this.validateUuid(webhookId, 'webhook_id');
907
+ await this.client.delete(`/webhooks/${webhookId}`);
908
+ }
909
+ async list() {
910
+ const response = await this.client.get('/webhooks');
911
+ const data = (response?.data || []).map((webhook) => this.normalizeWebhookPayload(webhook));
912
+ return {
913
+ data,
914
+ total: response?.pagination?.total || data.length,
915
+ page: response?.pagination?.page || 1,
916
+ limit: response?.pagination?.limit || data.length
917
+ };
918
+ }
919
+ validateWebhookParams(url, events) {
920
+ validateNotEmpty(url, 'url');
921
+ if (!events || events.length === 0) {
922
+ throw new ValidationException('Events cannot be empty');
923
+ }
924
+ }
925
+ validateUuid(id, fieldName) {
926
+ validateNotEmpty(id, fieldName);
927
+ if (!validateUuid(id)) {
928
+ throw new ValidationException(`Invalid ${fieldName} format`);
929
+ }
930
+ }
931
+ normalizeWebhookPayload(payload) {
932
+ return {
933
+ id: payload?.id,
934
+ url: payload?.url || '',
935
+ events: payload?.events || [],
936
+ secret: payload?.secret,
937
+ created_at: payload?.created_at || payload?.createdAt,
938
+ updated_at: payload?.updated_at || payload?.updatedAt
939
+ };
940
+ }
941
+ }
942
+
943
+ class LicenseChainClient {
944
+ constructor(config) {
945
+ if (config instanceof Configuration) {
946
+ this.config = config;
947
+ }
948
+ else {
949
+ this.config = new Configuration(config);
950
+ }
951
+ if (!this.config.isValid()) {
952
+ throw new ConfigurationError('API key is required');
953
+ }
954
+ this.apiClient = new ApiClient(this.config);
955
+ this.licenses = new LicenseService(this.apiClient);
956
+ this.users = new UserService(this.apiClient);
957
+ this.products = new ProductService(this.apiClient);
958
+ this.webhooks = new WebhookService(this.apiClient);
959
+ }
960
+ getConfiguration() {
961
+ return this.config;
962
+ }
963
+ getLicenses() {
964
+ return this.licenses;
965
+ }
966
+ getUsers() {
967
+ return this.users;
968
+ }
969
+ getProducts() {
970
+ return this.products;
971
+ }
972
+ getWebhooks() {
973
+ return this.webhooks;
974
+ }
975
+ async validateLicense(licenseKey, hwuid) {
976
+ const resolvedHwuid = hwuid && hwuid.trim() !== '' ? hwuid.trim() : this.defaultHwuid();
977
+ const valid = await this.licenses.validate(licenseKey, resolvedHwuid);
978
+ return { valid };
979
+ }
980
+ async ping() {
981
+ return this.apiClient.ping();
982
+ }
983
+ async health() {
984
+ return this.apiClient.health();
985
+ }
986
+ static create(apiKey, baseUrl) {
987
+ return new LicenseChainClient({
988
+ apiKey,
989
+ baseUrl: baseUrl || 'https://api.licensechain.app/v1'
990
+ });
991
+ }
992
+ static fromEnvironment() {
993
+ return new LicenseChainClient(Configuration.fromEnvironment());
994
+ }
995
+ updateConfig(config) {
996
+ if (config.apiKey)
997
+ this.config.setApiKey(config.apiKey);
998
+ if (config.baseUrl)
999
+ this.config.setBaseUrl(config.baseUrl);
1000
+ if (typeof config.timeout === 'number')
1001
+ this.config.setTimeout(config.timeout);
1002
+ if (typeof config.retries === 'number')
1003
+ this.config.setRetries(config.retries);
1004
+ this.apiClient = new ApiClient(this.config);
1005
+ this.licenses = new LicenseService(this.apiClient);
1006
+ this.users = new UserService(this.apiClient);
1007
+ this.products = new ProductService(this.apiClient);
1008
+ this.webhooks = new WebhookService(this.apiClient);
1009
+ }
1010
+ defaultHwuid() {
1011
+ const g = globalThis;
1012
+ if (g?.navigator) {
1013
+ return createHash('sha256')
1014
+ .update(`licensechain|js-browser|${g.navigator.userAgent}|${g.navigator.language}|${g.navigator.platform || 'unknown'}`)
1015
+ .digest('hex');
1016
+ }
1017
+ if (typeof process !== 'undefined') {
1018
+ return createHash('sha256')
1019
+ .update(`licensechain|js-node|${process.platform}|${process.arch}|${process.version}`)
1020
+ .digest('hex');
1021
+ }
1022
+ return createHash('sha256').update('licensechain|js|unknown|unknown|unknown').digest('hex');
1023
+ }
1024
+ }
1025
+
1026
+ class WebhookHandler {
1027
+ constructor(secret, tolerance = 300) {
1028
+ this.secret = secret;
1029
+ this.tolerance = tolerance; // seconds
1030
+ }
1031
+ verifySignature(payload, signature) {
1032
+ return verifyWebhookSignature(payload, signature, this.secret);
1033
+ }
1034
+ verifyTimestamp(timestamp) {
1035
+ try {
1036
+ const webhookTime = new Date(timestamp);
1037
+ const currentTime = new Date();
1038
+ const timeDiff = Math.abs((currentTime.getTime() - webhookTime.getTime()) / 1000);
1039
+ if (timeDiff > this.tolerance) {
1040
+ throw new ValidationException(`Webhook timestamp too old: ${timeDiff} seconds`);
1041
+ }
1042
+ }
1043
+ catch (error) {
1044
+ throw new ValidationException(`Invalid timestamp format: ${error}`);
1045
+ }
1046
+ }
1047
+ verifyWebhook(payload, signature, timestamp) {
1048
+ this.verifyTimestamp(timestamp);
1049
+ if (!this.verifySignature(payload, signature)) {
1050
+ throw new AuthenticationException('Invalid webhook signature');
1051
+ }
1052
+ }
1053
+ processEvent(eventData) {
1054
+ const payload = jsonSerialize(eventData.data || {});
1055
+ this.verifyWebhook(payload, eventData.signature, eventData.timestamp);
1056
+ const eventType = eventData.type || '';
1057
+ switch (eventType) {
1058
+ case 'license.created':
1059
+ this.handleLicenseCreated(eventData);
1060
+ break;
1061
+ case 'license.updated':
1062
+ this.handleLicenseUpdated(eventData);
1063
+ break;
1064
+ case 'license.revoked':
1065
+ this.handleLicenseRevoked(eventData);
1066
+ break;
1067
+ case 'license.expired':
1068
+ this.handleLicenseExpired(eventData);
1069
+ break;
1070
+ case 'user.created':
1071
+ this.handleUserCreated(eventData);
1072
+ break;
1073
+ case 'user.updated':
1074
+ this.handleUserUpdated(eventData);
1075
+ break;
1076
+ case 'user.deleted':
1077
+ this.handleUserDeleted(eventData);
1078
+ break;
1079
+ case 'product.created':
1080
+ this.handleProductCreated(eventData);
1081
+ break;
1082
+ case 'product.updated':
1083
+ this.handleProductUpdated(eventData);
1084
+ break;
1085
+ case 'product.deleted':
1086
+ this.handleProductDeleted(eventData);
1087
+ break;
1088
+ case 'payment.completed':
1089
+ this.handlePaymentCompleted(eventData);
1090
+ break;
1091
+ case 'payment.failed':
1092
+ this.handlePaymentFailed(eventData);
1093
+ break;
1094
+ case 'payment.refunded':
1095
+ this.handlePaymentRefunded(eventData);
1096
+ break;
1097
+ default:
1098
+ console.log(`Unknown webhook event type: ${eventType}`);
1099
+ }
1100
+ }
1101
+ handleLicenseCreated(eventData) {
1102
+ console.log(`License created: ${eventData.id}`);
1103
+ // Add custom logic for license created event
1104
+ }
1105
+ handleLicenseUpdated(eventData) {
1106
+ console.log(`License updated: ${eventData.id}`);
1107
+ // Add custom logic for license updated event
1108
+ }
1109
+ handleLicenseRevoked(eventData) {
1110
+ console.log(`License revoked: ${eventData.id}`);
1111
+ // Add custom logic for license revoked event
1112
+ }
1113
+ handleLicenseExpired(eventData) {
1114
+ console.log(`License expired: ${eventData.id}`);
1115
+ // Add custom logic for license expired event
1116
+ }
1117
+ handleUserCreated(eventData) {
1118
+ console.log(`User created: ${eventData.id}`);
1119
+ // Add custom logic for user created event
1120
+ }
1121
+ handleUserUpdated(eventData) {
1122
+ console.log(`User updated: ${eventData.id}`);
1123
+ // Add custom logic for user updated event
1124
+ }
1125
+ handleUserDeleted(eventData) {
1126
+ console.log(`User deleted: ${eventData.id}`);
1127
+ // Add custom logic for user deleted event
1128
+ }
1129
+ handleProductCreated(eventData) {
1130
+ console.log(`Product created: ${eventData.id}`);
1131
+ // Add custom logic for product created event
1132
+ }
1133
+ handleProductUpdated(eventData) {
1134
+ console.log(`Product updated: ${eventData.id}`);
1135
+ // Add custom logic for product updated event
1136
+ }
1137
+ handleProductDeleted(eventData) {
1138
+ console.log(`Product deleted: ${eventData.id}`);
1139
+ // Add custom logic for product deleted event
1140
+ }
1141
+ handlePaymentCompleted(eventData) {
1142
+ console.log(`Payment completed: ${eventData.id}`);
1143
+ // Add custom logic for payment completed event
1144
+ }
1145
+ handlePaymentFailed(eventData) {
1146
+ console.log(`Payment failed: ${eventData.id}`);
1147
+ // Add custom logic for payment failed event
1148
+ }
1149
+ handlePaymentRefunded(eventData) {
1150
+ console.log(`Payment refunded: ${eventData.id}`);
1151
+ // Add custom logic for payment refunded event
1152
+ }
1153
+ }
1154
+ class WebhookEvents {
1155
+ }
1156
+ WebhookEvents.LICENSE_CREATED = 'license.created';
1157
+ WebhookEvents.LICENSE_UPDATED = 'license.updated';
1158
+ WebhookEvents.LICENSE_REVOKED = 'license.revoked';
1159
+ WebhookEvents.LICENSE_EXPIRED = 'license.expired';
1160
+ WebhookEvents.USER_CREATED = 'user.created';
1161
+ WebhookEvents.USER_UPDATED = 'user.updated';
1162
+ WebhookEvents.USER_DELETED = 'user.deleted';
1163
+ WebhookEvents.PRODUCT_CREATED = 'product.created';
1164
+ WebhookEvents.PRODUCT_UPDATED = 'product.updated';
1165
+ WebhookEvents.PRODUCT_DELETED = 'product.deleted';
1166
+ WebhookEvents.PAYMENT_COMPLETED = 'payment.completed';
1167
+ WebhookEvents.PAYMENT_FAILED = 'payment.failed';
1168
+ WebhookEvents.PAYMENT_REFUNDED = 'payment.refunded';
1169
+ function createOutgoingWebhookSignature(payload, secret) {
1170
+ const crypto = require('crypto');
1171
+ return crypto.createHmac('sha256', secret).update(payload).digest('hex');
1172
+ }
1173
+ function verifyIncomingWebhookSignature(payload, signature, secret) {
1174
+ return verifyWebhookSignature(payload, signature, secret);
1175
+ }
1176
+
1177
+ /**
1178
+ * LicenseChain License Validator
1179
+ *
1180
+ * Easy-to-use license validation functionality.
1181
+ */
1182
+ class LicenseValidator {
1183
+ constructor(config) {
1184
+ this.client = new LicenseChainClient(config);
1185
+ }
1186
+ /**
1187
+ * Validate a license key
1188
+ */
1189
+ async validateLicense(licenseKey, appId, context) {
1190
+ try {
1191
+ const result = await this.client.validateLicense(licenseKey, appId);
1192
+ return result;
1193
+ }
1194
+ catch (error) {
1195
+ return {
1196
+ valid: false,
1197
+ error: error instanceof Error ? error.message : 'Unknown error',
1198
+ };
1199
+ }
1200
+ }
1201
+ /**
1202
+ * Check if license is valid (quick check)
1203
+ */
1204
+ async isValid(licenseKey, appId) {
1205
+ try {
1206
+ const result = await this.validateLicense(licenseKey, appId);
1207
+ return result.valid;
1208
+ }
1209
+ catch {
1210
+ return false;
1211
+ }
1212
+ }
1213
+ /**
1214
+ * Get license information
1215
+ */
1216
+ async getLicenseInfo(licenseKey, appId) {
1217
+ try {
1218
+ const result = await this.validateLicense(licenseKey, appId);
1219
+ return result.valid ? result : null;
1220
+ }
1221
+ catch {
1222
+ return null;
1223
+ }
1224
+ }
1225
+ /**
1226
+ * Check if license is expired
1227
+ */
1228
+ async isExpired(licenseKey, appId) {
1229
+ try {
1230
+ const result = await this.validateLicense(licenseKey, appId);
1231
+ if (!result.valid || !result.license) {
1232
+ return true;
1233
+ }
1234
+ const expiresAt = result.license.expiresAt;
1235
+ if (!expiresAt) {
1236
+ return false;
1237
+ }
1238
+ return new Date(expiresAt) < new Date();
1239
+ }
1240
+ catch {
1241
+ return true;
1242
+ }
1243
+ }
1244
+ /**
1245
+ * Get days until expiration
1246
+ */
1247
+ async getDaysUntilExpiration(licenseKey, appId) {
1248
+ try {
1249
+ const result = await this.validateLicense(licenseKey, appId);
1250
+ if (!result.valid || !result.license) {
1251
+ return null;
1252
+ }
1253
+ const expiresAt = result.license.expiresAt;
1254
+ if (!expiresAt) {
1255
+ return null;
1256
+ }
1257
+ const expDate = new Date(expiresAt);
1258
+ const now = new Date();
1259
+ const diffTime = expDate.getTime() - now.getTime();
1260
+ const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
1261
+ return Math.max(0, diffDays);
1262
+ }
1263
+ catch {
1264
+ return null;
1265
+ }
1266
+ }
1267
+ /**
1268
+ * Validate multiple licenses
1269
+ */
1270
+ async validateLicenses(licenseKeys, appId) {
1271
+ const promises = licenseKeys.map(key => this.validateLicense(key, appId));
1272
+ return Promise.all(promises);
1273
+ }
1274
+ /**
1275
+ * Validate with custom validation rules
1276
+ */
1277
+ async validateWithRules(licenseKey, rules, appId) {
1278
+ const result = await this.validateLicense(licenseKey, appId);
1279
+ if (!result.valid || !rules) {
1280
+ return result;
1281
+ }
1282
+ // Apply custom validation rules
1283
+ if (rules.maxUsage && result.license?.usageCount && result.license.usageCount > rules.maxUsage) {
1284
+ return {
1285
+ ...result,
1286
+ valid: false,
1287
+ error: 'Usage limit exceeded',
1288
+ };
1289
+ }
1290
+ if (rules.allowedFeatures && result.license?.features) {
1291
+ const invalidFeatures = result.license.features.filter((feature) => !rules.allowedFeatures.includes(feature));
1292
+ if (invalidFeatures.length > 0) {
1293
+ return {
1294
+ ...result,
1295
+ valid: false,
1296
+ error: `Invalid features: ${invalidFeatures.join(', ')}`,
1297
+ };
1298
+ }
1299
+ }
1300
+ if (rules.requiredFeatures && result.license?.features) {
1301
+ const missingFeatures = rules.requiredFeatures.filter(feature => !result.license.features.includes(feature));
1302
+ if (missingFeatures.length > 0) {
1303
+ return {
1304
+ ...result,
1305
+ valid: false,
1306
+ error: `Missing required features: ${missingFeatures.join(', ')}`,
1307
+ };
1308
+ }
1309
+ }
1310
+ if (rules.allowedDomains && result.license?.metadata?.domain) {
1311
+ const domain = result.license.metadata.domain;
1312
+ if (!rules.allowedDomains.includes(domain)) {
1313
+ return {
1314
+ ...result,
1315
+ valid: false,
1316
+ error: `Domain not allowed: ${domain}`,
1317
+ };
1318
+ }
1319
+ }
1320
+ if (rules.allowedIPs && result.license?.metadata?.ipAddress) {
1321
+ const ipAddress = result.license.metadata.ipAddress;
1322
+ if (!rules.allowedIPs.includes(ipAddress)) {
1323
+ return {
1324
+ ...result,
1325
+ valid: false,
1326
+ error: `IP address not allowed: ${ipAddress}`,
1327
+ };
1328
+ }
1329
+ }
1330
+ return result;
1331
+ }
1332
+ /**
1333
+ * Check if license has specific feature
1334
+ */
1335
+ async hasFeature(licenseKey, feature, appId) {
1336
+ try {
1337
+ const result = await this.validateLicense(licenseKey, appId);
1338
+ return result.valid && result.license?.features?.includes(feature) || false;
1339
+ }
1340
+ catch {
1341
+ return false;
1342
+ }
1343
+ }
1344
+ /**
1345
+ * Get license usage count
1346
+ */
1347
+ async getUsageCount(licenseKey, appId) {
1348
+ try {
1349
+ const result = await this.validateLicense(licenseKey, appId);
1350
+ return result.valid ? result.license?.usageCount || 0 : null;
1351
+ }
1352
+ catch {
1353
+ return null;
1354
+ }
1355
+ }
1356
+ /**
1357
+ * Get license metadata
1358
+ */
1359
+ async getMetadata(licenseKey, appId) {
1360
+ try {
1361
+ const result = await this.validateLicense(licenseKey, appId);
1362
+ return result.valid ? result.license?.metadata || {} : null;
1363
+ }
1364
+ catch {
1365
+ return null;
1366
+ }
1367
+ }
1368
+ /**
1369
+ * Get license features
1370
+ */
1371
+ async getFeatures(licenseKey, appId) {
1372
+ try {
1373
+ const result = await this.validateLicense(licenseKey, appId);
1374
+ return result.valid ? result.license?.features || [] : [];
1375
+ }
1376
+ catch {
1377
+ return [];
1378
+ }
1379
+ }
1380
+ /**
1381
+ * Get user information from license
1382
+ */
1383
+ async getUserInfo(licenseKey, appId) {
1384
+ try {
1385
+ const result = await this.validateLicense(licenseKey, appId);
1386
+ if (!result.valid) {
1387
+ return null;
1388
+ }
1389
+ return {
1390
+ email: result.user?.email,
1391
+ name: result.user?.name,
1392
+ id: result.user?.id,
1393
+ };
1394
+ }
1395
+ catch {
1396
+ return null;
1397
+ }
1398
+ }
1399
+ /**
1400
+ * Get application information from license
1401
+ */
1402
+ async getAppInfo(licenseKey, appId) {
1403
+ try {
1404
+ const result = await this.validateLicense(licenseKey, appId);
1405
+ if (!result.valid) {
1406
+ return null;
1407
+ }
1408
+ return {
1409
+ name: result.app?.name,
1410
+ id: result.app?.id,
1411
+ };
1412
+ }
1413
+ catch {
1414
+ return null;
1415
+ }
1416
+ }
1417
+ /**
1418
+ * Validate license with context
1419
+ */
1420
+ async validateWithContext(context) {
1421
+ const { licenseKey, appId, ...additionalContext } = context;
1422
+ try {
1423
+ const result = await this.validateLicense(licenseKey, appId);
1424
+ if (!result.valid) {
1425
+ return result;
1426
+ }
1427
+ // Add context validation logic here
1428
+ // For example, validate IP address, user agent, etc.
1429
+ return result;
1430
+ }
1431
+ catch (error) {
1432
+ return {
1433
+ valid: false,
1434
+ error: error instanceof Error ? error.message : 'Unknown error',
1435
+ };
1436
+ }
1437
+ }
1438
+ /**
1439
+ * Batch validate licenses with rules
1440
+ */
1441
+ async batchValidateWithRules(licenses) {
1442
+ const promises = licenses.map(({ licenseKey, appId, rules }) => rules
1443
+ ? this.validateWithRules(licenseKey, rules, appId)
1444
+ : this.validateLicense(licenseKey, appId));
1445
+ return Promise.all(promises);
1446
+ }
1447
+ /**
1448
+ * Get client instance for advanced operations
1449
+ */
1450
+ getClient() {
1451
+ return this.client;
1452
+ }
1453
+ /**
1454
+ * Update configuration
1455
+ */
1456
+ updateConfig(config) {
1457
+ this.client.updateConfig(config);
1458
+ }
1459
+ }
1460
+
1461
+ /** Must match Core API `LICENSE_TOKEN_USE_CLAIM`. */
1462
+ const LICENSE_TOKEN_USE_CLAIM = 'licensechain_license_v1';
1463
+ /**
1464
+ * Verify a `license_token` from POST /v1/licenses/verify using the JWKS URL
1465
+ * from the same response (`license_jwks_uri`) or `GET /v1/licenses/jwks`.
1466
+ * Same contract as `licensechain-node-sdk` — also exported from the package root (`dist/index.js`).
1467
+ */
1468
+ async function verifyLicenseAssertionJwt(token, jwksUrl, options) {
1469
+ const JWKS = jose.createRemoteJWKSet(new URL(String(jwksUrl).trim()));
1470
+ const { payload } = await jose.jwtVerify(String(token).trim(), JWKS, {
1471
+ algorithms: ['RS256'],
1472
+ ...(options?.issuer ? { issuer: options.issuer } : {}),
1473
+ });
1474
+ if (payload.token_use !== LICENSE_TOKEN_USE_CLAIM) {
1475
+ throw new Error(`Invalid license token: expected token_use "${LICENSE_TOKEN_USE_CLAIM}"`);
1476
+ }
1477
+ if (options?.expectedAppId != null && options.expectedAppId !== '' && payload.aud !== options.expectedAppId) {
1478
+ throw new Error('Invalid license token: aud does not match expected app id');
1479
+ }
1480
+ return payload;
1481
+ }
1482
+
1483
+ export { ApiClient, AuthenticationError, AuthenticationException, Configuration, ConfigurationError, ConfigurationException, LICENSE_TOKEN_USE_CLAIM, LicenseChainClient, LicenseChainException, LicenseService, LicenseValidationError, LicenseValidator, NetworkError, NetworkException, NotFoundError, NotFoundException, ProductService, RateLimitError, RateLimitException, ServerError, ServerException, TimeoutError, UserService, ValidationError, ValidationException, WebhookEvents, WebhookHandler, WebhookService, WebhookVerificationError, base64Decode, base64Encode, capitalizeFirst, chunkArray, createErrorFromResponse, createOutgoingWebhookSignature, createWebhookSignature, debounce, deepMerge, LicenseChainClient as default, flattenObject, formatBytes, formatDuration, formatTimestamp, generateLicenseKey, generateRandomBytes, generateRandomString, generateUuid, getCurrentDate, getCurrentDateFormatted, getCurrentTimestamp, isLicenseChainError, isRetryableError, isValidEmail, isValidIp, isValidJson, isValidUrl, jsonDeserialize, jsonSerialize, md5, parseTimestamp, removeSpecialChars, retryWithBackoff, sanitizeInput, sanitizeMetadata, sha1, sha256, sleep, slugify, throttle, toPascalCase, toSnakeCase, truncateString, unflattenObject, urlDecode, urlEncode, validateAmount, validateCurrency, validateDateRange, validateEmail, validateLicenseKey, validateNotEmpty, validatePagination, validatePositive, validateRange, validateStatus, validateUuid, verifyIncomingWebhookSignature, verifyLicenseAssertionJwt, verifyWebhookSignature };
1484
+ //# sourceMappingURL=index.esm.js.map