{"id":22311617,"url":"https://github.com/ludbek/powerform","last_synced_at":"2025-09-23T11:23:02.736Z","repository":{"id":9749860,"uuid":"63207104","full_name":"ludbek/powerform","owner":"ludbek","description":"A powerful form model.","archived":false,"fork":false,"pushed_at":"2023-01-09T02:23:57.000Z","size":858,"stargazers_count":47,"open_issues_count":2,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-09-01T10:12:23.524Z","etag":null,"topics":["form","javascript","model"],"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/ludbek.png","metadata":{"files":{"readme":"readme.md","changelog":"CHANGE_LOG.txt","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":"2016-07-13T02:21:14.000Z","updated_at":"2025-01-29T04:43:42.000Z","dependencies_parsed_at":"2023-01-13T15:33:09.770Z","dependency_job_id":null,"html_url":"https://github.com/ludbek/powerform","commit_stats":null,"previous_names":[],"tags_count":52,"template":false,"template_full_name":null,"purl":"pkg:github/ludbek/powerform","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ludbek%2Fpowerform","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ludbek%2Fpowerform/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ludbek%2Fpowerform/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ludbek%2Fpowerform/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ludbek","download_url":"https://codeload.github.com/ludbek/powerform/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ludbek%2Fpowerform/sbom","scorecard":{"id":603045,"data":{"date":"2025-08-11","repo":{"name":"github.com/ludbek/powerform","commit":"51f8deb400d01684d6f42a9374b0b89adb0d0f8a"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.3,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/push.yml:1","Warn: no topLevel permission defined: .github/workflows/release.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 0/9 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Pinned-Dependencies","score":2,"reason":"dependency not pinned by hash detected -- score normalized to 2","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/push.yml:8: update your workflow using https://app.stepsecurity.io/secureworkflow/ludbek/powerform/push.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:11: update your workflow using https://app.stepsecurity.io/secureworkflow/ludbek/powerform/release.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/ludbek/powerform/release.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/ludbek/powerform/release.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/ludbek/powerform/release.yml/master?enable=pin","Warn: npmCommand not pinned by hash: .github/workflows/push.yml:11","Info:   0 out of   5 GitHub-owned GitHubAction dependencies pinned","Info:   2 out of   3 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 24 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"10 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-gcx4-mw62-g8wm","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-21T00:54:04.827Z","repository_id":9749860,"created_at":"2025-08-21T00:54:04.827Z","updated_at":"2025-08-21T00:54:04.827Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275711082,"owners_count":25514200,"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-09-18T02:00:09.552Z","response_time":77,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","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":["form","javascript","model"],"created_at":"2024-12-03T21:25:36.076Z","updated_at":"2025-09-23T11:23:02.720Z","avatar_url":"https://github.com/ludbek.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/ludbek/powerform.svg?branch=master)](https://travis-ci.org/ludbek/powerform)\n\n\u003cimg src=\"https://user-images.githubusercontent.com/8296449/30893678-3e1702a8-a35f-11e7-8f6c-965ffe88cf40.png\" width=400\u003e\n\nLogo by [Anand](https://www.behance.net/mukhiyaanad378)\n\n## Introduction\n\nA tiny super portable form model which can be used in apps with or without frameworks like [React](https://github.com/facebook/react).\n\n## Showcase\n\n[Vanilla JS](https://codesandbox.io/s/powerform-vanilla-js-ug2sx)\n\n[React](https://codesandbox.io/s/powerform-react-17gqu)\n\n[Mithril](https://codesandbox.io/s/powerform-mithril-pdrl1?file=/src/index.js)\n\n## Breaking changes\n\nv4 introduces significant changes which are not backward compatible with v3.\nPlease checkout the [change log](CHANGE_LOG.txt).\n\n## Installation\n\n### yarn\n\n`yarn add powerform`\n\n### npm\n\n`npm install powerform`\n\n## Quick walk-through\n\n```javascript\n// es6\nimport { powerform } from \"powerform\";\nimport { required, minLength, equalsTo } from \"validatex\";\n\nconst schema = {\n  username: required(true),\n  password: [required(true), minLength(8)],\n  confirmPassword: [required(true), equalsTo(\"password\")],\n};\n\nconst form = powerform(schema);\n\n// assign values to fields\nform.username.setData(\"ausername\");\nform.password.setData(\"apassword\");\nform.confirmPassword.setData(\"bpassword\");\n\n// per field validation\nconsole.log(form.username.validate()) \u003e true;\nconsole.log(form.password.validate()) \u003e true;\nconsole.log(form.confirmPassword.validate()) \u003e false;\nconsole.log(form.confirmPassword.getError()) \u003e \"Passwords do not match.\";\n\n// validate all the fields at once\nconsole.log(form.validate()) \u003e false;\nconsole.log(form.getError()) \u003e\n  {\n    username: undefined,\n    password: undefined,\n    confirmPassword: \"Password and confirmation does not match.\",\n  };\n```\n\n## API\n\n### Form\n\n#### powerform(schema, config?: object)\n\nReturns a form.\n\n```javascript\n// reusing the schema from walkthrough\nconst form = powerform(schema);\n```\n\n##### Config schema\n\n```\n{\n  data: object,\n  onChange(data: object, form: Form): function,\n  onError(error: object, form: Form): function,\n  stopOnError: boolean\n}\n```\n\n##### Set initial values of field\n\nPass an object at `config.data` to set initial field values.\n\n```javascript\nconst config = {\n  data: {\n    username: \"a username\",\n    password: \"a password\",\n  },\n};\nconst form = powerform(schema, config);\nconsole.log(form.username.getData()) \u003e \"a username\";\nconsole.log(form.password.getData()) \u003e \"a password\";\n```\n\n##### Track changes in data and error\n\nChanges to values and errors of fields can be tracked through `config.onChange` callback.\n\n```javascript\nconst config = {\n  onChange: (data, form) =\u003e {\n    console.log(data);\n  },\n  onError: (error, form) =\u003e {\n    console.log(error);\n  },\n};\n\nconst form = powerform(schema, config);\nform.username.setData(\"a username\") \u003e\n  // logs data\n  {\n    username: \"a username\",\n    password: null,\n    confirmPassword: null,\n  };\nform.password.validate() \u003e\n  // logs changes to error\n  {\n    username: null,\n    password: \"This field is required.\",\n    confirmPassword: null,\n  };\n```\n\n##### Validate one field at a time\n\nIt is possible to stop validation as soon as one of the fields fails.\nTo enable this mode of validation set `config.stopOnError` to `true`.\nOne can control the order at which fields are validated by supplying `index` to fields.\n\n```javascript\nconst loginSchema = {\n  password: {validator: required(true), index: 2}\n  username: {validator: required(true), index: 1}\n}\nconst form = powerform(loginSchema, {stopOnError: true})\n\nconsole.log(form.validate())\n\u003e\u003e false\n\nconsole.log(form.getError())\n\u003e\u003e {username: \"This field is required.\"}\n\n```\n\n#### form.setData(data: object)\n\nSets value of fields of a form.\n\n```javascript\nconst form = powerform(schema);\nlet data = {\n  username: \"a username\",\n  password: \"a password\",\n};\nform.setData(data);\n\nconsole.log(form.username.getData()) \u003e \"a username\";\nconsole.log(form.password.getData()) \u003e \"a password\";\nconsole.log(form.confirmPassword.getData()) \u003e null;\n```\n\n#### form.getData()\n\nReturns key value pair of fields and their corresponding values.\n\n```javascript\nconst form = powerform(schema);\nlet data = {\n  username: \"a username\",\n  password: \"a password\",\n};\nform.setData(data);\n\nconsole.log(form.getData()) \u003e\n  {\n    username: \"a username\",\n    password: \"a password\",\n    confirmPassword: null,\n  };\n```\n\n#### form.getUpdates()\n\nReturns key value pair of updated fields and their corresponding values.\nThe data it returns can be used for patching a resource over API.\n\n```javascript\nconst userFormSchema = {\n  name: required(true),\n  address: required(true),\n  username: required(true),\n};\n\nconst form = powerform(userFormSchema);\nlet data = {\n  name: \"a name\",\n  address: \"an address\",\n};\nform.setData(data);\n\nconsole.log(form.getUpdates()) \u003e\n  {\n    name: \"a name\",\n    address: \"an address\",\n  };\n```\n\n#### form.setError(errors: object)\n\nSets error of fields in a form.\n\n```javascript\nconst form = powerform(schema);\nconst errors = {\n  username: \"Invalid username.\",\n  password: \"Password is too common.\",\n};\nform.setError(errors);\n\nconsole.log(form.username.getError()) \u003e \"Invalid username.\";\n\nconsole.log(form.password.getError()) \u003e \"Password is too common.\";\n\nconsole.log(form.confirmPassword.getError()) \u003e null;\n```\n\n#### form.getError()\n\nReturns key value pair of fields and their corresponding errors.\n\n```javascript\nconst form = powerform(schema);\nform.password.setData(\"1234567\");\nform.confirmPassword.setData(\"12\");\nform.validate();\n\nconsole.log(form.getError()) \u003e\n  {\n    username: \"This field is required.\",\n    password: \"This field must be at least 8 characters long.\",\n    confirmPassword: \"Passwords do not match.\",\n  };\n```\n\n#### form.isDirty()\n\nReturns `true` if value of one of the fields in a form has been updated.\nReturns `false` if non of the fields has been updated.\n\n```javascript\nconst form = powerform(schema);\n\nconsole.log(form.isDirty()) \u003e false;\n\nform.username.setData(\"a username\");\nconsole.log(f.isDirty()) \u003e true;\n```\n\n#### form.makePristine()\n\nSets initial value to current value in every fields.\n\n```javascript\nconst form = powerform(schema);\nform.username.setData(\"a username\");\n\nconsole.log(form.isDirty()) \u003e true;\n\nform.makePristine();\nconsole.log(form.isDirty()) \u003e false;\nconsole.log(form.username.getData()) \u003e \"a username\";\n```\n\n#### form.reset()\n\nResets all the fields of a form.\n\n```javascript\nconst form = powerform(schema);\nform.username.setData(\"a username\");\nform.password.setData(\"a password\");\nconsole.log(form.getData()) \u003e\n  {\n    username: \"a username\",\n    password: \"a password\",\n    confirmPassword: null,\n  };\n\nform.reset();\nconsole.log(form.getData()) \u003e\n  {\n    username: null,\n    password: null,\n    confirmPassword: null,\n  };\n```\n\n#### form.isValid()\n\nReturns `true` if all fields of a form are valid.\nReturns `false` if one of the fields in a form is invalid.\nUnlike `form.validate()` it does not set the error.\n\n```javascript\nconst form = powerform(schema);\nform.password.setData(\"1234567\");\n\nconsole.log(form.isValid()) \u003e false;\n\nconsole.log(form.getError()) \u003e\n  {\n    username: null,\n    password: null,\n    confirmPassword: null,\n  };\n```\n\n### Field\n\nEvery keys in a schema that is passed to `powerform` is turned into a Field. We do not need to directly instanciate it.\n\n#### Field(config?: object| function | [function])\n\nCreates and returns a field instance.\n\n##### Config schema\n\n```\n{\n  validator: function | [function],\n  default?: any,\n  debounce?: number,\n  onChange(value: any, field: Field)?: function\n  onError(error: any, field: Field)?: function\n}\n```\n\n##### Set default value\n\nA field can have default value.\n\n```javascript\nconst form = powerform({\n  username: { validator: required(true), default: \"orange\" },\n});\n\nconsole.log(form.username.getData()) \u003e \"orange\";\n```\n\n##### Trance changes in value and error\n\nChanges in value and error of a field can be tracked through `config.onChange` and `config.onError` callbacks.\n\n```javascript\nfunction logData(data, field) {\n  console.log('data: ', data)\n}\n\nfunction logError(data, field) {\n  console.log('error: ', error)\n}\n\nconst form = powerform({\n  username: {\n    validator: required(true),\n    default: 'orange',\n    onChange: logData,\n    onError: logError\n  }\n})\nform.username.validate()\n\u003e \"error: \" \"This field is required.\"\n\nform.username.setData('orange')\n\u003e \"data: \" \"orange\"\n\nform.username.validate()\n\u003e \"error: \" null\n```\n\n##### Debounce change in value\n\nChanges in data can be debounced.\n\n```javascript\nconst form = powerform({\n  username: {\n    validator: required(true),\n    default: 'orange',\n    onChange: logData,\n    onError: logError\n  }\n})\n\nform.username.setData(\"banana\")\n// after 1 second\n\u003e \"data: \" \"banana\"\n```\n\n#### Field.setData(value: any)\n\nSets field value.\n\n```javascript\nconst form = powerform({\n  name: required(true),\n});\nform.name.setData(\"a name\");\nconsole.log(form.name.getData()) \u003e \"a name\";\n```\n\n#### Field.getData()\n\nReturns field value.\n\n#### Field.modify(newValue: any, oldValue: any)\n\nModifies user's input value.\nExample usage -\n\n- capitalize user name as user types\n- insert space or dash as user types card number\n\n```javascript\nconst form = powerform({\n  name: {\n    validator: required(true),\n    modify(value) {\n      if (!value) return null;\n      return value.replace(/(?:^|\\s)\\S/g, (s) =\u003e s.toUpperCase());\n    },\n  },\n});\n\nform.name.setData(\"first last\");\nconsole.log(form.name.getData()) \u003e \"First Last\";\n```\n\n#### Field.clean(value: any)\n\nCleans the value.\n`form.getData()` uses this method to get clean data.\nIt is useful for situations where value in a view should be different to\nthe value in stores.\n\n```javascript\nconst form = powerform({\n  card: {\n    validator: required(true),\n    modify(newVal, oldVal) {\n      return newVal.length === 16\n        ? newCard\n            .split(\"-\")\n            .join(\"\")\n            .replace(/(\\d{4})/g, \"$1-\")\n            .replace(/(.)$/, \"\")\n        : newCard\n            .split(\"-\")\n            .join(\"\")\n            .replace(/(\\d{4})/g, \"$1-\");\n    },\n    clean(value) {\n      return card.split(\"-\").join(\"\");\n    },\n  },\n});\n\nform.card.setData(\"1111222233334444\");\nconsole.log(form.card.getData()) \u003e \"1111-2222-3333-4444\";\nconsole.log(form.getData()) \u003e { card: \"1111222233334444\" };\n```\n\n### field.validate(value: any, allValues: object)\n\n#### field.isValid()\n\nReturns `true` or `false` based upon the validity.\n\n#### field.setError(error: string)\n\nSets field error.\n\n#### field.getError()\n\nReturns field error.\nCall this method after validating the field.\n\n#### field.isDirty()\n\nReturns `true` if value of a field is changed else returns `false`.\n\n#### field.makePristine()\n\nMarks a field to be untouched.\nIt sets current value as initial value.\n\n#### field.reset()\n\nIt resets the field.\nSets initial value as current value.\n\n#### field.setAndValidate(value: any)\n\nSets and validates a field. It internally calls `Field.setData()` and `Field.validate()`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fludbek%2Fpowerform","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fludbek%2Fpowerform","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fludbek%2Fpowerform/lists"}