{"id":13683598,"url":"https://github.com/rawmodel/framework","last_synced_at":"2025-04-07T10:22:15.487Z","repository":{"id":41559685,"uuid":"67490535","full_name":"rawmodel/framework","owner":"rawmodel","description":"Strongly-typed JavaScript object with support for validation and error handling.","archived":false,"fork":false,"pushed_at":"2020-10-23T07:40:01.000Z","size":1453,"stargazers_count":143,"open_issues_count":2,"forks_count":16,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-03-31T09:06:02.860Z","etag":null,"topics":["backend","casting","db","error-handling","frontend","graphql","hacktoberfest","javascript","json","model","nested-models","nodejs","object","odm","orm","schema","type","typescript","unopinionated","validation"],"latest_commit_sha":null,"homepage":"https://rawmodel.github.io/framework/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rawmodel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"xpepermint","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2016-09-06T08:46:17.000Z","updated_at":"2024-11-24T04:16:26.000Z","dependencies_parsed_at":"2022-08-26T05:51:28.130Z","dependency_job_id":null,"html_url":"https://github.com/rawmodel/framework","commit_stats":null,"previous_names":["xpepermint/contextablejs","xpepermint/rawmodeljs"],"tags_count":43,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rawmodel%2Fframework","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rawmodel%2Fframework/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rawmodel%2Fframework/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rawmodel%2Fframework/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rawmodel","download_url":"https://codeload.github.com/rawmodel/framework/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247595326,"owners_count":20963943,"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":["backend","casting","db","error-handling","frontend","graphql","hacktoberfest","javascript","json","model","nested-models","nodejs","object","odm","orm","schema","type","typescript","unopinionated","validation"],"created_at":"2024-08-02T13:02:17.382Z","updated_at":"2025-04-07T10:22:15.464Z","avatar_url":"https://github.com/rawmodel.png","language":"TypeScript","readme":"# Rawmodel Framework\n\n[![Build Status](https://travis-ci.org/rawmodel/framework.svg?branch=master)](https://travis-ci.org/rawmodel/framework)\u0026nbsp;[![codecov](https://codecov.io/gh/rawmodel/framework/branch/master/graph/badge.svg)](https://codecov.io/gh/rawmodel/framework)\n\nRawmodel is a strongly-typed JavaScript object with support for validation and error handling. It's a lightweight open source framework for the **server** and **browser** (using module bundler), written with [TypeScript](https://www.typescriptlang.org). It's actively maintained, well tested and already used in production environments. The source code is available on [GitHub](https://github.com/rawmodel/framework) where you can also find our [issue tracker](https://github.com/rawmodel/framework/issues).\n\n## Introduction\n\nRawmodel provides a mechanism for creating strongly-typed data objects with built-in logic for unified data validation and error handling. It has a simple and intuitive API and tends to be a powerful, magic-free, minimalistic and unopinionated framework for writing application data layers where you have a complete control. It could be a perfect fit when writing an [Express.js](http://expressjs.com/) action, [GraphQL](http://graphql.org/) resolver or similar and it's easily extendable.\n\n## Installation\n\nRun the command below to install the package.\n\n```\n$ npm install --save @rawmodel/core\n$ npm install --save @rawmodel/handlers // OPTIONAL\n$ npm install --save @rawmodel/parsers // OPTIONAL\n$ npm install --save @rawmodel/schema // OPTIONAL\n$ npm install --save @rawmodel/validators // OPTIONAL\n```\n\nThis package uses promises thus you need to use [Promise polyfill](https://github.com/taylorhakes/promise-polyfill) when promises are not supported.\n\n## Example\n\nThe code below shows a basic usage example.\n\n```ts\nimport { Model, prop } from '@rawmodel/core';\n\n// defining a basic model\nclass User extends Model {\n  @prop()\n  public name: string;\n}\n\n// usage example\nconst model = new User({\n  'name': 'John Smith',\n});\nmodel.name; // =\u003e 'John Smith'\n```\n\n## Usage\n\nBelow we explain some of the most important features that this framework provides. Please check the API section to see a complete list of features.\n\n### Defining Props\n\nModel properties are defined using the `prop` ES6 decorator. The code below is an example of a basic model class with a `name` property.\n\n```ts\nimport { Model, prop } from '@rawmodel/core';\n\nclass User extends Model {\n  @prop()\n  public name: string;\n}\n\nconst user = new User();\nuser.name = 'John Smith';\nuser.name; // -\u003e \"John Smith\"\n```\n\n### Type Casting\n\nEach property has a built-in system for type casting, thus we can force a value to be automatically converted to a specific type when setting a value.\n\n```ts\nimport { ParserKind } from '@rawmodel/core';\nimport { stringParser } from '@rawmodel/parsers';\n\nclass User extends Model {\n  @prop({\n    parser: {\n      resolver: stringParser(),\n    },\n  })\n  public name: string;\n}\n```\n\nCommon types are supported by default. A `Model` also represents a type and you can create your own parsers when needed. Please see the API section for further details.\n\n### Nested Models\n\nAs mentioned above, a model class is already a type. This way you can create complex nested structures by nesting models as shown in the example below.\n\n```ts\nimport { Model, ParserKind, prop } from '@rawmodel/core';\n\nclass Address extends Model {\n  @prop()\n  public country: string;\n}\n\nclass Friend extends Model {\n  @prop()\n  public name: string;\n}\n\nclass User extends Model {\n  @prop({\n    parser: {\n      resolver: Address,\n    },\n  })\n  public address: Address;\n  @prop({\n    parser: {\n      array: true,\n      resolver: Friend,\n    },\n  })\n  public friends: Friend[];\n}\n```\n\n### Prop Default Value\n\nWe can set a `defaultValue` for each property which will automatically populate a property on creation.\n\nThe `defaultValue` can also be a method which returns a dynamic value. This function shares the context of the associated model.\n\n```ts\n@prop({\n  defaultValue() { return new Date() },\n})\npublic now: string;\n```\n\n### Prop Fake Value\n\nSimilar to default values, we can set a `fakeValue` for each property, to populate a property with fake data when calling the `fake()` method. This is useful when writting automated tests.\n\nThe `fakeValue` can also be a method which returns a dynamic value. This function shares the context of the associated model.\n\n```ts\n@prop({\n  fakeValue() { return new Date() },\n})\npublic today: string;\n```\n\n### Prop Empty Value\n\nBy default, all defined properties are set to `null`. Similar to default and fake values we can set an `emptyValue` option for each property, to automatically replace `null` values.\n\nThe `emptyValue` can also be a method which returns a dynamic value. This function shares the context of the associated model.\n\n```ts\n@prop({\n  emptyValue() { return '' },\n})\npublic name: string;\n```\n\n### Prop Value Transformation\n\nA property can have a custom `getter` and a custom `setter`. This function shares the context of the associated model.\n\n```ts\n@prop({\n  getter(value) { return value },\n  setter(value) { return value },\n})\npublic name: string;\n```\n\n### Value Assignments\n\nModel's properties are like properties of a Javascript Object. We can easily assign a value to a property through its setter method (e.g. `model.name = 'value';`). Instead of assigning properties one by one, we can use the `populate()` method to assign values to multiple enumerable properties.\n\n```ts\nmodel.populate({\n  'name': 'John Smith',\n  'age': 35,\n});\n```\n\nWe can allow only selected properties to be populated by using population strategies (e.g. useful when populating data received from a form).\n\n```ts\nclass User extends Model {\n  @prop({\n    populatable: ['internal'], // list population strategy names\n  })\n  public id: string;\n  @prop({\n    populatable: ['input', 'internal'], // list population strategy names\n  })\n  public name: string;\n}\n\nconst data = {\n  'id': 100,\n  'name': 'John Smith'\n};\nconst user = new User();\nuser.populate(data); // -\u003e { \"id\": 100, \"name\": \"John Smith\" }\nuser.populate(data, 'internal'); // -\u003e { \"id\": 100, \"name\": \"John Smith\" }\nuser.serialize(data, 'input'); // -\u003e { id: null, \"name\": \"John Smith\" }\n```\n\nModel properties also support dynamic data assignments. In translation, this means that we can populate a property using a function that shares the context of the associated model and is realized on property assignment.\n\n```ts\nuser.name = () =\u003e 'Join';\n```\n\nIt's encouraged to use the `populate()` method for assigning values unless you know how RawModel works in-depth. Adding items to an array through the native `push` method, directly assigning model instances and similar data manipulation can lead to strange effects.\n\n### Serialization \u0026 Filtering\n\nModel provides useful methods for object serialization and filtering. All enumerable properties are serializable by default and are thus included in the result object returned by the `serialize()` method. We can customize the output and include or exclude properties for different situations by using serialization strategies.\n\n```ts\nclass User extends Model {\n  @prop({\n    serializable: ['output'], // list serialization strategy names\n  })\n  public id: string;\n  @prop({\n    serializable: ['input', 'output'], // list serialization strategy names\n  })\n  public name: string;\n}\n\nconst user = new User({\n  'id': 100,\n  'name': 'John Smith',\n});\nuser.serialize(); // -\u003e { \"id\": 100, \"name\": \"John Smith\" }\nuser.serialize('input'); // -\u003e { \"name\": \"John Smith\" }\nuser.serialize('output'); // -\u003e { \"id\": 100, \"name\": \"John Smith\" }\n```\n\nA model can also be serialized into an array by using the `flatten()` method. We can thus easily scroll through all model values in a loop. The method also supports strategies thus we can customize the output and include or exclude properties for different situations.\n\n```ts\nuser.flatten(); // [{ path, value, prop }, ...]\nuser.flatten('input');\nuser.flatten('output');\n```\n\n### Commits \u0026 Rollbacks\n\nRawModel tracks changes for all properties and provides a mechanism for committing values and rollbacks.\n\n```ts\nclass User extends Model {\n  @prop()\n  public name: string;\n}\n\nconst user = new User();\nuser.name = 'Mandy Taylor'; // changing property's value\nuser.isChanged(); // -\u003e true\nuser.commit(); // set `initialValue` of each property to the value of  `value`\nuser.isChanged(); // -\u003e false\nuser.name = 'Tina Fey'; // changing property's value\nuser.rollback(); // -\u003e reset `value` of each property to its `initialValue` (last committed value)\n```\n\nNote that the `commit` method will memorize a serialized data and the `rollback` method will apply it back. Assigning functions or instances to properties is discourages.\n\n### Validation\n\nRawModel provides a simple mechanism for validating properties. All validators shares the context of the associated model.\n\n```ts\nclass User extends Model {\n  @prop({\n    validators: [ // property validation setup\n      { // validator recipe\n        resolver(v) { return !!v }, // [required] validator function\n        code: 422, // [optional] error code\n      },\n    ],\n  })\n  public name: string;\n}\n\nconst user = new User();\nuser.validate().catch((err) =\u003e {\n  user.collectErrors(); // -\u003e [{ path: ['name'], code: 422 }]\n});\n```\n\n### Error Handling\n\nRawModel provides a mechanism for handling property-related errors. The logic is aligned with the validation thus the validation and error handling can easily be managed in a unified way. This is great because we always deal with validation errors and can thus directly send these errors back to a user in a unified format. All handlers shares the context of the associated model.\n\n```ts\nclass User extends Model {\n  @prop({\n    handlers: [ // property error handling setup\n      { // handler recipe\n        resolver(e) { return e.message === 'foo' }, // [required] error resolve function\n        code: 31000, // [optional] error code\n      },\n    ],\n  })\n  public name: string;\n}\n\nconst error = new Error();\nconst user = new User();\nuser.handle(error).then(() =\u003e {\n  user.collectErrors(); // -\u003e [{ path: ['name'], code: 31000 }]\n});\n```\n\nThis mechanism is especially handful when saving data to a database. MongoDB database, for example, throws a uniqueness error (E11000) if we try to insert a value that already exists in the database. We can catch that error by using the `handle()` method and then return a unified validation error message to a user.\n\n### Raw Schema\n\n[JSON Schema](https://json-schema.org) is a pretty popular standard for describing JSON objects. It's sufficient for general use cases, but it's not powerful enough to cover all RawModel features. RawModel provides its own schema syntax which allows for the creation of generic models from a JSON definition.\n\nWe use `createModelClass` method to generate a new generic model class from a JSON definition. A model with a single property `name` could look something like this:\n\n```ts\nimport { createModelClass } from '@rawmodel/schema';\n\nconst schema = { // raw model schema\n  props: [ // properties definition\n    {\n      name: 'email', // property name\n    },\n  ],\n};\n\nconst Model = createModelClass(schema); // creates model class\n```\n\nWe can define static or dynamic default values. Dynamic values must have a resolver under the `defaultValues` option. If the property's `defaultValue` matches the resolver name, then the dynamic resolver is applied, otherwise, the static value of the `defaultValue` is copied. Similar logic applies to getters, setters, fake and empty values.\n\n```ts\nconst schema = {\n  defaultValues: {\n    currentDate() { return new Date() },\n  },\n  props: [\n    {\n      name: 'email',\n      defaultValue: 'Noname',\n    },\n    {\n      name: 'date',\n      defaultValue: 'currentDate', // referencing currentDate()\n    },\n  ],\n};\n```\n\nValidators and handlers can be defined in a similar way.\n\n```ts\nimport { stringLengthValidator } from '@rawmodel/validators';\n\nconst schema = {\n  validators: { // schema validators\n    stringLength: stringLengthValidator, // validator resolver\n  },\n  props: [ // schema properties\n    { // property definition\n      name: 'title', // property name\n      validators: [\n        {\n          resolver: 'stringLength', // validator resolver name\n          code: 30001, // validation error code\n          options: { min: 5 }, // validator arguments\n        },\n      ],\n    },\n  ],\n};\n```\n\nSchema supports basically all RawModel features. Check the API section for all the details.\n\n### GraphQL\n\nRawModel can be a perfect framework for writing GraphQL resolvers. An instance of a root model, in our case the `App` class, can represent GraphQL's `rootValue`.\n\n```ts\nimport { Model } from '@rawmodel/core';\nimport { graphql, buildSchema } from 'graphql';\n\nclass App extends Model { // root resolver\n  public hello() { // `hello` property resolver\n    return 'Hello World!';\n  }\n}\n\nconst schema = buildSchema(`\n  type Query {\n    hello: String\n  }\n`);\n\nconst root = new App(); // root resolver\n\ngraphql(schema, '{ hello }', root).then((response) =\u003e {\n  console.log(response);\n});\n```\n\n## API\n\n### @rawmodel/core\n\n**createModelClass(config)**\n\n\u003e Create the Model class from a list of property definitions.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| config.$.name | String | Yes | - | Property name.\n| config.$.prop.setter | Function | No | - | Custom setter.\n| config.$.prop.getter | Function | No | - | Custom getter.\n| config.$.prop.parser | Parser | No | - | Data type parser (see supported types).\n| config.$.prop.defaultValue | Any | No | - | Prop default value.\n| config.$.prop.fakeValue | Any | No | - | Prop fake value.\n| config.$.prop.emptyValue | Any | No | - | Prop empty value.\n| config.$.prop.validators | Array | No | - | List of validator recipes.\n| config.$.prop.handlers | Array | No | - | List of error handler recipes.\n| config.$.prop.populatable | String[] | No | - | List of strategies for populating the property value.\n| config.$.prop.serializable | String[] | No | - | List of strategies for serializing the property value.\n| config.$.prop.enumerable | Boolean | No | true | Indicates that the property is enumerable.\n\n```ts\nconst Model = createModelClass([\n  {\n    name: 'name',\n    prop: {\n      defaultValue: 'John Smith',\n    },\n  },\n]);\n```\n\n**Model\u003cContext\u003e(data, config)**\n\n\u003e Abstract class which represents a strongly-typed JavaScript object.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| data | Any | No | - | Data for populating model properties.\n| config.context | Any | No | - | Arbitrary context data.\n| config.parent | Model | Only when used as a submodel | - | Parent model instance.\n\n```ts\nclass User extends Model {\n  @prop({\n    set(v) { return v; }, // [optional] custom setter\n    get(v) { return v; }, // [optional] custom getter\n    parser: { // [optional] property type casting\n      array: true, // [optional] forces to array conversion when `true`\n      resolver: User, // [optional] parser function or Model\n    },\n    defaultValue: 'Noname', // [optional] property default value (value or function)\n    fakeValue: 'Noname', // [optional] property fake value (value or function)\n    emptyValue: '', // [optional] property empty value (value or function)\n    validators: [ // [optional] value validator recipes\n      { // validator recipe (check validatable.js for more)\n        resolver(v) { return !!v; }, // [required] validator resolve function (supports async)\n        code: 422, // [optional] error code\n      },\n    ],\n    handlers: [ // [optional] error handling recipies\n      { // handler recipe\n        resolver(e) { return e.message === 'foo'; }, // [required] handler resolve function (supports async)\n        code: 31000, // [required] error code\n      },\n    ],\n    populatable: ['input', 'internal'], // [optional] population strategies\n    serializable: ['input', 'output'], // [optional] serialization strategies\n    enumerable: true, // [optional] when set to `false` the property is not enumerable (ignored by `Object.keys()`)\n  })\n  public name: string; // [required] typescript property definition\n}\n```\n\n**Model.@prop(config)**\n\n\u003e Model property decorator\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| config.setter | Function | No | - | Custom setter.\n| config.getter | Function | No | - | Custom getter.\n| config.parser | Parser | No | - | Data type parser (see supported types).\n| config.defaultValue | Any | No | - | Prop default value.\n| config.fakeValue | Any | No | - | Prop fake value.\n| config.emptyValue | Any | No | - | Prop empty value.\n| config.validators | Array | No | - | List of validator recipes.\n| config.handlers | Array | No | - | List of error handler recipes.\n| config.populatable | String[] | No | - | List of strategies for populating the property value.\n| config.serializable | String[] | No | - | List of strategies for serializing the property value.\n| config.enumerable | Boolean | No | true | Indicates that the property is enumerable.\n\n**Model.prototype.__config**: Object\n\n\u003e Model configuration data.\n\n**Model.prototype.__props**: Object\n\n\u003e Model property instances.\n\n**Model.prototype.applyErrors(errors)**: Model\n\n\u003e Deeply populates properties with the provided `errors`.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| errors.$.path | Array | Yes | - | Property path array.\n| errors.$.code | Integer | Yes | - | Error code.\n\n```ts\nmodel.applyErrors([\n  {\n    path: ['books', 1, 'title'], // property path\n    code: 422, // error code\n  },\n]);\n```\n\n**Model.prototype.clone(data)**: Model\n\n\u003e Returns a new Model instance which is the exact copy of the original.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| data | Object | No | - | Data to override initial data.\n\n**Model.prototype.collectErrors()**: Array\n\n\u003e Returns a list of errors for all the properties ({path, code}[]).\n\n```ts\nmodel.collectErrors(); // =\u003e { path: ['name'], code: 300 }\n```\n\n**Model.prototype.commit()**: Model\n\n\u003e Sets initial value of each model property to the current value of a property. This is how property change tracking is restarted.\n\n**Model.prototype.empty()**: Model\n\n\u003e Sets all model properties to `null` or other empty values.\n\n**Model.prototype.fake()**: Model\n\n\u003e Sets each model property to its fake value if the fake value generator is defined.\n\n**Model.prototype.flatten(strategy)**: Array\n\n\u003e Converts the model into an array of properties.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| strategy | String | No | - | When the strategy name is provided, the output will include only the properties where the `serializable` option includes this strategy name. If the parameter is not provided then all properties are included in the result.\n\n```ts\nuser.flatten(); // -\u003e [{ path, prop, value }, ...]\n```\n\n**Model.prototype.freeze()**: Model\n\n\u003e Makes each model property not settable.\n\n**Model.prototype.getAncestors()**: Model[]\n\n\u003e Returns a list of all parent model instances.\n\n**Model.prototype.getContext()**: Context\n\n\u003e Returns model context data.\n\n**Model.prototype.getParent()**: Model\n\n\u003e Returns the parent model instance in a tree of models.\n\n**Model.prototype.getProp(...keys)**: Prop\n\n\u003e Returns a class instance of a property at path. Note that array values do not have properties but refer to object property.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| keys | Array | Yes | - | Path to a property (e.g. `['book', 0, 'title']`).\n\n**Model.prototype.handle(error, { quiet }): Promise(Model)**\n\n\u003e Tries to handle the `error` against each property handlers and populates the model with possible errors.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| error | Any | Yes | - | Error to be handled.\n| quiet | Boolean | No | true | When set to `false`, a handled validation error is thrown. This doesn't affect the unhandled errors (they are always thrown).\n\n```ts\ntry {\n  await model.validate(); // imagine it throws an error\n} catch (e) {\n  await model.handle(e);\n}\n```\n\n**Model.prototype.hasProp(...keys)**: Boolean\n\n\u003e Returns `true` when a property path exists. Note that array values do not have properties but refer to object property.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| keys | Array | Yes | - | Path to a property (e.g. `['book', 0, 'title']`).\n\n**Model.prototype.isChanged()**: Boolean\n\n\u003e Returns `true` if at least one model property has been changed.\n\n**Model.prototype.isEqual(value)**: Boolean\n\n\u003e Returns `true` when the provided `value` represents an object with the same properties as the model itself.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| value | Any | Yes | - | Arbitrary value.\n\n**Model.prototype.isValid()**: Boolean\n\n\u003e Returns `true` when all model properties are valid. Make sure that you call the `validate()` method first.\n\n**Model.prototype.invalidate()**: Model\n\n\u003e Clears `errors` on all properties.\n\n**Model.prototype.populate(data, strategy)**: Model\n\n\u003e Populates enumerable properties with data.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| data | Object | Yes | - | Data object.\n| strategy | String | No | - | When the strategy name is provided, only the properties where the `populatable` option includes this strategy name are populated. If the parameter is not provided then all properties are included in the process.\n\n**Model.prototype.reset()**: Model\n\n\u003e Sets each model property to its default value.\n\n**Model.prototype.rollback()**: Model\n\n\u003e Sets each model property to its initial value (last committed value). This is how you can discharge model changes.\n\n**Model.prototype.serialize(strategy)**: Object\n\n\u003e Converts a model into serialized data object. The result will include only enumerable properties.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| strategy | String | No | - | When the strategy name is provided, the output will include only the properties where the `serializable` option includes this strategy name. If the parameter is not provided then all properties are included in the result.\n\n**Model.prototype.validate({ quiet })**: Promise(Model)\n\n\u003e Validates model properties, populates the model with possible errors and throws a validation error if not all properties are valid unless the `quiet` is set to `true`.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| quiet | Boolean | No | true | When set to `false`, a validation error is thrown.\n\n```ts\ntry {\n  await model.validate(); // throws a validation error when invalid properties exist\n} catch (e) {\n  // `e` is a 422 validation error\n}\n```\n\n**Prop(config)**\n\n\u003e A model property.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| config.setter | Function | No | - | Custom setter.\n| config.getter | Function | No | - | Custom getter.\n| config.parser | Parser | No | - | Data type parser (see supported types).\n| config.defaultValue | Any | No | - | Prop default value.\n| config.fakeValue | Any | No | - | Prop fake value.\n| config.emptyValue | Any | No | - | Prop empty value.\n| config.validators | Array | No | - | List of validator recipes.\n| config.handlers | Array | No | - | List of error handler recipes.\n| config.populatable | String[] | No | - | List of strategies for populating the property value.\n| config.serializable | String[] | No | - | List of strategies for serializing the property value.\n| config.enumerable | Boolean | No | true | Indicates that the property is enumerable.\n| config.model | Model | No | null | Parent model instance.\n\n**Prop.prototype.__config**: Object\n\n\u003e Property configuration object.\n\n**Prop.prototype.empty()**: Prop\n\n\u003e Sets property and related sub-properties to `null`.\n\n**Prop.prototype.commit()**: Prop\n\n\u003e Sets initial value to the current value. This is how property change tracking is restarted.\n\n**Prop.prototype.fake()**: Prop\n\n\u003e Sets property to a generated fake value.\n\n**Prop.prototype.freeze()**: Prop\n\n\u003e Makes property not settable.\n\n**Prop.prototype.getErrorCode()**: Number\n\n\u003e Returns property error code (sets the `validate` method).\n\n**Prop.prototype.getInitialValue()**: Any\n\n\u003e Returns property initial value.\n\n**Prop.prototype.getValue()**: Any\n\n\u003e Returns current property value.\n\n**Prop.prototype.getRawValue()**: Any\n\n\u003e Returns current property raw value.\n\n**Prop.prototype.handle(error)**: Promise(Prop)\n\n\u003e Handles the `error` and populates the property with error.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| error | Any | Yes | - | Error to be handled.\n\n**Prop.prototype.isArray()**: Boolean\n\n\u003e Returns `true` if the property is an array.\n\n**Prop.prototype.isEmpty()**: Boolean\n\n\u003e Returns `true` if the property has no value.\n\n**Prop.prototype.isEqual(value)**: Boolean\n\n\u003e Returns `true` when the provided `value` represents an object that looks the same.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| value | Any | Yes | - | A value to compare with.\n\n**Prop.prototype.isChanged()**: Boolean\n\n\u003e Returns `true` if the property or at least one sub-property have been changed.\n\n**Prop.prototype.isPopulatable(strategy)**: Boolean\n\n\u003e Returns `true` if the property can be set.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| strategy | String | No | - | Populating strategy.\n\n**Prop.prototype.isSerializable(strategy)**: Boolean\n\n\u003e Returns `true` if the property can be serialized.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| strategy | String | No | - | Serialization strategy.\n\n**Prop.prototype.isValid()**: Boolean\n\n\u003e Returns `true` if the property and all sub-properties are valid (inverse of `hasErrors()`). Make sure that you call the `validate()` method first.\n\n**Prop.prototype.invalidate()**: Prop\n\n\u003e Clears the property error (the reverse of `validate()`).\n\n**Prop.prototype.reset()**: Prop\n\n\u003e Sets the property to its default value.\n\n**Prop.prototype.rollback()**: Prop\n\n\u003e Sets the property to its initial value (last committed value). This is how you can discharge property's changes.\n\n**Prop.prototype.serialize(strategy)**\n\n\u003e Returns a serialized property value. Note that only enumerable properties are serializable.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| strategy | String | No | - | Serialization strategy.\n\n**Prop.prototype.setValue(value)**\n\n\u003e Sets current property value.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| value | String | Yes | - | Arbitrary value.\n\n**Prop.prototype.validate()**: Promise(Prop)\n\n\u003e Validates the `value` and populates the property with error.\n\n### @rawmodel/schema\n\n**createModelClass(recipe):Class**\n\n\u003e Returns a new generic model class build from the provided schema `recipe`.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| recipe.getters | Object | No | - | Hash of getter functions which return a resolver.\n| recipe.setters | Object | No | - | Hash of setters functions which return a resolver.\n| recipe.defaultValues | Object | No | - | Hash of default value functions which return a resolver or static values.\n| recipe.fakeValues | Object | No | - | Hash of fake value functions which return a resolver or static values.\n| recipe.emptyValues | Object | No | - | Hash of empty value functions which return a resolver or static values.\n| recipe.parsers | Object | No | - | Hash of parser functions which return a resolver.\n| recipe.validators | Object | No | - | Hash of validator functions which return a resolver.\n| recipe.handlers | Object | No | - | Hash of handler functions which return a resolver.\n| recipe.props | Array | No | - | Hash of property definitions.\n| recipe.props.$.set | String | No | - | Setter resolver name.\n| recipe.props.$.get | String | No | - | Getter resolver name.\n| recipe.props.$.parser | Object | No | - | Data type parser recipe.\n| recipe.props.$.parser.array | Boolean | No | false | When `true` the input data will automatically be converted to array.\n| recipe.props.$.parser.resolver | String | No | - | Parser resolver name\n| recipe.props.$.defaultValue | Any | No | - | Default value resolver name or a value.\n| recipe.props.$.fakeValue | Any | No | - | Fake value resolver name or a value.\n| recipe.props.$.emptyValue | Any | No | - | Empty value resolver name or a value.\n| recipe.props.$.validators | Array | No | - | List of validator recipes.\n| recipe.props.$.validators.code | Integer | Yes | - | Validator error code.\n| recipe.props.$.validators.resolver | String | Yes | - | Validator resolver name.\n| recipe.props.$.validators.options | Object | No | - | Validator resolver arguments.\n| recipe.props.$.handlers | Array | No | - | List of error handler recipes.\n| recipe.props.$.handlers.code | Integer | Yes | - | Handler error code.\n| recipe.props.$.handlers.resolver | String | Yes | - | Handler resolver name.\n| recipe.props.$.handlers.options | Object | No | - | Handler resolver arguments.\n| recipe.props.$.populatable | Array | No | - | List of strategies for populating the property value.\n| recipe.props.$.serializable | Array | No | - | List of strategies for serializing the property value.\n| recipe.props.$.enumerable | Boolean | No | true | Indicates that the property is enumerable.\n\n```ts\nconst Model = createModelClass({\n  getters: {\n    customGetter(options: any) { // custom getter function which returns a resolver\n      return function(v: any) { return v; } // context aware resolver\n    },\n  },\n  setters: {}, // see getters\n  defaultValues: {\n    dynamicValue(options: any) { // custom default value function which returns a resolver\n      return function(v: any) { return v; } // context aware resolver\n    },\n    staticValue(options: any) { // custom default value function which returns a static value\n      return 'foo';\n  },\n  fakeValues: {}, // see defaultValues\n  emptyValues: {}, // see defaultValues\n  parsers: {\n    toString() { // custom parser function which returns a resolver\n      return function(v: any) { return v.toString(); }; // context aware resolver\n    },\n  },\n  validators: {\n    isPresent() { // custom validator function which returns a resolver\n      return function(v: any) { return !!v }; // context aware resolver (function or promise)\n    },\n  };\n  handlers: {}, // see validators\n  props: [\n    name: 'firstName', // property name\n    getter: 'customGetter', // getter name (defined in `getters`)\n    setter: 'customSetter', // setter name (defined in `setters`)\n    parser: {\n      array: true, // when `true` the input is converted to array\n      resolver: 'toString', // parser resolver name\n    },\n    defaultValue: 'none', // static default value\n    fakeValue: 'none', // static fake value\n    emptyValue: '', // static empty value\n    validators: [\n      {\n        code: 30001, // validator error code\n        resolver: 'isPresent', // validator resolver name\n      },\n    ],\n    handlers: [], // see validators\n    populatable: ['input', 'db'], // populatable strategies\n    serializable: ['input', 'db'], // serializable strategies\n    enumerable: true, // property is enumerable\n  ],\n});\n```\n\n### @rawmodel/parsers\n\n**NOTE:** Every model can be used as a parser `resolver`.\n\n**booleanParser()**: Function\n\n\u003e Converts a value to a boolean value.\n\n```ts\nconst recipe = {\n  array: true, // optional\n  resolver: booleanParser(),\n}\n```\n\n **bsonObjectIdStringParser()**: Function\n\n\u003e Converts a value to a valid BSON ObjectId string.\n\n**dateParser()**: Function\n\n\u003e Converts a value to a date object.\n\n**floatParser()**: Function\n\n\u003e Converts a value to a decimal number.\n\n**integerParser()**: Function\n\n\u003e Converts a value to an integer number.\n\n**stringParser**: Function\n\n\u003e Converts a value to a string.\n\n### @rawmodel/validators\n\nPlease note that the validators do not trigger if no value is present (on `undefined` or `null`). Make sure your custom validators follow the same concept. The exception are validators which verify value presence or absence.\n\n**absenceValidator()**: Function\n\n\u003e Validates that the specified property is blank.\n\n```ts\nimport { absenceValidator } from '@rawmodel/validators';\n\nconst recipe = {\n  resolver: absenceValidator(),\n  code: 422,\n};\n```\n\n**arrayExclusionValidator(options)**: Function\n\n\u003e Validates that the specified array property is not in an array of values.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| options.values | Array | Yes | - | Array of restricted values.\n\n**arrayInclusionValidator(options)**: Function\n\n\u003e Validates that the specified array property is in an array of values.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| options.values | Array | Yes | - | Array of allowed values.\n\n**arrayLengthValidator(options)**: Function\n\n\u003e Validates the size of an array.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| options.min | Number | No | - | Allowed minimum items count.\n| options.minOrEqual | Number | No | - | Allowed minimum items count (allowing equal).\n| options.max | Number | No | - | Allowed maximum items count.\n| options.maxOrEqual | Number | No | - | Allowed maximum items count (allowing equal).\n\n**base64Validator()**: Function\n\n\u003e Validates that the specified property is base64 encoded string.\n\n**bsonObjectIdValidator()**: Function\n\n\u003e Validates that the specified property is BSON ObjectId encoded string.\n\n**dateValidator(options)**: Function\n\n\u003e Validates that the specified property is a date string.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|----------|-----------\n| options.iso | Boolean | No | false | When `true` only ISO-8601 date format is accepted.\n\n**downcaseStringValidator()**: Function\n\n\u003e Validates that the specified property is lowercase.\n\n**emailValidator(options)**: Function\n\n\u003e Validates that the specified property is an email.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| options.allowDisplayName | Boolean | No | false | When set to true, the validator will also match `name \u003caddress\u003e`.\n| options.allowUtf8LocalPart | Boolean | No | false | When set to false, the validator will not allow any non-English UTF8 character in email address' local part.\n| options.requireTld | Boolean | No | true | When set to false, email addresses without having TLD in their domain will also be matched.\n\n**ethAddressValidator()**: Function\n\n\u003e Checks if the string represents an Ethereum address.\n\n**exclusionValidator(options)**: Function\n\n\u003e Validates that the specified property is not in an array of values.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| options.values | Array | Yes | - | Array of restricted values.\n\n**fqdnValidator(options)**: Function\n\n\u003e Validates that the specified property is a fully qualified domain name (e.g. domain.com).\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| options.requireTld | Boolean | No | true | Require top-level domain name.\n| options.allowUnderscores | Boolean | No | false | Allow string to include underscores.\n| options.allowTrailingDot | Boolean | No | false | Allow string to include a trailing dot.\n\n**hexColorValidator()**: Function\n\n\u003e Validates that the specified property is a hexadecimal color string.\n\n**hexValidator()**: Function\n\n\u003e Validates that a specified property is a hexadecimal number.\n\n**inclusionValidator(options)**: Function\n\n\u003e Validates that the specified property is in an array of values.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| options.values | Array | Yes | - | Array of allowed values.\n\n**jsonStringValidator(options)**: Function\n\n\u003e Validates that the specified property is a JSON string.\n\n**matchValidator(options)**: Function\n\n\u003e Validates that the specified property matches the pattern.\n\n| Key | Type | Required | Default | Description\n|-----|------|----------|---------|------------\n| options.regexp | RegExp | Yes | - | Regular expression pattern.\n\n**numberSizeValidator(options)**: Function\n\n\u003e Validates the size of a number.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| options.min | Number | No | - | Allowed minimum value.\n| options.minOrEqual | Number | No | - | Allowed minimum value (allowing equal).\n| options.max | Number | No | - | Allowed maximum value.\n| options.maxOrEqual | Number | No | - | Allowed maximum value (allowing equal).\n\n**presenceValidator()**: Function\n\n\u003e Validates that the specified property is not blank.\n\n**stringExclusionValidator(options)**: Function\n\n\u003e Checks if the string does not contain the seed.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| options.seed | String | Yes | - | The seed which should exist in the string.\n\n**stringInclusionValidator()**: Function\n\n\u003e Checks if the string contains the seed.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| options.seed | String | Yes | - | The seed which should exist in the string.\n\n**stringLengthValidator(options)**: Function\n\n\u003e Validates the length of the specified property.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| options.bytes | Boolean | No | false | When `true` the number of bytes is returned.\n| options.min | Number | No | - | Allowed minimum number of characters.\n| options.minOrEqual | Number | No | - | Allowed minimum value number of characters (allowing equal).\n| options.max | Number | No | - | Allowed maximum number of characters.\n| options.maxOrEqual | Number | No | - | Allowed maximum number of characters (allowing equal).\n\n**upcaseStringValidator()**: Function\n\n\u003e Validates that the specified property is uppercase.\n\n**uuidValidator(options)**: Function\n\n\u003e Validates that the specified property is a [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier).\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| options.version | Integer | No | - | UUID version (1, 2, 3, 4 or 5).\n\n### @rawmodel/handlers\n\n**mongoUniquenessHandler(options)**: Function\n\n\u003e Checks if the error represents a MongoDB unique constraint error.\n\n| Option | Type | Required | Default | Description\n|--------|------|----------|---------|------------\n| options.indexName | String | No | - | MongoDB collection's unique index name.\n\n```js\nimport { mongoUniquenessHandler } from '@rawmodel/handlers';\n\nconst recipe = { // make sure that this index name exists in your MongoDB collection\n  resolver: mongoUniquenessHandler({ indexName: 'uniqueEmail' }),\n  code: 422,\n};\n```\n\n## Packages\n\n| Package | Description | Version\n|-|-|-\n| [@rawmodel/core](https://github.com/rawmodel/framework/tree/master/packages/rawmodel-core) | Model and property classes. | [![NPM Version](https://badge.fury.io/js/@rawmodel%2Fcore.svg)](https://badge.fury.io/js/%40rawmodel%2Fcore)\n| [@rawmodel/handlers](https://github.com/rawmodel/framework/tree/master/packages/rawmodel-handlers) | Collection of error handlers. | [![NPM Version](https://badge.fury.io/js/@rawmodel%2Fhandlers.svg)](https://badge.fury.io/js/%40rawmodel%2Fhandlers)\n| [@rawmodel/parsers](https://github.com/rawmodel/framework/tree/master/packages/rawmodel-parsers) | Collection of data parsers. | [![NPM Version](https://badge.fury.io/js/@rawmodel%2Fparsers.svg)](https://badge.fury.io/js/%40rawmodel%2Fparsers)\n| [@rawmodel/schema](https://github.com/rawmodel/framework/tree/master/packages/rawmodel-schema) | Rawmodel schema definition utils. | [![NPM Version](https://badge.fury.io/js/@rawmodel%2Fschema.svg)](https://badge.fury.io/js/%40rawmodel%2Fschema)\n| [@rawmodel/utils](https://github.com/rawmodel/framework/tree/master/packages/rawmodel-utils) | Helper functions. | [![NPM Version](https://badge.fury.io/js/@rawmodel%2Futils.svg)](https://badge.fury.io/js/%40rawmodel%2Futils)\n| [@rawmodel/validators](https://github.com/rawmodel/framework/tree/master/packages/rawmodel-validators) | Collection of validators. | [![NPM Version](https://badge.fury.io/js/@rawmodel%2Fvalidators.svg)](https://badge.fury.io/js/%40rawmodel%2Fvalidators)\n\n## Contributing\n\nSee [CONTRIBUTING.md](https://github.com/rawmodel/framework/blob/master/CONTRIBUTING.md) for how to help out.\n\n## Licence\n\nSee [LICENSE](https://github.com/rawmodel/framework/blob/master/LICENCE) for details.\n","funding_links":["https://github.com/sponsors/xpepermint"],"categories":["TypeScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frawmodel%2Fframework","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frawmodel%2Fframework","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frawmodel%2Fframework/lists"}