Compare commits

...
5 Commits
Author SHA1 Message Date
rilaxik fd9f1cab18 upd core
- added zod integration (validation) for testing
2024-11-01 13:20:07 +00:00
rilaxik 21ca123797 upd core
- specified exact exports for further compatibility
2024-11-01 13:19:43 +00:00
rilaxik 495217ecd5 upd cli
- handled process exit
- updated docs + readme
- typos fix
2024-10-30 18:31:53 +00:00
rilaxik 7e43e0d106 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	cli/package.json
#	rpc/package.json
2024-10-30 17:01:42 +00:00
rilaxik 2f1d1d9f0d hotfix
- licenses
- package.json consistency
- links bump
2024-10-30 17:00:36 +00:00
32 changed files with 6579 additions and 8610 deletions
BIN
View File
Binary file not shown.
+5 -1
View File
@@ -8,11 +8,15 @@
"readme.md", "readme.md",
"LICENSE" "LICENSE"
], ],
"exports": {
".": "./dist/index.js"
},
"scripts": { "scripts": {
"build": "tsup" "build": "tsup"
}, },
"dependencies": { "dependencies": {
"@entityseven/rage-fw-rpc": "0.2.5" "@entityseven/rage-fw-rpc": "0.2.5",
"zod": "^3.23.8"
}, },
"peerDependencies": { "peerDependencies": {
"@entityseven/rage-fw-shared-types": "0.2.0", "@entityseven/rage-fw-shared-types": "0.2.0",
+19 -8
View File
@@ -1,11 +1,7 @@
import { Helper } from './helper'
import { rpc } from './rpc' import { rpc } from './rpc'
import { Helper } from './helper'
import { Validation } from './validation'
import type * as T from '../types' import type * as T from '../types'
import {
RageFW_BrowserEvent,
RageFW_ClientEvent,
RageFW_ServerEvent,
} from '../types'
/** Browser-side interactions */ /** Browser-side interactions */
export class Browser extends Helper { export class Browser extends Helper {
@@ -34,6 +30,8 @@ export class Browser extends Helper {
* *
* @param eventName - The name of the event to register * @param eventName - The name of the event to register
* @param callback - The callback function to be executed when the event is triggered * @param callback - The callback function to be executed when the event is triggered
* @param [options] - Optional settings for callback execution
* @param [options.validation] - Validation schema to be checked before the callback executes
* @returns {Browser} The current browser instance, enabling method chaining * @returns {Browser} The current browser instance, enabling method chaining
* *
* @example * @example
@@ -47,14 +45,27 @@ export class Browser extends Helper {
public register<EventName extends T.RageFW_BrowserEvent>( public register<EventName extends T.RageFW_BrowserEvent>(
eventName: EventName, eventName: EventName,
callback: T.RageFW_BrowserCallback<EventName>, callback: T.RageFW_BrowserCallback<EventName>,
options?: {
validation?: T.RageFW_ValidationOptions
},
): Browser { ): Browser {
this.log_('register', eventName, callback) this.log_('register', eventName, callback)
rpc.register< rpc.register<
Parameters<typeof callback>, Parameters<typeof callback>,
ReturnType<typeof callback>, ReturnType<typeof callback> | Promise<unknown>,
EventName EventName
>(eventName, async (...data) => await callback(...data)) >(eventName, async (...data) => {
if (!options?.validation) return await callback(...data)
const validationSuccess = Validation.process(
data,
options?.validation,
)
if (!validationSuccess) return
return await callback(...data)
})
return this return this
} }
+26
View File
@@ -0,0 +1,26 @@
import type * as T from '../types'
export class Validation {
constructor() {}
public static process<EventName extends T.RageFW_BrowserEvent>(
args: T.RageFW_BrowserArgs<EventName>,
validationOptions?: T.RageFW_ValidationOptions,
): boolean {
if (!validationOptions) return true
if ('schema' in validationOptions) {
const validationResponse = validationOptions.schema.safeParse(args)
if (validationResponse.success) {
return true
} else {
validationOptions.onError(validationResponse.error)
return false
}
} else {
const validationResponse = validationOptions.safeParse(args)
return validationResponse.success
}
}
}
+1
View File
@@ -1,3 +1,4 @@
export * from './browser' export * from './browser'
export * from './client' export * from './client'
export * from './server' export * from './server'
export * from './validation'
+11
View File
@@ -0,0 +1,11 @@
import type { z } from 'zod'
export type RageFW_ValidationSchema = z.ZodTuple<any, any> | z.ZodArray<any>
export type RageFW_ValidationSchemaExtended = {
schema: RageFW_ValidationSchema
onError: (error: z.ZodError) => unknown
}
export type RageFW_ValidationOptions =
| RageFW_ValidationSchema
| RageFW_ValidationSchemaExtended
-1
View File
@@ -4,7 +4,6 @@ export default defineConfig({
entry: ['src/index.ts'], entry: ['src/index.ts'],
outDir: './dist', outDir: './dist',
format: ['cjs'], format: ['cjs'],
noExternal: ['rage-rpc'],
experimentalDts: true, experimentalDts: true,
splitting: false, splitting: false,
sourcemap: false, sourcemap: false,
+3 -2
View File
@@ -1,13 +1,14 @@
{ {
"name": "@entityseven/create-rage-fw", "name": "@entityseven/create-rage-fw",
"version": "0.1.1", "version": "0.1.2",
"bin": { "bin": {
"rage-fw": "dist/index.js" "rage-fw": "dist/index.js"
}, },
"main": "dist/index.js", "main": "dist/index.js",
"files": [ "files": [
"dist/**/*", "dist/**/*",
"readme.md" "readme.md",
"LICENSE"
], ],
"scripts": { "scripts": {
"watch": "tsc -w", "watch": "tsc -w",
+26 -14
View File
@@ -1,29 +1,41 @@
# RageFW CLI To make you life easier while using RageFW we created a basic CLI. At the moment automation we have only works via [pnpm](https://pnpm.io/) and [bun](https://bun.sh/)
To make you life easier while using RageFW we created a basic CLI. At the moment automation we have only works via [pnpm](https://pnpm.io/)
``pnpm create @entityseven/rage-fw@latest`` ``pnpm create @entityseven/rage-fw@latest``
## TL;DR ``bun create @entityseven/rage-fw@latest``
# TL;DR
- ``Initialize new project`` - create new template project - ``Initialize new project`` - create new template project
- ``Test our RPC`` - scaffold an example for ``@entityseven/rage-fw-rpc``
- ``Install RAGE:MP updater`` - download and update RAGE:MP server files - ``Install RAGE:MP updater`` - download and update RAGE:MP server files
## Options # Options
For now, you will see a few available options. They are described in detail below For now, you will see a few available options. They are described in detail below
- ``Initialize new project`` - ``Initialize new project``
- ``Test our RPC``
- ``Install RAGE:MP updater`` - ``Install RAGE:MP updater``
### Initialize new project ## Initialize new project
Using this options will forward you to common project-creation menu Using this option will forward you to common project creation menu
- ``Enter project name``
This option will specify a name for your project which is used as a folder name too. Defaults to **rage-fw** - ``Enter project name``
This option will specify a name for your project which is used as a folder name too. Defaults to **rage-fw-example**
- ``Select frontend`` - ``Select front-end``
Use selector menu to choose which front-end framework you want to use. We will do our best to expand this menu with various solutions
Use this selector menu to choose which frontend framework you want to use. We will do our best to expand this menu after some time. ## Test our RPC
Defaults to **React + TypeScript (Vite)** Using this option will forward you to common project creation menu
### Install Rage:MP updater - ``Enter project name``
This option will simplify installation process of Rage:MP server files required to start your server This option will specify a name for your project which is used as a folder name too. Defaults to **rage-fw-rpc-example**
- ``Select front-end``
Use selector menu to choose which front-end framework you want to use. We will do our best to expand this menu with various solutions
## Install Rage:MP updater
This option will simplify installation and update process of Rage:MP server files required to start your server
# Contribution
If you wish to help us in expanding examples selection with different framework you are very welcome to open PRs and Issues
+6 -5
View File
@@ -18,19 +18,19 @@ export async function initProject() {
if (!framework) { if (!framework) {
framework = await select({ framework = await select({
message: c.gray('Select frontend:'), message: c.gray('Select front-end:'),
default: 'react-18', default: 'react-18',
loop: true, loop: true,
choices: [ choices: [
{ {
name: 'React + TypeScript (Vite)', name: 'React 18',
value: 'react-18', value: 'react-18',
description: 'React + TypeScript (Vite) as a frontend', description: 'React 18 + TypeScript (Vite) as a front-end',
}, },
], ],
}) })
} else { } else {
console.log(c.gray('Frontend:'), framework) console.log(c.gray('Front-end:'), framework)
} }
console.log( console.log(
@@ -38,7 +38,7 @@ export async function initProject() {
folder, folder,
c.gray('with'), c.gray('with'),
framework, framework,
c.gray('as a frontend..'), c.gray('as a front-end..'),
) )
cloneBranch( cloneBranch(
@@ -56,5 +56,6 @@ export async function initProject() {
}) })
.catch(e => { .catch(e => {
console.log(c.red('Error occured: \n', e)) console.log(c.red('Error occured: \n', e))
console.log(c.red('Please open an issue if you see this'))
}) })
} }
+7 -6
View File
@@ -5,14 +5,14 @@ import { cloneBranch } from '../utils/cloner'
const choices = { const choices = {
'rpc-react-18': { 'rpc-react-18': {
name: 'Vite + React 18 + TypeScript', name: 'React 18',
value: 'rpc-react-18', value: 'rpc-react-18',
description: 'Vite + React 18 + TypeScript as a frontend', description: 'Vite + React 18 + TypeScript as a front-end',
}, },
'rpc-svelte-5': { 'rpc-svelte-5': {
name: 'Vite + Svelte 5 + TypeScript', name: 'Svelte 5',
value: 'rpc-svelte-5', value: 'rpc-svelte-5',
description: 'Vite + Svelte 5 + TypeScript as a frontend', description: 'Vite + Svelte 5 + TypeScript as a front-end',
}, },
} as const } as const
@@ -31,13 +31,13 @@ export async function testRpc() {
if (!framework) { if (!framework) {
framework = await select({ framework = await select({
message: c.gray('Select frontend:'), message: c.gray('Select front-end:'),
default: 'rpc-react-18', default: 'rpc-react-18',
loop: true, loop: true,
choices: Object.values(choices), choices: Object.values(choices),
}) })
} else { } else {
console.log(c.gray('Frontend:'), framework) console.log(c.gray('Front-end:'), framework)
} }
console.log( console.log(
@@ -63,5 +63,6 @@ export async function testRpc() {
}) })
.catch(e => { .catch(e => {
console.log(c.red('Error occured: \n', e)) console.log(c.red('Error occured: \n', e))
console.log(c.red('Please open an issue if you see this'))
}) })
} }
+14 -4
View File
@@ -12,10 +12,19 @@ enum Actions {
UPDATER = 'UPDATER', UPDATER = 'UPDATER',
} }
process.on('exit', () => {
console.log(c.blueBright('\n\nRage FW CLI | Exiting..'))
process.exit(0)
})
process.on('SIGINT', () => {
console.log(c.blueBright('\n\nRage FW CLI | Exiting..'))
process.exit(0)
})
;(async () => { ;(async () => {
await checkForUpdates() await checkForUpdates()
console.log(c.blueBright('Rage FW CLI | Powered by Entity Seven Group ️<3')) console.log(c.blueBright('Rage FW CLI | Powered by Entity Seven Group <3'))
const action = await select({ const action = await select({
message: c.gray('Select action:'), message: c.gray('Select action:'),
@@ -26,16 +35,16 @@ enum Actions {
description: 'Initialize a new project and start developing', description: 'Initialize a new project and start developing',
}, },
{ {
name: 'Test our RPC', name: 'Test our Rpc',
value: Actions.TEST_RPC, value: Actions.TEST_RPC,
description: description:
'Initialize a new skeleton project with our RPC set up', 'Initialize a new skeleton project with our Rpc all set',
}, },
{ {
name: 'Install RAGE:MP updater', name: 'Install RAGE:MP updater',
value: Actions.UPDATER, value: Actions.UPDATER,
description: description:
'Use our tool to download or update RAGE:MP server files in two clicks', 'Use our tool to download or update RAGE:MP server files in just two clicks',
}, },
], ],
loop: true, loop: true,
@@ -53,5 +62,6 @@ enum Actions {
break break
default: default:
console.log(c.red('Something went wrong..')) console.log(c.red('Something went wrong..'))
console.log(c.red('Please open an issue if you see this'))
} }
})() })()
BIN
View File
Binary file not shown.
+47 -35
View File
@@ -1,38 +1,50 @@
{ {
"name": "@entityseven/rage-fw-client", "name": "@entityseven/rage-fw-client",
"version": "0.2.0", "version": "0.2.0",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/src/index.d.ts", "types": "dist/src/index.d.ts",
"files": [ "files": [
"dist/**/*", "dist/**/*",
"readme.md", "readme.md",
"LICENSE" "LICENSE"
], ],
"scripts": { "exports": {
"build": "tsup" ".": "./dist/index.js"
},
"scripts": {
"build": "tsup"
},
"dependencies": {
"@entityseven/rage-fw-rpc": "0.2.5",
"zod": "^3.23.8"
},
"peerDependencies": {
"@entityseven/rage-fw-shared-types": "0.2.0",
"@ragempcommunity/types-client": "^2.1.8"
},
"description": "Package used on a client-side of your Rage:MP Server",
"keywords": [
"rage-fw-client",
"ragefw",
"rage-fw",
"ragemp",
"rage:mp",
"rage",
"gta5"
],
"author": "Entity Seven Group",
"contributors": [
{
"name": "Danya H",
"email": "[email protected]",
"url": "https://github.com/rilaxik/"
}, },
"dependencies": { {
"@entityseven/rage-fw-rpc": "0.2.5" "name": "Oleksandr Honcharov",
}, "email": "[email protected]",
"peerDependencies": { "url": "https://github.com/SashaGoncharov19/"
"@entityseven/rage-fw-shared-types": "0.2.0", }
"@ragempcommunity/types-client": "^2.1.8" ],
}, "license": "Custom-Attribution-NoDerivs",
"description": "Package used on a client-side of your Rage:MP Server", "gitHead": "ffd542c1deddb3033e16e0dae7557313ae09b05f"
"keywords": ["rage-fw-client", "ragefw", "rage-fw", "ragemp", "rage:mp", "rage", "gta5"],
"author": "Entity Seven Group",
"contributors": [
{
"name": "Danya H",
"email": "[email protected]",
"url": "https://github.com/rilaxik/"
},
{
"name": "Oleksandr Honcharov",
"email": "[email protected]",
"url": "https://github.com/SashaGoncharov19/"
}
],
"license": "Custom-Attribution-NoDerivs",
"gitHead": "ffd542c1deddb3033e16e0dae7557313ae09b05f"
} }
+18 -2
View File
@@ -1,5 +1,6 @@
import { rpc } from './rpc' import { rpc } from './rpc'
import { Middleware } from './middleware' import { Middleware } from './middleware'
import { Validation } from './validation'
import type * as T from '../types' import type * as T from '../types'
/** Client-side interactions */ /** Client-side interactions */
@@ -10,6 +11,7 @@ export class Client {
* @param eventName - The name of the event to register * @param eventName - The name of the event to register
* @param callback - The callback function to be executed when the event is triggered * @param callback - The callback function to be executed when the event is triggered
* @param [options] - Optional settings for callback execution * @param [options] - Optional settings for callback execution
* @param [options.validation] - Validation schema to be checked before the callback executes
* @param [options.middlewares] - Middleware functions to be checked before the callback executes * @param [options.middlewares] - Middleware functions to be checked before the callback executes
* @returns {Client} The current client instance, enabling method chaining * @returns {Client} The current client instance, enabling method chaining
* *
@@ -44,6 +46,7 @@ export class Client {
eventName: EventName, eventName: EventName,
callback: T.RageFW_ClientCallback<EventName>, callback: T.RageFW_ClientCallback<EventName>,
options?: { options?: {
validation?: T.RageFW_ValidationOptions
middlewares?: T.RageFW_MiddlewareOptions<EventName> middlewares?: T.RageFW_MiddlewareOptions<EventName>
}, },
): Client { ): Client {
@@ -52,9 +55,22 @@ export class Client {
ReturnType<typeof callback> | Promise<unknown>, ReturnType<typeof callback> | Promise<unknown>,
EventName EventName
>(eventName, async (...data) => { >(eventName, async (...data) => {
if (!options?.middlewares) return await callback(...data) if (!options?.middlewares && !options?.validation)
return await callback(...data)
await Middleware.process(options.middlewares, callback, data) const validationSuccess = Validation.process(
data,
options?.validation,
)
if (!validationSuccess) return
const middlewaresSuccess = await Middleware.process(
data,
options?.middlewares,
)
if (!middlewaresSuccess) return
return await callback(...data)
}) })
return this return this
-1
View File
@@ -1,5 +1,4 @@
export * from './client' export * from './client'
export * from './logger' export * from './logger'
export * from './middleware'
export * from './player' export * from './player'
export * from './rpc' export * from './rpc'
+10 -9
View File
@@ -4,8 +4,8 @@ export class Middleware {
constructor() {} constructor() {}
private static async execute<EventName extends T.RageFW_ClientEvent>( private static async execute<EventName extends T.RageFW_ClientEvent>(
middlewares: T.RageFW_MiddlewareFunction<EventName>[],
args: T.RageFW_ClientArgs<EventName>, args: T.RageFW_ClientArgs<EventName>,
middlewares: T.RageFW_MiddlewareFunction<EventName>[],
): Promise<T.RageFW_MiddlewareResponseInternal> { ): Promise<T.RageFW_MiddlewareResponseInternal> {
for (let i = 0; i < middlewares.length; i++) { for (let i = 0; i < middlewares.length; i++) {
const result = await middlewares[i](...args) const result = await middlewares[i](...args)
@@ -22,25 +22,25 @@ export class Middleware {
} }
public static async process<EventName extends T.RageFW_ClientEvent>( public static async process<EventName extends T.RageFW_ClientEvent>(
middlewareOptions: T.RageFW_MiddlewareOptions<EventName>,
callback: T.RageFW_ClientCallback<EventName>,
args: T.RageFW_ClientArgs<EventName>, args: T.RageFW_ClientArgs<EventName>,
) { middlewareOptions?: T.RageFW_MiddlewareOptions<EventName>,
): Promise<boolean> {
if (!middlewareOptions) return true
if (Array.isArray(middlewareOptions)) { if (Array.isArray(middlewareOptions)) {
const middlewaresResponse = await Middleware.execute( const middlewaresResponse = await Middleware.execute(
middlewareOptions,
args, args,
middlewareOptions,
) )
return middlewaresResponse.success
if (middlewaresResponse.success) return await callback(...args)
} else { } else {
const middlewaresResponse = await Middleware.execute( const middlewaresResponse = await Middleware.execute(
middlewareOptions.executables,
args, args,
middlewareOptions.executables,
) )
if (middlewaresResponse.success) { if (middlewaresResponse.success) {
return await callback(...args) return true
} else { } else {
middlewareOptions.onError( middlewareOptions.onError(
middlewaresResponse.message ?? middlewaresResponse.message ??
@@ -48,6 +48,7 @@ export class Middleware {
middlewaresResponse.id + middlewaresResponse.id +
' failed', ' failed',
) )
return false
} }
} }
} }
+26
View File
@@ -0,0 +1,26 @@
import type * as T from '../types'
export class Validation {
constructor() {}
public static process<EventName extends T.RageFW_ClientEvent>(
args: T.RageFW_ClientArgs<EventName>,
validationOptions?: T.RageFW_ValidationOptions,
): boolean {
if (!validationOptions) return true
if ('schema' in validationOptions) {
const validationResponse = validationOptions.schema.safeParse(args)
if (validationResponse.success) {
return true
} else {
validationOptions.onError(validationResponse.error)
return false
}
} else {
const validationResponse = validationOptions.safeParse(args)
return validationResponse.success
}
}
}
+1
View File
@@ -2,3 +2,4 @@ export * from './browser'
export * from './client' export * from './client'
export * from './middleware' export * from './middleware'
export * from './server' export * from './server'
export * from './validation'
+11
View File
@@ -0,0 +1,11 @@
import type { z } from 'zod'
export type RageFW_ValidationSchema = z.ZodTuple<any, any> | z.ZodArray<any>
export type RageFW_ValidationSchemaExtended = {
schema: RageFW_ValidationSchema
onError: (error: z.ZodError) => unknown
}
export type RageFW_ValidationOptions =
| RageFW_ValidationSchema
| RageFW_ValidationSchemaExtended
-1
View File
@@ -4,7 +4,6 @@ export default defineConfig({
entry: ['src/index.ts'], entry: ['src/index.ts'],
outDir: './dist', outDir: './dist',
format: ['cjs'], format: ['cjs'],
noExternal: ['rage-rpc'],
experimentalDts: true, experimentalDts: true,
splitting: false, splitting: false,
sourcemap: false, sourcemap: false,
+6227 -8464
View File
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -8,7 +8,9 @@
"start": "npx ./dist create" "start": "npx ./dist create"
}, },
"files": [ "files": [
"dist/**/*" "dist/**/*",
"readme.md",
"LICENSE"
], ],
"devDependencies": { "devDependencies": {
"@microsoft/api-extractor": "^7.47.9", "@microsoft/api-extractor": "^7.47.9",
+3 -3
View File
@@ -3,13 +3,13 @@ is an all-in package with asynchronous RPC implementation for RageMP servers in
# Installation # Installation
``` shell ``` shell
npm i rage-fw-rpc npm i @entityseven/rage-fw-rpc
``` ```
```shell ```shell
pnpm i rage-fw-rpc pnpm i @entityseven/rage-fw-rpc
``` ```
```shell ```shell
yarn add rage-fw-rpc yarn add @entityseven/rage-fw-rpc
``` ```
Import installed package and initialize rpc: Import installed package and initialize rpc:
+47 -35
View File
@@ -1,38 +1,50 @@
{ {
"name": "@entityseven/rage-fw-server", "name": "@entityseven/rage-fw-server",
"version": "0.2.0", "version": "0.2.0",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/src/index.d.ts", "types": "dist/src/index.d.ts",
"files": [ "files": [
"dist/**/*", "dist/**/*",
"readme.md", "readme.md",
"LICENSE" "LICENSE"
], ],
"scripts": { "exports": {
"build": "tsup" ".": "./dist/index.js"
},
"scripts": {
"build": "tsup"
},
"dependencies": {
"@entityseven/rage-fw-rpc": "0.2.5",
"zod": "^3.23.8"
},
"peerDependencies": {
"@entityseven/rage-fw-shared-types": "0.2.0",
"@ragempcommunity/types-server": "^2.1.8"
},
"description": "Package used on a server-side of your Rage:MP Server",
"keywords": [
"rage-fw-server",
"ragefw",
"rage-fw",
"ragemp",
"rage:mp",
"rage",
"gta5"
],
"author": "Entity Seven Group",
"contributors": [
{
"name": "Danya H",
"email": "[email protected]",
"url": "https://github.com/rilaxik/"
}, },
"dependencies": { {
"@entityseven/rage-fw-rpc": "0.2.5" "name": "Oleksandr Honcharov",
}, "email": "[email protected]",
"peerDependencies": { "url": "https://github.com/SashaGoncharov19/"
"@entityseven/rage-fw-shared-types": "0.2.0", }
"@ragempcommunity/types-server": "^2.1.8" ],
}, "license": "Custom-Attribution-NoDerivs",
"description": "Package used on a server-side of your Rage:MP Server", "gitHead": "ffd542c1deddb3033e16e0dae7557313ae09b05f"
"keywords": ["rage-fw-server", "ragefw", "rage-fw", "ragemp", "rage:mp", "rage", "gta5"],
"author": "Entity Seven Group",
"contributors": [
{
"name": "Danya H",
"email": "[email protected]",
"url": "https://github.com/rilaxik/"
},
{
"name": "Oleksandr Honcharov",
"email": "[email protected]",
"url": "https://github.com/SashaGoncharov19/"
}
],
"license": "Custom-Attribution-NoDerivs",
"gitHead": "ffd542c1deddb3033e16e0dae7557313ae09b05f"
} }
-1
View File
@@ -1,5 +1,4 @@
export * from './logger' export * from './logger'
export * from './middleware'
export * from './player' export * from './player'
export * from './rpc' export * from './rpc'
export * from './server' export * from './server'
+10 -9
View File
@@ -4,8 +4,8 @@ export class Middleware {
constructor() {} constructor() {}
private static async execute<EventName extends T.RageFW_ServerEvent>( private static async execute<EventName extends T.RageFW_ServerEvent>(
middlewares: T.RageFW_MiddlewareFunction<EventName>[],
args: T.RageFW_ServerArgs<EventName>, args: T.RageFW_ServerArgs<EventName>,
middlewares: T.RageFW_MiddlewareFunction<EventName>[],
): Promise<T.RageFW_MiddlewareResponseInternal> { ): Promise<T.RageFW_MiddlewareResponseInternal> {
for (let i = 0; i < middlewares.length; i++) { for (let i = 0; i < middlewares.length; i++) {
const result = await middlewares[i](...args) const result = await middlewares[i](...args)
@@ -22,25 +22,25 @@ export class Middleware {
} }
public static async process<EventName extends T.RageFW_ServerEvent>( public static async process<EventName extends T.RageFW_ServerEvent>(
middlewareOptions: T.RageFW_MiddlewareOptions<EventName>,
callback: T.RageFW_ServerCallback<EventName>,
args: T.RageFW_ServerArgs<EventName>, args: T.RageFW_ServerArgs<EventName>,
) { middlewareOptions?: T.RageFW_MiddlewareOptions<EventName>,
): Promise<boolean> {
if (!middlewareOptions) return true
if (Array.isArray(middlewareOptions)) { if (Array.isArray(middlewareOptions)) {
const middlewaresResponse = await Middleware.execute( const middlewaresResponse = await Middleware.execute(
middlewareOptions,
args, args,
middlewareOptions,
) )
return middlewaresResponse.success
if (middlewaresResponse.success) return await callback(...args)
} else { } else {
const middlewaresResponse = await Middleware.execute( const middlewaresResponse = await Middleware.execute(
middlewareOptions.executables,
args, args,
middlewareOptions.executables,
) )
if (middlewaresResponse.success) { if (middlewaresResponse.success) {
return await callback(...args) return true
} else { } else {
middlewareOptions.onError( middlewareOptions.onError(
middlewaresResponse.message ?? middlewaresResponse.message ??
@@ -48,6 +48,7 @@ export class Middleware {
middlewaresResponse.id + middlewaresResponse.id +
' failed', ' failed',
) )
return false
} }
} }
} }
+18 -6
View File
@@ -1,5 +1,6 @@
import { rpc } from './rpc' import { rpc } from './rpc'
import { Middleware } from './middleware' import { Middleware } from './middleware'
import { Validation } from './validation'
import type * as T from '../types' import type * as T from '../types'
/** Server-side interactions */ /** Server-side interactions */
@@ -10,6 +11,7 @@ export class Server {
* @param eventName - The name of the event to register * @param eventName - The name of the event to register
* @param callback - The callback function to be executed when the event is triggered * @param callback - The callback function to be executed when the event is triggered
* @param [options] - Optional settings for callback execution * @param [options] - Optional settings for callback execution
* @param [options.validation] - Validation schema to be checked before the callback executes
* @param [options.middlewares] - Middleware functions to be checked before the callback executes * @param [options.middlewares] - Middleware functions to be checked before the callback executes
* @returns {Server} The current server instance, enabling method chaining * @returns {Server} The current server instance, enabling method chaining
* *
@@ -44,6 +46,7 @@ export class Server {
eventName: EventName, eventName: EventName,
callback: T.RageFW_ServerCallback<EventName>, callback: T.RageFW_ServerCallback<EventName>,
options?: { options?: {
validation?: T.RageFW_ValidationOptions
middlewares?: T.RageFW_MiddlewareOptions<EventName> middlewares?: T.RageFW_MiddlewareOptions<EventName>
}, },
): Server { ): Server {
@@ -52,9 +55,22 @@ export class Server {
ReturnType<typeof callback> | Promise<unknown>, ReturnType<typeof callback> | Promise<unknown>,
EventName EventName
>(eventName, async (...data) => { >(eventName, async (...data) => {
if (!options?.middlewares) return await callback(...data) if (!options?.middlewares && !options?.validation)
return await callback(...data)
await Middleware.process(options.middlewares, callback, data) const validationSuccess = Validation.process(
data,
options?.validation,
)
if (!validationSuccess) return
const middlewaresSuccess = await Middleware.process(
data,
options?.middlewares,
)
if (!middlewaresSuccess) return
return await callback(...data)
}) })
return this return this
@@ -90,7 +106,3 @@ export class Server {
// return rpc.call(eventName, args) // return rpc.call(eventName, args)
// } // }
} }
// new Server()
// .register('customServerEvent', async (a, b, c) => true)
// .unregister('customServerEvent')
+26
View File
@@ -0,0 +1,26 @@
import type * as T from '../types'
export class Validation {
constructor() {}
public static process<EventName extends T.RageFW_ServerEvent>(
args: T.RageFW_ServerArgs<EventName>,
validationOptions?: T.RageFW_ValidationOptions,
): boolean {
if (!validationOptions) return true
if ('schema' in validationOptions) {
const validationResponse = validationOptions.schema.safeParse(args)
if (validationResponse.success) {
return true
} else {
validationOptions.onError(validationResponse.error)
return false
}
} else {
const validationResponse = validationOptions.safeParse(args)
return validationResponse.success
}
}
}
+1
View File
@@ -2,3 +2,4 @@ export * from './browser'
export * from './client' export * from './client'
export * from './middleware' export * from './middleware'
export * from './server' export * from './server'
export * from './validation'
+11
View File
@@ -0,0 +1,11 @@
import type { z } from 'zod'
export type RageFW_ValidationSchema = z.ZodTuple<any, any> | z.ZodArray<any>
export type RageFW_ValidationSchemaExtended = {
schema: RageFW_ValidationSchema
onError: (error: z.ZodError) => unknown
}
export type RageFW_ValidationOptions =
| RageFW_ValidationSchema
| RageFW_ValidationSchemaExtended
+2 -2
View File
@@ -1,12 +1,12 @@
import { defineConfig } from 'tsup' import { defineConfig } from 'tsup'
export default defineConfig({ export default defineConfig({
entry: ['src/index.ts'], entry: ['./src/index.ts'],
outDir: './dist', outDir: './dist',
format: ['cjs'], format: ['cjs'],
noExternal: ['rage-rpc'],
experimentalDts: true, experimentalDts: true,
splitting: false, splitting: false,
sourcemap: false, sourcemap: false,
clean: true, clean: true,
bundle: false,
}) })