{"id":20168971,"url":"https://github.com/mage/mage-validator","last_synced_at":"2025-04-10T02:12:56.795Z","repository":{"id":57143463,"uuid":"92396164","full_name":"mage/mage-validator","owner":"mage","description":"Validation system for MAGE topics and user command input types (TypeScript).","archived":false,"fork":false,"pushed_at":"2023-01-12T01:41:48.000Z","size":494,"stargazers_count":0,"open_issues_count":7,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-24T03:43:56.635Z","etag":null,"topics":["mage","nodejs","typescript"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mage.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-05-25T11:18:42.000Z","updated_at":"2019-03-20T10:19:57.000Z","dependencies_parsed_at":"2023-02-09T09:01:09.079Z","dependency_job_id":null,"html_url":"https://github.com/mage/mage-validator","commit_stats":null,"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mage%2Fmage-validator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mage%2Fmage-validator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mage%2Fmage-validator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mage%2Fmage-validator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mage","download_url":"https://codeload.github.com/mage/mage-validator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247878022,"owners_count":21011158,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mage","nodejs","typescript"],"created_at":"2024-11-14T01:10:46.661Z","updated_at":"2025-04-10T02:12:56.771Z","avatar_url":"https://github.com/mage.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"mage-validator\n==============\n\nValidation system for MAGE topics and user command input types (TypeScript).\n\nInstallation\n-------------\n\n```shell\nnpm install --save mage-validator class-validator class-transformer reflect-metadata\n```\n\nYou need to install two peer dependencies alongside `mage-validator`:\n\n  1. `class-validator` so that you may add validation decorators to your data class\n  2. `class-transformer` to control how the received data will be deserialised\n  3. `relfect-metadata` will be needed by the two modules above to extract type information\n\n\nYou will also need to make sure that the following configuration is set in your\n`tsconfig.json`:\n\n```json\n{\n  \"experimentalDecorators\": true,\n  \"emitDecoratorMetadata\": true\n}\n```\n\nUsage\n-----\n\nmage-validator exports all functions exposed by class-validator and\nclass-transformer for convenience. A few changes and additions have\nhowever been made for convenience.\n\n### @Type decorator\n\n```typescript\n// Both are equivalent\n@Type(Hello)\n@Type(() =\u003e Hello)\n```\n\nThe `@Type` decorator has been customized to accept either a type \nor a function (instead of only a function).\n\n### @MapOf decorator\n\n```typescript\nfunction validateFunc(key: string, value: Child) {\n  throw new Error('never valid')\n}\n\nclass Child { @IsPositive() public id: number }\n\n@MapOf(Child)\nclass DynamicMap { [key: string]: Child }\n\nclass TestTopic extends ValidatedTopic {\n  // [...]\n\n  // Use a Map class\n  @Type(DynamicMap) public map: DynamicMap\n\n  // Use an anonymous object as a map\n  @MapOf(Child) public anonymousMap: { [key: string]: Child }\n\n  @MapOf(Child, validateFunc) public anotherMap: { [key: string]: Child }\n}\n```\n\n`mage-validator` also provides an additional `@MapOf` decorator for dealing with\nkey-value map objects; using this type will both ensure nested maps entries\nwill be typed and that each entries will be validated (so `@ValidateNested` \nis not required).\n\n### (Optional) Project structure\n\nIn normal MAGE projects, you need to put all your topics configuration under\n`./lib/archivist/index.ts`. However, this quickly becomes hard to manage as\nthe number of topics grows.\n\nInstead, we recommend using the following file structure:\n\n```plaintext\nlib/\n  archivist/\n    index.ts\n  modules/\n    modulename/\n      topics/\n        Players.ts\n      types/\n        PlayerConfig.ts\n        SomethingElse.ts\n      usercommands/\n        addGems.ts\n        commandTwo.ts\n      index.ts\n  index.ts\n```\n\nIn other words, we recommend to separate MAGE topics and attach them to their\nrelated modules under the `topics` folder; we also recommend to put other \ntypes (example: user command custom parameter type) under a `types` folder.\n\nTo make it easier for you to set this up, `mage-validator` comes with a method you\ncan simply drop into `./lib/archivist/index.ts`; it will scan each one of your\nmodules and attempt to load all topics defined in them.\n\n\u003e `./lib/archivist/index.ts`\n\n```typescript\nimport { loadTopicsFromModules } from 'mage-validator'\n\nloadTopicsFromModules(exports)\n```\n\n### User command parameters and return data validation\n\nValidation works by encapsulating messages into types:\n\n\u003e `./lib/modules/modulename/types/PlayerData.ts`\n\n```typescript\nimport { IsInt, Max } from 'mage-validator';\n\nexport default class {\n    @IsInt()\n    @Max(5)\n    public count: gems = 1 // Default value\n}\n```\n\nWhich then can be used as user commands types:\n\n\u003e `./lib/modules/modulename/usercommands/addGems.ts`\n\n```typescript\nimport * as mage from 'mage'\nimport { Type, IsInt, Max, ValidateNested, Acl } from 'mage-validator'\nimport PlayerData from '../types/PlayerData'\n\nexport default class {\n    @IsInt()\n    @Min(1)\n    public gemRegisterBonus: number\n\n    @ValidateNested()\n    @Type(() =\u003e PlayerData)\n    public data: PlayerData\n\n    @Acl('*')\n    public static async execute(state: mage.core.IState, data: PlayerData, gemRegisterBonus: number) {\n        data.gems += gemRegisterBonus\n        return data\n    }\n}\n```\n\nIn this case, both `data` and `gemRegisterBonus` will be validated prior to execution, and\n`data` will be validated once again once it is returned.\n\n### Topics\n\n#### Topic definition\n\nTopics work the same way as types, except that they contain the topic \nconfiguration as static parameters:\n\n\u003e `./lib/modules/modulename/topics/Player.ts`\n\n```typescript\nimport { ValidatedTopic, ValidateNested, IsUUID, IsAlpha } from 'mage-validator';\nimport { Type } from 'class-transform';\nimport PlayerData from '../topics/PlayerData'\n\nclass Index {\n  @isUUID(5)\n  playerId: string\n}\n\nexport default class {\n    // Index configuration\n    public static readonly index = ['playerId']\n    public static readonly indexType = Index\n\n    // Vaults configuration (optional)\n    public static readonly vaults = {}\n    \n    // Attribute instances\n    @IsAlpha()\n    public name: string\n\n    @ValidateNested()\n    @Type(() =\u003e PlayerData)\n    public data: PlayerData\n}\n```\n\nIf you wish to use [tome](https://github.com/Wizcorp/node-tomes) instead, you can\nyou can extend the `ValidatedTomeTopic` class instead. Unlike normal `tomes`, you will\nsimply access and set object values directly (instead of using `assign`, `set`, `valueOf` \nand so on).\n\n#### Loading topics\n\n\u003e `./lib/modules/modulename/usercommands/getPlayer.ts`\n\n```typescript\nimport * as mage from 'mage'\nimport { Acl } from 'mage-validator'\nimport Player from '../topics/Player'\n\nexport default class {\n    @IsUUID(5)\n    public id: string\n\n    @Acl('*')\n    public static async execute(state: mage.core.IState, id: string) {\n        return await Player.get(state, { playerId: id })\n    }\n}\n```\n\n#### Storing topics\n\nTopic instances also work as state wrappers:\n\n\u003e `./lib/modules/modulename/usercommands/createPlayer.ts`\n\n```typescript\nimport * as mage from 'mage'\nimport { Acl } from 'mage-validator'\nimport PlayerData from '../types/PlayerData'\nimport Player from '../topics/Player'\n\nexport default class {\n    @Acl('*')\n    public static async execute(state: mage.core.IState, name: string) {\n        const player: Player = Player.create(state, { playerId: '123' })\n        player.name = name\n        player.data = new PlayerData()\n        player.data.gems = 5\n\n        return player.add() // Same a state.archivist.add()\n    }\n}\n```\n\nIn this particular case, you could even simply put the player\ntopic directly as you user command parameter; all you will \nthen need to do is to set the index at some point before you attempt\nto record any operations:\n\n#### Topics as user command parameters\n\n\u003e `./lib/modules/modulename/usercommands/createPlayer.ts`\n\n```typescript\nimport * as mage from 'mage'\nimport { ValidateNested, Type, Acl } from 'mage-validator'\nimport Player from '../topics/Player'\n\nexport default class {\n    @ValidateNested()\n    @Type(() =\u003e Player)\n    public player: Player\n\n    @Acl('*')\n    public static async execute(state: mage.core.IState, player: Player) {\n        player.add()\n        return player\n    }\n}\n```\n\nLicense\n-------\n\nMIT.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmage%2Fmage-validator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmage%2Fmage-validator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmage%2Fmage-validator/lists"}