{"id":25707708,"url":"https://github.com/jhsware/isomorphic-schema","last_synced_at":"2025-07-24T01:03:58.461Z","repository":{"id":30006274,"uuid":"33554279","full_name":"jhsware/isomorphic-schema","owner":"jhsware","description":"Isomorphic Javascript form validation library. Supports nested forms, rules for skipping validation of fields and multi-field validation. Has i18n support.","archived":false,"fork":false,"pushed_at":"2024-02-02T16:30:07.000Z","size":445,"stargazers_count":5,"open_issues_count":2,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-22T18:51:58.905Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/jhsware.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-04-07T16:18:37.000Z","updated_at":"2021-04-11T08:40:49.000Z","dependencies_parsed_at":"2024-02-02T17:48:47.274Z","dependency_job_id":null,"html_url":"https://github.com/jhsware/isomorphic-schema","commit_stats":null,"previous_names":[],"tags_count":54,"template":false,"template_full_name":null,"purl":"pkg:github/jhsware/isomorphic-schema","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jhsware%2Fisomorphic-schema","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jhsware%2Fisomorphic-schema/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jhsware%2Fisomorphic-schema/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jhsware%2Fisomorphic-schema/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jhsware","download_url":"https://codeload.github.com/jhsware/isomorphic-schema/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jhsware%2Fisomorphic-schema/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266775354,"owners_count":23982273,"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","status":"online","status_checked_at":"2025-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2025-02-25T08:38:37.253Z","updated_at":"2025-07-24T01:03:58.432Z","avatar_url":"https://github.com/jhsware.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Isomorphic Schema\n[![Build Status](https://travis-ci.org/jhsware/isomorphic-schema.svg?branch=master)](https://travis-ci.org/jhsware/isomorphic-schema)\n[![gzip size](http://img.badgesize.io/https://unpkg.com/isomorphic-schema/dist/cjs/index.min.js?compression=gzip)](https://unpkg.com/isomorphic-schema/dist/cjs/index.min.js)\n\n\nIsomorphic Javascript form validation library. Supports nested forms, rules for skipping validation of fields and multi-field validation. Has i18n support.\n\nField validators use object prototype mechanism from [component-registry](https://github.com/jhsware/component-registry \"component-registry\") to support inheritance.\n\nFor more examples, please check out the tests.\n\nIf you want to see automatic form generation based on isomorphic-schema form definitions head over to [inferno-formlib](https://github.com/jhsware/inferno-formlib). That library could easily be ported to React.js. There is a handlebars library available at [kth-node-formlib](https://github.com/KTH/kth-node-formlib).\n\n## Overview ###\n\nThe purpose of the isomorphic-schema form validation package is to help create great user experiences when working with forms and form validation. With simple and readable syntax it both works both as a single source of truth and documentation of forms and/or the complete data model.\n\nYou can choose to only use isomorphic-schema to define your forms, you can use it to define your entity objects or you can use it for both.\n\nChances are you will start by only using it for forms, where one schema matches a single form. However it can also be used to validate data that is stored in a document database such as MongoDB.\n\nBy using the component-registry concept of interfaces and object prototypes, it is possible to register widgets to render the form fields. This is very powerful when creating a form rendering library where the form generator is entirely decoupled from the field widget implementations. This is explained a bit more further down.\n\n## Schema ###\n\nA schema contains a collection of named field validators that define a form. The schema also contains methods to allow form level actions such as form validation.\n\n### Creating a Schema ####\nTo create a schema you make a new instance of the Schema object. There are two required paramters to pass to the constructor:\n\n1 A name used for debugging\n\n2 An object containing named fields\n\n```JavaScript\nvar Schema = require('isomorphic-schema').Schema;\nvar TextField = require('isomorphic-schema/lib/field_validators/TextField');\nvar simpleSchema = new Schema(\"MyThing Schema\", {\n    title: new TextField({\n        label: 'Title',\n        placeholder: 'Type here...',\n        required: true\n    }),\n    author: new TextField({\n        label: 'Author',\n        placeholder: 'Type here...',\n        required: true\n    }) \n})\n```\n\nEach field has a field validator that determines what data that field accepts. There are a bunch a fields in isomorphic-schema, but you can easily create your own fields to get custom behaviour.\n\nBesides field validators you can also add form level validation with invariants and validation constraints.\n\n### Inheritance ###\nSchema supports inheritance to allow you to compose your forms. Instead of adding the debug name as first parameter you pass an object:\n\n```JavaScript\nvar compositeSchema = new Schema({\n    schemaName: \"MyComposite Schema\",\n    extends: [simpleSchema],\n    fields: {\n        status: new TextField({\n            label: 'Status',\n            placeholder: 'Type here...',\n            required: true\n        })\n    }\n})\n```\n\nThe order in your extends array is important. The fields are added in the order of the extends array and in case of naming conflicts, fields in schemas to the right overwrite those to the left. Your schema specific fields are placed last.\n\nNote! validation constraints and invariants are also inherited. Se the \n\n### Invariants ####\nAn invariant is a test on the entire data of a form that always needs to evaluate as true. A classic invariant test is that password and confirm_password is a match, or that to_date is larger than from_date.\n\n```JavaScript\nsimpleSchema.addInvariant(function (data, selectedFields) {\n    var tmpFields = ['password', 'confirm_password'];\n\n    // Check that all the fields in this invariant are passed\n    for (var i in tmpFields) {\n        var key = tmpFields[i];\n        if (selectedFields.indexOf(key) \u003c 0) {\n            // The fields aren't supplied so we don't do the test\n            return\n        }\n    }\n    \n    if (data[tmpFields[0]] !== data[tmpFields[1]]) {\n        return {\n            message: \"The passwords don't match!\",\n            fields: tmpFields\n        }\n    }\n})\n```\n\nNote! Invariants should only evaluate if all the fields that are part of that test are passed in selectedFields. Both data and selectedFields are passed by the simpleSchema.validate method to the invariant validator function.\n\n### Validation Constraints ####\n\nValidation constraints are used to skip validation of one or more fields depending on what the passed data looks like. This is useful if you have large forms where some fields depend on the value of other fields. An example could be that a publish_date field only is validated if the staus field of the object is set to published.\n\n```JavaScript\nsimpleSchema.addValidationConstraint(function (data, fieldKey) {\n    // Don't render or validate author when no title is set\n    if (fieldKey === 'author') {\n        return data.title ? true : false; \n    } else {\n        return true;\n    }\n})\n```\n\nValidation constraints are very nice in browser rendered forms because they allow you to hide fields that only should be shown if the user has entered specific values, such as choosing \"other\" in a list of options in a questionaire reveals a text field where other is specified.\n\n### Validating Form Data ####\n\nOnce you have created an instance of a schema such as `simpleSchema` above you can use it to validate and transform data that you have received from the browser. The validation part checks that the provided input is valid, otherwise returning field level errors that are inteded to be displayed to the user. The transform part makes sure that data is converted to correct data types since the HTML-input fields normally return simple strings.\n\n```JavaScript\nvar errors = simpleSchema.validate(inputData)\n\n// or if you have async fields such as something based on DynamicSelectAsyncBaseField:\nsimpleSchema.validateAsync(inputData, options, context).then((errors) =\u003e {\n    \n})\n```\n\noptions -- (optional) an options object you could use to pass i18n props to a DynamicSelectField (WARNING! it is mutated internally)\n\ncontext -- (optional) an optional param passed to all fields. If you fetch options etc. in your controller you could pass these in the context object and access them from the options utility for a DynamicSelectField.\n\nWhen we call validate, the schema calls validate on each field and returns an error object if any data is determined to be invalid.\n\n```JavaScript\nvar errors = {\n    fieldErrors: {\n        [field-name]: { [field-error-object] }\n    },\n    invariantErrors: [\n        // list of invariant error objects (validation checks on more than one field at a time, such as 'password' === 'confirmPassword')\n    ]\n}\n```\n\nIf the form data passes validation we need transform the form data to proper datatypes, such as integers and real objects, before we can pass it on to our storage mechanism.\n\n```JavaScript\nvar outp = simpleSchema.transform(inputData)\n```\n\nThe passed data is converted to proper datatypes by calling the method `fromString` which is available on each field validator. The resulting object can then be sent to an API or a database.\n\n## Field Validators ###\n\nField validators define what values are valid. This is an overview of the field validators available in the base package.\n\n### Validator Inheritance Hierarchy\n```\nbaseField\n    |- decimalField\n    |- integerField\n    |- textField\n    |   |- dateField\n    |   |- dateTimeField\n    |   |- emailField\n    |   |- orgNrField\n    |   |- passwordField\n    |   |- textAreaField\n    |   |- HTMLAreaField\n    |- listField\n    |- objectField\n    |- objectRelationField\n    |- multiSelectField\n    |- selectField\n    |- DynamicSelectBaseField (abstract class, requires subclassing)\n    |   |- DynamicSelectAsyncBaseField (abstract class, requires subclassing)\n    |- creditCardField\n    |- anyOf\nboolField    \n```\n\nOverview of field options:\n\n### BaseField\n\n*NOTE:* ALL fields except BoolField inherit from BaseField, so all fields have the following options\n\n**required:** {boolean} this field is required (must evaluate to `val == true`)\n\n**readOnly:** {boolean} this is a read only field \n\n### TextField\n**required:** {boolean} this field is required (must evaluate to `val == true`)\n\n**readOnly:** {boolean} this is a read only field \n\n**minLength:** {integer} minimum number of chars\n\n**maxLength:** {integer} maximum number of chars\n\n### DateField\nChecks for a valid date format using moment.js.\n\n**required:** {boolean} this field is required (must evaluate to `val == true`)\n\n**readOnly:** {boolean} this is a read only field \n\n### DateTimeField\nChecks for a valid date and time format using moment.js.\n\n**required:** {boolean} this field is required (must evaluate to `val == true`)\n\n**readOnly:** {boolean} this is a read only field \n\n### EmailField\nChecks for a valid e-mail address.\n\n**required:** {boolean} this field is required (must evaluate to `val == true`)\n\n**readOnly:** {boolean} this is a read only field \n\n### OrgNrField\nChecks for a valid swedish social security number.\n\n**required:** {boolean} this field is required (must evaluate to `val == true`)\n\n**readOnly:** {boolean} this is a read only field \n\n### PasswordField\nBasically a text field that allows you to implement a password input field when rendering. \n\n**required:** {boolean} this field is required (must evaluate to `val == true`)\n\n**readOnly:** {boolean} this is a read only field \n\n**minLength:** {integer} minimum number of chars\n\n**maxLength:** {integer} maximum number of chars\n\n### TextAreaField\n**required:** {boolean} this field is required (must evaluate to `val == true`)\n\n**readOnly:** {boolean} this is a read only field \n\n**minLength:** {integer} minimum number of chars\n\n**maxLength:** {integer} maximum number of chars\n\n### IntegerField, DecimalField\n**required:** {boolean} this field is required (must evaluate to `val == true`)\n\n**readOnly:** {boolean} this is a read only field \n\n**min:** {integer} minimum value \n\n**max:** {integer} maximum value\n\n### SelectField, MultiselectField\n**required:** {boolean} this field is required (must evaluate to `val == true`)\n\n**readOnly:** {boolean} this is a read only field \n\n**valueType:** a validator where the type matches 'name' in options (usually `textField({})`)\n\n**options:** {array} list of option objects of the form {name: string, title: string}\n\n### DynamicSelectBaseField (Abstract class)\nBy subclassing DynamicSelectBaseField you can create a custom select field that generates options at runtime.\n\nAny options are specified by your implementation.\n\n### DynamicSelectAsyncBaseField (Abstract class)\nBy subclassing DynamicSelectAsyncBaseField you can create a custom select field that generates options at runtime through an API-call, DB-call or other async operation.\n\nAny options are specified by your implementation.\n\n### ObjectField\n**required:** {boolean} this field is required (must evaluate to `val == true`)\n\n**readOnly:** {boolean} this is a read only field \n\n**schema:** {object} another Schema object\n\n### ListField\n**required:** {boolean} this field is required (must evaluate to `val == true`)\n\n**readOnly:** {boolean} this is a read only field \n\n**valueType:** {object} a validator that matches the items in the list (can be a simple type such as `new TextField({})` or `new ObjectField({schema: ...})`)\n\n**minLength:** {integer} minimum number of items\n\n**maxLength:** {integer} maximum number of items\n\n### HTMLAreaField\n**required:** {boolean} this field is required (must evaluate to `val == true`)\n\n**readOnly:** {boolean} this is a read only field \n\n**minLength:** {integer} minimum number of chars when tags have been removed\n\n**maxLength:** {integer} maximum number of chars when tags have been removed\n\n### AnyOf\n**required:** {boolean} this field is required (must evaluate to `val == true`)\n\n**readOnly:** {boolean} this is a read only field \n\n**valueTypes:** {array} list of value types than are allowed\n\n```JavaScript\nvalidators.anyOf({\n    valueTypes: [\n        validators.textField({})\n    ]\n})\n```\n\n## Creating Custom Fields ###\n\nThere are three use cases where you will want to create a custom field.\n\n1. You want to allow your form rendering library to render a custom widget\n\n2. You need to create custom validation rules\n\n3. You need to transform the value in order to render it nicely\n\n### Creating a Dead Simple Field to Enable Custom Widget ####\n\nSince each field is identified by it's interface, we first create one.\n\n```JavaScript\nimport { createInterfaceClass } from 'component-registry');\nconst Interface = createInterfaceClass('MyTestApp')\n\nexport const IMySpecialField = new Interface({\n  name: 'IMySpecialField'\n})\n```\n\n**Note:** we need to export the created interface to allow our custom widget to register itself as renderer for this field. \n\n```JavaScript\nimport { createObjectPrototype } from 'component-registry');\nimport TextField from 'isomorphic-schema/lib/field_validators/TextField');\n\nexport const MySpecialField = createObjectPrototype({\n  implements: [IMySpecialField],\n  extends: [TextField]\n  // We don't change any functionality so we don't need to implement any methods\n  // to override the inherited TextField behaviour\n})\n```\n\nThis field extends TextField and thus inherits all the functionality of a TextField. Because of this inheritance it also inherits the ITextField interface. If we don't create a custom widget that renders IMySpecialField, this field will be rendered with the widget that renders ITextField.\n\n### Creating a Field With Custom Validation ####\n\nAgain you start by creating an interface. \n\n```JavaScript\nimport { createInterfaceClass } from 'component-registry');\nconst Interface = createInterfaceClass('MyTestApp')\n\nexport const IMySpecialValidationField = new Interface({\n  name: 'IMySpecialValidationField'\n})\n```\n\nNow we extend a field that has the basic behaviour we need and then add our custom validation.\n\n```JavaScript\nimport { createObjectPrototype } from 'component-registry');\nimport { i18n } from 'isomorphic-schema/lib/utils')\nconst MyRegex = /(\\d{4}-){3}\\d{4}/\n\nexport const MySpecialValidationField = createObjectPrototype({\n  implements: [IMySpecialValidationField],\n  extends: [TextField],\n  \n  validate: function (inp, options, context) {\n    // Call the TextField validate method to invoke the validation we inherited\n    var error = this._ITextField.validate.call(this, inp)\n    if (error) { return error }\n\n    // Implement our custom regex validation\n    if (inp !== undefined \u0026\u0026 !(MyRegex.test(inp) \u0026\u0026 !inp.length === 19)) {\n      error = {\n        type: 'constraint_error',\n        i18nLabel: i18n('isomorphic-schema--my_special_incorrect_formatting', 'The Field must be of form ####-####-####-####'),\n        label: 'The Field must be of form ####-####-####-####'\n      }\n\n      return error\n    }\n  }\n})\n```\n\nNote that createObjectPrototype mounts methods of the extended fields using the interface name of that object prototype. That is why we type `this._ITextField` to access the validate method of the TextField. If we extend from our new MySpecialValidationField we would access it's validate method at `this._IMySpecialValidationField` which is also the name we set on the interface IMySpecialValidationField.\n\n### Creating a Field to Handle Complex Properties ####\n\nYupp, you start by creating an interface. \n\n```JavaScript\nimport { createInterfaceClass } from 'component-registry');\nconst Interface = createInterfaceClass('MyTestApp')\n\nexport const IMyComplexValidationField = new Interface({\n  name: 'IMyComplexValidationField'\n})\n```\n\nIn this example we will extend a SelectField in order to update data in an object. We will also force the available select options in the constructor to avoid having to enter that data in the schema. Since we are using the standard SelectField validation we don't need to add a validation method.\n\n```JavaScript\nimport TextField from 'isomorphic-schema/lib/field_validators/TextField';\nimport SelectField from 'isomorphic-schema/lib/field_validators/SelectField';\n\nexport const MyComplexValidationField = createObjectPrototype({\n  implements: [IMyComplexValidationField],\n  extends: [SelectField],\n  \n  constructor: function (options) {\n    // These are mandatory options so we override what ever crap the developer might have fed us :p\n    options.options = [\n      // Note that it is up to the form rendering library to choose how to render the title string, the i18n method\n      // is a convenience method that allows us to parse our code and find i18n strings that need to be translated.\n      // We could just use a simple string as option title if we only use a single language.\n      {name: 'happy_strong', title: i18n('form_visibility__option_happy_strong', 'Feeling happy and strong')},\n      {name: 'happy_weak', title: i18n('form_visibility__option_happy_weak', 'Feeling happy but weak')},\n      {name: 'sad_strong', title: i18n('form_visibility__option_sad_strong', 'Feeling sad but strong')},\n      {name: 'sad_weak', title: i18n('form_visibility__option_sad_weak', 'Feeling sad and weak')}\n    ]\n    options.valueType = new TextField({required: true})\n\n    this._ISelectField.constructor.call(this, options)\n  },\n\n  toFormattedString: function (inp) {\n    if (inp.happy \u0026\u0026 inp.strong) {\n        return 'happy_strong';\n    } else if (inp.happy) {\n        return 'happy_weak';\n    } else if (inp.strong) {\n        return 'sad_strong';\n    } else {\n        return 'sad_weak';\n    }\n  },\n\n  fromString: function (inp) {\n    var outp = {\n        happy: false,\n        strong: false\n    };\n    if (inp.indexOf('happy') \u003e= 0) {\n        outp.happy = true;\n    }\n    if (inp.indexOf('strong') \u003e= 0) {\n        outp.strong = true;\n    }\n    return outp;\n  }\n})\n```\n\nThe method `toFormattedString` converts the value sent to the field to a representation used internally in the field. In this case transforming it to be compatible with the SelectField we are extending.\n\nThe method `fromString` conversely converts the internal value back to the original object representation.\n\nThese two methods allow us great flexibility in what data a field can handle.\n\n## Creating a Form Rendering Library ###\n\n### Creating a Form Renderer ####\n\nRendering a form from a schema is not very difficult but there are several features you need to make sure you support. If you want to create your own form renderer you should take a look at an existing implementation and make the changes that suit your purpose. For server side template based rendering engines check the NPM-package `kth-node-formlib` https://github.com/KTH/kth-node-formlib and for React style isomorphic form rendering look at `protoncms-formlib` https://github.com/jhsware/protoncms-formlib\n\n### Creating a Field Widget ####\n\nThe form generator will do a lookup to find the widget it should render for a given field. This lookup asks for an adapter that implements an interface (in this case IInputFieldWidget)\n\n```JavaScript\nimport { createInterfaceClass } from 'component-registry');\nconst Interface = createInterfaceClass('MyTestApp')\n\nexport const IInputFieldWidget = new Interface({\n  name: 'IInputFieldWidget'\n})\n```\n\nand adapts the given field (in the example bellow IMySpecialField).\n\nThe actual lookup looks like this:\n\n```JavaScript\nimport { globalRegistry } from 'component-registry'\n\nconst theField = new MySpecialField(...);\nglobalRegistry.getAdapter(theField, IInputFieldWidget);\n```\n\nThe component-registry https://github.com/jhsware/component-registry will find the widget we are creating here:\n\n```JavaScript\nimport { globalRegistry } from 'component-registry'\nimport { createAdapter } from 'component-registry');\n\nconst MySpecialInputAdapter = createAdapter({\n  implements: IInputFieldWidget, // Which we just created\n  adapts: IMySpecialField, // Which was created a bit further up\n\n  render: function (key, data, fieldError, lang, objectNamespace) {\n    // this.context is the field validator object\n    var inputName = (objectNamespace ? objectNamespace + '.' + key : key)\n    \n    // Convenience method that creates the label of this widget\n    var outp = _standardLabel(key, this.context, lang)\n\n    // Always handle readOnly attribute\n    if (this.context.readOnly) {\n      if (data) {\n        outp += '\u003cdiv class=\"form-control-static\"\u003e' + data + '\u003c/div\u003e'\n      } else {\n        let placeholder = (this.context.placeholder ? this.context.placeholder : '')\n        outp += '\u003cdiv class=\"form-control-static\"\u003e' + placeholder + '\u003c/div\u003e'\n      }\n      outp += '\u003cinput type=\"hidden\" name=\"' + inputName + '\" value=\"' + (data || '') + '\" /\u003e'\n    } else {\n      outp += '\u003cinput type=\"text\" class=\"form-control\" value=\"' + (data || '') + '\" name=\"' + inputName + '\" id=\"' + inputName + '\" placeholder=\"' + (this.context.placeholder ? this.context.placeholder : '') + '\" /\u003e'\n    }\n\n    return outp\n  }\n}).registerWith(globalRegistry)\n```\n\nThe fact that we place the widget rendering code in a render method is determined by the form generator. Also the parameters are determined by the form generator. This example matches `kth-node-formlib`.\n\n## i18n ###\nisomorphic-schema supports i18n by providing i18nLabel properties that you can translate with your library of choice. There are two useful helper methods i18n and renderString:\n\n\n### i18n(label: string[, description: string]) ####\nUse `i18n` to create nice i18n labels when defining your schema. All it does is return the first argument, but it also allows you to parse your code to find i18n messages to translate. You will need to translate the message with your chosen i18n library when outputing the strings.\n\n```JavaScript\nimport TextAreaField from 'isomorphic-schema/lib/field_validators/TextAreaField');\nimport { i18n } from 'isomorphic-schema/lib/utils');\n\nconst description = new TextAreaField({\n    label: i18n('form_description_label', 'Description'),\n    placeholder: i18n('form_description_placeholder', 'Type here...'),\n    required: true\n})\n```\n\n### renderString(text: string, fieldeDef: object) ####\nUse `renderString` to substitute placeholders for values from the field validator options. This is used in your field widget when rendering a field error.\n\n```JavaScript\nimport IntegerField from 'isomorphic-schema/lib/field_validators/IntegerField';\nimport { renderString } from 'isomorphic-schema/lib/utils';\n\nconst fieldDef = new IntegerField({\n    min: 10,\n    max: 20\n})\n\nconst outp = renderString('Value too low. Min ${minValue}', fieldDef)\n// outp === 'Value too low. Min 10'\n```\n\n### Extracting i18n strings ####\nYou can use grep to extract all the i18n strings you have in your project.\n\n```\ngrep -ohr \"i18n([^)]*)\" ./path/to/your/code/*\n```\n\n**Note** this regex currently truncates on the first ending parenthesis, if that is a problem you need to create a better regex. \n\nWhen running this command on isomorphic-schema we get the following output (note, we added a prefix to avoid some comments etc):\n\n```\n// This is a regex that will return two match groups with i18nLabel and description, you can use it to create a js parser\nconst regex = /i18n\\(('[^'\"]*'(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$))[^'\"]+('[^'\"]*'(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$))\\)/g\n\n// If you only want to a simple extraction from commanline, try this:\n$ grep -ohr \"i18n(['\\\"][^'\\\"]*['\\\"].*\" ./lib/*\ni18n('isomorphic-schema--field_required', 'Required'),\ni18n('isomorphic-schema--text_field_no_string', 'The field doesn\\'t contain text'),\ni18n('isomorphic-schema--text_field_too_short', 'The text is too short. Min ${minLength} chars.'),\ni18n('isomorphic-schema--text_field_too_long', 'The text is too long. Max ${maxLength} chars.'),\ni18n('isomorphic-schema--text_area_field_no_string', 'The field doesn\\'t contain text'),\ni18n('isomorphic-schema--text_field_too_short', 'The text is too short. Min ${minLength} chars.'),\ni18n('isomorphic-schema--text_field_too_long', 'The text is too long. Max ${maxLength} chars.'),\ni18n('isomorphic-schema--integer_field_not_number', 'The field doesn\\'t contain numbers'),\ni18n('isomorphic-schema--integer_field_no_decimals', 'The field may not contain decimals'),\ni18n('isomorphic-schema--integer_field_too_small', 'The value is too small. Min ${minValue}'),\ni18n('isomorphic-schema--integer_field_too_big', 'The value is too big. Max ${maxValue}'),\ni18n('isomorphic-schema--decimal_field_not_number', 'The field doesn\\'t contain numbers'),\ni18n('isomorphic-schema--decimal_field_too_small', 'The value is too small. Min ${minValue}'),\ni18n('isomorphic-schema--decimal_field_too_big', 'The value is too big. Max ${maxValue}'),\ni18n('isomorphic-schema--credit_card_field_not_supported', 'Entered card type is not supported');\ni18n('isomorphic-schema--credit_card_field_incorrect_formatting', 'The card number is incorrectly entered');\ni18n('isomorphic-schema--date_field_incorrect_formatting', 'This doesn\\'t look like a date'),\ni18n('isomorphic-schema--date_time_field_incorrect_formatting', 'This doesn\\'t look like a date with time'),\ni18n('isomorphic-schema--email_field_incorrect_formatting', 'This is not a valid e-mail address'),\ni18n('isomorphic-schema--list_field_type_error', 'This is not proper list. This is a bug in the application'),\ni18n('isomorphic-schema--list_field_value_error_too_many_items', 'Too many items in list, max ${maxItems} allowed'),\ni18n('isomorphic-schema--list_field_value_error_too_few_items', 'Too few items in list, min ${minItems} allowed'),\ni18n('isomorphic-schema--list_field_value_error', 'There is an error in the content of this list'),\ni18n('isomorphic-schema--multi_select_field_value_error', 'One or more of the selected values is not allowed'),\ni18n('isomorphic-schema--object_field_value_error', 'There is an error in the content of this object'),\ni18n('isomorphic-schema--org_nr_field_incorrect_formatting', 'Malformatted'),\ni18n('isomorphic-schema--org_nr_field_too_short', 'Entered number is too short'),\ni18n('isomorphic-schema--org_nr_field_wrong_checksum', 'The entered number is incorrect (checksum error)'),\ni18n('isomorphic-schema--password_field_too_short', 'The password must contain at least 8 chars'),\ni18n('isomorphic-schema--select_field_value_error', 'The selected value is not allowed'),\ni18n('form_label_title', 'The Title')\n```\n\nYou will want to translate these strings in your project to internationalise your forms.\n\n## Sample usage ###\n\n**Note:** These examples don't use i18n enabled strings for readability. Substitute the strings with i18n(...) as shown above to support translations.\n\n```JavaScript\nimport Schema from 'isomorphic-schema/lib/schema';\nimport TextAreaField from 'isomorphic-schema/lib/field_validators/TextAreaField';\nimport TextField from 'isomorphic-schema/lib/field_validators/TextField';\nimport SelectField from 'isomorphic-schema/lib/field_validators/SelectField';\nimport ListField from 'isomorphic-schema/lib/field_validators/ListField';\nimport ObjectField from 'isomorphic-schema/lib/field_validators/ObjectField';\n\nconst mediaSchema = new Schema(\"Media Schema\", {\n    image_url: new TextField({\n        label: 'Image URL',\n        placeholder: 'http://...',\n        required: true\n    }),\n    description: new TextAreaField({\n        label: 'Description',\n        placeholder: 'Type here...',\n        required: true\n    })\n});\n\n\nconst myThing = new Schema(\"MyThing Schema\", {\n    difficulty: new SelectField({\n        label: 'Difficulty',\n        required: true,\n        valueType: new IntegerField(),\n        options: [\n            {name: 0, title: \"Easy\"},\n            {name: 1, title: \"Medium\"},\n            {name: 2, title: \"Hard\"}\n        ]\n    }),\n    description: new TextAreaField({\n        label: 'Description',\n        placeholder: 'Type here...',\n        required: true\n    }),\n    media: new ListField({\n        label: 'Media',\n        required: false,\n        valueType: new ObjectField({\n            label: 'Media File',\n            schema: mediaSchema,\n            requried: true,\n            help: 'Choose a media file...'\n        })\n    })\n});\n\nconst errors = mySchema.validate({\n    difficulty: 0,\n    description: \"This is my thing\"\n});\n\nif (typeof errors === 'undfined') {\n    console.log(\"We didn't get any validation errors!\");\n}\n```\n\n## Internal Notes ###\n\nTODO: Refactor 2.0:\n  - use async await instead of validateAsync for more readable code\n  - move filtering to helper function which operates on a schema and returns a new schema\n  - use this new filtering etc. in inferno-formlib to make it simpler\n\n\nTODO (SEB): Write async tests for AnyOf\n\nTODO - do basic type checking of params (such as adding valdiator as schema)\nDONE - create AnyOf validator\nDONE - explain purpose of isomorphic-schema\nDONE - What is a schema and what is a field validator\nTODO - What is component-registry\nSTARTED - Schema API\nSTARTED - Field validator API\nDONE - list field validators with options and common options\n    - label\n    - type\n    - help\nDONE - how to create a custom field\nDONE - how to creat a formlib to render schemas\n    DONE - formGenerator\n    DONE - component-registry\n    DONE - field widgets\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjhsware%2Fisomorphic-schema","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjhsware%2Fisomorphic-schema","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjhsware%2Fisomorphic-schema/lists"}