├── .env.example ├── .gitignore ├── README.md ├── .editorconfig ├── src ├── common │ ├── config.ts │ ├── locale.ts │ └── utils.ts ├── client │ └── index.ts └── server │ ├── db.ts │ ├── index.ts │ ├── commands.ts │ └── garage │ └── class.ts ├── packages └── database │ └── schema.prisma ├── static └── config.json ├── tsconfig.json ├── sql └── schema.sql ├── LICENSE ├── locales └── en.json ├── package.json ├── scripts ├── build.js └── manifest.go └── pnpm-lock.yaml /.env.example: -------------------------------------------------------------------------------- 1 | DATABASE_URL="" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | .env 4 | fxmanifest.lua -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [Moved to Codeberg](https://codeberg.org/death_enclaimed/fivem-parking) 2 | 3 | **This repository is not mirrored.** 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = crlf 7 | charset = utf-8 8 | trim_trailing_whitespace = false 9 | insert_final_newline = false -------------------------------------------------------------------------------- /src/common/config.ts: -------------------------------------------------------------------------------- 1 | import type Raw from '../../static/config.json'; 2 | import { loadJson } from './utils'; 3 | 4 | export type Config = typeof Raw; 5 | export default loadJson('static/config.json'); 6 | -------------------------------------------------------------------------------- /packages/database/schema.prisma: -------------------------------------------------------------------------------- 1 | generator client { 2 | provider = "prisma-client-js" 3 | binaryTargets = ["native"] 4 | engineType = "binary" 5 | } 6 | 7 | datasource db { 8 | provider = "mysql" 9 | url = env("DATABASE_URL") 10 | } -------------------------------------------------------------------------------- /src/common/locale.ts: -------------------------------------------------------------------------------- 1 | import { locale, type FlattenObjectKeys } from '@overextended/ox_lib'; 2 | 3 | type Raw = FlattenObjectKeys; 4 | 5 | function Locale(str: T, ...args: any[]): string; 6 | function Locale(str: T, ...args: any[]) { 7 | return locale(str, ...args); 8 | } 9 | 10 | export default Locale; 11 | -------------------------------------------------------------------------------- /static/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "Garage": { 3 | "StoreCost": 300, 4 | "RetrieveCost": 500 5 | }, 6 | "Impound": { 7 | "Cost": 1000, 8 | "Location": [ 9 | { 10 | "x": 407.9077, 11 | "y": -1632.7781, 12 | "z": 29.6505, 13 | "radius": 10 14 | } 15 | ] 16 | }, 17 | "Group": "group.admin", 18 | "Item": "money", 19 | "Webhook": "https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks" 20 | } 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "bundler", 4 | "module": "es2022", 5 | "target": "es2023", 6 | "lib": ["es2023"], 7 | "types": ["@citizenfx/client", "@citizenfx/server", "@types/node"], 8 | "resolveJsonModule": true, 9 | "esModuleInterop": true, 10 | "noImplicitAny": true, 11 | "noImplicitThis": true, 12 | "strictNullChecks": true, 13 | "strictFunctionTypes": true, 14 | "allowUnreachableCode": false 15 | }, 16 | "include": ["./src"] 17 | } 18 | -------------------------------------------------------------------------------- /src/client/index.ts: -------------------------------------------------------------------------------- 1 | import lib, { onServerCallback, triggerServerCallback } from '@overextended/ox_lib/client'; 2 | import Locale from '../common/locale'; 3 | 4 | onServerCallback('fivem-parking:client:listVehicles', (vehicles: { id: number; plate: string; model: string; stored: string | null }[]) => { 5 | const options = vehicles.map((vehicle) => ({ 6 | title: `(#${vehicle.id}) ${vehicle.model} (${vehicle.plate})`, 7 | description: `${vehicle.stored}`, 8 | onSelect: vehicle.stored === 'stored' ? () => triggerServerCallback('fivem-parking:server:spawnVehicle', 200, vehicle.id) : undefined, 9 | disabled: vehicle.stored !== 'stored', 10 | })); 11 | 12 | lib.registerContext({ 13 | id: 'vehicle_menu', 14 | title: Locale('your_vehicles'), 15 | options: options, 16 | }); 17 | 18 | lib.showContext('vehicle_menu'); 19 | }); 20 | -------------------------------------------------------------------------------- /sql/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS `vehicles` ( 2 | `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, 3 | `plate` CHAR(8) NOT NULL DEFAULT '', 4 | `vin` CHAR(17) NOT NULL, 5 | `owner` INT UNSIGNED NULL DEFAULT NULL, 6 | `group` VARCHAR(20) NULL DEFAULT NULL, 7 | `model` VARCHAR(20) NOT NULL, 8 | `class` TINYINT UNSIGNED NULL DEFAULT NULL, 9 | `data` JSON NOT NULL, 10 | `trunk` JSON NULL DEFAULT NULL, 11 | `glovebox` JSON NULL DEFAULT NULL, 12 | `stored` VARCHAR(50) NULL DEFAULT NULL, 13 | PRIMARY KEY (`id`), 14 | UNIQUE KEY `plate` (`plate`), 15 | UNIQUE KEY `vin` (`vin`), 16 | KEY `vehicles_owner_key` (`owner`), 17 | CONSTRAINT `vehicles_owner_fk` FOREIGN KEY (`owner`) REFERENCES `characters` (`charId`) ON DELETE CASCADE ON UPDATE CASCADE, 18 | CONSTRAINT `vehicles_group_fk` FOREIGN KEY (`group`) REFERENCES `ox_groups` (`name`) ON DELETE CASCADE ON UPDATE CASCADE 19 | ); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 BerkieBb & Brian Dougherty 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software with some restrictions. You may use, modify, and integrate 6 | the Software into any project, subject to the following conditions: 7 | 8 | You may not redistribute, sell, sublicense, or resell the Software, 9 | in its original or modified form, either as part of another project 10 | or as a standalone product, in any medium or method. 11 | 12 | The above copyright notice and this permission notice must be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, 20 | OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "your_vehicles": "Your Vehicles", 3 | "something_went_wrong": "^#d73232ERROR ^#ffffffSomething went wrong.", 4 | "no_vehicles_owned": "^#d73232ERROR ^#ffffffYou do not own any vehicles!", 5 | "no_player_found": "^#d73232ERROR ^#ffffffNo player with the specified ID found.", 6 | "no_vehicles_for_player": "^#d73232ERROR ^#ffffffNo vehicles found for player with the specified ID.", 7 | "not_inside_vehicle": "^#d73232ERROR ^#ffffffYou are not inside of a vehicle!", 8 | "not_vehicle_owner": "^#d73232ERROR ^#ffffffYou are not the owner of this vehicle!", 9 | "not_enough_money": "^#d73232ERROR ^#ffffffYou do not have enough money!", 10 | "not_in_impound_area": "^#d73232ERROR ^#ffffffYou are not in the impound area!", 11 | "not_impounded": "^#d73232ERROR ^#ffffffVehicle is not impounded!", 12 | "success_store": "^#5e81ac[INFO] ^#ffffffSuccessfully parked vehicle.", 13 | "success_restore": "^#5e81ac[INFO] ^#ffffffSuccessfully restored vehicle.", 14 | "success_spawned": "^#5e81ac[INFO] ^#ffffffSuccessfully spawned vehicle.", 15 | "success_deleted": "^#5e81ac[INFO] ^#ffffffSuccessfully deleted vehicle with the specified plate number from the database.", 16 | "failed_to_spawn": "^#d73232ERROR ^#ffffffFailed to spawn the vehicle.", 17 | "failed_to_give": "^#d73232ERROR ^#ffffffFailed to give vehicle.", 18 | "failed_to_find": "^#d73232ERROR ^#ffffffFailed to find vehicle.", 19 | "failed_to_delete": "^#d73232ERROR ^#ffffffFailed to delete vehicle with the specified plate number from the database." 20 | } 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fivem-parking", 3 | "description": "Server-side vehicle garage for CitizenFX servers.", 4 | "author": "BerkieBb, death_enclaimed", 5 | "version": "0.32.5", 6 | "license": "MIT", 7 | "type": "module", 8 | "scripts": { 9 | "connect": "npx prisma db pull && npx prisma generate", 10 | "build": "node scripts/build.js && go run scripts/manifest.go", 11 | "watch": "node scripts/build.js --watch", 12 | "ci": "pnpm run connect && pnpm run build" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://codeberg.org/death_enclaimed/fivem-parking" 17 | }, 18 | "prisma": { 19 | "schema": "./packages/database/schema.prisma" 20 | }, 21 | "manifest": { 22 | "client_scripts": [ 23 | "dist/client/*.js" 24 | ], 25 | "server_scripts": [ 26 | "dist/server/*.js" 27 | ], 28 | "files": [ 29 | "locales/*.json" 30 | ], 31 | "dependencies": [ 32 | "/server:12913", 33 | "/onesync", 34 | "ox_lib", 35 | "ox_core", 36 | "ox_inventory" 37 | ] 38 | }, 39 | "dependencies": { 40 | "@nativewrappers/fivem": "^0.0.90", 41 | "@nativewrappers/server": "^0.0.90", 42 | "@overextended/ox_core": "^1.5.1", 43 | "@overextended/ox_lib": "latest", 44 | "@prisma/client": "^6.13.0", 45 | "node-fetch": "^3.3.2" 46 | }, 47 | "devDependencies": { 48 | "@citizenfx/client": "latest", 49 | "@citizenfx/server": "latest", 50 | "@types/node": "^22.13.11", 51 | "esbuild": "^0.25.1", 52 | "prisma": "^6.13.0" 53 | }, 54 | "engines": { 55 | "node": ">=22.9.0" 56 | } 57 | } -------------------------------------------------------------------------------- /src/common/utils.ts: -------------------------------------------------------------------------------- 1 | import { cache } from '@overextended/ox_lib'; 2 | import fetch from 'node-fetch'; 3 | import Config from './config'; 4 | 5 | export function loadFile(path: string) { 6 | return LoadResourceFile(cache.resource, path); 7 | } 8 | 9 | export function loadJson(path: string): T { 10 | return JSON.parse(loadFile(path)) as T; 11 | } 12 | 13 | export function hasItem(source: number, item: string, amount: number = 1) { 14 | return exports.ox_inventory.GetItemCount(source, item) >= amount; 15 | } 16 | 17 | export async function removeItem(source: number, item: string, amount: number) { 18 | return exports.ox_inventory.RemoveItem(source, item, amount); 19 | } 20 | 21 | export function sendChatMessage(source: number, message: string) { 22 | return exports.chat.addMessage(source, message); 23 | } 24 | 25 | export function getArea( 26 | coords: { x: number; y: number; z: number }, 27 | areas: { x: number; y: number; z: number; radius: number }[], 28 | ) { 29 | return areas.some((area) => { 30 | const distance: number = Math.sqrt((coords.x - area.x) ** 2 + (coords.y - area.y) ** 2 + (coords.z - area.z) ** 2); 31 | return distance <= area.radius; 32 | }); 33 | } 34 | 35 | export async function sendLog(message: string) { 36 | const date = new Date(); 37 | await fetch(Config.Webhook, { 38 | method: 'POST', 39 | headers: { 'Content-Type': 'application/json' }, 40 | body: JSON.stringify({ 41 | username: cache.resource, 42 | content: `**[${`${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`}]** ${message}`, 43 | }), 44 | }); 45 | } 46 | -------------------------------------------------------------------------------- /src/server/db.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client'; 2 | 3 | const db = new (class Database { 4 | prisma: PrismaClient; 5 | 6 | constructor() { 7 | this.prisma = new PrismaClient(); 8 | } 9 | 10 | private async handle(operation: Promise, name: string): Promise { 11 | try { 12 | return await operation; 13 | } catch (error) { 14 | console.error(`${name}:`, error); 15 | return null; 16 | } 17 | } 18 | 19 | private async filterVehicle(filter: Record) { 20 | return this.handle(this.prisma.vehicles.findFirst({ where: filter, select: { id: true } }), 'filterVehicle'); 21 | } 22 | 23 | public async getVehicleById(id: number) { 24 | return (await this.filterVehicle({ id })) ?? false; 25 | } 26 | 27 | public async getVehicleOwner(id: number, owner: number) { 28 | return (await this.filterVehicle({ id, owner })) ?? false; 29 | } 30 | 31 | public async getVehicleStatus(id: number, status: string) { 32 | return await this.handle(this.prisma.vehicles.findFirst({ where: { id, stored: status } }), 'getVehicleStatus'); 33 | } 34 | 35 | public async getVehiclePlate(plate: string) { 36 | return await this.handle(this.prisma.vehicles.findFirst({ where: { plate } }), 'getVehiclePlate'); 37 | } 38 | 39 | public async getOwnedVehicles(owner: number) { 40 | return ((await this.handle(this.prisma.vehicles.findMany({ where: { owner }, select: { id: true, plate: true, owner: true, model: true, stored: true } }), 'getOwnedVehicles')) ?? []); 41 | } 42 | 43 | public async setVehicleStatus(id: number, status: string) { 44 | return await this.handle(this.prisma.vehicles.update({ where: { id }, data: { stored: status } }), 'setVehicleStatus'); 45 | } 46 | 47 | public async deleteVehicle(plate: string) { 48 | return await this.handle(this.prisma.vehicles.delete({ where: { plate } }), 'deleteVehicle'); 49 | } 50 | })(); 51 | 52 | export default db; 53 | -------------------------------------------------------------------------------- /src/server/index.ts: -------------------------------------------------------------------------------- 1 | import * as Cfx from '@nativewrappers/fivem'; 2 | import { GetPlayer, SpawnVehicle } from '@overextended/ox_core/server'; 3 | import { onClientCallback } from '@overextended/ox_lib/server'; 4 | import Config from '../common/config'; 5 | import Locale from '../common/locale'; 6 | import { hasItem, removeItem, sendChatMessage, sendLog } from '../common/utils'; 7 | import './commands'; 8 | import db from './db'; 9 | 10 | onClientCallback('fivem-parking:server:spawnVehicle', async (source: number, vehicleId: number) => { 11 | const player = GetPlayer(source); 12 | 13 | if (!player?.charId) return false; 14 | 15 | const vehicle = await db.getVehicleById(vehicleId); 16 | if (!vehicle) { 17 | sendChatMessage(source, Locale('something_went_wrong')); 18 | return false; 19 | } 20 | 21 | const owner = await db.getVehicleOwner(vehicleId, player.charId); 22 | if (!owner) { 23 | sendChatMessage(source, Locale('not_vehicle_owner')); 24 | return false; 25 | } 26 | 27 | if (!hasItem(source, Config.Item, Config.Garage.RetrieveCost)) { 28 | sendChatMessage(source, Locale('not_enough_money')); 29 | return false; 30 | } 31 | 32 | const money = await removeItem(source, Config.Item, Config.Garage.RetrieveCost); 33 | if (!money) return false; 34 | 35 | await Cfx.Delay(100); 36 | 37 | const success = await SpawnVehicle(vehicleId, player.getCoords()); 38 | if (!success) { 39 | sendChatMessage(source, Locale('failed_to_spawn')); 40 | return; 41 | } 42 | 43 | setImmediate(() => { 44 | TaskWarpPedIntoVehicle(GetPlayerPed(source), success.entity, -1); 45 | }); 46 | 47 | await db.setVehicleStatus(vehicleId, 'outside'); 48 | sendChatMessage(source, Locale('success_spawned')); 49 | await sendLog(`[VEHICLE] ${player.get('name')} (${source}) just spawned their vehicle #${vehicleId}! Position: ${player.getCoords()[0]} ${player.getCoords()[1]} ${player.getCoords()[2]} - dimension: ${GetPlayerRoutingBucket(String(source))}.`); 50 | }); 51 | -------------------------------------------------------------------------------- /src/server/commands.ts: -------------------------------------------------------------------------------- 1 | import { Command } from '@nativewrappers/server'; 2 | import Config from '../common/config'; 3 | import { Garage } from './garage/class'; 4 | 5 | new Command(['list', 'vg'], 'View a list of your owned vehicles.', async ({ source }) => { 6 | await Garage.prototype.listVehicles(source); 7 | }); 8 | 9 | new Command(['park', 'vp'], 'Store a vehicle in to your personal garage.', async ({ source }) => { 10 | await Garage.prototype.parkVehicle(source); 11 | }); 12 | 13 | new Command(['return', 'vi'], 'Restore a vehicle from the impound lot to your personal garage.', async (args) => { 14 | await Garage.prototype.returnVehicle(args.source, { vehicleId: args.vehicleId }); 15 | }, [ 16 | { 17 | name: 'vehicleId', 18 | type: 'number', 19 | } 20 | ] as const); 21 | 22 | new Command(['addveh'], 'Create a new vehicle and grant it to the target player.', async (args) => { 23 | await Garage.prototype.adminGiveVehicle(args.source, { model: args.model, playerId: args.playerId }); 24 | }, [ 25 | { 26 | name: 'model', 27 | type: 'string', 28 | }, 29 | { 30 | name: 'playerId', 31 | type: 'number', 32 | }, 33 | ] as const, Config.Group); 34 | 35 | new Command(['deleteveh', 'delveh'], "Delete a vehicle from the database and the owner's personal garage.", async (args) => { 36 | await Garage.prototype.adminDeleteVehicle(args.source, { plate: args.plate }); 37 | }, [ 38 | { 39 | name: 'plate', 40 | type: 'string', 41 | }, 42 | ] as const, Config.Group); 43 | 44 | new Command(['admincar', 'acar'], 'Create a new vehicle and set it as owned.', async (args) => { 45 | await Garage.prototype.adminSetVehicle(args.source, { model: args.model }); 46 | }, [ 47 | { 48 | name: 'model', 49 | type: 'string', 50 | }, 51 | ] as const, Config.Group); 52 | 53 | new Command(['alist', 'avg'], "View a list of the target player's owned vehicles.", async (args) => { 54 | await Garage.prototype.adminViewVehicles(args.source, { playerId: args.playerId }); 55 | }, [ 56 | { 57 | name: 'playerId', 58 | type: 'number', 59 | }, 60 | ] as const, Config.Group); 61 | -------------------------------------------------------------------------------- /scripts/build.js: -------------------------------------------------------------------------------- 1 | import { context } from 'esbuild'; 2 | import { readFile } from 'fs/promises'; 3 | import path from 'path'; 4 | import process from 'process'; 5 | 6 | const watch = process.argv.includes('--watch'); 7 | 8 | async function build(development) { 9 | const ctx = await context({ 10 | entryPoints: ['./src/client/index.ts', './src/server/index.ts'], 11 | outdir: './dist', 12 | platform: 'node', 13 | target: 'node22', 14 | bundle: true, 15 | minify: false, 16 | plugins: [ 17 | { 18 | name: 'build', 19 | setup(build) { 20 | build.onLoad({ filter: /.\.(js|ts)$/ }, async (args) => { 21 | const data = await readFile(args.path, 'utf8'); 22 | const escape = (p) => (/^win/.test(process.platform) ? p.replace(/\\/g, '/') : p); 23 | 24 | const global = /__(?=(filename|dirname))/g; 25 | const cache = global.test(data); 26 | 27 | const location = cache 28 | ? `const location = { filename: '${escape(args.path)}', dirname: '${escape(path.dirname(args.path))}' }; let __line = 0;\n` 29 | : ''; 30 | 31 | const insert = data 32 | .split('\n') 33 | .map((line, index) => `${line.includes('__line') ? `__line=${index + 1};` : ''}${line}`) 34 | .join('\n'); 35 | 36 | return { 37 | contents: cache ? location + insert.replace(global, 'location.') : insert, 38 | loader: path.extname(args.path).slice(1), 39 | }; 40 | }); 41 | 42 | build.onEnd(async (result) => { 43 | if (result.errors.length > 0) { 44 | console.error(`Build ended with ${result.errors.length} error(s):`); 45 | result.errors.forEach((error, i) => { 46 | console.error(`Error ${i + 1}: ${error.text}`); 47 | }); 48 | return; 49 | } 50 | 51 | console.log(development ? 'Successfully built (development)' : 'Successfully built (production)'); 52 | 53 | if (!development) { 54 | process.exit(0); 55 | } 56 | }); 57 | }, 58 | }, 59 | ], 60 | }); 61 | 62 | if (development) { 63 | await ctx.watch(); 64 | console.log('Watching for changes...'); 65 | } else { 66 | await ctx.rebuild(); 67 | } 68 | } 69 | 70 | watch ? build(true) : build(false); 71 | -------------------------------------------------------------------------------- /scripts/manifest.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "os" 8 | "reflect" 9 | "strings" 10 | ) 11 | 12 | type Package struct { 13 | Name string `json:"name"` 14 | Description string `json:"description"` 15 | Author string `json:"author"` 16 | Version string `json:"version"` 17 | Repository struct { 18 | URL string `json:"url"` 19 | } `json:"repository"` 20 | License string `json:"license"` 21 | Manifest *Defaults `json:"manifest,omitempty"` 22 | } 23 | 24 | type Defaults struct { 25 | FxVersion string `json:"fx_version,omitempty"` 26 | Game string `json:"game,omitempty"` 27 | NodeVersion string `json:"node_version,omitempty"` 28 | Client []string `json:"client_scripts,omitempty"` 29 | Server []string `json:"server_scripts,omitempty"` 30 | Files []string `json:"files,omitempty"` 31 | Dependencies []string `json:"dependencies,omitempty"` 32 | } 33 | 34 | type Config struct { 35 | Package string 36 | Output string 37 | Defaults Defaults 38 | } 39 | 40 | func Init() *Config { 41 | return &Config{ 42 | Package: "package.json", 43 | Output: "fxmanifest.lua", 44 | Defaults: Defaults{ 45 | FxVersion: "cerulean", 46 | Game: "gta5", 47 | NodeVersion: "22", 48 | Client: []string{"dist/client/*.js"}, 49 | Server: []string{"dist/server/*.js"}, 50 | Files: []string{"locales/*.json"}, 51 | Dependencies: []string{"/server:12913", "/onesync", "ox_lib", "ox_core", "ox_inventory"}, 52 | }, 53 | } 54 | } 55 | 56 | type ManifestGenerator struct { 57 | config *Config 58 | } 59 | 60 | func new(config *Config) *ManifestGenerator { 61 | return &ManifestGenerator{config: config} 62 | } 63 | 64 | func (mg *ManifestGenerator) load() (*Package, error) { 65 | if _, err := os.Stat(mg.config.Package); os.IsNotExist(err) { 66 | return nil, fmt.Errorf("os.Stat(); os.IsNotExist(): %w", err) 67 | } 68 | 69 | data, err := os.ReadFile(mg.config.Package) 70 | if err != nil { 71 | return nil, fmt.Errorf("os.ReadFile() %s: %w", mg.config.Package, err) 72 | } 73 | 74 | var pkg Package 75 | if err := json.Unmarshal(data, &pkg); err != nil { 76 | return nil, fmt.Errorf("json.Unmarshal() %s: %w", mg.config.Package, err) 77 | } 78 | 79 | return &pkg, nil 80 | } 81 | 82 | func sanitize(s string) string { 83 | replace := map[string]string{ 84 | "'": "\\'", 85 | "\n": "\\n", 86 | "\r": "\\r", 87 | } 88 | 89 | for old, new := range replace { 90 | s = strings.ReplaceAll(s, old, new) 91 | } 92 | return s 93 | } 94 | 95 | func add(lines *[]string, title string, items []string) { 96 | if len(items) == 0 { 97 | return 98 | } 99 | 100 | *lines = append(*lines, fmt.Sprintf("\n%s {", title)) 101 | for i, item := range items { 102 | comma := "," 103 | if i == len(items)-1 { 104 | comma = "" 105 | } 106 | *lines = append(*lines, fmt.Sprintf("\t'%s'%s", sanitize(item), comma)) 107 | } 108 | *lines = append(*lines, "}") 109 | } 110 | 111 | func merge(defaults Defaults, manifest *Defaults) Defaults { 112 | if manifest == nil { 113 | return defaults 114 | } 115 | 116 | result := defaults 117 | value := reflect.ValueOf(&result).Elem() 118 | data := reflect.ValueOf(manifest).Elem() 119 | 120 | for i := 0; i < data.NumField(); i++ { 121 | field := data.Field(i) 122 | if !field.IsZero() { 123 | value.Field(i).Set(field) 124 | } 125 | } 126 | 127 | return result 128 | } 129 | 130 | func optional(lines *[]string, field, value string) { 131 | if value != "" { 132 | *lines = append(*lines, fmt.Sprintf("%s '%s'", field, sanitize(value))) 133 | } 134 | } 135 | 136 | func (mg *ManifestGenerator) write(pkg *Package) string { 137 | var lines []string 138 | 139 | config := merge(mg.config.Defaults, pkg.Manifest) 140 | 141 | lines = append(lines, fmt.Sprintf("fx_version '%s'", config.FxVersion)) 142 | lines = append(lines, fmt.Sprintf("game '%s'", config.Game)) 143 | 144 | opt := []struct { 145 | field string 146 | value string 147 | }{ 148 | {"name", pkg.Name}, 149 | {"description", pkg.Description}, 150 | {"author", pkg.Author}, 151 | {"version", pkg.Version}, 152 | {"repository", pkg.Repository.URL}, 153 | {"license", pkg.License}, 154 | {"node_version", config.NodeVersion}, 155 | } 156 | 157 | for _, pf := range opt { 158 | optional(&lines, pf.field, pf.value) 159 | } 160 | 161 | req := []struct { 162 | title string 163 | items []string 164 | }{ 165 | {"client_scripts", config.Client}, 166 | {"server_scripts", config.Server}, 167 | {"files", config.Files}, 168 | {"dependencies", config.Dependencies}, 169 | } 170 | 171 | for _, section := range req { 172 | add(&lines, section.title, section.items) 173 | } 174 | 175 | return strings.Join(lines, "\n") 176 | } 177 | 178 | func (mg *ManifestGenerator) Generate() error { 179 | pkg, err := mg.load() 180 | if err != nil { 181 | return err 182 | } 183 | 184 | manifest := mg.write(pkg) 185 | 186 | if err := os.WriteFile(mg.config.Output, []byte(manifest), 0644); err != nil { 187 | return fmt.Errorf("os.WriteFile() %s: %w", mg.config.Output, err) 188 | } 189 | 190 | return nil 191 | } 192 | 193 | func main() { 194 | config := Init() 195 | generator := new(config) 196 | 197 | if err := generator.Generate(); err != nil { 198 | log.Fatalf("generator.Generate(): %v", err) 199 | } 200 | 201 | fmt.Printf("Successfully generated %s\n", config.Output) 202 | } 203 | -------------------------------------------------------------------------------- /src/server/garage/class.ts: -------------------------------------------------------------------------------- 1 | import * as Cfx from '@nativewrappers/fivem'; 2 | import { CreateVehicle, GetPlayer, GetVehicle } from '@overextended/ox_core/server'; 3 | import { triggerClientCallback } from '@overextended/ox_lib/server'; 4 | import Config from '../../common/config'; 5 | import Locale from '../../common/locale'; 6 | import { getArea, hasItem, removeItem, sendChatMessage, sendLog } from '../../common/utils'; 7 | import db from '../db'; 8 | 9 | export class Garage { 10 | id: number; 11 | plate: string; 12 | owner: number; 13 | model: string; 14 | stored: string | null; 15 | 16 | constructor(id: number, plate: string, owner: number, model: string, stored: string | null) { 17 | this.id = id; 18 | this.plate = plate; 19 | this.owner = owner; 20 | this.model = model; 21 | this.stored = stored; 22 | } 23 | 24 | public async listVehicles(source: number) { 25 | const player = GetPlayer(source); 26 | 27 | if (!player?.charId) return []; 28 | 29 | const vehicles = await db.getOwnedVehicles(player.charId); 30 | if (!vehicles || vehicles.length === 0) { 31 | sendChatMessage(source, Locale('no_vehicles_owned')); 32 | return []; 33 | } 34 | 35 | triggerClientCallback('fivem-parking:client:listVehicles', source, vehicles); 36 | 37 | return vehicles; 38 | } 39 | 40 | public async parkVehicle(source: number): Promise { 41 | const player = GetPlayer(source); 42 | 43 | if (!player?.charId) return false; 44 | 45 | const ped = GetVehiclePedIsIn(GetPlayerPed(source), false); 46 | if (ped === 0) { 47 | sendChatMessage(source, Locale('not_inside_vehicle')); 48 | return false; 49 | } 50 | 51 | const vehicle = GetVehicle(ped); 52 | if (!vehicle?.owner) { 53 | sendChatMessage(source, Locale('not_vehicle_owner')); 54 | return false; 55 | } 56 | 57 | if (!hasItem(source, Config.Item, Config.Garage.StoreCost)) { 58 | sendChatMessage(source, Locale('not_enough_money')); 59 | return false; 60 | } 61 | 62 | const success = await removeItem(source, Config.Item, Config.Garage.StoreCost); 63 | if (!success) return false; 64 | 65 | vehicle.setStored('stored', true); 66 | sendChatMessage(source, Locale('success_store')); 67 | await sendLog(`[VEHICLE] ${player.get('name')} (${source}) just parked vehicle #${vehicle.id} with plate #${vehicle.plate} at X: ${player.getCoords()[0]} Y: ${player.getCoords()[1]} Z: ${player.getCoords()[2]}, dimension: #${GetPlayerRoutingBucket(String(source))}.`); 68 | 69 | return true; 70 | } 71 | 72 | public async returnVehicle(source: number, args: { vehicleId: number }): Promise { 73 | const player = GetPlayer(source); 74 | 75 | if (!player?.charId) return false; 76 | 77 | const vehicleId = args.vehicleId; 78 | const coords = player.getCoords(); 79 | if (!getArea({ x: coords[0], y: coords[1], z: coords[2] }, Config.Impound.Location)) { 80 | sendChatMessage(source, Locale('not_in_impound_area')); 81 | return false; 82 | } 83 | 84 | const owner = await db.getVehicleOwner(vehicleId, player.charId); 85 | if (!owner) { 86 | sendChatMessage(source, Locale('not_vehicle_owner')); 87 | return false; 88 | } 89 | 90 | const status = await db.getVehicleStatus(vehicleId, 'impound'); 91 | if (!status) { 92 | sendChatMessage(source, Locale('not_impounded')); 93 | return false; 94 | } 95 | 96 | if (!hasItem(source, Config.Item, Config.Impound.Cost)) { 97 | sendChatMessage(source, Locale('not_enough_money')); 98 | return false; 99 | } 100 | 101 | const success = await removeItem(source, Config.Item, Config.Impound.Cost); 102 | if (!success) return false; 103 | 104 | await db.setVehicleStatus(vehicleId, 'stored'); 105 | sendChatMessage(source, Locale('success_restore')); 106 | 107 | return true; 108 | } 109 | 110 | public async adminGiveVehicle(source: number, args: { model: string; playerId: number }): Promise { 111 | const player = GetPlayer(source); 112 | 113 | if (!player?.charId) return false; 114 | 115 | const model = args.model; 116 | const playerId = args.playerId; 117 | 118 | const target = GetPlayer(playerId); 119 | if (!target?.charId) { 120 | sendChatMessage(source, Locale('no_player_found')); 121 | return false; 122 | } 123 | 124 | await Cfx.Delay(100); 125 | 126 | const vehicle = await CreateVehicle({ owner: target.charId, model: model }, player.getCoords()); 127 | if (!vehicle || vehicle.owner !== target.charId) { 128 | sendChatMessage(source, Locale('failed_to_give')); 129 | return false; 130 | } 131 | 132 | vehicle.setStored('stored', true); 133 | sendChatMessage(source, Locale('success_spawned')); 134 | 135 | return true; 136 | } 137 | 138 | public async adminDeleteVehicle(source: number, args: { plate: string }): Promise { 139 | const player = GetPlayer(source); 140 | 141 | if (!player?.charId) return false; 142 | 143 | const plate = args.plate; 144 | const result = await db.getVehiclePlate(plate); 145 | if (!result) { 146 | sendChatMessage(source, Locale('failed_to_find')); 147 | return false; 148 | } 149 | 150 | await Cfx.Delay(100); 151 | 152 | const success = await db.deleteVehicle(plate); 153 | if (!success) { 154 | sendChatMessage(source, Locale('failed_to_delete')); 155 | return false; 156 | } 157 | 158 | sendChatMessage(source, Locale('success_deleted')); 159 | 160 | return true; 161 | } 162 | 163 | public async adminSetVehicle(source: number, args: { model: string }): Promise { 164 | const player = GetPlayer(source); 165 | 166 | if (!player?.charId) return false; 167 | 168 | const model = args.model; 169 | 170 | await Cfx.Delay(100); 171 | 172 | const vehicle = await CreateVehicle({ owner: player.charId, model: model }, player.getCoords()); 173 | if (!vehicle || vehicle.owner !== player.charId) { 174 | sendChatMessage(source, Locale('failed_to_spawn')); 175 | return false; 176 | } 177 | 178 | vehicle.setStored('outside', false); 179 | sendChatMessage(source, Locale('success_spawned')); 180 | 181 | return true; 182 | } 183 | 184 | public async adminViewVehicles(source: number, args: { playerId: number }): Promise { 185 | const player = GetPlayer(source); 186 | 187 | if (!player?.charId) return false; 188 | 189 | const playerId = args.playerId; 190 | const target = GetPlayer(playerId); 191 | if (!target?.charId) { 192 | sendChatMessage(source, Locale('no_player_found')); 193 | return false; 194 | } 195 | 196 | const vehicles = await db.getOwnedVehicles(target.charId); 197 | if (vehicles.length === 0) { 198 | sendChatMessage(source, Locale('no_vehicles_for_player')); 199 | return false; 200 | } 201 | 202 | sendChatMessage(source, `^#5e81ac--------- ^#ffffff${target.get('name')} (${playerId}) Owned Vehicles ^#5e81ac---------`); 203 | sendChatMessage(source, vehicles.map((vehicle: { id: number; plate: string; model: string; stored: string | null }): string => `ID: ^#5e81ac${vehicle.id} ^#ffffff| Plate: ^#5e81ac${vehicle.plate} ^#ffffff| Model: ^#5e81ac${vehicle.model} ^#ffffff| Status: ^#5e81ac${vehicle.stored ?? 'N/A'}^#ffffff --- `).join('\n')); 204 | await sendLog(`${player.get('name')} (${source}) just used '/playervehicles' on ${target.get('name')} (${target.source}).`); 205 | 206 | return true; 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | '@nativewrappers/fivem': 12 | specifier: ^0.0.90 13 | version: 0.0.90 14 | '@nativewrappers/server': 15 | specifier: ^0.0.90 16 | version: 0.0.90 17 | '@overextended/ox_core': 18 | specifier: ^1.5.1 19 | version: 1.5.1 20 | '@overextended/ox_lib': 21 | specifier: latest 22 | version: 3.30.5 23 | '@prisma/client': 24 | specifier: ^6.13.0 25 | version: 6.13.0(prisma@6.13.0(typescript@5.8.2))(typescript@5.8.2) 26 | node-fetch: 27 | specifier: ^3.3.2 28 | version: 3.3.2 29 | devDependencies: 30 | '@citizenfx/client': 31 | specifier: latest 32 | version: 2.0.13796-1 33 | '@citizenfx/server': 34 | specifier: latest 35 | version: 2.0.13796-1 36 | '@types/node': 37 | specifier: ^22.13.11 38 | version: 22.13.13 39 | esbuild: 40 | specifier: ^0.25.1 41 | version: 0.25.1 42 | prisma: 43 | specifier: ^6.13.0 44 | version: 6.13.0(typescript@5.8.2) 45 | 46 | packages: 47 | 48 | '@babel/code-frame@7.27.1': 49 | resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} 50 | engines: {node: '>=6.9.0'} 51 | 52 | '@babel/helper-validator-identifier@7.27.1': 53 | resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} 54 | engines: {node: '>=6.9.0'} 55 | 56 | '@citizenfx/client@2.0.13796-1': 57 | resolution: {integrity: sha512-20Ce2UZwL9Hg7A5+uR7SMVSA6g12bt6XDxiYHBeTTmi7xfNC9TgzWW8KyLiSpyFsDeH0/QZZKwxDha9v1/RwHw==} 58 | 59 | '@citizenfx/server@2.0.13796-1': 60 | resolution: {integrity: sha512-FsSNHNeZPMQIEmTerirPdv41jNu1f/MXmqyn2G3VUH+JU10JK80TqNhb/LGwMVtjvI9O7PN0VGWwKsOE2d5OtA==} 61 | 62 | '@esbuild/aix-ppc64@0.25.1': 63 | resolution: {integrity: sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==} 64 | engines: {node: '>=18'} 65 | cpu: [ppc64] 66 | os: [aix] 67 | 68 | '@esbuild/android-arm64@0.25.1': 69 | resolution: {integrity: sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==} 70 | engines: {node: '>=18'} 71 | cpu: [arm64] 72 | os: [android] 73 | 74 | '@esbuild/android-arm@0.25.1': 75 | resolution: {integrity: sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==} 76 | engines: {node: '>=18'} 77 | cpu: [arm] 78 | os: [android] 79 | 80 | '@esbuild/android-x64@0.25.1': 81 | resolution: {integrity: sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==} 82 | engines: {node: '>=18'} 83 | cpu: [x64] 84 | os: [android] 85 | 86 | '@esbuild/darwin-arm64@0.25.1': 87 | resolution: {integrity: sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==} 88 | engines: {node: '>=18'} 89 | cpu: [arm64] 90 | os: [darwin] 91 | 92 | '@esbuild/darwin-x64@0.25.1': 93 | resolution: {integrity: sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==} 94 | engines: {node: '>=18'} 95 | cpu: [x64] 96 | os: [darwin] 97 | 98 | '@esbuild/freebsd-arm64@0.25.1': 99 | resolution: {integrity: sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==} 100 | engines: {node: '>=18'} 101 | cpu: [arm64] 102 | os: [freebsd] 103 | 104 | '@esbuild/freebsd-x64@0.25.1': 105 | resolution: {integrity: sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==} 106 | engines: {node: '>=18'} 107 | cpu: [x64] 108 | os: [freebsd] 109 | 110 | '@esbuild/linux-arm64@0.25.1': 111 | resolution: {integrity: sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==} 112 | engines: {node: '>=18'} 113 | cpu: [arm64] 114 | os: [linux] 115 | 116 | '@esbuild/linux-arm@0.25.1': 117 | resolution: {integrity: sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==} 118 | engines: {node: '>=18'} 119 | cpu: [arm] 120 | os: [linux] 121 | 122 | '@esbuild/linux-ia32@0.25.1': 123 | resolution: {integrity: sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==} 124 | engines: {node: '>=18'} 125 | cpu: [ia32] 126 | os: [linux] 127 | 128 | '@esbuild/linux-loong64@0.25.1': 129 | resolution: {integrity: sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==} 130 | engines: {node: '>=18'} 131 | cpu: [loong64] 132 | os: [linux] 133 | 134 | '@esbuild/linux-mips64el@0.25.1': 135 | resolution: {integrity: sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==} 136 | engines: {node: '>=18'} 137 | cpu: [mips64el] 138 | os: [linux] 139 | 140 | '@esbuild/linux-ppc64@0.25.1': 141 | resolution: {integrity: sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==} 142 | engines: {node: '>=18'} 143 | cpu: [ppc64] 144 | os: [linux] 145 | 146 | '@esbuild/linux-riscv64@0.25.1': 147 | resolution: {integrity: sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==} 148 | engines: {node: '>=18'} 149 | cpu: [riscv64] 150 | os: [linux] 151 | 152 | '@esbuild/linux-s390x@0.25.1': 153 | resolution: {integrity: sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==} 154 | engines: {node: '>=18'} 155 | cpu: [s390x] 156 | os: [linux] 157 | 158 | '@esbuild/linux-x64@0.25.1': 159 | resolution: {integrity: sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==} 160 | engines: {node: '>=18'} 161 | cpu: [x64] 162 | os: [linux] 163 | 164 | '@esbuild/netbsd-arm64@0.25.1': 165 | resolution: {integrity: sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==} 166 | engines: {node: '>=18'} 167 | cpu: [arm64] 168 | os: [netbsd] 169 | 170 | '@esbuild/netbsd-x64@0.25.1': 171 | resolution: {integrity: sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==} 172 | engines: {node: '>=18'} 173 | cpu: [x64] 174 | os: [netbsd] 175 | 176 | '@esbuild/openbsd-arm64@0.25.1': 177 | resolution: {integrity: sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==} 178 | engines: {node: '>=18'} 179 | cpu: [arm64] 180 | os: [openbsd] 181 | 182 | '@esbuild/openbsd-x64@0.25.1': 183 | resolution: {integrity: sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==} 184 | engines: {node: '>=18'} 185 | cpu: [x64] 186 | os: [openbsd] 187 | 188 | '@esbuild/sunos-x64@0.25.1': 189 | resolution: {integrity: sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==} 190 | engines: {node: '>=18'} 191 | cpu: [x64] 192 | os: [sunos] 193 | 194 | '@esbuild/win32-arm64@0.25.1': 195 | resolution: {integrity: sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==} 196 | engines: {node: '>=18'} 197 | cpu: [arm64] 198 | os: [win32] 199 | 200 | '@esbuild/win32-ia32@0.25.1': 201 | resolution: {integrity: sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==} 202 | engines: {node: '>=18'} 203 | cpu: [ia32] 204 | os: [win32] 205 | 206 | '@esbuild/win32-x64@0.25.1': 207 | resolution: {integrity: sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==} 208 | engines: {node: '>=18'} 209 | cpu: [x64] 210 | os: [win32] 211 | 212 | '@nativewrappers/client@1.7.33': 213 | resolution: {integrity: sha512-phuBBGdDPxZiZyw5CaFs1XWfvllnEtwATMdLaNucwMofVg/O/FjlP1bTUq4SOm4qhSZ4Zdo351ijHzBSIbZs6g==} 214 | 215 | '@nativewrappers/common@0.0.56': 216 | resolution: {integrity: sha512-z4/h3CM2JWuAj2GTgaKA1zl1PRg1/4414Gm/jw1QKQVv47ad2nV1vevdpVDAgRsxVI2YnYzGaGZ1see0JBdtyg==} 217 | 218 | '@nativewrappers/fivem@0.0.56': 219 | resolution: {integrity: sha512-0LWQos/Z6SlGNfSORRnaBEIBj4w67fp145fp57z/t5Qgvi5N7JyCZhePHB2DA5vPuz4iM2p/MksPAJM+9jGXKg==} 220 | 221 | '@nativewrappers/fivem@0.0.90': 222 | resolution: {integrity: sha512-gVs8w2vifemOecAU9b78/6puFiqcHNqzkzzYEQSrfWSkPwHPzDbVkAmK20mVRQclUDM/Yc23PDE7Fr0zrnP4Ag==} 223 | 224 | '@nativewrappers/server@0.0.56': 225 | resolution: {integrity: sha512-8TxpIBdBIMj/tiKh7zrJSNUDI0tUo9arJwwpVOSh5XM6c/A8KzW0H1RR16VE3nBYAbjLWDKqGSzuQsVSCLWT5Q==} 226 | 227 | '@nativewrappers/server@0.0.90': 228 | resolution: {integrity: sha512-9ZqOKlxB4nLHvqIFHp6JDTnUSSziY7wQ9KFj4aXECcm1TpXrUvSgr9J+sYTH+q0LREpxbrAo+Nyy92U/RlGRXg==} 229 | 230 | '@overextended/ox_core@1.5.1': 231 | resolution: {integrity: sha512-HGyRFOJNhAmHVX+PY8xb46TdCMWrDQDcnUBPlalZAjSmfckcaxbUkqdufd4nyfWITSrshXTzfJCu1Iww9CDhfg==} 232 | engines: {node: '>=22.9.0'} 233 | 234 | '@overextended/ox_lib@3.30.5': 235 | resolution: {integrity: sha512-aXPtICx4UGEiOmp8+DS5AW1bF2hE7F0Ae4ssgpsQOv+4eUv+UWcAyh0NYE7RQ3lXWV58US4cyPVYl4DWdkGfAA==} 236 | 237 | '@prisma/client@6.13.0': 238 | resolution: {integrity: sha512-8m2+I3dQovkV8CkDMluiwEV1TxV9EXdT6xaCz39O6jYw7mkf5gwfmi+cL4LJsEPwz5tG7sreBwkRpEMJedGYUQ==} 239 | engines: {node: '>=18.18'} 240 | peerDependencies: 241 | prisma: '*' 242 | typescript: '>=5.1.0' 243 | peerDependenciesMeta: 244 | prisma: 245 | optional: true 246 | typescript: 247 | optional: true 248 | 249 | '@prisma/config@6.13.0': 250 | resolution: {integrity: sha512-OYMM+pcrvj/NqNWCGESSxVG3O7kX6oWuGyvufTUNnDw740KIQvNyA4v0eILgkpuwsKIDU36beZCkUtIt0naTog==} 251 | 252 | '@prisma/debug@6.13.0': 253 | resolution: {integrity: sha512-um+9pfKJW0ihmM83id9FXGi5qEbVJ0Vxi1Gm0xpYsjwUBnw6s2LdPBbrsG9QXRX46K4CLWCTNvskXBup4i9hlw==} 254 | 255 | '@prisma/engines-version@6.13.0-35.361e86d0ea4987e9f53a565309b3eed797a6bcbd': 256 | resolution: {integrity: sha512-MpPyKSzBX7P/ZY9odp9TSegnS/yH3CSbchQE9f0yBg3l2QyN59I6vGXcoYcqKC9VTniS1s18AMmhyr1OWavjHg==} 257 | 258 | '@prisma/engines@6.13.0': 259 | resolution: {integrity: sha512-D+1B79LFvtWA0KTt8ALekQ6A/glB9w10ETknH5Y9g1k2NYYQOQy93ffiuqLn3Pl6IPJG3EsK/YMROKEaq8KBrA==} 260 | 261 | '@prisma/fetch-engine@6.13.0': 262 | resolution: {integrity: sha512-grmmq+4FeFKmaaytA8Ozc2+Tf3BC8xn/DVJos6LL022mfRlMZYjT3hZM0/xG7+5fO95zFG9CkDUs0m1S2rXs5Q==} 263 | 264 | '@prisma/get-platform@6.13.0': 265 | resolution: {integrity: sha512-Nii2pX50fY4QKKxQwm7/vvqT6Ku8yYJLZAFX4e2vzHwRdMqjugcOG5hOSLjxqoXb0cvOspV70TOhMzrw8kqAnw==} 266 | 267 | '@standard-schema/spec@1.0.0': 268 | resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} 269 | 270 | '@types/geojson@7946.0.16': 271 | resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} 272 | 273 | '@types/node@22.13.13': 274 | resolution: {integrity: sha512-ClsL5nMwKaBRwPcCvH8E7+nU4GxHVx1axNvMZTFHMEfNI7oahimt26P5zjVCRrjiIWj6YFXfE1v3dEp94wLcGQ==} 275 | 276 | '@types/normalize-package-data@2.4.4': 277 | resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} 278 | 279 | c12@3.1.0: 280 | resolution: {integrity: sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==} 281 | peerDependencies: 282 | magicast: ^0.3.5 283 | peerDependenciesMeta: 284 | magicast: 285 | optional: true 286 | 287 | chokidar@4.0.3: 288 | resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} 289 | engines: {node: '>= 14.16.0'} 290 | 291 | citty@0.1.6: 292 | resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} 293 | 294 | confbox@0.2.2: 295 | resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} 296 | 297 | consola@3.4.2: 298 | resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} 299 | engines: {node: ^14.18.0 || >=16.10.0} 300 | 301 | csstype@3.1.3: 302 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 303 | 304 | data-uri-to-buffer@4.0.1: 305 | resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} 306 | engines: {node: '>= 12'} 307 | 308 | deepmerge-ts@7.1.5: 309 | resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==} 310 | engines: {node: '>=16.0.0'} 311 | 312 | defu@6.1.4: 313 | resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} 314 | 315 | denque@2.1.0: 316 | resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} 317 | engines: {node: '>=0.10'} 318 | 319 | destr@2.0.5: 320 | resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} 321 | 322 | dotenv@16.6.1: 323 | resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} 324 | engines: {node: '>=12'} 325 | 326 | effect@3.16.12: 327 | resolution: {integrity: sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg==} 328 | 329 | esbuild@0.25.1: 330 | resolution: {integrity: sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==} 331 | engines: {node: '>=18'} 332 | hasBin: true 333 | 334 | exsolve@1.0.7: 335 | resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} 336 | 337 | fast-check@3.23.2: 338 | resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} 339 | engines: {node: '>=8.0.0'} 340 | 341 | fast-printf@1.6.10: 342 | resolution: {integrity: sha512-GwTgG9O4FVIdShhbVF3JxOgSBY2+ePGsu2V/UONgoCPzF9VY6ZdBMKsHKCYQHZwNk3qNouUolRDsgVxcVA5G1w==} 343 | engines: {node: '>=10.0'} 344 | 345 | fetch-blob@3.2.0: 346 | resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} 347 | engines: {node: ^12.20 || >= 14.13} 348 | 349 | find-up-simple@1.0.1: 350 | resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} 351 | engines: {node: '>=18'} 352 | 353 | formdata-polyfill@4.0.10: 354 | resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} 355 | engines: {node: '>=12.20.0'} 356 | 357 | giget@2.0.0: 358 | resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==} 359 | hasBin: true 360 | 361 | hosted-git-info@7.0.2: 362 | resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} 363 | engines: {node: ^16.14.0 || >=18.0.0} 364 | 365 | iconv-lite@0.6.3: 366 | resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} 367 | engines: {node: '>=0.10.0'} 368 | 369 | index-to-position@1.1.0: 370 | resolution: {integrity: sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==} 371 | engines: {node: '>=18'} 372 | 373 | jiti@2.5.1: 374 | resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} 375 | hasBin: true 376 | 377 | js-tokens@4.0.0: 378 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 379 | 380 | lru-cache@10.4.3: 381 | resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} 382 | 383 | mariadb@3.4.0: 384 | resolution: {integrity: sha512-hdRPcAzs+MTxK5VG1thBW18gGTlw6yWBe9YnLB65GLo7q0fO5DWsgomIevV/pXSaWRmD3qi6ka4oSFRTExRiEQ==} 385 | engines: {node: '>= 14'} 386 | 387 | node-domexception@1.0.0: 388 | resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} 389 | engines: {node: '>=10.5.0'} 390 | 391 | node-fetch-native@1.6.7: 392 | resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} 393 | 394 | node-fetch@3.3.2: 395 | resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} 396 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 397 | 398 | normalize-package-data@6.0.2: 399 | resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} 400 | engines: {node: ^16.14.0 || >=18.0.0} 401 | 402 | nypm@0.6.1: 403 | resolution: {integrity: sha512-hlacBiRiv1k9hZFiphPUkfSQ/ZfQzZDzC+8z0wL3lvDAOUu/2NnChkKuMoMjNur/9OpKuz2QsIeiPVN0xM5Q0w==} 404 | engines: {node: ^14.16.0 || >=16.10.0} 405 | hasBin: true 406 | 407 | ohash@2.0.11: 408 | resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} 409 | 410 | parse-json@8.3.0: 411 | resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} 412 | engines: {node: '>=18'} 413 | 414 | pathe@2.0.3: 415 | resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} 416 | 417 | perfect-debounce@1.0.0: 418 | resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} 419 | 420 | picocolors@1.1.1: 421 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 422 | 423 | pkg-types@2.2.0: 424 | resolution: {integrity: sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==} 425 | 426 | prisma@6.13.0: 427 | resolution: {integrity: sha512-dfzORf0AbcEyyzxuv2lEwG8g+WRGF/qDQTpHf/6JoHsyF5MyzCEZwClVaEmw3WXcobgadosOboKUgQU0kFs9kw==} 428 | engines: {node: '>=18.18'} 429 | hasBin: true 430 | peerDependencies: 431 | typescript: '>=5.1.0' 432 | peerDependenciesMeta: 433 | typescript: 434 | optional: true 435 | 436 | pure-rand@6.1.0: 437 | resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} 438 | 439 | rc9@2.1.2: 440 | resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} 441 | 442 | read-package-up@11.0.0: 443 | resolution: {integrity: sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==} 444 | engines: {node: '>=18'} 445 | 446 | read-pkg@9.0.1: 447 | resolution: {integrity: sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==} 448 | engines: {node: '>=18'} 449 | 450 | readdirp@4.1.2: 451 | resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} 452 | engines: {node: '>= 14.18.0'} 453 | 454 | safer-buffer@2.1.2: 455 | resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} 456 | 457 | semver@7.7.2: 458 | resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} 459 | engines: {node: '>=10'} 460 | hasBin: true 461 | 462 | spdx-correct@3.2.0: 463 | resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} 464 | 465 | spdx-exceptions@2.5.0: 466 | resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} 467 | 468 | spdx-expression-parse@3.0.1: 469 | resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} 470 | 471 | spdx-license-ids@3.0.22: 472 | resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} 473 | 474 | tinyexec@1.0.1: 475 | resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} 476 | 477 | type-fest@4.41.0: 478 | resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} 479 | engines: {node: '>=16'} 480 | 481 | typescript@5.8.2: 482 | resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} 483 | engines: {node: '>=14.17'} 484 | hasBin: true 485 | 486 | undici-types@6.20.0: 487 | resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} 488 | 489 | unicorn-magic@0.1.0: 490 | resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} 491 | engines: {node: '>=18'} 492 | 493 | validate-npm-package-license@3.0.4: 494 | resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} 495 | 496 | web-streams-polyfill@3.3.3: 497 | resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} 498 | engines: {node: '>= 8'} 499 | 500 | snapshots: 501 | 502 | '@babel/code-frame@7.27.1': 503 | dependencies: 504 | '@babel/helper-validator-identifier': 7.27.1 505 | js-tokens: 4.0.0 506 | picocolors: 1.1.1 507 | 508 | '@babel/helper-validator-identifier@7.27.1': {} 509 | 510 | '@citizenfx/client@2.0.13796-1': {} 511 | 512 | '@citizenfx/server@2.0.13796-1': {} 513 | 514 | '@esbuild/aix-ppc64@0.25.1': 515 | optional: true 516 | 517 | '@esbuild/android-arm64@0.25.1': 518 | optional: true 519 | 520 | '@esbuild/android-arm@0.25.1': 521 | optional: true 522 | 523 | '@esbuild/android-x64@0.25.1': 524 | optional: true 525 | 526 | '@esbuild/darwin-arm64@0.25.1': 527 | optional: true 528 | 529 | '@esbuild/darwin-x64@0.25.1': 530 | optional: true 531 | 532 | '@esbuild/freebsd-arm64@0.25.1': 533 | optional: true 534 | 535 | '@esbuild/freebsd-x64@0.25.1': 536 | optional: true 537 | 538 | '@esbuild/linux-arm64@0.25.1': 539 | optional: true 540 | 541 | '@esbuild/linux-arm@0.25.1': 542 | optional: true 543 | 544 | '@esbuild/linux-ia32@0.25.1': 545 | optional: true 546 | 547 | '@esbuild/linux-loong64@0.25.1': 548 | optional: true 549 | 550 | '@esbuild/linux-mips64el@0.25.1': 551 | optional: true 552 | 553 | '@esbuild/linux-ppc64@0.25.1': 554 | optional: true 555 | 556 | '@esbuild/linux-riscv64@0.25.1': 557 | optional: true 558 | 559 | '@esbuild/linux-s390x@0.25.1': 560 | optional: true 561 | 562 | '@esbuild/linux-x64@0.25.1': 563 | optional: true 564 | 565 | '@esbuild/netbsd-arm64@0.25.1': 566 | optional: true 567 | 568 | '@esbuild/netbsd-x64@0.25.1': 569 | optional: true 570 | 571 | '@esbuild/openbsd-arm64@0.25.1': 572 | optional: true 573 | 574 | '@esbuild/openbsd-x64@0.25.1': 575 | optional: true 576 | 577 | '@esbuild/sunos-x64@0.25.1': 578 | optional: true 579 | 580 | '@esbuild/win32-arm64@0.25.1': 581 | optional: true 582 | 583 | '@esbuild/win32-ia32@0.25.1': 584 | optional: true 585 | 586 | '@esbuild/win32-x64@0.25.1': 587 | optional: true 588 | 589 | '@nativewrappers/client@1.7.33': {} 590 | 591 | '@nativewrappers/common@0.0.56': {} 592 | 593 | '@nativewrappers/fivem@0.0.56': 594 | dependencies: 595 | '@nativewrappers/common': 0.0.56 596 | 597 | '@nativewrappers/fivem@0.0.90': {} 598 | 599 | '@nativewrappers/server@0.0.56': 600 | dependencies: 601 | '@nativewrappers/common': 0.0.56 602 | 603 | '@nativewrappers/server@0.0.90': {} 604 | 605 | '@overextended/ox_core@1.5.1': 606 | dependencies: 607 | '@nativewrappers/fivem': 0.0.56 608 | '@nativewrappers/server': 0.0.56 609 | '@overextended/ox_lib': 3.30.5 610 | mariadb: 3.4.0 611 | 612 | '@overextended/ox_lib@3.30.5': 613 | dependencies: 614 | '@nativewrappers/client': 1.7.33 615 | csstype: 3.1.3 616 | fast-printf: 1.6.10 617 | typescript: 5.8.2 618 | 619 | '@prisma/client@6.13.0(prisma@6.13.0(typescript@5.8.2))(typescript@5.8.2)': 620 | optionalDependencies: 621 | prisma: 6.13.0(typescript@5.8.2) 622 | typescript: 5.8.2 623 | 624 | '@prisma/config@6.13.0': 625 | dependencies: 626 | c12: 3.1.0 627 | deepmerge-ts: 7.1.5 628 | effect: 3.16.12 629 | read-package-up: 11.0.0 630 | transitivePeerDependencies: 631 | - magicast 632 | 633 | '@prisma/debug@6.13.0': {} 634 | 635 | '@prisma/engines-version@6.13.0-35.361e86d0ea4987e9f53a565309b3eed797a6bcbd': {} 636 | 637 | '@prisma/engines@6.13.0': 638 | dependencies: 639 | '@prisma/debug': 6.13.0 640 | '@prisma/engines-version': 6.13.0-35.361e86d0ea4987e9f53a565309b3eed797a6bcbd 641 | '@prisma/fetch-engine': 6.13.0 642 | '@prisma/get-platform': 6.13.0 643 | 644 | '@prisma/fetch-engine@6.13.0': 645 | dependencies: 646 | '@prisma/debug': 6.13.0 647 | '@prisma/engines-version': 6.13.0-35.361e86d0ea4987e9f53a565309b3eed797a6bcbd 648 | '@prisma/get-platform': 6.13.0 649 | 650 | '@prisma/get-platform@6.13.0': 651 | dependencies: 652 | '@prisma/debug': 6.13.0 653 | 654 | '@standard-schema/spec@1.0.0': {} 655 | 656 | '@types/geojson@7946.0.16': {} 657 | 658 | '@types/node@22.13.13': 659 | dependencies: 660 | undici-types: 6.20.0 661 | 662 | '@types/normalize-package-data@2.4.4': {} 663 | 664 | c12@3.1.0: 665 | dependencies: 666 | chokidar: 4.0.3 667 | confbox: 0.2.2 668 | defu: 6.1.4 669 | dotenv: 16.6.1 670 | exsolve: 1.0.7 671 | giget: 2.0.0 672 | jiti: 2.5.1 673 | ohash: 2.0.11 674 | pathe: 2.0.3 675 | perfect-debounce: 1.0.0 676 | pkg-types: 2.2.0 677 | rc9: 2.1.2 678 | 679 | chokidar@4.0.3: 680 | dependencies: 681 | readdirp: 4.1.2 682 | 683 | citty@0.1.6: 684 | dependencies: 685 | consola: 3.4.2 686 | 687 | confbox@0.2.2: {} 688 | 689 | consola@3.4.2: {} 690 | 691 | csstype@3.1.3: {} 692 | 693 | data-uri-to-buffer@4.0.1: {} 694 | 695 | deepmerge-ts@7.1.5: {} 696 | 697 | defu@6.1.4: {} 698 | 699 | denque@2.1.0: {} 700 | 701 | destr@2.0.5: {} 702 | 703 | dotenv@16.6.1: {} 704 | 705 | effect@3.16.12: 706 | dependencies: 707 | '@standard-schema/spec': 1.0.0 708 | fast-check: 3.23.2 709 | 710 | esbuild@0.25.1: 711 | optionalDependencies: 712 | '@esbuild/aix-ppc64': 0.25.1 713 | '@esbuild/android-arm': 0.25.1 714 | '@esbuild/android-arm64': 0.25.1 715 | '@esbuild/android-x64': 0.25.1 716 | '@esbuild/darwin-arm64': 0.25.1 717 | '@esbuild/darwin-x64': 0.25.1 718 | '@esbuild/freebsd-arm64': 0.25.1 719 | '@esbuild/freebsd-x64': 0.25.1 720 | '@esbuild/linux-arm': 0.25.1 721 | '@esbuild/linux-arm64': 0.25.1 722 | '@esbuild/linux-ia32': 0.25.1 723 | '@esbuild/linux-loong64': 0.25.1 724 | '@esbuild/linux-mips64el': 0.25.1 725 | '@esbuild/linux-ppc64': 0.25.1 726 | '@esbuild/linux-riscv64': 0.25.1 727 | '@esbuild/linux-s390x': 0.25.1 728 | '@esbuild/linux-x64': 0.25.1 729 | '@esbuild/netbsd-arm64': 0.25.1 730 | '@esbuild/netbsd-x64': 0.25.1 731 | '@esbuild/openbsd-arm64': 0.25.1 732 | '@esbuild/openbsd-x64': 0.25.1 733 | '@esbuild/sunos-x64': 0.25.1 734 | '@esbuild/win32-arm64': 0.25.1 735 | '@esbuild/win32-ia32': 0.25.1 736 | '@esbuild/win32-x64': 0.25.1 737 | 738 | exsolve@1.0.7: {} 739 | 740 | fast-check@3.23.2: 741 | dependencies: 742 | pure-rand: 6.1.0 743 | 744 | fast-printf@1.6.10: {} 745 | 746 | fetch-blob@3.2.0: 747 | dependencies: 748 | node-domexception: 1.0.0 749 | web-streams-polyfill: 3.3.3 750 | 751 | find-up-simple@1.0.1: {} 752 | 753 | formdata-polyfill@4.0.10: 754 | dependencies: 755 | fetch-blob: 3.2.0 756 | 757 | giget@2.0.0: 758 | dependencies: 759 | citty: 0.1.6 760 | consola: 3.4.2 761 | defu: 6.1.4 762 | node-fetch-native: 1.6.7 763 | nypm: 0.6.1 764 | pathe: 2.0.3 765 | 766 | hosted-git-info@7.0.2: 767 | dependencies: 768 | lru-cache: 10.4.3 769 | 770 | iconv-lite@0.6.3: 771 | dependencies: 772 | safer-buffer: 2.1.2 773 | 774 | index-to-position@1.1.0: {} 775 | 776 | jiti@2.5.1: {} 777 | 778 | js-tokens@4.0.0: {} 779 | 780 | lru-cache@10.4.3: {} 781 | 782 | mariadb@3.4.0: 783 | dependencies: 784 | '@types/geojson': 7946.0.16 785 | '@types/node': 22.13.13 786 | denque: 2.1.0 787 | iconv-lite: 0.6.3 788 | lru-cache: 10.4.3 789 | 790 | node-domexception@1.0.0: {} 791 | 792 | node-fetch-native@1.6.7: {} 793 | 794 | node-fetch@3.3.2: 795 | dependencies: 796 | data-uri-to-buffer: 4.0.1 797 | fetch-blob: 3.2.0 798 | formdata-polyfill: 4.0.10 799 | 800 | normalize-package-data@6.0.2: 801 | dependencies: 802 | hosted-git-info: 7.0.2 803 | semver: 7.7.2 804 | validate-npm-package-license: 3.0.4 805 | 806 | nypm@0.6.1: 807 | dependencies: 808 | citty: 0.1.6 809 | consola: 3.4.2 810 | pathe: 2.0.3 811 | pkg-types: 2.2.0 812 | tinyexec: 1.0.1 813 | 814 | ohash@2.0.11: {} 815 | 816 | parse-json@8.3.0: 817 | dependencies: 818 | '@babel/code-frame': 7.27.1 819 | index-to-position: 1.1.0 820 | type-fest: 4.41.0 821 | 822 | pathe@2.0.3: {} 823 | 824 | perfect-debounce@1.0.0: {} 825 | 826 | picocolors@1.1.1: {} 827 | 828 | pkg-types@2.2.0: 829 | dependencies: 830 | confbox: 0.2.2 831 | exsolve: 1.0.7 832 | pathe: 2.0.3 833 | 834 | prisma@6.13.0(typescript@5.8.2): 835 | dependencies: 836 | '@prisma/config': 6.13.0 837 | '@prisma/engines': 6.13.0 838 | optionalDependencies: 839 | typescript: 5.8.2 840 | transitivePeerDependencies: 841 | - magicast 842 | 843 | pure-rand@6.1.0: {} 844 | 845 | rc9@2.1.2: 846 | dependencies: 847 | defu: 6.1.4 848 | destr: 2.0.5 849 | 850 | read-package-up@11.0.0: 851 | dependencies: 852 | find-up-simple: 1.0.1 853 | read-pkg: 9.0.1 854 | type-fest: 4.41.0 855 | 856 | read-pkg@9.0.1: 857 | dependencies: 858 | '@types/normalize-package-data': 2.4.4 859 | normalize-package-data: 6.0.2 860 | parse-json: 8.3.0 861 | type-fest: 4.41.0 862 | unicorn-magic: 0.1.0 863 | 864 | readdirp@4.1.2: {} 865 | 866 | safer-buffer@2.1.2: {} 867 | 868 | semver@7.7.2: {} 869 | 870 | spdx-correct@3.2.0: 871 | dependencies: 872 | spdx-expression-parse: 3.0.1 873 | spdx-license-ids: 3.0.22 874 | 875 | spdx-exceptions@2.5.0: {} 876 | 877 | spdx-expression-parse@3.0.1: 878 | dependencies: 879 | spdx-exceptions: 2.5.0 880 | spdx-license-ids: 3.0.22 881 | 882 | spdx-license-ids@3.0.22: {} 883 | 884 | tinyexec@1.0.1: {} 885 | 886 | type-fest@4.41.0: {} 887 | 888 | typescript@5.8.2: {} 889 | 890 | undici-types@6.20.0: {} 891 | 892 | unicorn-magic@0.1.0: {} 893 | 894 | validate-npm-package-license@3.0.4: 895 | dependencies: 896 | spdx-correct: 3.2.0 897 | spdx-expression-parse: 3.0.1 898 | 899 | web-streams-polyfill@3.3.3: {} 900 | --------------------------------------------------------------------------------