@open-core/framework 1.0.1-beta.1 → 1.0.2-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +10 -0
  2. package/dist/server/bootstrap.js +2 -2
  3. package/dist/server/controllers/command.controller.d.ts +1 -1
  4. package/dist/server/controllers/command.controller.js +6 -6
  5. package/dist/server/decorators/command.d.ts +34 -4
  6. package/dist/server/decorators/command.js +11 -6
  7. package/dist/server/decorators/controller.d.ts +22 -0
  8. package/dist/server/decorators/controller.js +22 -0
  9. package/dist/server/decorators/coreEvent.d.ts +20 -0
  10. package/dist/server/decorators/coreEvent.js +20 -0
  11. package/dist/server/decorators/export.d.ts +38 -0
  12. package/dist/server/decorators/export.js +38 -0
  13. package/dist/server/decorators/guard.d.ts +51 -0
  14. package/dist/server/decorators/guard.js +43 -0
  15. package/dist/server/decorators/index.d.ts +3 -3
  16. package/dist/server/decorators/index.js +6 -3
  17. package/dist/server/decorators/onNet.d.ts +58 -0
  18. package/dist/server/decorators/onNet.js +57 -0
  19. package/dist/server/decorators/onTick.d.ts +31 -0
  20. package/dist/server/decorators/onTick.js +31 -0
  21. package/dist/server/decorators/public.d.ts +19 -8
  22. package/dist/server/decorators/public.js +19 -8
  23. package/dist/server/decorators/requiresState.d.ts +18 -17
  24. package/dist/server/decorators/requiresState.js +18 -17
  25. package/dist/server/decorators/throttle.d.ts +39 -0
  26. package/dist/server/decorators/throttle.js +27 -0
  27. package/dist/server/decorators/utils.d.ts +50 -0
  28. package/dist/server/decorators/utils.js +50 -0
  29. package/dist/server/error-handler.js +2 -2
  30. package/dist/server/services/command.service.d.ts +1 -1
  31. package/dist/server/services/command.service.js +9 -6
  32. package/dist/server/system/processors/command.processor.d.ts +2 -2
  33. package/dist/server/system/processors/command.processor.js +1 -2
  34. package/dist/server/system/processors/netEvent.processor.js +5 -4
  35. package/dist/server/system/schema-generator.d.ts +2 -0
  36. package/dist/server/system/schema-generator.js +28 -0
  37. package/dist/server/templates/admin/admin.controller-template.d.ts +7 -5
  38. package/package.json +19 -1
  39. package/dist/server/decorators/netEvent.d.ts +0 -36
  40. package/dist/server/decorators/netEvent.js +0 -40
@@ -2,6 +2,37 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OnTick = OnTick;
4
4
  const metadata_server_keys_1 = require("../system/metadata-server.keys");
5
+ /**
6
+ * OnTick
7
+ * ------------------------------------------------------------
8
+ * Marks a method to be executed on every server tick.
9
+ *
10
+ * Methods decorated with `@OnTick()` are automatically
11
+ * registered by the framework's scheduler and invoked on each
12
+ * FXServer tick (~ every 0–10 ms depending on workload).
13
+ *
14
+ * This decorator should be used for lightweight recurring
15
+ * logic: status updates, background checks, cleanup tasks,
16
+ * or time-based processes relevant to gameplay.
17
+ *
18
+ * Heavy or blocking operations should be avoided inside tick
19
+ * handlers, as they directly impact global server performance.
20
+ *
21
+ * ```ts
22
+ * export class SyncController {
23
+ * @OnTick()
24
+ * updatePlayers() {
25
+ * // Runs every tick
26
+ * this.service.syncPositions()
27
+ * }
28
+ * }
29
+ *
30
+ * ```
31
+ *
32
+ * Internally, the decorator only stores metadata. The
33
+ * server bootstrap scans for this metadata and binds the
34
+ * method to FiveM's tick cycle.
35
+ */
5
36
  function OnTick() {
6
37
  return (target, propertyKey) => {
7
38
  Reflect.defineMetadata(metadata_server_keys_1.METADATA_KEYS.TICK, {}, target, propertyKey);
@@ -1,16 +1,27 @@
1
1
  /**
2
- * **Only works on NetEvents in Server-side**
2
+ * Marks a server-side NetEvent handler as publicly accessible.
3
3
  *
4
- * Marks an endpoint as publicly accessible (no authentication required).
5
- * Use only for login, register, or public info endpoints.
4
+ * This decorator disables authentication requirements for the
5
+ * decorated method. It should only be applied to endpoints used for:
6
+ * - Login
7
+ * - Registration
8
+ * - Public information retrieval
6
9
  *
7
- * @example
10
+ * ## Security Warning
11
+ * This decorator must be used with caution. Public endpoints must NOT
12
+ * modify sensitive game state unless necessary.
13
+ *
14
+ * ## Example
8
15
  * ```ts
9
16
  * class AuthServerController {
10
- * @ Server.Public()
11
- * @ Server.OnNet('auth:login')
12
- * async login(player: Server.Player, credentials: AuthCredentials) { ... }
17
+ * @Public()
18
+ * @OnNet("auth:login")
19
+ * async login(player: Server.Player, credentials: AuthCredentials) {
20
+ * // no authentication required for this event
21
+ * }
13
22
  * }
14
23
  * ```
15
- * */
24
+ *
25
+ * @returns Method decorator that marks a handler as unauthenticated.
26
+ */
16
27
  export declare function Public(): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
@@ -3,20 +3,31 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Public = Public;
4
4
  const metadata_server_keys_1 = require("../system/metadata-server.keys");
5
5
  /**
6
- * **Only works on NetEvents in Server-side**
6
+ * Marks a server-side NetEvent handler as publicly accessible.
7
7
  *
8
- * Marks an endpoint as publicly accessible (no authentication required).
9
- * Use only for login, register, or public info endpoints.
8
+ * This decorator disables authentication requirements for the
9
+ * decorated method. It should only be applied to endpoints used for:
10
+ * - Login
11
+ * - Registration
12
+ * - Public information retrieval
10
13
  *
11
- * @example
14
+ * ## Security Warning
15
+ * This decorator must be used with caution. Public endpoints must NOT
16
+ * modify sensitive game state unless necessary.
17
+ *
18
+ * ## Example
12
19
  * ```ts
13
20
  * class AuthServerController {
14
- * @ Server.Public()
15
- * @ Server.OnNet('auth:login')
16
- * async login(player: Server.Player, credentials: AuthCredentials) { ... }
21
+ * @Public()
22
+ * @OnNet("auth:login")
23
+ * async login(player: Server.Player, credentials: AuthCredentials) {
24
+ * // no authentication required for this event
25
+ * }
17
26
  * }
18
27
  * ```
19
- * */
28
+ *
29
+ * @returns Method decorator that marks a handler as unauthenticated.
30
+ */
20
31
  function Public() {
21
32
  return function (target, propertyKey, descriptor) {
22
33
  Reflect.defineMetadata(metadata_server_keys_1.METADATA_KEYS.PUBLIC, true, target, propertyKey);
@@ -22,34 +22,35 @@ export interface StateRequirement {
22
22
  errorMessage?: string;
23
23
  }
24
24
  /**
25
- * **Game State Guard Decorator**
25
+ * Enforces gameplay state requirements before executing a method.
26
26
  *
27
- * A method decorator that enforces game logic constraints based on the player's dynamic states.
28
- * It intercepts the method call and validates the player's flags (e.g., 'dead', 'cuffed', 'driving')
29
- * before allowing the controller logic to execute.
27
+ * The decorator intercepts the method call and verifies whether
28
+ * the player satisfies required state flags (e.g. "dead", "cuffed",
29
+ * "on_duty_police").
30
30
  *
31
- * @remarks
32
- * - This decorator assumes the **first argument** of the decorated method is a `Server.Player` instance.
33
- * - It throws a `GAME_STATE_ERROR` (AppError) if requirements are not met, which should be caught by the global error handler.
31
+ * ## Whitelist (`has`)
32
+ * The player MUST have *all* required states. Missing any state blocks execution.
34
33
  *
35
- * @param req - The state requirements configuration (whitelist and/or blacklist).
34
+ * ## Blacklist (`missing`)
35
+ * The player MUST NOT have any of the forbidden states.
36
36
  *
37
- * @throws {Error} If the decorated method is called without a valid Player context (Server-side logic error).
38
- * @throws {AppError} If the player fails the state validation (Client-facing logic error).
37
+ * ## Error Handling
38
+ * - Throws a normal Error if the method is called without a valid Player.
39
+ * - Throws an `AppError(GAME_STATE_ERROR)` if validation fails.
39
40
  *
40
- * @example
41
+ * ## Example
41
42
  * ```ts
42
- * // Example 1: Action requires being alive (not dead) and not handcuffed
43
- * @RequiresState({ missing: ['dead', 'cuffed'] })
43
+ * @RequiresState({ missing: ["dead", "cuffed"] })
44
44
  * openInventory(player: Server.Player) { ... }
45
45
  *
46
- * // Example 2: Action requires being a police officer on duty
47
46
  * @RequiresState({
48
- * has: ['police_duty'],
49
- * missing: ['dead'],
50
- * errorMessage: 'You must be on duty to access the armory.'
47
+ * has: ["police_duty"],
48
+ * missing: ["dead"],
49
+ * errorMessage: "You must be on duty to access the armory."
51
50
  * })
52
51
  * openArmory(player: Server.Player) { ... }
53
52
  * ```
53
+ *
54
+ * @param req - State validation configuration.
54
55
  */
55
56
  export declare function RequiresState(req: StateRequirement): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
@@ -3,35 +3,36 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RequiresState = RequiresState;
4
4
  const utils_1 = require("../../utils");
5
5
  /**
6
- * **Game State Guard Decorator**
6
+ * Enforces gameplay state requirements before executing a method.
7
7
  *
8
- * A method decorator that enforces game logic constraints based on the player's dynamic states.
9
- * It intercepts the method call and validates the player's flags (e.g., 'dead', 'cuffed', 'driving')
10
- * before allowing the controller logic to execute.
8
+ * The decorator intercepts the method call and verifies whether
9
+ * the player satisfies required state flags (e.g. "dead", "cuffed",
10
+ * "on_duty_police").
11
11
  *
12
- * @remarks
13
- * - This decorator assumes the **first argument** of the decorated method is a `Server.Player` instance.
14
- * - It throws a `GAME_STATE_ERROR` (AppError) if requirements are not met, which should be caught by the global error handler.
12
+ * ## Whitelist (`has`)
13
+ * The player MUST have *all* required states. Missing any state blocks execution.
15
14
  *
16
- * @param req - The state requirements configuration (whitelist and/or blacklist).
15
+ * ## Blacklist (`missing`)
16
+ * The player MUST NOT have any of the forbidden states.
17
17
  *
18
- * @throws {Error} If the decorated method is called without a valid Player context (Server-side logic error).
19
- * @throws {AppError} If the player fails the state validation (Client-facing logic error).
18
+ * ## Error Handling
19
+ * - Throws a normal Error if the method is called without a valid Player.
20
+ * - Throws an `AppError(GAME_STATE_ERROR)` if validation fails.
20
21
  *
21
- * @example
22
+ * ## Example
22
23
  * ```ts
23
- * // Example 1: Action requires being alive (not dead) and not handcuffed
24
- * @RequiresState({ missing: ['dead', 'cuffed'] })
24
+ * @RequiresState({ missing: ["dead", "cuffed"] })
25
25
  * openInventory(player: Server.Player) { ... }
26
26
  *
27
- * // Example 2: Action requires being a police officer on duty
28
27
  * @RequiresState({
29
- * has: ['police_duty'],
30
- * missing: ['dead'],
31
- * errorMessage: 'You must be on duty to access the armory.'
28
+ * has: ["police_duty"],
29
+ * missing: ["dead"],
30
+ * errorMessage: "You must be on duty to access the armory."
32
31
  * })
33
32
  * openArmory(player: Server.Player) { ... }
34
33
  * ```
34
+ *
35
+ * @param req - State validation configuration.
35
36
  */
36
37
  function RequiresState(req) {
37
38
  return function (target, propertyKey, descriptor) {
@@ -1,9 +1,48 @@
1
1
  import type { SecurityAction } from '../types/security.types';
2
2
  interface ThrottleOptions {
3
+ /**
4
+ * limit of calls per windowMs
5
+ */
3
6
  limit: number;
7
+ /**
8
+ * time window for numeric overload
9
+ */
4
10
  windowMs: number;
11
+ /**
12
+ * action to perform on exceed limit
13
+ */
5
14
  onExceed?: SecurityAction;
15
+ /**
16
+ * custom message to display on exceed limit (ERROR MESSAGE)
17
+ */
6
18
  message?: string;
7
19
  }
20
+ /**
21
+ * Rate-limits how frequently a method can be called by a specific player.
22
+ *
23
+ * Uses `RateLimiterService` and a unique key composed of:
24
+ * `playerID:ClassName:MethodName`.
25
+ *
26
+ * ## Overload
27
+ * - `@Throttle(5, 1000)` → limit = 5 calls per 1000 ms
28
+ * - `@Throttle({ limit, windowMs, onExceed, message })`
29
+ *
30
+ * ## Behavior
31
+ * If the rate limit is exceeded:
32
+ * - A `SecurityError` is thrown.
33
+ * - Optional custom behavior (`onExceed`) may be executed.
34
+ *
35
+ * ## Example
36
+ * ```ts
37
+ * @Throttle(5, 2000)
38
+ * async search(player: Server.Player, query: string) { ... }
39
+ *
40
+ * @Throttle({ limit: 1, windowMs: 5000, message: "Too fast!" })
41
+ * async placeOrder(player: Server.Player) { ... }
42
+ * ```
43
+ *
44
+ * @param optionsOrLimit - Number (simple mode) or full config object.
45
+ * @param windowMs - Time window for numeric overload.
46
+ */
8
47
  export declare function Throttle(optionsOrLimit: number | ThrottleOptions, windowMs?: number): (target: any, propertyKey: string, descriptor?: PropertyDescriptor) => PropertyDescriptor | undefined;
9
48
  export {};
@@ -4,6 +4,33 @@ exports.Throttle = Throttle;
4
4
  const tsyringe_1 = require("tsyringe");
5
5
  const rate_limiter_service_1 = require("../services/rate-limiter.service");
6
6
  const utils_1 = require("../../utils");
7
+ /**
8
+ * Rate-limits how frequently a method can be called by a specific player.
9
+ *
10
+ * Uses `RateLimiterService` and a unique key composed of:
11
+ * `playerID:ClassName:MethodName`.
12
+ *
13
+ * ## Overload
14
+ * - `@Throttle(5, 1000)` → limit = 5 calls per 1000 ms
15
+ * - `@Throttle({ limit, windowMs, onExceed, message })`
16
+ *
17
+ * ## Behavior
18
+ * If the rate limit is exceeded:
19
+ * - A `SecurityError` is thrown.
20
+ * - Optional custom behavior (`onExceed`) may be executed.
21
+ *
22
+ * ## Example
23
+ * ```ts
24
+ * @Throttle(5, 2000)
25
+ * async search(player: Server.Player, query: string) { ... }
26
+ *
27
+ * @Throttle({ limit: 1, windowMs: 5000, message: "Too fast!" })
28
+ * async placeOrder(player: Server.Player) { ... }
29
+ * ```
30
+ *
31
+ * @param optionsOrLimit - Number (simple mode) or full config object.
32
+ * @param windowMs - Time window for numeric overload.
33
+ */
7
34
  function Throttle(optionsOrLimit, windowMs) {
8
35
  return function (target, propertyKey, descriptor) {
9
36
  if (!descriptor) {
@@ -1,7 +1,57 @@
1
1
  import type { BindingScope } from './bind';
2
+ /**
3
+ * Marks a class as a framework-managed Service.
4
+ *
5
+ * The decorator binds the class into the dependency injection
6
+ * container using the provided scope (default: `singleton`).
7
+ *
8
+ * Services represent reusable, stateless or stateful logic such as:
9
+ * - Business rules
10
+ * - Utility layers
11
+ * - Gameplay systems
12
+ * - Internal modules
13
+ *
14
+ * This decorator is a convenience wrapper over `@Bind()`, allowing
15
+ * future extension of service-specific behavior without changing
16
+ * end-user code.
17
+ *
18
+ * ## Example
19
+ * ```ts
20
+ * @Service()
21
+ * export class InventoryService {
22
+ * addItem() { ... }
23
+ * }
24
+ * ```
25
+ *
26
+ * @param options.scope - Optional binding scope (`singleton` | `transient`)
27
+ */
2
28
  export declare function Service(options?: {
3
29
  scope?: BindingScope;
4
30
  }): (target: any) => void;
31
+ /**
32
+ * Marks a class as a Repository within the framework.
33
+ *
34
+ * A Repository abstracts persistence operations (e.g., database,
35
+ * API, in-memory, or hybrid storage). It is registered in the
36
+ * dependency injection container using the provided scope
37
+ * (default: `singleton`).
38
+ *
39
+ * `@Repo()` is intentionally separate from `@Service()` to clearly
40
+ * distinguish persistence-oriented classes from business logic.
41
+ * In the future, repository-specific behavior (caching layers,
42
+ * transactional wrappers, profiling, etc.) can be attached here
43
+ * without modifying user code.
44
+ *
45
+ * ## Example
46
+ * ```ts
47
+ * @Repo()
48
+ * export class AccountRepository {
49
+ * async findById(id: string) { ... }
50
+ * }
51
+ * ```
52
+ *
53
+ * @param options.scope - Optional binding scope (`singleton` | `transient`)
54
+ */
5
55
  export declare function Repo(options?: {
6
56
  scope?: BindingScope;
7
57
  }): (target: any) => void;
@@ -3,10 +3,60 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Service = Service;
4
4
  exports.Repo = Repo;
5
5
  const bind_1 = require("./bind");
6
+ /**
7
+ * Marks a class as a framework-managed Service.
8
+ *
9
+ * The decorator binds the class into the dependency injection
10
+ * container using the provided scope (default: `singleton`).
11
+ *
12
+ * Services represent reusable, stateless or stateful logic such as:
13
+ * - Business rules
14
+ * - Utility layers
15
+ * - Gameplay systems
16
+ * - Internal modules
17
+ *
18
+ * This decorator is a convenience wrapper over `@Bind()`, allowing
19
+ * future extension of service-specific behavior without changing
20
+ * end-user code.
21
+ *
22
+ * ## Example
23
+ * ```ts
24
+ * @Service()
25
+ * export class InventoryService {
26
+ * addItem() { ... }
27
+ * }
28
+ * ```
29
+ *
30
+ * @param options.scope - Optional binding scope (`singleton` | `transient`)
31
+ */
6
32
  function Service(options) {
7
33
  var _a;
8
34
  return (0, bind_1.Bind)((_a = options === null || options === void 0 ? void 0 : options.scope) !== null && _a !== void 0 ? _a : 'singleton');
9
35
  }
36
+ /**
37
+ * Marks a class as a Repository within the framework.
38
+ *
39
+ * A Repository abstracts persistence operations (e.g., database,
40
+ * API, in-memory, or hybrid storage). It is registered in the
41
+ * dependency injection container using the provided scope
42
+ * (default: `singleton`).
43
+ *
44
+ * `@Repo()` is intentionally separate from `@Service()` to clearly
45
+ * distinguish persistence-oriented classes from business logic.
46
+ * In the future, repository-specific behavior (caching layers,
47
+ * transactional wrappers, profiling, etc.) can be attached here
48
+ * without modifying user code.
49
+ *
50
+ * ## Example
51
+ * ```ts
52
+ * @Repo()
53
+ * export class AccountRepository {
54
+ * async findById(id: string) { ... }
55
+ * }
56
+ * ```
57
+ *
58
+ * @param options.scope - Optional binding scope (`singleton` | `transient`)
59
+ */
10
60
  function Repo(options) {
11
61
  var _a;
12
62
  return (0, bind_1.Bind)((_a = options === null || options === void 0 ? void 0 : options.scope) !== null && _a !== void 0 ? _a : 'singleton');
@@ -14,8 +14,8 @@ function normalizeError(error, origin) {
14
14
  }
15
15
  function handleCommandError(error, meta, playerId) {
16
16
  const appError = normalizeError(error, 'server');
17
- logger_1.loggers.command.error(`Command execution failed: /${meta.name}`, {
18
- command: meta.name,
17
+ logger_1.loggers.command.error(`Command execution failed: /${meta.command}`, {
18
+ command: meta.command,
19
19
  handler: meta.methodName,
20
20
  playerId,
21
21
  code: appError.code,
@@ -8,7 +8,7 @@ export declare class CommandService {
8
8
  register(meta: CommandMetadata, handler: Function): void;
9
9
  execute(player: Server.Player, commandName: string, args: string[], raw: string): Promise<void>;
10
10
  getAllCommands(): {
11
- name: string;
11
+ command: string;
12
12
  description: string;
13
13
  usage: string;
14
14
  }[];
@@ -18,26 +18,29 @@ const utils_1 = require("../../utils");
18
18
  const zod_1 = __importDefault(require("zod"));
19
19
  const security_handler_contract_1 = require("../templates/security/security-handler.contract");
20
20
  const logger_1 = require("../../shared/logger");
21
+ const schema_generator_1 = require("../system/schema-generator");
21
22
  let CommandService = class CommandService {
22
23
  constructor(securityHandler) {
23
24
  this.securityHandler = securityHandler;
24
25
  this.commands = new Map();
25
26
  }
26
27
  register(meta, handler) {
27
- this.commands.set(meta.name.toLowerCase(), { meta, handler });
28
- logger_1.loggers.command.debug(`Registered: /${meta.name}${meta.schema ? ' [Validated]' : ''}`);
28
+ this.commands.set(meta.command.toLowerCase(), { meta, handler });
29
+ logger_1.loggers.command.debug(`Registered: /${meta.command}${meta.schema ? ' [Validated]' : ''}`);
29
30
  }
30
31
  async execute(player, commandName, args, raw) {
32
+ var _a;
31
33
  const entry = this.commands.get(commandName.toLowerCase());
32
34
  if (!entry) {
33
35
  return;
34
36
  }
35
37
  const { meta, handler } = entry;
36
38
  let validatedArgs = args;
37
- if (meta.schema) {
39
+ // Use explicit schema or auto-generate from paramTypes
40
+ const schema = (_a = meta.schema) !== null && _a !== void 0 ? _a : (0, schema_generator_1.generateSchemaFromTypes)(meta.paramTypes);
41
+ if (schema) {
38
42
  try {
39
- const result = await meta.schema.parseAsync(args);
40
- validatedArgs = Array.isArray(result) ? result : [result];
43
+ validatedArgs = await schema.parseAsync(args);
41
44
  }
42
45
  catch (error) {
43
46
  if (error instanceof zod_1.default.ZodError) {
@@ -60,7 +63,7 @@ let CommandService = class CommandService {
60
63
  return Array.from(this.commands.values()).map((c) => {
61
64
  var _a, _b;
62
65
  return ({
63
- name: c.meta.name,
66
+ command: c.meta.command,
64
67
  description: (_a = c.meta.description) !== null && _a !== void 0 ? _a : '',
65
68
  usage: (_b = c.meta.usage) !== null && _b !== void 0 ? _b : '',
66
69
  });
@@ -1,9 +1,9 @@
1
1
  import { DecoratorProcessor } from '../../../system/decorator-processor';
2
2
  import { CommandService } from '../../services';
3
- import { CommandConfig } from '../../decorators';
3
+ import { CommandMetadata } from '../../decorators/command';
4
4
  export declare class CommandProcessor implements DecoratorProcessor {
5
5
  private commandService;
6
6
  readonly metadataKey: string;
7
7
  constructor(commandService: CommandService);
8
- process(target: any, methodName: string, metadata: CommandConfig): void;
8
+ process(target: any, methodName: string, metadata: CommandMetadata): void;
9
9
  }
@@ -20,8 +20,7 @@ let CommandProcessor = class CommandProcessor {
20
20
  }
21
21
  process(target, methodName, metadata) {
22
22
  const handler = target[methodName].bind(target);
23
- const fullMeta = Object.assign(Object.assign({}, metadata), { methodName, target: target.constructor });
24
- this.commandService.register(fullMeta, handler);
23
+ this.commandService.register(metadata, handler);
25
24
  }
26
25
  };
27
26
  exports.CommandProcessor = CommandProcessor;
@@ -20,6 +20,7 @@ const security_handler_contract_1 = require("../../templates/security/security-h
20
20
  const utils_1 = require("../../../utils");
21
21
  const logger_1 = require("../../../shared/logger");
22
22
  const zod_1 = __importDefault(require("zod"));
23
+ const schema_generator_1 = require("../schema-generator");
23
24
  let NetEventProcessor = class NetEventProcessor {
24
25
  constructor(playerService, securityHandler) {
25
26
  this.playerService = playerService;
@@ -53,11 +54,11 @@ let NetEventProcessor = class NetEventProcessor {
53
54
  return;
54
55
  }
55
56
  let validatedArgs = args;
56
- if (metadata.schema) {
57
+ const schema = (0, schema_generator_1.generateSchemaFromTypes)(metadata.paramTypes);
58
+ if (schema) {
57
59
  try {
58
- const payload = args[0];
59
- const parsed = metadata.schema.parse(payload);
60
- validatedArgs = [parsed];
60
+ const parsed = schema.parse(args);
61
+ validatedArgs = Array.isArray(parsed) ? parsed : [parsed];
61
62
  }
62
63
  catch (error) {
63
64
  if (error instanceof zod_1.default.ZodError) {
@@ -0,0 +1,2 @@
1
+ import z from 'zod';
2
+ export declare function generateSchemaFromTypes(paramTypes: any[]): z.ZodTuple | null;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generateSchemaFromTypes = generateSchemaFromTypes;
7
+ const zod_1 = __importDefault(require("zod"));
8
+ const player_1 = require("../entities/player");
9
+ function typeToZodSchema(type) {
10
+ switch (type) {
11
+ case String: return zod_1.default.coerce.string();
12
+ case Number: return zod_1.default.coerce.number();
13
+ case Boolean: return zod_1.default.coerce.boolean();
14
+ case Array: return zod_1.default.array(zod_1.default.any());
15
+ case Object: return zod_1.default.any();
16
+ default: return zod_1.default.any();
17
+ }
18
+ }
19
+ function generateSchemaFromTypes(paramTypes) {
20
+ var _a;
21
+ if (!paramTypes || paramTypes.length <= 1)
22
+ return null;
23
+ if (paramTypes[0] !== player_1.Player) {
24
+ throw new Error(`@OnNet: First parameter must be Player, got ${(_a = paramTypes[0]) === null || _a === void 0 ? void 0 : _a.name}`);
25
+ }
26
+ const argSchemas = paramTypes.slice(1).map(typeToZodSchema);
27
+ return zod_1.default.tuple(argSchemas);
28
+ }
@@ -1,10 +1,12 @@
1
- import type { Server } from '../../..';
1
+ import { Player } from "../../entities";
2
+ type CommandArgs = string[] | any;
2
3
  /**
3
4
  * Template for admin controller implementations.
4
5
  */
5
6
  export interface AdminControllerTemplate {
6
- handleBanCommand(player: Server.Player, args: string[], raw?: string): Promise<void>;
7
- handleKickCommand(player: Server.Player, args: string[], raw?: string): Promise<void>;
8
- handleMuteCommand(player: Server.Player, args: string[], raw?: string): Promise<void>;
9
- handleUnmuteCommand(player: Server.Player, args: string[], raw?: string): Promise<void>;
7
+ handleBanCommand(player: Player, args: CommandArgs, raw?: string): Promise<void>;
8
+ handleKickCommand(player: Player, args: CommandArgs, raw?: string): Promise<void>;
9
+ handleMuteCommand(player: Player, args: CommandArgs, raw?: string): Promise<void>;
10
+ handleUnmuteCommand(player: Player, args: CommandArgs, raw?: string): Promise<void>;
10
11
  }
12
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-core/framework",
3
- "version": "1.0.1-beta.1",
3
+ "version": "1.0.2-beta.1",
4
4
  "description": "Secure, Event-Driven, OOP Engine for FiveM. Stop scripting, start engineering.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -18,6 +18,24 @@
18
18
  "README.md",
19
19
  "LICENSE"
20
20
  ],
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "import": "./dist/index.js",
25
+ "require": "./dist/index.js"
26
+ },
27
+ "./server": {
28
+ "types": "./dist/server/index.d.ts",
29
+ "import": "./dist/server/index.js",
30
+ "require": "./dist/server/index.js"
31
+ },
32
+ "./client": {
33
+ "types": "./dist/client/index.d.ts",
34
+ "import": "./dist/client/index.js",
35
+ "require": "./dist/client/index.js"
36
+ },
37
+ "./package.json": "./package.json"
38
+ },
21
39
  "scripts": {
22
40
  "build": "tsc -p tsconfig.build.json",
23
41
  "watch": "tsc -p tsconfig.build.json --watch",