Compare commits
	
		
			No commits in common. "napi-module" and "react-18" have entirely different histories.
		
	
	
		
			napi-modul
			...
			react-18
		
	
		
| @ -1,10 +1,10 @@ | |||||||
| { | { | ||||||
|   "name": "rage-fw-example-browser", |   "name": "rage-fw-example-cef", | ||||||
|   "version": "0.0.0", |   "version": "0.0.0", | ||||||
|   "type": "module", |   "type": "module", | ||||||
|   "author": "Entity Seven Group", |   "author": "Entity Seven Group", | ||||||
|   "license": "MIT", |   "license": "CC BY-ND", | ||||||
|   "description": "Browser side of rage-fw-example", |   "description": "CEF side of rage-fw example", | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "dev": "vite", |     "dev": "vite", | ||||||
|     "build": "tsc && vite build", |     "build": "tsc && vite build", | ||||||
| @ -12,12 +12,9 @@ | |||||||
|     "preview": "vite preview" |     "preview": "vite preview" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@entityseven/rage-fw-browser": "0.2.0", |  | ||||||
|     "@tailwindcss/vite": "^4.0.17", |  | ||||||
|     "react": "^18.2.0", |     "react": "^18.2.0", | ||||||
|     "react-dom": "^18.2.0", |     "react-dom": "^18.2.0", | ||||||
|     "tailwindcss": "^4.0.17", |     "rage-fw-cef": "latest" | ||||||
|     "zod": "^3.24.2" |  | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@types/react": "^18.2.66", |     "@types/react": "^18.2.66", | ||||||
|  | |||||||
| @ -1,30 +1,20 @@ | |||||||
| import React from 'react' | import { fw } from 'rage-fw-cef' | ||||||
| import LoginPage from './pages/Authorization/LoginPage' | import { useEffect, useState } from 'react' | ||||||
| import RegisterPage from './pages/Authorization/RegisterPage' |  | ||||||
| 
 |  | ||||||
| import './index.css' |  | ||||||
| 
 | 
 | ||||||
| function App() { | function App() { | ||||||
|     const [showLogin, setShowLogin] = React.useState(true) |     const [data, setData] = useState('') | ||||||
|  | 
 | ||||||
|  |     useEffect(() => { | ||||||
|  |         fw.event.register('customCefEvent', async ([test]) => { | ||||||
|  |             setData(p => p + ' ' + test) | ||||||
|  |             return 'from cef' | ||||||
|  |         }) | ||||||
|  |     }, []) | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|         <div> |         <div style={{ width: '100%', color: 'white', textAlign: 'center' }}> | ||||||
|             <div className="p-4 text-center"> |             <h1>Hello World!</h1> | ||||||
|                 <button |             <h2>{data}</h2> | ||||||
|                     onClick={() => setShowLogin(true)} |  | ||||||
|                     className={`mr-2 px-4 py-2 rounded ${showLogin ? 'bg-sky-600 text-white' : 'bg-gray-300'}`} |  | ||||||
|                 > |  | ||||||
|                     Show Login |  | ||||||
|                 </button> |  | ||||||
|                 <button |  | ||||||
|                     onClick={() => setShowLogin(false)} |  | ||||||
|                     className={`px-4 py-2 rounded ${!showLogin ? 'bg-sky-600 text-white' : 'bg-gray-300'}`} |  | ||||||
|                 > |  | ||||||
|                     Show Register |  | ||||||
|                 </button> |  | ||||||
|             </div> |  | ||||||
| 
 |  | ||||||
|             {showLogin ? <LoginPage /> : <RegisterPage />} |  | ||||||
|         </div> |         </div> | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,62 +0,0 @@ | |||||||
| import React from 'react' |  | ||||||
| 
 |  | ||||||
| interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> { |  | ||||||
|     isLoading?: boolean |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const Button: React.FC<ButtonProps> = ({ |  | ||||||
|     children, |  | ||||||
|     className, |  | ||||||
|     isLoading = false, |  | ||||||
|     disabled, |  | ||||||
|     ...props |  | ||||||
| }) => { |  | ||||||
|     const baseClasses = |  | ||||||
|         'w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white focus:outline-none focus:ring-2 focus:ring-offset-2' |  | ||||||
|     const activeClasses = 'bg-sky-600 hover:bg-sky-700 focus:ring-sky-500' |  | ||||||
|     const loadingClasses = 'bg-sky-400 cursor-not-allowed' |  | ||||||
|     const disabledClasses = 'bg-gray-400 cursor-not-allowed' |  | ||||||
| 
 |  | ||||||
|     const getButtonClasses = () => { |  | ||||||
|         if (isLoading) |  | ||||||
|             return `${baseClasses} ${loadingClasses} ${className || ''}` |  | ||||||
|         if (disabled) |  | ||||||
|             return `${baseClasses} ${disabledClasses} ${className || ''}` |  | ||||||
|         return `${baseClasses} ${activeClasses} ${className || ''}` |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return ( |  | ||||||
|         <button |  | ||||||
|             className={getButtonClasses()} |  | ||||||
|             disabled={disabled || isLoading} |  | ||||||
|             {...props} |  | ||||||
|         > |  | ||||||
|             {isLoading ? ( |  | ||||||
|                 <svg |  | ||||||
|                     className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" |  | ||||||
|                     xmlns="http://www.w3.org/2000/svg" |  | ||||||
|                     fill="none" |  | ||||||
|                     viewBox="0 0 24 24" |  | ||||||
|                 > |  | ||||||
|                     <circle |  | ||||||
|                         className="opacity-25" |  | ||||||
|                         cx="12" |  | ||||||
|                         cy="12" |  | ||||||
|                         r="10" |  | ||||||
|                         stroke="currentColor" |  | ||||||
|                         strokeWidth="4" |  | ||||||
|                     ></circle> |  | ||||||
|                     <path |  | ||||||
|                         className="opacity-75" |  | ||||||
|                         fill="currentColor" |  | ||||||
|                         d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" |  | ||||||
|                     ></path> |  | ||||||
|                 </svg> |  | ||||||
|             ) : ( |  | ||||||
|                 children |  | ||||||
|             )} |  | ||||||
|         </button> |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export default Button |  | ||||||
| @ -1,29 +0,0 @@ | |||||||
| import React from 'react' |  | ||||||
| 
 |  | ||||||
| interface CheckboxProps extends React.InputHTMLAttributes<HTMLInputElement> { |  | ||||||
|     label: string |  | ||||||
|     id: string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const Checkbox: React.FC<CheckboxProps> = ({ |  | ||||||
|     label, |  | ||||||
|     id, |  | ||||||
|     className, |  | ||||||
|     ...props |  | ||||||
| }) => { |  | ||||||
|     return ( |  | ||||||
|         <div className="flex items-center mb-4"> |  | ||||||
|             <input |  | ||||||
|                 id={id} |  | ||||||
|                 type="checkbox" |  | ||||||
|                 className={`h-4 w-4 rounded border-gray-300 text-sky-600 focus:ring-sky-500 ${className || ''}`} |  | ||||||
|                 {...props} |  | ||||||
|             /> |  | ||||||
|             <label htmlFor={id} className="ml-2 block text-sm text-gray-900"> |  | ||||||
|                 {label} |  | ||||||
|             </label> |  | ||||||
|         </div> |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export default Checkbox |  | ||||||
| @ -1,49 +0,0 @@ | |||||||
| import React from 'react' |  | ||||||
| 
 |  | ||||||
| interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> { |  | ||||||
|     label: string |  | ||||||
|     id: string |  | ||||||
|     error?: string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const Input: React.FC<InputProps> = ({ |  | ||||||
|     label, |  | ||||||
|     id, |  | ||||||
|     error, |  | ||||||
|     className, |  | ||||||
|     ...props |  | ||||||
| }) => { |  | ||||||
|     const baseClasses = |  | ||||||
|         'mt-1 block w-full px-3 py-2 bg-white border border-slate-300 rounded-md text-sm shadow-sm placeholder-slate-400 focus:outline-none focus:border-sky-500 focus:ring-1 focus:ring-sky-500 disabled:bg-slate-50 disabled:text-slate-500 disabled:border-slate-200 disabled:shadow-none' |  | ||||||
|     const errorClasses = |  | ||||||
|         'border-red-500 text-red-600 focus:border-red-500 focus:ring-red-500' |  | ||||||
| 
 |  | ||||||
|     return ( |  | ||||||
|         <div className="mb-4"> |  | ||||||
|             <label |  | ||||||
|                 htmlFor={id} |  | ||||||
|                 className="block text-sm font-medium text-slate-700" |  | ||||||
|             > |  | ||||||
|                 {label} |  | ||||||
|             </label> |  | ||||||
|             <input |  | ||||||
|                 id={id} |  | ||||||
|                 className={`${baseClasses} ${error ? errorClasses : ''} ${className || ''}`} |  | ||||||
|                 {...props} |  | ||||||
|                 aria-invalid={!!error} |  | ||||||
|                 aria-describedby={error ? `${id}-error` : undefined} |  | ||||||
|             /> |  | ||||||
|             {error && ( |  | ||||||
|                 <p |  | ||||||
|                     id={`${id}-error`} |  | ||||||
|                     className="mt-1 text-xs text-red-600" |  | ||||||
|                     role="alert" |  | ||||||
|                 > |  | ||||||
|                     {error} |  | ||||||
|                 </p> |  | ||||||
|             )} |  | ||||||
|         </div> |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export default Input |  | ||||||
| @ -1,139 +0,0 @@ | |||||||
| import React, { useState, useCallback } from 'react' |  | ||||||
| import { loginSchema, LoginFormData, FieldErrors } from '../validation' |  | ||||||
| import { ZodError } from 'zod' |  | ||||||
| 
 |  | ||||||
| interface UseLoginFormProps { |  | ||||||
|     onSubmitSuccess?: (data: LoginFormData) => void // Optional callback for successful submission
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export function useLoginForm({ onSubmitSuccess }: UseLoginFormProps = {}) { |  | ||||||
|     const [formData, setFormData] = useState<LoginFormData>({ |  | ||||||
|         identifier: '', // Can hold email or username
 |  | ||||||
|         password: '', |  | ||||||
|         saveLogin: false, |  | ||||||
|         savePassword: false, |  | ||||||
|     }) |  | ||||||
|     const [errors, setErrors] = useState<FieldErrors<LoginFormData>>({}) |  | ||||||
|     const [isLoading, setIsLoading] = useState(false) |  | ||||||
|     const [submitError, setSubmitError] = useState<string | null>(null) |  | ||||||
| 
 |  | ||||||
|     const handleChange = useCallback( |  | ||||||
|         (e: React.ChangeEvent<HTMLInputElement>) => { |  | ||||||
|             const { name, value, type, checked } = e.target |  | ||||||
|             setFormData(prev => ({ |  | ||||||
|                 ...prev, |  | ||||||
|                 [name]: type === 'checkbox' ? checked : value, |  | ||||||
|             })) |  | ||||||
|             // Clear error for the field being edited
 |  | ||||||
|             if (errors[name as keyof LoginFormData]) { |  | ||||||
|                 setErrors(prev => ({ ...prev, [name]: undefined })) |  | ||||||
|             } |  | ||||||
|             // Clear general submission error
 |  | ||||||
|             if (submitError) { |  | ||||||
|                 setSubmitError(null) |  | ||||||
|             } |  | ||||||
|         }, |  | ||||||
|         [errors, submitError], |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|     const validateForm = useCallback(() => { |  | ||||||
|         try { |  | ||||||
|             loginSchema.parse(formData) |  | ||||||
|             setErrors({}) |  | ||||||
|             return true |  | ||||||
|         } catch (error) { |  | ||||||
|             if (error instanceof ZodError) { |  | ||||||
|                 const fieldErrors: FieldErrors<LoginFormData> = {} |  | ||||||
|                 error.errors.forEach(err => { |  | ||||||
|                     if (err.path.length > 0) { |  | ||||||
|                         fieldErrors[err.path[0] as keyof LoginFormData] = |  | ||||||
|                             err.message |  | ||||||
|                     } |  | ||||||
|                 }) |  | ||||||
|                 setErrors(fieldErrors) |  | ||||||
|             } else { |  | ||||||
|                 console.error( |  | ||||||
|                     'An unexpected error occurred during validation:', |  | ||||||
|                     error, |  | ||||||
|                 ) |  | ||||||
|                 setSubmitError('An unexpected validation error occurred.') |  | ||||||
|             } |  | ||||||
|             return false |  | ||||||
|         } |  | ||||||
|     }, [formData]) |  | ||||||
| 
 |  | ||||||
|     const handleSubmit = useCallback( |  | ||||||
|         async (e: React.FormEvent<HTMLFormElement>) => { |  | ||||||
|             e.preventDefault() |  | ||||||
|             setSubmitError(null) |  | ||||||
| 
 |  | ||||||
|             if (!validateForm()) { |  | ||||||
|                 return |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             setIsLoading(true) |  | ||||||
|             console.log('Submitting login data:', formData) |  | ||||||
| 
 |  | ||||||
|             // --- TODO: Replace with actual API call to your RageMP server ---
 |  | ||||||
|             try { |  | ||||||
|                 // Example: Simulate API call
 |  | ||||||
|                 await new Promise(resolve => setTimeout(resolve, 1000)) |  | ||||||
| 
 |  | ||||||
|                 // Assuming API returns success:
 |  | ||||||
|                 console.log('Login successful!') |  | ||||||
|                 // Here you would typically receive a token or session info
 |  | ||||||
|                 // Handle 'saveLogin' and 'savePassword' (e.g., using localStorage/sessionStorage)
 |  | ||||||
|                 if (formData.saveLogin) { |  | ||||||
|                     localStorage.setItem('savedUsername', formData.identifier) // Example
 |  | ||||||
|                 } else { |  | ||||||
|                     localStorage.removeItem('savedUsername') // Example
 |  | ||||||
|                 } |  | ||||||
|                 // Password saving is generally discouraged for security reasons,
 |  | ||||||
|                 // but if required:
 |  | ||||||
|                 if (formData.savePassword) { |  | ||||||
|                     // Be VERY careful with storing passwords. Consider secure storage or tokens.
 |  | ||||||
|                     // localStorage.setItem('savedPassword', formData.password); // **Highly discouraged**
 |  | ||||||
|                     console.warn( |  | ||||||
|                         'Password saving enabled - ensure secure storage mechanism.', |  | ||||||
|                     ) |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 setErrors({}) // Clear errors on success
 |  | ||||||
|                 if (onSubmitSuccess) { |  | ||||||
|                     onSubmitSuccess(formData) // Call success callback
 |  | ||||||
|                 } |  | ||||||
|                 // You might redirect the user or update application state here
 |  | ||||||
| 
 |  | ||||||
|                 // --- Mock Error Handling (remove in real implementation) ---
 |  | ||||||
|                 // if (formData.identifier === 'wrong') {
 |  | ||||||
|                 //     throw new Error("Invalid credentials.");
 |  | ||||||
|                 // }
 |  | ||||||
|                 // --- End Mock Error Handling ---
 |  | ||||||
|             } catch (apiError: unknown) { |  | ||||||
|                 console.error('Login API error:', apiError) |  | ||||||
|                 setSubmitError('Login failed. Please check your credentials.') |  | ||||||
|             } finally { |  | ||||||
|                 setIsLoading(false) |  | ||||||
|             } |  | ||||||
|             // --- End API call section ---
 |  | ||||||
|         }, |  | ||||||
|         [formData, validateForm, onSubmitSuccess], |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|     // Effect to potentially load saved username on initial render (example)
 |  | ||||||
|     // useEffect(() => {
 |  | ||||||
|     //     const savedUser = localStorage.getItem('savedUsername');
 |  | ||||||
|     //     if (savedUser) {
 |  | ||||||
|     //         setFormData(prev => ({ ...prev, identifier: savedUser, saveLogin: true }));
 |  | ||||||
|     //     }
 |  | ||||||
|     // }, []);
 |  | ||||||
| 
 |  | ||||||
|     return { |  | ||||||
|         formData, |  | ||||||
|         errors, |  | ||||||
|         isLoading, |  | ||||||
|         submitError, |  | ||||||
|         handleChange, |  | ||||||
|         handleSubmit, |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,121 +0,0 @@ | |||||||
| import React, { useState, useCallback } from 'react' |  | ||||||
| import { registerSchema, RegisterFormData, FieldErrors } from '../validation' |  | ||||||
| import { ZodError } from 'zod' |  | ||||||
| 
 |  | ||||||
| interface UseRegisterFormProps { |  | ||||||
|     onSubmitSuccess?: (data: RegisterFormData) => void // Optional callback for successful submission
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export function useRegisterForm({ |  | ||||||
|     onSubmitSuccess, |  | ||||||
| }: UseRegisterFormProps = {}) { |  | ||||||
|     const [formData, setFormData] = useState<RegisterFormData>({ |  | ||||||
|         username: '', |  | ||||||
|         email: '', |  | ||||||
|         password: '', |  | ||||||
|         passwordRepeat: '', |  | ||||||
|         promocode: '', |  | ||||||
|     }) |  | ||||||
|     const [errors, setErrors] = useState<FieldErrors<RegisterFormData>>({}) |  | ||||||
|     const [isLoading, setIsLoading] = useState(false) |  | ||||||
|     const [submitError, setSubmitError] = useState<string | null>(null) // For general API errors
 |  | ||||||
| 
 |  | ||||||
|     const handleChange = useCallback( |  | ||||||
|         (e: React.ChangeEvent<HTMLInputElement>) => { |  | ||||||
|             const { name, value } = e.target |  | ||||||
|             setFormData(prev => ({ ...prev, [name]: value })) |  | ||||||
|             // Clear error for the field being edited
 |  | ||||||
|             if (errors[name as keyof RegisterFormData]) { |  | ||||||
|                 setErrors(prev => ({ ...prev, [name]: undefined })) |  | ||||||
|             } |  | ||||||
|             // Clear general submission error when user starts typing again
 |  | ||||||
|             if (submitError) { |  | ||||||
|                 setSubmitError(null) |  | ||||||
|             } |  | ||||||
|         }, |  | ||||||
|         [errors, submitError], |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|     const validateForm = useCallback(() => { |  | ||||||
|         try { |  | ||||||
|             registerSchema.parse(formData) |  | ||||||
|             setErrors({}) // Clear errors if validation passes
 |  | ||||||
|             return true |  | ||||||
|         } catch (error) { |  | ||||||
|             if (error instanceof ZodError) { |  | ||||||
|                 const fieldErrors: FieldErrors<RegisterFormData> = {} |  | ||||||
|                 error.errors.forEach(err => { |  | ||||||
|                     if (err.path.length > 0) { |  | ||||||
|                         fieldErrors[err.path[0] as keyof RegisterFormData] = |  | ||||||
|                             err.message |  | ||||||
|                     } |  | ||||||
|                 }) |  | ||||||
|                 setErrors(fieldErrors) |  | ||||||
|             } else { |  | ||||||
|                 console.error( |  | ||||||
|                     'An unexpected error occurred during validation:', |  | ||||||
|                     error, |  | ||||||
|                 ) |  | ||||||
|                 setSubmitError('An unexpected validation error occurred.') // Generic error for non-Zod issues
 |  | ||||||
|             } |  | ||||||
|             return false |  | ||||||
|         } |  | ||||||
|     }, [formData]) |  | ||||||
| 
 |  | ||||||
|     const handleSubmit = useCallback( |  | ||||||
|         async (e: React.FormEvent<HTMLFormElement>) => { |  | ||||||
|             e.preventDefault() |  | ||||||
|             setSubmitError(null) // Clear previous submit errors
 |  | ||||||
| 
 |  | ||||||
|             if (!validateForm()) { |  | ||||||
|                 return // Stop submission if validation fails
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             setIsLoading(true) |  | ||||||
|             console.log('Submitting registration data:', formData) |  | ||||||
| 
 |  | ||||||
|             // --- TODO: Replace with actual API call to your RageMP server ---
 |  | ||||||
|             try { |  | ||||||
|                 // Example: Simulate API call
 |  | ||||||
|                 await new Promise(resolve => setTimeout(resolve, 1500)) |  | ||||||
| 
 |  | ||||||
|                 // Assuming API returns success:
 |  | ||||||
|                 console.log('Registration successful!') |  | ||||||
|                 setFormData({ |  | ||||||
|                     username: '', |  | ||||||
|                     email: '', |  | ||||||
|                     password: '', |  | ||||||
|                     passwordRepeat: '', |  | ||||||
|                     promocode: '', |  | ||||||
|                 }) // Reset form
 |  | ||||||
|                 setErrors({}) |  | ||||||
|                 if (onSubmitSuccess) { |  | ||||||
|                     onSubmitSuccess(formData) // Call success callback if provided
 |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 // --- Mock Error Handling (remove in real implementation) ---
 |  | ||||||
|                 // if (formData.email.includes('fail')) {
 |  | ||||||
|                 //     throw new Error("Registration failed: Email already exists.");
 |  | ||||||
|                 // }
 |  | ||||||
|                 // --- End Mock Error Handling ---
 |  | ||||||
|             } catch (apiError: unknown) { |  | ||||||
|                 console.error('Registration API error:', apiError) |  | ||||||
|                 // Try to set a user-friendly error message
 |  | ||||||
|                 setSubmitError('Registration failed. Please try again.') |  | ||||||
|             } finally { |  | ||||||
|                 setIsLoading(false) |  | ||||||
|             } |  | ||||||
|             // --- End API call section ---
 |  | ||||||
|         }, |  | ||||||
|         [formData, validateForm, onSubmitSuccess], |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|     return { |  | ||||||
|         formData, |  | ||||||
|         errors, |  | ||||||
|         isLoading, |  | ||||||
|         submitError, |  | ||||||
|         handleChange, |  | ||||||
|         handleSubmit, |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,5 +1,3 @@ | |||||||
| @import "tailwindcss"; |  | ||||||
| 
 |  | ||||||
| body { | body { | ||||||
|   margin: 0; |   margin: 0; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,126 +0,0 @@ | |||||||
| import React from 'react' |  | ||||||
| import Input from '../../components/ui/Input' |  | ||||||
| import Checkbox from '../../components/ui/Checkbox' |  | ||||||
| import Button from '../../components/ui/Button' |  | ||||||
| import { useLoginForm } from '../../hooks/useLoginForm' |  | ||||||
| import { LoginFormData } from '../../validation' |  | ||||||
| 
 |  | ||||||
| const LoginPage: React.FC = () => { |  | ||||||
|     const handleLoginSuccess = (data: LoginFormData) => { |  | ||||||
|         console.log('Login success callback triggered:', data) |  | ||||||
|         // Redirect user or update app state
 |  | ||||||
|         alert(`Login successful for ${data.identifier}!`) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const { |  | ||||||
|         formData, |  | ||||||
|         errors, |  | ||||||
|         isLoading, |  | ||||||
|         submitError, |  | ||||||
|         handleChange, |  | ||||||
|         handleSubmit, |  | ||||||
|     } = useLoginForm({ onSubmitSuccess: handleLoginSuccess }) |  | ||||||
| 
 |  | ||||||
|     return ( |  | ||||||
|         <div className="min-h-screen bg-gray-100 flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8"> |  | ||||||
|             <div className="max-w-md w-full space-y-8 bg-white p-10 rounded-lg shadow-md"> |  | ||||||
|                 <div> |  | ||||||
|                     <h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900"> |  | ||||||
|                         Sign in to your account |  | ||||||
|                     </h2> |  | ||||||
|                     {/* Optional: Link to registration page */} |  | ||||||
|                     {/* <p className="mt-2 text-center text-sm text-gray-600"> |  | ||||||
|                         Or{' '} |  | ||||||
|                         <a href="/register" className="font-medium text-sky-600 hover:text-sky-500"> |  | ||||||
|                             create a new account |  | ||||||
|                         </a> |  | ||||||
|                     </p> */} |  | ||||||
|                 </div> |  | ||||||
|                 <form |  | ||||||
|                     className="mt-8 space-y-6" |  | ||||||
|                     onSubmit={handleSubmit} |  | ||||||
|                     noValidate |  | ||||||
|                 > |  | ||||||
|                     {/* Display general submission errors */} |  | ||||||
|                     {submitError && ( |  | ||||||
|                         <div |  | ||||||
|                             className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" |  | ||||||
|                             role="alert" |  | ||||||
|                         > |  | ||||||
|                             <strong className="font-bold">Error: </strong> |  | ||||||
|                             <span className="block sm:inline"> |  | ||||||
|                                 {submitError} |  | ||||||
|                             </span> |  | ||||||
|                         </div> |  | ||||||
|                     )} |  | ||||||
| 
 |  | ||||||
|                     <div className="rounded-md shadow-sm -space-y-px"> |  | ||||||
|                         <Input |  | ||||||
|                             label="Email or Username" |  | ||||||
|                             id="identifier" |  | ||||||
|                             name="identifier" |  | ||||||
|                             type="text" // Use text to allow both email and username
 |  | ||||||
|                             autoComplete="username" // Browsers often use 'username' for this field
 |  | ||||||
|                             required |  | ||||||
|                             placeholder="Email address or Username" |  | ||||||
|                             value={formData.identifier} |  | ||||||
|                             onChange={handleChange} |  | ||||||
|                             error={errors.identifier} |  | ||||||
|                         /> |  | ||||||
|                         <Input |  | ||||||
|                             label="Password" |  | ||||||
|                             id="password" |  | ||||||
|                             name="password" |  | ||||||
|                             type="password" |  | ||||||
|                             autoComplete="current-password" |  | ||||||
|                             required |  | ||||||
|                             placeholder="Password" |  | ||||||
|                             value={formData.password} |  | ||||||
|                             onChange={handleChange} |  | ||||||
|                             error={errors.password} |  | ||||||
|                         /> |  | ||||||
|                     </div> |  | ||||||
| 
 |  | ||||||
|                     <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between"> |  | ||||||
|                         <div className="flex flex-col"> |  | ||||||
|                             <Checkbox |  | ||||||
|                                 label="Remember username" |  | ||||||
|                                 id="saveLogin" |  | ||||||
|                                 name="saveLogin" |  | ||||||
|                                 checked={formData.saveLogin} |  | ||||||
|                                 onChange={handleChange} |  | ||||||
|                             /> |  | ||||||
|                             <Checkbox |  | ||||||
|                                 label="Remember password" |  | ||||||
|                                 id="savePassword" |  | ||||||
|                                 name="savePassword" |  | ||||||
|                                 checked={formData.savePassword} |  | ||||||
|                                 onChange={handleChange} |  | ||||||
|                                 // Add a warning or disable based on security policy
 |  | ||||||
|                             /> |  | ||||||
|                         </div> |  | ||||||
| 
 |  | ||||||
|                         {/* Optional: Forgot Password Link */} |  | ||||||
|                         {/* <div className="text-sm mt-2 sm:mt-0"> |  | ||||||
|                             <a href="/forgot-password" className="font-medium text-sky-600 hover:text-sky-500"> |  | ||||||
|                                 Forgot your password? |  | ||||||
|                             </a> |  | ||||||
|                         </div> */} |  | ||||||
|                     </div> |  | ||||||
| 
 |  | ||||||
|                     <div> |  | ||||||
|                         <Button |  | ||||||
|                             type="submit" |  | ||||||
|                             isLoading={isLoading} |  | ||||||
|                             disabled={isLoading} |  | ||||||
|                         > |  | ||||||
|                             {isLoading ? 'Signing In...' : 'Sign In'} |  | ||||||
|                         </Button> |  | ||||||
|                     </div> |  | ||||||
|                 </form> |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export default LoginPage |  | ||||||
| @ -1,134 +0,0 @@ | |||||||
| import React from 'react' |  | ||||||
| import Input from '../../components/ui/Input' |  | ||||||
| import Button from '../../components/ui/Button' |  | ||||||
| import { useRegisterForm } from '../../hooks/useRegisterForm' |  | ||||||
| import { RegisterFormData } from '../../validation' |  | ||||||
| 
 |  | ||||||
| const RegisterPage: React.FC = () => { |  | ||||||
|     const handleRegistrationSuccess = (data: RegisterFormData) => { |  | ||||||
|         console.log('Registration success callback triggered:', data) |  | ||||||
|         // Maybe show a success message or redirect
 |  | ||||||
|         alert(`Registration successful for ${data.username}!`) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const { |  | ||||||
|         formData, |  | ||||||
|         errors, |  | ||||||
|         isLoading, |  | ||||||
|         submitError, |  | ||||||
|         handleChange, |  | ||||||
|         handleSubmit, |  | ||||||
|     } = useRegisterForm({ onSubmitSuccess: handleRegistrationSuccess }) |  | ||||||
| 
 |  | ||||||
|     return ( |  | ||||||
|         <div className="min-h-screen bg-gray-100 flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8"> |  | ||||||
|             <div className="max-w-md w-full space-y-8 bg-white p-10 rounded-lg shadow-md"> |  | ||||||
|                 <div> |  | ||||||
|                     <h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900"> |  | ||||||
|                         Create your account |  | ||||||
|                     </h2> |  | ||||||
|                     {/* Optional: Link to login page */} |  | ||||||
|                     {/* <p className="mt-2 text-center text-sm text-gray-600"> |  | ||||||
|                         Or{' '} |  | ||||||
|                         <a href="/login" className="font-medium text-sky-600 hover:text-sky-500"> |  | ||||||
|                             sign in to your existing account |  | ||||||
|                         </a> |  | ||||||
|                     </p> */} |  | ||||||
|                 </div> |  | ||||||
|                 <form |  | ||||||
|                     className="mt-8 space-y-6" |  | ||||||
|                     onSubmit={handleSubmit} |  | ||||||
|                     noValidate |  | ||||||
|                 > |  | ||||||
|                     {/* Display general submission errors */} |  | ||||||
|                     {submitError && ( |  | ||||||
|                         <div |  | ||||||
|                             className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" |  | ||||||
|                             role="alert" |  | ||||||
|                         > |  | ||||||
|                             <strong className="font-bold">Error: </strong> |  | ||||||
|                             <span className="block sm:inline"> |  | ||||||
|                                 {submitError} |  | ||||||
|                             </span> |  | ||||||
|                         </div> |  | ||||||
|                     )} |  | ||||||
| 
 |  | ||||||
|                     {/* Use hidden input for honeypot or CSRF token if needed */} |  | ||||||
|                     {/* <input type="hidden" name="remember" defaultValue="true" /> */} |  | ||||||
|                     <div className="rounded-md shadow-sm -space-y-px"> |  | ||||||
|                         <Input |  | ||||||
|                             label="Username" |  | ||||||
|                             id="username" |  | ||||||
|                             name="username" |  | ||||||
|                             type="text" |  | ||||||
|                             autoComplete="username" |  | ||||||
|                             required |  | ||||||
|                             placeholder="Choose a username" |  | ||||||
|                             value={formData.username} |  | ||||||
|                             onChange={handleChange} |  | ||||||
|                             error={errors.username} |  | ||||||
|                         /> |  | ||||||
|                         <Input |  | ||||||
|                             label="Email address" |  | ||||||
|                             id="email" |  | ||||||
|                             name="email" |  | ||||||
|                             type="email" |  | ||||||
|                             autoComplete="email" |  | ||||||
|                             required |  | ||||||
|                             placeholder="your@email.com" |  | ||||||
|                             value={formData.email} |  | ||||||
|                             onChange={handleChange} |  | ||||||
|                             error={errors.email} |  | ||||||
|                         /> |  | ||||||
|                         <Input |  | ||||||
|                             label="Password" |  | ||||||
|                             id="password" |  | ||||||
|                             name="password" |  | ||||||
|                             type="password" |  | ||||||
|                             autoComplete="new-password" |  | ||||||
|                             required |  | ||||||
|                             placeholder="Create a password" |  | ||||||
|                             value={formData.password} |  | ||||||
|                             onChange={handleChange} |  | ||||||
|                             error={errors.password} |  | ||||||
|                         /> |  | ||||||
|                         <Input |  | ||||||
|                             label="Repeat Password" |  | ||||||
|                             id="passwordRepeat" |  | ||||||
|                             name="passwordRepeat" |  | ||||||
|                             type="password" |  | ||||||
|                             autoComplete="new-password" |  | ||||||
|                             required |  | ||||||
|                             placeholder="Confirm your password" |  | ||||||
|                             value={formData.passwordRepeat} |  | ||||||
|                             onChange={handleChange} |  | ||||||
|                             error={errors.passwordRepeat} |  | ||||||
|                         /> |  | ||||||
|                         <Input |  | ||||||
|                             label="Promocode (Optional)" |  | ||||||
|                             id="promocode" |  | ||||||
|                             name="promocode" |  | ||||||
|                             type="text" |  | ||||||
|                             placeholder="Enter promocode if you have one" |  | ||||||
|                             value={formData.promocode || ''} |  | ||||||
|                             onChange={handleChange} |  | ||||||
|                             error={errors.promocode} |  | ||||||
|                         /> |  | ||||||
|                     </div> |  | ||||||
| 
 |  | ||||||
|                     <div> |  | ||||||
|                         <Button |  | ||||||
|                             type="submit" |  | ||||||
|                             isLoading={isLoading} |  | ||||||
|                             disabled={isLoading} |  | ||||||
|                         > |  | ||||||
|                             {isLoading ? 'Registering...' : 'Register'} |  | ||||||
|                         </Button> |  | ||||||
|                     </div> |  | ||||||
|                 </form> |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export default RegisterPage |  | ||||||
| @ -1,6 +0,0 @@ | |||||||
| export * from './loginSchema.ts' |  | ||||||
| export * from './registerSchema.ts' |  | ||||||
| 
 |  | ||||||
| export type FieldErrors<T> = { |  | ||||||
|     [K in keyof T]?: string | undefined |  | ||||||
| } |  | ||||||
| @ -1,10 +0,0 @@ | |||||||
| import { z } from 'zod' |  | ||||||
| 
 |  | ||||||
| export const loginSchema = z.object({ |  | ||||||
|     identifier: z.string().min(1, { message: 'Email or Username is required' }), // Can be email or username
 |  | ||||||
|     password: z.string().min(1, { message: 'Password is required' }), |  | ||||||
|     saveLogin: z.boolean().optional().default(false), |  | ||||||
|     savePassword: z.boolean().optional().default(false), |  | ||||||
| }) |  | ||||||
| 
 |  | ||||||
| export type LoginFormData = z.infer<typeof loginSchema> |  | ||||||
| @ -1,28 +0,0 @@ | |||||||
| import { z } from 'zod' |  | ||||||
| 
 |  | ||||||
| // --- Registration Schema ---
 |  | ||||||
| export const registerSchema = z |  | ||||||
|     .object({ |  | ||||||
|         username: z |  | ||||||
|             .string() |  | ||||||
|             .min(3, { message: 'Username must be at least 3 characters long' }) |  | ||||||
|             .max(20, { message: 'Username cannot exceed 20 characters' }) |  | ||||||
|             .regex(/^[a-zA-Z0-9_]+$/, { |  | ||||||
|                 message: |  | ||||||
|                     'Username can only contain letters, numbers, and underscores', |  | ||||||
|             }), |  | ||||||
|         email: z.string().email({ message: 'Invalid email address' }), |  | ||||||
|         password: z |  | ||||||
|             .string() |  | ||||||
|             .min(8, { message: 'Password must be at least 8 characters long' }), |  | ||||||
|         // Optional: Add more complexity requirements if needed
 |  | ||||||
|         // .regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/, { message: "Password must contain uppercase, lowercase, and number" })
 |  | ||||||
|         passwordRepeat: z.string(), |  | ||||||
|         promocode: z.string().optional(), // Promocode is optional
 |  | ||||||
|     }) |  | ||||||
|     .refine(data => data.password === data.passwordRepeat, { |  | ||||||
|         message: "Passwords don't match", |  | ||||||
|         path: ['passwordRepeat'], // Set error path to passwordRepeat field
 |  | ||||||
|     }) |  | ||||||
| 
 |  | ||||||
| export type RegisterFormData = z.infer<typeof registerSchema> |  | ||||||
| @ -1,12 +1,11 @@ | |||||||
| import { defineConfig } from 'vite' | import { defineConfig } from 'vite' | ||||||
| import react from '@vitejs/plugin-react' | import react from '@vitejs/plugin-react' | ||||||
| import tailwindcss from '@tailwindcss/vite' |  | ||||||
| 
 | 
 | ||||||
| // https://vitejs.dev/config/
 | // https://vitejs.dev/config/
 | ||||||
| export default defineConfig({ | export default defineConfig({ | ||||||
|     plugins: [react(), tailwindcss()], |   plugins: [react()], | ||||||
|     build: { |   build: { | ||||||
|         outDir: '../../server/client_packages/cef', |     outDir: '../../server/client_packages/cef', | ||||||
|         emptyOutDir: true, |     emptyOutDir: true | ||||||
|     }, |   } | ||||||
| }) | }) | ||||||
|  | |||||||
| @ -8,6 +8,6 @@ | |||||||
|     "build": "esbuild src/index.ts --bundle --platform=node --outfile=../../server/client_packages/index.js --format=esm" |     "build": "esbuild src/index.ts --bundle --platform=node --outfile=../../server/client_packages/index.js --format=esm" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@entityseven/rage-fw-client": "latest" |     "rage-fw-client": "latest" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,3 +1,14 @@ | |||||||
| import { fw } from '@entityseven/rage-fw-client' | import { fw } from 'rage-fw-client' | ||||||
| 
 | 
 | ||||||
| fw.player.browser = mp.browsers.new('https://localhost:5173') | fw.player.browser = mp.browsers.new('package://cef/index.html') | ||||||
|  | 
 | ||||||
|  | fw.event.register('cefReady', async () => { | ||||||
|  |     fw.system.log.info('cefReady') | ||||||
|  | 
 | ||||||
|  |     const responseCef = await fw.player.triggerBrowser('customCefEvent', [ | ||||||
|  |         'from client', | ||||||
|  |     ]) | ||||||
|  |     fw.system.log.info(responseCef) | ||||||
|  | 
 | ||||||
|  |     await fw.player.triggerServer('customServerEvent', ['from client']) | ||||||
|  | }) | ||||||
|  | |||||||
| @ -5,9 +5,9 @@ | |||||||
|   "license": "CC BY-ND", |   "license": "CC BY-ND", | ||||||
|   "description": "Server side of rage-fw example", |   "description": "Server side of rage-fw example", | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "build": "esbuild src/index.ts --bundle --platform=node --target=node14.10 --external:*.node --format=cjs --outfile=../../server/packages/server/index.js" |     "build": "esbuild src/index.ts --bundle --platform=node --target=node10.4 --outfile=../../server/packages/server/index.js" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@entityseven/rage-fw-server": "latest" |     "rage-fw-server": "latest" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,3 +1,18 @@ | |||||||
| import { checkLicense, doSensitiveOperation } from './x86_64-pc-windows-msvc' | import { fw } from 'rage-fw-server' | ||||||
| 
 | 
 | ||||||
| console.log(doSensitiveOperation('test')) | fw.event.register('playerJoin', async ([player]) => { | ||||||
|  |     fw.system.log.info(`Connected: ${player.socialClub}`) | ||||||
|  | }) | ||||||
|  | 
 | ||||||
|  | fw.event.register('customServerEvent', async ([player, msg]) => { | ||||||
|  |     fw.system.log.info(player.socialClub + ' ' + msg) | ||||||
|  | 
 | ||||||
|  |     const resFromCef = await fw.player.triggerBrowser( | ||||||
|  |         player, | ||||||
|  |         'customCefEvent', | ||||||
|  |         ['from server'], | ||||||
|  |     ) | ||||||
|  |     fw.system.log.info(player.socialClub + ' ' + resFromCef) | ||||||
|  | 
 | ||||||
|  |     return 'from server' | ||||||
|  | }) | ||||||
|  | |||||||
										
											Binary file not shown.
										
									
								
							| @ -1,31 +0,0 @@ | |||||||
| /* tslint:disable */ |  | ||||||
| /* eslint-disable */ |  | ||||||
| 
 |  | ||||||
| /* auto-generated by NAPI-RS */ |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * # checkLicense |  | ||||||
|  * Accepts a 16-character license key string. |  | ||||||
|  * Gets the server's local IPv4 address, prints it to the console, |  | ||||||
|  * and validates the key. If valid, unlocks other functions. |  | ||||||
|  * |  | ||||||
|  * Returns `true` if the license key is valid, `false` otherwise. |  | ||||||
|  */ |  | ||||||
| export declare function checkLicense(licenseKey: string): boolean |  | ||||||
| /** |  | ||||||
|  * # doSensitiveOperation |  | ||||||
|  * An example function that should only run if the license is validated. |  | ||||||
|  * Takes a name as input and returns a greeting string. |  | ||||||
|  */ |  | ||||||
| export declare function doSensitiveOperation(name: string): string |  | ||||||
| /** |  | ||||||
|  * # getSecretData |  | ||||||
|  * Another example function that should only run if the license is validated. |  | ||||||
|  * Returns a dummy secret number. |  | ||||||
|  */ |  | ||||||
| export declare function getSecretData(): number |  | ||||||
| /** |  | ||||||
|  * # isLicensed (Optional utility) |  | ||||||
|  * Allows JavaScript to check the current license status without triggering exit. |  | ||||||
|  */ |  | ||||||
| export declare function isLicensed(): boolean |  | ||||||
| @ -1,318 +0,0 @@ | |||||||
| /* tslint:disable */ |  | ||||||
| /* eslint-disable */ |  | ||||||
| /* prettier-ignore */ |  | ||||||
| 
 |  | ||||||
| /* auto-generated by NAPI-RS */ |  | ||||||
| 
 |  | ||||||
| const { existsSync, readFileSync } = require('fs') |  | ||||||
| const { join } = require('path') |  | ||||||
| 
 |  | ||||||
| const { platform, arch } = process |  | ||||||
| 
 |  | ||||||
| let nativeBinding = null |  | ||||||
| let localFileExisted = false |  | ||||||
| let loadError = null |  | ||||||
| 
 |  | ||||||
| function isMusl() { |  | ||||||
|   // For Node 10
 |  | ||||||
|   if (!process.report || typeof process.report.getReport !== 'function') { |  | ||||||
|     try { |  | ||||||
|       const lddPath = require('child_process').execSync('which ldd').toString().trim() |  | ||||||
|       return readFileSync(lddPath, 'utf8').includes('musl') |  | ||||||
|     } catch (e) { |  | ||||||
|       return true |  | ||||||
|     } |  | ||||||
|   } else { |  | ||||||
|     const { glibcVersionRuntime } = process.report.getReport().header |  | ||||||
|     return !glibcVersionRuntime |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| switch (platform) { |  | ||||||
|   case 'android': |  | ||||||
|     switch (arch) { |  | ||||||
|       case 'arm64': |  | ||||||
|         localFileExisted = existsSync(join(__dirname, 'gta5.android-arm64.node')) |  | ||||||
|         try { |  | ||||||
|           if (localFileExisted) { |  | ||||||
|             nativeBinding = require('./gta5.android-arm64.node') |  | ||||||
|           } else { |  | ||||||
|             nativeBinding = require('gta5-android-arm64') |  | ||||||
|           } |  | ||||||
|         } catch (e) { |  | ||||||
|           loadError = e |  | ||||||
|         } |  | ||||||
|         break |  | ||||||
|       case 'arm': |  | ||||||
|         localFileExisted = existsSync(join(__dirname, 'gta5.android-arm-eabi.node')) |  | ||||||
|         try { |  | ||||||
|           if (localFileExisted) { |  | ||||||
|             nativeBinding = require('./gta5.android-arm-eabi.node') |  | ||||||
|           } else { |  | ||||||
|             nativeBinding = require('gta5-android-arm-eabi') |  | ||||||
|           } |  | ||||||
|         } catch (e) { |  | ||||||
|           loadError = e |  | ||||||
|         } |  | ||||||
|         break |  | ||||||
|       default: |  | ||||||
|         throw new Error(`Unsupported architecture on Android ${arch}`) |  | ||||||
|     } |  | ||||||
|     break |  | ||||||
|   case 'win32': |  | ||||||
|     switch (arch) { |  | ||||||
|       case 'x64': |  | ||||||
|         localFileExisted = existsSync( |  | ||||||
|           join(__dirname, 'gta5.win32-x64-msvc.node') |  | ||||||
|         ) |  | ||||||
|         try { |  | ||||||
|           if (localFileExisted) { |  | ||||||
|             nativeBinding = require('./gta5.win32-x64-msvc.node') |  | ||||||
|           } else { |  | ||||||
|             nativeBinding = require('gta5-win32-x64-msvc') |  | ||||||
|           } |  | ||||||
|         } catch (e) { |  | ||||||
|           loadError = e |  | ||||||
|         } |  | ||||||
|         break |  | ||||||
|       case 'ia32': |  | ||||||
|         localFileExisted = existsSync( |  | ||||||
|           join(__dirname, 'gta5.win32-ia32-msvc.node') |  | ||||||
|         ) |  | ||||||
|         try { |  | ||||||
|           if (localFileExisted) { |  | ||||||
|             nativeBinding = require('./gta5.win32-ia32-msvc.node') |  | ||||||
|           } else { |  | ||||||
|             nativeBinding = require('gta5-win32-ia32-msvc') |  | ||||||
|           } |  | ||||||
|         } catch (e) { |  | ||||||
|           loadError = e |  | ||||||
|         } |  | ||||||
|         break |  | ||||||
|       case 'arm64': |  | ||||||
|         localFileExisted = existsSync( |  | ||||||
|           join(__dirname, 'gta5.win32-arm64-msvc.node') |  | ||||||
|         ) |  | ||||||
|         try { |  | ||||||
|           if (localFileExisted) { |  | ||||||
|             nativeBinding = require('./gta5.win32-arm64-msvc.node') |  | ||||||
|           } else { |  | ||||||
|             nativeBinding = require('gta5-win32-arm64-msvc') |  | ||||||
|           } |  | ||||||
|         } catch (e) { |  | ||||||
|           loadError = e |  | ||||||
|         } |  | ||||||
|         break |  | ||||||
|       default: |  | ||||||
|         throw new Error(`Unsupported architecture on Windows: ${arch}`) |  | ||||||
|     } |  | ||||||
|     break |  | ||||||
|   case 'darwin': |  | ||||||
|     localFileExisted = existsSync(join(__dirname, 'gta5.darwin-universal.node')) |  | ||||||
|     try { |  | ||||||
|       if (localFileExisted) { |  | ||||||
|         nativeBinding = require('./gta5.darwin-universal.node') |  | ||||||
|       } else { |  | ||||||
|         nativeBinding = require('gta5-darwin-universal') |  | ||||||
|       } |  | ||||||
|       break |  | ||||||
|     } catch {} |  | ||||||
|     switch (arch) { |  | ||||||
|       case 'x64': |  | ||||||
|         localFileExisted = existsSync(join(__dirname, 'gta5.darwin-x64.node')) |  | ||||||
|         try { |  | ||||||
|           if (localFileExisted) { |  | ||||||
|             nativeBinding = require('./gta5.darwin-x64.node') |  | ||||||
|           } else { |  | ||||||
|             nativeBinding = require('gta5-darwin-x64') |  | ||||||
|           } |  | ||||||
|         } catch (e) { |  | ||||||
|           loadError = e |  | ||||||
|         } |  | ||||||
|         break |  | ||||||
|       case 'arm64': |  | ||||||
|         localFileExisted = existsSync( |  | ||||||
|           join(__dirname, 'gta5.darwin-arm64.node') |  | ||||||
|         ) |  | ||||||
|         try { |  | ||||||
|           if (localFileExisted) { |  | ||||||
|             nativeBinding = require('./gta5.darwin-arm64.node') |  | ||||||
|           } else { |  | ||||||
|             nativeBinding = require('gta5-darwin-arm64') |  | ||||||
|           } |  | ||||||
|         } catch (e) { |  | ||||||
|           loadError = e |  | ||||||
|         } |  | ||||||
|         break |  | ||||||
|       default: |  | ||||||
|         throw new Error(`Unsupported architecture on macOS: ${arch}`) |  | ||||||
|     } |  | ||||||
|     break |  | ||||||
|   case 'freebsd': |  | ||||||
|     if (arch !== 'x64') { |  | ||||||
|       throw new Error(`Unsupported architecture on FreeBSD: ${arch}`) |  | ||||||
|     } |  | ||||||
|     localFileExisted = existsSync(join(__dirname, 'gta5.freebsd-x64.node')) |  | ||||||
|     try { |  | ||||||
|       if (localFileExisted) { |  | ||||||
|         nativeBinding = require('./gta5.freebsd-x64.node') |  | ||||||
|       } else { |  | ||||||
|         nativeBinding = require('gta5-freebsd-x64') |  | ||||||
|       } |  | ||||||
|     } catch (e) { |  | ||||||
|       loadError = e |  | ||||||
|     } |  | ||||||
|     break |  | ||||||
|   case 'linux': |  | ||||||
|     switch (arch) { |  | ||||||
|       case 'x64': |  | ||||||
|         if (isMusl()) { |  | ||||||
|           localFileExisted = existsSync( |  | ||||||
|             join(__dirname, 'gta5.linux-x64-musl.node') |  | ||||||
|           ) |  | ||||||
|           try { |  | ||||||
|             if (localFileExisted) { |  | ||||||
|               nativeBinding = require('./gta5.linux-x64-musl.node') |  | ||||||
|             } else { |  | ||||||
|               nativeBinding = require('gta5-linux-x64-musl') |  | ||||||
|             } |  | ||||||
|           } catch (e) { |  | ||||||
|             loadError = e |  | ||||||
|           } |  | ||||||
|         } else { |  | ||||||
|           localFileExisted = existsSync( |  | ||||||
|             join(__dirname, 'gta5.linux-x64-gnu.node') |  | ||||||
|           ) |  | ||||||
|           try { |  | ||||||
|             if (localFileExisted) { |  | ||||||
|               nativeBinding = require('./gta5.linux-x64-gnu.node') |  | ||||||
|             } else { |  | ||||||
|               nativeBinding = require('gta5-linux-x64-gnu') |  | ||||||
|             } |  | ||||||
|           } catch (e) { |  | ||||||
|             loadError = e |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|         break |  | ||||||
|       case 'arm64': |  | ||||||
|         if (isMusl()) { |  | ||||||
|           localFileExisted = existsSync( |  | ||||||
|             join(__dirname, 'gta5.linux-arm64-musl.node') |  | ||||||
|           ) |  | ||||||
|           try { |  | ||||||
|             if (localFileExisted) { |  | ||||||
|               nativeBinding = require('./gta5.linux-arm64-musl.node') |  | ||||||
|             } else { |  | ||||||
|               nativeBinding = require('gta5-linux-arm64-musl') |  | ||||||
|             } |  | ||||||
|           } catch (e) { |  | ||||||
|             loadError = e |  | ||||||
|           } |  | ||||||
|         } else { |  | ||||||
|           localFileExisted = existsSync( |  | ||||||
|             join(__dirname, 'gta5.linux-arm64-gnu.node') |  | ||||||
|           ) |  | ||||||
|           try { |  | ||||||
|             if (localFileExisted) { |  | ||||||
|               nativeBinding = require('./gta5.linux-arm64-gnu.node') |  | ||||||
|             } else { |  | ||||||
|               nativeBinding = require('gta5-linux-arm64-gnu') |  | ||||||
|             } |  | ||||||
|           } catch (e) { |  | ||||||
|             loadError = e |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|         break |  | ||||||
|       case 'arm': |  | ||||||
|         if (isMusl()) { |  | ||||||
|           localFileExisted = existsSync( |  | ||||||
|             join(__dirname, 'gta5.linux-arm-musleabihf.node') |  | ||||||
|           ) |  | ||||||
|           try { |  | ||||||
|             if (localFileExisted) { |  | ||||||
|               nativeBinding = require('./gta5.linux-arm-musleabihf.node') |  | ||||||
|             } else { |  | ||||||
|               nativeBinding = require('gta5-linux-arm-musleabihf') |  | ||||||
|             } |  | ||||||
|           } catch (e) { |  | ||||||
|             loadError = e |  | ||||||
|           } |  | ||||||
|         } else { |  | ||||||
|           localFileExisted = existsSync( |  | ||||||
|             join(__dirname, 'gta5.linux-arm-gnueabihf.node') |  | ||||||
|           ) |  | ||||||
|           try { |  | ||||||
|             if (localFileExisted) { |  | ||||||
|               nativeBinding = require('./gta5.linux-arm-gnueabihf.node') |  | ||||||
|             } else { |  | ||||||
|               nativeBinding = require('gta5-linux-arm-gnueabihf') |  | ||||||
|             } |  | ||||||
|           } catch (e) { |  | ||||||
|             loadError = e |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|         break |  | ||||||
|       case 'riscv64': |  | ||||||
|         if (isMusl()) { |  | ||||||
|           localFileExisted = existsSync( |  | ||||||
|             join(__dirname, 'gta5.linux-riscv64-musl.node') |  | ||||||
|           ) |  | ||||||
|           try { |  | ||||||
|             if (localFileExisted) { |  | ||||||
|               nativeBinding = require('./gta5.linux-riscv64-musl.node') |  | ||||||
|             } else { |  | ||||||
|               nativeBinding = require('gta5-linux-riscv64-musl') |  | ||||||
|             } |  | ||||||
|           } catch (e) { |  | ||||||
|             loadError = e |  | ||||||
|           } |  | ||||||
|         } else { |  | ||||||
|           localFileExisted = existsSync( |  | ||||||
|             join(__dirname, 'gta5.linux-riscv64-gnu.node') |  | ||||||
|           ) |  | ||||||
|           try { |  | ||||||
|             if (localFileExisted) { |  | ||||||
|               nativeBinding = require('./gta5.linux-riscv64-gnu.node') |  | ||||||
|             } else { |  | ||||||
|               nativeBinding = require('gta5-linux-riscv64-gnu') |  | ||||||
|             } |  | ||||||
|           } catch (e) { |  | ||||||
|             loadError = e |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|         break |  | ||||||
|       case 's390x': |  | ||||||
|         localFileExisted = existsSync( |  | ||||||
|           join(__dirname, 'gta5.linux-s390x-gnu.node') |  | ||||||
|         ) |  | ||||||
|         try { |  | ||||||
|           if (localFileExisted) { |  | ||||||
|             nativeBinding = require('./gta5.linux-s390x-gnu.node') |  | ||||||
|           } else { |  | ||||||
|             nativeBinding = require('gta5-linux-s390x-gnu') |  | ||||||
|           } |  | ||||||
|         } catch (e) { |  | ||||||
|           loadError = e |  | ||||||
|         } |  | ||||||
|         break |  | ||||||
|       default: |  | ||||||
|         throw new Error(`Unsupported architecture on Linux: ${arch}`) |  | ||||||
|     } |  | ||||||
|     break |  | ||||||
|   default: |  | ||||||
|     throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| if (!nativeBinding) { |  | ||||||
|   if (loadError) { |  | ||||||
|     throw loadError |  | ||||||
|   } |  | ||||||
|   throw new Error(`Failed to load native binding`) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const { checkLicense, doSensitiveOperation, getSecretData, isLicensed } = nativeBinding |  | ||||||
| 
 |  | ||||||
| module.exports.checkLicense = checkLicense |  | ||||||
| module.exports.doSensitiveOperation = doSensitiveOperation |  | ||||||
| module.exports.getSecretData = getSecretData |  | ||||||
| module.exports.isLicensed = isLicensed |  | ||||||
| @ -4,8 +4,7 @@ | |||||||
|     "resolveJsonModule": true, |     "resolveJsonModule": true, | ||||||
|     "baseUrl": "./src", |     "baseUrl": "./src", | ||||||
|     "types": [ |     "types": [ | ||||||
|       "node", |       "../../node_modules/@ragempcommunity/types-server", | ||||||
|       "../../node_modules/@ragempcommunity/types-server/", |  | ||||||
|       "../shared/declarations/rage-fw-shared-types/" |       "../shared/declarations/rage-fw-shared-types/" | ||||||
|     ] |     ] | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -1,13 +1,11 @@ | |||||||
| declare module '@entityseven/rage-fw-shared-types' { | declare module 'rage-fw-shared-types' { | ||||||
|     export interface RageFW_ICustomClientEvent { |     export interface RageFW_ICustomClientEvent {} | ||||||
|         customClientEvent(greetings: string): string |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     export interface RageFW_ICustomServerEvent { |     export interface RageFW_ICustomServerEvent { | ||||||
|         customServerEvent(greetings: string): string |         customServerEvent(greetings: string): string | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     export interface RageFW_ICustomBrowserEvent { |     export interface RageFW_ICustomCefEvent { | ||||||
|         customBrowserEvent(greetings: string): string |         customCefEvent(greetings: string): string | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -4,21 +4,22 @@ | |||||||
|   "workspaces": ["apps/*"], |   "workspaces": ["apps/*"], | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "server:update": "cd server && rage-win64.exe", |     "server:update": "cd server && rage-win64.exe", | ||||||
|  | 
 | ||||||
|     "build:client": "cd apps/client && pnpm build", |     "build:client": "cd apps/client && pnpm build", | ||||||
|     "build:server": "cd apps/server && pnpm build", |     "build:server": "cd apps/server && pnpm build", | ||||||
|     "build:cef": "cd apps/cef && pnpm build", |     "build:cef": "cd apps/cef && pnpm build", | ||||||
|     "build:all": "pnpm build:client && pnpm build:server && pnpm build:cef" |     "build:all": "pnpm build:client && pnpm build:server && pnpm build:cef" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@entityseven/rage-fw-shared-types": "0.2.0", |  | ||||||
|     "@ragempcommunity/types-client": "^2.1.8", |     "@ragempcommunity/types-client": "^2.1.8", | ||||||
|     "@ragempcommunity/types-server": "^2.1.8", |     "@ragempcommunity/types-server": "^2.1.8", | ||||||
|     "@ragempcommunity/types-cef": "^2.1.8", |     "@ragempcommunity/types-cef": "^2.1.8", | ||||||
|  |     "rage-fw-shared-types": "latest", | ||||||
|     "esbuild": "^0.21.5", |     "esbuild": "^0.21.5", | ||||||
|     "typescript": "^5.4.5", |     "typescript": "^5.4.5", | ||||||
|     "prettier": "^3.3.2" |     "prettier": "^3.3.2" | ||||||
|   }, |   }, | ||||||
|   "author": "Entity Seven Group", |   "author": "Entity Seven Group", | ||||||
|   "license": "MIT", |   "license": "CC BY-ND", | ||||||
|   "version": "0.1.0" |   "version": "0.1.0" | ||||||
| } | } | ||||||
|  | |||||||
										
											Binary file not shown.
										
									
								
							| @ -1,7 +1,7 @@ | |||||||
| { | { | ||||||
|   "compilerOptions": { |   "compilerOptions": { | ||||||
|     "target": "ESNext", |     "target": "ESNext", | ||||||
|     "lib": ["ESNext", "DOM"], |     "lib": ["ESNext", "ES2019"], | ||||||
|     "moduleResolution": "node", |     "moduleResolution": "node", | ||||||
|     "module": "ESNext", |     "module": "ESNext", | ||||||
|     "resolveJsonModule": true, |     "resolveJsonModule": true, | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user