@moontra/moonui-pro 2.0.22 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/dist/index.mjs +215 -214
  2. package/package.json +4 -2
  3. package/src/__tests__/use-intersection-observer.test.tsx +216 -0
  4. package/src/__tests__/use-local-storage.test.tsx +174 -0
  5. package/src/__tests__/use-pro-access.test.tsx +183 -0
  6. package/src/components/advanced-chart/advanced-chart.test.tsx +281 -0
  7. package/src/components/advanced-chart/index.tsx +412 -0
  8. package/src/components/advanced-forms/index.tsx +431 -0
  9. package/src/components/animated-button/index.tsx +202 -0
  10. package/src/components/calendar/event-dialog.tsx +372 -0
  11. package/src/components/calendar/index.tsx +557 -0
  12. package/src/components/color-picker/index.tsx +434 -0
  13. package/src/components/dashboard/index.tsx +334 -0
  14. package/src/components/data-table/data-table.test.tsx +187 -0
  15. package/src/components/data-table/index.tsx +368 -0
  16. package/src/components/draggable-list/index.tsx +100 -0
  17. package/src/components/enhanced/button.tsx +360 -0
  18. package/src/components/enhanced/card.tsx +272 -0
  19. package/src/components/enhanced/dialog.tsx +248 -0
  20. package/src/components/enhanced/index.ts +3 -0
  21. package/src/components/error-boundary/index.tsx +111 -0
  22. package/src/components/file-upload/file-upload.test.tsx +242 -0
  23. package/src/components/file-upload/index.tsx +362 -0
  24. package/src/components/floating-action-button/index.tsx +209 -0
  25. package/src/components/github-stars/index.tsx +414 -0
  26. package/src/components/health-check/index.tsx +441 -0
  27. package/src/components/hover-card-3d/index.tsx +170 -0
  28. package/src/components/index.ts +76 -0
  29. package/src/components/kanban/index.tsx +436 -0
  30. package/src/components/lazy-component/index.tsx +342 -0
  31. package/src/components/magnetic-button/index.tsx +170 -0
  32. package/src/components/memory-efficient-data/index.tsx +352 -0
  33. package/src/components/optimized-image/index.tsx +427 -0
  34. package/src/components/performance-debugger/index.tsx +591 -0
  35. package/src/components/performance-monitor/index.tsx +775 -0
  36. package/src/components/pinch-zoom/index.tsx +172 -0
  37. package/src/components/rich-text-editor/index-old-backup.tsx +443 -0
  38. package/src/components/rich-text-editor/index.tsx +1537 -0
  39. package/src/components/rich-text-editor/slash-commands-extension.ts +220 -0
  40. package/src/components/rich-text-editor/slash-commands.css +35 -0
  41. package/src/components/rich-text-editor/table-styles.css +65 -0
  42. package/src/components/spotlight-card/index.tsx +194 -0
  43. package/src/components/swipeable-card/index.tsx +100 -0
  44. package/src/components/timeline/index.tsx +333 -0
  45. package/src/components/ui/animated-button.tsx +185 -0
  46. package/src/components/ui/avatar.tsx +135 -0
  47. package/src/components/ui/badge.tsx +225 -0
  48. package/src/components/ui/button.tsx +221 -0
  49. package/src/components/ui/card.tsx +141 -0
  50. package/src/components/ui/checkbox.tsx +256 -0
  51. package/src/components/ui/color-picker.tsx +95 -0
  52. package/src/components/ui/dialog.tsx +332 -0
  53. package/src/components/ui/dropdown-menu.tsx +200 -0
  54. package/src/components/ui/hover-card-3d.tsx +103 -0
  55. package/src/components/ui/index.ts +33 -0
  56. package/src/components/ui/input.tsx +219 -0
  57. package/src/components/ui/label.tsx +26 -0
  58. package/src/components/ui/magnetic-button.tsx +129 -0
  59. package/src/components/ui/popover.tsx +183 -0
  60. package/src/components/ui/select.tsx +273 -0
  61. package/src/components/ui/separator.tsx +140 -0
  62. package/src/components/ui/slider.tsx +351 -0
  63. package/src/components/ui/spotlight-card.tsx +119 -0
  64. package/src/components/ui/switch.tsx +83 -0
  65. package/src/components/ui/tabs.tsx +195 -0
  66. package/src/components/ui/textarea.tsx +25 -0
  67. package/src/components/ui/toast.tsx +313 -0
  68. package/src/components/ui/tooltip.tsx +152 -0
  69. package/src/components/virtual-list/index.tsx +369 -0
  70. package/src/hooks/use-chart.ts +205 -0
  71. package/src/hooks/use-data-table.ts +182 -0
  72. package/src/hooks/use-docs-pro-access.ts +13 -0
  73. package/src/hooks/use-license-check.ts +65 -0
  74. package/src/hooks/use-subscription.ts +19 -0
  75. package/src/index.ts +14 -0
  76. package/src/lib/micro-interactions.ts +255 -0
  77. package/src/lib/utils.ts +6 -0
  78. package/src/patterns/login-form/index.tsx +276 -0
  79. package/src/patterns/login-form/types.ts +67 -0
  80. package/src/setupTests.ts +41 -0
  81. package/src/styles/design-system.css +365 -0
  82. package/src/styles/index.css +4 -0
  83. package/src/styles/tailwind.css +6 -0
  84. package/src/styles/tokens.css +453 -0
  85. package/src/types/moonui.d.ts +22 -0
  86. package/src/use-intersection-observer.tsx +154 -0
  87. package/src/use-local-storage.tsx +71 -0
  88. package/src/use-paddle.ts +138 -0
  89. package/src/use-performance-optimizer.ts +379 -0
  90. package/src/use-pro-access.ts +141 -0
  91. package/src/use-scroll-animation.ts +221 -0
  92. package/src/use-subscription.ts +37 -0
  93. package/src/use-toast.ts +32 -0
  94. package/src/utils/chart-helpers.ts +257 -0
  95. package/src/utils/cn.ts +69 -0
  96. package/src/utils/data-processing.ts +151 -0
  97. package/src/utils/license-guard.tsx +177 -0
  98. package/src/utils/license-validator.tsx +183 -0
  99. package/src/utils/package-guard.ts +60 -0
@@ -0,0 +1,151 @@
1
+ export interface DataProcessor<T = any> {
2
+ filter: (data: T[], predicate: (item: T) => boolean) => T[]
3
+ sort: (data: T[], key: keyof T, direction?: 'asc' | 'desc') => T[]
4
+ group: (data: T[], key: keyof T) => Record<string, T[]>
5
+ aggregate: (data: T[], key: keyof T, operation: 'sum' | 'avg' | 'min' | 'max' | 'count') => number
6
+ paginate: (data: T[], page: number, pageSize: number) => T[]
7
+ search: (data: T[], query: string, searchKeys?: (keyof T)[]) => T[]
8
+ transform: <U>(data: T[], transformer: (item: T) => U) => U[]
9
+ }
10
+
11
+ export function createDataProcessor<T = any>(): DataProcessor<T> {
12
+ return {
13
+ filter: (data: T[], predicate: (item: T) => boolean) => {
14
+ return data.filter(predicate)
15
+ },
16
+
17
+ sort: (data: T[], key: keyof T, direction: 'asc' | 'desc' = 'asc') => {
18
+ return [...data].sort((a, b) => {
19
+ const aVal = a[key]
20
+ const bVal = b[key]
21
+
22
+ if (aVal === bVal) return 0
23
+
24
+ const comparison = aVal < bVal ? -1 : 1
25
+ return direction === 'asc' ? comparison : -comparison
26
+ })
27
+ },
28
+
29
+ group: (data: T[], key: keyof T) => {
30
+ return data.reduce((groups, item) => {
31
+ const groupKey = String(item[key])
32
+ if (!groups[groupKey]) {
33
+ groups[groupKey] = []
34
+ }
35
+ groups[groupKey].push(item)
36
+ return groups
37
+ }, {} as Record<string, T[]>)
38
+ },
39
+
40
+ aggregate: (data: T[], key: keyof T, operation: 'sum' | 'avg' | 'min' | 'max' | 'count') => {
41
+ const values = data.map(item => Number(item[key])).filter(val => !isNaN(val))
42
+
43
+ if (values.length === 0) return 0
44
+
45
+ switch (operation) {
46
+ case 'sum':
47
+ return values.reduce((sum, val) => sum + val, 0)
48
+ case 'avg':
49
+ return values.reduce((sum, val) => sum + val, 0) / values.length
50
+ case 'min':
51
+ return Math.min(...values)
52
+ case 'max':
53
+ return Math.max(...values)
54
+ case 'count':
55
+ return values.length
56
+ default:
57
+ return 0
58
+ }
59
+ },
60
+
61
+ paginate: (data: T[], page: number, pageSize: number) => {
62
+ const startIndex = (page - 1) * pageSize
63
+ const endIndex = startIndex + pageSize
64
+ return data.slice(startIndex, endIndex)
65
+ },
66
+
67
+ search: (data: T[], query: string, searchKeys?: (keyof T)[]) => {
68
+ if (!query.trim()) return data
69
+
70
+ const lowerQuery = query.toLowerCase()
71
+
72
+ return data.filter(item => {
73
+ const keysToSearch = searchKeys || Object.keys(item as Record<string, any>) as (keyof T)[]
74
+
75
+ return keysToSearch.some(key => {
76
+ const value = item[key]
77
+ if (value == null) return false
78
+ return String(value).toLowerCase().includes(lowerQuery)
79
+ })
80
+ })
81
+ },
82
+
83
+ transform: <U>(data: T[], transformer: (item: T) => U) => {
84
+ return data.map(transformer)
85
+ }
86
+ }
87
+ }
88
+
89
+ // Utility functions for common data operations
90
+ export function calculatePercentageChange(current: number, previous: number): number {
91
+ if (previous === 0) return current > 0 ? 100 : 0
92
+ return ((current - previous) / previous) * 100
93
+ }
94
+
95
+ export function formatNumber(value: number, options?: {
96
+ decimals?: number
97
+ currency?: string
98
+ percentage?: boolean
99
+ compact?: boolean
100
+ }): string {
101
+ const { decimals = 2, currency, percentage = false, compact = false } = options || {}
102
+
103
+ if (percentage) {
104
+ return `${value.toFixed(decimals)}%`
105
+ }
106
+
107
+ if (currency) {
108
+ return new Intl.NumberFormat('en-US', {
109
+ style: 'currency',
110
+ currency,
111
+ minimumFractionDigits: decimals,
112
+ maximumFractionDigits: decimals,
113
+ }).format(value)
114
+ }
115
+
116
+ if (compact && Math.abs(value) >= 1000) {
117
+ return new Intl.NumberFormat('en-US', {
118
+ notation: 'compact',
119
+ minimumFractionDigits: 0,
120
+ maximumFractionDigits: 1,
121
+ }).format(value)
122
+ }
123
+
124
+ return value.toFixed(decimals)
125
+ }
126
+
127
+ export function generateDateRange(start: Date, end: Date, interval: 'day' | 'week' | 'month' = 'day'): Date[] {
128
+ const dates: Date[] = []
129
+ const current = new Date(start)
130
+
131
+ while (current <= end) {
132
+ dates.push(new Date(current))
133
+
134
+ switch (interval) {
135
+ case 'day':
136
+ current.setDate(current.getDate() + 1)
137
+ break
138
+ case 'week':
139
+ current.setDate(current.getDate() + 7)
140
+ break
141
+ case 'month':
142
+ current.setMonth(current.getMonth() + 1)
143
+ break
144
+ }
145
+ }
146
+
147
+ return dates
148
+ }
149
+
150
+ // debounce function removed to avoid conflicts with @moontra/moonui
151
+ // Use debounce from @moontra/moonui instead
@@ -0,0 +1,177 @@
1
+ "use client"
2
+
3
+ import React, { useEffect, useState } from 'react';
4
+
5
+ const API_BASE = process.env.NEXT_PUBLIC_MOONUI_API || 'https://moonui.dev';
6
+
7
+ interface LicenseCheckResult {
8
+ valid: boolean;
9
+ error?: string;
10
+ loading: boolean;
11
+ }
12
+
13
+ // In-memory cache for license check
14
+ let licenseCache: {
15
+ result: boolean;
16
+ timestamp: number;
17
+ } | null = null;
18
+
19
+ const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
20
+
21
+ export function useLicenseCheck(): LicenseCheckResult {
22
+ const [state, setState] = useState<LicenseCheckResult>({
23
+ valid: false,
24
+ loading: true,
25
+ error: undefined
26
+ });
27
+
28
+ useEffect(() => {
29
+ checkLicense().then(result => {
30
+ setState({
31
+ valid: result.valid,
32
+ loading: false,
33
+ error: result.error
34
+ });
35
+ });
36
+ }, []);
37
+
38
+ return state;
39
+ }
40
+
41
+ async function checkLicense(): Promise<{ valid: boolean; error?: string }> {
42
+ // Check cache first
43
+ if (licenseCache && Date.now() - licenseCache.timestamp < CACHE_DURATION) {
44
+ return { valid: licenseCache.result };
45
+ }
46
+
47
+ try {
48
+ // Get auth token from localStorage or cookie
49
+ const token = getAuthToken();
50
+
51
+ if (!token) {
52
+ return { valid: false, error: 'No authentication token found' };
53
+ }
54
+
55
+ const response = await fetch(`${API_BASE}/api/license/verify`, {
56
+ method: 'POST',
57
+ headers: {
58
+ 'Authorization': `Bearer ${token}`,
59
+ 'Content-Type': 'application/json'
60
+ },
61
+ body: JSON.stringify({
62
+ component: 'runtime-check',
63
+ source: 'moonui-pro'
64
+ })
65
+ });
66
+
67
+ if (!response.ok) {
68
+ return { valid: false, error: 'License verification failed' };
69
+ }
70
+
71
+ const data = await response.json();
72
+
73
+ // Cache the result
74
+ licenseCache = {
75
+ result: data.valid && data.planType !== 'free',
76
+ timestamp: Date.now()
77
+ };
78
+
79
+ return { valid: licenseCache.result };
80
+ } catch (error) {
81
+ return { valid: false, error: 'Network error during license check' };
82
+ }
83
+ }
84
+
85
+ function getAuthToken(): string | null {
86
+ // First check localStorage (for client-side)
87
+ if (typeof window !== 'undefined') {
88
+ const stored = localStorage.getItem('moonui_auth');
89
+ if (stored) {
90
+ try {
91
+ const auth = JSON.parse(stored);
92
+ return auth.accessToken;
93
+ } catch {
94
+ // Invalid JSON
95
+ }
96
+ }
97
+ }
98
+
99
+ // Check cookies (for SSR)
100
+ if (typeof document !== 'undefined') {
101
+ const cookies = document.cookie.split(';');
102
+ for (const cookie of cookies) {
103
+ const [name, value] = cookie.trim().split('=');
104
+ if (name === 'moonui_token') {
105
+ return decodeURIComponent(value);
106
+ }
107
+ }
108
+ }
109
+
110
+ return null;
111
+ }
112
+
113
+ // License Guard HOC
114
+ export function withLicenseGuard<P extends object>(
115
+ Component: React.ComponentType<P>,
116
+ options?: {
117
+ fallback?: React.ReactNode;
118
+ onUnauthorized?: () => void;
119
+ }
120
+ ) {
121
+ return function LicenseGuardedComponent(props: P) {
122
+ const { valid, loading, error } = useLicenseCheck();
123
+
124
+ useEffect(() => {
125
+ if (!loading && !valid && options?.onUnauthorized) {
126
+ options.onUnauthorized();
127
+ }
128
+ }, [loading, valid]);
129
+
130
+ if (loading) {
131
+ return (
132
+ <div className="flex items-center justify-center p-8">
133
+ <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"></div>
134
+ </div>
135
+ );
136
+ }
137
+
138
+ if (!valid) {
139
+ if (options?.fallback) {
140
+ return <>{options.fallback}</>;
141
+ }
142
+
143
+ return (
144
+ <div className="rounded-lg border border-red-200 bg-red-50 p-8 text-center">
145
+ <h3 className="text-lg font-semibold text-red-900 mb-2">
146
+ Pro License Required
147
+ </h3>
148
+ <p className="text-red-700 mb-4">
149
+ This component requires a MoonUI Pro license.
150
+ </p>
151
+ <a
152
+ href="https://moonui.dev/pricing"
153
+ className="inline-flex items-center justify-center rounded-md bg-red-600 px-4 py-2 text-sm font-medium text-white hover:bg-red-700"
154
+ >
155
+ Get Pro License
156
+ </a>
157
+ {error && (
158
+ <p className="text-xs text-red-600 mt-4">
159
+ Error: {error}
160
+ </p>
161
+ )}
162
+ </div>
163
+ );
164
+ }
165
+
166
+ return <Component {...props} />;
167
+ };
168
+ }
169
+
170
+ // Self-protecting component wrapper
171
+ export function ProComponent<P extends object>({
172
+ component: Component,
173
+ ...props
174
+ }: P & { component: React.ComponentType<P> }) {
175
+ const GuardedComponent = withLicenseGuard(Component);
176
+ return <GuardedComponent {...(props as P)} />;
177
+ }
@@ -0,0 +1,183 @@
1
+ /**
2
+ * License Validation System for MoonUI Pro
3
+ * This ensures that only licensed users can access pro components
4
+ */
5
+
6
+ interface LicenseData {
7
+ key: string;
8
+ email: string;
9
+ expiresAt?: string;
10
+ features?: string[];
11
+ }
12
+
13
+ class LicenseValidator {
14
+ private static instance: LicenseValidator;
15
+ private licenseData: LicenseData | null = null;
16
+ private validationCache: Map<string, boolean> = new Map();
17
+ private readonly VALIDATION_ENDPOINT = 'https://api.moonui.dev/v1/license/validate';
18
+
19
+ private constructor() {
20
+ // Initialize license from environment or stored data
21
+ this.initializeLicense();
22
+ }
23
+
24
+ static getInstance(): LicenseValidator {
25
+ if (!LicenseValidator.instance) {
26
+ LicenseValidator.instance = new LicenseValidator();
27
+ }
28
+ return LicenseValidator.instance;
29
+ }
30
+
31
+ private initializeLicense(): void {
32
+ // Check environment variables
33
+ const licenseKey = process.env.MOONUI_LICENSE_KEY;
34
+ const licenseEmail = process.env.MOONUI_LICENSE_EMAIL;
35
+
36
+ if (licenseKey && licenseEmail) {
37
+ this.licenseData = {
38
+ key: licenseKey,
39
+ email: licenseEmail
40
+ };
41
+ }
42
+
43
+ // Check for license file
44
+ try {
45
+ const fs = require('fs');
46
+ const path = require('path');
47
+ const licensePath = path.join(process.cwd(), '.moonui', 'license.json');
48
+
49
+ if (fs.existsSync(licensePath)) {
50
+ const fileData = fs.readFileSync(licensePath, 'utf-8');
51
+ this.licenseData = JSON.parse(fileData);
52
+ }
53
+ } catch (error) {
54
+ // Ignore file reading errors in browser environment
55
+ }
56
+ }
57
+
58
+ async validateLicense(): Promise<boolean> {
59
+ if (!this.licenseData) {
60
+ console.warn('[MoonUI Pro] No license found. Please run "moonui add <pro-component>" to activate your license.');
61
+ return false;
62
+ }
63
+
64
+ // Check cache first
65
+ const cacheKey = `${this.licenseData.key}-${this.licenseData.email}`;
66
+ if (this.validationCache.has(cacheKey)) {
67
+ return this.validationCache.get(cacheKey)!;
68
+ }
69
+
70
+ // Check expiration date if available
71
+ if (this.licenseData.expiresAt) {
72
+ const expiryDate = new Date(this.licenseData.expiresAt);
73
+ if (expiryDate < new Date()) {
74
+ console.error('[MoonUI Pro] License has expired. Please renew your subscription.');
75
+ this.validationCache.set(cacheKey, false);
76
+ return false;
77
+ }
78
+ }
79
+
80
+ // Validate with server (with fallback for offline mode)
81
+ try {
82
+ const response = await fetch(this.VALIDATION_ENDPOINT, {
83
+ method: 'POST',
84
+ headers: {
85
+ 'Content-Type': 'application/json',
86
+ },
87
+ body: JSON.stringify({
88
+ key: this.licenseData.key,
89
+ email: this.licenseData.email,
90
+ }),
91
+ });
92
+
93
+ if (response.ok) {
94
+ const result = await response.json();
95
+ const isValid = result.valid === true;
96
+ this.validationCache.set(cacheKey, isValid);
97
+
98
+ if (!isValid) {
99
+ console.error('[MoonUI Pro] Invalid license. Please check your license key and email.');
100
+ }
101
+
102
+ return isValid;
103
+ }
104
+ } catch (error) {
105
+ // Fallback for offline mode or network errors
106
+ // Allow usage if license format is valid
107
+ const isValidFormat = this.isValidLicenseFormat();
108
+ this.validationCache.set(cacheKey, isValidFormat);
109
+ return isValidFormat;
110
+ }
111
+
112
+ return false;
113
+ }
114
+
115
+ private isValidLicenseFormat(): boolean {
116
+ if (!this.licenseData) return false;
117
+
118
+ // Basic format validation
119
+ const { key, email } = this.licenseData;
120
+ return (
121
+ typeof key === 'string' &&
122
+ key.length >= 32 &&
123
+ typeof email === 'string' &&
124
+ email.includes('@')
125
+ );
126
+ }
127
+
128
+ hasFeature(feature: string): boolean {
129
+ if (!this.licenseData || !this.licenseData.features) {
130
+ return false;
131
+ }
132
+ return this.licenseData.features.includes(feature);
133
+ }
134
+
135
+ getLicenseInfo(): Readonly<LicenseData> | null {
136
+ return this.licenseData ? { ...this.licenseData } : null;
137
+ }
138
+ }
139
+
140
+ // Export singleton instance
141
+ export const licenseValidator = LicenseValidator.getInstance();
142
+
143
+ // Higher-order component for license protection
144
+ export function withLicenseProtection<P extends object>(
145
+ Component: React.ComponentType<P>,
146
+ componentName: string
147
+ ): React.ComponentType<P> {
148
+ return (props: P) => {
149
+ const [isValid, setIsValid] = React.useState<boolean | null>(null);
150
+
151
+ React.useEffect(() => {
152
+ licenseValidator.validateLicense().then(setIsValid);
153
+ }, []);
154
+
155
+ if (isValid === null) {
156
+ // Loading state
157
+ return null;
158
+ }
159
+
160
+ if (!isValid) {
161
+ if (process.env.NODE_ENV === 'development') {
162
+ console.error(
163
+ `[MoonUI Pro] Component "${componentName}" requires a valid Pro license.\n` +
164
+ 'Get your license at: https://moonui.dev/pricing'
165
+ );
166
+ }
167
+
168
+ // Return a placeholder in production
169
+ return (
170
+ <div className="rounded-lg border-2 border-dashed border-gray-300 p-4 text-center">
171
+ <p className="text-sm text-gray-500">
172
+ This component requires a MoonUI Pro license.
173
+ </p>
174
+ </div>
175
+ );
176
+ }
177
+
178
+ return <Component {...props} />;
179
+ };
180
+ }
181
+
182
+ // Export React for component usage
183
+ import * as React from 'react';
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Package Access Guard
3
+ * Prevents direct NPM package usage without CLI
4
+ */
5
+
6
+ const ALLOWED_CALLERS = [
7
+ '@moontra/moonui-cli',
8
+ 'moonui-cli',
9
+ '.moonui/temp', // CLI temp directory
10
+ ];
11
+
12
+ const DEV_MODE = process.env.NODE_ENV === 'development';
13
+
14
+ export function checkPackageAccess(): void {
15
+ // Skip in development mode for easier testing
16
+ if (DEV_MODE) {
17
+ return;
18
+ }
19
+
20
+ // Check if running in a browser (client-side)
21
+ if (typeof window !== 'undefined') {
22
+ // Client-side access is allowed after server-side validation
23
+ return;
24
+ }
25
+
26
+ // Server-side check
27
+ const stack = new Error().stack;
28
+
29
+ if (!stack) {
30
+ throw new Error(
31
+ 'MoonUI Pro: This package can only be used via @moontra/moonui-cli. ' +
32
+ 'Please install components using: npx @moontra/moonui-cli add <component>'
33
+ );
34
+ }
35
+
36
+ // Check if called from allowed sources
37
+ const isAllowed = ALLOWED_CALLERS.some(caller => stack.includes(caller));
38
+
39
+ // Also check for local file paths that indicate CLI usage
40
+ const isCliGenerated = stack.includes('/components/ui/') ||
41
+ stack.includes('/components/pro/');
42
+
43
+ if (!isAllowed && !isCliGenerated) {
44
+ console.error('Stack trace:', stack);
45
+ throw new Error(
46
+ 'MoonUI Pro: Unauthorized package access detected.\n' +
47
+ 'This package can only be used via @moontra/moonui-cli.\n' +
48
+ 'Install components using: npx @moontra/moonui-cli add <component>\n\n' +
49
+ 'If you have a Pro license, make sure to:\n' +
50
+ '1. Login with: npx @moontra/moonui-cli login\n' +
51
+ '2. Install components via CLI\n\n' +
52
+ 'Learn more at: https://moonui.dev/docs/installation'
53
+ );
54
+ }
55
+ }
56
+
57
+ // Auto-check on import
58
+ if (typeof window === 'undefined') {
59
+ checkPackageAccess();
60
+ }