- added zod integration (validation) for testing
This commit is contained in:
Danya H 2024-11-01 13:20:07 +00:00
parent 21ca123797
commit fd9f1cab18
19 changed files with 191 additions and 40 deletions

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
} }

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
}
}
}

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'

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

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,

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

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'

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
} }
} }
} }

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
}
}
}

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'

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

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,

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'

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
} }
} }
} }

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')

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
}
}
}

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'

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

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,
}) })