@nice2dev/licensing 1.0.10

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.mjs ADDED
@@ -0,0 +1,4353 @@
1
+ import { g as generateMachineId, a as generateFingerprint, v as validateKeyFormat$1, e as extractKeyMetadata, t as tierMeetsRequirement } from "./FeatureGate-Da7fCJA5.js";
2
+ import { L, c, b, d, f, h, i, j, k, l, m, n, o, p, q, r, s, u, w, x, y, z, A, B, C, D, E, F, G, H, I, J } from "./FeatureGate-Da7fCJA5.js";
3
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
4
+ import React, { useState, useCallback, useEffect, useMemo } from "react";
5
+ const HEARTBEAT_INTERVAL_MS = 5 * 60 * 1e3;
6
+ class SeatManager {
7
+ constructor(serverUrl, apiKey) {
8
+ this.serverUrl = serverUrl;
9
+ this.apiKey = apiKey;
10
+ }
11
+ /**
12
+ * Acquire a seat for the current user
13
+ */
14
+ async acquireSeat(licenseKey, userId, displayName, email) {
15
+ const machineId = await generateMachineId();
16
+ try {
17
+ const response = await fetch(`${this.serverUrl}/seats/acquire`, {
18
+ method: "POST",
19
+ headers: {
20
+ "Content-Type": "application/json",
21
+ ...this.apiKey ? { "X-API-Key": this.apiKey } : {}
22
+ },
23
+ body: JSON.stringify({
24
+ licenseKey,
25
+ userId,
26
+ displayName,
27
+ email,
28
+ machineId
29
+ })
30
+ });
31
+ const data = await response.json();
32
+ if (!response.ok) {
33
+ return {
34
+ success: false,
35
+ error: data.error || "Failed to acquire seat"
36
+ };
37
+ }
38
+ this.currentSeat = data.seat;
39
+ this.startHeartbeat(licenseKey);
40
+ return {
41
+ success: true,
42
+ seat: data.seat
43
+ };
44
+ } catch (error) {
45
+ return {
46
+ success: false,
47
+ error: "Network error"
48
+ };
49
+ }
50
+ }
51
+ /**
52
+ * Release current seat
53
+ */
54
+ async releaseSeat(licenseKey) {
55
+ if (!this.currentSeat) {
56
+ return true;
57
+ }
58
+ this.stopHeartbeat();
59
+ try {
60
+ const response = await fetch(`${this.serverUrl}/seats/release`, {
61
+ method: "POST",
62
+ headers: {
63
+ "Content-Type": "application/json",
64
+ ...this.apiKey ? { "X-API-Key": this.apiKey } : {}
65
+ },
66
+ body: JSON.stringify({
67
+ licenseKey,
68
+ seatId: this.currentSeat.seatId
69
+ })
70
+ });
71
+ if (response.ok) {
72
+ this.currentSeat = void 0;
73
+ return true;
74
+ }
75
+ return false;
76
+ } catch {
77
+ return false;
78
+ }
79
+ }
80
+ /**
81
+ * Get all active seats for a license
82
+ */
83
+ async getActiveSeats(licenseKey) {
84
+ try {
85
+ const response = await fetch(
86
+ `${this.serverUrl}/seats/list?licenseKey=${encodeURIComponent(licenseKey)}`,
87
+ {
88
+ headers: {
89
+ ...this.apiKey ? { "X-API-Key": this.apiKey } : {}
90
+ }
91
+ }
92
+ );
93
+ if (!response.ok) {
94
+ return [];
95
+ }
96
+ const data = await response.json();
97
+ return data.seats || [];
98
+ } catch {
99
+ return [];
100
+ }
101
+ }
102
+ /**
103
+ * Force release a seat (admin function)
104
+ */
105
+ async forceReleaseSeat(licenseKey, seatId) {
106
+ try {
107
+ const response = await fetch(`${this.serverUrl}/seats/force-release`, {
108
+ method: "POST",
109
+ headers: {
110
+ "Content-Type": "application/json",
111
+ ...this.apiKey ? { "X-API-Key": this.apiKey } : {}
112
+ },
113
+ body: JSON.stringify({
114
+ licenseKey,
115
+ seatId
116
+ })
117
+ });
118
+ return response.ok;
119
+ } catch {
120
+ return false;
121
+ }
122
+ }
123
+ /**
124
+ * Check if seat limit is reached
125
+ */
126
+ async canAcquireSeat(license) {
127
+ if (license.maxSeats === null) {
128
+ return true;
129
+ }
130
+ const activeSeats = await this.getActiveSeats(license.key);
131
+ return activeSeats.length < license.maxSeats;
132
+ }
133
+ /**
134
+ * Start heartbeat to keep seat active
135
+ */
136
+ startHeartbeat(licenseKey) {
137
+ this.stopHeartbeat();
138
+ this.heartbeatInterval = setInterval(async () => {
139
+ if (!this.currentSeat) {
140
+ this.stopHeartbeat();
141
+ return;
142
+ }
143
+ try {
144
+ await fetch(`${this.serverUrl}/seats/heartbeat`, {
145
+ method: "POST",
146
+ headers: {
147
+ "Content-Type": "application/json",
148
+ ...this.apiKey ? { "X-API-Key": this.apiKey } : {}
149
+ },
150
+ body: JSON.stringify({
151
+ licenseKey,
152
+ seatId: this.currentSeat.seatId
153
+ })
154
+ });
155
+ } catch {
156
+ }
157
+ }, HEARTBEAT_INTERVAL_MS);
158
+ }
159
+ /**
160
+ * Stop heartbeat
161
+ */
162
+ stopHeartbeat() {
163
+ if (this.heartbeatInterval) {
164
+ clearInterval(this.heartbeatInterval);
165
+ this.heartbeatInterval = void 0;
166
+ }
167
+ }
168
+ /**
169
+ * Get current seat info
170
+ */
171
+ getCurrentSeat() {
172
+ return this.currentSeat;
173
+ }
174
+ /**
175
+ * Cleanup on unmount/close
176
+ */
177
+ async cleanup(licenseKey) {
178
+ await this.releaseSeat(licenseKey);
179
+ }
180
+ }
181
+ class FloatingLicenseManager {
182
+ constructor(serverUrl, apiKey) {
183
+ this.serverUrl = serverUrl;
184
+ this.apiKey = apiKey;
185
+ }
186
+ /**
187
+ * Acquire a floating license
188
+ */
189
+ async acquireLease(licenseKey) {
190
+ const machineId = await generateMachineId();
191
+ try {
192
+ const response = await fetch(`${this.serverUrl}/floating/acquire`, {
193
+ method: "POST",
194
+ headers: {
195
+ "Content-Type": "application/json",
196
+ ...this.apiKey ? { "X-API-Key": this.apiKey } : {}
197
+ },
198
+ body: JSON.stringify({
199
+ licenseKey,
200
+ machineId
201
+ })
202
+ });
203
+ const data = await response.json();
204
+ if (!response.ok) {
205
+ return {
206
+ success: false,
207
+ error: data.error || "No floating licenses available"
208
+ };
209
+ }
210
+ this.leaseId = data.leaseId;
211
+ this.startLeaseRenewal(licenseKey);
212
+ return {
213
+ success: true,
214
+ leaseId: data.leaseId,
215
+ expiresAt: data.expiresAt
216
+ };
217
+ } catch (error) {
218
+ return {
219
+ success: false,
220
+ error: "Network error"
221
+ };
222
+ }
223
+ }
224
+ /**
225
+ * Release floating license
226
+ */
227
+ async releaseLease(licenseKey) {
228
+ if (!this.leaseId) {
229
+ return true;
230
+ }
231
+ this.stopLeaseRenewal();
232
+ try {
233
+ const response = await fetch(`${this.serverUrl}/floating/release`, {
234
+ method: "POST",
235
+ headers: {
236
+ "Content-Type": "application/json",
237
+ ...this.apiKey ? { "X-API-Key": this.apiKey } : {}
238
+ },
239
+ body: JSON.stringify({
240
+ licenseKey,
241
+ leaseId: this.leaseId
242
+ })
243
+ });
244
+ if (response.ok) {
245
+ this.leaseId = void 0;
246
+ return true;
247
+ }
248
+ return false;
249
+ } catch {
250
+ return false;
251
+ }
252
+ }
253
+ /**
254
+ * Get floating license status
255
+ */
256
+ async getFloatingStatus(licenseKey) {
257
+ try {
258
+ const response = await fetch(
259
+ `${this.serverUrl}/floating/status?licenseKey=${encodeURIComponent(licenseKey)}`,
260
+ {
261
+ headers: {
262
+ ...this.apiKey ? { "X-API-Key": this.apiKey } : {}
263
+ }
264
+ }
265
+ );
266
+ if (!response.ok) {
267
+ return { total: 0, available: 0, leases: [] };
268
+ }
269
+ return response.json();
270
+ } catch {
271
+ return { total: 0, available: 0, leases: [] };
272
+ }
273
+ }
274
+ /**
275
+ * Start lease renewal interval
276
+ */
277
+ startLeaseRenewal(licenseKey) {
278
+ this.stopLeaseRenewal();
279
+ this.renewInterval = setInterval(
280
+ async () => {
281
+ if (!this.leaseId) {
282
+ this.stopLeaseRenewal();
283
+ return;
284
+ }
285
+ try {
286
+ const response = await fetch(`${this.serverUrl}/floating/renew`, {
287
+ method: "POST",
288
+ headers: {
289
+ "Content-Type": "application/json",
290
+ ...this.apiKey ? { "X-API-Key": this.apiKey } : {}
291
+ },
292
+ body: JSON.stringify({
293
+ licenseKey,
294
+ leaseId: this.leaseId
295
+ })
296
+ });
297
+ if (!response.ok) {
298
+ this.leaseId = void 0;
299
+ this.stopLeaseRenewal();
300
+ }
301
+ } catch {
302
+ }
303
+ },
304
+ 2 * 60 * 1e3
305
+ );
306
+ }
307
+ /**
308
+ * Stop lease renewal
309
+ */
310
+ stopLeaseRenewal() {
311
+ if (this.renewInterval) {
312
+ clearInterval(this.renewInterval);
313
+ this.renewInterval = void 0;
314
+ }
315
+ }
316
+ /**
317
+ * Check if we have an active lease
318
+ */
319
+ hasActiveLease() {
320
+ return !!this.leaseId;
321
+ }
322
+ /**
323
+ * Cleanup
324
+ */
325
+ async cleanup(licenseKey) {
326
+ await this.releaseLease(licenseKey);
327
+ }
328
+ }
329
+ function createSeatManager(serverUrl, apiKey) {
330
+ return new SeatManager(serverUrl, apiKey);
331
+ }
332
+ function createFloatingLicenseManager(serverUrl, apiKey) {
333
+ return new FloatingLicenseManager(serverUrl, apiKey);
334
+ }
335
+ function fnv1a(str) {
336
+ let hash = 2166136261;
337
+ for (let i2 = 0; i2 < str.length; i2++) {
338
+ hash ^= str.charCodeAt(i2);
339
+ hash = Math.imul(hash, 16777619);
340
+ }
341
+ return (hash >>> 0).toString(16).padStart(8, "0");
342
+ }
343
+ function hashLicenseKey(key) {
344
+ return fnv1a(key) + fnv1a(key + "nice2dev-salt");
345
+ }
346
+ class UsageTelemetry {
347
+ constructor(config) {
348
+ this.queue = [];
349
+ this.flushTimer = null;
350
+ this._active = false;
351
+ this.config = {
352
+ enabled: false,
353
+ batchSize: 20,
354
+ flushInterval: 6e4,
355
+ maxQueueSize: 500,
356
+ respectDNT: true,
357
+ apiKey: "",
358
+ ...config
359
+ };
360
+ }
361
+ /** Start collecting telemetry (no-op if disabled or DNT) */
362
+ start() {
363
+ if (!this.isAllowed()) {
364
+ return;
365
+ }
366
+ if (this._active) {
367
+ return;
368
+ }
369
+ this._active = true;
370
+ this.flushTimer = setInterval(() => this.flush(), this.config.flushInterval);
371
+ }
372
+ /** Stop collecting and flush remaining events */
373
+ async stop() {
374
+ if (!this._active) {
375
+ return;
376
+ }
377
+ this._active = false;
378
+ if (this.flushTimer) {
379
+ clearInterval(this.flushTimer);
380
+ this.flushTimer = null;
381
+ }
382
+ await this.flush();
383
+ }
384
+ /** Track a telemetry event */
385
+ track(type, data) {
386
+ if (!this._active) {
387
+ return;
388
+ }
389
+ const event = {
390
+ type,
391
+ licenseKeyHash: this.config.licenseKeyHash,
392
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
393
+ data
394
+ };
395
+ this.queue.push(event);
396
+ if (this.queue.length > this.config.maxQueueSize) {
397
+ this.queue = this.queue.slice(-this.config.maxQueueSize);
398
+ }
399
+ if (this.queue.length >= this.config.batchSize) {
400
+ this.flush();
401
+ }
402
+ }
403
+ /** Track feature usage */
404
+ trackFeatureUse(featureId) {
405
+ this.track("feature_use", { featureId });
406
+ }
407
+ /** Track activation event */
408
+ trackActivation() {
409
+ this.track("activation");
410
+ }
411
+ /** Track deactivation event */
412
+ trackDeactivation() {
413
+ this.track("deactivation");
414
+ }
415
+ /** Track validation event */
416
+ trackValidation(valid) {
417
+ this.track("validation", { valid });
418
+ }
419
+ /** Track error event */
420
+ trackError(errorCode) {
421
+ this.track("error", { errorCode });
422
+ }
423
+ /** Get queued event count */
424
+ get queueSize() {
425
+ return this.queue.length;
426
+ }
427
+ /** Check if telemetry is active */
428
+ get active() {
429
+ return this._active;
430
+ }
431
+ /** Flush queued events to server */
432
+ async flush() {
433
+ if (this.queue.length === 0) {
434
+ return;
435
+ }
436
+ const batch = this.queue.splice(0, this.config.batchSize);
437
+ try {
438
+ const headers = {
439
+ "Content-Type": "application/json"
440
+ };
441
+ if (this.config.apiKey) {
442
+ headers["X-API-Key"] = this.config.apiKey;
443
+ }
444
+ const response = await fetch(this.config.endpoint, {
445
+ method: "POST",
446
+ headers,
447
+ body: JSON.stringify({ events: batch })
448
+ });
449
+ if (!response.ok) {
450
+ this.queue.unshift(...batch);
451
+ }
452
+ } catch {
453
+ this.queue.unshift(...batch);
454
+ }
455
+ }
456
+ /** Enable telemetry (opt-in) */
457
+ enable() {
458
+ this.config.enabled = true;
459
+ this.start();
460
+ }
461
+ /** Disable telemetry (opt-out) */
462
+ disable() {
463
+ this.config.enabled = false;
464
+ this.stop();
465
+ }
466
+ /** Check if telemetry is allowed */
467
+ isAllowed() {
468
+ if (!this.config.enabled) {
469
+ return false;
470
+ }
471
+ if (this.config.respectDNT && typeof navigator !== "undefined" && navigator.doNotTrack === "1") {
472
+ return false;
473
+ }
474
+ return true;
475
+ }
476
+ }
477
+ function createTelemetry(config) {
478
+ return new UsageTelemetry(config);
479
+ }
480
+ const protectedModules = /* @__PURE__ */ new Map();
481
+ function registerProtectedModule(entry) {
482
+ protectedModules.set(entry.modulePath, entry);
483
+ }
484
+ function registerProtectedModules(entries) {
485
+ entries.forEach(registerProtectedModule);
486
+ }
487
+ function getAllProtectedModules() {
488
+ return Array.from(protectedModules.values());
489
+ }
490
+ function getProtectedModule(modulePath) {
491
+ return protectedModules.get(modulePath);
492
+ }
493
+ function isModuleAccessible(modulePath, currentTier) {
494
+ const entry = protectedModules.get(modulePath);
495
+ if (!entry) {
496
+ return true;
497
+ }
498
+ const tierRank2 = {
499
+ trial: 0,
500
+ personal: 1,
501
+ team: 2,
502
+ enterprise: 3,
503
+ site: 4,
504
+ oem: 5
505
+ };
506
+ return tierRank2[currentTier] >= tierRank2[entry.requiredTier];
507
+ }
508
+ function getInaccessibleModules(currentTier) {
509
+ return getAllProtectedModules().filter((m2) => !isModuleAccessible(m2.modulePath, currentTier));
510
+ }
511
+ function getAccessibleModules(currentTier) {
512
+ return getAllProtectedModules().filter((m2) => isModuleAccessible(m2.modulePath, currentTier));
513
+ }
514
+ function generateBundleTags() {
515
+ const byTier = /* @__PURE__ */ new Map();
516
+ for (const entry of protectedModules.values()) {
517
+ const key = `${entry.requiredTier}:${entry.accessLevel}`;
518
+ const group = byTier.get(key) ?? [];
519
+ group.push(entry);
520
+ byTier.set(key, group);
521
+ }
522
+ return Array.from(byTier.entries()).map(([key, entries]) => {
523
+ const [requiredTier, accessLevel] = key.split(":");
524
+ return {
525
+ chunkId: `protected-${requiredTier}-${accessLevel}`,
526
+ requiredTier,
527
+ accessLevel,
528
+ modules: entries.map((e) => e.modulePath)
529
+ };
530
+ });
531
+ }
532
+ function generateProtectionManifest() {
533
+ return {
534
+ version: "1.0.0",
535
+ generated: (/* @__PURE__ */ new Date()).toISOString(),
536
+ modules: getAllProtectedModules(),
537
+ bundleTags: generateBundleTags()
538
+ };
539
+ }
540
+ const DEFAULT_RATE_LIMITS = {
541
+ trial: { maxRequests: 30, windowMs: 6e4, burst: 5 },
542
+ personal: { maxRequests: 120, windowMs: 6e4, burst: 20 },
543
+ team: { maxRequests: 600, windowMs: 6e4, burst: 100 },
544
+ enterprise: { maxRequests: 3e3, windowMs: 6e4, burst: 500 },
545
+ site: { maxRequests: 1e4, windowMs: 6e4, burst: 2e3 },
546
+ oem: { maxRequests: 5e4, windowMs: 6e4, burst: 1e4 }
547
+ };
548
+ class RateLimiter {
549
+ constructor(config) {
550
+ this.config = config;
551
+ this.tokens = config.maxRequests + (config.burst ?? 0);
552
+ this.lastRefill = Date.now();
553
+ }
554
+ /** Refill tokens based on elapsed time */
555
+ refill() {
556
+ const now = Date.now();
557
+ const elapsed = now - this.lastRefill;
558
+ const maxTokens = this.config.maxRequests + (this.config.burst ?? 0);
559
+ const refillRate = this.config.maxRequests / this.config.windowMs;
560
+ const newTokens = elapsed * refillRate;
561
+ this.tokens = Math.min(maxTokens, this.tokens + newTokens);
562
+ this.lastRefill = now;
563
+ }
564
+ /** Check and consume one request token */
565
+ consume() {
566
+ this.refill();
567
+ const maxTokens = this.config.maxRequests + (this.config.burst ?? 0);
568
+ if (this.tokens >= 1) {
569
+ this.tokens -= 1;
570
+ return {
571
+ allowed: true,
572
+ remaining: Math.floor(this.tokens),
573
+ retryAfterMs: 0,
574
+ limit: maxTokens
575
+ };
576
+ }
577
+ const refillRate = this.config.maxRequests / this.config.windowMs;
578
+ const retryAfterMs = Math.ceil((1 - this.tokens) / refillRate);
579
+ return {
580
+ allowed: false,
581
+ remaining: 0,
582
+ retryAfterMs,
583
+ limit: maxTokens
584
+ };
585
+ }
586
+ /** Reset the limiter (e.g. after tier change) */
587
+ reset(config) {
588
+ if (config) {
589
+ this.config = config;
590
+ }
591
+ this.tokens = this.config.maxRequests + (this.config.burst ?? 0);
592
+ this.lastRefill = Date.now();
593
+ }
594
+ /** Peek at remaining tokens without consuming */
595
+ peek() {
596
+ this.refill();
597
+ const maxTokens = this.config.maxRequests + (this.config.burst ?? 0);
598
+ return {
599
+ allowed: this.tokens >= 1,
600
+ remaining: Math.floor(this.tokens),
601
+ retryAfterMs: 0,
602
+ limit: maxTokens
603
+ };
604
+ }
605
+ }
606
+ class ApiRateLimiter {
607
+ constructor(tier, config = DEFAULT_RATE_LIMITS) {
608
+ this.limiters = /* @__PURE__ */ new Map();
609
+ this.endpointOverrides = /* @__PURE__ */ new Map();
610
+ this.tier = tier;
611
+ this.config = config;
612
+ }
613
+ /** Set endpoint-specific rate limit overrides */
614
+ setEndpointOverride(endpoint, overrides) {
615
+ this.endpointOverrides.set(endpoint, overrides);
616
+ }
617
+ /** Update the current tier (resets all limiters) */
618
+ setTier(tier) {
619
+ this.tier = tier;
620
+ this.limiters.clear();
621
+ }
622
+ /** Get or create a limiter for an endpoint */
623
+ getLimiter(endpoint) {
624
+ let limiter = this.limiters.get(endpoint);
625
+ if (!limiter) {
626
+ const override = this.endpointOverrides.get(endpoint);
627
+ const tierConfig = (override && override[this.tier]) ?? this.config[this.tier];
628
+ limiter = new RateLimiter(tierConfig);
629
+ this.limiters.set(endpoint, limiter);
630
+ }
631
+ return limiter;
632
+ }
633
+ /** Check if a request to the given endpoint is allowed and consume a token */
634
+ check(endpoint = "default") {
635
+ return this.getLimiter(endpoint).consume();
636
+ }
637
+ /** Peek without consuming */
638
+ peek(endpoint = "default") {
639
+ return this.getLimiter(endpoint).peek();
640
+ }
641
+ /** Reset all limiters */
642
+ reset() {
643
+ this.limiters.clear();
644
+ }
645
+ }
646
+ function createApiRateLimiter(tier, config) {
647
+ return new ApiRateLimiter(tier, config);
648
+ }
649
+ const FEATURE_TIER_REQUIREMENTS = {
650
+ productName: "team",
651
+ logoUrl: "team",
652
+ logoDarkUrl: "team",
653
+ faviconUrl: "team",
654
+ primaryColor: "team",
655
+ secondaryColor: "team",
656
+ accentColor: "team",
657
+ customCss: "enterprise",
658
+ footerText: "enterprise",
659
+ hidePoweredBy: "enterprise",
660
+ customDomain: "site",
661
+ emailDomain: "site"
662
+ };
663
+ const tierRank = {
664
+ trial: 0,
665
+ personal: 1,
666
+ team: 2,
667
+ enterprise: 3,
668
+ site: 4,
669
+ oem: 5
670
+ };
671
+ class WhiteLabelManager {
672
+ constructor() {
673
+ this.config = {};
674
+ this.currentTier = "trial";
675
+ }
676
+ /** Set the active license tier */
677
+ setTier(tier) {
678
+ this.currentTier = tier;
679
+ }
680
+ /** Update white-label configuration (only applicable fields per tier are kept) */
681
+ setConfig(config) {
682
+ this.config = config;
683
+ }
684
+ /** Get the resolved configuration (filtered by tier permissions) */
685
+ getResolvedConfig() {
686
+ const resolved = {};
687
+ for (const [key, value] of Object.entries(this.config)) {
688
+ if (value === void 0) {
689
+ continue;
690
+ }
691
+ const requiredTier = FEATURE_TIER_REQUIREMENTS[key];
692
+ if (requiredTier && tierRank[this.currentTier] >= tierRank[requiredTier]) {
693
+ resolved[key] = value;
694
+ }
695
+ }
696
+ return resolved;
697
+ }
698
+ /** Check if a specific white-label feature is allowed */
699
+ isFeatureAllowed(feature) {
700
+ const requiredTier = FEATURE_TIER_REQUIREMENTS[feature];
701
+ return tierRank[this.currentTier] >= tierRank[requiredTier];
702
+ }
703
+ /** Get list of features not available at current tier */
704
+ getLockedFeatures() {
705
+ return Object.keys(FEATURE_TIER_REQUIREMENTS).filter(
706
+ (f2) => !this.isFeatureAllowed(f2)
707
+ );
708
+ }
709
+ /** Get list of features available at current tier */
710
+ getUnlockedFeatures() {
711
+ return Object.keys(FEATURE_TIER_REQUIREMENTS).filter(
712
+ (f2) => this.isFeatureAllowed(f2)
713
+ );
714
+ }
715
+ /** Generate CSS custom properties from the resolved config */
716
+ generateCssVariables() {
717
+ const cfg = this.getResolvedConfig();
718
+ const vars = [];
719
+ if (cfg.primaryColor) {
720
+ vars.push(`--nice-brand-primary: ${cfg.primaryColor};`);
721
+ }
722
+ if (cfg.secondaryColor) {
723
+ vars.push(`--nice-brand-secondary: ${cfg.secondaryColor};`);
724
+ }
725
+ if (cfg.accentColor) {
726
+ vars.push(`--nice-brand-accent: ${cfg.accentColor};`);
727
+ }
728
+ if (cfg.logoUrl) {
729
+ vars.push(`--nice-brand-logo: url(${cfg.logoUrl});`);
730
+ }
731
+ if (cfg.logoDarkUrl) {
732
+ vars.push(`--nice-brand-logo-dark: url(${cfg.logoDarkUrl});`);
733
+ }
734
+ if (vars.length === 0) {
735
+ return "";
736
+ }
737
+ return `:root {
738
+ ${vars.join("\n ")}
739
+ }`;
740
+ }
741
+ /** Apply the brand configuration to the document */
742
+ applyToDocument() {
743
+ if (typeof document === "undefined") {
744
+ return;
745
+ }
746
+ const cfg = this.getResolvedConfig();
747
+ const css = this.generateCssVariables();
748
+ if (css) {
749
+ let styleEl = document.getElementById("nice-whitelabel-styles");
750
+ if (!styleEl) {
751
+ styleEl = document.createElement("style");
752
+ styleEl.id = "nice-whitelabel-styles";
753
+ document.head.appendChild(styleEl);
754
+ }
755
+ styleEl.textContent = css + (cfg.customCss ? `
756
+ ${cfg.customCss}` : "");
757
+ }
758
+ if (cfg.faviconUrl) {
759
+ let link = document.querySelector('link[rel="icon"]');
760
+ if (!link) {
761
+ link = document.createElement("link");
762
+ link.rel = "icon";
763
+ document.head.appendChild(link);
764
+ }
765
+ link.href = cfg.faviconUrl;
766
+ }
767
+ if (cfg.productName) {
768
+ document.title = cfg.productName;
769
+ }
770
+ }
771
+ }
772
+ function createWhiteLabelManager(tier, config) {
773
+ const mgr = new WhiteLabelManager();
774
+ mgr.setTier(tier);
775
+ if (config) {
776
+ mgr.setConfig(config);
777
+ }
778
+ return mgr;
779
+ }
780
+ const DEFAULT_SLA_CONFIGS = {
781
+ trial: {
782
+ uptimePercent: 95,
783
+ supportResponseHours: 72,
784
+ supportResolutionHours: 168,
785
+ supportChannels: ["email"],
786
+ priority: 6,
787
+ includedHours: 0,
788
+ breachPenaltyPercent: 0
789
+ },
790
+ personal: {
791
+ uptimePercent: 99,
792
+ supportResponseHours: 48,
793
+ supportResolutionHours: 120,
794
+ supportChannels: ["email"],
795
+ priority: 5,
796
+ includedHours: 2,
797
+ breachPenaltyPercent: 0
798
+ },
799
+ team: {
800
+ uptimePercent: 99.5,
801
+ supportResponseHours: 24,
802
+ supportResolutionHours: 72,
803
+ supportChannels: ["email", "chat"],
804
+ priority: 4,
805
+ includedHours: 10,
806
+ breachPenaltyPercent: 5
807
+ },
808
+ enterprise: {
809
+ uptimePercent: 99.9,
810
+ supportResponseHours: 4,
811
+ supportResolutionHours: 24,
812
+ supportChannels: ["email", "chat", "phone"],
813
+ priority: 3,
814
+ includedHours: 40,
815
+ breachPenaltyPercent: 10
816
+ },
817
+ site: {
818
+ uptimePercent: 99.95,
819
+ supportResponseHours: 2,
820
+ supportResolutionHours: 12,
821
+ supportChannels: ["email", "chat", "phone", "dedicated-manager"],
822
+ priority: 2,
823
+ includedHours: null,
824
+ breachPenaltyPercent: 15
825
+ },
826
+ oem: {
827
+ uptimePercent: 99.99,
828
+ supportResponseHours: 1,
829
+ supportResolutionHours: 4,
830
+ supportChannels: ["email", "chat", "phone", "dedicated-manager", "on-site"],
831
+ priority: 1,
832
+ includedHours: null,
833
+ breachPenaltyPercent: 20
834
+ }
835
+ };
836
+ class SlaTracker {
837
+ constructor(tier, config = DEFAULT_SLA_CONFIGS) {
838
+ this.events = [];
839
+ this.tier = tier;
840
+ this.config = config;
841
+ }
842
+ /** Update the license tier */
843
+ setTier(tier) {
844
+ this.tier = tier;
845
+ }
846
+ /** Get the SLA config for the current tier */
847
+ getSlaConfig() {
848
+ return this.config[this.tier];
849
+ }
850
+ /** Record an event */
851
+ recordEvent(event) {
852
+ this.events.push(event);
853
+ }
854
+ /** Record an uptime check */
855
+ recordUptimeCheck() {
856
+ var _a;
857
+ this.recordEvent({
858
+ id: ((_a = crypto.randomUUID) == null ? void 0 : _a.call(crypto)) ?? Date.now().toString(36),
859
+ type: "uptime-check",
860
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
861
+ });
862
+ }
863
+ /** Record downtime */
864
+ recordDowntime(durationMs) {
865
+ var _a, _b;
866
+ const now = /* @__PURE__ */ new Date();
867
+ this.recordEvent({
868
+ id: ((_a = crypto.randomUUID) == null ? void 0 : _a.call(crypto)) ?? Date.now().toString(36),
869
+ type: "downtime-start",
870
+ timestamp: new Date(now.getTime() - durationMs).toISOString(),
871
+ durationMs
872
+ });
873
+ this.recordEvent({
874
+ id: ((_b = crypto.randomUUID) == null ? void 0 : _b.call(crypto)) ?? (Date.now() + 1).toString(36),
875
+ type: "downtime-end",
876
+ timestamp: now.toISOString()
877
+ });
878
+ }
879
+ /** Record a support ticket lifecycle */
880
+ recordTicket(responseTimeHours, resolutionTimeHours) {
881
+ var _a, _b;
882
+ const now = /* @__PURE__ */ new Date();
883
+ this.recordEvent({
884
+ id: ((_a = crypto.randomUUID) == null ? void 0 : _a.call(crypto)) ?? Date.now().toString(36),
885
+ type: "ticket-opened",
886
+ timestamp: now.toISOString(),
887
+ metadata: { responseTimeHours, resolutionTimeHours }
888
+ });
889
+ this.recordEvent({
890
+ id: ((_b = crypto.randomUUID) == null ? void 0 : _b.call(crypto)) ?? (Date.now() + 1).toString(36),
891
+ type: "ticket-resolved",
892
+ timestamp: new Date(now.getTime() + resolutionTimeHours * 36e5).toISOString(),
893
+ durationMs: resolutionTimeHours * 36e5,
894
+ metadata: { responseTimeHours }
895
+ });
896
+ }
897
+ /** Generate an SLA report for a given period */
898
+ generateReport(periodStart, periodEnd) {
899
+ const slaConfig = this.getSlaConfig();
900
+ const periodMs = periodEnd.getTime() - periodStart.getTime();
901
+ const periodEvents = this.events.filter((e) => {
902
+ const t = new Date(e.timestamp).getTime();
903
+ return t >= periodStart.getTime() && t <= periodEnd.getTime();
904
+ });
905
+ const downtimeEvents = periodEvents.filter((e) => e.type === "downtime-start");
906
+ const totalDowntimeMs = downtimeEvents.reduce((sum, e) => sum + (e.durationMs ?? 0), 0);
907
+ const actualUptimePercent = periodMs > 0 ? (periodMs - totalDowntimeMs) / periodMs * 100 : 100;
908
+ const tickets = periodEvents.filter((e) => e.type === "ticket-resolved");
909
+ const responseTimes = tickets.map((t) => {
910
+ var _a;
911
+ return ((_a = t.metadata) == null ? void 0 : _a.responseTimeHours) ?? 0;
912
+ }).filter((h2) => h2 > 0);
913
+ const resolutionTimes = tickets.map((t) => (t.durationMs ?? 0) / 36e5).filter((h2) => h2 > 0);
914
+ const avgResponseHours = responseTimes.length > 0 ? responseTimes.reduce((a, b2) => a + b2, 0) / responseTimes.length : 0;
915
+ const avgResolutionHours = resolutionTimes.length > 0 ? resolutionTimes.reduce((a, b2) => a + b2, 0) / resolutionTimes.length : 0;
916
+ let breachCount = 0;
917
+ if (actualUptimePercent < slaConfig.uptimePercent) {
918
+ breachCount++;
919
+ }
920
+ if (avgResponseHours > slaConfig.supportResponseHours && responseTimes.length > 0) {
921
+ breachCount++;
922
+ }
923
+ if (avgResolutionHours > slaConfig.supportResolutionHours && resolutionTimes.length > 0) {
924
+ breachCount++;
925
+ }
926
+ const compliant = breachCount === 0;
927
+ const creditPercent = compliant ? 0 : breachCount * slaConfig.breachPenaltyPercent;
928
+ return {
929
+ periodStart: periodStart.toISOString(),
930
+ periodEnd: periodEnd.toISOString(),
931
+ tier: this.tier,
932
+ slaConfig,
933
+ actualUptimePercent: Math.round(actualUptimePercent * 100) / 100,
934
+ totalDowntimeMs,
935
+ avgResponseHours: Math.round(avgResponseHours * 10) / 10,
936
+ avgResolutionHours: Math.round(avgResolutionHours * 10) / 10,
937
+ breachCount,
938
+ compliant,
939
+ creditPercent: Math.min(100, creditPercent)
940
+ };
941
+ }
942
+ /** Get all recorded events */
943
+ getEvents() {
944
+ return [...this.events];
945
+ }
946
+ /** Clear all events */
947
+ clearEvents() {
948
+ this.events = [];
949
+ }
950
+ }
951
+ function createSlaTracker(tier) {
952
+ return new SlaTracker(tier);
953
+ }
954
+ function auditHash(str) {
955
+ let hash = 2166136261;
956
+ for (let i2 = 0; i2 < str.length; i2++) {
957
+ hash ^= str.charCodeAt(i2);
958
+ hash = Math.imul(hash, 16777619);
959
+ }
960
+ return (hash >>> 0).toString(16).padStart(8, "0");
961
+ }
962
+ function hashKeyForAudit(key) {
963
+ return auditHash(key) + auditHash(key + "audit-salt");
964
+ }
965
+ class AuditTrail {
966
+ constructor(options) {
967
+ this.entries = [];
968
+ this.serverUrl = options == null ? void 0 : options.serverUrl;
969
+ this.apiKey = options == null ? void 0 : options.apiKey;
970
+ }
971
+ /** Append an audit entry */
972
+ log(event, licenseKey, actor, description, details) {
973
+ var _a;
974
+ const entry = {
975
+ id: ((_a = crypto.randomUUID) == null ? void 0 : _a.call(crypto)) ?? `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
976
+ event,
977
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
978
+ licenseKeyHash: hashKeyForAudit(licenseKey),
979
+ actor,
980
+ description,
981
+ previousValue: details == null ? void 0 : details.previousValue,
982
+ newValue: details == null ? void 0 : details.newValue,
983
+ ipHash: details == null ? void 0 : details.ipHash,
984
+ metadata: details == null ? void 0 : details.metadata
985
+ };
986
+ this.entries.push(entry);
987
+ if (this.serverUrl) {
988
+ this.sendToServer(entry).catch(() => {
989
+ });
990
+ }
991
+ return entry;
992
+ }
993
+ // ─── Convenience logging methods ────────────────────────────
994
+ logActivation(licenseKey, actor) {
995
+ return this.log("license.activated", licenseKey, actor, "License activated");
996
+ }
997
+ logDeactivation(licenseKey, actor) {
998
+ return this.log("license.deactivated", licenseKey, actor, "License deactivated");
999
+ }
1000
+ logTierChange(licenseKey, actor, from, to) {
1001
+ return this.log(
1002
+ "license.tier-changed",
1003
+ licenseKey,
1004
+ actor,
1005
+ `Tier changed from ${from} to ${to}`,
1006
+ { previousValue: from, newValue: to }
1007
+ );
1008
+ }
1009
+ logSeatsChange(licenseKey, actor, from, to) {
1010
+ return this.log(
1011
+ "license.seats-changed",
1012
+ licenseKey,
1013
+ actor,
1014
+ `Seats changed from ${from} to ${to}`,
1015
+ { previousValue: from, newValue: to }
1016
+ );
1017
+ }
1018
+ logRevocation(licenseKey, actor, reason) {
1019
+ return this.log(
1020
+ "license.revoked",
1021
+ licenseKey,
1022
+ actor,
1023
+ `License revoked${reason ? `: ${reason}` : ""}`,
1024
+ {
1025
+ metadata: reason ? { reason } : void 0
1026
+ }
1027
+ );
1028
+ }
1029
+ // ─── Query ──────────────────────────────────────────────────
1030
+ /** Query audit entries with filters */
1031
+ query(filter = {}) {
1032
+ var _a;
1033
+ let results = [...this.entries];
1034
+ if ((_a = filter.events) == null ? void 0 : _a.length) {
1035
+ const set = new Set(filter.events);
1036
+ results = results.filter((e) => set.has(e.event));
1037
+ }
1038
+ if (filter.licenseKeyHash) {
1039
+ results = results.filter((e) => e.licenseKeyHash === filter.licenseKeyHash);
1040
+ }
1041
+ if (filter.actorId) {
1042
+ results = results.filter((e) => e.actor.id === filter.actorId);
1043
+ }
1044
+ if (filter.from) {
1045
+ const from = filter.from.getTime();
1046
+ results = results.filter((e) => new Date(e.timestamp).getTime() >= from);
1047
+ }
1048
+ if (filter.to) {
1049
+ const to = filter.to.getTime();
1050
+ results = results.filter((e) => new Date(e.timestamp).getTime() <= to);
1051
+ }
1052
+ results.sort((a, b2) => new Date(b2.timestamp).getTime() - new Date(a.timestamp).getTime());
1053
+ if (filter.offset) {
1054
+ results = results.slice(filter.offset);
1055
+ }
1056
+ if (filter.limit) {
1057
+ results = results.slice(0, filter.limit);
1058
+ }
1059
+ return results;
1060
+ }
1061
+ /** Get total entry count (optionally filtered) */
1062
+ count(filter) {
1063
+ if (!filter) {
1064
+ return this.entries.length;
1065
+ }
1066
+ return this.query({ ...filter, limit: void 0, offset: void 0 }).length;
1067
+ }
1068
+ // ─── Export ─────────────────────────────────────────────────
1069
+ /** Export audit entries */
1070
+ export(filter, format = "json") {
1071
+ const entries = this.query(filter ?? {});
1072
+ if (format === "csv") {
1073
+ const header = "id,event,timestamp,licenseKeyHash,actorType,actorId,description";
1074
+ const rows = entries.map(
1075
+ (e) => `"${e.id}","${e.event}","${e.timestamp}","${e.licenseKeyHash}","${e.actor.type}","${e.actor.id}","${e.description.replace(/"/g, '""')}"`
1076
+ );
1077
+ return [header, ...rows].join("\n");
1078
+ }
1079
+ return JSON.stringify(entries, null, 2);
1080
+ }
1081
+ // ─── Server sync ────────────────────────────────────────────
1082
+ async sendToServer(entry) {
1083
+ if (!this.serverUrl) {
1084
+ return;
1085
+ }
1086
+ const headers = { "Content-Type": "application/json" };
1087
+ if (this.apiKey) {
1088
+ headers["X-API-Key"] = this.apiKey;
1089
+ }
1090
+ await fetch(`${this.serverUrl}/audit`, {
1091
+ method: "POST",
1092
+ headers,
1093
+ body: JSON.stringify(entry)
1094
+ });
1095
+ }
1096
+ /** Get all entries (read-only copy) */
1097
+ getAll() {
1098
+ return [...this.entries];
1099
+ }
1100
+ }
1101
+ function createAuditTrail(options) {
1102
+ return new AuditTrail(options);
1103
+ }
1104
+ class LicensePortalService {
1105
+ constructor(config) {
1106
+ this.config = config;
1107
+ }
1108
+ async request(endpoint, options = {}) {
1109
+ const url = `${this.config.apiBaseUrl}${endpoint}`;
1110
+ const headers = {
1111
+ "Content-Type": "application/json",
1112
+ ...this.config.authToken && {
1113
+ Authorization: `Bearer ${this.config.authToken}`
1114
+ }
1115
+ };
1116
+ const response = await fetch(url, {
1117
+ ...options,
1118
+ headers: { ...headers, ...options.headers }
1119
+ });
1120
+ if (!response.ok) {
1121
+ const error = await response.json().catch(() => ({}));
1122
+ throw {
1123
+ code: `HTTP_${response.status}`,
1124
+ message: error.message || response.statusText,
1125
+ details: error
1126
+ };
1127
+ }
1128
+ return response.json();
1129
+ }
1130
+ /** Get customer license overview */
1131
+ async getOverview() {
1132
+ return this.request(
1133
+ `/customers/${this.config.customerId}/licenses/overview`
1134
+ );
1135
+ }
1136
+ /** Get detailed license info */
1137
+ async getLicense(licenseKey) {
1138
+ return this.request(`/licenses/${encodeURIComponent(licenseKey)}`);
1139
+ }
1140
+ /** Activate license on current machine */
1141
+ async activateLicense(licenseKey, machineId, machineName) {
1142
+ return this.request(`/licenses/${encodeURIComponent(licenseKey)}/activate`, {
1143
+ method: "POST",
1144
+ body: JSON.stringify({ machineId, machineName })
1145
+ });
1146
+ }
1147
+ /** Deactivate license from machine */
1148
+ async deactivateLicense(licenseKey, machineId) {
1149
+ await this.request(`/licenses/${encodeURIComponent(licenseKey)}/deactivate`, {
1150
+ method: "POST",
1151
+ body: JSON.stringify({ machineId })
1152
+ });
1153
+ }
1154
+ /** Transfer license to another user/org */
1155
+ async transferLicense(licenseKey, targetEmail, notes) {
1156
+ return this.request(`/licenses/${encodeURIComponent(licenseKey)}/transfer`, {
1157
+ method: "POST",
1158
+ body: JSON.stringify({ targetEmail, notes })
1159
+ });
1160
+ }
1161
+ /** Get seat assignments */
1162
+ async getSeats(licenseKey) {
1163
+ return this.request(`/licenses/${encodeURIComponent(licenseKey)}/seats`);
1164
+ }
1165
+ /** Assign seat to user */
1166
+ async assignSeat(licenseKey, userId, email) {
1167
+ return this.request(`/licenses/${encodeURIComponent(licenseKey)}/seats`, {
1168
+ method: "POST",
1169
+ body: JSON.stringify({ userId, email })
1170
+ });
1171
+ }
1172
+ /** Remove seat assignment */
1173
+ async removeSeat(licenseKey, seatId) {
1174
+ await this.request(`/licenses/${encodeURIComponent(licenseKey)}/seats/${seatId}`, {
1175
+ method: "DELETE"
1176
+ });
1177
+ }
1178
+ /** Get subscription info */
1179
+ async getSubscription() {
1180
+ try {
1181
+ return await this.request(
1182
+ `/customers/${this.config.customerId}/subscription`
1183
+ );
1184
+ } catch {
1185
+ return null;
1186
+ }
1187
+ }
1188
+ /** Get invoices */
1189
+ async getInvoices(limit = 10, offset = 0) {
1190
+ return this.request(
1191
+ `/customers/${this.config.customerId}/invoices?limit=${limit}&offset=${offset}`
1192
+ );
1193
+ }
1194
+ /** Get action history */
1195
+ async getActionHistory(licenseKey, limit = 50) {
1196
+ const params = new URLSearchParams({ limit: String(limit) });
1197
+ if (licenseKey) {
1198
+ params.set("licenseKey", licenseKey);
1199
+ }
1200
+ return this.request(`/customers/${this.config.customerId}/actions?${params}`);
1201
+ }
1202
+ /** Request license renewal */
1203
+ async requestRenewal(licenseKey) {
1204
+ return this.request(`/licenses/${encodeURIComponent(licenseKey)}/renew`, {
1205
+ method: "POST"
1206
+ });
1207
+ }
1208
+ /** Request tier upgrade */
1209
+ async requestUpgrade(licenseKey, targetTier) {
1210
+ return this.request(`/licenses/${encodeURIComponent(licenseKey)}/upgrade`, {
1211
+ method: "POST",
1212
+ body: JSON.stringify({ targetTier })
1213
+ });
1214
+ }
1215
+ }
1216
+ function getStatusColor(status) {
1217
+ const colors = {
1218
+ valid: "#22c55e",
1219
+ expired: "#ef4444",
1220
+ revoked: "#a855f7",
1221
+ invalid: "#f97316",
1222
+ grace: "#eab308",
1223
+ pending: "#6b7280"
1224
+ };
1225
+ return colors[status] || "#6b7280";
1226
+ }
1227
+ function getTierLabel(tier) {
1228
+ const labels = {
1229
+ trial: "Trial",
1230
+ personal: "Personal",
1231
+ team: "Team",
1232
+ enterprise: "Enterprise",
1233
+ site: "Site License",
1234
+ oem: "OEM"
1235
+ };
1236
+ return labels[tier] || tier;
1237
+ }
1238
+ function formatDate(isoDate, locale = "en-US") {
1239
+ return new Date(isoDate).toLocaleDateString(locale, {
1240
+ year: "numeric",
1241
+ month: "short",
1242
+ day: "numeric"
1243
+ });
1244
+ }
1245
+ function getDaysUntilExpiration(expiresAt) {
1246
+ if (!expiresAt) {
1247
+ return null;
1248
+ }
1249
+ const now = /* @__PURE__ */ new Date();
1250
+ const expiry = new Date(expiresAt);
1251
+ const diff = expiry.getTime() - now.getTime();
1252
+ return Math.ceil(diff / (1e3 * 60 * 60 * 24));
1253
+ }
1254
+ function useLicensePortal(config) {
1255
+ const [state, setState] = useState({
1256
+ loading: true,
1257
+ error: null,
1258
+ overview: null,
1259
+ selectedLicense: null,
1260
+ actionHistory: [],
1261
+ subscription: null,
1262
+ invoices: [],
1263
+ activeTab: "overview"
1264
+ });
1265
+ const service = React.useMemo(
1266
+ () => new LicensePortalService(config),
1267
+ [config.apiBaseUrl, config.authToken, config.customerId]
1268
+ );
1269
+ const loadData = useCallback(async () => {
1270
+ var _a;
1271
+ setState((s2) => ({ ...s2, loading: true, error: null }));
1272
+ try {
1273
+ const [overview, subscription, actionsData, invoicesData] = await Promise.all([
1274
+ service.getOverview(),
1275
+ config.showBilling ? service.getSubscription() : null,
1276
+ service.getActionHistory(),
1277
+ config.showBilling ? service.getInvoices() : { invoices: [], total: 0 }
1278
+ ]);
1279
+ setState((s2) => ({
1280
+ ...s2,
1281
+ loading: false,
1282
+ overview,
1283
+ subscription,
1284
+ actionHistory: actionsData,
1285
+ invoices: invoicesData.invoices
1286
+ }));
1287
+ } catch (error) {
1288
+ const licenseError = error;
1289
+ setState((s2) => ({ ...s2, loading: false, error: licenseError }));
1290
+ (_a = config.onError) == null ? void 0 : _a.call(config, licenseError);
1291
+ }
1292
+ }, [service, config]);
1293
+ useEffect(() => {
1294
+ loadData();
1295
+ }, [loadData]);
1296
+ const activateLicense = useCallback(
1297
+ async (licenseKey, machineId, machineName) => {
1298
+ var _a, _b;
1299
+ try {
1300
+ const license = await service.activateLicense(licenseKey, machineId, machineName);
1301
+ (_a = config.onLicenseActivated) == null ? void 0 : _a.call(config, license);
1302
+ await loadData();
1303
+ return license;
1304
+ } catch (error) {
1305
+ (_b = config.onError) == null ? void 0 : _b.call(config, error);
1306
+ throw error;
1307
+ }
1308
+ },
1309
+ [service, config, loadData]
1310
+ );
1311
+ const deactivateLicense = useCallback(
1312
+ async (licenseKey, machineId) => {
1313
+ var _a, _b;
1314
+ try {
1315
+ await service.deactivateLicense(licenseKey, machineId);
1316
+ (_a = config.onLicenseDeactivated) == null ? void 0 : _a.call(config, licenseKey);
1317
+ await loadData();
1318
+ } catch (error) {
1319
+ (_b = config.onError) == null ? void 0 : _b.call(config, error);
1320
+ throw error;
1321
+ }
1322
+ },
1323
+ [service, config, loadData]
1324
+ );
1325
+ const transferLicense = useCallback(
1326
+ async (licenseKey, targetEmail, notes) => {
1327
+ var _a;
1328
+ try {
1329
+ const result = await service.transferLicense(licenseKey, targetEmail, notes);
1330
+ await loadData();
1331
+ return result;
1332
+ } catch (error) {
1333
+ (_a = config.onError) == null ? void 0 : _a.call(config, error);
1334
+ throw error;
1335
+ }
1336
+ },
1337
+ [service, config, loadData]
1338
+ );
1339
+ const selectLicense = useCallback((license) => {
1340
+ setState((s2) => ({ ...s2, selectedLicense: license }));
1341
+ }, []);
1342
+ const setActiveTab = useCallback((tab) => {
1343
+ setState((s2) => ({ ...s2, activeTab: tab }));
1344
+ }, []);
1345
+ return {
1346
+ state,
1347
+ service,
1348
+ loadData,
1349
+ activateLicense,
1350
+ deactivateLicense,
1351
+ transferLicense,
1352
+ selectLicense,
1353
+ setActiveTab
1354
+ };
1355
+ }
1356
+ const LicenseStatusBadge = ({ status, size = "md" }) => {
1357
+ const sizeClasses = {
1358
+ sm: "px-2 py-0.5 text-xs",
1359
+ md: "px-2.5 py-1 text-sm",
1360
+ lg: "px-3 py-1.5 text-base"
1361
+ };
1362
+ return /* @__PURE__ */ jsxs(
1363
+ "span",
1364
+ {
1365
+ className: `inline-flex items-center rounded-full font-medium ${sizeClasses[size]}`,
1366
+ style: {
1367
+ backgroundColor: `${getStatusColor(status)}20`,
1368
+ color: getStatusColor(status)
1369
+ },
1370
+ children: [
1371
+ /* @__PURE__ */ jsx(
1372
+ "span",
1373
+ {
1374
+ className: "mr-1.5 h-2 w-2 rounded-full",
1375
+ style: { backgroundColor: getStatusColor(status) }
1376
+ }
1377
+ ),
1378
+ status.charAt(0).toUpperCase() + status.slice(1)
1379
+ ]
1380
+ }
1381
+ );
1382
+ };
1383
+ const LicenseTierBadge = ({ tier, size = "md" }) => {
1384
+ const tierColors = {
1385
+ trial: "#6b7280",
1386
+ personal: "#3b82f6",
1387
+ team: "#8b5cf6",
1388
+ enterprise: "#f59e0b",
1389
+ site: "#10b981",
1390
+ oem: "#ec4899"
1391
+ };
1392
+ const sizeClasses = {
1393
+ sm: "px-2 py-0.5 text-xs",
1394
+ md: "px-2.5 py-1 text-sm",
1395
+ lg: "px-3 py-1.5 text-base"
1396
+ };
1397
+ return /* @__PURE__ */ jsx(
1398
+ "span",
1399
+ {
1400
+ className: `inline-flex items-center rounded font-semibold ${sizeClasses[size]}`,
1401
+ style: {
1402
+ backgroundColor: `${tierColors[tier]}20`,
1403
+ color: tierColors[tier],
1404
+ border: `1px solid ${tierColors[tier]}40`
1405
+ },
1406
+ children: getTierLabel(tier)
1407
+ }
1408
+ );
1409
+ };
1410
+ const LicenseCard = ({ license, onSelect, onActivate, onDeactivate, selected }) => {
1411
+ const daysRemaining = getDaysUntilExpiration(license.expiresAt);
1412
+ const isExpiringSoon = daysRemaining !== null && daysRemaining <= 30 && daysRemaining > 0;
1413
+ return /* @__PURE__ */ jsxs(
1414
+ "div",
1415
+ {
1416
+ className: `rounded-lg border p-4 transition-all cursor-pointer hover:shadow-md ${selected ? "border-blue-500 ring-2 ring-blue-200" : "border-gray-200"}`,
1417
+ onClick: onSelect,
1418
+ children: [
1419
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between mb-3", children: [
1420
+ /* @__PURE__ */ jsxs("div", { children: [
1421
+ /* @__PURE__ */ jsx("div", { className: "font-mono text-sm text-gray-500 mb-1", children: license.key }),
1422
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1423
+ /* @__PURE__ */ jsx(LicenseTierBadge, { tier: license.tier, size: "sm" }),
1424
+ /* @__PURE__ */ jsx(LicenseStatusBadge, { status: license.status, size: "sm" })
1425
+ ] })
1426
+ ] }),
1427
+ /* @__PURE__ */ jsxs("div", { className: "text-right text-sm", children: [
1428
+ /* @__PURE__ */ jsx("div", { className: "text-gray-500", children: "Seats" }),
1429
+ /* @__PURE__ */ jsxs("div", { className: "font-semibold", children: [
1430
+ license.activeSeats,
1431
+ "/",
1432
+ license.maxSeats ?? "∞"
1433
+ ] })
1434
+ ] })
1435
+ ] }),
1436
+ /* @__PURE__ */ jsxs("div", { className: "text-sm text-gray-600 mb-3", children: [
1437
+ /* @__PURE__ */ jsxs("div", { children: [
1438
+ "Licensee: ",
1439
+ license.licensee
1440
+ ] }),
1441
+ /* @__PURE__ */ jsxs("div", { children: [
1442
+ "Email: ",
1443
+ license.email
1444
+ ] })
1445
+ ] }),
1446
+ license.expiresAt && /* @__PURE__ */ jsx("div", { className: `text-sm ${isExpiringSoon ? "text-orange-600" : "text-gray-500"}`, children: daysRemaining !== null && daysRemaining > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
1447
+ "Expires in ",
1448
+ daysRemaining,
1449
+ " days (",
1450
+ formatDate(license.expiresAt),
1451
+ ")"
1452
+ ] }) : daysRemaining === 0 ? /* @__PURE__ */ jsx("span", { className: "text-red-600", children: "Expires today" }) : /* @__PURE__ */ jsxs("span", { className: "text-red-600", children: [
1453
+ "Expired ",
1454
+ formatDate(license.expiresAt)
1455
+ ] }) }),
1456
+ (onActivate || onDeactivate) && /* @__PURE__ */ jsxs("div", { className: "flex gap-2 mt-3 pt-3 border-t", children: [
1457
+ onActivate && license.status === "valid" && /* @__PURE__ */ jsx(
1458
+ "button",
1459
+ {
1460
+ className: "px-3 py-1.5 text-sm bg-blue-500 text-white rounded hover:bg-blue-600",
1461
+ onClick: (e) => {
1462
+ e.stopPropagation();
1463
+ onActivate();
1464
+ },
1465
+ children: "Activate"
1466
+ }
1467
+ ),
1468
+ onDeactivate && /* @__PURE__ */ jsx(
1469
+ "button",
1470
+ {
1471
+ className: "px-3 py-1.5 text-sm bg-gray-200 text-gray-700 rounded hover:bg-gray-300",
1472
+ onClick: (e) => {
1473
+ e.stopPropagation();
1474
+ onDeactivate();
1475
+ },
1476
+ children: "Deactivate"
1477
+ }
1478
+ )
1479
+ ] })
1480
+ ]
1481
+ }
1482
+ );
1483
+ };
1484
+ const LicensePortal = (props) => {
1485
+ const { className, style, ...config } = props;
1486
+ const { state, activateLicense, deactivateLicense, selectLicense, setActiveTab, loadData } = useLicensePortal(config);
1487
+ if (state.loading) {
1488
+ return /* @__PURE__ */ jsx("div", { className: `nice-license-portal ${className || ""}`, style, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center p-8", children: [
1489
+ /* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500" }),
1490
+ /* @__PURE__ */ jsx("span", { className: "ml-3 text-gray-600", children: "Loading licenses..." })
1491
+ ] }) });
1492
+ }
1493
+ if (state.error) {
1494
+ return /* @__PURE__ */ jsx("div", { className: `nice-license-portal ${className || ""}`, style, children: /* @__PURE__ */ jsxs("div", { className: "p-4 bg-red-50 border border-red-200 rounded-lg", children: [
1495
+ /* @__PURE__ */ jsx("h3", { className: "text-red-800 font-semibold", children: "Error loading licenses" }),
1496
+ /* @__PURE__ */ jsx("p", { className: "text-red-600 text-sm mt-1", children: state.error.message }),
1497
+ /* @__PURE__ */ jsx(
1498
+ "button",
1499
+ {
1500
+ className: "mt-3 px-4 py-2 bg-red-100 text-red-700 rounded hover:bg-red-200",
1501
+ onClick: loadData,
1502
+ children: "Retry"
1503
+ }
1504
+ )
1505
+ ] }) });
1506
+ }
1507
+ const { overview } = state;
1508
+ if (!overview) {
1509
+ return null;
1510
+ }
1511
+ const tabs = [
1512
+ { id: "overview", label: "Overview" },
1513
+ { id: "licenses", label: "Licenses" },
1514
+ { id: "seats", label: "Seat Management" },
1515
+ ...config.showBilling ? [{ id: "billing", label: "Billing" }] : [],
1516
+ ...config.showAnalytics ? [{ id: "analytics", label: "Analytics" }] : [],
1517
+ { id: "settings", label: "Settings" }
1518
+ ];
1519
+ return /* @__PURE__ */ jsxs("div", { className: `nice-license-portal ${className || ""}`, style, children: [
1520
+ /* @__PURE__ */ jsxs("div", { className: "mb-6", children: [
1521
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold text-gray-900", children: "License Portal" }),
1522
+ /* @__PURE__ */ jsx("p", { className: "text-gray-600", children: "Manage your Nice2Dev licenses and subscriptions" })
1523
+ ] }),
1524
+ /* @__PURE__ */ jsx("div", { className: "border-b border-gray-200 mb-6", children: /* @__PURE__ */ jsx("nav", { className: "flex gap-6", children: tabs.map((tab) => /* @__PURE__ */ jsx(
1525
+ "button",
1526
+ {
1527
+ className: `pb-3 text-sm font-medium border-b-2 transition-colors ${state.activeTab === tab.id ? "border-blue-500 text-blue-600" : "border-transparent text-gray-500 hover:text-gray-700"}`,
1528
+ onClick: () => setActiveTab(tab.id),
1529
+ children: tab.label
1530
+ },
1531
+ tab.id
1532
+ )) }) }),
1533
+ state.activeTab === "overview" && /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
1534
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-4 gap-4", children: [
1535
+ /* @__PURE__ */ jsxs("div", { className: "bg-white p-4 rounded-lg border border-gray-200", children: [
1536
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-500", children: "Total Licenses" }),
1537
+ /* @__PURE__ */ jsx("div", { className: "text-2xl font-bold", children: overview.totalLicenses })
1538
+ ] }),
1539
+ /* @__PURE__ */ jsxs("div", { className: "bg-white p-4 rounded-lg border border-gray-200", children: [
1540
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-500", children: "Active" }),
1541
+ /* @__PURE__ */ jsx("div", { className: "text-2xl font-bold text-green-600", children: overview.activeLicenses })
1542
+ ] }),
1543
+ /* @__PURE__ */ jsxs("div", { className: "bg-white p-4 rounded-lg border border-gray-200", children: [
1544
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-500", children: "Seats Used" }),
1545
+ /* @__PURE__ */ jsxs("div", { className: "text-2xl font-bold", children: [
1546
+ overview.usedSeats,
1547
+ "/",
1548
+ overview.totalSeats
1549
+ ] })
1550
+ ] }),
1551
+ /* @__PURE__ */ jsxs("div", { className: "bg-white p-4 rounded-lg border border-gray-200", children: [
1552
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-500", children: "Expiring Soon" }),
1553
+ /* @__PURE__ */ jsx("div", { className: "text-2xl font-bold text-orange-600", children: overview.expiringSoon.length })
1554
+ ] })
1555
+ ] }),
1556
+ overview.expiringSoon.length > 0 && /* @__PURE__ */ jsxs("div", { className: "bg-orange-50 border border-orange-200 rounded-lg p-4", children: [
1557
+ /* @__PURE__ */ jsx("h3", { className: "font-semibold text-orange-800 mb-2", children: "Licenses Expiring Soon" }),
1558
+ /* @__PURE__ */ jsx("div", { className: "space-y-2", children: overview.expiringSoon.map((license) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm", children: [
1559
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-orange-700", children: license.key }),
1560
+ /* @__PURE__ */ jsxs("span", { className: "text-orange-600", children: [
1561
+ getDaysUntilExpiration(license.expiresAt),
1562
+ " days remaining"
1563
+ ] })
1564
+ ] }, license.key)) })
1565
+ ] }),
1566
+ /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg border border-gray-200", children: [
1567
+ /* @__PURE__ */ jsx("div", { className: "p-4 border-b border-gray-200", children: /* @__PURE__ */ jsx("h3", { className: "font-semibold", children: "Recent Activity" }) }),
1568
+ /* @__PURE__ */ jsxs("div", { className: "divide-y divide-gray-100", children: [
1569
+ state.actionHistory.slice(0, 5).map((action) => /* @__PURE__ */ jsxs("div", { className: "p-4 flex items-center justify-between", children: [
1570
+ /* @__PURE__ */ jsxs("div", { children: [
1571
+ /* @__PURE__ */ jsx("span", { className: "capitalize font-medium", children: action.type }),
1572
+ /* @__PURE__ */ jsx("span", { className: "text-gray-500 ml-2 font-mono text-sm", children: action.licenseKey })
1573
+ ] }),
1574
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-500", children: formatDate(action.timestamp) })
1575
+ ] }, action.id)),
1576
+ state.actionHistory.length === 0 && /* @__PURE__ */ jsx("div", { className: "p-4 text-gray-500 text-center", children: "No recent activity" })
1577
+ ] })
1578
+ ] })
1579
+ ] }),
1580
+ state.activeTab === "licenses" && /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: [
1581
+ overview.licenses.map((license) => {
1582
+ var _a;
1583
+ return /* @__PURE__ */ jsx(
1584
+ LicenseCard,
1585
+ {
1586
+ license,
1587
+ selected: ((_a = state.selectedLicense) == null ? void 0 : _a.key) === license.key,
1588
+ onSelect: () => selectLicense(license),
1589
+ onActivate: () => {
1590
+ activateLicense(license.key, "machine-id-here");
1591
+ },
1592
+ onDeactivate: () => {
1593
+ deactivateLicense(license.key, "machine-id-here");
1594
+ }
1595
+ },
1596
+ license.key
1597
+ );
1598
+ }),
1599
+ overview.licenses.length === 0 && /* @__PURE__ */ jsx("div", { className: "col-span-full text-center py-8 text-gray-500", children: "No licenses found. Contact support to purchase a license." })
1600
+ ] }),
1601
+ state.activeTab === "billing" && config.showBilling && /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
1602
+ state.subscription && /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-6", children: [
1603
+ /* @__PURE__ */ jsx("h3", { className: "font-semibold mb-4", children: "Current Subscription" }),
1604
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
1605
+ /* @__PURE__ */ jsxs("div", { children: [
1606
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-500", children: "Plan" }),
1607
+ /* @__PURE__ */ jsx("div", { className: "font-medium", children: state.subscription.plan })
1608
+ ] }),
1609
+ /* @__PURE__ */ jsxs("div", { children: [
1610
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-500", children: "Status" }),
1611
+ /* @__PURE__ */ jsx(
1612
+ "div",
1613
+ {
1614
+ className: `font-medium capitalize ${state.subscription.status === "active" ? "text-green-600" : "text-orange-600"}`,
1615
+ children: state.subscription.status
1616
+ }
1617
+ )
1618
+ ] }),
1619
+ /* @__PURE__ */ jsxs("div", { children: [
1620
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-500", children: "Current Period" }),
1621
+ /* @__PURE__ */ jsxs("div", { className: "font-medium", children: [
1622
+ formatDate(state.subscription.currentPeriodStart),
1623
+ " -",
1624
+ " ",
1625
+ formatDate(state.subscription.currentPeriodEnd)
1626
+ ] })
1627
+ ] }),
1628
+ /* @__PURE__ */ jsxs("div", { children: [
1629
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-500", children: "Seats" }),
1630
+ /* @__PURE__ */ jsx("div", { className: "font-medium", children: state.subscription.seats })
1631
+ ] })
1632
+ ] })
1633
+ ] }),
1634
+ /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg border border-gray-200", children: [
1635
+ /* @__PURE__ */ jsx("div", { className: "p-4 border-b border-gray-200", children: /* @__PURE__ */ jsx("h3", { className: "font-semibold", children: "Invoices" }) }),
1636
+ /* @__PURE__ */ jsxs("div", { className: "divide-y divide-gray-100", children: [
1637
+ state.invoices.map((invoice) => /* @__PURE__ */ jsxs("div", { className: "p-4 flex items-center justify-between", children: [
1638
+ /* @__PURE__ */ jsxs("div", { children: [
1639
+ /* @__PURE__ */ jsx("div", { className: "font-medium", children: invoice.number }),
1640
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-500", children: formatDate(invoice.issuedAt) })
1641
+ ] }),
1642
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
1643
+ /* @__PURE__ */ jsx(
1644
+ "span",
1645
+ {
1646
+ className: `px-2 py-1 rounded text-sm ${invoice.status === "paid" ? "bg-green-100 text-green-700" : "bg-gray-100 text-gray-700"}`,
1647
+ children: invoice.status
1648
+ }
1649
+ ),
1650
+ /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
1651
+ invoice.currency,
1652
+ " ",
1653
+ invoice.amount.toFixed(2)
1654
+ ] }),
1655
+ invoice.downloadUrl && /* @__PURE__ */ jsx(
1656
+ "a",
1657
+ {
1658
+ href: invoice.downloadUrl,
1659
+ className: "text-blue-500 hover:text-blue-600",
1660
+ target: "_blank",
1661
+ rel: "noopener noreferrer",
1662
+ children: "Download"
1663
+ }
1664
+ )
1665
+ ] })
1666
+ ] }, invoice.id)),
1667
+ state.invoices.length === 0 && /* @__PURE__ */ jsx("div", { className: "p-4 text-gray-500 text-center", children: "No invoices" })
1668
+ ] })
1669
+ ] })
1670
+ ] }),
1671
+ state.activeTab === "seats" && /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-6", children: [
1672
+ /* @__PURE__ */ jsx("h3", { className: "font-semibold mb-4", children: "Seat Management" }),
1673
+ /* @__PURE__ */ jsx("p", { className: "text-gray-500 mb-4", children: "Select a license to manage seat assignments." })
1674
+ ] }),
1675
+ state.activeTab === "settings" && /* @__PURE__ */ jsx("div", { className: "bg-white rounded-lg border border-gray-200 p-6", children: /* @__PURE__ */ jsx("h3", { className: "font-semibold mb-4", children: "Portal Settings" }) })
1676
+ ] });
1677
+ };
1678
+ class ActivationService {
1679
+ constructor(config) {
1680
+ this.config = config;
1681
+ }
1682
+ async request(endpoint, options = {}) {
1683
+ const url = `${this.config.apiBaseUrl}${endpoint}`;
1684
+ const headers = {
1685
+ "Content-Type": "application/json",
1686
+ ...this.config.authToken && { Authorization: `Bearer ${this.config.authToken}` }
1687
+ };
1688
+ const response = await fetch(url, { ...options, headers });
1689
+ if (!response.ok) {
1690
+ const error = await response.json().catch(() => ({}));
1691
+ throw {
1692
+ code: `HTTP_${response.status}`,
1693
+ message: error.message || "Request failed",
1694
+ retryable: response.status >= 500,
1695
+ supportRequired: response.status === 403
1696
+ };
1697
+ }
1698
+ return response.json();
1699
+ }
1700
+ async validateKey(licenseKey) {
1701
+ return this.request("/licenses/validate", {
1702
+ method: "POST",
1703
+ body: JSON.stringify({ licenseKey })
1704
+ });
1705
+ }
1706
+ async checkHardware(licenseKey, machineId, fingerprint) {
1707
+ return this.request("/licenses/hardware-check", {
1708
+ method: "POST",
1709
+ body: JSON.stringify({ licenseKey, machineId, fingerprint })
1710
+ });
1711
+ }
1712
+ async activate(licenseKey, machineId, fingerprint, machineName) {
1713
+ return this.request(`/licenses/${encodeURIComponent(licenseKey)}/activate`, {
1714
+ method: "POST",
1715
+ body: JSON.stringify({ machineId, fingerprint, machineName })
1716
+ });
1717
+ }
1718
+ async getOfflineChallenge(licenseKey, machineId) {
1719
+ const result = await this.request("/licenses/offline/challenge", {
1720
+ method: "POST",
1721
+ body: JSON.stringify({ licenseKey, machineId })
1722
+ });
1723
+ return result.challenge;
1724
+ }
1725
+ async activateOffline(licenseKey, machineId, response) {
1726
+ return this.request("/licenses/offline/activate", {
1727
+ method: "POST",
1728
+ body: JSON.stringify({ licenseKey, machineId, response })
1729
+ });
1730
+ }
1731
+ }
1732
+ function formatLicenseKey(key) {
1733
+ const clean = key.replace(/[^A-Za-z0-9]/g, "").toUpperCase();
1734
+ const parts = [];
1735
+ for (let i2 = 0; i2 < clean.length; i2 += 4) {
1736
+ parts.push(clean.slice(i2, i2 + 4));
1737
+ }
1738
+ return parts.slice(0, 5).join("-");
1739
+ }
1740
+ function validateKeyFormat(key) {
1741
+ const pattern = /^[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/;
1742
+ return pattern.test(key);
1743
+ }
1744
+ async function getDefaultMachineName() {
1745
+ if (typeof window !== "undefined" && "userAgent" in navigator) {
1746
+ const ua = navigator.userAgent;
1747
+ if (ua.includes("Windows")) {
1748
+ return "Windows PC";
1749
+ }
1750
+ if (ua.includes("Mac")) {
1751
+ return "Mac";
1752
+ }
1753
+ if (ua.includes("Linux")) {
1754
+ return "Linux Machine";
1755
+ }
1756
+ if (ua.includes("Android")) {
1757
+ return "Android Device";
1758
+ }
1759
+ if (ua.includes("iOS")) {
1760
+ return "iOS Device";
1761
+ }
1762
+ }
1763
+ return "Unknown Device";
1764
+ }
1765
+ function useActivationWizard(config) {
1766
+ const [state, setState] = useState({
1767
+ step: "enter-key",
1768
+ licenseKey: "",
1769
+ validation: null,
1770
+ machineId: null,
1771
+ fingerprint: null,
1772
+ machineName: config.machineName || "",
1773
+ termsAccepted: false,
1774
+ error: null,
1775
+ activatedLicense: null,
1776
+ offlineChallenge: null
1777
+ });
1778
+ const service = React.useMemo(() => new ActivationService(config), [config]);
1779
+ const setLicenseKey = useCallback((key) => {
1780
+ const formatted = formatLicenseKey(key);
1781
+ setState((s2) => ({ ...s2, licenseKey: formatted, error: null }));
1782
+ }, []);
1783
+ const setMachineName = useCallback((name) => {
1784
+ setState((s2) => ({ ...s2, machineName: name }));
1785
+ }, []);
1786
+ const setTermsAccepted = useCallback((accepted) => {
1787
+ setState((s2) => ({ ...s2, termsAccepted: accepted }));
1788
+ }, []);
1789
+ const validateKey = useCallback(async () => {
1790
+ if (!validateKeyFormat(state.licenseKey)) {
1791
+ setState((s2) => ({
1792
+ ...s2,
1793
+ error: {
1794
+ code: "INVALID_FORMAT",
1795
+ message: "License key format is invalid. Expected format: XXXX-XXXX-XXXX-XXXX-XXXX",
1796
+ retryable: true
1797
+ }
1798
+ }));
1799
+ return;
1800
+ }
1801
+ setState((s2) => ({ ...s2, step: "validate", error: null }));
1802
+ try {
1803
+ const validation = await service.validateKey(state.licenseKey);
1804
+ if (!validation.valid) {
1805
+ setState((s2) => ({
1806
+ ...s2,
1807
+ step: "error",
1808
+ error: {
1809
+ code: validation.errorCode || "VALIDATION_FAILED",
1810
+ message: validation.errorMessage || "License key validation failed",
1811
+ retryable: true
1812
+ }
1813
+ }));
1814
+ return;
1815
+ }
1816
+ const fingerprintData = await generateFingerprint();
1817
+ const machineId = await generateMachineId();
1818
+ const machineName = state.machineName || await getDefaultMachineName();
1819
+ setState((s2) => ({
1820
+ ...s2,
1821
+ validation,
1822
+ machineId,
1823
+ fingerprint: fingerprintData,
1824
+ machineName,
1825
+ step: "hardware-check"
1826
+ }));
1827
+ const hwCheck = await service.checkHardware(state.licenseKey, machineId, fingerprintData);
1828
+ if (!hwCheck.allowed) {
1829
+ setState((s2) => ({
1830
+ ...s2,
1831
+ step: "error",
1832
+ error: {
1833
+ code: "HARDWARE_LIMIT",
1834
+ message: hwCheck.reason || "Maximum hardware activations reached",
1835
+ retryable: false,
1836
+ supportRequired: true
1837
+ }
1838
+ }));
1839
+ return;
1840
+ }
1841
+ setState((s2) => ({ ...s2, step: "terms" }));
1842
+ } catch (error) {
1843
+ setState((s2) => ({
1844
+ ...s2,
1845
+ step: "error",
1846
+ error
1847
+ }));
1848
+ }
1849
+ }, [state.licenseKey, state.machineName, service]);
1850
+ const acceptTermsAndActivate = useCallback(async () => {
1851
+ var _a, _b;
1852
+ if (!state.termsAccepted) {
1853
+ setState((s2) => ({
1854
+ ...s2,
1855
+ error: {
1856
+ code: "TERMS_NOT_ACCEPTED",
1857
+ message: "Please accept the terms of service to continue",
1858
+ retryable: true
1859
+ }
1860
+ }));
1861
+ return;
1862
+ }
1863
+ setState((s2) => ({ ...s2, step: "activating", error: null }));
1864
+ try {
1865
+ const license = await service.activate(
1866
+ state.licenseKey,
1867
+ state.machineId,
1868
+ state.fingerprint,
1869
+ state.machineName
1870
+ );
1871
+ setState((s2) => ({
1872
+ ...s2,
1873
+ step: "success",
1874
+ activatedLicense: license
1875
+ }));
1876
+ (_a = config.onActivationSuccess) == null ? void 0 : _a.call(config, license);
1877
+ } catch (error) {
1878
+ const activationError = error;
1879
+ setState((s2) => ({
1880
+ ...s2,
1881
+ step: "error",
1882
+ error: activationError
1883
+ }));
1884
+ (_b = config.onActivationError) == null ? void 0 : _b.call(config, activationError);
1885
+ }
1886
+ }, [state, service, config]);
1887
+ const startOfflineActivation = useCallback(async () => {
1888
+ if (!config.allowOfflineActivation) {
1889
+ return;
1890
+ }
1891
+ try {
1892
+ const machineId = state.machineId || await generateMachineId();
1893
+ const challenge = await service.getOfflineChallenge(state.licenseKey, machineId);
1894
+ setState((s2) => ({
1895
+ ...s2,
1896
+ machineId,
1897
+ offlineChallenge: challenge
1898
+ }));
1899
+ } catch (error) {
1900
+ setState((s2) => ({
1901
+ ...s2,
1902
+ error
1903
+ }));
1904
+ }
1905
+ }, [state.licenseKey, state.machineId, service, config.allowOfflineActivation]);
1906
+ const submitOfflineResponse = useCallback(
1907
+ async (response) => {
1908
+ var _a, _b;
1909
+ setState((s2) => ({ ...s2, step: "activating", error: null }));
1910
+ try {
1911
+ const license = await service.activateOffline(state.licenseKey, state.machineId, response);
1912
+ setState((s2) => ({
1913
+ ...s2,
1914
+ step: "success",
1915
+ activatedLicense: license
1916
+ }));
1917
+ (_a = config.onActivationSuccess) == null ? void 0 : _a.call(config, license);
1918
+ } catch (error) {
1919
+ const activationError = error;
1920
+ setState((s2) => ({
1921
+ ...s2,
1922
+ step: "error",
1923
+ error: activationError
1924
+ }));
1925
+ (_b = config.onActivationError) == null ? void 0 : _b.call(config, activationError);
1926
+ }
1927
+ },
1928
+ [state.licenseKey, state.machineId, service, config]
1929
+ );
1930
+ const reset = useCallback(() => {
1931
+ setState({
1932
+ step: "enter-key",
1933
+ licenseKey: "",
1934
+ validation: null,
1935
+ machineId: null,
1936
+ fingerprint: null,
1937
+ machineName: config.machineName || "",
1938
+ termsAccepted: false,
1939
+ error: null,
1940
+ activatedLicense: null,
1941
+ offlineChallenge: null
1942
+ });
1943
+ }, [config.machineName]);
1944
+ const goBack = useCallback(() => {
1945
+ setState((s2) => {
1946
+ const stepOrder = [
1947
+ "enter-key",
1948
+ "validate",
1949
+ "hardware-check",
1950
+ "terms",
1951
+ "activating",
1952
+ "success"
1953
+ ];
1954
+ const currentIndex = stepOrder.indexOf(s2.step);
1955
+ if (currentIndex > 0) {
1956
+ return { ...s2, step: stepOrder[currentIndex - 1], error: null };
1957
+ }
1958
+ return s2;
1959
+ });
1960
+ }, []);
1961
+ return {
1962
+ state,
1963
+ setLicenseKey,
1964
+ setMachineName,
1965
+ setTermsAccepted,
1966
+ validateKey,
1967
+ acceptTermsAndActivate,
1968
+ startOfflineActivation,
1969
+ submitOfflineResponse,
1970
+ reset,
1971
+ goBack
1972
+ };
1973
+ }
1974
+ const StepIndicator = ({ currentStep }) => {
1975
+ const steps = [
1976
+ { id: "enter-key", label: "Enter Key" },
1977
+ { id: "validate", label: "Validate" },
1978
+ { id: "hardware-check", label: "Hardware" },
1979
+ { id: "terms", label: "Terms" },
1980
+ { id: "activating", label: "Activate" },
1981
+ { id: "success", label: "Done" }
1982
+ ];
1983
+ const stepOrder = [
1984
+ "enter-key",
1985
+ "validate",
1986
+ "hardware-check",
1987
+ "terms",
1988
+ "activating",
1989
+ "success"
1990
+ ];
1991
+ const currentIndex = stepOrder.indexOf(currentStep);
1992
+ return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between mb-8", children: steps.map((step, index) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
1993
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center", children: [
1994
+ /* @__PURE__ */ jsx(
1995
+ "div",
1996
+ {
1997
+ className: `w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium ${index < currentIndex ? "bg-green-500 text-white" : index === currentIndex ? "bg-blue-500 text-white" : "bg-gray-200 text-gray-500"}`,
1998
+ children: index < currentIndex ? "✓" : index + 1
1999
+ }
2000
+ ),
2001
+ /* @__PURE__ */ jsx("span", { className: "text-xs mt-1 text-gray-500", children: step.label })
2002
+ ] }),
2003
+ index < steps.length - 1 && /* @__PURE__ */ jsx(
2004
+ "div",
2005
+ {
2006
+ className: `flex-1 h-0.5 mx-2 ${index < currentIndex ? "bg-green-500" : "bg-gray-200"}`
2007
+ }
2008
+ )
2009
+ ] }, step.id)) });
2010
+ };
2011
+ const ActivationWizard = (props) => {
2012
+ var _a;
2013
+ const { className, style, ...config } = props;
2014
+ const {
2015
+ state,
2016
+ setLicenseKey,
2017
+ setMachineName,
2018
+ setTermsAccepted,
2019
+ validateKey,
2020
+ acceptTermsAndActivate,
2021
+ startOfflineActivation,
2022
+ reset,
2023
+ goBack
2024
+ } = useActivationWizard(config);
2025
+ const [offlineResponse, setOfflineResponse] = useState("");
2026
+ return /* @__PURE__ */ jsxs(
2027
+ "div",
2028
+ {
2029
+ className: `nice-activation-wizard bg-white rounded-lg shadow-lg p-8 max-w-xl mx-auto ${className || ""}`,
2030
+ style,
2031
+ children: [
2032
+ /* @__PURE__ */ jsxs("div", { className: "text-center mb-6", children: [
2033
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold text-gray-900", children: "Activate License" }),
2034
+ /* @__PURE__ */ jsx("p", { className: "text-gray-600 mt-1", children: "Follow the steps to activate your Nice2Dev license" })
2035
+ ] }),
2036
+ state.step !== "error" && /* @__PURE__ */ jsx(StepIndicator, { currentStep: state.step }),
2037
+ state.step === "error" && state.error && /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
2038
+ /* @__PURE__ */ jsxs("div", { className: "bg-red-50 border border-red-200 rounded-lg p-6 text-center", children: [
2039
+ /* @__PURE__ */ jsx("div", { className: "text-red-500 text-4xl mb-3", children: "⚠️" }),
2040
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-red-800", children: "Activation Failed" }),
2041
+ /* @__PURE__ */ jsx("p", { className: "text-red-600 mt-2", children: state.error.message }),
2042
+ state.error.supportRequired && config.supportEmail && /* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-600 mt-4", children: [
2043
+ "Please contact support at",
2044
+ " ",
2045
+ /* @__PURE__ */ jsx("a", { href: `mailto:${config.supportEmail}`, className: "text-blue-500 hover:underline", children: config.supportEmail })
2046
+ ] })
2047
+ ] }),
2048
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3 justify-center", children: [
2049
+ state.error.retryable && /* @__PURE__ */ jsx(
2050
+ "button",
2051
+ {
2052
+ onClick: () => goBack(),
2053
+ className: "px-6 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600",
2054
+ children: "Try Again"
2055
+ }
2056
+ ),
2057
+ /* @__PURE__ */ jsx(
2058
+ "button",
2059
+ {
2060
+ onClick: reset,
2061
+ className: "px-6 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300",
2062
+ children: "Start Over"
2063
+ }
2064
+ )
2065
+ ] })
2066
+ ] }),
2067
+ state.step === "enter-key" && /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
2068
+ /* @__PURE__ */ jsxs("div", { children: [
2069
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "License Key" }),
2070
+ /* @__PURE__ */ jsx(
2071
+ "input",
2072
+ {
2073
+ type: "text",
2074
+ value: state.licenseKey,
2075
+ onChange: (e) => setLicenseKey(e.target.value),
2076
+ placeholder: "XXXX-XXXX-XXXX-XXXX-XXXX",
2077
+ className: "w-full px-4 py-3 border border-gray-300 rounded-lg font-mono text-center text-lg tracking-wider focus:ring-2 focus:ring-blue-500 focus:border-blue-500",
2078
+ maxLength: 29
2079
+ }
2080
+ ),
2081
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 mt-2", children: "Enter the license key you received upon purchase" })
2082
+ ] }),
2083
+ /* @__PURE__ */ jsxs("div", { children: [
2084
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Machine Name (optional)" }),
2085
+ /* @__PURE__ */ jsx(
2086
+ "input",
2087
+ {
2088
+ type: "text",
2089
+ value: state.machineName,
2090
+ onChange: (e) => setMachineName(e.target.value),
2091
+ placeholder: "My Work PC",
2092
+ className: "w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
2093
+ }
2094
+ )
2095
+ ] }),
2096
+ state.error && /* @__PURE__ */ jsx("div", { className: "text-red-600 text-sm bg-red-50 p-3 rounded-lg", children: state.error.message }),
2097
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
2098
+ /* @__PURE__ */ jsx(
2099
+ "button",
2100
+ {
2101
+ onClick: validateKey,
2102
+ disabled: !state.licenseKey,
2103
+ className: "flex-1 px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600 disabled:bg-gray-300 disabled:cursor-not-allowed",
2104
+ children: "Continue"
2105
+ }
2106
+ ),
2107
+ config.onCancel && /* @__PURE__ */ jsx(
2108
+ "button",
2109
+ {
2110
+ onClick: config.onCancel,
2111
+ className: "px-6 py-3 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300",
2112
+ children: "Cancel"
2113
+ }
2114
+ )
2115
+ ] }),
2116
+ config.allowOfflineActivation && /* @__PURE__ */ jsx(
2117
+ "button",
2118
+ {
2119
+ onClick: startOfflineActivation,
2120
+ className: "w-full text-sm text-blue-500 hover:underline",
2121
+ children: "Activate offline →"
2122
+ }
2123
+ )
2124
+ ] }),
2125
+ (state.step === "validate" || state.step === "hardware-check") && /* @__PURE__ */ jsxs("div", { className: "text-center py-8", children: [
2126
+ /* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500 mx-auto" }),
2127
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold mt-4", children: state.step === "validate" ? "Validating License..." : "Checking Hardware..." }),
2128
+ /* @__PURE__ */ jsx("p", { className: "text-gray-500 mt-2", children: state.step === "validate" ? "Verifying your license key with the server" : "Collecting hardware information for activation" })
2129
+ ] }),
2130
+ state.step === "terms" && /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
2131
+ /* @__PURE__ */ jsxs("div", { className: "bg-gray-50 rounded-lg p-4", children: [
2132
+ /* @__PURE__ */ jsx("h3", { className: "font-semibold mb-3", children: "License Details" }),
2133
+ ((_a = state.validation) == null ? void 0 : _a.license) && /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2 text-sm", children: [
2134
+ /* @__PURE__ */ jsx("div", { className: "text-gray-500", children: "Tier:" }),
2135
+ /* @__PURE__ */ jsx("div", { className: "font-medium capitalize", children: state.validation.license.tier }),
2136
+ /* @__PURE__ */ jsx("div", { className: "text-gray-500", children: "Licensee:" }),
2137
+ /* @__PURE__ */ jsx("div", { className: "font-medium", children: state.validation.license.licensee }),
2138
+ /* @__PURE__ */ jsx("div", { className: "text-gray-500", children: "Max Machines:" }),
2139
+ /* @__PURE__ */ jsx("div", { className: "font-medium", children: state.validation.license.maxMachines }),
2140
+ /* @__PURE__ */ jsx("div", { className: "text-gray-500", children: "This Machine:" }),
2141
+ /* @__PURE__ */ jsx("div", { className: "font-medium", children: state.machineName })
2142
+ ] })
2143
+ ] }),
2144
+ /* @__PURE__ */ jsxs("div", { className: "border border-gray-200 rounded-lg p-4 max-h-48 overflow-y-auto text-sm text-gray-600", children: [
2145
+ /* @__PURE__ */ jsx("h4", { className: "font-semibold text-gray-800 mb-2", children: "Terms of Service" }),
2146
+ /* @__PURE__ */ jsx("p", { children: "By activating this license, you agree to the Nice2Dev Software License Agreement. This license grants you the right to use the software on the specified number of machines according to your license tier. Unauthorized redistribution, reverse engineering, or violation of these terms may result in license revocation." })
2147
+ ] }),
2148
+ /* @__PURE__ */ jsxs("label", { className: "flex items-start gap-3 cursor-pointer", children: [
2149
+ /* @__PURE__ */ jsx(
2150
+ "input",
2151
+ {
2152
+ type: "checkbox",
2153
+ checked: state.termsAccepted,
2154
+ onChange: (e) => setTermsAccepted(e.target.checked),
2155
+ className: "mt-1 w-4 h-4 rounded border-gray-300 text-blue-500 focus:ring-blue-500"
2156
+ }
2157
+ ),
2158
+ /* @__PURE__ */ jsxs("span", { className: "text-sm text-gray-700", children: [
2159
+ "I accept the",
2160
+ " ",
2161
+ config.termsUrl ? /* @__PURE__ */ jsx(
2162
+ "a",
2163
+ {
2164
+ href: config.termsUrl,
2165
+ className: "text-blue-500 hover:underline",
2166
+ target: "_blank",
2167
+ rel: "noopener noreferrer",
2168
+ children: "Terms of Service"
2169
+ }
2170
+ ) : "Terms of Service",
2171
+ config.privacyUrl && /* @__PURE__ */ jsxs(Fragment, { children: [
2172
+ " ",
2173
+ "and",
2174
+ " ",
2175
+ /* @__PURE__ */ jsx(
2176
+ "a",
2177
+ {
2178
+ href: config.privacyUrl,
2179
+ className: "text-blue-500 hover:underline",
2180
+ target: "_blank",
2181
+ rel: "noopener noreferrer",
2182
+ children: "Privacy Policy"
2183
+ }
2184
+ )
2185
+ ] })
2186
+ ] })
2187
+ ] }),
2188
+ state.error && /* @__PURE__ */ jsx("div", { className: "text-red-600 text-sm bg-red-50 p-3 rounded-lg", children: state.error.message }),
2189
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
2190
+ /* @__PURE__ */ jsx(
2191
+ "button",
2192
+ {
2193
+ onClick: goBack,
2194
+ className: "px-6 py-3 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300",
2195
+ children: "Back"
2196
+ }
2197
+ ),
2198
+ /* @__PURE__ */ jsx(
2199
+ "button",
2200
+ {
2201
+ onClick: acceptTermsAndActivate,
2202
+ disabled: !state.termsAccepted,
2203
+ className: "flex-1 px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600 disabled:bg-gray-300 disabled:cursor-not-allowed",
2204
+ children: "Activate License"
2205
+ }
2206
+ )
2207
+ ] })
2208
+ ] }),
2209
+ state.step === "activating" && /* @__PURE__ */ jsxs("div", { className: "text-center py-8", children: [
2210
+ /* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500 mx-auto" }),
2211
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold mt-4", children: "Activating License..." }),
2212
+ /* @__PURE__ */ jsx("p", { className: "text-gray-500 mt-2", children: "Please wait while we activate your license" })
2213
+ ] }),
2214
+ state.step === "success" && state.activatedLicense && /* @__PURE__ */ jsxs("div", { className: "text-center space-y-6", children: [
2215
+ /* @__PURE__ */ jsx("div", { className: "text-green-500 text-6xl", children: "✓" }),
2216
+ /* @__PURE__ */ jsxs("div", { children: [
2217
+ /* @__PURE__ */ jsx("h2", { className: "text-xl font-bold text-gray-900", children: "License Activated!" }),
2218
+ /* @__PURE__ */ jsx("p", { className: "text-gray-600 mt-2", children: "Your license has been successfully activated on this machine." })
2219
+ ] }),
2220
+ /* @__PURE__ */ jsx("div", { className: "bg-green-50 rounded-lg p-4 text-left", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2 text-sm", children: [
2221
+ /* @__PURE__ */ jsx("div", { className: "text-gray-500", children: "License Key:" }),
2222
+ /* @__PURE__ */ jsx("div", { className: "font-mono", children: state.activatedLicense.key }),
2223
+ /* @__PURE__ */ jsx("div", { className: "text-gray-500", children: "Tier:" }),
2224
+ /* @__PURE__ */ jsx("div", { className: "capitalize", children: state.activatedLicense.tier }),
2225
+ /* @__PURE__ */ jsx("div", { className: "text-gray-500", children: "Machine:" }),
2226
+ /* @__PURE__ */ jsx("div", { children: state.machineName }),
2227
+ state.activatedLicense.expiresAt && /* @__PURE__ */ jsxs(Fragment, { children: [
2228
+ /* @__PURE__ */ jsx("div", { className: "text-gray-500", children: "Expires:" }),
2229
+ /* @__PURE__ */ jsx("div", { children: new Date(state.activatedLicense.expiresAt).toLocaleDateString() })
2230
+ ] })
2231
+ ] }) }),
2232
+ /* @__PURE__ */ jsx(
2233
+ "button",
2234
+ {
2235
+ onClick: () => {
2236
+ var _a2;
2237
+ reset();
2238
+ (_a2 = config.onCancel) == null ? void 0 : _a2.call(config);
2239
+ },
2240
+ className: "px-8 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600",
2241
+ children: "Done"
2242
+ }
2243
+ )
2244
+ ] }),
2245
+ state.offlineChallenge && /* @__PURE__ */ jsx("div", { className: "fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50", children: /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg p-6 max-w-md w-full", children: [
2246
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold mb-4", children: "Offline Activation" }),
2247
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
2248
+ /* @__PURE__ */ jsxs("div", { children: [
2249
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: "Challenge Code (send this to support):" }),
2250
+ /* @__PURE__ */ jsx(
2251
+ "textarea",
2252
+ {
2253
+ readOnly: true,
2254
+ value: state.offlineChallenge,
2255
+ className: "w-full px-3 py-2 border border-gray-300 rounded font-mono text-xs h-20"
2256
+ }
2257
+ )
2258
+ ] }),
2259
+ /* @__PURE__ */ jsxs("div", { children: [
2260
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: "Response Code (from support):" }),
2261
+ /* @__PURE__ */ jsx(
2262
+ "textarea",
2263
+ {
2264
+ value: offlineResponse,
2265
+ onChange: (e) => setOfflineResponse(e.target.value),
2266
+ placeholder: "Paste the response code here...",
2267
+ className: "w-full px-3 py-2 border border-gray-300 rounded font-mono text-xs h-20"
2268
+ }
2269
+ )
2270
+ ] }),
2271
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
2272
+ /* @__PURE__ */ jsx(
2273
+ "button",
2274
+ {
2275
+ onClick: () => {
2276
+ },
2277
+ disabled: !offlineResponse,
2278
+ className: "flex-1 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 disabled:bg-gray-300",
2279
+ children: "Activate"
2280
+ }
2281
+ ),
2282
+ /* @__PURE__ */ jsx(
2283
+ "button",
2284
+ {
2285
+ onClick: () => {
2286
+ setOfflineResponse("");
2287
+ },
2288
+ className: "px-4 py-2 bg-gray-200 text-gray-700 rounded hover:bg-gray-300",
2289
+ children: "Cancel"
2290
+ }
2291
+ )
2292
+ ] })
2293
+ ] })
2294
+ ] }) })
2295
+ ]
2296
+ }
2297
+ );
2298
+ };
2299
+ class LicenseTransferService {
2300
+ constructor(config) {
2301
+ this.config = config;
2302
+ }
2303
+ async request(endpoint, options = {}) {
2304
+ const url = `${this.config.apiBaseUrl}${endpoint}`;
2305
+ const headers = {
2306
+ "Content-Type": "application/json",
2307
+ ...this.config.authToken && { Authorization: `Bearer ${this.config.authToken}` }
2308
+ };
2309
+ const response = await fetch(url, { ...options, headers });
2310
+ if (!response.ok) {
2311
+ const error = await response.json().catch(() => ({}));
2312
+ throw {
2313
+ code: `HTTP_${response.status}`,
2314
+ message: error.message || "Request failed"
2315
+ };
2316
+ }
2317
+ return response.json();
2318
+ }
2319
+ /** Get transferable licenses */
2320
+ async getTransferableLicenses() {
2321
+ return this.request("/licenses/transferable");
2322
+ }
2323
+ /** Validate recipient email/organization */
2324
+ async validateRecipient(email) {
2325
+ return this.request("/transfers/validate-recipient", {
2326
+ method: "POST",
2327
+ body: JSON.stringify({ email })
2328
+ });
2329
+ }
2330
+ /** Initiate transfer */
2331
+ async initiateTransfer(data) {
2332
+ return this.request("/transfers", {
2333
+ method: "POST",
2334
+ body: JSON.stringify(data)
2335
+ });
2336
+ }
2337
+ /** Get transfer by ID */
2338
+ async getTransfer(transferId) {
2339
+ return this.request(`/transfers/${transferId}`);
2340
+ }
2341
+ /** Get pending transfers */
2342
+ async getPendingTransfers() {
2343
+ return this.request("/transfers?status=pending,pending_approval");
2344
+ }
2345
+ /** Get transfer history */
2346
+ async getTransferHistory(licenseKey) {
2347
+ const params = licenseKey ? `?licenseKey=${encodeURIComponent(licenseKey)}` : "";
2348
+ return this.request(`/transfers/history${params}`);
2349
+ }
2350
+ /** Cancel transfer */
2351
+ async cancelTransfer(transferId, reason) {
2352
+ return this.request(`/transfers/${transferId}/cancel`, {
2353
+ method: "POST",
2354
+ body: JSON.stringify({ reason })
2355
+ });
2356
+ }
2357
+ /** Accept transfer (as recipient) */
2358
+ async acceptTransfer(transferId, token) {
2359
+ return this.request(`/transfers/${transferId}/accept`, {
2360
+ method: "POST",
2361
+ body: JSON.stringify({ token })
2362
+ });
2363
+ }
2364
+ /** Approve transfer (admin) */
2365
+ async approveTransfer(transferId) {
2366
+ return this.request(`/transfers/${transferId}/approve`, {
2367
+ method: "POST"
2368
+ });
2369
+ }
2370
+ /** Reject transfer (admin) */
2371
+ async rejectTransfer(transferId, reason) {
2372
+ return this.request(`/transfers/${transferId}/reject`, {
2373
+ method: "POST",
2374
+ body: JSON.stringify({ reason })
2375
+ });
2376
+ }
2377
+ }
2378
+ function getTransferStatusColor(status) {
2379
+ const colors = {
2380
+ pending: "#f59e0b",
2381
+ pending_approval: "#8b5cf6",
2382
+ approved: "#3b82f6",
2383
+ rejected: "#ef4444",
2384
+ completed: "#22c55e",
2385
+ cancelled: "#6b7280",
2386
+ expired: "#9ca3af"
2387
+ };
2388
+ return colors[status];
2389
+ }
2390
+ function getTransferStatusLabel(status) {
2391
+ const labels = {
2392
+ pending: "Pending Acceptance",
2393
+ pending_approval: "Awaiting Approval",
2394
+ approved: "Approved",
2395
+ rejected: "Rejected",
2396
+ completed: "Completed",
2397
+ cancelled: "Cancelled",
2398
+ expired: "Expired"
2399
+ };
2400
+ return labels[status];
2401
+ }
2402
+ const TRANSFER_REASONS = [
2403
+ { value: "employee_change", label: "Employee leaving/joining" },
2404
+ { value: "department_transfer", label: "Department transfer" },
2405
+ { value: "organization_sale", label: "Organization sale/merger" },
2406
+ { value: "other", label: "Other" }
2407
+ ];
2408
+ function useLicenseTransfer(config) {
2409
+ const [step, setStep] = useState("select-license");
2410
+ const [licenses, setLicenses] = useState([]);
2411
+ const [formData, setFormData] = useState({
2412
+ licenseKey: "",
2413
+ recipientEmail: "",
2414
+ recipientName: "",
2415
+ notes: "",
2416
+ transferReason: "employee_change",
2417
+ confirmDeactivation: false
2418
+ });
2419
+ const [selectedLicense, setSelectedLicense] = useState(null);
2420
+ const [recipientValidation, setRecipientValidation] = useState(null);
2421
+ const [transfer, setTransfer] = useState(null);
2422
+ const [loading, setLoading] = useState(false);
2423
+ const [error, setError] = useState(null);
2424
+ const service = React.useMemo(() => new LicenseTransferService(config), [config]);
2425
+ const loadLicenses = useCallback(async () => {
2426
+ var _a;
2427
+ setLoading(true);
2428
+ setError(null);
2429
+ try {
2430
+ const data = await service.getTransferableLicenses();
2431
+ setLicenses(data);
2432
+ } catch (err) {
2433
+ setError(err);
2434
+ (_a = config.onError) == null ? void 0 : _a.call(config, err);
2435
+ } finally {
2436
+ setLoading(false);
2437
+ }
2438
+ }, [service, config]);
2439
+ const selectLicense = useCallback((license) => {
2440
+ setSelectedLicense(license);
2441
+ setFormData((f2) => ({ ...f2, licenseKey: license.key }));
2442
+ setStep("enter-recipient");
2443
+ }, []);
2444
+ const validateRecipient = useCallback(async () => {
2445
+ if (!formData.recipientEmail) {
2446
+ return;
2447
+ }
2448
+ setLoading(true);
2449
+ try {
2450
+ const result = await service.validateRecipient(formData.recipientEmail);
2451
+ setRecipientValidation({
2452
+ valid: result.valid,
2453
+ isExternal: result.isExternal,
2454
+ warnings: result.warnings
2455
+ });
2456
+ if (result.valid && result.recipientInfo) {
2457
+ setFormData((f2) => {
2458
+ var _a;
2459
+ return {
2460
+ ...f2,
2461
+ recipientName: ((_a = result.recipientInfo) == null ? void 0 : _a.name) || f2.recipientName
2462
+ };
2463
+ });
2464
+ }
2465
+ } catch (err) {
2466
+ setError(err);
2467
+ } finally {
2468
+ setLoading(false);
2469
+ }
2470
+ }, [formData.recipientEmail, service]);
2471
+ const proceedToReview = useCallback(() => {
2472
+ if ((recipientValidation == null ? void 0 : recipientValidation.valid) && formData.recipientEmail) {
2473
+ setStep("review");
2474
+ }
2475
+ }, [recipientValidation, formData.recipientEmail]);
2476
+ const initiateTransfer = useCallback(async () => {
2477
+ var _a, _b;
2478
+ if (!formData.confirmDeactivation) {
2479
+ setError({ code: "CONFIRMATION_REQUIRED", message: "Please confirm license deactivation" });
2480
+ return;
2481
+ }
2482
+ setStep("processing");
2483
+ setLoading(true);
2484
+ setError(null);
2485
+ try {
2486
+ const result = await service.initiateTransfer({
2487
+ licenseKey: formData.licenseKey,
2488
+ recipientEmail: formData.recipientEmail,
2489
+ recipientName: formData.recipientName,
2490
+ notes: formData.notes,
2491
+ reason: formData.transferReason
2492
+ });
2493
+ setTransfer(result);
2494
+ setStep("complete");
2495
+ (_a = config.onTransferComplete) == null ? void 0 : _a.call(config, result);
2496
+ } catch (err) {
2497
+ setError(err);
2498
+ setStep("review");
2499
+ (_b = config.onError) == null ? void 0 : _b.call(config, err);
2500
+ } finally {
2501
+ setLoading(false);
2502
+ }
2503
+ }, [formData, service, config]);
2504
+ const reset = useCallback(() => {
2505
+ setStep("select-license");
2506
+ setFormData({
2507
+ licenseKey: "",
2508
+ recipientEmail: "",
2509
+ recipientName: "",
2510
+ notes: "",
2511
+ transferReason: "employee_change",
2512
+ confirmDeactivation: false
2513
+ });
2514
+ setSelectedLicense(null);
2515
+ setRecipientValidation(null);
2516
+ setTransfer(null);
2517
+ setError(null);
2518
+ }, []);
2519
+ return {
2520
+ step,
2521
+ setStep,
2522
+ licenses,
2523
+ formData,
2524
+ setFormData,
2525
+ selectedLicense,
2526
+ recipientValidation,
2527
+ transfer,
2528
+ loading,
2529
+ error,
2530
+ loadLicenses,
2531
+ selectLicense,
2532
+ validateRecipient,
2533
+ proceedToReview,
2534
+ initiateTransfer,
2535
+ reset
2536
+ };
2537
+ }
2538
+ const TransferStatusBadge = ({ status }) => /* @__PURE__ */ jsx(
2539
+ "span",
2540
+ {
2541
+ className: "inline-flex items-center px-2.5 py-1 rounded-full text-sm font-medium",
2542
+ style: {
2543
+ backgroundColor: `${getTransferStatusColor(status)}20`,
2544
+ color: getTransferStatusColor(status)
2545
+ },
2546
+ children: getTransferStatusLabel(status)
2547
+ }
2548
+ );
2549
+ const LicenseTransfer = (props) => {
2550
+ var _a, _b;
2551
+ const { className, style, ...config } = props;
2552
+ const {
2553
+ step,
2554
+ licenses,
2555
+ formData,
2556
+ setFormData,
2557
+ selectedLicense,
2558
+ recipientValidation,
2559
+ transfer,
2560
+ loading,
2561
+ error,
2562
+ loadLicenses,
2563
+ selectLicense,
2564
+ validateRecipient,
2565
+ proceedToReview,
2566
+ initiateTransfer,
2567
+ reset,
2568
+ setStep
2569
+ } = useLicenseTransfer(config);
2570
+ React.useEffect(() => {
2571
+ loadLicenses();
2572
+ }, [loadLicenses]);
2573
+ return /* @__PURE__ */ jsxs(
2574
+ "div",
2575
+ {
2576
+ className: `nice-license-transfer bg-white rounded-lg shadow-lg p-8 max-w-2xl mx-auto ${className || ""}`,
2577
+ style,
2578
+ children: [
2579
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold text-gray-900 mb-2", children: "Transfer License" }),
2580
+ /* @__PURE__ */ jsx("p", { className: "text-gray-600 mb-6", children: "Transfer a license to another user or organization" }),
2581
+ error && /* @__PURE__ */ jsx("div", { className: "mb-6 p-4 bg-red-50 border border-red-200 rounded-lg", children: /* @__PURE__ */ jsx("p", { className: "text-red-700", children: error.message }) }),
2582
+ step === "select-license" && /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
2583
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: "Select a license to transfer" }),
2584
+ loading ? /* @__PURE__ */ jsx("div", { className: "text-center py-8", children: /* @__PURE__ */ jsx("div", { className: "animate-spin h-8 w-8 border-b-2 border-blue-500 rounded-full mx-auto" }) }) : licenses.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-center py-8 text-gray-500", children: "No transferable licenses found" }) : /* @__PURE__ */ jsx("div", { className: "grid gap-4", children: licenses.map((license) => /* @__PURE__ */ jsxs(
2585
+ "div",
2586
+ {
2587
+ className: "p-4 border border-gray-200 rounded-lg hover:border-blue-500 cursor-pointer transition-colors",
2588
+ onClick: () => selectLicense(license),
2589
+ children: [
2590
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-start", children: [
2591
+ /* @__PURE__ */ jsxs("div", { children: [
2592
+ /* @__PURE__ */ jsx("div", { className: "font-mono text-sm text-gray-500", children: license.key }),
2593
+ /* @__PURE__ */ jsx("div", { className: "font-medium mt-1", children: license.licensee })
2594
+ ] }),
2595
+ /* @__PURE__ */ jsx("span", { className: "px-2 py-1 bg-blue-100 text-blue-700 rounded text-sm capitalize", children: license.tier })
2596
+ ] }),
2597
+ /* @__PURE__ */ jsxs("div", { className: "mt-2 text-sm text-gray-500", children: [
2598
+ license.activeSeats,
2599
+ "/",
2600
+ license.maxSeats ?? "∞",
2601
+ " seats used"
2602
+ ] })
2603
+ ]
2604
+ },
2605
+ license.key
2606
+ )) })
2607
+ ] }),
2608
+ step === "enter-recipient" && selectedLicense && /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
2609
+ /* @__PURE__ */ jsxs("div", { className: "p-4 bg-gray-50 rounded-lg", children: [
2610
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-500", children: "Transferring license:" }),
2611
+ /* @__PURE__ */ jsx("div", { className: "font-mono", children: selectedLicense.key })
2612
+ ] }),
2613
+ /* @__PURE__ */ jsxs("div", { children: [
2614
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Recipient Email" }),
2615
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
2616
+ /* @__PURE__ */ jsx(
2617
+ "input",
2618
+ {
2619
+ type: "email",
2620
+ value: formData.recipientEmail,
2621
+ onChange: (e) => setFormData((f2) => ({ ...f2, recipientEmail: e.target.value })),
2622
+ onBlur: validateRecipient,
2623
+ placeholder: "recipient@company.com",
2624
+ className: "flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
2625
+ }
2626
+ ),
2627
+ /* @__PURE__ */ jsx(
2628
+ "button",
2629
+ {
2630
+ onClick: validateRecipient,
2631
+ disabled: !formData.recipientEmail || loading,
2632
+ className: "px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 disabled:opacity-50",
2633
+ children: "Validate"
2634
+ }
2635
+ )
2636
+ ] }),
2637
+ recipientValidation && /* @__PURE__ */ jsxs(
2638
+ "div",
2639
+ {
2640
+ className: `mt-2 text-sm ${recipientValidation.valid ? "text-green-600" : "text-red-600"}`,
2641
+ children: [
2642
+ recipientValidation.valid ? "✓ Valid recipient" : "✗ Invalid recipient",
2643
+ recipientValidation.isExternal && /* @__PURE__ */ jsx("span", { className: "ml-2 text-orange-600", children: "(External organization)" })
2644
+ ]
2645
+ }
2646
+ ),
2647
+ (_a = recipientValidation == null ? void 0 : recipientValidation.warnings) == null ? void 0 : _a.map((warning, i2) => /* @__PURE__ */ jsxs("div", { className: "mt-1 text-sm text-orange-600", children: [
2648
+ "⚠ ",
2649
+ warning
2650
+ ] }, i2))
2651
+ ] }),
2652
+ /* @__PURE__ */ jsxs("div", { children: [
2653
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Recipient Name" }),
2654
+ /* @__PURE__ */ jsx(
2655
+ "input",
2656
+ {
2657
+ type: "text",
2658
+ value: formData.recipientName,
2659
+ onChange: (e) => setFormData((f2) => ({ ...f2, recipientName: e.target.value })),
2660
+ placeholder: "John Doe",
2661
+ className: "w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
2662
+ }
2663
+ )
2664
+ ] }),
2665
+ /* @__PURE__ */ jsxs("div", { children: [
2666
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Transfer Reason" }),
2667
+ /* @__PURE__ */ jsx(
2668
+ "select",
2669
+ {
2670
+ value: formData.transferReason,
2671
+ onChange: (e) => setFormData((f2) => ({
2672
+ ...f2,
2673
+ transferReason: e.target.value
2674
+ })),
2675
+ className: "w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500",
2676
+ children: TRANSFER_REASONS.map((reason) => /* @__PURE__ */ jsx("option", { value: reason.value, children: reason.label }, reason.value))
2677
+ }
2678
+ )
2679
+ ] }),
2680
+ /* @__PURE__ */ jsxs("div", { children: [
2681
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Notes (optional)" }),
2682
+ /* @__PURE__ */ jsx(
2683
+ "textarea",
2684
+ {
2685
+ value: formData.notes,
2686
+ onChange: (e) => setFormData((f2) => ({ ...f2, notes: e.target.value })),
2687
+ placeholder: "Any additional information...",
2688
+ rows: 3,
2689
+ className: "w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
2690
+ }
2691
+ )
2692
+ ] }),
2693
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
2694
+ /* @__PURE__ */ jsx(
2695
+ "button",
2696
+ {
2697
+ onClick: () => setStep("select-license"),
2698
+ className: "px-6 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300",
2699
+ children: "Back"
2700
+ }
2701
+ ),
2702
+ /* @__PURE__ */ jsx(
2703
+ "button",
2704
+ {
2705
+ onClick: proceedToReview,
2706
+ disabled: !(recipientValidation == null ? void 0 : recipientValidation.valid) || !formData.recipientName,
2707
+ className: "flex-1 px-6 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 disabled:bg-gray-300",
2708
+ children: "Review Transfer"
2709
+ }
2710
+ )
2711
+ ] })
2712
+ ] }),
2713
+ step === "review" && selectedLicense && /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
2714
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: "Review Transfer Details" }),
2715
+ /* @__PURE__ */ jsxs("div", { className: "bg-gray-50 rounded-lg p-4 space-y-3", children: [
2716
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2 text-sm", children: [
2717
+ /* @__PURE__ */ jsx("div", { className: "text-gray-500", children: "License Key:" }),
2718
+ /* @__PURE__ */ jsx("div", { className: "font-mono", children: selectedLicense.key }),
2719
+ /* @__PURE__ */ jsx("div", { className: "text-gray-500", children: "Current Licensee:" }),
2720
+ /* @__PURE__ */ jsx("div", { children: selectedLicense.licensee }),
2721
+ /* @__PURE__ */ jsx("div", { className: "text-gray-500", children: "Tier:" }),
2722
+ /* @__PURE__ */ jsx("div", { className: "capitalize", children: selectedLicense.tier }),
2723
+ /* @__PURE__ */ jsx("div", { className: "text-gray-500", children: "Transfer To:" }),
2724
+ /* @__PURE__ */ jsx("div", { children: formData.recipientName }),
2725
+ /* @__PURE__ */ jsx("div", { className: "text-gray-500", children: "Recipient Email:" }),
2726
+ /* @__PURE__ */ jsx("div", { children: formData.recipientEmail }),
2727
+ /* @__PURE__ */ jsx("div", { className: "text-gray-500", children: "Reason:" }),
2728
+ /* @__PURE__ */ jsx("div", { children: (_b = TRANSFER_REASONS.find((r2) => r2.value === formData.transferReason)) == null ? void 0 : _b.label })
2729
+ ] }),
2730
+ formData.notes && /* @__PURE__ */ jsxs("div", { className: "pt-3 border-t border-gray-200", children: [
2731
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-500 mb-1", children: "Notes:" }),
2732
+ /* @__PURE__ */ jsx("div", { className: "text-sm", children: formData.notes })
2733
+ ] })
2734
+ ] }),
2735
+ /* @__PURE__ */ jsxs("div", { className: "p-4 bg-orange-50 border border-orange-200 rounded-lg", children: [
2736
+ /* @__PURE__ */ jsx("h3", { className: "font-semibold text-orange-800 mb-2", children: "⚠ Important" }),
2737
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-orange-700", children: "Once the transfer is completed, this license will be deactivated from all your machines and the new owner will need to activate it." })
2738
+ ] }),
2739
+ /* @__PURE__ */ jsxs("label", { className: "flex items-start gap-3 cursor-pointer", children: [
2740
+ /* @__PURE__ */ jsx(
2741
+ "input",
2742
+ {
2743
+ type: "checkbox",
2744
+ checked: formData.confirmDeactivation,
2745
+ onChange: (e) => setFormData((f2) => ({ ...f2, confirmDeactivation: e.target.checked })),
2746
+ className: "mt-1 w-4 h-4 rounded border-gray-300 text-blue-500 focus:ring-blue-500"
2747
+ }
2748
+ ),
2749
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-700", children: "I understand that this license will be deactivated and transferred to the recipient. This action cannot be undone without admin intervention." })
2750
+ ] }),
2751
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
2752
+ /* @__PURE__ */ jsx(
2753
+ "button",
2754
+ {
2755
+ onClick: () => setStep("enter-recipient"),
2756
+ className: "px-6 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300",
2757
+ children: "Back"
2758
+ }
2759
+ ),
2760
+ /* @__PURE__ */ jsx(
2761
+ "button",
2762
+ {
2763
+ onClick: initiateTransfer,
2764
+ disabled: !formData.confirmDeactivation || loading,
2765
+ className: "flex-1 px-6 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 disabled:bg-gray-300",
2766
+ children: loading ? "Processing..." : "Initiate Transfer"
2767
+ }
2768
+ )
2769
+ ] })
2770
+ ] }),
2771
+ step === "processing" && /* @__PURE__ */ jsxs("div", { className: "text-center py-12", children: [
2772
+ /* @__PURE__ */ jsx("div", { className: "animate-spin h-12 w-12 border-b-2 border-blue-500 rounded-full mx-auto" }),
2773
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold mt-4", children: "Processing Transfer..." }),
2774
+ /* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "Please wait while we process your transfer request" })
2775
+ ] }),
2776
+ step === "complete" && transfer && /* @__PURE__ */ jsxs("div", { className: "text-center space-y-6", children: [
2777
+ /* @__PURE__ */ jsx("div", { className: "text-green-500 text-6xl", children: "✓" }),
2778
+ /* @__PURE__ */ jsxs("div", { children: [
2779
+ /* @__PURE__ */ jsx("h2", { className: "text-xl font-bold", children: "Transfer Initiated" }),
2780
+ /* @__PURE__ */ jsx("p", { className: "text-gray-600 mt-2", children: transfer.approvalRequired ? "Your transfer request has been submitted for approval." : "An email has been sent to the recipient with instructions to accept the transfer." })
2781
+ ] }),
2782
+ /* @__PURE__ */ jsx("div", { className: "bg-gray-50 rounded-lg p-4 text-left", children: /* @__PURE__ */ jsxs("div", { className: "text-sm grid grid-cols-2 gap-2", children: [
2783
+ /* @__PURE__ */ jsx("div", { className: "text-gray-500", children: "Transfer ID:" }),
2784
+ /* @__PURE__ */ jsx("div", { className: "font-mono", children: transfer.id }),
2785
+ /* @__PURE__ */ jsx("div", { className: "text-gray-500", children: "Status:" }),
2786
+ /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(TransferStatusBadge, { status: transfer.status }) }),
2787
+ /* @__PURE__ */ jsx("div", { className: "text-gray-500", children: "Expires:" }),
2788
+ /* @__PURE__ */ jsx("div", { children: new Date(transfer.expiresAt).toLocaleDateString() })
2789
+ ] }) }),
2790
+ /* @__PURE__ */ jsx(
2791
+ "button",
2792
+ {
2793
+ onClick: reset,
2794
+ className: "px-8 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600",
2795
+ children: "Done"
2796
+ }
2797
+ )
2798
+ ] })
2799
+ ]
2800
+ }
2801
+ );
2802
+ };
2803
+ class BillingService {
2804
+ constructor(config) {
2805
+ this.config = config;
2806
+ }
2807
+ async request(endpoint, options = {}) {
2808
+ const url = `${this.config.apiBaseUrl}${endpoint}`;
2809
+ const headers = {
2810
+ "Content-Type": "application/json",
2811
+ ...this.config.authToken && { Authorization: `Bearer ${this.config.authToken}` }
2812
+ };
2813
+ const response = await fetch(url, { ...options, headers });
2814
+ if (!response.ok) {
2815
+ const error = await response.json().catch(() => ({}));
2816
+ throw new Error(error.message || `Request failed: ${response.status}`);
2817
+ }
2818
+ return response.json();
2819
+ }
2820
+ /* ─────────────────────────────────────────────────────────────
2821
+ PRICING & PLANS
2822
+ ───────────────────────────────────────────────────────────── */
2823
+ /** Get all pricing plans */
2824
+ async getPlans(currency) {
2825
+ const params = currency ? `?currency=${currency}` : "";
2826
+ return this.request(`/billing/plans${params}`);
2827
+ }
2828
+ /** Get plan by ID */
2829
+ async getPlan(planId) {
2830
+ return this.request(`/billing/plans/${planId}`);
2831
+ }
2832
+ /** Compare plans (for upgrade/downgrade) */
2833
+ async comparePlans(currentPlanId, targetPlanId, seats) {
2834
+ return this.request("/billing/plans/compare", {
2835
+ method: "POST",
2836
+ body: JSON.stringify({ currentPlanId, targetPlanId, seats })
2837
+ });
2838
+ }
2839
+ /* ─────────────────────────────────────────────────────────────
2840
+ SUBSCRIPTIONS
2841
+ ───────────────────────────────────────────────────────────── */
2842
+ /** Get current subscription */
2843
+ async getSubscription() {
2844
+ try {
2845
+ return await this.request("/billing/subscription");
2846
+ } catch {
2847
+ return null;
2848
+ }
2849
+ }
2850
+ /** Get subscription by ID */
2851
+ async getSubscriptionById(subscriptionId) {
2852
+ return this.request(`/billing/subscriptions/${subscriptionId}`);
2853
+ }
2854
+ /** Create checkout session for new subscription */
2855
+ async createCheckoutSession(options) {
2856
+ const provider = options.provider || this.detectPreferredProvider();
2857
+ return this.request("/billing/checkout", {
2858
+ method: "POST",
2859
+ body: JSON.stringify({ ...options, provider })
2860
+ });
2861
+ }
2862
+ /** Create billing portal session */
2863
+ async createPortalSession(returnUrl) {
2864
+ return this.request("/billing/portal", {
2865
+ method: "POST",
2866
+ body: JSON.stringify({ returnUrl })
2867
+ });
2868
+ }
2869
+ /** Update subscription (change plan, seats) */
2870
+ async updateSubscription(options) {
2871
+ return this.request("/billing/subscription", {
2872
+ method: "PATCH",
2873
+ body: JSON.stringify(options)
2874
+ });
2875
+ }
2876
+ /** Cancel subscription */
2877
+ async cancelSubscription(options) {
2878
+ return this.request("/billing/subscription/cancel", {
2879
+ method: "POST",
2880
+ body: JSON.stringify(options || { atPeriodEnd: true })
2881
+ });
2882
+ }
2883
+ /** Resume canceled subscription */
2884
+ async resumeSubscription() {
2885
+ return this.request("/billing/subscription/resume", {
2886
+ method: "POST"
2887
+ });
2888
+ }
2889
+ /** Pause subscription (Paddle only) */
2890
+ async pauseSubscription() {
2891
+ return this.request("/billing/subscription/pause", {
2892
+ method: "POST"
2893
+ });
2894
+ }
2895
+ /* ─────────────────────────────────────────────────────────────
2896
+ PAYMENT METHODS
2897
+ ───────────────────────────────────────────────────────────── */
2898
+ /** Get saved payment methods */
2899
+ async getPaymentMethods() {
2900
+ return this.request("/billing/payment-methods");
2901
+ }
2902
+ /** Add payment method (redirect to Stripe/Paddle) */
2903
+ async addPaymentMethod(returnUrl) {
2904
+ return this.request("/billing/payment-methods/add", {
2905
+ method: "POST",
2906
+ body: JSON.stringify({ returnUrl })
2907
+ });
2908
+ }
2909
+ /** Remove payment method */
2910
+ async removePaymentMethod(paymentMethodId) {
2911
+ await this.request(`/billing/payment-methods/${paymentMethodId}`, {
2912
+ method: "DELETE"
2913
+ });
2914
+ }
2915
+ /** Set default payment method */
2916
+ async setDefaultPaymentMethod(paymentMethodId) {
2917
+ await this.request(`/billing/payment-methods/${paymentMethodId}/default`, {
2918
+ method: "POST"
2919
+ });
2920
+ }
2921
+ /* ─────────────────────────────────────────────────────────────
2922
+ INVOICES
2923
+ ───────────────────────────────────────────────────────────── */
2924
+ /** Get invoices */
2925
+ async getInvoices(options) {
2926
+ const params = new URLSearchParams();
2927
+ if (options == null ? void 0 : options.limit) {
2928
+ params.set("limit", String(options.limit));
2929
+ }
2930
+ if (options == null ? void 0 : options.offset) {
2931
+ params.set("offset", String(options.offset));
2932
+ }
2933
+ if (options == null ? void 0 : options.status) {
2934
+ params.set("status", options.status);
2935
+ }
2936
+ return this.request(`/billing/invoices?${params}`);
2937
+ }
2938
+ /** Get upcoming invoice preview */
2939
+ async getUpcomingInvoice() {
2940
+ try {
2941
+ return await this.request("/billing/invoices/upcoming");
2942
+ } catch {
2943
+ return null;
2944
+ }
2945
+ }
2946
+ /** Download invoice PDF */
2947
+ async downloadInvoice(invoiceId) {
2948
+ const response = await fetch(`${this.config.apiBaseUrl}/billing/invoices/${invoiceId}/pdf`, {
2949
+ headers: {
2950
+ Authorization: this.config.authToken ? `Bearer ${this.config.authToken}` : ""
2951
+ }
2952
+ });
2953
+ return response.blob();
2954
+ }
2955
+ /* ─────────────────────────────────────────────────────────────
2956
+ COUPONS & PROMOTIONS
2957
+ ───────────────────────────────────────────────────────────── */
2958
+ /** Validate coupon code */
2959
+ async validateCoupon(code, planId) {
2960
+ return this.request("/billing/coupons/validate", {
2961
+ method: "POST",
2962
+ body: JSON.stringify({ code, planId })
2963
+ });
2964
+ }
2965
+ /** Apply coupon to subscription */
2966
+ async applyCoupon(code) {
2967
+ return this.request("/billing/subscription/coupon", {
2968
+ method: "POST",
2969
+ body: JSON.stringify({ code })
2970
+ });
2971
+ }
2972
+ /* ─────────────────────────────────────────────────────────────
2973
+ USAGE-BASED BILLING
2974
+ ───────────────────────────────────────────────────────────── */
2975
+ /** Report usage */
2976
+ async reportUsage(records) {
2977
+ await this.request("/billing/usage", {
2978
+ method: "POST",
2979
+ body: JSON.stringify({ records })
2980
+ });
2981
+ }
2982
+ /** Get usage summary */
2983
+ async getUsageSummary(startDate, endDate) {
2984
+ return this.request(`/billing/usage/summary?start=${startDate}&end=${endDate}`);
2985
+ }
2986
+ /* ─────────────────────────────────────────────────────────────
2987
+ UTILITIES
2988
+ ───────────────────────────────────────────────────────────── */
2989
+ detectPreferredProvider() {
2990
+ if (this.config.stripePublishableKey) {
2991
+ return "stripe";
2992
+ }
2993
+ if (this.config.paddleVendorId) {
2994
+ return "paddle";
2995
+ }
2996
+ return "stripe";
2997
+ }
2998
+ /** Format price for display */
2999
+ formatPrice(amount, currency = "USD") {
3000
+ return new Intl.NumberFormat("en-US", {
3001
+ style: "currency",
3002
+ currency,
3003
+ minimumFractionDigits: 0,
3004
+ maximumFractionDigits: 2
3005
+ }).format(amount / 100);
3006
+ }
3007
+ /** Calculate savings for yearly billing */
3008
+ calculateYearlySavings(plan) {
3009
+ const monthlyTotal = plan.monthlyPrice * 12;
3010
+ const yearlyTotal = plan.yearlyPrice;
3011
+ const savings = monthlyTotal - yearlyTotal;
3012
+ const savingsPercent = Math.round(savings / monthlyTotal * 100);
3013
+ return { monthlyTotal, yearlyTotal, savings, savingsPercent };
3014
+ }
3015
+ }
3016
+ function createBillingService(config) {
3017
+ return new BillingService(config);
3018
+ }
3019
+ class UsageAnalyticsService {
3020
+ constructor(config) {
3021
+ this.config = config;
3022
+ }
3023
+ async request(endpoint, options = {}) {
3024
+ const url = `${this.config.apiBaseUrl}${endpoint}`;
3025
+ const headers = {
3026
+ "Content-Type": "application/json",
3027
+ ...this.config.authToken && { Authorization: `Bearer ${this.config.authToken}` }
3028
+ };
3029
+ const response = await fetch(url, { ...options, headers });
3030
+ if (!response.ok) {
3031
+ const error = await response.json().catch(() => ({}));
3032
+ throw new Error(error.message || `Request failed: ${response.status}`);
3033
+ }
3034
+ return response.json();
3035
+ }
3036
+ getTimeParams(range, granularity) {
3037
+ const params = new URLSearchParams({ range });
3038
+ if (granularity) {
3039
+ params.set("granularity", granularity);
3040
+ }
3041
+ if (this.config.organizationId) {
3042
+ params.set("org", this.config.organizationId);
3043
+ }
3044
+ return params.toString();
3045
+ }
3046
+ /** Get overview metrics */
3047
+ async getOverview(range = "30d") {
3048
+ return this.request(`/analytics/overview?${this.getTimeParams(range)}`);
3049
+ }
3050
+ /** Get license activity over time */
3051
+ async getLicenseActivity(range = "30d", granularity = "day") {
3052
+ return this.request(
3053
+ `/analytics/licenses/activity?${this.getTimeParams(range, granularity)}`
3054
+ );
3055
+ }
3056
+ /** Get user activity over time */
3057
+ async getUserActivity(range = "30d", granularity = "day") {
3058
+ return this.request(
3059
+ `/analytics/users/activity?${this.getTimeParams(range, granularity)}`
3060
+ );
3061
+ }
3062
+ /** Get feature usage data */
3063
+ async getFeatureUsage(range = "30d", features) {
3064
+ const params = this.getTimeParams(range);
3065
+ const featuresParam = features ? `&features=${features.join(",")}` : "";
3066
+ return this.request(`/analytics/features?${params}${featuresParam}`);
3067
+ }
3068
+ /** Get top users by activity */
3069
+ async getTopUsers(range = "30d", limit = 20) {
3070
+ return this.request(
3071
+ `/analytics/users/top?${this.getTimeParams(range)}&limit=${limit}`
3072
+ );
3073
+ }
3074
+ /** Get license utilization */
3075
+ async getLicenseUtilization() {
3076
+ const params = this.config.organizationId ? `?org=${this.config.organizationId}` : "";
3077
+ return this.request(`/analytics/licenses/utilization${params}`);
3078
+ }
3079
+ /** Get geographic distribution */
3080
+ async getGeographicData(range = "30d") {
3081
+ return this.request(`/analytics/geo?${this.getTimeParams(range)}`);
3082
+ }
3083
+ /** Get trends for key metrics */
3084
+ async getTrends(range = "30d") {
3085
+ return this.request(`/analytics/trends?${this.getTimeParams(range)}`);
3086
+ }
3087
+ /** Get retention cohorts */
3088
+ async getRetentionCohorts(months = 6) {
3089
+ const params = this.config.organizationId ? `?org=${this.config.organizationId}&months=${months}` : `?months=${months}`;
3090
+ return this.request(`/analytics/retention${params}`);
3091
+ }
3092
+ /** Export analytics data */
3093
+ async exportData(range, format) {
3094
+ const response = await fetch(
3095
+ `${this.config.apiBaseUrl}/analytics/export?${this.getTimeParams(range)}&format=${format}`,
3096
+ {
3097
+ headers: {
3098
+ Authorization: this.config.authToken ? `Bearer ${this.config.authToken}` : ""
3099
+ }
3100
+ }
3101
+ );
3102
+ return response.blob();
3103
+ }
3104
+ }
3105
+ function useUsageAnalytics(config) {
3106
+ const [state, setState] = useState({
3107
+ loading: true,
3108
+ error: null,
3109
+ timeRange: config.defaultTimeRange || "30d",
3110
+ overview: null,
3111
+ licenseActivity: [],
3112
+ userActivity: [],
3113
+ featureUsage: [],
3114
+ topUsers: [],
3115
+ licenseUtilization: [],
3116
+ geoData: [],
3117
+ trends: [],
3118
+ retentionCohorts: []
3119
+ });
3120
+ const service = useMemo(() => new UsageAnalyticsService(config), [config]);
3121
+ const loadData = useCallback(
3122
+ async (range = state.timeRange) => {
3123
+ setState((s2) => ({ ...s2, loading: true, error: null, timeRange: range }));
3124
+ try {
3125
+ const [
3126
+ overview,
3127
+ licenseActivity,
3128
+ userActivity,
3129
+ featureUsage,
3130
+ topUsers,
3131
+ licenseUtilization,
3132
+ geoData,
3133
+ trends,
3134
+ retentionCohorts
3135
+ ] = await Promise.all([
3136
+ service.getOverview(range),
3137
+ service.getLicenseActivity(range),
3138
+ service.getUserActivity(range),
3139
+ service.getFeatureUsage(range),
3140
+ service.getTopUsers(range),
3141
+ service.getLicenseUtilization(),
3142
+ service.getGeographicData(range),
3143
+ service.getTrends(range),
3144
+ service.getRetentionCohorts()
3145
+ ]);
3146
+ setState((s2) => ({
3147
+ ...s2,
3148
+ loading: false,
3149
+ overview,
3150
+ licenseActivity,
3151
+ userActivity,
3152
+ featureUsage,
3153
+ topUsers,
3154
+ licenseUtilization,
3155
+ geoData,
3156
+ trends,
3157
+ retentionCohorts
3158
+ }));
3159
+ } catch (error) {
3160
+ setState((s2) => ({
3161
+ ...s2,
3162
+ loading: false,
3163
+ error: error.message
3164
+ }));
3165
+ }
3166
+ },
3167
+ [service, state.timeRange]
3168
+ );
3169
+ useEffect(() => {
3170
+ loadData();
3171
+ }, []);
3172
+ useEffect(() => {
3173
+ if (config.refreshInterval && config.refreshInterval > 0) {
3174
+ const interval = setInterval(() => loadData(), config.refreshInterval);
3175
+ return () => clearInterval(interval);
3176
+ }
3177
+ }, [config.refreshInterval, loadData]);
3178
+ const setTimeRange = useCallback(
3179
+ (range) => {
3180
+ loadData(range);
3181
+ },
3182
+ [loadData]
3183
+ );
3184
+ const exportData = useCallback(
3185
+ async (format) => {
3186
+ const blob = await service.exportData(state.timeRange, format);
3187
+ const url = URL.createObjectURL(blob);
3188
+ const a = document.createElement("a");
3189
+ a.href = url;
3190
+ a.download = `analytics-${state.timeRange}.${format}`;
3191
+ a.click();
3192
+ URL.revokeObjectURL(url);
3193
+ },
3194
+ [service, state.timeRange]
3195
+ );
3196
+ return {
3197
+ state,
3198
+ setTimeRange,
3199
+ refresh: loadData,
3200
+ exportData
3201
+ };
3202
+ }
3203
+ const MetricCard = ({ title, value, change, suffix, loading }) => /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [
3204
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-500 mb-1", children: title }),
3205
+ loading ? /* @__PURE__ */ jsx("div", { className: "h-8 bg-gray-100 rounded animate-pulse" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3206
+ /* @__PURE__ */ jsxs("div", { className: "text-2xl font-bold", children: [
3207
+ typeof value === "number" ? value.toLocaleString() : value,
3208
+ suffix && /* @__PURE__ */ jsx("span", { className: "text-sm font-normal text-gray-500 ml-1", children: suffix })
3209
+ ] }),
3210
+ change !== void 0 && /* @__PURE__ */ jsxs("div", { className: `text-sm mt-1 ${change >= 0 ? "text-green-600" : "text-red-600"}`, children: [
3211
+ change >= 0 ? "↑" : "↓",
3212
+ " ",
3213
+ Math.abs(change).toFixed(1),
3214
+ "%"
3215
+ ] })
3216
+ ] })
3217
+ ] });
3218
+ const TimeRangeSelector = ({ value, onChange }) => {
3219
+ const options = [
3220
+ { value: "24h", label: "24 Hours" },
3221
+ { value: "7d", label: "7 Days" },
3222
+ { value: "30d", label: "30 Days" },
3223
+ { value: "90d", label: "90 Days" },
3224
+ { value: "1y", label: "1 Year" },
3225
+ { value: "all", label: "All Time" }
3226
+ ];
3227
+ return /* @__PURE__ */ jsx("div", { className: "flex gap-1 bg-gray-100 rounded-lg p-1", children: options.map((opt) => /* @__PURE__ */ jsx(
3228
+ "button",
3229
+ {
3230
+ onClick: () => onChange(opt.value),
3231
+ className: `px-3 py-1.5 rounded-md text-sm font-medium transition-colors ${value === opt.value ? "bg-white text-gray-900 shadow-sm" : "text-gray-600 hover:text-gray-900"}`,
3232
+ children: opt.label
3233
+ },
3234
+ opt.value
3235
+ )) });
3236
+ };
3237
+ const FeatureUsageTable = ({ data, loading }) => /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg border border-gray-200 overflow-hidden", children: [
3238
+ /* @__PURE__ */ jsx("div", { className: "p-4 border-b border-gray-200", children: /* @__PURE__ */ jsx("h3", { className: "font-semibold", children: "Feature Usage" }) }),
3239
+ /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "w-full", children: [
3240
+ /* @__PURE__ */ jsx("thead", { className: "bg-gray-50", children: /* @__PURE__ */ jsxs("tr", { children: [
3241
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase", children: "Feature" }),
3242
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase", children: "Total Uses" }),
3243
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase", children: "Users" }),
3244
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase", children: "Avg/User" }),
3245
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase", children: "Trend" })
3246
+ ] }) }),
3247
+ /* @__PURE__ */ jsx("tbody", { className: "divide-y divide-gray-100", children: loading ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: 5, className: "px-4 py-8 text-center text-gray-500", children: "Loading..." }) }) : data.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: 5, className: "px-4 py-8 text-center text-gray-500", children: "No data available" }) }) : data.map((feature) => /* @__PURE__ */ jsxs("tr", { className: "hover:bg-gray-50", children: [
3248
+ /* @__PURE__ */ jsxs("td", { className: "px-4 py-3", children: [
3249
+ /* @__PURE__ */ jsx("div", { className: "font-medium", children: feature.featureName }),
3250
+ feature.category && /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500", children: feature.category })
3251
+ ] }),
3252
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-right font-mono", children: feature.totalUses.toLocaleString() }),
3253
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-right font-mono", children: feature.uniqueUsers.toLocaleString() }),
3254
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-right font-mono", children: feature.avgUsesPerUser.toFixed(1) }),
3255
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-right", children: /* @__PURE__ */ jsxs(
3256
+ "span",
3257
+ {
3258
+ className: `inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${feature.trend > 0 ? "bg-green-100 text-green-800" : feature.trend < 0 ? "bg-red-100 text-red-800" : "bg-gray-100 text-gray-800"}`,
3259
+ children: [
3260
+ feature.trend > 0 ? "+" : "",
3261
+ feature.trend.toFixed(1),
3262
+ "%"
3263
+ ]
3264
+ }
3265
+ ) })
3266
+ ] }, feature.featureId)) })
3267
+ ] }) })
3268
+ ] });
3269
+ const TopUsersTable = ({ data, loading }) => /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg border border-gray-200 overflow-hidden", children: [
3270
+ /* @__PURE__ */ jsx("div", { className: "p-4 border-b border-gray-200", children: /* @__PURE__ */ jsx("h3", { className: "font-semibold", children: "Top Active Users" }) }),
3271
+ /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "w-full", children: [
3272
+ /* @__PURE__ */ jsx("thead", { className: "bg-gray-50", children: /* @__PURE__ */ jsxs("tr", { children: [
3273
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase", children: "User" }),
3274
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase", children: "Sessions" }),
3275
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase", children: "Duration" }),
3276
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase", children: "Features" }),
3277
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase", children: "Last Active" })
3278
+ ] }) }),
3279
+ /* @__PURE__ */ jsx("tbody", { className: "divide-y divide-gray-100", children: loading ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: 5, className: "px-4 py-8 text-center text-gray-500", children: "Loading..." }) }) : data.map((user) => /* @__PURE__ */ jsxs("tr", { className: "hover:bg-gray-50", children: [
3280
+ /* @__PURE__ */ jsxs("td", { className: "px-4 py-3", children: [
3281
+ /* @__PURE__ */ jsx("div", { className: "font-medium", children: user.displayName || user.email }),
3282
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500 capitalize", children: user.tier })
3283
+ ] }),
3284
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-right font-mono", children: user.sessionCount.toLocaleString() }),
3285
+ /* @__PURE__ */ jsxs("td", { className: "px-4 py-3 text-right font-mono", children: [
3286
+ Math.round(user.totalDuration / 60),
3287
+ "h"
3288
+ ] }),
3289
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-right font-mono", children: user.featuresUsed }),
3290
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-sm text-gray-500", children: new Date(user.lastActive).toLocaleDateString() })
3291
+ ] }, user.userId)) })
3292
+ ] }) })
3293
+ ] });
3294
+ const LicenseUtilizationChart = ({ data, loading }) => /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg border border-gray-200", children: [
3295
+ /* @__PURE__ */ jsx("div", { className: "p-4 border-b border-gray-200", children: /* @__PURE__ */ jsx("h3", { className: "font-semibold", children: "License Utilization" }) }),
3296
+ /* @__PURE__ */ jsx("div", { className: "p-4 space-y-4", children: loading ? /* @__PURE__ */ jsx("div", { className: "text-center text-gray-500", children: "Loading..." }) : data.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-center text-gray-500", children: "No licenses" }) : data.map((license) => /* @__PURE__ */ jsxs("div", { children: [
3297
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between text-sm mb-1", children: [
3298
+ /* @__PURE__ */ jsx("span", { className: "font-medium truncate max-w-[200px]", children: license.licensee }),
3299
+ /* @__PURE__ */ jsxs("span", { className: "text-gray-500", children: [
3300
+ license.usedSeats,
3301
+ "/",
3302
+ license.totalSeats,
3303
+ " seats"
3304
+ ] })
3305
+ ] }),
3306
+ /* @__PURE__ */ jsx("div", { className: "h-2 bg-gray-100 rounded-full overflow-hidden", children: /* @__PURE__ */ jsx(
3307
+ "div",
3308
+ {
3309
+ className: `h-full rounded-full ${license.utilizationPercent >= 90 ? "bg-red-500" : license.utilizationPercent >= 70 ? "bg-yellow-500" : "bg-green-500"}`,
3310
+ style: { width: `${license.utilizationPercent}%` }
3311
+ }
3312
+ ) })
3313
+ ] }, license.licenseKey)) })
3314
+ ] });
3315
+ const UsageAnalyticsDashboard = (props) => {
3316
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
3317
+ const { className, style, ...config } = props;
3318
+ const { state, setTimeRange, refresh, exportData } = useUsageAnalytics(config);
3319
+ return /* @__PURE__ */ jsxs("div", { className: `nice-usage-analytics ${className || ""}`, style, children: [
3320
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-6", children: [
3321
+ /* @__PURE__ */ jsxs("div", { children: [
3322
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold text-gray-900", children: "Usage Analytics" }),
3323
+ /* @__PURE__ */ jsx("p", { className: "text-gray-600", children: "Monitor license usage and feature adoption" })
3324
+ ] }),
3325
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
3326
+ /* @__PURE__ */ jsx(TimeRangeSelector, { value: state.timeRange, onChange: setTimeRange }),
3327
+ /* @__PURE__ */ jsx(
3328
+ "button",
3329
+ {
3330
+ onClick: () => refresh(),
3331
+ className: "p-2 text-gray-500 hover:text-gray-700 rounded-lg hover:bg-gray-100",
3332
+ title: "Refresh",
3333
+ children: "↻"
3334
+ }
3335
+ ),
3336
+ /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsx(
3337
+ "button",
3338
+ {
3339
+ className: "px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300",
3340
+ onClick: () => exportData("csv"),
3341
+ children: "Export"
3342
+ }
3343
+ ) })
3344
+ ] })
3345
+ ] }),
3346
+ state.error && /* @__PURE__ */ jsx("div", { className: "mb-6 p-4 bg-red-50 border border-red-200 rounded-lg text-red-700", children: state.error }),
3347
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-4 mb-6", children: [
3348
+ /* @__PURE__ */ jsx(
3349
+ MetricCard,
3350
+ {
3351
+ title: "Active Licenses",
3352
+ value: ((_a = state.overview) == null ? void 0 : _a.activeLicenses) ?? 0,
3353
+ change: (_b = state.overview) == null ? void 0 : _b.activeLicensesChange,
3354
+ loading: state.loading
3355
+ }
3356
+ ),
3357
+ /* @__PURE__ */ jsx(
3358
+ MetricCard,
3359
+ {
3360
+ title: "Active Users",
3361
+ value: ((_c = state.overview) == null ? void 0 : _c.activeUsers) ?? 0,
3362
+ change: (_d = state.overview) == null ? void 0 : _d.activeUsersChange,
3363
+ loading: state.loading
3364
+ }
3365
+ ),
3366
+ /* @__PURE__ */ jsx(
3367
+ MetricCard,
3368
+ {
3369
+ title: "Feature Uses",
3370
+ value: ((_e = state.overview) == null ? void 0 : _e.featureUses) ?? 0,
3371
+ change: (_f = state.overview) == null ? void 0 : _f.featureUsesChange,
3372
+ loading: state.loading
3373
+ }
3374
+ ),
3375
+ /* @__PURE__ */ jsx(
3376
+ MetricCard,
3377
+ {
3378
+ title: "Avg Session",
3379
+ value: ((_h = (_g = state.overview) == null ? void 0 : _g.avgSessionDuration) == null ? void 0 : _h.toFixed(1)) ?? "0",
3380
+ suffix: "min",
3381
+ change: (_i = state.overview) == null ? void 0 : _i.avgSessionDurationChange,
3382
+ loading: state.loading
3383
+ }
3384
+ ),
3385
+ /* @__PURE__ */ jsx(
3386
+ MetricCard,
3387
+ {
3388
+ title: "Utilization",
3389
+ value: `${((_k = (_j = state.overview) == null ? void 0 : _j.utilizationRate) == null ? void 0 : _k.toFixed(0)) ?? 0}%`,
3390
+ change: (_l = state.overview) == null ? void 0 : _l.utilizationRateChange,
3391
+ loading: state.loading
3392
+ }
3393
+ )
3394
+ ] }),
3395
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6", children: [
3396
+ /* @__PURE__ */ jsx("div", { className: "lg:col-span-2", children: /* @__PURE__ */ jsx(FeatureUsageTable, { data: state.featureUsage, loading: state.loading }) }),
3397
+ /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(LicenseUtilizationChart, { data: state.licenseUtilization, loading: state.loading }) })
3398
+ ] }),
3399
+ /* @__PURE__ */ jsx("div", { className: "mb-6", children: /* @__PURE__ */ jsx(TopUsersTable, { data: state.topUsers, loading: state.loading }) }),
3400
+ state.geoData.length > 0 && /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [
3401
+ /* @__PURE__ */ jsx("h3", { className: "font-semibold mb-4", children: "Geographic Distribution" }),
3402
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4", children: state.geoData.slice(0, 6).map((geo) => /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
3403
+ /* @__PURE__ */ jsx("div", { className: "text-2xl mb-1", children: getCountryFlag(geo.countryCode) }),
3404
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: geo.country }),
3405
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-500", children: [
3406
+ geo.users,
3407
+ " users"
3408
+ ] })
3409
+ ] }, geo.countryCode)) })
3410
+ ] })
3411
+ ] });
3412
+ };
3413
+ function getCountryFlag(countryCode) {
3414
+ const codePoints = countryCode.toUpperCase().split("").map((char) => 127397 + char.charCodeAt(0));
3415
+ return String.fromCodePoint(...codePoints);
3416
+ }
3417
+ class RefundService {
3418
+ constructor(config) {
3419
+ this.config = config;
3420
+ }
3421
+ async request(endpoint, options = {}) {
3422
+ const url = `${this.config.apiBaseUrl}${endpoint}`;
3423
+ const headers = {
3424
+ "Content-Type": "application/json",
3425
+ ...this.config.authToken && { Authorization: `Bearer ${this.config.authToken}` }
3426
+ };
3427
+ const response = await fetch(url, { ...options, headers });
3428
+ if (!response.ok) {
3429
+ const error = await response.json().catch(() => ({}));
3430
+ throw new Error(error.message || `Request failed: ${response.status}`);
3431
+ }
3432
+ return response.json();
3433
+ }
3434
+ /* ─────────────────────────────────────────────────────────────
3435
+ REFUND ELIGIBILITY
3436
+ ───────────────────────────────────────────────────────────── */
3437
+ /** Check if license is eligible for refund */
3438
+ async checkEligibility(licenseKey) {
3439
+ return this.request(
3440
+ `/refunds/eligibility/${encodeURIComponent(licenseKey)}`
3441
+ );
3442
+ }
3443
+ /** Calculate refund amount */
3444
+ calculateRefundAmount(originalAmount, daysSincePurchase) {
3445
+ const policy = this.config.policy;
3446
+ if (daysSincePurchase > policy.refundPeriodDays) {
3447
+ return { amount: 0, percent: 0 };
3448
+ }
3449
+ if (daysSincePurchase <= policy.fullRefundDays) {
3450
+ return { amount: originalAmount, percent: 100 };
3451
+ }
3452
+ if (policy.partialRefundPercent) {
3453
+ const amount = Math.round(originalAmount * (policy.partialRefundPercent / 100));
3454
+ return { amount, percent: policy.partialRefundPercent };
3455
+ }
3456
+ if (policy.usageBasedRefundPercent !== void 0) {
3457
+ const usedDays = daysSincePurchase - policy.fullRefundDays;
3458
+ const remainingDays = policy.refundPeriodDays - policy.fullRefundDays;
3459
+ const usagePercent = Math.min(100, usedDays / remainingDays * 100);
3460
+ const refundPercent = Math.max(0, policy.usageBasedRefundPercent - usagePercent);
3461
+ const amount = Math.round(originalAmount * (refundPercent / 100));
3462
+ return { amount, percent: refundPercent };
3463
+ }
3464
+ return { amount: originalAmount, percent: 100 };
3465
+ }
3466
+ /* ─────────────────────────────────────────────────────────────
3467
+ REFUND REQUESTS
3468
+ ───────────────────────────────────────────────────────────── */
3469
+ /** Create refund request */
3470
+ async createRefundRequest(data) {
3471
+ return this.request("/refunds", {
3472
+ method: "POST",
3473
+ body: JSON.stringify(data)
3474
+ });
3475
+ }
3476
+ /** Get refund request by ID */
3477
+ async getRefundRequest(requestId) {
3478
+ return this.request(`/refunds/${requestId}`);
3479
+ }
3480
+ /** Get refund requests for customer */
3481
+ async getCustomerRefundRequests(customerId) {
3482
+ const endpoint = customerId ? `/refunds?customerId=${customerId}` : "/refunds";
3483
+ return this.request(endpoint);
3484
+ }
3485
+ /** Get pending refund requests (admin) */
3486
+ async getPendingRequests(limit = 50, offset = 0) {
3487
+ return this.request(`/refunds/pending?limit=${limit}&offset=${offset}`);
3488
+ }
3489
+ /** Cancel refund request (customer) */
3490
+ async cancelRefundRequest(requestId) {
3491
+ return this.request(`/refunds/${requestId}/cancel`, {
3492
+ method: "POST"
3493
+ });
3494
+ }
3495
+ /** Add attachment to refund request */
3496
+ async addAttachment(requestId, file) {
3497
+ const formData = new FormData();
3498
+ formData.append("file", file);
3499
+ const response = await fetch(`${this.config.apiBaseUrl}/refunds/${requestId}/attachments`, {
3500
+ method: "POST",
3501
+ headers: {
3502
+ Authorization: this.config.authToken ? `Bearer ${this.config.authToken}` : ""
3503
+ },
3504
+ body: formData
3505
+ });
3506
+ if (!response.ok) {
3507
+ throw new Error("Failed to upload attachment");
3508
+ }
3509
+ return response.json();
3510
+ }
3511
+ /* ─────────────────────────────────────────────────────────────
3512
+ ADMIN OPERATIONS
3513
+ ───────────────────────────────────────────────────────────── */
3514
+ /** Approve refund request (admin) */
3515
+ async approveRefund(requestId, options) {
3516
+ return this.request(`/refunds/${requestId}/approve`, {
3517
+ method: "POST",
3518
+ body: JSON.stringify(options || {})
3519
+ });
3520
+ }
3521
+ /** Reject refund request (admin) */
3522
+ async rejectRefund(requestId, reason) {
3523
+ return this.request(`/refunds/${requestId}/reject`, {
3524
+ method: "POST",
3525
+ body: JSON.stringify({ reason })
3526
+ });
3527
+ }
3528
+ /** Process refund (trigger payment refund) */
3529
+ async processRefund(requestId) {
3530
+ var _a, _b;
3531
+ const result = await this.request(`/refunds/${requestId}/process`, {
3532
+ method: "POST"
3533
+ });
3534
+ (_b = (_a = this.config).onRefundProcessed) == null ? void 0 : _b.call(_a, result);
3535
+ return result;
3536
+ }
3537
+ /* ─────────────────────────────────────────────────────────────
3538
+ LICENSE REVOCATION
3539
+ ───────────────────────────────────────────────────────────── */
3540
+ /** Revoke license */
3541
+ async revokeLicense(licenseKey, reason, options) {
3542
+ var _a, _b;
3543
+ const result = await this.request("/revocations", {
3544
+ method: "POST",
3545
+ body: JSON.stringify({
3546
+ licenseKey,
3547
+ reason,
3548
+ ...options
3549
+ })
3550
+ });
3551
+ (_b = (_a = this.config).onLicenseRevoked) == null ? void 0 : _b.call(_a, result);
3552
+ return result;
3553
+ }
3554
+ /** Get revocation history */
3555
+ async getRevocationHistory(licenseKey) {
3556
+ const endpoint = licenseKey ? `/revocations?licenseKey=${encodeURIComponent(licenseKey)}` : "/revocations";
3557
+ return this.request(endpoint);
3558
+ }
3559
+ /** Reactivate revoked license (if allowed) */
3560
+ async reactivateLicense(revocationId, reason) {
3561
+ return this.request(`/revocations/${revocationId}/reactivate`, {
3562
+ method: "POST",
3563
+ body: JSON.stringify({ reason })
3564
+ });
3565
+ }
3566
+ /* ─────────────────────────────────────────────────────────────
3567
+ STATISTICS
3568
+ ───────────────────────────────────────────────────────────── */
3569
+ /** Get refund statistics */
3570
+ async getRefundStats(startDate, endDate) {
3571
+ return this.request(`/refunds/stats?start=${startDate}&end=${endDate}`);
3572
+ }
3573
+ }
3574
+ const DEFAULT_REFUND_POLICY = {
3575
+ refundPeriodDays: 30,
3576
+ autoApprovalTiers: ["trial", "personal"],
3577
+ autoApprovalMaxAmount: 1e4,
3578
+ // $100
3579
+ maxRefundsPerYear: 3,
3580
+ requireReason: true,
3581
+ requireDetails: false,
3582
+ fullRefundDays: 14,
3583
+ partialRefundPercent: 50
3584
+ };
3585
+ function createRefundService(config) {
3586
+ return new RefundService(config);
3587
+ }
3588
+ class BuildLicenseValidator {
3589
+ constructor(config = {}) {
3590
+ this.licenseInfo = null;
3591
+ this.validationResult = null;
3592
+ this.config = {
3593
+ failOnInvalid: true,
3594
+ failOnExpired: true,
3595
+ allowOffline: true,
3596
+ validationTimeout: 1e4,
3597
+ ...config
3598
+ };
3599
+ }
3600
+ /** Get license key from config or environment */
3601
+ getLicenseKey() {
3602
+ if (this.config.licenseKey) {
3603
+ return this.config.licenseKey;
3604
+ }
3605
+ const envVar = this.config.licenseKeyEnvVar || "NICE2DEV_LICENSE_KEY";
3606
+ return process.env[envVar] || null;
3607
+ }
3608
+ /** Log message (unless silent) */
3609
+ log(message, type = "info") {
3610
+ if (this.config.silent) {
3611
+ return;
3612
+ }
3613
+ const prefix = "[nice2dev/license]";
3614
+ switch (type) {
3615
+ case "warn":
3616
+ console.warn(`${prefix} ⚠ ${message}`);
3617
+ break;
3618
+ case "error":
3619
+ console.error(`${prefix} ✗ ${message}`);
3620
+ break;
3621
+ default:
3622
+ console.log(`${prefix} ${message}`);
3623
+ }
3624
+ }
3625
+ /** Validate license online */
3626
+ async validateOnline(licenseKey) {
3627
+ if (!this.config.licenseServerUrl) {
3628
+ return null;
3629
+ }
3630
+ const endpoint = this.config.validationEndpoint || "/api/licenses/validate";
3631
+ const url = `${this.config.licenseServerUrl}${endpoint}`;
3632
+ try {
3633
+ const controller = new AbortController();
3634
+ const timeout = setTimeout(() => controller.abort(), this.config.validationTimeout);
3635
+ const response = await fetch(url, {
3636
+ method: "POST",
3637
+ headers: { "Content-Type": "application/json" },
3638
+ body: JSON.stringify({ licenseKey }),
3639
+ signal: controller.signal
3640
+ });
3641
+ clearTimeout(timeout);
3642
+ if (!response.ok) {
3643
+ this.log(`Online validation failed: ${response.status}`, "warn");
3644
+ return null;
3645
+ }
3646
+ const result = await response.json();
3647
+ return result.license || null;
3648
+ } catch (error) {
3649
+ this.log(`Online validation error: ${error.message}`, "warn");
3650
+ return null;
3651
+ }
3652
+ }
3653
+ /** Load cached license */
3654
+ async loadCachedLicense(licenseKey) {
3655
+ if (!this.config.allowOffline || !this.config.cacheDir) {
3656
+ return null;
3657
+ }
3658
+ const fs = await import("./__vite-browser-external-2Ng8QIWW.js").then((m2) => m2.promises).catch(() => null);
3659
+ if (!fs) {
3660
+ return null;
3661
+ }
3662
+ const cacheFile = `${this.config.cacheDir}/.license-cache.json`;
3663
+ try {
3664
+ const content = await fs.readFile(cacheFile, "utf-8");
3665
+ const cached = JSON.parse(content);
3666
+ if (cached.licenseKey === licenseKey && cached.license) {
3667
+ return cached.license;
3668
+ }
3669
+ } catch {
3670
+ }
3671
+ return null;
3672
+ }
3673
+ /** Save license to cache */
3674
+ async saveLicenseCache(licenseKey, license) {
3675
+ if (!this.config.cacheDir) {
3676
+ return;
3677
+ }
3678
+ const fs = await import("./__vite-browser-external-2Ng8QIWW.js").then((m2) => m2.promises).catch(() => null);
3679
+ const path = await import("./__vite-browser-external-2Ng8QIWW.js").catch(() => null);
3680
+ if (!fs || !path) {
3681
+ return;
3682
+ }
3683
+ try {
3684
+ await fs.mkdir(this.config.cacheDir, { recursive: true });
3685
+ const cacheFile = `${this.config.cacheDir}/.license-cache.json`;
3686
+ await fs.writeFile(
3687
+ cacheFile,
3688
+ JSON.stringify({ licenseKey, license, cachedAt: (/* @__PURE__ */ new Date()).toISOString() })
3689
+ );
3690
+ } catch {
3691
+ }
3692
+ }
3693
+ /** Validate license (main entry point) */
3694
+ async validate() {
3695
+ var _a;
3696
+ const result = {
3697
+ valid: false,
3698
+ warnings: [],
3699
+ errors: []
3700
+ };
3701
+ const licenseKey = this.getLicenseKey();
3702
+ if (!licenseKey) {
3703
+ if (this.config.failOnInvalid) {
3704
+ result.errors.push(
3705
+ "No license key provided. Set NICE2DEV_LICENSE_KEY or pass licenseKey option."
3706
+ );
3707
+ } else {
3708
+ result.warnings.push("No license key provided. Some features may be disabled.");
3709
+ result.valid = true;
3710
+ }
3711
+ this.validationResult = result;
3712
+ return result;
3713
+ }
3714
+ if (!validateKeyFormat$1(licenseKey)) {
3715
+ result.errors.push("Invalid license key format.");
3716
+ this.validationResult = result;
3717
+ return result;
3718
+ }
3719
+ const metadata = extractKeyMetadata(licenseKey);
3720
+ result.licenseKey = licenseKey;
3721
+ result.tier = metadata == null ? void 0 : metadata.tier;
3722
+ let license = await this.validateOnline(licenseKey);
3723
+ if (!license && this.config.allowOffline) {
3724
+ license = await this.loadCachedLicense(licenseKey);
3725
+ if (license) {
3726
+ result.warnings.push("Using cached license (offline mode).");
3727
+ }
3728
+ }
3729
+ if (!license) {
3730
+ this.log("Could not validate license online, using key metadata.", "warn");
3731
+ result.warnings.push("License could not be validated online.");
3732
+ if (this.config.requiredTier && (metadata == null ? void 0 : metadata.tier)) {
3733
+ if (!tierMeetsRequirement(metadata.tier, this.config.requiredTier)) {
3734
+ result.errors.push(
3735
+ `License tier "${metadata.tier}" does not meet required tier "${this.config.requiredTier}".`
3736
+ );
3737
+ this.validationResult = result;
3738
+ return result;
3739
+ }
3740
+ }
3741
+ result.valid = !this.config.failOnInvalid;
3742
+ this.validationResult = result;
3743
+ return result;
3744
+ }
3745
+ await this.saveLicenseCache(licenseKey, license);
3746
+ this.licenseInfo = license;
3747
+ result.licensee = license.licensee;
3748
+ result.tier = license.tier;
3749
+ result.features = license.features;
3750
+ result.expiresAt = license.expiresAt || void 0;
3751
+ if (license.expiresAt) {
3752
+ const expiresAt = new Date(license.expiresAt);
3753
+ const now = /* @__PURE__ */ new Date();
3754
+ const daysRemaining = Math.ceil(
3755
+ (expiresAt.getTime() - now.getTime()) / (1e3 * 60 * 60 * 24)
3756
+ );
3757
+ result.daysRemaining = daysRemaining;
3758
+ if (daysRemaining <= 0) {
3759
+ if (this.config.failOnExpired) {
3760
+ result.errors.push("License has expired.");
3761
+ this.validationResult = result;
3762
+ return result;
3763
+ } else {
3764
+ result.warnings.push("License has expired. Some features may be disabled.");
3765
+ }
3766
+ } else if (daysRemaining <= 30) {
3767
+ result.warnings.push(`License expires in ${daysRemaining} days.`);
3768
+ }
3769
+ }
3770
+ if (this.config.requiredTier) {
3771
+ if (!tierMeetsRequirement(license.tier, this.config.requiredTier)) {
3772
+ result.errors.push(
3773
+ `License tier "${license.tier}" does not meet required tier "${this.config.requiredTier}".`
3774
+ );
3775
+ this.validationResult = result;
3776
+ return result;
3777
+ }
3778
+ }
3779
+ if ((_a = this.config.requiredFeatures) == null ? void 0 : _a.length) {
3780
+ const missingFeatures = this.config.requiredFeatures.filter(
3781
+ (f2) => !license.features.includes(f2)
3782
+ );
3783
+ if (missingFeatures.length > 0) {
3784
+ result.errors.push(`License missing required features: ${missingFeatures.join(", ")}`);
3785
+ this.validationResult = result;
3786
+ return result;
3787
+ }
3788
+ }
3789
+ result.valid = true;
3790
+ this.validationResult = result;
3791
+ this.log(`License validated: ${license.licensee} (${license.tier})`);
3792
+ return result;
3793
+ }
3794
+ /** Get validation result */
3795
+ getResult() {
3796
+ return this.validationResult;
3797
+ }
3798
+ /** Get license info */
3799
+ getLicenseInfo() {
3800
+ return this.licenseInfo;
3801
+ }
3802
+ /** Check if feature is available */
3803
+ hasFeature(featureId) {
3804
+ var _a;
3805
+ return ((_a = this.licenseInfo) == null ? void 0 : _a.features.includes(featureId)) ?? false;
3806
+ }
3807
+ /** Check if tier meets requirement */
3808
+ meetsTier(requiredTier) {
3809
+ return this.licenseInfo ? tierMeetsRequirement(this.licenseInfo.tier, requiredTier) : false;
3810
+ }
3811
+ }
3812
+ class Nice2DevLicenseWebpackPlugin {
3813
+ constructor(options = {}) {
3814
+ this.validated = false;
3815
+ this.options = options;
3816
+ this.validator = new BuildLicenseValidator(options);
3817
+ }
3818
+ apply(compiler) {
3819
+ compiler.hooks.beforeRun.tapPromise("Nice2DevLicensePlugin", async () => {
3820
+ if (this.validated) {
3821
+ return;
3822
+ }
3823
+ const result = await this.validator.validate();
3824
+ this.validated = true;
3825
+ result.warnings.forEach((w2) => console.warn(`[nice2dev] ⚠ ${w2}`));
3826
+ if (result.errors.length > 0 && this.options.failOnInvalid !== false) {
3827
+ result.errors.forEach((e) => console.error(`[nice2dev] ✗ ${e}`));
3828
+ throw new Error("License validation failed");
3829
+ }
3830
+ });
3831
+ compiler.hooks.compilation.tap("Nice2DevLicensePlugin", (compilation) => {
3832
+ var _a, _b;
3833
+ const DefinePlugin = ((_a = compiler.webpack) == null ? void 0 : _a.DefinePlugin) || require("webpack").DefinePlugin;
3834
+ const license = this.validator.getLicenseInfo();
3835
+ new DefinePlugin({
3836
+ "process.env.NICE2DEV_LICENSE_VALID": JSON.stringify(
3837
+ ((_b = this.validator.getResult()) == null ? void 0 : _b.valid) ?? false
3838
+ ),
3839
+ "process.env.NICE2DEV_LICENSE_TIER": JSON.stringify((license == null ? void 0 : license.tier) ?? "trial"),
3840
+ "process.env.NICE2DEV_LICENSE_FEATURES": JSON.stringify((license == null ? void 0 : license.features) ?? [])
3841
+ }).apply(compiler);
3842
+ });
3843
+ }
3844
+ }
3845
+ function nice2devLicenseVitePlugin(options = {}) {
3846
+ const validator = new BuildLicenseValidator(options);
3847
+ let validated = false;
3848
+ return {
3849
+ name: "nice2dev-license",
3850
+ async buildStart() {
3851
+ if (validated) {
3852
+ return;
3853
+ }
3854
+ const result = await validator.validate();
3855
+ validated = true;
3856
+ result.warnings.forEach((w2) => this.warn(w2));
3857
+ if (result.errors.length > 0 && options.failOnInvalid !== false) {
3858
+ result.errors.forEach((e) => this.error(e));
3859
+ throw new Error("License validation failed");
3860
+ }
3861
+ },
3862
+ config() {
3863
+ var _a;
3864
+ const license = validator.getLicenseInfo();
3865
+ return {
3866
+ define: {
3867
+ "import.meta.env.NICE2DEV_LICENSE_VALID": JSON.stringify(
3868
+ ((_a = validator.getResult()) == null ? void 0 : _a.valid) ?? false
3869
+ ),
3870
+ "import.meta.env.NICE2DEV_LICENSE_TIER": JSON.stringify((license == null ? void 0 : license.tier) ?? "trial"),
3871
+ "import.meta.env.NICE2DEV_LICENSE_FEATURES": JSON.stringify((license == null ? void 0 : license.features) ?? [])
3872
+ }
3873
+ };
3874
+ }
3875
+ };
3876
+ }
3877
+ function nice2devLicenseRollupPlugin(options = {}) {
3878
+ const validator = new BuildLicenseValidator(options);
3879
+ let validated = false;
3880
+ return {
3881
+ name: "nice2dev-license",
3882
+ async buildStart() {
3883
+ if (validated) {
3884
+ return;
3885
+ }
3886
+ const result = await validator.validate();
3887
+ validated = true;
3888
+ result.warnings.forEach((w2) => this.warn(w2));
3889
+ if (result.errors.length > 0 && options.failOnInvalid !== false) {
3890
+ throw new Error(result.errors.join("\n"));
3891
+ }
3892
+ }
3893
+ };
3894
+ }
3895
+ function detectCIPlatform() {
3896
+ const env = process.env;
3897
+ if (env.GITHUB_ACTIONS === "true") {
3898
+ return "github-actions";
3899
+ }
3900
+ if (env.TF_BUILD === "True") {
3901
+ return "azure-devops";
3902
+ }
3903
+ if (env.GITLAB_CI === "true") {
3904
+ return "gitlab-ci";
3905
+ }
3906
+ if (env.JENKINS_URL) {
3907
+ return "jenkins";
3908
+ }
3909
+ if (env.CIRCLECI === "true") {
3910
+ return "circleci";
3911
+ }
3912
+ if (env.BITBUCKET_BUILD_NUMBER) {
3913
+ return "bitbucket-pipelines";
3914
+ }
3915
+ if (env.TRAVIS === "true") {
3916
+ return "travis-ci";
3917
+ }
3918
+ return "unknown";
3919
+ }
3920
+ function isForkPR() {
3921
+ const env = process.env;
3922
+ const platform = detectCIPlatform();
3923
+ switch (platform) {
3924
+ case "github-actions":
3925
+ return env.GITHUB_EVENT_NAME === "pull_request" && env.GITHUB_HEAD_REF !== void 0 && env.GITHUB_REPOSITORY !== env.GITHUB_HEAD_REPOSITORY;
3926
+ case "gitlab-ci":
3927
+ return env.CI_MERGE_REQUEST_SOURCE_PROJECT_PATH !== env.CI_PROJECT_PATH;
3928
+ case "azure-devops":
3929
+ return env.BUILD_REASON === "PullRequest" && env.SYSTEM_PULLREQUEST_ISFORK === "True";
3930
+ default:
3931
+ return false;
3932
+ }
3933
+ }
3934
+ class CIOutputFormatter {
3935
+ constructor(platform, format = "human") {
3936
+ this.platform = platform;
3937
+ this.outputFormat = format;
3938
+ }
3939
+ info(message) {
3940
+ if (this.outputFormat === "json") {
3941
+ return;
3942
+ }
3943
+ console.log(`ℹ️ ${message}`);
3944
+ }
3945
+ success(message) {
3946
+ if (this.outputFormat === "json") {
3947
+ return;
3948
+ }
3949
+ switch (this.platform) {
3950
+ case "github-actions":
3951
+ console.log(`::notice::${message}`);
3952
+ break;
3953
+ case "azure-devops":
3954
+ console.log(`##[section]${message}`);
3955
+ break;
3956
+ default:
3957
+ console.log(`✅ ${message}`);
3958
+ }
3959
+ }
3960
+ warning(message) {
3961
+ if (this.outputFormat === "json") {
3962
+ return;
3963
+ }
3964
+ switch (this.platform) {
3965
+ case "github-actions":
3966
+ console.log(`::warning::${message}`);
3967
+ break;
3968
+ case "azure-devops":
3969
+ console.log(`##[warning]${message}`);
3970
+ break;
3971
+ case "gitlab-ci":
3972
+ console.warn(`\x1B[33mWarning: ${message}\x1B[0m`);
3973
+ break;
3974
+ default:
3975
+ console.warn(`⚠️ ${message}`);
3976
+ }
3977
+ }
3978
+ error(message) {
3979
+ if (this.outputFormat === "json") {
3980
+ return;
3981
+ }
3982
+ switch (this.platform) {
3983
+ case "github-actions":
3984
+ console.log(`::error::${message}`);
3985
+ break;
3986
+ case "azure-devops":
3987
+ console.log(`##[error]${message}`);
3988
+ break;
3989
+ case "gitlab-ci":
3990
+ console.error(`\x1B[31mError: ${message}\x1B[0m`);
3991
+ break;
3992
+ default:
3993
+ console.error(`❌ ${message}`);
3994
+ }
3995
+ }
3996
+ setOutput(name, value) {
3997
+ switch (this.platform) {
3998
+ case "github-actions":
3999
+ const fs = require("fs");
4000
+ const outputFile = process.env.GITHUB_OUTPUT;
4001
+ if (outputFile) {
4002
+ fs.appendFileSync(outputFile, `${name}=${value}
4003
+ `);
4004
+ }
4005
+ break;
4006
+ case "azure-devops":
4007
+ console.log(`##vso[task.setvariable variable=${name}]${value}`);
4008
+ break;
4009
+ }
4010
+ }
4011
+ setEnvVar(name, value) {
4012
+ switch (this.platform) {
4013
+ case "github-actions":
4014
+ const fs = require("fs");
4015
+ const envFile = process.env.GITHUB_ENV;
4016
+ if (envFile) {
4017
+ fs.appendFileSync(envFile, `${name}=${value}
4018
+ `);
4019
+ }
4020
+ break;
4021
+ case "azure-devops":
4022
+ console.log(`##vso[task.setvariable variable=${name}]${value}`);
4023
+ break;
4024
+ }
4025
+ }
4026
+ group(name, content) {
4027
+ switch (this.platform) {
4028
+ case "github-actions":
4029
+ console.log(`::group::${name}`);
4030
+ content();
4031
+ console.log("::endgroup::");
4032
+ break;
4033
+ case "azure-devops":
4034
+ console.log(`##[group]${name}`);
4035
+ content();
4036
+ console.log("##[endgroup]");
4037
+ break;
4038
+ default:
4039
+ console.log(`
4040
+ --- ${name} ---`);
4041
+ content();
4042
+ console.log("");
4043
+ }
4044
+ }
4045
+ }
4046
+ async function validateLicenseOnline(licenseKey, serverUrl) {
4047
+ try {
4048
+ const response = await fetch(`${serverUrl}/api/licenses/validate`, {
4049
+ method: "POST",
4050
+ headers: { "Content-Type": "application/json" },
4051
+ body: JSON.stringify({ licenseKey, context: "ci" })
4052
+ });
4053
+ if (!response.ok) {
4054
+ return null;
4055
+ }
4056
+ const data = await response.json();
4057
+ return data.license || null;
4058
+ } catch {
4059
+ return null;
4060
+ }
4061
+ }
4062
+ async function runCILicenseCheck(options = {}) {
4063
+ var _a;
4064
+ const platform = detectCIPlatform();
4065
+ const formatter = new CIOutputFormatter(platform, options.outputFormat);
4066
+ const result = {
4067
+ success: false,
4068
+ platform,
4069
+ exitCode: 0,
4070
+ message: "",
4071
+ warnings: [],
4072
+ errors: []
4073
+ };
4074
+ if (options.skipForForks && isForkPR()) {
4075
+ formatter.info("Skipping license check for fork PR");
4076
+ result.success = true;
4077
+ result.message = "License check skipped for fork PR";
4078
+ return result;
4079
+ }
4080
+ const licenseKey = options.licenseKey || process.env[options.licenseKeyEnvVar || "NICE2DEV_LICENSE_KEY"];
4081
+ if (!licenseKey) {
4082
+ const error = "No license key found. Set NICE2DEV_LICENSE_KEY secret or pass licenseKey option.";
4083
+ formatter.error(error);
4084
+ result.errors.push(error);
4085
+ result.message = error;
4086
+ result.exitCode = options.failOnInvalid !== false ? 1 : 0;
4087
+ result.success = options.failOnInvalid === false;
4088
+ return result;
4089
+ }
4090
+ formatter.info(`Validating license: ${licenseKey.substring(0, 12)}...`);
4091
+ if (!validateKeyFormat$1(licenseKey)) {
4092
+ const error = "Invalid license key format";
4093
+ formatter.error(error);
4094
+ result.errors.push(error);
4095
+ result.message = error;
4096
+ result.exitCode = 1;
4097
+ return result;
4098
+ }
4099
+ const metadata = extractKeyMetadata(licenseKey);
4100
+ const license = {
4101
+ key: licenseKey,
4102
+ tier: (metadata == null ? void 0 : metadata.tier) || "trial",
4103
+ features: [],
4104
+ licensee: void 0,
4105
+ expiresAt: void 0,
4106
+ daysRemaining: void 0
4107
+ };
4108
+ result.license = license;
4109
+ if (options.licenseServerUrl) {
4110
+ const onlineLicense = await validateLicenseOnline(licenseKey, options.licenseServerUrl);
4111
+ if (onlineLicense) {
4112
+ license.licensee = onlineLicense.licensee;
4113
+ license.tier = onlineLicense.tier;
4114
+ license.features = onlineLicense.features;
4115
+ license.expiresAt = onlineLicense.expiresAt || void 0;
4116
+ formatter.success(`License validated for: ${onlineLicense.licensee} (${onlineLicense.tier})`);
4117
+ } else {
4118
+ const warning = "Could not validate license online, using key metadata";
4119
+ formatter.warning(warning);
4120
+ result.warnings.push(warning);
4121
+ }
4122
+ }
4123
+ if (license.expiresAt) {
4124
+ const expiresAt = new Date(license.expiresAt);
4125
+ const now = /* @__PURE__ */ new Date();
4126
+ const daysRemaining = Math.ceil((expiresAt.getTime() - now.getTime()) / (1e3 * 60 * 60 * 24));
4127
+ license.daysRemaining = daysRemaining;
4128
+ const graceDays = options.expirationGraceDays ?? 0;
4129
+ if (daysRemaining < -graceDays) {
4130
+ const error = `License expired ${Math.abs(daysRemaining)} days ago`;
4131
+ if (options.failOnExpired !== false) {
4132
+ formatter.error(error);
4133
+ result.errors.push(error);
4134
+ result.exitCode = 1;
4135
+ result.message = error;
4136
+ return result;
4137
+ } else {
4138
+ formatter.warning(error);
4139
+ result.warnings.push(error);
4140
+ }
4141
+ } else if (daysRemaining <= 0) {
4142
+ const warning = `License expired but within grace period (${graceDays} days)`;
4143
+ formatter.warning(warning);
4144
+ result.warnings.push(warning);
4145
+ } else if (daysRemaining <= 30) {
4146
+ const warning = `License expires in ${daysRemaining} days`;
4147
+ formatter.warning(warning);
4148
+ result.warnings.push(warning);
4149
+ }
4150
+ }
4151
+ if (options.requiredTier) {
4152
+ if (!tierMeetsRequirement(license.tier, options.requiredTier)) {
4153
+ const error = `License tier "${license.tier}" does not meet required tier "${options.requiredTier}"`;
4154
+ formatter.error(error);
4155
+ result.errors.push(error);
4156
+ result.exitCode = 1;
4157
+ result.message = error;
4158
+ return result;
4159
+ }
4160
+ formatter.info(`Tier check passed: ${license.tier} >= ${options.requiredTier}`);
4161
+ }
4162
+ if ((_a = options.requiredFeatures) == null ? void 0 : _a.length) {
4163
+ const missingFeatures = options.requiredFeatures.filter((f2) => !license.features.includes(f2));
4164
+ if (missingFeatures.length > 0) {
4165
+ const error = `Missing required features: ${missingFeatures.join(", ")}`;
4166
+ formatter.error(error);
4167
+ result.errors.push(error);
4168
+ result.exitCode = 1;
4169
+ result.message = error;
4170
+ return result;
4171
+ }
4172
+ formatter.info(`Feature check passed: ${options.requiredFeatures.join(", ")}`);
4173
+ }
4174
+ if (options.setEnvVars !== false) {
4175
+ formatter.setEnvVar("NICE2DEV_LICENSE_VALID", "true");
4176
+ formatter.setEnvVar("NICE2DEV_LICENSE_TIER", license.tier);
4177
+ formatter.setOutput("license-valid", "true");
4178
+ formatter.setOutput("license-tier", license.tier);
4179
+ }
4180
+ result.success = true;
4181
+ result.message = `License validated successfully (${license.tier})`;
4182
+ formatter.success(result.message);
4183
+ if (options.outputFormat === "json") {
4184
+ console.log(JSON.stringify(result, null, 2));
4185
+ }
4186
+ return result;
4187
+ }
4188
+ function generateGitHubActionsWorkflow(options) {
4189
+ return `name: License Check
4190
+
4191
+ on:
4192
+ pull_request:
4193
+ push:
4194
+ branches: [main, master]
4195
+
4196
+ jobs:
4197
+ license-check:
4198
+ runs-on: ubuntu-latest
4199
+ steps:
4200
+ - uses: actions/checkout@v4
4201
+
4202
+ - name: Setup Node.js
4203
+ uses: actions/setup-node@v4
4204
+ with:
4205
+ node-version: '20'
4206
+
4207
+ - name: Install dependencies
4208
+ run: npm ci
4209
+
4210
+ - name: Validate License
4211
+ run: npx nice2dev-license-check${options.requiredTier ? ` -t ${options.requiredTier}` : ""}${options.skipForForks ? " --skip-for-forks" : ""}
4212
+ env:
4213
+ NICE2DEV_LICENSE_KEY: \${{ secrets.NICE2DEV_LICENSE_KEY }}
4214
+ `;
4215
+ }
4216
+ function generateAzureDevOpsPipeline(options) {
4217
+ return `trigger:
4218
+ - main
4219
+ - master
4220
+
4221
+ pool:
4222
+ vmImage: 'ubuntu-latest'
4223
+
4224
+ steps:
4225
+ - task: NodeTool@0
4226
+ inputs:
4227
+ versionSpec: '20.x'
4228
+ displayName: 'Install Node.js'
4229
+
4230
+ - script: npm ci
4231
+ displayName: 'Install dependencies'
4232
+
4233
+ - script: npx nice2dev-license-check${options.requiredTier ? ` -t ${options.requiredTier}` : ""}
4234
+ displayName: 'Validate License'
4235
+ env:
4236
+ NICE2DEV_LICENSE_KEY: $(NICE2DEV_LICENSE_KEY)
4237
+ `;
4238
+ }
4239
+ function generateGitLabCIPipeline(options) {
4240
+ return `stages:
4241
+ - validate
4242
+
4243
+ license-check:
4244
+ stage: validate
4245
+ image: node:20
4246
+ script:
4247
+ - npm ci
4248
+ - npx nice2dev-license-check${options.requiredTier ? ` -t ${options.requiredTier}` : ""}
4249
+ variables:
4250
+ NICE2DEV_LICENSE_KEY: $NICE2DEV_LICENSE_KEY
4251
+ `;
4252
+ }
4253
+ const VERSION = "1.1.0";
4254
+ export {
4255
+ ActivationService,
4256
+ ActivationWizard,
4257
+ ApiRateLimiter,
4258
+ AuditTrail,
4259
+ BillingService,
4260
+ BuildLicenseValidator,
4261
+ DEFAULT_RATE_LIMITS,
4262
+ DEFAULT_REFUND_POLICY,
4263
+ DEFAULT_SLA_CONFIGS,
4264
+ FloatingLicenseManager,
4265
+ LicenseCard,
4266
+ LicensePortal,
4267
+ LicensePortalService,
4268
+ LicenseStatusBadge,
4269
+ LicenseTierBadge,
4270
+ LicenseTransfer,
4271
+ LicenseTransferService,
4272
+ L as LicenseValidator,
4273
+ Nice2DevLicenseWebpackPlugin,
4274
+ RateLimiter,
4275
+ RefundService,
4276
+ SeatManager,
4277
+ SlaTracker,
4278
+ TransferStatusBadge,
4279
+ UsageAnalyticsDashboard,
4280
+ UsageAnalyticsService,
4281
+ UsageTelemetry,
4282
+ VERSION,
4283
+ WhiteLabelManager,
4284
+ c as checkFeatureAccess,
4285
+ b as collectFingerprintData,
4286
+ d as compareFingerprintsData,
4287
+ f as compareTiers,
4288
+ createApiRateLimiter,
4289
+ createAuditTrail,
4290
+ createBillingService,
4291
+ createFloatingLicenseManager,
4292
+ createRefundService,
4293
+ createSeatManager,
4294
+ createSlaTracker,
4295
+ createTelemetry,
4296
+ createWhiteLabelManager,
4297
+ detectCIPlatform,
4298
+ extractKeyMetadata,
4299
+ generateAzureDevOpsPipeline,
4300
+ generateBundleTags,
4301
+ generateFingerprint,
4302
+ generateGitHubActionsWorkflow,
4303
+ generateGitLabCIPipeline,
4304
+ h as generateLicenseInfo,
4305
+ i as generateLicenseKey,
4306
+ generateMachineId,
4307
+ generateProtectionManifest,
4308
+ getAccessibleModules,
4309
+ j as getAllFeatures,
4310
+ k as getAllPlans,
4311
+ getAllProtectedModules,
4312
+ l as getAvailableFeatures,
4313
+ m as getDefaultFeatures,
4314
+ n as getDefaultMachines,
4315
+ o as getDefaultSeats,
4316
+ p as getFeature,
4317
+ q as getFeaturesByCategory,
4318
+ getInaccessibleModules,
4319
+ r as getMissingFeatures,
4320
+ s as getOrGenerateFingerprint,
4321
+ u as getPlan,
4322
+ getProtectedModule,
4323
+ w as getStoredFingerprint,
4324
+ x as getValidator,
4325
+ y as hasFeature,
4326
+ z as hasTier,
4327
+ hashKeyForAudit,
4328
+ hashLicenseKey,
4329
+ A as initializeDefaults,
4330
+ B as isFeatureLicensed,
4331
+ isForkPR,
4332
+ isModuleAccessible,
4333
+ C as maskLicenseKey,
4334
+ nice2devLicenseRollupPlugin,
4335
+ nice2devLicenseVitePlugin,
4336
+ D as normalizeLicenseKey,
4337
+ E as registerFeature,
4338
+ F as registerFeatures,
4339
+ G as registerPlan,
4340
+ H as registerPlans,
4341
+ registerProtectedModule,
4342
+ registerProtectedModules,
4343
+ runCILicenseCheck,
4344
+ I as storeFingerprint,
4345
+ tierMeetsRequirement,
4346
+ useActivationWizard,
4347
+ useLicensePortal,
4348
+ useLicenseTransfer,
4349
+ useUsageAnalytics,
4350
+ validateKeyFormat$1 as validateKeyFormat,
4351
+ J as validateLicense
4352
+ };
4353
+ //# sourceMappingURL=index.mjs.map