UNPKG

@flatfile/plugin-validate-number

Version:

A Flatfile Listener plugin for number validation

1 lines 8.01 kB
{"version":3,"sources":["../src/validate.number.plugin.ts"],"names":["validateNumberField","value","config","result","numberValue","integerPart","decimalPart","isPrime","error","validateNumber","listener","recordHook","record","field","warning","num","i","sqrt"],"mappings":";;;;AAyBO,SAASA,CACdC,CAAAA,CAAAA,CACAC,EACwB,CACxB,IAAMC,EAAiC,CACrC,KAAA,CAAO,KACP,MAAQ,CAAA,GACR,QAAU,CAAA,EACZ,CAEA,CAAA,GAAI,CACF,IAAIC,EAA+B,MAAOH,CAAAA,CAAK,EAC5C,OAAQC,CAAAA,CAAAA,CAAO,oBAAsB,GAAK,CAAA,EAAE,EAC5C,OAAQA,CAAAA,CAAAA,CAAO,cAAgB,GAAK,CAAA,GAAG,EAG1C,GAFAE,CAAAA,CAAc,WAAWA,CAAW,CAAA,CAEhC,KAAMA,CAAAA,CAAW,EACnB,OAAAD,CAAAA,CAAO,OAAO,IAAK,CAAA,kBAAkB,EAC9BA,CAmCT,CAAA,GAhCID,EAAO,KACTE,GAAAA,CAAAA,CAAc,KAAK,KAAMA,CAAAA,CAAW,GAGlCF,CAAO,CAAA,QAAA,GACTE,EAAc,IAAK,CAAA,KAAA,CAAMA,CAAW,CAGlCF,CAAAA,CAAAA,CAAAA,CAAO,aAAe,CAAC,MAAA,CAAO,UAAUE,CAAW,CAAA,EACrDD,EAAO,QAAS,CAAA,IAAA,CAAK,oBAAoB,CAGvCD,CAAAA,CAAAA,CAAO,MAAQ,KAEfA,CAAAA,GAAAA,CAAAA,CAAO,UAAYE,CAAcF,CAAAA,CAAAA,CAAO,IAAME,CAAeF,EAAAA,CAAAA,CAAO,GAEpEC,CAAAA,EAAAA,CAAAA,CAAO,SAAS,IACd,CAAA,CAAA,qBAAA,EAAwBD,EAAO,SAAY,CAAA,cAAA,CAAiB,EAAE,CAAGA,EAAAA,CAAAA,CAAO,GAAG,CAC7E,CAAA,CAAA,CAIAA,EAAO,GAAQ,GAAA,KAAA,CAAA,GAEfA,EAAO,SAAYE,CAAAA,CAAAA,CAAcF,EAAO,GAAME,CAAAA,CAAAA,EAAeF,CAAO,CAAA,GAAA,CAAA,EAEpEC,EAAO,QAAS,CAAA,IAAA,CACd,qBAAqBD,CAAO,CAAA,SAAA,CAAY,eAAiB,EAAE,CAAA,EAAGA,EAAO,GAAG,CAAA,CAC1E,EAIAA,CAAO,CAAA,SAAA,GAAc,QAAaA,CAAO,CAAA,KAAA,GAAU,OAAW,CAChE,GAAM,CAACG,CAAAA,CAAaC,CAAW,CAAIF,CAAAA,CAAAA,CAAY,UAAW,CAAA,KAAA,CAAM,GAAG,CAC/DC,CAAAA,CAAAA,CAAY,OAASH,CAAO,CAAA,SAAA,CAAYA,EAAO,KACjDC,EAAAA,CAAAA,CAAO,SAAS,IACd,CAAA,CAAA,kBAAA,EAAqBD,EAAO,SAAYA,CAAAA,CAAAA,CAAO,KAAK,CACtD,gCAAA,CAAA,CAAA,CAEEI,GAAeA,CAAY,CAAA,MAAA,CAASJ,EAAO,KAC7CC,EAAAA,CAAAA,CAAO,SAAS,IACd,CAAA,CAAA,kBAAA,EAAqBD,EAAO,KAAK,CAAA,+BAAA,CACnC,EAEJ,CAEIA,CAAAA,CAAO,UAAY,CAAC,mBAAA,CAAoB,KAAKE,CAAY,CAAA,QAAA,EAAU,CAAA,EACrED,EAAO,QAAS,CAAA,IAAA,CACd,gEACF,CAGED,CAAAA,CAAAA,CAAO,OAAS,KAAaE,CAAAA,EAAAA,CAAAA,CAAcF,EAAO,IAAS,GAAA,CAAA,EAC7DC,EAAO,QAAS,CAAA,IAAA,CAAK,yBAAyBD,CAAO,CAAA,IAAI,EAAE,CAGzDA,CAAAA,CAAAA,CAAO,YACLA,GAAAA,CAAAA,CAAO,aAAa,QAAS,CAAA,OAAO,GAAK,CAACK,CAAAA,CAAQH,CAAW,CAC/DD,EAAAA,CAAAA,CAAO,SAAS,IAAK,CAAA,wBAAwB,EAE3CD,CAAO,CAAA,YAAA,CAAa,SAAS,MAAM,CAAA,EAAKE,EAAc,CAAM,GAAA,CAAA,EAC9DD,CAAO,CAAA,QAAA,CAAS,KAAK,wBAAwB,CAAA,CAE3CD,EAAO,YAAa,CAAA,QAAA,CAAS,KAAK,CAAKE,EAAAA,CAAAA,CAAc,IAAM,CAC7DD,EAAAA,CAAAA,CAAO,SAAS,IAAK,CAAA,uBAAuB,GAIhDA,CAAO,CAAA,KAAA,CAAQC,EACjB,CAASI,MAAAA,CAAAA,CAAO,CACdL,CAAO,CAAA,MAAA,CAAO,KAAK,CAA2BK,wBAAAA,EAAAA,CAAAA,CAAM,OAAO,CAAE,CAAA,EAC/D,CAEA,OAAOL,CACT,CAEO,SAASM,CAAAA,CACdP,EACA,CACA,OAAQQ,GAA+B,CACrCA,CAAAA,CAAS,IACPC,2BAAWT,CAAAA,CAAAA,CAAO,SAAa,EAAA,IAAA,CAAM,MAAOU,CAA2B,EAAA,CACrE,QAAWC,CAASX,IAAAA,CAAAA,CAAO,OAAQ,CACjC,IAAMD,EAAQW,CAAO,CAAA,GAAA,CAAIC,CAAK,CAC9B,CAAA,GAAI,OAAOZ,CAAU,EAAA,QAAA,EAAY,OAAOA,CAAU,EAAA,QAAA,CAChD,OAAOW,CAAAA,CAET,IAAMT,CAASH,CAAAA,CAAAA,CAAoBC,EAAOC,CAAM,CAAA,CAC5CC,EAAO,KAAU,GAAA,IAAA,EACnBS,EAAO,GAAIC,CAAAA,CAAAA,CAAOV,EAAO,KAAK,CAAA,CAGhCA,EAAO,MAAO,CAAA,OAAA,CAASK,GAAUI,CAAO,CAAA,QAAA,CAASC,CAAOL,CAAAA,CAAK,CAAC,CAC9DL,CAAAA,CAAAA,CAAO,SAAS,OAASW,CAAAA,CAAAA,EACvBF,EAAO,UAAWC,CAAAA,CAAAA,CAAOC,CAAO,CAClC,EACF,CACA,OAAOF,CACT,CAAC,CACH,EACF,CACF,CAEO,SAASL,EAAQQ,CAAsB,CAAA,CAC5C,QAASC,CAAI,CAAA,CAAA,CAAGC,EAAO,IAAK,CAAA,IAAA,CAAKF,CAAG,CAAGC,CAAAA,CAAAA,EAAKC,EAAMD,CAChD,EAAA,CAAA,GAAID,EAAMC,CAAM,GAAA,CAAA,CAAG,OAAO,CAE5B,CAAA,CAAA,OAAOD,EAAM,CACf","file":"index.cjs","sourcesContent":["import { FlatfileListener } from '@flatfile/listener'\nimport { FlatfileRecord, recordHook } from '@flatfile/plugin-record-hook'\n\nexport interface NumberValidationConfig {\n min?: number\n max?: number\n inclusive?: boolean\n integerOnly?: boolean\n precision?: number\n scale?: number\n currency?: boolean\n step?: number\n thousandsSeparator?: string\n decimalPoint?: string\n specialTypes?: string[]\n round?: boolean\n truncate?: boolean\n}\n\nexport interface NumberValidationResult {\n value: number | null\n errors: string[]\n warnings: string[]\n}\n\nexport function validateNumberField(\n value: string | number,\n config: NumberValidationConfig\n): NumberValidationResult {\n const result: NumberValidationResult = {\n value: null,\n errors: [],\n warnings: [],\n }\n\n try {\n let numberValue: string | number = String(value)\n .replace(config.thousandsSeparator || ',', '')\n .replace(config.decimalPoint || '.', '.')\n numberValue = parseFloat(numberValue)\n\n if (isNaN(numberValue)) {\n result.errors.push('Must be a number')\n return result\n }\n\n if (config.round) {\n numberValue = Math.round(numberValue)\n }\n\n if (config.truncate) {\n numberValue = Math.trunc(numberValue)\n }\n\n if (config.integerOnly && !Number.isInteger(numberValue)) {\n result.warnings.push('Must be an integer')\n }\n\n if (config.min !== undefined) {\n if (\n config.inclusive ? numberValue < config.min : numberValue <= config.min\n ) {\n result.warnings.push(\n `Must be greater than ${config.inclusive ? 'or equal to ' : ''}${config.min}`\n )\n }\n }\n\n if (config.max !== undefined) {\n if (\n config.inclusive ? numberValue > config.max : numberValue >= config.max\n ) {\n result.warnings.push(\n `Must be less than ${config.inclusive ? 'or equal to ' : ''}${config.max}`\n )\n }\n }\n\n if (config.precision !== undefined && config.scale !== undefined) {\n const [integerPart, decimalPart] = numberValue.toString().split('.')\n if (integerPart.length > config.precision - config.scale) {\n result.warnings.push(\n `Must have at most ${config.precision - config.scale} digits before the decimal point`\n )\n }\n if (decimalPart && decimalPart.length > config.scale) {\n result.warnings.push(\n `Must have at most ${config.scale} digits after the decimal point`\n )\n }\n }\n\n if (config.currency && !/^\\d+(\\.\\d{1,2})?$/.test(numberValue.toString())) {\n result.warnings.push(\n 'Must be a valid currency value with at most two decimal places'\n )\n }\n\n if (config.step !== undefined && numberValue % config.step !== 0) {\n result.warnings.push(`Must be a multiple of ${config.step}`)\n }\n\n if (config.specialTypes) {\n if (config.specialTypes.includes('prime') && !isPrime(numberValue)) {\n result.warnings.push('Must be a prime number')\n }\n if (config.specialTypes.includes('even') && numberValue % 2 !== 0) {\n result.warnings.push('Must be an even number')\n }\n if (config.specialTypes.includes('odd') && numberValue % 2 === 0) {\n result.warnings.push('Must be an odd number')\n }\n }\n\n result.value = numberValue\n } catch (error) {\n result.errors.push(`Error processing value: ${error.message}`)\n }\n\n return result\n}\n\nexport function validateNumber(\n config: NumberValidationConfig & { fields: string[]; sheetSlug?: string }\n) {\n return (listener: FlatfileListener) => {\n listener.use(\n recordHook(config.sheetSlug || '**', async (record: FlatfileRecord) => {\n for (const field of config.fields) {\n const value = record.get(field)\n if (typeof value !== 'string' && typeof value !== 'number') {\n return record\n }\n const result = validateNumberField(value, config)\n if (result.value !== null) {\n record.set(field, result.value)\n }\n\n result.errors.forEach((error) => record.addError(field, error))\n result.warnings.forEach((warning) =>\n record.addWarning(field, warning)\n )\n }\n return record\n })\n )\n }\n}\n\nexport function isPrime(num: number): boolean {\n for (let i = 2, sqrt = Math.sqrt(num); i <= sqrt; i++) {\n if (num % i === 0) return false\n }\n return num > 1\n}\n"]}