diff --git a/.github/workflows/deploy-to-npm.yml b/.github/workflows/deploy-to-npm.yml new file mode 100644 index 0000000..e7df984 --- /dev/null +++ b/.github/workflows/deploy-to-npm.yml @@ -0,0 +1,40 @@ +name: Deploy to npm + +on: + push: + branches: + - main + paths: + - "package.json" + - ".github/workflows/deploy-to-npm.yml" + +jobs: + # Deploy npm module + deploy-npm-module: + name: Deploy changes to npm + + runs-on: ubuntu-latest + + steps: + # Environment and dependency setup + - name: Cloning repository + uses: actions/checkout@v4 + + - name: Set up Node.js (.nvmrc) + uses: actions/setup-node@v3 + with: + node-version-file: ".nvmrc" + cache: "npm" + + # install dependencies + - name: Install dependencies + run: npm ci + + - name: Build module files + run: npm run build + + - name: "Publish to NPM" + run: | + npm publish --access public + env: + NPM_ACCESS_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }} diff --git a/.nvmrc b/.nvmrc index 3c03207..209e3ef 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18 +20 diff --git a/README.md b/README.md index f39b083..51be835 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,8 @@ -# NPM package template +# POAP Data validations and sanetisers -> Note: this repo is a work in progress +This package contains a number of regex-based validations as well as string sanetisers that are common in POAP-related dApps. -This template by default assumes you are building a module that should run in `node.js` but also the browser. It assumes you want to ship both raw and bundled & minified versions of your library. If you disable `package.json` fields for specific platforms, the build process will skip building them +You can view the complete list of included elements here: -This repository is configured by default with `eslint` and `husky` based on the preferences of the [POAP Skunkworks team](https://github.com/poap-xyz/skunk-linter/). - -This repository also assumed you will be publishing new versions through Github actions. - -## One-time setup - -1. Generate an npm access token [here](https://www.npmjs.com/settings/actuallymentor/tokens) - - ideally use a granular access token that has read/write only to the package you are creating - - granular access tokens _expire_, so make sure you keep that in account (hopefully npm optioanlly changes this soon) -2. Add it as a Github Actions secret with the name `NPM_ACCESS_TOKEN` -3. New versions are deployed when you push code with a new `version` in `package.json` - - It's recommended to use strict [Semver](https://semver.org/) for this -4. Change the `package.json` fields: `name`, `description`, `author`, and `license` as you see fit -5. Disable package fields that are not useful to your project - - Browser module: remove `package.module` and `package.exports.node` - - Node-only module: remove `package.browser` - -## Setting up previews - -The preview templates are in `previews`. The default preview formats are a `react.js` app and a default `node.js` environment. - -The local publishing of your package is done by running `npm run publish:local` which uses `yalc` under the hood. - -When setting up your package, make sure to: - -1. Change the `package.name` field -2. Change it in the `previews/{react,node}/package.json` `name` fields too -3. Run `npm run publish:local` in the root directory -4. Run `yalc add your-package-name` in the `previews/{react,node}` folders - -## Writing your code - -All source files are in `src` and the default entry point is `index.js`. To publish changes to your local preview in `previews/` you can run `npm run build && npm run publish:local`. - -## Publishing locally - -If you want to deploy a new version without pushing code to Github, add your access token to a `.npntoken` file in the project root. Then run `npm publish` to publish your package. - -## Changing defaults - -You might have other assumptions than I did when creating this template. Chief among them, consider: - -1. Whether the `esbuild.mjs` settings make sense for your project \ No newline at end of file +- [View all regex validations](./src/modules/validations.js) +- [View all sanitisation functions](./src/modules/sanitisers.js) diff --git a/esbuild.mjs b/esbuild.mjs index 2060475..7595eff 100644 --- a/esbuild.mjs +++ b/esbuild.mjs @@ -6,7 +6,7 @@ import * as esbuild from 'esbuild' // Set the LTS version to support // see https://github.com/nodejs/release#release-schedule -const node_lts_version = 18 +const node_lts_version = 20 const oldest_node_to_support = '6.10' // Set browser support diff --git a/package-lock.json b/package-lock.json index 2faed40..ec28758 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,16 @@ { - "name": "npm-package-template", + "name": "xplatform-npm-package", "version": "0.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "npm-package-template", + "name": "xplatform-npm-package", "version": "0.0.1", "license": "MIT", + "dependencies": { + "xplatform-npm-package": "file:.yalc/xplatform-npm-package" + }, "devDependencies": { "@babel/eslint-parser": "^7.22.15", "@babel/preset-react": "^7.22.15", @@ -19,6 +22,7 @@ "yalc": "^1.0.0-pre.53" } }, + ".yalc/xplatform-npm-package": {}, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", @@ -3911,6 +3915,10 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/xplatform-npm-package": { + "resolved": ".yalc/xplatform-npm-package", + "link": true + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -6788,6 +6796,9 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "xplatform-npm-package": { + "version": "file:.yalc/xplatform-npm-package" + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 0eeacfc..089260c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,8 @@ { - "name": "xplatform-npm-package", + "name": "@poap/sane-data", + "private": false, "version": "0.0.1", - "description": "This is a template repository for the creation of new npm packages", + "description": "A collection of data validations and sanetisers", "main": "./src/index.js", "browser": "./dist/browser/index.js", "module": "./dist/legacy-node/index.js", @@ -30,8 +31,5 @@ "eslint-plugin-react": "^7.33.2", "husky": "^8.0.3", "yalc": "^1.0.0-pre.53" - }, - "dependencies": { - "xplatform-npm-package": "file:.yalc/xplatform-npm-package" } } \ No newline at end of file diff --git a/src/index.js b/src/index.js index 779e8ad..ce2b6f5 100644 --- a/src/index.js +++ b/src/index.js @@ -1 +1,5 @@ -export { log } from './modules/helpers.js' \ No newline at end of file +// Export all validations +export * from './modules/validations' + +// Export all sanitisers +export * from './modules/sanitisers' \ No newline at end of file diff --git a/src/modules/helpers.js b/src/modules/helpers.js deleted file mode 100644 index f212b3c..0000000 --- a/src/modules/helpers.js +++ /dev/null @@ -1,17 +0,0 @@ -// Check if we are running on a local dev server -export const localhost = typeof location !== 'undefined' && location.href.includes( 'localhost' ) - -// Check if we are running in debug mode -export const debug_mode = typeof location !== 'undefined' && location.href.includes( 'debug=true' ) - -// Check if we are running in development mode -export const dev = process.env.NODE_ENV === 'development' || process.env.verbose == 'true' || debug_mode || localhost - -/** - * * A logging function that logs while developing, but is silent in production - * @param {...*} messages - The messages to log. Can be any type, and any number of arguments. - * @returns {Void} does not return anything, merely logs to the console - */ -export const log = ( ...messages ) => { - if( dev ) console.log( 'Development log: ', ...messages ) -} \ No newline at end of file diff --git a/src/modules/sanitisers.js b/src/modules/sanitisers.js new file mode 100644 index 0000000..d0aceaf --- /dev/null +++ b/src/modules/sanitisers.js @@ -0,0 +1,116 @@ +/** + * General purpose sanitiser + * @param {String} input - Input to be sanetised + * @param {String} type - String that describes the input type, used for error messaging + * @param {RegExp} regex - Regular expression to match the input against + * @param {Boolean} throw_on_fail - Whether to throw an error if the input fails validation + * @returns a lowercased and trimmed string version of the input + */ +const sanetize = ( input, type, regex, throw_on_fail = true ) => { + + // Check for validation match + if( !`${ input }`.match( regex ) && throw_on_fail ) throw new Error( `${ type } input ${ input } is not valid` ) + + // If string is invalid, but throwing is disabled, log a warning + if( !`${ input }`.match( regex ) && !throw_on_fail ) console.warn( `${ type } input ${ input } is not valid` ) + + // Return normalised string version of input + return `${ input?.toLowerCase().trim() }` + +} + +/* ///////////////////////////////// +// Ethereum related sanitisations */ +// ///////////////////////////////*/ + +/** + * Validate and sanetise an Ethereum address. Throws on failed validation by default. NOTE: this check does not use checksummed addresses but a naive regex. If you require checksummed addresses, use the checksum function of your favored library. + * @param {String} input - Input to be sanetised + * @param {Boolean} throw_on_fail - Whether to throw an error if the input fails validation, when set to false, it will log a warning instead of throwing + * @returns {String} Validated, lowercased, trimmed string version of the input + */ +import { eth_address_regex } from './validations' +export const sanetise_eth_address = ( input, throw_on_fail = true ) => sanetize( + input, + 'Ethereum address', + eth_address_regex, + throw_on_fail +) + +/** + * Validate and sanetise an Ethereum Name Service address. Throws on failed validation by default. NOTE: this allowd all DNSSEC enabled domain names + * @param {String} input - Input to be sanetised + * @param {Boolean} throw_on_fail - Whether to throw an error if the input fails validation, when set to false, it will log a warning instead of throwing + * @returns {String} Validated, lowercased, trimmed string version of the input + */ +import { ens_regex } from './validations' +export const sanetise_ens_address = ( input, throw_on_fail = true ) => sanetize( + input, + 'Ethereum ENS', + ens_regex, + throw_on_fail +) + +/** + * Validate and sanetise an Ethereum address OR an Ethereum Name Service address. Throws on failed validation by default. NOTE: this check does not use checksummed addresses but a naive regex. If you require checksummed addresses, use the checksum function of your favored library. The ENS addresses allow all DNSSEC enabled domain names + * @param {String} input - Input to be sanetised + * @param {Boolean} throw_on_fail - Whether to throw an error if the input fails validation, when set to false, it will log a warning instead of throwing + * @returns {String} Validated, lowercased, trimmed string version of the input + */ +import { eth_or_ens_address_regex } from './validations' +export const sanetise_eth_or_ens_address = ( input, throw_on_fail = true ) => sanetize( + input, + 'Ethereum address or ENS', + eth_or_ens_address_regex, + throw_on_fail +) + +/* /////////////////////////////// +// POAP related sanitisations +// /////////////////////////////*/ + +/** + * Validate and sanetise a POAP drop ID. Throws on failed validation by default. NOTE: drop IDs are numbers, but this function returns a string. If you rely on types, be sure to cast the return value to a number if you need that. + * @param {String} input - Input to be sanetised + * @param {Boolean} throw_on_fail - Whether to throw an error if the input fails validation, when set to false, it will log a warning instead of throwing + * @returns {String} Validated, lowercased, trimmed string version of the input + */ +import { poap_id_regex } from './validations' +export const sanetise_poap_id = ( input, throw_on_fail = true ) => sanetize( + input, + 'POAP ID', + poap_id_regex, + throw_on_fail +) + +/** + * Validate and sanetise a POAP drop ID. Throws on failed validation by default. NOTE: drop IDs are numbers, but this function returns a string. If you rely on types, be sure to cast the return value to a number if you need that. + * @param {String} input - Input to be sanetised + * @param {Boolean} throw_on_fail - Whether to throw an error if the input fails validation, when set to false, it will log a warning instead of throwing + * @returns {String} Validated, lowercased, trimmed string version of the input + */ +import { poap_edit_code_regex } from './validations' +export const sanetise_poap_edit_code = ( input, throw_on_fail = true ) => sanetize( + input, + 'POAP edit code', + poap_edit_code_regex, + throw_on_fail +) + +/* /////////////////////////////// +// Web2 related sanitisations +// /////////////////////////////*/ + +/** + * Validate and sanetise an email address. Throws on failed validation by default. + * @param {String} input - Input to be sanetised + * @param {Boolean} throw_on_fail - Whether to throw an error if the input fails validation, when set to false, it will log a warning instead of throwing + * @returns {String} Validated, lowercased, trimmed string version of the input + */ +import { email_regex } from './validations' +export const sanetise_email = ( input, throw_on_fail = true ) => sanetize( + input, + 'email', + email_regex, + throw_on_fail +) \ No newline at end of file diff --git a/src/modules/validations.js b/src/modules/validations.js new file mode 100644 index 0000000..ad0f73c --- /dev/null +++ b/src/modules/validations.js @@ -0,0 +1,28 @@ +/* /////////////////////////////// +// Ethereum-related validations */ + +// Valid Ethereum address +export const eth_address_regex = /(0x[a-f0-9]{40})/i + +// Valid ENS regex, note that this may be any TLD or subdomain that supports DNSSEC +// valid domain names are ASCII, case-insensitive, and may contain digits, hyphens, and dots, but may not start with a hyphen (ending was illegal but is ok since RFC 2181) +// see https://en.wikipedia.org/wiki/Hostname#Syntax +export const ens_regex = /^(?!-)(?:[a-zA-Z0-9-]{1,63}(?